From 57ee7f52fdb9f6d61a4e6179d198ed1c8850ef75 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 16 Mar 2015 18:50:27 +0100 Subject: [PATCH] Adding mapbox-gl branch --- mapbox-gl/build/ol-debug.js | 128442 +++++++++++++++ mapbox-gl/build/ol-deps.js | 1696 + mapbox-gl/build/ol.js | 956 + mapbox-gl/closure-library/.gitignore | 12 + mapbox-gl/closure-library/.travis.yml | 14 + mapbox-gl/closure-library/AUTHORS | 17 + mapbox-gl/closure-library/CONTRIBUTING | 48 + mapbox-gl/closure-library/LICENSE | 176 + mapbox-gl/closure-library/README.md | 9 + mapbox-gl/closure-library/all_tests.html | 111 + mapbox-gl/closure-library/alltests.js | 16 + .../closure/bin/build/closurebuilder.py | 287 + .../closure/bin/build/depstree.py | 189 + .../closure/bin/build/depstree_test.py | 127 + .../closure/bin/build/depswriter.py | 204 + .../closure/bin/build/jscompiler.py | 135 + .../closure/bin/build/jscompiler_test.py | 120 + .../closure/bin/build/source.py | 127 + .../closure/bin/build/source_test.py | 152 + .../closure/bin/build/treescan.py | 78 + .../closure-library/closure/bin/calcdeps.py | 590 + .../closure/bin/labs/code/closure.el | 39 + .../closure/bin/labs/code/closure_test.el | 31 + .../closure/bin/labs/code/generate_jsdoc.py | 171 + .../bin/labs/code/generate_jsdoc_test.py | 167 + .../closure/bin/labs/code/run_el_tests.sh | 31 + .../closure/bin/labs/code/run_py_tests.sh | 28 + .../closure/bin/logos/logo.svg | 25 + .../closure/bin/logos/logoandlabel.svg | 25 + .../closure-library/closure/bin/scopify.py | 221 + .../closure/css/inlay/g-base.css | 82 + .../closure/css/inlay/g-fixed.css | 173 + .../closure/css/inlay/g-ratio.css | 253 + .../closure/goog/a11y/aria/announcer.js | 125 + .../goog/a11y/aria/announcer_test.html | 27 + .../closure/goog/a11y/aria/announcer_test.js | 129 + .../closure/goog/a11y/aria/aria.js | 386 + .../closure/goog/a11y/aria/aria_test.html | 27 + .../closure/goog/a11y/aria/aria_test.js | 269 + .../closure/goog/a11y/aria/attributes.js | 389 + .../closure/goog/a11y/aria/datatables.js | 68 + .../closure/goog/a11y/aria/roles.js | 216 + .../closure/goog/array/array.js | 1655 + .../closure/goog/array/array_test.html | 24 + .../closure/goog/array/array_test.js | 1786 + .../closure/goog/asserts/asserts.js | 365 + .../closure/goog/asserts/asserts_test.html | 22 + .../closure/goog/asserts/asserts_test.js | 242 + .../closure/goog/async/animationdelay.js | 273 + .../goog/async/animationdelay_test.html | 23 + .../closure/goog/async/animationdelay_test.js | 81 + .../closure/goog/async/conditionaldelay.js | 235 + .../goog/async/conditionaldelay_test.html | 22 + .../goog/async/conditionaldelay_test.js | 222 + .../closure/goog/async/delay.js | 179 + .../closure/goog/async/delay_test.html | 22 + .../closure/goog/async/delay_test.js | 142 + .../closure/goog/async/nexttick.js | 231 + .../closure/goog/async/nexttick_test.html | 22 + .../closure/goog/async/nexttick_test.js | 243 + .../closure-library/closure/goog/async/run.js | 150 + .../closure/goog/async/run_test.js | 147 + .../closure/goog/async/throttle.js | 193 + .../closure/goog/async/throttle_test.html | 22 + .../closure/goog/async/throttle_test.js | 95 + .../closure-library/closure/goog/base.js | 2496 + .../closure/goog/base_module_test.html | 20 + .../closure/goog/base_module_test.js | 113 + .../closure/goog/base_test.html | 33 + .../closure-library/closure/goog/base_test.js | 1495 + .../closure/goog/bootstrap/nodejs.js | 92 + .../closure/goog/bootstrap/webworkers.js | 37 + .../closure/goog/color/alpha.js | 472 + .../closure/goog/color/alpha_test.html | 22 + .../closure/goog/color/alpha_test.js | 321 + .../closure/goog/color/color.js | 776 + .../closure/goog/color/color_test.html | 22 + .../closure/goog/color/color_test.js | 667 + .../closure/goog/color/names.js | 176 + .../closure-library/closure/goog/crypt/aes.js | 1030 + .../closure/goog/crypt/aes_test.html | 24 + .../closure/goog/crypt/aes_test.js | 586 + .../closure/goog/crypt/arc4.js | 164 + .../closure/goog/crypt/arc4_test.html | 22 + .../closure/goog/crypt/arc4_test.js | 59 + .../closure/goog/crypt/base64.js | 286 + .../closure/goog/crypt/base64_test.html | 22 + .../closure/goog/crypt/base64_test.js | 156 + .../closure/goog/crypt/basen.js | 242 + .../closure/goog/crypt/basen_test.html | 22 + .../closure/goog/crypt/basen_test.js | 165 + .../closure/goog/crypt/blobhasher.js | 283 + .../closure/goog/crypt/blobhasher_test.html | 23 + .../closure/goog/crypt/blobhasher_test.js | 382 + .../closure/goog/crypt/blockcipher.js | 52 + .../closure/goog/crypt/bytestring_perf.html | 25 + .../closure/goog/crypt/bytestring_perf.js | 124 + .../closure-library/closure/goog/crypt/cbc.js | 153 + .../closure/goog/crypt/cbc_test.html | 18 + .../closure/goog/crypt/cbc_test.js | 100 + .../closure/goog/crypt/crypt.js | 173 + .../closure/goog/crypt/crypt_perf.html | 85 + .../closure/goog/crypt/crypt_test.html | 22 + .../closure/goog/crypt/crypt_test.js | 169 + .../closure/goog/crypt/hash.js | 69 + .../closure/goog/crypt/hash32.js | 184 + .../closure/goog/crypt/hash32_test.html | 22 + .../closure/goog/crypt/hash32_test.js | 284 + .../closure/goog/crypt/hashtester.js | 244 + .../closure/goog/crypt/hmac.js | 160 + .../closure/goog/crypt/hmac_test.html | 22 + .../closure/goog/crypt/hmac_test.js | 142 + .../closure-library/closure/goog/crypt/md5.js | 435 + .../closure/goog/crypt/md5_perf.html | 39 + .../closure/goog/crypt/md5_test.html | 23 + .../closure/goog/crypt/md5_test.js | 147 + .../closure/goog/crypt/pbkdf2.js | 128 + .../closure/goog/crypt/pbkdf2_test.html | 25 + .../closure/goog/crypt/pbkdf2_test.js | 61 + .../closure/goog/crypt/sha1.js | 311 + .../closure/goog/crypt/sha12mc_test.html | 22 + .../closure/goog/crypt/sha12mc_test.js | 130 + .../closure/goog/crypt/sha1_perf.html | 40 + .../closure/goog/crypt/sha1_test.html | 22 + .../closure/goog/crypt/sha1_test.js | 118 + .../closure/goog/crypt/sha2.js | 515 + .../closure/goog/crypt/sha224.js | 50 + .../closure/goog/crypt/sha224_perf.html | 40 + .../closure/goog/crypt/sha224_test.html | 22 + .../closure/goog/crypt/sha224_test.js | 75 + .../closure/goog/crypt/sha256.js | 49 + .../closure/goog/crypt/sha256_perf.html | 40 + .../closure/goog/crypt/sha256_test.html | 22 + .../closure/goog/crypt/sha256_test.js | 102 + .../closure/goog/crypt/sha2_64bit.js | 550 + .../closure/goog/crypt/sha2_64bit_test.html | 22 + .../closure/goog/crypt/sha2_64bit_test.js | 278 + .../closure/goog/crypt/sha384.js | 59 + .../closure/goog/crypt/sha512.js | 59 + .../closure/goog/crypt/sha512_256.js | 65 + .../closure/goog/crypt/sha512_perf.html | 41 + .../closure/goog/css/autocomplete.css | 43 + .../closure/goog/css/bubble.css | 84 + .../closure/goog/css/button.css | 38 + .../closure/goog/css/charpicker.css | 206 + .../closure/goog/css/checkbox.css | 38 + .../closure/goog/css/colormenubutton.css | 25 + .../closure/goog/css/colorpalette.css | 54 + .../goog/css/colorpicker-simplegrid.css | 49 + .../closure/goog/css/combobox.css | 54 + .../closure/goog/css/common.css | 41 + .../closure/goog/css/css3button.css | 77 + .../closure/goog/css/css3menubutton.css | 23 + .../closure/goog/css/custombutton.css | 161 + .../closure/goog/css/datepicker.css | 154 + .../closure/goog/css/dialog.css | 72 + .../closure/goog/css/dimensionpicker.css | 47 + .../closure/goog/css/dragdropdetector.css | 48 + .../closure/goog/css/editor/bubble.css | 73 + .../closure/goog/css/editor/dialog.css | 66 + .../goog/css/editor/equationeditor.css | 113 + .../closure/goog/css/editor/linkdialog.css | 36 + .../closure/goog/css/editortoolbar.css | 225 + .../closure/goog/css/filteredmenu.css | 30 + .../goog/css/filterobservingmenuitem.css | 25 + .../closure/goog/css/flatbutton.css | 64 + .../closure/goog/css/flatmenubutton.css | 63 + .../closure/goog/css/hovercard.css | 51 + .../closure/goog/css/hsvapalette.css | 231 + .../closure/goog/css/hsvpalette.css | 179 + .../closure/goog/css/imagelessbutton.css | 160 + .../closure/goog/css/imagelessmenubutton.css | 23 + .../closure/goog/css/inputdatepicker.css | 12 + .../closure/goog/css/linkbutton.css | 26 + .../closure-library/closure/goog/css/menu.css | 27 + .../closure/goog/css/menubar.css | 57 + .../closure/goog/css/menubutton.css | 169 + .../closure/goog/css/menuitem.css | 148 + .../closure/goog/css/menuseparator.css | 19 + .../closure/goog/css/multitestrunner.css | 121 + .../closure/goog/css/palette.css | 36 + .../closure/goog/css/popupdatepicker.css | 17 + .../closure/goog/css/roundedpanel.css | 29 + .../closure/goog/css/roundedtab.css | 158 + .../closure/goog/css/submenu.css | 38 + .../closure-library/closure/goog/css/tab.css | 105 + .../closure/goog/css/tabbar.css | 52 + .../closure/goog/css/tablesorter.css | 14 + .../closure/goog/css/toolbar.css | 400 + .../closure/goog/css/tooltip.css | 14 + .../closure-library/closure/goog/css/tree.css | 146 + .../closure/goog/css/tristatemenuitem.css | 43 + .../closure/goog/cssom/cssom.js | 454 + .../closure/goog/cssom/cssom_test.html | 39 + .../closure/goog/cssom/cssom_test.js | 323 + .../goog/cssom/cssom_test_import_1.css | 11 + .../goog/cssom/cssom_test_import_2.css | 10 + .../closure/goog/cssom/cssom_test_link_1.css | 10 + .../closure/goog/cssom/iframe/style.js | 1024 + .../closure/goog/cssom/iframe/style_test.html | 129 + .../closure/goog/cssom/iframe/style_test.js | 292 + .../goog/cssom/iframe/style_test_import.css | 10 + .../closure/goog/datasource/datamanager.js | 561 + .../closure/goog/datasource/datasource.js | 658 + .../goog/datasource/datasource_test.html | 22 + .../goog/datasource/datasource_test.js | 256 + .../closure/goog/datasource/expr.js | 545 + .../closure/goog/datasource/expr_test.html | 22 + .../closure/goog/datasource/expr_test.js | 95 + .../closure/goog/datasource/fastdatanode.js | 814 + .../goog/datasource/fastdatanode_test.html | 22 + .../goog/datasource/fastdatanode_test.js | 249 + .../closure/goog/datasource/jsdatasource.js | 462 + .../closure/goog/datasource/jsondatasource.js | 149 + .../goog/datasource/jsxmlhttpdatasource.js | 196 + .../datasource/jsxmlhttpdatasource_test.html | 22 + .../datasource/jsxmlhttpdatasource_test.js | 93 + .../closure/goog/datasource/xmldatasource.js | 417 + .../closure-library/closure/goog/date/date.js | 1758 + .../closure/goog/date/date_test.html | 22 + .../closure/goog/date/date_test.js | 1434 + .../closure/goog/date/datelike.js | 27 + .../closure/goog/date/daterange.js | 427 + .../closure/goog/date/daterange_test.html | 22 + .../closure/goog/date/daterange_test.js | 258 + .../closure/goog/date/duration.js | 153 + .../closure/goog/date/duration_test.html | 24 + .../closure/goog/date/duration_test.js | 131 + .../closure/goog/date/relative.js | 489 + .../closure/goog/date/relative_test.html | 24 + .../closure/goog/date/relative_test.js | 163 + .../closure/goog/date/relativewithplurals.js | 120 + .../goog/date/relativewithplurals_test.html | 24 + .../goog/date/relativewithplurals_test.js | 128 + .../closure/goog/date/utcdatetime.js | 190 + .../closure/goog/date/utcdatetime_test.html | 22 + .../closure/goog/date/utcdatetime_test.js | 127 + .../closure-library/closure/goog/db/cursor.js | 215 + .../closure-library/closure/goog/db/db.js | 185 + .../closure/goog/db/db_test.html | 23 + .../closure/goog/db/db_test.js | 1724 + .../closure-library/closure/goog/db/error.js | 364 + .../closure-library/closure/goog/db/index.js | 246 + .../closure/goog/db/indexeddb.js | 353 + .../closure/goog/db/keyrange.js | 118 + .../closure/goog/db/objectstore.js | 401 + .../closure/goog/db/old_db_test.html | 23 + .../closure/goog/db/old_db_test.js | 1238 + .../closure/goog/db/transaction.js | 223 + .../closure/goog/debug/console.js | 207 + .../closure/goog/debug/console_test.html | 22 + .../closure/goog/debug/console_test.js | 191 + .../closure/goog/debug/debug.js | 632 + .../closure/goog/debug/debug_test.html | 22 + .../closure/goog/debug/debug_test.js | 113 + .../closure/goog/debug/debugwindow.js | 615 + .../closure/goog/debug/debugwindow_test.html | 22 + .../closure/goog/debug/debugwindow_test.js | 25 + .../closure/goog/debug/devcss/devcss.js | 446 + .../goog/debug/devcss/devcss_test.html | 160 + .../closure/goog/debug/devcss/devcss_test.js | 216 + .../closure/goog/debug/devcss/devcssrunner.js | 26 + .../closure/goog/debug/divconsole.js | 149 + .../closure/goog/debug/enhanceerror_test.html | 22 + .../closure/goog/debug/enhanceerror_test.js | 133 + .../closure/goog/debug/entrypointregistry.js | 158 + .../goog/debug/entrypointregistry_test.html | 25 + .../goog/debug/entrypointregistry_test.js | 80 + .../closure/goog/debug/error.js | 54 + .../closure/goog/debug/error_test.html | 22 + .../closure/goog/debug/error_test.js | 109 + .../closure/goog/debug/errorhandler.js | 367 + .../goog/debug/errorhandler_async_test.html | 25 + .../goog/debug/errorhandler_async_test.js | 121 + .../closure/goog/debug/errorhandler_test.html | 25 + .../closure/goog/debug/errorhandler_test.js | 281 + .../closure/goog/debug/errorhandlerweakdep.js | 38 + .../closure/goog/debug/errorreporter.js | 429 + .../goog/debug/errorreporter_test.html | 25 + .../closure/goog/debug/errorreporter_test.js | 388 + .../closure/goog/debug/fancywindow.js | 374 + .../closure/goog/debug/formatter.js | 362 + .../closure/goog/debug/formatter_test.html | 22 + .../closure/goog/debug/formatter_test.js | 51 + .../closure/goog/debug/fpsdisplay.js | 164 + .../closure/goog/debug/fpsdisplay_test.html | 22 + .../closure/goog/debug/fpsdisplay_test.js | 49 + .../closure/goog/debug/gcdiagnostics.js | 143 + .../closure/goog/debug/logbuffer.js | 148 + .../closure/goog/debug/logbuffer_test.html | 22 + .../closure/goog/debug/logbuffer_test.js | 100 + .../closure/goog/debug/logger.js | 873 + .../closure/goog/debug/logger_test.html | 22 + .../closure/goog/debug/logger_test.js | 223 + .../closure/goog/debug/logrecord.js | 242 + .../closure/goog/debug/logrecordserializer.js | 121 + .../goog/debug/logrecordserializer_test.html | 22 + .../goog/debug/logrecordserializer_test.js | 86 + .../goog/debug/relativetimeprovider.js | 84 + .../closure/goog/debug/tracer.js | 725 + .../closure/goog/debug/tracer_test.html | 22 + .../closure/goog/debug/tracer_test.js | 52 + .../closure/goog/defineclass_test.html | 22 + .../closure/goog/defineclass_test.js | 110 + .../closure/goog/demos/advancedtooltip.html | 78 + .../closure/goog/demos/animationqueue.html | 149 + .../goog/demos/autocomplete-basic.html | 56 + .../goog/demos/autocompleteremote.html | 78 + .../goog/demos/autocompleteremotedata.js | 18 + .../goog/demos/autocompleterichremote.html | 137 + .../goog/demos/autocompleterichremotedata.js | 33 + .../closure/goog/demos/bidiinput.html | 72 + .../closure/goog/demos/blobhasher.html | 61 + .../closure/goog/demos/bubble.html | 250 + .../closure/goog/demos/button.html | 395 + .../closure/goog/demos/charcounter.html | 57 + .../closure/goog/demos/charpicker.html | 66 + .../closure/goog/demos/checkbox.html | 122 + .../closure/goog/demos/color-contrast.html | 60 + .../closure/goog/demos/colormenubutton.html | 218 + .../closure/goog/demos/colorpicker.html | 39 + .../closure/goog/demos/combobox.html | 160 + .../closure/goog/demos/container.html | 670 + .../closure/goog/demos/control.html | 477 + .../closure/goog/demos/css/demo.css | 75 + .../closure/goog/demos/css/emojipicker.css | 36 + .../closure/goog/demos/css/emojisprite.css | 92 + .../closure/goog/demos/css3button.html | 166 + .../closure/goog/demos/css3menubutton.html | 285 + .../goog/demos/cssspriteanimation.html | 80 + .../closure/goog/demos/datepicker.html | 311 + .../closure/goog/demos/debug.html | 118 + .../closure/goog/demos/depsgraph.html | 220 + .../closure/goog/demos/dialog.html | 152 + .../closure/goog/demos/dimensionpicker.html | 104 + .../goog/demos/dimensionpicker_rtl.html | 119 + .../closure/goog/demos/dom_selection.html | 88 + .../closure/goog/demos/drag.html | 191 + .../closure/goog/demos/dragdrop.html | 263 + .../closure/goog/demos/dragdropdetector.html | 46 + .../goog/demos/dragdropdetector_target.html | 17 + .../closure/goog/demos/dragger.html | 83 + .../closure/goog/demos/draglistgroup.html | 273 + .../closure/goog/demos/dragscrollsupport.html | 133 + .../closure/goog/demos/drilldownrow.html | 78 + .../closure/goog/demos/editor/deps.js | 21 + .../closure/goog/demos/editor/editor.html | 139 + .../goog/demos/editor/field_basic.html | 74 + .../closure/goog/demos/editor/helloworld.html | 91 + .../closure/goog/demos/editor/helloworld.js | 82 + .../goog/demos/editor/helloworld_test.html | 75 + .../goog/demos/editor/helloworlddialog.js | 173 + .../demos/editor/helloworlddialog_test.html | 100 + .../demos/editor/helloworlddialogplugin.js | 117 + .../editor/helloworlddialogplugin_test.html | 198 + .../goog/demos/editor/seamlessfield.html | 106 + .../goog/demos/editor/tableeditor.html | 94 + .../closure/goog/demos/effects.html | 162 + .../closure/goog/demos/emoji/200.gif | Bin 0 -> 941 bytes .../closure/goog/demos/emoji/201.gif | Bin 0 -> 980 bytes .../closure/goog/demos/emoji/202.gif | Bin 0 -> 1054 bytes .../closure/goog/demos/emoji/203.gif | Bin 0 -> 996 bytes .../closure/goog/demos/emoji/204.gif | Bin 0 -> 1016 bytes .../closure/goog/demos/emoji/205.gif | Bin 0 -> 1032 bytes .../closure/goog/demos/emoji/206.gif | Bin 0 -> 990 bytes .../closure/goog/demos/emoji/2BC.gif | Bin 0 -> 1039 bytes .../closure/goog/demos/emoji/2BD.gif | Bin 0 -> 986 bytes .../closure/goog/demos/emoji/2BE.gif | Bin 0 -> 1074 bytes .../closure/goog/demos/emoji/2BF.gif | Bin 0 -> 996 bytes .../closure/goog/demos/emoji/2C0.gif | Bin 0 -> 1036 bytes .../closure/goog/demos/emoji/2C1.gif | Bin 0 -> 1080 bytes .../closure/goog/demos/emoji/2C2.gif | Bin 0 -> 1049 bytes .../closure/goog/demos/emoji/2C3.gif | Bin 0 -> 1104 bytes .../closure/goog/demos/emoji/2C4.gif | Bin 0 -> 1072 bytes .../closure/goog/demos/emoji/2C5.gif | Bin 0 -> 1087 bytes .../closure/goog/demos/emoji/2C6.gif | Bin 0 -> 1041 bytes .../closure/goog/demos/emoji/2C7.gif | Bin 0 -> 1079 bytes .../closure/goog/demos/emoji/2C8.gif | Bin 0 -> 1049 bytes .../closure/goog/demos/emoji/2C9.gif | Bin 0 -> 996 bytes .../closure/goog/demos/emoji/2CA.gif | Bin 0 -> 2299 bytes .../closure/goog/demos/emoji/2CB.gif | Bin 0 -> 992 bytes .../closure/goog/demos/emoji/2CC.gif | Bin 0 -> 977 bytes .../closure/goog/demos/emoji/2CD.gif | Bin 0 -> 1035 bytes .../closure/goog/demos/emoji/2CE.gif | Bin 0 -> 1074 bytes .../closure/goog/demos/emoji/2CF.gif | Bin 0 -> 1022 bytes .../closure/goog/demos/emoji/2D0.gif | Bin 0 -> 987 bytes .../closure/goog/demos/emoji/2D1.gif | Bin 0 -> 997 bytes .../closure/goog/demos/emoji/2D2.gif | Bin 0 -> 1012 bytes .../closure/goog/demos/emoji/2D3.gif | Bin 0 -> 1040 bytes .../closure/goog/demos/emoji/2D4.gif | Bin 0 -> 1043 bytes .../closure/goog/demos/emoji/2D5.gif | Bin 0 -> 1014 bytes .../closure/goog/demos/emoji/2D6.gif | Bin 0 -> 1026 bytes .../closure/goog/demos/emoji/2D7.gif | Bin 0 -> 1048 bytes .../closure/goog/demos/emoji/2D8.gif | Bin 0 -> 884 bytes .../closure/goog/demos/emoji/2D9.gif | Bin 0 -> 974 bytes .../closure/goog/demos/emoji/2DA.gif | Bin 0 -> 920 bytes .../closure/goog/demos/emoji/2DB.gif | Bin 0 -> 949 bytes .../closure/goog/demos/emoji/2DC.gif | Bin 0 -> 949 bytes .../closure/goog/demos/emoji/2DD.gif | Bin 0 -> 1000 bytes .../closure/goog/demos/emoji/2DE.gif | Bin 0 -> 963 bytes .../closure/goog/demos/emoji/2DF.gif | Bin 0 -> 865 bytes .../closure/goog/demos/emoji/2E0.gif | Bin 0 -> 1018 bytes .../closure/goog/demos/emoji/2E1.gif | Bin 0 -> 1004 bytes .../closure/goog/demos/emoji/2E2.gif | Bin 0 -> 1046 bytes .../closure/goog/demos/emoji/2E3.gif | Bin 0 -> 1547 bytes .../closure/goog/demos/emoji/2E4.gif | Bin 0 -> 999 bytes .../closure/goog/demos/emoji/2E5.gif | Bin 0 -> 1032 bytes .../closure/goog/demos/emoji/2E6.gif | Bin 0 -> 1013 bytes .../closure/goog/demos/emoji/2E7.gif | Bin 0 -> 1040 bytes .../closure/goog/demos/emoji/2E8.gif | Bin 0 -> 1028 bytes .../closure/goog/demos/emoji/2E9.gif | Bin 0 -> 1030 bytes .../closure/goog/demos/emoji/2EA.gif | Bin 0 -> 1001 bytes .../closure/goog/demos/emoji/2EB.gif | Bin 0 -> 1086 bytes .../closure/goog/demos/emoji/2EC.gif | Bin 0 -> 1007 bytes .../closure/goog/demos/emoji/2ED.gif | Bin 0 -> 1045 bytes .../closure/goog/demos/emoji/2EE.gif | Bin 0 -> 1016 bytes .../closure/goog/demos/emoji/2EF.gif | Bin 0 -> 2363 bytes .../closure/goog/demos/emoji/2F0.gif | Bin 0 -> 1014 bytes .../closure/goog/demos/emoji/2F1.gif | Bin 0 -> 1902 bytes .../closure/goog/demos/emoji/2F2.gif | Bin 0 -> 1092 bytes .../closure/goog/demos/emoji/2F3.gif | Bin 0 -> 1033 bytes .../closure/goog/demos/emoji/2F4.gif | Bin 0 -> 1065 bytes .../closure/goog/demos/emoji/2F5.gif | Bin 0 -> 954 bytes .../closure/goog/demos/emoji/2F6.gif | Bin 0 -> 1030 bytes .../closure/goog/demos/emoji/2F7.gif | Bin 0 -> 1006 bytes .../closure/goog/demos/emoji/2F8.gif | Bin 0 -> 1016 bytes .../closure/goog/demos/emoji/2F9.gif | Bin 0 -> 1051 bytes .../closure/goog/demos/emoji/2FA.gif | Bin 0 -> 1082 bytes .../closure/goog/demos/emoji/2FB.gif | Bin 0 -> 1012 bytes .../closure/goog/demos/emoji/2FC.gif | Bin 0 -> 977 bytes .../closure/goog/demos/emoji/2FD.gif | Bin 0 -> 989 bytes .../closure/goog/demos/emoji/2FE.gif | Bin 0 -> 1036 bytes .../closure/goog/demos/emoji/2FF.gif | Bin 0 -> 1034 bytes .../closure/goog/demos/emoji/none.gif | Bin 0 -> 834 bytes .../closure/goog/demos/emoji/sprite.png | Bin 0 -> 25195 bytes .../closure/goog/demos/emoji/sprite2.png | Bin 0 -> 27856 bytes .../closure/goog/demos/emoji/unknown.gif | Bin 0 -> 90 bytes .../closure/goog/demos/event-propagation.html | 192 + .../closure/goog/demos/events.html | 94 + .../closure/goog/demos/eventtarget.html | 70 + .../closure/goog/demos/filedrophandler.html | 65 + .../closure/goog/demos/filteredmenu.html | 118 + .../closure/goog/demos/focushandler.html | 58 + .../closure/goog/demos/fpsdisplay.html | 50 + .../goog/demos/fx/css3/transition.html | 223 + .../closure/goog/demos/gauge.html | 158 + .../demos/graphics/advancedcoordinates.html | 141 + .../demos/graphics/advancedcoordinates2.html | 130 + .../goog/demos/graphics/basicelements.html | 264 + .../closure/goog/demos/graphics/events.html | 114 + .../goog/demos/graphics/modifyelements.html | 195 + .../closure/goog/demos/graphics/subpixel.html | 80 + .../closure/goog/demos/graphics/tiger.html | 105 + .../closure/goog/demos/graphics/tigerdata.js | 2841 + .../closure/goog/demos/history1.html | 132 + .../closure/goog/demos/history2.html | 119 + .../closure/goog/demos/history3.html | 116 + .../closure/goog/demos/history3js.html | 48 + .../closure/goog/demos/history_blank.html | 26 + .../closure/goog/demos/hovercard.html | 177 + .../closure/goog/demos/hsvapalette.html | 55 + .../closure/goog/demos/hsvpalette.html | 56 + .../closure/goog/demos/html5history.html | 87 + .../closure/goog/demos/imagelessbutton.html | 221 + .../goog/demos/imagelessmenubutton.html | 285 + .../closure/goog/demos/index.html | 20 + .../closure/goog/demos/index_nav.html | 255 + .../closure/goog/demos/index_splash.html | 27 + .../goog/demos/inline_block_quirks.html | 125 + .../goog/demos/inline_block_standards.html | 126 + .../closure/goog/demos/inputdatepicker.html | 60 + .../closure/goog/demos/inputhandler.html | 84 + .../closure/goog/demos/jsonprettyprinter.html | 80 + .../closure/goog/demos/keyboardshortcuts.html | 112 + .../closure/goog/demos/keyhandler.html | 118 + .../closure/goog/demos/labelinput.html | 42 + .../closure/goog/demos/menu.html | 220 + .../closure/goog/demos/menubar.html | 211 + .../closure/goog/demos/menubutton.html | 380 + .../closure/goog/demos/menubutton_frame.html | 27 + .../closure/goog/demos/menuitem.html | 164 + .../closure/goog/demos/mousewheelhandler.html | 109 + .../closure/goog/demos/onlinehandler.html | 79 + .../closure/goog/demos/palette.html | 299 + .../closure/goog/demos/pastehandler.html | 54 + .../goog/demos/pixeldensitymonitor.html | 51 + .../goog/demos/plaintextspellchecker.html | 118 + .../closure/goog/demos/popup.html | 206 + .../closure/goog/demos/popupcolorpicker.html | 49 + .../closure/goog/demos/popupdatepicker.html | 53 + .../closure/goog/demos/popupemojipicker.html | 408 + .../closure/goog/demos/popupmenu.html | 115 + .../closure/goog/demos/progressbar.html | 97 + .../closure/goog/demos/prompt.html | 92 + .../closure/goog/demos/quadtree.html | 107 + .../closure/goog/demos/ratings.html | 130 + .../goog/demos/richtextspellchecker.html | 101 + .../closure/goog/demos/roundedpanel.html | 139 + .../closure/goog/demos/samplecomponent.html | 75 + .../closure/goog/demos/samplecomponent.js | 198 + .../closure/goog/demos/scrollfloater.html | 138 + .../closure/goog/demos/select.html | 324 + .../goog/demos/selectionmenubutton.html | 186 + .../closure/goog/demos/serverchart.html | 122 + .../closure/goog/demos/slider.html | 128 + .../closure/goog/demos/splitbehavior.html | 163 + .../closure/goog/demos/splitpane.html | 243 + .../closure/goog/demos/stopevent.html | 171 + .../closure/goog/demos/submenus.html | 130 + .../closure/goog/demos/submenus2.html | 150 + .../closure/goog/demos/tabbar.html | 289 + .../closure/goog/demos/tablesorter.html | 116 + .../closure/goog/demos/tabpane.html | 302 + .../closure/goog/demos/textarea.html | 128 + .../closure/goog/demos/timers.html | 291 + .../closure/goog/demos/toolbar.html | 703 + .../closure/goog/demos/tooltip.html | 91 + .../closure/goog/demos/tracer.html | 92 + .../closure/goog/demos/tree/demo.html | 126 + .../closure/goog/demos/tree/testdata.js | 260 + .../closure/goog/demos/tweakui.html | 121 + .../closure/goog/demos/twothumbslider.html | 121 + .../closure/goog/demos/useragent.html | 214 + .../goog/demos/viewportsizemonitor.html | 71 + .../closure/goog/demos/wheelhandler.html | 144 + .../closure/goog/demos/xpc/blank.html | 7 + .../closure/goog/demos/xpc/index.html | 89 + .../closure/goog/demos/xpc/inner.html | 58 + .../closure/goog/demos/xpc/minimal/blank.html | 7 + .../closure/goog/demos/xpc/minimal/index.html | 105 + .../closure/goog/demos/xpc/minimal/inner.html | 75 + .../closure/goog/demos/xpc/minimal/relay.html | 7 + .../closure/goog/demos/xpc/relay.html | 16 + .../closure/goog/demos/xpc/xpcdemo.js | 307 + .../closure/goog/demos/zippy.html | 149 + .../closure-library/closure/goog/deps.js | 1457 + .../closure/goog/disposable/disposable.js | 307 + .../goog/disposable/disposable_test.html | 26 + .../goog/disposable/disposable_test.js | 313 + .../closure/goog/disposable/idisposable.js | 45 + .../closure/goog/dom/abstractmultirange.js | 76 + .../closure/goog/dom/abstractrange.js | 529 + .../closure/goog/dom/abstractrange_test.html | 31 + .../closure/goog/dom/abstractrange_test.js | 61 + .../goog/dom/animationframe/animationframe.js | 288 + .../dom/animationframe/animationframe_test.js | 265 + .../goog/dom/animationframe/polyfill.js | 61 + .../closure/goog/dom/annotate.js | 356 + .../closure/goog/dom/annotate_test.html | 57 + .../closure/goog/dom/annotate_test.js | 184 + .../closure/goog/dom/browserfeature.js | 72 + .../goog/dom/browserrange/abstractrange.js | 350 + .../goog/dom/browserrange/browserrange.js | 149 + .../dom/browserrange/browserrange_test.html | 31 + .../dom/browserrange/browserrange_test.js | 633 + .../goog/dom/browserrange/geckorange.js | 88 + .../closure/goog/dom/browserrange/ierange.js | 951 + .../goog/dom/browserrange/operarange.js | 84 + .../closure/goog/dom/browserrange/w3crange.js | 394 + .../goog/dom/browserrange/webkitrange.js | 113 + .../goog/dom/bufferedviewportsizemonitor.js | 203 + .../dom/bufferedviewportsizemonitor_test.html | 17 + .../dom/bufferedviewportsizemonitor_test.js | 130 + .../closure/goog/dom/classes.js | 239 + .../closure/goog/dom/classes_quirks_test.html | 63 + .../closure/goog/dom/classes_test.html | 64 + .../closure/goog/dom/classes_test.js | 231 + .../closure/goog/dom/classlist.js | 277 + .../closure/goog/dom/classlist_test.html | 29 + .../closure/goog/dom/classlist_test.js | 275 + .../closure/goog/dom/controlrange.js | 506 + .../closure/goog/dom/controlrange_test.html | 39 + .../closure/goog/dom/controlrange_test.js | 228 + .../closure/goog/dom/dataset.js | 140 + .../closure/goog/dom/dataset_test.html | 24 + .../closure/goog/dom/dataset_test.js | 108 + .../closure-library/closure/goog/dom/dom.js | 2987 + .../closure/goog/dom/dom_quirks_test.html | 128 + .../closure/goog/dom/dom_test.html | 128 + .../closure/goog/dom/dom_test.js | 1641 + .../closure/goog/dom/fontsizemonitor.js | 161 + .../goog/dom/fontsizemonitor_test.html | 40 + .../closure/goog/dom/fontsizemonitor_test.js | 257 + .../closure-library/closure/goog/dom/forms.js | 413 + .../closure/goog/dom/forms_test.html | 142 + .../closure/goog/dom/forms_test.js | 377 + .../closure/goog/dom/fullscreen.js | 144 + .../closure/goog/dom/iframe.js | 163 + .../closure/goog/dom/iframe_test.html | 43 + .../closure/goog/dom/iframe_test.js | 64 + .../closure-library/closure/goog/dom/iter.js | 129 + .../closure/goog/dom/iter_test.html | 20 + .../closure/goog/dom/iter_test.js | 97 + .../closure/goog/dom/multirange.js | 521 + .../closure/goog/dom/multirange_test.html | 27 + .../closure/goog/dom/multirange_test.js | 57 + .../closure/goog/dom/nodeiterator.js | 87 + .../closure/goog/dom/nodeiterator_test.html | 27 + .../closure/goog/dom/nodeiterator_test.js | 39 + .../closure/goog/dom/nodeoffset.js | 114 + .../closure/goog/dom/nodeoffset_test.html | 24 + .../closure/goog/dom/nodeoffset_test.js | 85 + .../closure/goog/dom/nodetype.js | 48 + .../goog/dom/pattern/abstractpattern.js | 60 + .../closure/goog/dom/pattern/allchildren.js | 75 + .../goog/dom/pattern/callback/callback.js | 82 + .../goog/dom/pattern/callback/counter.js | 73 + .../closure/goog/dom/pattern/callback/test.js | 78 + .../closure/goog/dom/pattern/childmatches.js | 155 + .../closure/goog/dom/pattern/endtag.js | 54 + .../closure/goog/dom/pattern/fulltag.js | 94 + .../closure/goog/dom/pattern/matcher.js | 151 + .../goog/dom/pattern/matcher_test.html | 34 + .../closure/goog/dom/pattern/matcher_test.js | 187 + .../closure/goog/dom/pattern/nodetype.js | 59 + .../closure/goog/dom/pattern/pattern.js | 93 + .../goog/dom/pattern/pattern_test.html | 39 + .../closure/goog/dom/pattern/pattern_test.js | 592 + .../closure/goog/dom/pattern/repeat.js | 191 + .../closure/goog/dom/pattern/sequence.js | 143 + .../closure/goog/dom/pattern/starttag.js | 53 + .../closure/goog/dom/pattern/tag.js | 150 + .../closure/goog/dom/pattern/text.js | 71 + .../closure-library/closure/goog/dom/range.js | 227 + .../closure/goog/dom/range_test.html | 44 + .../closure/goog/dom/range_test.js | 722 + .../closure/goog/dom/rangeendpoint.js | 32 + .../closure-library/closure/goog/dom/safe.js | 135 + .../closure/goog/dom/safe_test.html | 19 + .../closure/goog/dom/safe_test.js | 80 + .../closure/goog/dom/savedcaretrange.js | 215 + .../goog/dom/savedcaretrange_test.html | 71 + .../closure/goog/dom/savedcaretrange_test.js | 224 + .../closure/goog/dom/savedrange.js | 74 + .../closure/goog/dom/savedrange_test.html | 20 + .../closure/goog/dom/savedrange_test.js | 56 + .../closure/goog/dom/selection.js | 471 + .../closure/goog/dom/selection_test.html | 19 + .../closure/goog/dom/selection_test.js | 343 + .../closure/goog/dom/tagiterator.js | 368 + .../closure/goog/dom/tagiterator_test.html | 25 + .../closure/goog/dom/tagiterator_test.js | 584 + .../closure/goog/dom/tagname.js | 159 + .../closure/goog/dom/tagname_test.html | 22 + .../closure/goog/dom/tagname_test.js | 30 + .../closure-library/closure/goog/dom/tags.js | 42 + .../closure/goog/dom/tags_test.js | 26 + .../closure/goog/dom/textrange.js | 634 + .../closure/goog/dom/textrange_test.html | 46 + .../closure/goog/dom/textrange_test.js | 333 + .../closure/goog/dom/textrangeiterator.js | 246 + .../goog/dom/textrangeiterator_test.html | 27 + .../goog/dom/textrangeiterator_test.js | 138 + .../closure/goog/dom/vendor.js | 96 + .../closure/goog/dom/vendor_test.html | 19 + .../closure/goog/dom/vendor_test.js | 273 + .../closure/goog/dom/viewportsizemonitor.js | 180 + .../goog/dom/viewportsizemonitor_test.html | 19 + .../goog/dom/viewportsizemonitor_test.js | 137 + .../closure-library/closure/goog/dom/xml.js | 204 + .../closure/goog/dom/xml_test.html | 19 + .../closure/goog/dom/xml_test.js | 102 + .../closure/goog/editor/browserfeature.js | 273 + .../goog/editor/browserfeature_test.html | 32 + .../goog/editor/browserfeature_test.js | 112 + .../closure/goog/editor/clicktoeditwrapper.js | 434 + .../goog/editor/clicktoeditwrapper_test.html | 27 + .../goog/editor/clicktoeditwrapper_test.js | 133 + .../closure/goog/editor/command.js | 76 + .../goog/editor/contenteditablefield.js | 108 + .../editor/contenteditablefield_test.html | 29 + .../goog/editor/contenteditablefield_test.js | 54 + .../closure/goog/editor/defines.js | 34 + .../closure/goog/editor/field.js | 2749 + .../closure/goog/editor/field_test.html | 32 + .../closure/goog/editor/field_test.js | 1356 + .../closure/goog/editor/focus.js | 32 + .../closure/goog/editor/focus_test.html | 32 + .../closure/goog/editor/focus_test.js | 52 + .../closure/goog/editor/icontent.js | 300 + .../closure/goog/editor/icontent_test.html | 36 + .../closure/goog/editor/icontent_test.js | 216 + .../closure/goog/editor/link.js | 390 + .../closure/goog/editor/link_test.html | 27 + .../closure/goog/editor/link_test.js | 290 + .../closure/goog/editor/node.js | 484 + .../closure/goog/editor/node_test.html | 29 + .../closure/goog/editor/node_test.js | 645 + .../closure/goog/editor/plugin.js | 463 + .../closure/goog/editor/plugin_test.html | 30 + .../closure/goog/editor/plugin_test.js | 177 + .../editor/plugins/abstractbubbleplugin.js | 712 + .../plugins/abstractbubbleplugin_test.html | 31 + .../plugins/abstractbubbleplugin_test.js | 436 + .../editor/plugins/abstractdialogplugin.js | 333 + .../plugins/abstractdialogplugin_test.html | 26 + .../plugins/abstractdialogplugin_test.js | 403 + .../goog/editor/plugins/abstracttabhandler.js | 78 + .../plugins/abstracttabhandler_test.html | 28 + .../editor/plugins/abstracttabhandler_test.js | 81 + .../goog/editor/plugins/basictextformatter.js | 1769 + .../plugins/basictextformatter_test.html | 94 + .../editor/plugins/basictextformatter_test.js | 1212 + .../closure/goog/editor/plugins/blockquote.js | 451 + .../goog/editor/plugins/blockquote_test.html | 31 + .../goog/editor/plugins/blockquote_test.js | 209 + .../closure/goog/editor/plugins/emoticons.js | 89 + .../goog/editor/plugins/emoticons_test.html | 23 + .../goog/editor/plugins/emoticons_test.js | 84 + .../goog/editor/plugins/enterhandler.js | 768 + .../editor/plugins/enterhandler_test.html | 68 + .../goog/editor/plugins/enterhandler_test.js | 741 + .../goog/editor/plugins/firststrong.js | 327 + .../goog/editor/plugins/firststrong_test.html | 26 + .../goog/editor/plugins/firststrong_test.js | 406 + .../goog/editor/plugins/headerformatter.js | 96 + .../editor/plugins/headerformatter_test.html | 27 + .../editor/plugins/headerformatter_test.js | 95 + .../closure/goog/editor/plugins/linkbubble.js | 585 + .../goog/editor/plugins/linkbubble_test.html | 28 + .../goog/editor/plugins/linkbubble_test.js | 396 + .../goog/editor/plugins/linkdialogplugin.js | 438 + .../editor/plugins/linkdialogplugin_test.html | 29 + .../editor/plugins/linkdialogplugin_test.js | 749 + .../goog/editor/plugins/linkshortcutplugin.js | 62 + .../plugins/linkshortcutplugin_test.html | 27 + .../editor/plugins/linkshortcutplugin_test.js | 65 + .../goog/editor/plugins/listtabhandler.js | 68 + .../editor/plugins/listtabhandler_test.html | 30 + .../editor/plugins/listtabhandler_test.js | 167 + .../closure/goog/editor/plugins/loremipsum.js | 192 + .../goog/editor/plugins/loremipsum_test.html | 31 + .../goog/editor/plugins/loremipsum_test.js | 156 + .../goog/editor/plugins/removeformatting.js | 780 + .../editor/plugins/removeformatting_test.html | 42 + .../editor/plugins/removeformatting_test.js | 955 + .../goog/editor/plugins/spacestabhandler.js | 92 + .../editor/plugins/spacestabhandler_test.html | 30 + .../editor/plugins/spacestabhandler_test.js | 174 + .../goog/editor/plugins/tableeditor.js | 475 + .../goog/editor/plugins/tableeditor_test.html | 30 + .../goog/editor/plugins/tableeditor_test.js | 303 + .../goog/editor/plugins/tagonenterhandler.js | 744 + .../plugins/tagonenterhandler_test.html | 32 + .../editor/plugins/tagonenterhandler_test.js | 546 + .../closure/goog/editor/plugins/undoredo.js | 1016 + .../goog/editor/plugins/undoredo_test.html | 30 + .../goog/editor/plugins/undoredo_test.js | 516 + .../goog/editor/plugins/undoredomanager.js | 338 + .../editor/plugins/undoredomanager_test.html | 27 + .../editor/plugins/undoredomanager_test.js | 387 + .../goog/editor/plugins/undoredostate.js | 86 + .../editor/plugins/undoredostate_test.html | 28 + .../goog/editor/plugins/undoredostate_test.js | 34 + .../closure/goog/editor/range.js | 632 + .../closure/goog/editor/range_test.html | 92 + .../closure/goog/editor/range_test.js | 942 + .../closure/goog/editor/seamlessfield.js | 739 + .../editor/seamlessfield_quirks_test.html | 28 + .../goog/editor/seamlessfield_test.html | 32 + .../closure/goog/editor/seamlessfield_test.js | 469 + .../closure/goog/editor/style.js | 225 + .../closure/goog/editor/style_test.html | 27 + .../closure/goog/editor/style_test.js | 174 + .../closure/goog/editor/table.js | 570 + .../closure/goog/editor/table_test.html | 185 + .../closure/goog/editor/table_test.js | 482 + .../closure/goog/events/actioneventwrapper.js | 151 + .../goog/events/actioneventwrapper_test.html | 24 + .../goog/events/actioneventwrapper_test.js | 270 + .../closure/goog/events/actionhandler.js | 184 + .../goog/events/actionhandler_test.html | 25 + .../closure/goog/events/actionhandler_test.js | 80 + .../closure/goog/events/browserevent.js | 446 + .../goog/events/browserevent_test.html | 26 + .../closure/goog/events/browserevent_test.js | 165 + .../closure/goog/events/browserfeature.js | 85 + .../closure/goog/events/event.js | 143 + .../closure/goog/events/event_test.html | 23 + .../closure/goog/events/event_test.js | 68 + .../closure/goog/events/eventhandler.js | 459 + .../goog/events/eventhandler_test.html | 30 + .../closure/goog/events/eventhandler_test.js | 247 + .../closure/goog/events/eventid.js | 47 + .../closure/goog/events/events.js | 983 + .../closure/goog/events/events_test.html | 22 + .../closure/goog/events/events_test.js | 745 + .../closure/goog/events/eventtarget.js | 394 + .../closure/goog/events/eventtarget_test.html | 22 + .../closure/goog/events/eventtarget_test.js | 72 + .../eventtarget_via_googevents_test.html | 22 + .../events/eventtarget_via_googevents_test.js | 77 + .../eventtarget_via_w3cinterface_test.html | 22 + .../eventtarget_via_w3cinterface_test.js | 49 + .../closure/goog/events/eventtargettester.js | 1063 + .../closure/goog/events/eventtype.js | 232 + .../closure/goog/events/eventwrapper.js | 66 + .../closure/goog/events/filedrophandler.js | 222 + .../goog/events/filedrophandler_test.html | 24 + .../goog/events/filedrophandler_test.js | 250 + .../closure/goog/events/focushandler.js | 107 + .../closure/goog/events/imehandler.js | 369 + .../closure/goog/events/imehandler_test.html | 33 + .../closure/goog/events/imehandler_test.js | 266 + .../closure/goog/events/inputhandler.js | 220 + .../goog/events/inputhandler_test.html | 22 + .../closure/goog/events/inputhandler_test.js | 95 + .../closure/goog/events/keycodes.js | 415 + .../closure/goog/events/keycodes_test.html | 24 + .../closure/goog/events/keycodes_test.js | 174 + .../closure/goog/events/keyhandler.js | 556 + .../closure/goog/events/keyhandler_test.html | 22 + .../closure/goog/events/keyhandler_test.js | 728 + .../closure/goog/events/keynames.js | 139 + .../closure/goog/events/listenable.js | 335 + .../closure/goog/events/listenable_test.html | 22 + .../closure/goog/events/listenable_test.js | 29 + .../closure/goog/events/listener.js | 131 + .../closure/goog/events/listenermap.js | 308 + .../closure/goog/events/listenermap_test.html | 19 + .../closure/goog/events/listenermap_test.js | 161 + .../closure/goog/events/mousewheelhandler.js | 296 + .../goog/events/mousewheelhandler_test.html | 31 + .../goog/events/mousewheelhandler_test.js | 384 + .../closure/goog/events/onlinehandler.js | 159 + .../goog/events/onlinelistener_test.html | 25 + .../goog/events/onlinelistener_test.js | 154 + .../closure/goog/events/pastehandler.js | 517 + .../goog/events/pastehandler_test.html | 26 + .../closure/goog/events/pastehandler_test.js | 409 + .../closure/goog/events/wheelevent.js | 169 + .../closure/goog/events/wheelhandler.js | 159 + .../goog/events/wheelhandler_test.html | 31 + .../closure/goog/events/wheelhandler_test.js | 296 + .../closure/goog/format/emailaddress.js | 499 + .../goog/format/emailaddress_test.html | 22 + .../closure/goog/format/emailaddress_test.js | 229 + .../closure/goog/format/format.js | 502 + .../closure/goog/format/format_test.html | 22 + .../closure/goog/format/format_test.js | 303 + .../closure/goog/format/htmlprettyprinter.js | 409 + .../goog/format/htmlprettyprinter_test.html | 22 + .../goog/format/htmlprettyprinter_test.js | 205 + .../format/internationalizedemailaddress.js | 256 + .../internationalizedemailaddress_test.html | 22 + .../internationalizedemailaddress_test.js | 335 + .../closure/goog/format/jsonprettyprinter.js | 414 + .../goog/format/jsonprettyprinter_test.html | 22 + .../goog/format/jsonprettyprinter_test.js | 109 + .../closure-library/closure/goog/fs/entry.js | 272 + .../closure/goog/fs/entryimpl.js | 405 + .../closure-library/closure/goog/fs/error.js | 181 + .../closure/goog/fs/filereader.js | 288 + .../closure/goog/fs/filesaver.js | 166 + .../closure/goog/fs/filesystem.js | 41 + .../closure/goog/fs/filesystemimpl.js | 65 + .../closure/goog/fs/filewriter.js | 111 + .../closure-library/closure/goog/fs/fs.js | 319 + .../closure/goog/fs/fs_test.html | 25 + .../closure/goog/fs/fs_test.js | 842 + .../closure/goog/fs/progressevent.js | 69 + .../closure/goog/functions/functions.js | 332 + .../goog/functions/functions_test.html | 23 + .../closure/goog/functions/functions_test.js | 313 + .../closure/goog/fx/abstractdragdrop.js | 1540 + .../goog/fx/abstractdragdrop_test.html | 57 + .../closure/goog/fx/abstractdragdrop_test.js | 636 + .../closure/goog/fx/anim/anim.js | 211 + .../closure/goog/fx/anim/anim_test.html | 27 + .../closure/goog/fx/anim/anim_test.js | 218 + .../closure/goog/fx/animation.js | 524 + .../closure/goog/fx/animation_test.html | 22 + .../closure/goog/fx/animation_test.js | 116 + .../closure/goog/fx/animationqueue.js | 310 + .../closure/goog/fx/animationqueue_test.html | 22 + .../closure/goog/fx/animationqueue_test.js | 315 + .../closure/goog/fx/css3/fx.js | 63 + .../closure/goog/fx/css3/transition.js | 201 + .../closure/goog/fx/css3/transition_test.html | 25 + .../closure/goog/fx/css3/transition_test.js | 219 + .../closure/goog/fx/cssspriteanimation.js | 130 + .../goog/fx/cssspriteanimation_test.html | 33 + .../goog/fx/cssspriteanimation_test.js | 153 + .../closure-library/closure/goog/fx/dom.js | 686 + .../closure/goog/fx/dragdrop.js | 50 + .../closure/goog/fx/dragdropgroup.js | 109 + .../closure/goog/fx/dragdropgroup_test.html | 32 + .../closure/goog/fx/dragdropgroup_test.js | 229 + .../closure/goog/fx/dragger.js | 847 + .../closure/goog/fx/dragger_test.html | 37 + .../closure/goog/fx/dragger_test.js | 459 + .../closure/goog/fx/draglistgroup.js | 1312 + .../closure/goog/fx/draglistgroup_test.html | 42 + .../closure/goog/fx/draglistgroup_test.js | 397 + .../closure/goog/fx/dragscrollsupport.js | 300 + .../goog/fx/dragscrollsupport_test.html | 63 + .../closure/goog/fx/dragscrollsupport_test.js | 315 + .../closure-library/closure/goog/fx/easing.js | 85 + .../closure/goog/fx/easing_test.js | 33 + .../closure-library/closure/goog/fx/fx.js | 32 + .../closure/goog/fx/fx_test.html | 27 + .../closure/goog/fx/fx_test.js | 86 + .../closure/goog/fx/transition.js | 76 + .../closure/goog/fx/transitionbase.js | 236 + .../closure/goog/graphics/abstractgraphics.js | 454 + .../closure/goog/graphics/affinetransform.js | 588 + .../goog/graphics/affinetransform_test.html | 360 + .../closure/goog/graphics/canvaselement.js | 797 + .../closure/goog/graphics/canvasgraphics.js | 683 + .../closure/goog/graphics/element.js | 164 + .../closure/goog/graphics/ellipseelement.js | 63 + .../closure/goog/graphics/ext/coordinates.js | 159 + .../goog/graphics/ext/coordinates_test.html | 74 + .../closure/goog/graphics/ext/element.js | 963 + .../goog/graphics/ext/element_test.html | 144 + .../closure/goog/graphics/ext/ellipse.js | 60 + .../closure/goog/graphics/ext/ext.js | 29 + .../closure/goog/graphics/ext/graphics.js | 218 + .../closure/goog/graphics/ext/group.js | 216 + .../closure/goog/graphics/ext/image.js | 64 + .../closure/goog/graphics/ext/path.js | 142 + .../closure/goog/graphics/ext/path_test.html | 46 + .../closure/goog/graphics/ext/rectangle.js | 55 + .../closure/goog/graphics/ext/shape.js | 145 + .../goog/graphics/ext/strokeandfillelement.js | 70 + .../closure/goog/graphics/fill.js | 46 + .../closure/goog/graphics/font.js | 64 + .../closure/goog/graphics/graphics.js | 142 + .../closure/goog/graphics/groupelement.js | 58 + .../closure/goog/graphics/imageelement.js | 70 + .../closure/goog/graphics/lineargradient.js | 175 + .../closure/goog/graphics/path.js | 511 + .../closure/goog/graphics/path_test.html | 359 + .../closure/goog/graphics/pathelement.js | 54 + .../closure/goog/graphics/paths.js | 86 + .../closure/goog/graphics/paths_test.html | 98 + .../closure/goog/graphics/rectelement.js | 63 + .../closure/goog/graphics/solidfill.js | 74 + .../closure/goog/graphics/solidfill_test.html | 53 + .../closure/goog/graphics/stroke.js | 86 + .../goog/graphics/strokeandfillelement.js | 114 + .../closure/goog/graphics/svgelement.js | 284 + .../closure/goog/graphics/svggraphics.js | 879 + .../goog/graphics/svggraphics_test.html | 109 + .../closure/goog/graphics/textelement.js | 55 + .../closure/goog/graphics/vmlelement.js | 438 + .../closure/goog/graphics/vmlgraphics.js | 934 + .../closure/goog/history/event.js | 55 + .../closure/goog/history/eventtype.js | 30 + .../closure/goog/history/history.js | 1001 + .../closure/goog/history/history_test.html | 21 + .../closure/goog/history/history_test.js | 55 + .../closure/goog/history/html5history.js | 303 + .../goog/history/html5history_test.html | 22 + .../closure/goog/history/html5history_test.js | 159 + .../closure/goog/html/flash.js | 177 + .../closure/goog/html/flash_test.html | 19 + .../closure/goog/html/flash_test.js | 111 + .../closure/goog/html/legacyconversions.js | 180 + .../goog/html/legacyconversions_test.html | 19 + .../goog/html/legacyconversions_test.js | 95 + .../closure/goog/html/safehtml.js | 729 + .../closure/goog/html/safehtml_test.html | 19 + .../closure/goog/html/safehtml_test.js | 387 + .../closure/goog/html/safescript.js | 220 + .../closure/goog/html/safescript_test.html | 19 + .../closure/goog/html/safescript_test.js | 66 + .../closure/goog/html/safestyle.js | 393 + .../closure/goog/html/safestyle_test.html | 19 + .../closure/goog/html/safestyle_test.js | 185 + .../closure/goog/html/safestylesheet.js | 263 + .../goog/html/safestylesheet_test.html | 19 + .../closure/goog/html/safestylesheet_test.js | 97 + .../closure/goog/html/safeurl.js | 402 + .../closure/goog/html/safeurl_test.html | 19 + .../closure/goog/html/safeurl_test.js | 201 + .../closure/goog/html/silverlight.js | 92 + .../closure/goog/html/silverlight_test.html | 19 + .../closure/goog/html/silverlight_test.js | 60 + .../closure/goog/html/testing.js | 129 + .../closure/goog/html/trustedresourceurl.js | 224 + .../goog/html/trustedresourceurl_test.html | 19 + .../goog/html/trustedresourceurl_test.js | 60 + .../closure/goog/html/uncheckedconversions.js | 231 + .../goog/html/uncheckedconversions_test.html | 19 + .../goog/html/uncheckedconversions_test.js | 159 + .../closure/goog/html/utils.js | 67 + .../closure/goog/html/utils_test.html | 19 + .../closure/goog/html/utils_test.js | 122 + .../closure-library/closure/goog/i18n/bidi.js | 877 + .../closure/goog/i18n/bidi_test.html | 22 + .../closure/goog/i18n/bidi_test.js | 481 + .../closure/goog/i18n/bidiformatter.js | 596 + .../closure/goog/i18n/bidiformatter_test.html | 22 + .../closure/goog/i18n/bidiformatter_test.js | 535 + .../closure/goog/i18n/charlistdecompressor.js | 158 + .../goog/i18n/charlistdecompressor_test.html | 25 + .../goog/i18n/charlistdecompressor_test.js | 58 + .../closure/goog/i18n/charpickerdata.js | 3667 + .../closure/goog/i18n/collation.js | 58 + .../closure/goog/i18n/collation_test.html | 23 + .../closure/goog/i18n/collation_test.js | 64 + .../goog/i18n/compactnumberformatsymbols.js | 9631 ++ .../i18n/compactnumberformatsymbols_ext.js | 30072 ++++ .../closure/goog/i18n/currency.js | 434 + .../closure/goog/i18n/currency_test.html | 23 + .../closure/goog/i18n/currency_test.js | 267 + .../closure/goog/i18n/currencycodemap.js | 208 + .../closure/goog/i18n/datetimeformat.js | 758 + .../goog/i18n/datetimeformat_test.html | 23 + .../closure/goog/i18n/datetimeformat_test.js | 768 + .../closure/goog/i18n/datetimeparse.js | 1150 + .../closure/goog/i18n/datetimeparse_test.html | 26 + .../closure/goog/i18n/datetimeparse_test.js | 627 + .../closure/goog/i18n/datetimepatterns.js | 2520 + .../closure/goog/i18n/datetimepatternsext.js | 14232 ++ .../closure/goog/i18n/datetimesymbols.js | 4530 + .../closure/goog/i18n/datetimesymbolsext.js | 22754 +++ .../closure/goog/i18n/graphemebreak.js | 214 + .../closure/goog/i18n/graphemebreak_test.html | 22 + .../closure/goog/i18n/graphemebreak_test.js | 78 + .../closure/goog/i18n/messageformat.js | 780 + .../closure/goog/i18n/messageformat_test.html | 23 + .../closure/goog/i18n/messageformat_test.js | 464 + .../closure-library/closure/goog/i18n/mime.js | 111 + .../closure/goog/i18n/mime_test.html | 25 + .../closure/goog/i18n/mime_test.js | 43 + .../closure/goog/i18n/numberformat.js | 1249 + .../closure/goog/i18n/numberformat_test.html | 23 + .../closure/goog/i18n/numberformat_test.js | 1050 + .../closure/goog/i18n/numberformatsymbols.js | 4139 + .../goog/i18n/numberformatsymbolsext.js | 12374 ++ .../closure/goog/i18n/ordinalrules.js | 748 + .../closure/goog/i18n/pluralrules.js | 1120 + .../closure/goog/i18n/pluralrules_test.html | 23 + .../closure/goog/i18n/pluralrules_test.js | 102 + .../closure/goog/i18n/timezone.js | 341 + .../closure/goog/i18n/timezone_test.html | 22 + .../closure/goog/i18n/timezone_test.js | 160 + .../closure/goog/i18n/uchar.js | 1368 + .../goog/i18n/uchar/localnamefetcher.js | 74 + .../i18n/uchar/localnamefetcher_test.html | 23 + .../goog/i18n/uchar/localnamefetcher_test.js | 59 + .../closure/goog/i18n/uchar/namefetcher.js | 70 + .../goog/i18n/uchar/remotenamefetcher.js | 282 + .../i18n/uchar/remotenamefetcher_test.html | 23 + .../goog/i18n/uchar/remotenamefetcher_test.js | 108 + .../closure/goog/i18n/uchar_test.html | 25 + .../closure/goog/i18n/uchar_test.js | 125 + .../closure/goog/images/blank.gif | Bin 0 -> 49 bytes .../closure/goog/images/bubble_close.jpg | Bin 0 -> 586 bytes .../closure/goog/images/bubble_left.gif | Bin 0 -> 85 bytes .../closure/goog/images/bubble_right.gif | Bin 0 -> 86 bytes .../closure/goog/images/button-bg.gif | Bin 0 -> 454 bytes .../closure/goog/images/check-outline.gif | Bin 0 -> 69 bytes .../closure/goog/images/check-sprite.gif | Bin 0 -> 75 bytes .../closure/goog/images/check.gif | Bin 0 -> 53 bytes .../closure/goog/images/close_box.gif | Bin 0 -> 65 bytes .../closure/goog/images/color-swatch-tick.gif | Bin 0 -> 69 bytes .../closure/goog/images/dialog_close_box.gif | Bin 0 -> 86 bytes .../goog/images/dimension-highlighted.png | Bin 0 -> 171 bytes .../goog/images/dimension-unhighlighted.png | Bin 0 -> 171 bytes .../closure/goog/images/dropdn.gif | Bin 0 -> 51 bytes .../closure/goog/images/dropdn_disabled.gif | Bin 0 -> 51 bytes .../closure/goog/images/dropdown.gif | Bin 0 -> 78 bytes .../closure/goog/images/gears_bluedot.gif | Bin 0 -> 236 bytes .../closure/goog/images/gears_online.gif | Bin 0 -> 137 bytes .../closure/goog/images/gears_paused.gif | Bin 0 -> 93 bytes .../closure/goog/images/gears_syncing.gif | Bin 0 -> 761 bytes .../closure/goog/images/hsv-sprite-sm.gif | Bin 0 -> 11851 bytes .../closure/goog/images/hsv-sprite-sm.png | Bin 0 -> 19537 bytes .../closure/goog/images/hsv-sprite.gif | Bin 0 -> 33309 bytes .../closure/goog/images/hsv-sprite.png | Bin 0 -> 58142 bytes .../closure/goog/images/hsva-sprite-sm.gif | Bin 0 -> 12571 bytes .../closure/goog/images/hsva-sprite-sm.png | Bin 0 -> 19921 bytes .../closure/goog/images/hsva-sprite.gif | Bin 0 -> 36428 bytes .../closure/goog/images/hsva-sprite.png | Bin 0 -> 60591 bytes .../goog/images/left_anchor_bubble_bot.gif | Bin 0 -> 431 bytes .../goog/images/left_anchor_bubble_top.gif | Bin 0 -> 332 bytes .../closure/goog/images/menu-arrows.gif | Bin 0 -> 113 bytes .../closure/goog/images/minus.png | Bin 0 -> 238 bytes .../goog/images/no_anchor_bubble_bot.gif | Bin 0 -> 228 bytes .../goog/images/no_anchor_bubble_top.gif | Bin 0 -> 123 bytes .../closure/goog/images/offlineicons.png | Bin 0 -> 5643 bytes .../closure/goog/images/plus.png | Bin 0 -> 239 bytes .../closure/goog/images/ratingstars.gif | Bin 0 -> 1139 bytes .../goog/images/right_anchor_bubble_bot.gif | Bin 0 -> 425 bytes .../goog/images/right_anchor_bubble_top.gif | Bin 0 -> 335 bytes .../closure/goog/images/toolbar-bg.png | Bin 0 -> 203 bytes .../closure/goog/images/toolbar-separator.gif | Bin 0 -> 472 bytes .../closure/goog/images/toolbar_icons.gif | Bin 0 -> 1062 bytes .../closure/goog/images/tree/I.png | Bin 0 -> 232 bytes .../closure/goog/images/tree/cleardot.gif | Bin 0 -> 43 bytes .../closure/goog/images/tree/tree.gif | Bin 0 -> 1568 bytes .../closure/goog/images/tree/tree.png | Bin 0 -> 1262 bytes .../closure/goog/images/ui_controls.jpg | Bin 0 -> 21680 bytes .../closure-library/closure/goog/iter/iter.js | 1305 + .../closure/goog/iter/iter_test.html | 27 + .../closure/goog/iter/iter_test.js | 867 + .../closure/goog/json/evaljsonprocessor.js | 67 + .../closure/goog/json/hybrid.js | 103 + .../closure/goog/json/hybrid_test.html | 19 + .../closure/goog/json/hybrid_test.js | 157 + .../closure/goog/json/hybridjsonprocessor.js | 47 + .../goog/json/hybridjsonprocessor_test.html | 19 + .../goog/json/hybridjsonprocessor_test.js | 33 + .../closure-library/closure/goog/json/json.js | 369 + .../closure/goog/json/json_perf.html | 29 + .../closure/goog/json/json_perf.js | 112 + .../closure/goog/json/json_test.html | 19 + .../closure/goog/json/json_test.js | 563 + .../closure/goog/json/nativejsonprocessor.js | 73 + .../closure/goog/json/processor.js | 33 + .../closure/goog/json/processor_test.html | 22 + .../closure/goog/json/processor_test.js | 88 + .../goog/labs/dom/pagevisibilitymonitor.js | 211 + .../labs/dom/pagevisibilitymonitor_test.js | 87 + .../labs/events/nondisposableeventtarget.js | 305 + .../events/nondisposableeventtarget_test.html | 22 + .../events/nondisposableeventtarget_test.js | 72 + ...osableeventtarget_via_googevents_test.html | 22 + ...sposableeventtarget_via_googevents_test.js | 78 + .../closure/goog/labs/events/touch.js | 82 + .../closure/goog/labs/events/touch_test.html | 19 + .../closure/goog/labs/events/touch_test.js | 96 + .../closure/goog/labs/format/csv.js | 415 + .../closure/goog/labs/format/csv_test.html | 20 + .../closure/goog/labs/format/csv_test.js | 201 + .../goog/labs/html/attribute_rewriter.js | 74 + .../closure/goog/labs/html/sanitizer.js | 392 + .../closure/goog/labs/html/sanitizer_test.js | 270 + .../closure/goog/labs/html/scrubber.js | 1027 + .../closure/goog/labs/html/scrubber_test.js | 254 + .../closure/goog/labs/i18n/listformat.js | 261 + .../goog/labs/i18n/listformat_test.html | 23 + .../closure/goog/labs/i18n/listformat_test.js | 324 + .../closure/goog/labs/i18n/listsymbols.js | 1796 + .../closure/goog/labs/i18n/listsymbolsext.js | 10088 ++ .../closure/goog/labs/iterable/iterable.js | 139 + .../goog/labs/iterable/iterable_test.js | 146 + .../closure/goog/labs/mock/mock.js | 861 + .../closure/goog/labs/mock/mock_test.html | 21 + .../closure/goog/labs/mock/mock_test.js | 517 + .../closure/goog/labs/net/image.js | 94 + .../closure/goog/labs/net/image_test.html | 21 + .../closure/goog/labs/net/image_test.js | 87 + .../goog/labs/net/testdata/cleardot.gif | Bin 0 -> 43 bytes .../goog/labs/net/testdata/xhr_test_json.data | 2 + .../goog/labs/net/testdata/xhr_test_text.data | 1 + .../closure/goog/labs/net/webchannel.js | 302 + .../labs/net/webchannel/basetestchannel.js | 519 + .../goog/labs/net/webchannel/channel.js | 186 + .../labs/net/webchannel/channelrequest.js | 1268 + .../net/webchannel/channelrequest_test.html | 22 + .../net/webchannel/channelrequest_test.js | 310 + .../labs/net/webchannel/connectionstate.js | 50 + .../webchannel/forwardchannelrequestpool.js | 278 + .../forwardchannelrequestpool_test.html | 22 + .../forwardchannelrequestpool_test.js | 119 + .../goog/labs/net/webchannel/netutils.js | 162 + .../goog/labs/net/webchannel/requeststats.js | 386 + .../labs/net/webchannel/webchannelbase.js | 2097 + .../net/webchannel/webchannelbase_test.html | 22 + .../net/webchannel/webchannelbase_test.js | 1512 + .../net/webchannel/webchannelbasetransport.js | 376 + .../webchannelbasetransport_test.html | 22 + .../webchannelbasetransport_test.js | 254 + .../labs/net/webchannel/webchanneldebug.js | 300 + .../closure/goog/labs/net/webchannel/wire.js | 75 + .../goog/labs/net/webchannel/wirev8.js | 136 + .../goog/labs/net/webchannel/wirev8_test.html | 22 + .../goog/labs/net/webchannel/wirev8_test.js | 99 + .../goog/labs/net/webchanneltransport.js | 75 + .../labs/net/webchanneltransportfactory.js | 35 + .../closure/goog/labs/net/xhr.js | 468 + .../closure/goog/labs/net/xhr_test.html | 24 + .../closure/goog/labs/net/xhr_test.js | 462 + .../closure/goog/labs/object/object.js | 47 + .../closure/goog/labs/object/object_test.html | 25 + .../closure/goog/labs/object/object_test.js | 52 + .../goog/labs/pubsub/broadcastpubsub.js | 564 + .../goog/labs/pubsub/broadcastpubsub_test.js | 1059 + .../labs/storage/boundedcollectablestorage.js | 284 + .../boundedcollectablestorage_test.html | 22 + .../storage/boundedcollectablestorage_test.js | 74 + .../closure/goog/labs/structs/map.js | 348 + .../closure/goog/labs/structs/map_perf.js | 204 + .../closure/goog/labs/structs/map_test.html | 25 + .../closure/goog/labs/structs/map_test.js | 432 + .../closure/goog/labs/structs/multimap.js | 282 + .../goog/labs/structs/multimap_test.html | 25 + .../goog/labs/structs/multimap_test.js | 328 + .../goog/labs/style/pixeldensitymonitor.js | 179 + .../labs/style/pixeldensitymonitor_test.html | 17 + .../labs/style/pixeldensitymonitor_test.js | 146 + .../closure/goog/labs/testing/assertthat.js | 58 + .../goog/labs/testing/assertthat_test.html | 21 + .../goog/labs/testing/assertthat_test.js | 69 + .../goog/labs/testing/decoratormatcher.js | 95 + .../labs/testing/decoratormatcher_test.html | 21 + .../labs/testing/decoratormatcher_test.js | 41 + .../goog/labs/testing/dictionarymatcher.js | 273 + .../labs/testing/dictionarymatcher_test.html | 21 + .../labs/testing/dictionarymatcher_test.js | 65 + .../closure/goog/labs/testing/environment.js | 293 + .../goog/labs/testing/environment_test.html | 21 + .../goog/labs/testing/environment_test.js | 210 + .../labs/testing/environment_usage_test.js | 45 + .../closure/goog/labs/testing/logicmatcher.js | 212 + .../goog/labs/testing/logicmatcher_test.html | 21 + .../goog/labs/testing/logicmatcher_test.js | 57 + .../closure/goog/labs/testing/matcher.js | 80 + .../goog/labs/testing/numbermatcher.js | 346 + .../goog/labs/testing/numbermatcher_test.html | 21 + .../goog/labs/testing/numbermatcher_test.js | 71 + .../goog/labs/testing/objectmatcher.js | 317 + .../goog/labs/testing/objectmatcher_test.html | 21 + .../goog/labs/testing/objectmatcher_test.js | 95 + .../goog/labs/testing/stringmatcher.js | 415 + .../goog/labs/testing/stringmatcher_test.html | 21 + .../goog/labs/testing/stringmatcher_test.js | 92 + .../closure/goog/labs/useragent/browser.js | 310 + .../goog/labs/useragent/browser_test.html | 21 + .../goog/labs/useragent/browser_test.js | 291 + .../closure/goog/labs/useragent/device.js | 65 + .../goog/labs/useragent/device_test.html | 21 + .../goog/labs/useragent/device_test.js | 73 + .../closure/goog/labs/useragent/engine.js | 130 + .../goog/labs/useragent/engine_test.html | 21 + .../goog/labs/useragent/engine_test.js | 148 + .../closure/goog/labs/useragent/platform.js | 160 + .../goog/labs/useragent/platform_test.html | 21 + .../goog/labs/useragent/platform_test.js | 247 + .../goog/labs/useragent/test_agents.js | 348 + .../closure/goog/labs/useragent/util.js | 148 + .../goog/labs/useragent/util_test.html | 22 + .../closure/goog/labs/useragent/util_test.js | 105 + .../closure/goog/locale/countries.js | 291 + .../locale/countrylanguagenames_test.html | 26 + .../goog/locale/countrylanguagenames_test.js | 231 + .../goog/locale/defaultlocalenameconstants.js | 938 + .../closure/goog/locale/genericfontnames.js | 73 + .../goog/locale/genericfontnames_test.html | 26 + .../goog/locale/genericfontnames_test.js | 93 + .../goog/locale/genericfontnamesdata.js | 327 + .../closure/goog/locale/locale.js | 403 + .../goog/locale/nativenameconstants.js | 1354 + .../closure/goog/locale/scriptToLanguages.js | 482 + .../closure/goog/locale/timezonedetection.js | 116 + .../goog/locale/timezonedetection_test.html | 22 + .../goog/locale/timezonedetection_test.js | 130 + .../goog/locale/timezonefingerprint.js | 248 + .../closure/goog/locale/timezonelist.js | 131 + .../goog/locale/timezonelist_test.html | 26 + .../closure/goog/locale/timezonelist_test.js | 158 + .../closure-library/closure/goog/log/log.js | 197 + .../closure/goog/log/log_test.js | 187 + .../closure/goog/math/affinetransform.js | 589 + .../goog/math/affinetransform_test.html | 19 + .../closure/goog/math/affinetransform_test.js | 359 + .../closure/goog/math/bezier.js | 340 + .../closure/goog/math/bezier_test.html | 22 + .../closure/goog/math/bezier_test.js | 126 + .../closure-library/closure/goog/math/box.js | 389 + .../closure/goog/math/box_test.html | 22 + .../closure/goog/math/box_test.js | 321 + .../closure/goog/math/coordinate.js | 268 + .../closure/goog/math/coordinate3.js | 170 + .../closure/goog/math/coordinate3_test.html | 26 + .../closure/goog/math/coordinate3_test.js | 196 + .../closure/goog/math/coordinate_test.html | 22 + .../closure/goog/math/coordinate_test.js | 170 + .../closure/goog/math/exponentialbackoff.js | 104 + .../goog/math/exponentialbackoff_test.html | 22 + .../goog/math/exponentialbackoff_test.js | 63 + .../closure/goog/math/integer.js | 739 + .../closure/goog/math/integer_test.html | 22 + .../closure/goog/math/integer_test.js | 1651 + .../goog/math/interpolator/interpolator1.js | 64 + .../closure/goog/math/interpolator/linear1.js | 84 + .../goog/math/interpolator/linear1_test.html | 22 + .../goog/math/interpolator/linear1_test.js | 84 + .../closure/goog/math/interpolator/pchip1.js | 82 + .../goog/math/interpolator/pchip1_test.html | 22 + .../goog/math/interpolator/pchip1_test.js | 71 + .../closure/goog/math/interpolator/spline1.js | 203 + .../goog/math/interpolator/spline1_test.html | 22 + .../goog/math/interpolator/spline1_test.js | 100 + .../closure-library/closure/goog/math/line.js | 179 + .../closure/goog/math/line_test.html | 22 + .../closure/goog/math/line_test.js | 59 + .../closure-library/closure/goog/math/long.js | 804 + .../closure/goog/math/long_test.html | 22 + .../closure/goog/math/long_test.js | 1571 + .../closure-library/closure/goog/math/math.js | 435 + .../closure/goog/math/math_test.html | 22 + .../closure/goog/math/math_test.js | 332 + .../closure/goog/math/matrix.js | 681 + .../closure/goog/math/matrix_test.html | 22 + .../closure/goog/math/matrix_test.js | 429 + .../closure-library/closure/goog/math/path.js | 598 + .../closure/goog/math/path_test.html | 19 + .../closure/goog/math/path_test.js | 518 + .../closure/goog/math/paths.js | 86 + .../closure/goog/math/paths_test.html | 21 + .../closure/goog/math/paths_test.js | 50 + .../closure/goog/math/range.js | 186 + .../closure/goog/math/range_test.html | 22 + .../closure/goog/math/range_test.js | 142 + .../closure/goog/math/rangeset.js | 396 + .../closure/goog/math/rangeset_test.html | 22 + .../closure/goog/math/rangeset_test.js | 660 + .../closure-library/closure/goog/math/rect.js | 464 + .../closure/goog/math/rect_test.html | 22 + .../closure/goog/math/rect_test.js | 441 + .../closure-library/closure/goog/math/size.js | 208 + .../closure/goog/math/size_test.html | 22 + .../closure/goog/math/size_test.js | 175 + .../closure-library/closure/goog/math/tdma.js | 73 + .../closure/goog/math/tdma_test.html | 22 + .../closure/goog/math/tdma_test.js | 30 + .../closure-library/closure/goog/math/vec2.js | 284 + .../closure/goog/math/vec2_test.html | 22 + .../closure/goog/math/vec2_test.js | 220 + .../closure-library/closure/goog/math/vec3.js | 310 + .../closure/goog/math/vec3_test.html | 26 + .../closure/goog/math/vec3_test.js | 212 + .../closure/goog/memoize/memoize.js | 104 + .../closure/goog/memoize/memoize_test.html | 22 + .../closure/goog/memoize/memoize_test.js | 153 + .../closure/goog/messaging/abstractchannel.js | 209 + .../goog/messaging/abstractchannel_test.html | 23 + .../goog/messaging/abstractchannel_test.js | 82 + .../closure/goog/messaging/bufferedchannel.js | 287 + .../goog/messaging/bufferedchannel_test.html | 34 + .../goog/messaging/bufferedchannel_test.js | 268 + .../closure/goog/messaging/deferredchannel.js | 98 + .../goog/messaging/deferredchannel_test.html | 23 + .../goog/messaging/deferredchannel_test.js | 106 + .../closure/goog/messaging/loggerclient.js | 132 + .../goog/messaging/loggerclient_test.html | 23 + .../goog/messaging/loggerclient_test.js | 97 + .../closure/goog/messaging/loggerserver.js | 100 + .../goog/messaging/loggerserver_test.html | 23 + .../goog/messaging/loggerserver_test.js | 101 + .../closure/goog/messaging/messagechannel.js | 116 + .../closure/goog/messaging/messaging.js | 32 + .../goog/messaging/messaging_test.html | 23 + .../closure/goog/messaging/messaging_test.js | 35 + .../closure/goog/messaging/multichannel.js | 303 + .../goog/messaging/multichannel_test.html | 23 + .../goog/messaging/multichannel_test.js | 116 + .../closure/goog/messaging/portcaller.js | 152 + .../goog/messaging/portcaller_test.html | 23 + .../closure/goog/messaging/portcaller_test.js | 58 + .../closure/goog/messaging/portchannel.js | 401 + .../goog/messaging/portchannel_test.html | 392 + .../closure/goog/messaging/portnetwork.js | 78 + .../goog/messaging/portnetwork_test.html | 74 + .../closure/goog/messaging/portoperator.js | 198 + .../goog/messaging/portoperator_test.html | 23 + .../goog/messaging/portoperator_test.js | 107 + .../goog/messaging/respondingchannel.js | 234 + .../messaging/respondingchannel_test.html | 23 + .../goog/messaging/respondingchannel_test.js | 152 + .../messaging/testdata/portchannel_inner.html | 28 + .../messaging/testdata/portchannel_worker.js | 37 + .../portchannel_wrong_origin_inner.html | 29 + .../messaging/testdata/portnetwork_inner.html | 34 + .../messaging/testdata/portnetwork_worker1.js | 32 + .../messaging/testdata/portnetwork_worker2.js | 32 + .../goog/module/abstractmoduleloader.js | 58 + .../closure/goog/module/basemodule.js | 47 + .../closure/goog/module/loader.js | 345 + .../closure/goog/module/module.js | 33 + .../closure/goog/module/moduleinfo.js | 338 + .../closure/goog/module/moduleinfo_test.html | 24 + .../closure/goog/module/moduleinfo_test.js | 104 + .../closure/goog/module/moduleloadcallback.js | 87 + .../goog/module/moduleloadcallback_test.html | 25 + .../goog/module/moduleloadcallback_test.js | 45 + .../closure/goog/module/moduleloader.js | 461 + .../goog/module/moduleloader_test.html | 30 + .../closure/goog/module/moduleloader_test.js | 439 + .../closure/goog/module/modulemanager.js | 1358 + .../goog/module/modulemanager_test.html | 24 + .../closure/goog/module/modulemanager_test.js | 2200 + .../closure/goog/module/testdata/modA_1.js | 26 + .../closure/goog/module/testdata/modA_2.js | 29 + .../closure/goog/module/testdata/modB_1.js | 33 + .../closure/goog/net/browserchannel.js | 2765 + .../closure/goog/net/browserchannel_test.html | 26 + .../closure/goog/net/browserchannel_test.js | 1325 + .../closure/goog/net/browsertestchannel.js | 619 + .../closure/goog/net/bulkloader.js | 182 + .../closure/goog/net/bulkloader_test.html | 24 + .../closure/goog/net/bulkloader_test.js | 235 + .../closure/goog/net/bulkloaderhelper.js | 128 + .../closure/goog/net/channeldebug.js | 300 + .../closure/goog/net/channelrequest.js | 1312 + .../closure/goog/net/channelrequest_test.html | 24 + .../closure/goog/net/channelrequest_test.js | 282 + .../closure/goog/net/cookies.js | 371 + .../closure/goog/net/cookies_test.html | 22 + .../closure/goog/net/cookies_test.js | 265 + .../closure/goog/net/corsxmlhttpfactory.js | 272 + .../goog/net/corsxmlhttpfactory_test.html | 22 + .../goog/net/corsxmlhttpfactory_test.js | 44 + .../closure/goog/net/crossdomainrpc.js | 883 + .../closure/goog/net/crossdomainrpc_test.css | 7 + .../closure/goog/net/crossdomainrpc_test.gif | 0 .../closure/goog/net/crossdomainrpc_test.html | 30 + .../closure/goog/net/crossdomainrpc_test.js | 119 + .../net/crossdomainrpc_test_response.html | 59 + .../closure/goog/net/errorcode.js | 130 + .../closure/goog/net/eventtype.js | 37 + .../closure/goog/net/filedownloader.js | 746 + .../closure/goog/net/filedownloader_test.html | 23 + .../closure/goog/net/filedownloader_test.js | 371 + .../closure/goog/net/httpstatus.js | 116 + .../closure/goog/net/iframe_xhr_test.html | 36 + .../closure/goog/net/iframe_xhr_test.js | 144 + .../goog/net/iframe_xhr_test_response.html | 17 + .../closure/goog/net/iframeio.js | 1372 + .../net/iframeio_different_base_test.data | 2 + .../net/iframeio_different_base_test.html | 27 + .../goog/net/iframeio_different_base_test.js | 36 + .../closure/goog/net/iframeio_test.html | 115 + .../closure/goog/net/iframeio_test.js | 310 + .../closure/goog/net/iframeloadmonitor.js | 204 + .../goog/net/iframeloadmonitor_test.html | 26 + .../goog/net/iframeloadmonitor_test.js | 105 + .../net/iframeloadmonitor_test_frame.html | 12 + .../net/iframeloadmonitor_test_frame2.html | 12 + .../net/iframeloadmonitor_test_frame3.html | 12 + .../closure/goog/net/imageloader.js | 337 + .../closure/goog/net/imageloader_test.html | 25 + .../closure/goog/net/imageloader_test.js | 330 + .../closure/goog/net/imageloader_testimg1.gif | Bin 0 -> 453 bytes .../closure/goog/net/imageloader_testimg2.gif | Bin 0 -> 460 bytes .../closure/goog/net/imageloader_testimg3.gif | Bin 0 -> 13446 bytes .../closure/goog/net/ipaddress.js | 515 + .../closure/goog/net/ipaddress_test.html | 26 + .../closure/goog/net/ipaddress_test.js | 220 + .../closure/goog/net/jsloader.js | 367 + .../closure/goog/net/jsloader_test.html | 24 + .../closure/goog/net/jsloader_test.js | 137 + .../closure-library/closure/goog/net/jsonp.js | 340 + .../closure/goog/net/jsonp_test.html | 24 + .../closure/goog/net/jsonp_test.js | 321 + .../closure/goog/net/mockiframeio.js | 308 + .../goog/net/multiiframeloadmonitor.js | 118 + .../goog/net/multiiframeloadmonitor_test.html | 26 + .../goog/net/multiiframeloadmonitor_test.js | 162 + .../closure/goog/net/networkstatusmonitor.js | 47 + .../closure/goog/net/networktester.js | 397 + .../closure/goog/net/networktester_test.html | 22 + .../closure/goog/net/networktester_test.js | 237 + .../goog/net/testdata/jsloader_test1.js | 23 + .../goog/net/testdata/jsloader_test2.js | 23 + .../goog/net/testdata/jsloader_test3.js | 23 + .../goog/net/testdata/jsloader_test4.js | 23 + .../closure/goog/net/tmpnetwork.js | 164 + .../closure/goog/net/websocket.js | 513 + .../closure/goog/net/websocket_test.html | 24 + .../closure/goog/net/websocket_test.js | 363 + .../closure/goog/net/wrapperxmlhttpfactory.js | 71 + .../closure-library/closure/goog/net/xhrio.js | 1224 + .../closure/goog/net/xhrio_test.html | 25 + .../closure/goog/net/xhrio_test.js | 794 + .../closure/goog/net/xhriopool.js | 79 + .../closure/goog/net/xhrlike.js | 124 + .../closure/goog/net/xhrmanager.js | 772 + .../closure/goog/net/xhrmanager_test.html | 24 + .../closure/goog/net/xhrmanager_test.js | 120 + .../closure/goog/net/xmlhttp.js | 246 + .../closure/goog/net/xmlhttpfactory.js | 67 + .../closure/goog/net/xpc/crosspagechannel.js | 861 + .../goog/net/xpc/crosspagechannel_test.html | 36 + .../goog/net/xpc/crosspagechannel_test.js | 1081 + .../goog/net/xpc/crosspagechannelrole.js | 30 + .../closure/goog/net/xpc/directtransport.js | 635 + .../goog/net/xpc/directtransport_test.js | 290 + .../net/xpc/frameelementmethodtransport.js | 270 + .../goog/net/xpc/iframepollingtransport.js | 984 + .../net/xpc/iframepollingtransport_test.html | 24 + .../net/xpc/iframepollingtransport_test.js | 289 + .../goog/net/xpc/iframerelaytransport.js | 409 + .../goog/net/xpc/nativemessagingtransport.js | 648 + .../xpc/nativemessagingtransport_test.html | 24 + .../net/xpc/nativemessagingtransport_test.js | 300 + .../closure/goog/net/xpc/nixtransport.js | 483 + .../closure/goog/net/xpc/relay.js | 73 + .../goog/net/xpc/testdata/access_checker.html | 29 + .../goog/net/xpc/testdata/inner_peer.html | 99 + .../closure/goog/net/xpc/transport.js | 105 + .../closure/goog/net/xpc/xpc.js | 300 + .../closure/goog/object/object.js | 686 + .../closure/goog/object/object_test.html | 22 + .../closure/goog/object/object_test.js | 530 + .../goog/positioning/absoluteposition.js | 73 + .../goog/positioning/abstractposition.js | 44 + .../goog/positioning/anchoredposition.js | 92 + .../positioning/anchoredposition_test.html | 28 + .../goog/positioning/anchoredposition_test.js | 68 + .../positioning/anchoredviewportposition.js | 189 + .../anchoredviewportposition_test.html | 28 + .../anchoredviewportposition_test.js | 165 + .../anchoredviewportposition_test_iframe.html | 35 + .../goog/positioning/clientposition.js | 89 + .../goog/positioning/clientposition_test.html | 23 + .../goog/positioning/clientposition_test.js | 124 + .../goog/positioning/menuanchoredposition.js | 66 + .../menuanchoredposition_test.html | 39 + .../positioning/menuanchoredposition_test.js | 95 + .../closure/goog/positioning/positioning.js | 607 + .../goog/positioning/positioning_test.html | 199 + .../goog/positioning/positioning_test.js | 1197 + .../positioning/positioning_test_iframe1.html | 16 + .../positioning/positioning_test_iframe2.html | 13 + .../positioning/positioning_test_quirk.html | 9 + .../positioning_test_standard.html | 13 + .../positioning/viewportclientposition.js | 124 + .../viewportclientposition_test.html | 31 + .../viewportclientposition_test.js | 178 + .../goog/positioning/viewportposition.js | 66 + .../closure/goog/promise/promise.js | 1004 + .../closure/goog/promise/promise_test.html | 18 + .../closure/goog/promise/promise_test.js | 1488 + .../closure/goog/promise/resolver.js | 48 + .../closure/goog/promise/testsuiteadapter.js | 74 + .../closure/goog/promise/thenable.js | 111 + .../closure/goog/proto/proto.js | 44 + .../closure/goog/proto/serializer.js | 70 + .../closure/goog/proto/serializer_test.html | 22 + .../closure/goog/proto/serializer_test.js | 38 + .../closure/goog/proto2/descriptor.js | 202 + .../closure/goog/proto2/descriptor_test.html | 24 + .../closure/goog/proto2/descriptor_test.js | 78 + .../closure/goog/proto2/fielddescriptor.js | 306 + .../goog/proto2/fielddescriptor_test.html | 24 + .../goog/proto2/fielddescriptor_test.js | 139 + .../closure/goog/proto2/lazydeserializer.js | 70 + .../closure/goog/proto2/message.js | 774 + .../closure/goog/proto2/message_test.html | 24 + .../closure/goog/proto2/message_test.js | 466 + .../closure/goog/proto2/objectserializer.js | 176 + .../goog/proto2/objectserializer_test.html | 24 + .../goog/proto2/objectserializer_test.js | 567 + .../closure/goog/proto2/package_test.pb.js | 167 + .../closure/goog/proto2/pbliteserializer.js | 199 + .../goog/proto2/pbliteserializer_test.html | 24 + .../goog/proto2/pbliteserializer_test.js | 499 + .../closure/goog/proto2/proto_test.html | 24 + .../closure/goog/proto2/proto_test.js | 755 + .../closure/goog/proto2/serializer.js | 182 + .../closure/goog/proto2/test.pb.js | 4028 + .../goog/proto2/textformatserializer.js | 1070 + .../proto2/textformatserializer_test.html | 21 + .../goog/proto2/textformatserializer_test.js | 807 + .../closure/goog/proto2/util.js | 54 + .../closure/goog/pubsub/pubsub.js | 335 + .../closure/goog/pubsub/pubsub_perf.html | 290 + .../closure/goog/pubsub/pubsub_test.html | 25 + .../closure/goog/pubsub/pubsub_test.js | 631 + .../closure/goog/pubsub/topicid.js | 61 + .../closure/goog/pubsub/typedpubsub.js | 126 + .../closure/goog/pubsub/typedpubsub_test.html | 22 + .../closure/goog/pubsub/typedpubsub_test.js | 663 + .../closure/goog/reflect/reflect.js | 78 + .../closure/goog/result/chain_test.html | 236 + .../closure/goog/result/combine_test.html | 229 + .../closure/goog/result/deferredadaptor.js | 59 + .../goog/result/deferredadaptor_test.html | 86 + .../closure/goog/result/dependentresult.js | 45 + .../closure/goog/result/result_interface.js | 119 + .../closure/goog/result/resultutil.js | 556 + .../closure/goog/result/resultutil_test.html | 46 + .../closure/goog/result/simpleresult.js | 260 + .../goog/result/simpleresult_test.html | 375 + .../closure/goog/result/transform_test.html | 153 + .../closure/goog/result/wait_test.html | 211 + .../closure-library/closure/goog/soy/data.js | 160 + .../closure/goog/soy/data_test.html | 22 + .../closure/goog/soy/data_test.js | 33 + .../closure/goog/soy/renderer.js | 314 + .../closure/goog/soy/renderer_test.html | 25 + .../closure/goog/soy/renderer_test.js | 209 + .../closure-library/closure/goog/soy/soy.js | 218 + .../closure/goog/soy/soy_test.html | 25 + .../closure/goog/soy/soy_test.js | 225 + .../closure/goog/soy/soy_testhelper.js | 181 + .../closure/goog/spell/spellcheck.js | 478 + .../closure/goog/spell/spellcheck_test.html | 22 + .../closure/goog/spell/spellcheck_test.js | 110 + .../closure/goog/stats/basicstat.js | 270 + .../closure/goog/stats/basicstat_test.html | 22 + .../closure/goog/stats/basicstat_test.js | 165 + .../goog/storage/collectablestorage.js | 131 + .../goog/storage/collectablestorage_test.html | 23 + .../goog/storage/collectablestorage_test.js | 38 + .../goog/storage/collectablestoragetester.js | 71 + .../closure/goog/storage/encryptedstorage.js | 202 + .../goog/storage/encryptedstorage_test.html | 23 + .../goog/storage/encryptedstorage_test.js | 167 + .../closure/goog/storage/errorcode.js | 30 + .../closure/goog/storage/expiringstorage.js | 140 + .../goog/storage/expiringstorage_test.html | 23 + .../goog/storage/expiringstorage_test.js | 85 + .../goog/storage/mechanism/errorcode.js | 31 + .../mechanism/errorhandlingmechanism.js | 130 + .../errorhandlingmechanism_test.html | 24 + .../mechanism/errorhandlingmechanism_test.js | 77 + .../storage/mechanism/html5localstorage.js | 45 + .../mechanism/html5localstorage_test.html | 23 + .../mechanism/html5localstorage_test.js | 58 + .../storage/mechanism/html5sessionstorage.js | 46 + .../mechanism/html5sessionstorage_test.html | 23 + .../mechanism/html5sessionstorage_test.js | 59 + .../goog/storage/mechanism/html5webstorage.js | 171 + .../mechanism/html5webstorage_test.html | 23 + .../storage/mechanism/html5webstorage_test.js | 120 + .../goog/storage/mechanism/ieuserdata.js | 284 + .../storage/mechanism/ieuserdata_test.html | 23 + .../goog/storage/mechanism/ieuserdata_test.js | 75 + .../storage/mechanism/iterablemechanism.js | 85 + .../mechanism/iterablemechanismtester.js | 117 + .../goog/storage/mechanism/mechanism.js | 56 + .../storage/mechanism/mechanismfactory.js | 112 + .../mechanism/mechanismfactory_test.html | 23 + .../mechanism/mechanismfactory_test.js | 49 + .../mechanism/mechanismseparationtester.js | 89 + .../mechanism/mechanismsharingtester.js | 85 + .../mechanism/mechanismtestdefinition.js | 29 + .../goog/storage/mechanism/mechanismtester.js | 199 + .../storage/mechanism/prefixedmechanism.js | 98 + .../mechanism/prefixedmechanism_test.html | 23 + .../mechanism/prefixedmechanism_test.js | 61 + .../closure/goog/storage/richstorage.js | 149 + .../goog/storage/richstorage_test.html | 23 + .../closure/goog/storage/richstorage_test.js | 82 + .../closure/goog/storage/storage.js | 96 + .../closure/goog/storage/storage_test.html | 64 + .../closure/goog/storage/storage_test.js | 59 + .../closure/goog/string/const.js | 182 + .../closure/goog/string/const_test.html | 19 + .../closure/goog/string/const_test.js | 51 + .../closure/goog/string/linkify.js | 252 + .../closure/goog/string/linkify_test.html | 22 + .../closure/goog/string/linkify_test.js | 455 + .../closure/goog/string/newlines.js | 154 + .../closure/goog/string/newlines_test.html | 19 + .../closure/goog/string/newlines_test.js | 87 + .../closure/goog/string/parser.js | 38 + .../closure/goog/string/path.js | 169 + .../closure/goog/string/path_test.html | 21 + .../closure/goog/string/path_test.js | 108 + .../closure/goog/string/string.js | 1565 + .../closure/goog/string/string_test.html | 19 + .../closure/goog/string/string_test.js | 1327 + .../closure/goog/string/stringbuffer.js | 103 + .../goog/string/stringbuffer_test.html | 22 + .../closure/goog/string/stringbuffer_test.js | 97 + .../closure/goog/string/stringformat.js | 251 + .../goog/string/stringformat_test.html | 22 + .../closure/goog/string/stringformat_test.js | 218 + .../closure/goog/string/stringifier.js | 38 + .../closure/goog/string/typedstring.js | 48 + .../closure/goog/structs/avltree.js | 899 + .../closure/goog/structs/avltree_test.html | 22 + .../closure/goog/structs/avltree_test.js | 363 + .../closure/goog/structs/circularbuffer.js | 216 + .../goog/structs/circularbuffer_test.html | 22 + .../goog/structs/circularbuffer_test.js | 101 + .../closure/goog/structs/collection.js | 56 + .../closure/goog/structs/collection_test.html | 21 + .../closure/goog/structs/collection_test.js | 53 + .../closure/goog/structs/heap.js | 334 + .../closure/goog/structs/heap_test.html | 22 + .../closure/goog/structs/heap_test.js | 215 + .../closure/goog/structs/inversionmap.js | 155 + .../goog/structs/inversionmap_test.html | 22 + .../closure/goog/structs/inversionmap_test.js | 154 + .../closure/goog/structs/linkedmap.js | 488 + .../closure/goog/structs/linkedmap_test.html | 22 + .../closure/goog/structs/linkedmap_test.js | 296 + .../closure/goog/structs/map.js | 460 + .../closure/goog/structs/map_test.html | 22 + .../closure/goog/structs/map_test.js | 426 + .../closure/goog/structs/node.js | 73 + .../closure/goog/structs/pool.js | 376 + .../closure/goog/structs/pool_test.html | 22 + .../closure/goog/structs/pool_test.js | 285 + .../closure/goog/structs/prioritypool.js | 182 + .../goog/structs/prioritypool_test.html | 22 + .../closure/goog/structs/prioritypool_test.js | 582 + .../closure/goog/structs/priorityqueue.js | 66 + .../goog/structs/priorityqueue_test.html | 22 + .../goog/structs/priorityqueue_test.js | 175 + .../closure/goog/structs/quadtree.js | 570 + .../closure/goog/structs/quadtree_test.html | 22 + .../closure/goog/structs/quadtree_test.js | 174 + .../closure/goog/structs/queue.js | 187 + .../closure/goog/structs/queue_perf.html | 126 + .../closure/goog/structs/queue_test.html | 22 + .../closure/goog/structs/queue_test.js | 161 + .../closure/goog/structs/set.js | 279 + .../closure/goog/structs/set_perf.html | 267 + .../closure/goog/structs/set_test.html | 22 + .../closure/goog/structs/set_test.js | 579 + .../closure/goog/structs/simplepool.js | 200 + .../closure/goog/structs/stringset.js | 405 + .../closure/goog/structs/stringset_test.html | 24 + .../closure/goog/structs/stringset_test.js | 259 + .../closure/goog/structs/structs.js | 354 + .../closure/goog/structs/structs_test.html | 53 + .../closure/goog/structs/structs_test.js | 1040 + .../closure/goog/structs/treenode.js | 455 + .../closure/goog/structs/treenode_test.html | 24 + .../closure/goog/structs/treenode_test.js | 384 + .../closure/goog/structs/trie.js | 395 + .../closure/goog/structs/trie_test.html | 24 + .../closure/goog/structs/trie_test.js | 454 + .../closure/goog/structs/weak/weak.js | 159 + .../closure/goog/structs/weak/weak_test.js | 75 + .../closure/goog/style/bidi.js | 184 + .../closure/goog/style/bidi_test.html | 139 + .../closure/goog/style/bidi_test.js | 135 + .../closure/goog/style/cursor.js | 116 + .../closure/goog/style/cursor_test.html | 25 + .../closure/goog/style/cursor_test.js | 125 + .../closure/goog/style/style.js | 2027 + .../style/style_document_scroll_test.html | 45 + .../goog/style/style_document_scroll_test.js | 188 + .../closure/goog/style/style_quirks_test.html | 538 + .../closure/goog/style/style_test.html | 526 + .../closure/goog/style/style_test.js | 2640 + .../goog/style/style_test_iframe_quirk.html | 27 + .../style/style_test_iframe_standard.html | 30 + .../closure/goog/style/style_test_quirk.html | 9 + .../closure/goog/style/style_test_rect.svg | 11 + .../goog/style/style_test_standard.html | 11 + .../style/style_webkit_scrollbars_test.html | 42 + .../style/style_webkit_scrollbars_test.js | 59 + .../goog/style/stylescrollbartester.js | 78 + .../closure/goog/style/transform.js | 169 + .../closure/goog/style/transform_test.js | 136 + .../closure/goog/style/transition.js | 132 + .../closure/goog/style/transition_test.html | 27 + .../closure/goog/style/transition_test.js | 119 + .../closure/goog/test_module.js | 27 + .../closure/goog/test_module_dep.js | 26 + .../closure/goog/testing/asserts.js | 1263 + .../closure/goog/testing/asserts_test.html | 22 + .../closure/goog/testing/asserts_test.js | 1181 + .../closure/goog/testing/async/mockcontrol.js | 175 + .../goog/testing/async/mockcontrol_test.html | 23 + .../goog/testing/async/mockcontrol_test.js | 217 + .../closure/goog/testing/asynctestcase.js | 900 + .../testing/asynctestcase_async_test.html | 31 + .../goog/testing/asynctestcase_async_test.js | 136 + .../testing/asynctestcase_noasync_test.html | 33 + .../testing/asynctestcase_noasync_test.js | 111 + .../goog/testing/asynctestcase_test.html | 22 + .../goog/testing/asynctestcase_test.js | 52 + .../closure/goog/testing/benchmark.js | 96 + .../goog/testing/continuationtestcase.js | 691 + .../testing/continuationtestcase_test.html | 25 + .../goog/testing/continuationtestcase_test.js | 346 + .../closure/goog/testing/deferredtestcase.js | 154 + .../goog/testing/deferredtestcase_test.html | 23 + .../goog/testing/deferredtestcase_test.js | 137 + .../closure/goog/testing/dom.js | 624 + .../closure/goog/testing/dom_test.html | 28 + .../closure/goog/testing/dom_test.js | 434 + .../closure/goog/testing/editor/dom.js | 293 + .../closure/goog/testing/editor/dom_test.html | 28 + .../closure/goog/testing/editor/dom_test.js | 290 + .../closure/goog/testing/editor/fieldmock.js | 116 + .../closure/goog/testing/editor/testhelper.js | 180 + .../goog/testing/editor/testhelper_test.html | 25 + .../goog/testing/editor/testhelper_test.js | 175 + .../goog/testing/events/eventobserver.js | 87 + .../testing/events/eventobserver_test.html | 22 + .../goog/testing/events/eventobserver_test.js | 67 + .../closure/goog/testing/events/events.js | 727 + .../goog/testing/events/events_test.html | 41 + .../goog/testing/events/events_test.js | 624 + .../closure/goog/testing/events/matchers.js | 42 + .../goog/testing/events/matchers_test.html | 25 + .../goog/testing/events/matchers_test.js | 35 + .../goog/testing/events/onlinehandler.js | 65 + .../testing/events/onlinehandler_test.html | 26 + .../goog/testing/events/onlinehandler_test.js | 84 + .../closure/goog/testing/expectedfailures.js | 237 + .../goog/testing/expectedfailures_test.html | 22 + .../goog/testing/expectedfailures_test.js | 121 + .../closure/goog/testing/fs/blob.js | 135 + .../closure/goog/testing/fs/blob_test.html | 23 + .../closure/goog/testing/fs/blob_test.js | 65 + .../goog/testing/fs/directoryentry_test.html | 23 + .../goog/testing/fs/directoryentry_test.js | 319 + .../closure/goog/testing/fs/entry.js | 637 + .../closure/goog/testing/fs/entry_test.html | 23 + .../closure/goog/testing/fs/entry_test.js | 222 + .../closure/goog/testing/fs/file.js | 53 + .../goog/testing/fs/fileentry_test.html | 23 + .../closure/goog/testing/fs/fileentry_test.js | 88 + .../closure/goog/testing/fs/filereader.js | 275 + .../goog/testing/fs/filereader_test.html | 23 + .../goog/testing/fs/filereader_test.js | 234 + .../closure/goog/testing/fs/filesystem.js | 64 + .../closure/goog/testing/fs/filewriter.js | 268 + .../goog/testing/fs/filewriter_test.html | 23 + .../goog/testing/fs/filewriter_test.js | 322 + .../closure/goog/testing/fs/fs.js | 169 + .../closure/goog/testing/fs/fs_test.html | 23 + .../closure/goog/testing/fs/fs_test.js | 55 + .../goog/testing/fs/integration_test.html | 25 + .../goog/testing/fs/integration_test.js | 221 + .../closure/goog/testing/fs/progressevent.js | 82 + .../closure/goog/testing/functionmock.js | 176 + .../goog/testing/functionmock_test.html | 27 + .../closure/goog/testing/functionmock_test.js | 503 + .../closure/goog/testing/graphics.js | 64 + .../closure/goog/testing/i18n/asserts.js | 77 + .../goog/testing/i18n/asserts_test.html | 19 + .../closure/goog/testing/i18n/asserts_test.js | 67 + .../closure/goog/testing/jsunit.js | 157 + .../closure/goog/testing/loosemock.js | 242 + .../closure/goog/testing/loosemock_test.html | 22 + .../closure/goog/testing/loosemock_test.js | 342 + .../testing/messaging/mockmessagechannel.js | 80 + .../testing/messaging/mockmessageevent.js | 102 + .../goog/testing/messaging/mockmessageport.js | 86 + .../goog/testing/messaging/mockportnetwork.js | 66 + .../closure/goog/testing/mock.js | 645 + .../closure/goog/testing/mock_test.html | 22 + .../closure/goog/testing/mock_test.js | 260 + .../closure/goog/testing/mockclassfactory.js | 585 + .../goog/testing/mockclassfactory_test.html | 22 + .../goog/testing/mockclassfactory_test.js | 238 + .../closure/goog/testing/mockclock.js | 591 + .../closure/goog/testing/mockclock_test.html | 22 + .../closure/goog/testing/mockclock_test.js | 610 + .../closure/goog/testing/mockcontrol.js | 220 + .../goog/testing/mockcontrol_test.html | 25 + .../closure/goog/testing/mockcontrol_test.js | 121 + .../closure/goog/testing/mockinterface.js | 45 + .../closure/goog/testing/mockmatchers.js | 400 + .../goog/testing/mockmatchers_test.html | 28 + .../closure/goog/testing/mockmatchers_test.js | 378 + .../closure/goog/testing/mockrandom.js | 153 + .../closure/goog/testing/mockrandom_test.html | 22 + .../closure/goog/testing/mockrandom_test.js | 73 + .../closure/goog/testing/mockrange.js | 67 + .../closure/goog/testing/mockrange_test.html | 25 + .../closure/goog/testing/mockrange_test.js | 35 + .../closure/goog/testing/mockstorage.js | 108 + .../goog/testing/mockstorage_test.html | 25 + .../closure/goog/testing/mockstorage_test.js | 73 + .../closure/goog/testing/mockuseragent.js | 143 + .../goog/testing/mockuseragent_test.html | 23 + .../goog/testing/mockuseragent_test.js | 73 + .../closure/goog/testing/multitestrunner.js | 1450 + .../closure/goog/testing/net/xhrio.js | 751 + .../closure/goog/testing/net/xhrio_test.html | 22 + .../closure/goog/testing/net/xhrio_test.js | 423 + .../closure/goog/testing/net/xhriopool.js | 65 + .../goog/testing/objectpropertystring.js | 68 + .../closure/goog/testing/performancetable.css | 46 + .../closure/goog/testing/performancetable.js | 191 + .../closure/goog/testing/performancetimer.js | 418 + .../goog/testing/performancetimer_test.html | 27 + .../goog/testing/performancetimer_test.js | 198 + .../closure/goog/testing/propertyreplacer.js | 245 + .../goog/testing/propertyreplacer_test.html | 22 + .../goog/testing/propertyreplacer_test.js | 364 + .../closure/goog/testing/proto2/proto2.js | 145 + .../goog/testing/proto2/proto2_test.html | 22 + .../goog/testing/proto2/proto2_test.js | 137 + .../closure/goog/testing/pseudorandom.js | 180 + .../goog/testing/pseudorandom_test.html | 22 + .../closure/goog/testing/pseudorandom_test.js | 94 + .../closure/goog/testing/recordfunction.js | 215 + .../goog/testing/recordfunction_test.html | 24 + .../goog/testing/recordfunction_test.js | 175 + .../closure/goog/testing/shardingtestcase.js | 124 + .../goog/testing/shardingtestcase_test.html | 22 + .../goog/testing/shardingtestcase_test.js | 31 + .../closure/goog/testing/singleton.js | 46 + .../closure/goog/testing/singleton_test.html | 24 + .../closure/goog/testing/singleton_test.js | 33 + .../closure/goog/testing/stacktrace.js | 594 + .../closure/goog/testing/stacktrace_test.html | 24 + .../closure/goog/testing/stacktrace_test.js | 375 + .../goog/testing/storage/fakemechanism.js | 68 + .../closure/goog/testing/strictmock.js | 130 + .../closure/goog/testing/strictmock_test.html | 22 + .../closure/goog/testing/strictmock_test.js | 423 + .../goog/testing/style/layoutasserts.js | 310 + .../testing/style/layoutasserts_test.html | 29 + .../goog/testing/style/layoutasserts_test.js | 257 + .../closure/goog/testing/style/style.js | 101 + .../goog/testing/style/style_test.html | 23 + .../closure/goog/testing/style/style_test.js | 157 + .../closure/goog/testing/testcase.js | 1476 + .../closure/goog/testing/testcase_test.js | 276 + .../closure/goog/testing/testqueue.js | 66 + .../closure/goog/testing/testrunner.js | 439 + .../goog/testing/ui/rendererasserts.js | 58 + .../goog/testing/ui/rendererasserts_test.html | 25 + .../goog/testing/ui/rendererasserts_test.js | 46 + .../goog/testing/ui/rendererharness.js | 177 + .../closure/goog/testing/ui/style.js | 140 + .../goog/testing/ui/style_reference.html | 27 + .../closure/goog/testing/ui/style_test.html | 75 + .../closure/goog/testing/ui/style_test.js | 73 + .../closure/goog/testing/watchers.js | 46 + .../closure/goog/timer/timer.js | 329 + .../closure/goog/timer/timer_test.html | 22 + .../closure/goog/timer/timer_test.js | 149 + .../closure/goog/tweak/entries.js | 1002 + .../closure/goog/tweak/entries_test.html | 25 + .../closure/goog/tweak/entries_test.js | 85 + .../closure/goog/tweak/registry.js | 315 + .../closure/goog/tweak/registry_test.html | 25 + .../closure/goog/tweak/registry_test.js | 137 + .../closure/goog/tweak/testhelpers.js | 123 + .../closure/goog/tweak/tweak.js | 301 + .../closure/goog/tweak/tweakui.js | 826 + .../closure/goog/tweak/tweakui_test.html | 27 + .../closure/goog/tweak/tweakui_test.js | 275 + .../closure/goog/ui/abstractspellchecker.js | 1229 + .../closure-library/closure/goog/ui/ac/ac.js | 50 + .../closure/goog/ui/ac/ac_test.html | 28 + .../closure/goog/ui/ac/ac_test.js | 209 + .../closure/goog/ui/ac/arraymatcher.js | 216 + .../closure/goog/ui/ac/arraymatcher_test.html | 22 + .../closure/goog/ui/ac/arraymatcher_test.js | 133 + .../closure/goog/ui/ac/autocomplete.js | 921 + .../closure/goog/ui/ac/autocomplete_test.html | 26 + .../closure/goog/ui/ac/autocomplete_test.js | 1678 + .../closure/goog/ui/ac/cachingmatcher.js | 273 + .../goog/ui/ac/cachingmatcher_test.html | 22 + .../closure/goog/ui/ac/cachingmatcher_test.js | 214 + .../closure/goog/ui/ac/inputhandler.js | 1327 + .../closure/goog/ui/ac/inputhandler_test.html | 25 + .../closure/goog/ui/ac/inputhandler_test.js | 716 + .../closure/goog/ui/ac/remote.js | 114 + .../closure/goog/ui/ac/remotearraymatcher.js | 274 + .../goog/ui/ac/remotearraymatcher_test.html | 24 + .../goog/ui/ac/remotearraymatcher_test.js | 67 + .../closure/goog/ui/ac/renderer.js | 1100 + .../closure/goog/ui/ac/renderer_test.html | 38 + .../closure/goog/ui/ac/renderer_test.js | 730 + .../closure/goog/ui/ac/renderoptions.js | 80 + .../closure/goog/ui/ac/richinputhandler.js | 58 + .../closure/goog/ui/ac/richremote.js | 107 + .../goog/ui/ac/richremotearraymatcher.js | 125 + .../closure/goog/ui/activitymonitor.js | 348 + .../closure/goog/ui/activitymonitor_test.html | 25 + .../closure/goog/ui/activitymonitor_test.js | 147 + .../closure/goog/ui/advancedtooltip.js | 367 + .../closure/goog/ui/advancedtooltip_test.html | 33 + .../closure/goog/ui/advancedtooltip_test.js | 287 + .../closure/goog/ui/animatedzippy.js | 200 + .../closure/goog/ui/animatedzippy_test.html | 57 + .../closure/goog/ui/animatedzippy_test.js | 93 + .../closure/goog/ui/attachablemenu.js | 476 + .../closure/goog/ui/bidiinput.js | 180 + .../closure/goog/ui/bidiinput_test.html | 31 + .../closure/goog/ui/bidiinput_test.js | 122 + .../closure-library/closure/goog/ui/bubble.js | 473 + .../closure-library/closure/goog/ui/button.js | 214 + .../closure/goog/ui/button_perf.html | 176 + .../closure/goog/ui/button_test.html | 34 + .../closure/goog/ui/button_test.js | 283 + .../closure/goog/ui/buttonrenderer.js | 219 + .../closure/goog/ui/buttonrenderer_test.html | 27 + .../closure/goog/ui/buttonrenderer_test.js | 242 + .../closure/goog/ui/buttonside.js | 41 + .../closure/goog/ui/charcounter.js | 199 + .../closure/goog/ui/charcounter_test.html | 29 + .../closure/goog/ui/charcounter_test.js | 218 + .../closure/goog/ui/charpicker.js | 922 + .../closure/goog/ui/charpicker_test.html | 26 + .../closure/goog/ui/charpicker_test.js | 87 + .../closure/goog/ui/checkbox.js | 272 + .../closure/goog/ui/checkbox_test.html | 38 + .../closure/goog/ui/checkbox_test.js | 451 + .../closure/goog/ui/checkboxmenuitem.js | 53 + .../closure/goog/ui/checkboxrenderer.js | 190 + .../closure/goog/ui/colorbutton.js | 59 + .../closure/goog/ui/colorbutton_test.html | 26 + .../closure/goog/ui/colorbutton_test.js | 64 + .../closure/goog/ui/colorbuttonrenderer.js | 75 + .../closure/goog/ui/colormenubutton.js | 215 + .../goog/ui/colormenubuttonrenderer.js | 147 + .../goog/ui/colormenubuttonrenderer_test.html | 31 + .../goog/ui/colormenubuttonrenderer_test.js | 71 + .../closure/goog/ui/colorpalette.js | 178 + .../closure/goog/ui/colorpalette_test.html | 25 + .../closure/goog/ui/colorpalette_test.js | 169 + .../closure/goog/ui/colorpicker.js | 345 + .../closure/goog/ui/colorsplitbehavior.js | 61 + .../closure/goog/ui/combobox.js | 987 + .../closure/goog/ui/combobox_test.html | 48 + .../closure/goog/ui/combobox_test.js | 271 + .../closure/goog/ui/component.js | 1297 + .../closure/goog/ui/component_test.html | 27 + .../closure/goog/ui/component_test.js | 892 + .../closure/goog/ui/container.js | 1354 + .../closure/goog/ui/container_perf.html | 55 + .../closure/goog/ui/container_test.html | 27 + .../closure/goog/ui/container_test.js | 612 + .../closure/goog/ui/containerrenderer.js | 374 + .../goog/ui/containerrenderer_test.html | 27 + .../closure/goog/ui/containerrenderer_test.js | 225 + .../closure/goog/ui/containerscroller.js | 223 + .../goog/ui/containerscroller_test.html | 74 + .../closure/goog/ui/containerscroller_test.js | 182 + .../closure/goog/ui/control.js | 1423 + .../closure/goog/ui/control_perf.html | 166 + .../closure/goog/ui/control_test.html | 27 + .../closure/goog/ui/control_test.js | 2383 + .../closure/goog/ui/controlcontent.js | 28 + .../closure/goog/ui/controlrenderer.js | 947 + .../closure/goog/ui/controlrenderer_test.html | 27 + .../closure/goog/ui/controlrenderer_test.js | 1194 + .../closure/goog/ui/cookieeditor.js | 185 + .../closure/goog/ui/cookieeditor_test.html | 25 + .../closure/goog/ui/cookieeditor_test.js | 104 + .../closure/goog/ui/css3buttonrenderer.js | 154 + .../closure/goog/ui/css3menubuttonrenderer.js | 146 + .../closure/goog/ui/cssnames.js | 29 + .../closure/goog/ui/custombutton.js | 58 + .../closure/goog/ui/custombuttonrenderer.js | 270 + .../closure/goog/ui/customcolorpalette.js | 140 + .../goog/ui/customcolorpalette_test.html | 28 + .../goog/ui/customcolorpalette_test.js | 55 + .../closure/goog/ui/datepicker.js | 1549 + .../closure/goog/ui/datepicker_test.html | 26 + .../closure/goog/ui/datepicker_test.js | 362 + .../closure/goog/ui/datepickerrenderer.js | 55 + .../closure/goog/ui/decorate.js | 38 + .../closure/goog/ui/decorate_test.html | 33 + .../closure/goog/ui/decorate_test.js | 102 + .../goog/ui/defaultdatepickerrenderer.js | 202 + .../closure-library/closure/goog/ui/dialog.js | 1603 + .../closure/goog/ui/dialog_test.html | 26 + .../closure/goog/ui/dialog_test.js | 844 + .../closure/goog/ui/dimensionpicker.js | 318 + .../closure/goog/ui/dimensionpicker_test.html | 29 + .../closure/goog/ui/dimensionpicker_test.js | 249 + .../goog/ui/dimensionpickerrenderer.js | 420 + .../goog/ui/dimensionpickerrenderer_test.html | 22 + .../goog/ui/dimensionpickerrenderer_test.js | 55 + .../closure/goog/ui/dragdropdetector.js | 647 + .../closure/goog/ui/drilldownrow.js | 494 + .../closure/goog/ui/drilldownrow_test.html | 65 + .../closure/goog/ui/drilldownrow_test.js | 47 + .../closure/goog/ui/editor/abstractdialog.js | 444 + .../goog/ui/editor/abstractdialog_test.html | 28 + .../goog/ui/editor/abstractdialog_test.js | 483 + .../closure/goog/ui/editor/bubble.js | 559 + .../closure/goog/ui/editor/bubble_test.html | 30 + .../closure/goog/ui/editor/bubble_test.js | 255 + .../closure/goog/ui/editor/defaulttoolbar.js | 1066 + .../closure/goog/ui/editor/linkdialog.js | 1063 + .../goog/ui/editor/linkdialog_test.html | 30 + .../closure/goog/ui/editor/linkdialog_test.js | 666 + .../closure/goog/ui/editor/messages.js | 148 + .../closure/goog/ui/editor/tabpane.js | 201 + .../goog/ui/editor/toolbarcontroller.js | 296 + .../closure/goog/ui/editor/toolbarfactory.js | 439 + .../goog/ui/editor/toolbarfactory_test.html | 28 + .../goog/ui/editor/toolbarfactory_test.js | 73 + .../closure/goog/ui/emoji/emoji.js | 73 + .../closure/goog/ui/emoji/emojipalette.js | 289 + .../goog/ui/emoji/emojipaletterenderer.js | 209 + .../closure/goog/ui/emoji/emojipicker.js | 812 + .../goog/ui/emoji/emojipicker_test.html | 25 + .../closure/goog/ui/emoji/emojipicker_test.js | 910 + .../fast_nonprogressive_emojipicker_test.html | 23 + .../fast_nonprogressive_emojipicker_test.js | 248 + .../fast_progressive_emojipicker_test.html | 23 + .../fast_progressive_emojipicker_test.js | 248 + .../closure/goog/ui/emoji/popupemojipicker.js | 411 + .../goog/ui/emoji/popupemojipicker_test.html | 26 + .../goog/ui/emoji/popupemojipicker_test.js | 71 + .../emoji/progressiveemojipaletterenderer.js | 99 + .../closure/goog/ui/emoji/spriteinfo.js | 213 + .../goog/ui/emoji/spriteinfo_test.html | 22 + .../closure/goog/ui/emoji/spriteinfo_test.js | 45 + .../closure/goog/ui/filteredmenu.js | 633 + .../closure/goog/ui/filteredmenu_test.html | 61 + .../closure/goog/ui/filteredmenu_test.js | 308 + .../goog/ui/filterobservingmenuitem.js | 98 + .../ui/filterobservingmenuitemrenderer.js | 63 + .../closure/goog/ui/flatbuttonrenderer.js | 147 + .../closure/goog/ui/flatmenubuttonrenderer.js | 208 + .../closure/goog/ui/formpost.js | 108 + .../closure/goog/ui/formpost_test.html | 24 + .../closure/goog/ui/formpost_test.js | 109 + .../closure-library/closure/goog/ui/gauge.js | 1012 + .../closure/goog/ui/gaugetheme.js | 170 + .../closure/goog/ui/hovercard.js | 458 + .../closure/goog/ui/hovercard_test.html | 55 + .../closure/goog/ui/hovercard_test.js | 355 + .../closure/goog/ui/hsvapalette.js | 295 + .../closure/goog/ui/hsvapalette_test.html | 28 + .../closure/goog/ui/hsvapalette_test.js | 144 + .../closure/goog/ui/hsvpalette.js | 524 + .../closure/goog/ui/hsvpalette_test.html | 34 + .../closure/goog/ui/hsvpalette_test.js | 208 + .../closure/goog/ui/idgenerator.js | 48 + .../closure/goog/ui/idletimer.js | 300 + .../closure/goog/ui/idletimer_test.html | 22 + .../closure/goog/ui/idletimer_test.js | 97 + .../closure/goog/ui/iframemask.js | 258 + .../closure/goog/ui/iframemask_test.html | 38 + .../closure/goog/ui/iframemask_test.js | 214 + .../goog/ui/imagelessbuttonrenderer.js | 207 + .../goog/ui/imagelessmenubuttonrenderer.js | 210 + .../closure/goog/ui/inputdatepicker.js | 339 + .../closure/goog/ui/inputdatepicker_test.html | 29 + .../closure/goog/ui/inputdatepicker_test.js | 130 + .../closure/goog/ui/itemevent.js | 51 + .../goog/ui/keyboardshortcuthandler.js | 1158 + .../goog/ui/keyboardshortcuthandler_test.html | 54 + .../goog/ui/keyboardshortcuthandler_test.js | 737 + .../closure/goog/ui/labelinput.js | 612 + .../closure/goog/ui/labelinput_test.html | 24 + .../closure/goog/ui/labelinput_test.js | 250 + .../closure/goog/ui/linkbuttonrenderer.js | 67 + .../closure/goog/ui/media/flashobject.js | 631 + .../goog/ui/media/flashobject_test.html | 24 + .../closure/goog/ui/media/flashobject_test.js | 347 + .../closure/goog/ui/media/flickr.js | 314 + .../closure/goog/ui/media/flickr_test.html | 24 + .../closure/goog/ui/media/flickr_test.js | 106 + .../closure/goog/ui/media/googlevideo.js | 283 + .../goog/ui/media/googlevideo_test.html | 24 + .../closure/goog/ui/media/googlevideo_test.js | 94 + .../closure/goog/ui/media/media.js | 288 + .../closure/goog/ui/media/media_test.html | 24 + .../closure/goog/ui/media/media_test.js | 130 + .../closure/goog/ui/media/mediamodel.js | 978 + .../goog/ui/media/mediamodel_test.html | 25 + .../closure/goog/ui/media/mediamodel_test.js | 95 + .../closure/goog/ui/media/mp3.js | 226 + .../closure/goog/ui/media/mp3_test.html | 24 + .../closure/goog/ui/media/mp3_test.js | 66 + .../closure/goog/ui/media/photo.js | 143 + .../closure/goog/ui/media/photo_test.html | 24 + .../closure/goog/ui/media/photo_test.js | 49 + .../closure/goog/ui/media/picasa.js | 327 + .../closure/goog/ui/media/picasa_test.html | 24 + .../closure/goog/ui/media/picasa_test.js | 110 + .../closure/goog/ui/media/vimeo.js | 278 + .../closure/goog/ui/media/vimeo_test.html | 24 + .../closure/goog/ui/media/vimeo_test.js | 91 + .../closure/goog/ui/media/youtube.js | 358 + .../closure/goog/ui/media/youtube_test.html | 24 + .../closure/goog/ui/media/youtube_test.js | 259 + .../closure-library/closure/goog/ui/menu.js | 477 + .../closure/goog/ui/menu_test.html | 89 + .../closure/goog/ui/menu_test.js | 114 + .../closure/goog/ui/menubar.js | 44 + .../closure/goog/ui/menubardecorator.js | 35 + .../closure/goog/ui/menubarrenderer.js | 68 + .../closure/goog/ui/menubase.js | 190 + .../closure/goog/ui/menubutton.js | 1052 + .../closure/goog/ui/menubutton_test.html | 60 + .../closure/goog/ui/menubutton_test.js | 843 + .../goog/ui/menubutton_test_frame.html | 41 + .../closure/goog/ui/menubuttonrenderer.js | 191 + .../goog/ui/menubuttonrenderer_test.html | 35 + .../goog/ui/menubuttonrenderer_test.js | 168 + .../closure/goog/ui/menuheader.js | 62 + .../closure/goog/ui/menuheaderrenderer.js | 54 + .../closure/goog/ui/menuitem.js | 322 + .../closure/goog/ui/menuitem_test.html | 29 + .../closure/goog/ui/menuitem_test.js | 583 + .../closure/goog/ui/menuitemrenderer.js | 354 + .../goog/ui/menuitemrenderer_test.html | 27 + .../closure/goog/ui/menuitemrenderer_test.js | 243 + .../closure/goog/ui/menurenderer.js | 114 + .../closure/goog/ui/menuseparator.js | 52 + .../closure/goog/ui/menuseparatorrenderer.js | 112 + .../goog/ui/menuseparatorrenderer_test.html | 31 + .../goog/ui/menuseparatorrenderer_test.js | 43 + .../closure/goog/ui/mockactivitymonitor.js | 72 + .../goog/ui/mockactivitymonitor_test.html | 19 + .../goog/ui/mockactivitymonitor_test.js | 94 + .../closure/goog/ui/modalpopup.js | 747 + .../closure/goog/ui/modalpopup_test.html | 27 + .../closure/goog/ui/modalpopup_test.js | 439 + .../closure/goog/ui/nativebuttonrenderer.js | 209 + .../goog/ui/nativebuttonrenderer_test.html | 27 + .../goog/ui/nativebuttonrenderer_test.js | 217 + .../closure-library/closure/goog/ui/option.js | 68 + .../closure/goog/ui/palette.js | 604 + .../closure/goog/ui/palette_test.html | 24 + .../closure/goog/ui/palette_test.js | 188 + .../closure/goog/ui/paletterenderer.js | 383 + .../closure/goog/ui/paletterenderer_test.html | 29 + .../closure/goog/ui/paletterenderer_test.js | 91 + .../closure/goog/ui/plaintextspellchecker.js | 641 + .../goog/ui/plaintextspellchecker_test.html | 42 + .../goog/ui/plaintextspellchecker_test.js | 439 + .../closure-library/closure/goog/ui/popup.js | 337 + .../closure/goog/ui/popup_test.html | 36 + .../closure/goog/ui/popup_test.js | 120 + .../closure/goog/ui/popupbase.js | 892 + .../closure/goog/ui/popupbase_test.html | 47 + .../closure/goog/ui/popupbase_test.js | 485 + .../closure/goog/ui/popupcolorpicker.js | 475 + .../goog/ui/popupcolorpicker_test.html | 27 + .../closure/goog/ui/popupcolorpicker_test.js | 66 + .../closure/goog/ui/popupdatepicker.js | 288 + .../closure/goog/ui/popupdatepicker_test.html | 22 + .../closure/goog/ui/popupdatepicker_test.js | 67 + .../closure/goog/ui/popupmenu.js | 558 + .../closure/goog/ui/popupmenu_test.html | 24 + .../closure/goog/ui/popupmenu_test.js | 313 + .../closure/goog/ui/progressbar.js | 407 + .../closure-library/closure/goog/ui/prompt.js | 409 + .../closure/goog/ui/prompt_test.html | 25 + .../closure/goog/ui/prompt_test.js | 139 + .../closure/goog/ui/rangemodel.js | 303 + .../closure/goog/ui/rangemodel_test.html | 22 + .../closure/goog/ui/rangemodel_test.js | 266 + .../closure/goog/ui/ratings.js | 508 + .../closure/goog/ui/registry.js | 172 + .../closure/goog/ui/registry_test.html | 31 + .../closure/goog/ui/registry_test.js | 230 + .../closure/goog/ui/richtextspellchecker.js | 780 + .../goog/ui/richtextspellchecker_test.html | 38 + .../goog/ui/richtextspellchecker_test.js | 367 + .../closure/goog/ui/roundedpanel.js | 630 + .../closure/goog/ui/roundedpanel_test.html | 26 + .../closure/goog/ui/roundedpanel_test.js | 54 + .../closure/goog/ui/roundedtabrenderer.js | 198 + .../closure/goog/ui/scrollfloater.js | 636 + .../closure/goog/ui/scrollfloater_test.html | 29 + .../closure/goog/ui/scrollfloater_test.js | 136 + .../closure-library/closure/goog/ui/select.js | 526 + .../closure/goog/ui/select_test.html | 26 + .../closure/goog/ui/select_test.js | 329 + .../closure/goog/ui/selectionmenubutton.js | 300 + .../goog/ui/selectionmenubutton_test.html | 54 + .../goog/ui/selectionmenubutton_test.js | 201 + .../closure/goog/ui/selectionmodel.js | 301 + .../closure/goog/ui/selectionmodel_test.html | 23 + .../closure/goog/ui/selectionmodel_test.js | 220 + .../closure/goog/ui/separator.js | 80 + .../closure/goog/ui/serverchart.js | 1839 + .../closure/goog/ui/serverchart_test.html | 24 + .../closure/goog/ui/serverchart_test.js | 635 + .../closure-library/closure/goog/ui/slider.js | 136 + .../closure/goog/ui/sliderbase.js | 1672 + .../closure/goog/ui/sliderbase_test.html | 67 + .../closure/goog/ui/sliderbase_test.js | 948 + .../closure/goog/ui/splitbehavior.js | 342 + .../closure/goog/ui/splitbehavior_test.html | 26 + .../closure/goog/ui/splitbehavior_test.js | 154 + .../closure/goog/ui/splitpane.js | 909 + .../closure/goog/ui/splitpane_test.html | 26 + .../closure/goog/ui/splitpane_test.js | 223 + .../goog/ui/style/app/buttonrenderer.js | 202 + .../ui/style/app/buttonrenderer_test.html | 43 + .../goog/ui/style/app/buttonrenderer_test.js | 130 + .../goog/ui/style/app/menubuttonrenderer.js | 233 + .../ui/style/app/menubuttonrenderer_test.html | 48 + .../ui/style/app/menubuttonrenderer_test.js | 136 + .../style/app/primaryactionbuttonrenderer.js | 89 + .../app/primaryactionbuttonrenderer_test.html | 27 + .../app/primaryactionbuttonrenderer_test.js | 69 + .../closure/goog/ui/submenu.js | 671 + .../closure/goog/ui/submenu_test.html | 102 + .../closure/goog/ui/submenu_test.js | 547 + .../closure/goog/ui/submenurenderer.js | 239 + .../closure-library/closure/goog/ui/tab.js | 103 + .../closure/goog/ui/tab_test.html | 27 + .../closure/goog/ui/tab_test.js | 74 + .../closure-library/closure/goog/ui/tabbar.js | 395 + .../closure/goog/ui/tabbar_test.html | 27 + .../closure/goog/ui/tabbar_test.js | 606 + .../closure/goog/ui/tabbarrenderer.js | 165 + .../closure/goog/ui/tabbarrenderer_test.html | 27 + .../closure/goog/ui/tabbarrenderer_test.js | 132 + .../closure/goog/ui/tablesorter.js | 324 + .../closure/goog/ui/tablesorter_test.html | 80 + .../closure/goog/ui/tablesorter_test.js | 229 + .../closure/goog/ui/tabpane.js | 680 + .../closure/goog/ui/tabpane_test.html | 24 + .../closure/goog/ui/tabpane_test.js | 100 + .../closure/goog/ui/tabrenderer.js | 153 + .../closure/goog/ui/tabrenderer_test.html | 27 + .../closure/goog/ui/tabrenderer_test.js | 140 + .../closure/goog/ui/textarea.js | 736 + .../closure/goog/ui/textarea_test.html | 44 + .../closure/goog/ui/textarea_test.js | 348 + .../closure/goog/ui/textarearenderer.js | 170 + .../closure/goog/ui/togglebutton.js | 58 + .../closure/goog/ui/toolbar.js | 59 + .../closure/goog/ui/toolbar_test.html | 26 + .../closure/goog/ui/toolbar_test.js | 92 + .../closure/goog/ui/toolbarbutton.js | 54 + .../closure/goog/ui/toolbarbuttonrenderer.js | 57 + .../closure/goog/ui/toolbarcolormenubutton.js | 57 + .../goog/ui/toolbarcolormenubuttonrenderer.js | 101 + .../toolbarcolormenubuttonrenderer_test.html | 33 + .../ui/toolbarcolormenubuttonrenderer_test.js | 50 + .../closure/goog/ui/toolbarmenubutton.js | 56 + .../goog/ui/toolbarmenubuttonrenderer.js | 57 + .../closure/goog/ui/toolbarrenderer.js | 89 + .../closure/goog/ui/toolbarselect.js | 55 + .../closure/goog/ui/toolbarseparator.js | 53 + .../goog/ui/toolbarseparatorrenderer.js | 94 + .../ui/toolbarseparatorrenderer_test.html | 31 + .../goog/ui/toolbarseparatorrenderer_test.js | 67 + .../closure/goog/ui/toolbartogglebutton.js | 53 + .../closure/goog/ui/tooltip.js | 1017 + .../closure/goog/ui/tooltip_test.html | 25 + .../closure/goog/ui/tooltip_test.js | 394 + .../closure/goog/ui/tree/basenode.js | 1581 + .../closure/goog/ui/tree/basenode_test.html | 25 + .../closure/goog/ui/tree/basenode_test.js | 290 + .../closure/goog/ui/tree/treecontrol.js | 642 + .../goog/ui/tree/treecontrol_test.html | 26 + .../closure/goog/ui/tree/treecontrol_test.js | 106 + .../closure/goog/ui/tree/treenode.js | 100 + .../closure/goog/ui/tree/typeahead.js | 332 + .../closure/goog/ui/tree/typeahead_test.html | 26 + .../closure/goog/ui/tree/typeahead_test.js | 127 + .../closure/goog/ui/tristatemenuitem.js | 196 + .../goog/ui/tristatemenuitemrenderer.js | 92 + .../closure/goog/ui/twothumbslider.js | 158 + .../closure/goog/ui/twothumbslider_test.html | 25 + .../closure/goog/ui/twothumbslider_test.js | 34 + .../closure-library/closure/goog/ui/zippy.js | 461 + .../closure/goog/ui/zippy_test.html | 57 + .../closure/goog/ui/zippy_test.js | 267 + .../closure-library/closure/goog/uri/uri.js | 1526 + .../closure/goog/uri/uri_test.html | 21 + .../closure/goog/uri/uri_test.js | 1096 + .../closure-library/closure/goog/uri/utils.js | 1116 + .../closure/goog/uri/utils_test.html | 28 + .../closure/goog/uri/utils_test.js | 601 + .../closure/goog/useragent/adobereader.js | 90 + .../goog/useragent/adobereader_test.html | 22 + .../goog/useragent/adobereader_test.js | 29 + .../closure/goog/useragent/flash.js | 156 + .../closure/goog/useragent/flash_test.html | 22 + .../closure/goog/useragent/flash_test.js | 29 + .../closure/goog/useragent/iphoto.js | 87 + .../closure/goog/useragent/jscript.js | 95 + .../closure/goog/useragent/jscript_test.html | 41 + .../closure/goog/useragent/jscript_test.js | 54 + .../closure/goog/useragent/keyboard.js | 49 + .../closure/goog/useragent/keyboard_test.js | 225 + .../closure/goog/useragent/picasa.js | 112 + .../closure/goog/useragent/platform.js | 83 + .../closure/goog/useragent/platform_test.html | 26 + .../closure/goog/useragent/platform_test.js | 159 + .../closure/goog/useragent/product.js | 253 + .../goog/useragent/product_isversion.js | 142 + .../closure/goog/useragent/product_test.html | 30 + .../closure/goog/useragent/product_test.js | 380 + .../closure/goog/useragent/useragent.js | 519 + .../goog/useragent/useragent_quirks_test.html | 21 + .../goog/useragent/useragent_quirks_test.js | 25 + .../goog/useragent/useragent_test.html | 22 + .../closure/goog/useragent/useragent_test.js | 290 + .../goog/useragent/useragenttestutil.js | 116 + .../closure/goog/vec/float32array.js | 111 + .../closure/goog/vec/float32array_test.html | 69 + .../closure/goog/vec/float64array.js | 118 + .../closure/goog/vec/float64array_test.html | 69 + .../closure-library/closure/goog/vec/mat3.js | 1211 + .../closure/goog/vec/mat3_test.html | 465 + .../closure-library/closure/goog/vec/mat3d.js | 1039 + .../closure/goog/vec/mat3d_test.html | 432 + .../closure-library/closure/goog/vec/mat3f.js | 1039 + .../closure/goog/vec/mat3f_test.html | 432 + .../closure-library/closure/goog/vec/mat4.js | 1822 + .../closure/goog/vec/mat4_test.html | 781 + .../closure-library/closure/goog/vec/mat4d.js | 1769 + .../closure/goog/vec/mat4d_test.html | 738 + .../closure-library/closure/goog/vec/mat4f.js | 1769 + .../closure/goog/vec/mat4f_test.html | 738 + .../closure/goog/vec/matrix3.js | 720 + .../closure/goog/vec/matrix3_test.html | 301 + .../closure/goog/vec/matrix4.js | 1405 + .../closure/goog/vec/matrix4_test.html | 626 + .../closure/goog/vec/quaternion.js | 458 + .../closure/goog/vec/quaternion_test.html | 195 + .../closure-library/closure/goog/vec/ray.js | 95 + .../closure/goog/vec/ray_test.html | 66 + .../closure-library/closure/goog/vec/vec.js | 73 + .../closure-library/closure/goog/vec/vec2.js | 439 + .../closure/goog/vec/vec2_test.html | 254 + .../closure-library/closure/goog/vec/vec2d.js | 424 + .../closure/goog/vec/vec2d_test.html | 282 + .../closure-library/closure/goog/vec/vec2f.js | 424 + .../closure/goog/vec/vec2f_test.html | 282 + .../closure-library/closure/goog/vec/vec3.js | 542 + .../closure/goog/vec/vec3_test.html | 296 + .../closure-library/closure/goog/vec/vec3d.js | 426 + .../closure/goog/vec/vec3d_test.html | 270 + .../closure-library/closure/goog/vec/vec3f.js | 426 + .../closure/goog/vec/vec3f_test.html | 270 + .../closure-library/closure/goog/vec/vec4.js | 479 + .../closure/goog/vec/vec4_test.html | 230 + .../closure-library/closure/goog/vec/vec4d.js | 366 + .../closure/goog/vec/vec4d_test.html | 206 + .../closure-library/closure/goog/vec/vec4f.js | 366 + .../closure/goog/vec/vec4f_test.html | 206 + .../closure/goog/vec/vec_array_perf.html | 443 + .../closure/goog/vec/vec_perf.html | 101 + .../closure/goog/webgl/webgl.js | 2194 + .../closure/goog/window/window.js | 225 + .../closure/goog/window/window_test.html | 44 + .../closure/goog/window/window_test.js | 217 + mapbox-gl/closure-library/shamcs.py | 63 + .../third_party/closure/goog/base.js | 2 + .../goog/caja/string/html/htmlparser.js | 611 + .../goog/caja/string/html/htmlsanitizer.js | 605 + .../third_party/closure/goog/deps.js | 20 + .../closure/goog/dojo/dom/query.js | 1545 + .../closure/goog/dojo/dom/query_test.html | 64 + .../closure/goog/dojo/dom/query_test.js | 173 + .../goog/jpeg_encoder/jpeg_encoder_basic.js | 751 + .../goog/loremipsum/text/loremipsum.js | 712 + .../goog/loremipsum/text/loremipsum_test.html | 60 + .../closure/goog/mochikit/async/deferred.js | 924 + .../mochikit/async/deferred_async_test.html | 145 + .../goog/mochikit/async/deferred_test.html | 1070 + .../goog/mochikit/async/deferredlist.js | 206 + .../mochikit/async/deferredlist_test.html | 504 + .../third_party/closure/goog/osapi/osapi.js | 95 + .../third_party/closure/goog/svgpan/svgpan.js | 425 + mapbox-gl/css/ol.css | 1 + mapbox-gl/examples/Jugl.js | 8 + mapbox-gl/examples/accessible-require.js | 5 + mapbox-gl/examples/accessible.html | 72 + mapbox-gl/examples/accessible.js | 25 + mapbox-gl/examples/animation-require.js | 7 + mapbox-gl/examples/animation.html | 65 + mapbox-gl/examples/animation.js | 179 + mapbox-gl/examples/arcgis-tiled-require.js | 5 + mapbox-gl/examples/arcgis-tiled.html | 51 + mapbox-gl/examples/arcgis-tiled.js | 22 + mapbox-gl/examples/attributions-require.js | 6 + mapbox-gl/examples/attributions.html | 51 + mapbox-gl/examples/attributions.js | 26 + mapbox-gl/examples/bind-input-require.js | 7 + mapbox-gl/examples/bind-input.html | 92 + mapbox-gl/examples/bind-input.js | 60 + mapbox-gl/examples/bing-maps-require.js | 4 + mapbox-gl/examples/bing-maps.html | 60 + mapbox-gl/examples/bing-maps.js | 43 + mapbox-gl/examples/box-selection-require.js | 11 + mapbox-gl/examples/box-selection.html | 59 + mapbox-gl/examples/box-selection.js | 67 + .../examples/brightness-contrast-require.js | 5 + mapbox-gl/examples/brightness-contrast.html | 74 + mapbox-gl/examples/brightness-contrast.js | 68 + mapbox-gl/examples/button-title-require.js | 4 + mapbox-gl/examples/button-title.html | 62 + mapbox-gl/examples/button-title.js | 22 + mapbox-gl/examples/canvas-tiles-require.js | 8 + mapbox-gl/examples/canvas-tiles.html | 52 + mapbox-gl/examples/canvas-tiles.js | 27 + mapbox-gl/examples/center-require.js | 13 + mapbox-gl/examples/center.html | 131 + mapbox-gl/examples/center.js | 119 + mapbox-gl/examples/cluster-require.js | 14 + mapbox-gl/examples/cluster.html | 51 + mapbox-gl/examples/cluster.js | 64 + mapbox-gl/examples/custom-controls-require.js | 7 + mapbox-gl/examples/custom-controls.html | 64 + mapbox-gl/examples/custom-controls.js | 72 + mapbox-gl/examples/d3-require.js | 9 + mapbox-gl/examples/d3.html | 54 + mapbox-gl/examples/d3.js | 83 + mapbox-gl/examples/data/Butterfly.png | Bin 0 -> 62674 bytes .../examples/data/IGNWMTSCapabilities.xml | 1 + mapbox-gl/examples/data/WMTSCapabilities.xml | 279 + mapbox-gl/examples/data/arrow.png | Bin 0 -> 398 bytes .../data/geojson/countries-110m.geojson | 176 + .../examples/data/geojson/countries.geojson | 181 + .../data/geojson/line-samples.geojson | 16 + .../data/geojson/point-samples.geojson | 16 + .../data/geojson/polygon-samples.geojson | 16 + .../examples/data/geojson/switzerland.geojson | 4 + .../data/geolocation-orientation.json | 729 + .../examples/data/geolocation_marker.png | Bin 0 -> 829 bytes .../data/geolocation_marker_heading.png | Bin 0 -> 1673 bytes .../examples/data/gml/topp-states-wfs.xml | 2 + mapbox-gl/examples/data/gpx/fells_loop.gpx | 1077 + mapbox-gl/examples/data/icon.png | Bin 0 -> 2532 bytes .../examples/data/igc/Clement-Latour.igc | 15585 ++ .../examples/data/igc/Damien-de-Baenst.igc | 3319 + .../examples/data/igc/Sylvain-Dhonneur.igc | 16374 ++ mapbox-gl/examples/data/igc/Tom-Payne.igc | 9879 ++ mapbox-gl/examples/data/igc/Ulrich-Prinz.igc | 4611 + mapbox-gl/examples/data/kml/2012-02-10.kml | 9391 ++ .../data/kml/2012_Earthquakes_Mag5.kml | 10758 ++ mapbox-gl/examples/data/kml/timezones.kml | 1608 + mapbox-gl/examples/data/ogcsample.xml | 284 + mapbox-gl/examples/data/sld/countries.sld | 43 + mapbox-gl/examples/data/topojson/us.json | 1 + .../examples/data/topojson/world-110m.json | 1 + .../examples/device-orientation-require.js | 8 + mapbox-gl/examples/device-orientation.html | 58 + mapbox-gl/examples/device-orientation.js | 46 + .../drag-and-drop-image-vector-require.js | 18 + .../examples/drag-and-drop-image-vector.html | 56 + .../examples/drag-and-drop-image-vector.js | 138 + mapbox-gl/examples/drag-and-drop-require.js | 17 + mapbox-gl/examples/drag-and-drop.html | 56 + mapbox-gl/examples/drag-and-drop.js | 135 + mapbox-gl/examples/drag-features-require.js | 16 + mapbox-gl/examples/drag-features.html | 51 + mapbox-gl/examples/drag-features.js | 174 + .../examples/drag-rotate-and-zoom-require.js | 6 + mapbox-gl/examples/drag-rotate-and-zoom.html | 52 + mapbox-gl/examples/drag-rotate-and-zoom.js | 16 + .../draw-and-modify-features-require.js | 12 + .../examples/draw-and-modify-features.html | 61 + .../examples/draw-and-modify-features.js | 70 + mapbox-gl/examples/draw-features-require.js | 11 + mapbox-gl/examples/draw-features.html | 62 + mapbox-gl/examples/draw-features.js | 60 + mapbox-gl/examples/dynamic-data-require.js | 11 + mapbox-gl/examples/dynamic-data.html | 51 + mapbox-gl/examples/dynamic-data.js | 66 + .../examples/earthquake-clusters-require.js | 16 + mapbox-gl/examples/earthquake-clusters.html | 75 + mapbox-gl/examples/earthquake-clusters.js | 136 + mapbox-gl/examples/epsg-4326-require.js | 6 + mapbox-gl/examples/epsg-4326.html | 51 + mapbox-gl/examples/epsg-4326.js | 25 + mapbox-gl/examples/example-list.js | 1 + mapbox-gl/examples/example-list.xml | 1 + mapbox-gl/examples/export-map-require.js | 7 + mapbox-gl/examples/export-map.html | 56 + mapbox-gl/examples/export-map.js | 41 + mapbox-gl/examples/fractal-require.js | 6 + mapbox-gl/examples/fractal.html | 64 + mapbox-gl/examples/fractal.js | 117 + ...ull-screen-drag-rotate-and-zoom-require.js | 8 + .../full-screen-drag-rotate-and-zoom.html | 64 + .../full-screen-drag-rotate-and-zoom.js | 23 + mapbox-gl/examples/full-screen-require.js | 6 + mapbox-gl/examples/full-screen.html | 70 + mapbox-gl/examples/full-screen.js | 21 + mapbox-gl/examples/geojson-require.js | 13 + mapbox-gl/examples/geojson.html | 51 + mapbox-gl/examples/geojson.js | 186 + .../geolocation-orientation-require.js | 9 + .../examples/geolocation-orientation.html | 53 + mapbox-gl/examples/geolocation-orientation.js | 219 + mapbox-gl/examples/geolocation-require.js | 14 + mapbox-gl/examples/geolocation.html | 61 + mapbox-gl/examples/geolocation.js | 69 + .../examples/getfeatureinfo-image-require.js | 4 + mapbox-gl/examples/getfeatureinfo-image.html | 59 + mapbox-gl/examples/getfeatureinfo-image.js | 45 + .../examples/getfeatureinfo-tile-require.js | 4 + mapbox-gl/examples/getfeatureinfo-tile.html | 59 + mapbox-gl/examples/getfeatureinfo-tile.js | 45 + mapbox-gl/examples/gpx-require.js | 12 + mapbox-gl/examples/gpx.html | 62 + mapbox-gl/examples/gpx.js | 110 + mapbox-gl/examples/graticule-require.js | 7 + mapbox-gl/examples/graticule.html | 51 + mapbox-gl/examples/graticule.js | 24 + .../examples/heatmap-earthquakes-require.js | 6 + mapbox-gl/examples/heatmap-earthquakes.html | 61 + mapbox-gl/examples/heatmap-earthquakes.js | 45 + mapbox-gl/examples/hue-saturation-require.js | 6 + mapbox-gl/examples/hue-saturation.html | 74 + mapbox-gl/examples/hue-saturation.js | 71 + mapbox-gl/examples/icon-require.js | 11 + .../examples/icon-sprite-webgl-require.js | 9 + mapbox-gl/examples/icon-sprite-webgl.html | 58 + mapbox-gl/examples/icon-sprite-webgl.js | 131 + mapbox-gl/examples/icon.html | 62 + mapbox-gl/examples/icon.js | 84 + mapbox-gl/examples/igc-require.js | 16 + mapbox-gl/examples/igc.html | 58 + mapbox-gl/examples/igc.js | 176 + mapbox-gl/examples/image-filter-require.js | 5 + mapbox-gl/examples/image-filter.html | 65 + mapbox-gl/examples/image-filter.js | 140 + .../examples/image-vector-layer-require.js | 11 + mapbox-gl/examples/image-vector-layer.html | 59 + mapbox-gl/examples/image-vector-layer.js | 80 + mapbox-gl/examples/index.html | 209 + mapbox-gl/examples/kml-earthquakes-require.js | 10 + mapbox-gl/examples/kml-earthquakes.html | 75 + mapbox-gl/examples/kml-earthquakes.js | 86 + mapbox-gl/examples/kml-require.js | 8 + mapbox-gl/examples/kml-timezones-require.js | 9 + mapbox-gl/examples/kml-timezones.html | 63 + mapbox-gl/examples/kml-timezones.js | 94 + mapbox-gl/examples/kml.html | 61 + mapbox-gl/examples/kml.js | 82 + mapbox-gl/examples/layer-clipping-require.js | 5 + .../examples/layer-clipping-webgl-require.js | 6 + mapbox-gl/examples/layer-clipping-webgl.html | 56 + mapbox-gl/examples/layer-clipping-webgl.js | 101 + mapbox-gl/examples/layer-clipping.html | 51 + mapbox-gl/examples/layer-clipping.js | 40 + mapbox-gl/examples/layer-extent-require.js | 5 + mapbox-gl/examples/layer-extent.html | 68 + mapbox-gl/examples/layer-extent.js | 44 + mapbox-gl/examples/layer-group-require.js | 8 + mapbox-gl/examples/layer-group.html | 126 + mapbox-gl/examples/layer-group.js | 54 + mapbox-gl/examples/layer-spy-require.js | 5 + mapbox-gl/examples/layer-spy.html | 60 + mapbox-gl/examples/layer-spy.js | 62 + mapbox-gl/examples/layer-swipe-require.js | 6 + mapbox-gl/examples/layer-swipe.html | 52 + mapbox-gl/examples/layer-swipe.js | 44 + mapbox-gl/examples/lazy-source-require.js | 4 + mapbox-gl/examples/lazy-source.html | 73 + mapbox-gl/examples/lazy-source.js | 21 + mapbox-gl/examples/line-arrows-require.js | 11 + mapbox-gl/examples/line-arrows.html | 51 + mapbox-gl/examples/line-arrows.js | 55 + mapbox-gl/examples/loader.js | 69 + .../localized-openstreetmap-require.js | 6 + .../examples/localized-openstreetmap.html | 52 + mapbox-gl/examples/localized-openstreetmap.js | 48 + mapbox-gl/examples/mapbox-gl-require.js | 6 + mapbox-gl/examples/mapbox-gl.html | 56 + mapbox-gl/examples/mapbox-gl.js | 58 + .../examples/mapguide-untiled-require.js | 4 + mapbox-gl/examples/mapguide-untiled.html | 51 + mapbox-gl/examples/mapguide-untiled.js | 33 + mapbox-gl/examples/mapquest-require.js | 6 + mapbox-gl/examples/mapquest.html | 56 + mapbox-gl/examples/mapquest.js | 43 + mapbox-gl/examples/measure-require.js | 16 + mapbox-gl/examples/measure.html | 98 + mapbox-gl/examples/measure.js | 280 + .../examples/min-max-resolution-require.js | 6 + mapbox-gl/examples/min-max-resolution.html | 60 + mapbox-gl/examples/min-max-resolution.js | 32 + .../examples/mobile-full-screen-require.js | 5 + mapbox-gl/examples/mobile-full-screen.html | 30 + mapbox-gl/examples/mobile-full-screen.js | 35 + mapbox-gl/examples/modify-features-require.js | 9 + mapbox-gl/examples/modify-features.html | 53 + mapbox-gl/examples/modify-features.js | 28 + mapbox-gl/examples/modify-test-require.js | 11 + mapbox-gl/examples/modify-test.html | 57 + mapbox-gl/examples/modify-test.js | 237 + mapbox-gl/examples/mouse-position-require.js | 9 + mapbox-gl/examples/mouse-position.html | 59 + mapbox-gl/examples/mouse-position.js | 46 + mapbox-gl/examples/moveend-require.js | 7 + mapbox-gl/examples/moveend.html | 54 + mapbox-gl/examples/moveend.js | 42 + .../examples/navigation-controls-require.js | 6 + mapbox-gl/examples/navigation-controls.html | 56 + mapbox-gl/examples/navigation-controls.js | 25 + mapbox-gl/examples/overlay-require.js | 7 + mapbox-gl/examples/overlay.html | 82 + mapbox-gl/examples/overlay.js | 55 + .../examples/overviewmap-custom-require.js | 8 + mapbox-gl/examples/overviewmap-custom.html | 77 + mapbox-gl/examples/overviewmap-custom.js | 33 + mapbox-gl/examples/overviewmap-require.js | 6 + mapbox-gl/examples/overviewmap.html | 41 + mapbox-gl/examples/overviewmap.js | 15 + mapbox-gl/examples/polygon-styles-require.js | 9 + mapbox-gl/examples/polygon-styles.html | 57 + mapbox-gl/examples/polygon-styles.js | 91 + mapbox-gl/examples/popup-require.js | 7 + mapbox-gl/examples/popup.html | 104 + mapbox-gl/examples/popup.js | 66 + mapbox-gl/examples/preload-require.js | 4 + mapbox-gl/examples/preload.html | 55 + mapbox-gl/examples/preload.js | 32 + mapbox-gl/examples/regularshape-require.js | 10 + mapbox-gl/examples/regularshape.html | 51 + mapbox-gl/examples/regularshape.js | 89 + mapbox-gl/examples/rotation-require.js | 5 + mapbox-gl/examples/rotation.html | 52 + mapbox-gl/examples/rotation.js | 19 + mapbox-gl/examples/scale-line-require.js | 7 + mapbox-gl/examples/scale-line.html | 58 + mapbox-gl/examples/scale-line.js | 26 + mapbox-gl/examples/select-features-require.js | 8 + mapbox-gl/examples/select-features.html | 64 + mapbox-gl/examples/select-features.js | 67 + .../semi-transparent-layer-require.js | 6 + .../examples/semi-transparent-layer.html | 51 + mapbox-gl/examples/semi-transparent-layer.js | 19 + mapbox-gl/examples/side-by-side-require.js | 5 + mapbox-gl/examples/side-by-side.html | 63 + mapbox-gl/examples/side-by-side.js | 34 + mapbox-gl/examples/simple-require.js | 5 + mapbox-gl/examples/simple.html | 51 + mapbox-gl/examples/simple.js | 18 + .../examples/sphere-mollweide-require.js | 6 + mapbox-gl/examples/sphere-mollweide.html | 52 + mapbox-gl/examples/sphere-mollweide.js | 34 + mapbox-gl/examples/stamen-require.js | 5 + mapbox-gl/examples/stamen.html | 51 + mapbox-gl/examples/stamen.js | 21 + mapbox-gl/examples/static-image-require.js | 7 + mapbox-gl/examples/static-image.html | 60 + mapbox-gl/examples/static-image.js | 32 + .../examples/symbol-atlas-webgl-require.js | 12 + mapbox-gl/examples/symbol-atlas-webgl.html | 58 + mapbox-gl/examples/symbol-atlas-webgl.js | 110 + mapbox-gl/examples/synthetic-lines-require.js | 8 + mapbox-gl/examples/synthetic-lines.html | 110 + mapbox-gl/examples/synthetic-lines.js | 50 + .../examples/synthetic-points-require.js | 11 + mapbox-gl/examples/synthetic-points.html | 52 + mapbox-gl/examples/synthetic-points.js | 120 + mapbox-gl/examples/teleport-require.js | 5 + mapbox-gl/examples/teleport.html | 61 + mapbox-gl/examples/teleport.js | 26 + .../examples/tile-load-events-require.js | 4 + mapbox-gl/examples/tile-load-events.html | 79 + mapbox-gl/examples/tile-load-events.js | 97 + mapbox-gl/examples/tile-vector-require.js | 10 + mapbox-gl/examples/tile-vector.html | 67 + mapbox-gl/examples/tile-vector.js | 158 + mapbox-gl/examples/tilejson-require.js | 4 + mapbox-gl/examples/tilejson.html | 51 + mapbox-gl/examples/tilejson.js | 16 + mapbox-gl/examples/tileutfgrid-require.js | 6 + mapbox-gl/examples/tileutfgrid.html | 69 + mapbox-gl/examples/tileutfgrid.js | 64 + mapbox-gl/examples/tissot-require.js | 9 + mapbox-gl/examples/tissot.html | 56 + mapbox-gl/examples/tissot.js | 61 + mapbox-gl/examples/topojson-require.js | 9 + mapbox-gl/examples/topojson.html | 53 + mapbox-gl/examples/topojson.js | 35 + mapbox-gl/examples/vector-labels-require.js | 12 + mapbox-gl/examples/vector-labels.html | 361 + mapbox-gl/examples/vector-labels.js | 224 + mapbox-gl/examples/vector-layer-require.js | 11 + mapbox-gl/examples/vector-layer.html | 56 + mapbox-gl/examples/vector-layer.js | 114 + mapbox-gl/examples/vector-osm-require.js | 15 + mapbox-gl/examples/vector-osm.html | 56 + mapbox-gl/examples/vector-osm.js | 129 + mapbox-gl/examples/vector-wfs-require.js | 11 + mapbox-gl/examples/vector-wfs.html | 50 + mapbox-gl/examples/vector-wfs.js | 48 + mapbox-gl/examples/wkt-require.js | 8 + mapbox-gl/examples/wkt.html | 50 + mapbox-gl/examples/wkt.js | 25 + .../examples/wms-capabilities-require.js | 1 + mapbox-gl/examples/wms-capabilities.html | 49 + mapbox-gl/examples/wms-capabilities.js | 6 + mapbox-gl/examples/wms-custom-proj-require.js | 9 + mapbox-gl/examples/wms-custom-proj.html | 50 + mapbox-gl/examples/wms-custom-proj.js | 236 + .../examples/wms-image-custom-proj-require.js | 8 + mapbox-gl/examples/wms-image-custom-proj.html | 54 + mapbox-gl/examples/wms-image-custom-proj.js | 64 + mapbox-gl/examples/wms-image-require.js | 6 + mapbox-gl/examples/wms-image.html | 51 + mapbox-gl/examples/wms-image.js | 21 + mapbox-gl/examples/wms-no-proj-require.js | 8 + mapbox-gl/examples/wms-no-proj.html | 51 + mapbox-gl/examples/wms-no-proj.js | 53 + mapbox-gl/examples/wms-tiled-require.js | 5 + mapbox-gl/examples/wms-tiled.html | 51 + mapbox-gl/examples/wms-tiled.js | 21 + .../examples/wmts-capabilities-require.js | 1 + mapbox-gl/examples/wmts-capabilities.html | 49 + mapbox-gl/examples/wmts-capabilities.js | 6 + mapbox-gl/examples/wmts-hidpi-require.js | 6 + mapbox-gl/examples/wmts-hidpi.html | 56 + mapbox-gl/examples/wmts-hidpi.js | 67 + .../wmts-layer-from-capabilities-require.js | 7 + .../wmts-layer-from-capabilities.html | 51 + .../examples/wmts-layer-from-capabilities.js | 29 + mapbox-gl/examples/wmts-require.js | 10 + mapbox-gl/examples/wmts.html | 51 + mapbox-gl/examples/wmts.js | 54 + .../examples/xyz-esri-4326-512-require.js | 8 + mapbox-gl/examples/xyz-esri-4326-512.html | 51 + mapbox-gl/examples/xyz-esri-4326-512.js | 63 + mapbox-gl/examples/xyz-esri-require.js | 6 + mapbox-gl/examples/xyz-esri.html | 51 + mapbox-gl/examples/xyz-esri.js | 21 + mapbox-gl/examples/xyz-require.js | 7 + mapbox-gl/examples/xyz-retina-require.js | 7 + mapbox-gl/examples/xyz-retina.html | 52 + mapbox-gl/examples/xyz-retina.js | 33 + mapbox-gl/examples/xyz.html | 51 + mapbox-gl/examples/xyz.js | 37 + .../examples/zoom-constrained-require.js | 4 + mapbox-gl/examples/zoom-constrained.html | 52 + mapbox-gl/examples/zoom-constrained.js | 17 + mapbox-gl/examples/zoomify-require.js | 6 + mapbox-gl/examples/zoomify.html | 51 + mapbox-gl/examples/zoomify.js | 46 + mapbox-gl/examples/zoomslider-require.js | 5 + mapbox-gl/examples/zoomslider.html | 126 + mapbox-gl/examples/zoomslider.js | 29 + mapbox-gl/ol.ext/rbush.js | 600 + mapbox-gl/ol/ol/animation.js | 161 + mapbox-gl/ol/ol/animation.jsdoc | 13 + mapbox-gl/ol/ol/array.js | 104 + mapbox-gl/ol/ol/attribution.js | 76 + mapbox-gl/ol/ol/binary.js | 174 + mapbox-gl/ol/ol/canvasfunction.js | 17 + mapbox-gl/ol/ol/centerconstraint.js | 42 + mapbox-gl/ol/ol/collection.js | 283 + mapbox-gl/ol/ol/color/color.js | 332 + mapbox-gl/ol/ol/color/color.jsdoc | 8 + mapbox-gl/ol/ol/color/colormatrix.js | 205 + mapbox-gl/ol/ol/constraints.js | 35 + mapbox-gl/ol/ol/control.jsdoc | 3 + mapbox-gl/ol/ol/control/attributioncontrol.js | 397 + mapbox-gl/ol/ol/control/control.js | 140 + mapbox-gl/ol/ol/control/controldefaults.js | 47 + mapbox-gl/ol/ol/control/fullscreencontrol.js | 150 + .../ol/ol/control/mousepositioncontrol.js | 265 + mapbox-gl/ol/ol/control/overviewmapcontrol.js | 497 + mapbox-gl/ol/ol/control/rotatecontrol.js | 172 + mapbox-gl/ol/ol/control/scalelinecontrol.js | 343 + mapbox-gl/ol/ol/control/zoomcontrol.js | 132 + mapbox-gl/ol/ol/control/zoomslidercontrol.js | 327 + .../ol/ol/control/zoomtoextentcontrol.js | 86 + mapbox-gl/ol/ol/coordinate.js | 349 + mapbox-gl/ol/ol/coordinate.jsdoc | 3 + mapbox-gl/ol/ol/css.js | 37 + mapbox-gl/ol/ol/deviceorientation.js | 250 + mapbox-gl/ol/ol/dom/dom.js | 332 + mapbox-gl/ol/ol/dom/input.js | 148 + mapbox-gl/ol/ol/easing.js | 54 + mapbox-gl/ol/ol/easing.jsdoc | 4 + mapbox-gl/ol/ol/ellipsoid/ellipsoid.js | 185 + mapbox-gl/ol/ol/ellipsoid/wgs84ellipsoid.js | 10 + mapbox-gl/ol/ol/events/condition.js | 168 + mapbox-gl/ol/ol/events/condition.jsdoc | 18 + mapbox-gl/ol/ol/extent.js | 890 + mapbox-gl/ol/ol/extent.jsdoc | 3 + mapbox-gl/ol/ol/feature.js | 329 + mapbox-gl/ol/ol/feature.jsdoc | 3 + mapbox-gl/ol/ol/featureoverlay.js | 316 + mapbox-gl/ol/ol/format.jsdoc | 3 + mapbox-gl/ol/ol/format/binaryfeatureformat.js | 111 + mapbox-gl/ol/ol/format/featureformat.js | 184 + mapbox-gl/ol/ol/format/format.js | 12 + mapbox-gl/ol/ol/format/geojsonformat.js | 585 + mapbox-gl/ol/ol/format/gml/gml2format.js | 216 + mapbox-gl/ol/ol/format/gml/gml3format.js | 1350 + mapbox-gl/ol/ol/format/gml/gmlbaseformat.js | 599 + mapbox-gl/ol/ol/format/gpxformat.js | 876 + mapbox-gl/ol/ol/format/igcformat.js | 220 + mapbox-gl/ol/ol/format/jsonfeatureformat.js | 168 + mapbox-gl/ol/ol/format/kmlformat.js | 2710 + mapbox-gl/ol/ol/format/osmxmlformat.js | 236 + mapbox-gl/ol/ol/format/owsformat.js | 461 + mapbox-gl/ol/ol/format/polylineformat.js | 393 + mapbox-gl/ol/ol/format/textfeatureformat.js | 169 + mapbox-gl/ol/ol/format/topojsonformat.js | 409 + mapbox-gl/ol/ol/format/wfsformat.js | 770 + mapbox-gl/ol/ol/format/wktformat.js | 852 + .../ol/ol/format/wmscapabilitiesformat.js | 832 + .../ol/ol/format/wmsgetfeatureinfoformat.js | 145 + .../ol/ol/format/wmtscapabilitiesformat.js | 400 + mapbox-gl/ol/ol/format/xlinkformat.js | 17 + mapbox-gl/ol/ol/format/xmlfeatureformat.js | 255 + mapbox-gl/ol/ol/format/xmlformat.js | 48 + mapbox-gl/ol/ol/format/xsdformat.js | 183 + mapbox-gl/ol/ol/framestate.js | 21 + mapbox-gl/ol/ol/geolocation.js | 406 + mapbox-gl/ol/ol/geom/circle.js | 230 + mapbox-gl/ol/ol/geom/flat/areaflatgeom.js | 64 + mapbox-gl/ol/ol/geom/flat/centerflatgeom.js | 26 + mapbox-gl/ol/ol/geom/flat/closestflatgeom.js | 260 + mapbox-gl/ol/ol/geom/flat/containsflatgeom.js | 114 + mapbox-gl/ol/ol/geom/flat/deflateflatgeom.js | 91 + mapbox-gl/ol/ol/geom/flat/flipflatgeom.js | 37 + mapbox-gl/ol/ol/geom/flat/geodesicflatgeom.js | 180 + mapbox-gl/ol/ol/geom/flat/inflateflatgeom.js | 71 + .../ol/ol/geom/flat/interiorpointflatgeom.js | 92 + .../ol/ol/geom/flat/interpolateflatgeom.js | 190 + mapbox-gl/ol/ol/geom/flat/intersectsextent.js | 154 + mapbox-gl/ol/ol/geom/flat/lengthflatgeom.js | 43 + mapbox-gl/ol/ol/geom/flat/orientflatgeom.js | 115 + mapbox-gl/ol/ol/geom/flat/reverseflatgeom.js | 22 + mapbox-gl/ol/ol/geom/flat/segmentsflatgeom.js | 35 + mapbox-gl/ol/ol/geom/flat/simplifyflatgeom.js | 400 + .../ol/ol/geom/flat/transformflatgeom.js | 65 + mapbox-gl/ol/ol/geom/geom.jsdoc | 3 + mapbox-gl/ol/ol/geom/geometry.js | 238 + mapbox-gl/ol/ol/geom/geometrycollection.js | 294 + mapbox-gl/ol/ol/geom/linearring.js | 154 + mapbox-gl/ol/ol/geom/linestring.js | 254 + mapbox-gl/ol/ol/geom/multilinestring.js | 320 + mapbox-gl/ol/ol/geom/multipoint.js | 193 + mapbox-gl/ol/ol/geom/multipolygon.js | 404 + mapbox-gl/ol/ol/geom/point.js | 126 + mapbox-gl/ol/ol/geom/polygon.js | 395 + mapbox-gl/ol/ol/geom/simplegeometry.js | 287 + mapbox-gl/ol/ol/graticule.js | 435 + mapbox-gl/ol/ol/has.js | 150 + mapbox-gl/ol/ol/has.jsdoc | 3 + mapbox-gl/ol/ol/image.js | 152 + mapbox-gl/ol/ol/imagebase.js | 129 + mapbox-gl/ol/ol/imagecanvas.js | 38 + mapbox-gl/ol/ol/imageloadfunction.js | 21 + mapbox-gl/ol/ol/imagetile.js | 170 + mapbox-gl/ol/ol/imageurlfunction.js | 46 + .../interaction/doubleclickzoominteraction.js | 64 + .../ol/interaction/draganddropinteraction.js | 236 + .../ol/ol/interaction/dragboxinteraction.js | 206 + .../ol/ol/interaction/dragpaninteraction.js | 168 + .../dragrotateandzoominteraction.js | 149 + .../ol/interaction/dragrotateinteraction.js | 127 + .../ol/ol/interaction/dragzoominteraction.js | 65 + .../ol/ol/interaction/drawinteraction.js | 643 + mapbox-gl/ol/ol/interaction/interaction.js | 251 + mapbox-gl/ol/ol/interaction/interaction.jsdoc | 3 + .../ol/ol/interaction/interactiondefaults.js | 113 + .../ol/interaction/keyboardpaninteraction.js | 99 + .../ol/interaction/keyboardzoominteraction.js | 85 + .../ol/ol/interaction/modifyinteraction.js | 791 + .../interaction/mousewheelzoominteraction.js | 118 + .../ol/interaction/pinchrotateinteraction.js | 169 + .../ol/ol/interaction/pinchzoominteraction.js | 157 + .../ol/ol/interaction/pointerinteraction.js | 220 + .../ol/ol/interaction/selectinteraction.js | 312 + mapbox-gl/ol/ol/kinetic.js | 159 + mapbox-gl/ol/ol/layer/heatmaplayer.js | 313 + mapbox-gl/ol/ol/layer/imagelayer.js | 33 + mapbox-gl/ol/ol/layer/layer.js | 148 + mapbox-gl/ol/ol/layer/layer.jsdoc | 3 + mapbox-gl/ol/ol/layer/layerbase.js | 423 + mapbox-gl/ol/ol/layer/layergroup.js | 236 + mapbox-gl/ol/ol/layer/tilelayer.js | 111 + mapbox-gl/ol/ol/layer/vectorlayer.js | 158 + mapbox-gl/ol/ol/loadingstrategy.js | 60 + mapbox-gl/ol/ol/loadingstrategy.jsdoc | 5 + mapbox-gl/ol/ol/map.js | 1610 + mapbox-gl/ol/ol/mapbrowserevent.js | 529 + mapbox-gl/ol/ol/mapevent.js | 58 + mapbox-gl/ol/ol/math.js | 112 + mapbox-gl/ol/ol/object.js | 491 + mapbox-gl/ol/ol/observable.js | 114 + mapbox-gl/ol/ol/ol.js | 293 + mapbox-gl/ol/ol/overlay.js | 575 + mapbox-gl/ol/ol/pixel.js | 10 + mapbox-gl/ol/ol/pointer/eventsource.js | 55 + mapbox-gl/ol/ol/pointer/mousesource.js | 253 + mapbox-gl/ol/ol/pointer/mssource.js | 195 + mapbox-gl/ol/ol/pointer/nativesource.js | 136 + mapbox-gl/ol/ol/pointer/pointerevent.js | 276 + .../ol/ol/pointer/pointereventhandler.js | 496 + mapbox-gl/ol/ol/pointer/touchsource.js | 453 + mapbox-gl/ol/ol/proj/common.js | 24 + mapbox-gl/ol/ol/proj/epsg3857projection.js | 158 + mapbox-gl/ol/ol/proj/epsg4326projection.js | 68 + mapbox-gl/ol/ol/proj/proj.js | 716 + mapbox-gl/ol/ol/proj/proj.jsdoc | 54 + mapbox-gl/ol/ol/render.jsdoc | 3 + mapbox-gl/ol/ol/render/box.js | 160 + mapbox-gl/ol/ol/render/canvas/canvas.js | 96 + mapbox-gl/ol/ol/render/canvas/canvas.jsdoc | 3 + .../ol/ol/render/canvas/canvasimmediate.js | 1016 + mapbox-gl/ol/ol/render/canvas/canvasreplay.js | 2060 + mapbox-gl/ol/ol/render/ireplay.js | 50 + mapbox-gl/ol/ol/render/ivectorcontext.js | 139 + mapbox-gl/ol/ol/render/renderevent.js | 84 + mapbox-gl/ol/ol/render/vector.js | 347 + .../ol/ol/render/webgl/webglimagecolor.glsl | 46 + .../ol/render/webgl/webglimagecolorshader.js | 153 + .../ol/ol/render/webgl/webglimagedefault.glsl | 44 + .../render/webgl/webglimagedefaultshader.js | 147 + .../ol/ol/render/webgl/webglimmediate.js | 301 + mapbox-gl/ol/ol/render/webgl/webglreplay.js | 1276 + .../canvas/canvasimagelayerrenderer.js | 208 + .../ol/renderer/canvas/canvaslayerrenderer.js | 273 + .../ol/renderer/canvas/canvasmaprenderer.js | 207 + .../canvas/canvastilelayerrenderer.js | 451 + .../canvas/canvasvectorlayerrenderer.js | 279 + .../ol/renderer/dom/domimagelayerrenderer.js | 158 + .../ol/ol/renderer/dom/domlayerrenderer.js | 54 + .../ol/ol/renderer/dom/dommaprenderer.js | 272 + .../ol/renderer/dom/domtilelayerrenderer.js | 472 + .../ol/renderer/dom/domvectorlayerrenderer.js | 339 + mapbox-gl/ol/ol/renderer/layerrenderer.js | 332 + mapbox-gl/ol/ol/renderer/maprenderer.js | 381 + .../renderer/webgl/webglimagelayerrenderer.js | 325 + .../ol/renderer/webgl/webgllayerrenderer.js | 296 + .../ol/ol/renderer/webgl/webglmapcolor.glsl | 32 + .../ol/renderer/webgl/webglmapcolorshader.js | 129 + .../ol/ol/renderer/webgl/webglmapdefault.glsl | 30 + .../renderer/webgl/webglmapdefaultshader.js | 123 + .../ol/ol/renderer/webgl/webglmaprenderer.js | 711 + .../ol/ol/renderer/webgl/webgltilelayer.glsl | 25 + .../renderer/webgl/webgltilelayerrenderer.js | 393 + .../ol/renderer/webgl/webgltilelayershader.js | 111 + .../webgl/webglvectorlayerrenderer.js | 312 + mapbox-gl/ol/ol/resolutionconstraint.js | 76 + mapbox-gl/ol/ol/rotationconstraint.js | 87 + mapbox-gl/ol/ol/size.js | 21 + mapbox-gl/ol/ol/source.jsdoc | 3 + mapbox-gl/ol/ol/source/bingmapssource.js | 187 + mapbox-gl/ol/ol/source/clustersource.js | 163 + mapbox-gl/ol/ol/source/formatvectorsource.js | 125 + mapbox-gl/ol/ol/source/geojsonsource.js | 37 + mapbox-gl/ol/ol/source/gpxsource.js | 36 + mapbox-gl/ol/ol/source/igcsource.js | 33 + mapbox-gl/ol/ol/source/imagecanvassource.js | 91 + mapbox-gl/ol/ol/source/imagemapguidesource.js | 244 + mapbox-gl/ol/ol/source/imagesource.js | 101 + mapbox-gl/ol/ol/source/imagestaticsource.js | 66 + mapbox-gl/ol/ol/source/imagevectorsource.js | 297 + mapbox-gl/ol/ol/source/imagewmssource.js | 376 + mapbox-gl/ol/ol/source/kmlsource.js | 38 + mapbox-gl/ol/ol/source/mapquestsource.js | 99 + mapbox-gl/ol/ol/source/osmsource.js | 59 + mapbox-gl/ol/ol/source/osmxmlsource.js | 35 + mapbox-gl/ol/ol/source/servervectorsource.js | 126 + mapbox-gl/ol/ol/source/source.js | 165 + mapbox-gl/ol/ol/source/stamensource.js | 132 + mapbox-gl/ol/ol/source/staticvectorsource.js | 83 + .../ol/ol/source/tilearcgisrestsource.js | 236 + mapbox-gl/ol/ol/source/tiledebugsource.js | 107 + mapbox-gl/ol/ol/source/tileimagesource.js | 185 + mapbox-gl/ol/ol/source/tilejsonsource.js | 113 + mapbox-gl/ol/ol/source/tilesource.js | 268 + mapbox-gl/ol/ol/source/tileutfgridsource.js | 383 + mapbox-gl/ol/ol/source/tilevectorsource.js | 311 + mapbox-gl/ol/ol/source/tilewmssource.js | 420 + mapbox-gl/ol/ol/source/topojsonsource.js | 36 + mapbox-gl/ol/ol/source/vectorsource.js | 656 + mapbox-gl/ol/ol/source/wmssource.js | 17 + mapbox-gl/ol/ol/source/wmssource.jsdoc | 3 + mapbox-gl/ol/ol/source/wmtssource.js | 471 + mapbox-gl/ol/ol/source/xyzsource.js | 86 + mapbox-gl/ol/ol/source/zoomifysource.js | 185 + mapbox-gl/ol/ol/sphere/normal.js | 11 + mapbox-gl/ol/ol/sphere/sphere.js | 267 + mapbox-gl/ol/ol/sphere/wgs84sphere.js | 11 + mapbox-gl/ol/ol/structs/checksum.js | 16 + mapbox-gl/ol/ol/structs/lrucache.js | 248 + mapbox-gl/ol/ol/structs/priorityqueue.js | 291 + mapbox-gl/ol/ol/structs/rbush.js | 256 + mapbox-gl/ol/ol/style.jsdoc | 80 + mapbox-gl/ol/ol/style/atlasmanager.js | 445 + mapbox-gl/ol/ol/style/circlestyle.js | 442 + mapbox-gl/ol/ol/style/fillstyle.js | 66 + mapbox-gl/ol/ol/style/iconstyle.js | 648 + mapbox-gl/ol/ol/style/imagestyle.js | 243 + mapbox-gl/ol/ol/style/regularshapestyle.js | 540 + mapbox-gl/ol/ol/style/strokestyle.js | 221 + mapbox-gl/ol/ol/style/style.js | 363 + mapbox-gl/ol/ol/style/textstyle.js | 274 + mapbox-gl/ol/ol/tile.js | 95 + mapbox-gl/ol/ol/tilecache.js | 71 + mapbox-gl/ol/ol/tilecoord.js | 139 + mapbox-gl/ol/ol/tilegrid.jsdoc | 3 + mapbox-gl/ol/ol/tilegrid/tilegrid.js | 505 + mapbox-gl/ol/ol/tilegrid/wmtstilegrid.js | 123 + mapbox-gl/ol/ol/tilegrid/xyztilegrid.js | 131 + mapbox-gl/ol/ol/tilegrid/zoomifytilegrid.js | 79 + mapbox-gl/ol/ol/tileloadfunction.js | 11 + mapbox-gl/ol/ol/tilequeue.js | 100 + mapbox-gl/ol/ol/tilerange.js | 184 + mapbox-gl/ol/ol/tileurlfunction.js | 157 + mapbox-gl/ol/ol/transformfunction.js | 13 + mapbox-gl/ol/ol/vec/mat4.js | 82 + mapbox-gl/ol/ol/view.js | 773 + mapbox-gl/ol/ol/webgl/buffer.js | 56 + mapbox-gl/ol/ol/webgl/context.js | 410 + mapbox-gl/ol/ol/webgl/shader.js | 84 + mapbox-gl/ol/ol/webgl/shader.mustache | 103 + mapbox-gl/ol/ol/webgl/webgl.js | 44 + mapbox-gl/ol/ol/xml.js | 751 + .../bootstrap/css/bootstrap-responsive.css | 1109 + .../css/bootstrap-responsive.min.css | 9 + .../resources/bootstrap/css/bootstrap.css | 6158 + .../resources/bootstrap/css/bootstrap.min.css | 9 + .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../bootstrap/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes mapbox-gl/resources/bootstrap/js/bootstrap.js | 2276 + .../resources/bootstrap/js/bootstrap.min.js | 6 + mapbox-gl/resources/display-frame-rate.js | 39 + mapbox-gl/resources/example-behaviour.js | 263 + mapbox-gl/resources/jquery.min.js | 4 + mapbox-gl/resources/layout.css | 31 + mapbox-gl/resources/logo-32x32-optimized.png | Bin 0 -> 1116 bytes mapbox-gl/resources/logo-32x32.png | Bin 0 -> 1205 bytes mapbox-gl/resources/logo.png | Bin 0 -> 1365 bytes mapbox-gl/resources/textured_paper.jpeg | Bin 0 -> 3445 bytes 3109 files changed, 943365 insertions(+) create mode 100644 mapbox-gl/build/ol-debug.js create mode 100644 mapbox-gl/build/ol-deps.js create mode 100644 mapbox-gl/build/ol.js create mode 100644 mapbox-gl/closure-library/.gitignore create mode 100644 mapbox-gl/closure-library/.travis.yml create mode 100644 mapbox-gl/closure-library/AUTHORS create mode 100644 mapbox-gl/closure-library/CONTRIBUTING create mode 100644 mapbox-gl/closure-library/LICENSE create mode 100644 mapbox-gl/closure-library/README.md create mode 100644 mapbox-gl/closure-library/all_tests.html create mode 100644 mapbox-gl/closure-library/alltests.js create mode 100644 mapbox-gl/closure-library/closure/bin/build/closurebuilder.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/depstree.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/depstree_test.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/depswriter.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/jscompiler.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/jscompiler_test.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/source.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/source_test.py create mode 100644 mapbox-gl/closure-library/closure/bin/build/treescan.py create mode 100644 mapbox-gl/closure-library/closure/bin/calcdeps.py create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/closure.el create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/closure_test.el create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc.py create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc_test.py create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/run_el_tests.sh create mode 100644 mapbox-gl/closure-library/closure/bin/labs/code/run_py_tests.sh create mode 100644 mapbox-gl/closure-library/closure/bin/logos/logo.svg create mode 100644 mapbox-gl/closure-library/closure/bin/logos/logoandlabel.svg create mode 100644 mapbox-gl/closure-library/closure/bin/scopify.py create mode 100644 mapbox-gl/closure-library/closure/css/inlay/g-base.css create mode 100644 mapbox-gl/closure-library/closure/css/inlay/g-fixed.css create mode 100644 mapbox-gl/closure-library/closure/css/inlay/g-ratio.css create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/announcer.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/aria.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/attributes.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/datatables.js create mode 100644 mapbox-gl/closure-library/closure/goog/a11y/aria/roles.js create mode 100644 mapbox-gl/closure-library/closure/goog/array/array.js create mode 100644 mapbox-gl/closure-library/closure/goog/array/array_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/array/array_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/asserts/asserts.js create mode 100644 mapbox-gl/closure-library/closure/goog/asserts/asserts_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/asserts/asserts_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/animationdelay.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/animationdelay_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/async/animationdelay_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/conditionaldelay.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/delay.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/delay_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/async/delay_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/nexttick.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/nexttick_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/async/nexttick_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/run.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/run_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/throttle.js create mode 100644 mapbox-gl/closure-library/closure/goog/async/throttle_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/async/throttle_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/base.js create mode 100644 mapbox-gl/closure-library/closure/goog/base_module_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/base_module_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/base_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/base_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/bootstrap/nodejs.js create mode 100644 mapbox-gl/closure-library/closure/goog/bootstrap/webworkers.js create mode 100644 mapbox-gl/closure-library/closure/goog/color/alpha.js create mode 100644 mapbox-gl/closure-library/closure/goog/color/alpha_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/color/alpha_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/color/color.js create mode 100644 mapbox-gl/closure-library/closure/goog/color/color_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/color/color_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/color/names.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/aes.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/aes_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/aes_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/arc4.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/arc4_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/arc4_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/base64.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/base64_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/base64_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/basen.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/basen_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/basen_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/blobhasher.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/blockcipher.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/cbc.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/cbc_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/cbc_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/crypt.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/crypt_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/crypt_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/crypt_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hash.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hash32.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hash32_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hash32_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hashtester.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hmac.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hmac_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/hmac_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/md5.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/md5_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/md5_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/md5_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/pbkdf2.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha1.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha1_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha1_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha1_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha2.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha224.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha224_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha224_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha224_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha256.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha256_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha256_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha256_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha384.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha512.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha512_256.js create mode 100644 mapbox-gl/closure-library/closure/goog/crypt/sha512_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/css/autocomplete.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/bubble.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/button.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/charpicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/checkbox.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/colormenubutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/colorpalette.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/colorpicker-simplegrid.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/combobox.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/common.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/css3button.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/css3menubutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/custombutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/datepicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/dialog.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/dimensionpicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/dragdropdetector.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/editor/bubble.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/editor/dialog.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/editor/equationeditor.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/editor/linkdialog.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/editortoolbar.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/filteredmenu.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/filterobservingmenuitem.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/flatbutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/flatmenubutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/hovercard.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/hsvapalette.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/hsvpalette.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/imagelessbutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/imagelessmenubutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/inputdatepicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/linkbutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/menu.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/menubar.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/menubutton.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/menuitem.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/menuseparator.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/multitestrunner.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/palette.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/popupdatepicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/roundedpanel.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/roundedtab.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/submenu.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tab.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tabbar.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tablesorter.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/toolbar.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tooltip.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tree.css create mode 100644 mapbox-gl/closure-library/closure/goog/css/tristatemenuitem.css create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom.js create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_1.css create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_2.css create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/cssom_test_link_1.css create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/iframe/style.js create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/iframe/style_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/iframe/style_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/cssom/iframe/style_test_import.css create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/datamanager.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/datasource.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/datasource_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/datasource_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/expr.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/expr_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/expr_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/fastdatanode.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/fastdatanode_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/fastdatanode_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/jsdatasource.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/jsondatasource.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/jsxmlhttpdatasource.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/jsxmlhttpdatasource_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/jsxmlhttpdatasource_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/datasource/xmldatasource.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/date.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/date_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/date_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/datelike.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/daterange.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/daterange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/daterange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/duration.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/duration_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/duration_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/relative.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/relative_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/relative_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/relativewithplurals.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/relativewithplurals_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/relativewithplurals_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/utcdatetime.js create mode 100644 mapbox-gl/closure-library/closure/goog/date/utcdatetime_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/date/utcdatetime_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/cursor.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/db.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/db_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/db/db_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/error.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/index.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/indexeddb.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/keyrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/objectstore.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/old_db_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/db/old_db_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/db/transaction.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/console.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/console_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/console_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debug.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debug_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debug_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debugwindow.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debugwindow_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/debugwindow_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/devcss/devcss.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/devcss/devcss_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/devcss/devcss_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/devcss/devcssrunner.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/divconsole.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/enhanceerror_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/enhanceerror_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/entrypointregistry.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/entrypointregistry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/entrypointregistry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/error.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/error_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/error_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandler_async_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandler_async_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorhandlerweakdep.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorreporter.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorreporter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/errorreporter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/fancywindow.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/formatter.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/formatter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/formatter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/fpsdisplay.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/fpsdisplay_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/fpsdisplay_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/gcdiagnostics.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logbuffer.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logbuffer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logbuffer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logger.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logger_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logger_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logrecord.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logrecordserializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logrecordserializer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/logrecordserializer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/relativetimeprovider.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/tracer.js create mode 100644 mapbox-gl/closure-library/closure/goog/debug/tracer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/debug/tracer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/defineclass_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/defineclass_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/advancedtooltip.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/animationqueue.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/autocomplete-basic.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/autocompleteremote.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/autocompleteremotedata.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/autocompleterichremote.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/autocompleterichremotedata.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/bidiinput.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/blobhasher.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/bubble.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/button.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/charcounter.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/charpicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/checkbox.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/color-contrast.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/colormenubutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/colorpicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/combobox.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/container.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/control.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/css/demo.css create mode 100644 mapbox-gl/closure-library/closure/goog/demos/css/emojipicker.css create mode 100644 mapbox-gl/closure-library/closure/goog/demos/css/emojisprite.css create mode 100644 mapbox-gl/closure-library/closure/goog/demos/css3button.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/css3menubutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/cssspriteanimation.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/datepicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/debug.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/depsgraph.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dialog.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dimensionpicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dimensionpicker_rtl.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dom_selection.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/drag.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dragdrop.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dragdropdetector.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dragdropdetector_target.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dragger.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/draglistgroup.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/dragscrollsupport.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/drilldownrow.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/deps.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/editor.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/field_basic.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworld_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/seamlessfield.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/editor/tableeditor.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/effects.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/200.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/201.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/202.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/203.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/204.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/205.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/206.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2BC.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2BD.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2BE.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2BF.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C0.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C1.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C2.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C3.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C4.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C5.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C6.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C7.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C8.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2C9.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CA.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CB.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CC.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CD.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CE.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2CF.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D0.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D1.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D2.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D3.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D4.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D5.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D6.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D7.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D8.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2D9.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DA.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DB.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DC.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DD.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DE.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2DF.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E0.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E1.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E2.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E3.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E4.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E5.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E6.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E7.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E8.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2E9.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2EA.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2EB.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2EC.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2ED.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2EE.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2EF.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F0.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F1.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F2.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F3.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F4.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F5.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F6.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F7.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F8.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2F9.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FA.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FB.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FC.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FD.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FE.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/2FF.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/none.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/sprite.png create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/sprite2.png create mode 100644 mapbox-gl/closure-library/closure/goog/demos/emoji/unknown.gif create mode 100644 mapbox-gl/closure-library/closure/goog/demos/event-propagation.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/events.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/eventtarget.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/filedrophandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/filteredmenu.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/focushandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/fpsdisplay.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/fx/css3/transition.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/gauge.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates2.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/basicelements.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/events.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/modifyelements.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/subpixel.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/tiger.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/graphics/tigerdata.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/history1.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/history2.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/history3.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/history3js.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/history_blank.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/hovercard.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/hsvapalette.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/hsvpalette.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/html5history.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/imagelessbutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/imagelessmenubutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/index.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/index_nav.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/index_splash.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/inline_block_quirks.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/inline_block_standards.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/inputdatepicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/inputhandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/jsonprettyprinter.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/keyboardshortcuts.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/keyhandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/labelinput.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/menu.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/menubar.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/menubutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/menubutton_frame.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/menuitem.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/mousewheelhandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/onlinehandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/palette.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/pastehandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/pixeldensitymonitor.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/plaintextspellchecker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/popup.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/popupcolorpicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/popupdatepicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/popupemojipicker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/popupmenu.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/progressbar.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/prompt.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/quadtree.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/ratings.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/richtextspellchecker.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/roundedpanel.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/samplecomponent.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/samplecomponent.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/scrollfloater.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/select.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/selectionmenubutton.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/serverchart.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/slider.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/splitbehavior.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/splitpane.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/stopevent.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/submenus.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/submenus2.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tabbar.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tablesorter.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tabpane.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/textarea.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/timers.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/toolbar.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tooltip.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tracer.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tree/demo.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tree/testdata.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/tweakui.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/twothumbslider.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/useragent.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/viewportsizemonitor.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/wheelhandler.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/blank.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/index.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/inner.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/blank.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/index.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/inner.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/relay.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/relay.html create mode 100644 mapbox-gl/closure-library/closure/goog/demos/xpc/xpcdemo.js create mode 100644 mapbox-gl/closure-library/closure/goog/demos/zippy.html create mode 100644 mapbox-gl/closure-library/closure/goog/deps.js create mode 100644 mapbox-gl/closure-library/closure/goog/disposable/disposable.js create mode 100644 mapbox-gl/closure-library/closure/goog/disposable/disposable_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/disposable/disposable_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/disposable/idisposable.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/abstractmultirange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/abstractrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/animationframe/polyfill.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/annotate.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/annotate_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/annotate_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserfeature.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/abstractrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/geckorange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/ierange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/operarange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/w3crange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/browserrange/webkitrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classes.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classes_quirks_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classes_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classes_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classlist.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classlist_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/classlist_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/controlrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/controlrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/controlrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dataset.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dataset_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dataset_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dom.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dom_quirks_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/dom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/forms.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/forms_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/forms_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/fullscreen.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iframe.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iframe_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iframe_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iter.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/iter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/multirange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/multirange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/multirange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeiterator.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeoffset.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/nodetype.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/abstractpattern.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/allchildren.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/callback/callback.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/callback/counter.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/callback/test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/childmatches.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/endtag.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/fulltag.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/matcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/nodetype.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/pattern.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/repeat.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/sequence.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/starttag.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/tag.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/pattern/text.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/range.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/range_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/range_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/rangeendpoint.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/safe.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/safe_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/safe_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedcaretrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedcaretrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedcaretrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/savedrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/selection.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/selection_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/selection_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagiterator.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagname.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagname_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tagname_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tags.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/tags_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrangeiterator.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/vendor.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/vendor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/vendor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/xml.js create mode 100644 mapbox-gl/closure-library/closure/goog/dom/xml_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/dom/xml_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/browserfeature.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/browserfeature_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/browserfeature_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/clicktoeditwrapper.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/clicktoeditwrapper_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/clicktoeditwrapper_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/command.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/contenteditablefield.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/contenteditablefield_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/contenteditablefield_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/defines.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/field.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/field_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/field_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/focus.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/focus_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/focus_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/icontent.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/icontent_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/icontent_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/link.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/link_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/link_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/node.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/node_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/node_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugin_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/range.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/range_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/range_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/seamlessfield.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/seamlessfield_quirks_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/style.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/style_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/style_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/table.js create mode 100644 mapbox-gl/closure-library/closure/goog/editor/table_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/editor/table_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/actioneventwrapper.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/actionhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/actionhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/actionhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/browserevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/browserevent_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/browserevent_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/browserfeature.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/event.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/event_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/event_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventid.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/events.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/events_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/events_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtargettester.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventtype.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/eventwrapper.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/filedrophandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/focushandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/imehandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/imehandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/imehandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/inputhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/inputhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/inputhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/keycodes.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/keycodes_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/keycodes_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/keyhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/keyhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/keyhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/keynames.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenable.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenable_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenable_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/listener.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenermap.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenermap_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/listenermap_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/mousewheelhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/onlinehandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/pastehandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/pastehandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/pastehandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/wheelevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/wheelhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/emailaddress.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/emailaddress_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/format/emailaddress_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/format.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/format_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/format/format_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter.js create mode 100644 mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/entry.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/entryimpl.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/error.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/filereader.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/filesaver.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/filesystem.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/filesystemimpl.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/filewriter.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/fs.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/fs_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fs/fs_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fs/progressevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/functions/functions.js create mode 100644 mapbox-gl/closure-library/closure/goog/functions/functions_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/functions/functions_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/anim/anim.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animation.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animation_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animation_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animationqueue.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/css3/fx.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/css3/transition.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dom.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragdrop.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragdropgroup.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragger.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragger_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragger_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/draglistgroup.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/easing.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/easing_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/fx.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/fx_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/fx/fx_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/transition.js create mode 100644 mapbox-gl/closure-library/closure/goog/fx/transitionbase.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/abstractgraphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/affinetransform.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/affinetransform_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/canvaselement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/canvasgraphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/element.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ellipseelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/element.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/element_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/ellipse.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/ext.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/graphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/group.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/image.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/path.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/path_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/rectangle.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/shape.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/ext/strokeandfillelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/fill.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/font.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/graphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/groupelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/imageelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/lineargradient.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/path.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/path_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/pathelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/paths.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/paths_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/rectelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/solidfill.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/solidfill_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/stroke.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/strokeandfillelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/svgelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/svggraphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/svggraphics_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/textelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/vmlelement.js create mode 100644 mapbox-gl/closure-library/closure/goog/graphics/vmlgraphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/event.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/eventtype.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/history.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/history_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/history/history_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/html5history.js create mode 100644 mapbox-gl/closure-library/closure/goog/history/html5history_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/history/html5history_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/flash.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/flash_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/flash_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/legacyconversions.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safehtml.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safehtml_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/safehtml_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safescript.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safescript_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/safescript_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestyle.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestyle_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestyle_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestylesheet.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safeurl.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/safeurl_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/safeurl_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/silverlight.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/silverlight_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/silverlight_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/testing.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/trustedresourceurl.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/uncheckedconversions.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/utils.js create mode 100644 mapbox-gl/closure-library/closure/goog/html/utils_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/html/utils_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidi.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidi_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidi_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidiformatter.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/charpickerdata.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/collation.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/collation_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/collation_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols_ext.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/currency.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/currency_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/currency_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/currencycodemap.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeformat.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeparse.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimepatterns.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimepatternsext.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimesymbols.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/datetimesymbolsext.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/graphemebreak.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/messageformat.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/mime.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/mime_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/mime_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/numberformat.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbols.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbolsext.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/ordinalrules.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/pluralrules.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/timezone.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/timezone_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/timezone_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/namefetcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/i18n/uchar_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/images/blank.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/bubble_close.jpg create mode 100644 mapbox-gl/closure-library/closure/goog/images/bubble_left.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/bubble_right.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/button-bg.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/check-outline.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/check-sprite.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/check.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/close_box.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/color-swatch-tick.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/dialog_close_box.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/dimension-highlighted.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/dimension-unhighlighted.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/dropdn.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/dropdn_disabled.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/dropdown.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/gears_bluedot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/gears_online.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/gears_paused.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/gears_syncing.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsv-sprite.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsv-sprite.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsva-sprite.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/hsva-sprite.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_bot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_top.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/menu-arrows.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/minus.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_bot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_top.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/offlineicons.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/plus.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/ratingstars.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_bot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_top.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/toolbar-bg.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/toolbar-separator.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/toolbar_icons.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/tree/I.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/tree/cleardot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/tree/tree.gif create mode 100644 mapbox-gl/closure-library/closure/goog/images/tree/tree.png create mode 100644 mapbox-gl/closure-library/closure/goog/images/ui_controls.jpg create mode 100644 mapbox-gl/closure-library/closure/goog/iter/iter.js create mode 100644 mapbox-gl/closure-library/closure/goog/iter/iter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/iter/iter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/evaljsonprocessor.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybrid.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybrid_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybrid_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/json.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/json_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/json/json_perf.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/json_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/json/json_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/nativejsonprocessor.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/processor.js create mode 100644 mapbox-gl/closure-library/closure/goog/json/processor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/json/processor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/touch.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/touch_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/events/touch_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/format/csv.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/format/csv_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/format/csv_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/html/attribute_rewriter.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/html/sanitizer.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/html/sanitizer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/html/scrubber.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/html/scrubber_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/i18n/listformat.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbols.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbolsext.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/iterable/iterable.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/iterable/iterable_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/mock/mock.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/image.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/image_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/image_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/testdata/cleardot.gif create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/basetestchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channel.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/connectionstate.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/netutils.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/requeststats.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchanneldebug.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wire.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransportfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/xhr.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/object/object.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/object/object_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/object/object_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/map.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/map_perf.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/map_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/map_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/multimap.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/assertthat.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/environment.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/environment_usage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/matcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/browser.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/device.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/engine.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/platform.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/test_agents.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/util.js create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/countries.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/defaultlocalenameconstants.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/genericfontnames.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/genericfontnamesdata.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/locale.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/nativenameconstants.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/scriptToLanguages.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonedetection.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonefingerprint.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonelist.js create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/log/log.js create mode 100644 mapbox-gl/closure-library/closure/goog/log/log_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/affinetransform.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/affinetransform_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/affinetransform_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/bezier.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/bezier_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/bezier_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/box.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/box_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/box_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate3.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate3_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/coordinate_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/exponentialbackoff.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/integer.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/integer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/integer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/interpolator1.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/linear1.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/spline1.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/line.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/line_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/line_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/long.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/long_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/long_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/math.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/math_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/math_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/matrix.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/matrix_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/matrix_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/path.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/path_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/path_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/paths.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/paths_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/paths_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/range.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/range_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/range_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/rangeset.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/rangeset_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/rangeset_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/rect.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/rect_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/rect_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/size.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/size_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/size_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/tdma.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/tdma_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/tdma_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec2.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec2_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec2_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec3.js create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/math/vec3_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/memoize/memoize.js create mode 100644 mapbox-gl/closure-library/closure/goog/memoize/memoize_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/memoize/memoize_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/abstractchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/deferredchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerclient.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerserver.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/messagechannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/messaging.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/messaging_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/messaging_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/multichannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portcaller.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portnetwork.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portnetwork_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portoperator.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/respondingchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_inner.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_worker.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_wrong_origin_inner.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_inner.html create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js create mode 100644 mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/abstractmoduleloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/basemodule.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/loader.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/module.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleinfo.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloadcallback.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/module/moduleloader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/modulemanager.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/modulemanager_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/module/modulemanager_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/testdata/modA_1.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/testdata/modA_2.js create mode 100644 mapbox-gl/closure-library/closure/goog/module/testdata/modB_1.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/browserchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/browserchannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/browserchannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/browsertestchannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/bulkloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/bulkloader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/bulkloader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/bulkloaderhelper.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/channeldebug.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/channelrequest.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/channelrequest_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/channelrequest_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/cookies.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/cookies_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/cookies_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.css create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.gif create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test_response.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/errorcode.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/eventtype.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/filedownloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/filedownloader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/filedownloader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/httpstatus.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test_response.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.data create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeio_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame2.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame3.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader_testimg1.gif create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader_testimg2.gif create mode 100644 mapbox-gl/closure-library/closure/goog/net/imageloader_testimg3.gif create mode 100644 mapbox-gl/closure-library/closure/goog/net/ipaddress.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/ipaddress_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/ipaddress_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsloader.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsloader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsloader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsonp.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsonp_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/jsonp_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/mockiframeio.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/networkstatusmonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/networktester.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/networktester_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/networktester_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test1.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test2.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test3.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test4.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/tmpnetwork.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/websocket.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/websocket_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/websocket_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/wrapperxmlhttpfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrio.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrio_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrio_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhriopool.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrlike.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrmanager.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xmlhttp.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xmlhttpfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannelrole.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/directtransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/directtransport_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/frameelementmethodtransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/iframerelaytransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/nixtransport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/relay.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/testdata/access_checker.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/testdata/inner_peer.html create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/transport.js create mode 100644 mapbox-gl/closure-library/closure/goog/net/xpc/xpc.js create mode 100644 mapbox-gl/closure-library/closure/goog/object/object.js create mode 100644 mapbox-gl/closure-library/closure/goog/object/object_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/object/object_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/absoluteposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/abstractposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test_iframe.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/clientposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe1.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe2.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test_quirk.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/positioning_test_standard.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/positioning/viewportposition.js create mode 100644 mapbox-gl/closure-library/closure/goog/promise/promise.js create mode 100644 mapbox-gl/closure-library/closure/goog/promise/promise_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/promise/promise_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/promise/resolver.js create mode 100644 mapbox-gl/closure-library/closure/goog/promise/testsuiteadapter.js create mode 100644 mapbox-gl/closure-library/closure/goog/promise/thenable.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto/proto.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto/serializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto/serializer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto/serializer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/descriptor.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/lazydeserializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/message.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/message_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/message_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/objectserializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/package_test.pb.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/proto_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/proto_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/serializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/test.pb.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/textformatserializer.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/proto2/util.js create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/pubsub.js create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/pubsub_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/topicid.js create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub.js create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/reflect/reflect.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/chain_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/combine_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/deferredadaptor.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/deferredadaptor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/dependentresult.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/result_interface.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/resultutil.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/resultutil_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/simpleresult.js create mode 100644 mapbox-gl/closure-library/closure/goog/result/simpleresult_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/transform_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/result/wait_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/soy/data.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/data_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/soy/data_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/renderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/renderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/soy/renderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/soy.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/soy_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/soy/soy_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/soy/soy_testhelper.js create mode 100644 mapbox-gl/closure-library/closure/goog/spell/spellcheck.js create mode 100644 mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/stats/basicstat.js create mode 100644 mapbox-gl/closure-library/closure/goog/stats/basicstat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/stats/basicstat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/collectablestorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/collectablestoragetester.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/encryptedstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/errorcode.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/expiringstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/errorcode.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanism.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanismtester.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanism.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismseparationtester.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismsharingtester.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtestdefinition.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtester.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/richstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/richstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/richstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/storage.js create mode 100644 mapbox-gl/closure-library/closure/goog/storage/storage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/storage/storage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/const.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/const_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/const_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/linkify.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/linkify_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/linkify_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/newlines.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/newlines_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/newlines_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/parser.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/path.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/path_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/path_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/string.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/string_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/string_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringbuffer.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringformat.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringformat_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringformat_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/stringifier.js create mode 100644 mapbox-gl/closure-library/closure/goog/string/typedstring.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/avltree.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/avltree_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/avltree_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/circularbuffer.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/collection.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/collection_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/collection_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/heap.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/heap_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/heap_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/inversionmap.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/linkedmap.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/map.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/map_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/map_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/node.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/pool.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/pool_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/pool_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/prioritypool.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/priorityqueue.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/quadtree.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/quadtree_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/quadtree_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/queue.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/queue_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/queue_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/queue_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/set.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/set_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/set_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/set_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/simplepool.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/stringset.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/stringset_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/stringset_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/structs.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/structs_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/structs_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/treenode.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/treenode_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/treenode_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/trie.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/trie_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/structs/trie_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/weak/weak.js create mode 100644 mapbox-gl/closure-library/closure/goog/structs/weak/weak_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/bidi.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/bidi_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/bidi_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/cursor.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/cursor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/cursor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/style.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_quirks_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test_iframe_quirk.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test_iframe_standard.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test_quirk.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test_rect.svg create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_test_standard.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/stylescrollbartester.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/transform.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/transform_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/transition.js create mode 100644 mapbox-gl/closure-library/closure/goog/style/transition_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/style/transition_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/test_module.js create mode 100644 mapbox-gl/closure-library/closure/goog/test_module_dep.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asserts.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asserts_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asserts_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/benchmark.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/continuationtestcase.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/deferredtestcase.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/dom.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/dom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/dom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/dom.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/fieldmock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/testhelper.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/eventobserver.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/events.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/events_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/events_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/matchers.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/expectedfailures.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/blob.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/entry.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/file.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filereader.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filesystem.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filewriter.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/fs.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/fs/progressevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/functionmock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/functionmock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/functionmock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/graphics.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/i18n/asserts.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/jsunit.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/loosemock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/loosemock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/loosemock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessagechannel.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageport.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/messaging/mockportnetwork.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclassfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockclock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockcontrol.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockinterface.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockmatchers.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrandom.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrange.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrange_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockrange_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockstorage.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockuseragent.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/multitestrunner.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/net/xhrio.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/net/xhriopool.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/objectpropertystring.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/performancetable.css create mode 100644 mapbox-gl/closure-library/closure/goog/testing/performancetable.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/performancetimer.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/propertyreplacer.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/proto2/proto2.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/pseudorandom.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/recordfunction.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/shardingtestcase.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/singleton.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/singleton_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/singleton_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/stacktrace.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/storage/fakemechanism.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/strictmock.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/strictmock_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/strictmock_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/style.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/style_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/style/style_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/testcase.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/testcase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/testqueue.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/testrunner.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/rendererharness.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/style.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/style_reference.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/style_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/testing/ui/style_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/testing/watchers.js create mode 100644 mapbox-gl/closure-library/closure/goog/timer/timer.js create mode 100644 mapbox-gl/closure-library/closure/goog/timer/timer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/timer/timer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/entries.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/entries_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/entries_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/registry.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/registry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/registry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/testhelpers.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/tweak.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/tweakui.js create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/abstractspellchecker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/ac.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/remote.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/renderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/renderoptions.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/richinputhandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/richremote.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ac/richremotearraymatcher.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/activitymonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/advancedtooltip.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/animatedzippy.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/attachablemenu.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/bidiinput.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/bubble.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/button.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/button_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/button_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/button_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/buttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/buttonside.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charcounter.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charcounter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charcounter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charpicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charpicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/charpicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/checkbox.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/checkbox_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/checkbox_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/checkboxmenuitem.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/checkboxrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorbutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colormenubutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorpalette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorpicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/colorsplitbehavior.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/combobox.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/combobox_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/combobox_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/component.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/component_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/component_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/container.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/container_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/container_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/container_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerscroller.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/control.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/control_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/control_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/control_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/controlcontent.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/controlrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/cookieeditor.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/css3buttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/css3menubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/cssnames.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/custombutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/custombuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/customcolorpalette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/datepicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/datepicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/datepicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/datepickerrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/decorate.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/decorate_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/decorate_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/defaultdatepickerrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dialog.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dialog_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dialog_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/dragdropdetector.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/drilldownrow.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/bubble.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/defaulttoolbar.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/messages.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/tabpane.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/toolbarcontroller.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emoji.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emojipalette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/filteredmenu.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitem.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/flatbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/flatmenubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/formpost.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/formpost_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/formpost_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/gauge.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/gaugetheme.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hovercard.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hovercard_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hovercard_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvapalette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvpalette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/idgenerator.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/idletimer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/idletimer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/idletimer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/iframemask.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/iframemask_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/iframemask_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/imagelessbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/imagelessmenubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/inputdatepicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/itemevent.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/labelinput.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/labelinput_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/labelinput_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/linkbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flashobject.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flickr.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/googlevideo.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/media.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/media_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/media_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mediamodel.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mp3.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/photo.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/photo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/photo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/picasa.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/vimeo.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/youtube.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menu.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menu_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menu_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubar.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubardecorator.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubarrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubase.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubutton_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubutton_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubutton_test_frame.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuheader.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuheaderrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitem.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitem_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitem_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menurenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuseparator.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/modalpopup.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/option.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/palette.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/palette_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/palette_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/paletterenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popup.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popup_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popup_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupbase.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupbase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupbase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupdatepicker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupmenu.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/progressbar.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/prompt.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/prompt_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/prompt_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/rangemodel.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/ratings.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/registry.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/registry_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/registry_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/roundedpanel.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/roundedtabrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/scrollfloater.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/select.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/select_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/select_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmodel.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/separator.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/serverchart.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/serverchart_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/serverchart_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/slider.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/sliderbase.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitbehavior.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitpane.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitpane_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/splitpane_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/submenu.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/submenu_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/submenu_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/submenurenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tab.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tab_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tab_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbar.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbar_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbar_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tablesorter.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabpane.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabpane_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabpane_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/textarea.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/textarea_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/textarea_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/textarearenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/togglebutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbar.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbar_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbar_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarbutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarbuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarmenubutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarmenubuttonrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarselect.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarseparator.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/toolbartogglebutton.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tooltip.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tooltip_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tooltip_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/basenode.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/treenode.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/typeahead.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tristatemenuitem.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/tristatemenuitemrenderer.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/twothumbslider.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/zippy.js create mode 100644 mapbox-gl/closure-library/closure/goog/ui/zippy_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/ui/zippy_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/uri/uri.js create mode 100644 mapbox-gl/closure-library/closure/goog/uri/uri_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/uri/uri_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/uri/utils.js create mode 100644 mapbox-gl/closure-library/closure/goog/uri/utils_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/uri/utils_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/adobereader.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/flash.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/flash_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/flash_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/iphoto.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/jscript.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/jscript_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/jscript_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/keyboard.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/keyboard_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/picasa.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/platform.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/platform_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/platform_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/product.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/product_isversion.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/product_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/product_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragent.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragent_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragent_test.js create mode 100644 mapbox-gl/closure-library/closure/goog/useragent/useragenttestutil.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/float32array.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/float32array_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/float64array.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/float64array_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3d.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3d_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3f.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat3f_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4d.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4d_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4f.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/mat4f_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/matrix3.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/matrix3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/matrix4.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/matrix4_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/quaternion.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/quaternion_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/ray.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/ray_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2d.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2d_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2f.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec2f_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3d.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3d_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3f.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec3f_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4d.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4d_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4f.js create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec4f_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec_array_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/vec/vec_perf.html create mode 100644 mapbox-gl/closure-library/closure/goog/webgl/webgl.js create mode 100644 mapbox-gl/closure-library/closure/goog/window/window.js create mode 100644 mapbox-gl/closure-library/closure/goog/window/window_test.html create mode 100644 mapbox-gl/closure-library/closure/goog/window/window_test.js create mode 100644 mapbox-gl/closure-library/shamcs.py create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/base.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlparser.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlsanitizer.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/deps.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.html create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/jpeg_encoder/jpeg_encoder_basic.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum_test.html create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_async_test.html create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_test.html create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist_test.html create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/osapi/osapi.js create mode 100644 mapbox-gl/closure-library/third_party/closure/goog/svgpan/svgpan.js create mode 100644 mapbox-gl/css/ol.css create mode 100644 mapbox-gl/examples/Jugl.js create mode 100644 mapbox-gl/examples/accessible-require.js create mode 100644 mapbox-gl/examples/accessible.html create mode 100644 mapbox-gl/examples/accessible.js create mode 100644 mapbox-gl/examples/animation-require.js create mode 100644 mapbox-gl/examples/animation.html create mode 100644 mapbox-gl/examples/animation.js create mode 100644 mapbox-gl/examples/arcgis-tiled-require.js create mode 100644 mapbox-gl/examples/arcgis-tiled.html create mode 100644 mapbox-gl/examples/arcgis-tiled.js create mode 100644 mapbox-gl/examples/attributions-require.js create mode 100644 mapbox-gl/examples/attributions.html create mode 100644 mapbox-gl/examples/attributions.js create mode 100644 mapbox-gl/examples/bind-input-require.js create mode 100644 mapbox-gl/examples/bind-input.html create mode 100644 mapbox-gl/examples/bind-input.js create mode 100644 mapbox-gl/examples/bing-maps-require.js create mode 100644 mapbox-gl/examples/bing-maps.html create mode 100644 mapbox-gl/examples/bing-maps.js create mode 100644 mapbox-gl/examples/box-selection-require.js create mode 100644 mapbox-gl/examples/box-selection.html create mode 100644 mapbox-gl/examples/box-selection.js create mode 100644 mapbox-gl/examples/brightness-contrast-require.js create mode 100644 mapbox-gl/examples/brightness-contrast.html create mode 100644 mapbox-gl/examples/brightness-contrast.js create mode 100644 mapbox-gl/examples/button-title-require.js create mode 100644 mapbox-gl/examples/button-title.html create mode 100644 mapbox-gl/examples/button-title.js create mode 100644 mapbox-gl/examples/canvas-tiles-require.js create mode 100644 mapbox-gl/examples/canvas-tiles.html create mode 100644 mapbox-gl/examples/canvas-tiles.js create mode 100644 mapbox-gl/examples/center-require.js create mode 100644 mapbox-gl/examples/center.html create mode 100644 mapbox-gl/examples/center.js create mode 100644 mapbox-gl/examples/cluster-require.js create mode 100644 mapbox-gl/examples/cluster.html create mode 100644 mapbox-gl/examples/cluster.js create mode 100644 mapbox-gl/examples/custom-controls-require.js create mode 100644 mapbox-gl/examples/custom-controls.html create mode 100644 mapbox-gl/examples/custom-controls.js create mode 100644 mapbox-gl/examples/d3-require.js create mode 100644 mapbox-gl/examples/d3.html create mode 100644 mapbox-gl/examples/d3.js create mode 100644 mapbox-gl/examples/data/Butterfly.png create mode 100644 mapbox-gl/examples/data/IGNWMTSCapabilities.xml create mode 100644 mapbox-gl/examples/data/WMTSCapabilities.xml create mode 100644 mapbox-gl/examples/data/arrow.png create mode 100644 mapbox-gl/examples/data/geojson/countries-110m.geojson create mode 100644 mapbox-gl/examples/data/geojson/countries.geojson create mode 100644 mapbox-gl/examples/data/geojson/line-samples.geojson create mode 100644 mapbox-gl/examples/data/geojson/point-samples.geojson create mode 100644 mapbox-gl/examples/data/geojson/polygon-samples.geojson create mode 100644 mapbox-gl/examples/data/geojson/switzerland.geojson create mode 100644 mapbox-gl/examples/data/geolocation-orientation.json create mode 100644 mapbox-gl/examples/data/geolocation_marker.png create mode 100644 mapbox-gl/examples/data/geolocation_marker_heading.png create mode 100644 mapbox-gl/examples/data/gml/topp-states-wfs.xml create mode 100644 mapbox-gl/examples/data/gpx/fells_loop.gpx create mode 100644 mapbox-gl/examples/data/icon.png create mode 100644 mapbox-gl/examples/data/igc/Clement-Latour.igc create mode 100644 mapbox-gl/examples/data/igc/Damien-de-Baenst.igc create mode 100644 mapbox-gl/examples/data/igc/Sylvain-Dhonneur.igc create mode 100644 mapbox-gl/examples/data/igc/Tom-Payne.igc create mode 100644 mapbox-gl/examples/data/igc/Ulrich-Prinz.igc create mode 100644 mapbox-gl/examples/data/kml/2012-02-10.kml create mode 100644 mapbox-gl/examples/data/kml/2012_Earthquakes_Mag5.kml create mode 100644 mapbox-gl/examples/data/kml/timezones.kml create mode 100644 mapbox-gl/examples/data/ogcsample.xml create mode 100644 mapbox-gl/examples/data/sld/countries.sld create mode 100644 mapbox-gl/examples/data/topojson/us.json create mode 100644 mapbox-gl/examples/data/topojson/world-110m.json create mode 100644 mapbox-gl/examples/device-orientation-require.js create mode 100644 mapbox-gl/examples/device-orientation.html create mode 100644 mapbox-gl/examples/device-orientation.js create mode 100644 mapbox-gl/examples/drag-and-drop-image-vector-require.js create mode 100644 mapbox-gl/examples/drag-and-drop-image-vector.html create mode 100644 mapbox-gl/examples/drag-and-drop-image-vector.js create mode 100644 mapbox-gl/examples/drag-and-drop-require.js create mode 100644 mapbox-gl/examples/drag-and-drop.html create mode 100644 mapbox-gl/examples/drag-and-drop.js create mode 100644 mapbox-gl/examples/drag-features-require.js create mode 100644 mapbox-gl/examples/drag-features.html create mode 100644 mapbox-gl/examples/drag-features.js create mode 100644 mapbox-gl/examples/drag-rotate-and-zoom-require.js create mode 100644 mapbox-gl/examples/drag-rotate-and-zoom.html create mode 100644 mapbox-gl/examples/drag-rotate-and-zoom.js create mode 100644 mapbox-gl/examples/draw-and-modify-features-require.js create mode 100644 mapbox-gl/examples/draw-and-modify-features.html create mode 100644 mapbox-gl/examples/draw-and-modify-features.js create mode 100644 mapbox-gl/examples/draw-features-require.js create mode 100644 mapbox-gl/examples/draw-features.html create mode 100644 mapbox-gl/examples/draw-features.js create mode 100644 mapbox-gl/examples/dynamic-data-require.js create mode 100644 mapbox-gl/examples/dynamic-data.html create mode 100644 mapbox-gl/examples/dynamic-data.js create mode 100644 mapbox-gl/examples/earthquake-clusters-require.js create mode 100644 mapbox-gl/examples/earthquake-clusters.html create mode 100644 mapbox-gl/examples/earthquake-clusters.js create mode 100644 mapbox-gl/examples/epsg-4326-require.js create mode 100644 mapbox-gl/examples/epsg-4326.html create mode 100644 mapbox-gl/examples/epsg-4326.js create mode 100644 mapbox-gl/examples/example-list.js create mode 100644 mapbox-gl/examples/example-list.xml create mode 100644 mapbox-gl/examples/export-map-require.js create mode 100644 mapbox-gl/examples/export-map.html create mode 100644 mapbox-gl/examples/export-map.js create mode 100644 mapbox-gl/examples/fractal-require.js create mode 100644 mapbox-gl/examples/fractal.html create mode 100644 mapbox-gl/examples/fractal.js create mode 100644 mapbox-gl/examples/full-screen-drag-rotate-and-zoom-require.js create mode 100644 mapbox-gl/examples/full-screen-drag-rotate-and-zoom.html create mode 100644 mapbox-gl/examples/full-screen-drag-rotate-and-zoom.js create mode 100644 mapbox-gl/examples/full-screen-require.js create mode 100644 mapbox-gl/examples/full-screen.html create mode 100644 mapbox-gl/examples/full-screen.js create mode 100644 mapbox-gl/examples/geojson-require.js create mode 100644 mapbox-gl/examples/geojson.html create mode 100644 mapbox-gl/examples/geojson.js create mode 100644 mapbox-gl/examples/geolocation-orientation-require.js create mode 100644 mapbox-gl/examples/geolocation-orientation.html create mode 100644 mapbox-gl/examples/geolocation-orientation.js create mode 100644 mapbox-gl/examples/geolocation-require.js create mode 100644 mapbox-gl/examples/geolocation.html create mode 100644 mapbox-gl/examples/geolocation.js create mode 100644 mapbox-gl/examples/getfeatureinfo-image-require.js create mode 100644 mapbox-gl/examples/getfeatureinfo-image.html create mode 100644 mapbox-gl/examples/getfeatureinfo-image.js create mode 100644 mapbox-gl/examples/getfeatureinfo-tile-require.js create mode 100644 mapbox-gl/examples/getfeatureinfo-tile.html create mode 100644 mapbox-gl/examples/getfeatureinfo-tile.js create mode 100644 mapbox-gl/examples/gpx-require.js create mode 100644 mapbox-gl/examples/gpx.html create mode 100644 mapbox-gl/examples/gpx.js create mode 100644 mapbox-gl/examples/graticule-require.js create mode 100644 mapbox-gl/examples/graticule.html create mode 100644 mapbox-gl/examples/graticule.js create mode 100644 mapbox-gl/examples/heatmap-earthquakes-require.js create mode 100644 mapbox-gl/examples/heatmap-earthquakes.html create mode 100644 mapbox-gl/examples/heatmap-earthquakes.js create mode 100644 mapbox-gl/examples/hue-saturation-require.js create mode 100644 mapbox-gl/examples/hue-saturation.html create mode 100644 mapbox-gl/examples/hue-saturation.js create mode 100644 mapbox-gl/examples/icon-require.js create mode 100644 mapbox-gl/examples/icon-sprite-webgl-require.js create mode 100644 mapbox-gl/examples/icon-sprite-webgl.html create mode 100644 mapbox-gl/examples/icon-sprite-webgl.js create mode 100644 mapbox-gl/examples/icon.html create mode 100644 mapbox-gl/examples/icon.js create mode 100644 mapbox-gl/examples/igc-require.js create mode 100644 mapbox-gl/examples/igc.html create mode 100644 mapbox-gl/examples/igc.js create mode 100644 mapbox-gl/examples/image-filter-require.js create mode 100644 mapbox-gl/examples/image-filter.html create mode 100644 mapbox-gl/examples/image-filter.js create mode 100644 mapbox-gl/examples/image-vector-layer-require.js create mode 100644 mapbox-gl/examples/image-vector-layer.html create mode 100644 mapbox-gl/examples/image-vector-layer.js create mode 100644 mapbox-gl/examples/index.html create mode 100644 mapbox-gl/examples/kml-earthquakes-require.js create mode 100644 mapbox-gl/examples/kml-earthquakes.html create mode 100644 mapbox-gl/examples/kml-earthquakes.js create mode 100644 mapbox-gl/examples/kml-require.js create mode 100644 mapbox-gl/examples/kml-timezones-require.js create mode 100644 mapbox-gl/examples/kml-timezones.html create mode 100644 mapbox-gl/examples/kml-timezones.js create mode 100644 mapbox-gl/examples/kml.html create mode 100644 mapbox-gl/examples/kml.js create mode 100644 mapbox-gl/examples/layer-clipping-require.js create mode 100644 mapbox-gl/examples/layer-clipping-webgl-require.js create mode 100644 mapbox-gl/examples/layer-clipping-webgl.html create mode 100644 mapbox-gl/examples/layer-clipping-webgl.js create mode 100644 mapbox-gl/examples/layer-clipping.html create mode 100644 mapbox-gl/examples/layer-clipping.js create mode 100644 mapbox-gl/examples/layer-extent-require.js create mode 100644 mapbox-gl/examples/layer-extent.html create mode 100644 mapbox-gl/examples/layer-extent.js create mode 100644 mapbox-gl/examples/layer-group-require.js create mode 100644 mapbox-gl/examples/layer-group.html create mode 100644 mapbox-gl/examples/layer-group.js create mode 100644 mapbox-gl/examples/layer-spy-require.js create mode 100644 mapbox-gl/examples/layer-spy.html create mode 100644 mapbox-gl/examples/layer-spy.js create mode 100644 mapbox-gl/examples/layer-swipe-require.js create mode 100644 mapbox-gl/examples/layer-swipe.html create mode 100644 mapbox-gl/examples/layer-swipe.js create mode 100644 mapbox-gl/examples/lazy-source-require.js create mode 100644 mapbox-gl/examples/lazy-source.html create mode 100644 mapbox-gl/examples/lazy-source.js create mode 100644 mapbox-gl/examples/line-arrows-require.js create mode 100644 mapbox-gl/examples/line-arrows.html create mode 100644 mapbox-gl/examples/line-arrows.js create mode 100644 mapbox-gl/examples/loader.js create mode 100644 mapbox-gl/examples/localized-openstreetmap-require.js create mode 100644 mapbox-gl/examples/localized-openstreetmap.html create mode 100644 mapbox-gl/examples/localized-openstreetmap.js create mode 100644 mapbox-gl/examples/mapbox-gl-require.js create mode 100644 mapbox-gl/examples/mapbox-gl.html create mode 100644 mapbox-gl/examples/mapbox-gl.js create mode 100644 mapbox-gl/examples/mapguide-untiled-require.js create mode 100644 mapbox-gl/examples/mapguide-untiled.html create mode 100644 mapbox-gl/examples/mapguide-untiled.js create mode 100644 mapbox-gl/examples/mapquest-require.js create mode 100644 mapbox-gl/examples/mapquest.html create mode 100644 mapbox-gl/examples/mapquest.js create mode 100644 mapbox-gl/examples/measure-require.js create mode 100644 mapbox-gl/examples/measure.html create mode 100644 mapbox-gl/examples/measure.js create mode 100644 mapbox-gl/examples/min-max-resolution-require.js create mode 100644 mapbox-gl/examples/min-max-resolution.html create mode 100644 mapbox-gl/examples/min-max-resolution.js create mode 100644 mapbox-gl/examples/mobile-full-screen-require.js create mode 100644 mapbox-gl/examples/mobile-full-screen.html create mode 100644 mapbox-gl/examples/mobile-full-screen.js create mode 100644 mapbox-gl/examples/modify-features-require.js create mode 100644 mapbox-gl/examples/modify-features.html create mode 100644 mapbox-gl/examples/modify-features.js create mode 100644 mapbox-gl/examples/modify-test-require.js create mode 100644 mapbox-gl/examples/modify-test.html create mode 100644 mapbox-gl/examples/modify-test.js create mode 100644 mapbox-gl/examples/mouse-position-require.js create mode 100644 mapbox-gl/examples/mouse-position.html create mode 100644 mapbox-gl/examples/mouse-position.js create mode 100644 mapbox-gl/examples/moveend-require.js create mode 100644 mapbox-gl/examples/moveend.html create mode 100644 mapbox-gl/examples/moveend.js create mode 100644 mapbox-gl/examples/navigation-controls-require.js create mode 100644 mapbox-gl/examples/navigation-controls.html create mode 100644 mapbox-gl/examples/navigation-controls.js create mode 100644 mapbox-gl/examples/overlay-require.js create mode 100644 mapbox-gl/examples/overlay.html create mode 100644 mapbox-gl/examples/overlay.js create mode 100644 mapbox-gl/examples/overviewmap-custom-require.js create mode 100644 mapbox-gl/examples/overviewmap-custom.html create mode 100644 mapbox-gl/examples/overviewmap-custom.js create mode 100644 mapbox-gl/examples/overviewmap-require.js create mode 100644 mapbox-gl/examples/overviewmap.html create mode 100644 mapbox-gl/examples/overviewmap.js create mode 100644 mapbox-gl/examples/polygon-styles-require.js create mode 100644 mapbox-gl/examples/polygon-styles.html create mode 100644 mapbox-gl/examples/polygon-styles.js create mode 100644 mapbox-gl/examples/popup-require.js create mode 100644 mapbox-gl/examples/popup.html create mode 100644 mapbox-gl/examples/popup.js create mode 100644 mapbox-gl/examples/preload-require.js create mode 100644 mapbox-gl/examples/preload.html create mode 100644 mapbox-gl/examples/preload.js create mode 100644 mapbox-gl/examples/regularshape-require.js create mode 100644 mapbox-gl/examples/regularshape.html create mode 100644 mapbox-gl/examples/regularshape.js create mode 100644 mapbox-gl/examples/rotation-require.js create mode 100644 mapbox-gl/examples/rotation.html create mode 100644 mapbox-gl/examples/rotation.js create mode 100644 mapbox-gl/examples/scale-line-require.js create mode 100644 mapbox-gl/examples/scale-line.html create mode 100644 mapbox-gl/examples/scale-line.js create mode 100644 mapbox-gl/examples/select-features-require.js create mode 100644 mapbox-gl/examples/select-features.html create mode 100644 mapbox-gl/examples/select-features.js create mode 100644 mapbox-gl/examples/semi-transparent-layer-require.js create mode 100644 mapbox-gl/examples/semi-transparent-layer.html create mode 100644 mapbox-gl/examples/semi-transparent-layer.js create mode 100644 mapbox-gl/examples/side-by-side-require.js create mode 100644 mapbox-gl/examples/side-by-side.html create mode 100644 mapbox-gl/examples/side-by-side.js create mode 100644 mapbox-gl/examples/simple-require.js create mode 100644 mapbox-gl/examples/simple.html create mode 100644 mapbox-gl/examples/simple.js create mode 100644 mapbox-gl/examples/sphere-mollweide-require.js create mode 100644 mapbox-gl/examples/sphere-mollweide.html create mode 100644 mapbox-gl/examples/sphere-mollweide.js create mode 100644 mapbox-gl/examples/stamen-require.js create mode 100644 mapbox-gl/examples/stamen.html create mode 100644 mapbox-gl/examples/stamen.js create mode 100644 mapbox-gl/examples/static-image-require.js create mode 100644 mapbox-gl/examples/static-image.html create mode 100644 mapbox-gl/examples/static-image.js create mode 100644 mapbox-gl/examples/symbol-atlas-webgl-require.js create mode 100644 mapbox-gl/examples/symbol-atlas-webgl.html create mode 100644 mapbox-gl/examples/symbol-atlas-webgl.js create mode 100644 mapbox-gl/examples/synthetic-lines-require.js create mode 100644 mapbox-gl/examples/synthetic-lines.html create mode 100644 mapbox-gl/examples/synthetic-lines.js create mode 100644 mapbox-gl/examples/synthetic-points-require.js create mode 100644 mapbox-gl/examples/synthetic-points.html create mode 100644 mapbox-gl/examples/synthetic-points.js create mode 100644 mapbox-gl/examples/teleport-require.js create mode 100644 mapbox-gl/examples/teleport.html create mode 100644 mapbox-gl/examples/teleport.js create mode 100644 mapbox-gl/examples/tile-load-events-require.js create mode 100644 mapbox-gl/examples/tile-load-events.html create mode 100644 mapbox-gl/examples/tile-load-events.js create mode 100644 mapbox-gl/examples/tile-vector-require.js create mode 100644 mapbox-gl/examples/tile-vector.html create mode 100644 mapbox-gl/examples/tile-vector.js create mode 100644 mapbox-gl/examples/tilejson-require.js create mode 100644 mapbox-gl/examples/tilejson.html create mode 100644 mapbox-gl/examples/tilejson.js create mode 100644 mapbox-gl/examples/tileutfgrid-require.js create mode 100644 mapbox-gl/examples/tileutfgrid.html create mode 100644 mapbox-gl/examples/tileutfgrid.js create mode 100644 mapbox-gl/examples/tissot-require.js create mode 100644 mapbox-gl/examples/tissot.html create mode 100644 mapbox-gl/examples/tissot.js create mode 100644 mapbox-gl/examples/topojson-require.js create mode 100644 mapbox-gl/examples/topojson.html create mode 100644 mapbox-gl/examples/topojson.js create mode 100644 mapbox-gl/examples/vector-labels-require.js create mode 100644 mapbox-gl/examples/vector-labels.html create mode 100644 mapbox-gl/examples/vector-labels.js create mode 100644 mapbox-gl/examples/vector-layer-require.js create mode 100644 mapbox-gl/examples/vector-layer.html create mode 100644 mapbox-gl/examples/vector-layer.js create mode 100644 mapbox-gl/examples/vector-osm-require.js create mode 100644 mapbox-gl/examples/vector-osm.html create mode 100644 mapbox-gl/examples/vector-osm.js create mode 100644 mapbox-gl/examples/vector-wfs-require.js create mode 100644 mapbox-gl/examples/vector-wfs.html create mode 100644 mapbox-gl/examples/vector-wfs.js create mode 100644 mapbox-gl/examples/wkt-require.js create mode 100644 mapbox-gl/examples/wkt.html create mode 100644 mapbox-gl/examples/wkt.js create mode 100644 mapbox-gl/examples/wms-capabilities-require.js create mode 100644 mapbox-gl/examples/wms-capabilities.html create mode 100644 mapbox-gl/examples/wms-capabilities.js create mode 100644 mapbox-gl/examples/wms-custom-proj-require.js create mode 100644 mapbox-gl/examples/wms-custom-proj.html create mode 100644 mapbox-gl/examples/wms-custom-proj.js create mode 100644 mapbox-gl/examples/wms-image-custom-proj-require.js create mode 100644 mapbox-gl/examples/wms-image-custom-proj.html create mode 100644 mapbox-gl/examples/wms-image-custom-proj.js create mode 100644 mapbox-gl/examples/wms-image-require.js create mode 100644 mapbox-gl/examples/wms-image.html create mode 100644 mapbox-gl/examples/wms-image.js create mode 100644 mapbox-gl/examples/wms-no-proj-require.js create mode 100644 mapbox-gl/examples/wms-no-proj.html create mode 100644 mapbox-gl/examples/wms-no-proj.js create mode 100644 mapbox-gl/examples/wms-tiled-require.js create mode 100644 mapbox-gl/examples/wms-tiled.html create mode 100644 mapbox-gl/examples/wms-tiled.js create mode 100644 mapbox-gl/examples/wmts-capabilities-require.js create mode 100644 mapbox-gl/examples/wmts-capabilities.html create mode 100644 mapbox-gl/examples/wmts-capabilities.js create mode 100644 mapbox-gl/examples/wmts-hidpi-require.js create mode 100644 mapbox-gl/examples/wmts-hidpi.html create mode 100644 mapbox-gl/examples/wmts-hidpi.js create mode 100644 mapbox-gl/examples/wmts-layer-from-capabilities-require.js create mode 100644 mapbox-gl/examples/wmts-layer-from-capabilities.html create mode 100644 mapbox-gl/examples/wmts-layer-from-capabilities.js create mode 100644 mapbox-gl/examples/wmts-require.js create mode 100644 mapbox-gl/examples/wmts.html create mode 100644 mapbox-gl/examples/wmts.js create mode 100644 mapbox-gl/examples/xyz-esri-4326-512-require.js create mode 100644 mapbox-gl/examples/xyz-esri-4326-512.html create mode 100644 mapbox-gl/examples/xyz-esri-4326-512.js create mode 100644 mapbox-gl/examples/xyz-esri-require.js create mode 100644 mapbox-gl/examples/xyz-esri.html create mode 100644 mapbox-gl/examples/xyz-esri.js create mode 100644 mapbox-gl/examples/xyz-require.js create mode 100644 mapbox-gl/examples/xyz-retina-require.js create mode 100644 mapbox-gl/examples/xyz-retina.html create mode 100644 mapbox-gl/examples/xyz-retina.js create mode 100644 mapbox-gl/examples/xyz.html create mode 100644 mapbox-gl/examples/xyz.js create mode 100644 mapbox-gl/examples/zoom-constrained-require.js create mode 100644 mapbox-gl/examples/zoom-constrained.html create mode 100644 mapbox-gl/examples/zoom-constrained.js create mode 100644 mapbox-gl/examples/zoomify-require.js create mode 100644 mapbox-gl/examples/zoomify.html create mode 100644 mapbox-gl/examples/zoomify.js create mode 100644 mapbox-gl/examples/zoomslider-require.js create mode 100644 mapbox-gl/examples/zoomslider.html create mode 100644 mapbox-gl/examples/zoomslider.js create mode 100644 mapbox-gl/ol.ext/rbush.js create mode 100644 mapbox-gl/ol/ol/animation.js create mode 100644 mapbox-gl/ol/ol/animation.jsdoc create mode 100644 mapbox-gl/ol/ol/array.js create mode 100644 mapbox-gl/ol/ol/attribution.js create mode 100644 mapbox-gl/ol/ol/binary.js create mode 100644 mapbox-gl/ol/ol/canvasfunction.js create mode 100644 mapbox-gl/ol/ol/centerconstraint.js create mode 100644 mapbox-gl/ol/ol/collection.js create mode 100644 mapbox-gl/ol/ol/color/color.js create mode 100644 mapbox-gl/ol/ol/color/color.jsdoc create mode 100644 mapbox-gl/ol/ol/color/colormatrix.js create mode 100644 mapbox-gl/ol/ol/constraints.js create mode 100644 mapbox-gl/ol/ol/control.jsdoc create mode 100644 mapbox-gl/ol/ol/control/attributioncontrol.js create mode 100644 mapbox-gl/ol/ol/control/control.js create mode 100644 mapbox-gl/ol/ol/control/controldefaults.js create mode 100644 mapbox-gl/ol/ol/control/fullscreencontrol.js create mode 100644 mapbox-gl/ol/ol/control/mousepositioncontrol.js create mode 100644 mapbox-gl/ol/ol/control/overviewmapcontrol.js create mode 100644 mapbox-gl/ol/ol/control/rotatecontrol.js create mode 100644 mapbox-gl/ol/ol/control/scalelinecontrol.js create mode 100644 mapbox-gl/ol/ol/control/zoomcontrol.js create mode 100644 mapbox-gl/ol/ol/control/zoomslidercontrol.js create mode 100644 mapbox-gl/ol/ol/control/zoomtoextentcontrol.js create mode 100644 mapbox-gl/ol/ol/coordinate.js create mode 100644 mapbox-gl/ol/ol/coordinate.jsdoc create mode 100644 mapbox-gl/ol/ol/css.js create mode 100644 mapbox-gl/ol/ol/deviceorientation.js create mode 100644 mapbox-gl/ol/ol/dom/dom.js create mode 100644 mapbox-gl/ol/ol/dom/input.js create mode 100644 mapbox-gl/ol/ol/easing.js create mode 100644 mapbox-gl/ol/ol/easing.jsdoc create mode 100644 mapbox-gl/ol/ol/ellipsoid/ellipsoid.js create mode 100644 mapbox-gl/ol/ol/ellipsoid/wgs84ellipsoid.js create mode 100644 mapbox-gl/ol/ol/events/condition.js create mode 100644 mapbox-gl/ol/ol/events/condition.jsdoc create mode 100644 mapbox-gl/ol/ol/extent.js create mode 100644 mapbox-gl/ol/ol/extent.jsdoc create mode 100644 mapbox-gl/ol/ol/feature.js create mode 100644 mapbox-gl/ol/ol/feature.jsdoc create mode 100644 mapbox-gl/ol/ol/featureoverlay.js create mode 100644 mapbox-gl/ol/ol/format.jsdoc create mode 100644 mapbox-gl/ol/ol/format/binaryfeatureformat.js create mode 100644 mapbox-gl/ol/ol/format/featureformat.js create mode 100644 mapbox-gl/ol/ol/format/format.js create mode 100644 mapbox-gl/ol/ol/format/geojsonformat.js create mode 100644 mapbox-gl/ol/ol/format/gml/gml2format.js create mode 100644 mapbox-gl/ol/ol/format/gml/gml3format.js create mode 100644 mapbox-gl/ol/ol/format/gml/gmlbaseformat.js create mode 100644 mapbox-gl/ol/ol/format/gpxformat.js create mode 100644 mapbox-gl/ol/ol/format/igcformat.js create mode 100644 mapbox-gl/ol/ol/format/jsonfeatureformat.js create mode 100644 mapbox-gl/ol/ol/format/kmlformat.js create mode 100644 mapbox-gl/ol/ol/format/osmxmlformat.js create mode 100644 mapbox-gl/ol/ol/format/owsformat.js create mode 100644 mapbox-gl/ol/ol/format/polylineformat.js create mode 100644 mapbox-gl/ol/ol/format/textfeatureformat.js create mode 100644 mapbox-gl/ol/ol/format/topojsonformat.js create mode 100644 mapbox-gl/ol/ol/format/wfsformat.js create mode 100644 mapbox-gl/ol/ol/format/wktformat.js create mode 100644 mapbox-gl/ol/ol/format/wmscapabilitiesformat.js create mode 100644 mapbox-gl/ol/ol/format/wmsgetfeatureinfoformat.js create mode 100644 mapbox-gl/ol/ol/format/wmtscapabilitiesformat.js create mode 100644 mapbox-gl/ol/ol/format/xlinkformat.js create mode 100644 mapbox-gl/ol/ol/format/xmlfeatureformat.js create mode 100644 mapbox-gl/ol/ol/format/xmlformat.js create mode 100644 mapbox-gl/ol/ol/format/xsdformat.js create mode 100644 mapbox-gl/ol/ol/framestate.js create mode 100644 mapbox-gl/ol/ol/geolocation.js create mode 100644 mapbox-gl/ol/ol/geom/circle.js create mode 100644 mapbox-gl/ol/ol/geom/flat/areaflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/centerflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/closestflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/containsflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/deflateflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/flipflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/geodesicflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/inflateflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/interiorpointflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/interpolateflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/intersectsextent.js create mode 100644 mapbox-gl/ol/ol/geom/flat/lengthflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/orientflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/reverseflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/segmentsflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/simplifyflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/flat/transformflatgeom.js create mode 100644 mapbox-gl/ol/ol/geom/geom.jsdoc create mode 100644 mapbox-gl/ol/ol/geom/geometry.js create mode 100644 mapbox-gl/ol/ol/geom/geometrycollection.js create mode 100644 mapbox-gl/ol/ol/geom/linearring.js create mode 100644 mapbox-gl/ol/ol/geom/linestring.js create mode 100644 mapbox-gl/ol/ol/geom/multilinestring.js create mode 100644 mapbox-gl/ol/ol/geom/multipoint.js create mode 100644 mapbox-gl/ol/ol/geom/multipolygon.js create mode 100644 mapbox-gl/ol/ol/geom/point.js create mode 100644 mapbox-gl/ol/ol/geom/polygon.js create mode 100644 mapbox-gl/ol/ol/geom/simplegeometry.js create mode 100644 mapbox-gl/ol/ol/graticule.js create mode 100644 mapbox-gl/ol/ol/has.js create mode 100644 mapbox-gl/ol/ol/has.jsdoc create mode 100644 mapbox-gl/ol/ol/image.js create mode 100644 mapbox-gl/ol/ol/imagebase.js create mode 100644 mapbox-gl/ol/ol/imagecanvas.js create mode 100644 mapbox-gl/ol/ol/imageloadfunction.js create mode 100644 mapbox-gl/ol/ol/imagetile.js create mode 100644 mapbox-gl/ol/ol/imageurlfunction.js create mode 100644 mapbox-gl/ol/ol/interaction/doubleclickzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/draganddropinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/dragboxinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/dragpaninteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/dragrotateandzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/dragrotateinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/dragzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/drawinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/interaction.js create mode 100644 mapbox-gl/ol/ol/interaction/interaction.jsdoc create mode 100644 mapbox-gl/ol/ol/interaction/interactiondefaults.js create mode 100644 mapbox-gl/ol/ol/interaction/keyboardpaninteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/keyboardzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/modifyinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/mousewheelzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/pinchrotateinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/pinchzoominteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/pointerinteraction.js create mode 100644 mapbox-gl/ol/ol/interaction/selectinteraction.js create mode 100644 mapbox-gl/ol/ol/kinetic.js create mode 100644 mapbox-gl/ol/ol/layer/heatmaplayer.js create mode 100644 mapbox-gl/ol/ol/layer/imagelayer.js create mode 100644 mapbox-gl/ol/ol/layer/layer.js create mode 100644 mapbox-gl/ol/ol/layer/layer.jsdoc create mode 100644 mapbox-gl/ol/ol/layer/layerbase.js create mode 100644 mapbox-gl/ol/ol/layer/layergroup.js create mode 100644 mapbox-gl/ol/ol/layer/tilelayer.js create mode 100644 mapbox-gl/ol/ol/layer/vectorlayer.js create mode 100644 mapbox-gl/ol/ol/loadingstrategy.js create mode 100644 mapbox-gl/ol/ol/loadingstrategy.jsdoc create mode 100644 mapbox-gl/ol/ol/map.js create mode 100644 mapbox-gl/ol/ol/mapbrowserevent.js create mode 100644 mapbox-gl/ol/ol/mapevent.js create mode 100644 mapbox-gl/ol/ol/math.js create mode 100644 mapbox-gl/ol/ol/object.js create mode 100644 mapbox-gl/ol/ol/observable.js create mode 100644 mapbox-gl/ol/ol/ol.js create mode 100644 mapbox-gl/ol/ol/overlay.js create mode 100644 mapbox-gl/ol/ol/pixel.js create mode 100644 mapbox-gl/ol/ol/pointer/eventsource.js create mode 100644 mapbox-gl/ol/ol/pointer/mousesource.js create mode 100644 mapbox-gl/ol/ol/pointer/mssource.js create mode 100644 mapbox-gl/ol/ol/pointer/nativesource.js create mode 100644 mapbox-gl/ol/ol/pointer/pointerevent.js create mode 100644 mapbox-gl/ol/ol/pointer/pointereventhandler.js create mode 100644 mapbox-gl/ol/ol/pointer/touchsource.js create mode 100644 mapbox-gl/ol/ol/proj/common.js create mode 100644 mapbox-gl/ol/ol/proj/epsg3857projection.js create mode 100644 mapbox-gl/ol/ol/proj/epsg4326projection.js create mode 100644 mapbox-gl/ol/ol/proj/proj.js create mode 100644 mapbox-gl/ol/ol/proj/proj.jsdoc create mode 100644 mapbox-gl/ol/ol/render.jsdoc create mode 100644 mapbox-gl/ol/ol/render/box.js create mode 100644 mapbox-gl/ol/ol/render/canvas/canvas.js create mode 100644 mapbox-gl/ol/ol/render/canvas/canvas.jsdoc create mode 100644 mapbox-gl/ol/ol/render/canvas/canvasimmediate.js create mode 100644 mapbox-gl/ol/ol/render/canvas/canvasreplay.js create mode 100644 mapbox-gl/ol/ol/render/ireplay.js create mode 100644 mapbox-gl/ol/ol/render/ivectorcontext.js create mode 100644 mapbox-gl/ol/ol/render/renderevent.js create mode 100644 mapbox-gl/ol/ol/render/vector.js create mode 100644 mapbox-gl/ol/ol/render/webgl/webglimagecolor.glsl create mode 100644 mapbox-gl/ol/ol/render/webgl/webglimagecolorshader.js create mode 100644 mapbox-gl/ol/ol/render/webgl/webglimagedefault.glsl create mode 100644 mapbox-gl/ol/ol/render/webgl/webglimagedefaultshader.js create mode 100644 mapbox-gl/ol/ol/render/webgl/webglimmediate.js create mode 100644 mapbox-gl/ol/ol/render/webgl/webglreplay.js create mode 100644 mapbox-gl/ol/ol/renderer/canvas/canvasimagelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/canvas/canvaslayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/canvas/canvasmaprenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/canvas/canvastilelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/canvas/canvasvectorlayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/dom/domimagelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/dom/domlayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/dom/dommaprenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/dom/domtilelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/dom/domvectorlayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/layerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/maprenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglimagelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webgllayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglmapcolor.glsl create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglmapcolorshader.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglmapdefault.glsl create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglmapdefaultshader.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglmaprenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webgltilelayer.glsl create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webgltilelayerrenderer.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webgltilelayershader.js create mode 100644 mapbox-gl/ol/ol/renderer/webgl/webglvectorlayerrenderer.js create mode 100644 mapbox-gl/ol/ol/resolutionconstraint.js create mode 100644 mapbox-gl/ol/ol/rotationconstraint.js create mode 100644 mapbox-gl/ol/ol/size.js create mode 100644 mapbox-gl/ol/ol/source.jsdoc create mode 100644 mapbox-gl/ol/ol/source/bingmapssource.js create mode 100644 mapbox-gl/ol/ol/source/clustersource.js create mode 100644 mapbox-gl/ol/ol/source/formatvectorsource.js create mode 100644 mapbox-gl/ol/ol/source/geojsonsource.js create mode 100644 mapbox-gl/ol/ol/source/gpxsource.js create mode 100644 mapbox-gl/ol/ol/source/igcsource.js create mode 100644 mapbox-gl/ol/ol/source/imagecanvassource.js create mode 100644 mapbox-gl/ol/ol/source/imagemapguidesource.js create mode 100644 mapbox-gl/ol/ol/source/imagesource.js create mode 100644 mapbox-gl/ol/ol/source/imagestaticsource.js create mode 100644 mapbox-gl/ol/ol/source/imagevectorsource.js create mode 100644 mapbox-gl/ol/ol/source/imagewmssource.js create mode 100644 mapbox-gl/ol/ol/source/kmlsource.js create mode 100644 mapbox-gl/ol/ol/source/mapquestsource.js create mode 100644 mapbox-gl/ol/ol/source/osmsource.js create mode 100644 mapbox-gl/ol/ol/source/osmxmlsource.js create mode 100644 mapbox-gl/ol/ol/source/servervectorsource.js create mode 100644 mapbox-gl/ol/ol/source/source.js create mode 100644 mapbox-gl/ol/ol/source/stamensource.js create mode 100644 mapbox-gl/ol/ol/source/staticvectorsource.js create mode 100644 mapbox-gl/ol/ol/source/tilearcgisrestsource.js create mode 100644 mapbox-gl/ol/ol/source/tiledebugsource.js create mode 100644 mapbox-gl/ol/ol/source/tileimagesource.js create mode 100644 mapbox-gl/ol/ol/source/tilejsonsource.js create mode 100644 mapbox-gl/ol/ol/source/tilesource.js create mode 100644 mapbox-gl/ol/ol/source/tileutfgridsource.js create mode 100644 mapbox-gl/ol/ol/source/tilevectorsource.js create mode 100644 mapbox-gl/ol/ol/source/tilewmssource.js create mode 100644 mapbox-gl/ol/ol/source/topojsonsource.js create mode 100644 mapbox-gl/ol/ol/source/vectorsource.js create mode 100644 mapbox-gl/ol/ol/source/wmssource.js create mode 100644 mapbox-gl/ol/ol/source/wmssource.jsdoc create mode 100644 mapbox-gl/ol/ol/source/wmtssource.js create mode 100644 mapbox-gl/ol/ol/source/xyzsource.js create mode 100644 mapbox-gl/ol/ol/source/zoomifysource.js create mode 100644 mapbox-gl/ol/ol/sphere/normal.js create mode 100644 mapbox-gl/ol/ol/sphere/sphere.js create mode 100644 mapbox-gl/ol/ol/sphere/wgs84sphere.js create mode 100644 mapbox-gl/ol/ol/structs/checksum.js create mode 100644 mapbox-gl/ol/ol/structs/lrucache.js create mode 100644 mapbox-gl/ol/ol/structs/priorityqueue.js create mode 100644 mapbox-gl/ol/ol/structs/rbush.js create mode 100644 mapbox-gl/ol/ol/style.jsdoc create mode 100644 mapbox-gl/ol/ol/style/atlasmanager.js create mode 100644 mapbox-gl/ol/ol/style/circlestyle.js create mode 100644 mapbox-gl/ol/ol/style/fillstyle.js create mode 100644 mapbox-gl/ol/ol/style/iconstyle.js create mode 100644 mapbox-gl/ol/ol/style/imagestyle.js create mode 100644 mapbox-gl/ol/ol/style/regularshapestyle.js create mode 100644 mapbox-gl/ol/ol/style/strokestyle.js create mode 100644 mapbox-gl/ol/ol/style/style.js create mode 100644 mapbox-gl/ol/ol/style/textstyle.js create mode 100644 mapbox-gl/ol/ol/tile.js create mode 100644 mapbox-gl/ol/ol/tilecache.js create mode 100644 mapbox-gl/ol/ol/tilecoord.js create mode 100644 mapbox-gl/ol/ol/tilegrid.jsdoc create mode 100644 mapbox-gl/ol/ol/tilegrid/tilegrid.js create mode 100644 mapbox-gl/ol/ol/tilegrid/wmtstilegrid.js create mode 100644 mapbox-gl/ol/ol/tilegrid/xyztilegrid.js create mode 100644 mapbox-gl/ol/ol/tilegrid/zoomifytilegrid.js create mode 100644 mapbox-gl/ol/ol/tileloadfunction.js create mode 100644 mapbox-gl/ol/ol/tilequeue.js create mode 100644 mapbox-gl/ol/ol/tilerange.js create mode 100644 mapbox-gl/ol/ol/tileurlfunction.js create mode 100644 mapbox-gl/ol/ol/transformfunction.js create mode 100644 mapbox-gl/ol/ol/vec/mat4.js create mode 100644 mapbox-gl/ol/ol/view.js create mode 100644 mapbox-gl/ol/ol/webgl/buffer.js create mode 100644 mapbox-gl/ol/ol/webgl/context.js create mode 100644 mapbox-gl/ol/ol/webgl/shader.js create mode 100644 mapbox-gl/ol/ol/webgl/shader.mustache create mode 100644 mapbox-gl/ol/ol/webgl/webgl.js create mode 100644 mapbox-gl/ol/ol/xml.js create mode 100644 mapbox-gl/resources/bootstrap/css/bootstrap-responsive.css create mode 100644 mapbox-gl/resources/bootstrap/css/bootstrap-responsive.min.css create mode 100644 mapbox-gl/resources/bootstrap/css/bootstrap.css create mode 100644 mapbox-gl/resources/bootstrap/css/bootstrap.min.css create mode 100644 mapbox-gl/resources/bootstrap/img/glyphicons-halflings-white.png create mode 100644 mapbox-gl/resources/bootstrap/img/glyphicons-halflings.png create mode 100644 mapbox-gl/resources/bootstrap/js/bootstrap.js create mode 100644 mapbox-gl/resources/bootstrap/js/bootstrap.min.js create mode 100644 mapbox-gl/resources/display-frame-rate.js create mode 100644 mapbox-gl/resources/example-behaviour.js create mode 100644 mapbox-gl/resources/jquery.min.js create mode 100644 mapbox-gl/resources/layout.css create mode 100644 mapbox-gl/resources/logo-32x32-optimized.png create mode 100644 mapbox-gl/resources/logo-32x32.png create mode 100644 mapbox-gl/resources/logo.png create mode 100644 mapbox-gl/resources/textured_paper.jpeg diff --git a/mapbox-gl/build/ol-debug.js b/mapbox-gl/build/ol-debug.js new file mode 100644 index 0000000000..81c4d94ad7 --- /dev/null +++ b/mapbox-gl/build/ol-debug.js @@ -0,0 +1,128442 @@ +// OpenLayers 3. See http://openlayers.org/ +// License: https://raw.githubusercontent.com/openlayers/ol3/master/LICENSE.md +// Version: v3.3.0-49-g7512e78 + +(function (root, factory) { + if (typeof define === "function" && define.amd) { + define([], factory); + } else if (typeof exports === "object") { + module.exports = factory(); + } else { + root.ol = factory(); + } +}(this, function () { + var OPENLAYERS = {}; + var goog = this.goog = {}; +this.CLOSURE_NO_DEPS = true; +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bootstrap for the Google JS Library (Closure). + * + * In uncompiled mode base.js will write out Closure's deps file, unless the + * global CLOSURE_NO_DEPS is set to true. This allows projects to + * include their own deps file(s) from different locations. + * + * @author arv@google.com (Erik Arvidsson) + * + * @provideGoog + */ + + +/** + * @define {boolean} Overridden to true by the compiler when --closure_pass + * or --mark_as_compiled is specified. + */ +var COMPILED = false; + + +/** + * Base namespace for the Closure library. Checks to see goog is already + * defined in the current scope before assigning to prevent clobbering if + * base.js is loaded more than once. + * + * @const + */ +var goog = goog || {}; + + +/** + * Reference to the global context. In most cases this will be 'window'. + */ +goog.global = this; + + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before + * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, + * {@code goog.define} will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + *
+ *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + +/** + * Returns true if the specified value is not undefined. + * WARNING: Do not use this to test if an object has a property. Use the in + * operator instead. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + + +/** + * Builds an object structure for the provided namespace path, ensuring that + * names that already exist are not overwritten. For example: + * "a.b.c" -> a = {};a.b={};a.b.c={}; + * Used by goog.provide and goog.exportSymbol. + * @param {string} name name of the object that this file defines. + * @param {*=} opt_object the object to expose at the end of the path. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is |goog.global|. + * @private + */ +goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { + var parts = name.split('.'); + var cur = opt_objectToExportTo || goog.global; + + // Internet Explorer exhibits strange behavior when throwing errors from + // methods externed in this manner. See the testExportSymbolExceptions in + // base_test.html for an example. + if (!(parts[0] in cur) && cur.execScript) { + cur.execScript('var ' + parts[0]); + } + + // Certain browsers cannot parse code in the form for((a in b); c;); + // This pattern is produced by the JSCompiler when it collapses the + // statement above into the conditional loop below. To prevent this from + // happening, use a for-loop and reserve the init logic as below. + + // Parentheses added to eliminate strict JS warning in Firefox. + for (var part; parts.length && (part = parts.shift());) { + if (!parts.length && goog.isDef(opt_object)) { + // last part and we have an object; use it + cur[part] = opt_object; + } else if (cur[part]) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } +}; + + +/** + * Defines a named value. In uncompiled mode, the value is retreived from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler + * options or the value set in the CLOSURE_DEFINES object. + * + * @param {string} name The distinguished name to provide. + * @param {string|number|boolean} defaultValue + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { + value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; + } else if (goog.global.CLOSURE_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_DEFINES, name)) { + value = goog.global.CLOSURE_DEFINES[name]; + } + } + goog.exportPath_(name, value); +}; + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production js_binary can be easily stripped + * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most + * toString() methods should be declared inside an "if (goog.DEBUG)" conditional + * because they are generally used for debugging purposes and it is difficult + * for the JSCompiler to statically determine whether they are used. + */ +goog.DEBUG = true; + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as JSCompiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. + */ +goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the JSCompiler. + */ +goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements + * to discern dependencies, build dependency files (see deps.js), etc. + * + * @see goog.require + * @see goog.module + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + */ +goog.provide = function(name) { + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {Object=} opt_obj The object to embed in the namespace. + * @private + */ +goog.constructNamespace_ = function(name, opt_obj) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, opt_obj); +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lienent, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + */ +goog.module = function(name) { + if (!goog.isString(name) || + !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw Error('Invalid module identifier'); + } + if (!goog.isInModuleLoader_()) { + throw Error('Module ' + name + ' has been loaded incorrectly.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (goog.isProvided_(name)) { + // goog.require only return a value with-in goog.module files. + return name in goog.loadedModules_ ? + goog.loadedModules_[name] : + goog.getObjectByName(name); + } else { + return null; + } + } +}; + + +/** + * @private {?{ + * moduleName: (string|undefined), + * declareTestMethods: boolean + * }} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.moduleLoaderState_ != null; +}; + + +/** + * Indicate that a module's exports that are known test methods should + * be copied to the global object. This makes the test methods visible to + * test runners that inspect the global object. + * + * TODO(johnlenz): Make the test framework aware of goog.module so + * that this isn't necessary. Alternately combine this with goog.setTestOnly + * to minimize boiler plate. + * @suppress {missingProvide} + */ +goog.module.declareTestMethods = function() { + if (!goog.isInModuleLoader_()) { + throw new Error('goog.module.declareTestMethods must be called from ' + + 'within a goog.module'); + } + goog.moduleLoaderState_.declareTestMethods = true; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw new Error('goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error('goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw Error('Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +if (!COMPILED) { + + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} + + +/** + * Returns an object based on its fully qualified external name. The object + * is not found if null or undefined. If you are using a compilation pass that + * renames property names beware that using this function will not find renamed + * properties. + * + * @param {string} name The fully qualified name. + * @param {Object=} opt_obj The object within which to look; default is + * |goog.global|. + * @return {?} The value (object or primitive) or, if not found, null. + */ +goog.getObjectByName = function(name, opt_obj) { + var parts = name.split('.'); + var cur = opt_obj || goog.global; + for (var part; part = parts.shift(); ) { + if (goog.isDefAndNotNull(cur[part])) { + cur = cur[part]; + } else { + return null; + } + } + return cur; +}; + + +/** + * Globalizes a whole namespace, such as goog or goog.lang. + * + * @param {!Object} obj The namespace to globalize. + * @param {Object=} opt_global The object to add the properties to. + * @deprecated Properties may be explicitly exported to the global scope, but + * this should no longer be done in bulk. + */ +goog.globalize = function(obj, opt_global) { + var global = opt_global || goog.global; + for (var x in obj) { + global[x] = obj[x]; + } +}; + + +/** + * Adds a dependency from a file to the files it requires. + * @param {string} relPath The path to the js file. + * @param {!Array} provides An array of strings with + * the names of the objects this file provides. + * @param {!Array} requires An array of strings with + * the names of the objects this file requires. + * @param {boolean=} opt_isModule Whether this dependency must be loaded as + * a module as declared by goog.module. + */ +goog.addDependency = function(relPath, provides, requires, opt_isModule) { + if (goog.DEPENDENCIES_ENABLED) { + var provide, require; + var path = relPath.replace(/\\/g, '/'); + var deps = goog.dependencies_; + for (var i = 0; provide = provides[i]; i++) { + deps.nameToPath[provide] = path; + deps.pathIsModule[path] = !!opt_isModule; + } + for (var j = 0; require = requires[j]; j++) { + if (!(path in deps.requires)) { + deps.requires[path] = {}; + } + deps.requires[path][require] = true; + } + } +}; + + + + +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. +// +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. +// User-defined namespaces may need their own deps file. See http://go/js_deps, +// http://go/genjsdeps, or, externally, DepsWriter. +// https://developers.google.com/closure/library/docs/depswriter +// +// Because of legacy clients, the DOM loader can't be easily removed from +// base.js. Work is being done to make it disableable or replaceable for +// different environments (DOM-less JavaScript interpreters like Rhino or V8, +// for example). See bootstrap/ for more information. + + +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * Implements a system for the dynamic resolution of dependencies that works in + * parallel with the BUILD system. Note that all calls to goog.require will be + * stripped by the JSCompiler when the --closure_pass option is used. + * @see goog.provide + * @param {string} name Namespace to include (as was given in goog.provide()) in + * the form "goog.package.part". + * @return {?} If called within a goog.module file, the associated namespace or + * module otherwise null. + */ +goog.require = function(name) { + + // If the object already exists we do not need do do anything. + if (!COMPILED) { + if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { + goog.maybeProcessDeferredDep_(name); + } + + if (goog.isProvided_(name)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(name); + } else { + return null; + } + } + + if (goog.ENABLE_DEBUG_LOADER) { + var path = goog.getPathFromDeps_(name); + if (path) { + goog.included_[path] = true; + goog.writeScripts_(); + return null; + } + } + + var errorMessage = 'goog.require could not find: ' + name; + goog.logToConsole_(errorMessage); + + throw Error(errorMessage); + } +}; + + +/** + * Path for included scripts. + * @type {string} + */ +goog.basePath = ''; + + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to write out Closure's deps file. By default, the deps are written. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The function is passed the script source, which is a relative URI. It should + * return true if the script was imported, false otherwise. + * @type {(function(string): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + */ +goog.nullFunction = function() {}; + + +/** + * The identity function. Returns its first argument. + * + * @param {*=} opt_returnValue The single value that will be returned. + * @param {...*} var_args Optional trailing arguments. These are ignored. + * @return {?} The first argument. We can't know the type -- just pass it along + * without type. + * @deprecated Use goog.functions.identity instead. + */ +goog.identityFunction = function(opt_returnValue, var_args) { + return opt_returnValue; +}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * Note: This does not take the name of the function to override as an argument + * because that would make it more difficult to obfuscate our JavaScript code. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + */ +goog.abstractMethod = function() { + throw Error('unimplemented abstract method'); +}; + + +/** + * Adds a {@code getInstance} static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + */ +goog.addSingletonGetter = function(ctor) { + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + return ctor.instance_ = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the {@code goog.testing.singleton} module. The compiler + * removes this variable if unused. + * @type {!Array} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using {@code eval} when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or + * similiar. However in some environments the use of {@code eval} is banned + * so we provide an alternative. + */ +goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * the module identifier to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if goog.dependencies_ is available. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +if (goog.DEPENDENCIES_ENABLED) { + /** + * Object used to keep track of urls that have already been added. This record + * allows the prevention of circular dependencies. + * @private {!Object} + */ + goog.included_ = {}; + + + /** + * This object is used to keep track of dependencies and other data that is + * used for loading scripts. + * @private + * @type {{ + * pathIsModule: !Object, + * nameToPath: !Object, + * requires: !Object>, + * visited: !Object, + * written: !Object, + * deferred: !Object + * }} + */ + goog.dependencies_ = { + pathIsModule: {}, // 1 to 1 + + nameToPath: {}, // 1 to 1 + + requires: {}, // 1 to many + + // Used when resolving dependencies to prevent us from visiting file twice. + visited: {}, + + written: {}, // Used to keep track of script files we have written. + + deferred: {} // Used to track deferred module evaluations in old IEs + }; + + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + var doc = goog.global.document; + return typeof doc != 'undefined' && + 'write' in doc; // XULDocument misses write. + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.global.CLOSURE_BASE_PATH) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { + return; + } + var doc = goog.global.document; + var scripts = doc.getElementsByTagName('script'); + // Search backwards since the current script is in almost all cases the one + // that has base.js. + for (var i = scripts.length - 1; i >= 0; --i) { + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; + var qmark = src.lastIndexOf('?'); + var l = qmark == -1 ? src.length : qmark; + if (src.substr(l - 7, 7) == 'base.js') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; + + + /** + * Imports a script if, and only if, that script hasn't already been imported. + * (Must be called at execution time) + * @param {string} src Script source. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @private + */ + goog.importScript_ = function(src, opt_sourceText) { + var importScript = goog.global.CLOSURE_IMPORT_SCRIPT || + goog.writeScriptTag_; + if (importScript(src, opt_sourceText)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** @const @private {boolean} */ + goog.IS_OLD_IE_ = goog.global.document && + goog.global.document.all && !goog.global.atob; + + + /** + * Given a URL initiate retrieval and execution of the module. + * @param {string} src Script source URL. + * @private + */ + goog.importModule_ = function(src) { + // In an attempt to keep browsers from timing out loading scripts using + // synchronous XHRs, put each load in its own script block. + var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");'; + + if (goog.importScript_('', bootstrap)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** @private {!Array} */ + goog.queuedModules_ = []; + + + /** + * Return an appropriate module text. Suitable to insert into + * a script tag (that is unescaped). + * @param {string} srcUrl + * @param {string} scriptText + * @return {string} + * @private + */ + goog.wrapModule_ = function(srcUrl, scriptText) { + if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { + return '' + + 'goog.loadModule(function(exports) {' + + '"use strict";' + + scriptText + + '\n' + // terminate any trailing single line comment. + ';return exports' + + '});' + + '\n//# sourceURL=' + srcUrl + '\n'; + } else { + return '' + + 'goog.loadModule(' + + goog.global.JSON.stringify( + scriptText + '\n//# sourceURL=' + srcUrl + '\n') + + ');'; + } + }; + + // On IE9 and ealier, it is necessary to handle + // deferred module loads. In later browsers, the + // code to be evaluated is simply inserted as a script + // block in the correct order. To eval deferred + // code at the right time, we piggy back on goog.require to call + // goog.maybeProcessDeferredDep_. + // + // The goog.requires are used both to bootstrap + // the loading process (when no deps are available) and + // declare that they should be available. + // + // Here we eval the sources, if all the deps are available + // either already eval'd or goog.require'd. This will + // be the case when all the dependencies have already + // been loaded, and the dependent module is loaded. + // + // But this alone isn't sufficient because it is also + // necessary to handle the case where there is no root + // that is not deferred. For that there we register for an event + // and trigger goog.loadQueuedModules_ handle any remaining deferred + // evaluations. + + /** + * Handle any remaining deferred goog.module evals. + * @private + */ + goog.loadQueuedModules_ = function() { + var count = goog.queuedModules_.length; + if (count > 0) { + var queue = goog.queuedModules_; + goog.queuedModules_ = []; + for (var i = 0; i < count; i++) { + var path = queue[i]; + goog.maybeProcessDeferredPath_(path); + } + } + }; + + + /** + * Eval the named module if its dependencies are + * available. + * @param {string} name The module to load. + * @private + */ + goog.maybeProcessDeferredDep_ = function(name) { + if (goog.isDeferredModule_(name) && + goog.allDepsAreAvailable_(name)) { + var path = goog.getPathFromDeps_(name); + goog.maybeProcessDeferredPath_(goog.basePath + path); + } + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose evaluation has been deferred. + * @private + */ + goog.isDeferredModule_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && goog.dependencies_.pathIsModule[path]) { + var abspath = goog.basePath + path; + return (abspath) in goog.dependencies_.deferred; + } + return false; + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose declared dependencies have all been loaded + * (eval'd or a deferred module load) + * @private + */ + goog.allDepsAreAvailable_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && (path in goog.dependencies_.requires)) { + for (var requireName in goog.dependencies_.requires[path]) { + if (!goog.isProvided_(requireName) && + !goog.isDeferredModule_(requireName)) { + return false; + } + } + } + return true; + }; + + + /** + * @param {string} abspath + * @private + */ + goog.maybeProcessDeferredPath_ = function(abspath) { + if (abspath in goog.dependencies_.deferred) { + var src = goog.dependencies_.deferred[abspath]; + delete goog.dependencies_.deferred[abspath]; + goog.globalEval(src); + } + }; + + + /** + * @param {function(?):?|string} moduleDef The module definition. + */ + goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call {@code goog.module}, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: undefined, declareTestMethods: false}; + var exports; + if (goog.isFunction(moduleDef)) { + exports = moduleDef.call(goog.global, {}); + } else if (goog.isString(moduleDef)) { + exports = goog.loadModuleFromSource_.call(goog.global, moduleDef); + } else { + throw Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (!goog.isString(moduleName) || !moduleName) { + throw Error('Invalid module name \"' + moduleName + '\"'); + } + + // Don't seal legacy namespaces as they may be uses as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + goog.constructNamespace_(moduleName, exports); + } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) { + Object.seal(exports); + } + + goog.loadedModules_[moduleName] = exports; + if (goog.moduleLoaderState_.declareTestMethods) { + for (var entry in exports) { + if (entry.indexOf('test', 0) === 0 || + entry == 'tearDown' || + entry == 'setUp' || + entry == 'setUpPage' || + entry == 'tearDownPage') { + goog.global[entry] = exports[entry]; + } + } + } + } finally { + goog.moduleLoaderState_ = previousState; + } + }; + + + /** + * @param {string} source + * @return {!Object} + * @private + */ + goog.loadModuleFromSource_ = function(source) { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + var exports = {}; + eval(arguments[0]); + return exports; + }; + + + /** + * The default implementation of the import function. Writes a script tag to + * import the script. + * + * @param {string} src The script url. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @return {boolean} True if the script was imported, false otherwise. + * @private + */ + goog.writeScriptTag_ = function(src, opt_sourceText) { + if (goog.inHtmlDocument_()) { + var doc = goog.global.document; + + // If the user tries to require a new symbol after document load, + // something has gone terribly wrong. Doing a document.write would + // wipe out the page. + if (doc.readyState == 'complete') { + // Certain test frameworks load base.js multiple times, which tries + // to write deps.js each time. If that happens, just fail silently. + // These frameworks wipe the page between each load of base.js, so this + // is OK. + var isDeps = /\bdeps.js$/.test(src); + if (isDeps) { + return false; + } else { + throw Error('Cannot write "' + src + '" after document load'); + } + } + + var isOldIE = goog.IS_OLD_IE_; + + if (opt_sourceText === undefined) { + if (!isOldIE) { + doc.write( + ' + + + + + + + + + +

Closure - All JsUnit Tests

+ +
+ + + + + diff --git a/mapbox-gl/closure-library/alltests.js b/mapbox-gl/closure-library/alltests.js new file mode 100644 index 0000000000..f0ab11a435 --- /dev/null +++ b/mapbox-gl/closure-library/alltests.js @@ -0,0 +1,16 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var _allTests = [ +"closure/goog/a11y/aria/announcer_test.html","closure/goog/a11y/aria/aria_test.html","closure/goog/array/array_test.html","closure/goog/asserts/asserts_test.html","closure/goog/async/animationdelay_test.html","closure/goog/async/conditionaldelay_test.html","closure/goog/async/delay_test.html","closure/goog/async/nexttick_test.html","closure/goog/async/throttle_test.html","closure/goog/base_module_test.html","closure/goog/base_test.html","closure/goog/color/alpha_test.html","closure/goog/color/color_test.html","closure/goog/crypt/aes_test.html","closure/goog/crypt/arc4_test.html","closure/goog/crypt/base64_test.html","closure/goog/crypt/basen_test.html","closure/goog/crypt/blobhasher_test.html","closure/goog/crypt/cbc_test.html","closure/goog/crypt/crypt_test.html","closure/goog/crypt/hash32_test.html","closure/goog/crypt/hmac_test.html","closure/goog/crypt/md5_test.html","closure/goog/crypt/pbkdf2_test.html","closure/goog/crypt/sha1_test.html","closure/goog/crypt/sha224_test.html","closure/goog/crypt/sha256_test.html","closure/goog/crypt/sha2_64bit_test.html","closure/goog/cssom/cssom_test.html","closure/goog/cssom/iframe/style_test.html","closure/goog/datasource/datasource_test.html","closure/goog/datasource/expr_test.html","closure/goog/datasource/fastdatanode_test.html","closure/goog/datasource/jsxmlhttpdatasource_test.html","closure/goog/date/daterange_test.html","closure/goog/date/date_test.html","closure/goog/date/duration_test.html","closure/goog/date/relative_test.html","closure/goog/date/relativewithplurals_test.html","closure/goog/date/utcdatetime_test.html","closure/goog/db/db_test.html","closure/goog/db/old_db_test.html","closure/goog/debug/console_test.html","closure/goog/debug/debug_test.html","closure/goog/debug/debugwindow_test.html","closure/goog/debug/devcss/devcss_test.html","closure/goog/debug/enhanceerror_test.html","closure/goog/debug/entrypointregistry_test.html","closure/goog/debug/errorhandler_async_test.html","closure/goog/debug/errorhandler_test.html","closure/goog/debug/errorreporter_test.html","closure/goog/debug/error_test.html","closure/goog/debug/formatter_test.html","closure/goog/debug/fpsdisplay_test.html","closure/goog/debug/logbuffer_test.html","closure/goog/debug/logger_test.html","closure/goog/debug/logrecordserializer_test.html","closure/goog/debug/tracer_test.html","closure/goog/defineclass_test.html","closure/goog/disposable/disposable_test.html","closure/goog/dom/abstractrange_test.html","closure/goog/dom/annotate_test.html","closure/goog/dom/browserrange/browserrange_test.html","closure/goog/dom/bufferedviewportsizemonitor_test.html","closure/goog/dom/classes_quirks_test.html","closure/goog/dom/classes_test.html","closure/goog/dom/classlist_test.html","closure/goog/dom/controlrange_test.html","closure/goog/dom/dataset_test.html","closure/goog/dom/dom_quirks_test.html","closure/goog/dom/dom_test.html","closure/goog/dom/fontsizemonitor_test.html","closure/goog/dom/forms_test.html","closure/goog/dom/iframe_test.html","closure/goog/dom/iter_test.html","closure/goog/dom/multirange_test.html","closure/goog/dom/nodeiterator_test.html","closure/goog/dom/nodeoffset_test.html","closure/goog/dom/pattern/matcher_test.html","closure/goog/dom/pattern/pattern_test.html","closure/goog/dom/range_test.html","closure/goog/dom/safe_test.html","closure/goog/dom/savedcaretrange_test.html","closure/goog/dom/savedrange_test.html","closure/goog/dom/selection_test.html","closure/goog/dom/tagiterator_test.html","closure/goog/dom/tagname_test.html","closure/goog/dom/textrangeiterator_test.html","closure/goog/dom/textrange_test.html","closure/goog/dom/vendor_test.html","closure/goog/dom/viewportsizemonitor_test.html","closure/goog/dom/xml_test.html","closure/goog/editor/clicktoeditwrapper_test.html","closure/goog/editor/contenteditablefield_test.html","closure/goog/editor/field_test.html","closure/goog/editor/focus_test.html","closure/goog/editor/icontent_test.html","closure/goog/editor/link_test.html","closure/goog/editor/node_test.html","closure/goog/editor/plugins/abstractbubbleplugin_test.html","closure/goog/editor/plugins/abstractdialogplugin_test.html","closure/goog/editor/plugins/abstracttabhandler_test.html","closure/goog/editor/plugins/basictextformatter_test.html","closure/goog/editor/plugins/blockquote_test.html","closure/goog/editor/plugins/emoticons_test.html","closure/goog/editor/plugins/enterhandler_test.html","closure/goog/editor/plugins/firststrong_test.html","closure/goog/editor/plugins/headerformatter_test.html","closure/goog/editor/plugins/linkbubble_test.html","closure/goog/editor/plugins/linkdialogplugin_test.html","closure/goog/editor/plugins/linkshortcutplugin_test.html","closure/goog/editor/plugins/listtabhandler_test.html","closure/goog/editor/plugins/loremipsum_test.html","closure/goog/editor/plugins/removeformatting_test.html","closure/goog/editor/plugins/spacestabhandler_test.html","closure/goog/editor/plugins/tableeditor_test.html","closure/goog/editor/plugins/tagonenterhandler_test.html","closure/goog/editor/plugins/undoredomanager_test.html","closure/goog/editor/plugins/undoredostate_test.html","closure/goog/editor/plugins/undoredo_test.html","closure/goog/editor/plugin_test.html","closure/goog/editor/range_test.html","closure/goog/editor/seamlessfield_quirks_test.html","closure/goog/editor/seamlessfield_test.html","closure/goog/editor/style_test.html","closure/goog/editor/table_test.html","closure/goog/events/actioneventwrapper_test.html","closure/goog/events/actionhandler_test.html","closure/goog/events/browserevent_test.html","closure/goog/events/eventhandler_test.html","closure/goog/events/events_test.html","closure/goog/events/eventtarget_test.html","closure/goog/events/eventtarget_via_googevents_test.html","closure/goog/events/eventtarget_via_w3cinterface_test.html","closure/goog/events/event_test.html","closure/goog/events/filedrophandler_test.html","closure/goog/events/imehandler_test.html","closure/goog/events/inputhandler_test.html","closure/goog/events/keycodes_test.html","closure/goog/events/keyhandler_test.html","closure/goog/events/listenable_test.html","closure/goog/events/listenermap_test.html","closure/goog/events/mousewheelhandler_test.html","closure/goog/events/onlinelistener_test.html","closure/goog/events/pastehandler_test.html","closure/goog/events/wheelhandler_test.html","closure/goog/format/emailaddress_test.html","closure/goog/format/format_test.html","closure/goog/format/htmlprettyprinter_test.html","closure/goog/format/internationalizedemailaddress_test.html","closure/goog/format/jsonprettyprinter_test.html","closure/goog/fs/fs_test.html","closure/goog/functions/functions_test.html","closure/goog/fx/abstractdragdrop_test.html","closure/goog/fx/anim/anim_test.html","closure/goog/fx/animationqueue_test.html","closure/goog/fx/animation_test.html","closure/goog/fx/css3/transition_test.html","closure/goog/fx/cssspriteanimation_test.html","closure/goog/fx/dragdropgroup_test.html","closure/goog/fx/dragger_test.html","closure/goog/fx/draglistgroup_test.html","closure/goog/fx/dragscrollsupport_test.html","closure/goog/fx/fx_test.html","closure/goog/graphics/affinetransform_test.html","closure/goog/graphics/ext/coordinates_test.html","closure/goog/graphics/ext/element_test.html","closure/goog/graphics/ext/path_test.html","closure/goog/graphics/paths_test.html","closure/goog/graphics/path_test.html","closure/goog/graphics/solidfill_test.html","closure/goog/graphics/svggraphics_test.html","closure/goog/history/history_test.html","closure/goog/history/html5history_test.html","closure/goog/html/flash_test.html","closure/goog/html/legacyconversions_test.html","closure/goog/html/safehtml_test.html","closure/goog/html/safescript_test.html","closure/goog/html/safestylesheet_test.html","closure/goog/html/safestyle_test.html","closure/goog/html/safeurl_test.html","closure/goog/html/silverlight_test.html","closure/goog/html/trustedresourceurl_test.html","closure/goog/html/uncheckedconversions_test.html","closure/goog/html/utils_test.html","closure/goog/i18n/bidiformatter_test.html","closure/goog/i18n/bidi_test.html","closure/goog/i18n/charlistdecompressor_test.html","closure/goog/i18n/collation_test.html","closure/goog/i18n/currency_test.html","closure/goog/i18n/datetimeformat_test.html","closure/goog/i18n/datetimeparse_test.html","closure/goog/i18n/graphemebreak_test.html","closure/goog/i18n/messageformat_test.html","closure/goog/i18n/mime_test.html","closure/goog/i18n/numberformat_test.html","closure/goog/i18n/pluralrules_test.html","closure/goog/i18n/timezone_test.html","closure/goog/i18n/uchar/localnamefetcher_test.html","closure/goog/i18n/uchar/remotenamefetcher_test.html","closure/goog/i18n/uchar_test.html","closure/goog/iter/iter_test.html","closure/goog/json/hybridjsonprocessor_test.html","closure/goog/json/hybrid_test.html","closure/goog/json/json_test.html","closure/goog/json/processor_test.html","closure/goog/labs/events/nondisposableeventtarget_test.html","closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.html","closure/goog/labs/events/touch_test.html","closure/goog/labs/format/csv_test.html","closure/goog/labs/i18n/listformat_test.html","closure/goog/labs/mock/mock_test.html","closure/goog/labs/net/image_test.html","closure/goog/labs/net/webchannel/channelrequest_test.html","closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.html","closure/goog/labs/net/webchannel/webchannelbase_test.html","closure/goog/labs/net/webchannel/webchannelbasetransport_test.html","closure/goog/labs/net/webchannel/wirev8_test.html","closure/goog/labs/net/xhr_test.html","closure/goog/labs/object/object_test.html","closure/goog/labs/storage/boundedcollectablestorage_test.html","closure/goog/labs/structs/map_test.html","closure/goog/labs/structs/multimap_test.html","closure/goog/labs/style/pixeldensitymonitor_test.html","closure/goog/labs/testing/assertthat_test.html","closure/goog/labs/testing/decoratormatcher_test.html","closure/goog/labs/testing/dictionarymatcher_test.html","closure/goog/labs/testing/environment_test.html","closure/goog/labs/testing/logicmatcher_test.html","closure/goog/labs/testing/numbermatcher_test.html","closure/goog/labs/testing/objectmatcher_test.html","closure/goog/labs/testing/stringmatcher_test.html","closure/goog/labs/useragent/browser_test.html","closure/goog/labs/useragent/device_test.html","closure/goog/labs/useragent/engine_test.html","closure/goog/labs/useragent/platform_test.html","closure/goog/labs/useragent/util_test.html","closure/goog/locale/countrylanguagenames_test.html","closure/goog/locale/genericfontnames_test.html","closure/goog/locale/timezonedetection_test.html","closure/goog/locale/timezonelist_test.html","closure/goog/math/affinetransform_test.html","closure/goog/math/bezier_test.html","closure/goog/math/box_test.html","closure/goog/math/coordinate3_test.html","closure/goog/math/coordinate_test.html","closure/goog/math/exponentialbackoff_test.html","closure/goog/math/integer_test.html","closure/goog/math/interpolator/linear1_test.html","closure/goog/math/interpolator/pchip1_test.html","closure/goog/math/interpolator/spline1_test.html","closure/goog/math/line_test.html","closure/goog/math/long_test.html","closure/goog/math/math_test.html","closure/goog/math/matrix_test.html","closure/goog/math/paths_test.html","closure/goog/math/path_test.html","closure/goog/math/rangeset_test.html","closure/goog/math/range_test.html","closure/goog/math/rect_test.html","closure/goog/math/size_test.html","closure/goog/math/tdma_test.html","closure/goog/math/vec2_test.html","closure/goog/math/vec3_test.html","closure/goog/memoize/memoize_test.html","closure/goog/messaging/abstractchannel_test.html","closure/goog/messaging/bufferedchannel_test.html","closure/goog/messaging/deferredchannel_test.html","closure/goog/messaging/loggerclient_test.html","closure/goog/messaging/loggerserver_test.html","closure/goog/messaging/messaging_test.html","closure/goog/messaging/multichannel_test.html","closure/goog/messaging/portcaller_test.html","closure/goog/messaging/portchannel_test.html","closure/goog/messaging/portnetwork_test.html","closure/goog/messaging/portoperator_test.html","closure/goog/messaging/respondingchannel_test.html","closure/goog/module/moduleinfo_test.html","closure/goog/module/moduleloadcallback_test.html","closure/goog/module/moduleloader_test.html","closure/goog/module/modulemanager_test.html","closure/goog/net/browserchannel_test.html","closure/goog/net/bulkloader_test.html","closure/goog/net/channelrequest_test.html","closure/goog/net/cookies_test.html","closure/goog/net/corsxmlhttpfactory_test.html","closure/goog/net/crossdomainrpc_test.html","closure/goog/net/filedownloader_test.html","closure/goog/net/iframeio_different_base_test.html","closure/goog/net/iframeio_test.html","closure/goog/net/iframeloadmonitor_test.html","closure/goog/net/iframe_xhr_test.html","closure/goog/net/imageloader_test.html","closure/goog/net/ipaddress_test.html","closure/goog/net/jsloader_test.html","closure/goog/net/jsonp_test.html","closure/goog/net/multiiframeloadmonitor_test.html","closure/goog/net/networktester_test.html","closure/goog/net/websocket_test.html","closure/goog/net/xhrio_test.html","closure/goog/net/xhrmanager_test.html","closure/goog/net/xpc/crosspagechannel_test.html","closure/goog/net/xpc/iframepollingtransport_test.html","closure/goog/net/xpc/nativemessagingtransport_test.html","closure/goog/object/object_test.html","closure/goog/positioning/anchoredposition_test.html","closure/goog/positioning/anchoredviewportposition_test.html","closure/goog/positioning/clientposition_test.html","closure/goog/positioning/menuanchoredposition_test.html","closure/goog/positioning/positioning_test.html","closure/goog/positioning/viewportclientposition_test.html","closure/goog/promise/promise_test.html","closure/goog/proto2/descriptor_test.html","closure/goog/proto2/fielddescriptor_test.html","closure/goog/proto2/message_test.html","closure/goog/proto2/objectserializer_test.html","closure/goog/proto2/pbliteserializer_test.html","closure/goog/proto2/proto_test.html","closure/goog/proto2/textformatserializer_test.html","closure/goog/proto/serializer_test.html","closure/goog/pubsub/pubsub_test.html","closure/goog/pubsub/typedpubsub_test.html","closure/goog/result/chain_test.html","closure/goog/result/combine_test.html","closure/goog/result/deferredadaptor_test.html","closure/goog/result/resultutil_test.html","closure/goog/result/simpleresult_test.html","closure/goog/result/transform_test.html","closure/goog/result/wait_test.html","closure/goog/soy/data_test.html","closure/goog/soy/renderer_test.html","closure/goog/soy/soy_test.html","closure/goog/spell/spellcheck_test.html","closure/goog/stats/basicstat_test.html","closure/goog/storage/collectablestorage_test.html","closure/goog/storage/encryptedstorage_test.html","closure/goog/storage/expiringstorage_test.html","closure/goog/storage/mechanism/errorhandlingmechanism_test.html","closure/goog/storage/mechanism/html5localstorage_test.html","closure/goog/storage/mechanism/html5sessionstorage_test.html","closure/goog/storage/mechanism/html5webstorage_test.html","closure/goog/storage/mechanism/ieuserdata_test.html","closure/goog/storage/mechanism/mechanismfactory_test.html","closure/goog/storage/mechanism/prefixedmechanism_test.html","closure/goog/storage/richstorage_test.html","closure/goog/storage/storage_test.html","closure/goog/string/const_test.html","closure/goog/string/linkify_test.html","closure/goog/string/newlines_test.html","closure/goog/string/path_test.html","closure/goog/string/stringbuffer_test.html","closure/goog/string/stringformat_test.html","closure/goog/string/string_test.html","closure/goog/structs/avltree_test.html","closure/goog/structs/circularbuffer_test.html","closure/goog/structs/collection_test.html","closure/goog/structs/heap_test.html","closure/goog/structs/inversionmap_test.html","closure/goog/structs/linkedmap_test.html","closure/goog/structs/map_test.html","closure/goog/structs/pool_test.html","closure/goog/structs/prioritypool_test.html","closure/goog/structs/priorityqueue_test.html","closure/goog/structs/quadtree_test.html","closure/goog/structs/queue_test.html","closure/goog/structs/set_test.html","closure/goog/structs/stringset_test.html","closure/goog/structs/structs_test.html","closure/goog/structs/treenode_test.html","closure/goog/structs/trie_test.html","closure/goog/style/bidi_test.html","closure/goog/style/cursor_test.html","closure/goog/style/style_document_scroll_test.html","closure/goog/style/style_quirks_test.html","closure/goog/style/style_test.html","closure/goog/style/style_webkit_scrollbars_test.html","closure/goog/style/transition_test.html","closure/goog/testing/asserts_test.html","closure/goog/testing/async/mockcontrol_test.html","closure/goog/testing/asynctestcase_async_test.html","closure/goog/testing/asynctestcase_noasync_test.html","closure/goog/testing/asynctestcase_test.html","closure/goog/testing/continuationtestcase_test.html","closure/goog/testing/deferredtestcase_test.html","closure/goog/testing/dom_test.html","closure/goog/testing/editor/dom_test.html","closure/goog/testing/editor/testhelper_test.html","closure/goog/testing/events/eventobserver_test.html","closure/goog/testing/events/events_test.html","closure/goog/testing/events/matchers_test.html","closure/goog/testing/events/onlinehandler_test.html","closure/goog/testing/expectedfailures_test.html","closure/goog/testing/fs/blob_test.html","closure/goog/testing/fs/directoryentry_test.html","closure/goog/testing/fs/entry_test.html","closure/goog/testing/fs/fileentry_test.html","closure/goog/testing/fs/filereader_test.html","closure/goog/testing/fs/filewriter_test.html","closure/goog/testing/fs/fs_test.html","closure/goog/testing/fs/integration_test.html","closure/goog/testing/functionmock_test.html","closure/goog/testing/i18n/asserts_test.html","closure/goog/testing/loosemock_test.html","closure/goog/testing/mockclassfactory_test.html","closure/goog/testing/mockclock_test.html","closure/goog/testing/mockcontrol_test.html","closure/goog/testing/mockmatchers_test.html","closure/goog/testing/mockrandom_test.html","closure/goog/testing/mockrange_test.html","closure/goog/testing/mockstorage_test.html","closure/goog/testing/mock_test.html","closure/goog/testing/mockuseragent_test.html","closure/goog/testing/net/xhrio_test.html","closure/goog/testing/performancetimer_test.html","closure/goog/testing/propertyreplacer_test.html","closure/goog/testing/proto2/proto2_test.html","closure/goog/testing/pseudorandom_test.html","closure/goog/testing/recordfunction_test.html","closure/goog/testing/shardingtestcase_test.html","closure/goog/testing/singleton_test.html","closure/goog/testing/stacktrace_test.html","closure/goog/testing/strictmock_test.html","closure/goog/testing/style/layoutasserts_test.html","closure/goog/testing/style/style_test.html","closure/goog/testing/ui/rendererasserts_test.html","closure/goog/testing/ui/style_test.html","closure/goog/timer/timer_test.html","closure/goog/tweak/entries_test.html","closure/goog/tweak/registry_test.html","closure/goog/tweak/tweakui_test.html","closure/goog/ui/ac/ac_test.html","closure/goog/ui/ac/arraymatcher_test.html","closure/goog/ui/ac/autocomplete_test.html","closure/goog/ui/ac/cachingmatcher_test.html","closure/goog/ui/ac/inputhandler_test.html","closure/goog/ui/ac/remotearraymatcher_test.html","closure/goog/ui/ac/renderer_test.html","closure/goog/ui/activitymonitor_test.html","closure/goog/ui/advancedtooltip_test.html","closure/goog/ui/animatedzippy_test.html","closure/goog/ui/bidiinput_test.html","closure/goog/ui/buttonrenderer_test.html","closure/goog/ui/button_test.html","closure/goog/ui/charcounter_test.html","closure/goog/ui/charpicker_test.html","closure/goog/ui/checkbox_test.html","closure/goog/ui/colorbutton_test.html","closure/goog/ui/colormenubuttonrenderer_test.html","closure/goog/ui/colorpalette_test.html","closure/goog/ui/combobox_test.html","closure/goog/ui/component_test.html","closure/goog/ui/containerrenderer_test.html","closure/goog/ui/containerscroller_test.html","closure/goog/ui/container_test.html","closure/goog/ui/controlrenderer_test.html","closure/goog/ui/control_test.html","closure/goog/ui/cookieeditor_test.html","closure/goog/ui/customcolorpalette_test.html","closure/goog/ui/datepicker_test.html","closure/goog/ui/decorate_test.html","closure/goog/ui/dialog_test.html","closure/goog/ui/dimensionpickerrenderer_test.html","closure/goog/ui/dimensionpicker_test.html","closure/goog/ui/drilldownrow_test.html","closure/goog/ui/editor/abstractdialog_test.html","closure/goog/ui/editor/bubble_test.html","closure/goog/ui/editor/linkdialog_test.html","closure/goog/ui/editor/toolbarfactory_test.html","closure/goog/ui/emoji/emojipicker_test.html","closure/goog/ui/emoji/popupemojipicker_test.html","closure/goog/ui/emoji/spriteinfo_test.html","closure/goog/ui/filteredmenu_test.html","closure/goog/ui/formpost_test.html","closure/goog/ui/hovercard_test.html","closure/goog/ui/hsvapalette_test.html","closure/goog/ui/hsvpalette_test.html","closure/goog/ui/idletimer_test.html","closure/goog/ui/iframemask_test.html","closure/goog/ui/inputdatepicker_test.html","closure/goog/ui/keyboardshortcuthandler_test.html","closure/goog/ui/labelinput_test.html","closure/goog/ui/media/flashobject_test.html","closure/goog/ui/media/flickr_test.html","closure/goog/ui/media/googlevideo_test.html","closure/goog/ui/media/mediamodel_test.html","closure/goog/ui/media/media_test.html","closure/goog/ui/media/mp3_test.html","closure/goog/ui/media/photo_test.html","closure/goog/ui/media/picasa_test.html","closure/goog/ui/media/vimeo_test.html","closure/goog/ui/media/youtube_test.html","closure/goog/ui/menubuttonrenderer_test.html","closure/goog/ui/menubutton_test.html","closure/goog/ui/menuitemrenderer_test.html","closure/goog/ui/menuitem_test.html","closure/goog/ui/menuseparatorrenderer_test.html","closure/goog/ui/menu_test.html","closure/goog/ui/mockactivitymonitor_test.html","closure/goog/ui/modalpopup_test.html","closure/goog/ui/nativebuttonrenderer_test.html","closure/goog/ui/paletterenderer_test.html","closure/goog/ui/palette_test.html","closure/goog/ui/plaintextspellchecker_test.html","closure/goog/ui/popupbase_test.html","closure/goog/ui/popupcolorpicker_test.html","closure/goog/ui/popupdatepicker_test.html","closure/goog/ui/popupmenu_test.html","closure/goog/ui/popup_test.html","closure/goog/ui/prompt_test.html","closure/goog/ui/rangemodel_test.html","closure/goog/ui/registry_test.html","closure/goog/ui/richtextspellchecker_test.html","closure/goog/ui/roundedpanel_test.html","closure/goog/ui/scrollfloater_test.html","closure/goog/ui/selectionmenubutton_test.html","closure/goog/ui/selectionmodel_test.html","closure/goog/ui/select_test.html","closure/goog/ui/serverchart_test.html","closure/goog/ui/sliderbase_test.html","closure/goog/ui/splitbehavior_test.html","closure/goog/ui/splitpane_test.html","closure/goog/ui/style/app/buttonrenderer_test.html","closure/goog/ui/style/app/menubuttonrenderer_test.html","closure/goog/ui/style/app/primaryactionbuttonrenderer_test.html","closure/goog/ui/submenu_test.html","closure/goog/ui/tabbarrenderer_test.html","closure/goog/ui/tabbar_test.html","closure/goog/ui/tablesorter_test.html","closure/goog/ui/tabpane_test.html","closure/goog/ui/tabrenderer_test.html","closure/goog/ui/tab_test.html","closure/goog/ui/textarea_test.html","closure/goog/ui/toolbarcolormenubuttonrenderer_test.html","closure/goog/ui/toolbarseparatorrenderer_test.html","closure/goog/ui/toolbar_test.html","closure/goog/ui/tree/basenode_test.html","closure/goog/ui/tree/treecontrol_test.html","closure/goog/ui/tree/typeahead_test.html","closure/goog/ui/twothumbslider_test.html","closure/goog/ui/zippy_test.html","closure/goog/uri/uri_test.html","closure/goog/uri/utils_test.html","closure/goog/useragent/adobereader_test.html","closure/goog/useragent/flash_test.html","closure/goog/useragent/jscript_test.html","closure/goog/useragent/platform_test.html","closure/goog/useragent/product_test.html","closure/goog/useragent/useragent_quirks_test.html","closure/goog/useragent/useragent_test.html","closure/goog/vec/float32array_test.html","closure/goog/vec/float64array_test.html","closure/goog/vec/mat3d_test.html","closure/goog/vec/mat3f_test.html","closure/goog/vec/mat3_test.html","closure/goog/vec/mat4d_test.html","closure/goog/vec/mat4f_test.html","closure/goog/vec/mat4_test.html","closure/goog/vec/matrix3_test.html","closure/goog/vec/matrix4_test.html","closure/goog/vec/quaternion_test.html","closure/goog/vec/ray_test.html","closure/goog/vec/vec2d_test.html","closure/goog/vec/vec2f_test.html","closure/goog/vec/vec2_test.html","closure/goog/vec/vec3d_test.html","closure/goog/vec/vec3f_test.html","closure/goog/vec/vec3_test.html","closure/goog/vec/vec4d_test.html","closure/goog/vec/vec4f_test.html","closure/goog/vec/vec4_test.html","closure/goog/window/window_test.html","third_party/closure/goog/dojo/dom/query_test.html","third_party/closure/goog/loremipsum/text/loremipsum_test.html","third_party/closure/goog/mochikit/async/deferred_async_test.html","third_party/closure/goog/mochikit/async/deferredlist_test.html","third_party/closure/goog/mochikit/async/deferred_test.html"]; diff --git a/mapbox-gl/closure-library/closure/bin/build/closurebuilder.py b/mapbox-gl/closure-library/closure/bin/build/closurebuilder.py new file mode 100644 index 0000000000..b7ed6e43a4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/closurebuilder.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +# +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utility for Closure Library dependency calculation. + +ClosureBuilder scans source files to build dependency info. From the +dependencies, the script can produce a manifest in dependency order, +a concatenated script, or compiled output from the Closure Compiler. + +Paths to files can be expressed as individual arguments to the tool (intended +for use with find and xargs). As a convenience, --root can be used to specify +all JS files below a directory. + +usage: %prog [options] [file1.js file2.js ...] +""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import logging +import optparse +import os +import sys + +import depstree +import jscompiler +import source +import treescan + + +def _GetOptionsParser(): + """Get the options parser.""" + + parser = optparse.OptionParser(__doc__) + parser.add_option('-i', + '--input', + dest='inputs', + action='append', + default=[], + help='One or more input files to calculate dependencies ' + 'for. The namespaces in this file will be combined with ' + 'those given with the -n flag to form the set of ' + 'namespaces to find dependencies for.') + parser.add_option('-n', + '--namespace', + dest='namespaces', + action='append', + default=[], + help='One or more namespaces to calculate dependencies ' + 'for. These namespaces will be combined with those given ' + 'with the -i flag to form the set of namespaces to find ' + 'dependencies for. A Closure namespace is a ' + 'dot-delimited path expression declared with a call to ' + 'goog.provide() (e.g. "goog.array" or "foo.bar").') + parser.add_option('--root', + dest='roots', + action='append', + default=[], + help='The paths that should be traversed to build the ' + 'dependencies.') + parser.add_option('-o', + '--output_mode', + dest='output_mode', + type='choice', + action='store', + choices=['list', 'script', 'compiled'], + default='list', + help='The type of output to generate from this script. ' + 'Options are "list" for a list of filenames, "script" ' + 'for a single script containing the contents of all the ' + 'files, or "compiled" to produce compiled output with ' + 'the Closure Compiler. Default is "list".') + parser.add_option('-c', + '--compiler_jar', + dest='compiler_jar', + action='store', + help='The location of the Closure compiler .jar file.') + parser.add_option('-f', + '--compiler_flags', + dest='compiler_flags', + default=[], + action='append', + help='Additional flags to pass to the Closure compiler. ' + 'To pass multiple flags, --compiler_flags has to be ' + 'specified multiple times.') + parser.add_option('-j', + '--jvm_flags', + dest='jvm_flags', + default=[], + action='append', + help='Additional flags to pass to the JVM compiler. ' + 'To pass multiple flags, --jvm_flags has to be ' + 'specified multiple times.') + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + + return parser + + +def _GetInputByPath(path, sources): + """Get the source identified by a path. + + Args: + path: str, A path to a file that identifies a source. + sources: An iterable collection of source objects. + + Returns: + The source from sources identified by path, if found. Converts to + absolute paths for comparison. + """ + for js_source in sources: + # Convert both to absolute paths for comparison. + if os.path.abspath(path) == os.path.abspath(js_source.GetPath()): + return js_source + + +def _GetClosureBaseFile(sources): + """Given a set of sources, returns the one base.js file. + + Note that if zero or two or more base.js files are found, an error message + will be written and the program will be exited. + + Args: + sources: An iterable of _PathSource objects. + + Returns: + The _PathSource representing the base Closure file. + """ + base_files = [ + js_source for js_source in sources if _IsClosureBaseFile(js_source)] + + if not base_files: + logging.error('No Closure base.js file found.') + sys.exit(1) + if len(base_files) > 1: + logging.error('More than one Closure base.js files found at these paths:') + for base_file in base_files: + logging.error(base_file.GetPath()) + sys.exit(1) + return base_files[0] + + +def _IsClosureBaseFile(js_source): + """Returns true if the given _PathSource is the Closure base.js source.""" + return (os.path.basename(js_source.GetPath()) == 'base.js' and + js_source.provides == set(['goog'])) + + +class _PathSource(source.Source): + """Source file subclass that remembers its file path.""" + + def __init__(self, path): + """Initialize a source. + + Args: + path: str, Path to a JavaScript file. The source string will be read + from this file. + """ + super(_PathSource, self).__init__(source.GetFileContents(path)) + + self._path = path + + def __str__(self): + return 'PathSource %s' % self._path + + def GetPath(self): + """Returns the path.""" + return self._path + + +def _WrapGoogModuleSource(src): + return ('goog.loadModule(function(exports) {' + '"use strict";' + '{0}' + '\n' # terminate any trailing single line comment. + ';return exports' + '});\n').format(src) + + +def main(): + logging.basicConfig(format=(sys.argv[0] + ': %(message)s'), + level=logging.INFO) + options, args = _GetOptionsParser().parse_args() + + # Make our output pipe. + if options.output_file: + out = open(options.output_file, 'w') + else: + out = sys.stdout + + sources = set() + + logging.info('Scanning paths...') + for path in options.roots: + for js_path in treescan.ScanTreeForJsFiles(path): + sources.add(_PathSource(js_path)) + + # Add scripts specified on the command line. + for js_path in args: + sources.add(_PathSource(js_path)) + + logging.info('%s sources scanned.', len(sources)) + + # Though deps output doesn't need to query the tree, we still build it + # to validate dependencies. + logging.info('Building dependency tree..') + tree = depstree.DepsTree(sources) + + input_namespaces = set() + inputs = options.inputs or [] + for input_path in inputs: + js_input = _GetInputByPath(input_path, sources) + if not js_input: + logging.error('No source matched input %s', input_path) + sys.exit(1) + input_namespaces.update(js_input.provides) + + input_namespaces.update(options.namespaces) + + if not input_namespaces: + logging.error('No namespaces found. At least one namespace must be ' + 'specified with the --namespace or --input flags.') + sys.exit(2) + + # The Closure Library base file must go first. + base = _GetClosureBaseFile(sources) + deps = [base] + tree.GetDependencies(input_namespaces) + + output_mode = options.output_mode + if output_mode == 'list': + out.writelines([js_source.GetPath() + '\n' for js_source in deps]) + elif output_mode == 'script': + for js_source in deps: + src = js_source.GetSource() + if js_source.is_goog_module: + src = _WrapGoogModuleSource(src) + out.write(src + '\n') + elif output_mode == 'compiled': + logging.warning("""\ +Closure Compiler now natively understands and orders Closure dependencies and +is prefererred over using this script for performing JavaScript compilation. + +Please migrate your codebase. + +See: +https://github.com/google/closure-compiler/wiki/Manage-Closure-Dependencies +""") + + # Make sure a .jar is specified. + if not options.compiler_jar: + logging.error('--compiler_jar flag must be specified if --output is ' + '"compiled"') + sys.exit(2) + + # Will throw an error if the compilation fails. + compiled_source = jscompiler.Compile( + options.compiler_jar, + [js_source.GetPath() for js_source in deps], + jvm_flags=options.jvm_flags, + compiler_flags=options.compiler_flags) + + logging.info('JavaScript compilation succeeded.') + out.write(compiled_source) + + else: + logging.error('Invalid value for --output flag.') + sys.exit(2) + + +if __name__ == '__main__': + main() diff --git a/mapbox-gl/closure-library/closure/bin/build/depstree.py b/mapbox-gl/closure-library/closure/bin/build/depstree.py new file mode 100644 index 0000000000..f288dd3aa6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/depstree.py @@ -0,0 +1,189 @@ +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Class to represent a full Closure Library dependency tree. + +Offers a queryable tree of dependencies of a given set of sources. The tree +will also do logical validation to prevent duplicate provides and circular +dependencies. +""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +class DepsTree(object): + """Represents the set of dependencies between source files.""" + + def __init__(self, sources): + """Initializes the tree with a set of sources. + + Args: + sources: A set of JavaScript sources. + + Raises: + MultipleProvideError: A namespace is provided by muplitple sources. + NamespaceNotFoundError: A namespace is required but never provided. + """ + + self._sources = sources + self._provides_map = dict() + + # Ensure nothing was provided twice. + for source in sources: + for provide in source.provides: + if provide in self._provides_map: + raise MultipleProvideError( + provide, [self._provides_map[provide], source]) + + self._provides_map[provide] = source + + # Check that all required namespaces are provided. + for source in sources: + for require in source.requires: + if require not in self._provides_map: + raise NamespaceNotFoundError(require, source) + + def GetDependencies(self, required_namespaces): + """Get source dependencies, in order, for the given namespaces. + + Args: + required_namespaces: A string (for one) or list (for one or more) of + namespaces. + + Returns: + A list of source objects that provide those namespaces and all + requirements, in dependency order. + + Raises: + NamespaceNotFoundError: A namespace is requested but doesn't exist. + CircularDependencyError: A cycle is detected in the dependency tree. + """ + if isinstance(required_namespaces, str): + required_namespaces = [required_namespaces] + + deps_sources = [] + + for namespace in required_namespaces: + for source in DepsTree._ResolveDependencies( + namespace, [], self._provides_map, []): + if source not in deps_sources: + deps_sources.append(source) + + return deps_sources + + @staticmethod + def _ResolveDependencies(required_namespace, deps_list, provides_map, + traversal_path): + """Resolve dependencies for Closure source files. + + Follows the dependency tree down and builds a list of sources in dependency + order. This function will recursively call itself to fill all dependencies + below the requested namespaces, and then append its sources at the end of + the list. + + Args: + required_namespace: String of required namespace. + deps_list: List of sources in dependency order. This function will append + the required source once all of its dependencies are satisfied. + provides_map: Map from namespace to source that provides it. + traversal_path: List of namespaces of our path from the root down the + dependency/recursion tree. Used to identify cyclical dependencies. + This is a list used as a stack -- when the function is entered, the + current namespace is pushed and popped right before returning. + Each recursive call will check that the current namespace does not + appear in the list, throwing a CircularDependencyError if it does. + + Returns: + The given deps_list object filled with sources in dependency order. + + Raises: + NamespaceNotFoundError: A namespace is requested but doesn't exist. + CircularDependencyError: A cycle is detected in the dependency tree. + """ + + source = provides_map.get(required_namespace) + if not source: + raise NamespaceNotFoundError(required_namespace) + + if required_namespace in traversal_path: + traversal_path.append(required_namespace) # do this *after* the test + + # This must be a cycle. + raise CircularDependencyError(traversal_path) + + # If we don't have the source yet, we'll have to visit this namespace and + # add the required dependencies to deps_list. + if source not in deps_list: + traversal_path.append(required_namespace) + + for require in source.requires: + + # Append all other dependencies before we append our own. + DepsTree._ResolveDependencies(require, deps_list, provides_map, + traversal_path) + deps_list.append(source) + + traversal_path.pop() + + return deps_list + + +class BaseDepsTreeError(Exception): + """Base DepsTree error.""" + + def __init__(self): + Exception.__init__(self) + + +class CircularDependencyError(BaseDepsTreeError): + """Raised when a dependency cycle is encountered.""" + + def __init__(self, dependency_list): + BaseDepsTreeError.__init__(self) + self._dependency_list = dependency_list + + def __str__(self): + return ('Encountered circular dependency:\n%s\n' % + '\n'.join(self._dependency_list)) + + +class MultipleProvideError(BaseDepsTreeError): + """Raised when a namespace is provided more than once.""" + + def __init__(self, namespace, sources): + BaseDepsTreeError.__init__(self) + self._namespace = namespace + self._sources = sources + + def __str__(self): + source_strs = map(str, self._sources) + + return ('Namespace "%s" provided more than once in sources:\n%s\n' % + (self._namespace, '\n'.join(source_strs))) + + +class NamespaceNotFoundError(BaseDepsTreeError): + """Raised when a namespace is requested but not provided.""" + + def __init__(self, namespace, source=None): + BaseDepsTreeError.__init__(self) + self._namespace = namespace + self._source = source + + def __str__(self): + msg = 'Namespace "%s" never provided.' % self._namespace + if self._source: + msg += ' Required in %s' % self._source + return msg diff --git a/mapbox-gl/closure-library/closure/bin/build/depstree_test.py b/mapbox-gl/closure-library/closure/bin/build/depstree_test.py new file mode 100644 index 0000000000..eb4c99958e --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/depstree_test.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Unit test for depstree.""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import unittest + +import depstree + + +def _GetProvides(sources): + """Get all namespaces provided by a collection of sources.""" + + provides = set() + for source in sources: + provides.update(source.provides) + return provides + + +class MockSource(object): + """Mock Source file.""" + + def __init__(self, provides, requires): + self.provides = set(provides) + self.requires = set(requires) + + def __repr__(self): + return 'MockSource %s' % self.provides + + +class DepsTreeTestCase(unittest.TestCase): + """Unit test for DepsTree. Tests several common situations and errors.""" + + def AssertValidDependencies(self, deps_list): + """Validates a dependency list. + + Asserts that a dependency list is valid: For every source in the list, + ensure that every require is provided by a source earlier in the list. + + Args: + deps_list: A list of sources that should be in dependency order. + """ + + for i in range(len(deps_list)): + source = deps_list[i] + previous_provides = _GetProvides(deps_list[:i]) + for require in source.requires: + self.assertTrue( + require in previous_provides, + 'Namespace "%s" not provided before required by %s' % ( + require, source)) + + def testSimpleDepsTree(self): + a = MockSource(['A'], ['B', 'C']) + b = MockSource(['B'], []) + c = MockSource(['C'], ['D']) + d = MockSource(['D'], ['E']) + e = MockSource(['E'], []) + + tree = depstree.DepsTree([a, b, c, d, e]) + + self.AssertValidDependencies(tree.GetDependencies('A')) + self.AssertValidDependencies(tree.GetDependencies('B')) + self.AssertValidDependencies(tree.GetDependencies('C')) + self.AssertValidDependencies(tree.GetDependencies('D')) + self.AssertValidDependencies(tree.GetDependencies('E')) + + def testCircularDependency(self): + # Circular deps + a = MockSource(['A'], ['B']) + b = MockSource(['B'], ['C']) + c = MockSource(['C'], ['A']) + + tree = depstree.DepsTree([a, b, c]) + + self.assertRaises(depstree.CircularDependencyError, + tree.GetDependencies, 'A') + + def testRequiresUndefinedNamespace(self): + a = MockSource(['A'], ['B']) + b = MockSource(['B'], ['C']) + c = MockSource(['C'], ['D']) # But there is no D. + + def MakeDepsTree(): + return depstree.DepsTree([a, b, c]) + + self.assertRaises(depstree.NamespaceNotFoundError, MakeDepsTree) + + def testDepsForMissingNamespace(self): + a = MockSource(['A'], ['B']) + b = MockSource(['B'], []) + + tree = depstree.DepsTree([a, b]) + + # There is no C. + self.assertRaises(depstree.NamespaceNotFoundError, + tree.GetDependencies, 'C') + + def testMultipleRequires(self): + a = MockSource(['A'], ['B']) + b = MockSource(['B'], ['C']) + c = MockSource(['C'], []) + d = MockSource(['D'], ['B']) + + tree = depstree.DepsTree([a, b, c, d]) + self.AssertValidDependencies(tree.GetDependencies(['D', 'A'])) + + +if __name__ == '__main__': + unittest.main() diff --git a/mapbox-gl/closure-library/closure/bin/build/depswriter.py b/mapbox-gl/closure-library/closure/bin/build/depswriter.py new file mode 100644 index 0000000000..bc3be88a35 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/depswriter.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Generates out a Closure deps.js file given a list of JavaScript sources. + +Paths can be specified as arguments or (more commonly) specifying trees +with the flags (call with --help for descriptions). + +Usage: depswriter.py [path/to/js1.js [path/to/js2.js] ...] +""" + +import logging +import optparse +import os +import posixpath +import shlex +import sys + +import source +import treescan + + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +def MakeDepsFile(source_map): + """Make a generated deps file. + + Args: + source_map: A dict map of the source path to source.Source object. + + Returns: + str, A generated deps file source. + """ + + # Write in path alphabetical order + paths = sorted(source_map.keys()) + + lines = [] + + for path in paths: + js_source = source_map[path] + + # We don't need to add entries that don't provide anything. + if js_source.provides: + lines.append(_GetDepsLine(path, js_source)) + + return ''.join(lines) + + +def _GetDepsLine(path, js_source): + """Get a deps.js file string for a source.""" + + provides = sorted(js_source.provides) + requires = sorted(js_source.requires) + module = 'true' if js_source.is_goog_module else 'false' + + return 'goog.addDependency(\'%s\', %s, %s, %s);\n' % ( + path, provides, requires, module) + + +def _GetOptionsParser(): + """Get the options parser.""" + + parser = optparse.OptionParser(__doc__) + + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + parser.add_option('--root', + dest='roots', + default=[], + action='append', + help='A root directory to scan for JS source files. ' + 'Paths of JS files in generated deps file will be ' + 'relative to this path. This flag may be specified ' + 'multiple times.') + parser.add_option('--root_with_prefix', + dest='roots_with_prefix', + default=[], + action='append', + help='A root directory to scan for JS source files, plus ' + 'a prefix (if either contains a space, surround with ' + 'quotes). Paths in generated deps file will be relative ' + 'to the root, but preceded by the prefix. This flag ' + 'may be specified multiple times.') + parser.add_option('--path_with_depspath', + dest='paths_with_depspath', + default=[], + action='append', + help='A path to a source file and an alternate path to ' + 'the file in the generated deps file (if either contains ' + 'a space, surround with whitespace). This flag may be ' + 'specified multiple times.') + return parser + + +def _NormalizePathSeparators(path): + """Replaces OS-specific path separators with POSIX-style slashes. + + Args: + path: str, A file path. + + Returns: + str, The path with any OS-specific path separators (such as backslash on + Windows) replaced with URL-compatible forward slashes. A no-op on systems + that use POSIX paths. + """ + return path.replace(os.sep, posixpath.sep) + + +def _GetRelativePathToSourceDict(root, prefix=''): + """Scans a top root directory for .js sources. + + Args: + root: str, Root directory. + prefix: str, Prefix for returned paths. + + Returns: + dict, A map of relative paths (with prefix, if given), to source.Source + objects. + """ + # Remember and restore the cwd when we're done. We work from the root so + # that paths are relative from the root. + start_wd = os.getcwd() + os.chdir(root) + + path_to_source = {} + for path in treescan.ScanTreeForJsFiles('.'): + prefixed_path = _NormalizePathSeparators(os.path.join(prefix, path)) + path_to_source[prefixed_path] = source.Source(source.GetFileContents(path)) + + os.chdir(start_wd) + + return path_to_source + + +def _GetPair(s): + """Return a string as a shell-parsed tuple. Two values expected.""" + try: + # shlex uses '\' as an escape character, so they must be escaped. + s = s.replace('\\', '\\\\') + first, second = shlex.split(s) + return (first, second) + except: + raise Exception('Unable to parse input line as a pair: %s' % s) + + +def main(): + """CLI frontend to MakeDepsFile.""" + logging.basicConfig(format=(sys.argv[0] + ': %(message)s'), + level=logging.INFO) + options, args = _GetOptionsParser().parse_args() + + path_to_source = {} + + # Roots without prefixes + for root in options.roots: + path_to_source.update(_GetRelativePathToSourceDict(root)) + + # Roots with prefixes + for root_and_prefix in options.roots_with_prefix: + root, prefix = _GetPair(root_and_prefix) + path_to_source.update(_GetRelativePathToSourceDict(root, prefix=prefix)) + + # Source paths + for path in args: + path_to_source[path] = source.Source(source.GetFileContents(path)) + + # Source paths with alternate deps paths + for path_with_depspath in options.paths_with_depspath: + srcpath, depspath = _GetPair(path_with_depspath) + path_to_source[depspath] = source.Source(source.GetFileContents(srcpath)) + + # Make our output pipe. + if options.output_file: + out = open(options.output_file, 'w') + else: + out = sys.stdout + + out.write('// This file was autogenerated by %s.\n' % sys.argv[0]) + out.write('// Please do not edit.\n') + + out.write(MakeDepsFile(path_to_source)) + + +if __name__ == '__main__': + main() diff --git a/mapbox-gl/closure-library/closure/bin/build/jscompiler.py b/mapbox-gl/closure-library/closure/bin/build/jscompiler.py new file mode 100644 index 0000000000..cc6eb55f9e --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/jscompiler.py @@ -0,0 +1,135 @@ +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utility to use the Closure Compiler CLI from Python.""" + + +import logging +import os +import re +import subprocess + + +# Pulls just the major and minor version numbers from the first line of +# 'java -version'. Versions are in the format of [0-9]+\.[0-9]+\..* See: +# http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html +_VERSION_REGEX = re.compile(r'"([0-9]+)\.([0-9]+)') + + +class JsCompilerError(Exception): + """Raised if there's an error in calling the compiler.""" + pass + + +def _GetJavaVersionString(): + """Get the version string from the Java VM.""" + return subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT) + + +def _ParseJavaVersion(version_string): + """Returns a 2-tuple for the current version of Java installed. + + Args: + version_string: String of the Java version (e.g. '1.7.2-ea'). + + Returns: + The major and minor versions, as a 2-tuple (e.g. (1, 7)). + """ + match = _VERSION_REGEX.search(version_string) + if match: + version = tuple(int(x, 10) for x in match.groups()) + assert len(version) == 2 + return version + + +def _JavaSupports32BitMode(): + """Determines whether the JVM supports 32-bit mode on the platform.""" + # Suppresses process output to stderr and stdout from showing up in the + # console as we're only trying to determine 32-bit JVM support. + supported = False + try: + devnull = open(os.devnull, 'wb') + return subprocess.call( + ['java', '-d32', '-version'], stdout=devnull, stderr=devnull) == 0 + except IOError: + pass + else: + devnull.close() + return supported + + +def _GetJsCompilerArgs(compiler_jar_path, java_version, source_paths, + jvm_flags, compiler_flags): + """Assembles arguments for call to JsCompiler.""" + + if java_version < (1, 7): + raise JsCompilerError('Closure Compiler requires Java 1.7 or higher. ' + 'Please visit http://www.java.com/getjava') + + args = ['java'] + + # Add JVM flags we believe will produce the best performance. See + # https://groups.google.com/forum/#!topic/closure-library-discuss/7w_O9-vzlj4 + + # Attempt 32-bit mode if available (Java 7 on Mac OS X does not support 32-bit + # mode, for example). + if _JavaSupports32BitMode(): + args += ['-d32'] + + # Prefer the "client" VM. + args += ['-client'] + + # Add JVM flags, if any + if jvm_flags: + args += jvm_flags + + # Add the application JAR. + args += ['-jar', compiler_jar_path] + + for path in source_paths: + args += ['--js', path] + + # Add compiler flags, if any. + if compiler_flags: + args += compiler_flags + + return args + + +def Compile(compiler_jar_path, source_paths, + jvm_flags=None, + compiler_flags=None): + """Prepares command-line call to Closure Compiler. + + Args: + compiler_jar_path: Path to the Closure compiler .jar file. + source_paths: Source paths to build, in order. + jvm_flags: A list of additional flags to pass on to JVM. + compiler_flags: A list of additional flags to pass on to Closure Compiler. + + Returns: + The compiled source, as a string, or None if compilation failed. + """ + + java_version = _ParseJavaVersion(_GetJavaVersionString()) + + args = _GetJsCompilerArgs( + compiler_jar_path, java_version, source_paths, jvm_flags, compiler_flags) + + logging.info('Compiling with the following command: %s', ' '.join(args)) + + try: + return subprocess.check_output(args) + except subprocess.CalledProcessError: + raise JsCompilerError('JavaScript compilation failed.') diff --git a/mapbox-gl/closure-library/closure/bin/build/jscompiler_test.py b/mapbox-gl/closure-library/closure/bin/build/jscompiler_test.py new file mode 100644 index 0000000000..f523a6697b --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/jscompiler_test.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Unit test for depstree.""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import unittest + +import jscompiler + + +class JsCompilerTestCase(unittest.TestCase): + """Unit tests for jscompiler module.""" + + def testGetJsCompilerArgs(self): + + original_check = jscompiler._JavaSupports32BitMode + jscompiler._JavaSupports32BitMode = lambda: False + args = jscompiler._GetJsCompilerArgs( + 'path/to/jscompiler.jar', + (1, 7), + ['path/to/src1.js', 'path/to/src2.js'], + ['--test_jvm_flag'], + ['--test_compiler_flag'] + ) + + self.assertEqual( + ['java', '-client', '--test_jvm_flag', + '-jar', 'path/to/jscompiler.jar', + '--js', 'path/to/src1.js', + '--js', 'path/to/src2.js', '--test_compiler_flag'], + args) + + def CheckJava15RaisesError(): + jscompiler._GetJsCompilerArgs( + 'path/to/jscompiler.jar', + (1, 5), + ['path/to/src1.js', 'path/to/src2.js'], + ['--test_jvm_flag'], + ['--test_compiler_flag']) + + self.assertRaises(jscompiler.JsCompilerError, CheckJava15RaisesError) + jscompiler._JavaSupports32BitMode = original_check + + def testGetJsCompilerArgs32BitJava(self): + + original_check = jscompiler._JavaSupports32BitMode + + # Should include the -d32 flag only if 32-bit Java is supported by the + # system. + jscompiler._JavaSupports32BitMode = lambda: True + args = jscompiler._GetJsCompilerArgs( + 'path/to/jscompiler.jar', + (1, 7), + ['path/to/src1.js', 'path/to/src2.js'], + ['--test_jvm_flag'], + ['--test_compiler_flag']) + + self.assertEqual( + ['java', '-d32', '-client', '--test_jvm_flag', + '-jar', 'path/to/jscompiler.jar', + '--js', 'path/to/src1.js', + '--js', 'path/to/src2.js', + '--test_compiler_flag'], + args) + + # Should exclude the -d32 flag if 32-bit Java is not supported by the + # system. + jscompiler._JavaSupports32BitMode = lambda: False + args = jscompiler._GetJsCompilerArgs( + 'path/to/jscompiler.jar', + (1, 7), + ['path/to/src1.js', 'path/to/src2.js'], + ['--test_jvm_flag'], + ['--test_compiler_flag']) + + self.assertEqual( + ['java', '-client', '--test_jvm_flag', + '-jar', 'path/to/jscompiler.jar', + '--js', 'path/to/src1.js', + '--js', 'path/to/src2.js', + '--test_compiler_flag'], + args) + + jscompiler._JavaSupports32BitMode = original_check + + def testGetJavaVersion(self): + + def assertVersion(expected, version_string): + self.assertEquals(expected, jscompiler._ParseJavaVersion(version_string)) + + assertVersion((1, 7), _TEST_JAVA_VERSION_STRING) + assertVersion((1, 4), 'java version "1.4.0_03-ea"') + + +_TEST_JAVA_VERSION_STRING = """\ +openjdk version "1.7.0-google-v5" +OpenJDK Runtime Environment (build 1.7.0-google-v5-64327-39803485) +OpenJDK Server VM (build 22.0-b10, mixed mode) +""" + + +if __name__ == '__main__': + unittest.main() diff --git a/mapbox-gl/closure-library/closure/bin/build/source.py b/mapbox-gl/closure-library/closure/bin/build/source.py new file mode 100644 index 0000000000..be5e0d8ad6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/source.py @@ -0,0 +1,127 @@ +# Copyright 2009 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Scans a source JS file for its provided and required namespaces. + +Simple class to scan a JavaScript file and express its dependencies. +""" + +__author__ = 'nnaze@google.com' + + +import re + +_BASE_REGEX_STRING = r'^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' +_MODULE_REGEX = re.compile(_BASE_REGEX_STRING % 'module') +_PROVIDE_REGEX = re.compile(_BASE_REGEX_STRING % 'provide') + +_REQUIRE_REGEX_STRING = (r'^\s*(?:(?:var|let|const)\s+[a-zA-Z_$][a-zA-Z0-9$_]*' + r'\s*=\s*)?goog\.require\(\s*[\'"](.+)[\'"]\s*\)') +_REQUIRES_REGEX = re.compile(_REQUIRE_REGEX_STRING) + + +class Source(object): + """Scans a JavaScript source for its provided and required namespaces.""" + + # Matches a "/* ... */" comment. + # Note: We can't definitively distinguish a "/*" in a string literal without a + # state machine tokenizer. We'll assume that a line starting with whitespace + # and "/*" is a comment. + _COMMENT_REGEX = re.compile( + r""" + ^\s* # Start of a new line and whitespace + /\* # Opening "/*" + .*? # Non greedy match of any characters (including newlines) + \*/ # Closing "*/""", + re.MULTILINE | re.DOTALL | re.VERBOSE) + + def __init__(self, source): + """Initialize a source. + + Args: + source: str, The JavaScript source. + """ + + self.provides = set() + self.requires = set() + self.is_goog_module = False + + self._source = source + self._ScanSource() + + def GetSource(self): + """Get the source as a string.""" + return self._source + + @classmethod + def _StripComments(cls, source): + return cls._COMMENT_REGEX.sub('', source) + + @classmethod + def _HasProvideGoogFlag(cls, source): + """Determines whether the @provideGoog flag is in a comment.""" + for comment_content in cls._COMMENT_REGEX.findall(source): + if '@provideGoog' in comment_content: + return True + + return False + + def _ScanSource(self): + """Fill in provides and requires by scanning the source.""" + + stripped_source = self._StripComments(self.GetSource()) + + source_lines = stripped_source.splitlines() + for line in source_lines: + match = _PROVIDE_REGEX.match(line) + if match: + self.provides.add(match.group(1)) + match = _MODULE_REGEX.match(line) + if match: + self.provides.add(match.group(1)) + self.is_goog_module = True + match = _REQUIRES_REGEX.match(line) + if match: + self.requires.add(match.group(1)) + + # Closure's base file implicitly provides 'goog'. + # This is indicated with the @provideGoog flag. + if self._HasProvideGoogFlag(self.GetSource()): + + if len(self.provides) or len(self.requires): + raise Exception( + 'Base file should not provide or require namespaces.') + + self.provides.add('goog') + + +def GetFileContents(path): + """Get a file's contents as a string. + + Args: + path: str, Path to file. + + Returns: + str, Contents of file. + + Raises: + IOError: An error occurred opening or reading the file. + + """ + fileobj = open(path) + try: + return fileobj.read() + finally: + fileobj.close() diff --git a/mapbox-gl/closure-library/closure/bin/build/source_test.py b/mapbox-gl/closure-library/closure/bin/build/source_test.py new file mode 100644 index 0000000000..eb1591b442 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/source_test.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Unit test for source.""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import unittest + +import source + + +class SourceTestCase(unittest.TestCase): + """Unit test for source. Tests the parser on a known source input.""" + + def testSourceScan(self): + test_source = source.Source(_TEST_SOURCE) + + self.assertEqual(set(['foo', 'foo.test']), + test_source.provides) + self.assertEqual(set(['goog.dom', 'goog.events.EventType']), + test_source.requires) + self.assertFalse(test_source.is_goog_module) + + def testSourceScanBase(self): + test_source = source.Source(_TEST_BASE_SOURCE) + + self.assertEqual(set(['goog']), + test_source.provides) + self.assertEqual(test_source.requires, set()) + self.assertFalse(test_source.is_goog_module) + + def testSourceScanBadBase(self): + + def MakeSource(): + source.Source(_TEST_BAD_BASE_SOURCE) + + self.assertRaises(Exception, MakeSource) + + def testSourceScanGoogModule(self): + test_source = source.Source(_TEST_MODULE_SOURCE) + + self.assertEqual(set(['foo']), + test_source.provides) + self.assertEqual(set(['bar']), + test_source.requires) + self.assertTrue(test_source.is_goog_module) + + def testStripComments(self): + self.assertEquals( + '\nvar foo = function() {}', + source.Source._StripComments(( + '/* This is\n' + ' a comment split\n' + ' over multiple lines\n' + '*/\n' + 'var foo = function() {}'))) + + def testGoogStatementsInComments(self): + test_source = source.Source(_TEST_COMMENT_SOURCE) + + self.assertEqual(set(['foo']), + test_source.provides) + self.assertEqual(set(['goog.events.EventType']), + test_source.requires) + self.assertFalse(test_source.is_goog_module) + + def testHasProvideGoog(self): + self.assertTrue(source.Source._HasProvideGoogFlag(_TEST_BASE_SOURCE)) + self.assertTrue(source.Source._HasProvideGoogFlag(_TEST_BAD_BASE_SOURCE)) + self.assertFalse(source.Source._HasProvideGoogFlag(_TEST_COMMENT_SOURCE)) + + +_TEST_MODULE_SOURCE = """ +goog.module('foo'); +var b = goog.require('bar'); +""" + + +_TEST_SOURCE = """// Fake copyright notice + +/** Very important comment. */ + +goog.provide('foo'); +goog.provide('foo.test'); + +goog.require('goog.dom'); +goog.require('goog.events.EventType'); + +function foo() { + // Set bar to seventeen to increase performance. + this.bar = 17; +} +""" + +_TEST_COMMENT_SOURCE = """// Fake copyright notice + +goog.provide('foo'); + +/* +goog.provide('foo.test'); + */ + +/* +goog.require('goog.dom'); +*/ + +// goog.require('goog.dom'); + +goog.require('goog.events.EventType'); + +function bar() { + this.baz = 55; +} +""" + +_TEST_BASE_SOURCE = """ +/** + * @fileoverview The base file. + * @provideGoog + */ + +var goog = goog || {}; +""" + +_TEST_BAD_BASE_SOURCE = """ +/** + * @fileoverview The base file. + * @provideGoog + */ + +goog.provide('goog'); +""" + + +if __name__ == '__main__': + unittest.main() diff --git a/mapbox-gl/closure-library/closure/bin/build/treescan.py b/mapbox-gl/closure-library/closure/bin/build/treescan.py new file mode 100644 index 0000000000..6694593aab --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/build/treescan.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Shared utility functions for scanning directory trees.""" + +import os +import re + + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +# Matches a .js file path. +_JS_FILE_REGEX = re.compile(r'^.+\.js$') + + +def ScanTreeForJsFiles(root): + """Scans a directory tree for JavaScript files. + + Args: + root: str, Path to a root directory. + + Returns: + An iterable of paths to JS files, relative to cwd. + """ + return ScanTree(root, path_filter=_JS_FILE_REGEX) + + +def ScanTree(root, path_filter=None, ignore_hidden=True): + """Scans a directory tree for files. + + Args: + root: str, Path to a root directory. + path_filter: A regular expression filter. If set, only paths matching + the path_filter are returned. + ignore_hidden: If True, do not follow or return hidden directories or files + (those starting with a '.' character). + + Yields: + A string path to files, relative to cwd. + """ + + def OnError(os_error): + raise os_error + + for dirpath, dirnames, filenames in os.walk(root, onerror=OnError): + # os.walk allows us to modify dirnames to prevent decent into particular + # directories. Avoid hidden directories. + for dirname in dirnames: + if ignore_hidden and dirname.startswith('.'): + dirnames.remove(dirname) + + for filename in filenames: + + # nothing that starts with '.' + if ignore_hidden and filename.startswith('.'): + continue + + fullpath = os.path.join(dirpath, filename) + + if path_filter and not path_filter.match(fullpath): + continue + + yield os.path.normpath(fullpath) diff --git a/mapbox-gl/closure-library/closure/bin/calcdeps.py b/mapbox-gl/closure-library/closure/bin/calcdeps.py new file mode 100644 index 0000000000..9cb1a6db06 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/calcdeps.py @@ -0,0 +1,590 @@ +#!/usr/bin/env python +# +# Copyright 2006 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Calculates JavaScript dependencies without requiring Google's build system. + +This tool is deprecated and is provided for legacy users. +See build/closurebuilder.py and build/depswriter.py for the current tools. + +It iterates over a number of search paths and builds a dependency tree. With +the inputs provided, it walks the dependency tree and outputs all the files +required for compilation. +""" + + + + + +try: + import distutils.version +except ImportError: + # distutils is not available in all environments + distutils = None + +import logging +import optparse +import os +import re +import subprocess +import sys + + +_BASE_REGEX_STRING = '^\s*goog\.%s\(\s*[\'"](.+)[\'"]\s*\)' +req_regex = re.compile(_BASE_REGEX_STRING % 'require') +prov_regex = re.compile(_BASE_REGEX_STRING % 'provide') +ns_regex = re.compile('^ns:((\w+\.)*(\w+))$') +version_regex = re.compile('[\.0-9]+') + + +def IsValidFile(ref): + """Returns true if the provided reference is a file and exists.""" + return os.path.isfile(ref) + + +def IsJsFile(ref): + """Returns true if the provided reference is a Javascript file.""" + return ref.endswith('.js') + + +def IsNamespace(ref): + """Returns true if the provided reference is a namespace.""" + return re.match(ns_regex, ref) is not None + + +def IsDirectory(ref): + """Returns true if the provided reference is a directory.""" + return os.path.isdir(ref) + + +def ExpandDirectories(refs): + """Expands any directory references into inputs. + + Description: + Looks for any directories in the provided references. Found directories + are recursively searched for .js files, which are then added to the result + list. + + Args: + refs: a list of references such as files, directories, and namespaces + + Returns: + A list of references with directories removed and replaced by any + .js files that are found in them. Also, the paths will be normalized. + """ + result = [] + for ref in refs: + if IsDirectory(ref): + # Disable 'Unused variable' for subdirs + # pylint: disable=unused-variable + for (directory, subdirs, filenames) in os.walk(ref): + for filename in filenames: + if IsJsFile(filename): + result.append(os.path.join(directory, filename)) + else: + result.append(ref) + return map(os.path.normpath, result) + + +class DependencyInfo(object): + """Represents a dependency that is used to build and walk a tree.""" + + def __init__(self, filename): + self.filename = filename + self.provides = [] + self.requires = [] + + def __str__(self): + return '%s Provides: %s Requires: %s' % (self.filename, + repr(self.provides), + repr(self.requires)) + + +def BuildDependenciesFromFiles(files): + """Build a list of dependencies from a list of files. + + Description: + Takes a list of files, extracts their provides and requires, and builds + out a list of dependency objects. + + Args: + files: a list of files to be parsed for goog.provides and goog.requires. + + Returns: + A list of dependency objects, one for each file in the files argument. + """ + result = [] + filenames = set() + for filename in files: + if filename in filenames: + continue + + # Python 3 requires the file encoding to be specified + if (sys.version_info[0] < 3): + file_handle = open(filename, 'r') + else: + file_handle = open(filename, 'r', encoding='utf8') + + try: + dep = CreateDependencyInfo(filename, file_handle) + result.append(dep) + finally: + file_handle.close() + + filenames.add(filename) + + return result + + +def CreateDependencyInfo(filename, source): + """Create dependency info. + + Args: + filename: Filename for source. + source: File-like object containing source. + + Returns: + A DependencyInfo object with provides and requires filled. + """ + dep = DependencyInfo(filename) + for line in source: + if re.match(req_regex, line): + dep.requires.append(re.search(req_regex, line).group(1)) + if re.match(prov_regex, line): + dep.provides.append(re.search(prov_regex, line).group(1)) + return dep + + +def BuildDependencyHashFromDependencies(deps): + """Builds a hash for searching dependencies by the namespaces they provide. + + Description: + Dependency objects can provide multiple namespaces. This method enumerates + the provides of each dependency and adds them to a hash that can be used + to easily resolve a given dependency by a namespace it provides. + + Args: + deps: a list of dependency objects used to build the hash. + + Raises: + Exception: If a multiple files try to provide the same namepace. + + Returns: + A hash table { namespace: dependency } that can be used to resolve a + dependency by a namespace it provides. + """ + dep_hash = {} + for dep in deps: + for provide in dep.provides: + if provide in dep_hash: + raise Exception('Duplicate provide (%s) in (%s, %s)' % ( + provide, + dep_hash[provide].filename, + dep.filename)) + dep_hash[provide] = dep + return dep_hash + + +def CalculateDependencies(paths, inputs): + """Calculates the dependencies for given inputs. + + Description: + This method takes a list of paths (files, directories) and builds a + searchable data structure based on the namespaces that each .js file + provides. It then parses through each input, resolving dependencies + against this data structure. The final output is a list of files, + including the inputs, that represent all of the code that is needed to + compile the given inputs. + + Args: + paths: the references (files, directories) that are used to build the + dependency hash. + inputs: the inputs (files, directories, namespaces) that have dependencies + that need to be calculated. + + Raises: + Exception: if a provided input is invalid. + + Returns: + A list of all files, including inputs, that are needed to compile the given + inputs. + """ + deps = BuildDependenciesFromFiles(paths + inputs) + search_hash = BuildDependencyHashFromDependencies(deps) + result_list = [] + seen_list = [] + for input_file in inputs: + if IsNamespace(input_file): + namespace = re.search(ns_regex, input_file).group(1) + if namespace not in search_hash: + raise Exception('Invalid namespace (%s)' % namespace) + input_file = search_hash[namespace].filename + if not IsValidFile(input_file) or not IsJsFile(input_file): + raise Exception('Invalid file (%s)' % input_file) + seen_list.append(input_file) + file_handle = open(input_file, 'r') + try: + for line in file_handle: + if re.match(req_regex, line): + require = re.search(req_regex, line).group(1) + ResolveDependencies(require, search_hash, result_list, seen_list) + finally: + file_handle.close() + result_list.append(input_file) + + # All files depend on base.js, so put it first. + base_js_path = FindClosureBasePath(paths) + if base_js_path: + result_list.insert(0, base_js_path) + else: + logging.warning('Closure Library base.js not found.') + + return result_list + + +def FindClosureBasePath(paths): + """Given a list of file paths, return Closure base.js path, if any. + + Args: + paths: A list of paths. + + Returns: + The path to Closure's base.js file including filename, if found. + """ + + for path in paths: + pathname, filename = os.path.split(path) + + if filename == 'base.js': + f = open(path) + + is_base = False + + # Sanity check that this is the Closure base file. Check that this + # is where goog is defined. This is determined by the @provideGoog + # flag. + for line in f: + if '@provideGoog' in line: + is_base = True + break + + f.close() + + if is_base: + return path + +def ResolveDependencies(require, search_hash, result_list, seen_list): + """Takes a given requirement and resolves all of the dependencies for it. + + Description: + A given requirement may require other dependencies. This method + recursively resolves all dependencies for the given requirement. + + Raises: + Exception: when require does not exist in the search_hash. + + Args: + require: the namespace to resolve dependencies for. + search_hash: the data structure used for resolving dependencies. + result_list: a list of filenames that have been calculated as dependencies. + This variable is the output for this function. + seen_list: a list of filenames that have been 'seen'. This is required + for the dependency->dependant ordering. + """ + if require not in search_hash: + raise Exception('Missing provider for (%s)' % require) + + dep = search_hash[require] + if not dep.filename in seen_list: + seen_list.append(dep.filename) + for sub_require in dep.requires: + ResolveDependencies(sub_require, search_hash, result_list, seen_list) + result_list.append(dep.filename) + + +def GetDepsLine(dep, base_path): + """Returns a JS string for a dependency statement in the deps.js file. + + Args: + dep: The dependency that we're printing. + base_path: The path to Closure's base.js including filename. + """ + return 'goog.addDependency("%s", %s, %s);' % ( + GetRelpath(dep.filename, base_path), dep.provides, dep.requires) + + +def GetRelpath(path, start): + """Return a relative path to |path| from |start|.""" + # NOTE: Python 2.6 provides os.path.relpath, which has almost the same + # functionality as this function. Since we want to support 2.4, we have + # to implement it manually. :( + path_list = os.path.abspath(os.path.normpath(path)).split(os.sep) + start_list = os.path.abspath( + os.path.normpath(os.path.dirname(start))).split(os.sep) + + common_prefix_count = 0 + for i in range(0, min(len(path_list), len(start_list))): + if path_list[i] != start_list[i]: + break + common_prefix_count += 1 + + # Always use forward slashes, because this will get expanded to a url, + # not a file path. + return '/'.join(['..'] * (len(start_list) - common_prefix_count) + + path_list[common_prefix_count:]) + + +def PrintLine(msg, out): + out.write(msg) + out.write('\n') + + +def PrintDeps(source_paths, deps, out): + """Print out a deps.js file from a list of source paths. + + Args: + source_paths: Paths that we should generate dependency info for. + deps: Paths that provide dependency info. Their dependency info should + not appear in the deps file. + out: The output file. + + Returns: + True on success, false if it was unable to find the base path + to generate deps relative to. + """ + base_path = FindClosureBasePath(source_paths + deps) + if not base_path: + return False + + PrintLine('// This file was autogenerated by calcdeps.py', out) + excludesSet = set(deps) + + for dep in BuildDependenciesFromFiles(source_paths + deps): + if not dep.filename in excludesSet: + PrintLine(GetDepsLine(dep, base_path), out) + + return True + + +def PrintScript(source_paths, out): + for index, dep in enumerate(source_paths): + PrintLine('// Input %d' % index, out) + f = open(dep, 'r') + PrintLine(f.read(), out) + f.close() + + +def GetJavaVersion(): + """Returns the string for the current version of Java installed.""" + proc = subprocess.Popen(['java', '-version'], stderr=subprocess.PIPE) + proc.wait() + version_line = proc.stderr.read().splitlines()[0] + return version_regex.search(version_line).group() + + +def FilterByExcludes(options, files): + """Filters the given files by the exlusions specified at the command line. + + Args: + options: The flags to calcdeps. + files: The files to filter. + Returns: + A list of files. + """ + excludes = [] + if options.excludes: + excludes = ExpandDirectories(options.excludes) + + excludesSet = set(excludes) + return [i for i in files if not i in excludesSet] + + +def GetPathsFromOptions(options): + """Generates the path files from flag options. + + Args: + options: The flags to calcdeps. + Returns: + A list of files in the specified paths. (strings). + """ + + search_paths = options.paths + if not search_paths: + search_paths = ['.'] # Add default folder if no path is specified. + + search_paths = ExpandDirectories(search_paths) + return FilterByExcludes(options, search_paths) + + +def GetInputsFromOptions(options): + """Generates the inputs from flag options. + + Args: + options: The flags to calcdeps. + Returns: + A list of inputs (strings). + """ + inputs = options.inputs + if not inputs: # Parse stdin + logging.info('No inputs specified. Reading from stdin...') + inputs = filter(None, [line.strip('\n') for line in sys.stdin.readlines()]) + + logging.info('Scanning files...') + inputs = ExpandDirectories(inputs) + + return FilterByExcludes(options, inputs) + + +def Compile(compiler_jar_path, source_paths, out, flags=None): + """Prepares command-line call to Closure compiler. + + Args: + compiler_jar_path: Path to the Closure compiler .jar file. + source_paths: Source paths to build, in order. + flags: A list of additional flags to pass on to Closure compiler. + """ + args = ['java', '-jar', compiler_jar_path] + for path in source_paths: + args += ['--js', path] + + if flags: + args += flags + + logging.info('Compiling with the following command: %s', ' '.join(args)) + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + (stdoutdata, stderrdata) = proc.communicate() + if proc.returncode != 0: + logging.error('JavaScript compilation failed.') + sys.exit(1) + else: + out.write(stdoutdata) + + +def main(): + """The entrypoint for this script.""" + + logging.basicConfig(format='calcdeps.py: %(message)s', level=logging.INFO) + + usage = 'usage: %prog [options] arg' + parser = optparse.OptionParser(usage) + parser.add_option('-i', + '--input', + dest='inputs', + action='append', + help='The inputs to calculate dependencies for. Valid ' + 'values can be files, directories, or namespaces ' + '(ns:goog.net.XhrIo). Only relevant to "list" and ' + '"script" output.') + parser.add_option('-p', + '--path', + dest='paths', + action='append', + help='The paths that should be traversed to build the ' + 'dependencies.') + parser.add_option('-d', + '--dep', + dest='deps', + action='append', + help='Directories or files that should be traversed to ' + 'find required dependencies for the deps file. ' + 'Does not generate dependency information for names ' + 'provided by these files. Only useful in "deps" mode.') + parser.add_option('-e', + '--exclude', + dest='excludes', + action='append', + help='Files or directories to exclude from the --path ' + 'and --input flags') + parser.add_option('-o', + '--output_mode', + dest='output_mode', + action='store', + default='list', + help='The type of output to generate from this script. ' + 'Options are "list" for a list of filenames, "script" ' + 'for a single script containing the contents of all the ' + 'file, "deps" to generate a deps.js file for all ' + 'paths, or "compiled" to produce compiled output with ' + 'the Closure compiler.') + parser.add_option('-c', + '--compiler_jar', + dest='compiler_jar', + action='store', + help='The location of the Closure compiler .jar file.') + parser.add_option('-f', + '--compiler_flag', + '--compiler_flags', # for backwards compatability + dest='compiler_flags', + action='append', + help='Additional flag to pass to the Closure compiler. ' + 'May be specified multiple times to pass multiple flags.') + parser.add_option('--output_file', + dest='output_file', + action='store', + help=('If specified, write output to this path instead of ' + 'writing to standard output.')) + + (options, args) = parser.parse_args() + + search_paths = GetPathsFromOptions(options) + + if options.output_file: + out = open(options.output_file, 'w') + else: + out = sys.stdout + + if options.output_mode == 'deps': + result = PrintDeps(search_paths, ExpandDirectories(options.deps or []), out) + if not result: + logging.error('Could not find Closure Library in the specified paths') + sys.exit(1) + + return + + inputs = GetInputsFromOptions(options) + + logging.info('Finding Closure dependencies...') + deps = CalculateDependencies(search_paths, inputs) + output_mode = options.output_mode + + if output_mode == 'script': + PrintScript(deps, out) + elif output_mode == 'list': + # Just print out a dep per line + for dep in deps: + PrintLine(dep, out) + elif output_mode == 'compiled': + # Make sure a .jar is specified. + if not options.compiler_jar: + logging.error('--compiler_jar flag must be specified if --output is ' + '"compiled"') + sys.exit(1) + + # User friendly version check. + if distutils and not (distutils.version.LooseVersion(GetJavaVersion()) > + distutils.version.LooseVersion('1.6')): + logging.error('Closure Compiler requires Java 1.6 or higher.') + logging.error('Please visit http://www.java.com/getjava') + sys.exit(1) + + Compile(options.compiler_jar, deps, out, options.compiler_flags) + + else: + logging.error('Invalid value for --output flag.') + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/closure.el b/mapbox-gl/closure-library/closure/bin/labs/code/closure.el new file mode 100644 index 0000000000..394c483749 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/closure.el @@ -0,0 +1,39 @@ +;; Copyright 2013 The Closure Library Authors. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required `by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS-IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;; Closure JS code editing functions for emacs. + +;; Author: nnaze@google.com (Nathan Naze) + +;; Remember the path of this file, as we will base our paths on it. +(setq closure-el-path load-file-name) + +(defun closure-el-directory () + "Get the directory the closure.el file lives in." + (file-name-directory closure-el-path)) + +(defun closure-generate-jsdoc-path() + "The path of the generate_jsdoc.py script." + (concat (closure-el-directory) "generate_jsdoc.py")) + +(defun closure-insert-jsdoc () + "Insert JSDoc for the next function after the cursor." + (interactive) + (save-excursion ; Remembers cursor location + (call-process-region + (point) (point-max) + (closure-generate-jsdoc-path) + t t))) + +(provide 'closure) diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/closure_test.el b/mapbox-gl/closure-library/closure/bin/labs/code/closure_test.el new file mode 100644 index 0000000000..0a44e97dc6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/closure_test.el @@ -0,0 +1,31 @@ +;; Copyright 2013 The Closure Library Authors. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required `by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS-IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;; Unit tests for closure_test.el + +;; Author: nnaze@google.com (Nathan Naze) + +(require 'cl) + +(setq closure-test-directory (file-name-directory load-file-name)) + +(load-file (concat closure-test-directory "closure.el")) + +(setq closure-el-path "/test/path/closure.el") + +(assert (string= "/test/path/" (closure-el-directory))) + +(assert (string= + "/test/path/generate_jsdoc.py" + (closure-generate-jsdoc-path))) diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc.py b/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc.py new file mode 100644 index 0000000000..fc5ca45a48 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tool to insert JsDoc before a function. + +This script attempts to find the first function passed in to stdin, generate +JSDoc for it (with argument names and possibly return value), and inject +it in the string. This is intended to be used as a subprocess by editors +such as emacs and vi. +""" + +import re +import sys + + +# Matches a typical Closure-style function definition. +_FUNCTION_REGEX = re.compile(r""" +# Start of line +^ + +# Indentation +(?P[ ]*) + +# Identifier (handling split across line) +(?P\w+(\s*\.\s*\w+)*) + +# "= function" +\s* = \s* function \s* + +# opening paren +\( + +# Function arguments +(?P(?:\s|\w+|,)*) + +# closing paren +\) + +# opening bracket +\s* { + +""", re.MULTILINE | re.VERBOSE) + + +def _MatchFirstFunction(script): + """Match the first function seen in the script.""" + return _FUNCTION_REGEX.search(script) + + +def _ParseArgString(arg_string): + """Parse an argument string (inside parens) into parameter names.""" + for arg in arg_string.split(','): + arg = arg.strip() + if arg: + yield arg + + +def _ExtractFunctionBody(script, indentation=0): + """Attempt to return the function body.""" + + # Real extraction would require a token parser and state machines. + # We look for first bracket at the same level of indentation. + regex_str = r'{(.*?)^[ ]{%d}}' % indentation + + function_regex = re.compile(regex_str, re.MULTILINE | re.DOTALL) + match = function_regex.search(script) + if match: + return match.group(1) + + +def _ContainsReturnValue(function_body): + """Attempt to determine if the function body returns a value.""" + return_regex = re.compile(r'\breturn\b[^;]') + + # If this matches, we assume they're returning something. + return bool(return_regex.search(function_body)) + + +def _InsertString(original_string, inserted_string, index): + """Insert a string into another string at a given index.""" + return original_string[0:index] + inserted_string + original_string[index:] + + +def _GenerateJsDoc(args, return_val=False): + """Generate JSDoc for a function. + + Args: + args: A list of names of the argument. + return_val: Whether the function has a return value. + + Returns: + The JSDoc as a string. + """ + + lines = [] + lines.append('/**') + + lines += [' * @param {} %s' % arg for arg in args] + + if return_val: + lines.append(' * @return') + + lines.append(' */') + + return '\n'.join(lines) + '\n' + + +def _IndentString(source_string, indentation): + """Indent string some number of characters.""" + lines = [(indentation * ' ') + line + for line in source_string.splitlines(True)] + return ''.join(lines) + + +def InsertJsDoc(script): + """Attempt to insert JSDoc for the first seen function in the script. + + Args: + script: The script, as a string. + + Returns: + Returns the new string if function was found and JSDoc inserted. Otherwise + returns None. + """ + + match = _MatchFirstFunction(script) + if not match: + return + + # Add argument flags. + args_string = match.group('arguments') + args = _ParseArgString(args_string) + + start_index = match.start(0) + function_to_end = script[start_index:] + + lvalue_indentation = len(match.group('indentation')) + + return_val = False + function_body = _ExtractFunctionBody(function_to_end, lvalue_indentation) + if function_body: + return_val = _ContainsReturnValue(function_body) + + jsdoc = _GenerateJsDoc(args, return_val) + if lvalue_indentation: + jsdoc = _IndentString(jsdoc, lvalue_indentation) + + return _InsertString(script, jsdoc, start_index) + + +if __name__ == '__main__': + stdin_script = sys.stdin.read() + result = InsertJsDoc(stdin_script) + + if result: + sys.stdout.write(result) + else: + sys.stdout.write(stdin_script) diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc_test.py b/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc_test.py new file mode 100644 index 0000000000..f3aa20a9f3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/generate_jsdoc_test.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required `by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Unit test for generate_jsdoc.""" + +__author__ = 'nnaze@google.com (Nathan Naze)' + + +import re +import unittest + +import generate_jsdoc + + +class InsertJsDocTestCase(unittest.TestCase): + """Unit test for source. Tests the parser on a known source input.""" + + def testMatchFirstFunction(self): + match = generate_jsdoc._MatchFirstFunction(_TEST_SOURCE) + self.assertNotEqual(None, match) + self.assertEqual('aaa, bbb, ccc', match.group('arguments')) + + match = generate_jsdoc._MatchFirstFunction(_INDENTED_SOURCE) + self.assertNotEqual(None, match) + self.assertEqual('', match.group('arguments')) + + match = generate_jsdoc._MatchFirstFunction(_ODD_NEWLINES_SOURCE) + self.assertEquals('goog.\nfoo.\nbar\n.baz.\nqux', + match.group('identifier')) + + def testParseArgString(self): + self.assertEquals( + ['foo', 'bar', 'baz'], + list(generate_jsdoc._ParseArgString('foo, bar, baz'))) + + def testExtractFunctionBody(self): + self.assertEquals( + '\n // Function comments.\n return;\n', + generate_jsdoc._ExtractFunctionBody(_TEST_SOURCE)) + + self.assertEquals( + '\n var bar = 3;\n return true;\n', + generate_jsdoc._ExtractFunctionBody(_INDENTED_SOURCE, 2)) + + def testContainsValueReturn(self): + self.assertTrue(generate_jsdoc._ContainsReturnValue(_INDENTED_SOURCE)) + self.assertFalse(generate_jsdoc._ContainsReturnValue(_TEST_SOURCE)) + + def testInsertString(self): + self.assertEquals( + 'abc123def', + generate_jsdoc._InsertString('abcdef', '123', 3)) + + def testInsertJsDoc(self): + self.assertEquals( + _EXPECTED_INDENTED_SOURCE, + generate_jsdoc.InsertJsDoc(_INDENTED_SOURCE)) + + self.assertEquals( + _EXPECTED_TEST_SOURCE, + generate_jsdoc.InsertJsDoc(_TEST_SOURCE)) + + self.assertEquals( + _EXPECTED_ODD_NEWLINES_SOURCE, + generate_jsdoc.InsertJsDoc(_ODD_NEWLINES_SOURCE)) + + +_INDENTED_SOURCE = """\ + boo.foo.woo = function() { + var bar = 3; + return true; + }; +""" + +_EXPECTED_INDENTED_SOURCE = """\ + /** + * @return + */ + boo.foo.woo = function() { + var bar = 3; + return true; + }; +""" + + +_TEST_SOURCE = """\ + +// Random comment. + +goog.foo.bar = function (aaa, bbb, ccc) { + // Function comments. + return; +}; +""" + +_EXPECTED_TEST_SOURCE = """\ + +// Random comment. + +/** + * @param {} aaa + * @param {} bbb + * @param {} ccc + */ +goog.foo.bar = function (aaa, bbb, ccc) { + // Function comments. + return; +}; +""" + +_ODD_NEWLINES_SOURCE = """\ +goog. +foo. +bar +.baz. +qux + = + + function + +(aaa, + +bbb, ccc) { + // Function comments. + return; +}; +""" + +_EXPECTED_ODD_NEWLINES_SOURCE = """\ +/** + * @param {} aaa + * @param {} bbb + * @param {} ccc + */ +goog. +foo. +bar +.baz. +qux + = + + function + +(aaa, + +bbb, ccc) { + // Function comments. + return; +}; +""" + +if __name__ == '__main__': + unittest.main() diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/run_el_tests.sh b/mapbox-gl/closure-library/closure/bin/labs/code/run_el_tests.sh new file mode 100644 index 0000000000..ded2a74a11 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/run_el_tests.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Copyright 2013 The Closure Library Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Wraps the unit tests not using the Google framework so they may be run +# on the continuous build. +# +# Author: nnaze@google.com (Nathan Naze) +# +# Wraps the unit tests not using the Google framework so they may be run +# run on the continuous build. + +set -e + +source googletest.sh || exit 1 + +CLOSURE_SRCDIR=$TEST_SRCDIR/google3/javascript/closure/labs/bin/code/ + +emacs --script $CLOSURE_SRCDIR/closure_test.el diff --git a/mapbox-gl/closure-library/closure/bin/labs/code/run_py_tests.sh b/mapbox-gl/closure-library/closure/bin/labs/code/run_py_tests.sh new file mode 100644 index 0000000000..d075ebba1f --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/labs/code/run_py_tests.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright 2013 The Closure Library Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Wraps the unit tests not using the Google framework so they may be run +# on the continuous build. + +set -e + +source googletest.sh || exit 1 + +CLOSURE_SRCDIR=$TEST_SRCDIR/google3/javascript/closure/labs/bin/code/ + +PYTHONPATH=$CLOSURE_SRCDIR + +$CLOSURE_SRCDIR/generate_jsdoc_test.py diff --git a/mapbox-gl/closure-library/closure/bin/logos/logo.svg b/mapbox-gl/closure-library/closure/bin/logos/logo.svg new file mode 100644 index 0000000000..2cb6b88a31 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/logos/logo.svg @@ -0,0 +1,25 @@ + + + + + Closure Logo + + + + + diff --git a/mapbox-gl/closure-library/closure/bin/logos/logoandlabel.svg b/mapbox-gl/closure-library/closure/bin/logos/logoandlabel.svg new file mode 100644 index 0000000000..69c2d6849b --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/logos/logoandlabel.svg @@ -0,0 +1,25 @@ + + + + + + Closure + diff --git a/mapbox-gl/closure-library/closure/bin/scopify.py b/mapbox-gl/closure-library/closure/bin/scopify.py new file mode 100644 index 0000000000..d8057efbc9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/bin/scopify.py @@ -0,0 +1,221 @@ +#!/usr/bin/python +# +# Copyright 2010 The Closure Library Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS-IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Automatically converts codebases over to goog.scope. + +Usage: +cd path/to/my/dir; +../../../../javascript/closure/bin/scopify.py + +Scans every file in this directory, recursively. Looks for existing +goog.scope calls, and goog.require'd symbols. If it makes sense to +generate a goog.scope call for the file, then we will do so, and +try to auto-generate some aliases based on the goog.require'd symbols. + +Known Issues: + + When a file is goog.scope'd, the file contents will be indented +2. + This may put some lines over 80 chars. These will need to be fixed manually. + + We will only try to create aliases for capitalized names. We do not check + to see if those names will conflict with any existing locals. + + This creates merge conflicts for every line of every outstanding change. + If you intend to run this on your codebase, make sure your team members + know. Better yet, send them this script so that they can scopify their + outstanding changes and "accept theirs". + + When an alias is "captured", it can no longer be stubbed out for testing. + Run your tests. + +""" + +__author__ = 'nicksantos@google.com (Nick Santos)' + +import os.path +import re +import sys + +REQUIRES_RE = re.compile(r"goog.require\('([^']*)'\)") + +# Edit this manually if you want something to "always" be aliased. +# TODO(nicksantos): Add a flag for this. +DEFAULT_ALIASES = {} + +def Transform(lines): + """Converts the contents of a file into javascript that uses goog.scope. + + Arguments: + lines: A list of strings, corresponding to each line of the file. + Returns: + A new list of strings, or None if the file was not modified. + """ + requires = [] + + # Do an initial scan to be sure that this file can be processed. + for line in lines: + # Skip this file if it has already been scopified. + if line.find('goog.scope') != -1: + return None + + # If there are any global vars or functions, then we also have + # to skip the whole file. We might be able to deal with this + # more elegantly. + if line.find('var ') == 0 or line.find('function ') == 0: + return None + + for match in REQUIRES_RE.finditer(line): + requires.append(match.group(1)) + + if len(requires) == 0: + return None + + # Backwards-sort the requires, so that when one is a substring of another, + # we match the longer one first. + for val in DEFAULT_ALIASES.values(): + if requires.count(val) == 0: + requires.append(val) + + requires.sort() + requires.reverse() + + # Generate a map of requires to their aliases + aliases_to_globals = DEFAULT_ALIASES.copy() + for req in requires: + index = req.rfind('.') + if index == -1: + alias = req + else: + alias = req[(index + 1):] + + # Don't scopify lowercase namespaces, because they may conflict with + # local variables. + if alias[0].isupper(): + aliases_to_globals[alias] = req + + aliases_to_matchers = {} + globals_to_aliases = {} + for alias, symbol in aliases_to_globals.items(): + globals_to_aliases[symbol] = alias + aliases_to_matchers[alias] = re.compile('\\b%s\\b' % symbol) + + # Insert a goog.scope that aliases all required symbols. + result = [] + + START = 0 + SEEN_REQUIRES = 1 + IN_SCOPE = 2 + + mode = START + aliases_used = set() + insertion_index = None + num_blank_lines = 0 + for line in lines: + if mode == START: + result.append(line) + + if re.search(REQUIRES_RE, line): + mode = SEEN_REQUIRES + + elif mode == SEEN_REQUIRES: + if (line and + not re.search(REQUIRES_RE, line) and + not line.isspace()): + # There should be two blank lines before goog.scope + result += ['\n'] * 2 + result.append('goog.scope(function() {\n') + insertion_index = len(result) + result += ['\n'] * num_blank_lines + mode = IN_SCOPE + elif line.isspace(): + # Keep track of the number of blank lines before each block of code so + # that we can move them after the goog.scope line if necessary. + num_blank_lines += 1 + else: + # Print the blank lines we saw before this code block + result += ['\n'] * num_blank_lines + num_blank_lines = 0 + result.append(line) + + if mode == IN_SCOPE: + for symbol in requires: + if not symbol in globals_to_aliases: + continue + + alias = globals_to_aliases[symbol] + matcher = aliases_to_matchers[alias] + for match in matcher.finditer(line): + # Check to make sure we're not in a string. + # We do this by being as conservative as possible: + # if there are any quote or double quote characters + # before the symbol on this line, then bail out. + before_symbol = line[:match.start(0)] + if before_symbol.count('"') > 0 or before_symbol.count("'") > 0: + continue + + line = line.replace(match.group(0), alias) + aliases_used.add(alias) + + if line.isspace(): + # Truncate all-whitespace lines + result.append('\n') + else: + result.append(line) + + if len(aliases_used): + aliases_used = [alias for alias in aliases_used] + aliases_used.sort() + aliases_used.reverse() + for alias in aliases_used: + symbol = aliases_to_globals[alias] + result.insert(insertion_index, + 'var %s = %s;\n' % (alias, symbol)) + result.append('}); // goog.scope\n') + return result + else: + return None + +def TransformFileAt(path): + """Converts a file into javascript that uses goog.scope. + + Arguments: + path: A path to a file. + """ + f = open(path) + lines = Transform(f.readlines()) + if lines: + f = open(path, 'w') + for l in lines: + f.write(l) + f.close() + +if __name__ == '__main__': + args = sys.argv[1:] + if not len(args): + args = '.' + + for file_name in args: + if os.path.isdir(file_name): + for root, dirs, files in os.walk(file_name): + for name in files: + if name.endswith('.js') and \ + not os.path.islink(os.path.join(root, name)): + TransformFileAt(os.path.join(root, name)) + else: + if file_name.endswith('.js') and \ + not os.path.islink(file_name): + TransformFileAt(file_name) diff --git a/mapbox-gl/closure-library/closure/css/inlay/g-base.css b/mapbox-gl/closure-library/closure/css/inlay/g-base.css new file mode 100644 index 0000000000..3db72d3f82 --- /dev/null +++ b/mapbox-gl/closure-library/closure/css/inlay/g-base.css @@ -0,0 +1,82 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * This is the minimum CSS required to use the markup/classname patterns. + * @author elsigh@google.com (Lindsey Simon) + * @author ddiaz@google.com (Dustin Diaz) + */ + +/** + * Document container designed for fluid width scaling. + * Alternative g-doc- fixed-width classes are in gui-fixed.css. + */ +.g-doc { + width: 100%; + text-align: left; +} + +/** + * g-section fundamentally has to clear floats. There are many ways to do this. + * This technique is nice because it doesn't rely on overflow: hidden, which + * has the potential to hide your content in situations where a fixed size + * node takes up too much space (like a big table, or a text input or image. + * Works in Webkit, IE8, and FF3. + */ +.g-section { + width: 100%; + vertical-align: top; + display: inline-block; +} + +/** + * IE7-only hack. Nicely IE7 will clear floats with just block display + * and hasLayout. + */ +*:first-child+html .g-section { + display: block; +} + +/** + * IE6 cannot hang with overflow: visible. If we use the IE7 display block + * trick in IE6 we get severe float drop in nested grids. + */ +* html .g-section { + overflow: hidden; +} + +/* FF2 can't actually hang with overflow: visible. */ +@-moz-document url-prefix() { + .g-section { + overflow: hidden; + } +} + +/** + * FF3 now needs to be reset after the previous block which affects it as well. + * We target the tt element in this hack because no one uses it. + */ +@-moz-document url-prefix() { + .g-section,tt:default { + overflow: visible; + } +} + +/* Forces "hasLayout" fixing a gamut of bugs in <= IE7. */ +.g-section, +.g-unit { + zoom: 1; +} + +/* Used for splitting a template's units text-alignment to the outer edges. */ +.g-split .g-unit { + text-align: right; +} +.g-split .g-first { + text-align: left; +} diff --git a/mapbox-gl/closure-library/closure/css/inlay/g-fixed.css b/mapbox-gl/closure-library/closure/css/inlay/g-fixed.css new file mode 100644 index 0000000000..52ef5d160a --- /dev/null +++ b/mapbox-gl/closure-library/closure/css/inlay/g-fixed.css @@ -0,0 +1,173 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * Fixed templates + * @author ddiaz@google.com (Dustin Diaz) + * @author elsigh@google.com (Lindsey Simon) + * @fileoverview + * The nature of these templates is to have one unit be a fixed width + * and the supplementary unit to take up the rest of the width + * of its parents' container. + * + * Sample Usage: +
+
+

+ Lorem Ipsum... +

+
+
+

+ Lorem Ipsum... +

+
+
+ */ + +/* Document container designed for 1024x768 */ +/* TODO(ux-webdev): convert this to a straight px value - em assumes reset. */ +.g-doc-1024 { + width: 73.074em; + min-width: 950px; /* min-width doesn't work in IE6 */ + margin: 0 auto; + text-align: left; +} +/* IE 6 */ +* html .g-doc-1024 { + width: 71.313em; +} +/* IE 7 */ +*+html .g-doc-1024 { + width: 71.313em; +} + +/* Document container designed for 800x600 */ +/* TODO(ux-webdev): convert this to a straight px value - em assumes reset. */ +.g-doc-800 { + width: 57.69em; + min-width: 750px; /* min-width doesn't work in IE6 */ + margin: 0 auto; + text-align: left; +} +/* IE 6 */ +* html .g-doc-800 { + width: 56.3em; +} +/* IE 7 */ +*+html .g-doc-800 { + width: 56.3em; +} + +/* 160px */ +.g-tpl-160 .g-unit, +.g-unit .g-tpl-160 .g-unit, +.g-unit .g-unit .g-tpl-160 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-160 .g-unit { + margin: 0 0 0 160px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-160 .g-first, +.g-unit .g-unit .g-tpl-160 .g-first, +.g-unit .g-tpl-160 .g-first, +.g-tpl-160 .g-first { + margin: 0; + width: 160px; + float: left; +} + +/* 160px alt */ +.g-tpl-160-alt .g-unit, +.g-unit .g-tpl-160-alt .g-unit, +.g-unit .g-unit .g-tpl-160-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-160-alt .g-unit { + margin: 0 160px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-160-alt .g-first, +.g-unit .g-unit .g-tpl-160-alt .g-first, +.g-unit .g-tpl-160-alt .g-first, +.g-tpl-160-alt .g-first { + margin: 0; + width: 160px; + float: right; +} + +/* 180px */ +.g-tpl-180 .g-unit, +.g-unit .g-tpl-180 .g-unit, +.g-unit .g-unit .g-tpl-180 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-180 .g-unit { + margin: 0 0 0 180px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-180 .g-first, +.g-unit .g-unit .g-tpl-180 .g-first, +.g-unit .g-tpl-180 .g-first, +.g-tpl-180 .g-first { + margin: 0; + width: 180px; + float: left; +} + +/* 180px alt */ +.g-tpl-180-alt .g-unit, +.g-unit .g-tpl-180-alt .g-unit, +.g-unit .g-unit .g-tpl-180-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-180-alt .g-unit { + margin: 0 180px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-180-alt .g-first, +.g-unit .g-unit .g-tpl-180-alt .g-first, +.g-unit .g-tpl-180-alt .g-first, +.g-tpl-180-alt .g-first { + margin: 0; + width: 180px; + float: right; +} + +/* 300px */ +.g-tpl-300 .g-unit, +.g-unit .g-tpl-300 .g-unit, +.g-unit .g-unit .g-tpl-300 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-300 .g-unit { + margin: 0 0 0 300px; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-300 .g-first, +.g-unit .g-unit .g-tpl-300 .g-first, +.g-unit .g-tpl-300 .g-first, +.g-tpl-300 .g-first { + margin: 0; + width: 300px; + float: left; +} + +/* 300px alt */ +.g-tpl-300-alt .g-unit, +.g-unit .g-tpl-300-alt .g-unit, +.g-unit .g-unit .g-tpl-300-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-300-alt .g-unit { + margin: 0 300px 0 0; + width: auto; + float: none; +} +.g-unit .g-unit .g-unit .g-tpl-300-alt .g-first, +.g-unit .g-unit .g-tpl-300-alt .g-first, +.g-unit .g-tpl-300-alt .g-first, +.g-tpl-300-alt .g-first { + margin: 0; + width: 300px; + float: right; +} \ No newline at end of file diff --git a/mapbox-gl/closure-library/closure/css/inlay/g-ratio.css b/mapbox-gl/closure-library/closure/css/inlay/g-ratio.css new file mode 100644 index 0000000000..6017ec0863 --- /dev/null +++ b/mapbox-gl/closure-library/closure/css/inlay/g-ratio.css @@ -0,0 +1,253 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * CSS Inlay + * Percentage based templates + * @author ddiaz@google.com (Dustin Diaz) + * @author elsigh@google.com (Lindsey Simon) + * @fileoverview + * The first ten templates are described using the following convention: + * tpl-LEFT%-RIGHT% and tpl-LEFT%-RIGHT%-alt, where alt switches render order. + * + * The rationale for the percentage values are pretty fascinating. + * Three nine's are needed for a miminal affordance in the gap between units + * for Opera, while two for IE 5.5 and down. + * Straight percentages seem to work fine otherwise, but the values here test + * well cross-browser. + * + * Sample Usage: +
+
+

+ Lorem Ipsum... +

+
+
+

+ Lorem Ipsum... +

+
+
+ */ + +/* 25/75 */ +.g-tpl-25-75 .g-unit, +.g-unit .g-tpl-25-75 .g-unit, +.g-unit .g-unit .g-tpl-25-75 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-25-75 .g-unit { + width: 74.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-25-75 .g-first, +.g-unit .g-unit .g-tpl-25-75 .g-first, +.g-unit .g-tpl-25-75 .g-first, +.g-tpl-25-75 .g-first { + width: 24.999%; + float: left; + margin: 0; +} + +/* 25/75-alt */ +.g-tpl-25-75-alt .g-unit, +.g-unit .g-tpl-25-75-alt .g-unit, +.g-unit .g-unit .g-tpl-25-75-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-unit { + width: 24.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-25-75-alt .g-first, +.g-unit .g-unit .g-tpl-25-75-alt .g-first, +.g-unit .g-tpl-25-75-alt .g-first, +.g-tpl-25-75-alt .g-first { + width: 74.999%; + float: right; + margin: 0; +} + +/* 75/25 */ +.g-tpl-75-25 .g-unit, +.g-unit .g-tpl-75-25 .g-unit, +.g-unit .g-unit .g-tpl-75-25 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-75-25 .g-unit { + width: 24.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-75-25 .g-first, +.g-unit .g-unit .g-tpl-75-25 .g-first, +.g-unit .g-tpl-75-25 .g-first, +.g-tpl-75-25 .g-first { + width: 74.999%; + float: left; + margin: 0; +} + +/* 75/25-alt */ +.g-tpl-75-25-alt .g-unit, +.g-unit .g-tpl-75-25-alt .g-unit, +.g-unit .g-unit .g-tpl-75-25-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-unit { + width: 74.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-75-25-alt .g-first, +.g-unit .g-unit .g-tpl-75-25-alt .g-first, +.g-unit .g-tpl-75-25-alt .g-first, +.g-tpl-75-25-alt .g-first { + width: 24.999%; + float: right; + margin: 0; +} + +/* 33/67 */ +.g-tpl-33-67 .g-unit, +.g-unit .g-tpl-33-67 .g-unit, +.g-unit .g-unit .g-tpl-33-67 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-33-67 .g-unit { + width: 66.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-33-67 .g-first, +.g-unit .g-unit .g-tpl-33-67 .g-first, +.g-unit .g-tpl-33-67 .g-first, +.g-tpl-33-67 .g-first { + width: 32.999%; + float: left; + margin: 0; +} + +/* 33/67-alt */ +.g-tpl-33-67-alt .g-unit, +.g-unit .g-tpl-33-67-alt .g-unit, +.g-unit .g-unit .g-tpl-33-67-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-unit { + width: 32.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-33-67-alt .g-first, +.g-unit .g-unit .g-tpl-33-67-alt .g-first, +.g-unit .g-tpl-33-67-alt .g-first, +.g-tpl-33-67-alt .g-first { + width: 66.999%; + float: right; + margin: 0; +} +/* 67/33 */ +.g-tpl-67-33 .g-unit, +.g-unit .g-tpl-67-33 .g-unit, +.g-unit .g-unit .g-tpl-67-33 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-67-33 .g-unit { + width: 32.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-67-33 .g-first, +.g-unit .g-unit .g-tpl-67-33 .g-first, +.g-unit .g-tpl-67-33 .g-first, +.g-tpl-67-33 .g-first { + width: 66.999%; + float: left; + margin: 0; +} + +/* 67/33-alt */ +.g-tpl-67-33-alt .g-unit, +.g-unit .g-tpl-67-33-alt .g-unit, +.g-unit .g-unit .g-tpl-67-33-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-unit { + width: 66.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-67-33-alt .g-first, +.g-unit .g-unit .g-tpl-67-33-alt .g-first, +.g-unit .g-tpl-67-33-alt .g-first, +.g-tpl-67-33-alt .g-first { + width: 32.999%; + float: right; + margin: 0; +} + +/* 50/50 */ +.g-tpl-50-50 .g-unit, +.g-unit .g-tpl-50-50 .g-unit, +.g-unit .g-unit .g-tpl-50-50 .g-unit, +.g-unit .g-unit .g-unit .g-tpl-50-50 .g-unit { + width: 49.999%; + float: right; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-50-50 .g-first, +.g-unit .g-unit .g-tpl-50-50 .g-first, +.g-unit .g-tpl-50-50 .g-first, +.g-tpl-50-50 .g-first { + width: 49.999%; + float: left; + margin: 0; +} + +/* 50/50-alt */ +.g-tpl-50-50-alt .g-unit, +.g-unit .g-tpl-50-50-alt .g-unit, +.g-unit .g-unit .g-tpl-50-50-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-unit { + width: 49.999%; + float: left; + margin: 0; +} +.g-unit .g-unit .g-unit .g-tpl-50-50-alt .g-first, +.g-unit .g-unit .g-tpl-50-50-alt .g-first, +.g-unit .g-tpl-50-50-alt .g-first, +.g-tpl-50-50-alt .g-first { + width: 49.999%; + float: right; + margin: 0; +} + +/** + * Nest templates contain floating g-units. + * For these, width needs to be reset from the 100% for inline-block + * to auto. This fixes an issue with horizontal scrollbars. + */ +.g-tpl-nest { + width: auto; +} +/** + * Making any g-sections inside of g-tpl-nests display inline instead + * of display block solves an issue where inner sections add up incrementally + * their widths to set the width of the outer g-unit. This causes all kinds of + * problems with float-drops and display:inline fixes this. + */ +.g-tpl-nest .g-section { + display: inline; +} + +/* g-tpl-nest for multi unit nesting (float left), say for a menu. */ +.g-tpl-nest .g-unit, +.g-unit .g-tpl-nest .g-unit, +.g-unit .g-unit .g-tpl-nest .g-unit, +.g-unit .g-unit .g-unit .g-tpl-nest .g-unit { + float: left; + width: auto; + margin: 0; +} + +/* g-tpl-nest-alt for multi unit nesting (float right), say for a menu. */ +.g-tpl-nest-alt .g-unit, +.g-unit .g-tpl-nest-alt .g-unit, +.g-unit .g-unit .g-tpl-nest-alt .g-unit, +.g-unit .g-unit .g-unit .g-tpl-nest-alt .g-unit { + float: right; + width: auto; + margin: 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer.js new file mode 100644 index 0000000000..5f8ed2776c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer.js @@ -0,0 +1,125 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Announcer that allows messages to be spoken by assistive + * technologies. + */ + +goog.provide('goog.a11y.aria.Announcer'); + +goog.require('goog.Disposable'); +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.LivePriority'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.object'); + + + +/** + * Class that allows messages to be spoken by assistive technologies that the + * user may have active. + * + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.a11y.aria.Announcer = function(opt_domHelper) { + goog.a11y.aria.Announcer.base(this, 'constructor'); + + /** + * @type {goog.dom.DomHelper} + * @private + */ + this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); + + /** + * Map of priority to live region elements to use for communicating updates. + * Elements are created on demand. + * @type {Object} + * @private + */ + this.liveRegions_ = {}; +}; +goog.inherits(goog.a11y.aria.Announcer, goog.Disposable); + + +/** @override */ +goog.a11y.aria.Announcer.prototype.disposeInternal = function() { + goog.object.forEach( + this.liveRegions_, this.domHelper_.removeNode, this.domHelper_); + this.liveRegions_ = null; + this.domHelper_ = null; + goog.a11y.aria.Announcer.base(this, 'disposeInternal'); +}; + + +/** + * Announce a message to be read by any assistive technologies the user may + * have active. + * @param {string} message The message to announce to screen readers. + * @param {goog.a11y.aria.LivePriority=} opt_priority The priority of the + * message. Defaults to POLITE. + */ +goog.a11y.aria.Announcer.prototype.say = function(message, opt_priority) { + var priority = opt_priority || goog.a11y.aria.LivePriority.POLITE; + var liveRegion = this.getLiveRegion_(priority); + goog.dom.setTextContent(liveRegion, message); +}; + + +/** + * Returns an aria-live region that can be used to communicate announcements. + * @param {!goog.a11y.aria.LivePriority} priority The required priority. + * @return {!Element} A live region of the requested priority. + * @private + */ +goog.a11y.aria.Announcer.prototype.getLiveRegion_ = function(priority) { + // Removing the previous live region ensures that the newest message is always + // read without delay, even if the message is an exact duplicate of the prior + // message. + this.removeLiveRegion_(priority); + + var liveRegion = this.domHelper_.createElement('div'); + // Note that IE has a habit of declaring things that aren't display:none as + // invisible to third-party tools like JAWs, so we can't just use height:0. + liveRegion.style.position = 'absolute'; + liveRegion.style.top = '-1000px'; + liveRegion.style.height = '1px'; + liveRegion.style.overflow = 'hidden'; + goog.a11y.aria.setState(liveRegion, goog.a11y.aria.State.LIVE, + priority); + goog.a11y.aria.setState(liveRegion, goog.a11y.aria.State.ATOMIC, + 'true'); + this.domHelper_.getDocument().body.appendChild(liveRegion); + this.liveRegions_[priority] = liveRegion; + return liveRegion; +}; + + +/** + * Removes any previous live region that was used to communicate announcements. + * @param {!goog.a11y.aria.LivePriority} priority The required priority. + * @private + */ +goog.a11y.aria.Announcer.prototype.removeLiveRegion_ = function(priority) { + var liveRegion = this.liveRegions_[priority]; + if (liveRegion) { + this.domHelper_.removeNode(liveRegion); + delete this.liveRegions_[priority]; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.html b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.html new file mode 100644 index 0000000000..095fe46f8c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.html @@ -0,0 +1,27 @@ + + + + + + + + Closure Unit Tests - goog.a11y.aria announcer + + + + + +
+
+ + diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.js new file mode 100644 index 0000000000..e2dd860642 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/announcer_test.js @@ -0,0 +1,129 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.a11y.aria.AnnouncerTest'); +goog.setTestOnly('goog.a11y.aria.AnnouncerTest'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Announcer'); +goog.require('goog.a11y.aria.LivePriority'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.iframe'); +goog.require('goog.testing.jsunit'); + +var sandbox; +var someDiv; +var someSpan; + +function setUp() { + sandbox = goog.dom.getElement('sandbox'); + someDiv = goog.dom.createDom('div', {id: 'someDiv'}, 'DIV'); + someSpan = goog.dom.createDom('span', {id: 'someSpan'}, 'SPAN'); + sandbox.appendChild(someDiv); + someDiv.appendChild(someSpan); +} + +function tearDown() { + sandbox.innerHTML = ''; + someDiv = null; + someSpan = null; +} + +function testAnnouncerAndDispose() { + var text = 'test content'; + var announcer = new goog.a11y.aria.Announcer(goog.dom.getDomHelper()); + announcer.say(text); + checkLiveRegionContains(text, 'polite'); + goog.dispose(announcer); +} + +function testAnnouncerTwice() { + var text = 'test content1'; + var text2 = 'test content2'; + var announcer = new goog.a11y.aria.Announcer(goog.dom.getDomHelper()); + announcer.say(text); + announcer.say(text2); + checkLiveRegionContains(text2, 'polite'); + goog.dispose(announcer); +} + +function testAnnouncerTwiceSameMessage() { + var text = 'test content'; + var announcer = new goog.a11y.aria.Announcer(goog.dom.getDomHelper()); + announcer.say(text); + var firstLiveRegion = getLiveRegion('polite'); + announcer.say(text); + var secondLiveRegion = getLiveRegion('polite'); + assertNotEquals(firstLiveRegion, secondLiveRegion); + checkLiveRegionContains(text, 'polite'); + goog.dispose(announcer); +} + +function testAnnouncerAssertive() { + var text = 'test content'; + var announcer = new goog.a11y.aria.Announcer(goog.dom.getDomHelper()); + announcer.say(text, goog.a11y.aria.LivePriority.ASSERTIVE); + checkLiveRegionContains(text, 'assertive'); + goog.dispose(announcer); +} + +function testAnnouncerInIframe() { + var text = 'test content'; + var frame = goog.dom.iframe.createWithContent(sandbox); + var helper = goog.dom.getDomHelper( + goog.dom.getFrameContentDocument(frame).body); + var announcer = new goog.a11y.aria.Announcer(helper); + announcer.say(text, 'polite', helper); + checkLiveRegionContains(text, 'polite', helper); + goog.dispose(announcer); +} + +function testAnnouncerWithAriaHidden() { + var text = 'test content1'; + var text2 = 'test content2'; + var announcer = new goog.a11y.aria.Announcer(goog.dom.getDomHelper()); + announcer.say(text); + // Set aria-hidden attribute on the live region (simulates a modal dialog + // being opened). + var liveRegion = getLiveRegion('polite'); + goog.a11y.aria.setState(liveRegion, goog.a11y.aria.State.HIDDEN, true); + + // Announce a new message and make sure that the aria-hidden was removed. + announcer.say(text2); + checkLiveRegionContains(text2, 'polite'); + liveRegion = getLiveRegion('polite'); + assertEquals('', + goog.a11y.aria.getState(liveRegion, goog.a11y.aria.State.HIDDEN)); + goog.dispose(announcer); +} + +function getLiveRegion(priority, opt_domHelper) { + var dom = opt_domHelper || goog.dom.getDomHelper(); + var divs = dom.getElementsByTagNameAndClass('div', null); + var liveRegions = []; + goog.array.forEach(divs, function(div) { + if (goog.a11y.aria.getState(div, 'live') == priority) { + liveRegions.push(div); + } + }); + assertEquals(1, liveRegions.length); + return liveRegions[0]; +} + +function checkLiveRegionContains(text, priority, opt_domHelper) { + var liveRegion = getLiveRegion(priority, opt_domHelper); + assertEquals(text, goog.dom.getTextContent(liveRegion)); +} diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/aria.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria.js new file mode 100644 index 0000000000..1220d23828 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria.js @@ -0,0 +1,386 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Utilities for adding, removing and setting ARIA roles and + * states as defined by W3C ARIA standard: http://www.w3.org/TR/wai-aria/ + * All modern browsers have some form of ARIA support, so no browser checks are + * performed when adding ARIA to components. + * + */ + +goog.provide('goog.a11y.aria'); + +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.a11y.aria.datatables'); +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.object'); +goog.require('goog.string'); + + +/** + * ARIA states/properties prefix. + * @private + */ +goog.a11y.aria.ARIA_PREFIX_ = 'aria-'; + + +/** + * ARIA role attribute. + * @private + */ +goog.a11y.aria.ROLE_ATTRIBUTE_ = 'role'; + + +/** + * A list of tag names for which we don't need to set ARIA role and states + * because they have well supported semantics for screen readers or because + * they don't contain content to be made accessible. + * @private + */ +goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_ = [ + goog.dom.TagName.A, + goog.dom.TagName.AREA, + goog.dom.TagName.BUTTON, + goog.dom.TagName.HEAD, + goog.dom.TagName.INPUT, + goog.dom.TagName.LINK, + goog.dom.TagName.MENU, + goog.dom.TagName.META, + goog.dom.TagName.OPTGROUP, + goog.dom.TagName.OPTION, + goog.dom.TagName.PROGRESS, + goog.dom.TagName.STYLE, + goog.dom.TagName.SELECT, + goog.dom.TagName.SOURCE, + goog.dom.TagName.TEXTAREA, + goog.dom.TagName.TITLE, + goog.dom.TagName.TRACK +]; + + +/** + * Sets the role of an element. If the roleName is + * empty string or null, the role for the element is removed. + * We encourage clients to call the goog.a11y.aria.removeRole + * method instead of setting null and empty string values. + * Special handling for this case is added to ensure + * backword compatibility with existing code. + * + * @param {!Element} element DOM node to set role of. + * @param {!goog.a11y.aria.Role|string} roleName role name(s). + */ +goog.a11y.aria.setRole = function(element, roleName) { + if (!roleName) { + // Setting the ARIA role to empty string is not allowed + // by the ARIA standard. + goog.a11y.aria.removeRole(element); + } else { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.assert(goog.object.containsValue( + goog.a11y.aria.Role, roleName), 'No such ARIA role ' + roleName); + } + element.setAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_, roleName); + } +}; + + +/** + * Gets role of an element. + * @param {!Element} element DOM element to get role of. + * @return {goog.a11y.aria.Role} ARIA Role name. + */ +goog.a11y.aria.getRole = function(element) { + var role = element.getAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_); + return /** @type {goog.a11y.aria.Role} */ (role) || null; +}; + + +/** + * Removes role of an element. + * @param {!Element} element DOM element to remove the role from. + */ +goog.a11y.aria.removeRole = function(element) { + element.removeAttribute(goog.a11y.aria.ROLE_ATTRIBUTE_); +}; + + +/** + * Sets the state or property of an element. + * @param {!Element} element DOM node where we set state. + * @param {!(goog.a11y.aria.State|string)} stateName State attribute being set. + * Automatically adds prefix 'aria-' to the state name if the attribute is + * not an extra attribute. + * @param {string|boolean|number|!Array} value Value + * for the state attribute. + */ +goog.a11y.aria.setState = function(element, stateName, value) { + if (goog.isArray(value)) { + value = value.join(' '); + } + var attrStateName = goog.a11y.aria.getAriaAttributeName_(stateName); + if (value === '' || value == undefined) { + var defaultValueMap = goog.a11y.aria.datatables.getDefaultValuesMap(); + // Work around for browsers that don't properly support ARIA. + // According to the ARIA W3C standard, user agents should allow + // setting empty value which results in setting the default value + // for the ARIA state if such exists. The exact text from the ARIA W3C + // standard (http://www.w3.org/TR/wai-aria/states_and_properties): + // "When a value is indicated as the default, the user agent + // MUST follow the behavior prescribed by this value when the state or + // property is empty or undefined." + // The defaultValueMap contains the default values for the ARIA states + // and has as a key the goog.a11y.aria.State constant for the state. + if (stateName in defaultValueMap) { + element.setAttribute(attrStateName, defaultValueMap[stateName]); + } else { + element.removeAttribute(attrStateName); + } + } else { + element.setAttribute(attrStateName, value); + } +}; + + +/** + * Toggles the ARIA attribute of an element. + * Meant for attributes with a true/false value, but works with any attribute. + * If the attribute does not have a true/false value, the following rules apply: + * A not empty attribute will be removed. + * An empty attribute will be set to true. + * @param {!Element} el DOM node for which to set attribute. + * @param {!(goog.a11y.aria.State|string)} attr ARIA attribute being set. + * Automatically adds prefix 'aria-' to the attribute name if the attribute + * is not an extra attribute. + */ +goog.a11y.aria.toggleState = function(el, attr) { + var val = goog.a11y.aria.getState(el, attr); + if (!goog.string.isEmptyOrWhitespace(goog.string.makeSafe(val)) && + !(val == 'true' || val == 'false')) { + goog.a11y.aria.removeState(el, /** @type {!goog.a11y.aria.State} */ (attr)); + return; + } + goog.a11y.aria.setState(el, attr, val == 'true' ? 'false' : 'true'); +}; + + +/** + * Remove the state or property for the element. + * @param {!Element} element DOM node where we set state. + * @param {!goog.a11y.aria.State} stateName State name. + */ +goog.a11y.aria.removeState = function(element, stateName) { + element.removeAttribute(goog.a11y.aria.getAriaAttributeName_(stateName)); +}; + + +/** + * Gets value of specified state or property. + * @param {!Element} element DOM node to get state from. + * @param {!goog.a11y.aria.State|string} stateName State name. + * @return {string} Value of the state attribute. + */ +goog.a11y.aria.getState = function(element, stateName) { + // TODO(user): return properly typed value result -- + // boolean, number, string, null. We should be able to chain + // getState(...) and setState(...) methods. + + var attr = + /** @type {string|number|boolean} */ (element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + var isNullOrUndefined = attr == null || attr == undefined; + return isNullOrUndefined ? '' : String(attr); +}; + + +/** + * Returns the activedescendant element for the input element by + * using the activedescendant ARIA property of the given element. + * @param {!Element} element DOM node to get activedescendant + * element for. + * @return {?Element} DOM node of the activedescendant, if found. + */ +goog.a11y.aria.getActiveDescendant = function(element) { + var id = goog.a11y.aria.getState( + element, goog.a11y.aria.State.ACTIVEDESCENDANT); + return goog.dom.getOwnerDocument(element).getElementById(id); +}; + + +/** + * Sets the activedescendant ARIA property value for an element. + * If the activeElement is not null, it should have an id set. + * @param {!Element} element DOM node to set activedescendant ARIA property to. + * @param {?Element} activeElement DOM node being set as activedescendant. + */ +goog.a11y.aria.setActiveDescendant = function(element, activeElement) { + var id = ''; + if (activeElement) { + id = activeElement.id; + goog.asserts.assert(id, 'The active element should have an id.'); + } + + goog.a11y.aria.setState(element, goog.a11y.aria.State.ACTIVEDESCENDANT, id); +}; + + +/** + * Gets the label of the given element. + * @param {!Element} element DOM node to get label from. + * @return {string} label The label. + */ +goog.a11y.aria.getLabel = function(element) { + return goog.a11y.aria.getState(element, goog.a11y.aria.State.LABEL); +}; + + +/** + * Sets the label of the given element. + * @param {!Element} element DOM node to set label to. + * @param {string} label The label to set. + */ +goog.a11y.aria.setLabel = function(element, label) { + goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, label); +}; + + +/** + * Asserts that the element has a role set if it's not an HTML element whose + * semantics is well supported by most screen readers. + * Only to be used internally by the ARIA library in goog.a11y.aria.*. + * @param {!Element} element The element to assert an ARIA role set. + * @param {!goog.array.ArrayLike} allowedRoles The child roles of + * the roles. + */ +goog.a11y.aria.assertRoleIsSetInternalUtil = function(element, allowedRoles) { + if (goog.array.contains(goog.a11y.aria.TAGS_WITH_ASSUMED_ROLES_, + element.tagName)) { + return; + } + var elementRole = /** @type {string}*/ (goog.a11y.aria.getRole(element)); + goog.asserts.assert(elementRole != null, + 'The element ARIA role cannot be null.'); + + goog.asserts.assert(goog.array.contains(allowedRoles, elementRole), + 'Non existing or incorrect role set for element.' + + 'The role set is "' + elementRole + + '". The role should be any of "' + allowedRoles + + '". Check the ARIA specification for more details ' + + 'http://www.w3.org/TR/wai-aria/roles.'); +}; + + +/** + * Gets the boolean value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?boolean} Boolean value for the ARIA state value or null if + * the state value is not 'true', not 'false', or not set. + */ +goog.a11y.aria.getStateBoolean = function(element, stateName) { + var attr = + /** @type {string|boolean} */ (element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + goog.asserts.assert( + goog.isBoolean(attr) || attr == null || attr == 'true' || + attr == 'false'); + if (attr == null) { + return attr; + } + return goog.isBoolean(attr) ? attr : attr == 'true'; +}; + + +/** + * Gets the number value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?number} Number value for the ARIA state value or null if + * the state value is not a number or not set. + */ +goog.a11y.aria.getStateNumber = function(element, stateName) { + var attr = + /** @type {string|number} */ (element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName))); + goog.asserts.assert((attr == null || !isNaN(Number(attr))) && + !goog.isBoolean(attr)); + return attr == null ? null : Number(attr); +}; + + +/** + * Gets the string value of an ARIA state/property. + * @param {!Element} element The element to get the ARIA state for. + * @param {!goog.a11y.aria.State|string} stateName the ARIA state name. + * @return {?string} String value for the ARIA state value or null if + * the state value is empty string or not set. + */ +goog.a11y.aria.getStateString = function(element, stateName) { + var attr = element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName)); + goog.asserts.assert((attr == null || goog.isString(attr)) && + isNaN(Number(attr)) && attr != 'true' && attr != 'false'); + return attr == null ? null : attr; +}; + + +/** + * Gets array of strings value of the specified state or + * property for the element. + * Only to be used internally by the ARIA library in goog.a11y.aria.*. + * @param {!Element} element DOM node to get state from. + * @param {!goog.a11y.aria.State} stateName State name. + * @return {!goog.array.ArrayLike} string Array + * value of the state attribute. + */ +goog.a11y.aria.getStringArrayStateInternalUtil = function(element, stateName) { + var attrValue = element.getAttribute( + goog.a11y.aria.getAriaAttributeName_(stateName)); + return goog.a11y.aria.splitStringOnWhitespace_(attrValue); +}; + + +/** + * Splits the input stringValue on whitespace. + * @param {string} stringValue The value of the string to split. + * @return {!goog.array.ArrayLike} string Array + * value as result of the split. + * @private + */ +goog.a11y.aria.splitStringOnWhitespace_ = function(stringValue) { + return stringValue ? stringValue.split(/\s+/) : []; +}; + + +/** + * Adds the 'aria-' prefix to ariaName. + * @param {string} ariaName ARIA state/property name. + * @private + * @return {string} The ARIA attribute name with added 'aria-' prefix. + * @throws {Error} If no such attribute exists. + */ +goog.a11y.aria.getAriaAttributeName_ = function(ariaName) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.assert(ariaName, 'ARIA attribute cannot be empty.'); + goog.asserts.assert(goog.object.containsValue( + goog.a11y.aria.State, ariaName), + 'No such ARIA attribute ' + ariaName); + } + return goog.a11y.aria.ARIA_PREFIX_ + ariaName; +}; diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.html b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.html new file mode 100644 index 0000000000..d8e6286ad9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.html @@ -0,0 +1,27 @@ + + + + + + + + Closure Unit Tests - goog.a11y.aria + + + + + +
+
+ + diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.js new file mode 100644 index 0000000000..c208aa147e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/aria_test.js @@ -0,0 +1,269 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.goog.provide('goog.a11y.ariaTest'); + +goog.provide('goog.a11y.ariaTest'); +goog.setTestOnly('goog.a11y.ariaTest'); + +goog.require('goog.a11y.aria'); +goog.require('goog.a11y.aria.Role'); +goog.require('goog.a11y.aria.State'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.testing.jsunit'); + +var aria = goog.a11y.aria; +var Role = goog.a11y.aria.Role; +var State = goog.a11y.aria.State; +var sandbox; +var someDiv; +var someSpan; +var htmlButton; + +function setUp() { + sandbox = goog.dom.getElement('sandbox'); + someDiv = goog.dom.createDom( + goog.dom.TagName.DIV, {id: 'someDiv'}, 'DIV'); + someSpan = goog.dom.createDom( + goog.dom.TagName.SPAN, {id: 'someSpan'}, 'SPAN'); + htmlButton = goog.dom.createDom( + goog.dom.TagName.BUTTON, {id: 'someButton'}, 'BUTTON'); + goog.dom.appendChild(sandbox, someDiv); + goog.dom.appendChild(someDiv, someSpan); +} + +function tearDown() { + goog.dom.removeChildren(sandbox); + someDiv = null; + someSpan = null; + htmlButton = null; +} + +function testGetSetRole() { + assertNull('someDiv\'s role should be null', aria.getRole(someDiv)); + assertNull('someSpan\'s role should be null', aria.getRole(someSpan)); + + aria.setRole(someDiv, Role.MENU); + aria.setRole(someSpan, Role.MENU_ITEM); + + assertEquals('someDiv\'s role should be MENU', + Role.MENU, aria.getRole(someDiv)); + assertEquals('someSpan\'s role should be MENU_ITEM', + Role.MENU_ITEM, aria.getRole(someSpan)); + + var div = goog.dom.createElement(goog.dom.TagName.DIV); + goog.dom.appendChild(sandbox, div); + goog.dom.appendChild(div, goog.dom.createDom(goog.dom.TagName.SPAN, + {id: 'anotherSpan', role: Role.CHECKBOX})); + assertEquals('anotherSpan\'s role should be CHECKBOX', + Role.CHECKBOX, aria.getRole(goog.dom.getElement('anotherSpan'))); +} + +function testGetSetToggleState() { + assertThrows('Should throw because no state is specified.', + function() { + aria.getState(someDiv); + }); + assertThrows('Should throw because no state is specified.', + function() { + aria.getState(someDiv); + }); + aria.setState(someDiv, State.LABELLEDBY, 'someSpan'); + + assertEquals('someDiv\'s labelledby state should be "someSpan"', + 'someSpan', aria.getState(someDiv, State.LABELLEDBY)); + + // Test setting for aria-activedescendant with empty value. + assertFalse(someDiv.hasAttribute ? + someDiv.hasAttribute('aria-activedescendant') : + !!someDiv.getAttribute('aria-activedescendant')); + aria.setState(someDiv, State.ACTIVEDESCENDANT, 'someSpan'); + assertEquals('someSpan', aria.getState(someDiv, State.ACTIVEDESCENDANT)); + aria.setState(someDiv, State.ACTIVEDESCENDANT, ''); + assertFalse(someDiv.hasAttribute ? + someDiv.hasAttribute('aria-activedescendant') : + !!someDiv.getAttribute('aria-activedescendant')); + + // Test setting state that has a default value to empty value. + assertFalse(someDiv.hasAttribute ? + someDiv.hasAttribute('aria-relevant') : + !!someDiv.getAttribute('aria-relevant')); + aria.setState(someDiv, State.RELEVANT, aria.RelevantValues.TEXT); + assertEquals( + aria.RelevantValues.TEXT, aria.getState(someDiv, State.RELEVANT)); + aria.setState(someDiv, State.RELEVANT, ''); + assertEquals( + aria.RelevantValues.ADDITIONS + ' ' + aria.RelevantValues.TEXT, + aria.getState(someDiv, State.RELEVANT)); + + // Test toggling an attribute that has a true/false value. + aria.setState(someDiv, State.EXPANDED, false); + assertEquals('false', aria.getState(someDiv, State.EXPANDED)); + aria.toggleState(someDiv, State.EXPANDED); + assertEquals('true', aria.getState(someDiv, State.EXPANDED)); + aria.setState(someDiv, State.EXPANDED, true); + assertEquals('true', aria.getState(someDiv, State.EXPANDED)); + aria.toggleState(someDiv, State.EXPANDED); + assertEquals('false', aria.getState(someDiv, State.EXPANDED)); + + // Test toggling an attribute that does not have a true/false value. + aria.setState(someDiv, State.RELEVANT, aria.RelevantValues.TEXT); + assertEquals( + aria.RelevantValues.TEXT, aria.getState(someDiv, State.RELEVANT)); + aria.toggleState(someDiv, State.RELEVANT); + assertEquals('', aria.getState(someDiv, State.RELEVANT)); + aria.removeState(someDiv, State.RELEVANT); + assertEquals('', aria.getState(someDiv, State.RELEVANT)); + // This is not a valid value, but this is what happens if toggle is misused. + aria.toggleState(someDiv, State.RELEVANT); + assertEquals('true', aria.getState(someDiv, State.RELEVANT)); +} + +function testGetStateString() { + aria.setState(someDiv, State.LABEL, 'test_label'); + aria.setState( + someSpan, State.LABEL, aria.getStateString(someDiv, State.LABEL)); + assertEquals(aria.getState(someDiv, State.LABEL), + aria.getState(someSpan, State.LABEL)); + assertEquals('The someDiv\'s enum value should be "test_label".', + 'test_label', aria.getState(someDiv, State.LABEL)); + assertEquals('The someSpan\'s enum value should be "copy move".', + 'test_label', aria.getStateString(someSpan, State.LABEL)); + aria.setState(someDiv, State.MULTILINE, true); + var thrown = false; + try { + aria.getStateString(someDiv, State.MULTILINE); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateString on boolean.', thrown); + aria.setState(someDiv, State.LIVE, aria.LivePriority.ASSERTIVE); + thrown = false; + aria.setState(someDiv, State.LEVEL, 1); + try { + aria.getStateString(someDiv, State.LEVEL); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateString on numbers.', thrown); +} + + +function testGetStateStringArray() { + aria.setState(someDiv, State.LABELLEDBY, ['1', '2']); + aria.setState(someSpan, State.LABELLEDBY, + aria.getStringArrayStateInternalUtil(someDiv, State.LABELLEDBY)); + assertEquals(aria.getState(someDiv, State.LABELLEDBY), + aria.getState(someSpan, State.LABELLEDBY)); + + assertEquals('The someDiv\'s enum value should be "1 2".', '1 2', + aria.getState(someDiv, State.LABELLEDBY)); + assertEquals('The someSpan\'s enum value should be "1 2".', '1 2', + aria.getState(someSpan, State.LABELLEDBY)); + + assertSameElements('The someDiv\'s enum value should be "1 2".', + ['1', '2'], + aria.getStringArrayStateInternalUtil(someDiv, State.LABELLEDBY)); + assertSameElements('The someSpan\'s enum value should be "1 2".', + ['1', '2'], + aria.getStringArrayStateInternalUtil(someSpan, State.LABELLEDBY)); +} + + +function testGetStateNumber() { + aria.setState(someDiv, State.LEVEL, 1); + aria.setState( + someSpan, State.LEVEL, aria.getStateNumber(someDiv, State.LEVEL)); + assertEquals(aria.getState(someDiv, State.LEVEL), + aria.getState(someSpan, State.LEVEL)); + assertEquals('The someDiv\'s enum value should be "1".', '1', + aria.getState(someDiv, State.LEVEL)); + assertEquals('The someSpan\'s enum value should be "1".', '1', + aria.getState(someSpan, State.LEVEL)); + assertEquals('The someDiv\'s enum value should be "1".', 1, + aria.getStateNumber(someDiv, State.LEVEL)); + assertEquals('The someSpan\'s enum value should be "1".', 1, + aria.getStateNumber(someSpan, State.LEVEL)); + aria.setState(someDiv, State.MULTILINE, true); + var thrown = false; + try { + aria.getStateNumber(someDiv, State.MULTILINE); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateNumber on boolean.', thrown); + aria.setState(someDiv, State.LIVE, aria.LivePriority.ASSERTIVE); + thrown = false; + try { + aria.getStateBoolean(someDiv, State.LIVE); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateNumber on strings.', thrown); +} + +function testGetStateBoolean() { + assertNull(aria.getStateBoolean(someDiv, State.MULTILINE)); + + aria.setState(someDiv, State.MULTILINE, false); + assertFalse(aria.getStateBoolean(someDiv, State.MULTILINE)); + + aria.setState(someDiv, State.MULTILINE, true); + aria.setState(someSpan, State.MULTILINE, + aria.getStateBoolean(someDiv, State.MULTILINE)); + assertEquals(aria.getState(someDiv, State.MULTILINE), + aria.getState(someSpan, State.MULTILINE)); + assertEquals('The someDiv\'s enum value should be "true".', 'true', + aria.getState(someDiv, State.MULTILINE)); + assertEquals('The someSpan\'s enum value should be "true".', 'true', + aria.getState(someSpan, State.MULTILINE)); + assertEquals('The someDiv\'s enum value should be "true".', true, + aria.getStateBoolean(someDiv, State.MULTILINE)); + assertEquals('The someSpan\'s enum value should be "true".', true, + aria.getStateBoolean(someSpan, State.MULTILINE)); + aria.setState(someDiv, State.LEVEL, 1); + var thrown = false; + try { + aria.getStateBoolean(someDiv, State.LEVEL); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateBoolean on numbers.', thrown); + aria.setState(someDiv, State.LIVE, aria.LivePriority.ASSERTIVE); + thrown = false; + try { + aria.getStateBoolean(someDiv, State.LIVE); + } catch (e) { + thrown = true; + } + assertTrue('invalid use of getStateBoolean on strings.', thrown); +} + +function testGetSetActiveDescendant() { + aria.setActiveDescendant(someDiv, null); + assertNull('someDiv\'s activedescendant should be null', + aria.getActiveDescendant(someDiv)); + + aria.setActiveDescendant(someDiv, someSpan); + + assertEquals('someDiv\'s active descendant should be "someSpan"', + someSpan, aria.getActiveDescendant(someDiv)); +} + +function testGetSetLabel() { + assertEquals('someDiv\'s label should be ""', '', aria.getLabel(someDiv)); + + aria.setLabel(someDiv, 'somelabel'); + assertEquals('someDiv\'s label should be "somelabel"', 'somelabel', + aria.getLabel(someDiv)); +} diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/attributes.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/attributes.js new file mode 100644 index 0000000000..f4e0a3d074 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/attributes.js @@ -0,0 +1,389 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview The file contains generated enumerations for ARIA states + * and properties as defined by W3C ARIA standard: + * http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! For more details + * about how to edit it via the generator check go/closure-ariagen. + */ + +goog.provide('goog.a11y.aria.AutoCompleteValues'); +goog.provide('goog.a11y.aria.CheckedValues'); +goog.provide('goog.a11y.aria.DropEffectValues'); +goog.provide('goog.a11y.aria.ExpandedValues'); +goog.provide('goog.a11y.aria.GrabbedValues'); +goog.provide('goog.a11y.aria.InvalidValues'); +goog.provide('goog.a11y.aria.LivePriority'); +goog.provide('goog.a11y.aria.OrientationValues'); +goog.provide('goog.a11y.aria.PressedValues'); +goog.provide('goog.a11y.aria.RelevantValues'); +goog.provide('goog.a11y.aria.SelectedValues'); +goog.provide('goog.a11y.aria.SortValues'); +goog.provide('goog.a11y.aria.State'); + + +/** + * ARIA states and properties. + * @enum {string} + */ +goog.a11y.aria.State = { + // ARIA property for setting the currently active descendant of an element, + // for example the selected item in a list box. Value: ID of an element. + ACTIVEDESCENDANT: 'activedescendant', + + // ARIA property that, if true, indicates that all of a changed region should + // be presented, instead of only parts. Value: one of {true, false}. + ATOMIC: 'atomic', + + // ARIA property to specify that input completion is provided. Value: + // one of {'inline', 'list', 'both', 'none'}. + AUTOCOMPLETE: 'autocomplete', + + // ARIA state to indicate that an element and its subtree are being updated. + // Value: one of {true, false}. + BUSY: 'busy', + + // ARIA state for a checked item. Value: one of {'true', 'false', 'mixed', + // undefined}. + CHECKED: 'checked', + + // ARIA property that identifies the element or elements whose contents or + // presence are controlled by this element. + // Value: space-separated IDs of other elements. + CONTROLS: 'controls', + + // ARIA property that identifies the element or elements that describe + // this element. Value: space-separated IDs of other elements. + DESCRIBEDBY: 'describedby', + + // ARIA state for a disabled item. Value: one of {true, false}. + DISABLED: 'disabled', + + // ARIA property that indicates what functions can be performed when a + // dragged object is released on the drop target. Value: one of + // {'copy', 'move', 'link', 'execute', 'popup', 'none'}. + DROPEFFECT: 'dropeffect', + + // ARIA state for setting whether the element like a tree node is expanded. + // Value: one of {true, false, undefined}. + EXPANDED: 'expanded', + + // ARIA property that identifies the next element (or elements) in the + // recommended reading order of content. Value: space-separated ids of + // elements to flow to. + FLOWTO: 'flowto', + + // ARIA state that indicates an element's "grabbed" state in drag-and-drop. + // Value: one of {true, false, undefined}. + GRABBED: 'grabbed', + + // ARIA property indicating whether the element has a popup. + // Value: one of {true, false}. + HASPOPUP: 'haspopup', + + // ARIA state indicating that the element is not visible or perceivable + // to any user. Value: one of {true, false}. + HIDDEN: 'hidden', + + // ARIA state indicating that the entered value does not conform. Value: + // one of {false, true, 'grammar', 'spelling'} + INVALID: 'invalid', + + // ARIA property that provides a label to override any other text, value, or + // contents used to describe this element. Value: string. + LABEL: 'label', + + // ARIA property for setting the element which labels another element. + // Value: space-separated IDs of elements. + LABELLEDBY: 'labelledby', + + // ARIA property for setting the level of an element in the hierarchy. + // Value: integer. + LEVEL: 'level', + + // ARIA property indicating that an element will be updated, and + // describes the types of updates the user agents, assistive technologies, + // and user can expect from the live region. Value: one of {'off', 'polite', + // 'assertive'}. + LIVE: 'live', + + // ARIA property indicating whether a text box can accept multiline input. + // Value: one of {true, false}. + MULTILINE: 'multiline', + + // ARIA property indicating if the user may select more than one item. + // Value: one of {true, false}. + MULTISELECTABLE: 'multiselectable', + + // ARIA property indicating if the element is horizontal or vertical. + // Value: one of {'vertical', 'horizontal'}. + ORIENTATION: 'orientation', + + // ARIA property creating a visual, functional, or contextual parent/child + // relationship when the DOM hierarchy can't be used to represent it. + // Value: Space-separated IDs of elements. + OWNS: 'owns', + + // ARIA property that defines an element's number of position in a list. + // Value: integer. + POSINSET: 'posinset', + + // ARIA state for a pressed item. + // Value: one of {true, false, undefined, 'mixed'}. + PRESSED: 'pressed', + + // ARIA property indicating that an element is not editable. + // Value: one of {true, false}. + READONLY: 'readonly', + + // ARIA property indicating that change notifications within this subtree + // of a live region should be announced. Value: one of {'additions', + // 'removals', 'text', 'all', 'additions text'}. + RELEVANT: 'relevant', + + // ARIA property indicating that user input is required on this element + // before a form may be submitted. Value: one of {true, false}. + REQUIRED: 'required', + + // ARIA state for setting the currently selected item in the list. + // Value: one of {true, false, undefined}. + SELECTED: 'selected', + + // ARIA property defining the number of items in a list. Value: integer. + SETSIZE: 'setsize', + + // ARIA property indicating if items are sorted. Value: one of {'ascending', + // 'descending', 'none', 'other'}. + SORT: 'sort', + + // ARIA property for slider maximum value. Value: number. + VALUEMAX: 'valuemax', + + // ARIA property for slider minimum value. Value: number. + VALUEMIN: 'valuemin', + + // ARIA property for slider active value. Value: number. + VALUENOW: 'valuenow', + + // ARIA property for slider active value represented as text. + // Value: string. + VALUETEXT: 'valuetext' +}; + + +/** + * ARIA state values for AutoCompleteValues. + * @enum {string} + */ +goog.a11y.aria.AutoCompleteValues = { + // The system provides text after the caret as a suggestion + // for how to complete the field. + INLINE: 'inline', + // A list of choices appears from which the user can choose, + // but the edit box retains focus. + LIST: 'list', + // A list of choices appears and the currently selected suggestion + // also appears inline. + BOTH: 'both', + // No input completion suggestions are provided. + NONE: 'none' +}; + + +/** + * ARIA state values for DropEffectValues. + * @enum {string} + */ +goog.a11y.aria.DropEffectValues = { + // A duplicate of the source object will be dropped into the target. + COPY: 'copy', + // The source object will be removed from its current location + // and dropped into the target. + MOVE: 'move', + // A reference or shortcut to the dragged object + // will be created in the target object. + LINK: 'link', + // A function supported by the drop target is + // executed, using the drag source as an input. + EXECUTE: 'execute', + // There is a popup menu or dialog that allows the user to choose + // one of the drag operations (copy, move, link, execute) and any other + // drag functionality, such as cancel. + POPUP: 'popup', + // No operation can be performed; effectively + // cancels the drag operation if an attempt is made to drop on this object. + NONE: 'none' +}; + + +/** + * ARIA state values for LivePriority. + * @enum {string} + */ +goog.a11y.aria.LivePriority = { + // Updates to the region will not be presented to the user + // unless the assitive technology is currently focused on that region. + OFF: 'off', + // (Background change) Assistive technologies SHOULD announce + // updates at the next graceful opportunity, such as at the end of + // speaking the current sentence or when the user pauses typing. + POLITE: 'polite', + // This information has the highest priority and assistive + // technologies SHOULD notify the user immediately. + // Because an interruption may disorient users or cause them to not complete + // their current task, authors SHOULD NOT use the assertive value unless the + // interruption is imperative. + ASSERTIVE: 'assertive' +}; + + +/** + * ARIA state values for OrientationValues. + * @enum {string} + */ +goog.a11y.aria.OrientationValues = { + // The element is oriented vertically. + VERTICAL: 'vertical', + // The element is oriented horizontally. + HORIZONTAL: 'horizontal' +}; + + +/** + * ARIA state values for RelevantValues. + * @enum {string} + */ +goog.a11y.aria.RelevantValues = { + // Element nodes are added to the DOM within the live region. + ADDITIONS: 'additions', + // Text or element nodes within the live region are removed from the DOM. + REMOVALS: 'removals', + // Text is added to any DOM descendant nodes of the live region. + TEXT: 'text', + // Equivalent to the combination of all values, "additions removals text". + ALL: 'all' +}; + + +/** + * ARIA state values for SortValues. + * @enum {string} + */ +goog.a11y.aria.SortValues = { + // Items are sorted in ascending order by this column. + ASCENDING: 'ascending', + // Items are sorted in descending order by this column. + DESCENDING: 'descending', + // There is no defined sort applied to the column. + NONE: 'none', + // A sort algorithm other than ascending or descending has been applied. + OTHER: 'other' +}; + + +/** + * ARIA state values for CheckedValues. + * @enum {string} + */ +goog.a11y.aria.CheckedValues = { + // The selectable element is checked. + TRUE: 'true', + // The selectable element is not checked. + FALSE: 'false', + // Indicates a mixed mode value for a tri-state + // checkbox or menuitemcheckbox. + MIXED: 'mixed', + // The element does not support being checked. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for ExpandedValues. + * @enum {string} + */ +goog.a11y.aria.ExpandedValues = { + // The element, or another grouping element it controls, is expanded. + TRUE: 'true', + // The element, or another grouping element it controls, is collapsed. + FALSE: 'false', + // The element, or another grouping element + // it controls, is neither expandable nor collapsible; all its + // child elements are shown or there are no child elements. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for GrabbedValues. + * @enum {string} + */ +goog.a11y.aria.GrabbedValues = { + // Indicates that the element has been "grabbed" for dragging. + TRUE: 'true', + // Indicates that the element supports being dragged. + FALSE: 'false', + // Indicates that the element does not support being dragged. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for InvalidValues. + * @enum {string} + */ +goog.a11y.aria.InvalidValues = { + // There are no detected errors in the value. + FALSE: 'false', + // The value entered by the user has failed validation. + TRUE: 'true', + // A grammatical error was detected. + GRAMMAR: 'grammar', + // A spelling error was detected. + SPELLING: 'spelling' +}; + + +/** + * ARIA state values for PressedValues. + * @enum {string} + */ +goog.a11y.aria.PressedValues = { + // The element is pressed. + TRUE: 'true', + // The element supports being pressed but is not currently pressed. + FALSE: 'false', + // Indicates a mixed mode value for a tri-state toggle button. + MIXED: 'mixed', + // The element does not support being pressed. + UNDEFINED: 'undefined' +}; + + +/** + * ARIA state values for SelectedValues. + * @enum {string} + */ +goog.a11y.aria.SelectedValues = { + // The selectable element is selected. + TRUE: 'true', + // The selectable element is not selected. + FALSE: 'false', + // The element is not selectable. + UNDEFINED: 'undefined' +}; diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/datatables.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/datatables.js new file mode 100644 index 0000000000..f1ba566df8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/datatables.js @@ -0,0 +1,68 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +/** + * @fileoverview The file contains data tables generated from the ARIA + * standard schema http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! + */ + +goog.provide('goog.a11y.aria.datatables'); + +goog.require('goog.a11y.aria.State'); +goog.require('goog.object'); + + +/** + * A map that contains mapping between an ARIA state and the default value + * for it. Note that not all ARIA states have default values. + * + * @type {Object} + */ +goog.a11y.aria.DefaultStateValueMap_; + + +/** + * A method that creates a map that contains mapping between an ARIA state and + * the default value for it. Note that not all ARIA states have default values. + * + * @return {!Object} + * The names for each of the notification methods. + */ +goog.a11y.aria.datatables.getDefaultValuesMap = function() { + if (!goog.a11y.aria.DefaultStateValueMap_) { + goog.a11y.aria.DefaultStateValueMap_ = goog.object.create( + goog.a11y.aria.State.ATOMIC, false, + goog.a11y.aria.State.AUTOCOMPLETE, 'none', + goog.a11y.aria.State.DROPEFFECT, 'none', + goog.a11y.aria.State.HASPOPUP, false, + goog.a11y.aria.State.LIVE, 'off', + goog.a11y.aria.State.MULTILINE, false, + goog.a11y.aria.State.MULTISELECTABLE, false, + goog.a11y.aria.State.ORIENTATION, 'vertical', + goog.a11y.aria.State.READONLY, false, + goog.a11y.aria.State.RELEVANT, 'additions text', + goog.a11y.aria.State.REQUIRED, false, + goog.a11y.aria.State.SORT, 'none', + goog.a11y.aria.State.BUSY, false, + goog.a11y.aria.State.DISABLED, false, + goog.a11y.aria.State.HIDDEN, false, + goog.a11y.aria.State.INVALID, 'false'); + } + + return goog.a11y.aria.DefaultStateValueMap_; +}; diff --git a/mapbox-gl/closure-library/closure/goog/a11y/aria/roles.js b/mapbox-gl/closure-library/closure/goog/a11y/aria/roles.js new file mode 100644 index 0000000000..a282cc2d86 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/a11y/aria/roles.js @@ -0,0 +1,216 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview The file contains generated enumerations for ARIA roles + * as defined by W3C ARIA standard: http://www.w3.org/TR/wai-aria/. + * + * This is auto-generated code. Do not manually edit! For more details + * about how to edit it via the generator check go/closure-ariagen. + */ + +goog.provide('goog.a11y.aria.Role'); + + +/** + * ARIA role values. + * @enum {string} + */ +goog.a11y.aria.Role = { + // ARIA role for an alert element that doesn't need to be explicitly closed. + ALERT: 'alert', + + // ARIA role for an alert dialog element that takes focus and must be closed. + ALERTDIALOG: 'alertdialog', + + // ARIA role for an application that implements its own keyboard navigation. + APPLICATION: 'application', + + // ARIA role for an article. + ARTICLE: 'article', + + // ARIA role for a banner containing mostly site content, not page content. + BANNER: 'banner', + + // ARIA role for a button element. + BUTTON: 'button', + + // ARIA role for a checkbox button element; use with the CHECKED state. + CHECKBOX: 'checkbox', + + // ARIA role for a column header of a table or grid. + COLUMNHEADER: 'columnheader', + + // ARIA role for a combo box element. + COMBOBOX: 'combobox', + + // ARIA role for a supporting section of the document. + COMPLEMENTARY: 'complementary', + + // ARIA role for a large perceivable region that contains information + // about the parent document. + CONTENTINFO: 'contentinfo', + + // ARIA role for a definition of a term or concept. + DEFINITION: 'definition', + + // ARIA role for a dialog, some descendant must take initial focus. + DIALOG: 'dialog', + + // ARIA role for a directory, like a table of contents. + DIRECTORY: 'directory', + + // ARIA role for a part of a page that's a document, not a web application. + DOCUMENT: 'document', + + // ARIA role for a landmark region logically considered one form. + FORM: 'form', + + // ARIA role for an interactive control of tabular data. + GRID: 'grid', + + // ARIA role for a cell in a grid. + GRIDCELL: 'gridcell', + + // ARIA role for a group of related elements like tree item siblings. + GROUP: 'group', + + // ARIA role for a heading element. + HEADING: 'heading', + + // ARIA role for a container of elements that together comprise one image. + IMG: 'img', + + // ARIA role for a link. + LINK: 'link', + + // ARIA role for a list of non-interactive list items. + LIST: 'list', + + // ARIA role for a listbox. + LISTBOX: 'listbox', + + // ARIA role for a list item. + LISTITEM: 'listitem', + + // ARIA role for a live region where new information is added. + LOG: 'log', + + // ARIA landmark role for the main content in a document. Use only once. + MAIN: 'main', + + // ARIA role for a live region of non-essential information that changes. + MARQUEE: 'marquee', + + // ARIA role for a mathematical expression. + MATH: 'math', + + // ARIA role for a popup menu. + MENU: 'menu', + + // ARIA role for a menubar element containing menu elements. + MENUBAR: 'menubar', + + // ARIA role for menu item elements. + MENU_ITEM: 'menuitem', + + // ARIA role for a checkbox box element inside a menu. + MENU_ITEM_CHECKBOX: 'menuitemcheckbox', + + // ARIA role for a radio button element inside a menu. + MENU_ITEM_RADIO: 'menuitemradio', + + // ARIA landmark role for a collection of navigation links. + NAVIGATION: 'navigation', + + // ARIA role for a section ancillary to the main content. + NOTE: 'note', + + // ARIA role for option items that are children of combobox, listbox, menu, + // radiogroup, or tree elements. + OPTION: 'option', + + // ARIA role for ignorable cosmetic elements with no semantic significance. + PRESENTATION: 'presentation', + + // ARIA role for a progress bar element. + PROGRESSBAR: 'progressbar', + + // ARIA role for a radio button element. + RADIO: 'radio', + + // ARIA role for a group of connected radio button elements. + RADIOGROUP: 'radiogroup', + + // ARIA role for an important region of the page. + REGION: 'region', + + // ARIA role for a row of cells in a grid. + ROW: 'row', + + // ARIA role for a group of one or more rows in a grid. + ROWGROUP: 'rowgroup', + + // ARIA role for a row header of a table or grid. + ROWHEADER: 'rowheader', + + // ARIA role for a scrollbar element. + SCROLLBAR: 'scrollbar', + + // ARIA landmark role for a part of the page providing search functionality. + SEARCH: 'search', + + // ARIA role for a menu separator. + SEPARATOR: 'separator', + + // ARIA role for a slider. + SLIDER: 'slider', + + // ARIA role for a spin button. + SPINBUTTON: 'spinbutton', + + // ARIA role for a live region with advisory info less severe than an alert. + STATUS: 'status', + + // ARIA role for a tab button. + TAB: 'tab', + + // ARIA role for a tab bar (i.e. a list of tab buttons). + TAB_LIST: 'tablist', + + // ARIA role for a tab page (i.e. the element holding tab contents). + TAB_PANEL: 'tabpanel', + + // ARIA role for a textbox element. + TEXTBOX: 'textbox', + + // ARIA role for an element displaying elapsed time or time remaining. + TIMER: 'timer', + + // ARIA role for a toolbar element. + TOOLBAR: 'toolbar', + + // ARIA role for a tooltip element. + TOOLTIP: 'tooltip', + + // ARIA role for a tree. + TREE: 'tree', + + // ARIA role for a grid whose rows can be expanded and collapsed like a tree. + TREEGRID: 'treegrid', + + // ARIA role for a tree item that sometimes may be expanded or collapsed. + TREEITEM: 'treeitem' +}; diff --git a/mapbox-gl/closure-library/closure/goog/array/array.js b/mapbox-gl/closure-library/closure/goog/array/array.js new file mode 100644 index 0000000000..bd99626e48 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/array/array.js @@ -0,0 +1,1655 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating arrays. + * + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.array'); +goog.provide('goog.array.ArrayLike'); + +goog.require('goog.asserts'); + + +/** + * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should + * rely on Array.prototype functions, if available. + * + * The Array.prototype functions can be defined by external libraries like + * Prototype and setting this flag to false forces closure to use its own + * goog.array implementation. + * + * If your javascript can be loaded by a third party site and you are wary about + * relying on the prototype functions, specify + * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler. + * + * Setting goog.TRUSTED_SITE to false will automatically set + * NATIVE_ARRAY_PROTOTYPES to false. + */ +goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE); + + +/** + * @define {boolean} If true, JSCompiler will use the native implementation of + * array functions where appropriate (e.g., {@code Array#filter}) and remove the + * unused pure JS implementation. + */ +goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false); + + +/** + * @typedef {Array|NodeList|Arguments|{length: number}} + */ +goog.array.ArrayLike; + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.last. + * @param {Array|goog.array.ArrayLike} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.peek = function(array) { + return array[array.length - 1]; +}; + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.peek. + * @param {Array|goog.array.ArrayLike} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.last = goog.array.peek; + + +/** + * Reference to the original {@code Array.prototype}. + * @private + */ +goog.array.ARRAY_PROTOTYPE_ = Array.prototype; + + +// NOTE(arv): Since most of the array functions are generic it allows you to +// pass an array-like object. Strings have a length and are considered array- +// like. However, the 'in' operator does not work on strings so we cannot just +// use the array path even if the browser supports indexing into strings. We +// therefore end up splitting the string. + + +/** + * Returns the index of the first element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} + * + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at index 0. + * @return {number} The index of the first matching array element. + * @template T + */ +goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.indexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? + 0 : (opt_fromIndex < 0 ? + Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex); + + if (goog.isString(arr)) { + // Array.prototype.indexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.indexOf(obj, fromIndex); + } + + for (var i = fromIndex; i < arr.length; i++) { + if (i in arr && arr[i] === obj) + return i; + } + return -1; + }; + + +/** + * Returns the index of the last element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} + * + * @param {!Array|!goog.array.ArrayLike} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {?number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at the end of the array. + * @return {number} The index of the last matching array element. + * @template T + */ +goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.lastIndexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + // Firefox treats undefined and null as 0 in the fromIndex argument which + // leads it to always return -1 + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + + if (fromIndex < 0) { + fromIndex = Math.max(0, arr.length + fromIndex); + } + + if (goog.isString(arr)) { + // Array.prototype.lastIndexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.lastIndexOf(obj, fromIndex); + } + + for (var i = fromIndex; i >= 0; i--) { + if (i in arr && arr[i] === obj) + return i; + } + return -1; + }; + + +/** + * Calls a function for each element in an array. Skips holes in the array. + * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} + * + * @param {Array|goog.array.ArrayLike} arr Array or array like object over + * which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function takes 3 arguments (the element, the index and the + * array). The return value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @template T,S + */ +goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.forEach) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + f.call(opt_obj, arr2[i], i, arr); + } + } + }; + + +/** + * Calls a function for each element in an array, starting from the last + * element rather than the first. + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function + * takes 3 arguments (the element, the index and the array). The return + * value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @template T,S + */ +goog.array.forEachRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; --i) { + if (i in arr2) { + f.call(opt_obj, arr2[i], i, arr); + } + } +}; + + +/** + * Calls a function for each element in an array, and if the function returns + * true adds the element to a new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?):boolean} f The function to call for + * every element. This function + * takes 3 arguments (the element, the index and the array) and must + * return a Boolean. If the return value is true the element is added to the + * result array. If it is false the element is not included. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {!Array} a new array in which only elements that passed the test + * are present. + * @template T,S + */ +goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.filter) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = []; + var resLength = 0; + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + var val = arr2[i]; // in case f mutates arr2 + if (f.call(opt_obj, val, i, arr)) { + res[resLength++] = val; + } + } + } + return res; + }; + + +/** + * Calls a function for each element in an array and inserts the result into a + * new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-map} + * + * @param {Array|goog.array.ArrayLike} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call + * for every element. This function takes 3 arguments (the element, + * the index and the array) and should return something. The result will be + * inserted into a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array} a new array with the results from f. + * @template THIS, VALUE, RESULT + */ +goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.map) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = new Array(l); + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + res[i] = f.call(opt_obj, arr2[i], i, arr); + } + } + return res; + }; + + +/** + * Passes every element of an array into a function and accumulates the result. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce} + * + * For example: + * var a = [1, 2, 3, 4]; + * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); + * returns 10 + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {function(this:S, R, T, number, ?) : R} f The function to call for + * every element. This function + * takes 4 arguments (the function's previous result or the initial value, + * the value of the current array element, the current array index, and the + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Result of evaluating f repeatedly across the values of the array. + * @template T,S,R + */ +goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.reduce) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return goog.array.ARRAY_PROTOTYPE_.reduce.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEach(arr, function(val, index) { + rval = f.call(opt_obj, rval, val, index, arr); + }); + return rval; + }; + + +/** + * Passes every element of an array into a function and accumulates the result, + * starting from the last element and working towards the first. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright} + * + * For example: + * var a = ['a', 'b', 'c']; + * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); + * returns 'cba' + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, R, T, number, ?) : R} f The function to call for + * every element. This function + * takes 4 arguments (the function's previous result or the initial value, + * the value of the current array element, the current array index, and the + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Object returned as a result of evaluating f repeatedly across the + * values of the array. + * @template T,S,R + */ +goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.reduceRight) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEachRight(arr, function(val, index) { + rval = f.call(opt_obj, rval, val, index, arr); + }); + return rval; + }; + + +/** + * Calls f for each element of an array. If any call returns true, some() + * returns true (without checking the remaining elements). If all calls + * return false, some() returns false. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-some} + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} true if any element passes the test. + * @template T,S + */ +goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.some) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { + return true; + } + } + return false; + }; + + +/** + * Call f for each element of an array. If all calls return true, every() + * returns true. If any call returns false, every() returns false and + * does not continue to check the remaining elements. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-every} + * + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} false if any element fails the test. + * @template T,S + */ +goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.every) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) { + return false; + } + } + return true; + }; + + +/** + * Counts the array elements that fulfill the predicate, i.e. for which the + * callback function returns true. Skips holes in the array. + * + * @param {!(Array|goog.array.ArrayLike)} arr Array or array like object + * over which to iterate. + * @param {function(this: S, T, number, ?): boolean} f The function to call for + * every element. Takes 3 arguments (the element, the index and the array). + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @return {number} The number of the matching elements. + * @template T,S + */ +goog.array.count = function(arr, f, opt_obj) { + var count = 0; + goog.array.forEach(arr, function(element, index, arr) { + if (f.call(opt_obj, element, index, arr)) { + ++count; + } + }, opt_obj); + return count; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return that element. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {T|null} The first array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.find = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return its index. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the first array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndex = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return that element. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {T|null} The last array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.findRight = function(arr, f, opt_obj) { + var i = goog.array.findIndexRight(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return its index. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the last array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndexRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; i--) { + if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Whether the array contains the given object. + * @param {goog.array.ArrayLike} arr The array to test for the presence of the + * element. + * @param {*} obj The object for which to test. + * @return {boolean} true if obj is present. + */ +goog.array.contains = function(arr, obj) { + return goog.array.indexOf(arr, obj) >= 0; +}; + + +/** + * Whether the array is empty. + * @param {goog.array.ArrayLike} arr The array to test. + * @return {boolean} true if empty. + */ +goog.array.isEmpty = function(arr) { + return arr.length == 0; +}; + + +/** + * Clears the array. + * @param {goog.array.ArrayLike} arr Array or array like object to clear. + */ +goog.array.clear = function(arr) { + // For non real arrays we don't have the magic length so we delete the + // indices. + if (!goog.isArray(arr)) { + for (var i = arr.length - 1; i >= 0; i--) { + delete arr[i]; + } + } + arr.length = 0; +}; + + +/** + * Pushes an item into an array, if it's not already in the array. + * @param {Array} arr Array into which to insert the item. + * @param {T} obj Value to add. + * @template T + */ +goog.array.insert = function(arr, obj) { + if (!goog.array.contains(arr, obj)) { + arr.push(obj); + } +}; + + +/** + * Inserts an object at the given index of the array. + * @param {goog.array.ArrayLike} arr The array to modify. + * @param {*} obj The object to insert. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertAt = function(arr, obj, opt_i) { + goog.array.splice(arr, opt_i, 0, obj); +}; + + +/** + * Inserts at the given index of the array, all elements of another array. + * @param {goog.array.ArrayLike} arr The array to modify. + * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) { + goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd); +}; + + +/** + * Inserts an object into an array before a specified object. + * @param {Array} arr The array to modify. + * @param {T} obj The object to insert. + * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2 + * is omitted or not found, obj is inserted at the end of the array. + * @template T + */ +goog.array.insertBefore = function(arr, obj, opt_obj2) { + var i; + if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) { + arr.push(obj); + } else { + goog.array.insertAt(arr, obj, i); + } +}; + + +/** + * Removes the first occurrence of a particular value from an array. + * @param {Array|goog.array.ArrayLike} arr Array from which to remove + * value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.remove = function(arr, obj) { + var i = goog.array.indexOf(arr, obj); + var rv; + if ((rv = i >= 0)) { + goog.array.removeAt(arr, i); + } + return rv; +}; + + +/** + * Removes from an array the element at index i + * @param {goog.array.ArrayLike} arr Array or array like object from which to + * remove value. + * @param {number} i The index to remove. + * @return {boolean} True if an element was removed. + */ +goog.array.removeAt = function(arr, i) { + goog.asserts.assert(arr.length != null); + + // use generic form of splice + // splice returns the removed items and if successful the length of that + // will be 1 + return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1; +}; + + +/** + * Removes the first value that satisfies the given condition. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {boolean} True if an element was removed. + * @template T,S + */ +goog.array.removeIf = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + +/** + * Removes all values that satisfy the given condition. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The number of items removed + * @template T,S + */ +goog.array.removeAllIf = function(arr, f, opt_obj) { + var removedCount = 0; + goog.array.forEachRight(arr, function(val, index) { + if (f.call(opt_obj, val, index, arr)) { + if (goog.array.removeAt(arr, index)) { + removedCount++; + } + } + }); + return removedCount; +}; + + +/** + * Returns a new array that is the result of joining the arguments. If arrays + * are passed then their items are added, however, if non-arrays are passed they + * will be added to the return array as is. + * + * Note that ArrayLike objects will be added as is, rather than having their + * items added. + * + * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4] + * goog.array.concat(0, [1, 2]) -> [0, 1, 2] + * goog.array.concat([1, 2], null) -> [1, 2, null] + * + * There is bug in all current versions of IE (6, 7 and 8) where arrays created + * in an iframe become corrupted soon (not immediately) after the iframe is + * destroyed. This is common if loading data via goog.net.IframeIo, for example. + * This corruption only affects the concat method which will start throwing + * Catastrophic Errors (#-2147418113). + * + * See http://endoflow.com/scratch/corrupted-arrays.html for a test case. + * + * Internally goog.array should use this, so that all methods will continue to + * work on these broken array objects. + * + * @param {...*} var_args Items to concatenate. Arrays will have each item + * added, while primitives and objects will be added as is. + * @return {!Array} The new resultant array. + */ +goog.array.concat = function(var_args) { + return goog.array.ARRAY_PROTOTYPE_.concat.apply( + goog.array.ARRAY_PROTOTYPE_, arguments); +}; + + +/** + * Returns a new array that contains the contents of all the arrays passed. + * @param {...!Array} var_args + * @return {!Array} + * @template T + */ +goog.array.join = function(var_args) { + return goog.array.ARRAY_PROTOTYPE_.concat.apply( + goog.array.ARRAY_PROTOTYPE_, arguments); +}; + + +/** + * Converts an object to an array. + * @param {Array|goog.array.ArrayLike} object The object to convert to an + * array. + * @return {!Array} The object converted into an array. If object has a + * length property, every property indexed with a non-negative number + * less than length will be included in the result. If object does not + * have a length property, an empty array will be returned. + * @template T + */ +goog.array.toArray = function(object) { + var length = object.length; + + // If length is not a number the following it false. This case is kept for + // backwards compatibility since there are callers that pass objects that are + // not array like. + if (length > 0) { + var rv = new Array(length); + for (var i = 0; i < length; i++) { + rv[i] = object[i]; + } + return rv; + } + return []; +}; + + +/** + * Does a shallow copy of an array. + * @param {Array|goog.array.ArrayLike} arr Array or array-like object to + * clone. + * @return {!Array} Clone of the input array. + * @template T + */ +goog.array.clone = goog.array.toArray; + + +/** + * Extends an array with another array, element, or "array like" object. + * This function operates 'in-place', it does not create a new Array. + * + * Example: + * var a = []; + * goog.array.extend(a, [0, 1]); + * a; // [0, 1] + * goog.array.extend(a, 2); + * a; // [0, 1, 2] + * + * @param {Array} arr1 The array to modify. + * @param {...(Array|VALUE)} var_args The elements or arrays of elements + * to add to arr1. + * @template VALUE + */ +goog.array.extend = function(arr1, var_args) { + for (var i = 1; i < arguments.length; i++) { + var arr2 = arguments[i]; + if (goog.isArrayLike(arr2)) { + var len1 = arr1.length || 0; + var len2 = arr2.length || 0; + arr1.length = len1 + len2; + for (var j = 0; j < len2; j++) { + arr1[len1 + j] = arr2[j]; + } + } else { + arr1.push(arr2); + } + } +}; + + +/** + * Adds or removes elements from an array. This is a generic version of Array + * splice. This means that it might work on other objects similar to arrays, + * such as the arguments object. + * + * @param {Array|goog.array.ArrayLike} arr The array to modify. + * @param {number|undefined} index The index at which to start changing the + * array. If not defined, treated as 0. + * @param {number} howMany How many elements to remove (0 means no removal. A + * value below 0 is treated as zero and so is any other non number. Numbers + * are floored). + * @param {...T} var_args Optional, additional elements to insert into the + * array. + * @return {!Array} the removed elements. + * @template T + */ +goog.array.splice = function(arr, index, howMany, var_args) { + goog.asserts.assert(arr.length != null); + + return goog.array.ARRAY_PROTOTYPE_.splice.apply( + arr, goog.array.slice(arguments, 1)); +}; + + +/** + * Returns a new array from a segment of an array. This is a generic version of + * Array slice. This means that it might work on other objects similar to + * arrays, such as the arguments object. + * + * @param {Array|goog.array.ArrayLike} arr The array from + * which to copy a segment. + * @param {number} start The index of the first element to copy. + * @param {number=} opt_end The index after the last element to copy. + * @return {!Array} A new array containing the specified segment of the + * original array. + * @template T + */ +goog.array.slice = function(arr, start, opt_end) { + goog.asserts.assert(arr.length != null); + + // passing 1 arg to slice is not the same as passing 2 where the second is + // null or undefined (in that case the second argument is treated as 0). + // we could use slice on the arguments object and then use apply instead of + // testing the length + if (arguments.length <= 2) { + return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start); + } else { + return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end); + } +}; + + +/** + * Removes all duplicates from an array (retaining only the first + * occurrence of each array element). This function modifies the + * array in place and doesn't change the order of the non-duplicate items. + * + * For objects, duplicates are identified as having the same unique ID as + * defined by {@link goog.getUid}. + * + * Alternatively you can specify a custom hash function that returns a unique + * value for each item in the array it should consider unique. + * + * Runtime: N, + * Worstcase space: 2N (no dupes) + * + * @param {Array|goog.array.ArrayLike} arr The array from which to remove + * duplicates. + * @param {Array=} opt_rv An optional array in which to return the results, + * instead of performing the removal inplace. If specified, the original + * array will remain unchanged. + * @param {function(T):string=} opt_hashFn An optional function to use to + * apply to every item in the array. This function should return a unique + * value for each item in the array it should consider unique. + * @template T + */ +goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { + var returnArray = opt_rv || arr; + var defaultHashFn = function(item) { + // Prefix each type with a single character representing the type to + // prevent conflicting keys (e.g. true and 'true'). + return goog.isObject(current) ? 'o' + goog.getUid(current) : + (typeof current).charAt(0) + current; + }; + var hashFn = opt_hashFn || defaultHashFn; + + var seen = {}, cursorInsert = 0, cursorRead = 0; + while (cursorRead < arr.length) { + var current = arr[cursorRead++]; + var key = hashFn(current); + if (!Object.prototype.hasOwnProperty.call(seen, key)) { + seen[key] = true; + returnArray[cursorInsert++] = current; + } + } + returnArray.length = cursorInsert; +}; + + +/** + * Searches the specified array for the specified target using the binary + * search algorithm. If no opt_compareFn is specified, elements are compared + * using goog.array.defaultCompare, which compares the elements + * using the built in < and > operators. This will produce the expected + * behavior for homogeneous arrays of String(s) and Number(s). The array + * specified must be sorted in ascending order (as defined by the + * comparison function). If the array is not sorted, results are undefined. + * If the array contains multiple instances of the specified target value, any + * of these instances may be found. + * + * Runtime: O(log n) + * + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {TARGET} target The sought value. + * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @template TARGET, VALUE + */ +goog.array.binarySearch = function(arr, target, opt_compareFn) { + return goog.array.binarySearch_(arr, + opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */, + target); +}; + + +/** + * Selects an index in the specified array using the binary search algorithm. + * The evaluator receives an element and determines whether the desired index + * is before, at, or after it. The evaluator must be consistent (formally, + * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign) + * must be monotonically non-increasing). + * + * Runtime: O(log n) + * + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {function(this:THIS, VALUE, number, ?): number} evaluator + * Evaluator function that receives 3 arguments (the element, the index and + * the array). Should return a negative number, zero, or a positive number + * depending on whether the desired index is before, at, or after the + * element passed to it. + * @param {THIS=} opt_obj The object to be used as the value of 'this' + * within evaluator. + * @return {number} Index of the leftmost element matched by the evaluator, if + * such exists; otherwise (-(insertion point) - 1). The insertion point is + * the index of the first element for which the evaluator returns negative, + * or arr.length if no such element exists. The return value is non-negative + * iff a match is found. + * @template THIS, VALUE + */ +goog.array.binarySelect = function(arr, evaluator, opt_obj) { + return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */, + undefined /* opt_target */, opt_obj); +}; + + +/** + * Implementation of a binary search algorithm which knows how to use both + * comparison functions and evaluators. If an evaluator is provided, will call + * the evaluator with the given optional data object, conforming to the + * interface defined in binarySelect. Otherwise, if a comparison function is + * provided, will call the comparison function against the given data object. + * + * This implementation purposefully does not use goog.bind or goog.partial for + * performance reasons. + * + * Runtime: O(log n) + * + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {function(TARGET, VALUE): number| + * function(this:THIS, VALUE, number, ?): number} compareFn Either an + * evaluator or a comparison function, as defined by binarySearch + * and binarySelect above. + * @param {boolean} isEvaluator Whether the function is an evaluator or a + * comparison function. + * @param {TARGET=} opt_target If the function is a comparison function, then + * this is the target to binary search for. + * @param {THIS=} opt_selfObj If the function is an evaluator, this is an + * optional this object for the evaluator. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @template THIS, VALUE, TARGET + * @private + */ +goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, + opt_selfObj) { + var left = 0; // inclusive + var right = arr.length; // exclusive + var found; + while (left < right) { + var middle = (left + right) >> 1; + var compareResult; + if (isEvaluator) { + compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr); + } else { + compareResult = compareFn(opt_target, arr[middle]); + } + if (compareResult > 0) { + left = middle + 1; + } else { + right = middle; + // We are looking for the lowest index so we can't return immediately. + found = !compareResult; + } + } + // left is the index if found, or the insertion point otherwise. + // ~left is a shorthand for -left - 1. + return found ? left : ~left; +}; + + +/** + * Sorts the specified array into ascending order. If no opt_compareFn is + * specified, elements are compared using + * goog.array.defaultCompare, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s), unlike the native sort, + * but will give unpredictable results for heterogenous lists of strings and + * numbers with different numbers of digits. + * + * This sort is not guaranteed to be stable. + * + * Runtime: Same as Array.prototype.sort + * + * @param {Array} arr The array to be sorted. + * @param {?function(T,T):number=} opt_compareFn Optional comparison + * function by which the + * array is to be ordered. Should take 2 arguments to compare, and return a + * negative number, zero, or a positive number depending on whether the + * first argument is less than, equal to, or greater than the second. + * @template T + */ +goog.array.sort = function(arr, opt_compareFn) { + // TODO(arv): Update type annotation since null is not accepted. + arr.sort(opt_compareFn || goog.array.defaultCompare); +}; + + +/** + * Sorts the specified array into ascending order in a stable way. If no + * opt_compareFn is specified, elements are compared using + * goog.array.defaultCompare, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s). + * + * Runtime: Same as Array.prototype.sort, plus an additional + * O(n) overhead of copying the array twice. + * + * @param {Array} arr The array to be sorted. + * @param {?function(T, T): number=} opt_compareFn Optional comparison function + * by which the array is to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T + */ +goog.array.stableSort = function(arr, opt_compareFn) { + for (var i = 0; i < arr.length; i++) { + arr[i] = {index: i, value: arr[i]}; + } + var valueCompareFn = opt_compareFn || goog.array.defaultCompare; + function stableCompareFn(obj1, obj2) { + return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index; + }; + goog.array.sort(arr, stableCompareFn); + for (var i = 0; i < arr.length; i++) { + arr[i] = arr[i].value; + } +}; + + +/** + * Sort the specified array into ascending order based on item keys + * returned by the specified key function. + * If no opt_compareFn is specified, the keys are compared in ascending order + * using goog.array.defaultCompare. + * + * Runtime: O(S(f(n)), where S is runtime of goog.array.sort + * and f(n) is runtime of the key function. + * + * @param {Array} arr The array to be sorted. + * @param {function(T): K} keyFn Function taking array element and returning + * a key used for sorting this element. + * @param {?function(K, K): number=} opt_compareFn Optional comparison function + * by which the keys are to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T + * @template K + */ +goog.array.sortByKey = function(arr, keyFn, opt_compareFn) { + var keyCompareFn = opt_compareFn || goog.array.defaultCompare; + goog.array.sort(arr, function(a, b) { + return keyCompareFn(keyFn(a), keyFn(b)); + }); +}; + + +/** + * Sorts an array of objects by the specified object key and compare + * function. If no compare function is provided, the key values are + * compared in ascending order using goog.array.defaultCompare. + * This won't work for keys that get renamed by the compiler. So use + * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}. + * @param {Array} arr An array of objects to sort. + * @param {string} key The object key to sort by. + * @param {Function=} opt_compareFn The function to use to compare key + * values. + */ +goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) { + goog.array.sortByKey(arr, + function(obj) { return obj[key]; }, + opt_compareFn); +}; + + +/** + * Tells if the array is sorted. + * @param {!Array} arr The array. + * @param {?function(T,T):number=} opt_compareFn Function to compare the + * array elements. + * Should take 2 arguments to compare, and return a negative number, zero, + * or a positive number depending on whether the first argument is less + * than, equal to, or greater than the second. + * @param {boolean=} opt_strict If true no equal elements are allowed. + * @return {boolean} Whether the array is sorted. + * @template T + */ +goog.array.isSorted = function(arr, opt_compareFn, opt_strict) { + var compare = opt_compareFn || goog.array.defaultCompare; + for (var i = 1; i < arr.length; i++) { + var compareResult = compare(arr[i - 1], arr[i]); + if (compareResult > 0 || compareResult == 0 && opt_strict) { + return false; + } + } + return true; +}; + + +/** + * Compares two arrays for equality. Two arrays are considered equal if they + * have the same length and their corresponding elements are equal according to + * the comparison function. + * + * @param {goog.array.ArrayLike} arr1 The first array to compare. + * @param {goog.array.ArrayLike} arr2 The second array to compare. + * @param {Function=} opt_equalsFn Optional comparison function. + * Should take 2 arguments to compare, and return true if the arguments + * are equal. Defaults to {@link goog.array.defaultCompareEquality} which + * compares the elements using the built-in '===' operator. + * @return {boolean} Whether the two arrays are equal. + */ +goog.array.equals = function(arr1, arr2, opt_equalsFn) { + if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) || + arr1.length != arr2.length) { + return false; + } + var l = arr1.length; + var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality; + for (var i = 0; i < l; i++) { + if (!equalsFn(arr1[i], arr2[i])) { + return false; + } + } + return true; +}; + + +/** + * 3-way array compare function. + * @param {!Array|!goog.array.ArrayLike} arr1 The first array to + * compare. + * @param {!Array|!goog.array.ArrayLike} arr2 The second array to + * compare. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is to be ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {number} Negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template VALUE + */ +goog.array.compare3 = function(arr1, arr2, opt_compareFn) { + var compare = opt_compareFn || goog.array.defaultCompare; + var l = Math.min(arr1.length, arr2.length); + for (var i = 0; i < l; i++) { + var result = compare(arr1[i], arr2[i]); + if (result != 0) { + return result; + } + } + return goog.array.defaultCompare(arr1.length, arr2.length); +}; + + +/** + * Compares its two arguments for order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is less than, equal to, or greater than the second, + * respectively. + * @template VALUE + */ +goog.array.defaultCompare = function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; +}; + + +/** + * Compares its two arguments for inverse order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is greater than, equal to, or less than the second, + * respectively. + * @template VALUE + */ +goog.array.inverseDefaultCompare = function(a, b) { + return -goog.array.defaultCompare(a, b); +}; + + +/** + * Compares its two arguments for equality, using the built in === operator. + * @param {*} a The first object to compare. + * @param {*} b The second object to compare. + * @return {boolean} True if the two arguments are equal, false otherwise. + */ +goog.array.defaultCompareEquality = function(a, b) { + return a === b; +}; + + +/** + * Inserts a value into a sorted array. The array is not modified if the + * value is already present. + * @param {Array|goog.array.ArrayLike} array The array to modify. + * @param {VALUE} value The object to insert. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was inserted. + * @template VALUE + */ +goog.array.binaryInsert = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + if (index < 0) { + goog.array.insertAt(array, value, -(index + 1)); + return true; + } + return false; +}; + + +/** + * Removes a value from a sorted array. + * @param {!Array|!goog.array.ArrayLike} array The array to modify. + * @param {VALUE} value The object to remove. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was removed. + * @template VALUE + */ +goog.array.binaryRemove = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + return (index >= 0) ? goog.array.removeAt(array, index) : false; +}; + + +/** + * Splits an array into disjoint buckets according to a splitting function. + * @param {Array} array The array. + * @param {function(this:S, T,number,Array):?} sorter Function to call for + * every element. This takes 3 arguments (the element, the index and the + * array) and must return a valid object key (a string, number, etc), or + * undefined, if that object should not be placed in a bucket. + * @param {S=} opt_obj The object to be used as the value of 'this' within + * sorter. + * @return {!Object} An object, with keys being all of the unique return values + * of sorter, and values being arrays containing the items for + * which the splitter returned that key. + * @template T,S + */ +goog.array.bucket = function(array, sorter, opt_obj) { + var buckets = {}; + + for (var i = 0; i < array.length; i++) { + var value = array[i]; + var key = sorter.call(opt_obj, value, i, array); + if (goog.isDef(key)) { + // Push the value to the right bucket, creating it if necessary. + var bucket = buckets[key] || (buckets[key] = []); + bucket.push(value); + } + } + + return buckets; +}; + + +/** + * Creates a new object built from the provided array and the key-generation + * function. + * @param {Array|goog.array.ArrayLike} arr Array or array like object over + * which to iterate whose elements will be the values in the new object. + * @param {?function(this:S, T, number, ?) : string} keyFunc The function to + * call for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a string that will be used as the + * key for the element in the new object. If the function returns the same + * key for more than one element, the value for that key is + * implementation-defined. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within keyFunc. + * @return {!Object} The new object. + * @template T,S + */ +goog.array.toObject = function(arr, keyFunc, opt_obj) { + var ret = {}; + goog.array.forEach(arr, function(element, index) { + ret[keyFunc.call(opt_obj, element, index, arr)] = element; + }); + return ret; +}; + + +/** + * Creates a range of numbers in an arithmetic progression. + * + * Range takes 1, 2, or 3 arguments: + *
+ * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
+ * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
+ * range(-2, -5, -1) produces [-2, -3, -4]
+ * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
+ * 
+ * + * @param {number} startOrEnd The starting value of the range if an end argument + * is provided. Otherwise, the start value is 0, and this is the end value. + * @param {number=} opt_end The optional end value of the range. + * @param {number=} opt_step The step size between range values. Defaults to 1 + * if opt_step is undefined or 0. + * @return {!Array} An array of numbers for the requested range. May be + * an empty array if adding the step would not converge toward the end + * value. + */ +goog.array.range = function(startOrEnd, opt_end, opt_step) { + var array = []; + var start = 0; + var end = startOrEnd; + var step = opt_step || 1; + if (opt_end !== undefined) { + start = startOrEnd; + end = opt_end; + } + + if (step * (end - start) < 0) { + // Sign mismatch: start + step will never reach the end value. + return []; + } + + if (step > 0) { + for (var i = start; i < end; i += step) { + array.push(i); + } + } else { + for (var i = start; i > end; i += step) { + array.push(i); + } + } + return array; +}; + + +/** + * Returns an array consisting of the given value repeated N times. + * + * @param {VALUE} value The value to repeat. + * @param {number} n The repeat count. + * @return {!Array} An array with the repeated value. + * @template VALUE + */ +goog.array.repeat = function(value, n) { + var array = []; + for (var i = 0; i < n; i++) { + array[i] = value; + } + return array; +}; + + +/** + * Returns an array consisting of every argument with all arrays + * expanded in-place recursively. + * + * @param {...*} var_args The values to flatten. + * @return {!Array} An array containing the flattened values. + */ +goog.array.flatten = function(var_args) { + var CHUNK_SIZE = 8192; + + var result = []; + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (goog.isArray(element)) { + for (var c = 0; c < element.length; c += CHUNK_SIZE) { + var chunk = goog.array.slice(element, c, c + CHUNK_SIZE); + var recurseResult = goog.array.flatten.apply(null, chunk); + for (var r = 0; r < recurseResult.length; r++) { + result.push(recurseResult[r]); + } + } + } else { + result.push(element); + } + } + return result; +}; + + +/** + * Rotates an array in-place. After calling this method, the element at + * index i will be the element previously at index (i - n) % + * array.length, for all values of i between 0 and array.length - 1, + * inclusive. + * + * For example, suppose list comprises [t, a, n, k, s]. After invoking + * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k]. + * + * @param {!Array} array The array to rotate. + * @param {number} n The amount to rotate. + * @return {!Array} The array. + * @template T + */ +goog.array.rotate = function(array, n) { + goog.asserts.assert(array.length != null); + + if (array.length) { + n %= array.length; + if (n > 0) { + goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n)); + } else if (n < 0) { + goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n)); + } + } + return array; +}; + + +/** + * Moves one item of an array to a new position keeping the order of the rest + * of the items. Example use case: keeping a list of JavaScript objects + * synchronized with the corresponding list of DOM elements after one of the + * elements has been dragged to a new position. + * @param {!(Array|Arguments|{length:number})} arr The array to modify. + * @param {number} fromIndex Index of the item to move between 0 and + * {@code arr.length - 1}. + * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. + */ +goog.array.moveItem = function(arr, fromIndex, toIndex) { + goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length); + goog.asserts.assert(toIndex >= 0 && toIndex < arr.length); + // Remove 1 item at fromIndex. + var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1); + // Insert the removed item at toIndex. + goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]); + // We don't use goog.array.insertAt and goog.array.removeAt, because they're + // significantly slower than splice. +}; + + +/** + * Creates a new array for which the element at position i is an array of the + * ith element of the provided arrays. The returned array will only be as long + * as the shortest array provided; additional values are ignored. For example, + * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]]. + * + * This is similar to the zip() function in Python. See {@link + * http://docs.python.org/library/functions.html#zip} + * + * @param {...!goog.array.ArrayLike} var_args Arrays to be combined. + * @return {!Array>} A new array of arrays created from + * provided arrays. + */ +goog.array.zip = function(var_args) { + if (!arguments.length) { + return []; + } + var result = []; + for (var i = 0; true; i++) { + var value = []; + for (var j = 0; j < arguments.length; j++) { + var arr = arguments[j]; + // If i is larger than the array length, this is the shortest array. + if (i >= arr.length) { + return result; + } + value.push(arr[i]); + } + result.push(value); + } +}; + + +/** + * Shuffles the values in the specified array using the Fisher-Yates in-place + * shuffle (also known as the Knuth Shuffle). By default, calls Math.random() + * and so resets the state of that random number generator. Similarly, may reset + * the state of the any other specified random number generator. + * + * Runtime: O(n) + * + * @param {!Array} arr The array to be shuffled. + * @param {function():number=} opt_randFn Optional random function to use for + * shuffling. + * Takes no arguments, and returns a random number on the interval [0, 1). + * Defaults to Math.random() using JavaScript's built-in Math library. + */ +goog.array.shuffle = function(arr, opt_randFn) { + var randFn = opt_randFn || Math.random; + + for (var i = arr.length - 1; i > 0; i--) { + // Choose a random array index in [0, i] (inclusive with i). + var j = Math.floor(randFn() * (i + 1)); + + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +}; + + +/** + * Returns a new array of elements from arr, based on the indexes of elements + * provided by index_arr. For example, the result of index copying + * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c']. + * + * @param {!Array} arr The array to get a indexed copy from. + * @param {!Array} index_arr An array of indexes to get from arr. + * @return {!Array} A new array of elements from arr in index_arr order. + * @template T + */ +goog.array.copyByIndex = function(arr, index_arr) { + var result = []; + goog.array.forEach(index_arr, function(index) { + result.push(arr[index]); + }); + return result; +}; diff --git a/mapbox-gl/closure-library/closure/goog/array/array_test.html b/mapbox-gl/closure-library/closure/goog/array/array_test.html new file mode 100644 index 0000000000..654d8f3da7 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/array/array_test.html @@ -0,0 +1,24 @@ + + + + + + + Closure Unit Tests - goog.array + + + + + +
+
+ + diff --git a/mapbox-gl/closure-library/closure/goog/array/array_test.js b/mapbox-gl/closure-library/closure/goog/array/array_test.js new file mode 100644 index 0000000000..827c0f9e1f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/array/array_test.js @@ -0,0 +1,1786 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.arrayTest'); +goog.setTestOnly('goog.arrayTest'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); + +function testArrayLast() { + assertEquals(goog.array.last([1, 2, 3]), 3); + assertEquals(goog.array.last([1]), 1); + assertUndefined(goog.array.last([])); +} + +function testArrayLastWhenDeleted() { + var a = [1, 2, 3]; + delete a[2]; + assertUndefined(goog.array.last(a)); +} + +function testArrayIndexOf() { + assertEquals(goog.array.indexOf([0, 1, 2, 3], 1), 1); + assertEquals(goog.array.indexOf([0, 1, 1, 1], 1), 1); + assertEquals(goog.array.indexOf([0, 1, 2, 3], 4), -1); + assertEquals(goog.array.indexOf([0, 1, 2, 3], 1, 1), 1); + assertEquals(goog.array.indexOf([0, 1, 2, 3], 1, 2), -1); + assertEquals(goog.array.indexOf([0, 1, 2, 3], 1, -3), 1); + assertEquals(goog.array.indexOf([0, 1, 2, 3], 1, -2), -1); +} + +function testArrayIndexOfOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + assertEquals(goog.array.indexOf(a, undefined), -1); +} + +function testArrayIndexOfString() { + assertEquals(goog.array.indexOf('abcd', 'd'), 3); + assertEquals(goog.array.indexOf('abbb', 'b', 2), 2); + assertEquals(goog.array.indexOf('abcd', 'e'), -1); + assertEquals(goog.array.indexOf('abcd', 'cd'), -1); + assertEquals(goog.array.indexOf('0123', 1), -1); +} + +function testArrayLastIndexOf() { + assertEquals(goog.array.lastIndexOf([0, 1, 2, 3], 1), 1); + assertEquals(goog.array.lastIndexOf([0, 1, 1, 1], 1), 3); + assertEquals(goog.array.lastIndexOf([0, 1, 1, 1], 1, 2), 2); +} + +function testArrayLastIndexOfOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + assertEquals(goog.array.lastIndexOf(a, undefined), -1); +} + +function testArrayLastIndexOfString() { + assertEquals(goog.array.lastIndexOf('abcd', 'b'), 1); + assertEquals(goog.array.lastIndexOf('abbb', 'b'), 3); + assertEquals(goog.array.lastIndexOf('abbb', 'b', 2), 2); + assertEquals(goog.array.lastIndexOf('abcd', 'cd'), -1); + assertEquals(goog.array.lastIndexOf('0123', 1), -1); +} + +function testArrayForEachBasic() { + var s = ''; + var a = ['a', 'b', 'c', 'd']; + goog.array.forEach(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('Index is not a number', 'number', typeof index); + s += val + index; + }); + assertEquals('a0b1c2d3', s); +} + +function testArrayForEachWithEmptyArray() { + var a = new Array(100); + goog.array.forEach(a, function(val, index, a2) { + fail('The function should not be called since no values were assigned.'); + }); +} + +function testArrayForEachWithOnlySomeValuesAsigned() { + var count = 0; + var a = new Array(1000); + a[100] = undefined; + goog.array.forEach(a, function(val, index, a2) { + assertEquals(100, index); + count++; + }); + assertEquals('Should only call function when a value of array was assigned.', + 1, count); +} + +function testArrayForEachWithArrayLikeObject() { + var counter = goog.testing.recordFunction(); + var a = { + 'length': 1, + '0': 0, + '100': 100, + '101': 102 + }; + goog.array.forEach(a, counter); + assertEquals('Number of calls should not exceed the value of its length', 1, + counter.getCallCount()); +} + +function testArrayForEachOmitsDeleted() { + var s = ''; + var a = ['a', 'b', 'c', 'd']; + delete a[1]; + delete a[3]; + goog.array.forEach(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof index); + s += val + index; + }); + assertEquals('a0c2', s); +} + +function testArrayForEachScope() { + var scope = {}; + var a = ['a', 'b', 'c', 'd']; + goog.array.forEach(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof index); + assertEquals(this, scope); + }, scope); +} + +function testArrayForEachRight() { + var s = ''; + var a = ['a', 'b', 'c', 'd']; + goog.array.forEachRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof index); + s += val + String(index); + }); + assertEquals('d3c2b1a0', s); +} + +function testArrayForEachRightOmitsDeleted() { + var s = ''; + var a = ['a', 'b', 'c', 'd']; + delete a[1]; + delete a[3]; + goog.array.forEachRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof index); + assertEquals('string', typeof val); + s += val + String(index); + }); + assertEquals('c2a0', s); +} + +function testArrayFilter() { + var a = [0, 1, 2, 3]; + a = goog.array.filter(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertArrayEquals([2, 3], a); +} + +function testArrayFilterOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + a = goog.array.filter(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertArrayEquals([2], a); +} + +function testArrayFilterPreservesValues() { + var a = [0, 1, 2, 3]; + a = goog.array.filter(a, function(val, index, a2) { + assertEquals(a, a2); + // sometimes functions might be evil and do something like this, but we + // should still use the original values when returning the filtered array + a2[index] = a2[index] - 1; + return a2[index] >= 1; + }); + assertArrayEquals([2, 3], a); +} + +function testArrayMap() { + var a = [0, 1, 2, 3]; + var result = goog.array.map(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val * val; + }); + assertArrayEquals([0, 1, 4, 9], result); +} + +function testArrayMapOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var result = goog.array.map(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val * val; + }); + var expected = [0, 1, 4, 9]; + delete expected[1]; + delete expected[3]; + + assertArrayEquals(expected, result); + assertFalse('1' in result); + assertFalse('3' in result); +} + +function testArrayReduce() { + var a = [0, 1, 2, 3]; + assertEquals(6, goog.array.reduce(a, function(rval, val, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + return rval + val; + }, 0)); + + var scope = { + last: 0, + testFn: function(r, v, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + var l = this.last; + this.last = r + v; + return this.last + l; + } + }; + + assertEquals(10, goog.array.reduce(a, scope.testFn, 0, scope)); +} + +function testArrayReduceOmitDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + assertEquals(2, goog.array.reduce(a, function(rval, val, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + return rval + val; + }, 0)); + + var scope = { + last: 0, + testFn: function(r, v, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + var l = this.last; + this.last = r + v; + return this.last + l; + } + }; + + assertEquals(2, goog.array.reduce(a, scope.testFn, 0, scope)); +} + +function testArrayReduceRight() { + var a = [0, 1, 2, 3, 4]; + assertEquals('43210', goog.array.reduceRight(a, function(rval, val, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + return rval + val; + }, '')); + + var scope = { + last: '', + testFn: function(r, v, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + var l = this.last; + this.last = v; + return r + v + l; + } + }; + + a = ['a', 'b', 'c']; + assertEquals('_cbcab', goog.array.reduceRight(a, scope.testFn, '_', scope)); +} + +function testArrayReduceRightOmitsDeleted() { + var a = [0, 1, 2, 3, 4]; + delete a[1]; + delete a[4]; + assertEquals('320', goog.array.reduceRight(a, function(rval, val, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + return rval + val; + }, '')); + + scope = { + last: '', + testFn: function(r, v, i, arr) { + assertEquals('number', typeof i); + assertEquals(a, arr); + var l = this.last; + this.last = v; + return r + v + l; + } + }; + + a = ['a', 'b', 'c', 'd']; + delete a[1]; + delete a[3]; + assertEquals('_cac', goog.array.reduceRight(a, scope.testFn, '_', scope)); +} + +function testArrayFind() { + var a = [0, 1, 2, 3]; + var b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertEquals(2, b); + + b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertNull(b); + + a = 'abCD'; + b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'A' && val <= 'Z'; + }); + assertEquals('C', b); + + a = 'abcd'; + b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'A' && val <= 'Z'; + }); + assertNull(b); +} + +function testArrayFindOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + + assertEquals(2, b); + b = goog.array.find(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertNull(b); +} + +function testArrayFindIndex() { + var a = [0, 1, 2, 3]; + var b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertEquals(2, b); + + b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertEquals(-1, b); + + a = 'abCD'; + b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'A' && val <= 'Z'; + }); + assertEquals(2, b); + + a = 'abcd'; + b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'A' && val <= 'Z'; + }); + assertEquals(-1, b); +} + +function testArrayFindIndexOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertEquals(2, b); + + b = goog.array.findIndex(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertEquals(-1, b); +} + +function testArrayFindRight() { + var a = [0, 1, 2, 3]; + var b = goog.array.findRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val < 3; + }); + assertEquals(2, b); + b = goog.array.findRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertNull(b); +} + +function testArrayFindRightOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.findRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val < 3; + }); + assertEquals(2, b); + b = goog.array.findRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertNull(b); +} + +function testArrayFindIndexRight() { + var a = [0, 1, 2, 3]; + var b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val < 3; + }); + assertEquals(2, b); + + b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertEquals(-1, b); + + a = 'abCD'; + b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'a' && val <= 'z'; + }); + assertEquals(1, b); + + a = 'abcd'; + b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 'A' && val <= 'Z'; + }); + assertEquals(-1, b); +} + +function testArrayFindIndexRightOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val < 3; + }); + assertEquals(2, b); + b = goog.array.findIndexRight(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertEquals(-1, b); +} + +function testArraySome() { + var a = [0, 1, 2, 3]; + var b = goog.array.some(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertTrue(b); + b = goog.array.some(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertFalse(b); +} + +function testArraySomeOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.some(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertTrue(b); + b = goog.array.some(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertFalse(b); +} + +function testArrayEvery() { + var a = [0, 1, 2, 3]; + var b = goog.array.every(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val >= 0; + }); + assertTrue(b); + b = goog.array.every(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertFalse(b); +} + +function testArrayEveryOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + var b = goog.array.every(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val >= 0; + }); + assertTrue(b); + b = goog.array.every(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('number', typeof val); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertFalse(b); +} + +function testArrayCount() { + var a = [0, 1, 2, 3, 4]; + var context = {}; + assertEquals(3, goog.array.count(a, function(element, index, array) { + assertTrue(goog.isNumber(index)); + assertEquals(a, array); + assertEquals(context, this); + return element % 2 == 0; + }, context)); + + delete a[2]; + assertEquals('deleted element is ignored', 4, + goog.array.count(a, function() { + return true; + })); +} + +function testArrayContains() { + var a = [0, 1, 2, 3]; + assertTrue('contain, Should contain 3', goog.array.contains(a, 3)); + assertFalse('contain, Should not contain 4', goog.array.contains(a, 4)); + + var s = 'abcd'; + assertTrue('contain, Should contain d', goog.array.contains(s, 'd')); + assertFalse('contain, Should not contain e', goog.array.contains(s, 'e')); +} + +function testArrayContainsOmitsDeleted() { + var a = [0, 1, 2, 3]; + delete a[1]; + delete a[3]; + assertFalse('should not contain undefined', + goog.array.contains(a, undefined)); +} + +function testArrayInsert() { + var a = [0, 1, 2, 3]; + + goog.array.insert(a, 4); + assertEquals('insert, Should append 4', a[4], 4); + goog.array.insert(a, 3); + assertEquals('insert, Should not append 3', a.length, 5); + assertNotEquals('insert, Should not append 3', a[a.length - 1], 3); +} + +function testArrayInsertAt() { + var a = [0, 1, 2, 3]; + + goog.array.insertAt(a, 4, 2); + assertArrayEquals('insertAt, insert in middle', [0, 1, 4, 2, 3], a); + goog.array.insertAt(a, 5, 10); + assertArrayEquals('insertAt, too large value should append', + [0, 1, 4, 2, 3, 5], a); + goog.array.insertAt(a, 6); + assertArrayEquals('insertAt, null/undefined value should insert at 0', + [6, 0, 1, 4, 2, 3, 5], a); + goog.array.insertAt(a, 7, -2); + assertArrayEquals('insertAt, negative values start from end', + [6, 0, 1, 4, 2, 7, 3, 5], a); +} + +function testArrayInsertArrayAt() { + var a = [2, 5]; + goog.array.insertArrayAt(a, [3, 4], 1); + assertArrayEquals('insertArrayAt, insert in middle', [2, 3, 4, 5], a); + goog.array.insertArrayAt(a, [0, 1], 0); + assertArrayEquals('insertArrayAt, insert at beginning', + [0, 1, 2, 3, 4, 5], a); + goog.array.insertArrayAt(a, [6, 7], 6); + assertArrayEquals('insertArrayAt, insert at end', + [0, 1, 2, 3, 4, 5, 6, 7], a); + goog.array.insertArrayAt(a, ['x'], 4); + assertArrayEquals('insertArrayAt, insert one element', + [0, 1, 2, 3, 'x', 4, 5, 6, 7], a); + goog.array.insertArrayAt(a, [], 4); + assertArrayEquals('insertArrayAt, insert 0 elements', + [0, 1, 2, 3, 'x', 4, 5, 6, 7], a); + goog.array.insertArrayAt(a, ['y', 'z']); + assertArrayEquals('insertArrayAt, undefined value should insert at 0', + ['y', 'z', 0, 1, 2, 3, 'x', 4, 5, 6, 7], a); + goog.array.insertArrayAt(a, ['a'], null); + assertArrayEquals('insertArrayAt, null value should insert at 0', + ['a', 'y', 'z', 0, 1, 2, 3, 'x', 4, 5, 6, 7], a); + goog.array.insertArrayAt(a, ['b'], 100); + assertArrayEquals('insertArrayAt, too large value should append', + ['a', 'y', 'z', 0, 1, 2, 3, 'x', 4, 5, 6, 7, 'b'], a); + goog.array.insertArrayAt(a, ['c', 'd'], -2); + assertArrayEquals('insertArrayAt, negative values start from end', + ['a', 'y', 'z', 0, 1, 2, 3, 'x', 4, 5, 6, 'c', 'd', 7, 'b'], a); +} + +function testArrayInsertBefore() { + var a = ['a', 'b', 'c', 'd']; + goog.array.insertBefore(a, 'e', 'b'); + assertArrayEquals('insertBefore, with existing element', + ['a', 'e', 'b', 'c', 'd'], a); + goog.array.insertBefore(a, 'f', 'x'); + assertArrayEquals('insertBefore, with non existing element', + ['a', 'e', 'b', 'c', 'd', 'f'], a); +} + +function testArrayRemove() { + var a = ['a', 'b', 'c', 'd']; + goog.array.remove(a, 'c'); + assertArrayEquals('remove, remove existing element', ['a', 'b', 'd'], a); + goog.array.remove(a, 'x'); + assertArrayEquals('remove, remove non existing element', ['a', 'b', 'd'], a); +} + +function testArrayRemoveAt() { + var a = [0, 1, 2, 3]; + goog.array.removeAt(a, 2); + assertArrayEquals('removeAt, remove existing index', [0, 1, 3], a); + a = [0, 1, 2, 3]; + goog.array.removeAt(a, 10); + assertArrayEquals('removeAt, remove non existing index', [0, 1, 2, 3], a); + a = [0, 1, 2, 3]; + goog.array.removeAt(a, -2); + assertArrayEquals('removeAt, remove with negative index', [0, 1, 3], a); +} + +function testArrayRemoveIf() { + var a = [0, 1, 2, 3]; + var b = goog.array.removeIf(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 1; + }); + assertArrayEquals('removeIf, remove existing element', [0, 1, 3], a); + + a = [0, 1, 2, 3]; + var b = goog.array.removeIf(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('index is not a number', 'number', typeof index); + return val > 100; + }); + assertArrayEquals('removeIf, remove non-existing element', [0, 1, 2, 3], a); +} + +function testArrayClone() { + var a = [0, 1, 2, 3]; + var a2 = goog.array.clone(a); + assertArrayEquals('clone, should be equal', a, a2); + + var b = {0: 0, 1: 1, 2: 2, 3: 3, length: 4}; + var b2 = goog.array.clone(b); + for (var i = 0; i < b.length; i++) { + assertEquals('clone, should be equal', b[i], b2[i]); + } +} + +function testToArray() { + var a = [0, 1, 2, 3]; + var a2 = goog.array.toArray(a); + assertArrayEquals('toArray, should be equal', a, a2); + + var b = {0: 0, 1: 1, 2: 2, 3: 3, length: 4}; + var b2 = goog.array.toArray(b); + for (var i = 0; i < b.length; i++) { + assertEquals('toArray, should be equal', b[i], b2[i]); + } +} + +function testToArrayOnNonArrayLike() { + var nonArrayLike = {}; + assertArrayEquals('toArray on non ArrayLike should return an empty array', + [], goog.array.toArray(nonArrayLike)); + + var nonArrayLike2 = {length: 'hello world'}; + assertArrayEquals('toArray on non ArrayLike should return an empty array', + [], goog.array.toArray(nonArrayLike2)); +} + +function testExtend() { + var a = [0, 1]; + goog.array.extend(a, [2, 3]); + var a2 = [0, 1, 2, 3]; + assertArrayEquals('extend, should be equal', a, a2); + + var b = [0, 1]; + goog.array.extend(b, 2); + var b2 = [0, 1, 2]; + assertArrayEquals('extend, should be equal', b, b2); + + a = [0, 1]; + goog.array.extend(a, [2, 3], [4, 5]); + a2 = [0, 1, 2, 3, 4, 5]; + assertArrayEquals('extend, should be equal', a, a2); + + b = [0, 1]; + goog.array.extend(b, 2, 3); + b2 = [0, 1, 2, 3]; + assertArrayEquals('extend, should be equal', b, b2); + + var c = [0, 1]; + goog.array.extend(c, 2, [3, 4], 5, [6]); + var c2 = [0, 1, 2, 3, 4, 5, 6]; + assertArrayEquals('extend, should be equal', c, c2); + + var d = [0, 1]; + var arrayLikeObject = {0: 2, 1: 3, length: 2}; + goog.array.extend(d, arrayLikeObject); + var d2 = [0, 1, 2, 3]; + assertArrayEquals('extend, should be equal', d, d2); + + var e = [0, 1]; + var emptyArrayLikeObject = {length: 0}; + goog.array.extend(e, emptyArrayLikeObject); + assertArrayEquals('extend, should be equal', e, e); + + var f = [0, 1]; + var length3ArrayLikeObject = {0: 2, 1: 4, 2: 8, length: 3}; + goog.array.extend(f, length3ArrayLikeObject, length3ArrayLikeObject); + var f2 = [0, 1, 2, 4, 8, 2, 4, 8]; + assertArrayEquals('extend, should be equal', f2, f); + + var result = []; + var i = 1000000; + var bigArray = Array(i); + while (i--) { + bigArray[i] = i; + } + goog.array.extend(result, bigArray); + assertArrayEquals(bigArray, result); +} + +function testExtendWithArguments() { + function f() { + return arguments; + } + var a = [0]; + var a2 = [0, 1, 2, 3, 4, 5]; + goog.array.extend(a, f(1, 2, 3), f(4, 5)); + assertArrayEquals('extend, should be equal', a, a2); +} + +function testExtendWithQuerySelector() { + var a = [0]; + var d = goog.dom.getElementsByTagNameAndClass('div', 'foo'); + goog.array.extend(a, d); + assertEquals(2, a.length); +} + +function testArraySplice() { + var a = [0, 1, 2, 3]; + goog.array.splice(a, 1, 0, 4); + assertArrayEquals([0, 4, 1, 2, 3], a); + goog.array.splice(a, 1, 1, 5); + assertArrayEquals([0, 5, 1, 2, 3], a); + goog.array.splice(a, 1, 1); + assertArrayEquals([0, 1, 2, 3], a); + // var args + goog.array.splice(a, 1, 1, 4, 5, 6); + assertArrayEquals([0, 4, 5, 6, 2, 3], a); +} + +function testArraySlice() { + var a = [0, 1, 2, 3]; + a = goog.array.slice(a, 1, 3); + assertArrayEquals([1, 2], a); + a = [0, 1, 2, 3]; + a = goog.array.slice(a, 1, 6); + assertArrayEquals('slice, with too large end', [1, 2, 3], a); + a = [0, 1, 2, 3]; + a = goog.array.slice(a, 1, -1); + assertArrayEquals('slice, with negative end', [1, 2], a); + a = [0, 1, 2, 3]; + a = goog.array.slice(a, -2, 3); + assertArrayEquals('slice, with negative start', [2], a); +} + +function assertRemovedDuplicates(expected, original) { + var tempArr = goog.array.clone(original); + goog.array.removeDuplicates(tempArr); + assertArrayEquals(expected, tempArr); +} + +function testRemoveDuplicates() { + assertRemovedDuplicates([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6]); + assertRemovedDuplicates([9, 4, 2, 1, 3, 6, 0, -9], + [9, 4, 2, 4, 4, 2, 9, 1, 3, 6, 0, -9]); + assertRemovedDuplicates(['four', 'one', 'two', 'three', 'THREE'], + ['four', 'one', 'two', 'one', 'three', 'THREE', 'four', 'two']); + assertRemovedDuplicates([], []); + assertRemovedDuplicates( + ['abc', 'hasOwnProperty', 'toString'], + ['abc', 'hasOwnProperty', 'toString', 'abc']); + + var o1 = {}, o2 = {}, o3 = {}, o4 = {}; + assertRemovedDuplicates([o1, o2, o3, o4], [o1, o1, o2, o3, o2, o4]); + + // Mixed object types. + assertRemovedDuplicates([1, '1', 2, '2'], [1, '1', 2, '2']); + assertRemovedDuplicates([true, 'true', false, 'false'], + [true, 'true', false, 'false']); + assertRemovedDuplicates(['foo'], [String('foo'), 'foo']); + assertRemovedDuplicates([12], [Number(12), 12]); + + var obj = {}; + var uid = goog.getUid(obj); + assertRemovedDuplicates([obj, uid], [obj, uid]); +} + +function testRemoveDuplicates_customHashFn() { + var object1 = {key: 'foo'}; + var object2 = {key: 'bar'}; + var dupeObject = {key: 'foo'}; + var array = [object1, object2, dupeObject, 'bar']; + var hashFn = function(object) { + return goog.isObject(object) ? object.key : + (typeof object).charAt(0) + object; + }; + goog.array.removeDuplicates(array, /* opt_rv */ undefined, hashFn); + assertArrayEquals([object1, object2, 'bar'], array); +} + +function testBinaryInsertRemove() { + var makeChecker = function(array, fn, opt_compareFn) { + return function(value, expectResult, expectArray) { + var result = fn(array, value, opt_compareFn); + assertEquals(expectResult, result); + assertArrayEquals(expectArray, array); + } + }; + + var a = []; + var check = makeChecker(a, goog.array.binaryInsert); + check(3, true, [3]); + check(3, false, [3]); + check(1, true, [1, 3]); + check(5, true, [1, 3, 5]); + check(2, true, [1, 2, 3, 5]); + check(2, false, [1, 2, 3, 5]); + + check = makeChecker(a, goog.array.binaryRemove); + check(0, false, [1, 2, 3, 5]); + check(3, true, [1, 2, 5]); + check(1, true, [2, 5]); + check(5, true, [2]); + check(2, true, []); + check(2, false, []); + + // test with custom comparison function, which reverse orders numbers + var revNumCompare = function(a, b) { return b - a; }; + + check = makeChecker(a, goog.array.binaryInsert, revNumCompare); + check(3, true, [3]); + check(3, false, [3]); + check(1, true, [3, 1]); + check(5, true, [5, 3, 1]); + check(2, true, [5, 3, 2, 1]); + check(2, false, [5, 3, 2, 1]); + + check = makeChecker(a, goog.array.binaryRemove, revNumCompare); + check(0, false, [5, 3, 2, 1]); + check(3, true, [5, 2, 1]); + check(1, true, [5, 2]); + check(5, true, [2]); + check(2, true, []); + check(2, false, []); +} + +function testBinarySearch() { + var insertionPoint = function(position) { return -(position + 1)}; + var pos; + + // test default comparison on array of String(s) + var a = ['1000', '9', 'AB', 'ABC', 'ABCABC', 'ABD', 'ABDA', 'B', 'B', 'B', + 'C', 'CA', 'CC', 'ZZZ', 'ab', 'abc', 'abcabc', 'abd', 'abda', 'b', + 'c', 'ca', 'cc', 'zzz']; + + assertEquals('\'1000\' should be found at index 0', 0, + goog.array.binarySearch(a, '1000')); + assertEquals('\'zzz\' should be found at index ' + (a.length - 1), + a.length - 1, goog.array.binarySearch(a, 'zzz')); + assertEquals('\'C\' should be found at index 10', 10, + goog.array.binarySearch(a, 'C')); + pos = goog.array.binarySearch(a, 'B'); + assertTrue('\'B\' should be found at index 7 || 8 || 9', pos == 7 || + pos == 8 || pos == 9); + pos = goog.array.binarySearch(a, '100'); + assertTrue('\'100\' should not be found', pos < 0); + assertEquals('\'100\' should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySearch(a, 'zzz0'); + assertTrue('\'zzz0\' should not be found', pos < 0); + assertEquals('\'zzz0\' should have an insertion point of ' + (a.length), + a.length, insertionPoint(pos)); + pos = goog.array.binarySearch(a, 'BA'); + assertTrue('\'BA\' should not be found', pos < 0); + assertEquals('\'BA\' should have an insertion point of 10', 10, + insertionPoint(pos)); + + // test 0 length array with default comparison + var b = []; + + pos = goog.array.binarySearch(b, 'a'); + assertTrue('\'a\' should not be found', pos < 0); + assertEquals('\'a\' should have an insertion point of 0', 0, + insertionPoint(pos)); + + // test single element array with default lexiographical comparison + var c = ['only item']; + + assertEquals('\'only item\' should be found at index 0', 0, + goog.array.binarySearch(c, 'only item')); + pos = goog.array.binarySearch(c, 'a'); + assertTrue('\'a\' should not be found', pos < 0); + assertEquals('\'a\' should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySearch(c, 'z'); + assertTrue('\'z\' should not be found', pos < 0); + assertEquals('\'z\' should have an insertion point of 1', 1, + insertionPoint(pos)); + + // test default comparison on array of Number(s) + var d = [-897123.9, -321434.58758, -1321.3124, -324, -9, -3, 0, 0, 0, + 0.31255, 5, 142.88888708, 334, 342, 453, 54254]; + + assertEquals('-897123.9 should be found at index 0', 0, + goog.array.binarySearch(d, -897123.9)); + assertEquals('54254 should be found at index ' + (a.length - 1), + d.length - 1, goog.array.binarySearch(d, 54254)); + assertEquals( + '-3 should be found at index 5', 5, goog.array.binarySearch(d, -3)); + pos = goog.array.binarySearch(d, 0); + assertTrue('0 should be found at index 6 || 7 || 8', pos == 6 || + pos == 7 || pos == 8); + pos = goog.array.binarySearch(d, -900000); + assertTrue('-900000 should not be found', pos < 0); + assertEquals('-900000 should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySearch(d, '54255'); + assertTrue('54255 should not be found', pos < 0); + assertEquals('54255 should have an insertion point of ' + (d.length), + d.length, insertionPoint(pos)); + pos = goog.array.binarySearch(d, 1.1); + assertTrue('1.1 should not be found', pos < 0); + assertEquals('1.1 should have an insertion point of 10', 10, + insertionPoint(pos)); + + // test with custom comparison function, which reverse orders numbers + var revNumCompare = function(a, b) { return b - a; }; + + var e = [54254, 453, 342, 334, 142.88888708, 5, 0.31255, 0, 0, 0, -3, + -9, -324, -1321.3124, -321434.58758, -897123.9]; + + assertEquals('54254 should be found at index 0', 0, + goog.array.binarySearch(e, 54254, revNumCompare)); + assertEquals('-897123.9 should be found at index ' + (e.length - 1), + e.length - 1, goog.array.binarySearch(e, -897123.9, revNumCompare)); + assertEquals('-3 should be found at index 10', 10, + goog.array.binarySearch(e, -3, revNumCompare)); + pos = goog.array.binarySearch(e, 0, revNumCompare); + assertTrue('0 should be found at index || 10', pos == 7 || + pos == 9 || pos == 10); + pos = goog.array.binarySearch(e, 54254.1, revNumCompare); + assertTrue('54254.1 should not be found', pos < 0); + assertEquals('54254.1 should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySearch(e, -897124, revNumCompare); + assertTrue('-897124 should not be found', pos < 0); + assertEquals('-897124 should have an insertion point of ' + (e.length), + e.length, insertionPoint(pos)); + pos = goog.array.binarySearch(e, 1.1, revNumCompare); + assertTrue('1.1 should not be found', pos < 0); + assertEquals('1.1 should have an insertion point of 6', 6, + insertionPoint(pos)); + + // test 0 length array with custom comparison function + var f = []; + + pos = goog.array.binarySearch(f, 0, revNumCompare); + assertTrue('0 should not be found', pos < 0); + assertEquals('0 should have an insertion point of 0', 0, + insertionPoint(pos)); + + // test single element array with custom comparison function + var g = [1]; + + assertEquals('1 should be found at index 0', 0, goog.array.binarySearch(g, 1, + revNumCompare)); + pos = goog.array.binarySearch(g, 2, revNumCompare); + assertTrue('2 should not be found', pos < 0); + assertEquals('2 should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySearch(g, 0, revNumCompare); + assertTrue('0 should not be found', pos < 0); + assertEquals('0 should have an insertion point of 1', 1, + insertionPoint(pos)); + + assertEquals('binarySearch should find the index of the first 0', + 0, goog.array.binarySearch([0, 0, 1], 0)); + assertEquals('binarySearch should find the index of the first 1', + 1, goog.array.binarySearch([0, 1, 1], 1)); +} + + +function testBinarySearchPerformance() { + // Ensure that Array#slice, Function#apply and Function#call are not called + // from within binarySearch, since they have performance implications in IE. + + var propertyReplacer = new goog.testing.PropertyReplacer(); + propertyReplacer.replace(Array.prototype, 'slice', function() { + fail('Should not call Array#slice from binary search.'); + }); + propertyReplacer.replace(Function.prototype, 'apply', function() { + fail('Should not call Function#apply from binary search.'); + }); + propertyReplacer.replace(Function.prototype, 'call', function() { + fail('Should not call Function#call from binary search.'); + }); + + try { + var array = [1, 5, 7, 11, 13, 16, 19, 24, 28, 31, 33, 36, 40, 50, 52, 55]; + // Test with the default comparison function. + goog.array.binarySearch(array, 48); + // Test with a custom comparison function. + goog.array.binarySearch(array, 13, function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); + } finally { + // The test runner uses Function.prototype.apply to call tearDown in the + // global context so it has to be reset here. + propertyReplacer.reset(); + } +} + + +function testBinarySelect() { + var insertionPoint = function(position) { return -(position + 1)}; + var numbers = [-897123.9, -321434.58758, -1321.3124, -324, -9, -3, 0, 0, 0, + 0.31255, 5, 142.88888708, 334, 342, 453, 54254]; + var objects = goog.array.map(numbers, function(n) {return {n: n};}); + function makeEvaluator(target) { + return function(obj, i, arr) { + assertEquals(objects, arr); + assertEquals(obj, arr[i]); + return target - obj.n; + }; + } + assertEquals('{n:-897123.9} should be found at index 0', 0, + goog.array.binarySelect(objects, makeEvaluator(-897123.9))); + assertEquals('{n:54254} should be found at index ' + (objects.length - 1), + objects.length - 1, + goog.array.binarySelect(objects, makeEvaluator(54254))); + assertEquals('{n:-3} should be found at index 5', 5, + goog.array.binarySelect(objects, makeEvaluator(-3))); + pos = goog.array.binarySelect(objects, makeEvaluator(0)); + assertTrue('{n:0} should be found at index 6 || 7 || 8', pos == 6 || + pos == 7 || pos == 8); + pos = goog.array.binarySelect(objects, makeEvaluator(-900000)); + assertTrue('{n:-900000} should not be found', pos < 0); + assertEquals('{n:-900000} should have an insertion point of 0', 0, + insertionPoint(pos)); + pos = goog.array.binarySelect(objects, makeEvaluator('54255')); + assertTrue('{n:54255} should not be found', pos < 0); + assertEquals('{n:54255} should have an insertion point of ' + + (objects.length), objects.length, insertionPoint(pos)); + pos = goog.array.binarySelect(objects, makeEvaluator(1.1)); + assertTrue('{n:1.1} should not be found', pos < 0); + assertEquals('{n:1.1} should have an insertion point of 10', 10, + insertionPoint(pos)); +} + + +function testArrayEquals() { + // Test argument types. + assertFalse('array == not array', goog.array.equals([], null)); + assertFalse('not array == array', goog.array.equals(null, [])); + assertFalse('not array == not array', goog.array.equals(null, null)); + + // Test with default comparison function. + assertTrue('[] == []', goog.array.equals([], [])); + assertTrue('[1] == [1]', goog.array.equals([1], [1])); + assertTrue('["1"] == ["1"]', goog.array.equals(['1'], ['1'])); + assertFalse('[1] == ["1"]', goog.array.equals([1], ['1'])); + assertTrue('[null] == [null]', goog.array.equals([null], [null])); + assertFalse('[null] == [undefined]', goog.array.equals([null], [undefined])); + assertTrue('[1, 2] == [1, 2]', goog.array.equals([1, 2], [1, 2])); + assertFalse('[1, 2] == [2, 1]', goog.array.equals([1, 2], [2, 1])); + assertFalse('[1, 2] == [1]', goog.array.equals([1, 2], [1])); + assertFalse('[1] == [1, 2]', goog.array.equals([1], [1, 2])); + assertFalse('[{}] == [{}]', goog.array.equals([{}], [{}])); + + // Test with custom comparison function. + var cmp = function(a, b) { return typeof a == typeof b; }; + assertTrue('[] cmp []', goog.array.equals([], [], cmp)); + assertTrue('[1] cmp [1]', goog.array.equals([1], [1], cmp)); + assertTrue('[1] cmp [2]', goog.array.equals([1], [2], cmp)); + assertTrue('["1"] cmp ["1"]', goog.array.equals(['1'], ['1'], cmp)); + assertTrue('["1"] cmp ["2"]', goog.array.equals(['1'], ['2'], cmp)); + assertFalse('[1] cmp ["1"]', goog.array.equals([1], ['1'], cmp)); + assertTrue('[1, 2] cmp [3, 4]', goog.array.equals([1, 2], [3, 4], cmp)); + assertFalse('[1] cmp [2, 3]', goog.array.equals([1], [2, 3], cmp)); + assertTrue('[{}] cmp [{}]', goog.array.equals([{}], [{}], cmp)); + assertTrue('[{}] cmp [{a: 1}]', goog.array.equals([{}], [{a: 1}], cmp)); + + // Test with array-like objects. + assertTrue('[5] == obj [5]', + goog.array.equals([5], {0: 5, length: 1})); + assertTrue('obj [5] == [5]', + goog.array.equals({0: 5, length: 1}, [5])); + assertTrue('["x"] == obj ["x"]', + goog.array.equals(['x'], {0: 'x', length: 1})); + assertTrue('obj ["x"] == ["x"]', + goog.array.equals({0: 'x', length: 1}, ['x'])); + assertTrue('[5] == {0: 5, 1: 6, length: 1}', + goog.array.equals([5], {0: 5, 1: 6, length: 1})); + assertTrue('{0: 5, 1: 6, length: 1} == [5]', + goog.array.equals({0: 5, 1: 6, length: 1}, [5])); + assertFalse('[5, 6] == {0: 5, 1: 6, length: 1}', + goog.array.equals([5, 6], {0: 5, 1: 6, length: 1})); + assertFalse('{0: 5, 1: 6, length: 1}, [5, 6]', + goog.array.equals({0: 5, 1: 6, length: 1}, [5, 6])); + assertTrue('[5, 6] == obj [5, 6]', + goog.array.equals([5, 6], {0: 5, 1: 6, length: 2})); + assertTrue('obj [5, 6] == [5, 6]', + goog.array.equals({0: 5, 1: 6, length: 2}, [5, 6])); + assertFalse('{0: 5, 1: 6} == [5, 6]', + goog.array.equals({0: 5, 1: 6}, [5, 6])); +} + + +function testArrayCompare3Basic() { + assertEquals(0, goog.array.compare3([], [])); + assertEquals(0, goog.array.compare3(['111', '222'], ['111', '222'])); + assertEquals(-1, goog.array.compare3(['111', '222'], ['1111', ''])); + assertEquals(1, goog.array.compare3(['111', '222'], ['111'])); + assertEquals(1, goog.array.compare3(['11', '222', '333'], [])); + assertEquals(-1, goog.array.compare3([], ['11', '222', '333'])); +} + + +function testArrayCompare3ComparatorFn() { + function cmp(a, b) { + return a - b; + }; + assertEquals(0, goog.array.compare3([], [], cmp)); + assertEquals(0, goog.array.compare3([8, 4], [8, 4], cmp)); + assertEquals(-1, goog.array.compare3([4, 3], [5, 0])); + assertEquals(1, goog.array.compare3([6, 2], [6])); + assertEquals(1, goog.array.compare3([1, 2, 3], [])); + assertEquals(-1, goog.array.compare3([], [1, 2, 3])); +} + + +function testSort() { + // Test sorting empty array + var a = []; + goog.array.sort(a); + assertEquals('Sorted empty array is still an empty array (length 0)', 0, + a.length); + + // Test sorting homogenous array of String(s) of length > 1 + var b = ['JUST', '1', 'test', 'Array', 'to', 'test', 'array', 'Sort', + 'about', 'NOW', '!!']; + var bSorted = ['!!', '1', 'Array', 'JUST', 'NOW', 'Sort', 'about', 'array', + 'test', 'test', 'to']; + goog.array.sort(b); + assertArrayEquals(bSorted, b); + + // Test sorting already sorted array of String(s) of length > 1 + goog.array.sort(b); + assertArrayEquals(bSorted, b); + + // Test sorting homogenous array of integer Number(s) of length > 1 + var c = [100, 1, 2000, -1, 0, 1000023, 12312512, -12331, 123, 54325, + -38104783, 93708, 908, -213, -4, 5423, 0]; + var cSorted = [-38104783, -12331, -213, -4, -1, 0, 0, 1, 100, 123, 908, + 2000, 5423, 54325, 93708, 1000023, 12312512]; + goog.array.sort(c); + assertArrayEquals(cSorted, c); + + // Test sorting already sorted array of integer Number(s) of length > 1 + goog.array.sort(c); + assertArrayEquals(cSorted, c); + + // Test sorting homogenous array of Number(s) of length > 1 + var e = [-1321.3124, 0.31255, 54254, 0, 142.88888708, -321434.58758, -324, + 453, 334, -3, 5, -9, 342, -897123.9]; + var eSorted = [-897123.9, -321434.58758, -1321.3124, -324, -9, -3, 0, + 0.31255, 5, 142.88888708, 334, 342, 453, 54254]; + goog.array.sort(e); + assertArrayEquals(eSorted, e); + + // Test sorting already sorted array of Number(s) of length > 1 + goog.array.sort(e); + assertArrayEquals(eSorted, e); + + // Test sorting array of Number(s) of length > 1, + // using custom comparison function which does reverse ordering + var f = [-1321.3124, 0.31255, 54254, 0, 142.88888708, -321434.58758, + -324, 453, 334, -3, 5, -9, 342, -897123.9]; + var fSorted = [54254, 453, 342, 334, 142.88888708, 5, 0.31255, 0, -3, -9, + -324, -1321.3124, -321434.58758, -897123.9]; + goog.array.sort(f, function(a, b) { return b - a; }); + assertArrayEquals(fSorted, f); + + // Test sorting already sorted array of Number(s) of length > 1 + // using custom comparison function which does reverse ordering + goog.array.sort(f, function(a, b) {return b - a; }); + assertArrayEquals(fSorted, f); + + // Test sorting array of custom Object(s) of length > 1 that have + // an overriden toString + function ComparedObject(value) { + this.value = value; + }; + + ComparedObject.prototype.toString = function() { + return this.value; + }; + + var co1 = new ComparedObject('a'); + var co2 = new ComparedObject('b'); + var co3 = new ComparedObject('c'); + var co4 = new ComparedObject('d'); + + var g = [co3, co4, co2, co1]; + var gSorted = [co1, co2, co3, co4]; + goog.array.sort(g); + assertArrayEquals(gSorted, g); + + // Test sorting already sorted array of custom Object(s) of length > 1 + // that have an overriden toString + goog.array.sort(g); + assertArrayEquals(gSorted, g); + + // Test sorting an array of custom Object(s) of length > 1 using + // a custom comparison function + var h = [co4, co2, co1, co3]; + var hSorted = [co1, co2, co3, co4]; + goog.array.sort(h, function(a, b) { + return a.value > b.value ? 1 : a.value < b.value ? -1 : 0; + }); + assertArrayEquals(hSorted, h); + + // Test sorting already sorted array of custom Object(s) of length > 1 + // using a custom comparison function + goog.array.sort(h); + assertArrayEquals(hSorted, h); + + // Test sorting arrays of length 1 + var i = ['one']; + var iSorted = ['one']; + goog.array.sort(i); + assertArrayEquals(iSorted, i); + + var j = [1]; + var jSorted = [1]; + goog.array.sort(j); + assertArrayEquals(jSorted, j); + + var k = [1.1]; + var kSorted = [1.1]; + goog.array.sort(k); + assertArrayEquals(kSorted, k); + + var l = [co3]; + var lSorted = [co3]; + goog.array.sort(l); + assertArrayEquals(lSorted, l); + + var m = [co2]; + var mSorted = [co2]; + goog.array.sort(m, function(a, b) { + return a.value > b.value ? 1 : a.value < b.value ? -1 : 0; + }); + assertArrayEquals(mSorted, m); +} + +function testStableSort() { + // Test array with custom comparison function + var arr = [{key: 3, val: 'a'}, {key: 2, val: 'b'}, {key: 3, val: 'c'}, + {key: 4, val: 'd'}, {key: 3, val: 'e'}]; + var arrClone = goog.array.clone(arr); + + function comparisonFn(obj1, obj2) { + return obj1.key - obj2.key; + } + goog.array.stableSort(arr, comparisonFn); + var sortedValues = []; + for (var i = 0; i < arr.length; i++) { + sortedValues.push(arr[i].val); + } + var wantedSortedValues = ['b', 'a', 'c', 'e', 'd']; + assertArrayEquals(wantedSortedValues, sortedValues); + + // Test array without custom comparison function + var arr2 = []; + for (var i = 0; i < arrClone.length; i++) { + arr2.push({val: arrClone[i].val, toString: + goog.partial(function(index) { + return arrClone[index].key; + }, i)}); + } + goog.array.stableSort(arr2); + var sortedValues2 = []; + for (var i = 0; i < arr2.length; i++) { + sortedValues2.push(arr2[i].val); + } + assertArrayEquals(wantedSortedValues, sortedValues2); +} + +function testSortByKey() { + function Item(value) { + this.getValue = function() { + return value; + }; + } + var keyFn = function(item) { + return item.getValue(); + }; + + // Test without custom key comparison function + var arr1 = [new Item(3), new Item(2), new Item(1), new Item(5), new Item(4)]; + goog.array.sortByKey(arr1, keyFn); + var wantedSortedValues1 = [1, 2, 3, 4, 5]; + for (var i = 0; i < arr1.length; i++) { + assertEquals(wantedSortedValues1[i], arr1[i].getValue()); + } + + // Test with custom key comparison function + var arr2 = [new Item(3), new Item(2), new Item(1), new Item(5), new Item(4)]; + function comparisonFn(key1, key2) { + return -(key1 - key2); + } + goog.array.sortByKey(arr2, keyFn, comparisonFn); + var wantedSortedValues2 = [5, 4, 3, 2, 1]; + for (var i = 0; i < arr2.length; i++) { + assertEquals(wantedSortedValues2[i], arr2[i].getValue()); + } +} + +function testArrayBucketModulus() { + // bucket things by modulus + var a = {}; + var b = []; + + function modFive(num) { + return num % 5; + } + + for (var i = 0; i < 20; i++) { + var mod = modFive(i); + a[mod] = a[mod] || []; + a[mod].push(i); + b.push(i); + } + + var buckets = goog.array.bucket(b, modFive); + + for (var i = 0; i < 5; i++) { + // The order isn't defined, but they should be the same sorted. + goog.array.sort(a[i]); + goog.array.sort(buckets[i]); + assertArrayEquals(a[i], buckets[i]); + } +} + +function testArrayBucketEvenOdd() { + var a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + + // test even/odd + function isEven(value, index, array) { + assertEquals(value, array[index]); + assertEquals('number', typeof index); + assertEquals(a, array); + return value % 2 == 0; + } + + var b = goog.array.bucket(a, isEven); + + assertArrayEquals(b[true], [2, 4, 6, 8]); + assertArrayEquals(b[false], [1, 3, 5, 7, 9]); +} + +function testArrayBucketUsingThisObject() { + var a = [1, 2, 3, 4, 5]; + + var obj = { + specialValue: 2 + }; + + function isSpecialValue(value, index, array) { + return value == this.specialValue ? 1 : 0; + } + + var b = goog.array.bucket(a, isSpecialValue, obj); + assertArrayEquals(b[0], [1, 3, 4, 5]); + assertArrayEquals(b[1], [2]); +} + +function testArrayToObject() { + var a = [ + {name: 'a'}, + {name: 'b'}, + {name: 'c'}, + {name: 'd'}]; + + function getName(value, index, array) { + assertEquals(value, array[index]); + assertEquals('number', typeof index); + assertEquals(a, array); + return value.name; + } + + var b = goog.array.toObject(a, getName); + + for (var i = 0; i < a.length; i++) { + assertEquals(a[i], b[a[i].name]); + } +} + +function testRange() { + assertArrayEquals([], goog.array.range(0)); + assertArrayEquals([], goog.array.range(5, 5, 5)); + assertArrayEquals([], goog.array.range(-3, -3)); + assertArrayEquals([], goog.array.range(10, undefined, -1)); + assertArrayEquals([], goog.array.range(8, 0)); + assertArrayEquals([], goog.array.range(-5, -10, 3)); + + assertArrayEquals([0], goog.array.range(1)); + assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], goog.array.range(10)); + + assertArrayEquals([1], goog.array.range(1, 2)); + assertArrayEquals([-3, -2, -1, 0, 1, 2], goog.array.range(-3, 3)); + + assertArrayEquals([4], goog.array.range(4, 40, 400)); + assertArrayEquals([5, 8, 11, 14], goog.array.range(5, 15, 3)); + assertArrayEquals([1, -1, -3], goog.array.range(1, -5, -2)); + assertElementsRoughlyEqual([.2, .3, .4], goog.array.range(.2, .5, .1), 0.001); + + assertArrayEquals([0], goog.array.range(7, undefined, 9)); + assertArrayEquals([0, 2, 4, 6], goog.array.range(8, undefined, 2)); +} + +function testArrayRepeat() { + assertArrayEquals([], goog.array.repeat(3, 0)); + assertArrayEquals([], goog.array.repeat(3, -1)); + assertArrayEquals([3], goog.array.repeat(3, 1)); + assertArrayEquals([3, 3, 3], goog.array.repeat(3, 3)); + assertArrayEquals([null, null], goog.array.repeat(null, 2)); +} + +function testArrayFlatten() { + assertArrayEquals([1, 2, 3, 4, 5], goog.array.flatten(1, 2, 3, 4, 5)); + assertArrayEquals([1, 2, 3, 4, 5], goog.array.flatten(1, [2, [3, [4, 5]]])); + assertArrayEquals([1, 2, 3, 4], goog.array.flatten(1, [2, [3, [4]]])); + assertArrayEquals([1, 2, 3, 4], goog.array.flatten([[[1], 2], 3], 4)); + assertArrayEquals([1], goog.array.flatten([[1]])); + assertArrayEquals([], goog.array.flatten()); + assertArrayEquals([], goog.array.flatten([])); + assertArrayEquals(goog.array.repeat(3, 180002), + goog.array.flatten(3, goog.array.repeat(3, 180000), 3)); + assertArrayEquals(goog.array.repeat(3, 180000), + goog.array.flatten([goog.array.repeat(3, 180000)])); +} + +function testSortObjectsByKey() { + var sortedArray = buildSortedObjectArray(4); + var objects = + [sortedArray[1], sortedArray[2], sortedArray[0], sortedArray[3]]; + + goog.array.sortObjectsByKey(objects, 'name'); + validateObjectArray(sortedArray, objects); +} + +function testSortObjectsByKeyWithCompareFunction() { + var sortedArray = buildSortedObjectArray(4); + var objects = + [sortedArray[1], sortedArray[2], sortedArray[0], sortedArray[3]]; + var descSortedArray = + [sortedArray[3], sortedArray[2], sortedArray[1], sortedArray[0]]; + + function descCompare(a, b) { + return a < b ? 1 : a > b ? -1 : 0; + }; + + goog.array.sortObjectsByKey(objects, 'name', descCompare); + validateObjectArray(descSortedArray, objects); +} + +function buildSortedObjectArray(size) { + var objectArray = []; + for (var i = 0; i < size; i++) { + objectArray.push({ + 'name': 'name_' + i, + 'id': 'id_' + (size - i) + }); + } + + return objectArray; +} + +function validateObjectArray(expected, actual) { + assertEquals(expected.length, actual.length); + for (var i = 0; i < expected.length; i++) { + assertEquals(expected[i].name, actual[i].name); + assertEquals(expected[i].id, actual[i].id); + } +} + +function testIsSorted() { + assertTrue(goog.array.isSorted([1, 2, 3])); + assertTrue(goog.array.isSorted([1, 2, 2])); + assertFalse(goog.array.isSorted([1, 2, 1])); + + assertTrue(goog.array.isSorted([1, 2, 3], null, true)); + assertFalse(goog.array.isSorted([1, 2, 2], null, true)); + assertFalse(goog.array.isSorted([1, 2, 1], null, true)); + + function compare(a, b) { + return b - a; + } + + assertFalse(goog.array.isSorted([1, 2, 3], compare)); + assertTrue(goog.array.isSorted([3, 2, 2], compare)); +} + +function assertRotated(expect, array, rotate) { + assertArrayEquals(expect, goog.array.rotate(array, rotate)); +} + +function testRotate() { + assertRotated([], [], 3); + assertRotated([1], [1], 3); + assertRotated([1, 2, 3, 4, 0], [0, 1, 2, 3, 4], -6); + assertRotated([0, 1, 2, 3, 4], [0, 1, 2, 3, 4], -5); + assertRotated([4, 0, 1, 2, 3], [0, 1, 2, 3, 4], -4); + assertRotated([3, 4, 0, 1, 2], [0, 1, 2, 3, 4], -3); + assertRotated([2, 3, 4, 0, 1], [0, 1, 2, 3, 4], -2); + assertRotated([1, 2, 3, 4, 0], [0, 1, 2, 3, 4], -1); + assertRotated([0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 0); + assertRotated([4, 0, 1, 2, 3], [0, 1, 2, 3, 4], 1); + assertRotated([3, 4, 0, 1, 2], [0, 1, 2, 3, 4], 2); + assertRotated([2, 3, 4, 0, 1], [0, 1, 2, 3, 4], 3); + assertRotated([1, 2, 3, 4, 0], [0, 1, 2, 3, 4], 4); + assertRotated([0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 5); + assertRotated([4, 0, 1, 2, 3], [0, 1, 2, 3, 4], 6); +} + +function testMoveItemWithArray() { + var arr = [0, 1, 2, 3]; + goog.array.moveItem(arr, 1, 3); // toIndex > fromIndex + assertArrayEquals([0, 2, 3, 1], arr); + goog.array.moveItem(arr, 2, 0); // toIndex < fromIndex + assertArrayEquals([3, 0, 2, 1], arr); + goog.array.moveItem(arr, 1, 1); // toIndex == fromIndex + assertArrayEquals([3, 0, 2, 1], arr); + // Out-of-bounds indexes throw assertion errors. + assertThrows(function() { + goog.array.moveItem(arr, -1, 1); + }); + assertThrows(function() { + goog.array.moveItem(arr, 4, 1); + }); + assertThrows(function() { + goog.array.moveItem(arr, 1, -1); + }); + assertThrows(function() { + goog.array.moveItem(arr, 1, 4); + }); + // The array should not be modified by the out-of-bound calls. + assertArrayEquals([3, 0, 2, 1], arr); +} + +function testMoveItemWithArgumentsObject() { + var f = function() { + goog.array.moveItem(arguments, 0, 1); + return arguments; + }; + assertArrayEquals([1, 0], goog.array.toArray(f(0, 1))); +} + +function testConcat() { + var a1 = [1, 2, 3]; + var a2 = [4, 5, 6]; + var a3 = goog.array.concat(a1, a2); + a1.push(1); + a2.push(5); + assertArrayEquals([1, 2, 3, 4, 5, 6], a3); +} + +function testConcatWithNoSecondArg() { + var a1 = [1, 2, 3, 4]; + var a2 = goog.array.concat(a1); + a1.push(5); + assertArrayEquals([1, 2, 3, 4], a2); +} + +function testConcatWithNonArrayArgs() { + var a1 = [1, 2, 3, 4]; + var o = {0: 'a', 1: 'b', length: 2}; + var a2 = goog.array.concat(a1, 5, '10', o); + assertArrayEquals([1, 2, 3, 4, 5, '10', o], a2); +} + +function testConcatWithNull() { + var a1 = goog.array.concat(null, [1, 2, 3]); + var a2 = goog.array.concat([1, 2, 3], null); + assertArrayEquals([null, 1, 2, 3], a1); + assertArrayEquals([1, 2, 3, null], a2); +} + +function testZip() { + var a1 = goog.array.zip([1, 2, 3], [3, 2, 1]); + var a2 = goog.array.zip([1, 2], [3, 2, 1]); + var a3 = goog.array.zip(); + assertArrayEquals([[1, 3], [2, 2], [3, 1]], a1); + assertArrayEquals([[1, 3], [2, 2]], a2); + assertArrayEquals([], a3); +} + +function testShuffle() { + // Test array. This array should have unique values for the purposes of this + // test case. + var testArray = [1, 2, 3, 4, 5]; + var testArrayCopy = goog.array.clone(testArray); + + // Custom random function, which always returns a value approaching 1, + // resulting in a "shuffle" that preserves the order of original array + // (for array sizes that we work with here). + var noChangeShuffleFunction = function() { + return .999999; + }; + goog.array.shuffle(testArray, noChangeShuffleFunction); + assertArrayEquals(testArrayCopy, testArray); + + // Custom random function, which always returns 0, resulting in a + // deterministic "shuffle" that is predictable but differs from the + // original order of the array. + var testShuffleFunction = function() { + return 0; + }; + goog.array.shuffle(testArray, testShuffleFunction); + assertArrayEquals([2, 3, 4, 5, 1], testArray); + + // Test the use of a real random function(no optional RNG is specified). + goog.array.shuffle(testArray); + + // Ensure the shuffled array comprises the same elements (without regard to + // order). + assertSameElements(testArrayCopy, testArray); +} + +function testRemoveAllIf() { + var testArray = [9, 1, 9, 2, 9, 3, 4, 9, 9, 9, 5]; + var expectedArray = [1, 2, 3, 4, 5]; + + var actualOutput = goog.array.removeAllIf(testArray, function(el) { + return el == 9; + }); + + assertEquals(6, actualOutput); + assertArrayEquals(expectedArray, testArray); +} + +function testRemoveAllIf_noMatches() { + var testArray = [1]; + var expectedArray = [1]; + + var actualOutput = goog.array.removeAllIf(testArray, function(el) { + return false; + }); + + assertEquals(0, actualOutput); + assertArrayEquals(expectedArray, testArray); +} + +function testCopyByIndex() { + var testArray = [1, 2, 'a', 'b', 'c', 'd']; + var copyIndexes = [1, 3, 0, 0, 2]; + var expectedArray = [2, 'b', 1, 1, 'a']; + + var actualOutput = goog.array.copyByIndex(testArray, copyIndexes); + + assertArrayEquals(expectedArray, actualOutput); +} + +function testComparators() { + var greater = 42; + var smaller = 13; + + assertTrue(goog.array.defaultCompare(smaller, greater) < 0); + assertEquals(0, goog.array.defaultCompare(smaller, smaller)); + assertTrue(goog.array.defaultCompare(greater, smaller) > 0); + + assertTrue(goog.array.inverseDefaultCompare(greater, smaller) < 0); + assertEquals(0, goog.array.inverseDefaultCompare(greater, greater)); + assertTrue(goog.array.inverseDefaultCompare(smaller, greater) > 0); +} diff --git a/mapbox-gl/closure-library/closure/goog/asserts/asserts.js b/mapbox-gl/closure-library/closure/goog/asserts/asserts.js new file mode 100644 index 0000000000..94f5a088e5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/asserts/asserts.js @@ -0,0 +1,365 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities to check the preconditions, postconditions and + * invariants runtime. + * + * Methods in this package should be given special treatment by the compiler + * for type-inference. For example, goog.asserts.assert(foo) + * will restrict foo to a truthy value. + * + * The compiler has an option to disable asserts. So code like: + * + * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar()); + * + * will be transformed into: + * + * var x = foo(); + * + * The compiler will leave in foo() (because its return value is used), + * but it will remove bar() because it assumes it does not have side-effects. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.asserts'); +goog.provide('goog.asserts.AssertionError'); + +goog.require('goog.debug.Error'); +goog.require('goog.dom.NodeType'); +goog.require('goog.string'); + + +/** + * @define {boolean} Whether to strip out asserts or to leave them in. + */ +goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG); + + + +/** + * Error object for failed assertions. + * @param {string} messagePattern The pattern that was used to form message. + * @param {!Array<*>} messageArgs The items to substitute into the pattern. + * @constructor + * @extends {goog.debug.Error} + * @final + */ +goog.asserts.AssertionError = function(messagePattern, messageArgs) { + messageArgs.unshift(messagePattern); + goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs)); + // Remove the messagePattern afterwards to avoid permenantly modifying the + // passed in array. + messageArgs.shift(); + + /** + * The message pattern used to format the error message. Error handlers can + * use this to uniquely identify the assertion. + * @type {string} + */ + this.messagePattern = messagePattern; +}; +goog.inherits(goog.asserts.AssertionError, goog.debug.Error); + + +/** @override */ +goog.asserts.AssertionError.prototype.name = 'AssertionError'; + + +/** + * The default error handler. + * @param {!goog.asserts.AssertionError} e The exception to be handled. + */ +goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { throw e; }; + + +/** + * The handler responsible for throwing or logging assertion errors. + * @private {function(!goog.asserts.AssertionError)} + */ +goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER; + + +/** + * Throws an exception with the given message and "Assertion failed" prefixed + * onto it. + * @param {string} defaultMessage The message to use if givenMessage is empty. + * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage. + * @param {string|undefined} givenMessage Message supplied by the caller. + * @param {Array<*>} givenArgs The substitution arguments for givenMessage. + * @throws {goog.asserts.AssertionError} When the value is not a number. + * @private + */ +goog.asserts.doAssertFailure_ = + function(defaultMessage, defaultArgs, givenMessage, givenArgs) { + var message = 'Assertion failed'; + if (givenMessage) { + message += ': ' + givenMessage; + var args = givenArgs; + } else if (defaultMessage) { + message += ': ' + defaultMessage; + args = defaultArgs; + } + // The '' + works around an Opera 10 bug in the unit tests. Without it, + // a stack trace is added to var message above. With this, a stack trace is + // not added until this line (it causes the extra garbage to be added after + // the assertion message instead of in the middle of it). + var e = new goog.asserts.AssertionError('' + message, args || []); + goog.asserts.errorHandler_(e); +}; + + +/** + * Sets a custom error handler that can be used to customize the behavior of + * assertion failures, for example by turning all assertion failures into log + * messages. + * @param {function(!goog.asserts.AssertionError)} errorHandler + */ +goog.asserts.setErrorHandler = function(errorHandler) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_ = errorHandler; + } +}; + + +/** + * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is + * true. + * @template T + * @param {T} condition The condition to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {T} The value of the condition. + * @throws {goog.asserts.AssertionError} When the condition evaluates to false. + */ +goog.asserts.assert = function(condition, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !condition) { + goog.asserts.doAssertFailure_('', null, opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return condition; +}; + + +/** + * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case + * when we want to add a check in the unreachable area like switch-case + * statement: + * + *
+ *  switch(type) {
+ *    case FOO: doSomething(); break;
+ *    case BAR: doSomethingElse(); break;
+ *    default: goog.assert.fail('Unrecognized type: ' + type);
+ *      // We have only 2 types - "default:" section is unreachable code.
+ *  }
+ * 
+ * + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} Failure. + */ +goog.asserts.fail = function(opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_(new goog.asserts.AssertionError( + 'Failure' + (opt_message ? ': ' + opt_message : ''), + Array.prototype.slice.call(arguments, 1))); + } +}; + + +/** + * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {number} The value, guaranteed to be a number when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a number. + */ +goog.asserts.assertNumber = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) { + goog.asserts.doAssertFailure_('Expected number but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {number} */ (value); +}; + + +/** + * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {string} The value, guaranteed to be a string when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a string. + */ +goog.asserts.assertString = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) { + goog.asserts.doAssertFailure_('Expected string but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {string} */ (value); +}; + + +/** + * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Function} The value, guaranteed to be a function when asserts + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a function. + */ +goog.asserts.assertFunction = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) { + goog.asserts.doAssertFailure_('Expected function but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Function} */ (value); +}; + + +/** + * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Object} The value, guaranteed to be a non-null object. + * @throws {goog.asserts.AssertionError} When the value is not an object. + */ +goog.asserts.assertObject = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) { + goog.asserts.doAssertFailure_('Expected object but got %s: %s.', + [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Object} */ (value); +}; + + +/** + * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Array} The value, guaranteed to be a non-null array. + * @throws {goog.asserts.AssertionError} When the value is not an array. + */ +goog.asserts.assertArray = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) { + goog.asserts.doAssertFailure_('Expected array but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Array} */ (value); +}; + + +/** + * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {boolean} The value, guaranteed to be a boolean when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a boolean. + */ +goog.asserts.assertBoolean = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) { + goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {boolean} */ (value); +}; + + +/** + * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Element} The value, likely to be a DOM Element when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a boolean. + */ +goog.asserts.assertElement = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) || + value.nodeType != goog.dom.NodeType.ELEMENT)) { + goog.asserts.doAssertFailure_('Expected Element but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Element} */ (value); +}; + + +/** + * Checks if the value is an instance of the user-defined type if + * goog.asserts.ENABLE_ASSERTS is true. + * + * The compiler may tighten the type returned by this function. + * + * @param {*} value The value to check. + * @param {function(new: T, ...)} type A user-defined constructor. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} When the value is not an instance of + * type. + * @return {T} + * @template T + */ +goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) { + goog.asserts.doAssertFailure_('Expected instanceof %s but got %s.', + [goog.asserts.getType_(type), goog.asserts.getType_(value)], + opt_message, Array.prototype.slice.call(arguments, 3)); + } + return value; +}; + + +/** + * Checks that no enumerable keys are present in Object.prototype. Such keys + * would break most code that use {@code for (var ... in ...)} loops. + */ +goog.asserts.assertObjectPrototypeIsIntact = function() { + for (var key in Object.prototype) { + goog.asserts.fail(key + ' should not be enumerable in Object.prototype.'); + } +}; + + +/** + * Returns the type of a value. If a constructor is passed, and a suitable + * string cannot be found, 'unknown type name' will be returned. + * @param {*} value A constructor, object, or primitive. + * @return {string} The best display name for the value, or 'unknown type name'. + * @private + */ +goog.asserts.getType_ = function(value) { + if (value instanceof Function) { + return value.displayName || value.name || 'unknown type name'; + } else if (value instanceof Object) { + return value.constructor.displayName || value.constructor.name || + Object.prototype.toString.call(value); + } else { + return value === null ? 'null' : typeof value; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.html b/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.html new file mode 100644 index 0000000000..ab57507123 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.asserts.assert + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.js b/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.js new file mode 100644 index 0000000000..f2fa268f22 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/asserts/asserts_test.js @@ -0,0 +1,242 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.assertsTest'); +goog.setTestOnly('goog.assertsTest'); + +goog.require('goog.asserts'); +goog.require('goog.asserts.AssertionError'); +goog.require('goog.dom'); +goog.require('goog.string'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function doTestMessage(failFunc, expectedMsg) { + var error = assertThrows('failFunc should throw.', failFunc); + // Test error message. + // Opera 10 adds cruft to the end of the message, so do a startsWith check. + assertTrue('Message check failed. Expected: ' + expectedMsg + ' Actual: ' + + error.message, goog.string.startsWith(error.message, expectedMsg)); +} + +function testAssert() { + // None of them may throw exception + goog.asserts.assert(true); + goog.asserts.assert(1); + goog.asserts.assert([]); + goog.asserts.assert({}); + + assertThrows('assert(false)', goog.partial(goog.asserts.assert, false)); + assertThrows('assert(0)', goog.partial(goog.asserts.assert, 0)); + assertThrows('assert(null)', goog.partial(goog.asserts.assert, null)); + assertThrows('assert(undefined)', + goog.partial(goog.asserts.assert, undefined)); + + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assert, false), 'Assertion failed'); + doTestMessage(goog.partial(goog.asserts.assert, false, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + + +function testFail() { + assertThrows('fail()', goog.asserts.fail); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.fail, false), 'Failure'); + doTestMessage(goog.partial(goog.asserts.fail, 'ouch %s', 1), + 'Failure: ouch 1'); +} + +function testNumber() { + goog.asserts.assertNumber(1); + assertThrows('assertNumber(null)', + goog.partial(goog.asserts.assertNumber, null)); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertNumber, null), + 'Assertion failed: Expected number but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertNumber, '1234'), + 'Assertion failed: Expected number but got string: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertNumber, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testString() { + assertEquals('1', goog.asserts.assertString('1')); + assertThrows('assertString(null)', + goog.partial(goog.asserts.assertString, null)); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertString, null), + 'Assertion failed: Expected string but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertString, 1234), + 'Assertion failed: Expected string but got number: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertString, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testFunction() { + function f() {}; + assertEquals(f, goog.asserts.assertFunction(f)); + assertThrows('assertFunction(null)', + goog.partial(goog.asserts.assertFunction, null)); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertFunction, null), + 'Assertion failed: Expected function but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertFunction, 1234), + 'Assertion failed: Expected function but got number: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertFunction, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testObject() { + var o = {}; + assertEquals(o, goog.asserts.assertObject(o)); + assertThrows('assertObject(null)', + goog.partial(goog.asserts.assertObject, null)); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertObject, null), + 'Assertion failed: Expected object but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertObject, 1234), + 'Assertion failed: Expected object but got number: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertObject, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testArray() { + var a = []; + assertEquals(a, goog.asserts.assertArray(a)); + assertThrows('assertArray({})', + goog.partial(goog.asserts.assertArray, {})); + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertArray, null), + 'Assertion failed: Expected array but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertArray, 1234), + 'Assertion failed: Expected array but got number: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertArray, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testBoolean() { + assertEquals(true, goog.asserts.assertBoolean(true)); + assertEquals(false, goog.asserts.assertBoolean(false)); + assertThrows(goog.partial(goog.asserts.assertBoolean, null)); + assertThrows(goog.partial(goog.asserts.assertBoolean, 'foo')); + + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertBoolean, null), + 'Assertion failed: Expected boolean but got null: null.'); + doTestMessage(goog.partial(goog.asserts.assertBoolean, 1234), + 'Assertion failed: Expected boolean but got number: 1234.'); + doTestMessage(goog.partial(goog.asserts.assertBoolean, null, 'ouch %s', 1), + 'Assertion failed: ouch 1'); +} + +function testElement() { + assertThrows(goog.partial(goog.asserts.assertElement, null)); + assertThrows(goog.partial(goog.asserts.assertElement, 'foo')); + assertThrows(goog.partial(goog.asserts.assertElement, + goog.dom.createTextNode('foo'))); + var elem = goog.dom.createElement('div'); + assertEquals(elem, goog.asserts.assertElement(elem)); +} + +function testInstanceof() { + /** @constructor */ + var F = function() {}; + goog.asserts.assertInstanceof(new F(), F); + assertThrows('assertInstanceof({}, F)', + goog.partial(goog.asserts.assertInstanceof, {}, F)); + // IE lacks support for function.name and will fallback to toString(). + var object = goog.userAgent.IE ? '[object Object]' : 'Object'; + + // Test error messages. + doTestMessage(goog.partial(goog.asserts.assertInstanceof, {}, F), + 'Assertion failed: Expected instanceof unknown type name but got ' + + object + '.'); + doTestMessage(goog.partial(goog.asserts.assertInstanceof, {}, F, 'a %s', 1), + 'Assertion failed: a 1'); + doTestMessage(goog.partial(goog.asserts.assertInstanceof, null, F), + 'Assertion failed: Expected instanceof unknown type name but got null.'); + doTestMessage(goog.partial(goog.asserts.assertInstanceof, 5, F), + 'Assertion failed: ' + + 'Expected instanceof unknown type name but got number.'); + + // Test a constructor a with a name (IE does not support function.name). + if (!goog.userAgent.IE) { + F = function foo() {}; + doTestMessage(goog.partial(goog.asserts.assertInstanceof, {}, F), + 'Assertion failed: Expected instanceof foo but got ' + object + '.'); + } + + // Test a constructor with a displayName. + F.displayName = 'bar'; + doTestMessage(goog.partial(goog.asserts.assertInstanceof, {}, F), + 'Assertion failed: Expected instanceof bar but got ' + object + '.'); +} + +function testObjectPrototypeIsIntact() { + goog.asserts.assertObjectPrototypeIsIntact(); + var originalToString = Object.prototype.toString; + Object.prototype.toString = goog.nullFunction; + try { + goog.asserts.assertObjectPrototypeIsIntact(); + Object.prototype.foo = 1; + doTestMessage(goog.asserts.assertObjectPrototypeIsIntact, + 'Failure: foo should not be enumerable in Object.prototype.'); + } finally { + Object.prototype.toString = originalToString; + delete Object.prototype.foo; + } +} + +function testAssertionError() { + var error = new goog.asserts.AssertionError('foo %s %s', [1, 'two']); + assertEquals('Wrong message', 'foo 1 two', error.message); + assertEquals('Wrong messagePattern', 'foo %s %s', error.messagePattern); +} + +function testFailWithCustomErrorHandler() { + try { + var handledException; + goog.asserts.setErrorHandler( + function(e) { handledException = e; }); + + var expectedMessage = 'Failure: Gevalt!'; + + goog.asserts.fail('Gevalt!'); + assertTrue('handledException is null.', handledException != null); + assertTrue('Message check failed. Expected: ' + expectedMessage + + ' Actual: ' + handledException.message, + goog.string.startsWith(expectedMessage, handledException.message)); + } finally { + goog.asserts.setErrorHandler(goog.asserts.DEFAULT_ERROR_HANDLER); + } +} + +function testAssertWithCustomErrorHandler() { + try { + var handledException; + goog.asserts.setErrorHandler( + function(e) { handledException = e; }); + + var expectedMessage = 'Assertion failed: Gevalt!'; + + goog.asserts.assert(false, 'Gevalt!'); + assertTrue('handledException is null.', handledException != null); + assertTrue('Message check failed. Expected: ' + expectedMessage + + ' Actual: ' + handledException.message, + goog.string.startsWith(expectedMessage, handledException.message)); + } finally { + goog.asserts.setErrorHandler(goog.asserts.DEFAULT_ERROR_HANDLER); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/async/animationdelay.js b/mapbox-gl/closure-library/closure/goog/async/animationdelay.js new file mode 100644 index 0000000000..545cfb0cff --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/animationdelay.js @@ -0,0 +1,273 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A delayed callback that pegs to the next animation frame + * instead of a user-configurable timeout. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.async.AnimationDelay'); + +goog.require('goog.Disposable'); +goog.require('goog.events'); +goog.require('goog.functions'); + + + +// TODO(nicksantos): Should we factor out the common code between this and +// goog.async.Delay? I'm not sure if there's enough code for this to really +// make sense. Subclassing seems like the wrong approach for a variety of +// reasons. Maybe there should be a common interface? + + + +/** + * A delayed callback that pegs to the next animation frame + * instead of a user configurable timeout. By design, this should have + * the same interface as goog.async.Delay. + * + * Uses requestAnimationFrame and friends when available, but falls + * back to a timeout of goog.async.AnimationDelay.TIMEOUT. + * + * For more on requestAnimationFrame and how you can use it to create smoother + * animations, see: + * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + * + * @param {function(number)} listener Function to call when the delay completes. + * Will be passed the timestamp when it's called, in unix ms. + * @param {Window=} opt_window The window object to execute the delay in. + * Defaults to the global object. + * @param {Object=} opt_handler The object scope to invoke the function in. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.async.AnimationDelay = function(listener, opt_window, opt_handler) { + goog.async.AnimationDelay.base(this, 'constructor'); + + /** + * The function that will be invoked after a delay. + * @type {function(number)} + * @private + */ + this.listener_ = listener; + + /** + * The object context to invoke the callback in. + * @type {Object|undefined} + * @private + */ + this.handler_ = opt_handler; + + /** + * @type {Window} + * @private + */ + this.win_ = opt_window || window; + + /** + * Cached callback function invoked when the delay finishes. + * @type {function()} + * @private + */ + this.callback_ = goog.bind(this.doAction_, this); +}; +goog.inherits(goog.async.AnimationDelay, goog.Disposable); + + +/** + * Identifier of the active delay timeout, or event listener, + * or null when inactive. + * @type {goog.events.Key|number|null} + * @private + */ +goog.async.AnimationDelay.prototype.id_ = null; + + +/** + * If we're using dom listeners. + * @type {?boolean} + * @private + */ +goog.async.AnimationDelay.prototype.usingListeners_ = false; + + +/** + * Default wait timeout for animations (in milliseconds). Only used for timed + * animation, which uses a timer (setTimeout) to schedule animation. + * + * @type {number} + * @const + */ +goog.async.AnimationDelay.TIMEOUT = 20; + + +/** + * Name of event received from the requestAnimationFrame in Firefox. + * + * @type {string} + * @const + * @private + */ +goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_ = 'MozBeforePaint'; + + +/** + * Starts the delay timer. The provided listener function will be called + * before the next animation frame. + */ +goog.async.AnimationDelay.prototype.start = function() { + this.stop(); + this.usingListeners_ = false; + + var raf = this.getRaf_(); + var cancelRaf = this.getCancelRaf_(); + if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) { + // Because Firefox (Gecko) runs animation in separate threads, it also saves + // time by running the requestAnimationFrame callbacks in that same thread. + // Sadly this breaks the assumption of implicit thread-safety in JS, and can + // thus create thread-based inconsistencies on counters etc. + // + // Calling cycleAnimations_ using the MozBeforePaint event instead of as + // callback fixes this. + // + // Trigger this condition only if the mozRequestAnimationFrame is available, + // but not the W3C requestAnimationFrame function (as in draft) or the + // equivalent cancel functions. + this.id_ = goog.events.listen( + this.win_, + goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_, + this.callback_); + this.win_.mozRequestAnimationFrame(null); + this.usingListeners_ = true; + } else if (raf && cancelRaf) { + this.id_ = raf.call(this.win_, this.callback_); + } else { + this.id_ = this.win_.setTimeout( + // Prior to Firefox 13, Gecko passed a non-standard parameter + // to the callback that we want to ignore. + goog.functions.lock(this.callback_), + goog.async.AnimationDelay.TIMEOUT); + } +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.AnimationDelay.prototype.stop = function() { + if (this.isActive()) { + var raf = this.getRaf_(); + var cancelRaf = this.getCancelRaf_(); + if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) { + goog.events.unlistenByKey(this.id_); + } else if (raf && cancelRaf) { + cancelRaf.call(this.win_, /** @type {number} */ (this.id_)); + } else { + this.win_.clearTimeout(/** @type {number} */ (this.id_)); + } + } + this.id_ = null; +}; + + +/** + * Fires delay's action even if timer has already gone off or has not been + * started yet; guarantees action firing. Stops the delay timer. + */ +goog.async.AnimationDelay.prototype.fire = function() { + this.stop(); + this.doAction_(); +}; + + +/** + * Fires delay's action only if timer is currently active. Stops the delay + * timer. + */ +goog.async.AnimationDelay.prototype.fireIfActive = function() { + if (this.isActive()) { + this.fire(); + } +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.AnimationDelay.prototype.isActive = function() { + return this.id_ != null; +}; + + +/** + * Invokes the callback function after the delay successfully completes. + * @private + */ +goog.async.AnimationDelay.prototype.doAction_ = function() { + if (this.usingListeners_ && this.id_) { + goog.events.unlistenByKey(this.id_); + } + this.id_ = null; + + // We are not using the timestamp returned by requestAnimationFrame + // because it may be either a Date.now-style time or a + // high-resolution time (depending on browser implementation). Using + // goog.now() will ensure that the timestamp used is consistent and + // compatible with goog.fx.Animation. + this.listener_.call(this.handler_, goog.now()); +}; + + +/** @override */ +goog.async.AnimationDelay.prototype.disposeInternal = function() { + this.stop(); + goog.async.AnimationDelay.base(this, 'disposeInternal'); +}; + + +/** + * @return {?function(function(number)): number} The requestAnimationFrame + * function, or null if not available on this browser. + * @private + */ +goog.async.AnimationDelay.prototype.getRaf_ = function() { + var win = this.win_; + return win.requestAnimationFrame || + win.webkitRequestAnimationFrame || + win.mozRequestAnimationFrame || + win.oRequestAnimationFrame || + win.msRequestAnimationFrame || + null; +}; + + +/** + * @return {?function(number): number} The cancelAnimationFrame function, + * or null if not available on this browser. + * @private + */ +goog.async.AnimationDelay.prototype.getCancelRaf_ = function() { + var win = this.win_; + return win.cancelAnimationFrame || + win.cancelRequestAnimationFrame || + win.webkitCancelRequestAnimationFrame || + win.mozCancelRequestAnimationFrame || + win.oCancelRequestAnimationFrame || + win.msCancelRequestAnimationFrame || + null; +}; diff --git a/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.html b/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.html new file mode 100644 index 0000000000..49c027567e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.html @@ -0,0 +1,23 @@ + + + + + + JsUnit tests for goog.async.AnimationDelay + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.js b/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.js new file mode 100644 index 0000000000..820ee586d0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/animationdelay_test.js @@ -0,0 +1,81 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.async.AnimationDelayTest'); +goog.setTestOnly('goog.async.AnimationDelayTest'); + +goog.require('goog.async.AnimationDelay'); +goog.require('goog.testing.AsyncTestCase'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); + +var testCase = goog.testing.AsyncTestCase.createAndInstall(); +var stubs = new goog.testing.PropertyReplacer(); + +function tearDown() { + stubs.reset(); +} + +function testStart() { + var callCount = 0; + var start = goog.now(); + var delay = new goog.async.AnimationDelay(function(end) { + callCount++; + }); + + delay.start(); + testCase.waitForAsync('waiting for delay'); + + window.setTimeout(function() { + testCase.continueTesting(); + assertEquals(1, callCount); + }, 500); +} + +function testStop() { + var callCount = 0; + var start = goog.now(); + var delay = new goog.async.AnimationDelay(function(end) { + callCount++; + }); + + delay.start(); + testCase.waitForAsync('waiting for delay'); + delay.stop(); + + window.setTimeout(function() { + testCase.continueTesting(); + assertEquals(0, callCount); + }, 500); +} + +function testAlwaysUseGoogNowForHandlerTimestamp() { + var expectedValue = 12345.1; + stubs.set(goog, 'now', function() { + return expectedValue; + }); + + var handler = goog.testing.recordFunction(function(timestamp) { + assertEquals(expectedValue, timestamp); + }); + var delay = new goog.async.AnimationDelay(handler); + + delay.start(); + testCase.waitForAsync('waiting for delay'); + + window.setTimeout(function() { + testCase.continueTesting(); + assertEquals(1, handler.getCallCount()); + }, 500); +} diff --git a/mapbox-gl/closure-library/closure/goog/async/conditionaldelay.js b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay.js new file mode 100644 index 0000000000..1e3897579f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay.js @@ -0,0 +1,235 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines a class useful for handling functions that must be + * invoked later when some condition holds. Examples include deferred function + * calls that return a boolean flag whether it succedeed or not. + * + * Example: + * + * function deferred() { + * var succeeded = false; + * // ... custom code + * return succeeded; + * } + * + * var deferredCall = new goog.async.ConditionalDelay(deferred); + * deferredCall.onSuccess = function() { + * alert('Success: The deferred function has been successfully executed.'); + * } + * deferredCall.onFailure = function() { + * alert('Failure: Time limit exceeded.'); + * } + * + * // Call the deferred() every 100 msec until it returns true, + * // or 5 seconds pass. + * deferredCall.start(100, 5000); + * + * // Stop the deferred function call (does nothing if it's not active). + * deferredCall.stop(); + * + */ + + +goog.provide('goog.async.ConditionalDelay'); + +goog.require('goog.Disposable'); +goog.require('goog.async.Delay'); + + + +/** + * A ConditionalDelay object invokes the associated function after a specified + * interval delay and checks its return value. If the function returns + * {@code true} the conditional delay is cancelled and {@see #onSuccess} + * is called. Otherwise this object keeps to invoke the deferred function until + * either it returns {@code true} or the timeout is exceeded. In the latter case + * the {@see #onFailure} method will be called. + * + * The interval duration and timeout can be specified each time the delay is + * started. Calling start on an active delay will reset the timer. + * + * @param {function():boolean} listener Function to call when the delay + * completes. Should return a value that type-converts to {@code true} if + * the call succeeded and this delay should be stopped. + * @param {Object=} opt_handler The object scope to invoke the function in. + * @constructor + * @extends {goog.Disposable} + */ +goog.async.ConditionalDelay = function(listener, opt_handler) { + goog.Disposable.call(this); + + /** + * The function that will be invoked after a delay. + * @type {function():boolean} + * @private + */ + this.listener_ = listener; + + /** + * The object context to invoke the callback in. + * @type {Object|undefined} + * @private + */ + this.handler_ = opt_handler; + + /** + * The underlying goog.async.Delay delegate object. + * @type {goog.async.Delay} + * @private + */ + this.delay_ = new goog.async.Delay( + goog.bind(this.onTick_, this), 0 /*interval*/, this /*scope*/); +}; +goog.inherits(goog.async.ConditionalDelay, goog.Disposable); + + +/** + * The delay interval in milliseconds to between the calls to the callback. + * Note, that the callback may be invoked earlier than this interval if the + * timeout is exceeded. + * @type {number} + * @private + */ +goog.async.ConditionalDelay.prototype.interval_ = 0; + + +/** + * The timeout timestamp until which the delay is to be executed. + * A negative value means no timeout. + * @type {number} + * @private + */ +goog.async.ConditionalDelay.prototype.runUntil_ = 0; + + +/** + * True if the listener has been executed, and it returned {@code true}. + * @type {boolean} + * @private + */ +goog.async.ConditionalDelay.prototype.isDone_ = false; + + +/** @override */ +goog.async.ConditionalDelay.prototype.disposeInternal = function() { + this.delay_.dispose(); + delete this.listener_; + delete this.handler_; + goog.async.ConditionalDelay.superClass_.disposeInternal.call(this); +}; + + +/** + * Starts the delay timer. The provided listener function will be called + * repeatedly after the specified interval until the function returns + * {@code true} or the timeout is exceeded. Calling start on an active timer + * will stop the timer first. + * @param {number=} opt_interval The time interval between the function + * invocations (in milliseconds). Default is 0. + * @param {number=} opt_timeout The timeout interval (in milliseconds). Takes + * precedence over the {@code opt_interval}, i.e. if the timeout is less + * than the invocation interval, the function will be called when the + * timeout is exceeded. A negative value means no timeout. Default is 0. + */ +goog.async.ConditionalDelay.prototype.start = function(opt_interval, + opt_timeout) { + this.stop(); + this.isDone_ = false; + + var timeout = opt_timeout || 0; + this.interval_ = Math.max(opt_interval || 0, 0); + this.runUntil_ = timeout < 0 ? -1 : (goog.now() + timeout); + this.delay_.start( + timeout < 0 ? this.interval_ : Math.min(this.interval_, timeout)); +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.ConditionalDelay.prototype.stop = function() { + this.delay_.stop(); +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.ConditionalDelay.prototype.isActive = function() { + return this.delay_.isActive(); +}; + + +/** + * @return {boolean} True if the listener has been executed and returned + * {@code true} since the last call to {@see #start}. + */ +goog.async.ConditionalDelay.prototype.isDone = function() { + return this.isDone_; +}; + + +/** + * Called when the listener has been successfully executed and returned + * {@code true}. The {@see #isDone} method should return {@code true} by now. + * Designed for inheritance, should be overridden by subclasses or on the + * instances if they care. + */ +goog.async.ConditionalDelay.prototype.onSuccess = function() { + // Do nothing by default. +}; + + +/** + * Called when this delayed call is cancelled because the timeout has been + * exceeded, and the listener has never returned {@code true}. + * Designed for inheritance, should be overridden by subclasses or on the + * instances if they care. + */ +goog.async.ConditionalDelay.prototype.onFailure = function() { + // Do nothing by default. +}; + + +/** + * A callback function for the underlying {@code goog.async.Delay} object. When + * executed the listener function is called, and if it returns {@code true} + * the delay is stopped and the {@see #onSuccess} method is invoked. + * If the timeout is exceeded the delay is stopped and the + * {@see #onFailure} method is called. + * @private + */ +goog.async.ConditionalDelay.prototype.onTick_ = function() { + var successful = this.listener_.call(this.handler_); + if (successful) { + this.isDone_ = true; + this.onSuccess(); + } else { + // Try to reschedule the task. + if (this.runUntil_ < 0) { + // No timeout. + this.delay_.start(this.interval_); + } else { + var timeLeft = this.runUntil_ - goog.now(); + if (timeLeft <= 0) { + this.onFailure(); + } else { + this.delay_.start(Math.min(this.interval_, timeLeft)); + } + } + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.html b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.html new file mode 100644 index 0000000000..d0086443ae --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.async.ConditionalDelay + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.js b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.js new file mode 100644 index 0000000000..6bd105bcdf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/conditionaldelay_test.js @@ -0,0 +1,222 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.async.ConditionalDelayTest'); +goog.setTestOnly('goog.async.ConditionalDelayTest'); + +goog.require('goog.async.ConditionalDelay'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); + +var invoked = false; +var delay = null; +var clock = null; +var returnValue = true; +var onSuccessCalled = false; +var onFailureCalled = false; + + +function callback() { + invoked = true; + return returnValue; +} + + +function setUp() { + clock = new goog.testing.MockClock(true); + invoked = false; + returnValue = true; + onSuccessCalled = false; + onFailureCalled = false; + delay = new goog.async.ConditionalDelay(callback); + delay.onSuccess = function() { + onSuccessCalled = true; + }; + delay.onFailure = function() { + onFailureCalled = true; + }; +} + + +function tearDown() { + clock.dispose(); + delay.dispose(); +} + + +function testDelay() { + delay.start(200, 200); + assertFalse(invoked); + + clock.tick(100); + assertFalse(invoked); + + clock.tick(100); + assertTrue(invoked); +} + + +function testStop() { + delay.start(200, 500); + assertTrue(delay.isActive()); + + clock.tick(100); + assertFalse(invoked); + + delay.stop(); + clock.tick(100); + assertFalse(invoked); + + assertFalse(delay.isActive()); +} + + +function testIsActive() { + assertFalse(delay.isActive()); + delay.start(200, 200); + assertTrue(delay.isActive()); + clock.tick(200); + assertFalse(delay.isActive()); +} + + +function testRestart() { + delay.start(200, 50000); + clock.tick(100); + + delay.stop(); + assertFalse(invoked); + + delay.start(200, 50000); + clock.tick(199); + assertFalse(invoked); + + clock.tick(1); + assertTrue(invoked); + + invoked = false; + delay.start(200, 200); + clock.tick(200); + assertTrue(invoked); + + assertFalse(delay.isActive()); +} + + +function testDispose() { + delay.start(200, 200); + delay.dispose(); + assertTrue(delay.isDisposed()); + + clock.tick(500); + assertFalse(invoked); +} + + +function testConditionalDelay_Success() { + returnValue = false; + delay.start(100, 300); + + clock.tick(99); + assertFalse(invoked); + clock.tick(1); + assertTrue(invoked); + + assertTrue(delay.isActive()); + assertFalse(delay.isDone()); + assertFalse(onSuccessCalled); + assertFalse(onFailureCalled); + + returnValue = true; + + invoked = false; + clock.tick(100); + assertTrue(invoked); + + assertFalse(delay.isActive()); + assertTrue(delay.isDone()); + assertTrue(onSuccessCalled); + assertFalse(onFailureCalled); + + invoked = false; + clock.tick(200); + assertFalse(invoked); +} + + +function testConditionalDelay_Failure() { + returnValue = false; + delay.start(100, 300); + + clock.tick(99); + assertFalse(invoked); + clock.tick(1); + assertTrue(invoked); + + assertTrue(delay.isActive()); + assertFalse(delay.isDone()); + assertFalse(onSuccessCalled); + assertFalse(onFailureCalled); + + invoked = false; + clock.tick(100); + assertTrue(invoked); + assertFalse(onSuccessCalled); + assertFalse(onFailureCalled); + + invoked = false; + clock.tick(90); + assertFalse(invoked); + clock.tick(10); + assertTrue(invoked); + + assertFalse(delay.isActive()); + assertFalse(delay.isDone()); + assertFalse(onSuccessCalled); + assertTrue(onFailureCalled); +} + + +function testInfiniteDelay() { + returnValue = false; + delay.start(100, -1); + + // Test in a big enough loop. + for (var i = 0; i < 1000; ++i) { + clock.tick(80); + assertTrue(delay.isActive()); + assertFalse(delay.isDone()); + assertFalse(onSuccessCalled); + assertFalse(onFailureCalled); + } + + delay.stop(); + assertFalse(delay.isActive()); + assertFalse(delay.isDone()); + assertFalse(onSuccessCalled); + assertFalse(onFailureCalled); +} + +function testCallbackScope() { + var callbackCalled = false; + var scopeObject = {}; + function internalCallback() { + assertEquals(this, scopeObject); + callbackCalled = true; + return true; + } + delay = new goog.async.ConditionalDelay(internalCallback, scopeObject); + delay.start(200, 200); + clock.tick(201); + assertTrue(callbackCalled); +} diff --git a/mapbox-gl/closure-library/closure/goog/async/delay.js b/mapbox-gl/closure-library/closure/goog/async/delay.js new file mode 100644 index 0000000000..1c5916a216 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/delay.js @@ -0,0 +1,179 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines a class useful for handling functions that must be + * invoked after a delay, especially when that delay is frequently restarted. + * Examples include delaying before displaying a tooltip, menu hysteresis, + * idle timers, etc. + * @author brenneman@google.com (Shawn Brenneman) + * @see ../demos/timers.html + */ + + +goog.provide('goog.Delay'); +goog.provide('goog.async.Delay'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); + + + +/** + * A Delay object invokes the associated function after a specified delay. The + * interval duration can be specified once in the constructor, or can be defined + * each time the delay is started. Calling start on an active delay will reset + * the timer. + * + * @param {Function} listener Function to call when the delay completes. + * @param {number=} opt_interval The default length of the invocation delay (in + * milliseconds). + * @param {Object=} opt_handler The object scope to invoke the function in. + * @constructor + * @extends {goog.Disposable} + * @final + */ +goog.async.Delay = function(listener, opt_interval, opt_handler) { + goog.Disposable.call(this); + + /** + * The function that will be invoked after a delay. + * @type {Function} + * @private + */ + this.listener_ = listener; + + /** + * The default amount of time to delay before invoking the callback. + * @type {number} + * @private + */ + this.interval_ = opt_interval || 0; + + /** + * The object context to invoke the callback in. + * @type {Object|undefined} + * @private + */ + this.handler_ = opt_handler; + + + /** + * Cached callback function invoked when the delay finishes. + * @type {Function} + * @private + */ + this.callback_ = goog.bind(this.doAction_, this); +}; +goog.inherits(goog.async.Delay, goog.Disposable); + + + +/** + * A deprecated alias. + * @deprecated Use goog.async.Delay instead. + * @constructor + * @final + */ +goog.Delay = goog.async.Delay; + + +/** + * Identifier of the active delay timeout, or 0 when inactive. + * @type {number} + * @private + */ +goog.async.Delay.prototype.id_ = 0; + + +/** + * Disposes of the object, cancelling the timeout if it is still outstanding and + * removing all object references. + * @override + * @protected + */ +goog.async.Delay.prototype.disposeInternal = function() { + goog.async.Delay.superClass_.disposeInternal.call(this); + this.stop(); + delete this.listener_; + delete this.handler_; +}; + + +/** + * Starts the delay timer. The provided listener function will be called after + * the specified interval. Calling start on an active timer will reset the + * delay interval. + * @param {number=} opt_interval If specified, overrides the object's default + * interval with this one (in milliseconds). + */ +goog.async.Delay.prototype.start = function(opt_interval) { + this.stop(); + this.id_ = goog.Timer.callOnce( + this.callback_, + goog.isDef(opt_interval) ? opt_interval : this.interval_); +}; + + +/** + * Stops the delay timer if it is active. No action is taken if the timer is not + * in use. + */ +goog.async.Delay.prototype.stop = function() { + if (this.isActive()) { + goog.Timer.clear(this.id_); + } + this.id_ = 0; +}; + + +/** + * Fires delay's action even if timer has already gone off or has not been + * started yet; guarantees action firing. Stops the delay timer. + */ +goog.async.Delay.prototype.fire = function() { + this.stop(); + this.doAction_(); +}; + + +/** + * Fires delay's action only if timer is currently active. Stops the delay + * timer. + */ +goog.async.Delay.prototype.fireIfActive = function() { + if (this.isActive()) { + this.fire(); + } +}; + + +/** + * @return {boolean} True if the delay is currently active, false otherwise. + */ +goog.async.Delay.prototype.isActive = function() { + return this.id_ != 0; +}; + + +/** + * Invokes the callback function after the delay successfully completes. + * @private + */ +goog.async.Delay.prototype.doAction_ = function() { + this.id_ = 0; + if (this.listener_) { + this.listener_.call(this.handler_); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/async/delay_test.html b/mapbox-gl/closure-library/closure/goog/async/delay_test.html new file mode 100644 index 0000000000..3042a366fe --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/delay_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.async.Delay + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/async/delay_test.js b/mapbox-gl/closure-library/closure/goog/async/delay_test.js new file mode 100644 index 0000000000..f97b74c900 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/delay_test.js @@ -0,0 +1,142 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.async.DelayTest'); +goog.setTestOnly('goog.async.DelayTest'); + +goog.require('goog.async.Delay'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); + +var invoked = false; +var delay = null; +var clock = null; + + +function callback() { + invoked = true; +} + + +function setUp() { + clock = new goog.testing.MockClock(true); + invoked = false; + delay = new goog.async.Delay(callback, 200); +} + +function tearDown() { + clock.dispose(); + delay.dispose(); +} + + +function testDelay() { + delay.start(); + assertFalse(invoked); + + clock.tick(100); + assertFalse(invoked); + + clock.tick(100); + assertTrue(invoked); +} + + +function testStop() { + delay.start(); + + clock.tick(100); + assertFalse(invoked); + + delay.stop(); + clock.tick(100); + assertFalse(invoked); +} + + +function testIsActive() { + assertFalse(delay.isActive()); + delay.start(); + assertTrue(delay.isActive()); + clock.tick(200); + assertFalse(delay.isActive()); +} + + +function testRestart() { + delay.start(); + clock.tick(100); + + delay.stop(); + assertFalse(invoked); + + delay.start(); + clock.tick(199); + assertFalse(invoked); + + clock.tick(1); + assertTrue(invoked); + + invoked = false; + delay.start(); + clock.tick(200); + assertTrue(invoked); +} + + +function testOverride() { + delay.start(50); + clock.tick(49); + assertFalse(invoked); + + clock.tick(1); + assertTrue(invoked); +} + + +function testDispose() { + delay.start(); + delay.dispose(); + assertTrue(delay.isDisposed()); + + clock.tick(500); + assertFalse(invoked); +} + + +function testFire() { + delay.start(); + + clock.tick(50); + delay.fire(); + assertTrue(invoked); + assertFalse(delay.isActive()); + + invoked = false; + clock.tick(200); + assertFalse('Delay fired early with fire call, timeout should have been ' + + 'cleared', invoked); +} + +function testFireIfActive() { + delay.fireIfActive(); + assertFalse(invoked); + + delay.start(); + delay.fireIfActive(); + assertTrue(invoked); + invoked = false; + clock.tick(300); + assertFalse('Delay fired early with fireIfActive, timeout should have been ' + + 'cleared', invoked); +} diff --git a/mapbox-gl/closure-library/closure/goog/async/nexttick.js b/mapbox-gl/closure-library/closure/goog/async/nexttick.js new file mode 100644 index 0000000000..b0e3921beb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/nexttick.js @@ -0,0 +1,231 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a function to schedule running a function as soon + * as possible after the current JS execution stops and yields to the event + * loop. + * + */ + +goog.provide('goog.async.nextTick'); +goog.provide('goog.async.throwException'); + +goog.require('goog.debug.entryPointRegistry'); +goog.require('goog.functions'); +goog.require('goog.labs.userAgent.browser'); + + +/** + * Throw an item without interrupting the current execution context. For + * example, if processing a group of items in a loop, sometimes it is useful + * to report an error while still allowing the rest of the batch to be + * processed. + * @param {*} exception + */ +goog.async.throwException = function(exception) { + // Each throw needs to be in its own context. + goog.global.setTimeout(function() { throw exception; }, 0); +}; + + +/** + * Fires the provided callbacks as soon as possible after the current JS + * execution context. setTimeout(…, 0) takes at least 4ms when called from + * within another setTimeout(…, 0) for legacy reasons. + * + * This will not schedule the callback as a microtask (i.e. a task that can + * preempt user input or networking callbacks). It is meant to emulate what + * setTimeout(_, 0) would do if it were not throttled. If you desire microtask + * behavior, use {@see goog.Promise} instead. + * + * @param {function(this:SCOPE)} callback Callback function to fire as soon as + * possible. + * @param {SCOPE=} opt_context Object in whose scope to call the listener. + * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that + * ensures correctness at the cost of speed. See comments for details. + * @template SCOPE + */ +goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { + var cb = callback; + if (opt_context) { + cb = goog.bind(callback, opt_context); + } + cb = goog.async.nextTick.wrapCallback_(cb); + // window.setImmediate was introduced and currently only supported by IE10+, + // but due to a bug in the implementation it is not guaranteed that + // setImmediate is faster than setTimeout nor that setImmediate N is before + // setImmediate N+1. That is why we do not use the native version if + // available. We do, however, call setImmediate if it is a normal function + // because that indicates that it has been replaced by goog.testing.MockClock + // which we do want to support. + // See + // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 + // + // Note we do allow callers to also request setImmediate if they are willing + // to accept the possible tradeoffs of incorrectness in exchange for speed. + // The IE fallback of readystate change is much slower. + if (goog.isFunction(goog.global.setImmediate) && + (opt_useSetImmediate || !goog.global.Window || + goog.global.Window.prototype.setImmediate != goog.global.setImmediate)) { + goog.global.setImmediate(cb); + return; + } + + // Look for and cache the custom fallback version of setImmediate. + if (!goog.async.nextTick.setImmediate_) { + goog.async.nextTick.setImmediate_ = + goog.async.nextTick.getSetImmediateEmulator_(); + } + goog.async.nextTick.setImmediate_(cb); +}; + + +/** + * Cache for the setImmediate implementation. + * @type {function(function())} + * @private + */ +goog.async.nextTick.setImmediate_; + + +/** + * Determines the best possible implementation to run a function as soon as + * the JS event loop is idle. + * @return {function(function())} The "setImmediate" implementation. + * @private + */ +goog.async.nextTick.getSetImmediateEmulator_ = function() { + // Create a private message channel and use it to postMessage empty messages + // to ourselves. + var Channel = goog.global['MessageChannel']; + // If MessageChannel is not available and we are in a browser, implement + // an iframe based polyfill in browsers that have postMessage and + // document.addEventListener. The latter excludes IE8 because it has a + // synchronous postMessage implementation. + if (typeof Channel === 'undefined' && typeof window !== 'undefined' && + window.postMessage && window.addEventListener) { + /** @constructor */ + Channel = function() { + // Make an empty, invisible iframe. + var iframe = document.createElement('iframe'); + iframe.style.display = 'none'; + iframe.src = ''; + document.documentElement.appendChild(iframe); + var win = iframe.contentWindow; + var doc = win.document; + doc.open(); + doc.write(''); + doc.close(); + // Do not post anything sensitive over this channel, as the workaround for + // pages with file: origin could allow that information to be modified or + // intercepted. + var message = 'callImmediate' + Math.random(); + // The same origin policy rejects attempts to postMessage from file: urls + // unless the origin is '*'. + // TODO(b/16335441): Use '*' origin for data: and other similar protocols. + var origin = win.location.protocol == 'file:' ? + '*' : win.location.protocol + '//' + win.location.host; + var onmessage = goog.bind(function(e) { + // Validate origin and message to make sure that this message was + // intended for us. If the origin is set to '*' (see above) only the + // message needs to match since, for example, '*' != 'file://'. Allowing + // the wildcard is ok, as we are not concerned with security here. + if ((origin != '*' && e.origin != origin) || e.data != message) { + return; + } + this['port1'].onmessage(); + }, this); + win.addEventListener('message', onmessage, false); + this['port1'] = {}; + this['port2'] = { + postMessage: function() { + win.postMessage(message, origin); + } + }; + }; + } + if (typeof Channel !== 'undefined' && + (!goog.labs.userAgent.browser.isIE())) { + // Exclude all of IE due to + // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ + // which allows starving postMessage with a busy setTimeout loop. + // This currently affects IE10 and IE11 which would otherwise be able + // to use the postMessage based fallbacks. + var channel = new Channel(); + // Use a fifo linked list to call callbacks in the right order. + var head = {}; + var tail = head; + channel['port1'].onmessage = function() { + if (goog.isDef(head.next)) { + head = head.next; + var cb = head.cb; + head.cb = null; + cb(); + } + }; + return function(cb) { + tail.next = { + cb: cb + }; + tail = tail.next; + channel['port2'].postMessage(0); + }; + } + // Implementation for IE6+: Script elements fire an asynchronous + // onreadystatechange event when inserted into the DOM. + if (typeof document !== 'undefined' && 'onreadystatechange' in + document.createElement('script')) { + return function(cb) { + var script = document.createElement('script'); + script.onreadystatechange = function() { + // Clean up and call the callback. + script.onreadystatechange = null; + script.parentNode.removeChild(script); + script = null; + cb(); + cb = null; + }; + document.documentElement.appendChild(script); + }; + } + // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms + // or more. + return function(cb) { + goog.global.setTimeout(cb, 0); + }; +}; + + +/** + * Helper function that is overrided to protect callbacks with entry point + * monitor if the application monitors entry points. + * @param {function()} callback Callback function to fire as soon as possible. + * @return {function()} The wrapped callback. + * @private + */ +goog.async.nextTick.wrapCallback_ = goog.functions.identity; + + +// Register the callback function as an entry point, so that it can be +// monitored for exception handling, etc. This has to be done in this file +// since it requires special code to handle all browsers. +goog.debug.entryPointRegistry.register( + /** + * @param {function(!Function): !Function} transformer The transforming + * function. + */ + function(transformer) { + goog.async.nextTick.wrapCallback_ = transformer; + }); diff --git a/mapbox-gl/closure-library/closure/goog/async/nexttick_test.html b/mapbox-gl/closure-library/closure/goog/async/nexttick_test.html new file mode 100644 index 0000000000..556f4c123d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/nexttick_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.async.nextTick + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/async/nexttick_test.js b/mapbox-gl/closure-library/closure/goog/async/nexttick_test.js new file mode 100644 index 0000000000..f87a548373 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/nexttick_test.js @@ -0,0 +1,243 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.async.nextTickTest'); +goog.setTestOnly('goog.async.nextTickTest'); + +goog.require('goog.async.nextTick'); +goog.require('goog.debug.ErrorHandler'); +goog.require('goog.debug.entryPointRegistry'); +goog.require('goog.dom'); +goog.require('goog.labs.userAgent.browser'); +goog.require('goog.testing.AsyncTestCase'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); + +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall( + 'asyncNextTickTest'); + +var clock; +var propertyReplacer = new goog.testing.PropertyReplacer(); + +function setUp() { + clock = null; +} + +function tearDown() { + if (clock) { + clock.uninstall(); + } + goog.async.nextTick.setImmediate_ = undefined; + propertyReplacer.reset(); +} + + +function testNextTick() { + var c = 0; + var max = 100; + var async = true; + var counterStep = function(i) { + async = false; + assertEquals('Order correct', i, c); + c++; + if (c === max) { + asyncTestCase.continueTesting(); + } + }; + for (var i = 0; i < max; i++) { + goog.async.nextTick(goog.partial(counterStep, i)); + } + assertTrue(async); + asyncTestCase.waitForAsync('Wait for callback'); +} + + +function testNextTickSetImmediate() { + var c = 0; + var max = 100; + var async = true; + var counterStep = function(i) { + async = false; + assertEquals('Order correct', i, c); + c++; + if (c === max) { + asyncTestCase.continueTesting(); + } + }; + for (var i = 0; i < max; i++) { + goog.async.nextTick(goog.partial(counterStep, i), undefined, + /* opt_useSetImmediate */ true); + } + assertTrue(async); + asyncTestCase.waitForAsync('Wait for callback'); +} + +function testNextTickContext() { + var context = {}; + var c = 0; + var max = 10; + var async = true; + var counterStep = function(i) { + async = false; + assertEquals('Order correct', i, c); + assertEquals(context, this); + c++; + if (c === max) { + asyncTestCase.continueTesting(); + } + }; + for (var i = 0; i < max; i++) { + goog.async.nextTick(goog.partial(counterStep, i), context); + } + assertTrue(async); + asyncTestCase.waitForAsync('Wait for callback'); +} + + +function testNextTickMockClock() { + clock = new goog.testing.MockClock(true); + var result = ''; + goog.async.nextTick(function() { + result += 'a'; + }); + goog.async.nextTick(function() { + result += 'b'; + }); + goog.async.nextTick(function() { + result += 'c'; + }); + assertEquals('', result); + clock.tick(0); + assertEquals('abc', result); +} + + +function testNextTickDoesntSwallowError() { + var before = window.onerror; + var sentinel = 'sentinel'; + window.onerror = function(e) { + e = '' + e; + // Don't test for contents in IE7, which does not preserve the exception + // message. + if (e.indexOf('Exception thrown and not caught') == -1) { + assertContains(sentinel, e); + } + window.onerror = before; + asyncTestCase.continueTesting(); + return false; + }; + goog.async.nextTick(function() { + throw sentinel; + }); + asyncTestCase.waitForAsync('Wait for error'); +} + + +function testNextTickProtectEntryPoint() { + var errorHandlerCallbackCalled = false; + var errorHandler = new goog.debug.ErrorHandler(function() { + errorHandlerCallbackCalled = true; + }); + + // This is only testing wrapping the callback with the protected entry point, + // so it's okay to replace this function with a fake. + goog.async.nextTick.setImmediate_ = function(cb) { + try { + cb(); + fail('The callback should have thrown an error.'); + } catch (e) { + assertTrue( + e instanceof goog.debug.ErrorHandler.ProtectedFunctionError); + } + asyncTestCase.continueTesting(); + }; + var origSetImmediate; + if (window.setImmediate) { + origSetImmediate = window.setImmediate; + window.setImmediate = goog.async.nextTick.setImmediate_; + } + goog.debug.entryPointRegistry.monitorAll(errorHandler); + + function thrower() { + throw Error('This should be caught by the protected function.'); + } + asyncTestCase.waitForAsync('Wait for callback'); + goog.async.nextTick(thrower); + window.setImmediate = origSetImmediate; +} + + +function testNextTick_notStarvedBySetTimeout() { + // This test will timeout when affected by + // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ + // This test would fail without the fix introduced in cl/72472221 + // It keeps scheduling 0 timeouts and a single nextTick. If the nextTick + // ever fires, the IE specific problem does not occur. + var timeout; + function busy() { + timeout = setTimeout(function() { + busy(); + }, 0); + } + busy(); + goog.async.nextTick(function() { + if (timeout) { + clearTimeout(timeout); + } + asyncTestCase.continueTesting(); + }); + asyncTestCase.waitForAsync('Waiting not to starve'); +} + + +/** + * Test a scenario in which the iframe used by the postMessage polyfill gets a + * message that does not have match what is expected. In this case, the polyfill + * should not try to invoke a callback (which would result in an error because + * there would be no callbacks in the linked list). + */ +function testPostMessagePolyfillDoesNotPumpCallbackQueueIfMessageIsIncorrect() { + // IE does not use the postMessage polyfill. + if (goog.labs.userAgent.browser.isIE()) { + return; + } + + // Force postMessage pollyfill for setImmediate. + propertyReplacer.set(window, 'setImmediate', undefined); + propertyReplacer.set(window, 'MessageChannel', undefined); + + var callbackCalled = false; + goog.async.nextTick(function() { + callbackCalled = true; + }); + + var frame = document.getElementsByTagName('iframe')[0]; + frame.contentWindow.postMessage('bogus message', + window.location.protocol + '//' + window.location.host); + + var error = null; + frame.contentWindow.onerror = function(e) { + error = e; + }; + + setTimeout(function() { + assert('Callback should have been called.', callbackCalled); + assertNull('An unexpected error was thrown.', error); + goog.dom.removeNode(frame); + asyncTestCase.continueTesting(); + }, 0); + + asyncTestCase.waitForAsync('Waiting for callbacks to be invoked.'); +} + diff --git a/mapbox-gl/closure-library/closure/goog/async/run.js b/mapbox-gl/closure-library/closure/goog/async/run.js new file mode 100644 index 0000000000..4dbd3323fc --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/run.js @@ -0,0 +1,150 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.async.run'); + +goog.require('goog.async.nextTick'); +goog.require('goog.async.throwException'); +goog.require('goog.testing.watchers'); + + +/** + * Fires the provided callback just before the current callstack unwinds, or as + * soon as possible after the current JS execution context. + * @param {function(this:THIS)} callback + * @param {THIS=} opt_context Object to use as the "this value" when calling + * the provided function. + * @template THIS + */ +goog.async.run = function(callback, opt_context) { + if (!goog.async.run.schedule_) { + goog.async.run.initializeRunner_(); + } + if (!goog.async.run.workQueueScheduled_) { + // Nothing is currently scheduled, schedule it now. + goog.async.run.schedule_(); + goog.async.run.workQueueScheduled_ = true; + } + + goog.async.run.workQueue_.push( + new goog.async.run.WorkItem_(callback, opt_context)); +}; + + +/** + * Initializes the function to use to process the work queue. + * @private + */ +goog.async.run.initializeRunner_ = function() { + // If native Promises are available in the browser, just schedule the callback + // on a fulfilled promise, which is specified to be async, but as fast as + // possible. + if (goog.global.Promise && goog.global.Promise.resolve) { + var promise = goog.global.Promise.resolve(); + goog.async.run.schedule_ = function() { + promise.then(goog.async.run.processWorkQueue); + }; + } else { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + }; + } +}; + + +/** + * Forces goog.async.run to use nextTick instead of Promise. + * + * This should only be done in unit tests. It's useful because MockClock + * replaces nextTick, but not the browser Promise implementation, so it allows + * Promise-based code to be tested with MockClock. + */ +goog.async.run.forceNextTick = function() { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + }; +}; + + +/** + * The function used to schedule work asynchronousely. + * @private {function()} + */ +goog.async.run.schedule_; + + +/** @private {boolean} */ +goog.async.run.workQueueScheduled_ = false; + + +/** @private {!Array} */ +goog.async.run.workQueue_ = []; + + +if (goog.DEBUG) { + /** + * Reset the event queue. + * @private + */ + goog.async.run.resetQueue_ = function() { + goog.async.run.workQueueScheduled_ = false; + goog.async.run.workQueue_ = []; + }; + + // If there is a clock implemenation in use for testing + // and it is reset, reset the queue. + goog.testing.watchers.watchClockReset(goog.async.run.resetQueue_); +} + + +/** + * Run any pending goog.async.run work items. This function is not intended + * for general use, but for use by entry point handlers to run items ahead of + * goog.async.nextTick. + */ +goog.async.run.processWorkQueue = function() { + // NOTE: additional work queue items may be pushed while processing. + while (goog.async.run.workQueue_.length) { + // Don't let the work queue grow indefinitely. + var workItems = goog.async.run.workQueue_; + goog.async.run.workQueue_ = []; + for (var i = 0; i < workItems.length; i++) { + var workItem = workItems[i]; + try { + workItem.fn.call(workItem.scope); + } catch (e) { + goog.async.throwException(e); + } + } + } + + // There are no more work items, reset the work queue. + goog.async.run.workQueueScheduled_ = false; +}; + + + +/** + * @constructor + * @final + * @struct + * @private + * + * @param {function()} fn + * @param {Object|null|undefined} scope + */ +goog.async.run.WorkItem_ = function(fn, scope) { + /** @const */ this.fn = fn; + /** @const */ this.scope = scope; +}; diff --git a/mapbox-gl/closure-library/closure/goog/async/run_test.js b/mapbox-gl/closure-library/closure/goog/async/run_test.js new file mode 100644 index 0000000000..56d20526fa --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/run_test.js @@ -0,0 +1,147 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.async.runTest'); + +goog.require('goog.async.run'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); + +goog.setTestOnly('goog.async.runTest'); + + +var mockClock; +var futureCallback1, futureCallback2; + +function setUpPage() { + mockClock = new goog.testing.MockClock(); + mockClock.install(); +} + +function setUp() { + mockClock.reset(); + futureCallback1 = new goog.testing.recordFunction(); + futureCallback2 = new goog.testing.recordFunction(); +} + +function tearDown() { + futureCallback1 = null; + futureCallback2 = null; +} + +function tearDownPage() { + mockClock.uninstall(); + goog.dispose(mockClock); +} + +function testCalledAsync() { + goog.async.run(futureCallback1); + goog.async.run(futureCallback2); + + assertEquals(0, futureCallback1.getCallCount()); + assertEquals(0, futureCallback2.getCallCount()); + + // but the callbacks are scheduled... + mockClock.tick(); + + // and called. + assertEquals(1, futureCallback1.getCallCount()); + assertEquals(1, futureCallback2.getCallCount()); + + // and aren't called a second time. + assertEquals(1, futureCallback1.getCallCount()); + assertEquals(1, futureCallback2.getCallCount()); +} + +function testSequenceCalledInOrder() { + futureCallback1 = new goog.testing.recordFunction( + function() { + // called before futureCallback2 + assertEquals(0, futureCallback2.getCallCount()); + }); + futureCallback2 = new goog.testing.recordFunction( + function() { + // called after futureCallback1 + assertEquals(1, futureCallback1.getCallCount()); + }); + goog.async.run(futureCallback1); + goog.async.run(futureCallback2); + + // goog.async.run doesn't call the top callback immediately. + assertEquals(0, futureCallback1.getCallCount()); + + // but the callbacks are scheduled... + mockClock.tick(); + + // and called during the same "tick". + assertEquals(1, futureCallback1.getCallCount()); + assertEquals(1, futureCallback2.getCallCount()); +} + +function testSequenceScheduledTwice() { + goog.async.run(futureCallback1); + goog.async.run(futureCallback1); + + // goog.async.run doesn't call the top callback immediately. + assertEquals(0, futureCallback1.getCallCount()); + + // but the callbacks are scheduled... + mockClock.tick(); + + // and called twice during the same "tick". + assertEquals(2, futureCallback1.getCallCount()); +} + +function testSequenceCalledSync() { + futureCallback1 = new goog.testing.recordFunction( + function() { + goog.async.run(futureCallback2); + // goog.async.run doesn't call the inner callback immediately. + assertEquals(0, futureCallback2.getCallCount()); + }); + goog.async.run(futureCallback1); + + // goog.async.run doesn't call the top callback immediately. + assertEquals(0, futureCallback1.getCallCount()); + + // but the callbacks are scheduled... + mockClock.tick(); + + // and called during the same "tick". + assertEquals(1, futureCallback1.getCallCount()); + assertEquals(1, futureCallback2.getCallCount()); +} + +function testScope() { + var aScope = {}; + goog.async.run(futureCallback1); + goog.async.run(futureCallback2, aScope); + + // the callbacks are scheduled... + mockClock.tick(); + + // and called. + assertEquals(1, futureCallback1.getCallCount()); + assertEquals(1, futureCallback2.getCallCount()); + + // and get the correct scope. + var last1 = futureCallback1.popLastCall(); + assertEquals(0, last1.getArguments().length); + assertEquals(goog.global, last1.getThis()); + + var last2 = futureCallback2.popLastCall(); + assertEquals(0, last2.getArguments().length); + assertEquals(aScope, last2.getThis()); +} diff --git a/mapbox-gl/closure-library/closure/goog/async/throttle.js b/mapbox-gl/closure-library/closure/goog/async/throttle.js new file mode 100644 index 0000000000..939738eea4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/throttle.js @@ -0,0 +1,193 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the goog.async.Throttle class. + * + * @see ../demos/timers.html + */ + +goog.provide('goog.Throttle'); +goog.provide('goog.async.Throttle'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); + + + +/** + * Throttle will perform an action that is passed in no more than once + * per interval (specified in milliseconds). If it gets multiple signals + * to perform the action while it is waiting, it will only perform the action + * once at the end of the interval. + * @param {function(this: T)} listener Function to callback when the action is + * triggered. + * @param {number} interval Interval over which to throttle. The listener can + * only be called once per interval. + * @param {T=} opt_handler Object in whose scope to call the listener. + * @constructor + * @extends {goog.Disposable} + * @final + * @template T + */ +goog.async.Throttle = function(listener, interval, opt_handler) { + goog.Disposable.call(this); + + /** + * Function to callback + * @type {function(this: T)} + * @private + */ + this.listener_ = listener; + + /** + * Interval for the throttle time + * @type {number} + * @private + */ + this.interval_ = interval; + + /** + * "this" context for the listener + * @type {Object|undefined} + * @private + */ + this.handler_ = opt_handler; + + /** + * Cached callback function invoked after the throttle timeout completes + * @type {Function} + * @private + */ + this.callback_ = goog.bind(this.onTimer_, this); +}; +goog.inherits(goog.async.Throttle, goog.Disposable); + + + +/** + * A deprecated alias. + * @deprecated Use goog.async.Throttle instead. + * @constructor + * @final + */ +goog.Throttle = goog.async.Throttle; + + +/** + * Indicates that the action is pending and needs to be fired. + * @type {boolean} + * @private + */ +goog.async.Throttle.prototype.shouldFire_ = false; + + +/** + * Indicates the count of nested pauses currently in effect on the throttle. + * When this count is not zero, fired actions will be postponed until the + * throttle is resumed enough times to drop the pause count to zero. + * @type {number} + * @private + */ +goog.async.Throttle.prototype.pauseCount_ = 0; + + +/** + * Timer for scheduling the next callback + * @type {?number} + * @private + */ +goog.async.Throttle.prototype.timer_ = null; + + +/** + * Notifies the throttle that the action has happened. It will throttle the call + * so that the callback is not called too often according to the interval + * parameter passed to the constructor. + */ +goog.async.Throttle.prototype.fire = function() { + if (!this.timer_ && !this.pauseCount_) { + this.doAction_(); + } else { + this.shouldFire_ = true; + } +}; + + +/** + * Cancels any pending action callback. The throttle can be restarted by + * calling {@link #fire}. + */ +goog.async.Throttle.prototype.stop = function() { + if (this.timer_) { + goog.Timer.clear(this.timer_); + this.timer_ = null; + this.shouldFire_ = false; + } +}; + + +/** + * Pauses the throttle. All pending and future action callbacks will be + * delayed until the throttle is resumed. Pauses can be nested. + */ +goog.async.Throttle.prototype.pause = function() { + this.pauseCount_++; +}; + + +/** + * Resumes the throttle. If doing so drops the pausing count to zero, pending + * action callbacks will be executed as soon as possible, but still no sooner + * than an interval's delay after the previous call. Future action callbacks + * will be executed as normal. + */ +goog.async.Throttle.prototype.resume = function() { + this.pauseCount_--; + if (!this.pauseCount_ && this.shouldFire_ && !this.timer_) { + this.shouldFire_ = false; + this.doAction_(); + } +}; + + +/** @override */ +goog.async.Throttle.prototype.disposeInternal = function() { + goog.async.Throttle.superClass_.disposeInternal.call(this); + this.stop(); +}; + + +/** + * Handler for the timer to fire the throttle + * @private + */ +goog.async.Throttle.prototype.onTimer_ = function() { + this.timer_ = null; + + if (this.shouldFire_ && !this.pauseCount_) { + this.shouldFire_ = false; + this.doAction_(); + } +}; + + +/** + * Calls the callback + * @private + */ +goog.async.Throttle.prototype.doAction_ = function() { + this.timer_ = goog.Timer.callOnce(this.callback_, this.interval_); + this.listener_.call(this.handler_); +}; diff --git a/mapbox-gl/closure-library/closure/goog/async/throttle_test.html b/mapbox-gl/closure-library/closure/goog/async/throttle_test.html new file mode 100644 index 0000000000..2b300b92f1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/throttle_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.async.Throttle + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/async/throttle_test.js b/mapbox-gl/closure-library/closure/goog/async/throttle_test.js new file mode 100644 index 0000000000..a508f23178 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/async/throttle_test.js @@ -0,0 +1,95 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.async.ThrottleTest'); +goog.setTestOnly('goog.async.ThrottleTest'); + +goog.require('goog.async.Throttle'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); + +function testThrottle() { + var clock = new goog.testing.MockClock(true); + + var callBackCount = 0; + var callBackFunction = function() { + callBackCount++; + }; + + var throttle = new goog.async.Throttle(callBackFunction, 100); + assertEquals(0, callBackCount); + throttle.fire(); + assertEquals(1, callBackCount); + throttle.fire(); + assertEquals(1, callBackCount); + throttle.fire(); + throttle.fire(); + assertEquals(1, callBackCount); + clock.tick(101); + assertEquals(2, callBackCount); + clock.tick(101); + assertEquals(2, callBackCount); + + throttle.fire(); + assertEquals(3, callBackCount); + throttle.fire(); + assertEquals(3, callBackCount); + throttle.stop(); + clock.tick(101); + assertEquals(3, callBackCount); + throttle.fire(); + assertEquals(4, callBackCount); + clock.tick(101); + assertEquals(4, callBackCount); + + throttle.fire(); + throttle.fire(); + assertEquals(5, callBackCount); + throttle.pause(); + throttle.resume(); + assertEquals(5, callBackCount); + throttle.pause(); + clock.tick(101); + assertEquals(5, callBackCount); + throttle.resume(); + assertEquals(6, callBackCount); + clock.tick(101); + assertEquals(6, callBackCount); + throttle.pause(); + throttle.fire(); + assertEquals(6, callBackCount); + clock.tick(101); + assertEquals(6, callBackCount); + throttle.resume(); + assertEquals(7, callBackCount); + + throttle.pause(); + throttle.pause(); + clock.tick(101); + throttle.fire(); + throttle.resume(); + assertEquals(7, callBackCount); + throttle.resume(); + assertEquals(8, callBackCount); + + throttle.pause(); + throttle.pause(); + throttle.fire(); + throttle.resume(); + clock.tick(101); + assertEquals(8, callBackCount); + throttle.resume(); + assertEquals(9, callBackCount); + + clock.uninstall(); +} diff --git a/mapbox-gl/closure-library/closure/goog/base.js b/mapbox-gl/closure-library/closure/goog/base.js new file mode 100644 index 0000000000..87330a1656 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/base.js @@ -0,0 +1,2496 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bootstrap for the Google JS Library (Closure). + * + * In uncompiled mode base.js will write out Closure's deps file, unless the + * global CLOSURE_NO_DEPS is set to true. This allows projects to + * include their own deps file(s) from different locations. + * + * @author arv@google.com (Erik Arvidsson) + * + * @provideGoog + */ + + +/** + * @define {boolean} Overridden to true by the compiler when --closure_pass + * or --mark_as_compiled is specified. + */ +var COMPILED = false; + + +/** + * Base namespace for the Closure library. Checks to see goog is already + * defined in the current scope before assigning to prevent clobbering if + * base.js is loaded more than once. + * + * @const + */ +var goog = goog || {}; + + +/** + * Reference to the global context. In most cases this will be 'window'. + */ +goog.global = this; + + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before + * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, + * {@code goog.define} will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + *
+ *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + +/** + * Returns true if the specified value is not undefined. + * WARNING: Do not use this to test if an object has a property. Use the in + * operator instead. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + + +/** + * Builds an object structure for the provided namespace path, ensuring that + * names that already exist are not overwritten. For example: + * "a.b.c" -> a = {};a.b={};a.b.c={}; + * Used by goog.provide and goog.exportSymbol. + * @param {string} name name of the object that this file defines. + * @param {*=} opt_object the object to expose at the end of the path. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is |goog.global|. + * @private + */ +goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { + var parts = name.split('.'); + var cur = opt_objectToExportTo || goog.global; + + // Internet Explorer exhibits strange behavior when throwing errors from + // methods externed in this manner. See the testExportSymbolExceptions in + // base_test.html for an example. + if (!(parts[0] in cur) && cur.execScript) { + cur.execScript('var ' + parts[0]); + } + + // Certain browsers cannot parse code in the form for((a in b); c;); + // This pattern is produced by the JSCompiler when it collapses the + // statement above into the conditional loop below. To prevent this from + // happening, use a for-loop and reserve the init logic as below. + + // Parentheses added to eliminate strict JS warning in Firefox. + for (var part; parts.length && (part = parts.shift());) { + if (!parts.length && goog.isDef(opt_object)) { + // last part and we have an object; use it + cur[part] = opt_object; + } else if (cur[part]) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } +}; + + +/** + * Defines a named value. In uncompiled mode, the value is retreived from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler + * options or the value set in the CLOSURE_DEFINES object. + * + * @param {string} name The distinguished name to provide. + * @param {string|number|boolean} defaultValue + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { + value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; + } else if (goog.global.CLOSURE_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_DEFINES, name)) { + value = goog.global.CLOSURE_DEFINES[name]; + } + } + goog.exportPath_(name, value); +}; + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production js_binary can be easily stripped + * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most + * toString() methods should be declared inside an "if (goog.DEBUG)" conditional + * because they are generally used for debugging purposes and it is difficult + * for the JSCompiler to statically determine whether they are used. + */ +goog.DEBUG = true; + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as JSCompiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. + */ +goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the JSCompiler. + */ +goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements + * to discern dependencies, build dependency files (see deps.js), etc. + * + * @see goog.require + * @see goog.module + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + */ +goog.provide = function(name) { + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {Object=} opt_obj The object to embed in the namespace. + * @private + */ +goog.constructNamespace_ = function(name, opt_obj) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, opt_obj); +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lienent, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + */ +goog.module = function(name) { + if (!goog.isString(name) || + !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw Error('Invalid module identifier'); + } + if (!goog.isInModuleLoader_()) { + throw Error('Module ' + name + ' has been loaded incorrectly.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (goog.isProvided_(name)) { + // goog.require only return a value with-in goog.module files. + return name in goog.loadedModules_ ? + goog.loadedModules_[name] : + goog.getObjectByName(name); + } else { + return null; + } + } +}; + + +/** + * @private {?{ + * moduleName: (string|undefined), + * declareTestMethods: boolean + * }} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.moduleLoaderState_ != null; +}; + + +/** + * Indicate that a module's exports that are known test methods should + * be copied to the global object. This makes the test methods visible to + * test runners that inspect the global object. + * + * TODO(johnlenz): Make the test framework aware of goog.module so + * that this isn't necessary. Alternately combine this with goog.setTestOnly + * to minimize boiler plate. + * @suppress {missingProvide} + */ +goog.module.declareTestMethods = function() { + if (!goog.isInModuleLoader_()) { + throw new Error('goog.module.declareTestMethods must be called from ' + + 'within a goog.module'); + } + goog.moduleLoaderState_.declareTestMethods = true; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw new Error('goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error('goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw Error('Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +if (!COMPILED) { + + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} + + +/** + * Returns an object based on its fully qualified external name. The object + * is not found if null or undefined. If you are using a compilation pass that + * renames property names beware that using this function will not find renamed + * properties. + * + * @param {string} name The fully qualified name. + * @param {Object=} opt_obj The object within which to look; default is + * |goog.global|. + * @return {?} The value (object or primitive) or, if not found, null. + */ +goog.getObjectByName = function(name, opt_obj) { + var parts = name.split('.'); + var cur = opt_obj || goog.global; + for (var part; part = parts.shift(); ) { + if (goog.isDefAndNotNull(cur[part])) { + cur = cur[part]; + } else { + return null; + } + } + return cur; +}; + + +/** + * Globalizes a whole namespace, such as goog or goog.lang. + * + * @param {!Object} obj The namespace to globalize. + * @param {Object=} opt_global The object to add the properties to. + * @deprecated Properties may be explicitly exported to the global scope, but + * this should no longer be done in bulk. + */ +goog.globalize = function(obj, opt_global) { + var global = opt_global || goog.global; + for (var x in obj) { + global[x] = obj[x]; + } +}; + + +/** + * Adds a dependency from a file to the files it requires. + * @param {string} relPath The path to the js file. + * @param {!Array} provides An array of strings with + * the names of the objects this file provides. + * @param {!Array} requires An array of strings with + * the names of the objects this file requires. + * @param {boolean=} opt_isModule Whether this dependency must be loaded as + * a module as declared by goog.module. + */ +goog.addDependency = function(relPath, provides, requires, opt_isModule) { + if (goog.DEPENDENCIES_ENABLED) { + var provide, require; + var path = relPath.replace(/\\/g, '/'); + var deps = goog.dependencies_; + for (var i = 0; provide = provides[i]; i++) { + deps.nameToPath[provide] = path; + deps.pathIsModule[path] = !!opt_isModule; + } + for (var j = 0; require = requires[j]; j++) { + if (!(path in deps.requires)) { + deps.requires[path] = {}; + } + deps.requires[path][require] = true; + } + } +}; + + + + +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. +// +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. +// User-defined namespaces may need their own deps file. See http://go/js_deps, +// http://go/genjsdeps, or, externally, DepsWriter. +// https://developers.google.com/closure/library/docs/depswriter +// +// Because of legacy clients, the DOM loader can't be easily removed from +// base.js. Work is being done to make it disableable or replaceable for +// different environments (DOM-less JavaScript interpreters like Rhino or V8, +// for example). See bootstrap/ for more information. + + +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * Implements a system for the dynamic resolution of dependencies that works in + * parallel with the BUILD system. Note that all calls to goog.require will be + * stripped by the JSCompiler when the --closure_pass option is used. + * @see goog.provide + * @param {string} name Namespace to include (as was given in goog.provide()) in + * the form "goog.package.part". + * @return {?} If called within a goog.module file, the associated namespace or + * module otherwise null. + */ +goog.require = function(name) { + + // If the object already exists we do not need do do anything. + if (!COMPILED) { + if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { + goog.maybeProcessDeferredDep_(name); + } + + if (goog.isProvided_(name)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(name); + } else { + return null; + } + } + + if (goog.ENABLE_DEBUG_LOADER) { + var path = goog.getPathFromDeps_(name); + if (path) { + goog.included_[path] = true; + goog.writeScripts_(); + return null; + } + } + + var errorMessage = 'goog.require could not find: ' + name; + goog.logToConsole_(errorMessage); + + throw Error(errorMessage); + } +}; + + +/** + * Path for included scripts. + * @type {string} + */ +goog.basePath = ''; + + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to write out Closure's deps file. By default, the deps are written. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The function is passed the script source, which is a relative URI. It should + * return true if the script was imported, false otherwise. + * @type {(function(string): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + */ +goog.nullFunction = function() {}; + + +/** + * The identity function. Returns its first argument. + * + * @param {*=} opt_returnValue The single value that will be returned. + * @param {...*} var_args Optional trailing arguments. These are ignored. + * @return {?} The first argument. We can't know the type -- just pass it along + * without type. + * @deprecated Use goog.functions.identity instead. + */ +goog.identityFunction = function(opt_returnValue, var_args) { + return opt_returnValue; +}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * Note: This does not take the name of the function to override as an argument + * because that would make it more difficult to obfuscate our JavaScript code. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + */ +goog.abstractMethod = function() { + throw Error('unimplemented abstract method'); +}; + + +/** + * Adds a {@code getInstance} static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + */ +goog.addSingletonGetter = function(ctor) { + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + return ctor.instance_ = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the {@code goog.testing.singleton} module. The compiler + * removes this variable if unused. + * @type {!Array} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using {@code eval} when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or + * similiar. However in some environments the use of {@code eval} is banned + * so we provide an alternative. + */ +goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * the module identifier to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if goog.dependencies_ is available. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +if (goog.DEPENDENCIES_ENABLED) { + /** + * Object used to keep track of urls that have already been added. This record + * allows the prevention of circular dependencies. + * @private {!Object} + */ + goog.included_ = {}; + + + /** + * This object is used to keep track of dependencies and other data that is + * used for loading scripts. + * @private + * @type {{ + * pathIsModule: !Object, + * nameToPath: !Object, + * requires: !Object>, + * visited: !Object, + * written: !Object, + * deferred: !Object + * }} + */ + goog.dependencies_ = { + pathIsModule: {}, // 1 to 1 + + nameToPath: {}, // 1 to 1 + + requires: {}, // 1 to many + + // Used when resolving dependencies to prevent us from visiting file twice. + visited: {}, + + written: {}, // Used to keep track of script files we have written. + + deferred: {} // Used to track deferred module evaluations in old IEs + }; + + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + var doc = goog.global.document; + return typeof doc != 'undefined' && + 'write' in doc; // XULDocument misses write. + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.global.CLOSURE_BASE_PATH) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { + return; + } + var doc = goog.global.document; + var scripts = doc.getElementsByTagName('script'); + // Search backwards since the current script is in almost all cases the one + // that has base.js. + for (var i = scripts.length - 1; i >= 0; --i) { + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; + var qmark = src.lastIndexOf('?'); + var l = qmark == -1 ? src.length : qmark; + if (src.substr(l - 7, 7) == 'base.js') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; + + + /** + * Imports a script if, and only if, that script hasn't already been imported. + * (Must be called at execution time) + * @param {string} src Script source. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @private + */ + goog.importScript_ = function(src, opt_sourceText) { + var importScript = goog.global.CLOSURE_IMPORT_SCRIPT || + goog.writeScriptTag_; + if (importScript(src, opt_sourceText)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** @const @private {boolean} */ + goog.IS_OLD_IE_ = goog.global.document && + goog.global.document.all && !goog.global.atob; + + + /** + * Given a URL initiate retrieval and execution of the module. + * @param {string} src Script source URL. + * @private + */ + goog.importModule_ = function(src) { + // In an attempt to keep browsers from timing out loading scripts using + // synchronous XHRs, put each load in its own script block. + var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");'; + + if (goog.importScript_('', bootstrap)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** @private {!Array} */ + goog.queuedModules_ = []; + + + /** + * Return an appropriate module text. Suitable to insert into + * a script tag (that is unescaped). + * @param {string} srcUrl + * @param {string} scriptText + * @return {string} + * @private + */ + goog.wrapModule_ = function(srcUrl, scriptText) { + if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { + return '' + + 'goog.loadModule(function(exports) {' + + '"use strict";' + + scriptText + + '\n' + // terminate any trailing single line comment. + ';return exports' + + '});' + + '\n//# sourceURL=' + srcUrl + '\n'; + } else { + return '' + + 'goog.loadModule(' + + goog.global.JSON.stringify( + scriptText + '\n//# sourceURL=' + srcUrl + '\n') + + ');'; + } + }; + + // On IE9 and ealier, it is necessary to handle + // deferred module loads. In later browsers, the + // code to be evaluated is simply inserted as a script + // block in the correct order. To eval deferred + // code at the right time, we piggy back on goog.require to call + // goog.maybeProcessDeferredDep_. + // + // The goog.requires are used both to bootstrap + // the loading process (when no deps are available) and + // declare that they should be available. + // + // Here we eval the sources, if all the deps are available + // either already eval'd or goog.require'd. This will + // be the case when all the dependencies have already + // been loaded, and the dependent module is loaded. + // + // But this alone isn't sufficient because it is also + // necessary to handle the case where there is no root + // that is not deferred. For that there we register for an event + // and trigger goog.loadQueuedModules_ handle any remaining deferred + // evaluations. + + /** + * Handle any remaining deferred goog.module evals. + * @private + */ + goog.loadQueuedModules_ = function() { + var count = goog.queuedModules_.length; + if (count > 0) { + var queue = goog.queuedModules_; + goog.queuedModules_ = []; + for (var i = 0; i < count; i++) { + var path = queue[i]; + goog.maybeProcessDeferredPath_(path); + } + } + }; + + + /** + * Eval the named module if its dependencies are + * available. + * @param {string} name The module to load. + * @private + */ + goog.maybeProcessDeferredDep_ = function(name) { + if (goog.isDeferredModule_(name) && + goog.allDepsAreAvailable_(name)) { + var path = goog.getPathFromDeps_(name); + goog.maybeProcessDeferredPath_(goog.basePath + path); + } + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose evaluation has been deferred. + * @private + */ + goog.isDeferredModule_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && goog.dependencies_.pathIsModule[path]) { + var abspath = goog.basePath + path; + return (abspath) in goog.dependencies_.deferred; + } + return false; + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose declared dependencies have all been loaded + * (eval'd or a deferred module load) + * @private + */ + goog.allDepsAreAvailable_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && (path in goog.dependencies_.requires)) { + for (var requireName in goog.dependencies_.requires[path]) { + if (!goog.isProvided_(requireName) && + !goog.isDeferredModule_(requireName)) { + return false; + } + } + } + return true; + }; + + + /** + * @param {string} abspath + * @private + */ + goog.maybeProcessDeferredPath_ = function(abspath) { + if (abspath in goog.dependencies_.deferred) { + var src = goog.dependencies_.deferred[abspath]; + delete goog.dependencies_.deferred[abspath]; + goog.globalEval(src); + } + }; + + + /** + * @param {function(?):?|string} moduleDef The module definition. + */ + goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call {@code goog.module}, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: undefined, declareTestMethods: false}; + var exports; + if (goog.isFunction(moduleDef)) { + exports = moduleDef.call(goog.global, {}); + } else if (goog.isString(moduleDef)) { + exports = goog.loadModuleFromSource_.call(goog.global, moduleDef); + } else { + throw Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (!goog.isString(moduleName) || !moduleName) { + throw Error('Invalid module name \"' + moduleName + '\"'); + } + + // Don't seal legacy namespaces as they may be uses as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + goog.constructNamespace_(moduleName, exports); + } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) { + Object.seal(exports); + } + + goog.loadedModules_[moduleName] = exports; + if (goog.moduleLoaderState_.declareTestMethods) { + for (var entry in exports) { + if (entry.indexOf('test', 0) === 0 || + entry == 'tearDown' || + entry == 'setUp' || + entry == 'setUpPage' || + entry == 'tearDownPage') { + goog.global[entry] = exports[entry]; + } + } + } + } finally { + goog.moduleLoaderState_ = previousState; + } + }; + + + /** + * @param {string} source + * @return {!Object} + * @private + */ + goog.loadModuleFromSource_ = function(source) { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + var exports = {}; + eval(arguments[0]); + return exports; + }; + + + /** + * The default implementation of the import function. Writes a script tag to + * import the script. + * + * @param {string} src The script url. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @return {boolean} True if the script was imported, false otherwise. + * @private + */ + goog.writeScriptTag_ = function(src, opt_sourceText) { + if (goog.inHtmlDocument_()) { + var doc = goog.global.document; + + // If the user tries to require a new symbol after document load, + // something has gone terribly wrong. Doing a document.write would + // wipe out the page. + if (doc.readyState == 'complete') { + // Certain test frameworks load base.js multiple times, which tries + // to write deps.js each time. If that happens, just fail silently. + // These frameworks wipe the page between each load of base.js, so this + // is OK. + var isDeps = /\bdeps.js$/.test(src); + if (isDeps) { + return false; + } else { + throw Error('Cannot write "' + src + '" after document load'); + } + } + + var isOldIE = goog.IS_OLD_IE_; + + if (opt_sourceText === undefined) { + if (!isOldIE) { + doc.write( + ' + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/base_module_test.js b/mapbox-gl/closure-library/closure/goog/base_module_test.js new file mode 100644 index 0000000000..3678b48ab4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/base_module_test.js @@ -0,0 +1,113 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Unit tests for Closure's base.js's goog.module support. + */ + +goog.module('goog.baseModuleTest'); +goog.module.declareTestMethods(); +goog.setTestOnly('goog.baseModuleTest'); + + +// Used to test dynamic loading works, see testRequire* +var Timer = goog.require('goog.Timer'); +var Replacer = goog.require('goog.testing.PropertyReplacer'); +var jsunit = goog.require('goog.testing.jsunit'); + +var testModule = goog.require('goog.test_module'); + +var stubs = new Replacer(); + +function assertProvideFails(namespace) { + assertThrows('goog.provide(' + namespace + ') should have failed', + goog.partial(goog.provide, namespace)); +} + +function assertModuleFails(namespace) { + assertThrows('goog.module(' + namespace + ') should have failed', + goog.partial(goog.module, namespace)); +} + +exports = { + teardown: function() { + stubs.reset(); + }, + + testModuleDecl: function() { + // assert that goog.module doesn't modify the global namespace + assertUndefined('module failed to protect global namespace: ' + + 'goog.baseModuleTest', goog.baseModuleTest); + }, + + testModuleScoping: function() { + // assert test functions are exported to the global namespace + assertNotUndefined('module failed: testModule', testModule); + assertTrue('module failed: testModule', + goog.isFunction(goog.global.testModuleScoping)); + }, + + testProvideStrictness1: function() { + assertModuleFails('goog.xy'); // not in goog.loadModule + + assertProvideFails('goog.baseModuleTest'); // this file. + }, + + testProvideStrictness2: function() { + // goog.module "provides" a namespace + assertTrue(goog.isProvided_('goog.baseModuleTest')); + }, + + testExportSymbol: function() { + // Assert that export symbol works from within a goog.module. + var date = new Date(); + + assertTrue(typeof nodots == 'undefined'); + goog.exportSymbol('nodots', date); + assertEquals(date, nodots); // globals are accessible from within a module. + nodots = undefined; + }, + + //=== tests for Require logic === + + testLegacyRequire: function() { + // goog.Timer is a legacy module loaded above + assertNotUndefined('goog.Timer should be available', goog.Timer); + + // Verify that a legacy module can be aliases with goog.require + assertTrue('Timer should be the goog.Timer namespace object', + goog.Timer === Timer); + + // and its dependencies + assertNotUndefined( + 'goog.events.EventTarget should be available', + /** @suppress {missingRequire} */ goog.events.EventTarget); + }, + + testRequireModule: function() { + assertEquals('module failed to export legacy namespace: ' + + 'goog.test_module', testModule, goog.test_module); + assertUndefined('module failed to protect global namespace: ' + + 'goog.test_module_dep', goog.test_module_dep); + + // The test module is available under its alias + assertNotUndefined('testModule is loaded', testModule); + assertTrue('module failed: testModule', goog.isFunction(testModule)); + } +}; + +exports.testThisInModule = (function() { + assertEquals(this, goog.global); +}).bind(this); diff --git a/mapbox-gl/closure-library/closure/goog/base_test.html b/mapbox-gl/closure-library/closure/goog/base_test.html new file mode 100644 index 0000000000..f6814e457f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/base_test.html @@ -0,0 +1,33 @@ + + + + + +Closure Unit Tests - goog.* + + + + +
+ One + Two + Three +
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/base_test.js b/mapbox-gl/closure-library/closure/goog/base_test.js new file mode 100644 index 0000000000..31c2ebace8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/base_test.js @@ -0,0 +1,1495 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Unit tests for Closure's base.js. + * + * @nocompile + */ + +goog.provide('goog.baseTest'); + +goog.setTestOnly('goog.baseTest'); + +// Used to test dynamic loading works, see testRequire* +goog.require('goog.Timer'); +goog.require('goog.functions'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); +goog.require('goog.userAgent'); + +goog.require('goog.test_module'); +var earlyTestModuleGet = goog.module.get('goog.test_module'); + +function getFramedVars(name) { + var w = window.frames[name]; + var doc = w.document; + doc.open(); + doc.write(' + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/color/alpha_test.js b/mapbox-gl/closure-library/closure/goog/color/alpha_test.js new file mode 100644 index 0000000000..f9ed1a8011 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/color/alpha_test.js @@ -0,0 +1,321 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.color.alphaTest'); +goog.setTestOnly('goog.color.alphaTest'); + +goog.require('goog.array'); +goog.require('goog.color'); +goog.require('goog.color.alpha'); +goog.require('goog.testing.jsunit'); + +function testIsValidAlphaHexColor() { + var goodAlphaHexColors = ['#ffffffff', '#ff781259', '#01234567', '#Ff003DaB', + '#3CAF', '#abcdefab', '#3CAB']; + var badAlphaHexColors = ['#xxxxxxxx', '88990077', 'not_color', '#123456789', + 'fffffgfg']; + for (var i = 0; i < goodAlphaHexColors.length; i++) { + assertTrue(goodAlphaHexColors[i], + goog.color.alpha.isValidAlphaHexColor_(goodAlphaHexColors[i])); + } + for (var i = 0; i < badAlphaHexColors.length; i++) { + assertFalse(badAlphaHexColors[i], + goog.color.alpha.isValidAlphaHexColor_(badAlphaHexColors[i])); + } +} + +function testIsValidRgbaColor() { + var goodRgbaColors = ['rgba(255, 0, 0, 1)', 'rgba(255,127,0,1)', + 'rgba(0,0,255,0.5)', '(255, 26, 75, 0.2)', + 'RGBA(0, 55, 0, 0.6)', 'rgba(0, 200, 0, 0.123456789)']; + var badRgbaColors = ['(255, 0, 0)', '(2555,0,0, 0)', '(1,2,3,4,5)', + 'rgba(1,20,)', 'RGBA(20,20,20,)', 'RGBA', + 'rgba(255, 0, 0, 1.1)']; + for (var i = 0; i < goodRgbaColors.length; i++) { + assertEquals(goodRgbaColors[i], 4, + goog.color.alpha.isValidRgbaColor_(goodRgbaColors[i]).length); + } + for (var i = 0; i < badRgbaColors.length; i++) { + assertEquals(badRgbaColors[i], 0, + goog.color.alpha.isValidRgbaColor_(badRgbaColors[i]).length); + } +} + +function testIsValidHslaColor() { + var goodHslaColors = ['hsla(120, 0%, 0%, 1)', 'hsla(360,20%,0%,1)', + 'hsla(0,0%,50%,0.5)', 'HSLA(0, 55%, 0%, 0.6)', + 'hsla(0, 85%, 0%, 0.123456789)']; + var badHslaColors = ['(255, 0, 0, 0)', 'hsla(2555,0,0, 0)', 'hsla(1,2,3,4,5)', + 'hsla(1,20,)', 'HSLA(20,20,20,)', + 'hsla(255, 0, 0, 1.1)', 'HSLA']; + for (var i = 0; i < goodHslaColors.length; i++) { + assertEquals(goodHslaColors[i], 4, + goog.color.alpha.isValidHslaColor_(goodHslaColors[i]).length); + } + for (var i = 0; i < badHslaColors.length; i++) { + assertEquals(badHslaColors[i], 0, + goog.color.alpha.isValidHslaColor_(badHslaColors[i]).length); + } +} + +function testParse() { + var colors = ['rgba(15, 250, 77, 0.5)', '(127, 127, 127, 0.8)', '#ffeeddaa', + '12345678', 'hsla(160, 50%, 90%, 0.2)']; + var parsed = goog.array.map(colors, goog.color.alpha.parse); + assertEquals('rgba', parsed[0].type); + assertEquals(goog.color.alpha.rgbaToHex(15, 250, 77, 0.5), parsed[0].hex); + assertEquals('rgba', parsed[1].type); + assertEquals(goog.color.alpha.rgbaToHex(127, 127, 127, 0.8), parsed[1].hex); + assertEquals('hex', parsed[2].type); + assertEquals('#ffeeddaa', parsed[2].hex); + assertEquals('hex', parsed[3].type); + assertEquals('#12345678', parsed[3].hex); + assertEquals('hsla', parsed[4].type); + assertEquals('#d9f2ea33', parsed[4].hex); + + var badColors = ['rgb(01, 1, 23)', '(256, 256, 256)', '#ffeeddaa']; + for (var i = 0; i < badColors.length; i++) { + var e = assertThrows(badColors[i] + ' is not a valid color string', + goog.partial(goog.color.parse, badColors[i])); + assertContains('Error processing ' + badColors[i], + 'is not a valid color string', e.message); + } +} + +function testHexToRgba() { + var testColors = [['#B0FF2D66', [176, 255, 45, 0.4]], + ['#b26e5fcc', [178, 110, 95, 0.8]], + ['#66f3', [102, 102, 255, 0.2]]]; + + for (var i = 0; i < testColors.length; i++) { + var r = goog.color.alpha.hexToRgba(testColors[i][0]); + var t = testColors[i][1]; + + assertEquals('Red channel should match.', t[0], r[0]); + assertEquals('Green channel should match.', t[1], r[1]); + assertEquals('Blue channel should match.', t[2], r[2]); + assertEquals('Alpha channel should match.', t[3], r[3]); + } + + var badColors = ['', '#g00', 'some words']; + for (var i = 0; i < badColors.length; i++) { + var e = assertThrows( + goog.partial(goog.color.alpha.hexToRgba, badColors[i])); + assertEquals("'" + badColors[i] + "' is not a valid alpha hex color", + e.message); + } +} + +function testHexToRgbaStyle() { + assertEquals('rgba(255,0,0,1)', + goog.color.alpha.hexToRgbaStyle('#ff0000ff')); + assertEquals('rgba(206,206,206,0.8)', + goog.color.alpha.hexToRgbaStyle('#cecececc')); + assertEquals('rgba(51,204,170,0.2)', + goog.color.alpha.hexToRgbaStyle('#3CA3')); + assertEquals('rgba(1,2,3,0.016)', + goog.color.alpha.hexToRgbaStyle('#01020304')); + assertEquals('rgba(255,255,0,0.333)', + goog.color.alpha.hexToRgbaStyle('#FFFF0055')); + + var badHexColors = ['#12345', null, undefined, '#.1234567890']; + for (var i = 0; i < badHexColors.length; ++i) { + var e = assertThrows(badHexColors[i] + ' is an invalid hex color', + goog.partial(goog.color.alpha.hexToRgbaStyle, badHexColors[i])); + assertEquals("'" + badHexColors[i] + "' is not a valid alpha hex color", + e.message); + } +} + +function testRgbaToHex() { + assertEquals('#af13ffff', goog.color.alpha.rgbaToHex(175, 19, 255, 1)); + assertEquals('#357cf099', goog.color.alpha.rgbaToHex(53, 124, 240, 0.6)); + var badRgba = [[-1, -1, -1, -1], [0, 0, 0, 2], ['a', 'b', 'c', 'd'], + [undefined, 5, 5, 5]]; + for (var i = 0; i < badRgba.length; ++i) { + var e = assertThrows(badRgba[i] + ' is not a valid rgba color', + goog.partial(goog.color.alpha.rgbaArrayToHex, badRgba[i])); + assertContains('is not a valid RGBA color', e.message); + } +} + +function testRgbaToRgbaStyle() { + var testColors = [[[175, 19, 255, 1], 'rgba(175,19,255,1)'], + [[53, 124, 240, .6], 'rgba(53,124,240,0.6)'], + [[10, 20, 30, .1234567], 'rgba(10,20,30,0.123)'], + [[20, 30, 40, 1 / 3], 'rgba(20,30,40,0.333)']]; + + for (var i = 0; i < testColors.length; ++i) { + var r = goog.color.alpha.rgbaToRgbaStyle(testColors[i][0][0], + testColors[i][0][1], + testColors[i][0][2], + testColors[i][0][3]); + assertEquals(testColors[i][1], r); + } + + var badColors = [[0, 0, 0, 2]]; + for (var i = 0; i < badColors.length; ++i) { + var e = assertThrows(goog.partial(goog.color.alpha.rgbaToRgbaStyle, + badColors[i][0], badColors[i][1], badColors[i][2], badColors[i][3])); + + assertContains('is not a valid RGBA color', e.message); + } + + // Loop through all bad color values and ensure they fail in each channel. + var badValues = [-1, 300, 'a', undefined, null, NaN]; + var color = [0, 0, 0, 0]; + for (var i = 0; i < badValues.length; ++i) { + for (var channel = 0; channel < color.length; ++channel) { + color[channel] = badValues[i]; + var e = assertThrows(color + ' is not a valid rgba color', + goog.partial(goog.color.alpha.rgbaToRgbaStyle, color)); + assertContains('is not a valid RGBA color', e.message); + + color[channel] = 0; + } + } +} + +function testRgbaArrayToRgbaStyle() { + var testColors = [[[175, 19, 255, 1], 'rgba(175,19,255,1)'], + [[53, 124, 240, .6], 'rgba(53,124,240,0.6)']]; + + for (var i = 0; i < testColors.length; ++i) { + var r = goog.color.alpha.rgbaArrayToRgbaStyle(testColors[i][0]); + assertEquals(testColors[i][1], r); + } + + var badColors = [[0, 0, 0, 2]]; + for (var i = 0; i < badColors.length; ++i) { + var e = assertThrows(goog.partial(goog.color.alpha.rgbaArrayToRgbaStyle, + badColors[i])); + + assertContains('is not a valid RGBA color', e.message); + } + + // Loop through all bad color values and ensure they fail in each channel. + var badValues = [-1, 300, 'a', undefined, null, NaN]; + var color = [0, 0, 0, 0]; + for (var i = 0; i < badValues.length; ++i) { + for (var channel = 0; channel < color.length; ++channel) { + color[channel] = badValues[i]; + var e = assertThrows(color + ' is not a valid rgba color', + goog.partial(goog.color.alpha.rgbaToRgbaStyle, color)); + assertContains('is not a valid RGBA color', e.message); + + color[channel] = 0; + } + } +} + +function testRgbaArrayToHsla() { + var opaqueBlueRgb = [0, 0, 255, 1]; + var opaqueBlueHsl = goog.color.alpha.rgbaArrayToHsla(opaqueBlueRgb); + assertArrayEquals('Conversion from RGBA to HSLA should be as expected', + [240, 1, 0.5, 1], opaqueBlueHsl); + + var nearlyOpaqueYellowRgb = [255, 190, 0, 0.7]; + var nearlyOpaqueYellowHsl = + goog.color.alpha.rgbaArrayToHsla(nearlyOpaqueYellowRgb); + assertArrayEquals('Conversion from RGBA to HSLA should be as expected', + [45, 1, 0.5, 0.7], nearlyOpaqueYellowHsl); + + var transparentPurpleRgb = [180, 0, 255, 0]; + var transparentPurpleHsl = + goog.color.alpha.rgbaArrayToHsla(transparentPurpleRgb); + assertArrayEquals('Conversion from RGBA to HSLA should be as expected', + [282, 1, 0.5, 0], transparentPurpleHsl); +} + +function testNormalizeAlphaHex() { + var compactColor = '#abcd'; + var normalizedCompactColor = + goog.color.alpha.normalizeAlphaHex_(compactColor); + assertEquals('The color should have been normalized to the right length', + '#aabbccdd', normalizedCompactColor); + + var uppercaseColor = '#ABCDEF01'; + var normalizedUppercaseColor = + goog.color.alpha.normalizeAlphaHex_(uppercaseColor); + assertEquals('The color should have been normalized to lowercase', + '#abcdef01', normalizedUppercaseColor); +} + +function testHsvaArrayToHex() { + var opaqueSkyBlueHsv = [190, 1, 255, 1]; + var opaqueSkyBlueHex = goog.color.alpha.hsvaArrayToHex(opaqueSkyBlueHsv); + assertEquals('The HSVA array should have been properly converted to hex', + '#00d4ffff', opaqueSkyBlueHex); + + var halfTransparentPinkHsv = [300, 1, 255, 0.5]; + var halfTransparentPinkHex = + goog.color.alpha.hsvaArrayToHex(halfTransparentPinkHsv); + assertEquals('The HSVA array should have been properly converted to hex', + '#ff00ff7f', halfTransparentPinkHex); + + var transparentDarkTurquoiseHsv = [175, 1, 127, 0.5]; + var transparentDarkTurquoiseHex = + goog.color.alpha.hsvaArrayToHex(transparentDarkTurquoiseHsv); + assertEquals('The HSVA array should have been properly converted to hex', + '#007f747f', transparentDarkTurquoiseHex); +} + +function testExtractHexColor() { + var opaqueRed = '#ff0000ff'; + var red = goog.color.alpha.extractHexColor(opaqueRed); + assertEquals('The hex part of the color should have been extracted correctly', + '#ff0000', red); + + var halfOpaqueDarkGreenCompact = '#0507'; + var darkGreen = + goog.color.alpha.extractHexColor(halfOpaqueDarkGreenCompact); + assertEquals('The hex part of the color should have been extracted correctly', + '#005500', darkGreen); + +} + +function testExtractAlpha() { + var colors = ['#ff0000ff', '#0507', '#ff000005']; + var expectedOpacities = ['ff', '77', '05']; + + for (var i = 0; i < colors.length; i++) { + var opacity = goog.color.alpha.extractAlpha(colors[i]); + assertEquals('The alpha transparency should have been extracted correctly', + expectedOpacities[i], opacity); + } +} + +function testHslaArrayToRgbaStyle() { + assertEquals('rgba(102,255,102,0.5)', + goog.color.alpha.hslaArrayToRgbaStyle([120, 100, 70, 0.5])); + assertEquals('rgba(28,23,23,0.9)', + goog.color.alpha.hslaArrayToRgbaStyle([0, 10, 10, 0.9])); +} + +function testRgbaStyleParsableResult() { + var testColors = [[175, 19, 255, 1], + [53, 124, 240, .6], + [20, 30, 40, 0.3333333], + [255, 255, 255, 0.7071067811865476]]; + + for (var i = 0, testColor; testColor = testColors[i]; i++) { + var rgbaStyle = goog.color.alpha.rgbaStyle_(testColor); + var parsedColor = goog.color.alpha.hexToRgba( + goog.color.alpha.parse(rgbaStyle).hex); + assertEquals(testColor[0], parsedColor[0]); + assertEquals(testColor[1], parsedColor[1]); + assertEquals(testColor[2], parsedColor[2]); + // Parsing keeps a 1/255 accuracy on the alpha channel. + assertRoughlyEquals(testColor[3], parsedColor[3], 0.005); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/color/color.js b/mapbox-gl/closure-library/closure/goog/color/color.js new file mode 100644 index 0000000000..8220532b9f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/color/color.js @@ -0,0 +1,776 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities related to color and color conversion. + */ + +goog.provide('goog.color'); +goog.provide('goog.color.Hsl'); +goog.provide('goog.color.Hsv'); +goog.provide('goog.color.Rgb'); + +goog.require('goog.color.names'); +goog.require('goog.math'); + + +/** + * RGB color representation. An array containing three elements [r, g, b], + * each an integer in [0, 255], representing the red, green, and blue components + * of the color respectively. + * @typedef {Array} + */ +goog.color.Rgb; + + +/** + * HSV color representation. An array containing three elements [h, s, v]: + * h (hue) must be an integer in [0, 360], cyclic. + * s (saturation) must be a number in [0, 1]. + * v (value/brightness) must be an integer in [0, 255]. + * @typedef {Array} + */ +goog.color.Hsv; + + +/** + * HSL color representation. An array containing three elements [h, s, l]: + * h (hue) must be an integer in [0, 360], cyclic. + * s (saturation) must be a number in [0, 1]. + * l (lightness) must be a number in [0, 1]. + * @typedef {Array} + */ +goog.color.Hsl; + + +/** + * Parses a color out of a string. + * @param {string} str Color in some format. + * @return {{hex: string, type: string}} 'hex' is a string containing a hex + * representation of the color, 'type' is a string containing the type + * of color format passed in ('hex', 'rgb', 'named'). + */ +goog.color.parse = function(str) { + var result = {}; + str = String(str); + + var maybeHex = goog.color.prependHashIfNecessaryHelper(str); + if (goog.color.isValidHexColor_(maybeHex)) { + result.hex = goog.color.normalizeHex(maybeHex); + result.type = 'hex'; + return result; + } else { + var rgb = goog.color.isValidRgbColor_(str); + if (rgb.length) { + result.hex = goog.color.rgbArrayToHex(rgb); + result.type = 'rgb'; + return result; + } else if (goog.color.names) { + var hex = goog.color.names[str.toLowerCase()]; + if (hex) { + result.hex = hex; + result.type = 'named'; + return result; + } + } + } + throw Error(str + ' is not a valid color string'); +}; + + +/** + * Determines if the given string can be parsed as a color. + * {@see goog.color.parse}. + * @param {string} str Potential color string. + * @return {boolean} True if str is in a format that can be parsed to a color. + */ +goog.color.isValidColor = function(str) { + var maybeHex = goog.color.prependHashIfNecessaryHelper(str); + return !!(goog.color.isValidHexColor_(maybeHex) || + goog.color.isValidRgbColor_(str).length || + goog.color.names && goog.color.names[str.toLowerCase()]); +}; + + +/** + * Parses red, green, blue components out of a valid rgb color string. + * Throws Error if the color string is invalid. + * @param {string} str RGB representation of a color. + * {@see goog.color.isValidRgbColor_}. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.parseRgb = function(str) { + var rgb = goog.color.isValidRgbColor_(str); + if (!rgb.length) { + throw Error(str + ' is not a valid RGB color'); + } + return rgb; +}; + + +/** + * Converts a hex representation of a color to RGB. + * @param {string} hexColor Color to convert. + * @return {string} string of the form 'rgb(R,G,B)' which can be used in + * styles. + */ +goog.color.hexToRgbStyle = function(hexColor) { + return goog.color.rgbStyle_(goog.color.hexToRgb(hexColor)); +}; + + +/** + * Regular expression for extracting the digits in a hex color triplet. + * @type {RegExp} + * @private + */ +goog.color.hexTripletRe_ = /#(.)(.)(.)/; + + +/** + * Normalize an hex representation of a color + * @param {string} hexColor an hex color string. + * @return {string} hex color in the format '#rrggbb' with all lowercase + * literals. + */ +goog.color.normalizeHex = function(hexColor) { + if (!goog.color.isValidHexColor_(hexColor)) { + throw Error("'" + hexColor + "' is not a valid hex color"); + } + if (hexColor.length == 4) { // of the form #RGB + hexColor = hexColor.replace(goog.color.hexTripletRe_, '#$1$1$2$2$3$3'); + } + return hexColor.toLowerCase(); +}; + + +/** + * Converts a hex representation of a color to RGB. + * @param {string} hexColor Color to convert. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hexToRgb = function(hexColor) { + hexColor = goog.color.normalizeHex(hexColor); + var r = parseInt(hexColor.substr(1, 2), 16); + var g = parseInt(hexColor.substr(3, 2), 16); + var b = parseInt(hexColor.substr(5, 2), 16); + + return [r, g, b]; +}; + + +/** + * Converts a color from RGB to hex representation. + * @param {number} r Amount of red, int between 0 and 255. + * @param {number} g Amount of green, int between 0 and 255. + * @param {number} b Amount of blue, int between 0 and 255. + * @return {string} hex representation of the color. + */ +goog.color.rgbToHex = function(r, g, b) { + r = Number(r); + g = Number(g); + b = Number(b); + if (isNaN(r) || r < 0 || r > 255 || + isNaN(g) || g < 0 || g > 255 || + isNaN(b) || b < 0 || b > 255) { + throw Error('"(' + r + ',' + g + ',' + b + '") is not a valid RGB color'); + } + var hexR = goog.color.prependZeroIfNecessaryHelper(r.toString(16)); + var hexG = goog.color.prependZeroIfNecessaryHelper(g.toString(16)); + var hexB = goog.color.prependZeroIfNecessaryHelper(b.toString(16)); + return '#' + hexR + hexG + hexB; +}; + + +/** + * Converts a color from RGB to hex representation. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.rgbArrayToHex = function(rgb) { + return goog.color.rgbToHex(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts a color from RGB color space to HSL color space. + * Modified from {@link http://en.wikipedia.org/wiki/HLS_color_space}. + * @param {number} r Value of red, in [0, 255]. + * @param {number} g Value of green, in [0, 255]. + * @param {number} b Value of blue, in [0, 255]. + * @return {!goog.color.Hsl} hsl representation of the color. + */ +goog.color.rgbToHsl = function(r, g, b) { + // First must normalize r, g, b to be between 0 and 1. + var normR = r / 255; + var normG = g / 255; + var normB = b / 255; + var max = Math.max(normR, normG, normB); + var min = Math.min(normR, normG, normB); + var h = 0; + var s = 0; + + // Luminosity is the average of the max and min rgb color intensities. + var l = 0.5 * (max + min); + + // The hue and saturation are dependent on which color intensity is the max. + // If max and min are equal, the color is gray and h and s should be 0. + if (max != min) { + if (max == normR) { + h = 60 * (normG - normB) / (max - min); + } else if (max == normG) { + h = 60 * (normB - normR) / (max - min) + 120; + } else if (max == normB) { + h = 60 * (normR - normG) / (max - min) + 240; + } + + if (0 < l && l <= 0.5) { + s = (max - min) / (2 * l); + } else { + s = (max - min) / (2 - 2 * l); + } + } + + // Make sure the hue falls between 0 and 360. + return [Math.round(h + 360) % 360, s, l]; +}; + + +/** + * Converts a color from RGB color space to HSL color space. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {!goog.color.Hsl} hsl representation of the color. + */ +goog.color.rgbArrayToHsl = function(rgb) { + return goog.color.rgbToHsl(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Helper for hslToRgb. + * @param {number} v1 Helper variable 1. + * @param {number} v2 Helper variable 2. + * @param {number} vH Helper variable 3. + * @return {number} Appropriate RGB value, given the above. + * @private + */ +goog.color.hueToRgb_ = function(v1, v2, vH) { + if (vH < 0) { + vH += 1; + } else if (vH > 1) { + vH -= 1; + } + if ((6 * vH) < 1) { + return (v1 + (v2 - v1) * 6 * vH); + } else if (2 * vH < 1) { + return v2; + } else if (3 * vH < 2) { + return (v1 + (v2 - v1) * ((2 / 3) - vH) * 6); + } + return v1; +}; + + +/** + * Converts a color from HSL color space to RGB color space. + * Modified from {@link http://www.easyrgb.com/math.html} + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} l Luminosity, in [0, 1]. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hslToRgb = function(h, s, l) { + var r = 0; + var g = 0; + var b = 0; + var normH = h / 360; // normalize h to fall in [0, 1] + + if (s == 0) { + r = g = b = l * 255; + } else { + var temp1 = 0; + var temp2 = 0; + if (l < 0.5) { + temp2 = l * (1 + s); + } else { + temp2 = l + s - (s * l); + } + temp1 = 2 * l - temp2; + r = 255 * goog.color.hueToRgb_(temp1, temp2, normH + (1 / 3)); + g = 255 * goog.color.hueToRgb_(temp1, temp2, normH); + b = 255 * goog.color.hueToRgb_(temp1, temp2, normH - (1 / 3)); + } + + return [Math.round(r), Math.round(g), Math.round(b)]; +}; + + +/** + * Converts a color from HSL color space to RGB color space. + * @param {goog.color.Hsl} hsl hsl representation of the color. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hslArrayToRgb = function(hsl) { + return goog.color.hslToRgb(hsl[0], hsl[1], hsl[2]); +}; + + +/** + * Helper for isValidHexColor_. + * @type {RegExp} + * @private + */ +goog.color.validHexColorRe_ = /^#(?:[0-9a-f]{3}){1,2}$/i; + + +/** + * Checks if a string is a valid hex color. We expect strings of the format + * #RRGGBB (ex: #1b3d5f) or #RGB (ex: #3CA == #33CCAA). + * @param {string} str String to check. + * @return {boolean} Whether the string is a valid hex color. + * @private + */ +goog.color.isValidHexColor_ = function(str) { + return goog.color.validHexColorRe_.test(str); +}; + + +/** + * Helper for isNormalizedHexColor_. + * @type {RegExp} + * @private + */ +goog.color.normalizedHexColorRe_ = /^#[0-9a-f]{6}$/; + + +/** + * Checks if a string is a normalized hex color. + * We expect strings of the format #RRGGBB (ex: #1b3d5f) + * using only lowercase letters. + * @param {string} str String to check. + * @return {boolean} Whether the string is a normalized hex color. + * @private + */ +goog.color.isNormalizedHexColor_ = function(str) { + return goog.color.normalizedHexColorRe_.test(str); +}; + + +/** + * Regular expression for matching and capturing RGB style strings. Helper for + * isValidRgbColor_. + * @type {RegExp} + * @private + */ +goog.color.rgbColorRe_ = + /^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i; + + +/** + * Checks if a string is a valid rgb color. We expect strings of the format + * '(r, g, b)', or 'rgb(r, g, b)', where each color component is an int in + * [0, 255]. + * @param {string} str String to check. + * @return {!goog.color.Rgb} the rgb representation of the color if it is + * a valid color, or the empty array otherwise. + * @private + */ +goog.color.isValidRgbColor_ = function(str) { + // Each component is separate (rather than using a repeater) so we can + // capture the match. Also, we explicitly set each component to be either 0, + // or start with a non-zero, to prevent octal numbers from slipping through. + var regExpResultArray = str.match(goog.color.rgbColorRe_); + if (regExpResultArray) { + var r = Number(regExpResultArray[1]); + var g = Number(regExpResultArray[2]); + var b = Number(regExpResultArray[3]); + if (r >= 0 && r <= 255 && + g >= 0 && g <= 255 && + b >= 0 && b <= 255) { + return [r, g, b]; + } + } + return []; +}; + + +/** + * Takes a hex value and prepends a zero if it's a single digit. + * Small helper method for use by goog.color and friends. + * @param {string} hex Hex value to prepend if single digit. + * @return {string} hex value prepended with zero if it was single digit, + * otherwise the same value that was passed in. + */ +goog.color.prependZeroIfNecessaryHelper = function(hex) { + return hex.length == 1 ? '0' + hex : hex; +}; + + +/** + * Takes a string a prepends a '#' sign if one doesn't exist. + * Small helper method for use by goog.color and friends. + * @param {string} str String to check. + * @return {string} The value passed in, prepended with a '#' if it didn't + * already have one. + */ +goog.color.prependHashIfNecessaryHelper = function(str) { + return str.charAt(0) == '#' ? str : '#' + str; +}; + + +/** + * Takes an array of [r, g, b] and converts it into a string appropriate for + * CSS styles. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {string} string of the form 'rgb(r,g,b)'. + * @private + */ +goog.color.rgbStyle_ = function(rgb) { + return 'rgb(' + rgb.join(',') + ')'; +}; + + +/** + * Converts an HSV triplet to an RGB array. V is brightness because b is + * reserved for blue in RGB. + * @param {number} h Hue value in [0, 360]. + * @param {number} s Saturation value in [0, 1]. + * @param {number} brightness brightness in [0, 255]. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hsvToRgb = function(h, s, brightness) { + var red = 0; + var green = 0; + var blue = 0; + if (s == 0) { + red = brightness; + green = brightness; + blue = brightness; + } else { + var sextant = Math.floor(h / 60); + var remainder = (h / 60) - sextant; + var val1 = brightness * (1 - s); + var val2 = brightness * (1 - (s * remainder)); + var val3 = brightness * (1 - (s * (1 - remainder))); + switch (sextant) { + case 1: + red = val2; + green = brightness; + blue = val1; + break; + case 2: + red = val1; + green = brightness; + blue = val3; + break; + case 3: + red = val1; + green = val2; + blue = brightness; + break; + case 4: + red = val3; + green = val1; + blue = brightness; + break; + case 5: + red = brightness; + green = val1; + blue = val2; + break; + case 6: + case 0: + red = brightness; + green = val3; + blue = val1; + break; + } + } + + return [Math.floor(red), Math.floor(green), Math.floor(blue)]; +}; + + +/** + * Converts from RGB values to an array of HSV values. + * @param {number} red Red value in [0, 255]. + * @param {number} green Green value in [0, 255]. + * @param {number} blue Blue value in [0, 255]. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.rgbToHsv = function(red, green, blue) { + + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + var hue; + var saturation; + var value = max; + if (min == max) { + hue = 0; + saturation = 0; + } else { + var delta = (max - min); + saturation = delta / max; + + if (red == max) { + hue = (green - blue) / delta; + } else if (green == max) { + hue = 2 + ((blue - red) / delta); + } else { + hue = 4 + ((red - green) / delta); + } + hue *= 60; + if (hue < 0) { + hue += 360; + } + if (hue > 360) { + hue -= 360; + } + } + + return [hue, saturation, value]; +}; + + +/** + * Converts from an array of RGB values to an array of HSV values. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.rgbArrayToHsv = function(rgb) { + return goog.color.rgbToHsv(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts an HSV triplet to an RGB array. + * @param {goog.color.Hsv} hsv hsv representation of the color. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hsvArrayToRgb = function(hsv) { + return goog.color.hsvToRgb(hsv[0], hsv[1], hsv[2]); +}; + + +/** + * Converts a hex representation of a color to HSL. + * @param {string} hex Color to convert. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.hexToHsl = function(hex) { + var rgb = goog.color.hexToRgb(hex); + return goog.color.rgbToHsl(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts from h,s,l values to a hex string + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} l Luminosity, in [0, 1]. + * @return {string} hex representation of the color. + */ +goog.color.hslToHex = function(h, s, l) { + return goog.color.rgbArrayToHex(goog.color.hslToRgb(h, s, l)); +}; + + +/** + * Converts from an hsl array to a hex string + * @param {goog.color.Hsl} hsl hsl representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.hslArrayToHex = function(hsl) { + return goog.color.rgbArrayToHex(goog.color.hslToRgb(hsl[0], hsl[1], hsl[2])); +}; + + +/** + * Converts a hex representation of a color to HSV + * @param {string} hex Color to convert. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.hexToHsv = function(hex) { + return goog.color.rgbArrayToHsv(goog.color.hexToRgb(hex)); +}; + + +/** + * Converts from h,s,v values to a hex string + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} v Value, in [0, 255]. + * @return {string} hex representation of the color. + */ +goog.color.hsvToHex = function(h, s, v) { + return goog.color.rgbArrayToHex(goog.color.hsvToRgb(h, s, v)); +}; + + +/** + * Converts from an HSV array to a hex string + * @param {goog.color.Hsv} hsv hsv representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.hsvArrayToHex = function(hsv) { + return goog.color.hsvToHex(hsv[0], hsv[1], hsv[2]); +}; + + +/** + * Calculates the Euclidean distance between two color vectors on an HSL sphere. + * A demo of the sphere can be found at: + * http://en.wikipedia.org/wiki/HSL_color_space + * In short, a vector for color (H, S, L) in this system can be expressed as + * (S*L'*cos(2*PI*H), S*L'*sin(2*PI*H), L), where L' = abs(L - 0.5), and we + * simply calculate the 1-2 distance using these coordinates + * @param {goog.color.Hsl} hsl1 First color in hsl representation. + * @param {goog.color.Hsl} hsl2 Second color in hsl representation. + * @return {number} Distance between the two colors, in the range [0, 1]. + */ +goog.color.hslDistance = function(hsl1, hsl2) { + var sl1, sl2; + if (hsl1[2] <= 0.5) { + sl1 = hsl1[1] * hsl1[2]; + } else { + sl1 = hsl1[1] * (1.0 - hsl1[2]); + } + + if (hsl2[2] <= 0.5) { + sl2 = hsl2[1] * hsl2[2]; + } else { + sl2 = hsl2[1] * (1.0 - hsl2[2]); + } + + var h1 = hsl1[0] / 360.0; + var h2 = hsl2[0] / 360.0; + var dh = (h1 - h2) * 2.0 * Math.PI; + return (hsl1[2] - hsl2[2]) * (hsl1[2] - hsl2[2]) + + sl1 * sl1 + sl2 * sl2 - 2 * sl1 * sl2 * Math.cos(dh); +}; + + +/** + * Blend two colors together, using the specified factor to indicate the weight + * given to the first color + * @param {goog.color.Rgb} rgb1 First color represented in rgb. + * @param {goog.color.Rgb} rgb2 Second color represented in rgb. + * @param {number} factor The weight to be given to rgb1 over rgb2. Values + * should be in the range [0, 1]. If less than 0, factor will be set to 0. + * If greater than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined color represented in rgb. + */ +goog.color.blend = function(rgb1, rgb2, factor) { + factor = goog.math.clamp(factor, 0, 1); + + return [ + Math.round(factor * rgb1[0] + (1.0 - factor) * rgb2[0]), + Math.round(factor * rgb1[1] + (1.0 - factor) * rgb2[1]), + Math.round(factor * rgb1[2] + (1.0 - factor) * rgb2[2]) + ]; +}; + + +/** + * Adds black to the specified color, darkening it + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @param {number} factor Number in the range [0, 1]. 0 will do nothing, while + * 1 will return black. If less than 0, factor will be set to 0. If greater + * than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined rgb color. + */ +goog.color.darken = function(rgb, factor) { + var black = [0, 0, 0]; + return goog.color.blend(black, rgb, factor); +}; + + +/** + * Adds white to the specified color, lightening it + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @param {number} factor Number in the range [0, 1]. 0 will do nothing, while + * 1 will return white. If less than 0, factor will be set to 0. If greater + * than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined rgb color. + */ +goog.color.lighten = function(rgb, factor) { + var white = [255, 255, 255]; + return goog.color.blend(white, rgb, factor); +}; + + +/** + * Find the "best" (highest-contrast) of the suggested colors for the prime + * color. Uses W3C formula for judging readability and visual accessibility: + * http://www.w3.org/TR/AERT#color-contrast + * @param {goog.color.Rgb} prime Color represented as a rgb array. + * @param {Array} suggestions Array of colors, + * each representing a rgb array. + * @return {!goog.color.Rgb} Highest-contrast color represented by an array.. + */ +goog.color.highContrast = function(prime, suggestions) { + var suggestionsWithDiff = []; + for (var i = 0; i < suggestions.length; i++) { + suggestionsWithDiff.push({ + color: suggestions[i], + diff: goog.color.yiqBrightnessDiff_(suggestions[i], prime) + + goog.color.colorDiff_(suggestions[i], prime) + }); + } + suggestionsWithDiff.sort(function(a, b) { + return b.diff - a.diff; + }); + return suggestionsWithDiff[0].color; +}; + + +/** + * Calculate brightness of a color according to YIQ formula (brightness is Y). + * More info on YIQ here: http://en.wikipedia.org/wiki/YIQ. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb Color represented by a rgb array. + * @return {number} brightness (Y). + * @private + */ +goog.color.yiqBrightness_ = function(rgb) { + return Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000); +}; + + +/** + * Calculate difference in brightness of two colors. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb1 Color represented by a rgb array. + * @param {goog.color.Rgb} rgb2 Color represented by a rgb array. + * @return {number} Brightness difference. + * @private + */ +goog.color.yiqBrightnessDiff_ = function(rgb1, rgb2) { + return Math.abs(goog.color.yiqBrightness_(rgb1) - + goog.color.yiqBrightness_(rgb2)); +}; + + +/** + * Calculate color difference between two colors. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb1 Color represented by a rgb array. + * @param {goog.color.Rgb} rgb2 Color represented by a rgb array. + * @return {number} Color difference. + * @private + */ +goog.color.colorDiff_ = function(rgb1, rgb2) { + return Math.abs(rgb1[0] - rgb2[0]) + Math.abs(rgb1[1] - rgb2[1]) + + Math.abs(rgb1[2] - rgb2[2]); +}; diff --git a/mapbox-gl/closure-library/closure/goog/color/color_test.html b/mapbox-gl/closure-library/closure/goog/color/color_test.html new file mode 100644 index 0000000000..e8c37088da --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/color/color_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.color + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/color/color_test.js b/mapbox-gl/closure-library/closure/goog/color/color_test.js new file mode 100644 index 0000000000..8da740ac34 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/color/color_test.js @@ -0,0 +1,667 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +goog.provide('goog.colorTest'); +goog.setTestOnly('goog.colorTest'); + +goog.require('goog.array'); +goog.require('goog.color'); +goog.require('goog.color.names'); +goog.require('goog.testing.jsunit'); + +function testIsValidColor() { + var goodColors = ['#ffffff', '#ff7812', '#012345', '#Ff003D', '#3CA', + '(255, 26, 75)', 'RGB(2, 3, 4)', '(0,0,0)', 'white', + 'blue']; + var badColors = ['#xxxxxx', '8899000', 'not_color', '#1234567', 'fffffg', + '(2555,0,0)', '(1,2,3,4)', 'rgb(1,20,)', 'RGB(20,20,20,)', + 'omgwtfbbq']; + for (var i = 0; i < goodColors.length; i++) { + assertTrue(goodColors[i], goog.color.isValidColor(goodColors[i])); + } + for (var i = 0; i < badColors.length; i++) { + assertFalse(badColors[i], goog.color.isValidColor(badColors[i])); + } +} + + +function testIsValidHexColor() { + var goodHexColors = ['#ffffff', '#ff7812', '#012345', '#Ff003D', '#3CA']; + var badHexColors = ['#xxxxxx', '889900', 'not_color', '#1234567', 'fffffg']; + for (var i = 0; i < goodHexColors.length; i++) { + assertTrue(goodHexColors[i], goog.color.isValidHexColor_(goodHexColors[i])); + } + for (var i = 0; i < badHexColors.length; i++) { + assertFalse(badHexColors[i], goog.color.isValidHexColor_(badHexColors[i])); + } +} + + +function testIsValidRgbColor() { + var goodRgbColors = ['(255, 26, 75)', 'RGB(2, 3, 4)', '(0,0,0)', + 'rgb(255,255,255)']; + var badRgbColors = ['(2555,0,0)', '(1,2,3,4)', 'rgb(1,20,)', + 'RGB(20,20,20,)']; + for (var i = 0; i < goodRgbColors.length; i++) { + assertEquals(goodRgbColors[i], + goog.color.isValidRgbColor_(goodRgbColors[i]).length, 3); + } + for (var i = 0; i < badRgbColors.length; i++) { + assertEquals(badRgbColors[i], + goog.color.isValidRgbColor_(badRgbColors[i]).length, 0); + } +} + + +function testParse() { + var colors = ['rgb(15, 250, 77)', '(127, 127, 127)', '#ffeedd', '123456', + 'magenta']; + var parsed = goog.array.map(colors, goog.color.parse); + assertEquals('rgb', parsed[0].type); + assertEquals(goog.color.rgbToHex(15, 250, 77), parsed[0].hex); + assertEquals('rgb', parsed[1].type); + assertEquals(goog.color.rgbToHex(127, 127, 127), parsed[1].hex); + assertEquals('hex', parsed[2].type); + assertEquals('#ffeedd', parsed[2].hex); + assertEquals('hex', parsed[3].type); + assertEquals('#123456', parsed[3].hex); + assertEquals('named', parsed[4].type); + assertEquals('#ff00ff', parsed[4].hex); + + var badColors = ['rgb(01, 1, 23)', '(256, 256, 256)', '#ffeeddaa']; + for (var i = 0; i < badColors.length; i++) { + var e = assertThrows(goog.partial(goog.color.parse, badColors[i])); + assertContains('is not a valid color string', e.message); + } +} + + +function testHexToRgb() { + var testColors = [['#B0FF2D', [176, 255, 45]], + ['#b26e5f', [178, 110, 95]], + ['#66f', [102, 102, 255]]]; + + for (var i = 0; i < testColors.length; i++) { + var r = goog.color.hexToRgb(testColors[i][0]); + var t = testColors[i][1]; + + assertEquals('Red channel should match.', t[0], r[0]); + assertEquals('Green channel should match.', t[1], r[1]); + assertEquals('Blue channel should match.', t[2], r[2]); + } + + var badColors = ['', '#g00', 'some words']; + for (var i = 0; i < badColors.length; i++) { + var e = assertThrows(goog.partial(goog.color.hexToRgb, badColors[i])); + assertEquals("'" + badColors[i] + "' is not a valid hex color", e.message); + } +} + + +function testHexToRgbStyle() { + assertEquals('rgb(255,0,0)', goog.color.hexToRgbStyle(goog.color.names.red)); + assertEquals('rgb(206,206,206)', goog.color.hexToRgbStyle('#cecece')); + assertEquals('rgb(51,204,170)', goog.color.hexToRgbStyle('#3CA')); + var badHexColors = ['#1234', null, undefined, '#.1234567890']; + for (var i = 0; i < badHexColors.length; ++i) { + var badHexColor = badHexColors[i]; + var e = assertThrows(goog.partial(goog.color.hexToRgbStyle, badHexColor)); + assertEquals("'" + badHexColor + "' is not a valid hex color", e.message); + } +} + + +function testRgbToHex() { + assertEquals(goog.color.names.red, goog.color.rgbToHex(255, 0, 0)); + assertEquals('#af13ff', goog.color.rgbToHex(175, 19, 255)); + var badRgb = [[-1, -1, -1], [256, 0, 0], ['a', 'b', 'c'], [undefined, 5, 5]]; + for (var i = 0; i < badRgb.length; ++i) { + var e = assertThrows(goog.partial(goog.color.rgbArrayToHex, badRgb[i])); + assertContains('is not a valid RGB color', e.message); + } +} + + +function testRgbToHsl() { + var rgb = [255, 171, 32]; + var hsl = goog.color.rgbArrayToHsl(rgb); + assertEquals(37, hsl[0]); + assertTrue(1.0 - hsl[1] < 0.01); + assertTrue(hsl[2] - .5625 < 0.01); +} + + +function testHslToRgb() { + var hsl = [60, 0.5, 0.1]; + var rgb = goog.color.hslArrayToRgb(hsl); + assertEquals(38, rgb[0]); + assertEquals(38, rgb[1]); + assertEquals(13, rgb[2]); +} + + +// Tests accuracy of HSL to RGB conversion +function testHSLBidiToRGB() { + var DELTA = 1; + + var color = [[100, 56, 200], + [255, 0, 0], + [0, 0, 255], + [0, 255, 0], + [255, 255, 255], + [0, 0, 0]]; + + for (var i = 0; i < color.length; i++) { + colorConversionTestHelper( + function(color) { + return goog.color.rgbToHsl(color[0], color[1], color[2]); + }, + function(color) { + return goog.color.hslToRgb(color[0], color[1], color[2]); + }, + color[i], DELTA); + + colorConversionTestHelper( + function(color) { return goog.color.rgbArrayToHsl(color); }, + function(color) { return goog.color.hslArrayToRgb(color); }, + color[i], DELTA); + } +} + + +// Tests HSV to RGB conversion +function testHSVToRGB() { + var DELTA = 1; + + var color = [[100, 56, 200], + [255, 0, 0], + [0, 0, 255], + [0, 255, 0], + [255, 255, 255], + [0, 0, 0]]; + + for (var i = 0; i < color.length; i++) { + colorConversionTestHelper( + function(color) { + return goog.color.rgbToHsv(color[0], color[1], color[2]); + }, + function(color) { + return goog.color.hsvToRgb(color[0], color[1], color[2]); + }, + color[i], DELTA); + + colorConversionTestHelper( + function(color) { return goog.color.rgbArrayToHsv(color); }, + function(color) { return goog.color.hsvArrayToRgb(color); }, + color[i], DELTA); + } +} + +// Tests that HSV space is (0-360) for hue +function testHSVSpecRangeIsCorrect() { + var color = [0, 0, 255]; // Blue is in the middle of hue range + + var hsv = goog.color.rgbToHsv(color[0], color[1], color[2]); + + assertTrue("H in HSV space looks like it's not 0-360", hsv[0] > 1); +} + +// Tests conversion between HSL and Hex +function testHslToHex() { + var DELTA = 1; + + var color = [[0, 0, 0], + [20, 0.5, 0.5], + [0, 0, 1], + [255, .45, .76]]; + + for (var i = 0; i < color.length; i++) { + colorConversionTestHelper( + function(hsl) { return goog.color.hslToHex(hsl[0], hsl[1], hsl[2]); }, + function(hex) { return goog.color.hexToHsl(hex); }, + color[i], DELTA); + + colorConversionTestHelper( + function(hsl) { return goog.color.hslArrayToHex(hsl); }, + function(hex) { return goog.color.hexToHsl(hex); }, + color[i], DELTA); + } +} + +// Tests conversion between HSV and Hex +function testHsvToHex() { + var DELTA = 1; + + var color = [[0, 0, 0], + [.5, 0.5, 155], + [0, 0, 255], + [.7, .45, 21]]; + + for (var i = 0; i < color.length; i++) { + colorConversionTestHelper( + function(hsl) { return goog.color.hsvToHex(hsl[0], hsl[1], hsl[2]); }, + function(hex) { return goog.color.hexToHsv(hex); }, + color[i], DELTA); + + colorConversionTestHelper( + function(hsl) { return goog.color.hsvArrayToHex(hsl); }, + function(hex) { return goog.color.hexToHsv(hex); }, + color[i], DELTA); + } +} + + +/** + * This helper method compares two RGB colors, checking that each color + * component is the same. + * @param {Array} rgb1 Color represented by a 3-element array with + * red, green, and blue values respectively, in the range [0, 255]. + * @param {Array} rgb2 Color represented by a 3-element array with + * red, green, and blue values respectively, in the range [0, 255]. + * @return {boolean} True if the colors are the same, false otherwise. + */ +function rgbColorsAreEqual(rgb1, rgb2) { + return (rgb1[0] == rgb2[0] && + rgb1[1] == rgb2[1] && + rgb1[2] == rgb2[2]); +} + + +/** + * This method runs unit tests against goog.color.blend(). Test cases include: + * blending arbitrary colors with factors of 0 and 1, blending the same colors + * using arbitrary factors, blending different colors of varying factors, + * and blending colors using factors outside the expected range. + */ +function testColorBlend() { + // Define some RGB colors for our tests. + var black = [0, 0, 0]; + var blue = [0, 0, 255]; + var gray = [128, 128, 128]; + var green = [0, 255, 0]; + var purple = [128, 0, 128]; + var red = [255, 0, 0]; + var yellow = [255, 255, 0]; + var white = [255, 255, 255]; + + // Blend arbitrary colors, using 0 and 1 for factors. Using 0 should return + // the first color, while using 1 should return the second color. + var redWithNoGreen = goog.color.blend(red, green, 1); + assertTrue('red + 0 * green = red', + rgbColorsAreEqual(red, redWithNoGreen)); + var whiteWithAllBlue = goog.color.blend(white, blue, 0); + assertTrue('white + 1 * blue = blue', + rgbColorsAreEqual(blue, whiteWithAllBlue)); + + // Blend the same colors using arbitrary factors. This should return the + // same colors. + var greenWithGreen = goog.color.blend(green, green, .25); + assertTrue('green + .25 * green = green', + rgbColorsAreEqual(green, greenWithGreen)); + + // Blend different colors using varying factors. + var blackWithWhite = goog.color.blend(black, white, .5); + assertTrue('black + .5 * white = gray', + rgbColorsAreEqual(gray, blackWithWhite)); + var redAndBlue = goog.color.blend(red, blue, .5); + assertTrue('red + .5 * blue = purple', + rgbColorsAreEqual(purple, redAndBlue)); + var lightGreen = goog.color.blend(green, white, .75); + assertTrue('green + .25 * white = a lighter shade of white', + lightGreen[0] > 0 && + lightGreen[1] == 255 && + lightGreen[2] > 0); + + // Blend arbitrary colors using factors outside the expected range. + var noGreenAllPurple = goog.color.blend(green, purple, -0.5); + assertTrue('green * -0.5 + purple = purple.', + rgbColorsAreEqual(purple, noGreenAllPurple)); + var allBlueNoYellow = goog.color.blend(blue, yellow, 1.37); + assertTrue('blue * 1.37 + yellow = blue.', + rgbColorsAreEqual(blue, allBlueNoYellow)); +} + + +/** + * This method runs unit tests against goog.color.darken(). Test cases + * include darkening black with arbitrary factors, edge cases (using 0 and 1), + * darkening colors using various factors, and darkening colors using factors + * outside the expected range. + */ +function testColorDarken() { + // Define some RGB colors + var black = [0, 0, 0]; + var green = [0, 255, 0]; + var darkGray = [68, 68, 68]; + var olive = [128, 128, 0]; + var purple = [128, 0, 128]; + var white = [255, 255, 255]; + + // Darken black by an arbitrary factor, which should still return black. + var darkBlack = goog.color.darken(black, .63); + assertTrue('black darkened by .63 is still black.', + rgbColorsAreEqual(black, darkBlack)); + + // Call darken() with edge-case factors (0 and 1). + var greenNotDarkened = goog.color.darken(green, 0); + assertTrue('green darkened by 0 is still green.', + rgbColorsAreEqual(green, greenNotDarkened)); + var whiteFullyDarkened = goog.color.darken(white, 1); + assertTrue('white darkened by 1 is black.', + rgbColorsAreEqual(black, whiteFullyDarkened)); + + // Call darken() with various colors and factors. The result should be + // a color with less luminance. + var whiteHsl = goog.color.rgbToHsl(white[0], + white[1], + white[2]); + var whiteDarkened = goog.color.darken(white, .43); + var whiteDarkenedHsl = goog.color.rgbToHsl(whiteDarkened[0], + whiteDarkened[1], + whiteDarkened[2]); + assertTrue('White that\'s darkened has less luminance than white.', + whiteDarkenedHsl[2] < whiteHsl[2]); + var purpleHsl = goog.color.rgbToHsl(purple[0], + purple[1], + purple[2]); + var purpleDarkened = goog.color.darken(purple, .1); + var purpleDarkenedHsl = goog.color.rgbToHsl(purpleDarkened[0], + purpleDarkened[1], + purpleDarkened[2]); + assertTrue('Purple that\'s darkened has less luminance than purple.', + purpleDarkenedHsl[2] < purpleHsl[2]); + + // Call darken() with factors outside the expected range. + var darkGrayTurnedBlack = goog.color.darken(darkGray, 2.1); + assertTrue('Darkening dark gray by 2.1 returns black.', + rgbColorsAreEqual(black, darkGrayTurnedBlack)); + var whiteNotDarkened = goog.color.darken(white, -0.62); + assertTrue('Darkening white by -0.62 returns white.', + rgbColorsAreEqual(white, whiteNotDarkened)); +} + + +/** + * This method runs unit tests against goog.color.lighten(). Test cases + * include lightening white with arbitrary factors, edge cases (using 0 and 1), + * lightening colors using various factors, and lightening colors using factors + * outside the expected range. + */ +function testColorLighten() { + // Define some RGB colors + var black = [0, 0, 0]; + var brown = [165, 42, 42]; + var navy = [0, 0, 128]; + var orange = [255, 165, 0]; + var white = [255, 255, 255]; + + // Lighten white by an arbitrary factor, which should still return white. + var lightWhite = goog.color.lighten(white, .41); + assertTrue('white lightened by .41 is still white.', + rgbColorsAreEqual(white, lightWhite)); + + // Call lighten() with edge-case factors(0 and 1). + var navyNotLightened = goog.color.lighten(navy, 0); + assertTrue('navy lightened by 0 is still navy.', + rgbColorsAreEqual(navy, navyNotLightened)); + var orangeFullyLightened = goog.color.lighten(orange, 1); + assertTrue('orange lightened by 1 is white.', + rgbColorsAreEqual(white, orangeFullyLightened)); + + // Call lighten() with various colors and factors. The result should be + // a color with greater luminance. + var blackHsl = goog.color.rgbToHsl(black[0], + black[1], + black[2]); + var blackLightened = goog.color.lighten(black, .33); + var blackLightenedHsl = goog.color.rgbToHsl(blackLightened[0], + blackLightened[1], + blackLightened[2]); + assertTrue('Black that\'s lightened has more luminance than black.', + blackLightenedHsl[2] >= blackHsl[2]); + var orangeHsl = goog.color.rgbToHsl(orange[0], + orange[1], + orange[2]); + var orangeLightened = goog.color.lighten(orange, .91); + var orangeLightenedHsl = goog.color.rgbToHsl(orangeLightened[0], + orangeLightened[1], + orangeLightened[2]); + assertTrue('Orange that\'s lightened has more luminance than orange.', + orangeLightenedHsl[2] >= orangeHsl[2]); + + // Call lighten() with factors outside the expected range. + var navyTurnedWhite = goog.color.lighten(navy, 1.01); + assertTrue('Lightening navy by 1.01 returns white.', + rgbColorsAreEqual(white, navyTurnedWhite)); + var brownNotLightened = goog.color.lighten(brown, -0.0000001); + assertTrue('Lightening brown by -0.0000001 returns brown.', + rgbColorsAreEqual(brown, brownNotLightened)); +} + + +/** + * This method runs unit tests against goog.color.hslDistance(). + */ +function testHslDistance() { + // Define some HSL colors + var aliceBlueHsl = goog.color.rgbToHsl(240, 248, 255); + var blackHsl = goog.color.rgbToHsl(0, 0, 0); + var ghostWhiteHsl = goog.color.rgbToHsl(248, 248, 255); + var navyHsl = goog.color.rgbToHsl(0, 0, 128); + var redHsl = goog.color.rgbToHsl(255, 0, 0); + var whiteHsl = goog.color.rgbToHsl(255, 255, 255); + + // The distance between the same colors should be 0. + assertTrue('There is no HSL distance between white and white.', + goog.color.hslDistance(whiteHsl, whiteHsl) == 0); + assertTrue('There is no HSL distance between red and red.', + goog.color.hslDistance(redHsl, redHsl) == 0); + + // The distance between various colors should be within certain thresholds. + var hslDistance = goog.color.hslDistance(whiteHsl, ghostWhiteHsl); + assertTrue('The HSL distance between white and ghost white is > 0.', + hslDistance > 0); + assertTrue('The HSL distance between white and ghost white is <= 0.02.', + hslDistance <= 0.02); + hslDistance = goog.color.hslDistance(whiteHsl, redHsl); + assertTrue('The HSL distance betwen white and red is > 0.02.', + hslDistance > 0.02); + hslDistance = goog.color.hslDistance(navyHsl, aliceBlueHsl); + assertTrue('The HSL distance between navy and alice blue is > 0.02.', + hslDistance > 0.02); + hslDistance = goog.color.hslDistance(blackHsl, whiteHsl); + assertTrue('The HSL distance between white and black is 1.', + hslDistance == 1); +} + + +/** + * This method runs unit tests against goog.color.yiqBrightness_(). + */ +function testYiqBrightness() { + var white = [255, 255, 255]; + var black = [0, 0, 0]; + var coral = [255, 127, 80]; + var lightgreen = [144, 238, 144]; + + var whiteBrightness = goog.color.yiqBrightness_(white); + var blackBrightness = goog.color.yiqBrightness_(black); + var coralBrightness = goog.color.yiqBrightness_(coral); + var lightgreenBrightness = goog.color.yiqBrightness_(lightgreen); + + // brightness should be a number + assertTrue('White brightness is a number.', + typeof whiteBrightness == 'number'); + assertTrue('Coral brightness is a number.', + typeof coralBrightness == 'number'); + + // brightness for known colors should match known values + assertEquals('White brightness is 255', whiteBrightness, 255); + assertEquals('Black brightness is 0', blackBrightness, 0); + assertEquals('Coral brightness is 160', coralBrightness, 160); + assertEquals('Lightgreen brightness is 199', lightgreenBrightness, 199); +} + + +/** + * This method runs unit tests against goog.color.yiqBrightnessDiff_(). + */ +function testYiqBrightnessDiff() { + var colors = { + 'deeppink': [255, 20, 147], + 'indigo': [75, 0, 130], + 'saddlebrown': [139, 69, 19] + }; + + var diffs = new Object(); + for (name1 in colors) { + for (name2 in colors) { + diffs[name1 + '-' + name2] = + goog.color.yiqBrightnessDiff_(colors[name1], colors[name2]); + } + } + + for (pair in diffs) { + // each brightness diff should be a number + assertTrue(pair + ' diff is a number.', typeof diffs[pair] == 'number'); + // each brightness diff should be greater than or equal to 0 + assertTrue(pair + ' diff is greater than or equal to 0.', diffs[pair] >= 0); + } + + // brightness diff for same-color pairs should be 0 + assertEquals('deeppink-deeppink is 0.', diffs['deeppink-deeppink'], 0); + assertEquals('indigo-indigo is 0.', diffs['indigo-indigo'], 0); + + // brightness diff for known pairs should match known values + assertEquals('deeppink-indigo is 68.', diffs['deeppink-indigo'], 68); + assertEquals('saddlebrown-deeppink is 21.', + diffs['saddlebrown-deeppink'], 21); + + // reversed pairs should have equal values + assertEquals('indigo-saddlebrown is 47.', diffs['indigo-saddlebrown'], 47); + assertEquals('saddlebrown-indigo is also 47.', + diffs['saddlebrown-indigo'], 47); +} + + +/** + * This method runs unit tests against goog.color.colorDiff_(). + */ +function testColorDiff() { + var colors = { + 'mediumblue': [0, 0, 205], + 'oldlace': [253, 245, 230], + 'orchid': [218, 112, 214] + }; + + var diffs = new Object(); + for (name1 in colors) { + for (name2 in colors) { + diffs[name1 + '-' + name2] = + goog.color.colorDiff_(colors[name1], colors[name2]); + } + } + + for (pair in diffs) { + // each color diff should be a number + assertTrue(pair + ' diff is a number.', typeof diffs[pair] == 'number'); + // each color diff should be greater than or equal to 0 + assertTrue(pair + ' diff is greater than or equal to 0.', diffs[pair] >= 0); + } + + // color diff for same-color pairs should be 0 + assertEquals('mediumblue-mediumblue is 0.', + diffs['mediumblue-mediumblue'], 0); + assertEquals('oldlace-oldlace is 0.', diffs['oldlace-oldlace'], 0); + + // color diff for known pairs should match known values + assertEquals('mediumblue-oldlace is 523.', diffs['mediumblue-oldlace'], 523); + assertEquals('oldlace-orchid is 184.', diffs['oldlace-orchid'], 184); + + // reversed pairs should have equal values + assertEquals('orchid-mediumblue is 339.', diffs['orchid-mediumblue'], 339); + assertEquals('mediumblue-orchid is also 339.', + diffs['mediumblue-orchid'], 339); +} + + +/** + * This method runs unit tests against goog.color.highContrast(). + */ +function testHighContrast() { + white = [255, 255, 255]; + black = [0, 0, 0]; + lemonchiffron = [255, 250, 205]; + sienna = [160, 82, 45]; + + var suggestion = goog.color.highContrast( + black, [white, black, sienna, lemonchiffron]); + + // should return an array of three numbers + assertTrue('Return value is an array.', typeof suggestion == 'object'); + assertTrue('Return value is 3 long.', suggestion.length == 3); + + // known color combos should return a known (i.e. human-verified) suggestion + assertArrayEquals('White is best on sienna.', + goog.color.highContrast( + sienna, [white, black, sienna, lemonchiffron]), white); + assertArrayEquals('Black is best on lemonchiffron.', + goog.color.highContrast( + white, [white, black, sienna, lemonchiffron]), black); +} + + +/** + * Helper function for color conversion functions between two colorspaces. + * @param {Function} funcOne Function that converts from 1st colorspace to 2nd + * @param {Function} funcTwo Function that converts from 2nd colorspace to 2nd + * @param {Array} color The color array passed to funcOne + * @param {number} DELTA Margin of error for each element in color + */ +function colorConversionTestHelper(funcOne, funcTwo, color, DELTA) { + + var temp = funcOne(color); + + if (!goog.color.isValidHexColor_(temp)) { + assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[0])); + assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[1])); + assertTrue('First conversion had a NaN: ' + temp, !isNaN(temp[2])); + } + + var back = funcTwo(temp); + + if (!goog.color.isValidHexColor_(temp)) { + assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[0])); + assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[1])); + assertTrue('Second conversion had a NaN: ' + back, !isNaN(back[2])); + } + + assertColorFuzzyEquals('Color was off', color, back, DELTA); +} + + +/** + * Checks equivalence between two colors' respective values. Accepts +- delta + * for each pair of values + * @param {string} Str + * @param {Array} expected + * @param {Array} actual + * @param {number} delta Margin of error for each element in color array + */ +function assertColorFuzzyEquals(str, expected, actual, delta) { + assertTrue(str + ' Expected: ' + expected + ' and got: ' + actual + + ' w/ delta: ' + delta, + (Math.abs(expected[0] - actual[0]) <= delta) && + (Math.abs(expected[1] - actual[1]) <= delta) && + (Math.abs(expected[2] - actual[2]) <= delta)); +} diff --git a/mapbox-gl/closure-library/closure/goog/color/names.js b/mapbox-gl/closure-library/closure/goog/color/names.js new file mode 100644 index 0000000000..c4b3ac8d46 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/color/names.js @@ -0,0 +1,176 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Names of standard colors with their associated hex values. + */ + +goog.provide('goog.color.names'); + + +/** + * A map that contains a lot of colors that are recognised by various browsers. + * This list is way larger than the minimal one dictated by W3C. + * The keys of this map are the lowercase "readable" names of the colors, while + * the values are the "hex" values. + */ +goog.color.names = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgreen': '#006400', + 'darkgrey': '#a9a9a9', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'grey': '#808080', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred': '#cd5c5c', + 'indigo': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgray': '#d3d3d3', + 'lightgreen': '#90ee90', + 'lightgrey': '#d3d3d3', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370db', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#db7093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32' +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/aes.js b/mapbox-gl/closure-library/closure/goog/crypt/aes.js new file mode 100644 index 0000000000..050e644c31 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/aes.js @@ -0,0 +1,1030 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of AES in JavaScript. + * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard + * + * @author nnaze@google.com (Nathan Naze) - port to Closure + */ + +goog.provide('goog.crypt.Aes'); + +goog.require('goog.asserts'); +goog.require('goog.crypt.BlockCipher'); + + + +/** + * Implementation of AES in JavaScript. + * See http://en.wikipedia.org/wiki/Advanced_Encryption_Standard + * + * WARNING: This is ECB mode only. If you are encrypting something + * longer than 16 bytes, or encrypting more than one value with the same key + * (so basically, always) you need to use this with a block cipher mode of + * operation. See goog.crypt.Cbc. + * + * See http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation for more + * information. + * + * @constructor + * @implements {goog.crypt.BlockCipher} + * @param {!Array} key The key as an array of integers in {0, 255}. + * The key must have lengths of 16, 24, or 32 integers for 128-, + * 192-, or 256-bit encryption, respectively. + * @final + * @struct + */ +goog.crypt.Aes = function(key) { + goog.crypt.Aes.assertKeyArray_(key); + + /** + * The AES key. + * @type {!Array} + * @private + */ + this.key_ = key; + + /** + * Key length, in words. + * @type {number} + * @private + */ + this.keyLength_ = this.key_.length / 4; + + /** + * Number of rounds. Based on key length per AES spec. + * @type {number} + * @private + */ + this.numberOfRounds_ = this.keyLength_ + 6; + + /** + * 4x4 byte array containing the current state. + * @type {!Array>} + * @private + */ + this.state_ = [[], [], [], []]; + + /** + * Scratch temporary array for calculation. + * @type {!Array>} + * @private + */ + this.temp_ = [[], [], [], []]; + + this.keyExpansion_(); +}; + + +/** + * @define {boolean} Whether to call test method stubs. This can be enabled + * for unit testing. + */ +goog.define('goog.crypt.Aes.ENABLE_TEST_MODE', false); + + +/** + * @override + */ +goog.crypt.Aes.prototype.encrypt = function(input) { + + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testKeySchedule_(0, this.keySchedule_, 0); + } + + this.copyInput_(input); + this.addRoundKey_(0); + + for (var round = 1; round < this.numberOfRounds_; ++round) { + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testKeySchedule_(round, this.keySchedule_, round); + this.testStartRound_(round, this.state_); + } + + this.subBytes_(goog.crypt.Aes.SBOX_); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterSubBytes_(round, this.state_); + } + + this.shiftRows_(); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterShiftRows_(round, this.state_); + } + + this.mixColumns_(); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterMixColumns_(round, this.state_); + } + + this.addRoundKey_(round); + } + + this.subBytes_(goog.crypt.Aes.SBOX_); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterSubBytes_(round, this.state_); + } + + this.shiftRows_(); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterShiftRows_(round, this.state_); + } + + this.addRoundKey_(this.numberOfRounds_); + + return this.generateOutput_(); +}; + + +/** + * @override + */ +goog.crypt.Aes.prototype.decrypt = function(input) { + + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testKeySchedule_(0, this.keySchedule_, this.numberOfRounds_); + } + + this.copyInput_(input); + this.addRoundKey_(this.numberOfRounds_); + + for (var round = 1; round < this.numberOfRounds_; ++round) { + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testKeySchedule_(round, this.keySchedule_, + this.numberOfRounds_ - round); + this.testStartRound_(round, this.state_); + } + + this.invShiftRows_(); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterShiftRows_(round, this.state_); + } + + this.subBytes_(goog.crypt.Aes.INV_SBOX_); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterSubBytes_(round, this.state_); + } + + this.addRoundKey_(this.numberOfRounds_ - round); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterAddRoundKey_(round, this.state_); + } + + this.invMixColumns_(); + } + + this.invShiftRows_(); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterShiftRows_(round, this.state_); + } + + this.subBytes_(goog.crypt.Aes.INV_SBOX_); + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testAfterSubBytes_(this.numberOfRounds_, this.state_); + } + + if (goog.crypt.Aes.ENABLE_TEST_MODE) { + this.testKeySchedule_(this.numberOfRounds_, this.keySchedule_, 0); + } + + this.addRoundKey_(0); + + return this.generateOutput_(); +}; + + +/** + * Block size, in words. Fixed at 4 per AES spec. + * @type {number} + * @private + */ +goog.crypt.Aes.BLOCK_SIZE_ = 4; + + +/** + * Asserts that the key's array of integers is in the correct format. + * @param {!Array} arr AES key as array of integers. + * @private + */ +goog.crypt.Aes.assertKeyArray_ = function(arr) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.assert(arr.length == 16 || arr.length == 24 || + arr.length == 32, + 'Key must have length 16, 24, or 32.'); + for (var i = 0; i < arr.length; i++) { + goog.asserts.assertNumber(arr[i]); + goog.asserts.assert(arr[i] >= 0 && arr[i] <= 255); + } + } +}; + + +/** + * Tests can populate this with a callback, and that callback will get called + * at the start of each round *in both functions encrypt() and decrypt()*. + * @param {number} roundNum Round number. + * @param {!Array>} Current state. + * @private + */ +goog.crypt.Aes.prototype.testStartRound_ = goog.nullFunction; + + +/** + * Tests can populate this with a callback, and that callback will get called + * each round right after the SubBytes step gets executed *in both functions + * encrypt() and decrypt()*. + * @param {number} roundNum Round number. + * @param {!Array>} Current state. + * @private + */ +goog.crypt.Aes.prototype.testAfterSubBytes_ = goog.nullFunction; + + +/** + * Tests can populate this with a callback, and that callback will get called + * each round right after the ShiftRows step gets executed *in both functions + * encrypt() and decrypt()*. + * @param {number} roundNum Round number. + * @param {!Array>} Current state. + * @private + */ +goog.crypt.Aes.prototype.testAfterShiftRows_ = goog.nullFunction; + + +/** + * Tests can populate this with a callback, and that callback will get called + * each round right after the MixColumns step gets executed *but only in the + * decrypt() function*. + * @param {number} roundNum Round number. + * @param {!Array>} Current state. + * @private + */ +goog.crypt.Aes.prototype.testAfterMixColumns_ = goog.nullFunction; + + +/** + * Tests can populate this with a callback, and that callback will get called + * each round right after the AddRoundKey step gets executed encrypt(). + * @param {number} roundNum Round number. + * @param {!Array>} Current state. + * @private + */ +goog.crypt.Aes.prototype.testAfterAddRoundKey_ = goog.nullFunction; + + +/** + * Tests can populate this with a callback, and that callback will get called + * before each round on the round key. *Gets called in both the encrypt() and + * decrypt() functions.* + * @param {number} roundNum Round number. + * @param {!Array} Computed key schedule. + * @param {number} index The index into the key schedule to test. This is not + * necessarily roundNum because the key schedule is used in reverse + * in the case of decryption. + * @private + */ +goog.crypt.Aes.prototype.testKeySchedule_ = goog.nullFunction; + + +/** + * Helper to copy input into the AES state matrix. + * @param {!Array} input Byte array to copy into the state matrix. + * @private + */ +goog.crypt.Aes.prototype.copyInput_ = function(input) { + var v, p; + + goog.asserts.assert(input.length == goog.crypt.Aes.BLOCK_SIZE_ * 4, + 'Expecting input of 4 times block size.'); + + for (var r = 0; r < goog.crypt.Aes.BLOCK_SIZE_; r++) { + for (var c = 0; c < 4; c++) { + p = c * 4 + r; + v = input[p]; + + goog.asserts.assert( + v <= 255 && v >= 0, + 'Invalid input. Value %s at position %s is not a byte.', v, p); + + this.state_[r][c] = v; + } + } +}; + + +/** + * Helper to copy the state matrix into an output array. + * @return {!Array} Output byte array. + * @private + */ +goog.crypt.Aes.prototype.generateOutput_ = function() { + var output = []; + for (var r = 0; r < goog.crypt.Aes.BLOCK_SIZE_; r++) { + for (var c = 0; c < 4; c++) { + output[c * 4 + r] = this.state_[r][c]; + } + } + return output; +}; + + +/** + * AES's AddRoundKey procedure. Add the current round key to the state. + * @param {number} round The current round. + * @private + */ +goog.crypt.Aes.prototype.addRoundKey_ = function(round) { + for (var r = 0; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.state_[r][c] ^= this.keySchedule_[round * 4 + c][r]; + } + } +}; + + +/** + * AES's SubBytes procedure. Substitute bytes from the precomputed SBox lookup + * into the state. + * @param {!Array} box The SBox or invSBox. + * @private + */ +goog.crypt.Aes.prototype.subBytes_ = function(box) { + for (var r = 0; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.state_[r][c] = box[this.state_[r][c]]; + } + } +}; + + +/** + * AES's ShiftRows procedure. Shift the values in each row to the right. Each + * row is shifted one more slot than the one above it. + * @private + */ +goog.crypt.Aes.prototype.shiftRows_ = function() { + for (var r = 1; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.temp_[r][c] = this.state_[r][c]; + } + } + + for (var r = 1; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.state_[r][c] = this.temp_[r][(c + r) % + goog.crypt.Aes.BLOCK_SIZE_]; + } + } +}; + + +/** + * AES's InvShiftRows procedure. Shift the values in each row to the right. + * @private + */ +goog.crypt.Aes.prototype.invShiftRows_ = function() { + for (var r = 1; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.temp_[r][(c + r) % goog.crypt.Aes.BLOCK_SIZE_] = + this.state_[r][c]; + } + } + + for (var r = 1; r < 4; r++) { + for (var c = 0; c < 4; c++) { + this.state_[r][c] = this.temp_[r][c]; + } + } +}; + + +/** + * AES's MixColumns procedure. Mix the columns of the state using magic. + * @private + */ +goog.crypt.Aes.prototype.mixColumns_ = function() { + var s = this.state_; + var t = this.temp_[0]; + + for (var c = 0; c < 4; c++) { + t[0] = s[0][c]; + t[1] = s[1][c]; + t[2] = s[2][c]; + t[3] = s[3][c]; + + s[0][c] = (goog.crypt.Aes.MULT_2_[t[0]] ^ + goog.crypt.Aes.MULT_3_[t[1]] ^ t[2] ^ t[3]); + s[1][c] = (t[0] ^ goog.crypt.Aes.MULT_2_[t[1]] ^ + goog.crypt.Aes.MULT_3_[t[2]] ^ t[3]); + s[2][c] = (t[0] ^ t[1] ^ goog.crypt.Aes.MULT_2_[t[2]] ^ + goog.crypt.Aes.MULT_3_[t[3]]); + s[3][c] = (goog.crypt.Aes.MULT_3_[t[0]] ^ t[1] ^ t[2] ^ + goog.crypt.Aes.MULT_2_[t[3]]); + } +}; + + +/** + * AES's InvMixColumns procedure. + * @private + */ +goog.crypt.Aes.prototype.invMixColumns_ = function() { + var s = this.state_; + var t = this.temp_[0]; + + for (var c = 0; c < 4; c++) { + t[0] = s[0][c]; + t[1] = s[1][c]; + t[2] = s[2][c]; + t[3] = s[3][c]; + + s[0][c] = ( + goog.crypt.Aes.MULT_E_[t[0]] ^ goog.crypt.Aes.MULT_B_[t[1]] ^ + goog.crypt.Aes.MULT_D_[t[2]] ^ goog.crypt.Aes.MULT_9_[t[3]]); + + s[1][c] = ( + goog.crypt.Aes.MULT_9_[t[0]] ^ goog.crypt.Aes.MULT_E_[t[1]] ^ + goog.crypt.Aes.MULT_B_[t[2]] ^ goog.crypt.Aes.MULT_D_[t[3]]); + + s[2][c] = ( + goog.crypt.Aes.MULT_D_[t[0]] ^ goog.crypt.Aes.MULT_9_[t[1]] ^ + goog.crypt.Aes.MULT_E_[t[2]] ^ goog.crypt.Aes.MULT_B_[t[3]]); + + s[3][c] = ( + goog.crypt.Aes.MULT_B_[t[0]] ^ goog.crypt.Aes.MULT_D_[t[1]] ^ + goog.crypt.Aes.MULT_9_[t[2]] ^ goog.crypt.Aes.MULT_E_[t[3]]); + } +}; + + +/** + * AES's KeyExpansion procedure. Create the key schedule from the initial key. + * @private + */ +goog.crypt.Aes.prototype.keyExpansion_ = function() { + this.keySchedule_ = new Array(goog.crypt.Aes.BLOCK_SIZE_ * ( + this.numberOfRounds_ + 1)); + + for (var rowNum = 0; rowNum < this.keyLength_; rowNum++) { + this.keySchedule_[rowNum] = [ + this.key_[4 * rowNum], + this.key_[4 * rowNum + 1], + this.key_[4 * rowNum + 2], + this.key_[4 * rowNum + 3] + ]; + } + + var temp = new Array(4); + + for (var rowNum = this.keyLength_; + rowNum < (goog.crypt.Aes.BLOCK_SIZE_ * (this.numberOfRounds_ + 1)); + rowNum++) { + temp[0] = this.keySchedule_[rowNum - 1][0]; + temp[1] = this.keySchedule_[rowNum - 1][1]; + temp[2] = this.keySchedule_[rowNum - 1][2]; + temp[3] = this.keySchedule_[rowNum - 1][3]; + + if (rowNum % this.keyLength_ == 0) { + this.rotWord_(temp); + this.subWord_(temp); + + temp[0] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][0]; + temp[1] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][1]; + temp[2] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][2]; + temp[3] ^= goog.crypt.Aes.RCON_[rowNum / this.keyLength_][3]; + } else if (this.keyLength_ > 6 && rowNum % this.keyLength_ == 4) { + this.subWord_(temp); + } + + this.keySchedule_[rowNum] = new Array(4); + this.keySchedule_[rowNum][0] = + this.keySchedule_[rowNum - this.keyLength_][0] ^ temp[0]; + this.keySchedule_[rowNum][1] = + this.keySchedule_[rowNum - this.keyLength_][1] ^ temp[1]; + this.keySchedule_[rowNum][2] = + this.keySchedule_[rowNum - this.keyLength_][2] ^ temp[2]; + this.keySchedule_[rowNum][3] = + this.keySchedule_[rowNum - this.keyLength_][3] ^ temp[3]; + } +}; + + +/** + * AES's SubWord procedure. + * @param {!Array} w Bytes to find the SBox substitution for. + * @return {!Array} The substituted bytes. + * @private + */ +goog.crypt.Aes.prototype.subWord_ = function(w) { + w[0] = goog.crypt.Aes.SBOX_[w[0]]; + w[1] = goog.crypt.Aes.SBOX_[w[1]]; + w[2] = goog.crypt.Aes.SBOX_[w[2]]; + w[3] = goog.crypt.Aes.SBOX_[w[3]]; + + return w; +}; + + +/** + * AES's RotWord procedure. + * @param {!Array} w Array of bytes to rotate. + * @return {!Array} The rotated bytes. + * @private + */ +goog.crypt.Aes.prototype.rotWord_ = function(w) { + var temp = w[0]; + + w[0] = w[1]; + w[1] = w[2]; + w[2] = w[3]; + w[3] = temp; + + return w; +}; + + +/** + * The key schedule. + * @type {!Array} + * @private + */ +goog.crypt.Aes.prototype.keySchedule_; + + +/** + * Precomputed SBox lookup. + * @type {!Array} + * @private + */ +goog.crypt.Aes.SBOX_ = [ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, + + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, + 0xa4, 0x72, 0xc0, + + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, + 0xd8, 0x31, 0x15, + + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, + 0x27, 0xb2, 0x75, + + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, + 0xe3, 0x2f, 0x84, + + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, + 0x4c, 0x58, 0xcf, + + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, + 0x3c, 0x9f, 0xa8, + + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, + 0xff, 0xf3, 0xd2, + + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, + 0x5d, 0x19, 0x73, + + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, + 0x5e, 0x0b, 0xdb, + + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, + 0x95, 0xe4, 0x79, + + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, + 0x7a, 0xae, 0x08, + + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, + 0xbd, 0x8b, 0x8a, + + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, + + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, + 0x55, 0x28, 0xdf, + + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, + 0x54, 0xbb, 0x16 +]; + + +/** + * Precomputed InvSBox lookup. + * @type {!Array} + * @private + */ +goog.crypt.Aes.INV_SBOX_ = [ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, + 0xf3, 0xd7, 0xfb, + + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, + 0xde, 0xe9, 0xcb, + + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, + 0xfa, 0xc3, 0x4e, + + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, + 0x8b, 0xd1, 0x25, + + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, + 0x65, 0xb6, 0x92, + + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, + 0x8d, 0x9d, 0x84, + + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, + 0xb3, 0x45, 0x06, + + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, + 0x13, 0x8a, 0x6b, + + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, + 0xb4, 0xe6, 0x73, + + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, + 0x75, 0xdf, 0x6e, + + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, + 0x18, 0xbe, 0x1b, + + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, + 0xcd, 0x5a, 0xf4, + + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, + 0x80, 0xec, 0x5f, + + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, + 0xc9, 0x9c, 0xef, + + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, + 0x53, 0x99, 0x61, + + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, + 0x21, 0x0c, 0x7d +]; + + +/** + * Precomputed RCon lookup. + * @type {!Array} + * @private + */ +goog.crypt.Aes.RCON_ = [ + [0x00, 0x00, 0x00, 0x00], + [0x01, 0x00, 0x00, 0x00], + [0x02, 0x00, 0x00, 0x00], + [0x04, 0x00, 0x00, 0x00], + [0x08, 0x00, 0x00, 0x00], + [0x10, 0x00, 0x00, 0x00], + [0x20, 0x00, 0x00, 0x00], + [0x40, 0x00, 0x00, 0x00], + [0x80, 0x00, 0x00, 0x00], + [0x1b, 0x00, 0x00, 0x00], + [0x36, 0x00, 0x00, 0x00] +]; + + +/** + * Precomputed lookup of multiplication by 2 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_2_ = [ + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1A, 0x1C, 0x1E, + + 0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, + 0x38, 0x3A, 0x3C, 0x3E, + + 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, + 0x58, 0x5A, 0x5C, 0x5E, + + 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76, + 0x78, 0x7A, 0x7C, 0x7E, + + 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, + 0x98, 0x9A, 0x9C, 0x9E, + + 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, + 0xB8, 0xBA, 0xBC, 0xBE, + + 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, + 0xD8, 0xDA, 0xDC, 0xDE, + + 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, + 0xF8, 0xFA, 0xFC, 0xFE, + + 0x1B, 0x19, 0x1F, 0x1D, 0x13, 0x11, 0x17, 0x15, 0x0B, 0x09, 0x0F, 0x0D, + 0x03, 0x01, 0x07, 0x05, + + 0x3B, 0x39, 0x3F, 0x3D, 0x33, 0x31, 0x37, 0x35, 0x2B, 0x29, 0x2F, 0x2D, + 0x23, 0x21, 0x27, 0x25, + + 0x5B, 0x59, 0x5F, 0x5D, 0x53, 0x51, 0x57, 0x55, 0x4B, 0x49, 0x4F, 0x4D, + 0x43, 0x41, 0x47, 0x45, + + 0x7B, 0x79, 0x7F, 0x7D, 0x73, 0x71, 0x77, 0x75, 0x6B, 0x69, 0x6F, 0x6D, + 0x63, 0x61, 0x67, 0x65, + + 0x9B, 0x99, 0x9F, 0x9D, 0x93, 0x91, 0x97, 0x95, 0x8B, 0x89, 0x8F, 0x8D, + 0x83, 0x81, 0x87, 0x85, + + 0xBB, 0xB9, 0xBF, 0xBD, 0xB3, 0xB1, 0xB7, 0xB5, 0xAB, 0xA9, 0xAF, 0xAD, + 0xA3, 0xA1, 0xA7, 0xA5, + + 0xDB, 0xD9, 0xDF, 0xDD, 0xD3, 0xD1, 0xD7, 0xD5, 0xCB, 0xC9, 0xCF, 0xCD, + 0xC3, 0xC1, 0xC7, 0xC5, + + 0xFB, 0xF9, 0xFF, 0xFD, 0xF3, 0xF1, 0xF7, 0xF5, 0xEB, 0xE9, 0xEF, 0xED, + 0xE3, 0xE1, 0xE7, 0xE5 +]; + + +/** + * Precomputed lookup of multiplication by 3 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_3_ = [ + 0x00, 0x03, 0x06, 0x05, 0x0C, 0x0F, 0x0A, 0x09, 0x18, 0x1B, 0x1E, 0x1D, + 0x14, 0x17, 0x12, 0x11, + + 0x30, 0x33, 0x36, 0x35, 0x3C, 0x3F, 0x3A, 0x39, 0x28, 0x2B, 0x2E, 0x2D, + 0x24, 0x27, 0x22, 0x21, + + 0x60, 0x63, 0x66, 0x65, 0x6C, 0x6F, 0x6A, 0x69, 0x78, 0x7B, 0x7E, 0x7D, + 0x74, 0x77, 0x72, 0x71, + + 0x50, 0x53, 0x56, 0x55, 0x5C, 0x5F, 0x5A, 0x59, 0x48, 0x4B, 0x4E, 0x4D, + 0x44, 0x47, 0x42, 0x41, + + 0xC0, 0xC3, 0xC6, 0xC5, 0xCC, 0xCF, 0xCA, 0xC9, 0xD8, 0xDB, 0xDE, 0xDD, + 0xD4, 0xD7, 0xD2, 0xD1, + + 0xF0, 0xF3, 0xF6, 0xF5, 0xFC, 0xFF, 0xFA, 0xF9, 0xE8, 0xEB, 0xEE, 0xED, + 0xE4, 0xE7, 0xE2, 0xE1, + + 0xA0, 0xA3, 0xA6, 0xA5, 0xAC, 0xAF, 0xAA, 0xA9, 0xB8, 0xBB, 0xBE, 0xBD, + 0xB4, 0xB7, 0xB2, 0xB1, + + 0x90, 0x93, 0x96, 0x95, 0x9C, 0x9F, 0x9A, 0x99, 0x88, 0x8B, 0x8E, 0x8D, + 0x84, 0x87, 0x82, 0x81, + + 0x9B, 0x98, 0x9D, 0x9E, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, + 0x8F, 0x8C, 0x89, 0x8A, + + 0xAB, 0xA8, 0xAD, 0xAE, 0xA7, 0xA4, 0xA1, 0xA2, 0xB3, 0xB0, 0xB5, 0xB6, + 0xBF, 0xBC, 0xB9, 0xBA, + + 0xFB, 0xF8, 0xFD, 0xFE, 0xF7, 0xF4, 0xF1, 0xF2, 0xE3, 0xE0, 0xE5, 0xE6, + 0xEF, 0xEC, 0xE9, 0xEA, + + 0xCB, 0xC8, 0xCD, 0xCE, 0xC7, 0xC4, 0xC1, 0xC2, 0xD3, 0xD0, 0xD5, 0xD6, + 0xDF, 0xDC, 0xD9, 0xDA, + + 0x5B, 0x58, 0x5D, 0x5E, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, + 0x4F, 0x4C, 0x49, 0x4A, + + 0x6B, 0x68, 0x6D, 0x6E, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, + 0x7F, 0x7C, 0x79, 0x7A, + + 0x3B, 0x38, 0x3D, 0x3E, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, + 0x2F, 0x2C, 0x29, 0x2A, + + 0x0B, 0x08, 0x0D, 0x0E, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, + 0x1F, 0x1C, 0x19, 0x1A +]; + + +/** + * Precomputed lookup of multiplication by 9 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_9_ = [ + 0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, + 0x6C, 0x65, 0x7E, 0x77, + + 0x90, 0x99, 0x82, 0x8B, 0xB4, 0xBD, 0xA6, 0xAF, 0xD8, 0xD1, 0xCA, 0xC3, + 0xFC, 0xF5, 0xEE, 0xE7, + + 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x73, 0x7A, 0x61, 0x68, + 0x57, 0x5E, 0x45, 0x4C, + + 0xAB, 0xA2, 0xB9, 0xB0, 0x8F, 0x86, 0x9D, 0x94, 0xE3, 0xEA, 0xF1, 0xF8, + 0xC7, 0xCE, 0xD5, 0xDC, + + 0x76, 0x7F, 0x64, 0x6D, 0x52, 0x5B, 0x40, 0x49, 0x3E, 0x37, 0x2C, 0x25, + 0x1A, 0x13, 0x08, 0x01, + + 0xE6, 0xEF, 0xF4, 0xFD, 0xC2, 0xCB, 0xD0, 0xD9, 0xAE, 0xA7, 0xBC, 0xB5, + 0x8A, 0x83, 0x98, 0x91, + + 0x4D, 0x44, 0x5F, 0x56, 0x69, 0x60, 0x7B, 0x72, 0x05, 0x0C, 0x17, 0x1E, + 0x21, 0x28, 0x33, 0x3A, + + 0xDD, 0xD4, 0xCF, 0xC6, 0xF9, 0xF0, 0xEB, 0xE2, 0x95, 0x9C, 0x87, 0x8E, + 0xB1, 0xB8, 0xA3, 0xAA, + + 0xEC, 0xE5, 0xFE, 0xF7, 0xC8, 0xC1, 0xDA, 0xD3, 0xA4, 0xAD, 0xB6, 0xBF, + 0x80, 0x89, 0x92, 0x9B, + + 0x7C, 0x75, 0x6E, 0x67, 0x58, 0x51, 0x4A, 0x43, 0x34, 0x3D, 0x26, 0x2F, + 0x10, 0x19, 0x02, 0x0B, + + 0xD7, 0xDE, 0xC5, 0xCC, 0xF3, 0xFA, 0xE1, 0xE8, 0x9F, 0x96, 0x8D, 0x84, + 0xBB, 0xB2, 0xA9, 0xA0, + + 0x47, 0x4E, 0x55, 0x5C, 0x63, 0x6A, 0x71, 0x78, 0x0F, 0x06, 0x1D, 0x14, + 0x2B, 0x22, 0x39, 0x30, + + 0x9A, 0x93, 0x88, 0x81, 0xBE, 0xB7, 0xAC, 0xA5, 0xD2, 0xDB, 0xC0, 0xC9, + 0xF6, 0xFF, 0xE4, 0xED, + + 0x0A, 0x03, 0x18, 0x11, 0x2E, 0x27, 0x3C, 0x35, 0x42, 0x4B, 0x50, 0x59, + 0x66, 0x6F, 0x74, 0x7D, + + 0xA1, 0xA8, 0xB3, 0xBA, 0x85, 0x8C, 0x97, 0x9E, 0xE9, 0xE0, 0xFB, 0xF2, + 0xCD, 0xC4, 0xDF, 0xD6, + + 0x31, 0x38, 0x23, 0x2A, 0x15, 0x1C, 0x07, 0x0E, 0x79, 0x70, 0x6B, 0x62, + 0x5D, 0x54, 0x4F, 0x46 +]; + + +/** + * Precomputed lookup of multiplication by 11 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_B_ = [ + 0x00, 0x0B, 0x16, 0x1D, 0x2C, 0x27, 0x3A, 0x31, 0x58, 0x53, 0x4E, 0x45, + 0x74, 0x7F, 0x62, 0x69, + + 0xB0, 0xBB, 0xA6, 0xAD, 0x9C, 0x97, 0x8A, 0x81, 0xE8, 0xE3, 0xFE, 0xF5, + 0xC4, 0xCF, 0xD2, 0xD9, + + 0x7B, 0x70, 0x6D, 0x66, 0x57, 0x5C, 0x41, 0x4A, 0x23, 0x28, 0x35, 0x3E, + 0x0F, 0x04, 0x19, 0x12, + + 0xCB, 0xC0, 0xDD, 0xD6, 0xE7, 0xEC, 0xF1, 0xFA, 0x93, 0x98, 0x85, 0x8E, + 0xBF, 0xB4, 0xA9, 0xA2, + + 0xF6, 0xFD, 0xE0, 0xEB, 0xDA, 0xD1, 0xCC, 0xC7, 0xAE, 0xA5, 0xB8, 0xB3, + 0x82, 0x89, 0x94, 0x9F, + + 0x46, 0x4D, 0x50, 0x5B, 0x6A, 0x61, 0x7C, 0x77, 0x1E, 0x15, 0x08, 0x03, + 0x32, 0x39, 0x24, 0x2F, + + 0x8D, 0x86, 0x9B, 0x90, 0xA1, 0xAA, 0xB7, 0xBC, 0xD5, 0xDE, 0xC3, 0xC8, + 0xF9, 0xF2, 0xEF, 0xE4, + + 0x3D, 0x36, 0x2B, 0x20, 0x11, 0x1A, 0x07, 0x0C, 0x65, 0x6E, 0x73, 0x78, + 0x49, 0x42, 0x5F, 0x54, + + 0xF7, 0xFC, 0xE1, 0xEA, 0xDB, 0xD0, 0xCD, 0xC6, 0xAF, 0xA4, 0xB9, 0xB2, + 0x83, 0x88, 0x95, 0x9E, + + 0x47, 0x4C, 0x51, 0x5A, 0x6B, 0x60, 0x7D, 0x76, 0x1F, 0x14, 0x09, 0x02, + 0x33, 0x38, 0x25, 0x2E, + + 0x8C, 0x87, 0x9A, 0x91, 0xA0, 0xAB, 0xB6, 0xBD, 0xD4, 0xDF, 0xC2, 0xC9, + 0xF8, 0xF3, 0xEE, 0xE5, + + 0x3C, 0x37, 0x2A, 0x21, 0x10, 0x1B, 0x06, 0x0D, 0x64, 0x6F, 0x72, 0x79, + 0x48, 0x43, 0x5E, 0x55, + + 0x01, 0x0A, 0x17, 0x1C, 0x2D, 0x26, 0x3B, 0x30, 0x59, 0x52, 0x4F, 0x44, + 0x75, 0x7E, 0x63, 0x68, + + 0xB1, 0xBA, 0xA7, 0xAC, 0x9D, 0x96, 0x8B, 0x80, 0xE9, 0xE2, 0xFF, 0xF4, + 0xC5, 0xCE, 0xD3, 0xD8, + + 0x7A, 0x71, 0x6C, 0x67, 0x56, 0x5D, 0x40, 0x4B, 0x22, 0x29, 0x34, 0x3F, + 0x0E, 0x05, 0x18, 0x13, + + 0xCA, 0xC1, 0xDC, 0xD7, 0xE6, 0xED, 0xF0, 0xFB, 0x92, 0x99, 0x84, 0x8F, + 0xBE, 0xB5, 0xA8, 0xA3 +]; + + +/** + * Precomputed lookup of multiplication by 13 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_D_ = [ + 0x00, 0x0D, 0x1A, 0x17, 0x34, 0x39, 0x2E, 0x23, 0x68, 0x65, 0x72, 0x7F, + 0x5C, 0x51, 0x46, 0x4B, + + 0xD0, 0xDD, 0xCA, 0xC7, 0xE4, 0xE9, 0xFE, 0xF3, 0xB8, 0xB5, 0xA2, 0xAF, + 0x8C, 0x81, 0x96, 0x9B, + + 0xBB, 0xB6, 0xA1, 0xAC, 0x8F, 0x82, 0x95, 0x98, 0xD3, 0xDE, 0xC9, 0xC4, + 0xE7, 0xEA, 0xFD, 0xF0, + + 0x6B, 0x66, 0x71, 0x7C, 0x5F, 0x52, 0x45, 0x48, 0x03, 0x0E, 0x19, 0x14, + 0x37, 0x3A, 0x2D, 0x20, + + 0x6D, 0x60, 0x77, 0x7A, 0x59, 0x54, 0x43, 0x4E, 0x05, 0x08, 0x1F, 0x12, + 0x31, 0x3C, 0x2B, 0x26, + + 0xBD, 0xB0, 0xA7, 0xAA, 0x89, 0x84, 0x93, 0x9E, 0xD5, 0xD8, 0xCF, 0xC2, + 0xE1, 0xEC, 0xFB, 0xF6, + + 0xD6, 0xDB, 0xCC, 0xC1, 0xE2, 0xEF, 0xF8, 0xF5, 0xBE, 0xB3, 0xA4, 0xA9, + 0x8A, 0x87, 0x90, 0x9D, + + 0x06, 0x0B, 0x1C, 0x11, 0x32, 0x3F, 0x28, 0x25, 0x6E, 0x63, 0x74, 0x79, + 0x5A, 0x57, 0x40, 0x4D, + + 0xDA, 0xD7, 0xC0, 0xCD, 0xEE, 0xE3, 0xF4, 0xF9, 0xB2, 0xBF, 0xA8, 0xA5, + 0x86, 0x8B, 0x9C, 0x91, + + 0x0A, 0x07, 0x10, 0x1D, 0x3E, 0x33, 0x24, 0x29, 0x62, 0x6F, 0x78, 0x75, + 0x56, 0x5B, 0x4C, 0x41, + + 0x61, 0x6C, 0x7B, 0x76, 0x55, 0x58, 0x4F, 0x42, 0x09, 0x04, 0x13, 0x1E, + 0x3D, 0x30, 0x27, 0x2A, + + 0xB1, 0xBC, 0xAB, 0xA6, 0x85, 0x88, 0x9F, 0x92, 0xD9, 0xD4, 0xC3, 0xCE, + 0xED, 0xE0, 0xF7, 0xFA, + + 0xB7, 0xBA, 0xAD, 0xA0, 0x83, 0x8E, 0x99, 0x94, 0xDF, 0xD2, 0xC5, 0xC8, + 0xEB, 0xE6, 0xF1, 0xFC, + + 0x67, 0x6A, 0x7D, 0x70, 0x53, 0x5E, 0x49, 0x44, 0x0F, 0x02, 0x15, 0x18, + 0x3B, 0x36, 0x21, 0x2C, + + 0x0C, 0x01, 0x16, 0x1B, 0x38, 0x35, 0x22, 0x2F, 0x64, 0x69, 0x7E, 0x73, + 0x50, 0x5D, 0x4A, 0x47, + + 0xDC, 0xD1, 0xC6, 0xCB, 0xE8, 0xE5, 0xF2, 0xFF, 0xB4, 0xB9, 0xAE, 0xA3, + 0x80, 0x8D, 0x9A, 0x97 +]; + + +/** + * Precomputed lookup of multiplication by 14 in GF(2^8) + * @type {!Array} + * @private + */ +goog.crypt.Aes.MULT_E_ = [ + 0x00, 0x0E, 0x1C, 0x12, 0x38, 0x36, 0x24, 0x2A, 0x70, 0x7E, 0x6C, 0x62, + 0x48, 0x46, 0x54, 0x5A, + + 0xE0, 0xEE, 0xFC, 0xF2, 0xD8, 0xD6, 0xC4, 0xCA, 0x90, 0x9E, 0x8C, 0x82, + 0xA8, 0xA6, 0xB4, 0xBA, + + 0xDB, 0xD5, 0xC7, 0xC9, 0xE3, 0xED, 0xFF, 0xF1, 0xAB, 0xA5, 0xB7, 0xB9, + 0x93, 0x9D, 0x8F, 0x81, + + 0x3B, 0x35, 0x27, 0x29, 0x03, 0x0D, 0x1F, 0x11, 0x4B, 0x45, 0x57, 0x59, + 0x73, 0x7D, 0x6F, 0x61, + + 0xAD, 0xA3, 0xB1, 0xBF, 0x95, 0x9B, 0x89, 0x87, 0xDD, 0xD3, 0xC1, 0xCF, + 0xE5, 0xEB, 0xF9, 0xF7, + + 0x4D, 0x43, 0x51, 0x5F, 0x75, 0x7B, 0x69, 0x67, 0x3D, 0x33, 0x21, 0x2F, + 0x05, 0x0B, 0x19, 0x17, + + 0x76, 0x78, 0x6A, 0x64, 0x4E, 0x40, 0x52, 0x5C, 0x06, 0x08, 0x1A, 0x14, + 0x3E, 0x30, 0x22, 0x2C, + + 0x96, 0x98, 0x8A, 0x84, 0xAE, 0xA0, 0xB2, 0xBC, 0xE6, 0xE8, 0xFA, 0xF4, + 0xDE, 0xD0, 0xC2, 0xCC, + + 0x41, 0x4F, 0x5D, 0x53, 0x79, 0x77, 0x65, 0x6B, 0x31, 0x3F, 0x2D, 0x23, + 0x09, 0x07, 0x15, 0x1B, + + 0xA1, 0xAF, 0xBD, 0xB3, 0x99, 0x97, 0x85, 0x8B, 0xD1, 0xDF, 0xCD, 0xC3, + 0xE9, 0xE7, 0xF5, 0xFB, + + 0x9A, 0x94, 0x86, 0x88, 0xA2, 0xAC, 0xBE, 0xB0, 0xEA, 0xE4, 0xF6, 0xF8, + 0xD2, 0xDC, 0xCE, 0xC0, + + 0x7A, 0x74, 0x66, 0x68, 0x42, 0x4C, 0x5E, 0x50, 0x0A, 0x04, 0x16, 0x18, + 0x32, 0x3C, 0x2E, 0x20, + + 0xEC, 0xE2, 0xF0, 0xFE, 0xD4, 0xDA, 0xC8, 0xC6, 0x9C, 0x92, 0x80, 0x8E, + 0xA4, 0xAA, 0xB8, 0xB6, + + 0x0C, 0x02, 0x10, 0x1E, 0x34, 0x3A, 0x28, 0x26, 0x7C, 0x72, 0x60, 0x6E, + 0x44, 0x4A, 0x58, 0x56, + + 0x37, 0x39, 0x2B, 0x25, 0x0F, 0x01, 0x13, 0x1D, 0x47, 0x49, 0x5B, 0x55, + 0x7F, 0x71, 0x63, 0x6D, + + 0xD7, 0xD9, 0xCB, 0xC5, 0xEF, 0xE1, 0xF3, 0xFD, 0xA7, 0xA9, 0xBB, 0xB5, + 0x9F, 0x91, 0x83, 0x8D +]; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/aes_test.html b/mapbox-gl/closure-library/closure/goog/crypt/aes_test.html new file mode 100644 index 0000000000..8643067cf2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/aes_test.html @@ -0,0 +1,24 @@ + + + + + + + goog.crypt.Aes unit test + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/aes_test.js b/mapbox-gl/closure-library/closure/goog/crypt/aes_test.js new file mode 100644 index 0000000000..b34ed416c1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/aes_test.js @@ -0,0 +1,586 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.AesTest'); +goog.setTestOnly('goog.crypt.AesTest'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Aes'); +goog.require('goog.testing.jsunit'); +goog.crypt.Aes.ENABLE_TEST_MODE = true; + +/* + * Unit test for goog.crypt.Aes using the test vectors from the spec: + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +var testData = null; + +function test128() { + doTest('000102030405060708090a0b0c0d0e0f', + '00112233445566778899aabbccddeeff', + v128, + true /* encrypt */); +} + +function test192() { + doTest('000102030405060708090a0b0c0d0e0f1011121314151617', + '00112233445566778899aabbccddeeff', + v192, + true /* encrypt */); +} + +function test256() { + doTest('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + '00112233445566778899aabbccddeeff', + v256, + true /* encrypt */); +} + +function test128d() { + doTest('000102030405060708090a0b0c0d0e0f', + '69c4e0d86a7b0430d8cdb78070b4c55a', + v128d, + false /* decrypt */); +} + +function test192d() { + doTest('000102030405060708090a0b0c0d0e0f1011121314151617', + 'dda97ca4864cdfe06eaf70a0ec0d7191', + v192d, + false /* decrypt */); +} + +function test256d() { + doTest('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + '8ea2b7ca516745bfeafc49904b496089', + v256d, + false /* decrypt */); +} + +function doTest(key, input, values, dir) { + testData = values; + var keyArray = goog.crypt.hexToByteArray(key); + var aes = new goog.crypt.Aes(keyArray); + + aes.testKeySchedule_ = onTestKeySchedule; + aes.testStartRound_ = onTestStartRound; + aes.testAfterSubBytes_ = onTestAfterSubBytes; + aes.testAfterShiftRows_ = onTestAfterShiftRows; + aes.testAfterMixColumns_ = onTestAfterMixColumns; + aes.testAfterAddRoundKey_ = onTestAfterAddRoundKey; + + var inputArr = goog.crypt.hexToByteArray(input); + var keyArr = goog.crypt.hexToByteArray(key); + var outputArr = []; + + var outputArr; + if (dir) { + outputArr = aes.encrypt(inputArr); + } else { + outputArr = aes.decrypt(inputArr); + } + + assertEquals('Incorrect output for test ' + testData.name, + testData[testData.length - 1].output, + encodeHex(outputArr)); +} + +function onTestKeySchedule(roundNum, keySchedule, keyScheduleIndex) { + assertEquals( + 'Incorrect key for round ' + roundNum, + testData[roundNum].k_sch, encodeKey(keySchedule, keyScheduleIndex)); +} + +function onTestStartRound(roundNum, state) { + assertEquals('Incorrect state for test ' + testData.name + + ' at start round ' + roundNum, + testData[roundNum].start, encodeState(state)); +} + +function onTestAfterSubBytes(roundNum, state) { + assertEquals('Incorrect state for test ' + testData.name + + ' after sub bytes in round ' + roundNum, + testData[roundNum].s_box, encodeState(state)); +} + +function onTestAfterShiftRows(roundNum, state) { + assertEquals('Incorrect state for test ' + testData.name + + ' after shift rows in round ' + roundNum, + testData[roundNum].s_row, encodeState(state)); +} + +function onTestAfterMixColumns(roundNum, state) { + assertEquals('Incorrect state for test ' + testData.name + + ' after mix columns in round ' + roundNum, + testData[roundNum].m_col, encodeState(state)); +} + +function onTestAfterAddRoundKey(roundNum, state) { + assertEquals('Incorrect state for test ' + testData.name + + ' after adding round key in round ' + roundNum, + testData[roundNum].k_add, encodeState(state)); +} + +function encodeHex(arr) { + var str = []; + + for (var i = 0; i < arr.length; i++) { + str.push(encodeByte(arr[i])); + } + + return str.join(''); +} + +function encodeState(state) { + var s = []; + + for (var c = 0; c < 4; c++) { + for (var r = 0; r < 4; r++) { + s.push(encodeByte(state[r][c])); + } + } + + return s.join(''); +} + +function encodeKey(key, round) { + var s = []; + + for (var r = round * 4; r < (round * 4 + 4); r++) { + for (var c = 0; c < 4; c++) { + s.push(encodeByte(key[r][c])); + } + } + + return s.join(''); +} + +function encodeByte(val) { + val = Number(val).toString(16); + + if (val.length == 1) { + val = '0' + val; + } + + return val; +} + +var v128 = []; +(function v128_init() { + for (var i = 0; i <= 10; i++) v128[i] = {}; + v128.name = '128'; + v128[0].input = '00112233445566778899aabbccddeeff'; + v128[0].k_sch = '000102030405060708090a0b0c0d0e0f'; + v128[1].start = '00102030405060708090a0b0c0d0e0f0'; + v128[1].s_box = '63cab7040953d051cd60e0e7ba70e18c'; + v128[1].s_row = '6353e08c0960e104cd70b751bacad0e7'; + v128[1].m_col = '5f72641557f5bc92f7be3b291db9f91a'; + v128[1].k_sch = 'd6aa74fdd2af72fadaa678f1d6ab76fe'; + v128[2].start = '89d810e8855ace682d1843d8cb128fe4'; + v128[2].s_box = 'a761ca9b97be8b45d8ad1a611fc97369'; + v128[2].s_row = 'a7be1a6997ad739bd8c9ca451f618b61'; + v128[2].m_col = 'ff87968431d86a51645151fa773ad009'; + v128[2].k_sch = 'b692cf0b643dbdf1be9bc5006830b3fe'; + v128[3].start = '4915598f55e5d7a0daca94fa1f0a63f7'; + v128[3].s_box = '3b59cb73fcd90ee05774222dc067fb68'; + v128[3].s_row = '3bd92268fc74fb735767cbe0c0590e2d'; + v128[3].m_col = '4c9c1e66f771f0762c3f868e534df256'; + v128[3].k_sch = 'b6ff744ed2c2c9bf6c590cbf0469bf41'; + v128[4].start = 'fa636a2825b339c940668a3157244d17'; + v128[4].s_box = '2dfb02343f6d12dd09337ec75b36e3f0'; + v128[4].s_row = '2d6d7ef03f33e334093602dd5bfb12c7'; + v128[4].m_col = '6385b79ffc538df997be478e7547d691'; + v128[4].k_sch = '47f7f7bc95353e03f96c32bcfd058dfd'; + v128[5].start = '247240236966b3fa6ed2753288425b6c'; + v128[5].s_box = '36400926f9336d2d9fb59d23c42c3950'; + v128[5].s_row = '36339d50f9b539269f2c092dc4406d23'; + v128[5].m_col = 'f4bcd45432e554d075f1d6c51dd03b3c'; + v128[5].k_sch = '3caaa3e8a99f9deb50f3af57adf622aa'; + v128[6].start = 'c81677bc9b7ac93b25027992b0261996'; + v128[6].s_box = 'e847f56514dadde23f77b64fe7f7d490'; + v128[6].s_row = 'e8dab6901477d4653ff7f5e2e747dd4f'; + v128[6].m_col = '9816ee7400f87f556b2c049c8e5ad036'; + v128[6].k_sch = '5e390f7df7a69296a7553dc10aa31f6b'; + v128[7].start = 'c62fe109f75eedc3cc79395d84f9cf5d'; + v128[7].s_box = 'b415f8016858552e4bb6124c5f998a4c'; + v128[7].s_row = 'b458124c68b68a014b99f82e5f15554c'; + v128[7].m_col = 'c57e1c159a9bd286f05f4be098c63439'; + v128[7].k_sch = '14f9701ae35fe28c440adf4d4ea9c026'; + v128[8].start = 'd1876c0f79c4300ab45594add66ff41f'; + v128[8].s_box = '3e175076b61c04678dfc2295f6a8bfc0'; + v128[8].s_row = '3e1c22c0b6fcbf768da85067f6170495'; + v128[8].m_col = 'baa03de7a1f9b56ed5512cba5f414d23'; + v128[8].k_sch = '47438735a41c65b9e016baf4aebf7ad2'; + v128[9].start = 'fde3bad205e5d0d73547964ef1fe37f1'; + v128[9].s_box = '5411f4b56bd9700e96a0902fa1bb9aa1'; + v128[9].s_row = '54d990a16ba09ab596bbf40ea111702f'; + v128[9].m_col = 'e9f74eec023020f61bf2ccf2353c21c7'; + v128[9].k_sch = '549932d1f08557681093ed9cbe2c974e'; + v128[10].start = 'bd6e7c3df2b5779e0b61216e8b10b689'; + v128[10].s_box = '7a9f102789d5f50b2beffd9f3dca4ea7'; + v128[10].s_row = '7ad5fda789ef4e272bca100b3d9ff59f'; + v128[10].k_sch = '13111d7fe3944a17f307a78b4d2b30c5'; + v128[10].output = '69c4e0d86a7b0430d8cdb78070b4c55a'; +})(); + +var v128d = []; +(function v128d_init() { + for (var i = 0; i <= 10; i++) v128d[i] = {}; + v128d.name = '128d'; + v128d[0].input = '69c4e0d86a7b0430d8cdb78070b4c55a'; + v128d[0].k_sch = '13111d7fe3944a17f307a78b4d2b30c5'; + v128d[1].start = '7ad5fda789ef4e272bca100b3d9ff59f'; + v128d[1].s_row = '7a9f102789d5f50b2beffd9f3dca4ea7'; + v128d[1].s_box = 'bd6e7c3df2b5779e0b61216e8b10b689'; + v128d[1].k_sch = '549932d1f08557681093ed9cbe2c974e'; + v128d[1].k_add = 'e9f74eec023020f61bf2ccf2353c21c7'; + v128d[2].start = '54d990a16ba09ab596bbf40ea111702f'; + v128d[2].s_row = '5411f4b56bd9700e96a0902fa1bb9aa1'; + v128d[2].s_box = 'fde3bad205e5d0d73547964ef1fe37f1'; + v128d[2].k_sch = '47438735a41c65b9e016baf4aebf7ad2'; + v128d[2].k_add = 'baa03de7a1f9b56ed5512cba5f414d23'; + v128d[3].start = '3e1c22c0b6fcbf768da85067f6170495'; + v128d[3].s_row = '3e175076b61c04678dfc2295f6a8bfc0'; + v128d[3].s_box = 'd1876c0f79c4300ab45594add66ff41f'; + v128d[3].k_sch = '14f9701ae35fe28c440adf4d4ea9c026'; + v128d[3].k_add = 'c57e1c159a9bd286f05f4be098c63439'; + v128d[4].start = 'b458124c68b68a014b99f82e5f15554c'; + v128d[4].s_row = 'b415f8016858552e4bb6124c5f998a4c'; + v128d[4].s_box = 'c62fe109f75eedc3cc79395d84f9cf5d'; + v128d[4].k_sch = '5e390f7df7a69296a7553dc10aa31f6b'; + v128d[4].k_add = '9816ee7400f87f556b2c049c8e5ad036'; + v128d[5].start = 'e8dab6901477d4653ff7f5e2e747dd4f'; + v128d[5].s_row = 'e847f56514dadde23f77b64fe7f7d490'; + v128d[5].s_box = 'c81677bc9b7ac93b25027992b0261996'; + v128d[5].k_sch = '3caaa3e8a99f9deb50f3af57adf622aa'; + v128d[5].k_add = 'f4bcd45432e554d075f1d6c51dd03b3c'; + v128d[6].start = '36339d50f9b539269f2c092dc4406d23'; + v128d[6].s_row = '36400926f9336d2d9fb59d23c42c3950'; + v128d[6].s_box = '247240236966b3fa6ed2753288425b6c'; + v128d[6].k_sch = '47f7f7bc95353e03f96c32bcfd058dfd'; + v128d[6].k_add = '6385b79ffc538df997be478e7547d691'; + v128d[7].start = '2d6d7ef03f33e334093602dd5bfb12c7'; + v128d[7].s_row = '2dfb02343f6d12dd09337ec75b36e3f0'; + v128d[7].s_box = 'fa636a2825b339c940668a3157244d17'; + v128d[7].k_sch = 'b6ff744ed2c2c9bf6c590cbf0469bf41'; + v128d[7].k_add = '4c9c1e66f771f0762c3f868e534df256'; + v128d[8].start = '3bd92268fc74fb735767cbe0c0590e2d'; + v128d[8].s_row = '3b59cb73fcd90ee05774222dc067fb68'; + v128d[8].s_box = '4915598f55e5d7a0daca94fa1f0a63f7'; + v128d[8].k_sch = 'b692cf0b643dbdf1be9bc5006830b3fe'; + v128d[8].k_add = 'ff87968431d86a51645151fa773ad009'; + v128d[9].start = 'a7be1a6997ad739bd8c9ca451f618b61'; + v128d[9].s_row = 'a761ca9b97be8b45d8ad1a611fc97369'; + v128d[9].s_box = '89d810e8855ace682d1843d8cb128fe4'; + v128d[9].k_sch = 'd6aa74fdd2af72fadaa678f1d6ab76fe'; + v128d[9].k_add = '5f72641557f5bc92f7be3b291db9f91a'; + v128d[10].start = '6353e08c0960e104cd70b751bacad0e7'; + v128d[10].s_row = '63cab7040953d051cd60e0e7ba70e18c'; + v128d[10].s_box = '00102030405060708090a0b0c0d0e0f0'; + v128d[10].k_sch = '000102030405060708090a0b0c0d0e0f'; + v128d[10].output = '00112233445566778899aabbccddeeff'; +})(); + +var v192 = []; +(function v192_init() { + for (var i = 0; i <= 12; i++) v192[i] = {}; + v192.name = '192'; + v192[0].input = '00112233445566778899aabbccddeeff'; + v192[0].k_sch = '000102030405060708090a0b0c0d0e0f'; + v192[1].start = '00102030405060708090a0b0c0d0e0f0'; + v192[1].s_box = '63cab7040953d051cd60e0e7ba70e18c'; + v192[1].s_row = '6353e08c0960e104cd70b751bacad0e7'; + v192[1].m_col = '5f72641557f5bc92f7be3b291db9f91a'; + v192[1].k_sch = '10111213141516175846f2f95c43f4fe'; + v192[2].start = '4f63760643e0aa85aff8c9d041fa0de4'; + v192[2].s_box = '84fb386f1ae1ac977941dd70832dd769'; + v192[2].s_row = '84e1dd691a41d76f792d389783fbac70'; + v192[2].m_col = '9f487f794f955f662afc86abd7f1ab29'; + v192[2].k_sch = '544afef55847f0fa4856e2e95c43f4fe'; + v192[3].start = 'cb02818c17d2af9c62aa64428bb25fd7'; + v192[3].s_box = '1f770c64f0b579deaaac432c3d37cf0e'; + v192[3].s_row = '1fb5430ef0accf64aa370cde3d77792c'; + v192[3].m_col = 'b7a53ecbbf9d75a0c40efc79b674cc11'; + v192[3].k_sch = '40f949b31cbabd4d48f043b810b7b342'; + v192[4].start = 'f75c7778a327c8ed8cfebfc1a6c37f53'; + v192[4].s_box = '684af5bc0acce85564bb0878242ed2ed'; + v192[4].s_row = '68cc08ed0abbd2bc642ef555244ae878'; + v192[4].m_col = '7a1e98bdacb6d1141a6944dd06eb2d3e'; + v192[4].k_sch = '58e151ab04a2a5557effb5416245080c'; + v192[5].start = '22ffc916a81474416496f19c64ae2532'; + v192[5].s_box = '9316dd47c2fa92834390a1de43e43f23'; + v192[5].s_row = '93faa123c2903f4743e4dd83431692de'; + v192[5].m_col = 'aaa755b34cffe57cef6f98e1f01c13e6'; + v192[5].k_sch = '2ab54bb43a02f8f662e3a95d66410c08'; + v192[6].start = '80121e0776fd1d8a8d8c31bc965d1fee'; + v192[6].s_box = 'cdc972c53854a47e5d64c765904cc028'; + v192[6].s_row = 'cd54c7283864c0c55d4c727e90c9a465'; + v192[6].m_col = '921f748fd96e937d622d7725ba8ba50c'; + v192[6].k_sch = 'f501857297448d7ebdf1c6ca87f33e3c'; + v192[7].start = '671ef1fd4e2a1e03dfdcb1ef3d789b30'; + v192[7].s_box = '8572a1542fe5727b9e86c8df27bc1404'; + v192[7].s_row = '85e5c8042f8614549ebca17b277272df'; + v192[7].m_col = 'e913e7b18f507d4b227ef652758acbcc'; + v192[7].k_sch = 'e510976183519b6934157c9ea351f1e0'; + v192[8].start = '0c0370d00c01e622166b8accd6db3a2c'; + v192[8].s_box = 'fe7b5170fe7c8e93477f7e4bf6b98071'; + v192[8].s_row = 'fe7c7e71fe7f807047b95193f67b8e4b'; + v192[8].m_col = '6cf5edf996eb0a069c4ef21cbfc25762'; + v192[8].k_sch = '1ea0372a995309167c439e77ff12051e'; + v192[9].start = '7255dad30fb80310e00d6c6b40d0527c'; + v192[9].s_box = '40fc5766766c7bcae1d7507f09700010'; + v192[9].s_row = '406c501076d70066e17057ca09fc7b7f'; + v192[9].m_col = '7478bcdce8a50b81d4327a9009188262'; + v192[9].k_sch = 'dd7e0e887e2fff68608fc842f9dcc154'; + v192[10].start = 'a906b254968af4e9b4bdb2d2f0c44336'; + v192[10].s_box = 'd36f3720907ebf1e8d7a37b58c1c1a05'; + v192[10].s_row = 'd37e3705907a1a208d1c371e8c6fbfb5'; + v192[10].m_col = '0d73cc2d8f6abe8b0cf2dd9bb83d422e'; + v192[10].k_sch = '859f5f237a8d5a3dc0c02952beefd63a'; + v192[11].start = '88ec930ef5e7e4b6cc32f4c906d29414'; + v192[11].s_box = 'c4cedcabe694694e4b23bfdd6fb522fa'; + v192[11].s_row = 'c494bffae62322ab4bb5dc4e6fce69dd'; + v192[11].m_col = '71d720933b6d677dc00b8f28238e0fb7'; + v192[11].k_sch = 'de601e7827bcdf2ca223800fd8aeda32'; + v192[12].start = 'afb73eeb1cd1b85162280f27fb20d585'; + v192[12].s_box = '79a9b2e99c3e6cd1aa3476cc0fb70397'; + v192[12].s_row = '793e76979c3403e9aab7b2d10fa96ccc'; + v192[12].k_sch = 'a4970a331a78dc09c418c271e3a41d5d'; + v192[12].output = 'dda97ca4864cdfe06eaf70a0ec0d7191'; +})(); + +var v192d = []; +(function v192d_init() { + for (var i = 0; i <= 12; i++) v192d[i] = {}; + v192d.name = '192d'; + v192d[0].input = 'dda97ca4864cdfe06eaf70a0ec0d7191'; + v192d[0].k_sch = 'a4970a331a78dc09c418c271e3a41d5d'; + v192d[1].start = '793e76979c3403e9aab7b2d10fa96ccc'; + v192d[1].s_row = '79a9b2e99c3e6cd1aa3476cc0fb70397'; + v192d[1].s_box = 'afb73eeb1cd1b85162280f27fb20d585'; + v192d[1].k_sch = 'de601e7827bcdf2ca223800fd8aeda32'; + v192d[1].k_add = '71d720933b6d677dc00b8f28238e0fb7'; + v192d[2].start = 'c494bffae62322ab4bb5dc4e6fce69dd'; + v192d[2].s_row = 'c4cedcabe694694e4b23bfdd6fb522fa'; + v192d[2].s_box = '88ec930ef5e7e4b6cc32f4c906d29414'; + v192d[2].k_sch = '859f5f237a8d5a3dc0c02952beefd63a'; + v192d[2].k_add = '0d73cc2d8f6abe8b0cf2dd9bb83d422e'; + v192d[3].start = 'd37e3705907a1a208d1c371e8c6fbfb5'; + v192d[3].s_row = 'd36f3720907ebf1e8d7a37b58c1c1a05'; + v192d[3].s_box = 'a906b254968af4e9b4bdb2d2f0c44336'; + v192d[3].k_sch = 'dd7e0e887e2fff68608fc842f9dcc154'; + v192d[3].k_add = '7478bcdce8a50b81d4327a9009188262'; + v192d[4].start = '406c501076d70066e17057ca09fc7b7f'; + v192d[4].s_row = '40fc5766766c7bcae1d7507f09700010'; + v192d[4].s_box = '7255dad30fb80310e00d6c6b40d0527c'; + v192d[4].k_sch = '1ea0372a995309167c439e77ff12051e'; + v192d[4].k_add = '6cf5edf996eb0a069c4ef21cbfc25762'; + v192d[5].start = 'fe7c7e71fe7f807047b95193f67b8e4b'; + v192d[5].s_row = 'fe7b5170fe7c8e93477f7e4bf6b98071'; + v192d[5].s_box = '0c0370d00c01e622166b8accd6db3a2c'; + v192d[5].k_sch = 'e510976183519b6934157c9ea351f1e0'; + v192d[5].k_add = 'e913e7b18f507d4b227ef652758acbcc'; + v192d[6].start = '85e5c8042f8614549ebca17b277272df'; + v192d[6].s_row = '8572a1542fe5727b9e86c8df27bc1404'; + v192d[6].s_box = '671ef1fd4e2a1e03dfdcb1ef3d789b30'; + v192d[6].k_sch = 'f501857297448d7ebdf1c6ca87f33e3c'; + v192d[6].k_add = '921f748fd96e937d622d7725ba8ba50c'; + v192d[7].start = 'cd54c7283864c0c55d4c727e90c9a465'; + v192d[7].s_row = 'cdc972c53854a47e5d64c765904cc028'; + v192d[7].s_box = '80121e0776fd1d8a8d8c31bc965d1fee'; + v192d[7].k_sch = '2ab54bb43a02f8f662e3a95d66410c08'; + v192d[7].k_add = 'aaa755b34cffe57cef6f98e1f01c13e6'; + v192d[8].start = '93faa123c2903f4743e4dd83431692de'; + v192d[8].s_row = '9316dd47c2fa92834390a1de43e43f23'; + v192d[8].s_box = '22ffc916a81474416496f19c64ae2532'; + v192d[8].k_sch = '58e151ab04a2a5557effb5416245080c'; + v192d[8].k_add = '7a1e98bdacb6d1141a6944dd06eb2d3e'; + v192d[9].start = '68cc08ed0abbd2bc642ef555244ae878'; + v192d[9].s_row = '684af5bc0acce85564bb0878242ed2ed'; + v192d[9].s_box = 'f75c7778a327c8ed8cfebfc1a6c37f53'; + v192d[9].k_sch = '40f949b31cbabd4d48f043b810b7b342'; + v192d[9].k_add = 'b7a53ecbbf9d75a0c40efc79b674cc11'; + v192d[10].start = '1fb5430ef0accf64aa370cde3d77792c'; + v192d[10].s_row = '1f770c64f0b579deaaac432c3d37cf0e'; + v192d[10].s_box = 'cb02818c17d2af9c62aa64428bb25fd7'; + v192d[10].k_sch = '544afef55847f0fa4856e2e95c43f4fe'; + v192d[10].k_add = '9f487f794f955f662afc86abd7f1ab29'; + v192d[11].start = '84e1dd691a41d76f792d389783fbac70'; + v192d[11].s_row = '84fb386f1ae1ac977941dd70832dd769'; + v192d[11].s_box = '4f63760643e0aa85aff8c9d041fa0de4'; + v192d[11].k_sch = '10111213141516175846f2f95c43f4fe'; + v192d[11].k_add = '5f72641557f5bc92f7be3b291db9f91a'; + v192d[12].start = '6353e08c0960e104cd70b751bacad0e7'; + v192d[12].s_row = '63cab7040953d051cd60e0e7ba70e18c'; + v192d[12].s_box = '00102030405060708090a0b0c0d0e0f0'; + v192d[12].k_sch = '000102030405060708090a0b0c0d0e0f'; + v192d[12].output = '00112233445566778899aabbccddeeff'; +})(); + +var v256 = []; +(function v256_init() { + for (var i = 0; i <= 14; i++) v256[i] = {}; + v256.name = '256'; + v256[0].input = '00112233445566778899aabbccddeeff'; + v256[0].k_sch = '000102030405060708090a0b0c0d0e0f'; + v256[1].start = '00102030405060708090a0b0c0d0e0f0'; + v256[1].s_box = '63cab7040953d051cd60e0e7ba70e18c'; + v256[1].s_row = '6353e08c0960e104cd70b751bacad0e7'; + v256[1].m_col = '5f72641557f5bc92f7be3b291db9f91a'; + v256[1].k_sch = '101112131415161718191a1b1c1d1e1f'; + v256[2].start = '4f63760643e0aa85efa7213201a4e705'; + v256[2].s_box = '84fb386f1ae1ac97df5cfd237c49946b'; + v256[2].s_row = '84e1fd6b1a5c946fdf4938977cfbac23'; + v256[2].m_col = 'bd2a395d2b6ac438d192443e615da195'; + v256[2].k_sch = 'a573c29fa176c498a97fce93a572c09c'; + v256[3].start = '1859fbc28a1c00a078ed8aadc42f6109'; + v256[3].s_box = 'adcb0f257e9c63e0bc557e951c15ef01'; + v256[3].s_row = 'ad9c7e017e55ef25bc150fe01ccb6395'; + v256[3].m_col = '810dce0cc9db8172b3678c1e88a1b5bd'; + v256[3].k_sch = '1651a8cd0244beda1a5da4c10640bade'; + v256[4].start = '975c66c1cb9f3fa8a93a28df8ee10f63'; + v256[4].s_box = '884a33781fdb75c2d380349e19f876fb'; + v256[4].s_row = '88db34fb1f807678d3f833c2194a759e'; + v256[4].m_col = 'b2822d81abe6fb275faf103a078c0033'; + v256[4].k_sch = 'ae87dff00ff11b68a68ed5fb03fc1567'; + v256[5].start = '1c05f271a417e04ff921c5c104701554'; + v256[5].s_box = '9c6b89a349f0e18499fda678f2515920'; + v256[5].s_row = '9cf0a62049fd59a399518984f26be178'; + v256[5].m_col = 'aeb65ba974e0f822d73f567bdb64c877'; + v256[5].k_sch = '6de1f1486fa54f9275f8eb5373b8518d'; + v256[6].start = 'c357aae11b45b7b0a2c7bd28a8dc99fa'; + v256[6].s_box = '2e5bacf8af6ea9e73ac67a34c286ee2d'; + v256[6].s_row = '2e6e7a2dafc6eef83a86ace7c25ba934'; + v256[6].m_col = 'b951c33c02e9bd29ae25cdb1efa08cc7'; + v256[6].k_sch = 'c656827fc9a799176f294cec6cd5598b'; + v256[7].start = '7f074143cb4e243ec10c815d8375d54c'; + v256[7].s_box = 'd2c5831a1f2f36b278fe0c4cec9d0329'; + v256[7].s_row = 'd22f0c291ffe031a789d83b2ecc5364c'; + v256[7].m_col = 'ebb19e1c3ee7c9e87d7535e9ed6b9144'; + v256[7].k_sch = '3de23a75524775e727bf9eb45407cf39'; + v256[8].start = 'd653a4696ca0bc0f5acaab5db96c5e7d'; + v256[8].s_box = 'f6ed49f950e06576be74624c565058ff'; + v256[8].s_row = 'f6e062ff507458f9be50497656ed654c'; + v256[8].m_col = '5174c8669da98435a8b3e62ca974a5ea'; + v256[8].k_sch = '0bdc905fc27b0948ad5245a4c1871c2f'; + v256[9].start = '5aa858395fd28d7d05e1a38868f3b9c5'; + v256[9].s_box = 'bec26a12cfb55dff6bf80ac4450d56a6'; + v256[9].s_row = 'beb50aa6cff856126b0d6aff45c25dc4'; + v256[9].m_col = '0f77ee31d2ccadc05430a83f4ef96ac3'; + v256[9].k_sch = '45f5a66017b2d387300d4d33640a820a'; + v256[10].start = '4a824851c57e7e47643de50c2af3e8c9'; + v256[10].s_box = 'd61352d1a6f3f3a04327d9fee50d9bdd'; + v256[10].s_row = 'd6f3d9dda6279bd1430d52a0e513f3fe'; + v256[10].m_col = 'bd86f0ea748fc4f4630f11c1e9331233'; + v256[10].k_sch = '7ccff71cbeb4fe5413e6bbf0d261a7df'; + v256[11].start = 'c14907f6ca3b3aa070e9aa313b52b5ec'; + v256[11].s_box = '783bc54274e280e0511eacc7e200d5ce'; + v256[11].s_row = '78e2acce741ed5425100c5e0e23b80c7'; + v256[11].m_col = 'af8690415d6e1dd387e5fbedd5c89013'; + v256[11].k_sch = 'f01afafee7a82979d7a5644ab3afe640'; + v256[12].start = '5f9c6abfbac634aa50409fa766677653'; + v256[12].s_box = 'cfde0208f4b418ac5309db5c338538ed'; + v256[12].s_row = 'cfb4dbedf4093808538502ac33de185c'; + v256[12].m_col = '7427fae4d8a695269ce83d315be0392b'; + v256[12].k_sch = '2541fe719bf500258813bbd55a721c0a'; + v256[13].start = '516604954353950314fb86e401922521'; + v256[13].s_box = 'd133f22a1aed2a7bfa0f44697c4f3ffd'; + v256[13].s_row = 'd1ed44fd1a0f3f2afa4ff27b7c332a69'; + v256[13].m_col = '2c21a820306f154ab712c75eee0da04f'; + v256[13].k_sch = '4e5a6699a9f24fe07e572baacdf8cdea'; + v256[14].start = '627bceb9999d5aaac945ecf423f56da5'; + v256[14].s_box = 'aa218b56ee5ebeacdd6ecebf26e63c06'; + v256[14].s_row = 'aa5ece06ee6e3c56dde68bac2621bebf'; + v256[14].k_sch = '24fc79ccbf0979e9371ac23c6d68de36'; + v256[14].output = '8ea2b7ca516745bfeafc49904b496089'; +})(); + +var v256d = []; +(function v256d_init() { + for (var i = 0; i <= 14; i++) v256d[i] = {}; + v256d.name = '256d'; + v256d[0].input = '8ea2b7ca516745bfeafc49904b496089'; + v256d[0].k_sch = '24fc79ccbf0979e9371ac23c6d68de36'; + v256d[1].start = 'aa5ece06ee6e3c56dde68bac2621bebf'; + v256d[1].s_row = 'aa218b56ee5ebeacdd6ecebf26e63c06'; + v256d[1].s_box = '627bceb9999d5aaac945ecf423f56da5'; + v256d[1].k_sch = '4e5a6699a9f24fe07e572baacdf8cdea'; + v256d[1].k_add = '2c21a820306f154ab712c75eee0da04f'; + v256d[2].start = 'd1ed44fd1a0f3f2afa4ff27b7c332a69'; + v256d[2].s_row = 'd133f22a1aed2a7bfa0f44697c4f3ffd'; + v256d[2].s_box = '516604954353950314fb86e401922521'; + v256d[2].k_sch = '2541fe719bf500258813bbd55a721c0a'; + v256d[2].k_add = '7427fae4d8a695269ce83d315be0392b'; + v256d[3].start = 'cfb4dbedf4093808538502ac33de185c'; + v256d[3].s_row = 'cfde0208f4b418ac5309db5c338538ed'; + v256d[3].s_box = '5f9c6abfbac634aa50409fa766677653'; + v256d[3].k_sch = 'f01afafee7a82979d7a5644ab3afe640'; + v256d[3].k_add = 'af8690415d6e1dd387e5fbedd5c89013'; + v256d[4].start = '78e2acce741ed5425100c5e0e23b80c7'; + v256d[4].s_row = '783bc54274e280e0511eacc7e200d5ce'; + v256d[4].s_box = 'c14907f6ca3b3aa070e9aa313b52b5ec'; + v256d[4].k_sch = '7ccff71cbeb4fe5413e6bbf0d261a7df'; + v256d[4].k_add = 'bd86f0ea748fc4f4630f11c1e9331233'; + v256d[5].start = 'd6f3d9dda6279bd1430d52a0e513f3fe'; + v256d[5].s_row = 'd61352d1a6f3f3a04327d9fee50d9bdd'; + v256d[5].s_box = '4a824851c57e7e47643de50c2af3e8c9'; + v256d[5].k_sch = '45f5a66017b2d387300d4d33640a820a'; + v256d[5].k_add = '0f77ee31d2ccadc05430a83f4ef96ac3'; + v256d[6].start = 'beb50aa6cff856126b0d6aff45c25dc4'; + v256d[6].s_row = 'bec26a12cfb55dff6bf80ac4450d56a6'; + v256d[6].s_box = '5aa858395fd28d7d05e1a38868f3b9c5'; + v256d[6].k_sch = '0bdc905fc27b0948ad5245a4c1871c2f'; + v256d[6].k_add = '5174c8669da98435a8b3e62ca974a5ea'; + v256d[7].start = 'f6e062ff507458f9be50497656ed654c'; + v256d[7].s_row = 'f6ed49f950e06576be74624c565058ff'; + v256d[7].s_box = 'd653a4696ca0bc0f5acaab5db96c5e7d'; + v256d[7].k_sch = '3de23a75524775e727bf9eb45407cf39'; + v256d[7].k_add = 'ebb19e1c3ee7c9e87d7535e9ed6b9144'; + v256d[8].start = 'd22f0c291ffe031a789d83b2ecc5364c'; + v256d[8].s_row = 'd2c5831a1f2f36b278fe0c4cec9d0329'; + v256d[8].s_box = '7f074143cb4e243ec10c815d8375d54c'; + v256d[8].k_sch = 'c656827fc9a799176f294cec6cd5598b'; + v256d[8].k_add = 'b951c33c02e9bd29ae25cdb1efa08cc7'; + v256d[9].start = '2e6e7a2dafc6eef83a86ace7c25ba934'; + v256d[9].s_row = '2e5bacf8af6ea9e73ac67a34c286ee2d'; + v256d[9].s_box = 'c357aae11b45b7b0a2c7bd28a8dc99fa'; + v256d[9].k_sch = '6de1f1486fa54f9275f8eb5373b8518d'; + v256d[9].k_add = 'aeb65ba974e0f822d73f567bdb64c877'; + v256d[10].start = '9cf0a62049fd59a399518984f26be178'; + v256d[10].s_row = '9c6b89a349f0e18499fda678f2515920'; + v256d[10].s_box = '1c05f271a417e04ff921c5c104701554'; + v256d[10].k_sch = 'ae87dff00ff11b68a68ed5fb03fc1567'; + v256d[10].k_add = 'b2822d81abe6fb275faf103a078c0033'; + v256d[11].start = '88db34fb1f807678d3f833c2194a759e'; + v256d[11].s_row = '884a33781fdb75c2d380349e19f876fb'; + v256d[11].s_box = '975c66c1cb9f3fa8a93a28df8ee10f63'; + v256d[11].k_sch = '1651a8cd0244beda1a5da4c10640bade'; + v256d[11].k_add = '810dce0cc9db8172b3678c1e88a1b5bd'; + v256d[12].start = 'ad9c7e017e55ef25bc150fe01ccb6395'; + v256d[12].s_row = 'adcb0f257e9c63e0bc557e951c15ef01'; + v256d[12].s_box = '1859fbc28a1c00a078ed8aadc42f6109'; + v256d[12].k_sch = 'a573c29fa176c498a97fce93a572c09c'; + v256d[12].k_add = 'bd2a395d2b6ac438d192443e615da195'; + v256d[13].start = '84e1fd6b1a5c946fdf4938977cfbac23'; + v256d[13].s_row = '84fb386f1ae1ac97df5cfd237c49946b'; + v256d[13].s_box = '4f63760643e0aa85efa7213201a4e705'; + v256d[13].k_sch = '101112131415161718191a1b1c1d1e1f'; + v256d[13].k_add = '5f72641557f5bc92f7be3b291db9f91a'; + v256d[14].start = '6353e08c0960e104cd70b751bacad0e7'; + v256d[14].s_row = '63cab7040953d051cd60e0e7ba70e18c'; + v256d[14].s_box = '00102030405060708090a0b0c0d0e0f0'; + v256d[14].k_sch = '000102030405060708090a0b0c0d0e0f'; + v256d[14].output = '00112233445566778899aabbccddeeff'; +})(); diff --git a/mapbox-gl/closure-library/closure/goog/crypt/arc4.js b/mapbox-gl/closure-library/closure/goog/crypt/arc4.js new file mode 100644 index 0000000000..73c6758263 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/arc4.js @@ -0,0 +1,164 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview ARC4 streamcipher implementation. A description of the + * algorithm can be found at: + * http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt. + * + * Usage: + * + * var arc4 = new goog.crypt.Arc4(); + * arc4.setKey(key); + * arc4.discard(1536); + * arc4.crypt(bytes); + * + * + * Note: For converting between strings and byte arrays, goog.crypt.base64 may + * be useful. + * + */ + +goog.provide('goog.crypt.Arc4'); + +goog.require('goog.asserts'); + + + +/** + * ARC4 streamcipher implementation. + * @constructor + * @final + * @struct + */ +goog.crypt.Arc4 = function() { + /** + * A permutation of all 256 possible bytes. + * @type {Array} + * @private + */ + this.state_ = []; + + /** + * 8 bit index pointer into this.state_. + * @type {number} + * @private + */ + this.index1_ = 0; + + /** + * 8 bit index pointer into this.state_. + * @type {number} + * @private + */ + this.index2_ = 0; +}; + + +/** + * Initialize the cipher for use with new key. + * @param {Array} key A byte array containing the key. + * @param {number=} opt_length Indicates # of bytes to take from the key. + */ +goog.crypt.Arc4.prototype.setKey = function(key, opt_length) { + goog.asserts.assertArray(key, 'Key parameter must be a byte array'); + + if (!opt_length) { + opt_length = key.length; + } + + var state = this.state_; + + for (var i = 0; i < 256; ++i) { + state[i] = i; + } + + var j = 0; + for (var i = 0; i < 256; ++i) { + j = (j + state[i] + key[i % opt_length]) & 255; + + var tmp = state[i]; + state[i] = state[j]; + state[j] = tmp; + } + + this.index1_ = 0; + this.index2_ = 0; +}; + + +/** + * Discards n bytes of the keystream. + * These days 1536 is considered a decent amount to drop to get the key state + * warmed-up enough for secure usage. This is not done in the constructor to + * preserve efficiency for use cases that do not need this. + * NOTE: Discard is identical to crypt without actually xoring any data. It's + * unfortunate to have this code duplicated, but this was done for performance + * reasons. Alternatives which were attempted: + * 1. Create a temp array of the correct length and pass it to crypt. This + * works but needlessly allocates an array. But more importantly this + * requires choosing an array type (Array or Uint8Array) in discard, and + * choosing a different type than will be passed to crypt by the client + * code hurts the javascript engines ability to optimize crypt (7x hit in + * v8). + * 2. Make data option in crypt so discard can pass null, this has a huge + * perf hit for crypt. + * @param {number} length Number of bytes to disregard from the stream. + */ +goog.crypt.Arc4.prototype.discard = function(length) { + var i = this.index1_; + var j = this.index2_; + var state = this.state_; + + for (var n = 0; n < length; ++n) { + i = (i + 1) & 255; + j = (j + state[i]) & 255; + + var tmp = state[i]; + state[i] = state[j]; + state[j] = tmp; + } + + this.index1_ = i; + this.index2_ = j; +}; + + +/** + * En- or decrypt (same operation for streamciphers like ARC4) + * @param {Array|Uint8Array} data The data to be xor-ed in place. + * @param {number=} opt_length The number of bytes to crypt. + */ +goog.crypt.Arc4.prototype.crypt = function(data, opt_length) { + if (!opt_length) { + opt_length = data.length; + } + var i = this.index1_; + var j = this.index2_; + var state = this.state_; + + for (var n = 0; n < opt_length; ++n) { + i = (i + 1) & 255; + j = (j + state[i]) & 255; + + var tmp = state[i]; + state[i] = state[j]; + state[j] = tmp; + + data[n] ^= state[(state[i] + state[j]) & 255]; + } + + this.index1_ = i; + this.index2_ = j; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.html b/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.html new file mode 100644 index 0000000000..3395316393 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.arc4 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.js b/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.js new file mode 100644 index 0000000000..856f4f9332 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/arc4_test.js @@ -0,0 +1,59 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Arc4Test'); +goog.setTestOnly('goog.crypt.Arc4Test'); + +goog.require('goog.array'); +goog.require('goog.crypt.Arc4'); +goog.require('goog.testing.jsunit'); + +function testEncryptionDecryption() { + var key = [0x25, 0x26, 0x27, 0x28]; + var startArray = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67]; + var byteArray = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67]; + + var arc4 = new goog.crypt.Arc4(); + arc4.setKey(key); + arc4.crypt(byteArray); + + assertArrayEquals(byteArray, [0x51, 0xBB, 0xDD, 0x95, 0x9B, 0x42, 0x34]); + + // The same key and crypt call should unencrypt the data back to its original + // state + arc4 = new goog.crypt.Arc4(); + arc4.setKey(key); + arc4.crypt(byteArray); + assertArrayEquals(byteArray, startArray); +} + +function testDiscard() { + var key = [0x25, 0x26, 0x27, 0x28]; + var data = [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67]; + + var arc4 = new goog.crypt.Arc4(); + arc4.setKey(key); + arc4.discard(256); + var withDiscard = goog.array.clone(data); + arc4.crypt(withDiscard); + + // First encrypting a dummy array should give the same result as + // discarding. + arc4 = new goog.crypt.Arc4(); + arc4.setKey(key); + var withCrypt = goog.array.clone(data); + arc4.crypt(new Array(256)); + arc4.crypt(withCrypt); + assertArrayEquals(withDiscard, withCrypt); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/base64.js b/mapbox-gl/closure-library/closure/goog/crypt/base64.js new file mode 100644 index 0000000000..9103fa177e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/base64.js @@ -0,0 +1,286 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base64 en/decoding. Not much to say here except that we + * work with decoded values in arrays of bytes. By "byte" I mean a number + * in [0, 255]. + * + * @author doughtie@google.com (Gavin Doughtie) + */ + +goog.provide('goog.crypt.base64'); +goog.require('goog.crypt'); +goog.require('goog.userAgent'); + +// Static lookup maps, lazily populated by init_() + + +/** + * Maps bytes to characters. + * @type {Object} + * @private + */ +goog.crypt.base64.byteToCharMap_ = null; + + +/** + * Maps characters to bytes. + * @type {Object} + * @private + */ +goog.crypt.base64.charToByteMap_ = null; + + +/** + * Maps bytes to websafe characters. + * @type {Object} + * @private + */ +goog.crypt.base64.byteToCharMapWebSafe_ = null; + + +/** + * Maps websafe characters to bytes. + * @type {Object} + * @private + */ +goog.crypt.base64.charToByteMapWebSafe_ = null; + + +/** + * Our default alphabet, shared between + * ENCODED_VALS and ENCODED_VALS_WEBSAFE + * @type {string} + */ +goog.crypt.base64.ENCODED_VALS_BASE = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + 'abcdefghijklmnopqrstuvwxyz' + + '0123456789'; + + +/** + * Our default alphabet. Value 64 (=) is special; it means "nothing." + * @type {string} + */ +goog.crypt.base64.ENCODED_VALS = + goog.crypt.base64.ENCODED_VALS_BASE + '+/='; + + +/** + * Our websafe alphabet. + * @type {string} + */ +goog.crypt.base64.ENCODED_VALS_WEBSAFE = + goog.crypt.base64.ENCODED_VALS_BASE + '-_.'; + + +/** + * Whether this browser supports the atob and btoa functions. This extension + * started at Mozilla but is now implemented by many browsers. We use the + * ASSUME_* variables to avoid pulling in the full useragent detection library + * but still allowing the standard per-browser compilations. + * + * @type {boolean} + */ +goog.crypt.base64.HAS_NATIVE_SUPPORT = goog.userAgent.GECKO || + goog.userAgent.WEBKIT || + goog.userAgent.OPERA || + typeof(goog.global.atob) == 'function'; + + +/** + * Base64-encode an array of bytes. + * + * @param {Array|Uint8Array} input An array of bytes (numbers with + * value in [0, 255]) to encode. + * @param {boolean=} opt_webSafe Boolean indicating we should use the + * alternative alphabet. + * @return {string} The base64 encoded string. + */ +goog.crypt.base64.encodeByteArray = function(input, opt_webSafe) { + if (!goog.isArrayLike(input)) { + throw Error('encodeByteArray takes an array as a parameter'); + } + + goog.crypt.base64.init_(); + + var byteToCharMap = opt_webSafe ? + goog.crypt.base64.byteToCharMapWebSafe_ : + goog.crypt.base64.byteToCharMap_; + + var output = []; + + for (var i = 0; i < input.length; i += 3) { + var byte1 = input[i]; + var haveByte2 = i + 1 < input.length; + var byte2 = haveByte2 ? input[i + 1] : 0; + var haveByte3 = i + 2 < input.length; + var byte3 = haveByte3 ? input[i + 2] : 0; + + var outByte1 = byte1 >> 2; + var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); + var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); + var outByte4 = byte3 & 0x3F; + + if (!haveByte3) { + outByte4 = 64; + + if (!haveByte2) { + outByte3 = 64; + } + } + + output.push(byteToCharMap[outByte1], + byteToCharMap[outByte2], + byteToCharMap[outByte3], + byteToCharMap[outByte4]); + } + + return output.join(''); +}; + + +/** + * Base64-encode a string. + * + * @param {string} input A string to encode. + * @param {boolean=} opt_webSafe If true, we should use the + * alternative alphabet. + * @return {string} The base64 encoded string. + */ +goog.crypt.base64.encodeString = function(input, opt_webSafe) { + // Shortcut for Mozilla browsers that implement + // a native base64 encoder in the form of "btoa/atob" + if (goog.crypt.base64.HAS_NATIVE_SUPPORT && !opt_webSafe) { + return goog.global.btoa(input); + } + return goog.crypt.base64.encodeByteArray( + goog.crypt.stringToByteArray(input), opt_webSafe); +}; + + +/** + * Base64-decode a string. + * + * @param {string} input to decode. + * @param {boolean=} opt_webSafe True if we should use the + * alternative alphabet. + * @return {string} string representing the decoded value. + */ +goog.crypt.base64.decodeString = function(input, opt_webSafe) { + // Shortcut for Mozilla browsers that implement + // a native base64 encoder in the form of "btoa/atob" + if (goog.crypt.base64.HAS_NATIVE_SUPPORT && !opt_webSafe) { + return goog.global.atob(input); + } + return goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(input, opt_webSafe)); +}; + + +/** + * Base64-decode a string. + * + * In base-64 decoding, groups of four characters are converted into three + * bytes. If the encoder did not apply padding, the input length may not + * be a multiple of 4. + * + * In this case, the last group will have fewer than 4 characters, and + * padding will be inferred. If the group has one or two characters, it decodes + * to one byte. If the group has three characters, it decodes to two bytes. + * + * @param {string} input Input to decode. + * @param {boolean=} opt_webSafe True if we should use the web-safe alphabet. + * @return {!Array} bytes representing the decoded value. + */ +goog.crypt.base64.decodeStringToByteArray = function(input, opt_webSafe) { + goog.crypt.base64.init_(); + + var charToByteMap = opt_webSafe ? + goog.crypt.base64.charToByteMapWebSafe_ : + goog.crypt.base64.charToByteMap_; + + var output = []; + + for (var i = 0; i < input.length; ) { + var byte1 = charToByteMap[input.charAt(i++)]; + + var haveByte2 = i < input.length; + var byte2 = haveByte2 ? charToByteMap[input.charAt(i)] : 0; + ++i; + + var haveByte3 = i < input.length; + var byte3 = haveByte3 ? charToByteMap[input.charAt(i)] : 64; + ++i; + + var haveByte4 = i < input.length; + var byte4 = haveByte4 ? charToByteMap[input.charAt(i)] : 64; + ++i; + + if (byte1 == null || byte2 == null || + byte3 == null || byte4 == null) { + throw Error(); + } + + var outByte1 = (byte1 << 2) | (byte2 >> 4); + output.push(outByte1); + + if (byte3 != 64) { + var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2); + output.push(outByte2); + + if (byte4 != 64) { + var outByte3 = ((byte3 << 6) & 0xC0) | byte4; + output.push(outByte3); + } + } + } + + return output; +}; + + +/** + * Lazy static initialization function. Called before + * accessing any of the static map variables. + * @private + */ +goog.crypt.base64.init_ = function() { + if (!goog.crypt.base64.byteToCharMap_) { + goog.crypt.base64.byteToCharMap_ = {}; + goog.crypt.base64.charToByteMap_ = {}; + goog.crypt.base64.byteToCharMapWebSafe_ = {}; + goog.crypt.base64.charToByteMapWebSafe_ = {}; + + // We want quick mappings back and forth, so we precompute two maps. + for (var i = 0; i < goog.crypt.base64.ENCODED_VALS.length; i++) { + goog.crypt.base64.byteToCharMap_[i] = + goog.crypt.base64.ENCODED_VALS.charAt(i); + goog.crypt.base64.charToByteMap_[goog.crypt.base64.byteToCharMap_[i]] = i; + goog.crypt.base64.byteToCharMapWebSafe_[i] = + goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i); + goog.crypt.base64.charToByteMapWebSafe_[ + goog.crypt.base64.byteToCharMapWebSafe_[i]] = i; + + // Be forgiving when decoding and correctly decode both encodings. + if (i >= goog.crypt.base64.ENCODED_VALS_BASE.length) { + goog.crypt.base64.charToByteMap_[ + goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i)] = i; + goog.crypt.base64.charToByteMapWebSafe_[ + goog.crypt.base64.ENCODED_VALS.charAt(i)] = i; + } + } + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/base64_test.html b/mapbox-gl/closure-library/closure/goog/crypt/base64_test.html new file mode 100644 index 0000000000..27db42c38e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/base64_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.base64 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/base64_test.js b/mapbox-gl/closure-library/closure/goog/crypt/base64_test.js new file mode 100644 index 0000000000..c4946884e0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/base64_test.js @@ -0,0 +1,156 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.base64Test'); +goog.setTestOnly('goog.crypt.base64Test'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.base64'); +goog.require('goog.testing.jsunit'); + +// Static test data +var tests = [ + '', '', + 'f', 'Zg==', + 'fo', 'Zm8=', + 'foo', 'Zm9v', + 'foob', 'Zm9vYg==', + 'fooba', 'Zm9vYmE=', + 'foobar', 'Zm9vYmFy', + + // Testing non-ascii characters (1-10 in chinese) + '\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89\xe5\x9b\x9b\xe4\xba\x94\xe5' + + '\x85\xad\xe4\xb8\x83\xe5\x85\xab\xe4\xb9\x9d\xe5\x8d\x81', + '5LiA5LqM5LiJ5Zub5LqU5YWt5LiD5YWr5Lmd5Y2B']; + +function testByteArrayEncoding() { + // Let's see if it's sane by feeding it some well-known values. Index i + // has the input and index i+1 has the expected value. + for (var i = 0; i < tests.length; i += 2) { + var enc = goog.crypt.base64.encodeByteArray( + goog.crypt.stringToByteArray(tests[i])); + assertEquals(tests[i + 1], enc); + var dec = goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(enc)); + assertEquals(tests[i], dec); + + // Check that websafe decoding accepts non-websafe codes. + dec = goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(enc, true /* websafe */)); + assertEquals(tests[i], dec); + + // Re-encode as websafe. + enc = goog.crypt.base64.encodeByteArray( + goog.crypt.stringToByteArray(tests[i], true /* websafe */)); + + // Check that non-websafe decoding accepts websafe codes. + dec = goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(enc)); + assertEquals(tests[i], dec); + + // Check that websafe decoding accepts websafe codes. + dec = goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(enc, true /* websafe */)); + assertEquals(tests[i], dec); + } +} + +function testOddLengthByteArrayEncoding() { + var buffer = [0, 0, 0]; + var encodedBuffer = goog.crypt.base64.encodeByteArray(buffer); + assertEquals('AAAA', encodedBuffer); + + var decodedBuffer = goog.crypt.base64.decodeStringToByteArray(encodedBuffer); + assertEquals(decodedBuffer.length, buffer.length); + for (i = 0; i < buffer.length; i++) { + assertEquals(buffer[i], decodedBuffer[i]); + } +} + +// Tests that decoding a string where the length is not a multiple of 4 does +// not produce spurious trailing zeroes. This is a regression test for +// cl/65120705, which fixes a bug that was introduced when support for +// non-padded base64 encoding was added in cl/20209336. +function testOddLengthByteArrayDecoding() { + // The base-64 encoding of the bytes [97, 98, 99, 100], with no padding. + // The padded version would be "YWJjZA==" (length 8), or "YWJjZA.." if + // web-safe. + var encodedBuffer = 'YWJjZA'; + var decodedBuffer1 = goog.crypt.base64.decodeStringToByteArray(encodedBuffer); + assertEquals(4, decodedBuffer1.length); + // Note that byteArrayToString ignores any trailing zeroes because + // String.fromCharCode(0) is ''. + assertEquals('abcd', goog.crypt.byteArrayToString(decodedBuffer1)); + + // Repeat the test in web-safe decoding mode. + var decodedBuffer2 = goog.crypt.base64.decodeStringToByteArray(encodedBuffer, + true /* web-safe */); + assertEquals(4, decodedBuffer2.length); + assertEquals('abcd', goog.crypt.byteArrayToString(decodedBuffer2)); +} + +function testShortcutPathEncoding() { + // Test the higher-level API (tests the btoa/atob shortcut path) + for (var i = 0; i < tests.length; i += 2) { + var enc = goog.crypt.base64.encodeString(tests[i]); + assertEquals(tests[i + 1], enc); + var dec = goog.crypt.base64.decodeString(enc); + assertEquals(tests[i], dec); + } +} + +function testMultipleIterations() { + // Now run it through its paces + + var numIterations = 100; + for (var i = 0; i < numIterations; i++) { + + var input = []; + for (var j = 0; j < i; j++) + input[j] = j % 256; + + var encoded = goog.crypt.base64.encodeByteArray(input); + var decoded = goog.crypt.base64.decodeStringToByteArray(encoded); + assertEquals('Decoded length not equal to input length?', + input.length, decoded.length); + + for (var j = 0; j < i; j++) + assertEquals('Values differ at position ' + j, input[j], decoded[j]); + } +} + +function testWebSafeEncoding() { + // Test non-websafe / websafe difference + var test = '>>>???>>>???=/+'; + var enc = goog.crypt.base64.encodeByteArray( + goog.crypt.stringToByteArray(test)); + assertEquals('Non-websafe broken?', 'Pj4+Pz8/Pj4+Pz8/PS8r', enc); + enc = goog.crypt.base64.encodeString(test); + assertEquals('Non-websafe broken?', 'Pj4+Pz8/Pj4+Pz8/PS8r', enc); + enc = goog.crypt.base64.encodeByteArray( + goog.crypt.stringToByteArray(test), true /* websafe */); + assertEquals('Websafe encoding broken', 'Pj4-Pz8_Pj4-Pz8_PS8r', enc); + enc = goog.crypt.base64.encodeString(test, true); + assertEquals('Non-websafe broken?', 'Pj4-Pz8_Pj4-Pz8_PS8r', enc); + var dec = goog.crypt.byteArrayToString( + goog.crypt.base64.decodeStringToByteArray(enc, true /* websafe */)); + assertEquals('Websafe decoding broken', test, dec); + dec = goog.crypt.base64.decodeString(enc, true /* websafe */); + assertEquals('Websafe decoding broken', test, dec); + + // Test parsing malformed characters + assertThrows('Didn\'t throw on malformed input', function() { + goog.crypt.base64.decodeStringToByteArray('foooooo)oooo', true /*websafe*/); + }); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/basen.js b/mapbox-gl/closure-library/closure/goog/crypt/basen.js new file mode 100644 index 0000000000..2bac248f8d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/basen.js @@ -0,0 +1,242 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Numeric base conversion library. Works for arbitrary bases and + * arbitrary length numbers. + * + * For base-64 conversion use base64.js because it is optimized for the specific + * conversion to base-64 while this module is generic. Base-64 is defined here + * mostly for demonstration purpose. + * + * TODO: Make base64 and baseN classes that have common interface. (Perhaps...) + * + */ + +goog.provide('goog.crypt.baseN'); + + +/** + * Base-2, i.e. '01'. + * @type {string} + */ +goog.crypt.baseN.BASE_BINARY = '01'; + + +/** + * Base-8, i.e. '01234567'. + * @type {string} + */ +goog.crypt.baseN.BASE_OCTAL = '01234567'; + + +/** + * Base-10, i.e. '0123456789'. + * @type {string} + */ +goog.crypt.baseN.BASE_DECIMAL = '0123456789'; + + +/** + * Base-16 using lower case, i.e. '0123456789abcdef'. + * @type {string} + */ +goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL = '0123456789abcdef'; + + +/** + * Base-16 using upper case, i.e. '0123456789ABCDEF'. + * @type {string} + */ +goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL = '0123456789ABCDEF'; + + +/** + * The more-known version of the BASE-64 encoding. Uses + and / characters. + * @type {string} + */ +goog.crypt.baseN.BASE_64 = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + + +/** + * URL-safe version of the BASE-64 encoding. + * @type {string} + */ +goog.crypt.baseN.BASE_64_URL_SAFE = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; + + +/** + * Converts a number from one numeric base to another. + * + * The bases are represented as strings, which list allowed digits. Each digit + * should be unique. The bases can either be user defined, or any of + * goog.crypt.baseN.BASE_xxx. + * + * The number is in human-readable format, most significant digit first, and is + * a non-negative integer. Base designators such as $, 0x, d, b or h (at end) + * will be interpreted as digits, so avoid them. Leading zeros will be trimmed. + * + * Note: for huge bases the result may be inaccurate because of overflowing + * 64-bit doubles used by JavaScript for integer calculus. This may happen + * if the product of the number of digits in the input and output bases comes + * close to 10^16, which is VERY unlikely (100M digits in each base), but + * may be possible in the future unicode world. (Unicode 3.2 has less than 100K + * characters. However, it reserves some more, close to 1M.) + * + * @param {string} number The number to convert. + * @param {string} inputBase The numeric base the number is in (all digits). + * @param {string} outputBase Requested numeric base. + * @return {string} The converted number. + */ +goog.crypt.baseN.recodeString = function(number, inputBase, outputBase) { + if (outputBase == '') { + throw Error('Empty output base'); + } + + // Check if number is 0 (special case when we don't want to return ''). + var isZero = true; + for (var i = 0, n = number.length; i < n; i++) { + if (number.charAt(i) != inputBase.charAt(0)) { + isZero = false; + break; + } + } + if (isZero) { + return outputBase.charAt(0); + } + + var numberDigits = goog.crypt.baseN.stringToArray_(number, inputBase); + + var inputBaseSize = inputBase.length; + var outputBaseSize = outputBase.length; + + // result = 0. + var result = []; + + // For all digits of number, starting with the most significant ... + for (var i = numberDigits.length - 1; i >= 0; i--) { + + // result *= number.base. + var carry = 0; + for (var j = 0, n = result.length; j < n; j++) { + var digit = result[j]; + // This may overflow for huge bases. See function comment. + digit = digit * inputBaseSize + carry; + if (digit >= outputBaseSize) { + var remainder = digit % outputBaseSize; + carry = (digit - remainder) / outputBaseSize; + digit = remainder; + } else { + carry = 0; + } + result[j] = digit; + } + while (carry) { + var remainder = carry % outputBaseSize; + result.push(remainder); + carry = (carry - remainder) / outputBaseSize; + } + + // result += number[i]. + carry = numberDigits[i]; + var j = 0; + while (carry) { + if (j >= result.length) { + // Extend result with a leading zero which will be overwritten below. + result.push(0); + } + var digit = result[j]; + digit += carry; + if (digit >= outputBaseSize) { + var remainder = digit % outputBaseSize; + carry = (digit - remainder) / outputBaseSize; + digit = remainder; + } else { + carry = 0; + } + result[j] = digit; + j++; + } + } + + return goog.crypt.baseN.arrayToString_(result, outputBase); +}; + + +/** + * Converts a string representation of a number to an array of digit values. + * + * More precisely, the digit values are indices into the number base, which + * is represented as a string, which can either be user defined or one of the + * BASE_xxx constants. + * + * Throws an Error if the number contains a digit not found in the base. + * + * @param {string} number The string to convert, most significant digit first. + * @param {string} base Digits in the base. + * @return {!Array} Array of digit values, least significant digit + * first. + * @private + */ +goog.crypt.baseN.stringToArray_ = function(number, base) { + var index = {}; + for (var i = 0, n = base.length; i < n; i++) { + index[base.charAt(i)] = i; + } + var result = []; + for (var i = number.length - 1; i >= 0; i--) { + var character = number.charAt(i); + var digit = index[character]; + if (typeof digit == 'undefined') { + throw Error('Number ' + number + + ' contains a character not found in base ' + + base + ', which is ' + character); + } + result.push(digit); + } + return result; +}; + + +/** + * Converts an array representation of a number to a string. + * + * More precisely, the elements of the input array are indices into the base, + * which is represented as a string, which can either be user defined or one of + * the BASE_xxx constants. + * + * Throws an Error if the number contains a digit which is outside the range + * 0 ... base.length - 1. + * + * @param {Array} number Array of digit values, least significant + * first. + * @param {string} base Digits in the base. + * @return {string} Number as a string, most significant digit first. + * @private + */ +goog.crypt.baseN.arrayToString_ = function(number, base) { + var n = number.length; + var chars = []; + var baseSize = base.length; + for (var i = n - 1; i >= 0; i--) { + var digit = number[i]; + if (digit >= baseSize || digit < 0) { + throw Error('Number ' + number + ' contains an invalid digit: ' + digit); + } + chars.push(base.charAt(digit)); + } + return chars.join(''); +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/basen_test.html b/mapbox-gl/closure-library/closure/goog/crypt/basen_test.html new file mode 100644 index 0000000000..c29a7cd435 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/basen_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.baseN + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/basen_test.js b/mapbox-gl/closure-library/closure/goog/crypt/basen_test.js new file mode 100644 index 0000000000..bf6339787f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/basen_test.js @@ -0,0 +1,165 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tests for arbitrary base conversion library baseconversion.js. + */ + +goog.provide('goog.crypt.baseNTest'); +goog.setTestOnly('goog.crypt.baseNTest'); + +goog.require('goog.crypt.baseN'); +goog.require('goog.testing.jsunit'); + +function testDecToHex() { + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '0', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, '0'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '9', + goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL, '9'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '13', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, 'd'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '255', + goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL, 'FF'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, + '53425987345897', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, + '309734ff5de9'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, + '987080888', + goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL, + '3AD5A8B8'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, + '009341587237', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, + '22ccd4f25'); +} + +function testBinToDec() { + verifyConversion( + goog.crypt.baseN.BASE_BINARY, + '11101010101000100010010000010010010000111101000100110111000000100001' + + '01100100111110110010000010110100111101000010010100001011111011111100' + + '00000010000010000101010101000000000101100000000100011111011101111001' + + '10000001000000000100101110001001001101101001101111010101111100010001' + + '11011100000110111000000100111011100100010010011001111011001111001011' + + '10001000101111001010011101101100110110011110010000011100101011110010' + + '11010001001111110011000000001001011011111011010000110011010000010111' + + '10111100000001100010111100000100000000110001011101011110100000011010' + + '0110000100011111', + goog.crypt.baseN.BASE_DECIMAL, + '34589745906769047354795784390596748934723904739085568907689045723489' + + '05745789789078907890789023447892365623589745678902348976234598723459' + + '087523496723486089723459078349087'); +} + +function testDecToBin() { + verifyConversion( + goog.crypt.baseN.BASE_DECIMAL, + '00342589674590347859734908573490568347534805468907960579056785605496' + + '83475873465859072390486756098742380573908572390463805745656623475234' + + '82345670247851902784123897349486238502378940637925807378946358964328' + + '57906148572346857346409823758034763928401296023947234784623765456367' + + '764623627623574', + goog.crypt.baseN.BASE_BINARY, + '10010011011100101010001111100111001100110000110111111110010110101000' + + '01010110110010000111000001100110100101010000101001100001011000101111' + + '01011101111100101101010010000111011110011110010101111001110010100100' + + '10111110000101111011010000000111111011110010011110101011100101000001' + + '00011000101010011001101000011101001010001101011110101001011011100101' + + '11100000101000010010101001011001100100101110111101010000011010001010' + + '01011100100111110001100111100100011001001001100011011100100111011111' + + '01000100101001000100110001011010010000011010111101111111111111110100' + + '01100101001111001111100110101000001100100000111111100101110010111011' + + '10110110001100100011101010110110100001001000101011001001100011010110' + + '10110100000110000110010111110100000100110110010010010101111001001111' + + '11100100000010101111110100011010011101011010001101110011100110111111' + + '11000100001111010000000101011011000010010000000100111111010110111100' + + '00101111010011011010011010010001000101100001111001110010010110'); +} + +function test7To9() { + verifyConversion( + '0123456', // Base 7. + '60625646056660665666066534602566346056634560665606666656465634265434' + + '66563465664566346406366534664656650660665623456663456654360665', + '012345678', // Base 9. + '11451222686557606458341381287142358175337801548087003804852781764284' + + '273762357630423116743334671762638240652740158536'); +} + +function testZeros() { + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '0', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, '0'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '000', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, '0'); + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '0000007', + goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL, '7'); +} + +function testArbitraryBases() { + verifyConversion('X9(', // Base 3. + '9(XX((9X(XX9(9X9(X9(', + 'a:*o9', // Base 5. + ':oa**:9o9**9oo'); +} + +function testEmptyBases() { + var e = assertThrows(function() { + goog.crypt.baseN.recodeString('1230', '', '0123'); + }); + assertEquals('Exception message', 'Number 1230 contains a character ' + + 'not found in base , which is 0', e.message); + + e = assertThrows(function() { + goog.crypt.baseN.recodeString('1230', '0123', ''); + }); + assertEquals('Exception message', 'Empty output base', e.message); +} + +function testInvalidDigits() { + var e = assertThrows(function() { + goog.crypt.baseN.recodeString('123x456', '01234567', '01234567'); + }); + assertEquals('Exception message', 'Number 123x456 contains a character ' + + 'not found in base 01234567, which is x', e.message); +} + +function makeHugeBase() { + // Number of digits in the base. + // Tests break if this is set to 200'000. The reason for that is + // String.fromCharCode(196609) == String.fromCharCode(1). + var baseSize = 20000; + var tab = []; + for (var i = 0; i < baseSize; i++) { + tab.push(String.fromCharCode(i)); + } + return tab.join(''); +} + +function testHugeInputBase() { + verifyConversion(makeHugeBase(), String.fromCharCode(12345), + goog.crypt.baseN.BASE_DECIMAL, '12345'); +} + +function testHugeOutputBase() { + verifyConversion(goog.crypt.baseN.BASE_DECIMAL, '12345', + makeHugeBase(), String.fromCharCode(12345)); +} + +function verifyConversion(inputBase, inputNumber, outputBase, outputNumber) { + assertEquals(outputNumber, + goog.crypt.baseN.recodeString(inputNumber, + inputBase, + outputBase)); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/blobhasher.js b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher.js new file mode 100644 index 0000000000..2f2613696c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher.js @@ -0,0 +1,283 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Asynchronous hash computer for the Blob interface. + * + * The Blob interface, part of the HTML5 File API, is supported on Chrome 7+, + * Firefox 4.0 and Opera 11. No Blob interface implementation is expected on + * Internet Explorer 10. Chrome 11, Firefox 5.0 and the subsequent release of + * Opera are supposed to use vendor prefixes due to evolving API, see + * http://dev.w3.org/2006/webapi/FileAPI/ for details. + * + * This implementation currently uses upcoming Chrome and Firefox prefixes, + * plus the original Blob.slice specification, as implemented on Chrome 10 + * and Firefox 4.0. + * + */ + +goog.provide('goog.crypt.BlobHasher'); +goog.provide('goog.crypt.BlobHasher.EventType'); + +goog.require('goog.asserts'); +goog.require('goog.events.EventTarget'); +goog.require('goog.fs'); +goog.require('goog.log'); + + + +/** + * Construct the hash computer. + * + * @param {!goog.crypt.Hash} hashFn The hash function to use. + * @param {number=} opt_blockSize Processing block size. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.crypt.BlobHasher = function(hashFn, opt_blockSize) { + goog.crypt.BlobHasher.base(this, 'constructor'); + + /** + * The actual hash function. + * @type {!goog.crypt.Hash} + * @private + */ + this.hashFn_ = hashFn; + + /** + * The blob being processed or null if no blob is being processed. + * @type {Blob} + * @private + */ + this.blob_ = null; + + /** + * Computed hash value. + * @type {Array} + * @private + */ + this.hashVal_ = null; + + /** + * Number of bytes already processed. + * @type {number} + * @private + */ + this.bytesProcessed_ = 0; + + /** + * The number of bytes to hash or Infinity for no limit. + * @type {number} + * @private + */ + this.hashingLimit_ = Infinity; + + /** + * Processing block size. + * @type {number} + * @private + */ + this.blockSize_ = opt_blockSize || 5000000; + + /** + * File reader object. Will be null if no chunk is currently being read. + * @type {FileReader} + * @private + */ + this.fileReader_ = null; + + /** + * The logger used by this object. + * @type {goog.log.Logger} + * @private + */ + this.logger_ = goog.log.getLogger('goog.crypt.BlobHasher'); +}; +goog.inherits(goog.crypt.BlobHasher, goog.events.EventTarget); + + +/** + * Event names for hash computation events + * @enum {string} + */ +goog.crypt.BlobHasher.EventType = { + STARTED: 'started', + PROGRESS: 'progress', + THROTTLED: 'throttled', + COMPLETE: 'complete', + ABORT: 'abort', + ERROR: 'error' +}; + + +/** + * Start the hash computation. + * @param {!Blob} blob The blob of data to compute the hash for. + */ +goog.crypt.BlobHasher.prototype.hash = function(blob) { + this.abort(); + this.hashFn_.reset(); + this.blob_ = blob; + this.hashVal_ = null; + this.bytesProcessed_ = 0; + this.dispatchEvent(goog.crypt.BlobHasher.EventType.STARTED); + + this.processNextBlock_(); +}; + + +/** + * Sets the maximum number of bytes to hash or Infinity for no limit. Can be + * called before hash() to throttle the hash computation. The hash computation + * can then be continued by repeatedly calling setHashingLimit() with greater + * byte offsets. This is useful if you don't need the hash until some time in + * the future, for example when uploading a file and you don't need the hash + * until the transfer is complete. + * @param {number} byteOffset The byte offset to compute the hash up to. + * Should be a non-negative integer or Infinity for no limit. Negative + * values are not allowed. + */ +goog.crypt.BlobHasher.prototype.setHashingLimit = function(byteOffset) { + goog.asserts.assert(byteOffset >= 0, 'Hashing limit must be non-negative.'); + this.hashingLimit_ = byteOffset; + + // Resume processing if a blob is currently being hashed, but no block read + // is currently in progress. + if (this.blob_ && !this.fileReader_) { + this.processNextBlock_(); + } +}; + + +/** + * Abort hash computation. + */ +goog.crypt.BlobHasher.prototype.abort = function() { + if (this.fileReader_) { + this.fileReader_.abort(); + this.fileReader_ = null; + } + + if (this.blob_) { + this.blob_ = null; + this.dispatchEvent(goog.crypt.BlobHasher.EventType.ABORT); + } +}; + + +/** + * @return {number} Number of bytes processed so far. + */ +goog.crypt.BlobHasher.prototype.getBytesProcessed = function() { + return this.bytesProcessed_; +}; + + +/** + * @return {Array} The computed hash value or null if not ready. + */ +goog.crypt.BlobHasher.prototype.getHash = function() { + return this.hashVal_; +}; + + +/** + * Helper function setting up the processing for the next block, or finalizing + * the computation if all blocks were processed. + * @private + */ +goog.crypt.BlobHasher.prototype.processNextBlock_ = function() { + goog.asserts.assert(this.blob_, 'A hash computation must be in progress.'); + + if (this.bytesProcessed_ < this.blob_.size) { + + if (this.hashingLimit_ <= this.bytesProcessed_) { + // Throttle limit reached. Wait until we are allowed to hash more bytes. + this.dispatchEvent(goog.crypt.BlobHasher.EventType.THROTTLED); + return; + } + + // We have to reset the FileReader every time, otherwise it fails on + // Chrome, including the latest Chrome 12 beta. + // http://code.google.com/p/chromium/issues/detail?id=82346 + this.fileReader_ = new FileReader(); + this.fileReader_.onload = goog.bind(this.onLoad_, this); + this.fileReader_.onerror = goog.bind(this.onError_, this); + + var endOffset = Math.min(this.hashingLimit_, this.blob_.size); + var size = Math.min(endOffset - this.bytesProcessed_, this.blockSize_); + var chunk = goog.fs.sliceBlob(this.blob_, this.bytesProcessed_, + this.bytesProcessed_ + size); + if (!chunk || chunk.size != size) { + goog.log.error(this.logger_, 'Failed slicing the blob'); + this.onError_(); + return; + } + + if (this.fileReader_.readAsArrayBuffer) { + this.fileReader_.readAsArrayBuffer(chunk); + } else if (this.fileReader_.readAsBinaryString) { + this.fileReader_.readAsBinaryString(chunk); + } else { + goog.log.error(this.logger_, 'Failed calling the chunk reader'); + this.onError_(); + } + } else { + this.hashVal_ = this.hashFn_.digest(); + this.blob_ = null; + this.dispatchEvent(goog.crypt.BlobHasher.EventType.COMPLETE); + } +}; + + +/** + * Handle processing block loaded. + * @private + */ +goog.crypt.BlobHasher.prototype.onLoad_ = function() { + goog.log.info(this.logger_, 'Successfully loaded a chunk'); + + var array = null; + if (this.fileReader_.result instanceof Array || + goog.isString(this.fileReader_.result)) { + array = this.fileReader_.result; + } else if (goog.global['ArrayBuffer'] && goog.global['Uint8Array'] && + this.fileReader_.result instanceof ArrayBuffer) { + array = new Uint8Array(this.fileReader_.result); + } + if (!array) { + goog.log.error(this.logger_, 'Failed reading the chunk'); + this.onError_(); + return; + } + + this.hashFn_.update(array); + this.bytesProcessed_ += array.length; + this.fileReader_ = null; + this.dispatchEvent(goog.crypt.BlobHasher.EventType.PROGRESS); + + this.processNextBlock_(); +}; + + +/** + * Handles error. + * @private + */ +goog.crypt.BlobHasher.prototype.onError_ = function() { + this.fileReader_ = null; + this.blob_ = null; + this.dispatchEvent(goog.crypt.BlobHasher.EventType.ERROR); +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.html b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.html new file mode 100644 index 0000000000..d680f81adb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.html @@ -0,0 +1,23 @@ + + + + + + + Closure Unit Tests - goog.crypt.BlobHasher + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.js b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.js new file mode 100644 index 0000000000..a9641c50b3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/blobhasher_test.js @@ -0,0 +1,382 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.BlobHasherTest'); +goog.setTestOnly('goog.crypt.BlobHasherTest'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.BlobHasher'); +goog.require('goog.crypt.Md5'); +goog.require('goog.events'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); + +// A browser-independent mock of goog.fs.sliceBlob. The actual implementation +// calls the underlying slice method differently based on browser version. +// This mock does not support negative opt_end. +var fsSliceBlobMock = function(blob, start, opt_end) { + if (!goog.isNumber(opt_end)) { + opt_end = blob.size; + } + return blob.slice(start, opt_end); +}; + +// Mock out the Blob using a string. +BlobMock = function(string) { + this.data = string; + this.size = this.data.length; +}; + +BlobMock.prototype.slice = function(start, end) { + return new BlobMock(this.data.substr(start, end - start)); +}; + + +// Mock out the FileReader to have control over the flow. +FileReaderMock = function() { + this.array_ = []; + this.result = null; + this.readyState = this.EMPTY; + + this.onload = null; + this.onabort = null; + this.onerror = null; +}; + +FileReaderMock.prototype.EMPTY = 0; +FileReaderMock.prototype.LOADING = 1; +FileReaderMock.prototype.DONE = 2; + +FileReaderMock.prototype.mockLoad = function() { + this.readyState = this.DONE; + this.result = this.array_; + if (this.onload) { + this.onload.call(); + } +}; + +FileReaderMock.prototype.abort = function() { + this.readyState = this.DONE; + if (this.onabort) { + this.onabort.call(); + } +}; + +FileReaderMock.prototype.mockError = function() { + this.readyState = this.DONE; + if (this.onerror) { + this.onerror.call(); + } +}; + +FileReaderMock.prototype.readAsArrayBuffer = function(blobMock) { + this.readyState = this.LOADING; + this.array_ = []; + for (var i = 0; i < blobMock.size; ++i) { + this.array_[i] = blobMock.data.charCodeAt(i); + } +}; + +FileReaderMock.prototype.isLoading = function() { + return this.readyState == this.LOADING; +}; + +var stubs = new goog.testing.PropertyReplacer(); +function setUp() { + stubs.set(goog.global, 'FileReader', FileReaderMock); + stubs.set(goog.fs, 'sliceBlob', fsSliceBlobMock); +} + +function tearDown() { + stubs.reset(); +} + + +/** + * Makes the blobHasher read chunks from the blob and hash it. The number of + * reads shall not exceed a pre-determined number (typically blob size / chunk + * size) for computing hash. This function fails fast (after maxReads is + * reached), assuming that the hasher failed to generate hashes. This prevents + * the test suite from going into infinite loop. + * @param {!goog.crypt.BlobHasher} blobHasher Hasher in action. + * @param {number} maxReads Max number of read attempts. + */ +function readFromBlob(blobHasher, maxReads) { + var counter = 0; + while (blobHasher.fileReader_ && blobHasher.fileReader_.isLoading() && + counter <= maxReads) { + blobHasher.fileReader_.mockLoad(); + counter++; + } + assertTrue(counter <= maxReads); + return counter; +} + +function testBasicOperations() { + if (!window.Blob) { + return; + } + + // Test hashing with one chunk. + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn); + var blob = new BlobMock('The quick brown fox jumps over the lazy dog'); + blobHasher.hash(blob); + readFromBlob(blobHasher, 1); + assertEquals('9e107d9d372bb6826bd81d3542a419d6', + goog.crypt.byteArrayToHex(blobHasher.getHash())); + + // Test hashing with multiple chunks. + blobHasher = new goog.crypt.BlobHasher(hashFn, 7); + blobHasher.hash(blob); + readFromBlob(blobHasher, Math.ceil(blob.size / 7)); + assertEquals('9e107d9d372bb6826bd81d3542a419d6', + goog.crypt.byteArrayToHex(blobHasher.getHash())); + + // Test hashing with no chunks. + blob = new BlobMock(''); + blobHasher.hash(blob); + readFromBlob(blobHasher, 1); + assertEquals('d41d8cd98f00b204e9800998ecf8427e', + goog.crypt.byteArrayToHex(blobHasher.getHash())); + +} + +function testNormalFlow() { + if (!window.Blob) { + return; + } + + // Test the flow with one chunk. + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn, 13); + var blob = new BlobMock('short'); + var startedEvents = 0; + var progressEvents = 0; + var completeEvents = 0; + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.STARTED, + function() { ++startedEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.PROGRESS, + function() { ++progressEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE, + function() { ++completeEvents; }); + blobHasher.hash(blob); + assertEquals(1, startedEvents); + assertEquals(0, progressEvents); + assertEquals(0, completeEvents); + readFromBlob(blobHasher, 1); + assertEquals(1, startedEvents); + assertEquals(1, progressEvents); + assertEquals(1, completeEvents); + + // Test the flow with multiple chunks. + blob = new BlobMock('The quick brown fox jumps over the lazy dog'); + startedEvents = 0; + progressEvents = 0; + completeEvents = 0; + var progressLoops = 0; + blobHasher.hash(blob); + assertEquals(1, startedEvents); + assertEquals(0, progressEvents); + assertEquals(0, completeEvents); + progressLoops = readFromBlob(blobHasher, Math.ceil(blob.size / 13)); + assertEquals(1, startedEvents); + assertEquals(progressLoops, progressEvents); + assertEquals(1, completeEvents); +} + +function testAbortsAndErrors() { + if (!window.Blob) { + return; + } + + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn, 13); + var blob = new BlobMock('The quick brown fox jumps over the lazy dog'); + var abortEvents = 0; + var errorEvents = 0; + var completeEvents = 0; + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.ABORT, + function() { ++abortEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.ERROR, + function() { ++errorEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE, + function() { ++completeEvents; }); + + // Immediate abort. + blobHasher.hash(blob); + assertEquals(0, abortEvents); + assertEquals(0, errorEvents); + assertEquals(0, completeEvents); + blobHasher.abort(); + blobHasher.abort(); + assertEquals(1, abortEvents); + assertEquals(0, errorEvents); + assertEquals(0, completeEvents); + abortEvents = 0; + + // Delayed abort. + blobHasher.hash(blob); + blobHasher.fileReader_.mockLoad(); + assertEquals(0, abortEvents); + assertEquals(0, errorEvents); + assertEquals(0, completeEvents); + blobHasher.abort(); + blobHasher.abort(); + assertEquals(1, abortEvents); + assertEquals(0, errorEvents); + assertEquals(0, completeEvents); + abortEvents = 0; + + // Immediate error. + blobHasher.hash(blob); + blobHasher.fileReader_.mockError(); + assertEquals(0, abortEvents); + assertEquals(1, errorEvents); + assertEquals(0, completeEvents); + errorEvents = 0; + + // Delayed error. + blobHasher.hash(blob); + blobHasher.fileReader_.mockLoad(); + blobHasher.fileReader_.mockError(); + assertEquals(0, abortEvents); + assertEquals(1, errorEvents); + assertEquals(0, completeEvents); + abortEvents = 0; + +} + +function testBasicThrottling() { + if (!window.Blob) { + return; + } + + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn, 5); + var blob = new BlobMock('The quick brown fox jumps over the lazy dog'); + var throttledEvents = 0; + var completeEvents = 0; + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED, + function() { ++throttledEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE, + function() { ++completeEvents; }); + + // Start a throttled hash. No chunks should be processed yet. + blobHasher.setHashingLimit(0); + assertEquals(0, throttledEvents); + blobHasher.hash(blob); + assertEquals(1, throttledEvents); + assertEquals(0, blobHasher.getBytesProcessed()); + assertNull(blobHasher.fileReader_); + + // One chunk should be processed. + blobHasher.setHashingLimit(4); + assertEquals(1, throttledEvents); + assertEquals(1, readFromBlob(blobHasher, 1)); + assertEquals(2, throttledEvents); + assertEquals(4, blobHasher.getBytesProcessed()); + + // One more chunk should be processed. + blobHasher.setHashingLimit(5); + assertEquals(2, throttledEvents); + assertEquals(1, readFromBlob(blobHasher, 1)); + assertEquals(3, throttledEvents); + assertEquals(5, blobHasher.getBytesProcessed()); + + // Two more chunks should be processed. + blobHasher.setHashingLimit(15); + assertEquals(3, throttledEvents); + assertEquals(2, readFromBlob(blobHasher, 2)); + assertEquals(4, throttledEvents); + assertEquals(15, blobHasher.getBytesProcessed()); + + // The entire blob should be processed. + blobHasher.setHashingLimit(Infinity); + var expectedChunks = Math.ceil(blob.size / 5) - 3; + assertEquals(expectedChunks, readFromBlob(blobHasher, expectedChunks)); + assertEquals(4, throttledEvents); + assertEquals(1, completeEvents); + assertEquals('9e107d9d372bb6826bd81d3542a419d6', + goog.crypt.byteArrayToHex(blobHasher.getHash())); +} + +function testLengthZeroThrottling() { + if (!window.Blob) { + return; + } + + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn); + var throttledEvents = 0; + var completeEvents = 0; + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED, + function() { ++throttledEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE, + function() { ++completeEvents; }); + + // Test throttling with length 0 blob. + var blob = new BlobMock(''); + blobHasher.setHashingLimit(0); + blobHasher.hash(blob); + assertEquals(0, throttledEvents); + assertEquals(1, completeEvents); + assertEquals('d41d8cd98f00b204e9800998ecf8427e', + goog.crypt.byteArrayToHex(blobHasher.getHash())); +} + +function testAbortsAndErrorsWhileThrottling() { + if (!window.Blob) { + return; + } + + var hashFn = new goog.crypt.Md5(); + var blobHasher = new goog.crypt.BlobHasher(hashFn, 5); + var blob = new BlobMock('The quick brown fox jumps over the lazy dog'); + var abortEvents = 0; + var errorEvents = 0; + var throttledEvents = 0; + var completeEvents = 0; + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.ABORT, + function() { ++abortEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.ERROR, + function() { ++errorEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.THROTTLED, + function() { ++throttledEvents; }); + goog.events.listen(blobHasher, goog.crypt.BlobHasher.EventType.COMPLETE, + function() { ++completeEvents; }); + + // Test that processing cannot be continued after abort. + blobHasher.setHashingLimit(0); + blobHasher.hash(blob); + assertEquals(1, throttledEvents); + blobHasher.abort(); + assertEquals(1, abortEvents); + blobHasher.setHashingLimit(10); + assertNull(blobHasher.fileReader_); + assertEquals(1, throttledEvents); + assertEquals(0, completeEvents); + assertNull(blobHasher.getHash()); + + // Test that processing cannot be continued after error. + blobHasher.hash(blob); + assertEquals(1, throttledEvents); + blobHasher.fileReader_.mockError(); + assertEquals(1, errorEvents); + blobHasher.setHashingLimit(100); + assertNull(blobHasher.fileReader_); + assertEquals(1, throttledEvents); + assertEquals(0, completeEvents); + assertNull(blobHasher.getHash()); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/blockcipher.js b/mapbox-gl/closure-library/closure/goog/crypt/blockcipher.js new file mode 100644 index 0000000000..be766f0e00 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/blockcipher.js @@ -0,0 +1,52 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Interface definition of a block cipher. A block cipher is a + * pair of algorithms that implement encryption and decryption of input bytes. + * + * @see http://en.wikipedia.org/wiki/Block_cipher + * + * @author nnaze@google.com (Nathan Naze) + */ + +goog.provide('goog.crypt.BlockCipher'); + + + +/** + * Interface definition for a block cipher. + * @interface + */ +goog.crypt.BlockCipher = function() {}; + + +/** + * Encrypt a plaintext block. The implementation may expect (and assert) + * a particular block length. + * @param {!Array} input Plaintext array of input bytes. + * @return {!Array} Encrypted ciphertext array of bytes. Should be the + * same length as input. + */ +goog.crypt.BlockCipher.prototype.encrypt; + + +/** + * Decrypt a plaintext block. The implementation may expect (and assert) + * a particular block length. + * @param {!Array} input Ciphertext. Array of input bytes. + * @return {!Array} Decrypted plaintext array of bytes. Should be the + * same length as input. + */ +goog.crypt.BlockCipher.prototype.decrypt; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.html new file mode 100644 index 0000000000..cf1be84ef6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.html @@ -0,0 +1,25 @@ + + + + + + Closure Performance Tests - byteArrayToString + + + + +

Closure Performance Tests - byteArrayToString

+
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.js b/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.js new file mode 100644 index 0000000000..3e5e86a129 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/bytestring_perf.js @@ -0,0 +1,124 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Performance test for different implementations of + * byteArrayToString. + */ + + +goog.provide('goog.crypt.byteArrayToStringPerf'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.testing.PerformanceTable'); + +goog.setTestOnly('goog.crypt.byteArrayToStringPerf'); + + +var table = new goog.testing.PerformanceTable( + goog.dom.getElement('perfTable')); + + +var BYTES_LENGTH = Math.pow(2, 20); +var CHUNK_SIZE = 8192; + +function getBytes() { + var bytes = []; + for (var i = 0; i < BYTES_LENGTH; i++) { + bytes.push('A'.charCodeAt(0)); + } + return bytes; +} + +function copyAndSpliceByteArray(bytes) { + + // Copy the passed byte array since we're going to destroy it. + var remainingBytes = goog.array.clone(bytes); + var strings = []; + + // Convert each chunk to a string. + while (remainingBytes.length) { + var chunk = goog.array.splice(remainingBytes, 0, CHUNK_SIZE); + strings.push(String.fromCharCode.apply(null, chunk)); + } + return strings.join(''); +} + +function sliceByteArrayConcat(bytes) { + var str = ''; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + str += String.fromCharCode.apply(null, chunk); + } + return str; +} + + +function sliceByteArrayJoin(bytes) { + var strings = []; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + strings.push(String.fromCharCode.apply(null, chunk)); + } + return strings.join(''); +} + +function mapByteArray(bytes) { + var strings = goog.array.map(bytes, String.fromCharCode); + return strings.join(''); +} + +function forLoopByteArrayConcat(bytes) { + var str = ''; + for (var i = 0; i < bytes.length; i++) { + str += String.fromCharCode(bytes[i]); + } + return str; +} + +function forLoopByteArrayJoin(bytes) { + var strs = []; + for (var i = 0; i < bytes.length; i++) { + strs.push(String.fromCharCode(bytes[i])); + } + return strs.join(''); +} + + +function run() { + var bytes = getBytes(); + table.run(goog.partial(copyAndSpliceByteArray, getBytes()), + 'Copy array and splice out chunks.'); + + table.run(goog.partial(sliceByteArrayConcat, getBytes()), + 'Slice out copies of the byte array, concatenating results'); + + table.run(goog.partial(sliceByteArrayJoin, getBytes()), + 'Slice out copies of the byte array, joining results'); + + table.run(goog.partial(forLoopByteArrayConcat, getBytes()), + 'Use for loop with concat.'); + + table.run(goog.partial(forLoopByteArrayJoin, getBytes()), + 'Use for loop with join.'); + + // Purposefully commented out. This ends up being tremendously expensive. + // table.run(goog.partial(mapByteArray, getBytes()), + // 'Use goog.array.map and fromCharCode.'); + +} + +run(); + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/cbc.js b/mapbox-gl/closure-library/closure/goog/crypt/cbc.js new file mode 100644 index 0000000000..68a265615c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/cbc.js @@ -0,0 +1,153 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of CBC mode for block ciphers. See + * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation + * #Cipher-block_chaining_.28CBC.29. for description. + * + * @author nnaze@google.com (Nathan Naze) + */ + +goog.provide('goog.crypt.Cbc'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.crypt'); + + + +/** + * Implements the CBC mode for block ciphers. See + * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation + * #Cipher-block_chaining_.28CBC.29 + * + * @param {!goog.crypt.BlockCipher} cipher The block cipher to use. + * @param {number=} opt_blockSize The block size of the cipher in bytes. + * Defaults to 16 bytes. + * @constructor + * @final + * @struct + */ +goog.crypt.Cbc = function(cipher, opt_blockSize) { + + /** + * Block cipher. + * @type {!goog.crypt.BlockCipher} + * @private + */ + this.cipher_ = cipher; + + /** + * Block size in bytes. + * @type {number} + * @private + */ + this.blockSize_ = opt_blockSize || 16; +}; + + +/** + * Encrypt a message. + * + * @param {!Array} plainText Message to encrypt. An array of bytes. + * The length should be a multiple of the block size. + * @param {!Array} initialVector Initial vector for the CBC mode. + * An array of bytes with the same length as the block size. + * @return {!Array} Encrypted message. + */ +goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) { + + goog.asserts.assert( + plainText.length % this.blockSize_ == 0, + 'Data\'s length must be multiple of block size.'); + + goog.asserts.assert( + initialVector.length == this.blockSize_, + 'Initial vector must be size of one block.'); + + // Implementation of + // http://en.wikipedia.org/wiki/File:Cbc_encryption.png + + var cipherText = []; + var vector = initialVector; + + // Generate each block of the encrypted cypher text. + for (var blockStartIndex = 0; + blockStartIndex < plainText.length; + blockStartIndex += this.blockSize_) { + + // Takes one block from the input message. + var plainTextBlock = goog.array.slice( + plainText, + blockStartIndex, + blockStartIndex + this.blockSize_); + + var input = goog.crypt.xorByteArray(plainTextBlock, vector); + var resultBlock = this.cipher_.encrypt(input); + + goog.array.extend(cipherText, resultBlock); + vector = resultBlock; + } + + return cipherText; +}; + + +/** + * Decrypt a message. + * + * @param {!Array} cipherText Message to decrypt. An array of bytes. + * The length should be a multiple of the block size. + * @param {!Array} initialVector Initial vector for the CBC mode. + * An array of bytes with the same length as the block size. + * @return {!Array} Decrypted message. + */ +goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) { + + goog.asserts.assert( + cipherText.length % this.blockSize_ == 0, + 'Data\'s length must be multiple of block size.'); + + goog.asserts.assert( + initialVector.length == this.blockSize_, + 'Initial vector must be size of one block.'); + + // Implementation of + // http://en.wikipedia.org/wiki/File:Cbc_decryption.png + + var plainText = []; + var blockStartIndex = 0; + var vector = initialVector; + + // Generate each block of the decrypted plain text. + while (blockStartIndex < cipherText.length) { + + // Takes one block. + var cipherTextBlock = goog.array.slice( + cipherText, + blockStartIndex, + blockStartIndex + this.blockSize_); + + var resultBlock = this.cipher_.decrypt(cipherTextBlock); + var plainTextBlock = goog.crypt.xorByteArray(vector, resultBlock); + + goog.array.extend(plainText, plainTextBlock); + vector = cipherTextBlock; + + blockStartIndex += this.blockSize_; + } + + return plainText; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.html b/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.html new file mode 100644 index 0000000000..f722ff4d78 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.html @@ -0,0 +1,18 @@ + + + + +Closure Unit Tests - goog.crypt.Cbc + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.js b/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.js new file mode 100644 index 0000000000..ef56a1ee06 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/cbc_test.js @@ -0,0 +1,100 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Unit tests for CBC mode for block ciphers. + * + * @author nnaze@google.com (Nathan Naze) + */ + +/** @suppress {extraProvide} */ +goog.provide('goog.crypt.CbcTest'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Aes'); +goog.require('goog.crypt.Cbc'); +goog.require('goog.testing.jsunit'); + +goog.setTestOnly('goog.crypt.CbcTest'); + +function stringToBytes(s) { + var bytes = new Array(s.length); + for (var i = 0; i < s.length; ++i) + bytes[i] = s.charCodeAt(i) & 255; + return bytes; +} + +function runCbcAesTest(keyBytes, initialVectorBytes, plainTextBytes, + cipherTextBytes) { + + var aes = new goog.crypt.Aes(keyBytes); + var cbc = new goog.crypt.Cbc(aes); + + var encryptedBytes = cbc.encrypt(plainTextBytes, initialVectorBytes); + assertEquals('Encrypted bytes should match cipher text.', + goog.crypt.byteArrayToHex(cipherTextBytes), + goog.crypt.byteArrayToHex(encryptedBytes)); + + var decryptedBytes = cbc.decrypt(cipherTextBytes, initialVectorBytes); + assertEquals('Decrypted bytes should match plain text.', + goog.crypt.byteArrayToHex(plainTextBytes), + goog.crypt.byteArrayToHex(decryptedBytes)); +} + +function testAesCbcCipherAlgorithm() { + // Test values from http://www.ietf.org/rfc/rfc3602.txt + + // Case #1 + runCbcAesTest( + goog.crypt.hexToByteArray('06a9214036b8a15b512e03d534120006'), + goog.crypt.hexToByteArray('3dafba429d9eb430b422da802c9fac41'), + stringToBytes('Single block msg'), + goog.crypt.hexToByteArray('e353779c1079aeb82708942dbe77181a')); + + // Case #2 + runCbcAesTest( + goog.crypt.hexToByteArray('c286696d887c9aa0611bbb3e2025a45a'), + goog.crypt.hexToByteArray('562e17996d093d28ddb3ba695a2e6f58'), + goog.crypt.hexToByteArray( + '000102030405060708090a0b0c0d0e0f' + + '101112131415161718191a1b1c1d1e1f'), + goog.crypt.hexToByteArray( + 'd296cd94c2cccf8a3a863028b5e1dc0a' + + '7586602d253cfff91b8266bea6d61ab1')); + + // Case #3 + runCbcAesTest( + goog.crypt.hexToByteArray('6c3ea0477630ce21a2ce334aa746c2cd'), + goog.crypt.hexToByteArray('c782dc4c098c66cbd9cd27d825682c81'), + stringToBytes('This is a 48-byte message (exactly 3 AES blocks)'), + goog.crypt.hexToByteArray( + 'd0a02b3836451753d493665d33f0e886' + + '2dea54cdb293abc7506939276772f8d5' + + '021c19216bad525c8579695d83ba2684')); + + // Case #4 + runCbcAesTest( + goog.crypt.hexToByteArray('56e47a38c5598974bc46903dba290349'), + goog.crypt.hexToByteArray('8ce82eefbea0da3c44699ed7db51b7d9'), + goog.crypt.hexToByteArray( + 'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf' + + 'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf' + + 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf' + + 'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'), + goog.crypt.hexToByteArray( + 'c30e32ffedc0774e6aff6af0869f71aa' + + '0f3af07a9a31a9c684db207eb0ef8e4e' + + '35907aa632c3ffdf868bb7b29d3d46ad' + + '83ce9f9a102ee99d49a53e87f4c3da55')); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/crypt.js b/mapbox-gl/closure-library/closure/goog/crypt/crypt.js new file mode 100644 index 0000000000..e8f722aeeb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/crypt.js @@ -0,0 +1,173 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Namespace with crypto related helper functions. + */ + +goog.provide('goog.crypt'); + +goog.require('goog.array'); +goog.require('goog.asserts'); + + +/** + * Turns a string into an array of bytes; a "byte" being a JS number in the + * range 0-255. + * @param {string} str String value to arrify. + * @return {!Array} Array of numbers corresponding to the + * UCS character codes of each character in str. + */ +goog.crypt.stringToByteArray = function(str) { + var output = [], p = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + while (c > 0xff) { + output[p++] = c & 0xff; + c >>= 8; + } + output[p++] = c; + } + return output; +}; + + +/** + * Turns an array of numbers into the string given by the concatenation of the + * characters to which the numbers correspond. + * @param {Array} bytes Array of numbers representing characters. + * @return {string} Stringification of the array. + */ +goog.crypt.byteArrayToString = function(bytes) { + var CHUNK_SIZE = 8192; + + // Special-case the simple case for speed's sake. + if (bytes.length < CHUNK_SIZE) { + return String.fromCharCode.apply(null, bytes); + } + + // The remaining logic splits conversion by chunks since + // Function#apply() has a maximum parameter count. + // See discussion: http://goo.gl/LrWmZ9 + + var str = ''; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + str += String.fromCharCode.apply(null, chunk); + } + return str; +}; + + +/** + * Turns an array of numbers into the hex string given by the concatenation of + * the hex values to which the numbers correspond. + * @param {Uint8Array|Array} array Array of numbers representing + * characters. + * @return {string} Hex string. + */ +goog.crypt.byteArrayToHex = function(array) { + return goog.array.map(array, function(numByte) { + var hexByte = numByte.toString(16); + return hexByte.length > 1 ? hexByte : '0' + hexByte; + }).join(''); +}; + + +/** + * Converts a hex string into an integer array. + * @param {string} hexString Hex string of 16-bit integers (two characters + * per integer). + * @return {!Array} Array of {0,255} integers for the given string. + */ +goog.crypt.hexToByteArray = function(hexString) { + goog.asserts.assert(hexString.length % 2 == 0, + 'Key string length must be multiple of 2'); + var arr = []; + for (var i = 0; i < hexString.length; i += 2) { + arr.push(parseInt(hexString.substring(i, i + 2), 16)); + } + return arr; +}; + + +/** + * Converts a JS string to a UTF-8 "byte" array. + * @param {string} str 16-bit unicode string. + * @return {!Array} UTF-8 byte array. + */ +goog.crypt.stringToUtf8ByteArray = function(str) { + // TODO(user): Use native implementations if/when available + str = str.replace(/\r\n/g, '\n'); + var out = [], p = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c < 128) { + out[p++] = c; + } else if (c < 2048) { + out[p++] = (c >> 6) | 192; + out[p++] = (c & 63) | 128; + } else { + out[p++] = (c >> 12) | 224; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } + } + return out; +}; + + +/** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Uint8Array|Array} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ +goog.crypt.utf8ByteArrayToString = function(bytes) { + // TODO(user): Use native implementations if/when available + var out = [], pos = 0, c = 0; + while (pos < bytes.length) { + var c1 = bytes[pos++]; + if (c1 < 128) { + out[c++] = String.fromCharCode(c1); + } else if (c1 > 191 && c1 < 224) { + var c2 = bytes[pos++]; + out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63); + } else { + var c2 = bytes[pos++]; + var c3 = bytes[pos++]; + out[c++] = String.fromCharCode( + (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63); + } + } + return out.join(''); +}; + + +/** + * XOR two byte arrays. + * @param {!ArrayBufferView|!Array} bytes1 Byte array 1. + * @param {!ArrayBufferView|!Array} bytes2 Byte array 2. + * @return {!Array} Resulting XOR of the two byte arrays. + */ +goog.crypt.xorByteArray = function(bytes1, bytes2) { + goog.asserts.assert( + bytes1.length == bytes2.length, + 'XOR array lengths must match'); + + var result = []; + for (var i = 0; i < bytes1.length; i++) { + result.push(bytes1[i] ^ bytes2[i]); + } + return result; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/crypt_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/crypt_perf.html new file mode 100644 index 0000000000..5f073759f5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/crypt_perf.html @@ -0,0 +1,85 @@ + + + + + + + Closure Performance Tests - UTF8 encoding and decoding + + + + + +

Closure Performance Tests - UTF8 encoding and decoding

+

+ User-agent: + +

+
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.html b/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.html new file mode 100644 index 0000000000..5a119fc2fd --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.js b/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.js new file mode 100644 index 0000000000..deb8451aa4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/crypt_test.js @@ -0,0 +1,169 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.cryptTest'); +goog.setTestOnly('goog.cryptTest'); + +goog.require('goog.crypt'); +goog.require('goog.string'); +goog.require('goog.testing.jsunit'); + +var UTF8_RANGES_BYTE_ARRAY = [ + 0x00, + 0x7F, + 0xC2, 0x80, + 0xDF, 0xBF, + 0xE0, 0xA0, 0x80, + 0xEF, 0xBF, 0xBF]; + +var UTF8_RANGES_STRING = '\u0000\u007F\u0080\u07FF\u0800\uFFFF'; + +function testStringToUtf8ByteArray() { + // Known encodings taken from Java's String.getBytes("UTF8") + + assertArrayEquals('ASCII', + [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100], + goog.crypt.stringToUtf8ByteArray('Hello, world')); + + assertArrayEquals('Latin', + [83, 99, 104, 195, 182, 110], + goog.crypt.stringToUtf8ByteArray('Sch\u00f6n')); + + assertArrayEquals('limits of the first 3 UTF-8 character ranges', + UTF8_RANGES_BYTE_ARRAY, + goog.crypt.stringToUtf8ByteArray(UTF8_RANGES_STRING)); +} + +function testUtf8ByteArrayToString() { + // Known encodings taken from Java's String.getBytes("UTF8") + + assertEquals('ASCII', 'Hello, world', goog.crypt.utf8ByteArrayToString( + [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100])); + + assertEquals('Latin', 'Sch\u00f6n', goog.crypt.utf8ByteArrayToString( + [83, 99, 104, 195, 182, 110])); + + assertEquals('limits of the first 3 UTF-8 character ranges', + UTF8_RANGES_STRING, + goog.crypt.utf8ByteArrayToString(UTF8_RANGES_BYTE_ARRAY)); +} + + +/** + * Same as testUtf8ByteArrayToString but with Uint8Array instead of + * Array. + */ +function testUint8ArrayToString() { + if (!goog.global.Uint8Array) { + // Uint8Array not supported. + return; + } + + var arr = new Uint8Array( + [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]); + assertEquals('ASCII', 'Hello, world', goog.crypt.utf8ByteArrayToString(arr)); + + arr = new Uint8Array([83, 99, 104, 195, 182, 110]); + assertEquals('Latin', 'Sch\u00f6n', goog.crypt.utf8ByteArrayToString(arr)); + + arr = new Uint8Array(UTF8_RANGES_BYTE_ARRAY); + assertEquals('limits of the first 3 UTF-8 character ranges', + UTF8_RANGES_STRING, + goog.crypt.utf8ByteArrayToString(arr)); +} + +function testByteArrayToString() { + assertEquals('', goog.crypt.byteArrayToString([])); + assertEquals('abc', goog.crypt.byteArrayToString([97, 98, 99])); +} + +function testHexToByteArray() { + assertElementsEquals( + [202, 254, 222, 173], + // Java magic number + goog.crypt.hexToByteArray('cafedead')); + + assertElementsEquals( + [222, 173, 190, 239], + // IBM magic number + goog.crypt.hexToByteArray('DEADBEEF')); +} + +function testByteArrayToHex() { + assertEquals( + // Java magic number + 'cafedead', + goog.crypt.byteArrayToHex([202, 254, 222, 173])); + + assertEquals( + // IBM magic number + 'deadbeef', + goog.crypt.byteArrayToHex([222, 173, 190, 239])); +} + + +/** Same as testByteArrayToHex but with Uint8Array instead of Array. */ +function testUint8ArrayToHex() { + if (!goog.isDef(goog.global.Uint8Array)) { + // Uint8Array not supported. + return; + } + + assertEquals( + // Java magic number + 'cafedead', + goog.crypt.byteArrayToHex(new Uint8Array([202, 254, 222, 173]))); + + assertEquals( + // IBM magic number + 'deadbeef', + goog.crypt.byteArrayToHex(new Uint8Array([222, 173, 190, 239]))); +} + +function testXorByteArray() { + assertElementsEquals( + [20, 83, 96, 66], + goog.crypt.xorByteArray([202, 254, 222, 173], [222, 173, 190, 239])); +} + + +/** Same as testXorByteArray but with Uint8Array instead of Array. */ +function testXorUint8Array() { + if (!goog.isDef(goog.global.Uint8Array)) { + // Uint8Array not supported. + return; + } + + assertElementsEquals( + [20, 83, 96, 66], + goog.crypt.xorByteArray( + new Uint8Array([202, 254, 222, 173]), + new Uint8Array([222, 173, 190, 239]))); +} + + +// Tests a one-megabyte byte array conversion to string. +// This would break on many JS implementations unless byteArrayToString +// split the input up. +// See discussion and bug report: http://goo.gl/LrWmZ9 +function testByteArrayToStringCallStack() { + // One megabyte is 2 to the 20th. + var count = Math.pow(2, 20); + var bytes = []; + for (var i = 0; i < count; i++) { + bytes.push('A'.charCodeAt(0)); + } + var str = goog.crypt.byteArrayToString(bytes); + assertEquals(goog.string.repeat('A', count), str); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hash.js b/mapbox-gl/closure-library/closure/goog/crypt/hash.js new file mode 100644 index 0000000000..51209be61d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hash.js @@ -0,0 +1,69 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Abstract cryptographic hash interface. + * + * See goog.crypt.Sha1 and goog.crypt.Md5 for sample implementations. + * + */ + +goog.provide('goog.crypt.Hash'); + + + +/** + * Create a cryptographic hash instance. + * + * @constructor + * @struct + */ +goog.crypt.Hash = function() { + /** + * The block size for the hasher. + * @type {number} + */ + this.blockSize = -1; +}; + + +/** + * Resets the internal accumulator. + */ +goog.crypt.Hash.prototype.reset = goog.abstractMethod; + + +/** + * Adds a byte array (array with values in [0-255] range) or a string (might + * only contain 8-bit, i.e., Latin1 characters) to the internal accumulator. + * + * Many hash functions operate on blocks of data and implement optimizations + * when a full chunk of data is readily available. Hence it is often preferable + * to provide large chunks of data (a kilobyte or more) than to repeatedly + * call the update method with few tens of bytes. If this is not possible, or + * not feasible, it might be good to provide data in multiplies of hash block + * size (often 64 bytes). Please see the implementation and performance tests + * of your favourite hash. + * + * @param {Array|Uint8Array|string} bytes Data used for the update. + * @param {number=} opt_length Number of bytes to use. + */ +goog.crypt.Hash.prototype.update = goog.abstractMethod; + + +/** + * @return {!Array} The finalized hash computed + * from the internal accumulator. + */ +goog.crypt.Hash.prototype.digest = goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hash32.js b/mapbox-gl/closure-library/closure/goog/crypt/hash32.js new file mode 100644 index 0000000000..fa24ccf43e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hash32.js @@ -0,0 +1,184 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of 32-bit hashing functions. + * + * This is a direct port from the Google Java Hash class + * + */ + +goog.provide('goog.crypt.hash32'); + +goog.require('goog.crypt'); + + +/** + * Default seed used during hashing, digits of pie. + * See SEED32 in http://go/base.hash.java + * @type {number} + */ +goog.crypt.hash32.SEED32 = 314159265; + + +/** + * Arbitrary constant used during hashing. + * See CONSTANT32 in http://go/base.hash.java + * @type {number} + */ +goog.crypt.hash32.CONSTANT32 = -1640531527; + + +/** + * Hashes a string to a 32-bit value. + * @param {string} str String to hash. + * @return {number} 32-bit hash. + */ +goog.crypt.hash32.encodeString = function(str) { + return goog.crypt.hash32.encodeByteArray(goog.crypt.stringToByteArray(str)); +}; + + +/** + * Hashes a string to a 32-bit value, converting the string to UTF-8 before + * doing the encoding. + * @param {string} str String to hash. + * @return {number} 32-bit hash. + */ +goog.crypt.hash32.encodeStringUtf8 = function(str) { + return goog.crypt.hash32.encodeByteArray( + goog.crypt.stringToUtf8ByteArray(str)); +}; + + +/** + * Hashes an integer to a 32-bit value. + * @param {number} value Number to hash. + * @return {number} 32-bit hash. + */ +goog.crypt.hash32.encodeInteger = function(value) { + // TODO(user): Does this make sense in JavaScript with doubles? Should we + // force the value to be in the correct range? + return goog.crypt.hash32.mix32_({ + a: value, + b: goog.crypt.hash32.CONSTANT32, + c: goog.crypt.hash32.SEED32 + }); +}; + + +/** + * Hashes a "byte" array to a 32-bit value using the supplied seed. + * @param {Array} bytes Array of bytes. + * @param {number=} opt_offset The starting position to use for hash + * computation. + * @param {number=} opt_length Number of bytes that are used for hashing. + * @param {number=} opt_seed The seed. + * @return {number} 32-bit hash. + */ +goog.crypt.hash32.encodeByteArray = function( + bytes, opt_offset, opt_length, opt_seed) { + var offset = opt_offset || 0; + var length = opt_length || bytes.length; + var seed = opt_seed || goog.crypt.hash32.SEED32; + + var mix = { + a: goog.crypt.hash32.CONSTANT32, + b: goog.crypt.hash32.CONSTANT32, + c: seed + }; + + var keylen; + for (keylen = length; keylen >= 12; keylen -= 12, offset += 12) { + mix.a += goog.crypt.hash32.wordAt_(bytes, offset); + mix.b += goog.crypt.hash32.wordAt_(bytes, offset + 4); + mix.c += goog.crypt.hash32.wordAt_(bytes, offset + 8); + goog.crypt.hash32.mix32_(mix); + } + // Hash any remaining bytes + mix.c += length; + switch (keylen) { // deal with rest. Some cases fall through + case 11: mix.c += (bytes[offset + 10]) << 24; + case 10: mix.c += (bytes[offset + 9] & 0xff) << 16; + case 9 : mix.c += (bytes[offset + 8] & 0xff) << 8; + // the first byte of c is reserved for the length + case 8 : + mix.b += goog.crypt.hash32.wordAt_(bytes, offset + 4); + mix.a += goog.crypt.hash32.wordAt_(bytes, offset); + break; + case 7 : mix.b += (bytes[offset + 6] & 0xff) << 16; + case 6 : mix.b += (bytes[offset + 5] & 0xff) << 8; + case 5 : mix.b += (bytes[offset + 4] & 0xff); + case 4 : + mix.a += goog.crypt.hash32.wordAt_(bytes, offset); + break; + case 3 : mix.a += (bytes[offset + 2] & 0xff) << 16; + case 2 : mix.a += (bytes[offset + 1] & 0xff) << 8; + case 1 : mix.a += (bytes[offset + 0] & 0xff); + // case 0 : nothing left to add + } + return goog.crypt.hash32.mix32_(mix); +}; + + +/** + * Performs an inplace mix of an object with the integer properties (a, b, c) + * and returns the final value of c. + * @param {Object} mix Object with properties, a, b, and c. + * @return {number} The end c-value for the mixing. + * @private + */ +goog.crypt.hash32.mix32_ = function(mix) { + var a = mix.a, b = mix.b, c = mix.c; + a -= b; a -= c; a ^= c >>> 13; + b -= c; b -= a; b ^= a << 8; + c -= a; c -= b; c ^= b >>> 13; + a -= b; a -= c; a ^= c >>> 12; + b -= c; b -= a; b ^= a << 16; + c -= a; c -= b; c ^= b >>> 5; + a -= b; a -= c; a ^= c >>> 3; + b -= c; b -= a; b ^= a << 10; + c -= a; c -= b; c ^= b >>> 15; + mix.a = a; mix.b = b; mix.c = c; + return c; +}; + + +/** + * Returns the word at a given offset. Treating an array of bytes a word at a + * time is far more efficient than byte-by-byte. + * @param {Array} bytes Array of bytes. + * @param {number} offset Offset in the byte array. + * @return {number} Integer value for the word. + * @private + */ +goog.crypt.hash32.wordAt_ = function(bytes, offset) { + var a = goog.crypt.hash32.toSigned_(bytes[offset + 0]); + var b = goog.crypt.hash32.toSigned_(bytes[offset + 1]); + var c = goog.crypt.hash32.toSigned_(bytes[offset + 2]); + var d = goog.crypt.hash32.toSigned_(bytes[offset + 3]); + return a + (b << 8) + (c << 16) + (d << 24); +}; + + +/** + * Converts an unsigned "byte" to signed, that is, convert a value in the range + * (0, 2^8-1) to (-2^7, 2^7-1) in order to be compatible with Java's byte type. + * @param {number} n Unsigned "byte" value. + * @return {number} Signed "byte" value. + * @private + */ +goog.crypt.hash32.toSigned_ = function(n) { + return n > 127 ? n - 256 : n; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.html b/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.html new file mode 100644 index 0000000000..48406169df --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.hash32 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.js b/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.js new file mode 100644 index 0000000000..6230629bc9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hash32_test.js @@ -0,0 +1,284 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.hash32Test'); +goog.setTestOnly('goog.crypt.hash32Test'); + +goog.require('goog.crypt.hash32'); +goog.require('goog.testing.TestCase'); +goog.require('goog.testing.jsunit'); + +// NOTE: This test uses a custom test case, see end of script block + +// Test data based on known input/output pairs generated using +// http://go/hash.java + +function testEncodeInteger() { + assertEquals(898813988, goog.crypt.hash32.encodeInteger(305419896)); +} + +function testEncodeByteArray() { + assertEquals(-1497024495, + goog.crypt.hash32.encodeByteArray([10, 20, 30, 40])); + assertEquals(-961586214, + goog.crypt.hash32.encodeByteArray([3, 1, 4, 1, 5, 9])); + assertEquals(-1482202299, + goog.crypt.hash32.encodeByteArray([127, 0, 0, 0, 123, 45])); + assertEquals(170907881, + goog.crypt.hash32.encodeByteArray([9, 1, 1])); +} + +function testKnownByteArrays() { + for (var i = 0; i < byteArrays.length; i++) { + assertEquals(byteArrays[i], + goog.crypt.hash32.encodeByteArray(createByteArray(i))); + } +} + +function testEncodeString() { + assertEquals(-937588052, goog.crypt.hash32.encodeString('Hello, world')); + assertEquals(62382810, goog.crypt.hash32.encodeString('Sch\xF6n')); +} + +function testEncodeStringUtf8() { + assertEquals(-937588052, + goog.crypt.hash32.encodeStringUtf8('Hello, world')); + assertEquals(-833263351, goog.crypt.hash32.encodeStringUtf8('Sch\xF6n')); + + assertEquals(-1771620293, goog.crypt.hash32.encodeStringUtf8( + '\u043A\u0440')); +} + +function testEncodeString_ascii() { + assertEquals('For ascii characters UTF8 should be the same', + goog.crypt.hash32.encodeStringUtf8('abc123'), + goog.crypt.hash32.encodeString('abc123')); + + assertEquals('For ascii characters UTF8 should be the same', + goog.crypt.hash32.encodeStringUtf8('The,quick.brown-fox'), + goog.crypt.hash32.encodeString('The,quick.brown-fox')); + + assertNotEquals('For non-ascii characters UTF-8 encoding is different', + goog.crypt.hash32.encodeStringUtf8('Sch\xF6n'), + goog.crypt.hash32.encodeString('Sch\xF6n')); +} + +function testEncodeString_poe() { + var poe = "Once upon a midnight dreary, while I pondered weak and weary," + + "Over many a quaint and curious volume of forgotten lore," + + "While I nodded, nearly napping, suddenly there came a tapping," + + "As of some one gently rapping, rapping at my chamber door." + + "`'Tis some visitor,' I muttered, `tapping at my chamber door -" + + "Only this, and nothing more.'" + + "Ah, distinctly I remember it was in the bleak December," + + "And each separate dying ember wrought its ghost upon the floor." + + "Eagerly I wished the morrow; - vainly I had sought to borrow" + + "From my books surcease of sorrow - sorrow for the lost Lenore -" + + "For the rare and radiant maiden whom the angels named Lenore -" + + "Nameless here for evermore." + + "And the silken sad uncertain rustling of each purple curtain" + + "Thrilled me - filled me with fantastic terrors never felt before;" + + "So that now, to still the beating of my heart, I stood repeating" + + "`'Tis some visitor entreating entrance at my chamber door -" + + "Some late visitor entreating entrance at my chamber door; -" + + "This it is, and nothing more,'" + + "Presently my soul grew stronger; hesitating then no longer," + + "`Sir,' said I, `or Madam, truly your forgiveness I implore;" + + "But the fact is I was napping, and so gently you came rapping," + + "And so faintly you came tapping, tapping at my chamber door," + + "That I scarce was sure I heard you' - here I opened wide the door; -" + + "Darkness there, and nothing more." + + "Deep into that darkness peering, long I stood there wondering, " + + "fearing," + + "Doubting, dreaming dreams no mortal ever dared to dream before" + + "But the silence was unbroken, and the darkness gave no token," + + "And the only word there spoken was the whispered word, `Lenore!'" + + "This I whispered, and an echo murmured back the word, `Lenore!'" + + "Merely this and nothing more." + + "Back into the chamber turning, all my soul within me burning," + + "Soon again I heard a tapping somewhat louder than before." + + "`Surely,\' said I, `surely that is something at my window lattice;" + + "Let me see then, what thereat is, and this mystery explore -" + + "Let my heart be still a moment and this mystery explore; -" + + "'Tis the wind and nothing more!'"; + + assertEquals(147608747, goog.crypt.hash32.encodeString(poe)); + assertEquals(147608747, goog.crypt.hash32.encodeStringUtf8(poe)); +} + +function testBenchmarking() { + if (!testCase) return; + // Not a real test, just outputs some timing + function makeString(n) { + var str = []; + for (var i = 0; i < n; i++) { + str.push(String.fromCharCode(Math.round(Math.random() * 500))); + } + return str.join(''); + } + for (var i = 0; i < 50000; i += 10000) { + var str = makeString(i); + var start = goog.now(); + var hash = goog.crypt.hash32.encodeString(str); + var diff = goog.now() - start; + testCase.saveMessage( + 'testBenchmarking : hashing ' + i + ' chars in ' + diff + 'ms'); + } +} + +function createByteArray(n) { + var arr = []; + for (var i = 0; i < n; i++) { + arr.push(i); + } + return arr; +} + +var byteArrays = { + 0: 1539411136, + 1: 1773524747, + 2: -254958930, + 3: 1532114172, + 4: 1923165449, + 5: 1611874589, + 6: 1502126780, + 7: -751745251, + 8: -292491321, + 9: 1106193218, + 10: -722791438, + 11: -2130666060, + 12: -259304553, + 13: 871461192, + 14: 865773084, + 15: 1615738330, + 16: -1836636447, + 17: -485722519, + 18: -120832227, + 19: 1954449704, + 20: 491312921, + 21: -1955462668, + 22: 168565425, + 23: -105893922, + 24: 620486614, + 25: -1789602428, + 26: 1765793554, + 27: 1723370948, + 28: -1275405721, + 29: 140421019, + 30: -1438726307, + 31: 538438903, + 32: -729123980, + 33: 1213490939, + 34: -1814248478, + 35: 1943703398, + 36: 1603073219, + 37: -2139639543, + 38: -694153941, + 39: 137511516, + 40: -249943726, + 41: -1166126060, + 42: 53464833, + 43: -915350862, + 44: 1306585409, + 45: 1064798289, + 46: 335555913, + 47: 224485496, + 48: 275599760, + 49: 409559869, + 50: 673770580, + 51: -2113819879, + 52: -791338727, + 53: -1716479479, + 54: 1795018816, + 55: 2020139343, + 56: -1652827750, + 57: -1509632558, + 58: 751641995, + 59: -217881377, + 60: -476546900, + 61: -1893349644, + 62: -729290332, + 63: 1359899321, + 64: 1811814306, + 65: 2100363086, + 66: -794920327, + 67: -1667555017, + 68: -549980099, + 69: -21170740, + 70: -1324143722, + 71: 1406730195, + 72: 2111381574, + 73: -1667480052, + 74: 1071811178, + 75: -1080194099, + 76: -181186882, + 77: 268677507, + 78: -546766334, + 79: 555953522, + 80: -981311675, + 81: 1988867392, + 82: 773172547, + 83: 1160806722, + 84: -1455460187, + 85: 83493600, + 86: 155365142, + 87: 1714618071, + 88: 1487712615, + 89: -810670278, + 90: 2031655097, + 91: 1286349470, + 92: -1873594211, + 93: 1875867480, + 94: -1096259787, + 95: -1054968610, + 96: -1723043458, + 97: 1278708307, + 98: -601104085, + 99: 1497928579, + 100: 1329732615, + 101: -1281696190, + 102: 1471511953, + 103: -62666299, + 104: 807569747, + 105: -1927974759, + 106: 1462243717, + 107: -862975602, + 108: 824369927, + 109: -1448816781, + 110: 1434162022, + 111: -881501413, + 112: -1554381107, + 113: -1730883204, + 114: 431236217, + 115: 1877278608, + 116: -673864625, + 117: 143000665, + 118: -596902829, + 119: 1038860559, + 120: 805884326, + 121: -1536181710, + 122: -1357373256, + 123: 1405134250, + 124: -860816481, + 125: 1393578269, + 126: -810682545, + 127: -635515639 +}; + +var testCase; +if (G_testRunner) { + testCase = new goog.testing.TestCase(document.title); + testCase.autoDiscoverTests(); + G_testRunner.initialize(testCase); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hashtester.js b/mapbox-gl/closure-library/closure/goog/crypt/hashtester.js new file mode 100644 index 0000000000..e4e804c578 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hashtester.js @@ -0,0 +1,244 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Unit tests for the abstract cryptographic hash interface. + * + */ + +goog.provide('goog.crypt.hashTester'); + +goog.require('goog.array'); +goog.require('goog.crypt'); +goog.require('goog.dom'); +goog.require('goog.testing.PerformanceTable'); +goog.require('goog.testing.PseudoRandom'); +goog.require('goog.testing.asserts'); +goog.setTestOnly('hashTester'); + + +/** + * Runs basic tests. + * + * @param {!goog.crypt.Hash} hash A hash instance. + */ +goog.crypt.hashTester.runBasicTests = function(hash) { + // Compute first hash. + hash.update([97, 158]); + var golden1 = hash.digest(); + + // Compute second hash. + hash.reset(); + hash.update('aB'); + var golden2 = hash.digest(); + assertTrue('Two different inputs resulted in a hash collision', + !!goog.testing.asserts.findDifferences(golden1, golden2)); + + // Empty hash. + hash.reset(); + var empty = hash.digest(); + assertTrue('Empty hash collided with a non-trivial one', + !!goog.testing.asserts.findDifferences(golden1, empty) && + !!goog.testing.asserts.findDifferences(golden2, empty)); + + // Zero-length array update. + hash.reset(); + hash.update([]); + assertArrayEquals('Updating with an empty array did not give an empty hash', + empty, hash.digest()); + + // Zero-length string update. + hash.reset(); + hash.update(''); + assertArrayEquals('Updating with an empty string did not give an empty hash', + empty, hash.digest()); + + // Recompute the first hash. + hash.reset(); + hash.update([97, 158]); + assertArrayEquals('The reset did not produce the initial state', + golden1, hash.digest()); + + // Check for a trivial collision. + hash.reset(); + hash.update([158, 97]); + assertTrue('Swapping bytes resulted in a hash collision', + !!goog.testing.asserts.findDifferences(golden1, hash.digest())); + + // Compare array and string input. + hash.reset(); + hash.update([97, 66]); + assertArrayEquals('String and array inputs should give the same result', + golden2, hash.digest()); + + // Compute in parts. + hash.reset(); + hash.update('a'); + hash.update([158]); + assertArrayEquals('Partial updates resulted in a different hash', + golden1, hash.digest()); + + // Test update with specified length. + hash.reset(); + hash.update('aB', 0); + hash.update([97, 158, 32], 2); + assertArrayEquals('Updating with an explicit buffer length did not work', + golden1, hash.digest()); +}; + + +/** + * Runs block tests. + * + * @param {!goog.crypt.Hash} hash A hash instance. + * @param {number} blockBytes Size of the hash block. + */ +goog.crypt.hashTester.runBlockTests = function(hash, blockBytes) { + // Compute a message which is 1 byte shorter than hash block size. + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var message = ''; + for (var i = 0; i < blockBytes - 1; i++) { + message += chars.charAt(i % chars.length); + } + + // Compute golden hash for 1 block + 2 bytes. + hash.update(message + '123'); + var golden1 = hash.digest(); + + // Compute golden hash for 2 blocks + 1 byte. + hash.reset(); + hash.update(message + message + '123'); + var golden2 = hash.digest(); + + // Almost fill a block, then overflow. + hash.reset(); + hash.update(message); + hash.update('123'); + assertArrayEquals(golden1, hash.digest()); + + // Fill a block. + hash.reset(); + hash.update(message + '1'); + hash.update('23'); + assertArrayEquals(golden1, hash.digest()); + + // Overflow a block. + hash.reset(); + hash.update(message + '12'); + hash.update('3'); + assertArrayEquals(golden1, hash.digest()); + + // Test single overflow with an array. + hash.reset(); + hash.update(goog.crypt.stringToByteArray(message + '123')); + assertArrayEquals(golden1, hash.digest()); + + // Almost fill a block, then overflow this and the next block. + hash.reset(); + hash.update(message); + hash.update(message + '123'); + assertArrayEquals(golden2, hash.digest()); + + // Fill two blocks. + hash.reset(); + hash.update(message + message + '12'); + hash.update('3'); + assertArrayEquals(golden2, hash.digest()); + + // Test double overflow with an array. + hash.reset(); + hash.update(goog.crypt.stringToByteArray(message)); + hash.update(goog.crypt.stringToByteArray(message + '123')); + assertArrayEquals(golden2, hash.digest()); +}; + + +/** + * Runs performance tests. + * + * @param {function():!goog.crypt.Hash} hashFactory A hash factory. + * @param {string} hashName Name of the hashing function. + */ +goog.crypt.hashTester.runPerfTests = function(hashFactory, hashName) { + var body = goog.dom.getDocument().body; + var perfTable = goog.dom.createElement('div'); + goog.dom.appendChild(body, perfTable); + + var table = new goog.testing.PerformanceTable(perfTable); + + function runPerfTest(byteLength, updateCount) { + var label = (hashName + ': ' + updateCount + ' update(s) of ' + byteLength + + ' bytes'); + + function run(data, dataType) { + table.run(function() { + var hash = hashFactory(); + for (var i = 0; i < updateCount; i++) { + hash.update(data, byteLength); + } + var digest = hash.digest(); + }, label + ' (' + dataType + ')'); + } + + var byteArray = goog.crypt.hashTester.createRandomByteArray_(byteLength); + var byteString = goog.crypt.hashTester.createByteString_(byteArray); + + run(byteArray, 'byte array'); + run(byteString, 'byte string'); + } + + var MESSAGE_LENGTH_LONG = 10000000; // 10 Mbytes + var MESSAGE_LENGTH_SHORT = 10; // 10 bytes + var MESSAGE_COUNT_SHORT = MESSAGE_LENGTH_LONG / MESSAGE_LENGTH_SHORT; + + runPerfTest(MESSAGE_LENGTH_LONG, 1); + runPerfTest(MESSAGE_LENGTH_SHORT, MESSAGE_COUNT_SHORT); +}; + + +/** + * Creates and returns a random byte array. + * + * @param {number} length Length of the byte array. + * @return {!Array} An array of bytes. + * @private + */ +goog.crypt.hashTester.createRandomByteArray_ = function(length) { + var random = new goog.testing.PseudoRandom(0); + var bytes = []; + + for (var i = 0; i < length; ++i) { + // Generates an integer from 0 to 255. + var b = Math.floor(random.random() * 0x100); + bytes.push(b); + } + + return bytes; +}; + + +/** + * Creates a string from an array of bytes. + * + * @param {!Array} bytes An array of bytes. + * @return {string} The string encoded by the bytes. + * @private + */ +goog.crypt.hashTester.createByteString_ = function(bytes) { + var str = ''; + goog.array.forEach(bytes, function(b) { + str += String.fromCharCode(b); + }); + return str; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hmac.js b/mapbox-gl/closure-library/closure/goog/crypt/hmac.js new file mode 100644 index 0000000000..15e0e1dffb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hmac.js @@ -0,0 +1,160 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of HMAC in JavaScript. + * + * Usage: + * var hmac = new goog.crypt.Hmac(new goog.crypt.sha1(), key, 64); + * var digest = hmac.getHmac(bytes); + * + * @author benyu@google.com (Jige Yu) - port to closure + */ + + +goog.provide('goog.crypt.Hmac'); + +goog.require('goog.crypt.Hash'); + + + +/** + * @constructor + * @param {!goog.crypt.Hash} hasher An object to serve as a hash function. + * @param {Array} key The secret key to use to calculate the hmac. + * Should be an array of not more than {@code blockSize} integers in + {0, 255}. + * @param {number=} opt_blockSize Optional. The block size {@code hasher} uses. + * If not specified, uses the block size from the hasher, or 16 if it is + * not specified. + * @extends {goog.crypt.Hash} + * @final + * @struct + */ +goog.crypt.Hmac = function(hasher, key, opt_blockSize) { + goog.crypt.Hmac.base(this, 'constructor'); + + /** + * The underlying hasher to calculate hash. + * + * @type {!goog.crypt.Hash} + * @private + */ + this.hasher_ = hasher; + + this.blockSize = opt_blockSize || hasher.blockSize || 16; + + /** + * The outer padding array of hmac + * + * @type {!Array} + * @private + */ + this.keyO_ = new Array(this.blockSize); + + /** + * The inner padding array of hmac + * + * @type {!Array} + * @private + */ + this.keyI_ = new Array(this.blockSize); + + this.initialize_(key); +}; +goog.inherits(goog.crypt.Hmac, goog.crypt.Hash); + + +/** + * Outer padding byte of HMAC algorith, per http://en.wikipedia.org/wiki/HMAC + * + * @type {number} + * @private + */ +goog.crypt.Hmac.OPAD_ = 0x5c; + + +/** + * Inner padding byte of HMAC algorith, per http://en.wikipedia.org/wiki/HMAC + * + * @type {number} + * @private + */ +goog.crypt.Hmac.IPAD_ = 0x36; + + +/** + * Initializes Hmac by precalculating the inner and outer paddings. + * + * @param {Array} key The secret key to use to calculate the hmac. + * Should be an array of not more than {@code blockSize} integers in + {0, 255}. + * @private + */ +goog.crypt.Hmac.prototype.initialize_ = function(key) { + if (key.length > this.blockSize) { + this.hasher_.update(key); + key = this.hasher_.digest(); + this.hasher_.reset(); + } + // Precalculate padded and xor'd keys. + var keyByte; + for (var i = 0; i < this.blockSize; i++) { + if (i < key.length) { + keyByte = key[i]; + } else { + keyByte = 0; + } + this.keyO_[i] = keyByte ^ goog.crypt.Hmac.OPAD_; + this.keyI_[i] = keyByte ^ goog.crypt.Hmac.IPAD_; + } + // Be ready for an immediate update. + this.hasher_.update(this.keyI_); +}; + + +/** @override */ +goog.crypt.Hmac.prototype.reset = function() { + this.hasher_.reset(); + this.hasher_.update(this.keyI_); +}; + + +/** @override */ +goog.crypt.Hmac.prototype.update = function(bytes, opt_length) { + this.hasher_.update(bytes, opt_length); +}; + + +/** @override */ +goog.crypt.Hmac.prototype.digest = function() { + var temp = this.hasher_.digest(); + this.hasher_.reset(); + this.hasher_.update(this.keyO_); + this.hasher_.update(temp); + return this.hasher_.digest(); +}; + + +/** + * Calculates an HMAC for a given message. + * + * @param {Array|Uint8Array|string} message Data to Hmac. + * @return {!Array} the digest of the given message. + */ +goog.crypt.Hmac.prototype.getHmac = function(message) { + this.reset(); + this.update(message); + return this.digest(); +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.html b/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.html new file mode 100644 index 0000000000..c612ece76b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.sha1 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.js b/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.js new file mode 100644 index 0000000000..8f1046abc9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/hmac_test.js @@ -0,0 +1,142 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.HmacTest'); +goog.setTestOnly('goog.crypt.HmacTest'); + +goog.require('goog.crypt.Hmac'); +goog.require('goog.crypt.Sha1'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); + +function stringToBytes(s) { + var bytes = new Array(s.length); + + for (var i = 0; i < s.length; ++i) { + bytes[i] = s.charCodeAt(i) & 255; + } + return bytes; +} + +function hexToBytes(str) { + var arr = []; + + for (var i = 0; i < str.length; i += 2) { + arr.push(parseInt(str.substring(i, i + 2), 16)); + } + + return arr; +} + +function bytesToHex(b) { + var hexchars = '0123456789abcdef'; + var hexrep = new Array(b.length * 2); + + for (var i = 0; i < b.length; ++i) { + hexrep[i * 2] = hexchars.charAt((b[i] >> 4) & 15); + hexrep[i * 2 + 1] = hexchars.charAt(b[i] & 15); + } + return hexrep.join(''); +} + + +/** + * helper to get an hmac of the given message with the given key. + */ +function getHmac(key, message, opt_blockSize) { + var hasher = new goog.crypt.Sha1(); + var hmacer = new goog.crypt.Hmac(hasher, key, opt_blockSize); + return bytesToHex(hmacer.getHmac(message)); +} + +function testBasicOperations() { + var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), 'key', 64); + goog.crypt.hashTester.runBasicTests(hmac); +} + +function testBasicOperationsWithNoBlockSize() { + var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), 'key'); + goog.crypt.hashTester.runBasicTests(hmac); +} + +function testHmac() { + // HMAC test vectors from: + // http://tools.ietf.org/html/2202 + + assertEquals('test 1 failed', + 'b617318655057264e28bc0b6fb378c8ef146be00', + getHmac(hexToBytes('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'), + stringToBytes('Hi There'))); + + assertEquals('test 2 failed', + 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79', + getHmac(stringToBytes('Jefe'), + stringToBytes('what do ya want for nothing?'))); + + assertEquals('test 3 failed', + '125d7342b9ac11cd91a39af48aa17b4f63f175d3', + getHmac(hexToBytes('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + hexToBytes('dddddddddddddddddddddddddddddddddddddddd' + + 'dddddddddddddddddddddddddddddddddddddddd' + + 'dddddddddddddddddddd'))); + + assertEquals('test 4 failed', + '4c9007f4026250c6bc8414f9bf50c86c2d7235da', + getHmac(hexToBytes('0102030405060708090a0b0c0d0e0f10111213141516171819'), + hexToBytes('cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd' + + 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd' + + 'cdcdcdcdcdcdcdcdcdcd'))); + + assertEquals('test 5 failed', + '4c1a03424b55e07fe7f27be1d58bb9324a9a5a04', + getHmac(hexToBytes('0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c'), + stringToBytes('Test With Truncation'))); + + assertEquals('test 6 failed', + 'aa4ae5e15272d00e95705637ce8a3b55ed402112', + getHmac(hexToBytes('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'), + stringToBytes( + 'Test Using Larger Than Block-Size Key - Hash Key First'))); + + assertEquals('test 7 failed', + 'b617318655057264e28bc0b6fb378c8ef146be00', + getHmac(hexToBytes('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'), + stringToBytes('Hi There'), 64)); + + assertEquals('test 8 failed', + '941f806707826395dc510add6a45ce9933db976e', + getHmac(hexToBytes('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'), + stringToBytes('Hi There'), 32)); +} + + +/** Regression test for Bug 12863104 */ +function testUpdateWithLongKey() { + // Calling update() then digest() should give the same result as just + // calling getHmac() + var key = hexToBytes('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + var message = 'Secret Message'; + var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), key); + hmac.update(message); + var result1 = bytesToHex(hmac.digest()); + hmac.reset(); + var result2 = bytesToHex(hmac.getHmac(message)); + assertEquals('Results must be the same', result1, result2); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/md5.js b/mapbox-gl/closure-library/closure/goog/crypt/md5.js new file mode 100644 index 0000000000..56335e152d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/md5.js @@ -0,0 +1,435 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview MD5 cryptographic hash. + * Implementation of http://tools.ietf.org/html/rfc1321 with common + * optimizations and tweaks (see http://en.wikipedia.org/wiki/MD5). + * + * Usage: + * var md5 = new goog.crypt.Md5(); + * md5.update(bytes); + * var hash = md5.digest(); + * + * Performance: + * Chrome 23 ~680 Mbit/s + * Chrome 13 (in a VM) ~250 Mbit/s + * Firefox 6.0 (in a VM) ~100 Mbit/s + * IE9 (in a VM) ~27 Mbit/s + * Firefox 3.6 ~15 Mbit/s + * IE8 (in a VM) ~13 Mbit/s + * + */ + +goog.provide('goog.crypt.Md5'); + +goog.require('goog.crypt.Hash'); + + + +/** + * MD5 cryptographic hash constructor. + * @constructor + * @extends {goog.crypt.Hash} + * @final + * @struct + */ +goog.crypt.Md5 = function() { + goog.crypt.Md5.base(this, 'constructor'); + + this.blockSize = 512 / 8; + + /** + * Holds the current values of accumulated A-D variables (MD buffer). + * @type {!Array} + * @private + */ + this.chain_ = new Array(4); + + /** + * A buffer holding the data until the whole block can be processed. + * @type {!Array} + * @private + */ + this.block_ = new Array(this.blockSize); + + /** + * The length of yet-unprocessed data as collected in the block. + * @type {number} + * @private + */ + this.blockLength_ = 0; + + /** + * The total length of the message so far. + * @type {number} + * @private + */ + this.totalLength_ = 0; + + this.reset(); +}; +goog.inherits(goog.crypt.Md5, goog.crypt.Hash); + + +/** + * Integer rotation constants used by the abbreviated implementation. + * They are hardcoded in the unrolled implementation, so it is left + * here commented out. + * @type {Array} + * @private + * +goog.crypt.Md5.S_ = [ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +]; + */ + +/** + * Sine function constants used by the abbreviated implementation. + * They are hardcoded in the unrolled implementation, so it is left + * here commented out. + * @type {Array} + * @private + * +goog.crypt.Md5.T_ = [ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +]; + */ + + +/** @override */ +goog.crypt.Md5.prototype.reset = function() { + this.chain_[0] = 0x67452301; + this.chain_[1] = 0xefcdab89; + this.chain_[2] = 0x98badcfe; + this.chain_[3] = 0x10325476; + + this.blockLength_ = 0; + this.totalLength_ = 0; +}; + + +/** + * Internal compress helper function. It takes a block of data (64 bytes) + * and updates the accumulator. + * @param {Array|Uint8Array|string} buf The block to compress. + * @param {number=} opt_offset Offset of the block in the buffer. + * @private + */ +goog.crypt.Md5.prototype.compress_ = function(buf, opt_offset) { + if (!opt_offset) { + opt_offset = 0; + } + + // We allocate the array every time, but it's cheap in practice. + var X = new Array(16); + + // Get 16 little endian words. It is not worth unrolling this for Chrome 11. + if (goog.isString(buf)) { + for (var i = 0; i < 16; ++i) { + X[i] = (buf.charCodeAt(opt_offset++)) | + (buf.charCodeAt(opt_offset++) << 8) | + (buf.charCodeAt(opt_offset++) << 16) | + (buf.charCodeAt(opt_offset++) << 24); + } + } else { + for (var i = 0; i < 16; ++i) { + X[i] = (buf[opt_offset++]) | + (buf[opt_offset++] << 8) | + (buf[opt_offset++] << 16) | + (buf[opt_offset++] << 24); + } + } + + var A = this.chain_[0]; + var B = this.chain_[1]; + var C = this.chain_[2]; + var D = this.chain_[3]; + var sum = 0; + + /* + * This is an abbreviated implementation, it is left here commented out for + * reference purposes. See below for an unrolled version in use. + * + var f, n, tmp; + for (var i = 0; i < 64; ++i) { + + if (i < 16) { + f = (D ^ (B & (C ^ D))); + n = i; + } else if (i < 32) { + f = (C ^ (D & (B ^ C))); + n = (5 * i + 1) % 16; + } else if (i < 48) { + f = (B ^ C ^ D); + n = (3 * i + 5) % 16; + } else { + f = (C ^ (B | (~D))); + n = (7 * i) % 16; + } + + tmp = D; + D = C; + C = B; + sum = (A + f + goog.crypt.Md5.T_[i] + X[n]) & 0xffffffff; + B += ((sum << goog.crypt.Md5.S_[i]) & 0xffffffff) | + (sum >>> (32 - goog.crypt.Md5.S_[i])); + A = tmp; + } + */ + + /* + * This is an unrolled MD5 implementation, which gives ~30% speedup compared + * to the abbreviated implementation above, as measured on Chrome 11. It is + * important to keep 32-bit croppings to minimum and inline the integer + * rotation. + */ + sum = (A + (D ^ (B & (C ^ D))) + X[0] + 0xd76aa478) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[1] + 0xe8c7b756) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[2] + 0x242070db) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[3] + 0xc1bdceee) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[4] + 0xf57c0faf) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[5] + 0x4787c62a) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[6] + 0xa8304613) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[7] + 0xfd469501) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[8] + 0x698098d8) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[9] + 0x8b44f7af) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[10] + 0xffff5bb1) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[11] + 0x895cd7be) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[12] + 0x6b901122) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[13] + 0xfd987193) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[14] + 0xa679438e) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[15] + 0x49b40821) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (C ^ (D & (B ^ C))) + X[1] + 0xf61e2562) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[6] + 0xc040b340) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[11] + 0x265e5a51) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[0] + 0xe9b6c7aa) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[5] + 0xd62f105d) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[10] + 0x02441453) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[15] + 0xd8a1e681) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[4] + 0xe7d3fbc8) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[9] + 0x21e1cde6) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[14] + 0xc33707d6) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[3] + 0xf4d50d87) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[8] + 0x455a14ed) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[13] + 0xa9e3e905) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[2] + 0xfcefa3f8) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[7] + 0x676f02d9) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[12] + 0x8d2a4c8a) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (B ^ C ^ D) + X[5] + 0xfffa3942) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[8] + 0x8771f681) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[11] + 0x6d9d6122) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[14] + 0xfde5380c) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[1] + 0xa4beea44) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[4] + 0x4bdecfa9) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[7] + 0xf6bb4b60) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[10] + 0xbebfbc70) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[13] + 0x289b7ec6) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[0] + 0xeaa127fa) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[3] + 0xd4ef3085) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[6] + 0x04881d05) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[9] + 0xd9d4d039) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[12] + 0xe6db99e5) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[15] + 0x1fa27cf8) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[2] + 0xc4ac5665) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (C ^ (B | (~D))) + X[0] + 0xf4292244) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[7] + 0x432aff97) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[14] + 0xab9423a7) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[5] + 0xfc93a039) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[12] + 0x655b59c3) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[3] + 0x8f0ccc92) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[10] + 0xffeff47d) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[1] + 0x85845dd1) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[8] + 0x6fa87e4f) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[15] + 0xfe2ce6e0) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[6] + 0xa3014314) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[13] + 0x4e0811a1) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[4] + 0xf7537e82) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[11] + 0xbd3af235) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[2] + 0x2ad7d2bb) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[9] + 0xeb86d391) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + + this.chain_[0] = (this.chain_[0] + A) & 0xffffffff; + this.chain_[1] = (this.chain_[1] + B) & 0xffffffff; + this.chain_[2] = (this.chain_[2] + C) & 0xffffffff; + this.chain_[3] = (this.chain_[3] + D) & 0xffffffff; +}; + + +/** @override */ +goog.crypt.Md5.prototype.update = function(bytes, opt_length) { + if (!goog.isDef(opt_length)) { + opt_length = bytes.length; + } + var lengthMinusBlock = opt_length - this.blockSize; + + // Copy some object properties to local variables in order to save on access + // time from inside the loop (~10% speedup was observed on Chrome 11). + var block = this.block_; + var blockLength = this.blockLength_; + var i = 0; + + // The outer while loop should execute at most twice. + while (i < opt_length) { + // When we have no data in the block to top up, we can directly process the + // input buffer (assuming it contains sufficient data). This gives ~30% + // speedup on Chrome 14 and ~70% speedup on Firefox 6.0, but requires that + // the data is provided in large chunks (or in multiples of 64 bytes). + if (blockLength == 0) { + while (i <= lengthMinusBlock) { + this.compress_(bytes, i); + i += this.blockSize; + } + } + + if (goog.isString(bytes)) { + while (i < opt_length) { + block[blockLength++] = bytes.charCodeAt(i++); + if (blockLength == this.blockSize) { + this.compress_(block); + blockLength = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } else { + while (i < opt_length) { + block[blockLength++] = bytes[i++]; + if (blockLength == this.blockSize) { + this.compress_(block); + blockLength = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } + } + + this.blockLength_ = blockLength; + this.totalLength_ += opt_length; +}; + + +/** @override */ +goog.crypt.Md5.prototype.digest = function() { + // This must accommodate at least 1 padding byte (0x80), 8 bytes of + // total bitlength, and must end at a 64-byte boundary. + var pad = new Array((this.blockLength_ < 56 ? + this.blockSize : + this.blockSize * 2) - this.blockLength_); + + // Add padding: 0x80 0x00* + pad[0] = 0x80; + for (var i = 1; i < pad.length - 8; ++i) { + pad[i] = 0; + } + // Add the total number of bits, little endian 64-bit integer. + var totalBits = this.totalLength_ * 8; + for (var i = pad.length - 8; i < pad.length; ++i) { + pad[i] = totalBits & 0xff; + totalBits /= 0x100; // Don't use bit-shifting here! + } + this.update(pad); + + var digest = new Array(16); + var n = 0; + for (var i = 0; i < 4; ++i) { + for (var j = 0; j < 32; j += 8) { + digest[n++] = (this.chain_[i] >>> j) & 0xff; + } + } + return digest; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/md5_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/md5_perf.html new file mode 100644 index 0000000000..57aa8c88f8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/md5_perf.html @@ -0,0 +1,39 @@ + + + + + +Closure Performance Tests - goog.crypt.Md5 + + + + + +

Closure Performance Tests - goog.crypt.Md5

+

+User-agent: + +

+ + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/md5_test.html b/mapbox-gl/closure-library/closure/goog/crypt/md5_test.html new file mode 100644 index 0000000000..b37e301e8b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/md5_test.html @@ -0,0 +1,23 @@ + + + + + + + Closure Unit Tests - goog.crypt.Md5 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/md5_test.js b/mapbox-gl/closure-library/closure/goog/crypt/md5_test.js new file mode 100644 index 0000000000..ac1ab8370e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/md5_test.js @@ -0,0 +1,147 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Md5Test'); +goog.setTestOnly('goog.crypt.Md5Test'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Md5'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); + +var sixty = '123456789012345678901234567890123456789012345678901234567890'; + +function testBasicOperations() { + var md5 = new goog.crypt.Md5(); + goog.crypt.hashTester.runBasicTests(md5); +} + +function testBlockOperations() { + var md5 = new goog.crypt.Md5(); + goog.crypt.hashTester.runBlockTests(md5, 64); +} + +function testHashing() { + // Empty stream. + var md5 = new goog.crypt.Md5(); + assertEquals('d41d8cd98f00b204e9800998ecf8427e', + goog.crypt.byteArrayToHex(md5.digest())); + + // Simple stream. + md5.reset(); + md5.update([97]); + assertEquals('0cc175b9c0f1b6a831c399e269772661', + goog.crypt.byteArrayToHex(md5.digest())); + + // Simple stream with two updates. + md5.reset(); + md5.update([97]); + md5.update('bc'); + assertEquals('900150983cd24fb0d6963f7d28e17f72', + goog.crypt.byteArrayToHex(md5.digest())); + + // RFC 1321 standard test. + md5.reset(); + md5.update('abcdefghijklmnopqrstuvwxyz'); + assertEquals('c3fcd3d76192e4007dfb496cca67e13b', + goog.crypt.byteArrayToHex(md5.digest())); + + // RFC 1321 standard test with two updates. + md5.reset(); + md5.update('message '); + md5.update('digest'); + assertEquals('f96b697d7cb7938d525a2f31aaf161d0', + goog.crypt.byteArrayToHex(md5.digest())); + + // RFC 1321 standard test with three updates. + md5.reset(); + md5.update('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); + md5.update('abcdefghijklmnopqrstuvwxyz'); + md5.update('0123456789'); + assertEquals('d174ab98d277d9f5a5611c2c9f419d9f', + goog.crypt.byteArrayToHex(md5.digest())); +} + +function testPadding() { + // Message + padding fits in two 64-byte blocks. + var md5 = new goog.crypt.Md5(); + md5.update(sixty); + md5.update(sixty.substr(0, 59)); + assertEquals('6261005311809757906e04c0d670492d', + goog.crypt.byteArrayToHex(md5.digest())); + + // Message + padding does not fit in two 64-byte blocks. + md5.reset(); + md5.update(sixty); + md5.update(sixty); + assertEquals('1d453b96d48d5e0cec4a20a71fecaa81', + goog.crypt.byteArrayToHex(md5.digest())); +} + +function testTwoAccumulators() { + // Two accumulators in parallel. + var md5_A = new goog.crypt.Md5(); + var md5_B = new goog.crypt.Md5(); + md5_A.update(sixty); + md5_B.update(sixty); + md5_A.update(sixty + '1'); + md5_B.update(sixty + '2'); + assertEquals('0801d688cc107d4789ec8b9a4519f01f', + goog.crypt.byteArrayToHex(md5_A.digest())); + assertEquals('6e1a35ffc185d1e684d6ed281c0d4bd2', + goog.crypt.byteArrayToHex(md5_B.digest())); +} + +function testCollision() { + // Check a known collision. + var A = [0xd1, 0x31, 0xdd, 0x02, 0xc5, 0xe6, 0xee, 0xc4, + 0x69, 0x3d, 0x9a, 0x06, 0x98, 0xaf, 0xf9, 0x5c, + 0x2f, 0xca, 0xb5, 0x87, 0x12, 0x46, 0x7e, 0xab, + 0x40, 0x04, 0x58, 0x3e, 0xb8, 0xfb, 0x7f, 0x89, + 0x55, 0xad, 0x34, 0x06, 0x09, 0xf4, 0xb3, 0x02, + 0x83, 0xe4, 0x88, 0x83, 0x25, 0x71, 0x41, 0x5a, + 0x08, 0x51, 0x25, 0xe8, 0xf7, 0xcd, 0xc9, 0x9f, + 0xd9, 0x1d, 0xbd, 0xf2, 0x80, 0x37, 0x3c, 0x5b, + 0xd8, 0x82, 0x3e, 0x31, 0x56, 0x34, 0x8f, 0x5b, + 0xae, 0x6d, 0xac, 0xd4, 0x36, 0xc9, 0x19, 0xc6, + 0xdd, 0x53, 0xe2, 0xb4, 0x87, 0xda, 0x03, 0xfd, + 0x02, 0x39, 0x63, 0x06, 0xd2, 0x48, 0xcd, 0xa0, + 0xe9, 0x9f, 0x33, 0x42, 0x0f, 0x57, 0x7e, 0xe8, + 0xce, 0x54, 0xb6, 0x70, 0x80, 0xa8, 0x0d, 0x1e, + 0xc6, 0x98, 0x21, 0xbc, 0xb6, 0xa8, 0x83, 0x93, + 0x96, 0xf9, 0x65, 0x2b, 0x6f, 0xf7, 0x2a, 0x70]; + var B = [0xd1, 0x31, 0xdd, 0x02, 0xc5, 0xe6, 0xee, 0xc4, + 0x69, 0x3d, 0x9a, 0x06, 0x98, 0xaf, 0xf9, 0x5c, + 0x2f, 0xca, 0xb5, 0x07, 0x12, 0x46, 0x7e, 0xab, + 0x40, 0x04, 0x58, 0x3e, 0xb8, 0xfb, 0x7f, 0x89, + 0x55, 0xad, 0x34, 0x06, 0x09, 0xf4, 0xb3, 0x02, + 0x83, 0xe4, 0x88, 0x83, 0x25, 0xf1, 0x41, 0x5a, + 0x08, 0x51, 0x25, 0xe8, 0xf7, 0xcd, 0xc9, 0x9f, + 0xd9, 0x1d, 0xbd, 0x72, 0x80, 0x37, 0x3c, 0x5b, + 0xd8, 0x82, 0x3e, 0x31, 0x56, 0x34, 0x8f, 0x5b, + 0xae, 0x6d, 0xac, 0xd4, 0x36, 0xc9, 0x19, 0xc6, + 0xdd, 0x53, 0xe2, 0x34, 0x87, 0xda, 0x03, 0xfd, + 0x02, 0x39, 0x63, 0x06, 0xd2, 0x48, 0xcd, 0xa0, + 0xe9, 0x9f, 0x33, 0x42, 0x0f, 0x57, 0x7e, 0xe8, + 0xce, 0x54, 0xb6, 0x70, 0x80, 0x28, 0x0d, 0x1e, + 0xc6, 0x98, 0x21, 0xbc, 0xb6, 0xa8, 0x83, 0x93, + 0x96, 0xf9, 0x65, 0xab, 0x6f, 0xf7, 0x2a, 0x70]; + var digest = '79054025255fb1a26e4bc422aef54eb4'; + var md5_A = new goog.crypt.Md5(); + var md5_B = new goog.crypt.Md5(); + md5_A.update(A); + md5_B.update(B); + assertEquals(digest, goog.crypt.byteArrayToHex(md5_A.digest())); + assertEquals(digest, goog.crypt.byteArrayToHex(md5_B.digest())); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2.js b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2.js new file mode 100644 index 0000000000..2fd1807e7d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2.js @@ -0,0 +1,128 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implementation of PBKDF2 in JavaScript. + * @see http://en.wikipedia.org/wiki/PBKDF2 + * + * Currently we only support HMAC-SHA1 as the underlying hash function. To add a + * new hash function, add a static method similar to deriveKeyFromPasswordSha1() + * and implement the specific computeBlockCallback() using the hash function. + * + * Usage: + * var key = pbkdf2.deriveKeySha1( + * stringToByteArray('password'), stringToByteArray('salt'), 1000, 128); + * + */ + +goog.provide('goog.crypt.pbkdf2'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.crypt'); +goog.require('goog.crypt.Hmac'); +goog.require('goog.crypt.Sha1'); + + +/** + * Derives key from password using PBKDF2-SHA1 + * @param {!Array} password Byte array representation of the password + * from which the key is derived. + * @param {!Array} initialSalt Byte array representation of the salt. + * @param {number} iterations Number of interations when computing the key. + * @param {number} keyLength Length of the output key in bits. + * Must be multiple of 8. + * @return {!Array} Byte array representation of the output key. + */ +goog.crypt.pbkdf2.deriveKeySha1 = function( + password, initialSalt, iterations, keyLength) { + // Length of the HMAC-SHA1 output in bits. + var HASH_LENGTH = 160; + + /** + * Compute each block of the key using HMAC-SHA1. + * @param {!Array} index Byte array representation of the index of + * the block to be computed. + * @return {!Array} Byte array representation of the output block. + */ + var computeBlock = function(index) { + // Initialize the result to be array of 0 such that its xor with the first + // block would be the first block. + var result = goog.array.repeat(0, HASH_LENGTH / 8); + // Initialize the salt of the first iteration to initialSalt || i. + var salt = initialSalt.concat(index); + var hmac = new goog.crypt.Hmac(new goog.crypt.Sha1(), password, 64); + // Compute and XOR each iteration. + for (var i = 0; i < iterations; i++) { + // The salt of the next iteration is the result of the current iteration. + salt = hmac.getHmac(salt); + result = goog.crypt.xorByteArray(result, salt); + } + return result; + }; + + return goog.crypt.pbkdf2.deriveKeyFromPassword_( + computeBlock, HASH_LENGTH, keyLength); +}; + + +/** + * Compute each block of the key using PBKDF2. + * @param {Function} computeBlock Function to compute each block of the output + * key. + * @param {number} hashLength Length of each block in bits. This is determined + * by the specific hash function used. Must be multiple of 8. + * @param {number} keyLength Length of the output key in bits. + * Must be multiple of 8. + * @return {!Array} Byte array representation of the output key. + * @private + */ +goog.crypt.pbkdf2.deriveKeyFromPassword_ = + function(computeBlock, hashLength, keyLength) { + goog.asserts.assert(keyLength % 8 == 0, 'invalid output key length'); + + // Compute and concactate each block of the output key. + var numBlocks = Math.ceil(keyLength / hashLength); + goog.asserts.assert(numBlocks >= 1, 'invalid number of blocks'); + var result = []; + for (var i = 1; i <= numBlocks; i++) { + var indexBytes = goog.crypt.pbkdf2.integerToByteArray_(i); + result = result.concat(computeBlock(indexBytes)); + } + + // Trim the last block if needed. + var lastBlockSize = keyLength % hashLength; + if (lastBlockSize != 0) { + var desiredBytes = ((numBlocks - 1) * hashLength + lastBlockSize) / 8; + result.splice(desiredBytes, (hashLength - lastBlockSize) / 8); + } + return result; +}; + + +/** + * Converts an integer number to a 32-bit big endian byte array. + * @param {number} n Integer number to be converted. + * @return {!Array} Byte Array representation of the 32-bit big endian + * encoding of n. + * @private + */ +goog.crypt.pbkdf2.integerToByteArray_ = function(n) { + var result = new Array(4); + result[0] = n >> 24 & 0xFF; + result[1] = n >> 16 & 0xFF; + result[2] = n >> 8 & 0xFF; + result[3] = n & 0xFF; + return result; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.html b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.html new file mode 100644 index 0000000000..de6d1bd9da --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.html @@ -0,0 +1,25 @@ + + + + + + + + Closure Unit Tests - goog.crypt.pbkdf2 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.js b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.js new file mode 100644 index 0000000000..8aee360244 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/pbkdf2_test.js @@ -0,0 +1,61 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.pbkdf2Test'); +goog.setTestOnly('goog.crypt.pbkdf2Test'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.pbkdf2'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function testPBKDF2() { + // PBKDF2 test vectors from: + // http://tools.ietf.org/html/rfc6070 + + if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('7')) { + return; + } + + var testPassword = goog.crypt.stringToByteArray('password'); + var testSalt = goog.crypt.stringToByteArray('salt'); + + assertElementsEquals( + goog.crypt.hexToByteArray('0c60c80f961f0e71f3a9b524af6012062fe037a6'), + goog.crypt.pbkdf2.deriveKeySha1(testPassword, testSalt, 1, 160)); + + assertElementsEquals( + goog.crypt.hexToByteArray('ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957'), + goog.crypt.pbkdf2.deriveKeySha1(testPassword, testSalt, 2, 160)); + + assertElementsEquals( + goog.crypt.hexToByteArray('4b007901b765489abead49d926f721d065a429c1'), + goog.crypt.pbkdf2.deriveKeySha1(testPassword, testSalt, 4096, 160)); + + testPassword = goog.crypt.stringToByteArray('passwordPASSWORDpassword'); + testSalt = + goog.crypt.stringToByteArray('saltSALTsaltSALTsaltSALTsaltSALTsalt'); + + assertElementsEquals( + goog.crypt.hexToByteArray( + '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038'), + goog.crypt.pbkdf2.deriveKeySha1(testPassword, testSalt, 4096, 200)); + + testPassword = goog.crypt.stringToByteArray('pass\0word'); + testSalt = goog.crypt.stringToByteArray('sa\0lt'); + + assertElementsEquals( + goog.crypt.hexToByteArray('56fa6aa75548099dcc37d7f03425e0c3'), + goog.crypt.pbkdf2.deriveKeySha1(testPassword, testSalt, 4096, 128)); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha1.js b/mapbox-gl/closure-library/closure/goog/crypt/sha1.js new file mode 100644 index 0000000000..a1979ea422 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha1.js @@ -0,0 +1,311 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-1 cryptographic hash. + * Variable names follow the notation in FIPS PUB 180-3: + * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf. + * + * Usage: + * var sha1 = new goog.crypt.sha1(); + * sha1.update(bytes); + * var hash = sha1.digest(); + * + * Performance: + * Chrome 23: ~400 Mbit/s + * Firefox 16: ~250 Mbit/s + * + * Note: The idiom expr|0 is used to provide a type-hint to the VM, in order + * to avoid unnecessary uint32-double-uint32 roundtripping. + */ + +goog.provide('goog.crypt.Sha1'); + +goog.require('goog.crypt.Hash'); + + + +/** + * SHA-1 cryptographic hash constructor. + * + * The properties declared here are discussed in the above algorithm document. + * @constructor + * @extends {goog.crypt.Hash} + * @final + * @struct + */ +goog.crypt.Sha1 = function() { + goog.crypt.Sha1.base(this, 'constructor'); + + this.blockSize = 512 / 8; + + /** + * Holds the previous values of accumulated variables a-e in the compress_ + * function. + * @type {!Array} + * @private + */ + this.chain_ = []; + + /** + * A buffer holding the partially computed hash result. + * @type {!Array} + * @private + */ + this.buf_ = []; + + /** + * An array of 80 bytes, each a part of the message to be hashed. Referred to + * as the message schedule in the docs. + * @type {!Array} + * @private + */ + this.W_ = []; + + /** + * Contains data needed to pad messages less than 64 bytes. + * @type {!Array} + * @private + */ + this.pad_ = []; + + this.pad_[0] = 128; + for (var i = 1; i < this.blockSize; ++i) { + this.pad_[i] = 0; + } + + /** + * @private {number} + */ + this.inbuf_ = 0; + + /** + * @private {number} + */ + this.total_ = 0; + + this.reset(); +}; +goog.inherits(goog.crypt.Sha1, goog.crypt.Hash); + + +/** @override */ +goog.crypt.Sha1.prototype.reset = function() { + this.chain_[0] = 0x67452301; + this.chain_[1] = 0xefcdab89; + this.chain_[2] = 0x98badcfe; + this.chain_[3] = 0x10325476; + this.chain_[4] = 0xc3d2e1f0; + + this.inbuf_ = 0; + this.total_ = 0; +}; + + +/** + * Internal compress helper function. + * @param {!Array|!Uint8Array|string} buf Block to compress. + * @param {number=} opt_offset Offset of the block in the buffer. + * @private + */ +goog.crypt.Sha1.prototype.compress_ = function(buf, opt_offset) { + if (!opt_offset) { + opt_offset = 0; + } + + var W = this.W_; + var i; + + // get 16 big endian words + if (goog.isString(buf)) { + for (i = 0; i < 16; i++) { + // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS + // have a bug that turns the post-increment ++ operator into pre-increment + // during JIT compilation. We have code that depends heavily on SHA-1 for + // correctness and which is affected by this bug, so I've removed all uses + // of post-increment ++ in which the result value is used. We can revert + // this change once the Safari bug + // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and + // most clients have been updated. + W[i] = (((buf.charCodeAt(opt_offset) << 24) | + (buf.charCodeAt(opt_offset + 1) << 16) | + (buf.charCodeAt(opt_offset + 2) << 8) | + (buf.charCodeAt(opt_offset + 3))) & 0xffffffff) | 0; + opt_offset += 4; + } + } else { + for (i = 0; i < 16; i++) { + W[i] = (((buf[opt_offset] << 24) | + (buf[opt_offset + 1] << 16) | + (buf[opt_offset + 2] << 8) | + (buf[opt_offset + 3])) & 0xffffffff) | 0; + opt_offset += 4; + } + } + + var a = this.chain_[0]; + var b = this.chain_[1]; + var c = this.chain_[2]; + var d = this.chain_[3]; + var e = this.chain_[4]; + var f, k, t; + + // Steps 0-16. + for (i = 0; i < 16; i++) { + f = d ^ (b & (c ^ d)); + k = 0x5a827999; + + t = ((((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff) | 0; + e = d; + d = c; + c = (((b << 30) | (b >>> 2)) & 0xffffffff) | 0; + b = a; + a = t; + } + // Steps 16-80. W is formally described as an 80-word array, and usually + // computed that way. However, only 16 elements are needed for any iteration + // so we compute W on the fly and keep only the last 16 values. This improves + // performance by about 10% on Chrome 35. + for (i = 16; i < 80; i++) { + t = W[(i - 3) & 15] ^ W[(i - 8) & 15] ^ W[(i - 14) & 15] ^ W[i & 15]; + W[i & 15] = (((t << 1) | (t >>> 31)) & 0xfffffff) | 0; + if (i < 40) { + if (i < 20) { + f = d ^ (b & (c ^ d)); + k = 0x5a827999; + } else { + f = b ^ c ^ d; + k = 0x6ed9eba1; + } + } else { + if (i < 60) { + f = (b & c) | (d & (b | c)); + k = 0x8f1bbcdc; + } else { + f = b ^ c ^ d; + k = 0xca62c1d6; + } + } + + t = ((((a << 5) | (a >>> 27)) + f + e + k + W[i & 15]) & 0xffffffff) | 0; + e = d; + d = c; + c = (((b << 30) | (b >>> 2)) & 0xffffffff) | 0; + b = a; + a = t; + } + + this.chain_[0] = ((this.chain_[0] + a) & 0xfffffff) | 0; + this.chain_[1] = ((this.chain_[1] + b) & 0xfffffff) | 0; + this.chain_[2] = ((this.chain_[2] + c) & 0xfffffff) | 0; + this.chain_[3] = ((this.chain_[3] + d) & 0xfffffff) | 0; + this.chain_[4] = ((this.chain_[4] + e) & 0xfffffff) | 0; +}; + + +/** @override */ +goog.crypt.Sha1.prototype.update = function(bytes, opt_length) { + // TODO(johnlenz): tighten the function signature and remove this check + if (bytes === null) { + return; + } + + if (!goog.isDef(opt_length)) { + opt_length = bytes.length; + } + opt_length = (bytes.length < opt_length) ? bytes.length : opt_length; + + var lengthMinusBlock = opt_length - this.blockSize; + var n = 0; + // Using local instead of member variables gives ~5% speedup on Firefox 16. + var buf = this.buf_; + var inbuf = this.inbuf_; + + // The outer while loop should execute at most twice. + while (n < opt_length) { + // When we have no data in the block to top up, we can directly process the + // input buffer (assuming it contains sufficient data). This gives ~25% + // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that + // the data is provided in large chunks (or in multiples of 64 bytes). + if (inbuf === 0) { + while (n <= lengthMinusBlock) { + this.compress_(bytes, n); + n += this.blockSize; + } + } + + if (goog.isString(bytes)) { + while (n < opt_length) { + buf[inbuf] = bytes.charCodeAt(n); + ++inbuf; + ++n; + if (inbuf == this.blockSize) { + this.compress_(buf); + inbuf = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } else { + while (n < opt_length) { + buf[inbuf] = bytes[n]; + ++inbuf; + ++n; + if (inbuf == this.blockSize) { + this.compress_(buf); + inbuf = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } + } + + this.inbuf_ = inbuf; + this.total_ += opt_length; +}; + + +/** @override */ +goog.crypt.Sha1.prototype.digest = function() { + var digest = []; + var totalBits = this.total_ * 8; + + var i; + + // Add pad 0x80 0x00*. + if (this.inbuf_ < 56) { + this.update(this.pad_, 56 - this.inbuf_); + } else { + this.update(this.pad_, this.blockSize - (this.inbuf_ - 56)); + } + + // Add # bits. + for (i = this.blockSize - 1; i >= 56; i--) { + this.buf_[i] = totalBits & 255; + totalBits /= 256; // Don't use bit-shifting here! + } + + this.compress_(this.buf_); + + var n = 0; + for (i = 0; i < 5; i++) { + for (var j = 24; j >= 0; j -= 8) { + digest[n] = (this.chain_[i] >> j) & 255; + ++n; + } + } + + return digest; +}; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.html b/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.html new file mode 100644 index 0000000000..67d1871f78 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.shaX Monte Carlo KATs + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.js b/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.js new file mode 100644 index 0000000000..bb6924ca25 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha12mc_test.js @@ -0,0 +1,130 @@ +/* Warning: These tests take about 8 minutes to run. */ + +goog.provide('goog.crypt.ShaMcTest'); +goog.setTestOnly('goog.crypt.ShaMcTest'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha1'); +goog.require('goog.crypt.Sha224'); +goog.require('goog.crypt.Sha256'); +goog.require('goog.crypt.Sha384'); +goog.require('goog.crypt.Sha512'); + +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function testSha1() { + var sha = new goog.crypt.Sha1(); + var initial_state = 'Sha1'; + var count = 1638; + var state = goog.crypt.stringToByteArray(initial_state); + var digest; + for (var i = 0; i < count; i++) { + sha.reset(); + sha.update(state); + digest = sha.digest(); + state = goog.array.concat(digest, state); + } + assertEquals(32764, state.length); + assertEquals('9da05831d6441141b62545eb4bf3bcc92b8c8276', + goog.crypt.byteArrayToHex(digest)); + sha.reset(); + for (var i = 0; i < (32764 + 10); i++) { + sha.update(state, i); + } + assertEquals('70a4f1a7b523d9989a1cfd0f512a906abbfefe5f', + goog.crypt.byteArrayToHex(sha.digest())); +} + +function testSha224() { + var sha = new goog.crypt.Sha224(); + var initial_state = 'Sha224'; + var count = 1170; + var state = goog.crypt.stringToByteArray(initial_state); + var digest; + for (var i = 0; i < count; i++) { + sha.reset(); + sha.update(state); + digest = sha.digest(); + state = goog.array.concat(digest, state); + } + assertEquals(32766, state.length); + assertEquals('c7636f5369a057fde53f3a70cb2880795a35af53db38ed8a04cbcbfe', + goog.crypt.byteArrayToHex(digest)); + sha.reset(); + for (var i = 0; i < (32766 + 10); i++) { + sha.update(state, i); + } + assertEquals('69f7e71bbf9a7bd15832fa77e09cbe458dcea284ddb00a69eb3ed78a', + goog.crypt.byteArrayToHex(sha.digest())); +} + +function testSha256() { + var sha = new goog.crypt.Sha256(); + var initial_state = 'Sha256'; + var count = 1024; + var state = goog.crypt.stringToByteArray(initial_state); + var digest; + for (var i = 0; i < count; i++) { + sha.reset(); + sha.update(state); + digest = sha.digest(); + state = goog.array.concat(digest, state); + } + assertEquals(32774, state.length); + assertEquals('bdc98db7476b58c33161211099b02c27da6bed3959a8b1d4e600f4d628ba0200', + goog.crypt.byteArrayToHex(digest)); + sha.reset(); + for (var i = 0; i < (32774 + 10); i++) { + sha.update(state, i); + } + assertEquals('26cc26f6429ce20c1b537d67cc288231a18de2a258d8bf529751439cd7c71d37', + goog.crypt.byteArrayToHex(sha.digest())); +} + +function testSha384() { + var sha = new goog.crypt.Sha384(); + var initial_state = 'Sha384'; + var count = 682; + var state = goog.crypt.stringToByteArray(initial_state); + var digest; + for (var i = 0; i < count; i++) { + sha.reset(); + sha.update(state); + digest = sha.digest(); + state = goog.array.concat(digest, state); + } + assertEquals(32742, state.length); + assertEquals('1e7017a365fd31f6c439efb3eabef783a1e09ebcbb357bc4c9aac5fa9d731a167ee8105cd1c76159a1c27c56c5d1bc8c', + goog.crypt.byteArrayToHex(digest)); + sha.reset(); + for (var i = 0; i < (32742 + 10); i++) { + sha.update(state, i); + } + assertEquals('0cd4695e15f9089e767b2866e1728588d5cece4ad13e4943aa5bd5f9debbe133e2fac302851a2e90e13c318ace25fbb8', + goog.crypt.byteArrayToHex(sha.digest())); +} + +function testSha512() { + var sha = new goog.crypt.Sha512(); + var initial_state = 'Sha512'; + var count = 512; + var state = goog.crypt.stringToByteArray(initial_state); + var digest; + for (var i = 0; i < count; i++) { + sha.reset(); + sha.update(state); + digest = sha.digest(); + state = goog.array.concat(digest, state); + } + assertEquals(32774, state.length); + assertEquals('5c9c25961a9171e2a79b9be65e05ce238752e7bfbaf3696c6ed63b8ee2735315d2cb58bf70a5f08dd70ecab029bd0725dcdd84dacd063ea9148cb3e5d7fa948a', + goog.crypt.byteArrayToHex(digest)); + sha.reset(); + for (var i = 0; i < (32774 + 10); i++) { + sha.update(state, i); + } + assertEquals('10a310e050cf9b2e9d4fc3c0a8f8e183c158ff28c23a42fd5b7777f449cfbe92655eee2bb42fb47c6900e001153b74a5db777b9b2d1543dc30fe98face94f106', + goog.crypt.byteArrayToHex(sha.digest())); +} + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha1_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/sha1_perf.html new file mode 100644 index 0000000000..b26c155340 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha1_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha1 + + + + + +

Closure Performance Tests - goog.crypt.Sha1

+

+User-agent: + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.html b/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.html new file mode 100644 index 0000000000..e679016dbf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.sha1 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.js b/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.js new file mode 100644 index 0000000000..771b3d022f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha1_test.js @@ -0,0 +1,118 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Sha1Test'); +goog.setTestOnly('goog.crypt.Sha1Test'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha1'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function testBasicOperations() { + var sha1 = new goog.crypt.Sha1(); + goog.crypt.hashTester.runBasicTests(sha1); +} + +function testBlockOperations() { + var sha1 = new goog.crypt.Sha1(); + goog.crypt.hashTester.runBlockTests(sha1, 64); +} + +function testHashing() { + // Test vectors from: + // csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + + // Empty stream. + var sha1 = new goog.crypt.Sha1(); + assertEquals('da39a3ee5e6b4b0d3255bfef95601890afd80709', + goog.crypt.byteArrayToHex(sha1.digest())); + + // Test one-block message. + sha1.reset(); + sha1.update([0x61, 0x62, 0x63]); + assertEquals('a9993e364706816aba3e25717850c26c9cd0d89d', + goog.crypt.byteArrayToHex(sha1.digest())); + + // Test multi-block message. + sha1.reset(); + sha1.update('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'); + assertEquals('84983e441c3bd26ebaae4aa1f95129e5e54670f1', + goog.crypt.byteArrayToHex(sha1.digest())); + + // The following test might cause timeouts on IE7. + if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('8')) { + // Test long message. + var thousandAs = []; + for (var i = 0; i < 1000; ++i) { + thousandAs[i] = 0x61; + } + sha1.reset(); + for (var i = 0; i < 1000; ++i) { + sha1.update(thousandAs); + } + assertEquals('34aa973cd4c4daa4f61eeb2bdbad27316534016f', + goog.crypt.byteArrayToHex(sha1.digest())); + } + + // Test standard message. + sha1.reset(); + sha1.update('The quick brown fox jumps over the lazy dog'); + assertEquals('2fd4e1c67a2d28fced849ee1bb76e7391b93eb12', + goog.crypt.byteArrayToHex(sha1.digest())); + + sha1.reset(); + sha1.update(goog.string.repeat('a', 1024)); + assertEquals('8eca554631df9ead14510e1a70ae48c70f9b9384', + goog.crypt.byteArrayToHex(sha1.digest())); + +} + +function testLength() { + var sha = new goog.crypt.Sha1(); + + // Test that truncating a message works. + sha.reset(); + sha.update('abc'); + assertEquals('a9993e364706816aba3e25717850c26c9cd0d89d', + goog.crypt.byteArrayToHex(sha.digest())); + sha.reset(); + sha.update('abcde', 3); + assertEquals('a9993e364706816aba3e25717850c26c9cd0d89d', + goog.crypt.byteArrayToHex(sha.digest())); + + // Test that lengths work correctly. + var message = goog.crypt.hexToByteArray( + 'd9b28b643d16efc8a17a532c05deb79069421bf4cda67f58310ae3bc956e4720' + + 'f9d2ab845d360fe8c19a734c25fed7b089623b14edc69f78512a03dcb58e6740'); + + // Lengths from 0 to 64. + sha.reset(); + for (var i = 0; i < 64; i++) { + sha.update(message, i); + } + assertElementsEquals(goog.crypt.hexToByteArray( + '04df2fcf7b12b7735e0a3d05d9723702aa70de30'), + sha.digest()); + + // Lengths from 0 to 71 to include message overrun cases. + sha.reset(); + for (var i = 0; i < 71; i++) { + sha.update(message, i); + } + assertElementsEquals(goog.crypt.hexToByteArray( + 'f62df7546b7f70351a5c25bfd9e77ba90ca697f4'), + sha.digest()); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha2.js b/mapbox-gl/closure-library/closure/goog/crypt/sha2.js new file mode 100644 index 0000000000..428f3a3c55 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha2.js @@ -0,0 +1,515 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for SHA-2 cryptographic hash. + * + * Variable names follow the notation in FIPS PUB 180-3: + * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf. + * + * Some code similar to SHA1 are borrowed from sha1.js written by mschilder@. + * + */ + +goog.provide('goog.crypt.Sha2'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.crypt.Hash'); + + + +/** + * SHA-2 cryptographic hash constructor. + * This constructor should not be used directly to create the object. Rather, + * one should use the constructor of the sub-classes. + * @param {number} numHashBlocks The size of output in 16-byte blocks. + * @param {!Array} initHashBlocks The hash-specific initialization + * @constructor + * @extends {goog.crypt.Hash} + * @struct + */ +goog.crypt.Sha2 = function(numHashBlocks, initHashBlocks) { + goog.crypt.Sha2.base(this, 'constructor'); + + this.blockSize = goog.crypt.Sha2.BLOCKSIZE_; + + /** + * A chunk holding the currently processed message bytes. Once the chunk has + * 64 bytes, we feed it into computeChunk_ function and reset this.chunk_. + * @private {!Array|!Uint8Array} + */ + this.chunk_ = goog.global['Uint8Array'] ? + new Uint8Array(this.blockSize) : new Array(this.blockSize); + + /** + * Current number of bytes in this.chunk_. + * @private {number} + */ + this.inChunk_ = 0; + + /** + * Total number of bytes in currently processed message. + * @private {number} + */ + this.total_ = 0; + + + /** + * Holds the previous values of accumulated hash a-h in the computeChunk_ + * function. + * @private {!Array|!Int32Array} + */ + this.hash_ = []; + + /** + * The number of output hash blocks (each block is 4 bytes long). + * @private {number} + */ + this.numHashBlocks_ = numHashBlocks; + + /** + * @private {!Array} initHashBlocks + */ + this.initHashBlocks_ = initHashBlocks; + + /** + * Temporary array used in chunk computation. Allocate here as a + * member rather than as a local within computeChunk_() as a + * performance optimization to reduce the number of allocations and + * reduce garbage collection. + * @private {!Int32Array|!Array} + */ + this.w_ = goog.global['Int32Array'] ? new Int32Array(64) : new Array(64); + + if (!goog.isDef(goog.crypt.Sha2.Kx_)) { + // This is the first time this constructor has been called. + if (goog.global['Int32Array']) { + // Typed arrays exist + goog.crypt.Sha2.Kx_ = new Int32Array(goog.crypt.Sha2.K_); + } else { + // Typed arrays do not exist + goog.crypt.Sha2.Kx_ = goog.crypt.Sha2.K_; + } + } + + this.reset(); +}; +goog.inherits(goog.crypt.Sha2, goog.crypt.Hash); + + +/** + * The block size + * @private {number} + */ +goog.crypt.Sha2.BLOCKSIZE_ = 512 / 8; + + +/** + * Contains data needed to pad messages less than BLOCK_SIZE_ bytes. + * @private {!Array} + */ +goog.crypt.Sha2.PADDING_ = goog.array.concat(128, + goog.array.repeat(0, goog.crypt.Sha2.BLOCKSIZE_ - 1)); + + +/** @override */ +goog.crypt.Sha2.prototype.reset = function() { + this.inChunk_ = 0; + this.total_ = 0; + this.hash_ = goog.global['Int32Array'] ? + new Int32Array(this.initHashBlocks_) : + goog.array.clone(this.initHashBlocks_); +}; + + +/** Helper function to precompute a message schedule. + * + * TODO(dlg): This is essentially static... + * + * @param {!Array|Uint8Array|string} buf Data used for the update. + * @param {number=} opt_offset Optional offset into the data. + * + * @return {!Array|Int32Array} w A 64-element array of uint32s, representing the + * prescheduled message, with round constants already added. + */ +goog.crypt.Sha2.prototype.preschedule = function(buf, opt_offset) { + if (!opt_offset) { + opt_offset = 0; + } + + // Divide the chunk into 16 32-bit-words. + var w = goog.global['Int32Array'] ? new Int32Array(64) : new Array(64); + var i; + // get 16 big endian words + if (goog.isString(buf)) { + for (i = 0; i < 16; i++) { + w[i] = (((buf.charCodeAt(opt_offset) << 24) | + (buf.charCodeAt(opt_offset + 1) << 16) | + (buf.charCodeAt(opt_offset + 2) << 8) | + (buf.charCodeAt(opt_offset + 3))) & 0xffffffff) | 0; + opt_offset += 4; + } + } else { + for (i = 0; i < 16; i++) { + w[i] = (((buf[opt_offset] << 24) | + (buf[opt_offset + 1] << 16) | + (buf[opt_offset + 2] << 8) | + (buf[opt_offset + 3])) & 0xffffffff) | 0; + opt_offset += 4; + } + } + for (i = 16; i < 64; i++) { + var w_15 = w[i - 15] | 0; + var s0 = ((((w_15 >>> 7) | (w_15 << 25)) ^ + ((w_15 >>> 18) | (w_15 << 14)) ^ + (w_15 >>> 3)) & 0xffffffff) | 0; + var w_2 = w[i - 2] | 0; + var s1 = ((((w_2 >>> 17) | (w_2 << 15)) ^ + ((w_2 >>> 19) | (w_2 << 13)) ^ + (w_2 >>> 10)) & 0xffffffff) | 0; + + // As a performance optimization, construct the sum a pair at a time + // with casting to integer (bitwise OR) to eliminate unnecessary + // double<->integer conversions. + var partialSum1 = (((w[i - 16] | 0) + s0) & 0xffffffff) | 0; + var partialSum2 = (((w[i - 7] | 0) + s1) & 0xffffffff) | 0; + w[i] = (partialSum1 + partialSum2) | 0; + } + for (i = 0; i < 64; i++) { + w[i] = (w[i] + (goog.crypt.Sha2.Kx_[i] | 0) | 0); + } + return w; +}; + + +/** + * Update this goog.crypt.Sha2 instance's chaining value using a precomputed + * schedule. Increment the total digested bytes by 64. + * + * @param {Array.} precomputed_schedule An SHA2-32b schedule + * precomputed by goog.crypt.Sha2.preschedule. + */ +goog.crypt.Sha2.prototype.scheduledUpdate = function(precomputed_schedule) { + var w = precomputed_schedule; + + var a = this.hash_[0] | 0; + var b = this.hash_[1] | 0; + var c = this.hash_[2] | 0; + var d = this.hash_[3] | 0; + var e = this.hash_[4] | 0; + var f = this.hash_[5] | 0; + var g = this.hash_[6] | 0; + var h = this.hash_[7] | 0; + for (var i = 0; i < 64; i++) { + var S0 = ((a >>> 2) | (a << 30)) ^ + ((a >>> 13) | (a << 19)) ^ + ((a >>> 22) | (a << 10)); + var maj = ((a & b) ^ (a & c) ^ (b & c)); + var t2 = (S0 + maj) | 0; + var S1 = ((e >>> 6) | (e << 26)) ^ + ((e >>> 11) | (e << 21)) ^ + ((e >>> 25) | (e << 7)); + var ch = ((e & f) ^ ((~ e) & g)); + + var partialSum1 = (h + S1) | 0; + var partialSum3 = (ch + (w[i] | 0)) | 0; + var t1 = (partialSum1 + partialSum3) | 0; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + this.total_ += this.blockSize; + + this.hash_[0] = (this.hash_[0] + a) | 0; + this.hash_[1] = (this.hash_[1] + b) | 0; + this.hash_[2] = (this.hash_[2] + c) | 0; + this.hash_[3] = (this.hash_[3] + d) | 0; + this.hash_[4] = (this.hash_[4] + e) | 0; + this.hash_[5] = (this.hash_[5] + f) | 0; + this.hash_[6] = (this.hash_[6] + g) | 0; + this.hash_[7] = (this.hash_[7] + h) | 0; +}; + + +/** + * Helper function to compute the hashes for a given 512-bit message chunk. + * @param {!Array|Uint8Array|string} buf Data used for the update. + * @param {number=} opt_offset Optional offset into the data. + * + * @private + */ +goog.crypt.Sha2.prototype.computeChunk_ = function(buf, opt_offset) { + // Performance notes: + // - Sum numbers pairwise and cast to integer using | after to avoid + // unnecessary double<->integer conversions. + // - The message schedule is updated "in-place" to reduce the amount + // of garbage that has to be collected. + + if (!opt_offset) { + opt_offset = 0; + } + + // The message schedule. + var w = this.w_; + var i; + // Divide the chunk into 16 32-bit-words. + if (goog.isString(buf)) { + for (i = 0; i < 16; i++) { + // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS + // have a bug that turns the post-increment ++ operator into pre-increment + // during JIT compilation. + // TODO(someone): Bug resolved 2013-02-27 at: + // https://bugs.webkit.org/show_bug.cgi?id=109036, + // https://bugs.webkit.org/show_bug.cgi?id=110991 + // (Is this still needed? If there's no significant performance advantage, + // current code is clearer.) + w[i] = (buf.charCodeAt(opt_offset) << 24) | + (buf.charCodeAt(opt_offset + 1) << 16) | + (buf.charCodeAt(opt_offset + 2) << 8) | + (buf.charCodeAt(opt_offset + 3)); + opt_offset += 4; + } + } else { + for (i = 0; i < 16; i++) { + w[i] = (buf[opt_offset] << 24) | + (buf[opt_offset + 1] << 16) | + (buf[opt_offset + 2] << 8) | + (buf[opt_offset + 3]); + opt_offset += 4; + } + } + + var a = this.hash_[0] | 0; + var b = this.hash_[1] | 0; + var c = this.hash_[2] | 0; + var d = this.hash_[3] | 0; + var e = this.hash_[4] | 0; + var f = this.hash_[5] | 0; + var g = this.hash_[6] | 0; + var h = this.hash_[7] | 0; + // Do steps 0-16. + for (i = 0; i < 16; i++) { + var S0 = ((a >>> 2) | (a << 30)) ^ + ((a >>> 13) | (a << 19)) ^ + ((a >>> 22) | (a << 10)); + var maj = ((a & b) ^ (a & c) ^ (b & c)); + var t2 = (S0 + maj) | 0; + var S1 = ((e >>> 6) | (e << 26)) ^ + ((e >>> 11) | (e << 21)) ^ + ((e >>> 25) | (e << 7)); + var ch = ((e & f) ^ ((~ e) & g)); + + var partialSum1 = (h + S1) | 0; + var partialSum2 = (ch + (goog.crypt.Sha2.Kx_[i] | 0)) | 0; + var partialSum3 = (partialSum2 + (w[i] | 0)) | 0; + var t1 = (partialSum1 + partialSum3) | 0; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + // Do steps 16-64. + for (i = 16; i < 64; i++) { + var w_15 = w[(i - 15) & 15] | 0; + var s0 = ((w_15 >>> 7) | (w_15 << 25)) ^ + ((w_15 >>> 18) | (w_15 << 14)) ^ + (w_15 >>> 3); + var w_2 = w[(i - 2) & 15] | 0; + var s1 = ((w_2 >>> 17) | (w_2 << 15)) ^ + ((w_2 >>> 19) | (w_2 << 13)) ^ + (w_2 >>> 10); + + var partialSum1 = ((w[(i - 16) & 15] | 0) + s0) | 0; + var partialSum2 = ((w[(i - 7) & 15] | 0) + s1) | 0; + w[i & 15] = (partialSum1 + partialSum2) | 0; + + var S0 = ((a >>> 2) | (a << 30)) ^ + ((a >>> 13) | (a << 19)) ^ + ((a >>> 22) | (a << 10)); + var maj = ((a & b) ^ (a & c) ^ (b & c)); + var t2 = (S0 + maj) | 0; + var S1 = ((e >>> 6) | (e << 26)) ^ + ((e >>> 11) | (e << 21)) ^ + ((e >>> 25) | (e << 7)); + var ch = ((e & f) ^ ((~ e) & g)); + + var partialSum1 = (h + S1) | 0; + var partialSum2 = (ch + (goog.crypt.Sha2.Kx_[i] | 0)) | 0; + var partialSum3 = (partialSum2 + (w[i & 15] | 0)) | 0; + var t1 = (partialSum1 + partialSum3) | 0; + + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + + this.hash_[0] = (this.hash_[0] + a) | 0; + this.hash_[1] = (this.hash_[1] + b) | 0; + this.hash_[2] = (this.hash_[2] + c) | 0; + this.hash_[3] = (this.hash_[3] + d) | 0; + this.hash_[4] = (this.hash_[4] + e) | 0; + this.hash_[5] = (this.hash_[5] + f) | 0; + this.hash_[6] = (this.hash_[6] + g) | 0; + this.hash_[7] = (this.hash_[7] + h) | 0; +}; + + +/** @override */ +goog.crypt.Sha2.prototype.update = function(bytes, opt_length) { + if (!goog.isDef(opt_length)) { + opt_length = bytes.length; + } + opt_length = (bytes.length < opt_length) ? bytes.length : opt_length; + // Process the message from left to right up to |opt_length| bytes. + // When we get a 512-bit chunk, compute the hash of it and reset + // this.chunk_. If the message isn't a multiple of 64 bytes (512 bits) + // we store the partial chunk in this.chunk_, and the index of the end + // of the partial chunk into this.inChunk_. + var n = 0; + var inbuf = this.inChunk_; + var buf = this.chunk_; + var lengthMinusBlock = opt_length - this.blockSize; + // The outer while loop should execute at most twice. + while (n < opt_length) { + // When we have no data in the block to top up, we can directly process the + // input buffer (assuming it contains sufficient data). This gives ~25% + // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that + // the data is provided in large chunks (or in multiples of 64 bytes). + if (inbuf == 0) { + while (n <= lengthMinusBlock) { + this.computeChunk_(bytes, n); + n += this.blockSize; + } + } + + if (goog.isString(bytes)) { + while (n < opt_length) { + buf[inbuf] = bytes.charCodeAt(n); + ++inbuf; + ++n; + if (inbuf == this.blockSize) { + this.computeChunk_(buf); + inbuf = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } else { + while (n < opt_length) { + buf[inbuf] = bytes[n]; + ++inbuf; + ++n; + if (inbuf == this.blockSize) { + this.computeChunk_(buf); + inbuf = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } + } + + // Save the current position in this.chunk_ to support partial updates. + this.inChunk_ = inbuf; + + // Update the total message bytes processed. + this.total_ += opt_length; +}; + + +/** @override */ +goog.crypt.Sha2.prototype.digest = function() { + var digest = []; + var totalBits = this.total_ * 8; + + // Append pad 0x80 0x00*. + if (this.inChunk_ < 56) { + this.update(goog.crypt.Sha2.PADDING_, 56 - this.inChunk_); + } else { + this.update(goog.crypt.Sha2.PADDING_, + this.blockSize - (this.inChunk_ - 56)); + } + + // Append number of bits as a big-endian 64-bit uint. + for (var i = 63; i >= 56; i--) { + this.chunk_[i] = totalBits & 255; + totalBits /= 256; // Don't use bit-shifting here! + } + this.computeChunk_(this.chunk_); + + // Finally, output the result's digest. + var n = 0; + for (var i = 0; i < this.numHashBlocks_; i++) { + for (var j = 24; j >= 0; j -= 8) { + digest[n++] = ((this.hash_[i] >> j) & 255); + } + } + return digest; +}; + + +/** + * Constants used in SHA-2. + * @const + * @private {!Array} + */ +goog.crypt.Sha2.K_ = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +]; + + +/** + * Sha2.K as an Int32Array if this JS supports typed arrays; otherwise, + * the same array as Sha2.K. + * + * The compiler cannot remove an Int32Array, even if it is not needed + * (There are certain cases where creating an Int32Array is not + * side-effect free). Instead, the first time we construct a Sha2 + * instance, we convert or assign Sha2.K as appropriate. + * @private {undefined|!Array|!Int32Array} + */ +goog.crypt.Sha2.Kx_; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha224.js b/mapbox-gl/closure-library/closure/goog/crypt/sha224.js new file mode 100644 index 0000000000..40c59e9013 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha224.js @@ -0,0 +1,50 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-224 cryptographic hash. + * + * Usage: + * var sha224 = new goog.crypt.Sha224(); + * sha224.update(bytes); + * var hash = sha224.digest(); + * + */ + +goog.provide('goog.crypt.Sha224'); + +goog.require('goog.crypt.Sha2'); + + + +/** + * SHA-224 cryptographic hash constructor. + * + * @constructor + * @extends {goog.crypt.Sha2} + * @final + * @struct + */ +goog.crypt.Sha224 = function() { + goog.crypt.Sha224.base(this, 'constructor', + 7, goog.crypt.Sha224.INIT_HASH_BLOCK_); +}; +goog.inherits(goog.crypt.Sha224, goog.crypt.Sha2); + + +/** @private {!Array} */ +goog.crypt.Sha224.INIT_HASH_BLOCK_ = [ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]; + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha224_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/sha224_perf.html new file mode 100644 index 0000000000..4c39e6440a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha224_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha224 + + + + + +

Closure Performance Tests - goog.crypt.Sha224

+

+User-agent: + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.html b/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.html new file mode 100644 index 0000000000..1a76672c6c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.sha224 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.js b/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.js new file mode 100644 index 0000000000..65f1ce2229 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha224_test.js @@ -0,0 +1,75 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Sha224Test'); +goog.setTestOnly('goog.crypt.Sha224Test'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha224'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); + +function testBasicOperations() { + var sha224 = new goog.crypt.Sha224(); + goog.crypt.hashTester.runBasicTests(sha224); +} + +function testHashing() { + // Some test vectors from: + // csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + + var sha224 = new goog.crypt.Sha224(); + + // NIST one block test vector. + sha224.update(goog.crypt.stringToByteArray('abc')); + assertEquals( + '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7', + goog.crypt.byteArrayToHex(sha224.digest())); + + // NIST multi-block test vector. + sha224.reset(); + sha224.update( + goog.crypt.stringToByteArray( + 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq')); + assertEquals( + '75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525', + goog.crypt.byteArrayToHex(sha224.digest())); + + // Message larger than one block (but less than two). + sha224.reset(); + var biggerThanOneBlock = 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq' + + 'asdfljhr78yasdfljh45opa78sdf' + + '120839414104897aavnasdfafasd'; + assertTrue(biggerThanOneBlock.length > goog.crypt.Sha2.BLOCKSIZE_ && + biggerThanOneBlock.length < 2 * goog.crypt.Sha2.BLOCKSIZE_); + sha224.update(goog.crypt.stringToByteArray(biggerThanOneBlock)); + assertEquals( + '27c9b678012becd6891bac653f355b2d26f63132e840644d565f5dac', + goog.crypt.byteArrayToHex(sha224.digest())); + + // Message larger than two blocks. + sha224.reset(); + var biggerThanTwoBlocks = 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq' + + 'asdfljhr78yasdfljh45opa78sdf' + + '120839414104897aavnasdfafasd' + + 'laasdouvhalacbnalalseryalcla'; + assertTrue(biggerThanTwoBlocks.length > 2 * goog.crypt.Sha2.BLOCKSIZE_); + sha224.update(goog.crypt.stringToByteArray(biggerThanTwoBlocks)); + assertEquals( + '1c2c1455cc984eef6f25ec9d79b1c661b3794887c3d0b24111ed9803', + goog.crypt.byteArrayToHex(sha224.digest())); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha256.js b/mapbox-gl/closure-library/closure/goog/crypt/sha256.js new file mode 100644 index 0000000000..38dafb0710 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha256.js @@ -0,0 +1,49 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-256 cryptographic hash. + * + * Usage: + * var sha256 = new goog.crypt.Sha256(); + * sha256.update(bytes); + * var hash = sha256.digest(); + * + */ + +goog.provide('goog.crypt.Sha256'); + +goog.require('goog.crypt.Sha2'); + + + +/** + * SHA-256 cryptographic hash constructor. + * + * @constructor + * @extends {goog.crypt.Sha2} + * @final + * @struct + */ +goog.crypt.Sha256 = function() { + goog.crypt.Sha256.base(this, 'constructor', + 8, goog.crypt.Sha256.INIT_HASH_BLOCK_); +}; +goog.inherits(goog.crypt.Sha256, goog.crypt.Sha2); + + +/** @private {!Array} */ +goog.crypt.Sha256.INIT_HASH_BLOCK_ = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha256_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/sha256_perf.html new file mode 100644 index 0000000000..0b7922b3f2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha256_perf.html @@ -0,0 +1,40 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha256 + + + + + +

Closure Performance Tests - goog.crypt.Sha256

+

+User-agent: + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.html b/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.html new file mode 100644 index 0000000000..60a5738c45 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.sha256 + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.js b/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.js new file mode 100644 index 0000000000..5249f8607c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha256_test.js @@ -0,0 +1,102 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Sha256Test'); +goog.setTestOnly('goog.crypt.Sha256Test'); + +goog.require('goog.array'); +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha256'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); + +function testBasicOperations() { + var sha256 = new goog.crypt.Sha256(); + goog.crypt.hashTester.runBasicTests(sha256); +} + +function testHashing() { + // Some test vectors from: + // csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + + var sha256 = new goog.crypt.Sha256(); + + // Empty message. + sha256.update([]); + assertEquals( + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + goog.crypt.byteArrayToHex(sha256.digest())); + + // NIST one block test vector. + sha256.reset(); + sha256.update(goog.crypt.stringToByteArray('abc')); + assertEquals( + 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + goog.crypt.byteArrayToHex(sha256.digest())); + + // NIST multi-block test vector. + sha256.reset(); + sha256.update( + goog.crypt.stringToByteArray( + 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq')); + assertEquals( + '248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1', + goog.crypt.byteArrayToHex(sha256.digest())); + + // Message larger than one block (but less than two). + sha256.reset(); + var biggerThanOneBlock = 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq' + + 'asdfljhr78yasdfljh45opa78sdf' + + '120839414104897aavnasdfafasd'; + assertTrue(biggerThanOneBlock.length > goog.crypt.Sha2.BLOCKSIZE_ && + biggerThanOneBlock.length < 2 * goog.crypt.Sha2.BLOCKSIZE_); + sha256.update(goog.crypt.stringToByteArray(biggerThanOneBlock)); + assertEquals( + '390a5035433e46b740600f3117d11ece3c64706dc889106666ac04fe4f458abc', + goog.crypt.byteArrayToHex(sha256.digest())); + + // Message larger than two blocks. + sha256.reset(); + var biggerThanTwoBlocks = 'abcdbcdecdefdefgefghfghighij' + + 'hijkijkljklmklmnlmnomnopnopq' + + 'asdfljhr78yasdfljh45opa78sdf' + + '120839414104897aavnasdfafasd' + + 'laasdouvhalacbnalalseryalcla'; + assertTrue(biggerThanTwoBlocks.length > 2 * goog.crypt.Sha2.BLOCKSIZE_); + sha256.update(goog.crypt.stringToByteArray(biggerThanTwoBlocks)); + assertEquals( + 'd655c513fd347e9be372d891f8bb42895ca310fabf6ead6681ebc66a04e84db5', + goog.crypt.byteArrayToHex(sha256.digest())); +} + +function testPreschedule() { + // Test using prescheduled messages. + var sha = new goog.crypt.Sha256(); + var message = goog.crypt.hexToByteArray( + 'd9b28b643d16efc8a17a532c05deb79069421bf4cda67f58310ae3bc956e4720' + + 'f9d2ab845d360fe8c19a734c25fed7b089623b14edc69f78512a03dcb58e6740'); + + var scheduled = sha.preschedule(message); + sha.scheduledUpdate(scheduled); + var w_scheduled = goog.array.toArray(sha.hash_); + var digest_scheduled = sha.digest(); + + sha.reset(); + sha.update(message); + var w_afterupdate = goog.array.toArray(sha.hash_); + assertElementsEquals(w_scheduled, w_afterupdate); + assertElementsEquals(sha.digest(), digest_scheduled); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit.js b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit.js new file mode 100644 index 0000000000..077b9c4342 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit.js @@ -0,0 +1,550 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for the 64-bit SHA-2 cryptographic hashes. + * + * Variable names follow the notation in FIPS PUB 180-3: + * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf. + * + * This code borrows heavily from the 32-bit SHA2 implementation written by + * Yue Zhang (zysxqn@). + * + * @author fy@google.com (Frank Yellin) + */ + +goog.provide('goog.crypt.Sha2_64bit'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.crypt.Hash'); +goog.require('goog.math.Long'); + + + +/** + * Constructs a SHA-2 64-bit cryptographic hash. + * This class should not be used. Rather, one should use one of its + * subclasses. + * @constructor + * @param {number} numHashBlocks The size of the output in 16-byte blocks + * @param {!Array} initHashBlocks The hash-specific initialization + * vector, as a sequence of sixteen 32-bit numbers. + * @extends {goog.crypt.Hash} + * @struct + */ +goog.crypt.Sha2_64bit = function(numHashBlocks, initHashBlocks) { + goog.crypt.Sha2_64bit.base(this, 'constructor'); + + /** + * The number of bytes that are digested in each pass of this hasher. + * @const {number} + */ + this.blockSize = goog.crypt.Sha2_64bit.BLOCK_SIZE_; + + /** + * A chunk holding the currently processed message bytes. Once the chunk has + * {@code this.blocksize} bytes, we feed it into [@code computeChunk_}. + * @private {!Uint8Array|!Array} + */ + this.chunk_ = goog.isDef(goog.global.Uint8Array) ? + new Uint8Array(goog.crypt.Sha2_64bit.BLOCK_SIZE_) : + new Array(goog.crypt.Sha2_64bit.BLOCK_SIZE_); + + /** + * Current number of bytes in {@code this.chunk_}. + * @private {number} + */ + this.chunkBytes_ = 0; + + /** + * Total number of bytes in currently processed message. + * @private {number} + */ + this.total_ = 0; + + /** + * Holds the previous values of accumulated hash a-h in the + * {@code computeChunk_} function. + * @private {!Array} + */ + this.hash_ = []; + + /** + * The number of blocks of output produced by this hash function, where each + * block is eight bytes long. + * @private {number} + */ + this.numHashBlocks_ = numHashBlocks; + + /** + * Temporary array used in chunk computation. Allocate here as a + * member rather than as a local within computeChunk_() as a + * performance optimization to reduce the number of allocations and + * reduce garbage collection. + * @type {!Array} + * @private + */ + this.w_ = []; + + /** + * The value to which {@code this.hash_} should be reset when this + * Hasher is reset. + * @private @const {!Array} + */ + this.initHashBlocks_ = goog.crypt.Sha2_64bit.toLongArray_(initHashBlocks); + + /** + * If true, we have taken the digest from this hasher, but we have not + * yet reset it. + * + * @private {boolean} + */ + this.needsReset_ = false; + + this.reset(); +}; +goog.inherits(goog.crypt.Sha2_64bit, goog.crypt.Hash); + + +/** + * The number of bytes that are digested in each pass of this hasher. + * @private @const {number} + */ +goog.crypt.Sha2_64bit.BLOCK_SIZE_ = 1024 / 8; + + +/** + * Contains data needed to pad messages less than {@code blocksize} bytes. + * @private {!Array} + */ +goog.crypt.Sha2_64bit.PADDING_ = goog.array.concat( + [0x80], goog.array.repeat(0, goog.crypt.Sha2_64bit.BLOCK_SIZE_ - 1)); + + +/** + * Resets this hash function. + * @override + */ +goog.crypt.Sha2_64bit.prototype.reset = function() { + this.chunkBytes_ = 0; + this.total_ = 0; + this.hash_ = goog.array.clone(this.initHashBlocks_); + this.needsReset_ = false; +}; + + +/** @override */ +goog.crypt.Sha2_64bit.prototype.update = function(message, opt_length) { + var length = goog.isDef(opt_length) ? opt_length : message.length; + + // Make sure this hasher is usable. + if (this.needsReset_) { + throw Error('this hasher needs to be reset'); + } + // Process the message from left to right up to |length| bytes. + // When we get a 512-bit chunk, compute the hash of it and reset + // this.chunk_. The message might not be multiple of 512 bits so we + // might end up with a chunk that is less than 512 bits. We store + // such partial chunk in chunk_ and it will be filled up later + // in digest(). + var chunkBytes = this.chunkBytes_; + + // The input message could be either byte array or string. + if (goog.isString(message)) { + for (var i = 0; i < length; i++) { + var b = message.charCodeAt(i); + if (b > 255) { + throw Error('Characters must be in range [0,255]'); + } + this.chunk_[chunkBytes++] = b; + if (chunkBytes == this.blockSize) { + this.computeChunk_(); + chunkBytes = 0; + } + } + } else if (goog.isArray(message)) { + for (var i = 0; i < length; i++) { + var b = message[i]; + // Hack: b|0 coerces b to an integer, so the last part confirms that + // b has no fractional part. + if (!goog.isNumber(b) || b < 0 || b > 255 || b != (b | 0)) { + throw Error('message must be a byte array'); + } + this.chunk_[chunkBytes++] = b; + if (chunkBytes == this.blockSize) { + this.computeChunk_(); + chunkBytes = 0; + } + } + } else { + throw Error('message must be string or array'); + } + + // Record the current bytes in chunk to support partial update. + this.chunkBytes_ = chunkBytes; + + // Record total message bytes we have processed so far. + this.total_ += length; +}; + + +/** @override */ +goog.crypt.Sha2_64bit.prototype.digest = function() { + if (this.needsReset_) { + throw Error('this hasher needs to be reset'); + } + var totalBits = this.total_ * 8; + + // Append pad 0x80 0x00* until this.chunkBytes_ == 112 + if (this.chunkBytes_ < 112) { + this.update(goog.crypt.Sha2_64bit.PADDING_, 112 - this.chunkBytes_); + } else { + // the rest of this block, plus 112 bytes of next block + this.update(goog.crypt.Sha2_64bit.PADDING_, + this.blockSize - this.chunkBytes_ + 112); + } + + // Append # bits in the 64-bit big-endian format. + for (var i = 127; i >= 112; i--) { + this.chunk_[i] = totalBits & 255; + totalBits /= 256; // Don't use bit-shifting here! + } + this.computeChunk_(); + + // Finally, output the result digest. + var n = 0; + var digest = new Array(8 * this.numHashBlocks_); + for (var i = 0; i < this.numHashBlocks_; i++) { + var block = this.hash_[i]; + var high = block.getHighBits(); + var low = block.getLowBits(); + for (var j = 24; j >= 0; j -= 8) { + digest[n++] = ((high >> j) & 255); + } + for (var j = 24; j >= 0; j -= 8) { + digest[n++] = ((low >> j) & 255); + } + } + + // The next call to this hasher must be a reset + this.needsReset_ = true; + return digest; +}; + + +/** + * Updates this hash by processing the 1024-bit message chunk in this.chunk_. + * @private + */ +goog.crypt.Sha2_64bit.prototype.computeChunk_ = function() { + var chunk = this.chunk_; + var K_ = goog.crypt.Sha2_64bit.K_; + + // Divide the chunk into 16 64-bit-words. + var w = this.w_; + for (var i = 0; i < 16; i++) { + var offset = i * 8; + w[i] = new goog.math.Long( + (chunk[offset + 4] << 24) | (chunk[offset + 5] << 16) | + (chunk[offset + 6] << 8) | (chunk[offset + 7]), + (chunk[offset] << 24) | (chunk[offset + 1] << 16) | + (chunk[offset + 2] << 8) | (chunk[offset + 3])); + + } + + // Extend the w[] array to be the number of rounds. + for (var i = 16; i < 80; i++) { + var s0 = this.sigma0_(w[i - 15]); + var s1 = this.sigma1_(w[i - 2]); + w[i] = this.sum_(w[i - 16], w[i - 7], s0, s1); + } + + var a = this.hash_[0]; + var b = this.hash_[1]; + var c = this.hash_[2]; + var d = this.hash_[3]; + var e = this.hash_[4]; + var f = this.hash_[5]; + var g = this.hash_[6]; + var h = this.hash_[7]; + for (var i = 0; i < 80; i++) { + var S0 = this.Sigma0_(a); + var maj = this.majority_(a, b, c); + var t2 = S0.add(maj); + var S1 = this.Sigma1_(e); + var ch = this.choose_(e, f, g); + var t1 = this.sum_(h, S1, ch, K_[i], w[i]); + h = g; + g = f; + f = e; + e = d.add(t1); + d = c; + c = b; + b = a; + a = t1.add(t2); + } + + this.hash_[0] = this.hash_[0].add(a); + this.hash_[1] = this.hash_[1].add(b); + this.hash_[2] = this.hash_[2].add(c); + this.hash_[3] = this.hash_[3].add(d); + this.hash_[4] = this.hash_[4].add(e); + this.hash_[5] = this.hash_[5].add(f); + this.hash_[6] = this.hash_[6].add(g); + this.hash_[7] = this.hash_[7].add(h); +}; + + +/** + * Calculates the SHA2 64-bit sigma0 function. + * rotateRight(value, 1) ^ rotateRight(value, 8) ^ (value >>> 7) + * + * @private + * @param {!goog.math.Long} value + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.sigma0_ = function(value) { + var valueLow = value.getLowBits(); + var valueHigh = value.getHighBits(); + // Implementation note: We purposely do not use the shift operations defined + // in goog.math.Long. Inlining the code for specific values of shifting and + // not generating the intermediate results doubles the speed of this code. + var low = (valueLow >>> 1) ^ (valueHigh << 31) ^ + (valueLow >>> 8) ^ (valueHigh << 24) ^ + (valueLow >>> 7) ^ (valueHigh << 25); + var high = (valueHigh >>> 1) ^ (valueLow << 31) ^ + (valueHigh >>> 8) ^ (valueLow << 24) ^ + (valueHigh >>> 7); + return new goog.math.Long(low, high); +}; + + +/** + * Calculates the SHA2 64-bit sigma1 function. + * rotateRight(value, 19) ^ rotateRight(value, 61) ^ (value >>> 6) + * + * @private + * @param {!goog.math.Long} value + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.sigma1_ = function(value) { + var valueLow = value.getLowBits(); + var valueHigh = value.getHighBits(); + // Implementation note: See _sigma0() above + var low = (valueLow >>> 19) ^ (valueHigh << 13) ^ + (valueHigh >>> 29) ^ (valueLow << 3) ^ + (valueLow >>> 6) ^ (valueHigh << 26); + var high = (valueHigh >>> 19) ^ (valueLow << 13) ^ + (valueLow >>> 29) ^ (valueHigh << 3) ^ + (valueHigh >>> 6); + return new goog.math.Long(low, high); +}; + + +/** + * Calculates the SHA2 64-bit Sigma0 function. + * rotateRight(value, 28) ^ rotateRight(value, 34) ^ rotateRight(value, 39) + * + * @private + * @param {!goog.math.Long} value + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.Sigma0_ = function(value) { + var valueLow = value.getLowBits(); + var valueHigh = value.getHighBits(); + // Implementation note: See _sigma0() above + var low = (valueLow >>> 28) ^ (valueHigh << 4) ^ + (valueHigh >>> 2) ^ (valueLow << 30) ^ + (valueHigh >>> 7) ^ (valueLow << 25); + var high = (valueHigh >>> 28) ^ (valueLow << 4) ^ + (valueLow >>> 2) ^ (valueHigh << 30) ^ + (valueLow >>> 7) ^ (valueHigh << 25); + return new goog.math.Long(low, high); +}; + + +/** + * Calculates the SHA2 64-bit Sigma1 function. + * rotateRight(value, 14) ^ rotateRight(value, 18) ^ rotateRight(value, 41) + * + * @private + * @param {!goog.math.Long} value + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.Sigma1_ = function(value) { + var valueLow = value.getLowBits(); + var valueHigh = value.getHighBits(); + // Implementation note: See _sigma0() above + var low = (valueLow >>> 14) ^ (valueHigh << 18) ^ + (valueLow >>> 18) ^ (valueHigh << 14) ^ + (valueHigh >>> 9) ^ (valueLow << 23); + var high = (valueHigh >>> 14) ^ (valueLow << 18) ^ + (valueHigh >>> 18) ^ (valueLow << 14) ^ + (valueLow >>> 9) ^ (valueHigh << 23); + return new goog.math.Long(low, high); +}; + + +/** + * Calculates the SHA-2 64-bit choose function. + * + * This function uses {@code value} as a mask to choose bits from either + * {@code one} if the bit is set or {@code two} if the bit is not set. + * + * @private + * @param {!goog.math.Long} value + * @param {!goog.math.Long} one + * @param {!goog.math.Long} two + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.choose_ = function(value, one, two) { + var valueLow = value.getLowBits(); + var valueHigh = value.getHighBits(); + return new goog.math.Long( + (valueLow & one.getLowBits()) | (~valueLow & two.getLowBits()), + (valueHigh & one.getHighBits()) | (~valueHigh & two.getHighBits())); +}; + + +/** + * Calculates the SHA-2 64-bit majority function. + * This function returns, for each bit position, the bit held by the majority + * of its three arguments. + * + * @private + * @param {!goog.math.Long} one + * @param {!goog.math.Long} two + * @param {!goog.math.Long} three + * @return {!goog.math.Long} + */ +goog.crypt.Sha2_64bit.prototype.majority_ = function(one, two, three) { + return new goog.math.Long( + (one.getLowBits() & two.getLowBits()) | + (two.getLowBits() & three.getLowBits()) | + (one.getLowBits() & three.getLowBits()), + (one.getHighBits() & two.getHighBits()) | + (two.getHighBits() & three.getHighBits()) | + (one.getHighBits() & three.getHighBits())); +}; + + +/** + * Adds two or more goog.math.Long values. + * + * @private + * @param {!goog.math.Long} one first summand + * @param {!goog.math.Long} two second summand + * @param {...goog.math.Long} var_args more arguments to sum + * @return {!goog.math.Long} The resulting sum. + */ +goog.crypt.Sha2_64bit.prototype.sum_ = function(one, two, var_args) { + // The low bits may be signed, but they represent a 32-bit unsigned quantity. + // We must be careful to normalize them. + // This doesn't matter for the high bits. + // Implementation note: Performance testing shows that this method runs + // fastest when the first two arguments are pulled out of the loop. + var low = (one.getLowBits() ^ 0x80000000) + (two.getLowBits() ^ 0x80000000); + var high = one.getHighBits() + two.getHighBits(); + for (var i = arguments.length - 1; i >= 2; --i) { + low += arguments[i].getLowBits() ^ 0x80000000; + high += arguments[i].getHighBits(); + } + // Because of the ^0x80000000, each value we added is 0x80000000 too small. + // Add arguments.length * 0x80000000 to the current sum. We can do this + // quickly by adding 0x80000000 to low when the number of arguments is + // odd, and adding (number of arguments) >> 1 to high. + if (arguments.length & 1) { + low += 0x80000000; + } + high += arguments.length >> 1; + + // If low is outside the range [0, 0xFFFFFFFF], its overflow or underflow + // should be added to high. We don't actually need to modify low or + // normalize high because the goog.math.Long constructor already does that. + high += Math.floor(low / 0x100000000); + return new goog.math.Long(low, high); +}; + + +/** + * Converts an array of 32-bit integers into an array of goog.math.Long + * elements. + * + * @private + * @param {!Array} values An array of 32-bit numbers. Its length + * must be even. Each pair of numbers represents a 64-bit integer + * in big-endian order + * @return {!Array} + */ +goog.crypt.Sha2_64bit.toLongArray_ = function(values) { + goog.asserts.assert(values.length % 2 == 0); + var result = []; + for (var i = 0; i < values.length; i += 2) { + result.push(new goog.math.Long(values[i + 1], values[i])); + } + return result; +}; + + +/** + * Fixed constants used in SHA-512 variants. + * + * These values are from Section 4.2.3 of + * http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + * @const + * @private {!Array} + */ +goog.crypt.Sha2_64bit.K_ = goog.crypt.Sha2_64bit.toLongArray_([ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, + 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, + 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, + 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, + 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, + 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, + 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, + 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, + 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, + 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, + 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5, + 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, + 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4, + 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, + 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70, + 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, + 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, + 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, + 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, + 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, + 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, + 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, + 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, + 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, + 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, + 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, + 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, + 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, + 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b, + 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, + 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178, + 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, + 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, + 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, + 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, + 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 +]); diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.html b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.html new file mode 100644 index 0000000000..1637e8a24c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.html @@ -0,0 +1,22 @@ + + + + + + + Closure Unit Tests - goog.crypt.sha2 64-bit hashes + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.js b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.js new file mode 100644 index 0000000000..2320946871 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha2_64bit_test.js @@ -0,0 +1,278 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.crypt.Sha2_64bit_test'); +goog.setTestOnly('goog.crypt.Sha2_64bit_test'); + +goog.require('goog.array'); +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha384'); +goog.require('goog.crypt.Sha512'); +goog.require('goog.crypt.Sha512_256'); +goog.require('goog.crypt.hashTester'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + + +/** + * Each object in the test vector array is a source text and one or more + * hashes of that source text. The source text is either a string or a + * byte array. + *

+ * All hash values, except for the empty string, are from public sources: + * csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + * csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA384.pdf + * csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_256.pdf + * csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA2_Additional.pdf + * en.wikipedia.org/wiki/SHA-2#Examples_of_SHA-2_variants + */ +var TEST_VECTOR = [ + { + // Make sure the algorithm correctly handles the empty string + source: + '', + 512: + 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce' + + '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' + }, + { + source: + 'abc', + 512: + 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' + + '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', + 384: + 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163' + + '1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7', + 256: + '53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23' + }, + { + source: + 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn' + + 'hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', + 512: + '8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018' + + '501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909', + 384: + '09330c33f71147e83d192fc782cd1b4753111b173b3b05d2' + + '2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039', + 256: + '3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a' + }, + { + source: + 'The quick brown fox jumps over the lazy dog', + 512: + '07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb64' + + '2e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6', + 384: + 'ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c49' + + '4011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1', + 256: + 'dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d' + } +]; + + +/** + * For each integer key N, the value is the SHA-512 value of a string + * consisting of N repetitions of the character 'a'. + */ +var TEST_FENCEPOST_VECTOR = { + 110: + 'c825949632e509824543f7eaf159fb6041722fce3c1cdcbb613b3d37ff107c51' + + '9417baac32f8e74fe29d7f4823bf6886956603dca5354a6ed6e4a542e06b7d28', + 111: + 'fa9121c7b32b9e01733d034cfc78cbf67f926c7ed83e82200ef8681819692176' + + '0b4beff48404df811b953828274461673c68d04e297b0eb7b2b4d60fc6b566a2', + 112: + 'c01d080efd492776a1c43bd23dd99d0a2e626d481e16782e75d54c2503b5dc32' + + 'bd05f0f1ba33e568b88fd2d970929b719ecbb152f58f130a407c8830604b70ca', + 113: + '55ddd8ac210a6e18ba1ee055af84c966e0dbff091c43580ae1be703bdb85da31' + + 'acf6948cf5bd90c55a20e5450f22fb89bd8d0085e39f85a86cc46abbca75e24d' +}; + + +/** + * Simple sanity tests for hash functions. + */ +function testBasicOperations() { + var sha512 = new goog.crypt.Sha512(); + goog.crypt.hashTester.runBasicTests(sha512); + + var sha384 = new goog.crypt.Sha384(); + goog.crypt.hashTester.runBasicTests(sha384); + + var sha256 = new goog.crypt.Sha512_256(); + goog.crypt.hashTester.runBasicTests(sha256); +} + + +/** + * Function called by the actual testers to ensure that specific strings + * hash to specific published values. + * + * Each item in the vector has a "source" and one or more additional keys. + * If the item has a key matching the key argument passed to this + * function, it is the expected value of the hash function. + * + * @param {!goog.crypt.Sha2_64bit} hasher The hasher to test + * @param {number} length The length of the resulting hash, in bits. + * Also the key to use in TEST_VECTOR for the expected hash value + */ +function hashGoldenTester(hasher, length) { + goog.array.forEach(TEST_VECTOR, function(data) { + hasher.update(data.source); + var digest = hasher.digest(); + assertEquals('Hash digest has the wrong length', length, digest.length * 8); + if (data[length]) { + // We're given an expected value + var expected = goog.crypt.hexToByteArray(data[length]); + assertElementsEquals( + 'Wrong result for hash' + length + '(\'' + data.source + '\')', + expected, digest); + } + hasher.reset(); + }); +} + + +/** Test that Sha512() returns the published values */ +function testHashing512() { + hashGoldenTester(new goog.crypt.Sha512(), 512); +} + + +/** Test that Sha384 returns the published values */ +function testHashing384() { + hashGoldenTester(new goog.crypt.Sha384(), 384); +} + + +/** Test that Sha512_256 returns the published values */ +function testHashing256() { + hashGoldenTester(new goog.crypt.Sha512_256(), 256); +} + + +/** Test that the opt_length works */ +function testHashing_optLength() { + var hasher = new goog.crypt.Sha512(); + hasher.update('1234567890'); + var digest1 = hasher.digest(); + hasher.reset(); + hasher.update('12345678901234567890', 10); + var digest2 = hasher.digest(); + assertElementsEquals(digest1, digest2); +} + + +/** + * Make sure that we correctly handle strings whose length is 110-113. + * This is the area where we are likely to hit fencepost errors in the padding + * code. + */ +function testFencepostErrors() { + var hasher = new goog.crypt.Sha512(); + A64 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + A128 = A64 + A64; + for (var i = 110; i <= 113; i++) { + hasher.update(A128, i); + var digest = hasher.digest(); + var expected = goog.crypt.hexToByteArray(TEST_FENCEPOST_VECTOR[i]); + assertElementsEquals('Fencepost ' + i, expected, digest); + hasher.reset(); + } +} + + +/** Test one really large string using SHA512 */ +function testHashing512Large() { + // This test tends to time out on IE7. + if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('8')) { + var hasher = new goog.crypt.Sha512(); + hasher.update(goog.array.repeat(0, 1000000)); + var digest = hasher.digest(); + var expected = goog.crypt.hexToByteArray( + 'ce044bc9fd43269d5bbc946cbebc3bb711341115cc4abdf2edbc3ff2c57ad4b1' + + '5deb699bda257fea5aef9c6e55fcf4cf9dc25a8c3ce25f2efe90908379bff7ed'); + assertElementsEquals(expected, digest); + } +} + + +/** Check that the code throws an error for bad input */ +function testBadInput_nullNotAllowed() { + var hasher = new goog.crypt.Sha512(); + assertThrows('Null input not allowed', function() { + hasher.update({}); + }); +} + +function testBadInput_noFloatingPoint() { + var hasher = new goog.crypt.Sha512(); + assertThrows('Floating point not allows', function() { + hasher.update([1, 2, 3, 4, 4.5]); + }); +} + +function testBadInput_negativeNotAllowed() { + var hasher = new goog.crypt.Sha512(); + assertThrows('Negative not allowed', function() { + hasher.update([1, 2, 3, 4, -10]); + }); +} + +function testBadInput_mustBeByteArray() { + var hasher = new goog.crypt.Sha512(); + assertThrows('Must be byte array', function() { + hasher.update([1, 2, 3, 4, {}]); + }); +} + +function testBadInput_byteTooLarge() { + var hasher = new goog.crypt.Sha512(); + assertThrows('>255 not allowed', function() { + hasher.update([1, 2, 3, 4, 256]); + }); +} + +function testBadInput_characterTooLarge() { + var hasher = new goog.crypt.Sha512(); + assertThrows('>255 not allowed', function() { + hasher.update('abc' + String.fromCharCode(256)); + }); +} + + +function testHasherNeedsReset_beforeDigest() { + var hasher = new goog.crypt.Sha512(); + hasher.update('abc'); + hasher.digest(); + assertThrows('Need reset after digest', function() { + hasher.digest(); + }); +} + + +function testHasherNeedsReset_beforeUpdate() { + var hasher = new goog.crypt.Sha512(); + hasher.update('abc'); + hasher.digest(); + assertThrows('Need reset after digest', function() { + hasher.update('abc'); + }); +} diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha384.js b/mapbox-gl/closure-library/closure/goog/crypt/sha384.js new file mode 100644 index 0000000000..08ad94630b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha384.js @@ -0,0 +1,59 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-384 cryptographic hash. + * + * Usage: + * var sha384 = new goog.crypt.Sha384(); + * sha384.update(bytes); + * var hash = sha384.digest(); + * + * @author fy@google.com (Frank Yellin) + */ + +goog.provide('goog.crypt.Sha384'); + +goog.require('goog.crypt.Sha2_64bit'); + + + +/** + * Constructs a SHA-384 cryptographic hash. + * + * @constructor + * @extends {goog.crypt.Sha2_64bit} + * @final + * @struct + */ +goog.crypt.Sha384 = function() { + goog.crypt.Sha384.base(this, 'constructor', 6 /* numHashBlocks */, + goog.crypt.Sha384.INIT_HASH_BLOCK_); +}; +goog.inherits(goog.crypt.Sha384, goog.crypt.Sha2_64bit); + + +/** @private {!Array} */ +goog.crypt.Sha384.INIT_HASH_BLOCK_ = [ + // Section 5.3.4 of + // csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + 0xcbbb9d5d, 0xc1059ed8, // H0 + 0x629a292a, 0x367cd507, // H1 + 0x9159015a, 0x3070dd17, // H2 + 0x152fecd8, 0xf70e5939, // H3 + 0x67332667, 0xffc00b31, // H4 + 0x8eb44a87, 0x68581511, // H5 + 0xdb0c2e0d, 0x64f98fa7, // H6 + 0x47b5481d, 0xbefa4fa4 // H7 +]; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha512.js b/mapbox-gl/closure-library/closure/goog/crypt/sha512.js new file mode 100644 index 0000000000..9ac228057d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha512.js @@ -0,0 +1,59 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-512 cryptographic hash. + * + * Usage: + * var sha512 = new goog.crypt.Sha512(); + * sha512.update(bytes); + * var hash = sha512.digest(); + * + * @author fy@google.com (Frank Yellin) + */ + +goog.provide('goog.crypt.Sha512'); + +goog.require('goog.crypt.Sha2_64bit'); + + + +/** + * Constructs a SHA-512 cryptographic hash. + * + * @constructor + * @extends {goog.crypt.Sha2_64bit} + * @final + * @struct + */ +goog.crypt.Sha512 = function() { + goog.crypt.Sha512.base(this, 'constructor', 8 /* numHashBlocks */, + goog.crypt.Sha512.INIT_HASH_BLOCK_); +}; +goog.inherits(goog.crypt.Sha512, goog.crypt.Sha2_64bit); + + +/** @private {!Array} */ +goog.crypt.Sha512.INIT_HASH_BLOCK_ = [ + // Section 5.3.5 of + // csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + 0x6a09e667, 0xf3bcc908, // H0 + 0xbb67ae85, 0x84caa73b, // H1 + 0x3c6ef372, 0xfe94f82b, // H2 + 0xa54ff53a, 0x5f1d36f1, // H3 + 0x510e527f, 0xade682d1, // H4 + 0x9b05688c, 0x2b3e6c1f, // H5 + 0x1f83d9ab, 0xfb41bd6b, // H6 + 0x5be0cd19, 0x137e2179 // H7 +]; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha512_256.js b/mapbox-gl/closure-library/closure/goog/crypt/sha512_256.js new file mode 100644 index 0000000000..75a4256fb9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha512_256.js @@ -0,0 +1,65 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview SHA-512/256 cryptographic hash. + * + * WARNING: SHA-256 and SHA-512/256 are different members of the SHA-2 + * family of hashes. Although both give 32-byte results, the two results + * should bear no relationship to each other. + * + * Please be careful before using this hash function. + *

+ * Usage: + * var sha512_256 = new goog.crypt.Sha512_256(); + * sha512_256.update(bytes); + * var hash = sha512_256.digest(); + * + * @author fy@google.com (Frank Yellin) + */ + +goog.provide('goog.crypt.Sha512_256'); + +goog.require('goog.crypt.Sha2_64bit'); + + + +/** + * Constructs a SHA-512/256 cryptographic hash. + * + * @constructor + * @extends {goog.crypt.Sha2_64bit} + * @final + * @struct + */ +goog.crypt.Sha512_256 = function() { + goog.crypt.Sha512_256.base(this, 'constructor', 4 /* numHashBlocks */, + goog.crypt.Sha512_256.INIT_HASH_BLOCK_); +}; +goog.inherits(goog.crypt.Sha512_256, goog.crypt.Sha2_64bit); + + +/** @private {!Array} */ +goog.crypt.Sha512_256.INIT_HASH_BLOCK_ = [ + // Section 5.3.6.2 of + // csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + 0x22312194, 0xFC2BF72C, // H0 + 0x9F555FA3, 0xC84C64C2, // H1 + 0x2393B86B, 0x6F53B151, // H2 + 0x96387719, 0x5940EABD, // H3 + 0x96283EE2, 0xA88EFFE3, // H4 + 0xBE5E1E25, 0x53863992, // H5 + 0x2B0199FC, 0x2C85B8AA, // H6 + 0x0EB72DDC, 0x81C52CA2 // H7 +]; diff --git a/mapbox-gl/closure-library/closure/goog/crypt/sha512_perf.html b/mapbox-gl/closure-library/closure/goog/crypt/sha512_perf.html new file mode 100644 index 0000000000..e7cf5d21ac --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/crypt/sha512_perf.html @@ -0,0 +1,41 @@ + + + + + +Closure Performance Tests - goog.crypt.Sha512 + + + + + +

Closure Performance Tests - goog.crypt.Sha512

+

+User-agent: + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/css/autocomplete.css b/mapbox-gl/closure-library/closure/goog/css/autocomplete.css new file mode 100644 index 0000000000..033ceba427 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/autocomplete.css @@ -0,0 +1,43 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styles for goog.ui.ac.AutoComplete and its derivatives. + * Note: these styles need some work to get them working properly at various + * font sizes other than the default. + * + * @author pupius@google.com (Daniel Pupius) + * @author annams@google.com (Srinivas Annam) + */ + + +/* + * TODO(annams): Rename (here and in renderer.js) to specify class name as + * goog-autocomplete-renderer + */ +.ac-renderer { + font: normal 13px Arial, sans-serif; + position: absolute; + background: #fff; + border: 1px solid #666; + -moz-box-shadow: 2px 2px 2px rgba(102, 102, 102, .4); + -webkit-box-shadow: 2px 2px 2px rgba(102, 102, 102, .4); + width: 300px; +} + +.ac-row { + cursor: pointer; + padding: .4em; +} + +.ac-highlighted { + font-weight: bold; +} + +.ac-active { + background-color: #b2b4bf; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/bubble.css b/mapbox-gl/closure-library/closure/goog/css/bubble.css new file mode 100644 index 0000000000..4e8d612bff --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/bubble.css @@ -0,0 +1,84 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +.goog-bubble-font { + font-size: 80%; + color: #888888; +} + +.goog-bubble-close-button { + background-image:url(//ssl.gstatic.com/closure/bubble_close.jpg); + background-color: white; + background-position: top right; + background-repeat: no-repeat; + width: 16px; + height: 16px; +} + +.goog-bubble-left { + background-image:url(//ssl.gstatic.com/closure/bubble_left.gif); + background-position:left; + background-repeat:repeat-y; + width: 4px; +} + +.goog-bubble-right { + background-image:url(//ssl.gstatic.com/closure/bubble_right.gif); + background-position: right; + background-repeat: repeat-y; + width: 4px; +} + +.goog-bubble-top-right-anchor { + background-image:url(//ssl.gstatic.com/closure/right_anchor_bubble_top.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 16px; +} + +.goog-bubble-top-left-anchor { + background-image:url(//ssl.gstatic.com/closure/left_anchor_bubble_top.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 16px; +} + +.goog-bubble-top-no-anchor { + background-image:url(//ssl.gstatic.com/closure/no_anchor_bubble_top.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 6px; +} + +.goog-bubble-bottom-right-anchor { + background-image:url(//ssl.gstatic.com/closure/right_anchor_bubble_bot.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 16px; +} + +.goog-bubble-bottom-left-anchor { + background-image:url(//ssl.gstatic.com/closure/left_anchor_bubble_bot.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 16px; +} + +.goog-bubble-bottom-no-anchor { + background-image:url(//ssl.gstatic.com/closure/no_anchor_bubble_bot.gif); + background-position: center; + background-repeat: no-repeat; + width: 147px; + height: 8px; +} + + diff --git a/mapbox-gl/closure-library/closure/goog/css/button.css b/mapbox-gl/closure-library/closure/goog/css/button.css new file mode 100644 index 0000000000..b80a92f142 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/button.css @@ -0,0 +1,38 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for buttons rendered by goog.ui.ButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + +.goog-button { + color: #036; + border-color: #036; + background-color: #69c; +} + +/* State: disabled. */ +.goog-button-disabled { + border-color: #333; + color: #333; + background-color: #999; +} + +/* State: hover. */ +.goog-button-hover { + color: #369; + border-color: #369; + background-color: #9cf; +} + +/* State: active. */ +.goog-button-active { + color: #69c; + border-color: #69c; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/charpicker.css b/mapbox-gl/closure-library/closure/goog/css/charpicker.css new file mode 100644 index 0000000000..fb33447746 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/charpicker.css @@ -0,0 +1,206 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ +/* Author: cibu@google.com (Cibu Johny) */ + +.goog-char-picker { + background-color: #ddd; + padding: 16px; + border: 1px solid #777; +} + +/* goog.ui.HoverCard */ +.goog-char-picker-hovercard { + border: solid 5px #ffcc33; + min-width: 64px; + max-width: 160px; + padding: 16px; + background-color: white; + text-align: center; + position: absolute; + visibility: hidden; +} + +.goog-char-picker-name { + font-size: x-small; +} + +.goog-char-picker-unicode { + font-size: x-small; + color: GrayText; +} + +.goog-char-picker-char-zoom { + font-size: xx-large; +} + +/* + * grid + */ +.goog-char-picker-grid-container { + border: 1px solid #777; + background-color: #fff; + width: 272px; +} + +.goog-char-picker-grid { + overflow: hidden; + height: 250px; + width: 250px; + position: relative; +} + +.goog-stick { + width: 1px; + overflow: hidden; +} +.goog-stickwrap { + width: 17px; + height: 250px; + float: right; + overflow: auto; +} + +.goog-char-picker-recents { + border: 1px solid #777; + background-color: #fff; + height: 25px; + width: 275px; + margin: 0 0 16px 0; + position: relative; +} + +.goog-char-picker-notice { + font-size: x-small; + height: 16px; + color: GrayText; + margin: 0 0 16px 0; +} + +/* + * Hex entry + */ + +.goog-char-picker-uplus { +} + +.goog-char-picker-input-box { + width: 96px; +} + +.label-input-label { + color: GrayText; +} + +.goog-char-picker-okbutton { +} + +/* + * Grid buttons + */ +.goog-char-picker-grid .goog-flat-button { + position: relative; + width: 24px; + height: 24px; + line-height: 24px; + border-bottom: 1px solid #ddd; + border-right: 1px solid #ddd; + text-align: center; + cursor: pointer; + outline: none; +} + +.goog-char-picker-grid .goog-flat-button-hover, +.goog-char-picker-grid .goog-flat-button-focus { + background-color: #ffcc33; +} + +/* + * goog.ui.Menu + */ + +/* State: resting. */ +.goog-char-picker-button { + border-width: 0px; + margin: 0; + padding: 0; + position: absolute; + background-position: center left; +} + +/* State: resting. */ +.goog-char-picker-menu { + background-color: #fff; + border-color: #ccc #666 #666 #ccc; + border-style: solid; + border-width: 1px; + cursor: default; + margin: 0; + outline: none; + padding: 0; + position: absolute; + max-height: 400px; + overflow-y: auto; + overflow-x: hide; +} + +/* + * goog.ui.MenuItem + */ + +/* State: resting. */ +.goog-char-picker-menu .goog-menuitem { + color: #000; + list-style: none; + margin: 0; + /* 28px on the left for icon or checkbox; 10ex on the right for shortcut. */ + padding: 1px 32px 1px 8px; + white-space: nowrap; +} + +.goog-char-picker-menu2 .goog-menuitem { + color: #000; + list-style: none; + margin: 0; + /* 28px on the left for icon or checkbox; 10ex on the right for shortcut. */ + padding: 1px 32px 1px 8px; + white-space: nowrap; +} + +.goog-char-picker-menu .goog-subtitle { + color: #fff !important; + background-color: #666; + font-weight: bold; + list-style: none; + margin: 0; + /* 28px on the left for icon or checkbox; 10ex on the right for shortcut. */ + padding: 3px 32px 3px 8px; + white-space: nowrap; +} + +/* BiDi override for the resting state. */ +.goog-char-picker-menu .goog-menuitem-rtl { + /* Flip left/right padding for BiDi. */ + padding: 2px 16px 2px 32px !important; +} + +/* State: hover. */ +.goog-char-picker-menu .goog-menuitem-highlight { + background-color: #d6e9f8; +} +/* + * goog.ui.MenuSeparator + */ + +/* State: resting. */ +.goog-char-picker-menu .goog-menuseparator { + border-top: 1px solid #ccc; + margin: 2px 0; + padding: 0; +} + diff --git a/mapbox-gl/closure-library/closure/goog/css/checkbox.css b/mapbox-gl/closure-library/closure/goog/css/checkbox.css new file mode 100644 index 0000000000..2aed8b5188 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/checkbox.css @@ -0,0 +1,38 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pallosp@google.com (Peter Pallos) */ + +/* Sample 3-state checkbox styles. */ + +.goog-checkbox { + border: 1px solid #1C5180; + display: -moz-inline-box; + display: inline-block; + font-size: 1px; /* Fixes the height in IE6 */ + height: 11px; + margin: 0 4px 0 1px; + vertical-align: text-bottom; + width: 11px; +} + +.goog-checkbox-checked { + background: #fff url(//ssl.gstatic.com/closure/check-sprite.gif) no-repeat 2px center; +} + +.goog-checkbox-undetermined { + background: #bbb url(//ssl.gstatic.com/closure/check-sprite.gif) no-repeat 2px center; +} + +.goog-checkbox-unchecked { + background: #fff; +} + +.goog-checkbox-disabled { + border: 1px solid lightgray; + background-position: -7px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/colormenubutton.css b/mapbox-gl/closure-library/closure/goog/css/colormenubutton.css new file mode 100644 index 0000000000..83655ddb15 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/colormenubutton.css @@ -0,0 +1,25 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for buttons created by goog.ui.ColorMenuButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +/* Color indicator. */ +.goog-color-menu-button-indicator { + border-bottom: 4px solid #f0f0f0; +} + +/* Thinner padding for color picker buttons, to leave room for the indicator. */ +.goog-color-menu-button .goog-menu-button-inner-box, +.goog-toolbar-color-menu-button .goog-toolbar-menu-button-inner-box { + padding-top: 2px !important; + padding-bottom: 2px !important; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/colorpalette.css b/mapbox-gl/closure-library/closure/goog/css/colorpalette.css new file mode 100644 index 0000000000..17bed426f6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/colorpalette.css @@ -0,0 +1,54 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for color palettes. + * + * @author pupius@google.com (Daniel Pupius) + * @author attila@google.com (Attila Bodis) + */ + + +.goog-palette-cell .goog-palette-colorswatch { + border: none; + font-size: x-small; + height: 18px; + position: relative; + width: 18px; +} + +.goog-palette-cell-hover .goog-palette-colorswatch { + border: 1px solid #fff; + height: 16px; + width: 16px; +} + +.goog-palette-cell-selected .goog-palette-colorswatch { + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -368px 0; + border: 1px solid #333; + color: #fff; + font-weight: bold; + height: 16px; + width: 16px; +} + +.goog-palette-customcolor { + background-color: #fafafa; + border: 1px solid #eee; + color: #666; + font-size: x-small; + height: 15px; + position: relative; + width: 15px; +} + +.goog-palette-cell-hover .goog-palette-customcolor { + background-color: #fee; + border: 1px solid #f66; + color: #f66; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/colorpicker-simplegrid.css b/mapbox-gl/closure-library/closure/goog/css/colorpicker-simplegrid.css new file mode 100644 index 0000000000..b98f5dccf6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/colorpicker-simplegrid.css @@ -0,0 +1,49 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ + +/* + Styles to make the colorpicker look like the old gmail color picker + NOTE: without CSS scoping this will override styles defined in palette.css +*/ +.goog-palette { + outline: none; + cursor: default; +} + +.goog-palette-table { + border: 1px solid #666; + border-collapse: collapse; +} + +.goog-palette-cell { + height: 13px; + width: 15px; + margin: 0; + border: 0; + text-align: center; + vertical-align: middle; + border-right: 1px solid #666; + font-size: 1px; +} + +.goog-palette-colorswatch { + position: relative; + height: 13px; + width: 15px; + border: 1px solid #666; +} + +.goog-palette-cell-hover .goog-palette-colorswatch { + border: 1px solid #FFF; +} + +.goog-palette-cell-selected .goog-palette-colorswatch { + border: 1px solid #000; + color: #fff; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/combobox.css b/mapbox-gl/closure-library/closure/goog/css/combobox.css new file mode 100644 index 0000000000..dd1571ab45 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/combobox.css @@ -0,0 +1,54 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ +/* Author: pallosp@google.com (Peter Pallos) */ + +/* Styles for goog.ui.ComboBox and its derivatives. */ + + +.goog-combobox { + background: #ddd url(//ssl.gstatic.com/closure/button-bg.gif) repeat-x scroll left top; + border: 1px solid #b5b6b5; + font: normal small arial, sans-serif; +} + +.goog-combobox input { + background-color: #fff; + border: 0; + border-right: 1px solid #b5b6b5; + color: #000; + font: normal small arial, sans-serif; + margin: 0; + padding: 0 0 0 2px; + vertical-align: bottom; /* override demo.css */ + width: 200px; +} + +.goog-combobox input.label-input-label { + background-color: #fff; + color: #aaa; +} + +.goog-combobox .goog-menu { + margin-top: -1px; + width: 219px; /* input width + button width + 3 * 1px border */ + z-index: 1000; +} + +.goog-combobox-button { + cursor: pointer; + display: inline-block; + font-size: 10px; + text-align: center; + width: 16px; +} + +/* IE6 only hack */ +* html .goog-combobox-button { + padding: 0 3px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/common.css b/mapbox-gl/closure-library/closure/goog/css/common.css new file mode 100644 index 0000000000..de140b88ee --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/common.css @@ -0,0 +1,41 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Cross-browser implementation of the "display: inline-block" CSS property. + * See http://www.w3.org/TR/CSS21/visuren.html#propdef-display for details. + * Tested on IE 6 & 7, FF 1.5 & 2.0, Safari 2 & 3, Webkit, and Opera 9. + * + * @author attila@google.com (Attila Bodis) + */ + +/* + * Default rule; only Safari, Webkit, and Opera handle it without hacks. + */ +.goog-inline-block { + position: relative; + display: -moz-inline-box; /* Ignored by FF3 and later. */ + display: inline-block; +} + +/* + * Pre-IE7 IE hack. On IE, "display: inline-block" only gives the element + * layout, but doesn't give it inline behavior. Subsequently setting display + * to inline does the trick. + */ +* html .goog-inline-block { + display: inline; +} + +/* + * IE7-only hack. On IE, "display: inline-block" only gives the element + * layout, but doesn't give it inline behavior. Subsequently setting display + * to inline does the trick. + */ +*:first-child+html .goog-inline-block { + display: inline; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/css3button.css b/mapbox-gl/closure-library/closure/goog/css/css3button.css new file mode 100644 index 0000000000..a26306f397 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/css3button.css @@ -0,0 +1,77 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: slightlyoff@google.com (Alex Russell) */ +/* Author: eae@google.com (Emil A Eklund) */ + +/* Imageless button styles. */ +.goog-css3-button { + margin: 0 2px; + padding: 3px 6px; + text-align: center; + vertical-align: middle; + white-space: nowrap; + cursor: default; + outline: none; + font-family: Arial, sans-serif; + color: #000; + border: 1px solid #bbb; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + /* TODO(eae): Change this to -webkit-linear-gradient once + https://bugs.webkit.org/show_bug.cgi?id=28152 is resolved. */ + background: -webkit-gradient(linear, 0% 40%, 0% 70%, from(#f9f9f9), + to(#e3e3e3)); + /* @alternate */ background: -moz-linear-gradient(top, #f9f9f9, #e3e3e3); +} + + +/* Styles for different states (hover, active, focused, open, checked). */ +.goog-css3-button-hover { + border-color: #939393 !important; +} + +.goog-css3-button-focused { + border-color: #444; +} + +.goog-css3-button-active, .goog-css3-button-open, .goog-css3-button-checked { + border-color: #444 !important; + background: -webkit-gradient(linear, 0% 40%, 0% 70%, from(#e3e3e3), + to(#f9f9f9)); + /* @alternate */ background: -moz-linear-gradient(top, #e3e3e3, #f9f9f9); +} + +.goog-css3-button-disabled { + color: #888; +} + +.goog-css3-button-primary { + font-weight: bold; +} + + +/* + * Pill (collapsed border) styles. + */ +.goog-css3-button-collapse-right { + margin-right: 0 !important; + border-right: 1px solid #bbb; + -webkit-border-top-right-radius: 0px; + -webkit-border-bottom-right-radius: 0px; + -moz-border-radius-topright: 0px; + -moz-border-radius-bottomright: 0px; +} + +.goog-css3-button-collapse-left { + border-left: 1px solid #f9f9f9; + margin-left: 0 !important; + -webkit-border-top-left-radius: 0px; + -webkit-border-bottom-left-radius: 0px; + -moz-border-radius-topleft: 0px; + -moz-border-radius-bottomleft: 0px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/css3menubutton.css b/mapbox-gl/closure-library/closure/goog/css/css3menubutton.css new file mode 100644 index 0000000000..a02070067e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/css3menubutton.css @@ -0,0 +1,23 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for buttons created by goog.ui.Css3MenuButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + * @author dalewis@google.com (Darren Lewis) + */ + +/* Dropdown arrow style. */ +.goog-css3-button-dropdown { + height: 16px; + width: 7px; + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0; + vertical-align: top; + margin-left: 3px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/custombutton.css b/mapbox-gl/closure-library/closure/goog/css/custombutton.css new file mode 100644 index 0000000000..1f17052708 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/custombutton.css @@ -0,0 +1,161 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for custom buttons rendered by goog.ui.CustomButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + +.goog-custom-button { + margin: 2px; + border: 0; + padding: 0; + font-family: Arial, sans-serif; + color: #000; + /* Client apps may override the URL at which they serve the image. */ + background: #ddd url(//ssl.gstatic.com/editor/button-bg.png) repeat-x top left; + text-decoration: none; + list-style: none; + vertical-align: middle; + cursor: default; + outline: none; +} + +/* Pseudo-rounded corners. */ +.goog-custom-button-outer-box, +.goog-custom-button-inner-box { + border-style: solid; + border-color: #aaa; + vertical-align: top; +} + +.goog-custom-button-outer-box { + margin: 0; + border-width: 1px 0; + padding: 0; +} + +.goog-custom-button-inner-box { + margin: 0 -1px; + border-width: 0 1px; + padding: 3px 4px; + white-space: nowrap; /* Prevents buttons from line breaking on android. */ +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-custom-button-inner-box { + /* IE6 needs to have the box shifted to make the borders line up. */ + left: -1px; +} +/* Pre-IE7 BiDi fixes. */ +* html .goog-custom-button-rtl .goog-custom-button-outer-box { + /* @noflip */ left: -1px; +} +* html .goog-custom-button-rtl .goog-custom-button-inner-box { + /* @noflip */ right: auto; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-custom-button-inner-box { + /* IE7 needs to have the box shifted to make the borders line up. */ + left: -1px; +} +/* IE7 BiDi fix. */ +*:first-child+html .goog-custom-button-rtl .goog-custom-button-inner-box { + /* @noflip */ left: 1px; +} + +/* Safari-only hacks. */ +::root .goog-custom-button, +::root .goog-custom-button-outer-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: 0; +} + +::root .goog-custom-button-inner-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: normal; +} + +/* State: disabled. */ +.goog-custom-button-disabled { + background-image: none !important; + opacity: 0.3; + -moz-opacity: 0.3; + filter: alpha(opacity=30); +} + +.goog-custom-button-disabled .goog-custom-button-outer-box, +.goog-custom-button-disabled .goog-custom-button-inner-box { + color: #333 !important; + border-color: #999 !important; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-custom-button-disabled { + margin: 2px 1px !important; + padding: 0 1px !important; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-custom-button-disabled { + margin: 2px 1px !important; + padding: 0 1px !important; +} + +/* State: hover. */ +.goog-custom-button-hover .goog-custom-button-outer-box, +.goog-custom-button-hover .goog-custom-button-inner-box { + border-color: #9cf #69e #69e #7af !important; /* Hover border wins. */ +} + +/* State: active, checked. */ +.goog-custom-button-active, +.goog-custom-button-checked { + background-color: #bbb; + background-position: bottom left; +} + +/* State: focused. */ +.goog-custom-button-focused .goog-custom-button-outer-box, +.goog-custom-button-focused .goog-custom-button-inner-box { + border-color: orange; +} + +/* Pill (collapsed border) styles. */ +.goog-custom-button-collapse-right, +.goog-custom-button-collapse-right .goog-custom-button-outer-box, +.goog-custom-button-collapse-right .goog-custom-button-inner-box { + margin-right: 0; +} + +.goog-custom-button-collapse-left, +.goog-custom-button-collapse-left .goog-custom-button-outer-box, +.goog-custom-button-collapse-left .goog-custom-button-inner-box { + margin-left: 0; +} + +.goog-custom-button-collapse-left .goog-custom-button-inner-box { + border-left: 1px solid #fff; +} + +.goog-custom-button-collapse-left.goog-custom-button-checked +.goog-custom-button-inner-box { + border-left: 1px solid #ddd; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-custom-button-collapse-left .goog-custom-button-inner-box { + left: 0; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-custom-button-collapse-left +.goog-custom-button-inner-box { + left: 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/datepicker.css b/mapbox-gl/closure-library/closure/goog/css/datepicker.css new file mode 100644 index 0000000000..6d5b914d03 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/datepicker.css @@ -0,0 +1,154 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for a goog.ui.DatePicker. + * + * @author arv@google.com (Erik Arvidsson) + */ + +.goog-date-picker, +.goog-date-picker th, +.goog-date-picker td { + font: 13px Arial, sans-serif; +} + +.goog-date-picker { + -moz-user-focus: normal; + -moz-user-select: none; + position: relative; + border: 1px solid #000; + float: left; + padding: 2px; + color: #000; + background: #c3d9ff; + cursor: default; +} + +.goog-date-picker th { + text-align: center; +} + +.goog-date-picker td { + text-align: center; + vertical-align: middle; + padding: 1px 3px; +} + + +.goog-date-picker-menu { + position: absolute; + background: threedface; + border: 1px solid gray; + -moz-user-focus: normal; + z-index: 1; + outline: none; +} + +.goog-date-picker-menu ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +.goog-date-picker-menu ul li { + cursor: default; +} + +.goog-date-picker-menu-selected { + background: #ccf; +} + +.goog-date-picker th { + font-size: .9em; +} + +.goog-date-picker td div { + float: left; +} + +.goog-date-picker button { + padding: 0px; + margin: 1px 0; + border: 0; + color: #20c; + font-weight: bold; + background: transparent; +} + +.goog-date-picker-date { + background: #fff; +} + +.goog-date-picker-week, +.goog-date-picker-wday { + padding: 1px 3px; + border: 0; + border-color: #a2bbdd; + border-style: solid; +} + +.goog-date-picker-week { + border-right-width: 1px; +} + +.goog-date-picker-wday { + border-bottom-width: 1px; +} + +.goog-date-picker-head td { + text-align: center; +} + +/** Use td.className instead of !important */ +td.goog-date-picker-today-cont { + text-align: center; +} + +/** Use td.className instead of !important */ +td.goog-date-picker-none-cont { + text-align: center; +} + +.goog-date-picker-month { + min-width: 11ex; + white-space: nowrap; +} + +.goog-date-picker-year { + min-width: 6ex; + white-space: nowrap; +} + +.goog-date-picker-monthyear { + white-space: nowrap; +} + +.goog-date-picker table { + border-collapse: collapse; +} + +.goog-date-picker-other-month { + color: #888; +} + +.goog-date-picker-wkend-start, +.goog-date-picker-wkend-end { + background: #eee; +} + +/** Use td.className instead of !important */ +td.goog-date-picker-selected { + background: #c3d9ff; +} + +.goog-date-picker-today { + background: #9ab; + font-weight: bold !important; + border-color: #246 #9bd #9bd #246; + color: #fff; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/dialog.css b/mapbox-gl/closure-library/closure/goog/css/dialog.css new file mode 100644 index 0000000000..6bf9020ab1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/dialog.css @@ -0,0 +1,72 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for goog.ui.Dialog. + * + * @author ssaviano@google.com (Steven Saviano) + * @author attila@google.com (Attila Bodis) + */ + + +.modal-dialog { + background: #c1d9ff; + border: 1px solid #3a5774; + color: #000; + padding: 4px; + position: absolute; +} + +.modal-dialog a, +.modal-dialog a:link, +.modal-dialog a:visited { + color: #06c; + cursor: pointer; +} + +.modal-dialog-bg { + background: #666; + left: 0; + position: absolute; + top: 0; +} + +.modal-dialog-title { + background: #e0edfe; + color: #000; + cursor: pointer; + font-size: 120%; + font-weight: bold; + + /* Add padding on the right to ensure the close button has room. */ + padding: 8px 31px 8px 8px; + + position: relative; + _zoom: 1; /* Ensures proper width in IE6 RTL. */ +} + +.modal-dialog-title-close { + /* Client apps may override the URL at which they serve the sprite. */ + background: #e0edfe url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -528px 0; + cursor: default; + height: 15px; + position: absolute; + right: 10px; + top: 8px; + width: 15px; + vertical-align: middle; +} + +.modal-dialog-buttons, +.modal-dialog-content { + background-color: #fff; + padding: 8px; +} + +.goog-buttonset-default { + font-weight: bold; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/dimensionpicker.css b/mapbox-gl/closure-library/closure/goog/css/dimensionpicker.css new file mode 100644 index 0000000000..5c51ab85f6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/dimensionpicker.css @@ -0,0 +1,47 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for dimension pickers rendered by goog.ui.DimensionPickerRenderer. + * + * Author: robbyw@google.com (Robby Walker) + * Author: abefettig@google.com (Abe Fettig) + */ + +.goog-dimension-picker { + font-size: 18px; + padding: 4px; +} + +.goog-dimension-picker div { + position: relative; +} + +.goog-dimension-picker div.goog-dimension-picker-highlighted { +/* Client apps must provide the URL at which they serve the image. */ + /* background: url(dimension-highlighted.png); */ + left: 0; + overflow: hidden; + position: absolute; + top: 0; +} + +.goog-dimension-picker-unhighlighted { + /* Client apps must provide the URL at which they serve the image. */ + /* background: url(dimension-unhighlighted.png); */ +} + +.goog-dimension-picker-status { + font-size: 10pt; + text-align: center; +} + +.goog-dimension-picker div.goog-dimension-picker-mousecatcher { + left: 0; + position: absolute !important; + top: 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/dragdropdetector.css b/mapbox-gl/closure-library/closure/goog/css/dragdropdetector.css new file mode 100644 index 0000000000..88266d6ccf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/dragdropdetector.css @@ -0,0 +1,48 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for the drag drop detector. + * + * Author: robbyw@google.com (Robby Walker) + * Author: wcrosby@google.com (Wayne Crosby) + */ + +.goog-dragdrop-w3c-editable-iframe { + position: absolute; + width: 100%; + height: 10px; + top: -150px; + left: 0; + z-index: 10000; + padding: 0; + overflow: hidden; + opacity: 0; + -moz-opacity: 0; +} + +.goog-dragdrop-ie-editable-iframe { + width: 100%; + height: 5000px; +} + +.goog-dragdrop-ie-input { + width: 100%; + height: 5000px; +} + +.goog-dragdrop-ie-div { + position: absolute; + top: -5000px; + left: 0; + width: 100%; + height: 5000px; + z-index: 10000; + background-color: white; + filter: alpha(opacity=0); + overflow: hidden; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/editor/bubble.css b/mapbox-gl/closure-library/closure/goog/css/editor/bubble.css new file mode 100644 index 0000000000..c76f98f77c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/editor/bubble.css @@ -0,0 +1,73 @@ +/* + * Copyright 2005 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Bubble styles. + * + * @author robbyw@google.com (Robby Walker) + * @author nicksantos@google.com (Nick Santos) + * @author jparent@google.com (Julie Parent) + */ + +div.tr_bubble { + position: absolute; + + background-color: #e0ecff; + border: 1px solid #99c0ff; + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + font-size: 83%; + font-family: Arial, Helvetica, sans-serif; + padding: 2px 19px 6px 6px; + white-space: nowrap; +} + +.tr_bubble_link { + color: #00c; + text-decoration: underline; + cursor: pointer; + font-size: 100%; +} + +.tr_bubble .tr_option-link, +.tr_bubble #tr_delete-image, +.tr_bubble #tr_module-options-link { + font-size: 83%; +} + +.tr_bubble_closebox { + position: absolute; + cursor: default; + background: url(//ssl.gstatic.com/editor/bubble_closebox.gif) top left no-repeat; + padding: 0; + margin: 0; + width: 10px; + height: 10px; + top: 3px; + right: 5px; +} + +div.tr_bubble_panel { + padding: 2px 0 1px; +} + +div.tr_bubble_panel_title { + display: none; +} + +div.tr_multi_bubble div.tr_bubble_panel_title { + margin-right: 1px; + display: block; + float: left; + width: 50px; +} + +div.tr_multi_bubble div.tr_bubble_panel { + padding: 2px 0 1px; + margin-right: 50px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/editor/dialog.css b/mapbox-gl/closure-library/closure/goog/css/editor/dialog.css new file mode 100644 index 0000000000..8868a1054c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/editor/dialog.css @@ -0,0 +1,66 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styles for Editor dialogs and their sub-components. + * + * @author marcosalmeida@google.com (Marcos Almeida) + */ + + +.tr-dialog { + width: 475px; +} + +.tr-dialog .goog-tab-content { + margin: 0; + border: 1px solid #6b90da; + padding: 4px 8px; + background: #fff; + overflow: auto; +} + +.tr-tabpane { + font-size: 10pt; + padding: 1.3ex 0; +} + +.tr-tabpane-caption { + font-size: 10pt; + margin-bottom: 0.7ex; + background-color: #fffaf5; + line-height: 1.3em; +} + +.tr-tabpane .goog-tab-content { + border: none; + padding: 5px 7px 1px; +} + +.tr-tabpane .goog-tab { + background-color: #fff; + border: none; + width: 136px; + line-height: 1.3em; + margin-bottom: 0.7ex; +} + +.tr-tabpane .goog-tab { + text-decoration: underline; + color: blue; + cursor: pointer; +} + +.tr-tabpane .goog-tab-selected { + font-weight: bold; + text-decoration: none; + color: black; +} + +.tr-tabpane .goog-tab input { + margin: -2px 5px 0 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/editor/equationeditor.css b/mapbox-gl/closure-library/closure/goog/css/editor/equationeditor.css new file mode 100644 index 0000000000..b6aff348b3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/editor/equationeditor.css @@ -0,0 +1,113 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * The CSS definition for everything inside equation editor dialog. + * + * Author: yoah@google.com (Yoah Bar-David) + * Author: kfk@google.com (Ming Zhang) + */ + +.ee-modal-dialog { + width: 475px; +} + +.ee-content { + background: #FFF; + border: 1px solid #369; + overflow: auto; + padding: 4px 8px; +} + +.ee-tex { + border: 1px solid #000; + display: block; + height: 7.5em; + margin: 4px 0 10px 0; + width: 100%; +} + +.ee-section-title { + font-weight: bold; +} + +.ee-section-title-floating { + float: left; +} + +#ee-section-learn-more { + float: right; +} + +.ee-preview-container { + border: 1px dashed #ccc; + height: 80px; + margin: 4px 0 10px 0; + width: 100%; + overflow: auto; +} + +.ee-warning { + color: #F00; +} + +.ee-palette { + border: 1px solid #aaa; + left: 0; + outline: none; + position: absolute; +} + +.ee-palette-table { + border: 0; + border-collapse: separate; +} + +.ee-palette-cell { + background: #F0F0F0; + border: 1px solid #FFF; + margin: 0; + padding: 1px; +} + +.ee-palette-cell-hover { + background: #E2ECF9 !important; + border: 1px solid #000; + padding: 1px; +} + +.ee-palette-cell-selected { + background: #F0F0F0; + border: 1px solid #CCC !important; + padding: 1px; +} + +.ee-menu-palette-table { + margin-right: 10px; +} + +.ee-menu-palette { + outline: none; + padding-top: 2px; +} + +.ee-menu-palette-cell { + background: #F0F0F0 none repeat scroll 0 0; + border-color: #888 #AAA #AAA #888; + border-style: solid; + border-width: 1px; +} +.ee-menu-palette-cell-hover, +.ee-menu-palette-cell-selected { + background: #F0F0F0; +} + +.ee-palette-item, +.ee-menu-palette-item { + background-image: url(//ssl.gstatic.com/editor/ee-palettes.gif); +} + diff --git a/mapbox-gl/closure-library/closure/goog/css/editor/linkdialog.css b/mapbox-gl/closure-library/closure/goog/css/editor/linkdialog.css new file mode 100644 index 0000000000..a58a4b2a22 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/editor/linkdialog.css @@ -0,0 +1,36 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/** + * Styles for the Editor's Edit Link dialog. + * + * @author marcosalmeida@google.com (Marcos Almeida) + */ + + +.tr-link-dialog-explanation-text { + font-size: 83%; + margin-top: 15px; +} + +.tr-link-dialog-target-input { + width: 98%; /* 98% prevents scroll bars in standards mode. */ + /* Input boxes for URLs and email address should always be LTR. */ + direction: ltr; +} + +.tr-link-dialog-email-warning { + text-align: center; + color: #c00; + font-weight: bold; +} + +.tr_pseudo-link { + color: #00c; + text-decoration: underline; + cursor: pointer; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/editortoolbar.css b/mapbox-gl/closure-library/closure/goog/css/editortoolbar.css new file mode 100644 index 0000000000..1e26f4fe0d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/editortoolbar.css @@ -0,0 +1,225 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + + +/* + * Editor toolbar styles. + * + * @author attila@google.com (Attila Bodis) + */ + +/* Common base style for all icons. */ +.tr-icon { + width: 16px; + height: 16px; + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat; + vertical-align: middle; +} + +.goog-color-menu-button-indicator .tr-icon { + height: 14px; +} + +/* Undo (redo when the chrome is right-to-left). */ +.tr-undo, +.goog-toolbar-button-rtl .tr-redo { + background-position: 0; +} + +/* Redo (undo when the chrome is right-to-left). */ +.tr-redo, +.goog-toolbar-button-rtl .tr-undo { + background-position: -16px; +} + +/* Font name. */ +.tr-fontName .goog-toolbar-menu-button-caption { + color: #246; + width: 16ex; + height: 16px; + overflow: hidden; +} + +/* Font size. */ +.tr-fontSize .goog-toolbar-menu-button-caption { + color: #246; + width: 8ex; + height: 16px; + overflow: hidden; +} + +/* Bold. */ +.tr-bold { + background-position: -32px; +} + +/* Italic. */ +.tr-italic { + background-position: -48px; +} + +/* Underline. */ +.tr-underline { + background-position: -64px; +} + +/* Foreground color. */ +.tr-foreColor { + height: 14px; + background-position: -80px; +} + +/* Background color. */ +.tr-backColor { + height: 14px; + background-position: -96px; +} + +/* Link. */ +.tr-link { + font-weight: bold; + color: #009; + text-decoration: underline; +} + +/* Insert image. */ +.tr-image { + background-position: -112px; +} + +/* Insert drawing. */ +.tr-newDrawing { + background-position: -592px; +} + +/* Insert special character. */ +.tr-spChar { + font-weight: bold; + color: #900; +} + +/* Increase indent. */ +.tr-indent { + background-position: -128px; +} + +/* Increase ident in right-to-left text mode, regardless of chrome direction. */ +.tr-rtl-mode .tr-indent { + background-position: -400px; +} + +/* Decrease indent. */ +.tr-outdent { + background-position: -144px; +} + +/* Decrease indent in right-to-left text mode, regardless of chrome direction. */ +.tr-rtl-mode .tr-outdent { + background-position: -416px; +} + +/* Bullet (unordered) list. */ +.tr-insertUnorderedList { + background-position: -160px; +} + +/* Bullet list in right-to-left text mode, regardless of chrome direction. */ +.tr-rtl-mode .tr-insertUnorderedList { + background-position: -432px; +} + +/* Number (ordered) list. */ +.tr-insertOrderedList { + background-position: -176px; +} + +/* Number list in right-to-left text mode, regardless of chrome direction. */ +.tr-rtl-mode .tr-insertOrderedList { + background-position: -448px; +} + +/* Text alignment buttons. */ +.tr-justifyLeft { + background-position: -192px; +} +.tr-justifyCenter { + background-position: -208px; +} +.tr-justifyRight { + background-position: -224px; +} +.tr-justifyFull { + background-position: -480px; +} + +/* Blockquote. */ +.tr-BLOCKQUOTE { + background-position: -240px; +} + +/* Blockquote in right-to-left text mode, regardless of chrome direction. */ +.tr-rtl-mode .tr-BLOCKQUOTE { + background-position: -464px; +} + +/* Remove formatting. */ +.tr-removeFormat { + background-position: -256px; +} + +/* Spellcheck. */ +.tr-spell { + background-position: -272px; +} + +/* Left-to-right text direction. */ +.tr-ltr { + background-position: -288px; +} + +/* Right-to-left text direction. */ +.tr-rtl { + background-position: -304px; +} + +/* Insert iGoogle module. */ +.tr-insertModule { + background-position: -496px; +} + +/* Strike through text */ +.tr-strikeThrough { + background-position: -544px; +} + +/* Subscript */ +.tr-subscript { + background-position: -560px; +} + +/* Superscript */ +.tr-superscript { + background-position: -576px; +} + +/* Insert drawing. */ +.tr-equation { + background-position: -608px; +} + +/* Edit HTML. */ +.tr-editHtml { + color: #009; +} + +/* "Format block" menu. */ +.tr-formatBlock .goog-toolbar-menu-button-caption { + color: #246; + width: 12ex; + height: 16px; + overflow: hidden; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/filteredmenu.css b/mapbox-gl/closure-library/closure/goog/css/filteredmenu.css new file mode 100644 index 0000000000..b43a113253 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/filteredmenu.css @@ -0,0 +1,30 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ + +/* goog.ui.FilteredMenu */ + +.goog-menu-filter { + margin: 2px; + border: 1px solid silver; + background: white; + overflow: hidden; +} + +.goog-menu-filter div { + color: gray; + position: absolute; + padding: 1px; +} + +.goog-menu-filter input { + margin: 0; + border: 0; + background: transparent; + width: 100%; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/filterobservingmenuitem.css b/mapbox-gl/closure-library/closure/goog/css/filterobservingmenuitem.css new file mode 100644 index 0000000000..d48a609ac4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/filterobservingmenuitem.css @@ -0,0 +1,25 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ + +/* goog.ui.FilterObservingMenuItem */ + +.goog-filterobsmenuitem { + padding: 2px 5px; + margin: 0; + list-style: none; +} + +.goog-filterobsmenuitem-highlight { + background-color: #4279A5; + color: #FFF; +} + +.goog-filterobsmenuitem-disabled { + color: #999; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/flatbutton.css b/mapbox-gl/closure-library/closure/goog/css/flatbutton.css new file mode 100644 index 0000000000..1cc39b6916 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/flatbutton.css @@ -0,0 +1,64 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for flat buttons created by goog.ui.FlatButtonRenderer. + * + * @author brianp@google.com (Brian Peterson) + */ + +.goog-flat-button { + position: relative; + /*width: 20ex;*/ + margin: 2px; + border: 1px solid #000; + padding: 2px 6px; + font: normal 13px "Trebuchet MS", Tahoma, Arial, sans-serif; + color: #fff; + background-color: #8c2425; + cursor: pointer; + outline: none; +} + +/* State: disabled. */ +.goog-flat-button-disabled { + border-color: #888; + color: #888; + background-color: #ccc; + cursor: default; +} + +/* State: hover. */ +.goog-flat-button-hover { + border-color: #8c2425; + color: #8c2425; + background-color: #eaa4a5; +} + +/* State: active, selected, checked. */ +.goog-flat-button-active, +.goog-flat-button-selected, +.goog-flat-button-checked { + border-color: #5b4169; + color: #5b4169; + background-color: #d1a8ea; +} + +/* State: focused. */ +.goog-flat-button-focused { + border-color: #5b4169; +} + +/* Pill (collapsed border) styles. */ +.goog-flat-button-collapse-right { + margin-right: 0; +} + +.goog-flat-button-collapse-left { + margin-left: 0; + border-left: none; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/flatmenubutton.css b/mapbox-gl/closure-library/closure/goog/css/flatmenubutton.css new file mode 100644 index 0000000000..577821782c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/flatmenubutton.css @@ -0,0 +1,63 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for buttons created by goog.ui.FlatMenuButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + * @author tlb@google.com (Thierry Le Boulenge) + */ + + +.goog-flat-menu-button { + background-color: #fff; + border: 1px solid #c9c9c9; + color: #333; + cursor: pointer; + font: normal 95%; + list-style: none; + margin: 0 2px; + outline: none; + padding: 1px 4px; + position: relative; + text-decoration: none; + vertical-align: middle; +} + +.goog-flat-menu-button-disabled * { + border-color: #ccc; + color: #999; + cursor: default; +} + +.goog-flat-menu-button-hover { + border-color: #9cf #69e #69e #7af !important; /* Hover border wins. */ +} + +.goog-flat-menu-button-active { + background-color: #bbb; + background-position: bottom left; +} + +.goog-flat-menu-button-focused { + border-color: #bbb; +} + +.goog-flat-menu-button-caption { + padding-right: 10px; + vertical-align: top; +} + +.goog-flat-menu-button-dropdown { + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0; + position: absolute; + right: 2px; + top: 0; + vertical-align: top; + width: 7px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/hovercard.css b/mapbox-gl/closure-library/closure/goog/css/hovercard.css new file mode 100644 index 0000000000..2d64e4fbf0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/hovercard.css @@ -0,0 +1,51 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: larrypo@google.com (Larry Powelson) */ + +.goog-hovercard div { + border: solid 5px #69748C; + width: 300px; + height: 115px; + background-color: white; + font-family: arial, sans-serif; +} + +.goog-hovercard .goog-shadow { + border: transparent; + background-color: black; + filter: alpha(Opacity=1); + opacity: 0.01; + -moz-opacity: 0.01; +} + +.goog-hovercard table { + border-collapse: collapse; + border-spacing: 0px; +} + +.goog-hovercard-icons td { + border-bottom: 1px solid #ccc; + padding: 0px; + margin: 0px; + text-align: center; + height: 19px; + width: 100px; + font-size: 90%; +} + +.goog-hovercard-icons td + td { + border-left: 1px solid #ccc; +} + +.goog-hovercard-content { + border-collapse: collapse; +} + +.goog-hovercard-content td { + padding-left: 15px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/hsvapalette.css b/mapbox-gl/closure-library/closure/goog/css/hsvapalette.css new file mode 100644 index 0000000000..ec52e3927e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/hsvapalette.css @@ -0,0 +1,231 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * All Rights Reserved. + * + * Styles for the HSV color palette. + * + * @author smcbride@google.com (Sean McBride) + * @author arv@google.com (Erik Arvidsson) + * @author manucornet@google.com (Manu Cornet) + */ + +.goog-hsva-palette, +.goog-hsva-palette-sm { + position: relative; + border: 1px solid #999; + border-color: #ccc #999 #999 #ccc; + width: 442px; + height: 276px; +} + +.goog-hsva-palette-sm { + width: 205px; + height: 185px; +} + +.goog-hsva-palette label span, +.goog-hsva-palette-sm label span { + display: none; +} + +.goog-hsva-palette-hs-backdrop, +.goog-hsva-palette-sm-hs-backdrop, +.goog-hsva-palette-hs-image, +.goog-hsva-palette-sm-hs-image { + position: absolute; + top: 10px; + left: 10px; + width: 256px; + height: 256px; + border: 1px solid #999; +} + +.goog-hsva-palette-sm-hs-backdrop, +.goog-hsva-palette-sm-hs-image { + top: 45px; + width: 128px; + height: 128px; +} + +.goog-hsva-palette-hs-backdrop, +.goog-hsva-palette-sm-hs-backdrop { + background-color: #000; +} + +.goog-hsva-palette-hs-image, +.goog-hsva-palette-v-image, +.goog-hsva-palette-a-image, +.goog-hsva-palette-hs-handle, +.goog-hsva-palette-v-handle, +.goog-hsva-palette-a-handle, +.goog-hsva-palette-swatch-backdrop { + background-image: url(//ssl.gstatic.com/closure/hsva-sprite.png); +} + +.goog-hsva-palette-noalpha .goog-hsva-palette-hs-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-v-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-a-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-hs-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-v-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-a-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-swatch-backdrop { + background-image: url(//ssl.gstatic.com/closure/hsva-sprite.gif); +} + +.goog-hsva-palette-sm-hs-image, +.goog-hsva-palette-sm-v-image, +.goog-hsva-palette-sm-a-image, +.goog-hsva-palette-sm-hs-handle, +.goog-hsva-palette-sm-v-handle, +.goog-hsva-palette-sm-a-handle, +.goog-hsva-palette-sm-swatch-backdrop { + background-image: url(//ssl.gstatic.com/closure/hsva-sprite-sm.png); +} + +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-hs-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-v-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-a-image, +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-hs-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-v-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-sm-a-handle, +.goog-hsva-palette-noalpha .goog-hsva-palette-swatch-backdrop { + background-image: url(//ssl.gstatic.com/closure/hsva-sprite-sm.gif); +} + +.goog-hsva-palette-hs-image, +.goog-hsva-palette-sm-hs-image { + background-position: 0 0; +} + +.goog-hsva-palette-hs-handle, +.goog-hsva-palette-sm-hs-handle { + position: absolute; + left: 5px; + top: 5px; + width: 11px; + height: 11px; + overflow: hidden; + background-position: 0 -256px; +} + +.goog-hsva-palette-sm-hs-handle { + top: 40px; + background-position: 0 -128px; +} + +.goog-hsva-palette-v-image, +.goog-hsva-palette-a-image, +.goog-hsva-palette-sm-v-image, +.goog-hsva-palette-sm-a-image { + position: absolute; + top: 10px; + left: 286px; + width: 19px; + height: 256px; + border: 1px solid #999; + background-color: #fff; + background-position: -256px 0; +} + +.goog-hsva-palette-a-image { + left: 325px; + background-position: -275px 0; +} + +.goog-hsva-palette-sm-v-image, +.goog-hsva-palette-sm-a-image { + top: 45px; + left: 155px; + width: 9px; + height: 128px; + background-position: -128px 0; +} + +.goog-hsva-palette-sm-a-image { + left: 182px; + background-position: -137px 0; +} + +.goog-hsva-palette-v-handle, +.goog-hsva-palette-a-handle, +.goog-hsva-palette-sm-v-handle, +.goog-hsva-palette-sm-a-handle { + position: absolute; + top: 5px; + left: 279px; + width: 35px; + height: 11px; + background-position: -11px -256px; + overflow: hidden; +} + +.goog-hsva-palette-a-handle { + left: 318px; +} + +.goog-hsva-palette-sm-v-handle, +.goog-hsva-palette-sm-a-handle { + top: 40px; + left: 148px; + width: 25px; + background-position: -11px -128px; +} + +.goog-hsva-palette-sm-a-handle { + left: 175px; +} + +.goog-hsva-palette-swatch, +.goog-hsva-palette-swatch-backdrop, +.goog-hsva-palette-sm-swatch, +.goog-hsva-palette-sm-swatch-backdrop { + position: absolute; + top: 10px; + right: 10px; + width: 65px; + height: 65px; + border: 1px solid #999; + background-color: #fff; + background-position: -294px 0; +} + +.goog-hsva-palette-sm-swatch, +.goog-hsva-palette-sm-swatch-backdrop { + top: 10px; + right: auto; + left: 10px; + width: 30px; + height: 22px; + background-position: -36px -128px; +} + +.goog-hsva-palette-swatch, +.goog-hsva-palette-sm-swatch { + z-index: 5; +} + +.goog-hsva-palette-swatch-backdrop, +.goog-hsva-palette-sm-swatch-backdrop { + z-index: 1; +} + +.goog-hsva-palette-input, +.goog-hsva-palette-sm-input { + position: absolute; + top: 85px; + right: 10px; + width: 65px; + font-size: 80%; +} + +.goog-hsva-palette-sm-input { + top: 10px; + right: auto; + left: 50px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/hsvpalette.css b/mapbox-gl/closure-library/closure/goog/css/hsvpalette.css new file mode 100644 index 0000000000..8449ed59aa --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/hsvpalette.css @@ -0,0 +1,179 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * All Rights Reserved. + * + * Styles for the HSV color palette. + * + * @author smcbride@google.com (Sean McBride) + * @author arv@google.com (Erik Arvidsson) + * @author manucornet@google.com (Manu Cornet) + */ + +.goog-hsv-palette, +.goog-hsv-palette-sm { + position: relative; + border: 1px solid #999; + border-color: #ccc #999 #999 #ccc; + width: 400px; + height: 276px; +} + +.goog-hsv-palette-sm { + width: 182px; + height: 185px; +} + +.goog-hsv-palette label span, +.goog-hsv-palette-sm label span { + display: none; +} + +.goog-hsv-palette-hs-backdrop, +.goog-hsv-palette-sm-hs-backdrop, +.goog-hsv-palette-hs-image, +.goog-hsv-palette-sm-hs-image { + position: absolute; + top: 10px; + left: 10px; + width: 256px; + height: 256px; + border: 1px solid #999; +} + +.goog-hsv-palette-sm-hs-backdrop, +.goog-hsv-palette-sm-hs-image { + top: 45px; + width: 128px; + height: 128px; +} + +.goog-hsv-palette-hs-backdrop, +.goog-hsv-palette-sm-hs-backdrop { + background-color: #000; +} + +.goog-hsv-palette-hs-image, +.goog-hsv-palette-v-image, +.goog-hsv-palette-hs-handle, +.goog-hsv-palette-v-handle { + background-image: url(//ssl.gstatic.com/closure/hsv-sprite.png); +} + +.goog-hsv-palette-noalpha .goog-hsv-palette-hs-image, +.goog-hsv-palette-noalpha .goog-hsv-palette-v-image, +.goog-hsv-palette-noalpha .goog-hsv-palette-hs-handle, +.goog-hsv-palette-noalpha .goog-hsv-palette-v-handle { + background-image: url(//ssl.gstatic.com/closure/hsv-sprite.gif); +} + +.goog-hsv-palette-sm-hs-image, +.goog-hsv-palette-sm-v-image, +.goog-hsv-palette-sm-hs-handle, +.goog-hsv-palette-sm-v-handle { + background-image: url(//ssl.gstatic.com/closure/hsv-sprite-sm.png); +} + +.goog-hsv-palette-noalpha .goog-hsv-palette-sm-hs-image, +.goog-hsv-palette-noalpha .goog-hsv-palette-sm-v-image, +.goog-hsv-palette-noalpha .goog-hsv-palette-sm-hs-handle, +.goog-hsv-palette-noalpha .goog-hsv-palette-sm-v-handle { + background-image: url(//ssl.gstatic.com/closure/hsv-sprite-sm.gif); +} + +.goog-hsv-palette-hs-image, +.goog-hsv-palette-sm-hs-image { + background-position: 0 0; +} + +.goog-hsv-palette-hs-handle, +.goog-hsv-palette-sm-hs-handle { + position: absolute; + left: 5px; + top: 5px; + width: 11px; + height: 11px; + overflow: hidden; + background-position: 0 -256px; +} + +.goog-hsv-palette-sm-hs-handle { + top: 40px; + background-position: 0 -128px; +} + +.goog-hsv-palette-v-image, +.goog-hsv-palette-sm-v-image { + position: absolute; + top: 10px; + left: 286px; + width: 19px; + height: 256px; + border: 1px solid #999; + background-color: #fff; + background-position: -256px 0; +} + +.goog-hsv-palette-sm-v-image { + top: 45px; + left: 155px; + width: 9px; + height: 128px; + background-position: -128px 0; +} + +.goog-hsv-palette-v-handle, +.goog-hsv-palette-sm-v-handle { + position: absolute; + top: 5px; + left: 279px; + width: 35px; + height: 11px; + background-position: -11px -256px; + overflow: hidden; +} + +.goog-hsv-palette-sm-v-handle { + top: 40px; + left: 148px; + width: 25px; + background-position: -11px -128px; +} + +.goog-hsv-palette-swatch, +.goog-hsv-palette-sm-swatch { + position: absolute; + top: 10px; + right: 10px; + width: 65px; + height: 65px; + border: 1px solid #999; + background-color: #fff; +} + +.goog-hsv-palette-sm-swatch { + top: 10px; + right: auto; + left: 10px; + width: 30px; + height: 22px; +} + +.goog-hsv-palette-input, +.goog-hsv-palette-sm-input { + position: absolute; + top: 85px; + right: 10px; + width: 65px; +} + +.goog-hsv-palette-sm-input { + top: 10px; + right: auto; + left: 50px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/imagelessbutton.css b/mapbox-gl/closure-library/closure/goog/css/imagelessbutton.css new file mode 100644 index 0000000000..23c7fee6b2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/imagelessbutton.css @@ -0,0 +1,160 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for buttons created by goog.ui.ImagelessButtonRenderer. + * + * WARNING: This file uses some ineffecient selectors and it may be + * best to avoid using this file in extremely large, or performance + * critical applications. + * + * @author: eae@google.com (Emil A Eklund) + * @author: gboyer@google.com (Garrett Boyer) + */ + + +/* Imageless button styles. */ + +/* The base element of the button. */ +.goog-imageless-button { + /* Set the background color at the outermost level. */ + background: #e3e3e3; + /* Place a top and bottom border. Do it on this outermost div so that + * it is easier to make pill buttons work properly. */ + border-color: #bbb; + border-style: solid; + border-width: 1px 0; + color: #222; /* Text content color. */ + cursor: default; + font-family: Arial, sans-serif; + line-height: 0; /* For Opera and old WebKit. */ + list-style: none; + /* Built-in margin for the component. Because of the negative margins + * used to simulate corner rounding, the effective left and right margin is + * actually only 1px. */ + margin: 2px; + outline: none; + padding: 0; + text-decoration: none; + vertical-align: middle; +} + +/* + * Pseudo-rounded corners. Works by pulling the left and right sides slightly + * outside of the parent bounding box before drawing the left and right + * borders. + */ +.goog-imageless-button-outer-box { + /* Left and right border that protrude outside the parent. */ + border-color: #bbb; + border-style: solid; + border-width: 0 1px; + /* Same as margin: 0 -1px, except works better cross browser. These are + * intended to be RTL flipped to work better in IE7. */ + left: -1px; + margin-right: -2px; +} + +/* + * A div to give the light and medium shades of the button that takes up no + * vertical space. + */ +.goog-imageless-button-top-shadow { + /* Light top color in the content. */ + background: #f9f9f9; + /* Thin medium shade. */ + border-bottom: 3px solid #eee; + /* Control height with line-height, since height: will trigger hasLayout. + * Specified in pixels, as a compromise to avoid rounding errors. */ + line-height: 9px; + /* Undo all space this takes up. */ + margin-bottom: -12px; +} + +/* Actual content area for the button. */ +.goog-imageless-button-content { + line-height: 1.5em; + padding: 0px 4px; + text-align: center; +} + + +/* Pill (collapsed border) styles. */ +.goog-imageless-button-collapse-right { + /* Draw a border on the root element to square the button off. The border + * on the outer-box element remains, but gets obscured by the next button. */ + border-right-width: 1px; + margin-right: -2px; /* Undoes the margins between the two buttons. */ +} + +.goog-imageless-button-collapse-left .goog-imageless-button-outer-box { + /* Don't bleed to the left -- keep the border self contained in the box. */ + border-left-color: #eee; + left: 0; + margin-right: -1px; /* Versus the default of -2px. */ +} + + +/* Disabled styles. */ +.goog-imageless-button-disabled, +.goog-imageless-button-disabled .goog-imageless-button-outer-box { + background: #eee; + border-color: #ccc; + color: #666; /* For text */ +} + +.goog-imageless-button-disabled .goog-imageless-button-top-shadow { + /* Just hide the shadow instead of setting individual colors. */ + visibility: hidden; +} + + +/* + * Active and checked styles. + * Identical except for text color according to GUIG. + */ +.goog-imageless-button-active, .goog-imageless-button-checked { + background: #f9f9f9; +} + +.goog-imageless-button-active .goog-imageless-button-top-shadow, +.goog-imageless-button-checked .goog-imageless-button-top-shadow { + background: #e3e3e3; +} + +.goog-imageless-button-active { + color: #000; +} + + +/* Hover styles. Higher priority to override other border styles. */ +.goog-imageless-button-hover, +.goog-imageless-button-hover .goog-imageless-button-outer-box, +.goog-imageless-button-focused, +.goog-imageless-button-focused .goog-imageless-button-outer-box { + border-color: #000; +} + + +/* IE6 hacks. This is the only place inner-box is used. */ +* html .goog-imageless-button-inner-box { + /* Give the element inline-block behavior so that the shadow appears. + * The main requirement is to give the element layout without having the side + * effect of taking up a full line. */ + display: inline; + /* Allow the shadow to show through, overriding position:relative from the + * goog-inline-block styles. */ + position: static; + zoom: 1; +} + +* html .goog-imageless-button-outer-box { + /* In RTL mode, IE is off by one pixel. To fix, override the left: -1px + * (which was flipped to right) without having any effect on LTR mode + * (where IE ignores right when left is specified). */ + /* @noflip */ right: 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/imagelessmenubutton.css b/mapbox-gl/closure-library/closure/goog/css/imagelessmenubutton.css new file mode 100644 index 0000000000..0c8b6fdcc9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/imagelessmenubutton.css @@ -0,0 +1,23 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for buttons created by goog.ui.ImagelessMenuButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + * @author dalewis@google.com (Darren Lewis) + */ + +/* Dropdown arrow style. */ +.goog-imageless-button-dropdown { + height: 16px; + width: 7px; + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0; + vertical-align: top; + margin-right: 2px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/inputdatepicker.css b/mapbox-gl/closure-library/closure/goog/css/inputdatepicker.css new file mode 100644 index 0000000000..4f93182196 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/inputdatepicker.css @@ -0,0 +1,12 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: arv@google.com (Erik Arvidsson) */ + +/* goog.ui.InputDatePicker */ + +@import url(popupdatepicker.css); diff --git a/mapbox-gl/closure-library/closure/goog/css/linkbutton.css b/mapbox-gl/closure-library/closure/goog/css/linkbutton.css new file mode 100644 index 0000000000..9f9ec3a3f9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/linkbutton.css @@ -0,0 +1,26 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Styling for link buttons created by goog.ui.LinkButtonRenderer. + * + * @author robbyw@google.com (Robby Walker) + */ + +.goog-link-button { + position: relative; + color: #00f; + text-decoration: underline; + cursor: pointer; +} + +/* State: disabled. */ +.goog-link-button-disabled { + color: #888; + text-decoration: none; + cursor: default; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/menu.css b/mapbox-gl/closure-library/closure/goog/css/menu.css new file mode 100644 index 0000000000..da66222d72 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/menu.css @@ -0,0 +1,27 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for menus created by goog.ui.MenuRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +.goog-menu { + background: #fff; + border-color: #ccc #666 #666 #ccc; + border-style: solid; + border-width: 1px; + cursor: default; + font: normal 13px Arial, sans-serif; + margin: 0; + outline: none; + padding: 4px 0; + position: absolute; + z-index: 20000; /* Arbitrary, but some apps depend on it... */ +} diff --git a/mapbox-gl/closure-library/closure/goog/css/menubar.css b/mapbox-gl/closure-library/closure/goog/css/menubar.css new file mode 100644 index 0000000000..6f69b4664c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/menubar.css @@ -0,0 +1,57 @@ +/* + * Copyright 2012 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * styling for goog.ui.menuBar and child buttons. + * + * @author tvykruta@google.com (Tomas Vykruta) + */ + + +.goog-menubar { + cursor: default; + outline: none; + position: relative; + white-space: nowrap; + background: #fff; +} + +.goog-menubar .goog-menu-button { + padding: 1px 1px; + margin: 0px 0px; + outline: none; + border: none; + background: #fff; + /* @alternate */ border: 1px solid #fff; +} + +.goog-menubar .goog-menu-button-dropdown { + display: none; +} + +.goog-menubar .goog-menu-button-outer-box { + border: none; +} + +.goog-menubar .goog-menu-button-inner-box { + border: none; +} + +.goog-menubar .goog-menu-button-hover { + background: #eee; + border: 1px solid #eee; +} + +.goog-menubar .goog-menu-button-open { + background: #fff; + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; +} + +.goog-menubar .goog-menu-button-disabled { + color: #ccc; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/menubutton.css b/mapbox-gl/closure-library/closure/goog/css/menubutton.css new file mode 100644 index 0000000000..82c94b248a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/menubutton.css @@ -0,0 +1,169 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for buttons created by goog.ui.MenuButtonRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +/* State: resting. */ +.goog-menu-button { + /* Client apps may override the URL at which they serve the image. */ + background: #ddd url(//ssl.gstatic.com/editor/button-bg.png) repeat-x top left; + border: 0; + color: #000; + cursor: pointer; + list-style: none; + margin: 2px; + outline: none; + padding: 0; + text-decoration: none; + vertical-align: middle; +} + +/* Pseudo-rounded corners. */ +.goog-menu-button-outer-box, +.goog-menu-button-inner-box { + border-style: solid; + border-color: #aaa; + vertical-align: top; +} +.goog-menu-button-outer-box { + margin: 0; + border-width: 1px 0; + padding: 0; +} +.goog-menu-button-inner-box { + margin: 0 -1px; + border-width: 0 1px; + padding: 3px 4px; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-menu-button-inner-box { + /* IE6 needs to have the box shifted to make the borders line up. */ + left: -1px; +} + +/* Pre-IE7 BiDi fixes. */ +* html .goog-menu-button-rtl .goog-menu-button-outer-box { + /* @noflip */ left: -1px; + /* @noflip */ right: auto; +} +* html .goog-menu-button-rtl .goog-menu-button-inner-box { + /* @noflip */ right: auto; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-menu-button-inner-box { + /* IE7 needs to have the box shifted to make the borders line up. */ + left: -1px; +} +/* IE7 BiDi fix. */ +*:first-child+html .goog-menu-button-rtl .goog-menu-button-inner-box { + /* @noflip */ left: 1px; + /* @noflip */ right: auto; +} + +/* Safari-only hacks. */ +::root .goog-menu-button, +::root .goog-menu-button-outer-box, +::root .goog-menu-button-inner-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: 0; +} +::root .goog-menu-button-caption, +::root .goog-menu-button-dropdown { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: normal; +} + +/* State: disabled. */ +.goog-menu-button-disabled { + background-image: none !important; + opacity: 0.3; + -moz-opacity: 0.3; + filter: alpha(opacity=30); +} +.goog-menu-button-disabled .goog-menu-button-outer-box, +.goog-menu-button-disabled .goog-menu-button-inner-box, +.goog-menu-button-disabled .goog-menu-button-caption, +.goog-menu-button-disabled .goog-menu-button-dropdown { + color: #333 !important; + border-color: #999 !important; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-menu-button-disabled { + margin: 2px 1px !important; + padding: 0 1px !important; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-menu-button-disabled { + margin: 2px 1px !important; + padding: 0 1px !important; +} + +/* State: hover. */ +.goog-menu-button-hover .goog-menu-button-outer-box, +.goog-menu-button-hover .goog-menu-button-inner-box { + border-color: #9cf #69e #69e #7af !important; /* Hover border wins. */ +} + +/* State: active, open. */ +.goog-menu-button-active, +.goog-menu-button-open { + background-color: #bbb; + background-position: bottom left; +} + +/* State: focused. */ +.goog-menu-button-focused .goog-menu-button-outer-box, +.goog-menu-button-focused .goog-menu-button-inner-box { + border-color: orange; +} + +/* Caption style. */ +.goog-menu-button-caption { + padding: 0 4px 0 0; + vertical-align: top; +} + +/* Dropdown arrow style. */ +.goog-menu-button-dropdown { + height: 15px; + width: 7px; + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0; + vertical-align: top; +} + +/* Pill (collapsed border) styles. */ +/* TODO(gboyer): Remove specific menu button styles and have any button support being a menu button. */ +.goog-menu-button-collapse-right, +.goog-menu-button-collapse-right .goog-menu-button-outer-box, +.goog-menu-button-collapse-right .goog-menu-button-inner-box { + margin-right: 0; +} + +.goog-menu-button-collapse-left, +.goog-menu-button-collapse-left .goog-menu-button-outer-box, +.goog-menu-button-collapse-left .goog-menu-button-inner-box { + margin-left: 0; +} + +.goog-menu-button-collapse-left .goog-menu-button-inner-box { + border-left: 1px solid #fff; +} + +.goog-menu-button-collapse-left.goog-menu-button-checked +.goog-menu-button-inner-box { + border-left: 1px solid #ddd; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/menuitem.css b/mapbox-gl/closure-library/closure/goog/css/menuitem.css new file mode 100644 index 0000000000..cea9de6801 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/menuitem.css @@ -0,0 +1,148 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for menus created by goog.ui.MenuItemRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +/** + * State: resting. + * + * NOTE(mleibman,chrishenry): + * The RTL support in Closure is provided via two mechanisms -- "rtl" CSS + * classes and BiDi flipping done by the CSS compiler. Closure supports RTL + * with or without the use of the CSS compiler. In order for them not + * to conflict with each other, the "rtl" CSS classes need to have the @noflip + * annotation. The non-rtl counterparts should ideally have them as well, but, + * since .goog-menuitem existed without .goog-menuitem-rtl for so long before + * being added, there is a risk of people having templates where they are not + * rendering the .goog-menuitem-rtl class when in RTL and instead rely solely + * on the BiDi flipping by the CSS compiler. That's why we're not adding the + * @noflip to .goog-menuitem. + */ +.goog-menuitem { + color: #000; + font: normal 13px Arial, sans-serif; + list-style: none; + margin: 0; + /* 28px on the left for icon or checkbox; 7em on the right for shortcut. */ + padding: 4px 7em 4px 28px; + white-space: nowrap; +} + +/* BiDi override for the resting state. */ +/* @noflip */ +.goog-menuitem.goog-menuitem-rtl { + /* Flip left/right padding for BiDi. */ + padding-left: 7em; + padding-right: 28px; +} + +/* If a menu doesn't have checkable items or items with icons, remove padding. */ +.goog-menu-nocheckbox .goog-menuitem, +.goog-menu-noicon .goog-menuitem { + padding-left: 12px; +} + +/* + * If a menu doesn't have items with shortcuts, leave just enough room for + * submenu arrows, if they are rendered. + */ +.goog-menu-noaccel .goog-menuitem { + padding-right: 20px; +} + +.goog-menuitem-content { + color: #000; + font: normal 13px Arial, sans-serif; +} + +/* State: disabled. */ +.goog-menuitem-disabled .goog-menuitem-accel, +.goog-menuitem-disabled .goog-menuitem-content { + color: #ccc !important; +} +.goog-menuitem-disabled .goog-menuitem-icon { + opacity: 0.3; + -moz-opacity: 0.3; + filter: alpha(opacity=30); +} + +/* State: hover. */ +.goog-menuitem-highlight, +.goog-menuitem-hover { + background-color: #d6e9f8; + /* Use an explicit top and bottom border so that the selection is visible + * in high contrast mode. */ + border-color: #d6e9f8; + border-style: dotted; + border-width: 1px 0; + padding-bottom: 3px; + padding-top: 3px; +} + +/* State: selected/checked. */ +.goog-menuitem-checkbox, +.goog-menuitem-icon { + background-repeat: no-repeat; + height: 16px; + left: 6px; + position: absolute; + right: auto; + vertical-align: middle; + width: 16px; +} + +/* BiDi override for the selected/checked state. */ +/* @noflip */ +.goog-menuitem-rtl .goog-menuitem-checkbox, +.goog-menuitem-rtl .goog-menuitem-icon { + /* Flip left/right positioning. */ + left: auto; + right: 6px; +} + +.goog-option-selected .goog-menuitem-checkbox, +.goog-option-selected .goog-menuitem-icon { + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0; +} + +/* Keyboard shortcut ("accelerator") style. */ +.goog-menuitem-accel { + color: #999; + /* Keyboard shortcuts are untranslated; always left-to-right. */ + /* @noflip */ direction: ltr; + left: auto; + padding: 0 6px; + position: absolute; + right: 0; + text-align: right; +} + +/* BiDi override for shortcut style. */ +/* @noflip */ +.goog-menuitem-rtl .goog-menuitem-accel { + /* Flip left/right positioning and text alignment. */ + left: 0; + right: auto; + text-align: left; +} + +/* Mnemonic styles. */ +.goog-menuitem-mnemonic-hint { + text-decoration: underline; +} + +.goog-menuitem-mnemonic-separator { + color: #999; + font-size: 12px; + padding-left: 4px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/menuseparator.css b/mapbox-gl/closure-library/closure/goog/css/menuseparator.css new file mode 100644 index 0000000000..de1354f70d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/menuseparator.css @@ -0,0 +1,19 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for menus created by goog.ui.MenuSeparatorRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +.goog-menuseparator { + border-top: 1px solid #ccc; + margin: 4px 0; + padding: 0; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/multitestrunner.css b/mapbox-gl/closure-library/closure/goog/css/multitestrunner.css new file mode 100644 index 0000000000..2cdffeac30 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/multitestrunner.css @@ -0,0 +1,121 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ + +.goog-testrunner { + background-color: #EEE; + border: 1px solid #999; + padding: 10px; + padding-bottom: 25px; +} + +.goog-testrunner-progress { + width: auto; + height: 20px; + background-color: #FFF; + border: 1px solid #999; +} + +.goog-testrunner-progress table { + width: 100%; + height: 20px; + border-collapse: collapse; +} + +.goog-testrunner-buttons { + margin-top: 7px; +} + +.goog-testrunner-buttons button { + width: 75px; +} + +.goog-testrunner-log, +.goog-testrunner-report, +.goog-testrunner-stats { + margin-top: 7px; + width: auto; + height: 400px; + background-color: #FFF; + border: 1px solid #999; + font: normal medium monospace; + padding: 5px; + overflow: auto; /* Opera doesn't support overflow-y. */ + overflow-y: scroll; + overflow-x: auto; +} + +.goog-testrunner-report div { + margin-bottom: 6px; + border-bottom: 1px solid #999; +} + +.goog-testrunner-stats table { + margin-top: 20px; + border-collapse: collapse; + border: 1px solid #EEE; +} + +.goog-testrunner-stats td, +.goog-testrunner-stats th { + padding: 2px 6px; + border: 1px solid #F0F0F0; +} + +.goog-testrunner-stats th { + font-weight: bold; +} + +.goog-testrunner-stats .center { + text-align: center; +} + +.goog-testrunner-progress-summary { + font: bold small sans-serif; +} + +.goog-testrunner iframe { + position: absolute; + left: -640px; + top: -480px; + width: 640px; + height: 480px; + margin: 0; + border: 0; + padding: 0; +} + +.goog-testrunner-report-failure { + color: #900; +} + +.goog-testrunner-reporttab, +.goog-testrunner-logtab, +.goog-testrunner-statstab { + float: left; + width: 50px; + height: 16px; + text-align: center; + font: normal small arial, helvetica, sans-serif; + color: #666; + background-color: #DDD; + border: 1px solid #999; + border-top: 0; + cursor: pointer; +} + +.goog-testrunner-reporttab, +.goog-testrunner-logtab { + border-right: 0; +} + +.goog-testrunner-activetab { + font-weight: bold; + color: #000; + background-color: #CCC; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/palette.css b/mapbox-gl/closure-library/closure/goog/css/palette.css new file mode 100644 index 0000000000..8360afc16b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/palette.css @@ -0,0 +1,36 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for palettes created by goog.ui.PaletteRenderer. + * + * @author pupius@google.com (Daniel Pupius) + * @author attila@google.com (Attila Bodis) + */ + + +.goog-palette { + cursor: default; + outline: none; +} + +.goog-palette-table { + border: 1px solid #666; + border-collapse: collapse; + margin: 5px; +} + +.goog-palette-cell { + border: 0; + border-right: 1px solid #666; + cursor: pointer; + height: 18px; + margin: 0; + text-align: center; + vertical-align: middle; + width: 18px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/popupdatepicker.css b/mapbox-gl/closure-library/closure/goog/css/popupdatepicker.css new file mode 100644 index 0000000000..133173ab53 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/popupdatepicker.css @@ -0,0 +1,17 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for a goog.ui.PopupDatePicker. + * + * @author arv@google.com (Erik Arvidsson) + */ + +.goog-date-picker { + position: absolute; +} + diff --git a/mapbox-gl/closure-library/closure/goog/css/roundedpanel.css b/mapbox-gl/closure-library/closure/goog/css/roundedpanel.css new file mode 100644 index 0000000000..d931e41d1f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/roundedpanel.css @@ -0,0 +1,29 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styles for RoundedPanel. + * + * @author pallosp@google.com (Peter Pallos) + */ + +.goog-roundedpanel { + position: relative; + z-index: 0; +} + +.goog-roundedpanel-background { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: -1; +} + +.goog-roundedpanel-content { +} diff --git a/mapbox-gl/closure-library/closure/goog/css/roundedtab.css b/mapbox-gl/closure-library/closure/goog/css/roundedtab.css new file mode 100644 index 0000000000..17fe155f56 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/roundedtab.css @@ -0,0 +1,158 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: attila@google.com (Attila Bodis) */ + + +/* + * Styles used by goog.ui.RoundedTabRenderer. + */ +.goog-rounded-tab { + border: 0; + cursor: default; + padding: 0; +} + +.goog-tab-bar-top .goog-rounded-tab, +.goog-tab-bar-bottom .goog-rounded-tab { + float: left; + margin: 0 4px 0 0; +} + +.goog-tab-bar-start .goog-rounded-tab, +.goog-tab-bar-end .goog-rounded-tab { + margin: 0 0 4px 0; +} + +.goog-rounded-tab-caption { + border: 0; + color: #fff; + margin: 0; + padding: 4px 8px; +} + +.goog-rounded-tab-caption, +.goog-rounded-tab-inner-edge, +.goog-rounded-tab-outer-edge { + background: #036; + border-right: 1px solid #003; +} + +.goog-rounded-tab-inner-edge, +.goog-rounded-tab-outer-edge { + font-size: 1px; + height: 1px; + overflow: hidden; +} + +/* State: Hover */ +.goog-rounded-tab-hover .goog-rounded-tab-caption, +.goog-rounded-tab-hover .goog-rounded-tab-inner-edge, +.goog-rounded-tab-hover .goog-rounded-tab-outer-edge { + background-color: #69c; + border-right: 1px solid #369; +} + +/* State: Disabled */ +.goog-rounded-tab-disabled .goog-rounded-tab-caption, +.goog-rounded-tab-disabled .goog-rounded-tab-inner-edge, +.goog-rounded-tab-disabled .goog-rounded-tab-outer-edge { + background: #ccc; + border-right: 1px solid #ccc; +} + +/* State: Selected */ +.goog-rounded-tab-selected .goog-rounded-tab-caption, +.goog-rounded-tab-selected .goog-rounded-tab-inner-edge, +.goog-rounded-tab-selected .goog-rounded-tab-outer-edge { + background: #369 !important; /* Selected trumps hover. */ + border-right: 1px solid #036 !important; +} + + +/* + * Styles for horizontal (top or bottom) tabs. + */ +.goog-tab-bar-top .goog-rounded-tab { + vertical-align: bottom; +} + +.goog-tab-bar-bottom .goog-rounded-tab { + vertical-align: top; +} + +.goog-tab-bar-top .goog-rounded-tab-outer-edge, +.goog-tab-bar-bottom .goog-rounded-tab-outer-edge { + margin: 0 3px; +} + +.goog-tab-bar-top .goog-rounded-tab-inner-edge, +.goog-tab-bar-bottom .goog-rounded-tab-inner-edge { + margin: 0 1px; +} + + +/* + * Styles for vertical (start or end) tabs. + */ +.goog-tab-bar-start .goog-rounded-tab-table, +.goog-tab-bar-end .goog-rounded-tab-table { + width: 100%; +} + +.goog-tab-bar-start .goog-rounded-tab-inner-edge { + margin-left: 1px; +} + +.goog-tab-bar-start .goog-rounded-tab-outer-edge { + margin-left: 3px; +} + +.goog-tab-bar-end .goog-rounded-tab-inner-edge { + margin-right: 1px; +} + +.goog-tab-bar-end .goog-rounded-tab-outer-edge { + margin-right: 3px; +} + + +/* + * Overrides for start tabs. + */ +.goog-tab-bar-start .goog-rounded-tab-table, +.goog-tab-bar-end .goog-rounded-tab-table { + width: 12ex; /* TODO(attila): Make this work for variable width. */ +} + +.goog-tab-bar-start .goog-rounded-tab-caption, +.goog-tab-bar-start .goog-rounded-tab-inner-edge, +.goog-tab-bar-start .goog-rounded-tab-outer-edge { + border-left: 1px solid #003; + border-right: 0; +} + +.goog-tab-bar-start .goog-rounded-tab-hover .goog-rounded-tab-caption, +.goog-tab-bar-start .goog-rounded-tab-hover .goog-rounded-tab-inner-edge, +.goog-tab-bar-start .goog-rounded-tab-hover .goog-rounded-tab-outer-edge { + border-left: 1px solid #369 !important; + border-right: 0 !important; +} + +.goog-tab-bar-start .goog-rounded-tab-selected .goog-rounded-tab-outer-edge, +.goog-tab-bar-start .goog-rounded-tab-selected .goog-rounded-tab-inner-edge, +.goog-tab-bar-start .goog-rounded-tab-selected .goog-rounded-tab-caption { + border-left: 1px solid #036 !important; + border-right: 0 !important; +} + +.goog-tab-bar-start .goog-rounded-tab-disabled .goog-rounded-tab-outer-edge, +.goog-tab-bar-start .goog-rounded-tab-disabled .goog-rounded-tab-inner-edge, +.goog-tab-bar-start .goog-rounded-tab-disabled .goog-rounded-tab-caption { + border-left: 1px solid #ccc !important; + border-right: 0 !important; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/submenu.css b/mapbox-gl/closure-library/closure/goog/css/submenu.css new file mode 100644 index 0000000000..1159b289cf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/submenu.css @@ -0,0 +1,38 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for menus created by goog.ui.SubMenuRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + +/* State: resting. */ +/* @noflip */ +.goog-submenu-arrow { + color: #000; + left: auto; + padding-right: 6px; + position: absolute; + right: 0; + text-align: right; +} + +/* BiDi override. */ +/* @noflip */ +.goog-menuitem-rtl .goog-submenu-arrow { + text-align: left; + left: 0; + right: auto; + padding-left: 6px; +} + +/* State: disabled. */ +.goog-menuitem-disabled .goog-submenu-arrow { + color: #ccc; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/tab.css b/mapbox-gl/closure-library/closure/goog/css/tab.css new file mode 100644 index 0000000000..6c7dfe2ef0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tab.css @@ -0,0 +1,105 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: attila@google.com (Attila Bodis) */ +/* Author: eae@google.com (Emil A. Eklund) */ + + +/* + * Styles used by goog.ui.TabRenderer. + */ +.goog-tab { + position: relative; + padding: 4px 8px; + color: #00c; + text-decoration: underline; + cursor: default; +} + +.goog-tab-bar-top .goog-tab { + margin: 1px 4px 0 0; + border-bottom: 0; + float: left; +} + +.goog-tab-bar-top:after, +.goog-tab-bar-bottom:after { + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.goog-tab-bar-bottom .goog-tab { + margin: 0 4px 1px 0; + border-top: 0; + float: left; +} + +.goog-tab-bar-start .goog-tab { + margin: 0 0 4px 1px; + border-right: 0; +} + +.goog-tab-bar-end .goog-tab { + margin: 0 1px 4px 0; + border-left: 0; +} + +/* State: Hover */ +.goog-tab-hover { + background: #eee; +} + +/* State: Disabled */ +.goog-tab-disabled { + color: #666; +} + +/* State: Selected */ +.goog-tab-selected { + color: #000; + background: #fff; + text-decoration: none; + font-weight: bold; + border: 1px solid #6b90da; +} + +.goog-tab-bar-top { + padding-top: 5px !important; + padding-left: 5px !important; + border-bottom: 1px solid #6b90da !important; +} +/* + * Shift selected tabs 1px towards the contents (and compensate via margin and + * padding) to visually merge the borders of the tab with the borders of the + * content area. + */ +.goog-tab-bar-top .goog-tab-selected { + top: 1px; + margin-top: 0; + padding-bottom: 5px; +} + +.goog-tab-bar-bottom .goog-tab-selected { + top: -1px; + margin-bottom: 0; + padding-top: 5px; +} + +.goog-tab-bar-start .goog-tab-selected { + left: 1px; + margin-left: 0; + padding-right: 9px; +} + +.goog-tab-bar-end .goog-tab-selected { + left: -1px; + margin-right: 0; + padding-left: 9px; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/tabbar.css b/mapbox-gl/closure-library/closure/goog/css/tabbar.css new file mode 100644 index 0000000000..514aa9bacd --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tabbar.css @@ -0,0 +1,52 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: attila@google.com (Attila Bodis) */ +/* Author: eae@google.com (Emil A. Eklund) */ + + +/* + * Styles used by goog.ui.TabBarRenderer. + */ +.goog-tab-bar { + margin: 0; + border: 0; + padding: 0; + list-style: none; + cursor: default; + outline: none; + background: #ebeff9; +} + +.goog-tab-bar-clear { + clear: both; + height: 0; + overflow: hidden; +} + +.goog-tab-bar-start { + float: left; +} + +.goog-tab-bar-end { + float: right; +} + + +/* + * IE6-only hacks to fix the gap between the floated tabs and the content. + * IE7 and later will ignore these. + */ +/* @if user.agent ie6 */ +* html .goog-tab-bar-start { + margin-right: -3px; +} + +* html .goog-tab-bar-end { + margin-left: -3px; +} +/* @endif */ diff --git a/mapbox-gl/closure-library/closure/goog/css/tablesorter.css b/mapbox-gl/closure-library/closure/goog/css/tablesorter.css new file mode 100644 index 0000000000..126f007ffb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tablesorter.css @@ -0,0 +1,14 @@ +/* + * Copyright 2008 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: robbyw@google.com (Robby Walker) */ + +/* Styles for goog.ui.TableSorter. */ + +.goog-tablesorter-header { + cursor: pointer +} diff --git a/mapbox-gl/closure-library/closure/goog/css/toolbar.css b/mapbox-gl/closure-library/closure/goog/css/toolbar.css new file mode 100644 index 0000000000..5c39ddedaf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/toolbar.css @@ -0,0 +1,400 @@ +/* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* + * Standard styling for toolbars and toolbar items. + * + * @author attila@google.com (Attila Bodis) + */ + + +/* + * Styles used by goog.ui.ToolbarRenderer. + */ + +.goog-toolbar { + /* Client apps may override the URL at which they serve the image. */ + background: #fafafa url(//ssl.gstatic.com/editor/toolbar-bg.png) repeat-x bottom left; + border-bottom: 1px solid #d5d5d5; + cursor: default; + font: normal 12px Arial, sans-serif; + margin: 0; + outline: none; + padding: 2px; + position: relative; + zoom: 1; /* The toolbar element must have layout on IE. */ +} + +/* + * Styles used by goog.ui.ToolbarButtonRenderer. + */ + +.goog-toolbar-button { + margin: 0 2px; + border: 0; + padding: 0; + font-family: Arial, sans-serif; + color: #333; + text-decoration: none; + list-style: none; + vertical-align: middle; + cursor: default; + outline: none; +} + +/* Pseudo-rounded corners. */ +.goog-toolbar-button-outer-box, +.goog-toolbar-button-inner-box { + border: 0; + vertical-align: top; +} + +.goog-toolbar-button-outer-box { + margin: 0; + padding: 1px 0; +} + +.goog-toolbar-button-inner-box { + margin: 0 -1px; + padding: 3px 4px; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-toolbar-button-inner-box { + /* IE6 needs to have the box shifted to make the borders line up. */ + left: -1px; +} + +/* Pre-IE7 BiDi fixes. */ +* html .goog-toolbar-button-rtl .goog-toolbar-button-outer-box { + /* @noflip */ left: -1px; +} +* html .goog-toolbar-button-rtl .goog-toolbar-button-inner-box { + /* @noflip */ right: auto; +} + + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-toolbar-button-inner-box { + /* IE7 needs to have the box shifted to make the borders line up. */ + left: -1px; +} + +/* IE7 BiDi fix. */ +*:first-child+html .goog-toolbar-button-rtl .goog-toolbar-button-inner-box { + /* @noflip */ left: 1px; + /* @noflip */ right: auto; +} + +/* Safari-only hacks. */ +::root .goog-toolbar-button, +::root .goog-toolbar-button-outer-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: 0; +} + +::root .goog-toolbar-button-inner-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: normal; +} + +/* Disabled styles. */ +.goog-toolbar-button-disabled { + opacity: 0.3; + -moz-opacity: 0.3; + filter: alpha(opacity=30); +} + +.goog-toolbar-button-disabled .goog-toolbar-button-outer-box, +.goog-toolbar-button-disabled .goog-toolbar-button-inner-box { + /* Disabled text/border color trumps everything else. */ + color: #333 !important; + border-color: #999 !important; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-toolbar-button-disabled { + /* IE can't apply alpha to an element with a transparent background... */ + background-color: #f0f0f0; + margin: 0 1px; + padding: 0 1px; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-toolbar-button-disabled { + /* IE can't apply alpha to an element with a transparent background... */ + background-color: #f0f0f0; + margin: 0 1px; + padding: 0 1px; +} + +/* Only draw borders when in a non-default state. */ +.goog-toolbar-button-hover .goog-toolbar-button-outer-box, +.goog-toolbar-button-active .goog-toolbar-button-outer-box, +.goog-toolbar-button-checked .goog-toolbar-button-outer-box, +.goog-toolbar-button-selected .goog-toolbar-button-outer-box { + border-width: 1px 0; + border-style: solid; + padding: 0; +} + +.goog-toolbar-button-hover .goog-toolbar-button-inner-box, +.goog-toolbar-button-active .goog-toolbar-button-inner-box, +.goog-toolbar-button-checked .goog-toolbar-button-inner-box, +.goog-toolbar-button-selected .goog-toolbar-button-inner-box { + border-width: 0 1px; + border-style: solid; + padding: 3px; +} + +/* Hover styles. */ +.goog-toolbar-button-hover .goog-toolbar-button-outer-box, +.goog-toolbar-button-hover .goog-toolbar-button-inner-box { + /* Hover border style wins over active/checked/selected. */ + border-color: #a1badf !important; +} + +/* Active/checked/selected styles. */ +.goog-toolbar-button-active, +.goog-toolbar-button-checked, +.goog-toolbar-button-selected { + /* Active/checked/selected background color always wins. */ + background-color: #dde1eb !important; +} + +.goog-toolbar-button-active .goog-toolbar-button-outer-box, +.goog-toolbar-button-active .goog-toolbar-button-inner-box, +.goog-toolbar-button-checked .goog-toolbar-button-outer-box, +.goog-toolbar-button-checked .goog-toolbar-button-inner-box, +.goog-toolbar-button-selected .goog-toolbar-button-outer-box, +.goog-toolbar-button-selected .goog-toolbar-button-inner-box { + border-color: #729bd1; +} + +/* Pill (collapsed border) styles. */ +.goog-toolbar-button-collapse-right, +.goog-toolbar-button-collapse-right .goog-toolbar-button-outer-box, +.goog-toolbar-button-collapse-right .goog-toolbar-button-inner-box { + margin-right: 0; +} + +.goog-toolbar-button-collapse-left, +.goog-toolbar-button-collapse-left .goog-toolbar-button-outer-box, +.goog-toolbar-button-collapse-left .goog-toolbar-button-inner-box { + margin-left: 0; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-toolbar-button-collapse-left .goog-toolbar-button-inner-box { + left: 0; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-toolbar-button-collapse-left +.goog-toolbar-button-inner-box { + left: 0; +} + + +/* + * Styles used by goog.ui.ToolbarMenuButtonRenderer. + */ + +.goog-toolbar-menu-button { + margin: 0 2px; + border: 0; + padding: 0; + font-family: Arial, sans-serif; + color: #333; + text-decoration: none; + list-style: none; + vertical-align: middle; + cursor: default; + outline: none; +} + +/* Pseudo-rounded corners. */ +.goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-inner-box { + border: 0; + vertical-align: top; +} + +.goog-toolbar-menu-button-outer-box { + margin: 0; + padding: 1px 0; +} + +.goog-toolbar-menu-button-inner-box { + margin: 0 -1px; + padding: 3px 4px; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-toolbar-menu-button-inner-box { + /* IE6 needs to have the box shifted to make the borders line up. */ + left: -1px; +} + +/* Pre-IE7 BiDi fixes. */ +* html .goog-toolbar-menu-button-rtl .goog-toolbar-menu-button-outer-box { + /* @noflip */ left: -1px; +} +* html .goog-toolbar-menu-button-rtl .goog-toolbar-menu-button-inner-box { + /* @noflip */ right: auto; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-toolbar-menu-button-inner-box { + /* IE7 needs to have the box shifted to make the borders line up. */ + left: -1px; +} + +/* IE7 BiDi fix. */ +*:first-child+html .goog-toolbar-menu-button-rtl + .goog-toolbar-menu-button-inner-box { + /* @noflip */ left: 1px; + /* @noflip */ right: auto; +} + +/* Safari-only hacks. */ +::root .goog-toolbar-menu-button, +::root .goog-toolbar-menu-button-outer-box, +::root .goog-toolbar-menu-button-inner-box { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: 0; +} + +::root .goog-toolbar-menu-button-caption, +::root .goog-toolbar-menu-button-dropdown { + /* Required to make pseudo-rounded corners work on Safari. */ + line-height: normal; +} + +/* Disabled styles. */ +.goog-toolbar-menu-button-disabled { + opacity: 0.3; + -moz-opacity: 0.3; + filter: alpha(opacity=30); +} + +.goog-toolbar-menu-button-disabled .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-disabled .goog-toolbar-menu-button-inner-box { + /* Disabled text/border color trumps everything else. */ + color: #333 !important; + border-color: #999 !important; +} + +/* Pre-IE7 IE hack; ignored by IE7 and all non-IE browsers. */ +* html .goog-toolbar-menu-button-disabled { + /* IE can't apply alpha to an element with a transparent background... */ + background-color: #f0f0f0; + margin: 0 1px; + padding: 0 1px; +} + +/* IE7-only hack; ignored by all other browsers. */ +*:first-child+html .goog-toolbar-menu-button-disabled { + /* IE can't apply alpha to an element with a transparent background... */ + background-color: #f0f0f0; + margin: 0 1px; + padding: 0 1px; +} + +/* Only draw borders when in a non-default state. */ +.goog-toolbar-menu-button-hover .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-active .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-open .goog-toolbar-menu-button-outer-box { + border-width: 1px 0; + border-style: solid; + padding: 0; +} + +.goog-toolbar-menu-button-hover .goog-toolbar-menu-button-inner-box, +.goog-toolbar-menu-button-active .goog-toolbar-menu-button-inner-box, +.goog-toolbar-menu-button-open .goog-toolbar-menu-button-inner-box { + border-width: 0 1px; + border-style: solid; + padding: 3px; +} + +/* Hover styles. */ +.goog-toolbar-menu-button-hover .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-hover .goog-toolbar-menu-button-inner-box { + /* Hover border color trumps active/open style. */ + border-color: #a1badf !important; +} + +/* Active/open styles. */ +.goog-toolbar-menu-button-active, +.goog-toolbar-menu-button-open { + /* Active/open background color wins. */ + background-color: #dde1eb !important; +} + +.goog-toolbar-menu-button-active .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-active .goog-toolbar-menu-button-inner-box, +.goog-toolbar-menu-button-open .goog-toolbar-menu-button-outer-box, +.goog-toolbar-menu-button-open .goog-toolbar-menu-button-inner-box { + border-color: #729bd1; +} + +/* Menu button caption style. */ +.goog-toolbar-menu-button-caption { + padding: 0 4px 0 0; + vertical-align: middle; +} + +/* Dropdown style. */ +.goog-toolbar-menu-button-dropdown { + width: 7px; + /* Client apps may override the URL at which they serve the sprite. */ + background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -388px 0; + vertical-align: middle; +} + + +/* + * Styles used by goog.ui.ToolbarSeparatorRenderer. + */ + +.goog-toolbar-separator { + margin: 0 2px; + border-left: 1px solid #d6d6d6; + border-right: 1px solid #f7f7f7; + padding: 0; + width: 0; + text-decoration: none; + list-style: none; + outline: none; + vertical-align: middle; + line-height: normal; + font-size: 120%; + overflow: hidden; +} + + +/* + * Additional styling for toolbar select controls, which always have borders. + */ + +.goog-toolbar-select .goog-toolbar-menu-button-outer-box { + border-width: 1px 0; + border-style: solid; + padding: 0; +} + +.goog-toolbar-select .goog-toolbar-menu-button-inner-box { + border-width: 0 1px; + border-style: solid; + padding: 3px; +} + +.goog-toolbar-select .goog-toolbar-menu-button-outer-box, +.goog-toolbar-select .goog-toolbar-menu-button-inner-box { + border-color: #bfcbdf; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/tooltip.css b/mapbox-gl/closure-library/closure/goog/css/tooltip.css new file mode 100644 index 0000000000..026458327a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tooltip.css @@ -0,0 +1,14 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +.goog-tooltip { + background: #ffe; + border: 1px solid #999; + border-width: 1px 2px 2px 1px; + padding: 6px; + z-index: 30000; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/tree.css b/mapbox-gl/closure-library/closure/goog/css/tree.css new file mode 100644 index 0000000000..aeb1d0b3d4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tree.css @@ -0,0 +1,146 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: arv@google.com (Erik Arvidsson) */ +/* Author: eae@google.com (Emil A Eklund) */ +/* Author: jonp@google.com (Jon Perlow) */ + +/* + TODO(arv): Currently the sprite image has the height 16px. We should make the + image taller which would allow better flexibility when it comes to the height + of a tree row. +*/ + +.goog-tree-root:focus { + outline: none; +} + +.goog-tree-row { + white-space: nowrap; + font: icon; + line-height: 16px; + height: 16px; +} + +.goog-tree-row span { + overflow: hidden; + text-overflow: ellipsis; +} + +.goog-tree-children { + background-repeat: repeat-y; + background-image: url(//ssl.gstatic.com/closure/tree/I.png) !important; + background-position-y: 1px !important; /* IE only */ + font: icon; +} + +.goog-tree-children-nolines { + font: icon; +} + +.goog-tree-icon { + background-image: url(//ssl.gstatic.com/closure/tree/tree.png); +} + +.goog-tree-expand-icon { + vertical-align: middle; + height: 16px; + width: 16px; + cursor: default; +} + +.goog-tree-expand-icon-plus { + width: 19px; + background-position: 0 0; +} + +.goog-tree-expand-icon-minus { + width: 19px; + background-position: -24px 0; +} + +.goog-tree-expand-icon-tplus { + width: 19px; + background-position: -48px 0; +} + +.goog-tree-expand-icon-tminus { + width: 19px; + background-position: -72px 0; +} + +.goog-tree-expand-icon-lplus { + width: 19px; + background-position: -96px 0; +} + +.goog-tree-expand-icon-lminus { + width: 19px; + background-position: -120px 0; +} + +.goog-tree-expand-icon-t { + width: 19px; + background-position: -144px 0; +} + +.goog-tree-expand-icon-l { + width: 19px; + background-position: -168px 0; +} + +.goog-tree-expand-icon-blank { + width: 19px; + background-position: -168px -24px; +} + +.goog-tree-collapsed-folder-icon { + vertical-align: middle; + height: 16px; + width: 16px; + background-position: -0px -24px; +} + +.goog-tree-expanded-folder-icon { + vertical-align: middle; + height: 16px; + width: 16px; + background-position: -24px -24px; +} + +.goog-tree-file-icon { + vertical-align: middle; + height: 16px; + width: 16px; + background-position: -48px -24px; +} + +.goog-tree-item-label { + margin-left: 3px; + padding: 1px 2px 1px 2px; + text-decoration: none; + color: WindowText; + cursor: default; +} + +.goog-tree-item-label:hover { + text-decoration: underline; +} + +.selected .goog-tree-item-label { + background-color: ButtonFace; + color: ButtonText; +} + +.focused .selected .goog-tree-item-label { + background-color: Highlight; + color: HighlightText; +} + +.goog-tree-hide-root { + display: none; +} diff --git a/mapbox-gl/closure-library/closure/goog/css/tristatemenuitem.css b/mapbox-gl/closure-library/closure/goog/css/tristatemenuitem.css new file mode 100644 index 0000000000..8c98448afb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/css/tristatemenuitem.css @@ -0,0 +1,43 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: pupius@google.com (Daniel Pupius) */ + +/* goog.ui.TriStateMenuItem */ + +.goog-tristatemenuitem { + padding: 2px 5px; + margin: 0; + list-style: none; +} + +.goog-tristatemenuitem-highlight { + background-color: #4279A5; + color: #FFF; +} + +.goog-tristatemenuitem-disabled { + color: #999; +} + +.goog-tristatemenuitem-checkbox { + float: left; + width: 10px; + height: 1.1em; +} + +.goog-tristatemenuitem-partially-checked { + background-image: url(//ssl.gstatic.com/closure/check-outline.gif); + background-position: 4px 50%; + background-repeat: no-repeat; +} + +.goog-tristatemenuitem-fully-checked { + background-image: url(//ssl.gstatic.com/closure/check.gif); + background-position: 4px 50%; + background-repeat: no-repeat; +} diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom.js b/mapbox-gl/closure-library/closure/goog/cssom/cssom.js new file mode 100644 index 0000000000..0be1b5add9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom.js @@ -0,0 +1,454 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview CSS Object Model helper functions. + * References: + * - W3C: http://dev.w3.org/csswg/cssom/ + * - MSDN: http://msdn.microsoft.com/en-us/library/ms531209(VS.85).aspx. + * @supported in FF3, IE6, IE7, Safari 3.1.2, Chrome + * TODO(user): Fix in Opera. + * TODO(user): Consider hacking page, media, etc.. to work. + * This would be pretty challenging. IE returns the text for any rule + * regardless of whether or not the media is correct or not. Firefox at + * least supports CSSRule.type to figure out if it's a media type and then + * we could do something interesting, but IE offers no way for us to tell. + */ + +goog.provide('goog.cssom'); +goog.provide('goog.cssom.CssRuleType'); + +goog.require('goog.array'); +goog.require('goog.dom'); + + +/** + * Enumeration of {@code CSSRule} types. + * @enum {number} + */ +goog.cssom.CssRuleType = { + STYLE: 1, + IMPORT: 3, + MEDIA: 4, + FONT_FACE: 5, + PAGE: 6, + NAMESPACE: 7 +}; + + +/** + * Recursively gets all CSS as text, optionally starting from a given + * CSSStyleSheet. + * @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet The CSSStyleSheet. + * @return {string} css text. + */ +goog.cssom.getAllCssText = function(opt_styleSheet) { + var styleSheet = opt_styleSheet || document.styleSheets; + return /** @type {string} */ (goog.cssom.getAllCss_(styleSheet, true)); +}; + + +/** + * Recursively gets all CSSStyleRules, optionally starting from a given + * CSSStyleSheet. + * Note that this excludes any CSSImportRules, CSSMediaRules, etc.. + * @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet The CSSStyleSheet. + * @return {Array} A list of CSSStyleRules. + */ +goog.cssom.getAllCssStyleRules = function(opt_styleSheet) { + var styleSheet = opt_styleSheet || document.styleSheets; + return /** @type {!Array} */ ( + goog.cssom.getAllCss_(styleSheet, false)); +}; + + +/** + * Returns the CSSRules from a styleSheet. + * Worth noting here is that IE and FF differ in terms of what they will return. + * Firefox will return styleSheet.cssRules, which includes ImportRules and + * anything which implements the CSSRules interface. IE returns simply a list of + * CSSRules. + * @param {CSSStyleSheet} styleSheet The CSSStyleSheet. + * @throws {Error} If we cannot access the rules on a stylesheet object - this + * can happen if a stylesheet object's rules are accessed before the rules + * have been downloaded and parsed and are "ready". + * @return {CSSRuleList} An array of CSSRules or null. + */ +goog.cssom.getCssRulesFromStyleSheet = function(styleSheet) { + var cssRuleList = null; + try { + // Select cssRules unless it isn't present. For pre-IE9 IE, use the rules + // collection instead. + // It's important to be consistent in using only the W3C or IE apis on + // IE9+ where both are present to ensure that there is no indexing + // mismatches - the collections are subtly different in what the include or + // exclude which can lead to one collection being longer than the other + // depending on the page's construction. + cssRuleList = styleSheet.cssRules /* W3C */ || styleSheet.rules /* IE */; + } catch (e) { + // This can happen if we try to access the CSSOM before it's "ready". + if (e.code == 15) { + // Firefox throws an NS_ERROR_DOM_INVALID_ACCESS_ERR error if a stylesheet + // is read before it has been fully parsed. Let the caller know which + // stylesheet failed. + e.styleSheet = styleSheet; + throw e; + } + } + return cssRuleList; +}; + + +/** + * Gets all CSSStyleSheet objects starting from some CSSStyleSheet. Note that we + * want to return the sheets in the order of the cascade, therefore if we + * encounter an import, we will splice that CSSStyleSheet object in front of + * the CSSStyleSheet that contains it in the returned array of CSSStyleSheets. + * @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet A CSSStyleSheet. + * @param {boolean=} opt_includeDisabled If true, includes disabled stylesheets, + * defaults to false. + * @return {!Array} A list of CSSStyleSheet objects. + */ +goog.cssom.getAllCssStyleSheets = function(opt_styleSheet, + opt_includeDisabled) { + var styleSheetsOutput = []; + var styleSheet = opt_styleSheet || document.styleSheets; + var includeDisabled = goog.isDef(opt_includeDisabled) ? opt_includeDisabled : + false; + + // Imports need to go first. + if (styleSheet.imports && styleSheet.imports.length) { + for (var i = 0, n = styleSheet.imports.length; i < n; i++) { + goog.array.extend(styleSheetsOutput, + goog.cssom.getAllCssStyleSheets(styleSheet.imports[i])); + } + + } else if (styleSheet.length) { + // In case we get a StyleSheetList object. + // http://dev.w3.org/csswg/cssom/#the-stylesheetlist + for (var i = 0, n = styleSheet.length; i < n; i++) { + goog.array.extend(styleSheetsOutput, + goog.cssom.getAllCssStyleSheets(styleSheet[i])); + } + } else { + // We need to walk through rules in browsers which implement .cssRules + // to see if there are styleSheets buried in there. + // If we have a CSSStyleSheet within CssRules. + var cssRuleList = goog.cssom.getCssRulesFromStyleSheet( + /** @type {!CSSStyleSheet} */ (styleSheet)); + if (cssRuleList && cssRuleList.length) { + // Chrome does not evaluate cssRuleList[i] to undefined when i >=n; + // so we use a (i < n) check instead of cssRuleList[i] in the loop below + // and in other places where we iterate over a rules list. + // See issue # 5917 in Chromium. + for (var i = 0, n = cssRuleList.length, cssRule; i < n; i++) { + cssRule = cssRuleList[i]; + // There are more stylesheets to get on this object.. + if (cssRule.styleSheet) { + goog.array.extend(styleSheetsOutput, + goog.cssom.getAllCssStyleSheets(cssRule.styleSheet)); + } + } + } + } + + // This is a CSSStyleSheet. (IE uses .rules, W3c and Opera cssRules.) + if ((styleSheet.type || styleSheet.rules || styleSheet.cssRules) && + (!styleSheet.disabled || includeDisabled)) { + styleSheetsOutput.push(styleSheet); + } + + return styleSheetsOutput; +}; + + +/** + * Gets the cssText from a CSSRule object cross-browserly. + * @param {CSSRule} cssRule A CSSRule. + * @return {string} cssText The text for the rule, including the selector. + */ +goog.cssom.getCssTextFromCssRule = function(cssRule) { + var cssText = ''; + + if (cssRule.cssText) { + // W3C. + cssText = cssRule.cssText; + } else if (cssRule.style && cssRule.style.cssText && cssRule.selectorText) { + // IE: The spacing here is intended to make the result consistent with + // FF and Webkit. + // We also remove the special properties that we may have added in + // getAllCssStyleRules since IE includes those in the cssText. + var styleCssText = cssRule.style.cssText. + replace(/\s*-closure-parent-stylesheet:\s*\[object\];?\s*/gi, ''). + replace(/\s*-closure-rule-index:\s*[\d]+;?\s*/gi, ''); + var thisCssText = cssRule.selectorText + ' { ' + styleCssText + ' }'; + cssText = thisCssText; + } + + return cssText; +}; + + +/** + * Get the index of the CSSRule in it's CSSStyleSheet. + * @param {CSSRule} cssRule A CSSRule. + * @param {CSSStyleSheet=} opt_parentStyleSheet A reference to the stylesheet + * object this cssRule belongs to. + * @throws {Error} When we cannot get the parentStyleSheet. + * @return {number} The index of the CSSRule, or -1. + */ +goog.cssom.getCssRuleIndexInParentStyleSheet = function(cssRule, + opt_parentStyleSheet) { + // Look for our special style.ruleIndex property from getAllCss. + if (cssRule.style && cssRule.style['-closure-rule-index']) { + return cssRule.style['-closure-rule-index']; + } + + var parentStyleSheet = opt_parentStyleSheet || + goog.cssom.getParentStyleSheet(cssRule); + + if (!parentStyleSheet) { + // We could call getAllCssStyleRules() here to get our special indexes on + // the style object, but that seems like it could be wasteful. + throw Error('Cannot find a parentStyleSheet.'); + } + + var cssRuleList = goog.cssom.getCssRulesFromStyleSheet(parentStyleSheet); + if (cssRuleList && cssRuleList.length) { + for (var i = 0, n = cssRuleList.length, thisCssRule; i < n; i++) { + thisCssRule = cssRuleList[i]; + if (thisCssRule == cssRule) { + return i; + } + } + } + return -1; +}; + + +/** + * We do some trickery in getAllCssStyleRules that hacks this in for IE. + * If the cssRule object isn't coming from a result of that function call, this + * method will return undefined in IE. + * @param {CSSRule} cssRule The CSSRule. + * @return {CSSStyleSheet} A styleSheet object. + */ +goog.cssom.getParentStyleSheet = function(cssRule) { + return cssRule.parentStyleSheet || + cssRule.style && + cssRule.style['-closure-parent-stylesheet']; +}; + + +/** + * Replace a cssRule with some cssText for a new rule. + * If the cssRule object is not one of objects returned by + * getAllCssStyleRules, then you'll need to provide both the styleSheet and + * possibly the index, since we can't infer them from the standard cssRule + * object in IE. We do some trickery in getAllCssStyleRules to hack this in. + * @param {CSSRule} cssRule A CSSRule. + * @param {string} cssText The text for the new CSSRule. + * @param {CSSStyleSheet=} opt_parentStyleSheet A reference to the stylesheet + * object this cssRule belongs to. + * @param {number=} opt_index The index of the cssRule in its parentStylesheet. + * @throws {Error} If we cannot find a parentStyleSheet. + * @throws {Error} If we cannot find a css rule index. + */ +goog.cssom.replaceCssRule = function(cssRule, cssText, opt_parentStyleSheet, + opt_index) { + var parentStyleSheet = opt_parentStyleSheet || + goog.cssom.getParentStyleSheet(cssRule); + if (parentStyleSheet) { + var index = opt_index >= 0 ? opt_index : + goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule, parentStyleSheet); + if (index >= 0) { + goog.cssom.removeCssRule(parentStyleSheet, index); + goog.cssom.addCssRule(parentStyleSheet, cssText, index); + } else { + throw Error('Cannot proceed without the index of the cssRule.'); + } + } else { + throw Error('Cannot proceed without the parentStyleSheet.'); + } +}; + + +/** + * Cross browser function to add a CSSRule into a CSSStyleSheet, optionally + * at a given index. + * @param {CSSStyleSheet} cssStyleSheet The CSSRule's parentStyleSheet. + * @param {string} cssText The text for the new CSSRule. + * @param {number=} opt_index The index of the cssRule in its parentStylesheet. + * @throws {Error} If the css rule text appears to be ill-formatted. + * TODO(bowdidge): Inserting at index 0 fails on Firefox 2 and 3 with an + * exception warning "Node cannot be inserted at the specified point in + * the hierarchy." + */ +goog.cssom.addCssRule = function(cssStyleSheet, cssText, opt_index) { + var index = opt_index; + if (index < 0 || index == undefined) { + // If no index specified, insert at the end of the current list + // of rules. + var rules = goog.cssom.getCssRulesFromStyleSheet(cssStyleSheet); + index = rules.length; + } + if (cssStyleSheet.insertRule) { + // W3C (including IE9+). + cssStyleSheet.insertRule(cssText, index); + + } else { + // IE, pre 9: We have to parse the cssRule text to get the selector + // separated from the style text. + // aka Everything that isn't a colon, followed by a colon, then + // the rest is the style part. + var matches = /^([^\{]+)\{([^\{]+)\}/.exec(cssText); + if (matches.length == 3) { + var selector = matches[1]; + var style = matches[2]; + cssStyleSheet.addRule(selector, style, index); + } else { + throw Error('Your CSSRule appears to be ill-formatted.'); + } + } +}; + + +/** + * Cross browser function to remove a CSSRule in a CSSStyleSheet at an index. + * @param {CSSStyleSheet} cssStyleSheet The CSSRule's parentStyleSheet. + * @param {number} index The CSSRule's index in the parentStyleSheet. + */ +goog.cssom.removeCssRule = function(cssStyleSheet, index) { + if (cssStyleSheet.deleteRule) { + // W3C. + cssStyleSheet.deleteRule(index); + + } else { + // IE. + cssStyleSheet.removeRule(index); + } +}; + + +/** + * Appends a DOM node to HEAD containing the css text that's passed in. + * @param {string} cssText CSS to add to the end of the document. + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper user for + * document interactions. + * @return {!Element} The newly created STYLE element. + */ +goog.cssom.addCssText = function(cssText, opt_domHelper) { + var document = opt_domHelper ? opt_domHelper.getDocument() : + goog.dom.getDocument(); + var cssNode = document.createElement('style'); + cssNode.type = 'text/css'; + var head = document.getElementsByTagName('head')[0]; + head.appendChild(cssNode); + if (cssNode.styleSheet) { + // IE. + cssNode.styleSheet.cssText = cssText; + } else { + // W3C. + var cssTextNode = document.createTextNode(cssText); + cssNode.appendChild(cssTextNode); + } + return cssNode; +}; + + +/** + * Cross browser method to get the filename from the StyleSheet's href. + * Explorer only returns the filename in the href, while other agents return + * the full path. + * @param {!StyleSheet} styleSheet Any valid StyleSheet object with an href. + * @throws {Error} When there's no href property found. + * @return {?string} filename The filename, or null if not an external + * styleSheet. + */ +goog.cssom.getFileNameFromStyleSheet = function(styleSheet) { + var href = styleSheet.href; + + // Another IE/FF difference. IE returns an empty string, while FF and others + // return null for CSSStyleSheets not from an external file. + if (!href) { + return null; + } + + // We need the regexp to ensure we get the filename minus any query params. + var matches = /([^\/\?]+)[^\/]*$/.exec(href); + var filename = matches[1]; + return filename; +}; + + +/** + * Recursively gets all CSS text or rules. + * @param {CSSStyleSheet|StyleSheetList} styleSheet The CSSStyleSheet. + * @param {boolean} isTextOutput If true, output is cssText, otherwise cssRules. + * @return {string|!Array} cssText or cssRules. + * @private + */ +goog.cssom.getAllCss_ = function(styleSheet, isTextOutput) { + var cssOut = []; + var styleSheets = goog.cssom.getAllCssStyleSheets(styleSheet); + + for (var i = 0; styleSheet = styleSheets[i]; i++) { + var cssRuleList = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + + if (cssRuleList && cssRuleList.length) { + + // We're going to track cssRule index if we want rule output. + if (!isTextOutput) { + var ruleIndex = 0; + } + + for (var j = 0, n = cssRuleList.length, cssRule; j < n; j++) { + cssRule = cssRuleList[j]; + // Gets cssText output, ignoring CSSImportRules. + if (isTextOutput && !cssRule.href) { + var res = goog.cssom.getCssTextFromCssRule(cssRule); + cssOut.push(res); + + } else if (!cssRule.href) { + // Gets cssRules output, ignoring CSSImportRules. + if (cssRule.style) { + // This is a fun little hack to get parentStyleSheet into the rule + // object for IE since it failed to implement rule.parentStyleSheet. + // We can later read this property when doing things like hunting + // for indexes in order to delete a given CSSRule. + // Unfortunately we have to use the style object to store these + // pieces of info since the rule object is read-only. + if (!cssRule.parentStyleSheet) { + cssRule.style['-closure-parent-stylesheet'] = styleSheet; + } + + // This is a hack to help with possible removal of the rule later, + // where we just append the rule's index in its parentStyleSheet + // onto the style object as a property. + // Unfortunately we have to use the style object to store these + // pieces of info since the rule object is read-only. + cssRule.style['-closure-rule-index'] = ruleIndex; + } + cssOut.push(cssRule); + } + + if (!isTextOutput) { + ruleIndex++; + } + } + } + } + return isTextOutput ? cssOut.join(' ') : cssOut; +}; + diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.html b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.html new file mode 100644 index 0000000000..cdd18a1980 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.html @@ -0,0 +1,39 @@ + + + + + + + + Closure Unit Tests - CSS Object Model helper + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.js b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.js new file mode 100644 index 0000000000..80b5002ff1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test.js @@ -0,0 +1,323 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.cssomTest'); +goog.setTestOnly('goog.cssomTest'); + +goog.require('goog.array'); +goog.require('goog.cssom'); +goog.require('goog.cssom.CssRuleType'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +// Since sheet cssom_test1.css's first line is to import +// cssom_test2.css, we should get 2 before one in the string. +var cssText = '.css-link-1 { display: block; } ' + + '.css-import-2 { display: block; } ' + + '.css-import-1 { display: block; } ' + + '.css-style-1 { display: block; } ' + + '.css-style-2 { display: block; } ' + + '.css-style-3 { display: block; }'; + +var replacementCssText = '.css-repl-1 { display: block; }'; + +var isIe7 = goog.userAgent.IE && + (goog.userAgent.compare(goog.userAgent.VERSION, '7.0') == 0); + +// We're going to toLowerCase cssText before testing, because IE returns +// CSS property names in UPPERCASE, and the function shouldn't +// "fix" the text as it would be expensive and rarely of use. +// Same goes for the trailing whitespace in IE. +// Same goes for fixing the optimized removal of trailing ; in rules. +// Also needed for Opera. +function fixCssTextForIe(cssText) { + cssText = cssText.toLowerCase().replace(/\s*$/, ''); + if (cssText.match(/[^;] \}/)) { + cssText = cssText.replace(/([^;]) \}/g, '$1; }'); + } + return cssText; +} + +function testGetFileNameFromStyleSheet() { + var styleSheet = {'href': 'http://foo.com/something/filename.css'}; + assertEquals('filename.css', + goog.cssom.getFileNameFromStyleSheet(styleSheet)); + + styleSheet = {'href': 'https://foo.com:123/something/filename.css'}; + assertEquals('filename.css', + goog.cssom.getFileNameFromStyleSheet(styleSheet)); + + styleSheet = {'href': 'http://foo.com/something/filename.css?bar=bas'}; + assertEquals('filename.css', + goog.cssom.getFileNameFromStyleSheet(styleSheet)); + + styleSheet = {'href': 'filename.css?bar=bas'}; + assertEquals('filename.css', + goog.cssom.getFileNameFromStyleSheet(styleSheet)); + + styleSheet = {'href': 'filename.css'}; + assertEquals('filename.css', + goog.cssom.getFileNameFromStyleSheet(styleSheet)); +} + +function testGetAllCssStyleSheets() { + var styleSheets = goog.cssom.getAllCssStyleSheets(); + assertEquals(4, styleSheets.length); + // Makes sure they're in the right cascade order. + assertEquals('cssom_test_link_1.css', + goog.cssom.getFileNameFromStyleSheet(styleSheets[0])); + assertEquals('cssom_test_import_2.css', + goog.cssom.getFileNameFromStyleSheet(styleSheets[1])); + assertEquals('cssom_test_import_1.css', + goog.cssom.getFileNameFromStyleSheet(styleSheets[2])); + // Not an external styleSheet + assertNull(goog.cssom.getFileNameFromStyleSheet(styleSheets[3])); +} + +function testGetAllCssText() { + var allCssText = goog.cssom.getAllCssText(); + // In IE7, a CSSRule object gets included twice and replaces another + // existing CSSRule object. We aren't using + // goog.testing.ExpectedFailures since it brings in additional CSS + // which breaks a lot of our expectations about the number of rules + // present in a style sheet. + if (!isIe7) { + assertEquals(cssText, fixCssTextForIe(allCssText)); + } +} + +function testGetAllCssStyleRules() { + var allCssRules = goog.cssom.getAllCssStyleRules(); + assertEquals(6, allCssRules.length); +} + + +function testAddCssText() { + var newCssText = '.css-add-1 { display: block; }'; + var newCssNode = goog.cssom.addCssText(newCssText); + + assertEquals(document.styleSheets.length, 3); + + var allCssText = goog.cssom.getAllCssText(); + + // In IE7, a CSSRule object gets included twice and replaces another + // existing CSSRule object. We aren't using + // goog.testing.ExpectedFailures since it brings in additional CSS + // which breaks a lot of our expectations about the number of rules + // present in a style sheet. + if (!isIe7) { + // Opera inserts the CSSRule to the first position. And fixCssText + // is also needed to clean up whitespace. + if (goog.userAgent.OPERA) { + assertEquals(newCssText + ' ' + cssText, + fixCssTextForIe(allCssText)); + } else { + assertEquals(cssText + ' ' + newCssText, + fixCssTextForIe(allCssText)); + } + } + + var cssRules = goog.cssom.getAllCssStyleRules(); + assertEquals(7, cssRules.length); + + // Remove the new stylesheet now so it doesn't interfere with other + // tests. + newCssNode.parentNode.removeChild(newCssNode); + // Sanity check. + cssRules = goog.cssom.getAllCssStyleRules(); + assertEquals(6, cssRules.length); +} + +function testAddCssRule() { + // test that addCssRule correctly adds the rule to the style + // sheet. + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var newCssRule = '.css-addCssRule { display: block; }'; + var rules = styleSheet.rules || styleSheet.cssRules; + var origNumberOfRules = rules.length; + + goog.cssom.addCssRule(styleSheet, newCssRule, 1); + + rules = styleSheet.rules || styleSheet.cssRules; + var newNumberOfRules = rules.length; + assertEquals(newNumberOfRules, origNumberOfRules + 1); + + // Remove the added rule so we don't mess up other tests. + goog.cssom.removeCssRule(styleSheet, 1); +} + +function testAddCssRuleAtPos() { + // test that addCssRule correctly adds the rule to the style + // sheet at the specified position. + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var newCssRule = '.css-addCssRulePos { display: block; }'; + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var origNumberOfRules = rules.length; + + // Firefox croaks if we try to insert a CSSRule at an index that + // contains a CSSImport Rule. Since we deal only with CSSStyleRule + // objects, we find the first CSSStyleRule and return its index. + // + // NOTE(user): We could have unified the code block below for all + // browsers but IE6 horribly mangled up the stylesheet by creating + // duplicate instances of a rule when removeCssRule was invoked + // just after addCssRule with the looping construct in. This is + // perfectly fine since IE's styleSheet.rules does not contain + // references to anything but CSSStyleRules. + var pos = 0; + if (styleSheet.cssRules) { + pos = goog.array.findIndex(rules, function(rule) { + return rule.type == goog.cssom.CssRuleType.STYLE; + }); + } + goog.cssom.addCssRule(styleSheet, newCssRule, pos); + + rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var newNumberOfRules = rules.length; + assertEquals(newNumberOfRules, origNumberOfRules + 1); + + // Remove the added rule so we don't mess up other tests. + goog.cssom.removeCssRule(styleSheet, pos); + + rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + assertEquals(origNumberOfRules, rules.length); +} + +function testAddCssRuleNoIndex() { + // How well do we handle cases where the optional index is + // not passed in? + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var origNumberOfRules = rules.length; + var newCssRule = '.css-addCssRuleNoIndex { display: block; }'; + + // Try inserting the rule without specifying an index. + // Make sure we don't throw an exception, and that we added + // the entry. + goog.cssom.addCssRule(styleSheet, newCssRule); + + rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var newNumberOfRules = rules.length; + assertEquals(newNumberOfRules, origNumberOfRules + 1); + + // Remove the added rule so we don't mess up the other tests. + goog.cssom.removeCssRule(styleSheet, newNumberOfRules - 1); + + rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + assertEquals(origNumberOfRules, rules.length); +} + + +function testGetParentStyleSheetAfterGetAllCssStyleRules() { + var cssRules = goog.cssom.getAllCssStyleRules(); + var cssRule = cssRules[4]; + var parentStyleSheet = goog.cssom.getParentStyleSheet(cssRule); + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + assertEquals(styleSheet, parentStyleSheet); +} + +function testGetCssRuleIndexInParentStyleSheetAfterGetAllCssStyleRules() { + var cssRules = goog.cssom.getAllCssStyleRules(); + var cssRule = cssRules[4]; + // Note here that this is correct - IE's styleSheet.rules does not + // contain references to anything but CSSStyleRules while FF and others + // include anything that inherits from the CSSRule interface. + // See http://dev.w3.org/csswg/cssom/#cssrule. + var parentStyleSheet = goog.cssom.getParentStyleSheet(cssRule); + var ruleIndex = goog.isDefAndNotNull(parentStyleSheet.cssRules) ? 2 : 1; + assertEquals(ruleIndex, + goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule)); +} + +function testGetCssRuleIndexInParentStyleSheetNonStyleRule() { + // IE's styleSheet.rules only contain CSSStyleRules. + if (!goog.userAgent.IE) { + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var newCssRule = '@media print { .css-nonStyle { display: block; } }'; + goog.cssom.addCssRule(styleSheet, newCssRule); + var rules = styleSheet.rules || styleSheet.cssRules; + var cssRule = rules[rules.length - 1]; + assertEquals(goog.cssom.CssRuleType.MEDIA, cssRule.type); + // Make sure we don't throw an exception. + goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule, styleSheet); + // Remove the added rule. + goog.cssom.removeCssRule(styleSheet, rules.length - 1); + } +} + +// Tests the scenario where we have a known stylesheet and index. +function testReplaceCssRuleWithStyleSheetAndIndex() { + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var index = 2; + var origCssRule = rules[index]; + var origCssText = + fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule)); + + goog.cssom.replaceCssRule(origCssRule, replacementCssText, styleSheet, + index); + + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var newCssRule = rules[index]; + var newCssText = goog.cssom.getCssTextFromCssRule(newCssRule); + assertEquals(replacementCssText, fixCssTextForIe(newCssText)); + + // Now we need to re-replace our rule, to preserve parity for the other + // tests. + goog.cssom.replaceCssRule(newCssRule, origCssText, styleSheet, index); + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var nowCssRule = rules[index]; + var nowCssText = goog.cssom.getCssTextFromCssRule(nowCssRule); + assertEquals(origCssText, fixCssTextForIe(nowCssText)); +} + +function testReplaceCssRuleUsingGetAllCssStyleRules() { + var cssRules = goog.cssom.getAllCssStyleRules(); + var origCssRule = cssRules[4]; + var origCssText = + fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule)); + // notice we don't pass in the stylesheet or index. + goog.cssom.replaceCssRule(origCssRule, replacementCssText); + + var styleSheets = goog.cssom.getAllCssStyleSheets(); + var styleSheet = styleSheets[3]; + var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); + var index = goog.isDefAndNotNull(styleSheet.cssRules) ? 2 : 1; + var newCssRule = rules[index]; + var newCssText = + fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule)); + assertEquals(replacementCssText, newCssText); + + // try getting it the other way around too. + var cssRules = goog.cssom.getAllCssStyleRules(); + var newCssRule = cssRules[4]; + var newCssText = + fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule)); + assertEquals(replacementCssText, newCssText); + + // Now we need to re-replace our rule, to preserve parity for the other + // tests. + goog.cssom.replaceCssRule(newCssRule, origCssText); + var cssRules = goog.cssom.getAllCssStyleRules(); + var nowCssRule = cssRules[4]; + var nowCssText = + fixCssTextForIe(goog.cssom.getCssTextFromCssRule(nowCssRule)); + assertEquals(origCssText, nowCssText); +} diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_1.css b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_1.css new file mode 100644 index 0000000000..566f907ff8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_1.css @@ -0,0 +1,11 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +@import "cssom_test_import_2.css"; +.css-import-1 { + display: block; +} diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_2.css b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_2.css new file mode 100644 index 0000000000..dc31c9620a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_import_2.css @@ -0,0 +1,10 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +.css-import-2 { + display: block; +} diff --git a/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_link_1.css b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_link_1.css new file mode 100644 index 0000000000..832a8e31ce --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/cssom_test_link_1.css @@ -0,0 +1,10 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +.css-link-1 { + display: block; +} diff --git a/mapbox-gl/closure-library/closure/goog/cssom/iframe/style.js b/mapbox-gl/closure-library/closure/goog/cssom/iframe/style.js new file mode 100644 index 0000000000..731bf357ce --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/cssom/iframe/style.js @@ -0,0 +1,1024 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved. + +/** + * @fileoverview Provides utility routines for copying modified + * {@code CSSRule} objects from the parent document into iframes so that any + * content in the iframe will be styled as if it was inline in the parent + * document. + * + *

+ * For example, you might have this CSS rule: + * + * #content .highlighted { background-color: yellow; } + * + * And this DOM structure: + * + *

+ * + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/autocompleterichremotedata.js b/mapbox-gl/closure-library/closure/goog/demos/autocompleterichremotedata.js new file mode 100644 index 0000000000..0fad7db1ec --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/autocompleterichremotedata.js @@ -0,0 +1,33 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** @nocompile */ + +[ + ['apple', + {name: 'Fuji', url: 'http://www.google.com/images?q=fuji+apple'}, + {name: 'Gala', url: 'http://www.google.com/images?q=gala+apple'}, + {name: 'Golden Delicious', + url: 'http://www.google.com/images?q=golden delicious+apple'} + ], + ['citrus', + {name: 'Lemon', url: 'http://www.google.com/images?q=lemon+fruit'}, + {name: 'Orange', url: 'http://www.google.com/images?q=orange+fruit'} + ], + ['berry', + {name: 'Strawberry', url: 'http://www.google.com/images?q=strawberry+fruit'}, + {name: 'Blueberry', url: 'http://www.google.com/images?q=blueberry+fruit'}, + {name: 'Blackberry', url: 'http://www.google.com/images?q=blackberry+fruit'} + ] +] diff --git a/mapbox-gl/closure-library/closure/goog/demos/bidiinput.html b/mapbox-gl/closure-library/closure/goog/demos/bidiinput.html new file mode 100644 index 0000000000..7cafe51de1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/bidiinput.html @@ -0,0 +1,72 @@ + + + + + goog.ui.BidiInput + + + + + +

goog.ui.BidiInput

+ +

+ The direction of the input field changes automatically to RTL (right to left) + if the contents is in an RTL language (e.g. Hebrew or Arabic). +

+ +
+ A decorated input:  + Text: + +
+ +
+ +
+ An input created programmatically:  + Text: ! + + +
+ + +
+ +
+ A decorated textarea:  + + +
+ +
+ +
+
+ Right to left div:  + + +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/blobhasher.html b/mapbox-gl/closure-library/closure/goog/demos/blobhasher.html new file mode 100644 index 0000000000..2403f82652 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/blobhasher.html @@ -0,0 +1,61 @@ + + + + +goog.crypt.BlobHasher + + + + + +

goog.crypt.BlobHasher

+ + + +
File: + + +
MD5:
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/bubble.html b/mapbox-gl/closure-library/closure/goog/demos/bubble.html new file mode 100644 index 0000000000..e10bb0e3fd --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/bubble.html @@ -0,0 +1,250 @@ + + + + + + goog.ui.Bubble + + + + + + +

goog.ui.Bubble

+ + + + + + + + + + + + + + + + + + +
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
X:
Y:
Corner orientation(0-3):
Auto-hide (true or false):
Timeout (ms):
Decorated
+ +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/button.html b/mapbox-gl/closure-library/closure/goog/demos/button.html new file mode 100644 index 0000000000..7d86c906de --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/button.html @@ -0,0 +1,395 @@ + + + + + goog.ui.Button and goog.ui.ToggleButton + + + + + + + + + + +

goog.ui.Button

+
+ + The first Button was created programmatically, + the second by decorating an <input> element:  + +
+ +
+
+
+ +
+
+
+ +

goog.ui.FlatButtonRenderer

+
+ + Buttons made with <div>'s instead of + <input>'s or <button>'s + The first rendered, the second decorated:  + +
+ +
+
+
My Flat Button

+ +
+
+
+ Combined +
+ Buttons +
+
+
+
+ +

goog.ui.LinkButtonRenderer

+
+ + Like FlatButtonRenderer, except the style makes the button appear to be a + link. + +
+ +
+ +

goog.ui.CustomButton & goog.ui.ToggleButton

+
+ + These buttons were rendered using goog.ui.CustomButton: +   + +
+ These buttons were created programmatically:
+
+
+ These buttons were created by decorating some DIVs, and they dispatch + state transition events (watch the event log):
+
+ +
+ Decorated Button, yay! +
+
Decorated Disabled
+
Another Button
+
+ Archive +
+ Delete +
+ Report Spam +
+
+
+ Use these ToggleButtons to hide/show and enable/disable + the middle button:
+
Enable
+   +
Show
+

+ Combined toggle buttons:
+
+ Bold +
+ Italics +
+ Underlined +
+
+
+ These buttons have icons, and the second one has an extra CSS class:
+
+
+ + The button with the orange + outline has keyboard focus. Hit Enter to activate focused buttons. + +
+
+
+ +
+ Event Log +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/charcounter.html b/mapbox-gl/closure-library/closure/goog/demos/charcounter.html new file mode 100644 index 0000000000..a37c32148f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/charcounter.html @@ -0,0 +1,57 @@ + + + + + goog.ui.CharCounter + + + + + +

goog.ui.CharCounter

+ +

+ + character(s) remaining +

+

+ + You have entered character(s) of a maximum 160. +

+

+ + character(s) remaining + + +

+

+ + character(s) remaining +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/charpicker.html b/mapbox-gl/closure-library/closure/goog/demos/charpicker.html new file mode 100644 index 0000000000..7c769c8d02 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/charpicker.html @@ -0,0 +1,66 @@ + + + + + goog.ui.CharPicker + + + + + + + + + + + + + + + + + +

goog.ui.CharPicker

+

You have selected: none +

+ + +

+ +

+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/checkbox.html b/mapbox-gl/closure-library/closure/goog/demos/checkbox.html new file mode 100644 index 0000000000..7bfd59fdbc --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/checkbox.html @@ -0,0 +1,122 @@ + + + + + goog.ui.Checkbox + + + + + + +

goog.ui.Checkbox

+

This is a tri-state checkbox.

+
+ Enable/disable
+
+
root
+
+
leaf 1
+
leaf 2
+
+
+
+ Created with render +
+
+
+ Created with decorate + + + + +
+

+ +
+ Event Log for 'root', 'leaf1', 'leaf2' +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/color-contrast.html b/mapbox-gl/closure-library/closure/goog/demos/color-contrast.html new file mode 100644 index 0000000000..58eeac7884 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/color-contrast.html @@ -0,0 +1,60 @@ + + + + + + + Color Contrast Test + + + + + +

+ # + + This text should be readable +

+

(Only choosing from black and white.)

+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/colormenubutton.html b/mapbox-gl/closure-library/closure/goog/demos/colormenubutton.html new file mode 100644 index 0000000000..ba68c0f264 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/colormenubutton.html @@ -0,0 +1,218 @@ + + + + + goog.ui.ColorMenuButton + + + + + + + + + + + + + + + +

goog.ui.ColorMenuButton Demo

+ + + + + + + +
+
+ goog.ui.ColorMenuButton demo: +
This button was created programmatically: 
+
+
+ This button decorates a DIV:  +
Color
+
+
+
This button has a custom color menu: 
+
+
+
+
+ + goog.ui.ToolbarColorMenuButtonRenderer demo: + +
+ This toolbar button was created programmatically:  +
+
+
+ This toolbar button decorates a DIV:  +
Color
+
+
+
+ This is what these would look like in an actual toolbar, with + icons instead of text captions: +
+
+
+
+
+
+
+
+
+
+
+
+
+ BiDi is all the rage these days +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/colorpicker.html b/mapbox-gl/closure-library/closure/goog/demos/colorpicker.html new file mode 100644 index 0000000000..863a598f4b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/colorpicker.html @@ -0,0 +1,39 @@ + + + + + goog.ui.ColorPicker + + + + + + +

goog.ui.ColorPicker

+ +

Simple Color Grid

+
+

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/combobox.html b/mapbox-gl/closure-library/closure/goog/demos/combobox.html new file mode 100644 index 0000000000..d336b6ee27 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/combobox.html @@ -0,0 +1,160 @@ + + + + + goog.ui.ComboBox + + + + + + + + + + +

goog.ui.ComboBox

+
cb.value = ''
+ +
+ LTR +
+
+ +
+ LTR +
+
+ +
+
+ LTR +
+
+
+ +
+ +
+ RTL +
+
+ +
+ RTL +
+
+ +
+
+ RTL +
+
+
+ + +
+ + Clear Log +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/container.html b/mapbox-gl/closure-library/closure/goog/demos/container.html new file mode 100644 index 0000000000..2b39733307 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/container.html @@ -0,0 +1,670 @@ + + + + + goog.ui.Container + + + + + + + + +

goog.ui.Container

+

goog.ui.Container is a base class for menus and toolbars.

+
+ These containers were created programmatically: + + + + + + + + + + + +
+ Vertical container example: + + Horizontal container example: +
+ +   + +
+ +   + +
+ +
+ +   + +
+ +   + +
+ +
+ Try enabling and disabling containers with & without + state transition events, and compare performance! +
+
+
+
+ Non-focusable container with focusable controls: + In this case, the container itself isn't focusable, but each control is:
+
+
+
+
+
+ Another horizontal container: + +
+
+
+
+
+ A decorated container: + +
+
+ Select month +
+
January
+
February
+
March
+
April
+
May
+
June
+
July
+
August
+
September
+
October
+
November
+
December
+
+
+
+ Year +
+
2001
+
2002
+
2003
+
2004
+
2005
+
2006
+
2007
+
2008
+
2009
+
2010
+
+
+
+ Toggle Button +
+
+
Fancy Toggle Button
+
+
+ Another Button +
+
+
+
+
+
+ The same container, right-to-left: + + +
+
+ Select month +
+
January
+
February
+
March
+
April
+
May
+
June
+
July
+
August
+
September
+
October
+
November
+
December
+
+
+
+ Year +
+
2001
+
2002
+
2003
+
2004
+
2005
+
2006
+
2007
+
2008
+
2009
+
2010
+
+
+
+ Toggle Button +
+
+
Fancy Toggle Button
+
+
+ Another Button +
+
+
+
+
+
+ A scrolling container: + +

+ Put focus in the text box and use the arrow keys: + +

+

+ Or quick jump to item: + + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15 + +

+
+
menuitem 0
+
menuitem 1
+
menuitem 2
+
menuitem 3
+
tog 4
+
tog 5
+
tog 6
+
toggley 7
+
toggley 8
+
toggley 9
+
toggley 10
+
toggley 11
+
toggley 12
+
toggley 13
+
menuitem 14
+
menuitem 15
+
+
+
+
+ +
+ Event Log +
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/control.html b/mapbox-gl/closure-library/closure/goog/demos/control.html new file mode 100644 index 0000000000..705ee3b87c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/control.html @@ -0,0 +1,477 @@ + + + + + goog.ui.Control Demo + + + + + + +

goog.ui.Control

+ + + + + + + +
+ +
+ This control was created programmatically:  +
+
+ This control dispatches ENTER, LEAVE, and ACTION events on + mouseover, mouseout, and mouseup, respectively. It supports + keyboard focus. +
+
+
+ This was created by decorating a SPAN:  + + Decorated Example + +
+ + You need to enable this component first. + +
+
+ This control is configured to dispatch state transition events in + addition to ENTER, LEAVE, and ACTION. It also supports keyboard + focus. Watch the event log as you interact with this component. +
+
+ +
+ +
+
+

Custom Renderers

+
+ + This control was created using a custom renderer:  + +
+
+
+
+ + This was created by decorating a DIV via a custom renderer:  + +
+ +   + + + Insert Picture + +
+
+
+

Extra CSS Styling

+
+ + These controls have extra CSS classes applied:  + +
+
+
+ Use the addClassName API to add additional CSS class names + to controls, before or after they're rendered or decorated. +
+
+

Right-to-Left Rendering

+
+ + These controls are rendered right-to-left:  + +

These right-to-left controls were progammatically created:

+
+

These right-to-left controls were decorated:

+
+
+ Hello, world +
+ Sample control +
+
+

+ On pre-FF3 Gecko, margin-left and margin-right are + ignored, so controls render right next to each other. + A workaround is to include some &nbsp;s in between + controls. +

+
+
+ +
+ Event Log +
+
+
+ + The control with the + orange outline + has keyboard focus. + +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/css/demo.css b/mapbox-gl/closure-library/closure/goog/demos/css/demo.css new file mode 100644 index 0000000000..6eb82e85b3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/css/demo.css @@ -0,0 +1,75 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: attila@google.com (Attila Bodis) */ + + +@import url(../../css/common.css); + + +body { + background-color: #ffe; + font: normal 10pt Arial, sans-serif; +} + + +/* Misc. styles used for logging and debugging. */ +fieldset { + padding: 4px 8px; + margin-bottom: 1em; +} + +fieldset legend { + font-weight: bold; + color: #036; +} + +label, input { + vertical-align: middle; +} + +.hint { + font-size: 90%; + color: #369; +} + +.goog-debug-panel { + border: 1px solid #369; +} + +.goog-debug-panel .logdiv { + position: relative; + width: 100%; + height: 8em; + overflow: scroll; + overflow-x: hidden; + overflow-y: scroll; +} + +.goog-debug-panel .logdiv .logmsg { + font: normal 10px "Lucida Sans Typewriter", "Courier New", Courier, fixed; +} + +.perf { + margin: 0; + border: 0; + padding: 4px; + font: italic 95% Arial, sans-serif; + color: #999; +} + +#perf { + position: absolute; + right: 0; + bottom: 0; + text-align: right; + margin: 0; + border: 0; + padding: 4px; + font: italic 95% Arial, sans-serif; + color: #999; +} diff --git a/mapbox-gl/closure-library/closure/goog/demos/css/emojipicker.css b/mapbox-gl/closure-library/closure/goog/demos/css/emojipicker.css new file mode 100644 index 0000000000..826d5bd54f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/css/emojipicker.css @@ -0,0 +1,36 @@ +/* + * Copyright 2007 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* Author: dalewis@google.com (Darren Lewis) */ + +/* Styles used in the emojipicker demo */ +.goog-ui-popupemojipicker { + position: absolute; + -moz-outline: 0; + outline: 0; + visibility: hidden; +} + +.goog-palette-cell { + padding: 2px; + background: white; +} + +.goog-palette-cell div { + vertical-align: middle; + text-align: center; + margin: auto; +} + +.goog-palette-cell-wrapper { + width: 25px; + height: 25px; +} + +.goog-palette-cell-hover { + background: lightblue; +} diff --git a/mapbox-gl/closure-library/closure/goog/demos/css/emojisprite.css b/mapbox-gl/closure-library/closure/goog/demos/css/emojisprite.css new file mode 100644 index 0000000000..fe1a2cc184 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/css/emojisprite.css @@ -0,0 +1,92 @@ +/* + * Copyright 2010 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + +/* This file is autogenerated. To regenerate, do: +cd google3/javascript/closure/demos/emoji +/home/build/static/projects/sitespeed optisprite *.gif + +This will generate an optimal sprite in /usr/local/google/tmp/bestsprite.tar.gz. + +Rename the optimal sprite to "sprite.png" (from sprite_XX.png), rename the css +to "sprite.css" (from sprite_XX.css), then change all the URLs in sprite.css to +point to sprite.png in google3/javascript/closure/demos/emoji, and cp the +sprite.png into that directory. +*/ + +.SPRITE_200{background:no-repeat url(../emoji/sprite.png) -18px 0;width:18px;height:18px} +.SPRITE_201{background:no-repeat url(../emoji/sprite.png) 0 -234px;width:18px;height:18px} +.SPRITE_202{background:no-repeat url(../emoji/sprite.png) -18px -338px;width:18px;height:18px} +.SPRITE_203{background:no-repeat url(../emoji/sprite.png) -36px 0;width:18px;height:18px} +.SPRITE_204{background:no-repeat url(../emoji/sprite.png) 0 -305px;width:18px;height:19px} +.SPRITE_205{background:no-repeat url(../emoji/sprite.png) -36px -126px;width:18px;height:18px} +.SPRITE_206{background:no-repeat url(../emoji/sprite.png) -36px -36px;width:18px;height:18px} +.SPRITE_2BC{background:no-repeat url(../emoji/sprite.png) -18px -144px;width:18px;height:18px} +.SPRITE_2BD{background:no-repeat url(../emoji/sprite.png) 0 -18px;width:18px;height:18px} +.SPRITE_2BE{background:no-repeat url(../emoji/sprite.png) -36px -54px;width:18px;height:18px} +.SPRITE_2BF{background:no-repeat url(../emoji/sprite.png) 0 -126px;width:18px;height:18px} +.SPRITE_2C0{background:no-repeat url(../emoji/sprite.png) -18px -305px;width:18px;height:18px} +.SPRITE_2C1{background:no-repeat url(../emoji/sprite.png) 0 -287px;width:18px;height:18px} +.SPRITE_2C2{background:no-repeat url(../emoji/sprite.png) -18px -126px;width:18px;height:18px} +.SPRITE_2C3{background:no-repeat url(../emoji/sprite.png) -36px -234px;width:18px;height:20px} +.SPRITE_2C4{background:no-repeat url(../emoji/sprite.png) -36px -72px;width:18px;height:18px} +.SPRITE_2C5{background:no-repeat url(../emoji/sprite.png) -54px -54px;width:18px;height:18px} +.SPRITE_2C6{background:no-repeat url(../emoji/sprite.png) 0 -72px;width:18px;height:18px} +.SPRITE_2C7{background:no-repeat url(../emoji/sprite.png) -18px -180px;width:18px;height:18px} +.SPRITE_2C8{background:no-repeat url(../emoji/sprite.png) -36px -198px;width:18px;height:18px} +.SPRITE_2C9{background:no-repeat url(../emoji/sprite.png) -36px -287px;width:18px;height:18px} +.SPRITE_2CB{background:no-repeat url(../emoji/sprite.png) -54px -252px;width:18px;height:18px} +.SPRITE_2CC{background:no-repeat url(../emoji/sprite.png) -54px -288px;width:18px;height:16px} +.SPRITE_2CD{background:no-repeat url(../emoji/sprite.png) -36px -162px;width:18px;height:18px} +.SPRITE_2CE{background:no-repeat url(../emoji/sprite.png) 0 -269px;width:18px;height:18px} +.SPRITE_2CF{background:no-repeat url(../emoji/sprite.png) -36px -108px;width:18px;height:18px} +.SPRITE_2D0{background:no-repeat url(../emoji/sprite.png) -36px -338px;width:18px;height:18px} +.SPRITE_2D1{background:no-repeat url(../emoji/sprite.png) 0 -338px;width:18px;height:18px} +.SPRITE_2D2{background:no-repeat url(../emoji/sprite.png) -54px -36px;width:18px;height:16px} +.SPRITE_2D3{background:no-repeat url(../emoji/sprite.png) -36px -305px;width:18px;height:18px} +.SPRITE_2D4{background:no-repeat url(../emoji/sprite.png) -36px -18px;width:18px;height:18px} +.SPRITE_2D5{background:no-repeat url(../emoji/sprite.png) -18px -108px;width:18px;height:18px} +.SPRITE_2D6{background:no-repeat url(../emoji/sprite.png) -36px -144px;width:18px;height:18px} +.SPRITE_2D7{background:no-repeat url(../emoji/sprite.png) 0 -36px;width:18px;height:18px} +.SPRITE_2D8{background:no-repeat url(../emoji/sprite.png) -54px -126px;width:18px;height:18px} +.SPRITE_2D9{background:no-repeat url(../emoji/sprite.png) -18px -287px;width:18px;height:18px} +.SPRITE_2DA{background:no-repeat url(../emoji/sprite.png) -54px -216px;width:18px;height:18px} +.SPRITE_2DB{background:no-repeat url(../emoji/sprite.png) -36px -180px;width:18px;height:18px} +.SPRITE_2DC{background:no-repeat url(../emoji/sprite.png) 0 -54px;width:18px;height:18px} +.SPRITE_2DD{background:no-repeat url(../emoji/sprite.png) -18px -72px;width:18px;height:18px} +.SPRITE_2DE{background:no-repeat url(../emoji/sprite.png) -36px -90px;width:18px;height:18px} +.SPRITE_2DF{background:no-repeat url(../emoji/sprite.png) -54px -108px;width:18px;height:18px} +.SPRITE_2E0{background:no-repeat url(../emoji/sprite.png) -18px -198px;width:18px;height:18px} +.SPRITE_2E1{background:no-repeat url(../emoji/sprite.png) 0 -180px;width:18px;height:18px} +.SPRITE_2E2{background:no-repeat url(../emoji/sprite.png) -54px -338px;width:18px;height:18px} +.SPRITE_2E4{background:no-repeat url(../emoji/sprite.png) -54px -198px;width:18px;height:18px} +.SPRITE_2E5{background:no-repeat url(../emoji/sprite.png) 0 -162px;width:18px;height:18px} +.SPRITE_2E6{background:no-repeat url(../emoji/sprite.png) -54px -270px;width:18px;height:18px} +.SPRITE_2E7{background:no-repeat url(../emoji/sprite.png) 0 -108px;width:18px;height:18px} +.SPRITE_2E8{background:no-repeat url(../emoji/sprite.png) 0 -198px;width:18px;height:18px} +.SPRITE_2E9{background:no-repeat url(../emoji/sprite.png) -54px 0;width:18px;height:18px} +.SPRITE_2EA{background:no-repeat url(../emoji/sprite.png) -54px -144px;width:18px;height:18px} +.SPRITE_2EB{background:no-repeat url(../emoji/sprite.png) -18px -36px;width:18px;height:18px} +.SPRITE_2EC{background:no-repeat url(../emoji/sprite.png) -18px -18px;width:18px;height:18px} +.SPRITE_2ED{background:no-repeat url(../emoji/sprite.png) -36px -269px;width:18px;height:18px} +.SPRITE_2EE{background:no-repeat url(../emoji/sprite.png) -18px -90px;width:18px;height:18px} +.SPRITE_2F0{background:no-repeat url(../emoji/sprite.png) 0 0;width:18px;height:18px} +.SPRITE_2F2{background:no-repeat url(../emoji/sprite.png) -54px -234px;width:18px;height:18px} +.SPRITE_2F3{background:no-repeat url(../emoji/sprite.png) 0 -144px;width:18px;height:18px} +.SPRITE_2F4{background:no-repeat url(../emoji/sprite.png) 0 -252px;width:18px;height:17px} +.SPRITE_2F5{background:no-repeat url(../emoji/sprite.png) -54px -321px;width:18px;height:14px} +.SPRITE_2F6{background:no-repeat url(../emoji/sprite.png) -36px -254px;width:18px;height:15px} +.SPRITE_2F7{background:no-repeat url(../emoji/sprite.png) -18px -54px;width:18px;height:18px} +.SPRITE_2F8{background:no-repeat url(../emoji/sprite.png) 0 -216px;width:18px;height:18px} +.SPRITE_2F9{background:no-repeat url(../emoji/sprite.png) -18px -234px;width:18px;height:18px} +.SPRITE_2FA{background:no-repeat url(../emoji/sprite.png) -18px -216px;width:18px;height:18px} +.SPRITE_2FB{background:no-repeat url(../emoji/sprite.png) -36px -216px;width:18px;height:18px} +.SPRITE_2FC{background:no-repeat url(../emoji/sprite.png) -54px -162px;width:18px;height:18px} +.SPRITE_2FD{background:no-repeat url(../emoji/sprite.png) 0 -90px;width:18px;height:18px} +.SPRITE_2FE{background:no-repeat url(../emoji/sprite.png) -54px -305px;width:18px;height:16px} +.SPRITE_2FF{background:no-repeat url(../emoji/sprite.png) -54px -72px;width:18px;height:16px} +.SPRITE_none{background:no-repeat url(../emoji/sprite.png) -54px -180px;width:18px;height:18px} +.SPRITE_unknown{background:no-repeat url(../emoji/sprite.png) -36px -323px;width:14px;height:15px} diff --git a/mapbox-gl/closure-library/closure/goog/demos/css3button.html b/mapbox-gl/closure-library/closure/goog/demos/css3button.html new file mode 100644 index 0000000000..235d303dd8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/css3button.html @@ -0,0 +1,166 @@ + + + + + + goog.ui.Css3ButtonRenderer Demo + + + + + + + + +

Demo of goog.ui.Css3ButtonRenderer

+
+ + These buttons were rendered using + goog.ui.Css3ButtonRenderer: + +
+ These buttons were created programmatically:
+
+
+ These buttons were created by decorating some DIVs, and they dispatch + state transition events (watch the event log):
+
+
+ Decorated Button, yay! +
Decorated Disabled +
Another Button
+ Archive +
+ Delete +
+ Report Spam +
+
+
+ Use these ToggleButtons to hide/show and enable/disable + the middle button:
+
Enable
+
Show
+

+ Combined toggle buttons
+
+ Bold +
+ Italics +
+ Underlined +
+
+
+
+ +
+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/css3menubutton.html b/mapbox-gl/closure-library/closure/goog/demos/css3menubutton.html new file mode 100644 index 0000000000..915b987090 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/css3menubutton.html @@ -0,0 +1,285 @@ + + + + + goog.ui.Css3MenuButtonRenderer Demo + + + + + + + + + + + + +

goog.ui.Css3MenuButtonRenderer

+ + + + + + + +
+
+ + These MenuButtons were created programmatically: +   + + + + + + + + +
+ + + Enable first button: + +   + Show second button: + +   +
+ +
+
+
+ + This MenuButton decorates an element:  + + + + + + + + +
+
+ +
+ Format + +
+
Bold
+
Italic
+
Underline
+
+
+ Strikethrough +
+
+
Font...
+
Color...
+
+
+
+ Enable button: + +   + Show button: + +   +
+ +
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/cssspriteanimation.html b/mapbox-gl/closure-library/closure/goog/demos/cssspriteanimation.html new file mode 100644 index 0000000000..5d46ab4924 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/cssspriteanimation.html @@ -0,0 +1,80 @@ + + + + +CssSpriteAnimation demo + + + + + + +

The following just runs and runs...

+
+ +

The animation is just an ordinary animation so you can pause it etc. +

+ +

+ + +

+ +

The animation can be played once by stopping it after it finishes for the +first time. + +

+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/datepicker.html b/mapbox-gl/closure-library/closure/goog/demos/datepicker.html new file mode 100644 index 0000000000..1e2b8db595 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/datepicker.html @@ -0,0 +1,311 @@ + + + + + + goog.ui.DatePicker + + + + + + +

goog.ui.DatePicker

+ + + +
+

Default: ISO 8601

+
+
 
+ +

+

Custom

+
+
+
+
+
+
+
+
+
+
+
+
+
 
+ +
+ +

English (US)

+
+
 
+ +

+ +

German

+
+
 
+ +

+ +

Malayalam

+
+
 
+ +

+ +
+ +

Arabic (Yemen)

+
+
 
+ +

+ +

Thai

+
+
 
+ +

+ +

Japanese

+
+
 
+ +

+ +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/debug.html b/mapbox-gl/closure-library/closure/goog/demos/debug.html new file mode 100644 index 0000000000..a291e77ae9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/debug.html @@ -0,0 +1,118 @@ + + + + +Debug + + + + +Look in the log window for debugging examples. + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/depsgraph.html b/mapbox-gl/closure-library/closure/goog/demos/depsgraph.html new file mode 100644 index 0000000000..a1f596204c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/depsgraph.html @@ -0,0 +1,220 @@ + + + + +Deps Tree + + + + + +

Closure Dependency Graph

+ + + +
selected item
+
...is in same file as the selected item
+
...is a dependency of the selected item
+
the selected item is a dependency of...
+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dialog.html b/mapbox-gl/closure-library/closure/goog/demos/dialog.html new file mode 100644 index 0000000000..e1118ef21e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dialog.html @@ -0,0 +1,152 @@ + + + + + goog.ui.Dialog + + + + + + + + +

goog.ui.Dialog

+
+ + (use "Space" to open dialog with no Iframe, "Enter" to open with Iframe + mask +
+
+ +
+ +
+ + + +
+ A sample web page +

+ A World Beyond AJAX: Accessing Google's APIs from Flash and + Non-JavaScript Environments +

+ Vadim Spivak (Google) + +

+ AJAX isn't the only way to access Google APIs. Learn how to use Google's + services from Flash and other non-JavaScript programming environments. + We'll show you how easy it is to augment your site with dynamic search + and feed data from non-JavaScript environments. +

+ +

+ Participants should be familiar with general web application + development. +

+ +

Select Element: + +

+ +

+ + + + + + +

+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker.html b/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker.html new file mode 100644 index 0000000000..9146bee82b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker.html @@ -0,0 +1,104 @@ + + + + + goog.ui.DimensionPicker + + + + + + + +

goog.ui.DimensionPicker

+ + + + + + + +
+
+ Demo of the goog.ui.DimensionPicker + component: + +
+ +
+ +
+
+
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker_rtl.html b/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker_rtl.html new file mode 100644 index 0000000000..173d4771ba --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dimensionpicker_rtl.html @@ -0,0 +1,119 @@ + + + + + goog.ui.DimensionPicker + + + + + + + + + +

goog.ui.DimensionPicker

+ + + + + + + +
+ +
+ Event Log +
+
+
+
+ Demo of the goog.ui.DimensionPicker + component: + +
+

+ +
+ +
+
+
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dom_selection.html b/mapbox-gl/closure-library/closure/goog/demos/dom_selection.html new file mode 100644 index 0000000000..51cdd7400d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dom_selection.html @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/drag.html b/mapbox-gl/closure-library/closure/goog/demos/drag.html new file mode 100644 index 0000000000..8d4b5499ec --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/drag.html @@ -0,0 +1,191 @@ + + + + goog.fx.Dragger + + + + + + + +

goog.fx.Dragger

+

Demonstrations of the drag utiities.

+ +
+ +
Drag Me...
+
Drag Me...
+
Drag Me...
+ +
+
0
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dragdrop.html b/mapbox-gl/closure-library/closure/goog/demos/dragdrop.html new file mode 100644 index 0000000000..725c8f1da4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dragdrop.html @@ -0,0 +1,263 @@ + + + + + goog.fx.DragDrop + + + + + + + +

goog.fx.DragDrop

+ +List 1 (combined source/target, can be dropped on list 1, list 2, button 1 or +button 2). +
    +
  • Item 1.1
  • +
  • Item 1.2
  • +
  • Item 1.3
  • +
  • Item 1.4
  • +
  • Item 1.5
  • +
  • Item 1.6
  • +
  • Item 1.7
  • +
  • Item 1.8
  • +
  • Item 1.9
  • +
  • Item 1.10
  • +
  • Item 1.11
  • +
  • Item 1.12
  • +
  • Item 1.13
  • +
  • Item 1.14
  • +
  • Item 1.15
  • +
+ +List 2 (source only, can be dropped on list 1 or button 2) +
    +
  • Item 2.1
  • +
  • Item 2.2
  • +
  • Item 2.3
  • +
  • Item 2.4
  • +
  • Item 2.5
  • +
  • Item 2.6
  • +
  • Item 2.7
  • +
  • Item 2.8
  • +
  • Item 2.9
  • +
  • Item 2.10
  • +
  • Item 2.11
  • +
  • Item 2.12
  • +
  • Item 2.13
  • +
  • Item 2.14
  • +
  • Item 2.15
  • +
+ +
+ Button 1 (combined source/target, can be dropped on list 1) +
+ +
+ Button 2 (target only) +
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector.html b/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector.html new file mode 100644 index 0000000000..6787c0bc33 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector.html @@ -0,0 +1,46 @@ + + + + + goog.ui.DragDropDetector + + + + + + + +

goog.ui.DragDropDetector

+

Try dropping images from other web pages on this page.

+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector_target.html b/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector_target.html new file mode 100644 index 0000000000..fe09b48ebe --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dragdropdetector_target.html @@ -0,0 +1,17 @@ + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dragger.html b/mapbox-gl/closure-library/closure/goog/demos/dragger.html new file mode 100644 index 0000000000..90eb4d1819 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dragger.html @@ -0,0 +1,83 @@ + + + + +goog.fx.Dragger + + + + + + + +

goog.fx.Dragger

+ +

This demo shows how to use a dragger to capture mouse move events. It tests +that you can drag things outside the window and that alerts ends the dragging. + +

Drag me

+
Status
+ +
Drag over me to generate an +alert
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/draglistgroup.html b/mapbox-gl/closure-library/closure/goog/demos/draglistgroup.html new file mode 100644 index 0000000000..7ac5733782 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/draglistgroup.html @@ -0,0 +1,273 @@ + + + + + goog.fx.DragListGroup + + + + + + + + +

goog.fx.DragListGroup

+

You can drag any squares into any of the first 4 lists.

+
+ +

Horizontal list 1 (grows right):

+
+
1
+
2
+
3
+
4
+
+
+ + + + + + + + + +
+

Vertical list 1:

+
+
1
+
2
+
3
+
4
+
+
+

Vertical list 2 (style changes on drag hover):

+
+
1
+
2
+
3
+
4
+
+
+
+

Horizontal list 3 (grows left):

+

Bug: drop position is off by one.

+
+
1
+
2
+
3
+
4
+
+
+ +

Horizontal list 5 (grows right, has multiple rows, hysteresis is enabled):

+

Bug: can't drop into the last row.

+
+
11
+
22
+
33
+
44
+
55
+
66
+
77
+
88
+
99
+
+
+ +

The items in this list can be moved around with shift-dragging:

+
+
1
+
2
+
3
+
4
+
+
+ +

The items have different width:

+

+ Bug: the drop positions are off. + For example try moving box 1 a bit to the left. +

+
+
1
+
2
+
3
+
4
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/dragscrollsupport.html b/mapbox-gl/closure-library/closure/goog/demos/dragscrollsupport.html new file mode 100644 index 0000000000..8d54a4da22 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/dragscrollsupport.html @@ -0,0 +1,133 @@ + + + + + goog.fx.DragScrollSupport + + + + + + + +

goog.fx.DragScrollSupport

+ +List 1 in a scrollable area. +
+
    +
  • Item 1.1 ----------
  • +
  • Item 1.2 ----------
  • +
  • Item 1.3 ----------
  • +
  • Item 1.4 ----------
  • +
  • Item 1.5 ----------
  • +
  • Item 1.6 ----------
  • +
  • Item 1.7 ----------
  • +
  • Item 1.8 ----------
  • +
  • Item 1.9 ----------
  • +
  • Item 1.10 ----------
  • +
  • Item 1.11 ----------
  • +
  • Item 1.12 ----------
  • +
  • Item 1.13 ----------
  • +
  • Item 1.14 ----------
  • +
  • Item 1.15 ----------
  • +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/drilldownrow.html b/mapbox-gl/closure-library/closure/goog/demos/drilldownrow.html new file mode 100644 index 0000000000..cc8d87a1c9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/drilldownrow.html @@ -0,0 +1,78 @@ + + + + +Demo of DrilldownRow + + + + + + + + + + + + + + + + +
Column HeadSecond Head
First rowSecond column
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/deps.js b/mapbox-gl/closure-library/closure/goog/demos/editor/deps.js new file mode 100644 index 0000000000..70b0973515 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/deps.js @@ -0,0 +1,21 @@ +// Copyright 2009 The Closure Library Authors. +// All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file has been auto-generated by GenJsDeps, please do not edit. + +goog.addDependency('demos/editor/equationeditor.js', ['goog.demos.editor.EquationEditor'], ['goog.ui.equation.EquationEditorDialog']); +goog.addDependency('demos/editor/helloworld.js', ['goog.demos.editor.HelloWorld'], ['goog.dom', 'goog.dom.TagName', 'goog.editor.Plugin']); +goog.addDependency('demos/editor/helloworlddialog.js', ['goog.demos.editor.HelloWorldDialog', 'goog.demos.editor.HelloWorldDialog.OkEvent'], ['goog.dom.TagName', 'goog.events.Event', 'goog.string', 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.AbstractDialog.Builder', 'goog.ui.editor.AbstractDialog.EventType']); +goog.addDependency('demos/editor/helloworlddialogplugin.js', ['goog.demos.editor.HelloWorldDialogPlugin', 'goog.demos.editor.HelloWorldDialogPlugin.Command'], ['goog.demos.editor.HelloWorldDialog', 'goog.dom.TagName', 'goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.range', 'goog.functions', 'goog.ui.editor.AbstractDialog.EventType']); diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/editor.html b/mapbox-gl/closure-library/closure/goog/demos/editor/editor.html new file mode 100644 index 0000000000..24aba99e12 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/editor.html @@ -0,0 +1,139 @@ + + + + + + goog.editor Demo + + + + + + + + + + + + + + + + + + + + + + + + + + + +

goog.editor Demo

+

This is a demonstration of a editable field, with installed plugins, +hooked up to a toolbar.

+
+
+
+
+

Current field contents + (updates as contents of the editable field above change):
+
+ + (Use to set contents of the editable field to the contents of this textarea) +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/field_basic.html b/mapbox-gl/closure-library/closure/goog/demos/editor/field_basic.html new file mode 100644 index 0000000000..69a46dc343 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/field_basic.html @@ -0,0 +1,74 @@ + + + + + + goog.editor.Field + + + + + + +

goog.editor.Field

+

This is a very basic demonstration of how to make a region editable.

+ + +

+
I am a regular div. Click "Make Editable" above to transform me into an editable region.
+
+

Current field contents + (updates as contents of the editable field above change):
+
+ + (Use to set contents of the editable field to the contents of this textarea) +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.html b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.html new file mode 100644 index 0000000000..6ce5d99b16 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.html @@ -0,0 +1,91 @@ + + + + + + + goog.editor Hello World plugins Demo + + + + + + + + +

goog.editor Hello World plugins Demo

+

This is a demonstration of an editable field with the two sample plugins + installed: goog.editor.plugins.HelloWorld and + goog.editor.plugins.HelloWorldDialogPlugin.

+
+ +
+
    +
  • Click Hello World to insert "Hello World!".
  • +
  • Click Hello World Dialog to open a dialog where you can customize + your hello world message to be inserted.
  • +
The hello world message will be inserted at the cursor, or will replace + the selected text.
+
+

Current field contents + (updates as contents of the editable field above change):
+
+ + (Use to set contents of the editable field to the contents of this textarea) +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.js b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.js new file mode 100644 index 0000000000..4b2f0c7a5e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld.js @@ -0,0 +1,82 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A simple plugin that inserts 'Hello World!' on command. This + * plugin is intended to be an example of a very simple plugin for plugin + * developers. + * + * @author gak@google.com (Gregory Kick) + * @see helloworld.html + */ + +goog.provide('goog.demos.editor.HelloWorld'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Plugin'); + + + +/** + * Plugin to insert 'Hello World!' into an editable field. + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.demos.editor.HelloWorld = function() { + goog.editor.Plugin.call(this); +}; +goog.inherits(goog.demos.editor.HelloWorld, goog.editor.Plugin); + + +/** @override */ +goog.demos.editor.HelloWorld.prototype.getTrogClassId = function() { + return 'HelloWorld'; +}; + + +/** + * Commands implemented by this plugin. + * @enum {string} + */ +goog.demos.editor.HelloWorld.COMMAND = { + HELLO_WORLD: '+helloWorld' +}; + + +/** @override */ +goog.demos.editor.HelloWorld.prototype.isSupportedCommand = function( + command) { + return command == goog.demos.editor.HelloWorld.COMMAND.HELLO_WORLD; +}; + + +/** + * Executes a command. Does not fire any BEFORECHANGE, CHANGE, or + * SELECTIONCHANGE events (these are handled by the super class implementation + * of {@code execCommand}. + * @param {string} command Command to execute. + * @override + * @protected + */ +goog.demos.editor.HelloWorld.prototype.execCommandInternal = function( + command) { + var domHelper = this.getFieldObject().getEditableDomHelper(); + var range = this.getFieldObject().getRange(); + range.removeContents(); + var newNode = + domHelper.createDom(goog.dom.TagName.SPAN, null, 'Hello World!'); + range.insertNode(newNode, false); +}; diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld_test.html b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld_test.html new file mode 100644 index 0000000000..f6f83542ff --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworld_test.html @@ -0,0 +1,75 @@ + + + + + + + + +Closure Unit Tests - goog.demos.editor.HelloWorld + + + + + + + +
 
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog.js b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog.js new file mode 100644 index 0000000000..5ab271e161 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog.js @@ -0,0 +1,173 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An example of how to write a dialog to be opened by a plugin. + * + */ + +goog.provide('goog.demos.editor.HelloWorldDialog'); +goog.provide('goog.demos.editor.HelloWorldDialog.OkEvent'); + +goog.require('goog.dom.TagName'); +goog.require('goog.events.Event'); +goog.require('goog.string'); +goog.require('goog.ui.editor.AbstractDialog'); + + +// *** Public interface ***************************************************** // + + + +/** + * Creates a dialog to let the user enter a customized hello world message. + * @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the + * dialog's dom structure. + * @constructor + * @extends {goog.ui.editor.AbstractDialog} + * @final + */ +goog.demos.editor.HelloWorldDialog = function(domHelper) { + goog.ui.editor.AbstractDialog.call(this, domHelper); +}; +goog.inherits(goog.demos.editor.HelloWorldDialog, + goog.ui.editor.AbstractDialog); + + +// *** Event **************************************************************** // + + + +/** + * OK event object for the hello world dialog. + * @param {string} message Customized hello world message chosen by the user. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.demos.editor.HelloWorldDialog.OkEvent = function(message) { + this.message = message; +}; +goog.inherits(goog.demos.editor.HelloWorldDialog.OkEvent, goog.events.Event); + + +/** + * Event type. + * @type {goog.ui.editor.AbstractDialog.EventType} + * @override + */ +goog.demos.editor.HelloWorldDialog.OkEvent.prototype.type = + goog.ui.editor.AbstractDialog.EventType.OK; + + +/** + * Customized hello world message chosen by the user. + * @type {string} + */ +goog.demos.editor.HelloWorldDialog.OkEvent.prototype.message; + + +// *** Protected interface ************************************************** // + + +/** @override */ +goog.demos.editor.HelloWorldDialog.prototype.createDialogControl = function() { + var builder = new goog.ui.editor.AbstractDialog.Builder(this); + /** @desc Title of the hello world dialog. */ + var MSG_HELLO_WORLD_DIALOG_TITLE = goog.getMsg('Add a Hello World message'); + builder.setTitle(MSG_HELLO_WORLD_DIALOG_TITLE). + setContent(this.createContent_()); + return builder.build(); +}; + + +/** + * Creates and returns the event object to be used when dispatching the OK + * event to listeners, or returns null to prevent the dialog from closing. + * @param {goog.events.Event} e The event object dispatched by the wrapped + * dialog. + * @return {goog.demos.editor.HelloWorldDialog.OkEvent} The event object to be + * used when dispatching the OK event to listeners. + * @protected + * @override + */ +goog.demos.editor.HelloWorldDialog.prototype.createOkEvent = function(e) { + var message = this.getMessage_(); + if (message && + goog.demos.editor.HelloWorldDialog.isValidHelloWorld_(message)) { + return new goog.demos.editor.HelloWorldDialog.OkEvent(message); + } else { + /** @desc Error message telling the user why their message was rejected. */ + var MSG_HELLO_WORLD_DIALOG_ERROR = + goog.getMsg('Your message must contain the words "hello" and "world".'); + this.dom.getWindow().alert(MSG_HELLO_WORLD_DIALOG_ERROR); + return null; // Prevents the dialog from closing. + } +}; + + +// *** Private implementation *********************************************** // + + +/** + * Input element where the user will type their hello world message. + * @type {Element} + * @private + */ +goog.demos.editor.HelloWorldDialog.prototype.input_; + + +/** + * Creates the DOM structure that makes up the dialog's content area. + * @return {Element} The DOM structure that makes up the dialog's content area. + * @private + */ +goog.demos.editor.HelloWorldDialog.prototype.createContent_ = function() { + /** @desc Sample hello world message to prepopulate the dialog with. */ + var MSG_HELLO_WORLD_DIALOG_SAMPLE = goog.getMsg('Hello, world!'); + this.input_ = this.dom.createDom(goog.dom.TagName.INPUT, + {size: 25, value: MSG_HELLO_WORLD_DIALOG_SAMPLE}); + /** @desc Prompt telling the user to enter a hello world message. */ + var MSG_HELLO_WORLD_DIALOG_PROMPT = + goog.getMsg('Enter your Hello World message'); + return this.dom.createDom(goog.dom.TagName.DIV, + null, + [MSG_HELLO_WORLD_DIALOG_PROMPT, this.input_]); +}; + + +/** + * Returns the hello world message currently typed into the dialog's input. + * @return {?string} The hello world message currently typed into the dialog's + * input, or null if called before the input is created. + * @private + */ +goog.demos.editor.HelloWorldDialog.prototype.getMessage_ = function() { + return this.input_ && this.input_.value; +}; + + +/** + * Returns whether or not the given message contains the strings "hello" and + * "world". Case-insensitive and order doesn't matter. + * @param {string} message The message to be checked. + * @return {boolean} Whether or not the given message contains the strings + * "hello" and "world". + * @private + */ +goog.demos.editor.HelloWorldDialog.isValidHelloWorld_ = function(message) { + message = message.toLowerCase(); + return goog.string.contains(message, 'hello') && + goog.string.contains(message, 'world'); +}; diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog_test.html b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog_test.html new file mode 100644 index 0000000000..8f4d0dfb7d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialog_test.html @@ -0,0 +1,100 @@ + + + + + + +Closure Unit Tests - goog.demos.editor.HelloWorldDialog + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin.js b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin.js new file mode 100644 index 0000000000..56a65b67ad --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin.js @@ -0,0 +1,117 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An example of how to write a dialog plugin. + * + */ + +goog.provide('goog.demos.editor.HelloWorldDialogPlugin'); +goog.provide('goog.demos.editor.HelloWorldDialogPlugin.Command'); + +goog.require('goog.demos.editor.HelloWorldDialog'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.plugins.AbstractDialogPlugin'); +goog.require('goog.editor.range'); +goog.require('goog.functions'); +goog.require('goog.ui.editor.AbstractDialog'); + + +// *** Public interface ***************************************************** // + + + +/** + * A plugin that opens the hello world dialog. + * @constructor + * @extends {goog.editor.plugins.AbstractDialogPlugin} + * @final + */ +goog.demos.editor.HelloWorldDialogPlugin = function() { + goog.editor.plugins.AbstractDialogPlugin.call(this, + goog.demos.editor.HelloWorldDialogPlugin.Command.HELLO_WORLD_DIALOG); +}; +goog.inherits(goog.demos.editor.HelloWorldDialogPlugin, + goog.editor.plugins.AbstractDialogPlugin); + + +/** + * Commands implemented by this plugin. + * @enum {string} + */ +goog.demos.editor.HelloWorldDialogPlugin.Command = { + HELLO_WORLD_DIALOG: 'helloWorldDialog' +}; + + +/** @override */ +goog.demos.editor.HelloWorldDialogPlugin.prototype.getTrogClassId = + goog.functions.constant('HelloWorldDialog'); + + +// *** Protected interface ************************************************** // + + +/** + * Creates a new instance of the dialog and registers for the relevant events. + * @param {goog.dom.DomHelper} dialogDomHelper The dom helper to be used to + * create the dialog. + * @return {goog.demos.editor.HelloWorldDialog} The dialog. + * @override + * @protected + */ +goog.demos.editor.HelloWorldDialogPlugin.prototype.createDialog = function( + dialogDomHelper) { + var dialog = new goog.demos.editor.HelloWorldDialog(dialogDomHelper); + dialog.addEventListener(goog.ui.editor.AbstractDialog.EventType.OK, + this.handleOk_, + false, + this); + return dialog; +}; + + +// *** Private implementation *********************************************** // + + +/** + * Handles the OK event from the dialog by inserting the hello world message + * into the field. + * @param {goog.demos.editor.HelloWorldDialog.OkEvent} e OK event object. + * @private + */ +goog.demos.editor.HelloWorldDialogPlugin.prototype.handleOk_ = function(e) { + // First restore the selection so we can manipulate the field's content + // according to what was selected. + this.restoreOriginalSelection(); + + // Notify listeners that the field's contents are about to change. + this.getFieldObject().dispatchBeforeChange(); + + // Now we can clear out what was previously selected (if anything). + var range = this.getFieldObject().getRange(); + range.removeContents(); + // And replace it with a span containing our hello world message. + var createdNode = this.getFieldDomHelper().createDom(goog.dom.TagName.SPAN, + null, + e.message); + createdNode = range.insertNode(createdNode, false); + // Place the cursor at the end of the new text node (false == to the right). + goog.editor.range.placeCursorNextTo(createdNode, false); + + // Notify listeners that the field's selection has changed. + this.getFieldObject().dispatchSelectionChangeEvent(); + // Notify listeners that the field's contents have changed. + this.getFieldObject().dispatchChange(); +}; diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin_test.html b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin_test.html new file mode 100644 index 0000000000..3bcb29c1ac --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/helloworlddialogplugin_test.html @@ -0,0 +1,198 @@ + + + + + + +Closure Unit Tests - goog.demos.editor.HelloWorldDialogPlugin + + + + + + + +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/seamlessfield.html b/mapbox-gl/closure-library/closure/goog/demos/editor/seamlessfield.html new file mode 100644 index 0000000000..64265dfd14 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/seamlessfield.html @@ -0,0 +1,106 @@ + + + + + + goog.editor.SeamlessField + + + + + + +

goog.editor.SeamlessField

+

This is a very basic demonstration of how to make a region editable, that + blends in with the surrounding page, even if the editable content is inside + an iframe.

+ + +
+
+

+
I am a regular div. + Click "Make Editable" above to transform me into an editable region. + I'll grow and shrink with my content! + And I'll inherit styles from the parent document. + +

Heading styled by outer document.

+
    +
  1. And lists too! One!
  2. +
  3. Two!
  4. +
+

Paragraph 1

+
+

Inherited CSS works!

+
+
+

+
+
+

Current field contents + (updates as contents of the editable field above change):
+
+ + (Use to set contents of the editable field to the contents of this textarea) +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/editor/tableeditor.html b/mapbox-gl/closure-library/closure/goog/demos/editor/tableeditor.html new file mode 100644 index 0000000000..2eedbd56f2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/editor/tableeditor.html @@ -0,0 +1,94 @@ + + + + + + goog.editor.plugins.TableEditor Demo + + + + + + + + + + + + + + + + + + + + +

goog.editor.plugins.TableEditor Demo

+

This is a demonstration of the table editor plugin for goog.editor.

+
+
+
+
+

Current field contents + (updates as contents of the editable field above change):
+
+ + (Use to set contents of the editable field to the contents of this textarea) +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/effects.html b/mapbox-gl/closure-library/closure/goog/demos/effects.html new file mode 100644 index 0000000000..a9de5fee68 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/effects.html @@ -0,0 +1,162 @@ + + + + + goog.fx.dom + + + + + + +

goog.fx.dom

+

Demonstrations of the goog.fx.dom library

+ +

+
+
+
+ +

+ +

+
+
+
+
+ +

+ +

+
+ +

+ +

+
+
+
+ +

+ +

+ +

+ + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/200.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/200.gif new file mode 100644 index 0000000000000000000000000000000000000000..6245f6968ce0af941fe4ad49126017e6f9d5c114 GIT binary patch literal 941 zcmW+#OGs5=5S}u8p+>swb#){3P;7z#klTd{G+6?3V1j#y!fYT3#DQQBc|az%5eNK3 z1RO#&z#tkbF$x_Z1~#C9MV_3uQRA@)d64i2T2#!!gj``4D$o>SA>@dp5dd^y96`e^ zqC_5$iEYFIO)w0HPz^AM#wKJz2Z(_UXz~;JG}@@~2p2<;@CRB{O#X3ruF<`{8J*B$ zj;`XfQP7@xl%20earhA;hmI79_M1dH95{l;=YT6AJa}@dsSKOUutZL3EORxHo2-t_ zRmU!tmh@K~>nlHUvhG%S{XIU>zvt@9y3Vnh?irg-v)Ac1o<8+(TS_xiqgnr+XQu{o zERp-Scm3++f?vlsFV=2N*2Ly(V~bU>`RdsBcwsUgn>$eSd-uj`WjimFl;G1Z{IhNi z*l7ne;E%{B%DhRIwW;#_>_A)QhAoF*BtH&!q^&Hj#`8Wsy|VbZeS6Dzcf8~MKu<$) zUiV1no5{Y0;&Vln&C`#X>TZ;L80so(JUt-`4F_-UdfYhC@v3;Zb6@-Lk|bh-E#KOH zE`KTQ8EXCU?m@$=rLUuxx|)0IJCItm^8W&? B(eeNQ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/201.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/201.gif new file mode 100644 index 0000000000000000000000000000000000000000..b740d39de4db5ea79864cf5b32a5cbabf57aca1a GIT binary patch literal 980 zcmW+#SxA*p5T2mWpoh991T7ayB}!5n)2^m1?zb=#yf;&C*QK$CLXDsvDj%w+hzxug z3?ZV8HZ@SmAc`b{V+DaC(qi^ti6ykCw4A>4pSNLVzHR=$sHAX9pw>q|nkBOC8Ee&A zv6ihRR~ciCS|ir5HRPIVtg2P9%2vtEim{?r#0py>x5~z{S{BP}nSf-;SW-)3i7lZ3 zHlTrpWlWRfaZimAW7rsSw5U{#icvO7P6e8xM#KmkAtws6tcJxf8zyMPsv$KbhS(5T zp&DQi4V93E9*BVrXkcNPoFP}^i7Km-^9Nc~A}Xvx&Ms7-$)e241oseyStU_oCD=nA zkcn-?0sjyIhfobLh=xjxLI;R}4QODIOU|`XFXe8ekBOO~`@{5Ca>~HRDg5_9xaJ z>WMaXMH}ze)L*Zzt_p=Jg27-x@xh$j()9FDI{=~60YX?^+Y~GwvP$YTzl@AAo zDx>%IMXqIK7lo?ZYvs~`sFds~O-Wf_R~Jo7OU=p(l$Y-xO{@zG!>pS^w@=8+w zFMog2hnew#%<#vY*0~?2vTyfHXSTNdPJYyJ{C?fp*-6d$GxuR@^PP<&=T|JH?Elm) B)v*8o literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/202.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/202.gif new file mode 100644 index 0000000000000000000000000000000000000000..2bc9be6927d1b9f01cbae0152042a5bf7e4bd15b GIT binary patch literal 1054 zcmW+#ZAjH;6hFO)H*RQHtu+y+z1;aip$AMi8{1$?3$50^X&?M@Hlj>wP8OTQKp2ii zR0M}R@3s%arO?VOOC=}d-Ez;Q&0;}iZHd}Im#(~N#_9L`pKphA&hPE~|K;1tic4zJ zD2)` zjl_tJPyidyz``=7$@BQ9&WUsE9C=z)s!qizJ0-6IO;IP}gq@HVg;`d|;+P#1G-B0} zIub|h2&_;IFo=dq$U+aqzy>t1uuR^Nukl2cRmuAUEh-TeRv~W}D$rz6W@UnVh{CLr zD6takArHvJHsXMPh=4<=1{g#`B}SnG#J~nLu*fIp+oPJ@0=`7z1!OyD%ksYtoB}S{eQPUU#`=cZ#T@3HLXibRbTpKbwTc`(4koO z$CD!$wkM|69cv6F#Xm??#(Ep3V{KJBdB5-Q#K`I4m6aJKTi+jl*wgUS_pjH7kF=dA zf1F>suP*6KMPx@t@4-hm2A0ehP4y*>cDCgGwZE$C;KR&)<}>X*MM83-4KOIsru0JjN#Bcup&zZ|Tqu0Jl|28Ez^UcrZX6{CZTB_cnH(mg5um#cp literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/203.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/203.gif new file mode 100644 index 0000000000000000000000000000000000000000..1ce3f561442dc5e2b97bc4f7c61b75fa915c72a4 GIT binary patch literal 996 zcmW+#O-NNy5S~iHHZC-+OuX-z-j`_<4p?fDZ45CZsfFOfaB~caL<gR4lQgNLpxr6GcyZTBedkY5rtM79pqa+_M{I=KGs_59R0W%_@tgXj&mM_K7jt z7-b9_L$(qzqKzmcY=mr6i_zL>Wi%U2c2lV1X06Hk11&10v|_Eu+Jy=*>9Uulapvj)dPNR()k8m*r34fqP z#biGYo2z$k+w@LoWA?6MXT6|p>Rxts)r-R(!ED_LF044l@K00fojP~ zUUZV@o#d%>XUdl{nOE@9=Z+=0V?HR2i9{;^L@l(+KQ53h3wsCcOYGIE%n&vJQAN!J$ zGUB?!I|BIh{eL6Yz$m3d1OA9;qR1y?HYEJ%Db2YM854K>+0kp4n%0GTOTKm|?ysH8 zs0eQA{qVSccJ^RPxT`b2b|K=<%X5nznHN9zKW}?bI5|+g@_VMf{Br4|%G}1ah0QSz z(Z9N(UqgMVp|h($XU3m~0$Hbv?^ZWnt!xX|#7#GBEvPw_T{YHsYjCKiEp)Fubo*6J zRQtP!RU;KQTBCyZ2hN+0!7E+6`_D{BS&3|J&8jLN%x?;IpEywU%`G@`vt?pmU9K;B Q`S9ZPlMS6))<;mzfAhKGo&W#< literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/204.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/204.gif new file mode 100644 index 0000000000000000000000000000000000000000..2166ce8b3d0e7344478b36640471c60716e73ed9 GIT binary patch literal 1016 zcmW+#SxA&o6h1{!p_EioY^%WBCrja6gRh#FQyZdR;{Q4tkZA-Bp_*(i%LD-)2Etdda@C00TKY(N7G z%a|s|J1TOlV3vurGjWwuPvh*e9*l2~F(V1;Uc zK{QlC7J48CHlTrpWpajGjVH#iG35M#7L|w*HbTxWRG`UXm<E6K05Pxu4J>lWxi)G%79kfT{DBq~voIl7*o6u-g;)qVB54Ew zT^L8uaEmCB2V`O!aX=Fc!y!}y45G0KSq@wEno93R; zV^XpgFWq(U*tWgpx56*Ks(#kN+uaRSb$64luAEom&(4`sQ+r0Wqqiq~TaRi*m9 zdGJCdKK+c}!-jyJWm-x1G$5=@B<-|}?#)|`@w9GH=} z=6e09NsZO}S}Hp8D!ZDhkB@kMx#48`*LRQZ6kiGLOf3wI-*jek#2+8paq(AQk#G0M z*YC~_jGjyfMx72M-&q@bR`#iG*{pj!ZNZ8_@}9Qosa?MNn;&N`3f9(ctxj}?yIM*M zmhav8<4}9(e13lUPG4DCO(Gh45scMtkJcAdWG6M$U!3_MbJXp>SJoB literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/205.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/205.gif new file mode 100644 index 0000000000000000000000000000000000000000..363e045b336be8b283f3c2df63a5ed47c693fe72 GIT binary patch literal 1032 zcmW+#eQ1qg6hE}KJB_le6~aht)2M?sv?wORidxN9QtMDmt$mq^4f{tS#;!*7@zQ7h!JsA{3pn-*D zOq1hr&sZzgvbE%BQ5j>6Si{zkQ-P)!D`JJMkQ0SjHkQRQTPA44swHDdEU_i9LN&l3 z8Y&?RJrDyM(7?hnIYX|-6JyvIa{fSzO2h~oA!ipV&}1>ph6(N=3bTg95F3I$*~pb3WI5UK$N(b$A6=m0UW0Zr~iZW?XWc!Y}~NcaOSDkk@FxLmz^ zyQX(S7qfR2H|quMQunfRt6m)L2%bZSuj7vI zMZov)yzfe-ud2W|`gG>%l(>ZW>BHyd{k@pfcVc1P&N<E0nV%{H!SFRqli=4zv_@M;)nMl07%*?`zK;9NQjf zsTf&3<6_JVc~x|8Yj0>Qd;W*&#@?Dmo6`c%504I&-t|w)Tm4SXHEvtvo+koeJ36Yh~< zRinzPv!~CLk$UB~da;tb_vC zfCd(pF-?xgJ+anU%hr;kMI~azSYa#VRG?|Z8ed(ikCa3zEXPmXFlLB|qw z)E9e`7#m7ll+?(XthvLvGZlq8u};T)t6zMppL{y(U$y?#cZspV_5TKvR-PoUJWgJ| zm$Gy_b@6(7^ioFj!p3NOX5>^>q%~{4F?+rtJ6so-JCqBkztwrO2R6-C=FjXZm?GvzhEhT4cKWLG~ka&Bl15dP0ZQX)3v)}dHi90YfnLO+tl!2{bJSkogI^*x^PR; z-B9y_zpe6qcYb-(uc31}w?5W{F2@aANk8_Xrnn&*-!bA3Pgb-}e7#XR_9#%+t}QRN z7gq(IosC}!zR&Ef9SO$#(?3E_yUK#!Ud6(`?y0`U-~D~(w_H0C>RJ0Im{gtKx#Mcw z%cFerMD0M+&x@b8KG<+4bTVF#ceV}(Lcww!eSJD5ufI7melX+h%tT-Fd~ZWuc~L%< F{s$rw?9c!J literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2BC.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2BC.gif new file mode 100644 index 0000000000000000000000000000000000000000..aecbdc0d39116428d3217d41804b06ce680fe7cb GIT binary patch literal 1039 zcmW+#SxA*p5FSgp_qwK8nzyT&Whzq9bj6!xLXib6ga|!EA>m0)%LS5X0R{0b2zuxc z_EKOFCTgaZ8bJl-iLlksvQq59#K=f(o4)g(w_#?!ZT>%h&#vr1Q4Gb<9Ff>(M6`$! zVG**GF=DhBC5FY2ZE7Q0i&mmpG}&2YL}^h<6pJFe%0^fVD`6HUAPE^EEu@542nDbK z4J<5Unk(Jf$^jP1YZ1Q7NSrYem*BRG`U9GixTehbYWyQkqy3 z>>&@x#5Ur9e~5rXs0J8BLnTI`1H`}vG_c4fXWOXpSShkW!XIc+F)JqI3cFB&rXp5^ z9Fa5vfG&(9Xt+g`$OAI5jX0nQhT#yZ0S3|7ge>R)F|Ywm_C$6XZPa*#iy=t(11%~h z`*GM@oqOA+b3z-la}_)51Z`7yva_pB9QFu~LyP2ywl{HfSYQVlp98Lh@ZbqlSy}$5 z=;--XYir_GOeL;)pS0$YH~F$Rab-kg(BhC_mn%Fv+U4i+x0Gtr(Zx=ut>3Y;BXxCU@=#ywm*+=&E{xvu_D5i! z1}>LQyliegnser6!QlOMr>eIm%ztdV+E`W8eb%2letquZ>FI`v7taP=t#dPD)yI=s O_nrLEdpanHa{dE)>F7NG literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2BD.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2BD.gif new file mode 100644 index 0000000000000000000000000000000000000000..0b352dd629ebcdae9013526b80ff519145eb60de GIT binary patch literal 986 zcmW+#TS!$=5FIVh(2TB{X{KqZb(Nw9(eT40PGI^t&C=)$u>nB5hKb78zDQZv{7P|GK!5NyUNYaFagPsHYA3WAvS~p z*nkEWmN8A1$3D?oY0X-br9~y8MQLFzWL2Q4L@T8gYeiNRW?3{V&8(TA5v!U+lhVYR zzzWp>gJ`IPEc8GOY(N7G%VZ7N8c&I^2w8uiMWvJ|7Dd)BRG`U9n1u=MAqulXN{EGE z4|zZ)wh;&XLj)W`HNYSmDlrNjAO<#|fkies+eVGYN|6l`{y>Y0Sur73*o6u-6|o}Z zh@=q!bYUDp!!4pj9*~J`!~sn(42Mt+Fo?z`WI+drfemP~C$iIMqsAj#3_-#lXi+iQ zkHhBb+}k#t6WW-atJqm5Xq&o|on3X}ut#tlS|mrby@{j40z1(79B?Is2Tzu2VPP>U z))Tp5Y<#@GzyDpVyQin;&YFlDq2YU;m8AFw1U=XrcstW~(<-0Z)cfI){TmaeBV#TG zudWGOTOAtSu|96A-|~oMetqxz@(T+60s>Og(%kX!mntiL<3obCtndt_zZiajl>cvA-yZ+~ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2BE.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2BE.gif new file mode 100644 index 0000000000000000000000000000000000000000..282c361d38d9d3d2938077e1a7e567f1de4a4e5a GIT binary patch literal 1074 zcmW+#YfR2z6hFq2HkI5qrN(P(%3_`MCeg}9b2n3FA-A5GON}nCrG&)?%5CJ?D}1qG zU0y?ql5%aH#7J({ht{N`np$$%YQGN#G!m?xDIO0iO8Xi-V2q)?KTB%=aNA(aqHuo7fMVV0$0p_mmDG-6eeR3sF! zBCtX=z#tkbAqzba0~^r5!ZH~{rp6OevXo@}ffkhzQm__P>aEF`mJf_sR-M2S2g6WfRbnqU|Xp&DQijZMgc4iEzy&}2?zrqM=?N4OY*gg?-t zVlp3x$VF%;loFZW(0 z$hg>4*^m^MJjQM8E!V^9>i533b++)cA&ahs#yX>yrtQwEarX+0SznfzT54Z?{lLi= zUXy~IC7%KU!yX>VeShu@KK*IKqej3e&4LE}5k5rjnm*=li(7RiCcF5>ZT{`B#^##(#Yn#METi;p=s z#YYC`e17)Se+4DgCbfH)`Y%YI_~cE(thIR+=^O56`#P@HRW|hpt!zmy?TgARuG{(6 z-doh4+3nw?nos!_I)cJOt0uX7&yVg5dVL}!`}f^lxqg?t+MBB%L}j+soiEJEPpr$O F$p6Bz5xD>W literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2BF.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2BF.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b88ee7ea48abe05af44138a140264ee6ba24bca GIT binary patch literal 996 zcmW+#O-NNy5FW`&%(Aqe+Vy$6`Q8h$A8JrjBifX-sTNUzMig-bOGS_*S4uxZkzrIa z@q~6+U`bg*>9Uulapvj)dPNR()k8m*r34fqP z#biGYo2!3s+w@OpWA?9NXZ@gU>V9^1)sMp-!FOnpe9`tMz77lQL*sM6l@K00St>6w z>qY*0ktsJk;f7*vXv7V@4zBJCdL5Z=)XBQ+WHql07AGy4^g?4^D3%$HWra+3PM_lr zxS>Zu?__GAA&}APWR)&ker!ehXj(Xum{hbdIcZ_SOmyi~ePFsGV`5Kmyl~anMsGGZ zJR6DN)AwfLD2Gvl(TVEtf0&F}Q9lXcZ`PpW1fp2?nG?DW*-I2G@kb{~4x^6hD5@y|EGUClqf zHx91h;kPwk?%s6T*R}Om4PQ(Ax1hf?|JJAHXZ!!`jqdL}($g|jF_rtWtD?LrHnPtd zykGn7+>4JVt`{9|nAd%(pnJpPy6vew=hKqQ@*kAvZR!nFAMEM9l=tCY$;GnnJHoAB P^YglnHV%!>kEi1Q#75Z- literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2C0.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2C0.gif new file mode 100644 index 0000000000000000000000000000000000000000..17fa1a39ac7940f954717a6f672e3cefc8c9ad0a GIT binary patch literal 1036 zcmW+#YfR2z6hAdq8)2^xExF7x<}%vO2Z|)jZN6wWA4se@eLyNp*yNSMl;xedJZzfT zkUnHunp-z4m&3{$QEM$T@vgF!dfD3^zvusaJ3Ht6Zs-5sxOu~hq`f}!(HN0+Ppvi9 zinVMlxk}Zlu_{*CD!HanYm7Bw4O>HQR@92IB39T6xm8xn#uWr9YmDj6kFVkNLb zHNYSmDj^F!5Ca>~z``;)L$1aXW7rsS{y>XL#0VQ9XBR5aWHHQ!3GN{ZvxdYF8-hLL z0h!oF9Pkeja0t}^gJ`J4D0F}r*nkEWx#V0MH6DwQ3ljc7i;7v8kSpv$1)4%EgdCAH z0)Q@zBWSoql*j`zv5h#O35MYissRSk*n}+T05PxuP3}Z)8g0~ggo`0a_ya8}Ciii; zT)lg{rguUYvv(CY>jmvn_p)=VUL5WSo3R- zSg<5~?|yk+er^1`(NA6N`o18oXnyQ=zrUoasya6}@8-?QieMhr^*zXn1(IzrVk; zv$L+QE)WRd)1UiyLIjM`5@^65;UkLvNniHGZ%W*`@`&{<+IlU<|KcqDnpoUA__gI^ z%&flJhmo17C&xn<_SYw7HRKPz90)F`=y`r(sCS{>tEqfi)b%>IEXqG8DZleU^SkKY z*{O%VJ_!UOn%+KJGe(n3qI;XpV;%Z>-{oY}g3O1S%RS@NG-4Tt?L zr8_4M-gurLYD(7nwKE$Fs#~hk>-%zg9{mfynO>OFeLwqWDDG2mnlxVT(_7zS&+S_M zc6;#4?WjH7&kkhPHcy%{lF@c|@s{jKBS~GaN_{5^!3ne3+zEepZMGa~C|X-1bJ@_W@wyZ; zjF!2UPN&6|OM6eDHs%ncokFY7q;MYJ_xJwy?0KK>^Z9k=SEI zw1^U65wevrVzd|~hQ*L=Y9m^UR-#!n*}KY!(xQ|o7DaZHjj$G0!YoWs5;8(sNC~kJ z3TQ(bS{TM{vU}{&#wcUh7_w_oX>GJJnvEt)fu_<%DWljZvQW6o+ORUrh6#)qHKYwG zLu?3JK@BlLgA!QiVKKBJ4J{0lHDqfXr8R3!)*sfQQc5e#ol#-wy8VW*-l2H*q+uup^Dn0k4Gc;0ddu zW*m%!=SbtYul32&5G8M-CFqLO_3f;QY49(Pn5afSN1pWiUOxC=)xgH$=&H-hA`gZ< z&mY@!Yy7_}!|xvU-rhI0>vT-`NKAcBoIh*IMc>Su8FQtf@Iy(?ug7_HsUtfc9w|v* znzv-~jm5F0YvNyRUx80QuG`fMMrjHd@JF~o0@MBedQKGh5443dM?5<@wmj`)nYVM* zpn{6r0nNGf;b6$)`&5~>q^tHq`LW(Fw|<*cd%o;s)0@Dl)jK9UN_`)FcwA{o$dlgs zQM-<|#|Bd0r5s&yWcPpDUJzKB)3$r+Ac#is`XQhg$Ac zXASc$4D9c2i~C*dyO_M|%d1C4dvB#Axh`#cac<(yqD5(~O~Lt9p~88in`$!?&UQ|J zxVJbnlwT9vxM5egth)8@x;u0Cd~qNDylPrwLi+ho+|V=M6UQ{?{&es2w!cfM%>Gkx JU4{*z#Q#O5E3E(k literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2C2.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2C2.gif new file mode 100644 index 0000000000000000000000000000000000000000..01dadbeaf48151374604ea8219b4707c53179e5b GIT binary patch literal 1049 zcmW+#YfO(}6h9LEV{>V4lNlztlVhVI%+e@67@GZEmg&s3rD=2fyW~Uopc%?DW@)pG z6}A5)ubHJW^C*V7Cb!XOHCimU_ zRo1FCYDF!pCD$}ojZq_NSPi*Zu_{JIR9J=FDqCfvEXu4*KvJ?wMoE-d2?ekL4J<5U znjDXN##*tKttCf`${1_J8n%X<3N*!75i4wkoG8q)u`HI^GC?C&Eg4Ht1$R+36sPR~YT#)bwT2#!!gj``4D$o>SA>@dp5dd^y z96`e^qC_5$iEYFIO)w0HPz^AM#wKJz2Z(_UXmTfV(`ci{BU}tY!XIc+F}aV!i0j;bMQ_Qq=FX@iEW zkDFCIJ=>cpHFI*Vji1cfdyj6~nEvhSNqrLWxqj%Qt5dbH=f4Mmmv{5^{f^qQ6^BE? zzm04DKF(?PGBuo1c`!L+{&X!H{_NbcRq4~N=Ek%ikFDD``pWjWGn?a|7ml)pvkHO< z-ijIc^hb6@b^|+2ga-T(F+_nMq|w91<~|%z5QyH@JF{(AZQi>ZaRW|OlqKf1JUqsk z39)rYdRJxLKb5d9wP2vPIx&9f$Jz-^OZxmNu6UJ{H>v7aaB#<~TgzYX@3}55yrHyo zUXPsM^%JKbmuHmpYJFCenzZXr^`-|$iZ0IG({-mJ_;d1s7m@9)FTBFO&2vIqRv!9V z)3Dl$Y-@>I6zQ%RW8u#N;``VPyq~}kM*Hkun`QN{E-Y%cgJa^~Y z`u^uLi-#wNI>Yy31AWevY>W8*t0V1l(~aG~!UukrFFt)M|3j~m#Zp#n`I7DA3l8Ua8T#t}5!B1+@|nb<}g&;-M92-N_CXlz0jbbuJxfF^q)JB>DK zJi^5gB>aIE6_foqY_86|ZPPiSjoG=1oppk?sXN)(RVNO61jnI8azxvkI65q_1C7rC zS3-F31gdQ=cYeyMJ?<41?$7+bg7&l>^~-f3@A94;(Zx6m+cOVkD z&|7wF_IPLFlH}XXpSCx7&iGSKmZhGpT-{XWJyEnG_)T(L?EI!3OKY|y=6DwG_a^Ml zT)bt~qOaF1+_QC4Ak~%aT9m%vla=xD`1F%!qoZJy(x8FtKS41t9XsdA*z5Jj+^tI4 zEWh^Eyt^9M6#o9`=as{^)*mU~nV*%Ia4#?w?p)f{9R29L`&i7`uIi!Q;<@C>i(mT3 z21;{%Bd4=MfBaS)?fLM^TkE}A!=3?m-jSF0V*3yIU)-$R-e1)>*tYCW)tg_+rt zUC;P4;c3O_Kyi2d^E=IP4|WxK&%N%Led}tk`XZEi;@ju*PPW~y|7+~prJvi9@lqF<(BzFXxp$+=ABUo%DEEI(@fDi@ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2C4.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2C4.gif new file mode 100644 index 0000000000000000000000000000000000000000..224527b6a977561489874c29e452ce36ce466e47 GIT binary patch literal 1072 zcmW+#ZAjf^6hDEGGQ_$K0=-H0p(58E?J&5dmnBlJVMxJXaAmR&JuXBiIN!=Ng7PI~ z%WUo`u}rogo8(wd4VG-XV{XSjkPKVnkSk94#tq#Tv0JmYfPSjTK|Wuo!ZpFw0ih2(vIj zBUXj1kP%`butGJ!AQ~zm3q2468_>YQGC4!8#xvHkwdDMP7L_s9ur=iDLIs*^EVE^T zdx*lUC1Z&#!5;E}Ol%_#_=gBMgld36G*n^~IzS9;Km&_ha;}XUkBuQ0B>aIE6|-SN zuCNOgXc}Te$Pq~+0O-Ovf`(f}i98?^+lT|2U>FXe8ekBOO~`@{5Ca>~1jwvh(B=PwcDe;HSZN~uSR0`e%_k;et*{9Q%#jR-nDe2XxbWT9_g%E`r(7^3m@%yK5fnxZgy9XwH9rCQxbVMGg_Ow z(0H&aC$P)E^K4P>)rz;z7le|1N%-{B*5e+5QQ8F!_#=Eo@i3j>Z+LNPpzNQm#Qgm~ zrtB!69y-_6mX~&=Zv0SHxIQ)&s-B--8@*QgRq$B+o#F6$AUgQydSBwsCmT|NW&Y5O zaA8SD=yM%>XK}W&RdOHX$Fax|2c{c*+JJ8$}5DO))I?Gt0o1(!O1zg)igNy*p2 zxT7QGkIyY1mg0?v11k>#so}EtKYD%7#j^I*Zp?%}oI97_UwUtHB)u}=&#Za45E<<| zaX(nGBtLZ*_TfW0*)n)FmF&g$* zUK&F)G_5Ufml!i1FGH+*6XO;_Ey-1jna(1=w-+K@8D zhQJEd0E1|#ge>$x3~WFH3(I5;*&0u2&03T72U=80X~kNRwF?z!veL|&3GN{ZvznA9 z)&zUV12VCVIN%>5;1H?-2GLN7QRo0MumKG$vdP&tYCKkoY>@B=T2#!63Aw^9RG_Jd z6(L6?jR2qv;|LmV5he0~Ol%_#Xo6ulgld36G&Ug%IzS9;K$AU@okklq9^qmL68=Do ziphQ)Hdp7~w&|SE#_U|h&N@Nc)Sc|?suPDjg5%I4Iil@N932+efyU>6D0MvsR@) zEZ_K|W?Ot*oXh3Hr$1vTb`*@#0%*V=kw7#KrppJSebeS=|GN>Nx~VaLaZ&b|T?5gi z4QGl&v-cNVeCnQF6>1p@pU(&iI-c_>S-6NV)66vv)VW zy|3$6ZhwBd#n=D4tI1zI+h6u|IJA5F!uLU6G}69pRA1duPj!2F)1pJC(<-9F6*Y@{ z-`y@P>}ojR9pAZo=a%8W-{u^wOYwYvdAs)b_~h3n{%Xz4z=wq~-tL$uVejSq*5#if z{o%0_I@5RkiOg8ErLj5HH&PbK`!|?4=>PTM@RW6LJEp``zPckfj~yz^^ZdSbsrb=w N$93O>hOx1<>_6)NDJTE{ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2C6.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2C6.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b1e7318d4c91d418ce048e499123ba08545afe8 GIT binary patch literal 1041 zcmW+#YfR2z6h9_ijG~s97HzG}T-pv+D{L60X>D!Dy3E)!9};OOF_*Cz%T$&}Yb$(^ zO{~~ku3M2lJlJaL&3fljGPjp>)0E@){GV@U=bYc|{QsMhH^#;945nZjBNF?Jh!#;I zEJC(2MvNAt#IP8$O>IPL(MmLnCOfN)C@o5fVo_vQ*$8W4CCtJEBq1ZDg_IBrp#V0Z zfrVvEljX5b8>5V2W608?(%NWcG#gD;1)545rHo>u$cn-&Ys1Pg8zyMPsv&Ji8Dc|V zg=&C7G*m(sdLRZipn-*DvW9Gpr?h6R$@&8=Dy6hyt;pJi3N%@1X3Yfm5QSMyN)u~> zJ>&tI*hU=i4-s$()c}KNsKh9AfEd_-1{T@mY#TKmD@8U)_ya8}X2pbDVHYaURK$vq zBa%h{(1mdX4Y!CAc|az%5eGEEFdRZPz#tl%kOdte1~#C{p2$w4jT(<|F$4*Jphd-G zKMtF#b8p*pPH1Cxu3~4Mpl#|-c6QZ?!ydtLXptPz_9l)F3+zDSbHJ4l9z20+wDO8s z5Pf^z!l^#ff@TFaHr{G$t(_Qo@v5z2s5@n{b5&1WtbA%nOJ37(GEq9JrR&;2Yvqpg zl&eJSAJI0s;c?=|_(HOo35a0uA^hf{FaSBmv=nF19u0ukc?S6}K!iF2C>5lFX#O z+EZsk{PNQM>Zh$M82Gifs{Q3=I^e&jP!Hy1uRcEAl@;F;xcgDT=UGkR@ppTZef)bP z+lvA{4~xfrTZTrL-b|}Wot^lrY(>t4a_{@g*GtN?o*eKFcseVCBAWO0ca3~&3$1>e z_x+=%m|lN(6%0>|>?-kwCp;d0)-mGp&R?4w@BXl%xhzGbTq9y@(P@(Fo9&$s8Y$#j8a=Kbd>F$v z5}ToEv=5#M6K1(Bw>Hd}cYE!%Mw4?{@zGdGqH^o1X0>AGH&ydsfw`iYlv; ztE^RP)QVbGORj0G8ly(kuo`l+VpWWasIUsTRkq4TS(I6sfTU!VjFKp^5(;1g8dzAy zG&vskjJ0AdTT6}>l`+EpV_7V-Wr9YmS~8Zz5?cZ*R09m6 zp%Svt12M1x4J<5^GvsPKF@}vH=MS`~M2xTza(1BtO%}s!nBX3wFl$H*u_4$)9*~J` z!~y>h0f$fxFo=dqj6w&9femP2kxS0CQRA@)xgg;Ww5XVc3Aw^9RG=xuLdX$GBLL{a zID&>-M2S2g6WfRbnqU|Xp&DQijZMgc4iEzy(Bw|!rqM=?N4OY*gg?-tVsamc%hkKL zYkDViF?&~WvtH0HbuT-&>c!!X;5l?io@jRyPlp5cpz%52N(c|0K$X66;?mr4(a}BH zT5_%yFT7ux6#1B@4L+@lZmjZ!UydC#FsY-a`noS7cX`Vs&9^V8&tz=!MEy-y~*NehMtb|u}|lze-0{rV&JMf~9O z;DN3kk(`8!3zub7u6?kotY})`>yt~pi^pHCIQ{eUwdAtz#TCb1l@(5I|C5uRJEOYL zpI5Tx*5*L-3IFS+x8?aUEgR`8K-{q?pRr>QfBq!zF)|ATj)(5F^G35Ub D6tf(y literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2C8.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2C8.gif new file mode 100644 index 0000000000000000000000000000000000000000..cb44d16dfb6cd2be5c6f0f5db07e7233f8f35300 GIT binary patch literal 1049 zcmW+#ZA{H!6hFm>v@vTV*7edtR@RooVuog3KWH}bfg5W$*bo^KrSgTD)z*e*30W;B zThZ;tN_4kuJb9V7?5f4+-pnZUa!HTh^MAgbopXL~=l@@`ZuO$YnNbu)Lqx_tZHzKT zjA3KQR$3daj25HWXtGVEjZ#L5QEU|1S$gZ+BtPG1`HcUV=qzx%UVu%f) z05+h3g=I{W<*`p`En2hIWNA?;rIl#KT9H+OrYJ3R)F|Ywm_C$6XZPa*#iy=t(11%~h z`*GM@oqOA+b3z-la}_)51Z`7yva_pB9QFu~LyP2ywl{HfSYQVlp98Lh@Zbql!{Ry;J-(Q{Pt2YAsEyEu0;yoHKH1(rCGB z_(IIctLwqDF@2@+WkvEaKYFk-y<$srPeI(f!>-q-7W`@`a;L3$b@k}8y|Jwsv5lMJ znsYL0*3bKt<^I_E0H1zrXlxja(mZIu9}z_qF-T^dYqfjrVmf%@3W4OiwAk(wtg2@q_Q{ge%K#cGTWHz5Ds}Te$<_b$0>_ zV_wzp!-nPgatPEAcaqYIwF>r zY?&g843`!$CoJLRv@( zu@G3H8ek9&m5_xVh=C1gU}2f8AzR}qtyycb{y>XLDXmy5vUZ^YO;(y&Gr>JXVOEpU z#F}6ac|az%5eNK31RO#&z#tkbF$x_Z1~#C9MK(FxMvccxkqr|5K#Pi5F(Fsjg$gtk zu_EM%q!9pgVH`ojEuusokcn-?0ZlLrhfobLh{h&lK?jI|4QR3_veRgz#v@z|LBbzs zQ8C$%!{+MT+cupO+L)cI*jXoNo4S*oU3KEHM{pckBuBKpiKD{;JJ9$Xa3zEXPoT;T zjO8{77X-JI+;W%Sbsz28b?koT@q0xzp*>Z7#dU*ws~_*HeVTP{s4es^r~di=rqRs$ zk*o&E4vgnCiU;e>T%5!f1+2wC~bxY{1I_PF@t1c*SHGH-REQCe};xrfA?a2 z#|OphZ->ja`Vu!C30+KE*IPN0xLYpxu8#Vrx?ATKx<3sxzJBxYbaiU!v+AX#uQwJO zlQJs5P4>*?|4Q2FPoHRJZ`#`aGp^YFL#xB>{Z*f7Y0g_v>aLFNx!K`Oo9;X7|1k00 zI~(b%jOwXa-ZuOG$;pJdwy0MrZ!^x-nE8af1J`!=0>c|R^L*EPPfY(SX?GP~>OIvG Sf2GJBh&&ow6@P?#xcUUjGSRGCM&nfr8z?E4sx5;UD8HFa@ks@joOK_GLB2BVWkui z$$Hh=XdI(W8RkM0r$MNVHH^!Y89NiRhGyn>oX2M$o`?H-eII;YukW+Z=l%HvhX#3i zC7VE|&^mwy2E-FE2*6=)CdKZ1_okfStSU58sj|MFajOkDeW}{G?NnB`?zrbQ43Yk0 z>yMz*OqH1lB+DQ_zl8|^J9IVKjLBqb)M|0gcZUxj5wdBAs(%0Zkrs=jt3x_Vq9k+Dc5z~zorS3^)Y2tZ0W{qB1s zG;VHlT3Qe^3@R&um31!;HzfiDLbp<>1iwv7%CAIaYryCzu(JdF7n0F2g+fu9f^~H4 zQm_93va|U`MWQ2Lf!DM}4hP)64UjthT3R41?6;|3pT7hkG2wh(-s1ZYpt6$?)*6 zjg4}-45ucv{jIeV7D7|6ln_-0|`8$;r3={{Ldo{lMf~nBlOcWt(zC^Qmc{nb{LI zgkE1>4vsE~jvfHah`3`xB_*#^A~rea_ih$T!R0OzV+!JQ2LytUkVAAj-7!c8xaVc( zLsrX=rky!S&eJF>f7oFo00a*(Kn8I(U?y#%0*i9}XzeMn_LUPXA%Q^R8+8xN)j#eB zy}S+OMSBP?gv?e_3N=ehOZxiOz@G@hKA#H+0KUFJxzb;Jm0sHOs;q2%WO8WN9&7vw zp>Xw4_jJ=K_{Wc{y4Y?Dh^bgw`Zy~A3v=M<(?u75Nqhj_rfQ6K4j3acy(yf|SKTQo z;Fd9+K04QOzxB~FSX=Wswu$pwqf&xESy|QPD|^G$%ds|c2M2A(&DO^Zko8>_wP9vr z0@_M&>j1z5z3{-;m;k@XK=(ba@GaCJ{02hTee1nzOGE#y34{cYT@9>yfoy1sK$btb zyV$@k35Z41$mtbz`54`#T%|1VS{aB_?4Kpo)E&08%GGCmz?qS1Z7`v)UGE2DjYpex zqx4|A6TUvJN}_RRjR=|_HqoyBJTEO$rqWyy;a^0Ihx~-4e=AYUHbS+CS2{16k@$DP-nG=Pva2(HWs3dXGdz2PJwa zsitqlu`c8;qDpUWh-Z5VQ=AY=yJvE5+hBfkXw`ub{`V*Akn(|87W$Yi!Zp?6uE;1c zhT*R0SCpf6fX?~$juZVwpJlYBwWru~EG`{~6O6^S+hQCjzPxGMik&f@Nxr#}#%VO- zsW$H%A|Yu3AMmsS=`$hx_Z{z>aT@2Cw7VVqj&@Cz_Jyw!;lxF86n8f0W=M;{F7$yG zmVIDhmQ48gOz`EY#A5Z8dr2=V;l^%}NRN!1#qW1s#^Jg}H{3@W^0Q(ED|07`9|FdB4U5})rIK8wY zo{>#K(?A~$mWnvtvDjb=Q@it@$Yc$*yEb8WF0qSoT5dmwuf3d<3!ge?sI()4BaW42 z9%}FODa9kJ?bCm*bWTpzt=11ar>SyvDe2Abi9^bk4Egnx{chCKP{q(Q3lFF-a+3lZ ze~{-R{1#vMQpA}B$Z52;aUePDhRBw$PQw!ws^^( z7C}+85+e_7I!5IoLWICTf>M%jYnh(+dvC+cJllNVmTj9uS%=~%jwXrBKQ(h^V#a3V zD^*ixDyD2ozUkDQGbiTQ9Qj#M6K5hOY(jpO)z}$}F&h(*jMT^(i4hy205+h3g=I{W z=kd>}iYlv;r$yzQI#I{!$g4n8oQkNh3VBhOWv494tW3~|RVAk+N~{D{s0J8BLnUOP z2V!6Y8dz8+Z^+kp;v73i-XCaDi8x^=hy(s1 z0uG@XU=R(J7=;cH0~^r5BA=XZqsC(q@6LN)Js6bPQg^(kXMgY)-aRd#w zh!S}~Cbkg=G{G<&LN&l38k>*>9Uulapvj-ePos?*k8m*r34fqP#pFK@pKEY$-waOZ zV-Bw3XM>=9>OppXHHgC>A#muC0@3~^fesHGK;v`3l@K00fvT*eDr4)tr5l>k_O@gd zJY0Y9{;DJ0Inunk=-Jw1ZMi2~cb13u>|XVtaiFZzi(&v zHYEM)iuuzXI~Gou2`|vvb<=m_tzrI5UBb`Wl)ma5t64BvmGtLC;#hI&(BYM@igKC? z^YG~>PtS^gQCbcS_#@(oqPoeV6GJsgJ(r?-hWJWLa;S8C(VEnPwA-2Sam)U3{)dKV z*J3`E%^z7Y)O9B6bIkV6#>IV=6Or{53wJe!abz8~5*(taTG)STneH*r1ORe7NQ93xPl>Hw|>*iqq literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2CC.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2CC.gif new file mode 100644 index 0000000000000000000000000000000000000000..bd757fab902aa4f256649956acc9811f0b97b61d GIT binary patch literal 977 zcmW+#T}YHs5FK|d>LntS!d+J_aow~uMqSMkDpA6usMQ{N$S?<@GBQx>ArT}YQFgZakM~Et`lGBH^$V60Fg`8ZlK#@h6l?m!W3cX6A z#7d9{KR^@Humk!*0t&$zU?2^aXax@}1~#C9Mb0@_Mvh<+az;WQ)*@pTCin`uV1c3# z3&BS?4FkXn?Jyc@VI}+kO-#cMaDrec1Z#kSG$ug{Jg^wpfF^e$*NrlA1mR*Z68f+f z8IyY)&R6f=uIQc6+3a1#^?F7-*FEptsAq>ef~U|Sd8FM+RTl2e4GixtiSI4%+ZIlQbDk9B z-VX*kLYa8_Ie$`9Ae0Khf!seqYvN?r29wjb5?wRv^8R+jGxv0V363=X`W~1(_chd5 zy)+ga?|NT#X)1Z>-OJXuD?_nItxbvU`C$Kr&c2b#8~%UuQ%4VGJ-wa$w(u~#IQQ)0 z;)lt)$FWm~J3scm9$bn)cr)5CyfRfY9ZHNfy?8ZP+dBIE{POP~brX$ekG0jc*m-muKsTTCObkd{p@#5)a6K literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2CD.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2CD.gif new file mode 100644 index 0000000000000000000000000000000000000000..f42f5a151612ab85205925c948e5b3863c46d213 GIT binary patch literal 1035 zcmW+#SxA&o6h6{oAtV_TRFIPoMMOadY}6TTp)rZ-p=g0bM(AL|)X)&QloW!MWZD9D zRg!4IAT=$`%p@t2@QM%Z&16K8A!Sji6I0y2``@?2Ip^EX|7Yj;+-d$KN}@?3W1lug z86(E9F=Q*PjaEjB(QGu?rqV_!qr@mSitMatBg%*vVIyQ$SsPY{#V{KtAQ{qzlp!(1 zhEM<-(7?hnrpfZyr?eKWS!=SisFc!5v|_Eusz6he7SY05$cn-&E6t*rH4`*qRg=;r znphK9p&DQi4V93E9*BVrXkcNPtRY+DiBhZ-S%08KC8C6tkhKdHXtF3~#RT^dg;_p#n`I z7DA3l8Ua8T#t}5!B1+@|nb<}g&;-M92-N_CXlz0jbbuJxfF^q)JB>DKJi^5gB>aIE z6_foqY_86|ZPPiSjoG=1oppk?sXN)(RVNO61jnI8azxvkI65q_1C7rCS3-F31gh+s z!3`(+*B^c8ExhgB*X}K7OWkv0apt+?^y=iaYL};GX-@r$ZCBFrZ+iB&X6(D|DQNQ+ zwr2%8Hid3^gI!sN?z@9`mTozcQP#iaSl_zx$G+18c_&+4YjYE3&)rz}GUvjOuW8tG zs>9bcdaz|+!_$eL7q{@~yZ%g#gHc)z4frFHh-Q2tGjm}yF|YT?&*-Lj=%mn zUVm|`tFt)V<^6lO^?h$?bZ(`N28VmgCL>iTOAfev`{%pzpUwNez#aVf`NR2mzW%Uu z{Og01__6TaqUhU0t1uuRU7tMQBoi;(jNT2#h}VKLz}AaZ8vc>;+Thg(W({ATXTRnSX z{qn@)sR{V>1HVQLgHf6V4frEsiK1cp(YuPTHH-42M-MH3u%aeo&%lXK5obzW+2ZBJP{ujp0do0~lc ztI`VpcD!hrKZR-}6?9r;wzJi*~ zJAdBk-yX?~zmfa?`Q_~P`s}peK-R)pyDAgYbDNr*KbCz8tSO(7*Buv(`MlwJPvg;X wF<-AO*s*_7*{+_LaMRYdKx<)iX3j0(6ppEDFD?#W8hBEhmfqWOcP}mY58@^sGXMYp literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2CF.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2CF.gif new file mode 100644 index 0000000000000000000000000000000000000000..2f7d40750acb60af9c055f4513f116c5a1504b3e GIT binary patch literal 1022 zcmW+#X-Je|5T4!5$|MoXii)KIQz+Kf(QY*~DxpXyB0=foC5Q#P!6;fHBuff^1TXfZ z`cq+Q5v@U@LS&XNNzp|I(P4+$VV4>!(=*@uZp#n`qYzR3b zX#@aW7)Q`>iztx?WMUg}KobnZAyfklqOl2C&;ep#1DfaIE6_b7( znyYtjZF(ovn7ym$tQWMV?q#Q|UL1M^&!HlDqV*=84h8n0@j2j12oIh>MdXXeWoT%) zudmqWXsN{SmAE@x$h^7Hc+ESP`f zP+|S03i*4ywfSgMZEpG6S-Cmkk)dBbC%29~@A%W()3|l{x}`Yv1|eoTt( z$+&c(eZN0D`Bi4^`PSY2@1lX`P|D&x-)DqY4}P5*+x6!0z{@f1b#2ivor!5xHJ@|$ zQSHy7st46=d*4?d-qtg*t}QLvlAdt2@O44OhmP{3?dd78!*3f)AIALU=b~4BZwXX= zn|?F$`BB5hYsqDImhHG5={mD!+L4N&l=@Cbc0M{Zk~${4e@c6-IoKVkfA#C)-F)K{hu$d>A2jxb>LJm??v{6>-Vd&HsxyC0)LK= z6pJVW!4{Q;NgH3H2P=Yz!W4X=i+H*5FRST0_w0t5`TpkKa96~j~AQ~z$3LPK@HlTq;E;-jmjmIM7f`mWNqGA>%YJfpBHX#c-Kn!d^lRJ@{MjJIA;bI69{y>Y0$$cCy zSO4Cw>7UTW>|e#r`a!$Y{p{STABQ`F@6aLnqTNk=9S+!s#^-=5Av|~jRnmPgDO53* zk@-3GK=+-Fn9gHcdMdZJ9!YF0h!>6vrpNVr3B6uo$O}BE+}>WAbRjqGT1l|^RB~ro zN}spGR3zUnPVM*7?v?KAE6sZ91)q6^Ek~;c^6E#6j$h7e8_byemOCEaxiWun=~F;o zCoF|_;nUx{7P|pPDH|H_M+AsA57YBY>%scok?R`Bs#&cMbzS)}_2@>hy??BEF+4Dr z5j{KBrqT2JOMZoh8|ES{HRbW&3$I3O?c{QNvAS42RNoss@i(RE%|g!LXx&WoEka} z-?mm(<@5$0{)=Y*EE|4uvi0}kQsGEL%b9T8^w;(A?oE4|=D+N|`6nl>pkemI##lQ1 EAHmGwNB{r; literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D1.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D1.gif new file mode 100644 index 0000000000000000000000000000000000000000..2bc951d30a95135e434346c6300d2d772b1b6284 GIT binary patch literal 997 zcmW+#Uuey76hF5tBeAwDT6SY<4|64FVJ&74X2YV!NUp`|#IDSfCJn1ytvxW2IYLH> z7o|mQ54t3|d`TuAwlb}0tktsZUSr+k^Zk9_PUoD@zw`TTuc_Kpb|jZ_DNSVlIWsjA zGd3e%IcHAIi8(e$zNyYsO~sT=$a6l@^+yDO%`QVCb)+v%qoczE5RP}fJ|&7 z4)})%ID~3|K{Qli6gof*Y(N8xd~&{x8jnTD2MK?mMa3*k$Q5>>0!<+nLXJop0YDeV z5j5N)O5_2V*hU=C1jBF$)c}KNY(f@vfEd_-CVwJ7jW%jL!o?6I{DBq~lm9q;uED*1 zGdQ7-Ik<|S4TAQm2if`6AP#?oz@bM9MEjcrIy`Uyjn4sBLU`~5sJhKq?p zyX*G1p1<(oP4ZAQIyf{mF*99Ky1w_>)0Q(W6%||d?yak?-rm@F^zGYZSJ&lJO^5AM zduMdjpKHtC9Sg_G^S?c6Yp$*BjKwmU%-r1E?CfkRl^P!(9~l`L7#K(w{gIhB9% z)~1fpkJ>o;VMTe#U?McOe{E0SQu=-SUwUof^VT#C)oq+=D7!(En)fDBeBnO*xl%O0 z^v9mIUtK%0x|ba9zhfV3w%u$#w2wv(F8tg-^KeTmC3<#NpN?%lH=Jsn>Yf~|KRf(- zVjx|*vgF+KrRSsFOByQrbKZyYPc(X+G*zg3S5`nDb@vRH9fue2YYcQLc;ezd&h L!S~8V*;M`?RyOGy literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D2.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D2.gif new file mode 100644 index 0000000000000000000000000000000000000000..a7c50dbfb6b0c95cfe7fdd45a49c28cd28f39c6a GIT binary patch literal 1012 zcmW+#TS!(x5FSymASFu?QAq2-z)R$4nNosKR??pk(t{}EWmU*D70Z-B1k0>MN@z$e z2oa>D2UAfo%2K0E5HD$_6=7Ny1WEqp!}jgjw_#?!+nkef;#mBlb314|trHphv@v3g zGKP&ITWM{y7_E$EqscZ!8xbSQ2pb_gtF%#KlroBqBD>1kuozZ`*)RdgkTxWSlp!{R z0@#2C7M3whmd8HPT4~K%lchx^qD5(8Eo4=osYENK6>CLS6lPg8E6uE#pb@K@M3d6Q zn!pOx0E1|#ge>$x3~WFH3(I5;*&0uYun1XyphcyWC>BN5E>xh&N|=QS?jZ`ZLQ05* zU=MjfCbkg={6hpBLN&l38Y(dg9Uulapn*j;Ion2!$4Ze668=DoidiusSJ;ILG!?NT z`cdZNvdhFbs!K4KRqtCS*Yeh=C1gvL~|BXrsm>Tns_NA81iA z*^k5K>fGBlofF!aovYYcCup0xlbv04;;=_>99kqtw7rR=!vZ_d_#AL0ga=QcdK(@W z7#9BLS%&*w$gdk=-^7(0yR#@E;Omv><}=}iasIOxqkD5>-d{?bt2#1Y>ne!aHkG$$ zx-@>eJV9qi)hF+ox)5Ks&u=s%Vj?eQq9ATOH+HEpf97uar{4DJxS-km#HT0sUXKmw zEIiwnmedj*`nwS_ zK3ek8Yb3buR>n~EYJa_drK{DqG$FJ!)w?lstZAWJ8d7eDyej+q^G!pCt9795M@8FO zuTRU(^ARh}lOx^V`zsR$-JXHPb6hGub$(C8UczfCVZuYwHadFCqB@&B^ktz9zkTa}}Z9b-XiQPytg>t+M zvAB^78rd*IXiv5%G@D|E%*X6y^D&O!^MCG6=bYcy`Tz6h&&!>*CXLc)fXMuFW@;v8 zY(~Cv&YYSPb8L=$Q=O@riYc3tpA~1KCSt-SR24JV}eGkI#NgCh#i3yssRSk zPzhP+ff(3;1{Rjd8}c=tsIn?~f1pJrqQWZV?Lq~bEXu4*a1T+KRT3puf<5E`nb<}g z@DCAi2-N_CXsE;}bbuJxfCd)%R)F|Ywm{zQHnZPa*#iy=t(11%~h|8e+SgM0gC za6%t*a1}or1npA~vh%A!9R3J_Lyr`Q_BRQ1c;EmUp98Lh@ZbqleG(l!6Z)M9{X81( z*%x|W9qHdct~Z|1u`}!Yu8a@cA_J8f$*qx3rID8PvF|03?oHA5qU>Z*^qWL`H%7bG zWhK`~d)8*&D2%uj*`3R?x|d}&FUr20pZT(2{NsXH%Yq3yb==dr*{!o;H}l5VcgmOKjYC+VZsmw~t9B0%S-}nco@4mY`oO8av^WOdg`*vm?OP~ab5s7_9 zM2jd979m?1BSwo+Vpt5>rZ%FrXeF9OlbuyYloq8#u_&^uY=pJ25@ulnl8_P7LQ05* zPyidyz``=7$@18zjZwz1F=S~`X>GJJnvEu_0!^ijQbw^+WJO_?wP9tL4HGnC)sQx% z46z}wLN&l38Y&?RJrDyM(7?hnSwptQQ(CjuWc`5_l~P)hy$8n7!IKtU=WQ>$bt?K0~^p}Ph_XjMvX_f7=na9 z(4u0pABWA=y|-<;C$urUSFy8h&^C29JG<(}VUOTCv`DUKdlOfO1$LqFIp9hN51v5f z%=nyXuQO3T{i8I}kRGY$$i>uf9*-w&8DFz$JTWD>A~`1hm{d6nmCn51nX7PS{Z7p5 zL_M*Y3McAyraVrt%n6>F{(Exz)A8s)NwoV&w7DSiFh6oPZ|eGi$+LSWeY?YlGs3wX zKFFc;ZTR$4W*5c5C~b!Z{1FL6OD9O`etm9!@aFR3=XI-R1~%^Sjt<Vmr-=IJ!|sc@L1Z&;0sUh^Q-f|(9r9^Tti!ZYOt`oCMEY()054+T5q=b zhRfR9I^O@9D_z_Fw7snLaa=Y32_5Y^AIUHG><<@r)rJbrdG6dO>aAbj*O+^|YG}`u N4==VZtXoN0{{ex!AxHoK literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D5.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D5.gif new file mode 100644 index 0000000000000000000000000000000000000000..4837a48f67f7779f251bae6be1c68e8f5775fd19 GIT binary patch literal 1014 zcmW+#TS$~q5S}2Rf+XxpQeZ)4g;EoxD!oe~RaB<${O4_$neR6LzddhTR`$UZN}&Xix@T35 zs;IImxyo9#My;r2wd9({sxfLr4XYtHD^|s*hzhHaTV<{3 zpn-*DOq1hr&sZzgvbE%BQ5j>6Si{zkQ-P)!D`JJMkQ0SjHkQRQTPA44swHDdEU_i9 zLN&l38Y&?RJrDyM(7?hnIYX|-6JyvIa{fSzO2h~oA!ipV&}1>ph6(N=3bTg95F3I$ z*~pb3WI5UK$N(b$A6=m0UW0Zr~iZW?XWc!Y}~NcaOSDkk@F zxLo~vyQY6a7qfpAH|q!OQuniSt9~5r2);vyWv1y zczHZh61%W2c8+6p8AJITotqY&pB7DBI*`0Lp_K`(N=yd5@d|IE(i^Mr#zJ1~h&Np3 z4U~G(!}0#ocyCF(=g_}b`(lsw#u|3V>I?o}-Z64&>qzyc;r-de2LeNRYX@^P2eVcW zW~AfOPaB&$1x9H#G~ka&A)5AuwDxCb^R4#NNv*ZByOM7N%KMgFeSP-g^}w-jpP#0l z4}Q-%IlB2rTT!T|?btxq?WNathM(2_eAO{$c}8(=?eDIVRb5r#4V9z6rZ>M06>mKL zr>5r5*pfSW$OFSreeBx8%nWl#odtT lz_a?IhffY)S+RFz`L#=Tj#SNCQ&yZc>vl9aLzAXb&VM+135Wmy literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D6.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D6.gif new file mode 100644 index 0000000000000000000000000000000000000000..bd7230fdf4c5409a68c9f88f0c3d9cda5df3a193 GIT binary patch literal 1026 zcmW+#YiQ146hDd@wGSKR!&)pUi&-4mF#DpsiHW)FgQkW(F5COgCATE4rP&8WO%7&! zLoO|A51SBP^BuYk!l~4d1 z(7?hnrpfd8C)OHk*;?|ns6?z7D{O_l3N(#aW2|9o$cw@(i)CY(EfX|i)sk2;me>+l zp&DQi4V93E9*BVrXkcNPydhuX84(sC?+>)7j1j|P$lHYqG}#EVFu^@UVOGcpu@LMb z56Hwe;(&jMfJ3MT7(_!QMxg`5zy>t1$S3F9sPWhs@*~pb3WI5UK$N(b$A6=m0UW0Zslyej07mc!Y}~NcaOSDklGN z_*}z#`(}7TA9HvWKN|+^QxCKAt6?1e2%$rd6pHpY33Yhj5E`EYu7vR52~;WF<%VM} zS?vaQxPcwXPnAK#yr7p00w>9}=Te53YaAi$!c~u}KiPOsy$0ENEEE%jS9w;jqSb;1x zXJqUc7^PXzfIlLWD6NOIHgGpCJ**q|pmt(metBDUytCr`v6Aw8^*?&kS3EtQRTNG3 ze|x_F*2Vb;vfpIbw!Di)hrhHR&x)+PP<-%rTjk95((Gax?d$#;nb^Frsi>#pSJ}-e z_x5~mofD4?e0*8c-MTlJ9BaONqSqY_-j=-Yy1wDrx6OB=?-#agd6DS+dGf>J{n4Hi zd56y2EWaQ7oM<|~FS_yJ?v~f_KaCsSJ!rU{+jK3xyz}b1SFvFH+8L{NFFmy>Ix@G{ ro-5eXKlG$yTKl2l!Nwvft$19Yk-Pr>*}SihYSZeDtQpRqN{jvj#Pk;A literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D7.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D7.gif new file mode 100644 index 0000000000000000000000000000000000000000..880829fe556095da7517eb26462bd4a0b6981ff3 GIT binary patch literal 1048 zcmW+#X-HL35FTl+^!n~AD5vk-^KY1$Z<~8FGS~V8yQ3(I!bD=95z!(_ zghj|!#)#2klo%F6wyBM1En10Y(PU?p5v4^bQ7nq=DjQ)ftb|#ZfFxvuw2%^FAr!y{ zG_bIYX|g=_X=9WzYz$diR9YLYjAo6j@Q2Wo=j)X2S%HST&>#DMM@s ztWXUwh=xkYLJ!2i1~jm+OxBRC@s!rAHCcb4MWvKhtQA?iP=O{Z&8(T=9-=U-NoitD zu!lS#6WfRb{viSmp&DQi4V4&$4iEzy(7+;_oNc4VW2MLj34fqP#jKc+E9^oAnu=Hv zazxSy0J<=apy3u#A`i&KHsXLL7=}Zr1{g$R6SANK#J~nL*%R4mv{B;`E`}iC545P5 z?8jkq_3mw(-U)5Y-c{_Z7qm^?%g(NPao8hx4lR-=+TO&|VSznpd=9u0!h- zvNzPR)48z3sbr@-X}EM%a03te;s)m~7@R$SV5-j@D+)_-c&xzvSLpsJaGithz&^Ks zpZhI8^nTCi)9s^e+nlDH(Mx`(CfhliI#QK2T(RcYf#tz1i-TDy!HpcuP7P+H3@uF@ zT9hytACFIe{-5y?FiH!d0e?gk(WDSbNp*X})n_Ls^p!;49{LnGG~;#Vi`>T8C6zxW z)~tVep5}_(f+$LN`#c>-|M3ldreNH2Ttx^{%X#62EE3 z#?p^1%}#rA{ga}e`{m;#s;^yViR%RM68_E)#v{a})C(eEvLg^^1O_)p?9;>-*3M5xcF<__0}_0 Vv4P4tG1FGByHfr9ctiv(`wz(K5h?%x literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D8.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D8.gif new file mode 100644 index 0000000000000000000000000000000000000000..7d727db91fb4a6fde72158cc2780d75c69aa1ba1 GIT binary patch literal 884 zcmchWv1*oK426&KW9uXoCzaw)v5JU8)4|eBL9PB2#0$`h1Ox@a8xRqx;9wPp4Bfj6 z1veeK1##51lcSq-icj8)Fw+;3oSbv=ez!JmUR&E*(1QL-)salg#|^Ix$qIEF#uk)V>D{95#^tNFRb+e|`33WqS9+)dz1@FTJ=h`?R{XIXT+7cxQL% O+4s|5KJI;esr7#h%xnDs literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2D9.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2D9.gif new file mode 100644 index 0000000000000000000000000000000000000000..98a0fa2057775f1bced8549a63f1be189ec4d682 GIT binary patch literal 974 zcmW+#O-Phs5S}7S=@J&~K`n#m&Ff-5Jd|&wFiHgcAg;Yp?iL86Z zTD4ZJWoyY*##p1)h&5~txuzPcYE`VVRdTaptf&>S!dA$wvazg|#WGtaAXze&)RI_Y zODKR1XkcL()8u&EQ)9#!HijH6DpjLml#P;8fu^VtF~Ua3iNY+aVKK~x2^z6#NDYZ0 zHUw6v1{g#`C1jxoVqgOrSXd@!$klkF%Btl2ffkjB3agN_3l(UxD6=xbJw#zvNt9R# z_K*i;VjFS5KSaPGR09m6p%SCe0b*bS8d&6#b8XakEJ7|w_ya8}W?@3EunQGv3b7D! zMA8TVx-gEQ;TBOM56Hwe;(#U?hC`?Z7(`tA6S--$QR5LVh9Kb&w5XWe z$Ki7I?(Lf130=(IRotu>v`gK~&aHZJxFdKD9g-*7-Ne)3fIVn@4!9D+gC|hU&CSis z%!ES6hlk&kmHDfy1A#zQRaIq0#j)~oZfZK)*myb;2{$x^>gsA+TU(;h=8cW@wYAku zX1TjNzO}WL&1Pq3XVdBQ^z?Kpl^P!(9~~X-@9*#H>+9|9O(v6xM542^GaL?wLZSNl z`d~1KPygWNj%_eXWzc{>qL3(mfNVi=Wa!z%{rtU0E>1o!9liB$XKZ)Ro~5z2&9?Ecsp92rt^Wr@;bp6oesp6o2aqxS~E`IW%Zgali(!sDspi^_g?;`Tk+E1RPJ#bd>fav# literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2DA.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2DA.gif new file mode 100644 index 0000000000000000000000000000000000000000..c8318163094d6d17f7203d6b2e7f4e5336b71b97 GIT binary patch literal 920 zcmW+#O-Phs5S~I(DkvUu2n9{lrHmqol77f;)j^_x zS_tw`Q8!T(gY3Ak*+J}3<{_{s6FY=Ohv}K`y$v(-{LT06&+W@(Pb4To3q+6ER^E@>LGTYAnWVOh7UUMrtHRY=i>XfCd(p zF-^|nPeYIpI0SN9RH{KGC3Bp0hMPZgTumtA71dUh?q=6(52LdZp0}P^} z60*<(F|Yv*EG&~7@)}Q6S(V%$Xi~z#>o1+oA*L#PHAL}L@OpaaCf1~mDJd>U=kc!Y}~NcaOSDklFpJlFW%-i%M^ zF~?W&**IuVJyJ7{at8;aj)Ca#gFQ#?v~`|pozG@ZwzS~Wul!rI z2u7&^8t_LXh)OPzl~(*Y_i@|Jl2vOadR83UdAoGrOrhsx#cv& zTi;b>E|yO{`&V=K`0)M3V|{Oz6wU0qu%#?t$h}XF-}uzMba|@x+xX?(_Up^0d-)yR izh~a`e*Kw$eQjZA>{fAEe*4wWqgM(a8aqd?Q|o^OFR*6- literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2DB.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2DB.gif new file mode 100644 index 0000000000000000000000000000000000000000..301c931a9ef2970583964720ab6f3dfb5eaed48e GIT binary patch literal 949 zcmW+#TS$~q5FSgSD9ctViB;Q`q97k~9;!7EB)ULt_!0?bWH~BoN*V-GB2ns#LV+}e zRsO`Ed6Y`=k%j#GhvtxortU6Lh;)orA6{-OS z(NGCl=z$p6fCd(p$s6)Do~W`ad4HfqC8EMAq!OmGiTm{k%bR)Rg`0h!oF z9Pkeja0t}^gJ`J4D0F}r*nkEW`Q&^XH6DwQ4-)=Bi;7v8kSpv$1)4%EgdCAH0)Q@z zBWSoql*j`zv5h#O35MYissRSk*n}+T05PxuP5wlF8g0~ggo`0a_ya8}CjW8xT!VZ2 zW^h6ub8rzZ8lMBMgz(@ARCDFg6Dw98Nlsa) z$XKY%nk&m(sEYoojLb>oZ%y`}>g@T7=xlj(u5#mi)#mxLv473^Kbm$YE*(s?7bQAR zT31ElYQ;=h^lNEkqIkpG!rCe2Mjfzg!kP-|%H)`unS z<9&4#1KS23cN}|GKmKv{b<3g5U2Qf}bGs*H(_q8cHQ;l+b*u`{2apKENHejls*^s01hGN+`dcYLUAc_QBZ T^n7Ug{zy;9jr{9LNtE{=KjF{J literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2DC.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2DC.gif new file mode 100644 index 0000000000000000000000000000000000000000..27ab40852b4f0b6bfc94838baa81331a3955d373 GIT binary patch literal 949 zcmW+#OGuSb5FWvW8(SDc;zF!N5oN(FOr)TfCKae%l|rJ7h89{@1QnSJHzMZsk)7L{wOXyj`e3lSP@83GN{Zvr3}GO0b7KAQRh& z1O6cb4xt)g5Dk?Wg$@t{8_>WapPX-_#$yrkLBbzsQ85b>a)n)}KvRf?kRy^t0MLbT z1P!-{5_v!-wh;$3!7v;`HNYSmn~((^AO<#|$)Ctiqm3Gma4`f4f1pLhZ19z!)bY~Nn5H@GQw+jG_K!!W5Yh(oZGTB)W%2Jx0gNIb0Ly_xi6>k#lDLE z166~0HLvrpMe}NhLO=*xC>$+p94)#&R^0lo@Xo~1yHm&R&z3xxKl%96sh-cJ17FLB zmhkDPuEfT`C~blU{1FL6ag8+D{5R|2Om}76>W%;I#oyjtIz834?rCmndfA#MXQ#6| zXCpOnODoAA&b0Jh{Lxbp8yTPPFI}3eIB~e4rK@!D<(uM;&gAs-nTfxus?$1lU73EL zbns?4`C0jX_oeC8s^U<^@Yv_$=)(88;hJx~mzIa(7k1v7`FL)ytFC0c S`{!cT%tG3pcBU)2PZEQw1!}`!Cdq!+N zU}<9JUe?+evE^AlFc#6wW;NaJtwj<$e$W5;c6QGBy`BGmdvQ^2UUfXh(-e`qXH|`= zsIn@#%38HXt*B+Si1@3agM?WvgtIMVXZeNJ>`8D2Wm)p#V0Z zfrVvEljCvESS!}Dwd80~8DouD!`6^ffu~z``;)L$1aXW7rsS{y>XL#0VQ9XBR5aWHHQ!3GN{ZvxdYF8-hLL z0h!oF9Pkeja0t}^gJ`J4D0F}r*nkEWx#V0MH6DwQ3ljc7i;7v8kSpv$1)4%EgdCAH z0)Q@zBWSoql*j`zv5h#O35MYissRSk*n}+T05PxuP3}Z)8g0~ggo`0a_ya8}Ciii; zT)lg{rguUYvv(CY>jmvn_p)=VUL5WSo@6pD%eJ zCD7^f-}L#%mS;_@$r)R{E|Rk$Qm`emr6{_yB)WU=W#wZvwbLg~PM$j-PVkTV zQic;#!%1lq$$_s)X_4eWI4$EvV#?Rl^aqRm;iW60=_^A?saJgd@yykuS!=oj%c6zF z13Py8y3>JAKXrQcEEuIsXuuy4Pc*lewDEFAKTprj+-kkPxqiex>28{THTT%J z-);FZ{nfc=h6fk^{g6HQxcSGsM@ue@C9SWI?Z3EoQCDS8o8E7I9ST=$YhIuuzV&fc zg$-YVz4@=Y+Mk~p`hA#>lpfmGxMQG>YxY!?whZ<4etLiJYEM%|T*p6&tqgWVD?2vM z?Y|ZZ9r#?*KGSup;9B0#iGO8XiC->7WAA3o%gqSBoRjdT?sns<;L)m>+V7{bAAUSO X^ZnP>nCYRKl+#lUZ#&EDHc|e6bQS4{ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2DE.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2DE.gif new file mode 100644 index 0000000000000000000000000000000000000000..b9a7272dd85eb7cc2d93d076adba35bd3bc461d7 GIT binary patch literal 963 zcmW+#OGs2<6h1|eZi-xmGjs1u-Vz6&9S9{XLP-n^7cQcRu;>ImjG}j-C_;wOgN*pG zg$p4H+5{O>3Z>8@f6WMqUIr2r**u&mtIg^A@4q{obH3NPx2bu1ZC!g2710urx@T35 zs;IImxyo9#My;r2wd9({sxfLr4XYtHD^|s*hzhHaTV<{3 zpn-*DOq1hr&sZzgvbE%BQ5j>6Si{zkQ-P)!D`JJMkQ0SjHkQRQTPA44swHDdEU_i9 zLN&l38Y&?RJrDyM(7?hnIYX|-6JyvIa{fSzO2h~oA!ipV&}1>ph6(N=3bTg95F3I$ z*~pb3WI5UK$N(b$A6=m0UW0Zr~iZW?XWc!Y}~NcaOSDkk@F zxLm_~yJmPo7jt+OHyZ}+QV+9pt6?1O2%$rV6pD5?33WK&5E`EYu7vR52~?S6RmKm} zesCw^Wux);5&umr@hBD_j`~9pFSmPd{_xS<+4H}9deg;zIug%#B{@IH`oRxBSnz|d ze(=#NnT#gV>q^r>+2Z=-Xe^Q0P<}5O&nK$}BVN9|=4QmpRC0!EpC>DGJ9hrMdIO(+ z;_r%OFiPdnfIp&$s9=b+aJcl zJq>+R`|2*n){OJzlV6@)S{!ZZO|=e4ifb-b7bJf5(Bf*XB~k zn+CVk->>-ascP;QOmL@^D-+FNTn=A$xBuu0UKzr7^Yjjrz0{w(=yd-$s#g5!!pz? zD0-xOx}~d0(QA?BX_h89GMb8%r!2)*SOW%WSVD^$i@^pOEQZyBIww5BBGmd=OXePK z;cB_CplBXuVM0Bm=nZ+uLde4pXfll*=!XOfVGS6hVM!}ISPVAMV5yz!GC5)HY9rCd zS~4>?;VW`sLD6L{e8g!CfEVo;jasb44`?!t9dJT03SkWxq?v>kJXj1i(CQO)H)V1{ zTnr=8$67K|KaTcw+`Eh8gl+S{OL*(OyWA%AGdA71}H_&mS9 mosVBmAHL7Mo#tfo)1T?uho8I8f6tD8tZW^hAKm!AcK!oHp=H1T literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E0.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E0.gif new file mode 100644 index 0000000000000000000000000000000000000000..7fd754ab1636ba861a487e65a2767e5092135f3a GIT binary patch literal 1018 zcmW+#SxA&o6h2mHWfAxsMD!p?1#xUricGC6rwnFX(1x(ED@l!MX-rZLEu=&XDAdU@ z@xiUKMaHBuNs27E79nJ*v>}mbhHWNPx9|SQvT?u?-rnj$jxX=B6~ zWegibw$j>YFvta^~A#F$uDMM@s z1+W1PEG%Q1ERTJnwbGikCQFM-M2phGTF9zEQ;AkeE7ppvD9o~GR+?EeK_gZ*i6*6q zHGvhX0S3`f30dfY7}$UY7M95xvNfI(VG*+aK#NK#Q7nqAU8q2ll`snv+(Q&*g_IBr z!5;E}Ol%_#_=gBMgld36G*n^~IzS9;Km&_xa<+{ckCh@DB>aIE6|-VOuCNOgXewew z$Pq~+0O-Ovf`(f}i98?^+lT|2U>FXe8ekBOO~`@{5Ca>~WKU$L(MF9&xEO+jKhUCL zvLA=d)w#EAIw!O-J6Ex@PS7@WCp)|9#9@!%IJ8KPXnPY!hXr<^@j2j12oIh>b)qvT z7|slay+>|)%38OSHl-KVrmQGf;o8HAd*hQzT<&A5Gb`8l&aE%Fw0>tzYSERnlDf>o zQ1b4}>q;9l_BCuMs!J`q?%v{P>efRo+k#=w(RRZ^j=k!sj=DLt*8gR2rEk^wMM;6d_Ez_(e>kP;@1NNU{VgOMGjbzVn~mFf-q4{y!%-J2kD)M?RV*vhJz1##*tK zttD5fS~XV1DqAJjG-{2pMyz3L$jypcF;>J1TOqf~YS~y8%WRo|WJxU~z#^BNYoo?v5pqGoA81iA3lnmMU8q1)h=q_Nl12c~ zg>eK8w}=vXKqj^k2Q1POnjMaASk4wtKU zZ`brr=wkM+;%2>|UFu$TZq+xkUY`uCY}xl>_Ovmz?BdlJb}t5K`KazEa0f3 zVAN@jJ`?m61!K+z*Ph!GTbdFmOHHiK${q$ z#fkXz6XwH~!6@y72K*5|qVQg_t2S2UH?&+ITDkqi-1|UtX=xz!(%0_V6;Eoub(9Qu-B`>X7~NYk__}a#{q3Qa+SQ-?Dvu@wl8ZxK zHG!tm_|~%D!_V^SN(P%sz8qikz<(!{UiSA-^roEKL#Y>s!_q5$G=?(DXXYZa{D)4p zUzr^1y*Hb5GP5OOw5R%3U42IR!czW+!1D|3*JgfBj~|;jKl*lIF0sCOB0i&HexxTf XR9lc)u`n~eHUH(q@!sSW%PHeOFS6~k literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E2.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E2.gif new file mode 100644 index 0000000000000000000000000000000000000000..1718dae3ad70de32d33ab5c222172bd3ff3919bb GIT binary patch literal 1046 zcmW+#YfQ~y7=MV3*<5m&iOhV!=JMhB(2>h*tk#fAWH@EHJSaJ_WT%93n=$6{2@*)TyPRt>2kF~o+z z3e^CEXsCoN^gs-3Km!ZQa6la(1BtO%`QVCb)+v%qoczE5RP} zfJ|&74)})%ID~3|K{Qli6gof*Y(N8xTyn0B8jnTD1qpwkMa3*k$Q5>>0!<+nLXJop z0YDeV5j5N)O5_2V*hU=C1jBF$)c}KNY(f@vfEd_-CU+t?jW%jL!o?6I{DBq~llwSa zuHL;}(>tMy*}ICH^@4V(d)c{FFAjGE&!I!|M7x`KIvlVEjn4sBLU`~5s^ZL=yoKjK zTTyXRB#)g9#)q9tQkFL8^ zmfld39xBbaP?}L&yewR_BviWU;@;J@!Id?;mRIjyaUnOga$8!}jwRLki$hz|s;hgSM_#`T{vVnIPTDgSe{ZGO|W zocTN6H&;a-1`_Y@`!}XMF7?{j=Hge;*Lh#ICnS}3-`;iR(cr9>Jv|+#Ccb>^Zz=5i zAed0!d(Kz*J+Y#setP%ZvX9Z$65sa3mcZMN$DN~wti4kfdw(>c>E^iP;H!IGk(aS| zO@Xi7Pa-L??BwEKKQ?uCW%`egj^?&Uj>IbO7WO>-d|=EIzc0|;e)w7D$n4hO-*81w z-BtgbqEENhN9t$%oLBU%Lo+4~%UW}NRI4sXJ2^hNeO*F&Z`X-X_SyVt|AueweRFSj Z?CXopX|tpLXKjZDJkdlas_ZcNO-$}l z9B2~E&uw1`4;TOFJ}gAzPpLfxXtEj$&@DLQY?&Z>M~H-Sxi9!W_n^!DLx8g zKR)Jp!*mYJUN+6@TJqSBl5o0evWQD~%yk2p0R@5%s$ODe>GY`Jlf~CV`YIACQ)m{8 z8K6TgK$`xR5-zRkZj|DetJ9iC6C)yciH2J4%;<*uACf_zRdwB?yTIvfi`pjU9}C@$(nM^*c*kIzw%|xP2o8ggPN~->ODI-^7<#|SC2WS>EASE zq~FTzTJHFXxX#19b$SVY-ZX8Vn~@8C7$2y6aw~XhsI?~Dqv!Df!}NHkG^?}tQ>{wY zRdR5&t8{#*qo>eI@qfacQQzH|r);`clM@Zy=xtLX7;k7RiBk@4uCJ-LEVSShU0D&U9qEA=cxWeCMsf|xIB|-nIPHV}rqtSd z(drJd;DmM2apG*xC0#vpy#F2;%e(TOv~P<#3u$9bfx5O+risSN+iZ2o%DnF|Ln2>z z6Rr_EwF=L@6S=8M*MdF=pmAK8ExE@T77|9)&tdoo81D`U7@)9f7eoQf4M-DT7Y9NY z-O$N4$S~7VJZR!Z#nxbwfLZcu4tjWDgQnvQm@9=mnmE~|S6(E|q>ObY6eM}VR5}2- zg6(HJ!uD<7#@^w(hKa)zgJcbbPO8l3vai{Pi_fD%Z>eP02`iT*&f(;8GOLZB;b-6R;a7c; z=h3l-iyoI&9IvM=$}7EyJeSR7Q{v?5WCoAkjIVf+Yqeh#__6FwoYXE8RaZz4f1i1W z;JB{c0b_@wd@l4qQl|@bp~tx^vbRf{i&NKAZ_zlx7ahXgaM-`$goQqdfA*R72i&N= zwKN&Ot$3SPyo2*0(vg##vS*8A`#IgD-Hpu7V)_05gBz)fUMzcr(9~ zs>Vn|V|~x~Wb9zz_r^N8PX@EC2>aMun4i0r-tb&i5He;RAIo^KXQ}^w#zSlG!vm0jSd(Ho6W@mVl^MWaumWYgf+8AYw z7{kVpt+X~;87)S$(PW!S8>NgAqu402v!abCBVvS&kX>bMSQ!?>Y?y##NE=dy#1I=o z0c=163(J@$%VVF?TC`@Z$g*Mu~KCHffkjB5>`UiE>xh&qL>vE+(Q&*6^SBN z1bfH>GO>*~;2$F35UK$N(NKv|=m0UW0Szp&$=Nn)JQg7vB>aIE6|*oQSJ;ILG=*3Q zIU;EU09_bI&~S?=kq2aA8*xAr48tK*0}P_E30cqqVqgQB?1}6&+Nkje7ekQn2U=82 z_T#X*I`_6s=Y%$9=PGvA3EHOaWM@~MIP4J|hZe~ZZExb}u)q#9J_lS0;lUHAW}b)o zJHq_!Zr?NaM2qKMVubg8M!f7wKJU%El9G0u4^^h-*QOOVq#rKYdAcC3p*;3$SIBJF z+WGF#xz5mOyBp?fcYkSfPqljfya@5Pdd8YJ%|7vrJct^tk9b`h+kZ2r{c3c7estTB zElv9(YZ4;x>2LhEVi}B5EHvPc2qp>`AhU9P*6pL6WdW}6oR>cu`cApl1^V87pG+$b z?YR>f71WSfx+ZSbK-TuU(^p-jQ2Dido}&hRW;Fdd7*yroe W7_6?`A5`Z(H#IO<{Wm^>cK-)@`RMon literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E5.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E5.gif new file mode 100644 index 0000000000000000000000000000000000000000..ff8f45b5fa87f24ed225b7f8dc5e71ec4709d49f GIT binary patch literal 1032 zcmW+#SxA*p5FVkGR*cZlyjtnK*kUmhrq)Ay4{bz5*n&hy=s|B5?F&7$&?-g2P|*g} zLuor<1x1q8oi;4HSy5=gwXn@B3Tpb!f8K_f`L_B0$x|ogk1k23Wco#9{;8RmIWsmR zU#XgksWW9$@=Z|_F>xkrLVnh%IWgzVu{rXqtj1#OjMQq^kJS{2_6{o@~QM4eN|>d1@2EQ_*JW@Um#tSX6;Q(`5sLN&l38Y&?R zJrDyM(7?hnc|*R&b0RE4-XCaDIVXh z0f$fxFo=dqj6w&9femP2kx$OIQRA_5p#n`u> ziztx?WMUg}KobnZAyfklqOl2C&;ep#1DgDa{50CA@dy_~knjguR80Qk@VN%}_RZjg zKIY&mel`f&rygYISA#hG5dw!EDG=>%66o;20W>}bTnXX96R0#R{3s`KxPPd2@X&`@ zk=vo1*ue0q&_In0i={_m8DY&Fa=%~XLH0;%?r6_C|GVGq&(+@F4yMGSy<4}W9WO7q zwR>K}$>`5Jm8++Q&aRl8sL0qodvJMSzvjh*YG`%lib|P;qa4@ z$kMWYcSwR!8UhXYBa(@_d?4$Zo)}s@s_cDDkNNqPi^i_}`0PS`O4-qZt#tIIo-UlFg^ z*X?w4QO)v1JSizWeg3s*)4rvD56<{dbT(f5?Nwdgr-rJ`_N>08BaYN;Xu7tn`PaG7 zgmo`>?3rC!m3MvFyQ}T>IbVz4Y~23kT=DwH!%KFxC!X#P@6C|4sVR5c29)3JzGh+5 zl8n^qH`iD3>nnZ6{0WU&)n(?UQ!g5?6s6hA&Lwwuw*xS007^G;+koPE6VQK*T0HCJpd);Pv%3`0mhE=Y?B*?YV* z&4rH(;%3vL))e81O<6v+hz)B@*&-Ro@A*G>r*qEl>-_&qmleh4mW3!pzlh8~XQpOi z#%AOz=gg@&F~{b}H`SS{shF}U`B`x$Y9c0VLVlHoRJ!d5gVZZHlTrp zWlWRj@lTx-=h!*&w5U{_ic@wPQ(d2AukHEtd7MoJ0@tvsv~tIj@S`cp&DQi z4V93E9*BVrXkcNPydhuXi7Km-_Xk>3A}Xvx-Y!(2$)e241oseyStU_oCD=nAkcn-? z0sjyIhfobLh=xjxLI;R}4QODIPtLbdFXe8ekBOO~`@{5Ca>~l_-eRZ_U>VPUMB_ zD+?}e$vamz>r%X+W_^xsoO5J#wzbvnT9i={OCP>-I8mH4)DZ7EvTC5Vv~}IAM`eY# zmQQV7lcP(fG|rD)U!0v-FgY0uYhffA%Sz78N^tl>UO15-InUv`{K&rOgp<=Vwx@^i z>1X{OJpx8)1~lN02oa^cAWNMz?e>NR$5O_%#GA*Iw4Lbh?18G!W=a*aed>MPY zD>gpk$>G83#F8^<4egDYe>=+S&iB4-{5kx%=34ot_N%{}IzE2>)-`Wme^>Y0qPl_m z2ZwqtMW+sw+}*wXU25iVT2;Z0-hW5`bgoP`Rh@~(`ybv+#*X&3?tk3*s%CR;cgvx{ i$unomuJ`x$Jbm~kH@359MRdl}TTPX(M~)gv^Zo;~_v~r_ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E7.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E7.gif new file mode 100644 index 0000000000000000000000000000000000000000..157042d3479fa293deae6e1b1c6e06a5431dd01a GIT binary patch literal 1040 zcmW+#Sx8n<5S}uyAcAapNvY_e9zvucmyEI`tq^)B=pjh07_gPZg|LXkmx91htI3w9 zFp{)IY_S5{&0}GSzoGt?jGR7LUMyz3L$TihiRjXo^t&*D+V@0iq6}Ccdm5pV!ESA|a0m+iFq?W`ITS5VB zKm!ZQm?p>Lo*E;@urcIlQK=dgqimF%3N%HHh!HkIP84QY4U1tmOwfo`LuyD2u_3TR zHNYSmDj^F!5Ca>~z``;)L$1aXRaPbE545O6R9JoNJ@TV-a#e!XIc+F$)uNgGhlQ z3U=VrU;1mtG#I6o(11T8hG=Gl?5x-o3A++YXU>!DHDfu?%KAE5Dr&~M(@W=mX|9c~ z?|*r+??ZESs4nhx!NLVsOOm#Qn+hVMPf~}q#yhI5Sw8G#jW; zPg?%BSGT&m>V_W|etmm=epg#;%E94IO}#%St}%b$!Q76{w%Vk_Jx@2pht5{7KlO7m zGIIA&b>hi?4OQXJO%3ag+lk0XX4B&JCw}km4qrHuv+l&7((&iZQ-4OnGfoe+Mo(SK LAB>8cPO1L^iC7&( literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E8.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E8.gif new file mode 100644 index 0000000000000000000000000000000000000000..1eb1cc90a574835225dd0ae1fd2eba0379ab6dc0 GIT binary patch literal 1028 zcmW+#SxA*p5FSe+5eaTdLbkmGffzPsY70rZMC(J+gK$&JF+?-9$hw46f1=(VFB~1d!qzJBx)#*F`c^hWt+vfkXH)o}$75K)ySYa#VR@qoq%VL==6Ob$!OKM3hu_YA1 z1~jm+jA?Q_?x`_i3>!m^7L}?|G0H~CsX$ZIh!|lbXLM1@tz*@X%;S(I6s;2xqdt0YRS1bfH> zGO>*~;2$F35UK$N(NKv|=m0UW0Szp2$+aIE6|*oQSJ;ILG=*3QIU;EU z09_bI&~S?=kq2aA8*xAr48tK*0}P_E30cqqVqgQB+=<*Y+Nkje7ekQn2U=82?&EN| zdiQos?}RR9?<#KA3)-dbW#?ACINT9DhYraT?QY`faKIikJ_lS0;lUHA8hQWamBqD5 ztDASq*!i}?SxKjt=N3h+Dv3$2j?1cDw0-dS?FwJUg@pXeNyS%_OKzqgypwtC{+1el zL{c!Z=vqo?XI{g<(fWUVC4W8~`qf+fxoN|j>+8lJ=Z3rYd~M0?x|liHefU9LYOr|O zkJcl@dc5mU89x1`Q;`ucN(s<_Kf*^edx&gQd``=ajJoOY!nHZ){7(XtzQlQLF@x&@ z;gOf`ntm?nD4QC5ABel_-&EKCsr~!>IsO@esKUyG-o#Mqfj?imJ1>0+Wt|-RJhZ=d zX7>8(@VEZJ#JInwW~{GbL49Ya^;pi2(XBUjE*+Z z^>?sp-^ZH0181s&+e2yPBTpOVHkCC7Pkb6^-`MjyKBaW5t6}=vtp(4j3i}IQy-3Sj zQ#Fz>x9e2%mA1;Wi-V1&Eur>%L(jWoC$=SLlnqy&j^5po_ozJj<5c)>%;fW0TJs+< Ce*!B2 literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2E9.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2E9.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b9814905d49fbd2bbcde47608820678fb6f2a06 GIT binary patch literal 1030 zcmW+#Sx8n<5FV*8i5C6{U#y6*f~19^NGo4jR6=@)G6-!S+c#RM#IO{KTq-b`w4_1` z+OSL$Nu?qrP8RK?(ee|9MOs=ahSPWMc^hWt+veV+l*M!6*M=xWJw)c8GgC7$V>9xV zbLP~Xm}7I~o9ax}R7}~F{H!<=H4zgwA-~GbSdGP)jR{Cb&Pa{Kh>cJH8_>YQGN#G% z_@~Z^bL<>>T2!h|#VI=_uL4a`C*p*ikQaqnR>$I)9TPNS)sZ?9N9+i!Pz^AMhDyjn z55&L*G_bHt-jJ{HM3q&^`vWa15fxS;Zx<@iWKm{if_sRknjguRLsJJTwxa~&=g`J`cdZNvdhFbs!K4KRqtCS*Yeh=C1g@+b1sXrsm>Tns_NA81iA`H#cr8r<7AgA@9g zgRA)2AZVX@key!*;_ycZ9D1Zcw7*H9!vhD<_#AL0ga=QcD#?s3s9b)(X8Ftd^ebmm zemu$SY2G;Sa$DEKReev^^*_ygUOvC&T2lDV(%@R%Nw%3xr*FP&&ui-o@n~j67{0B@$jh1sDj9jn7RYj zJSMAT$JpU}!jFrmM$gKKKl)?StFFn-A8T%XZM-oozVpMJoj-r))xEvcTD!Y5bo*M{ zwAR{PU#btz%`7Ty&F*c~ei@N|G`p>;uj|do^!WYVk8}FZZ7;o7wYniEoP2Ct!-4ju zoR+hxF}1rsS8d*U_2<}zBme$ZE_qv(UbnCMb;bIcjz5j>y1%R)omiI9krNR?bN>S_ CvkLYA literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2EA.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2EA.gif new file mode 100644 index 0000000000000000000000000000000000000000..40d60a6dbe251f8e41d62abc8b8e4688a3cb8928 GIT binary patch literal 1001 zcmW+#SxA*p5S}Q|l(tITWX}biPi5XNDnMr}CazO<*xOMGPTqDi2pgK_^ z^bir!B50aLWU0}yhqNL!MYM;az*4&?(e$1FybUw+ZS()hDM|4O$6VwhKatpHM6`$! zVG**GF=DhBC5FY2ZE7Q0i&mmpG}&2YL}^h<6pJFe%0^fVD`6HUAPE^EEu@542nDbK z4J<5Unk(Jf$^jP1YZ1Q7NSrYem*BRG`U9GixTehbYWyQkqy3 z>>&@x#5Ur9e~5rXs0J8BLnTI`1H`}vG_c4fXWOXpSShkW!XIc+F)JqI3cFB&rXp5^ z9Fa5vfG&(9Xt+g`$OAI5jX0nQhT#yZ0S3|7ge>R)F|Ywm_C$6XZPa*#iy=t(11%~h z`*GM@oqOA+b3z-la}_)51Z`7yva_pB9QFu~LyP2ywl{HfSYQVlp98Lh@Zbqle_vg> zyK_~+(j~`(7o1$QI6E-d9hCT`Mjdye;pTRA9}_fUmGk< z9Is6K=G#A6m+ilP;$K7l%+1`71)E+Tj%nW){UCKsM~&xJLS+4haBozoZ{13FL`YFu zYEg(Q*R{B%rN!s-;nR3LZf>2HnvudWK7Ks zcodl!Ej^bqM`Q22_GV;MT#f%dntWmSSdjw%wNY z9Cuw)Smzo4#OedPlaim7Pu2ttmBmJEEjTK8Q4hw6aK;REeQ`g+GUPW;>wKGENl*|8_QqryKBWDce`)V{q@+SA;9 dDYEtSpYJ(gTib`9y$fkOmD#&2uJbHy{tu-w?p6Q* literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2EB.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2EB.gif new file mode 100644 index 0000000000000000000000000000000000000000..8e2ca7d81b415a087b4953d8e2bc15c60f291575 GIT binary patch literal 1086 zcmW+#ZAg}96h8I=VNk=&eDULXU%yK&*lE>VN+jfBGpVT8a(<5PR+df5g0dfTj5i8d z>sJR|f)!+88o^`nq~*rOz`G59gk^hOMJuC5_GNAj?ymc}{~gXb*VlQTlC^7Ad|Vqx zarBnR*r$yVW0Wy$4B1L+qs3@tG#gE}DcXn_QAXGZ*;%EH5~Gw+Y!um5)`rEfGR%ev zNQSf_F{BK!Ar!y{G_bIYX|g=_iPlPM)|xCWDiJM83u_^(0!<}aDXmy5vZ64{qFHHX z%><2D)g+pfCe{R2s0J8BLnUOP2V!6Y8dz8+Ysl7kN`yto`U5R0r9`nPvUZ^YO;*Az zOmGiTm=#h&EChSV12VCVIN%>5;1H?-2GLN7QRo0MumKG$vdP&tYCKkoY>@B=T2#!6 z3Aw^9RG_Jd6(L6?jR2qv;|LmV5he0~Ol%_#Xo6ulgld36G&Ug%IzS9;K$AU@okklq z9^qmL68=DoiphQ)Hdp`Nw&|bH#_V6k&iX;y)cx%2svn0vg745G`J(Mjd>t0phsNiC zD__jw|Gg83 z9xoodTKwU_wt11^cJFY@e^h5;>BMUE0{oMKTNng+EUxw=gJ<+1P zccX>LeXZx0@A>Q3*20{!<13qH4t+N?d9%&kb?>)Zdq);Wy}f5Ar2X()Lb9SdOj3cf4QJ_YHIE8k4746nrAO~;2kOZ(;YZoJU2i5$5`RM z%$}pCW9PKHZuCa$XWJEY4pi^R~!eD literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2EC.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2EC.gif new file mode 100644 index 0000000000000000000000000000000000000000..884e2267a270797791285fe2e79144b05fad64b8 GIT binary patch literal 1007 zcmW+#ZD`G56hFiA^69!tVlSbU+Ah;kuB}aN*!JO$MasI%blVZlTzTsnwP|kbthrs`CwRDnlbNbA|X~DzvusaJDqcWZ|DCnDO(X-vcXS&`b}irGuEoL zVl7)st}@0NwMMLAYsfX#SXHZHm93JS6=OxMh!wU%Zk3H?wJes|G6Bhwv80y75?ew6 zY(N7G%a|s|46z}w zLN&l38Y&?RJrDyM(7?hnIYX|-6IE6v=MS`~L{wOXoL#6ulSP@83GN{Zvr3}GO0b7K zAQRh&1O6cb4xt)g5Dk?Wg$@t{8_>Wamz-;(#$yq3LBbzsQ85b>a)n)}KvRf?kRy^t z0MLbT1P!-{5_v!-wh;$3!7v;`HNYSmn~((^AO<#|$(_heqm3Gma4`f4f1pLh0@&+fQ*IrsgG%%?}@4IBu4 zt(!BnyAq%N>^~DyV3cy80e^&_Xc8PxPAffDT~Iq|;;-y^-(D5$O&uG3*0uG`ojpJQ z)vRgAiWlaL<%H83LZ_4Uf5M+`KZ(BWZ5%h&U3z|HX753t?Qc03U0+xqYkRk?v9Bs4 z_I>2)+>S_mY2B*sO^?rpT7#LNr!+l#5NLf79lW=HR#)K5on7yfJHJdnI_qZb+mWWM zh8ry@(cv?9uje&aS4Lm=-1kjf96p>JKXBs5qtTaZ67NiBM|4xYuiFySx17*=8xPZ;`DT*-TU3YqC5F$IVt_ zV${C=@`&aBAV00rm=k#$e46>E*P zY%RG;)v8!ER@o}Krl=LMVyv(gaZEE!8|2?ekL z4J<5UnjDXNqH0uGl^iW95f!7tD&$n4X+(`t!)nNh!YqrjQD$X=Myx7{l2KwMutGJ! zAQ~zm3q2468_>YQGC4!8#xo);Le3v(Q5hqK#gMZL6=<>%W?_PRh{CLp5n>_OLmrTc zZNvfp5CMl!4KRp?N{m7Wh=C1gV3AACwNc}-G30`TKhUCLHcZGBcA)}ILu?2+B54Ew zT^L8uaEmCB2V`O!aX=Fc!y!}y45G0KS-yTf(WQ>1Lt zo5rQ~)JS~#<9?+i!6;=y1OA8rQQ9}MzR|hGq1L@=O}~PVdOKPV`i8IbxAcZuE0UWw zJ)1amO|)WUxZGbk=jq3oJy zadLOwr-sR?k>a;{;LpRNn%lKA-yh@H<&0Y;U8zrZmG%@wwtv>bR}DR5PFDPW;F}&D z+}={yaj*XTg5>nE%R1FJy?iXdNmaQfI zUanrdG5c=${`-wQps3(QbzkT6zO?VPk)2=iYZJeR6hzuP+l~Y)tFL|8`|fyfOaJie Qu96A9%l|&-7iZDz|Kd3ER{#J2 literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2EE.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2EE.gif new file mode 100644 index 0000000000000000000000000000000000000000..a96fbd1bec499b92436d37dd067810ffe926e18a GIT binary patch literal 1016 zcmW+#TS$~q5T49Lq9oRy@-Y$md9nVCva(Z!5v+hUu(^Pjh2X1?3}|CVi=*RI>=BOk?y)IFHEKmIt0mVoR*g|3YFG`qS+OccMO0XY+$vjTqb$m-Oh8hyN=8YPSP2EN z0SzoHW11X~d&XL^maQd6i^>>l#2U7SoC-9>SP?61g`6nNvau|d*)l;RRxKGzVu>w* z6{-OS(NGCl=z$p6fCd(p$r*Aro*2W%kn;yxR3b*$2syh@fhLP#HcW62QJ6I(hS(77 zArHvJHsXMPh=4<=1{g#`B}SnG#J~nLu*fCn+NkkZgj|sD2U=9j!h~F57b?&cVj<*+ zq!9pgVH`ojEuusokcn-?0ZlLrhfobLh{h&lK?jI|4QO&Fa?@y|#v@z|LBbzsQ8BrX z!{zGT+cmutx|qGIxLGe~m%5joTlL~_NAMgvBu})viKoK>d(ikCa3zEXPaf6i_gw&P+!xwlz$=i3Zje`u+a2 zv^0GBOA@KGV3bxu1O5mfQF@dHXEs&dYAHCGI)CZP7xT;Shu-&J3Z?H_+H>Z^pYl)5 z&sq!5W(Vp%_YU^o366d4S$25MyZ+{1yQ_mQGsd?!eD`FQb8tYc(;z~I`McY lE9w7HReR-0*W$o|@dvSv@~1V)+SlFZ?u9-*%UM8${{fx(0pb7v literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2EF.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2EF.gif new file mode 100644 index 0000000000000000000000000000000000000000..13a0d2b80c7caa0ac92ff74a2451f74e717aef6d GIT binary patch literal 2363 zcmaizdpOhk1INF+(B`_v_3*P;Ko(u@DYg0npUchtK~m zHBEu&Rse@xl5=1-lxR&X9tQ} zmt|#$1zx1hud+lF-{T4Az&!6&sc+pm)8$I~p<{CS5VZH;Q2#OWu4b(&+G(%UizH^fmeX%0eOFATEsS4OQ3MseXM2gc@`qwFu-u< z!=Q$>Z>!pGkK#Zr6dTuNW?ZhJ!RsG*Pq67&m&;Rl(rVW|tpoB=KK1}xE|W>WE_`_t zU~u}vJw9LB**Qh^oLXL9Oi3Dv?E~9(x4c+dmqcpz_XGDtNmM0>^?ZGXRY)dx=O%tKb0?mpW)qZMiW940jFEuYC5R zO)x`c4t9U#*ulZ)vX@=MF=;X`_bAB&{BjQ5xKwz!699?MLYk7UQnrmvT{6W)E?=?T zmNxlfV;El%2zPB`o6xe>#}v?Pr$+yoT(6* z7}W&?{p%VlwlIIWLFu-ICHT~*zRK0q+a-3o3En*a^Q#o(WWST%q%y95zSaQl-2F() znYzewY2ZyypHmsnGUTKPqAoA}Jqr@bKb*clI^_?2iBuC!8hlLHIQ;_ou!Z-`1uA;n zQWbf?-8lN|m)G-Cy-{@_K?2N312Z2~5}D%9Gn$tfcK;0Y4ZQ_}!wcgRtAf6TT_mv; zZN$}ORM7698hT)?!g=CiWK83dD(;bza!ulHmBp`WD{F{P4|L_%^_ijFQO73vhxRgJ zB*~{2D+SVV2><{(QvSVvL=Px?Q+ye&&NPaJDG33CAV_ZwsbB#Ci1>X&KQw_*09rw7 zZ-HD|85l*QndeOtd1&}ZTZtRN=V|t?t#>Xp-8!cmyM@l5Dl-h{=9{hOR>WtI!L-&# zoXc}5L0pFsy1DVE#wZ_JtaTtb=$sodf3x1E@0zEMtH_hZB}a~ zwD5*-7Ak&=`@_l&(yz|i)}-h_!PAwyui=P;VEI$N+BZKz8I~D`=OP$PVYec0m`>SXDYm5InKM0ZxGQl21QYp=x56h`?393}Vv_+r2?*K@9Q+j=> z@^wwO-rBG41Z5hW@b@cCem3D~(Ktw0wXUgm(nz6S?^~I^;Ful@KP$%9Q1Y-lro2Fcx_kH<1RL4Z?fEjaw~qrg)Z9pl0{n-A(j~fnTo<$ zICnnLdt4uEop5%06Y_Sr!OgJC(^}k~4^<@txnOL)&dp`3w%zK1gzC0j!Cmh? zbYMx!EI#?S>eGu)z1JG{VRFxGHWwuCR z6wPoa-#KL=s!^C|Ygc@3dfVrY;e?q(Whl*5>9x$Lt|y*B_3bC(+X||3;Jr$=y{3`> zBeK+ku~-?ex8cX;J8iN&@Rlh1*6UC>R}O?-%&^{(FHKr|Ft1_ z^>|Kg`iqdS;hZdfNjfQHW^Qp-0Y^j_%5Sc^)$G-Blv@W}aBF*F;Gw9pZ@%WER=Qk> z54=WPg4k-qZi5R5V_ce^*pyYVSqvN`Xt9GEk>< zP*2fLuT5P~9odDZ&J>|l%jF_6yJSG~m~fb_=Y-EU@keBf8pPRO%80nGdOG&wFE_c) zB$+)s+OS&ru*;Ij_10z>@kIT}HggpS`O6K|N&Pt=?v9ng(n&;Bx@{PHSSN#}>7AxgJzb6b z=!iD%JNs575;~e~OrCqCN+2YhPr>f%amM7uwH0j6O`gm+B^?#zVo!@$p2Qu&=GkQO n)yX!Tf&8gD{=snKb8eFK>C%+VP%*N6OasPLt;o7VnZl^ z4QOCt8PjBW>=Uh()~q#IT2vxhlor-PRt1_$v{G8JR%AtCmPNDD%$f-rv8qWlDNU>i ztWXUwh=xkYLJ!2i1~jm+OxBRC@stRQko5;zR7#0rQDp5x1)8jcS(xA+qA)9@gjfjn zkOyR98*#utM8F|b0}P^}5~I)oVqgOrSY(s4ZPa+I6xkr*545P56%%rWU8q1)5i3HD zNE!h^7se4Z+#*Wk0h!oF9MA;Aa0t}^gJ^6*7Ic6Z*nlQ`B0G&XYCOWl5G4G878R5I zIBc%Yy=~Jup^e$Oik)?Wwy8VW*;OYFdj!X!MRG*jn>acwumg?H0arqJ@C2$`k*;Ua zyWS=w4aFzkjdgod(?66~{7Fbz*qt((nM>qHenE%Ny*Sn}85|Otm7P;uQtB5LS^V%@ zKu~aaL`1CHonKIpae#wELvr(SKaIrHl*e8@vm-AjJtZ!r`c%dJ-a8u{y?ija_r&q( zeF?uj$sUgfpZ=cpKtC9z3~0a~;UWtCNG2?9?dHz?mo{gfCQmNck6+w2w_fXA?jP&$ ze}6LNM&!WJ3!6GTWA*V|QA_;{B7Dg_^vUr`g`Eg#zJOPcay*GY0^^V?5no&v1@Y^;rCu&S)T6e z9EqqaYMXu1(D?IT{ma(hL*7QukZ<~|?|IWkYI9wW|CYtB+QDI8e)DYTqXAFV*YT-< jjrK1+k~r47;vFtbkK&1`#o4aTnX1|!9hGl_0;%vnkz?8v literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F1.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F1.gif new file mode 100644 index 0000000000000000000000000000000000000000..d04c68d0f142d6a910dd7340c2d67347c011c4a9 GIT binary patch literal 1902 zcmZXTX*k>Y7RP@Hu>>8V);m*)YALmq4n{X?#84@v##TW!)}p<4WV#V*tEDs zw%VcyO&LjJFYPU6EMwPLD-{VrNdA*~?u+U3-1Fi*=gsFi=lOom7mIVYwjrnh6|eyT zal9IfMfG3x8<+jrT4=87A-Xpx?NBb#)Nx3Mi0 zLr?^OI2;BZ_LDyN{5*XzTAQ8|yL55?yq{E*jocVPvGZwVuZ(O5nXIp`zm@v^`@R8s za|?$(_n9C?EiACGwEVKlC!KYEeQv+l+#KiX`s_Y6(bqb}5_!ShaIn3-=!kNd8K^1F zoS9pKqT~R8g&9lz$8B$SPj5w}ZDj#D@|bEf4)E4DxI)p#v6*RI-D`&t*u#e7Q_~fX z9tZ>iD^&y(i|qdJv4L`h8?H1o@@JqKSXo&SL?MsZ+KNP?^_iJ?Z`+-nosD7E`VrgB zrsgeYY?uu)-EBV5&|`~v;<;klK{4a!n!9#gM))*_MqJZmK5{@jYi9j z^-x73h=-AEu3*p~+2pS-*c7&RbdC4`c82SED6F$2W!G+v?Q^hEKLDKt*%8=@KR&W7 z6*<+nN9&5Zz0Nczd3Llk&~h&>Ob*vSuc$nuQAqNKqT|N#_~p>hZSR9E<@dK3RNjR% zo#!#DtE=m4d`9~DwTP(M`B}!xq7kgih9`cJ;?IGgca7Bvo)$yX?E2E&#f61hXA_7( z_|VDP;c(_nP1B-IGMUV#J$ojLW8ah)a;65EbUL)O^ssuBovJdw*Zi&!vF3VNa3W@X)gymywf2QGbtDV0J9~HI0PVYP%=wGq5^;% z1T+@-tuB-oh-+&~0kx%+l2iphuF*`{qz2l<1N2K?x5JYB7|PTEyN#Z=df#ZNob9IiAnyCo+B%QP*^ z>d)&{n^2EP6!*g#Lu9Lk_n)CUEL8WG#FU$vpi!yUO0ySuN0@;O6n$aS@yBHkkZkoc z^VCB63cl3hdTd^&5i?mo;F7P!uk1AKti?~5X%EuPfH=GFiTD~axOq^QndAa)4X0+p z05fp+7pW5`{t*@db^!$-EfLlTK=M0QSafYNys%F$yu#fkRh)QbyWp{(j)9&yX0`S` z*rgMy9I;xv5B)VSFX81SeDL2qHtL~Wa^13snZRXQD33qNF~7C%?eRk}#W-MI;in=6 zuOrs5-ZH7*;@HF!x2NvWzBQZGBMh~$+7`_6=GEQt`&wQ;2HUAwdkvtNfd$ zW8Q7iBO1#8EdBZ$Akn~+>ULFYkzlNQ=3uH08IIr?dA`WGZ*V#Kx7OMzN7OYG!o<2@ zZs-j_#|7BUm*w2fa4Ko@N_mKFG6-l}T)m!>He@fnuoW_rwgC63p=OFJvkn z^XP#$srVOk5(?%l6D0CM+$FR|GLD<}#}%Kca8|6-O+BUA=b>C^qJyWpqd6r%$q~7I zc-apSYv{QK#tjO(#kqbDzs$9a0V`mvhtcn!zZ z{c6tdsh-~-=}wPMEAP5ykI=Ko2ClK4~!)`@X5?=teu+uF7? zQ(SV>4@>pi#1B=6FwTc25Gnb3lTy-=A1H7^Z-{}`AC=v~h0=Wrh^RT>uPi%JXC+e9 z8=((i8H#4w1`6@qulb8<==E4cu36 z`aj2ICFfg9^8YWFnum5*C9(JKsws82X1sRvURNC2DN8uKBh~uqac3T6Z>Wu{E$hC8 R!96{lnXpfu0)qkDzW^Aync@Hd literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F2.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F2.gif new file mode 100644 index 0000000000000000000000000000000000000000..402dfce7324969ee374295826317a9fdbc4aef81 GIT binary patch literal 1092 zcmW+#ZA^|~6h5!s_kF5iWLWend z8>5U7W7rt7nbt-tqs3@8nru;Nqm)r%6dOghSF{mjM2xTzvZJgGE5l-#4HJ+IX+z48 z7-B<6fDLG1VHn+H_t;Qci`J|)*|o@&(n_>qt;n)KQIrp76SShmluojt!5>`T1E?A(*qL>vE)Pody z6^SBN1bOfSG%*c3pdTcl5Uc?P(qM^J@W5hV0~%OlowH@+2o@o0B=lh|GG<|duaFBC zC?L*tqj2fG8~u8tbhmY94xJ`i4Bl+pOc-lFg_tbKEKEBq)wkfK40R531>o~JBufMEzan#F3lV!F>P&l`eXaN9tfo=;DA3Ojwl+U_w;91C(YdHi>Vqvx$Mo2%GPlU_ceY?tS^tf zzv1@bnxck-pZ~0hOiV3EuL|EO{n|41*rG$9ukIh+xqn5*y1&hZ^IHZb)R%ocSMzT7 z!HyeiyZ*eLpFF<(Vp;gy-3wJ`6K0pbs$Fxtq^TozQ{>0vBiZZjdeT1Bu6nntYYbIJ z^2k%S>UbvgKa=pTX^E7QN1vYerkA&Ga9Z20bT$>uJJI(y(3qb3Eo*I0ee>&3Qz*El zKmW_ihr>IDZR;r58rgfkF8}(k20GdD{mtcypXAM7I=KC2WkK=N(N9Xl?y)RM7afNJDCZA6$y7>dCAAV-u~uZ7QfVo*6k4*DWM+lZLTVwjU@ge3veGOy3(c&VfTT%jlA44j z)`SAsfCd(pF-?ZYJgJmWij^Wmi%Lo*g_5i!85L*>sf191l^`Puvn&-0#jKd15vz)% zBB6*CffcF&2GLLnS?GZn*nkEWmdO}0HJ*@?r6l7Iw5WuTf~6p17b?(XA(kGv*}96EwSqROTiKaaD-Lr6%b`KCM4OvfIt;J{jn4sBLU`~5 zs!5kS;BxmnVn<`$y$)B2ExbHpbyeh=Kw`>#+NPuOk3|Cs;n zVCVIqLo+Fflg>RAe|C9_f80{7XNRAi?tADP>Nx(-KA%!%ceI@gw0s>X_Z6s0f7gxP z>{UnF%V!TyxQ}@oGD{)>wf@wI*ase8@m?+g{D*WDawFZ=xbbm-P?nT0rX^)mFGXGm;slu+6q}hr7b_@AEKoX_ z1?hu9l0m7pX$46{$Fj&a)W{%E35{%7OL0%%`On)hGv96gzhGN__J#usDUPOzw0TNv zskP9WwI)+3rIpl5XvJEQX-cJ~)KX~4T9TO+N(-rl(1Nufv&u@d)GRc!W&)BXrAcZM znphJGU;`RhSjIFN9`mG9LMc{?3@s`tl@v;{l4Mk%DWnoY308uPD9o}{EEKb1f<~+= zl8S^PRs>e41{g#`C1jxoVqgOrSXd@w$kcd3N|ut0KhUBQLJF3Gj9sWelZ9lKOmGiT zn3W_Xu_V|-9*~J`!~y>h0f$fxFo=dqj6w&9femP2kx9<9QRA@?WP*f0(4t}%Ovn{> zp#n`oEC@LwX#@aW7)Q`>iztx?WMUg}KobnZAyfklqOl2C&;ep#1Dec<%rx4l@dy_~ zknjguR7~dMFu7XyHcjh!PJ4Jnm?OOZeSc|bS$B6&d;3FYWYoO*(Fuu3Ny#be)@Se9y{GbI)%ltW6lS;C z9nmqd@r#!%Tkdi=Bgtk9v)g0i;?grR*0|ky`S~j|Gmm&Ycbl8X#>YaT(A?bI?CfkH z5EvdF9vmF(?d@%CZN;ab`p;p5QCbBJWc~??gz35X>D7%dYMi5&YbxCdPmg?QPhFH9 z6MzuIo`X6@= zpEy=s-IlP~qd&EcU0RUnD?j%mV#{>J#a!>7K)tVFqBtijr?13S^{z1Yf6iYTwJw nJ!PHWoe%nW^xB=CpTUlSc`IuFzB)7FejVXTo*a60mU8|B_znj{ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F5.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F5.gif new file mode 100644 index 0000000000000000000000000000000000000000..bed6e7191672c24bfc573ebdbc8bc51e2d5fb6cc GIT binary patch literal 954 zcmW+#T}V_>5S~h;NLNbwK`V@qUMh+SOtWMQ(oA~mrD998*>!W33Y0a=qz8-Ah=@29 zn<+>Ul~f><5kgAjp{O(nMCzdjGYfau#4u0a-19cf%=b6<7Vq7&Ex&vz`DmKR>9fum z=fpX7jvoY%g&NEjdjL2BhIihq_bk37$@R{osh1wb!;4qV|GkHa%3GDN8*Sb zp#V0ZfrVvElk(^@){3=kEh#N3W2_Nt*cwt5Xo|5SR@e%uD9p04ESA|aK_gZz8B1b` zErAuP0S3`f30dfY7}$UY7M4j3X^khwurZ|mK#NMm2pb`_3l(Ux7-qu+_Yj3yLt=;x z!5;E}Ol%_#_=gBMgld36G*n^~IzS9;Km&_3Ic=lHV-eCI;SaQ^n1uhy$8n7!IKtU=WQ>$bt?K0~^q!C(>!OQR5LVh9Kb&w5XW$ zp~mc8MQ6RBHFYmLUG?J7BX|xK$rG(N@pLG#2aV4GS3-F31ghy!_)jRD zZ0|^1Zc8*YCL}ajDZ`~@AN{+Z75QUZ_+-YCnw416*}JXkdquAZ$u@wK`par#WMsd=pN?9|0e zM;50i@7%?wzjh{N9*oikXdwM3XyFYS{FhzQ-QL$UfAIQveRun==9EuYtBN0W^w(9s zTRqzq$$k~d@yD;V{K_e8`rAAFH265s6a6_585)QL@}lwQ(ec#%UH-O_-tx>9n{Exp z_`Gjp!%(oVb}qgs_vXa+g4Wb8&9~ofjTKx-ot$~lGZe_HAIq9s@Z|UCj*jHW{fc|b b-@K^(@%rJumID(-r_Q}^?L4$OhqnI*)&$fD literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F6.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F6.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9b885f773fd6b69dafa29007fabb299614f0abc GIT binary patch literal 1030 zcmW+#SxA*p5FRN|A1v&p!X=_ah$3uo%SgdU3QSt?#f73cJ!x8MRvL(DL==IzjQ46; z57G(}*-lU-AxBH7Of;zsuh>F#y;-(Q-}%qmFf-pa|DUsEb6WbIMYND+iPSx-Dyl}6 zRmoM>sui_HEvqHh6ssaCMuk<#%^Isl)EG6ahTJM!Wl=WDtV}>svPz<4lvoJ`umKG$ zEMuA+k9%URv6ihRM~h0tim}30$f-cnh&9F==EQXw2s6dmAFbfmhLlkC(j1UXK9`b-p zY$Fc%hX^=?YJfpBRALl5Kn!d^1B+a8u8kUxjUg8#{DBq~vtdH6unQGv8e&7p5lJHe z=)yRHhFe65JRlR>hy$8n7!IKtU=WQ>$bt?K0~^rfPUNQ1MvX_f7=na9(4t~;ABW4; zySHn4Cv-7;S8=mm&@Oc^JGbh^;f~-rbV#0PcN0&C1NNZtIp9hN51v33YHPpB$@^C( zjOOhK6c&F!Q1@7R@`GIILNOxGn}O>J~|L=q=uXJziq%DULl zP?(>OPk;HZ`C%|htD%A1KS2=_q>-_Ao}bS=9{LegnD%1X#$&T%M?SUejn8bhEwlg&HFAJwjV z+)g2VZ4(myV@W*R8xVF*Kqo~+M_9nWnH3!{wxC|U<^ literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F7.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F7.gif new file mode 100644 index 0000000000000000000000000000000000000000..5bdcb643277b2eee6fff38ff7d988478ff3e9f2a GIT binary patch literal 1006 zcmW+#TS$~q5S~C#2@5PLO3Hdj$wiO>&9)0FBpDVY*+Ya`GS+4qT|luOl0v$uL=haY zpa{YqEXoJjpb|q%k75$~kfKkOP-MF7ZVUTQ-}%qmFf-q6{=fXd{+-3OnUqN}B6ZKI z8dXtcRdSWJYK>Y^%WBCrja6gRh#FQyZdR;{Q4tkZA-Bp_*(i%LD-)2Etdda@C00TK zY(N7G%a|s|J1TOlV3vurGjWwuPvh*e9*l2~F( zV1;UcK{QlC7J48CHlTrpWpajGjVH#iG35M#7L|w*HbTxWRG`UXm<E6K05Pxu4J>lWxi)G%79kfT{DBq~voIl7*o6u-g;)qV zB54EwT^L8uaEmCB2V`O!aX=Fc!y!}y45G0KS+-3>)Jcrlrw!kXQ{Pd`AT5vboSrl z8}aFH{g<)=>=b|o{1KT%X``g+S-Fd|!M3zD;WzCsPn5RJ_NUhkchwYLp9yb`R3Gjh zYWQwX{Mz38xTtOB9e{U(isC_q=a}I4AXj)Eu9<1)oDr`x9zt-BHG4t+HBJ${4 zYU8HT#$WlTg6o@R!_7ZlB}a$GXZJNn;%3va{JHSupI;a9ipI0vw=T}#TXS$K5bito zZD}y|PtDtgBRwyw+K=X)Y)`qfxiZ|hP}aSxC6Tft(o^v;zjox=*{baFuH$8$ftJjt XmABUwgs$dZs4bgXo$-2A8U_CYZ3+E0 literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F8.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F8.gif new file mode 100644 index 0000000000000000000000000000000000000000..629016b2bf2087c93387fbfaa3ec0533a571c270 GIT binary patch literal 1016 zcmW+#TS$~q5FRtSAVEoxkQ#x#*rL@$cPlcL5bdF;m*|2v1-X?Gno-0CMYTl{M4oIb z3aW=njLK_eFQ#(XF5VTD#4OSXwN_Ss_w=3rybUw+-RA$Zwq>qadmxr#X@bbur;Sm@ zh%syo*-C4pmC<4}8%?&Uv{A|^F^Y{MJ1g3VG9pIU2-#KEhLvG4%!Ua_hO{ANNDQ$d z6u<^Fu&|72vOM-Ftwn3rnk+3UrL+>QSSzwB&=jRbw6GSkqA<%!vuI|`1dUkLq%?^p z)&y3l1{g#`C1jxoVqgOrSXd@&$kuqG6e~s6A81jDC}AaJ?Lq~bEQ(n%!97G_R*@)T zMX-lFAQRh&1O6cb4xt)g5Dk?Wg$@t{8_>Wao1AT<#$yq(LBbzsQ85b>a)n)}KvRf? zkRy^t0MLbT1P!-{5_v!-wh;$3!7v;`HNYSmn~((^AO<#|$)3ngqm3Gma4`f4f1pLh zWIqm@t9x(TbWdnwcCTV*-JosiZgzInjl&+nb!d@X(e@^;4h!r;<8#235FR{%%9#u% z9jRY>=p}n!W#nI3SMo0TL`~MUhMkYP?lld0{0-IJe|EecEc_Ji9(T@F)?R;GQ>W{k zNVv7VJ`(Bh`_HzvJRKYTI6l_uOlmXrCG@v>BJ?fd47{!M4|n-Sf7VV7UFxXH3$+!$ zR=Zzb-g+}{2|oSB;ptH@N*-vy9}!D5tBuU;__(Km4@JHi$w{5TgbhU_ebvJQeG_OCDa)!F4V z9=*7+bfRw7prme2EAdoR3>UAw|IXi6+T7E8E0A5fEBfGg=y^g+@!^*4g-u%)ZA!~) z3Y7U%dTUOe`VkDI2g}~%exG)mXV%}XE<7Jy{57<_XZ7(L<5zbaN{P>J?2k*GU*+9X pi-b{M+IkTv40ZJ14$6cfffZSumH@{|B#84B!9& literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2F9.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2F9.gif new file mode 100644 index 0000000000000000000000000000000000000000..f8b41da66e072270790bccf9d4d7fbfc6b40bf49 GIT binary patch literal 1051 zcmW+#YfO(}6hH24ME_U0b&*TWJssuJu-Li`%@{T_G<9Tzgo*LReP(@V%$eJK$$V(d z+#=eDg`Q1iX#1lnce?!NF5BbxywA6@bI$K}-ZvvFea6h300rn5k#)~ltJaFOY%RIU z7;DrTv4*W7*HmLwt%_B)N^Vw+6}2K(*b2E-HkQ@0SZ2!vBumDUS`tfa2?ekL4J<5U znjDXNYK$1e#*m{$rD{}+vQcs>&=fTyM%V~BQJ7^lEQZ-IK_gZTsUb1MhQJEd0E1|# zge>$x3~WFH3(MpTxf)MYS(ThW(4rDiVHI+Ap#n`7WmYD*hbYV{i4rTp9`b-pY$Fc% zhX^=?YJfpBRALl5Kn!d^1B+a8u8kUxMaTsSf1pLhEKJB1cA)}IAr?Z8NE!h^7se4Z z+#*Wk0h!oF9MA;Aa0t}^gJ^6*7Ic6Z*nlQ?A~%gTYCOWl5G4G878R5GI9#s&yxm7<7cLd*|L-IwtoA^2$un&#T0arqJ@C2&7Wba#POli)P154u@ zx5X5u4d0zLvUyj0UW&Ia*(*qm%un%hrv)U%%Sj64CVQ(A0~=;U)aQ>|J~906nuJTs zMlVVTUpqB&Q&Pm~g`n^tj@rVW+0Y z77g?8>Bs%)*$qZ%A~fKS2oUxCO}1ZDX4vVp{XM++F)KPp&O6w>t~mI-;LL-{9qlj6YYv6p%q@y~Q2um%#p>C6{??!1BiU8ky_E#e&NgY^2?R?-fh2L_jvN!D?KTxzIckKAtGa+HbxmE z#;`GDE3J)IMvKvGG})%oMk%AjC^m}htY{<3h!|lbWLH@mR))ne8zvwb(uR~FF~o*Y z02|Q2!ZN1G^4O=e7Oh!pvb3m_(n_>qt;niCQ<={gvYuxLqM^bU!JHNe zKFtm^`LjEA(Bw8PokP*P(?A zcPA}6?eSb(zaF3dwBMsfz$ndu2K*86L}Orj?9|EW=iR&eJL57_=0|HnZyPQ?@|;Xq zesEBi|EWo_Xr%1lhupuN&u48Y?T-11Uk+rr_x3!zGjeo+D||V6 z>sZ>*cURK2S5;?E9cd0Gmwn##_^Nl_wu-4=B7v@?ij45SH?b?deOoeXiw0k}_E)A? z-@X6${o$TpN4q11tBYgzD$}BoQqyqzW@B9UsuS-%TtD8IR$X7d^-H^uvPQv3b|-I0PfZ6#fo#{GP9a46Q)_;J5) H8M*%hFuEma literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2FB.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2FB.gif new file mode 100644 index 0000000000000000000000000000000000000000..620d898f844a03fbbec2431de20dc96ede6e95c2 GIT binary patch literal 1012 zcmW+#YiP}37=Ko3gzY$!6VBNfIhfl#jWL%cjVUws#Wm)`Gm6<{CY#)iTnce|DfvKF zi(1GqLpcZa>NKm}WSV3yhp3gy2an(Tf4@CF&-1%I|NoN0g4{W!e)7{VB6ZKI8dXtc zRdSWJYK>Y^%WBCrja6gRh#FQyZdR;{Q4tkZA-Bp_*(i%LD-)2Etdda@C00TKY(N7G z%a|s|J1TOlV3vurGjWwuPvh*e9*l2~F(V1;Uc zK{QlC7J48CHlTrpWpajGjVH#iG35M#7L|w*HbTxWRG`UXm<E6K05Pxu4J>lWxi)G%79kfT{DBq~voIl7*o6u-g;)qVB54Ew zT^L8uaEmCB2V`O!aX=Fc!y!}y45G0KSJIQhetZ6Eg?Xnz)s-fP-_@X+0+`%j{=?}^WswuUd3 zhU(U2))r?}6$Hz32W`q3z9lbEe`06xgdr=ZjwlNI7iRdoJKE=t8aC6HydX0;Bh5F( zmpUsHtg5WUr$6@3fFu~DDbRpF!cR1?m2^;W>5S1&4-eF?hUe2u7u6&>TlrJPn8npU z$FJJ8Cp*31bg=7b!;PZQyuD*mci-u2O>aKcKk6@+rL24y`O?|@rmrXz+cWeg9^NAJnL^Sv$m|GhYS{#SXZx%PK=2K03 zGAI(4R5aI8X#o#ZS!dA$wvazg|#WGtaAXze&)RI_YODKR1 zXkcL()8u&EQ)9#!HijH6DpjLml#P;8fu^VtF~Ua3iNY+aVKK~x2^z6#NDYZ0HUw6v z1{g#`C1jxoVqgOrSXd@!$klkF%Btl2ffkjB3agN_3l(UxD6=xbJw#zvNt9R#_K*i; zVjFS5KSaPGR09m6p%SCe0b*bS8d&6#b8XakEJ7|w_ya8}W?@3EunQGv3b7D!MA8TV zx-gEQ;TBOM56Hwe;(#U?hC`?Z7(`tA6S--$QR5LVh9Kb&w5XWe$Ki7I z?(Lf130=(IRotu>v`gK~&aHZJxFdKD9g-*7-Ne)3fIVn@4!9D+gC|hsWFDS6)Bf_x zla`xptsO(xn_5e61qwSKXZJtY{;ZaV1Brb#$$Rr(|96HW&Id6!sx@jwEvqHhG**pKBWhR;xmmF)MnzOuh1@DzWuq+0tV}>svPwotlvoJ`umKG$ zEMuA+k9)>iv6ihRM~liBYs4D1hMWpC#aIz5Y=xXC%(Ag8mf12vBUUXLOJa#FffcF& z2GLLnS?GZn*nkEWmdP1%HJ%v5#*p&|T2vxN*a$hhP=O|kVKz)~4^fykB!<`!>>&@x z#5Ur9e~5rXs0J8BLnTI`1H`}vG_c4e=h~?8ScF`V@CRB{%)*3RVHYaU6k;Ldh@=q! zbYUDp!!4pj9*~J`!~sn(42Mt+Fo?z`WI+drfemPKCvwwhqsAj#3_-#lXi+h_kHh8a z-P<+26S|natGHP&XqUQ|om=(da7XYQIwViDyNRd60ejH+9B?Is2T!1SkdoST=twX% zZ8SaWTSi{v=B&qyR}OD2?%lqxds9hk!Qo?p<}HEx!d8Fm#XXnW3+t|z-FURQ=Xsod zpvg}O6NBa274q*{Xlp~pWLtRut%S*@$iH>-{+wAbad1iVj--I_$lkTpnQ1Q%?EaaT z-oG<@BqtM}e$1cH5E!L6Xuuy4K{Wjy>5Qn~C391bPTUF4jZMf+tO&llTfHDYs^fU( zz}5P!+TNu7-EoUQ`lk9jihe!3e0u)5zUGRd@7XcFU3o1>f&;}l1GWAsrC*-<%Y3!b zul=EIw>Qwtn%SknufsXx8+%eK!+OW6&&O|TZ5-??xW7Kou%hPYtFsH=r$?`<{MI#g z)Au7Ud13LQQ^C|LxrvpLLxW-E8Tm_#vO3>hJMkp9psF|dO~=TE&n+)}2lC})$!L3L M-Lxv1GmDo02g_&dJ^%m! literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/2FE.gif b/mapbox-gl/closure-library/closure/goog/demos/emoji/2FE.gif new file mode 100644 index 0000000000000000000000000000000000000000..a8888c57ca9794e6c41fa59429a890568d324533 GIT binary patch literal 1036 zcmW+#ZAhJU6hE;+?L&A8ieQ^~wIHD{;}Cki8E)pxUc|C7Y9ejasbD!-?F~~9Vz+&8 zC2ZIUB~oUieHdGogN-(N8{}cRIpJ>Zy6Hl_IUTvxaN0fn{?Gs0;hgh(JI_;Hv#~U> z^%08EJt7a!+|^y&*_}dV=B94q#%>gvYOd-kuIx%-R?J0R#D!fbtg<<)vpBOe0m;dn z)JdG!2?ekL4J<5UngUOFY9?lEMu8TUs;QW=DFqd1ikgTCn@|viSyp2)W@CaztQx72 z7_kvpp&DQi4V93E9*BVrXkcNPf}v33i7Km7@CRB{A}Xvx!7fyw$)e241oseyStU_o zCD=nAkcn-?0sjyIhfobLh=xjxLI;R}4QOCdNG`NdZAmIFXe8ekBOO~`@{5Ca>~6iyVT(MF9&xEO+jKhUCL z3Li(vHM)0bMkfq0M^_26QP3gvD7&y4#SxAWISfdV=x~!rM*xnX@j2j12oIh>^`Gg| zy`ziMow4)rg@1w`%95{IbM!(cxo=_N;f5E6Gtaif;`4XT z*S)oVPuaDLeW|}Q&+q^1a>s0c$Lg|={ycZMvg`K64ZE^GyfgCH!8M7M>7Uvw2R|z) z)A8nQtG~Q6R=TUaYWL7oM^BdxCwv_11~?e@l&&81;xL+Jxy#^wz1Rv5``F4^2>r7tI&np^K)S<}`RD+|qs*J0%L zfo96aP*#COxcwDtT+=j5fe5czsk;7jm4Oa2}nlHNR7mZjZgp^(7?hn zrpfd8r_PCU>>PPoRH{zJDLW;v0!>jT;)I=$7lm0?$KseB6EtGgkvbAb>f{7b?(XQD$X=dx*lUk|?nf>>&@x#5Ur9 ze~5rXs0J8BLnTI`1H`}vG_c4g=i8|9ScH6#@CRB{%)*3RVHYaU6k;Ldh@=q!bYUDp z!!4pj9*~J`!~sn(42Mt+Fo?z`WI+drfemQ#C-T#1qsAj#3_-#lXi+iwkHhC0+}k&U z6Z)8gtN7U2hg!Z{GfmpQBrbUlb44Ul@LU>DSft z?`K#2s5sVAQTSD}UY^T-n>()|oKl;Sa6f&{?Y)T=Y4JB#$CW3|(xq|d7sr+?n2{eh z1)qL=|ELi#N(-QY{69fs+Q`PvNUXe)T<~{b{J!;7S5i(6_O9-UmR@^S)SH+1Y1h_M z$tC03ULEW!ZC)1n@$UBIw!4|BMO}|`M`mxC9m;8qss5eTbb0gP_NseNipRHA=O?9= zG*3Nq;LY{c=l62z8fQMr{a9anZ2pvtN5|)dtJhR@)-TCxeOr+ld9`WdiJcGgLkWvA zo<_Pl>O;|mchV1cb#ymGeqO7IbbflUWA~xh_Au$=)|Stjt_lI6P3+ zos|DMrQqV9aBpO5?9*`jm*fdGjYro;2lMVvuM2h86ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW3IP8AEC2ui01^Na000Od0RIUbNU)&6g9sBUT*$DY!-o(fN}Ncs MqQ#3CF$M$xI|l!d&j0`b literal 0 HcmV?d00001 diff --git a/mapbox-gl/closure-library/closure/goog/demos/emoji/sprite.png b/mapbox-gl/closure-library/closure/goog/demos/emoji/sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..f16efa97584603a2d97f14c5d638be4b96aea5e3 GIT binary patch literal 25195 zcmV*EKx@B=P)Ub~C%# zgalvz&u^aRFn8{~v)TLMd(OFcc6K&To+w*VQj%baiHT|@OO=xI&OcxBR+n zy(i{A9MhWsB1$?lZ@w(O>AzZ*m&5f+={ZT4FOPWc=T&OSR}Gp7`!%I23+nmn&a$$x zMpLc#Gx}x+&dPh5$l(tM%8@mLH(lI?4TWdzjvj!z{VQ1c0INMV~g|4(TRoSg{Ix5;SFn~+q|vPd&WA~49*YjyU3pZ^p>3sE6J9GDhfzalH}W_ zJ+;lxfCtR1!uj)!ehLWp+VfyH+4#<7lH&ZWX}#klPx~c53g!pMTOSS3X=j|7>6_Z! zrJNjHah}N6r^%PERFx}w-y_T0_wzjuH2`L3wIXUe;wrcb`aUjReykoB(Y~q{dz=Ce zK>l3VLH0lYA1$*7*4}sehXOLS_yLNfBy+RO0J(6!%q#NQnFnF(ybUBkL##Tq!vzj~cEurbThr@)!VvFt=+v_r;S^ zQbXFpx8&qxA*)4;LoS?6c_nRKJ~berB;1Xdqy> zshgFn7||jDdSqBdF?)DOf{?k0Kg@d`9T{*hBvh%F~N1MuPRkl1#yi1 zUbcdGvzE?%^5xaQjB zye@`vP{pCohU&zR?8H2W7?YZstYB~_`rv92-2?yt+FT`0fy~TbNX-w>DQD%Ei#wN; zO|MS#w{A&XW30g69>YEV!3n+F6?P!NWiynwu$x|*XT+$}S=@^1X^Gr(}8o)|On>3cHVeIUt4YvyLgQZN&*hb@dLz$O92L>%uL#E9)yx` zH@{S#-Hj}Ee11vIW~oWh7p_e5h(oN($y^K`I6wjrb-=(-5awOUbFNMl{bEuiI9^vn zHOzlBi}|d@aJN4@#OD~C41i)-*)u?v3%ClY-qJmp3iFnMLOXpq#}|&d8Sy7mNZvFb zVX=BsofwFoXcyRR6rkn^pPq_k1JP~n~Po`WzRZC!r^d{>G`F#6%b`0NmwuqhhlE!0&-kM z)xco)Is@N6LV@fk<4hWh#s~JEGHvwHzMSKn%WIr;K)?U-hse>RvesFOpduid0SiEY z0T7nzOe8s$YAqmR1V{|caoN|cr- zO2^4DXHteWJ@BVZb1~2gkMBMptsO;*Jq!9K@Y7>A%ym5>&bqdJoLtl+Y|EqxPV;LZQcijMatJAy? zd#(LUnOw4`?w2%AQA!P~NFNGd4g@K;Bueny&%xUjwMx$d?5`@Ai9AJ{j|Z8eo706aWFn z>_h=sHv88B0*p9u2PnYux)@;0#j*k@+{Ks>{%c(fD8Ru!zw<3gbpkU$3=pcKE+#-- z1As{H2mc0|dFEyA&>qIdIK+Oj%Q59L-K|KI71(|wMTBpd>Z5BwK zq03?e#9Zgl==2%01Ix`~xe18%)wzAE$~ip}LoCV3NVnd0n~WMgM)#^9XUhzb-T~qa z8f9GC?lCn$03fsPlMZKIvoax39xhoXDxmO< zxlZ_eC(`zB1>~v?W3hUlqoO@=6=MQq0X!x^wpi75*f3pI0B8Sa149?3&a8R=y8{Ak zP9MDjz&Sa+Sk3mmJr0zt0E$^b-pd_F#%VBiFKyUm4bSC`z@?AiWslsr+;Jjq2N*2; zf*NWu*}wp-ua{$KrUNEY2MAZceMe0;oG$Q8RIV_gf>T@|zmZhy|@H2?%0 zAW`4@eZ6dCBgQF7qx!Ln~bciDS?XW6&3uN?gE@BpCShH2gIku7A;&F!?${`UsxIl$sI z(g7vAfPfJfcZGrhkg26iu`3l$vNM%n&oTXCQP8dyCFJ<3v(*kxD;g6h1`nK< zI#-rouCFOyO~`1}@3=eOeZ4P+)|Eh7O`9P5-@aIOBqqv^V)3%0WP)rzudD(A!oi=M zblbVE+opTRtla>ai*+sATUN!_(7nOI#Q;%0{&H1M-vKT3sv~=jsRvT{zz$3(3@X?Y2 za#XUbSA8A4D#TF_K!9XQ(iyGOj(RWyM*F@Q4WxXD0Xh*(O-Yg6(Bkk~kTRTd^{i4CyG&Dgzxulw^ zC=#H3uUsNu-_Sx(4uG&c3=Y>qJv*D!NOv0t0)X~RY8_AzfXY^I+ff|txZeKRjZ`ta zO2^C3BSxuF-c~L_2VN@fN;eRjYoVT3U#+Hlb2Tp@+=!bWXkz2;B-Bc6+w058Z+SR>YAQ-2O)fnRo$O+2Y?gI$r zc+FG{Nd0O7kcSimcuCp<>-s&2NbmCEIq~u z$~zkeZYppAZSe;L992U~vVOJ)Ak>7LQLk$87;0i~fFAChm=0)X?Wzg@H&f%-i7_r9 zM_nA#X2D$Bjs(^|^%MT1Dnd0t0nQUeFVN$!%U05WT_W)cbt3>(C=&?Q#4hGT7NtWA4V_ z3V{V}V4)n~Flhu9;Cul&5P*?kUgSUno}Yr&Cf1kNd$d&dLOISqviYHF6c9=w7>65h z2;^kCyXuveA@vHMfQtpQp1Bx+yea~WsffKnBIW?eKp7lhWdJnU1E@s+5blMWnakmR zxFHirt#jNHaPw-G(?L#e9VgF?=&vru-cr4vaSnE4{SpB$D z2LonaW2(Unk}1pF&ER;AIxiqH8S3x?()a8HYIQ9qaJxa=3Dm$0pA#|A;Wf@xySi3E z9X#Otj5gegb4JuJ;seN;+c0?CCL*8+Y2Le>AafC@y|KuvB+G6n4p(CmS)sutk{EZH zgii+bW>5zJDLn+<*b=Iqv@i0yfgGTbCW|NpH5mYdV8$_R1ZH4m4^V`OB7khhY;zs} zSVluN7?3Q}Vbl?~2-tpWF(!XZ0#OE&!tg;GBvsCJ(Pz`d9pVcriq?<74xse4SR_C} zg}8u7>nt(2D>k^?@Gdm~WSIy^sKJyJp4l*tX#s{pzdbJT;I0EmHlAz4f^MVW=e ziL_WHD5E51(*W^H`LS%cSS*)Ah(#GLcW@blMQiva4qfSUOk0}gGDrC3j^KjSJ7CI| z{>g3@m&~=ilgDU)cD0O`gLfBEcTz}Btno|xxwhrV1+yR^@5L`ul1%|jO$I0Y!rIBW zV3y^r!}V`VjUEf7X3xi5<2bS1BRTqJ(>@&{SZ4jsI`cefkx)VJjm(!nQSuk77Gl)X zCzik2ta&qO*REa2S1B;+XtQ>!_kEGR53ZB`53digzBAWJ*V{gj=3|yi<&G&Q3@UBi zFgbKzGdc7?3z08IMt!}y1q+F@NYj~bN=mBKZ1=Qltu9_FZ@;}+rj$<*`UOErBsh26 zd8bYg4a9YmCb)Yp>|vBUb?)r8)ue!IzXv~4P)P%x&Jv`wwaJpU zZn%4nd_Ge4O{}ga8%9YwxZy%y@40c#5@F@FbL4QBuHuYNfdD3<;e*nk$0Aww(mOJ{ za1nXu)~PaZ{EJd5p}#6E{5>#e-~fSva||%bmt1V^>Lh610+L$4wtr;cP}GKo&oIqwcLd zpb!0laRU~h`px=GLG`+Ct;VO!-D$WEe9t8%<fW=bp2N8@xy$f3x2f9!ihy3e#e$08hwae<{}2D$3>k3aK3(0j=Xoj3kL%XfB?raO3D%-wm~ocpl(KNs@>=D*j&wt zJAXgnfViQK{4lMlY#EcNMwvR==o5Y(8H|A*xOS4Sy?5C_*TsNAUt$d|2~bpp3VE$7 z1`X1|ntE`jRgX`V@27Ng4Jg#nW-8)5EM`E&CChGPwrK7}K(;u8S^)Cnt#NX2)dgyl zsiV!}6X%fW+$`oOU{BSWzBa!?q5=XGL44!$FDW465b+4t1W?7YB{U-uPYewEKUd9C z#h@gNGIg|J^G99eW;0$RW=I5c0Cl|S9p96Ve=h%>1UTYzzmDJ3aMKXM|tlkp7D z#}oTYhbBo15Ert*Oj$mk7qDPGVD`LptL{06bF&2Kis!yl7XwgS;|IhK5J5OHppj7l zF-PJx7#xE{2@IGxgHj08@6K4@#vo#kLUY^>K*S&e3giYAOKAXRz^M)d2H&DVHHC$m z4A9n>rfIyP9vA!kmce=s=kgj^ibH)K2mmTk>n`6DnFOlK__>$^2J>|>EST*qi`k@* z7y}$)40YZ#&t@?Q*F#abCvJM)Ip^r75`dAt@2fj8U?7>B0f90~pnobU zPz#1~00tma5oQ?Loszj+&cCy*zS=5Y@lxdR6` zGB{pUdDFZG>0k`vnD(%Bm3;8d`~alXl=S%H>@~eR;OrP+#GX7ceC12Go_Bw$P%umL zn9Yg&7PKciQN%q6!Wff3vXj?POR_Tnn@M9VJBUH32$11R50)Fa832eeK){_a%G8na zX-`hd;_Z6|#f)^{Y07&M7qKV0)X2&`lcQG!bAagSS00kgiVN7-U8!Vd_ zWhpR+L+bxfbT~P&Sj{RZGa!-ea7$+6;T@R(0zzHQaVb-fx|ri$z{#?D!Y_-kvd_-t zkE~qo$nHk=v#f`k`oUy=W0OUTI|OIadbX7FPh2oN5jCNl%r2-|W+Sa*!O39u-WN|F z_?ofoAC``MQGI)jy0WW56SbXn>dV*VD#)^unSaf@blH+pw{pC+ ztY1})a>+vZvpld3W2(ri%M)cquS)V~7ad%R7gUQdp570p`Z*$LMTWK6_M- zegB-=(T^UKEf0;7NgZm&1j@iHY*tQY)h(-*(jd+)PmU`h$>(PQIEpbiJjf(dKrLF! z!9~x>k!!A1KwcH?C{tc&2Mh|^UaFkzC{b3Z2NLJ}Z%Rm8$GIg}Y1nIQX&yWGVyU1rBKcvsu zG51=2OEa~L>sJu^_$i1if+8{ia-i(*-A{H@teV07P!{0!RIek?djNIr+)ifSeVxJ~ zm;wkWX4{{?NdZyM@&F}qjvpLs?_<5>$mS_>bo)%*|M|d$YH1%%S39<9f*#{;5h3L- zaDHIr%a^@=Tdr>mI4FmkkdA-wprk!_z1-QYw(~!BR_NElop@p{wkyoVh&w>|DZY|C z5~dP!y}flCxPSn|Jm<@e%ha7@!vbFgDID6COqP#EG!ugw4ru3dS398B%JKbA$?a01pmE2VoojGZB_24( z*33~LfH62`3Tf%Zs<;f0dUR?Lq8x0?Q=k5QI+GW#kC?KYo61J6& z4{?{>m8uCU3J=__@^C)~a5x|o#2!@u0<{pR&8VY|J|AA&OWn-QHMhcbfFs{6$^aw- z9H)=D8s`$c1D_$)svsYK@VFd1@QDk?h2yc1dhYvk{>Kawsv*q-gMj;dsmxP1aMUH8 z__d}~;B#}_q+_f4I>=qWtyBoljOwZ&Lf(%9E5&g`>QNX9vN(h>NJ*WkaDM&nepz_y zmGa$lQ+y#&M;m>9PPx*JO?sa+1N{AiM_ly)0AMKU{h1S8*LrQ%c!fbdeQ+~W^wqWf z(;)>PspXfE{DQhp2^_^49JcDEi>MkHw7Qt%KFoOxgsR9H6lErb)Dw^R4qh zpk4+2oce&=H*tV`G-H@51S4=`c1#<6et4vNsDeTOIWT}ih5`m4fMW`>dit1xA_0n~ z9@0I4yvlJun|J0jxRe2sub!0cZ``ehdH@6j%0Mz`xESw+b9s%B7~}+JU@+Gqt~}K$ z&Yely#F1nAaE`?wonbR*)B&KQuPv5?kId1_01C%47myse0tUaQ4~Bb@&GMgjZitKd z4creiQHEXS!2L{J;pawNxH53ujUcUYCkP7#0WZ>GRWFfr?5ZpC z%wmxBOO5doCuy4*GRqD(2075kSV85S{HuYM(n;YM@y4=Mv<+1}5yLDx)_&fg?Y&_3 zkAVrfxwBh;*?*a{ki4Xy?*Ht0S;RMV?)m3S^JY!;(WR%Nuv1Ud=S*j*UfVowSgE?~ zaWdXU$7LUj05g_8Ilg^;(KuOt{f+YTirOMu^8{?uDe}%UJ>{J%ZxGru%w3#;mM&9P z2aZvuF01wKz^m4h-PcW)y(32}fVHJF#x-W-;eB!)5`))!`<%f1&FboMbU_(8a(|-k zAA5h43|Rl9!V8B~Gw`%uRY9R5g{5`t=4x4~M^P@Inn{5<*R9iJZ}mE|)tP+G3cplR zp-P56PnC|#T|jFm4wIDBWLfai)nZD*j$Kz@z8^F}>uC=M1XTNsszu8Na_Hbj*}C;} znf*xY_1wAPRnUR^X9foElzjm?3JUL&tAL0($CnpVcLE55Mlk@v%}~ra|CZFMb=H(_J z9eiqF@IMc^ShLo$RXx%JzbYwLTJ{zZ{DcTVm;xqZ<=JE{ZV^LMfzpbv%Pzl zAA{vnlFge_W%cT{x(}}p?|<~jI$uD$9orb?@<}Xxa(%nRJ zM@vqd)qDHX9u7!99&!TjTbT?#mOi<<#qt{#-T|Wq%s>FouRgrLAE1B5wmt?x1YPXg zZx*?>Y}vDtSv|M6cCaybSfG#lt13|*F$x^!W8BCU4&$BIZj&X8#%BO`a^MV{ z6@c^E&jACHQx_4CJ*1Y$TKqmt_CDBI_FUa6)Of<)-0re(Szqm=kHN^~7gffL=_7J* zx-5M#D`r_8f7EjUuzT8z0U%%eDtnf!4pei_vC}`?Ut>v%kXZSj;29 z1y>7qKkp!81(oSJ8j!~=kUguu4#2^+oU#G~(w1$3W2aAeOp5rZbq5MD=g{%70dryz z&;W$W4tzf(;)Akl)v76{1U?GVkBZJZ=Nvt5(W1HR*|SHE9XqC9s@wGC7HQL_O{Rdd zW1)ZsJuG_`u9E%V{U!Ui{1E_zavV4ciDMu+edrq#AV)C=kB)I4y8|p@k^bC+4Dzzzq!c91Z|ofBp4x&%O5r z?kVI8zlV<~7!>4pt;!1`J8gNsZ-1izXzz%*0SK10%*6l#AdiCRyL<3#JujyL+5Cqf z4G@4acqWFF|N3jY9-}tEU<7H}vIQWsJ^<1IUlE)bq8ba*?1PgPa72&f211Z>BpfT4B78^BOEC;$gTdAXV9O+ls< zzYRr2i#s_6=#8$U1V|Q(_Bt*E01TxV6g8(G^&_t7Be^+Iv;`>;B5i(ipd1a-MMl(@ z+H=}S-9_#7(~K`#U!?{x0AQ4@9;2U~!=BfO1jt=pcr6P{4T3L0`1s*{a{Q~0L+rqx zziF_}%3?KAnvZ%?sfmqrux*xh46zGWc2)qGUmNFeZunwVSLV)wR2QaxmW9PC8w&aD z-a&e!xYwwQH0R0ji6R391Oqn%0O~M+-ZuIgoD7#cLVh(R+{S0=at9j~V0hffRsA!?{v>nSXlniA60cB^McsY1|G1*bNq5`74FTR4jP&!T?DpgkZYs!?@ z&%c2c?GH8|v0Ti)c(t~INn4jsjWTuKK9S&{7;jyb>NVYU+2X~E)oz=1y8wp&H~{{9 zbv3o9st9ZI1LW2N2<04^S3vI#Qb=(X6cS%;0?hp-%P1TawZ*BMTC7AwNH%Cwk9?L_ z%>bF1iXuT;J*0{$K>&kVfPn#M?A6!l{^gff=pIGQ?o~>5-d#>D5`dW8%1=Ridz4W) z?86lVpbs2ZI~Z5dRng?a#ni%K*?{W}T^ac92d))_5a9?z;+!V*u@bpsZR{Rb*vzUCe>8y?8~}#dbF?7os39AY2Ye{P-I2%liXtEyup@ z0(#?#e(F*g06BHE86eK_b20BRpz1en>nh0WV!=4%x)`n*RTWWO&1)fm^hx7peDBKv zl^LM$-#AQ4N|uVvW{d5K7t?RL#Mfx^_GNExmY^ISe*&+cM;cT;SKSFkVU(?%HdKUV zCwuuqasdUu4Cl410xEXF5D7O1?`M8}E%y9!Yk4{LY;j3jdzKvkET5Y2Q#Rk_m0cg6 zDUaTePvaBc5t}+B!xTg^RmDFA=+;Z>2*wQwTR)j7TORKtsdqMuYVS{}AaOcBWyn@Wqfd5HA4ECjKD$1U}9@ZFx?ONGiZ!7j1b+3*uq_%5T zDTRdj0RqTxZ|0L#^YZE501?E=RyhSIqT+H2(7aKJ?qE^S$Prys6+k+^ajYC)f4Rb; zjIwYK?5jJ=>pARs4KEmv*TuUE9p&00KrdKx_=YYRx2dF-z&Vl&Oa@ zbwB}9Qc?h-cLp6EgO<+>sE9a3Cd+9BWk+opZjMq&rQ%dkkpKY((`TqUff_fnn6t89 zLp918j~v&+B>@E_&hrK200~i0jDWJDq7#J^a%luZb;P9|9zYBdbFXnbuB4EHaVR94 zt!Uu}bN~e=fr0b=0NI+1%OxiYC^JYocHIOh1aVtxc;VRg;%!ZJKBQSJXn-OhTf7RL zDf<9&Tny!)B3v!j7?k7cIFSo&(JrU~0Z?XsV8U1N877L~1!mFSiDImP!Xf22h(~HB z$iR}tq;CG`PlwBU)>)FjV8Mt+`{{Y}{-gIg`d{fxmdh@?T-S!zTr*L|jTh{LnTVf===o~P^SWgqCx@yFzk17yhc6k1llrX z%4i*XKS=J!N4rJ_jE{!+tnJG|vhVR8viHF*YLuzdPi5(2{iA$*KvTy|yklt{X z8n~J%$qYb;%T-iJUZW0sp;BOp!rscL3I%!Nl;3Ck+Y83Yo=I)wAiuw8vBoJ=M;m?U z8|^NU%r=Wb*r@Rnq67NXfq?BYIF51rXR!RK1M1FV7lgb>BtT{c<-=0(ZojLGmrzLF zIOTg2M3Hz6%qYnHEdTE@ISOcew=VL_^4ap%y*CTxkj5P?TB>Rc5Sa>;#K2`RATtMq^z03e*H%Q-5 zwwS!xyPEturJJO^b+;V*@Ilx9e(7dajVT9E^qX0yOei2Gfk1jA7+Zj4#Fau<7P+`CVDD~d+OJ0yyZW(z_L>t++fbPQqp(0h80};qMHBlt) z%xfI4fHse5q5uqt7mUFHBJl^XQ6oANMY5JHP?_TZiePQ(Vfm4rD+5dn0hQ{+AX5~8 za7C1s0niC7HB3Pa8f5?p7y9oVeL{>n+5m}qLfk9TJP5TgYX$@cFpE9m!8tn{v*)wV z4k!i}Qw15IOctxCMJYxV-QxCnw;9Z#F$} zT`UJLcQ9ZeF?c|cp~8)NloJyi+kOcK=1+rQh&kDHt60qcU8yCzN{FovIeJx91 zyxZD@MER^zWwmwjm1T8_%Cai1(m(oSgia5wm-dUkSL^ubW@&%#TIn!jom!il-jn9T zm*xKZ`@CvZ`JulP5N|IfduA6`qrA0aq7#=Y%jPB(weDns0RT|itv=|Qg7N^O+eU`Y`dy#2n_Zp36SjlAe~G=xD^Tl60qpsa?C3saIiY>KBN#( z0j|_}!L9P+HOpn`%c~R!ZNT~Xu4M|%Akmj|2EO%_f(!wKl880tPQb9VP8)lUsb_A4 z0WCKHz4hfwGVrdYl9Zg{2Iz8)11{El{ObV=gQK@0ueetYcPcWqyAB>0DXFOnqt;t9 zRUyEs{^tD(11J?A8YVZ+c|>8d&qI*4fJ`hR=2+}WEmu8|v3@zjHFZI!9vA$uS`7`- z^UB2K3Ls(&xAOt%su$O)S^$JKxOB0va?DEI+(WIzObvUYp{*EqJmISIxK$g&WC0$=J47-hgT1Bxf5 zXE3KN>HSC4h$Z<)Hd18(2{^#Oz3RX7kgA7Rgko^RJkH{kb2t}pzHf3|(-Lx@v%^Exh zo0$=;71YS(sxInK$;ciThrCw?P7o%wqZ~B{40W`H_l=bP70~13n#krS$EyM8z56cK zy4(iD1|YzA7oR8!Xt)>wnk=bGv8~OJQgXV6y6F~UE z5d&l@qV1nr$$J4|;Y$(&1hiazGZuReelR10g8o+lnQNXDKm=mq4Nwe>!O86){jUII zkT7D7mH#O?|C4|UC8yt|b>S0lmEe|0HY#QleD0Xv|ZUn0EAJ-BB=&2BFS>> z*Cq;N&nIU~vr)CB*R|EPOkLV%XUgHZrJY!pM>r>2aE6wZy~E0@{bP^-biw$Bsv-l_ z{@tz;NjY9Phd((@_I;FBl>;0gQ3ld+zWw}OeuadZVs*I?IF5P@&_4#LK+w%l5ugA> znY!}^hQ=h^YX7d`Y5-WZw6KCPIHn?uHu?Yv3;zi|zfI;luX{^NIWi(n;p}ms>>FNQ zj*UrBJ2L7YfCM075paM~cvwBxqPZ3>6b>lK0q6x}@yJvZ&DGwzytUk4x1vlbT|j2l zjZ+l?YD>SeT6cSVVVPZ{s2=}0=p@D^R05=eLmW`Vq#!I*gsYjmk*!QU1ZDuC5bUoH z&U3*~wtD)Qn_*vdGLqW8wEXAv^G>dEj($~Cj<3xpU#=)F)yCD4JOdjl z91KtZs&Y>YH9Mzi{R*lcFCfetq%m)h4hIC}Sl{FRDQy+d=RM1uJU~DJ&av;x$>EJ9 zWp8Rp`C`=tGGk7hLP9;b7JHQCjX?l11rdnZt*`Dm{`;*$u98p;u7y!1U=yD#29>y=u)2v&3aKMI*|8aQR*z~phxt%e(!IqX z0G%}Dpcn&$ia16|+ukTHUrZ1E{EK;zrG8V;sDVB8`DO=RohE-Qyh`tXrj9ao7zpl6Cp>gXy_-w5snBnA!$fB?&Yxj$%Y^+4k|2#D`<#FE8O&)yLgb$Kkm@RR=0c4g8`w?gJ={GN!GdhngR>P9luK*03v-K z9i5=Y8Vkj+gjmw#0=c?)!GC5^3J|a5)aw$wiA!dr@@Ut9SaaFh%8cW&g$w-qFR=M- zb%xZ@MXJ}R%g$?Ue!7Bed+h@G=J^ux(k&Hp1=ySh+4u>ZM}}1r?4N@C_nNa63OAku z2{^*v_~5$}cbVl@A|oC;t>I0KGq}ni^mpA5c6;;iwSmmlGhx1nR{e z$&$8dq=Gs4(Ll95pAJ)7aa*I9Aq{=>LK*XTJLz**GqtOp=`6$Mw$9SPwoNakK>X~p z5jk`1XZe7NvbbG>7^LVVYYr6v9sX#dr2Y3!RgkIZ*yd}rY-)-I1{?o$N16OWcijW$ z_E#^KiHo}`G%VAZXKIoh;R`SGONZFuIi=IVGMc!wTMN$%Au6?eX%vg1W8i1y*=~W9rDJ z#}n0lSzc0pe5s6VeXhKGa9=eA^ugf3>qLYEoaftJFW=v^M7Ga(U3R>BodN=sxf*a# zmbsgsOD)-Wt!&x0P>nK>P|i)udg&g!d3i4xxuAnI7~vmFfP;dL%_*vY0Q3H3@d{?& ztGCL=h1V*mEmxP03drDGI6O%f-ceoQ{PF4q^4p3M^7|_RqYf2i>RJmLjCRJKqQ8R8 z9#dCyAOUCVvlr?fKp1Z4RS|%I0TlgJOvflse7=ji*=?`&S69QPz0qH;ezvo;oRIOM z*#OxzrJfp4EGPrz*v2QEjFuvEZ%x)?fYHYfk{_IHuNRkJSCn)B1&p$}S*C!NcbTHf zK|#R5)rd#L9fPERf~qoCv)4d93_M&x)S(yxbil=WYnhldVSb+skN|`-0CZ{h)_U0l zV|@n{I|kChr~0Up!U2t5*g6B8zg{mEfby~f$59SS`gvIyb+M>f(bNMNFC6MDXj_m* zIe@VYN5JMa7%+zWOn-ZT8-svjZsm0`%Bbzq``b$6(dnO(5NFgJNDLl87}t?-Eetpu zQ||@jh1BPs<~lL;!cj0Q(%}$;vZNk^gK>OMmA*2yYM@Q11E8U2)tBTvNiyin zjB6@l5Gg*~mDp21b$MU6eCvvSx+nhF`pjGl^VLk6i< zay|WRYIUn%AdUowz5tC12&H&q4p5jAd5t!p0m%=JsmNT+>q@xWb&D?2m}KP?Cyt>W z@8Z>p!gz(i7xcVplv2T&O4 z9amBpTM@`yL*@kHj^Cw^FIIdh+h6-i4$OUD2W`hcz5k9Iq$yKJ8-1*QYD(Y-wHY+A z2L=84hBGPjq;g`DADoDQ2)G(}Jq8>DVirv~;TuZ`vJ)VPHN+-n%~w6$U6&VjZ74&s za0Y0D#lD;OaZpLA3d3R%W$Fx|^~ZP(X3)5p8L8faF|LHdm?wGvc2osjeoCDX7zQLO zQ2aJjLJ&4%SrP4mnO6;7NCv}e)Y0bcYpMeRFi;D7%v6&j*G8C#-E+@PAr{N;c!h6Y zjv6f+MvRm%hL4c-$-`vLkfCZP`u?h%pFi8Lky5pWY>v<7Hzf_srj3ha-`-7*41b7Pj9bgNLef34 zkW?>_JxB^Du9Ey!HbHjQsw2A_H72Y(9uJYL!nNjvtl9Q*q-H5Q)Br?-_? zQu@o5*9WU{91aP^tiE!PEbo4i8juE;DI-t!?CCxYQ+0}5_P zi~`cc2GNs2IGhuKOxqhpBo1M9cw-8S!XI`wkf(emmi6 zZEIUJWk4CB+iB=U1&RKRAGJuhh(b#I5R8Z;A`Z0pn-D@ryqI!Zj<5 zdQahy$^pP&Q3X(lM*yJ>wP2)q6k`ga{AA;%3TWbl2{PrDTh+KF=EfdzE(BBqh$bZq zO6|f06cTj?4~qp5iitU30jR@$A8TqS#t?`641kus=*OPpWGCwxpwB(AWqmMPnQADT zdtt!)cFZ_89?=It_QSL9ee|*3CBoowD^n7{0E8Jd>jRwHF?>Wlp-$bb0qXtAuQKJe zm99PT_M7rn>RYmD^Lx5C^Y+1#Q&aU^UV}*>0hpls)!1C z(4xY{^`W@;cj~OLxO?C3RHNLYd})P)ax5lg1&&^E$m)+rGCKha(q>2I}eK_8jP(|J?5vPD&dHoGF z+->WY&1%FY3)Hw(R^em|P^2ws%nF4B9LkyXM z3nlRy076ACPP{hcCyRG(|F0U#!KJdJoE!(F*LEG1V6pT?1(wnQVVMeb0m6WwG~5xG z^fUE%0pU^>i?EMgdtE_s0S!RJqwKE5MpmtxbAWImbE8-`T#LC7gJ-EhrlJfG(mU0(&_b7VjKRh!87XuP*1}OHJxtczKp-ky)5AyP%O>+)VlgaN0mX&i*gu2quVXmS! z1}}T84hC%i(Jy>*=zXfp`f7FL*Wsh(_nW4=F^TyRu0}a4@2T?B9i`Qqouv2dHUglw z?{}6u&vld&Ru5_18;oP!h&uovI}u~dIHsPMV{R1*P-@u<0drJl3L>-dKz{o*gIM8o zeXXN(dpiKA@13o*e9`M26;jc89VN>yg=)xD03wxFzvp9}?_|~GfPhN8<_E?XP|$F> zv6ZU^X4hmuP5-f7ykqd4Ug;>^Uk^a?S~tgl6!&E~?rl*!_^7i-t9CGFa_686=r zSJg5B*C8n0&^V3;Cla7oAnk23R$=rhRbGSjk!j1+oJpEoU#@|4EM7{MR_~_f2MIt= zmI!_u=!`SvGzUgB%X50*hIZh*RkCc>05xgaL9R@mDg7>eNR4uvwio;UqHUcdcEAWT{_2^tP1| zjfyImSy$FlI71yE{i+v~ZixY-43uzC;cf*S)WSUj%$_Mzk81%2mopWmI(@eLKw^*# z*Z={7G61|r9iRXNP>gc60bMcdCAD@Phf2MA>42`RTgZhorfD&S(JHQ>45(I2hSt77 zjWTtY)-EK;9!MP9>)=)Zd4JGt3V=Q80S@lgxOibz4Q7ylW9qWksgU<99cb~h&z3x= zoT^`#$?LcgKu{M(`P|b^4}6)%=|f*&>N(j5wA2?6>H!Y!#ejPy7LmDK%gd^PiSqgI zDr%IeqYa=2h_bmBaJc;j7*}=ZscPCDgweKAVKvIz!yp02RHQM*i94u<0i#0Pv}QfM zw5wo(ZlrZg8|VX6AmVZne~)a3PxQ|W(6~Cq)uqg}0JOMof(zy49`R}hi#GZgpdpU& z8UR8u#2qgjG8SNF4yvF=nL66Gdmym~5QAfoz}Y^uvB=h~I)8fR*=HrLVnwY_a^O&= zjy7R_6@-*2pm0b4`p@N0%f(l`=o&G|08Or2A_NdF_DuVDnbR;%t}anH#Hgc>_wE@1?dIyf#|+SJ52Q|Ax=Y}vrbJ)#&6Vq9;1%gXn*V(U3?SkVE;G7r zQ3VqIA3$A(wBI10AO>kM2X{dgWGn`LQ}1{e5M}Btn|Wgp&;o!03fenqf)1YdZ&xf2 zwrs8zUT=ViM=qdm$|i`}FCIAGcs~B<+_|${dg&!P|G|3o>M1>XT%^D1WC|iE1BXvK zEDl+maql_}#+$(&IAk>X@)|bxF*OWrR790hqGY^GXj{SsM439;a5wsL4jIkOWI+Yt zVyYs?%`kvjkXCrqJOAmF$zZ0SkpI;uO9vj__EDL57mn4j0ps$^FLx)300Ni+!fmdo zQ%nbHkl3RtfJB-w8XV5$HO!?L@Vu)MWa-pI>C>*2{tr(b)JR=xYPZr_rj9ls_NY?G zbuWuSdCo`&^kLUTRR^juIA+w*W>bJnV^BC8w*LM5>G}sKLxv32{h&ctxPXW$1YWWe z111gu$P7^7V3LDyIEQn24FVR$jgF2WPPH!EjM%ffYANq2RsVC&pM-UJ&a3x|4?t@V#IMZf;!p}eLjKErp zdlm`O#2MTPAf_Nyk5fn7`tFjz6;A523@9@f3x~9CR=@z@XV&fqhty3hAr1kcM!^CK zV`7EkAx53GS&-)3kV_dJH?siE{KsMtWoO0huGw^namJZh|5aaLpcu6&H{V%Kr!zDCV^ykF75XPstawtsyJ~P~cP zb74!lv|m59!53aAeVjEQb=iS~r9Rv|c0hSfJuQ8}r=6zPNG>Q@O4_z>FFkwpR-=s4 zXrs@``jLBp%)Rj@`TCywWdGw&iafhmj^8<5_Kq8`pYhM_c98;$<+T<+)?Jzne_84b zep}~QF#)PsySBQ?seudD3g*_^rpf2)KbNCNkGh|YQ>KnK`p`Ew0o6&WqhPkroyT%p z{+{))9J+so?05DDX3tUM7+8N?dbxs%_Q{KEJVm^KK3&#GVva!?Y)?5w{{Rp&+vwAv zVIvn(Zoqls_S@y?{Dlfefuzip19Kh?vA-XkF9#o;Cr6wziULx>y!kRdFl9{vF+iNBiY;FxHQFf^GyHl)?4l zAE(`+KrqUaDh3vz1_^gtl2kuLMJR?FuXq${xPkzIiU8F5@~5TyRnN+g@0Ayn1034) zMSx)6#)R9(>Q6Lpl#P_*~ng+;%(mJ^r0Qs%*h!w z3Oe8b2v-n)8z{5^iE=ar)$6!W4F!4CLd@|30>rMd6|^t)yPhrO#vg@5ngEHwOacgio_O*}HOk+9`>nRo2iKwx`{2AN$2Q!C zjHD?(n8hrmTNXPKO!ErI<6hxe3_u?bah4S`is|f`ppGlqQX?FW7m^#Ki7y0PGfVG) zLn)X+44)4HD^H$0ISPpB1IjQ+3dUz1r1dE0;YYMDS9YAn|F@ns>Zva8e$L10-U!V_sR`8evoh-Am}{E0R<4`9mwo(y0yQ*=y6kjcj#h-ZskA zJ=h~bW;H0MmggVbQYK~Z_l9Bu1PD!o9}SS-=hl@suPdj6KeVE~YiTbeWoV=B^9hw) zNGAh?KJ4U?^j`-Cj2BM$akTBl&WSR&Q4E)UZNP^gDkeu~7t=DZ4nI~|4$m&B_0}c} zO3UttN(b7Ux}R>27XbgKM(+NkvHK(#2M!M`vHBxSPAtz=eDC7g zf~7Pp=K^9M@dV-tKu`#vP|&0{Mb+f-(vtRQN!{aKfcSBG8C4E&_&_eF0eNBg-c0HM zDl)&5Bs)q13=2@NlA>*&T-i{5o!VU%wXPr!CB?Z9l>`>Y5l>|B44gOvB<{RoamSkl zAoTzvtvKD~u?q5)zxT1vIc107tOu(d#W9k9)-;Bl0 zPN^xV07OtW_uD=_-i5Pfcp3S7ZlI2KVC58`a${;se8+O? zKBf+@>i`50>;Z*x3e9RGg|Ex_RF}t_mD89b??0fB$X>HE0fdU?I6-@CQJ?#yQG^$Q z1!Lk3byiOXqYd?NPEIPQ*Yu~N?RDO=^fMgkTYRql87C6xQxOf%Nd-U zo=G!EV4QMuLqSmhz~z#HY5@%DVLoJvI(KX%Rh$80kYucI1;qkLRn#~>G9a5pA4v&J z(!6^EME2s7hTK|o}QNa+8*v}yb)$_5WfN79+;BxL&t-|6E1J{M9<6?erOi8GSK%E;|%sXR7 zJy>#Tve**Co3S*TIe<`-7Z9m_>O~b~{-_3$GNi5!)?h_5>o?DN9RLq+6n5W{&4U17 zP?-A=m<@_Ivv~t`P6N^bJ1}gI>u6RRnf{z1vRfnt>8yJ|HHGx(hb>>JY3&prG$bo6?C;&(-Vo`+) zDmm1s$5e67F>o@d#}AaBxo^&yT?6W$x?pDU$0mxV9?N20NW>CA;Wnxgr>~j0A?<*n zZ&WysjjJb1CN+~~*VhO!>T~*M8nUysUAbLa-||7W-iN{RvrH8OfcKv0Cf~1g(&8Ft z&a)|Bx~)51_Pm*2exF`iR^C|s9|P5P%|U5Df3q|m`1}d|{ZmgpO?sz4SkAwE%Bj+< zP4M|_z}d9&B9ZSC6b6>IK5t;3nlDgxK&8D}Kn^`pS{1eW;c_Pxmcm)`ld7kFzh|@k zH~HAy9k>I^ZsFm618=!Xwd(1A1LdUs-XtB41LgR8=LO&d0i}I>mK=ZT0tEyZ6lLX; zOg&b&eoWSWgt1YhhSIHDS836rMaX&8t5=hH_59Tcoc51@Egc?6mF6Q~ z&K4k)V-QX|?KG)XyN++UblI>giv(l^v?*V@VjNRIsY$vA%xnLtqrn@+n3|}6u49~J z1n*%T3)yZ`)FCdFICL-FAIGll*eZ;99XtiVAbzjEB6jXz2vB$lXj1!{Wh&~mP`Rz4AM;H3CO5t!^mDvY+9&_ z$yaxpKKPAmE2t{T=kYWEWbn9*nJLHOj@~HhxLUZ1ynx~_93WLMpXRz1K)OzOPv)%r zUY4%kCX-+MM!G$)RxZ4IjT@IT1BAwf+E_1ZRP&KzGrxDV<8 z0ziO6%%RNo-GRT=0M&QeDsAJO%v|j0Qsv~;#CWy0t5t|t_8z-{mT0UEGS6MayYFCfQ-PzC@n%FKNLhJx8s zziHQ@K>MiW+K!T<1?f>miVA`H{(AMr$^_&{<;A5-Wt=Di2nAupAXK7I93W~A90LVB z+L=f38e|}Y^6tCuh72CpLOFmV92jlK z(cU|Q&s5<{4BU@&Z>CD}oS!=r#bB^z z@VJ_*q97p4YBpiyJZ9H=6|hNTVKR8(g)McDQP-+fD;+!&LCUn_PS$SwtjvtbZniFC zX3ZWYMO*1-YpD^E=7~X07E@Tkc;t;m7Jm#7?YN@>I+Zk| zKL6xJw9GD;WdBN^vhL6)H*7x;xvx@Rf z_kiUj+zn%^$h((TmgPMw%90N8YIjwMaz_aOnERn0bN0gWE22^k%(SGo*IIS}{yKgGz9!GQIy~ zq*wuAV{f`u>NofQ0<9O&;lqbz!GeYQ4Wg-2r^&W$+Z4>b&Lr{kFTT*b_Nkk8`yHyN zaCHC!5C8%O5cHGZCIJ}79xEmLu89}=(&z8F0hoR&0uFZMxr^o4nn`lxkC}3GtMi`j zZ>5&@(R3#P&6K}aT&>5rTe4T((E!PYfQtd=>9;@B!2>BXK>POXQy>6he{9in`p}Sj zCtM||n>J}5R5X741hrdlyG_jt3n+&lDx+@ZRTp&t0o0!4@_GzJlosxK258$e<0S3C zS~+@XkpfEle7=GSz)6wgj!_3FAfcp~06qBDBDIENy6UeVJoD>n10u zABolFSCw^7{H<4pqLTNw+^c$YdZ%Xu;g_sgR}SL zJLTBN4`+Zw9c}wRd_-OC=o9VrT!MF01#S8CbNThFZ`H+6(vNanuDI$tg~P@^8X%N` zf&y>?Pwl15<7iM9E@K${s))Hx(&a}@Ig~$ z_LS)gE;B&HqQ7<=c9fPPm5TRKM9`zf{|!0eZhjr2w2^L36+mg8;`dZ5#v9 z3&kNVz z^wfjnM7D2`C+6J}(Zy_@Lz=g=53Cupmde3H#}p7jo-~i*!T}kir5zG9rCW?4z5w89 z#|%Y^-s1cCuQKgirvp9vT0mhv$CX_4hKB^N#9pOeF14I@h z8jrT+{>~UANw4CbcTLSG9@@CVv=VQJ~I?#Wt;QZrx^eO z2z8MZWSa%za0uRZl;*8tpun-f>zy@Q>>*H7XK-*!RA*o_075CK#0(&2slj6a;acHo zKGv^d00b4G8dALX;RTTZu@P&ct+(Fn9s1=siy<~h292r!#T>`X3IiZTG=vPQUa z!i}qLfqBqR*#YE&Vnxqu5zJ!&q_JgayXg~uP1MGOjevBBq_f5PuA>T94TQOEizqU8i2{v+(7TBHkV z%%V|GI0P~nt`%PI9UwOFoY)^kXXnHMSn4;Z71fAYdHx041EtIAGG}r^MHzcRNnfLl zKBLad|F0NR`u~#8metqzvU6A2|JW0<@80|6!1Q}`A8yotKj2ciws2ATSAk(^;!GJ` z<2b9JHUntkIA;F` zD5n59F=*cnH~9jx81zxY#{c_SEH?lVd@Wl=QqbPZuaGAzCQ43BF#mOc*nS*6R#gNH zFCa1*3i_#IXJNkd|3wDNPA22B#(!_TS&ogHpk{!MIIw_&da^R({{Je?dm+8;Kp`dp z1W3dkl=H8hLFdFo(Ypb~<|rHd6Y*wmAanmC3KjPfx9p$Hp&{>-sz)*Iu+xruBYYZt4D9lrQtf zCCa#N-Q@03qjaB~ji-(K!8!cO`6Az(uKRFEkpS5SkaHYJ4X5;xIrlv*=QJ#<&`tyr zszE((-@Z~F9P*r8-|tBo)pemv?)#)n2!2s-$OxZrGL0X3xoq6HQC5ArT6P@RFE79P zid3r?`FGh{wrnAT2M>}yeR}J~cQ2e&xvaVqhC4l)yhPQ4a)85rWQTcjQ|~7nds^aR$k~`90 z|L{X41d|QZg1^9hihT3!x0+6Gzx{RrMED;BihaJt_b&Jvg2f%+m~v(;x=WTVzfu9M zS=mjGw|!jEg=B!dF{#Mx_L6@=Cv_)40f^&5Pjr#O^E*l5xgAwSSy2q`gko;K_-VPJ zd%&FlGN#)?-2)AH82xZP>TmAxSm5uh9Udn?EWSnl`s*)Qy?V7AJa{npMU;awW5xsS zHvvsK-mfxo4!~&h!jX-q$~UW$6bOLWb4=Nk#LRX{wfK<0O}qgDP|gX)prR=qwa%=- z+^hj2<~--v^nr`yh4C+Ga0U{vyz%Jzo{woCi%lcCEJ*)57xnJ%g30i)@q6#dq{)-i z!oTN*f((!s4j^`aT2hVjfi;EQGVS5+W|^+QHBDTlM6#^77phQqat3cOfJMv!5Wr9k zDxz+b1LdavPX%I=7Z4k2iMHW-0P${X)~t3FMz$g#8w76K!>uUeY8;0{0_s=etEllB zg-6zxB6qe7K=K%>Is4Hra@y>!A#Zx^H}x|G1{^XK$^niAXWR%S;d&e!6o3-22e+RW z`q~8DXd7?{-d;!?d*c)XS4+*~x){fRF&DEwzrT}T*3PZwn!N_W7$pG;BS@ED*jDa& zA^p=+0yT99HWpGl^hd_;i=Yk+2xh1R5EFYnE~7im*EmBNBPIa|%K#@9Kx||>%oN2r zUN{DVy+L?)+4;KX7*{iJkpOu?Bu2^tJ zNvH!uN%R8}YBCi8%KuZvOaO7C6Y&Tz1oKNTyG&}-NPknjQCz9#{atNpi-O z4Ry~kaA?Q%aIt8BY&$Twlndh1k4tJCLRDnB)F344$GwxTWMa=V^TMy-xkaYATK1` z4EH6;PweRb|ba=?X#-u>vwksK#4I&^8OtaDc#lfPj*4HSf~GT*^0NMcPn?A0$+S zdPw!re)I@1WGj>s36M=7y)kIx+$vdKKHyd;$`s_c<_e!V6O36}P-n3yW;k9Y;cmdV zY06aHbK{eD!sxf%y}YtJ|6z&Y2M8#%+tS0}#G;@Kiggrd0XoC-A{b+EBU}wAmZc1q zZ>Gx1cK2oJcW-#he%m=IodrN{ns??y009h{$N=e}f_)4SPtznL@fukIqWyhE;d;9rWcHS=; qjTt!EO_wUcC+3+;Ma@i^Z2u1`SIM#fl?Zi~q={6Wht59YaL+-Yh?V5Fu;d?JuFAP{>_Zs%&|Qjg6JKxHx(L z{rA#Z$YKF+R>~eDT zHL~Qn;z3_*UC&bX$_qfK0|W-xpobpQ{gNe1b&rA*JLH!mFBJBefryKXkq;JDR4~fG zNv!YS{b5q&es){^=6&+hf}2Fnyd<*qCOP|MW|{Z+%|T!2n{H)P2@EJeEEq-|K%am9 zxx#S_*s)g%sR0HMU|4*7j2=sDgt%SslEF?N`(2hkeS;+KC@yknxX7046m0$zIYPRk z`xwBgH5)sMxn0p4XO1i4!hiwehQf9{)5ZtP9alH>jgf4`#;C58R>UvtQk)D*n3b^JKvQ`R&t8vSwN)-CHn{3)wF@urmFY2wSyv1T?8Kp;Wbq3z9v9qKS?4azhz5$42ib7n`gbMR z{AIyVXVZI`oC%oZL5zh9W?c~&ps+1tK9hKpFaWV&s7PIkV5g4!DhtNcmObxylm|To49`8|&I3qKr28@ltqQu=z-c$nskl2`5J*JHd zfa|6VMNF}D@>A;L*ILV&;|D_yCIHNU_$ZbJF#nCYD(v~{x818`(bzh&~M^QV)2!C+u^%@}KzLze^0CYG=|zB8PyrBDr*Ol^!Mr&?GY8T_(0n#t0a%IKQ#bR*6ZC%1s>3}&rv?mlti_n@*KpQ$-CHa7lr z0kBwZ*RF+Bty0P7HK?mzy;|yD_gI@q`RDDda$(WUa(-GixfHNVfr^WZHS<+W0K}?g z&&a$v4+H>*wH>WjL{2@JR}$9Tpmt_-u2jEBd~8gb!3GYz-NgS`S@>GjfY$(uw%Bl9 z|Majta^kLn0UfDJ_{JoK-Pg(KCqu`_mQodDcePr=etm(Gf?9u%DJ0v<)z9`D|yN$}O@Zqg-5hqnx-i5YD!v9Bvv6>c^(7~`;q)bk=!;!@t#eRs4lAp>(hP2&f0^_hkh4PZ&s;k9w zua-fszwt&ru2-+F96x?sE?&GSY}+9v@{rSZm zZ`K|C!M2u&Rv6T>cDP7oIo3Y6g6+)_EqjWTNY*w-MGG*w^W<^t>T9o4D7yy|F!u%| z>S4V7ppeHJ7nXD5bI7^4Y;s}2%^tK=OqT&O#D>PWbFFz&yhX&3Kw!d(R zWX*!1jDjLw0(;X#Bs2e7y zn7U)(;vo$%UW+(FEe~LX^PVJoz#f16ae48jm$i-hbLY-05Q=&F_`9S9GlK^rU|ut@ zKnGLIf*s10&oL%t8)NEV@9OjHuxTV zOvV9v zCZhb&M<2=f@#A&Bd-ra6|NVKYVgQ)A>N>!a{pIxUz2(GfE#>$_4Sa1x6FE7xt(;uc zN&C?E%&~qRGw1ReKJNziT`bnV`?#V4C2q{5MwvQypO9eZ&YhDPGiJ(TLx;)8k)!3{ z!Gj9)(krjXmhZlkgoFgG8~yBas-O>;aVfp}rw7RCpL)sB7M11D&4uJ}o~R_tU({o! ze56KMIrYU|Mzw(+Fmo=i;SXiil1&-aESNQ`S^%6I?BvOl3IZ7Wi*x3ho8K6DX~=^T zzkR#*LA3`S7@{`nnP=313;=5#BZqTEsZuB3X`f_9u_N^>DD<&=Ys#6!eLc#B0ISk# zg}#}iF6f1G%+%3FpJc(QNHXCZ z;3%gEW)wPeVW3A9V?YvXw1onTiHVisbta14a}mc5zbmNG=I+ZrBp6Ddme-@ zu-bv4J}O!DfRovU97`4~*pdo`n4sBEkc`NAVP(ZMq0 z3^2;n(T1WxaSrFID>Av-iPjp5*|MFph%E~Si076)WyR7uK{M#mY)li94Z{K7|3CvkWxh;hy6+l>=xpyOh;Epz?R&=heMp@&{xFD_z zAaI^+imR2!-HN|MExcq>}$9&NXM z?duhaEG!u_UGM$tNb*~$(xrv|MT-Z140mOn;(K7=*59pWYN+L>UJ_WW0|rE7?)w#rzxlG~n0}nY zK&?_1Dmm41WZxkb(A$x<#cbMa76%$MBS@ub?RDAKUvM5W9oqeEaIxGlLmUV=P!}BID` zVMjH}C`%iCY=3L00R#Ym0m0z)kMy-X5EtLfFDD<4(!TWhXR7BS_^4JSZ&&@$6*rQ{ z3Ky5Nkws;4WG%-9aX$v%yiGGzYR>y*^+U+)Ii{ZW@)awpJ8|VL(5Pod%a+Ysz)}YYfX8Et=rQmp>*Jy*bhJQ}9O&C$jq<_5MRmaW zBky+-gmppG`uO8Ay016Kngx8bA_G16=Rv&*W#Cw1Vxo^?EYEB` zeS^L6iBhQXclV;|il_kuP}Ex(Yb2Syy-Uc3mfk=94UTN83R$Jr0qAgWa9 zph~eP_S{)9bC#^KcI{dn{D47&gakp|haWC-z*vgmvbZ5jGy1Mvxk~$4CTZA)E9glg z#3u8ZjY9?m44e1wRgT?zZ#M^wvb)W`#o(>?amBMk3v2A0?B!7eAgk2L-bwX;JQJl* z@pqLF=07X~MNkICfB^~vievsd5@Qsy$plE00*c8RNPtlX7vt(PH(}Rg%;fxW+#k%& zVN4O6H)qZqeGLotMY|FnsNP;PK*Sip9Mi@z@NO_Wxht4kHGr&Y>@6HI1)K%6P(T7k zx<7U5lq$f$bq?dhCnlM=?*zz(n(Dpt7JX>Ph21LwVchaFzlSgz-@*^ zB@8&5IWg*pOA?7YD-u5>0&6zS1B^15h4-J&fDHAQ9Gf5l2JdP|*#Iz`*?{BfTjl&S zCB`vrfZ1ulHIF$exi?fIwouZ($!hCy!{nfxKYEy03Rn-iwof}o`K=X=TDTKn4A2c{ zW6mmQ%^%7=LF+gDOdVNEXP8= z5CfM#Oa|L?B&gAr;zxQ?f|~cUs#Ue~dntphUnf#CT6SJ#7{B^L`}0NBT({)+S&9L; zW5yBv$GcMTTjjht)vCkytwpl5InTc9V47$OVuU@S% zoon4JBRgL&RSHBX=r>!oTsjywX!t0pTK85-4OG)vd!*Sb@luy>JERiqv?)_}!DQ3^ zLr2JxR~m>c%`MOO$S-pzOibPtlLM2j9#;hFot0nd+yh*418er`MvW)j591iH`VTG- zFUGuJ2h1At;=q9-6DFt~o;poVzI2y@9UInM&W(S~(>8v*9D3r}nuvZ5+=^Uc1NDPth4pW(in$Bvamv${!Ev0dM8kzaRzudax4KS_ez_uyj+ z#m3%&dU=DCyypoi|MFagYX16Wp{~iecr{h*<#=iQ>Lxu-2~L(QNwO(ZB(YK@A9W5i zY9umZM3R|>z+uz2+Nh>3nH<>J&A&S_Htw}~vT^J0(x>}KnfTOL1@;Gvy0xp6zIU|a zet@)oYPC#W@q;Yfd{Blh*e7jX*&w&Ru-=Kgu-;1tv`rfyWwyQXhI6j`uwSHZ-SENO zS@HHfy(Km#PU2$XJp0%aDTb?|1Ovw?Gj{>X?*g!=e*4k0p7#EWwH@VB{8ZiPL^=5IWAfWo zxz)am$RS@mm-NYP*|oH>e5wv8_PQpSwg9ME4bo(Y|hP{iMW!3}LzjN@}uQ(l#LzM2u{y_DU^mUi99N`yVi z1zY*A+8}ac$s-bR^>?Oix%BAym6R&KUBMbQ{74QTJ{R8qjWVcW4G?Sb-ebUULH~3g z?&iN31`b1Ef7P1wy66LS?tG$*opx3tGVjy6JMQ=_JUDJo{FU-I-j4RnV?U=F3>OWx ztfHQFdwlq~NU5gB)b$D!m@iwl91>f9bHx*mOIE3}T)z3{V5-KU$}o6;`{VbOX#z{> zMl%#xsZt;NDB~MAUK@O=On+q=s| z3e`B|byW{whYsI3$|kE=)oK-zHDWS7fPVOXuXOMIgk;IlTCTdPfw`%?O%5G87Wf-I zX2C%mUX^8|Ty;%vwL(!nWtW-PBCf=Zw*hkHxJfdZg)he4axgIcUg)la3U2!%$gn4# zkcgWyW|G=B-RxtvGG=kgC2qJ$ZpxVH%De)}Z46$DmXZrmCFEj>61qRzFRxrIQ(VN9 zKP?y~{d3*o29*B{7%)dFMah}RbIaiZ#TAV5$*5v7zd)o+$X`hJ>kAf?6$|OOqJcP+r?}&a$7&Y#QOpg7YXW!CoJV}}8dogX;X=idjPF<)z?8uLGVQ=! z*&@HtsO&m8Q;jlkC^dXh2i;>M7I%<-Gnz@YzDb|EA6zMqx}J4G zz~1jv)PY&jE=tWp(ncQ(*3%fT;S3Um_&hydz4ccN08|64;l1xj+Xvs1pH>$Ulmd!2 zHy9AWc;5Su*YYDDv{H9`W?2_?No@2dUF4znT1x#PNk0!Wta4r-FkJE7rcpAvdZav* zH;0c=M;m=?*6d-7*U%S3oqDnJB|^Wk;Q49?gLi|W3}7|yn42sZYB6Ac#i&Ce5{JhX zJ8GGj8#4WN2ac=FKmjApaIFEAb18_whfx=h{;PQvW1tucwQpBRe>Z#m)Itg|WbkvI zL>;;_9MqH)Nm~ZZ>vhS0~1RvhL@0Marmq_qfJVbAaa?v0QDE1yFqX)W(^O zBl~~#@WxXQ7Ze$izN%Q2<}(FDF}FIzlp73yql1cRU+Ry(m*0sm3unP_FI>)MLbv<* zmjPrNuwb~?eU)cU-d;9evICSPBd^G?v- zGTHF{360bRWBh(yj4QtN!9qGvJAa`D6Y|)Gk!qB!LiC|8=LD$bzX4XF(YxxNsR7d^ zkr^;b0f>?2aXEFxrJhXKbFwF{LfyhxxRbAYjDZI;+a4B_^F~h^!PrYR zlfy-#<*Ndre{S$lxyo|1S}nCBm8!~~!o_4!zVLpe?qr)Ta_XKoa^kMey8o;FJ?Vdc z$3Rbw8ZF1Wbk#R!mKF593i^|s4V%ibw(aFu%QkYXNee9>>C;~!lY`rH+hAGz_>*#A zX+@E}5gyx~K~}xnURK@zgwURTVD%cxnK^Ui{KJnbm|L~O1&avn0HNri{DtLk-a zaGdkADbLOy2*;rHw|(Q|WZ$~7a$!b6IX^C1_iujDU%GC7OQF*Zj;n4)pb7+l8JJTY zJImqXr32g)B>_HOwvv1h1Z=}2y>*_N@!>;amBTJRR#krJK1AzjPd6~)#k%5AKUXBK z82rysCG`FKU@m&1atb}&>yEn){3bH@zO z)RQQJfdL&1j3kk;-^5jXjH>hO=8x5IL0}U;&!$PlmeU8!#EiZ22Ma{`4DwisGJ+=Urj5_Fe&!7+Vyi>E_qBZT*{V;bO`_Jk(OrJQjzrq|U!}%v0of?e7 z!JsS{v18+jIJ2pqdJ+dQ*MHS?ncuF9Jkz;#`hk%VgT<16JmP-1XL6Idg(J;lC%)(G)Ks8L`NCbQTrs5qTd=|HbW6p170trIlHIgrfxgRuoIyBak=$+hBW|x7L+RY$n}t{AYOF=4XTp7$%N#iuco6)2QXDwm9s`c+G-QO=h=OR8VX{q*2lKWdTo z=RgBU9H1v3AE-aDDx0Rr#UJLXU0C~??3&PDhBhmoYQXHhsz}a$ zRmFnkf3!vbY*=bpC~iMe0|pd8iKo^o$X{bS$^H?o)&7XmlO#*#hx>T9l*t{q#+rmrXHkE2>QT>Nw$wL7a8ldG?}Hsp)6 zY}rI6zVMhrnG+QZ7;A?<7_MN{^IQm&Z+OrYx;Q#>&+}#Z}$Eok-2&L zZLtmvD3ppfgGl)DHA$TNxQuC2(Ol`k-{Sy?_1}(di`4C8>x`g^DHLt$3PAk#gDi+SPP$`MZs7Cf(Ug<6N! z&ybC?o|JX-#wv*0sKZbXSpLvx&kYBRSOW@}9KEYJ7W9O6E$aCwh3$HKsHf}&(*4fa zs@z}C-XRqW-N#sPAmT;@}6)WtvFBV`6xl8A2A6$%3ouu?D9Q*%yYV84Gk$x(}e1B!xQjDN&&!DX*M zpb*s42X{oZ-H&$B`mzOb>eXkk>WaqAn6oT^s%F3_^L0q-i8n3~*^zU34WEx$7#N@~ z?k_Cow&jx(@%iMtHMhvClOq)lwQxc9DCrYpKmep_nf;VucS>}0v@BUNHwnzRpSvDN z;tntd#%lmUnKTaA;nlCIf*9qCt6$b+L79n~dis)Bu#0aF(${c9or=$+U>APtq z?F1bW;%kj$JrpC}a7W?^KvmBhb3k!N?VkyOu&!v!4%oY%*a};%BscQJo5qwkX*lzb z_c++9BiKb^X@v{grIjWXK#e;RYw`NItw2}a^JbKs`s^l68h`)fKDUO6#lV99`9rGmZ=09GLkSllnoQ2y$X+DYXY@no}K>Ru<4 z&y8ksK<*6?xBXA=xa-udBTbq#@p;H6MjdUo-PT{O|2J-E8A(^D=f;fD+a3$>*w7)) zo|wZZw`keYX)9MY{CB+0O)elZ=@xMVxuN`zFU!VY?ekkj_n{edBs!Rd&0t z8^mq4t=8gZQfO*Zd2~bt2gE|frR80)8a1j*n>MYbUcGugCzmZ-MygctydVQb5rA;D zgFEKwRPpPS{_@-S`_wMJ|EMH<{hHPR*tUIpJ%@8GRC410Ea&tV63d;v0pcdrtz4?1 z1`IGOh{a3f`RAXPMvapG0@|ho{;&1`KtyS=BfX)0y3s9D-&*)~M#9)SS^ndd?XwMc-+up!sLF zQYA6!QjD8n!-gr;E3do~^f!J1MZw~Q@@j@7UMvv%zt+623ZWcEnL66A>HTkWvLmlS za^Q+HxGZtadDLQ>WV%h8MyG+2e-p zrwy=e-MZBQ`}&bC(yW%}G2*zIO&Gc)+cM2#-gh6_oWr@iM&W@KC8~L0bu+7m z+r2KH@m-JW7f>kS?UlRiZ<;_wCU9Vah zg+x6Iw)ex)8c)>Yid&xUq33WeuTkgus*<-!VI3&Opcy=n09`lLV;mD#C`|Go&a%JY z`t$+H;7(tD`KA2u!+wpKUw-*TfdD*w_>k^5Z{DnJlP6CO3YPiC8$Hi5GC*?T3w1jN z3Y>LE22UAfsMo$cOxMI6=kglX4YT*FDtU@#(>)-#8)h?Nyw~&*5OJQ*J_1I&g57st zH&p=`_CT_oIdfWpfCUou=!25M!1S##GcaQ1?5`hbMnoM9r2qxYs)pH&$py&ld2qaL z3=W74BdV){3>?>t@u~w5YB8T#1#cQuLlt(w$Y>UZpRILoxFAXaOuVt-j_wU`zXwgr zm#e0G;Os4rH?}m*8(@13D6ti6QB1}ojyf;7Q}(~#!6+M}d%%DMrtJI8#LjD|g}RPA z)~;Pk-hTUSg~IP~Ny_X2qYbqzB#PN~_O#cjIR-m%8P@`+*n>^wzCWG9ji=fDJF4OZsHZ`rlLuBdfBXS+k&;twX{86dX|4`MGz> z#q~qw{2$}x!rswxa9jhm#I<8hG8!-cEPY6iam(2M!tc@nb@1K6l6ZQ9TsS*N!4kJj zS0E3RS;_Ec<1Nz_5IB^(@}W@03zfJd&H?c^1=~GKmF2uE7S8Q{7V5;3F>>+iHv*uj zqwUmJlhh?I%x74zv=s;Jgokzh7) z*a;aZM=fIq26a`l$EfD+YeBEb1j68OE9-)Pjvc5*qToZI7-i~g9O9AyU9l52s|mLP z3>??n)2@XAS;Z(*XR{$I5qmMNNH_)vluZckRfdf%{XCbcvrxD%D%%TkMS|HFv4dl< zs0bi)lr=Yy?Q2!LBEjr})_-uskiA_Ar3_#`3*yW5fa>YJO31>I)#bfMD+Sb9bGK2Z z?zMJB<@IWX)h^o=3V5VA#v{Wo3=Vj#ICakB#Qh#A4uJl;#M-X%`_#(v>0^a;&}Wx6 zbu4jyOh6lTTZWWy;4UASeF*%~ejeii?>y;#M|U|ntDT&9t(6*O>S)tGw!ilw@OGf* zCghe26LV`B$a8O&lyejFX}xWe83p9ngaV#6Q}^?TD7`Rul)H>~#JH4ReV$Z*!VLIG zUpeu7GdUbpTn^`qlEe9ndW^EEqm4e=mvhXyyar%geFZQBcAz-+`|Bi73DZ2~Q?C|M z#nKndxZ26MHV)9S{`KVKoQ`UgEg*f=EnQ&53m_;0Ac_rbluJ!!6_CU!`E-vP0`Sw= zf~piyWWDqQJ7wJO&pvXrOLaMPLqXl2IXBS67P0n#x}dv_zMNyhcr6CafQcjEV$2;a zzrlktfKv}B?^4khsL$^!DvJh0D^&V`5m!I;lB2C-??PJYb$@^HEQHp%!3dL;9oIzXC=UJacwHn!xlk7)BXl2r!a`b<;y*qa3L9 zeRzJlr;c_YuV^q7a1Y)l3F3)5TTe!$4YfGuig(3z&lGbeY5)MTW;b)1j$OvdiL1tS~s z=cM!lyTl3F=VsBnW(>9R0E~o!%i5%pe#zq!msI{SrVCu-iou|)a;QcENuR8k!Xg-T z)KXXk2b;$jxP@WvB4JnJu#^V!pLNB{0cNvZYQWMZLtf6SYhNq5*PQ%Vpex$Gmlf`^gZT{>5Vr+;&2(`~ zf3V~R^8Z^`yrPoF|Jfy#|F)z50NL8NpM2M+uWXL(E$e&sQVZ4p#+z@JOl9&&rc$|m zELVlXQolh1eNT}-molan6E;iJIX|d1pR!Y$zO+H?1GXCtUoCa}EDHVlYr5VguUD# z2bdgFn>&_qVtr|~!-Qbv=429QCSPk6sFx8nCr%*Q#=QT(Yc;$aRYpy8gyw;ZOn~ zz<{dpz)~sMVycwy_?9$$=BuEJIdGfJfH#`>0c3RL%JNnB915Ke*fbSkqZ}OLy6L=8gSNvLE3@W8T*mkI&zH^srQ-`uJwu< zfB>N&kQT}TLkR{81nMwYlmP_8{n+EC^r0O<3!hf733&_1>;jScM66Ix#X_a12KUH~ z*>X$Cwoj^3KmmvW3t0U}R%r5Yz-&U;+N-y!1sE{}j5XqIUejg)@ZS%4NZT6c${}e5 z^M8(6lQG{&on8yYAB<#U!J_KC>VTmX=cpU{gOSo1IDLg=>0TvF-!QJ?00cU`)Ow29m+co4k#dTKU@(* zq1t_ysHsxc{Ge)f|G?wQsDdJ7ITS+yXJYoQL;%;7iJ7SFQVTakIiDrfdu<%^UsAE^ z&X~Pxzgf+&SH;X03XInR3?TQS76pOD6-gXqMa)jroPz;L zyE75ug3M(Mnk5yPk1`;ndD?M5)B`fktkzLBl?!zJ{xoaG6-A@vjeG^eejcYkn9tz7 zOD_ustk20Ppf1;|x1-=T@9oPvZ!!UlerbU6xy8X)1ix%lJ04m5lEJJC2L)pzt{6Dk zju^7WG4;fhb-&B80rRhb*^R-YirX$HklQ`&^Ei{c5luDNP3Fbw)~PM|^5>VU_${2P zt~MuRkW|%cTX@^0{_@JC@|$^Z)#ETjX*C{vf(`fvwUv0?>j(zt=J zFI%>>mVxCs9H{3?<&*m}XHl?Z;mVgU@3~DkS3P^q9MZ61UA5HI*R4}KY_M9jo5}sL z>?}}N zHkg}J_NYjoYp%Uc$3r<8u)Xs0&pRcYdJov%O8{FK5{!+3qLdr#Xu+cT(19~%dH@h8 zw{eaFrY9IbBY!DiY$w{blVdT}6}?zs0Ix%0jS zjuB(Yf&FR*j~%s8#yI}VTmIGnj>&yXN--8bn60;9$9PDM%PiP27hH>0ZM{E?AewYO zU9nNR-RGM;4%e%%Dp@cxAF5b4B)+I4fdCj1?7Hi&QzLb|0m7}fVEYOc6>Gn^pg6Yw zU(1#)OTV+_O*FcywIP)y3d7y#VwSFihFe}z))?YH06NfztS zp&hq`^3#tyBsKMJSA460=Ybn*3r0CmwvG*md+)v1nVDjY~ z?z?xt&jDi+-d4D{4%j9MAM?T-^%{VH4a?#8ZCd<%1@04X>;P$vV_OH*5o5o2Gb85(fB|v*=*9pr z2CM-5II?N8_3r+(r)C}l0#^hG^?-D{qTLuMD&S()I3{5La>A=9kX#7ref}f&K)L#u z1pqo&y?_7IfLT`r2ta>V^xv4E$%1y_%NzwDNyMtfGArOjk^%PpzB!I9TJ)ZzRL}c; z?)q@SQgX#$^BC%|$^ZZZ)W%jo(nxZRcADLK_enBamzw(2xFQPKY-p7W1xC8pDZsmo zaw`=Q%HF|VCJSpL#kchSSfnkJ`jfjK`^B3uzV!kTQ$gJ?+%2wjIoTN%eOcZPSkN+B zrq+nMq}RI2+yiz@`eS48a)zsGFW5@`PQ$Ejqqjd$_QYSsuq^X zY5WxF)h1~;12%y6#x%p#*Z7o6l_@LXDQ3V9F=rUCziQN!m1dIsv10h|TdcmbqF`~= z3p-%!Qv=0-t5vs;GRYw2E9kFE8wIcODTf6W3T$41Cesh0y`E_JlnbWYC zOo)k8C=5uB!&S--{_3l*mI@Us1uYjSl!}M&y1|ZBsH{M4u)hn1{+v829eL$$0d8`Y zBq(dd+t|{9Zvq9ls&Tudn91OdAD?oG>Zu5m$p&nDWN|rEq=X!)Ttz-ASw_LaalcbD z`YRY}O$-8NmGg)52YakgL3z1cv@B>?LtZGBPs?xBt>RbC@ATPFEsSJ0y>wam(SYHG z;Xc8NBmvaLMNjE>FDHk9ax3>((cB^7Hn%P%2V$eu4h|@tVi|j@NDAM$K%vwG`K&sH zHKyc?R}_wHI57n1K{4QQdj`{ZFI?0S&CpoDpgJum~}yZOc4AUy}K{^+q$4h2(E%$+5zYHnA2 zCx2o2I66vgWtn0j%kJY8!Fs3SeRc*%2J}}f957X_W|V^YCy)L)a|sD#T$QR~%Or|W z<@XDO@pgM^z$Vu%q9CYdGh#R~MtqTAxG*FUe=sKJ;ORa?+S7e%z+S(3fN^|Qr+9DcB-=Ci(Sho+?2jmB(6`4#|&stGi~&nHqg_b3zM#k;F9k& zmj<7I_docArtn%kcIzGmA7HO(6m@JNZWa=dtH*;X)0 z-VE_j(vW>TUq0<4XA&Mz^ZByr`9Y3(>K^pk5T`%qy5Rt-wz!2FzoGqC$0+&dwVZOI zS!4j*+Gf!b3hYSf3UchRVREA103G;-Aa@l!QQW_e1E*gXW`HOA%jrEmyGuSC&x!L(LSeEchz%%v|kqv26gVEn??3rqkDh2WWmbEc%Ga! zYP6gvTS@ljkCfN_R6AU(R6w7%?YqzV1@FWma%$zBayU9#4(E=N!}*HJq0EI8M8EU= zb1ymCva-{rdtqzmG4N zQRQfdsyb*@#Ha-ba8^0})d+cnSG3XNgI^bS z;4GLs=G?Jk-3(>66-?e0ZJ#7pG=stgQR_&pa!KxG1`pWrp$$EX0amD((~iQl{}C~Ab_#wn6g!lo7Z_|q5=|%{cGs4mTl!&*L&nhp`t-!jC%h*zF-@= zDcI4rRXj-|agfGPP%x7$v^@xz8w!A9-{w=Je0qHjr%b!QTiVR&fSKEsBQZ5};D_@? z%4Z=S!tLK@T2QcNl|32KxE?MR3=DPYf3%I)HBCPYM)o67*ulq|z{dS3q>%ehGHI7XTIbcRjj%l+=M%&K&{yGE$R*R2t zT#;jdtSj0+zpu(78>W_VEZ86C`5PGTlD!||>Ea>#Tuv}I0DC+5&llaSdya8Q3mh)k z-^M~s&>{Pz*%iH6)a{C>gv+%b_nl@zfO4Bf@U`a7nmxxr(TvvS;qDjA{KrP@gqoa_ z?eoK*BEjtMBd6u(-%rcF?OSE{#xJxSsxRjMpS?M_ip(2YM;1L^&c~=v(?bSRyT!(t zi|VqT6BA|p@-y<`L6Ill_(&eCev^!;d6f(tx-2s0g>&Q zl5FEOa{SXQ^845Vvf|0ImjrlrvI&8qwd6!>h>Y29s_y{1nq$CL9rb`MYY-t{H;jm}jsTNDf+6tv}3Lm9O!SO6SgTzv)y)R4H}c-;MkJqTR3j$2D9;mB0$B*#mbp4 z#%up=Pe*BeN>D*v^0_v0zE44kZ(Bg_xF&P(7{diOPPknXpYTAT0AOp@T=W5})?~9( ztM;Dud#ql6Y1Hq4G<)z*Y2EI}BrxlSUe~iw3m$4M$37XW{eYv)etfI6J?r_@Q;ijD zOZ$SM!N`QDwdT%x3f1WjkwTFl%S$hBaKN^0|5d@dR-L7a)yO|w-8Xh^qSgl}2AJ2) zPzsfPT=2BS#CRT`%yFFAbDVBqxMKI`f6#bADO?K`h_i$Qk7^s@cT05fPh@7VHVOtH zK>4dLmE5`KJ29r2&y^@pYiqv_CiZ5kLYXR$*84I3oQsMvv1tRdF$P%B4#VbO)XA9a zR@tnCfExDLUR`nEel~%S?yt%)S(=sUBYXPiRIt*e*J|AjH_p}<*Ex)=h-%*0B92fB zFsoRc0pu8WcXs2oe6QtDvNG!M0B#&aa zBI%ywFtu?XsZ(>M#@@LZbu_71AdE6`SgraN$(nPH&jbw^iebbUswj*BQ8Q2$5NO(& z2g6nCMBm~%XOR(mg!*15S*^p=zcr>v;l$65JBmqksh!U8!Ojs9dBbxG)Naf{sn=zf zf}xgG&2dHVa!O;3m4{J|IZIWu4-7R%++lpmfD7@%2f347Ron2*(cn zc9SfhTUt&Gzd=Ds?HO-aAO(wjs|FOA5I3&+x~PIa>7E!fSy3Uq@#c;-8($WTcHCCM za5dI|Mj6LUERouAzn||eoHCSt4*r=7nz9)@fPZYwVsfCleLtT;5++@(iv7Gkn}QDL z@vu}V6Q{8SAZlT?YHijS;Z_A07Pf%kUUrh^7BaL4eP*jaIAPTWm znpf)e|@DdG^bHA;k1H{FMrN{4YBk_y!ikyh>K)USg`KuN3j<$WITSg-9Aq*sM6{A&Mn=8jz=kx=Edy%bAbqOdATRZ8CZ|uGluh4!A=Fbxn{9uzby4~Khkf$fAHT`S zduuuE^lkR|N-5WFrj&30rehpOH<|RWKwooRM&JLZf6Yu18xtWtD@MrNiKBG+v}?~^ z8G1(z=~pj@d_F%;md+X{-K*Rn;|AR(=l}UfjvPNJFAeLZ=Ws5s(do5~(&dfKKGtdc zMrr-bmr{4&Vky}?PSO)5pv)Eb2V48~w=%d*X_++aE)%%04h2z+x=kCtmM^~gQr(pEFkU0n ze-0#oonPOifE~}n&-jx-1<(B2WN@(KP_4BXtZok@k9>!~=__?Z252||tNTTuoJ&v$cNJd$=dEWUM zbc4AKHcP1?x#ZXQH6n)(%i&*tm(R!amcJetY}Gr{ z6E>c8R6e=WNj~eB)|9beid^GMMk?yGjyKnA( zbw$7;YX<&kPinzJDr~`ie`lEdlb8g?3gh&F10sL@r7^X0?OIv=$tO-bQ@}T4?pC!^ z14f+P{)z`IZ-wX68O&VuKPTL!bC{}?kf4*WJ+`Znjo)qaIsWd6Mrj1wAdMgSBxsyn zUNAGbvqOu?p9c@>pf6s$D4%?`N{${q>VT}<5U*yp)vH%)rc4QpIgC_~kvxd84lk{j z_Ajjy+>&{&^R%tb#C=)fOVhm<>_V>$o^)@vehjdhAS6e8q<6iWbHiX}`r&tAj3%F9j^=Pctqs-0}S9ljX;$gH#Qh zP<}pfQCB6>_gXo(^m+Mv-k2mej151=9HPt%)Ca}H@2^NZe>!sjY(|+3D{UlscIpNcI`7L`9A4&^Oz>6EB*8P z?rrP9QnKdWXj$5!gv@OgrS?ME0+$9nsdov%Qi4j4heuhUnRN=w>y-R`LL6G^+pp31?fEQ%RUqEl^$!4RR5&@ zK_2(zR*Cl@Eg)c&siQ4ba0}YSO5*l@3Up>|H?`y6_EuZ^OpO4zfCsc4>Z>4!3l@{Z zxuQIKSD7~YgnB?5P}qq#JIMK+PsxQt<8}YnE4Qj8el=F@;+i3Pj9aD%H>rFt0qoq` zMVE)co7EtbnGCKA|3|2h@?d4@u&w z4U%x`ZAsWSNo31(2PkpVR7v2ia_^(cfkU~FP*W=R7Qnu|YrGtY8?GQIXLMK9*jsZe z=kpN6gWiWAW{K4OS?@D%)m4rAx!dUL{}4o=R23K0#r2aF1Rx7#P2m>asS1aL1L}h& zkIN6k=gFa0m&@UgA5$;@txE!hlGZJK!0clyTgB+$c|@f`5l^T3Ygr##30SMZ$5vYR z!^MD-{c{390-#JE>yn&HQVD>XQH|f>HYdcz>mE~=G;l!eeeYJ?1BT(As1^Xm2RDto z5lg5-oti1*jtbT$5RAb|{CI`>b^kb-IqH7dKliDiaMaO8p969CJFyq<|93XP7Popz zl|nI~a7p5gxUz5x=2g+UWKb}ZD2`Mq6V!kX4HlbB(Zlz(^e7X=Xrm8*)UaS1 z9;qh*Y60YiqRu80n>4#dAAFF(>%*1pB5_rF%d}R;sQl+P`ZBeVDHS zBhB*wU;pG0$`yGnCSL$?DInY{4F+vjAGRJy&cOn*;?$Tv3WU8?>j8B?6rvvE7$CrU zU0>BK>SLt)b(I3=F@G@Ts$h>_4xyL@LmdVV2*!Xh%KWaWt@j7zLim88j60@)VNP=! zZNLKOh64uWG?vJK%x4&7tEdBP&avwGD27t6mAy}uVeeK9 zqs}T8uvWZGVD{?UB@)LZ17e3s*qV(g_gc}y`NSA%pqf=|=q*jvY~o-KkiSv^i(p+R z_SWSSP^UI^^IhzkRmQ2$yL#G8AH5^s9Pc8S0a|mvK1$fz72MRS>GSz!bB@n~m_(on z!DT>M5NpAd3VPdeey}D9=N84@p15vPJhw33+m~c+TP}IKH$x>K2n&)N%)fb~`LAs= z29%Mv9xj>UVEAm}i!ToMvDh?zlyB!d#pK|!TV&q{dF8{Wi(mc=@zLm7a`MZ&WZ%4I zvg)PaF=v6cZ<`}0Pi&XO#9eA9j(?+ddsku)c^O-`WqC6CxZhA6!WLuUpA-< z8+*$y&o(y8mtG3@c=J5s55;c*pFQ)l=bh!m^OAV+fL#1%lNvuAx_E43`6#Z7>{{MK zjpO9t0Q=(gGHL+L>YC^BL9KYPmYn;lo19ri*!&6-%r^DCw4XLqsJ}kxtYEm|@z>hOwpI7&SENRFO!^cyYT;^0D#iOi zNy2yfGofy>d@N1lJsrmO&+W;mb#`DE&efwY0K0p>pbYmV=6=?6a*2h0EkGoXUS7ht$>vHq)_ z*92tyQ&rS}vB?6Mi`!-!)#GI9s8~G)ls+lKP3m1-ti92*sE`SXFXpW&{XB2paO@A( z?B%aD#S=rsSug|KW<&b1=a?_7`h%eaV0X7|sNfiP*TC3g;Lf~#yDAqHi~%ov%8-w* zqw@u>wq<5rH8LP(kAdabAFSg?zsgg~Ryg*`%1`C9_|Ihf&egiN=I(>V#>eZqyoN$e z_QVo!3>q+upJ~Dcu~_{wLX6hC!F=FQ0vM75iGg{Fed4wOjsZe81l%SRH&~h8izOy5 zUWRNw?_+yUdj4e39{XhP(SSY}=kgkEFk*{AqYwb7V?R?&nL65nf}s>DoZZw*CtR`} z^As@@&-)q%2)CmTE}5JQGcero_P6#p#S#yNa2CU;f~C~Y z!Vnq^+Rjsytx{Gs3x!%Mhu)(Glup>M+*?%L3@95D{;muolMA}fdURN>VUvd z40EsSIoD3m$!)cUtr9F8*Zpqk4mq@Jx165(g$~vp|NO-&CwWk&jyC$({_%0HpQvYG zEQ@d{Qoc?5WIoJY28MR}a!xoj77*8BuCf!fd$Oi293bm@s6^%ijFevX#jka~3gw#atM7)h5N@N6Hg{jEazw`K zua_*@vV}C-ugRG4%KP}aYpdNQ$b$qJe2tnlq#Do1^fT&d&v5m%3W!nu&wv5J^2Fb$ zr=8oPf&~j|9s4T{E-2VdX5eM3Rtaf0xxjclLUgH83iiKt&%f&zk#ki%Kjs>9Z;*sk zAoI=U{wgM<(Qd(TO*a^I*bO(`^sho5Yg1S*zMn-dJd#7^*KRA-pI9$#rtOuEuWocK zqV~-<%io2H$^OXTHX(l@eRi!s81YuMYK*Grb=Q9fs#RgRur#yCo~vcYN2TSyjxWmM zrkw--f56TaF0OEHqYj&2qKLw=w`EjBF~2zc_n>xU6xnp0f?+6U-7-ZmYZm6K{83K7 zhw|iAIKMdjcc8Xsl*Emh)PO@d+>)3J3U;(a(V*rJX5AAwEG{Vy|5YdjL%F3l$)Vc{ z%YzmC-Xg!X+VzsSIg=#tUCJP~{p}L6a!g76u^l%UW?i=S&FKf`c0V7e?Ov!>L7*&{ ze4k0-f|&*Tb#XqkvW}3G58a|F{TpB*l~Sn8@^>gGf3Sr0zI%S@0WfY&6l~8`a_Z}> zT8E3KC)n}ZA|-M8O(MIl3+jGA4Jn_@UuYEU_lyb`V5S}*e=v?w5%sXk4R)-VXP!!2 zXTssD8$92u4#)l6TfSQ|%G3RB3~Bv3W!1eu7^^l*27kbicH2tTt}A!J`nasEVaC7|P+6K~s8qY<8MR z4_EYe%fFgM{X0-rJ%BLcG(G>X;g!l={?||Zwx?j}HR#knw(H$KA4uFi@5*ak-tzk^ zs<|6+S@KXDd&}9E>&V$x>WO^UPyW|X9}W9Retvtsy!pT)dF=Mt^5|_dWpu|`@^ss| zDS&K>K2#pc2>jmNPHbmb2sPI)eu0)bL7b7K}Ff(3f-43bnXvae2Q< z(PW<@2MC7SO^KbS>Yx-*?E5vFCc`_EqE`1oD<H&mlc9c}bU2;quu zsAeN-Ntc0@Wc#dWwO`ES{?msAW$)Y~^5x5A6zt0$p7;Iz;Q%A1<{BH@ZH~-;;6qIs zzyaxwx5wMRseNqh^=&mHh;k;b{+Vq6o~R^0j;<}c21ctfrJ`^6S!k2FzoWtvehI;bJupiQlu-)h4lF9Dt{Cc*HK>6xcx$Kvz#|=I$$%EqHI67_ z#2jFlLTOx?L6ICngY`=mj5*GN-Tq=-iHY@vQlO;7bssZTx>6h<3ZWz=R4GX&c<4bP?uv#MdCQPprFoG?(7!|iao~x%P4OQ;cmDjFgEL1 zNGJ1!eT#Ali(m#zY@yg+pP0$mmC}j5U{JxpNEEmqnNL%^Ng5c60Tz}@DT1crwuKc3 zu4s*vZ<7Qrhoa1L{&5*<#bM_ycTz~TQkUaKlfeUlI_xp4QfmKC>w3v%x+`I`e<6*I zj#kT;Kfh$Y#P=dgrS>brmvgIBxsp__SXx500ht!S`jOeCZFF`ioArMH*SSL*sa~T- zivKZ>i%bn1P-E^aqSn4#Zu!@hn?3c7u>M2fQSQ2oDgO|Ig?glE0qIja&!xd-o77XV zaQioW?pyH(t2^WqNht=2yNC>-KaJoIl_Fe;IZpa{odCy6cRa2zSs!391U{;-AV4;+=?idWrUn!2QVw8b%E9WGXN}o^A!E@Ddxt23V z$xjb2mW4~!C<(zJxy{#9(4%`lYYU?J>Ao&cHyUl;&^SxwA;_Q(j zL*%KaN2&2~j3?VgI)g_g0BYsSCKYpJRXFM_H0FR+S^AP-xLwYXZFSH<#Kp%eM1{}B zt0F*^{dAl{08@NIZ+UX^B)tgi-2=T4lUt_b%P&iE=Lst|0YkN>&6?|XV)R#()_$$u zgJ#gUpH&V}z%UDI`v6tZ{)lV-5$y!aJFJx&3f5db$$?>CdBu1Id}!%hg$AtXGo7`6 z$;aC3-scYqRg22vf`w&yxk|D=vPf88#Ujo~{V2BctF>x##y%*6YFsZ9?=PsTRnD4G zfq(-NSlq8&iJKJ2XI_i>Vv=CQN|t^#R0-gK0s=Rzx@v-|MXaF^?#g3Z;G4^|*qz4n) z<<)hWGKzhWH8DncThUVTdEQ6``)K(mYPjXzT|3o?JDWgozo1ZTsM4?9ojwf=nB+i; zM=3P}L>+DZeSu@(fbA?5sUYLCWLFCYmf`v=a%anYvVGYtCXOPcSb>bX`XG4#hH48Q zdDQ0#Bu5VYsfJRxUPz_vX0N{ghFfNNvW2hp3%*3Muz*=tv__2Kijc@b52x0Rsx9f`PH&dH`XRS$W`!fLYsIrjq>HXMp@Ze54a|%x}0P z<^RboODMH`KaSv0iTO*jqB(Yh#hceqGYP{gMy8B6b1bU;>rmDmiLVsl-09vQ%xk#j zppvCMs}a-JGYzEi509IAR=F%!q|vSQnr3i_`BF=(JZY*|u%dAz*r9ujEO zAATX}o2=jUs_c|2#Z*$DLsO^e)3yJ3{S7%g?o~Nu_NOOKR^u4RKkmL)0b^p)`3Z7B zF<_vu9S_%%-{aZ_*uH1l%8mzX=$B)b^{njtMh#H2pM6#?OrNPh3KBO}PEVfVWB*K< zE@!4plk=vGg1}|Vm`TzVYJ0Du^37dkRJBlGCqKGN_B>HfPzo^2CIO&O1OUnm{=5qc zAk+iyw6_ho4tevZ9~esfxa=LZ@A_2LfujtzeNYXjjM;Xp8VP|_fq2~wD3oAr_tWU- z6a=F@w0N{mycQ0(oEKB|@~U>M&%K%qh#Ll$hZJk(iQiu7ExR78t?C3b)FgAp>*_m3 zvmQypswFetl-|XP>tN#_9jeC`j5@L-b+{^SyYIFRQtonowiqy=^yZgQHbhsIX#H@0D<%NFaAl+d8aJU$Q*LlnU0)$%B z(MDX^I865?6>&xUpjvYMNH~CnYPWI3x)4Zfb{U0A<ZuQrd9EaBgdewTS=HmgT1FrFzNSX4M@bEi30yqg<*`cFCAA^j}2w zy5F#IZJ{3Z&afEP=|5XQUY@WPQJ9kzcd}h98Z1!%WF2i-#NtbNdg^K|c=lG8akQ)tL z^`~x^$uGYlH&!pCuwlcYlpE^YH&@HawHZ|@pn#(cTms(;TAfAVP|xj}432?XFaRhs zaJNwhAYfbwjIw>5wnM53F3H!q>OS32ZhE$XLfvds1Qgc{3k>%=b+nHf17EW+hXPqB zs~ScdeE`Gw#UuMV#WG&61I%;74HA*zDxIG)8g~N(H8ILJU44yb?lOJo%SF(0%$9Rv zQ^_);g}NPpfN`9Cb}Pv-y@lkM+FVsjwF}~E{$P9t(G6zf&8o(0Xv~TTL{uIdTZd%9EjXqoq;Bie73-QF@fw|EeW4Yp*Yn?UfQvTz%$$nNX5(Z!>g8ltv zW(SC}t*4K5M~rOApcoubhkDf%*}GR4)$hLdo4qt-LHzOAHJUgu%D5ipUyoTZ<~R?S zN3jz_hv=YjH~SaLGxh7L`PW-8;?2%+BngxQ5F>dMnb}xgoS*b-?IaNDPH)SucJ>dy zFZK{0d2h*~?qnxp>d1EX*cx@V9+fzU`OHcCUd3=lRn52~2B=LQ3QfIHjxrh1D&~LD z0pt&iv2igvc)l|V0O=m}V)zB5PqS!!033?-@77-5Tsi&mX!&F2gZe%nb(E>Yc-w$_ z-U6VW->zYz?%Sejk}F=D1ol~13l_&Zhf0G zhf%hPg+6Y@l6%^Rg~J_j)0^*>0L1>ySE1~qj%~et{IM5| zlx?%3jWN{H^UX#EY}TTsTxaGkj52j>`pcOnQ+mKsevAolsO0mvHj_-O19G1RSY1Xo5X9A6k`qpz?!ZLO@gq;`fivp6k~F}It|Ze!TnIsDi(5C zrD~v>89a+n+zddBGNz!ou2IT=rGNFcp0yzNbR-Yr2)6^oDyC|gI?msJS2x{LpB6A; z&L)mzJCfnzy>KYR-7hN(3u#s3IN0jm-yLn@VE(c(M%e_bv!>Lg)V0^8>1jIdghJv- zc5zJVCf+Qd9GI)7px(F&2IX_Fb=9@Hv|`;D%IqSU3z6J?TC#Whah|DwbtOx&+%uEz z-9ny3QX%fFk{Z z-UaUraX0**+5oZ!5O%Q8{{b+zHzT8EaGN&rLjV4{k4^8tGX7iJ1O4umZQHiVns3+1 z;nSyN$;Tf_nJSe-f0VU;J?YV-yWD&oESXP*@S0^STYFnT{_wqJhvMVdEn<^vY)Ak%VA>i*)=CsKZV|7rRA@4sc; zx^?<6YrWMl`&VCmMF;Ly%0jVm4xqsXnHIb3u6SF=E)J0Y#xpaA4jZcGhO&wQmKtUL z^*BSF2-g8_wGpKcq;Ah``Fl8PIS<6v=U0g_YkiUDIIb7EFO&PnUQ z|24RjzL&` zw;bH%OIZv4&!O1dzXx#Ho{{pu+_>dNA*_Gx+&<-q|0~b?g-VvbVV;8AiF1QgRp3>I= ziUsw@AAhWHw$9!6M6E#zaeMwE`UdRz(Tmh1HclQdTuqwi$uA4bwowZk?pU|iJy4qt zcnmmGcgb<<{k;6s&y1FByY?vDbI(2J)VZO~KH_?_{n42e)sGz1DyRlB+6OEY}u;G;DJINo)*d56qKpQ1p&e}t!nY6&!Lpy z82GW#3p^m-{VK=jt@D@{jok z#0Lz8h$}Y~84#fH-HWMFrjE8lE;#l+U<_2%`uICZxR9XcxT94KB>^*iRJ9&yhkKw+ zEBR&dMEUHc5klG4GccgYfomro@3j_;A~9cR;WM*WWd8aMmb=Vsk7P99b>@ii~Z2_ zNdfFc->II#+bn2Z5fH#!iqZF2kBNF-fEu~8%hSd3%5&w41{iL_pP9~1>MiG|n1Qq^ z?E_}#E_?NR)yS#J=o%Uc+SvWFUZg0{+ z-}4;Vy@tBr$TkJEOdV~&w=0$1aYNr2GlNt!adT|^$4T=SiNounK+#4YbzQ%-|3snO z@@dC1^6OJ=B=NHs@y+p+YWo&7>IX@~THQ;7BCK^?=Yi;t3$?x_h87l(y&h%brySiCX&}X^&{BIHS#i+2rE$VxP3`FTmMYJ7HXnfnz9RVbn}N>ia%? zySU?OTFod0+c~h74%~vcfh-j8#1|JNSb}kbxf6zUJ9i?nU_d%Akh5=9sW`cjRG!mR zQ|xyQA5;UBfn$_yJw`u!4trk1D#l<@0ytdl&*yIUG3sdJ0-%=L4TIf20R{vvM-1U+ z7-fqLkHPtVRIk5bwx`ZI2Nppb2!%>W2i=)0T;?>=OXmU5?n1(II%*U zG4OKPvMR(Q#q#(Vb+*kW56%sM1AqZrW8kP^!QE~2bwjZ+P~aFefMh&>qaLL~L9sDd zj6t%u@q;oJip*$NAhy4?fETn7);JEQ&3|Li19N5zYRLfw+fdEM88E=HC!W{?jk^EI zyLbzRLT(^y|LM0V0CT7I6rrxaF0<4tT1?*r&6Fitvg9t~W7N|p&3{xZ8ZDbjlvLXo zRZ`aFEh%dvON4yA9yeY3*Fy9)Drd{ir?osEV~vZK@=e}xtihcNW#!79@>G$cLO(BH zSpV^s(xv6cF2&{Wv-#!t#5`)0_co7~4FyWd&RWH_F4VxVZa0R1LG3xtg^7%=Dk~nH zEazIa*6%F?0#MQFuSvCbb7awnt7Kx1oU&@vNa^;#0?A*riz*xHKR}uF#jDCMJ&H-< zx*I*ufpFPBA7@o4jCSn9CbtBAy;lkp(skdkqQzy9H^B-ruPIq3hl9XpUTgVAl0eWK-`E3V8a%r80H?UI%RF>*F2RTU$P?)~i!*b<-HH zd-8(^xEi*lYK+1Kb4B`Dccc$4yEHn`3w8oFbC#TX>S;ONGgeOY?klJJ4N#+eGPaML z=+R5Qt5Z)N%;NX8FkF$?LJbTUpw2)2jGW)MU*A2lV6*}Le(Sbs^dUCwUtbs`S07`r zCku8U=!p5gFiVzWEx&R^QXj zaYZ2LtDnM-^|&S~qB>U}Abl#1aY5A4kE0vc1W2ub2ZD02pSO57()W3sZZIIu_UR`x zD#ggs`VHiaDO=Tof@xMX12|r~d{CQSELy<;B)M$+;6nu?&WJZ$7%&ua$JD_G3;95> zM>#G4`(SXF6%02bS-1@s#2qm}h@*HDM}WqA-z$$bijj24p#ESeM=V*l1Hz_v+SqeU zJ#!ldY)~*0V<(>&9u&;R*xKqfgJ(qs$CYQG#1YDnKt6wUh7)7NmcknM1dJH77lMmJ z@Y_T|tPxW-w&Dwy^<=`%OcVg3W%R6gzS6eezcEXbG6u zZC13CF);)bF+`m^-RmFPy)lN1q9ATu+4U7Et~$m*-S$=c{IcPJtd4PP`}za9x1R#T)~4X&M6 zUN-k$*ar_cQ=^RHw1xGzI@9;hCr8M|!9&z6*m(m96lw)sq2NY3VgrS{Stz%P?sTuo z!y989(;n)N?L%8c>Ib6%;`789W$I`P`wx-k^;a4gVh#}Ch%1x|h%I+kBspMAx@0@I zp$R#c+K2^b4xp~6ib{}c64Dy17y05D?8mj4Z?{|^=xbWHWlB^Uqz03~!qSaf4@ zWnpw>Eo5PIWdJfTFgPtRIV~_WR53R?GdVgkG%GMTIxsNDRrufl001R)MObugZ)9m^ zc`amNbY%cCFfceRFgYzSG*mG+Ix{&sGBhhNI65#enB + + + +goog.events + + + + + + + +

goog.events - Stop Propagation

+

Test the cancelling of capture and bubbling events. Click + one of the nodes to see the event trace, then use the check boxes to cancel the + capture or bubble at a given branch. + (Double click the text area to clear it)

+ +
+ +
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/events.html b/mapbox-gl/closure-library/closure/goog/demos/events.html new file mode 100644 index 0000000000..314260db70 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/events.html @@ -0,0 +1,94 @@ + + + + Event Test + + + + +

+ Link 1
+ Link 2
+ Link 3
+ Link 4 +

+

+ Listen | + UnListen | + Remove One | + Remove Two | + Remove Three | +

+

+
+  
+ Test 1 +
+     Test 2 +
+         Test 3 +
+     Test 2 +
+ Test 1 +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/eventtarget.html b/mapbox-gl/closure-library/closure/goog/demos/eventtarget.html new file mode 100644 index 0000000000..b26250e385 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/eventtarget.html @@ -0,0 +1,70 @@ + + + + Event Test + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/filedrophandler.html b/mapbox-gl/closure-library/closure/goog/demos/filedrophandler.html new file mode 100644 index 0000000000..1c14928e7d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/filedrophandler.html @@ -0,0 +1,65 @@ + + + + + goog.events.FileDropHandler Demo + + + + + +

Demo of goog.events.FileDropHandler

+ +
+ Demo of the goog.events.FileDropHandler: + + +
+ +
+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/filteredmenu.html b/mapbox-gl/closure-library/closure/goog/demos/filteredmenu.html new file mode 100644 index 0000000000..b07a58b946 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/filteredmenu.html @@ -0,0 +1,118 @@ + + + + +goog.ui.FilteredMenu + + + + + + + + + + + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/focushandler.html b/mapbox-gl/closure-library/closure/goog/demos/focushandler.html new file mode 100644 index 0000000000..b29bc100bf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/focushandler.html @@ -0,0 +1,58 @@ + + + + +goog.events.FocusHandler + + + + + +

goog.events.FocusHandler

+

i1: + + +

i2 + + +

i3: + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/fpsdisplay.html b/mapbox-gl/closure-library/closure/goog/demos/fpsdisplay.html new file mode 100644 index 0000000000..65f9c5015c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/fpsdisplay.html @@ -0,0 +1,50 @@ + + + + +FPS Display + + + + +

+ + + +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/fx/css3/transition.html b/mapbox-gl/closure-library/closure/goog/demos/fx/css3/transition.html new file mode 100644 index 0000000000..30d70c0cd2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/fx/css3/transition.html @@ -0,0 +1,223 @@ + + + + + +Closure: CSS3 Transition Demo + + + + + + + + +
+
+
+ CSS3 transition choices +
+
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
Hi there!
+
+ + +
+ Event log for the transition object +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/gauge.html b/mapbox-gl/closure-library/closure/goog/demos/gauge.html new file mode 100644 index 0000000000..cc7ac08fc3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/gauge.html @@ -0,0 +1,158 @@ + + + + + goog.ui.Gauge + + + + + + + + + +

goog.ui.Gauge

+

Note: This component requires vector graphics support

+ + + + + + + + + + + + + +
+ Basic + + Background colors, title. custom ticks + + Value change, formatted value, tick labels + + Custom colors +
+ + + + + +
+ + +
+
+ +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates.html new file mode 100644 index 0000000000..6274fda217 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates.html @@ -0,0 +1,141 @@ + + + + + Graphics Advanced Coordinates Demo Page + + + + + + + +
+

+ W: + H: + R: +

+ +

The front ellipse is sized based on absolute units. The back ellipse is + sized based on percentage of the parent.

+ +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates2.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates2.html new file mode 100644 index 0000000000..f05323fe03 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/advancedcoordinates2.html @@ -0,0 +1,130 @@ + + + + + Graphics Advanced Coordinates Demo Page - + Using Percentage Based Surface Size + + + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/basicelements.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/basicelements.html new file mode 100644 index 0000000000..d55645af1c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/basicelements.html @@ -0,0 +1,264 @@ + + + + + + Graphics Basic Elements Demo Page + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Text: fonts, alignment, vertical-alignment, direction + + Basic shapes: Rectangle, Circle, Ellipses, Path, Clip to canvas +
+ + + +
+ Paths: Lines, arcs, curves + + Colors: solid, gradients, transparency +
+ + + +
+ Coordinate scaling + stroke types +
+ +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/events.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/events.html new file mode 100644 index 0000000000..40abb31073 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/events.html @@ -0,0 +1,114 @@ + + + + + Graphics Basic events Demo Page + + + + + + + + +
+ +
+ +
+ +
+ +

+ Clear Log +

+ +
+ +

Try to mouse over, mouse out, or click the ellipse and the group of + circles. The ellipse will be disposed in 10 sec. +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/modifyelements.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/modifyelements.html new file mode 100644 index 0000000000..1d21c583ba --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/modifyelements.html @@ -0,0 +1,195 @@ + + + + + + Modifing Graphic Elements Demo + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Colors (stroke/fill): + + + + + +
Rectangle position: + + + +
Rectangle size: + + + +
Ellipse center: + + + +
Ellipse radius: + + + +
Path: + + +
Text: + +
+ + +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/subpixel.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/subpixel.html new file mode 100644 index 0000000000..947eae2820 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/subpixel.html @@ -0,0 +1,80 @@ + + + + +Sub pixel rendering + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/tiger.html b/mapbox-gl/closure-library/closure/goog/demos/graphics/tiger.html new file mode 100644 index 0000000000..64deebcdde --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/tiger.html @@ -0,0 +1,105 @@ + + + + +The SVG tiger drawn with goog.graphics + + + + + + + +
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/graphics/tigerdata.js b/mapbox-gl/closure-library/closure/goog/demos/graphics/tigerdata.js new file mode 100644 index 0000000000..633c55cfa6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/graphics/tigerdata.js @@ -0,0 +1,2841 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview This data is generated from an SVG image of a tiger. + * + * @author arv@google.com (Erik Arvidsson) + */ + + +var tigerData = [{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [77.696, 284.285]}, + {t: 'C', p: [77.696, 284.285, 77.797, 286.179, 76.973, 286.16]}, + {t: 'C', p: [76.149, 286.141, 59.695, 238.066, 39.167, 240.309]}, + {t: 'C', p: [39.167, 240.309, 56.95, 232.956, 77.696, 284.285]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [81.226, 281.262]}, + {t: 'C', p: [81.226, 281.262, 80.677, 283.078, 79.908, 282.779]}, + {t: 'C', p: [79.14, 282.481, 80.023, 231.675, 59.957, 226.801]}, + {t: 'C', p: [59.957, 226.801, 79.18, 225.937, 81.226, 281.262]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [108.716, 323.59]}, + {t: 'C', p: [108.716, 323.59, 110.352, 324.55, 109.882, 325.227]}, + {t: 'C', p: [109.411, 325.904, 60.237, 313.102, 50.782, 331.459]}, + {t: 'C', p: [50.782, 331.459, 54.461, 312.572, 108.716, 323.59]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [105.907, 333.801]}, + {t: 'C', p: [105.907, 333.801, 107.763, 334.197, 107.529, 334.988]}, + {t: 'C', p: [107.296, 335.779, 56.593, 339.121, 53.403, 359.522]}, + {t: 'C', p: [53.403, 359.522, 50.945, 340.437, 105.907, 333.801]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [101.696, 328.276]}, + {t: 'C', p: [101.696, 328.276, 103.474, 328.939, 103.128, 329.687]}, + {t: 'C', p: [102.782, 330.435, 52.134, 326.346, 46.002, 346.064]}, + {t: 'C', p: [46.002, 346.064, 46.354, 326.825, 101.696, 328.276]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [90.991, 310.072]}, + {t: 'C', p: [90.991, 310.072, 92.299, 311.446, 91.66, 311.967]}, + {t: 'C', p: [91.021, 312.488, 47.278, 286.634, 33.131, 301.676]}, + {t: 'C', p: [33.131, 301.676, 41.872, 284.533, 90.991, 310.072]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [83.446, 314.263]}, + {t: 'C', p: [83.446, 314.263, 84.902, 315.48, 84.326, 316.071]}, + {t: 'C', p: [83.75, 316.661, 37.362, 295.922, 25.008, 312.469]}, + {t: 'C', p: [25.008, 312.469, 31.753, 294.447, 83.446, 314.263]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [80.846, 318.335]}, + {t: 'C', p: [80.846, 318.335, 82.454, 319.343, 81.964, 320.006]}, + {t: 'C', p: [81.474, 320.669, 32.692, 306.446, 22.709, 324.522]}, + {t: 'C', p: [22.709, 324.522, 26.934, 305.749, 80.846, 318.335]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [91.58, 318.949]}, + {t: 'C', p: [91.58, 318.949, 92.702, 320.48, 92.001, 320.915]}, + {t: 'C', p: [91.3, 321.35, 51.231, 290.102, 35.273, 303.207]}, + {t: 'C', p: [35.273, 303.207, 46.138, 287.326, 91.58, 318.949]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [71.8, 290]}, + {t: 'C', p: [71.8, 290, 72.4, 291.8, 71.6, 292]}, + {t: 'C', p: [70.8, 292.2, 42.2, 250.2, 22.999, 257.8]}, + {t: 'C', p: [22.999, 257.8, 38.2, 246, 71.8, 290]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [72.495, 296.979]}, + {t: 'C', p: [72.495, 296.979, 73.47, 298.608, 72.731, 298.975]}, + {t: 'C', p: [71.993, 299.343, 35.008, 264.499, 17.899, 276.061]}, + {t: 'C', p: [17.899, 276.061, 30.196, 261.261, 72.495, 296.979]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.172}, + p: [{t: 'M', p: [72.38, 301.349]}, + {t: 'C', p: [72.38, 301.349, 73.502, 302.88, 72.801, 303.315]}, + {t: 'C', p: [72.1, 303.749, 32.031, 272.502, 16.073, 285.607]}, + {t: 'C', p: [16.073, 285.607, 26.938, 269.726, 72.38, 301.349]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: '#000', p: [{t: 'M', p: [70.17, 303.065]}, + {t: 'C', p: [70.673, 309.113, 71.661, 315.682, 73.4, 318.801]}, + {t: 'C', p: [73.4, 318.801, 69.8, 331.201, 78.6, 344.401]}, + {t: 'C', p: [78.6, 344.401, 78.2, 351.601, 79.8, 354.801]}, + {t: 'C', p: [79.8, 354.801, 83.8, 363.201, 88.6, 364.001]}, + {t: 'C', p: [92.484, 364.648, 101.207, 367.717, 111.068, 369.121]}, + {t: 'C', p: [111.068, 369.121, 128.2, 383.201, 125, 396.001]}, + {t: 'C', p: [125, 396.001, 124.6, 412.401, 121, 414.001]}, + {t: 'C', p: [121, 414.001, 132.6, 402.801, 123, 419.601]}, + {t: 'L', p: [118.6, 438.401]}, + {t: 'C', p: [118.6, 438.401, 144.2, 416.801, 128.6, 435.201]}, + {t: 'L', p: [118.6, 461.201]}, + {t: 'C', p: [118.6, 461.201, 138.2, 442.801, 131, 451.201]}, + {t: 'L', p: [127.8, 460.001]}, + {t: 'C', p: [127.8, 460.001, 171, 432.801, 140.2, 462.401]}, + {t: 'C', p: [140.2, 462.401, 148.2, 458.801, 152.6, 461.601]}, + {t: 'C', p: [152.6, 461.601, 159.4, 460.401, 158.6, 462.001]}, + {t: 'C', p: [158.6, 462.001, 137.8, 472.401, 134.2, 490.801]}, + {t: 'C', p: [134.2, 490.801, 142.6, 480.801, 139.4, 491.601]}, + {t: 'L', p: [139.8, 503.201]}, + {t: 'C', p: [139.8, 503.201, 143.8, 481.601, 143.4, 519.201]}, + {t: 'C', p: [143.4, 519.201, 162.6, 501.201, 151, 522.001]}, + {t: 'L', p: [151, 538.801]}, + {t: 'C', p: [151, 538.801, 166.2, 522.401, 159.8, 535.201]}, + {t: 'C', p: [159.8, 535.201, 169.8, 526.401, 165.8, 541.601]}, + {t: 'C', p: [165.8, 541.601, 165, 552.001, 169.4, 540.801]}, + {t: 'C', p: [169.4, 540.801, 185.4, 510.201, 179.4, 536.401]}, + {t: 'C', p: [179.4, 536.401, 178.6, 555.601, 183.4, 540.801]}, + {t: 'C', p: [183.4, 540.801, 183.8, 551.201, 193, 558.401]}, + {t: 'C', p: [193, 558.401, 191.8, 507.601, 204.6, 543.601]}, + {t: 'L', p: [208.6, 560.001]}, + {t: 'C', p: [208.6, 560.001, 211.4, 550.801, 211, 545.601]}, + {t: 'C', p: [211, 545.601, 225.8, 529.201, 219, 553.601]}, + {t: 'C', p: [219, 553.601, 234.2, 530.801, 231, 544.001]}, + {t: 'C', p: [231, 544.001, 223.4, 560.001, 225, 564.801]}, + {t: 'C', p: [225, 564.801, 241.8, 530.001, 243, 528.401]}, + {t: 'C', p: [243, 528.401, 241, 570.802, 251.8, 534.801]}, + {t: 'C', p: [251.8, 534.801, 257.4, 546.801, 254.6, 551.201]}, + {t: 'C', p: [254.6, 551.201, 262.6, 543.201, 261.8, 540.001]}, + {t: 'C', p: [261.8, 540.001, 266.4, 531.801, 269.2, 545.401]}, + {t: 'C', p: [269.2, 545.401, 271, 554.801, 272.6, 551.601]}, + {t: 'C', p: [272.6, 551.601, 276.6, 575.602, 277.8, 552.801]}, + {t: 'C', p: [277.8, 552.801, 279.4, 539.201, 272.2, 527.601]}, + {t: 'C', p: [272.2, 527.601, 273, 524.401, 270.2, 520.401]}, + {t: 'C', p: [270.2, 520.401, 283.8, 542.001, 276.6, 513.201]}, + {t: 'C', p: [276.6, 513.201, 287.801, 521.201, 289.001, 521.201]}, + {t: 'C', p: [289.001, 521.201, 275.4, 498.001, 284.2, 502.801]}, + {t: 'C', p: [284.2, 502.801, 279, 492.401, 297.001, 504.401]}, + {t: 'C', p: [297.001, 504.401, 281, 488.401, 298.601, 498.001]}, + {t: 'C', p: [298.601, 498.001, 306.601, 504.401, 299.001, 494.401]}, + {t: 'C', p: [299.001, 494.401, 284.6, 478.401, 306.601, 496.401]}, + {t: 'C', p: [306.601, 496.401, 318.201, 512.801, 319.001, 515.601]}, + {t: 'C', p: [319.001, 515.601, 309.001, 486.401, 304.601, 483.601]}, + {t: 'C', p: [304.601, 483.601, 313.001, 447.201, 354.201, 462.801]}, + {t: 'C', p: [354.201, 462.801, 361.001, 480.001, 365.401, 461.601]}, + {t: 'C', p: [365.401, 461.601, 378.201, 455.201, 389.401, 482.801]}, + {t: 'C', p: [389.401, 482.801, 393.401, 469.201, 392.601, 466.401]}, + {t: 'C', p: [392.601, 466.401, 399.401, 467.601, 398.601, 466.401]}, + {t: 'C', p: [398.601, 466.401, 411.801, 470.801, 413.001, 470.001]}, + {t: 'C', p: [413.001, 470.001, 419.801, 476.801, 420.201, 473.201]}, + {t: 'C', p: [420.201, 473.201, 429.401, 476.001, 427.401, 472.401]}, + {t: 'C', p: [427.401, 472.401, 436.201, 488.001, 436.601, 491.601]}, + {t: 'L', p: [439.001, 477.601]}, + {t: 'L', p: [441.001, 480.401]}, + {t: 'C', p: [441.001, 480.401, 442.601, 472.801, 441.801, 471.601]}, + {t: 'C', p: [441.001, 470.401, 461.801, 478.401, 466.601, 499.201]}, + {t: 'L', p: [468.601, 507.601]}, + {t: 'C', p: [468.601, 507.601, 474.601, 492.801, 473.001, 488.801]}, + {t: 'C', p: [473.001, 488.801, 478.201, 489.601, 478.601, 494.001]}, + {t: 'C', p: [478.601, 494.001, 482.601, 470.801, 477.801, 464.801]}, + {t: 'C', p: [477.801, 464.801, 482.201, 464.001, 483.401, 467.601]}, + {t: 'L', p: [483.401, 460.401]}, + {t: 'C', p: [483.401, 460.401, 490.601, 461.201, 490.601, 458.801]}, + {t: 'C', p: [490.601, 458.801, 495.001, 454.801, 497.001, 459.601]}, + {t: 'C', p: [497.001, 459.601, 484.601, 424.401, 503.001, 443.601]}, + {t: 'C', p: [503.001, 443.601, 510.201, 454.401, 506.601, 435.601]}, + {t: 'C', p: [503.001, 416.801, 499.001, 415.201, 503.801, 414.801]}, + {t: 'C', p: [503.801, 414.801, 504.601, 411.201, 502.601, 409.601]}, + {t: 'C', p: [500.601, 408.001, 503.801, 409.601, 503.801, 409.601]}, + {t: 'C', p: [503.801, 409.601, 508.601, 413.601, 503.401, 391.601]}, + {t: 'C', p: [503.401, 391.601, 509.801, 393.201, 497.801, 364.001]}, + {t: 'C', p: [497.801, 364.001, 500.601, 361.601, 496.601, 353.201]}, + {t: 'C', p: [496.601, 353.201, 504.601, 357.601, 507.401, 356.001]}, + {t: 'C', p: [507.401, 356.001, 507.001, 354.401, 503.801, 350.401]}, + {t: 'C', p: [503.801, 350.401, 482.201, 295.6, 502.601, 317.601]}, + {t: 'C', p: [502.601, 317.601, 514.451, 331.151, 508.051, 308.351]}, + {t: 'C', p: [508.051, 308.351, 498.94, 284.341, 499.717, 280.045]}, + {t: 'L', p: [70.17, 303.065]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: '#000', p: [{t: 'M', p: [499.717, 280.245]}, + {t: 'C', p: [500.345, 280.426, 502.551, 281.55, 503.801, 283.2]}, + {t: 'C', p: [503.801, 283.2, 510.601, 294, 505.401, 275.6]}, + {t: 'C', p: [505.401, 275.6, 496.201, 246.8, 505.001, 258]}, + {t: 'C', p: [505.001, 258, 511.001, 265.2, 507.801, 251.6]}, + {t: 'C', p: [503.936, 235.173, 501.401, 228.8, 501.401, 228.8]}, + {t: 'C', p: [501.401, 228.8, 513.001, 233.6, 486.201, 194]}, + {t: 'L', p: [495.001, 197.6]}, + {t: 'C', p: [495.001, 197.6, 475.401, 158, 453.801, 152.8]}, + {t: 'L', p: [445.801, 146.8]}, + {t: 'C', p: [445.801, 146.8, 484.201, 108.8, 471.401, 72]}, + {t: 'C', p: [471.401, 72, 464.601, 66.8, 455.001, 76]}, + {t: 'C', p: [455.001, 76, 448.601, 80.8, 442.601, 79.2]}, + {t: 'C', p: [442.601, 79.2, 411.801, 80.4, 409.801, 80.4]}, + {t: 'C', p: [407.801, 80.4, 373.001, 43.2, 307.401, 60.8]}, + {t: 'C', p: [307.401, 60.8, 302.201, 62.8, 297.801, 61.6]}, + {t: 'C', p: [297.801, 61.6, 279.4, 45.6, 230.6, 68.4]}, + {t: 'C', p: [230.6, 68.4, 220.6, 70.4, 219, 70.4]}, + {t: 'C', p: [217.4, 70.4, 214.6, 70.4, 206.6, 76.8]}, + {t: 'C', p: [198.6, 83.2, 198.2, 84, 196.2, 85.6]}, + {t: 'C', p: [196.2, 85.6, 179.8, 96.8, 175, 97.6]}, + {t: 'C', p: [175, 97.6, 163.4, 104, 159, 114]}, + {t: 'L', p: [155.4, 115.2]}, + {t: 'C', p: [155.4, 115.2, 153.8, 122.4, 153.4, 123.6]}, + {t: 'C', p: [153.4, 123.6, 148.6, 127.2, 147.8, 132.8]}, + {t: 'C', p: [147.8, 132.8, 139, 138.8, 139.4, 143.2]}, + {t: 'C', p: [139.4, 143.2, 137.8, 148.4, 137, 153.2]}, + {t: 'C', p: [137, 153.2, 129.8, 158, 130.6, 160.8]}, + {t: 'C', p: [130.6, 160.8, 123, 174.8, 124.2, 181.6]}, + {t: 'C', p: [124.2, 181.6, 117.8, 181.2, 115, 183.6]}, + {t: 'C', p: [115, 183.6, 114.2, 188.4, 112.6, 188.8]}, + {t: 'C', p: [112.6, 188.8, 109.8, 190, 112.2, 194]}, + {t: 'C', p: [112.2, 194, 110.6, 196.8, 110.2, 198.4]}, + {t: 'C', p: [110.2, 198.4, 111, 201.2, 106.6, 206.8]}, + {t: 'C', p: [106.6, 206.8, 100.2, 225.6, 102.2, 230.8]}, + {t: 'C', p: [102.2, 230.8, 102.6, 235.6, 99.8, 237.2]}, + {t: 'C', p: [99.8, 237.2, 96.2, 236.8, 104.6, 248.8]}, + {t: 'C', p: [104.6, 248.8, 105.4, 250, 102.2, 252.4]}, + {t: 'C', p: [102.2, 252.4, 85, 256, 82.6, 272.4]}, + {t: 'C', p: [82.6, 272.4, 69, 287.2, 69, 292.4]}, + {t: 'C', p: [69, 294.705, 69.271, 297.852, 69.97, 302.465]}, + {t: 'C', p: [69.97, 302.465, 69.4, 310.801, 97, 311.601]}, + {t: 'C', p: [124.6, 312.401, 499.717, 280.245, 499.717, 280.245]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [84.4, 302.6]}, + {t: 'C', p: [59.4, 263.2, 73.8, 319.601, 73.8, 319.601]}, + {t: 'C', p: [82.6, 354.001, 212.2, 316.401, 212.2, 316.401]}, + {t: 'C', p: [212.2, 316.401, 381.001, 286, 392.201, 282]}, + {t: 'C', p: [403.401, 278, 498.601, 284.4, 498.601, 284.4]}, + {t: 'L', p: [493.001, 267.6]}, + {t: 'C', p: [428.201, 221.2, 409.001, 244.4, 395.401, 240.4]}, + {t: 'C', p: [381.801, 236.4, 384.201, 246, 381.001, 246.8]}, + {t: 'C', p: [377.801, 247.6, 338.601, 222.8, 332.201, 223.6]}, + {t: 'C', p: [325.801, 224.4, 300.459, 200.649, 315.401, 232.4]}, + {t: 'C', p: [331.401, 266.4, 257, 271.6, 240.2, 260.4]}, + {t: 'C', p: [223.4, 249.2, 247.4, 278.8, 247.4, 278.8]}, + {t: 'C', p: [265.8, 298.8, 231.4, 282, 231.4, 282]}, + {t: 'C', p: [197, 269.2, 173, 294.8, 169.8, 295.6]}, + {t: 'C', p: [166.6, 296.4, 161.8, 299.6, 161, 293.2]}, + {t: 'C', p: [160.2, 286.8, 152.69, 270.099, 121, 296.4]}, + {t: 'C', p: [101, 313.001, 87.2, 291, 87.2, 291]}, + {t: 'L', p: [84.4, 302.6]}, + {t: 'z', p: []}]}, + +{f: '#e87f3a', s: null, p: [{t: 'M', p: [333.51, 225.346]}, + {t: 'C', p: [327.11, 226.146, 301.743, 202.407, 316.71, 234.146]}, + {t: 'C', p: [333.31, 269.346, 258.31, 273.346, 241.51, 262.146]}, + {t: 'C', p: [224.709, 250.946, 248.71, 280.546, 248.71, 280.546]}, + {t: 'C', p: [267.11, 300.546, 232.709, 283.746, 232.709, 283.746]}, + {t: 'C', p: [198.309, 270.946, 174.309, 296.546, 171.109, 297.346]}, + {t: 'C', p: [167.909, 298.146, 163.109, 301.346, 162.309, 294.946]}, + {t: 'C', p: [161.509, 288.546, 154.13, 272.012, 122.309, 298.146]}, + {t: 'C', p: [101.073, 315.492, 87.582, 294.037, 87.582, 294.037]}, + {t: 'L', p: [84.382, 304.146]}, + {t: 'C', p: [59.382, 264.346, 74.454, 322.655, 74.454, 322.655]}, + {t: 'C', p: [83.255, 357.056, 213.509, 318.146, 213.509, 318.146]}, + {t: 'C', p: [213.509, 318.146, 382.31, 287.746, 393.51, 283.746]}, + {t: 'C', p: [404.71, 279.746, 499.038, 286.073, 499.038, 286.073]}, + {t: 'L', p: [493.51, 268.764]}, + {t: 'C', p: [428.71, 222.364, 410.31, 246.146, 396.71, 242.146]}, + {t: 'C', p: [383.11, 238.146, 385.51, 247.746, 382.31, 248.546]}, + {t: 'C', p: [379.11, 249.346, 339.91, 224.546, 333.51, 225.346]}, + {t: 'z', p: []}]}, + +{f: '#ea8c4d', s: null, p: [{t: 'M', p: [334.819, 227.091]}, + {t: 'C', p: [328.419, 227.891, 303.685, 203.862, 318.019, 235.891]}, + {t: 'C', p: [334.219, 272.092, 259.619, 275.092, 242.819, 263.892]}, + {t: 'C', p: [226.019, 252.692, 250.019, 282.292, 250.019, 282.292]}, + {t: 'C', p: [268.419, 302.292, 234.019, 285.492, 234.019, 285.492]}, + {t: 'C', p: [199.619, 272.692, 175.618, 298.292, 172.418, 299.092]}, + {t: 'C', p: [169.218, 299.892, 164.418, 303.092, 163.618, 296.692]}, + {t: 'C', p: [162.818, 290.292, 155.57, 273.925, 123.618, 299.892]}, + {t: 'C', p: [101.145, 317.983, 87.964, 297.074, 87.964, 297.074]}, + {t: 'L', p: [84.364, 305.692]}, + {t: 'C', p: [60.564, 266.692, 75.109, 325.71, 75.109, 325.71]}, + {t: 'C', p: [83.909, 360.11, 214.819, 319.892, 214.819, 319.892]}, + {t: 'C', p: [214.819, 319.892, 383.619, 289.492, 394.819, 285.492]}, + {t: 'C', p: [406.019, 281.492, 499.474, 287.746, 499.474, 287.746]}, + {t: 'L', p: [494.02, 269.928]}, + {t: 'C', p: [429.219, 223.528, 411.619, 247.891, 398.019, 243.891]}, + {t: 'C', p: [384.419, 239.891, 386.819, 249.491, 383.619, 250.292]}, + {t: 'C', p: [380.419, 251.092, 341.219, 226.291, 334.819, 227.091]}, + {t: 'z', p: []}]}, + +{f: '#ec9961', s: null, p: [{t: 'M', p: [336.128, 228.837]}, + {t: 'C', p: [329.728, 229.637, 304.999, 205.605, 319.328, 237.637]}, + {t: 'C', p: [336.128, 275.193, 260.394, 276.482, 244.128, 265.637]}, + {t: 'C', p: [227.328, 254.437, 251.328, 284.037, 251.328, 284.037]}, + {t: 'C', p: [269.728, 304.037, 235.328, 287.237, 235.328, 287.237]}, + {t: 'C', p: [200.928, 274.437, 176.928, 300.037, 173.728, 300.837]}, + {t: 'C', p: [170.528, 301.637, 165.728, 304.837, 164.928, 298.437]}, + {t: 'C', p: [164.128, 292.037, 157.011, 275.839, 124.927, 301.637]}, + {t: 'C', p: [101.218, 320.474, 88.345, 300.11, 88.345, 300.11]}, + {t: 'L', p: [84.345, 307.237]}, + {t: 'C', p: [62.545, 270.437, 75.764, 328.765, 75.764, 328.765]}, + {t: 'C', p: [84.564, 363.165, 216.128, 321.637, 216.128, 321.637]}, + {t: 'C', p: [216.128, 321.637, 384.928, 291.237, 396.129, 287.237]}, + {t: 'C', p: [407.329, 283.237, 499.911, 289.419, 499.911, 289.419]}, + {t: 'L', p: [494.529, 271.092]}, + {t: 'C', p: [429.729, 224.691, 412.929, 249.637, 399.329, 245.637]}, + {t: 'C', p: [385.728, 241.637, 388.128, 251.237, 384.928, 252.037]}, + {t: 'C', p: [381.728, 252.837, 342.528, 228.037, 336.128, 228.837]}, + {t: 'z', p: []}]}, + +{f: '#eea575', s: null, p: [{t: 'M', p: [337.438, 230.583]}, + {t: 'C', p: [331.037, 231.383, 306.814, 207.129, 320.637, 239.383]}, + {t: 'C', p: [337.438, 278.583, 262.237, 278.583, 245.437, 267.383]}, + {t: 'C', p: [228.637, 256.183, 252.637, 285.783, 252.637, 285.783]}, + {t: 'C', p: [271.037, 305.783, 236.637, 288.983, 236.637, 288.983]}, + {t: 'C', p: [202.237, 276.183, 178.237, 301.783, 175.037, 302.583]}, + {t: 'C', p: [171.837, 303.383, 167.037, 306.583, 166.237, 300.183]}, + {t: 'C', p: [165.437, 293.783, 158.452, 277.752, 126.237, 303.383]}, + {t: 'C', p: [101.291, 322.965, 88.727, 303.146, 88.727, 303.146]}, + {t: 'L', p: [84.327, 308.783]}, + {t: 'C', p: [64.527, 273.982, 76.418, 331.819, 76.418, 331.819]}, + {t: 'C', p: [85.218, 366.22, 217.437, 323.383, 217.437, 323.383]}, + {t: 'C', p: [217.437, 323.383, 386.238, 292.983, 397.438, 288.983]}, + {t: 'C', p: [408.638, 284.983, 500.347, 291.092, 500.347, 291.092]}, + {t: 'L', p: [495.038, 272.255]}, + {t: 'C', p: [430.238, 225.855, 414.238, 251.383, 400.638, 247.383]}, + {t: 'C', p: [387.038, 243.383, 389.438, 252.983, 386.238, 253.783]}, + {t: 'C', p: [383.038, 254.583, 343.838, 229.783, 337.438, 230.583]}, + {t: 'z', p: []}]}, + +{f: '#f1b288', s: null, p: [{t: 'M', p: [338.747, 232.328]}, + {t: 'C', p: [332.347, 233.128, 306.383, 209.677, 321.947, 241.128]}, + {t: 'C', p: [341.147, 279.928, 263.546, 280.328, 246.746, 269.128]}, + {t: 'C', p: [229.946, 257.928, 253.946, 287.528, 253.946, 287.528]}, + {t: 'C', p: [272.346, 307.528, 237.946, 290.728, 237.946, 290.728]}, + {t: 'C', p: [203.546, 277.928, 179.546, 303.528, 176.346, 304.328]}, + {t: 'C', p: [173.146, 305.128, 168.346, 308.328, 167.546, 301.928]}, + {t: 'C', p: [166.746, 295.528, 159.892, 279.665, 127.546, 305.128]}, + {t: 'C', p: [101.364, 325.456, 89.109, 306.183, 89.109, 306.183]}, + {t: 'L', p: [84.309, 310.328]}, + {t: 'C', p: [66.309, 277.128, 77.073, 334.874, 77.073, 334.874]}, + {t: 'C', p: [85.873, 369.274, 218.746, 325.128, 218.746, 325.128]}, + {t: 'C', p: [218.746, 325.128, 387.547, 294.728, 398.747, 290.728]}, + {t: 'C', p: [409.947, 286.728, 500.783, 292.764, 500.783, 292.764]}, + {t: 'L', p: [495.547, 273.419]}, + {t: 'C', p: [430.747, 227.019, 415.547, 253.128, 401.947, 249.128]}, + {t: 'C', p: [388.347, 245.128, 390.747, 254.728, 387.547, 255.528]}, + {t: 'C', p: [384.347, 256.328, 345.147, 231.528, 338.747, 232.328]}, + {t: 'z', p: []}]}, + +{f: '#f3bf9c', s: null, p: [{t: 'M', p: [340.056, 234.073]}, + {t: 'C', p: [333.655, 234.873, 307.313, 211.613, 323.255, 242.873]}, + {t: 'C', p: [343.656, 282.874, 264.855, 282.074, 248.055, 270.874]}, + {t: 'C', p: [231.255, 259.674, 255.255, 289.274, 255.255, 289.274]}, + {t: 'C', p: [273.655, 309.274, 239.255, 292.474, 239.255, 292.474]}, + {t: 'C', p: [204.855, 279.674, 180.855, 305.274, 177.655, 306.074]}, + {t: 'C', p: [174.455, 306.874, 169.655, 310.074, 168.855, 303.674]}, + {t: 'C', p: [168.055, 297.274, 161.332, 281.578, 128.855, 306.874]}, + {t: 'C', p: [101.436, 327.947, 89.491, 309.219, 89.491, 309.219]}, + {t: 'L', p: [84.291, 311.874]}, + {t: 'C', p: [68.291, 281.674, 77.727, 337.929, 77.727, 337.929]}, + {t: 'C', p: [86.527, 372.329, 220.055, 326.874, 220.055, 326.874]}, + {t: 'C', p: [220.055, 326.874, 388.856, 296.474, 400.056, 292.474]}, + {t: 'C', p: [411.256, 288.474, 501.22, 294.437, 501.22, 294.437]}, + {t: 'L', p: [496.056, 274.583]}, + {t: 'C', p: [431.256, 228.183, 416.856, 254.874, 403.256, 250.874]}, + {t: 'C', p: [389.656, 246.873, 392.056, 256.474, 388.856, 257.274]}, + {t: 'C', p: [385.656, 258.074, 346.456, 233.273, 340.056, 234.073]}, + {t: 'z', p: []}]}, + +{f: '#f5ccb0', s: null, p: [{t: 'M', p: [341.365, 235.819]}, + {t: 'C', p: [334.965, 236.619, 307.523, 213.944, 324.565, 244.619]}, + {t: 'C', p: [346.565, 284.219, 266.164, 283.819, 249.364, 272.619]}, + {t: 'C', p: [232.564, 261.419, 256.564, 291.019, 256.564, 291.019]}, + {t: 'C', p: [274.964, 311.019, 240.564, 294.219, 240.564, 294.219]}, + {t: 'C', p: [206.164, 281.419, 182.164, 307.019, 178.964, 307.819]}, + {t: 'C', p: [175.764, 308.619, 170.964, 311.819, 170.164, 305.419]}, + {t: 'C', p: [169.364, 299.019, 162.773, 283.492, 130.164, 308.619]}, + {t: 'C', p: [101.509, 330.438, 89.873, 312.256, 89.873, 312.256]}, + {t: 'L', p: [84.273, 313.419]}, + {t: 'C', p: [69.872, 285.019, 78.382, 340.983, 78.382, 340.983]}, + {t: 'C', p: [87.182, 375.384, 221.364, 328.619, 221.364, 328.619]}, + {t: 'C', p: [221.364, 328.619, 390.165, 298.219, 401.365, 294.219]}, + {t: 'C', p: [412.565, 290.219, 501.656, 296.11, 501.656, 296.11]}, + {t: 'L', p: [496.565, 275.746]}, + {t: 'C', p: [431.765, 229.346, 418.165, 256.619, 404.565, 252.619]}, + {t: 'C', p: [390.965, 248.619, 393.365, 258.219, 390.165, 259.019]}, + {t: 'C', p: [386.965, 259.819, 347.765, 235.019, 341.365, 235.819]}, + {t: 'z', p: []}]}, + +{f: '#f8d8c4', s: null, p: [{t: 'M', p: [342.674, 237.565]}, + {t: 'C', p: [336.274, 238.365, 308.832, 215.689, 325.874, 246.365]}, + {t: 'C', p: [347.874, 285.965, 267.474, 285.565, 250.674, 274.365]}, + {t: 'C', p: [233.874, 263.165, 257.874, 292.765, 257.874, 292.765]}, + {t: 'C', p: [276.274, 312.765, 241.874, 295.965, 241.874, 295.965]}, + {t: 'C', p: [207.473, 283.165, 183.473, 308.765, 180.273, 309.565]}, + {t: 'C', p: [177.073, 310.365, 172.273, 313.565, 171.473, 307.165]}, + {t: 'C', p: [170.673, 300.765, 164.214, 285.405, 131.473, 310.365]}, + {t: 'C', p: [101.582, 332.929, 90.255, 315.293, 90.255, 315.293]}, + {t: 'L', p: [84.255, 314.965]}, + {t: 'C', p: [70.654, 288.564, 79.037, 344.038, 79.037, 344.038]}, + {t: 'C', p: [87.837, 378.438, 222.673, 330.365, 222.673, 330.365]}, + {t: 'C', p: [222.673, 330.365, 391.474, 299.965, 402.674, 295.965]}, + {t: 'C', p: [413.874, 291.965, 502.093, 297.783, 502.093, 297.783]}, + {t: 'L', p: [497.075, 276.91]}, + {t: 'C', p: [432.274, 230.51, 419.474, 258.365, 405.874, 254.365]}, + {t: 'C', p: [392.274, 250.365, 394.674, 259.965, 391.474, 260.765]}, + {t: 'C', p: [388.274, 261.565, 349.074, 236.765, 342.674, 237.565]}, + {t: 'z', p: []}]}, + +{f: '#fae5d7', s: null, p: [{t: 'M', p: [343.983, 239.31]}, + {t: 'C', p: [337.583, 240.11, 310.529, 217.223, 327.183, 248.11]}, + {t: 'C', p: [349.183, 288.91, 268.783, 287.31, 251.983, 276.11]}, + {t: 'C', p: [235.183, 264.91, 259.183, 294.51, 259.183, 294.51]}, + {t: 'C', p: [277.583, 314.51, 243.183, 297.71, 243.183, 297.71]}, + {t: 'C', p: [208.783, 284.91, 184.783, 310.51, 181.583, 311.31]}, + {t: 'C', p: [178.382, 312.11, 173.582, 315.31, 172.782, 308.91]}, + {t: 'C', p: [171.982, 302.51, 165.654, 287.318, 132.782, 312.11]}, + {t: 'C', p: [101.655, 335.42, 90.637, 318.329, 90.637, 318.329]}, + {t: 'L', p: [84.236, 316.51]}, + {t: 'C', p: [71.236, 292.51, 79.691, 347.093, 79.691, 347.093]}, + {t: 'C', p: [88.491, 381.493, 223.983, 332.11, 223.983, 332.11]}, + {t: 'C', p: [223.983, 332.11, 392.783, 301.71, 403.983, 297.71]}, + {t: 'C', p: [415.183, 293.71, 502.529, 299.456, 502.529, 299.456]}, + {t: 'L', p: [497.583, 278.074]}, + {t: 'C', p: [432.783, 231.673, 420.783, 260.11, 407.183, 256.11]}, + {t: 'C', p: [393.583, 252.11, 395.983, 261.71, 392.783, 262.51]}, + {t: 'C', p: [389.583, 263.31, 350.383, 238.51, 343.983, 239.31]}, + {t: 'z', p: []}]}, + +{f: '#fcf2eb', s: null, p: [{t: 'M', p: [345.292, 241.055]}, + {t: 'C', p: [338.892, 241.855, 312.917, 218.411, 328.492, 249.855]}, + {t: 'C', p: [349.692, 292.656, 270.092, 289.056, 253.292, 277.856]}, + {t: 'C', p: [236.492, 266.656, 260.492, 296.256, 260.492, 296.256]}, + {t: 'C', p: [278.892, 316.256, 244.492, 299.456, 244.492, 299.456]}, + {t: 'C', p: [210.092, 286.656, 186.092, 312.256, 182.892, 313.056]}, + {t: 'C', p: [179.692, 313.856, 174.892, 317.056, 174.092, 310.656]}, + {t: 'C', p: [173.292, 304.256, 167.095, 289.232, 134.092, 313.856]}, + {t: 'C', p: [101.727, 337.911, 91.018, 321.365, 91.018, 321.365]}, + {t: 'L', p: [84.218, 318.056]}, + {t: 'C', p: [71.418, 294.856, 80.346, 350.147, 80.346, 350.147]}, + {t: 'C', p: [89.146, 384.547, 225.292, 333.856, 225.292, 333.856]}, + {t: 'C', p: [225.292, 333.856, 394.093, 303.456, 405.293, 299.456]}, + {t: 'C', p: [416.493, 295.456, 502.965, 301.128, 502.965, 301.128]}, + {t: 'L', p: [498.093, 279.237]}, + {t: 'C', p: [433.292, 232.837, 422.093, 261.856, 408.493, 257.856]}, + {t: 'C', p: [394.893, 253.855, 397.293, 263.456, 394.093, 264.256]}, + {t: 'C', p: [390.892, 265.056, 351.692, 240.255, 345.292, 241.055]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [84.2, 319.601]}, + {t: 'C', p: [71.4, 297.6, 81, 353.201, 81, 353.201]}, + {t: 'C', p: [89.8, 387.601, 226.6, 335.601, 226.6, 335.601]}, + {t: 'C', p: [226.6, 335.601, 395.401, 305.2, 406.601, 301.2]}, + {t: 'C', p: [417.801, 297.2, 503.401, 302.8, 503.401, 302.8]}, + {t: 'L', p: [498.601, 280.4]}, + {t: 'C', p: [433.801, 234, 423.401, 263.6, 409.801, 259.6]}, + {t: 'C', p: [396.201, 255.6, 398.601, 265.2, 395.401, 266]}, + {t: 'C', p: [392.201, 266.8, 353.001, 242, 346.601, 242.8]}, + {t: 'C', p: [340.201, 243.6, 314.981, 219.793, 329.801, 251.6]}, + {t: 'C', p: [352.028, 299.307, 269.041, 289.227, 254.6, 279.6]}, + {t: 'C', p: [237.8, 268.4, 261.8, 298, 261.8, 298]}, + {t: 'C', p: [280.2, 318.001, 245.8, 301.2, 245.8, 301.2]}, + {t: 'C', p: [211.4, 288.4, 187.4, 314.001, 184.2, 314.801]}, + {t: 'C', p: [181, 315.601, 176.2, 318.801, 175.4, 312.401]}, + {t: 'C', p: [174.6, 306, 168.535, 291.144, 135.4, 315.601]}, + {t: 'C', p: [101.8, 340.401, 91.4, 324.401, 91.4, 324.401]}, + {t: 'L', p: [84.2, 319.601]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [125.8, 349.601]}, + {t: 'C', p: [125.8, 349.601, 118.6, 361.201, 139.4, 374.401]}, + {t: 'C', p: [139.4, 374.401, 140.8, 375.801, 122.8, 371.601]}, + {t: 'C', p: [122.8, 371.601, 116.6, 369.601, 115, 359.201]}, + {t: 'C', p: [115, 359.201, 110.2, 354.801, 105.4, 349.201]}, + {t: 'C', p: [100.6, 343.601, 125.8, 349.601, 125.8, 349.601]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [265.8, 302]}, + {t: 'C', p: [265.8, 302, 283.498, 328.821, 282.9, 333.601]}, + {t: 'C', p: [281.6, 344.001, 281.4, 353.601, 284.6, 357.601]}, + {t: 'C', p: [287.801, 361.601, 296.601, 394.801, 296.601, 394.801]}, + {t: 'C', p: [296.601, 394.801, 296.201, 396.001, 308.601, 358.001]}, + {t: 'C', p: [308.601, 358.001, 320.201, 342.001, 300.201, 323.601]}, + {t: 'C', p: [300.201, 323.601, 265, 294.8, 265.8, 302]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [145.8, 376.401]}, + {t: 'C', p: [145.8, 376.401, 157, 383.601, 142.6, 414.801]}, + {t: 'L', p: [149, 412.401]}, + {t: 'C', p: [149, 412.401, 148.2, 423.601, 145, 426.001]}, + {t: 'L', p: [152.2, 422.801]}, + {t: 'C', p: [152.2, 422.801, 157, 430.801, 153, 435.601]}, + {t: 'C', p: [153, 435.601, 169.8, 443.601, 169, 450.001]}, + {t: 'C', p: [169, 450.001, 175.4, 442.001, 171.4, 435.601]}, + {t: 'C', p: [167.4, 429.201, 160.2, 433.201, 161, 414.801]}, + {t: 'L', p: [152.2, 418.001]}, + {t: 'C', p: [152.2, 418.001, 157.8, 409.201, 157.8, 402.801]}, + {t: 'L', p: [149.8, 405.201]}, + {t: 'C', p: [149.8, 405.201, 165.269, 378.623, 154.6, 377.201]}, + {t: 'C', p: [148.6, 376.401, 145.8, 376.401, 145.8, 376.401]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [178.2, 393.201]}, + {t: 'C', p: [178.2, 393.201, 181, 388.801, 178.2, 389.601]}, + {t: 'C', p: [175.4, 390.401, 144.2, 405.201, 138.2, 414.801]}, + {t: 'C', p: [138.2, 414.801, 172.6, 390.401, 178.2, 393.201]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [188.6, 401.201]}, + {t: 'C', p: [188.6, 401.201, 191.4, 396.801, 188.6, 397.601]}, + {t: 'C', p: [185.8, 398.401, 154.6, 413.201, 148.6, 422.801]}, + {t: 'C', p: [148.6, 422.801, 183, 398.401, 188.6, 401.201]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [201.8, 386.001]}, + {t: 'C', p: [201.8, 386.001, 204.6, 381.601, 201.8, 382.401]}, + {t: 'C', p: [199, 383.201, 167.8, 398.001, 161.8, 407.601]}, + {t: 'C', p: [161.8, 407.601, 196.2, 383.201, 201.8, 386.001]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [178.6, 429.601]}, + {t: 'C', p: [178.6, 429.601, 178.6, 423.601, 175.8, 424.401]}, + {t: 'C', p: [173, 425.201, 137, 442.801, 131, 452.401]}, + {t: 'C', p: [131, 452.401, 173, 426.801, 178.6, 429.601]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [179.8, 418.801]}, + {t: 'C', p: [179.8, 418.801, 181, 414.001, 178.2, 414.801]}, + {t: 'C', p: [176.2, 414.801, 149.8, 426.401, 143.8, 436.001]}, + {t: 'C', p: [143.8, 436.001, 173.4, 414.401, 179.8, 418.801]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [165.4, 466.401]}, + {t: 'L', p: [155.4, 474.001]}, + {t: 'C', p: [155.4, 474.001, 165.8, 466.401, 169.4, 467.601]}, + {t: 'C', p: [169.4, 467.601, 162.6, 478.801, 161.8, 484.001]}, + {t: 'C', p: [161.8, 484.001, 172.2, 471.201, 177.8, 471.601]}, + {t: 'C', p: [177.8, 471.601, 185.4, 472.001, 185.4, 482.801]}, + {t: 'C', p: [185.4, 482.801, 191, 472.401, 194.2, 472.801]}, + {t: 'C', p: [194.2, 472.801, 195.4, 479.201, 194.2, 486.001]}, + {t: 'C', p: [194.2, 486.001, 198.2, 478.401, 202.2, 480.001]}, + {t: 'C', p: [202.2, 480.001, 208.6, 478.001, 207.8, 489.601]}, + {t: 'C', p: [207.8, 489.601, 207.8, 500.001, 207, 502.801]}, + {t: 'C', p: [207, 502.801, 212.6, 476.401, 215, 476.001]}, + {t: 'C', p: [215, 476.001, 223, 474.801, 227.8, 483.601]}, + {t: 'C', p: [227.8, 483.601, 223.8, 476.001, 228.6, 478.001]}, + {t: 'C', p: [228.6, 478.001, 239.4, 479.601, 242.6, 486.401]}, + {t: 'C', p: [242.6, 486.401, 235.8, 474.401, 241.4, 477.601]}, + {t: 'C', p: [241.4, 477.601, 248.2, 477.601, 249.4, 484.001]}, + {t: 'C', p: [249.4, 484.001, 257.8, 505.201, 259.8, 506.801]}, + {t: 'C', p: [259.8, 506.801, 252.2, 485.201, 253.8, 485.201]}, + {t: 'C', p: [253.8, 485.201, 251.8, 473.201, 257, 488.001]}, + {t: 'C', p: [257, 488.001, 253.8, 474.001, 259.4, 474.801]}, + {t: 'C', p: [265, 475.601, 269.4, 485.601, 277.8, 483.201]}, + {t: 'C', p: [277.8, 483.201, 287.401, 488.801, 289.401, 419.601]}, + {t: 'L', p: [165.4, 466.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [170.2, 373.601]}, + {t: 'C', p: [170.2, 373.601, 185, 367.601, 225, 373.601]}, + {t: 'C', p: [225, 373.601, 232.2, 374.001, 239, 365.201]}, + {t: 'C', p: [245.8, 356.401, 272.6, 349.201, 279, 351.201]}, + {t: 'L', p: [288.601, 357.601]}, + {t: 'L', p: [289.401, 358.801]}, + {t: 'C', p: [289.401, 358.801, 301.801, 369.201, 302.201, 376.801]}, + {t: 'C', p: [302.601, 384.401, 287.801, 432.401, 278.2, 448.401]}, + {t: 'C', p: [268.6, 464.401, 259, 476.801, 239.8, 474.401]}, + {t: 'C', p: [239.8, 474.401, 219, 470.401, 193.4, 474.401]}, + {t: 'C', p: [193.4, 474.401, 164.2, 472.801, 161.4, 464.801]}, + {t: 'C', p: [158.6, 456.801, 172.6, 441.601, 172.6, 441.601]}, + {t: 'C', p: [172.6, 441.601, 177, 433.201, 175.8, 418.801]}, + {t: 'C', p: [174.6, 404.401, 175, 376.401, 170.2, 373.601]}, + {t: 'z', p: []}]}, + +{f: '#e5668c', s: null, p: [{t: 'M', p: [192.2, 375.601]}, + {t: 'C', p: [200.6, 394.001, 171, 459.201, 171, 459.201]}, + {t: 'C', p: [169, 460.801, 183.66, 466.846, 193.8, 464.401]}, + {t: 'C', p: [204.746, 461.763, 245, 466.001, 245, 466.001]}, + {t: 'C', p: [268.6, 450.401, 281.4, 406.001, 281.4, 406.001]}, + {t: 'C', p: [281.4, 406.001, 291.801, 382.001, 274.2, 378.801]}, + {t: 'C', p: [256.6, 375.601, 192.2, 375.601, 192.2, 375.601]}, + {t: 'z', p: []}]}, + +{f: '#b23259', s: null, p: [{t: 'M', p: [190.169, 406.497]}, + {t: 'C', p: [193.495, 393.707, 195.079, 381.906, 192.2, 375.601]}, + {t: 'C', p: [192.2, 375.601, 254.6, 382.001, 265.8, 361.201]}, + {t: 'C', p: [270.041, 353.326, 284.801, 384.001, 284.4, 393.601]}, + {t: 'C', p: [284.4, 393.601, 221.4, 408.001, 206.6, 396.801]}, + {t: 'L', p: [190.169, 406.497]}, + {t: 'z', p: []}]}, + +{f: '#a5264c', s: null, p: [{t: 'M', p: [194.6, 422.801]}, + {t: 'C', p: [194.6, 422.801, 196.6, 430.001, 194.2, 434.001]}, + {t: 'C', p: [194.2, 434.001, 192.6, 434.801, 191.4, 435.201]}, + {t: 'C', p: [191.4, 435.201, 192.6, 438.801, 198.6, 440.401]}, + {t: 'C', p: [198.6, 440.401, 200.6, 444.801, 203, 445.201]}, + {t: 'C', p: [205.4, 445.601, 210.2, 451.201, 214.2, 450.001]}, + {t: 'C', p: [218.2, 448.801, 229.4, 444.801, 229.4, 444.801]}, + {t: 'C', p: [229.4, 444.801, 235, 441.601, 243.8, 445.201]}, + {t: 'C', p: [243.8, 445.201, 246.175, 444.399, 246.6, 440.401]}, + {t: 'C', p: [247.1, 435.701, 250.2, 432.001, 252.2, 430.001]}, + {t: 'C', p: [254.2, 428.001, 263.8, 415.201, 262.6, 414.801]}, + {t: 'C', p: [261.4, 414.401, 194.6, 422.801, 194.6, 422.801]}, + {t: 'z', p: []}]}, + +{f: '#ff727f', s: '#000', p: [{t: 'M', p: [190.2, 374.401]}, + {t: 'C', p: [190.2, 374.401, 187.4, 396.801, 190.6, 405.201]}, + {t: 'C', p: [193.8, 413.601, 193, 415.601, 192.2, 419.601]}, + {t: 'C', p: [191.4, 423.601, 195.8, 433.601, 201.4, 439.601]}, + {t: 'L', p: [213.4, 441.201]}, + {t: 'C', p: [213.4, 441.201, 228.6, 437.601, 237.8, 440.401]}, + {t: 'C', p: [237.8, 440.401, 246.794, 441.744, 250.2, 426.801]}, + {t: 'C', p: [250.2, 426.801, 255, 420.401, 262.2, 417.601]}, + {t: 'C', p: [269.4, 414.801, 276.6, 373.201, 272.6, 365.201]}, + {t: 'C', p: [268.6, 357.201, 254.2, 352.801, 238.2, 368.401]}, + {t: 'C', p: [222.2, 384.001, 220.2, 367.201, 190.2, 374.401]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [191.8, 449.201]}, + {t: 'C', p: [191.8, 449.201, 191, 447.201, 186.6, 446.801]}, + {t: 'C', p: [186.6, 446.801, 164.2, 443.201, 155.8, 430.801]}, + {t: 'C', p: [155.8, 430.801, 149, 425.201, 153.4, 436.801]}, + {t: 'C', p: [153.4, 436.801, 163.8, 457.201, 170.6, 460.001]}, + {t: 'C', p: [170.6, 460.001, 187, 464.001, 191.8, 449.201]}, + {t: 'z', p: []}]}, + +{f: '#cc3f4c', s: null, p: [{t: 'M', p: [271.742, 385.229]}, + {t: 'C', p: [272.401, 377.323, 274.354, 368.709, 272.6, 365.201]}, + {t: 'C', p: [266.154, 352.307, 249.181, 357.695, 238.2, 368.401]}, + {t: 'C', p: [222.2, 384.001, 220.2, 367.201, 190.2, 374.401]}, + {t: 'C', p: [190.2, 374.401, 188.455, 388.364, 189.295, 398.376]}, + {t: 'C', p: [189.295, 398.376, 226.6, 386.801, 227.4, 392.401]}, + {t: 'C', p: [227.4, 392.401, 229, 389.201, 238.2, 389.201]}, + {t: 'C', p: [247.4, 389.201, 270.142, 388.029, 271.742, 385.229]}, + {t: 'z', p: []}]}, + +{f: null, s: {c: '#a51926', w: 2}, + p: [{t: 'M', p: [228.6, 375.201]}, + {t: 'C', p: [228.6, 375.201, 233.4, 380.001, 229.8, 389.601]}, + {t: 'C', p: [229.8, 389.601, 215.4, 405.601, 217.4, 419.601]}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [180.6, 460.001]}, + {t: 'C', p: [180.6, 460.001, 176.2, 447.201, 185, 454.001]}, + {t: 'C', p: [185, 454.001, 189.8, 456.001, 188.6, 457.601]}, + {t: 'C', p: [187.4, 459.201, 181.8, 463.201, 180.6, 460.001]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [185.64, 461.201]}, + {t: 'C', p: [185.64, 461.201, 182.12, 450.961, 189.16, 456.401]}, + {t: 'C', p: [189.16, 456.401, 193.581, 458.849, 192.04, 459.281]}, + {t: 'C', p: [187.48, 460.561, 192.04, 463.121, 185.64, 461.201]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [190.44, 461.201]}, + {t: 'C', p: [190.44, 461.201, 186.92, 450.961, 193.96, 456.401]}, + {t: 'C', p: [193.96, 456.401, 198.335, 458.711, 196.84, 459.281]}, + {t: 'C', p: [193.48, 460.561, 196.84, 463.121, 190.44, 461.201]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [197.04, 461.401]}, + {t: 'C', p: [197.04, 461.401, 193.52, 451.161, 200.56, 456.601]}, + {t: 'C', p: [200.56, 456.601, 204.943, 458.933, 203.441, 459.481]}, + {t: 'C', p: [200.48, 460.561, 203.441, 463.321, 197.04, 461.401]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [203.52, 461.321]}, + {t: 'C', p: [203.52, 461.321, 200, 451.081, 207.041, 456.521]}, + {t: 'C', p: [207.041, 456.521, 210.881, 458.121, 209.921, 459.401]}, + {t: 'C', p: [208.961, 460.681, 209.921, 463.241, 203.52, 461.321]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [210.2, 462.001]}, + {t: 'C', p: [210.2, 462.001, 205.4, 449.601, 214.6, 456.001]}, + {t: 'C', p: [214.6, 456.001, 219.4, 458.001, 218.2, 459.601]}, + {t: 'C', p: [217, 461.201, 218.2, 464.401, 210.2, 462.001]}, + {t: 'z', p: []}]}, + +{f: null, s: {c: '#a5264c', w: 2}, + p: [{t: 'M', p: [181.8, 444.801]}, + {t: 'C', p: [181.8, 444.801, 195, 442.001, 201, 445.201]}, + {t: 'C', p: [201, 445.201, 207, 446.401, 208.2, 446.001]}, + {t: 'C', p: [209.4, 445.601, 212.6, 445.201, 212.6, 445.201]}]}, + +{f: null, s: {c: '#a5264c', w: 2}, + p: [{t: 'M', p: [215.8, 453.601]}, + {t: 'C', p: [215.8, 453.601, 227.8, 440.001, 239.8, 444.401]}, + {t: 'C', p: [246.816, 446.974, 245.8, 443.601, 246.6, 440.801]}, + {t: 'C', p: [247.4, 438.001, 247.6, 433.801, 252.6, 430.801]}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [233, 437.601]}, + {t: 'C', p: [233, 437.601, 229, 426.801, 226.2, 439.601]}, + {t: 'C', p: [223.4, 452.401, 220.2, 456.001, 218.6, 458.801]}, + {t: 'C', p: [218.6, 458.801, 218.6, 464.001, 227, 463.601]}, + {t: 'C', p: [227, 463.601, 237.8, 463.201, 238.2, 460.401]}, + {t: 'C', p: [238.6, 457.601, 237, 446.001, 233, 437.601]}, + {t: 'z', p: []}]}, + +{f: null, s: {c: '#a5264c', w: 2}, + p: [{t: 'M', p: [247, 444.801]}, + {t: 'C', p: [247, 444.801, 250.6, 442.401, 253, 443.601]}]}, + +{f: null, s: {c: '#a5264c', w: 2}, + p: [{t: 'M', p: [253.5, 428.401]}, + {t: 'C', p: [253.5, 428.401, 256.4, 423.501, 261.2, 422.701]}]}, + +{f: '#b2b2b2', s: null, p: [{t: 'M', p: [174.2, 465.201]}, + {t: 'C', p: [174.2, 465.201, 192.2, 468.401, 196.6, 466.801]}, + {t: 'C', p: [196.6, 466.801, 205.4, 466.801, 197, 468.801]}, + {t: 'C', p: [197, 468.801, 184.2, 468.801, 176.2, 467.601]}, + {t: 'C', p: [176.2, 467.601, 164.6, 462.001, 174.2, 465.201]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [188.2, 372.001]}, + {t: 'C', p: [188.2, 372.001, 205.8, 372.001, 207.8, 372.801]}, + {t: 'C', p: [207.8, 372.801, 215, 403.601, 211.4, 411.201]}, + {t: 'C', p: [211.4, 411.201, 210.2, 414.001, 207.4, 408.401]}, + {t: 'C', p: [207.4, 408.401, 189, 375.601, 185.8, 373.601]}, + {t: 'C', p: [182.6, 371.601, 187, 372.001, 188.2, 372.001]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [111.1, 369.301]}, + {t: 'C', p: [111.1, 369.301, 120, 371.001, 132.6, 373.601]}, + {t: 'C', p: [132.6, 373.601, 137.4, 396.001, 140.6, 400.801]}, + {t: 'C', p: [143.8, 405.601, 140.2, 405.601, 136.6, 402.801]}, + {t: 'C', p: [133, 400.001, 118.2, 386.001, 116.2, 381.601]}, + {t: 'C', p: [114.2, 377.201, 111.1, 369.301, 111.1, 369.301]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [132.961, 373.818]}, + {t: 'C', p: [132.961, 373.818, 138.761, 375.366, 139.77, 377.581]}, + {t: 'C', p: [140.778, 379.795, 138.568, 383.092, 138.568, 383.092]}, + {t: 'C', p: [138.568, 383.092, 137.568, 386.397, 136.366, 384.235]}, + {t: 'C', p: [135.164, 382.072, 132.292, 374.412, 132.961, 373.818]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [133, 373.601]}, + {t: 'C', p: [133, 373.601, 136.6, 378.801, 140.2, 378.801]}, + {t: 'C', p: [143.8, 378.801, 144.182, 378.388, 147, 379.001]}, + {t: 'C', p: [151.6, 380.001, 151.2, 378.001, 157.8, 379.201]}, + {t: 'C', p: [160.44, 379.681, 163, 378.801, 165.8, 380.001]}, + {t: 'C', p: [168.6, 381.201, 171.8, 380.401, 173, 378.401]}, + {t: 'C', p: [174.2, 376.401, 179, 372.201, 179, 372.201]}, + {t: 'C', p: [179, 372.201, 166.2, 374.001, 163.4, 374.801]}, + {t: 'C', p: [163.4, 374.801, 141, 376.001, 133, 373.601]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [177.6, 373.801]}, + {t: 'C', p: [177.6, 373.801, 171.15, 377.301, 170.75, 379.701]}, + {t: 'C', p: [170.35, 382.101, 176, 385.801, 176, 385.801]}, + {t: 'C', p: [176, 385.801, 178.75, 390.401, 179.35, 388.001]}, + {t: 'C', p: [179.95, 385.601, 178.4, 374.201, 177.6, 373.801]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [140.115, 379.265]}, + {t: 'C', p: [140.115, 379.265, 147.122, 390.453, 147.339, 379.242]}, + {t: 'C', p: [147.339, 379.242, 147.896, 377.984, 146.136, 377.962]}, + {t: 'C', p: [140.061, 377.886, 141.582, 373.784, 140.115, 379.265]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [147.293, 379.514]}, + {t: 'C', p: [147.293, 379.514, 155.214, 390.701, 154.578, 379.421]}, + {t: 'C', p: [154.578, 379.421, 154.585, 379.089, 152.832, 378.936]}, + {t: 'C', p: [148.085, 378.522, 148.43, 374.004, 147.293, 379.514]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [154.506, 379.522]}, + {t: 'C', p: [154.506, 379.522, 162.466, 390.15, 161.797, 380.484]}, + {t: 'C', p: [161.797, 380.484, 161.916, 379.251, 160.262, 378.95]}, + {t: 'C', p: [156.37, 378.244, 156.159, 374.995, 154.506, 379.522]}, + {t: 'z', p: []}]}, + +{f: '#ffc', s: {c: '#000', w: 0.5}, + p: [{t: 'M', p: [161.382, 379.602]}, + {t: 'C', p: [161.382, 379.602, 169.282, 391.163, 169.63, 381.382]}, + {t: 'C', p: [169.63, 381.382, 171.274, 380.004, 169.528, 379.782]}, + {t: 'C', p: [163.71, 379.042, 164.508, 374.588, 161.382, 379.602]}, + {t: 'z', p: []}]}, + +{f: '#e5e5b2', s: null, p: [{t: 'M', p: [125.208, 383.132]}, + {t: 'L', p: [117.55, 381.601]}, + {t: 'C', p: [114.95, 376.601, 112.85, 370.451, 112.85, 370.451]}, + {t: 'C', p: [112.85, 370.451, 119.2, 371.451, 131.7, 374.251]}, + {t: 'C', p: [131.7, 374.251, 132.576, 377.569, 134.048, 383.364]}, + {t: 'L', p: [125.208, 383.132]}, + {t: 'z', p: []}]}, + +{f: '#e5e5b2', s: null, p: [{t: 'M', p: [190.276, 378.47]}, + {t: 'C', p: [188.61, 375.964, 187.293, 374.206, 186.643, 373.8]}, + {t: 'C', p: [183.63, 371.917, 187.773, 372.294, 188.902, 372.294]}, + {t: 'C', p: [188.902, 372.294, 205.473, 372.294, 207.356, 373.047]}, + {t: 'C', p: [207.356, 373.047, 207.88, 375.289, 208.564, 378.68]}, + {t: 'C', p: [208.564, 378.68, 198.476, 376.67, 190.276, 378.47]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [243.88, 240.321]}, + {t: 'C', p: [271.601, 244.281, 297.121, 208.641, 298.881, 198.96]}, + {t: 'C', p: [300.641, 189.28, 290.521, 177.4, 290.521, 177.4]}, + {t: 'C', p: [291.841, 174.32, 287.001, 160.24, 281.721, 151]}, + {t: 'C', p: [276.441, 141.76, 260.54, 142.734, 243, 141.76]}, + {t: 'C', p: [227.16, 140.88, 208.68, 164.2, 207.36, 165.96]}, + {t: 'C', p: [206.04, 167.72, 212.2, 206.001, 213.52, 211.721]}, + {t: 'C', p: [214.84, 217.441, 212.2, 243.841, 212.2, 243.841]}, + {t: 'C', p: [246.44, 234.741, 216.16, 236.361, 243.88, 240.321]}, + {t: 'z', p: []}]}, + +{f: '#ea8e51', s: null, p: [{t: 'M', p: [208.088, 166.608]}, + {t: 'C', p: [206.792, 168.336, 212.84, 205.921, 214.136, 211.537]}, + {t: 'C', p: [215.432, 217.153, 212.84, 243.073, 212.84, 243.073]}, + {t: 'C', p: [245.512, 234.193, 216.728, 235.729, 243.944, 239.617]}, + {t: 'C', p: [271.161, 243.505, 296.217, 208.513, 297.945, 199.008]}, + {t: 'C', p: [299.673, 189.504, 289.737, 177.84, 289.737, 177.84]}, + {t: 'C', p: [291.033, 174.816, 286.281, 160.992, 281.097, 151.92]}, + {t: 'C', p: [275.913, 142.848, 260.302, 143.805, 243.08, 142.848]}, + {t: 'C', p: [227.528, 141.984, 209.384, 164.88, 208.088, 166.608]}, + {t: 'z', p: []}]}, + +{f: '#efaa7c', s: null, p: [{t: 'M', p: [208.816, 167.256]}, + {t: 'C', p: [207.544, 168.952, 213.48, 205.841, 214.752, 211.353]}, + {t: 'C', p: [216.024, 216.865, 213.48, 242.305, 213.48, 242.305]}, + {t: 'C', p: [244.884, 233.145, 217.296, 235.097, 244.008, 238.913]}, + {t: 'C', p: [270.721, 242.729, 295.313, 208.385, 297.009, 199.056]}, + {t: 'C', p: [298.705, 189.728, 288.953, 178.28, 288.953, 178.28]}, + {t: 'C', p: [290.225, 175.312, 285.561, 161.744, 280.473, 152.84]}, + {t: 'C', p: [275.385, 143.936, 260.063, 144.875, 243.16, 143.936]}, + {t: 'C', p: [227.896, 143.088, 210.088, 165.56, 208.816, 167.256]}, + {t: 'z', p: []}]}, + +{f: '#f4c6a8', s: null, p: [{t: 'M', p: [209.544, 167.904]}, + {t: 'C', p: [208.296, 169.568, 214.12, 205.761, 215.368, 211.169]}, + {t: 'C', p: [216.616, 216.577, 214.12, 241.537, 214.12, 241.537]}, + {t: 'C', p: [243.556, 232.497, 217.864, 234.465, 244.072, 238.209]}, + {t: 'C', p: [270.281, 241.953, 294.409, 208.257, 296.073, 199.105]}, + {t: 'C', p: [297.737, 189.952, 288.169, 178.72, 288.169, 178.72]}, + {t: 'C', p: [289.417, 175.808, 284.841, 162.496, 279.849, 153.76]}, + {t: 'C', p: [274.857, 145.024, 259.824, 145.945, 243.24, 145.024]}, + {t: 'C', p: [228.264, 144.192, 210.792, 166.24, 209.544, 167.904]}, + {t: 'z', p: []}]}, + +{f: '#f9e2d3', s: null, p: [{t: 'M', p: [210.272, 168.552]}, + {t: 'C', p: [209.048, 170.184, 214.76, 205.681, 215.984, 210.985]}, + {t: 'C', p: [217.208, 216.289, 214.76, 240.769, 214.76, 240.769]}, + {t: 'C', p: [242.628, 231.849, 218.432, 233.833, 244.136, 237.505]}, + {t: 'C', p: [269.841, 241.177, 293.505, 208.129, 295.137, 199.152]}, + {t: 'C', p: [296.769, 190.176, 287.385, 179.16, 287.385, 179.16]}, + {t: 'C', p: [288.609, 176.304, 284.121, 163.248, 279.225, 154.68]}, + {t: 'C', p: [274.329, 146.112, 259.585, 147.015, 243.32, 146.112]}, + {t: 'C', p: [228.632, 145.296, 211.496, 166.92, 210.272, 168.552]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [244.2, 236.8]}, + {t: 'C', p: [269.4, 240.4, 292.601, 208, 294.201, 199.2]}, + {t: 'C', p: [295.801, 190.4, 286.601, 179.6, 286.601, 179.6]}, + {t: 'C', p: [287.801, 176.8, 283.4, 164, 278.6, 155.6]}, + {t: 'C', p: [273.8, 147.2, 259.346, 148.086, 243.4, 147.2]}, + {t: 'C', p: [229, 146.4, 212.2, 167.6, 211, 169.2]}, + {t: 'C', p: [209.8, 170.8, 215.4, 205.6, 216.6, 210.8]}, + {t: 'C', p: [217.8, 216, 215.4, 240, 215.4, 240]}, + {t: 'C', p: [240.9, 231.4, 219, 233.2, 244.2, 236.8]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [290.601, 202.8]}, + {t: 'C', p: [290.601, 202.8, 262.8, 210.4, 251.2, 208.8]}, + {t: 'C', p: [251.2, 208.8, 235.4, 202.2, 226.6, 224]}, + {t: 'C', p: [226.6, 224, 223, 231.2, 221, 233.2]}, + {t: 'C', p: [219, 235.2, 290.601, 202.8, 290.601, 202.8]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [294.401, 200.6]}, + {t: 'C', p: [294.401, 200.6, 265.4, 212.8, 255.4, 212.4]}, + {t: 'C', p: [255.4, 212.4, 239, 207.8, 230.6, 222.4]}, + {t: 'C', p: [230.6, 222.4, 222.2, 231.6, 219, 233.2]}, + {t: 'C', p: [219, 233.2, 218.6, 234.8, 225, 230.8]}, + {t: 'L', p: [235.4, 236]}, + {t: 'C', p: [235.4, 236, 250.2, 245.6, 259.8, 229.6]}, + {t: 'C', p: [259.8, 229.6, 263.8, 218.4, 263.8, 216.4]}, + {t: 'C', p: [263.8, 214.4, 285, 208.8, 286.601, 208.4]}, + {t: 'C', p: [288.201, 208, 294.801, 203.8, 294.401, 200.6]}, + {t: 'z', p: []}]}, + +{f: '#99cc32', s: null, p: [{t: 'M', p: [247, 236.514]}, + {t: 'C', p: [240.128, 236.514, 231.755, 232.649, 231.755, 226.4]}, + {t: 'C', p: [231.755, 220.152, 240.128, 213.887, 247, 213.887]}, + {t: 'C', p: [253.874, 213.887, 259.446, 218.952, 259.446, 225.2]}, + {t: 'C', p: [259.446, 231.449, 253.874, 236.514, 247, 236.514]}, + {t: 'z', p: []}]}, + +{f: '#659900', s: null, p: [{t: 'M', p: [243.377, 219.83]}, + {t: 'C', p: [238.531, 220.552, 233.442, 222.055, 233.514, 221.839]}, + {t: 'C', p: [235.054, 217.22, 241.415, 213.887, 247, 213.887]}, + {t: 'C', p: [251.296, 213.887, 255.084, 215.865, 257.32, 218.875]}, + {t: 'C', p: [257.32, 218.875, 252.004, 218.545, 243.377, 219.83]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [255.4, 219.6]}, + {t: 'C', p: [255.4, 219.6, 251, 216.4, 251, 218.6]}, + {t: 'C', p: [251, 218.6, 254.6, 223, 255.4, 219.6]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [245.4, 227.726]}, + {t: 'C', p: [242.901, 227.726, 240.875, 225.7, 240.875, 223.2]}, + {t: 'C', p: [240.875, 220.701, 242.901, 218.675, 245.4, 218.675]}, + {t: 'C', p: [247.9, 218.675, 249.926, 220.701, 249.926, 223.2]}, + {t: 'C', p: [249.926, 225.7, 247.9, 227.726, 245.4, 227.726]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [141.4, 214.4]}, + {t: 'C', p: [141.4, 214.4, 138.2, 193.2, 140.6, 188.8]}, + {t: 'C', p: [140.6, 188.8, 151.4, 178.8, 151, 175.2]}, + {t: 'C', p: [151, 175.2, 150.6, 157.2, 149.4, 156.4]}, + {t: 'C', p: [148.2, 155.6, 140.6, 149.6, 134.6, 156]}, + {t: 'C', p: [134.6, 156, 124.2, 174, 125, 180.4]}, + {t: 'L', p: [125, 182.4]}, + {t: 'C', p: [125, 182.4, 117.4, 182, 115.8, 184]}, + {t: 'C', p: [115.8, 184, 114.6, 189.2, 113.4, 189.6]}, + {t: 'C', p: [113.4, 189.6, 110.6, 192, 112.6, 194.8]}, + {t: 'C', p: [112.6, 194.8, 110.6, 197.2, 111, 201.2]}, + {t: 'L', p: [118.6, 205.2]}, + {t: 'C', p: [118.6, 205.2, 120.6, 219.6, 131.4, 224.8]}, + {t: 'C', p: [136.236, 227.129, 139.4, 220.4, 141.4, 214.4]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [140.4, 212.56]}, + {t: 'C', p: [140.4, 212.56, 137.52, 193.48, 139.68, 189.52]}, + {t: 'C', p: [139.68, 189.52, 149.4, 180.52, 149.04, 177.28]}, + {t: 'C', p: [149.04, 177.28, 148.68, 161.08, 147.6, 160.36]}, + {t: 'C', p: [146.52, 159.64, 139.68, 154.24, 134.28, 160]}, + {t: 'C', p: [134.28, 160, 124.92, 176.2, 125.64, 181.96]}, + {t: 'L', p: [125.64, 183.76]}, + {t: 'C', p: [125.64, 183.76, 118.8, 183.4, 117.36, 185.2]}, + {t: 'C', p: [117.36, 185.2, 116.28, 189.88, 115.2, 190.24]}, + {t: 'C', p: [115.2, 190.24, 112.68, 192.4, 114.48, 194.92]}, + {t: 'C', p: [114.48, 194.92, 112.68, 197.08, 113.04, 200.68]}, + {t: 'L', p: [119.88, 204.28]}, + {t: 'C', p: [119.88, 204.28, 121.68, 217.24, 131.4, 221.92]}, + {t: 'C', p: [135.752, 224.015, 138.6, 217.96, 140.4, 212.56]}, + {t: 'z', p: []}]}, + +{f: '#eb955c', s: null, p: [{t: 'M', p: [148.95, 157.39]}, + {t: 'C', p: [147.86, 156.53, 140.37, 150.76, 134.52, 157]}, + {t: 'C', p: [134.52, 157, 124.38, 174.55, 125.16, 180.79]}, + {t: 'L', p: [125.16, 182.74]}, + {t: 'C', p: [125.16, 182.74, 117.75, 182.35, 116.19, 184.3]}, + {t: 'C', p: [116.19, 184.3, 115.02, 189.37, 113.85, 189.76]}, + {t: 'C', p: [113.85, 189.76, 111.12, 192.1, 113.07, 194.83]}, + {t: 'C', p: [113.07, 194.83, 111.12, 197.17, 111.51, 201.07]}, + {t: 'L', p: [118.92, 204.97]}, + {t: 'C', p: [118.92, 204.97, 120.87, 219.01, 131.4, 224.08]}, + {t: 'C', p: [136.114, 226.35, 139.2, 219.79, 141.15, 213.94]}, + {t: 'C', p: [141.15, 213.94, 138.03, 193.27, 140.37, 188.98]}, + {t: 'C', p: [140.37, 188.98, 150.9, 179.23, 150.51, 175.72]}, + {t: 'C', p: [150.51, 175.72, 150.12, 158.17, 148.95, 157.39]}, + {t: 'z', p: []}]}, + +{f: '#f2b892', s: null, p: [{t: 'M', p: [148.5, 158.38]}, + {t: 'C', p: [147.52, 157.46, 140.14, 151.92, 134.44, 158]}, + {t: 'C', p: [134.44, 158, 124.56, 175.1, 125.32, 181.18]}, + {t: 'L', p: [125.32, 183.08]}, + {t: 'C', p: [125.32, 183.08, 118.1, 182.7, 116.58, 184.6]}, + {t: 'C', p: [116.58, 184.6, 115.44, 189.54, 114.3, 189.92]}, + {t: 'C', p: [114.3, 189.92, 111.64, 192.2, 113.54, 194.86]}, + {t: 'C', p: [113.54, 194.86, 111.64, 197.14, 112.02, 200.94]}, + {t: 'L', p: [119.24, 204.74]}, + {t: 'C', p: [119.24, 204.74, 121.14, 218.42, 131.4, 223.36]}, + {t: 'C', p: [135.994, 225.572, 139, 219.18, 140.9, 213.48]}, + {t: 'C', p: [140.9, 213.48, 137.86, 193.34, 140.14, 189.16]}, + {t: 'C', p: [140.14, 189.16, 150.4, 179.66, 150.02, 176.24]}, + {t: 'C', p: [150.02, 176.24, 149.64, 159.14, 148.5, 158.38]}, + {t: 'z', p: []}]}, + +{f: '#f8dcc8', s: null, p: [{t: 'M', p: [148.05, 159.37]}, + {t: 'C', p: [147.18, 158.39, 139.91, 153.08, 134.36, 159]}, + {t: 'C', p: [134.36, 159, 124.74, 175.65, 125.48, 181.57]}, + {t: 'L', p: [125.48, 183.42]}, + {t: 'C', p: [125.48, 183.42, 118.45, 183.05, 116.97, 184.9]}, + {t: 'C', p: [116.97, 184.9, 115.86, 189.71, 114.75, 190.08]}, + {t: 'C', p: [114.75, 190.08, 112.16, 192.3, 114.01, 194.89]}, + {t: 'C', p: [114.01, 194.89, 112.16, 197.11, 112.53, 200.81]}, + {t: 'L', p: [119.56, 204.51]}, + {t: 'C', p: [119.56, 204.51, 121.41, 217.83, 131.4, 222.64]}, + {t: 'C', p: [135.873, 224.794, 138.8, 218.57, 140.65, 213.02]}, + {t: 'C', p: [140.65, 213.02, 137.69, 193.41, 139.91, 189.34]}, + {t: 'C', p: [139.91, 189.34, 149.9, 180.09, 149.53, 176.76]}, + {t: 'C', p: [149.53, 176.76, 149.16, 160.11, 148.05, 159.37]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [140.4, 212.46]}, + {t: 'C', p: [140.4, 212.46, 137.52, 193.48, 139.68, 189.52]}, + {t: 'C', p: [139.68, 189.52, 149.4, 180.52, 149.04, 177.28]}, + {t: 'C', p: [149.04, 177.28, 148.68, 161.08, 147.6, 160.36]}, + {t: 'C', p: [146.84, 159.32, 139.68, 154.24, 134.28, 160]}, + {t: 'C', p: [134.28, 160, 124.92, 176.2, 125.64, 181.96]}, + {t: 'L', p: [125.64, 183.76]}, + {t: 'C', p: [125.64, 183.76, 118.8, 183.4, 117.36, 185.2]}, + {t: 'C', p: [117.36, 185.2, 116.28, 189.88, 115.2, 190.24]}, + {t: 'C', p: [115.2, 190.24, 112.68, 192.4, 114.48, 194.92]}, + {t: 'C', p: [114.48, 194.92, 112.68, 197.08, 113.04, 200.68]}, + {t: 'L', p: [119.88, 204.28]}, + {t: 'C', p: [119.88, 204.28, 121.68, 217.24, 131.4, 221.92]}, + {t: 'C', p: [135.752, 224.015, 138.6, 217.86, 140.4, 212.46]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [137.3, 206.2]}, + {t: 'C', p: [137.3, 206.2, 115.7, 196, 114.8, 195.2]}, + {t: 'C', p: [114.8, 195.2, 123.9, 203.4, 124.7, 203.4]}, + {t: 'C', p: [125.5, 203.4, 137.3, 206.2, 137.3, 206.2]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [120.2, 200]}, + {t: 'C', p: [120.2, 200, 138.6, 203.6, 138.6, 208]}, + {t: 'C', p: [138.6, 210.912, 138.357, 224.331, 133, 222.8]}, + {t: 'C', p: [124.6, 220.4, 128.2, 206, 120.2, 200]}, + {t: 'z', p: []}]}, + +{f: '#99cc32', s: null, p: [{t: 'M', p: [128.6, 203.8]}, + {t: 'C', p: [128.6, 203.8, 137.578, 205.274, 138.6, 208]}, + {t: 'C', p: [139.2, 209.6, 139.863, 217.908, 134.4, 219]}, + {t: 'C', p: [129.848, 219.911, 127.618, 209.69, 128.6, 203.8]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [214.595, 246.349]}, + {t: 'C', p: [214.098, 244.607, 215.409, 244.738, 217.2, 244.2]}, + {t: 'C', p: [219.2, 243.6, 231.4, 239.8, 232.2, 237.2]}, + {t: 'C', p: [233, 234.6, 246.2, 239, 246.2, 239]}, + {t: 'C', p: [248, 239.8, 252.4, 242.4, 252.4, 242.4]}, + {t: 'C', p: [257.2, 243.6, 263.8, 244, 263.8, 244]}, + {t: 'C', p: [266.2, 245, 269.6, 247.8, 269.6, 247.8]}, + {t: 'C', p: [284.2, 258, 296.601, 250.8, 296.601, 250.8]}, + {t: 'C', p: [316.601, 244.2, 310.601, 227, 310.601, 227]}, + {t: 'C', p: [307.601, 218, 310.801, 214.6, 310.801, 214.6]}, + {t: 'C', p: [311.001, 210.8, 318.201, 217.2, 318.201, 217.2]}, + {t: 'C', p: [320.801, 221.4, 321.601, 226.4, 321.601, 226.4]}, + {t: 'C', p: [329.601, 237.6, 326.201, 219.8, 326.201, 219.8]}, + {t: 'C', p: [326.401, 218.8, 323.601, 215.2, 323.601, 214]}, + {t: 'C', p: [323.601, 212.8, 321.801, 209.4, 321.801, 209.4]}, + {t: 'C', p: [318.801, 206, 321.201, 199, 321.201, 199]}, + {t: 'C', p: [323.001, 185.2, 320.801, 187, 320.801, 187]}, + {t: 'C', p: [319.601, 185.2, 310.401, 195.2, 310.401, 195.2]}, + {t: 'C', p: [308.201, 198.6, 302.201, 200.2, 302.201, 200.2]}, + {t: 'C', p: [299.401, 202, 296.001, 200.6, 296.001, 200.6]}, + {t: 'C', p: [293.401, 200.2, 287.801, 207.2, 287.801, 207.2]}, + {t: 'C', p: [290.601, 207, 293.001, 211.4, 295.401, 211.6]}, + {t: 'C', p: [297.801, 211.8, 299.601, 209.2, 301.201, 208.6]}, + {t: 'C', p: [302.801, 208, 305.601, 213.8, 305.601, 213.8]}, + {t: 'C', p: [306.001, 216.4, 300.401, 221.2, 300.401, 221.2]}, + {t: 'C', p: [300.001, 225.8, 298.401, 224.2, 298.401, 224.2]}, + {t: 'C', p: [295.401, 223.6, 294.201, 227.4, 293.201, 232]}, + {t: 'C', p: [292.201, 236.6, 288.001, 237, 288.001, 237]}, + {t: 'C', p: [286.401, 244.4, 285.2, 241.4, 285.2, 241.4]}, + {t: 'C', p: [285, 235.8, 279, 241.6, 279, 241.6]}, + {t: 'C', p: [277.8, 243.6, 273.2, 241.4, 273.2, 241.4]}, + {t: 'C', p: [266.4, 239.4, 268.8, 237.4, 268.8, 237.4]}, + {t: 'C', p: [270.6, 235.2, 281.8, 237.4, 281.8, 237.4]}, + {t: 'C', p: [284, 235.8, 276, 231.8, 276, 231.8]}, + {t: 'C', p: [275.4, 230, 276.4, 225.6, 276.4, 225.6]}, + {t: 'C', p: [277.6, 222.4, 284.4, 216.8, 284.4, 216.8]}, + {t: 'C', p: [293.801, 215.6, 291.001, 214, 291.001, 214]}, + {t: 'C', p: [284.801, 208.8, 279, 216.4, 279, 216.4]}, + {t: 'C', p: [276.8, 222.6, 259.4, 237.6, 259.4, 237.6]}, + {t: 'C', p: [254.6, 241, 257.2, 234.2, 253.2, 237.6]}, + {t: 'C', p: [249.2, 241, 228.6, 232, 228.6, 232]}, + {t: 'C', p: [217.038, 230.807, 214.306, 246.549, 210.777, 243.429]}, + {t: 'C', p: [210.777, 243.429, 216.195, 251.949, 214.595, 246.349]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [409.401, 80]}, + {t: 'C', p: [409.401, 80, 383.801, 88, 381.001, 106.8]}, + {t: 'C', p: [381.001, 106.8, 378.601, 129.6, 399.001, 147.2]}, + {t: 'C', p: [399.001, 147.2, 399.401, 153.6, 401.401, 156.8]}, + {t: 'C', p: [401.401, 156.8, 399.801, 161.6, 418.601, 154]}, + {t: 'L', p: [445.801, 145.6]}, + {t: 'C', p: [445.801, 145.6, 452.201, 143.2, 457.401, 134.4]}, + {t: 'C', p: [462.601, 125.6, 477.801, 106.8, 474.201, 81.6]}, + {t: 'C', p: [474.201, 81.6, 475.401, 70.4, 469.401, 70]}, + {t: 'C', p: [469.401, 70, 461.001, 68.4, 453.801, 76]}, + {t: 'C', p: [453.801, 76, 447.001, 79.2, 444.601, 78.8]}, + {t: 'L', p: [409.401, 80]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [464.022, 79.01]}, + {t: 'C', p: [464.022, 79.01, 466.122, 70.08, 461.282, 74.92]}, + {t: 'C', p: [461.282, 74.92, 454.242, 80.64, 446.761, 80.64]}, + {t: 'C', p: [446.761, 80.64, 432.241, 82.84, 427.841, 96.04]}, + {t: 'C', p: [427.841, 96.04, 423.881, 122.88, 431.801, 128.6]}, + {t: 'C', p: [431.801, 128.6, 436.641, 136.08, 443.681, 129.48]}, + {t: 'C', p: [450.722, 122.88, 466.222, 92.65, 464.022, 79.01]}, + {t: 'z', p: []}]}, + +{f: '#323232', s: null, p: [{t: 'M', p: [463.648, 79.368]}, + {t: 'C', p: [463.648, 79.368, 465.738, 70.624, 460.986, 75.376]}, + {t: 'C', p: [460.986, 75.376, 454.074, 80.992, 446.729, 80.992]}, + {t: 'C', p: [446.729, 80.992, 432.473, 83.152, 428.153, 96.112]}, + {t: 'C', p: [428.153, 96.112, 424.265, 122.464, 432.041, 128.08]}, + {t: 'C', p: [432.041, 128.08, 436.793, 135.424, 443.705, 128.944]}, + {t: 'C', p: [450.618, 122.464, 465.808, 92.76, 463.648, 79.368]}, + {t: 'z', p: []}]}, + +{f: '#666', s: null, p: [{t: 'M', p: [463.274, 79.726]}, + {t: 'C', p: [463.274, 79.726, 465.354, 71.168, 460.69, 75.832]}, + {t: 'C', p: [460.69, 75.832, 453.906, 81.344, 446.697, 81.344]}, + {t: 'C', p: [446.697, 81.344, 432.705, 83.464, 428.465, 96.184]}, + {t: 'C', p: [428.465, 96.184, 424.649, 122.048, 432.281, 127.56]}, + {t: 'C', p: [432.281, 127.56, 436.945, 134.768, 443.729, 128.408]}, + {t: 'C', p: [450.514, 122.048, 465.394, 92.87, 463.274, 79.726]}, + {t: 'z', p: []}]}, + +{f: '#999', s: null, p: [{t: 'M', p: [462.9, 80.084]}, + {t: 'C', p: [462.9, 80.084, 464.97, 71.712, 460.394, 76.288]}, + {t: 'C', p: [460.394, 76.288, 453.738, 81.696, 446.665, 81.696]}, + {t: 'C', p: [446.665, 81.696, 432.937, 83.776, 428.777, 96.256]}, + {t: 'C', p: [428.777, 96.256, 425.033, 121.632, 432.521, 127.04]}, + {t: 'C', p: [432.521, 127.04, 437.097, 134.112, 443.753, 127.872]}, + {t: 'C', p: [450.41, 121.632, 464.98, 92.98, 462.9, 80.084]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [462.526, 80.442]}, + {t: 'C', p: [462.526, 80.442, 464.586, 72.256, 460.098, 76.744]}, + {t: 'C', p: [460.098, 76.744, 453.569, 82.048, 446.633, 82.048]}, + {t: 'C', p: [446.633, 82.048, 433.169, 84.088, 429.089, 96.328]}, + {t: 'C', p: [429.089, 96.328, 425.417, 121.216, 432.761, 126.52]}, + {t: 'C', p: [432.761, 126.52, 437.249, 133.456, 443.777, 127.336]}, + {t: 'C', p: [450.305, 121.216, 464.566, 93.09, 462.526, 80.442]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [462.151, 80.8]}, + {t: 'C', p: [462.151, 80.8, 464.201, 72.8, 459.801, 77.2]}, + {t: 'C', p: [459.801, 77.2, 453.401, 82.4, 446.601, 82.4]}, + {t: 'C', p: [446.601, 82.4, 433.401, 84.4, 429.401, 96.4]}, + {t: 'C', p: [429.401, 96.4, 425.801, 120.8, 433.001, 126]}, + {t: 'C', p: [433.001, 126, 437.401, 132.8, 443.801, 126.8]}, + {t: 'C', p: [450.201, 120.8, 464.151, 93.2, 462.151, 80.8]}, + {t: 'z', p: []}]}, + +{f: '#992600', s: null, p: [{t: 'M', p: [250.6, 284]}, + {t: 'C', p: [250.6, 284, 230.2, 264.8, 222.2, 264]}, + {t: 'C', p: [222.2, 264, 187.8, 260, 173, 278]}, + {t: 'C', p: [173, 278, 190.6, 257.6, 218.2, 263.2]}, + {t: 'C', p: [218.2, 263.2, 196.6, 258.8, 184.2, 262]}, + {t: 'C', p: [184.2, 262, 167.4, 262, 157.8, 276]}, + {t: 'L', p: [155, 280.8]}, + {t: 'C', p: [155, 280.8, 159, 266, 177.4, 260]}, + {t: 'C', p: [177.4, 260, 200.2, 255.2, 211, 260]}, + {t: 'C', p: [211, 260, 189.4, 253.2, 179.4, 255.2]}, + {t: 'C', p: [179.4, 255.2, 149, 252.8, 136.2, 279.2]}, + {t: 'C', p: [136.2, 279.2, 140.2, 264.8, 155, 257.6]}, + {t: 'C', p: [155, 257.6, 168.6, 248.8, 189, 251.6]}, + {t: 'C', p: [189, 251.6, 203.4, 254.8, 208.6, 257.2]}, + {t: 'C', p: [213.8, 259.6, 212.6, 256.8, 204.2, 252]}, + {t: 'C', p: [204.2, 252, 198.6, 242, 184.6, 242.4]}, + {t: 'C', p: [184.6, 242.4, 141.8, 246, 131.4, 258]}, + {t: 'C', p: [131.4, 258, 145, 246.8, 155.4, 244]}, + {t: 'C', p: [155.4, 244, 177.8, 236, 186.2, 236.8]}, + {t: 'C', p: [186.2, 236.8, 211, 237.8, 218.6, 233.8]}, + {t: 'C', p: [218.6, 233.8, 207.4, 238.8, 210.6, 242]}, + {t: 'C', p: [213.8, 245.2, 220.6, 252.8, 220.6, 254]}, + {t: 'C', p: [220.6, 255.2, 244.8, 277.3, 248.4, 281.7]}, + {t: 'L', p: [250.6, 284]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [389, 478]}, + {t: 'C', p: [389, 478, 373.5, 441.5, 361, 432]}, + {t: 'C', p: [361, 432, 387, 448, 390.5, 466]}, + {t: 'C', p: [390.5, 466, 390.5, 476, 389, 478]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [436, 485.5]}, + {t: 'C', p: [436, 485.5, 409.5, 430.5, 391, 406.5]}, + {t: 'C', p: [391, 406.5, 434.5, 444, 439.5, 470.5]}, + {t: 'L', p: [440, 476]}, + {t: 'L', p: [437, 473.5]}, + {t: 'C', p: [437, 473.5, 436.5, 482.5, 436, 485.5]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [492.5, 437]}, + {t: 'C', p: [492.5, 437, 430, 377.5, 428.5, 375]}, + {t: 'C', p: [428.5, 375, 489, 441, 492, 448.5]}, + {t: 'C', p: [492, 448.5, 490, 439.5, 492.5, 437]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [304, 480.5]}, + {t: 'C', p: [304, 480.5, 323.5, 428.5, 342.5, 451]}, + {t: 'C', p: [342.5, 451, 357.5, 461, 357, 464]}, + {t: 'C', p: [357, 464, 353, 457.5, 335, 458]}, + {t: 'C', p: [335, 458, 316, 455, 304, 480.5]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [494.5, 353]}, + {t: 'C', p: [494.5, 353, 449.5, 324.5, 442, 323]}, + {t: 'C', p: [430.193, 320.639, 491.5, 352, 496.5, 362.5]}, + {t: 'C', p: [496.5, 362.5, 498.5, 360, 494.5, 353]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [343.801, 459.601]}, + {t: 'C', p: [343.801, 459.601, 364.201, 457.601, 371.001, 450.801]}, + {t: 'L', p: [375.401, 454.401]}, + {t: 'L', p: [393.001, 416.001]}, + {t: 'L', p: [396.601, 421.201]}, + {t: 'C', p: [396.601, 421.201, 411.001, 406.401, 410.201, 398.401]}, + {t: 'C', p: [409.401, 390.401, 423.001, 404.401, 423.001, 404.401]}, + {t: 'C', p: [423.001, 404.401, 422.201, 392.801, 429.401, 399.601]}, + {t: 'C', p: [429.401, 399.601, 427.001, 384.001, 435.401, 392.001]}, + {t: 'C', p: [435.401, 392.001, 424.864, 361.844, 447.401, 387.601]}, + {t: 'C', p: [453.001, 394.001, 448.601, 387.201, 448.601, 387.201]}, + {t: 'C', p: [448.601, 387.201, 422.601, 339.201, 444.201, 353.601]}, + {t: 'C', p: [444.201, 353.601, 446.201, 330.801, 445.001, 326.401]}, + {t: 'C', p: [443.801, 322.001, 441.801, 299.6, 437.001, 294.4]}, + {t: 'C', p: [432.201, 289.2, 437.401, 287.6, 443.001, 292.8]}, + {t: 'C', p: [443.001, 292.8, 431.801, 268.8, 445.001, 280.8]}, + {t: 'C', p: [445.001, 280.8, 441.401, 265.6, 437.001, 262.8]}, + {t: 'C', p: [437.001, 262.8, 431.401, 245.6, 446.601, 256.4]}, + {t: 'C', p: [446.601, 256.4, 442.201, 244, 439.001, 240.8]}, + {t: 'C', p: [439.001, 240.8, 427.401, 213.2, 434.601, 218]}, + {t: 'L', p: [439.001, 221.6]}, + {t: 'C', p: [439.001, 221.6, 432.201, 207.6, 438.601, 212]}, + {t: 'C', p: [445.001, 216.4, 445.001, 216, 445.001, 216]}, + {t: 'C', p: [445.001, 216, 423.801, 182.8, 444.201, 200.4]}, + {t: 'C', p: [444.201, 200.4, 436.042, 186.482, 432.601, 179.6]}, + {t: 'C', p: [432.601, 179.6, 413.801, 159.2, 428.201, 165.6]}, + {t: 'L', p: [433.001, 167.2]}, + {t: 'C', p: [433.001, 167.2, 424.201, 157.2, 416.201, 155.6]}, + {t: 'C', p: [408.201, 154, 418.601, 147.6, 425.001, 149.6]}, + {t: 'C', p: [431.401, 151.6, 447.001, 159.2, 447.001, 159.2]}, + {t: 'C', p: [447.001, 159.2, 459.801, 178, 463.801, 178.4]}, + {t: 'C', p: [463.801, 178.4, 443.801, 170.8, 449.801, 178.8]}, + {t: 'C', p: [449.801, 178.8, 464.201, 192.8, 457.001, 192.4]}, + {t: 'C', p: [457.001, 192.4, 451.001, 199.6, 455.801, 208.4]}, + {t: 'C', p: [455.801, 208.4, 437.342, 190.009, 452.201, 215.6]}, + {t: 'L', p: [459.001, 232]}, + {t: 'C', p: [459.001, 232, 434.601, 207.2, 445.801, 229.2]}, + {t: 'C', p: [445.801, 229.2, 463.001, 252.8, 465.001, 253.2]}, + {t: 'C', p: [467.001, 253.6, 471.401, 262.4, 471.401, 262.4]}, + {t: 'L', p: [467.001, 260.4]}, + {t: 'L', p: [472.201, 269.2]}, + {t: 'C', p: [472.201, 269.2, 461.001, 257.2, 467.001, 270.4]}, + {t: 'L', p: [472.601, 284.8]}, + {t: 'C', p: [472.601, 284.8, 452.201, 262.8, 465.801, 292.4]}, + {t: 'C', p: [465.801, 292.4, 449.401, 287.2, 458.201, 304.4]}, + {t: 'C', p: [458.201, 304.4, 456.601, 320.401, 457.001, 325.601]}, + {t: 'C', p: [457.401, 330.801, 458.601, 359.201, 454.201, 367.201]}, + {t: 'C', p: [449.801, 375.201, 460.201, 394.401, 462.201, 398.401]}, + {t: 'C', p: [464.201, 402.401, 467.801, 413.201, 459.001, 404.001]}, + {t: 'C', p: [450.201, 394.801, 454.601, 400.401, 456.601, 409.201]}, + {t: 'C', p: [458.601, 418.001, 464.601, 433.601, 463.801, 439.201]}, + {t: 'C', p: [463.801, 439.201, 462.601, 440.401, 459.401, 436.801]}, + {t: 'C', p: [459.401, 436.801, 444.601, 414.001, 446.201, 428.401]}, + {t: 'C', p: [446.201, 428.401, 445.001, 436.401, 441.801, 445.201]}, + {t: 'C', p: [441.801, 445.201, 438.601, 456.001, 438.601, 447.201]}, + {t: 'C', p: [438.601, 447.201, 435.401, 430.401, 432.601, 438.001]}, + {t: 'C', p: [429.801, 445.601, 426.201, 451.601, 423.401, 454.001]}, + {t: 'C', p: [420.601, 456.401, 415.401, 433.601, 414.201, 444.001]}, + {t: 'C', p: [414.201, 444.001, 402.201, 431.601, 397.401, 448.001]}, + {t: 'L', p: [385.801, 464.401]}, + {t: 'C', p: [385.801, 464.401, 385.401, 452.001, 384.201, 458.001]}, + {t: 'C', p: [384.201, 458.001, 354.201, 464.001, 343.801, 459.601]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [309.401, 102.8]}, + {t: 'C', p: [309.401, 102.8, 297.801, 94.8, 293.801, 95.2]}, + {t: 'C', p: [289.801, 95.6, 321.401, 86.4, 362.601, 114]}, + {t: 'C', p: [362.601, 114, 367.401, 116.8, 371.001, 116.4]}, + {t: 'C', p: [371.001, 116.4, 374.201, 118.8, 371.401, 122.4]}, + {t: 'C', p: [371.401, 122.4, 362.601, 132, 373.801, 143.2]}, + {t: 'C', p: [373.801, 143.2, 392.201, 150, 386.601, 141.2]}, + {t: 'C', p: [386.601, 141.2, 397.401, 145.2, 399.801, 149.2]}, + {t: 'C', p: [402.201, 153.2, 401.001, 149.2, 401.001, 149.2]}, + {t: 'C', p: [401.001, 149.2, 394.601, 142, 388.601, 136.8]}, + {t: 'C', p: [388.601, 136.8, 383.401, 134.8, 380.601, 126.4]}, + {t: 'C', p: [377.801, 118, 375.401, 108, 379.801, 104.8]}, + {t: 'C', p: [379.801, 104.8, 375.801, 109.2, 376.601, 105.2]}, + {t: 'C', p: [377.401, 101.2, 381.001, 97.6, 382.601, 97.2]}, + {t: 'C', p: [384.201, 96.8, 400.601, 81, 407.401, 80.6]}, + {t: 'C', p: [407.401, 80.6, 398.201, 82, 395.201, 81]}, + {t: 'C', p: [392.201, 80, 365.601, 68.6, 359.601, 67.4]}, + {t: 'C', p: [359.601, 67.4, 342.801, 60.8, 354.801, 62.8]}, + {t: 'C', p: [354.801, 62.8, 390.601, 66.6, 408.801, 79.8]}, + {t: 'C', p: [408.801, 79.8, 401.601, 71.4, 383.201, 64.4]}, + {t: 'C', p: [383.201, 64.4, 361.001, 51.8, 325.801, 56.8]}, + {t: 'C', p: [325.801, 56.8, 308.001, 60, 300.201, 61.8]}, + {t: 'C', p: [300.201, 61.8, 297.601, 61.2, 297.001, 60.8]}, + {t: 'C', p: [296.401, 60.4, 284.6, 51.4, 257, 58.4]}, + {t: 'C', p: [257, 58.4, 240, 63, 231.4, 67.8]}, + {t: 'C', p: [231.4, 67.8, 216.2, 69, 212.6, 72.2]}, + {t: 'C', p: [212.6, 72.2, 194, 86.8, 192, 87.6]}, + {t: 'C', p: [190, 88.4, 178.6, 96, 177.8, 96.4]}, + {t: 'C', p: [177.8, 96.4, 202.4, 89.8, 204.8, 87.4]}, + {t: 'C', p: [207.2, 85, 224.6, 82.4, 227, 83.8]}, + {t: 'C', p: [229.4, 85.2, 237.8, 84.6, 228.2, 85.2]}, + {t: 'C', p: [228.2, 85.2, 303.801, 100, 304.601, 102]}, + {t: 'C', p: [305.401, 104, 309.401, 102.8, 309.401, 102.8]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [380.801, 93.6]}, + {t: 'C', p: [380.801, 93.6, 370.601, 86.2, 368.601, 86.2]}, + {t: 'C', p: [366.601, 86.2, 354.201, 76, 350.001, 76.4]}, + {t: 'C', p: [345.801, 76.8, 333.601, 66.8, 306.201, 75]}, + {t: 'C', p: [306.201, 75, 305.601, 73, 309.201, 72.2]}, + {t: 'C', p: [309.201, 72.2, 315.601, 70, 316.001, 69.4]}, + {t: 'C', p: [316.001, 69.4, 336.201, 65.2, 343.401, 68.8]}, + {t: 'C', p: [343.401, 68.8, 352.601, 71.4, 358.801, 77.6]}, + {t: 'C', p: [358.801, 77.6, 370.001, 80.8, 373.201, 79.8]}, + {t: 'C', p: [373.201, 79.8, 382.001, 82, 382.401, 83.8]}, + {t: 'C', p: [382.401, 83.8, 388.201, 86.8, 386.401, 89.4]}, + {t: 'C', p: [386.401, 89.4, 386.801, 91, 380.801, 93.6]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [368.33, 91.491]}, + {t: 'C', p: [369.137, 92.123, 370.156, 92.221, 370.761, 93.03]}, + {t: 'C', p: [370.995, 93.344, 370.706, 93.67, 370.391, 93.767]}, + {t: 'C', p: [369.348, 94.084, 368.292, 93.514, 367.15, 94.102]}, + {t: 'C', p: [366.748, 94.309, 366.106, 94.127, 365.553, 93.978]}, + {t: 'C', p: [363.921, 93.537, 362.092, 93.512, 360.401, 94.2]}, + {t: 'C', p: [358.416, 93.071, 356.056, 93.655, 353.975, 92.654]}, + {t: 'C', p: [353.917, 92.627, 353.695, 92.973, 353.621, 92.946]}, + {t: 'C', p: [350.575, 91.801, 346.832, 92.084, 344.401, 89.8]}, + {t: 'C', p: [341.973, 89.388, 339.616, 88.926, 337.188, 88.246]}, + {t: 'C', p: [335.37, 87.737, 333.961, 86.748, 332.341, 85.916]}, + {t: 'C', p: [330.964, 85.208, 329.507, 84.686, 327.973, 84.314]}, + {t: 'C', p: [326.11, 83.862, 324.279, 83.974, 322.386, 83.454]}, + {t: 'C', p: [322.293, 83.429, 322.101, 83.773, 322.019, 83.746]}, + {t: 'C', p: [321.695, 83.638, 321.405, 83.055, 321.234, 83.108]}, + {t: 'C', p: [319.553, 83.63, 318.065, 82.658, 316.401, 83]}, + {t: 'C', p: [315.223, 81.776, 313.495, 82.021, 311.949, 81.579]}, + {t: 'C', p: [308.985, 80.731, 305.831, 82.001, 302.801, 81]}, + {t: 'C', p: [306.914, 79.158, 311.601, 80.39, 315.663, 78.321]}, + {t: 'C', p: [317.991, 77.135, 320.653, 78.237, 323.223, 77.477]}, + {t: 'C', p: [323.71, 77.333, 324.401, 77.131, 324.801, 77.8]}, + {t: 'C', p: [324.935, 77.665, 325.117, 77.426, 325.175, 77.454]}, + {t: 'C', p: [327.625, 78.611, 329.94, 79.885, 332.422, 80.951]}, + {t: 'C', p: [332.763, 81.097, 333.295, 80.865, 333.547, 81.067]}, + {t: 'C', p: [335.067, 82.283, 337.01, 82.18, 338.401, 83.4]}, + {t: 'C', p: [340.099, 82.898, 341.892, 83.278, 343.621, 82.654]}, + {t: 'C', p: [343.698, 82.627, 343.932, 82.968, 343.965, 82.946]}, + {t: 'C', p: [345.095, 82.198, 346.25, 82.469, 347.142, 82.773]}, + {t: 'C', p: [347.48, 82.888, 348.143, 83.135, 348.448, 83.209]}, + {t: 'C', p: [349.574, 83.485, 350.43, 83.965, 351.609, 84.148]}, + {t: 'C', p: [351.723, 84.166, 351.908, 83.826, 351.98, 83.854]}, + {t: 'C', p: [353.103, 84.292, 354.145, 84.236, 354.801, 85.4]}, + {t: 'C', p: [354.936, 85.265, 355.101, 85.027, 355.183, 85.054]}, + {t: 'C', p: [356.21, 85.392, 356.859, 86.147, 357.96, 86.388]}, + {t: 'C', p: [358.445, 86.494, 359.057, 87.12, 359.633, 87.296]}, + {t: 'C', p: [362.025, 88.027, 363.868, 89.556, 366.062, 90.451]}, + {t: 'C', p: [366.821, 90.761, 367.697, 90.995, 368.33, 91.491]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [291.696, 77.261]}, + {t: 'C', p: [289.178, 75.536, 286.81, 74.43, 284.368, 72.644]}, + {t: 'C', p: [284.187, 72.511, 283.827, 72.681, 283.625, 72.559]}, + {t: 'C', p: [282.618, 71.95, 281.73, 71.369, 280.748, 70.673]}, + {t: 'C', p: [280.209, 70.291, 279.388, 70.302, 278.88, 70.044]}, + {t: 'C', p: [276.336, 68.752, 273.707, 68.194, 271.2, 67]}, + {t: 'C', p: [271.882, 66.362, 273.004, 66.606, 273.6, 65.8]}, + {t: 'C', p: [273.795, 66.08, 274.033, 66.364, 274.386, 66.173]}, + {t: 'C', p: [276.064, 65.269, 277.914, 65.116, 279.59, 65.206]}, + {t: 'C', p: [281.294, 65.298, 283.014, 65.603, 284.789, 65.875]}, + {t: 'C', p: [285.096, 65.922, 285.295, 66.445, 285.618, 66.542]}, + {t: 'C', p: [287.846, 67.205, 290.235, 66.68, 292.354, 67.518]}, + {t: 'C', p: [293.945, 68.147, 295.515, 68.97, 296.754, 70.245]}, + {t: 'C', p: [297.006, 70.505, 296.681, 70.806, 296.401, 71]}, + {t: 'C', p: [296.789, 70.891, 297.062, 71.097, 297.173, 71.41]}, + {t: 'C', p: [297.257, 71.649, 297.257, 71.951, 297.173, 72.19]}, + {t: 'C', p: [297.061, 72.502, 296.782, 72.603, 296.408, 72.654]}, + {t: 'C', p: [295.001, 72.844, 296.773, 71.464, 296.073, 71.912]}, + {t: 'C', p: [294.8, 72.726, 295.546, 74.132, 294.801, 75.4]}, + {t: 'C', p: [294.521, 75.206, 294.291, 74.988, 294.401, 74.6]}, + {t: 'C', p: [294.635, 75.122, 294.033, 75.412, 293.865, 75.728]}, + {t: 'C', p: [293.48, 76.453, 292.581, 77.868, 291.696, 77.261]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [259.198, 84.609]}, + {t: 'C', p: [256.044, 83.815, 252.994, 83.93, 249.978, 82.654]}, + {t: 'C', p: [249.911, 82.626, 249.688, 82.973, 249.624, 82.946]}, + {t: 'C', p: [248.258, 82.352, 247.34, 81.386, 246.264, 80.34]}, + {t: 'C', p: [245.351, 79.452, 243.693, 79.839, 242.419, 79.352]}, + {t: 'C', p: [242.095, 79.228, 241.892, 78.716, 241.591, 78.677]}, + {t: 'C', p: [240.372, 78.52, 239.445, 77.571, 238.4, 77]}, + {t: 'C', p: [240.736, 76.205, 243.147, 76.236, 245.609, 75.852]}, + {t: 'C', p: [245.722, 75.834, 245.867, 76.155, 246, 76.155]}, + {t: 'C', p: [246.136, 76.155, 246.266, 75.934, 246.4, 75.8]}, + {t: 'C', p: [246.595, 76.08, 246.897, 76.406, 247.154, 76.152]}, + {t: 'C', p: [247.702, 75.612, 248.258, 75.802, 248.798, 75.842]}, + {t: 'C', p: [248.942, 75.852, 249.067, 76.155, 249.2, 76.155]}, + {t: 'C', p: [249.336, 76.155, 249.467, 75.844, 249.6, 75.844]}, + {t: 'C', p: [249.736, 75.845, 249.867, 76.155, 250, 76.155]}, + {t: 'C', p: [250.136, 76.155, 250.266, 75.934, 250.4, 75.8]}, + {t: 'C', p: [251.092, 76.582, 251.977, 76.028, 252.799, 76.207]}, + {t: 'C', p: [253.837, 76.434, 254.104, 77.582, 255.178, 77.88]}, + {t: 'C', p: [259.893, 79.184, 264.03, 81.329, 268.393, 83.416]}, + {t: 'C', p: [268.7, 83.563, 268.91, 83.811, 268.8, 84.2]}, + {t: 'C', p: [269.067, 84.2, 269.38, 84.112, 269.57, 84.244]}, + {t: 'C', p: [270.628, 84.976, 271.669, 85.524, 272.366, 86.622]}, + {t: 'C', p: [272.582, 86.961, 272.253, 87.368, 272.02, 87.316]}, + {t: 'C', p: [267.591, 86.321, 263.585, 85.713, 259.198, 84.609]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [245.338, 128.821]}, + {t: 'C', p: [243.746, 127.602, 243.162, 125.571, 242.034, 123.779]}, + {t: 'C', p: [241.82, 123.439, 242.094, 123.125, 242.411, 123.036]}, + {t: 'C', p: [242.971, 122.877, 243.514, 123.355, 243.923, 123.557]}, + {t: 'C', p: [245.668, 124.419, 247.203, 125.661, 249.2, 125.8]}, + {t: 'C', p: [251.19, 128.034, 255.45, 128.419, 255.457, 131.8]}, + {t: 'C', p: [255.458, 132.659, 254.03, 131.741, 253.6, 132.6]}, + {t: 'C', p: [251.149, 131.597, 248.76, 131.7, 246.38, 130.233]}, + {t: 'C', p: [245.763, 129.852, 246.093, 129.399, 245.338, 128.821]}, + {t: 'z', p: []}]}, + +{f: '#cc7226', s: null, p: [{t: 'M', p: [217.8, 76.244]}, + {t: 'C', p: [217.935, 76.245, 224.966, 76.478, 224.949, 76.592]}, + {t: 'C', p: [224.904, 76.901, 217.174, 77.95, 216.81, 77.78]}, + {t: 'C', p: [216.646, 77.704, 209.134, 80.134, 209, 80]}, + {t: 'C', p: [209.268, 79.865, 217.534, 76.244, 217.8, 76.244]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [233.2, 86]}, + {t: 'C', p: [233.2, 86, 218.4, 87.8, 214, 89]}, + {t: 'C', p: [209.6, 90.2, 191, 97.8, 188, 99.8]}, + {t: 'C', p: [188, 99.8, 174.6, 105.2, 157.6, 125.2]}, + {t: 'C', p: [157.6, 125.2, 165.2, 121.8, 167.4, 119]}, + {t: 'C', p: [167.4, 119, 181, 106.4, 180.8, 109]}, + {t: 'C', p: [180.8, 109, 193, 100.4, 192.4, 102.6]}, + {t: 'C', p: [192.4, 102.6, 216.8, 91.4, 214.8, 94.6]}, + {t: 'C', p: [214.8, 94.6, 236.4, 90, 235.4, 92]}, + {t: 'C', p: [235.4, 92, 254.2, 96.4, 251.4, 96.6]}, + {t: 'C', p: [251.4, 96.6, 245.6, 97.8, 252, 101.4]}, + {t: 'C', p: [252, 101.4, 248.6, 105.8, 243.2, 101.8]}, + {t: 'C', p: [237.8, 97.8, 240.8, 100, 235.8, 101]}, + {t: 'C', p: [235.8, 101, 233.2, 101.8, 228.6, 97.8]}, + {t: 'C', p: [228.6, 97.8, 223, 93.2, 214.2, 96.8]}, + {t: 'C', p: [214.2, 96.8, 183.6, 109.4, 181.6, 110]}, + {t: 'C', p: [181.6, 110, 178, 112.8, 175.6, 116.4]}, + {t: 'C', p: [175.6, 116.4, 169.8, 120.8, 166.8, 122.2]}, + {t: 'C', p: [166.8, 122.2, 154, 133.8, 152.8, 135.2]}, + {t: 'C', p: [152.8, 135.2, 149.4, 140.4, 148.6, 140.8]}, + {t: 'C', p: [148.6, 140.8, 155, 137, 157, 135]}, + {t: 'C', p: [157, 135, 171, 125, 176.4, 124.2]}, + {t: 'C', p: [176.4, 124.2, 180.8, 121.2, 181.6, 119.8]}, + {t: 'C', p: [181.6, 119.8, 196, 110.6, 200.2, 110.6]}, + {t: 'C', p: [200.2, 110.6, 209.4, 115.8, 211.8, 108.8]}, + {t: 'C', p: [211.8, 108.8, 217.6, 107, 223.2, 108.2]}, + {t: 'C', p: [223.2, 108.2, 226.4, 105.6, 225.6, 103.4]}, + {t: 'C', p: [225.6, 103.4, 227.2, 101.6, 228.2, 105.4]}, + {t: 'C', p: [228.2, 105.4, 231.6, 109, 236.4, 107]}, + {t: 'C', p: [236.4, 107, 240.4, 106.8, 238.4, 109.2]}, + {t: 'C', p: [238.4, 109.2, 234, 113, 222.2, 113.2]}, + {t: 'C', p: [222.2, 113.2, 209.8, 113.8, 193.4, 121.4]}, + {t: 'C', p: [193.4, 121.4, 163.6, 131.8, 154.4, 142.2]}, + {t: 'C', p: [154.4, 142.2, 148, 151, 142.6, 152.2]}, + {t: 'C', p: [142.6, 152.2, 136.8, 153, 130.8, 160.4]}, + {t: 'C', p: [130.8, 160.4, 140.6, 154.6, 149.6, 154.6]}, + {t: 'C', p: [149.6, 154.6, 153.6, 152.2, 149.8, 155.8]}, + {t: 'C', p: [149.8, 155.8, 146.2, 163.4, 147.8, 168.8]}, + {t: 'C', p: [147.8, 168.8, 147.2, 174, 146.4, 175.6]}, + {t: 'C', p: [146.4, 175.6, 138.6, 188.4, 138.6, 190.8]}, + {t: 'C', p: [138.6, 193.2, 139.8, 203, 140.2, 203.6]}, + {t: 'C', p: [140.6, 204.2, 139.2, 202, 143, 204.4]}, + {t: 'C', p: [146.8, 206.8, 149.6, 208.4, 150.4, 211.2]}, + {t: 'C', p: [151.2, 214, 148.4, 205.8, 148.2, 204]}, + {t: 'C', p: [148, 202.2, 143.8, 195, 144.6, 192.6]}, + {t: 'C', p: [144.6, 192.6, 145.6, 193.6, 146.4, 195]}, + {t: 'C', p: [146.4, 195, 145.8, 194.4, 146.4, 190.8]}, + {t: 'C', p: [146.4, 190.8, 147.2, 185.6, 148.6, 182.4]}, + {t: 'C', p: [150, 179.2, 152, 175.4, 152.4, 174.6]}, + {t: 'C', p: [152.8, 173.8, 152.8, 168, 154.2, 170.6]}, + {t: 'L', p: [157.6, 173.2]}, + {t: 'C', p: [157.6, 173.2, 154.8, 170.6, 157, 168.4]}, + {t: 'C', p: [157, 168.4, 156, 162.8, 157.8, 160.2]}, + {t: 'C', p: [157.8, 160.2, 164.8, 151.8, 166.4, 150.8]}, + {t: 'C', p: [168, 149.8, 166.6, 150.2, 166.6, 150.2]}, + {t: 'C', p: [166.6, 150.2, 172.6, 146, 166.8, 147.6]}, + {t: 'C', p: [166.8, 147.6, 162.8, 149.2, 159.8, 149.2]}, + {t: 'C', p: [159.8, 149.2, 152.2, 151.2, 156.2, 147]}, + {t: 'C', p: [160.2, 142.8, 170.2, 137.4, 174, 137.6]}, + {t: 'L', p: [174.8, 139.2]}, + {t: 'L', p: [186, 136.8]}, + {t: 'L', p: [184.8, 137.6]}, + {t: 'C', p: [184.8, 137.6, 184.6, 137.4, 188.8, 137]}, + {t: 'C', p: [193, 136.6, 198.8, 138, 200.2, 136.2]}, + {t: 'C', p: [201.6, 134.4, 205, 133.4, 204.6, 134.8]}, + {t: 'C', p: [204.2, 136.2, 204, 138.2, 204, 138.2]}, + {t: 'C', p: [204, 138.2, 209, 132.4, 208.4, 134.6]}, + {t: 'C', p: [207.8, 136.8, 199.6, 142, 198.2, 148.2]}, + {t: 'L', p: [208.6, 140]}, + {t: 'L', p: [212.2, 137]}, + {t: 'C', p: [212.2, 137, 215.8, 139.2, 216, 137.6]}, + {t: 'C', p: [216.2, 136, 220.8, 130.2, 222, 130.4]}, + {t: 'C', p: [223.2, 130.6, 225.2, 127.8, 225, 130.4]}, + {t: 'C', p: [224.8, 133, 232.4, 138.4, 232.4, 138.4]}, + {t: 'C', p: [232.4, 138.4, 235.6, 136.6, 237, 138]}, + {t: 'C', p: [238.4, 139.4, 242.6, 118.2, 242.6, 118.2]}, + {t: 'L', p: [267.6, 107.6]}, + {t: 'L', p: [311.201, 104.2]}, + {t: 'L', p: [294.201, 97.4]}, + {t: 'L', p: [233.2, 86]}, + {t: 'z', p: []}]}, + +{f: null, s: {c: '#4c0000', w: 2}, + p: [{t: 'M', p: [251.4, 285]}, + {t: 'C', p: [251.4, 285, 236.4, 268.2, 228, 265.6]}, + {t: 'C', p: [228, 265.6, 214.6, 258.8, 190, 266.6]}]}, + +{f: null, s: {c: '#4c0000', w: 2}, + p: [{t: 'M', p: [224.8, 264.2]}, + {t: 'C', p: [224.8, 264.2, 199.6, 256.2, 184.2, 260.4]}, + {t: 'C', p: [184.2, 260.4, 165.8, 262.4, 157.4, 276.2]}]}, + +{f: null, s: {c: '#4c0000', w: 2}, + p: [{t: 'M', p: [221.2, 263]}, + {t: 'C', p: [221.2, 263, 204.2, 255.8, 189.4, 253.6]}, + {t: 'C', p: [189.4, 253.6, 172.8, 251, 156.2, 258.2]}, + {t: 'C', p: [156.2, 258.2, 144, 264.2, 138.6, 274.4]}]}, + +{f: null, s: {c: '#4c0000', w: 2}, + p: [{t: 'M', p: [222.2, 263.4]}, + {t: 'C', p: [222.2, 263.4, 206.8, 252.4, 205.8, 251]}, + {t: 'C', p: [205.8, 251, 198.8, 240, 185.8, 239.6]}, + {t: 'C', p: [185.8, 239.6, 164.4, 240.4, 147.2, 248.4]}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [220.895, 254.407]}, + {t: 'C', p: [222.437, 255.87, 249.4, 284.8, 249.4, 284.8]}, + {t: 'C', p: [284.6, 321.401, 256.6, 287.2, 256.6, 287.2]}, + {t: 'C', p: [249, 282.4, 239.8, 263.6, 239.8, 263.6]}, + {t: 'C', p: [238.6, 260.8, 253.8, 270.8, 253.8, 270.8]}, + {t: 'C', p: [257.8, 271.6, 271.4, 290.8, 271.4, 290.8]}, + {t: 'C', p: [264.6, 288.4, 269.4, 295.6, 269.4, 295.6]}, + {t: 'C', p: [272.2, 297.6, 292.601, 313.201, 292.601, 313.201]}, + {t: 'C', p: [296.201, 317.201, 300.201, 318.801, 300.201, 318.801]}, + {t: 'C', p: [314.201, 313.601, 307.801, 326.801, 307.801, 326.801]}, + {t: 'C', p: [310.201, 333.601, 315.801, 322.001, 315.801, 322.001]}, + {t: 'C', p: [327.001, 305.2, 310.601, 307.601, 310.601, 307.601]}, + {t: 'C', p: [280.6, 310.401, 273.8, 294.4, 273.8, 294.4]}, + {t: 'C', p: [271.4, 292, 280.2, 294.4, 280.2, 294.4]}, + {t: 'C', p: [288.601, 296.4, 273, 282, 273, 282]}, + {t: 'C', p: [275.4, 282, 284.6, 288.8, 284.6, 288.8]}, + {t: 'C', p: [295.001, 298, 297.001, 296, 297.001, 296]}, + {t: 'C', p: [315.001, 287.2, 325.401, 294.8, 325.401, 294.8]}, + {t: 'C', p: [327.401, 296.4, 321.801, 303.2, 323.401, 308.401]}, + {t: 'C', p: [325.001, 313.601, 329.801, 326.001, 329.801, 326.001]}, + {t: 'C', p: [327.401, 327.601, 327.801, 338.401, 327.801, 338.401]}, + {t: 'C', p: [344.601, 361.601, 335.001, 359.601, 335.001, 359.601]}, + {t: 'C', p: [319.401, 359.201, 334.201, 366.801, 334.201, 366.801]}, + {t: 'C', p: [337.401, 368.801, 346.201, 376.001, 346.201, 376.001]}, + {t: 'C', p: [343.401, 374.801, 341.801, 380.001, 341.801, 380.001]}, + {t: 'C', p: [346.601, 384.001, 343.801, 388.801, 343.801, 388.801]}, + {t: 'C', p: [337.801, 390.001, 336.601, 394.001, 336.601, 394.001]}, + {t: 'C', p: [343.401, 402.001, 333.401, 402.401, 333.401, 402.401]}, + {t: 'C', p: [337.001, 406.801, 332.201, 418.801, 332.201, 418.801]}, + {t: 'C', p: [327.401, 418.801, 321.001, 424.401, 321.001, 424.401]}, + {t: 'C', p: [323.401, 429.201, 313.001, 434.801, 313.001, 434.801]}, + {t: 'C', p: [304.601, 436.401, 307.401, 443.201, 307.401, 443.201]}, + {t: 'C', p: [299.401, 449.201, 297.001, 465.201, 297.001, 465.201]}, + {t: 'C', p: [296.201, 475.601, 293.801, 478.801, 299.001, 476.801]}, + {t: 'C', p: [304.201, 474.801, 303.401, 462.401, 303.401, 462.401]}, + {t: 'C', p: [298.601, 446.801, 341.401, 430.801, 341.401, 430.801]}, + {t: 'C', p: [345.401, 429.201, 346.201, 424.001, 346.201, 424.001]}, + {t: 'C', p: [348.201, 424.401, 357.001, 432.001, 357.001, 432.001]}, + {t: 'C', p: [364.601, 443.201, 365.001, 434.001, 365.001, 434.001]}, + {t: 'C', p: [366.201, 430.401, 364.601, 424.401, 364.601, 424.401]}, + {t: 'C', p: [370.601, 402.801, 356.601, 396.401, 356.601, 396.401]}, + {t: 'C', p: [346.601, 362.801, 360.601, 371.201, 360.601, 371.201]}, + {t: 'C', p: [363.401, 376.801, 374.201, 382.001, 374.201, 382.001]}, + {t: 'L', p: [377.801, 379.601]}, + {t: 'C', p: [376.201, 374.801, 384.601, 368.801, 384.601, 368.801]}, + {t: 'C', p: [387.401, 375.201, 393.401, 367.201, 393.401, 367.201]}, + {t: 'C', p: [397.001, 342.801, 409.401, 357.201, 409.401, 357.201]}, + {t: 'C', p: [413.401, 358.401, 414.601, 351.601, 414.601, 351.601]}, + {t: 'C', p: [418.201, 341.201, 414.601, 327.601, 414.601, 327.601]}, + {t: 'C', p: [418.201, 327.201, 427.801, 333.201, 427.801, 333.201]}, + {t: 'C', p: [430.601, 329.601, 421.401, 312.801, 425.401, 315.201]}, + {t: 'C', p: [429.401, 317.601, 433.801, 319.201, 433.801, 319.201]}, + {t: 'C', p: [434.601, 317.201, 424.601, 304.801, 424.601, 304.801]}, + {t: 'C', p: [420.201, 302, 415.001, 281.6, 415.001, 281.6]}, + {t: 'C', p: [422.201, 285.2, 412.201, 270, 412.201, 270]}, + {t: 'C', p: [412.201, 266.8, 418.201, 255.6, 418.201, 255.6]}, + {t: 'C', p: [417.401, 248.8, 418.201, 249.2, 418.201, 249.2]}, + {t: 'C', p: [421.001, 250.4, 429.001, 252, 422.201, 245.6]}, + {t: 'C', p: [415.401, 239.2, 423.001, 234.4, 423.001, 234.4]}, + {t: 'C', p: [427.401, 231.6, 413.801, 232, 413.801, 232]}, + {t: 'C', p: [408.601, 227.6, 409.001, 223.6, 409.001, 223.6]}, + {t: 'C', p: [417.001, 225.6, 402.601, 211.2, 400.201, 207.6]}, + {t: 'C', p: [397.801, 204, 407.401, 198.8, 407.401, 198.8]}, + {t: 'C', p: [420.601, 195.2, 409.001, 192, 409.001, 192]}, + {t: 'C', p: [389.401, 192.4, 400.201, 181.6, 400.201, 181.6]}, + {t: 'C', p: [406.201, 182, 404.601, 179.6, 404.601, 179.6]}, + {t: 'C', p: [399.401, 178.4, 389.801, 172, 389.801, 172]}, + {t: 'C', p: [385.801, 168.4, 389.401, 169.2, 389.401, 169.2]}, + {t: 'C', p: [406.201, 170.4, 377.401, 159.2, 377.401, 159.2]}, + {t: 'C', p: [385.401, 159.2, 367.401, 148.8, 367.401, 148.8]}, + {t: 'C', p: [365.401, 147.2, 362.201, 139.6, 362.201, 139.6]}, + {t: 'C', p: [356.201, 134.4, 351.401, 127.6, 351.401, 127.6]}, + {t: 'C', p: [351.001, 123.2, 346.201, 118.4, 346.201, 118.4]}, + {t: 'C', p: [334.601, 104.8, 329.001, 105.2, 329.001, 105.2]}, + {t: 'C', p: [314.201, 101.6, 309.001, 102.4, 309.001, 102.4]}, + {t: 'L', p: [256.2, 106.8]}, + {t: 'C', p: [229.8, 119.6, 237.6, 140.6, 237.6, 140.6]}, + {t: 'C', p: [244, 149, 253.2, 145.2, 253.2, 145.2]}, + {t: 'C', p: [257.8, 139, 269.4, 141.2, 269.4, 141.2]}, + {t: 'C', p: [289.801, 144.4, 287.201, 140.8, 287.201, 140.8]}, + {t: 'C', p: [284.801, 136.2, 268.6, 130, 268.4, 129.4]}, + {t: 'C', p: [268.2, 128.8, 259.4, 125.4, 259.4, 125.4]}, + {t: 'C', p: [256.4, 124.2, 252, 115, 252, 115]}, + {t: 'C', p: [248.8, 111.6, 264.6, 117.4, 264.6, 117.4]}, + {t: 'C', p: [263.4, 118.4, 270.8, 122.4, 270.8, 122.4]}, + {t: 'C', p: [288.201, 121.4, 298.801, 132.2, 298.801, 132.2]}, + {t: 'C', p: [309.601, 148.8, 309.801, 140.6, 309.801, 140.6]}, + {t: 'C', p: [312.601, 131.2, 300.801, 110, 300.801, 110]}, + {t: 'C', p: [301.201, 108, 309.401, 114.6, 309.401, 114.6]}, + {t: 'C', p: [310.801, 112.6, 311.601, 118.4, 311.601, 118.4]}, + {t: 'C', p: [311.801, 120.8, 315.601, 128.8, 315.601, 128.8]}, + {t: 'C', p: [318.401, 141.8, 322.001, 134.4, 322.001, 134.4]}, + {t: 'L', p: [326.601, 143.8]}, + {t: 'C', p: [328.001, 146.4, 322.001, 154, 322.001, 154]}, + {t: 'C', p: [321.801, 156.8, 322.601, 156.6, 317.001, 164.2]}, + {t: 'C', p: [311.401, 171.8, 314.801, 176.2, 314.801, 176.2]}, + {t: 'C', p: [313.401, 182.8, 322.201, 182.4, 322.201, 182.4]}, + {t: 'C', p: [324.801, 184.6, 328.201, 184.6, 328.201, 184.6]}, + {t: 'C', p: [330.001, 186.6, 332.401, 186, 332.401, 186]}, + {t: 'C', p: [334.001, 182.2, 340.201, 184.2, 340.201, 184.2]}, + {t: 'C', p: [341.601, 181.8, 349.801, 181.4, 349.801, 181.4]}, + {t: 'C', p: [350.801, 178.8, 351.201, 177.2, 354.601, 176.6]}, + {t: 'C', p: [358.001, 176, 333.401, 133, 333.401, 133]}, + {t: 'C', p: [339.801, 132.2, 331.601, 119.8, 331.601, 119.8]}, + {t: 'C', p: [329.401, 113.2, 340.801, 127.8, 343.001, 129.2]}, + {t: 'C', p: [345.201, 130.6, 346.201, 132.8, 344.601, 132.6]}, + {t: 'C', p: [343.001, 132.4, 341.201, 134.6, 342.601, 134.8]}, + {t: 'C', p: [344.001, 135, 357.001, 150, 360.401, 160.2]}, + {t: 'C', p: [363.801, 170.4, 369.801, 174.4, 376.001, 180.4]}, + {t: 'C', p: [382.201, 186.4, 381.401, 210.6, 381.401, 210.6]}, + {t: 'C', p: [381.001, 219.4, 387.001, 230, 387.001, 230]}, + {t: 'C', p: [389.001, 233.8, 384.801, 252, 384.801, 252]}, + {t: 'C', p: [382.801, 254.2, 384.201, 255, 384.201, 255]}, + {t: 'C', p: [385.201, 256.2, 392.001, 269.4, 392.001, 269.4]}, + {t: 'C', p: [390.201, 269.2, 393.801, 272.8, 393.801, 272.8]}, + {t: 'C', p: [399.001, 278.8, 392.601, 275.8, 392.601, 275.8]}, + {t: 'C', p: [386.601, 274.2, 393.601, 284, 393.601, 284]}, + {t: 'C', p: [394.801, 285.8, 385.801, 281.2, 385.801, 281.2]}, + {t: 'C', p: [376.601, 280.6, 388.201, 287.8, 388.201, 287.8]}, + {t: 'C', p: [396.801, 295, 385.401, 290.6, 385.401, 290.6]}, + {t: 'C', p: [380.801, 288.8, 384.001, 295.6, 384.001, 295.6]}, + {t: 'C', p: [387.201, 297.2, 404.401, 304.2, 404.401, 304.2]}, + {t: 'C', p: [404.801, 308.001, 401.801, 313.001, 401.801, 313.001]}, + {t: 'C', p: [402.201, 317.001, 400.001, 320.401, 400.001, 320.401]}, + {t: 'C', p: [398.801, 328.601, 398.201, 329.401, 398.201, 329.401]}, + {t: 'C', p: [394.001, 329.601, 386.601, 343.401, 386.601, 343.401]}, + {t: 'C', p: [384.801, 346.001, 374.601, 358.001, 374.601, 358.001]}, + {t: 'C', p: [372.601, 365.001, 354.601, 357.801, 354.601, 357.801]}, + {t: 'C', p: [348.001, 361.201, 350.001, 357.801, 350.001, 357.801]}, + {t: 'C', p: [349.601, 355.601, 354.401, 349.601, 354.401, 349.601]}, + {t: 'C', p: [361.401, 347.001, 358.801, 336.201, 358.801, 336.201]}, + {t: 'C', p: [362.801, 334.801, 351.601, 332.001, 351.801, 330.801]}, + {t: 'C', p: [352.001, 329.601, 357.801, 328.201, 357.801, 328.201]}, + {t: 'C', p: [365.801, 326.201, 361.401, 323.801, 361.401, 323.801]}, + {t: 'C', p: [360.801, 319.801, 363.801, 314.201, 363.801, 314.201]}, + {t: 'C', p: [375.401, 313.401, 363.801, 297.2, 363.801, 297.2]}, + {t: 'C', p: [353.001, 289.6, 352.001, 283.8, 352.001, 283.8]}, + {t: 'C', p: [364.601, 275.6, 356.401, 263.2, 356.601, 259.6]}, + {t: 'C', p: [356.801, 256, 358.001, 234.4, 358.001, 234.4]}, + {t: 'C', p: [356.001, 228.2, 353.001, 214.6, 353.001, 214.6]}, + {t: 'C', p: [355.201, 209.4, 362.601, 196.8, 362.601, 196.8]}, + {t: 'C', p: [365.401, 192.6, 374.201, 187.8, 372.001, 184.8]}, + {t: 'C', p: [369.801, 181.8, 362.001, 183.6, 362.001, 183.6]}, + {t: 'C', p: [354.201, 182.2, 354.801, 187.4, 354.801, 187.4]}, + {t: 'C', p: [353.201, 188.4, 352.401, 193.4, 352.401, 193.4]}, + {t: 'C', p: [351.68, 201.333, 342.801, 207.6, 342.801, 207.6]}, + {t: 'C', p: [331.601, 213.8, 340.801, 217.8, 340.801, 217.8]}, + {t: 'C', p: [346.801, 224.4, 337.001, 224.6, 337.001, 224.6]}, + {t: 'C', p: [326.001, 222.8, 334.201, 233, 334.201, 233]}, + {t: 'C', p: [345.001, 245.8, 342.001, 248.6, 342.001, 248.6]}, + {t: 'C', p: [331.801, 249.6, 344.401, 258.8, 344.401, 258.8]}, + {t: 'C', p: [344.401, 258.8, 343.601, 256.8, 343.801, 258.6]}, + {t: 'C', p: [344.001, 260.4, 347.001, 264.6, 347.801, 266.6]}, + {t: 'C', p: [348.601, 268.6, 344.601, 268.8, 344.601, 268.8]}, + {t: 'C', p: [345.201, 278.4, 329.801, 274.2, 329.801, 274.2]}, + {t: 'C', p: [329.801, 274.2, 329.801, 274.2, 328.201, 274.4]}, + {t: 'C', p: [326.601, 274.6, 315.401, 273.8, 309.601, 271.6]}, + {t: 'C', p: [303.801, 269.4, 297.001, 269.4, 297.001, 269.4]}, + {t: 'C', p: [297.001, 269.4, 293.001, 271.2, 285.4, 271]}, + {t: 'C', p: [277.8, 270.8, 269.8, 273.6, 269.8, 273.6]}, + {t: 'C', p: [265.4, 273.2, 274, 268.8, 274.2, 269]}, + {t: 'C', p: [274.4, 269.2, 280, 263.6, 272, 264.2]}, + {t: 'C', p: [250.203, 265.835, 239.4, 255.6, 239.4, 255.6]}, + {t: 'C', p: [237.4, 254.2, 234.8, 251.4, 234.8, 251.4]}, + {t: 'C', p: [224.8, 249.4, 236.2, 263.8, 236.2, 263.8]}, + {t: 'C', p: [237.4, 265.2, 236, 266.2, 236, 266.2]}, + {t: 'C', p: [235.2, 264.6, 227.4, 259.2, 227.4, 259.2]}, + {t: 'C', p: [224.589, 258.227, 223.226, 256.893, 220.895, 254.407]}, + {t: 'z', p: []}]}, + +{f: '#4c0000', s: null, p: [{t: 'M', p: [197, 242.8]}, + {t: 'C', p: [197, 242.8, 208.6, 248.4, 211.2, 251.2]}, + {t: 'C', p: [213.8, 254, 227.8, 265.4, 227.8, 265.4]}, + {t: 'C', p: [227.8, 265.4, 222.4, 263.4, 219.8, 261.6]}, + {t: 'C', p: [217.2, 259.8, 206.4, 251.6, 206.4, 251.6]}, + {t: 'C', p: [206.4, 251.6, 202.6, 245.6, 197, 242.8]}, + {t: 'z', p: []}]}, + +{f: '#99cc32', s: null, p: [{t: 'M', p: [138.991, 211.603]}, + {t: 'C', p: [139.328, 211.455, 138.804, 208.743, 138.6, 208.2]}, + {t: 'C', p: [137.578, 205.474, 128.6, 204, 128.6, 204]}, + {t: 'C', p: [128.373, 205.365, 128.318, 206.961, 128.424, 208.599]}, + {t: 'C', p: [128.424, 208.599, 133.292, 214.118, 138.991, 211.603]}, + {t: 'z', p: []}]}, + +{f: '#659900', s: null, p: [{t: 'M', p: [138.991, 211.403]}, + {t: 'C', p: [138.542, 211.561, 138.976, 208.669, 138.8, 208.2]}, + {t: 'C', p: [137.778, 205.474, 128.6, 203.9, 128.6, 203.9]}, + {t: 'C', p: [128.373, 205.265, 128.318, 206.861, 128.424, 208.499]}, + {t: 'C', p: [128.424, 208.499, 132.692, 213.618, 138.991, 211.403]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [134.6, 211.546]}, + {t: 'C', p: [133.975, 211.546, 133.469, 210.406, 133.469, 209]}, + {t: 'C', p: [133.469, 207.595, 133.975, 206.455, 134.6, 206.455]}, + {t: 'C', p: [135.225, 206.455, 135.732, 207.595, 135.732, 209]}, + {t: 'C', p: [135.732, 210.406, 135.225, 211.546, 134.6, 211.546]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [134.6, 209]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [89, 309.601]}, + {t: 'C', p: [89, 309.601, 83.4, 319.601, 108.2, 313.601]}, + {t: 'C', p: [108.2, 313.601, 122.2, 312.401, 124.6, 310.001]}, + {t: 'C', p: [125.8, 310.801, 134.166, 313.734, 137, 314.401]}, + {t: 'C', p: [143.8, 316.001, 152.2, 306, 152.2, 306]}, + {t: 'C', p: [152.2, 306, 156.8, 295.5, 159.6, 295.5]}, + {t: 'C', p: [162.4, 295.5, 159.2, 297.1, 159.2, 297.1]}, + {t: 'C', p: [159.2, 297.1, 152.6, 307.201, 153, 308.801]}, + {t: 'C', p: [153, 308.801, 147.8, 328.801, 131.8, 329.601]}, + {t: 'C', p: [131.8, 329.601, 115.65, 330.551, 117, 336.401]}, + {t: 'C', p: [117, 336.401, 125.8, 334.001, 128.2, 336.401]}, + {t: 'C', p: [128.2, 336.401, 139, 336.001, 131, 342.401]}, + {t: 'L', p: [124.2, 354.001]}, + {t: 'C', p: [124.2, 354.001, 124.34, 357.919, 114.2, 354.401]}, + {t: 'C', p: [104.4, 351.001, 94.1, 338.101, 94.1, 338.101]}, + {t: 'C', p: [94.1, 338.101, 78.15, 323.551, 89, 309.601]}, + {t: 'z', p: []}]}, + +{f: '#e59999', s: null, p: [{t: 'M', p: [87.8, 313.601]}, + {t: 'C', p: [87.8, 313.601, 85.8, 323.201, 122.6, 312.801]}, + {t: 'C', p: [122.6, 312.801, 127, 312.801, 129.4, 313.601]}, + {t: 'C', p: [131.8, 314.401, 143.8, 317.201, 145.8, 316.001]}, + {t: 'C', p: [145.8, 316.001, 138.6, 329.601, 127, 328.001]}, + {t: 'C', p: [127, 328.001, 113.8, 329.601, 114.2, 334.401]}, + {t: 'C', p: [114.2, 334.401, 118.2, 341.601, 123, 344.001]}, + {t: 'C', p: [123, 344.001, 125.8, 346.401, 125.4, 349.601]}, + {t: 'C', p: [125, 352.801, 122.2, 354.401, 120.2, 355.201]}, + {t: 'C', p: [118.2, 356.001, 115, 352.801, 113.4, 352.801]}, + {t: 'C', p: [111.8, 352.801, 103.4, 346.401, 99, 341.601]}, + {t: 'C', p: [94.6, 336.801, 86.2, 324.801, 86.6, 322.001]}, + {t: 'C', p: [87, 319.201, 87.8, 313.601, 87.8, 313.601]}, + {t: 'z', p: []}]}, + +{f: '#b26565', s: null, p: [{t: 'M', p: [91, 331.051]}, + {t: 'C', p: [93.6, 335.001, 96.8, 339.201, 99, 341.601]}, + {t: 'C', p: [103.4, 346.401, 111.8, 352.801, 113.4, 352.801]}, + {t: 'C', p: [115, 352.801, 118.2, 356.001, 120.2, 355.201]}, + {t: 'C', p: [122.2, 354.401, 125, 352.801, 125.4, 349.601]}, + {t: 'C', p: [125.8, 346.401, 123, 344.001, 123, 344.001]}, + {t: 'C', p: [119.934, 342.468, 117.194, 338.976, 115.615, 336.653]}, + {t: 'C', p: [115.615, 336.653, 115.8, 339.201, 110.6, 338.401]}, + {t: 'C', p: [105.4, 337.601, 100.2, 334.801, 98.6, 331.601]}, + {t: 'C', p: [97, 328.401, 94.6, 326.001, 96.2, 329.601]}, + {t: 'C', p: [97.8, 333.201, 100.2, 336.801, 101.8, 337.201]}, + {t: 'C', p: [103.4, 337.601, 103, 338.801, 100.6, 338.401]}, + {t: 'C', p: [98.2, 338.001, 95.4, 337.601, 91, 332.401]}, + {t: 'z', p: []}]}, + +{f: '#992600', s: null, p: [{t: 'M', p: [88.4, 310.001]}, + {t: 'C', p: [88.4, 310.001, 90.2, 296.4, 91.4, 292.4]}, + {t: 'C', p: [91.4, 292.4, 90.6, 285.6, 93, 281.4]}, + {t: 'C', p: [95.4, 277.2, 97.4, 271, 100.4, 265.6]}, + {t: 'C', p: [103.4, 260.2, 103.6, 256.2, 107.6, 254.6]}, + {t: 'C', p: [111.6, 253, 117.6, 244.4, 120.4, 243.4]}, + {t: 'C', p: [123.2, 242.4, 123, 243.2, 123, 243.2]}, + {t: 'C', p: [123, 243.2, 129.8, 228.4, 143.4, 232.4]}, + {t: 'C', p: [143.4, 232.4, 127.2, 229.6, 143, 220.2]}, + {t: 'C', p: [143, 220.2, 138.2, 221.3, 141.5, 214.3]}, + {t: 'C', p: [143.701, 209.632, 143.2, 216.4, 132.2, 228.2]}, + {t: 'C', p: [132.2, 228.2, 127.2, 236.8, 122, 239.8]}, + {t: 'C', p: [116.8, 242.8, 104.8, 249.8, 103.6, 253.6]}, + {t: 'C', p: [102.4, 257.4, 99.2, 263.2, 97.2, 264.8]}, + {t: 'C', p: [95.2, 266.4, 92.4, 270.6, 92, 274]}, + {t: 'C', p: [92, 274, 90.8, 278, 89.4, 279.2]}, + {t: 'C', p: [88, 280.4, 87.8, 283.6, 87.8, 285.6]}, + {t: 'C', p: [87.8, 287.6, 85.8, 290.4, 86, 292.8]}, + {t: 'C', p: [86, 292.8, 86.8, 311.801, 86.4, 313.801]}, + {t: 'L', p: [88.4, 310.001]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [79.8, 314.601]}, + {t: 'C', p: [79.8, 314.601, 77.8, 313.201, 73.4, 319.201]}, + {t: 'C', p: [73.4, 319.201, 80.7, 352.201, 80.7, 353.601]}, + {t: 'C', p: [80.7, 353.601, 81.8, 351.501, 80.5, 344.301]}, + {t: 'C', p: [79.2, 337.101, 78.3, 324.401, 78.3, 324.401]}, + {t: 'L', p: [79.8, 314.601]}, + {t: 'z', p: []}]}, + +{f: '#992600', s: null, p: [{t: 'M', p: [101.4, 254]}, + {t: 'C', p: [101.4, 254, 83.8, 257.2, 84.2, 286.4]}, + {t: 'L', p: [83.4, 311.201]}, + {t: 'C', p: [83.4, 311.201, 82.2, 285.6, 81, 284]}, + {t: 'C', p: [79.8, 282.4, 83.8, 271.2, 80.6, 277.2]}, + {t: 'C', p: [80.6, 277.2, 66.6, 291.2, 74.6, 312.401]}, + {t: 'C', p: [74.6, 312.401, 76.1, 315.701, 73.1, 311.101]}, + {t: 'C', p: [73.1, 311.101, 68.5, 298.5, 69.6, 292.1]}, + {t: 'C', p: [69.6, 292.1, 69.8, 289.9, 71.7, 287.1]}, + {t: 'C', p: [71.7, 287.1, 80.3, 275.4, 83, 273.1]}, + {t: 'C', p: [83, 273.1, 84.8, 258.7, 100.2, 253.5]}, + {t: 'C', p: [100.2, 253.5, 105.9, 251.2, 101.4, 254]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [240.8, 187.8]}, + {t: 'C', p: [241.46, 187.446, 241.451, 186.476, 242.031, 186.303]}, + {t: 'C', p: [243.18, 185.959, 243.344, 184.892, 243.862, 184.108]}, + {t: 'C', p: [244.735, 182.789, 244.928, 181.256, 245.51, 179.765]}, + {t: 'C', p: [245.782, 179.065, 245.809, 178.11, 245.496, 177.45]}, + {t: 'C', p: [244.322, 174.969, 243.62, 172.52, 242.178, 170.094]}, + {t: 'C', p: [241.91, 169.644, 241.648, 168.85, 241.447, 168.252]}, + {t: 'C', p: [240.984, 166.868, 239.727, 165.877, 238.867, 164.557]}, + {t: 'C', p: [238.579, 164.116, 239.104, 163.191, 238.388, 163.107]}, + {t: 'C', p: [237.491, 163.002, 236.042, 162.422, 235.809, 163.448]}, + {t: 'C', p: [235.221, 166.035, 236.232, 168.558, 237.2, 171]}, + {t: 'C', p: [236.418, 171.692, 236.752, 172.613, 236.904, 173.38]}, + {t: 'C', p: [237.614, 176.986, 236.416, 180.338, 235.655, 183.812]}, + {t: 'C', p: [235.632, 183.916, 235.974, 184.114, 235.946, 184.176]}, + {t: 'C', p: [234.724, 186.862, 233.272, 189.307, 231.453, 191.688]}, + {t: 'C', p: [230.695, 192.68, 229.823, 193.596, 229.326, 194.659]}, + {t: 'C', p: [228.958, 195.446, 228.55, 196.412, 228.8, 197.4]}, + {t: 'C', p: [225.365, 200.18, 223.115, 204.025, 220.504, 207.871]}, + {t: 'C', p: [220.042, 208.551, 220.333, 209.76, 220.884, 210.029]}, + {t: 'C', p: [221.697, 210.427, 222.653, 209.403, 223.123, 208.557]}, + {t: 'C', p: [223.512, 207.859, 223.865, 207.209, 224.356, 206.566]}, + {t: 'C', p: [224.489, 206.391, 224.31, 205.972, 224.445, 205.851]}, + {t: 'C', p: [227.078, 203.504, 228.747, 200.568, 231.2, 198.2]}, + {t: 'C', p: [233.15, 197.871, 234.687, 196.873, 236.435, 195.86]}, + {t: 'C', p: [236.743, 195.681, 237.267, 195.93, 237.557, 195.735]}, + {t: 'C', p: [239.31, 194.558, 239.308, 192.522, 239.414, 190.612]}, + {t: 'C', p: [239.464, 189.728, 239.66, 188.411, 240.8, 187.8]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [231.959, 183.334]}, + {t: 'C', p: [232.083, 183.257, 231.928, 182.834, 232.037, 182.618]}, + {t: 'C', p: [232.199, 182.294, 232.602, 182.106, 232.764, 181.782]}, + {t: 'C', p: [232.873, 181.566, 232.71, 181.186, 232.846, 181.044]}, + {t: 'C', p: [235.179, 178.597, 235.436, 175.573, 234.4, 172.6]}, + {t: 'C', p: [235.424, 171.98, 235.485, 170.718, 235.06, 169.871]}, + {t: 'C', p: [234.207, 168.171, 234.014, 166.245, 233.039, 164.702]}, + {t: 'C', p: [232.237, 163.433, 230.659, 162.189, 229.288, 163.492]}, + {t: 'C', p: [228.867, 163.892, 228.546, 164.679, 228.824, 165.391]}, + {t: 'C', p: [228.888, 165.554, 229.173, 165.7, 229.146, 165.782]}, + {t: 'C', p: [229.039, 166.106, 228.493, 166.33, 228.487, 166.602]}, + {t: 'C', p: [228.457, 168.098, 227.503, 169.609, 228.133, 170.938]}, + {t: 'C', p: [228.905, 172.567, 229.724, 174.424, 230.4, 176.2]}, + {t: 'C', p: [229.166, 178.316, 230.199, 180.765, 228.446, 182.642]}, + {t: 'C', p: [228.31, 182.788, 228.319, 183.174, 228.441, 183.376]}, + {t: 'C', p: [228.733, 183.862, 229.139, 184.268, 229.625, 184.56]}, + {t: 'C', p: [229.827, 184.681, 230.175, 184.683, 230.375, 184.559]}, + {t: 'C', p: [230.953, 184.197, 231.351, 183.71, 231.959, 183.334]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [294.771, 173.023]}, + {t: 'C', p: [296.16, 174.815, 296.45, 177.61, 294.401, 179]}, + {t: 'C', p: [294.951, 182.309, 298.302, 180.33, 300.401, 179.8]}, + {t: 'C', p: [300.292, 179.412, 300.519, 179.068, 300.802, 179.063]}, + {t: 'C', p: [301.859, 179.048, 302.539, 178.016, 303.601, 178.2]}, + {t: 'C', p: [304.035, 176.643, 305.673, 175.941, 306.317, 174.561]}, + {t: 'C', p: [308.043, 170.866, 307.452, 166.593, 304.868, 163.347]}, + {t: 'C', p: [304.666, 163.093, 304.883, 162.576, 304.759, 162.214]}, + {t: 'C', p: [304.003, 160.003, 301.935, 159.688, 300.001, 159]}, + {t: 'C', p: [298.824, 155.125, 298.163, 151.094, 296.401, 147.4]}, + {t: 'C', p: [294.787, 147.15, 294.089, 145.411, 292.752, 144.691]}, + {t: 'C', p: [291.419, 143.972, 290.851, 145.551, 290.892, 146.597]}, + {t: 'C', p: [290.899, 146.802, 291.351, 147.026, 291.181, 147.391]}, + {t: 'C', p: [291.105, 147.555, 290.845, 147.666, 290.845, 147.8]}, + {t: 'C', p: [290.846, 147.935, 291.067, 148.066, 291.201, 148.2]}, + {t: 'C', p: [290.283, 149.02, 288.86, 149.497, 288.565, 150.642]}, + {t: 'C', p: [287.611, 154.352, 290.184, 157.477, 291.852, 160.678]}, + {t: 'C', p: [292.443, 161.813, 291.707, 163.084, 290.947, 164.292]}, + {t: 'C', p: [290.509, 164.987, 290.617, 166.114, 290.893, 166.97]}, + {t: 'C', p: [291.645, 169.301, 293.236, 171.04, 294.771, 173.023]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [257.611, 191.409]}, + {t: 'C', p: [256.124, 193.26, 252.712, 195.829, 255.629, 197.757]}, + {t: 'C', p: [255.823, 197.886, 256.193, 197.89, 256.366, 197.756]}, + {t: 'C', p: [258.387, 196.191, 260.39, 195.288, 262.826, 194.706]}, + {t: 'C', p: [262.95, 194.677, 263.224, 195.144, 263.593, 194.983]}, + {t: 'C', p: [265.206, 194.28, 267.216, 194.338, 268.4, 193]}, + {t: 'C', p: [272.167, 193.224, 275.732, 192.108, 279.123, 190.8]}, + {t: 'C', p: [280.284, 190.352, 281.554, 189.793, 282.755, 189.291]}, + {t: 'C', p: [284.131, 188.715, 285.335, 187.787, 286.447, 186.646]}, + {t: 'C', p: [286.58, 186.51, 286.934, 186.6, 287.201, 186.6]}, + {t: 'C', p: [287.161, 185.737, 288.123, 185.61, 288.37, 184.988]}, + {t: 'C', p: [288.462, 184.756, 288.312, 184.36, 288.445, 184.258]}, + {t: 'C', p: [290.583, 182.628, 291.503, 180.61, 290.334, 178.233]}, + {t: 'C', p: [290.049, 177.655, 289.8, 177.037, 289.234, 176.561]}, + {t: 'C', p: [288.149, 175.65, 287.047, 176.504, 286, 176.2]}, + {t: 'C', p: [285.841, 176.828, 285.112, 176.656, 284.726, 176.854]}, + {t: 'C', p: [283.867, 177.293, 282.534, 176.708, 281.675, 177.146]}, + {t: 'C', p: [280.313, 177.841, 279.072, 178.01, 277.65, 178.387]}, + {t: 'C', p: [277.338, 178.469, 276.56, 178.373, 276.4, 179]}, + {t: 'C', p: [276.266, 178.866, 276.118, 178.632, 276.012, 178.654]}, + {t: 'C', p: [274.104, 179.05, 272.844, 179.264, 271.543, 180.956]}, + {t: 'C', p: [271.44, 181.089, 270.998, 180.91, 270.839, 181.045]}, + {t: 'C', p: [269.882, 181.853, 269.477, 183.087, 268.376, 183.759]}, + {t: 'C', p: [268.175, 183.882, 267.823, 183.714, 267.629, 183.843]}, + {t: 'C', p: [266.983, 184.274, 266.616, 184.915, 265.974, 185.362]}, + {t: 'C', p: [265.645, 185.591, 265.245, 185.266, 265.277, 185.01]}, + {t: 'C', p: [265.522, 183.063, 266.175, 181.276, 265.6, 179.4]}, + {t: 'C', p: [267.677, 176.88, 270.194, 174.931, 272, 172.2]}, + {t: 'C', p: [272.015, 170.034, 272.707, 167.888, 272.594, 165.811]}, + {t: 'C', p: [272.584, 165.618, 272.296, 164.885, 272.17, 164.538]}, + {t: 'C', p: [271.858, 163.684, 272.764, 162.618, 271.92, 161.894]}, + {t: 'C', p: [270.516, 160.691, 269.224, 161.567, 268.4, 163]}, + {t: 'C', p: [266.562, 163.39, 264.496, 164.083, 262.918, 162.849]}, + {t: 'C', p: [261.911, 162.062, 261.333, 161.156, 260.534, 160.1]}, + {t: 'C', p: [259.549, 158.798, 259.884, 157.362, 259.954, 155.798]}, + {t: 'C', p: [259.96, 155.67, 259.645, 155.534, 259.645, 155.4]}, + {t: 'C', p: [259.646, 155.265, 259.866, 155.134, 260, 155]}, + {t: 'C', p: [259.294, 154.374, 259.019, 153.316, 258, 153]}, + {t: 'C', p: [258.305, 151.908, 257.629, 151.024, 256.758, 150.722]}, + {t: 'C', p: [254.763, 150.031, 253.086, 151.943, 251.194, 152.016]}, + {t: 'C', p: [250.68, 152.035, 250.213, 150.997, 249.564, 150.672]}, + {t: 'C', p: [249.132, 150.456, 248.428, 150.423, 248.066, 150.689]}, + {t: 'C', p: [247.378, 151.193, 246.789, 151.307, 246.031, 151.512]}, + {t: 'C', p: [244.414, 151.948, 243.136, 153.042, 241.656, 153.897]}, + {t: 'C', p: [240.171, 154.754, 239.216, 156.191, 238.136, 157.511]}, + {t: 'C', p: [237.195, 158.663, 237.059, 161.077, 238.479, 161.577]}, + {t: 'C', p: [240.322, 162.227, 241.626, 159.524, 243.592, 159.85]}, + {t: 'C', p: [243.904, 159.901, 244.11, 160.212, 244, 160.6]}, + {t: 'C', p: [244.389, 160.709, 244.607, 160.48, 244.8, 160.2]}, + {t: 'C', p: [245.658, 161.219, 246.822, 161.556, 247.76, 162.429]}, + {t: 'C', p: [248.73, 163.333, 250.476, 162.915, 251.491, 163.912]}, + {t: 'C', p: [253.02, 165.414, 252.461, 168.095, 254.4, 169.4]}, + {t: 'C', p: [253.814, 170.713, 253.207, 171.99, 252.872, 173.417]}, + {t: 'C', p: [252.59, 174.623, 253.584, 175.82, 254.795, 175.729]}, + {t: 'C', p: [256.053, 175.635, 256.315, 174.876, 256.8, 173.8]}, + {t: 'C', p: [257.067, 174.067, 257.536, 174.364, 257.495, 174.58]}, + {t: 'C', p: [257.038, 176.967, 256.011, 178.96, 255.553, 181.391]}, + {t: 'C', p: [255.494, 181.708, 255.189, 181.91, 254.8, 181.8]}, + {t: 'C', p: [254.332, 185.949, 250.28, 188.343, 247.735, 191.508]}, + {t: 'C', p: [247.332, 192.01, 247.328, 193.259, 247.737, 193.662]}, + {t: 'C', p: [249.14, 195.049, 251.1, 193.503, 252.8, 193]}, + {t: 'C', p: [253.013, 191.794, 253.872, 190.852, 255.204, 190.908]}, + {t: 'C', p: [255.46, 190.918, 255.695, 190.376, 256.019, 190.246]}, + {t: 'C', p: [256.367, 190.108, 256.869, 190.332, 257.155, 190.134]}, + {t: 'C', p: [258.884, 188.939, 260.292, 187.833, 262.03, 186.644]}, + {t: 'C', p: [262.222, 186.513, 262.566, 186.672, 262.782, 186.564]}, + {t: 'C', p: [263.107, 186.402, 263.294, 186.015, 263.617, 185.83]}, + {t: 'C', p: [263.965, 185.63, 264.207, 185.92, 264.4, 186.2]}, + {t: 'C', p: [263.754, 186.549, 263.75, 187.506, 263.168, 187.708]}, + {t: 'C', p: [262.393, 187.976, 261.832, 188.489, 261.158, 188.936]}, + {t: 'C', p: [260.866, 189.129, 260.207, 188.881, 260.103, 189.06]}, + {t: 'C', p: [259.505, 190.088, 258.321, 190.526, 257.611, 191.409]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [202.2, 142]}, + {t: 'C', p: [202.2, 142, 192.962, 139.128, 181.8, 164.8]}, + {t: 'C', p: [181.8, 164.8, 179.4, 170, 177, 172]}, + {t: 'C', p: [174.6, 174, 163.4, 177.6, 161.4, 181.6]}, + {t: 'L', p: [151, 197.6]}, + {t: 'C', p: [151, 197.6, 165.8, 181.6, 169, 179.2]}, + {t: 'C', p: [169, 179.2, 177, 170.8, 173.8, 177.6]}, + {t: 'C', p: [173.8, 177.6, 159.8, 188.4, 161, 197.6]}, + {t: 'C', p: [161, 197.6, 155.4, 212, 154.6, 214]}, + {t: 'C', p: [154.6, 214, 170.6, 182, 173, 180.8]}, + {t: 'C', p: [175.4, 179.6, 176.6, 179.6, 175.4, 183.2]}, + {t: 'C', p: [174.2, 186.8, 173.8, 203.2, 171, 205.2]}, + {t: 'C', p: [171, 205.2, 179, 184.8, 178.2, 181.6]}, + {t: 'C', p: [178.2, 181.6, 181.4, 178, 183.8, 183.2]}, + {t: 'L', p: [182.6, 199.2]}, + {t: 'L', p: [187, 211.2]}, + {t: 'C', p: [187, 211.2, 184.6, 200, 186.2, 184.4]}, + {t: 'C', p: [186.2, 184.4, 184.2, 174, 188.2, 179.6]}, + {t: 'C', p: [192.2, 185.2, 201.8, 191.2, 201.8, 196]}, + {t: 'C', p: [201.8, 196, 196.6, 178.4, 187.4, 173.6]}, + {t: 'L', p: [183.4, 179.6]}, + {t: 'L', p: [182.2, 177.6]}, + {t: 'C', p: [182.2, 177.6, 178.6, 176.8, 183, 170]}, + {t: 'C', p: [187.4, 163.2, 187, 162.4, 187, 162.4]}, + {t: 'C', p: [187, 162.4, 193.4, 169.6, 195, 169.6]}, + {t: 'C', p: [195, 169.6, 208.2, 162, 209.4, 186.4]}, + {t: 'C', p: [209.4, 186.4, 216.2, 172, 207, 165.2]}, + {t: 'C', p: [207, 165.2, 192.2, 163.2, 193.4, 158]}, + {t: 'L', p: [200.6, 145.6]}, + {t: 'C', p: [204.2, 140.4, 202.6, 143.2, 202.6, 143.2]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [182.2, 158.4]}, + {t: 'C', p: [182.2, 158.4, 169.4, 158.4, 166.2, 163.6]}, + {t: 'L', p: [159, 173.2]}, + {t: 'C', p: [159, 173.2, 176.2, 163.2, 180.2, 162]}, + {t: 'C', p: [184.2, 160.8, 182.2, 158.4, 182.2, 158.4]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [142.2, 164.8]}, + {t: 'C', p: [142.2, 164.8, 140.2, 166, 139.8, 168.8]}, + {t: 'C', p: [139.4, 171.6, 137, 172, 137.8, 174.8]}, + {t: 'C', p: [138.6, 177.6, 140.6, 180, 140.6, 176]}, + {t: 'C', p: [140.6, 172, 142.2, 170, 143, 168.8]}, + {t: 'C', p: [143.8, 167.6, 145.4, 163.2, 142.2, 164.8]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [133.4, 226]}, + {t: 'C', p: [133.4, 226, 125, 222, 121.8, 218.4]}, + {t: 'C', p: [118.6, 214.8, 119.052, 219.966, 114.2, 219.6]}, + {t: 'C', p: [108.353, 219.159, 109.4, 203.2, 109.4, 203.2]}, + {t: 'L', p: [105.4, 210.8]}, + {t: 'C', p: [105.4, 210.8, 104.2, 225.2, 112.2, 222.8]}, + {t: 'C', p: [116.107, 221.628, 117.4, 223.2, 115.8, 224]}, + {t: 'C', p: [114.2, 224.8, 121.4, 225.2, 118.6, 226.8]}, + {t: 'C', p: [115.8, 228.4, 130.2, 223.2, 127.8, 233.6]}, + {t: 'L', p: [133.4, 226]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [120.8, 240.4]}, + {t: 'C', p: [120.8, 240.4, 105.4, 244.8, 101.8, 235.2]}, + {t: 'C', p: [101.8, 235.2, 97, 237.6, 99.2, 240.6]}, + {t: 'C', p: [101.4, 243.6, 102.6, 244, 102.6, 244]}, + {t: 'C', p: [102.6, 244, 108, 245.2, 107.4, 246]}, + {t: 'C', p: [106.8, 246.8, 104.4, 250.2, 104.4, 250.2]}, + {t: 'C', p: [104.4, 250.2, 114.6, 244.2, 120.8, 240.4]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [349.201, 318.601]}, + {t: 'C', p: [348.774, 320.735, 347.103, 321.536, 345.201, 322.201]}, + {t: 'C', p: [343.284, 321.243, 340.686, 318.137, 338.801, 320.201]}, + {t: 'C', p: [338.327, 319.721, 337.548, 319.661, 337.204, 318.999]}, + {t: 'C', p: [336.739, 318.101, 337.011, 317.055, 336.669, 316.257]}, + {t: 'C', p: [336.124, 314.985, 335.415, 313.619, 335.601, 312.201]}, + {t: 'C', p: [337.407, 311.489, 338.002, 309.583, 337.528, 307.82]}, + {t: 'C', p: [337.459, 307.563, 337.03, 307.366, 337.23, 307.017]}, + {t: 'C', p: [337.416, 306.694, 337.734, 306.467, 338.001, 306.2]}, + {t: 'C', p: [337.866, 306.335, 337.721, 306.568, 337.61, 306.548]}, + {t: 'C', p: [337, 306.442, 337.124, 305.805, 337.254, 305.418]}, + {t: 'C', p: [337.839, 303.672, 339.853, 303.408, 341.201, 304.6]}, + {t: 'C', p: [341.457, 304.035, 341.966, 304.229, 342.401, 304.2]}, + {t: 'C', p: [342.351, 303.621, 342.759, 303.094, 342.957, 302.674]}, + {t: 'C', p: [343.475, 301.576, 345.104, 302.682, 345.901, 302.07]}, + {t: 'C', p: [346.977, 301.245, 348.04, 300.546, 349.118, 301.149]}, + {t: 'C', p: [350.927, 302.162, 352.636, 303.374, 353.835, 305.115]}, + {t: 'C', p: [354.41, 305.949, 354.65, 307.23, 354.592, 308.188]}, + {t: 'C', p: [354.554, 308.835, 353.173, 308.483, 352.83, 309.412]}, + {t: 'C', p: [352.185, 311.16, 354.016, 311.679, 354.772, 313.017]}, + {t: 'C', p: [354.97, 313.366, 354.706, 313.67, 354.391, 313.768]}, + {t: 'C', p: [353.98, 313.896, 353.196, 313.707, 353.334, 314.16]}, + {t: 'C', p: [354.306, 317.353, 351.55, 318.031, 349.201, 318.601]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: null, p: [{t: 'M', p: [339.6, 338.201]}, + {t: 'C', p: [339.593, 336.463, 337.992, 334.707, 339.201, 333.001]}, + {t: 'C', p: [339.336, 333.135, 339.467, 333.356, 339.601, 333.356]}, + {t: 'C', p: [339.736, 333.356, 339.867, 333.135, 340.001, 333.001]}, + {t: 'C', p: [341.496, 335.217, 345.148, 336.145, 345.006, 338.991]}, + {t: 'C', p: [344.984, 339.438, 343.897, 340.356, 344.801, 341.001]}, + {t: 'C', p: [342.988, 342.349, 342.933, 344.719, 342.001, 346.601]}, + {t: 'C', p: [340.763, 346.315, 339.551, 345.952, 338.401, 345.401]}, + {t: 'C', p: [338.753, 343.915, 338.636, 342.231, 339.456, 340.911]}, + {t: 'C', p: [339.89, 340.213, 339.603, 339.134, 339.6, 338.201]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [173.4, 329.201]}, + {t: 'C', p: [173.4, 329.201, 156.542, 339.337, 170.6, 324.001]}, + {t: 'C', p: [179.4, 314.401, 189.4, 308.801, 189.4, 308.801]}, + {t: 'C', p: [189.4, 308.801, 199.8, 304.4, 203.4, 303.2]}, + {t: 'C', p: [207, 302, 222.2, 296.8, 225.4, 296.4]}, + {t: 'C', p: [228.6, 296, 238.2, 292, 245, 296]}, + {t: 'C', p: [251.8, 300, 259.8, 304.4, 259.8, 304.4]}, + {t: 'C', p: [259.8, 304.4, 243.4, 296, 239.8, 298.4]}, + {t: 'C', p: [236.2, 300.8, 229, 300.4, 223, 303.6]}, + {t: 'C', p: [223, 303.6, 208.2, 308.001, 205, 310.001]}, + {t: 'C', p: [201.8, 312.001, 191.4, 323.601, 189.8, 322.801]}, + {t: 'C', p: [188.2, 322.001, 190.2, 321.601, 191.4, 318.801]}, + {t: 'C', p: [192.6, 316.001, 190.6, 314.401, 182.6, 320.801]}, + {t: 'C', p: [174.6, 327.201, 173.4, 329.201, 173.4, 329.201]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [180.805, 323.234]}, + {t: 'C', p: [180.805, 323.234, 182.215, 310.194, 190.693, 311.859]}, + {t: 'C', p: [190.693, 311.859, 198.919, 307.689, 201.641, 305.721]}, + {t: 'C', p: [201.641, 305.721, 209.78, 304.019, 211.09, 303.402]}, + {t: 'C', p: [229.569, 294.702, 244.288, 299.221, 244.835, 298.101]}, + {t: 'C', p: [245.381, 296.982, 265.006, 304.099, 268.615, 308.185]}, + {t: 'C', p: [269.006, 308.628, 258.384, 302.588, 248.686, 300.697]}, + {t: 'C', p: [240.413, 299.083, 218.811, 300.944, 207.905, 306.48]}, + {t: 'C', p: [204.932, 307.989, 195.987, 313.773, 193.456, 313.662]}, + {t: 'C', p: [190.925, 313.55, 180.805, 323.234, 180.805, 323.234]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [177, 348.801]}, + {t: 'C', p: [177, 348.801, 161.8, 346.401, 178.6, 344.801]}, + {t: 'C', p: [178.6, 344.801, 196.6, 342.801, 200.6, 337.601]}, + {t: 'C', p: [200.6, 337.601, 214.2, 328.401, 217, 328.001]}, + {t: 'C', p: [219.8, 327.601, 249.8, 320.401, 250.2, 318.001]}, + {t: 'C', p: [250.6, 315.601, 256.2, 315.601, 257.8, 316.401]}, + {t: 'C', p: [259.4, 317.201, 258.6, 318.401, 255.8, 319.201]}, + {t: 'C', p: [253, 320.001, 221.8, 336.401, 215.4, 337.601]}, + {t: 'C', p: [209, 338.801, 197.4, 346.401, 192.6, 347.601]}, + {t: 'C', p: [187.8, 348.801, 177, 348.801, 177, 348.801]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [196.52, 341.403]}, + {t: 'C', p: [196.52, 341.403, 187.938, 340.574, 196.539, 339.755]}, + {t: 'C', p: [196.539, 339.755, 205.355, 336.331, 207.403, 333.668]}, + {t: 'C', p: [207.403, 333.668, 214.367, 328.957, 215.8, 328.753]}, + {t: 'C', p: [217.234, 328.548, 231.194, 324.861, 231.399, 323.633]}, + {t: 'C', p: [231.604, 322.404, 265.67, 309.823, 270.09, 313.013]}, + {t: 'C', p: [273.001, 315.114, 263.1, 313.437, 253.466, 317.847]}, + {t: 'C', p: [252.111, 318.467, 218.258, 333.054, 214.981, 333.668]}, + {t: 'C', p: [211.704, 334.283, 205.765, 338.174, 203.307, 338.788]}, + {t: 'C', p: [200.85, 339.403, 196.52, 341.403, 196.52, 341.403]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [188.6, 343.601]}, + {t: 'C', p: [188.6, 343.601, 193.8, 343.201, 192.6, 344.801]}, + {t: 'C', p: [191.4, 346.401, 189, 345.601, 189, 345.601]}, + {t: 'L', p: [188.6, 343.601]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [181.4, 345.201]}, + {t: 'C', p: [181.4, 345.201, 186.6, 344.801, 185.4, 346.401]}, + {t: 'C', p: [184.2, 348.001, 181.8, 347.201, 181.8, 347.201]}, + {t: 'L', p: [181.4, 345.201]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [171, 346.801]}, + {t: 'C', p: [171, 346.801, 176.2, 346.401, 175, 348.001]}, + {t: 'C', p: [173.8, 349.601, 171.4, 348.801, 171.4, 348.801]}, + {t: 'L', p: [171, 346.801]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [163.4, 347.601]}, + {t: 'C', p: [163.4, 347.601, 168.6, 347.201, 167.4, 348.801]}, + {t: 'C', p: [166.2, 350.401, 163.8, 349.601, 163.8, 349.601]}, + {t: 'L', p: [163.4, 347.601]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [201.8, 308.001]}, + {t: 'C', p: [201.8, 308.001, 206.2, 308.001, 205, 309.601]}, + {t: 'C', p: [203.8, 311.201, 200.6, 310.801, 200.6, 310.801]}, + {t: 'L', p: [201.8, 308.001]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [191.8, 313.601]}, + {t: 'C', p: [191.8, 313.601, 198.306, 311.46, 195.8, 314.801]}, + {t: 'C', p: [194.6, 316.401, 192.2, 315.601, 192.2, 315.601]}, + {t: 'L', p: [191.8, 313.601]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [180.6, 318.401]}, + {t: 'C', p: [180.6, 318.401, 185.8, 318.001, 184.6, 319.601]}, + {t: 'C', p: [183.4, 321.201, 181, 320.401, 181, 320.401]}, + {t: 'L', p: [180.6, 318.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [173, 324.401]}, + {t: 'C', p: [173, 324.401, 178.2, 324.001, 177, 325.601]}, + {t: 'C', p: [175.8, 327.201, 173.4, 326.401, 173.4, 326.401]}, + {t: 'L', p: [173, 324.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [166.2, 329.201]}, + {t: 'C', p: [166.2, 329.201, 171.4, 328.801, 170.2, 330.401]}, + {t: 'C', p: [169, 332.001, 166.6, 331.201, 166.6, 331.201]}, + {t: 'L', p: [166.2, 329.201]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [205.282, 335.598]}, + {t: 'C', p: [205.282, 335.598, 212.203, 335.066, 210.606, 337.195]}, + {t: 'C', p: [209.009, 339.325, 205.814, 338.26, 205.814, 338.26]}, + {t: 'L', p: [205.282, 335.598]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [215.682, 330.798]}, + {t: 'C', p: [215.682, 330.798, 222.603, 330.266, 221.006, 332.395]}, + {t: 'C', p: [219.409, 334.525, 216.214, 333.46, 216.214, 333.46]}, + {t: 'L', p: [215.682, 330.798]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [226.482, 326.398]}, + {t: 'C', p: [226.482, 326.398, 233.403, 325.866, 231.806, 327.995]}, + {t: 'C', p: [230.209, 330.125, 227.014, 329.06, 227.014, 329.06]}, + {t: 'L', p: [226.482, 326.398]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [236.882, 321.598]}, + {t: 'C', p: [236.882, 321.598, 243.803, 321.066, 242.206, 323.195]}, + {t: 'C', p: [240.609, 325.325, 237.414, 324.26, 237.414, 324.26]}, + {t: 'L', p: [236.882, 321.598]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [209.282, 303.598]}, + {t: 'C', p: [209.282, 303.598, 216.203, 303.066, 214.606, 305.195]}, + {t: 'C', p: [213.009, 307.325, 209.014, 307.06, 209.014, 307.06]}, + {t: 'L', p: [209.282, 303.598]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [219.282, 300.398]}, + {t: 'C', p: [219.282, 300.398, 226.203, 299.866, 224.606, 301.995]}, + {t: 'C', p: [223.009, 304.125, 218.614, 303.86, 218.614, 303.86]}, + {t: 'L', p: [219.282, 300.398]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [196.6, 340.401]}, + {t: 'C', p: [196.6, 340.401, 201.8, 340.001, 200.6, 341.601]}, + {t: 'C', p: [199.4, 343.201, 197, 342.401, 197, 342.401]}, + {t: 'L', p: [196.6, 340.401]}, + {t: 'z', p: []}]}, + +{f: '#992600', s: null, p: [{t: 'M', p: [123.4, 241.2]}, + {t: 'C', p: [123.4, 241.2, 119, 250, 118.6, 253.2]}, + {t: 'C', p: [118.6, 253.2, 119.4, 244.4, 120.6, 242.4]}, + {t: 'C', p: [121.8, 240.4, 123.4, 241.2, 123.4, 241.2]}, + {t: 'z', p: []}]}, + +{f: '#992600', s: null, p: [{t: 'M', p: [105, 255.2]}, + {t: 'C', p: [105, 255.2, 101.8, 269.6, 102.2, 272.4]}, + {t: 'C', p: [102.2, 272.4, 101, 260.8, 101.4, 259.6]}, + {t: 'C', p: [101.8, 258.4, 105, 255.2, 105, 255.2]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [125.8, 180.6]}, + {t: 'L', p: [125.6, 183.8]}, + {t: 'L', p: [123.4, 184]}, + {t: 'C', p: [123.4, 184, 137.6, 196.6, 138.2, 204.2]}, + {t: 'C', p: [138.2, 204.2, 139, 196, 125.8, 180.6]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [129.784, 181.865]}, + {t: 'C', p: [129.353, 181.449, 129.572, 180.704, 129.164, 180.444]}, + {t: 'C', p: [128.355, 179.928, 130.462, 179.871, 130.234, 179.155]}, + {t: 'C', p: [129.851, 177.949, 130.038, 177.928, 129.916, 176.652]}, + {t: 'C', p: [129.859, 176.054, 130.447, 174.514, 130.832, 174.074]}, + {t: 'C', p: [132.278, 172.422, 130.954, 169.49, 132.594, 167.939]}, + {t: 'C', p: [132.898, 167.65, 133.274, 167.098, 133.559, 166.68]}, + {t: 'C', p: [134.218, 165.717, 135.402, 165.229, 136.352, 164.401]}, + {t: 'C', p: [136.67, 164.125, 136.469, 163.298, 137.038, 163.39]}, + {t: 'C', p: [137.752, 163.505, 138.993, 163.375, 138.948, 164.216]}, + {t: 'C', p: [138.835, 166.336, 137.506, 168.056, 136.226, 169.724]}, + {t: 'C', p: [136.677, 170.428, 136.219, 171.063, 135.935, 171.62]}, + {t: 'C', p: [134.6, 174.24, 134.789, 177.081, 134.615, 179.921]}, + {t: 'C', p: [134.61, 180.006, 134.303, 180.084, 134.311, 180.137]}, + {t: 'C', p: [134.664, 182.472, 135.248, 184.671, 136.127, 186.9]}, + {t: 'C', p: [136.493, 187.83, 136.964, 188.725, 137.114, 189.652]}, + {t: 'C', p: [137.225, 190.338, 137.328, 191.171, 136.92, 191.876]}, + {t: 'C', p: [138.955, 194.766, 137.646, 197.417, 138.815, 200.948]}, + {t: 'C', p: [139.022, 201.573, 140.714, 203.487, 140.251, 203.326]}, + {t: 'C', p: [137.738, 202.455, 137.626, 202.057, 137.449, 201.304]}, + {t: 'C', p: [137.303, 200.681, 136.973, 199.304, 136.736, 198.702]}, + {t: 'C', p: [136.672, 198.538, 136.501, 196.654, 136.423, 196.532]}, + {t: 'C', p: [134.91, 194.15, 136.268, 194.326, 134.898, 191.968]}, + {t: 'C', p: [133.47, 191.288, 132.504, 190.184, 131.381, 189.022]}, + {t: 'C', p: [131.183, 188.818, 132.326, 188.094, 132.145, 187.881]}, + {t: 'C', p: [131.053, 186.592, 129.9, 185.825, 130.236, 184.332]}, + {t: 'C', p: [130.391, 183.642, 130.528, 182.585, 129.784, 181.865]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [126.2, 183.6]}, + {t: 'C', p: [126.2, 183.6, 126.6, 190.4, 129, 192]}, + {t: 'C', p: [131.4, 193.6, 130.2, 192.8, 127, 191.6]}, + {t: 'C', p: [123.8, 190.4, 125, 189.6, 125, 189.6]}, + {t: 'C', p: [125, 189.6, 122.2, 190, 124.6, 192]}, + {t: 'C', p: [127, 194, 130.6, 196.4, 129, 196.4]}, + {t: 'C', p: [127.4, 196.4, 119.8, 192.4, 119.8, 189.6]}, + {t: 'C', p: [119.8, 186.8, 118.8, 182.7, 118.8, 182.7]}, + {t: 'C', p: [118.8, 182.7, 119.9, 181.9, 124.7, 182]}, + {t: 'C', p: [124.7, 182, 126.1, 182.7, 126.2, 183.6]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [125.4, 202.2]}, + {t: 'C', p: [125.4, 202.2, 116.88, 199.409, 98.4, 202.8]}, + {t: 'C', p: [98.4, 202.8, 107.431, 200.722, 126.2, 203]}, + {t: 'C', p: [136.5, 204.25, 125.4, 202.2, 125.4, 202.2]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [127.498, 202.129]}, + {t: 'C', p: [127.498, 202.129, 119.252, 198.611, 100.547, 200.392]}, + {t: 'C', p: [100.547, 200.392, 109.725, 199.103, 128.226, 202.995]}, + {t: 'C', p: [138.38, 205.131, 127.498, 202.129, 127.498, 202.129]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [129.286, 202.222]}, + {t: 'C', p: [129.286, 202.222, 121.324, 198.101, 102.539, 198.486]}, + {t: 'C', p: [102.539, 198.486, 111.787, 197.882, 129.948, 203.14]}, + {t: 'C', p: [139.914, 206.025, 129.286, 202.222, 129.286, 202.222]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [130.556, 202.445]}, + {t: 'C', p: [130.556, 202.445, 123.732, 198.138, 106.858, 197.04]}, + {t: 'C', p: [106.858, 197.04, 115.197, 197.21, 131.078, 203.319]}, + {t: 'C', p: [139.794, 206.672, 130.556, 202.445, 130.556, 202.445]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [245.84, 212.961]}, + {t: 'C', p: [245.84, 212.961, 244.91, 213.605, 245.124, 212.424]}, + {t: 'C', p: [245.339, 211.243, 273.547, 198.073, 277.161, 198.323]}, + {t: 'C', p: [277.161, 198.323, 246.913, 211.529, 245.84, 212.961]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [242.446, 213.6]}, + {t: 'C', p: [242.446, 213.6, 241.57, 214.315, 241.691, 213.121]}, + {t: 'C', p: [241.812, 211.927, 268.899, 196.582, 272.521, 196.548]}, + {t: 'C', p: [272.521, 196.548, 243.404, 212.089, 242.446, 213.6]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [239.16, 214.975]}, + {t: 'C', p: [239.16, 214.975, 238.332, 215.747, 238.374, 214.547]}, + {t: 'C', p: [238.416, 213.348, 258.233, 197.851, 268.045, 195.977]}, + {t: 'C', p: [268.045, 195.977, 250.015, 204.104, 239.16, 214.975]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [236.284, 216.838]}, + {t: 'C', p: [236.284, 216.838, 235.539, 217.532, 235.577, 216.453]}, + {t: 'C', p: [235.615, 215.373, 253.449, 201.426, 262.28, 199.74]}, + {t: 'C', p: [262.28, 199.74, 246.054, 207.054, 236.284, 216.838]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [204.6, 364.801]}, + {t: 'C', p: [204.6, 364.801, 189.4, 362.401, 206.2, 360.801]}, + {t: 'C', p: [206.2, 360.801, 224.2, 358.801, 228.2, 353.601]}, + {t: 'C', p: [228.2, 353.601, 241.8, 344.401, 244.6, 344.001]}, + {t: 'C', p: [247.4, 343.601, 263.8, 340.001, 264.2, 337.601]}, + {t: 'C', p: [264.6, 335.201, 270.6, 332.801, 272.2, 333.601]}, + {t: 'C', p: [273.8, 334.401, 273.8, 343.601, 271, 344.401]}, + {t: 'C', p: [268.2, 345.201, 249.4, 352.401, 243, 353.601]}, + {t: 'C', p: [236.6, 354.801, 225, 362.401, 220.2, 363.601]}, + {t: 'C', p: [215.4, 364.801, 204.6, 364.801, 204.6, 364.801]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [277.6, 327.401]}, + {t: 'C', p: [277.6, 327.401, 274.6, 329.001, 273.4, 331.601]}, + {t: 'C', p: [273.4, 331.601, 267, 342.201, 252.8, 345.401]}, + {t: 'C', p: [252.8, 345.401, 229.8, 354.401, 222, 356.401]}, + {t: 'C', p: [222, 356.401, 208.6, 361.401, 201.2, 360.601]}, + {t: 'C', p: [201.2, 360.601, 194.2, 360.801, 200.4, 362.401]}, + {t: 'C', p: [200.4, 362.401, 220.6, 360.401, 224, 358.601]}, + {t: 'C', p: [224, 358.601, 239.6, 353.401, 242.6, 350.801]}, + {t: 'C', p: [245.6, 348.201, 263.8, 343.201, 266, 341.201]}, + {t: 'C', p: [268.2, 339.201, 278, 330.801, 277.6, 327.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [218.882, 358.911]}, + {t: 'C', p: [218.882, 358.911, 224.111, 358.685, 222.958, 360.234]}, + {t: 'C', p: [221.805, 361.784, 219.357, 360.91, 219.357, 360.91]}, + {t: 'L', p: [218.882, 358.911]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [211.68, 360.263]}, + {t: 'C', p: [211.68, 360.263, 216.908, 360.037, 215.756, 361.586]}, + {t: 'C', p: [214.603, 363.136, 212.155, 362.263, 212.155, 362.263]}, + {t: 'L', p: [211.68, 360.263]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [201.251, 361.511]}, + {t: 'C', p: [201.251, 361.511, 206.48, 361.284, 205.327, 362.834]}, + {t: 'C', p: [204.174, 364.383, 201.726, 363.51, 201.726, 363.51]}, + {t: 'L', p: [201.251, 361.511]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [193.617, 362.055]}, + {t: 'C', p: [193.617, 362.055, 198.846, 361.829, 197.693, 363.378]}, + {t: 'C', p: [196.54, 364.928, 194.092, 364.054, 194.092, 364.054]}, + {t: 'L', p: [193.617, 362.055]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [235.415, 351.513]}, + {t: 'C', p: [235.415, 351.513, 242.375, 351.212, 240.84, 353.274]}, + {t: 'C', p: [239.306, 355.336, 236.047, 354.174, 236.047, 354.174]}, + {t: 'L', p: [235.415, 351.513]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [245.73, 347.088]}, + {t: 'C', p: [245.73, 347.088, 251.689, 343.787, 251.155, 348.849]}, + {t: 'C', p: [250.885, 351.405, 246.362, 349.749, 246.362, 349.749]}, + {t: 'L', p: [245.73, 347.088]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [254.862, 344.274]}, + {t: 'C', p: [254.862, 344.274, 262.021, 340.573, 260.287, 346.035]}, + {t: 'C', p: [259.509, 348.485, 255.493, 346.935, 255.493, 346.935]}, + {t: 'L', p: [254.862, 344.274]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [264.376, 339.449]}, + {t: 'C', p: [264.376, 339.449, 268.735, 334.548, 269.801, 341.21]}, + {t: 'C', p: [270.207, 343.748, 265.008, 342.11, 265.008, 342.11]}, + {t: 'L', p: [264.376, 339.449]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [226.834, 355.997]}, + {t: 'C', p: [226.834, 355.997, 232.062, 355.77, 230.91, 357.32]}, + {t: 'C', p: [229.757, 358.869, 227.308, 357.996, 227.308, 357.996]}, + {t: 'L', p: [226.834, 355.997]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [262.434, 234.603]}, + {t: 'C', p: [262.434, 234.603, 261.708, 235.268, 261.707, 234.197]}, + {t: 'C', p: [261.707, 233.127, 279.191, 219.863, 288.034, 218.479]}, + {t: 'C', p: [288.034, 218.479, 271.935, 225.208, 262.434, 234.603]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [265.4, 298.4]}, + {t: 'C', p: [265.4, 298.4, 287.401, 320.801, 296.601, 324.401]}, + {t: 'C', p: [296.601, 324.401, 305.801, 335.601, 301.801, 361.601]}, + {t: 'C', p: [301.801, 361.601, 298.601, 369.201, 295.401, 348.401]}, + {t: 'C', p: [295.401, 348.401, 298.601, 323.201, 287.401, 339.201]}, + {t: 'C', p: [287.401, 339.201, 279, 329.301, 285.4, 329.601]}, + {t: 'C', p: [285.4, 329.601, 288.601, 331.601, 289.001, 330.001]}, + {t: 'C', p: [289.401, 328.401, 281.4, 314.801, 264.2, 300.4]}, + {t: 'C', p: [247, 286, 265.4, 298.4, 265.4, 298.4]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [207, 337.201]}, + {t: 'C', p: [207, 337.201, 206.8, 335.401, 208.6, 336.201]}, + {t: 'C', p: [210.4, 337.001, 304.601, 343.201, 336.201, 367.201]}, + {t: 'C', p: [336.201, 367.201, 291.001, 344.001, 207, 337.201]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [217.4, 332.801]}, + {t: 'C', p: [217.4, 332.801, 217.2, 331.001, 219, 331.801]}, + {t: 'C', p: [220.8, 332.601, 357.401, 331.601, 381.001, 364.001]}, + {t: 'C', p: [381.001, 364.001, 359.001, 338.801, 217.4, 332.801]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [229, 328.801]}, + {t: 'C', p: [229, 328.801, 228.8, 327.001, 230.6, 327.801]}, + {t: 'C', p: [232.4, 328.601, 405.801, 315.601, 429.401, 348.001]}, + {t: 'C', p: [429.401, 348.001, 419.801, 322.401, 229, 328.801]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [239, 324.001]}, + {t: 'C', p: [239, 324.001, 238.8, 322.201, 240.6, 323.001]}, + {t: 'C', p: [242.4, 323.801, 364.601, 285.2, 388.201, 317.601]}, + {t: 'C', p: [388.201, 317.601, 374.801, 293, 239, 324.001]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [181, 346.801]}, + {t: 'C', p: [181, 346.801, 180.8, 345.001, 182.6, 345.801]}, + {t: 'C', p: [184.4, 346.601, 202.2, 348.801, 204.2, 387.601]}, + {t: 'C', p: [204.2, 387.601, 197, 345.601, 181, 346.801]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [172.2, 348.401]}, + {t: 'C', p: [172.2, 348.401, 172, 346.601, 173.8, 347.401]}, + {t: 'C', p: [175.6, 348.201, 189.8, 343.601, 187, 382.401]}, + {t: 'C', p: [187, 382.401, 188.2, 347.201, 172.2, 348.401]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [164.2, 348.801]}, + {t: 'C', p: [164.2, 348.801, 164, 347.001, 165.8, 347.801]}, + {t: 'C', p: [167.6, 348.601, 183, 349.201, 170.6, 371.601]}, + {t: 'C', p: [170.6, 371.601, 180.2, 347.601, 164.2, 348.801]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [211.526, 304.465]}, + {t: 'C', p: [211.526, 304.465, 211.082, 306.464, 212.631, 305.247]}, + {t: 'C', p: [228.699, 292.622, 261.141, 233.72, 316.826, 228.086]}, + {t: 'C', p: [316.826, 228.086, 278.518, 215.976, 211.526, 304.465]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [222.726, 302.665]}, + {t: 'C', p: [222.726, 302.665, 221.363, 301.472, 223.231, 300.847]}, + {t: 'C', p: [225.099, 300.222, 337.541, 227.72, 376.826, 235.686]}, + {t: 'C', p: [376.826, 235.686, 349.719, 228.176, 222.726, 302.665]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [201.885, 308.767]}, + {t: 'C', p: [201.885, 308.767, 201.376, 310.366, 203.087, 309.39]}, + {t: 'C', p: [212.062, 304.27, 215.677, 247.059, 259.254, 245.804]}, + {t: 'C', p: [259.254, 245.804, 226.843, 231.09, 201.885, 308.767]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [181.962, 319.793]}, + {t: 'C', p: [181.962, 319.793, 180.885, 321.079, 182.838, 320.825]}, + {t: 'C', p: [193.084, 319.493, 214.489, 278.222, 258.928, 283.301]}, + {t: 'C', p: [258.928, 283.301, 226.962, 268.955, 181.962, 319.793]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [193.2, 313.667]}, + {t: 'C', p: [193.2, 313.667, 192.389, 315.136, 194.258, 314.511]}, + {t: 'C', p: [204.057, 311.237, 217.141, 266.625, 261.729, 263.078]}, + {t: 'C', p: [261.729, 263.078, 227.603, 255.135, 193.2, 313.667]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [174.922, 324.912]}, + {t: 'C', p: [174.922, 324.912, 174.049, 325.954, 175.631, 325.748]}, + {t: 'C', p: [183.93, 324.669, 201.268, 291.24, 237.264, 295.354]}, + {t: 'C', p: [237.264, 295.354, 211.371, 283.734, 174.922, 324.912]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [167.323, 330.821]}, + {t: 'C', p: [167.323, 330.821, 166.318, 331.866, 167.909, 331.748]}, + {t: 'C', p: [172.077, 331.439, 202.715, 298.36, 221.183, 313.862]}, + {t: 'C', p: [221.183, 313.862, 209.168, 295.139, 167.323, 330.821]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [236.855, 298.898]}, + {t: 'C', p: [236.855, 298.898, 235.654, 297.543, 237.586, 297.158]}, + {t: 'C', p: [239.518, 296.774, 360.221, 239.061, 398.184, 251.927]}, + {t: 'C', p: [398.184, 251.927, 372.243, 241.053, 236.855, 298.898]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [203.4, 363.201]}, + {t: 'C', p: [203.4, 363.201, 203.2, 361.401, 205, 362.201]}, + {t: 'C', p: [206.8, 363.001, 222.2, 363.601, 209.8, 386.001]}, + {t: 'C', p: [209.8, 386.001, 219.4, 362.001, 203.4, 363.201]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [213.8, 361.601]}, + {t: 'C', p: [213.8, 361.601, 213.6, 359.801, 215.4, 360.601]}, + {t: 'C', p: [217.2, 361.401, 235, 363.601, 237, 402.401]}, + {t: 'C', p: [237, 402.401, 229.8, 360.401, 213.8, 361.601]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [220.6, 360.001]}, + {t: 'C', p: [220.6, 360.001, 220.4, 358.201, 222.2, 359.001]}, + {t: 'C', p: [224, 359.801, 248.6, 363.201, 272.2, 395.601]}, + {t: 'C', p: [272.2, 395.601, 236.6, 358.801, 220.6, 360.001]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [228.225, 357.972]}, + {t: 'C', p: [228.225, 357.972, 227.788, 356.214, 229.678, 356.768]}, + {t: 'C', p: [231.568, 357.322, 252.002, 355.423, 290.099, 389.599]}, + {t: 'C', p: [290.099, 389.599, 243.924, 354.656, 228.225, 357.972]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [238.625, 353.572]}, + {t: 'C', p: [238.625, 353.572, 238.188, 351.814, 240.078, 352.368]}, + {t: 'C', p: [241.968, 352.922, 276.802, 357.423, 328.499, 392.399]}, + {t: 'C', p: [328.499, 392.399, 254.324, 350.256, 238.625, 353.572]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [198.2, 342.001]}, + {t: 'C', p: [198.2, 342.001, 198, 340.201, 199.8, 341.001]}, + {t: 'C', p: [201.6, 341.801, 255, 344.401, 285.4, 371.201]}, + {t: 'C', p: [285.4, 371.201, 250.499, 346.426, 198.2, 342.001]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [188.2, 346.001]}, + {t: 'C', p: [188.2, 346.001, 188, 344.201, 189.8, 345.001]}, + {t: 'C', p: [191.6, 345.801, 216.2, 349.201, 239.8, 381.601]}, + {t: 'C', p: [239.8, 381.601, 204.2, 344.801, 188.2, 346.001]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [249.503, 348.962]}, + {t: 'C', p: [249.503, 348.962, 248.938, 347.241, 250.864, 347.655]}, + {t: 'C', p: [252.79, 348.068, 287.86, 350.004, 341.981, 381.098]}, + {t: 'C', p: [341.981, 381.098, 264.317, 346.704, 249.503, 348.962]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [257.903, 346.562]}, + {t: 'C', p: [257.903, 346.562, 257.338, 344.841, 259.264, 345.255]}, + {t: 'C', p: [261.19, 345.668, 296.26, 347.604, 350.381, 378.698]}, + {t: 'C', p: [350.381, 378.698, 273.317, 343.904, 257.903, 346.562]}, + {t: 'z', p: []}]}, + +{f: '#fff', s: {c: '#000', w: 0.1}, + p: [{t: 'M', p: [267.503, 341.562]}, + {t: 'C', p: [267.503, 341.562, 266.938, 339.841, 268.864, 340.255]}, + {t: 'C', p: [270.79, 340.668, 313.86, 345.004, 403.582, 379.298]}, + {t: 'C', p: [403.582, 379.298, 282.917, 338.904, 267.503, 341.562]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [156.2, 348.401]}, + {t: 'C', p: [156.2, 348.401, 161.4, 348.001, 160.2, 349.601]}, + {t: 'C', p: [159, 351.201, 156.6, 350.401, 156.6, 350.401]}, + {t: 'L', p: [156.2, 348.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [187, 362.401]}, + {t: 'C', p: [187, 362.401, 192.2, 362.001, 191, 363.601]}, + {t: 'C', p: [189.8, 365.201, 187.4, 364.401, 187.4, 364.401]}, + {t: 'L', p: [187, 362.401]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [178.2, 362.001]}, + {t: 'C', p: [178.2, 362.001, 183.4, 361.601, 182.2, 363.201]}, + {t: 'C', p: [181, 364.801, 178.6, 364.001, 178.6, 364.001]}, + {t: 'L', p: [178.2, 362.001]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [82.831, 350.182]}, + {t: 'C', p: [82.831, 350.182, 87.876, 351.505, 86.218, 352.624]}, + {t: 'C', p: [84.561, 353.744, 82.554, 352.202, 82.554, 352.202]}, + {t: 'L', p: [82.831, 350.182]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [84.831, 340.582]}, + {t: 'C', p: [84.831, 340.582, 89.876, 341.905, 88.218, 343.024]}, + {t: 'C', p: [86.561, 344.144, 84.554, 342.602, 84.554, 342.602]}, + {t: 'L', p: [84.831, 340.582]}, + {t: 'z', p: []}]}, + +{f: '#000', s: null, p: [{t: 'M', p: [77.631, 336.182]}, + {t: 'C', p: [77.631, 336.182, 82.676, 337.505, 81.018, 338.624]}, + {t: 'C', p: [79.361, 339.744, 77.354, 338.202, 77.354, 338.202]}, + {t: 'L', p: [77.631, 336.182]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [157.4, 411.201]}, + {t: 'C', p: [157.4, 411.201, 155.8, 411.201, 151.8, 413.201]}, + {t: 'C', p: [149.8, 413.201, 138.6, 416.801, 133, 426.801]}, + {t: 'C', p: [133, 426.801, 145.4, 417.201, 157.4, 411.201]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [245.116, 503.847]}, + {t: 'C', p: [245.257, 504.105, 245.312, 504.525, 245.604, 504.542]}, + {t: 'C', p: [246.262, 504.582, 247.495, 504.883, 247.37, 504.247]}, + {t: 'C', p: [246.522, 499.941, 245.648, 495.004, 241.515, 493.197]}, + {t: 'C', p: [240.876, 492.918, 239.434, 493.331, 239.36, 494.215]}, + {t: 'C', p: [239.233, 495.739, 239.116, 497.088, 239.425, 498.554]}, + {t: 'C', p: [239.725, 499.975, 241.883, 499.985, 242.8, 498.601]}, + {t: 'C', p: [243.736, 500.273, 244.168, 502.116, 245.116, 503.847]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [234.038, 508.581]}, + {t: 'C', p: [234.786, 509.994, 234.659, 511.853, 236.074, 512.416]}, + {t: 'C', p: [236.814, 512.71, 238.664, 511.735, 238.246, 510.661]}, + {t: 'C', p: [237.444, 508.6, 237.056, 506.361, 235.667, 504.55]}, + {t: 'C', p: [235.467, 504.288, 235.707, 503.755, 235.547, 503.427]}, + {t: 'C', p: [234.953, 502.207, 233.808, 501.472, 232.4, 501.801]}, + {t: 'C', p: [231.285, 504.004, 232.433, 506.133, 233.955, 507.842]}, + {t: 'C', p: [234.091, 507.994, 233.925, 508.37, 234.038, 508.581]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [194.436, 503.391]}, + {t: 'C', p: [194.328, 503.014, 194.29, 502.551, 194.455, 502.23]}, + {t: 'C', p: [194.986, 501.197, 195.779, 500.075, 195.442, 499.053]}, + {t: 'C', p: [195.094, 497.997, 193.978, 498.179, 193.328, 498.748]}, + {t: 'C', p: [192.193, 499.742, 192.144, 501.568, 191.453, 502.927]}, + {t: 'C', p: [191.257, 503.313, 191.308, 503.886, 190.867, 504.277]}, + {t: 'C', p: [190.393, 504.698, 189.953, 506.222, 190.049, 506.793]}, + {t: 'C', p: [190.102, 507.106, 189.919, 517.014, 190.141, 516.751]}, + {t: 'C', p: [190.76, 516.018, 193.81, 506.284, 193.879, 505.392]}, + {t: 'C', p: [193.936, 504.661, 194.668, 504.196, 194.436, 503.391]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [168.798, 496.599]}, + {t: 'C', p: [171.432, 494.1, 174.222, 491.139, 173.78, 487.427]}, + {t: 'C', p: [173.664, 486.451, 171.889, 486.978, 171.702, 487.824]}, + {t: 'C', p: [170.9, 491.449, 168.861, 494.11, 166.293, 496.502]}, + {t: 'C', p: [164.097, 498.549, 162.235, 504.893, 162, 505.401]}, + {t: 'C', p: [165.697, 500.145, 167.954, 497.399, 168.798, 496.599]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [155.224, 490.635]}, + {t: 'C', p: [155.747, 490.265, 155.445, 489.774, 155.662, 489.442]}, + {t: 'C', p: [156.615, 487.984, 157.916, 486.738, 157.934, 485]}, + {t: 'C', p: [157.937, 484.723, 157.559, 484.414, 157.224, 484.638]}, + {t: 'C', p: [156.947, 484.822, 156.605, 484.952, 156.497, 485.082]}, + {t: 'C', p: [154.467, 487.531, 153.067, 490.202, 151.624, 493.014]}, + {t: 'C', p: [151.441, 493.371, 150.297, 497.862, 150.61, 497.973]}, + {t: 'C', p: [150.849, 498.058, 152.569, 493.877, 152.779, 493.763]}, + {t: 'C', p: [154.042, 493.077, 154.054, 491.462, 155.224, 490.635]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [171.957, 510.179]}, + {t: 'C', p: [172.401, 509.31, 173.977, 508.108, 173.864, 507.219]}, + {t: 'C', p: [173.746, 506.291, 174.214, 504.848, 173.302, 505.536]}, + {t: 'C', p: [172.045, 506.484, 168.596, 507.833, 168.326, 513.641]}, + {t: 'C', p: [168.3, 514.212, 171.274, 511.519, 171.957, 510.179]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [186.4, 493.001]}, + {t: 'C', p: [186.8, 492.333, 187.508, 492.806, 187.967, 492.543]}, + {t: 'C', p: [188.615, 492.171, 189.226, 491.613, 189.518, 490.964]}, + {t: 'C', p: [190.488, 488.815, 192.257, 486.995, 192.4, 484.601]}, + {t: 'C', p: [190.909, 483.196, 190.23, 485.236, 189.6, 486.201]}, + {t: 'C', p: [188.277, 484.554, 187.278, 486.428, 185.978, 486.947]}, + {t: 'C', p: [185.908, 486.975, 185.695, 486.628, 185.62, 486.655]}, + {t: 'C', p: [184.443, 487.095, 183.763, 488.176, 182.765, 488.957]}, + {t: 'C', p: [182.594, 489.091, 182.189, 488.911, 182.042, 489.047]}, + {t: 'C', p: [181.39, 489.65, 180.417, 489.975, 180.137, 490.657]}, + {t: 'C', p: [179.027, 493.364, 175.887, 495.459, 174, 503.001]}, + {t: 'C', p: [174.381, 503.91, 178.512, 496.359, 178.999, 495.661]}, + {t: 'C', p: [179.835, 494.465, 179.953, 497.322, 181.229, 496.656]}, + {t: 'C', p: [181.28, 496.629, 181.466, 496.867, 181.6, 497.001]}, + {t: 'C', p: [181.794, 496.721, 182.012, 496.492, 182.4, 496.601]}, + {t: 'C', p: [182.4, 496.201, 182.266, 495.645, 182.467, 495.486]}, + {t: 'C', p: [183.704, 494.509, 183.62, 493.441, 184.4, 492.201]}, + {t: 'C', p: [184.858, 492.99, 185.919, 492.271, 186.4, 493.001]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [246.2, 547.401]}, + {t: 'C', p: [246.2, 547.401, 253.6, 527.001, 249.2, 515.801]}, + {t: 'C', p: [249.2, 515.801, 260.6, 537.401, 256, 548.601]}, + {t: 'C', p: [256, 548.601, 255.6, 538.201, 251.6, 533.201]}, + {t: 'C', p: [251.6, 533.201, 247.6, 546.001, 246.2, 547.401]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [231.4, 544.801]}, + {t: 'C', p: [231.4, 544.801, 236.8, 536.001, 228.8, 517.601]}, + {t: 'C', p: [228.8, 517.601, 228, 538.001, 221.2, 549.001]}, + {t: 'C', p: [221.2, 549.001, 235.4, 528.801, 231.4, 544.801]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [221.4, 542.801]}, + {t: 'C', p: [221.4, 542.801, 221.2, 522.801, 221.6, 519.801]}, + {t: 'C', p: [221.6, 519.801, 217.8, 536.401, 207.6, 546.001]}, + {t: 'C', p: [207.6, 546.001, 222, 534.001, 221.4, 542.801]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [211.8, 510.801]}, + {t: 'C', p: [211.8, 510.801, 217.8, 524.401, 207.8, 542.801]}, + {t: 'C', p: [207.8, 542.801, 214.2, 530.601, 209.4, 523.601]}, + {t: 'C', p: [209.4, 523.601, 212, 520.201, 211.8, 510.801]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [192.6, 542.401]}, + {t: 'C', p: [192.6, 542.401, 191.6, 526.801, 193.4, 524.601]}, + {t: 'C', p: [193.4, 524.601, 193.6, 518.201, 193.2, 517.201]}, + {t: 'C', p: [193.2, 517.201, 197.2, 511.001, 197.4, 518.401]}, + {t: 'C', p: [197.4, 518.401, 198.8, 526.201, 201.6, 530.801]}, + {t: 'C', p: [201.6, 530.801, 205.2, 536.201, 205, 542.601]}, + {t: 'C', p: [205, 542.601, 195, 512.401, 192.6, 542.401]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [189, 514.801]}, + {t: 'C', p: [189, 514.801, 182.4, 525.601, 180.6, 544.601]}, + {t: 'C', p: [180.6, 544.601, 179.2, 538.401, 183, 524.001]}, + {t: 'C', p: [183, 524.001, 187.2, 508.601, 189, 514.801]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [167.2, 534.601]}, + {t: 'C', p: [167.2, 534.601, 172.2, 529.201, 173.6, 524.201]}, + {t: 'C', p: [173.6, 524.201, 177.2, 508.401, 170.8, 517.001]}, + {t: 'C', p: [170.8, 517.001, 171, 525.001, 162.8, 532.401]}, + {t: 'C', p: [162.8, 532.401, 167.6, 530.001, 167.2, 534.601]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [161.4, 529.601]}, + {t: 'C', p: [161.4, 529.601, 164.8, 512.201, 165.6, 511.401]}, + {t: 'C', p: [165.6, 511.401, 167.4, 508.001, 164.6, 511.201]}, + {t: 'C', p: [164.6, 511.201, 155.8, 530.401, 151.8, 537.001]}, + {t: 'C', p: [151.8, 537.001, 159.8, 527.801, 161.4, 529.601]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [155.6, 513.001]}, + {t: 'C', p: [155.6, 513.001, 167.2, 490.601, 145.4, 516.401]}, + {t: 'C', p: [145.4, 516.401, 156.4, 506.601, 155.6, 513.001]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [140.2, 498.401]}, + {t: 'C', p: [140.2, 498.401, 145, 479.601, 147.6, 479.801]}, + {t: 'C', p: [147.6, 479.801, 155.8, 470.801, 149.2, 481.401]}, + {t: 'C', p: [149.2, 481.401, 143.2, 491.001, 143.8, 500.801]}, + {t: 'C', p: [143.8, 500.801, 143.2, 491.201, 140.2, 498.401]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [470.5, 487]}, + {t: 'C', p: [470.5, 487, 458.5, 477, 456, 473.5]}, + {t: 'C', p: [456, 473.5, 469.5, 492, 469.5, 499]}, + {t: 'C', p: [469.5, 499, 472, 491.5, 470.5, 487]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [476, 465]}, + {t: 'C', p: [476, 465, 455, 450, 451.5, 442.5]}, + {t: 'C', p: [451.5, 442.5, 478, 472, 478, 476.5]}, + {t: 'C', p: [478, 476.5, 478.5, 467.5, 476, 465]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [493, 311]}, + {t: 'C', p: [493, 311, 481, 303, 479.5, 305]}, + {t: 'C', p: [479.5, 305, 490, 311.5, 492.5, 320]}, + {t: 'C', p: [492.5, 320, 491, 311, 493, 311]}, + {t: 'z', p: []}]}, + +{f: '#ccc', s: null, p: [{t: 'M', p: [501.5, 391.5]}, + {t: 'L', p: [484, 379.5]}, + {t: 'C', p: [484, 379.5, 503, 396.5, 503.5, 400.5]}, + {t: 'L', p: [501.5, 391.5]}, + {t: 'z', p: []}]}, + +{f: null, s: '#000', p: [{t: 'M', p: [110.75, 369]}, + {t: 'L', p: [132.75, 373.75]}]}, + +{f: null, s: '#000', p: [{t: 'M', p: [161, 531]}, + {t: 'C', p: [161, 531, 160.5, 527.5, 151.5, 538]}]}, + +{f: null, s: '#000', p: [{t: 'M', p: [166.5, 536]}, + {t: 'C', p: [166.5, 536, 168.5, 529.5, 162, 534]}]}, + +{f: null, s: '#000', p: [{t: 'M', p: [220.5, 544.5]}, + {t: 'C', p: [220.5, 544.5, 222, 533.5, 210.5, 546.5]}]}] diff --git a/mapbox-gl/closure-library/closure/goog/demos/history1.html b/mapbox-gl/closure-library/closure/goog/demos/history1.html new file mode 100644 index 0000000000..63b3a0c04c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/history1.html @@ -0,0 +1,132 @@ + + + + + goog.History + + + + + + + +

goog.History

+

This page demonstrates the goog.History object which can create new browser + history entries without leaving the page. This version uses the hash portion of + the URL to make the history state available to the user. These URLs can be + bookmarked, edited, pasted in emails, etc., just like normal URLs. The browser's + back and forward buttons will navigate between the visited history states.

+ +

Try following the hash links below, or updating the location with your own + tokens. Replacing the token will update the page address without appending a + new history entry.

+ +

+ Set #fragment
+ first
+ second
+ third +

+ +

+ Set Token
+ + + + + +

+ +

+ + + +

+ +
+

The current history state:

+
+
+ +

The state should be correctly restored after you + leave the page and hit the back button.

+ +

The history object can also be created so that the history state is not + user-visible/modifiable. + See history2.html for a demo. + To see visible/modifiable history work when the goog.History code itself is + loaded inside a hidden iframe, + see history3.html. +

+ +
+ Event Log +
+
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/history2.html b/mapbox-gl/closure-library/closure/goog/demos/history2.html new file mode 100644 index 0000000000..01a166041c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/history2.html @@ -0,0 +1,119 @@ + + + + + goog.History #2 + + + + + + +

goog.History #2

+

This page demonstrates the goog.History object which can create new browser + history entries without leaving the page. This version maintains the history + state internally, so that states are not visible or editable by the user, but + the back and forward buttons can still be used to move between history states. +

+ +

Try setting a few history tokens using the buttons and box below, then hit + the back and forward buttons to test if the tokens are correctly restored.

+ + + + +
+ + + + + + + +

+ + + +

+ +
+

The current history state:

+
+
+ +

The state should be correctly restored after you + leave the page and hit the back button.

+ +

The history object can also be created so that the history state is visible + and modifiable by the user. See history1.html for a + demo.

+ +
+ Event Log +
+
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/history3.html b/mapbox-gl/closure-library/closure/goog/demos/history3.html new file mode 100644 index 0000000000..d1466f80f9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/history3.html @@ -0,0 +1,116 @@ + + + + +History Demo 3 + + + + + + +

This page demonstrates a goog.History object used in an iframe. Loading JS +code in an iframe is useful for large apps because the JS code can be sent in +bite-sized script blocks that browsers can evaluate incrementally, as they are +received over the wire.

+ +

For an introduction to the goog.History object, see history1.html and history2.html. This demo uses visible history, like +the first demo.

+ +

Try following the hash links below, or updating the location with your own +tokens. Replacing the token will update the page address without appending a +new history entry.

+ +

+ Set #fragment
+ first
+ second
+ third +

+ +

+ Set Token
+ + + + + +

+ +

+ + + +

+ +
+

The current history state:

+
+
+ +

The state should be correctly restored after you +leave the page and hit the back button.

+ +
+ Event Log +
+
+ + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/history3js.html b/mapbox-gl/closure-library/closure/goog/demos/history3js.html new file mode 100644 index 0000000000..072dfca6b6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/history3js.html @@ -0,0 +1,48 @@ + + + + +History Demo JavaScript Page + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/history_blank.html b/mapbox-gl/closure-library/closure/goog/demos/history_blank.html new file mode 100644 index 0000000000..189d905626 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/history_blank.html @@ -0,0 +1,26 @@ + + + +Intentionally left blank + + +This is a blank helper page for the goog.History demos. See +demo 1 and +demo 2. + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/hovercard.html b/mapbox-gl/closure-library/closure/goog/demos/hovercard.html new file mode 100644 index 0000000000..c36f6d314e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/hovercard.html @@ -0,0 +1,177 @@ + + + + + goog.ui.HoverCard + + + + + + + +

goog.ui.HoverCard

+

+ Show by mouse position:


+ Tom Smith + Dick Jones + Harry Brown + +


Show hovercard to the right:


+ Tom Smith + Dick Jones + Harry Brown + +


Show hovercard below:


+ Tom Smith + Dick Jones + Harry Brown + +


+ +

+ + + + +
+ Event Log +
+
+
+
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/hsvapalette.html b/mapbox-gl/closure-library/closure/goog/demos/hsvapalette.html new file mode 100644 index 0000000000..ec60c75d56 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/hsvapalette.html @@ -0,0 +1,55 @@ + + + + + + goog.ui.HsvaPalette + + + + + + + +

goog.ui.HsvaPalette

+ +

Normal Size

+ + + +

Smaller Size

+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/hsvpalette.html b/mapbox-gl/closure-library/closure/goog/demos/hsvpalette.html new file mode 100644 index 0000000000..2717ed18e5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/hsvpalette.html @@ -0,0 +1,56 @@ + + + + + + goog.ui.HsvPalette + + + + + + + +

goog.ui.HsvPalette

+ +

Normal Size

+ + + +

Smaller Size

+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/html5history.html b/mapbox-gl/closure-library/closure/goog/demos/html5history.html new file mode 100644 index 0000000000..b886846205 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/html5history.html @@ -0,0 +1,87 @@ + + + + + goog.history.Html5History Demo + + + + + + +

goog.history.Html5History

+ + + +
+ +
+
+ +
+
+ +
+
+ +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/imagelessbutton.html b/mapbox-gl/closure-library/closure/goog/demos/imagelessbutton.html new file mode 100644 index 0000000000..86462d72e9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/imagelessbutton.html @@ -0,0 +1,221 @@ + + + + + + goog.ui.ImagelessButtonRenderer Demo + + + + + + + + +

goog.ui.ImagelessButtonRenderer

+
+ + These buttons were rendered using + goog.ui.ImagelessButtonRenderer: + +
+ These buttons were created programmatically:
+
+
+ These buttons were created by decorating some DIVs, and they dispatch + state transition events (watch the event log):
+
+ +
+ Decorated Button, yay! +
+
Decorated Disabled
+
Another Button
+
+ Archive +
+ Delete +
+ Report Spam +
+
+
+ Use these ToggleButtons to hide/show and enable/disable + the middle button:
+
Enable
+ +
Show
+ +

+ Combined toggle buttons
+
+ Bold +
+ Italics +
+ Underlined +
+

+ These buttons have icons, and the second one has an extra CSS class:
+
+
+
+
+
+ +
+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/imagelessmenubutton.html b/mapbox-gl/closure-library/closure/goog/demos/imagelessmenubutton.html new file mode 100644 index 0000000000..cdf18d605e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/imagelessmenubutton.html @@ -0,0 +1,285 @@ + + + + + goog.ui.ImagelessMenuButtonRenderer Demo + + + + + + + + + + + + +

goog.ui.ImagelessMenuButtonRenderer

+ + + + + + + +
+
+ + These MenuButtons were created programmatically: +   + + + + + + + + +
+ + + Enable first button: + +   + Show second button: + +   +
+ +
+
+
+ + This MenuButton decorates an element:  + + + + + + + + +
+
+ +
+ Format + +
+
Bold
+
Italic
+
Underline
+
+
+ Strikethrough +
+
+
Font...
+
Color...
+
+
+
+ Enable button: + +   + Show button: + +   +
+ +
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/index.html b/mapbox-gl/closure-library/closure/goog/demos/index.html new file mode 100644 index 0000000000..056c893803 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/index.html @@ -0,0 +1,20 @@ + + + + + Closure Demos + + + + + + + Are you kidding me? No frames?!? + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/index_nav.html b/mapbox-gl/closure-library/closure/goog/demos/index_nav.html new file mode 100644 index 0000000000..1002554e07 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/index_nav.html @@ -0,0 +1,255 @@ + + + + + Closure Demos + + + + + + + +

Index

+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/index_splash.html b/mapbox-gl/closure-library/closure/goog/demos/index_splash.html new file mode 100644 index 0000000000..86397df9b8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/index_splash.html @@ -0,0 +1,27 @@ + + + + + Closure Demos + + + + +

Welcome to Closure!

+

Use the tree in the navigation pane to view Closure demos.

+
+

New! Common UI Controls

+

Check out these widgets by clicking on the demo links on the left:

+ Common UI controls + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/inline_block_quirks.html b/mapbox-gl/closure-library/closure/goog/demos/inline_block_quirks.html new file mode 100644 index 0000000000..75202b8148 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/inline_block_quirks.html @@ -0,0 +1,125 @@ + + + + goog.style.setInlineBlock in quirks mode + + + + + + + +

goog.style.setInlineBlock in quirks mode

+

+ This is a demonstration of the goog-inline-block CSS style. + This page is in quirks mode. + Click here for standards mode. +

+
+ Hey, are these really +
DIV
s + inlined in my text here? I mean, I thought +
DIV
s + were block-level elements, and you couldn't inline them... + Must be that new +
goog-inline-block
+ style... (Hint: Try resizing the window to see the +
DIV
s + flow naturally.) + Arv asked for an inline-block DIV with more interesting contents, so here + goes: +
+
+ blue dot + Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. + Donec rhoncus neque ut + neque porta consequat. + In tincidunt tellus vehicula tellus. Etiam ornare nunc + vel lectus. Vivamus quis nibh. Sed nunc. + On FF1.5 and FF2.0, you need to wrap the contents of your + inline-block element in a DIV or P with fixed width to get line + wrapping. +
+
+
+
+

+ These are + SPANs + with the + goog-inline-block + style applied, so you can style them like block-level elements. + For example, give them + 10px margin, a + 10px border, or + 10px padding. + I used + vertical-align: middle + to make them all line up reasonably well. + (Except on Safari 2. Go figure.) +

+

+ This is what the same content looks like without goog-inline-block: +

+

+ These are + SPANs + with the + goog-inline-block + style applied, so you can style them like block-level elements. + For example, give them + 10px margin, a + 10px border, or + 10px padding. + I used + vertical-align: middle + to make them all line up reasonably well. + (Except on Safari 2. Go figure.) +

+

+ Click here to use goog.style.setInlineBlock() to apply the inline-block style to these SPANs. +

+
+

+ Works on Internet Explorer 6 & 7, Firefox 1.5, 2.0 & 3.0 Beta, Safari 2 & 3, + Webkit nightlies, and Opera 9. + Note: DIVs nested in SPANs don't work on Opera. +

+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/inline_block_standards.html b/mapbox-gl/closure-library/closure/goog/demos/inline_block_standards.html new file mode 100644 index 0000000000..5f4304893f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/inline_block_standards.html @@ -0,0 +1,126 @@ + + + + + goog.style.setInlineBlock in standards mode + + + + + + + +

goog.style.setInlineBlock in standards mode

+

+ This is a demonstration of the goog-inline-block CSS style. + This page is in standards mode. + Click here for quirks mode. +

+
+ Hey, are these really +
DIV
s + inlined in my text here? I mean, I thought +
DIV
s + were block-level elements, and you couldn't inline them... + Must be that new +
goog-inline-block
+ style... (Hint: Try resizing the window to see the +
DIV
s + flow naturally.) + Arv asked for an inline-block DIV with more interesting contents, so here + goes: +
+
+ blue dot + Lorem ipsum dolor sit amet, + consectetuer adipiscing elit. + Donec rhoncus neque ut + neque porta consequat. + In tincidunt tellus vehicula tellus. Etiam ornare nunc + vel lectus. Vivamus quis nibh. Sed nunc. + On FF1.5 and FF2.0, you need to wrap the contents of your + inline-block element in a DIV or P with fixed width to get line + wrapping. +
+
+
+
+

+ These are + SPANs + with the + goog-inline-block + style applied, so you can style them like block-level elements. + For example, give them + 10px margin, a + 10px border, or + 10px padding. + I used + vertical-align: middle + to make them all line up reasonably well. + (Except on Safari 2. Go figure.) +

+

+ This is what the same content looks like without goog-inline-block: +

+

+ These are + SPANs + with the + goog-inline-block + style applied, so you can style them like block-level elements. + For example, give them + 10px margin, a + 10px border, or + 10px padding. + I used + vertical-align: middle + to make them all line up reasonably well. + (Except on Safari 2. Go figure.) +

+

+ Click here to use goog.style.setInlineBlock() to apply the inline-block style to these SPANs. +

+
+

+ Works on Internet Explorer 6 & 7, Firefox 1.5, 2.0 & 3.0 Beta, Safari 2 & 3, + Webkit nightlies, and Opera 9. + Note: DIVs nested in SPANs don't work on Opera. +

+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/inputdatepicker.html b/mapbox-gl/closure-library/closure/goog/demos/inputdatepicker.html new file mode 100644 index 0000000000..1e6c25fe01 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/inputdatepicker.html @@ -0,0 +1,60 @@ + + + + + goog.ui.InputDatePicker + + + + + + + + +

goog.ui.InputDatePicker

+ +
+ +
+
+ +
+
+ + +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/inputhandler.html b/mapbox-gl/closure-library/closure/goog/demos/inputhandler.html new file mode 100644 index 0000000000..5559bfc345 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/inputhandler.html @@ -0,0 +1,84 @@ + + + + +goog.events.InputHandler + + + + + +

goog.events.InputHandler

+

+ + +

+ + + +

+ + +

+

+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/jsonprettyprinter.html b/mapbox-gl/closure-library/closure/goog/demos/jsonprettyprinter.html new file mode 100644 index 0000000000..369d7483f3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/jsonprettyprinter.html @@ -0,0 +1,80 @@ + + + + +Demo - goog.format.JsonPrettyPrinter + + + + + + + +Pretty-printed JSON. +
+
+ +Pretty-printed JSON (Formatted using CSS). +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/keyboardshortcuts.html b/mapbox-gl/closure-library/closure/goog/demos/keyboardshortcuts.html new file mode 100644 index 0000000000..2baec7b813 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/keyboardshortcuts.html @@ -0,0 +1,112 @@ + + + + + goog.ui.KeyboardShortcutHandler + + + + + + +

goog.ui.KeyboardShortcutHandler

+
+ + + + + + +
+    Shortcuts:
+      A
+      T E S T
+      Shift+F12
+      Shift+F11 C
+      Ctrl+A
+      G O O G
+      B C
+      B D
+      Alt+Q A
+      Alt+Q Shift+A
+      Alt+Q Shift+B
+      Space
+      Home
+      Enter
+      G S
+      S
+      Meta+y
+  
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/keyhandler.html b/mapbox-gl/closure-library/closure/goog/demos/keyhandler.html new file mode 100644 index 0000000000..b76d7ad6ad --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/keyhandler.html @@ -0,0 +1,118 @@ + + + + +goog.events.KeyHandler + + + + + + +

goog.events.KeyHandler

+

+ +
+
+
+
+
Focusable div
+
+ +
+ No Tab inside this

+ +
+
+
+
Focusable div
+
+ +
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/labelinput.html b/mapbox-gl/closure-library/closure/goog/demos/labelinput.html new file mode 100644 index 0000000000..a760b667f1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/labelinput.html @@ -0,0 +1,42 @@ + + + + + goog.ui.LabelInput + + + + + + +

goog.ui.LabelInput

+

This component decorates an input with default text which disappears upon focus.

+
+ +
+ + +
+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/menu.html b/mapbox-gl/closure-library/closure/goog/demos/menu.html new file mode 100644 index 0000000000..39eafef4ae --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/menu.html @@ -0,0 +1,220 @@ + + + + + goog.ui.Menu + + + + + + + + +

goog.ui.Menu

+
+ This is a very basic menu class, it doesn't handle its display or + dismissal. It just exists, listens to keys and mouse events and can fire + events for selections or highlights. +
+ +
+
+ + + + + + + + + + + + + +
+ + + + + +
+ Here's a menu with checkbox items.
You checked:  + Bold
+ +
+ Here's a BiDi menu with checkbox items.
+ +
+ Here's a menu with an explicit content container.
+ +
+
+
+ +
+ Event Log +
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/menubar.html b/mapbox-gl/closure-library/closure/goog/demos/menubar.html new file mode 100644 index 0000000000..f559ea2518 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/menubar.html @@ -0,0 +1,211 @@ + + + + + goog.ui.menuBar Demo + + + + + + + + + + + + +

goog.ui.menuBar example

+ + + + + + + + + + + +
+
+ + This menu bar was created programmatically: +   + + + + + + + +
+ +
+
+
+
+
+ + This menu bar is decorated: +   + + + +
+ + +
+
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/menubutton.html b/mapbox-gl/closure-library/closure/goog/demos/menubutton.html new file mode 100644 index 0000000000..a15f2ec563 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/menubutton.html @@ -0,0 +1,380 @@ + + + + + goog.ui.MenuButton Demo + + + + + + + + + + + +

goog.ui.MenuButton

+ + + + + + + +
+
+ + These MenuButtons were created programmatically: +   + + + + + + + + +
+ + + Enable first button: + +   + Show second button: + +   +
+ +
+
+
+ + This MenuButton decorates an element:  + + + + + + + + +
+
+ +
+ Format + +
+
Bold
+
Italic
+
Underline
+
+
+ Strikethrough +
+
+
Font...
+
Color...
+
+
+
+ Enable button: + +   + Show button: + +   +
+ +
+
+
+ + This MenuButton accompanies a + CustomButton to form a combo button: +   + +
+ +
+
+
+ + These MenuButtons demonstrate + menu positioning options: +   + +
+ + + + + +
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/menubutton_frame.html b/mapbox-gl/closure-library/closure/goog/demos/menubutton_frame.html new file mode 100644 index 0000000000..7f01363922 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/menubutton_frame.html @@ -0,0 +1,27 @@ + + + + + goog.ui.MenuButton Positioning Frame Demo + + + + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/menuitem.html b/mapbox-gl/closure-library/closure/goog/demos/menuitem.html new file mode 100644 index 0000000000..433d680c75 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/menuitem.html @@ -0,0 +1,164 @@ + + + + + goog.ui.MenuItem Demo + + + + + + + + + + + +

goog.ui.MenuItem

+ + + + + + +
+
+ + Use the first letter of each menuitem to activate:   + + + + + + + +
+ +
+ +
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/mousewheelhandler.html b/mapbox-gl/closure-library/closure/goog/demos/mousewheelhandler.html new file mode 100644 index 0000000000..ebbc6c4212 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/mousewheelhandler.html @@ -0,0 +1,109 @@ + + + + +goog.events.MouseWheelHandler + + + + + + + +

goog.events.MouseWheelHandler

+ +

Use your mousewheel on the gray box below to move the cross hair. + +

+
+
+
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/onlinehandler.html b/mapbox-gl/closure-library/closure/goog/demos/onlinehandler.html new file mode 100644 index 0000000000..21bbcb5c6d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/onlinehandler.html @@ -0,0 +1,79 @@ + + + + + +goog.net.OnlineHandler + + + + + + +

This page reports whether your browser is online or offline. It will detect +changes to the reported state and fire events when this changes. The +OnlineHandler acts as a wrapper around the HTML5 events online and +offline and emulates these for older browsers.

+ +

Try changing File -> Work Offline in your browser.

+ +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/palette.html b/mapbox-gl/closure-library/closure/goog/demos/palette.html new file mode 100644 index 0000000000..2c9ed5e48a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/palette.html @@ -0,0 +1,299 @@ + + + + + goog.ui.Palette & goog.ui.ColorPalette + + + + + + + +

goog.ui.Palette & goog.ui.ColorPalette

+ + + + + + + +
+
+ Demo of the goog.ui.Palette: +
+ + +
+ Note that if you don't specify any dimensions, the palette will auto-size + to fit your items in the smallest square.
+
+
+
+
+ Demo of the goog.ui.ColorPalette: +
+

The color you selected was: + +   + + +

+
+
+
+
+ Demo of the goog.ui.CustomColorPalette: +
+

The color you selected was: + +   + + +

+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/pastehandler.html b/mapbox-gl/closure-library/closure/goog/demos/pastehandler.html new file mode 100644 index 0000000000..ac42f411ce --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/pastehandler.html @@ -0,0 +1,54 @@ + + + + + PasteHandler Test + + + + + +

Demo of goog.events.PasteHandler

+ +
+ Demo of the goog.events.PasteHandler: + + +
+ +
+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/pixeldensitymonitor.html b/mapbox-gl/closure-library/closure/goog/demos/pixeldensitymonitor.html new file mode 100644 index 0000000000..0cbe2a4922 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/pixeldensitymonitor.html @@ -0,0 +1,51 @@ + + + + + goog.labs.style.PixelDensityMonitor + + + + + +

goog.labs.style.PixelDensityMonitor

+
+ Move between high dpi and normal screens to see density change events. +
+
+ Event log +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/plaintextspellchecker.html b/mapbox-gl/closure-library/closure/goog/demos/plaintextspellchecker.html new file mode 100644 index 0000000000..88b23b94ea --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/plaintextspellchecker.html @@ -0,0 +1,118 @@ + + + + +Plain Text Spell Checker + + + + + + +

Plain Text Spell Checker

+

+ The words "test", "words", "a", and "few" are set to be valid words, + all others are considered spelling mistakes. +

+

+ The following keyboard shortcuts can be used to navigate inside the editor: +

    +
  • Previous misspelled word: ctrl + left-arrow
  • +
  • next misspelled word: ctrl + right-arrow
  • +
  • Open suggestions menu: down arrow
  • +
+

+

+ + + + +

+ + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/popup.html b/mapbox-gl/closure-library/closure/goog/demos/popup.html new file mode 100644 index 0000000000..91d286cdd5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/popup.html @@ -0,0 +1,206 @@ + + + + + goog.ui.Popup + + + + + + + +

goog.ui.Popup

+ + +

Positioning relative to an anchor element

+
+ Button Corner + + + + +
+ Popup Corner + + + + + +
+ Margin + Top: + Right: + Bottom: + Left: + + +
+
+
+
+ + + +
+
+ +

Iframe to test cross frame dismissal

+ + +
+
+ +
+

Positioning at coordinates

+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/popupcolorpicker.html b/mapbox-gl/closure-library/closure/goog/demos/popupcolorpicker.html new file mode 100644 index 0000000000..a52d8207e2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/popupcolorpicker.html @@ -0,0 +1,49 @@ + + + + + goog.ui.PopupColorPicker + + + + + + + +

goog.ui.PopupColorPicker

+ Show 1 + Show 2 + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/popupdatepicker.html b/mapbox-gl/closure-library/closure/goog/demos/popupdatepicker.html new file mode 100644 index 0000000000..f83ea400b1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/popupdatepicker.html @@ -0,0 +1,53 @@ + + + + + goog.ui.PopupDatePicker + + + + + + + + +

goog.ui.PopupDatePicker

+ + Show 1 + Show 2 + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/popupemojipicker.html b/mapbox-gl/closure-library/closure/goog/demos/popupemojipicker.html new file mode 100644 index 0000000000..ebf1521281 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/popupemojipicker.html @@ -0,0 +1,408 @@ + + + + + Popup Emoji Picker + + + + + + + + + +

Popup Emoji Picker Demo

+This is a demo of popupemojipickers and docked emoji pickers. Selecting an +emoji inserts a pseudo image tag into the text area with the id of that emoji. + +

Sprited Emojipicker (contains a mix of sprites and non-sprites):

+
+ +

Sprited Progressively-rendered Emojipicker (contains a mix of sprites and + non-sprites):

+
+

Popup Emoji:

+Gimme some emoji +
+ +

Fast-load Progressive Sprited Emojipicker

+
+ +

Fast-load Non-progressive Sprited Emojipicker

+
+ +
+ +

Docked emoji:

+
+ +

Single Page of Emoji

+
+ +

Delayed load popup picker:

+More emoji + +

Delayed load docked picker:

+ + Click to load + +
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/popupmenu.html b/mapbox-gl/closure-library/closure/goog/demos/popupmenu.html new file mode 100644 index 0000000000..92f6a28400 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/popupmenu.html @@ -0,0 +1,115 @@ + + + + + goog.ui.PopupMenu + + + + + + + + + +

goog.ui.PopupMenu

+
+ This shows a 2 popup menus, each menu has been attached to two targets. +

+
+ +
+ Event log +
+
+
+
+ Hello there I'm italic! +
+
+
+ + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/progressbar.html b/mapbox-gl/closure-library/closure/goog/demos/progressbar.html new file mode 100644 index 0000000000..246db3a379 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/progressbar.html @@ -0,0 +1,97 @@ + + + + + goog.ui.ProgressBar + + + + + + +

goog.ui.ProgressBar

+
+
+ +
+
+
+ Decorated element +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/prompt.html b/mapbox-gl/closure-library/closure/goog/demos/prompt.html new file mode 100644 index 0000000000..700dccba0f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/prompt.html @@ -0,0 +1,92 @@ + + + + + goog.ui.Prompt + + + + + + + +

goog.ui.Prompt

+ +

The default text is selected when the prompt displays

+ +

You can use 'Enter' or 'Esc' to click 'Ok' or 'Cancel' respectively

+ +

+ + Prompt + +

+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/quadtree.html b/mapbox-gl/closure-library/closure/goog/demos/quadtree.html new file mode 100644 index 0000000000..e17e6dcf0f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/quadtree.html @@ -0,0 +1,107 @@ + + + + +QuadTree Demo + + + + + +
+
+

Click on the area to the left to add a point to the quadtree, clicking on + a point will remove it from the tree.

+

+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/ratings.html b/mapbox-gl/closure-library/closure/goog/demos/ratings.html new file mode 100644 index 0000000000..c773674380 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/ratings.html @@ -0,0 +1,130 @@ + + + + +Ratings Widget + + + + + + +
+ + +
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/richtextspellchecker.html b/mapbox-gl/closure-library/closure/goog/demos/richtextspellchecker.html new file mode 100644 index 0000000000..e10036ca96 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/richtextspellchecker.html @@ -0,0 +1,101 @@ + + + + + goog.ui.RichTextSpellChecker + + + + + + + +

goog.ui.RichTextSpellChecker

+

+ The words "test", "words", "a", and "few" are set to be valid words, all others are considered spelling mistakes. +

+

+ If keyboard navigation is enabled, then the following shortcuts can be used + inside the editor: +

    +
  • Previous misspelled word: ctrl + left-arrow
  • +
  • next misspelled word: ctrl + right-arrow
  • +
  • Open suggestions menu: down arrow
  • +
+

+

+ +

+ + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/roundedpanel.html b/mapbox-gl/closure-library/closure/goog/demos/roundedpanel.html new file mode 100644 index 0000000000..c11b111ee3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/roundedpanel.html @@ -0,0 +1,139 @@ + + + + + + goog.ui.RoundedPanel Demo + + + + + + + +
+
+
+ Panel Width:
+ +
+
+ Panel Height:
+ +
+
+ Border Width:
+ +
+
+ Border Color:
+ +
+
+ Radius:
+ +
+
+ Background Color:
+ +
+
+ Corners:
+ +
+
Rendering Time:
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.html b/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.html new file mode 100644 index 0000000000..f5a8c51fa8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.html @@ -0,0 +1,75 @@ + + + + + goog.ui.Component + + + + + + + + + + +

goog.ui.Component

+ + +
+

Click on this big, colored box:

+
+ +
+ +
+

Or this box:

+ +
Label from decorated DIV.
+
+ +
+

This box's label keeps changing:

+ +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.js b/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.js new file mode 100644 index 0000000000..d25d77deb8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/samplecomponent.js @@ -0,0 +1,198 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A simple, sample component. + * + */ +goog.provide('goog.demos.SampleComponent'); + +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.KeyHandler'); +goog.require('goog.ui.Component'); + + + +/** + * A simple box that changes colour when clicked. This class demonstrates the + * goog.ui.Component API, and is keyboard accessible, as per + * http://wiki/Main/ClosureKeyboardAccessible + * + * @param {string=} opt_label A label to display. Defaults to "Click Me" if none + * provided. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper to use. + * + * @extends {goog.ui.Component} + * @constructor + * @final + */ +goog.demos.SampleComponent = function(opt_label, opt_domHelper) { + goog.base(this, opt_domHelper); + + /** + * The label to display. + * @type {string} + * @private + */ + this.initialLabel_ = opt_label || 'Click Me'; + + /** + * The current color. + * @type {string} + * @private + */ + this.color_ = 'red'; + + /** + * Keyboard handler for this object. This object is created once the + * component's DOM element is known. + * + * @type {goog.events.KeyHandler?} + * @private + */ + this.kh_ = null; +}; +goog.inherits(goog.demos.SampleComponent, goog.ui.Component); + + +/** + * Changes the color of the element. + * @private + */ +goog.demos.SampleComponent.prototype.changeColor_ = function() { + if (this.color_ == 'red') { + this.color_ = 'green'; + } else if (this.color_ == 'green') { + this.color_ = 'blue'; + } else { + this.color_ = 'red'; + } + this.getElement().style.backgroundColor = this.color_; +}; + + +/** + * Creates an initial DOM representation for the component. + * @override + */ +goog.demos.SampleComponent.prototype.createDom = function() { + this.decorateInternal(this.dom_.createElement('div')); +}; + + +/** + * Decorates an existing HTML DIV element as a SampleComponent. + * + * @param {Element} element The DIV element to decorate. The element's + * text, if any will be used as the component's label. + * @override + */ +goog.demos.SampleComponent.prototype.decorateInternal = function(element) { + goog.base(this, 'decorateInternal', element); + if (!this.getLabelText()) { + this.setLabelText(this.initialLabel_); + } + + var elem = this.getElement(); + goog.dom.classlist.add(elem, goog.getCssName('goog-sample-component')); + elem.style.backgroundColor = this.color_; + elem.tabIndex = 0; + + this.kh_ = new goog.events.KeyHandler(elem); + this.getHandler().listen(this.kh_, goog.events.KeyHandler.EventType.KEY, + this.onKey_); +}; + + +/** @override */ +goog.demos.SampleComponent.prototype.disposeInternal = function() { + goog.base(this, 'disposeInternal'); + if (this.kh_) { + this.kh_.dispose(); + } +}; + + +/** + * Called when component's element is known to be in the document. + * @override + */ +goog.demos.SampleComponent.prototype.enterDocument = function() { + goog.base(this, 'enterDocument'); + this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK, + this.onDivClicked_); +}; + + +/** + * Called when component's element is known to have been removed from the + * document. + * @override + */ +goog.demos.SampleComponent.prototype.exitDocument = function() { + goog.base(this, 'exitDocument'); +}; + + +/** + * Gets the current label text. + * + * @return {string} The current text set into the label, or empty string if + * none set. + */ +goog.demos.SampleComponent.prototype.getLabelText = function() { + if (!this.getElement()) { + return ''; + } + return goog.dom.getTextContent(this.getElement()); +}; + + +/** + * Handles DIV element clicks, causing the DIV's colour to change. + * @param {goog.events.Event} event The click event. + * @private + */ +goog.demos.SampleComponent.prototype.onDivClicked_ = function(event) { + this.changeColor_(); +}; + + +/** + * Fired when user presses a key while the DIV has focus. If the user presses + * space or enter, the color will be changed. + * @param {goog.events.Event} event The key event. + * @private + */ +goog.demos.SampleComponent.prototype.onKey_ = function(event) { + var keyCodes = goog.events.KeyCodes; + if (event.keyCode == keyCodes.SPACE || event.keyCode == keyCodes.ENTER) { + this.changeColor_(); + } +}; + + +/** + * Sets the current label text. Has no effect if component is not rendered. + * + * @param {string} text The text to set as the label. + */ +goog.demos.SampleComponent.prototype.setLabelText = function(text) { + if (this.getElement()) { + goog.dom.setTextContent(this.getElement(), text); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/demos/scrollfloater.html b/mapbox-gl/closure-library/closure/goog/demos/scrollfloater.html new file mode 100644 index 0000000000..f06d21898f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/scrollfloater.html @@ -0,0 +1,138 @@ + + + + + +ScrollFloater + + + + + + + +
+ + + + + + +
+
+ This content does not float. +
+
+ This content does not float. +
+
+ This floater is constrained within a container and has a top offset of 50px. +
+
+
+ This content does not float. +
+
+
+
+
+ This content does not float. +
+ +
+
+ This floater is very tall. +

+ This tall floater is pinned to the bottom of the window when + your window is shorter and floats at the top when it is taller. +

+
+
+
+

This is the bottom of the page.

+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/select.html b/mapbox-gl/closure-library/closure/goog/demos/select.html new file mode 100644 index 0000000000..80f5e0e155 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/select.html @@ -0,0 +1,324 @@ + + + + + goog.ui.Select & goog.ui.Option + + + + + + + + + + + +

goog.ui.Select & goog.ui.Option

+
+ Demo of the goog.ui.Select component: +
+   + +
+
+   + +
+
+  (This control doesn't auto-highlight; it only dispatches + ENTER and LEAVE events.) +
+
+ Click here to add a new option for the best movie, + here + to hide/show the select component for the best movie, or + here + to set the worst movie of all time to "Catwoman." +
+
+
+
+ This goog.ui.Select was decorated: +
+   + +
+
+
+
+
+ +
+ + Demo of goog.ui.Select using + goog.ui.FlatMenuButtonRenderer: + +
+   + +
+
+   + +
+
+  (This control doesn't auto-highlight; it only dispatches + ENTER and LEAVE events.) +
+
+ Click here + to add a new option for the best Arnold movie, + here + to hide/show the select component for the best Arnold movie, or + here + to set the worst Arnold movie to "Jingle All the Way." +
+
+
+
+ This Flat goog.ui.Select was decorated: +
+   + +
+
+
+
+
+ + +
+ Event Log +
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/selectionmenubutton.html b/mapbox-gl/closure-library/closure/goog/demos/selectionmenubutton.html new file mode 100644 index 0000000000..7d9894252c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/selectionmenubutton.html @@ -0,0 +1,186 @@ + + + + + goog.ui.SelectionMenuButton Demo + + + + + + + +

goog.ui.SelectionMenuButton

+ + + + + + + +
+
+ + This SelectionMenuButton was created programmatically: +   + + + + + + + + +
+ + + Enable button: + +   +
+ +
+
+
+ + This SelectionMenuButton decorates an element:  + + + + + + + + +
+
+ + + +
+
All
+
None
+
+
Starred
+
+ Unstarred +
+
+
Read
+
Unread
+
+
+
+ Enable button: + +   + Show button: + +   +
+ +
+
+
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/serverchart.html b/mapbox-gl/closure-library/closure/goog/demos/serverchart.html new file mode 100644 index 0000000000..1684ec2dec --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/serverchart.html @@ -0,0 +1,122 @@ + + + + + goog.ui.ServerChart + + + + + + +

goog.ui.ServerChart

+
+

Line Chart:

+
+
+

Finance Chart: Add a Line +

+
+
+

Pie Chart:

+
+
+

Filled Line Chart:

+
+
+

Bar Chart:

+
+
+

Venn Diagram:

+
+ + diff --git a/mapbox-gl/closure-library/closure/goog/demos/slider.html b/mapbox-gl/closure-library/closure/goog/demos/slider.html new file mode 100644 index 0000000000..1c85e67f76 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/slider.html @@ -0,0 +1,128 @@ + + + + + goog.ui.Slider + + + + + + + +

goog.ui.Slider

+ +
+ Horizontal Slider +
+ +
+
+
+ + MoveToPointEnabled + + Enable +
+ + +
+ +
+ Vertical Slider, inserted w/ script + + + Enable +
+ + +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/splitbehavior.html b/mapbox-gl/closure-library/closure/goog/demos/splitbehavior.html new file mode 100644 index 0000000000..60a9aecccf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/splitbehavior.html @@ -0,0 +1,163 @@ + + + + + goog.ui.SplitBehavior + + + + + + + + + + + + + + + + +

goog.ui.SplitBehavior

+
+ + Split behavior - render + +
+
+
+
+
+ +
+ + Split behavior - decorate + +
+
+ Bold +
+
+
+
+ + Color Split behavior + +
+
+

goog.ui.ColorButton

+
+ + These buttons were rendered using goog.ui.ColorButton: +   + +
+ Rendered ColorButton: +
+
+ Decorated ColorButton: +
+
Color2
+
+
+ + +
+ Event Log +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/splitpane.html b/mapbox-gl/closure-library/closure/goog/demos/splitpane.html new file mode 100644 index 0000000000..366df63839 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/splitpane.html @@ -0,0 +1,243 @@ + + + + + goog.ui.SplitPane + + + + + + + +

goog.ui.SplitPane

+ Left Component Size: + Width: + Height: +
+ First One + Second One + + +

+

+
+ Left Frame +
+
+ +
+
+
+ First Component Width: + +
+ +
+

+ + +

+

+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/stopevent.html b/mapbox-gl/closure-library/closure/goog/demos/stopevent.html new file mode 100644 index 0000000000..83cbd7c85b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/stopevent.html @@ -0,0 +1,171 @@ + + + + Stop Event Propagation + + + + + + + + +

Stop Event

+

Test the cancelling of capture and bubbling events. Click + one of the nodes to see the event trace, then use the check boxes to cancel + the capture or bubble at a given branch. (Double click the text area to clear + it)

+ +
+ +
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/submenus.html b/mapbox-gl/closure-library/closure/goog/demos/submenus.html new file mode 100644 index 0000000000..7f710679fb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/submenus.html @@ -0,0 +1,130 @@ + + + + + goog.ui.SubMenu + + + + + + + + + +

goog.ui.SubMenu

+

Demonstration of different of hierarchical menus.

+

+ +

+ Here's a menu (with submenus) defined in markup: +

+
+
Open...
+
Open Recent +
+
Annual Report.pdf
+
Quarterly Update.pdf
+
Enemies List.txt
+
More +
+
Foo.txt
+
Bar.txt
+
+
+
+
+
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/submenus2.html b/mapbox-gl/closure-library/closure/goog/demos/submenus2.html new file mode 100644 index 0000000000..fd41b20e8c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/submenus2.html @@ -0,0 +1,150 @@ + + + + + goog.ui.SubMenu + + + + + + + + + +

goog.ui.SubMenu

+

Demonstration of different hierarchical menus which share its submenus. + A flyweight pattern demonstration for submenus.

+

+ + +
+
Google
+
Yahoo
+
MSN
+
+
Bla...
+
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tabbar.html b/mapbox-gl/closure-library/closure/goog/demos/tabbar.html new file mode 100644 index 0000000000..845e6bc0a5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tabbar.html @@ -0,0 +1,289 @@ + + + + + goog.ui.TabBar + + + + + + + + + +

goog.ui.TabBar

+

+ A goog.ui.TabBar is a subclass of goog.ui.Container, + designed to host one or more goog.ui.Tabs. The tabs in the + first four tab bars on this demo page were decorated using the default + tab renderer. Tabs in the last two tab bars were decorated using the + rounded tab renderer (goog.ui.RoundedTabRenderer). +

+ + + + + + + + + + + + + + + + + + +
+ Above tab content:

+
+
Hello
+
Settings
+
More
+
Advanced
+
+ +
+
+ Use the keyboard or the mouse to switch tabs. +
+ + +
+ Below tab content:

+
+ Use the keyboard or the mouse to switch tabs. +
+ +
+
+
Hello
+
Settings
+
More
+
Advanced
+
+ +
+ + +
+ Before tab content:

+
+
Hello
+
Settings
+
More
+
Advanced
+
+
+ Use the keyboard or the mouse to switch tabs. +
+ +
+ + +
+ After tab content:

+
+
Hello
+
Settings
+
More
+
Advanced
+
+
+ Use the keyboard or the mouse to switch tabs. +
+ +
+ + +
+ Above tab content (rounded corners):

+
+
Hello
+
Settings +
+
More
+
Advanced +
+
+ +
+
+ Use the keyboard or the mouse to switch tabs. +
+ + +
+ Before tab content (rounded corners):

+
+
Hello
+
Settings
+
More
+
Advanced +
+
+
+ Use the keyboard or the mouse to switch tabs. +
+ +
+ + +
+ +
+ Event Log +
+
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tablesorter.html b/mapbox-gl/closure-library/closure/goog/demos/tablesorter.html new file mode 100644 index 0000000000..dcbcbff14f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tablesorter.html @@ -0,0 +1,116 @@ + + + + + goog.ui.TableSorter + + + + + + + +

goog.ui.TableSorter

+

+ Number sorts numerically, month sorts alphabetically, and days sorts + numerically in reverse. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NumberMonthDays (non-leap year)
1January31
2Februrary28
3March31
4April30
5May31
6June30
7July31
8August31
9September30
10October31
11November30
12December31
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tabpane.html b/mapbox-gl/closure-library/closure/goog/demos/tabpane.html new file mode 100644 index 0000000000..20aaf5d769 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tabpane.html @@ -0,0 +1,302 @@ + + + + + goog.ui.TabPane + + + + + + + +

goog.ui.TabPane

+ +
+ selected in tab pane 1.

+ +

Bottom tabs

+
+
+

Initial page

+

+ Page created automatically from tab pane child node. +

+
+
+ +

Left tabs

+
+
+

Front page!

+

+ Page created automatically from tab pane child node. +

+
+
+ +

Right tabs

+
+
+

Right 1

+

+ Page created automatically from tab pane child node. +

+
+
+

Right 2

+

+ So was this page. +

+
+
+ +
+

Page 1

+

+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis ac augue sed + massa placerat iaculis. Aliquam tempor dictum massa. Quisque vehicula justo + ut tellus. Integer urna. Aliquam libero justo, ornare at, pretium ac, + vulputate quis, ante. Sed arcu. Etiam sit amet turpis. Maecenas pede. Sed + turpis. Sed ultricies commodo nisl. Morbi eget magna quis nisi euismod + porttitor. Vivamus lacinia massa et sem. Donec consequat ligula sed tellus. + Suspendisse enim sapien, vestibulum nec, eleifend id, placerat sit amet, + risus. Mauris in pede ac lorem varius facilisis. Donec dui. Nam mollis nisi + eu neque. Cras luctus nisl at sapien. Ut eleifend, odio id luctus + pellentesque, lorem diam dictum velit, ac gravida lectus magna vel velit. +

+

+ Etiam tempus, ante semper iaculis ultrices, ligula eros lobortis tellus, sit + amet luctus dolor nisl sit amet dolor. Donec in velit. Vivamus facilisis. + Proin nisi felis, commodo ut, porta dignissim, vestibulum quis, ligula. Ut + egestas porttitor tortor. Ut porttitor diam a est. Sed placerat. Aliquam + luctus est a risus. Aenean blandit nibh et justo. Phasellus vel lectus ut + leo dictum consequat. Nam tincidunt facilisis nulla. Nunc nonummy tempus + quam. Aliquam id enim. Sed rhoncus cursus lorem. Curabitur ultricies, enim + quis eleifend mattis, est velit dapibus dolor, quis laoreet arcu tortor + volutpat tortor. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. Curabitur nec mauris et purus aliquam + mattis. Cras rhoncus posuere sapien. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos hymenaeos. +

+

+ Mauris lacinia ornare nunc. Donec molestie. Sed nulla libero, tincidunt vel, + porta sit amet, nonummy eget, augue. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos hymenaeos. Donec ac risus. Cras + euismod congue orci. Mauris mattis, ipsum at vestibulum bibendum, odio est + rhoncus nisi, vel aliquam ante velit quis neque. Duis nonummy tortor id + ante. Aenean auctor odio non nulla. Fusce hendrerit, mi et fringilla + venenatis, sem ipsum fermentum lorem, vel posuere urna eros eget massa. +

+

+ Nulla nec sapien eget mauris pretium tempor. Phasellus scelerisque quam id + mauris. Cras erat ante, pretium ut, vestibulum ac, tincidunt ut, nunc. + Vivamus velit sapien, feugiat ac, elementum ac, viverra non, leo. Phasellus + imperdiet, magna at placerat consectetuer, enim urna aliquam augue, nec + tincidunt justo lectus nec lectus. Nam neque. Nullam ullamcorper euismod + augue. Maecenas arcu purus, sollicitudin nec, consequat a, gravida quis, + massa. Nullam bibendum viverra risus. Sed nibh. Morbi dapibus pellentesque + erat. +

+

+ Cras non tellus. Maecenas nulla est, tincidunt sed, porta sit amet, placerat + sed, diam. Morbi pulvinar. Vestibulum ante ipsum primis in faucibus orci + luctus et ultrices posuere cubilia Curae; Praesent felis lacus, pretium at, + egestas sed, fermentum at, est. Pellentesque sagittis feugiat orci. Nam + augue. Sed eget dolor. Proin vitae metus scelerisque massa fermentum tempus. + Nulla facilisi. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. Aenean eleifend, leo gravida mollis + tempor, tellus ipsum porttitor leo, eget condimentum tellus neque sit amet + orci. Sed non lectus. Suspendisse nonummy purus ac massa. Sed quis elit + dapibus nunc semper porta. Maecenas risus eros, euismod quis, sagittis eget, + aliquet eget, dui. Donec vel nibh. Vivamus nunc purus, euismod in, feugiat + in, sodales vitae, nunc. Nulla lobortis. +

+
+ +
+

+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et nisi id + lorem tempor semper. Suspendisse ante. Integer ligula urna, venenatis quis, + placerat vitae, commodo quis, sapien. Quisque nec lectus. Sed non dolor. Sed + congue, nisi in pharetra consequat, odio diam pulvinar arcu, in laoreet elit + risus id ipsum. Class aptent taciti sociosqu ad litora torquent per conubia + nostra, per inceptos hymenaeos. Praesent tellus enim, imperdiet a, sagittis + id, pulvinar vel, tortor. Integer nulla. Sed nulla augue, lacinia id, + vulputate eu, rhoncus non, ante. Integer lobortis eros vitae quam. Phasellus + sagittis, ipsum sollicitudin bibendum laoreet, arcu erat luctus lacus, vel + pharetra felis metus tincidunt diam. Cras ac augue in enim ultricies + aliquam. +

+ + + +
+ +
+

Page 5

+

+ This is page 5. +

+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/textarea.html b/mapbox-gl/closure-library/closure/goog/demos/textarea.html new file mode 100644 index 0000000000..a71a3c0368 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/textarea.html @@ -0,0 +1,128 @@ + + + + + goog.ui.Textarea + + + + + +

goog.ui.Textarea

+
+ + The first Textarea was created programmatically, + the second by decorating a <textarea> + element:  + +
+ +
+
+ +
+ +
+
+ +
+ + This is a textarea with a minHeight set to 200px. + + +
+ +
+
+ +
+ + This is a textarea with a padding-bottom of 3em. + + +
+ + +
+ Event Log +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/timers.html b/mapbox-gl/closure-library/closure/goog/demos/timers.html new file mode 100644 index 0000000000..193817890b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/timers.html @@ -0,0 +1,291 @@ + + + + + goog.Timer, goog.async.Throttle goog.async.Delay + + + + + + + + +

A Collection of Time Based Utilities

+

goog.async.Delay

+

An action can be invoked after some delay.

+ Delay (seconds): + + +
+ Delay Status: Not Set + +

goog.async.Throttle

+ A throttle prevents the action from being called more than once per time + interval. +
+ 'Create' the Throttle, then hit the 'Do Throttle' button a lot + of times. Notice the number of 'Hits' increasing with each button press. +
+ The action will be invoked no more than once per time interval. +

+ Throttle interval (seconds): + + + +
+ Throttle Hits: +
+ Throttle Action Called: +

+

goog.Timer

+ A timer can be set up to call a timeout function on every 'tick' of the timer. +

+ Timer interval (seconds): + + + + +
+ Timer Status: Not Set +

+

goog.Timer.callOnce

+ Timer also has a useful utility function that can call an action after some + timeout. +
+ This a shortcut/replacement for window.setTimeout, and has a + corresponding goog.Timer.clear as well, which stops the action. +

+ Timeout (seconds): + + +
+ Do Once Status: +

+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/toolbar.html b/mapbox-gl/closure-library/closure/goog/demos/toolbar.html new file mode 100644 index 0000000000..267222e02f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/toolbar.html @@ -0,0 +1,703 @@ + + + + + goog.ui.Toolbar + + + + + + + + + + +

goog.ui.Toolbar

+
+ These toolbars were created programmatically: + +   + +   + +
+
+
+
+
+
+ +   + +   + +
+
+
+
+
+
+
+
+
+ This toolbar was created by decorating a bunch of DIVs: + +   + +   + +
+
+
+
Button
+
+ Fancy Button +
+
+
+ Disabled Button +
+ +
+
+ Toggle Button +
+
+
+
+
+
+
+
+ +   + +   + +
+
+
+
Button
+
+
Fancy Button
+
+
+
+ Disabled Button +
+
+ Menu Button +
+
Foo
+
Bar
+
????... + Ctrl+P
+
???? ?-HTML (????? ZIP)
+
+
Cake
+
+
+
+
+ Toggle Button +
+
+ +
 
+
+
+
+
+
+
+
+
+ This is starting to look like an editor toolbar: + +   + +   + +   + +
+
+
+
+ Select font +
+
Normal
+
Times
+
Courier New
+
Georgia
+
Trebuchet
+
Verdana
+
+
+
+ Size +
+
7pt
+
10pt
+
14pt
+
18pt
+
24pt
+
36pt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Red
+
Green
+
Blue
+
+
+
+
+
+
Red
+
Green
+
Blue
+
+
+
+
Style
+
+
Clear formatting
+
+
Normal paragraph text
+
Minor heading (H3)
+
Sub-heading (H2)
+
Heading (H1)
+
+
Indent more
+
Indent less
+
Blockquote
+
+
+
+
+
+   + Insert +
+
+
Picture
+
Drawing
+
Other...
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Check spelling
+
+
+
+
+
+
+
+ +
+ Event Log +
+ Warning! On Gecko, the event log may cause + the page to flicker when mousing over toolbar items. This is a Gecko + issue triggered by scrolling in the event log; see + bug 756988.
+
+ Enable logging: +
+
+
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tooltip.html b/mapbox-gl/closure-library/closure/goog/demos/tooltip.html new file mode 100644 index 0000000000..7b6f743b4e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tooltip.html @@ -0,0 +1,91 @@ + + + + + goog.ui.Tooltip + + + + + + + +

goog.ui.Tooltip

+

+ + + + +

+ +

+ Demo tooltips in text and and of nested + tooltips, where an element triggers + one tooltip and an element inside the first element triggers another + one. +

+ +
+
+ +
+
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tracer.html b/mapbox-gl/closure-library/closure/goog/demos/tracer.html new file mode 100644 index 0000000000..b145353417 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tracer.html @@ -0,0 +1,92 @@ + + + + +goog.debug.Tracer + + + + + + +
+ +
+ + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tree/demo.html b/mapbox-gl/closure-library/closure/goog/demos/tree/demo.html new file mode 100644 index 0000000000..d4581b473c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tree/demo.html @@ -0,0 +1,126 @@ + + + + + + goog.ui.tree.TreeControl + + + + + + + + +

goog.ui.tree.TreeControl

+
+ +

+ + + + + +

+ + +

+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/tree/testdata.js b/mapbox-gl/closure-library/closure/goog/demos/tree/testdata.js new file mode 100644 index 0000000000..0e578c727a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tree/testdata.js @@ -0,0 +1,260 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +var testData = +['Countries', [['A', [['Afghanistan'], +['Albania'], +['Algeria'], +['American Samoa'], +['Andorra'], +['Angola'], +['Anguilla'], +['Antarctica'], +['Antigua and Barbuda'], +['Argentina'], +['Armenia'], +['Aruba'], +['Australia'], +['Austria'], +['Azerbaijan']]], +['B', [['Bahamas'], +['Bahrain'], +['Bangladesh'], +['Barbados'], +['Belarus'], +['Belgium'], +['Belize'], +['Benin'], +['Bermuda'], +['Bhutan'], +['Bolivia'], +['Bosnia and Herzegovina'], +['Botswana'], +['Bouvet Island'], +['Brazil'], +['British Indian Ocean Territory'], +['Brunei Darussalam'], +['Bulgaria'], +['Burkina Faso'], +['Burundi']]], +['C', [['Cambodia'], +['Cameroon'], +['Canada'], +['Cape Verde'], +['Cayman Islands'], +['Central African Republic'], +['Chad'], +['Chile'], +['China'], +['Christmas Island'], +['Cocos (Keeling) Islands'], +['Colombia'], +['Comoros'], +['Congo'], +['Congo, the Democratic Republic of the'], +['Cook Islands'], +['Costa Rica'], +['Croatia'], +['Cuba'], +['Cyprus'], +['Czech Republic'], +['C\u00f4te d\u2019Ivoire']]], +['D', [['Denmark'], +['Djibouti'], +['Dominica'], +['Dominican Republic']]], +['E', [['Ecuador'], +['Egypt'], +['El Salvador'], +['Equatorial Guinea'], +['Eritrea'], +['Estonia'], +['Ethiopia']]], +['F', [['Falkland Islands (Malvinas)'], +['Faroe Islands'], +['Fiji'], +['Finland'], +['France'], +['French Guiana'], +['French Polynesia'], +['French Southern Territories']]], +['G', [['Gabon'], +['Gambia'], +['Georgia'], +['Germany'], +['Ghana'], +['Gibraltar'], +['Greece'], +['Greenland'], +['Grenada'], +['Guadeloupe'], +['Guam'], +['Guatemala'], +['Guernsey'], +['Guinea'], +['Guinea-Bissau'], +['Guyana']]], +['H', [['Haiti'], +['Heard Island and McDonald Islands'], +['Holy See (Vatican City State)'], +['Honduras'], +['Hong Kong'], +['Hungary']]], +['I', [['Iceland'], +['India'], +['Indonesia'], +['Iran, Islamic Republic of'], +['Iraq'], +['Ireland'], +['Isle of Man'], +['Israel'], +['Italy']]], +['J', [['Jamaica'], +['Japan'], +['Jersey'], +['Jordan']]], +['K', [['Kazakhstan'], +['Kenya'], +['Kiribati'], +['Korea, Democratic People\u2019s Republic of'], +['Korea, Republic of'], +['Kuwait'], +['Kyrgyzstan']]], +['L', [['Lao People\u2019s Democratic Republic'], +['Latvia'], +['Lebanon'], +['Lesotho'], +['Liberia'], +['Libyan Arab Jamahiriya'], +['Liechtenstein'], +['Lithuania'], +['Luxembourg']]], +['M', [['Macao'], +['Macedonia, the former Yugoslav Republic of'], +['Madagascar'], +['Malawi'], +['Malaysia'], +['Maldives'], +['Mali'], +['Malta'], +['Marshall Islands'], +['Martinique'], +['Mauritania'], +['Mauritius'], +['Mayotte'], +['Mexico'], +['Micronesia, Federated States of'], +['Moldova, Republic of'], +['Monaco'], +['Mongolia'], +['Montenegro'], +['Montserrat'], +['Morocco'], +['Mozambique'], +['Myanmar']]], +['N', [['Namibia'], +['Nauru'], +['Nepal'], +['Netherlands'], +['Netherlands Antilles'], +['New Caledonia'], +['New Zealand'], +['Nicaragua'], +['Niger'], +['Nigeria'], +['Niue'], +['Norfolk Island'], +['Northern Mariana Islands'], +['Norway']]], +['O', [['Oman']]], +['P', [['Pakistan'], +['Palau'], +['Palestinian Territory, Occupied'], +['Panama'], +['Papua New Guinea'], +['Paraguay'], +['Peru'], +['Philippines'], +['Pitcairn'], +['Poland'], +['Portugal'], +['Puerto Rico']]], +['Q', [['Qatar']]], +['R', [['Romania'], +['Russian Federation'], +['Rwanda'], +['R\u00e9union']]], +['S', [['Saint Barth\u00e9lemy'], +['Saint Helena'], +['Saint Kitts and Nevis'], +['Saint Lucia'], +['Saint Martin (French part)'], +['Saint Pierre and Miquelon'], +['Saint Vincent and the Grenadines'], +['Samoa'], +['San Marino'], +['Sao Tome and Principe'], +['Saudi Arabia'], +['Senegal'], +['Serbia'], +['Seychelles'], +['Sierra Leone'], +['Singapore'], +['Slovakia'], +['Slovenia'], +['Solomon Islands'], +['Somalia'], +['South Africa'], +['South Georgia and the South Sandwich Islands'], +['Spain'], +['Sri Lanka'], +['Sudan'], +['Suriname'], +['Svalbard and Jan Mayen'], +['Swaziland'], +['Sweden'], +['Switzerland'], +['Syrian Arab Republic']]], +['T', [['Taiwan, Province of China'], +['Tajikistan'], +['Tanzania, United Republic of'], +['Thailand'], +['Timor-Leste'], +['Togo'], +['Tokelau'], +['Tonga'], +['Trinidad and Tobago'], +['Tunisia'], +['Turkey'], +['Turkmenistan'], +['Turks and Caicos Islands'], +['Tuvalu']]], +['U', [['Uganda'], +['Ukraine'], +['United Arab Emirates'], +['United Kingdom'], +['United States'], +['United States Minor Outlying Islands'], +['Uruguay'], +['Uzbekistan']]], +['V', [['Vanuatu'], +['Venezuela'], +['Viet Nam'], +['Virgin Islands, British'], +['Virgin Islands, U.S.']]], +['W', [['Wallis and Futuna'], +['Western Sahara']]], +['Y', [['Yemen']]], +['Z', [['Zambia'], +['Zimbabwe']]], +['\u00c5', [['\u00c5land Islands']]]]] diff --git a/mapbox-gl/closure-library/closure/goog/demos/tweakui.html b/mapbox-gl/closure-library/closure/goog/demos/tweakui.html new file mode 100644 index 0000000000..6a88e9331a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/tweakui.html @@ -0,0 +1,121 @@ + + + + + goog.tweak.TweakUi + + + + + +

goog.ui.TweakUi

+The goog.tweak package provides a convenient and flexible way to add +configurable settings to an app. These settings: +
    +
  • can be set at compile time +
  • can be set in code (using goog.tweak.overrideDefaultValue) +
  • can be set by query parameters +
  • can be set through the TweakUi interface +
+Tweaks IDs are checked by the compiler and tweaks can be fully removed when +tweakProcessing=STRIP. Tweaks are great for toggling debugging facilities. + +

A collapsible menu

+

An expanded menu

+
    +
  • When "Apply Tweaks" is clicked, all non-default values are encoded into + query parameters and the page is refreshed. +
  • Blue entries are ones where the value of the tweak will change without + clicking apply tweaks (the value of goog.tweak.get*() will change) +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/twothumbslider.html b/mapbox-gl/closure-library/closure/goog/demos/twothumbslider.html new file mode 100644 index 0000000000..6c1d113ead --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/twothumbslider.html @@ -0,0 +1,121 @@ + + + + + goog.ui.TwoThumbSlider + + + + + +

goog.ui.TwoThumbSlider

+
+
+ +
+
+
+
+ +
+ +
+ + +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/useragent.html b/mapbox-gl/closure-library/closure/goog/demos/useragent.html new file mode 100644 index 0000000000..326841402e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/useragent.html @@ -0,0 +1,214 @@ + + + + + goog.userAgent + + + + + + + + +

goog.userAgent

+ +
+ + + +
+
+ +
+ + + +
+
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/viewportsizemonitor.html b/mapbox-gl/closure-library/closure/goog/demos/viewportsizemonitor.html new file mode 100644 index 0000000000..f958f5dac5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/viewportsizemonitor.html @@ -0,0 +1,71 @@ + + + + + goog.dom.ViewportSizeMonitor + + + + + + +

goog.dom.ViewportSizeMonitor

+
+ Current Size: Loading... +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/wheelhandler.html b/mapbox-gl/closure-library/closure/goog/demos/wheelhandler.html new file mode 100644 index 0000000000..586351a025 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/wheelhandler.html @@ -0,0 +1,144 @@ + + + + +goog.events.WheelHandler + + + + + + + +

goog.events.WheelHandler

+ +

Use your mousewheel on the gray box below to move the cross hair. + +

+
+
+
+
+ +
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/blank.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/blank.html new file mode 100644 index 0000000000..5108baa3df --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/blank.html @@ -0,0 +1,7 @@ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/index.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/index.html new file mode 100644 index 0000000000..e516ef26e6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/index.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + +
+ + +

+this page: +

+ +

See CrossPageChannel and +CrossDomainCommunication for details

+ +

+select transport:
+Auto | +Native messaging | +Frame element method | +Iframe relay | +Iframe polling | + +Fragment URL +

+ +

+
+
+ + + +

+Out [clear]:
+
+ + + +
+ + +
+ + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/inner.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/inner.html new file mode 100644 index 0000000000..02e17885c2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/inner.html @@ -0,0 +1,58 @@ + + + + + + + + + + + +

this page:

+ + +
+ +(n = )
+ +mousemove-forwarding: + + +
Click me!
+
+ + + +
+
+ + + + +

+Out [clear]:
+
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/blank.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/blank.html new file mode 100644 index 0000000000..5108baa3df --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/blank.html @@ -0,0 +1,7 @@ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/index.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/index.html new file mode 100644 index 0000000000..75dfb36efd --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/index.html @@ -0,0 +1,105 @@ + + + + + + + + + + + +
+ +

+ +

+ + +

+ +
+
+ +
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/inner.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/inner.html new file mode 100644 index 0000000000..b35773ae92 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/inner.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + +

+ +

+ + +

+ +
+ + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/relay.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/relay.html new file mode 100644 index 0000000000..7cc700206a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/minimal/relay.html @@ -0,0 +1,7 @@ + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/relay.html b/mapbox-gl/closure-library/closure/goog/demos/xpc/relay.html new file mode 100644 index 0000000000..d2943357c0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/relay.html @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/demos/xpc/xpcdemo.js b/mapbox-gl/closure-library/closure/goog/demos/xpc/xpcdemo.js new file mode 100644 index 0000000000..e3f722f31b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/xpc/xpcdemo.js @@ -0,0 +1,307 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Contains application code for the XPC demo. + * This script is used in both the container page and the iframe. + * + */ + +goog.require('goog.Uri'); +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.json'); +goog.require('goog.log'); +goog.require('goog.net.xpc.CfgFields'); +goog.require('goog.net.xpc.CrossPageChannel'); + + + +/** + * Namespace for the demo. We don't use goog.provide here because it's not a + * real module (cannot be required). + */ +var xpcdemo = {}; + + +/** + * Global function to kick off initialization in the containing document. + */ +goog.global.initOuter = function() { + goog.events.listen(window, 'load', function() { xpcdemo.initOuter(); }); +}; + + +/** + * Global function to kick off initialization in the iframe. + */ +goog.global.initInner = function() { + goog.events.listen(window, 'load', function() { xpcdemo.initInner(); }); +}; + + +/** + * Initializes XPC in the containing page. + */ +xpcdemo.initOuter = function() { + // Build the configuration object. + var cfg = {}; + + var ownUri = new goog.Uri(window.location.href); + var relayUri = ownUri.resolve(new goog.Uri('relay.html')); + var pollUri = ownUri.resolve(new goog.Uri('blank.html')); + + // Determine the peer domain. Uses the value of the URI-parameter + // 'peerdomain'. If that parameter is not present, it falls back to + // the own domain so that the demo will work out of the box (but + // communication will of course not cross domain-boundaries). For + // real cross-domain communication, the easiest way is to point two + // different host-names to the same webserver and then hit the + // following URI: + // http://host1.com/path/to/closure/demos/xpc/index.html?peerdomain=host2.com + var peerDomain = ownUri.getParameterValue('peerdomain') || ownUri.getDomain(); + + cfg[goog.net.xpc.CfgFields.LOCAL_RELAY_URI] = relayUri.toString(); + cfg[goog.net.xpc.CfgFields.PEER_RELAY_URI] = + relayUri.setDomain(peerDomain).toString(); + + cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] = pollUri.toString(); + cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] = + pollUri.setDomain(peerDomain).toString(); + + + // Force transport to be used if tp-parameter is set. + var tp = ownUri.getParameterValue('tp'); + if (tp) { + cfg[goog.net.xpc.CfgFields.TRANSPORT] = parseInt(tp, 10); + } + + + // Construct the URI of the peer page. + + var peerUri = ownUri.resolve( + new goog.Uri('inner.html')).setDomain(peerDomain); + // Passthrough of verbose and compiled flags. + if (goog.isDef(ownUri.getParameterValue('verbose'))) { + peerUri.setParameterValue('verbose', ''); + } + if (goog.isDef(ownUri.getParameterValue('compiled'))) { + peerUri.setParameterValue('compiled', ''); + } + + cfg[goog.net.xpc.CfgFields.PEER_URI] = peerUri; + + // Instantiate the channel. + xpcdemo.channel = new goog.net.xpc.CrossPageChannel(cfg); + + // Create the peer iframe. + xpcdemo.peerIframe = xpcdemo.channel.createPeerIframe( + goog.dom.getElement('iframeContainer')); + + xpcdemo.initCommon_(); + + goog.dom.getElement('inactive').style.display = 'none'; + goog.dom.getElement('active').style.display = ''; +}; + + +/** + * Initialization in the iframe. + */ +xpcdemo.initInner = function() { + // Get the channel configuration passed by the containing document. + var cfg = goog.json.parse( + (new goog.Uri(window.location.href)).getParameterValue('xpc')); + + xpcdemo.channel = new goog.net.xpc.CrossPageChannel(cfg); + + xpcdemo.initCommon_(); +}; + + +/** + * Initializes the demo. + * Registers service-handlers and connects the channel. + * @private + */ +xpcdemo.initCommon_ = function() { + var xpcLogger = goog.log.getLogger('goog.net.xpc'); + goog.log.addHandler(xpcLogger, function(logRecord) { + xpcdemo.log('[XPC] ' + logRecord.getMessage()); + }); + xpcLogger.setLevel(window.location.href.match(/verbose/) ? + goog.log.Level.ALL : goog.log.Level.INFO); + + // Register services. + xpcdemo.channel.registerService('log', xpcdemo.log); + xpcdemo.channel.registerService('ping', xpcdemo.pingHandler_); + xpcdemo.channel.registerService('events', xpcdemo.eventsMsgHandler_); + + // Connect the channel. + xpcdemo.channel.connect(function() { + xpcdemo.channel.send('log', 'Hi from ' + window.location.host); + goog.events.listen(goog.dom.getElement('clickfwd'), + 'click', xpcdemo.mouseEventHandler_); + }); +}; + + +/** + * Kills the peer iframe and the disposes the channel. + */ +xpcdemo.teardown = function() { + goog.events.unlisten(goog.dom.getElement('clickfwd'), + goog.events.EventType.CLICK, xpcdemo.mouseEventHandler_); + + xpcdemo.channel.dispose(); + delete xpcdemo.channel; + + goog.dom.removeNode(xpcdemo.peerIframe); + xpcdemo.peerIframe = null; + + goog.dom.getElement('inactive').style.display = ''; + goog.dom.getElement('active').style.display = 'none'; +}; + + +/** + * Logging function. Inserts log-message into element with it id 'console'. + * @param {string} msgString The log-message. + */ +xpcdemo.log = function(msgString) { + xpcdemo.consoleElm || (xpcdemo.consoleElm = goog.dom.getElement('console')); + var msgElm = goog.dom.createDom('div'); + msgElm.innerHTML = msgString; + xpcdemo.consoleElm.insertBefore(msgElm, xpcdemo.consoleElm.firstChild); +}; + + +/** + * Sends a ping request to the peer. + */ +xpcdemo.ping = function() { + // send current time + xpcdemo.channel.send('ping', goog.now() + ''); +}; + + +/** + * The handler function for incoming pings (messages sent to the service + * called 'ping'); + * @param {string} payload The message payload. + * @private + */ +xpcdemo.pingHandler_ = function(payload) { + // is the incoming message a response to a ping we sent? + if (payload.charAt(0) == '#') { + // calculate roundtrip time and log + var dt = goog.now() - parseInt(payload.substring(1), 10); + xpcdemo.log('roundtrip: ' + dt + 'ms'); + } else { + // incoming message is a ping initiated from peer + // -> prepend with '#' and send back + xpcdemo.channel.send('ping', '#' + payload); + xpcdemo.log('ping reply sent'); + } +}; + + +/** + * Counter for mousemove events. + * @type {number} + * @private + */ +xpcdemo.mmCount_ = 0; + + +/** + * Holds timestamp when the last mousemove rate has been logged. + * @type {number} + * @private + */ +xpcdemo.mmLastRateOutput_ = 0; + + +/** + * Start mousemove event forwarding. Registers a listener on the document which + * sends them over the channel. + */ +xpcdemo.startMousemoveForwarding = function() { + goog.events.listen(document, goog.events.EventType.MOUSEMOVE, + xpcdemo.mouseEventHandler_); + xpcdemo.mmLastRateOutput_ = goog.now(); +}; + + +/** + * Stop mousemove event forwarding. + */ +xpcdemo.stopMousemoveForwarding = function() { + goog.events.unlisten(document, goog.events.EventType.MOUSEMOVE, + xpcdemo.mouseEventHandler_); +}; + + +/** + * Function to be used as handler for mouse-events. + * @param {goog.events.BrowserEvent} e The mouse event. + * @private + */ +xpcdemo.mouseEventHandler_ = function(e) { + xpcdemo.channel.send('events', + [e.type, e.clientX, e.clientY, goog.now()].join(',')); +}; + + +/** + * Handler for the 'events' service. + * @param {string} payload The string returned from the xpcdemo. + * @private + */ +xpcdemo.eventsMsgHandler_ = function(payload) { + var now = goog.now(); + var args = payload.split(','); + var type = args[0]; + var pageX = args[1]; + var pageY = args[2]; + var time = parseInt(args[3], 10); + + var msg = type + ': (' + pageX + ',' + pageY + '), latency: ' + (now - time); + xpcdemo.log(msg); + + if (type == goog.events.EventType.MOUSEMOVE) { + xpcdemo.mmCount_++; + var dt = now - xpcdemo.mmLastRateOutput_; + if (dt > 1000) { + msg = 'RATE (mousemove/s): ' + (1000 * xpcdemo.mmCount_ / dt); + xpcdemo.log(msg); + xpcdemo.mmLastRateOutput_ = now; + xpcdemo.mmCount_ = 0; + } + } +}; + + +/** + * Send multiple messages. + * @param {number} n The number of messages to send. + */ +xpcdemo.sendN = function(n) { + xpcdemo.count_ || (xpcdemo.count_ = 1); + + for (var i = 0; i < n; i++) { + xpcdemo.channel.send('log', '' + xpcdemo.count_++); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/demos/zippy.html b/mapbox-gl/closure-library/closure/goog/demos/zippy.html new file mode 100644 index 0000000000..ae007c4624 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/demos/zippy.html @@ -0,0 +1,149 @@ + + + + + goog.ui.Zippy + + + + + + + +

goog.ui.Zippy

+ +

Zippy 1

+

+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et nisi id + lorem tempor semper. Suspendisse ante. Integer ligula urna, venenatis quis, + placerat vitae, commodo quis, sapien. Quisque nec lectus. Sed non dolor. Sed + congue, nisi in pharetra consequat, odio diam pulvinar arcu, in laoreet elit + risus id ipsum. Class aptent taciti sociosqu ad litora torquent per conubia + nostra, per inceptos hymenaeos. Praesent tellus enim, imperdiet a, sagittis + id, pulvinar vel, tortor. Integer nulla. Sed nulla augue, lacinia id, + vulputate eu, rhoncus non, ante. Integer lobortis eros vitae quam. Phasellus + sagittis, ipsum sollicitudin bibendum laoreet, arcu erat luctus lacus, vel + pharetra felis metus tincidunt diam. Cras ac augue in enim ultricies aliquam. +

+ +
+

Zippy 2

+

+ Nunc et eros. Aliquam felis lectus, sagittis ac, sagittis eu, accumsan + vitae, leo. Maecenas suscipit, arcu eget elementum tincidunt, erat ligula + porttitor dui, quis ornare nisi turpis at ipsum. Vivamus magna tortor, + porttitor eu, cursus ut, vulputate in, nulla. Quisque nonummy feugiat + turpis. Cras lobortis lobortis elit. Aliquam congue, pede suscipit + condimentum convallis, diam purus dictum lacus, eu scelerisque mi est + molestie libero. Duis luctus dapibus nibh. Sed condimentum iaculis metus. + Pellentesque habitant morbi tristique senectus et netus et malesuada fames + ac turpis egestas. In pharetra dolor porta eros facilisis pellentesque. + Proin quam mi, sodales vel, tincidunt sit amet, convallis vel, eros. + Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere + cubilia Curae; Phasellus velit augue, rutrum sit amet, posuere nec, euismod + ac, elit. Etiam nisi. +

+
+ +
+

Zippy 3

+

+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas commodo + convallis nisi. Cras rhoncus elit non dolor. Vivamus gravida ultricies arcu. + Praesent ipsum erat, vehicula et, ultrices at, dignissim at, ipsum. Aenean + venenatis. Fusce blandit laoreet urna. Aliquam et pede condimentum lorem + posuere molestie. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. Fusce euismod, justo in feugiat feugiat, + urna metus sagittis felis, in varius neque mauris vitae dui. Nunc vel sapien + in diam laoreet euismod. Mauris quis felis ut ipsum auctor feugiat. Nulla + facilisi. Proin vitae urna. Quisque dignissim commodo nisl. Curabitur + bibendum. +

+
+ +
+ Zippy 2 sets the expanded state of zippy 3 to the inverted expanded state of + itself. Hence expanding zippy 2 collapses zippy 3 and vice verse. +
+
+ Zippy 2 and 3 are animated, zippy 1 is not. +
+ +
+
+ + + diff --git a/mapbox-gl/closure-library/closure/goog/deps.js b/mapbox-gl/closure-library/closure/goog/deps.js new file mode 100644 index 0000000000..399048201d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/deps.js @@ -0,0 +1,1457 @@ +// Copyright 2015 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file has been auto-generated by GenJsDeps, please do not edit. + +goog.addDependency('../../third_party/closure/goog/caja/string/html/htmlparser.js', ['goog.string.html.HtmlParser', 'goog.string.html.HtmlParser.EFlags', 'goog.string.html.HtmlParser.Elements', 'goog.string.html.HtmlParser.Entities', 'goog.string.html.HtmlSaxHandler'], [], false); +goog.addDependency('../../third_party/closure/goog/caja/string/html/htmlsanitizer.js', ['goog.string.html.HtmlSanitizer', 'goog.string.html.HtmlSanitizer.AttributeType', 'goog.string.html.HtmlSanitizer.Attributes', 'goog.string.html.htmlSanitize'], ['goog.string.StringBuffer', 'goog.string.html.HtmlParser', 'goog.string.html.HtmlParser.EFlags', 'goog.string.html.HtmlParser.Elements', 'goog.string.html.HtmlSaxHandler'], false); +goog.addDependency('../../third_party/closure/goog/dojo/dom/query.js', ['goog.dom.query'], ['goog.array', 'goog.dom', 'goog.functions', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('../../third_party/closure/goog/jpeg_encoder/jpeg_encoder_basic.js', ['goog.crypt.JpegEncoder'], ['goog.crypt.base64'], false); +goog.addDependency('../../third_party/closure/goog/loremipsum/text/loremipsum.js', ['goog.text.LoremIpsum'], ['goog.array', 'goog.math', 'goog.string', 'goog.structs.Map', 'goog.structs.Set'], false); +goog.addDependency('../../third_party/closure/goog/mochikit/async/deferred.js', ['goog.async.Deferred', 'goog.async.Deferred.AlreadyCalledError', 'goog.async.Deferred.CanceledError'], ['goog.Promise', 'goog.Thenable', 'goog.array', 'goog.asserts', 'goog.debug.Error'], false); +goog.addDependency('../../third_party/closure/goog/mochikit/async/deferredlist.js', ['goog.async.DeferredList'], ['goog.async.Deferred'], false); +goog.addDependency('../../third_party/closure/goog/osapi/osapi.js', ['goog.osapi'], [], false); +goog.addDependency('../../third_party/closure/goog/svgpan/svgpan.js', ['svgpan.SvgPan'], ['goog.Disposable', 'goog.events', 'goog.events.EventType', 'goog.events.MouseWheelHandler'], false); +goog.addDependency('a11y/aria/announcer.js', ['goog.a11y.aria.Announcer'], ['goog.Disposable', 'goog.a11y.aria', 'goog.a11y.aria.LivePriority', 'goog.a11y.aria.State', 'goog.dom', 'goog.object'], false); +goog.addDependency('a11y/aria/announcer_test.js', ['goog.a11y.aria.AnnouncerTest'], ['goog.a11y.aria', 'goog.a11y.aria.Announcer', 'goog.a11y.aria.LivePriority', 'goog.a11y.aria.State', 'goog.array', 'goog.dom', 'goog.dom.iframe', 'goog.testing.jsunit'], false); +goog.addDependency('a11y/aria/aria.js', ['goog.a11y.aria'], ['goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.a11y.aria.datatables', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.object', 'goog.string'], false); +goog.addDependency('a11y/aria/aria_test.js', ['goog.a11y.ariaTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.TagName', 'goog.testing.jsunit'], false); +goog.addDependency('a11y/aria/attributes.js', ['goog.a11y.aria.AutoCompleteValues', 'goog.a11y.aria.CheckedValues', 'goog.a11y.aria.DropEffectValues', 'goog.a11y.aria.ExpandedValues', 'goog.a11y.aria.GrabbedValues', 'goog.a11y.aria.InvalidValues', 'goog.a11y.aria.LivePriority', 'goog.a11y.aria.OrientationValues', 'goog.a11y.aria.PressedValues', 'goog.a11y.aria.RelevantValues', 'goog.a11y.aria.SelectedValues', 'goog.a11y.aria.SortValues', 'goog.a11y.aria.State'], [], false); +goog.addDependency('a11y/aria/datatables.js', ['goog.a11y.aria.datatables'], ['goog.a11y.aria.State', 'goog.object'], false); +goog.addDependency('a11y/aria/roles.js', ['goog.a11y.aria.Role'], [], false); +goog.addDependency('array/array.js', ['goog.array', 'goog.array.ArrayLike'], ['goog.asserts'], false); +goog.addDependency('array/array_test.js', ['goog.arrayTest'], ['goog.array', 'goog.dom', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('asserts/asserts.js', ['goog.asserts', 'goog.asserts.AssertionError'], ['goog.debug.Error', 'goog.dom.NodeType', 'goog.string'], false); +goog.addDependency('asserts/asserts_test.js', ['goog.assertsTest'], ['goog.asserts', 'goog.asserts.AssertionError', 'goog.dom', 'goog.string', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('async/animationdelay.js', ['goog.async.AnimationDelay'], ['goog.Disposable', 'goog.events', 'goog.functions'], false); +goog.addDependency('async/animationdelay_test.js', ['goog.async.AnimationDelayTest'], ['goog.async.AnimationDelay', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('async/conditionaldelay.js', ['goog.async.ConditionalDelay'], ['goog.Disposable', 'goog.async.Delay'], false); +goog.addDependency('async/conditionaldelay_test.js', ['goog.async.ConditionalDelayTest'], ['goog.async.ConditionalDelay', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('async/delay.js', ['goog.Delay', 'goog.async.Delay'], ['goog.Disposable', 'goog.Timer'], false); +goog.addDependency('async/delay_test.js', ['goog.async.DelayTest'], ['goog.async.Delay', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('async/nexttick.js', ['goog.async.nextTick', 'goog.async.throwException'], ['goog.debug.entryPointRegistry', 'goog.functions', 'goog.labs.userAgent.browser'], false); +goog.addDependency('async/nexttick_test.js', ['goog.async.nextTickTest'], ['goog.async.nextTick', 'goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.dom', 'goog.labs.userAgent.browser', 'goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('async/run.js', ['goog.async.run'], ['goog.async.nextTick', 'goog.async.throwException', 'goog.testing.watchers'], false); +goog.addDependency('async/run_test.js', ['goog.async.runTest'], ['goog.async.run', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('async/throttle.js', ['goog.Throttle', 'goog.async.Throttle'], ['goog.Disposable', 'goog.Timer'], false); +goog.addDependency('async/throttle_test.js', ['goog.async.ThrottleTest'], ['goog.async.Throttle', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('base.js', ['goog'], [], false); +goog.addDependency('base_module_test.js', ['goog.baseModuleTest'], ['goog.Timer', 'goog.test_module', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], true); +goog.addDependency('base_test.js', ['an.existing.path', 'dup.base', 'far.out', 'goog.baseTest', 'goog.explicit', 'goog.implicit.explicit', 'goog.test', 'goog.test.name', 'goog.test.name.space', 'goog.xy', 'goog.xy.z', 'ns', 'testDep.bar'], ['goog.Timer', 'goog.functions', 'goog.test_module', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('color/alpha.js', ['goog.color.alpha'], ['goog.color'], false); +goog.addDependency('color/alpha_test.js', ['goog.color.alphaTest'], ['goog.array', 'goog.color', 'goog.color.alpha', 'goog.testing.jsunit'], false); +goog.addDependency('color/color.js', ['goog.color', 'goog.color.Hsl', 'goog.color.Hsv', 'goog.color.Rgb'], ['goog.color.names', 'goog.math'], false); +goog.addDependency('color/color_test.js', ['goog.colorTest'], ['goog.array', 'goog.color', 'goog.color.names', 'goog.testing.jsunit'], false); +goog.addDependency('color/names.js', ['goog.color.names'], [], false); +goog.addDependency('crypt/aes.js', ['goog.crypt.Aes'], ['goog.asserts', 'goog.crypt.BlockCipher'], false); +goog.addDependency('crypt/aes_test.js', ['goog.crypt.AesTest'], ['goog.crypt', 'goog.crypt.Aes', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/arc4.js', ['goog.crypt.Arc4'], ['goog.asserts'], false); +goog.addDependency('crypt/arc4_test.js', ['goog.crypt.Arc4Test'], ['goog.array', 'goog.crypt.Arc4', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/base64.js', ['goog.crypt.base64'], ['goog.crypt', 'goog.userAgent'], false); +goog.addDependency('crypt/base64_test.js', ['goog.crypt.base64Test'], ['goog.crypt', 'goog.crypt.base64', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/basen.js', ['goog.crypt.baseN'], [], false); +goog.addDependency('crypt/basen_test.js', ['goog.crypt.baseNTest'], ['goog.crypt.baseN', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/blobhasher.js', ['goog.crypt.BlobHasher', 'goog.crypt.BlobHasher.EventType'], ['goog.asserts', 'goog.events.EventTarget', 'goog.fs', 'goog.log'], false); +goog.addDependency('crypt/blobhasher_test.js', ['goog.crypt.BlobHasherTest'], ['goog.crypt', 'goog.crypt.BlobHasher', 'goog.crypt.Md5', 'goog.events', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/blockcipher.js', ['goog.crypt.BlockCipher'], [], false); +goog.addDependency('crypt/bytestring_perf.js', ['goog.crypt.byteArrayToStringPerf'], ['goog.array', 'goog.dom', 'goog.testing.PerformanceTable'], false); +goog.addDependency('crypt/cbc.js', ['goog.crypt.Cbc'], ['goog.array', 'goog.asserts', 'goog.crypt'], false); +goog.addDependency('crypt/cbc_test.js', ['goog.crypt.CbcTest'], ['goog.crypt', 'goog.crypt.Aes', 'goog.crypt.Cbc', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/crypt.js', ['goog.crypt'], ['goog.array', 'goog.asserts'], false); +goog.addDependency('crypt/crypt_test.js', ['goog.cryptTest'], ['goog.crypt', 'goog.string', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/hash.js', ['goog.crypt.Hash'], [], false); +goog.addDependency('crypt/hash32.js', ['goog.crypt.hash32'], ['goog.crypt'], false); +goog.addDependency('crypt/hash32_test.js', ['goog.crypt.hash32Test'], ['goog.crypt.hash32', 'goog.testing.TestCase', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/hashtester.js', ['goog.crypt.hashTester'], ['goog.array', 'goog.crypt', 'goog.dom', 'goog.testing.PerformanceTable', 'goog.testing.PseudoRandom', 'goog.testing.asserts'], false); +goog.addDependency('crypt/hmac.js', ['goog.crypt.Hmac'], ['goog.crypt.Hash'], false); +goog.addDependency('crypt/hmac_test.js', ['goog.crypt.HmacTest'], ['goog.crypt.Hmac', 'goog.crypt.Sha1', 'goog.crypt.hashTester', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/md5.js', ['goog.crypt.Md5'], ['goog.crypt.Hash'], false); +goog.addDependency('crypt/md5_test.js', ['goog.crypt.Md5Test'], ['goog.crypt', 'goog.crypt.Md5', 'goog.crypt.hashTester', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/pbkdf2.js', ['goog.crypt.pbkdf2'], ['goog.array', 'goog.asserts', 'goog.crypt', 'goog.crypt.Hmac', 'goog.crypt.Sha1'], false); +goog.addDependency('crypt/pbkdf2_test.js', ['goog.crypt.pbkdf2Test'], ['goog.crypt', 'goog.crypt.pbkdf2', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('crypt/sha1.js', ['goog.crypt.Sha1'], ['goog.crypt.Hash'], false); +goog.addDependency('crypt/sha1_test.js', ['goog.crypt.Sha1Test'], ['goog.crypt', 'goog.crypt.Sha1', 'goog.crypt.hashTester', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('crypt/sha2.js', ['goog.crypt.Sha2'], ['goog.array', 'goog.asserts', 'goog.crypt.Hash'], false); +goog.addDependency('crypt/sha224.js', ['goog.crypt.Sha224'], ['goog.crypt.Sha2'], false); +goog.addDependency('crypt/sha224_test.js', ['goog.crypt.Sha224Test'], ['goog.crypt', 'goog.crypt.Sha224', 'goog.crypt.hashTester', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/sha256.js', ['goog.crypt.Sha256'], ['goog.crypt.Sha2'], false); +goog.addDependency('crypt/sha256_test.js', ['goog.crypt.Sha256Test'], ['goog.crypt', 'goog.crypt.Sha256', 'goog.crypt.hashTester', 'goog.testing.jsunit'], false); +goog.addDependency('crypt/sha2_64bit.js', ['goog.crypt.Sha2_64bit'], ['goog.array', 'goog.asserts', 'goog.crypt.Hash', 'goog.math.Long'], false); +goog.addDependency('crypt/sha2_64bit_test.js', ['goog.crypt.Sha2_64bit_test'], ['goog.array', 'goog.crypt', 'goog.crypt.Sha384', 'goog.crypt.Sha512', 'goog.crypt.Sha512_256', 'goog.crypt.hashTester', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('crypt/sha384.js', ['goog.crypt.Sha384'], ['goog.crypt.Sha2_64bit'], false); +goog.addDependency('crypt/sha512.js', ['goog.crypt.Sha512'], ['goog.crypt.Sha2_64bit'], false); +goog.addDependency('crypt/sha512_256.js', ['goog.crypt.Sha512_256'], ['goog.crypt.Sha2_64bit'], false); +goog.addDependency('cssom/cssom.js', ['goog.cssom', 'goog.cssom.CssRuleType'], ['goog.array', 'goog.dom'], false); +goog.addDependency('cssom/cssom_test.js', ['goog.cssomTest'], ['goog.array', 'goog.cssom', 'goog.cssom.CssRuleType', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('cssom/iframe/style.js', ['goog.cssom.iframe.style'], ['goog.asserts', 'goog.cssom', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.string', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('cssom/iframe/style_test.js', ['goog.cssom.iframe.styleTest'], ['goog.cssom', 'goog.cssom.iframe.style', 'goog.dom', 'goog.dom.DomHelper', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('datasource/datamanager.js', ['goog.ds.DataManager'], ['goog.ds.BasicNodeList', 'goog.ds.DataNode', 'goog.ds.Expr', 'goog.object', 'goog.string', 'goog.structs', 'goog.structs.Map'], false); +goog.addDependency('datasource/datasource.js', ['goog.ds.BaseDataNode', 'goog.ds.BasicNodeList', 'goog.ds.DataNode', 'goog.ds.DataNodeList', 'goog.ds.EmptyNodeList', 'goog.ds.LoadState', 'goog.ds.SortedNodeList', 'goog.ds.Util', 'goog.ds.logger'], ['goog.array', 'goog.log'], false); +goog.addDependency('datasource/datasource_test.js', ['goog.ds.JsDataSourceTest'], ['goog.dom.xml', 'goog.ds.DataManager', 'goog.ds.JsDataSource', 'goog.ds.SortedNodeList', 'goog.ds.XmlDataSource', 'goog.testing.jsunit'], false); +goog.addDependency('datasource/expr.js', ['goog.ds.Expr'], ['goog.ds.BasicNodeList', 'goog.ds.EmptyNodeList', 'goog.string'], false); +goog.addDependency('datasource/expr_test.js', ['goog.ds.ExprTest'], ['goog.ds.DataManager', 'goog.ds.Expr', 'goog.ds.JsDataSource', 'goog.testing.jsunit'], false); +goog.addDependency('datasource/fastdatanode.js', ['goog.ds.AbstractFastDataNode', 'goog.ds.FastDataNode', 'goog.ds.FastListNode', 'goog.ds.PrimitiveFastDataNode'], ['goog.ds.DataManager', 'goog.ds.DataNodeList', 'goog.ds.EmptyNodeList', 'goog.string'], false); +goog.addDependency('datasource/fastdatanode_test.js', ['goog.ds.FastDataNodeTest'], ['goog.array', 'goog.ds.DataManager', 'goog.ds.Expr', 'goog.ds.FastDataNode', 'goog.testing.jsunit'], false); +goog.addDependency('datasource/jsdatasource.js', ['goog.ds.JsDataSource', 'goog.ds.JsPropertyDataSource'], ['goog.ds.BaseDataNode', 'goog.ds.BasicNodeList', 'goog.ds.DataManager', 'goog.ds.DataNode', 'goog.ds.EmptyNodeList', 'goog.ds.LoadState'], false); +goog.addDependency('datasource/jsondatasource.js', ['goog.ds.JsonDataSource'], ['goog.Uri', 'goog.dom', 'goog.ds.DataManager', 'goog.ds.JsDataSource', 'goog.ds.LoadState', 'goog.ds.logger'], false); +goog.addDependency('datasource/jsxmlhttpdatasource.js', ['goog.ds.JsXmlHttpDataSource'], ['goog.Uri', 'goog.ds.DataManager', 'goog.ds.FastDataNode', 'goog.ds.LoadState', 'goog.ds.logger', 'goog.events', 'goog.log', 'goog.net.EventType', 'goog.net.XhrIo'], false); +goog.addDependency('datasource/jsxmlhttpdatasource_test.js', ['goog.ds.JsXmlHttpDataSourceTest'], ['goog.ds.JsXmlHttpDataSource', 'goog.testing.TestQueue', 'goog.testing.jsunit', 'goog.testing.net.XhrIo'], false); +goog.addDependency('datasource/xmldatasource.js', ['goog.ds.XmlDataSource', 'goog.ds.XmlHttpDataSource'], ['goog.Uri', 'goog.dom.NodeType', 'goog.dom.xml', 'goog.ds.BasicNodeList', 'goog.ds.DataManager', 'goog.ds.DataNode', 'goog.ds.LoadState', 'goog.ds.logger', 'goog.net.XhrIo', 'goog.string'], false); +goog.addDependency('date/date.js', ['goog.date', 'goog.date.Date', 'goog.date.DateTime', 'goog.date.Interval', 'goog.date.month', 'goog.date.weekDay'], ['goog.asserts', 'goog.date.DateLike', 'goog.i18n.DateTimeSymbols', 'goog.string'], false); +goog.addDependency('date/date_test.js', ['goog.dateTest'], ['goog.array', 'goog.date', 'goog.date.Date', 'goog.date.DateTime', 'goog.date.Interval', 'goog.date.month', 'goog.date.weekDay', 'goog.i18n.DateTimeSymbols', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.platform', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('date/datelike.js', ['goog.date.DateLike'], [], false); +goog.addDependency('date/daterange.js', ['goog.date.DateRange', 'goog.date.DateRange.Iterator', 'goog.date.DateRange.StandardDateRangeKeys'], ['goog.date.Date', 'goog.date.Interval', 'goog.iter.Iterator', 'goog.iter.StopIteration'], false); +goog.addDependency('date/daterange_test.js', ['goog.date.DateRangeTest'], ['goog.date.Date', 'goog.date.DateRange', 'goog.date.Interval', 'goog.i18n.DateTimeSymbols', 'goog.testing.jsunit'], false); +goog.addDependency('date/duration.js', ['goog.date.duration'], ['goog.i18n.DateTimeFormat', 'goog.i18n.MessageFormat'], false); +goog.addDependency('date/duration_test.js', ['goog.date.durationTest'], ['goog.date.duration', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_bn', 'goog.i18n.DateTimeSymbols_en', 'goog.i18n.DateTimeSymbols_fa', 'goog.testing.jsunit'], false); +goog.addDependency('date/relative.js', ['goog.date.relative', 'goog.date.relative.TimeDeltaFormatter', 'goog.date.relative.Unit'], ['goog.i18n.DateTimeFormat'], false); +goog.addDependency('date/relative_test.js', ['goog.date.relativeTest'], ['goog.date.DateTime', 'goog.date.relative', 'goog.i18n.DateTimeFormat', 'goog.testing.jsunit'], false); +goog.addDependency('date/relativewithplurals.js', ['goog.date.relativeWithPlurals'], ['goog.date.relative', 'goog.date.relative.Unit', 'goog.i18n.MessageFormat'], false); +goog.addDependency('date/relativewithplurals_test.js', ['goog.date.relativeWithPluralsTest'], ['goog.date.relative', 'goog.date.relativeTest', 'goog.date.relativeWithPlurals', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_bn', 'goog.i18n.DateTimeSymbols_en', 'goog.i18n.DateTimeSymbols_fa', 'goog.i18n.NumberFormatSymbols', 'goog.i18n.NumberFormatSymbols_bn', 'goog.i18n.NumberFormatSymbols_en', 'goog.i18n.NumberFormatSymbols_fa'], false); +goog.addDependency('date/utcdatetime.js', ['goog.date.UtcDateTime'], ['goog.date', 'goog.date.Date', 'goog.date.DateTime', 'goog.date.Interval'], false); +goog.addDependency('date/utcdatetime_test.js', ['goog.date.UtcDateTimeTest'], ['goog.date.Interval', 'goog.date.UtcDateTime', 'goog.date.month', 'goog.date.weekDay', 'goog.testing.jsunit'], false); +goog.addDependency('db/cursor.js', ['goog.db.Cursor'], ['goog.async.Deferred', 'goog.db.Error', 'goog.debug', 'goog.events.EventTarget'], false); +goog.addDependency('db/db.js', ['goog.db', 'goog.db.BlockedCallback', 'goog.db.UpgradeNeededCallback'], ['goog.asserts', 'goog.async.Deferred', 'goog.db.Error', 'goog.db.IndexedDb', 'goog.db.Transaction'], false); +goog.addDependency('db/db_test.js', ['goog.dbTest'], ['goog.Disposable', 'goog.array', 'goog.async.Deferred', 'goog.async.DeferredList', 'goog.db', 'goog.db.Cursor', 'goog.db.Error', 'goog.db.IndexedDb', 'goog.db.KeyRange', 'goog.db.Transaction', 'goog.events', 'goog.object', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('db/error.js', ['goog.db.Error', 'goog.db.Error.ErrorCode', 'goog.db.Error.ErrorName', 'goog.db.Error.VersionChangeBlockedError'], ['goog.debug.Error'], false); +goog.addDependency('db/index.js', ['goog.db.Index'], ['goog.async.Deferred', 'goog.db.Cursor', 'goog.db.Error', 'goog.debug'], false); +goog.addDependency('db/indexeddb.js', ['goog.db.IndexedDb'], ['goog.async.Deferred', 'goog.db.Error', 'goog.db.ObjectStore', 'goog.db.Transaction', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget'], false); +goog.addDependency('db/keyrange.js', ['goog.db.KeyRange'], [], false); +goog.addDependency('db/objectstore.js', ['goog.db.ObjectStore'], ['goog.async.Deferred', 'goog.db.Cursor', 'goog.db.Error', 'goog.db.Index', 'goog.debug', 'goog.events'], false); +goog.addDependency('db/old_db_test.js', ['goog.oldDbTest'], ['goog.async.Deferred', 'goog.db', 'goog.db.Cursor', 'goog.db.Error', 'goog.db.IndexedDb', 'goog.db.KeyRange', 'goog.db.Transaction', 'goog.events', 'goog.testing.AsyncTestCase', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('db/transaction.js', ['goog.db.Transaction', 'goog.db.Transaction.TransactionMode'], ['goog.async.Deferred', 'goog.db.Error', 'goog.db.ObjectStore', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget'], false); +goog.addDependency('debug/console.js', ['goog.debug.Console'], ['goog.debug.LogManager', 'goog.debug.Logger', 'goog.debug.TextFormatter'], false); +goog.addDependency('debug/console_test.js', ['goog.debug.ConsoleTest'], ['goog.debug.Console', 'goog.debug.LogRecord', 'goog.debug.Logger', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('debug/debug.js', ['goog.debug'], ['goog.array', 'goog.html.SafeHtml', 'goog.html.SafeUrl', 'goog.html.uncheckedconversions', 'goog.string.Const', 'goog.structs.Set', 'goog.userAgent'], false); +goog.addDependency('debug/debug_test.js', ['goog.debugTest'], ['goog.debug', 'goog.html.SafeHtml', 'goog.structs.Set', 'goog.testing.jsunit'], false); +goog.addDependency('debug/debugwindow.js', ['goog.debug.DebugWindow'], ['goog.debug.HtmlFormatter', 'goog.debug.LogManager', 'goog.debug.Logger', 'goog.structs.CircularBuffer', 'goog.userAgent'], false); +goog.addDependency('debug/debugwindow_test.js', ['goog.debug.DebugWindowTest'], ['goog.debug.DebugWindow', 'goog.testing.jsunit'], false); +goog.addDependency('debug/devcss/devcss.js', ['goog.debug.DevCss', 'goog.debug.DevCss.UserAgent'], ['goog.asserts', 'goog.cssom', 'goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('debug/devcss/devcss_test.js', ['goog.debug.DevCssTest'], ['goog.debug.DevCss', 'goog.style', 'goog.testing.jsunit'], false); +goog.addDependency('debug/devcss/devcssrunner.js', ['goog.debug.devCssRunner'], ['goog.debug.DevCss'], false); +goog.addDependency('debug/divconsole.js', ['goog.debug.DivConsole'], ['goog.debug.HtmlFormatter', 'goog.debug.LogManager', 'goog.dom.safe', 'goog.html.SafeHtml', 'goog.style'], false); +goog.addDependency('debug/enhanceerror_test.js', ['goog.debugEnhanceErrorTest'], ['goog.debug', 'goog.testing.jsunit'], false); +goog.addDependency('debug/entrypointregistry.js', ['goog.debug.EntryPointMonitor', 'goog.debug.entryPointRegistry'], ['goog.asserts'], false); +goog.addDependency('debug/entrypointregistry_test.js', ['goog.debug.entryPointRegistryTest'], ['goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.testing.jsunit'], false); +goog.addDependency('debug/error.js', ['goog.debug.Error'], [], false); +goog.addDependency('debug/error_test.js', ['goog.debug.ErrorTest'], ['goog.debug.Error', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('debug/errorhandler.js', ['goog.debug.ErrorHandler', 'goog.debug.ErrorHandler.ProtectedFunctionError'], ['goog.Disposable', 'goog.asserts', 'goog.debug', 'goog.debug.EntryPointMonitor', 'goog.debug.Error', 'goog.debug.Trace'], false); +goog.addDependency('debug/errorhandler_async_test.js', ['goog.debug.ErrorHandlerAsyncTest'], ['goog.debug.ErrorHandler', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('debug/errorhandler_test.js', ['goog.debug.ErrorHandlerTest'], ['goog.debug.ErrorHandler', 'goog.testing.MockControl', 'goog.testing.jsunit'], false); +goog.addDependency('debug/errorhandlerweakdep.js', ['goog.debug.errorHandlerWeakDep'], [], false); +goog.addDependency('debug/errorreporter.js', ['goog.debug.ErrorReporter', 'goog.debug.ErrorReporter.ExceptionEvent'], ['goog.asserts', 'goog.debug', 'goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.log', 'goog.net.XhrIo', 'goog.object', 'goog.string', 'goog.uri.utils', 'goog.userAgent'], false); +goog.addDependency('debug/errorreporter_test.js', ['goog.debug.ErrorReporterTest'], ['goog.debug.ErrorReporter', 'goog.events', 'goog.functions', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('debug/fancywindow.js', ['goog.debug.FancyWindow'], ['goog.array', 'goog.debug.DebugWindow', 'goog.debug.LogManager', 'goog.debug.Logger', 'goog.dom.DomHelper', 'goog.dom.safe', 'goog.html.SafeHtml', 'goog.object', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('debug/formatter.js', ['goog.debug.Formatter', 'goog.debug.HtmlFormatter', 'goog.debug.TextFormatter'], ['goog.debug', 'goog.debug.Logger', 'goog.debug.RelativeTimeProvider', 'goog.html.SafeHtml'], false); +goog.addDependency('debug/formatter_test.js', ['goog.debug.FormatterTest'], ['goog.debug.HtmlFormatter', 'goog.debug.LogRecord', 'goog.debug.Logger', 'goog.html.SafeHtml', 'goog.testing.jsunit'], false); +goog.addDependency('debug/fpsdisplay.js', ['goog.debug.FpsDisplay'], ['goog.asserts', 'goog.async.AnimationDelay', 'goog.dom', 'goog.ui.Component'], false); +goog.addDependency('debug/fpsdisplay_test.js', ['goog.debug.FpsDisplayTest'], ['goog.debug.FpsDisplay', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('debug/gcdiagnostics.js', ['goog.debug.GcDiagnostics'], ['goog.debug.Trace', 'goog.log', 'goog.userAgent'], false); +goog.addDependency('debug/logbuffer.js', ['goog.debug.LogBuffer'], ['goog.asserts', 'goog.debug.LogRecord'], false); +goog.addDependency('debug/logbuffer_test.js', ['goog.debug.LogBufferTest'], ['goog.debug.LogBuffer', 'goog.debug.Logger', 'goog.testing.jsunit'], false); +goog.addDependency('debug/logger.js', ['goog.debug.LogManager', 'goog.debug.Loggable', 'goog.debug.Logger', 'goog.debug.Logger.Level'], ['goog.array', 'goog.asserts', 'goog.debug', 'goog.debug.LogBuffer', 'goog.debug.LogRecord'], false); +goog.addDependency('debug/logger_test.js', ['goog.debug.LoggerTest'], ['goog.debug.LogManager', 'goog.debug.Logger', 'goog.testing.jsunit'], false); +goog.addDependency('debug/logrecord.js', ['goog.debug.LogRecord'], [], false); +goog.addDependency('debug/logrecordserializer.js', ['goog.debug.logRecordSerializer'], ['goog.debug.LogRecord', 'goog.debug.Logger', 'goog.json', 'goog.object'], false); +goog.addDependency('debug/logrecordserializer_test.js', ['goog.debug.logRecordSerializerTest'], ['goog.debug.LogRecord', 'goog.debug.Logger', 'goog.debug.logRecordSerializer', 'goog.testing.jsunit'], false); +goog.addDependency('debug/relativetimeprovider.js', ['goog.debug.RelativeTimeProvider'], [], false); +goog.addDependency('debug/tracer.js', ['goog.debug.Trace'], ['goog.array', 'goog.debug.Logger', 'goog.iter', 'goog.log', 'goog.structs.Map', 'goog.structs.SimplePool'], false); +goog.addDependency('debug/tracer_test.js', ['goog.debug.TraceTest'], ['goog.debug.Trace', 'goog.testing.jsunit'], false); +goog.addDependency('defineclass_test.js', ['goog.defineClassTest'], ['goog.testing.jsunit'], false); +goog.addDependency('disposable/disposable.js', ['goog.Disposable', 'goog.dispose', 'goog.disposeAll'], ['goog.disposable.IDisposable'], false); +goog.addDependency('disposable/disposable_test.js', ['goog.DisposableTest'], ['goog.Disposable', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('disposable/idisposable.js', ['goog.disposable.IDisposable'], [], false); +goog.addDependency('dom/abstractmultirange.js', ['goog.dom.AbstractMultiRange'], ['goog.array', 'goog.dom', 'goog.dom.AbstractRange'], false); +goog.addDependency('dom/abstractrange.js', ['goog.dom.AbstractRange', 'goog.dom.RangeIterator', 'goog.dom.RangeType'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.SavedCaretRange', 'goog.dom.TagIterator', 'goog.userAgent'], false); +goog.addDependency('dom/abstractrange_test.js', ['goog.dom.AbstractRangeTest'], ['goog.dom', 'goog.dom.AbstractRange', 'goog.dom.Range', 'goog.testing.jsunit'], false); +goog.addDependency('dom/animationframe/animationframe.js', ['goog.dom.animationFrame', 'goog.dom.animationFrame.Spec', 'goog.dom.animationFrame.State'], ['goog.dom.animationFrame.polyfill'], false); +goog.addDependency('dom/animationframe/polyfill.js', ['goog.dom.animationFrame.polyfill'], [], false); +goog.addDependency('dom/annotate.js', ['goog.dom.annotate', 'goog.dom.annotate.AnnotateFn'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.safe', 'goog.html.SafeHtml'], false); +goog.addDependency('dom/annotate_test.js', ['goog.dom.annotateTest'], ['goog.dom', 'goog.dom.annotate', 'goog.html.SafeHtml', 'goog.testing.jsunit'], false); +goog.addDependency('dom/browserfeature.js', ['goog.dom.BrowserFeature'], ['goog.userAgent'], false); +goog.addDependency('dom/browserrange/abstractrange.js', ['goog.dom.browserrange.AbstractRange'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.RangeEndpoint', 'goog.dom.TagName', 'goog.dom.TextRangeIterator', 'goog.iter', 'goog.math.Coordinate', 'goog.string', 'goog.string.StringBuffer', 'goog.userAgent'], false); +goog.addDependency('dom/browserrange/browserrange.js', ['goog.dom.browserrange', 'goog.dom.browserrange.Error'], ['goog.dom', 'goog.dom.BrowserFeature', 'goog.dom.NodeType', 'goog.dom.browserrange.GeckoRange', 'goog.dom.browserrange.IeRange', 'goog.dom.browserrange.OperaRange', 'goog.dom.browserrange.W3cRange', 'goog.dom.browserrange.WebKitRange', 'goog.userAgent'], false); +goog.addDependency('dom/browserrange/browserrange_test.js', ['goog.dom.browserrangeTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.RangeEndpoint', 'goog.dom.TagName', 'goog.dom.browserrange', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/browserrange/geckorange.js', ['goog.dom.browserrange.GeckoRange'], ['goog.dom.browserrange.W3cRange'], false); +goog.addDependency('dom/browserrange/ierange.js', ['goog.dom.browserrange.IeRange'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.RangeEndpoint', 'goog.dom.TagName', 'goog.dom.browserrange.AbstractRange', 'goog.log', 'goog.string'], false); +goog.addDependency('dom/browserrange/operarange.js', ['goog.dom.browserrange.OperaRange'], ['goog.dom.browserrange.W3cRange'], false); +goog.addDependency('dom/browserrange/w3crange.js', ['goog.dom.browserrange.W3cRange'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.RangeEndpoint', 'goog.dom.browserrange.AbstractRange', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('dom/browserrange/webkitrange.js', ['goog.dom.browserrange.WebKitRange'], ['goog.dom.RangeEndpoint', 'goog.dom.browserrange.W3cRange', 'goog.userAgent'], false); +goog.addDependency('dom/bufferedviewportsizemonitor.js', ['goog.dom.BufferedViewportSizeMonitor'], ['goog.asserts', 'goog.async.Delay', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType'], false); +goog.addDependency('dom/bufferedviewportsizemonitor_test.js', ['goog.dom.BufferedViewportSizeMonitorTest'], ['goog.dom.BufferedViewportSizeMonitor', 'goog.dom.ViewportSizeMonitor', 'goog.events', 'goog.events.EventType', 'goog.math.Size', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit'], false); +goog.addDependency('dom/classes.js', ['goog.dom.classes'], ['goog.array'], false); +goog.addDependency('dom/classes_test.js', ['goog.dom.classes_test'], ['goog.dom', 'goog.dom.classes', 'goog.testing.jsunit'], false); +goog.addDependency('dom/classlist.js', ['goog.dom.classlist'], ['goog.array'], false); +goog.addDependency('dom/classlist_test.js', ['goog.dom.classlist_test'], ['goog.dom', 'goog.dom.classlist', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit'], false); +goog.addDependency('dom/controlrange.js', ['goog.dom.ControlRange', 'goog.dom.ControlRangeIterator'], ['goog.array', 'goog.dom', 'goog.dom.AbstractMultiRange', 'goog.dom.AbstractRange', 'goog.dom.RangeIterator', 'goog.dom.RangeType', 'goog.dom.SavedRange', 'goog.dom.TagWalkType', 'goog.dom.TextRange', 'goog.iter.StopIteration', 'goog.userAgent'], false); +goog.addDependency('dom/controlrange_test.js', ['goog.dom.ControlRangeTest'], ['goog.dom', 'goog.dom.ControlRange', 'goog.dom.RangeType', 'goog.dom.TagName', 'goog.dom.TextRange', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/dataset.js', ['goog.dom.dataset'], ['goog.string'], false); +goog.addDependency('dom/dataset_test.js', ['goog.dom.datasetTest'], ['goog.dom', 'goog.dom.dataset', 'goog.testing.jsunit'], false); +goog.addDependency('dom/dom.js', ['goog.dom', 'goog.dom.Appendable', 'goog.dom.DomHelper'], ['goog.array', 'goog.asserts', 'goog.dom.BrowserFeature', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.safe', 'goog.html.SafeHtml', 'goog.math.Coordinate', 'goog.math.Size', 'goog.object', 'goog.string', 'goog.string.Unicode', 'goog.userAgent'], false); +goog.addDependency('dom/dom_test.js', ['goog.dom.dom_test'], ['goog.dom', 'goog.dom.BrowserFeature', 'goog.dom.DomHelper', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.functions', 'goog.html.testing', 'goog.object', 'goog.string.Unicode', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('dom/fontsizemonitor.js', ['goog.dom.FontSizeMonitor', 'goog.dom.FontSizeMonitor.EventType'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.userAgent'], false); +goog.addDependency('dom/fontsizemonitor_test.js', ['goog.dom.FontSizeMonitorTest'], ['goog.dom', 'goog.dom.FontSizeMonitor', 'goog.events', 'goog.events.Event', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/forms.js', ['goog.dom.forms'], ['goog.structs.Map'], false); +goog.addDependency('dom/forms_test.js', ['goog.dom.formsTest'], ['goog.dom', 'goog.dom.forms', 'goog.testing.jsunit'], false); +goog.addDependency('dom/fullscreen.js', ['goog.dom.fullscreen', 'goog.dom.fullscreen.EventType'], ['goog.dom', 'goog.userAgent'], false); +goog.addDependency('dom/iframe.js', ['goog.dom.iframe'], ['goog.dom', 'goog.userAgent'], false); +goog.addDependency('dom/iframe_test.js', ['goog.dom.iframeTest'], ['goog.dom', 'goog.dom.iframe', 'goog.testing.jsunit'], false); +goog.addDependency('dom/iter.js', ['goog.dom.iter.AncestorIterator', 'goog.dom.iter.ChildIterator', 'goog.dom.iter.SiblingIterator'], ['goog.iter.Iterator', 'goog.iter.StopIteration'], false); +goog.addDependency('dom/iter_test.js', ['goog.dom.iterTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.iter.AncestorIterator', 'goog.dom.iter.ChildIterator', 'goog.dom.iter.SiblingIterator', 'goog.testing.dom', 'goog.testing.jsunit'], false); +goog.addDependency('dom/multirange.js', ['goog.dom.MultiRange', 'goog.dom.MultiRangeIterator'], ['goog.array', 'goog.dom.AbstractMultiRange', 'goog.dom.AbstractRange', 'goog.dom.RangeIterator', 'goog.dom.RangeType', 'goog.dom.SavedRange', 'goog.dom.TextRange', 'goog.iter.StopIteration', 'goog.log'], false); +goog.addDependency('dom/multirange_test.js', ['goog.dom.MultiRangeTest'], ['goog.dom', 'goog.dom.MultiRange', 'goog.dom.Range', 'goog.iter', 'goog.testing.jsunit'], false); +goog.addDependency('dom/nodeiterator.js', ['goog.dom.NodeIterator'], ['goog.dom.TagIterator'], false); +goog.addDependency('dom/nodeiterator_test.js', ['goog.dom.NodeIteratorTest'], ['goog.dom', 'goog.dom.NodeIterator', 'goog.testing.dom', 'goog.testing.jsunit'], false); +goog.addDependency('dom/nodeoffset.js', ['goog.dom.NodeOffset'], ['goog.Disposable', 'goog.dom.TagName'], false); +goog.addDependency('dom/nodeoffset_test.js', ['goog.dom.NodeOffsetTest'], ['goog.dom', 'goog.dom.NodeOffset', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.testing.jsunit'], false); +goog.addDependency('dom/nodetype.js', ['goog.dom.NodeType'], [], false); +goog.addDependency('dom/pattern/abstractpattern.js', ['goog.dom.pattern.AbstractPattern'], ['goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/allchildren.js', ['goog.dom.pattern.AllChildren'], ['goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/callback/callback.js', ['goog.dom.pattern.callback'], ['goog.dom', 'goog.dom.TagWalkType', 'goog.iter'], false); +goog.addDependency('dom/pattern/callback/counter.js', ['goog.dom.pattern.callback.Counter'], [], false); +goog.addDependency('dom/pattern/callback/test.js', ['goog.dom.pattern.callback.Test'], ['goog.iter.StopIteration'], false); +goog.addDependency('dom/pattern/childmatches.js', ['goog.dom.pattern.ChildMatches'], ['goog.dom.pattern.AllChildren', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/endtag.js', ['goog.dom.pattern.EndTag'], ['goog.dom.TagWalkType', 'goog.dom.pattern.Tag'], false); +goog.addDependency('dom/pattern/fulltag.js', ['goog.dom.pattern.FullTag'], ['goog.dom.pattern.MatchType', 'goog.dom.pattern.StartTag', 'goog.dom.pattern.Tag'], false); +goog.addDependency('dom/pattern/matcher.js', ['goog.dom.pattern.Matcher'], ['goog.dom.TagIterator', 'goog.dom.pattern.MatchType', 'goog.iter'], false); +goog.addDependency('dom/pattern/matcher_test.js', ['goog.dom.pattern.matcherTest'], ['goog.dom', 'goog.dom.pattern.EndTag', 'goog.dom.pattern.FullTag', 'goog.dom.pattern.Matcher', 'goog.dom.pattern.Repeat', 'goog.dom.pattern.Sequence', 'goog.dom.pattern.StartTag', 'goog.dom.pattern.callback.Counter', 'goog.dom.pattern.callback.Test', 'goog.iter.StopIteration', 'goog.testing.jsunit'], false); +goog.addDependency('dom/pattern/nodetype.js', ['goog.dom.pattern.NodeType'], ['goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/pattern.js', ['goog.dom.pattern', 'goog.dom.pattern.MatchType'], [], false); +goog.addDependency('dom/pattern/pattern_test.js', ['goog.dom.patternTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagWalkType', 'goog.dom.pattern.AllChildren', 'goog.dom.pattern.ChildMatches', 'goog.dom.pattern.EndTag', 'goog.dom.pattern.FullTag', 'goog.dom.pattern.MatchType', 'goog.dom.pattern.NodeType', 'goog.dom.pattern.Repeat', 'goog.dom.pattern.Sequence', 'goog.dom.pattern.StartTag', 'goog.dom.pattern.Text', 'goog.testing.jsunit'], false); +goog.addDependency('dom/pattern/repeat.js', ['goog.dom.pattern.Repeat'], ['goog.dom.NodeType', 'goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/sequence.js', ['goog.dom.pattern.Sequence'], ['goog.dom.NodeType', 'goog.dom.pattern', 'goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/pattern/starttag.js', ['goog.dom.pattern.StartTag'], ['goog.dom.TagWalkType', 'goog.dom.pattern.Tag'], false); +goog.addDependency('dom/pattern/tag.js', ['goog.dom.pattern.Tag'], ['goog.dom.pattern', 'goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType', 'goog.object'], false); +goog.addDependency('dom/pattern/text.js', ['goog.dom.pattern.Text'], ['goog.dom.NodeType', 'goog.dom.pattern', 'goog.dom.pattern.AbstractPattern', 'goog.dom.pattern.MatchType'], false); +goog.addDependency('dom/range.js', ['goog.dom.Range'], ['goog.dom', 'goog.dom.AbstractRange', 'goog.dom.BrowserFeature', 'goog.dom.ControlRange', 'goog.dom.MultiRange', 'goog.dom.NodeType', 'goog.dom.TextRange', 'goog.userAgent'], false); +goog.addDependency('dom/range_test.js', ['goog.dom.RangeTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.RangeType', 'goog.dom.TagName', 'goog.dom.TextRange', 'goog.dom.browserrange', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/rangeendpoint.js', ['goog.dom.RangeEndpoint'], [], false); +goog.addDependency('dom/safe.js', ['goog.dom.safe'], ['goog.html.SafeHtml', 'goog.html.SafeUrl'], false); +goog.addDependency('dom/safe_test.js', ['goog.dom.safeTest'], ['goog.dom.safe', 'goog.html.SafeUrl', 'goog.html.testing', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('dom/savedcaretrange.js', ['goog.dom.SavedCaretRange'], ['goog.array', 'goog.dom', 'goog.dom.SavedRange', 'goog.dom.TagName', 'goog.string'], false); +goog.addDependency('dom/savedcaretrange_test.js', ['goog.dom.SavedCaretRangeTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.SavedCaretRange', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/savedrange.js', ['goog.dom.SavedRange'], ['goog.Disposable', 'goog.log'], false); +goog.addDependency('dom/savedrange_test.js', ['goog.dom.SavedRangeTest'], ['goog.dom', 'goog.dom.Range', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/selection.js', ['goog.dom.selection'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('dom/selection_test.js', ['goog.dom.selectionTest'], ['goog.dom', 'goog.dom.selection', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/tagiterator.js', ['goog.dom.TagIterator', 'goog.dom.TagWalkType'], ['goog.dom', 'goog.dom.NodeType', 'goog.iter.Iterator', 'goog.iter.StopIteration'], false); +goog.addDependency('dom/tagiterator_test.js', ['goog.dom.TagIteratorTest'], ['goog.dom', 'goog.dom.TagIterator', 'goog.dom.TagWalkType', 'goog.iter', 'goog.iter.StopIteration', 'goog.testing.dom', 'goog.testing.jsunit'], false); +goog.addDependency('dom/tagname.js', ['goog.dom.TagName'], [], false); +goog.addDependency('dom/tagname_test.js', ['goog.dom.TagNameTest'], ['goog.dom.TagName', 'goog.object', 'goog.testing.jsunit'], false); +goog.addDependency('dom/tags.js', ['goog.dom.tags'], ['goog.object'], false); +goog.addDependency('dom/tags_test.js', ['goog.dom.tagsTest'], ['goog.dom.tags', 'goog.testing.jsunit'], false); +goog.addDependency('dom/textrange.js', ['goog.dom.TextRange'], ['goog.array', 'goog.dom', 'goog.dom.AbstractRange', 'goog.dom.RangeType', 'goog.dom.SavedRange', 'goog.dom.TagName', 'goog.dom.TextRangeIterator', 'goog.dom.browserrange', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('dom/textrange_test.js', ['goog.dom.TextRangeTest'], ['goog.dom', 'goog.dom.ControlRange', 'goog.dom.Range', 'goog.dom.TextRange', 'goog.math.Coordinate', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('dom/textrangeiterator.js', ['goog.dom.TextRangeIterator'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.RangeIterator', 'goog.dom.TagName', 'goog.iter.StopIteration'], false); +goog.addDependency('dom/textrangeiterator_test.js', ['goog.dom.TextRangeIteratorTest'], ['goog.dom', 'goog.dom.TagName', 'goog.dom.TextRangeIterator', 'goog.iter.StopIteration', 'goog.testing.dom', 'goog.testing.jsunit'], false); +goog.addDependency('dom/vendor.js', ['goog.dom.vendor'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('dom/vendor_test.js', ['goog.dom.vendorTest'], ['goog.array', 'goog.dom.vendor', 'goog.labs.userAgent.util', 'goog.testing.MockUserAgent', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgentTestUtil'], false); +goog.addDependency('dom/viewportsizemonitor.js', ['goog.dom.ViewportSizeMonitor'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.math.Size'], false); +goog.addDependency('dom/viewportsizemonitor_test.js', ['goog.dom.ViewportSizeMonitorTest'], ['goog.dom.ViewportSizeMonitor', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.math.Size', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('dom/xml.js', ['goog.dom.xml'], ['goog.dom', 'goog.dom.NodeType'], false); +goog.addDependency('dom/xml_test.js', ['goog.dom.xmlTest'], ['goog.dom.xml', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/browserfeature.js', ['goog.editor.BrowserFeature'], ['goog.editor.defines', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('editor/browserfeature_test.js', ['goog.editor.BrowserFeatureTest'], ['goog.dom', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit'], false); +goog.addDependency('editor/clicktoeditwrapper.js', ['goog.editor.ClickToEditWrapper'], ['goog.Disposable', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.range', 'goog.events.BrowserEvent', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.log'], false); +goog.addDependency('editor/clicktoeditwrapper_test.js', ['goog.editor.ClickToEditWrapperTest'], ['goog.dom', 'goog.dom.Range', 'goog.editor.ClickToEditWrapper', 'goog.editor.SeamlessField', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('editor/command.js', ['goog.editor.Command'], [], false); +goog.addDependency('editor/contenteditablefield.js', ['goog.editor.ContentEditableField'], ['goog.asserts', 'goog.editor.Field', 'goog.log'], false); +goog.addDependency('editor/contenteditablefield_test.js', ['goog.editor.ContentEditableFieldTest'], ['goog.dom', 'goog.editor.ContentEditableField', 'goog.editor.field_test', 'goog.testing.jsunit'], false); +goog.addDependency('editor/defines.js', ['goog.editor.defines'], [], false); +goog.addDependency('editor/field.js', ['goog.editor.Field', 'goog.editor.Field.EventType'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.array', 'goog.asserts', 'goog.async.Delay', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Plugin', 'goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo', 'goog.editor.node', 'goog.editor.range', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.functions', 'goog.log', 'goog.log.Level', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('editor/field_test.js', ['goog.editor.field_test'], ['goog.dom', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.range', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.functions', 'goog.testing.LooseMock', 'goog.testing.MockClock', 'goog.testing.dom', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('editor/focus.js', ['goog.editor.focus'], ['goog.dom.selection'], false); +goog.addDependency('editor/focus_test.js', ['goog.editor.focusTest'], ['goog.dom.selection', 'goog.editor.BrowserFeature', 'goog.editor.focus', 'goog.testing.jsunit'], false); +goog.addDependency('editor/icontent.js', ['goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo'], ['goog.dom', 'goog.editor.BrowserFeature', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('editor/icontent_test.js', ['goog.editor.icontentTest'], ['goog.dom', 'goog.editor.BrowserFeature', 'goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/link.js', ['goog.editor.Link'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.node', 'goog.editor.range', 'goog.string', 'goog.string.Unicode', 'goog.uri.utils', 'goog.uri.utils.ComponentIndex'], false); +goog.addDependency('editor/link_test.js', ['goog.editor.LinkTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Link', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/node.js', ['goog.editor.node'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.iter.ChildIterator', 'goog.dom.iter.SiblingIterator', 'goog.iter', 'goog.object', 'goog.string', 'goog.string.Unicode', 'goog.userAgent'], false); +goog.addDependency('editor/node_test.js', ['goog.editor.nodeTest'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.editor.node', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugin.js', ['goog.editor.Plugin'], ['goog.events.EventTarget', 'goog.functions', 'goog.log', 'goog.object', 'goog.reflect', 'goog.userAgent'], false); +goog.addDependency('editor/plugin_test.js', ['goog.editor.PluginTest'], ['goog.editor.Field', 'goog.editor.Plugin', 'goog.functions', 'goog.testing.StrictMock', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/abstractbubbleplugin.js', ['goog.editor.plugins.AbstractBubblePlugin'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.editor.Plugin', 'goog.editor.style', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.actionEventWrapper', 'goog.functions', 'goog.string.Unicode', 'goog.ui.Component', 'goog.ui.editor.Bubble', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/abstractbubbleplugin_test.js', ['goog.editor.plugins.AbstractBubblePluginTest'], ['goog.dom', 'goog.editor.plugins.AbstractBubblePlugin', 'goog.events.BrowserEvent', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.functions', 'goog.style', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.editor.Bubble', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/abstractdialogplugin.js', ['goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.plugins.AbstractDialogPlugin.EventType'], ['goog.dom', 'goog.dom.Range', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.range', 'goog.events', 'goog.ui.editor.AbstractDialog'], false); +goog.addDependency('editor/plugins/abstractdialogplugin_test.js', ['goog.editor.plugins.AbstractDialogPluginTest'], ['goog.dom.SavedRange', 'goog.editor.Field', 'goog.editor.plugins.AbstractDialogPlugin', 'goog.events.Event', 'goog.events.EventHandler', 'goog.functions', 'goog.testing.MockClock', 'goog.testing.MockControl', 'goog.testing.PropertyReplacer', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers.ArgumentMatcher', 'goog.ui.editor.AbstractDialog', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/abstracttabhandler.js', ['goog.editor.plugins.AbstractTabHandler'], ['goog.editor.Plugin', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/abstracttabhandler_test.js', ['goog.editor.plugins.AbstractTabHandlerTest'], ['goog.editor.Field', 'goog.editor.plugins.AbstractTabHandler', 'goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.testing.StrictMock', 'goog.testing.editor.FieldMock', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/basictextformatter.js', ['goog.editor.plugins.BasicTextFormatter', 'goog.editor.plugins.BasicTextFormatter.COMMAND'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Link', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.range', 'goog.editor.style', 'goog.iter', 'goog.iter.StopIteration', 'goog.log', 'goog.object', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.ui.editor.messages', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/basictextformatter_test.js', ['goog.editor.plugins.BasicTextFormatterTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.plugins.BasicTextFormatter', 'goog.object', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.LooseMock', 'goog.testing.PropertyReplacer', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/blockquote.js', ['goog.editor.plugins.Blockquote'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Plugin', 'goog.editor.node', 'goog.functions', 'goog.log'], false); +goog.addDependency('editor/plugins/blockquote_test.js', ['goog.editor.plugins.BlockquoteTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.plugins.Blockquote', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/emoticons.js', ['goog.editor.plugins.Emoticons'], ['goog.dom.TagName', 'goog.editor.Plugin', 'goog.editor.range', 'goog.functions', 'goog.ui.emoji.Emoji', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/emoticons_test.js', ['goog.editor.plugins.EmoticonsTest'], ['goog.Uri', 'goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.editor.Field', 'goog.editor.plugins.Emoticons', 'goog.testing.jsunit', 'goog.ui.emoji.Emoji', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/enterhandler.js', ['goog.editor.plugins.EnterHandler'], ['goog.dom', 'goog.dom.NodeOffset', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.plugins.Blockquote', 'goog.editor.range', 'goog.editor.style', 'goog.events.KeyCodes', 'goog.functions', 'goog.object', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/enterhandler_test.js', ['goog.editor.plugins.EnterHandlerTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.plugins.Blockquote', 'goog.editor.plugins.EnterHandler', 'goog.editor.range', 'goog.events', 'goog.events.KeyCodes', 'goog.testing.ExpectedFailures', 'goog.testing.MockClock', 'goog.testing.dom', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/firststrong.js', ['goog.editor.plugins.FirstStrong'], ['goog.dom.NodeType', 'goog.dom.TagIterator', 'goog.dom.TagName', 'goog.editor.Command', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.range', 'goog.i18n.bidi', 'goog.i18n.uChar', 'goog.iter', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/firststrong_test.js', ['goog.editor.plugins.FirstStrongTest'], ['goog.dom.Range', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.plugins.FirstStrong', 'goog.editor.range', 'goog.events.KeyCodes', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/headerformatter.js', ['goog.editor.plugins.HeaderFormatter'], ['goog.editor.Command', 'goog.editor.Plugin', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/headerformatter_test.js', ['goog.editor.plugins.HeaderFormatterTest'], ['goog.dom', 'goog.editor.Command', 'goog.editor.plugins.BasicTextFormatter', 'goog.editor.plugins.HeaderFormatter', 'goog.events.BrowserEvent', 'goog.testing.LooseMock', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/linkbubble.js', ['goog.editor.plugins.LinkBubble', 'goog.editor.plugins.LinkBubble.Action'], ['goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.editor.Command', 'goog.editor.Link', 'goog.editor.plugins.AbstractBubblePlugin', 'goog.editor.range', 'goog.functions', 'goog.string', 'goog.style', 'goog.ui.editor.messages', 'goog.uri.utils', 'goog.window'], false); +goog.addDependency('editor/plugins/linkbubble_test.js', ['goog.editor.plugins.LinkBubbleTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Command', 'goog.editor.Link', 'goog.editor.plugins.LinkBubble', 'goog.events.BrowserEvent', 'goog.events.Event', 'goog.events.EventType', 'goog.string', 'goog.style', 'goog.testing.FunctionMock', 'goog.testing.PropertyReplacer', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/linkdialogplugin.js', ['goog.editor.plugins.LinkDialogPlugin'], ['goog.array', 'goog.dom', 'goog.editor.Command', 'goog.editor.plugins.AbstractDialogPlugin', 'goog.events.EventHandler', 'goog.functions', 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.LinkDialog', 'goog.uri.utils'], false); +goog.addDependency('editor/plugins/linkdialogplugin_test.js', ['goog.ui.editor.plugins.LinkDialogTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.Link', 'goog.editor.plugins.LinkDialogPlugin', 'goog.string', 'goog.string.Unicode', 'goog.testing.MockControl', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.editor.dom', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.LinkDialog', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/linkshortcutplugin.js', ['goog.editor.plugins.LinkShortcutPlugin'], ['goog.editor.Command', 'goog.editor.Plugin'], false); +goog.addDependency('editor/plugins/linkshortcutplugin_test.js', ['goog.editor.plugins.LinkShortcutPluginTest'], ['goog.dom', 'goog.editor.Field', 'goog.editor.plugins.BasicTextFormatter', 'goog.editor.plugins.LinkBubble', 'goog.editor.plugins.LinkShortcutPlugin', 'goog.events.KeyCodes', 'goog.testing.PropertyReplacer', 'goog.testing.dom', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/listtabhandler.js', ['goog.editor.plugins.ListTabHandler'], ['goog.dom', 'goog.dom.TagName', 'goog.editor.Command', 'goog.editor.plugins.AbstractTabHandler', 'goog.iter'], false); +goog.addDependency('editor/plugins/listtabhandler_test.js', ['goog.editor.plugins.ListTabHandlerTest'], ['goog.dom', 'goog.editor.Command', 'goog.editor.plugins.ListTabHandler', 'goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.functions', 'goog.testing.StrictMock', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/loremipsum.js', ['goog.editor.plugins.LoremIpsum'], ['goog.asserts', 'goog.dom', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.node', 'goog.functions', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/loremipsum_test.js', ['goog.editor.plugins.LoremIpsumTest'], ['goog.dom', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.plugins.LoremIpsum', 'goog.string.Unicode', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/removeformatting.js', ['goog.editor.plugins.RemoveFormatting'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.range', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/removeformatting_test.js', ['goog.editor.plugins.RemoveFormattingTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.plugins.RemoveFormatting', 'goog.string', 'goog.testing.ExpectedFailures', 'goog.testing.dom', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/spacestabhandler.js', ['goog.editor.plugins.SpacesTabHandler'], ['goog.dom.TagName', 'goog.editor.plugins.AbstractTabHandler', 'goog.editor.range'], false); +goog.addDependency('editor/plugins/spacestabhandler_test.js', ['goog.editor.plugins.SpacesTabHandlerTest'], ['goog.dom', 'goog.dom.Range', 'goog.editor.plugins.SpacesTabHandler', 'goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.functions', 'goog.testing.StrictMock', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/tableeditor.js', ['goog.editor.plugins.TableEditor'], ['goog.array', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Plugin', 'goog.editor.Table', 'goog.editor.node', 'goog.editor.range', 'goog.object', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/tableeditor_test.js', ['goog.editor.plugins.TableEditorTest'], ['goog.dom', 'goog.dom.Range', 'goog.editor.plugins.TableEditor', 'goog.object', 'goog.string', 'goog.testing.ExpectedFailures', 'goog.testing.JsUnitException', 'goog.testing.editor.FieldMock', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/tagonenterhandler.js', ['goog.editor.plugins.TagOnEnterHandler'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.Command', 'goog.editor.node', 'goog.editor.plugins.EnterHandler', 'goog.editor.range', 'goog.editor.style', 'goog.events.KeyCodes', 'goog.functions', 'goog.string.Unicode', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/tagonenterhandler_test.js', ['goog.editor.plugins.TagOnEnterHandlerTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.plugins.TagOnEnterHandler', 'goog.events.KeyCodes', 'goog.string.Unicode', 'goog.testing.dom', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/plugins/undoredo.js', ['goog.editor.plugins.UndoRedo'], ['goog.dom', 'goog.dom.NodeOffset', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.editor.Command', 'goog.editor.Field', 'goog.editor.Plugin', 'goog.editor.node', 'goog.editor.plugins.UndoRedoManager', 'goog.editor.plugins.UndoRedoState', 'goog.events', 'goog.events.EventHandler', 'goog.log', 'goog.object'], false); +goog.addDependency('editor/plugins/undoredo_test.js', ['goog.editor.plugins.UndoRedoTest'], ['goog.array', 'goog.dom', 'goog.dom.browserrange', 'goog.editor.Field', 'goog.editor.plugins.LoremIpsum', 'goog.editor.plugins.UndoRedo', 'goog.events', 'goog.functions', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.StrictMock', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/undoredomanager.js', ['goog.editor.plugins.UndoRedoManager', 'goog.editor.plugins.UndoRedoManager.EventType'], ['goog.editor.plugins.UndoRedoState', 'goog.events', 'goog.events.EventTarget'], false); +goog.addDependency('editor/plugins/undoredomanager_test.js', ['goog.editor.plugins.UndoRedoManagerTest'], ['goog.editor.plugins.UndoRedoManager', 'goog.editor.plugins.UndoRedoState', 'goog.events', 'goog.testing.StrictMock', 'goog.testing.jsunit'], false); +goog.addDependency('editor/plugins/undoredostate.js', ['goog.editor.plugins.UndoRedoState'], ['goog.events.EventTarget'], false); +goog.addDependency('editor/plugins/undoredostate_test.js', ['goog.editor.plugins.UndoRedoStateTest'], ['goog.editor.plugins.UndoRedoState', 'goog.testing.jsunit'], false); +goog.addDependency('editor/range.js', ['goog.editor.range', 'goog.editor.range.Point'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.dom.RangeEndpoint', 'goog.dom.SavedCaretRange', 'goog.editor.node', 'goog.editor.style', 'goog.iter', 'goog.userAgent'], false); +goog.addDependency('editor/range_test.js', ['goog.editor.rangeTest'], ['goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.range', 'goog.editor.range.Point', 'goog.string', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('editor/seamlessfield.js', ['goog.editor.SeamlessField'], ['goog.cssom.iframe.style', 'goog.dom', 'goog.dom.Range', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Field', 'goog.editor.icontent', 'goog.editor.icontent.FieldFormatInfo', 'goog.editor.icontent.FieldStyleInfo', 'goog.editor.node', 'goog.events', 'goog.events.EventType', 'goog.log', 'goog.style'], false); +goog.addDependency('editor/seamlessfield_test.js', ['goog.editor.seamlessfield_test'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.editor.Field', 'goog.editor.SeamlessField', 'goog.events', 'goog.functions', 'goog.style', 'goog.testing.MockClock', 'goog.testing.MockRange', 'goog.testing.jsunit'], false); +goog.addDependency('editor/style.js', ['goog.editor.style'], ['goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.events.EventType', 'goog.object', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('editor/style_test.js', ['goog.editor.styleTest'], ['goog.dom', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.style', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.style', 'goog.testing.LooseMock', 'goog.testing.jsunit', 'goog.testing.mockmatchers'], false); +goog.addDependency('editor/table.js', ['goog.editor.Table', 'goog.editor.TableCell', 'goog.editor.TableRow'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.log', 'goog.string.Unicode', 'goog.style'], false); +goog.addDependency('editor/table_test.js', ['goog.editor.TableTest'], ['goog.dom', 'goog.editor.Table', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/actioneventwrapper.js', ['goog.events.actionEventWrapper'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.dom', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.EventWrapper', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('events/actioneventwrapper_test.js', ['goog.events.actionEventWrapperTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.events', 'goog.events.EventHandler', 'goog.events.KeyCodes', 'goog.events.actionEventWrapper', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('events/actionhandler.js', ['goog.events.ActionEvent', 'goog.events.ActionHandler', 'goog.events.ActionHandler.EventType', 'goog.events.BeforeActionEvent'], ['goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('events/actionhandler_test.js', ['goog.events.ActionHandlerTest'], ['goog.dom', 'goog.events', 'goog.events.ActionHandler', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('events/browserevent.js', ['goog.events.BrowserEvent', 'goog.events.BrowserEvent.MouseButton'], ['goog.events.BrowserFeature', 'goog.events.Event', 'goog.events.EventType', 'goog.reflect', 'goog.userAgent'], false); +goog.addDependency('events/browserevent_test.js', ['goog.events.BrowserEventTest'], ['goog.events.BrowserEvent', 'goog.events.BrowserFeature', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/browserfeature.js', ['goog.events.BrowserFeature'], ['goog.userAgent'], false); +goog.addDependency('events/event.js', ['goog.events.Event', 'goog.events.EventLike'], ['goog.Disposable', 'goog.events.EventId'], false); +goog.addDependency('events/event_test.js', ['goog.events.EventTest'], ['goog.events.Event', 'goog.events.EventId', 'goog.events.EventTarget', 'goog.testing.jsunit'], false); +goog.addDependency('events/eventhandler.js', ['goog.events.EventHandler'], ['goog.Disposable', 'goog.events', 'goog.object'], false); +goog.addDependency('events/eventhandler_test.js', ['goog.events.EventHandlerTest'], ['goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('events/eventid.js', ['goog.events.EventId'], [], false); +goog.addDependency('events/events.js', ['goog.events', 'goog.events.CaptureSimulationMode', 'goog.events.Key', 'goog.events.ListenableType'], ['goog.asserts', 'goog.debug.entryPointRegistry', 'goog.events.BrowserEvent', 'goog.events.BrowserFeature', 'goog.events.Listenable', 'goog.events.ListenerMap'], false); +goog.addDependency('events/events_test.js', ['goog.eventsTest'], ['goog.asserts.AssertionError', 'goog.debug.EntryPointMonitor', 'goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.dom', 'goog.events', 'goog.events.BrowserFeature', 'goog.events.CaptureSimulationMode', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.Listener', 'goog.functions', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('events/eventtarget.js', ['goog.events.EventTarget'], ['goog.Disposable', 'goog.asserts', 'goog.events', 'goog.events.Event', 'goog.events.Listenable', 'goog.events.ListenerMap', 'goog.object'], false); +goog.addDependency('events/eventtarget_test.js', ['goog.events.EventTargetTest'], ['goog.events.EventTarget', 'goog.events.Listenable', 'goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType', 'goog.testing.jsunit'], false); +goog.addDependency('events/eventtarget_via_googevents_test.js', ['goog.events.EventTargetGoogEventsTest'], ['goog.events', 'goog.events.EventTarget', 'goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType', 'goog.testing', 'goog.testing.jsunit'], false); +goog.addDependency('events/eventtarget_via_w3cinterface_test.js', ['goog.events.EventTargetW3CTest'], ['goog.events.EventTarget', 'goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType', 'goog.testing.jsunit'], false); +goog.addDependency('events/eventtargettester.js', ['goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType'], ['goog.array', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.testing.asserts', 'goog.testing.recordFunction'], false); +goog.addDependency('events/eventtype.js', ['goog.events.EventType'], ['goog.userAgent'], false); +goog.addDependency('events/eventwrapper.js', ['goog.events.EventWrapper'], [], false); +goog.addDependency('events/filedrophandler.js', ['goog.events.FileDropHandler', 'goog.events.FileDropHandler.EventType'], ['goog.array', 'goog.dom', 'goog.events.BrowserEvent', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.log', 'goog.log.Level'], false); +goog.addDependency('events/filedrophandler_test.js', ['goog.events.FileDropHandlerTest'], ['goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.FileDropHandler', 'goog.testing.jsunit'], false); +goog.addDependency('events/focushandler.js', ['goog.events.FocusHandler', 'goog.events.FocusHandler.EventType'], ['goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.userAgent'], false); +goog.addDependency('events/imehandler.js', ['goog.events.ImeHandler', 'goog.events.ImeHandler.Event', 'goog.events.ImeHandler.EventType'], ['goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('events/imehandler_test.js', ['goog.events.ImeHandlerTest'], ['goog.array', 'goog.dom', 'goog.events', 'goog.events.ImeHandler', 'goog.events.KeyCodes', 'goog.object', 'goog.string', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/inputhandler.js', ['goog.events.InputHandler', 'goog.events.InputHandler.EventType'], ['goog.Timer', 'goog.dom', 'goog.events.BrowserEvent', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('events/inputhandler_test.js', ['goog.events.InputHandlerTest'], ['goog.dom', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('events/keycodes.js', ['goog.events.KeyCodes'], ['goog.userAgent'], false); +goog.addDependency('events/keycodes_test.js', ['goog.events.KeyCodesTest'], ['goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.object', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/keyhandler.js', ['goog.events.KeyEvent', 'goog.events.KeyHandler', 'goog.events.KeyHandler.EventType'], ['goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.userAgent'], false); +goog.addDependency('events/keyhandler_test.js', ['goog.events.KeyEventTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/keynames.js', ['goog.events.KeyNames'], [], false); +goog.addDependency('events/listenable.js', ['goog.events.Listenable', 'goog.events.ListenableKey'], ['goog.events.EventId'], false); +goog.addDependency('events/listenable_test.js', ['goog.events.ListenableTest'], ['goog.events.Listenable', 'goog.testing.jsunit'], false); +goog.addDependency('events/listener.js', ['goog.events.Listener'], ['goog.events.ListenableKey'], false); +goog.addDependency('events/listenermap.js', ['goog.events.ListenerMap'], ['goog.array', 'goog.events.Listener', 'goog.object'], false); +goog.addDependency('events/listenermap_test.js', ['goog.events.ListenerMapTest'], ['goog.dispose', 'goog.events', 'goog.events.EventId', 'goog.events.EventTarget', 'goog.events.ListenerMap', 'goog.testing.jsunit'], false); +goog.addDependency('events/mousewheelhandler.js', ['goog.events.MouseWheelEvent', 'goog.events.MouseWheelHandler', 'goog.events.MouseWheelHandler.EventType'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.math', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('events/mousewheelhandler_test.js', ['goog.events.MouseWheelHandlerTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.MouseWheelEvent', 'goog.events.MouseWheelHandler', 'goog.functions', 'goog.string', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/onlinehandler.js', ['goog.events.OnlineHandler', 'goog.events.OnlineHandler.EventType'], ['goog.Timer', 'goog.events.BrowserFeature', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.net.NetworkStatusMonitor'], false); +goog.addDependency('events/onlinelistener_test.js', ['goog.events.OnlineHandlerTest'], ['goog.events', 'goog.events.BrowserFeature', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.OnlineHandler', 'goog.net.NetworkStatusMonitor', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('events/pastehandler.js', ['goog.events.PasteHandler', 'goog.events.PasteHandler.EventType', 'goog.events.PasteHandler.State'], ['goog.Timer', 'goog.async.ConditionalDelay', 'goog.events.BrowserEvent', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.log', 'goog.userAgent'], false); +goog.addDependency('events/pastehandler_test.js', ['goog.events.PasteHandlerTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.PasteHandler', 'goog.testing.MockClock', 'goog.testing.MockUserAgent', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('events/wheelevent.js', ['goog.events.WheelEvent'], ['goog.asserts', 'goog.events.BrowserEvent'], false); +goog.addDependency('events/wheelhandler.js', ['goog.events.WheelHandler'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.WheelEvent', 'goog.style', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('events/wheelhandler_test.js', ['goog.events.WheelHandlerTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.WheelEvent', 'goog.events.WheelHandler', 'goog.string', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('format/emailaddress.js', ['goog.format.EmailAddress'], ['goog.string'], false); +goog.addDependency('format/emailaddress_test.js', ['goog.format.EmailAddressTest'], ['goog.array', 'goog.format.EmailAddress', 'goog.testing.jsunit'], false); +goog.addDependency('format/format.js', ['goog.format'], ['goog.i18n.GraphemeBreak', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('format/format_test.js', ['goog.formatTest'], ['goog.dom', 'goog.format', 'goog.string', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('format/htmlprettyprinter.js', ['goog.format.HtmlPrettyPrinter', 'goog.format.HtmlPrettyPrinter.Buffer'], ['goog.object', 'goog.string.StringBuffer'], false); +goog.addDependency('format/htmlprettyprinter_test.js', ['goog.format.HtmlPrettyPrinterTest'], ['goog.format.HtmlPrettyPrinter', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('format/internationalizedemailaddress.js', ['goog.format.InternationalizedEmailAddress'], ['goog.format.EmailAddress', 'goog.string'], false); +goog.addDependency('format/internationalizedemailaddress_test.js', ['goog.format.InternationalizedEmailAddressTest'], ['goog.array', 'goog.format.InternationalizedEmailAddress', 'goog.testing.jsunit'], false); +goog.addDependency('format/jsonprettyprinter.js', ['goog.format.JsonPrettyPrinter', 'goog.format.JsonPrettyPrinter.HtmlDelimiters', 'goog.format.JsonPrettyPrinter.TextDelimiters'], ['goog.json', 'goog.json.Serializer', 'goog.string', 'goog.string.StringBuffer', 'goog.string.format'], false); +goog.addDependency('format/jsonprettyprinter_test.js', ['goog.format.JsonPrettyPrinterTest'], ['goog.format.JsonPrettyPrinter', 'goog.testing.jsunit'], false); +goog.addDependency('fs/entry.js', ['goog.fs.DirectoryEntry', 'goog.fs.DirectoryEntry.Behavior', 'goog.fs.Entry', 'goog.fs.FileEntry'], [], false); +goog.addDependency('fs/entryimpl.js', ['goog.fs.DirectoryEntryImpl', 'goog.fs.EntryImpl', 'goog.fs.FileEntryImpl'], ['goog.array', 'goog.async.Deferred', 'goog.fs.DirectoryEntry', 'goog.fs.Entry', 'goog.fs.Error', 'goog.fs.FileEntry', 'goog.fs.FileWriter', 'goog.functions', 'goog.string'], false); +goog.addDependency('fs/error.js', ['goog.fs.Error', 'goog.fs.Error.ErrorCode'], ['goog.debug.Error', 'goog.object', 'goog.string'], false); +goog.addDependency('fs/filereader.js', ['goog.fs.FileReader', 'goog.fs.FileReader.EventType', 'goog.fs.FileReader.ReadyState'], ['goog.async.Deferred', 'goog.events.EventTarget', 'goog.fs.Error', 'goog.fs.ProgressEvent'], false); +goog.addDependency('fs/filesaver.js', ['goog.fs.FileSaver', 'goog.fs.FileSaver.EventType', 'goog.fs.FileSaver.ReadyState'], ['goog.events.EventTarget', 'goog.fs.Error', 'goog.fs.ProgressEvent'], false); +goog.addDependency('fs/filesystem.js', ['goog.fs.FileSystem'], [], false); +goog.addDependency('fs/filesystemimpl.js', ['goog.fs.FileSystemImpl'], ['goog.fs.DirectoryEntryImpl', 'goog.fs.FileSystem'], false); +goog.addDependency('fs/filewriter.js', ['goog.fs.FileWriter'], ['goog.fs.Error', 'goog.fs.FileSaver'], false); +goog.addDependency('fs/fs.js', ['goog.fs'], ['goog.array', 'goog.async.Deferred', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.fs.FileSystemImpl', 'goog.userAgent'], false); +goog.addDependency('fs/fs_test.js', ['goog.fsTest'], ['goog.array', 'goog.async.Deferred', 'goog.async.DeferredList', 'goog.dom', 'goog.events', 'goog.fs', 'goog.fs.DirectoryEntry', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.fs.FileSaver', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('fs/progressevent.js', ['goog.fs.ProgressEvent'], ['goog.events.Event'], false); +goog.addDependency('functions/functions.js', ['goog.functions'], [], false); +goog.addDependency('functions/functions_test.js', ['goog.functionsTest'], ['goog.functions', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('fx/abstractdragdrop.js', ['goog.fx.AbstractDragDrop', 'goog.fx.AbstractDragDrop.EventType', 'goog.fx.DragDropEvent', 'goog.fx.DragDropItem'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.fx.Dragger', 'goog.math.Box', 'goog.math.Coordinate', 'goog.style'], false); +goog.addDependency('fx/abstractdragdrop_test.js', ['goog.fx.AbstractDragDropTest'], ['goog.array', 'goog.events.EventType', 'goog.functions', 'goog.fx.AbstractDragDrop', 'goog.fx.DragDropItem', 'goog.math.Box', 'goog.math.Coordinate', 'goog.style', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('fx/anim/anim.js', ['goog.fx.anim', 'goog.fx.anim.Animated'], ['goog.async.AnimationDelay', 'goog.async.Delay', 'goog.object'], false); +goog.addDependency('fx/anim/anim_test.js', ['goog.fx.animTest'], ['goog.async.AnimationDelay', 'goog.async.Delay', 'goog.events', 'goog.functions', 'goog.fx.Animation', 'goog.fx.anim', 'goog.object', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('fx/animation.js', ['goog.fx.Animation', 'goog.fx.Animation.EventType', 'goog.fx.Animation.State', 'goog.fx.AnimationEvent'], ['goog.array', 'goog.events.Event', 'goog.fx.Transition', 'goog.fx.TransitionBase', 'goog.fx.anim', 'goog.fx.anim.Animated'], false); +goog.addDependency('fx/animation_test.js', ['goog.fx.AnimationTest'], ['goog.events', 'goog.fx.Animation', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('fx/animationqueue.js', ['goog.fx.AnimationParallelQueue', 'goog.fx.AnimationQueue', 'goog.fx.AnimationSerialQueue'], ['goog.array', 'goog.asserts', 'goog.events', 'goog.fx.Transition', 'goog.fx.TransitionBase'], false); +goog.addDependency('fx/animationqueue_test.js', ['goog.fx.AnimationQueueTest'], ['goog.events', 'goog.fx.Animation', 'goog.fx.AnimationParallelQueue', 'goog.fx.AnimationSerialQueue', 'goog.fx.Transition', 'goog.fx.anim', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('fx/css3/fx.js', ['goog.fx.css3'], ['goog.fx.css3.Transition'], false); +goog.addDependency('fx/css3/transition.js', ['goog.fx.css3.Transition'], ['goog.Timer', 'goog.asserts', 'goog.fx.TransitionBase', 'goog.style', 'goog.style.transition'], false); +goog.addDependency('fx/css3/transition_test.js', ['goog.fx.css3.TransitionTest'], ['goog.dispose', 'goog.dom', 'goog.events', 'goog.fx.Transition', 'goog.fx.css3.Transition', 'goog.style.transition', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('fx/cssspriteanimation.js', ['goog.fx.CssSpriteAnimation'], ['goog.fx.Animation'], false); +goog.addDependency('fx/cssspriteanimation_test.js', ['goog.fx.CssSpriteAnimationTest'], ['goog.fx.CssSpriteAnimation', 'goog.math.Box', 'goog.math.Size', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('fx/dom.js', ['goog.fx.dom', 'goog.fx.dom.BgColorTransform', 'goog.fx.dom.ColorTransform', 'goog.fx.dom.Fade', 'goog.fx.dom.FadeIn', 'goog.fx.dom.FadeInAndShow', 'goog.fx.dom.FadeOut', 'goog.fx.dom.FadeOutAndHide', 'goog.fx.dom.PredefinedEffect', 'goog.fx.dom.Resize', 'goog.fx.dom.ResizeHeight', 'goog.fx.dom.ResizeWidth', 'goog.fx.dom.Scroll', 'goog.fx.dom.Slide', 'goog.fx.dom.SlideFrom', 'goog.fx.dom.Swipe'], ['goog.color', 'goog.events', 'goog.fx.Animation', 'goog.fx.Transition', 'goog.style', 'goog.style.bidi'], false); +goog.addDependency('fx/dragdrop.js', ['goog.fx.DragDrop'], ['goog.fx.AbstractDragDrop', 'goog.fx.DragDropItem'], false); +goog.addDependency('fx/dragdropgroup.js', ['goog.fx.DragDropGroup'], ['goog.dom', 'goog.fx.AbstractDragDrop', 'goog.fx.DragDropItem'], false); +goog.addDependency('fx/dragdropgroup_test.js', ['goog.fx.DragDropGroupTest'], ['goog.events', 'goog.fx.DragDropGroup', 'goog.testing.jsunit'], false); +goog.addDependency('fx/dragger.js', ['goog.fx.DragEvent', 'goog.fx.Dragger', 'goog.fx.Dragger.EventType'], ['goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.math.Coordinate', 'goog.math.Rect', 'goog.style', 'goog.style.bidi', 'goog.userAgent'], false); +goog.addDependency('fx/dragger_test.js', ['goog.fx.DraggerTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.Event', 'goog.events.EventType', 'goog.fx.Dragger', 'goog.math.Rect', 'goog.style.bidi', 'goog.testing.StrictMock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('fx/draglistgroup.js', ['goog.fx.DragListDirection', 'goog.fx.DragListGroup', 'goog.fx.DragListGroup.EventType', 'goog.fx.DragListGroupEvent'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.fx.Dragger', 'goog.math.Coordinate', 'goog.string', 'goog.style'], false); +goog.addDependency('fx/draglistgroup_test.js', ['goog.fx.DragListGroupTest'], ['goog.array', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.BrowserFeature', 'goog.events.Event', 'goog.events.EventType', 'goog.fx.DragEvent', 'goog.fx.DragListDirection', 'goog.fx.DragListGroup', 'goog.fx.Dragger', 'goog.math.Coordinate', 'goog.object', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('fx/dragscrollsupport.js', ['goog.fx.DragScrollSupport'], ['goog.Disposable', 'goog.Timer', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.math.Coordinate', 'goog.style'], false); +goog.addDependency('fx/dragscrollsupport_test.js', ['goog.fx.DragScrollSupportTest'], ['goog.fx.DragScrollSupport', 'goog.math.Coordinate', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit'], false); +goog.addDependency('fx/easing.js', ['goog.fx.easing'], [], false); +goog.addDependency('fx/easing_test.js', ['goog.fx.easingTest'], ['goog.fx.easing', 'goog.testing.jsunit'], false); +goog.addDependency('fx/fx.js', ['goog.fx'], ['goog.asserts', 'goog.fx.Animation', 'goog.fx.Animation.EventType', 'goog.fx.Animation.State', 'goog.fx.AnimationEvent', 'goog.fx.Transition.EventType', 'goog.fx.easing'], false); +goog.addDependency('fx/fx_test.js', ['goog.fxTest'], ['goog.fx.Animation', 'goog.object', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('fx/transition.js', ['goog.fx.Transition', 'goog.fx.Transition.EventType'], [], false); +goog.addDependency('fx/transitionbase.js', ['goog.fx.TransitionBase', 'goog.fx.TransitionBase.State'], ['goog.events.EventTarget', 'goog.fx.Transition'], false); +goog.addDependency('graphics/abstractgraphics.js', ['goog.graphics.AbstractGraphics'], ['goog.dom', 'goog.graphics.Path', 'goog.math.Coordinate', 'goog.math.Size', 'goog.style', 'goog.ui.Component'], false); +goog.addDependency('graphics/affinetransform.js', ['goog.graphics.AffineTransform'], ['goog.math'], false); +goog.addDependency('graphics/canvaselement.js', ['goog.graphics.CanvasEllipseElement', 'goog.graphics.CanvasGroupElement', 'goog.graphics.CanvasImageElement', 'goog.graphics.CanvasPathElement', 'goog.graphics.CanvasRectElement', 'goog.graphics.CanvasTextElement'], ['goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.graphics.EllipseElement', 'goog.graphics.GroupElement', 'goog.graphics.ImageElement', 'goog.graphics.Path', 'goog.graphics.PathElement', 'goog.graphics.RectElement', 'goog.graphics.TextElement', 'goog.math', 'goog.string'], false); +goog.addDependency('graphics/canvasgraphics.js', ['goog.graphics.CanvasGraphics'], ['goog.events.EventType', 'goog.graphics.AbstractGraphics', 'goog.graphics.CanvasEllipseElement', 'goog.graphics.CanvasGroupElement', 'goog.graphics.CanvasImageElement', 'goog.graphics.CanvasPathElement', 'goog.graphics.CanvasRectElement', 'goog.graphics.CanvasTextElement', 'goog.graphics.SolidFill', 'goog.math.Size', 'goog.style'], false); +goog.addDependency('graphics/element.js', ['goog.graphics.Element'], ['goog.asserts', 'goog.events', 'goog.events.EventTarget', 'goog.events.Listenable', 'goog.graphics.AffineTransform', 'goog.math'], false); +goog.addDependency('graphics/ellipseelement.js', ['goog.graphics.EllipseElement'], ['goog.graphics.StrokeAndFillElement'], false); +goog.addDependency('graphics/ext/coordinates.js', ['goog.graphics.ext.coordinates'], ['goog.string'], false); +goog.addDependency('graphics/ext/element.js', ['goog.graphics.ext.Element'], ['goog.events.EventTarget', 'goog.functions', 'goog.graphics.ext.coordinates'], false); +goog.addDependency('graphics/ext/ellipse.js', ['goog.graphics.ext.Ellipse'], ['goog.graphics.ext.StrokeAndFillElement'], false); +goog.addDependency('graphics/ext/ext.js', ['goog.graphics.ext'], ['goog.graphics.ext.Ellipse', 'goog.graphics.ext.Graphics', 'goog.graphics.ext.Group', 'goog.graphics.ext.Image', 'goog.graphics.ext.Rectangle', 'goog.graphics.ext.Shape', 'goog.graphics.ext.coordinates'], false); +goog.addDependency('graphics/ext/graphics.js', ['goog.graphics.ext.Graphics'], ['goog.events', 'goog.events.EventType', 'goog.graphics', 'goog.graphics.ext.Group'], false); +goog.addDependency('graphics/ext/group.js', ['goog.graphics.ext.Group'], ['goog.array', 'goog.graphics.ext.Element'], false); +goog.addDependency('graphics/ext/image.js', ['goog.graphics.ext.Image'], ['goog.graphics.ext.Element'], false); +goog.addDependency('graphics/ext/path.js', ['goog.graphics.ext.Path'], ['goog.graphics.AffineTransform', 'goog.graphics.Path', 'goog.math.Rect'], false); +goog.addDependency('graphics/ext/rectangle.js', ['goog.graphics.ext.Rectangle'], ['goog.graphics.ext.StrokeAndFillElement'], false); +goog.addDependency('graphics/ext/shape.js', ['goog.graphics.ext.Shape'], ['goog.graphics.ext.StrokeAndFillElement'], false); +goog.addDependency('graphics/ext/strokeandfillelement.js', ['goog.graphics.ext.StrokeAndFillElement'], ['goog.graphics.ext.Element'], false); +goog.addDependency('graphics/fill.js', ['goog.graphics.Fill'], [], false); +goog.addDependency('graphics/font.js', ['goog.graphics.Font'], [], false); +goog.addDependency('graphics/graphics.js', ['goog.graphics'], ['goog.dom', 'goog.graphics.CanvasGraphics', 'goog.graphics.SvgGraphics', 'goog.graphics.VmlGraphics', 'goog.userAgent'], false); +goog.addDependency('graphics/groupelement.js', ['goog.graphics.GroupElement'], ['goog.graphics.Element'], false); +goog.addDependency('graphics/imageelement.js', ['goog.graphics.ImageElement'], ['goog.graphics.Element'], false); +goog.addDependency('graphics/lineargradient.js', ['goog.graphics.LinearGradient'], ['goog.asserts', 'goog.graphics.Fill'], false); +goog.addDependency('graphics/path.js', ['goog.graphics.Path', 'goog.graphics.Path.Segment'], ['goog.array', 'goog.math'], false); +goog.addDependency('graphics/pathelement.js', ['goog.graphics.PathElement'], ['goog.graphics.StrokeAndFillElement'], false); +goog.addDependency('graphics/paths.js', ['goog.graphics.paths'], ['goog.graphics.Path', 'goog.math.Coordinate'], false); +goog.addDependency('graphics/rectelement.js', ['goog.graphics.RectElement'], ['goog.graphics.StrokeAndFillElement'], false); +goog.addDependency('graphics/solidfill.js', ['goog.graphics.SolidFill'], ['goog.graphics.Fill'], false); +goog.addDependency('graphics/stroke.js', ['goog.graphics.Stroke'], [], false); +goog.addDependency('graphics/strokeandfillelement.js', ['goog.graphics.StrokeAndFillElement'], ['goog.graphics.Element'], false); +goog.addDependency('graphics/svgelement.js', ['goog.graphics.SvgEllipseElement', 'goog.graphics.SvgGroupElement', 'goog.graphics.SvgImageElement', 'goog.graphics.SvgPathElement', 'goog.graphics.SvgRectElement', 'goog.graphics.SvgTextElement'], ['goog.dom', 'goog.graphics.EllipseElement', 'goog.graphics.GroupElement', 'goog.graphics.ImageElement', 'goog.graphics.PathElement', 'goog.graphics.RectElement', 'goog.graphics.TextElement'], false); +goog.addDependency('graphics/svggraphics.js', ['goog.graphics.SvgGraphics'], ['goog.Timer', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.graphics.AbstractGraphics', 'goog.graphics.LinearGradient', 'goog.graphics.Path', 'goog.graphics.SolidFill', 'goog.graphics.Stroke', 'goog.graphics.SvgEllipseElement', 'goog.graphics.SvgGroupElement', 'goog.graphics.SvgImageElement', 'goog.graphics.SvgPathElement', 'goog.graphics.SvgRectElement', 'goog.graphics.SvgTextElement', 'goog.math', 'goog.math.Size', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('graphics/textelement.js', ['goog.graphics.TextElement'], ['goog.graphics.StrokeAndFillElement'], false); +goog.addDependency('graphics/vmlelement.js', ['goog.graphics.VmlEllipseElement', 'goog.graphics.VmlGroupElement', 'goog.graphics.VmlImageElement', 'goog.graphics.VmlPathElement', 'goog.graphics.VmlRectElement', 'goog.graphics.VmlTextElement'], ['goog.dom', 'goog.graphics.EllipseElement', 'goog.graphics.GroupElement', 'goog.graphics.ImageElement', 'goog.graphics.PathElement', 'goog.graphics.RectElement', 'goog.graphics.TextElement'], false); +goog.addDependency('graphics/vmlgraphics.js', ['goog.graphics.VmlGraphics'], ['goog.array', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.graphics.AbstractGraphics', 'goog.graphics.LinearGradient', 'goog.graphics.Path', 'goog.graphics.SolidFill', 'goog.graphics.VmlEllipseElement', 'goog.graphics.VmlGroupElement', 'goog.graphics.VmlImageElement', 'goog.graphics.VmlPathElement', 'goog.graphics.VmlRectElement', 'goog.graphics.VmlTextElement', 'goog.math', 'goog.math.Size', 'goog.string', 'goog.style'], false); +goog.addDependency('history/event.js', ['goog.history.Event'], ['goog.events.Event', 'goog.history.EventType'], false); +goog.addDependency('history/eventtype.js', ['goog.history.EventType'], [], false); +goog.addDependency('history/history.js', ['goog.History', 'goog.History.Event', 'goog.History.EventType'], ['goog.Timer', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.history.Event', 'goog.history.EventType', 'goog.labs.userAgent.device', 'goog.memoize', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('history/history_test.js', ['goog.HistoryTest'], ['goog.History', 'goog.dispose', 'goog.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('history/html5history.js', ['goog.history.Html5History', 'goog.history.Html5History.TokenTransformer'], ['goog.asserts', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.history.Event'], false); +goog.addDependency('history/html5history_test.js', ['goog.history.Html5HistoryTest'], ['goog.history.Html5History', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.mockmatchers'], false); +goog.addDependency('html/flash.js', ['goog.html.flash'], ['goog.asserts', 'goog.html.SafeHtml'], false); +goog.addDependency('html/flash_test.js', ['goog.html.flashTest'], ['goog.html.SafeHtml', 'goog.html.TrustedResourceUrl', 'goog.html.flash', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/legacyconversions.js', ['goog.html.legacyconversions'], ['goog.html.SafeHtml', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl'], false); +goog.addDependency('html/legacyconversions_test.js', ['goog.html.legacyconversionsTest'], ['goog.html.SafeHtml', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.legacyconversions', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('html/safehtml.js', ['goog.html.SafeHtml'], ['goog.array', 'goog.asserts', 'goog.dom.tags', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.DirectionalString', 'goog.object', 'goog.string', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/safehtml_test.js', ['goog.html.safeHtmlTest'], ['goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.testing', 'goog.i18n.bidi.Dir', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/safescript.js', ['goog.html.SafeScript'], ['goog.asserts', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/safescript_test.js', ['goog.html.safeScriptTest'], ['goog.html.SafeScript', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/safestyle.js', ['goog.html.SafeStyle'], ['goog.array', 'goog.asserts', 'goog.string', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/safestyle_test.js', ['goog.html.safeStyleTest'], ['goog.html.SafeStyle', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/safestylesheet.js', ['goog.html.SafeStyleSheet'], ['goog.array', 'goog.asserts', 'goog.string', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/safestylesheet_test.js', ['goog.html.safeStyleSheetTest'], ['goog.html.SafeStyleSheet', 'goog.string', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/safeurl.js', ['goog.html.SafeUrl'], ['goog.asserts', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.DirectionalString', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/safeurl_test.js', ['goog.html.safeUrlTest'], ['goog.html.SafeUrl', 'goog.i18n.bidi.Dir', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/silverlight.js', ['goog.html.silverlight'], ['goog.html.SafeHtml', 'goog.html.TrustedResourceUrl', 'goog.html.flash', 'goog.string.Const'], false); +goog.addDependency('html/silverlight_test.js', ['goog.html.silverlightTest'], ['goog.html.SafeHtml', 'goog.html.TrustedResourceUrl', 'goog.html.silverlight', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/testing.js', ['goog.html.testing'], ['goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl'], false); +goog.addDependency('html/trustedresourceurl.js', ['goog.html.TrustedResourceUrl'], ['goog.asserts', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.DirectionalString', 'goog.string.Const', 'goog.string.TypedString'], false); +goog.addDependency('html/trustedresourceurl_test.js', ['goog.html.trustedResourceUrlTest'], ['goog.html.TrustedResourceUrl', 'goog.i18n.bidi.Dir', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/uncheckedconversions.js', ['goog.html.uncheckedconversions'], ['goog.asserts', 'goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.string', 'goog.string.Const'], false); +goog.addDependency('html/uncheckedconversions_test.js', ['goog.html.uncheckedconversionsTest'], ['goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.uncheckedconversions', 'goog.i18n.bidi.Dir', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('html/utils.js', ['goog.html.utils'], ['goog.string'], false); +goog.addDependency('html/utils_test.js', ['goog.html.UtilsTest'], ['goog.array', 'goog.dom.TagName', 'goog.html.utils', 'goog.object', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/bidi.js', ['goog.i18n.bidi', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.DirectionalString', 'goog.i18n.bidi.Format'], [], false); +goog.addDependency('i18n/bidi_test.js', ['goog.i18n.bidiTest'], ['goog.i18n.bidi', 'goog.i18n.bidi.Dir', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/bidiformatter.js', ['goog.i18n.BidiFormatter'], ['goog.html.SafeHtml', 'goog.html.legacyconversions', 'goog.i18n.bidi', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.Format'], false); +goog.addDependency('i18n/bidiformatter_test.js', ['goog.i18n.BidiFormatterTest'], ['goog.html.SafeHtml', 'goog.i18n.BidiFormatter', 'goog.i18n.bidi.Dir', 'goog.i18n.bidi.Format', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/charlistdecompressor.js', ['goog.i18n.CharListDecompressor'], ['goog.array', 'goog.i18n.uChar'], false); +goog.addDependency('i18n/charlistdecompressor_test.js', ['goog.i18n.CharListDecompressorTest'], ['goog.i18n.CharListDecompressor', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/charpickerdata.js', ['goog.i18n.CharPickerData'], [], false); +goog.addDependency('i18n/collation.js', ['goog.i18n.collation'], [], false); +goog.addDependency('i18n/collation_test.js', ['goog.i18n.collationTest'], ['goog.i18n.collation', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('i18n/compactnumberformatsymbols.js', ['goog.i18n.CompactNumberFormatSymbols', 'goog.i18n.CompactNumberFormatSymbols_af', 'goog.i18n.CompactNumberFormatSymbols_af_ZA', 'goog.i18n.CompactNumberFormatSymbols_am', 'goog.i18n.CompactNumberFormatSymbols_am_ET', 'goog.i18n.CompactNumberFormatSymbols_ar', 'goog.i18n.CompactNumberFormatSymbols_ar_001', 'goog.i18n.CompactNumberFormatSymbols_az', 'goog.i18n.CompactNumberFormatSymbols_az_Latn_AZ', 'goog.i18n.CompactNumberFormatSymbols_bg', 'goog.i18n.CompactNumberFormatSymbols_bg_BG', 'goog.i18n.CompactNumberFormatSymbols_bn', 'goog.i18n.CompactNumberFormatSymbols_bn_BD', 'goog.i18n.CompactNumberFormatSymbols_br', 'goog.i18n.CompactNumberFormatSymbols_br_FR', 'goog.i18n.CompactNumberFormatSymbols_ca', 'goog.i18n.CompactNumberFormatSymbols_ca_AD', 'goog.i18n.CompactNumberFormatSymbols_ca_ES', 'goog.i18n.CompactNumberFormatSymbols_ca_ES_VALENCIA', 'goog.i18n.CompactNumberFormatSymbols_ca_FR', 'goog.i18n.CompactNumberFormatSymbols_ca_IT', 'goog.i18n.CompactNumberFormatSymbols_chr', 'goog.i18n.CompactNumberFormatSymbols_chr_US', 'goog.i18n.CompactNumberFormatSymbols_cs', 'goog.i18n.CompactNumberFormatSymbols_cs_CZ', 'goog.i18n.CompactNumberFormatSymbols_cy', 'goog.i18n.CompactNumberFormatSymbols_cy_GB', 'goog.i18n.CompactNumberFormatSymbols_da', 'goog.i18n.CompactNumberFormatSymbols_da_DK', 'goog.i18n.CompactNumberFormatSymbols_da_GL', 'goog.i18n.CompactNumberFormatSymbols_de', 'goog.i18n.CompactNumberFormatSymbols_de_AT', 'goog.i18n.CompactNumberFormatSymbols_de_BE', 'goog.i18n.CompactNumberFormatSymbols_de_CH', 'goog.i18n.CompactNumberFormatSymbols_de_DE', 'goog.i18n.CompactNumberFormatSymbols_de_LU', 'goog.i18n.CompactNumberFormatSymbols_el', 'goog.i18n.CompactNumberFormatSymbols_el_GR', 'goog.i18n.CompactNumberFormatSymbols_en', 'goog.i18n.CompactNumberFormatSymbols_en_001', 'goog.i18n.CompactNumberFormatSymbols_en_AS', 'goog.i18n.CompactNumberFormatSymbols_en_AU', 'goog.i18n.CompactNumberFormatSymbols_en_DG', 'goog.i18n.CompactNumberFormatSymbols_en_FM', 'goog.i18n.CompactNumberFormatSymbols_en_GB', 'goog.i18n.CompactNumberFormatSymbols_en_GU', 'goog.i18n.CompactNumberFormatSymbols_en_IE', 'goog.i18n.CompactNumberFormatSymbols_en_IN', 'goog.i18n.CompactNumberFormatSymbols_en_IO', 'goog.i18n.CompactNumberFormatSymbols_en_MH', 'goog.i18n.CompactNumberFormatSymbols_en_MP', 'goog.i18n.CompactNumberFormatSymbols_en_PR', 'goog.i18n.CompactNumberFormatSymbols_en_PW', 'goog.i18n.CompactNumberFormatSymbols_en_SG', 'goog.i18n.CompactNumberFormatSymbols_en_TC', 'goog.i18n.CompactNumberFormatSymbols_en_UM', 'goog.i18n.CompactNumberFormatSymbols_en_US', 'goog.i18n.CompactNumberFormatSymbols_en_VG', 'goog.i18n.CompactNumberFormatSymbols_en_VI', 'goog.i18n.CompactNumberFormatSymbols_en_ZA', 'goog.i18n.CompactNumberFormatSymbols_en_ZW', 'goog.i18n.CompactNumberFormatSymbols_es', 'goog.i18n.CompactNumberFormatSymbols_es_419', 'goog.i18n.CompactNumberFormatSymbols_es_EA', 'goog.i18n.CompactNumberFormatSymbols_es_ES', 'goog.i18n.CompactNumberFormatSymbols_es_IC', 'goog.i18n.CompactNumberFormatSymbols_et', 'goog.i18n.CompactNumberFormatSymbols_et_EE', 'goog.i18n.CompactNumberFormatSymbols_eu', 'goog.i18n.CompactNumberFormatSymbols_eu_ES', 'goog.i18n.CompactNumberFormatSymbols_fa', 'goog.i18n.CompactNumberFormatSymbols_fa_IR', 'goog.i18n.CompactNumberFormatSymbols_fi', 'goog.i18n.CompactNumberFormatSymbols_fi_FI', 'goog.i18n.CompactNumberFormatSymbols_fil', 'goog.i18n.CompactNumberFormatSymbols_fil_PH', 'goog.i18n.CompactNumberFormatSymbols_fr', 'goog.i18n.CompactNumberFormatSymbols_fr_BL', 'goog.i18n.CompactNumberFormatSymbols_fr_CA', 'goog.i18n.CompactNumberFormatSymbols_fr_FR', 'goog.i18n.CompactNumberFormatSymbols_fr_GF', 'goog.i18n.CompactNumberFormatSymbols_fr_GP', 'goog.i18n.CompactNumberFormatSymbols_fr_MC', 'goog.i18n.CompactNumberFormatSymbols_fr_MF', 'goog.i18n.CompactNumberFormatSymbols_fr_MQ', 'goog.i18n.CompactNumberFormatSymbols_fr_PM', 'goog.i18n.CompactNumberFormatSymbols_fr_RE', 'goog.i18n.CompactNumberFormatSymbols_fr_YT', 'goog.i18n.CompactNumberFormatSymbols_ga', 'goog.i18n.CompactNumberFormatSymbols_ga_IE', 'goog.i18n.CompactNumberFormatSymbols_gl', 'goog.i18n.CompactNumberFormatSymbols_gl_ES', 'goog.i18n.CompactNumberFormatSymbols_gsw', 'goog.i18n.CompactNumberFormatSymbols_gsw_CH', 'goog.i18n.CompactNumberFormatSymbols_gsw_LI', 'goog.i18n.CompactNumberFormatSymbols_gu', 'goog.i18n.CompactNumberFormatSymbols_gu_IN', 'goog.i18n.CompactNumberFormatSymbols_haw', 'goog.i18n.CompactNumberFormatSymbols_haw_US', 'goog.i18n.CompactNumberFormatSymbols_he', 'goog.i18n.CompactNumberFormatSymbols_he_IL', 'goog.i18n.CompactNumberFormatSymbols_hi', 'goog.i18n.CompactNumberFormatSymbols_hi_IN', 'goog.i18n.CompactNumberFormatSymbols_hr', 'goog.i18n.CompactNumberFormatSymbols_hr_HR', 'goog.i18n.CompactNumberFormatSymbols_hu', 'goog.i18n.CompactNumberFormatSymbols_hu_HU', 'goog.i18n.CompactNumberFormatSymbols_hy', 'goog.i18n.CompactNumberFormatSymbols_hy_AM', 'goog.i18n.CompactNumberFormatSymbols_id', 'goog.i18n.CompactNumberFormatSymbols_id_ID', 'goog.i18n.CompactNumberFormatSymbols_in', 'goog.i18n.CompactNumberFormatSymbols_is', 'goog.i18n.CompactNumberFormatSymbols_is_IS', 'goog.i18n.CompactNumberFormatSymbols_it', 'goog.i18n.CompactNumberFormatSymbols_it_IT', 'goog.i18n.CompactNumberFormatSymbols_it_SM', 'goog.i18n.CompactNumberFormatSymbols_iw', 'goog.i18n.CompactNumberFormatSymbols_ja', 'goog.i18n.CompactNumberFormatSymbols_ja_JP', 'goog.i18n.CompactNumberFormatSymbols_ka', 'goog.i18n.CompactNumberFormatSymbols_ka_GE', 'goog.i18n.CompactNumberFormatSymbols_kk', 'goog.i18n.CompactNumberFormatSymbols_kk_Cyrl_KZ', 'goog.i18n.CompactNumberFormatSymbols_km', 'goog.i18n.CompactNumberFormatSymbols_km_KH', 'goog.i18n.CompactNumberFormatSymbols_kn', 'goog.i18n.CompactNumberFormatSymbols_kn_IN', 'goog.i18n.CompactNumberFormatSymbols_ko', 'goog.i18n.CompactNumberFormatSymbols_ko_KR', 'goog.i18n.CompactNumberFormatSymbols_ky', 'goog.i18n.CompactNumberFormatSymbols_ky_Cyrl_KG', 'goog.i18n.CompactNumberFormatSymbols_ln', 'goog.i18n.CompactNumberFormatSymbols_ln_CD', 'goog.i18n.CompactNumberFormatSymbols_lo', 'goog.i18n.CompactNumberFormatSymbols_lo_LA', 'goog.i18n.CompactNumberFormatSymbols_lt', 'goog.i18n.CompactNumberFormatSymbols_lt_LT', 'goog.i18n.CompactNumberFormatSymbols_lv', 'goog.i18n.CompactNumberFormatSymbols_lv_LV', 'goog.i18n.CompactNumberFormatSymbols_mk', 'goog.i18n.CompactNumberFormatSymbols_mk_MK', 'goog.i18n.CompactNumberFormatSymbols_ml', 'goog.i18n.CompactNumberFormatSymbols_ml_IN', 'goog.i18n.CompactNumberFormatSymbols_mn', 'goog.i18n.CompactNumberFormatSymbols_mn_Cyrl_MN', 'goog.i18n.CompactNumberFormatSymbols_mr', 'goog.i18n.CompactNumberFormatSymbols_mr_IN', 'goog.i18n.CompactNumberFormatSymbols_ms', 'goog.i18n.CompactNumberFormatSymbols_ms_Latn_MY', 'goog.i18n.CompactNumberFormatSymbols_mt', 'goog.i18n.CompactNumberFormatSymbols_mt_MT', 'goog.i18n.CompactNumberFormatSymbols_my', 'goog.i18n.CompactNumberFormatSymbols_my_MM', 'goog.i18n.CompactNumberFormatSymbols_nb', 'goog.i18n.CompactNumberFormatSymbols_nb_NO', 'goog.i18n.CompactNumberFormatSymbols_nb_SJ', 'goog.i18n.CompactNumberFormatSymbols_ne', 'goog.i18n.CompactNumberFormatSymbols_ne_NP', 'goog.i18n.CompactNumberFormatSymbols_nl', 'goog.i18n.CompactNumberFormatSymbols_nl_NL', 'goog.i18n.CompactNumberFormatSymbols_no', 'goog.i18n.CompactNumberFormatSymbols_no_NO', 'goog.i18n.CompactNumberFormatSymbols_or', 'goog.i18n.CompactNumberFormatSymbols_or_IN', 'goog.i18n.CompactNumberFormatSymbols_pa', 'goog.i18n.CompactNumberFormatSymbols_pa_Guru_IN', 'goog.i18n.CompactNumberFormatSymbols_pl', 'goog.i18n.CompactNumberFormatSymbols_pl_PL', 'goog.i18n.CompactNumberFormatSymbols_pt', 'goog.i18n.CompactNumberFormatSymbols_pt_BR', 'goog.i18n.CompactNumberFormatSymbols_pt_PT', 'goog.i18n.CompactNumberFormatSymbols_ro', 'goog.i18n.CompactNumberFormatSymbols_ro_RO', 'goog.i18n.CompactNumberFormatSymbols_ru', 'goog.i18n.CompactNumberFormatSymbols_ru_RU', 'goog.i18n.CompactNumberFormatSymbols_si', 'goog.i18n.CompactNumberFormatSymbols_si_LK', 'goog.i18n.CompactNumberFormatSymbols_sk', 'goog.i18n.CompactNumberFormatSymbols_sk_SK', 'goog.i18n.CompactNumberFormatSymbols_sl', 'goog.i18n.CompactNumberFormatSymbols_sl_SI', 'goog.i18n.CompactNumberFormatSymbols_sq', 'goog.i18n.CompactNumberFormatSymbols_sq_AL', 'goog.i18n.CompactNumberFormatSymbols_sr', 'goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_RS', 'goog.i18n.CompactNumberFormatSymbols_sv', 'goog.i18n.CompactNumberFormatSymbols_sv_SE', 'goog.i18n.CompactNumberFormatSymbols_sw', 'goog.i18n.CompactNumberFormatSymbols_sw_TZ', 'goog.i18n.CompactNumberFormatSymbols_ta', 'goog.i18n.CompactNumberFormatSymbols_ta_IN', 'goog.i18n.CompactNumberFormatSymbols_te', 'goog.i18n.CompactNumberFormatSymbols_te_IN', 'goog.i18n.CompactNumberFormatSymbols_th', 'goog.i18n.CompactNumberFormatSymbols_th_TH', 'goog.i18n.CompactNumberFormatSymbols_tl', 'goog.i18n.CompactNumberFormatSymbols_tr', 'goog.i18n.CompactNumberFormatSymbols_tr_TR', 'goog.i18n.CompactNumberFormatSymbols_uk', 'goog.i18n.CompactNumberFormatSymbols_uk_UA', 'goog.i18n.CompactNumberFormatSymbols_ur', 'goog.i18n.CompactNumberFormatSymbols_ur_PK', 'goog.i18n.CompactNumberFormatSymbols_uz', 'goog.i18n.CompactNumberFormatSymbols_uz_Latn_UZ', 'goog.i18n.CompactNumberFormatSymbols_vi', 'goog.i18n.CompactNumberFormatSymbols_vi_VN', 'goog.i18n.CompactNumberFormatSymbols_zh', 'goog.i18n.CompactNumberFormatSymbols_zh_CN', 'goog.i18n.CompactNumberFormatSymbols_zh_HK', 'goog.i18n.CompactNumberFormatSymbols_zh_Hans_CN', 'goog.i18n.CompactNumberFormatSymbols_zh_TW', 'goog.i18n.CompactNumberFormatSymbols_zu', 'goog.i18n.CompactNumberFormatSymbols_zu_ZA'], [], false); +goog.addDependency('i18n/compactnumberformatsymbols_ext.js', ['goog.i18n.CompactNumberFormatSymbolsExt', 'goog.i18n.CompactNumberFormatSymbols_aa', 'goog.i18n.CompactNumberFormatSymbols_aa_DJ', 'goog.i18n.CompactNumberFormatSymbols_aa_ER', 'goog.i18n.CompactNumberFormatSymbols_aa_ET', 'goog.i18n.CompactNumberFormatSymbols_af_NA', 'goog.i18n.CompactNumberFormatSymbols_agq', 'goog.i18n.CompactNumberFormatSymbols_agq_CM', 'goog.i18n.CompactNumberFormatSymbols_ak', 'goog.i18n.CompactNumberFormatSymbols_ak_GH', 'goog.i18n.CompactNumberFormatSymbols_ar_AE', 'goog.i18n.CompactNumberFormatSymbols_ar_BH', 'goog.i18n.CompactNumberFormatSymbols_ar_DJ', 'goog.i18n.CompactNumberFormatSymbols_ar_DZ', 'goog.i18n.CompactNumberFormatSymbols_ar_EG', 'goog.i18n.CompactNumberFormatSymbols_ar_EH', 'goog.i18n.CompactNumberFormatSymbols_ar_ER', 'goog.i18n.CompactNumberFormatSymbols_ar_IL', 'goog.i18n.CompactNumberFormatSymbols_ar_IQ', 'goog.i18n.CompactNumberFormatSymbols_ar_JO', 'goog.i18n.CompactNumberFormatSymbols_ar_KM', 'goog.i18n.CompactNumberFormatSymbols_ar_KW', 'goog.i18n.CompactNumberFormatSymbols_ar_LB', 'goog.i18n.CompactNumberFormatSymbols_ar_LY', 'goog.i18n.CompactNumberFormatSymbols_ar_MA', 'goog.i18n.CompactNumberFormatSymbols_ar_MR', 'goog.i18n.CompactNumberFormatSymbols_ar_OM', 'goog.i18n.CompactNumberFormatSymbols_ar_PS', 'goog.i18n.CompactNumberFormatSymbols_ar_QA', 'goog.i18n.CompactNumberFormatSymbols_ar_SA', 'goog.i18n.CompactNumberFormatSymbols_ar_SD', 'goog.i18n.CompactNumberFormatSymbols_ar_SO', 'goog.i18n.CompactNumberFormatSymbols_ar_SS', 'goog.i18n.CompactNumberFormatSymbols_ar_SY', 'goog.i18n.CompactNumberFormatSymbols_ar_TD', 'goog.i18n.CompactNumberFormatSymbols_ar_TN', 'goog.i18n.CompactNumberFormatSymbols_ar_YE', 'goog.i18n.CompactNumberFormatSymbols_as', 'goog.i18n.CompactNumberFormatSymbols_as_IN', 'goog.i18n.CompactNumberFormatSymbols_asa', 'goog.i18n.CompactNumberFormatSymbols_asa_TZ', 'goog.i18n.CompactNumberFormatSymbols_ast', 'goog.i18n.CompactNumberFormatSymbols_ast_ES', 'goog.i18n.CompactNumberFormatSymbols_az_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_az_Cyrl_AZ', 'goog.i18n.CompactNumberFormatSymbols_az_Latn', 'goog.i18n.CompactNumberFormatSymbols_bas', 'goog.i18n.CompactNumberFormatSymbols_bas_CM', 'goog.i18n.CompactNumberFormatSymbols_be', 'goog.i18n.CompactNumberFormatSymbols_be_BY', 'goog.i18n.CompactNumberFormatSymbols_bem', 'goog.i18n.CompactNumberFormatSymbols_bem_ZM', 'goog.i18n.CompactNumberFormatSymbols_bez', 'goog.i18n.CompactNumberFormatSymbols_bez_TZ', 'goog.i18n.CompactNumberFormatSymbols_bm', 'goog.i18n.CompactNumberFormatSymbols_bm_Latn', 'goog.i18n.CompactNumberFormatSymbols_bm_Latn_ML', 'goog.i18n.CompactNumberFormatSymbols_bn_IN', 'goog.i18n.CompactNumberFormatSymbols_bo', 'goog.i18n.CompactNumberFormatSymbols_bo_CN', 'goog.i18n.CompactNumberFormatSymbols_bo_IN', 'goog.i18n.CompactNumberFormatSymbols_brx', 'goog.i18n.CompactNumberFormatSymbols_brx_IN', 'goog.i18n.CompactNumberFormatSymbols_bs', 'goog.i18n.CompactNumberFormatSymbols_bs_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_bs_Cyrl_BA', 'goog.i18n.CompactNumberFormatSymbols_bs_Latn', 'goog.i18n.CompactNumberFormatSymbols_bs_Latn_BA', 'goog.i18n.CompactNumberFormatSymbols_cgg', 'goog.i18n.CompactNumberFormatSymbols_cgg_UG', 'goog.i18n.CompactNumberFormatSymbols_ckb', 'goog.i18n.CompactNumberFormatSymbols_ckb_Arab', 'goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IQ', 'goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IR', 'goog.i18n.CompactNumberFormatSymbols_ckb_IQ', 'goog.i18n.CompactNumberFormatSymbols_ckb_IR', 'goog.i18n.CompactNumberFormatSymbols_ckb_Latn', 'goog.i18n.CompactNumberFormatSymbols_ckb_Latn_IQ', 'goog.i18n.CompactNumberFormatSymbols_dav', 'goog.i18n.CompactNumberFormatSymbols_dav_KE', 'goog.i18n.CompactNumberFormatSymbols_de_LI', 'goog.i18n.CompactNumberFormatSymbols_dje', 'goog.i18n.CompactNumberFormatSymbols_dje_NE', 'goog.i18n.CompactNumberFormatSymbols_dsb', 'goog.i18n.CompactNumberFormatSymbols_dsb_DE', 'goog.i18n.CompactNumberFormatSymbols_dua', 'goog.i18n.CompactNumberFormatSymbols_dua_CM', 'goog.i18n.CompactNumberFormatSymbols_dyo', 'goog.i18n.CompactNumberFormatSymbols_dyo_SN', 'goog.i18n.CompactNumberFormatSymbols_dz', 'goog.i18n.CompactNumberFormatSymbols_dz_BT', 'goog.i18n.CompactNumberFormatSymbols_ebu', 'goog.i18n.CompactNumberFormatSymbols_ebu_KE', 'goog.i18n.CompactNumberFormatSymbols_ee', 'goog.i18n.CompactNumberFormatSymbols_ee_GH', 'goog.i18n.CompactNumberFormatSymbols_ee_TG', 'goog.i18n.CompactNumberFormatSymbols_el_CY', 'goog.i18n.CompactNumberFormatSymbols_en_150', 'goog.i18n.CompactNumberFormatSymbols_en_AG', 'goog.i18n.CompactNumberFormatSymbols_en_AI', 'goog.i18n.CompactNumberFormatSymbols_en_BB', 'goog.i18n.CompactNumberFormatSymbols_en_BE', 'goog.i18n.CompactNumberFormatSymbols_en_BM', 'goog.i18n.CompactNumberFormatSymbols_en_BS', 'goog.i18n.CompactNumberFormatSymbols_en_BW', 'goog.i18n.CompactNumberFormatSymbols_en_BZ', 'goog.i18n.CompactNumberFormatSymbols_en_CA', 'goog.i18n.CompactNumberFormatSymbols_en_CC', 'goog.i18n.CompactNumberFormatSymbols_en_CK', 'goog.i18n.CompactNumberFormatSymbols_en_CM', 'goog.i18n.CompactNumberFormatSymbols_en_CX', 'goog.i18n.CompactNumberFormatSymbols_en_DM', 'goog.i18n.CompactNumberFormatSymbols_en_ER', 'goog.i18n.CompactNumberFormatSymbols_en_FJ', 'goog.i18n.CompactNumberFormatSymbols_en_FK', 'goog.i18n.CompactNumberFormatSymbols_en_GD', 'goog.i18n.CompactNumberFormatSymbols_en_GG', 'goog.i18n.CompactNumberFormatSymbols_en_GH', 'goog.i18n.CompactNumberFormatSymbols_en_GI', 'goog.i18n.CompactNumberFormatSymbols_en_GM', 'goog.i18n.CompactNumberFormatSymbols_en_GY', 'goog.i18n.CompactNumberFormatSymbols_en_HK', 'goog.i18n.CompactNumberFormatSymbols_en_IM', 'goog.i18n.CompactNumberFormatSymbols_en_JE', 'goog.i18n.CompactNumberFormatSymbols_en_JM', 'goog.i18n.CompactNumberFormatSymbols_en_KE', 'goog.i18n.CompactNumberFormatSymbols_en_KI', 'goog.i18n.CompactNumberFormatSymbols_en_KN', 'goog.i18n.CompactNumberFormatSymbols_en_KY', 'goog.i18n.CompactNumberFormatSymbols_en_LC', 'goog.i18n.CompactNumberFormatSymbols_en_LR', 'goog.i18n.CompactNumberFormatSymbols_en_LS', 'goog.i18n.CompactNumberFormatSymbols_en_MG', 'goog.i18n.CompactNumberFormatSymbols_en_MO', 'goog.i18n.CompactNumberFormatSymbols_en_MS', 'goog.i18n.CompactNumberFormatSymbols_en_MT', 'goog.i18n.CompactNumberFormatSymbols_en_MU', 'goog.i18n.CompactNumberFormatSymbols_en_MW', 'goog.i18n.CompactNumberFormatSymbols_en_MY', 'goog.i18n.CompactNumberFormatSymbols_en_NA', 'goog.i18n.CompactNumberFormatSymbols_en_NF', 'goog.i18n.CompactNumberFormatSymbols_en_NG', 'goog.i18n.CompactNumberFormatSymbols_en_NR', 'goog.i18n.CompactNumberFormatSymbols_en_NU', 'goog.i18n.CompactNumberFormatSymbols_en_NZ', 'goog.i18n.CompactNumberFormatSymbols_en_PG', 'goog.i18n.CompactNumberFormatSymbols_en_PH', 'goog.i18n.CompactNumberFormatSymbols_en_PK', 'goog.i18n.CompactNumberFormatSymbols_en_PN', 'goog.i18n.CompactNumberFormatSymbols_en_RW', 'goog.i18n.CompactNumberFormatSymbols_en_SB', 'goog.i18n.CompactNumberFormatSymbols_en_SC', 'goog.i18n.CompactNumberFormatSymbols_en_SD', 'goog.i18n.CompactNumberFormatSymbols_en_SH', 'goog.i18n.CompactNumberFormatSymbols_en_SL', 'goog.i18n.CompactNumberFormatSymbols_en_SS', 'goog.i18n.CompactNumberFormatSymbols_en_SX', 'goog.i18n.CompactNumberFormatSymbols_en_SZ', 'goog.i18n.CompactNumberFormatSymbols_en_TK', 'goog.i18n.CompactNumberFormatSymbols_en_TO', 'goog.i18n.CompactNumberFormatSymbols_en_TT', 'goog.i18n.CompactNumberFormatSymbols_en_TV', 'goog.i18n.CompactNumberFormatSymbols_en_TZ', 'goog.i18n.CompactNumberFormatSymbols_en_UG', 'goog.i18n.CompactNumberFormatSymbols_en_VC', 'goog.i18n.CompactNumberFormatSymbols_en_VU', 'goog.i18n.CompactNumberFormatSymbols_en_WS', 'goog.i18n.CompactNumberFormatSymbols_en_ZM', 'goog.i18n.CompactNumberFormatSymbols_eo', 'goog.i18n.CompactNumberFormatSymbols_eo_001', 'goog.i18n.CompactNumberFormatSymbols_es_AR', 'goog.i18n.CompactNumberFormatSymbols_es_BO', 'goog.i18n.CompactNumberFormatSymbols_es_CL', 'goog.i18n.CompactNumberFormatSymbols_es_CO', 'goog.i18n.CompactNumberFormatSymbols_es_CR', 'goog.i18n.CompactNumberFormatSymbols_es_CU', 'goog.i18n.CompactNumberFormatSymbols_es_DO', 'goog.i18n.CompactNumberFormatSymbols_es_EC', 'goog.i18n.CompactNumberFormatSymbols_es_GQ', 'goog.i18n.CompactNumberFormatSymbols_es_GT', 'goog.i18n.CompactNumberFormatSymbols_es_HN', 'goog.i18n.CompactNumberFormatSymbols_es_MX', 'goog.i18n.CompactNumberFormatSymbols_es_NI', 'goog.i18n.CompactNumberFormatSymbols_es_PA', 'goog.i18n.CompactNumberFormatSymbols_es_PE', 'goog.i18n.CompactNumberFormatSymbols_es_PH', 'goog.i18n.CompactNumberFormatSymbols_es_PR', 'goog.i18n.CompactNumberFormatSymbols_es_PY', 'goog.i18n.CompactNumberFormatSymbols_es_SV', 'goog.i18n.CompactNumberFormatSymbols_es_US', 'goog.i18n.CompactNumberFormatSymbols_es_UY', 'goog.i18n.CompactNumberFormatSymbols_es_VE', 'goog.i18n.CompactNumberFormatSymbols_ewo', 'goog.i18n.CompactNumberFormatSymbols_ewo_CM', 'goog.i18n.CompactNumberFormatSymbols_fa_AF', 'goog.i18n.CompactNumberFormatSymbols_ff', 'goog.i18n.CompactNumberFormatSymbols_ff_CM', 'goog.i18n.CompactNumberFormatSymbols_ff_GN', 'goog.i18n.CompactNumberFormatSymbols_ff_MR', 'goog.i18n.CompactNumberFormatSymbols_ff_SN', 'goog.i18n.CompactNumberFormatSymbols_fo', 'goog.i18n.CompactNumberFormatSymbols_fo_FO', 'goog.i18n.CompactNumberFormatSymbols_fr_BE', 'goog.i18n.CompactNumberFormatSymbols_fr_BF', 'goog.i18n.CompactNumberFormatSymbols_fr_BI', 'goog.i18n.CompactNumberFormatSymbols_fr_BJ', 'goog.i18n.CompactNumberFormatSymbols_fr_CD', 'goog.i18n.CompactNumberFormatSymbols_fr_CF', 'goog.i18n.CompactNumberFormatSymbols_fr_CG', 'goog.i18n.CompactNumberFormatSymbols_fr_CH', 'goog.i18n.CompactNumberFormatSymbols_fr_CI', 'goog.i18n.CompactNumberFormatSymbols_fr_CM', 'goog.i18n.CompactNumberFormatSymbols_fr_DJ', 'goog.i18n.CompactNumberFormatSymbols_fr_DZ', 'goog.i18n.CompactNumberFormatSymbols_fr_GA', 'goog.i18n.CompactNumberFormatSymbols_fr_GN', 'goog.i18n.CompactNumberFormatSymbols_fr_GQ', 'goog.i18n.CompactNumberFormatSymbols_fr_HT', 'goog.i18n.CompactNumberFormatSymbols_fr_KM', 'goog.i18n.CompactNumberFormatSymbols_fr_LU', 'goog.i18n.CompactNumberFormatSymbols_fr_MA', 'goog.i18n.CompactNumberFormatSymbols_fr_MG', 'goog.i18n.CompactNumberFormatSymbols_fr_ML', 'goog.i18n.CompactNumberFormatSymbols_fr_MR', 'goog.i18n.CompactNumberFormatSymbols_fr_MU', 'goog.i18n.CompactNumberFormatSymbols_fr_NC', 'goog.i18n.CompactNumberFormatSymbols_fr_NE', 'goog.i18n.CompactNumberFormatSymbols_fr_PF', 'goog.i18n.CompactNumberFormatSymbols_fr_RW', 'goog.i18n.CompactNumberFormatSymbols_fr_SC', 'goog.i18n.CompactNumberFormatSymbols_fr_SN', 'goog.i18n.CompactNumberFormatSymbols_fr_SY', 'goog.i18n.CompactNumberFormatSymbols_fr_TD', 'goog.i18n.CompactNumberFormatSymbols_fr_TG', 'goog.i18n.CompactNumberFormatSymbols_fr_TN', 'goog.i18n.CompactNumberFormatSymbols_fr_VU', 'goog.i18n.CompactNumberFormatSymbols_fr_WF', 'goog.i18n.CompactNumberFormatSymbols_fur', 'goog.i18n.CompactNumberFormatSymbols_fur_IT', 'goog.i18n.CompactNumberFormatSymbols_fy', 'goog.i18n.CompactNumberFormatSymbols_fy_NL', 'goog.i18n.CompactNumberFormatSymbols_gd', 'goog.i18n.CompactNumberFormatSymbols_gd_GB', 'goog.i18n.CompactNumberFormatSymbols_gsw_FR', 'goog.i18n.CompactNumberFormatSymbols_guz', 'goog.i18n.CompactNumberFormatSymbols_guz_KE', 'goog.i18n.CompactNumberFormatSymbols_gv', 'goog.i18n.CompactNumberFormatSymbols_gv_IM', 'goog.i18n.CompactNumberFormatSymbols_ha', 'goog.i18n.CompactNumberFormatSymbols_ha_Latn', 'goog.i18n.CompactNumberFormatSymbols_ha_Latn_GH', 'goog.i18n.CompactNumberFormatSymbols_ha_Latn_NE', 'goog.i18n.CompactNumberFormatSymbols_ha_Latn_NG', 'goog.i18n.CompactNumberFormatSymbols_hr_BA', 'goog.i18n.CompactNumberFormatSymbols_hsb', 'goog.i18n.CompactNumberFormatSymbols_hsb_DE', 'goog.i18n.CompactNumberFormatSymbols_ia', 'goog.i18n.CompactNumberFormatSymbols_ia_FR', 'goog.i18n.CompactNumberFormatSymbols_ig', 'goog.i18n.CompactNumberFormatSymbols_ig_NG', 'goog.i18n.CompactNumberFormatSymbols_ii', 'goog.i18n.CompactNumberFormatSymbols_ii_CN', 'goog.i18n.CompactNumberFormatSymbols_it_CH', 'goog.i18n.CompactNumberFormatSymbols_jgo', 'goog.i18n.CompactNumberFormatSymbols_jgo_CM', 'goog.i18n.CompactNumberFormatSymbols_jmc', 'goog.i18n.CompactNumberFormatSymbols_jmc_TZ', 'goog.i18n.CompactNumberFormatSymbols_kab', 'goog.i18n.CompactNumberFormatSymbols_kab_DZ', 'goog.i18n.CompactNumberFormatSymbols_kam', 'goog.i18n.CompactNumberFormatSymbols_kam_KE', 'goog.i18n.CompactNumberFormatSymbols_kde', 'goog.i18n.CompactNumberFormatSymbols_kde_TZ', 'goog.i18n.CompactNumberFormatSymbols_kea', 'goog.i18n.CompactNumberFormatSymbols_kea_CV', 'goog.i18n.CompactNumberFormatSymbols_khq', 'goog.i18n.CompactNumberFormatSymbols_khq_ML', 'goog.i18n.CompactNumberFormatSymbols_ki', 'goog.i18n.CompactNumberFormatSymbols_ki_KE', 'goog.i18n.CompactNumberFormatSymbols_kk_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_kkj', 'goog.i18n.CompactNumberFormatSymbols_kkj_CM', 'goog.i18n.CompactNumberFormatSymbols_kl', 'goog.i18n.CompactNumberFormatSymbols_kl_GL', 'goog.i18n.CompactNumberFormatSymbols_kln', 'goog.i18n.CompactNumberFormatSymbols_kln_KE', 'goog.i18n.CompactNumberFormatSymbols_ko_KP', 'goog.i18n.CompactNumberFormatSymbols_kok', 'goog.i18n.CompactNumberFormatSymbols_kok_IN', 'goog.i18n.CompactNumberFormatSymbols_ks', 'goog.i18n.CompactNumberFormatSymbols_ks_Arab', 'goog.i18n.CompactNumberFormatSymbols_ks_Arab_IN', 'goog.i18n.CompactNumberFormatSymbols_ksb', 'goog.i18n.CompactNumberFormatSymbols_ksb_TZ', 'goog.i18n.CompactNumberFormatSymbols_ksf', 'goog.i18n.CompactNumberFormatSymbols_ksf_CM', 'goog.i18n.CompactNumberFormatSymbols_ksh', 'goog.i18n.CompactNumberFormatSymbols_ksh_DE', 'goog.i18n.CompactNumberFormatSymbols_kw', 'goog.i18n.CompactNumberFormatSymbols_kw_GB', 'goog.i18n.CompactNumberFormatSymbols_ky_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_lag', 'goog.i18n.CompactNumberFormatSymbols_lag_TZ', 'goog.i18n.CompactNumberFormatSymbols_lb', 'goog.i18n.CompactNumberFormatSymbols_lb_LU', 'goog.i18n.CompactNumberFormatSymbols_lg', 'goog.i18n.CompactNumberFormatSymbols_lg_UG', 'goog.i18n.CompactNumberFormatSymbols_lkt', 'goog.i18n.CompactNumberFormatSymbols_lkt_US', 'goog.i18n.CompactNumberFormatSymbols_ln_AO', 'goog.i18n.CompactNumberFormatSymbols_ln_CF', 'goog.i18n.CompactNumberFormatSymbols_ln_CG', 'goog.i18n.CompactNumberFormatSymbols_lu', 'goog.i18n.CompactNumberFormatSymbols_lu_CD', 'goog.i18n.CompactNumberFormatSymbols_luo', 'goog.i18n.CompactNumberFormatSymbols_luo_KE', 'goog.i18n.CompactNumberFormatSymbols_luy', 'goog.i18n.CompactNumberFormatSymbols_luy_KE', 'goog.i18n.CompactNumberFormatSymbols_mas', 'goog.i18n.CompactNumberFormatSymbols_mas_KE', 'goog.i18n.CompactNumberFormatSymbols_mas_TZ', 'goog.i18n.CompactNumberFormatSymbols_mer', 'goog.i18n.CompactNumberFormatSymbols_mer_KE', 'goog.i18n.CompactNumberFormatSymbols_mfe', 'goog.i18n.CompactNumberFormatSymbols_mfe_MU', 'goog.i18n.CompactNumberFormatSymbols_mg', 'goog.i18n.CompactNumberFormatSymbols_mg_MG', 'goog.i18n.CompactNumberFormatSymbols_mgh', 'goog.i18n.CompactNumberFormatSymbols_mgh_MZ', 'goog.i18n.CompactNumberFormatSymbols_mgo', 'goog.i18n.CompactNumberFormatSymbols_mgo_CM', 'goog.i18n.CompactNumberFormatSymbols_mn_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_ms_Latn', 'goog.i18n.CompactNumberFormatSymbols_ms_Latn_BN', 'goog.i18n.CompactNumberFormatSymbols_ms_Latn_SG', 'goog.i18n.CompactNumberFormatSymbols_mua', 'goog.i18n.CompactNumberFormatSymbols_mua_CM', 'goog.i18n.CompactNumberFormatSymbols_naq', 'goog.i18n.CompactNumberFormatSymbols_naq_NA', 'goog.i18n.CompactNumberFormatSymbols_nd', 'goog.i18n.CompactNumberFormatSymbols_nd_ZW', 'goog.i18n.CompactNumberFormatSymbols_ne_IN', 'goog.i18n.CompactNumberFormatSymbols_nl_AW', 'goog.i18n.CompactNumberFormatSymbols_nl_BE', 'goog.i18n.CompactNumberFormatSymbols_nl_BQ', 'goog.i18n.CompactNumberFormatSymbols_nl_CW', 'goog.i18n.CompactNumberFormatSymbols_nl_SR', 'goog.i18n.CompactNumberFormatSymbols_nl_SX', 'goog.i18n.CompactNumberFormatSymbols_nmg', 'goog.i18n.CompactNumberFormatSymbols_nmg_CM', 'goog.i18n.CompactNumberFormatSymbols_nn', 'goog.i18n.CompactNumberFormatSymbols_nn_NO', 'goog.i18n.CompactNumberFormatSymbols_nnh', 'goog.i18n.CompactNumberFormatSymbols_nnh_CM', 'goog.i18n.CompactNumberFormatSymbols_nr', 'goog.i18n.CompactNumberFormatSymbols_nr_ZA', 'goog.i18n.CompactNumberFormatSymbols_nso', 'goog.i18n.CompactNumberFormatSymbols_nso_ZA', 'goog.i18n.CompactNumberFormatSymbols_nus', 'goog.i18n.CompactNumberFormatSymbols_nus_SD', 'goog.i18n.CompactNumberFormatSymbols_nyn', 'goog.i18n.CompactNumberFormatSymbols_nyn_UG', 'goog.i18n.CompactNumberFormatSymbols_om', 'goog.i18n.CompactNumberFormatSymbols_om_ET', 'goog.i18n.CompactNumberFormatSymbols_om_KE', 'goog.i18n.CompactNumberFormatSymbols_os', 'goog.i18n.CompactNumberFormatSymbols_os_GE', 'goog.i18n.CompactNumberFormatSymbols_os_RU', 'goog.i18n.CompactNumberFormatSymbols_pa_Arab', 'goog.i18n.CompactNumberFormatSymbols_pa_Arab_PK', 'goog.i18n.CompactNumberFormatSymbols_pa_Guru', 'goog.i18n.CompactNumberFormatSymbols_ps', 'goog.i18n.CompactNumberFormatSymbols_ps_AF', 'goog.i18n.CompactNumberFormatSymbols_pt_AO', 'goog.i18n.CompactNumberFormatSymbols_pt_CV', 'goog.i18n.CompactNumberFormatSymbols_pt_GW', 'goog.i18n.CompactNumberFormatSymbols_pt_MO', 'goog.i18n.CompactNumberFormatSymbols_pt_MZ', 'goog.i18n.CompactNumberFormatSymbols_pt_ST', 'goog.i18n.CompactNumberFormatSymbols_pt_TL', 'goog.i18n.CompactNumberFormatSymbols_qu', 'goog.i18n.CompactNumberFormatSymbols_qu_BO', 'goog.i18n.CompactNumberFormatSymbols_qu_EC', 'goog.i18n.CompactNumberFormatSymbols_qu_PE', 'goog.i18n.CompactNumberFormatSymbols_rm', 'goog.i18n.CompactNumberFormatSymbols_rm_CH', 'goog.i18n.CompactNumberFormatSymbols_rn', 'goog.i18n.CompactNumberFormatSymbols_rn_BI', 'goog.i18n.CompactNumberFormatSymbols_ro_MD', 'goog.i18n.CompactNumberFormatSymbols_rof', 'goog.i18n.CompactNumberFormatSymbols_rof_TZ', 'goog.i18n.CompactNumberFormatSymbols_ru_BY', 'goog.i18n.CompactNumberFormatSymbols_ru_KG', 'goog.i18n.CompactNumberFormatSymbols_ru_KZ', 'goog.i18n.CompactNumberFormatSymbols_ru_MD', 'goog.i18n.CompactNumberFormatSymbols_ru_UA', 'goog.i18n.CompactNumberFormatSymbols_rw', 'goog.i18n.CompactNumberFormatSymbols_rw_RW', 'goog.i18n.CompactNumberFormatSymbols_rwk', 'goog.i18n.CompactNumberFormatSymbols_rwk_TZ', 'goog.i18n.CompactNumberFormatSymbols_sah', 'goog.i18n.CompactNumberFormatSymbols_sah_RU', 'goog.i18n.CompactNumberFormatSymbols_saq', 'goog.i18n.CompactNumberFormatSymbols_saq_KE', 'goog.i18n.CompactNumberFormatSymbols_sbp', 'goog.i18n.CompactNumberFormatSymbols_sbp_TZ', 'goog.i18n.CompactNumberFormatSymbols_se', 'goog.i18n.CompactNumberFormatSymbols_se_FI', 'goog.i18n.CompactNumberFormatSymbols_se_NO', 'goog.i18n.CompactNumberFormatSymbols_se_SE', 'goog.i18n.CompactNumberFormatSymbols_seh', 'goog.i18n.CompactNumberFormatSymbols_seh_MZ', 'goog.i18n.CompactNumberFormatSymbols_ses', 'goog.i18n.CompactNumberFormatSymbols_ses_ML', 'goog.i18n.CompactNumberFormatSymbols_sg', 'goog.i18n.CompactNumberFormatSymbols_sg_CF', 'goog.i18n.CompactNumberFormatSymbols_shi', 'goog.i18n.CompactNumberFormatSymbols_shi_Latn', 'goog.i18n.CompactNumberFormatSymbols_shi_Latn_MA', 'goog.i18n.CompactNumberFormatSymbols_shi_Tfng', 'goog.i18n.CompactNumberFormatSymbols_shi_Tfng_MA', 'goog.i18n.CompactNumberFormatSymbols_smn', 'goog.i18n.CompactNumberFormatSymbols_smn_FI', 'goog.i18n.CompactNumberFormatSymbols_sn', 'goog.i18n.CompactNumberFormatSymbols_sn_ZW', 'goog.i18n.CompactNumberFormatSymbols_so', 'goog.i18n.CompactNumberFormatSymbols_so_DJ', 'goog.i18n.CompactNumberFormatSymbols_so_ET', 'goog.i18n.CompactNumberFormatSymbols_so_KE', 'goog.i18n.CompactNumberFormatSymbols_so_SO', 'goog.i18n.CompactNumberFormatSymbols_sq_MK', 'goog.i18n.CompactNumberFormatSymbols_sq_XK', 'goog.i18n.CompactNumberFormatSymbols_sr_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_BA', 'goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_ME', 'goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_XK', 'goog.i18n.CompactNumberFormatSymbols_sr_Latn', 'goog.i18n.CompactNumberFormatSymbols_sr_Latn_BA', 'goog.i18n.CompactNumberFormatSymbols_sr_Latn_ME', 'goog.i18n.CompactNumberFormatSymbols_sr_Latn_RS', 'goog.i18n.CompactNumberFormatSymbols_sr_Latn_XK', 'goog.i18n.CompactNumberFormatSymbols_ss', 'goog.i18n.CompactNumberFormatSymbols_ss_SZ', 'goog.i18n.CompactNumberFormatSymbols_ss_ZA', 'goog.i18n.CompactNumberFormatSymbols_ssy', 'goog.i18n.CompactNumberFormatSymbols_ssy_ER', 'goog.i18n.CompactNumberFormatSymbols_sv_AX', 'goog.i18n.CompactNumberFormatSymbols_sv_FI', 'goog.i18n.CompactNumberFormatSymbols_sw_KE', 'goog.i18n.CompactNumberFormatSymbols_sw_UG', 'goog.i18n.CompactNumberFormatSymbols_swc', 'goog.i18n.CompactNumberFormatSymbols_swc_CD', 'goog.i18n.CompactNumberFormatSymbols_ta_LK', 'goog.i18n.CompactNumberFormatSymbols_ta_MY', 'goog.i18n.CompactNumberFormatSymbols_ta_SG', 'goog.i18n.CompactNumberFormatSymbols_teo', 'goog.i18n.CompactNumberFormatSymbols_teo_KE', 'goog.i18n.CompactNumberFormatSymbols_teo_UG', 'goog.i18n.CompactNumberFormatSymbols_ti', 'goog.i18n.CompactNumberFormatSymbols_ti_ER', 'goog.i18n.CompactNumberFormatSymbols_ti_ET', 'goog.i18n.CompactNumberFormatSymbols_tn', 'goog.i18n.CompactNumberFormatSymbols_tn_BW', 'goog.i18n.CompactNumberFormatSymbols_tn_ZA', 'goog.i18n.CompactNumberFormatSymbols_to', 'goog.i18n.CompactNumberFormatSymbols_to_TO', 'goog.i18n.CompactNumberFormatSymbols_tr_CY', 'goog.i18n.CompactNumberFormatSymbols_ts', 'goog.i18n.CompactNumberFormatSymbols_ts_ZA', 'goog.i18n.CompactNumberFormatSymbols_twq', 'goog.i18n.CompactNumberFormatSymbols_twq_NE', 'goog.i18n.CompactNumberFormatSymbols_tzm', 'goog.i18n.CompactNumberFormatSymbols_tzm_Latn', 'goog.i18n.CompactNumberFormatSymbols_tzm_Latn_MA', 'goog.i18n.CompactNumberFormatSymbols_ug', 'goog.i18n.CompactNumberFormatSymbols_ug_Arab', 'goog.i18n.CompactNumberFormatSymbols_ug_Arab_CN', 'goog.i18n.CompactNumberFormatSymbols_ur_IN', 'goog.i18n.CompactNumberFormatSymbols_uz_Arab', 'goog.i18n.CompactNumberFormatSymbols_uz_Arab_AF', 'goog.i18n.CompactNumberFormatSymbols_uz_Cyrl', 'goog.i18n.CompactNumberFormatSymbols_uz_Cyrl_UZ', 'goog.i18n.CompactNumberFormatSymbols_uz_Latn', 'goog.i18n.CompactNumberFormatSymbols_vai', 'goog.i18n.CompactNumberFormatSymbols_vai_Latn', 'goog.i18n.CompactNumberFormatSymbols_vai_Latn_LR', 'goog.i18n.CompactNumberFormatSymbols_vai_Vaii', 'goog.i18n.CompactNumberFormatSymbols_vai_Vaii_LR', 'goog.i18n.CompactNumberFormatSymbols_ve', 'goog.i18n.CompactNumberFormatSymbols_ve_ZA', 'goog.i18n.CompactNumberFormatSymbols_vo', 'goog.i18n.CompactNumberFormatSymbols_vo_001', 'goog.i18n.CompactNumberFormatSymbols_vun', 'goog.i18n.CompactNumberFormatSymbols_vun_TZ', 'goog.i18n.CompactNumberFormatSymbols_wae', 'goog.i18n.CompactNumberFormatSymbols_wae_CH', 'goog.i18n.CompactNumberFormatSymbols_xog', 'goog.i18n.CompactNumberFormatSymbols_xog_UG', 'goog.i18n.CompactNumberFormatSymbols_yav', 'goog.i18n.CompactNumberFormatSymbols_yav_CM', 'goog.i18n.CompactNumberFormatSymbols_yi', 'goog.i18n.CompactNumberFormatSymbols_yi_001', 'goog.i18n.CompactNumberFormatSymbols_yo', 'goog.i18n.CompactNumberFormatSymbols_yo_BJ', 'goog.i18n.CompactNumberFormatSymbols_yo_NG', 'goog.i18n.CompactNumberFormatSymbols_zgh', 'goog.i18n.CompactNumberFormatSymbols_zgh_MA', 'goog.i18n.CompactNumberFormatSymbols_zh_Hans', 'goog.i18n.CompactNumberFormatSymbols_zh_Hans_HK', 'goog.i18n.CompactNumberFormatSymbols_zh_Hans_MO', 'goog.i18n.CompactNumberFormatSymbols_zh_Hans_SG', 'goog.i18n.CompactNumberFormatSymbols_zh_Hant', 'goog.i18n.CompactNumberFormatSymbols_zh_Hant_HK', 'goog.i18n.CompactNumberFormatSymbols_zh_Hant_MO', 'goog.i18n.CompactNumberFormatSymbols_zh_Hant_TW'], [], false); +goog.addDependency('i18n/currency.js', ['goog.i18n.currency', 'goog.i18n.currency.CurrencyInfo', 'goog.i18n.currency.CurrencyInfoTier2'], [], false); +goog.addDependency('i18n/currency_test.js', ['goog.i18n.currencyTest'], ['goog.i18n.NumberFormat', 'goog.i18n.currency', 'goog.i18n.currency.CurrencyInfo', 'goog.object', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/currencycodemap.js', ['goog.i18n.currencyCodeMap', 'goog.i18n.currencyCodeMapTier2'], [], false); +goog.addDependency('i18n/datetimeformat.js', ['goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeFormat.Format'], ['goog.asserts', 'goog.date', 'goog.i18n.DateTimeSymbols', 'goog.i18n.TimeZone', 'goog.string'], false); +goog.addDependency('i18n/datetimeformat_test.js', ['goog.i18n.DateTimeFormatTest'], ['goog.date.Date', 'goog.date.DateTime', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimePatterns', 'goog.i18n.DateTimePatterns_de', 'goog.i18n.DateTimePatterns_en', 'goog.i18n.DateTimePatterns_fa', 'goog.i18n.DateTimePatterns_fr', 'goog.i18n.DateTimePatterns_ja', 'goog.i18n.DateTimePatterns_sv', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_ar_AE', 'goog.i18n.DateTimeSymbols_ar_SA', 'goog.i18n.DateTimeSymbols_bn_BD', 'goog.i18n.DateTimeSymbols_de', 'goog.i18n.DateTimeSymbols_en', 'goog.i18n.DateTimeSymbols_en_GB', 'goog.i18n.DateTimeSymbols_en_IE', 'goog.i18n.DateTimeSymbols_en_IN', 'goog.i18n.DateTimeSymbols_en_US', 'goog.i18n.DateTimeSymbols_fa', 'goog.i18n.DateTimeSymbols_fr', 'goog.i18n.DateTimeSymbols_fr_DJ', 'goog.i18n.DateTimeSymbols_he_IL', 'goog.i18n.DateTimeSymbols_ja', 'goog.i18n.DateTimeSymbols_ro_RO', 'goog.i18n.DateTimeSymbols_sv', 'goog.i18n.TimeZone', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/datetimeparse.js', ['goog.i18n.DateTimeParse'], ['goog.date', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeSymbols'], false); +goog.addDependency('i18n/datetimeparse_test.js', ['goog.i18n.DateTimeParseTest'], ['goog.date.Date', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeParse', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_en', 'goog.i18n.DateTimeSymbols_fa', 'goog.i18n.DateTimeSymbols_fr', 'goog.i18n.DateTimeSymbols_pl', 'goog.i18n.DateTimeSymbols_zh', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('i18n/datetimepatterns.js', ['goog.i18n.DateTimePatterns', 'goog.i18n.DateTimePatterns_af', 'goog.i18n.DateTimePatterns_am', 'goog.i18n.DateTimePatterns_ar', 'goog.i18n.DateTimePatterns_az', 'goog.i18n.DateTimePatterns_bg', 'goog.i18n.DateTimePatterns_bn', 'goog.i18n.DateTimePatterns_br', 'goog.i18n.DateTimePatterns_ca', 'goog.i18n.DateTimePatterns_chr', 'goog.i18n.DateTimePatterns_cs', 'goog.i18n.DateTimePatterns_cy', 'goog.i18n.DateTimePatterns_da', 'goog.i18n.DateTimePatterns_de', 'goog.i18n.DateTimePatterns_de_AT', 'goog.i18n.DateTimePatterns_de_CH', 'goog.i18n.DateTimePatterns_el', 'goog.i18n.DateTimePatterns_en', 'goog.i18n.DateTimePatterns_en_AU', 'goog.i18n.DateTimePatterns_en_GB', 'goog.i18n.DateTimePatterns_en_IE', 'goog.i18n.DateTimePatterns_en_IN', 'goog.i18n.DateTimePatterns_en_SG', 'goog.i18n.DateTimePatterns_en_US', 'goog.i18n.DateTimePatterns_en_ZA', 'goog.i18n.DateTimePatterns_es', 'goog.i18n.DateTimePatterns_es_419', 'goog.i18n.DateTimePatterns_es_ES', 'goog.i18n.DateTimePatterns_et', 'goog.i18n.DateTimePatterns_eu', 'goog.i18n.DateTimePatterns_fa', 'goog.i18n.DateTimePatterns_fi', 'goog.i18n.DateTimePatterns_fil', 'goog.i18n.DateTimePatterns_fr', 'goog.i18n.DateTimePatterns_fr_CA', 'goog.i18n.DateTimePatterns_ga', 'goog.i18n.DateTimePatterns_gl', 'goog.i18n.DateTimePatterns_gsw', 'goog.i18n.DateTimePatterns_gu', 'goog.i18n.DateTimePatterns_haw', 'goog.i18n.DateTimePatterns_he', 'goog.i18n.DateTimePatterns_hi', 'goog.i18n.DateTimePatterns_hr', 'goog.i18n.DateTimePatterns_hu', 'goog.i18n.DateTimePatterns_hy', 'goog.i18n.DateTimePatterns_id', 'goog.i18n.DateTimePatterns_in', 'goog.i18n.DateTimePatterns_is', 'goog.i18n.DateTimePatterns_it', 'goog.i18n.DateTimePatterns_iw', 'goog.i18n.DateTimePatterns_ja', 'goog.i18n.DateTimePatterns_ka', 'goog.i18n.DateTimePatterns_kk', 'goog.i18n.DateTimePatterns_km', 'goog.i18n.DateTimePatterns_kn', 'goog.i18n.DateTimePatterns_ko', 'goog.i18n.DateTimePatterns_ky', 'goog.i18n.DateTimePatterns_ln', 'goog.i18n.DateTimePatterns_lo', 'goog.i18n.DateTimePatterns_lt', 'goog.i18n.DateTimePatterns_lv', 'goog.i18n.DateTimePatterns_mk', 'goog.i18n.DateTimePatterns_ml', 'goog.i18n.DateTimePatterns_mn', 'goog.i18n.DateTimePatterns_mo', 'goog.i18n.DateTimePatterns_mr', 'goog.i18n.DateTimePatterns_ms', 'goog.i18n.DateTimePatterns_mt', 'goog.i18n.DateTimePatterns_my', 'goog.i18n.DateTimePatterns_nb', 'goog.i18n.DateTimePatterns_ne', 'goog.i18n.DateTimePatterns_nl', 'goog.i18n.DateTimePatterns_no', 'goog.i18n.DateTimePatterns_no_NO', 'goog.i18n.DateTimePatterns_or', 'goog.i18n.DateTimePatterns_pa', 'goog.i18n.DateTimePatterns_pl', 'goog.i18n.DateTimePatterns_pt', 'goog.i18n.DateTimePatterns_pt_BR', 'goog.i18n.DateTimePatterns_pt_PT', 'goog.i18n.DateTimePatterns_ro', 'goog.i18n.DateTimePatterns_ru', 'goog.i18n.DateTimePatterns_sh', 'goog.i18n.DateTimePatterns_si', 'goog.i18n.DateTimePatterns_sk', 'goog.i18n.DateTimePatterns_sl', 'goog.i18n.DateTimePatterns_sq', 'goog.i18n.DateTimePatterns_sr', 'goog.i18n.DateTimePatterns_sv', 'goog.i18n.DateTimePatterns_sw', 'goog.i18n.DateTimePatterns_ta', 'goog.i18n.DateTimePatterns_te', 'goog.i18n.DateTimePatterns_th', 'goog.i18n.DateTimePatterns_tl', 'goog.i18n.DateTimePatterns_tr', 'goog.i18n.DateTimePatterns_uk', 'goog.i18n.DateTimePatterns_ur', 'goog.i18n.DateTimePatterns_uz', 'goog.i18n.DateTimePatterns_vi', 'goog.i18n.DateTimePatterns_zh', 'goog.i18n.DateTimePatterns_zh_CN', 'goog.i18n.DateTimePatterns_zh_HK', 'goog.i18n.DateTimePatterns_zh_TW', 'goog.i18n.DateTimePatterns_zu'], [], false); +goog.addDependency('i18n/datetimepatternsext.js', ['goog.i18n.DateTimePatternsExt', 'goog.i18n.DateTimePatterns_af_NA', 'goog.i18n.DateTimePatterns_af_ZA', 'goog.i18n.DateTimePatterns_agq', 'goog.i18n.DateTimePatterns_agq_CM', 'goog.i18n.DateTimePatterns_ak', 'goog.i18n.DateTimePatterns_ak_GH', 'goog.i18n.DateTimePatterns_am_ET', 'goog.i18n.DateTimePatterns_ar_001', 'goog.i18n.DateTimePatterns_ar_AE', 'goog.i18n.DateTimePatterns_ar_BH', 'goog.i18n.DateTimePatterns_ar_DJ', 'goog.i18n.DateTimePatterns_ar_DZ', 'goog.i18n.DateTimePatterns_ar_EG', 'goog.i18n.DateTimePatterns_ar_EH', 'goog.i18n.DateTimePatterns_ar_ER', 'goog.i18n.DateTimePatterns_ar_IL', 'goog.i18n.DateTimePatterns_ar_IQ', 'goog.i18n.DateTimePatterns_ar_JO', 'goog.i18n.DateTimePatterns_ar_KM', 'goog.i18n.DateTimePatterns_ar_KW', 'goog.i18n.DateTimePatterns_ar_LB', 'goog.i18n.DateTimePatterns_ar_LY', 'goog.i18n.DateTimePatterns_ar_MA', 'goog.i18n.DateTimePatterns_ar_MR', 'goog.i18n.DateTimePatterns_ar_OM', 'goog.i18n.DateTimePatterns_ar_PS', 'goog.i18n.DateTimePatterns_ar_QA', 'goog.i18n.DateTimePatterns_ar_SA', 'goog.i18n.DateTimePatterns_ar_SD', 'goog.i18n.DateTimePatterns_ar_SO', 'goog.i18n.DateTimePatterns_ar_SS', 'goog.i18n.DateTimePatterns_ar_SY', 'goog.i18n.DateTimePatterns_ar_TD', 'goog.i18n.DateTimePatterns_ar_TN', 'goog.i18n.DateTimePatterns_ar_YE', 'goog.i18n.DateTimePatterns_as', 'goog.i18n.DateTimePatterns_as_IN', 'goog.i18n.DateTimePatterns_asa', 'goog.i18n.DateTimePatterns_asa_TZ', 'goog.i18n.DateTimePatterns_az_Cyrl', 'goog.i18n.DateTimePatterns_az_Cyrl_AZ', 'goog.i18n.DateTimePatterns_az_Latn', 'goog.i18n.DateTimePatterns_az_Latn_AZ', 'goog.i18n.DateTimePatterns_bas', 'goog.i18n.DateTimePatterns_bas_CM', 'goog.i18n.DateTimePatterns_be', 'goog.i18n.DateTimePatterns_be_BY', 'goog.i18n.DateTimePatterns_bem', 'goog.i18n.DateTimePatterns_bem_ZM', 'goog.i18n.DateTimePatterns_bez', 'goog.i18n.DateTimePatterns_bez_TZ', 'goog.i18n.DateTimePatterns_bg_BG', 'goog.i18n.DateTimePatterns_bm', 'goog.i18n.DateTimePatterns_bm_Latn', 'goog.i18n.DateTimePatterns_bm_Latn_ML', 'goog.i18n.DateTimePatterns_bn_BD', 'goog.i18n.DateTimePatterns_bn_IN', 'goog.i18n.DateTimePatterns_bo', 'goog.i18n.DateTimePatterns_bo_CN', 'goog.i18n.DateTimePatterns_bo_IN', 'goog.i18n.DateTimePatterns_br_FR', 'goog.i18n.DateTimePatterns_brx', 'goog.i18n.DateTimePatterns_brx_IN', 'goog.i18n.DateTimePatterns_bs', 'goog.i18n.DateTimePatterns_bs_Cyrl', 'goog.i18n.DateTimePatterns_bs_Cyrl_BA', 'goog.i18n.DateTimePatterns_bs_Latn', 'goog.i18n.DateTimePatterns_bs_Latn_BA', 'goog.i18n.DateTimePatterns_ca_AD', 'goog.i18n.DateTimePatterns_ca_ES', 'goog.i18n.DateTimePatterns_ca_FR', 'goog.i18n.DateTimePatterns_ca_IT', 'goog.i18n.DateTimePatterns_cgg', 'goog.i18n.DateTimePatterns_cgg_UG', 'goog.i18n.DateTimePatterns_chr_US', 'goog.i18n.DateTimePatterns_cs_CZ', 'goog.i18n.DateTimePatterns_cy_GB', 'goog.i18n.DateTimePatterns_da_DK', 'goog.i18n.DateTimePatterns_da_GL', 'goog.i18n.DateTimePatterns_dav', 'goog.i18n.DateTimePatterns_dav_KE', 'goog.i18n.DateTimePatterns_de_BE', 'goog.i18n.DateTimePatterns_de_DE', 'goog.i18n.DateTimePatterns_de_LI', 'goog.i18n.DateTimePatterns_de_LU', 'goog.i18n.DateTimePatterns_dje', 'goog.i18n.DateTimePatterns_dje_NE', 'goog.i18n.DateTimePatterns_dsb', 'goog.i18n.DateTimePatterns_dsb_DE', 'goog.i18n.DateTimePatterns_dua', 'goog.i18n.DateTimePatterns_dua_CM', 'goog.i18n.DateTimePatterns_dyo', 'goog.i18n.DateTimePatterns_dyo_SN', 'goog.i18n.DateTimePatterns_dz', 'goog.i18n.DateTimePatterns_dz_BT', 'goog.i18n.DateTimePatterns_ebu', 'goog.i18n.DateTimePatterns_ebu_KE', 'goog.i18n.DateTimePatterns_ee', 'goog.i18n.DateTimePatterns_ee_GH', 'goog.i18n.DateTimePatterns_ee_TG', 'goog.i18n.DateTimePatterns_el_CY', 'goog.i18n.DateTimePatterns_el_GR', 'goog.i18n.DateTimePatterns_en_001', 'goog.i18n.DateTimePatterns_en_150', 'goog.i18n.DateTimePatterns_en_AG', 'goog.i18n.DateTimePatterns_en_AI', 'goog.i18n.DateTimePatterns_en_AS', 'goog.i18n.DateTimePatterns_en_BB', 'goog.i18n.DateTimePatterns_en_BE', 'goog.i18n.DateTimePatterns_en_BM', 'goog.i18n.DateTimePatterns_en_BS', 'goog.i18n.DateTimePatterns_en_BW', 'goog.i18n.DateTimePatterns_en_BZ', 'goog.i18n.DateTimePatterns_en_CA', 'goog.i18n.DateTimePatterns_en_CC', 'goog.i18n.DateTimePatterns_en_CK', 'goog.i18n.DateTimePatterns_en_CM', 'goog.i18n.DateTimePatterns_en_CX', 'goog.i18n.DateTimePatterns_en_DG', 'goog.i18n.DateTimePatterns_en_DM', 'goog.i18n.DateTimePatterns_en_ER', 'goog.i18n.DateTimePatterns_en_FJ', 'goog.i18n.DateTimePatterns_en_FK', 'goog.i18n.DateTimePatterns_en_FM', 'goog.i18n.DateTimePatterns_en_GD', 'goog.i18n.DateTimePatterns_en_GG', 'goog.i18n.DateTimePatterns_en_GH', 'goog.i18n.DateTimePatterns_en_GI', 'goog.i18n.DateTimePatterns_en_GM', 'goog.i18n.DateTimePatterns_en_GU', 'goog.i18n.DateTimePatterns_en_GY', 'goog.i18n.DateTimePatterns_en_HK', 'goog.i18n.DateTimePatterns_en_IM', 'goog.i18n.DateTimePatterns_en_IO', 'goog.i18n.DateTimePatterns_en_JE', 'goog.i18n.DateTimePatterns_en_JM', 'goog.i18n.DateTimePatterns_en_KE', 'goog.i18n.DateTimePatterns_en_KI', 'goog.i18n.DateTimePatterns_en_KN', 'goog.i18n.DateTimePatterns_en_KY', 'goog.i18n.DateTimePatterns_en_LC', 'goog.i18n.DateTimePatterns_en_LR', 'goog.i18n.DateTimePatterns_en_LS', 'goog.i18n.DateTimePatterns_en_MG', 'goog.i18n.DateTimePatterns_en_MH', 'goog.i18n.DateTimePatterns_en_MO', 'goog.i18n.DateTimePatterns_en_MP', 'goog.i18n.DateTimePatterns_en_MS', 'goog.i18n.DateTimePatterns_en_MT', 'goog.i18n.DateTimePatterns_en_MU', 'goog.i18n.DateTimePatterns_en_MW', 'goog.i18n.DateTimePatterns_en_MY', 'goog.i18n.DateTimePatterns_en_NA', 'goog.i18n.DateTimePatterns_en_NF', 'goog.i18n.DateTimePatterns_en_NG', 'goog.i18n.DateTimePatterns_en_NR', 'goog.i18n.DateTimePatterns_en_NU', 'goog.i18n.DateTimePatterns_en_NZ', 'goog.i18n.DateTimePatterns_en_PG', 'goog.i18n.DateTimePatterns_en_PH', 'goog.i18n.DateTimePatterns_en_PK', 'goog.i18n.DateTimePatterns_en_PN', 'goog.i18n.DateTimePatterns_en_PR', 'goog.i18n.DateTimePatterns_en_PW', 'goog.i18n.DateTimePatterns_en_RW', 'goog.i18n.DateTimePatterns_en_SB', 'goog.i18n.DateTimePatterns_en_SC', 'goog.i18n.DateTimePatterns_en_SD', 'goog.i18n.DateTimePatterns_en_SH', 'goog.i18n.DateTimePatterns_en_SL', 'goog.i18n.DateTimePatterns_en_SS', 'goog.i18n.DateTimePatterns_en_SX', 'goog.i18n.DateTimePatterns_en_SZ', 'goog.i18n.DateTimePatterns_en_TC', 'goog.i18n.DateTimePatterns_en_TK', 'goog.i18n.DateTimePatterns_en_TO', 'goog.i18n.DateTimePatterns_en_TT', 'goog.i18n.DateTimePatterns_en_TV', 'goog.i18n.DateTimePatterns_en_TZ', 'goog.i18n.DateTimePatterns_en_UG', 'goog.i18n.DateTimePatterns_en_UM', 'goog.i18n.DateTimePatterns_en_US_POSIX', 'goog.i18n.DateTimePatterns_en_VC', 'goog.i18n.DateTimePatterns_en_VG', 'goog.i18n.DateTimePatterns_en_VI', 'goog.i18n.DateTimePatterns_en_VU', 'goog.i18n.DateTimePatterns_en_WS', 'goog.i18n.DateTimePatterns_en_ZM', 'goog.i18n.DateTimePatterns_en_ZW', 'goog.i18n.DateTimePatterns_eo', 'goog.i18n.DateTimePatterns_es_AR', 'goog.i18n.DateTimePatterns_es_BO', 'goog.i18n.DateTimePatterns_es_CL', 'goog.i18n.DateTimePatterns_es_CO', 'goog.i18n.DateTimePatterns_es_CR', 'goog.i18n.DateTimePatterns_es_CU', 'goog.i18n.DateTimePatterns_es_DO', 'goog.i18n.DateTimePatterns_es_EA', 'goog.i18n.DateTimePatterns_es_EC', 'goog.i18n.DateTimePatterns_es_GQ', 'goog.i18n.DateTimePatterns_es_GT', 'goog.i18n.DateTimePatterns_es_HN', 'goog.i18n.DateTimePatterns_es_IC', 'goog.i18n.DateTimePatterns_es_MX', 'goog.i18n.DateTimePatterns_es_NI', 'goog.i18n.DateTimePatterns_es_PA', 'goog.i18n.DateTimePatterns_es_PE', 'goog.i18n.DateTimePatterns_es_PH', 'goog.i18n.DateTimePatterns_es_PR', 'goog.i18n.DateTimePatterns_es_PY', 'goog.i18n.DateTimePatterns_es_SV', 'goog.i18n.DateTimePatterns_es_US', 'goog.i18n.DateTimePatterns_es_UY', 'goog.i18n.DateTimePatterns_es_VE', 'goog.i18n.DateTimePatterns_et_EE', 'goog.i18n.DateTimePatterns_eu_ES', 'goog.i18n.DateTimePatterns_ewo', 'goog.i18n.DateTimePatterns_ewo_CM', 'goog.i18n.DateTimePatterns_fa_AF', 'goog.i18n.DateTimePatterns_fa_IR', 'goog.i18n.DateTimePatterns_ff', 'goog.i18n.DateTimePatterns_ff_CM', 'goog.i18n.DateTimePatterns_ff_GN', 'goog.i18n.DateTimePatterns_ff_MR', 'goog.i18n.DateTimePatterns_ff_SN', 'goog.i18n.DateTimePatterns_fi_FI', 'goog.i18n.DateTimePatterns_fil_PH', 'goog.i18n.DateTimePatterns_fo', 'goog.i18n.DateTimePatterns_fo_FO', 'goog.i18n.DateTimePatterns_fr_BE', 'goog.i18n.DateTimePatterns_fr_BF', 'goog.i18n.DateTimePatterns_fr_BI', 'goog.i18n.DateTimePatterns_fr_BJ', 'goog.i18n.DateTimePatterns_fr_BL', 'goog.i18n.DateTimePatterns_fr_CD', 'goog.i18n.DateTimePatterns_fr_CF', 'goog.i18n.DateTimePatterns_fr_CG', 'goog.i18n.DateTimePatterns_fr_CH', 'goog.i18n.DateTimePatterns_fr_CI', 'goog.i18n.DateTimePatterns_fr_CM', 'goog.i18n.DateTimePatterns_fr_DJ', 'goog.i18n.DateTimePatterns_fr_DZ', 'goog.i18n.DateTimePatterns_fr_FR', 'goog.i18n.DateTimePatterns_fr_GA', 'goog.i18n.DateTimePatterns_fr_GF', 'goog.i18n.DateTimePatterns_fr_GN', 'goog.i18n.DateTimePatterns_fr_GP', 'goog.i18n.DateTimePatterns_fr_GQ', 'goog.i18n.DateTimePatterns_fr_HT', 'goog.i18n.DateTimePatterns_fr_KM', 'goog.i18n.DateTimePatterns_fr_LU', 'goog.i18n.DateTimePatterns_fr_MA', 'goog.i18n.DateTimePatterns_fr_MC', 'goog.i18n.DateTimePatterns_fr_MF', 'goog.i18n.DateTimePatterns_fr_MG', 'goog.i18n.DateTimePatterns_fr_ML', 'goog.i18n.DateTimePatterns_fr_MQ', 'goog.i18n.DateTimePatterns_fr_MR', 'goog.i18n.DateTimePatterns_fr_MU', 'goog.i18n.DateTimePatterns_fr_NC', 'goog.i18n.DateTimePatterns_fr_NE', 'goog.i18n.DateTimePatterns_fr_PF', 'goog.i18n.DateTimePatterns_fr_PM', 'goog.i18n.DateTimePatterns_fr_RE', 'goog.i18n.DateTimePatterns_fr_RW', 'goog.i18n.DateTimePatterns_fr_SC', 'goog.i18n.DateTimePatterns_fr_SN', 'goog.i18n.DateTimePatterns_fr_SY', 'goog.i18n.DateTimePatterns_fr_TD', 'goog.i18n.DateTimePatterns_fr_TG', 'goog.i18n.DateTimePatterns_fr_TN', 'goog.i18n.DateTimePatterns_fr_VU', 'goog.i18n.DateTimePatterns_fr_WF', 'goog.i18n.DateTimePatterns_fr_YT', 'goog.i18n.DateTimePatterns_fur', 'goog.i18n.DateTimePatterns_fur_IT', 'goog.i18n.DateTimePatterns_fy', 'goog.i18n.DateTimePatterns_fy_NL', 'goog.i18n.DateTimePatterns_ga_IE', 'goog.i18n.DateTimePatterns_gd', 'goog.i18n.DateTimePatterns_gd_GB', 'goog.i18n.DateTimePatterns_gl_ES', 'goog.i18n.DateTimePatterns_gsw_CH', 'goog.i18n.DateTimePatterns_gsw_FR', 'goog.i18n.DateTimePatterns_gsw_LI', 'goog.i18n.DateTimePatterns_gu_IN', 'goog.i18n.DateTimePatterns_guz', 'goog.i18n.DateTimePatterns_guz_KE', 'goog.i18n.DateTimePatterns_gv', 'goog.i18n.DateTimePatterns_gv_IM', 'goog.i18n.DateTimePatterns_ha', 'goog.i18n.DateTimePatterns_ha_Latn', 'goog.i18n.DateTimePatterns_ha_Latn_GH', 'goog.i18n.DateTimePatterns_ha_Latn_NE', 'goog.i18n.DateTimePatterns_ha_Latn_NG', 'goog.i18n.DateTimePatterns_haw_US', 'goog.i18n.DateTimePatterns_he_IL', 'goog.i18n.DateTimePatterns_hi_IN', 'goog.i18n.DateTimePatterns_hr_BA', 'goog.i18n.DateTimePatterns_hr_HR', 'goog.i18n.DateTimePatterns_hsb', 'goog.i18n.DateTimePatterns_hsb_DE', 'goog.i18n.DateTimePatterns_hu_HU', 'goog.i18n.DateTimePatterns_hy_AM', 'goog.i18n.DateTimePatterns_id_ID', 'goog.i18n.DateTimePatterns_ig', 'goog.i18n.DateTimePatterns_ig_NG', 'goog.i18n.DateTimePatterns_ii', 'goog.i18n.DateTimePatterns_ii_CN', 'goog.i18n.DateTimePatterns_is_IS', 'goog.i18n.DateTimePatterns_it_CH', 'goog.i18n.DateTimePatterns_it_IT', 'goog.i18n.DateTimePatterns_it_SM', 'goog.i18n.DateTimePatterns_ja_JP', 'goog.i18n.DateTimePatterns_jgo', 'goog.i18n.DateTimePatterns_jgo_CM', 'goog.i18n.DateTimePatterns_jmc', 'goog.i18n.DateTimePatterns_jmc_TZ', 'goog.i18n.DateTimePatterns_ka_GE', 'goog.i18n.DateTimePatterns_kab', 'goog.i18n.DateTimePatterns_kab_DZ', 'goog.i18n.DateTimePatterns_kam', 'goog.i18n.DateTimePatterns_kam_KE', 'goog.i18n.DateTimePatterns_kde', 'goog.i18n.DateTimePatterns_kde_TZ', 'goog.i18n.DateTimePatterns_kea', 'goog.i18n.DateTimePatterns_kea_CV', 'goog.i18n.DateTimePatterns_khq', 'goog.i18n.DateTimePatterns_khq_ML', 'goog.i18n.DateTimePatterns_ki', 'goog.i18n.DateTimePatterns_ki_KE', 'goog.i18n.DateTimePatterns_kk_Cyrl', 'goog.i18n.DateTimePatterns_kk_Cyrl_KZ', 'goog.i18n.DateTimePatterns_kkj', 'goog.i18n.DateTimePatterns_kkj_CM', 'goog.i18n.DateTimePatterns_kl', 'goog.i18n.DateTimePatterns_kl_GL', 'goog.i18n.DateTimePatterns_kln', 'goog.i18n.DateTimePatterns_kln_KE', 'goog.i18n.DateTimePatterns_km_KH', 'goog.i18n.DateTimePatterns_kn_IN', 'goog.i18n.DateTimePatterns_ko_KP', 'goog.i18n.DateTimePatterns_ko_KR', 'goog.i18n.DateTimePatterns_kok', 'goog.i18n.DateTimePatterns_kok_IN', 'goog.i18n.DateTimePatterns_ks', 'goog.i18n.DateTimePatterns_ks_Arab', 'goog.i18n.DateTimePatterns_ks_Arab_IN', 'goog.i18n.DateTimePatterns_ksb', 'goog.i18n.DateTimePatterns_ksb_TZ', 'goog.i18n.DateTimePatterns_ksf', 'goog.i18n.DateTimePatterns_ksf_CM', 'goog.i18n.DateTimePatterns_ksh', 'goog.i18n.DateTimePatterns_ksh_DE', 'goog.i18n.DateTimePatterns_kw', 'goog.i18n.DateTimePatterns_kw_GB', 'goog.i18n.DateTimePatterns_ky_Cyrl', 'goog.i18n.DateTimePatterns_ky_Cyrl_KG', 'goog.i18n.DateTimePatterns_lag', 'goog.i18n.DateTimePatterns_lag_TZ', 'goog.i18n.DateTimePatterns_lb', 'goog.i18n.DateTimePatterns_lb_LU', 'goog.i18n.DateTimePatterns_lg', 'goog.i18n.DateTimePatterns_lg_UG', 'goog.i18n.DateTimePatterns_lkt', 'goog.i18n.DateTimePatterns_lkt_US', 'goog.i18n.DateTimePatterns_ln_AO', 'goog.i18n.DateTimePatterns_ln_CD', 'goog.i18n.DateTimePatterns_ln_CF', 'goog.i18n.DateTimePatterns_ln_CG', 'goog.i18n.DateTimePatterns_lo_LA', 'goog.i18n.DateTimePatterns_lt_LT', 'goog.i18n.DateTimePatterns_lu', 'goog.i18n.DateTimePatterns_lu_CD', 'goog.i18n.DateTimePatterns_luo', 'goog.i18n.DateTimePatterns_luo_KE', 'goog.i18n.DateTimePatterns_luy', 'goog.i18n.DateTimePatterns_luy_KE', 'goog.i18n.DateTimePatterns_lv_LV', 'goog.i18n.DateTimePatterns_mas', 'goog.i18n.DateTimePatterns_mas_KE', 'goog.i18n.DateTimePatterns_mas_TZ', 'goog.i18n.DateTimePatterns_mer', 'goog.i18n.DateTimePatterns_mer_KE', 'goog.i18n.DateTimePatterns_mfe', 'goog.i18n.DateTimePatterns_mfe_MU', 'goog.i18n.DateTimePatterns_mg', 'goog.i18n.DateTimePatterns_mg_MG', 'goog.i18n.DateTimePatterns_mgh', 'goog.i18n.DateTimePatterns_mgh_MZ', 'goog.i18n.DateTimePatterns_mgo', 'goog.i18n.DateTimePatterns_mgo_CM', 'goog.i18n.DateTimePatterns_mk_MK', 'goog.i18n.DateTimePatterns_ml_IN', 'goog.i18n.DateTimePatterns_mn_Cyrl', 'goog.i18n.DateTimePatterns_mn_Cyrl_MN', 'goog.i18n.DateTimePatterns_mr_IN', 'goog.i18n.DateTimePatterns_ms_Latn', 'goog.i18n.DateTimePatterns_ms_Latn_BN', 'goog.i18n.DateTimePatterns_ms_Latn_MY', 'goog.i18n.DateTimePatterns_ms_Latn_SG', 'goog.i18n.DateTimePatterns_mt_MT', 'goog.i18n.DateTimePatterns_mua', 'goog.i18n.DateTimePatterns_mua_CM', 'goog.i18n.DateTimePatterns_my_MM', 'goog.i18n.DateTimePatterns_naq', 'goog.i18n.DateTimePatterns_naq_NA', 'goog.i18n.DateTimePatterns_nb_NO', 'goog.i18n.DateTimePatterns_nb_SJ', 'goog.i18n.DateTimePatterns_nd', 'goog.i18n.DateTimePatterns_nd_ZW', 'goog.i18n.DateTimePatterns_ne_IN', 'goog.i18n.DateTimePatterns_ne_NP', 'goog.i18n.DateTimePatterns_nl_AW', 'goog.i18n.DateTimePatterns_nl_BE', 'goog.i18n.DateTimePatterns_nl_BQ', 'goog.i18n.DateTimePatterns_nl_CW', 'goog.i18n.DateTimePatterns_nl_NL', 'goog.i18n.DateTimePatterns_nl_SR', 'goog.i18n.DateTimePatterns_nl_SX', 'goog.i18n.DateTimePatterns_nmg', 'goog.i18n.DateTimePatterns_nmg_CM', 'goog.i18n.DateTimePatterns_nn', 'goog.i18n.DateTimePatterns_nn_NO', 'goog.i18n.DateTimePatterns_nnh', 'goog.i18n.DateTimePatterns_nnh_CM', 'goog.i18n.DateTimePatterns_nus', 'goog.i18n.DateTimePatterns_nus_SD', 'goog.i18n.DateTimePatterns_nyn', 'goog.i18n.DateTimePatterns_nyn_UG', 'goog.i18n.DateTimePatterns_om', 'goog.i18n.DateTimePatterns_om_ET', 'goog.i18n.DateTimePatterns_om_KE', 'goog.i18n.DateTimePatterns_or_IN', 'goog.i18n.DateTimePatterns_os', 'goog.i18n.DateTimePatterns_os_GE', 'goog.i18n.DateTimePatterns_os_RU', 'goog.i18n.DateTimePatterns_pa_Arab', 'goog.i18n.DateTimePatterns_pa_Arab_PK', 'goog.i18n.DateTimePatterns_pa_Guru', 'goog.i18n.DateTimePatterns_pa_Guru_IN', 'goog.i18n.DateTimePatterns_pl_PL', 'goog.i18n.DateTimePatterns_ps', 'goog.i18n.DateTimePatterns_ps_AF', 'goog.i18n.DateTimePatterns_pt_AO', 'goog.i18n.DateTimePatterns_pt_CV', 'goog.i18n.DateTimePatterns_pt_GW', 'goog.i18n.DateTimePatterns_pt_MO', 'goog.i18n.DateTimePatterns_pt_MZ', 'goog.i18n.DateTimePatterns_pt_ST', 'goog.i18n.DateTimePatterns_pt_TL', 'goog.i18n.DateTimePatterns_qu', 'goog.i18n.DateTimePatterns_qu_BO', 'goog.i18n.DateTimePatterns_qu_EC', 'goog.i18n.DateTimePatterns_qu_PE', 'goog.i18n.DateTimePatterns_rm', 'goog.i18n.DateTimePatterns_rm_CH', 'goog.i18n.DateTimePatterns_rn', 'goog.i18n.DateTimePatterns_rn_BI', 'goog.i18n.DateTimePatterns_ro_MD', 'goog.i18n.DateTimePatterns_ro_RO', 'goog.i18n.DateTimePatterns_rof', 'goog.i18n.DateTimePatterns_rof_TZ', 'goog.i18n.DateTimePatterns_ru_BY', 'goog.i18n.DateTimePatterns_ru_KG', 'goog.i18n.DateTimePatterns_ru_KZ', 'goog.i18n.DateTimePatterns_ru_MD', 'goog.i18n.DateTimePatterns_ru_RU', 'goog.i18n.DateTimePatterns_ru_UA', 'goog.i18n.DateTimePatterns_rw', 'goog.i18n.DateTimePatterns_rw_RW', 'goog.i18n.DateTimePatterns_rwk', 'goog.i18n.DateTimePatterns_rwk_TZ', 'goog.i18n.DateTimePatterns_sah', 'goog.i18n.DateTimePatterns_sah_RU', 'goog.i18n.DateTimePatterns_saq', 'goog.i18n.DateTimePatterns_saq_KE', 'goog.i18n.DateTimePatterns_sbp', 'goog.i18n.DateTimePatterns_sbp_TZ', 'goog.i18n.DateTimePatterns_se', 'goog.i18n.DateTimePatterns_se_FI', 'goog.i18n.DateTimePatterns_se_NO', 'goog.i18n.DateTimePatterns_se_SE', 'goog.i18n.DateTimePatterns_seh', 'goog.i18n.DateTimePatterns_seh_MZ', 'goog.i18n.DateTimePatterns_ses', 'goog.i18n.DateTimePatterns_ses_ML', 'goog.i18n.DateTimePatterns_sg', 'goog.i18n.DateTimePatterns_sg_CF', 'goog.i18n.DateTimePatterns_shi', 'goog.i18n.DateTimePatterns_shi_Latn', 'goog.i18n.DateTimePatterns_shi_Latn_MA', 'goog.i18n.DateTimePatterns_shi_Tfng', 'goog.i18n.DateTimePatterns_shi_Tfng_MA', 'goog.i18n.DateTimePatterns_si_LK', 'goog.i18n.DateTimePatterns_sk_SK', 'goog.i18n.DateTimePatterns_sl_SI', 'goog.i18n.DateTimePatterns_smn', 'goog.i18n.DateTimePatterns_smn_FI', 'goog.i18n.DateTimePatterns_sn', 'goog.i18n.DateTimePatterns_sn_ZW', 'goog.i18n.DateTimePatterns_so', 'goog.i18n.DateTimePatterns_so_DJ', 'goog.i18n.DateTimePatterns_so_ET', 'goog.i18n.DateTimePatterns_so_KE', 'goog.i18n.DateTimePatterns_so_SO', 'goog.i18n.DateTimePatterns_sq_AL', 'goog.i18n.DateTimePatterns_sq_MK', 'goog.i18n.DateTimePatterns_sq_XK', 'goog.i18n.DateTimePatterns_sr_Cyrl', 'goog.i18n.DateTimePatterns_sr_Cyrl_BA', 'goog.i18n.DateTimePatterns_sr_Cyrl_ME', 'goog.i18n.DateTimePatterns_sr_Cyrl_RS', 'goog.i18n.DateTimePatterns_sr_Cyrl_XK', 'goog.i18n.DateTimePatterns_sr_Latn', 'goog.i18n.DateTimePatterns_sr_Latn_BA', 'goog.i18n.DateTimePatterns_sr_Latn_ME', 'goog.i18n.DateTimePatterns_sr_Latn_RS', 'goog.i18n.DateTimePatterns_sr_Latn_XK', 'goog.i18n.DateTimePatterns_sv_AX', 'goog.i18n.DateTimePatterns_sv_FI', 'goog.i18n.DateTimePatterns_sv_SE', 'goog.i18n.DateTimePatterns_sw_KE', 'goog.i18n.DateTimePatterns_sw_TZ', 'goog.i18n.DateTimePatterns_sw_UG', 'goog.i18n.DateTimePatterns_swc', 'goog.i18n.DateTimePatterns_swc_CD', 'goog.i18n.DateTimePatterns_ta_IN', 'goog.i18n.DateTimePatterns_ta_LK', 'goog.i18n.DateTimePatterns_ta_MY', 'goog.i18n.DateTimePatterns_ta_SG', 'goog.i18n.DateTimePatterns_te_IN', 'goog.i18n.DateTimePatterns_teo', 'goog.i18n.DateTimePatterns_teo_KE', 'goog.i18n.DateTimePatterns_teo_UG', 'goog.i18n.DateTimePatterns_th_TH', 'goog.i18n.DateTimePatterns_ti', 'goog.i18n.DateTimePatterns_ti_ER', 'goog.i18n.DateTimePatterns_ti_ET', 'goog.i18n.DateTimePatterns_to', 'goog.i18n.DateTimePatterns_to_TO', 'goog.i18n.DateTimePatterns_tr_CY', 'goog.i18n.DateTimePatterns_tr_TR', 'goog.i18n.DateTimePatterns_twq', 'goog.i18n.DateTimePatterns_twq_NE', 'goog.i18n.DateTimePatterns_tzm', 'goog.i18n.DateTimePatterns_tzm_Latn', 'goog.i18n.DateTimePatterns_tzm_Latn_MA', 'goog.i18n.DateTimePatterns_ug', 'goog.i18n.DateTimePatterns_ug_Arab', 'goog.i18n.DateTimePatterns_ug_Arab_CN', 'goog.i18n.DateTimePatterns_uk_UA', 'goog.i18n.DateTimePatterns_ur_IN', 'goog.i18n.DateTimePatterns_ur_PK', 'goog.i18n.DateTimePatterns_uz_Arab', 'goog.i18n.DateTimePatterns_uz_Arab_AF', 'goog.i18n.DateTimePatterns_uz_Cyrl', 'goog.i18n.DateTimePatterns_uz_Cyrl_UZ', 'goog.i18n.DateTimePatterns_uz_Latn', 'goog.i18n.DateTimePatterns_uz_Latn_UZ', 'goog.i18n.DateTimePatterns_vai', 'goog.i18n.DateTimePatterns_vai_Latn', 'goog.i18n.DateTimePatterns_vai_Latn_LR', 'goog.i18n.DateTimePatterns_vai_Vaii', 'goog.i18n.DateTimePatterns_vai_Vaii_LR', 'goog.i18n.DateTimePatterns_vi_VN', 'goog.i18n.DateTimePatterns_vun', 'goog.i18n.DateTimePatterns_vun_TZ', 'goog.i18n.DateTimePatterns_wae', 'goog.i18n.DateTimePatterns_wae_CH', 'goog.i18n.DateTimePatterns_xog', 'goog.i18n.DateTimePatterns_xog_UG', 'goog.i18n.DateTimePatterns_yav', 'goog.i18n.DateTimePatterns_yav_CM', 'goog.i18n.DateTimePatterns_yi', 'goog.i18n.DateTimePatterns_yi_001', 'goog.i18n.DateTimePatterns_yo', 'goog.i18n.DateTimePatterns_yo_BJ', 'goog.i18n.DateTimePatterns_yo_NG', 'goog.i18n.DateTimePatterns_zgh', 'goog.i18n.DateTimePatterns_zgh_MA', 'goog.i18n.DateTimePatterns_zh_Hans', 'goog.i18n.DateTimePatterns_zh_Hans_CN', 'goog.i18n.DateTimePatterns_zh_Hans_HK', 'goog.i18n.DateTimePatterns_zh_Hans_MO', 'goog.i18n.DateTimePatterns_zh_Hans_SG', 'goog.i18n.DateTimePatterns_zh_Hant', 'goog.i18n.DateTimePatterns_zh_Hant_HK', 'goog.i18n.DateTimePatterns_zh_Hant_MO', 'goog.i18n.DateTimePatterns_zh_Hant_TW', 'goog.i18n.DateTimePatterns_zu_ZA'], ['goog.i18n.DateTimePatterns'], false); +goog.addDependency('i18n/datetimesymbols.js', ['goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_af', 'goog.i18n.DateTimeSymbols_am', 'goog.i18n.DateTimeSymbols_ar', 'goog.i18n.DateTimeSymbols_az', 'goog.i18n.DateTimeSymbols_bg', 'goog.i18n.DateTimeSymbols_bn', 'goog.i18n.DateTimeSymbols_br', 'goog.i18n.DateTimeSymbols_ca', 'goog.i18n.DateTimeSymbols_chr', 'goog.i18n.DateTimeSymbols_cs', 'goog.i18n.DateTimeSymbols_cy', 'goog.i18n.DateTimeSymbols_da', 'goog.i18n.DateTimeSymbols_de', 'goog.i18n.DateTimeSymbols_de_AT', 'goog.i18n.DateTimeSymbols_de_CH', 'goog.i18n.DateTimeSymbols_el', 'goog.i18n.DateTimeSymbols_en', 'goog.i18n.DateTimeSymbols_en_AU', 'goog.i18n.DateTimeSymbols_en_GB', 'goog.i18n.DateTimeSymbols_en_IE', 'goog.i18n.DateTimeSymbols_en_IN', 'goog.i18n.DateTimeSymbols_en_ISO', 'goog.i18n.DateTimeSymbols_en_SG', 'goog.i18n.DateTimeSymbols_en_US', 'goog.i18n.DateTimeSymbols_en_ZA', 'goog.i18n.DateTimeSymbols_es', 'goog.i18n.DateTimeSymbols_es_419', 'goog.i18n.DateTimeSymbols_es_ES', 'goog.i18n.DateTimeSymbols_et', 'goog.i18n.DateTimeSymbols_eu', 'goog.i18n.DateTimeSymbols_fa', 'goog.i18n.DateTimeSymbols_fi', 'goog.i18n.DateTimeSymbols_fil', 'goog.i18n.DateTimeSymbols_fr', 'goog.i18n.DateTimeSymbols_fr_CA', 'goog.i18n.DateTimeSymbols_ga', 'goog.i18n.DateTimeSymbols_gl', 'goog.i18n.DateTimeSymbols_gsw', 'goog.i18n.DateTimeSymbols_gu', 'goog.i18n.DateTimeSymbols_haw', 'goog.i18n.DateTimeSymbols_he', 'goog.i18n.DateTimeSymbols_hi', 'goog.i18n.DateTimeSymbols_hr', 'goog.i18n.DateTimeSymbols_hu', 'goog.i18n.DateTimeSymbols_hy', 'goog.i18n.DateTimeSymbols_id', 'goog.i18n.DateTimeSymbols_in', 'goog.i18n.DateTimeSymbols_is', 'goog.i18n.DateTimeSymbols_it', 'goog.i18n.DateTimeSymbols_iw', 'goog.i18n.DateTimeSymbols_ja', 'goog.i18n.DateTimeSymbols_ka', 'goog.i18n.DateTimeSymbols_kk', 'goog.i18n.DateTimeSymbols_km', 'goog.i18n.DateTimeSymbols_kn', 'goog.i18n.DateTimeSymbols_ko', 'goog.i18n.DateTimeSymbols_ky', 'goog.i18n.DateTimeSymbols_ln', 'goog.i18n.DateTimeSymbols_lo', 'goog.i18n.DateTimeSymbols_lt', 'goog.i18n.DateTimeSymbols_lv', 'goog.i18n.DateTimeSymbols_mk', 'goog.i18n.DateTimeSymbols_ml', 'goog.i18n.DateTimeSymbols_mn', 'goog.i18n.DateTimeSymbols_mr', 'goog.i18n.DateTimeSymbols_ms', 'goog.i18n.DateTimeSymbols_mt', 'goog.i18n.DateTimeSymbols_my', 'goog.i18n.DateTimeSymbols_nb', 'goog.i18n.DateTimeSymbols_ne', 'goog.i18n.DateTimeSymbols_nl', 'goog.i18n.DateTimeSymbols_no', 'goog.i18n.DateTimeSymbols_no_NO', 'goog.i18n.DateTimeSymbols_or', 'goog.i18n.DateTimeSymbols_pa', 'goog.i18n.DateTimeSymbols_pl', 'goog.i18n.DateTimeSymbols_pt', 'goog.i18n.DateTimeSymbols_pt_BR', 'goog.i18n.DateTimeSymbols_pt_PT', 'goog.i18n.DateTimeSymbols_ro', 'goog.i18n.DateTimeSymbols_ru', 'goog.i18n.DateTimeSymbols_si', 'goog.i18n.DateTimeSymbols_sk', 'goog.i18n.DateTimeSymbols_sl', 'goog.i18n.DateTimeSymbols_sq', 'goog.i18n.DateTimeSymbols_sr', 'goog.i18n.DateTimeSymbols_sv', 'goog.i18n.DateTimeSymbols_sw', 'goog.i18n.DateTimeSymbols_ta', 'goog.i18n.DateTimeSymbols_te', 'goog.i18n.DateTimeSymbols_th', 'goog.i18n.DateTimeSymbols_tl', 'goog.i18n.DateTimeSymbols_tr', 'goog.i18n.DateTimeSymbols_uk', 'goog.i18n.DateTimeSymbols_ur', 'goog.i18n.DateTimeSymbols_uz', 'goog.i18n.DateTimeSymbols_vi', 'goog.i18n.DateTimeSymbols_zh', 'goog.i18n.DateTimeSymbols_zh_CN', 'goog.i18n.DateTimeSymbols_zh_HK', 'goog.i18n.DateTimeSymbols_zh_TW', 'goog.i18n.DateTimeSymbols_zu'], [], false); +goog.addDependency('i18n/datetimesymbolsext.js', ['goog.i18n.DateTimeSymbolsExt', 'goog.i18n.DateTimeSymbols_aa', 'goog.i18n.DateTimeSymbols_aa_DJ', 'goog.i18n.DateTimeSymbols_aa_ER', 'goog.i18n.DateTimeSymbols_aa_ET', 'goog.i18n.DateTimeSymbols_af_NA', 'goog.i18n.DateTimeSymbols_af_ZA', 'goog.i18n.DateTimeSymbols_agq', 'goog.i18n.DateTimeSymbols_agq_CM', 'goog.i18n.DateTimeSymbols_ak', 'goog.i18n.DateTimeSymbols_ak_GH', 'goog.i18n.DateTimeSymbols_am_ET', 'goog.i18n.DateTimeSymbols_ar_001', 'goog.i18n.DateTimeSymbols_ar_AE', 'goog.i18n.DateTimeSymbols_ar_BH', 'goog.i18n.DateTimeSymbols_ar_DJ', 'goog.i18n.DateTimeSymbols_ar_DZ', 'goog.i18n.DateTimeSymbols_ar_EG', 'goog.i18n.DateTimeSymbols_ar_EH', 'goog.i18n.DateTimeSymbols_ar_ER', 'goog.i18n.DateTimeSymbols_ar_IL', 'goog.i18n.DateTimeSymbols_ar_IQ', 'goog.i18n.DateTimeSymbols_ar_JO', 'goog.i18n.DateTimeSymbols_ar_KM', 'goog.i18n.DateTimeSymbols_ar_KW', 'goog.i18n.DateTimeSymbols_ar_LB', 'goog.i18n.DateTimeSymbols_ar_LY', 'goog.i18n.DateTimeSymbols_ar_MA', 'goog.i18n.DateTimeSymbols_ar_MR', 'goog.i18n.DateTimeSymbols_ar_OM', 'goog.i18n.DateTimeSymbols_ar_PS', 'goog.i18n.DateTimeSymbols_ar_QA', 'goog.i18n.DateTimeSymbols_ar_SA', 'goog.i18n.DateTimeSymbols_ar_SD', 'goog.i18n.DateTimeSymbols_ar_SO', 'goog.i18n.DateTimeSymbols_ar_SS', 'goog.i18n.DateTimeSymbols_ar_SY', 'goog.i18n.DateTimeSymbols_ar_TD', 'goog.i18n.DateTimeSymbols_ar_TN', 'goog.i18n.DateTimeSymbols_ar_YE', 'goog.i18n.DateTimeSymbols_as', 'goog.i18n.DateTimeSymbols_as_IN', 'goog.i18n.DateTimeSymbols_asa', 'goog.i18n.DateTimeSymbols_asa_TZ', 'goog.i18n.DateTimeSymbols_ast', 'goog.i18n.DateTimeSymbols_ast_ES', 'goog.i18n.DateTimeSymbols_az_Cyrl', 'goog.i18n.DateTimeSymbols_az_Cyrl_AZ', 'goog.i18n.DateTimeSymbols_az_Latn', 'goog.i18n.DateTimeSymbols_az_Latn_AZ', 'goog.i18n.DateTimeSymbols_bas', 'goog.i18n.DateTimeSymbols_bas_CM', 'goog.i18n.DateTimeSymbols_be', 'goog.i18n.DateTimeSymbols_be_BY', 'goog.i18n.DateTimeSymbols_bem', 'goog.i18n.DateTimeSymbols_bem_ZM', 'goog.i18n.DateTimeSymbols_bez', 'goog.i18n.DateTimeSymbols_bez_TZ', 'goog.i18n.DateTimeSymbols_bg_BG', 'goog.i18n.DateTimeSymbols_bm', 'goog.i18n.DateTimeSymbols_bm_Latn', 'goog.i18n.DateTimeSymbols_bm_Latn_ML', 'goog.i18n.DateTimeSymbols_bn_BD', 'goog.i18n.DateTimeSymbols_bn_IN', 'goog.i18n.DateTimeSymbols_bo', 'goog.i18n.DateTimeSymbols_bo_CN', 'goog.i18n.DateTimeSymbols_bo_IN', 'goog.i18n.DateTimeSymbols_br_FR', 'goog.i18n.DateTimeSymbols_brx', 'goog.i18n.DateTimeSymbols_brx_IN', 'goog.i18n.DateTimeSymbols_bs', 'goog.i18n.DateTimeSymbols_bs_Cyrl', 'goog.i18n.DateTimeSymbols_bs_Cyrl_BA', 'goog.i18n.DateTimeSymbols_bs_Latn', 'goog.i18n.DateTimeSymbols_bs_Latn_BA', 'goog.i18n.DateTimeSymbols_ca_AD', 'goog.i18n.DateTimeSymbols_ca_ES', 'goog.i18n.DateTimeSymbols_ca_ES_VALENCIA', 'goog.i18n.DateTimeSymbols_ca_FR', 'goog.i18n.DateTimeSymbols_ca_IT', 'goog.i18n.DateTimeSymbols_cgg', 'goog.i18n.DateTimeSymbols_cgg_UG', 'goog.i18n.DateTimeSymbols_chr_US', 'goog.i18n.DateTimeSymbols_ckb', 'goog.i18n.DateTimeSymbols_ckb_Arab', 'goog.i18n.DateTimeSymbols_ckb_Arab_IQ', 'goog.i18n.DateTimeSymbols_ckb_Arab_IR', 'goog.i18n.DateTimeSymbols_ckb_IQ', 'goog.i18n.DateTimeSymbols_ckb_IR', 'goog.i18n.DateTimeSymbols_ckb_Latn', 'goog.i18n.DateTimeSymbols_ckb_Latn_IQ', 'goog.i18n.DateTimeSymbols_cs_CZ', 'goog.i18n.DateTimeSymbols_cy_GB', 'goog.i18n.DateTimeSymbols_da_DK', 'goog.i18n.DateTimeSymbols_da_GL', 'goog.i18n.DateTimeSymbols_dav', 'goog.i18n.DateTimeSymbols_dav_KE', 'goog.i18n.DateTimeSymbols_de_BE', 'goog.i18n.DateTimeSymbols_de_DE', 'goog.i18n.DateTimeSymbols_de_LI', 'goog.i18n.DateTimeSymbols_de_LU', 'goog.i18n.DateTimeSymbols_dje', 'goog.i18n.DateTimeSymbols_dje_NE', 'goog.i18n.DateTimeSymbols_dsb', 'goog.i18n.DateTimeSymbols_dsb_DE', 'goog.i18n.DateTimeSymbols_dua', 'goog.i18n.DateTimeSymbols_dua_CM', 'goog.i18n.DateTimeSymbols_dyo', 'goog.i18n.DateTimeSymbols_dyo_SN', 'goog.i18n.DateTimeSymbols_dz', 'goog.i18n.DateTimeSymbols_dz_BT', 'goog.i18n.DateTimeSymbols_ebu', 'goog.i18n.DateTimeSymbols_ebu_KE', 'goog.i18n.DateTimeSymbols_ee', 'goog.i18n.DateTimeSymbols_ee_GH', 'goog.i18n.DateTimeSymbols_ee_TG', 'goog.i18n.DateTimeSymbols_el_CY', 'goog.i18n.DateTimeSymbols_el_GR', 'goog.i18n.DateTimeSymbols_en_001', 'goog.i18n.DateTimeSymbols_en_150', 'goog.i18n.DateTimeSymbols_en_AG', 'goog.i18n.DateTimeSymbols_en_AI', 'goog.i18n.DateTimeSymbols_en_AS', 'goog.i18n.DateTimeSymbols_en_BB', 'goog.i18n.DateTimeSymbols_en_BE', 'goog.i18n.DateTimeSymbols_en_BM', 'goog.i18n.DateTimeSymbols_en_BS', 'goog.i18n.DateTimeSymbols_en_BW', 'goog.i18n.DateTimeSymbols_en_BZ', 'goog.i18n.DateTimeSymbols_en_CA', 'goog.i18n.DateTimeSymbols_en_CC', 'goog.i18n.DateTimeSymbols_en_CK', 'goog.i18n.DateTimeSymbols_en_CM', 'goog.i18n.DateTimeSymbols_en_CX', 'goog.i18n.DateTimeSymbols_en_DG', 'goog.i18n.DateTimeSymbols_en_DM', 'goog.i18n.DateTimeSymbols_en_ER', 'goog.i18n.DateTimeSymbols_en_FJ', 'goog.i18n.DateTimeSymbols_en_FK', 'goog.i18n.DateTimeSymbols_en_FM', 'goog.i18n.DateTimeSymbols_en_GD', 'goog.i18n.DateTimeSymbols_en_GG', 'goog.i18n.DateTimeSymbols_en_GH', 'goog.i18n.DateTimeSymbols_en_GI', 'goog.i18n.DateTimeSymbols_en_GM', 'goog.i18n.DateTimeSymbols_en_GU', 'goog.i18n.DateTimeSymbols_en_GY', 'goog.i18n.DateTimeSymbols_en_HK', 'goog.i18n.DateTimeSymbols_en_IM', 'goog.i18n.DateTimeSymbols_en_IO', 'goog.i18n.DateTimeSymbols_en_JE', 'goog.i18n.DateTimeSymbols_en_JM', 'goog.i18n.DateTimeSymbols_en_KE', 'goog.i18n.DateTimeSymbols_en_KI', 'goog.i18n.DateTimeSymbols_en_KN', 'goog.i18n.DateTimeSymbols_en_KY', 'goog.i18n.DateTimeSymbols_en_LC', 'goog.i18n.DateTimeSymbols_en_LR', 'goog.i18n.DateTimeSymbols_en_LS', 'goog.i18n.DateTimeSymbols_en_MG', 'goog.i18n.DateTimeSymbols_en_MH', 'goog.i18n.DateTimeSymbols_en_MO', 'goog.i18n.DateTimeSymbols_en_MP', 'goog.i18n.DateTimeSymbols_en_MS', 'goog.i18n.DateTimeSymbols_en_MT', 'goog.i18n.DateTimeSymbols_en_MU', 'goog.i18n.DateTimeSymbols_en_MW', 'goog.i18n.DateTimeSymbols_en_MY', 'goog.i18n.DateTimeSymbols_en_NA', 'goog.i18n.DateTimeSymbols_en_NF', 'goog.i18n.DateTimeSymbols_en_NG', 'goog.i18n.DateTimeSymbols_en_NR', 'goog.i18n.DateTimeSymbols_en_NU', 'goog.i18n.DateTimeSymbols_en_NZ', 'goog.i18n.DateTimeSymbols_en_PG', 'goog.i18n.DateTimeSymbols_en_PH', 'goog.i18n.DateTimeSymbols_en_PK', 'goog.i18n.DateTimeSymbols_en_PN', 'goog.i18n.DateTimeSymbols_en_PR', 'goog.i18n.DateTimeSymbols_en_PW', 'goog.i18n.DateTimeSymbols_en_RW', 'goog.i18n.DateTimeSymbols_en_SB', 'goog.i18n.DateTimeSymbols_en_SC', 'goog.i18n.DateTimeSymbols_en_SD', 'goog.i18n.DateTimeSymbols_en_SH', 'goog.i18n.DateTimeSymbols_en_SL', 'goog.i18n.DateTimeSymbols_en_SS', 'goog.i18n.DateTimeSymbols_en_SX', 'goog.i18n.DateTimeSymbols_en_SZ', 'goog.i18n.DateTimeSymbols_en_TC', 'goog.i18n.DateTimeSymbols_en_TK', 'goog.i18n.DateTimeSymbols_en_TO', 'goog.i18n.DateTimeSymbols_en_TT', 'goog.i18n.DateTimeSymbols_en_TV', 'goog.i18n.DateTimeSymbols_en_TZ', 'goog.i18n.DateTimeSymbols_en_UG', 'goog.i18n.DateTimeSymbols_en_UM', 'goog.i18n.DateTimeSymbols_en_VC', 'goog.i18n.DateTimeSymbols_en_VG', 'goog.i18n.DateTimeSymbols_en_VI', 'goog.i18n.DateTimeSymbols_en_VU', 'goog.i18n.DateTimeSymbols_en_WS', 'goog.i18n.DateTimeSymbols_en_ZM', 'goog.i18n.DateTimeSymbols_en_ZW', 'goog.i18n.DateTimeSymbols_eo', 'goog.i18n.DateTimeSymbols_eo_001', 'goog.i18n.DateTimeSymbols_es_AR', 'goog.i18n.DateTimeSymbols_es_BO', 'goog.i18n.DateTimeSymbols_es_CL', 'goog.i18n.DateTimeSymbols_es_CO', 'goog.i18n.DateTimeSymbols_es_CR', 'goog.i18n.DateTimeSymbols_es_CU', 'goog.i18n.DateTimeSymbols_es_DO', 'goog.i18n.DateTimeSymbols_es_EA', 'goog.i18n.DateTimeSymbols_es_EC', 'goog.i18n.DateTimeSymbols_es_GQ', 'goog.i18n.DateTimeSymbols_es_GT', 'goog.i18n.DateTimeSymbols_es_HN', 'goog.i18n.DateTimeSymbols_es_IC', 'goog.i18n.DateTimeSymbols_es_MX', 'goog.i18n.DateTimeSymbols_es_NI', 'goog.i18n.DateTimeSymbols_es_PA', 'goog.i18n.DateTimeSymbols_es_PE', 'goog.i18n.DateTimeSymbols_es_PH', 'goog.i18n.DateTimeSymbols_es_PR', 'goog.i18n.DateTimeSymbols_es_PY', 'goog.i18n.DateTimeSymbols_es_SV', 'goog.i18n.DateTimeSymbols_es_US', 'goog.i18n.DateTimeSymbols_es_UY', 'goog.i18n.DateTimeSymbols_es_VE', 'goog.i18n.DateTimeSymbols_et_EE', 'goog.i18n.DateTimeSymbols_eu_ES', 'goog.i18n.DateTimeSymbols_ewo', 'goog.i18n.DateTimeSymbols_ewo_CM', 'goog.i18n.DateTimeSymbols_fa_AF', 'goog.i18n.DateTimeSymbols_fa_IR', 'goog.i18n.DateTimeSymbols_ff', 'goog.i18n.DateTimeSymbols_ff_CM', 'goog.i18n.DateTimeSymbols_ff_GN', 'goog.i18n.DateTimeSymbols_ff_MR', 'goog.i18n.DateTimeSymbols_ff_SN', 'goog.i18n.DateTimeSymbols_fi_FI', 'goog.i18n.DateTimeSymbols_fil_PH', 'goog.i18n.DateTimeSymbols_fo', 'goog.i18n.DateTimeSymbols_fo_FO', 'goog.i18n.DateTimeSymbols_fr_BE', 'goog.i18n.DateTimeSymbols_fr_BF', 'goog.i18n.DateTimeSymbols_fr_BI', 'goog.i18n.DateTimeSymbols_fr_BJ', 'goog.i18n.DateTimeSymbols_fr_BL', 'goog.i18n.DateTimeSymbols_fr_CD', 'goog.i18n.DateTimeSymbols_fr_CF', 'goog.i18n.DateTimeSymbols_fr_CG', 'goog.i18n.DateTimeSymbols_fr_CH', 'goog.i18n.DateTimeSymbols_fr_CI', 'goog.i18n.DateTimeSymbols_fr_CM', 'goog.i18n.DateTimeSymbols_fr_DJ', 'goog.i18n.DateTimeSymbols_fr_DZ', 'goog.i18n.DateTimeSymbols_fr_FR', 'goog.i18n.DateTimeSymbols_fr_GA', 'goog.i18n.DateTimeSymbols_fr_GF', 'goog.i18n.DateTimeSymbols_fr_GN', 'goog.i18n.DateTimeSymbols_fr_GP', 'goog.i18n.DateTimeSymbols_fr_GQ', 'goog.i18n.DateTimeSymbols_fr_HT', 'goog.i18n.DateTimeSymbols_fr_KM', 'goog.i18n.DateTimeSymbols_fr_LU', 'goog.i18n.DateTimeSymbols_fr_MA', 'goog.i18n.DateTimeSymbols_fr_MC', 'goog.i18n.DateTimeSymbols_fr_MF', 'goog.i18n.DateTimeSymbols_fr_MG', 'goog.i18n.DateTimeSymbols_fr_ML', 'goog.i18n.DateTimeSymbols_fr_MQ', 'goog.i18n.DateTimeSymbols_fr_MR', 'goog.i18n.DateTimeSymbols_fr_MU', 'goog.i18n.DateTimeSymbols_fr_NC', 'goog.i18n.DateTimeSymbols_fr_NE', 'goog.i18n.DateTimeSymbols_fr_PF', 'goog.i18n.DateTimeSymbols_fr_PM', 'goog.i18n.DateTimeSymbols_fr_RE', 'goog.i18n.DateTimeSymbols_fr_RW', 'goog.i18n.DateTimeSymbols_fr_SC', 'goog.i18n.DateTimeSymbols_fr_SN', 'goog.i18n.DateTimeSymbols_fr_SY', 'goog.i18n.DateTimeSymbols_fr_TD', 'goog.i18n.DateTimeSymbols_fr_TG', 'goog.i18n.DateTimeSymbols_fr_TN', 'goog.i18n.DateTimeSymbols_fr_VU', 'goog.i18n.DateTimeSymbols_fr_WF', 'goog.i18n.DateTimeSymbols_fr_YT', 'goog.i18n.DateTimeSymbols_fur', 'goog.i18n.DateTimeSymbols_fur_IT', 'goog.i18n.DateTimeSymbols_fy', 'goog.i18n.DateTimeSymbols_fy_NL', 'goog.i18n.DateTimeSymbols_ga_IE', 'goog.i18n.DateTimeSymbols_gd', 'goog.i18n.DateTimeSymbols_gd_GB', 'goog.i18n.DateTimeSymbols_gl_ES', 'goog.i18n.DateTimeSymbols_gsw_CH', 'goog.i18n.DateTimeSymbols_gsw_FR', 'goog.i18n.DateTimeSymbols_gsw_LI', 'goog.i18n.DateTimeSymbols_gu_IN', 'goog.i18n.DateTimeSymbols_guz', 'goog.i18n.DateTimeSymbols_guz_KE', 'goog.i18n.DateTimeSymbols_gv', 'goog.i18n.DateTimeSymbols_gv_IM', 'goog.i18n.DateTimeSymbols_ha', 'goog.i18n.DateTimeSymbols_ha_Latn', 'goog.i18n.DateTimeSymbols_ha_Latn_GH', 'goog.i18n.DateTimeSymbols_ha_Latn_NE', 'goog.i18n.DateTimeSymbols_ha_Latn_NG', 'goog.i18n.DateTimeSymbols_haw_US', 'goog.i18n.DateTimeSymbols_he_IL', 'goog.i18n.DateTimeSymbols_hi_IN', 'goog.i18n.DateTimeSymbols_hr_BA', 'goog.i18n.DateTimeSymbols_hr_HR', 'goog.i18n.DateTimeSymbols_hsb', 'goog.i18n.DateTimeSymbols_hsb_DE', 'goog.i18n.DateTimeSymbols_hu_HU', 'goog.i18n.DateTimeSymbols_hy_AM', 'goog.i18n.DateTimeSymbols_ia', 'goog.i18n.DateTimeSymbols_ia_FR', 'goog.i18n.DateTimeSymbols_id_ID', 'goog.i18n.DateTimeSymbols_ig', 'goog.i18n.DateTimeSymbols_ig_NG', 'goog.i18n.DateTimeSymbols_ii', 'goog.i18n.DateTimeSymbols_ii_CN', 'goog.i18n.DateTimeSymbols_is_IS', 'goog.i18n.DateTimeSymbols_it_CH', 'goog.i18n.DateTimeSymbols_it_IT', 'goog.i18n.DateTimeSymbols_it_SM', 'goog.i18n.DateTimeSymbols_ja_JP', 'goog.i18n.DateTimeSymbols_jgo', 'goog.i18n.DateTimeSymbols_jgo_CM', 'goog.i18n.DateTimeSymbols_jmc', 'goog.i18n.DateTimeSymbols_jmc_TZ', 'goog.i18n.DateTimeSymbols_ka_GE', 'goog.i18n.DateTimeSymbols_kab', 'goog.i18n.DateTimeSymbols_kab_DZ', 'goog.i18n.DateTimeSymbols_kam', 'goog.i18n.DateTimeSymbols_kam_KE', 'goog.i18n.DateTimeSymbols_kde', 'goog.i18n.DateTimeSymbols_kde_TZ', 'goog.i18n.DateTimeSymbols_kea', 'goog.i18n.DateTimeSymbols_kea_CV', 'goog.i18n.DateTimeSymbols_khq', 'goog.i18n.DateTimeSymbols_khq_ML', 'goog.i18n.DateTimeSymbols_ki', 'goog.i18n.DateTimeSymbols_ki_KE', 'goog.i18n.DateTimeSymbols_kk_Cyrl', 'goog.i18n.DateTimeSymbols_kk_Cyrl_KZ', 'goog.i18n.DateTimeSymbols_kkj', 'goog.i18n.DateTimeSymbols_kkj_CM', 'goog.i18n.DateTimeSymbols_kl', 'goog.i18n.DateTimeSymbols_kl_GL', 'goog.i18n.DateTimeSymbols_kln', 'goog.i18n.DateTimeSymbols_kln_KE', 'goog.i18n.DateTimeSymbols_km_KH', 'goog.i18n.DateTimeSymbols_kn_IN', 'goog.i18n.DateTimeSymbols_ko_KP', 'goog.i18n.DateTimeSymbols_ko_KR', 'goog.i18n.DateTimeSymbols_kok', 'goog.i18n.DateTimeSymbols_kok_IN', 'goog.i18n.DateTimeSymbols_ks', 'goog.i18n.DateTimeSymbols_ks_Arab', 'goog.i18n.DateTimeSymbols_ks_Arab_IN', 'goog.i18n.DateTimeSymbols_ksb', 'goog.i18n.DateTimeSymbols_ksb_TZ', 'goog.i18n.DateTimeSymbols_ksf', 'goog.i18n.DateTimeSymbols_ksf_CM', 'goog.i18n.DateTimeSymbols_ksh', 'goog.i18n.DateTimeSymbols_ksh_DE', 'goog.i18n.DateTimeSymbols_kw', 'goog.i18n.DateTimeSymbols_kw_GB', 'goog.i18n.DateTimeSymbols_ky_Cyrl', 'goog.i18n.DateTimeSymbols_ky_Cyrl_KG', 'goog.i18n.DateTimeSymbols_lag', 'goog.i18n.DateTimeSymbols_lag_TZ', 'goog.i18n.DateTimeSymbols_lb', 'goog.i18n.DateTimeSymbols_lb_LU', 'goog.i18n.DateTimeSymbols_lg', 'goog.i18n.DateTimeSymbols_lg_UG', 'goog.i18n.DateTimeSymbols_lkt', 'goog.i18n.DateTimeSymbols_lkt_US', 'goog.i18n.DateTimeSymbols_ln_AO', 'goog.i18n.DateTimeSymbols_ln_CD', 'goog.i18n.DateTimeSymbols_ln_CF', 'goog.i18n.DateTimeSymbols_ln_CG', 'goog.i18n.DateTimeSymbols_lo_LA', 'goog.i18n.DateTimeSymbols_lt_LT', 'goog.i18n.DateTimeSymbols_lu', 'goog.i18n.DateTimeSymbols_lu_CD', 'goog.i18n.DateTimeSymbols_luo', 'goog.i18n.DateTimeSymbols_luo_KE', 'goog.i18n.DateTimeSymbols_luy', 'goog.i18n.DateTimeSymbols_luy_KE', 'goog.i18n.DateTimeSymbols_lv_LV', 'goog.i18n.DateTimeSymbols_mas', 'goog.i18n.DateTimeSymbols_mas_KE', 'goog.i18n.DateTimeSymbols_mas_TZ', 'goog.i18n.DateTimeSymbols_mer', 'goog.i18n.DateTimeSymbols_mer_KE', 'goog.i18n.DateTimeSymbols_mfe', 'goog.i18n.DateTimeSymbols_mfe_MU', 'goog.i18n.DateTimeSymbols_mg', 'goog.i18n.DateTimeSymbols_mg_MG', 'goog.i18n.DateTimeSymbols_mgh', 'goog.i18n.DateTimeSymbols_mgh_MZ', 'goog.i18n.DateTimeSymbols_mgo', 'goog.i18n.DateTimeSymbols_mgo_CM', 'goog.i18n.DateTimeSymbols_mk_MK', 'goog.i18n.DateTimeSymbols_ml_IN', 'goog.i18n.DateTimeSymbols_mn_Cyrl', 'goog.i18n.DateTimeSymbols_mn_Cyrl_MN', 'goog.i18n.DateTimeSymbols_mr_IN', 'goog.i18n.DateTimeSymbols_ms_Latn', 'goog.i18n.DateTimeSymbols_ms_Latn_BN', 'goog.i18n.DateTimeSymbols_ms_Latn_MY', 'goog.i18n.DateTimeSymbols_ms_Latn_SG', 'goog.i18n.DateTimeSymbols_mt_MT', 'goog.i18n.DateTimeSymbols_mua', 'goog.i18n.DateTimeSymbols_mua_CM', 'goog.i18n.DateTimeSymbols_my_MM', 'goog.i18n.DateTimeSymbols_naq', 'goog.i18n.DateTimeSymbols_naq_NA', 'goog.i18n.DateTimeSymbols_nb_NO', 'goog.i18n.DateTimeSymbols_nb_SJ', 'goog.i18n.DateTimeSymbols_nd', 'goog.i18n.DateTimeSymbols_nd_ZW', 'goog.i18n.DateTimeSymbols_ne_IN', 'goog.i18n.DateTimeSymbols_ne_NP', 'goog.i18n.DateTimeSymbols_nl_AW', 'goog.i18n.DateTimeSymbols_nl_BE', 'goog.i18n.DateTimeSymbols_nl_BQ', 'goog.i18n.DateTimeSymbols_nl_CW', 'goog.i18n.DateTimeSymbols_nl_NL', 'goog.i18n.DateTimeSymbols_nl_SR', 'goog.i18n.DateTimeSymbols_nl_SX', 'goog.i18n.DateTimeSymbols_nmg', 'goog.i18n.DateTimeSymbols_nmg_CM', 'goog.i18n.DateTimeSymbols_nn', 'goog.i18n.DateTimeSymbols_nn_NO', 'goog.i18n.DateTimeSymbols_nnh', 'goog.i18n.DateTimeSymbols_nnh_CM', 'goog.i18n.DateTimeSymbols_nr', 'goog.i18n.DateTimeSymbols_nr_ZA', 'goog.i18n.DateTimeSymbols_nso', 'goog.i18n.DateTimeSymbols_nso_ZA', 'goog.i18n.DateTimeSymbols_nus', 'goog.i18n.DateTimeSymbols_nus_SD', 'goog.i18n.DateTimeSymbols_nyn', 'goog.i18n.DateTimeSymbols_nyn_UG', 'goog.i18n.DateTimeSymbols_om', 'goog.i18n.DateTimeSymbols_om_ET', 'goog.i18n.DateTimeSymbols_om_KE', 'goog.i18n.DateTimeSymbols_or_IN', 'goog.i18n.DateTimeSymbols_os', 'goog.i18n.DateTimeSymbols_os_GE', 'goog.i18n.DateTimeSymbols_os_RU', 'goog.i18n.DateTimeSymbols_pa_Arab', 'goog.i18n.DateTimeSymbols_pa_Arab_PK', 'goog.i18n.DateTimeSymbols_pa_Guru', 'goog.i18n.DateTimeSymbols_pa_Guru_IN', 'goog.i18n.DateTimeSymbols_pl_PL', 'goog.i18n.DateTimeSymbols_ps', 'goog.i18n.DateTimeSymbols_ps_AF', 'goog.i18n.DateTimeSymbols_pt_AO', 'goog.i18n.DateTimeSymbols_pt_CV', 'goog.i18n.DateTimeSymbols_pt_GW', 'goog.i18n.DateTimeSymbols_pt_MO', 'goog.i18n.DateTimeSymbols_pt_MZ', 'goog.i18n.DateTimeSymbols_pt_ST', 'goog.i18n.DateTimeSymbols_pt_TL', 'goog.i18n.DateTimeSymbols_qu', 'goog.i18n.DateTimeSymbols_qu_BO', 'goog.i18n.DateTimeSymbols_qu_EC', 'goog.i18n.DateTimeSymbols_qu_PE', 'goog.i18n.DateTimeSymbols_rm', 'goog.i18n.DateTimeSymbols_rm_CH', 'goog.i18n.DateTimeSymbols_rn', 'goog.i18n.DateTimeSymbols_rn_BI', 'goog.i18n.DateTimeSymbols_ro_MD', 'goog.i18n.DateTimeSymbols_ro_RO', 'goog.i18n.DateTimeSymbols_rof', 'goog.i18n.DateTimeSymbols_rof_TZ', 'goog.i18n.DateTimeSymbols_ru_BY', 'goog.i18n.DateTimeSymbols_ru_KG', 'goog.i18n.DateTimeSymbols_ru_KZ', 'goog.i18n.DateTimeSymbols_ru_MD', 'goog.i18n.DateTimeSymbols_ru_RU', 'goog.i18n.DateTimeSymbols_ru_UA', 'goog.i18n.DateTimeSymbols_rw', 'goog.i18n.DateTimeSymbols_rw_RW', 'goog.i18n.DateTimeSymbols_rwk', 'goog.i18n.DateTimeSymbols_rwk_TZ', 'goog.i18n.DateTimeSymbols_sah', 'goog.i18n.DateTimeSymbols_sah_RU', 'goog.i18n.DateTimeSymbols_saq', 'goog.i18n.DateTimeSymbols_saq_KE', 'goog.i18n.DateTimeSymbols_sbp', 'goog.i18n.DateTimeSymbols_sbp_TZ', 'goog.i18n.DateTimeSymbols_se', 'goog.i18n.DateTimeSymbols_se_FI', 'goog.i18n.DateTimeSymbols_se_NO', 'goog.i18n.DateTimeSymbols_se_SE', 'goog.i18n.DateTimeSymbols_seh', 'goog.i18n.DateTimeSymbols_seh_MZ', 'goog.i18n.DateTimeSymbols_ses', 'goog.i18n.DateTimeSymbols_ses_ML', 'goog.i18n.DateTimeSymbols_sg', 'goog.i18n.DateTimeSymbols_sg_CF', 'goog.i18n.DateTimeSymbols_shi', 'goog.i18n.DateTimeSymbols_shi_Latn', 'goog.i18n.DateTimeSymbols_shi_Latn_MA', 'goog.i18n.DateTimeSymbols_shi_Tfng', 'goog.i18n.DateTimeSymbols_shi_Tfng_MA', 'goog.i18n.DateTimeSymbols_si_LK', 'goog.i18n.DateTimeSymbols_sk_SK', 'goog.i18n.DateTimeSymbols_sl_SI', 'goog.i18n.DateTimeSymbols_smn', 'goog.i18n.DateTimeSymbols_smn_FI', 'goog.i18n.DateTimeSymbols_sn', 'goog.i18n.DateTimeSymbols_sn_ZW', 'goog.i18n.DateTimeSymbols_so', 'goog.i18n.DateTimeSymbols_so_DJ', 'goog.i18n.DateTimeSymbols_so_ET', 'goog.i18n.DateTimeSymbols_so_KE', 'goog.i18n.DateTimeSymbols_so_SO', 'goog.i18n.DateTimeSymbols_sq_AL', 'goog.i18n.DateTimeSymbols_sq_MK', 'goog.i18n.DateTimeSymbols_sq_XK', 'goog.i18n.DateTimeSymbols_sr_Cyrl', 'goog.i18n.DateTimeSymbols_sr_Cyrl_BA', 'goog.i18n.DateTimeSymbols_sr_Cyrl_ME', 'goog.i18n.DateTimeSymbols_sr_Cyrl_RS', 'goog.i18n.DateTimeSymbols_sr_Cyrl_XK', 'goog.i18n.DateTimeSymbols_sr_Latn', 'goog.i18n.DateTimeSymbols_sr_Latn_BA', 'goog.i18n.DateTimeSymbols_sr_Latn_ME', 'goog.i18n.DateTimeSymbols_sr_Latn_RS', 'goog.i18n.DateTimeSymbols_sr_Latn_XK', 'goog.i18n.DateTimeSymbols_ss', 'goog.i18n.DateTimeSymbols_ss_SZ', 'goog.i18n.DateTimeSymbols_ss_ZA', 'goog.i18n.DateTimeSymbols_ssy', 'goog.i18n.DateTimeSymbols_ssy_ER', 'goog.i18n.DateTimeSymbols_sv_AX', 'goog.i18n.DateTimeSymbols_sv_FI', 'goog.i18n.DateTimeSymbols_sv_SE', 'goog.i18n.DateTimeSymbols_sw_KE', 'goog.i18n.DateTimeSymbols_sw_TZ', 'goog.i18n.DateTimeSymbols_sw_UG', 'goog.i18n.DateTimeSymbols_swc', 'goog.i18n.DateTimeSymbols_swc_CD', 'goog.i18n.DateTimeSymbols_ta_IN', 'goog.i18n.DateTimeSymbols_ta_LK', 'goog.i18n.DateTimeSymbols_ta_MY', 'goog.i18n.DateTimeSymbols_ta_SG', 'goog.i18n.DateTimeSymbols_te_IN', 'goog.i18n.DateTimeSymbols_teo', 'goog.i18n.DateTimeSymbols_teo_KE', 'goog.i18n.DateTimeSymbols_teo_UG', 'goog.i18n.DateTimeSymbols_th_TH', 'goog.i18n.DateTimeSymbols_ti', 'goog.i18n.DateTimeSymbols_ti_ER', 'goog.i18n.DateTimeSymbols_ti_ET', 'goog.i18n.DateTimeSymbols_tn', 'goog.i18n.DateTimeSymbols_tn_BW', 'goog.i18n.DateTimeSymbols_tn_ZA', 'goog.i18n.DateTimeSymbols_to', 'goog.i18n.DateTimeSymbols_to_TO', 'goog.i18n.DateTimeSymbols_tr_CY', 'goog.i18n.DateTimeSymbols_tr_TR', 'goog.i18n.DateTimeSymbols_ts', 'goog.i18n.DateTimeSymbols_ts_ZA', 'goog.i18n.DateTimeSymbols_twq', 'goog.i18n.DateTimeSymbols_twq_NE', 'goog.i18n.DateTimeSymbols_tzm', 'goog.i18n.DateTimeSymbols_tzm_Latn', 'goog.i18n.DateTimeSymbols_tzm_Latn_MA', 'goog.i18n.DateTimeSymbols_ug', 'goog.i18n.DateTimeSymbols_ug_Arab', 'goog.i18n.DateTimeSymbols_ug_Arab_CN', 'goog.i18n.DateTimeSymbols_uk_UA', 'goog.i18n.DateTimeSymbols_ur_IN', 'goog.i18n.DateTimeSymbols_ur_PK', 'goog.i18n.DateTimeSymbols_uz_Arab', 'goog.i18n.DateTimeSymbols_uz_Arab_AF', 'goog.i18n.DateTimeSymbols_uz_Cyrl', 'goog.i18n.DateTimeSymbols_uz_Cyrl_UZ', 'goog.i18n.DateTimeSymbols_uz_Latn', 'goog.i18n.DateTimeSymbols_uz_Latn_UZ', 'goog.i18n.DateTimeSymbols_vai', 'goog.i18n.DateTimeSymbols_vai_Latn', 'goog.i18n.DateTimeSymbols_vai_Latn_LR', 'goog.i18n.DateTimeSymbols_vai_Vaii', 'goog.i18n.DateTimeSymbols_vai_Vaii_LR', 'goog.i18n.DateTimeSymbols_ve', 'goog.i18n.DateTimeSymbols_ve_ZA', 'goog.i18n.DateTimeSymbols_vi_VN', 'goog.i18n.DateTimeSymbols_vo', 'goog.i18n.DateTimeSymbols_vo_001', 'goog.i18n.DateTimeSymbols_vun', 'goog.i18n.DateTimeSymbols_vun_TZ', 'goog.i18n.DateTimeSymbols_wae', 'goog.i18n.DateTimeSymbols_wae_CH', 'goog.i18n.DateTimeSymbols_xog', 'goog.i18n.DateTimeSymbols_xog_UG', 'goog.i18n.DateTimeSymbols_yav', 'goog.i18n.DateTimeSymbols_yav_CM', 'goog.i18n.DateTimeSymbols_yi', 'goog.i18n.DateTimeSymbols_yi_001', 'goog.i18n.DateTimeSymbols_yo', 'goog.i18n.DateTimeSymbols_yo_BJ', 'goog.i18n.DateTimeSymbols_yo_NG', 'goog.i18n.DateTimeSymbols_zgh', 'goog.i18n.DateTimeSymbols_zgh_MA', 'goog.i18n.DateTimeSymbols_zh_Hans', 'goog.i18n.DateTimeSymbols_zh_Hans_CN', 'goog.i18n.DateTimeSymbols_zh_Hans_HK', 'goog.i18n.DateTimeSymbols_zh_Hans_MO', 'goog.i18n.DateTimeSymbols_zh_Hans_SG', 'goog.i18n.DateTimeSymbols_zh_Hant', 'goog.i18n.DateTimeSymbols_zh_Hant_HK', 'goog.i18n.DateTimeSymbols_zh_Hant_MO', 'goog.i18n.DateTimeSymbols_zh_Hant_TW', 'goog.i18n.DateTimeSymbols_zu_ZA'], ['goog.i18n.DateTimeSymbols'], false); +goog.addDependency('i18n/graphemebreak.js', ['goog.i18n.GraphemeBreak'], ['goog.structs.InversionMap'], false); +goog.addDependency('i18n/graphemebreak_test.js', ['goog.i18n.GraphemeBreakTest'], ['goog.i18n.GraphemeBreak', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/messageformat.js', ['goog.i18n.MessageFormat'], ['goog.asserts', 'goog.i18n.NumberFormat', 'goog.i18n.ordinalRules', 'goog.i18n.pluralRules'], false); +goog.addDependency('i18n/messageformat_test.js', ['goog.i18n.MessageFormatTest'], ['goog.i18n.MessageFormat', 'goog.i18n.NumberFormatSymbols_hr', 'goog.i18n.pluralRules', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/mime.js', ['goog.i18n.mime', 'goog.i18n.mime.encode'], ['goog.array'], false); +goog.addDependency('i18n/mime_test.js', ['goog.i18n.mime.encodeTest'], ['goog.i18n.mime.encode', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/numberformat.js', ['goog.i18n.NumberFormat', 'goog.i18n.NumberFormat.CurrencyStyle', 'goog.i18n.NumberFormat.Format'], ['goog.asserts', 'goog.i18n.CompactNumberFormatSymbols', 'goog.i18n.NumberFormatSymbols', 'goog.i18n.currency', 'goog.math'], false); +goog.addDependency('i18n/numberformat_test.js', ['goog.i18n.NumberFormatTest'], ['goog.i18n.CompactNumberFormatSymbols', 'goog.i18n.CompactNumberFormatSymbols_de', 'goog.i18n.CompactNumberFormatSymbols_en', 'goog.i18n.CompactNumberFormatSymbols_fr', 'goog.i18n.NumberFormat', 'goog.i18n.NumberFormatSymbols', 'goog.i18n.NumberFormatSymbols_de', 'goog.i18n.NumberFormatSymbols_en', 'goog.i18n.NumberFormatSymbols_fr', 'goog.i18n.NumberFormatSymbols_pl', 'goog.i18n.NumberFormatSymbols_ro', 'goog.testing.ExpectedFailures', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('i18n/numberformatsymbols.js', ['goog.i18n.NumberFormatSymbols', 'goog.i18n.NumberFormatSymbols_af', 'goog.i18n.NumberFormatSymbols_af_ZA', 'goog.i18n.NumberFormatSymbols_am', 'goog.i18n.NumberFormatSymbols_am_ET', 'goog.i18n.NumberFormatSymbols_ar', 'goog.i18n.NumberFormatSymbols_ar_001', 'goog.i18n.NumberFormatSymbols_az', 'goog.i18n.NumberFormatSymbols_az_Latn_AZ', 'goog.i18n.NumberFormatSymbols_bg', 'goog.i18n.NumberFormatSymbols_bg_BG', 'goog.i18n.NumberFormatSymbols_bn', 'goog.i18n.NumberFormatSymbols_bn_BD', 'goog.i18n.NumberFormatSymbols_br', 'goog.i18n.NumberFormatSymbols_br_FR', 'goog.i18n.NumberFormatSymbols_ca', 'goog.i18n.NumberFormatSymbols_ca_AD', 'goog.i18n.NumberFormatSymbols_ca_ES', 'goog.i18n.NumberFormatSymbols_ca_ES_VALENCIA', 'goog.i18n.NumberFormatSymbols_ca_FR', 'goog.i18n.NumberFormatSymbols_ca_IT', 'goog.i18n.NumberFormatSymbols_chr', 'goog.i18n.NumberFormatSymbols_chr_US', 'goog.i18n.NumberFormatSymbols_cs', 'goog.i18n.NumberFormatSymbols_cs_CZ', 'goog.i18n.NumberFormatSymbols_cy', 'goog.i18n.NumberFormatSymbols_cy_GB', 'goog.i18n.NumberFormatSymbols_da', 'goog.i18n.NumberFormatSymbols_da_DK', 'goog.i18n.NumberFormatSymbols_da_GL', 'goog.i18n.NumberFormatSymbols_de', 'goog.i18n.NumberFormatSymbols_de_AT', 'goog.i18n.NumberFormatSymbols_de_BE', 'goog.i18n.NumberFormatSymbols_de_CH', 'goog.i18n.NumberFormatSymbols_de_DE', 'goog.i18n.NumberFormatSymbols_de_LU', 'goog.i18n.NumberFormatSymbols_el', 'goog.i18n.NumberFormatSymbols_el_GR', 'goog.i18n.NumberFormatSymbols_en', 'goog.i18n.NumberFormatSymbols_en_001', 'goog.i18n.NumberFormatSymbols_en_AS', 'goog.i18n.NumberFormatSymbols_en_AU', 'goog.i18n.NumberFormatSymbols_en_DG', 'goog.i18n.NumberFormatSymbols_en_FM', 'goog.i18n.NumberFormatSymbols_en_GB', 'goog.i18n.NumberFormatSymbols_en_GU', 'goog.i18n.NumberFormatSymbols_en_IE', 'goog.i18n.NumberFormatSymbols_en_IN', 'goog.i18n.NumberFormatSymbols_en_IO', 'goog.i18n.NumberFormatSymbols_en_MH', 'goog.i18n.NumberFormatSymbols_en_MP', 'goog.i18n.NumberFormatSymbols_en_PR', 'goog.i18n.NumberFormatSymbols_en_PW', 'goog.i18n.NumberFormatSymbols_en_SG', 'goog.i18n.NumberFormatSymbols_en_TC', 'goog.i18n.NumberFormatSymbols_en_UM', 'goog.i18n.NumberFormatSymbols_en_US', 'goog.i18n.NumberFormatSymbols_en_VG', 'goog.i18n.NumberFormatSymbols_en_VI', 'goog.i18n.NumberFormatSymbols_en_ZA', 'goog.i18n.NumberFormatSymbols_en_ZW', 'goog.i18n.NumberFormatSymbols_es', 'goog.i18n.NumberFormatSymbols_es_419', 'goog.i18n.NumberFormatSymbols_es_EA', 'goog.i18n.NumberFormatSymbols_es_ES', 'goog.i18n.NumberFormatSymbols_es_IC', 'goog.i18n.NumberFormatSymbols_et', 'goog.i18n.NumberFormatSymbols_et_EE', 'goog.i18n.NumberFormatSymbols_eu', 'goog.i18n.NumberFormatSymbols_eu_ES', 'goog.i18n.NumberFormatSymbols_fa', 'goog.i18n.NumberFormatSymbols_fa_IR', 'goog.i18n.NumberFormatSymbols_fi', 'goog.i18n.NumberFormatSymbols_fi_FI', 'goog.i18n.NumberFormatSymbols_fil', 'goog.i18n.NumberFormatSymbols_fil_PH', 'goog.i18n.NumberFormatSymbols_fr', 'goog.i18n.NumberFormatSymbols_fr_BL', 'goog.i18n.NumberFormatSymbols_fr_CA', 'goog.i18n.NumberFormatSymbols_fr_FR', 'goog.i18n.NumberFormatSymbols_fr_GF', 'goog.i18n.NumberFormatSymbols_fr_GP', 'goog.i18n.NumberFormatSymbols_fr_MC', 'goog.i18n.NumberFormatSymbols_fr_MF', 'goog.i18n.NumberFormatSymbols_fr_MQ', 'goog.i18n.NumberFormatSymbols_fr_PM', 'goog.i18n.NumberFormatSymbols_fr_RE', 'goog.i18n.NumberFormatSymbols_fr_YT', 'goog.i18n.NumberFormatSymbols_ga', 'goog.i18n.NumberFormatSymbols_ga_IE', 'goog.i18n.NumberFormatSymbols_gl', 'goog.i18n.NumberFormatSymbols_gl_ES', 'goog.i18n.NumberFormatSymbols_gsw', 'goog.i18n.NumberFormatSymbols_gsw_CH', 'goog.i18n.NumberFormatSymbols_gsw_LI', 'goog.i18n.NumberFormatSymbols_gu', 'goog.i18n.NumberFormatSymbols_gu_IN', 'goog.i18n.NumberFormatSymbols_haw', 'goog.i18n.NumberFormatSymbols_haw_US', 'goog.i18n.NumberFormatSymbols_he', 'goog.i18n.NumberFormatSymbols_he_IL', 'goog.i18n.NumberFormatSymbols_hi', 'goog.i18n.NumberFormatSymbols_hi_IN', 'goog.i18n.NumberFormatSymbols_hr', 'goog.i18n.NumberFormatSymbols_hr_HR', 'goog.i18n.NumberFormatSymbols_hu', 'goog.i18n.NumberFormatSymbols_hu_HU', 'goog.i18n.NumberFormatSymbols_hy', 'goog.i18n.NumberFormatSymbols_hy_AM', 'goog.i18n.NumberFormatSymbols_id', 'goog.i18n.NumberFormatSymbols_id_ID', 'goog.i18n.NumberFormatSymbols_in', 'goog.i18n.NumberFormatSymbols_is', 'goog.i18n.NumberFormatSymbols_is_IS', 'goog.i18n.NumberFormatSymbols_it', 'goog.i18n.NumberFormatSymbols_it_IT', 'goog.i18n.NumberFormatSymbols_it_SM', 'goog.i18n.NumberFormatSymbols_iw', 'goog.i18n.NumberFormatSymbols_ja', 'goog.i18n.NumberFormatSymbols_ja_JP', 'goog.i18n.NumberFormatSymbols_ka', 'goog.i18n.NumberFormatSymbols_ka_GE', 'goog.i18n.NumberFormatSymbols_kk', 'goog.i18n.NumberFormatSymbols_kk_Cyrl_KZ', 'goog.i18n.NumberFormatSymbols_km', 'goog.i18n.NumberFormatSymbols_km_KH', 'goog.i18n.NumberFormatSymbols_kn', 'goog.i18n.NumberFormatSymbols_kn_IN', 'goog.i18n.NumberFormatSymbols_ko', 'goog.i18n.NumberFormatSymbols_ko_KR', 'goog.i18n.NumberFormatSymbols_ky', 'goog.i18n.NumberFormatSymbols_ky_Cyrl_KG', 'goog.i18n.NumberFormatSymbols_ln', 'goog.i18n.NumberFormatSymbols_ln_CD', 'goog.i18n.NumberFormatSymbols_lo', 'goog.i18n.NumberFormatSymbols_lo_LA', 'goog.i18n.NumberFormatSymbols_lt', 'goog.i18n.NumberFormatSymbols_lt_LT', 'goog.i18n.NumberFormatSymbols_lv', 'goog.i18n.NumberFormatSymbols_lv_LV', 'goog.i18n.NumberFormatSymbols_mk', 'goog.i18n.NumberFormatSymbols_mk_MK', 'goog.i18n.NumberFormatSymbols_ml', 'goog.i18n.NumberFormatSymbols_ml_IN', 'goog.i18n.NumberFormatSymbols_mn', 'goog.i18n.NumberFormatSymbols_mn_Cyrl_MN', 'goog.i18n.NumberFormatSymbols_mr', 'goog.i18n.NumberFormatSymbols_mr_IN', 'goog.i18n.NumberFormatSymbols_ms', 'goog.i18n.NumberFormatSymbols_ms_Latn_MY', 'goog.i18n.NumberFormatSymbols_mt', 'goog.i18n.NumberFormatSymbols_mt_MT', 'goog.i18n.NumberFormatSymbols_my', 'goog.i18n.NumberFormatSymbols_my_MM', 'goog.i18n.NumberFormatSymbols_nb', 'goog.i18n.NumberFormatSymbols_nb_NO', 'goog.i18n.NumberFormatSymbols_nb_SJ', 'goog.i18n.NumberFormatSymbols_ne', 'goog.i18n.NumberFormatSymbols_ne_NP', 'goog.i18n.NumberFormatSymbols_nl', 'goog.i18n.NumberFormatSymbols_nl_NL', 'goog.i18n.NumberFormatSymbols_no', 'goog.i18n.NumberFormatSymbols_no_NO', 'goog.i18n.NumberFormatSymbols_or', 'goog.i18n.NumberFormatSymbols_or_IN', 'goog.i18n.NumberFormatSymbols_pa', 'goog.i18n.NumberFormatSymbols_pa_Guru_IN', 'goog.i18n.NumberFormatSymbols_pl', 'goog.i18n.NumberFormatSymbols_pl_PL', 'goog.i18n.NumberFormatSymbols_pt', 'goog.i18n.NumberFormatSymbols_pt_BR', 'goog.i18n.NumberFormatSymbols_pt_PT', 'goog.i18n.NumberFormatSymbols_ro', 'goog.i18n.NumberFormatSymbols_ro_RO', 'goog.i18n.NumberFormatSymbols_ru', 'goog.i18n.NumberFormatSymbols_ru_RU', 'goog.i18n.NumberFormatSymbols_si', 'goog.i18n.NumberFormatSymbols_si_LK', 'goog.i18n.NumberFormatSymbols_sk', 'goog.i18n.NumberFormatSymbols_sk_SK', 'goog.i18n.NumberFormatSymbols_sl', 'goog.i18n.NumberFormatSymbols_sl_SI', 'goog.i18n.NumberFormatSymbols_sq', 'goog.i18n.NumberFormatSymbols_sq_AL', 'goog.i18n.NumberFormatSymbols_sr', 'goog.i18n.NumberFormatSymbols_sr_Cyrl_RS', 'goog.i18n.NumberFormatSymbols_sv', 'goog.i18n.NumberFormatSymbols_sv_SE', 'goog.i18n.NumberFormatSymbols_sw', 'goog.i18n.NumberFormatSymbols_sw_TZ', 'goog.i18n.NumberFormatSymbols_ta', 'goog.i18n.NumberFormatSymbols_ta_IN', 'goog.i18n.NumberFormatSymbols_te', 'goog.i18n.NumberFormatSymbols_te_IN', 'goog.i18n.NumberFormatSymbols_th', 'goog.i18n.NumberFormatSymbols_th_TH', 'goog.i18n.NumberFormatSymbols_tl', 'goog.i18n.NumberFormatSymbols_tr', 'goog.i18n.NumberFormatSymbols_tr_TR', 'goog.i18n.NumberFormatSymbols_uk', 'goog.i18n.NumberFormatSymbols_uk_UA', 'goog.i18n.NumberFormatSymbols_ur', 'goog.i18n.NumberFormatSymbols_ur_PK', 'goog.i18n.NumberFormatSymbols_uz', 'goog.i18n.NumberFormatSymbols_uz_Latn_UZ', 'goog.i18n.NumberFormatSymbols_vi', 'goog.i18n.NumberFormatSymbols_vi_VN', 'goog.i18n.NumberFormatSymbols_zh', 'goog.i18n.NumberFormatSymbols_zh_CN', 'goog.i18n.NumberFormatSymbols_zh_HK', 'goog.i18n.NumberFormatSymbols_zh_Hans_CN', 'goog.i18n.NumberFormatSymbols_zh_TW', 'goog.i18n.NumberFormatSymbols_zu', 'goog.i18n.NumberFormatSymbols_zu_ZA'], [], false); +goog.addDependency('i18n/numberformatsymbolsext.js', ['goog.i18n.NumberFormatSymbolsExt', 'goog.i18n.NumberFormatSymbols_aa', 'goog.i18n.NumberFormatSymbols_aa_DJ', 'goog.i18n.NumberFormatSymbols_aa_ER', 'goog.i18n.NumberFormatSymbols_aa_ET', 'goog.i18n.NumberFormatSymbols_af_NA', 'goog.i18n.NumberFormatSymbols_agq', 'goog.i18n.NumberFormatSymbols_agq_CM', 'goog.i18n.NumberFormatSymbols_ak', 'goog.i18n.NumberFormatSymbols_ak_GH', 'goog.i18n.NumberFormatSymbols_ar_AE', 'goog.i18n.NumberFormatSymbols_ar_BH', 'goog.i18n.NumberFormatSymbols_ar_DJ', 'goog.i18n.NumberFormatSymbols_ar_DZ', 'goog.i18n.NumberFormatSymbols_ar_EG', 'goog.i18n.NumberFormatSymbols_ar_EH', 'goog.i18n.NumberFormatSymbols_ar_ER', 'goog.i18n.NumberFormatSymbols_ar_IL', 'goog.i18n.NumberFormatSymbols_ar_IQ', 'goog.i18n.NumberFormatSymbols_ar_JO', 'goog.i18n.NumberFormatSymbols_ar_KM', 'goog.i18n.NumberFormatSymbols_ar_KW', 'goog.i18n.NumberFormatSymbols_ar_LB', 'goog.i18n.NumberFormatSymbols_ar_LY', 'goog.i18n.NumberFormatSymbols_ar_MA', 'goog.i18n.NumberFormatSymbols_ar_MR', 'goog.i18n.NumberFormatSymbols_ar_OM', 'goog.i18n.NumberFormatSymbols_ar_PS', 'goog.i18n.NumberFormatSymbols_ar_QA', 'goog.i18n.NumberFormatSymbols_ar_SA', 'goog.i18n.NumberFormatSymbols_ar_SD', 'goog.i18n.NumberFormatSymbols_ar_SO', 'goog.i18n.NumberFormatSymbols_ar_SS', 'goog.i18n.NumberFormatSymbols_ar_SY', 'goog.i18n.NumberFormatSymbols_ar_TD', 'goog.i18n.NumberFormatSymbols_ar_TN', 'goog.i18n.NumberFormatSymbols_ar_YE', 'goog.i18n.NumberFormatSymbols_as', 'goog.i18n.NumberFormatSymbols_as_IN', 'goog.i18n.NumberFormatSymbols_asa', 'goog.i18n.NumberFormatSymbols_asa_TZ', 'goog.i18n.NumberFormatSymbols_ast', 'goog.i18n.NumberFormatSymbols_ast_ES', 'goog.i18n.NumberFormatSymbols_az_Cyrl', 'goog.i18n.NumberFormatSymbols_az_Cyrl_AZ', 'goog.i18n.NumberFormatSymbols_az_Latn', 'goog.i18n.NumberFormatSymbols_bas', 'goog.i18n.NumberFormatSymbols_bas_CM', 'goog.i18n.NumberFormatSymbols_be', 'goog.i18n.NumberFormatSymbols_be_BY', 'goog.i18n.NumberFormatSymbols_bem', 'goog.i18n.NumberFormatSymbols_bem_ZM', 'goog.i18n.NumberFormatSymbols_bez', 'goog.i18n.NumberFormatSymbols_bez_TZ', 'goog.i18n.NumberFormatSymbols_bm', 'goog.i18n.NumberFormatSymbols_bm_Latn', 'goog.i18n.NumberFormatSymbols_bm_Latn_ML', 'goog.i18n.NumberFormatSymbols_bn_IN', 'goog.i18n.NumberFormatSymbols_bo', 'goog.i18n.NumberFormatSymbols_bo_CN', 'goog.i18n.NumberFormatSymbols_bo_IN', 'goog.i18n.NumberFormatSymbols_brx', 'goog.i18n.NumberFormatSymbols_brx_IN', 'goog.i18n.NumberFormatSymbols_bs', 'goog.i18n.NumberFormatSymbols_bs_Cyrl', 'goog.i18n.NumberFormatSymbols_bs_Cyrl_BA', 'goog.i18n.NumberFormatSymbols_bs_Latn', 'goog.i18n.NumberFormatSymbols_bs_Latn_BA', 'goog.i18n.NumberFormatSymbols_cgg', 'goog.i18n.NumberFormatSymbols_cgg_UG', 'goog.i18n.NumberFormatSymbols_ckb', 'goog.i18n.NumberFormatSymbols_ckb_Arab', 'goog.i18n.NumberFormatSymbols_ckb_Arab_IQ', 'goog.i18n.NumberFormatSymbols_ckb_Arab_IR', 'goog.i18n.NumberFormatSymbols_ckb_IQ', 'goog.i18n.NumberFormatSymbols_ckb_IR', 'goog.i18n.NumberFormatSymbols_ckb_Latn', 'goog.i18n.NumberFormatSymbols_ckb_Latn_IQ', 'goog.i18n.NumberFormatSymbols_dav', 'goog.i18n.NumberFormatSymbols_dav_KE', 'goog.i18n.NumberFormatSymbols_de_LI', 'goog.i18n.NumberFormatSymbols_dje', 'goog.i18n.NumberFormatSymbols_dje_NE', 'goog.i18n.NumberFormatSymbols_dsb', 'goog.i18n.NumberFormatSymbols_dsb_DE', 'goog.i18n.NumberFormatSymbols_dua', 'goog.i18n.NumberFormatSymbols_dua_CM', 'goog.i18n.NumberFormatSymbols_dyo', 'goog.i18n.NumberFormatSymbols_dyo_SN', 'goog.i18n.NumberFormatSymbols_dz', 'goog.i18n.NumberFormatSymbols_dz_BT', 'goog.i18n.NumberFormatSymbols_ebu', 'goog.i18n.NumberFormatSymbols_ebu_KE', 'goog.i18n.NumberFormatSymbols_ee', 'goog.i18n.NumberFormatSymbols_ee_GH', 'goog.i18n.NumberFormatSymbols_ee_TG', 'goog.i18n.NumberFormatSymbols_el_CY', 'goog.i18n.NumberFormatSymbols_en_150', 'goog.i18n.NumberFormatSymbols_en_AG', 'goog.i18n.NumberFormatSymbols_en_AI', 'goog.i18n.NumberFormatSymbols_en_BB', 'goog.i18n.NumberFormatSymbols_en_BE', 'goog.i18n.NumberFormatSymbols_en_BM', 'goog.i18n.NumberFormatSymbols_en_BS', 'goog.i18n.NumberFormatSymbols_en_BW', 'goog.i18n.NumberFormatSymbols_en_BZ', 'goog.i18n.NumberFormatSymbols_en_CA', 'goog.i18n.NumberFormatSymbols_en_CC', 'goog.i18n.NumberFormatSymbols_en_CK', 'goog.i18n.NumberFormatSymbols_en_CM', 'goog.i18n.NumberFormatSymbols_en_CX', 'goog.i18n.NumberFormatSymbols_en_DM', 'goog.i18n.NumberFormatSymbols_en_ER', 'goog.i18n.NumberFormatSymbols_en_FJ', 'goog.i18n.NumberFormatSymbols_en_FK', 'goog.i18n.NumberFormatSymbols_en_GD', 'goog.i18n.NumberFormatSymbols_en_GG', 'goog.i18n.NumberFormatSymbols_en_GH', 'goog.i18n.NumberFormatSymbols_en_GI', 'goog.i18n.NumberFormatSymbols_en_GM', 'goog.i18n.NumberFormatSymbols_en_GY', 'goog.i18n.NumberFormatSymbols_en_HK', 'goog.i18n.NumberFormatSymbols_en_IM', 'goog.i18n.NumberFormatSymbols_en_JE', 'goog.i18n.NumberFormatSymbols_en_JM', 'goog.i18n.NumberFormatSymbols_en_KE', 'goog.i18n.NumberFormatSymbols_en_KI', 'goog.i18n.NumberFormatSymbols_en_KN', 'goog.i18n.NumberFormatSymbols_en_KY', 'goog.i18n.NumberFormatSymbols_en_LC', 'goog.i18n.NumberFormatSymbols_en_LR', 'goog.i18n.NumberFormatSymbols_en_LS', 'goog.i18n.NumberFormatSymbols_en_MG', 'goog.i18n.NumberFormatSymbols_en_MO', 'goog.i18n.NumberFormatSymbols_en_MS', 'goog.i18n.NumberFormatSymbols_en_MT', 'goog.i18n.NumberFormatSymbols_en_MU', 'goog.i18n.NumberFormatSymbols_en_MW', 'goog.i18n.NumberFormatSymbols_en_MY', 'goog.i18n.NumberFormatSymbols_en_NA', 'goog.i18n.NumberFormatSymbols_en_NF', 'goog.i18n.NumberFormatSymbols_en_NG', 'goog.i18n.NumberFormatSymbols_en_NR', 'goog.i18n.NumberFormatSymbols_en_NU', 'goog.i18n.NumberFormatSymbols_en_NZ', 'goog.i18n.NumberFormatSymbols_en_PG', 'goog.i18n.NumberFormatSymbols_en_PH', 'goog.i18n.NumberFormatSymbols_en_PK', 'goog.i18n.NumberFormatSymbols_en_PN', 'goog.i18n.NumberFormatSymbols_en_RW', 'goog.i18n.NumberFormatSymbols_en_SB', 'goog.i18n.NumberFormatSymbols_en_SC', 'goog.i18n.NumberFormatSymbols_en_SD', 'goog.i18n.NumberFormatSymbols_en_SH', 'goog.i18n.NumberFormatSymbols_en_SL', 'goog.i18n.NumberFormatSymbols_en_SS', 'goog.i18n.NumberFormatSymbols_en_SX', 'goog.i18n.NumberFormatSymbols_en_SZ', 'goog.i18n.NumberFormatSymbols_en_TK', 'goog.i18n.NumberFormatSymbols_en_TO', 'goog.i18n.NumberFormatSymbols_en_TT', 'goog.i18n.NumberFormatSymbols_en_TV', 'goog.i18n.NumberFormatSymbols_en_TZ', 'goog.i18n.NumberFormatSymbols_en_UG', 'goog.i18n.NumberFormatSymbols_en_VC', 'goog.i18n.NumberFormatSymbols_en_VU', 'goog.i18n.NumberFormatSymbols_en_WS', 'goog.i18n.NumberFormatSymbols_en_ZM', 'goog.i18n.NumberFormatSymbols_eo', 'goog.i18n.NumberFormatSymbols_eo_001', 'goog.i18n.NumberFormatSymbols_es_AR', 'goog.i18n.NumberFormatSymbols_es_BO', 'goog.i18n.NumberFormatSymbols_es_CL', 'goog.i18n.NumberFormatSymbols_es_CO', 'goog.i18n.NumberFormatSymbols_es_CR', 'goog.i18n.NumberFormatSymbols_es_CU', 'goog.i18n.NumberFormatSymbols_es_DO', 'goog.i18n.NumberFormatSymbols_es_EC', 'goog.i18n.NumberFormatSymbols_es_GQ', 'goog.i18n.NumberFormatSymbols_es_GT', 'goog.i18n.NumberFormatSymbols_es_HN', 'goog.i18n.NumberFormatSymbols_es_MX', 'goog.i18n.NumberFormatSymbols_es_NI', 'goog.i18n.NumberFormatSymbols_es_PA', 'goog.i18n.NumberFormatSymbols_es_PE', 'goog.i18n.NumberFormatSymbols_es_PH', 'goog.i18n.NumberFormatSymbols_es_PR', 'goog.i18n.NumberFormatSymbols_es_PY', 'goog.i18n.NumberFormatSymbols_es_SV', 'goog.i18n.NumberFormatSymbols_es_US', 'goog.i18n.NumberFormatSymbols_es_UY', 'goog.i18n.NumberFormatSymbols_es_VE', 'goog.i18n.NumberFormatSymbols_ewo', 'goog.i18n.NumberFormatSymbols_ewo_CM', 'goog.i18n.NumberFormatSymbols_fa_AF', 'goog.i18n.NumberFormatSymbols_ff', 'goog.i18n.NumberFormatSymbols_ff_CM', 'goog.i18n.NumberFormatSymbols_ff_GN', 'goog.i18n.NumberFormatSymbols_ff_MR', 'goog.i18n.NumberFormatSymbols_ff_SN', 'goog.i18n.NumberFormatSymbols_fo', 'goog.i18n.NumberFormatSymbols_fo_FO', 'goog.i18n.NumberFormatSymbols_fr_BE', 'goog.i18n.NumberFormatSymbols_fr_BF', 'goog.i18n.NumberFormatSymbols_fr_BI', 'goog.i18n.NumberFormatSymbols_fr_BJ', 'goog.i18n.NumberFormatSymbols_fr_CD', 'goog.i18n.NumberFormatSymbols_fr_CF', 'goog.i18n.NumberFormatSymbols_fr_CG', 'goog.i18n.NumberFormatSymbols_fr_CH', 'goog.i18n.NumberFormatSymbols_fr_CI', 'goog.i18n.NumberFormatSymbols_fr_CM', 'goog.i18n.NumberFormatSymbols_fr_DJ', 'goog.i18n.NumberFormatSymbols_fr_DZ', 'goog.i18n.NumberFormatSymbols_fr_GA', 'goog.i18n.NumberFormatSymbols_fr_GN', 'goog.i18n.NumberFormatSymbols_fr_GQ', 'goog.i18n.NumberFormatSymbols_fr_HT', 'goog.i18n.NumberFormatSymbols_fr_KM', 'goog.i18n.NumberFormatSymbols_fr_LU', 'goog.i18n.NumberFormatSymbols_fr_MA', 'goog.i18n.NumberFormatSymbols_fr_MG', 'goog.i18n.NumberFormatSymbols_fr_ML', 'goog.i18n.NumberFormatSymbols_fr_MR', 'goog.i18n.NumberFormatSymbols_fr_MU', 'goog.i18n.NumberFormatSymbols_fr_NC', 'goog.i18n.NumberFormatSymbols_fr_NE', 'goog.i18n.NumberFormatSymbols_fr_PF', 'goog.i18n.NumberFormatSymbols_fr_RW', 'goog.i18n.NumberFormatSymbols_fr_SC', 'goog.i18n.NumberFormatSymbols_fr_SN', 'goog.i18n.NumberFormatSymbols_fr_SY', 'goog.i18n.NumberFormatSymbols_fr_TD', 'goog.i18n.NumberFormatSymbols_fr_TG', 'goog.i18n.NumberFormatSymbols_fr_TN', 'goog.i18n.NumberFormatSymbols_fr_VU', 'goog.i18n.NumberFormatSymbols_fr_WF', 'goog.i18n.NumberFormatSymbols_fur', 'goog.i18n.NumberFormatSymbols_fur_IT', 'goog.i18n.NumberFormatSymbols_fy', 'goog.i18n.NumberFormatSymbols_fy_NL', 'goog.i18n.NumberFormatSymbols_gd', 'goog.i18n.NumberFormatSymbols_gd_GB', 'goog.i18n.NumberFormatSymbols_gsw_FR', 'goog.i18n.NumberFormatSymbols_guz', 'goog.i18n.NumberFormatSymbols_guz_KE', 'goog.i18n.NumberFormatSymbols_gv', 'goog.i18n.NumberFormatSymbols_gv_IM', 'goog.i18n.NumberFormatSymbols_ha', 'goog.i18n.NumberFormatSymbols_ha_Latn', 'goog.i18n.NumberFormatSymbols_ha_Latn_GH', 'goog.i18n.NumberFormatSymbols_ha_Latn_NE', 'goog.i18n.NumberFormatSymbols_ha_Latn_NG', 'goog.i18n.NumberFormatSymbols_hr_BA', 'goog.i18n.NumberFormatSymbols_hsb', 'goog.i18n.NumberFormatSymbols_hsb_DE', 'goog.i18n.NumberFormatSymbols_ia', 'goog.i18n.NumberFormatSymbols_ia_FR', 'goog.i18n.NumberFormatSymbols_ig', 'goog.i18n.NumberFormatSymbols_ig_NG', 'goog.i18n.NumberFormatSymbols_ii', 'goog.i18n.NumberFormatSymbols_ii_CN', 'goog.i18n.NumberFormatSymbols_it_CH', 'goog.i18n.NumberFormatSymbols_jgo', 'goog.i18n.NumberFormatSymbols_jgo_CM', 'goog.i18n.NumberFormatSymbols_jmc', 'goog.i18n.NumberFormatSymbols_jmc_TZ', 'goog.i18n.NumberFormatSymbols_kab', 'goog.i18n.NumberFormatSymbols_kab_DZ', 'goog.i18n.NumberFormatSymbols_kam', 'goog.i18n.NumberFormatSymbols_kam_KE', 'goog.i18n.NumberFormatSymbols_kde', 'goog.i18n.NumberFormatSymbols_kde_TZ', 'goog.i18n.NumberFormatSymbols_kea', 'goog.i18n.NumberFormatSymbols_kea_CV', 'goog.i18n.NumberFormatSymbols_khq', 'goog.i18n.NumberFormatSymbols_khq_ML', 'goog.i18n.NumberFormatSymbols_ki', 'goog.i18n.NumberFormatSymbols_ki_KE', 'goog.i18n.NumberFormatSymbols_kk_Cyrl', 'goog.i18n.NumberFormatSymbols_kkj', 'goog.i18n.NumberFormatSymbols_kkj_CM', 'goog.i18n.NumberFormatSymbols_kl', 'goog.i18n.NumberFormatSymbols_kl_GL', 'goog.i18n.NumberFormatSymbols_kln', 'goog.i18n.NumberFormatSymbols_kln_KE', 'goog.i18n.NumberFormatSymbols_ko_KP', 'goog.i18n.NumberFormatSymbols_kok', 'goog.i18n.NumberFormatSymbols_kok_IN', 'goog.i18n.NumberFormatSymbols_ks', 'goog.i18n.NumberFormatSymbols_ks_Arab', 'goog.i18n.NumberFormatSymbols_ks_Arab_IN', 'goog.i18n.NumberFormatSymbols_ksb', 'goog.i18n.NumberFormatSymbols_ksb_TZ', 'goog.i18n.NumberFormatSymbols_ksf', 'goog.i18n.NumberFormatSymbols_ksf_CM', 'goog.i18n.NumberFormatSymbols_ksh', 'goog.i18n.NumberFormatSymbols_ksh_DE', 'goog.i18n.NumberFormatSymbols_kw', 'goog.i18n.NumberFormatSymbols_kw_GB', 'goog.i18n.NumberFormatSymbols_ky_Cyrl', 'goog.i18n.NumberFormatSymbols_lag', 'goog.i18n.NumberFormatSymbols_lag_TZ', 'goog.i18n.NumberFormatSymbols_lb', 'goog.i18n.NumberFormatSymbols_lb_LU', 'goog.i18n.NumberFormatSymbols_lg', 'goog.i18n.NumberFormatSymbols_lg_UG', 'goog.i18n.NumberFormatSymbols_lkt', 'goog.i18n.NumberFormatSymbols_lkt_US', 'goog.i18n.NumberFormatSymbols_ln_AO', 'goog.i18n.NumberFormatSymbols_ln_CF', 'goog.i18n.NumberFormatSymbols_ln_CG', 'goog.i18n.NumberFormatSymbols_lu', 'goog.i18n.NumberFormatSymbols_lu_CD', 'goog.i18n.NumberFormatSymbols_luo', 'goog.i18n.NumberFormatSymbols_luo_KE', 'goog.i18n.NumberFormatSymbols_luy', 'goog.i18n.NumberFormatSymbols_luy_KE', 'goog.i18n.NumberFormatSymbols_mas', 'goog.i18n.NumberFormatSymbols_mas_KE', 'goog.i18n.NumberFormatSymbols_mas_TZ', 'goog.i18n.NumberFormatSymbols_mer', 'goog.i18n.NumberFormatSymbols_mer_KE', 'goog.i18n.NumberFormatSymbols_mfe', 'goog.i18n.NumberFormatSymbols_mfe_MU', 'goog.i18n.NumberFormatSymbols_mg', 'goog.i18n.NumberFormatSymbols_mg_MG', 'goog.i18n.NumberFormatSymbols_mgh', 'goog.i18n.NumberFormatSymbols_mgh_MZ', 'goog.i18n.NumberFormatSymbols_mgo', 'goog.i18n.NumberFormatSymbols_mgo_CM', 'goog.i18n.NumberFormatSymbols_mn_Cyrl', 'goog.i18n.NumberFormatSymbols_ms_Latn', 'goog.i18n.NumberFormatSymbols_ms_Latn_BN', 'goog.i18n.NumberFormatSymbols_ms_Latn_SG', 'goog.i18n.NumberFormatSymbols_mua', 'goog.i18n.NumberFormatSymbols_mua_CM', 'goog.i18n.NumberFormatSymbols_naq', 'goog.i18n.NumberFormatSymbols_naq_NA', 'goog.i18n.NumberFormatSymbols_nd', 'goog.i18n.NumberFormatSymbols_nd_ZW', 'goog.i18n.NumberFormatSymbols_ne_IN', 'goog.i18n.NumberFormatSymbols_nl_AW', 'goog.i18n.NumberFormatSymbols_nl_BE', 'goog.i18n.NumberFormatSymbols_nl_BQ', 'goog.i18n.NumberFormatSymbols_nl_CW', 'goog.i18n.NumberFormatSymbols_nl_SR', 'goog.i18n.NumberFormatSymbols_nl_SX', 'goog.i18n.NumberFormatSymbols_nmg', 'goog.i18n.NumberFormatSymbols_nmg_CM', 'goog.i18n.NumberFormatSymbols_nn', 'goog.i18n.NumberFormatSymbols_nn_NO', 'goog.i18n.NumberFormatSymbols_nnh', 'goog.i18n.NumberFormatSymbols_nnh_CM', 'goog.i18n.NumberFormatSymbols_nr', 'goog.i18n.NumberFormatSymbols_nr_ZA', 'goog.i18n.NumberFormatSymbols_nso', 'goog.i18n.NumberFormatSymbols_nso_ZA', 'goog.i18n.NumberFormatSymbols_nus', 'goog.i18n.NumberFormatSymbols_nus_SD', 'goog.i18n.NumberFormatSymbols_nyn', 'goog.i18n.NumberFormatSymbols_nyn_UG', 'goog.i18n.NumberFormatSymbols_om', 'goog.i18n.NumberFormatSymbols_om_ET', 'goog.i18n.NumberFormatSymbols_om_KE', 'goog.i18n.NumberFormatSymbols_os', 'goog.i18n.NumberFormatSymbols_os_GE', 'goog.i18n.NumberFormatSymbols_os_RU', 'goog.i18n.NumberFormatSymbols_pa_Arab', 'goog.i18n.NumberFormatSymbols_pa_Arab_PK', 'goog.i18n.NumberFormatSymbols_pa_Guru', 'goog.i18n.NumberFormatSymbols_ps', 'goog.i18n.NumberFormatSymbols_ps_AF', 'goog.i18n.NumberFormatSymbols_pt_AO', 'goog.i18n.NumberFormatSymbols_pt_CV', 'goog.i18n.NumberFormatSymbols_pt_GW', 'goog.i18n.NumberFormatSymbols_pt_MO', 'goog.i18n.NumberFormatSymbols_pt_MZ', 'goog.i18n.NumberFormatSymbols_pt_ST', 'goog.i18n.NumberFormatSymbols_pt_TL', 'goog.i18n.NumberFormatSymbols_qu', 'goog.i18n.NumberFormatSymbols_qu_BO', 'goog.i18n.NumberFormatSymbols_qu_EC', 'goog.i18n.NumberFormatSymbols_qu_PE', 'goog.i18n.NumberFormatSymbols_rm', 'goog.i18n.NumberFormatSymbols_rm_CH', 'goog.i18n.NumberFormatSymbols_rn', 'goog.i18n.NumberFormatSymbols_rn_BI', 'goog.i18n.NumberFormatSymbols_ro_MD', 'goog.i18n.NumberFormatSymbols_rof', 'goog.i18n.NumberFormatSymbols_rof_TZ', 'goog.i18n.NumberFormatSymbols_ru_BY', 'goog.i18n.NumberFormatSymbols_ru_KG', 'goog.i18n.NumberFormatSymbols_ru_KZ', 'goog.i18n.NumberFormatSymbols_ru_MD', 'goog.i18n.NumberFormatSymbols_ru_UA', 'goog.i18n.NumberFormatSymbols_rw', 'goog.i18n.NumberFormatSymbols_rw_RW', 'goog.i18n.NumberFormatSymbols_rwk', 'goog.i18n.NumberFormatSymbols_rwk_TZ', 'goog.i18n.NumberFormatSymbols_sah', 'goog.i18n.NumberFormatSymbols_sah_RU', 'goog.i18n.NumberFormatSymbols_saq', 'goog.i18n.NumberFormatSymbols_saq_KE', 'goog.i18n.NumberFormatSymbols_sbp', 'goog.i18n.NumberFormatSymbols_sbp_TZ', 'goog.i18n.NumberFormatSymbols_se', 'goog.i18n.NumberFormatSymbols_se_FI', 'goog.i18n.NumberFormatSymbols_se_NO', 'goog.i18n.NumberFormatSymbols_se_SE', 'goog.i18n.NumberFormatSymbols_seh', 'goog.i18n.NumberFormatSymbols_seh_MZ', 'goog.i18n.NumberFormatSymbols_ses', 'goog.i18n.NumberFormatSymbols_ses_ML', 'goog.i18n.NumberFormatSymbols_sg', 'goog.i18n.NumberFormatSymbols_sg_CF', 'goog.i18n.NumberFormatSymbols_shi', 'goog.i18n.NumberFormatSymbols_shi_Latn', 'goog.i18n.NumberFormatSymbols_shi_Latn_MA', 'goog.i18n.NumberFormatSymbols_shi_Tfng', 'goog.i18n.NumberFormatSymbols_shi_Tfng_MA', 'goog.i18n.NumberFormatSymbols_smn', 'goog.i18n.NumberFormatSymbols_smn_FI', 'goog.i18n.NumberFormatSymbols_sn', 'goog.i18n.NumberFormatSymbols_sn_ZW', 'goog.i18n.NumberFormatSymbols_so', 'goog.i18n.NumberFormatSymbols_so_DJ', 'goog.i18n.NumberFormatSymbols_so_ET', 'goog.i18n.NumberFormatSymbols_so_KE', 'goog.i18n.NumberFormatSymbols_so_SO', 'goog.i18n.NumberFormatSymbols_sq_MK', 'goog.i18n.NumberFormatSymbols_sq_XK', 'goog.i18n.NumberFormatSymbols_sr_Cyrl', 'goog.i18n.NumberFormatSymbols_sr_Cyrl_BA', 'goog.i18n.NumberFormatSymbols_sr_Cyrl_ME', 'goog.i18n.NumberFormatSymbols_sr_Cyrl_XK', 'goog.i18n.NumberFormatSymbols_sr_Latn', 'goog.i18n.NumberFormatSymbols_sr_Latn_BA', 'goog.i18n.NumberFormatSymbols_sr_Latn_ME', 'goog.i18n.NumberFormatSymbols_sr_Latn_RS', 'goog.i18n.NumberFormatSymbols_sr_Latn_XK', 'goog.i18n.NumberFormatSymbols_ss', 'goog.i18n.NumberFormatSymbols_ss_SZ', 'goog.i18n.NumberFormatSymbols_ss_ZA', 'goog.i18n.NumberFormatSymbols_ssy', 'goog.i18n.NumberFormatSymbols_ssy_ER', 'goog.i18n.NumberFormatSymbols_sv_AX', 'goog.i18n.NumberFormatSymbols_sv_FI', 'goog.i18n.NumberFormatSymbols_sw_KE', 'goog.i18n.NumberFormatSymbols_sw_UG', 'goog.i18n.NumberFormatSymbols_swc', 'goog.i18n.NumberFormatSymbols_swc_CD', 'goog.i18n.NumberFormatSymbols_ta_LK', 'goog.i18n.NumberFormatSymbols_ta_MY', 'goog.i18n.NumberFormatSymbols_ta_SG', 'goog.i18n.NumberFormatSymbols_teo', 'goog.i18n.NumberFormatSymbols_teo_KE', 'goog.i18n.NumberFormatSymbols_teo_UG', 'goog.i18n.NumberFormatSymbols_ti', 'goog.i18n.NumberFormatSymbols_ti_ER', 'goog.i18n.NumberFormatSymbols_ti_ET', 'goog.i18n.NumberFormatSymbols_tn', 'goog.i18n.NumberFormatSymbols_tn_BW', 'goog.i18n.NumberFormatSymbols_tn_ZA', 'goog.i18n.NumberFormatSymbols_to', 'goog.i18n.NumberFormatSymbols_to_TO', 'goog.i18n.NumberFormatSymbols_tr_CY', 'goog.i18n.NumberFormatSymbols_ts', 'goog.i18n.NumberFormatSymbols_ts_ZA', 'goog.i18n.NumberFormatSymbols_twq', 'goog.i18n.NumberFormatSymbols_twq_NE', 'goog.i18n.NumberFormatSymbols_tzm', 'goog.i18n.NumberFormatSymbols_tzm_Latn', 'goog.i18n.NumberFormatSymbols_tzm_Latn_MA', 'goog.i18n.NumberFormatSymbols_ug', 'goog.i18n.NumberFormatSymbols_ug_Arab', 'goog.i18n.NumberFormatSymbols_ug_Arab_CN', 'goog.i18n.NumberFormatSymbols_ur_IN', 'goog.i18n.NumberFormatSymbols_uz_Arab', 'goog.i18n.NumberFormatSymbols_uz_Arab_AF', 'goog.i18n.NumberFormatSymbols_uz_Cyrl', 'goog.i18n.NumberFormatSymbols_uz_Cyrl_UZ', 'goog.i18n.NumberFormatSymbols_uz_Latn', 'goog.i18n.NumberFormatSymbols_vai', 'goog.i18n.NumberFormatSymbols_vai_Latn', 'goog.i18n.NumberFormatSymbols_vai_Latn_LR', 'goog.i18n.NumberFormatSymbols_vai_Vaii', 'goog.i18n.NumberFormatSymbols_vai_Vaii_LR', 'goog.i18n.NumberFormatSymbols_ve', 'goog.i18n.NumberFormatSymbols_ve_ZA', 'goog.i18n.NumberFormatSymbols_vo', 'goog.i18n.NumberFormatSymbols_vo_001', 'goog.i18n.NumberFormatSymbols_vun', 'goog.i18n.NumberFormatSymbols_vun_TZ', 'goog.i18n.NumberFormatSymbols_wae', 'goog.i18n.NumberFormatSymbols_wae_CH', 'goog.i18n.NumberFormatSymbols_xog', 'goog.i18n.NumberFormatSymbols_xog_UG', 'goog.i18n.NumberFormatSymbols_yav', 'goog.i18n.NumberFormatSymbols_yav_CM', 'goog.i18n.NumberFormatSymbols_yi', 'goog.i18n.NumberFormatSymbols_yi_001', 'goog.i18n.NumberFormatSymbols_yo', 'goog.i18n.NumberFormatSymbols_yo_BJ', 'goog.i18n.NumberFormatSymbols_yo_NG', 'goog.i18n.NumberFormatSymbols_zgh', 'goog.i18n.NumberFormatSymbols_zgh_MA', 'goog.i18n.NumberFormatSymbols_zh_Hans', 'goog.i18n.NumberFormatSymbols_zh_Hans_HK', 'goog.i18n.NumberFormatSymbols_zh_Hans_MO', 'goog.i18n.NumberFormatSymbols_zh_Hans_SG', 'goog.i18n.NumberFormatSymbols_zh_Hant', 'goog.i18n.NumberFormatSymbols_zh_Hant_HK', 'goog.i18n.NumberFormatSymbols_zh_Hant_MO', 'goog.i18n.NumberFormatSymbols_zh_Hant_TW'], ['goog.i18n.NumberFormatSymbols'], false); +goog.addDependency('i18n/ordinalrules.js', ['goog.i18n.ordinalRules'], [], false); +goog.addDependency('i18n/pluralrules.js', ['goog.i18n.pluralRules'], [], false); +goog.addDependency('i18n/pluralrules_test.js', ['goog.i18n.pluralRulesTest'], ['goog.i18n.pluralRules', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/timezone.js', ['goog.i18n.TimeZone'], ['goog.array', 'goog.date.DateLike', 'goog.string'], false); +goog.addDependency('i18n/timezone_test.js', ['goog.i18n.TimeZoneTest'], ['goog.i18n.TimeZone', 'goog.testing.jsunit'], false); +goog.addDependency('i18n/uchar.js', ['goog.i18n.uChar'], [], false); +goog.addDependency('i18n/uchar/localnamefetcher.js', ['goog.i18n.uChar.LocalNameFetcher'], ['goog.i18n.uChar', 'goog.i18n.uChar.NameFetcher', 'goog.log'], false); +goog.addDependency('i18n/uchar/localnamefetcher_test.js', ['goog.i18n.uChar.LocalNameFetcherTest'], ['goog.i18n.uChar.LocalNameFetcher', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('i18n/uchar/namefetcher.js', ['goog.i18n.uChar.NameFetcher'], [], false); +goog.addDependency('i18n/uchar/remotenamefetcher.js', ['goog.i18n.uChar.RemoteNameFetcher'], ['goog.Disposable', 'goog.Uri', 'goog.i18n.uChar', 'goog.i18n.uChar.NameFetcher', 'goog.log', 'goog.net.XhrIo', 'goog.structs.Map'], false); +goog.addDependency('i18n/uchar/remotenamefetcher_test.js', ['goog.i18n.uChar.RemoteNameFetcherTest'], ['goog.i18n.uChar.RemoteNameFetcher', 'goog.net.XhrIo', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.testing.recordFunction'], false); +goog.addDependency('i18n/uchar_test.js', ['goog.i18n.uCharTest'], ['goog.i18n.uChar', 'goog.testing.jsunit'], false); +goog.addDependency('iter/iter.js', ['goog.iter', 'goog.iter.Iterable', 'goog.iter.Iterator', 'goog.iter.StopIteration'], ['goog.array', 'goog.asserts', 'goog.functions', 'goog.math'], false); +goog.addDependency('iter/iter_test.js', ['goog.iterTest'], ['goog.iter', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.testing.jsunit'], false); +goog.addDependency('json/evaljsonprocessor.js', ['goog.json.EvalJsonProcessor'], ['goog.json', 'goog.json.Processor', 'goog.json.Serializer'], false); +goog.addDependency('json/hybrid.js', ['goog.json.hybrid'], ['goog.asserts', 'goog.json'], false); +goog.addDependency('json/hybrid_test.js', ['goog.json.hybridTest'], ['goog.json', 'goog.json.hybrid', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('json/hybridjsonprocessor.js', ['goog.json.HybridJsonProcessor'], ['goog.json.Processor', 'goog.json.hybrid'], false); +goog.addDependency('json/hybridjsonprocessor_test.js', ['goog.json.HybridJsonProcessorTest'], ['goog.json.HybridJsonProcessor', 'goog.json.hybrid', 'goog.testing.jsunit'], false); +goog.addDependency('json/json.js', ['goog.json', 'goog.json.Replacer', 'goog.json.Reviver', 'goog.json.Serializer'], [], false); +goog.addDependency('json/json_perf.js', ['goog.jsonPerf'], ['goog.dom', 'goog.json', 'goog.math', 'goog.string', 'goog.testing.PerformanceTable', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('json/json_test.js', ['goog.jsonTest'], ['goog.functions', 'goog.json', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('json/nativejsonprocessor.js', ['goog.json.NativeJsonProcessor'], ['goog.asserts', 'goog.json.Processor'], false); +goog.addDependency('json/processor.js', ['goog.json.Processor'], ['goog.string.Parser', 'goog.string.Stringifier'], false); +goog.addDependency('json/processor_test.js', ['goog.json.processorTest'], ['goog.json.EvalJsonProcessor', 'goog.json.NativeJsonProcessor', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('labs/dom/pagevisibilitymonitor.js', ['goog.labs.dom.PageVisibilityEvent', 'goog.labs.dom.PageVisibilityMonitor', 'goog.labs.dom.PageVisibilityState'], ['goog.dom', 'goog.dom.vendor', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.memoize'], false); +goog.addDependency('labs/dom/pagevisibilitymonitor_test.js', ['goog.labs.dom.PageVisibilityMonitorTest'], ['goog.events', 'goog.functions', 'goog.labs.dom.PageVisibilityMonitor', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('labs/events/nondisposableeventtarget.js', ['goog.labs.events.NonDisposableEventTarget'], ['goog.array', 'goog.asserts', 'goog.events.Event', 'goog.events.Listenable', 'goog.events.ListenerMap', 'goog.object'], false); +goog.addDependency('labs/events/nondisposableeventtarget_test.js', ['goog.labs.events.NonDisposableEventTargetTest'], ['goog.events.Listenable', 'goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType', 'goog.labs.events.NonDisposableEventTarget', 'goog.testing.jsunit'], false); +goog.addDependency('labs/events/nondisposableeventtarget_via_googevents_test.js', ['goog.labs.events.NonDisposableEventTargetGoogEventsTest'], ['goog.events', 'goog.events.eventTargetTester', 'goog.events.eventTargetTester.KeyType', 'goog.events.eventTargetTester.UnlistenReturnType', 'goog.labs.events.NonDisposableEventTarget', 'goog.testing', 'goog.testing.jsunit'], false); +goog.addDependency('labs/events/touch.js', ['goog.labs.events.touch', 'goog.labs.events.touch.TouchData'], ['goog.array', 'goog.asserts', 'goog.events.EventType', 'goog.string'], false); +goog.addDependency('labs/events/touch_test.js', ['goog.labs.events.touchTest'], ['goog.labs.events.touch', 'goog.testing.jsunit'], false); +goog.addDependency('labs/format/csv.js', ['goog.labs.format.csv', 'goog.labs.format.csv.ParseError', 'goog.labs.format.csv.Token'], ['goog.array', 'goog.asserts', 'goog.debug.Error', 'goog.object', 'goog.string', 'goog.string.newlines'], false); +goog.addDependency('labs/format/csv_test.js', ['goog.labs.format.csvTest'], ['goog.labs.format.csv', 'goog.labs.format.csv.ParseError', 'goog.object', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('labs/html/attribute_rewriter.js', ['goog.labs.html.AttributeRewriter', 'goog.labs.html.AttributeValue', 'goog.labs.html.attributeRewriterPresubmitWorkaround'], [], false); +goog.addDependency('labs/html/sanitizer.js', ['goog.labs.html.Sanitizer'], ['goog.asserts', 'goog.html.SafeUrl', 'goog.labs.html.attributeRewriterPresubmitWorkaround', 'goog.labs.html.scrubber', 'goog.object', 'goog.string'], false); +goog.addDependency('labs/html/sanitizer_test.js', ['goog.labs.html.SanitizerTest'], ['goog.html.SafeUrl', 'goog.labs.html.Sanitizer', 'goog.string', 'goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('labs/html/scrubber.js', ['goog.labs.html.scrubber'], ['goog.array', 'goog.dom.tags', 'goog.labs.html.attributeRewriterPresubmitWorkaround', 'goog.string'], false); +goog.addDependency('labs/html/scrubber_test.js', ['goog.html.ScrubberTest'], ['goog.labs.html.scrubber', 'goog.object', 'goog.string', 'goog.testing.jsunit'], false); +goog.addDependency('labs/i18n/listformat.js', ['goog.labs.i18n.GenderInfo', 'goog.labs.i18n.GenderInfo.Gender', 'goog.labs.i18n.ListFormat'], ['goog.asserts', 'goog.labs.i18n.ListFormatSymbols'], false); +goog.addDependency('labs/i18n/listformat_test.js', ['goog.labs.i18n.ListFormatTest'], ['goog.labs.i18n.GenderInfo', 'goog.labs.i18n.ListFormat', 'goog.labs.i18n.ListFormatSymbols', 'goog.labs.i18n.ListFormatSymbols_el', 'goog.labs.i18n.ListFormatSymbols_en', 'goog.labs.i18n.ListFormatSymbols_fr', 'goog.labs.i18n.ListFormatSymbols_ml', 'goog.labs.i18n.ListFormatSymbols_zu', 'goog.testing.jsunit'], false); +goog.addDependency('labs/i18n/listsymbols.js', ['goog.labs.i18n.ListFormatSymbols', 'goog.labs.i18n.ListFormatSymbols_af', 'goog.labs.i18n.ListFormatSymbols_am', 'goog.labs.i18n.ListFormatSymbols_ar', 'goog.labs.i18n.ListFormatSymbols_az', 'goog.labs.i18n.ListFormatSymbols_bg', 'goog.labs.i18n.ListFormatSymbols_bn', 'goog.labs.i18n.ListFormatSymbols_br', 'goog.labs.i18n.ListFormatSymbols_ca', 'goog.labs.i18n.ListFormatSymbols_chr', 'goog.labs.i18n.ListFormatSymbols_cs', 'goog.labs.i18n.ListFormatSymbols_cy', 'goog.labs.i18n.ListFormatSymbols_da', 'goog.labs.i18n.ListFormatSymbols_de', 'goog.labs.i18n.ListFormatSymbols_de_AT', 'goog.labs.i18n.ListFormatSymbols_de_CH', 'goog.labs.i18n.ListFormatSymbols_el', 'goog.labs.i18n.ListFormatSymbols_en', 'goog.labs.i18n.ListFormatSymbols_en_AU', 'goog.labs.i18n.ListFormatSymbols_en_GB', 'goog.labs.i18n.ListFormatSymbols_en_IE', 'goog.labs.i18n.ListFormatSymbols_en_IN', 'goog.labs.i18n.ListFormatSymbols_en_SG', 'goog.labs.i18n.ListFormatSymbols_en_US', 'goog.labs.i18n.ListFormatSymbols_en_ZA', 'goog.labs.i18n.ListFormatSymbols_es', 'goog.labs.i18n.ListFormatSymbols_es_419', 'goog.labs.i18n.ListFormatSymbols_es_ES', 'goog.labs.i18n.ListFormatSymbols_et', 'goog.labs.i18n.ListFormatSymbols_eu', 'goog.labs.i18n.ListFormatSymbols_fa', 'goog.labs.i18n.ListFormatSymbols_fi', 'goog.labs.i18n.ListFormatSymbols_fil', 'goog.labs.i18n.ListFormatSymbols_fr', 'goog.labs.i18n.ListFormatSymbols_fr_CA', 'goog.labs.i18n.ListFormatSymbols_ga', 'goog.labs.i18n.ListFormatSymbols_gl', 'goog.labs.i18n.ListFormatSymbols_gsw', 'goog.labs.i18n.ListFormatSymbols_gu', 'goog.labs.i18n.ListFormatSymbols_haw', 'goog.labs.i18n.ListFormatSymbols_he', 'goog.labs.i18n.ListFormatSymbols_hi', 'goog.labs.i18n.ListFormatSymbols_hr', 'goog.labs.i18n.ListFormatSymbols_hu', 'goog.labs.i18n.ListFormatSymbols_hy', 'goog.labs.i18n.ListFormatSymbols_id', 'goog.labs.i18n.ListFormatSymbols_in', 'goog.labs.i18n.ListFormatSymbols_is', 'goog.labs.i18n.ListFormatSymbols_it', 'goog.labs.i18n.ListFormatSymbols_iw', 'goog.labs.i18n.ListFormatSymbols_ja', 'goog.labs.i18n.ListFormatSymbols_ka', 'goog.labs.i18n.ListFormatSymbols_kk', 'goog.labs.i18n.ListFormatSymbols_km', 'goog.labs.i18n.ListFormatSymbols_kn', 'goog.labs.i18n.ListFormatSymbols_ko', 'goog.labs.i18n.ListFormatSymbols_ky', 'goog.labs.i18n.ListFormatSymbols_ln', 'goog.labs.i18n.ListFormatSymbols_lo', 'goog.labs.i18n.ListFormatSymbols_lt', 'goog.labs.i18n.ListFormatSymbols_lv', 'goog.labs.i18n.ListFormatSymbols_mk', 'goog.labs.i18n.ListFormatSymbols_ml', 'goog.labs.i18n.ListFormatSymbols_mn', 'goog.labs.i18n.ListFormatSymbols_mo', 'goog.labs.i18n.ListFormatSymbols_mr', 'goog.labs.i18n.ListFormatSymbols_ms', 'goog.labs.i18n.ListFormatSymbols_mt', 'goog.labs.i18n.ListFormatSymbols_my', 'goog.labs.i18n.ListFormatSymbols_nb', 'goog.labs.i18n.ListFormatSymbols_ne', 'goog.labs.i18n.ListFormatSymbols_nl', 'goog.labs.i18n.ListFormatSymbols_no', 'goog.labs.i18n.ListFormatSymbols_no_NO', 'goog.labs.i18n.ListFormatSymbols_or', 'goog.labs.i18n.ListFormatSymbols_pa', 'goog.labs.i18n.ListFormatSymbols_pl', 'goog.labs.i18n.ListFormatSymbols_pt', 'goog.labs.i18n.ListFormatSymbols_pt_BR', 'goog.labs.i18n.ListFormatSymbols_pt_PT', 'goog.labs.i18n.ListFormatSymbols_ro', 'goog.labs.i18n.ListFormatSymbols_ru', 'goog.labs.i18n.ListFormatSymbols_sh', 'goog.labs.i18n.ListFormatSymbols_si', 'goog.labs.i18n.ListFormatSymbols_sk', 'goog.labs.i18n.ListFormatSymbols_sl', 'goog.labs.i18n.ListFormatSymbols_sq', 'goog.labs.i18n.ListFormatSymbols_sr', 'goog.labs.i18n.ListFormatSymbols_sv', 'goog.labs.i18n.ListFormatSymbols_sw', 'goog.labs.i18n.ListFormatSymbols_ta', 'goog.labs.i18n.ListFormatSymbols_te', 'goog.labs.i18n.ListFormatSymbols_th', 'goog.labs.i18n.ListFormatSymbols_tl', 'goog.labs.i18n.ListFormatSymbols_tr', 'goog.labs.i18n.ListFormatSymbols_uk', 'goog.labs.i18n.ListFormatSymbols_ur', 'goog.labs.i18n.ListFormatSymbols_uz', 'goog.labs.i18n.ListFormatSymbols_vi', 'goog.labs.i18n.ListFormatSymbols_zh', 'goog.labs.i18n.ListFormatSymbols_zh_CN', 'goog.labs.i18n.ListFormatSymbols_zh_HK', 'goog.labs.i18n.ListFormatSymbols_zh_TW', 'goog.labs.i18n.ListFormatSymbols_zu'], [], false); +goog.addDependency('labs/i18n/listsymbolsext.js', ['goog.labs.i18n.ListFormatSymbolsExt', 'goog.labs.i18n.ListFormatSymbols_af_NA', 'goog.labs.i18n.ListFormatSymbols_af_ZA', 'goog.labs.i18n.ListFormatSymbols_agq', 'goog.labs.i18n.ListFormatSymbols_agq_CM', 'goog.labs.i18n.ListFormatSymbols_ak', 'goog.labs.i18n.ListFormatSymbols_ak_GH', 'goog.labs.i18n.ListFormatSymbols_am_ET', 'goog.labs.i18n.ListFormatSymbols_ar_001', 'goog.labs.i18n.ListFormatSymbols_ar_AE', 'goog.labs.i18n.ListFormatSymbols_ar_BH', 'goog.labs.i18n.ListFormatSymbols_ar_DJ', 'goog.labs.i18n.ListFormatSymbols_ar_DZ', 'goog.labs.i18n.ListFormatSymbols_ar_EG', 'goog.labs.i18n.ListFormatSymbols_ar_EH', 'goog.labs.i18n.ListFormatSymbols_ar_ER', 'goog.labs.i18n.ListFormatSymbols_ar_IL', 'goog.labs.i18n.ListFormatSymbols_ar_IQ', 'goog.labs.i18n.ListFormatSymbols_ar_JO', 'goog.labs.i18n.ListFormatSymbols_ar_KM', 'goog.labs.i18n.ListFormatSymbols_ar_KW', 'goog.labs.i18n.ListFormatSymbols_ar_LB', 'goog.labs.i18n.ListFormatSymbols_ar_LY', 'goog.labs.i18n.ListFormatSymbols_ar_MA', 'goog.labs.i18n.ListFormatSymbols_ar_MR', 'goog.labs.i18n.ListFormatSymbols_ar_OM', 'goog.labs.i18n.ListFormatSymbols_ar_PS', 'goog.labs.i18n.ListFormatSymbols_ar_QA', 'goog.labs.i18n.ListFormatSymbols_ar_SA', 'goog.labs.i18n.ListFormatSymbols_ar_SD', 'goog.labs.i18n.ListFormatSymbols_ar_SO', 'goog.labs.i18n.ListFormatSymbols_ar_SS', 'goog.labs.i18n.ListFormatSymbols_ar_SY', 'goog.labs.i18n.ListFormatSymbols_ar_TD', 'goog.labs.i18n.ListFormatSymbols_ar_TN', 'goog.labs.i18n.ListFormatSymbols_ar_YE', 'goog.labs.i18n.ListFormatSymbols_as', 'goog.labs.i18n.ListFormatSymbols_as_IN', 'goog.labs.i18n.ListFormatSymbols_asa', 'goog.labs.i18n.ListFormatSymbols_asa_TZ', 'goog.labs.i18n.ListFormatSymbols_az_Cyrl', 'goog.labs.i18n.ListFormatSymbols_az_Cyrl_AZ', 'goog.labs.i18n.ListFormatSymbols_az_Latn', 'goog.labs.i18n.ListFormatSymbols_az_Latn_AZ', 'goog.labs.i18n.ListFormatSymbols_bas', 'goog.labs.i18n.ListFormatSymbols_bas_CM', 'goog.labs.i18n.ListFormatSymbols_be', 'goog.labs.i18n.ListFormatSymbols_be_BY', 'goog.labs.i18n.ListFormatSymbols_bem', 'goog.labs.i18n.ListFormatSymbols_bem_ZM', 'goog.labs.i18n.ListFormatSymbols_bez', 'goog.labs.i18n.ListFormatSymbols_bez_TZ', 'goog.labs.i18n.ListFormatSymbols_bg_BG', 'goog.labs.i18n.ListFormatSymbols_bm', 'goog.labs.i18n.ListFormatSymbols_bm_Latn', 'goog.labs.i18n.ListFormatSymbols_bm_Latn_ML', 'goog.labs.i18n.ListFormatSymbols_bn_BD', 'goog.labs.i18n.ListFormatSymbols_bn_IN', 'goog.labs.i18n.ListFormatSymbols_bo', 'goog.labs.i18n.ListFormatSymbols_bo_CN', 'goog.labs.i18n.ListFormatSymbols_bo_IN', 'goog.labs.i18n.ListFormatSymbols_br_FR', 'goog.labs.i18n.ListFormatSymbols_brx', 'goog.labs.i18n.ListFormatSymbols_brx_IN', 'goog.labs.i18n.ListFormatSymbols_bs', 'goog.labs.i18n.ListFormatSymbols_bs_Cyrl', 'goog.labs.i18n.ListFormatSymbols_bs_Cyrl_BA', 'goog.labs.i18n.ListFormatSymbols_bs_Latn', 'goog.labs.i18n.ListFormatSymbols_bs_Latn_BA', 'goog.labs.i18n.ListFormatSymbols_ca_AD', 'goog.labs.i18n.ListFormatSymbols_ca_ES', 'goog.labs.i18n.ListFormatSymbols_ca_FR', 'goog.labs.i18n.ListFormatSymbols_ca_IT', 'goog.labs.i18n.ListFormatSymbols_cgg', 'goog.labs.i18n.ListFormatSymbols_cgg_UG', 'goog.labs.i18n.ListFormatSymbols_chr_US', 'goog.labs.i18n.ListFormatSymbols_cs_CZ', 'goog.labs.i18n.ListFormatSymbols_cy_GB', 'goog.labs.i18n.ListFormatSymbols_da_DK', 'goog.labs.i18n.ListFormatSymbols_da_GL', 'goog.labs.i18n.ListFormatSymbols_dav', 'goog.labs.i18n.ListFormatSymbols_dav_KE', 'goog.labs.i18n.ListFormatSymbols_de_BE', 'goog.labs.i18n.ListFormatSymbols_de_DE', 'goog.labs.i18n.ListFormatSymbols_de_LI', 'goog.labs.i18n.ListFormatSymbols_de_LU', 'goog.labs.i18n.ListFormatSymbols_dje', 'goog.labs.i18n.ListFormatSymbols_dje_NE', 'goog.labs.i18n.ListFormatSymbols_dsb', 'goog.labs.i18n.ListFormatSymbols_dsb_DE', 'goog.labs.i18n.ListFormatSymbols_dua', 'goog.labs.i18n.ListFormatSymbols_dua_CM', 'goog.labs.i18n.ListFormatSymbols_dyo', 'goog.labs.i18n.ListFormatSymbols_dyo_SN', 'goog.labs.i18n.ListFormatSymbols_dz', 'goog.labs.i18n.ListFormatSymbols_dz_BT', 'goog.labs.i18n.ListFormatSymbols_ebu', 'goog.labs.i18n.ListFormatSymbols_ebu_KE', 'goog.labs.i18n.ListFormatSymbols_ee', 'goog.labs.i18n.ListFormatSymbols_ee_GH', 'goog.labs.i18n.ListFormatSymbols_ee_TG', 'goog.labs.i18n.ListFormatSymbols_el_CY', 'goog.labs.i18n.ListFormatSymbols_el_GR', 'goog.labs.i18n.ListFormatSymbols_en_001', 'goog.labs.i18n.ListFormatSymbols_en_150', 'goog.labs.i18n.ListFormatSymbols_en_AG', 'goog.labs.i18n.ListFormatSymbols_en_AI', 'goog.labs.i18n.ListFormatSymbols_en_AS', 'goog.labs.i18n.ListFormatSymbols_en_BB', 'goog.labs.i18n.ListFormatSymbols_en_BE', 'goog.labs.i18n.ListFormatSymbols_en_BM', 'goog.labs.i18n.ListFormatSymbols_en_BS', 'goog.labs.i18n.ListFormatSymbols_en_BW', 'goog.labs.i18n.ListFormatSymbols_en_BZ', 'goog.labs.i18n.ListFormatSymbols_en_CA', 'goog.labs.i18n.ListFormatSymbols_en_CC', 'goog.labs.i18n.ListFormatSymbols_en_CK', 'goog.labs.i18n.ListFormatSymbols_en_CM', 'goog.labs.i18n.ListFormatSymbols_en_CX', 'goog.labs.i18n.ListFormatSymbols_en_DG', 'goog.labs.i18n.ListFormatSymbols_en_DM', 'goog.labs.i18n.ListFormatSymbols_en_ER', 'goog.labs.i18n.ListFormatSymbols_en_FJ', 'goog.labs.i18n.ListFormatSymbols_en_FK', 'goog.labs.i18n.ListFormatSymbols_en_FM', 'goog.labs.i18n.ListFormatSymbols_en_GD', 'goog.labs.i18n.ListFormatSymbols_en_GG', 'goog.labs.i18n.ListFormatSymbols_en_GH', 'goog.labs.i18n.ListFormatSymbols_en_GI', 'goog.labs.i18n.ListFormatSymbols_en_GM', 'goog.labs.i18n.ListFormatSymbols_en_GU', 'goog.labs.i18n.ListFormatSymbols_en_GY', 'goog.labs.i18n.ListFormatSymbols_en_HK', 'goog.labs.i18n.ListFormatSymbols_en_IM', 'goog.labs.i18n.ListFormatSymbols_en_IO', 'goog.labs.i18n.ListFormatSymbols_en_JE', 'goog.labs.i18n.ListFormatSymbols_en_JM', 'goog.labs.i18n.ListFormatSymbols_en_KE', 'goog.labs.i18n.ListFormatSymbols_en_KI', 'goog.labs.i18n.ListFormatSymbols_en_KN', 'goog.labs.i18n.ListFormatSymbols_en_KY', 'goog.labs.i18n.ListFormatSymbols_en_LC', 'goog.labs.i18n.ListFormatSymbols_en_LR', 'goog.labs.i18n.ListFormatSymbols_en_LS', 'goog.labs.i18n.ListFormatSymbols_en_MG', 'goog.labs.i18n.ListFormatSymbols_en_MH', 'goog.labs.i18n.ListFormatSymbols_en_MO', 'goog.labs.i18n.ListFormatSymbols_en_MP', 'goog.labs.i18n.ListFormatSymbols_en_MS', 'goog.labs.i18n.ListFormatSymbols_en_MT', 'goog.labs.i18n.ListFormatSymbols_en_MU', 'goog.labs.i18n.ListFormatSymbols_en_MW', 'goog.labs.i18n.ListFormatSymbols_en_MY', 'goog.labs.i18n.ListFormatSymbols_en_NA', 'goog.labs.i18n.ListFormatSymbols_en_NF', 'goog.labs.i18n.ListFormatSymbols_en_NG', 'goog.labs.i18n.ListFormatSymbols_en_NR', 'goog.labs.i18n.ListFormatSymbols_en_NU', 'goog.labs.i18n.ListFormatSymbols_en_NZ', 'goog.labs.i18n.ListFormatSymbols_en_PG', 'goog.labs.i18n.ListFormatSymbols_en_PH', 'goog.labs.i18n.ListFormatSymbols_en_PK', 'goog.labs.i18n.ListFormatSymbols_en_PN', 'goog.labs.i18n.ListFormatSymbols_en_PR', 'goog.labs.i18n.ListFormatSymbols_en_PW', 'goog.labs.i18n.ListFormatSymbols_en_RW', 'goog.labs.i18n.ListFormatSymbols_en_SB', 'goog.labs.i18n.ListFormatSymbols_en_SC', 'goog.labs.i18n.ListFormatSymbols_en_SD', 'goog.labs.i18n.ListFormatSymbols_en_SH', 'goog.labs.i18n.ListFormatSymbols_en_SL', 'goog.labs.i18n.ListFormatSymbols_en_SS', 'goog.labs.i18n.ListFormatSymbols_en_SX', 'goog.labs.i18n.ListFormatSymbols_en_SZ', 'goog.labs.i18n.ListFormatSymbols_en_TC', 'goog.labs.i18n.ListFormatSymbols_en_TK', 'goog.labs.i18n.ListFormatSymbols_en_TO', 'goog.labs.i18n.ListFormatSymbols_en_TT', 'goog.labs.i18n.ListFormatSymbols_en_TV', 'goog.labs.i18n.ListFormatSymbols_en_TZ', 'goog.labs.i18n.ListFormatSymbols_en_UG', 'goog.labs.i18n.ListFormatSymbols_en_UM', 'goog.labs.i18n.ListFormatSymbols_en_US_POSIX', 'goog.labs.i18n.ListFormatSymbols_en_VC', 'goog.labs.i18n.ListFormatSymbols_en_VG', 'goog.labs.i18n.ListFormatSymbols_en_VI', 'goog.labs.i18n.ListFormatSymbols_en_VU', 'goog.labs.i18n.ListFormatSymbols_en_WS', 'goog.labs.i18n.ListFormatSymbols_en_ZM', 'goog.labs.i18n.ListFormatSymbols_en_ZW', 'goog.labs.i18n.ListFormatSymbols_eo', 'goog.labs.i18n.ListFormatSymbols_es_AR', 'goog.labs.i18n.ListFormatSymbols_es_BO', 'goog.labs.i18n.ListFormatSymbols_es_CL', 'goog.labs.i18n.ListFormatSymbols_es_CO', 'goog.labs.i18n.ListFormatSymbols_es_CR', 'goog.labs.i18n.ListFormatSymbols_es_CU', 'goog.labs.i18n.ListFormatSymbols_es_DO', 'goog.labs.i18n.ListFormatSymbols_es_EA', 'goog.labs.i18n.ListFormatSymbols_es_EC', 'goog.labs.i18n.ListFormatSymbols_es_GQ', 'goog.labs.i18n.ListFormatSymbols_es_GT', 'goog.labs.i18n.ListFormatSymbols_es_HN', 'goog.labs.i18n.ListFormatSymbols_es_IC', 'goog.labs.i18n.ListFormatSymbols_es_MX', 'goog.labs.i18n.ListFormatSymbols_es_NI', 'goog.labs.i18n.ListFormatSymbols_es_PA', 'goog.labs.i18n.ListFormatSymbols_es_PE', 'goog.labs.i18n.ListFormatSymbols_es_PH', 'goog.labs.i18n.ListFormatSymbols_es_PR', 'goog.labs.i18n.ListFormatSymbols_es_PY', 'goog.labs.i18n.ListFormatSymbols_es_SV', 'goog.labs.i18n.ListFormatSymbols_es_US', 'goog.labs.i18n.ListFormatSymbols_es_UY', 'goog.labs.i18n.ListFormatSymbols_es_VE', 'goog.labs.i18n.ListFormatSymbols_et_EE', 'goog.labs.i18n.ListFormatSymbols_eu_ES', 'goog.labs.i18n.ListFormatSymbols_ewo', 'goog.labs.i18n.ListFormatSymbols_ewo_CM', 'goog.labs.i18n.ListFormatSymbols_fa_AF', 'goog.labs.i18n.ListFormatSymbols_fa_IR', 'goog.labs.i18n.ListFormatSymbols_ff', 'goog.labs.i18n.ListFormatSymbols_ff_CM', 'goog.labs.i18n.ListFormatSymbols_ff_GN', 'goog.labs.i18n.ListFormatSymbols_ff_MR', 'goog.labs.i18n.ListFormatSymbols_ff_SN', 'goog.labs.i18n.ListFormatSymbols_fi_FI', 'goog.labs.i18n.ListFormatSymbols_fil_PH', 'goog.labs.i18n.ListFormatSymbols_fo', 'goog.labs.i18n.ListFormatSymbols_fo_FO', 'goog.labs.i18n.ListFormatSymbols_fr_BE', 'goog.labs.i18n.ListFormatSymbols_fr_BF', 'goog.labs.i18n.ListFormatSymbols_fr_BI', 'goog.labs.i18n.ListFormatSymbols_fr_BJ', 'goog.labs.i18n.ListFormatSymbols_fr_BL', 'goog.labs.i18n.ListFormatSymbols_fr_CD', 'goog.labs.i18n.ListFormatSymbols_fr_CF', 'goog.labs.i18n.ListFormatSymbols_fr_CG', 'goog.labs.i18n.ListFormatSymbols_fr_CH', 'goog.labs.i18n.ListFormatSymbols_fr_CI', 'goog.labs.i18n.ListFormatSymbols_fr_CM', 'goog.labs.i18n.ListFormatSymbols_fr_DJ', 'goog.labs.i18n.ListFormatSymbols_fr_DZ', 'goog.labs.i18n.ListFormatSymbols_fr_FR', 'goog.labs.i18n.ListFormatSymbols_fr_GA', 'goog.labs.i18n.ListFormatSymbols_fr_GF', 'goog.labs.i18n.ListFormatSymbols_fr_GN', 'goog.labs.i18n.ListFormatSymbols_fr_GP', 'goog.labs.i18n.ListFormatSymbols_fr_GQ', 'goog.labs.i18n.ListFormatSymbols_fr_HT', 'goog.labs.i18n.ListFormatSymbols_fr_KM', 'goog.labs.i18n.ListFormatSymbols_fr_LU', 'goog.labs.i18n.ListFormatSymbols_fr_MA', 'goog.labs.i18n.ListFormatSymbols_fr_MC', 'goog.labs.i18n.ListFormatSymbols_fr_MF', 'goog.labs.i18n.ListFormatSymbols_fr_MG', 'goog.labs.i18n.ListFormatSymbols_fr_ML', 'goog.labs.i18n.ListFormatSymbols_fr_MQ', 'goog.labs.i18n.ListFormatSymbols_fr_MR', 'goog.labs.i18n.ListFormatSymbols_fr_MU', 'goog.labs.i18n.ListFormatSymbols_fr_NC', 'goog.labs.i18n.ListFormatSymbols_fr_NE', 'goog.labs.i18n.ListFormatSymbols_fr_PF', 'goog.labs.i18n.ListFormatSymbols_fr_PM', 'goog.labs.i18n.ListFormatSymbols_fr_RE', 'goog.labs.i18n.ListFormatSymbols_fr_RW', 'goog.labs.i18n.ListFormatSymbols_fr_SC', 'goog.labs.i18n.ListFormatSymbols_fr_SN', 'goog.labs.i18n.ListFormatSymbols_fr_SY', 'goog.labs.i18n.ListFormatSymbols_fr_TD', 'goog.labs.i18n.ListFormatSymbols_fr_TG', 'goog.labs.i18n.ListFormatSymbols_fr_TN', 'goog.labs.i18n.ListFormatSymbols_fr_VU', 'goog.labs.i18n.ListFormatSymbols_fr_WF', 'goog.labs.i18n.ListFormatSymbols_fr_YT', 'goog.labs.i18n.ListFormatSymbols_fur', 'goog.labs.i18n.ListFormatSymbols_fur_IT', 'goog.labs.i18n.ListFormatSymbols_fy', 'goog.labs.i18n.ListFormatSymbols_fy_NL', 'goog.labs.i18n.ListFormatSymbols_ga_IE', 'goog.labs.i18n.ListFormatSymbols_gd', 'goog.labs.i18n.ListFormatSymbols_gd_GB', 'goog.labs.i18n.ListFormatSymbols_gl_ES', 'goog.labs.i18n.ListFormatSymbols_gsw_CH', 'goog.labs.i18n.ListFormatSymbols_gsw_FR', 'goog.labs.i18n.ListFormatSymbols_gsw_LI', 'goog.labs.i18n.ListFormatSymbols_gu_IN', 'goog.labs.i18n.ListFormatSymbols_guz', 'goog.labs.i18n.ListFormatSymbols_guz_KE', 'goog.labs.i18n.ListFormatSymbols_gv', 'goog.labs.i18n.ListFormatSymbols_gv_IM', 'goog.labs.i18n.ListFormatSymbols_ha', 'goog.labs.i18n.ListFormatSymbols_ha_Latn', 'goog.labs.i18n.ListFormatSymbols_ha_Latn_GH', 'goog.labs.i18n.ListFormatSymbols_ha_Latn_NE', 'goog.labs.i18n.ListFormatSymbols_ha_Latn_NG', 'goog.labs.i18n.ListFormatSymbols_haw_US', 'goog.labs.i18n.ListFormatSymbols_he_IL', 'goog.labs.i18n.ListFormatSymbols_hi_IN', 'goog.labs.i18n.ListFormatSymbols_hr_BA', 'goog.labs.i18n.ListFormatSymbols_hr_HR', 'goog.labs.i18n.ListFormatSymbols_hsb', 'goog.labs.i18n.ListFormatSymbols_hsb_DE', 'goog.labs.i18n.ListFormatSymbols_hu_HU', 'goog.labs.i18n.ListFormatSymbols_hy_AM', 'goog.labs.i18n.ListFormatSymbols_id_ID', 'goog.labs.i18n.ListFormatSymbols_ig', 'goog.labs.i18n.ListFormatSymbols_ig_NG', 'goog.labs.i18n.ListFormatSymbols_ii', 'goog.labs.i18n.ListFormatSymbols_ii_CN', 'goog.labs.i18n.ListFormatSymbols_is_IS', 'goog.labs.i18n.ListFormatSymbols_it_CH', 'goog.labs.i18n.ListFormatSymbols_it_IT', 'goog.labs.i18n.ListFormatSymbols_it_SM', 'goog.labs.i18n.ListFormatSymbols_ja_JP', 'goog.labs.i18n.ListFormatSymbols_jgo', 'goog.labs.i18n.ListFormatSymbols_jgo_CM', 'goog.labs.i18n.ListFormatSymbols_jmc', 'goog.labs.i18n.ListFormatSymbols_jmc_TZ', 'goog.labs.i18n.ListFormatSymbols_ka_GE', 'goog.labs.i18n.ListFormatSymbols_kab', 'goog.labs.i18n.ListFormatSymbols_kab_DZ', 'goog.labs.i18n.ListFormatSymbols_kam', 'goog.labs.i18n.ListFormatSymbols_kam_KE', 'goog.labs.i18n.ListFormatSymbols_kde', 'goog.labs.i18n.ListFormatSymbols_kde_TZ', 'goog.labs.i18n.ListFormatSymbols_kea', 'goog.labs.i18n.ListFormatSymbols_kea_CV', 'goog.labs.i18n.ListFormatSymbols_khq', 'goog.labs.i18n.ListFormatSymbols_khq_ML', 'goog.labs.i18n.ListFormatSymbols_ki', 'goog.labs.i18n.ListFormatSymbols_ki_KE', 'goog.labs.i18n.ListFormatSymbols_kk_Cyrl', 'goog.labs.i18n.ListFormatSymbols_kk_Cyrl_KZ', 'goog.labs.i18n.ListFormatSymbols_kkj', 'goog.labs.i18n.ListFormatSymbols_kkj_CM', 'goog.labs.i18n.ListFormatSymbols_kl', 'goog.labs.i18n.ListFormatSymbols_kl_GL', 'goog.labs.i18n.ListFormatSymbols_kln', 'goog.labs.i18n.ListFormatSymbols_kln_KE', 'goog.labs.i18n.ListFormatSymbols_km_KH', 'goog.labs.i18n.ListFormatSymbols_kn_IN', 'goog.labs.i18n.ListFormatSymbols_ko_KP', 'goog.labs.i18n.ListFormatSymbols_ko_KR', 'goog.labs.i18n.ListFormatSymbols_kok', 'goog.labs.i18n.ListFormatSymbols_kok_IN', 'goog.labs.i18n.ListFormatSymbols_ks', 'goog.labs.i18n.ListFormatSymbols_ks_Arab', 'goog.labs.i18n.ListFormatSymbols_ks_Arab_IN', 'goog.labs.i18n.ListFormatSymbols_ksb', 'goog.labs.i18n.ListFormatSymbols_ksb_TZ', 'goog.labs.i18n.ListFormatSymbols_ksf', 'goog.labs.i18n.ListFormatSymbols_ksf_CM', 'goog.labs.i18n.ListFormatSymbols_ksh', 'goog.labs.i18n.ListFormatSymbols_ksh_DE', 'goog.labs.i18n.ListFormatSymbols_kw', 'goog.labs.i18n.ListFormatSymbols_kw_GB', 'goog.labs.i18n.ListFormatSymbols_ky_Cyrl', 'goog.labs.i18n.ListFormatSymbols_ky_Cyrl_KG', 'goog.labs.i18n.ListFormatSymbols_lag', 'goog.labs.i18n.ListFormatSymbols_lag_TZ', 'goog.labs.i18n.ListFormatSymbols_lb', 'goog.labs.i18n.ListFormatSymbols_lb_LU', 'goog.labs.i18n.ListFormatSymbols_lg', 'goog.labs.i18n.ListFormatSymbols_lg_UG', 'goog.labs.i18n.ListFormatSymbols_lkt', 'goog.labs.i18n.ListFormatSymbols_lkt_US', 'goog.labs.i18n.ListFormatSymbols_ln_AO', 'goog.labs.i18n.ListFormatSymbols_ln_CD', 'goog.labs.i18n.ListFormatSymbols_ln_CF', 'goog.labs.i18n.ListFormatSymbols_ln_CG', 'goog.labs.i18n.ListFormatSymbols_lo_LA', 'goog.labs.i18n.ListFormatSymbols_lt_LT', 'goog.labs.i18n.ListFormatSymbols_lu', 'goog.labs.i18n.ListFormatSymbols_lu_CD', 'goog.labs.i18n.ListFormatSymbols_luo', 'goog.labs.i18n.ListFormatSymbols_luo_KE', 'goog.labs.i18n.ListFormatSymbols_luy', 'goog.labs.i18n.ListFormatSymbols_luy_KE', 'goog.labs.i18n.ListFormatSymbols_lv_LV', 'goog.labs.i18n.ListFormatSymbols_mas', 'goog.labs.i18n.ListFormatSymbols_mas_KE', 'goog.labs.i18n.ListFormatSymbols_mas_TZ', 'goog.labs.i18n.ListFormatSymbols_mer', 'goog.labs.i18n.ListFormatSymbols_mer_KE', 'goog.labs.i18n.ListFormatSymbols_mfe', 'goog.labs.i18n.ListFormatSymbols_mfe_MU', 'goog.labs.i18n.ListFormatSymbols_mg', 'goog.labs.i18n.ListFormatSymbols_mg_MG', 'goog.labs.i18n.ListFormatSymbols_mgh', 'goog.labs.i18n.ListFormatSymbols_mgh_MZ', 'goog.labs.i18n.ListFormatSymbols_mgo', 'goog.labs.i18n.ListFormatSymbols_mgo_CM', 'goog.labs.i18n.ListFormatSymbols_mk_MK', 'goog.labs.i18n.ListFormatSymbols_ml_IN', 'goog.labs.i18n.ListFormatSymbols_mn_Cyrl', 'goog.labs.i18n.ListFormatSymbols_mn_Cyrl_MN', 'goog.labs.i18n.ListFormatSymbols_mr_IN', 'goog.labs.i18n.ListFormatSymbols_ms_Latn', 'goog.labs.i18n.ListFormatSymbols_ms_Latn_BN', 'goog.labs.i18n.ListFormatSymbols_ms_Latn_MY', 'goog.labs.i18n.ListFormatSymbols_ms_Latn_SG', 'goog.labs.i18n.ListFormatSymbols_mt_MT', 'goog.labs.i18n.ListFormatSymbols_mua', 'goog.labs.i18n.ListFormatSymbols_mua_CM', 'goog.labs.i18n.ListFormatSymbols_my_MM', 'goog.labs.i18n.ListFormatSymbols_naq', 'goog.labs.i18n.ListFormatSymbols_naq_NA', 'goog.labs.i18n.ListFormatSymbols_nb_NO', 'goog.labs.i18n.ListFormatSymbols_nb_SJ', 'goog.labs.i18n.ListFormatSymbols_nd', 'goog.labs.i18n.ListFormatSymbols_nd_ZW', 'goog.labs.i18n.ListFormatSymbols_ne_IN', 'goog.labs.i18n.ListFormatSymbols_ne_NP', 'goog.labs.i18n.ListFormatSymbols_nl_AW', 'goog.labs.i18n.ListFormatSymbols_nl_BE', 'goog.labs.i18n.ListFormatSymbols_nl_BQ', 'goog.labs.i18n.ListFormatSymbols_nl_CW', 'goog.labs.i18n.ListFormatSymbols_nl_NL', 'goog.labs.i18n.ListFormatSymbols_nl_SR', 'goog.labs.i18n.ListFormatSymbols_nl_SX', 'goog.labs.i18n.ListFormatSymbols_nmg', 'goog.labs.i18n.ListFormatSymbols_nmg_CM', 'goog.labs.i18n.ListFormatSymbols_nn', 'goog.labs.i18n.ListFormatSymbols_nn_NO', 'goog.labs.i18n.ListFormatSymbols_nnh', 'goog.labs.i18n.ListFormatSymbols_nnh_CM', 'goog.labs.i18n.ListFormatSymbols_nus', 'goog.labs.i18n.ListFormatSymbols_nus_SD', 'goog.labs.i18n.ListFormatSymbols_nyn', 'goog.labs.i18n.ListFormatSymbols_nyn_UG', 'goog.labs.i18n.ListFormatSymbols_om', 'goog.labs.i18n.ListFormatSymbols_om_ET', 'goog.labs.i18n.ListFormatSymbols_om_KE', 'goog.labs.i18n.ListFormatSymbols_or_IN', 'goog.labs.i18n.ListFormatSymbols_os', 'goog.labs.i18n.ListFormatSymbols_os_GE', 'goog.labs.i18n.ListFormatSymbols_os_RU', 'goog.labs.i18n.ListFormatSymbols_pa_Arab', 'goog.labs.i18n.ListFormatSymbols_pa_Arab_PK', 'goog.labs.i18n.ListFormatSymbols_pa_Guru', 'goog.labs.i18n.ListFormatSymbols_pa_Guru_IN', 'goog.labs.i18n.ListFormatSymbols_pl_PL', 'goog.labs.i18n.ListFormatSymbols_ps', 'goog.labs.i18n.ListFormatSymbols_ps_AF', 'goog.labs.i18n.ListFormatSymbols_pt_AO', 'goog.labs.i18n.ListFormatSymbols_pt_CV', 'goog.labs.i18n.ListFormatSymbols_pt_GW', 'goog.labs.i18n.ListFormatSymbols_pt_MO', 'goog.labs.i18n.ListFormatSymbols_pt_MZ', 'goog.labs.i18n.ListFormatSymbols_pt_ST', 'goog.labs.i18n.ListFormatSymbols_pt_TL', 'goog.labs.i18n.ListFormatSymbols_qu', 'goog.labs.i18n.ListFormatSymbols_qu_BO', 'goog.labs.i18n.ListFormatSymbols_qu_EC', 'goog.labs.i18n.ListFormatSymbols_qu_PE', 'goog.labs.i18n.ListFormatSymbols_rm', 'goog.labs.i18n.ListFormatSymbols_rm_CH', 'goog.labs.i18n.ListFormatSymbols_rn', 'goog.labs.i18n.ListFormatSymbols_rn_BI', 'goog.labs.i18n.ListFormatSymbols_ro_MD', 'goog.labs.i18n.ListFormatSymbols_ro_RO', 'goog.labs.i18n.ListFormatSymbols_rof', 'goog.labs.i18n.ListFormatSymbols_rof_TZ', 'goog.labs.i18n.ListFormatSymbols_ru_BY', 'goog.labs.i18n.ListFormatSymbols_ru_KG', 'goog.labs.i18n.ListFormatSymbols_ru_KZ', 'goog.labs.i18n.ListFormatSymbols_ru_MD', 'goog.labs.i18n.ListFormatSymbols_ru_RU', 'goog.labs.i18n.ListFormatSymbols_ru_UA', 'goog.labs.i18n.ListFormatSymbols_rw', 'goog.labs.i18n.ListFormatSymbols_rw_RW', 'goog.labs.i18n.ListFormatSymbols_rwk', 'goog.labs.i18n.ListFormatSymbols_rwk_TZ', 'goog.labs.i18n.ListFormatSymbols_sah', 'goog.labs.i18n.ListFormatSymbols_sah_RU', 'goog.labs.i18n.ListFormatSymbols_saq', 'goog.labs.i18n.ListFormatSymbols_saq_KE', 'goog.labs.i18n.ListFormatSymbols_sbp', 'goog.labs.i18n.ListFormatSymbols_sbp_TZ', 'goog.labs.i18n.ListFormatSymbols_se', 'goog.labs.i18n.ListFormatSymbols_se_FI', 'goog.labs.i18n.ListFormatSymbols_se_NO', 'goog.labs.i18n.ListFormatSymbols_se_SE', 'goog.labs.i18n.ListFormatSymbols_seh', 'goog.labs.i18n.ListFormatSymbols_seh_MZ', 'goog.labs.i18n.ListFormatSymbols_ses', 'goog.labs.i18n.ListFormatSymbols_ses_ML', 'goog.labs.i18n.ListFormatSymbols_sg', 'goog.labs.i18n.ListFormatSymbols_sg_CF', 'goog.labs.i18n.ListFormatSymbols_shi', 'goog.labs.i18n.ListFormatSymbols_shi_Latn', 'goog.labs.i18n.ListFormatSymbols_shi_Latn_MA', 'goog.labs.i18n.ListFormatSymbols_shi_Tfng', 'goog.labs.i18n.ListFormatSymbols_shi_Tfng_MA', 'goog.labs.i18n.ListFormatSymbols_si_LK', 'goog.labs.i18n.ListFormatSymbols_sk_SK', 'goog.labs.i18n.ListFormatSymbols_sl_SI', 'goog.labs.i18n.ListFormatSymbols_smn', 'goog.labs.i18n.ListFormatSymbols_smn_FI', 'goog.labs.i18n.ListFormatSymbols_sn', 'goog.labs.i18n.ListFormatSymbols_sn_ZW', 'goog.labs.i18n.ListFormatSymbols_so', 'goog.labs.i18n.ListFormatSymbols_so_DJ', 'goog.labs.i18n.ListFormatSymbols_so_ET', 'goog.labs.i18n.ListFormatSymbols_so_KE', 'goog.labs.i18n.ListFormatSymbols_so_SO', 'goog.labs.i18n.ListFormatSymbols_sq_AL', 'goog.labs.i18n.ListFormatSymbols_sq_MK', 'goog.labs.i18n.ListFormatSymbols_sq_XK', 'goog.labs.i18n.ListFormatSymbols_sr_Cyrl', 'goog.labs.i18n.ListFormatSymbols_sr_Cyrl_BA', 'goog.labs.i18n.ListFormatSymbols_sr_Cyrl_ME', 'goog.labs.i18n.ListFormatSymbols_sr_Cyrl_RS', 'goog.labs.i18n.ListFormatSymbols_sr_Cyrl_XK', 'goog.labs.i18n.ListFormatSymbols_sr_Latn', 'goog.labs.i18n.ListFormatSymbols_sr_Latn_BA', 'goog.labs.i18n.ListFormatSymbols_sr_Latn_ME', 'goog.labs.i18n.ListFormatSymbols_sr_Latn_RS', 'goog.labs.i18n.ListFormatSymbols_sr_Latn_XK', 'goog.labs.i18n.ListFormatSymbols_sv_AX', 'goog.labs.i18n.ListFormatSymbols_sv_FI', 'goog.labs.i18n.ListFormatSymbols_sv_SE', 'goog.labs.i18n.ListFormatSymbols_sw_KE', 'goog.labs.i18n.ListFormatSymbols_sw_TZ', 'goog.labs.i18n.ListFormatSymbols_sw_UG', 'goog.labs.i18n.ListFormatSymbols_swc', 'goog.labs.i18n.ListFormatSymbols_swc_CD', 'goog.labs.i18n.ListFormatSymbols_ta_IN', 'goog.labs.i18n.ListFormatSymbols_ta_LK', 'goog.labs.i18n.ListFormatSymbols_ta_MY', 'goog.labs.i18n.ListFormatSymbols_ta_SG', 'goog.labs.i18n.ListFormatSymbols_te_IN', 'goog.labs.i18n.ListFormatSymbols_teo', 'goog.labs.i18n.ListFormatSymbols_teo_KE', 'goog.labs.i18n.ListFormatSymbols_teo_UG', 'goog.labs.i18n.ListFormatSymbols_th_TH', 'goog.labs.i18n.ListFormatSymbols_ti', 'goog.labs.i18n.ListFormatSymbols_ti_ER', 'goog.labs.i18n.ListFormatSymbols_ti_ET', 'goog.labs.i18n.ListFormatSymbols_to', 'goog.labs.i18n.ListFormatSymbols_to_TO', 'goog.labs.i18n.ListFormatSymbols_tr_CY', 'goog.labs.i18n.ListFormatSymbols_tr_TR', 'goog.labs.i18n.ListFormatSymbols_twq', 'goog.labs.i18n.ListFormatSymbols_twq_NE', 'goog.labs.i18n.ListFormatSymbols_tzm', 'goog.labs.i18n.ListFormatSymbols_tzm_Latn', 'goog.labs.i18n.ListFormatSymbols_tzm_Latn_MA', 'goog.labs.i18n.ListFormatSymbols_ug', 'goog.labs.i18n.ListFormatSymbols_ug_Arab', 'goog.labs.i18n.ListFormatSymbols_ug_Arab_CN', 'goog.labs.i18n.ListFormatSymbols_uk_UA', 'goog.labs.i18n.ListFormatSymbols_ur_IN', 'goog.labs.i18n.ListFormatSymbols_ur_PK', 'goog.labs.i18n.ListFormatSymbols_uz_Arab', 'goog.labs.i18n.ListFormatSymbols_uz_Arab_AF', 'goog.labs.i18n.ListFormatSymbols_uz_Cyrl', 'goog.labs.i18n.ListFormatSymbols_uz_Cyrl_UZ', 'goog.labs.i18n.ListFormatSymbols_uz_Latn', 'goog.labs.i18n.ListFormatSymbols_uz_Latn_UZ', 'goog.labs.i18n.ListFormatSymbols_vai', 'goog.labs.i18n.ListFormatSymbols_vai_Latn', 'goog.labs.i18n.ListFormatSymbols_vai_Latn_LR', 'goog.labs.i18n.ListFormatSymbols_vai_Vaii', 'goog.labs.i18n.ListFormatSymbols_vai_Vaii_LR', 'goog.labs.i18n.ListFormatSymbols_vi_VN', 'goog.labs.i18n.ListFormatSymbols_vun', 'goog.labs.i18n.ListFormatSymbols_vun_TZ', 'goog.labs.i18n.ListFormatSymbols_wae', 'goog.labs.i18n.ListFormatSymbols_wae_CH', 'goog.labs.i18n.ListFormatSymbols_xog', 'goog.labs.i18n.ListFormatSymbols_xog_UG', 'goog.labs.i18n.ListFormatSymbols_yav', 'goog.labs.i18n.ListFormatSymbols_yav_CM', 'goog.labs.i18n.ListFormatSymbols_yi', 'goog.labs.i18n.ListFormatSymbols_yi_001', 'goog.labs.i18n.ListFormatSymbols_yo', 'goog.labs.i18n.ListFormatSymbols_yo_BJ', 'goog.labs.i18n.ListFormatSymbols_yo_NG', 'goog.labs.i18n.ListFormatSymbols_zgh', 'goog.labs.i18n.ListFormatSymbols_zgh_MA', 'goog.labs.i18n.ListFormatSymbols_zh_Hans', 'goog.labs.i18n.ListFormatSymbols_zh_Hans_CN', 'goog.labs.i18n.ListFormatSymbols_zh_Hans_HK', 'goog.labs.i18n.ListFormatSymbols_zh_Hans_MO', 'goog.labs.i18n.ListFormatSymbols_zh_Hans_SG', 'goog.labs.i18n.ListFormatSymbols_zh_Hant', 'goog.labs.i18n.ListFormatSymbols_zh_Hant_HK', 'goog.labs.i18n.ListFormatSymbols_zh_Hant_MO', 'goog.labs.i18n.ListFormatSymbols_zh_Hant_TW', 'goog.labs.i18n.ListFormatSymbols_zu_ZA'], ['goog.labs.i18n.ListFormatSymbols'], false); +goog.addDependency('labs/iterable/iterable.js', ['goog.labs.iterable'], [], true); +goog.addDependency('labs/iterable/iterable_test.js', ['goog.labs.iterableTest'], ['goog.labs.iterable', 'goog.testing.jsunit', 'goog.testing.recordFunction'], true); +goog.addDependency('labs/mock/mock.js', ['goog.labs.mock', 'goog.labs.mock.VerificationError'], ['goog.array', 'goog.asserts', 'goog.debug', 'goog.debug.Error', 'goog.functions', 'goog.object'], false); +goog.addDependency('labs/mock/mock_test.js', ['goog.labs.mockTest'], ['goog.array', 'goog.labs.mock', 'goog.labs.mock.VerificationError', 'goog.labs.testing.AnythingMatcher', 'goog.labs.testing.GreaterThanMatcher', 'goog.string', 'goog.testing.jsunit'], false); +goog.addDependency('labs/net/image.js', ['goog.labs.net.image'], ['goog.Promise', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.net.EventType', 'goog.userAgent'], false); +goog.addDependency('labs/net/image_test.js', ['goog.labs.net.imageTest'], ['goog.labs.net.image', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('labs/net/webchannel.js', ['goog.net.WebChannel'], ['goog.events', 'goog.events.Event'], false); +goog.addDependency('labs/net/webchannel/basetestchannel.js', ['goog.labs.net.webChannel.BaseTestChannel'], ['goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat'], false); +goog.addDependency('labs/net/webchannel/channel.js', ['goog.labs.net.webChannel.Channel'], [], false); +goog.addDependency('labs/net/webchannel/channelrequest.js', ['goog.labs.net.webChannel.ChannelRequest'], ['goog.Timer', 'goog.async.Throttle', 'goog.events.EventHandler', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.XmlHttp', 'goog.object', 'goog.uri.utils.StandardQueryParam', 'goog.userAgent'], false); +goog.addDependency('labs/net/webchannel/channelrequest_test.js', ['goog.labs.net.webChannel.channelRequestTest'], ['goog.Uri', 'goog.functions', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.testing.recordFunction'], false); +goog.addDependency('labs/net/webchannel/connectionstate.js', ['goog.labs.net.webChannel.ConnectionState'], [], false); +goog.addDependency('labs/net/webchannel/forwardchannelrequestpool.js', ['goog.labs.net.webChannel.ForwardChannelRequestPool'], ['goog.array', 'goog.string', 'goog.structs.Set'], false); +goog.addDependency('labs/net/webchannel/forwardchannelrequestpool_test.js', ['goog.labs.net.webChannel.forwardChannelRequestPoolTest'], ['goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.ForwardChannelRequestPool', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('labs/net/webchannel/netutils.js', ['goog.labs.net.webChannel.netUtils'], ['goog.Uri', 'goog.labs.net.webChannel.WebChannelDebug'], false); +goog.addDependency('labs/net/webchannel/requeststats.js', ['goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Event', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.labs.net.webChannel.requestStats.ServerReachabilityEvent', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.labs.net.webChannel.requestStats.StatEvent', 'goog.labs.net.webChannel.requestStats.TimingEvent'], ['goog.events.Event', 'goog.events.EventTarget'], false); +goog.addDependency('labs/net/webchannel/webchannelbase.js', ['goog.labs.net.webChannel.WebChannelBase'], ['goog.Uri', 'goog.array', 'goog.asserts', 'goog.debug.TextFormatter', 'goog.json', 'goog.labs.net.webChannel.BaseTestChannel', 'goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.ConnectionState', 'goog.labs.net.webChannel.ForwardChannelRequestPool', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.Wire', 'goog.labs.net.webChannel.WireV8', 'goog.labs.net.webChannel.netUtils', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.log', 'goog.net.XhrIo', 'goog.object', 'goog.string', 'goog.structs', 'goog.structs.CircularBuffer'], false); +goog.addDependency('labs/net/webchannel/webchannelbase_test.js', ['goog.labs.net.webChannel.webChannelBaseTest'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.functions', 'goog.json', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.ForwardChannelRequestPool', 'goog.labs.net.webChannel.WebChannelBase', 'goog.labs.net.webChannel.WebChannelBaseTransport', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.Wire', 'goog.labs.net.webChannel.netUtils', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.structs.Map', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('labs/net/webchannel/webchannelbasetransport.js', ['goog.labs.net.webChannel.WebChannelBaseTransport'], ['goog.asserts', 'goog.events.EventTarget', 'goog.labs.net.webChannel.WebChannelBase', 'goog.log', 'goog.net.WebChannel', 'goog.net.WebChannelTransport', 'goog.object', 'goog.string.path'], false); +goog.addDependency('labs/net/webchannel/webchannelbasetransport_test.js', ['goog.labs.net.webChannel.webChannelBaseTransportTest'], ['goog.events', 'goog.labs.net.webChannel.WebChannelBaseTransport', 'goog.net.WebChannel', 'goog.testing.jsunit'], false); +goog.addDependency('labs/net/webchannel/webchanneldebug.js', ['goog.labs.net.webChannel.WebChannelDebug'], ['goog.json', 'goog.log'], false); +goog.addDependency('labs/net/webchannel/wire.js', ['goog.labs.net.webChannel.Wire'], [], false); +goog.addDependency('labs/net/webchannel/wirev8.js', ['goog.labs.net.webChannel.WireV8'], ['goog.asserts', 'goog.json', 'goog.json.NativeJsonProcessor', 'goog.structs'], false); +goog.addDependency('labs/net/webchannel/wirev8_test.js', ['goog.labs.net.webChannel.WireV8Test'], ['goog.labs.net.webChannel.WireV8', 'goog.testing.jsunit'], false); +goog.addDependency('labs/net/webchanneltransport.js', ['goog.net.WebChannelTransport'], [], false); +goog.addDependency('labs/net/webchanneltransportfactory.js', ['goog.net.createWebChannelTransport'], ['goog.functions', 'goog.labs.net.webChannel.WebChannelBaseTransport'], false); +goog.addDependency('labs/net/xhr.js', ['goog.labs.net.xhr', 'goog.labs.net.xhr.Error', 'goog.labs.net.xhr.HttpError', 'goog.labs.net.xhr.Options', 'goog.labs.net.xhr.PostData', 'goog.labs.net.xhr.ResponseType', 'goog.labs.net.xhr.TimeoutError'], ['goog.Promise', 'goog.debug.Error', 'goog.json', 'goog.net.HttpStatus', 'goog.net.XmlHttp', 'goog.string', 'goog.uri.utils', 'goog.userAgent'], false); +goog.addDependency('labs/net/xhr_test.js', ['goog.labs.net.xhrTest'], ['goog.Promise', 'goog.labs.net.xhr', 'goog.net.WrapperXmlHttpFactory', 'goog.net.XmlHttp', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('labs/object/object.js', ['goog.labs.object'], [], false); +goog.addDependency('labs/object/object_test.js', ['goog.labs.objectTest'], ['goog.labs.object', 'goog.testing.jsunit'], false); +goog.addDependency('labs/pubsub/broadcastpubsub.js', ['goog.labs.pubsub.BroadcastPubSub'], ['goog.Disposable', 'goog.Timer', 'goog.array', 'goog.async.run', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.json', 'goog.log', 'goog.math', 'goog.pubsub.PubSub', 'goog.storage.Storage', 'goog.storage.mechanism.HTML5LocalStorage', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('labs/pubsub/broadcastpubsub_test.js', ['goog.labs.pubsub.BroadcastPubSubTest'], ['goog.array', 'goog.debug.Logger', 'goog.json', 'goog.labs.pubsub.BroadcastPubSub', 'goog.storage.Storage', 'goog.structs.Map', 'goog.testing.MockClock', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.testing.mockmatchers.ArgumentMatcher', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('labs/storage/boundedcollectablestorage.js', ['goog.labs.storage.BoundedCollectableStorage'], ['goog.array', 'goog.asserts', 'goog.iter', 'goog.storage.CollectableStorage', 'goog.storage.ErrorCode', 'goog.storage.ExpiringStorage'], false); +goog.addDependency('labs/storage/boundedcollectablestorage_test.js', ['goog.labs.storage.BoundedCollectableStorageTest'], ['goog.labs.storage.BoundedCollectableStorage', 'goog.storage.collectableStorageTester', 'goog.storage.storage_test', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.storage.FakeMechanism'], false); +goog.addDependency('labs/structs/map.js', ['goog.labs.structs.Map'], ['goog.array', 'goog.asserts', 'goog.labs.object', 'goog.object'], false); +goog.addDependency('labs/structs/map_perf.js', ['goog.labs.structs.MapPerf'], ['goog.asserts', 'goog.dom', 'goog.labs.structs.Map', 'goog.structs.Map', 'goog.testing.PerformanceTable', 'goog.testing.jsunit'], false); +goog.addDependency('labs/structs/map_test.js', ['goog.labs.structs.MapTest'], ['goog.labs.structs.Map', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('labs/structs/multimap.js', ['goog.labs.structs.Multimap'], ['goog.array', 'goog.labs.object', 'goog.labs.structs.Map'], false); +goog.addDependency('labs/structs/multimap_test.js', ['goog.labs.structs.MultimapTest'], ['goog.labs.structs.Map', 'goog.labs.structs.Multimap', 'goog.testing.jsunit'], false); +goog.addDependency('labs/style/pixeldensitymonitor.js', ['goog.labs.style.PixelDensityMonitor', 'goog.labs.style.PixelDensityMonitor.Density', 'goog.labs.style.PixelDensityMonitor.EventType'], ['goog.events', 'goog.events.EventTarget'], false); +goog.addDependency('labs/style/pixeldensitymonitor_test.js', ['goog.labs.style.PixelDensityMonitorTest'], ['goog.array', 'goog.dom.DomHelper', 'goog.events', 'goog.labs.style.PixelDensityMonitor', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('labs/testing/assertthat.js', ['goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat'], ['goog.debug.Error'], false); +goog.addDependency('labs/testing/assertthat_test.js', ['goog.labs.testing.assertThatTest'], ['goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('labs/testing/decoratormatcher.js', ['goog.labs.testing.AnythingMatcher'], ['goog.labs.testing.Matcher'], false); +goog.addDependency('labs/testing/decoratormatcher_test.js', ['goog.labs.testing.decoratorMatcherTest'], ['goog.labs.testing.AnythingMatcher', 'goog.labs.testing.GreaterThanMatcher', 'goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/dictionarymatcher.js', ['goog.labs.testing.HasEntriesMatcher', 'goog.labs.testing.HasEntryMatcher', 'goog.labs.testing.HasKeyMatcher', 'goog.labs.testing.HasValueMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher', 'goog.object'], false); +goog.addDependency('labs/testing/dictionarymatcher_test.js', ['goog.labs.testing.dictionaryMatcherTest'], ['goog.labs.testing.HasEntryMatcher', 'goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/environment.js', ['goog.labs.testing.Environment'], ['goog.array', 'goog.debug.Console', 'goog.testing.MockClock', 'goog.testing.MockControl', 'goog.testing.TestCase', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/environment_test.js', ['goog.labs.testing.environmentTest'], ['goog.labs.testing.Environment', 'goog.testing.MockControl', 'goog.testing.TestCase', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/environment_usage_test.js', ['goog.labs.testing.environmentUsageTest'], ['goog.labs.testing.Environment'], false); +goog.addDependency('labs/testing/logicmatcher.js', ['goog.labs.testing.AllOfMatcher', 'goog.labs.testing.AnyOfMatcher', 'goog.labs.testing.IsNotMatcher'], ['goog.array', 'goog.labs.testing.Matcher'], false); +goog.addDependency('labs/testing/logicmatcher_test.js', ['goog.labs.testing.logicMatcherTest'], ['goog.labs.testing.AllOfMatcher', 'goog.labs.testing.GreaterThanMatcher', 'goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/matcher.js', ['goog.labs.testing.Matcher'], [], false); +goog.addDependency('labs/testing/numbermatcher.js', ['goog.labs.testing.CloseToMatcher', 'goog.labs.testing.EqualToMatcher', 'goog.labs.testing.GreaterThanEqualToMatcher', 'goog.labs.testing.GreaterThanMatcher', 'goog.labs.testing.LessThanEqualToMatcher', 'goog.labs.testing.LessThanMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher'], false); +goog.addDependency('labs/testing/numbermatcher_test.js', ['goog.labs.testing.numberMatcherTest'], ['goog.labs.testing.LessThanMatcher', 'goog.labs.testing.MatcherError', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/objectmatcher.js', ['goog.labs.testing.HasPropertyMatcher', 'goog.labs.testing.InstanceOfMatcher', 'goog.labs.testing.IsNullMatcher', 'goog.labs.testing.IsNullOrUndefinedMatcher', 'goog.labs.testing.IsUndefinedMatcher', 'goog.labs.testing.ObjectEqualsMatcher'], ['goog.labs.testing.Matcher'], false); +goog.addDependency('labs/testing/objectmatcher_test.js', ['goog.labs.testing.objectMatcherTest'], ['goog.labs.testing.MatcherError', 'goog.labs.testing.ObjectEqualsMatcher', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/testing/stringmatcher.js', ['goog.labs.testing.ContainsStringMatcher', 'goog.labs.testing.EndsWithMatcher', 'goog.labs.testing.EqualToIgnoringWhitespaceMatcher', 'goog.labs.testing.EqualsMatcher', 'goog.labs.testing.RegexMatcher', 'goog.labs.testing.StartsWithMatcher', 'goog.labs.testing.StringContainsInOrderMatcher'], ['goog.asserts', 'goog.labs.testing.Matcher', 'goog.string'], false); +goog.addDependency('labs/testing/stringmatcher_test.js', ['goog.labs.testing.stringMatcherTest'], ['goog.labs.testing.MatcherError', 'goog.labs.testing.StringContainsInOrderMatcher', 'goog.labs.testing.assertThat', 'goog.testing.jsunit'], false); +goog.addDependency('labs/useragent/browser.js', ['goog.labs.userAgent.browser'], ['goog.array', 'goog.labs.userAgent.util', 'goog.object', 'goog.string'], false); +goog.addDependency('labs/useragent/browser_test.js', ['goog.labs.userAgent.browserTest'], ['goog.labs.userAgent.browser', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.jsunit'], false); +goog.addDependency('labs/useragent/device.js', ['goog.labs.userAgent.device'], ['goog.labs.userAgent.util'], false); +goog.addDependency('labs/useragent/device_test.js', ['goog.labs.userAgent.deviceTest'], ['goog.labs.userAgent.device', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.jsunit'], false); +goog.addDependency('labs/useragent/engine.js', ['goog.labs.userAgent.engine'], ['goog.array', 'goog.labs.userAgent.util', 'goog.string'], false); +goog.addDependency('labs/useragent/engine_test.js', ['goog.labs.userAgent.engineTest'], ['goog.labs.userAgent.engine', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.jsunit'], false); +goog.addDependency('labs/useragent/platform.js', ['goog.labs.userAgent.platform'], ['goog.labs.userAgent.util', 'goog.string'], false); +goog.addDependency('labs/useragent/platform_test.js', ['goog.labs.userAgent.platformTest'], ['goog.labs.userAgent.platform', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.jsunit'], false); +goog.addDependency('labs/useragent/test_agents.js', ['goog.labs.userAgent.testAgents'], [], false); +goog.addDependency('labs/useragent/util.js', ['goog.labs.userAgent.util'], ['goog.string'], false); +goog.addDependency('labs/useragent/util_test.js', ['goog.labs.userAgent.utilTest'], ['goog.functions', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('locale/countries.js', ['goog.locale.countries'], [], false); +goog.addDependency('locale/countrylanguagenames_test.js', ['goog.locale.countryLanguageNamesTest'], ['goog.locale', 'goog.testing.jsunit'], false); +goog.addDependency('locale/defaultlocalenameconstants.js', ['goog.locale.defaultLocaleNameConstants'], [], false); +goog.addDependency('locale/genericfontnames.js', ['goog.locale.genericFontNames'], [], false); +goog.addDependency('locale/genericfontnames_test.js', ['goog.locale.genericFontNamesTest'], ['goog.locale.genericFontNames', 'goog.testing.jsunit'], false); +goog.addDependency('locale/genericfontnamesdata.js', ['goog.locale.genericFontNamesData'], [], false); +goog.addDependency('locale/locale.js', ['goog.locale'], ['goog.locale.nativeNameConstants'], false); +goog.addDependency('locale/nativenameconstants.js', ['goog.locale.nativeNameConstants'], [], false); +goog.addDependency('locale/scriptToLanguages.js', ['goog.locale.scriptToLanguages'], ['goog.locale'], false); +goog.addDependency('locale/timezonedetection.js', ['goog.locale.timeZoneDetection'], ['goog.locale.TimeZoneFingerprint'], false); +goog.addDependency('locale/timezonedetection_test.js', ['goog.locale.timeZoneDetectionTest'], ['goog.locale.timeZoneDetection', 'goog.testing.jsunit'], false); +goog.addDependency('locale/timezonefingerprint.js', ['goog.locale.TimeZoneFingerprint'], [], false); +goog.addDependency('locale/timezonelist.js', ['goog.locale.TimeZoneList'], ['goog.locale'], false); +goog.addDependency('locale/timezonelist_test.js', ['goog.locale.TimeZoneListTest'], ['goog.locale', 'goog.locale.TimeZoneList', 'goog.testing.jsunit'], false); +goog.addDependency('log/log.js', ['goog.log', 'goog.log.Level', 'goog.log.LogRecord', 'goog.log.Logger'], ['goog.debug', 'goog.debug.LogManager', 'goog.debug.LogRecord', 'goog.debug.Logger'], false); +goog.addDependency('log/log_test.js', ['goog.logTest'], ['goog.debug.LogManager', 'goog.log', 'goog.log.Level', 'goog.testing.jsunit'], false); +goog.addDependency('math/affinetransform.js', ['goog.math.AffineTransform'], ['goog.math'], false); +goog.addDependency('math/affinetransform_test.js', ['goog.math.AffineTransformTest'], ['goog.array', 'goog.math', 'goog.math.AffineTransform', 'goog.testing.jsunit'], false); +goog.addDependency('math/bezier.js', ['goog.math.Bezier'], ['goog.math', 'goog.math.Coordinate'], false); +goog.addDependency('math/bezier_test.js', ['goog.math.BezierTest'], ['goog.math', 'goog.math.Bezier', 'goog.math.Coordinate', 'goog.testing.jsunit'], false); +goog.addDependency('math/box.js', ['goog.math.Box'], ['goog.math.Coordinate'], false); +goog.addDependency('math/box_test.js', ['goog.math.BoxTest'], ['goog.math.Box', 'goog.math.Coordinate', 'goog.testing.jsunit'], false); +goog.addDependency('math/coordinate.js', ['goog.math.Coordinate'], ['goog.math'], false); +goog.addDependency('math/coordinate3.js', ['goog.math.Coordinate3'], [], false); +goog.addDependency('math/coordinate3_test.js', ['goog.math.Coordinate3Test'], ['goog.math.Coordinate3', 'goog.testing.jsunit'], false); +goog.addDependency('math/coordinate_test.js', ['goog.math.CoordinateTest'], ['goog.math.Coordinate', 'goog.testing.jsunit'], false); +goog.addDependency('math/exponentialbackoff.js', ['goog.math.ExponentialBackoff'], ['goog.asserts'], false); +goog.addDependency('math/exponentialbackoff_test.js', ['goog.math.ExponentialBackoffTest'], ['goog.math.ExponentialBackoff', 'goog.testing.jsunit'], false); +goog.addDependency('math/integer.js', ['goog.math.Integer'], [], false); +goog.addDependency('math/integer_test.js', ['goog.math.IntegerTest'], ['goog.math.Integer', 'goog.testing.jsunit'], false); +goog.addDependency('math/interpolator/interpolator1.js', ['goog.math.interpolator.Interpolator1'], [], false); +goog.addDependency('math/interpolator/linear1.js', ['goog.math.interpolator.Linear1'], ['goog.array', 'goog.asserts', 'goog.math', 'goog.math.interpolator.Interpolator1'], false); +goog.addDependency('math/interpolator/linear1_test.js', ['goog.math.interpolator.Linear1Test'], ['goog.math.interpolator.Linear1', 'goog.testing.jsunit'], false); +goog.addDependency('math/interpolator/pchip1.js', ['goog.math.interpolator.Pchip1'], ['goog.math', 'goog.math.interpolator.Spline1'], false); +goog.addDependency('math/interpolator/pchip1_test.js', ['goog.math.interpolator.Pchip1Test'], ['goog.math.interpolator.Pchip1', 'goog.testing.jsunit'], false); +goog.addDependency('math/interpolator/spline1.js', ['goog.math.interpolator.Spline1'], ['goog.array', 'goog.asserts', 'goog.math', 'goog.math.interpolator.Interpolator1', 'goog.math.tdma'], false); +goog.addDependency('math/interpolator/spline1_test.js', ['goog.math.interpolator.Spline1Test'], ['goog.math.interpolator.Spline1', 'goog.testing.jsunit'], false); +goog.addDependency('math/line.js', ['goog.math.Line'], ['goog.math', 'goog.math.Coordinate'], false); +goog.addDependency('math/line_test.js', ['goog.math.LineTest'], ['goog.math.Coordinate', 'goog.math.Line', 'goog.testing.jsunit'], false); +goog.addDependency('math/long.js', ['goog.math.Long'], [], false); +goog.addDependency('math/long_test.js', ['goog.math.LongTest'], ['goog.math.Long', 'goog.testing.jsunit'], false); +goog.addDependency('math/math.js', ['goog.math'], ['goog.array', 'goog.asserts'], false); +goog.addDependency('math/math_test.js', ['goog.mathTest'], ['goog.math', 'goog.testing.jsunit'], false); +goog.addDependency('math/matrix.js', ['goog.math.Matrix'], ['goog.array', 'goog.math', 'goog.math.Size', 'goog.string'], false); +goog.addDependency('math/matrix_test.js', ['goog.math.MatrixTest'], ['goog.math.Matrix', 'goog.testing.jsunit'], false); +goog.addDependency('math/path.js', ['goog.math.Path', 'goog.math.Path.Segment'], ['goog.array', 'goog.math'], false); +goog.addDependency('math/path_test.js', ['goog.math.PathTest'], ['goog.array', 'goog.math.AffineTransform', 'goog.math.Path', 'goog.testing.jsunit'], false); +goog.addDependency('math/paths.js', ['goog.math.paths'], ['goog.math.Coordinate', 'goog.math.Path'], false); +goog.addDependency('math/paths_test.js', ['goog.math.pathsTest'], ['goog.math.Coordinate', 'goog.math.paths', 'goog.testing.jsunit'], false); +goog.addDependency('math/range.js', ['goog.math.Range'], ['goog.asserts'], false); +goog.addDependency('math/range_test.js', ['goog.math.RangeTest'], ['goog.math.Range', 'goog.testing.jsunit'], false); +goog.addDependency('math/rangeset.js', ['goog.math.RangeSet'], ['goog.array', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.math.Range'], false); +goog.addDependency('math/rangeset_test.js', ['goog.math.RangeSetTest'], ['goog.iter', 'goog.math.Range', 'goog.math.RangeSet', 'goog.testing.jsunit'], false); +goog.addDependency('math/rect.js', ['goog.math.Rect'], ['goog.math.Box', 'goog.math.Coordinate', 'goog.math.Size'], false); +goog.addDependency('math/rect_test.js', ['goog.math.RectTest'], ['goog.math.Box', 'goog.math.Coordinate', 'goog.math.Rect', 'goog.math.Size', 'goog.testing.jsunit'], false); +goog.addDependency('math/size.js', ['goog.math.Size'], [], false); +goog.addDependency('math/size_test.js', ['goog.math.SizeTest'], ['goog.math.Size', 'goog.testing.jsunit'], false); +goog.addDependency('math/tdma.js', ['goog.math.tdma'], [], false); +goog.addDependency('math/tdma_test.js', ['goog.math.tdmaTest'], ['goog.math.tdma', 'goog.testing.jsunit'], false); +goog.addDependency('math/vec2.js', ['goog.math.Vec2'], ['goog.math', 'goog.math.Coordinate'], false); +goog.addDependency('math/vec2_test.js', ['goog.math.Vec2Test'], ['goog.math.Vec2', 'goog.testing.jsunit'], false); +goog.addDependency('math/vec3.js', ['goog.math.Vec3'], ['goog.math', 'goog.math.Coordinate3'], false); +goog.addDependency('math/vec3_test.js', ['goog.math.Vec3Test'], ['goog.math.Coordinate3', 'goog.math.Vec3', 'goog.testing.jsunit'], false); +goog.addDependency('memoize/memoize.js', ['goog.memoize'], [], false); +goog.addDependency('memoize/memoize_test.js', ['goog.memoizeTest'], ['goog.memoize', 'goog.testing.jsunit'], false); +goog.addDependency('messaging/abstractchannel.js', ['goog.messaging.AbstractChannel'], ['goog.Disposable', 'goog.json', 'goog.log', 'goog.messaging.MessageChannel'], false); +goog.addDependency('messaging/abstractchannel_test.js', ['goog.messaging.AbstractChannelTest'], ['goog.messaging.AbstractChannel', 'goog.testing.MockControl', 'goog.testing.async.MockControl', 'goog.testing.jsunit'], false); +goog.addDependency('messaging/bufferedchannel.js', ['goog.messaging.BufferedChannel'], ['goog.Disposable', 'goog.Timer', 'goog.events', 'goog.log', 'goog.messaging.MessageChannel', 'goog.messaging.MultiChannel'], false); +goog.addDependency('messaging/bufferedchannel_test.js', ['goog.messaging.BufferedChannelTest'], ['goog.debug.Console', 'goog.dom', 'goog.log', 'goog.log.Level', 'goog.messaging.BufferedChannel', 'goog.testing.MockClock', 'goog.testing.MockControl', 'goog.testing.async.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/deferredchannel.js', ['goog.messaging.DeferredChannel'], ['goog.Disposable', 'goog.messaging.MessageChannel'], false); +goog.addDependency('messaging/deferredchannel_test.js', ['goog.messaging.DeferredChannelTest'], ['goog.async.Deferred', 'goog.messaging.DeferredChannel', 'goog.testing.MockControl', 'goog.testing.async.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/loggerclient.js', ['goog.messaging.LoggerClient'], ['goog.Disposable', 'goog.debug', 'goog.debug.LogManager', 'goog.debug.Logger'], false); +goog.addDependency('messaging/loggerclient_test.js', ['goog.messaging.LoggerClientTest'], ['goog.debug', 'goog.debug.Logger', 'goog.messaging.LoggerClient', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/loggerserver.js', ['goog.messaging.LoggerServer'], ['goog.Disposable', 'goog.log', 'goog.log.Level'], false); +goog.addDependency('messaging/loggerserver_test.js', ['goog.messaging.LoggerServerTest'], ['goog.debug.LogManager', 'goog.debug.Logger', 'goog.log', 'goog.log.Level', 'goog.messaging.LoggerServer', 'goog.testing.MockControl', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/messagechannel.js', ['goog.messaging.MessageChannel'], [], false); +goog.addDependency('messaging/messaging.js', ['goog.messaging'], [], false); +goog.addDependency('messaging/messaging_test.js', ['goog.testing.messaging.MockMessageChannelTest'], ['goog.messaging', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/multichannel.js', ['goog.messaging.MultiChannel', 'goog.messaging.MultiChannel.VirtualChannel'], ['goog.Disposable', 'goog.log', 'goog.messaging.MessageChannel', 'goog.object'], false); +goog.addDependency('messaging/multichannel_test.js', ['goog.messaging.MultiChannelTest'], ['goog.messaging.MultiChannel', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel', 'goog.testing.mockmatchers.IgnoreArgument'], false); +goog.addDependency('messaging/portcaller.js', ['goog.messaging.PortCaller'], ['goog.Disposable', 'goog.async.Deferred', 'goog.messaging.DeferredChannel', 'goog.messaging.PortChannel', 'goog.messaging.PortNetwork', 'goog.object'], false); +goog.addDependency('messaging/portcaller_test.js', ['goog.messaging.PortCallerTest'], ['goog.events.EventTarget', 'goog.messaging.PortCaller', 'goog.messaging.PortNetwork', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/portchannel.js', ['goog.messaging.PortChannel'], ['goog.Timer', 'goog.array', 'goog.async.Deferred', 'goog.debug', 'goog.events', 'goog.events.EventType', 'goog.json', 'goog.log', 'goog.messaging.AbstractChannel', 'goog.messaging.DeferredChannel', 'goog.object', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('messaging/portnetwork.js', ['goog.messaging.PortNetwork'], [], false); +goog.addDependency('messaging/portoperator.js', ['goog.messaging.PortOperator'], ['goog.Disposable', 'goog.asserts', 'goog.log', 'goog.messaging.PortChannel', 'goog.messaging.PortNetwork', 'goog.object'], false); +goog.addDependency('messaging/portoperator_test.js', ['goog.messaging.PortOperatorTest'], ['goog.messaging.PortNetwork', 'goog.messaging.PortOperator', 'goog.testing.MockControl', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel', 'goog.testing.messaging.MockMessagePort'], false); +goog.addDependency('messaging/respondingchannel.js', ['goog.messaging.RespondingChannel'], ['goog.Disposable', 'goog.log', 'goog.messaging.MultiChannel'], false); +goog.addDependency('messaging/respondingchannel_test.js', ['goog.messaging.RespondingChannelTest'], ['goog.messaging.RespondingChannel', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('messaging/testdata/portchannel_worker.js', ['goog.messaging.testdata.portchannel_worker'], ['goog.messaging.PortChannel'], false); +goog.addDependency('messaging/testdata/portnetwork_worker1.js', ['goog.messaging.testdata.portnetwork_worker1'], ['goog.messaging.PortCaller', 'goog.messaging.PortChannel'], false); +goog.addDependency('messaging/testdata/portnetwork_worker2.js', ['goog.messaging.testdata.portnetwork_worker2'], ['goog.messaging.PortCaller', 'goog.messaging.PortChannel'], false); +goog.addDependency('module/abstractmoduleloader.js', ['goog.module.AbstractModuleLoader'], ['goog.module'], false); +goog.addDependency('module/basemodule.js', ['goog.module.BaseModule'], ['goog.Disposable', 'goog.module'], false); +goog.addDependency('module/loader.js', ['goog.module.Loader'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.module', 'goog.object'], false); +goog.addDependency('module/module.js', ['goog.module'], [], false); +goog.addDependency('module/moduleinfo.js', ['goog.module.ModuleInfo'], ['goog.Disposable', 'goog.functions', 'goog.module', 'goog.module.BaseModule', 'goog.module.ModuleLoadCallback'], false); +goog.addDependency('module/moduleinfo_test.js', ['goog.module.ModuleInfoTest'], ['goog.module.BaseModule', 'goog.module.ModuleInfo', 'goog.testing.jsunit'], false); +goog.addDependency('module/moduleloadcallback.js', ['goog.module.ModuleLoadCallback'], ['goog.debug.entryPointRegistry', 'goog.debug.errorHandlerWeakDep', 'goog.module'], false); +goog.addDependency('module/moduleloadcallback_test.js', ['goog.module.ModuleLoadCallbackTest'], ['goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.functions', 'goog.module.ModuleLoadCallback', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('module/moduleloader.js', ['goog.module.ModuleLoader'], ['goog.Timer', 'goog.array', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.log', 'goog.module.AbstractModuleLoader', 'goog.net.BulkLoader', 'goog.net.EventType', 'goog.net.jsloader', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('module/moduleloader_test.js', ['goog.module.ModuleLoaderTest'], ['goog.array', 'goog.dom', 'goog.events', 'goog.functions', 'goog.module.ModuleLoader', 'goog.module.ModuleManager', 'goog.net.BulkLoader', 'goog.net.XmlHttp', 'goog.object', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.events.EventObserver', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('module/modulemanager.js', ['goog.module.ModuleManager', 'goog.module.ModuleManager.CallbackType', 'goog.module.ModuleManager.FailureType'], ['goog.Disposable', 'goog.array', 'goog.asserts', 'goog.async.Deferred', 'goog.debug.Trace', 'goog.dispose', 'goog.log', 'goog.module', 'goog.module.ModuleInfo', 'goog.module.ModuleLoadCallback', 'goog.object'], false); +goog.addDependency('module/modulemanager_test.js', ['goog.module.ModuleManagerTest'], ['goog.array', 'goog.functions', 'goog.module.BaseModule', 'goog.module.ModuleManager', 'goog.testing', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('module/testdata/modA_1.js', ['goog.module.testdata.modA_1'], [], false); +goog.addDependency('module/testdata/modA_2.js', ['goog.module.testdata.modA_2'], ['goog.module.ModuleManager'], false); +goog.addDependency('module/testdata/modB_1.js', ['goog.module.testdata.modB_1'], ['goog.module.ModuleManager'], false); +goog.addDependency('net/browserchannel.js', ['goog.net.BrowserChannel', 'goog.net.BrowserChannel.Error', 'goog.net.BrowserChannel.Event', 'goog.net.BrowserChannel.Handler', 'goog.net.BrowserChannel.LogSaver', 'goog.net.BrowserChannel.QueuedMap', 'goog.net.BrowserChannel.ServerReachability', 'goog.net.BrowserChannel.ServerReachabilityEvent', 'goog.net.BrowserChannel.Stat', 'goog.net.BrowserChannel.StatEvent', 'goog.net.BrowserChannel.State', 'goog.net.BrowserChannel.TimingEvent'], ['goog.Uri', 'goog.array', 'goog.asserts', 'goog.debug.TextFormatter', 'goog.events.Event', 'goog.events.EventTarget', 'goog.json', 'goog.json.EvalJsonProcessor', 'goog.log', 'goog.net.BrowserTestChannel', 'goog.net.ChannelDebug', 'goog.net.ChannelRequest', 'goog.net.XhrIo', 'goog.net.tmpnetwork', 'goog.object', 'goog.string', 'goog.structs', 'goog.structs.CircularBuffer'], false); +goog.addDependency('net/browserchannel_test.js', ['goog.net.BrowserChannelTest'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.functions', 'goog.json', 'goog.net.BrowserChannel', 'goog.net.ChannelDebug', 'goog.net.ChannelRequest', 'goog.net.tmpnetwork', 'goog.structs.Map', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('net/browsertestchannel.js', ['goog.net.BrowserTestChannel'], ['goog.json.EvalJsonProcessor', 'goog.net.ChannelRequest', 'goog.net.ChannelRequest.Error', 'goog.net.tmpnetwork', 'goog.string.Parser', 'goog.userAgent'], false); +goog.addDependency('net/bulkloader.js', ['goog.net.BulkLoader'], ['goog.events.EventHandler', 'goog.events.EventTarget', 'goog.log', 'goog.net.BulkLoaderHelper', 'goog.net.EventType', 'goog.net.XhrIo'], false); +goog.addDependency('net/bulkloader_test.js', ['goog.net.BulkLoaderTest'], ['goog.events.Event', 'goog.events.EventHandler', 'goog.net.BulkLoader', 'goog.net.EventType', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('net/bulkloaderhelper.js', ['goog.net.BulkLoaderHelper'], ['goog.Disposable', 'goog.log'], false); +goog.addDependency('net/channeldebug.js', ['goog.net.ChannelDebug'], ['goog.json', 'goog.log'], false); +goog.addDependency('net/channelrequest.js', ['goog.net.ChannelRequest', 'goog.net.ChannelRequest.Error'], ['goog.Timer', 'goog.async.Throttle', 'goog.events.EventHandler', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.XmlHttp', 'goog.object', 'goog.userAgent'], false); +goog.addDependency('net/channelrequest_test.js', ['goog.net.ChannelRequestTest'], ['goog.Uri', 'goog.functions', 'goog.net.BrowserChannel', 'goog.net.ChannelDebug', 'goog.net.ChannelRequest', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.testing.recordFunction'], false); +goog.addDependency('net/cookies.js', ['goog.net.Cookies', 'goog.net.cookies'], [], false); +goog.addDependency('net/cookies_test.js', ['goog.net.cookiesTest'], ['goog.array', 'goog.net.cookies', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('net/corsxmlhttpfactory.js', ['goog.net.CorsXmlHttpFactory', 'goog.net.IeCorsXhrAdapter'], ['goog.net.HttpStatus', 'goog.net.XhrLike', 'goog.net.XmlHttp', 'goog.net.XmlHttpFactory'], false); +goog.addDependency('net/corsxmlhttpfactory_test.js', ['goog.net.CorsXmlHttpFactoryTest'], ['goog.net.CorsXmlHttpFactory', 'goog.net.IeCorsXhrAdapter', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('net/crossdomainrpc.js', ['goog.net.CrossDomainRpc'], ['goog.Uri', 'goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.json', 'goog.log', 'goog.net.EventType', 'goog.net.HttpStatus', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('net/crossdomainrpc_test.js', ['goog.net.CrossDomainRpcTest'], ['goog.log', 'goog.log.Level', 'goog.net.CrossDomainRpc', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('net/errorcode.js', ['goog.net.ErrorCode'], [], false); +goog.addDependency('net/eventtype.js', ['goog.net.EventType'], [], false); +goog.addDependency('net/filedownloader.js', ['goog.net.FileDownloader', 'goog.net.FileDownloader.Error'], ['goog.Disposable', 'goog.asserts', 'goog.async.Deferred', 'goog.crypt.hash32', 'goog.debug.Error', 'goog.events', 'goog.events.EventHandler', 'goog.fs', 'goog.fs.DirectoryEntry', 'goog.fs.Error', 'goog.fs.FileSaver', 'goog.net.EventType', 'goog.net.XhrIo', 'goog.net.XhrIoPool', 'goog.object'], false); +goog.addDependency('net/filedownloader_test.js', ['goog.net.FileDownloaderTest'], ['goog.fs.Error', 'goog.net.ErrorCode', 'goog.net.FileDownloader', 'goog.net.XhrIo', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.fs', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit', 'goog.testing.net.XhrIoPool'], false); +goog.addDependency('net/httpstatus.js', ['goog.net.HttpStatus'], [], false); +goog.addDependency('net/iframe_xhr_test.js', ['goog.net.iframeXhrTest'], ['goog.Timer', 'goog.debug.Console', 'goog.debug.LogManager', 'goog.debug.Logger', 'goog.events', 'goog.net.IframeIo', 'goog.net.XhrIo', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('net/iframeio.js', ['goog.net.IframeIo', 'goog.net.IframeIo.IncrementalDataEvent'], ['goog.Timer', 'goog.Uri', 'goog.asserts', 'goog.debug', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.json', 'goog.log', 'goog.log.Level', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.reflect', 'goog.string', 'goog.structs', 'goog.userAgent'], false); +goog.addDependency('net/iframeio_different_base_test.js', ['goog.net.iframeIoDifferentBaseTest'], ['goog.events', 'goog.net.EventType', 'goog.net.IframeIo', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('net/iframeio_test.js', ['goog.net.IframeIoTest'], ['goog.debug', 'goog.debug.DivConsole', 'goog.debug.LogManager', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.log', 'goog.log.Level', 'goog.net.IframeIo', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('net/iframeloadmonitor.js', ['goog.net.IframeLoadMonitor'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.userAgent'], false); +goog.addDependency('net/iframeloadmonitor_test.js', ['goog.net.IframeLoadMonitorTest'], ['goog.dom', 'goog.events', 'goog.net.IframeLoadMonitor', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('net/imageloader.js', ['goog.net.ImageLoader'], ['goog.array', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.net.EventType', 'goog.object', 'goog.userAgent'], false); +goog.addDependency('net/imageloader_test.js', ['goog.net.ImageLoaderTest'], ['goog.Timer', 'goog.array', 'goog.dispose', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.net.EventType', 'goog.net.ImageLoader', 'goog.object', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('net/ipaddress.js', ['goog.net.IpAddress', 'goog.net.Ipv4Address', 'goog.net.Ipv6Address'], ['goog.array', 'goog.math.Integer', 'goog.object', 'goog.string'], false); +goog.addDependency('net/ipaddress_test.js', ['goog.net.IpAddressTest'], ['goog.math.Integer', 'goog.net.IpAddress', 'goog.net.Ipv4Address', 'goog.net.Ipv6Address', 'goog.testing.jsunit'], false); +goog.addDependency('net/jsloader.js', ['goog.net.jsloader', 'goog.net.jsloader.Error', 'goog.net.jsloader.ErrorCode', 'goog.net.jsloader.Options'], ['goog.array', 'goog.async.Deferred', 'goog.debug.Error', 'goog.dom', 'goog.dom.TagName'], false); +goog.addDependency('net/jsloader_test.js', ['goog.net.jsloaderTest'], ['goog.array', 'goog.dom', 'goog.net.jsloader', 'goog.net.jsloader.ErrorCode', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('net/jsonp.js', ['goog.net.Jsonp'], ['goog.Uri', 'goog.net.jsloader'], false); +goog.addDependency('net/jsonp_test.js', ['goog.net.JsonpTest'], ['goog.net.Jsonp', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('net/mockiframeio.js', ['goog.net.MockIFrameIo'], ['goog.events.EventTarget', 'goog.json', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.IframeIo'], false); +goog.addDependency('net/multiiframeloadmonitor.js', ['goog.net.MultiIframeLoadMonitor'], ['goog.events', 'goog.net.IframeLoadMonitor'], false); +goog.addDependency('net/multiiframeloadmonitor_test.js', ['goog.net.MultiIframeLoadMonitorTest'], ['goog.dom', 'goog.net.IframeLoadMonitor', 'goog.net.MultiIframeLoadMonitor', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('net/networkstatusmonitor.js', ['goog.net.NetworkStatusMonitor'], ['goog.events.Listenable'], false); +goog.addDependency('net/networktester.js', ['goog.net.NetworkTester'], ['goog.Timer', 'goog.Uri', 'goog.log'], false); +goog.addDependency('net/networktester_test.js', ['goog.net.NetworkTesterTest'], ['goog.Uri', 'goog.net.NetworkTester', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('net/testdata/jsloader_test1.js', ['goog.net.testdata.jsloader_test1'], [], false); +goog.addDependency('net/testdata/jsloader_test2.js', ['goog.net.testdata.jsloader_test2'], [], false); +goog.addDependency('net/testdata/jsloader_test3.js', ['goog.net.testdata.jsloader_test3'], [], false); +goog.addDependency('net/testdata/jsloader_test4.js', ['goog.net.testdata.jsloader_test4'], [], false); +goog.addDependency('net/tmpnetwork.js', ['goog.net.tmpnetwork'], ['goog.Uri', 'goog.net.ChannelDebug'], false); +goog.addDependency('net/websocket.js', ['goog.net.WebSocket', 'goog.net.WebSocket.ErrorEvent', 'goog.net.WebSocket.EventType', 'goog.net.WebSocket.MessageEvent'], ['goog.Timer', 'goog.asserts', 'goog.debug.entryPointRegistry', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.log'], false); +goog.addDependency('net/websocket_test.js', ['goog.net.WebSocketTest'], ['goog.debug.EntryPointMonitor', 'goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.events', 'goog.functions', 'goog.net.WebSocket', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('net/wrapperxmlhttpfactory.js', ['goog.net.WrapperXmlHttpFactory'], ['goog.net.XhrLike', 'goog.net.XmlHttpFactory'], false); +goog.addDependency('net/xhrio.js', ['goog.net.XhrIo', 'goog.net.XhrIo.ResponseType'], ['goog.Timer', 'goog.array', 'goog.debug.entryPointRegistry', 'goog.events.EventTarget', 'goog.json', 'goog.log', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.HttpStatus', 'goog.net.XmlHttp', 'goog.object', 'goog.string', 'goog.structs', 'goog.structs.Map', 'goog.uri.utils', 'goog.userAgent'], false); +goog.addDependency('net/xhrio_test.js', ['goog.net.XhrIoTest'], ['goog.Uri', 'goog.debug.EntryPointMonitor', 'goog.debug.ErrorHandler', 'goog.debug.entryPointRegistry', 'goog.events', 'goog.functions', 'goog.net.EventType', 'goog.net.WrapperXmlHttpFactory', 'goog.net.XhrIo', 'goog.net.XmlHttp', 'goog.object', 'goog.string', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.testing.recordFunction'], false); +goog.addDependency('net/xhriopool.js', ['goog.net.XhrIoPool'], ['goog.net.XhrIo', 'goog.structs.PriorityPool'], false); +goog.addDependency('net/xhrlike.js', ['goog.net.XhrLike'], [], false); +goog.addDependency('net/xhrmanager.js', ['goog.net.XhrManager', 'goog.net.XhrManager.Event', 'goog.net.XhrManager.Request'], ['goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.XhrIo', 'goog.net.XhrIoPool', 'goog.structs.Map'], false); +goog.addDependency('net/xhrmanager_test.js', ['goog.net.XhrManagerTest'], ['goog.events', 'goog.net.EventType', 'goog.net.XhrIo', 'goog.net.XhrManager', 'goog.testing.jsunit', 'goog.testing.net.XhrIoPool', 'goog.testing.recordFunction'], false); +goog.addDependency('net/xmlhttp.js', ['goog.net.DefaultXmlHttpFactory', 'goog.net.XmlHttp', 'goog.net.XmlHttp.OptionType', 'goog.net.XmlHttp.ReadyState', 'goog.net.XmlHttpDefines'], ['goog.asserts', 'goog.net.WrapperXmlHttpFactory', 'goog.net.XmlHttpFactory'], false); +goog.addDependency('net/xmlhttpfactory.js', ['goog.net.XmlHttpFactory'], ['goog.net.XhrLike'], false); +goog.addDependency('net/xpc/crosspagechannel.js', ['goog.net.xpc.CrossPageChannel'], ['goog.Uri', 'goog.async.Deferred', 'goog.async.Delay', 'goog.dispose', 'goog.dom', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.json', 'goog.log', 'goog.messaging.AbstractChannel', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.ChannelStates', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.DirectTransport', 'goog.net.xpc.FrameElementMethodTransport', 'goog.net.xpc.IframePollingTransport', 'goog.net.xpc.IframeRelayTransport', 'goog.net.xpc.NativeMessagingTransport', 'goog.net.xpc.NixTransport', 'goog.net.xpc.TransportTypes', 'goog.net.xpc.UriCfgFields', 'goog.string', 'goog.uri.utils', 'goog.userAgent'], false); +goog.addDependency('net/xpc/crosspagechannel_test.js', ['goog.net.xpc.CrossPageChannelTest'], ['goog.Disposable', 'goog.Uri', 'goog.async.Deferred', 'goog.dom', 'goog.labs.userAgent.browser', 'goog.log', 'goog.log.Level', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannel', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.TransportTypes', 'goog.object', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('net/xpc/crosspagechannelrole.js', ['goog.net.xpc.CrossPageChannelRole'], [], false); +goog.addDependency('net/xpc/directtransport.js', ['goog.net.xpc.DirectTransport'], ['goog.Timer', 'goog.async.Deferred', 'goog.events.EventHandler', 'goog.log', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes', 'goog.object'], false); +goog.addDependency('net/xpc/directtransport_test.js', ['goog.net.xpc.DirectTransportTest'], ['goog.dom', 'goog.labs.userAgent.browser', 'goog.log', 'goog.log.Level', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannel', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.TransportTypes', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('net/xpc/frameelementmethodtransport.js', ['goog.net.xpc.FrameElementMethodTransport'], ['goog.log', 'goog.net.xpc', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes'], false); +goog.addDependency('net/xpc/iframepollingtransport.js', ['goog.net.xpc.IframePollingTransport', 'goog.net.xpc.IframePollingTransport.Receiver', 'goog.net.xpc.IframePollingTransport.Sender'], ['goog.array', 'goog.dom', 'goog.log', 'goog.log.Level', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes', 'goog.userAgent'], false); +goog.addDependency('net/xpc/iframepollingtransport_test.js', ['goog.net.xpc.IframePollingTransportTest'], ['goog.Timer', 'goog.dom', 'goog.dom.TagName', 'goog.functions', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannel', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.TransportTypes', 'goog.object', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('net/xpc/iframerelaytransport.js', ['goog.net.xpc.IframeRelayTransport'], ['goog.dom', 'goog.dom.safe', 'goog.events', 'goog.html.SafeHtml', 'goog.log', 'goog.log.Level', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes', 'goog.string', 'goog.string.Const', 'goog.userAgent'], false); +goog.addDependency('net/xpc/nativemessagingtransport.js', ['goog.net.xpc.NativeMessagingTransport'], ['goog.Timer', 'goog.asserts', 'goog.async.Deferred', 'goog.events', 'goog.events.EventHandler', 'goog.log', 'goog.net.xpc', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes'], false); +goog.addDependency('net/xpc/nativemessagingtransport_test.js', ['goog.net.xpc.NativeMessagingTransportTest'], ['goog.dom', 'goog.events', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannel', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.NativeMessagingTransport', 'goog.testing.jsunit'], false); +goog.addDependency('net/xpc/nixtransport.js', ['goog.net.xpc.NixTransport'], ['goog.log', 'goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.CrossPageChannelRole', 'goog.net.xpc.Transport', 'goog.net.xpc.TransportTypes', 'goog.reflect'], false); +goog.addDependency('net/xpc/relay.js', ['goog.net.xpc.relay'], [], false); +goog.addDependency('net/xpc/transport.js', ['goog.net.xpc.Transport'], ['goog.Disposable', 'goog.dom', 'goog.net.xpc.TransportNames'], false); +goog.addDependency('net/xpc/xpc.js', ['goog.net.xpc', 'goog.net.xpc.CfgFields', 'goog.net.xpc.ChannelStates', 'goog.net.xpc.TransportNames', 'goog.net.xpc.TransportTypes', 'goog.net.xpc.UriCfgFields'], ['goog.log'], false); +goog.addDependency('object/object.js', ['goog.object'], [], false); +goog.addDependency('object/object_test.js', ['goog.objectTest'], ['goog.functions', 'goog.object', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('positioning/absoluteposition.js', ['goog.positioning.AbsolutePosition'], ['goog.math.Coordinate', 'goog.positioning', 'goog.positioning.AbstractPosition'], false); +goog.addDependency('positioning/abstractposition.js', ['goog.positioning.AbstractPosition'], [], false); +goog.addDependency('positioning/anchoredposition.js', ['goog.positioning.AnchoredPosition'], ['goog.positioning', 'goog.positioning.AbstractPosition'], false); +goog.addDependency('positioning/anchoredposition_test.js', ['goog.positioning.AnchoredPositionTest'], ['goog.dom', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.style', 'goog.testing.jsunit'], false); +goog.addDependency('positioning/anchoredviewportposition.js', ['goog.positioning.AnchoredViewportPosition'], ['goog.positioning', 'goog.positioning.AnchoredPosition', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus'], false); +goog.addDependency('positioning/anchoredviewportposition_test.js', ['goog.positioning.AnchoredViewportPositionTest'], ['goog.dom', 'goog.math.Box', 'goog.positioning.AnchoredViewportPosition', 'goog.positioning.Corner', 'goog.positioning.OverflowStatus', 'goog.style', 'goog.testing.jsunit'], false); +goog.addDependency('positioning/clientposition.js', ['goog.positioning.ClientPosition'], ['goog.asserts', 'goog.dom', 'goog.math.Coordinate', 'goog.positioning', 'goog.positioning.AbstractPosition', 'goog.style'], false); +goog.addDependency('positioning/clientposition_test.js', ['goog.positioning.clientPositionTest'], ['goog.dom', 'goog.positioning.ClientPosition', 'goog.positioning.Corner', 'goog.style', 'goog.testing.jsunit'], false); +goog.addDependency('positioning/menuanchoredposition.js', ['goog.positioning.MenuAnchoredPosition'], ['goog.positioning.AnchoredViewportPosition', 'goog.positioning.Overflow'], false); +goog.addDependency('positioning/menuanchoredposition_test.js', ['goog.positioning.MenuAnchoredPositionTest'], ['goog.dom', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.testing.jsunit'], false); +goog.addDependency('positioning/positioning.js', ['goog.positioning', 'goog.positioning.Corner', 'goog.positioning.CornerBit', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus'], ['goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.math.Coordinate', 'goog.math.Rect', 'goog.math.Size', 'goog.style', 'goog.style.bidi'], false); +goog.addDependency('positioning/positioning_test.js', ['goog.positioningTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.math.Box', 'goog.math.Coordinate', 'goog.math.Size', 'goog.positioning', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('positioning/viewportclientposition.js', ['goog.positioning.ViewportClientPosition'], ['goog.dom', 'goog.math.Coordinate', 'goog.positioning', 'goog.positioning.ClientPosition', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus', 'goog.style'], false); +goog.addDependency('positioning/viewportclientposition_test.js', ['goog.positioning.ViewportClientPositionTest'], ['goog.dom', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.ViewportClientPosition', 'goog.style', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('positioning/viewportposition.js', ['goog.positioning.ViewportPosition'], ['goog.math.Coordinate', 'goog.positioning', 'goog.positioning.AbstractPosition', 'goog.positioning.Corner', 'goog.style'], false); +goog.addDependency('promise/promise.js', ['goog.Promise'], ['goog.Thenable', 'goog.asserts', 'goog.async.run', 'goog.async.throwException', 'goog.debug.Error', 'goog.promise.Resolver'], false); +goog.addDependency('promise/promise_test.js', ['goog.PromiseTest'], ['goog.Promise', 'goog.Thenable', 'goog.functions', 'goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('promise/resolver.js', ['goog.promise.Resolver'], [], false); +goog.addDependency('promise/testsuiteadapter.js', ['goog.promise.testSuiteAdapter'], ['goog.Promise'], false); +goog.addDependency('promise/thenable.js', ['goog.Thenable'], [], false); +goog.addDependency('proto/proto.js', ['goog.proto'], ['goog.proto.Serializer'], false); +goog.addDependency('proto/serializer.js', ['goog.proto.Serializer'], ['goog.json.Serializer', 'goog.string'], false); +goog.addDependency('proto/serializer_test.js', ['goog.protoTest'], ['goog.proto', 'goog.testing.jsunit'], false); +goog.addDependency('proto2/descriptor.js', ['goog.proto2.Descriptor', 'goog.proto2.Metadata'], ['goog.array', 'goog.asserts', 'goog.object', 'goog.string'], false); +goog.addDependency('proto2/descriptor_test.js', ['goog.proto2.DescriptorTest'], ['goog.proto2.Descriptor', 'goog.proto2.Message', 'goog.testing.jsunit'], false); +goog.addDependency('proto2/fielddescriptor.js', ['goog.proto2.FieldDescriptor'], ['goog.asserts', 'goog.string'], false); +goog.addDependency('proto2/fielddescriptor_test.js', ['goog.proto2.FieldDescriptorTest'], ['goog.proto2.FieldDescriptor', 'goog.proto2.Message', 'goog.testing.jsunit'], false); +goog.addDependency('proto2/lazydeserializer.js', ['goog.proto2.LazyDeserializer'], ['goog.asserts', 'goog.proto2.Message', 'goog.proto2.Serializer'], false); +goog.addDependency('proto2/message.js', ['goog.proto2.Message'], ['goog.asserts', 'goog.proto2.Descriptor', 'goog.proto2.FieldDescriptor'], false); +goog.addDependency('proto2/message_test.js', ['goog.proto2.MessageTest'], ['goog.testing.jsunit', 'proto2.TestAllTypes', 'proto2.TestAllTypes.NestedEnum', 'proto2.TestAllTypes.NestedMessage', 'proto2.TestAllTypes.OptionalGroup', 'proto2.TestAllTypes.RepeatedGroup'], false); +goog.addDependency('proto2/objectserializer.js', ['goog.proto2.ObjectSerializer'], ['goog.asserts', 'goog.proto2.FieldDescriptor', 'goog.proto2.Serializer', 'goog.string'], false); +goog.addDependency('proto2/objectserializer_test.js', ['goog.proto2.ObjectSerializerTest'], ['goog.proto2.ObjectSerializer', 'goog.proto2.Serializer', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'proto2.TestAllTypes'], false); +goog.addDependency('proto2/package_test.pb.js', ['someprotopackage.TestPackageTypes'], ['goog.proto2.Message', 'proto2.TestAllTypes'], false); +goog.addDependency('proto2/pbliteserializer.js', ['goog.proto2.PbLiteSerializer'], ['goog.asserts', 'goog.proto2.FieldDescriptor', 'goog.proto2.LazyDeserializer', 'goog.proto2.Serializer'], false); +goog.addDependency('proto2/pbliteserializer_test.js', ['goog.proto2.PbLiteSerializerTest'], ['goog.proto2.PbLiteSerializer', 'goog.testing.jsunit', 'proto2.TestAllTypes'], false); +goog.addDependency('proto2/proto_test.js', ['goog.proto2.messageTest'], ['goog.proto2.FieldDescriptor', 'goog.testing.jsunit', 'proto2.TestAllTypes', 'someprotopackage.TestPackageTypes'], false); +goog.addDependency('proto2/serializer.js', ['goog.proto2.Serializer'], ['goog.asserts', 'goog.proto2.FieldDescriptor', 'goog.proto2.Message'], false); +goog.addDependency('proto2/test.pb.js', ['proto2.TestAllTypes', 'proto2.TestAllTypes.NestedEnum', 'proto2.TestAllTypes.NestedMessage', 'proto2.TestAllTypes.OptionalGroup', 'proto2.TestAllTypes.RepeatedGroup', 'proto2.TestDefaultChild', 'proto2.TestDefaultParent'], ['goog.proto2.Message'], false); +goog.addDependency('proto2/textformatserializer.js', ['goog.proto2.TextFormatSerializer'], ['goog.array', 'goog.asserts', 'goog.json', 'goog.math', 'goog.object', 'goog.proto2.FieldDescriptor', 'goog.proto2.Message', 'goog.proto2.Serializer', 'goog.string'], false); +goog.addDependency('proto2/textformatserializer_test.js', ['goog.proto2.TextFormatSerializerTest'], ['goog.proto2.ObjectSerializer', 'goog.proto2.TextFormatSerializer', 'goog.testing.jsunit', 'proto2.TestAllTypes'], false); +goog.addDependency('proto2/util.js', ['goog.proto2.Util'], ['goog.asserts'], false); +goog.addDependency('pubsub/pubsub.js', ['goog.pubsub.PubSub'], ['goog.Disposable', 'goog.array'], false); +goog.addDependency('pubsub/pubsub_test.js', ['goog.pubsub.PubSubTest'], ['goog.array', 'goog.pubsub.PubSub', 'goog.testing.jsunit'], false); +goog.addDependency('pubsub/topicid.js', ['goog.pubsub.TopicId'], [], false); +goog.addDependency('pubsub/typedpubsub.js', ['goog.pubsub.TypedPubSub'], ['goog.Disposable', 'goog.pubsub.PubSub'], false); +goog.addDependency('pubsub/typedpubsub_test.js', ['goog.pubsub.TypedPubSubTest'], ['goog.array', 'goog.pubsub.TopicId', 'goog.pubsub.TypedPubSub', 'goog.testing.jsunit'], false); +goog.addDependency('reflect/reflect.js', ['goog.reflect'], [], false); +goog.addDependency('result/deferredadaptor.js', ['goog.result.DeferredAdaptor'], ['goog.async.Deferred', 'goog.result', 'goog.result.Result'], false); +goog.addDependency('result/dependentresult.js', ['goog.result.DependentResult'], ['goog.result.Result'], false); +goog.addDependency('result/result_interface.js', ['goog.result.Result'], ['goog.Thenable'], false); +goog.addDependency('result/resultutil.js', ['goog.result'], ['goog.array', 'goog.result.DependentResult', 'goog.result.Result', 'goog.result.SimpleResult'], false); +goog.addDependency('result/simpleresult.js', ['goog.result.SimpleResult', 'goog.result.SimpleResult.StateError'], ['goog.Promise', 'goog.Thenable', 'goog.debug.Error', 'goog.result.Result'], false); +goog.addDependency('soy/data.js', ['goog.soy.data.SanitizedContent', 'goog.soy.data.SanitizedContentKind'], ['goog.html.SafeHtml', 'goog.html.uncheckedconversions', 'goog.string.Const'], false); +goog.addDependency('soy/data_test.js', ['goog.soy.dataTest'], ['goog.html.SafeHtml', 'goog.soy.testHelper', 'goog.testing.jsunit'], false); +goog.addDependency('soy/renderer.js', ['goog.soy.InjectedDataSupplier', 'goog.soy.Renderer'], ['goog.asserts', 'goog.dom', 'goog.soy', 'goog.soy.data.SanitizedContent', 'goog.soy.data.SanitizedContentKind'], false); +goog.addDependency('soy/renderer_test.js', ['goog.soy.RendererTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.html.SafeHtml', 'goog.i18n.bidi.Dir', 'goog.soy.Renderer', 'goog.soy.data.SanitizedContentKind', 'goog.soy.testHelper', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('soy/soy.js', ['goog.soy'], ['goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.soy.data.SanitizedContent', 'goog.soy.data.SanitizedContentKind', 'goog.string'], false); +goog.addDependency('soy/soy_test.js', ['goog.soyTest'], ['goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.functions', 'goog.soy', 'goog.soy.testHelper', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('soy/soy_testhelper.js', ['goog.soy.testHelper'], ['goog.dom', 'goog.dom.TagName', 'goog.i18n.bidi.Dir', 'goog.soy.data.SanitizedContent', 'goog.soy.data.SanitizedContentKind', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('spell/spellcheck.js', ['goog.spell.SpellCheck', 'goog.spell.SpellCheck.WordChangedEvent'], ['goog.Timer', 'goog.events.Event', 'goog.events.EventTarget', 'goog.structs.Set'], false); +goog.addDependency('spell/spellcheck_test.js', ['goog.spell.SpellCheckTest'], ['goog.spell.SpellCheck', 'goog.testing.jsunit'], false); +goog.addDependency('stats/basicstat.js', ['goog.stats.BasicStat'], ['goog.asserts', 'goog.log', 'goog.string.format', 'goog.structs.CircularBuffer'], false); +goog.addDependency('stats/basicstat_test.js', ['goog.stats.BasicStatTest'], ['goog.array', 'goog.stats.BasicStat', 'goog.string.format', 'goog.testing.PseudoRandom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('storage/collectablestorage.js', ['goog.storage.CollectableStorage'], ['goog.array', 'goog.iter', 'goog.storage.ErrorCode', 'goog.storage.ExpiringStorage', 'goog.storage.RichStorage'], false); +goog.addDependency('storage/collectablestorage_test.js', ['goog.storage.CollectableStorageTest'], ['goog.storage.CollectableStorage', 'goog.storage.collectableStorageTester', 'goog.storage.storage_test', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.storage.FakeMechanism'], false); +goog.addDependency('storage/collectablestoragetester.js', ['goog.storage.collectableStorageTester'], ['goog.testing.asserts'], false); +goog.addDependency('storage/encryptedstorage.js', ['goog.storage.EncryptedStorage'], ['goog.crypt', 'goog.crypt.Arc4', 'goog.crypt.Sha1', 'goog.crypt.base64', 'goog.json', 'goog.json.Serializer', 'goog.storage.CollectableStorage', 'goog.storage.ErrorCode', 'goog.storage.RichStorage'], false); +goog.addDependency('storage/encryptedstorage_test.js', ['goog.storage.EncryptedStorageTest'], ['goog.json', 'goog.storage.EncryptedStorage', 'goog.storage.ErrorCode', 'goog.storage.RichStorage', 'goog.storage.collectableStorageTester', 'goog.storage.storage_test', 'goog.testing.MockClock', 'goog.testing.PseudoRandom', 'goog.testing.jsunit', 'goog.testing.storage.FakeMechanism'], false); +goog.addDependency('storage/errorcode.js', ['goog.storage.ErrorCode'], [], false); +goog.addDependency('storage/expiringstorage.js', ['goog.storage.ExpiringStorage'], ['goog.storage.RichStorage'], false); +goog.addDependency('storage/expiringstorage_test.js', ['goog.storage.ExpiringStorageTest'], ['goog.storage.ExpiringStorage', 'goog.storage.storage_test', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.testing.storage.FakeMechanism'], false); +goog.addDependency('storage/mechanism/errorcode.js', ['goog.storage.mechanism.ErrorCode'], [], false); +goog.addDependency('storage/mechanism/errorhandlingmechanism.js', ['goog.storage.mechanism.ErrorHandlingMechanism'], ['goog.storage.mechanism.Mechanism'], false); +goog.addDependency('storage/mechanism/errorhandlingmechanism_test.js', ['goog.storage.mechanism.ErrorHandlingMechanismTest'], ['goog.storage.mechanism.ErrorHandlingMechanism', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('storage/mechanism/html5localstorage.js', ['goog.storage.mechanism.HTML5LocalStorage'], ['goog.storage.mechanism.HTML5WebStorage'], false); +goog.addDependency('storage/mechanism/html5localstorage_test.js', ['goog.storage.mechanism.HTML5LocalStorageTest'], ['goog.storage.mechanism.HTML5LocalStorage', 'goog.storage.mechanism.mechanismSeparationTester', 'goog.storage.mechanism.mechanismSharingTester', 'goog.storage.mechanism.mechanismTestDefinition', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('storage/mechanism/html5sessionstorage.js', ['goog.storage.mechanism.HTML5SessionStorage'], ['goog.storage.mechanism.HTML5WebStorage'], false); +goog.addDependency('storage/mechanism/html5sessionstorage_test.js', ['goog.storage.mechanism.HTML5SessionStorageTest'], ['goog.storage.mechanism.HTML5SessionStorage', 'goog.storage.mechanism.mechanismSeparationTester', 'goog.storage.mechanism.mechanismSharingTester', 'goog.storage.mechanism.mechanismTestDefinition', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('storage/mechanism/html5webstorage.js', ['goog.storage.mechanism.HTML5WebStorage'], ['goog.asserts', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.storage.mechanism.ErrorCode', 'goog.storage.mechanism.IterableMechanism'], false); +goog.addDependency('storage/mechanism/html5webstorage_test.js', ['goog.storage.mechanism.HTML5MockStorage', 'goog.storage.mechanism.HTML5WebStorageTest', 'goog.storage.mechanism.MockThrowableStorage'], ['goog.storage.mechanism.ErrorCode', 'goog.storage.mechanism.HTML5WebStorage', 'goog.testing.jsunit'], false); +goog.addDependency('storage/mechanism/ieuserdata.js', ['goog.storage.mechanism.IEUserData'], ['goog.asserts', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.storage.mechanism.ErrorCode', 'goog.storage.mechanism.IterableMechanism', 'goog.structs.Map', 'goog.userAgent'], false); +goog.addDependency('storage/mechanism/ieuserdata_test.js', ['goog.storage.mechanism.IEUserDataTest'], ['goog.storage.mechanism.IEUserData', 'goog.storage.mechanism.mechanismSeparationTester', 'goog.storage.mechanism.mechanismSharingTester', 'goog.storage.mechanism.mechanismTestDefinition', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('storage/mechanism/iterablemechanism.js', ['goog.storage.mechanism.IterableMechanism'], ['goog.array', 'goog.asserts', 'goog.iter', 'goog.storage.mechanism.Mechanism'], false); +goog.addDependency('storage/mechanism/iterablemechanismtester.js', ['goog.storage.mechanism.iterableMechanismTester'], ['goog.iter.Iterator', 'goog.storage.mechanism.IterableMechanism', 'goog.testing.asserts'], false); +goog.addDependency('storage/mechanism/mechanism.js', ['goog.storage.mechanism.Mechanism'], [], false); +goog.addDependency('storage/mechanism/mechanismfactory.js', ['goog.storage.mechanism.mechanismfactory'], ['goog.storage.mechanism.HTML5LocalStorage', 'goog.storage.mechanism.HTML5SessionStorage', 'goog.storage.mechanism.IEUserData', 'goog.storage.mechanism.PrefixedMechanism'], false); +goog.addDependency('storage/mechanism/mechanismfactory_test.js', ['goog.storage.mechanism.mechanismfactoryTest'], ['goog.storage.mechanism.mechanismfactory', 'goog.testing.jsunit'], false); +goog.addDependency('storage/mechanism/mechanismseparationtester.js', ['goog.storage.mechanism.mechanismSeparationTester'], ['goog.iter.StopIteration', 'goog.storage.mechanism.mechanismTestDefinition', 'goog.testing.asserts'], false); +goog.addDependency('storage/mechanism/mechanismsharingtester.js', ['goog.storage.mechanism.mechanismSharingTester'], ['goog.iter.StopIteration', 'goog.storage.mechanism.mechanismTestDefinition', 'goog.testing.asserts'], false); +goog.addDependency('storage/mechanism/mechanismtestdefinition.js', ['goog.storage.mechanism.mechanismTestDefinition'], [], false); +goog.addDependency('storage/mechanism/mechanismtester.js', ['goog.storage.mechanism.mechanismTester'], ['goog.storage.mechanism.ErrorCode', 'goog.testing.asserts', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('storage/mechanism/prefixedmechanism.js', ['goog.storage.mechanism.PrefixedMechanism'], ['goog.iter.Iterator', 'goog.storage.mechanism.IterableMechanism'], false); +goog.addDependency('storage/mechanism/prefixedmechanism_test.js', ['goog.storage.mechanism.PrefixedMechanismTest'], ['goog.storage.mechanism.HTML5LocalStorage', 'goog.storage.mechanism.PrefixedMechanism', 'goog.storage.mechanism.mechanismSeparationTester', 'goog.storage.mechanism.mechanismSharingTester', 'goog.testing.jsunit'], false); +goog.addDependency('storage/richstorage.js', ['goog.storage.RichStorage', 'goog.storage.RichStorage.Wrapper'], ['goog.storage.ErrorCode', 'goog.storage.Storage'], false); +goog.addDependency('storage/richstorage_test.js', ['goog.storage.RichStorageTest'], ['goog.storage.ErrorCode', 'goog.storage.RichStorage', 'goog.storage.storage_test', 'goog.testing.jsunit', 'goog.testing.storage.FakeMechanism'], false); +goog.addDependency('storage/storage.js', ['goog.storage.Storage'], ['goog.json', 'goog.storage.ErrorCode'], false); +goog.addDependency('storage/storage_test.js', ['goog.storage.storage_test'], ['goog.structs.Map', 'goog.testing.asserts'], false); +goog.addDependency('string/const.js', ['goog.string.Const'], ['goog.asserts', 'goog.string.TypedString'], false); +goog.addDependency('string/const_test.js', ['goog.string.constTest'], ['goog.string.Const', 'goog.testing.jsunit'], false); +goog.addDependency('string/linkify.js', ['goog.string.linkify'], ['goog.string'], false); +goog.addDependency('string/linkify_test.js', ['goog.string.linkifyTest'], ['goog.string', 'goog.string.linkify', 'goog.testing.dom', 'goog.testing.jsunit'], false); +goog.addDependency('string/newlines.js', ['goog.string.newlines', 'goog.string.newlines.Line'], ['goog.array'], false); +goog.addDependency('string/newlines_test.js', ['goog.string.newlinesTest'], ['goog.string.newlines', 'goog.testing.jsunit'], false); +goog.addDependency('string/parser.js', ['goog.string.Parser'], [], false); +goog.addDependency('string/path.js', ['goog.string.path'], ['goog.array', 'goog.string'], false); +goog.addDependency('string/path_test.js', ['goog.string.pathTest'], ['goog.string.path', 'goog.testing.jsunit'], false); +goog.addDependency('string/string.js', ['goog.string', 'goog.string.Unicode'], [], false); +goog.addDependency('string/string_test.js', ['goog.stringTest'], ['goog.functions', 'goog.object', 'goog.string', 'goog.string.Unicode', 'goog.testing.MockControl', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit'], false); +goog.addDependency('string/stringbuffer.js', ['goog.string.StringBuffer'], [], false); +goog.addDependency('string/stringbuffer_test.js', ['goog.string.StringBufferTest'], ['goog.string.StringBuffer', 'goog.testing.jsunit'], false); +goog.addDependency('string/stringformat.js', ['goog.string.format'], ['goog.string'], false); +goog.addDependency('string/stringformat_test.js', ['goog.string.formatTest'], ['goog.string.format', 'goog.testing.jsunit'], false); +goog.addDependency('string/stringifier.js', ['goog.string.Stringifier'], [], false); +goog.addDependency('string/typedstring.js', ['goog.string.TypedString'], [], false); +goog.addDependency('structs/avltree.js', ['goog.structs.AvlTree', 'goog.structs.AvlTree.Node'], ['goog.structs.Collection'], false); +goog.addDependency('structs/avltree_test.js', ['goog.structs.AvlTreeTest'], ['goog.array', 'goog.structs.AvlTree', 'goog.testing.jsunit'], false); +goog.addDependency('structs/circularbuffer.js', ['goog.structs.CircularBuffer'], [], false); +goog.addDependency('structs/circularbuffer_test.js', ['goog.structs.CircularBufferTest'], ['goog.structs.CircularBuffer', 'goog.testing.jsunit'], false); +goog.addDependency('structs/collection.js', ['goog.structs.Collection'], [], false); +goog.addDependency('structs/collection_test.js', ['goog.structs.CollectionTest'], ['goog.structs.AvlTree', 'goog.structs.Set', 'goog.testing.jsunit'], false); +goog.addDependency('structs/heap.js', ['goog.structs.Heap'], ['goog.array', 'goog.object', 'goog.structs.Node'], false); +goog.addDependency('structs/heap_test.js', ['goog.structs.HeapTest'], ['goog.structs', 'goog.structs.Heap', 'goog.testing.jsunit'], false); +goog.addDependency('structs/inversionmap.js', ['goog.structs.InversionMap'], ['goog.array'], false); +goog.addDependency('structs/inversionmap_test.js', ['goog.structs.InversionMapTest'], ['goog.structs.InversionMap', 'goog.testing.jsunit'], false); +goog.addDependency('structs/linkedmap.js', ['goog.structs.LinkedMap'], ['goog.structs.Map'], false); +goog.addDependency('structs/linkedmap_test.js', ['goog.structs.LinkedMapTest'], ['goog.structs.LinkedMap', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('structs/map.js', ['goog.structs.Map'], ['goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.object'], false); +goog.addDependency('structs/map_test.js', ['goog.structs.MapTest'], ['goog.iter', 'goog.structs', 'goog.structs.Map', 'goog.testing.jsunit'], false); +goog.addDependency('structs/node.js', ['goog.structs.Node'], [], false); +goog.addDependency('structs/pool.js', ['goog.structs.Pool'], ['goog.Disposable', 'goog.structs.Queue', 'goog.structs.Set'], false); +goog.addDependency('structs/pool_test.js', ['goog.structs.PoolTest'], ['goog.structs.Pool', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('structs/prioritypool.js', ['goog.structs.PriorityPool'], ['goog.structs.Pool', 'goog.structs.PriorityQueue'], false); +goog.addDependency('structs/prioritypool_test.js', ['goog.structs.PriorityPoolTest'], ['goog.structs.PriorityPool', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('structs/priorityqueue.js', ['goog.structs.PriorityQueue'], ['goog.structs.Heap'], false); +goog.addDependency('structs/priorityqueue_test.js', ['goog.structs.PriorityQueueTest'], ['goog.structs', 'goog.structs.PriorityQueue', 'goog.testing.jsunit'], false); +goog.addDependency('structs/quadtree.js', ['goog.structs.QuadTree', 'goog.structs.QuadTree.Node', 'goog.structs.QuadTree.Point'], ['goog.math.Coordinate'], false); +goog.addDependency('structs/quadtree_test.js', ['goog.structs.QuadTreeTest'], ['goog.structs', 'goog.structs.QuadTree', 'goog.testing.jsunit'], false); +goog.addDependency('structs/queue.js', ['goog.structs.Queue'], ['goog.array'], false); +goog.addDependency('structs/queue_test.js', ['goog.structs.QueueTest'], ['goog.structs.Queue', 'goog.testing.jsunit'], false); +goog.addDependency('structs/set.js', ['goog.structs.Set'], ['goog.structs', 'goog.structs.Collection', 'goog.structs.Map'], false); +goog.addDependency('structs/set_test.js', ['goog.structs.SetTest'], ['goog.iter', 'goog.structs', 'goog.structs.Set', 'goog.testing.jsunit'], false); +goog.addDependency('structs/simplepool.js', ['goog.structs.SimplePool'], ['goog.Disposable'], false); +goog.addDependency('structs/stringset.js', ['goog.structs.StringSet'], ['goog.asserts', 'goog.iter'], false); +goog.addDependency('structs/stringset_test.js', ['goog.structs.StringSetTest'], ['goog.array', 'goog.iter', 'goog.structs.StringSet', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('structs/structs.js', ['goog.structs'], ['goog.array', 'goog.object'], false); +goog.addDependency('structs/structs_test.js', ['goog.structsTest'], ['goog.array', 'goog.structs', 'goog.structs.Map', 'goog.structs.Set', 'goog.testing.jsunit'], false); +goog.addDependency('structs/treenode.js', ['goog.structs.TreeNode'], ['goog.array', 'goog.asserts', 'goog.structs.Node'], false); +goog.addDependency('structs/treenode_test.js', ['goog.structs.TreeNodeTest'], ['goog.structs.TreeNode', 'goog.testing.jsunit'], false); +goog.addDependency('structs/trie.js', ['goog.structs.Trie'], ['goog.object', 'goog.structs'], false); +goog.addDependency('structs/trie_test.js', ['goog.structs.TrieTest'], ['goog.object', 'goog.structs', 'goog.structs.Trie', 'goog.testing.jsunit'], false); +goog.addDependency('structs/weak/weak.js', ['goog.structs.weak'], ['goog.userAgent'], false); +goog.addDependency('structs/weak/weak_test.js', ['goog.structs.weakTest'], ['goog.array', 'goog.structs.weak', 'goog.testing.jsunit'], false); +goog.addDependency('style/bidi.js', ['goog.style.bidi'], ['goog.dom', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('style/bidi_test.js', ['goog.style.bidiTest'], ['goog.dom', 'goog.style', 'goog.style.bidi', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('style/cursor.js', ['goog.style.cursor'], ['goog.userAgent'], false); +goog.addDependency('style/cursor_test.js', ['goog.style.cursorTest'], ['goog.style.cursor', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('style/style.js', ['goog.style'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.vendor', 'goog.math.Box', 'goog.math.Coordinate', 'goog.math.Rect', 'goog.math.Size', 'goog.object', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('style/style_document_scroll_test.js', ['goog.style.style_document_scroll_test'], ['goog.dom', 'goog.style', 'goog.testing.jsunit'], false); +goog.addDependency('style/style_test.js', ['goog.style_test'], ['goog.array', 'goog.color', 'goog.dom', 'goog.events.BrowserEvent', 'goog.labs.userAgent.util', 'goog.math.Box', 'goog.math.Coordinate', 'goog.math.Rect', 'goog.math.Size', 'goog.object', 'goog.string', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.MockUserAgent', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgentTestUtil', 'goog.userAgentTestUtil.UserAgents'], false); +goog.addDependency('style/style_webkit_scrollbars_test.js', ['goog.style.webkitScrollbarsTest'], ['goog.asserts', 'goog.style', 'goog.styleScrollbarTester', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('style/stylescrollbartester.js', ['goog.styleScrollbarTester'], ['goog.dom', 'goog.style', 'goog.testing.asserts'], false); +goog.addDependency('style/transform.js', ['goog.style.transform'], ['goog.functions', 'goog.math.Coordinate', 'goog.math.Coordinate3', 'goog.style', 'goog.userAgent', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('style/transform_test.js', ['goog.style.transformTest'], ['goog.dom', 'goog.style.transform', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('style/transition.js', ['goog.style.transition', 'goog.style.transition.Css3Property'], ['goog.array', 'goog.asserts', 'goog.dom.safe', 'goog.dom.vendor', 'goog.functions', 'goog.html.SafeHtml', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('style/transition_test.js', ['goog.style.transitionTest'], ['goog.style', 'goog.style.transition', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('test_module.js', ['goog.test_module'], ['goog.test_module_dep'], true); +goog.addDependency('test_module_dep.js', ['goog.test_module_dep'], [], true); +goog.addDependency('testing/asserts.js', ['goog.testing.JsUnitException', 'goog.testing.asserts', 'goog.testing.asserts.ArrayLike'], ['goog.testing.stacktrace'], false); +goog.addDependency('testing/asserts_test.js', ['goog.testing.assertsTest'], ['goog.array', 'goog.dom', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.labs.userAgent.browser', 'goog.string', 'goog.structs.Map', 'goog.structs.Set', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('testing/async/mockcontrol.js', ['goog.testing.async.MockControl'], ['goog.asserts', 'goog.async.Deferred', 'goog.debug', 'goog.testing.asserts', 'goog.testing.mockmatchers.IgnoreArgument'], false); +goog.addDependency('testing/async/mockcontrol_test.js', ['goog.testing.async.MockControlTest'], ['goog.async.Deferred', 'goog.testing.MockControl', 'goog.testing.asserts', 'goog.testing.async.MockControl', 'goog.testing.jsunit'], false); +goog.addDependency('testing/asynctestcase.js', ['goog.testing.AsyncTestCase', 'goog.testing.AsyncTestCase.ControlBreakingException'], ['goog.testing.TestCase', 'goog.testing.asserts'], false); +goog.addDependency('testing/asynctestcase_async_test.js', ['goog.testing.AsyncTestCaseAsyncTest'], ['goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('testing/asynctestcase_noasync_test.js', ['goog.testing.AsyncTestCaseSyncTest'], ['goog.testing.AsyncTestCase', 'goog.testing.jsunit'], false); +goog.addDependency('testing/asynctestcase_test.js', ['goog.testing.AsyncTestCaseTest'], ['goog.debug.Error', 'goog.testing.AsyncTestCase', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('testing/benchmark.js', ['goog.testing.benchmark'], ['goog.dom', 'goog.dom.TagName', 'goog.testing.PerformanceTable', 'goog.testing.PerformanceTimer', 'goog.testing.TestCase'], false); +goog.addDependency('testing/continuationtestcase.js', ['goog.testing.ContinuationTestCase', 'goog.testing.ContinuationTestCase.Step', 'goog.testing.ContinuationTestCase.Test'], ['goog.array', 'goog.events.EventHandler', 'goog.testing.TestCase', 'goog.testing.asserts'], false); +goog.addDependency('testing/continuationtestcase_test.js', ['goog.testing.ContinuationTestCaseTest'], ['goog.events', 'goog.events.EventTarget', 'goog.testing.ContinuationTestCase', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.TestCase', 'goog.testing.jsunit'], false); +goog.addDependency('testing/deferredtestcase.js', ['goog.testing.DeferredTestCase'], ['goog.testing.AsyncTestCase', 'goog.testing.TestCase'], false); +goog.addDependency('testing/deferredtestcase_test.js', ['goog.testing.DeferredTestCaseTest'], ['goog.async.Deferred', 'goog.testing.DeferredTestCase', 'goog.testing.TestCase', 'goog.testing.TestRunner', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('testing/dom.js', ['goog.testing.dom'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeIterator', 'goog.dom.NodeType', 'goog.dom.TagIterator', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.iter', 'goog.object', 'goog.string', 'goog.style', 'goog.testing.asserts', 'goog.userAgent'], false); +goog.addDependency('testing/dom_test.js', ['goog.testing.domTest'], ['goog.dom', 'goog.dom.TagName', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('testing/editor/dom.js', ['goog.testing.editor.dom'], ['goog.dom.NodeType', 'goog.dom.TagIterator', 'goog.dom.TagWalkType', 'goog.iter', 'goog.string', 'goog.testing.asserts'], false); +goog.addDependency('testing/editor/dom_test.js', ['goog.testing.editor.domTest'], ['goog.dom', 'goog.dom.TagName', 'goog.functions', 'goog.testing.editor.dom', 'goog.testing.jsunit'], false); +goog.addDependency('testing/editor/fieldmock.js', ['goog.testing.editor.FieldMock'], ['goog.dom', 'goog.dom.Range', 'goog.editor.Field', 'goog.testing.LooseMock', 'goog.testing.mockmatchers'], false); +goog.addDependency('testing/editor/testhelper.js', ['goog.testing.editor.TestHelper'], ['goog.Disposable', 'goog.dom', 'goog.dom.Range', 'goog.editor.BrowserFeature', 'goog.editor.node', 'goog.editor.plugins.AbstractBubblePlugin', 'goog.testing.dom'], false); +goog.addDependency('testing/editor/testhelper_test.js', ['goog.testing.editor.TestHelperTest'], ['goog.dom', 'goog.dom.TagName', 'goog.editor.node', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('testing/events/eventobserver.js', ['goog.testing.events.EventObserver'], ['goog.array'], false); +goog.addDependency('testing/events/eventobserver_test.js', ['goog.testing.events.EventObserverTest'], ['goog.array', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.testing.events.EventObserver', 'goog.testing.jsunit'], false); +goog.addDependency('testing/events/events.js', ['goog.testing.events', 'goog.testing.events.Event'], ['goog.Disposable', 'goog.asserts', 'goog.dom.NodeType', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.BrowserFeature', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.object', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('testing/events/events_test.js', ['goog.testing.eventsTest'], ['goog.array', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Coordinate', 'goog.string', 'goog.style', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.userAgent'], false); +goog.addDependency('testing/events/matchers.js', ['goog.testing.events.EventMatcher'], ['goog.events.Event', 'goog.testing.mockmatchers.ArgumentMatcher'], false); +goog.addDependency('testing/events/matchers_test.js', ['goog.testing.events.EventMatcherTest'], ['goog.events.Event', 'goog.testing.events.EventMatcher', 'goog.testing.jsunit'], false); +goog.addDependency('testing/events/onlinehandler.js', ['goog.testing.events.OnlineHandler'], ['goog.events.EventTarget', 'goog.net.NetworkStatusMonitor'], false); +goog.addDependency('testing/events/onlinehandler_test.js', ['goog.testing.events.OnlineHandlerTest'], ['goog.events', 'goog.net.NetworkStatusMonitor', 'goog.testing.events.EventObserver', 'goog.testing.events.OnlineHandler', 'goog.testing.jsunit'], false); +goog.addDependency('testing/expectedfailures.js', ['goog.testing.ExpectedFailures'], ['goog.debug.DivConsole', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.EventType', 'goog.log', 'goog.style', 'goog.testing.JsUnitException', 'goog.testing.TestCase', 'goog.testing.asserts'], false); +goog.addDependency('testing/expectedfailures_test.js', ['goog.testing.ExpectedFailuresTest'], ['goog.debug.Logger', 'goog.testing.ExpectedFailures', 'goog.testing.JsUnitException', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/blob.js', ['goog.testing.fs.Blob'], ['goog.crypt.base64'], false); +goog.addDependency('testing/fs/blob_test.js', ['goog.testing.fs.BlobTest'], ['goog.testing.fs.Blob', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/directoryentry_test.js', ['goog.testing.fs.DirectoryEntryTest'], ['goog.array', 'goog.fs.DirectoryEntry', 'goog.fs.Error', 'goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/entry.js', ['goog.testing.fs.DirectoryEntry', 'goog.testing.fs.Entry', 'goog.testing.fs.FileEntry'], ['goog.Timer', 'goog.array', 'goog.asserts', 'goog.async.Deferred', 'goog.fs.DirectoryEntry', 'goog.fs.DirectoryEntryImpl', 'goog.fs.Entry', 'goog.fs.Error', 'goog.fs.FileEntry', 'goog.functions', 'goog.object', 'goog.string', 'goog.testing.fs.File', 'goog.testing.fs.FileWriter'], false); +goog.addDependency('testing/fs/entry_test.js', ['goog.testing.fs.EntryTest'], ['goog.fs.DirectoryEntry', 'goog.fs.Error', 'goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/file.js', ['goog.testing.fs.File'], ['goog.testing.fs.Blob'], false); +goog.addDependency('testing/fs/fileentry_test.js', ['goog.testing.fs.FileEntryTest'], ['goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.fs.FileEntry', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/filereader.js', ['goog.testing.fs.FileReader'], ['goog.Timer', 'goog.events.EventTarget', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.testing.fs.ProgressEvent'], false); +goog.addDependency('testing/fs/filereader_test.js', ['goog.testing.fs.FileReaderTest'], ['goog.Timer', 'goog.async.Deferred', 'goog.events', 'goog.fs.Error', 'goog.fs.FileReader', 'goog.fs.FileSaver', 'goog.testing.AsyncTestCase', 'goog.testing.fs.FileReader', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/filesystem.js', ['goog.testing.fs.FileSystem'], ['goog.fs.FileSystem', 'goog.testing.fs.DirectoryEntry'], false); +goog.addDependency('testing/fs/filewriter.js', ['goog.testing.fs.FileWriter'], ['goog.Timer', 'goog.events.EventTarget', 'goog.fs.Error', 'goog.fs.FileSaver', 'goog.string', 'goog.testing.fs.ProgressEvent'], false); +goog.addDependency('testing/fs/filewriter_test.js', ['goog.testing.fs.FileWriterTest'], ['goog.async.Deferred', 'goog.events', 'goog.fs.Error', 'goog.fs.FileSaver', 'goog.testing.AsyncTestCase', 'goog.testing.MockClock', 'goog.testing.fs.Blob', 'goog.testing.fs.FileSystem', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/fs.js', ['goog.testing.fs'], ['goog.Timer', 'goog.array', 'goog.async.Deferred', 'goog.fs', 'goog.testing.fs.Blob', 'goog.testing.fs.FileSystem'], false); +goog.addDependency('testing/fs/fs_test.js', ['goog.testing.fsTest'], ['goog.testing.AsyncTestCase', 'goog.testing.fs', 'goog.testing.fs.Blob', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/integration_test.js', ['goog.testing.fs.integrationTest'], ['goog.async.Deferred', 'goog.async.DeferredList', 'goog.events', 'goog.fs', 'goog.fs.DirectoryEntry', 'goog.fs.Error', 'goog.fs.FileSaver', 'goog.testing.AsyncTestCase', 'goog.testing.PropertyReplacer', 'goog.testing.fs', 'goog.testing.jsunit'], false); +goog.addDependency('testing/fs/progressevent.js', ['goog.testing.fs.ProgressEvent'], ['goog.events.Event'], false); +goog.addDependency('testing/functionmock.js', ['goog.testing', 'goog.testing.FunctionMock', 'goog.testing.GlobalFunctionMock', 'goog.testing.MethodMock'], ['goog.object', 'goog.testing.LooseMock', 'goog.testing.Mock', 'goog.testing.PropertyReplacer', 'goog.testing.StrictMock'], false); +goog.addDependency('testing/functionmock_test.js', ['goog.testing.FunctionMockTest'], ['goog.array', 'goog.string', 'goog.testing', 'goog.testing.FunctionMock', 'goog.testing.Mock', 'goog.testing.StrictMock', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.mockmatchers'], false); +goog.addDependency('testing/graphics.js', ['goog.testing.graphics'], ['goog.graphics.Path', 'goog.testing.asserts'], false); +goog.addDependency('testing/i18n/asserts.js', ['goog.testing.i18n.asserts'], ['goog.testing.jsunit'], false); +goog.addDependency('testing/i18n/asserts_test.js', ['goog.testing.i18n.assertsTest'], ['goog.testing.ExpectedFailures', 'goog.testing.i18n.asserts'], false); +goog.addDependency('testing/jsunit.js', ['goog.testing.jsunit'], ['goog.testing.TestCase', 'goog.testing.TestRunner'], false); +goog.addDependency('testing/loosemock.js', ['goog.testing.LooseExpectationCollection', 'goog.testing.LooseMock'], ['goog.array', 'goog.structs.Map', 'goog.testing.Mock'], false); +goog.addDependency('testing/loosemock_test.js', ['goog.testing.LooseMockTest'], ['goog.testing.LooseMock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.mockmatchers'], false); +goog.addDependency('testing/messaging/mockmessagechannel.js', ['goog.testing.messaging.MockMessageChannel'], ['goog.messaging.AbstractChannel', 'goog.testing.asserts'], false); +goog.addDependency('testing/messaging/mockmessageevent.js', ['goog.testing.messaging.MockMessageEvent'], ['goog.events.BrowserEvent', 'goog.events.EventType', 'goog.testing.events.Event'], false); +goog.addDependency('testing/messaging/mockmessageport.js', ['goog.testing.messaging.MockMessagePort'], ['goog.events.EventTarget'], false); +goog.addDependency('testing/messaging/mockportnetwork.js', ['goog.testing.messaging.MockPortNetwork'], ['goog.messaging.PortNetwork', 'goog.testing.messaging.MockMessageChannel'], false); +goog.addDependency('testing/mock.js', ['goog.testing.Mock', 'goog.testing.MockExpectation'], ['goog.array', 'goog.object', 'goog.testing.JsUnitException', 'goog.testing.MockInterface', 'goog.testing.mockmatchers'], false); +goog.addDependency('testing/mock_test.js', ['goog.testing.MockTest'], ['goog.array', 'goog.testing', 'goog.testing.Mock', 'goog.testing.MockControl', 'goog.testing.MockExpectation', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockclassfactory.js', ['goog.testing.MockClassFactory', 'goog.testing.MockClassRecord'], ['goog.array', 'goog.object', 'goog.testing.LooseMock', 'goog.testing.StrictMock', 'goog.testing.TestCase', 'goog.testing.mockmatchers'], false); +goog.addDependency('testing/mockclassfactory_test.js', ['fake.BaseClass', 'fake.ChildClass', 'goog.testing.MockClassFactoryTest'], ['goog.testing', 'goog.testing.MockClassFactory', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockclock.js', ['goog.testing.MockClock'], ['goog.Disposable', 'goog.async.run', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.watchers'], false); +goog.addDependency('testing/mockclock_test.js', ['goog.testing.MockClockTest'], ['goog.Promise', 'goog.Timer', 'goog.events', 'goog.functions', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordFunction'], false); +goog.addDependency('testing/mockcontrol.js', ['goog.testing.MockControl'], ['goog.array', 'goog.testing', 'goog.testing.LooseMock', 'goog.testing.StrictMock'], false); +goog.addDependency('testing/mockcontrol_test.js', ['goog.testing.MockControlTest'], ['goog.testing.Mock', 'goog.testing.MockControl', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockinterface.js', ['goog.testing.MockInterface'], [], false); +goog.addDependency('testing/mockmatchers.js', ['goog.testing.mockmatchers', 'goog.testing.mockmatchers.ArgumentMatcher', 'goog.testing.mockmatchers.IgnoreArgument', 'goog.testing.mockmatchers.InstanceOf', 'goog.testing.mockmatchers.ObjectEquals', 'goog.testing.mockmatchers.RegexpMatch', 'goog.testing.mockmatchers.SaveArgument', 'goog.testing.mockmatchers.TypeOf'], ['goog.array', 'goog.dom', 'goog.testing.asserts'], false); +goog.addDependency('testing/mockmatchers_test.js', ['goog.testing.mockmatchersTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.testing.mockmatchers.ArgumentMatcher'], false); +goog.addDependency('testing/mockrandom.js', ['goog.testing.MockRandom'], ['goog.Disposable'], false); +goog.addDependency('testing/mockrandom_test.js', ['goog.testing.MockRandomTest'], ['goog.testing.MockRandom', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockrange.js', ['goog.testing.MockRange'], ['goog.dom.AbstractRange', 'goog.testing.LooseMock'], false); +goog.addDependency('testing/mockrange_test.js', ['goog.testing.MockRangeTest'], ['goog.testing.MockRange', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockstorage.js', ['goog.testing.MockStorage'], ['goog.structs.Map'], false); +goog.addDependency('testing/mockstorage_test.js', ['goog.testing.MockStorageTest'], ['goog.testing.MockStorage', 'goog.testing.jsunit'], false); +goog.addDependency('testing/mockuseragent.js', ['goog.testing.MockUserAgent'], ['goog.Disposable', 'goog.labs.userAgent.util', 'goog.testing.PropertyReplacer', 'goog.userAgent'], false); +goog.addDependency('testing/mockuseragent_test.js', ['goog.testing.MockUserAgentTest'], ['goog.dispose', 'goog.testing.MockUserAgent', 'goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('testing/multitestrunner.js', ['goog.testing.MultiTestRunner', 'goog.testing.MultiTestRunner.TestFrame'], ['goog.Timer', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventHandler', 'goog.functions', 'goog.string', 'goog.ui.Component', 'goog.ui.ServerChart', 'goog.ui.TableSorter'], false); +goog.addDependency('testing/net/xhrio.js', ['goog.testing.net.XhrIo'], ['goog.array', 'goog.dom.xml', 'goog.events', 'goog.events.EventTarget', 'goog.json', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.HttpStatus', 'goog.net.XhrIo', 'goog.net.XmlHttp', 'goog.object', 'goog.structs.Map'], false); +goog.addDependency('testing/net/xhrio_test.js', ['goog.testing.net.XhrIoTest'], ['goog.dom.xml', 'goog.events', 'goog.events.Event', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.XmlHttp', 'goog.object', 'goog.testing.MockControl', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.mockmatchers.InstanceOf', 'goog.testing.net.XhrIo'], false); +goog.addDependency('testing/net/xhriopool.js', ['goog.testing.net.XhrIoPool'], ['goog.net.XhrIoPool', 'goog.testing.net.XhrIo'], false); +goog.addDependency('testing/objectpropertystring.js', ['goog.testing.ObjectPropertyString'], [], false); +goog.addDependency('testing/performancetable.js', ['goog.testing.PerformanceTable'], ['goog.dom', 'goog.dom.TagName', 'goog.testing.PerformanceTimer'], false); +goog.addDependency('testing/performancetimer.js', ['goog.testing.PerformanceTimer', 'goog.testing.PerformanceTimer.Task'], ['goog.array', 'goog.async.Deferred', 'goog.math'], false); +goog.addDependency('testing/performancetimer_test.js', ['goog.testing.PerformanceTimerTest'], ['goog.async.Deferred', 'goog.dom', 'goog.math', 'goog.testing.MockClock', 'goog.testing.PerformanceTimer', 'goog.testing.jsunit'], false); +goog.addDependency('testing/propertyreplacer.js', ['goog.testing.PropertyReplacer'], ['goog.testing.ObjectPropertyString', 'goog.userAgent'], false); +goog.addDependency('testing/propertyreplacer_test.js', ['goog.testing.PropertyReplacerTest'], ['goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('testing/proto2/proto2.js', ['goog.testing.proto2'], ['goog.proto2.Message', 'goog.proto2.ObjectSerializer', 'goog.testing.asserts'], false); +goog.addDependency('testing/proto2/proto2_test.js', ['goog.testing.proto2Test'], ['goog.testing.jsunit', 'goog.testing.proto2', 'proto2.TestAllTypes'], false); +goog.addDependency('testing/pseudorandom.js', ['goog.testing.PseudoRandom'], ['goog.Disposable'], false); +goog.addDependency('testing/pseudorandom_test.js', ['goog.testing.PseudoRandomTest'], ['goog.testing.PseudoRandom', 'goog.testing.jsunit'], false); +goog.addDependency('testing/recordfunction.js', ['goog.testing.FunctionCall', 'goog.testing.recordConstructor', 'goog.testing.recordFunction'], ['goog.testing.asserts'], false); +goog.addDependency('testing/recordfunction_test.js', ['goog.testing.recordFunctionTest'], ['goog.functions', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.recordConstructor', 'goog.testing.recordFunction'], false); +goog.addDependency('testing/shardingtestcase.js', ['goog.testing.ShardingTestCase'], ['goog.asserts', 'goog.testing.TestCase'], false); +goog.addDependency('testing/shardingtestcase_test.js', ['goog.testing.ShardingTestCaseTest'], ['goog.testing.ShardingTestCase', 'goog.testing.TestCase', 'goog.testing.asserts', 'goog.testing.jsunit'], false); +goog.addDependency('testing/singleton.js', ['goog.testing.singleton'], [], false); +goog.addDependency('testing/singleton_test.js', ['goog.testing.singletonTest'], ['goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.singleton'], false); +goog.addDependency('testing/stacktrace.js', ['goog.testing.stacktrace', 'goog.testing.stacktrace.Frame'], [], false); +goog.addDependency('testing/stacktrace_test.js', ['goog.testing.stacktraceTest'], ['goog.functions', 'goog.string', 'goog.testing.ExpectedFailures', 'goog.testing.PropertyReplacer', 'goog.testing.StrictMock', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.stacktrace', 'goog.testing.stacktrace.Frame', 'goog.userAgent'], false); +goog.addDependency('testing/storage/fakemechanism.js', ['goog.testing.storage.FakeMechanism'], ['goog.storage.mechanism.IterableMechanism', 'goog.structs.Map'], false); +goog.addDependency('testing/strictmock.js', ['goog.testing.StrictMock'], ['goog.array', 'goog.testing.Mock'], false); +goog.addDependency('testing/strictmock_test.js', ['goog.testing.StrictMockTest'], ['goog.testing.StrictMock', 'goog.testing.jsunit'], false); +goog.addDependency('testing/style/layoutasserts.js', ['goog.testing.style.layoutasserts'], ['goog.style', 'goog.testing.asserts', 'goog.testing.style'], false); +goog.addDependency('testing/style/layoutasserts_test.js', ['goog.testing.style.layoutassertsTest'], ['goog.dom', 'goog.style', 'goog.testing.jsunit', 'goog.testing.style.layoutasserts'], false); +goog.addDependency('testing/style/style.js', ['goog.testing.style'], ['goog.dom', 'goog.math.Rect', 'goog.style'], false); +goog.addDependency('testing/style/style_test.js', ['goog.testing.styleTest'], ['goog.dom', 'goog.style', 'goog.testing.jsunit', 'goog.testing.style'], false); +goog.addDependency('testing/testcase.js', ['goog.testing.TestCase', 'goog.testing.TestCase.Error', 'goog.testing.TestCase.Order', 'goog.testing.TestCase.Result', 'goog.testing.TestCase.Test'], ['goog.Promise', 'goog.Thenable', 'goog.object', 'goog.testing.asserts', 'goog.testing.stacktrace'], false); +goog.addDependency('testing/testcase_test.js', ['goog.testing.TestCaseTest'], ['goog.Promise', 'goog.testing.TestCase', 'goog.testing.jsunit'], false); +goog.addDependency('testing/testqueue.js', ['goog.testing.TestQueue'], [], false); +goog.addDependency('testing/testrunner.js', ['goog.testing.TestRunner'], ['goog.testing.TestCase'], false); +goog.addDependency('testing/ui/rendererasserts.js', ['goog.testing.ui.rendererasserts'], ['goog.testing.asserts', 'goog.ui.ControlRenderer'], false); +goog.addDependency('testing/ui/rendererasserts_test.js', ['goog.testing.ui.rendererassertsTest'], ['goog.testing.asserts', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.ControlRenderer'], false); +goog.addDependency('testing/ui/rendererharness.js', ['goog.testing.ui.RendererHarness'], ['goog.Disposable', 'goog.dom.NodeType', 'goog.testing.asserts', 'goog.testing.dom'], false); +goog.addDependency('testing/ui/style.js', ['goog.testing.ui.style'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.testing.asserts'], false); +goog.addDependency('testing/ui/style_test.js', ['goog.testing.ui.styleTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.style'], false); +goog.addDependency('testing/watchers.js', ['goog.testing.watchers'], [], false); +goog.addDependency('timer/timer.js', ['goog.Timer'], ['goog.Promise', 'goog.events.EventTarget'], false); +goog.addDependency('timer/timer_test.js', ['goog.TimerTest'], ['goog.Promise', 'goog.Timer', 'goog.events', 'goog.testing.MockClock', 'goog.testing.jsunit'], false); +goog.addDependency('tweak/entries.js', ['goog.tweak.BaseEntry', 'goog.tweak.BasePrimitiveSetting', 'goog.tweak.BaseSetting', 'goog.tweak.BooleanGroup', 'goog.tweak.BooleanInGroupSetting', 'goog.tweak.BooleanSetting', 'goog.tweak.ButtonAction', 'goog.tweak.NumericSetting', 'goog.tweak.StringSetting'], ['goog.array', 'goog.asserts', 'goog.log', 'goog.object'], false); +goog.addDependency('tweak/entries_test.js', ['goog.tweak.BaseEntryTest'], ['goog.testing.MockControl', 'goog.testing.jsunit', 'goog.tweak.testhelpers'], false); +goog.addDependency('tweak/registry.js', ['goog.tweak.Registry'], ['goog.array', 'goog.asserts', 'goog.log', 'goog.string', 'goog.tweak.BasePrimitiveSetting', 'goog.tweak.BaseSetting', 'goog.tweak.BooleanSetting', 'goog.tweak.NumericSetting', 'goog.tweak.StringSetting', 'goog.uri.utils'], false); +goog.addDependency('tweak/registry_test.js', ['goog.tweak.RegistryTest'], ['goog.asserts.AssertionError', 'goog.testing.jsunit', 'goog.tweak', 'goog.tweak.testhelpers'], false); +goog.addDependency('tweak/testhelpers.js', ['goog.tweak.testhelpers'], ['goog.tweak', 'goog.tweak.BooleanGroup', 'goog.tweak.BooleanInGroupSetting', 'goog.tweak.BooleanSetting', 'goog.tweak.ButtonAction', 'goog.tweak.NumericSetting', 'goog.tweak.Registry', 'goog.tweak.StringSetting'], false); +goog.addDependency('tweak/tweak.js', ['goog.tweak', 'goog.tweak.ConfigParams'], ['goog.asserts', 'goog.tweak.BaseSetting', 'goog.tweak.BooleanGroup', 'goog.tweak.BooleanInGroupSetting', 'goog.tweak.BooleanSetting', 'goog.tweak.ButtonAction', 'goog.tweak.NumericSetting', 'goog.tweak.Registry', 'goog.tweak.StringSetting'], false); +goog.addDependency('tweak/tweakui.js', ['goog.tweak.EntriesPanel', 'goog.tweak.TweakUi'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.object', 'goog.style', 'goog.tweak', 'goog.tweak.BaseEntry', 'goog.tweak.BooleanGroup', 'goog.tweak.BooleanInGroupSetting', 'goog.tweak.BooleanSetting', 'goog.tweak.ButtonAction', 'goog.tweak.NumericSetting', 'goog.tweak.StringSetting', 'goog.ui.Zippy', 'goog.userAgent'], false); +goog.addDependency('tweak/tweakui_test.js', ['goog.tweak.TweakUiTest'], ['goog.dom', 'goog.string', 'goog.testing.jsunit', 'goog.tweak', 'goog.tweak.TweakUi', 'goog.tweak.testhelpers'], false); +goog.addDependency('ui/abstractspellchecker.js', ['goog.ui.AbstractSpellChecker', 'goog.ui.AbstractSpellChecker.AsyncResult'], ['goog.a11y.aria', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.dom.selection', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.math.Coordinate', 'goog.spell.SpellCheck', 'goog.structs.Set', 'goog.style', 'goog.ui.Component', 'goog.ui.MenuItem', 'goog.ui.MenuSeparator', 'goog.ui.PopupMenu'], false); +goog.addDependency('ui/ac/ac.js', ['goog.ui.ac'], ['goog.ui.ac.ArrayMatcher', 'goog.ui.ac.AutoComplete', 'goog.ui.ac.InputHandler', 'goog.ui.ac.Renderer'], false); +goog.addDependency('ui/ac/ac_test.js', ['goog.ui.acTest'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.dom.selection', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.style', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.ui.ac', 'goog.userAgent'], false); +goog.addDependency('ui/ac/arraymatcher.js', ['goog.ui.ac.ArrayMatcher'], ['goog.string'], false); +goog.addDependency('ui/ac/arraymatcher_test.js', ['goog.ui.ac.ArrayMatcherTest'], ['goog.testing.jsunit', 'goog.ui.ac.ArrayMatcher'], false); +goog.addDependency('ui/ac/autocomplete.js', ['goog.ui.ac.AutoComplete', 'goog.ui.ac.AutoComplete.EventType'], ['goog.array', 'goog.asserts', 'goog.events', 'goog.events.EventTarget', 'goog.object'], false); +goog.addDependency('ui/ac/autocomplete_test.js', ['goog.ui.ac.AutoCompleteTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.string', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.ui.ac.AutoComplete', 'goog.ui.ac.InputHandler', 'goog.ui.ac.RenderOptions', 'goog.ui.ac.Renderer'], false); +goog.addDependency('ui/ac/cachingmatcher.js', ['goog.ui.ac.CachingMatcher'], ['goog.array', 'goog.async.Throttle', 'goog.ui.ac.ArrayMatcher', 'goog.ui.ac.RenderOptions'], false); +goog.addDependency('ui/ac/cachingmatcher_test.js', ['goog.ui.ac.CachingMatcherTest'], ['goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.ui.ac.CachingMatcher'], false); +goog.addDependency('ui/ac/inputhandler.js', ['goog.ui.ac.InputHandler'], ['goog.Disposable', 'goog.Timer', 'goog.a11y.aria', 'goog.dom', 'goog.dom.selection', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.string', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('ui/ac/inputhandler_test.js', ['goog.ui.ac.InputHandlerTest'], ['goog.dom.selection', 'goog.events.BrowserEvent', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.KeyCodes', 'goog.functions', 'goog.object', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.ui.ac.InputHandler', 'goog.userAgent'], false); +goog.addDependency('ui/ac/remote.js', ['goog.ui.ac.Remote'], ['goog.ui.ac.AutoComplete', 'goog.ui.ac.InputHandler', 'goog.ui.ac.RemoteArrayMatcher', 'goog.ui.ac.Renderer'], false); +goog.addDependency('ui/ac/remotearraymatcher.js', ['goog.ui.ac.RemoteArrayMatcher'], ['goog.Disposable', 'goog.Uri', 'goog.events', 'goog.json', 'goog.net.EventType', 'goog.net.XhrIo'], false); +goog.addDependency('ui/ac/remotearraymatcher_test.js', ['goog.ui.ac.RemoteArrayMatcherTest'], ['goog.json', 'goog.net.XhrIo', 'goog.testing.MockControl', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.ui.ac.RemoteArrayMatcher'], false); +goog.addDependency('ui/ac/renderer.js', ['goog.ui.ac.Renderer', 'goog.ui.ac.Renderer.CustomRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dispose', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.fx.dom.FadeInAndShow', 'goog.fx.dom.FadeOutAndHide', 'goog.positioning', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.string', 'goog.style', 'goog.ui.IdGenerator', 'goog.ui.ac.AutoComplete'], false); +goog.addDependency('ui/ac/renderer_test.js', ['goog.ui.ac.RendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.fx.dom.FadeInAndShow', 'goog.fx.dom.FadeOutAndHide', 'goog.string', 'goog.style', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.ac.AutoComplete', 'goog.ui.ac.Renderer'], false); +goog.addDependency('ui/ac/renderoptions.js', ['goog.ui.ac.RenderOptions'], [], false); +goog.addDependency('ui/ac/richinputhandler.js', ['goog.ui.ac.RichInputHandler'], ['goog.ui.ac.InputHandler'], false); +goog.addDependency('ui/ac/richremote.js', ['goog.ui.ac.RichRemote'], ['goog.ui.ac.AutoComplete', 'goog.ui.ac.Remote', 'goog.ui.ac.Renderer', 'goog.ui.ac.RichInputHandler', 'goog.ui.ac.RichRemoteArrayMatcher'], false); +goog.addDependency('ui/ac/richremotearraymatcher.js', ['goog.ui.ac.RichRemoteArrayMatcher'], ['goog.json', 'goog.ui.ac.RemoteArrayMatcher'], false); +goog.addDependency('ui/activitymonitor.js', ['goog.ui.ActivityMonitor'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType'], false); +goog.addDependency('ui/activitymonitor_test.js', ['goog.ui.ActivityMonitorTest'], ['goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.ActivityMonitor'], false); +goog.addDependency('ui/advancedtooltip.js', ['goog.ui.AdvancedTooltip'], ['goog.events', 'goog.events.EventType', 'goog.math.Box', 'goog.math.Coordinate', 'goog.style', 'goog.ui.Tooltip', 'goog.userAgent'], false); +goog.addDependency('ui/advancedtooltip_test.js', ['goog.ui.AdvancedTooltipTest'], ['goog.dom', 'goog.events.Event', 'goog.events.EventType', 'goog.math.Box', 'goog.math.Coordinate', 'goog.style', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.AdvancedTooltip', 'goog.ui.Tooltip', 'goog.userAgent'], false); +goog.addDependency('ui/animatedzippy.js', ['goog.ui.AnimatedZippy'], ['goog.dom', 'goog.events', 'goog.fx.Animation', 'goog.fx.Transition', 'goog.fx.easing', 'goog.ui.Zippy', 'goog.ui.ZippyEvent'], false); +goog.addDependency('ui/animatedzippy_test.js', ['goog.ui.AnimatedZippyTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.events', 'goog.functions', 'goog.fx.Animation', 'goog.fx.Transition', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.ui.AnimatedZippy', 'goog.ui.Zippy'], false); +goog.addDependency('ui/attachablemenu.js', ['goog.ui.AttachableMenu'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.Event', 'goog.events.KeyCodes', 'goog.string', 'goog.style', 'goog.ui.ItemEvent', 'goog.ui.MenuBase', 'goog.ui.PopupBase', 'goog.userAgent'], false); +goog.addDependency('ui/bidiinput.js', ['goog.ui.BidiInput'], ['goog.dom', 'goog.events', 'goog.events.InputHandler', 'goog.i18n.bidi', 'goog.i18n.bidi.Dir', 'goog.ui.Component'], false); +goog.addDependency('ui/bidiinput_test.js', ['goog.ui.BidiInputTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.BidiInput'], false); +goog.addDependency('ui/bubble.js', ['goog.ui.Bubble'], ['goog.Timer', 'goog.events', 'goog.events.EventType', 'goog.math.Box', 'goog.positioning', 'goog.positioning.AbsolutePosition', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.positioning.CornerBit', 'goog.style', 'goog.ui.Component', 'goog.ui.Popup'], false); +goog.addDependency('ui/button.js', ['goog.ui.Button', 'goog.ui.Button.Side'], ['goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.ui.ButtonRenderer', 'goog.ui.ButtonSide', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.NativeButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/button_test.js', ['goog.ui.ButtonTest'], ['goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Button', 'goog.ui.ButtonRenderer', 'goog.ui.ButtonSide', 'goog.ui.Component', 'goog.ui.NativeButtonRenderer'], false); +goog.addDependency('ui/buttonrenderer.js', ['goog.ui.ButtonRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.asserts', 'goog.ui.ButtonSide', 'goog.ui.Component', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/buttonrenderer_test.js', ['goog.ui.ButtonRendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.testing.ExpectedFailures', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Button', 'goog.ui.ButtonRenderer', 'goog.ui.ButtonSide', 'goog.ui.Component'], false); +goog.addDependency('ui/buttonside.js', ['goog.ui.ButtonSide'], [], false); +goog.addDependency('ui/charcounter.js', ['goog.ui.CharCounter', 'goog.ui.CharCounter.Display'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.InputHandler'], false); +goog.addDependency('ui/charcounter_test.js', ['goog.ui.CharCounterTest'], ['goog.dom', 'goog.testing.asserts', 'goog.testing.jsunit', 'goog.ui.CharCounter', 'goog.userAgent'], false); +goog.addDependency('ui/charpicker.js', ['goog.ui.CharPicker'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.i18n.CharListDecompressor', 'goog.i18n.uChar', 'goog.structs.Set', 'goog.style', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.ContainerScroller', 'goog.ui.FlatButtonRenderer', 'goog.ui.HoverCard', 'goog.ui.LabelInput', 'goog.ui.Menu', 'goog.ui.MenuButton', 'goog.ui.MenuItem', 'goog.ui.Tooltip'], false); +goog.addDependency('ui/charpicker_test.js', ['goog.ui.CharPickerTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dispose', 'goog.dom', 'goog.events.Event', 'goog.events.EventType', 'goog.i18n.CharPickerData', 'goog.i18n.uChar.NameFetcher', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.ui.CharPicker', 'goog.ui.FlatButtonRenderer'], false); +goog.addDependency('ui/checkbox.js', ['goog.ui.Checkbox', 'goog.ui.Checkbox.State'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.string', 'goog.ui.CheckboxRenderer', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.registry'], false); +goog.addDependency('ui/checkbox_test.js', ['goog.ui.CheckboxTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.KeyCodes', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Checkbox', 'goog.ui.CheckboxRenderer', 'goog.ui.Component', 'goog.ui.ControlRenderer', 'goog.ui.decorate'], false); +goog.addDependency('ui/checkboxmenuitem.js', ['goog.ui.CheckBoxMenuItem'], ['goog.ui.MenuItem', 'goog.ui.registry'], false); +goog.addDependency('ui/checkboxrenderer.js', ['goog.ui.CheckboxRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom.classlist', 'goog.object', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/colorbutton.js', ['goog.ui.ColorButton'], ['goog.ui.Button', 'goog.ui.ColorButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/colorbutton_test.js', ['goog.ui.ColorButtonTest'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.ui.ColorButton', 'goog.ui.decorate'], false); +goog.addDependency('ui/colorbuttonrenderer.js', ['goog.ui.ColorButtonRenderer'], ['goog.asserts', 'goog.dom.classlist', 'goog.functions', 'goog.ui.ColorMenuButtonRenderer'], false); +goog.addDependency('ui/colormenubutton.js', ['goog.ui.ColorMenuButton'], ['goog.array', 'goog.object', 'goog.ui.ColorMenuButtonRenderer', 'goog.ui.ColorPalette', 'goog.ui.Component', 'goog.ui.Menu', 'goog.ui.MenuButton', 'goog.ui.registry'], false); +goog.addDependency('ui/colormenubuttonrenderer.js', ['goog.ui.ColorMenuButtonRenderer'], ['goog.asserts', 'goog.color', 'goog.dom.classlist', 'goog.ui.MenuButtonRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/colormenubuttonrenderer_test.js', ['goog.ui.ColorMenuButtonTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.RendererHarness', 'goog.testing.ui.rendererasserts', 'goog.ui.ColorMenuButton', 'goog.ui.ColorMenuButtonRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/colorpalette.js', ['goog.ui.ColorPalette'], ['goog.array', 'goog.color', 'goog.style', 'goog.ui.Palette', 'goog.ui.PaletteRenderer'], false); +goog.addDependency('ui/colorpalette_test.js', ['goog.ui.ColorPaletteTest'], ['goog.color', 'goog.testing.jsunit', 'goog.ui.ColorPalette'], false); +goog.addDependency('ui/colorpicker.js', ['goog.ui.ColorPicker', 'goog.ui.ColorPicker.EventType'], ['goog.ui.ColorPalette', 'goog.ui.Component'], false); +goog.addDependency('ui/colorsplitbehavior.js', ['goog.ui.ColorSplitBehavior'], ['goog.ui.ColorMenuButton', 'goog.ui.SplitBehavior'], false); +goog.addDependency('ui/combobox.js', ['goog.ui.ComboBox', 'goog.ui.ComboBoxItem'], ['goog.Timer', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventType', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.log', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.ui.ItemEvent', 'goog.ui.LabelInput', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.MenuSeparator', 'goog.ui.registry', 'goog.userAgent'], false); +goog.addDependency('ui/combobox_test.js', ['goog.ui.ComboBoxTest'], ['goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events.KeyCodes', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.ComboBox', 'goog.ui.ComboBoxItem', 'goog.ui.Component', 'goog.ui.ControlRenderer', 'goog.ui.LabelInput', 'goog.ui.Menu', 'goog.ui.MenuItem'], false); +goog.addDependency('ui/component.js', ['goog.ui.Component', 'goog.ui.Component.Error', 'goog.ui.Component.EventType', 'goog.ui.Component.State'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.object', 'goog.style', 'goog.ui.IdGenerator'], false); +goog.addDependency('ui/component_test.js', ['goog.ui.ComponentTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.events.EventTarget', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.Component'], false); +goog.addDependency('ui/container.js', ['goog.ui.Container', 'goog.ui.Container.EventType', 'goog.ui.Container.Orientation'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.object', 'goog.style', 'goog.ui.Component', 'goog.ui.ContainerRenderer', 'goog.ui.Control'], false); +goog.addDependency('ui/container_test.js', ['goog.ui.ContainerTest'], ['goog.a11y.aria', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.KeyCodes', 'goog.events.KeyEvent', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Container', 'goog.ui.Control'], false); +goog.addDependency('ui/containerrenderer.js', ['goog.ui.ContainerRenderer'], ['goog.a11y.aria', 'goog.array', 'goog.asserts', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.string', 'goog.style', 'goog.ui.registry', 'goog.userAgent'], false); +goog.addDependency('ui/containerrenderer_test.js', ['goog.ui.ContainerRendererTest'], ['goog.dom', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Container', 'goog.ui.ContainerRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/containerscroller.js', ['goog.ui.ContainerScroller'], ['goog.Disposable', 'goog.Timer', 'goog.events.EventHandler', 'goog.style', 'goog.ui.Component', 'goog.ui.Container'], false); +goog.addDependency('ui/containerscroller_test.js', ['goog.ui.ContainerScrollerTest'], ['goog.dom', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Container', 'goog.ui.ContainerScroller'], false); +goog.addDependency('ui/control.js', ['goog.ui.Control'], ['goog.array', 'goog.dom', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.string', 'goog.ui.Component', 'goog.ui.ControlContent', 'goog.ui.ControlRenderer', 'goog.ui.decorate', 'goog.ui.registry', 'goog.userAgent'], false); +goog.addDependency('ui/control_test.js', ['goog.ui.ControlTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.array', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.object', 'goog.string', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.ControlRenderer', 'goog.ui.registry', 'goog.userAgent'], false); +goog.addDependency('ui/controlcontent.js', ['goog.ui.ControlContent'], [], false); +goog.addDependency('ui/controlrenderer.js', ['goog.ui.ControlRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.object', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/controlrenderer_test.js', ['goog.ui.ControlRendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.object', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.ControlRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/cookieeditor.js', ['goog.ui.CookieEditor'], ['goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.events.EventType', 'goog.net.cookies', 'goog.string', 'goog.style', 'goog.ui.Component'], false); +goog.addDependency('ui/cookieeditor_test.js', ['goog.ui.CookieEditorTest'], ['goog.dom', 'goog.events.Event', 'goog.events.EventType', 'goog.net.cookies', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.CookieEditor'], false); +goog.addDependency('ui/css3buttonrenderer.js', ['goog.ui.Css3ButtonRenderer'], ['goog.asserts', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.ui.Button', 'goog.ui.ButtonRenderer', 'goog.ui.Component', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.registry'], false); +goog.addDependency('ui/css3menubuttonrenderer.js', ['goog.ui.Css3MenuButtonRenderer'], ['goog.dom', 'goog.dom.TagName', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.MenuButton', 'goog.ui.MenuButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/cssnames.js', ['goog.ui.INLINE_BLOCK_CLASSNAME'], [], false); +goog.addDependency('ui/custombutton.js', ['goog.ui.CustomButton'], ['goog.ui.Button', 'goog.ui.CustomButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/custombuttonrenderer.js', ['goog.ui.CustomButtonRenderer'], ['goog.a11y.aria.Role', 'goog.asserts', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.string', 'goog.ui.ButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME'], false); +goog.addDependency('ui/customcolorpalette.js', ['goog.ui.CustomColorPalette'], ['goog.color', 'goog.dom', 'goog.dom.classlist', 'goog.ui.ColorPalette', 'goog.ui.Component'], false); +goog.addDependency('ui/customcolorpalette_test.js', ['goog.ui.CustomColorPaletteTest'], ['goog.dom.TagName', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.ui.CustomColorPalette'], false); +goog.addDependency('ui/datepicker.js', ['goog.ui.DatePicker', 'goog.ui.DatePicker.Events', 'goog.ui.DatePickerEvent'], ['goog.a11y.aria', 'goog.asserts', 'goog.date.Date', 'goog.date.DateRange', 'goog.date.Interval', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyHandler', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimePatterns', 'goog.i18n.DateTimeSymbols', 'goog.style', 'goog.ui.Component', 'goog.ui.DefaultDatePickerRenderer', 'goog.ui.IdGenerator'], false); +goog.addDependency('ui/datepicker_test.js', ['goog.ui.DatePickerTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.date.Date', 'goog.date.DateRange', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_en_US', 'goog.i18n.DateTimeSymbols_zh_HK', 'goog.style', 'goog.testing.jsunit', 'goog.ui.DatePicker'], false); +goog.addDependency('ui/datepickerrenderer.js', ['goog.ui.DatePickerRenderer'], [], false); +goog.addDependency('ui/decorate.js', ['goog.ui.decorate'], ['goog.ui.registry'], false); +goog.addDependency('ui/decorate_test.js', ['goog.ui.decorateTest'], ['goog.testing.jsunit', 'goog.ui.decorate', 'goog.ui.registry'], false); +goog.addDependency('ui/defaultdatepickerrenderer.js', ['goog.ui.DefaultDatePickerRenderer'], ['goog.dom', 'goog.dom.TagName', 'goog.ui.DatePickerRenderer'], false); +goog.addDependency('ui/dialog.js', ['goog.ui.Dialog', 'goog.ui.Dialog.ButtonSet', 'goog.ui.Dialog.ButtonSet.DefaultButtons', 'goog.ui.Dialog.DefaultButtonCaptions', 'goog.ui.Dialog.DefaultButtonKeys', 'goog.ui.Dialog.Event', 'goog.ui.Dialog.EventType'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.dom.safe', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.fx.Dragger', 'goog.html.SafeHtml', 'goog.html.legacyconversions', 'goog.math.Rect', 'goog.string', 'goog.structs.Map', 'goog.style', 'goog.ui.ModalPopup'], false); +goog.addDependency('ui/dialog_test.js', ['goog.ui.DialogTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.fx.css3', 'goog.html.SafeHtml', 'goog.html.testing', 'goog.style', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Dialog', 'goog.userAgent'], false); +goog.addDependency('ui/dimensionpicker.js', ['goog.ui.DimensionPicker'], ['goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Size', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.DimensionPickerRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/dimensionpicker_test.js', ['goog.ui.DimensionPickerTest'], ['goog.dom', 'goog.dom.TagName', 'goog.events.KeyCodes', 'goog.math.Size', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.DimensionPicker', 'goog.ui.DimensionPickerRenderer'], false); +goog.addDependency('ui/dimensionpickerrenderer.js', ['goog.ui.DimensionPickerRenderer'], ['goog.a11y.aria.Announcer', 'goog.a11y.aria.LivePriority', 'goog.dom', 'goog.dom.TagName', 'goog.i18n.bidi', 'goog.style', 'goog.ui.ControlRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/dimensionpickerrenderer_test.js', ['goog.ui.DimensionPickerRendererTest'], ['goog.a11y.aria.LivePriority', 'goog.array', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.DimensionPicker', 'goog.ui.DimensionPickerRenderer'], false); +goog.addDependency('ui/dragdropdetector.js', ['goog.ui.DragDropDetector', 'goog.ui.DragDropDetector.EventType', 'goog.ui.DragDropDetector.ImageDropEvent', 'goog.ui.DragDropDetector.LinkDropEvent'], ['goog.dom', 'goog.dom.TagName', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.math.Coordinate', 'goog.string', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('ui/drilldownrow.js', ['goog.ui.DrilldownRow'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.ui.Component'], false); +goog.addDependency('ui/drilldownrow_test.js', ['goog.ui.DrilldownRowTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.DrilldownRow'], false); +goog.addDependency('ui/editor/abstractdialog.js', ['goog.ui.editor.AbstractDialog', 'goog.ui.editor.AbstractDialog.Builder', 'goog.ui.editor.AbstractDialog.EventType'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventTarget', 'goog.string', 'goog.ui.Dialog', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/editor/abstractdialog_test.js', ['goog.ui.editor.AbstractDialogTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.classlist', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.KeyCodes', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers.ArgumentMatcher', 'goog.ui.editor.AbstractDialog', 'goog.userAgent'], false); +goog.addDependency('ui/editor/bubble.js', ['goog.ui.editor.Bubble'], ['goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.dom.ViewportSizeMonitor', 'goog.dom.classlist', 'goog.editor.style', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.functions', 'goog.log', 'goog.math.Box', 'goog.object', 'goog.positioning', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.ui.PopupBase', 'goog.userAgent'], false); +goog.addDependency('ui/editor/bubble_test.js', ['goog.ui.editor.BubbleTest'], ['goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.EventType', 'goog.positioning.Corner', 'goog.positioning.OverflowStatus', 'goog.string', 'goog.style', 'goog.testing.editor.TestHelper', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.editor.Bubble'], false); +goog.addDependency('ui/editor/defaulttoolbar.js', ['goog.ui.editor.ButtonDescriptor', 'goog.ui.editor.DefaultToolbar'], ['goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.editor.Command', 'goog.style', 'goog.ui.editor.ToolbarFactory', 'goog.ui.editor.messages', 'goog.userAgent'], false); +goog.addDependency('ui/editor/linkdialog.js', ['goog.ui.editor.LinkDialog', 'goog.ui.editor.LinkDialog.BeforeTestLinkEvent', 'goog.ui.editor.LinkDialog.EventType', 'goog.ui.editor.LinkDialog.OkEvent'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.TagName', 'goog.dom.safe', 'goog.editor.BrowserFeature', 'goog.editor.Link', 'goog.editor.focus', 'goog.editor.node', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.InputHandler', 'goog.html.SafeHtml', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.LinkButtonRenderer', 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.TabPane', 'goog.ui.editor.messages', 'goog.userAgent', 'goog.window'], false); +goog.addDependency('ui/editor/linkdialog_test.js', ['goog.ui.editor.LinkDialogTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.dom.TagName', 'goog.editor.BrowserFeature', 'goog.editor.Link', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.style', 'goog.testing.MockControl', 'goog.testing.PropertyReplacer', 'goog.testing.dom', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.testing.mockmatchers.ArgumentMatcher', 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.LinkDialog', 'goog.ui.editor.messages', 'goog.userAgent'], false); +goog.addDependency('ui/editor/messages.js', ['goog.ui.editor.messages'], ['goog.html.uncheckedconversions', 'goog.string.Const'], false); +goog.addDependency('ui/editor/tabpane.js', ['goog.ui.editor.TabPane'], ['goog.asserts', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.style', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.Tab', 'goog.ui.TabBar'], false); +goog.addDependency('ui/editor/toolbarcontroller.js', ['goog.ui.editor.ToolbarController'], ['goog.editor.Field', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.ui.Component'], false); +goog.addDependency('ui/editor/toolbarfactory.js', ['goog.ui.editor.ToolbarFactory'], ['goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.string', 'goog.string.Unicode', 'goog.style', 'goog.ui.Component', 'goog.ui.Container', 'goog.ui.Option', 'goog.ui.Toolbar', 'goog.ui.ToolbarButton', 'goog.ui.ToolbarColorMenuButton', 'goog.ui.ToolbarMenuButton', 'goog.ui.ToolbarRenderer', 'goog.ui.ToolbarSelect', 'goog.userAgent'], false); +goog.addDependency('ui/editor/toolbarfactory_test.js', ['goog.ui.editor.ToolbarFactoryTest'], ['goog.dom', 'goog.testing.ExpectedFailures', 'goog.testing.editor.TestHelper', 'goog.testing.jsunit', 'goog.ui.editor.ToolbarFactory', 'goog.userAgent'], false); +goog.addDependency('ui/emoji/emoji.js', ['goog.ui.emoji.Emoji'], [], false); +goog.addDependency('ui/emoji/emojipalette.js', ['goog.ui.emoji.EmojiPalette'], ['goog.events.EventType', 'goog.net.ImageLoader', 'goog.ui.Palette', 'goog.ui.emoji.Emoji', 'goog.ui.emoji.EmojiPaletteRenderer'], false); +goog.addDependency('ui/emoji/emojipaletterenderer.js', ['goog.ui.emoji.EmojiPaletteRenderer'], ['goog.a11y.aria', 'goog.asserts', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.style', 'goog.ui.PaletteRenderer', 'goog.ui.emoji.Emoji'], false); +goog.addDependency('ui/emoji/emojipicker.js', ['goog.ui.emoji.EmojiPicker'], ['goog.log', 'goog.style', 'goog.ui.Component', 'goog.ui.TabPane', 'goog.ui.emoji.Emoji', 'goog.ui.emoji.EmojiPalette', 'goog.ui.emoji.EmojiPaletteRenderer', 'goog.ui.emoji.ProgressiveEmojiPaletteRenderer'], false); +goog.addDependency('ui/emoji/emojipicker_test.js', ['goog.ui.emoji.EmojiPickerTest'], ['goog.dom.classlist', 'goog.events.EventHandler', 'goog.style', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.emoji.Emoji', 'goog.ui.emoji.EmojiPicker', 'goog.ui.emoji.SpriteInfo'], false); +goog.addDependency('ui/emoji/fast_nonprogressive_emojipicker_test.js', ['goog.ui.emoji.FastNonProgressiveEmojiPickerTest'], ['goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.net.EventType', 'goog.style', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.ui.emoji.Emoji', 'goog.ui.emoji.EmojiPicker', 'goog.ui.emoji.SpriteInfo', 'goog.userAgent'], false); +goog.addDependency('ui/emoji/fast_progressive_emojipicker_test.js', ['goog.ui.emoji.FastProgressiveEmojiPickerTest'], ['goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.net.EventType', 'goog.style', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.ui.emoji.Emoji', 'goog.ui.emoji.EmojiPicker', 'goog.ui.emoji.SpriteInfo'], false); +goog.addDependency('ui/emoji/popupemojipicker.js', ['goog.ui.emoji.PopupEmojiPicker'], ['goog.events.EventType', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.ui.Component', 'goog.ui.Popup', 'goog.ui.emoji.EmojiPicker'], false); +goog.addDependency('ui/emoji/popupemojipicker_test.js', ['goog.ui.emoji.PopupEmojiPickerTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.emoji.PopupEmojiPicker'], false); +goog.addDependency('ui/emoji/progressiveemojipaletterenderer.js', ['goog.ui.emoji.ProgressiveEmojiPaletteRenderer'], ['goog.style', 'goog.ui.emoji.EmojiPaletteRenderer'], false); +goog.addDependency('ui/emoji/spriteinfo.js', ['goog.ui.emoji.SpriteInfo'], [], false); +goog.addDependency('ui/emoji/spriteinfo_test.js', ['goog.ui.emoji.SpriteInfoTest'], ['goog.testing.jsunit', 'goog.ui.emoji.SpriteInfo'], false); +goog.addDependency('ui/filteredmenu.js', ['goog.ui.FilteredMenu'], ['goog.a11y.aria', 'goog.a11y.aria.AutoCompleteValues', 'goog.a11y.aria.State', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.object', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.ui.FilterObservingMenuItem', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.userAgent'], false); +goog.addDependency('ui/filteredmenu_test.js', ['goog.ui.FilteredMenuTest'], ['goog.a11y.aria', 'goog.a11y.aria.AutoCompleteValues', 'goog.a11y.aria.State', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Rect', 'goog.style', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.FilteredMenu', 'goog.ui.MenuItem'], false); +goog.addDependency('ui/filterobservingmenuitem.js', ['goog.ui.FilterObservingMenuItem'], ['goog.ui.FilterObservingMenuItemRenderer', 'goog.ui.MenuItem', 'goog.ui.registry'], false); +goog.addDependency('ui/filterobservingmenuitemrenderer.js', ['goog.ui.FilterObservingMenuItemRenderer'], ['goog.ui.MenuItemRenderer'], false); +goog.addDependency('ui/flatbuttonrenderer.js', ['goog.ui.FlatButtonRenderer'], ['goog.a11y.aria.Role', 'goog.asserts', 'goog.dom.classlist', 'goog.ui.Button', 'goog.ui.ButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.registry'], false); +goog.addDependency('ui/flatmenubuttonrenderer.js', ['goog.ui.FlatMenuButtonRenderer'], ['goog.dom', 'goog.style', 'goog.ui.FlatButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.Menu', 'goog.ui.MenuButton', 'goog.ui.MenuRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/formpost.js', ['goog.ui.FormPost'], ['goog.array', 'goog.dom.TagName', 'goog.dom.safe', 'goog.html.SafeHtml', 'goog.ui.Component'], false); +goog.addDependency('ui/formpost_test.js', ['goog.ui.FormPostTest'], ['goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.object', 'goog.testing.jsunit', 'goog.ui.FormPost', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('ui/gauge.js', ['goog.ui.Gauge', 'goog.ui.GaugeColoredRange'], ['goog.a11y.aria', 'goog.asserts', 'goog.events', 'goog.fx.Animation', 'goog.fx.Transition', 'goog.fx.easing', 'goog.graphics', 'goog.graphics.Font', 'goog.graphics.Path', 'goog.graphics.SolidFill', 'goog.math', 'goog.ui.Component', 'goog.ui.GaugeTheme'], false); +goog.addDependency('ui/gaugetheme.js', ['goog.ui.GaugeTheme'], ['goog.graphics.LinearGradient', 'goog.graphics.SolidFill', 'goog.graphics.Stroke'], false); +goog.addDependency('ui/hovercard.js', ['goog.ui.HoverCard', 'goog.ui.HoverCard.EventType', 'goog.ui.HoverCard.TriggerEvent'], ['goog.array', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.ui.AdvancedTooltip', 'goog.ui.PopupBase', 'goog.ui.Tooltip'], false); +goog.addDependency('ui/hovercard_test.js', ['goog.ui.HoverCardTest'], ['goog.dom', 'goog.events', 'goog.math.Coordinate', 'goog.style', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.HoverCard'], false); +goog.addDependency('ui/hsvapalette.js', ['goog.ui.HsvaPalette'], ['goog.array', 'goog.color.alpha', 'goog.dom.TagName', 'goog.events', 'goog.events.EventType', 'goog.style', 'goog.ui.Component', 'goog.ui.HsvPalette'], false); +goog.addDependency('ui/hsvapalette_test.js', ['goog.ui.HsvaPaletteTest'], ['goog.color.alpha', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events.Event', 'goog.math.Coordinate', 'goog.style', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.HsvaPalette', 'goog.userAgent'], false); +goog.addDependency('ui/hsvpalette.js', ['goog.ui.HsvPalette'], ['goog.color', 'goog.dom.TagName', 'goog.events', 'goog.events.EventType', 'goog.events.InputHandler', 'goog.style', 'goog.style.bidi', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/hsvpalette_test.js', ['goog.ui.HsvPaletteTest'], ['goog.color', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.math.Coordinate', 'goog.style', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.HsvPalette', 'goog.userAgent'], false); +goog.addDependency('ui/idgenerator.js', ['goog.ui.IdGenerator'], [], false); +goog.addDependency('ui/idletimer.js', ['goog.ui.IdleTimer'], ['goog.Timer', 'goog.events', 'goog.events.EventTarget', 'goog.structs.Set', 'goog.ui.ActivityMonitor'], false); +goog.addDependency('ui/idletimer_test.js', ['goog.ui.IdleTimerTest'], ['goog.events', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.ui.IdleTimer', 'goog.ui.MockActivityMonitor'], false); +goog.addDependency('ui/iframemask.js', ['goog.ui.IframeMask'], ['goog.Disposable', 'goog.Timer', 'goog.dom', 'goog.dom.iframe', 'goog.events.EventHandler', 'goog.style'], false); +goog.addDependency('ui/iframemask_test.js', ['goog.ui.IframeMaskTest'], ['goog.dom', 'goog.dom.TagName', 'goog.dom.iframe', 'goog.structs.Pool', 'goog.style', 'goog.testing.MockClock', 'goog.testing.StrictMock', 'goog.testing.jsunit', 'goog.ui.IframeMask', 'goog.ui.Popup', 'goog.ui.PopupBase', 'goog.userAgent'], false); +goog.addDependency('ui/imagelessbuttonrenderer.js', ['goog.ui.ImagelessButtonRenderer'], ['goog.dom.classlist', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.CustomButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.registry'], false); +goog.addDependency('ui/imagelessmenubuttonrenderer.js', ['goog.ui.ImagelessMenuButtonRenderer'], ['goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.MenuButton', 'goog.ui.MenuButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/inputdatepicker.js', ['goog.ui.InputDatePicker'], ['goog.date.DateTime', 'goog.dom', 'goog.string', 'goog.ui.Component', 'goog.ui.DatePicker', 'goog.ui.PopupBase', 'goog.ui.PopupDatePicker'], false); +goog.addDependency('ui/inputdatepicker_test.js', ['goog.ui.InputDatePickerTest'], ['goog.dom', 'goog.i18n.DateTimeFormat', 'goog.i18n.DateTimeParse', 'goog.testing.jsunit', 'goog.ui.InputDatePicker'], false); +goog.addDependency('ui/itemevent.js', ['goog.ui.ItemEvent'], ['goog.events.Event'], false); +goog.addDependency('ui/keyboardshortcuthandler.js', ['goog.ui.KeyboardShortcutEvent', 'goog.ui.KeyboardShortcutHandler', 'goog.ui.KeyboardShortcutHandler.EventType'], ['goog.Timer', 'goog.array', 'goog.asserts', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyNames', 'goog.object', 'goog.userAgent'], false); +goog.addDependency('ui/keyboardshortcuthandler_test.js', ['goog.ui.KeyboardShortcutHandlerTest'], ['goog.dom', 'goog.events', 'goog.events.BrowserEvent', 'goog.events.KeyCodes', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.StrictMock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.KeyboardShortcutHandler', 'goog.userAgent'], false); +goog.addDependency('ui/labelinput.js', ['goog.ui.LabelInput'], ['goog.Timer', 'goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/labelinput_test.js', ['goog.ui.LabelInputTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventType', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.LabelInput', 'goog.userAgent'], false); +goog.addDependency('ui/linkbuttonrenderer.js', ['goog.ui.LinkButtonRenderer'], ['goog.ui.Button', 'goog.ui.FlatButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/media/flashobject.js', ['goog.ui.media.FlashObject', 'goog.ui.media.FlashObject.ScriptAccessLevel', 'goog.ui.media.FlashObject.Wmodes'], ['goog.asserts', 'goog.dom.safe', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.flash', 'goog.html.legacyconversions', 'goog.log', 'goog.object', 'goog.string', 'goog.structs.Map', 'goog.style', 'goog.ui.Component', 'goog.userAgent', 'goog.userAgent.flash'], false); +goog.addDependency('ui/media/flashobject_test.js', ['goog.ui.media.FlashObjectTest'], ['goog.dom', 'goog.dom.DomHelper', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.html.SafeUrl', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.userAgent'], false); +goog.addDependency('ui/media/flickr.js', ['goog.ui.media.FlickrSet', 'goog.ui.media.FlickrSetModel'], ['goog.html.TrustedResourceUrl', 'goog.string.Const', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/flickr_test.js', ['goog.ui.media.FlickrSetTest'], ['goog.dom', 'goog.html.testing', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.FlickrSet', 'goog.ui.media.FlickrSetModel', 'goog.ui.media.Media'], false); +goog.addDependency('ui/media/googlevideo.js', ['goog.ui.media.GoogleVideo', 'goog.ui.media.GoogleVideoModel'], ['goog.string', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/googlevideo_test.js', ['goog.ui.media.GoogleVideoTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.GoogleVideo', 'goog.ui.media.GoogleVideoModel', 'goog.ui.media.Media'], false); +goog.addDependency('ui/media/media.js', ['goog.ui.media.Media', 'goog.ui.media.MediaRenderer'], ['goog.asserts', 'goog.style', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/media/media_test.js', ['goog.ui.media.MediaTest'], ['goog.dom', 'goog.math.Size', 'goog.testing.jsunit', 'goog.ui.ControlRenderer', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/mediamodel.js', ['goog.ui.media.MediaModel', 'goog.ui.media.MediaModel.Category', 'goog.ui.media.MediaModel.Credit', 'goog.ui.media.MediaModel.Credit.Role', 'goog.ui.media.MediaModel.Credit.Scheme', 'goog.ui.media.MediaModel.Medium', 'goog.ui.media.MediaModel.MimeType', 'goog.ui.media.MediaModel.Player', 'goog.ui.media.MediaModel.SubTitle', 'goog.ui.media.MediaModel.Thumbnail'], ['goog.array', 'goog.html.TrustedResourceUrl', 'goog.html.legacyconversions'], false); +goog.addDependency('ui/media/mediamodel_test.js', ['goog.ui.media.MediaModelTest'], ['goog.testing.jsunit', 'goog.ui.media.MediaModel'], false); +goog.addDependency('ui/media/mp3.js', ['goog.ui.media.Mp3'], ['goog.string', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/mp3_test.js', ['goog.ui.media.Mp3Test'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.Mp3'], false); +goog.addDependency('ui/media/photo.js', ['goog.ui.media.Photo'], ['goog.ui.media.Media', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/photo_test.js', ['goog.ui.media.PhotoTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.MediaModel', 'goog.ui.media.Photo'], false); +goog.addDependency('ui/media/picasa.js', ['goog.ui.media.PicasaAlbum', 'goog.ui.media.PicasaAlbumModel'], ['goog.html.TrustedResourceUrl', 'goog.string.Const', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/picasa_test.js', ['goog.ui.media.PicasaTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.PicasaAlbum', 'goog.ui.media.PicasaAlbumModel'], false); +goog.addDependency('ui/media/vimeo.js', ['goog.ui.media.Vimeo', 'goog.ui.media.VimeoModel'], ['goog.string', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/vimeo_test.js', ['goog.ui.media.VimeoTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.Vimeo', 'goog.ui.media.VimeoModel'], false); +goog.addDependency('ui/media/youtube.js', ['goog.ui.media.Youtube', 'goog.ui.media.YoutubeModel'], ['goog.string', 'goog.ui.Component', 'goog.ui.media.FlashObject', 'goog.ui.media.Media', 'goog.ui.media.MediaModel', 'goog.ui.media.MediaRenderer'], false); +goog.addDependency('ui/media/youtube_test.js', ['goog.ui.media.YoutubeTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.media.FlashObject', 'goog.ui.media.Youtube', 'goog.ui.media.YoutubeModel'], false); +goog.addDependency('ui/menu.js', ['goog.ui.Menu', 'goog.ui.Menu.EventType'], ['goog.math.Coordinate', 'goog.string', 'goog.style', 'goog.ui.Component.EventType', 'goog.ui.Component.State', 'goog.ui.Container', 'goog.ui.Container.Orientation', 'goog.ui.MenuHeader', 'goog.ui.MenuItem', 'goog.ui.MenuRenderer', 'goog.ui.MenuSeparator'], false); +goog.addDependency('ui/menu_test.js', ['goog.ui.MenuTest'], ['goog.dom', 'goog.events', 'goog.math.Coordinate', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Menu'], false); +goog.addDependency('ui/menubar.js', ['goog.ui.menuBar'], ['goog.ui.Container', 'goog.ui.MenuBarRenderer'], false); +goog.addDependency('ui/menubardecorator.js', ['goog.ui.menuBarDecorator'], ['goog.ui.MenuBarRenderer', 'goog.ui.menuBar', 'goog.ui.registry'], false); +goog.addDependency('ui/menubarrenderer.js', ['goog.ui.MenuBarRenderer'], ['goog.a11y.aria.Role', 'goog.ui.Container', 'goog.ui.ContainerRenderer'], false); +goog.addDependency('ui/menubase.js', ['goog.ui.MenuBase'], ['goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyHandler', 'goog.ui.Popup'], false); +goog.addDependency('ui/menubutton.js', ['goog.ui.MenuButton'], ['goog.Timer', 'goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.math.Box', 'goog.math.Rect', 'goog.positioning', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.positioning.Overflow', 'goog.style', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.IdGenerator', 'goog.ui.Menu', 'goog.ui.MenuButtonRenderer', 'goog.ui.MenuItem', 'goog.ui.MenuRenderer', 'goog.ui.registry', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('ui/menubutton_test.js', ['goog.ui.MenuButtonTest'], ['goog.Timer', 'goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.positioning', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.positioning.Overflow', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.PropertyReplacer', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.Menu', 'goog.ui.MenuButton', 'goog.ui.MenuItem', 'goog.ui.SubMenu', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('ui/menubuttonrenderer.js', ['goog.ui.MenuButtonRenderer'], ['goog.dom', 'goog.style', 'goog.ui.CustomButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.Menu', 'goog.ui.MenuRenderer'], false); +goog.addDependency('ui/menubuttonrenderer_test.js', ['goog.ui.MenuButtonRendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.MenuButton', 'goog.ui.MenuButtonRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/menuheader.js', ['goog.ui.MenuHeader'], ['goog.ui.Component', 'goog.ui.Control', 'goog.ui.MenuHeaderRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/menuheaderrenderer.js', ['goog.ui.MenuHeaderRenderer'], ['goog.ui.ControlRenderer'], false); +goog.addDependency('ui/menuitem.js', ['goog.ui.MenuItem'], ['goog.a11y.aria.Role', 'goog.array', 'goog.dom', 'goog.dom.classlist', 'goog.math.Coordinate', 'goog.string', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.MenuItemRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/menuitem_test.js', ['goog.ui.MenuItemTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.array', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.events.KeyCodes', 'goog.math.Coordinate', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.MenuItem', 'goog.ui.MenuItemRenderer'], false); +goog.addDependency('ui/menuitemrenderer.js', ['goog.ui.MenuItemRenderer'], ['goog.a11y.aria.Role', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.ui.Component', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/menuitemrenderer_test.js', ['goog.ui.MenuItemRendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Component', 'goog.ui.MenuItem', 'goog.ui.MenuItemRenderer'], false); +goog.addDependency('ui/menurenderer.js', ['goog.ui.MenuRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.ui.ContainerRenderer', 'goog.ui.Separator'], false); +goog.addDependency('ui/menuseparator.js', ['goog.ui.MenuSeparator'], ['goog.ui.MenuSeparatorRenderer', 'goog.ui.Separator', 'goog.ui.registry'], false); +goog.addDependency('ui/menuseparatorrenderer.js', ['goog.ui.MenuSeparatorRenderer'], ['goog.dom', 'goog.dom.classlist', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/menuseparatorrenderer_test.js', ['goog.ui.MenuSeparatorRendererTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.MenuSeparator', 'goog.ui.MenuSeparatorRenderer'], false); +goog.addDependency('ui/mockactivitymonitor.js', ['goog.ui.MockActivityMonitor'], ['goog.events.EventType', 'goog.ui.ActivityMonitor'], false); +goog.addDependency('ui/mockactivitymonitor_test.js', ['goog.ui.MockActivityMonitorTest'], ['goog.events', 'goog.functions', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.ActivityMonitor', 'goog.ui.MockActivityMonitor'], false); +goog.addDependency('ui/modalpopup.js', ['goog.ui.ModalPopup'], ['goog.Timer', 'goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.dom.iframe', 'goog.events', 'goog.events.EventType', 'goog.events.FocusHandler', 'goog.fx.Transition', 'goog.string', 'goog.style', 'goog.ui.Component', 'goog.ui.PopupBase', 'goog.userAgent'], false); +goog.addDependency('ui/modalpopup_test.js', ['goog.ui.ModalPopupTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dispose', 'goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.fx.Transition', 'goog.fx.css3', 'goog.string', 'goog.style', 'goog.testing.MockClock', 'goog.testing.jsunit', 'goog.ui.ModalPopup', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/nativebuttonrenderer.js', ['goog.ui.NativeButtonRenderer'], ['goog.asserts', 'goog.dom.classlist', 'goog.events.EventType', 'goog.ui.ButtonRenderer', 'goog.ui.Component'], false); +goog.addDependency('ui/nativebuttonrenderer_test.js', ['goog.ui.NativeButtonRendererTest'], ['goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.testing.ExpectedFailures', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.NativeButtonRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/option.js', ['goog.ui.Option'], ['goog.ui.Component', 'goog.ui.MenuItem', 'goog.ui.registry'], false); +goog.addDependency('ui/palette.js', ['goog.ui.Palette'], ['goog.array', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.math.Size', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.PaletteRenderer', 'goog.ui.SelectionModel'], false); +goog.addDependency('ui/palette_test.js', ['goog.ui.PaletteTest'], ['goog.a11y.aria', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyEvent', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.Container', 'goog.ui.Palette'], false); +goog.addDependency('ui/paletterenderer.js', ['goog.ui.PaletteRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeIterator', 'goog.dom.NodeType', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.iter', 'goog.style', 'goog.ui.ControlRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/paletterenderer_test.js', ['goog.ui.PaletteRendererTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.dom', 'goog.testing.jsunit', 'goog.ui.Palette', 'goog.ui.PaletteRenderer'], false); +goog.addDependency('ui/plaintextspellchecker.js', ['goog.ui.PlainTextSpellChecker'], ['goog.Timer', 'goog.a11y.aria', 'goog.asserts', 'goog.dom', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.spell.SpellCheck', 'goog.style', 'goog.ui.AbstractSpellChecker', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/plaintextspellchecker_test.js', ['goog.ui.PlainTextSpellCheckerTest'], ['goog.Timer', 'goog.dom', 'goog.events.KeyCodes', 'goog.spell.SpellCheck', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.PlainTextSpellChecker'], false); +goog.addDependency('ui/popup.js', ['goog.ui.Popup', 'goog.ui.Popup.AbsolutePosition', 'goog.ui.Popup.AnchoredPosition', 'goog.ui.Popup.AnchoredViewPortPosition', 'goog.ui.Popup.ClientPosition', 'goog.ui.Popup.Overflow', 'goog.ui.Popup.ViewPortClientPosition', 'goog.ui.Popup.ViewPortPosition'], ['goog.math.Box', 'goog.positioning.AbsolutePosition', 'goog.positioning.AnchoredPosition', 'goog.positioning.AnchoredViewportPosition', 'goog.positioning.ClientPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.ViewportClientPosition', 'goog.positioning.ViewportPosition', 'goog.style', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/popup_test.js', ['goog.ui.PopupTest'], ['goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.style', 'goog.testing.jsunit', 'goog.ui.Popup', 'goog.userAgent'], false); +goog.addDependency('ui/popupbase.js', ['goog.ui.PopupBase', 'goog.ui.PopupBase.EventType', 'goog.ui.PopupBase.Type'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.events', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.fx.Transition', 'goog.style', 'goog.userAgent'], false); +goog.addDependency('ui/popupbase_test.js', ['goog.ui.PopupBaseTest'], ['goog.dom', 'goog.events', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.fx.Transition', 'goog.fx.css3', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/popupcolorpicker.js', ['goog.ui.PopupColorPicker'], ['goog.asserts', 'goog.dom.classlist', 'goog.events.EventType', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.ui.ColorPicker', 'goog.ui.Component', 'goog.ui.Popup'], false); +goog.addDependency('ui/popupcolorpicker_test.js', ['goog.ui.PopupColorPickerTest'], ['goog.dom', 'goog.events', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.ColorPicker', 'goog.ui.PopupColorPicker'], false); +goog.addDependency('ui/popupdatepicker.js', ['goog.ui.PopupDatePicker'], ['goog.events.EventType', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.style', 'goog.ui.Component', 'goog.ui.DatePicker', 'goog.ui.Popup', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/popupdatepicker_test.js', ['goog.ui.PopupDatePickerTest'], ['goog.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.PopupBase', 'goog.ui.PopupDatePicker'], false); +goog.addDependency('ui/popupmenu.js', ['goog.ui.PopupMenu'], ['goog.events.EventType', 'goog.positioning.AnchoredViewportPosition', 'goog.positioning.Corner', 'goog.positioning.MenuAnchoredPosition', 'goog.positioning.Overflow', 'goog.positioning.ViewportClientPosition', 'goog.structs.Map', 'goog.style', 'goog.ui.Component', 'goog.ui.Menu', 'goog.ui.PopupBase', 'goog.userAgent'], false); +goog.addDependency('ui/popupmenu_test.js', ['goog.ui.PopupMenuTest'], ['goog.dom', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.math.Box', 'goog.math.Coordinate', 'goog.positioning.Corner', 'goog.style', 'goog.testing.jsunit', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.PopupMenu'], false); +goog.addDependency('ui/progressbar.js', ['goog.ui.ProgressBar', 'goog.ui.ProgressBar.Orientation'], ['goog.a11y.aria', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.ui.Component', 'goog.ui.RangeModel', 'goog.userAgent'], false); +goog.addDependency('ui/prompt.js', ['goog.ui.Prompt'], ['goog.Timer', 'goog.dom', 'goog.events', 'goog.events.EventType', 'goog.functions', 'goog.html.SafeHtml', 'goog.html.legacyconversions', 'goog.ui.Component', 'goog.ui.Dialog', 'goog.userAgent'], false); +goog.addDependency('ui/prompt_test.js', ['goog.ui.PromptTest'], ['goog.dom.selection', 'goog.events.InputHandler', 'goog.events.KeyCodes', 'goog.functions', 'goog.string', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.BidiInput', 'goog.ui.Dialog', 'goog.ui.Prompt', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('ui/rangemodel.js', ['goog.ui.RangeModel'], ['goog.events.EventTarget', 'goog.ui.Component'], false); +goog.addDependency('ui/rangemodel_test.js', ['goog.ui.RangeModelTest'], ['goog.testing.jsunit', 'goog.ui.RangeModel'], false); +goog.addDependency('ui/ratings.js', ['goog.ui.Ratings', 'goog.ui.Ratings.EventType'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom.classlist', 'goog.events.EventType', 'goog.ui.Component'], false); +goog.addDependency('ui/registry.js', ['goog.ui.registry'], ['goog.asserts', 'goog.dom.classlist'], false); +goog.addDependency('ui/registry_test.js', ['goog.ui.registryTest'], ['goog.object', 'goog.testing.jsunit', 'goog.ui.registry'], false); +goog.addDependency('ui/richtextspellchecker.js', ['goog.ui.RichTextSpellChecker'], ['goog.Timer', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.Range', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.math.Coordinate', 'goog.spell.SpellCheck', 'goog.string.StringBuffer', 'goog.style', 'goog.ui.AbstractSpellChecker', 'goog.ui.Component', 'goog.ui.PopupMenu'], false); +goog.addDependency('ui/richtextspellchecker_test.js', ['goog.ui.RichTextSpellCheckerTest'], ['goog.dom.Range', 'goog.dom.classlist', 'goog.events.KeyCodes', 'goog.object', 'goog.spell.SpellCheck', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.RichTextSpellChecker'], false); +goog.addDependency('ui/roundedpanel.js', ['goog.ui.BaseRoundedPanel', 'goog.ui.CssRoundedPanel', 'goog.ui.GraphicsRoundedPanel', 'goog.ui.RoundedPanel', 'goog.ui.RoundedPanel.Corner'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.graphics', 'goog.graphics.Path', 'goog.graphics.SolidFill', 'goog.graphics.Stroke', 'goog.math', 'goog.math.Coordinate', 'goog.style', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/roundedpanel_test.js', ['goog.ui.RoundedPanelTest'], ['goog.testing.jsunit', 'goog.ui.CssRoundedPanel', 'goog.ui.GraphicsRoundedPanel', 'goog.ui.RoundedPanel', 'goog.userAgent'], false); +goog.addDependency('ui/roundedtabrenderer.js', ['goog.ui.RoundedTabRenderer'], ['goog.dom', 'goog.ui.Tab', 'goog.ui.TabBar', 'goog.ui.TabRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/scrollfloater.js', ['goog.ui.ScrollFloater', 'goog.ui.ScrollFloater.EventType'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventType', 'goog.style', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/scrollfloater_test.js', ['goog.ui.ScrollFloaterTest'], ['goog.dom', 'goog.events', 'goog.style', 'goog.testing.jsunit', 'goog.ui.ScrollFloater'], false); +goog.addDependency('ui/select.js', ['goog.ui.Select'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.events.EventType', 'goog.ui.Component', 'goog.ui.IdGenerator', 'goog.ui.MenuButton', 'goog.ui.MenuItem', 'goog.ui.MenuRenderer', 'goog.ui.SelectionModel', 'goog.ui.registry'], false); +goog.addDependency('ui/select_test.js', ['goog.ui.SelectTest'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.CustomButtonRenderer', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.Select', 'goog.ui.Separator'], false); +goog.addDependency('ui/selectionmenubutton.js', ['goog.ui.SelectionMenuButton', 'goog.ui.SelectionMenuButton.SelectionState'], ['goog.events.EventType', 'goog.style', 'goog.ui.Component', 'goog.ui.MenuButton', 'goog.ui.MenuItem', 'goog.ui.registry'], false); +goog.addDependency('ui/selectionmenubutton_test.js', ['goog.ui.SelectionMenuButtonTest'], ['goog.dom', 'goog.events', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.SelectionMenuButton'], false); +goog.addDependency('ui/selectionmodel.js', ['goog.ui.SelectionModel'], ['goog.array', 'goog.events.EventTarget', 'goog.events.EventType'], false); +goog.addDependency('ui/selectionmodel_test.js', ['goog.ui.SelectionModelTest'], ['goog.array', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.SelectionModel'], false); +goog.addDependency('ui/separator.js', ['goog.ui.Separator'], ['goog.a11y.aria', 'goog.asserts', 'goog.ui.Component', 'goog.ui.Control', 'goog.ui.MenuSeparatorRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/serverchart.js', ['goog.ui.ServerChart', 'goog.ui.ServerChart.AxisDisplayType', 'goog.ui.ServerChart.ChartType', 'goog.ui.ServerChart.EncodingType', 'goog.ui.ServerChart.Event', 'goog.ui.ServerChart.LegendPosition', 'goog.ui.ServerChart.MaximumValue', 'goog.ui.ServerChart.MultiAxisAlignment', 'goog.ui.ServerChart.MultiAxisType', 'goog.ui.ServerChart.UriParam', 'goog.ui.ServerChart.UriTooLongEvent'], ['goog.Uri', 'goog.array', 'goog.asserts', 'goog.events.Event', 'goog.string', 'goog.ui.Component'], false); +goog.addDependency('ui/serverchart_test.js', ['goog.ui.ServerChartTest'], ['goog.Uri', 'goog.events', 'goog.testing.jsunit', 'goog.ui.ServerChart'], false); +goog.addDependency('ui/slider.js', ['goog.ui.Slider', 'goog.ui.Slider.Orientation'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.dom', 'goog.ui.SliderBase'], false); +goog.addDependency('ui/sliderbase.js', ['goog.ui.SliderBase', 'goog.ui.SliderBase.AnimationFactory', 'goog.ui.SliderBase.Orientation'], ['goog.Timer', 'goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.events.KeyHandler', 'goog.events.MouseWheelHandler', 'goog.functions', 'goog.fx.AnimationParallelQueue', 'goog.fx.Dragger', 'goog.fx.Transition', 'goog.fx.dom.ResizeHeight', 'goog.fx.dom.ResizeWidth', 'goog.fx.dom.Slide', 'goog.math', 'goog.math.Coordinate', 'goog.style', 'goog.style.bidi', 'goog.ui.Component', 'goog.ui.RangeModel'], false); +goog.addDependency('ui/sliderbase_test.js', ['goog.ui.SliderBaseTest'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.fx.Animation', 'goog.math.Coordinate', 'goog.style', 'goog.style.bidi', 'goog.testing.MockClock', 'goog.testing.MockControl', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.mockmatchers', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.SliderBase', 'goog.userAgent'], false); +goog.addDependency('ui/splitbehavior.js', ['goog.ui.SplitBehavior', 'goog.ui.SplitBehavior.DefaultHandlers'], ['goog.Disposable', 'goog.asserts', 'goog.dispose', 'goog.dom', 'goog.dom.NodeType', 'goog.dom.classlist', 'goog.events.EventHandler', 'goog.ui.ButtonSide', 'goog.ui.Component', 'goog.ui.decorate', 'goog.ui.registry'], false); +goog.addDependency('ui/splitbehavior_test.js', ['goog.ui.SplitBehaviorTest'], ['goog.array', 'goog.dom', 'goog.events', 'goog.events.Event', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.CustomButton', 'goog.ui.Menu', 'goog.ui.MenuButton', 'goog.ui.MenuItem', 'goog.ui.SplitBehavior', 'goog.ui.decorate'], false); +goog.addDependency('ui/splitpane.js', ['goog.ui.SplitPane', 'goog.ui.SplitPane.Orientation'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventType', 'goog.fx.Dragger', 'goog.math.Rect', 'goog.math.Size', 'goog.style', 'goog.ui.Component', 'goog.userAgent'], false); +goog.addDependency('ui/splitpane_test.js', ['goog.ui.SplitPaneTest'], ['goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.math.Size', 'goog.style', 'goog.testing.events', 'goog.testing.jsunit', 'goog.testing.recordFunction', 'goog.ui.Component', 'goog.ui.SplitPane'], false); +goog.addDependency('ui/style/app/buttonrenderer.js', ['goog.ui.style.app.ButtonRenderer'], ['goog.dom.classlist', 'goog.ui.Button', 'goog.ui.CustomButtonRenderer', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.registry'], false); +goog.addDependency('ui/style/app/buttonrenderer_test.js', ['goog.ui.style.app.ButtonRendererTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.style', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.style.app.ButtonRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/style/app/menubuttonrenderer.js', ['goog.ui.style.app.MenuButtonRenderer'], ['goog.a11y.aria.Role', 'goog.array', 'goog.dom', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuRenderer', 'goog.ui.style.app.ButtonRenderer'], false); +goog.addDependency('ui/style/app/menubuttonrenderer_test.js', ['goog.ui.style.app.MenuButtonRendererTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.style', 'goog.ui.Component', 'goog.ui.MenuButton', 'goog.ui.style.app.MenuButtonRenderer'], false); +goog.addDependency('ui/style/app/primaryactionbuttonrenderer.js', ['goog.ui.style.app.PrimaryActionButtonRenderer'], ['goog.ui.Button', 'goog.ui.registry', 'goog.ui.style.app.ButtonRenderer'], false); +goog.addDependency('ui/style/app/primaryactionbuttonrenderer_test.js', ['goog.ui.style.app.PrimaryActionButtonRendererTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.style', 'goog.ui.Button', 'goog.ui.Component', 'goog.ui.style.app.PrimaryActionButtonRenderer'], false); +goog.addDependency('ui/submenu.js', ['goog.ui.SubMenu'], ['goog.Timer', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.KeyCodes', 'goog.positioning.AnchoredViewportPosition', 'goog.positioning.Corner', 'goog.style', 'goog.ui.Component', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.SubMenuRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/submenu_test.js', ['goog.ui.SubMenuTest'], ['goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.KeyCodes', 'goog.functions', 'goog.positioning', 'goog.positioning.Overflow', 'goog.style', 'goog.testing.MockClock', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.ui.SubMenu', 'goog.ui.SubMenuRenderer'], false); +goog.addDependency('ui/submenurenderer.js', ['goog.ui.SubMenuRenderer'], ['goog.a11y.aria', 'goog.a11y.aria.State', 'goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuItemRenderer'], false); +goog.addDependency('ui/tab.js', ['goog.ui.Tab'], ['goog.ui.Component', 'goog.ui.Control', 'goog.ui.TabRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/tab_test.js', ['goog.ui.TabTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Tab', 'goog.ui.TabRenderer'], false); +goog.addDependency('ui/tabbar.js', ['goog.ui.TabBar', 'goog.ui.TabBar.Location'], ['goog.ui.Component.EventType', 'goog.ui.Container', 'goog.ui.Container.Orientation', 'goog.ui.Tab', 'goog.ui.TabBarRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/tabbar_test.js', ['goog.ui.TabBarTest'], ['goog.dom', 'goog.events', 'goog.events.Event', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.Container', 'goog.ui.Tab', 'goog.ui.TabBar', 'goog.ui.TabBarRenderer'], false); +goog.addDependency('ui/tabbarrenderer.js', ['goog.ui.TabBarRenderer'], ['goog.a11y.aria.Role', 'goog.object', 'goog.ui.ContainerRenderer'], false); +goog.addDependency('ui/tabbarrenderer_test.js', ['goog.ui.TabBarRendererTest'], ['goog.a11y.aria.Role', 'goog.dom', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Container', 'goog.ui.TabBar', 'goog.ui.TabBarRenderer'], false); +goog.addDependency('ui/tablesorter.js', ['goog.ui.TableSorter', 'goog.ui.TableSorter.EventType'], ['goog.array', 'goog.dom', 'goog.dom.TagName', 'goog.dom.classlist', 'goog.events.EventType', 'goog.functions', 'goog.ui.Component'], false); +goog.addDependency('ui/tablesorter_test.js', ['goog.ui.TableSorterTest'], ['goog.array', 'goog.dom', 'goog.dom.classlist', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.TableSorter'], false); +goog.addDependency('ui/tabpane.js', ['goog.ui.TabPane', 'goog.ui.TabPane.Events', 'goog.ui.TabPane.TabLocation', 'goog.ui.TabPane.TabPage', 'goog.ui.TabPaneEvent'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.events.Event', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.style'], false); +goog.addDependency('ui/tabpane_test.js', ['goog.ui.TabPaneTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.TabPane'], false); +goog.addDependency('ui/tabrenderer.js', ['goog.ui.TabRenderer'], ['goog.a11y.aria.Role', 'goog.ui.Component', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/tabrenderer_test.js', ['goog.ui.TabRendererTest'], ['goog.a11y.aria.Role', 'goog.dom', 'goog.dom.classlist', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.testing.ui.rendererasserts', 'goog.ui.Tab', 'goog.ui.TabRenderer'], false); +goog.addDependency('ui/textarea.js', ['goog.ui.Textarea', 'goog.ui.Textarea.EventType'], ['goog.asserts', 'goog.dom', 'goog.dom.classlist', 'goog.events.EventType', 'goog.style', 'goog.ui.Control', 'goog.ui.TextareaRenderer', 'goog.userAgent'], false); +goog.addDependency('ui/textarea_test.js', ['goog.ui.TextareaTest'], ['goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.style', 'goog.testing.ExpectedFailures', 'goog.testing.events.EventObserver', 'goog.testing.jsunit', 'goog.ui.Textarea', 'goog.ui.TextareaRenderer', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('ui/textarearenderer.js', ['goog.ui.TextareaRenderer'], ['goog.dom.TagName', 'goog.ui.Component', 'goog.ui.ControlRenderer'], false); +goog.addDependency('ui/togglebutton.js', ['goog.ui.ToggleButton'], ['goog.ui.Button', 'goog.ui.Component', 'goog.ui.CustomButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbar.js', ['goog.ui.Toolbar'], ['goog.ui.Container', 'goog.ui.ToolbarRenderer'], false); +goog.addDependency('ui/toolbar_test.js', ['goog.ui.ToolbarTest'], ['goog.a11y.aria', 'goog.dom', 'goog.events.EventType', 'goog.testing.events', 'goog.testing.events.Event', 'goog.testing.jsunit', 'goog.ui.Toolbar', 'goog.ui.ToolbarMenuButton'], false); +goog.addDependency('ui/toolbarbutton.js', ['goog.ui.ToolbarButton'], ['goog.ui.Button', 'goog.ui.ToolbarButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbarbuttonrenderer.js', ['goog.ui.ToolbarButtonRenderer'], ['goog.ui.CustomButtonRenderer'], false); +goog.addDependency('ui/toolbarcolormenubutton.js', ['goog.ui.ToolbarColorMenuButton'], ['goog.ui.ColorMenuButton', 'goog.ui.ToolbarColorMenuButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbarcolormenubuttonrenderer.js', ['goog.ui.ToolbarColorMenuButtonRenderer'], ['goog.asserts', 'goog.dom.classlist', 'goog.ui.ColorMenuButtonRenderer', 'goog.ui.MenuButtonRenderer', 'goog.ui.ToolbarMenuButtonRenderer'], false); +goog.addDependency('ui/toolbarcolormenubuttonrenderer_test.js', ['goog.ui.ToolbarColorMenuButtonRendererTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.testing.ui.RendererHarness', 'goog.testing.ui.rendererasserts', 'goog.ui.ToolbarColorMenuButton', 'goog.ui.ToolbarColorMenuButtonRenderer'], false); +goog.addDependency('ui/toolbarmenubutton.js', ['goog.ui.ToolbarMenuButton'], ['goog.ui.MenuButton', 'goog.ui.ToolbarMenuButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbarmenubuttonrenderer.js', ['goog.ui.ToolbarMenuButtonRenderer'], ['goog.ui.MenuButtonRenderer'], false); +goog.addDependency('ui/toolbarrenderer.js', ['goog.ui.ToolbarRenderer'], ['goog.a11y.aria.Role', 'goog.ui.Container', 'goog.ui.ContainerRenderer', 'goog.ui.Separator', 'goog.ui.ToolbarSeparatorRenderer'], false); +goog.addDependency('ui/toolbarselect.js', ['goog.ui.ToolbarSelect'], ['goog.ui.Select', 'goog.ui.ToolbarMenuButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbarseparator.js', ['goog.ui.ToolbarSeparator'], ['goog.ui.Separator', 'goog.ui.ToolbarSeparatorRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/toolbarseparatorrenderer.js', ['goog.ui.ToolbarSeparatorRenderer'], ['goog.asserts', 'goog.dom.classlist', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.MenuSeparatorRenderer'], false); +goog.addDependency('ui/toolbarseparatorrenderer_test.js', ['goog.ui.ToolbarSeparatorRendererTest'], ['goog.dom', 'goog.dom.classlist', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.INLINE_BLOCK_CLASSNAME', 'goog.ui.ToolbarSeparator', 'goog.ui.ToolbarSeparatorRenderer'], false); +goog.addDependency('ui/toolbartogglebutton.js', ['goog.ui.ToolbarToggleButton'], ['goog.ui.ToggleButton', 'goog.ui.ToolbarButtonRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/tooltip.js', ['goog.ui.Tooltip', 'goog.ui.Tooltip.CursorTooltipPosition', 'goog.ui.Tooltip.ElementTooltipPosition', 'goog.ui.Tooltip.State'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.dom.safe', 'goog.events', 'goog.events.EventType', 'goog.html.legacyconversions', 'goog.math.Box', 'goog.math.Coordinate', 'goog.positioning', 'goog.positioning.AnchoredPosition', 'goog.positioning.Corner', 'goog.positioning.Overflow', 'goog.positioning.OverflowStatus', 'goog.positioning.ViewportPosition', 'goog.structs.Set', 'goog.style', 'goog.ui.Popup', 'goog.ui.PopupBase'], false); +goog.addDependency('ui/tooltip_test.js', ['goog.ui.TooltipTest'], ['goog.dom', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventType', 'goog.html.testing', 'goog.math.Coordinate', 'goog.positioning.AbsolutePosition', 'goog.style', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.TestQueue', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.PopupBase', 'goog.ui.Tooltip', 'goog.userAgent'], false); +goog.addDependency('ui/tree/basenode.js', ['goog.ui.tree.BaseNode', 'goog.ui.tree.BaseNode.EventType'], ['goog.Timer', 'goog.a11y.aria', 'goog.asserts', 'goog.dom.safe', 'goog.events.Event', 'goog.events.KeyCodes', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.html.legacyconversions', 'goog.string', 'goog.string.StringBuffer', 'goog.style', 'goog.ui.Component'], false); +goog.addDependency('ui/tree/basenode_test.js', ['goog.ui.tree.BaseNodeTest'], ['goog.dom', 'goog.dom.classlist', 'goog.html.legacyconversions', 'goog.html.testing', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.ui.Component', 'goog.ui.tree.BaseNode', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode'], false); +goog.addDependency('ui/tree/treecontrol.js', ['goog.ui.tree.TreeControl'], ['goog.a11y.aria', 'goog.asserts', 'goog.dom.classlist', 'goog.events.EventType', 'goog.events.FocusHandler', 'goog.events.KeyHandler', 'goog.html.SafeHtml', 'goog.log', 'goog.ui.tree.BaseNode', 'goog.ui.tree.TreeNode', 'goog.ui.tree.TypeAhead', 'goog.userAgent'], false); +goog.addDependency('ui/tree/treecontrol_test.js', ['goog.ui.tree.TreeControlTest'], ['goog.dom', 'goog.testing.jsunit', 'goog.ui.tree.TreeControl'], false); +goog.addDependency('ui/tree/treenode.js', ['goog.ui.tree.TreeNode'], ['goog.ui.tree.BaseNode'], false); +goog.addDependency('ui/tree/typeahead.js', ['goog.ui.tree.TypeAhead', 'goog.ui.tree.TypeAhead.Offset'], ['goog.array', 'goog.events.KeyCodes', 'goog.string', 'goog.structs.Trie'], false); +goog.addDependency('ui/tree/typeahead_test.js', ['goog.ui.tree.TypeAheadTest'], ['goog.dom', 'goog.events.KeyCodes', 'goog.testing.jsunit', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TypeAhead'], false); +goog.addDependency('ui/tristatemenuitem.js', ['goog.ui.TriStateMenuItem', 'goog.ui.TriStateMenuItem.State'], ['goog.dom.classlist', 'goog.ui.Component', 'goog.ui.MenuItem', 'goog.ui.TriStateMenuItemRenderer', 'goog.ui.registry'], false); +goog.addDependency('ui/tristatemenuitemrenderer.js', ['goog.ui.TriStateMenuItemRenderer'], ['goog.asserts', 'goog.dom.classlist', 'goog.ui.MenuItemRenderer'], false); +goog.addDependency('ui/twothumbslider.js', ['goog.ui.TwoThumbSlider'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.dom', 'goog.ui.SliderBase'], false); +goog.addDependency('ui/twothumbslider_test.js', ['goog.ui.TwoThumbSliderTest'], ['goog.testing.jsunit', 'goog.ui.SliderBase', 'goog.ui.TwoThumbSlider'], false); +goog.addDependency('ui/zippy.js', ['goog.ui.Zippy', 'goog.ui.Zippy.Events', 'goog.ui.ZippyEvent'], ['goog.a11y.aria', 'goog.a11y.aria.Role', 'goog.a11y.aria.State', 'goog.dom', 'goog.dom.classlist', 'goog.events.Event', 'goog.events.EventHandler', 'goog.events.EventTarget', 'goog.events.EventType', 'goog.events.KeyCodes', 'goog.style'], false); +goog.addDependency('ui/zippy_test.js', ['goog.ui.ZippyTest'], ['goog.a11y.aria', 'goog.dom', 'goog.dom.classlist', 'goog.events', 'goog.object', 'goog.testing.events', 'goog.testing.jsunit', 'goog.ui.Zippy'], false); +goog.addDependency('uri/uri.js', ['goog.Uri', 'goog.Uri.QueryData'], ['goog.array', 'goog.string', 'goog.structs', 'goog.structs.Map', 'goog.uri.utils', 'goog.uri.utils.ComponentIndex', 'goog.uri.utils.StandardQueryParam'], false); +goog.addDependency('uri/uri_test.js', ['goog.UriTest'], ['goog.Uri', 'goog.testing.jsunit'], false); +goog.addDependency('uri/utils.js', ['goog.uri.utils', 'goog.uri.utils.ComponentIndex', 'goog.uri.utils.QueryArray', 'goog.uri.utils.QueryValue', 'goog.uri.utils.StandardQueryParam'], ['goog.asserts', 'goog.string', 'goog.userAgent'], false); +goog.addDependency('uri/utils_test.js', ['goog.uri.utilsTest'], ['goog.functions', 'goog.string', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.uri.utils'], false); +goog.addDependency('useragent/adobereader.js', ['goog.userAgent.adobeReader'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('useragent/adobereader_test.js', ['goog.userAgent.adobeReaderTest'], ['goog.testing.jsunit', 'goog.userAgent.adobeReader'], false); +goog.addDependency('useragent/flash.js', ['goog.userAgent.flash'], ['goog.string'], false); +goog.addDependency('useragent/flash_test.js', ['goog.userAgent.flashTest'], ['goog.testing.jsunit', 'goog.userAgent.flash'], false); +goog.addDependency('useragent/iphoto.js', ['goog.userAgent.iphoto'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('useragent/jscript.js', ['goog.userAgent.jscript'], ['goog.string'], false); +goog.addDependency('useragent/jscript_test.js', ['goog.userAgent.jscriptTest'], ['goog.testing.jsunit', 'goog.userAgent.jscript'], false); +goog.addDependency('useragent/keyboard.js', ['goog.userAgent.keyboard'], ['goog.labs.userAgent.platform'], false); +goog.addDependency('useragent/keyboard_test.js', ['goog.userAgent.keyboardTest'], ['goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.MockUserAgent', 'goog.testing.jsunit', 'goog.userAgent.keyboard', 'goog.userAgentTestUtil'], false); +goog.addDependency('useragent/picasa.js', ['goog.userAgent.picasa'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('useragent/platform.js', ['goog.userAgent.platform'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('useragent/platform_test.js', ['goog.userAgent.platformTest'], ['goog.testing.MockUserAgent', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.platform', 'goog.userAgentTestUtil'], false); +goog.addDependency('useragent/product.js', ['goog.userAgent.product'], ['goog.userAgent'], false); +goog.addDependency('useragent/product_isversion.js', ['goog.userAgent.product.isVersion'], ['goog.string', 'goog.userAgent', 'goog.userAgent.product'], false); +goog.addDependency('useragent/product_test.js', ['goog.userAgent.productTest'], ['goog.array', 'goog.labs.userAgent.util', 'goog.testing.MockUserAgent', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product', 'goog.userAgent.product.isVersion', 'goog.userAgentTestUtil'], false); +goog.addDependency('useragent/useragent.js', ['goog.userAgent'], ['goog.labs.userAgent.browser', 'goog.labs.userAgent.engine', 'goog.labs.userAgent.platform', 'goog.labs.userAgent.util', 'goog.string'], false); +goog.addDependency('useragent/useragent_quirks_test.js', ['goog.userAgentQuirksTest'], ['goog.testing.jsunit', 'goog.userAgent'], false); +goog.addDependency('useragent/useragent_test.js', ['goog.userAgentTest'], ['goog.array', 'goog.labs.userAgent.platform', 'goog.labs.userAgent.testAgents', 'goog.labs.userAgent.util', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgentTestUtil'], false); +goog.addDependency('useragent/useragenttestutil.js', ['goog.userAgentTestUtil', 'goog.userAgentTestUtil.UserAgents'], ['goog.labs.userAgent.browser', 'goog.labs.userAgent.engine', 'goog.labs.userAgent.platform', 'goog.userAgent', 'goog.userAgent.keyboard', 'goog.userAgent.platform', 'goog.userAgent.product', 'goog.userAgent.product.isVersion'], false); +goog.addDependency('vec/float32array.js', ['goog.vec.Float32Array'], [], false); +goog.addDependency('vec/float64array.js', ['goog.vec.Float64Array'], [], false); +goog.addDependency('vec/mat3.js', ['goog.vec.Mat3'], ['goog.vec'], false); +goog.addDependency('vec/mat3d.js', ['goog.vec.mat3d', 'goog.vec.mat3d.Type'], ['goog.vec'], false); +goog.addDependency('vec/mat3f.js', ['goog.vec.mat3f', 'goog.vec.mat3f.Type'], ['goog.vec'], false); +goog.addDependency('vec/mat4.js', ['goog.vec.Mat4'], ['goog.vec', 'goog.vec.Vec3', 'goog.vec.Vec4'], false); +goog.addDependency('vec/mat4d.js', ['goog.vec.mat4d', 'goog.vec.mat4d.Type'], ['goog.vec', 'goog.vec.vec3d', 'goog.vec.vec4d'], false); +goog.addDependency('vec/mat4f.js', ['goog.vec.mat4f', 'goog.vec.mat4f.Type'], ['goog.vec', 'goog.vec.vec3f', 'goog.vec.vec4f'], false); +goog.addDependency('vec/matrix3.js', ['goog.vec.Matrix3'], [], false); +goog.addDependency('vec/matrix4.js', ['goog.vec.Matrix4'], ['goog.vec', 'goog.vec.Vec3', 'goog.vec.Vec4'], false); +goog.addDependency('vec/quaternion.js', ['goog.vec.Quaternion'], ['goog.vec', 'goog.vec.Vec3', 'goog.vec.Vec4'], false); +goog.addDependency('vec/ray.js', ['goog.vec.Ray'], ['goog.vec.Vec3'], false); +goog.addDependency('vec/vec.js', ['goog.vec', 'goog.vec.AnyType', 'goog.vec.ArrayType', 'goog.vec.Float32', 'goog.vec.Float64', 'goog.vec.Number'], ['goog.vec.Float32Array', 'goog.vec.Float64Array'], false); +goog.addDependency('vec/vec2.js', ['goog.vec.Vec2'], ['goog.vec'], false); +goog.addDependency('vec/vec2d.js', ['goog.vec.vec2d', 'goog.vec.vec2d.Type'], ['goog.vec'], false); +goog.addDependency('vec/vec2f.js', ['goog.vec.vec2f', 'goog.vec.vec2f.Type'], ['goog.vec'], false); +goog.addDependency('vec/vec3.js', ['goog.vec.Vec3'], ['goog.vec'], false); +goog.addDependency('vec/vec3d.js', ['goog.vec.vec3d', 'goog.vec.vec3d.Type'], ['goog.vec'], false); +goog.addDependency('vec/vec3f.js', ['goog.vec.vec3f', 'goog.vec.vec3f.Type'], ['goog.vec'], false); +goog.addDependency('vec/vec4.js', ['goog.vec.Vec4'], ['goog.vec'], false); +goog.addDependency('vec/vec4d.js', ['goog.vec.vec4d', 'goog.vec.vec4d.Type'], ['goog.vec'], false); +goog.addDependency('vec/vec4f.js', ['goog.vec.vec4f', 'goog.vec.vec4f.Type'], ['goog.vec'], false); +goog.addDependency('webgl/webgl.js', ['goog.webgl'], [], false); +goog.addDependency('window/window.js', ['goog.window'], ['goog.string', 'goog.userAgent'], false); +goog.addDependency('window/window_test.js', ['goog.windowTest'], ['goog.dom', 'goog.events', 'goog.string', 'goog.testing.AsyncTestCase', 'goog.testing.jsunit', 'goog.window'], false); diff --git a/mapbox-gl/closure-library/closure/goog/disposable/disposable.js b/mapbox-gl/closure-library/closure/goog/disposable/disposable.js new file mode 100644 index 0000000000..d9c89d96e8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/disposable/disposable.js @@ -0,0 +1,307 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implements the disposable interface. The dispose method is used + * to clean up references and resources. + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.Disposable'); +/** @suppress {extraProvide} */ +goog.provide('goog.dispose'); +/** @suppress {extraProvide} */ +goog.provide('goog.disposeAll'); + +goog.require('goog.disposable.IDisposable'); + + + +/** + * Class that provides the basic implementation for disposable objects. If your + * class holds one or more references to COM objects, DOM nodes, or other + * disposable objects, it should extend this class or implement the disposable + * interface (defined in goog.disposable.IDisposable). + * @constructor + * @implements {goog.disposable.IDisposable} + */ +goog.Disposable = function() { + if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) { + if (goog.Disposable.INCLUDE_STACK_ON_CREATION) { + this.creationStack = new Error().stack; + } + goog.Disposable.instances_[goog.getUid(this)] = this; + } + // Support sealing + this.disposed_ = this.disposed_; + this.onDisposeCallbacks_ = this.onDisposeCallbacks_; +}; + + +/** + * @enum {number} Different monitoring modes for Disposable. + */ +goog.Disposable.MonitoringMode = { + /** + * No monitoring. + */ + OFF: 0, + /** + * Creating and disposing the goog.Disposable instances is monitored. All + * disposable objects need to call the {@code goog.Disposable} base + * constructor. The PERMANENT mode must be switched on before creating any + * goog.Disposable instances. + */ + PERMANENT: 1, + /** + * INTERACTIVE mode can be switched on and off on the fly without producing + * errors. It also doesn't warn if the disposable objects don't call the + * {@code goog.Disposable} base constructor. + */ + INTERACTIVE: 2 +}; + + +/** + * @define {number} The monitoring mode of the goog.Disposable + * instances. Default is OFF. Switching on the monitoring is only + * recommended for debugging because it has a significant impact on + * performance and memory usage. If switched off, the monitoring code + * compiles down to 0 bytes. + */ +goog.define('goog.Disposable.MONITORING_MODE', 0); + + +/** + * @define {boolean} Whether to attach creation stack to each created disposable + * instance; This is only relevant for when MonitoringMode != OFF. + */ +goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true); + + +/** + * Maps the unique ID of every undisposed {@code goog.Disposable} object to + * the object itself. + * @type {!Object} + * @private + */ +goog.Disposable.instances_ = {}; + + +/** + * @return {!Array} All {@code goog.Disposable} objects that + * haven't been disposed of. + */ +goog.Disposable.getUndisposedObjects = function() { + var ret = []; + for (var id in goog.Disposable.instances_) { + if (goog.Disposable.instances_.hasOwnProperty(id)) { + ret.push(goog.Disposable.instances_[Number(id)]); + } + } + return ret; +}; + + +/** + * Clears the registry of undisposed objects but doesn't dispose of them. + */ +goog.Disposable.clearUndisposedObjects = function() { + goog.Disposable.instances_ = {}; +}; + + +/** + * Whether the object has been disposed of. + * @type {boolean} + * @private + */ +goog.Disposable.prototype.disposed_ = false; + + +/** + * Callbacks to invoke when this object is disposed. + * @type {Array} + * @private + */ +goog.Disposable.prototype.onDisposeCallbacks_; + + +/** + * If monitoring the goog.Disposable instances is enabled, stores the creation + * stack trace of the Disposable instance. + * @const {string} + */ +goog.Disposable.prototype.creationStack; + + +/** + * @return {boolean} Whether the object has been disposed of. + * @override + */ +goog.Disposable.prototype.isDisposed = function() { + return this.disposed_; +}; + + +/** + * @return {boolean} Whether the object has been disposed of. + * @deprecated Use {@link #isDisposed} instead. + */ +goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed; + + +/** + * Disposes of the object. If the object hasn't already been disposed of, calls + * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should + * override {@link #disposeInternal} in order to delete references to COM + * objects, DOM nodes, and other disposable objects. Reentrant. + * + * @return {void} Nothing. + * @override + */ +goog.Disposable.prototype.dispose = function() { + if (!this.disposed_) { + // Set disposed_ to true first, in case during the chain of disposal this + // gets disposed recursively. + this.disposed_ = true; + this.disposeInternal(); + if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) { + var uid = goog.getUid(this); + if (goog.Disposable.MONITORING_MODE == + goog.Disposable.MonitoringMode.PERMANENT && + !goog.Disposable.instances_.hasOwnProperty(uid)) { + throw Error(this + ' did not call the goog.Disposable base ' + + 'constructor or was disposed of after a clearUndisposedObjects ' + + 'call'); + } + delete goog.Disposable.instances_[uid]; + } + } +}; + + +/** + * Associates a disposable object with this object so that they will be disposed + * together. + * @param {goog.disposable.IDisposable} disposable that will be disposed when + * this object is disposed. + */ +goog.Disposable.prototype.registerDisposable = function(disposable) { + this.addOnDisposeCallback(goog.partial(goog.dispose, disposable)); +}; + + +/** + * Invokes a callback function when this object is disposed. Callbacks are + * invoked in the order in which they were added. If a callback is added to + * an already disposed Disposable, it will be called immediately. + * @param {function(this:T):?} callback The callback function. + * @param {T=} opt_scope An optional scope to call the callback in. + * @template T + */ +goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) { + if (this.disposed_) { + callback.call(opt_scope); + return; + } + if (!this.onDisposeCallbacks_) { + this.onDisposeCallbacks_ = []; + } + + this.onDisposeCallbacks_.push( + goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback); +}; + + +/** + * Deletes or nulls out any references to COM objects, DOM nodes, or other + * disposable objects. Classes that extend {@code goog.Disposable} should + * override this method. + * Not reentrant. To avoid calling it twice, it must only be called from the + * subclass' {@code disposeInternal} method. Everywhere else the public + * {@code dispose} method must be used. + * For example: + *
+ *   mypackage.MyClass = function() {
+ *     mypackage.MyClass.base(this, 'constructor');
+ *     // Constructor logic specific to MyClass.
+ *     ...
+ *   };
+ *   goog.inherits(mypackage.MyClass, goog.Disposable);
+ *
+ *   mypackage.MyClass.prototype.disposeInternal = function() {
+ *     // Dispose logic specific to MyClass.
+ *     ...
+ *     // Call superclass's disposeInternal at the end of the subclass's, like
+ *     // in C++, to avoid hard-to-catch issues.
+ *     mypackage.MyClass.base(this, 'disposeInternal');
+ *   };
+ * 
+ * @protected + */ +goog.Disposable.prototype.disposeInternal = function() { + if (this.onDisposeCallbacks_) { + while (this.onDisposeCallbacks_.length) { + this.onDisposeCallbacks_.shift()(); + } + } +}; + + +/** + * Returns True if we can verify the object is disposed. + * Calls {@code isDisposed} on the argument if it supports it. If obj + * is not an object with an isDisposed() method, return false. + * @param {*} obj The object to investigate. + * @return {boolean} True if we can verify the object is disposed. + */ +goog.Disposable.isDisposed = function(obj) { + if (obj && typeof obj.isDisposed == 'function') { + return obj.isDisposed(); + } + return false; +}; + + +/** + * Calls {@code dispose} on the argument if it supports it. If obj is not an + * object with a dispose() method, this is a no-op. + * @param {*} obj The object to dispose of. + */ +goog.dispose = function(obj) { + if (obj && typeof obj.dispose == 'function') { + obj.dispose(); + } +}; + + +/** + * Calls {@code dispose} on each member of the list that supports it. (If the + * member is an ArrayLike, then {@code goog.disposeAll()} will be called + * recursively on each of its members.) If the member is not an object with a + * {@code dispose()} method, then it is ignored. + * @param {...*} var_args The list. + */ +goog.disposeAll = function(var_args) { + for (var i = 0, len = arguments.length; i < len; ++i) { + var disposable = arguments[i]; + if (goog.isArrayLike(disposable)) { + goog.disposeAll.apply(null, disposable); + } else { + goog.dispose(disposable); + } + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.html b/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.html new file mode 100644 index 0000000000..be73dbf1af --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.html @@ -0,0 +1,26 @@ + + + + + + + + Closure Unit Tests - goog.Disposable + + + + + +
+ Hello! +
+ + diff --git a/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.js b/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.js new file mode 100644 index 0000000000..e95db01b69 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/disposable/disposable_test.js @@ -0,0 +1,313 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.DisposableTest'); +goog.setTestOnly('goog.DisposableTest'); + +goog.require('goog.Disposable'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); +var d1, d2; + +// Sample subclass of goog.Disposable. + +function DisposableTest() { + goog.Disposable.call(this); + this.element = document.getElementById('someElement'); +} +goog.inherits(DisposableTest, goog.Disposable); + +DisposableTest.prototype.disposeInternal = function() { + DisposableTest.superClass_.disposeInternal.call(this); + delete this.element; +}; + +// Class that doesn't inherit from goog.Disposable, but implements the +// disposable interface via duck typing. + +function DisposableDuck() { + this.element = document.getElementById('someElement'); +} + +DisposableDuck.prototype.dispose = function() { + delete this.element; +}; + +// Class which calls dispose recursively. + +function RecursiveDisposable() { + this.disposedCount = 0; +} +goog.inherits(RecursiveDisposable, goog.Disposable); + +RecursiveDisposable.prototype.disposeInternal = function() { + ++this.disposedCount; + assertEquals('Disposed too many times', 1, this.disposedCount); + this.dispose(); +}; + +// Test methods. + +function setUp() { + d1 = new goog.Disposable(); + d2 = new DisposableTest(); +} + +function tearDown() { + goog.Disposable.MONITORING_MODE = goog.Disposable.MonitoringMode.OFF; + goog.Disposable.INCLUDE_STACK_ON_CREATION = true; + goog.Disposable.instances_ = {}; + d1.dispose(); + d2.dispose(); +} + +function testConstructor() { + assertFalse(d1.isDisposed()); + assertFalse(d2.isDisposed()); + assertEquals(document.getElementById('someElement'), d2.element); +} + +function testDispose() { + assertFalse(d1.isDisposed()); + d1.dispose(); + assertTrue('goog.Disposable instance should have been disposed of', + d1.isDisposed()); + + assertFalse(d2.isDisposed()); + d2.dispose(); + assertTrue('goog.DisposableTest instance should have been disposed of', + d2.isDisposed()); +} + +function testDisposeInternal() { + assertNotUndefined(d2.element); + d2.dispose(); + assertUndefined('goog.DisposableTest.prototype.disposeInternal should ' + + 'have deleted the element reference', d2.element); +} + +function testDisposeAgain() { + d2.dispose(); + assertUndefined('goog.DisposableTest.prototype.disposeInternal should ' + + 'have deleted the element reference', d2.element); + // Manually reset the element to a non-null value, and call dispose(). + // Because the object is already marked disposed, disposeInternal won't + // be called again. + d2.element = document.getElementById('someElement'); + d2.dispose(); + assertNotUndefined('disposeInternal should not be called again if the ' + + 'object has already been marked disposed', d2.element); +} + +function testDisposeWorksRecursively() { + new RecursiveDisposable().dispose(); +} + +function testStaticDispose() { + assertFalse(d1.isDisposed()); + goog.dispose(d1); + assertTrue('goog.Disposable instance should have been disposed of', + d1.isDisposed()); + + assertFalse(d2.isDisposed()); + goog.dispose(d2); + assertTrue('goog.DisposableTest instance should have been disposed of', + d2.isDisposed()); + + var duck = new DisposableDuck(); + assertNotUndefined(duck.element); + goog.dispose(duck); + assertUndefined('goog.dispose should have disposed of object that ' + + 'implements the disposable interface', duck.element); +} + +function testStaticDisposeOnNonDisposableType() { + // Call goog.dispose() with various types and make sure no errors are + // thrown. + goog.dispose(true); + goog.dispose(false); + goog.dispose(null); + goog.dispose(undefined); + goog.dispose(''); + goog.dispose([]); + goog.dispose({}); + + function A() {} + goog.dispose(new A()); +} + +function testMonitoringFailure() { + function BadDisposable() {}; + goog.inherits(BadDisposable, goog.Disposable); + + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.PERMANENT; + + var badDisposable = new BadDisposable; + assertArrayEquals('no disposable objects registered', [], + goog.Disposable.getUndisposedObjects()); + assertThrows('the base ctor should have been called', + goog.bind(badDisposable.dispose, badDisposable)); +} + +function testGetUndisposedObjects() { + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.PERMANENT; + + var d1 = new DisposableTest(); + var d2 = new DisposableTest(); + assertSameElements('the undisposed instances', [d1, d2], + goog.Disposable.getUndisposedObjects()); + + d1.dispose(); + assertSameElements('1 undisposed instance left', [d2], + goog.Disposable.getUndisposedObjects()); + + d1.dispose(); + assertSameElements('second disposal of the same object is no-op', [d2], + goog.Disposable.getUndisposedObjects()); + + d2.dispose(); + assertSameElements('all objects have been disposed of', [], + goog.Disposable.getUndisposedObjects()); +} + +function testClearUndisposedObjects() { + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.PERMANENT; + + var d1 = new DisposableTest(); + var d2 = new DisposableTest(); + d2.dispose(); + goog.Disposable.clearUndisposedObjects(); + assertSameElements('no undisposed object in the registry', [], + goog.Disposable.getUndisposedObjects()); + + assertThrows('disposal after clearUndisposedObjects()', function() { + d1.dispose(); + }); + + // d2 is already disposed of, the redisposal shouldn't throw error. + d2.dispose(); +} + +function testRegisterDisposable() { + var d1 = new DisposableTest(); + var d2 = new DisposableTest(); + + d1.registerDisposable(d2); + d1.dispose(); + + assertTrue('d2 should be disposed when d1 is disposed', d2.isDisposed()); +} + +function testDisposeAll() { + var d1 = new DisposableTest(); + var d2 = new DisposableTest(); + + goog.disposeAll(d1, d2); + + assertTrue('d1 should be disposed', d1.isDisposed()); + assertTrue('d2 should be disposed', d2.isDisposed()); +} + +function testDisposeAllRecursive() { + var d1 = new DisposableTest(); + var d2 = new DisposableTest(); + var d3 = new DisposableTest(); + var d4 = new DisposableTest(); + + goog.disposeAll(d1, [[d2], d3, d4]); + + assertTrue('d1 should be disposed', d1.isDisposed()); + assertTrue('d2 should be disposed', d2.isDisposed()); + assertTrue('d3 should be disposed', d3.isDisposed()); + assertTrue('d4 should be disposed', d4.isDisposed()); +} + +function testCreationStack() { + if (!new Error().stack) + return; + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.PERMANENT; + var disposableStack = new DisposableTest().creationStack; + // Check that the name of this test function occurs in the stack trace. + assertNotEquals(-1, disposableStack.indexOf('testCreationStack')); +} + +function testMonitoredWithoutCreationStack() { + if (!new Error().stack) + return; + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.PERMANENT; + goog.Disposable.INCLUDE_STACK_ON_CREATION = false; + var d1 = new DisposableTest(); + + // Check that it is tracked, but not with a creation stack. + assertUndefined(d1.creationStack); + assertSameElements('the undisposed instance', [d1], + goog.Disposable.getUndisposedObjects()); +} + +function testOnDisposeCallback() { + var callback = goog.testing.recordFunction(); + d1.addOnDisposeCallback(callback); + assertEquals('callback called too early', 0, callback.getCallCount()); + d1.dispose(); + assertEquals('callback should be called once on dispose', + 1, callback.getCallCount()); +} + +function testOnDisposeCallbackOrder() { + var invocations = []; + var callback = function(str) { + invocations.push(str); + }; + d1.addOnDisposeCallback(goog.partial(callback, 'a')); + d1.addOnDisposeCallback(goog.partial(callback, 'b')); + goog.dispose(d1); + assertArrayEquals('callbacks should be called in chronological order', + ['a', 'b'], invocations); +} + +function testAddOnDisposeCallbackAfterDispose() { + var callback = goog.testing.recordFunction(); + var scope = {}; + goog.dispose(d1); + d1.addOnDisposeCallback(callback, scope); + assertEquals('Callback should be immediately called if already disposed', 1, + callback.getCallCount()); + assertEquals('Callback scope should be respected', scope, + callback.getLastCall().getThis()); +} + +function testInteractiveMonitoring() { + var d1 = new DisposableTest(); + goog.Disposable.MONITORING_MODE = + goog.Disposable.MonitoringMode.INTERACTIVE; + var d2 = new DisposableTest(); + + assertSameElements('only 1 undisposed instance tracked', [d2], + goog.Disposable.getUndisposedObjects()); + + // No errors should be thrown. + d1.dispose(); + + assertSameElements('1 undisposed instance left', [d2], + goog.Disposable.getUndisposedObjects()); + + d2.dispose(); + assertSameElements('all disposed', [], + goog.Disposable.getUndisposedObjects()); +} diff --git a/mapbox-gl/closure-library/closure/goog/disposable/idisposable.js b/mapbox-gl/closure-library/closure/goog/disposable/idisposable.js new file mode 100644 index 0000000000..917d17ed39 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/disposable/idisposable.js @@ -0,0 +1,45 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the disposable interface. A disposable object + * has a dispose method to to clean up references and resources. + * @author nnaze@google.com (Nathan Naze) + */ + + +goog.provide('goog.disposable.IDisposable'); + + + +/** + * Interface for a disposable object. If a instance requires cleanup + * (references COM objects, DOM notes, or other disposable objects), it should + * implement this interface (it may subclass goog.Disposable). + * @interface + */ +goog.disposable.IDisposable = function() {}; + + +/** + * Disposes of the object and its resources. + * @return {void} Nothing. + */ +goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod; + + +/** + * @return {boolean} Whether the object has been disposed of. + */ +goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/dom/abstractmultirange.js b/mapbox-gl/closure-library/closure/goog/dom/abstractmultirange.js new file mode 100644 index 0000000000..d45d38d498 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/abstractmultirange.js @@ -0,0 +1,76 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with ranges comprised of multiple + * sub-ranges. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.AbstractMultiRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.AbstractRange'); + + + +/** + * Creates a new multi range with no properties. Do not use this + * constructor: use one of the goog.dom.Range.createFrom* methods instead. + * @constructor + * @extends {goog.dom.AbstractRange} + */ +goog.dom.AbstractMultiRange = function() { +}; +goog.inherits(goog.dom.AbstractMultiRange, goog.dom.AbstractRange); + + +/** @override */ +goog.dom.AbstractMultiRange.prototype.containsRange = function( + otherRange, opt_allowPartial) { + // TODO(user): This will incorrectly return false if two (or more) adjacent + // elements are both in the control range, and are also in the text range + // being compared to. + var ranges = this.getTextRanges(); + var otherRanges = otherRange.getTextRanges(); + + var fn = opt_allowPartial ? goog.array.some : goog.array.every; + return fn(otherRanges, function(otherRange) { + return goog.array.some(ranges, function(range) { + return range.containsRange(otherRange, opt_allowPartial); + }); + }); +}; + + +/** @override */ +goog.dom.AbstractMultiRange.prototype.insertNode = function(node, before) { + if (before) { + goog.dom.insertSiblingBefore(node, this.getStartNode()); + } else { + goog.dom.insertSiblingAfter(node, this.getEndNode()); + } + return node; +}; + + +/** @override */ +goog.dom.AbstractMultiRange.prototype.surroundWithNodes = function(startNode, + endNode) { + this.insertNode(startNode, true); + this.insertNode(endNode, false); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/abstractrange.js b/mapbox-gl/closure-library/closure/goog/dom/abstractrange.js new file mode 100644 index 0000000000..7d66bfbdb8 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/abstractrange.js @@ -0,0 +1,529 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Interface definitions for working with ranges + * in HTML documents. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.AbstractRange'); +goog.provide('goog.dom.RangeIterator'); +goog.provide('goog.dom.RangeType'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.SavedCaretRange'); +goog.require('goog.dom.TagIterator'); +goog.require('goog.userAgent'); + + +/** + * Types of ranges. + * @enum {string} + */ +goog.dom.RangeType = { + TEXT: 'text', + CONTROL: 'control', + MULTI: 'mutli' +}; + + + +/** + * Creates a new selection with no properties. Do not use this constructor - + * use one of the goog.dom.Range.from* methods instead. + * @constructor + */ +goog.dom.AbstractRange = function() { +}; + + +/** + * Gets the browser native selection object from the given window. + * @param {Window} win The window to get the selection object from. + * @return {Object} The browser native selection object, or null if it could + * not be retrieved. + */ +goog.dom.AbstractRange.getBrowserSelectionForWindow = function(win) { + if (win.getSelection) { + // W3C + return win.getSelection(); + } else { + // IE + var doc = win.document; + var sel = doc.selection; + if (sel) { + // IE has a bug where it sometimes returns a selection from the wrong + // document. Catching these cases now helps us avoid problems later. + try { + var range = sel.createRange(); + // Only TextRanges have a parentElement method. + if (range.parentElement) { + if (range.parentElement().document != doc) { + return null; + } + } else if (!range.length || + /** @type {ControlRange} */ (range).item(0).document != doc) { + // For ControlRanges, check that the range has items, and that + // the first item in the range is in the correct document. + return null; + } + } catch (e) { + // If the selection is in the wrong document, and the wrong document is + // in a different domain, IE will throw an exception. + return null; + } + // TODO(user|robbyw) Sometimes IE 6 returns a selection instance + // when there is no selection. This object has a 'type' property equals + // to 'None' and a typeDetail property bound to undefined. Ideally this + // function should not return this instance. + return sel; + } + return null; + } +}; + + +/** + * Tests if the given Object is a controlRange. + * @param {Object} range The range object to test. + * @return {boolean} Whether the given Object is a controlRange. + */ +goog.dom.AbstractRange.isNativeControlRange = function(range) { + // For now, tests for presence of a control range function. + return !!range && !!range.addElement; +}; + + +/** + * @return {!goog.dom.AbstractRange} A clone of this range. + */ +goog.dom.AbstractRange.prototype.clone = goog.abstractMethod; + + +/** + * @return {goog.dom.RangeType} The type of range represented by this object. + */ +goog.dom.AbstractRange.prototype.getType = goog.abstractMethod; + + +/** + * @return {Range|TextRange} The native browser range object. + */ +goog.dom.AbstractRange.prototype.getBrowserRangeObject = goog.abstractMethod; + + +/** + * Sets the native browser range object, overwriting any state this range was + * storing. + * @param {Range|TextRange} nativeRange The native browser range object. + * @return {boolean} Whether the given range was accepted. If not, the caller + * will need to call goog.dom.Range.createFromBrowserRange to create a new + * range object. + */ +goog.dom.AbstractRange.prototype.setBrowserRangeObject = function(nativeRange) { + return false; +}; + + +/** + * @return {number} The number of text ranges in this range. + */ +goog.dom.AbstractRange.prototype.getTextRangeCount = goog.abstractMethod; + + +/** + * Get the i-th text range in this range. The behavior is undefined if + * i >= getTextRangeCount or i < 0. + * @param {number} i The range number to retrieve. + * @return {goog.dom.TextRange} The i-th text range. + */ +goog.dom.AbstractRange.prototype.getTextRange = goog.abstractMethod; + + +/** + * Gets an array of all text ranges this range is comprised of. For non-multi + * ranges, returns a single element array containing this. + * @return {!Array} Array of text ranges. + */ +goog.dom.AbstractRange.prototype.getTextRanges = function() { + var output = []; + for (var i = 0, len = this.getTextRangeCount(); i < len; i++) { + output.push(this.getTextRange(i)); + } + return output; +}; + + +/** + * @return {Node} The deepest node that contains the entire range. + */ +goog.dom.AbstractRange.prototype.getContainer = goog.abstractMethod; + + +/** + * Returns the deepest element in the tree that contains the entire range. + * @return {Element} The deepest element that contains the entire range. + */ +goog.dom.AbstractRange.prototype.getContainerElement = function() { + var node = this.getContainer(); + return /** @type {Element} */ ( + node.nodeType == goog.dom.NodeType.ELEMENT ? node : node.parentNode); +}; + + +/** + * @return {Node} The element or text node the range starts in. For text + * ranges, the range comprises all text between the start and end position. + * For other types of range, start and end give bounds of the range but + * do not imply all nodes in those bounds are selected. + */ +goog.dom.AbstractRange.prototype.getStartNode = goog.abstractMethod; + + +/** + * @return {number} The offset into the node the range starts in. For text + * nodes, this is an offset into the node value. For elements, this is + * an offset into the childNodes array. + */ +goog.dom.AbstractRange.prototype.getStartOffset = goog.abstractMethod; + + +/** + * @return {goog.math.Coordinate} The coordinate of the selection start node + * and offset. + */ +goog.dom.AbstractRange.prototype.getStartPosition = goog.abstractMethod; + + +/** + * @return {Node} The element or text node the range ends in. + */ +goog.dom.AbstractRange.prototype.getEndNode = goog.abstractMethod; + + +/** + * @return {number} The offset into the node the range ends in. For text + * nodes, this is an offset into the node value. For elements, this is + * an offset into the childNodes array. + */ +goog.dom.AbstractRange.prototype.getEndOffset = goog.abstractMethod; + + +/** + * @return {goog.math.Coordinate} The coordinate of the selection end + * node and offset. + */ +goog.dom.AbstractRange.prototype.getEndPosition = goog.abstractMethod; + + +/** + * @return {Node} The element or text node the range is anchored at. + */ +goog.dom.AbstractRange.prototype.getAnchorNode = function() { + return this.isReversed() ? this.getEndNode() : this.getStartNode(); +}; + + +/** + * @return {number} The offset into the node the range is anchored at. For + * text nodes, this is an offset into the node value. For elements, this + * is an offset into the childNodes array. + */ +goog.dom.AbstractRange.prototype.getAnchorOffset = function() { + return this.isReversed() ? this.getEndOffset() : this.getStartOffset(); +}; + + +/** + * @return {Node} The element or text node the range is focused at - i.e. where + * the cursor is. + */ +goog.dom.AbstractRange.prototype.getFocusNode = function() { + return this.isReversed() ? this.getStartNode() : this.getEndNode(); +}; + + +/** + * @return {number} The offset into the node the range is focused at - i.e. + * where the cursor is. For text nodes, this is an offset into the node + * value. For elements, this is an offset into the childNodes array. + */ +goog.dom.AbstractRange.prototype.getFocusOffset = function() { + return this.isReversed() ? this.getStartOffset() : this.getEndOffset(); +}; + + +/** + * @return {boolean} Whether the selection is reversed. + */ +goog.dom.AbstractRange.prototype.isReversed = function() { + return false; +}; + + +/** + * @return {!Document} The document this selection is a part of. + */ +goog.dom.AbstractRange.prototype.getDocument = function() { + // Using start node in IE was crashing the browser in some cases so use + // getContainer for that browser. It's also faster for IE, but still slower + // than start node for other browsers so we continue to use getStartNode when + // it is not problematic. See bug 1687309. + return goog.dom.getOwnerDocument(goog.userAgent.IE ? + this.getContainer() : this.getStartNode()); +}; + + +/** + * @return {!Window} The window this selection is a part of. + */ +goog.dom.AbstractRange.prototype.getWindow = function() { + return goog.dom.getWindow(this.getDocument()); +}; + + +/** + * Tests if this range contains the given range. + * @param {goog.dom.AbstractRange} range The range to test. + * @param {boolean=} opt_allowPartial If true, the range can be partially + * contained in the selection, otherwise the range must be entirely + * contained. + * @return {boolean} Whether this range contains the given range. + */ +goog.dom.AbstractRange.prototype.containsRange = goog.abstractMethod; + + +/** + * Tests if this range contains the given node. + * @param {Node} node The node to test for. + * @param {boolean=} opt_allowPartial If not set or false, the node must be + * entirely contained in the selection for this function to return true. + * @return {boolean} Whether this range contains the given node. + */ +goog.dom.AbstractRange.prototype.containsNode = function(node, + opt_allowPartial) { + return this.containsRange(goog.dom.Range.createFromNodeContents(node), + opt_allowPartial); +}; + + +/** + * Tests whether this range is valid (i.e. whether its endpoints are still in + * the document). A range becomes invalid when, after this object was created, + * either one or both of its endpoints are removed from the document. Use of + * an invalid range can lead to runtime errors, particularly in IE. + * @return {boolean} Whether the range is valid. + */ +goog.dom.AbstractRange.prototype.isRangeInDocument = goog.abstractMethod; + + +/** + * @return {boolean} Whether the range is collapsed. + */ +goog.dom.AbstractRange.prototype.isCollapsed = goog.abstractMethod; + + +/** + * @return {string} The text content of the range. + */ +goog.dom.AbstractRange.prototype.getText = goog.abstractMethod; + + +/** + * Returns the HTML fragment this range selects. This is slow on all browsers. + * The HTML fragment may not be valid HTML, for instance if the user selects + * from a to b inclusively in the following html: + * + * >div<a>/div<b + * + * This method will return + * + * a</div>b + * + * If you need valid HTML, use {@link #getValidHtml} instead. + * + * @return {string} HTML fragment of the range, does not include context + * containing elements. + */ +goog.dom.AbstractRange.prototype.getHtmlFragment = goog.abstractMethod; + + +/** + * Returns valid HTML for this range. This is fast on IE, and semi-fast on + * other browsers. + * @return {string} Valid HTML of the range, including context containing + * elements. + */ +goog.dom.AbstractRange.prototype.getValidHtml = goog.abstractMethod; + + +/** + * Returns pastable HTML for this range. This guarantees that any child items + * that must have specific ancestors will have them, for instance all TDs will + * be contained in a TR in a TBODY in a TABLE and all LIs will be contained in + * a UL or OL as appropriate. This is semi-fast on all browsers. + * @return {string} Pastable HTML of the range, including context containing + * elements. + */ +goog.dom.AbstractRange.prototype.getPastableHtml = goog.abstractMethod; + + +/** + * Returns a RangeIterator over the contents of the range. Regardless of the + * direction of the range, the iterator will move in document order. + * @param {boolean=} opt_keys Unused for this iterator. + * @return {!goog.dom.RangeIterator} An iterator over tags in the range. + */ +goog.dom.AbstractRange.prototype.__iterator__ = goog.abstractMethod; + + +// RANGE ACTIONS + + +/** + * Sets this range as the selection in its window. + */ +goog.dom.AbstractRange.prototype.select = goog.abstractMethod; + + +/** + * Removes the contents of the range from the document. + */ +goog.dom.AbstractRange.prototype.removeContents = goog.abstractMethod; + + +/** + * Inserts a node before (or after) the range. The range may be disrupted + * beyond recovery because of the way this splits nodes. + * @param {Node} node The node to insert. + * @param {boolean} before True to insert before, false to insert after. + * @return {Node} The node added to the document. This may be different + * than the node parameter because on IE we have to clone it. + */ +goog.dom.AbstractRange.prototype.insertNode = goog.abstractMethod; + + +/** + * Replaces the range contents with (possibly a copy of) the given node. The + * range may be disrupted beyond recovery because of the way this splits nodes. + * @param {Node} node The node to insert. + * @return {Node} The node added to the document. This may be different + * than the node parameter because on IE we have to clone it. + */ +goog.dom.AbstractRange.prototype.replaceContentsWithNode = function(node) { + if (!this.isCollapsed()) { + this.removeContents(); + } + + return this.insertNode(node, true); +}; + + +/** + * Surrounds this range with the two given nodes. The range may be disrupted + * beyond recovery because of the way this splits nodes. + * @param {Element} startNode The node to insert at the start. + * @param {Element} endNode The node to insert at the end. + */ +goog.dom.AbstractRange.prototype.surroundWithNodes = goog.abstractMethod; + + +// SAVE/RESTORE + + +/** + * Saves the range so that if the start and end nodes are left alone, it can + * be restored. + * @return {!goog.dom.SavedRange} A range representation that can be restored + * as long as the endpoint nodes of the selection are not modified. + */ +goog.dom.AbstractRange.prototype.saveUsingDom = goog.abstractMethod; + + +/** + * Saves the range using HTML carets. As long as the carets remained in the + * HTML, the range can be restored...even when the HTML is copied across + * documents. + * @return {goog.dom.SavedCaretRange?} A range representation that can be + * restored as long as carets are not removed. Returns null if carets + * could not be created. + */ +goog.dom.AbstractRange.prototype.saveUsingCarets = function() { + return (this.getStartNode() && this.getEndNode()) ? + new goog.dom.SavedCaretRange(this) : null; +}; + + +// RANGE MODIFICATION + + +/** + * Collapses the range to one of its boundary points. + * @param {boolean} toAnchor Whether to collapse to the anchor of the range. + */ +goog.dom.AbstractRange.prototype.collapse = goog.abstractMethod; + +// RANGE ITERATION + + + +/** + * Subclass of goog.dom.TagIterator that iterates over a DOM range. It + * adds functions to determine the portion of each text node that is selected. + * @param {Node} node The node to start traversal at. When null, creates an + * empty iterator. + * @param {boolean=} opt_reverse Whether to traverse nodes in reverse. + * @constructor + * @extends {goog.dom.TagIterator} + */ +goog.dom.RangeIterator = function(node, opt_reverse) { + goog.dom.TagIterator.call(this, node, opt_reverse, true); +}; +goog.inherits(goog.dom.RangeIterator, goog.dom.TagIterator); + + +/** + * @return {number} The offset into the current node, or -1 if the current node + * is not a text node. + */ +goog.dom.RangeIterator.prototype.getStartTextOffset = goog.abstractMethod; + + +/** + * @return {number} The end offset into the current node, or -1 if the current + * node is not a text node. + */ +goog.dom.RangeIterator.prototype.getEndTextOffset = goog.abstractMethod; + + +/** + * @return {Node} node The iterator's start node. + */ +goog.dom.RangeIterator.prototype.getStartNode = goog.abstractMethod; + + +/** + * @return {Node} The iterator's end node. + */ +goog.dom.RangeIterator.prototype.getEndNode = goog.abstractMethod; + + +/** + * @return {boolean} Whether a call to next will fail. + */ +goog.dom.RangeIterator.prototype.isLast = goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.html b/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.html new file mode 100644 index 0000000000..c708ad9579 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.html @@ -0,0 +1,31 @@ + + + + + + + Closure Unit Tests - goog.dom.abstractrange + + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.js new file mode 100644 index 0000000000..edd28ffe54 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/abstractrange_test.js @@ -0,0 +1,61 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.AbstractRangeTest'); +goog.setTestOnly('goog.dom.AbstractRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.Range'); +goog.require('goog.testing.jsunit'); + +function testCorrectDocument() { + var a = goog.dom.getElement('a').contentWindow; + var b = goog.dom.getElement('b').contentWindow; + + a.document.body.focus(); + var selection = goog.dom.AbstractRange.getBrowserSelectionForWindow(a); + assertNotNull('Selection must not be null', selection); + var range = goog.dom.Range.createFromBrowserSelection(selection); + assertEquals('getBrowserSelectionForWindow must return selection in the ' + + 'correct document', a.document, range.getDocument()); + + // This is intended to trip up Internet Explorer -- + // see http://b/2048934 + b.document.body.focus(); + selection = goog.dom.AbstractRange.getBrowserSelectionForWindow(a); + // Some (non-IE) browsers keep a separate selection state for each document + // in the same browser window. That's fine, as long as the selection object + // requested from the window object is correctly associated with that + // window's document. + if (selection != null && selection.rangeCount != 0) { + range = goog.dom.Range.createFromBrowserSelection(selection); + assertEquals('getBrowserSelectionForWindow must return selection in ' + + 'the correct document', a.document, range.getDocument()); + } else { + assertTrue(selection == null || selection.rangeCount == 0); + } +} + +function testSelectionIsControlRange() { + var c = goog.dom.getElement('c').contentWindow; + // Only IE supports control ranges + if (c.document.body.createControlRange) { + var controlRange = c.document.body.createControlRange(); + controlRange.add(c.document.getElementsByTagName('img')[0]); + controlRange.select(); + var selection = goog.dom.AbstractRange.getBrowserSelectionForWindow(c); + assertNotNull('Selection must not be null', selection); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe.js b/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe.js new file mode 100644 index 0000000000..e4e7d5ad5c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe.js @@ -0,0 +1,288 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview goog.dom.animationFrame permits work to be done in-sync with + * the render refresh rate of the browser and to divide work up globally based + * on whether the intent is to measure or to mutate the DOM. The latter avoids + * repeated style recalculation which can be really slow. + * + * Goals of the API: + *
    + *
  • Make it easy to schedule work for the next animation frame. + *
  • Make it easy to only do work once per animation frame, even if two + * events fire that trigger the same work. + *
  • Make it easy to do all work in two phases to avoid repeated style + * recalculation caused by interleaved reads and writes. + *
  • Avoid creating closures per schedule operation. + *
+ * + * + * Programmatic: + *
+ * var animationTask = goog.dom.animationFrame.createTask({
+ *     measure: function(state) {
+ *       state.width = goog.style.getSize(elem).width;
+ *       this.animationTask();
+ *     },
+ *     mutate: function(state) {
+ *       goog.style.setWidth(elem, Math.floor(state.width / 2));
+ *     }
+ *   }, this);
+ * });
+ * 
+ * + * See also + * https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame + */ + +goog.provide('goog.dom.animationFrame'); +goog.provide('goog.dom.animationFrame.Spec'); +goog.provide('goog.dom.animationFrame.State'); + +goog.require('goog.dom.animationFrame.polyfill'); + +// Install the polyfill. +goog.dom.animationFrame.polyfill.install(); + + +/** + * @typedef {{ + * id: number, + * fn: !Function, + * context: (!Object|undefined) + * }} + * @private + */ +goog.dom.animationFrame.Task_; + + +/** + * @typedef {{ + * measureTask: goog.dom.animationFrame.Task_, + * mutateTask: goog.dom.animationFrame.Task_, + * state: (!Object|undefined), + * args: (!Array|undefined), + * isScheduled: boolean + * }} + * @private + */ +goog.dom.animationFrame.TaskSet_; + + +/** + * @typedef {{ + * measure: (!Function|undefined), + * mutate: (!Function|undefined) + * }} + */ +goog.dom.animationFrame.Spec; + + + +/** + * A type to represent state. Users may add properties as desired. + * @constructor + * @final + */ +goog.dom.animationFrame.State = function() {}; + + +/** + * Saves a set of tasks to be executed in the next requestAnimationFrame phase. + * This list is initialized once before any event firing occurs. It is not + * affected by the fired events or the requestAnimationFrame processing (unless + * a new event is created during the processing). + * @private {!Array>} + */ +goog.dom.animationFrame.tasks_ = [[], []]; + + +/** + * Values are 0 or 1, for whether the first or second array should be used to + * lookup or add tasks. + * @private {number} + */ +goog.dom.animationFrame.doubleBufferIndex_ = 0; + + +/** + * Whether we have already requested an animation frame that hasn't happened + * yet. + * @private {boolean} + */ +goog.dom.animationFrame.requestedFrame_ = false; + + +/** + * Counter to generate IDs for tasks. + * @private {number} + */ +goog.dom.animationFrame.taskId_ = 0; + + +/** + * Whether the animationframe runTasks_ loop is currently running. + * @private {boolean} + */ +goog.dom.animationFrame.running_ = false; + + +/** + * Returns a function that schedules the two passed-in functions to be run upon + * the next animation frame. Calling the function again during the same + * animation frame does nothing. + * + * The function under the "measure" key will run first and together with all + * other functions scheduled under this key and the function under "mutate" will + * run after that. + * + * @param {{ + * measure: (function(this:THIS, !goog.dom.animationFrame.State)|undefined), + * mutate: (function(this:THIS, !goog.dom.animationFrame.State)|undefined) + * }} spec + * @param {THIS=} opt_context Context in which to run the function. + * @return {function(...?)} + * @template THIS + */ +goog.dom.animationFrame.createTask = function(spec, opt_context) { + var genericSpec = /** @type {!goog.dom.animationFrame.Spec} */ (spec); + var id = goog.dom.animationFrame.taskId_++; + var measureTask = { + id: id, + fn: spec.measure, + context: opt_context + }; + var mutateTask = { + id: id, + fn: spec.mutate, + context: opt_context + }; + + var taskSet = { + measureTask: measureTask, + mutateTask: mutateTask, + state: {}, + args: undefined, + isScheduled: false + }; + + return function() { + // Default the context to the one that was used to call the tasks scheduler + // (this function). + if (!opt_context) { + measureTask.context = this; + mutateTask.context = this; + } + + // Save args and state. + if (arguments.length > 0) { + // The state argument goes last. That is kinda horrible but compatible + // with {@see wiz.async.method}. + if (!taskSet.args) { + taskSet.args = []; + } + taskSet.args.length = 0; + taskSet.args.push.apply(taskSet.args, arguments); + taskSet.args.push(taskSet.state); + } else { + if (!taskSet.args || taskSet.args.length == 0) { + taskSet.args = [taskSet.state]; + } else { + taskSet.args[0] = taskSet.state; + taskSet.args.length = 1; + } + } + if (!taskSet.isScheduled) { + taskSet.isScheduled = true; + var tasksArray = goog.dom.animationFrame.tasks_[ + goog.dom.animationFrame.doubleBufferIndex_]; + tasksArray.push(taskSet); + } + goog.dom.animationFrame.requestAnimationFrame_(); + }; +}; + + +/** + * Run scheduled tasks. + * @private + */ +goog.dom.animationFrame.runTasks_ = function() { + goog.dom.animationFrame.running_ = true; + goog.dom.animationFrame.requestedFrame_ = false; + var tasksArray = goog.dom.animationFrame + .tasks_[goog.dom.animationFrame.doubleBufferIndex_]; + var taskLength = tasksArray.length; + + // During the runTasks_, if there is a recursive call to queue up more + // task(s) for the next frame, we use double-buffering for that. + goog.dom.animationFrame.doubleBufferIndex_ = + (goog.dom.animationFrame.doubleBufferIndex_ + 1) % 2; + + var task; + + // Run all the measure tasks first. + for (var i = 0; i < taskLength; ++i) { + task = tasksArray[i]; + var measureTask = task.measureTask; + task.isScheduled = false; + if (measureTask.fn) { + // TODO (perumaal): Handle any exceptions thrown by the lambda. + measureTask.fn.apply(measureTask.context, task.args); + } + } + + // Run the mutate tasks next. + for (var i = 0; i < taskLength; ++i) { + task = tasksArray[i]; + var mutateTask = task.mutateTask; + task.isScheduled = false; + if (mutateTask.fn) { + // TODO (perumaal): Handle any exceptions thrown by the lambda. + mutateTask.fn.apply(mutateTask.context, task.args); + } + + // Clear state for next vsync. + task.state = {}; + } + + // Clear the tasks array as we have finished processing all the tasks. + tasksArray.length = 0; + goog.dom.animationFrame.running_ = false; +}; + + +/** + * @return {boolean} Whether the animationframe is currently running. For use + * by callers who need not to delay tasks scheduled during runTasks_ for an + * additional frame. + */ +goog.dom.animationFrame.isRunning = function() { + return goog.dom.animationFrame.running_; +}; + + +/** + * Request {@see goog.dom.animationFrame.runTasks_} to be called upon the + * next animation frame if we haven't done so already. + * @private + */ +goog.dom.animationFrame.requestAnimationFrame_ = function() { + if (goog.dom.animationFrame.requestedFrame_) { + return; + } + goog.dom.animationFrame.requestedFrame_ = true; + window.requestAnimationFrame(goog.dom.animationFrame.runTasks_); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe_test.js b/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe_test.js new file mode 100644 index 0000000000..a716cc1263 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/animationframe/animationframe_test.js @@ -0,0 +1,265 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tests for goog.dom.animationFrame. + */ + +goog.setTestOnly(); + +goog.require('goog.dom.animationFrame'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); + + +var NEXT_FRAME = goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT; +var mockClock; +var t0, t1; +var result; + +function setUp() { + mockClock = new goog.testing.MockClock(true); + result = ''; + t0 = goog.dom.animationFrame.createTask({ + measure: function() { + result += 'me0'; + }, + mutate: function() { + result += 'mu0'; + } + }); + t1 = goog.dom.animationFrame.createTask({ + measure: function() { + result += 'me1'; + }, + mutate: function() { + result += 'mu1'; + } + }); + assertEquals('', result); +} + +function tearDown() { + mockClock.dispose(); +} + +function testCreateTask_one() { + t0(); + assertEquals('', result); + mockClock.tick(NEXT_FRAME); + assertEquals('me0mu0', result); + mockClock.tick(NEXT_FRAME); + assertEquals('me0mu0', result); + t0(); + t0(); // Should do nothing. + mockClock.tick(NEXT_FRAME); + assertEquals('me0mu0me0mu0', result); +} + +function testCreateTask_onlyMutate() { + t0 = goog.dom.animationFrame.createTask({ + mutate: function() { + result += 'mu0'; + } + }); + t0(); + assertEquals('', result); + mockClock.tick(NEXT_FRAME); + assertEquals('mu0', result); +} + +function testCreateTask_onlyMeasure() { + t0 = goog.dom.animationFrame.createTask({ + mutate: function() { + result += 'me0'; + } + }); + t0(); + assertEquals('', result); + mockClock.tick(NEXT_FRAME); + assertEquals('me0', result); +} + +function testCreateTask_two() { + t0(); + t1(); + assertEquals('', result); + mockClock.tick(NEXT_FRAME); + assertEquals('me0me1mu0mu1', result); + mockClock.tick(NEXT_FRAME); + assertEquals('me0me1mu0mu1', result); + t0(); + t1(); + t0(); + t1(); + mockClock.tick(NEXT_FRAME); + assertEquals('me0me1mu0mu1me0me1mu0mu1', result); +} + +function testCreateTask_recurse() { + var stop = false; + var recurse = goog.dom.animationFrame.createTask({ + measure: function() { + if (!stop) { + recurse(); + } + result += 're0'; + }, + mutate: function() { + result += 'ru0'; + } + }); + recurse(); + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0', result); + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0re0ru0', result); + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0re0ru0re0ru0', result); + t0(); + stop = true; + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result); + + // Recursion should have stopped now. + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result); + assertFalse(goog.dom.animationFrame.requestedFrame_); + mockClock.tick(NEXT_FRAME); + assertEquals('re0ru0re0ru0re0ru0re0me0ru0mu0', result); + assertFalse(goog.dom.animationFrame.requestedFrame_); +} + +function testCreateTask_recurseTwoMethodsWithState() { + var stop = false; + var recurse1 = goog.dom.animationFrame.createTask({ + measure: function(state) { + if (!stop) { + recurse2(); + } + result += 'r1e0'; + state.text = 'T0'; + }, + mutate: function(state) { + result += 'r1u0' + state.text; + } + }); + var recurse2 = goog.dom.animationFrame.createTask({ + measure: function(state) { + if (!stop) { + recurse1(); + } + result += 'r2e0'; + state.text = 'T1'; + }, + mutate: function(state) { + result += 'r2u0' + state.text; + } + }); + + var taskLength = goog.dom.animationFrame.tasks_[0].length; + + recurse1(); + mockClock.tick(NEXT_FRAME); + // Only recurse1 executed. + assertEquals('r1e0r1u0T0', result); + + mockClock.tick(NEXT_FRAME); + // Recurse2 executed and queueup recurse1. + assertEquals('r1e0r1u0T0r2e0r2u0T1', result); + + mockClock.tick(NEXT_FRAME); + // Recurse1 executed and queueup recurse2. + assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0', result); + + stop = true; + mockClock.tick(NEXT_FRAME); + // Recurse2 executed and should have stopped. + assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result); + assertFalse(goog.dom.animationFrame.requestedFrame_); + + mockClock.tick(NEXT_FRAME); + assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result); + assertFalse(goog.dom.animationFrame.requestedFrame_); + + mockClock.tick(NEXT_FRAME); + assertEquals('r1e0r1u0T0r2e0r2u0T1r1e0r1u0T0r2e0r2u0T1', result); + assertFalse(goog.dom.animationFrame.requestedFrame_); +} + +function testCreateTask_args() { + var context = {context: true}; + var s = goog.dom.animationFrame.createTask({ + measure: function(state) { + assertEquals(context, this); + assertUndefined(state.foo); + state.foo = 'foo'; + }, + mutate: function(state) { + assertEquals(context, this); + result += state.foo; + } + }, context); + s(); + mockClock.tick(NEXT_FRAME); + assertEquals('foo', result); + + var dynamicContext = goog.dom.animationFrame.createTask({ + measure: function(state) { + assertEquals(context, this); + }, + mutate: function(state) { + assertEquals(context, this); + result += 'bar'; + } + }); + dynamicContext.call(context); + mockClock.tick(NEXT_FRAME); + assertEquals('foobar', result); + + var moreArgs = goog.dom.animationFrame.createTask({ + measure: function(event, state) { + assertEquals(context, this); + assertEquals('event', event); + state.baz = 'baz'; + }, + mutate: function(event, state) { + assertEquals('event', event); + assertEquals(context, this); + result += state.baz; + } + }); + moreArgs.call(context, 'event'); + mockClock.tick(NEXT_FRAME); + assertEquals('foobarbaz', result); +} + +function testIsRunning() { + var result = ''; + var task = goog.dom.animationFrame.createTask({ + measure: function() { + result += 'me'; + assertTrue(goog.dom.animationFrame.isRunning()); + }, + mutate: function() { + result += 'mu'; + assertTrue(goog.dom.animationFrame.isRunning()); + } + }); + task(); + assertFalse(goog.dom.animationFrame.isRunning()); + mockClock.tick(NEXT_FRAME); + assertFalse(goog.dom.animationFrame.isRunning()); + assertEquals('memu', result); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/animationframe/polyfill.js b/mapbox-gl/closure-library/closure/goog/dom/animationframe/polyfill.js new file mode 100644 index 0000000000..19e88668a7 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/animationframe/polyfill.js @@ -0,0 +1,61 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A polyfill for window.requestAnimationFrame and + * window.cancelAnimationFrame. + * Code based on https://gist.github.com/paulirish/1579671 + */ + +goog.provide('goog.dom.animationFrame.polyfill'); + + +/** + * @define {boolean} If true, will install the requestAnimationFrame polyfill. + */ +goog.define('goog.dom.animationFrame.polyfill.ENABLED', true); + + +/** + * Installs the requestAnimationFrame (and cancelAnimationFrame) polyfill. + */ +goog.dom.animationFrame.polyfill.install = + goog.dom.animationFrame.polyfill.ENABLED ? function() { + var vendors = ['ms', 'moz', 'webkit', 'o']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + + 'CancelAnimationFrame'] || + window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + var lastTime = 0; + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + lastTime = currTime + timeToCall; + return window.setTimeout(function() { + callback(currTime + timeToCall); + }, timeToCall); + }; + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + } +} : goog.nullFunction; diff --git a/mapbox-gl/closure-library/closure/goog/dom/annotate.js b/mapbox-gl/closure-library/closure/goog/dom/annotate.js new file mode 100644 index 0000000000..e75ecdb76e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/annotate.js @@ -0,0 +1,356 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Methods for annotating occurrences of query terms in text or + * in a DOM tree. Adapted from Gmail code. + * + */ + +goog.provide('goog.dom.annotate'); +goog.provide('goog.dom.annotate.AnnotateFn'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.safe'); +goog.require('goog.html.SafeHtml'); + + +/** + * A function that takes: + * (1) the number of the term that is "hit", + * (2) the HTML (search term) to be annotated, + * and returns the annotated term as an HTML. + * @typedef {function(number, !goog.html.SafeHtml): !goog.html.SafeHtml} + */ +goog.dom.annotate.AnnotateFn; + + +/** + * Calls {@code annotateFn} for each occurrence of a search term in text nodes + * under {@code node}. Returns the number of hits. + * + * @param {Node} node A DOM node. + * @param {Array>} terms + * An array of [searchTerm, matchWholeWordOnly] tuples. + * The matchWholeWordOnly value is a per-term attribute because some terms + * may be CJK, while others are not. (For correctness, matchWholeWordOnly + * should always be false for CJK terms.). + * @param {goog.dom.annotate.AnnotateFn} annotateFn + * @param {*=} opt_ignoreCase Whether to ignore the case of the query + * terms when looking for matches. + * @param {Array=} opt_classesToSkip Nodes with one of these CSS class + * names (and its descendants) will be skipped. + * @param {number=} opt_maxMs Number of milliseconds after which this function, + * if still annotating, should stop and return. + * + * @return {boolean} Whether any terms were annotated. + */ +goog.dom.annotate.annotateTerms = function(node, terms, annotateFn, + opt_ignoreCase, + opt_classesToSkip, + opt_maxMs) { + if (opt_ignoreCase) { + terms = goog.dom.annotate.lowercaseTerms_(terms); + } + var stopTime = opt_maxMs > 0 ? goog.now() + opt_maxMs : 0; + + return goog.dom.annotate.annotateTermsInNode_( + node, terms, annotateFn, opt_ignoreCase, opt_classesToSkip || [], + stopTime, 0); +}; + + +/** + * The maximum recursion depth allowed. Any DOM nodes deeper than this are + * ignored. + * @type {number} + * @private + */ +goog.dom.annotate.MAX_RECURSION_ = 200; + + +/** + * The node types whose descendants should not be affected by annotation. + * @private {Array} + */ +goog.dom.annotate.NODES_TO_SKIP_ = ['SCRIPT', 'STYLE', 'TEXTAREA']; + + +/** + * Recursive helper function. + * + * @param {Node} node A DOM node. + * @param {Array>} terms + * An array of [searchTerm, matchWholeWordOnly] tuples. + * The matchWholeWordOnly value is a per-term attribute because some terms + * may be CJK, while others are not. (For correctness, matchWholeWordOnly + * should always be false for CJK terms.). + * @param {goog.dom.annotate.AnnotateFn} annotateFn + * @param {*} ignoreCase Whether to ignore the case of the query terms + * when looking for matches. + * @param {Array} classesToSkip Nodes with one of these CSS class + * names will be skipped (as will their descendants). + * @param {number} stopTime Deadline for annotation operation (ignored if 0). + * @param {number} recursionLevel How deep this recursive call is; pass the + * value 0 in the initial call. + * @return {boolean} Whether any terms were annotated. + * @private + */ +goog.dom.annotate.annotateTermsInNode_ = + function(node, terms, annotateFn, ignoreCase, classesToSkip, + stopTime, recursionLevel) { + if ((stopTime > 0 && goog.now() >= stopTime) || + recursionLevel > goog.dom.annotate.MAX_RECURSION_) { + return false; + } + + var annotated = false; + + if (node.nodeType == goog.dom.NodeType.TEXT) { + var html = goog.dom.annotate.helpAnnotateText_(node.nodeValue, terms, + annotateFn, ignoreCase); + if (html != null) { + // Replace the text with the annotated html. First we put the html into + // a temporary node, to get its DOM structure. To avoid adding a wrapper + // element as a side effect, we'll only actually use the temporary node's + // children. + var tempNode = goog.dom.getOwnerDocument(node).createElement('SPAN'); + goog.dom.safe.setInnerHtml(tempNode, html); + + var parentNode = node.parentNode; + var nodeToInsert; + while ((nodeToInsert = tempNode.firstChild) != null) { + // Each parentNode.insertBefore call removes the inserted node from + // tempNode's list of children. + parentNode.insertBefore(nodeToInsert, node); + } + + parentNode.removeChild(node); + annotated = true; + } + } else if (node.hasChildNodes() && + !goog.array.contains(goog.dom.annotate.NODES_TO_SKIP_, + node.tagName)) { + var classes = node.className.split(/\s+/); + var skip = goog.array.some(classes, function(className) { + return goog.array.contains(classesToSkip, className); + }); + + if (!skip) { + ++recursionLevel; + var curNode = node.firstChild; + var numTermsAnnotated = 0; + while (curNode) { + var nextNode = curNode.nextSibling; + var curNodeAnnotated = goog.dom.annotate.annotateTermsInNode_( + curNode, terms, annotateFn, ignoreCase, classesToSkip, + stopTime, recursionLevel); + annotated = annotated || curNodeAnnotated; + curNode = nextNode; + } + } + } + + return annotated; +}; + + +/** + * Regular expression that matches non-word characters. + * + * Performance note: Testing a one-character string using this regex is as fast + * as the equivalent string test ("a-zA-Z0-9_".indexOf(c) < 0), give or take a + * few percent. (The regex is about 5% faster in IE 6 and about 4% slower in + * Firefox 1.5.) If performance becomes critical, it may be better to convert + * the character to a numerical char code and check whether it falls in the + * word character ranges. A quick test suggests that could be 33% faster. + * + * @type {RegExp} + * @private + */ +goog.dom.annotate.NONWORD_RE_ = /\W/; + + +/** + * Annotates occurrences of query terms in plain text. This process consists of + * identifying all occurrences of all query terms, calling a provided function + * to get the appropriate replacement HTML for each occurrence, and + * HTML-escaping all the text. + * + * @param {string} text The plain text to be searched. + * @param {Array>} terms An array of + * [{string} searchTerm, {boolean} matchWholeWordOnly] tuples. + * The matchWholeWordOnly value is a per-term attribute because some terms + * may be CJK, while others are not. (For correctness, matchWholeWordOnly + * should always be false for CJK terms.). + * @param {goog.dom.annotate.AnnotateFn} annotateFn + * @param {*=} opt_ignoreCase Whether to ignore the case of the query + * terms when looking for matches. + * @return {goog.html.SafeHtml} The HTML equivalent of {@code text} with terms + * annotated, or null if the text did not contain any of the terms. + */ +goog.dom.annotate.annotateText = function(text, terms, annotateFn, + opt_ignoreCase) { + if (opt_ignoreCase) { + terms = goog.dom.annotate.lowercaseTerms_(terms); + } + return goog.dom.annotate.helpAnnotateText_(text, terms, annotateFn, + opt_ignoreCase); +}; + + +/** + * Annotates occurrences of query terms in plain text. This process consists of + * identifying all occurrences of all query terms, calling a provided function + * to get the appropriate replacement HTML for each occurrence, and + * HTML-escaping all the text. + * + * @param {string} text The plain text to be searched. + * @param {Array>} terms An array of + * [{string} searchTerm, {boolean} matchWholeWordOnly] tuples. + * If {@code ignoreCase} is true, each search term must already be lowercase. + * The matchWholeWordOnly value is a per-term attribute because some terms + * may be CJK, while others are not. (For correctness, matchWholeWordOnly + * should always be false for CJK terms.). + * @param {goog.dom.annotate.AnnotateFn} annotateFn + * @param {*} ignoreCase Whether to ignore the case of the query terms + * when looking for matches. + * @return {goog.html.SafeHtml} The HTML equivalent of {@code text} with terms + * annotated, or null if the text did not contain any of the terms. + * @private + */ +goog.dom.annotate.helpAnnotateText_ = function(text, terms, annotateFn, + ignoreCase) { + var hit = false; + var resultHtml = null; + var textToSearch = ignoreCase ? text.toLowerCase() : text; + var textLen = textToSearch.length; + var numTerms = terms.length; + + // Each element will be an array of hit positions for the term. + var termHits = new Array(numTerms); + + // First collect all the hits into allHits. + for (var i = 0; i < numTerms; i++) { + var term = terms[i]; + var hits = []; + var termText = term[0]; + if (termText != '') { + var matchWholeWordOnly = term[1]; + var termLen = termText.length; + var pos = 0; + // Find each hit for term t and append to termHits. + while (pos < textLen) { + var hitPos = textToSearch.indexOf(termText, pos); + if (hitPos == -1) { + break; + } else { + var prevCharPos = hitPos - 1; + var nextCharPos = hitPos + termLen; + if (!matchWholeWordOnly || + ((prevCharPos < 0 || + goog.dom.annotate.NONWORD_RE_.test( + textToSearch.charAt(prevCharPos))) && + (nextCharPos >= textLen || + goog.dom.annotate.NONWORD_RE_.test( + textToSearch.charAt(nextCharPos))))) { + hits.push(hitPos); + hit = true; + } + pos = hitPos + termLen; + } + } + } + termHits[i] = hits; + } + + if (hit) { + var html = []; + var pos = 0; + + while (true) { + // First determine which of the n terms is the next hit. + var termIndexOfNextHit; + var posOfNextHit = -1; + + for (var i = 0; i < numTerms; i++) { + var hits = termHits[i]; + // pull off the position of the next hit of term t + // (it's always the first in the array because we're shifting + // hits off the front of the array as we process them) + // this is the next candidate to consider for the next overall hit + if (!goog.array.isEmpty(hits)) { + var hitPos = hits[0]; + + // Discard any hits embedded in the previous hit. + while (hitPos >= 0 && hitPos < pos) { + hits.shift(); + hitPos = goog.array.isEmpty(hits) ? -1 : hits[0]; + } + + if (hitPos >= 0 && (posOfNextHit < 0 || hitPos < posOfNextHit)) { + termIndexOfNextHit = i; + posOfNextHit = hitPos; + } + } + } + + // Quit if there are no more hits. + if (posOfNextHit < 0) break; + + // Remove the next hit from our hit list. + termHits[termIndexOfNextHit].shift(); + + // Append everything from the end of the last hit up to this one. + html.push(text.substr(pos, posOfNextHit - pos)); + + // Append the annotated term. + var termLen = terms[termIndexOfNextHit][0].length; + var termHtml = goog.html.SafeHtml.htmlEscape( + text.substr(posOfNextHit, termLen)); + html.push( + annotateFn(goog.asserts.assertNumber(termIndexOfNextHit), termHtml)); + + pos = posOfNextHit + termLen; + } + + // Append everything after the last hit. + html.push(text.substr(pos)); + return goog.html.SafeHtml.concat(html); + } else { + return null; + } +}; + + +/** + * Converts terms to lowercase. + * + * @param {Array>} terms An array of + * [{string} searchTerm, {boolean} matchWholeWordOnly] tuples. + * @return {!Array>} An array of + * [{string} searchTerm, {boolean} matchWholeWordOnly] tuples. + * @private + */ +goog.dom.annotate.lowercaseTerms_ = function(terms) { + var lowercaseTerms = []; + for (var i = 0; i < terms.length; ++i) { + var term = terms[i]; + lowercaseTerms[i] = [term[0].toLowerCase(), term[1]]; + } + return lowercaseTerms; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/annotate_test.html b/mapbox-gl/closure-library/closure/goog/dom/annotate_test.html new file mode 100644 index 0000000000..92bd82f6d2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/annotate_test.html @@ -0,0 +1,57 @@ + + + + + +Closure Unit Tests - goog.dom.annotate + + + + + +Tom & Jerry + + + + + + + + + + + + + + + + + +
This little piggyThat little piggy
This little piggyThat little piggy
This little piggyThat little Piggy
This little piggyThat little Piggy
+ +
+ + + + + + + Your browser cannot display this object. + +
+ + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/annotate_test.js b/mapbox-gl/closure-library/closure/goog/dom/annotate_test.js new file mode 100644 index 0000000000..2c2dfa6a7e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/annotate_test.js @@ -0,0 +1,184 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.annotateTest'); +goog.setTestOnly('goog.dom.annotateTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.annotate'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.testing.jsunit'); + +var $ = goog.dom.getElement; + +var TEXT = 'This little piggy cried "Wee! Wee! Wee!" all the way home.'; + +function doAnnotation(termIndex, termHtml) { + return goog.html.SafeHtml.create('span', {'class': 'c' + termIndex}, + termHtml); +} + +// goog.dom.annotate.annotateText tests + +function testAnnotateText() { + var terms = [['pig', true]]; + var html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + assertEquals(null, html); + + terms = [['pig', false]]; + html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried ' + + '"Wee! Wee! Wee!" all the way home.', html); + + terms = [[' piggy ', true]]; + html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + assertEquals(null, html); + + terms = [[' piggy ', false]]; + html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried ' + + '"Wee! Wee! Wee!" all the way home.', html); + + terms = [['goose', true], ['piggy', true]]; + html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried ' + + '"Wee! Wee! Wee!" all the way home.', html); +} + +function testAnnotateTextHtmlEscaping() { + var terms = [['a', false]]; + var html = goog.dom.annotate.annotateText('&a', terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('&a', html); + + terms = [['a', false]]; + html = goog.dom.annotate.annotateText('a&', terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('a&', html); + + terms = [['&', false]]; + html = goog.dom.annotate.annotateText('&', terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('&', html); +} + +function testAnnotateTextIgnoreCase() { + var terms = [['wEe', true]]; + var html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation, true); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried "Wee! ' + + 'Wee! Wee!' + + '" all the way home.', html); + + terms = [['WEE!', true], ['HE', false]]; + html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation, true); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried "Wee! ' + + 'Wee! Wee!' + + '" all the way home.', html); +} + +function testAnnotateTextOverlappingTerms() { + var terms = [['tt', false], ['little', false]]; + var html = goog.dom.annotate.annotateText(TEXT, terms, doAnnotation); + html = goog.html.SafeHtml.unwrap(html); + assertEquals('This little piggy cried "Wee! ' + + 'Wee! Wee!" all the way home.', html); +} + +// goog.dom.annotate.annotateTerms tests + +function testAnnotateTerms() { + var terms = [['pig', true]]; + assertFalse(goog.dom.annotate.annotateTerms($('p'), terms, doAnnotation)); + assertEquals('Tom & Jerry', $('p').innerHTML); + + terms = [['Tom', true]]; + assertTrue(goog.dom.annotate.annotateTerms($('p'), terms, doAnnotation)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('p')); + assertEquals(1, spans.length); + assertEquals('Tom', spans[0].innerHTML); + assertEquals(' & Jerry', spans[0].nextSibling.nodeValue); +} + +function testAnnotateTermsInTable() { + var terms = [['pig', false]]; + assertTrue(goog.dom.annotate.annotateTerms($('q'), terms, doAnnotation)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('q')); + assertEquals(2, spans.length); + assertEquals('pig', spans[0].innerHTML); + assertEquals('gy', spans[0].nextSibling.nodeValue); + assertEquals('pig', spans[1].innerHTML); + assertEquals('I', spans[1].parentNode.tagName); +} + +function testAnnotateTermsWithClassExclusions() { + var terms = [['pig', false]]; + var classesToIgnore = ['s']; + assertTrue(goog.dom.annotate.annotateTerms($('r'), terms, doAnnotation, + false, classesToIgnore)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('r')); + assertEquals(1, spans.length); + assertEquals('pig', spans[0].innerHTML); + assertEquals('gy', spans[0].nextSibling.nodeValue); +} + +function testAnnotateTermsIgnoreCase() { + var terms1 = [['pig', false]]; + assertTrue(goog.dom.annotate.annotateTerms( + $('t'), terms1, doAnnotation, true)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('t')); + assertEquals(2, spans.length); + assertEquals('pig', spans[0].innerHTML); + assertEquals('gy', spans[0].nextSibling.nodeValue); + assertEquals('Pig', spans[1].innerHTML); + + var terms2 = [['Pig', false]]; + assertTrue(goog.dom.annotate.annotateTerms( + $('u'), terms2, doAnnotation, true)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('u')); + assertEquals(2, spans.length); + assertEquals('pig', spans[0].innerHTML); + assertEquals('gy', spans[0].nextSibling.nodeValue); + assertEquals('Pig', spans[1].innerHTML); +} + +function testAnnotateTermsInObject() { + var terms = [['object', true]]; + assertTrue(goog.dom.annotate.annotateTerms($('o'), terms, doAnnotation)); + var spans = goog.dom.getElementsByTagNameAndClass('SPAN', 'c0', $('o')); + assertEquals(1, spans.length); + assertEquals('object', spans[0].innerHTML); +} + +function testAnnotateTermsInScript() { + var terms = [['variable', true]]; + assertFalse(goog.dom.annotate.annotateTerms($('script'), terms, + doAnnotation)); +} + +function testAnnotateTermsInStyle() { + var terms = [['color', true]]; + assertFalse(goog.dom.annotate.annotateTerms($('style'), terms, + doAnnotation)); +} + +function testAnnotateTermsInHtmlComment() { + var terms = [['note', true]]; + assertFalse(goog.dom.annotate.annotateTerms($('comment'), terms, + doAnnotation)); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserfeature.js b/mapbox-gl/closure-library/closure/goog/dom/browserfeature.js new file mode 100644 index 0000000000..2c70cda4b9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserfeature.js @@ -0,0 +1,72 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Browser capability checks for the dom package. + * + */ + + +goog.provide('goog.dom.BrowserFeature'); + +goog.require('goog.userAgent'); + + +/** + * Enum of browser capabilities. + * @enum {boolean} + */ +goog.dom.BrowserFeature = { + /** + * Whether attributes 'name' and 'type' can be added to an element after it's + * created. False in Internet Explorer prior to version 9. + */ + CAN_ADD_NAME_OR_TYPE_ATTRIBUTES: !goog.userAgent.IE || + goog.userAgent.isDocumentModeOrHigher(9), + + /** + * Whether we can use element.children to access an element's Element + * children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment + * nodes in the collection.) + */ + CAN_USE_CHILDREN_ATTRIBUTE: !goog.userAgent.GECKO && !goog.userAgent.IE || + goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) || + goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'), + + /** + * Opera, Safari 3, and Internet Explorer 9 all support innerText but they + * include text nodes in script and style tags. Not document-mode-dependent. + */ + CAN_USE_INNER_TEXT: ( + goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')), + + /** + * MSIE, Opera, and Safari>=4 support element.parentElement to access an + * element's parent if it is an Element. + */ + CAN_USE_PARENT_ELEMENT_PROPERTY: goog.userAgent.IE || goog.userAgent.OPERA || + goog.userAgent.WEBKIT, + + /** + * Whether NoScope elements need a scoped element written before them in + * innerHTML. + * MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1 + */ + INNER_HTML_NEEDS_SCOPED_ELEMENT: goog.userAgent.IE, + + /** + * Whether we use legacy IE range API. + */ + LEGACY_IE_RANGES: goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/abstractrange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/abstractrange.js new file mode 100644 index 0000000000..3956f3a5fc --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/abstractrange.js @@ -0,0 +1,350 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the browser range interface. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.AbstractRange'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.TextRangeIterator'); +goog.require('goog.iter'); +goog.require('goog.math.Coordinate'); +goog.require('goog.string'); +goog.require('goog.string.StringBuffer'); +goog.require('goog.userAgent'); + + + +/** + * The constructor for abstract ranges. Don't call this from subclasses. + * @constructor + */ +goog.dom.browserrange.AbstractRange = function() { +}; + + +/** + * @return {goog.dom.browserrange.AbstractRange} A clone of this range. + */ +goog.dom.browserrange.AbstractRange.prototype.clone = goog.abstractMethod; + + +/** + * Returns the browser native implementation of the range. Please refrain from + * using this function - if you find you need the range please add wrappers for + * the functionality you need rather than just using the native range. + * @return {Range|TextRange} The browser native range object. + */ +goog.dom.browserrange.AbstractRange.prototype.getBrowserRange = + goog.abstractMethod; + + +/** + * Returns the deepest node in the tree that contains the entire range. + * @return {Node} The deepest node that contains the entire range. + */ +goog.dom.browserrange.AbstractRange.prototype.getContainer = + goog.abstractMethod; + + +/** + * Returns the node the range starts in. + * @return {Node} The element or text node the range starts in. + */ +goog.dom.browserrange.AbstractRange.prototype.getStartNode = + goog.abstractMethod; + + +/** + * Returns the offset into the node the range starts in. + * @return {number} The offset into the node the range starts in. For text + * nodes, this is an offset into the node value. For elements, this is + * an offset into the childNodes array. + */ +goog.dom.browserrange.AbstractRange.prototype.getStartOffset = + goog.abstractMethod; + + +/** + * @return {goog.math.Coordinate} The coordinate of the selection start node + * and offset. + */ +goog.dom.browserrange.AbstractRange.prototype.getStartPosition = function() { + goog.asserts.assert(this.range_.getClientRects, + 'Getting selection coordinates is not supported.'); + + var rects = this.range_.getClientRects(); + if (rects.length) { + return new goog.math.Coordinate(rects[0]['left'], rects[0]['top']); + } + return null; +}; + + +/** + * Returns the node the range ends in. + * @return {Node} The element or text node the range ends in. + */ +goog.dom.browserrange.AbstractRange.prototype.getEndNode = + goog.abstractMethod; + + +/** + * Returns the offset into the node the range ends in. + * @return {number} The offset into the node the range ends in. For text + * nodes, this is an offset into the node value. For elements, this is + * an offset into the childNodes array. + */ +goog.dom.browserrange.AbstractRange.prototype.getEndOffset = + goog.abstractMethod; + + +/** + * @return {goog.math.Coordinate} The coordinate of the selection end node + * and offset. + */ +goog.dom.browserrange.AbstractRange.prototype.getEndPosition = function() { + goog.asserts.assert(this.range_.getClientRects, + 'Getting selection coordinates is not supported.'); + + var rects = this.range_.getClientRects(); + if (rects.length) { + var lastRect = goog.array.peek(rects); + return new goog.math.Coordinate(lastRect['right'], lastRect['bottom']); + } + return null; +}; + + +/** + * Compares one endpoint of this range with the endpoint of another browser + * native range object. + * @param {Range|TextRange} range The browser native range to compare against. + * @param {goog.dom.RangeEndpoint} thisEndpoint The endpoint of this range + * to compare with. + * @param {goog.dom.RangeEndpoint} otherEndpoint The endpoint of the other + * range to compare with. + * @return {number} 0 if the endpoints are equal, negative if this range + * endpoint comes before the other range endpoint, and positive otherwise. + */ +goog.dom.browserrange.AbstractRange.prototype.compareBrowserRangeEndpoints = + goog.abstractMethod; + + +/** + * Tests if this range contains the given range. + * @param {goog.dom.browserrange.AbstractRange} abstractRange The range to test. + * @param {boolean=} opt_allowPartial If not set or false, the range must be + * entirely contained in the selection for this function to return true. + * @return {boolean} Whether this range contains the given range. + */ +goog.dom.browserrange.AbstractRange.prototype.containsRange = + function(abstractRange, opt_allowPartial) { + // IE sometimes misreports the boundaries for collapsed ranges. So if the + // other range is collapsed, make sure the whole range is contained. This is + // logically equivalent, and works around IE's bug. + var checkPartial = opt_allowPartial && !abstractRange.isCollapsed(); + + var range = abstractRange.getBrowserRange(); + var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END; + /** @preserveTry */ + try { + if (checkPartial) { + // There are two ways to not overlap. Being before, and being after. + // Before is represented by this.end before range.start: comparison < 0. + // After is represented by this.start after range.end: comparison > 0. + // The below is the negation of not overlapping. + return this.compareBrowserRangeEndpoints(range, end, start) >= 0 && + this.compareBrowserRangeEndpoints(range, start, end) <= 0; + + } else { + // Return true if this range bounds the parameter range from both sides. + return this.compareBrowserRangeEndpoints(range, end, end) >= 0 && + this.compareBrowserRangeEndpoints(range, start, start) <= 0; + } + } catch (e) { + if (!goog.userAgent.IE) { + throw e; + } + // IE sometimes throws exceptions when one range is invalid, i.e. points + // to a node that has been removed from the document. Return false in this + // case. + return false; + } +}; + + +/** + * Tests if this range contains the given node. + * @param {Node} node The node to test. + * @param {boolean=} opt_allowPartial If not set or false, the node must be + * entirely contained in the selection for this function to return true. + * @return {boolean} Whether this range contains the given node. + */ +goog.dom.browserrange.AbstractRange.prototype.containsNode = function(node, + opt_allowPartial) { + return this.containsRange( + goog.dom.browserrange.createRangeFromNodeContents(node), + opt_allowPartial); +}; + + +/** + * Tests if the selection is collapsed - i.e. is just a caret. + * @return {boolean} Whether the range is collapsed. + */ +goog.dom.browserrange.AbstractRange.prototype.isCollapsed = + goog.abstractMethod; + + +/** + * @return {string} The text content of the range. + */ +goog.dom.browserrange.AbstractRange.prototype.getText = + goog.abstractMethod; + + +/** + * Returns the HTML fragment this range selects. This is slow on all browsers. + * @return {string} HTML fragment of the range, does not include context + * containing elements. + */ +goog.dom.browserrange.AbstractRange.prototype.getHtmlFragment = function() { + var output = new goog.string.StringBuffer(); + goog.iter.forEach(this, function(node, ignore, it) { + if (node.nodeType == goog.dom.NodeType.TEXT) { + output.append(goog.string.htmlEscape(node.nodeValue.substring( + it.getStartTextOffset(), it.getEndTextOffset()))); + } else if (node.nodeType == goog.dom.NodeType.ELEMENT) { + if (it.isEndTag()) { + if (goog.dom.canHaveChildren(node)) { + output.append(''); + } + } else { + var shallow = node.cloneNode(false); + var html = goog.dom.getOuterHtml(shallow); + if (goog.userAgent.IE && node.tagName == goog.dom.TagName.LI) { + // For an LI, IE just returns "
  • " with no closing tag + output.append(html); + } else { + var index = html.lastIndexOf('<'); + output.append(index ? html.substr(0, index) : html); + } + } + } + }, this); + + return output.toString(); +}; + + +/** + * Returns valid HTML for this range. This is fast on IE, and semi-fast on + * other browsers. + * @return {string} Valid HTML of the range, including context containing + * elements. + */ +goog.dom.browserrange.AbstractRange.prototype.getValidHtml = + goog.abstractMethod; + + +/** + * Returns a RangeIterator over the contents of the range. Regardless of the + * direction of the range, the iterator will move in document order. + * @param {boolean=} opt_keys Unused for this iterator. + * @return {!goog.dom.RangeIterator} An iterator over tags in the range. + */ +goog.dom.browserrange.AbstractRange.prototype.__iterator__ = function( + opt_keys) { + return new goog.dom.TextRangeIterator(this.getStartNode(), + this.getStartOffset(), this.getEndNode(), this.getEndOffset()); +}; + + +// SELECTION MODIFICATION + + +/** + * Set this range as the selection in its window. + * @param {boolean=} opt_reverse Whether to select the range in reverse, + * if possible. + */ +goog.dom.browserrange.AbstractRange.prototype.select = + goog.abstractMethod; + + +/** + * Removes the contents of the range from the document. As a side effect, the + * selection will be collapsed. The behavior of content removal is normalized + * across browsers. For instance, IE sometimes creates extra text nodes that + * a W3C browser does not. That behavior is corrected for. + */ +goog.dom.browserrange.AbstractRange.prototype.removeContents = + goog.abstractMethod; + + +/** + * Surrounds the text range with the specified element (on Mozilla) or with a + * clone of the specified element (on IE). Returns a reference to the + * surrounding element if the operation was successful; returns null if the + * operation failed. + * @param {Element} element The element with which the selection is to be + * surrounded. + * @return {Element} The surrounding element (same as the argument on Mozilla, + * but not on IE), or null if unsuccessful. + */ +goog.dom.browserrange.AbstractRange.prototype.surroundContents = + goog.abstractMethod; + + +/** + * Inserts a node before (or after) the range. The range may be disrupted + * beyond recovery because of the way this splits nodes. + * @param {Node} node The node to insert. + * @param {boolean} before True to insert before, false to insert after. + * @return {Node} The node added to the document. This may be different + * than the node parameter because on IE we have to clone it. + */ +goog.dom.browserrange.AbstractRange.prototype.insertNode = + goog.abstractMethod; + + +/** + * Surrounds this range with the two given nodes. The range may be disrupted + * beyond recovery because of the way this splits nodes. + * @param {Element} startNode The node to insert at the start. + * @param {Element} endNode The node to insert at the end. + */ +goog.dom.browserrange.AbstractRange.prototype.surroundWithNodes = + goog.abstractMethod; + + +/** + * Collapses the range to one of its boundary points. + * @param {boolean} toStart Whether to collapse to the start of the range. + */ +goog.dom.browserrange.AbstractRange.prototype.collapse = + goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange.js new file mode 100644 index 0000000000..0cd70e7cf9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange.js @@ -0,0 +1,149 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the browser range namespace and interface, as + * well as several useful utility functions. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + * + * @supported IE6, IE7, FF1.5+, Safari. + */ + + +goog.provide('goog.dom.browserrange'); +goog.provide('goog.dom.browserrange.Error'); + +goog.require('goog.dom'); +goog.require('goog.dom.BrowserFeature'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.browserrange.GeckoRange'); +goog.require('goog.dom.browserrange.IeRange'); +goog.require('goog.dom.browserrange.OperaRange'); +goog.require('goog.dom.browserrange.W3cRange'); +goog.require('goog.dom.browserrange.WebKitRange'); +goog.require('goog.userAgent'); + + +/** + * Common error constants. + * @enum {string} + */ +goog.dom.browserrange.Error = { + NOT_IMPLEMENTED: 'Not Implemented' +}; + + +// NOTE(robbyw): While it would be nice to eliminate the duplicate switches +// below, doing so uncovers bugs in the JsCompiler in which +// necessary code is stripped out. + + +/** + * Static method that returns the proper type of browser range. + * @param {Range|TextRange} range A browser range object. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRange = function(range) { + if (goog.dom.BrowserFeature.LEGACY_IE_RANGES) { + return new goog.dom.browserrange.IeRange( + /** @type {TextRange} */ (range), + goog.dom.getOwnerDocument(range.parentElement())); + } else if (goog.userAgent.WEBKIT) { + return new goog.dom.browserrange.WebKitRange( + /** @type {Range} */ (range)); + } else if (goog.userAgent.GECKO) { + return new goog.dom.browserrange.GeckoRange( + /** @type {Range} */ (range)); + } else if (goog.userAgent.OPERA) { + return new goog.dom.browserrange.OperaRange( + /** @type {Range} */ (range)); + } else { + // Default other browsers, including Opera, to W3c ranges. + return new goog.dom.browserrange.W3cRange( + /** @type {Range} */ (range)); + } +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRangeFromNodeContents = function(node) { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + return goog.dom.browserrange.IeRange.createFromNodeContents(node); + } else if (goog.userAgent.WEBKIT) { + return goog.dom.browserrange.WebKitRange.createFromNodeContents(node); + } else if (goog.userAgent.GECKO) { + return goog.dom.browserrange.GeckoRange.createFromNodeContents(node); + } else if (goog.userAgent.OPERA) { + return goog.dom.browserrange.OperaRange.createFromNodeContents(node); + } else { + // Default other browsers to W3c ranges. + return goog.dom.browserrange.W3cRange.createFromNodeContents(node); + } +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. This is + * either the index into the childNodes array for element startNodes or + * the index into the character array for text startNodes. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. This is + * either the index into the childNodes array for element endNodes or + * the index into the character array for text endNodes. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.createRangeFromNodes = function(startNode, startOffset, + endNode, endOffset) { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + return goog.dom.browserrange.IeRange.createFromNodes(startNode, startOffset, + endNode, endOffset); + } else if (goog.userAgent.WEBKIT) { + return goog.dom.browserrange.WebKitRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else if (goog.userAgent.GECKO) { + return goog.dom.browserrange.GeckoRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else if (goog.userAgent.OPERA) { + return goog.dom.browserrange.OperaRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } else { + // Default other browsers to W3c ranges. + return goog.dom.browserrange.W3cRange.createFromNodes(startNode, + startOffset, endNode, endOffset); + } +}; + + +/** + * Tests whether the given node can contain a range end point. + * @param {Node} node The node to check. + * @return {boolean} Whether the given node can contain a range end point. + */ +goog.dom.browserrange.canContainRangeEndpoint = function(node) { + // NOTE(user, bloom): This is not complete, as divs with style - + // 'display:inline-block' or 'position:absolute' can also not contain range + // endpoints. A more complete check is to see if that element can be partially + // selected (can be container) or not. + return goog.dom.canHaveChildren(node) || + node.nodeType == goog.dom.NodeType.TEXT; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.html b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.html new file mode 100644 index 0000000000..b96c9e9696 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.html @@ -0,0 +1,31 @@ + + + + + +Closure Unit Tests - goog.dom.browserrange + + + + +
    +
    Text
    abc
    def
    +
    abc
    +
    +
    Text that
    will be deleted
    +
    +
    Text Text
    +
    0123456789
    +
    0123456789
    0123456789
    +
    outer
    inner
    outer2
    +
    +

    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.js new file mode 100644 index 0000000000..797b21dade --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/browserrange_test.js @@ -0,0 +1,633 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.browserrangeTest'); +goog.setTestOnly('goog.dom.browserrangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.browserrange'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var test1; +var test2; +var cetest; +var empty; +var dynamic; +var onlybrdiv; + +function setUpPage() { + test1 = goog.dom.getElement('test1'); + test2 = goog.dom.getElement('test2'); + cetest = goog.dom.getElement('cetest'); + empty = goog.dom.getElement('empty'); + dynamic = goog.dom.getElement('dynamic'); + onlybrdiv = goog.dom.getElement('onlybr'); +} + +function testCreate() { + assertNotNull('Browser range object can be created for node', + goog.dom.browserrange.createRangeFromNodeContents(test1)); +} + +function testRangeEndPoints() { + var container = cetest.firstChild; + var range = goog.dom.browserrange.createRangeFromNodes( + container, 2, container, 2); + range.select(); + + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + var endNode = selRange.getEndNode(); + var startOffset = selRange.getStartOffset(); + var endOffset = selRange.getEndOffset(); + if (goog.userAgent.WEBKIT) { + assertEquals('Start node should have text: abc', + 'abc', startNode.nodeValue); + assertEquals('End node should have text: abc', 'abc', endNode.nodeValue); + assertEquals('Start offset should be 3', 3, startOffset); + assertEquals('End offset should be 3', 3, endOffset); + } else { + assertEquals('Start node should be the first div', container, startNode); + assertEquals('End node should be the first div', container, endNode); + assertEquals('Start offset should be 2', 2, startOffset); + assertEquals('End offset should be 2', 2, endOffset); + } +} + +function testCreateFromNodeContents() { + var range = goog.dom.Range.createFromNodeContents(onlybrdiv); + goog.testing.dom.assertRangeEquals(onlybrdiv, 0, onlybrdiv, 1, range); +} + +function normalizeHtml(str) { + return str.toLowerCase().replace(/[\n\r\f"]/g, ''); +} + +// TODO(robbyw): We really need tests for (and code fixes for) +// createRangeFromNodes in the following cases: +// * BR boundary (before + after) + +function testCreateFromNodes() { + var start = test1.firstChild; + var range = goog.dom.browserrange.createRangeFromNodes(start, 2, + test2.firstChild, 2); + assertNotNull('Browser range object can be created for W3C node range', + range); + + assertEquals('Start node should be selected at start endpoint', start, + range.getStartNode()); + assertEquals('Selection should start at offset 2', 2, + range.getStartOffset()); + + assertEquals('Text node should be selected at end endpoint', + test2.firstChild, range.getEndNode()); + assertEquals('Selection should end at offset 2', 2, range.getEndOffset()); + + assertTrue('Text content should be "xt\\s*ab"', + /xt\s*ab/.test(range.getText())); + assertFalse('Nodes range is not collapsed', range.isCollapsed()); + assertEquals('Should contain correct html fragment', + 'xt
  • ab', + normalizeHtml(range.getHtmlFragment())); + assertEquals('Should contain correct valid html', + '
    xt
    ab
    ', + normalizeHtml(range.getValidHtml())); +} + + +function testTextNode() { + var range = goog.dom.browserrange.createRangeFromNodeContents( + test1.firstChild); + + assertEquals('Text node should be selected at start endpoint', 'Text', + range.getStartNode().nodeValue); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('Text node should be selected at end endpoint', 'Text', + range.getEndNode().nodeValue); + assertEquals('Selection should end at offset 4', 'Text'.length, + range.getEndOffset()); + + assertEquals('Container should be text node', goog.dom.NodeType.TEXT, + range.getContainer().nodeType); + + assertEquals('Text content should be "Text"', 'Text', range.getText()); + assertFalse('Text range is not collapsed', range.isCollapsed()); + assertEquals('Should contain correct html fragment', 'Text', + range.getHtmlFragment()); + assertEquals('Should contain correct valid html', + 'Text', range.getValidHtml()); + +} + +function testTextNodes() { + goog.dom.removeChildren(dynamic); + dynamic.appendChild(goog.dom.createTextNode('Part1')); + dynamic.appendChild(goog.dom.createTextNode('Part2')); + var range = goog.dom.browserrange.createRangeFromNodes( + dynamic.firstChild, 0, dynamic.lastChild, 5); + + assertEquals('Text node 1 should be selected at start endpoint', 'Part1', + range.getStartNode().nodeValue); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('Text node 2 should be selected at end endpoint', 'Part2', + range.getEndNode().nodeValue); + assertEquals('Selection should end at offset 5', 'Part2'.length, + range.getEndOffset()); + + assertEquals('Container should be DIV', goog.dom.TagName.DIV, + range.getContainer().tagName); + + assertEquals('Text content should be "Part1Part2"', 'Part1Part2', + range.getText()); + assertFalse('Text range is not collapsed', range.isCollapsed()); + assertEquals('Should contain correct html fragment', 'Part1Part2', + range.getHtmlFragment()); + assertEquals('Should contain correct valid html', + 'part1part2', + normalizeHtml(range.getValidHtml())); + +} + +function testDiv() { + var range = goog.dom.browserrange.createRangeFromNodeContents(test2); + + assertEquals('Text node "abc" should be selected at start endpoint', 'abc', + range.getStartNode().nodeValue); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('Text node "def" should be selected at end endpoint', 'def', + range.getEndNode().nodeValue); + assertEquals('Selection should end at offset 3', 'def'.length, + range.getEndOffset()); + + assertEquals('Container should be DIV', 'DIV', + range.getContainer().tagName); + + assertTrue('Div text content should be "abc\\s*def"', + /abc\s*def/.test(range.getText())); + assertEquals('Should contain correct html fragment', 'abc
    def', + normalizeHtml(range.getHtmlFragment())); + assertEquals('Should contain correct valid html', + '
    abc
    def
    ', + normalizeHtml(range.getValidHtml())); + assertFalse('Div range is not collapsed', range.isCollapsed()); +} + +function testEmptyNodeHtmlInsert() { + var range = goog.dom.browserrange.createRangeFromNodeContents(empty); + var html = 'hello'; + range.insertNode(goog.dom.htmlToDocumentFragment(html)); + assertEquals('Html is not inserted correctly', html, + normalizeHtml(empty.innerHTML)); + goog.dom.removeChildren(empty); +} + +function testEmptyNode() { + var range = goog.dom.browserrange.createRangeFromNodeContents(empty); + + assertEquals('DIV be selected at start endpoint', 'DIV', + range.getStartNode().tagName); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('DIV should be selected at end endpoint', 'DIV', + range.getEndNode().tagName); + assertEquals('Selection should end at offset 0', 0, + range.getEndOffset()); + + assertEquals('Container should be DIV', 'DIV', + range.getContainer().tagName); + + assertEquals('Empty text content should be ""', '', range.getText()); + assertTrue('Empty range is collapsed', range.isCollapsed()); + assertEquals('Should contain correct valid html', '
    ', + normalizeHtml(range.getValidHtml())); + assertEquals('Should contain no html fragment', '', + range.getHtmlFragment()); +} + + +function testRemoveContents() { + var outer = goog.dom.getElement('removeTest'); + var range = goog.dom.browserrange.createRangeFromNodeContents( + outer.firstChild); + + range.removeContents(); + + assertEquals('Removed range content should be ""', '', range.getText()); + assertTrue('Removed range is now collapsed', range.isCollapsed()); + assertEquals('Outer div has 1 child now', 1, outer.childNodes.length); + assertEquals('Inner div is empty', 0, outer.firstChild.childNodes.length); +} + + +function testRemoveContentsEmptyNode() { + var outer = goog.dom.getElement('removeTestEmptyNode'); + var range = goog.dom.browserrange.createRangeFromNodeContents( + outer); + + range.removeContents(); + + assertEquals('Removed range content should be ""', '', range.getText()); + assertTrue('Removed range is now collapsed', range.isCollapsed()); + assertEquals('Outer div should have 0 children now', + 0, outer.childNodes.length); +} + + +function testRemoveContentsSingleNode() { + var outer = goog.dom.getElement('removeTestSingleNode'); + var range = goog.dom.browserrange.createRangeFromNodeContents( + outer.firstChild); + + range.removeContents(); + + assertEquals('Removed range content should be ""', '', range.getText()); + assertTrue('Removed range is now collapsed', range.isCollapsed()); + assertEquals('', goog.dom.getTextContent(outer)); +} + + +function testRemoveContentsMidNode() { + var outer = goog.dom.getElement('removeTestMidNode'); + var textNode = outer.firstChild.firstChild; + var range = goog.dom.browserrange.createRangeFromNodes( + textNode, 1, textNode, 4); + + assertEquals('Previous range content should be "123"', '123', + range.getText()); + range.removeContents(); + + assertEquals('Removed range content should be "0456789"', '0456789', + goog.dom.getTextContent(outer)); +} + + +function testRemoveContentsMidMultipleNodes() { + var outer = goog.dom.getElement('removeTestMidMultipleNodes'); + var firstTextNode = outer.firstChild.firstChild; + var lastTextNode = outer.lastChild.firstChild; + var range = goog.dom.browserrange.createRangeFromNodes( + firstTextNode, 1, lastTextNode, 4); + + assertEquals('Previous range content', '1234567890123', + range.getText().replace(/\s/g, '')); + range.removeContents(); + + assertEquals('Removed range content should be "0456789"', '0456789', + goog.dom.getTextContent(outer).replace(/\s/g, '')); +} + + +function testRemoveDivCaretRange() { + var outer = goog.dom.getElement('sandbox'); + outer.innerHTML = '
    Test1
    '; + var range = goog.dom.browserrange.createRangeFromNodes( + outer.lastChild, 0, outer.lastChild, 0); + + range.removeContents(); + range.insertNode(goog.dom.createDom('span', undefined, 'Hello'), true); + + assertEquals('Resulting contents', 'Test1Hello', + goog.dom.getTextContent(outer).replace(/\s/g, '')); +} + + +function testCollapse() { + var range = goog.dom.browserrange.createRangeFromNodeContents(test2); + assertFalse('Div range is not collapsed', range.isCollapsed()); + range.collapse(); + assertTrue('Div range is collapsed after call to empty()', + range.isCollapsed()); + + range = goog.dom.browserrange.createRangeFromNodeContents(empty); + assertTrue('Empty range is collapsed', range.isCollapsed()); + range.collapse(); + assertTrue('Empty range is still collapsed', range.isCollapsed()); +} + + +function testIdWithSpecialCharacters() { + goog.dom.removeChildren(dynamic); + dynamic.appendChild(goog.dom.createTextNode('1')); + dynamic.appendChild(goog.dom.createDom('div', {id: '<>'})); + dynamic.appendChild(goog.dom.createTextNode('2')); + var range = goog.dom.browserrange.createRangeFromNodes( + dynamic.firstChild, 0, dynamic.lastChild, 1); + + // Difference in special character handling is ok. + assertContains('Should have correct html fragment', + normalizeHtml(range.getHtmlFragment()), + [ + '1
    >
    2', // IE + '1
    >
    2', // WebKit + '1
    2' // Others + ]); +} + +function testEndOfChildren() { + dynamic.innerHTML = + '123
    456
    text'; + var range = goog.dom.browserrange.createRangeFromNodes( + goog.dom.getElement('a'), 3, goog.dom.getElement('b'), 1); + assertEquals('Should have correct text.', 'text', range.getText()); +} + +function testEndOfDiv() { + dynamic.innerHTML = '
    abc
    def
    '; + var a = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes(a, 1, a, 1); + var expectedStartNode = a; + var expectedStartOffset = 1; + var expectedEndNode = a; + var expectedEndOffset = 1; + assertEquals('startNode is wrong', expectedStartNode, range.getStartNode()); + assertEquals('startOffset is wrong', + expectedStartOffset, range.getStartOffset()); + assertEquals('endNode is wrong', expectedEndNode, range.getEndNode()); + assertEquals('endOffset is wrong', expectedEndOffset, range.getEndOffset()); +} + +function testRangeEndingWithBR() { + dynamic.innerHTML = '123
    456
    '; + var spanElem = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes( + spanElem, 0, spanElem, 2); + var htmlText = range.getValidHtml().toLowerCase(); + assertContains('Should include BR in HTML.', 'br', htmlText); + assertEquals('Should have correct text.', '123', range.getText()); + + range.select(); + + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Start node should be span', spanElem, startNode); + } else { + assertEquals('Startnode should have text:123', + '123', startNode.nodeValue); + } + assertEquals('Startoffset should be 0', 0, selRange.getStartOffset()); + var endNode = selRange.getEndNode(); + assertEquals('Endnode should be span', spanElem, endNode); + assertEquals('Endoffset should be 2', 2, selRange.getEndOffset()); +} + +function testRangeEndingWithBR2() { + dynamic.innerHTML = '123
    '; + var spanElem = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes( + spanElem, 0, spanElem, 2); + var htmlText = range.getValidHtml().toLowerCase(); + assertContains('Should include BR in HTML.', 'br', htmlText); + assertEquals('Should have correct text.', '123', range.getText()); + + range.select(); + + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Start node should be span', spanElem, startNode); + } else { + assertEquals('Start node should have text:123', + '123', startNode.nodeValue); + } + assertEquals('Startoffset should be 0', 0, selRange.getStartOffset()); + var endNode = selRange.getEndNode(); + if (goog.userAgent.WEBKIT) { + assertEquals('Endnode should have text', '123', endNode.nodeValue); + assertEquals('Endoffset should be 3', 3, selRange.getEndOffset()); + } else { + assertEquals('Endnode should be span', spanElem, endNode); + assertEquals('Endoffset should be 2', 2, selRange.getEndOffset()); + } +} + +function testRangeEndingBeforeBR() { + dynamic.innerHTML = '123
    456
    '; + var spanElem = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes( + spanElem, 0, spanElem, 1); + var htmlText = range.getValidHtml().toLowerCase(); + assertNotContains('Should not include BR in HTML.', 'br', htmlText); + assertEquals('Should have correct text.', '123', range.getText()); + range.select(); + + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Start node should be span', spanElem, startNode); + } else { + assertEquals('Startnode should have text:123', + '123', startNode.nodeValue); + } + assertEquals('Startoffset should be 0', 0, selRange.getStartOffset()); + var endNode = selRange.getEndNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Endnode should be span', spanElem, endNode); + assertEquals('Endoffset should be 1', 1, selRange.getEndOffset()); + } else { + assertEquals('Endnode should have text:123', '123', endNode.nodeValue); + assertEquals('Endoffset should be 3', 3, selRange.getEndOffset()); + } +} + +function testRangeStartingWithBR() { + dynamic.innerHTML = '123
    456
    '; + var spanElem = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes( + spanElem, 1, spanElem, 3); + var htmlText = range.getValidHtml().toLowerCase(); + assertContains('Should include BR in HTML.', 'br', htmlText); + // Firefox returns '456' as the range text while IE returns '\r\n456'. + // Therefore skipping the text check. + + range.select(); + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + assertEquals('Start node should be span', spanElem, startNode); + assertEquals('Startoffset should be 1', 1, selRange.getStartOffset()); + var endNode = selRange.getEndNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Endnode should be span', spanElem, endNode); + assertEquals('Endoffset should be 3', 3, selRange.getEndOffset()); + } else { + assertEquals('Endnode should have text:456', '456', endNode.nodeValue); + assertEquals('Endoffset should be 3', 3, selRange.getEndOffset()); + } +} + +function testRangeStartingAfterBR() { + dynamic.innerHTML = '123
    4567
    '; + var spanElem = goog.dom.getElement('a'); + var range = goog.dom.browserrange.createRangeFromNodes( + spanElem, 2, spanElem, 3); + var htmlText = range.getValidHtml().toLowerCase(); + assertNotContains('Should not include BR in HTML.', 'br', htmlText); + assertEquals('Should have correct text.', '4567', range.getText()); + + range.select(); + + var selRange = goog.dom.Range.createFromWindow(); + var startNode = selRange.getStartNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Start node should be span', spanElem, startNode); + assertEquals('Startoffset should be 2', 2, selRange.getStartOffset()); + } else { + assertEquals('Startnode should have text:4567', + '4567', startNode.nodeValue); + assertEquals('Startoffset should be 0', 0, selRange.getStartOffset()); + } + var endNode = selRange.getEndNode(); + if (goog.userAgent.GECKO || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9))) { + assertEquals('Endnode should be span', spanElem, endNode); + assertEquals('Endoffset should be 3', 3, selRange.getEndOffset()); + } else { + assertEquals('Endnode should have text:4567', '4567', endNode.nodeValue); + assertEquals('Endoffset should be 4', 4, selRange.getEndOffset()); + } + +} + +function testCollapsedRangeBeforeBR() { + dynamic.innerHTML = '123
    456
    '; + var range = goog.dom.browserrange.createRangeFromNodes( + goog.dom.getElement('a'), 1, goog.dom.getElement('a'), 1); + // Firefox returns as the range HTML while IE returns + // empty string. Therefore skipping the HTML check. + assertEquals('Should have no text.', '', range.getText()); +} + +function testCollapsedRangeAfterBR() { + dynamic.innerHTML = '123
    456
    '; + var range = goog.dom.browserrange.createRangeFromNodes( + goog.dom.getElement('a'), 2, goog.dom.getElement('a'), 2); + // Firefox returns as the range HTML while IE returns + // empty string. Therefore skipping the HTML check. + assertEquals('Should have no text.', '', range.getText()); +} + +function testCompareBrowserRangeEndpoints() { + var outer = goog.dom.getElement('outer'); + var inner = goog.dom.getElement('inner'); + var range_outer = goog.dom.browserrange.createRangeFromNodeContents(outer); + var range_inner = goog.dom.browserrange.createRangeFromNodeContents(inner); + + assertEquals( + 'The start of the inner selection should be after the outer.', + 1, + range_inner.compareBrowserRangeEndpoints( + range_outer.getBrowserRange(), + goog.dom.RangeEndpoint.START, + goog.dom.RangeEndpoint.START)); + + assertEquals( + "The start of the inner selection should be before the outer's end.", + -1, + range_inner.compareBrowserRangeEndpoints( + range_outer.getBrowserRange(), + goog.dom.RangeEndpoint.START, + goog.dom.RangeEndpoint.END)); + + assertEquals( + "The end of the inner selection should be after the outer's start.", + 1, + range_inner.compareBrowserRangeEndpoints( + range_outer.getBrowserRange(), + goog.dom.RangeEndpoint.END, + goog.dom.RangeEndpoint.START)); + + assertEquals( + "The end of the inner selection should be before the outer's end.", + -1, + range_inner.compareBrowserRangeEndpoints( + range_outer.getBrowserRange(), + goog.dom.RangeEndpoint.END, + goog.dom.RangeEndpoint.END)); + +} + + +/** + * Regression test for a bug in IeRange.insertNode_ where if the node to be + * inserted was not an element (e.g. a text node), it would clone the node + * in the inserting process but return the original node instead of the newly + * created and inserted node. + */ +function testInsertNodeNonElement() { + dynamic.innerHTML = 'beforeafter'; + var range = goog.dom.browserrange.createRangeFromNodes( + dynamic.firstChild, 6, dynamic.firstChild, 6); + var newNode = goog.dom.createTextNode('INSERTED'); + var inserted = range.insertNode(newNode, false); + + assertEquals('Text should be inserted between "before" and "after"', + 'beforeINSERTEDafter', + goog.dom.getRawTextContent(dynamic)); + assertEquals('Node returned by insertNode() should be a child of the div' + + ' containing the text', + dynamic, + inserted.parentNode); +} + +function testSelectOverwritesOldSelection() { + goog.dom.browserrange.createRangeFromNodes(test1, 0, test1, 1).select(); + goog.dom.browserrange.createRangeFromNodes(test2, 0, test2, 1).select(); + assertEquals('The old selection must be replaced with the new one', + 'abc', goog.dom.Range.createFromWindow().getText()); +} + +// Following testcase is special for IE. The comparison of ranges created in +// testcases with a range over empty span using native inRange fails. So the +// fallback mechanism is needed. +function testGetContainerInTextNodesAroundEmptySpan() { + dynamic.innerHTML = 'abcdef'; + var abc = dynamic.firstChild; + var def = dynamic.lastChild; + + var range; + range = goog.dom.browserrange.createRangeFromNodes(abc, 1, abc, 1); + assertEquals('textNode abc should be the range container', + abc, range.getContainer()); + assertEquals('textNode abc should be the range start node', + abc, range.getStartNode()); + assertEquals('textNode abc should be the range end node', + abc, range.getEndNode()); + + range = goog.dom.browserrange.createRangeFromNodes(def, 1, def, 1); + assertEquals('textNode def should be the range container', + def, range.getContainer()); + assertEquals('textNode def should be the range start node', + def, range.getStartNode()); + assertEquals('textNode def should be the range end node', + def, range.getEndNode()); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/geckorange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/geckorange.js new file mode 100644 index 0000000000..b01f2dd436 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/geckorange.js @@ -0,0 +1,88 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the Gecko specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.GeckoRange'); + +goog.require('goog.dom.browserrange.W3cRange'); + + + +/** + * The constructor for Gecko specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.GeckoRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.GeckoRange, goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.GeckoRange} A Gecko range wrapper object. + */ +goog.dom.browserrange.GeckoRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.GeckoRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. + * @return {!goog.dom.browserrange.GeckoRange} A wrapper object. + */ +goog.dom.browserrange.GeckoRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.GeckoRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.GeckoRange.prototype.selectInternal = function( + selection, reversed) { + if (!reversed || this.isCollapsed()) { + // The base implementation for select() is more robust, and works fine for + // collapsed and forward ranges. This works around + // https://bugzilla.mozilla.org/show_bug.cgi?id=773137, and is tested by + // range_test.html's testFocusedElementDisappears. + goog.dom.browserrange.GeckoRange.base( + this, 'selectInternal', selection, reversed); + } else { + // Reversed selection -- start with a caret on the end node, and extend it + // back to the start. Unfortunately, collapse() fails when focus is + // invalid. + selection.collapse(this.getEndNode(), this.getEndOffset()); + selection.extend(this.getStartNode(), this.getStartOffset()); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/ierange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/ierange.js new file mode 100644 index 0000000000..00e6bba2ee --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/ierange.js @@ -0,0 +1,951 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the IE browser specific range wrapper. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.IeRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.browserrange.AbstractRange'); +goog.require('goog.log'); +goog.require('goog.string'); + + + +/** + * The constructor for IE specific browser ranges. + * @param {TextRange} range The range object. + * @param {Document} doc The document the range exists in. + * @constructor + * @extends {goog.dom.browserrange.AbstractRange} + * @final + */ +goog.dom.browserrange.IeRange = function(range, doc) { + /** + * The browser range object this class wraps. + * @type {TextRange} + * @private + */ + this.range_ = range; + + /** + * The document the range exists in. + * @type {Document} + * @private + */ + this.doc_ = doc; +}; +goog.inherits(goog.dom.browserrange.IeRange, + goog.dom.browserrange.AbstractRange); + + +/** + * Logging object. + * @type {goog.log.Logger} + * @private + */ +goog.dom.browserrange.IeRange.logger_ = + goog.log.getLogger('goog.dom.browserrange.IeRange'); + + +/** + * Returns a browser range spanning the given node's contents. + * @param {Node} node The node to select. + * @return {!TextRange} A browser range spanning the node's contents. + * @private + */ +goog.dom.browserrange.IeRange.getBrowserRangeForNode_ = function(node) { + var nodeRange = goog.dom.getOwnerDocument(node).body.createTextRange(); + if (node.nodeType == goog.dom.NodeType.ELEMENT) { + // Elements are easy. + nodeRange.moveToElementText(node); + // Note(user) : If there are no child nodes of the element, the + // range.htmlText includes the element's outerHTML. The range created above + // is not collapsed, and should be collapsed explicitly. + // Example : node =
    + // But if the node is sth like
    , it shouldnt be collapsed. + if (goog.dom.browserrange.canContainRangeEndpoint(node) && + !node.childNodes.length) { + nodeRange.collapse(false); + } + } else { + // Text nodes are hard. + // Compute the offset from the nearest element related position. + var offset = 0; + var sibling = node; + while (sibling = sibling.previousSibling) { + var nodeType = sibling.nodeType; + if (nodeType == goog.dom.NodeType.TEXT) { + offset += sibling.length; + } else if (nodeType == goog.dom.NodeType.ELEMENT) { + // Move to the space after this element. + nodeRange.moveToElementText(sibling); + break; + } + } + + if (!sibling) { + nodeRange.moveToElementText(node.parentNode); + } + + nodeRange.collapse(!sibling); + + if (offset) { + nodeRange.move('character', offset); + } + + nodeRange.moveEnd('character', node.length); + } + + return nodeRange; +}; + + +/** + * Returns a browser range spanning the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!TextRange} A browser range spanning the node's contents. + * @private + */ +goog.dom.browserrange.IeRange.getBrowserRangeForNodes_ = function(startNode, + startOffset, endNode, endOffset) { + // Create a range starting at the correct start position. + var child, collapse = false; + if (startNode.nodeType == goog.dom.NodeType.ELEMENT) { + if (startOffset > startNode.childNodes.length) { + goog.log.error(goog.dom.browserrange.IeRange.logger_, + 'Cannot have startOffset > startNode child count'); + } + child = startNode.childNodes[startOffset]; + collapse = !child; + startNode = child || startNode.lastChild || startNode; + startOffset = 0; + } + var leftRange = goog.dom.browserrange.IeRange. + getBrowserRangeForNode_(startNode); + + // This happens only when startNode is a text node. + if (startOffset) { + leftRange.move('character', startOffset); + } + + + // The range movements in IE are still an approximation to the standard W3C + // behavior, and IE has its trickery when it comes to htmlText and text + // properties of the range. So we short-circuit computation whenever we can. + if (startNode == endNode && startOffset == endOffset) { + leftRange.collapse(true); + return leftRange; + } + + // This can happen only when the startNode is an element, and there is no node + // at the given offset. We start at the last point inside the startNode in + // that case. + if (collapse) { + leftRange.collapse(false); + } + + // Create a range that ends at the right position. + collapse = false; + if (endNode.nodeType == goog.dom.NodeType.ELEMENT) { + if (endOffset > endNode.childNodes.length) { + goog.log.error(goog.dom.browserrange.IeRange.logger_, + 'Cannot have endOffset > endNode child count'); + } + child = endNode.childNodes[endOffset]; + endNode = child || endNode.lastChild || endNode; + endOffset = 0; + collapse = !child; + } + var rightRange = goog.dom.browserrange.IeRange. + getBrowserRangeForNode_(endNode); + rightRange.collapse(!collapse); + if (endOffset) { + rightRange.moveEnd('character', endOffset); + } + + // Merge and return. + leftRange.setEndPoint('EndToEnd', rightRange); + return leftRange; +}; + + +/** + * Create a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.IeRange} An IE range wrapper object. + */ +goog.dom.browserrange.IeRange.createFromNodeContents = function(node) { + var range = new goog.dom.browserrange.IeRange( + goog.dom.browserrange.IeRange.getBrowserRangeForNode_(node), + goog.dom.getOwnerDocument(node)); + + if (!goog.dom.browserrange.canContainRangeEndpoint(node)) { + range.startNode_ = range.endNode_ = range.parentNode_ = node.parentNode; + range.startOffset_ = goog.array.indexOf(range.parentNode_.childNodes, node); + range.endOffset_ = range.startOffset_ + 1; + } else { + // Note(user) : Emulate the behavior of W3CRange - Go to deepest possible + // range containers on both edges. It seems W3CRange did this to match the + // IE behavior, and now it is a circle. Changing W3CRange may break clients + // in all sorts of ways. + var tempNode, leaf = node; + while ((tempNode = leaf.firstChild) && + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + range.startNode_ = leaf; + range.startOffset_ = 0; + + leaf = node; + while ((tempNode = leaf.lastChild) && + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + range.endNode_ = leaf; + range.endOffset_ = leaf.nodeType == goog.dom.NodeType.ELEMENT ? + leaf.childNodes.length : leaf.length; + range.parentNode_ = node; + } + return range; +}; + + +/** + * Static method that returns the proper type of browser range. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.AbstractRange} A wrapper object. + */ +goog.dom.browserrange.IeRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + var range = new goog.dom.browserrange.IeRange( + goog.dom.browserrange.IeRange.getBrowserRangeForNodes_(startNode, + startOffset, endNode, endOffset), + goog.dom.getOwnerDocument(startNode)); + range.startNode_ = startNode; + range.startOffset_ = startOffset; + range.endNode_ = endNode; + range.endOffset_ = endOffset; + return range; +}; + + +// Even though goog.dom.TextRange does similar caching to below, keeping these +// caches allows for better performance in the get*Offset methods. + + +/** + * Lazy cache of the node containing the entire selection. + * @type {Node} + * @private + */ +goog.dom.browserrange.IeRange.prototype.parentNode_ = null; + + +/** + * Lazy cache of the node containing the start of the selection. + * @type {Node} + * @private + */ +goog.dom.browserrange.IeRange.prototype.startNode_ = null; + + +/** + * Lazy cache of the node containing the end of the selection. + * @type {Node} + * @private + */ +goog.dom.browserrange.IeRange.prototype.endNode_ = null; + + +/** + * Lazy cache of the offset in startNode_ where this range starts. + * @type {number} + * @private + */ +goog.dom.browserrange.IeRange.prototype.startOffset_ = -1; + + +/** + * Lazy cache of the offset in endNode_ where this range ends. + * @type {number} + * @private + */ +goog.dom.browserrange.IeRange.prototype.endOffset_ = -1; + + +/** + * @return {!goog.dom.browserrange.IeRange} A clone of this range. + * @override + */ +goog.dom.browserrange.IeRange.prototype.clone = function() { + var range = new goog.dom.browserrange.IeRange( + this.range_.duplicate(), this.doc_); + range.parentNode_ = this.parentNode_; + range.startNode_ = this.startNode_; + range.endNode_ = this.endNode_; + return range; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getBrowserRange = function() { + return this.range_; +}; + + +/** + * Clears the cached values for containers. + * @private + */ +goog.dom.browserrange.IeRange.prototype.clearCachedValues_ = function() { + this.parentNode_ = this.startNode_ = this.endNode_ = null; + this.startOffset_ = this.endOffset_ = -1; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getContainer = function() { + if (!this.parentNode_) { + var selectText = this.range_.text; + + // If the selection ends with spaces, we need to remove these to get the + // parent container of only the real contents. This is to get around IE's + // inconsistency where it selects the spaces after a word when you double + // click, but leaves out the spaces during execCommands. + var range = this.range_.duplicate(); + // We can't use goog.string.trimRight, as that will remove other whitespace + // too. + var rightTrimmedSelectText = selectText.replace(/ +$/, ''); + var numSpacesAtEnd = selectText.length - rightTrimmedSelectText.length; + if (numSpacesAtEnd) { + range.moveEnd('character', -numSpacesAtEnd); + } + + // Get the parent node. This should be the end, but alas, it is not. + var parent = range.parentElement(); + + var htmlText = range.htmlText; + var htmlTextLen = goog.string.stripNewlines(htmlText).length; + if (this.isCollapsed() && htmlTextLen > 0) { + return (this.parentNode_ = parent); + } + + // Deal with selection bug where IE thinks one of the selection's children + // is actually the selection's parent. Relies on the assumption that the + // HTML text of the parent container is longer than the length of the + // selection's HTML text. + + // Also note IE will sometimes insert \r and \n whitespace, which should be + // disregarded. Otherwise the loop may run too long and return wrong parent + while (htmlTextLen > goog.string.stripNewlines(parent.outerHTML).length) { + parent = parent.parentNode; + } + + // Deal with IE's selecting the outer tags when you double click + // If the innerText is the same, then we just want the inner node + while (parent.childNodes.length == 1 && + parent.innerText == goog.dom.browserrange.IeRange.getNodeText_( + parent.firstChild)) { + // A container should be an element which can have children or a text + // node. Elements like IMG, BR, etc. can not be containers. + if (!goog.dom.browserrange.canContainRangeEndpoint(parent.firstChild)) { + break; + } + parent = parent.firstChild; + } + + // If the selection is empty, we may need to do extra work to position it + // properly. + if (selectText.length == 0) { + parent = this.findDeepestContainer_(parent); + } + + this.parentNode_ = parent; + } + + return this.parentNode_; +}; + + +/** + * Helper method to find the deepest parent for this range, starting + * the search from {@code node}, which must contain the range. + * @param {Node} node The node to start the search from. + * @return {Node} The deepest parent for this range. + * @private + */ +goog.dom.browserrange.IeRange.prototype.findDeepestContainer_ = function(node) { + var childNodes = node.childNodes; + for (var i = 0, len = childNodes.length; i < len; i++) { + var child = childNodes[i]; + + if (goog.dom.browserrange.canContainRangeEndpoint(child)) { + var childRange = + goog.dom.browserrange.IeRange.getBrowserRangeForNode_(child); + var start = goog.dom.RangeEndpoint.START; + var end = goog.dom.RangeEndpoint.END; + + // There are two types of erratic nodes where the range over node has + // different htmlText than the node's outerHTML. + // Case 1 - A node with magic   child. In this case : + // nodeRange.htmlText shows   ('

     

    ), while + // node.outerHTML doesn't show the magic node (

    ). + // Case 2 - Empty span. In this case : + // node.outerHTML shows '' + // node.htmlText is just empty string ''. + var isChildRangeErratic = (childRange.htmlText != child.outerHTML); + + // Moreover the inRange comparison fails only when the + var isNativeInRangeErratic = this.isCollapsed() && isChildRangeErratic; + + // In case 2 mentioned above, childRange is also collapsed. So we need to + // compare start of this range with both start and end of child range. + var inChildRange = isNativeInRangeErratic ? + (this.compareBrowserRangeEndpoints(childRange, start, start) >= 0 && + this.compareBrowserRangeEndpoints(childRange, start, end) <= 0) : + this.range_.inRange(childRange); + if (inChildRange) { + return this.findDeepestContainer_(child); + } + } + } + + return node; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getStartNode = function() { + if (!this.startNode_) { + this.startNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.START); + if (this.isCollapsed()) { + this.endNode_ = this.startNode_; + } + } + return this.startNode_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getStartOffset = function() { + if (this.startOffset_ < 0) { + this.startOffset_ = this.getOffset_(goog.dom.RangeEndpoint.START); + if (this.isCollapsed()) { + this.endOffset_ = this.startOffset_; + } + } + return this.startOffset_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getEndNode = function() { + if (this.isCollapsed()) { + return this.getStartNode(); + } + if (!this.endNode_) { + this.endNode_ = this.getEndpointNode_(goog.dom.RangeEndpoint.END); + } + return this.endNode_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getEndOffset = function() { + if (this.isCollapsed()) { + return this.getStartOffset(); + } + if (this.endOffset_ < 0) { + this.endOffset_ = this.getOffset_(goog.dom.RangeEndpoint.END); + if (this.isCollapsed()) { + this.startOffset_ = this.endOffset_; + } + } + return this.endOffset_; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.compareBrowserRangeEndpoints = function( + range, thisEndpoint, otherEndpoint) { + return this.range_.compareEndPoints( + (thisEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End') + + 'To' + + (otherEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End'), + range); +}; + + +/** + * Recurses to find the correct node for the given endpoint. + * @param {goog.dom.RangeEndpoint} endpoint The endpoint to get the node for. + * @param {Node=} opt_node Optional node to start the search from. + * @return {Node} The deepest node containing the endpoint. + * @private + */ +goog.dom.browserrange.IeRange.prototype.getEndpointNode_ = function(endpoint, + opt_node) { + + /** @type {Node} */ + var node = opt_node || this.getContainer(); + + // If we're at a leaf in the DOM, we're done. + if (!node || !node.firstChild) { + return node; + } + + var start = goog.dom.RangeEndpoint.START, end = goog.dom.RangeEndpoint.END; + var isStartEndpoint = endpoint == start; + + // Find the first/last child that overlaps the selection. + // NOTE(user) : One of the children can be the magic   node. This + // node will have only nodeType property as valid and accessible. All other + // dom related properties like ownerDocument, parentNode, nextSibling etc + // cause error when accessed. Therefore use the for-loop on childNodes to + // iterate. + for (var j = 0, length = node.childNodes.length; j < length; j++) { + var i = isStartEndpoint ? j : length - j - 1; + var child = node.childNodes[i]; + var childRange; + try { + childRange = goog.dom.browserrange.createRangeFromNodeContents(child); + } catch (e) { + // If the child is the magic   node, then the above will throw + // error. The magic node exists only when editing using keyboard, so can + // not add any unit test. + continue; + } + var ieRange = childRange.getBrowserRange(); + + // Case 1 : Finding end points when this range is collapsed. + // Note that in case of collapsed range, getEnd{Node,Offset} call + // getStart{Node,Offset}. + if (this.isCollapsed()) { + // Handle situations where caret is not in a text node. In such cases, + // the adjacent child won't be a valid range endpoint container. + if (!goog.dom.browserrange.canContainRangeEndpoint(child)) { + // The following handles a scenario like

    [caret]
    , + // where point should be (div, 1). + if (this.compareBrowserRangeEndpoints(ieRange, start, start) == 0) { + this.startOffset_ = this.endOffset_ = i; + return node; + } + } else if (childRange.containsRange(this)) { + // For collapsed range, we should invert the containsRange check with + // childRange. + return this.getEndpointNode_(endpoint, child); + } + + // Case 2 - The first child encountered to have overlap this range is + // contained entirely in this range. + } else if (this.containsRange(childRange)) { + // If it is an element which can not be a range endpoint container, the + // current child offset can be used to deduce the endpoint offset. + if (!goog.dom.browserrange.canContainRangeEndpoint(child)) { + + // Container can't be any deeper, so current node is the container. + if (isStartEndpoint) { + this.startOffset_ = i; + } else { + this.endOffset_ = i + 1; + } + return node; + } + + // If child can contain range endpoints, recurse inside this child. + return this.getEndpointNode_(endpoint, child); + + // Case 3 - Partial non-adjacency overlap. + } else if (this.compareBrowserRangeEndpoints(ieRange, start, end) < 0 && + this.compareBrowserRangeEndpoints(ieRange, end, start) > 0) { + // If this child overlaps the selection partially, recurse down to find + // the first/last child the next level down that overlaps the selection + // completely. We do not consider edge-adjacency (== 0) as overlap. + return this.getEndpointNode_(endpoint, child); + } + + } + + // None of the children of this node overlapped the selection, that means + // the selection starts/ends in this node directly. + return node; +}; + + +/** + * Compares one endpoint of this range with the endpoint of a node. + * For internal methods, we should prefer this method to containsNode. + * containsNode has a lot of false negatives when we're dealing with + * {@code
    } tags. + * + * @param {Node} node The node to compare against. + * @param {goog.dom.RangeEndpoint} thisEndpoint The endpoint of this range + * to compare with. + * @param {goog.dom.RangeEndpoint} otherEndpoint The endpoint of the node + * to compare with. + * @return {number} 0 if the endpoints are equal, negative if this range + * endpoint comes before the other node endpoint, and positive otherwise. + * @private + */ +goog.dom.browserrange.IeRange.prototype.compareNodeEndpoints_ = + function(node, thisEndpoint, otherEndpoint) { + return this.range_.compareEndPoints( + (thisEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End') + + 'To' + + (otherEndpoint == goog.dom.RangeEndpoint.START ? 'Start' : 'End'), + goog.dom.browserrange.createRangeFromNodeContents(node). + getBrowserRange()); +}; + + +/** + * Returns the offset into the start/end container. + * @param {goog.dom.RangeEndpoint} endpoint The endpoint to get the offset for. + * @param {Node=} opt_container The container to get the offset relative to. + * Defaults to the value returned by getStartNode/getEndNode. + * @return {number} The offset. + * @private + */ +goog.dom.browserrange.IeRange.prototype.getOffset_ = function(endpoint, + opt_container) { + var isStartEndpoint = endpoint == goog.dom.RangeEndpoint.START; + var container = opt_container || + (isStartEndpoint ? this.getStartNode() : this.getEndNode()); + + if (container.nodeType == goog.dom.NodeType.ELEMENT) { + // Find the first/last child that overlaps the selection + var children = container.childNodes; + var len = children.length; + var edge = isStartEndpoint ? 0 : len - 1; + var sign = isStartEndpoint ? 1 : - 1; + + // We find the index in the child array of the endpoint of the selection. + for (var i = edge; i >= 0 && i < len; i += sign) { + var child = children[i]; + // Ignore the child nodes, which could be end point containers. + if (goog.dom.browserrange.canContainRangeEndpoint(child)) { + continue; + } + // Stop looping when we reach the edge of the selection. + var endPointCompare = + this.compareNodeEndpoints_(child, endpoint, endpoint); + if (endPointCompare == 0) { + return isStartEndpoint ? i : i + 1; + } + } + + // When starting from the end in an empty container, we erroneously return + // -1: fix this to return 0. + return i == -1 ? 0 : i; + } else { + // Get a temporary range object. + var range = this.range_.duplicate(); + + // Create a range that selects the entire container. + var nodeRange = goog.dom.browserrange.IeRange.getBrowserRangeForNode_( + container); + + // Now, intersect our range with the container range - this should give us + // the part of our selection that is in the container. + range.setEndPoint(isStartEndpoint ? 'EndToEnd' : 'StartToStart', nodeRange); + + var rangeLength = range.text.length; + return isStartEndpoint ? container.length - rangeLength : rangeLength; + } +}; + + +/** + * Returns the text of the given node. Uses IE specific properties. + * @param {Node} node The node to retrieve the text of. + * @return {string} The node's text. + * @private + */ +goog.dom.browserrange.IeRange.getNodeText_ = function(node) { + return node.nodeType == goog.dom.NodeType.TEXT ? + node.nodeValue : node.innerText; +}; + + +/** + * Tests whether this range is valid (i.e. whether its endpoints are still in + * the document). A range becomes invalid when, after this object was created, + * either one or both of its endpoints are removed from the document. Use of + * an invalid range can lead to runtime errors, particularly in IE. + * @return {boolean} Whether the range is valid. + */ +goog.dom.browserrange.IeRange.prototype.isRangeInDocument = function() { + var range = this.doc_.body.createTextRange(); + range.moveToElementText(this.doc_.body); + + return this.containsRange( + new goog.dom.browserrange.IeRange(range, this.doc_), true); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.isCollapsed = function() { + // Note(user) : The earlier implementation used (range.text == ''), but this + // fails when (range.htmlText == '
    ') + // Alternative: this.range_.htmlText == ''; + return this.range_.compareEndPoints('StartToEnd', this.range_) == 0; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getText = function() { + return this.range_.text; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.getValidHtml = function() { + return this.range_.htmlText; +}; + + +// SELECTION MODIFICATION + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.select = function(opt_reverse) { + // IE doesn't support programmatic reversed selections. + this.range_.select(); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.removeContents = function() { + // NOTE: Sometimes htmlText is non-empty, but the range is actually empty. + // TODO(gboyer): The htmlText check is probably unnecessary, but I left it in + // for paranoia. + if (!this.isCollapsed() && this.range_.htmlText) { + // Store some before-removal state. + var startNode = this.getStartNode(); + var endNode = this.getEndNode(); + var oldText = this.range_.text; + + // IE sometimes deletes nodes unrelated to the selection. This trick fixes + // that problem most of the time. Even though it looks like a no-op, it is + // somehow changing IE's internal state such that empty unrelated nodes are + // no longer deleted. + var clone = this.range_.duplicate(); + clone.moveStart('character', 1); + clone.moveStart('character', -1); + + // However, sometimes moving the start back and forth ends up changing the + // range. + // TODO(gboyer): This condition used to happen for empty ranges, but (1) + // never worked, and (2) the isCollapsed call should protect against empty + // ranges better than before. However, this is left for paranoia. + if (clone.text == oldText) { + this.range_ = clone; + } + + // Use the browser's native deletion code. + this.range_.text = ''; + this.clearCachedValues_(); + + // Unfortunately, when deleting a portion of a single text node, IE creates + // an extra text node unlike other browsers which just change the text in + // the node. We normalize for that behavior here, making IE behave like all + // the other browsers. + var newStartNode = this.getStartNode(); + var newStartOffset = this.getStartOffset(); + /** @preserveTry */ + try { + var sibling = startNode.nextSibling; + if (startNode == endNode && startNode.parentNode && + startNode.nodeType == goog.dom.NodeType.TEXT && + sibling && sibling.nodeType == goog.dom.NodeType.TEXT) { + startNode.nodeValue += sibling.nodeValue; + goog.dom.removeNode(sibling); + + // Make sure to reselect the appropriate position. + this.range_ = goog.dom.browserrange.IeRange.getBrowserRangeForNode_( + newStartNode); + this.range_.move('character', newStartOffset); + this.clearCachedValues_(); + } + } catch (e) { + // IE throws errors on orphaned nodes. + } + } +}; + + +/** + * @param {TextRange} range The range to get a dom helper for. + * @return {!goog.dom.DomHelper} A dom helper for the document the range + * resides in. + * @private + */ +goog.dom.browserrange.IeRange.getDomHelper_ = function(range) { + return goog.dom.getDomHelper(range.parentElement()); +}; + + +/** + * Pastes the given element into the given range, returning the resulting + * element. + * @param {TextRange} range The range to paste into. + * @param {Element} element The node to insert a copy of. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper object for the document + * the range resides in. + * @return {Element} The resulting copy of element. + * @private + */ +goog.dom.browserrange.IeRange.pasteElement_ = function(range, element, + opt_domHelper) { + opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_( + range); + + // Make sure the node has a unique id. + var id; + var originalId = id = element.id; + if (!id) { + id = element.id = goog.string.createUniqueString(); + } + + // Insert (a clone of) the node. + range.pasteHTML(element.outerHTML); + + // Pasting the outerHTML of the modified element into the document creates + // a clone of the element argument. We want to return a reference to the + // clone, not the original. However we need to remove the temporary ID + // first. + element = opt_domHelper.getElement(id); + + // If element is null here, we failed. + if (element) { + if (!originalId) { + element.removeAttribute('id'); + } + } + + return element; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.surroundContents = function(element) { + // Make sure the element is detached from the document. + goog.dom.removeNode(element); + + // IE more or less guarantees that range.htmlText is well-formed & valid. + element.innerHTML = this.range_.htmlText; + element = goog.dom.browserrange.IeRange.pasteElement_(this.range_, element); + + // If element is null here, we failed. + if (element) { + this.range_.moveToElementText(element); + } + + this.clearCachedValues_(); + + return element; +}; + + +/** + * Internal handler for inserting a node. + * @param {TextRange} clone A clone of this range's browser range object. + * @param {Node} node The node to insert. + * @param {boolean} before Whether to insert the node before or after the range. + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use. + * @return {Node} The resulting copy of node. + * @private + */ +goog.dom.browserrange.IeRange.insertNode_ = function(clone, node, + before, opt_domHelper) { + // Get a DOM helper. + opt_domHelper = opt_domHelper || goog.dom.browserrange.IeRange.getDomHelper_( + clone); + + // If it's not an element, wrap it in one. + var isNonElement; + if (node.nodeType != goog.dom.NodeType.ELEMENT) { + isNonElement = true; + node = opt_domHelper.createDom(goog.dom.TagName.DIV, null, node); + } + + clone.collapse(before); + node = goog.dom.browserrange.IeRange.pasteElement_(clone, + /** @type {!Element} */ (node), opt_domHelper); + + // If we didn't want an element, unwrap the element and return the node. + if (isNonElement) { + // pasteElement_() may have returned a copy of the wrapper div, and the + // node it wraps could also be a new copy. So we must extract that new + // node from the new wrapper. + var newNonElement = node.firstChild; + opt_domHelper.flattenElement(node); + node = newNonElement; + } + + return node; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.insertNode = function(node, before) { + var output = goog.dom.browserrange.IeRange.insertNode_( + this.range_.duplicate(), node, before); + this.clearCachedValues_(); + return output; +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.surroundWithNodes = function( + startNode, endNode) { + var clone1 = this.range_.duplicate(); + var clone2 = this.range_.duplicate(); + goog.dom.browserrange.IeRange.insertNode_(clone1, startNode, true); + goog.dom.browserrange.IeRange.insertNode_(clone2, endNode, false); + + this.clearCachedValues_(); +}; + + +/** @override */ +goog.dom.browserrange.IeRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart); + + if (toStart) { + this.endNode_ = this.startNode_; + this.endOffset_ = this.startOffset_; + } else { + this.startNode_ = this.endNode_; + this.startOffset_ = this.endOffset_; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/operarange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/operarange.js new file mode 100644 index 0000000000..f277dc1b94 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/operarange.js @@ -0,0 +1,84 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the Opera specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + */ + + +goog.provide('goog.dom.browserrange.OperaRange'); + +goog.require('goog.dom.browserrange.W3cRange'); + + + +/** + * The constructor for Opera specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.OperaRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.OperaRange, goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.OperaRange} A Opera range wrapper object. + */ +goog.dom.browserrange.OperaRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.OperaRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. + * @return {!goog.dom.browserrange.OperaRange} A wrapper object. + */ +goog.dom.browserrange.OperaRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.OperaRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.OperaRange.prototype.selectInternal = function( + selection, reversed) { + // Avoid using addRange as we have to removeAllRanges first, which + // blurs editable fields in Opera. + selection.collapse(this.getStartNode(), this.getStartOffset()); + if (this.getEndNode() != this.getStartNode() || + this.getEndOffset() != this.getStartOffset()) { + selection.extend(this.getEndNode(), this.getEndOffset()); + } + // This can happen if the range isn't in an editable field. + if (selection.rangeCount == 0) { + selection.addRange(this.range_); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/w3crange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/w3crange.js new file mode 100644 index 0000000000..599b50ca53 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/w3crange.js @@ -0,0 +1,394 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the W3C spec following range wrapper. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.W3cRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.browserrange.AbstractRange'); +goog.require('goog.string'); +goog.require('goog.userAgent'); + + + +/** + * The constructor for W3C specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.AbstractRange} + */ +goog.dom.browserrange.W3cRange = function(range) { + this.range_ = range; +}; +goog.inherits(goog.dom.browserrange.W3cRange, + goog.dom.browserrange.AbstractRange); + + +/** + * Returns a browser range spanning the given node's contents. + * @param {Node} node The node to select. + * @return {!Range} A browser range spanning the node's contents. + * @protected + */ +goog.dom.browserrange.W3cRange.getBrowserRangeForNode = function(node) { + var nodeRange = goog.dom.getOwnerDocument(node).createRange(); + + if (node.nodeType == goog.dom.NodeType.TEXT) { + nodeRange.setStart(node, 0); + nodeRange.setEnd(node, node.length); + } else { + /** @suppress {missingRequire} */ + if (!goog.dom.browserrange.canContainRangeEndpoint(node)) { + var rangeParent = node.parentNode; + var rangeStartOffset = goog.array.indexOf(rangeParent.childNodes, node); + nodeRange.setStart(rangeParent, rangeStartOffset); + nodeRange.setEnd(rangeParent, rangeStartOffset + 1); + } else { + var tempNode, leaf = node; + while ((tempNode = leaf.firstChild) && + /** @suppress {missingRequire} */ + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + nodeRange.setStart(leaf, 0); + + leaf = node; + while ((tempNode = leaf.lastChild) && + /** @suppress {missingRequire} */ + goog.dom.browserrange.canContainRangeEndpoint(tempNode)) { + leaf = tempNode; + } + nodeRange.setEnd(leaf, leaf.nodeType == goog.dom.NodeType.ELEMENT ? + leaf.childNodes.length : leaf.length); + } + } + + return nodeRange; +}; + + +/** + * Returns a browser range spanning the given nodes. + * @param {Node} startNode The node to start with - should not be a BR. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with - should not be a BR. + * @param {number} endOffset The offset within the end node. + * @return {!Range} A browser range spanning the node's contents. + * @protected + */ +goog.dom.browserrange.W3cRange.getBrowserRangeForNodes = function(startNode, + startOffset, endNode, endOffset) { + // Create and return the range. + var nodeRange = goog.dom.getOwnerDocument(startNode).createRange(); + nodeRange.setStart(startNode, startOffset); + nodeRange.setEnd(endNode, endOffset); + return nodeRange; +}; + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.W3cRange} A Gecko range wrapper object. + */ +goog.dom.browserrange.W3cRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.W3cRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.W3cRange} A wrapper object. + */ +goog.dom.browserrange.W3cRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.W3cRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** + * @return {!goog.dom.browserrange.W3cRange} A clone of this range. + * @override + */ +goog.dom.browserrange.W3cRange.prototype.clone = function() { + return new this.constructor(this.range_.cloneRange()); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getBrowserRange = function() { + return this.range_; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getContainer = function() { + return this.range_.commonAncestorContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getStartNode = function() { + return this.range_.startContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getStartOffset = function() { + return this.range_.startOffset; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getEndNode = function() { + return this.range_.endContainer; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getEndOffset = function() { + return this.range_.endOffset; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.compareBrowserRangeEndpoints = + function(range, thisEndpoint, otherEndpoint) { + return this.range_.compareBoundaryPoints( + otherEndpoint == goog.dom.RangeEndpoint.START ? + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_START : + goog.global['Range'].START_TO_END) : + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].END_TO_START : + goog.global['Range'].END_TO_END), + /** @type {Range} */ (range)); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.isCollapsed = function() { + return this.range_.collapsed; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getText = function() { + return this.range_.toString(); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.getValidHtml = function() { + var div = goog.dom.getDomHelper(this.range_.startContainer).createDom('div'); + div.appendChild(this.range_.cloneContents()); + var result = div.innerHTML; + + if (goog.string.startsWith(result, '<') || + !this.isCollapsed() && !goog.string.contains(result, '<')) { + // We attempt to mimic IE, which returns no containing element when a + // only text nodes are selected, does return the containing element when + // the selection is empty, and does return the element when multiple nodes + // are selected. + return result; + } + + var container = this.getContainer(); + container = container.nodeType == goog.dom.NodeType.ELEMENT ? container : + container.parentNode; + + var html = goog.dom.getOuterHtml( + /** @type {!Element} */ (container.cloneNode(false))); + return html.replace('>', '>' + result); +}; + + +// SELECTION MODIFICATION + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.select = function(reverse) { + var win = goog.dom.getWindow(goog.dom.getOwnerDocument(this.getStartNode())); + this.selectInternal(win.getSelection(), reverse); +}; + + +/** + * Select this range. + * @param {Selection} selection Browser selection object. + * @param {*} reverse Whether to select this range in reverse. + * @protected + */ +goog.dom.browserrange.W3cRange.prototype.selectInternal = function(selection, + reverse) { + // Browser-specific tricks are needed to create reversed selections + // programatically. For this generic W3C codepath, ignore the reverse + // parameter. + selection.removeAllRanges(); + selection.addRange(this.range_); +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.removeContents = function() { + var range = this.range_; + range.extractContents(); + + if (range.startContainer.hasChildNodes()) { + // Remove any now empty nodes surrounding the extracted contents. + var rangeStartContainer = + range.startContainer.childNodes[range.startOffset]; + if (rangeStartContainer) { + var rangePrevious = rangeStartContainer.previousSibling; + + if (goog.dom.getRawTextContent(rangeStartContainer) == '') { + goog.dom.removeNode(rangeStartContainer); + } + + if (rangePrevious && goog.dom.getRawTextContent(rangePrevious) == '') { + goog.dom.removeNode(rangePrevious); + } + } + } + + if (goog.userAgent.IE) { + // Unfortunately, when deleting a portion of a single text node, IE creates + // an extra text node instead of modifying the nodeValue of the start node. + // We normalize for that behavior here, similar to code in + // goog.dom.browserrange.IeRange#removeContents + // See https://connect.microsoft.com/IE/feedback/details/746591 + var startNode = this.getStartNode(); + var startOffset = this.getStartOffset(); + var endNode = this.getEndNode(); + var endOffset = this.getEndOffset(); + var sibling = startNode.nextSibling; + if (startNode == endNode && startNode.parentNode && + startNode.nodeType == goog.dom.NodeType.TEXT && + sibling && sibling.nodeType == goog.dom.NodeType.TEXT) { + startNode.nodeValue += sibling.nodeValue; + goog.dom.removeNode(sibling); + + // Modifying the node value clears the range offsets. Reselect the + // position in the modified start node. + range.setStart(startNode, startOffset); + range.setEnd(endNode, endOffset); + } + } +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.surroundContents = function(element) { + this.range_.surroundContents(element); + return element; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.insertNode = function(node, before) { + var range = this.range_.cloneRange(); + range.collapse(before); + range.insertNode(node); + range.detach(); + + return node; +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.surroundWithNodes = function( + startNode, endNode) { + var win = goog.dom.getWindow( + goog.dom.getOwnerDocument(this.getStartNode())); + /** @suppress {missingRequire} */ + var selectionRange = goog.dom.Range.createFromWindow(win); + if (selectionRange) { + var sNode = selectionRange.getStartNode(); + var eNode = selectionRange.getEndNode(); + var sOffset = selectionRange.getStartOffset(); + var eOffset = selectionRange.getEndOffset(); + } + + var clone1 = this.range_.cloneRange(); + var clone2 = this.range_.cloneRange(); + + clone1.collapse(false); + clone2.collapse(true); + + clone1.insertNode(endNode); + clone2.insertNode(startNode); + + clone1.detach(); + clone2.detach(); + + if (selectionRange) { + // There are 4 ways that surroundWithNodes can wreck the saved + // selection object. All of them happen when an inserted node splits + // a text node, and one of the end points of the selection was in the + // latter half of that text node. + // + // Clients of this library should use saveUsingCarets to avoid this + // problem. Unfortunately, saveUsingCarets uses this method, so that's + // not really an option for us. :( We just recompute the offsets. + var isInsertedNode = function(n) { + return n == startNode || n == endNode; + }; + if (sNode.nodeType == goog.dom.NodeType.TEXT) { + while (sOffset > sNode.length) { + sOffset -= sNode.length; + do { + sNode = sNode.nextSibling; + } while (isInsertedNode(sNode)); + } + } + + if (eNode.nodeType == goog.dom.NodeType.TEXT) { + while (eOffset > eNode.length) { + eOffset -= eNode.length; + do { + eNode = eNode.nextSibling; + } while (isInsertedNode(eNode)); + } + } + + /** @suppress {missingRequire} */ + goog.dom.Range.createFromNodes( + sNode, /** @type {number} */ (sOffset), + eNode, /** @type {number} */ (eOffset)).select(); + } +}; + + +/** @override */ +goog.dom.browserrange.W3cRange.prototype.collapse = function(toStart) { + this.range_.collapse(toStart); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/browserrange/webkitrange.js b/mapbox-gl/closure-library/closure/goog/dom/browserrange/webkitrange.js new file mode 100644 index 0000000000..e572dd85fc --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/browserrange/webkitrange.js @@ -0,0 +1,113 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of the WebKit specific range wrapper. Inherits most + * functionality from W3CRange, but adds exceptions as necessary. + * + * DO NOT USE THIS FILE DIRECTLY. Use goog.dom.Range instead. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.browserrange.WebKitRange'); + +goog.require('goog.dom.RangeEndpoint'); +goog.require('goog.dom.browserrange.W3cRange'); +goog.require('goog.userAgent'); + + + +/** + * The constructor for WebKit specific browser ranges. + * @param {Range} range The range object. + * @constructor + * @extends {goog.dom.browserrange.W3cRange} + * @final + */ +goog.dom.browserrange.WebKitRange = function(range) { + goog.dom.browserrange.W3cRange.call(this, range); +}; +goog.inherits(goog.dom.browserrange.WebKitRange, + goog.dom.browserrange.W3cRange); + + +/** + * Creates a range object that selects the given node's text. + * @param {Node} node The node to select. + * @return {!goog.dom.browserrange.WebKitRange} A WebKit range wrapper object. + */ +goog.dom.browserrange.WebKitRange.createFromNodeContents = function(node) { + return new goog.dom.browserrange.WebKitRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNode(node)); +}; + + +/** + * Creates a range object that selects between the given nodes. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the start node. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the end node. + * @return {!goog.dom.browserrange.WebKitRange} A wrapper object. + */ +goog.dom.browserrange.WebKitRange.createFromNodes = function(startNode, + startOffset, endNode, endOffset) { + return new goog.dom.browserrange.WebKitRange( + goog.dom.browserrange.W3cRange.getBrowserRangeForNodes(startNode, + startOffset, endNode, endOffset)); +}; + + +/** @override */ +goog.dom.browserrange.WebKitRange.prototype.compareBrowserRangeEndpoints = + function(range, thisEndpoint, otherEndpoint) { + // Webkit pre-528 has some bugs where compareBoundaryPoints() doesn't work the + // way it is supposed to, but if we reverse the sense of two comparisons, + // it works fine. + // https://bugs.webkit.org/show_bug.cgi?id=20738 + if (goog.userAgent.isVersionOrHigher('528')) { + return (goog.dom.browserrange.WebKitRange.superClass_. + compareBrowserRangeEndpoints.call( + this, range, thisEndpoint, otherEndpoint)); + } + return this.range_.compareBoundaryPoints( + otherEndpoint == goog.dom.RangeEndpoint.START ? + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_START : + goog.global['Range'].END_TO_START) : // Sense reversed + (thisEndpoint == goog.dom.RangeEndpoint.START ? + goog.global['Range'].START_TO_END : // Sense reversed + goog.global['Range'].END_TO_END), + /** @type {Range} */ (range)); +}; + + +/** @override */ +goog.dom.browserrange.WebKitRange.prototype.selectInternal = function( + selection, reversed) { + // Unselect everything. This addresses a bug in Webkit where it sometimes + // caches the old selection. + // https://bugs.webkit.org/show_bug.cgi?id=20117 + selection.removeAllRanges(); + + if (reversed) { + selection.setBaseAndExtent(this.getEndNode(), this.getEndOffset(), + this.getStartNode(), this.getStartOffset()); + } else { + selection.setBaseAndExtent(this.getStartNode(), this.getStartOffset(), + this.getEndNode(), this.getEndOffset()); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor.js b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor.js new file mode 100644 index 0000000000..01ab526c1d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor.js @@ -0,0 +1,203 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A viewport size monitor that buffers RESIZE events until the + * window size has stopped changing, within a specified period of time. For + * every RESIZE event dispatched, this will dispatch up to two *additional* + * events: + * - {@link #EventType.RESIZE_WIDTH} if the viewport's width has changed since + * the last buffered dispatch. + * - {@link #EventType.RESIZE_HEIGHT} if the viewport's height has changed since + * the last buffered dispatch. + * You likely only need to listen to one of the three events. But if you need + * more, just be cautious of duplicating effort. + * + */ + +goog.provide('goog.dom.BufferedViewportSizeMonitor'); + +goog.require('goog.asserts'); +goog.require('goog.async.Delay'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); + + + +/** + * Creates a new BufferedViewportSizeMonitor. + * @param {!goog.dom.ViewportSizeMonitor} viewportSizeMonitor The + * underlying viewport size monitor. + * @param {number=} opt_bufferMs The buffer time, in ms. If not specified, this + * value defaults to {@link #RESIZE_EVENT_DELAY_MS_}. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.dom.BufferedViewportSizeMonitor = function( + viewportSizeMonitor, opt_bufferMs) { + goog.dom.BufferedViewportSizeMonitor.base(this, 'constructor'); + + /** + * The underlying viewport size monitor. + * @type {goog.dom.ViewportSizeMonitor} + * @private + */ + this.viewportSizeMonitor_ = viewportSizeMonitor; + + /** + * The current size of the viewport. + * @type {goog.math.Size} + * @private + */ + this.currentSize_ = this.viewportSizeMonitor_.getSize(); + + /** + * The resize buffer time in ms. + * @type {number} + * @private + */ + this.resizeBufferMs_ = opt_bufferMs || + goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_; + + /** + * Listener key for the viewport size monitor. + * @type {goog.events.Key} + * @private + */ + this.listenerKey_ = goog.events.listen( + viewportSizeMonitor, + goog.events.EventType.RESIZE, + this.handleResize_, + false, + this); +}; +goog.inherits(goog.dom.BufferedViewportSizeMonitor, goog.events.EventTarget); + + +/** + * Additional events to dispatch. + * @enum {string} + */ +goog.dom.BufferedViewportSizeMonitor.EventType = { + RESIZE_HEIGHT: goog.events.getUniqueId('resizeheight'), + RESIZE_WIDTH: goog.events.getUniqueId('resizewidth') +}; + + +/** + * Delay for the resize event. + * @type {goog.async.Delay} + * @private + */ +goog.dom.BufferedViewportSizeMonitor.prototype.resizeDelay_; + + +/** + * Default number of milliseconds to wait after a resize event to relayout the + * page. + * @type {number} + * @const + * @private + */ +goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_ = 100; + + +/** @override */ +goog.dom.BufferedViewportSizeMonitor.prototype.disposeInternal = + function() { + goog.events.unlistenByKey(this.listenerKey_); + goog.dom.BufferedViewportSizeMonitor.base(this, 'disposeInternal'); +}; + + +/** + * Handles resize events on the underlying ViewportMonitor. + * @private + */ +goog.dom.BufferedViewportSizeMonitor.prototype.handleResize_ = + function() { + // Lazily create when needed. + if (!this.resizeDelay_) { + this.resizeDelay_ = new goog.async.Delay( + this.onWindowResize_, + this.resizeBufferMs_, + this); + this.registerDisposable(this.resizeDelay_); + } + this.resizeDelay_.start(); +}; + + +/** + * Window resize callback that determines whether to reflow the view contents. + * @private + */ +goog.dom.BufferedViewportSizeMonitor.prototype.onWindowResize_ = + function() { + if (this.viewportSizeMonitor_.isDisposed()) { + return; + } + + var previousSize = this.currentSize_; + var currentSize = this.viewportSizeMonitor_.getSize(); + + goog.asserts.assert(currentSize, + 'Viewport size should be set at this point'); + + this.currentSize_ = currentSize; + + if (previousSize) { + + var resized = false; + + // Width has changed + if (previousSize.width != currentSize.width) { + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH); + resized = true; + } + + // Height has changed + if (previousSize.height != currentSize.height) { + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT); + resized = true; + } + + // If either has changed, this is a resize event. + if (resized) { + this.dispatchEvent(goog.events.EventType.RESIZE); + } + + } else { + // If we didn't have a previous size, we consider all events to have + // changed. + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_HEIGHT); + this.dispatchEvent( + goog.dom.BufferedViewportSizeMonitor.EventType.RESIZE_WIDTH); + this.dispatchEvent(goog.events.EventType.RESIZE); + } +}; + + +/** + * Returns the current size of the viewport. + * @return {goog.math.Size?} The current viewport size. + */ +goog.dom.BufferedViewportSizeMonitor.prototype.getSize = function() { + return this.currentSize_ ? this.currentSize_.clone() : null; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.html b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.html new file mode 100644 index 0000000000..7817389c97 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.html @@ -0,0 +1,17 @@ + + + + +Tests for goog.dom.BufferedViewportSizeMonitor + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.js b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.js new file mode 100644 index 0000000000..f2e6388b02 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/bufferedviewportsizemonitor_test.js @@ -0,0 +1,130 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Tests for goog.dom.BufferedViewportSizeMonitor. + * + */ + + +/** @suppress {extraProvide} */ +goog.provide('goog.dom.BufferedViewportSizeMonitorTest'); + +goog.require('goog.dom.BufferedViewportSizeMonitor'); +goog.require('goog.dom.ViewportSizeMonitor'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Size'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.events'); +goog.require('goog.testing.events.Event'); +goog.require('goog.testing.jsunit'); + +goog.setTestOnly('goog.dom.BufferedViewportSizeMonitorTest'); + +var RESIZE_DELAY = goog.dom.BufferedViewportSizeMonitor.RESIZE_EVENT_DELAY_MS_; +var INITIAL_SIZE = new goog.math.Size(111, 111); + +var mockControl; +var viewportSizeMonitor; +var bufferedVsm; +var timer = new goog.testing.MockClock(); +var resizeEventCount = 0; +var size; + +var resizeCallback = function() { + resizeEventCount++; +}; + +function setUp() { + timer.install(); + + size = INITIAL_SIZE; + viewportSizeMonitor = new goog.dom.ViewportSizeMonitor(); + viewportSizeMonitor.getSize = function() { return size; }; + bufferedVsm = new goog.dom.BufferedViewportSizeMonitor(viewportSizeMonitor); + + goog.events.listen(bufferedVsm, goog.events.EventType.RESIZE, resizeCallback); +} + +function tearDown() { + goog.events.unlisten( + bufferedVsm, goog.events.EventType.RESIZE, resizeCallback); + resizeEventCount = 0; + timer.uninstall(); +} + +function testInitialSizes() { + assertTrue(goog.math.Size.equals(INITIAL_SIZE, bufferedVsm.getSize())); +} + +function testWindowResize() { + assertEquals(0, resizeEventCount); + resize(100, 100); + timer.tick(RESIZE_DELAY - 1); + assertEquals( + 'No resize expected before the delay is fired', 0, resizeEventCount); + timer.tick(1); + assertEquals('Expected resize after delay', 1, resizeEventCount); + assertTrue(goog.math.Size.equals( + new goog.math.Size(100, 100), bufferedVsm.getSize())); +} + +function testWindowResize_eventBatching() { + assertEquals('No resize calls expected before resize events', + 0, resizeEventCount); + resize(100, 100); + timer.tick(RESIZE_DELAY - 1); + resize(200, 200); + assertEquals( + 'No resize expected before the delay is fired', 0, resizeEventCount); + timer.tick(1); + assertEquals( + 'No resize expected when delay is restarted', 0, resizeEventCount); + timer.tick(RESIZE_DELAY); + assertEquals('Expected resize after delay', 1, resizeEventCount); +} + +function testWindowResize_noChange() { + resize(100, 100); + timer.tick(RESIZE_DELAY); + assertEquals(1, resizeEventCount); + resize(100, 100); + timer.tick(RESIZE_DELAY); + assertEquals( + 'No resize expected when size doesn\'t change', 1, resizeEventCount); + assertTrue(goog.math.Size.equals( + new goog.math.Size(100, 100), bufferedVsm.getSize())); +} + +function testWindowResize_previousSize() { + resize(100, 100); + timer.tick(RESIZE_DELAY); + assertEquals(1, resizeEventCount); + assertTrue(goog.math.Size.equals( + new goog.math.Size(100, 100), bufferedVsm.getSize())); + + resize(200, 200); + timer.tick(RESIZE_DELAY); + assertEquals(2, resizeEventCount); + assertTrue(goog.math.Size.equals( + new goog.math.Size(200, 200), bufferedVsm.getSize())); +} + +function resize(width, height) { + size = new goog.math.Size(width, height); + goog.testing.events.fireBrowserEvent( + new goog.testing.events.Event( + goog.events.EventType.RESIZE, viewportSizeMonitor)); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/classes.js b/mapbox-gl/closure-library/closure/goog/dom/classes.js new file mode 100644 index 0000000000..0f1db74433 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classes.js @@ -0,0 +1,239 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for adding, removing and setting classes. Prefer + * {@link goog.dom.classlist} over these utilities since goog.dom.classlist + * conforms closer to the semantics of Element.classList, is faster (uses + * native methods rather than parsing strings on every call) and compiles + * to smaller code as a result. + * + * Note: these utilities are meant to operate on HTMLElements and + * will not work on elements with differing interfaces (such as SVGElements). + * + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.dom.classes'); + +goog.require('goog.array'); + + +/** + * Sets the entire class name of an element. + * @param {Node} element DOM node to set class of. + * @param {string} className Class name(s) to apply to element. + * @deprecated Use goog.dom.classlist.set instead. + */ +goog.dom.classes.set = function(element, className) { + element.className = className; +}; + + +/** + * Gets an array of class names on an element + * @param {Node} element DOM node to get class of. + * @return {!Array} Class names on {@code element}. Some browsers add extra + * properties to the array. Do not depend on any of these! + * @deprecated Use goog.dom.classlist.get instead. + */ +goog.dom.classes.get = function(element) { + var className = element.className; + // Some types of elements don't have a className in IE (e.g. iframes). + // Furthermore, in Firefox, className is not a string when the element is + // an SVG element. + return goog.isString(className) && className.match(/\S+/g) || []; +}; + + +/** + * Adds a class or classes to an element. Does not add multiples of class names. + * @param {Node} element DOM node to add class to. + * @param {...string} var_args Class names to add. + * @return {boolean} Whether class was added (or all classes were added). + * @deprecated Use goog.dom.classlist.add or goog.dom.classlist.addAll instead. + */ +goog.dom.classes.add = function(element, var_args) { + var classes = goog.dom.classes.get(element); + var args = goog.array.slice(arguments, 1); + var expectedCount = classes.length + args.length; + goog.dom.classes.add_(classes, args); + goog.dom.classes.set(element, classes.join(' ')); + return classes.length == expectedCount; +}; + + +/** + * Removes a class or classes from an element. + * @param {Node} element DOM node to remove class from. + * @param {...string} var_args Class name(s) to remove. + * @return {boolean} Whether all classes in {@code var_args} were found and + * removed. + * @deprecated Use goog.dom.classlist.remove or goog.dom.classlist.removeAll + * instead. + */ +goog.dom.classes.remove = function(element, var_args) { + var classes = goog.dom.classes.get(element); + var args = goog.array.slice(arguments, 1); + var newClasses = goog.dom.classes.getDifference_(classes, args); + goog.dom.classes.set(element, newClasses.join(' ')); + return newClasses.length == classes.length - args.length; +}; + + +/** + * Helper method for {@link goog.dom.classes.add} and + * {@link goog.dom.classes.addRemove}. Adds one or more classes to the supplied + * classes array. + * @param {Array} classes All class names for the element, will be + * updated to have the classes supplied in {@code args} added. + * @param {Array} args Class names to add. + * @private + */ +goog.dom.classes.add_ = function(classes, args) { + for (var i = 0; i < args.length; i++) { + if (!goog.array.contains(classes, args[i])) { + classes.push(args[i]); + } + } +}; + + +/** + * Helper method for {@link goog.dom.classes.remove} and + * {@link goog.dom.classes.addRemove}. Calculates the difference of two arrays. + * @param {!Array} arr1 First array. + * @param {!Array} arr2 Second array. + * @return {!Array} The first array without the elements of the second + * array. + * @private + */ +goog.dom.classes.getDifference_ = function(arr1, arr2) { + return goog.array.filter(arr1, function(item) { + return !goog.array.contains(arr2, item); + }); +}; + + +/** + * Switches a class on an element from one to another without disturbing other + * classes. If the fromClass isn't removed, the toClass won't be added. + * @param {Node} element DOM node to swap classes on. + * @param {string} fromClass Class to remove. + * @param {string} toClass Class to add. + * @return {boolean} Whether classes were switched. + * @deprecated Use goog.dom.classlist.swap instead. + */ +goog.dom.classes.swap = function(element, fromClass, toClass) { + var classes = goog.dom.classes.get(element); + + var removed = false; + for (var i = 0; i < classes.length; i++) { + if (classes[i] == fromClass) { + goog.array.splice(classes, i--, 1); + removed = true; + } + } + + if (removed) { + classes.push(toClass); + goog.dom.classes.set(element, classes.join(' ')); + } + + return removed; +}; + + +/** + * Adds zero or more classes to an element and removes zero or more as a single + * operation. Unlike calling {@link goog.dom.classes.add} and + * {@link goog.dom.classes.remove} separately, this is more efficient as it only + * parses the class property once. + * + * If a class is in both the remove and add lists, it will be added. Thus, + * you can use this instead of {@link goog.dom.classes.swap} when you have + * more than two class names that you want to swap. + * + * @param {Node} element DOM node to swap classes on. + * @param {?(string|Array)} classesToRemove Class or classes to + * remove, if null no classes are removed. + * @param {?(string|Array)} classesToAdd Class or classes to add, if + * null no classes are added. + * @deprecated Use goog.dom.classlist.addRemove instead. + */ +goog.dom.classes.addRemove = function(element, classesToRemove, classesToAdd) { + var classes = goog.dom.classes.get(element); + if (goog.isString(classesToRemove)) { + goog.array.remove(classes, classesToRemove); + } else if (goog.isArray(classesToRemove)) { + classes = goog.dom.classes.getDifference_(classes, classesToRemove); + } + + if (goog.isString(classesToAdd) && + !goog.array.contains(classes, classesToAdd)) { + classes.push(classesToAdd); + } else if (goog.isArray(classesToAdd)) { + goog.dom.classes.add_(classes, classesToAdd); + } + + goog.dom.classes.set(element, classes.join(' ')); +}; + + +/** + * Returns true if an element has a class. + * @param {Node} element DOM node to test. + * @param {string} className Class name to test for. + * @return {boolean} Whether element has the class. + * @deprecated Use goog.dom.classlist.contains instead. + */ +goog.dom.classes.has = function(element, className) { + return goog.array.contains(goog.dom.classes.get(element), className); +}; + + +/** + * Adds or removes a class depending on the enabled argument. + * @param {Node} element DOM node to add or remove the class on. + * @param {string} className Class name to add or remove. + * @param {boolean} enabled Whether to add or remove the class (true adds, + * false removes). + * @deprecated Use goog.dom.classlist.enable or goog.dom.classlist.enableAll + * instead. + */ +goog.dom.classes.enable = function(element, className, enabled) { + if (enabled) { + goog.dom.classes.add(element, className); + } else { + goog.dom.classes.remove(element, className); + } +}; + + +/** + * Removes a class if an element has it, and adds it the element doesn't have + * it. Won't affect other classes on the node. + * @param {Node} element DOM node to toggle class on. + * @param {string} className Class to toggle. + * @return {boolean} True if class was added, false if it was removed + * (in other words, whether element has the class after this function has + * been called). + * @deprecated Use goog.dom.classlist.toggle instead. + */ +goog.dom.classes.toggle = function(element, className) { + var add = !goog.dom.classes.has(element, className); + goog.dom.classes.enable(element, className, add); + return add; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/classes_quirks_test.html b/mapbox-gl/closure-library/closure/goog/dom/classes_quirks_test.html new file mode 100644 index 0000000000..dc9a81790c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classes_quirks_test.html @@ -0,0 +1,63 @@ + + + + +Closure Unit Tests - goog.dom.classes= + + + + +
    + Test Element +
    + +
    + + + + + + + + +

    + +
    +
    +
    + + +

    + + a + c + d + + e + f + g + +

    +

    + h +

    + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/classes_test.html b/mapbox-gl/closure-library/closure/goog/dom/classes_test.html new file mode 100644 index 0000000000..bdde043f84 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classes_test.html @@ -0,0 +1,64 @@ + + + + + + +Closure Unit Tests - goog.dom.classes + + + + +
    + Test Element +
    + +
    + + + + + + + + +

    + +
    +
    +
    + + +

    + + a + c + d + + e + f + g + +

    +

    + h +

    + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/classes_test.js b/mapbox-gl/closure-library/closure/goog/dom/classes_test.js new file mode 100644 index 0000000000..12f19d0846 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classes_test.js @@ -0,0 +1,231 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Shared code for classes_test.html & classes_quirks_test.html. + */ + +goog.provide('goog.dom.classes_test'); +goog.setTestOnly('goog.dom.classes_test'); + +goog.require('goog.dom'); +goog.require('goog.dom.classes'); +goog.require('goog.testing.jsunit'); + + +var classes = goog.dom.classes; + +function testGet() { + var el = document.createElement('div'); + assertArrayEquals([], goog.dom.classes.get(el)); + el.className = 'C'; + assertArrayEquals(['C'], goog.dom.classes.get(el)); + el.className = 'C D'; + assertArrayEquals(['C', 'D'], goog.dom.classes.get(el)); + el.className = 'C\nD'; + assertArrayEquals(['C', 'D'], goog.dom.classes.get(el)); + el.className = ' C '; + assertArrayEquals(['C'], goog.dom.classes.get(el)); +} + +function testSetAddHasRemove() { + var el = goog.dom.getElement('p1'); + classes.set(el, 'SOMECLASS'); + assertTrue('Should have SOMECLASS', classes.has(el, 'SOMECLASS')); + + classes.set(el, 'OTHERCLASS'); + assertTrue('Should have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertFalse('Should not have SOMECLASS', classes.has(el, 'SOMECLASS')); + + classes.add(el, 'WOOCLASS'); + assertTrue('Should have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertTrue('Should have WOOCLASS', classes.has(el, 'WOOCLASS')); + + classes.add(el, 'ACLASS', 'BCLASS', 'CCLASS'); + assertTrue('Should have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertTrue('Should have WOOCLASS', classes.has(el, 'WOOCLASS')); + assertTrue('Should have ACLASS', classes.has(el, 'ACLASS')); + assertTrue('Should have BCLASS', classes.has(el, 'BCLASS')); + assertTrue('Should have CCLASS', classes.has(el, 'CCLASS')); + + classes.remove(el, 'CCLASS'); + assertTrue('Should have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertTrue('Should have WOOCLASS', classes.has(el, 'WOOCLASS')); + assertTrue('Should have ACLASS', classes.has(el, 'ACLASS')); + assertTrue('Should have BCLASS', classes.has(el, 'BCLASS')); + assertFalse('Should not have CCLASS', classes.has(el, 'CCLASS')); + + classes.remove(el, 'ACLASS', 'BCLASS'); + assertTrue('Should have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertTrue('Should have WOOCLASS', classes.has(el, 'WOOCLASS')); + assertFalse('Should not have ACLASS', classes.has(el, 'ACLASS')); + assertFalse('Should not have BCLASS', classes.has(el, 'BCLASS')); +} + +// While support for this isn't implied in the method documentation, +// this is a frequently used pattern. +function testAddWithSpacesInClassName() { + var el = goog.dom.getElement('p1'); + classes.add(el, 'CLASS1 CLASS2', 'CLASS3 CLASS4'); + assertTrue('Should have CLASS1', classes.has(el, 'CLASS1')); + assertTrue('Should have CLASS2', classes.has(el, 'CLASS2')); + assertTrue('Should have CLASS3', classes.has(el, 'CLASS3')); + assertTrue('Should have CLASS4', classes.has(el, 'CLASS4')); +} + +function testSwap() { + var el = goog.dom.getElement('p1'); + classes.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have FIRST class', classes.has(el, 'SOMECLASS')); + assertFalse('Should not have second class', classes.has(el, 'second')); + + classes.swap(el, 'FIRST', 'second'); + + assertFalse('Should not have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have FIRST class', classes.has(el, 'SOMECLASS')); + assertTrue('Should have second class', classes.has(el, 'second')); + + classes.swap(el, 'second', 'FIRST'); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have FIRST class', classes.has(el, 'SOMECLASS')); + assertFalse('Should not have second class', classes.has(el, 'second')); +} + +function testEnable() { + var el = goog.dom.getElement('p1'); + classes.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); + + classes.enable(el, 'FIRST', false); + + assertFalse('Should not have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); + + classes.enable(el, 'FIRST', true); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); +} + +function testToggle() { + var el = goog.dom.getElement('p1'); + classes.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); + + classes.toggle(el, 'FIRST'); + + assertFalse('Should not have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); + + classes.toggle(el, 'FIRST'); + + assertTrue('Should have FIRST class', classes.has(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', classes.has(el, 'SOMECLASS')); +} + +function testAddNotAddingMultiples() { + var el = goog.dom.getElement('span6'); + assertTrue(classes.add(el, 'A')); + assertEquals('A', el.className); + assertFalse(classes.add(el, 'A')); + assertEquals('A', el.className); + assertFalse(classes.add(el, 'B', 'B')); + assertEquals('A B', el.className); +} + +function testAddRemoveString() { + var el = goog.dom.getElement('span6'); + el.className = 'A'; + + goog.dom.classes.addRemove(el, 'A', 'B'); + assertEquals('B', el.className); + + goog.dom.classes.addRemove(el, null, 'C'); + assertEquals('B C', el.className); + + goog.dom.classes.addRemove(el, 'C', 'D'); + assertEquals('B D', el.className); + + goog.dom.classes.addRemove(el, 'D', null); + assertEquals('B', el.className); +} + +function testAddRemoveArray() { + var el = goog.dom.getElement('span6'); + el.className = 'A'; + + goog.dom.classes.addRemove(el, ['A'], ['B']); + assertEquals('B', el.className); + + goog.dom.classes.addRemove(el, [], ['C']); + assertEquals('B C', el.className); + + goog.dom.classes.addRemove(el, ['C'], ['D']); + assertEquals('B D', el.className); + + goog.dom.classes.addRemove(el, ['D'], []); + assertEquals('B', el.className); +} + +function testAddRemoveMultiple() { + var el = goog.dom.getElement('span6'); + el.className = 'A'; + + goog.dom.classes.addRemove(el, ['A'], ['B', 'C', 'D']); + assertEquals('B C D', el.className); + + goog.dom.classes.addRemove(el, [], ['E', 'F']); + assertEquals('B C D E F', el.className); + + goog.dom.classes.addRemove(el, ['C', 'E'], []); + assertEquals('B D F', el.className); + + goog.dom.classes.addRemove(el, ['B'], ['G']); + assertEquals('D F G', el.className); +} + +// While support for this isn't implied in the method documentation, +// this is a frequently used pattern. +function testAddRemoveWithSpacesInClassName() { + var el = goog.dom.getElement('p1'); + classes.addRemove(el, '', 'CLASS1 CLASS2'); + assertTrue('Should have CLASS1', classes.has(el, 'CLASS1')); + assertTrue('Should have CLASS2', classes.has(el, 'CLASS2')); +} + +function testHasWithNewlines() { + var el = goog.dom.getElement('p3'); + assertTrue('Should have SOMECLASS', classes.has(el, 'SOMECLASS')); + assertTrue('Should also have OTHERCLASS', classes.has(el, 'OTHERCLASS')); + assertFalse('Should not have WEIRDCLASS', classes.has(el, 'WEIRDCLASS')); +} + +function testEmptyClassNames() { + var el = goog.dom.getElement('span1'); + // At the very least, make sure these do not error out. + assertFalse('Should not have an empty class', classes.has(el, '')); + classes.add(el, ''); + classes.toggle(el, ''); + assertFalse('Should not remove an empty class', classes.remove(el, '')); + classes.swap(el, '', 'OTHERCLASS'); + classes.swap(el, 'TEST1', ''); + classes.addRemove(el, '', ''); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/classlist.js b/mapbox-gl/closure-library/closure/goog/dom/classlist.js new file mode 100644 index 0000000000..dcbb7ed276 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classlist.js @@ -0,0 +1,277 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for detecting, adding and removing classes. Prefer + * this over goog.dom.classes for new code since it attempts to use classList + * (DOMTokenList: http://dom.spec.whatwg.org/#domtokenlist) which is faster + * and requires less code. + * + * Note: these utilities are meant to operate on HTMLElements + * and may have unexpected behavior on elements with differing interfaces + * (such as SVGElements). + */ + + +goog.provide('goog.dom.classlist'); + +goog.require('goog.array'); + + +/** + * Override this define at build-time if you know your target supports it. + * @define {boolean} Whether to use the classList property (DOMTokenList). + */ +goog.define('goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST', false); + + +/** + * Gets an array-like object of class names on an element. + * @param {Element} element DOM node to get the classes of. + * @return {!goog.array.ArrayLike} Class names on {@code element}. + */ +goog.dom.classlist.get = function(element) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + return element.classList; + } + + var className = element.className; + // Some types of elements don't have a className in IE (e.g. iframes). + // Furthermore, in Firefox, className is not a string when the element is + // an SVG element. + return goog.isString(className) && className.match(/\S+/g) || []; +}; + + +/** + * Sets the entire class name of an element. + * @param {Element} element DOM node to set class of. + * @param {string} className Class name(s) to apply to element. + */ +goog.dom.classlist.set = function(element, className) { + element.className = className; +}; + + +/** + * Returns true if an element has a class. This method may throw a DOM + * exception for an invalid or empty class name if DOMTokenList is used. + * @param {Element} element DOM node to test. + * @param {string} className Class name to test for. + * @return {boolean} Whether element has the class. + */ +goog.dom.classlist.contains = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + return element.classList.contains(className); + } + return goog.array.contains(goog.dom.classlist.get(element), className); +}; + + +/** + * Adds a class to an element. Does not add multiples of class names. This + * method may throw a DOM exception for an invalid or empty class name if + * DOMTokenList is used. + * @param {Element} element DOM node to add class to. + * @param {string} className Class name to add. + */ +goog.dom.classlist.add = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + element.classList.add(className); + return; + } + + if (!goog.dom.classlist.contains(element, className)) { + // Ensure we add a space if this is not the first class name added. + element.className += element.className.length > 0 ? + (' ' + className) : className; + } +}; + + +/** + * Convenience method to add a number of class names at once. + * @param {Element} element The element to which to add classes. + * @param {goog.array.ArrayLike} classesToAdd An array-like object + * containing a collection of class names to add to the element. + * This method may throw a DOM exception if classesToAdd contains invalid + * or empty class names. + */ +goog.dom.classlist.addAll = function(element, classesToAdd) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + goog.array.forEach(classesToAdd, function(className) { + goog.dom.classlist.add(element, className); + }); + return; + } + + var classMap = {}; + + // Get all current class names into a map. + goog.array.forEach(goog.dom.classlist.get(element), + function(className) { + classMap[className] = true; + }); + + // Add new class names to the map. + goog.array.forEach(classesToAdd, + function(className) { + classMap[className] = true; + }); + + // Flatten the keys of the map into the className. + element.className = ''; + for (var className in classMap) { + element.className += element.className.length > 0 ? + (' ' + className) : className; + } +}; + + +/** + * Removes a class from an element. This method may throw a DOM exception + * for an invalid or empty class name if DOMTokenList is used. + * @param {Element} element DOM node to remove class from. + * @param {string} className Class name to remove. + */ +goog.dom.classlist.remove = function(element, className) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + element.classList.remove(className); + return; + } + + if (goog.dom.classlist.contains(element, className)) { + // Filter out the class name. + element.className = goog.array.filter( + goog.dom.classlist.get(element), + function(c) { + return c != className; + }).join(' '); + } +}; + + +/** + * Removes a set of classes from an element. Prefer this call to + * repeatedly calling {@code goog.dom.classlist.remove} if you want to remove + * a large set of class names at once. + * @param {Element} element The element from which to remove classes. + * @param {goog.array.ArrayLike} classesToRemove An array-like object + * containing a collection of class names to remove from the element. + * This method may throw a DOM exception if classesToRemove contains invalid + * or empty class names. + */ +goog.dom.classlist.removeAll = function(element, classesToRemove) { + if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) { + goog.array.forEach(classesToRemove, function(className) { + goog.dom.classlist.remove(element, className); + }); + return; + } + // Filter out those classes in classesToRemove. + element.className = goog.array.filter( + goog.dom.classlist.get(element), + function(className) { + // If this class is not one we are trying to remove, + // add it to the array of new class names. + return !goog.array.contains(classesToRemove, className); + }).join(' '); +}; + + +/** + * Adds or removes a class depending on the enabled argument. This method + * may throw a DOM exception for an invalid or empty class name if DOMTokenList + * is used. + * @param {Element} element DOM node to add or remove the class on. + * @param {string} className Class name to add or remove. + * @param {boolean} enabled Whether to add or remove the class (true adds, + * false removes). + */ +goog.dom.classlist.enable = function(element, className, enabled) { + if (enabled) { + goog.dom.classlist.add(element, className); + } else { + goog.dom.classlist.remove(element, className); + } +}; + + +/** + * Adds or removes a set of classes depending on the enabled argument. This + * method may throw a DOM exception for an invalid or empty class name if + * DOMTokenList is used. + * @param {!Element} element DOM node to add or remove the class on. + * @param {goog.array.ArrayLike} classesToEnable An array-like object + * containing a collection of class names to add or remove from the element. + * @param {boolean} enabled Whether to add or remove the classes (true adds, + * false removes). + */ +goog.dom.classlist.enableAll = function(element, classesToEnable, enabled) { + var f = enabled ? goog.dom.classlist.addAll : + goog.dom.classlist.removeAll; + f(element, classesToEnable); +}; + + +/** + * Switches a class on an element from one to another without disturbing other + * classes. If the fromClass isn't removed, the toClass won't be added. This + * method may throw a DOM exception if the class names are empty or invalid. + * @param {Element} element DOM node to swap classes on. + * @param {string} fromClass Class to remove. + * @param {string} toClass Class to add. + * @return {boolean} Whether classes were switched. + */ +goog.dom.classlist.swap = function(element, fromClass, toClass) { + if (goog.dom.classlist.contains(element, fromClass)) { + goog.dom.classlist.remove(element, fromClass); + goog.dom.classlist.add(element, toClass); + return true; + } + return false; +}; + + +/** + * Removes a class if an element has it, and adds it the element doesn't have + * it. Won't affect other classes on the node. This method may throw a DOM + * exception if the class name is empty or invalid. + * @param {Element} element DOM node to toggle class on. + * @param {string} className Class to toggle. + * @return {boolean} True if class was added, false if it was removed + * (in other words, whether element has the class after this function has + * been called). + */ +goog.dom.classlist.toggle = function(element, className) { + var add = !goog.dom.classlist.contains(element, className); + goog.dom.classlist.enable(element, className, add); + return add; +}; + + +/** + * Adds and removes a class of an element. Unlike + * {@link goog.dom.classlist.swap}, this method adds the classToAdd regardless + * of whether the classToRemove was present and had been removed. This method + * may throw a DOM exception if the class names are empty or invalid. + * + * @param {Element} element DOM node to swap classes on. + * @param {string} classToRemove Class to remove. + * @param {string} classToAdd Class to add. + */ +goog.dom.classlist.addRemove = function(element, classToRemove, classToAdd) { + goog.dom.classlist.remove(element, classToRemove); + goog.dom.classlist.add(element, classToAdd); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/classlist_test.html b/mapbox-gl/closure-library/closure/goog/dom/classlist_test.html new file mode 100644 index 0000000000..01df653723 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classlist_test.html @@ -0,0 +1,29 @@ + + + + +Closure Unit Tests - goog.dom.classlist + + + + +

    +

    + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/classlist_test.js b/mapbox-gl/closure-library/closure/goog/dom/classlist_test.js new file mode 100644 index 0000000000..927bd01ce2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/classlist_test.js @@ -0,0 +1,275 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Shared code for classlist_test.html. + */ + +goog.provide('goog.dom.classlist_test'); +goog.setTestOnly('goog.dom.classlist_test'); + +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.testing.ExpectedFailures'); +goog.require('goog.testing.jsunit'); + +var expectedFailures = new goog.testing.ExpectedFailures(); +var classlist = goog.dom.classlist; + +function tearDown() { + expectedFailures.handleTearDown(); +} + +function testGet() { + var el = document.createElement('div'); + assertTrue(classlist.get(el).length == 0); + el.className = 'C'; + assertElementsEquals(['C'], classlist.get(el)); + el.className = 'C D'; + assertElementsEquals(['C', 'D'], classlist.get(el)); + el.className = 'C\nD'; + assertElementsEquals(['C', 'D'], classlist.get(el)); + el.className = ' C '; + assertElementsEquals(['C'], classlist.get(el)); +} + +function testContainsWithNewlines() { + var el = goog.dom.getElement('p1'); + assertTrue('Should not have SOMECLASS', classlist.contains(el, 'SOMECLASS')); + assertTrue('Should also have OTHERCLASS', + classlist.contains(el, 'OTHERCLASS')); + assertFalse('Should not have WEIRDCLASS', + classlist.contains(el, 'WEIRDCLASS')); +} + +function testContainsCaseSensitive() { + var el = goog.dom.getElement('p2'); + assertFalse('Should not have camelcase', + classlist.contains(el, 'camelcase')); + assertFalse('Should not have CAMELCASE', + classlist.contains(el, 'CAMELCASE')); + assertTrue('Should have camelCase', + classlist.contains(el, 'camelCase')); +} + +function testAddNotAddingMultiples() { + var el = document.createElement('div'); + classlist.add(el, 'A'); + assertEquals('A', el.className); + classlist.add(el, 'A'); + assertEquals('A', el.className); + classlist.add(el, 'B', 'B'); + assertEquals('A B', el.className); +} + +function testAddCaseSensitive() { + var el = document.createElement('div'); + classlist.add(el, 'A'); + assertTrue(classlist.contains(el, 'A')); + assertFalse(classlist.contains(el, 'a')); + classlist.add(el, 'a'); + assertTrue(classlist.contains(el, 'A')); + assertTrue(classlist.contains(el, 'a')); + assertEquals('A a', el.className); +} + +function testAddAll() { + var elem = document.createElement('div'); + elem.className = 'foo goog-bar'; + + goog.dom.classlist.addAll(elem, ['goog-baz', 'foo']); + assertEquals(3, classlist.get(elem).length); + assertTrue(goog.dom.classlist.contains(elem, 'foo')); + assertTrue(goog.dom.classlist.contains(elem, 'goog-bar')); + assertTrue(goog.dom.classlist.contains(elem, 'goog-baz')); +} + +function testAddAllEmpty() { + var classes = 'foo bar'; + var elem = document.createElement('div'); + elem.className = classes; + + goog.dom.classlist.addAll(elem, []); + assertEquals(elem.className, classes); +} + +function testRemove() { + var el = document.createElement('div'); + el.className = 'A B C'; + classlist.remove(el, 'B'); + assertEquals('A C', el.className); +} + +function testRemoveCaseSensitive() { + var el = document.createElement('div'); + el.className = 'A B C'; + classlist.remove(el, 'b'); + assertEquals('A B C', el.className); +} + +function testRemoveAll() { + var elem = document.createElement('div'); + elem.className = 'foo bar baz'; + + goog.dom.classlist.removeAll(elem, ['bar', 'foo']); + assertFalse(goog.dom.classlist.contains(elem, 'foo')); + assertFalse(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); +} + +function testRemoveAllOne() { + var elem = document.createElement('div'); + elem.className = 'foo bar baz'; + + goog.dom.classlist.removeAll(elem, ['bar']); + assertFalse(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'foo')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); +} + +function testRemoveAllSomeNotPresent() { + var elem = document.createElement('div'); + elem.className = 'foo bar baz'; + + goog.dom.classlist.removeAll(elem, ['a', 'bar']); + assertTrue(goog.dom.classlist.contains(elem, 'foo')); + assertFalse(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); +} + +function testRemoveAllCaseSensitive() { + var elem = document.createElement('div'); + elem.className = 'foo bar baz'; + + goog.dom.classlist.removeAll(elem, ['BAR', 'foo']); + assertFalse(goog.dom.classlist.contains(elem, 'foo')); + assertTrue(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); +} + +function testEnable() { + var el = goog.dom.getElement('p1'); + classlist.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); + + classlist.enable(el, 'FIRST', false); + + assertFalse('Should not have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); + + classlist.enable(el, 'FIRST', true); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); +} + +function testEnableNotAddingMultiples() { + var el = document.createElement('div'); + classlist.enable(el, 'A', true); + assertEquals('A', el.className); + classlist.enable(el, 'A', true); + assertEquals('A', el.className); + classlist.enable(el, 'B', 'B', true); + assertEquals('A B', el.className); +} + +function testEnableAllRemove() { + var elem = document.createElement('div'); + elem.className = 'foo bar baz'; + + // Test removing some classes (some not present). + goog.dom.classlist.enableAll(elem, ['a', 'bar'], false /* enable */); + assertTrue(goog.dom.classlist.contains(elem, 'foo')); + assertFalse(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); + assertFalse(goog.dom.classlist.contains(elem, 'a')); +} + +function testEnableAllAdd() { + var elem = document.createElement('div'); + elem.className = 'foo bar'; + + // Test adding some classes (some duplicate). + goog.dom.classlist.enableAll(elem, ['a', 'bar', 'baz'], true /* enable */); + assertTrue(goog.dom.classlist.contains(elem, 'foo')); + assertTrue(goog.dom.classlist.contains(elem, 'bar')); + assertTrue(goog.dom.classlist.contains(elem, 'baz')); + assertTrue(goog.dom.classlist.contains(elem, 'a')); +} + +function testSwap() { + var el = goog.dom.getElement('p1'); + classlist.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have FIRST class', classlist.contains(el, 'SOMECLASS')); + assertFalse('Should not have second class', classlist.contains(el, 'second')); + + classlist.swap(el, 'FIRST', 'second'); + + assertFalse('Should not have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have FIRST class', classlist.contains(el, 'SOMECLASS')); + assertTrue('Should have second class', classlist.contains(el, 'second')); + + classlist.swap(el, 'second', 'FIRST'); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have FIRST class', classlist.contains(el, 'SOMECLASS')); + assertFalse('Should not have second class', classlist.contains(el, 'second')); +} + +function testToggle() { + var el = goog.dom.getElement('p1'); + classlist.set(el, 'SOMECLASS FIRST'); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); + + var ret = classlist.toggle(el, 'FIRST'); + + assertFalse('Should not have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); + assertFalse('Return value should have been false', ret); + + ret = classlist.toggle(el, 'FIRST'); + + assertTrue('Should have FIRST class', classlist.contains(el, 'FIRST')); + assertTrue('Should have SOMECLASS class', + classlist.contains(el, 'SOMECLASS')); + assertTrue('Return value should have been true', ret); +} + +function testAddRemoveString() { + var el = document.createElement('div'); + el.className = 'A'; + + classlist.addRemove(el, 'A', 'B'); + assertEquals('B', el.className); + + classlist.addRemove(el, 'Z', 'C'); + assertEquals('B C', el.className); + + classlist.addRemove(el, 'C', 'D'); + assertEquals('B D', el.className); + + classlist.addRemove(el, 'D', 'B'); + assertEquals('B', el.className); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/controlrange.js b/mapbox-gl/closure-library/closure/goog/dom/controlrange.js new file mode 100644 index 0000000000..cf3256adcb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/controlrange.js @@ -0,0 +1,506 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with IE control ranges. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.ControlRange'); +goog.provide('goog.dom.ControlRangeIterator'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.AbstractMultiRange'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.RangeIterator'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.SavedRange'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.dom.TextRange'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.userAgent'); + + + +/** + * Create a new control selection with no properties. Do not use this + * constructor: use one of the goog.dom.Range.createFrom* methods instead. + * @constructor + * @extends {goog.dom.AbstractMultiRange} + * @final + */ +goog.dom.ControlRange = function() { +}; +goog.inherits(goog.dom.ControlRange, goog.dom.AbstractMultiRange); + + +/** + * Create a new range wrapper from the given browser range object. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Object} controlRange The browser range object. + * @return {!goog.dom.ControlRange} A range wrapper object. + */ +goog.dom.ControlRange.createFromBrowserRange = function(controlRange) { + var range = new goog.dom.ControlRange(); + range.range_ = controlRange; + return range; +}; + + +/** + * Create a new range wrapper that selects the given element. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {...Element} var_args The element(s) to select. + * @return {!goog.dom.ControlRange} A range wrapper object. + */ +goog.dom.ControlRange.createFromElements = function(var_args) { + var range = goog.dom.getOwnerDocument(arguments[0]).body.createControlRange(); + for (var i = 0, len = arguments.length; i < len; i++) { + range.addElement(arguments[i]); + } + return goog.dom.ControlRange.createFromBrowserRange(range); +}; + + +/** + * The IE control range obejct. + * @type {Object} + * @private + */ +goog.dom.ControlRange.prototype.range_ = null; + + +/** + * Cached list of elements. + * @type {Array?} + * @private + */ +goog.dom.ControlRange.prototype.elements_ = null; + + +/** + * Cached sorted list of elements. + * @type {Array?} + * @private + */ +goog.dom.ControlRange.prototype.sortedElements_ = null; + + +// Method implementations + + +/** + * Clear cached values. + * @private + */ +goog.dom.ControlRange.prototype.clearCachedValues_ = function() { + this.elements_ = null; + this.sortedElements_ = null; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.clone = function() { + return goog.dom.ControlRange.createFromElements.apply(this, + this.getElements()); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getType = function() { + return goog.dom.RangeType.CONTROL; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getBrowserRangeObject = function() { + return this.range_ || document.body.createControlRange(); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.setBrowserRangeObject = function(nativeRange) { + if (!goog.dom.AbstractRange.isNativeControlRange(nativeRange)) { + return false; + } + this.range_ = nativeRange; + return true; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getTextRangeCount = function() { + return this.range_ ? this.range_.length : 0; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getTextRange = function(i) { + return goog.dom.TextRange.createFromNodeContents(this.range_.item(i)); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getContainer = function() { + return goog.dom.findCommonAncestor.apply(null, this.getElements()); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getStartNode = function() { + return this.getSortedElements()[0]; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getStartOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getEndNode = function() { + var sorted = this.getSortedElements(); + var startsLast = /** @type {Node} */ (goog.array.peek(sorted)); + return /** @type {Node} */ (goog.array.find(sorted, function(el) { + return goog.dom.contains(el, startsLast); + })); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getEndOffset = function() { + return this.getEndNode().childNodes.length; +}; + + +// TODO(robbyw): Figure out how to unify getElements with TextRange API. +/** + * @return {!Array} Array of elements in the control range. + */ +goog.dom.ControlRange.prototype.getElements = function() { + if (!this.elements_) { + this.elements_ = []; + if (this.range_) { + for (var i = 0; i < this.range_.length; i++) { + this.elements_.push(this.range_.item(i)); + } + } + } + + return this.elements_; +}; + + +/** + * @return {!Array} Array of elements comprising the control range, + * sorted by document order. + */ +goog.dom.ControlRange.prototype.getSortedElements = function() { + if (!this.sortedElements_) { + this.sortedElements_ = this.getElements().concat(); + this.sortedElements_.sort(function(a, b) { + return a.sourceIndex - b.sourceIndex; + }); + } + + return this.sortedElements_; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.isRangeInDocument = function() { + var returnValue = false; + + try { + returnValue = goog.array.every(this.getElements(), function(element) { + // On IE, this throws an exception when the range is detached. + return goog.userAgent.IE ? + !!element.parentNode : + goog.dom.contains(element.ownerDocument.body, element); + }); + } catch (e) { + // IE sometimes throws Invalid Argument errors for detached elements. + // Note: trying to return a value from the above try block can cause IE + // to crash. It is necessary to use the local returnValue. + } + + return returnValue; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.isCollapsed = function() { + return !this.range_ || !this.range_.length; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getText = function() { + // TODO(robbyw): What about for table selections? Should those have text? + return ''; +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getHtmlFragment = function() { + return goog.array.map(this.getSortedElements(), goog.dom.getOuterHtml). + join(''); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getValidHtml = function() { + return this.getHtmlFragment(); +}; + + +/** @override */ +goog.dom.ControlRange.prototype.getPastableHtml = + goog.dom.ControlRange.prototype.getValidHtml; + + +/** @override */ +goog.dom.ControlRange.prototype.__iterator__ = function(opt_keys) { + return new goog.dom.ControlRangeIterator(this); +}; + + +// RANGE ACTIONS + + +/** @override */ +goog.dom.ControlRange.prototype.select = function() { + if (this.range_) { + this.range_.select(); + } +}; + + +/** @override */ +goog.dom.ControlRange.prototype.removeContents = function() { + // TODO(robbyw): Test implementing with execCommand('Delete') + if (this.range_) { + var nodes = []; + for (var i = 0, len = this.range_.length; i < len; i++) { + nodes.push(this.range_.item(i)); + } + goog.array.forEach(nodes, goog.dom.removeNode); + + this.collapse(false); + } +}; + + +/** @override */ +goog.dom.ControlRange.prototype.replaceContentsWithNode = function(node) { + // Control selections have to have the node inserted before removing the + // selection contents because a collapsed control range doesn't have start or + // end nodes. + var result = this.insertNode(node, true); + + if (!this.isCollapsed()) { + this.removeContents(); + } + + return result; +}; + + +// SAVE/RESTORE + + +/** @override */ +goog.dom.ControlRange.prototype.saveUsingDom = function() { + return new goog.dom.DomSavedControlRange_(this); +}; + + +// RANGE MODIFICATION + + +/** @override */ +goog.dom.ControlRange.prototype.collapse = function(toAnchor) { + // TODO(robbyw): Should this return a text range? If so, API needs to change. + this.range_ = null; + this.clearCachedValues_(); +}; + + +// SAVED RANGE OBJECTS + + + +/** + * A SavedRange implementation using DOM endpoints. + * @param {goog.dom.ControlRange} range The range to save. + * @constructor + * @extends {goog.dom.SavedRange} + * @private + */ +goog.dom.DomSavedControlRange_ = function(range) { + /** + * The element list. + * @type {Array} + * @private + */ + this.elements_ = range.getElements(); +}; +goog.inherits(goog.dom.DomSavedControlRange_, goog.dom.SavedRange); + + +/** @override */ +goog.dom.DomSavedControlRange_.prototype.restoreInternal = function() { + var doc = this.elements_.length ? + goog.dom.getOwnerDocument(this.elements_[0]) : document; + var controlRange = doc.body.createControlRange(); + for (var i = 0, len = this.elements_.length; i < len; i++) { + controlRange.addElement(this.elements_[i]); + } + return goog.dom.ControlRange.createFromBrowserRange(controlRange); +}; + + +/** @override */ +goog.dom.DomSavedControlRange_.prototype.disposeInternal = function() { + goog.dom.DomSavedControlRange_.superClass_.disposeInternal.call(this); + delete this.elements_; +}; + + +// RANGE ITERATION + + + +/** + * Subclass of goog.dom.TagIterator that iterates over a DOM range. It + * adds functions to determine the portion of each text node that is selected. + * + * @param {goog.dom.ControlRange?} range The range to traverse. + * @constructor + * @extends {goog.dom.RangeIterator} + * @final + */ +goog.dom.ControlRangeIterator = function(range) { + if (range) { + this.elements_ = range.getSortedElements(); + this.startNode_ = this.elements_.shift(); + this.endNode_ = /** @type {Node} */ (goog.array.peek(this.elements_)) || + this.startNode_; + } + + goog.dom.RangeIterator.call(this, this.startNode_, false); +}; +goog.inherits(goog.dom.ControlRangeIterator, goog.dom.RangeIterator); + + +/** + * The first node in the selection. + * @type {Node} + * @private + */ +goog.dom.ControlRangeIterator.prototype.startNode_ = null; + + +/** + * The last node in the selection. + * @type {Node} + * @private + */ +goog.dom.ControlRangeIterator.prototype.endNode_ = null; + + +/** + * The list of elements left to traverse. + * @type {Array?} + * @private + */ +goog.dom.ControlRangeIterator.prototype.elements_ = null; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getStartTextOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getEndTextOffset = function() { + return 0; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getStartNode = function() { + return this.startNode_; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.getEndNode = function() { + return this.endNode_; +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.isLast = function() { + return !this.depth && !this.elements_.length; +}; + + +/** + * Move to the next position in the selection. + * Throws {@code goog.iter.StopIteration} when it passes the end of the range. + * @return {Node} The node at the next position. + * @override + */ +goog.dom.ControlRangeIterator.prototype.next = function() { + // Iterate over each element in the range, and all of its children. + if (this.isLast()) { + throw goog.iter.StopIteration; + } else if (!this.depth) { + var el = this.elements_.shift(); + this.setPosition(el, + goog.dom.TagWalkType.START_TAG, + goog.dom.TagWalkType.START_TAG); + return el; + } + + // Call the super function. + return goog.dom.ControlRangeIterator.superClass_.next.call(this); +}; + + +/** @override */ +goog.dom.ControlRangeIterator.prototype.copyFrom = function(other) { + this.elements_ = other.elements_; + this.startNode_ = other.startNode_; + this.endNode_ = other.endNode_; + + goog.dom.ControlRangeIterator.superClass_.copyFrom.call(this, other); +}; + + +/** + * @return {!goog.dom.ControlRangeIterator} An identical iterator. + * @override + */ +goog.dom.ControlRangeIterator.prototype.clone = function() { + var copy = new goog.dom.ControlRangeIterator(null); + copy.copyFrom(this); + return copy; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.html b/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.html new file mode 100644 index 0000000000..4bd100fd22 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.html @@ -0,0 +1,39 @@ + + + + + +Closure Unit Tests - goog.dom.ControlRange + + + + +
    +
    + +
    + +
    ab
    cd
    + + + + + + + +
    moof
    + foo + + bar +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.js new file mode 100644 index 0000000000..59b011d805 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/controlrange_test.js @@ -0,0 +1,228 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.ControlRangeTest'); +goog.setTestOnly('goog.dom.ControlRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.ControlRange'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.TextRange'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var logo; +var table; + +function setUpPage() { + logo = goog.dom.getElement('logo'); + table = goog.dom.getElement('table'); +} + +function testCreateFromElement() { + if (!goog.userAgent.IE) { + return; + } + assertNotNull('Control range object can be created for element', + goog.dom.ControlRange.createFromElements(logo)); +} + +function testCreateFromRange() { + if (!goog.userAgent.IE) { + return; + } + var range = document.body.createControlRange(); + range.addElement(table); + assertNotNull('Control range object can be created for element', + goog.dom.ControlRange.createFromBrowserRange(range)); +} + +function testSelect() { + if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('11')) { + return; + } + + var range = goog.dom.ControlRange.createFromElements(table); + range.select(); + + assertEquals('Control range should be selected', 'Control', + document.selection.type); + assertEquals('Control range should have length 1', 1, + document.selection.createRange().length); + assertEquals('Control range should select table', table, + document.selection.createRange().item(0)); +} + +function testControlRangeIterator() { + if (!goog.userAgent.IE) { + return; + } + var range = goog.dom.ControlRange.createFromElements(logo, table); + // Each node is included twice - once as a start tag, once as an end. + goog.testing.dom.assertNodesMatch(range, ['#logo', '#logo', '#table', + '#tbody', '#tr1', '#td11', 'a', '#td11', '#td12', 'b', '#td12', '#tr1', + '#tr2', '#td21', 'c', '#td21', '#td22', 'd', '#td22', '#tr2', '#tbody', + '#table']); +} + +function testBounds() { + if (!goog.userAgent.IE) { + return; + } + + // Initialize in both orders. + helpTestBounds(goog.dom.ControlRange.createFromElements(logo, table)); + helpTestBounds(goog.dom.ControlRange.createFromElements(table, logo)); +} + +function helpTestBounds(range) { + assertEquals('Start node is logo', logo, range.getStartNode()); + assertEquals('Start offset is 0', 0, range.getStartOffset()); + assertEquals('End node is table', table, range.getEndNode()); + assertEquals('End offset is 1', 1, range.getEndOffset()); +} + +function testCollapse() { + if (!goog.userAgent.IE) { + return; + } + + var range = goog.dom.ControlRange.createFromElements(logo, table); + assertFalse('Not initially collapsed', range.isCollapsed()); + range.collapse(); + assertTrue('Successfully collapsed', range.isCollapsed()); +} + +function testGetContainer() { + if (!goog.userAgent.IE) { + return; + } + + var range = goog.dom.ControlRange.createFromElements(logo); + assertEquals('Single element range is contained by itself', logo, + range.getContainer()); + + range = goog.dom.ControlRange.createFromElements(logo, table); + assertEquals('Two element range is contained by body', document.body, + range.getContainer()); +} + +function testSave() { + if (!goog.userAgent.IE) { + return; + } + + var range = goog.dom.ControlRange.createFromElements(logo, table); + var savedRange = range.saveUsingDom(); + + range.collapse(); + assertTrue('Successfully collapsed', range.isCollapsed()); + + range = savedRange.restore(); + assertEquals('Restored a control range', goog.dom.RangeType.CONTROL, + range.getType()); + assertFalse('Not collapsed after restore', range.isCollapsed()); + helpTestBounds(range); +} + +function testRemoveContents() { + if (!goog.userAgent.IE) { + return; + } + + var img = goog.dom.createDom('IMG'); + img.src = logo.src; + + var div = goog.dom.getElement('test1'); + div.innerHTML = ''; + div.appendChild(img); + assertEquals('Div has 1 child', 1, div.childNodes.length); + + var range = goog.dom.ControlRange.createFromElements(img); + range.removeContents(); + assertEquals('Div has 0 children', 0, div.childNodes.length); + assertTrue('Range is collapsed', range.isCollapsed()); +} + +function testReplaceContents() { + // Test a control range. + if (!goog.userAgent.IE) { + return; + } + + var outer = goog.dom.getElement('test1'); + outer.innerHTML = + '
    ' + + 'Hello ' + + '
    '; + range = goog.dom.ControlRange.createFromElements( + outer.getElementsByTagName(goog.dom.TagName.INPUT)[0]); + goog.dom.ControlRange.createFromElements(table); + range.replaceContentsWithNode(goog.dom.createTextNode('World')); + assertEquals('Hello World', outer.firstChild.innerHTML); +} + +function testContainsRange() { + if (!goog.userAgent.IE) { + return; + } + + var table2 = goog.dom.getElement('table2'); + var table2td = goog.dom.getElement('table2td'); + var logo2 = goog.dom.getElement('logo2'); + + var range = goog.dom.ControlRange.createFromElements(logo, table); + var range2 = goog.dom.ControlRange.createFromElements(logo); + assertTrue('Control range contains the other control range', + range.containsRange(range2)); + assertTrue('Control range partially contains the other control range', + range2.containsRange(range, true)); + + range2 = goog.dom.ControlRange.createFromElements(table2); + assertFalse('Control range does not contain the other control range', + range.containsRange(range2)); + + range = goog.dom.ControlRange.createFromElements(table2); + range2 = goog.dom.TextRange.createFromNodeContents(table2td); + assertTrue('Control range contains text range', + range.containsRange(range2)); + + range2 = goog.dom.TextRange.createFromNodeContents(table); + assertFalse('Control range does not contain text range', + range.containsRange(range2)); + + range = goog.dom.ControlRange.createFromElements(logo2); + range2 = goog.dom.TextRange.createFromNodeContents(table2); + assertFalse('Control range does not fully contain text range', + range.containsRange(range2, false)); + + range2 = goog.dom.ControlRange.createFromElements(table2); + assertTrue('Control range contains the other control range (2)', + range2.containsRange(range)); +} + +function testCloneRange() { + if (!goog.userAgent.IE) { + return; + } + var range = goog.dom.ControlRange.createFromElements(logo); + assertNotNull('Control range object created for element', range); + + var cloneRange = range.clone(); + assertNotNull('Cloned control range object', cloneRange); + assertArrayEquals('Control range and clone have same elements', + range.getElements(), cloneRange.getElements()); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/dataset.js b/mapbox-gl/closure-library/closure/goog/dom/dataset.js new file mode 100644 index 0000000000..579851321a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dataset.js @@ -0,0 +1,140 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for adding, removing and setting values in + * an Element's dataset. + * See {@link http://www.w3.org/TR/html5/Overview.html#dom-dataset}. + * + */ + +goog.provide('goog.dom.dataset'); + +goog.require('goog.string'); + + +/** + * The DOM attribute name prefix that must be present for it to be considered + * for a dataset. + * @type {string} + * @const + * @private + */ +goog.dom.dataset.PREFIX_ = 'data-'; + + +/** + * Sets a custom data attribute on an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to set the custom data attribute on. + * @param {string} key Key for the custom data attribute. + * @param {string} value Value for the custom data attribute. + */ +goog.dom.dataset.set = function(element, key, value) { + if (element.dataset) { + element.dataset[key] = value; + } else { + element.setAttribute( + goog.dom.dataset.PREFIX_ + goog.string.toSelectorCase(key), + value); + } +}; + + +/** + * Gets a custom data attribute from an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + * @return {?string} The attribute value, if it exists. + */ +goog.dom.dataset.get = function(element, key) { + if (element.dataset) { + // Android browser (non-chrome) returns the empty string for + // element.dataset['doesNotExist']. + if (!(key in element.dataset)) { + return null; + } + return element.dataset[key]; + } else { + return element.getAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } +}; + + +/** + * Removes a custom data attribute from an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + */ +goog.dom.dataset.remove = function(element, key) { + if (element.dataset) { + delete element.dataset[key]; + } else { + element.removeAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } +}; + + +/** + * Checks whether custom data attribute exists on an element. The key should be + * in camelCase format (e.g "keyName" for the "data-key-name" attribute). + * + * @param {Element} element DOM node to get the custom data attribute from. + * @param {string} key Key for the custom data attribute. + * @return {boolean} Whether the attribute exists. + */ +goog.dom.dataset.has = function(element, key) { + if (element.dataset) { + return key in element.dataset; + } else if (element.hasAttribute) { + return element.hasAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key)); + } else { + return !!(element.getAttribute(goog.dom.dataset.PREFIX_ + + goog.string.toSelectorCase(key))); + } +}; + + +/** + * Gets all custom data attributes as a string map. The attribute names will be + * camel cased (e.g., data-foo-bar -> dataset['fooBar']). This operation is not + * safe for attributes having camel-cased names clashing with already existing + * properties (e.g., data-to-string -> dataset['toString']). + * @param {!Element} element DOM node to get the data attributes from. + * @return {!Object} The string map containing data attributes and their + * respective values. + */ +goog.dom.dataset.getAll = function(element) { + if (element.dataset) { + return element.dataset; + } else { + var dataset = {}; + var attributes = element.attributes; + for (var i = 0; i < attributes.length; ++i) { + var attribute = attributes[i]; + if (goog.string.startsWith(attribute.name, + goog.dom.dataset.PREFIX_)) { + // We use substr(5), since it's faster than replacing 'data-' with ''. + var key = goog.string.toCamelCase(attribute.name.substr(5)); + dataset[key] = attribute.value; + } + } + return dataset; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/dataset_test.html b/mapbox-gl/closure-library/closure/goog/dom/dataset_test.html new file mode 100644 index 0000000000..8599bdf9ce --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dataset_test.html @@ -0,0 +1,24 @@ + + + + + +Closure Unit Tests - goog.dom.dataset + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/dataset_test.js b/mapbox-gl/closure-library/closure/goog/dom/dataset_test.js new file mode 100644 index 0000000000..83cc7699eb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dataset_test.js @@ -0,0 +1,108 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.datasetTest'); +goog.setTestOnly('goog.dom.datasetTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.dataset'); +goog.require('goog.testing.jsunit'); + +var $ = goog.dom.getElement; +var dataset = goog.dom.dataset; + + +function setUp() { + var el = $('el2'); + el.setAttribute('data-dynamic-key', 'dynamic'); +} + + +function testHas() { + var el = $('el1'); + + assertTrue('Dataset should have an existing key', + dataset.has(el, 'basicKey')); + assertTrue('Dataset should have an existing (unusual) key', + dataset.has(el, 'UnusualKey1')); + assertTrue('Dataset should have an existing (unusual) key', + dataset.has(el, 'unusual-Key2')); + assertTrue('Dataset should have an existing (bizarre) key', + dataset.has(el, '-Bizarre--Key')); + assertFalse('Dataset should not have a non-existent key', + dataset.has(el, 'bogusKey')); +} + + +function testGet() { + var el = $('el1'); + + assertEquals('Dataset should return the proper value for an existing key', + dataset.get(el, 'basicKey'), 'basic'); + assertEquals('Dataset should have an existing (unusual) key', + dataset.get(el, 'UnusualKey1'), 'unusual1'); + assertEquals('Dataset should have an existing (unusual) key', + dataset.get(el, 'unusual-Key2'), 'unusual2'); + assertEquals('Dataset should have an existing (bizarre) key', + dataset.get(el, '-Bizarre--Key'), 'bizarre'); + assertFalse( + 'Dataset should return null or an empty string for a non-existent key', + !!dataset.get(el, 'bogusKey')); + + el = $('el2'); + assertEquals('Dataset should return the proper value for an existing key', + dataset.get(el, 'dynamicKey'), 'dynamic'); +} + + +function testSet() { + var el = $('el2'); + + dataset.set(el, 'newKey', 'newValue'); + assertTrue('Dataset should have a newly created key', + dataset.has(el, 'newKey')); + assertEquals('Dataset should return the proper value for a newly created key', + dataset.get(el, 'newKey'), 'newValue'); + + dataset.set(el, 'dynamicKey', 'customValue'); + assertTrue('Dataset should have a modified, existing key', + dataset.has(el, 'dynamicKey')); + assertEquals('Dataset should return the proper value for a modified key', + dataset.get(el, 'dynamicKey'), 'customValue'); +} + + +function testRemove() { + var el = $('el2'); + + dataset.remove(el, 'dynamicKey'); + assertFalse('Dataset should not have a removed key', + dataset.has(el, 'dynamicKey')); + assertFalse('Dataset should return null or an empty string for removed key', + !!dataset.get(el, 'dynamicKey')); +} + + +function testGetAll() { + var el = $('el1'); + var expectedDataset = { + 'basicKey': 'basic', + 'UnusualKey1': 'unusual1', + 'unusual-Key2': 'unusual2', + '-Bizarre--Key': 'bizarre' + }; + assertHashEquals('Dataset should have basicKey, UnusualKey1, ' + + 'unusual-Key2, and -Bizarre--Key', + expectedDataset, dataset.getAll(el)); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/dom.js b/mapbox-gl/closure-library/closure/goog/dom/dom.js new file mode 100644 index 0000000000..1c827e1fef --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dom.js @@ -0,0 +1,2987 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating the browser's Document Object Model + * Inspiration taken *heavily* from mochikit (http://mochikit.com/). + * + * You can use {@link goog.dom.DomHelper} to create new dom helpers that refer + * to a different document object. This is useful if you are working with + * frames or multiple windows. + * + * @author arv@google.com (Erik Arvidsson) + */ + + +// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem +// is that getTextContent should mimic the DOM3 textContent. We should add a +// getInnerText (or getText) which tries to return the visible text, innerText. + + +goog.provide('goog.dom'); +goog.provide('goog.dom.Appendable'); +goog.provide('goog.dom.DomHelper'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom.BrowserFeature'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.safe'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.math.Coordinate'); +goog.require('goog.math.Size'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.userAgent'); + + +/** + * @define {boolean} Whether we know at compile time that the browser is in + * quirks mode. + */ +goog.define('goog.dom.ASSUME_QUIRKS_MODE', false); + + +/** + * @define {boolean} Whether we know at compile time that the browser is in + * standards compliance mode. + */ +goog.define('goog.dom.ASSUME_STANDARDS_MODE', false); + + +/** + * Whether we know the compatibility mode at compile time. + * @type {boolean} + * @private + */ +goog.dom.COMPAT_MODE_KNOWN_ = + goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE; + + +/** + * Gets the DomHelper object for the document where the element resides. + * @param {(Node|Window)=} opt_element If present, gets the DomHelper for this + * element. + * @return {!goog.dom.DomHelper} The DomHelper. + */ +goog.dom.getDomHelper = function(opt_element) { + return opt_element ? + new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) : + (goog.dom.defaultDomHelper_ || + (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper())); +}; + + +/** + * Cached default DOM helper. + * @type {goog.dom.DomHelper} + * @private + */ +goog.dom.defaultDomHelper_; + + +/** + * Gets the document object being used by the dom library. + * @return {!Document} Document object. + */ +goog.dom.getDocument = function() { + return document; +}; + + +/** + * Gets an element from the current document by element id. + * + * If an Element is passed in, it is returned. + * + * @param {string|Element} element Element ID or a DOM node. + * @return {Element} The element with the given ID, or the node passed in. + */ +goog.dom.getElement = function(element) { + return goog.dom.getElementHelper_(document, element); +}; + + +/** + * Gets an element by id from the given document (if present). + * If an element is given, it is returned. + * @param {!Document} doc + * @param {string|Element} element Element ID or a DOM node. + * @return {Element} The resulting element. + * @private + */ +goog.dom.getElementHelper_ = function(doc, element) { + return goog.isString(element) ? + doc.getElementById(element) : + element; +}; + + +/** + * Gets an element by id, asserting that the element is found. + * + * This is used when an element is expected to exist, and should fail with + * an assertion error if it does not (if assertions are enabled). + * + * @param {string} id Element ID. + * @return {!Element} The element with the given ID, if it exists. + */ +goog.dom.getRequiredElement = function(id) { + return goog.dom.getRequiredElementHelper_(document, id); +}; + + +/** + * Helper function for getRequiredElementHelper functions, both static and + * on DomHelper. Asserts the element with the given id exists. + * @param {!Document} doc + * @param {string} id + * @return {!Element} The element with the given ID, if it exists. + * @private + */ +goog.dom.getRequiredElementHelper_ = function(doc, id) { + // To prevent users passing in Elements as is permitted in getElement(). + goog.asserts.assertString(id); + var element = goog.dom.getElementHelper_(doc, id); + element = goog.asserts.assertElement(element, + 'No element found with id: ' + id); + return element; +}; + + +/** + * Alias for getElement. + * @param {string|Element} element Element ID or a DOM node. + * @return {Element} The element with the given ID, or the node passed in. + * @deprecated Use {@link goog.dom.getElement} instead. + */ +goog.dom.$ = goog.dom.getElement; + + +/** + * Looks up elements by both tag and class name, using browser native functions + * ({@code querySelectorAll}, {@code getElementsByTagName} or + * {@code getElementsByClassName}) where possible. This function + * is a useful, if limited, way of collecting a list of DOM elements + * with certain characteristics. {@code goog.dom.query} offers a + * more powerful and general solution which allows matching on CSS3 + * selector expressions, but at increased cost in code size. If all you + * need is particular tags belonging to a single class, this function + * is fast and sleek. + * + * Note that tag names are case sensitive in the SVG namespace, and this + * function converts opt_tag to uppercase for comparisons. For queries in the + * SVG namespace you should use querySelector or querySelectorAll instead. + * https://bugzilla.mozilla.org/show_bug.cgi?id=963870 + * https://bugs.webkit.org/show_bug.cgi?id=83438 + * + * @see {goog.dom.query} + * + * @param {?string=} opt_tag Element tag name. + * @param {?string=} opt_class Optional class name. + * @param {(Document|Element)=} opt_el Optional element to look in. + * @return { {length: number} } Array-like list of elements (only a length + * property and numerical indices are guaranteed to exist). + */ +goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) { + return goog.dom.getElementsByTagNameAndClass_(document, opt_tag, opt_class, + opt_el); +}; + + +/** + * Returns a static, array-like list of the elements with the provided + * className. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {(Document|Element)=} opt_el Optional element to look in. + * @return { {length: number} } The items found with the class name provided. + */ +goog.dom.getElementsByClass = function(className, opt_el) { + var parent = opt_el || document; + if (goog.dom.canUseQuerySelector_(parent)) { + return parent.querySelectorAll('.' + className); + } + return goog.dom.getElementsByTagNameAndClass_( + document, '*', className, opt_el); +}; + + +/** + * Returns the first element with the provided className. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {Element|Document=} opt_el Optional element to look in. + * @return {Element} The first item with the class name provided. + */ +goog.dom.getElementByClass = function(className, opt_el) { + var parent = opt_el || document; + var retVal = null; + if (goog.dom.canUseQuerySelector_(parent)) { + retVal = parent.querySelector('.' + className); + } else { + retVal = goog.dom.getElementsByTagNameAndClass_( + document, '*', className, opt_el)[0]; + } + return retVal || null; +}; + + +/** + * Ensures an element with the given className exists, and then returns the + * first element with the provided className. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {!Element|!Document=} opt_root Optional element or document to look + * in. + * @return {!Element} The first item with the class name provided. + * @throws {goog.asserts.AssertionError} Thrown if no element is found. + */ +goog.dom.getRequiredElementByClass = function(className, opt_root) { + var retValue = goog.dom.getElementByClass(className, opt_root); + return goog.asserts.assert(retValue, + 'No element found with className: ' + className); +}; + + +/** + * Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and + * fast W3C Selectors API. + * @param {!(Element|Document)} parent The parent document object. + * @return {boolean} whether or not we can use parent.querySelector* APIs. + * @private + */ +goog.dom.canUseQuerySelector_ = function(parent) { + return !!(parent.querySelectorAll && parent.querySelector); +}; + + +/** + * Helper for {@code getElementsByTagNameAndClass}. + * @param {!Document} doc The document to get the elements in. + * @param {?string=} opt_tag Element tag name. + * @param {?string=} opt_class Optional class name. + * @param {(Document|Element)=} opt_el Optional element to look in. + * @return { {length: number} } Array-like list of elements (only a length + * property and numerical indices are guaranteed to exist). + * @private + */ +goog.dom.getElementsByTagNameAndClass_ = function(doc, opt_tag, opt_class, + opt_el) { + var parent = opt_el || doc; + var tagName = (opt_tag && opt_tag != '*') ? opt_tag.toUpperCase() : ''; + + if (goog.dom.canUseQuerySelector_(parent) && + (tagName || opt_class)) { + var query = tagName + (opt_class ? '.' + opt_class : ''); + return parent.querySelectorAll(query); + } + + // Use the native getElementsByClassName if available, under the assumption + // that even when the tag name is specified, there will be fewer elements to + // filter through when going by class than by tag name + if (opt_class && parent.getElementsByClassName) { + var els = parent.getElementsByClassName(opt_class); + + if (tagName) { + var arrayLike = {}; + var len = 0; + + // Filter for specific tags if requested. + for (var i = 0, el; el = els[i]; i++) { + if (tagName == el.nodeName) { + arrayLike[len++] = el; + } + } + arrayLike.length = len; + + return arrayLike; + } else { + return els; + } + } + + var els = parent.getElementsByTagName(tagName || '*'); + + if (opt_class) { + var arrayLike = {}; + var len = 0; + for (var i = 0, el; el = els[i]; i++) { + var className = el.className; + // Check if className has a split function since SVG className does not. + if (typeof className.split == 'function' && + goog.array.contains(className.split(/\s+/), opt_class)) { + arrayLike[len++] = el; + } + } + arrayLike.length = len; + return arrayLike; + } else { + return els; + } +}; + + +/** + * Alias for {@code getElementsByTagNameAndClass}. + * @param {?string=} opt_tag Element tag name. + * @param {?string=} opt_class Optional class name. + * @param {Element=} opt_el Optional element to look in. + * @return { {length: number} } Array-like list of elements (only a length + * property and numerical indices are guaranteed to exist). + * @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead. + */ +goog.dom.$$ = goog.dom.getElementsByTagNameAndClass; + + +/** + * Sets multiple properties on a node. + * @param {Element} element DOM node to set properties on. + * @param {Object} properties Hash of property:value pairs. + */ +goog.dom.setProperties = function(element, properties) { + goog.object.forEach(properties, function(val, key) { + if (key == 'style') { + element.style.cssText = val; + } else if (key == 'class') { + element.className = val; + } else if (key == 'for') { + element.htmlFor = val; + } else if (key in goog.dom.DIRECT_ATTRIBUTE_MAP_) { + element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val); + } else if (goog.string.startsWith(key, 'aria-') || + goog.string.startsWith(key, 'data-')) { + element.setAttribute(key, val); + } else { + element[key] = val; + } + }); +}; + + +/** + * Map of attributes that should be set using + * element.setAttribute(key, val) instead of element[key] = val. Used + * by goog.dom.setProperties. + * + * @private {!Object} + * @const + */ +goog.dom.DIRECT_ATTRIBUTE_MAP_ = { + 'cellpadding': 'cellPadding', + 'cellspacing': 'cellSpacing', + 'colspan': 'colSpan', + 'frameborder': 'frameBorder', + 'height': 'height', + 'maxlength': 'maxLength', + 'role': 'role', + 'rowspan': 'rowSpan', + 'type': 'type', + 'usemap': 'useMap', + 'valign': 'vAlign', + 'width': 'width' +}; + + +/** + * Gets the dimensions of the viewport. + * + * Gecko Standards mode: + * docEl.clientWidth Width of viewport excluding scrollbar. + * win.innerWidth Width of viewport including scrollbar. + * body.clientWidth Width of body element. + * + * docEl.clientHeight Height of viewport excluding scrollbar. + * win.innerHeight Height of viewport including scrollbar. + * body.clientHeight Height of document. + * + * Gecko Backwards compatible mode: + * docEl.clientWidth Width of viewport excluding scrollbar. + * win.innerWidth Width of viewport including scrollbar. + * body.clientWidth Width of viewport excluding scrollbar. + * + * docEl.clientHeight Height of document. + * win.innerHeight Height of viewport including scrollbar. + * body.clientHeight Height of viewport excluding scrollbar. + * + * IE6/7 Standards mode: + * docEl.clientWidth Width of viewport excluding scrollbar. + * win.innerWidth Undefined. + * body.clientWidth Width of body element. + * + * docEl.clientHeight Height of viewport excluding scrollbar. + * win.innerHeight Undefined. + * body.clientHeight Height of document element. + * + * IE5 + IE6/7 Backwards compatible mode: + * docEl.clientWidth 0. + * win.innerWidth Undefined. + * body.clientWidth Width of viewport excluding scrollbar. + * + * docEl.clientHeight 0. + * win.innerHeight Undefined. + * body.clientHeight Height of viewport excluding scrollbar. + * + * Opera 9 Standards and backwards compatible mode: + * docEl.clientWidth Width of viewport excluding scrollbar. + * win.innerWidth Width of viewport including scrollbar. + * body.clientWidth Width of viewport excluding scrollbar. + * + * docEl.clientHeight Height of document. + * win.innerHeight Height of viewport including scrollbar. + * body.clientHeight Height of viewport excluding scrollbar. + * + * WebKit: + * Safari 2 + * docEl.clientHeight Same as scrollHeight. + * docEl.clientWidth Same as innerWidth. + * win.innerWidth Width of viewport excluding scrollbar. + * win.innerHeight Height of the viewport including scrollbar. + * frame.innerHeight Height of the viewport exluding scrollbar. + * + * Safari 3 (tested in 522) + * + * docEl.clientWidth Width of viewport excluding scrollbar. + * docEl.clientHeight Height of viewport excluding scrollbar in strict mode. + * body.clientHeight Height of viewport excluding scrollbar in quirks mode. + * + * @param {Window=} opt_window Optional window element to test. + * @return {!goog.math.Size} Object with values 'width' and 'height'. + */ +goog.dom.getViewportSize = function(opt_window) { + // TODO(arv): This should not take an argument + return goog.dom.getViewportSize_(opt_window || window); +}; + + +/** + * Helper for {@code getViewportSize}. + * @param {Window} win The window to get the view port size for. + * @return {!goog.math.Size} Object with values 'width' and 'height'. + * @private + */ +goog.dom.getViewportSize_ = function(win) { + var doc = win.document; + var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body; + return new goog.math.Size(el.clientWidth, el.clientHeight); +}; + + +/** + * Calculates the height of the document. + * + * @return {number} The height of the current document. + */ +goog.dom.getDocumentHeight = function() { + return goog.dom.getDocumentHeight_(window); +}; + + +/** + * Calculates the height of the document of the given window. + * + * Function code copied from the opensocial gadget api: + * gadgets.window.adjustHeight(opt_height) + * + * @private + * @param {!Window} win The window whose document height to retrieve. + * @return {number} The height of the document of the given window. + */ +goog.dom.getDocumentHeight_ = function(win) { + // NOTE(eae): This method will return the window size rather than the document + // size in webkit quirks mode. + var doc = win.document; + var height = 0; + + if (doc) { + // Calculating inner content height is hard and different between + // browsers rendering in Strict vs. Quirks mode. We use a combination of + // three properties within document.body and document.documentElement: + // - scrollHeight + // - offsetHeight + // - clientHeight + // These values differ significantly between browsers and rendering modes. + // But there are patterns. It just takes a lot of time and persistence + // to figure out. + + var body = doc.body; + var docEl = doc.documentElement; + if (!(docEl && body)) { + return 0; + } + + // Get the height of the viewport + var vh = goog.dom.getViewportSize_(win).height; + if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) { + // In Strict mode: + // The inner content height is contained in either: + // document.documentElement.scrollHeight + // document.documentElement.offsetHeight + // Based on studying the values output by different browsers, + // use the value that's NOT equal to the viewport height found above. + height = docEl.scrollHeight != vh ? + docEl.scrollHeight : docEl.offsetHeight; + } else { + // In Quirks mode: + // documentElement.clientHeight is equal to documentElement.offsetHeight + // except in IE. In most browsers, document.documentElement can be used + // to calculate the inner content height. + // However, in other browsers (e.g. IE), document.body must be used + // instead. How do we know which one to use? + // If document.documentElement.clientHeight does NOT equal + // document.documentElement.offsetHeight, then use document.body. + var sh = docEl.scrollHeight; + var oh = docEl.offsetHeight; + if (docEl.clientHeight != oh) { + sh = body.scrollHeight; + oh = body.offsetHeight; + } + + // Detect whether the inner content height is bigger or smaller + // than the bounding box (viewport). If bigger, take the larger + // value. If smaller, take the smaller value. + if (sh > vh) { + // Content is larger + height = sh > oh ? sh : oh; + } else { + // Content is smaller + height = sh < oh ? sh : oh; + } + } + } + + return height; +}; + + +/** + * Gets the page scroll distance as a coordinate object. + * + * @param {Window=} opt_window Optional window element to test. + * @return {!goog.math.Coordinate} Object with values 'x' and 'y'. + * @deprecated Use {@link goog.dom.getDocumentScroll} instead. + */ +goog.dom.getPageScroll = function(opt_window) { + var win = opt_window || goog.global || window; + return goog.dom.getDomHelper(win.document).getDocumentScroll(); +}; + + +/** + * Gets the document scroll distance as a coordinate object. + * + * @return {!goog.math.Coordinate} Object with values 'x' and 'y'. + */ +goog.dom.getDocumentScroll = function() { + return goog.dom.getDocumentScroll_(document); +}; + + +/** + * Helper for {@code getDocumentScroll}. + * + * @param {!Document} doc The document to get the scroll for. + * @return {!goog.math.Coordinate} Object with values 'x' and 'y'. + * @private + */ +goog.dom.getDocumentScroll_ = function(doc) { + var el = goog.dom.getDocumentScrollElement_(doc); + var win = goog.dom.getWindow_(doc); + if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') && + win.pageYOffset != el.scrollTop) { + // The keyboard on IE10 touch devices shifts the page using the pageYOffset + // without modifying scrollTop. For this case, we want the body scroll + // offsets. + return new goog.math.Coordinate(el.scrollLeft, el.scrollTop); + } + return new goog.math.Coordinate(win.pageXOffset || el.scrollLeft, + win.pageYOffset || el.scrollTop); +}; + + +/** + * Gets the document scroll element. + * @return {!Element} Scrolling element. + */ +goog.dom.getDocumentScrollElement = function() { + return goog.dom.getDocumentScrollElement_(document); +}; + + +/** + * Helper for {@code getDocumentScrollElement}. + * @param {!Document} doc The document to get the scroll element for. + * @return {!Element} Scrolling element. + * @private + */ +goog.dom.getDocumentScrollElement_ = function(doc) { + // WebKit needs body.scrollLeft in both quirks mode and strict mode. We also + // default to the documentElement if the document does not have a body (e.g. + // a SVG document). + if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) { + return doc.documentElement; + } + return doc.body || doc.documentElement; +}; + + +/** + * Gets the window object associated with the given document. + * + * @param {Document=} opt_doc Document object to get window for. + * @return {!Window} The window associated with the given document. + */ +goog.dom.getWindow = function(opt_doc) { + // TODO(arv): This should not take an argument. + return opt_doc ? goog.dom.getWindow_(opt_doc) : window; +}; + + +/** + * Helper for {@code getWindow}. + * + * @param {!Document} doc Document object to get window for. + * @return {!Window} The window associated with the given document. + * @private + */ +goog.dom.getWindow_ = function(doc) { + return doc.parentWindow || doc.defaultView; +}; + + +/** + * Returns a dom node with a set of attributes. This function accepts varargs + * for subsequent nodes to be added. Subsequent nodes will be added to the + * first node as childNodes. + * + * So: + * createDom('div', null, createDom('p'), createDom('p')); + * would return a div with two child paragraphs + * + * @param {string} tagName Tag to create. + * @param {(Object|Array|string)=} opt_attributes If object, then a map + * of name-value pairs for attributes. If a string, then this is the + * className of the new element. If an array, the elements will be joined + * together as the className of the new element. + * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or + * strings for text nodes. If one of the var_args is an array or NodeList,i + * its elements will be added as childNodes instead. + * @return {!Element} Reference to a DOM node. + */ +goog.dom.createDom = function(tagName, opt_attributes, var_args) { + return goog.dom.createDom_(document, arguments); +}; + + +/** + * Helper for {@code createDom}. + * @param {!Document} doc The document to create the DOM in. + * @param {!Arguments} args Argument object passed from the callers. See + * {@code goog.dom.createDom} for details. + * @return {!Element} Reference to a DOM node. + * @private + */ +goog.dom.createDom_ = function(doc, args) { + var tagName = args[0]; + var attributes = args[1]; + + // Internet Explorer is dumb: http://msdn.microsoft.com/workshop/author/ + // dhtml/reference/properties/name_2.asp + // Also does not allow setting of 'type' attribute on 'input' or 'button'. + if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes && + (attributes.name || attributes.type)) { + var tagNameArr = ['<', tagName]; + if (attributes.name) { + tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name), + '"'); + } + if (attributes.type) { + tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type), + '"'); + + // Clone attributes map to remove 'type' without mutating the input. + var clone = {}; + goog.object.extend(clone, attributes); + + // JSCompiler can't see how goog.object.extend added this property, + // because it was essentially added by reflection. + // So it needs to be quoted. + delete clone['type']; + + attributes = clone; + } + tagNameArr.push('>'); + tagName = tagNameArr.join(''); + } + + var element = doc.createElement(tagName); + + if (attributes) { + if (goog.isString(attributes)) { + element.className = attributes; + } else if (goog.isArray(attributes)) { + element.className = attributes.join(' '); + } else { + goog.dom.setProperties(element, attributes); + } + } + + if (args.length > 2) { + goog.dom.append_(doc, element, args, 2); + } + + return element; +}; + + +/** + * Appends a node with text or other nodes. + * @param {!Document} doc The document to create new nodes in. + * @param {!Node} parent The node to append nodes to. + * @param {!Arguments} args The values to add. See {@code goog.dom.append}. + * @param {number} startIndex The index of the array to start from. + * @private + */ +goog.dom.append_ = function(doc, parent, args, startIndex) { + function childHandler(child) { + // TODO(user): More coercion, ala MochiKit? + if (child) { + parent.appendChild(goog.isString(child) ? + doc.createTextNode(child) : child); + } + } + + for (var i = startIndex; i < args.length; i++) { + var arg = args[i]; + // TODO(attila): Fix isArrayLike to return false for a text node. + if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) { + // If the argument is a node list, not a real array, use a clone, + // because forEach can't be used to mutate a NodeList. + goog.array.forEach(goog.dom.isNodeList(arg) ? + goog.array.toArray(arg) : arg, + childHandler); + } else { + childHandler(arg); + } + } +}; + + +/** + * Alias for {@code createDom}. + * @param {string} tagName Tag to create. + * @param {(string|Object)=} opt_attributes If object, then a map of name-value + * pairs for attributes. If a string, then this is the className of the new + * element. + * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or + * strings for text nodes. If one of the var_args is an array, its + * children will be added as childNodes instead. + * @return {!Element} Reference to a DOM node. + * @deprecated Use {@link goog.dom.createDom} instead. + */ +goog.dom.$dom = goog.dom.createDom; + + +/** + * Creates a new element. + * @param {string} name Tag name. + * @return {!Element} The new element. + */ +goog.dom.createElement = function(name) { + return document.createElement(name); +}; + + +/** + * Creates a new text node. + * @param {number|string} content Content. + * @return {!Text} The new text node. + */ +goog.dom.createTextNode = function(content) { + return document.createTextNode(String(content)); +}; + + +/** + * Create a table. + * @param {number} rows The number of rows in the table. Must be >= 1. + * @param {number} columns The number of columns in the table. Must be >= 1. + * @param {boolean=} opt_fillWithNbsp If true, fills table entries with + * {@code goog.string.Unicode.NBSP} characters. + * @return {!Element} The created table. + */ +goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) { + // TODO(user): Return HTMLTableElement, also in prototype function. + // Callers need to be updated to e.g. not assign numbers to table.cellSpacing. + return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp); +}; + + +/** + * Create a table. + * @param {!Document} doc Document object to use to create the table. + * @param {number} rows The number of rows in the table. Must be >= 1. + * @param {number} columns The number of columns in the table. Must be >= 1. + * @param {boolean} fillWithNbsp If true, fills table entries with + * {@code goog.string.Unicode.NBSP} characters. + * @return {!HTMLTableElement} The created table. + * @private + */ +goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) { + var table = /** @type {!HTMLTableElement} */ + (doc.createElement(goog.dom.TagName.TABLE)); + var tbody = table.appendChild(doc.createElement(goog.dom.TagName.TBODY)); + for (var i = 0; i < rows; i++) { + var tr = doc.createElement(goog.dom.TagName.TR); + for (var j = 0; j < columns; j++) { + var td = doc.createElement(goog.dom.TagName.TD); + // IE <= 9 will create a text node if we set text content to the empty + // string, so we avoid doing it unless necessary. This ensures that the + // same DOM tree is returned on all browsers. + if (fillWithNbsp) { + goog.dom.setTextContent(td, goog.string.Unicode.NBSP); + } + tr.appendChild(td); + } + tbody.appendChild(tr); + } + return table; +}; + + +/** + * Converts HTML markup into a node. + * @param {!goog.html.SafeHtml} html The HTML markup to convert. + * @return {!Node} The resulting node. + */ +goog.dom.safeHtmlToNode = function(html) { + return goog.dom.safeHtmlToNode_(document, html); +}; + + +/** + * Helper for {@code safeHtmlToNode}. + * @param {!Document} doc The document. + * @param {!goog.html.SafeHtml} html The HTML markup to convert. + * @return {!Node} The resulting node. + * @private + */ +goog.dom.safeHtmlToNode_ = function(doc, html) { + var tempDiv = doc.createElement('div'); + if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) { + goog.dom.safe.setInnerHtml(tempDiv, + goog.html.SafeHtml.concat(goog.html.SafeHtml.create('br'), html)); + tempDiv.removeChild(tempDiv.firstChild); + } else { + goog.dom.safe.setInnerHtml(tempDiv, html); + } + return goog.dom.childrenToNode_(doc, tempDiv); +}; + + +/** + * Converts an HTML string into a document fragment. The string must be + * sanitized in order to avoid cross-site scripting. For example + * {@code goog.dom.htmlToDocumentFragment('<img src=x onerror=alert(0)>')} + * triggers an alert in all browsers, even if the returned document fragment + * is thrown away immediately. + * + * @param {string} htmlString The HTML string to convert. + * @return {!Node} The resulting document fragment. + */ +goog.dom.htmlToDocumentFragment = function(htmlString) { + return goog.dom.htmlToDocumentFragment_(document, htmlString); +}; + + +// TODO(jakubvrana): Merge with {@code safeHtmlToNode_}. +/** + * Helper for {@code htmlToDocumentFragment}. + * + * @param {!Document} doc The document. + * @param {string} htmlString The HTML string to convert. + * @return {!Node} The resulting document fragment. + * @private + */ +goog.dom.htmlToDocumentFragment_ = function(doc, htmlString) { + var tempDiv = doc.createElement('div'); + if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) { + tempDiv.innerHTML = '
    ' + htmlString; + tempDiv.removeChild(tempDiv.firstChild); + } else { + tempDiv.innerHTML = htmlString; + } + return goog.dom.childrenToNode_(doc, tempDiv); +}; + + +/** + * Helper for {@code htmlToDocumentFragment_}. + * @param {!Document} doc The document. + * @param {!Node} tempDiv The input node. + * @return {!Node} The resulting node. + * @private + */ +goog.dom.childrenToNode_ = function(doc, tempDiv) { + if (tempDiv.childNodes.length == 1) { + return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild)); + } else { + var fragment = doc.createDocumentFragment(); + while (tempDiv.firstChild) { + fragment.appendChild(tempDiv.firstChild); + } + return fragment; + } +}; + + +/** + * Returns true if the browser is in "CSS1-compatible" (standards-compliant) + * mode, false otherwise. + * @return {boolean} True if in CSS1-compatible mode. + */ +goog.dom.isCss1CompatMode = function() { + return goog.dom.isCss1CompatMode_(document); +}; + + +/** + * Returns true if the browser is in "CSS1-compatible" (standards-compliant) + * mode, false otherwise. + * @param {!Document} doc The document to check. + * @return {boolean} True if in CSS1-compatible mode. + * @private + */ +goog.dom.isCss1CompatMode_ = function(doc) { + if (goog.dom.COMPAT_MODE_KNOWN_) { + return goog.dom.ASSUME_STANDARDS_MODE; + } + + return doc.compatMode == 'CSS1Compat'; +}; + + +/** + * Determines if the given node can contain children, intended to be used for + * HTML generation. + * + * IE natively supports node.canHaveChildren but has inconsistent behavior. + * Prior to IE8 the base tag allows children and in IE9 all nodes return true + * for canHaveChildren. + * + * In practice all non-IE browsers allow you to add children to any node, but + * the behavior is inconsistent: + * + *
    + *   var a = document.createElement('br');
    + *   a.appendChild(document.createTextNode('foo'));
    + *   a.appendChild(document.createTextNode('bar'));
    + *   console.log(a.childNodes.length);  // 2
    + *   console.log(a.innerHTML);  // Chrome: "", IE9: "foobar", FF3.5: "foobar"
    + * 
    + * + * For more information, see: + * http://dev.w3.org/html5/markup/syntax.html#syntax-elements + * + * TODO(user): Rename shouldAllowChildren() ? + * + * @param {Node} node The node to check. + * @return {boolean} Whether the node can contain children. + */ +goog.dom.canHaveChildren = function(node) { + if (node.nodeType != goog.dom.NodeType.ELEMENT) { + return false; + } + switch (node.tagName) { + case goog.dom.TagName.APPLET: + case goog.dom.TagName.AREA: + case goog.dom.TagName.BASE: + case goog.dom.TagName.BR: + case goog.dom.TagName.COL: + case goog.dom.TagName.COMMAND: + case goog.dom.TagName.EMBED: + case goog.dom.TagName.FRAME: + case goog.dom.TagName.HR: + case goog.dom.TagName.IMG: + case goog.dom.TagName.INPUT: + case goog.dom.TagName.IFRAME: + case goog.dom.TagName.ISINDEX: + case goog.dom.TagName.KEYGEN: + case goog.dom.TagName.LINK: + case goog.dom.TagName.NOFRAMES: + case goog.dom.TagName.NOSCRIPT: + case goog.dom.TagName.META: + case goog.dom.TagName.OBJECT: + case goog.dom.TagName.PARAM: + case goog.dom.TagName.SCRIPT: + case goog.dom.TagName.SOURCE: + case goog.dom.TagName.STYLE: + case goog.dom.TagName.TRACK: + case goog.dom.TagName.WBR: + return false; + } + return true; +}; + + +/** + * Appends a child to a node. + * @param {Node} parent Parent. + * @param {Node} child Child. + */ +goog.dom.appendChild = function(parent, child) { + parent.appendChild(child); +}; + + +/** + * Appends a node with text or other nodes. + * @param {!Node} parent The node to append nodes to. + * @param {...goog.dom.Appendable} var_args The things to append to the node. + * If this is a Node it is appended as is. + * If this is a string then a text node is appended. + * If this is an array like object then fields 0 to length - 1 are appended. + */ +goog.dom.append = function(parent, var_args) { + goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1); +}; + + +/** + * Removes all the child nodes on a DOM node. + * @param {Node} node Node to remove children from. + */ +goog.dom.removeChildren = function(node) { + // Note: Iterations over live collections can be slow, this is the fastest + // we could find. The double parenthesis are used to prevent JsCompiler and + // strict warnings. + var child; + while ((child = node.firstChild)) { + node.removeChild(child); + } +}; + + +/** + * Inserts a new node before an existing reference node (i.e. as the previous + * sibling). If the reference node has no parent, then does nothing. + * @param {Node} newNode Node to insert. + * @param {Node} refNode Reference node to insert before. + */ +goog.dom.insertSiblingBefore = function(newNode, refNode) { + if (refNode.parentNode) { + refNode.parentNode.insertBefore(newNode, refNode); + } +}; + + +/** + * Inserts a new node after an existing reference node (i.e. as the next + * sibling). If the reference node has no parent, then does nothing. + * @param {Node} newNode Node to insert. + * @param {Node} refNode Reference node to insert after. + */ +goog.dom.insertSiblingAfter = function(newNode, refNode) { + if (refNode.parentNode) { + refNode.parentNode.insertBefore(newNode, refNode.nextSibling); + } +}; + + +/** + * Insert a child at a given index. If index is larger than the number of child + * nodes that the parent currently has, the node is inserted as the last child + * node. + * @param {Element} parent The element into which to insert the child. + * @param {Node} child The element to insert. + * @param {number} index The index at which to insert the new child node. Must + * not be negative. + */ +goog.dom.insertChildAt = function(parent, child, index) { + // Note that if the second argument is null, insertBefore + // will append the child at the end of the list of children. + parent.insertBefore(child, parent.childNodes[index] || null); +}; + + +/** + * Removes a node from its parent. + * @param {Node} node The node to remove. + * @return {Node} The node removed if removed; else, null. + */ +goog.dom.removeNode = function(node) { + return node && node.parentNode ? node.parentNode.removeChild(node) : null; +}; + + +/** + * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no + * parent. + * @param {Node} newNode Node to insert. + * @param {Node} oldNode Node to replace. + */ +goog.dom.replaceNode = function(newNode, oldNode) { + var parent = oldNode.parentNode; + if (parent) { + parent.replaceChild(newNode, oldNode); + } +}; + + +/** + * Flattens an element. That is, removes it and replace it with its children. + * Does nothing if the element is not in the document. + * @param {Element} element The element to flatten. + * @return {Element|undefined} The original element, detached from the document + * tree, sans children; or undefined, if the element was not in the document + * to begin with. + */ +goog.dom.flattenElement = function(element) { + var child, parent = element.parentNode; + if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) { + // Use IE DOM method (supported by Opera too) if available + if (element.removeNode) { + return /** @type {Element} */ (element.removeNode(false)); + } else { + // Move all children of the original node up one level. + while ((child = element.firstChild)) { + parent.insertBefore(child, element); + } + + // Detach the original element. + return /** @type {Element} */ (goog.dom.removeNode(element)); + } + } +}; + + +/** + * Returns an array containing just the element children of the given element. + * @param {Element} element The element whose element children we want. + * @return {!(Array|NodeList)} An array or array-like list of just the element + * children of the given element. + */ +goog.dom.getChildren = function(element) { + // We check if the children attribute is supported for child elements + // since IE8 misuses the attribute by also including comments. + if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE && + element.children != undefined) { + return element.children; + } + // Fall back to manually filtering the element's child nodes. + return goog.array.filter(element.childNodes, function(node) { + return node.nodeType == goog.dom.NodeType.ELEMENT; + }); +}; + + +/** + * Returns the first child node that is an element. + * @param {Node} node The node to get the first child element of. + * @return {Element} The first child node of {@code node} that is an element. + */ +goog.dom.getFirstElementChild = function(node) { + if (node.firstElementChild != undefined) { + return /** @type {!Element} */(node).firstElementChild; + } + return goog.dom.getNextElementNode_(node.firstChild, true); +}; + + +/** + * Returns the last child node that is an element. + * @param {Node} node The node to get the last child element of. + * @return {Element} The last child node of {@code node} that is an element. + */ +goog.dom.getLastElementChild = function(node) { + if (node.lastElementChild != undefined) { + return /** @type {!Element} */(node).lastElementChild; + } + return goog.dom.getNextElementNode_(node.lastChild, false); +}; + + +/** + * Returns the first next sibling that is an element. + * @param {Node} node The node to get the next sibling element of. + * @return {Element} The next sibling of {@code node} that is an element. + */ +goog.dom.getNextElementSibling = function(node) { + if (node.nextElementSibling != undefined) { + return /** @type {!Element} */(node).nextElementSibling; + } + return goog.dom.getNextElementNode_(node.nextSibling, true); +}; + + +/** + * Returns the first previous sibling that is an element. + * @param {Node} node The node to get the previous sibling element of. + * @return {Element} The first previous sibling of {@code node} that is + * an element. + */ +goog.dom.getPreviousElementSibling = function(node) { + if (node.previousElementSibling != undefined) { + return /** @type {!Element} */(node).previousElementSibling; + } + return goog.dom.getNextElementNode_(node.previousSibling, false); +}; + + +/** + * Returns the first node that is an element in the specified direction, + * starting with {@code node}. + * @param {Node} node The node to get the next element from. + * @param {boolean} forward Whether to look forwards or backwards. + * @return {Element} The first element. + * @private + */ +goog.dom.getNextElementNode_ = function(node, forward) { + while (node && node.nodeType != goog.dom.NodeType.ELEMENT) { + node = forward ? node.nextSibling : node.previousSibling; + } + + return /** @type {Element} */ (node); +}; + + +/** + * Returns the next node in source order from the given node. + * @param {Node} node The node. + * @return {Node} The next node in the DOM tree, or null if this was the last + * node. + */ +goog.dom.getNextNode = function(node) { + if (!node) { + return null; + } + + if (node.firstChild) { + return node.firstChild; + } + + while (node && !node.nextSibling) { + node = node.parentNode; + } + + return node ? node.nextSibling : null; +}; + + +/** + * Returns the previous node in source order from the given node. + * @param {Node} node The node. + * @return {Node} The previous node in the DOM tree, or null if this was the + * first node. + */ +goog.dom.getPreviousNode = function(node) { + if (!node) { + return null; + } + + if (!node.previousSibling) { + return node.parentNode; + } + + node = node.previousSibling; + while (node && node.lastChild) { + node = node.lastChild; + } + + return node; +}; + + +/** + * Whether the object looks like a DOM node. + * @param {?} obj The object being tested for node likeness. + * @return {boolean} Whether the object looks like a DOM node. + */ +goog.dom.isNodeLike = function(obj) { + return goog.isObject(obj) && obj.nodeType > 0; +}; + + +/** + * Whether the object looks like an Element. + * @param {?} obj The object being tested for Element likeness. + * @return {boolean} Whether the object looks like an Element. + */ +goog.dom.isElement = function(obj) { + return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT; +}; + + +/** + * Returns true if the specified value is a Window object. This includes the + * global window for HTML pages, and iframe windows. + * @param {?} obj Variable to test. + * @return {boolean} Whether the variable is a window. + */ +goog.dom.isWindow = function(obj) { + return goog.isObject(obj) && obj['window'] == obj; +}; + + +/** + * Returns an element's parent, if it's an Element. + * @param {Element} element The DOM element. + * @return {Element} The parent, or null if not an Element. + */ +goog.dom.getParentElement = function(element) { + var parent; + if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) { + var isIe9 = goog.userAgent.IE && + goog.userAgent.isVersionOrHigher('9') && + !goog.userAgent.isVersionOrHigher('10'); + // SVG elements in IE9 can't use the parentElement property. + // goog.global['SVGElement'] is not defined in IE9 quirks mode. + if (!(isIe9 && goog.global['SVGElement'] && + element instanceof goog.global['SVGElement'])) { + parent = element.parentElement; + if (parent) { + return parent; + } + } + } + parent = element.parentNode; + return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null; +}; + + +/** + * Whether a node contains another node. + * @param {Node} parent The node that should contain the other node. + * @param {Node} descendant The node to test presence of. + * @return {boolean} Whether the parent node contains the descendent node. + */ +goog.dom.contains = function(parent, descendant) { + // We use browser specific methods for this if available since it is faster + // that way. + + // IE DOM + if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) { + return parent == descendant || parent.contains(descendant); + } + + // W3C DOM Level 3 + if (typeof parent.compareDocumentPosition != 'undefined') { + return parent == descendant || + Boolean(parent.compareDocumentPosition(descendant) & 16); + } + + // W3C DOM Level 1 + while (descendant && parent != descendant) { + descendant = descendant.parentNode; + } + return descendant == parent; +}; + + +/** + * Compares the document order of two nodes, returning 0 if they are the same + * node, a negative number if node1 is before node2, and a positive number if + * node2 is before node1. Note that we compare the order the tags appear in the + * document so in the tree text the B node is considered to be + * before the I node. + * + * @param {Node} node1 The first node to compare. + * @param {Node} node2 The second node to compare. + * @return {number} 0 if the nodes are the same node, a negative number if node1 + * is before node2, and a positive number if node2 is before node1. + */ +goog.dom.compareNodeOrder = function(node1, node2) { + // Fall out quickly for equality. + if (node1 == node2) { + return 0; + } + + // Use compareDocumentPosition where available + if (node1.compareDocumentPosition) { + // 4 is the bitmask for FOLLOWS. + return node1.compareDocumentPosition(node2) & 2 ? 1 : -1; + } + + // Special case for document nodes on IE 7 and 8. + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + if (node1.nodeType == goog.dom.NodeType.DOCUMENT) { + return -1; + } + if (node2.nodeType == goog.dom.NodeType.DOCUMENT) { + return 1; + } + } + + // Process in IE using sourceIndex - we check to see if the first node has + // a source index or if its parent has one. + if ('sourceIndex' in node1 || + (node1.parentNode && 'sourceIndex' in node1.parentNode)) { + var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT; + var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT; + + if (isElement1 && isElement2) { + return node1.sourceIndex - node2.sourceIndex; + } else { + var parent1 = node1.parentNode; + var parent2 = node2.parentNode; + + if (parent1 == parent2) { + return goog.dom.compareSiblingOrder_(node1, node2); + } + + if (!isElement1 && goog.dom.contains(parent1, node2)) { + return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2); + } + + + if (!isElement2 && goog.dom.contains(parent2, node1)) { + return goog.dom.compareParentsDescendantNodeIe_(node2, node1); + } + + return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) - + (isElement2 ? node2.sourceIndex : parent2.sourceIndex); + } + } + + // For Safari, we compare ranges. + var doc = goog.dom.getOwnerDocument(node1); + + var range1, range2; + range1 = doc.createRange(); + range1.selectNode(node1); + range1.collapse(true); + + range2 = doc.createRange(); + range2.selectNode(node2); + range2.collapse(true); + + return range1.compareBoundaryPoints(goog.global['Range'].START_TO_END, + range2); +}; + + +/** + * Utility function to compare the position of two nodes, when + * {@code textNode}'s parent is an ancestor of {@code node}. If this entry + * condition is not met, this function will attempt to reference a null object. + * @param {!Node} textNode The textNode to compare. + * @param {Node} node The node to compare. + * @return {number} -1 if node is before textNode, +1 otherwise. + * @private + */ +goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) { + var parent = textNode.parentNode; + if (parent == node) { + // If textNode is a child of node, then node comes first. + return -1; + } + var sibling = node; + while (sibling.parentNode != parent) { + sibling = sibling.parentNode; + } + return goog.dom.compareSiblingOrder_(sibling, textNode); +}; + + +/** + * Utility function to compare the position of two nodes known to be non-equal + * siblings. + * @param {Node} node1 The first node to compare. + * @param {!Node} node2 The second node to compare. + * @return {number} -1 if node1 is before node2, +1 otherwise. + * @private + */ +goog.dom.compareSiblingOrder_ = function(node1, node2) { + var s = node2; + while ((s = s.previousSibling)) { + if (s == node1) { + // We just found node1 before node2. + return -1; + } + } + + // Since we didn't find it, node1 must be after node2. + return 1; +}; + + +/** + * Find the deepest common ancestor of the given nodes. + * @param {...Node} var_args The nodes to find a common ancestor of. + * @return {Node} The common ancestor of the nodes, or null if there is none. + * null will only be returned if two or more of the nodes are from different + * documents. + */ +goog.dom.findCommonAncestor = function(var_args) { + var i, count = arguments.length; + if (!count) { + return null; + } else if (count == 1) { + return arguments[0]; + } + + var paths = []; + var minLength = Infinity; + for (i = 0; i < count; i++) { + // Compute the list of ancestors. + var ancestors = []; + var node = arguments[i]; + while (node) { + ancestors.unshift(node); + node = node.parentNode; + } + + // Save the list for comparison. + paths.push(ancestors); + minLength = Math.min(minLength, ancestors.length); + } + var output = null; + for (i = 0; i < minLength; i++) { + var first = paths[0][i]; + for (var j = 1; j < count; j++) { + if (first != paths[j][i]) { + return output; + } + } + output = first; + } + return output; +}; + + +/** + * Returns the owner document for a node. + * @param {Node|Window} node The node to get the document for. + * @return {!Document} The document owning the node. + */ +goog.dom.getOwnerDocument = function(node) { + // TODO(nnaze): Update param signature to be non-nullable. + goog.asserts.assert(node, 'Node cannot be null or undefined.'); + return /** @type {!Document} */ ( + node.nodeType == goog.dom.NodeType.DOCUMENT ? node : + node.ownerDocument || node.document); +}; + + +/** + * Cross-browser function for getting the document element of a frame or iframe. + * @param {Element} frame Frame element. + * @return {!Document} The frame content document. + */ +goog.dom.getFrameContentDocument = function(frame) { + var doc = frame.contentDocument || frame.contentWindow.document; + return doc; +}; + + +/** + * Cross-browser function for getting the window of a frame or iframe. + * @param {Element} frame Frame element. + * @return {Window} The window associated with the given frame. + */ +goog.dom.getFrameContentWindow = function(frame) { + return frame.contentWindow || + goog.dom.getWindow(goog.dom.getFrameContentDocument(frame)); +}; + + +/** + * Sets the text content of a node, with cross-browser support. + * @param {Node} node The node to change the text content of. + * @param {string|number} text The value that should replace the node's content. + */ +goog.dom.setTextContent = function(node, text) { + goog.asserts.assert(node != null, + 'goog.dom.setTextContent expects a non-null value for node'); + + if ('textContent' in node) { + node.textContent = text; + } else if (node.nodeType == goog.dom.NodeType.TEXT) { + node.data = text; + } else if (node.firstChild && + node.firstChild.nodeType == goog.dom.NodeType.TEXT) { + // If the first child is a text node we just change its data and remove the + // rest of the children. + while (node.lastChild != node.firstChild) { + node.removeChild(node.lastChild); + } + node.firstChild.data = text; + } else { + goog.dom.removeChildren(node); + var doc = goog.dom.getOwnerDocument(node); + node.appendChild(doc.createTextNode(String(text))); + } +}; + + +/** + * Gets the outerHTML of a node, which islike innerHTML, except that it + * actually contains the HTML of the node itself. + * @param {Element} element The element to get the HTML of. + * @return {string} The outerHTML of the given element. + */ +goog.dom.getOuterHtml = function(element) { + // IE, Opera and WebKit all have outerHTML. + if ('outerHTML' in element) { + return element.outerHTML; + } else { + var doc = goog.dom.getOwnerDocument(element); + var div = doc.createElement('div'); + div.appendChild(element.cloneNode(true)); + return div.innerHTML; + } +}; + + +/** + * Finds the first descendant node that matches the filter function, using + * a depth first search. This function offers the most general purpose way + * of finding a matching element. You may also wish to consider + * {@code goog.dom.query} which can express many matching criteria using + * CSS selector expressions. These expressions often result in a more + * compact representation of the desired result. + * @see goog.dom.query + * + * @param {Node} root The root of the tree to search. + * @param {function(Node) : boolean} p The filter function. + * @return {Node|undefined} The found node or undefined if none is found. + */ +goog.dom.findNode = function(root, p) { + var rv = []; + var found = goog.dom.findNodes_(root, p, rv, true); + return found ? rv[0] : undefined; +}; + + +/** + * Finds all the descendant nodes that match the filter function, using a + * a depth first search. This function offers the most general-purpose way + * of finding a set of matching elements. You may also wish to consider + * {@code goog.dom.query} which can express many matching criteria using + * CSS selector expressions. These expressions often result in a more + * compact representation of the desired result. + + * @param {Node} root The root of the tree to search. + * @param {function(Node) : boolean} p The filter function. + * @return {!Array} The found nodes or an empty array if none are found. + */ +goog.dom.findNodes = function(root, p) { + var rv = []; + goog.dom.findNodes_(root, p, rv, false); + return rv; +}; + + +/** + * Finds the first or all the descendant nodes that match the filter function, + * using a depth first search. + * @param {Node} root The root of the tree to search. + * @param {function(Node) : boolean} p The filter function. + * @param {!Array} rv The found nodes are added to this array. + * @param {boolean} findOne If true we exit after the first found node. + * @return {boolean} Whether the search is complete or not. True in case findOne + * is true and the node is found. False otherwise. + * @private + */ +goog.dom.findNodes_ = function(root, p, rv, findOne) { + if (root != null) { + var child = root.firstChild; + while (child) { + if (p(child)) { + rv.push(child); + if (findOne) { + return true; + } + } + if (goog.dom.findNodes_(child, p, rv, findOne)) { + return true; + } + child = child.nextSibling; + } + } + return false; +}; + + +/** + * Map of tags whose content to ignore when calculating text length. + * @private {!Object} + * @const + */ +goog.dom.TAGS_TO_IGNORE_ = { + 'SCRIPT': 1, + 'STYLE': 1, + 'HEAD': 1, + 'IFRAME': 1, + 'OBJECT': 1 +}; + + +/** + * Map of tags which have predefined values with regard to whitespace. + * @private {!Object} + * @const + */ +goog.dom.PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'}; + + +/** + * Returns true if the element has a tab index that allows it to receive + * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements + * natively support keyboard focus, even if they have no tab index. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element has a tab index that allows keyboard + * focus. + * @see http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + */ +goog.dom.isFocusableTabIndex = function(element) { + return goog.dom.hasSpecifiedTabIndex_(element) && + goog.dom.isTabIndexFocusable_(element); +}; + + +/** + * Enables or disables keyboard focus support on the element via its tab index. + * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true + * (or elements that natively support keyboard focus, like form elements) can + * receive keyboard focus. See http://go/tabindex for more info. + * @param {Element} element Element whose tab index is to be changed. + * @param {boolean} enable Whether to set or remove a tab index on the element + * that supports keyboard focus. + */ +goog.dom.setFocusableTabIndex = function(element, enable) { + if (enable) { + element.tabIndex = 0; + } else { + // Set tabIndex to -1 first, then remove it. This is a workaround for + // Safari (confirmed in version 4 on Windows). When removing the attribute + // without setting it to -1 first, the element remains keyboard focusable + // despite not having a tabIndex attribute anymore. + element.tabIndex = -1; + element.removeAttribute('tabIndex'); // Must be camelCase! + } +}; + + +/** + * Returns true if the element can be focused, i.e. it has a tab index that + * allows it to receive keyboard focus (tabIndex >= 0), or it is an element + * that natively supports keyboard focus. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element allows keyboard focus. + */ +goog.dom.isFocusable = function(element) { + var focusable; + // Some elements can have unspecified tab index and still receive focus. + if (goog.dom.nativelySupportsFocus_(element)) { + // Make sure the element is not disabled ... + focusable = !element.disabled && + // ... and if a tab index is specified, it allows focus. + (!goog.dom.hasSpecifiedTabIndex_(element) || + goog.dom.isTabIndexFocusable_(element)); + } else { + focusable = goog.dom.isFocusableTabIndex(element); + } + + // IE requires elements to be visible in order to focus them. + return focusable && goog.userAgent.IE ? + goog.dom.hasNonZeroBoundingRect_(element) : focusable; +}; + + +/** + * Returns true if the element has a specified tab index. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element has a specified tab index. + * @private + */ +goog.dom.hasSpecifiedTabIndex_ = function(element) { + // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(), + // which returns an object with a 'specified' property if tabIndex is + // specified. This works on other browsers, too. + var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase! + return goog.isDefAndNotNull(attrNode) && attrNode.specified; +}; + + +/** + * Returns true if the element's tab index allows the element to be focused. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element's tab index allows focus. + * @private + */ +goog.dom.isTabIndexFocusable_ = function(element) { + var index = element.tabIndex; + // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534. + return goog.isNumber(index) && index >= 0 && index < 32768; +}; + + +/** + * Returns true if the element is focusable even when tabIndex is not set. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element natively supports focus. + * @private + */ +goog.dom.nativelySupportsFocus_ = function(element) { + return element.tagName == goog.dom.TagName.A || + element.tagName == goog.dom.TagName.INPUT || + element.tagName == goog.dom.TagName.TEXTAREA || + element.tagName == goog.dom.TagName.SELECT || + element.tagName == goog.dom.TagName.BUTTON; +}; + + +/** + * Returns true if the element has a bounding rectangle that would be visible + * (i.e. its width and height are greater than zero). + * @param {!Element} element Element to check. + * @return {boolean} Whether the element has a non-zero bounding rectangle. + * @private + */ +goog.dom.hasNonZeroBoundingRect_ = function(element) { + var rect = goog.isFunction(element['getBoundingClientRect']) ? + element.getBoundingClientRect() : + {'height': element.offsetHeight, 'width': element.offsetWidth}; + return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0; +}; + + +/** + * Returns the text content of the current node, without markup and invisible + * symbols. New lines are stripped and whitespace is collapsed, + * such that each character would be visible. + * + * In browsers that support it, innerText is used. Other browsers attempt to + * simulate it via node traversal. Line breaks are canonicalized in IE. + * + * @param {Node} node The node from which we are getting content. + * @return {string} The text content. + */ +goog.dom.getTextContent = function(node) { + var textContent; + // Note(arv): IE9, Opera, and Safari 3 support innerText but they include + // text nodes in script tags. So we revert to use a user agent test here. + if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && ('innerText' in node)) { + textContent = goog.string.canonicalizeNewlines(node.innerText); + // Unfortunately .innerText() returns text with ­ symbols + // We need to filter it out and then remove duplicate whitespaces + } else { + var buf = []; + goog.dom.getTextContent_(node, buf, true); + textContent = buf.join(''); + } + + // Strip ­ entities. goog.format.insertWordBreaks inserts them in Opera. + textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, ''); + // Strip ​ entities. goog.format.insertWordBreaks inserts them in IE8. + textContent = textContent.replace(/\u200B/g, ''); + + // Skip this replacement on old browsers with working innerText, which + // automatically turns   into ' ' and / +/ into ' ' when reading + // innerText. + if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) { + textContent = textContent.replace(/ +/g, ' '); + } + if (textContent != ' ') { + textContent = textContent.replace(/^\s*/, ''); + } + + return textContent; +}; + + +/** + * Returns the text content of the current node, without markup. + * + * Unlike {@code getTextContent} this method does not collapse whitespaces + * or normalize lines breaks. + * + * @param {Node} node The node from which we are getting content. + * @return {string} The raw text content. + */ +goog.dom.getRawTextContent = function(node) { + var buf = []; + goog.dom.getTextContent_(node, buf, false); + + return buf.join(''); +}; + + +/** + * Recursive support function for text content retrieval. + * + * @param {Node} node The node from which we are getting content. + * @param {Array} buf string buffer. + * @param {boolean} normalizeWhitespace Whether to normalize whitespace. + * @private + */ +goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) { + if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) { + // ignore certain tags + } else if (node.nodeType == goog.dom.NodeType.TEXT) { + if (normalizeWhitespace) { + buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, '')); + } else { + buf.push(node.nodeValue); + } + } else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) { + buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]); + } else { + var child = node.firstChild; + while (child) { + goog.dom.getTextContent_(child, buf, normalizeWhitespace); + child = child.nextSibling; + } + } +}; + + +/** + * Returns the text length of the text contained in a node, without markup. This + * is equivalent to the selection length if the node was selected, or the number + * of cursor movements to traverse the node. Images & BRs take one space. New + * lines are ignored. + * + * @param {Node} node The node whose text content length is being calculated. + * @return {number} The length of {@code node}'s text content. + */ +goog.dom.getNodeTextLength = function(node) { + return goog.dom.getTextContent(node).length; +}; + + +/** + * Returns the text offset of a node relative to one of its ancestors. The text + * length is the same as the length calculated by goog.dom.getNodeTextLength. + * + * @param {Node} node The node whose offset is being calculated. + * @param {Node=} opt_offsetParent The node relative to which the offset will + * be calculated. Defaults to the node's owner document's body. + * @return {number} The text offset. + */ +goog.dom.getNodeTextOffset = function(node, opt_offsetParent) { + var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body; + var buf = []; + while (node && node != root) { + var cur = node; + while ((cur = cur.previousSibling)) { + buf.unshift(goog.dom.getTextContent(cur)); + } + node = node.parentNode; + } + // Trim left to deal with FF cases when there might be line breaks and empty + // nodes at the front of the text + return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length; +}; + + +/** + * Returns the node at a given offset in a parent node. If an object is + * provided for the optional third parameter, the node and the remainder of the + * offset will stored as properties of this object. + * @param {Node} parent The parent node. + * @param {number} offset The offset into the parent node. + * @param {Object=} opt_result Object to be used to store the return value. The + * return value will be stored in the form {node: Node, remainder: number} + * if this object is provided. + * @return {Node} The node at the given offset. + */ +goog.dom.getNodeAtOffset = function(parent, offset, opt_result) { + var stack = [parent], pos = 0, cur = null; + while (stack.length > 0 && pos < offset) { + cur = stack.pop(); + if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) { + // ignore certain tags + } else if (cur.nodeType == goog.dom.NodeType.TEXT) { + var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' '); + pos += text.length; + } else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) { + pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length; + } else { + for (var i = cur.childNodes.length - 1; i >= 0; i--) { + stack.push(cur.childNodes[i]); + } + } + } + if (goog.isObject(opt_result)) { + opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0; + opt_result.node = cur; + } + + return cur; +}; + + +/** + * Returns true if the object is a {@code NodeList}. To qualify as a NodeList, + * the object must have a numeric length property and an item function (which + * has type 'string' on IE for some reason). + * @param {Object} val Object to test. + * @return {boolean} Whether the object is a NodeList. + */ +goog.dom.isNodeList = function(val) { + // TODO(attila): Now the isNodeList is part of goog.dom we can use + // goog.userAgent to make this simpler. + // A NodeList must have a length property of type 'number' on all platforms. + if (val && typeof val.length == 'number') { + // A NodeList is an object everywhere except Safari, where it's a function. + if (goog.isObject(val)) { + // A NodeList must have an item function (on non-IE platforms) or an item + // property of type 'string' (on IE). + return typeof val.item == 'function' || typeof val.item == 'string'; + } else if (goog.isFunction(val)) { + // On Safari, a NodeList is a function with an item property that is also + // a function. + return typeof val.item == 'function'; + } + } + + // Not a NodeList. + return false; +}; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that has the passed + * tag name and/or class name. If the passed element matches the specified + * criteria, the element itself is returned. + * @param {Node} element The DOM node to start with. + * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or + * null/undefined to match only based on class name). + * @param {?string=} opt_class The class name to match (or null/undefined to + * match only based on tag name). + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Element} The first ancestor that matches the passed criteria, or + * null if no match is found. + */ +goog.dom.getAncestorByTagNameAndClass = function(element, opt_tag, opt_class, + opt_maxSearchSteps) { + if (!opt_tag && !opt_class) { + return null; + } + var tagName = opt_tag ? opt_tag.toUpperCase() : null; + return /** @type {Element} */ (goog.dom.getAncestor(element, + function(node) { + return (!tagName || node.nodeName == tagName) && + (!opt_class || goog.isString(node.className) && + goog.array.contains(node.className.split(/\s+/), opt_class)); + }, true, opt_maxSearchSteps)); +}; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that has the passed + * class name. If the passed element matches the specified criteria, the + * element itself is returned. + * @param {Node} element The DOM node to start with. + * @param {string} className The class name to match. + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Element} The first ancestor that matches the passed criteria, or + * null if none match. + */ +goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) { + return goog.dom.getAncestorByTagNameAndClass(element, null, className, + opt_maxSearchSteps); +}; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that passes the + * matcher function. + * @param {Node} element The DOM node to start with. + * @param {function(Node) : boolean} matcher A function that returns true if the + * passed node matches the desired criteria. + * @param {boolean=} opt_includeNode If true, the node itself is included in + * the search (the first call to the matcher will pass startElement as + * the node to test). + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Node} DOM node that matched the matcher, or null if there was + * no match. + */ +goog.dom.getAncestor = function( + element, matcher, opt_includeNode, opt_maxSearchSteps) { + if (!opt_includeNode) { + element = element.parentNode; + } + var ignoreSearchSteps = opt_maxSearchSteps == null; + var steps = 0; + while (element && (ignoreSearchSteps || steps <= opt_maxSearchSteps)) { + if (matcher(element)) { + return element; + } + element = element.parentNode; + steps++; + } + // Reached the root of the DOM without a match + return null; +}; + + +/** + * Determines the active element in the given document. + * @param {Document} doc The document to look in. + * @return {Element} The active element. + */ +goog.dom.getActiveElement = function(doc) { + try { + return doc && doc.activeElement; + } catch (e) { + // NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE + // throws an exception. I'm not 100% sure why, but I suspect it chokes + // on document.activeElement if the activeElement has been recently + // removed from the DOM by a JS operation. + // + // We assume that an exception here simply means + // "there is no active element." + } + + return null; +}; + + +/** + * Gives the current devicePixelRatio. + * + * By default, this is the value of window.devicePixelRatio (which should be + * preferred if present). + * + * If window.devicePixelRatio is not present, the ratio is calculated with + * window.matchMedia, if present. Otherwise, gives 1.0. + * + * Some browsers (including Chrome) consider the browser zoom level in the pixel + * ratio, so the value may change across multiple calls. + * + * @return {number} The number of actual pixels per virtual pixel. + */ +goog.dom.getPixelRatio = function() { + var win = goog.dom.getWindow(); + + // devicePixelRatio does not work on Mobile firefox. + // TODO(user): Enable this check on a known working mobile Gecko version. + // Filed a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=896804 + var isFirefoxMobile = goog.userAgent.GECKO && goog.userAgent.MOBILE; + + if (goog.isDef(win.devicePixelRatio) && !isFirefoxMobile) { + return win.devicePixelRatio; + } else if (win.matchMedia) { + return goog.dom.matchesPixelRatio_(.75) || + goog.dom.matchesPixelRatio_(1.5) || + goog.dom.matchesPixelRatio_(2) || + goog.dom.matchesPixelRatio_(3) || 1; + } + return 1; +}; + + +/** + * Calculates a mediaQuery to check if the current device supports the + * given actual to virtual pixel ratio. + * @param {number} pixelRatio The ratio of actual pixels to virtual pixels. + * @return {number} pixelRatio if applicable, otherwise 0. + * @private + */ +goog.dom.matchesPixelRatio_ = function(pixelRatio) { + var win = goog.dom.getWindow(); + var query = ('(-webkit-min-device-pixel-ratio: ' + pixelRatio + '),' + + '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' + + '(min-resolution: ' + pixelRatio + 'dppx)'); + return win.matchMedia(query).matches ? pixelRatio : 0; +}; + + + +/** + * Create an instance of a DOM helper with a new document object. + * @param {Document=} opt_document Document object to associate with this + * DOM helper. + * @constructor + */ +goog.dom.DomHelper = function(opt_document) { + /** + * Reference to the document object to use + * @type {!Document} + * @private + */ + this.document_ = opt_document || goog.global.document || document; +}; + + +/** + * Gets the dom helper object for the document where the element resides. + * @param {Node=} opt_node If present, gets the DomHelper for this node. + * @return {!goog.dom.DomHelper} The DomHelper. + */ +goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper; + + +/** + * Sets the document object. + * @param {!Document} document Document object. + */ +goog.dom.DomHelper.prototype.setDocument = function(document) { + this.document_ = document; +}; + + +/** + * Gets the document object being used by the dom library. + * @return {!Document} Document object. + */ +goog.dom.DomHelper.prototype.getDocument = function() { + return this.document_; +}; + + +/** + * Alias for {@code getElementById}. If a DOM node is passed in then we just + * return that. + * @param {string|Element} element Element ID or a DOM node. + * @return {Element} The element with the given ID, or the node passed in. + */ +goog.dom.DomHelper.prototype.getElement = function(element) { + return goog.dom.getElementHelper_(this.document_, element); +}; + + +/** + * Gets an element by id, asserting that the element is found. + * + * This is used when an element is expected to exist, and should fail with + * an assertion error if it does not (if assertions are enabled). + * + * @param {string} id Element ID. + * @return {!Element} The element with the given ID, if it exists. + */ +goog.dom.DomHelper.prototype.getRequiredElement = function(id) { + return goog.dom.getRequiredElementHelper_(this.document_, id); +}; + + +/** + * Alias for {@code getElement}. + * @param {string|Element} element Element ID or a DOM node. + * @return {Element} The element with the given ID, or the node passed in. + * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead. + */ +goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement; + + +/** + * Looks up elements by both tag and class name, using browser native functions + * ({@code querySelectorAll}, {@code getElementsByTagName} or + * {@code getElementsByClassName}) where possible. The returned array is a live + * NodeList or a static list depending on the code path taken. + * + * @see goog.dom.query + * + * @param {?string=} opt_tag Element tag name or * for all tags. + * @param {?string=} opt_class Optional class name. + * @param {(Document|Element)=} opt_el Optional element to look in. + * @return { {length: number} } Array-like list of elements (only a length + * property and numerical indices are guaranteed to exist). + */ +goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(opt_tag, + opt_class, + opt_el) { + return goog.dom.getElementsByTagNameAndClass_(this.document_, opt_tag, + opt_class, opt_el); +}; + + +/** + * Returns an array of all the elements with the provided className. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {Element|Document=} opt_el Optional element to look in. + * @return { {length: number} } The items found with the class name provided. + */ +goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) { + var doc = opt_el || this.document_; + return goog.dom.getElementsByClass(className, doc); +}; + + +/** + * Returns the first element we find matching the provided class name. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {(Element|Document)=} opt_el Optional element to look in. + * @return {Element} The first item found with the class name provided. + */ +goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) { + var doc = opt_el || this.document_; + return goog.dom.getElementByClass(className, doc); +}; + + +/** + * Ensures an element with the given className exists, and then returns the + * first element with the provided className. + * @see {goog.dom.query} + * @param {string} className the name of the class to look for. + * @param {(!Element|!Document)=} opt_root Optional element or document to look + * in. + * @return {!Element} The first item found with the class name provided. + * @throws {goog.asserts.AssertionError} Thrown if no element is found. + */ +goog.dom.DomHelper.prototype.getRequiredElementByClass = function(className, + opt_root) { + var root = opt_root || this.document_; + return goog.dom.getRequiredElementByClass(className, root); +}; + + +/** + * Alias for {@code getElementsByTagNameAndClass}. + * @deprecated Use DomHelper getElementsByTagNameAndClass. + * @see goog.dom.query + * + * @param {?string=} opt_tag Element tag name. + * @param {?string=} opt_class Optional class name. + * @param {Element=} opt_el Optional element to look in. + * @return { {length: number} } Array-like list of elements (only a length + * property and numerical indices are guaranteed to exist). + */ +goog.dom.DomHelper.prototype.$$ = + goog.dom.DomHelper.prototype.getElementsByTagNameAndClass; + + +/** + * Sets a number of properties on a node. + * @param {Element} element DOM node to set properties on. + * @param {Object} properties Hash of property:value pairs. + */ +goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties; + + +/** + * Gets the dimensions of the viewport. + * @param {Window=} opt_window Optional window element to test. Defaults to + * the window of the Dom Helper. + * @return {!goog.math.Size} Object with values 'width' and 'height'. + */ +goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) { + // TODO(arv): This should not take an argument. That breaks the rule of a + // a DomHelper representing a single frame/window/document. + return goog.dom.getViewportSize(opt_window || this.getWindow()); +}; + + +/** + * Calculates the height of the document. + * + * @return {number} The height of the document. + */ +goog.dom.DomHelper.prototype.getDocumentHeight = function() { + return goog.dom.getDocumentHeight_(this.getWindow()); +}; + + +/** + * Typedef for use with goog.dom.createDom and goog.dom.append. + * @typedef {Object|string|Array|NodeList} + */ +goog.dom.Appendable; + + +/** + * Returns a dom node with a set of attributes. This function accepts varargs + * for subsequent nodes to be added. Subsequent nodes will be added to the + * first node as childNodes. + * + * So: + * createDom('div', null, createDom('p'), createDom('p')); + * would return a div with two child paragraphs + * + * An easy way to move all child nodes of an existing element to a new parent + * element is: + * createDom('div', null, oldElement.childNodes); + * which will remove all child nodes from the old element and add them as + * child nodes of the new DIV. + * + * @param {string} tagName Tag to create. + * @param {Object|string=} opt_attributes If object, then a map of name-value + * pairs for attributes. If a string, then this is the className of the new + * element. + * @param {...goog.dom.Appendable} var_args Further DOM nodes or + * strings for text nodes. If one of the var_args is an array or + * NodeList, its elements will be added as childNodes instead. + * @return {!Element} Reference to a DOM node. + */ +goog.dom.DomHelper.prototype.createDom = function(tagName, + opt_attributes, + var_args) { + return goog.dom.createDom_(this.document_, arguments); +}; + + +/** + * Alias for {@code createDom}. + * @param {string} tagName Tag to create. + * @param {(Object|string)=} opt_attributes If object, then a map of name-value + * pairs for attributes. If a string, then this is the className of the new + * element. + * @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for + * text nodes. If one of the var_args is an array, its children will be + * added as childNodes instead. + * @return {!Element} Reference to a DOM node. + * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead. + */ +goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom; + + +/** + * Creates a new element. + * @param {string} name Tag name. + * @return {!Element} The new element. + */ +goog.dom.DomHelper.prototype.createElement = function(name) { + return this.document_.createElement(name); +}; + + +/** + * Creates a new text node. + * @param {number|string} content Content. + * @return {!Text} The new text node. + */ +goog.dom.DomHelper.prototype.createTextNode = function(content) { + return this.document_.createTextNode(String(content)); +}; + + +/** + * Create a table. + * @param {number} rows The number of rows in the table. Must be >= 1. + * @param {number} columns The number of columns in the table. Must be >= 1. + * @param {boolean=} opt_fillWithNbsp If true, fills table entries with + * {@code goog.string.Unicode.NBSP} characters. + * @return {!HTMLElement} The created table. + */ +goog.dom.DomHelper.prototype.createTable = function(rows, columns, + opt_fillWithNbsp) { + return goog.dom.createTable_(this.document_, rows, columns, + !!opt_fillWithNbsp); +}; + + +/** + * Converts an HTML into a node or a document fragment. A single Node is used if + * {@code html} only generates a single node. If {@code html} generates multiple + * nodes then these are put inside a {@code DocumentFragment}. + * @param {!goog.html.SafeHtml} html The HTML markup to convert. + * @return {!Node} The resulting node. + */ +goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) { + return goog.dom.safeHtmlToNode_(this.document_, html); +}; + + +/** + * Converts an HTML string into a node or a document fragment. A single Node + * is used if the {@code htmlString} only generates a single node. If the + * {@code htmlString} generates multiple nodes then these are put inside a + * {@code DocumentFragment}. + * + * @param {string} htmlString The HTML string to convert. + * @return {!Node} The resulting node. + */ +goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) { + return goog.dom.htmlToDocumentFragment_(this.document_, htmlString); +}; + + +/** + * Returns true if the browser is in "CSS1-compatible" (standards-compliant) + * mode, false otherwise. + * @return {boolean} True if in CSS1-compatible mode. + */ +goog.dom.DomHelper.prototype.isCss1CompatMode = function() { + return goog.dom.isCss1CompatMode_(this.document_); +}; + + +/** + * Gets the window object associated with the document. + * @return {!Window} The window associated with the given document. + */ +goog.dom.DomHelper.prototype.getWindow = function() { + return goog.dom.getWindow_(this.document_); +}; + + +/** + * Gets the document scroll element. + * @return {!Element} Scrolling element. + */ +goog.dom.DomHelper.prototype.getDocumentScrollElement = function() { + return goog.dom.getDocumentScrollElement_(this.document_); +}; + + +/** + * Gets the document scroll distance as a coordinate object. + * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'. + */ +goog.dom.DomHelper.prototype.getDocumentScroll = function() { + return goog.dom.getDocumentScroll_(this.document_); +}; + + +/** + * Determines the active element in the given document. + * @param {Document=} opt_doc The document to look in. + * @return {Element} The active element. + */ +goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) { + return goog.dom.getActiveElement(opt_doc || this.document_); +}; + + +/** + * Appends a child to a node. + * @param {Node} parent Parent. + * @param {Node} child Child. + */ +goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild; + + +/** + * Appends a node with text or other nodes. + * @param {!Node} parent The node to append nodes to. + * @param {...goog.dom.Appendable} var_args The things to append to the node. + * If this is a Node it is appended as is. + * If this is a string then a text node is appended. + * If this is an array like object then fields 0 to length - 1 are appended. + */ +goog.dom.DomHelper.prototype.append = goog.dom.append; + + +/** + * Determines if the given node can contain children, intended to be used for + * HTML generation. + * + * @param {Node} node The node to check. + * @return {boolean} Whether the node can contain children. + */ +goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren; + + +/** + * Removes all the child nodes on a DOM node. + * @param {Node} node Node to remove children from. + */ +goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren; + + +/** + * Inserts a new node before an existing reference node (i.e., as the previous + * sibling). If the reference node has no parent, then does nothing. + * @param {Node} newNode Node to insert. + * @param {Node} refNode Reference node to insert before. + */ +goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore; + + +/** + * Inserts a new node after an existing reference node (i.e., as the next + * sibling). If the reference node has no parent, then does nothing. + * @param {Node} newNode Node to insert. + * @param {Node} refNode Reference node to insert after. + */ +goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter; + + +/** + * Insert a child at a given index. If index is larger than the number of child + * nodes that the parent currently has, the node is inserted as the last child + * node. + * @param {Element} parent The element into which to insert the child. + * @param {Node} child The element to insert. + * @param {number} index The index at which to insert the new child node. Must + * not be negative. + */ +goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt; + + +/** + * Removes a node from its parent. + * @param {Node} node The node to remove. + * @return {Node} The node removed if removed; else, null. + */ +goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode; + + +/** + * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no + * parent. + * @param {Node} newNode Node to insert. + * @param {Node} oldNode Node to replace. + */ +goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode; + + +/** + * Flattens an element. That is, removes it and replace it with its children. + * @param {Element} element The element to flatten. + * @return {Element|undefined} The original element, detached from the document + * tree, sans children, or undefined if the element was already not in the + * document. + */ +goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement; + + +/** + * Returns an array containing just the element children of the given element. + * @param {Element} element The element whose element children we want. + * @return {!(Array|NodeList)} An array or array-like list of just the element + * children of the given element. + */ +goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren; + + +/** + * Returns the first child node that is an element. + * @param {Node} node The node to get the first child element of. + * @return {Element} The first child node of {@code node} that is an element. + */ +goog.dom.DomHelper.prototype.getFirstElementChild = + goog.dom.getFirstElementChild; + + +/** + * Returns the last child node that is an element. + * @param {Node} node The node to get the last child element of. + * @return {Element} The last child node of {@code node} that is an element. + */ +goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild; + + +/** + * Returns the first next sibling that is an element. + * @param {Node} node The node to get the next sibling element of. + * @return {Element} The next sibling of {@code node} that is an element. + */ +goog.dom.DomHelper.prototype.getNextElementSibling = + goog.dom.getNextElementSibling; + + +/** + * Returns the first previous sibling that is an element. + * @param {Node} node The node to get the previous sibling element of. + * @return {Element} The first previous sibling of {@code node} that is + * an element. + */ +goog.dom.DomHelper.prototype.getPreviousElementSibling = + goog.dom.getPreviousElementSibling; + + +/** + * Returns the next node in source order from the given node. + * @param {Node} node The node. + * @return {Node} The next node in the DOM tree, or null if this was the last + * node. + */ +goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode; + + +/** + * Returns the previous node in source order from the given node. + * @param {Node} node The node. + * @return {Node} The previous node in the DOM tree, or null if this was the + * first node. + */ +goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode; + + +/** + * Whether the object looks like a DOM node. + * @param {?} obj The object being tested for node likeness. + * @return {boolean} Whether the object looks like a DOM node. + */ +goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike; + + +/** + * Whether the object looks like an Element. + * @param {?} obj The object being tested for Element likeness. + * @return {boolean} Whether the object looks like an Element. + */ +goog.dom.DomHelper.prototype.isElement = goog.dom.isElement; + + +/** + * Returns true if the specified value is a Window object. This includes the + * global window for HTML pages, and iframe windows. + * @param {?} obj Variable to test. + * @return {boolean} Whether the variable is a window. + */ +goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow; + + +/** + * Returns an element's parent, if it's an Element. + * @param {Element} element The DOM element. + * @return {Element} The parent, or null if not an Element. + */ +goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement; + + +/** + * Whether a node contains another node. + * @param {Node} parent The node that should contain the other node. + * @param {Node} descendant The node to test presence of. + * @return {boolean} Whether the parent node contains the descendent node. + */ +goog.dom.DomHelper.prototype.contains = goog.dom.contains; + + +/** + * Compares the document order of two nodes, returning 0 if they are the same + * node, a negative number if node1 is before node2, and a positive number if + * node2 is before node1. Note that we compare the order the tags appear in the + * document so in the tree text the B node is considered to be + * before the I node. + * + * @param {Node} node1 The first node to compare. + * @param {Node} node2 The second node to compare. + * @return {number} 0 if the nodes are the same node, a negative number if node1 + * is before node2, and a positive number if node2 is before node1. + */ +goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder; + + +/** + * Find the deepest common ancestor of the given nodes. + * @param {...Node} var_args The nodes to find a common ancestor of. + * @return {Node} The common ancestor of the nodes, or null if there is none. + * null will only be returned if two or more of the nodes are from different + * documents. + */ +goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor; + + +/** + * Returns the owner document for a node. + * @param {Node} node The node to get the document for. + * @return {!Document} The document owning the node. + */ +goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument; + + +/** + * Cross browser function for getting the document element of an iframe. + * @param {Element} iframe Iframe element. + * @return {!Document} The frame content document. + */ +goog.dom.DomHelper.prototype.getFrameContentDocument = + goog.dom.getFrameContentDocument; + + +/** + * Cross browser function for getting the window of a frame or iframe. + * @param {Element} frame Frame element. + * @return {Window} The window associated with the given frame. + */ +goog.dom.DomHelper.prototype.getFrameContentWindow = + goog.dom.getFrameContentWindow; + + +/** + * Sets the text content of a node, with cross-browser support. + * @param {Node} node The node to change the text content of. + * @param {string|number} text The value that should replace the node's content. + */ +goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent; + + +/** + * Gets the outerHTML of a node, which islike innerHTML, except that it + * actually contains the HTML of the node itself. + * @param {Element} element The element to get the HTML of. + * @return {string} The outerHTML of the given element. + */ +goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml; + + +/** + * Finds the first descendant node that matches the filter function. This does + * a depth first search. + * @param {Node} root The root of the tree to search. + * @param {function(Node) : boolean} p The filter function. + * @return {Node|undefined} The found node or undefined if none is found. + */ +goog.dom.DomHelper.prototype.findNode = goog.dom.findNode; + + +/** + * Finds all the descendant nodes that matches the filter function. This does a + * depth first search. + * @param {Node} root The root of the tree to search. + * @param {function(Node) : boolean} p The filter function. + * @return {Array} The found nodes or an empty array if none are found. + */ +goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes; + + +/** + * Returns true if the element has a tab index that allows it to receive + * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements + * natively support keyboard focus, even if they have no tab index. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element has a tab index that allows keyboard + * focus. + */ +goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex; + + +/** + * Enables or disables keyboard focus support on the element via its tab index. + * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true + * (or elements that natively support keyboard focus, like form elements) can + * receive keyboard focus. See http://go/tabindex for more info. + * @param {Element} element Element whose tab index is to be changed. + * @param {boolean} enable Whether to set or remove a tab index on the element + * that supports keyboard focus. + */ +goog.dom.DomHelper.prototype.setFocusableTabIndex = + goog.dom.setFocusableTabIndex; + + +/** + * Returns true if the element can be focused, i.e. it has a tab index that + * allows it to receive keyboard focus (tabIndex >= 0), or it is an element + * that natively supports keyboard focus. + * @param {!Element} element Element to check. + * @return {boolean} Whether the element allows keyboard focus. + */ +goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable; + + +/** + * Returns the text contents of the current node, without markup. New lines are + * stripped and whitespace is collapsed, such that each character would be + * visible. + * + * In browsers that support it, innerText is used. Other browsers attempt to + * simulate it via node traversal. Line breaks are canonicalized in IE. + * + * @param {Node} node The node from which we are getting content. + * @return {string} The text content. + */ +goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent; + + +/** + * Returns the text length of the text contained in a node, without markup. This + * is equivalent to the selection length if the node was selected, or the number + * of cursor movements to traverse the node. Images & BRs take one space. New + * lines are ignored. + * + * @param {Node} node The node whose text content length is being calculated. + * @return {number} The length of {@code node}'s text content. + */ +goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength; + + +/** + * Returns the text offset of a node relative to one of its ancestors. The text + * length is the same as the length calculated by + * {@code goog.dom.getNodeTextLength}. + * + * @param {Node} node The node whose offset is being calculated. + * @param {Node=} opt_offsetParent Defaults to the node's owner document's body. + * @return {number} The text offset. + */ +goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset; + + +/** + * Returns the node at a given offset in a parent node. If an object is + * provided for the optional third parameter, the node and the remainder of the + * offset will stored as properties of this object. + * @param {Node} parent The parent node. + * @param {number} offset The offset into the parent node. + * @param {Object=} opt_result Object to be used to store the return value. The + * return value will be stored in the form {node: Node, remainder: number} + * if this object is provided. + * @return {Node} The node at the given offset. + */ +goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset; + + +/** + * Returns true if the object is a {@code NodeList}. To qualify as a NodeList, + * the object must have a numeric length property and an item function (which + * has type 'string' on IE for some reason). + * @param {Object} val Object to test. + * @return {boolean} Whether the object is a NodeList. + */ +goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that has the passed + * tag name and/or class name. If the passed element matches the specified + * criteria, the element itself is returned. + * @param {Node} element The DOM node to start with. + * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or + * null/undefined to match only based on class name). + * @param {?string=} opt_class The class name to match (or null/undefined to + * match only based on tag name). + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Element} The first ancestor that matches the passed criteria, or + * null if no match is found. + */ +goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass = + goog.dom.getAncestorByTagNameAndClass; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that has the passed + * class name. If the passed element matches the specified criteria, the + * element itself is returned. + * @param {Node} element The DOM node to start with. + * @param {string} class The class name to match. + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Element} The first ancestor that matches the passed criteria, or + * null if none match. + */ +goog.dom.DomHelper.prototype.getAncestorByClass = + goog.dom.getAncestorByClass; + + +/** + * Walks up the DOM hierarchy returning the first ancestor that passes the + * matcher function. + * @param {Node} element The DOM node to start with. + * @param {function(Node) : boolean} matcher A function that returns true if the + * passed node matches the desired criteria. + * @param {boolean=} opt_includeNode If true, the node itself is included in + * the search (the first call to the matcher will pass startElement as + * the node to test). + * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the + * dom. + * @return {Node} DOM node that matched the matcher, or null if there was + * no match. + */ +goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor; diff --git a/mapbox-gl/closure-library/closure/goog/dom/dom_quirks_test.html b/mapbox-gl/closure-library/closure/goog/dom/dom_quirks_test.html new file mode 100644 index 0000000000..746d429b8b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dom_quirks_test.html @@ -0,0 +1,128 @@ + + + + +Closure Unit Tests - goog.dom + + + + + +
    + abc def g h ij kl mn opq +
    + + +
    + Test Element +
    + +
    + + +
    + + + + + +
    + +
    + +

    + +
    +
    +
    + + +

    + + a + c + d + + e + f + g + +

    + + + + +
      +
    + + + +

    + +
    +

    + ancestorTest +

    +
    + +
    Test
    +
    Test
    +
    Test
    +
    Test
    +
    Test
    +
    Test
    + +
    + Test + + + + + + + + + + + +
    + +
    Replace Test
    + + + +
    hello world
    +
    hello world
    + + + + Foo + + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/dom_test.html b/mapbox-gl/closure-library/closure/goog/dom/dom_test.html new file mode 100644 index 0000000000..bf05d3ce13 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dom_test.html @@ -0,0 +1,128 @@ + + + + + + +Closure Unit Tests - goog.dom + + + + + +
    + abc def g h ij kl mn opq +
    + + +
    + Test Element +
    + +
    + + +
    + + + + + +
    + +
    + +

    + +
    +
    +
    + + +

    + + a + c + d + + e + f + g + +

    + + + + +
      +
    + + + +

    + +
    +

    + ancestorTest +

    +
    + +
    Test
    +
    Test
    +
    Test
    +
    Test
    +
    Test
    +
    Test
    + +
    + Test + + + + + + + + + + + +
    + +
    Replace Test
    + + + +
    hello world
    +
    hello world
    + + + + Foo + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/dom_test.js b/mapbox-gl/closure-library/closure/goog/dom/dom_test.js new file mode 100644 index 0000000000..bfb6a904f5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/dom_test.js @@ -0,0 +1,1641 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Shared code for dom_test.html and dom_quirks_test.html. + */ + +/** @suppress {extraProvide} */ +goog.provide('goog.dom.dom_test'); + +goog.require('goog.dom'); +goog.require('goog.dom.BrowserFeature'); +goog.require('goog.dom.DomHelper'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.functions'); +goog.require('goog.html.testing'); +goog.require('goog.object'); +goog.require('goog.string.Unicode'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.asserts'); +goog.require('goog.userAgent'); +goog.require('goog.userAgent.product'); +goog.require('goog.userAgent.product.isVersion'); + +goog.setTestOnly('dom_test'); + +var $ = goog.dom.getElement; + +var divForTestingScrolling; +var myIframe; +var myIframeDoc; +var stubs; + +function setUpPage() { + + stubs = new goog.testing.PropertyReplacer(); + divForTestingScrolling = document.createElement('div'); + divForTestingScrolling.style.width = '5000px'; + divForTestingScrolling.style.height = '5000px'; + document.body.appendChild(divForTestingScrolling); + + // Setup for the iframe + myIframe = $('myIframe'); + myIframeDoc = goog.dom.getFrameContentDocument( + /** @type {HTMLIFrameElement} */ (myIframe)); + + // Set up document for iframe: total height of elements in document is 65 + // If the elements are not create like below, IE will get a wrong height for + // the document. + myIframeDoc.open(); + // Make sure we progate the compat mode + myIframeDoc.write((goog.dom.isCss1CompatMode() ? '' : '') + + '' + + '
    ' + + 'hello world
    ' + + '
    ' + + 'hello world
    '); + myIframeDoc.close(); +} + +function tearDownPage() { + document.body.removeChild(divForTestingScrolling); +} + +function tearDown() { + window.scrollTo(0, 0); + stubs.reset(); +} + +function testDom() { + assert('Dom library exists', typeof goog.dom != 'undefined'); +} + +function testGetElement() { + var el = $('testEl'); + assertEquals('Should be able to get id', el.id, 'testEl'); + + assertEquals($, goog.dom.getElement); + assertEquals(goog.dom.$, goog.dom.getElement); +} + +function testGetElementDomHelper() { + var domHelper = new goog.dom.DomHelper(); + var el = domHelper.getElement('testEl'); + assertEquals('Should be able to get id', el.id, 'testEl'); +} + +function testGetRequiredElement() { + var el = goog.dom.getRequiredElement('testEl'); + assertTrue(goog.isDefAndNotNull(el)); + assertEquals('testEl', el.id); + assertThrows(function() { + goog.dom.getRequiredElement('does_not_exist'); + }); +} + +function testGetRequiredElementDomHelper() { + var domHelper = new goog.dom.DomHelper(); + var el = domHelper.getRequiredElement('testEl'); + assertTrue(goog.isDefAndNotNull(el)); + assertEquals('testEl', el.id); + assertThrows(function() { + goog.dom.getRequiredElementByClass('does_not_exist', container); + }); +} + +function testGetRequiredElementByClassDomHelper() { + var domHelper = new goog.dom.DomHelper(); + assertNotNull(domHelper.getRequiredElementByClass('test1')); + assertNotNull(domHelper.getRequiredElementByClass('test2')); + + var container = domHelper.getElement('span-container'); + assertNotNull(domHelper.getElementByClass('test1', container)); + assertThrows(function() { + domHelper.getRequiredElementByClass('does_not_exist', container); + }); +} + +function testGetElementsByTagNameAndClass() { + assertEquals('Should get 6 spans', + goog.dom.getElementsByTagNameAndClass('span').length, 6); + assertEquals('Should get 6 spans', + goog.dom.getElementsByTagNameAndClass('SPAN').length, 6); + assertEquals('Should get 3 spans', + goog.dom.getElementsByTagNameAndClass('span', 'test1').length, 3); + assertEquals('Should get 1 span', + goog.dom.getElementsByTagNameAndClass('span', 'test2').length, 1); + assertEquals('Should get 1 span', + goog.dom.getElementsByTagNameAndClass('SPAN', 'test2').length, 1); + assertEquals('Should get lots of elements', + goog.dom.getElementsByTagNameAndClass().length, + document.getElementsByTagName('*').length); + + assertEquals('Should get 1 span', + goog.dom.getElementsByTagNameAndClass('span', null, $('testEl')).length, + 1); + + // '*' as the tag name should be equivalent to all tags + var container = goog.dom.getElement('span-container'); + assertEquals(5, + goog.dom.getElementsByTagNameAndClass('*', undefined, container).length); + assertEquals(3, + goog.dom.getElementsByTagNameAndClass('*', 'test1', container).length); + assertEquals(1, + goog.dom.getElementsByTagNameAndClass('*', 'test2', container).length); + + // Some version of WebKit have problems with mixed-case class names + assertEquals(1, + goog.dom.getElementsByTagNameAndClass( + undefined, 'mixedCaseClass').length); + + // Make sure that out of bounds indices are OK + assertUndefined( + goog.dom.getElementsByTagNameAndClass(undefined, 'noSuchClass')[0]); + + assertEquals(goog.dom.getElementsByTagNameAndClass, + goog.dom.getElementsByTagNameAndClass); +} + +function testGetElementsByClass() { + assertEquals(3, goog.dom.getElementsByClass('test1').length); + assertEquals(1, goog.dom.getElementsByClass('test2').length); + assertEquals(0, goog.dom.getElementsByClass('nonexistant').length); + + var container = goog.dom.getElement('span-container'); + assertEquals(3, goog.dom.getElementsByClass('test1', container).length); +} + +function testGetElementByClass() { + assertNotNull(goog.dom.getElementByClass('test1')); + assertNotNull(goog.dom.getElementByClass('test2')); + // assertNull(goog.dom.getElementByClass('nonexistant')); + + var container = goog.dom.getElement('span-container'); + assertNotNull(goog.dom.getElementByClass('test1', container)); +} + +function testSetProperties() { + var attrs = {'name': 'test3', 'title': 'A title', 'random': 'woop'}; + var el = $('testEl'); + + var res = goog.dom.setProperties(el, attrs); + assertEquals('Should be equal', el.name, 'test3'); + assertEquals('Should be equal', el.title, 'A title'); + assertEquals('Should be equal', el.random, 'woop'); +} + +function testSetPropertiesDirectAttributeMap() { + var attrs = {'usemap': '#myMap'}; + var el = goog.dom.createDom('img'); + + var res = goog.dom.setProperties(el, attrs); + assertEquals('Should be equal', '#myMap', el.getAttribute('usemap')); +} + +function testSetPropertiesAria() { + var attrs = { + 'aria-hidden': 'true', + 'aria-label': 'This is a label', + 'role': 'presentation' + }; + var el = goog.dom.createDom('div'); + + goog.dom.setProperties(el, attrs); + assertEquals('Should be equal', 'true', el.getAttribute('aria-hidden')); + assertEquals('Should be equal', + 'This is a label', el.getAttribute('aria-label')); + assertEquals('Should be equal', 'presentation', el.getAttribute('role')); +} + +function testSetPropertiesData() { + var attrs = { + 'data-tooltip': 'This is a tooltip', + 'data-tooltip-delay': '100' + }; + var el = goog.dom.createDom('div'); + + goog.dom.setProperties(el, attrs); + assertEquals('Should be equal', 'This is a tooltip', + el.getAttribute('data-tooltip')); + assertEquals('Should be equal', '100', + el.getAttribute('data-tooltip-delay')); +} + +function testSetTableProperties() { + var attrs = { + 'style': 'padding-left: 10px;', + 'class': 'mytestclass', + 'height': '101', + 'cellpadding': '15' + }; + var el = $('testTable1'); + + var res = goog.dom.setProperties(el, attrs); + assertEquals('Should be equal', el.style.paddingLeft, '10px'); + assertEquals('Should be equal', el.className, 'mytestclass'); + assertEquals('Should be equal', el.getAttribute('height'), '101'); + assertEquals('Should be equal', el.cellPadding, '15'); +} + +function testGetViewportSize() { + // TODO: This is failing in the test runner now, fix later. + //var dims = getViewportSize(); + //assertNotUndefined('Should be defined at least', dims.width); + //assertNotUndefined('Should be defined at least', dims.height); +} + +function testGetViewportSizeInIframe() { + var iframe = /** @type {HTMLIFrameElement} */ (goog.dom.getElement('iframe')); + var contentDoc = goog.dom.getFrameContentDocument(iframe); + contentDoc.write(''); + + var outerSize = goog.dom.getViewportSize(); + var innerSize = (new goog.dom.DomHelper(contentDoc)).getViewportSize(); + assert('Viewport sizes must not match', + innerSize.width != outerSize.width); +} + +function testGetDocumentHeightInIframe() { + var doc = goog.dom.getDomHelper(myIframeDoc).getDocument(); + var height = goog.dom.getDomHelper(myIframeDoc).getDocumentHeight(); + + // Broken in webkit quirks mode and in IE8+ + if ((goog.dom.isCss1CompatMode_(doc) || !goog.userAgent.WEBKIT) && + !isIE8OrHigher()) { + assertEquals('height should be 65', 42 + 23, height); + } +} + +function testCreateDom() { + var el = goog.dom.createDom('div', + { + style: 'border: 1px solid black; width: 50%; background-color: #EEE;', + onclick: "alert('woo')" + }, + goog.dom.createDom( + 'p', {style: 'font: normal 12px arial; color: red; '}, 'Para 1'), + goog.dom.createDom( + 'p', {style: 'font: bold 18px garamond; color: blue; '}, 'Para 2'), + goog.dom.createDom( + 'p', {style: 'font: normal 24px monospace; color: green'}, + 'Para 3 ', + goog.dom.createDom('a', { + name: 'link', href: 'http://bbc.co.uk' + }, + 'has a link'), + ', how cool is this?')); + + assertEquals('Tagname should be a DIV', 'DIV', el.tagName); + assertEquals('Style width should be 50%', '50%', el.style.width); + assertEquals('first child is a P tag', 'P', el.childNodes[0].tagName); + assertEquals('second child .innerHTML', 'Para 2', + el.childNodes[1].innerHTML); + + assertEquals(goog.dom.createDom, goog.dom.createDom); +} + +function testCreateDomNoChildren() { + var el; + + // Test unspecified children. + el = goog.dom.createDom('div'); + assertNull('firstChild should be null', el.firstChild); + + // Test null children. + el = goog.dom.createDom('div', null, null); + assertNull('firstChild should be null', el.firstChild); + + // Test empty array of children. + el = goog.dom.createDom('div', null, []); + assertNull('firstChild should be null', el.firstChild); +} + +function testCreateDomAcceptsArray() { + var items = [ + goog.dom.createDom('li', {}, 'Item 1'), + goog.dom.createDom('li', {}, 'Item 2') + ]; + var ul = goog.dom.createDom('ul', {}, items); + assertEquals('List should have two children', 2, ul.childNodes.length); + assertEquals('First child should be an LI tag', + 'LI', ul.firstChild.tagName); + assertEquals('Item 1', ul.childNodes[0].innerHTML); + assertEquals('Item 2', ul.childNodes[1].innerHTML); +} + +function testCreateDomStringArg() { + var el; + + // Test string arg. + el = goog.dom.createDom('div', null, 'Hello'); + assertEquals('firstChild should be a text node', goog.dom.NodeType.TEXT, + el.firstChild.nodeType); + assertEquals('firstChild should have node value "Hello"', 'Hello', + el.firstChild.nodeValue); + + // Test text node arg. + el = goog.dom.createDom('div', null, goog.dom.createTextNode('World')); + assertEquals('firstChild should be a text node', goog.dom.NodeType.TEXT, + el.firstChild.nodeType); + assertEquals('firstChild should have node value "World"', 'World', + el.firstChild.nodeValue); +} + +function testCreateDomNodeListArg() { + var el; + var emptyElem = goog.dom.createDom('div'); + var simpleElem = goog.dom.createDom('div', null, 'Hello, world!'); + var complexElem = goog.dom.createDom('div', null, 'Hello, ', + goog.dom.createDom('b', null, 'world'), goog.dom.createTextNode('!')); + + // Test empty node list. + el = goog.dom.createDom('div', null, emptyElem.childNodes); + assertNull('emptyElem.firstChild should be null', emptyElem.firstChild); + assertNull('firstChild should be null', el.firstChild); + + // Test simple node list. + el = goog.dom.createDom('div', null, simpleElem.childNodes); + assertNull('simpleElem.firstChild should be null', simpleElem.firstChild); + assertEquals('firstChild should be a text node with value "Hello, world!"', + 'Hello, world!', el.firstChild.nodeValue); + + // Test complex node list. + el = goog.dom.createDom('div', null, complexElem.childNodes); + assertNull('complexElem.firstChild should be null', complexElem.firstChild); + assertEquals('Element should have 3 child nodes', 3, el.childNodes.length); + assertEquals('childNodes[0] should be a text node with value "Hello, "', + 'Hello, ', el.childNodes[0].nodeValue); + assertEquals('childNodes[1] should be an element node with tagName "B"', + 'B', el.childNodes[1].tagName); + assertEquals('childNodes[2] should be a text node with value "!"', '!', + el.childNodes[2].nodeValue); +} + +function testCreateDomWithTypeAttribute() { + var el = goog.dom.createDom('button', {'type': 'reset', 'id': 'cool-button'}, + 'Cool button'); + assertNotNull('Button with type attribute was created successfully', el); + assertEquals('Button has correct type attribute', 'reset', el.type); + assertEquals('Button has correct id', 'cool-button', el.id); +} + +function testCreateDomWithClassList() { + var el = goog.dom.createDom('div', ['foo', 'bar']); + assertEquals('foo bar', el.className); +} + +function testContains() { + assertTrue('HTML should contain BODY', goog.dom.contains( + document.documentElement, document.body)); + assertTrue('Document should contain BODY', goog.dom.contains( + document, document.body)); + + var d = goog.dom.createDom('p', null, 'A paragraph'); + var t = d.firstChild; + assertTrue('Same element', goog.dom.contains(d, d)); + assertTrue('Same text', goog.dom.contains(t, t)); + assertTrue('Nested text', goog.dom.contains(d, t)); + assertFalse('Nested text, reversed', goog.dom.contains(t, d)); + assertFalse('Disconnected element', goog.dom.contains( + document, d)); + goog.dom.appendChild(document.body, d); + assertTrue('Connected element', goog.dom.contains( + document, d)); + goog.dom.removeNode(d); +} + +function testCreateDomWithClassName() { + var el = goog.dom.createDom('div', 'cls'); + assertNull('firstChild should be null', el.firstChild); + assertEquals('Tagname should be a DIV', 'DIV', el.tagName); + assertEquals('ClassName should be cls', 'cls', el.className); + + el = goog.dom.createDom('div', ''); + assertEquals('ClassName should be empty', '', el.className); +} + +function testCompareNodeOrder() { + var b1 = $('b1'); + var b2 = $('b2'); + var p2 = $('p2'); + + assertEquals('equal nodes should compare to 0', 0, + goog.dom.compareNodeOrder(b1, b1)); + + assertTrue('parent should come before child', + goog.dom.compareNodeOrder(p2, b1) < 0); + assertTrue('child should come after parent', + goog.dom.compareNodeOrder(b1, p2) > 0); + + assertTrue('parent should come before text child', + goog.dom.compareNodeOrder(b1, b1.firstChild) < 0); + assertTrue('text child should come after parent', goog.dom.compareNodeOrder( + b1.firstChild, b1) > 0); + + assertTrue('first sibling should come before second', + goog.dom.compareNodeOrder(b1, b2) < 0); + assertTrue('second sibling should come after first', + goog.dom.compareNodeOrder(b2, b1) > 0); + + assertTrue('text node after cousin element returns correct value', + goog.dom.compareNodeOrder(b1.nextSibling, b1) > 0); + assertTrue('text node before cousin element returns correct value', + goog.dom.compareNodeOrder(b1, b1.nextSibling) < 0); + + assertTrue('text node is before once removed cousin element', + goog.dom.compareNodeOrder(b1.firstChild, b2) < 0); + assertTrue('once removed cousin element is before text node', + goog.dom.compareNodeOrder(b2, b1.firstChild) > 0); + + assertTrue('text node is after once removed cousin text node', + goog.dom.compareNodeOrder(b1.nextSibling, b1.firstChild) > 0); + assertTrue('once removed cousin text node is before text node', + goog.dom.compareNodeOrder(b1.firstChild, b1.nextSibling) < 0); + + assertTrue('first text node is before second text node', + goog.dom.compareNodeOrder(b1.previousSibling, b1.nextSibling) < 0); + assertTrue('second text node is after first text node', + goog.dom.compareNodeOrder(b1.nextSibling, b1.previousSibling) > 0); + + assertTrue('grandchild is after grandparent', + goog.dom.compareNodeOrder(b1.firstChild, b1.parentNode) > 0); + assertTrue('grandparent is after grandchild', + goog.dom.compareNodeOrder(b1.parentNode, b1.firstChild) < 0); + + assertTrue('grandchild is after grandparent', + goog.dom.compareNodeOrder(b1.firstChild, b1.parentNode) > 0); + assertTrue('grandparent is after grandchild', + goog.dom.compareNodeOrder(b1.parentNode, b1.firstChild) < 0); + + assertTrue('second cousins compare correctly', + goog.dom.compareNodeOrder(b1.firstChild, b2.firstChild) < 0); + assertTrue('second cousins compare correctly in reverse', + goog.dom.compareNodeOrder(b2.firstChild, b1.firstChild) > 0); + + assertTrue('testEl2 is after testEl', + goog.dom.compareNodeOrder($('testEl2'), $('testEl')) > 0); + assertTrue('testEl is before testEl2', + goog.dom.compareNodeOrder($('testEl'), $('testEl2')) < 0); + + var p = $('order-test'); + var text1 = document.createTextNode('1'); + p.appendChild(text1); + var text2 = document.createTextNode('1'); + p.appendChild(text2); + + assertEquals('Equal text nodes should compare to 0', 0, + goog.dom.compareNodeOrder(text1, text1)); + assertTrue('First text node is before second', + goog.dom.compareNodeOrder(text1, text2) < 0); + assertTrue('Second text node is after first', + goog.dom.compareNodeOrder(text2, text1) > 0); + assertTrue('Late text node is after b1', + goog.dom.compareNodeOrder(text1, $('b1')) > 0); + + assertTrue('Document node is before non-document node', + goog.dom.compareNodeOrder(document, b1) < 0); + assertTrue('Non-document node is after document node', + goog.dom.compareNodeOrder(b1, document) > 0); +} + +function testFindCommonAncestor() { + var b1 = $('b1'); + var b2 = $('b2'); + var p1 = $('p1'); + var p2 = $('p2'); + var testEl2 = $('testEl2'); + + assertNull('findCommonAncestor() = null', goog.dom.findCommonAncestor()); + assertEquals('findCommonAncestor(b1) = b1', b1, + goog.dom.findCommonAncestor(b1)); + assertEquals('findCommonAncestor(b1, b1) = b1', b1, + goog.dom.findCommonAncestor(b1, b1)); + assertEquals('findCommonAncestor(b1, b2) = p2', p2, + goog.dom.findCommonAncestor(b1, b2)); + assertEquals('findCommonAncestor(p1, b2) = body', document.body, + goog.dom.findCommonAncestor(p1, b2)); + assertEquals('findCommonAncestor(testEl2, b1, b2, p1, p2) = body', + document.body, goog.dom.findCommonAncestor(testEl2, b1, b2, p1, p2)); + + var outOfDoc = document.createElement('div'); + assertNull('findCommonAncestor(outOfDoc, b1) = null', + goog.dom.findCommonAncestor(outOfDoc, b1)); +} + +function testRemoveNode() { + var b = document.createElement('b'); + var el = $('p1'); + el.appendChild(b); + goog.dom.removeNode(b); + assertTrue('b should have been removed', el.lastChild != b); +} + +function testReplaceNode() { + var n = $('toReplace'); + var previousSibling = n.previousSibling; + var goodNode = goog.dom.createDom('div', {'id': 'goodReplaceNode'}); + goog.dom.replaceNode(goodNode, n); + + assertEquals('n should have been replaced', previousSibling.nextSibling, + goodNode); + assertNull('n should no longer be in the DOM tree', $('toReplace')); + + var badNode = goog.dom.createDom('div', {'id': 'badReplaceNode'}); + goog.dom.replaceNode(badNode, n); + assertNull('badNode should not be in the DOM tree', $('badReplaceNode')); +} + +function testAppendChildAt() { + var parent = $('p2'); + var origNumChildren = parent.childNodes.length; + + var child1 = document.createElement('div'); + goog.dom.insertChildAt(parent, child1, origNumChildren); + assertEquals(origNumChildren + 1, parent.childNodes.length); + + var child2 = document.createElement('div'); + goog.dom.insertChildAt(parent, child2, origNumChildren + 42); + assertEquals(origNumChildren + 2, parent.childNodes.length); + + var child3 = document.createElement('div'); + goog.dom.insertChildAt(parent, child3, 0); + assertEquals(origNumChildren + 3, parent.childNodes.length); + + var child4 = document.createElement('div'); + goog.dom.insertChildAt(parent, child3, 2); + assertEquals(origNumChildren + 3, parent.childNodes.length); + + parent.removeChild(child1); + parent.removeChild(child2); + parent.removeChild(child3); + + var emptyParentNotInDocument = document.createElement('div'); + goog.dom.insertChildAt(emptyParentNotInDocument, child1, 0); + assertEquals(1, emptyParentNotInDocument.childNodes.length); +} + +function testFlattenElement() { + var text = document.createTextNode('Text'); + var br = document.createElement('br'); + var span = goog.dom.createDom('span', null, text, br); + assertEquals('span should have 2 children', 2, span.childNodes.length); + + var el = $('p1'); + el.appendChild(span); + + var ret = goog.dom.flattenElement(span); + + assertTrue('span should have been removed', el.lastChild != span); + assertFalse('span should have no parent', !!span.parentNode && + span.parentNode.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT); + assertEquals('span should have no children', 0, span.childNodes.length); + assertEquals('Last child of p should be br', br, el.lastChild); + assertEquals('Previous sibling of br should be text', text, + br.previousSibling); + + var outOfDoc = goog.dom.createDom('span', null, '1 child'); + // Should do nothing. + goog.dom.flattenElement(outOfDoc); + assertEquals('outOfDoc should still have 1 child', 1, + outOfDoc.childNodes.length); +} + +function testIsNodeLike() { + assertTrue('document should be node like', goog.dom.isNodeLike(document)); + assertTrue('document.body should be node like', + goog.dom.isNodeLike(document.body)); + assertTrue('a text node should be node like', goog.dom.isNodeLike( + document.createTextNode(''))); + + assertFalse('null should not be node like', goog.dom.isNodeLike(null)); + assertFalse('a string should not be node like', goog.dom.isNodeLike('abcd')); + + assertTrue('custom object should be node like', + goog.dom.isNodeLike({nodeType: 1})); +} + +function testIsElement() { + assertFalse('document is not an element', goog.dom.isElement(document)); + assertTrue('document.body is an element', + goog.dom.isElement(document.body)); + assertFalse('a text node is not an element', goog.dom.isElement( + document.createTextNode(''))); + assertTrue('an element created with createElement() is an element', + goog.dom.isElement(document.createElement('a'))); + + assertFalse('null is not an element', goog.dom.isElement(null)); + assertFalse('a string is not an element', goog.dom.isElement('abcd')); + + assertTrue('custom object is an element', + goog.dom.isElement({nodeType: 1})); + assertFalse('custom non-element object is a not an element', + goog.dom.isElement({someProperty: 'somevalue'})); +} + +function testIsWindow() { + var global = goog.global; + var frame = window.frames['frame']; + var otherWindow = window.open('', 'blank'); + var object = {window: goog.global}; + var nullVar = null; + var notDefined; + + try { + // Use try/finally to ensure that we clean up the window we open, even if an + // assertion fails or something else goes wrong. + assertTrue('global object in HTML context should be a window', + goog.dom.isWindow(goog.global)); + assertTrue('iframe window should be a window', goog.dom.isWindow(frame)); + if (otherWindow) { + assertTrue('other window should be a window', + goog.dom.isWindow(otherWindow)); + } + assertFalse('object should not be a window', goog.dom.isWindow(object)); + assertFalse('null should not be a window', goog.dom.isWindow(nullVar)); + assertFalse('undefined should not be a window', + goog.dom.isWindow(notDefined)); + } finally { + if (otherWindow) { + otherWindow.close(); + } + } +} + +function testGetOwnerDocument() { + assertEquals(goog.dom.getOwnerDocument($('p1')), document); + assertEquals(goog.dom.getOwnerDocument(document.body), document); + assertEquals(goog.dom.getOwnerDocument(document.documentElement), document); +} + +// Tests the breakages resulting in rollback cl/64715474 +function testGetOwnerDocumentNonNodeInput() { + // We should fail on null. + assertThrows(function() { + goog.dom.getOwnerDocument(null); + }); + assertEquals(document, goog.dom.getOwnerDocument(window)); +} + +function testDomHelper() { + var x = new goog.dom.DomHelper(window.frames['frame'].document); + assertTrue('Should have some HTML', + x.getDocument().body.innerHTML.length > 0); +} + +function testGetFirstElementChild() { + var p2 = $('p2'); + var b1 = goog.dom.getFirstElementChild(p2); + assertNotNull('First element child of p2 should not be null', b1); + assertEquals('First element child is b1', 'b1', b1.id); + + var c = goog.dom.getFirstElementChild(b1); + assertNull('First element child of b1 should be null', c); + + // Test with an undefined firstElementChild attribute. + var b2 = $('b2'); + var mockP2 = { + childNodes: [b1, b2], + firstChild: b1, + firstElementChild: undefined + }; + + b1 = goog.dom.getFirstElementChild(mockP2); + assertNotNull('First element child of mockP2 should not be null', b1); + assertEquals('First element child is b1', 'b1', b1.id); +} + +function testGetLastElementChild() { + var p2 = $('p2'); + var b2 = goog.dom.getLastElementChild(p2); + assertNotNull('Last element child of p2 should not be null', b2); + assertEquals('Last element child is b2', 'b2', b2.id); + + var c = goog.dom.getLastElementChild(b2); + assertNull('Last element child of b2 should be null', c); + + // Test with an undefined lastElementChild attribute. + var b1 = $('b1'); + var mockP2 = { + childNodes: [b1, b2], + lastChild: b2, + lastElementChild: undefined + }; + + b2 = goog.dom.getLastElementChild(mockP2); + assertNotNull('Last element child of mockP2 should not be null', b2); + assertEquals('Last element child is b2', 'b2', b2.id); +} + +function testGetNextElementSibling() { + var b1 = $('b1'); + var b2 = goog.dom.getNextElementSibling(b1); + assertNotNull('Next element sibling of b1 should not be null', b1); + assertEquals('Next element sibling is b2', 'b2', b2.id); + + var c = goog.dom.getNextElementSibling(b2); + assertNull('Next element sibling of b2 should be null', c); + + // Test with an undefined nextElementSibling attribute. + var mockB1 = { + nextSibling: b2, + nextElementSibling: undefined + }; + + b2 = goog.dom.getNextElementSibling(mockB1); + assertNotNull('Next element sibling of mockB1 should not be null', b1); + assertEquals('Next element sibling is b2', 'b2', b2.id); +} + +function testGetPreviousElementSibling() { + var b2 = $('b2'); + var b1 = goog.dom.getPreviousElementSibling(b2); + assertNotNull('Previous element sibling of b2 should not be null', b1); + assertEquals('Previous element sibling is b1', 'b1', b1.id); + + var c = goog.dom.getPreviousElementSibling(b1); + assertNull('Previous element sibling of b1 should be null', c); + + // Test with an undefined previousElementSibling attribute. + var mockB2 = { + previousSibling: b1, + previousElementSibling: undefined + }; + + b1 = goog.dom.getPreviousElementSibling(mockB2); + assertNotNull('Previous element sibling of mockB2 should not be null', b1); + assertEquals('Previous element sibling is b1', 'b1', b1.id); +} + +function testGetChildren() { + var p2 = $('p2'); + var children = goog.dom.getChildren(p2); + assertNotNull('Elements array should not be null', children); + assertEquals('List of element children should be length two.', 2, + children.length); + + var b1 = $('b1'); + var b2 = $('b2'); + assertObjectEquals('First element child should be b1.', b1, children[0]); + assertObjectEquals('Second element child should be b2.', b2, children[1]); + + var noChildren = goog.dom.getChildren(b1); + assertNotNull('Element children array should not be null', noChildren); + assertEquals('List of element children should be length zero.', 0, + noChildren.length); + + // Test with an undefined children attribute. + var mockP2 = { + childNodes: [b1, b2], + children: undefined + }; + + children = goog.dom.getChildren(mockP2); + assertNotNull('Elements array should not be null', children); + assertEquals('List of element children should be length two.', 2, + children.length); + + assertObjectEquals('First element child should be b1.', b1, children[0]); + assertObjectEquals('Second element child should be b2.', b2, children[1]); +} + +function testGetNextNode() { + var tree = goog.dom.htmlToDocumentFragment( + '
    ' + + '

    Some text

    ' + + '
    Some special text
    ' + + '
    Foo
    ' + + '
    '); + + assertNull(goog.dom.getNextNode(null)); + + var node = tree; + var next = function() { + return node = goog.dom.getNextNode(node); + }; + + assertEquals('P', next().tagName); + assertEquals('Some text', next().nodeValue); + assertEquals('BLOCKQUOTE', next().tagName); + assertEquals('Some ', next().nodeValue); + assertEquals('I', next().tagName); + assertEquals('special', next().nodeValue); + assertEquals(' ', next().nodeValue); + assertEquals('B', next().tagName); + assertEquals('text', next().nodeValue); + assertEquals('ADDRESS', next().tagName); + assertEquals(goog.dom.NodeType.COMMENT, next().nodeType); + assertEquals('Foo', next().nodeValue); + + assertNull(next()); +} + +function testGetPreviousNode() { + var tree = goog.dom.htmlToDocumentFragment( + '
    ' + + '

    Some text

    ' + + '
    Some special text
    ' + + '
    Foo
    ' + + '
    '); + + assertNull(goog.dom.getPreviousNode(null)); + + var node = tree.lastChild.lastChild; + var previous = function() { + return node = goog.dom.getPreviousNode(node); + }; + + assertEquals(goog.dom.NodeType.COMMENT, previous().nodeType); + assertEquals('ADDRESS', previous().tagName); + assertEquals('text', previous().nodeValue); + assertEquals('B', previous().tagName); + assertEquals(' ', previous().nodeValue); + assertEquals('special', previous().nodeValue); + assertEquals('I', previous().tagName); + assertEquals('Some ', previous().nodeValue); + assertEquals('BLOCKQUOTE', previous().tagName); + assertEquals('Some text', previous().nodeValue); + assertEquals('P', previous().tagName); + assertEquals('DIV', previous().tagName); + + if (!goog.userAgent.IE) { + // Internet Explorer maintains a parentNode for Elements after they are + // removed from the hierarchy. Everyone else agrees on a null parentNode. + assertNull(previous()); + } +} + +function testSetTextContent() { + var p1 = $('p1'); + var s = 'hello world'; + goog.dom.setTextContent(p1, s); + assertEquals('We should have one childNode after setTextContent', 1, + p1.childNodes.length); + assertEquals(s, p1.firstChild.data); + assertEquals(s, p1.innerHTML); + + s = 'four elefants < five ants'; + var sHtml = 'four elefants < five ants'; + goog.dom.setTextContent(p1, s); + assertEquals('We should have one childNode after setTextContent', 1, + p1.childNodes.length); + assertEquals(s, p1.firstChild.data); + assertEquals(sHtml, p1.innerHTML); + + // ensure that we remove existing children + p1.innerHTML = 'abc'; + s = 'hello world'; + goog.dom.setTextContent(p1, s); + assertEquals('We should have one childNode after setTextContent', 1, + p1.childNodes.length); + assertEquals(s, p1.firstChild.data); + + // same but start with an element + p1.innerHTML = 'abc'; + s = 'hello world'; + goog.dom.setTextContent(p1, s); + assertEquals('We should have one childNode after setTextContent', 1, + p1.childNodes.length); + assertEquals(s, p1.firstChild.data); + + // Text/CharacterData + p1.innerHTML = 'before'; + s = 'after'; + goog.dom.setTextContent(p1.firstChild, s); + assertEquals('We should have one childNode after setTextContent', 1, + p1.childNodes.length); + assertEquals(s, p1.firstChild.data); + + // DocumentFragment + var df = document.createDocumentFragment(); + s = 'hello world'; + goog.dom.setTextContent(df, s); + assertEquals('We should have one childNode after setTextContent', 1, + df.childNodes.length); + assertEquals(s, df.firstChild.data); + + // clean up + p1.innerHTML = ''; +} + +function testFindNode() { + var expected = document.body; + var result = goog.dom.findNode(document, function(n) { + return n.nodeType == goog.dom.NodeType.ELEMENT && n.tagName == 'BODY'; + }); + assertEquals(expected, result); + + expected = document.getElementsByTagName('P')[0]; + result = goog.dom.findNode(document, function(n) { + return n.nodeType == goog.dom.NodeType.ELEMENT && n.tagName == 'P'; + }); + assertEquals(expected, result); + + result = goog.dom.findNode(document, function(n) { + return false; + }); + assertUndefined(result); +} + +function testFindNodes() { + var expected = document.getElementsByTagName('P'); + var result = goog.dom.findNodes(document, function(n) { + return n.nodeType == goog.dom.NodeType.ELEMENT && n.tagName == 'P'; + }); + assertEquals(expected.length, result.length); + assertEquals(expected[0], result[0]); + assertEquals(expected[1], result[1]); + + result = goog.dom.findNodes(document, function(n) { + return false; + }).length; + assertEquals(0, result); +} + +function createTestDom(txt) { + var dom = goog.dom.createDom('div'); + dom.innerHTML = txt; + return dom; +} + +function testIsFocusableTabIndex() { + assertFalse('isFocusableTabIndex() must be false for no tab index', + goog.dom.isFocusableTabIndex(goog.dom.getElement('noTabIndex'))); + assertFalse('isFocusableTabIndex() must be false for tab index -2', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndexNegative2'))); + assertFalse('isFocusableTabIndex() must be false for tab index -1', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndexNegative1'))); + + // WebKit on Mac doesn't support focusable DIVs until version 526 and later. + if (!goog.userAgent.WEBKIT || !goog.userAgent.MAC || + goog.userAgent.isVersionOrHigher('526')) { + assertTrue('isFocusableTabIndex() must be true for tab index 0', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndex0'))); + assertTrue('isFocusableTabIndex() must be true for tab index 1', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndex1'))); + assertTrue('isFocusableTabIndex() must be true for tab index 2', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndex2'))); + } +} + +function testSetFocusableTabIndex() { + // WebKit on Mac doesn't support focusable DIVs until version 526 and later. + if (!goog.userAgent.WEBKIT || !goog.userAgent.MAC || + goog.userAgent.isVersionOrHigher('526')) { + // Test enabling focusable tab index. + goog.dom.setFocusableTabIndex(goog.dom.getElement('noTabIndex'), true); + assertTrue('isFocusableTabIndex() must be true after enabling tab index', + goog.dom.isFocusableTabIndex(goog.dom.getElement('noTabIndex'))); + + // Test disabling focusable tab index that was added programmatically. + goog.dom.setFocusableTabIndex(goog.dom.getElement('noTabIndex'), false); + assertFalse('isFocusableTabIndex() must be false after disabling tab ' + + 'index that was programmatically added', + goog.dom.isFocusableTabIndex(goog.dom.getElement('noTabIndex'))); + + // Test disabling focusable tab index that was specified in markup. + goog.dom.setFocusableTabIndex(goog.dom.getElement('tabIndex0'), false); + assertFalse('isFocusableTabIndex() must be false after disabling tab ' + + 'index that was specified in markup', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndex0'))); + + // Test re-enabling focusable tab index. + goog.dom.setFocusableTabIndex(goog.dom.getElement('tabIndex0'), true); + assertTrue('isFocusableTabIndex() must be true after reenabling tabindex', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndex0'))); + } +} + +function testIsFocusable() { + // Test all types of form elements with no tab index specified are focusable. + assertTrue('isFocusable() must be true for anchor elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('noTabIndexAnchor'))); + assertTrue('isFocusable() must be true for input elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('noTabIndexInput'))); + assertTrue('isFocusable() must be true for textarea elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('noTabIndexTextArea'))); + assertTrue('isFocusable() must be true for select elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('noTabIndexSelect'))); + assertTrue('isFocusable() must be true for button elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('noTabIndexButton'))); + + // Test form element with negative tab index is not focusable. + assertFalse('isFocusable() must be false for form elements with ' + + 'negative tab index', + goog.dom.isFocusable(goog.dom.getElement('negTabIndexButton'))); + + // Test form element with zero tab index is focusable. + assertTrue('isFocusable() must be true for form elements with ' + + 'zero tab index', + goog.dom.isFocusable(goog.dom.getElement('zeroTabIndexButton'))); + + // Test form element with positive tab index is focusable. + assertTrue('isFocusable() must be true for form elements with ' + + 'positive tab index', + goog.dom.isFocusable(goog.dom.getElement('posTabIndexButton'))); + + // Test disabled form element with no tab index is not focusable. + assertFalse('isFocusable() must be false for disabled form elements with ' + + 'no tab index', + goog.dom.isFocusable(goog.dom.getElement('disabledNoTabIndexButton'))); + + // Test disabled form element with negative tab index is not focusable. + assertFalse('isFocusable() must be false for disabled form elements with ' + + 'negative tab index', + goog.dom.isFocusable(goog.dom.getElement('disabledNegTabIndexButton'))); + + // Test disabled form element with zero tab index is not focusable. + assertFalse('isFocusable() must be false for disabled form elements with ' + + 'zero tab index', + goog.dom.isFocusable(goog.dom.getElement('disabledZeroTabIndexButton'))); + + // Test disabled form element with positive tab index is not focusable. + assertFalse('isFocusable() must be false for disabled form elements with ' + + 'positive tab index', + goog.dom.isFocusable(goog.dom.getElement('disabledPosTabIndexButton'))); + + // Test non-form types should return same value as isFocusableTabIndex() + assertEquals('isFocusable() and isFocusableTabIndex() must agree for ' + + ' no tab index', + goog.dom.isFocusableTabIndex(goog.dom.getElement('noTabIndex')), + goog.dom.isFocusable(goog.dom.getElement('noTabIndex'))); + assertEquals('isFocusable() and isFocusableTabIndex() must agree for ' + + ' tab index -2', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndexNegative2')), + goog.dom.isFocusable(goog.dom.getElement('tabIndexNegative2'))); + assertEquals('isFocusable() and isFocusableTabIndex() must agree for ' + + ' tab index -1', + goog.dom.isFocusableTabIndex(goog.dom.getElement('tabIndexNegative1')), + goog.dom.isFocusable(goog.dom.getElement('tabIndexNegative1'))); + +} + +function testGetTextContent() { + function t(inp, out) { + assertEquals(out.replace(/ /g, '_'), + goog.dom.getTextContent( + createTestDom(inp)).replace(/ /g, '_')); + } + + t('abcde', 'abcde'); + t('abcdefgh', 'abcdefgh'); + t('a')); + assertEquals('SCRIPT', script.tagName); + + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + // Removing an Element from a DOM tree in IE sets its parentNode to a new + // DocumentFragment. Bizarre! + assertEquals(goog.dom.NodeType.DOCUMENT_FRAGMENT, + goog.dom.removeNode(div).parentNode.nodeType); + } else { + assertNull(div.parentNode); + } +} + +function testHtmlToDocumentFragment() { + var docFragment = goog.dom.htmlToDocumentFragment('12'); + assertNull(docFragment.parentNode); + assertEquals(2, docFragment.childNodes.length); + + var div = goog.dom.htmlToDocumentFragment('
    3
    '); + assertEquals('DIV', div.tagName); + + var script = goog.dom.htmlToDocumentFragment(''); + assertEquals('SCRIPT', script.tagName); + + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + // Removing an Element from a DOM tree in IE sets its parentNode to a new + // DocumentFragment. Bizarre! + assertEquals(goog.dom.NodeType.DOCUMENT_FRAGMENT, + goog.dom.removeNode(div).parentNode.nodeType); + } else { + assertNull(div.parentNode); + } +} + +function testAppend() { + var div = document.createElement('div'); + var b = document.createElement('b'); + var c = document.createTextNode('c'); + goog.dom.append(div, 'a', b, c); + assertEqualsCaseAndLeadingWhitespaceInsensitive('ac', div.innerHTML); +} + +function testAppend2() { + var div = myIframeDoc.createElement('div'); + var b = myIframeDoc.createElement('b'); + var c = myIframeDoc.createTextNode('c'); + goog.dom.append(div, 'a', b, c); + assertEqualsCaseAndLeadingWhitespaceInsensitive('ac', div.innerHTML); +} + +function testAppend3() { + var div = document.createElement('div'); + var b = document.createElement('b'); + var c = document.createTextNode('c'); + goog.dom.append(div, ['a', b, c]); + assertEqualsCaseAndLeadingWhitespaceInsensitive('ac', div.innerHTML); +} + +function testAppend4() { + var div = document.createElement('div'); + var div2 = document.createElement('div'); + div2.innerHTML = 'ac'; + goog.dom.append(div, div2.childNodes); + assertEqualsCaseAndLeadingWhitespaceInsensitive('ac', div.innerHTML); + assertFalse(div2.hasChildNodes()); +} + +function testGetDocumentScroll() { + // setUpPage added divForTestingScrolling to the DOM. It's not init'd here so + // it can be shared amonst other tests. + window.scrollTo(100, 100); + + assertEquals(100, goog.dom.getDocumentScroll().x); + assertEquals(100, goog.dom.getDocumentScroll().y); +} + +function testGetDocumentScrollOfFixedViewport() { + // iOS and perhaps other environments don't actually support scrolling. + // Instead, you view the document's fixed layout through a screen viewport. + // We need getDocumentScroll to handle this case though. + // In case of IE10 though, we do want to use scrollLeft/scrollTop + // because the rest of the positioning is done off the scrolled away origin. + var fakeDocumentScrollElement = {scrollLeft: 0, scrollTop: 0}; + var fakeDocument = { + defaultView: {pageXOffset: 100, pageYOffset: 100}, + documentElement: fakeDocumentScrollElement, + body: fakeDocumentScrollElement + }; + var dh = goog.dom.getDomHelper(document); + dh.setDocument(fakeDocument); + if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher(10)) { + assertEquals(0, dh.getDocumentScroll().x); + assertEquals(0, dh.getDocumentScroll().y); + } else { + assertEquals(100, dh.getDocumentScroll().x); + assertEquals(100, dh.getDocumentScroll().y); + } +} + + +function testGetDocumentScrollFromDocumentWithoutABody() { + // Some documents, like SVG docs, do not have a body element. The document + // element should be used when computing the document scroll for these + // documents. + var fakeDocument = { + defaultView: {pageXOffset: 0, pageYOffset: 0}, + documentElement: {scrollLeft: 0, scrollTop: 0} + }; + + var dh = new goog.dom.DomHelper(fakeDocument); + assertEquals(fakeDocument.documentElement, dh.getDocumentScrollElement()); + assertEquals(0, dh.getDocumentScroll().x); + assertEquals(0, dh.getDocumentScroll().y); + // OK if this does not throw. +} + +function testActiveElementIE() { + if (!goog.userAgent.IE) { + return; + } + + var link = goog.dom.getElement('link'); + link.focus(); + + assertEquals(link.tagName, goog.dom.getActiveElement(document).tagName); + assertEquals(link, goog.dom.getActiveElement(document)); +} + +function testParentElement() { + var testEl = $('testEl'); + var bodyEl = goog.dom.getParentElement(testEl); + assertNotNull(bodyEl); + var htmlEl = goog.dom.getParentElement(bodyEl); + assertNotNull(htmlEl); + var documentNotAnElement = goog.dom.getParentElement(htmlEl); + assertNull(documentNotAnElement); + + var tree = goog.dom.htmlToDocumentFragment( + '
    ' + + '

    Some text

    ' + + '
    Some special text
    ' + + '
    Foo
    ' + + '
    '); + assertNull(goog.dom.getParentElement(tree)); + pEl = goog.dom.getNextNode(tree); + var fragmentRootEl = goog.dom.getParentElement(pEl); + assertEquals(tree, fragmentRootEl); + + var detachedEl = goog.dom.createDom('div'); + var detachedHasNoParent = goog.dom.getParentElement(detachedEl); + assertNull(detachedHasNoParent); + + // svg is not supported in IE8 and below or in IE9 quirks mode + var supported = !goog.userAgent.IE || + goog.userAgent.isDocumentModeOrHigher(10) || + (goog.dom.isCss1CompatMode() && goog.userAgent.isDocumentModeOrHigher(9)); + if (!supported) { + return; + } + + var svg = $('testSvg'); + assertNotNull(svg); + var rect = $('testRect'); + assertNotNull(rect); + var g = $('testG'); + assertNotNull(g); + + if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9')) { + // test to make sure IE9 is returning undefined for .parentElement + assertUndefined(g.parentElement); + assertUndefined(rect.parentElement); + assertUndefined(svg.parentElement); + } + var shouldBeG = goog.dom.getParentElement(rect); + assertEquals(g, shouldBeG); + var shouldBeSvg = goog.dom.getParentElement(g); + assertEquals(svg, shouldBeSvg); + var shouldBeBody = goog.dom.getParentElement(svg); + assertEquals(bodyEl, shouldBeBody); +} + + +/** + * @return {boolean} Returns true if the userAgent is IE8 or higher. + */ +function isIE8OrHigher() { + return goog.userAgent.IE && goog.userAgent.product.isVersion('8'); +} + + +function testDevicePixelRatio() { + stubs.set(goog.dom, 'getWindow', goog.functions.constant( + { + matchMedia: function(query) { + return { + matches: query.indexOf('1.5') >= 0 + }; + } + })); + + stubs.set(goog.functions, 'CACHE_RETURN_VALUE', false); + + assertEquals(goog.dom.getPixelRatio(), 1.5); + + stubs.set(goog.dom, 'getWindow', goog.functions.constant( + {devicePixelRatio: 2.0})); + goog.dom.devicePixelRatio_ = null; + assertEquals(goog.dom.getPixelRatio(), 2); + + stubs.set(goog.dom, 'getWindow', goog.functions.constant({})); + goog.dom.devicePixelRatio_ = null; + assertEquals(goog.dom.getPixelRatio(), 1); +} + diff --git a/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor.js b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor.js new file mode 100644 index 0000000000..31fc783196 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor.js @@ -0,0 +1,161 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A class that can be used to listen to font size changes. + * @author arv@google.com (Erik Arvidsson) + */ + +goog.provide('goog.dom.FontSizeMonitor'); +goog.provide('goog.dom.FontSizeMonitor.EventType'); + +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.userAgent'); + + +// TODO(arv): Move this to goog.events instead. + + + +/** + * This class can be used to monitor changes in font size. Instances will + * dispatch a {@code goog.dom.FontSizeMonitor.EventType.CHANGE} event. + * Example usage: + *
    + * var fms = new goog.dom.FontSizeMonitor();
    + * goog.events.listen(fms, goog.dom.FontSizeMonitor.EventType.CHANGE,
    + *     function(e) {
    + *       alert('Font size was changed');
    + *     });
    + * 
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper object that is used to + * determine where to insert the DOM nodes used to determine when the font + * size changes. + * @constructor + * @extends {goog.events.EventTarget} + * @final + */ +goog.dom.FontSizeMonitor = function(opt_domHelper) { + goog.events.EventTarget.call(this); + + var dom = opt_domHelper || goog.dom.getDomHelper(); + + /** + * Offscreen iframe which we use to detect resize events. + * @type {Element} + * @private + */ + this.sizeElement_ = dom.createDom( + // The size of the iframe is expressed in em, which are font size relative + // which will cause the iframe to be resized when the font size changes. + // The actual values are not relevant as long as we can ensure that the + // iframe has a non zero size and is completely off screen. + goog.userAgent.IE ? 'div' : 'iframe', { + 'style': 'position:absolute;width:9em;height:9em;top:-99em', + 'tabIndex': -1, + 'aria-hidden': 'true' + }); + var p = dom.getDocument().body; + p.insertBefore(this.sizeElement_, p.firstChild); + + /** + * The object that we listen to resize events on. + * @type {Element|Window} + * @private + */ + var resizeTarget = this.resizeTarget_ = + goog.userAgent.IE ? this.sizeElement_ : + goog.dom.getFrameContentWindow( + /** @type {HTMLIFrameElement} */ (this.sizeElement_)); + + // We need to open and close the document to get Firefox 2 to work. We must + // not do this for IE in case we are using HTTPS since accessing the document + // on an about:blank iframe in IE using HTTPS raises a Permission Denied + // error. + if (goog.userAgent.GECKO) { + var doc = resizeTarget.document; + doc.open(); + doc.close(); + } + + // Listen to resize event on the window inside the iframe. + goog.events.listen(resizeTarget, goog.events.EventType.RESIZE, + this.handleResize_, false, this); + + /** + * Last measured width of the iframe element. + * @type {number} + * @private + */ + this.lastWidth_ = this.sizeElement_.offsetWidth; +}; +goog.inherits(goog.dom.FontSizeMonitor, goog.events.EventTarget); + + +/** + * The event types that the FontSizeMonitor fires. + * @enum {string} + */ +goog.dom.FontSizeMonitor.EventType = { + // TODO(arv): Change value to 'change' after updating the callers. + CHANGE: 'fontsizechange' +}; + + +/** + * Constant for the change event. + * @type {string} + * @deprecated Use {@code goog.dom.FontSizeMonitor.EventType.CHANGE} instead. + */ +goog.dom.FontSizeMonitor.CHANGE_EVENT = + goog.dom.FontSizeMonitor.EventType.CHANGE; + + +/** @override */ +goog.dom.FontSizeMonitor.prototype.disposeInternal = function() { + goog.dom.FontSizeMonitor.superClass_.disposeInternal.call(this); + + goog.events.unlisten(this.resizeTarget_, goog.events.EventType.RESIZE, + this.handleResize_, false, this); + this.resizeTarget_ = null; + + // Firefox 2 crashes if the iframe is removed during the unload phase. + if (!goog.userAgent.GECKO || + goog.userAgent.isVersionOrHigher('1.9')) { + goog.dom.removeNode(this.sizeElement_); + } + delete this.sizeElement_; +}; + + +/** + * Handles the onresize event of the iframe and dispatches a change event in + * case its size really changed. + * @param {goog.events.BrowserEvent} e The event object. + * @private + */ +goog.dom.FontSizeMonitor.prototype.handleResize_ = function(e) { + // Only dispatch the event if the size really changed. Some newer browsers do + // not really change the font-size, instead they zoom the whole page. This + // does trigger window resize events on the iframe but the logical pixel size + // remains the same (the device pixel size changes but that is irrelevant). + var currentWidth = this.sizeElement_.offsetWidth; + if (this.lastWidth_ != currentWidth) { + this.lastWidth_ = currentWidth; + this.dispatchEvent(goog.dom.FontSizeMonitor.EventType.CHANGE); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.html b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.html new file mode 100644 index 0000000000..acdb0faeaf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.html @@ -0,0 +1,40 @@ + + + + + + +Closure Unit Tests - goog.dom.FontSizeMonitor + + + + + + + + + +
    + + +
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.js b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.js new file mode 100644 index 0000000000..93fb2c7b29 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/fontsizemonitor_test.js @@ -0,0 +1,257 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.FontSizeMonitorTest'); +goog.setTestOnly('goog.dom.FontSizeMonitorTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.FontSizeMonitor'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function isBuggyGecko() { + return goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'); +} + +var monitor; + +function setUp() { + monitor = new goog.dom.FontSizeMonitor(); +} + +function tearDown() { + monitor.dispose(); +} + +function getResizeTarget() { + return goog.userAgent.IE ? monitor.sizeElement_ : + goog.dom.getFrameContentWindow(monitor.sizeElement_); +} + +function testFontSizeNoChange() { + // This tests that firing the resize event without changing the font-size + // does not trigger the event. + + var fired = false; + goog.events.listen(monitor, goog.dom.FontSizeMonitor.EventType.CHANGE, + function(e) { + fired = true; + }); + + var resizeEvent = new goog.events.Event('resize', getResizeTarget()); + goog.testing.events.fireBrowserEvent(resizeEvent); + + assertFalse('The font size should not have changed', fired); +} + +function testFontSizeChanged() { + // One can trigger the iframe resize by changing the + // document.body.style.fontSize but the event is fired asynchronously in + // Firefox. Instead, we just override the lastWidth_ to simulate that the + // size changed. + + var fired = false; + goog.events.listen(monitor, goog.dom.FontSizeMonitor.EventType.CHANGE, + function(e) { + fired = true; + }); + + monitor.lastWidth_--; + + var resizeEvent = new goog.events.Event('resize', getResizeTarget()); + goog.testing.events.fireBrowserEvent(resizeEvent); + + assertTrue('The font size should have changed', fired); +} + +function testCreateAndDispose() { + var frameCount = window.frames.length; + var iframeElementCount = document.getElementsByTagName('iframe').length; + var divElementCount = document.getElementsByTagName('div').length; + + var monitor = new goog.dom.FontSizeMonitor(); + monitor.dispose(); + + var newFrameCount = window.frames.length; + var newIframeElementCount = document.getElementsByTagName('iframe').length; + var newDivElementCount = document.getElementsByTagName('div').length; + + assertEquals('There should be no trailing frames', + frameCount + isBuggyGecko(), newFrameCount); + assertEquals('There should be no trailing iframe elements', + iframeElementCount + isBuggyGecko(), + newIframeElementCount); + assertEquals('There should be no trailing div elements', + divElementCount, newDivElementCount); +} + +function testWithDomHelper() { + var frameCount = window.frames.length; + var iframeElementCount = document.getElementsByTagName('iframe').length; + var divElementCount = document.getElementsByTagName('div').length; + + var monitor = new goog.dom.FontSizeMonitor(goog.dom.getDomHelper()); + + var newFrameCount = window.frames.length; + var newIframeElementCount = document.getElementsByTagName('iframe').length; + var newDivElementCount = document.getElementsByTagName('div').length; + + if (goog.userAgent.IE) { + assertEquals('There should be one new div element', + divElementCount + 1, newDivElementCount); + } else { + assertEquals('There should be one new frame', + frameCount + 1, newFrameCount); + assertEquals('There should be one new iframe element', + iframeElementCount + 1, newIframeElementCount); + } + + // Use the first iframe in the doc. This is added in the HTML markup. + var win = window.frames[0]; + var doc = win.document; + doc.open(); + doc.write(''); + doc.close(); + var domHelper = goog.dom.getDomHelper(doc); + + var frameCount2 = win.frames.length; + var iframeElementCount2 = doc.getElementsByTagName('iframe').length; + var divElementCount2 = doc.getElementsByTagName('div').length; + + var monitor2 = new goog.dom.FontSizeMonitor(domHelper); + + var newFrameCount2 = win.frames.length; + var newIframeElementCount2 = doc.getElementsByTagName('iframe').length; + var newDivElementCount2 = doc.getElementsByTagName('div').length; + + if (goog.userAgent.IE) { + assertEquals('There should be one new div element', + divElementCount2 + 1, newDivElementCount2); + } else { + assertEquals('There should be one new frame', frameCount2 + 1, + newFrameCount2); + assertEquals('There should be one new iframe element', + iframeElementCount2 + 1, newIframeElementCount2); + } + + monitor.dispose(); + monitor2.dispose(); +} + +function testEnsureThatDocIsOpenedForGecko() { + + var pr = new goog.testing.PropertyReplacer(); + pr.set(goog.userAgent, 'GECKO', true); + pr.set(goog.userAgent, 'IE', false); + + var openCalled = false; + var closeCalled = false; + var instance = { + document: { + open: function() { + openCalled = true; + }, + close: function() { + closeCalled = true; + } + }, + attachEvent: function() {} + }; + + pr.set(goog.dom, 'getFrameContentWindow', function() { + return instance; + }); + + try { + var monitor = new goog.dom.FontSizeMonitor(); + + assertTrue('doc.open should have been called', openCalled); + assertTrue('doc.close should have been called', closeCalled); + + monitor.dispose(); + } finally { + pr.reset(); + } +} + +function testFirefox2WorkAroundFirefox3() { + var pr = new goog.testing.PropertyReplacer(); + pr.set(goog.userAgent, 'GECKO', true); + pr.set(goog.userAgent, 'IE', false); + + try { + // 1.9 should clear iframes + pr.set(goog.userAgent, 'VERSION', '1.9'); + goog.userAgent.isVersionOrHigherCache_ = {}; + + var frameCount = window.frames.length; + var iframeElementCount = document.getElementsByTagName('iframe').length; + var divElementCount = document.getElementsByTagName('div').length; + + var monitor = new goog.dom.FontSizeMonitor(); + monitor.dispose(); + + var newFrameCount = window.frames.length; + var newIframeElementCount = document.getElementsByTagName('iframe').length; + var newDivElementCount = document.getElementsByTagName('div').length; + + assertEquals('There should be no trailing frames', + frameCount, newFrameCount); + assertEquals('There should be no trailing iframe elements', + iframeElementCount, + newIframeElementCount); + assertEquals('There should be no trailing div elements', + divElementCount, newDivElementCount); + } finally { + pr.reset(); + } +} + + +function testFirefox2WorkAroundFirefox2() { + var pr = new goog.testing.PropertyReplacer(); + pr.set(goog.userAgent, 'GECKO', true); + pr.set(goog.userAgent, 'IE', false); + + try { + // 1.8 should NOT clear iframes + pr.set(goog.userAgent, 'VERSION', '1.8'); + goog.userAgent.isVersionOrHigherCache_ = {}; + + var frameCount = window.frames.length; + var iframeElementCount = document.getElementsByTagName('iframe').length; + var divElementCount = document.getElementsByTagName('div').length; + + var monitor = new goog.dom.FontSizeMonitor(); + monitor.dispose(); + + var newFrameCount = window.frames.length; + var newIframeElementCount = document.getElementsByTagName('iframe').length; + var newDivElementCount = document.getElementsByTagName('div').length; + + assertEquals('There should be no trailing frames', + frameCount + 1, newFrameCount); + assertEquals('There should be no trailing iframe elements', + iframeElementCount + 1, + newIframeElementCount); + assertEquals('There should be no trailing div elements', + divElementCount, newDivElementCount); + } finally { + pr.reset(); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/forms.js b/mapbox-gl/closure-library/closure/goog/dom/forms.js new file mode 100644 index 0000000000..53d686c6a2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/forms.js @@ -0,0 +1,413 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating a form and elements. + * + * @author arv@google.com (Erik Arvidsson) + */ + +goog.provide('goog.dom.forms'); + +goog.require('goog.structs.Map'); + + +/** + * Returns form data as a map of name to value arrays. This doesn't + * support file inputs. + * @param {HTMLFormElement} form The form. + * @return {!goog.structs.Map.>} A map of the form data + * as field name to arrays of values. + */ +goog.dom.forms.getFormDataMap = function(form) { + var map = new goog.structs.Map(); + goog.dom.forms.getFormDataHelper_(form, map, + goog.dom.forms.addFormDataToMap_); + return map; +}; + + +/** + * Returns the form data as an application/x-www-url-encoded string. This + * doesn't support file inputs. + * @param {HTMLFormElement} form The form. + * @return {string} An application/x-www-url-encoded string. + */ +goog.dom.forms.getFormDataString = function(form) { + var sb = []; + goog.dom.forms.getFormDataHelper_(form, sb, + goog.dom.forms.addFormDataToStringBuffer_); + return sb.join('&'); +}; + + +/** + * Returns the form data as a map or an application/x-www-url-encoded + * string. This doesn't support file inputs. + * @param {HTMLFormElement} form The form. + * @param {Object} result The object form data is being put in. + * @param {Function} fnAppend Function that takes {@code result}, an element + * name, and an element value, and adds the name/value pair to the result + * object. + * @private + */ +goog.dom.forms.getFormDataHelper_ = function(form, result, fnAppend) { + var els = form.elements; + for (var el, i = 0; el = els[i]; i++) { + if (// Make sure we don't include elements that are not part of the form. + // Some browsers include non-form elements. Check for 'form' property. + // See http://code.google.com/p/closure-library/issues/detail?id=227 + // and + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#the-input-element + (el.form != form) || + el.disabled || + // HTMLFieldSetElement has a form property but no value. + el.tagName.toLowerCase() == 'fieldset') { + continue; + } + + var name = el.name; + switch (el.type.toLowerCase()) { + case 'file': + // file inputs are not supported + case 'submit': + case 'reset': + case 'button': + // don't submit these + break; + case 'select-multiple': + var values = goog.dom.forms.getValue(el); + if (values != null) { + for (var value, j = 0; value = values[j]; j++) { + fnAppend(result, name, value); + } + } + break; + default: + var value = goog.dom.forms.getValue(el); + if (value != null) { + fnAppend(result, name, value); + } + } + } + + // input[type=image] are not included in the elements collection + var inputs = form.getElementsByTagName('input'); + for (var input, i = 0; input = inputs[i]; i++) { + if (input.form == form && input.type.toLowerCase() == 'image') { + name = input.name; + fnAppend(result, name, input.value); + fnAppend(result, name + '.x', '0'); + fnAppend(result, name + '.y', '0'); + } + } +}; + + +/** + * Adds the name/value pair to the map. + * @param {!goog.structs.Map.>} map The map to add to. + * @param {string} name The name. + * @param {string} value The value. + * @private + */ +goog.dom.forms.addFormDataToMap_ = function(map, name, value) { + var array = map.get(name); + if (!array) { + array = []; + map.set(name, array); + } + array.push(value); +}; + + +/** + * Adds a name/value pair to an string buffer array in the form 'name=value'. + * @param {Array} sb The string buffer array for storing data. + * @param {string} name The name. + * @param {string} value The value. + * @private + */ +goog.dom.forms.addFormDataToStringBuffer_ = function(sb, name, value) { + sb.push(encodeURIComponent(name) + '=' + encodeURIComponent(value)); +}; + + +/** + * Whether the form has a file input. + * @param {HTMLFormElement} form The form. + * @return {boolean} Whether the form has a file input. + */ +goog.dom.forms.hasFileInput = function(form) { + var els = form.elements; + for (var el, i = 0; el = els[i]; i++) { + if (!el.disabled && el.type && el.type.toLowerCase() == 'file') { + return true; + } + } + return false; +}; + + +/** + * Enables or disables either all elements in a form or a single form element. + * @param {Element} el The element, either a form or an element within a form. + * @param {boolean} disabled Whether the element should be disabled. + */ +goog.dom.forms.setDisabled = function(el, disabled) { + // disable all elements in a form + if (el.tagName == 'FORM') { + var els = el.elements; + for (var i = 0; el = els[i]; i++) { + goog.dom.forms.setDisabled(el, disabled); + } + } else { + // makes sure to blur buttons, multi-selects, and any elements which + // maintain keyboard/accessibility focus when disabled + if (disabled == true) { + el.blur(); + } + el.disabled = disabled; + } +}; + + +/** + * Focuses, and optionally selects the content of, a form element. + * @param {Element} el The form element. + */ +goog.dom.forms.focusAndSelect = function(el) { + el.focus(); + if (el.select) { + el.select(); + } +}; + + +/** + * Whether a form element has a value. + * @param {Element} el The element. + * @return {boolean} Whether the form has a value. + */ +goog.dom.forms.hasValue = function(el) { + var value = goog.dom.forms.getValue(el); + return !!value; +}; + + +/** + * Whether a named form field has a value. + * @param {HTMLFormElement} form The form element. + * @param {string} name Name of an input to the form. + * @return {boolean} Whether the form has a value. + */ +goog.dom.forms.hasValueByName = function(form, name) { + var value = goog.dom.forms.getValueByName(form, name); + return !!value; +}; + + +/** + * Gets the current value of any element with a type. + * @param {Element} el The element. + * @return {string|Array|null} The current value of the element + * (or null). + */ +goog.dom.forms.getValue = function(el) { + var type = el.type; + if (!goog.isDef(type)) { + return null; + } + switch (type.toLowerCase()) { + case 'checkbox': + case 'radio': + return goog.dom.forms.getInputChecked_(el); + case 'select-one': + return goog.dom.forms.getSelectSingle_(el); + case 'select-multiple': + return goog.dom.forms.getSelectMultiple_(el); + default: + return goog.isDef(el.value) ? el.value : null; + } +}; + + +/** + * Alias for goog.dom.form.element.getValue + * @type {Function} + * @deprecated Use {@link goog.dom.forms.getValue} instead. + * @suppress {missingProvide} + */ +goog.dom.$F = goog.dom.forms.getValue; + + +/** + * Returns the value of the named form field. In the case of radio buttons, + * returns the value of the checked button with the given name. + * + * @param {HTMLFormElement} form The form element. + * @param {string} name Name of an input to the form. + * + * @return {Array|string|null} The value of the form element, or + * null if the form element does not exist or has no value. + */ +goog.dom.forms.getValueByName = function(form, name) { + var els = form.elements[name]; + + if (els) { + if (els.type) { + return goog.dom.forms.getValue(els); + } else { + for (var i = 0; i < els.length; i++) { + var val = goog.dom.forms.getValue(els[i]); + if (val) { + return val; + } + } + } + } + return null; +}; + + +/** + * Gets the current value of a checkable input element. + * @param {Element} el The element. + * @return {?string} The value of the form element (or null). + * @private + */ +goog.dom.forms.getInputChecked_ = function(el) { + return el.checked ? el.value : null; +}; + + +/** + * Gets the current value of a select-one element. + * @param {Element} el The element. + * @return {?string} The value of the form element (or null). + * @private + */ +goog.dom.forms.getSelectSingle_ = function(el) { + var selectedIndex = el.selectedIndex; + return selectedIndex >= 0 ? el.options[selectedIndex].value : null; +}; + + +/** + * Gets the current value of a select-multiple element. + * @param {Element} el The element. + * @return {Array?} The value of the form element (or null). + * @private + */ +goog.dom.forms.getSelectMultiple_ = function(el) { + var values = []; + for (var option, i = 0; option = el.options[i]; i++) { + if (option.selected) { + values.push(option.value); + } + } + return values.length ? values : null; +}; + + +/** + * Sets the current value of any element with a type. + * @param {Element} el The element. + * @param {*=} opt_value The value to give to the element, which will be coerced + * by the browser in the default case using toString. This value should be + * an array for setting the value of select multiple elements. + */ +goog.dom.forms.setValue = function(el, opt_value) { + var type = el.type; + if (goog.isDef(type)) { + switch (type.toLowerCase()) { + case 'checkbox': + case 'radio': + goog.dom.forms.setInputChecked_(el, + /** @type {string} */ (opt_value)); + break; + case 'select-one': + goog.dom.forms.setSelectSingle_(el, + /** @type {string} */ (opt_value)); + break; + case 'select-multiple': + goog.dom.forms.setSelectMultiple_(el, + /** @type {Array} */ (opt_value)); + break; + default: + el.value = goog.isDefAndNotNull(opt_value) ? opt_value : ''; + } + } +}; + + +/** + * Sets a checkable input element's checked property. + * #TODO(user): This seems potentially unintuitive since it doesn't set + * the value property but my hunch is that the primary use case is to check a + * checkbox, not to reset its value property. + * @param {Element} el The element. + * @param {string|boolean=} opt_value The value, sets the element checked if + * val is set. + * @private + */ +goog.dom.forms.setInputChecked_ = function(el, opt_value) { + el.checked = opt_value; +}; + + +/** + * Sets the value of a select-one element. + * @param {Element} el The element. + * @param {string=} opt_value The value of the selected option element. + * @private + */ +goog.dom.forms.setSelectSingle_ = function(el, opt_value) { + // unset any prior selections + el.selectedIndex = -1; + if (goog.isString(opt_value)) { + for (var option, i = 0; option = el.options[i]; i++) { + if (option.value == opt_value) { + option.selected = true; + break; + } + } + } +}; + + +/** + * Sets the value of a select-multiple element. + * @param {Element} el The element. + * @param {Array|string=} opt_value The value of the selected option + * element(s). + * @private + */ +goog.dom.forms.setSelectMultiple_ = function(el, opt_value) { + // reset string opt_values as an array + if (goog.isString(opt_value)) { + opt_value = [opt_value]; + } + for (var option, i = 0; option = el.options[i]; i++) { + // we have to reset the other options to false for select-multiple + option.selected = false; + if (opt_value) { + for (var value, j = 0; value = opt_value[j]; j++) { + if (option.value == value) { + option.selected = true; + } + } + } + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/forms_test.html b/mapbox-gl/closure-library/closure/goog/dom/forms_test.html new file mode 100644 index 0000000000..3e7b77715c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/forms_test.html @@ -0,0 +1,142 @@ + + + + + +Closure Unit Tests - goog.dom.forms + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Checkboxes + + +
    + + +
    + Radio Buttons + + +
    + +
    + Radio Buttons + + +
    + + + + + + + + + + +
    + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/forms_test.js b/mapbox-gl/closure-library/closure/goog/dom/forms_test.js new file mode 100644 index 0000000000..e444be5bf2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/forms_test.js @@ -0,0 +1,377 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.formsTest'); +goog.setTestOnly('goog.dom.formsTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.forms'); +goog.require('goog.testing.jsunit'); + +function testGetFormDataString() { + var el = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getFormDataString(el); + assertEquals( + 'in1=foo&in2=bar&in2=baaz&in3=&pass=bar&textarea=foo%20bar%20baz&' + + 'select1=1&select2=a&select2=c&select3=&checkbox1=on&radio=X&radio2=Y', + result); +} + +function testGetFormDataMap() { + var el = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getFormDataMap(el); + + assertArrayEquals(['foo'], result.get('in1')); + assertArrayEquals(['bar', 'baaz'], result.get('in2')); + assertArrayEquals(['1'], result.get('select1')); + assertArrayEquals(['a', 'c'], result.get('select2')); + assertArrayEquals(['on'], result.get('checkbox1')); + assertUndefined(result.get('select6')); + assertUndefined(result.get('checkbox2')); + assertArrayEquals(['X'], result.get('radio')); + assertArrayEquals(['Y'], result.get('radio2')); +} + +function testHasFileInput() { + var el = goog.dom.getElement('testform1'); + assertFalse(goog.dom.forms.hasFileInput(el)); + el = goog.dom.getElement('testform2'); + assertTrue(goog.dom.forms.hasFileInput(el)); +} + + +function testGetValueOnAtypicalValueElements() { + var el = goog.dom.getElement('testdiv1'); + var result = goog.dom.forms.getValue(el); + assertNull(result); + var el = goog.dom.getElement('testfieldset1'); + var result = goog.dom.forms.getValue(el); + assertNull(result); + var el = goog.dom.getElement('testlegend1'); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testHasValueInput() { + var el = goog.dom.getElement('in1'); + var result = goog.dom.forms.hasValue(el); + assertTrue(result); +} + +function testGetValueByNameForNonExistentElement() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'non_existent'); + assertNull(result); +} + +function testHasValueByNameInput() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'in1'); + assertTrue(result); +} + +function testHasValueInputEmpty() { + var el = goog.dom.getElement('in3'); + var result = goog.dom.forms.hasValue(el); + assertFalse(result); +} + +function testHasValueByNameEmpty() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'in3'); + assertFalse(result); +} + +function testHasValueRadio() { + var el = goog.dom.getElement('radio1'); + var result = goog.dom.forms.hasValue(el); + assertTrue(result); +} + +function testHasValueByNameRadio() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'radio'); + assertTrue(result); +} + +function testHasValueRadioNotChecked() { + var el = goog.dom.getElement('radio2'); + var result = goog.dom.forms.hasValue(el); + assertFalse(result); +} + +function testHasValueByNameRadioNotChecked() { + var form = goog.dom.getElement('testform3'); + var result = goog.dom.forms.hasValueByName(form, 'radio3'); + assertFalse(result); +} + +function testHasValueSelectSingle() { + var el = goog.dom.getElement('select1'); + var result = goog.dom.forms.hasValue(el); + assertTrue(result); +} + +function testHasValueByNameSelectSingle() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'select1'); + assertTrue(result); +} + +function testHasValueSelectMultiple() { + var el = goog.dom.getElement('select2'); + var result = goog.dom.forms.hasValue(el); + assertTrue(result); +} + +function testHasValueByNameSelectMultiple() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'select2'); + assertTrue(result); +} + +function testHasValueSelectNotSelected() { + // select without value + var el = goog.dom.getElement('select3'); + var result = goog.dom.forms.hasValue(el); + assertFalse(result); +} + +function testHasValueByNameSelectNotSelected() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.hasValueByName(form, 'select3'); + assertFalse(result); +} + +function testHasValueSelectMultipleNotSelected() { + var el = goog.dom.getElement('select6'); + var result = goog.dom.forms.hasValue(el); + assertFalse(result); +} + +function testHasValueByNameSelectMultipleNotSelected() { + var form = goog.dom.getElement('testform3'); + var result = goog.dom.forms.hasValueByName(form, 'select6'); + assertFalse(result); +} + +// TODO(user): make this a meaningful selenium test +function testSetDisabledFalse() { +} +function testSetDisabledTrue() { +} + +// TODO(user): make this a meaningful selenium test +function testFocusAndSelect() { + var el = goog.dom.getElement('in1'); + goog.dom.forms.focusAndSelect(el); +} + +function testGetValueInput() { + var el = goog.dom.getElement('in1'); + var result = goog.dom.forms.getValue(el); + assertEquals('foo', result); +} + +function testSetValueInput() { + var el = goog.dom.getElement('in3'); + goog.dom.forms.setValue(el, 'foo'); + assertEquals('foo', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, 3500); + assertEquals('3500', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, 0); + assertEquals('0', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, null); + assertEquals('', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, undefined); + assertEquals('', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, false); + assertEquals('false', goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, {}); + assertEquals({}.toString(), goog.dom.forms.getValue(el)); + + goog.dom.forms.setValue(el, { + toString: function() { + return 'test'; + } + }); + assertEquals('test', goog.dom.forms.getValue(el)); + + // unset + goog.dom.forms.setValue(el); + assertEquals('', goog.dom.forms.getValue(el)); +} + +function testGetValuePassword() { + var el = goog.dom.getElement('pass'); + var result = goog.dom.forms.getValue(el); + assertEquals('bar', result); +} + +function testGetValueByNamePassword() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'pass'); + assertEquals('bar', result); +} + +function testGetValueTextarea() { + var el = goog.dom.getElement('textarea1'); + var result = goog.dom.forms.getValue(el); + assertEquals('foo bar baz', result); +} + +function testGetValueByNameTextarea() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'textarea1'); + assertEquals('foo bar baz', result); +} + +function testSetValueTextarea() { + var el = goog.dom.getElement('textarea2'); + goog.dom.forms.setValue(el, 'foo bar baz'); + var result = goog.dom.forms.getValue(el); + assertEquals('foo bar baz', result); +} + +function testGetValueSelectSingle() { + var el = goog.dom.getElement('select1'); + var result = goog.dom.forms.getValue(el); + assertEquals('1', result); +} + +function testGetValueByNameSelectSingle() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'select1'); + assertEquals('1', result); +} + +function testSetValueSelectSingle() { + var el = goog.dom.getElement('select4'); + goog.dom.forms.setValue(el, '2'); + var result = goog.dom.forms.getValue(el); + assertEquals('2', result); + // unset + goog.dom.forms.setValue(el); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testSetValueSelectSingleEmptyString() { + var el = goog.dom.getElement('select7'); + // unset + goog.dom.forms.setValue(el); + var result = goog.dom.forms.getValue(el); + assertNull(result); + goog.dom.forms.setValue(el, ''); + result = goog.dom.forms.getValue(el); + assertEquals('', result); +} + +function testGetValueSelectMultiple() { + var el = goog.dom.getElement('select2'); + var result = goog.dom.forms.getValue(el); + assertArrayEquals(['a', 'c'], result); +} + +function testGetValueSelectMultipleNotSelected() { + var el = goog.dom.getElement('select6'); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testGetValueByNameSelectMultiple() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'select2'); + assertArrayEquals(['a', 'c'], result); +} + +function testSetValueSelectMultiple() { + var el = goog.dom.getElement('select5'); + goog.dom.forms.setValue(el, ['a', 'c']); + var result = goog.dom.forms.getValue(el); + assertArrayEquals(['a', 'c'], result); + + goog.dom.forms.setValue(el, 'a'); + var result = goog.dom.forms.getValue(el); + assertArrayEquals(['a'], result); + + // unset + goog.dom.forms.setValue(el); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testGetValueCheckbox() { + var el = goog.dom.getElement('checkbox1'); + var result = goog.dom.forms.getValue(el); + assertEquals('on', result); + var el = goog.dom.getElement('checkbox2'); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testGetValueByNameCheckbox() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'checkbox1'); + assertEquals('on', result); + result = goog.dom.forms.getValueByName(form, 'checkbox2'); + assertNull(result); +} + +function testGetValueRadio() { + var el = goog.dom.getElement('radio1'); + var result = goog.dom.forms.getValue(el); + assertEquals('X', result); + var el = goog.dom.getElement('radio2'); + var result = goog.dom.forms.getValue(el); + assertNull(result); +} + +function testGetValueByNameRadio() { + var form = goog.dom.getElement('testform1'); + var result = goog.dom.forms.getValueByName(form, 'radio'); + assertEquals('X', result); + + result = goog.dom.forms.getValueByName(form, 'radio2'); + assertEquals('Y', result); +} + +function testGetValueButton() { + var el = goog.dom.getElement('button'); + var result = goog.dom.forms.getValue(el); + assertEquals('button', result); +} + +function testGetValueSubmit() { + var el = goog.dom.getElement('submit'); + var result = goog.dom.forms.getValue(el); + assertEquals('submit', result); +} + +function testGetValueReset() { + var el = goog.dom.getElement('reset'); + var result = goog.dom.forms.getValue(el); + assertEquals('reset', result); +} + +function testGetFormDataHelperAndNonInputElements() { + var el = goog.dom.getElement('testform4'); + goog.dom.forms.getFormDataHelper_(el, {}, goog.nullFunction); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/fullscreen.js b/mapbox-gl/closure-library/closure/goog/dom/fullscreen.js new file mode 100644 index 0000000000..195421350e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/fullscreen.js @@ -0,0 +1,144 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Functions for managing full screen status of the DOM. + * + */ + +goog.provide('goog.dom.fullscreen'); +goog.provide('goog.dom.fullscreen.EventType'); + +goog.require('goog.dom'); +goog.require('goog.userAgent'); + + +/** + * Event types for full screen. + * @enum {string} + */ +goog.dom.fullscreen.EventType = { + /** Dispatched by the Document when the fullscreen status changes. */ + CHANGE: (function() { + if (goog.userAgent.WEBKIT) { + return 'webkitfullscreenchange'; + } + if (goog.userAgent.GECKO) { + return 'mozfullscreenchange'; + } + if (goog.userAgent.IE) { + return 'MSFullscreenChange'; + } + // Opera 12-14, and W3C standard (Draft): + // https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html + return 'fullscreenchange'; + })() +}; + + +/** + * Determines if full screen is supported. + * @param {!goog.dom.DomHelper=} opt_domHelper The DomHelper for the DOM being + * queried. If not provided, use the current DOM. + * @return {boolean} True iff full screen is supported. + */ +goog.dom.fullscreen.isSupported = function(opt_domHelper) { + var doc = goog.dom.fullscreen.getDocument_(opt_domHelper); + var body = doc.body; + return !!(body.webkitRequestFullscreen || + (body.mozRequestFullScreen && doc.mozFullScreenEnabled) || + (body.msRequestFullscreen && doc.msFullscreenEnabled) || + (body.requestFullscreen && doc.fullscreenEnabled)); +}; + + +/** + * Requests putting the element in full screen. + * @param {!Element} element The element to put full screen. + */ +goog.dom.fullscreen.requestFullScreen = function(element) { + if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } else if (element.requestFullscreen) { + element.requestFullscreen(); + } +}; + + +/** + * Requests putting the element in full screen with full keyboard access. + * @param {!Element} element The element to put full screen. + */ +goog.dom.fullscreen.requestFullScreenWithKeys = function( + element) { + if (element.mozRequestFullScreenWithKeys) { + element.mozRequestFullScreenWithKeys(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else { + goog.dom.fullscreen.requestFullScreen(element); + } +}; + + +/** + * Exits full screen. + * @param {!goog.dom.DomHelper=} opt_domHelper The DomHelper for the DOM being + * queried. If not provided, use the current DOM. + */ +goog.dom.fullscreen.exitFullScreen = function(opt_domHelper) { + var doc = goog.dom.fullscreen.getDocument_(opt_domHelper); + if (doc.webkitCancelFullScreen) { + doc.webkitCancelFullScreen(); + } else if (doc.mozCancelFullScreen) { + doc.mozCancelFullScreen(); + } else if (doc.msExitFullscreen) { + doc.msExitFullscreen(); + } else if (doc.exitFullscreen) { + doc.exitFullscreen(); + } +}; + + +/** + * Determines if the document is full screen. + * @param {!goog.dom.DomHelper=} opt_domHelper The DomHelper for the DOM being + * queried. If not provided, use the current DOM. + * @return {boolean} Whether the document is full screen. + */ +goog.dom.fullscreen.isFullScreen = function(opt_domHelper) { + var doc = goog.dom.fullscreen.getDocument_(opt_domHelper); + // IE 11 doesn't have similar boolean property, so check whether + // document.msFullscreenElement is null instead. + return !!(doc.webkitIsFullScreen || doc.mozFullScreen || + doc.msFullscreenElement || doc.fullscreenElement); +}; + + +/** + * Gets the document object of the dom. + * @param {!goog.dom.DomHelper=} opt_domHelper The DomHelper for the DOM being + * queried. If not provided, use the current DOM. + * @return {!Document} The dom document. + * @private + */ +goog.dom.fullscreen.getDocument_ = function(opt_domHelper) { + return opt_domHelper ? + opt_domHelper.getDocument() : + goog.dom.getDomHelper().getDocument(); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/iframe.js b/mapbox-gl/closure-library/closure/goog/dom/iframe.js new file mode 100644 index 0000000000..8298e22e0a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iframe.js @@ -0,0 +1,163 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for creating and working with iframes + * cross-browser. + * @author gboyer@google.com (Garry Boyer) + */ + + +goog.provide('goog.dom.iframe'); + +goog.require('goog.dom'); +goog.require('goog.userAgent'); + + +/** + * Safe source for a blank iframe. + * + * Intentionally not about:blank, which gives mixed content warnings in IE6 + * over HTTPS. + * + * @type {string} + */ +goog.dom.iframe.BLANK_SOURCE = 'javascript:""'; + + +/** + * Safe source for a new blank iframe that may not cause a new load of the + * iframe. This is different from {@code goog.dom.iframe.BLANK_SOURCE} in that + * it will allow an iframe to be loaded synchronously in more browsers, notably + * Gecko, following the javascript protocol spec. + * + * NOTE: This should not be used to replace the source of an existing iframe. + * The new src value will be ignored, per the spec. + * + * Due to cross-browser differences, the load is not guaranteed to be + * synchronous. If code depends on the load of the iframe, + * then {@code goog.net.IframeLoadMonitor} or a similar technique should be + * used. + * + * According to + * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#javascript-protocol + * the 'javascript:""' URL should trigger a new load of the iframe, which may be + * asynchronous. A void src, such as 'javascript:undefined', does not change + * the browsing context document's, and thus should not trigger another load. + * + * Intentionally not about:blank, which also triggers a load. + * + * NOTE: 'javascript:' URL handling spec compliance varies per browser. IE + * throws an error with 'javascript:undefined'. Webkit browsers will reload the + * iframe when setting this source on an existing iframe. + * + * @type {string} + */ +goog.dom.iframe.BLANK_SOURCE_NEW_FRAME = goog.userAgent.IE ? + 'javascript:""' : + 'javascript:undefined'; + + +/** + * Styles to help ensure an undecorated iframe. + * @type {string} + * @private + */ +goog.dom.iframe.STYLES_ = 'border:0;vertical-align:bottom;'; + + +/** + * Creates a completely blank iframe element. + * + * The iframe will not caused mixed-content warnings for IE6 under HTTPS. + * The iframe will also have no borders or padding, so that the styled width + * and height will be the actual width and height of the iframe. + * + * This function currently only attempts to create a blank iframe. There + * are no guarantees to the contents of the iframe or whether it is rendered + * in quirks mode. + * + * @param {goog.dom.DomHelper} domHelper The dom helper to use. + * @param {string=} opt_styles CSS styles for the iframe. + * @return {!HTMLIFrameElement} A completely blank iframe. + */ +goog.dom.iframe.createBlank = function(domHelper, opt_styles) { + return /** @type {!HTMLIFrameElement} */ (domHelper.createDom('iframe', { + 'frameborder': 0, + // Since iframes are inline elements, we must align to bottom to + // compensate for the line descent. + 'style': goog.dom.iframe.STYLES_ + (opt_styles || ''), + 'src': goog.dom.iframe.BLANK_SOURCE + })); +}; + + +/** + * Writes the contents of a blank iframe that has already been inserted + * into the document. + * @param {!HTMLIFrameElement} iframe An iframe with no contents, such as + * one created by goog.dom.iframe.createBlank, but already appended to + * a parent document. + * @param {string} content Content to write to the iframe, from doctype to + * the HTML close tag. + */ +goog.dom.iframe.writeContent = function(iframe, content) { + var doc = goog.dom.getFrameContentDocument(iframe); + doc.open(); + doc.write(content); + doc.close(); +}; + + +// TODO(gboyer): Provide a higher-level API for the most common use case, so +// that you can just provide a list of stylesheets and some content HTML. +/** + * Creates a same-domain iframe containing preloaded content. + * + * This is primarily useful for DOM sandboxing. One use case is to embed + * a trusted Javascript app with potentially conflicting CSS styles. The + * second case is to reduce the cost of layout passes by the browser -- for + * example, you can perform sandbox sizing of characters in an iframe while + * manipulating a heavy DOM in the main window. The iframe and parent frame + * can access each others' properties and functions without restriction. + * + * @param {!Element} parentElement The parent element in which to append the + * iframe. + * @param {string=} opt_headContents Contents to go into the iframe's head. + * @param {string=} opt_bodyContents Contents to go into the iframe's body. + * @param {string=} opt_styles CSS styles for the iframe itself, before adding + * to the parent element. + * @param {boolean=} opt_quirks Whether to use quirks mode (false by default). + * @return {!HTMLIFrameElement} An iframe that has the specified contents. + */ +goog.dom.iframe.createWithContent = function( + parentElement, opt_headContents, opt_bodyContents, opt_styles, opt_quirks) { + var domHelper = goog.dom.getDomHelper(parentElement); + // Generate the HTML content. + var contentBuf = []; + + if (!opt_quirks) { + contentBuf.push(''); + } + contentBuf.push('', opt_headContents, '', + opt_bodyContents, ''); + + var iframe = goog.dom.iframe.createBlank(domHelper, opt_styles); + + // Cannot manipulate iframe content until it is in a document. + parentElement.appendChild(iframe); + goog.dom.iframe.writeContent(iframe, contentBuf.join('')); + + return iframe; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/iframe_test.html b/mapbox-gl/closure-library/closure/goog/dom/iframe_test.html new file mode 100644 index 0000000000..7c952af435 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iframe_test.html @@ -0,0 +1,43 @@ + + + + + + +Closure Unit Tests - goog.dom.iframe + + + + + +
    +
    + Blank Iframe - The below area should be completely white. +
    + + + +
    +
    +
    +
    +
    + +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/iframe_test.js b/mapbox-gl/closure-library/closure/goog/dom/iframe_test.js new file mode 100644 index 0000000000..1b93f5e93b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iframe_test.js @@ -0,0 +1,64 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.iframeTest'); +goog.setTestOnly('goog.dom.iframeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.iframe'); +goog.require('goog.testing.jsunit'); + +var domHelper; +var sandbox; + +function setUpPage() { + domHelper = goog.dom.getDomHelper(); + sandbox = domHelper.getElement('sandbox'); +} + +function setUp() { + goog.dom.removeChildren(sandbox); +} + +function testCreateWithContent() { + var iframe = goog.dom.iframe.createWithContent(sandbox, + 'Foo Title', '
    Test
    ', + 'position: absolute', + false /* opt_quirks */); + + var doc = goog.dom.getFrameContentDocument(iframe); + assertNotNull(doc.getElementById('blah')); + assertEquals('Foo Title', doc.title); + assertEquals('absolute', iframe.style.position); +} + +function testCreateBlankYieldsIframeWithNoBorderOrPadding() { + var iframe = goog.dom.iframe.createBlank(domHelper); + iframe.style.width = '350px'; + iframe.style.height = '250px'; + var blankElement = domHelper.getElement('blank'); + blankElement.appendChild(iframe); + assertEquals( + 'Width should be as styled: no extra borders, padding, etc.', + 350, blankElement.offsetWidth); + assertEquals( + 'Height should be as styled: no extra borders, padding, etc.', + 250, blankElement.offsetHeight); +} + +function testCreateBlankWithStyles() { + var iframe = goog.dom.iframe.createBlank(domHelper, 'position:absolute'); + assertEquals('absolute', iframe.style.position); + assertEquals('bottom', iframe.style.verticalAlign); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/iter.js b/mapbox-gl/closure-library/closure/goog/dom/iter.js new file mode 100644 index 0000000000..7516414500 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iter.js @@ -0,0 +1,129 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Iterators over DOM nodes. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.iter.AncestorIterator'); +goog.provide('goog.dom.iter.ChildIterator'); +goog.provide('goog.dom.iter.SiblingIterator'); + +goog.require('goog.iter.Iterator'); +goog.require('goog.iter.StopIteration'); + + + +/** + * Iterator over a Node's siblings. + * @param {Node} node The node to start with. + * @param {boolean=} opt_includeNode Whether to return the given node as the + * first return value from next. + * @param {boolean=} opt_reverse Whether to traverse siblings in reverse + * document order. + * @constructor + * @extends {goog.iter.Iterator} + */ +goog.dom.iter.SiblingIterator = function(node, opt_includeNode, opt_reverse) { + /** + * The current node, or null if iteration is finished. + * @type {Node} + * @private + */ + this.node_ = node; + + /** + * Whether to iterate in reverse. + * @type {boolean} + * @private + */ + this.reverse_ = !!opt_reverse; + + if (node && !opt_includeNode) { + this.next(); + } +}; +goog.inherits(goog.dom.iter.SiblingIterator, goog.iter.Iterator); + + +/** @override */ +goog.dom.iter.SiblingIterator.prototype.next = function() { + var node = this.node_; + if (!node) { + throw goog.iter.StopIteration; + } + this.node_ = this.reverse_ ? node.previousSibling : node.nextSibling; + return node; +}; + + + +/** + * Iterator over an Element's children. + * @param {Element} element The element to iterate over. + * @param {boolean=} opt_reverse Optionally traverse children from last to + * first. + * @param {number=} opt_startIndex Optional starting index. + * @constructor + * @extends {goog.dom.iter.SiblingIterator} + * @final + */ +goog.dom.iter.ChildIterator = function(element, opt_reverse, opt_startIndex) { + if (!goog.isDef(opt_startIndex)) { + opt_startIndex = opt_reverse && element.childNodes.length ? + element.childNodes.length - 1 : 0; + } + goog.dom.iter.SiblingIterator.call(this, element.childNodes[opt_startIndex], + true, opt_reverse); +}; +goog.inherits(goog.dom.iter.ChildIterator, goog.dom.iter.SiblingIterator); + + + +/** + * Iterator over a Node's ancestors, stopping after the document body. + * @param {Node} node The node to start with. + * @param {boolean=} opt_includeNode Whether to return the given node as the + * first return value from next. + * @constructor + * @extends {goog.iter.Iterator} + * @final + */ +goog.dom.iter.AncestorIterator = function(node, opt_includeNode) { + /** + * The current node, or null if iteration is finished. + * @type {Node} + * @private + */ + this.node_ = node; + + if (node && !opt_includeNode) { + this.next(); + } +}; +goog.inherits(goog.dom.iter.AncestorIterator, goog.iter.Iterator); + + +/** @override */ +goog.dom.iter.AncestorIterator.prototype.next = function() { + var node = this.node_; + if (!node) { + throw goog.iter.StopIteration; + } + this.node_ = node.parentNode; + return node; +}; + diff --git a/mapbox-gl/closure-library/closure/goog/dom/iter_test.html b/mapbox-gl/closure-library/closure/goog/dom/iter_test.html new file mode 100644 index 0000000000..8936c0c78d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iter_test.html @@ -0,0 +1,20 @@ + + + + +Closure Unit Tests - goog.dom.iter + + + + +
    abc
    def
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/iter_test.js b/mapbox-gl/closure-library/closure/goog/dom/iter_test.js new file mode 100644 index 0000000000..d141c0604a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/iter_test.js @@ -0,0 +1,97 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.iterTest'); +goog.setTestOnly('goog.dom.iterTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.iter.AncestorIterator'); +goog.require('goog.dom.iter.ChildIterator'); +goog.require('goog.dom.iter.SiblingIterator'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); + +var test; +var br; + +function setUpPage() { + test = goog.dom.getElement('test'); + br = goog.dom.getElement('br'); +} + +function testNextSibling() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.SiblingIterator(test.firstChild), + ['#br', 'def']); +} + +function testNextSiblingInclusive() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.SiblingIterator(test.firstChild, true), + ['abc', '#br', 'def']); +} + +function testPreviousSibling() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.SiblingIterator(test.lastChild, false, true), + ['#br', 'abc']); +} + +function testPreviousSiblingInclusive() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.SiblingIterator(test.lastChild, true, true), + ['def', '#br', 'abc']); +} + +function testChildIterator() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.ChildIterator(test), + ['abc', '#br', 'def']); +} + +function testChildIteratorIndex() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.ChildIterator(test, false, 1), + ['#br', 'def']); +} + +function testChildIteratorReverse() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.ChildIterator(test, true), + ['def', '#br', 'abc']); +} + +function testEmptyChildIteratorReverse() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.ChildIterator(br, true), []); +} + +function testChildIteratorIndexReverse() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.ChildIterator(test, true, 1), + ['#br', 'abc']); +} + +function testAncestorIterator() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.AncestorIterator(br), + ['#test', '#body', '#html', goog.dom.NodeType.DOCUMENT]); +} + +function testAncestorIteratorInclusive() { + goog.testing.dom.assertNodesMatch( + new goog.dom.iter.AncestorIterator(br, true), + ['#br', '#test', '#body', '#html', goog.dom.NodeType.DOCUMENT]); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/multirange.js b/mapbox-gl/closure-library/closure/goog/dom/multirange.js new file mode 100644 index 0000000000..69e3b41b2d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/multirange.js @@ -0,0 +1,521 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with W3C multi-part ranges. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.MultiRange'); +goog.provide('goog.dom.MultiRangeIterator'); + +goog.require('goog.array'); +goog.require('goog.dom.AbstractMultiRange'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.RangeIterator'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.SavedRange'); +goog.require('goog.dom.TextRange'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.log'); + + + +/** + * Creates a new multi part range with no properties. Do not use this + * constructor: use one of the goog.dom.Range.createFrom* methods instead. + * @constructor + * @extends {goog.dom.AbstractMultiRange} + * @final + */ +goog.dom.MultiRange = function() { + /** + * Array of browser sub-ranges comprising this multi-range. + * @type {Array} + * @private + */ + this.browserRanges_ = []; + + /** + * Lazily initialized array of range objects comprising this multi-range. + * @type {Array} + * @private + */ + this.ranges_ = []; + + /** + * Lazily computed sorted version of ranges_, sorted by start point. + * @type {Array?} + * @private + */ + this.sortedRanges_ = null; + + /** + * Lazily computed container node. + * @type {Node} + * @private + */ + this.container_ = null; +}; +goog.inherits(goog.dom.MultiRange, goog.dom.AbstractMultiRange); + + +/** + * Creates a new range wrapper from the given browser selection object. Do not + * use this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Selection} selection The browser selection object. + * @return {!goog.dom.MultiRange} A range wrapper object. + */ +goog.dom.MultiRange.createFromBrowserSelection = function(selection) { + var range = new goog.dom.MultiRange(); + for (var i = 0, len = selection.rangeCount; i < len; i++) { + range.browserRanges_.push(selection.getRangeAt(i)); + } + return range; +}; + + +/** + * Creates a new range wrapper from the given browser ranges. Do not + * use this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Array} browserRanges The browser ranges. + * @return {!goog.dom.MultiRange} A range wrapper object. + */ +goog.dom.MultiRange.createFromBrowserRanges = function(browserRanges) { + var range = new goog.dom.MultiRange(); + range.browserRanges_ = goog.array.clone(browserRanges); + return range; +}; + + +/** + * Creates a new range wrapper from the given goog.dom.TextRange objects. Do + * not use this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Array} textRanges The text range objects. + * @return {!goog.dom.MultiRange} A range wrapper object. + */ +goog.dom.MultiRange.createFromTextRanges = function(textRanges) { + var range = new goog.dom.MultiRange(); + range.ranges_ = textRanges; + range.browserRanges_ = goog.array.map(textRanges, function(range) { + return range.getBrowserRangeObject(); + }); + return range; +}; + + +/** + * Logging object. + * @type {goog.log.Logger} + * @private + */ +goog.dom.MultiRange.prototype.logger_ = + goog.log.getLogger('goog.dom.MultiRange'); + + +// Method implementations + + +/** + * Clears cached values. Should be called whenever this.browserRanges_ is + * modified. + * @private + */ +goog.dom.MultiRange.prototype.clearCachedValues_ = function() { + this.ranges_ = []; + this.sortedRanges_ = null; + this.container_ = null; +}; + + +/** + * @return {!goog.dom.MultiRange} A clone of this range. + * @override + */ +goog.dom.MultiRange.prototype.clone = function() { + return goog.dom.MultiRange.createFromBrowserRanges(this.browserRanges_); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getType = function() { + return goog.dom.RangeType.MULTI; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getBrowserRangeObject = function() { + // NOTE(robbyw): This method does not make sense for multi-ranges. + if (this.browserRanges_.length > 1) { + goog.log.warning(this.logger_, + 'getBrowserRangeObject called on MultiRange with more than 1 range'); + } + return this.browserRanges_[0]; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.setBrowserRangeObject = function(nativeRange) { + // TODO(robbyw): Look in to adding setBrowserSelectionObject. + return false; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getTextRangeCount = function() { + return this.browserRanges_.length; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getTextRange = function(i) { + if (!this.ranges_[i]) { + this.ranges_[i] = goog.dom.TextRange.createFromBrowserRange( + this.browserRanges_[i]); + } + return this.ranges_[i]; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getContainer = function() { + if (!this.container_) { + var nodes = []; + for (var i = 0, len = this.getTextRangeCount(); i < len; i++) { + nodes.push(this.getTextRange(i).getContainer()); + } + this.container_ = goog.dom.findCommonAncestor.apply(null, nodes); + } + return this.container_; +}; + + +/** + * @return {!Array} An array of sub-ranges, sorted by start + * point. + */ +goog.dom.MultiRange.prototype.getSortedRanges = function() { + if (!this.sortedRanges_) { + this.sortedRanges_ = this.getTextRanges(); + this.sortedRanges_.sort(function(a, b) { + var aStartNode = a.getStartNode(); + var aStartOffset = a.getStartOffset(); + var bStartNode = b.getStartNode(); + var bStartOffset = b.getStartOffset(); + + if (aStartNode == bStartNode && aStartOffset == bStartOffset) { + return 0; + } + + return goog.dom.Range.isReversed(aStartNode, aStartOffset, bStartNode, + bStartOffset) ? 1 : -1; + }); + } + return this.sortedRanges_; +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getStartNode = function() { + return this.getSortedRanges()[0].getStartNode(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getStartOffset = function() { + return this.getSortedRanges()[0].getStartOffset(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getEndNode = function() { + // NOTE(robbyw): This may return the wrong node if any subranges overlap. + return goog.array.peek(this.getSortedRanges()).getEndNode(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getEndOffset = function() { + // NOTE(robbyw): This may return the wrong value if any subranges overlap. + return goog.array.peek(this.getSortedRanges()).getEndOffset(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.isRangeInDocument = function() { + return goog.array.every(this.getTextRanges(), function(range) { + return range.isRangeInDocument(); + }); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.isCollapsed = function() { + return this.browserRanges_.length == 0 || + this.browserRanges_.length == 1 && this.getTextRange(0).isCollapsed(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getText = function() { + return goog.array.map(this.getTextRanges(), function(range) { + return range.getText(); + }).join(''); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getHtmlFragment = function() { + return this.getValidHtml(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getValidHtml = function() { + // NOTE(robbyw): This does not behave well if the sub-ranges overlap. + return goog.array.map(this.getTextRanges(), function(range) { + return range.getValidHtml(); + }).join(''); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.getPastableHtml = function() { + // TODO(robbyw): This should probably do something smart like group TR and TD + // selections in to the same table. + return this.getValidHtml(); +}; + + +/** @override */ +goog.dom.MultiRange.prototype.__iterator__ = function(opt_keys) { + return new goog.dom.MultiRangeIterator(this); +}; + + +// RANGE ACTIONS + + +/** @override */ +goog.dom.MultiRange.prototype.select = function() { + var selection = goog.dom.AbstractRange.getBrowserSelectionForWindow( + this.getWindow()); + selection.removeAllRanges(); + for (var i = 0, len = this.getTextRangeCount(); i < len; i++) { + selection.addRange(this.getTextRange(i).getBrowserRangeObject()); + } +}; + + +/** @override */ +goog.dom.MultiRange.prototype.removeContents = function() { + goog.array.forEach(this.getTextRanges(), function(range) { + range.removeContents(); + }); +}; + + +// SAVE/RESTORE + + +/** @override */ +goog.dom.MultiRange.prototype.saveUsingDom = function() { + return new goog.dom.DomSavedMultiRange_(this); +}; + + +// RANGE MODIFICATION + + +/** + * Collapses this range to a single point, either the first or last point + * depending on the parameter. This will result in the number of ranges in this + * multi range becoming 1. + * @param {boolean} toAnchor Whether to collapse to the anchor. + * @override + */ +goog.dom.MultiRange.prototype.collapse = function(toAnchor) { + if (!this.isCollapsed()) { + var range = toAnchor ? this.getTextRange(0) : this.getTextRange( + this.getTextRangeCount() - 1); + + this.clearCachedValues_(); + range.collapse(toAnchor); + this.ranges_ = [range]; + this.sortedRanges_ = [range]; + this.browserRanges_ = [range.getBrowserRangeObject()]; + } +}; + + +// SAVED RANGE OBJECTS + + + +/** + * A SavedRange implementation using DOM endpoints. + * @param {goog.dom.MultiRange} range The range to save. + * @constructor + * @extends {goog.dom.SavedRange} + * @private + */ +goog.dom.DomSavedMultiRange_ = function(range) { + /** + * Array of saved ranges. + * @type {Array} + * @private + */ + this.savedRanges_ = goog.array.map(range.getTextRanges(), function(range) { + return range.saveUsingDom(); + }); +}; +goog.inherits(goog.dom.DomSavedMultiRange_, goog.dom.SavedRange); + + +/** + * @return {!goog.dom.MultiRange} The restored range. + * @override + */ +goog.dom.DomSavedMultiRange_.prototype.restoreInternal = function() { + var ranges = goog.array.map(this.savedRanges_, function(savedRange) { + return savedRange.restore(); + }); + return goog.dom.MultiRange.createFromTextRanges(ranges); +}; + + +/** @override */ +goog.dom.DomSavedMultiRange_.prototype.disposeInternal = function() { + goog.dom.DomSavedMultiRange_.superClass_.disposeInternal.call(this); + + goog.array.forEach(this.savedRanges_, function(savedRange) { + savedRange.dispose(); + }); + delete this.savedRanges_; +}; + + +// RANGE ITERATION + + + +/** + * Subclass of goog.dom.TagIterator that iterates over a DOM range. It + * adds functions to determine the portion of each text node that is selected. + * + * @param {goog.dom.MultiRange} range The range to traverse. + * @constructor + * @extends {goog.dom.RangeIterator} + * @final + */ +goog.dom.MultiRangeIterator = function(range) { + if (range) { + this.iterators_ = goog.array.map( + range.getSortedRanges(), + function(r) { + return goog.iter.toIterator(r); + }); + } + + goog.dom.RangeIterator.call( + this, range ? this.getStartNode() : null, false); +}; +goog.inherits(goog.dom.MultiRangeIterator, goog.dom.RangeIterator); + + +/** + * The list of range iterators left to traverse. + * @type {Array?} + * @private + */ +goog.dom.MultiRangeIterator.prototype.iterators_ = null; + + +/** + * The index of the current sub-iterator being traversed. + * @type {number} + * @private + */ +goog.dom.MultiRangeIterator.prototype.currentIdx_ = 0; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.getStartTextOffset = function() { + return this.iterators_[this.currentIdx_].getStartTextOffset(); +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.getEndTextOffset = function() { + return this.iterators_[this.currentIdx_].getEndTextOffset(); +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.getStartNode = function() { + return this.iterators_[0].getStartNode(); +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.getEndNode = function() { + return goog.array.peek(this.iterators_).getEndNode(); +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.isLast = function() { + return this.iterators_[this.currentIdx_].isLast(); +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.next = function() { + /** @preserveTry */ + try { + var it = this.iterators_[this.currentIdx_]; + var next = it.next(); + this.setPosition(it.node, it.tagType, it.depth); + return next; + } catch (ex) { + if (ex !== goog.iter.StopIteration || + this.iterators_.length - 1 == this.currentIdx_) { + throw ex; + } else { + // In case we got a StopIteration, increment counter and try again. + this.currentIdx_++; + return this.next(); + } + } +}; + + +/** @override */ +goog.dom.MultiRangeIterator.prototype.copyFrom = function(other) { + this.iterators_ = goog.array.clone(other.iterators_); + goog.dom.MultiRangeIterator.superClass_.copyFrom.call(this, other); +}; + + +/** + * @return {!goog.dom.MultiRangeIterator} An identical iterator. + * @override + */ +goog.dom.MultiRangeIterator.prototype.clone = function() { + var copy = new goog.dom.MultiRangeIterator(null); + copy.copyFrom(this); + return copy; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/multirange_test.html b/mapbox-gl/closure-library/closure/goog/dom/multirange_test.html new file mode 100644 index 0000000000..191c9a903c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/multirange_test.html @@ -0,0 +1,27 @@ + + + + + + +Closure Unit Tests - goog.dom.MultiRange + + + + +
    +
    abc
    +
    defghi
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/multirange_test.js b/mapbox-gl/closure-library/closure/goog/dom/multirange_test.js new file mode 100644 index 0000000000..f9aeb75743 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/multirange_test.js @@ -0,0 +1,57 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.MultiRangeTest'); +goog.setTestOnly('goog.dom.MultiRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.MultiRange'); +goog.require('goog.dom.Range'); +goog.require('goog.iter'); +goog.require('goog.testing.jsunit'); + +var range; +function setUp() { + range = new goog.dom.MultiRange.createFromTextRanges([ + goog.dom.Range.createFromNodeContents(goog.dom.getElement('test2')), + goog.dom.Range.createFromNodeContents(goog.dom.getElement('test1')) + ]); +} + +function testStartAndEnd() { + assertEquals(goog.dom.getElement('test1').firstChild, range.getStartNode()); + assertEquals(0, range.getStartOffset()); + assertEquals(goog.dom.getElement('test2').firstChild, range.getEndNode()); + assertEquals(6, range.getEndOffset()); +} + +function testStartAndEndIterator() { + var it = goog.iter.toIterator(range); + assertEquals(goog.dom.getElement('test1').firstChild, it.getStartNode()); + assertEquals(0, it.getStartTextOffset()); + assertEquals(goog.dom.getElement('test2').firstChild, it.getEndNode()); + assertEquals(3, it.getEndTextOffset()); + + it.next(); + it.next(); + assertEquals(6, it.getEndTextOffset()); +} + +function testIteration() { + var tags = goog.iter.toArray(range); + assertEquals(2, tags.length); + + assertEquals(goog.dom.getElement('test1').firstChild, tags[0]); + assertEquals(goog.dom.getElement('test2').firstChild, tags[1]); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeiterator.js b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator.js new file mode 100644 index 0000000000..bca563c3e3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator.js @@ -0,0 +1,87 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Iterator subclass for DOM tree traversal. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.NodeIterator'); + +goog.require('goog.dom.TagIterator'); + + + +/** + * A DOM tree traversal iterator. + * + * Starting with the given node, the iterator walks the DOM in order, reporting + * events for each node. The iterator acts as a prefix iterator: + * + *
    + * <div>1<span>2</span>3</div>
    + * 
    + * + * Will return the following nodes: + * + * [div, 1, span, 2, 3] + * + * With the following depths + * + * [1, 1, 2, 2, 1] + * + * Imagining | represents iterator position, the traversal stops at + * each of the following locations: + * + *
    <div>|1|<span>|2|</span>3|</div>
    + * + * The iterator can also be used in reverse mode, which will return the nodes + * and states in the opposite order. The depths will be slightly different + * since, like in normal mode, the depth is computed *after* the last move. + * + * Lastly, it is possible to create an iterator that is unconstrained, meaning + * that it will continue iterating until the end of the document instead of + * until exiting the start node. + * + * @param {Node=} opt_node The start node. Defaults to an empty iterator. + * @param {boolean=} opt_reversed Whether to traverse the tree in reverse. + * @param {boolean=} opt_unconstrained Whether the iterator is not constrained + * to the starting node and its children. + * @param {number=} opt_depth The starting tree depth. + * @constructor + * @extends {goog.dom.TagIterator} + * @final + */ +goog.dom.NodeIterator = function(opt_node, opt_reversed, + opt_unconstrained, opt_depth) { + goog.dom.TagIterator.call(this, opt_node, opt_reversed, opt_unconstrained, + null, opt_depth); +}; +goog.inherits(goog.dom.NodeIterator, goog.dom.TagIterator); + + +/** + * Moves to the next position in the DOM tree. + * @return {Node} Returns the next node, or throws a goog.iter.StopIteration + * exception if the end of the iterator's range has been reached. + * @override + */ +goog.dom.NodeIterator.prototype.next = function() { + do { + goog.dom.NodeIterator.superClass_.next.call(this); + } while (this.isEndTag()); + + return this.node; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.html b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.html new file mode 100644 index 0000000000..23c6bc2657 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.html @@ -0,0 +1,27 @@ + + + + + +goog.dom.NodeIterator Tests + + + + + + +
    Text

    Text

    +
    • Not
    • Closed
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.js b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.js new file mode 100644 index 0000000000..bed98a3fd1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeiterator_test.js @@ -0,0 +1,39 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.NodeIteratorTest'); +goog.setTestOnly('goog.dom.NodeIteratorTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeIterator'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); + +function testBasic() { + goog.testing.dom.assertNodesMatch( + new goog.dom.NodeIterator(goog.dom.getElement('test')), + ['#test', '#a1', 'T', '#b1', 'e', 'xt', '#span1', '#p1', 'Text']); +} + +function testUnclosed() { + goog.testing.dom.assertNodesMatch( + new goog.dom.NodeIterator(goog.dom.getElement('test2')), + ['#test2', '#li1', 'Not', '#li2', 'Closed']); +} + +function testReverse() { + goog.testing.dom.assertNodesMatch( + new goog.dom.NodeIterator(goog.dom.getElement('test'), true), + ['Text', '#p1', '#span1', 'xt', 'e', '#b1', 'T', '#a1', '#test']); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeoffset.js b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset.js new file mode 100644 index 0000000000..9a65dbea83 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset.js @@ -0,0 +1,114 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Object to store the offset from one node to another in a way + * that works on any similar DOM structure regardless of whether it is the same + * actual nodes. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.NodeOffset'); + +goog.require('goog.Disposable'); +goog.require('goog.dom.TagName'); + + + +/** + * Object to store the offset from one node to another in a way that works on + * any similar DOM structure regardless of whether it is the same actual nodes. + * @param {Node} node The node to get the offset for. + * @param {Node} baseNode The node to calculate the offset from. + * @extends {goog.Disposable} + * @constructor + * @final + */ +goog.dom.NodeOffset = function(node, baseNode) { + goog.Disposable.call(this); + + /** + * A stack of childNode offsets. + * @type {Array} + * @private + */ + this.offsetStack_ = []; + + /** + * A stack of childNode names. + * @type {Array} + * @private + */ + this.nameStack_ = []; + + while (node && node.nodeName != goog.dom.TagName.BODY && node != baseNode) { + // Compute the sibling offset. + var siblingOffset = 0; + var sib = node.previousSibling; + while (sib) { + sib = sib.previousSibling; + ++siblingOffset; + } + this.offsetStack_.unshift(siblingOffset); + this.nameStack_.unshift(node.nodeName); + + node = node.parentNode; + } +}; +goog.inherits(goog.dom.NodeOffset, goog.Disposable); + + +/** + * @return {string} A string representation of this object. + * @override + */ +goog.dom.NodeOffset.prototype.toString = function() { + var strs = []; + var name; + for (var i = 0; name = this.nameStack_[i]; i++) { + strs.push(this.offsetStack_[i] + ',' + name); + } + return strs.join('\n'); +}; + + +/** + * Walk the dom and find the node relative to baseNode. Returns null on + * failure. + * @param {Node} baseNode The node to start walking from. Should be equivalent + * to the node passed in to the constructor, in that it should have the + * same contents. + * @return {Node} The node relative to baseNode, or null on failure. + */ +goog.dom.NodeOffset.prototype.findTargetNode = function(baseNode) { + var name; + var curNode = baseNode; + for (var i = 0; name = this.nameStack_[i]; ++i) { + curNode = curNode.childNodes[this.offsetStack_[i]]; + + // Sanity check and make sure the element names match. + if (!curNode || curNode.nodeName != name) { + return null; + } + } + return curNode; +}; + + +/** @override */ +goog.dom.NodeOffset.prototype.disposeInternal = function() { + delete this.offsetStack_; + delete this.nameStack_; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.html b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.html new file mode 100644 index 0000000000..8d85496519 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.html @@ -0,0 +1,24 @@ + + + + + +goog.dom.NodeOffset Tests + + + + + +
    Text
    and more text.
    +
    +
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.js b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.js new file mode 100644 index 0000000000..0f1de76e4b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodeoffset_test.js @@ -0,0 +1,85 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.NodeOffsetTest'); +goog.setTestOnly('goog.dom.NodeOffsetTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeOffset'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.testing.jsunit'); + +var test1; +var test2; +var i; +var empty; + +function setUpPage() { + test1 = goog.dom.getElement('test1'); + i = goog.dom.getElement('i'); + test2 = goog.dom.getElement('test2'); + test2.innerHTML = test1.innerHTML; + empty = goog.dom.getElement('empty'); +} + +function testElementOffset() { + var nodeOffset = new goog.dom.NodeOffset(i, test1); + + var recovered = nodeOffset.findTargetNode(test2); + assertNotNull('Should recover a node.', recovered); + assertEquals('Should recover an I node.', goog.dom.TagName.I, + recovered.tagName); + assertTrue('Should recover a child of test2', + goog.dom.contains(test2, recovered)); + assertFalse('Should not recover a child of test1', + goog.dom.contains(test1, recovered)); + + nodeOffset.dispose(); +} + +function testNodeOffset() { + var nodeOffset = new goog.dom.NodeOffset(i.firstChild, test1); + + var recovered = nodeOffset.findTargetNode(test2); + assertNotNull('Should recover a node.', recovered); + assertEquals('Should recover a text node.', goog.dom.NodeType.TEXT, + recovered.nodeType); + assertEquals('Should have correct contents.', 'text.', + recovered.nodeValue); + assertTrue('Should recover a child of test2', + goog.dom.contains(test2, recovered)); + assertFalse('Should not recover a child of test1', + goog.dom.contains(test1, recovered)); + + nodeOffset.dispose(); +} + +function testToString() { + var nodeOffset = new goog.dom.NodeOffset(i.firstChild, test1); + + assertEquals('Should have correct string representation', + '3,B\n1,I\n0,#text', nodeOffset.toString()); + + nodeOffset.dispose(); +} + +function testBadRecovery() { + var nodeOffset = new goog.dom.NodeOffset(i.firstChild, test1); + + var recovered = nodeOffset.findTargetNode(empty); + assertNull('Should recover nothing.', recovered); + + nodeOffset.dispose(); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/nodetype.js b/mapbox-gl/closure-library/closure/goog/dom/nodetype.js new file mode 100644 index 0000000000..cccb4706ec --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/nodetype.js @@ -0,0 +1,48 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of goog.dom.NodeType. + */ + +goog.provide('goog.dom.NodeType'); + + +/** + * Constants for the nodeType attribute in the Node interface. + * + * These constants match those specified in the Node interface. These are + * usually present on the Node object in recent browsers, but not in older + * browsers (specifically, early IEs) and thus are given here. + * + * In some browsers (early IEs), these are not defined on the Node object, + * so they are provided here. + * + * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247 + * @enum {number} + */ +goog.dom.NodeType = { + ELEMENT: 1, + ATTRIBUTE: 2, + TEXT: 3, + CDATA_SECTION: 4, + ENTITY_REFERENCE: 5, + ENTITY: 6, + PROCESSING_INSTRUCTION: 7, + COMMENT: 8, + DOCUMENT: 9, + DOCUMENT_TYPE: 10, + DOCUMENT_FRAGMENT: 11, + NOTATION: 12 +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/abstractpattern.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/abstractpattern.js new file mode 100644 index 0000000000..6015b09c5e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/abstractpattern.js @@ -0,0 +1,60 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern base class. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.AbstractPattern'); + +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Base pattern class for DOM matching. + * + * @constructor + */ +goog.dom.pattern.AbstractPattern = function() { +}; + + +/** + * The first node matched by this pattern. + * @type {Node} + */ +goog.dom.pattern.AbstractPattern.prototype.matchedNode = null; + + +/** + * Reset any internal state this pattern keeps. + */ +goog.dom.pattern.AbstractPattern.prototype.reset = function() { + // The base implementation does nothing. +}; + + +/** + * Test whether this pattern matches the given token. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} {@code MATCH} if the pattern matches. + */ +goog.dom.pattern.AbstractPattern.prototype.matchToken = function(token, type) { + return goog.dom.pattern.MatchType.NO_MATCH; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/allchildren.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/allchildren.js new file mode 100644 index 0000000000..86f8cdf9e3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/allchildren.js @@ -0,0 +1,75 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match any children of a tag. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.AllChildren'); + +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches any nodes at or below the current tree depth. + * + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + */ +goog.dom.pattern.AllChildren = function() { +}; +goog.inherits(goog.dom.pattern.AllChildren, goog.dom.pattern.AbstractPattern); + + +/** + * Tracks the matcher's depth to detect the end of the tag. + * + * @type {number} + * @private + */ +goog.dom.pattern.AllChildren.prototype.depth_ = 0; + + +/** + * Test whether the given token is on the same level. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} {@code MATCHING} if the token is on the + * same level or deeper and {@code BACKTRACK_MATCH} if not. + * @override + */ +goog.dom.pattern.AllChildren.prototype.matchToken = function(token, type) { + this.depth_ += type; + + if (this.depth_ >= 0) { + return goog.dom.pattern.MatchType.MATCHING; + } else { + this.depth_ = 0; + return goog.dom.pattern.MatchType.BACKTRACK_MATCH; + } +}; + + +/** + * Reset any internal state this pattern keeps. + * @override + */ +goog.dom.pattern.AllChildren.prototype.reset = function() { + this.depth_ = 0; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/callback.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/callback.js new file mode 100644 index 0000000000..7d7aa60c33 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/callback.js @@ -0,0 +1,82 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Useful callback functions for the DOM matcher. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.callback'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.iter'); + + +/** + * Callback function for use in {@link goog.dom.pattern.Matcher.addPattern} + * that removes the matched node from the tree. Should be used in conjunciton + * with a {@link goog.dom.pattern.StartTag} pattern. + * + * @param {Node} node The node matched by the pattern. + * @param {goog.dom.TagIterator} position The position where the match + * finished. + * @return {boolean} Returns true to indicate tree changes were made. + */ +goog.dom.pattern.callback.removeNode = function(node, position) { + // Find out which position would be next. + position.setPosition(node, goog.dom.TagWalkType.END_TAG); + + goog.iter.nextOrValue(position, null); + + // Remove the node. + goog.dom.removeNode(node); + + // Correct for the depth change. + position.depth -= 1; + + // Indicate that we made position/tree changes. + return true; +}; + + +/** + * Callback function for use in {@link goog.dom.pattern.Matcher.addPattern} + * that removes the matched node from the tree and replaces it with its + * children. Should be used in conjunction with a + * {@link goog.dom.pattern.StartTag} pattern. + * + * @param {Element} node The node matched by the pattern. + * @param {goog.dom.TagIterator} position The position where the match + * finished. + * @return {boolean} Returns true to indicate tree changes were made. + */ +goog.dom.pattern.callback.flattenElement = function(node, position) { + // Find out which position would be next. + position.setPosition(node, node.firstChild ? + goog.dom.TagWalkType.START_TAG : + goog.dom.TagWalkType.END_TAG); + + goog.iter.nextOrValue(position, null); + + // Flatten the node. + goog.dom.flattenElement(node); + + // Correct for the depth change. + position.depth -= 1; + + // Indicate that we made position/tree changes. + return true; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/counter.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/counter.js new file mode 100644 index 0000000000..003c71a0ad --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/counter.js @@ -0,0 +1,73 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Callback object that counts matches. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.callback.Counter'); + + + +/** + * Callback class for counting matches. + * @constructor + * @final + */ +goog.dom.pattern.callback.Counter = function() { +}; + + +/** + * The count of objects matched so far. + * + * @type {number} + */ +goog.dom.pattern.callback.Counter.prototype.count = 0; + + +/** + * The callback function. Suitable as a callback for + * {@link goog.dom.pattern.Matcher}. + * @type {Function} + * @private + */ +goog.dom.pattern.callback.Counter.prototype.callback_ = null; + + +/** + * Get a bound callback function that is suitable as a callback for + * {@link goog.dom.pattern.Matcher}. + * + * @return {!Function} A callback function. + */ +goog.dom.pattern.callback.Counter.prototype.getCallback = function() { + if (!this.callback_) { + this.callback_ = goog.bind(function() { + this.count++; + return false; + }, this); + } + return this.callback_; +}; + + +/** + * Reset the counter. + */ +goog.dom.pattern.callback.Counter.prototype.reset = function() { + this.count = 0; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/test.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/test.js new file mode 100644 index 0000000000..1bfc923bcb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/callback/test.js @@ -0,0 +1,78 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Callback object that tests if a pattern matches at least once. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.callback.Test'); + +goog.require('goog.iter.StopIteration'); + + + +/** + * Callback class for testing for at least one match. + * @constructor + * @final + */ +goog.dom.pattern.callback.Test = function() { +}; + + +/** + * Whether or not the pattern matched. + * + * @type {boolean} + */ +goog.dom.pattern.callback.Test.prototype.matched = false; + + +/** + * The callback function. Suitable as a callback for + * {@link goog.dom.pattern.Matcher}. + * @type {Function} + * @private + */ +goog.dom.pattern.callback.Test.prototype.callback_ = null; + + +/** + * Get a bound callback function that is suitable as a callback for + * {@link goog.dom.pattern.Matcher}. + * + * @return {!Function} A callback function. + */ +goog.dom.pattern.callback.Test.prototype.getCallback = function() { + if (!this.callback_) { + this.callback_ = goog.bind(function(node, position) { + // Mark our match. + this.matched = true; + + // Stop searching. + throw goog.iter.StopIteration; + }, this); + } + return this.callback_; +}; + + +/** + * Reset the counter. + */ +goog.dom.pattern.callback.Test.prototype.reset = function() { + this.matched = false; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/childmatches.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/childmatches.js new file mode 100644 index 0000000000..a2d8dc44d1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/childmatches.js @@ -0,0 +1,155 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match any children of a tag, and + * specifically collect those that match a child pattern. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.ChildMatches'); + +goog.require('goog.dom.pattern.AllChildren'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches any nodes at or below the current tree depth. + * + * @param {goog.dom.pattern.AbstractPattern} childPattern Pattern to collect + * child matches of. + * @param {number=} opt_minimumMatches Enforce a minimum nuber of matches. + * Defaults to 0. + * @constructor + * @extends {goog.dom.pattern.AllChildren} + * @final + */ +goog.dom.pattern.ChildMatches = function(childPattern, opt_minimumMatches) { + this.childPattern_ = childPattern; + this.matches = []; + this.minimumMatches_ = opt_minimumMatches || 0; + goog.dom.pattern.AllChildren.call(this); +}; +goog.inherits(goog.dom.pattern.ChildMatches, goog.dom.pattern.AllChildren); + + +/** + * Array of matched child nodes. + * + * @type {Array} + */ +goog.dom.pattern.ChildMatches.prototype.matches; + + +/** + * Minimum number of matches. + * + * @type {number} + * @private + */ +goog.dom.pattern.ChildMatches.prototype.minimumMatches_ = 0; + + +/** + * The child pattern to collect matches from. + * + * @type {goog.dom.pattern.AbstractPattern} + * @private + */ +goog.dom.pattern.ChildMatches.prototype.childPattern_; + + +/** + * Whether the pattern has recently matched or failed to match and will need to + * be reset when starting a new round of matches. + * + * @type {boolean} + * @private + */ +goog.dom.pattern.ChildMatches.prototype.needsReset_ = false; + + +/** + * Test whether the given token is on the same level. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} {@code MATCHING} if the token is on the + * same level or deeper and {@code BACKTRACK_MATCH} if not. + * @override + */ +goog.dom.pattern.ChildMatches.prototype.matchToken = function(token, type) { + // Defer resets so we maintain our matches array until the last possible time. + if (this.needsReset_) { + this.reset(); + } + + // Call the super-method to ensure we stay in the child tree. + var status = + goog.dom.pattern.AllChildren.prototype.matchToken.apply(this, arguments); + + switch (status) { + case goog.dom.pattern.MatchType.MATCHING: + var backtrack = false; + + switch (this.childPattern_.matchToken(token, type)) { + case goog.dom.pattern.MatchType.BACKTRACK_MATCH: + backtrack = true; + case goog.dom.pattern.MatchType.MATCH: + // Collect the match. + this.matches.push(this.childPattern_.matchedNode); + break; + + default: + // Keep trying if we haven't hit a terminal state. + break; + } + + if (backtrack) { + // The only interesting result is a MATCH, since BACKTRACK_MATCH means + // we are hitting an infinite loop on something like a Repeat(0). + if (this.childPattern_.matchToken(token, type) == + goog.dom.pattern.MatchType.MATCH) { + this.matches.push(this.childPattern_.matchedNode); + } + } + return goog.dom.pattern.MatchType.MATCHING; + + case goog.dom.pattern.MatchType.BACKTRACK_MATCH: + // TODO(robbyw): this should return something like BACKTRACK_NO_MATCH + // when we don't meet our minimum. + this.needsReset_ = true; + return (this.matches.length >= this.minimumMatches_) ? + goog.dom.pattern.MatchType.BACKTRACK_MATCH : + goog.dom.pattern.MatchType.NO_MATCH; + + default: + this.needsReset_ = true; + return status; + } +}; + + +/** + * Reset any internal state this pattern keeps. + * @override + */ +goog.dom.pattern.ChildMatches.prototype.reset = function() { + this.needsReset_ = false; + this.matches.length = 0; + this.childPattern_.reset(); + goog.dom.pattern.AllChildren.prototype.reset.call(this); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/endtag.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/endtag.js new file mode 100644 index 0000000000..409f952dec --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/endtag.js @@ -0,0 +1,54 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match the end of a tag. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.EndTag'); + +goog.require('goog.dom.TagWalkType'); +goog.require('goog.dom.pattern.Tag'); + + + +/** + * Pattern object that matches a closing tag. + * + * @param {string|RegExp} tag Name of the tag. Also will accept a regular + * expression to match against the tag name. + * @param {Object=} opt_attrs Optional map of attribute names to desired values. + * This pattern will only match when all attributes are present and match + * the string or regular expression value provided here. + * @param {Object=} opt_styles Optional map of CSS style names to desired + * values. This pattern will only match when all styles are present and + * match the string or regular expression value provided here. + * @param {Function=} opt_test Optional function that takes the element as a + * parameter and returns true if this pattern should match it. + * @constructor + * @extends {goog.dom.pattern.Tag} + * @final + */ +goog.dom.pattern.EndTag = function(tag, opt_attrs, opt_styles, opt_test) { + goog.dom.pattern.Tag.call( + this, + tag, + goog.dom.TagWalkType.END_TAG, + opt_attrs, + opt_styles, + opt_test); +}; +goog.inherits(goog.dom.pattern.EndTag, goog.dom.pattern.Tag); diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/fulltag.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/fulltag.js new file mode 100644 index 0000000000..16264e9ae9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/fulltag.js @@ -0,0 +1,94 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a tag and all of its children. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.FullTag'); + +goog.require('goog.dom.pattern.MatchType'); +goog.require('goog.dom.pattern.StartTag'); +goog.require('goog.dom.pattern.Tag'); + + + +/** + * Pattern object that matches a full tag including all its children. + * + * @param {string|RegExp} tag Name of the tag. Also will accept a regular + * expression to match against the tag name. + * @param {Object=} opt_attrs Optional map of attribute names to desired values. + * This pattern will only match when all attributes are present and match + * the string or regular expression value provided here. + * @param {Object=} opt_styles Optional map of CSS style names to desired + * values. This pattern will only match when all styles are present and + * match the string or regular expression value provided here. + * @param {Function=} opt_test Optional function that takes the element as a + * parameter and returns true if this pattern should match it. + * @constructor + * @extends {goog.dom.pattern.StartTag} + * @final + */ +goog.dom.pattern.FullTag = function(tag, opt_attrs, opt_styles, opt_test) { + goog.dom.pattern.StartTag.call( + this, + tag, + opt_attrs, + opt_styles, + opt_test); +}; +goog.inherits(goog.dom.pattern.FullTag, goog.dom.pattern.StartTag); + + +/** + * Tracks the matcher's depth to detect the end of the tag. + * + * @type {number} + * @private + */ +goog.dom.pattern.FullTag.prototype.depth_ = 0; + + +/** + * Test whether the given token is a start tag token which matches the tag name, + * style, and attributes provided in the constructor. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH at the end of our + * tag, MATCHING if we are within the tag, and + * NO_MATCH if the starting tag does not match. + * @override + */ +goog.dom.pattern.FullTag.prototype.matchToken = function(token, type) { + if (!this.depth_) { + // If we have not yet started, make sure we match as a StartTag. + if (goog.dom.pattern.Tag.prototype.matchToken.call(this, token, type)) { + this.depth_ = type; + return goog.dom.pattern.MatchType.MATCHING; + + } else { + return goog.dom.pattern.MatchType.NO_MATCH; + } + } else { + this.depth_ += type; + + return this.depth_ ? + goog.dom.pattern.MatchType.MATCHING : + goog.dom.pattern.MatchType.MATCH; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher.js new file mode 100644 index 0000000000..fb3cc7d23e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher.js @@ -0,0 +1,151 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern matcher. Allows for simple searching of DOM + * using patterns descended from {@link goog.dom.pattern.AbstractPattern}. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.Matcher'); + +goog.require('goog.dom.TagIterator'); +goog.require('goog.dom.pattern.MatchType'); +goog.require('goog.iter'); + + +// TODO(robbyw): Allow for backtracks of size > 1. + + + +/** + * Given a set of patterns and a root node, this class tests the patterns in + * parallel. + * + * It is not (yet) a smart matcher - it doesn't do any advanced backtracking. + * Given the pattern DIV, SPAN the matcher will not match + * DIV, DIV, SPAN because it starts matching at the first + * DIV, fails to match SPAN at the second, and never + * backtracks to try again. + * + * It is also possible to have a set of complex patterns that when matched in + * parallel will miss some possible matches. Running multiple times will catch + * all matches eventually. + * + * @constructor + * @final + */ +goog.dom.pattern.Matcher = function() { + this.patterns_ = []; + this.callbacks_ = []; +}; + + +/** + * Array of patterns to attempt to match in parallel. + * + * @type {Array} + * @private + */ +goog.dom.pattern.Matcher.prototype.patterns_; + + +/** + * Array of callbacks to call when a pattern is matched. The indexing is the + * same as the {@link #patterns_} array. + * + * @type {Array} + * @private + */ +goog.dom.pattern.Matcher.prototype.callbacks_; + + +/** + * Adds a pattern to be matched. The callback can return an object whose keys + * are processing instructions. + * + * @param {goog.dom.pattern.AbstractPattern} pattern The pattern to add. + * @param {Function} callback Function to call when a match is found. Uses + * the above semantics. + */ +goog.dom.pattern.Matcher.prototype.addPattern = function(pattern, callback) { + this.patterns_.push(pattern); + this.callbacks_.push(callback); +}; + + +/** + * Resets all the patterns. + * + * @private + */ +goog.dom.pattern.Matcher.prototype.reset_ = function() { + for (var i = 0, len = this.patterns_.length; i < len; i++) { + this.patterns_[i].reset(); + } +}; + + +/** + * Test the given node against all patterns. + * + * @param {goog.dom.TagIterator} position A position in a node walk that is + * located at the token to process. + * @return {boolean} Whether a pattern modified the position or tree + * and its callback resulted in DOM structure or position modification. + * @private + */ +goog.dom.pattern.Matcher.prototype.matchToken_ = function(position) { + for (var i = 0, len = this.patterns_.length; i < len; i++) { + var pattern = this.patterns_[i]; + switch (pattern.matchToken(position.node, position.tagType)) { + case goog.dom.pattern.MatchType.MATCH: + case goog.dom.pattern.MatchType.BACKTRACK_MATCH: + var callback = this.callbacks_[i]; + + // Callbacks are allowed to modify the current position, but must + // return true if the do. + if (callback(pattern.matchedNode, position, pattern)) { + return true; + } + + default: + // Do nothing. + break; + } + } + + return false; +}; + + +/** + * Match the set of patterns against a match tree. + * + * @param {Node} node The root node of the tree to match. + */ +goog.dom.pattern.Matcher.prototype.match = function(node) { + var position = new goog.dom.TagIterator(node); + + this.reset_(); + + goog.iter.forEach(position, function() { + while (this.matchToken_(position)) { + // Since we've moved, our old pattern statuses don't make sense any more. + // Reset them. + this.reset_(); + } + }, this); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.html b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.html new file mode 100644 index 0000000000..b5ebdb3ae6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.html @@ -0,0 +1,34 @@ + + + + + + + +goog.dom.pattern.Matcher Tests + + + + +

    + +

    +

    + x +

    +

    Text

    +

    Other Text

    + + +
    xyz
    + +

    xyz

    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.js new file mode 100644 index 0000000000..901f2d28a9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/matcher_test.js @@ -0,0 +1,187 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.pattern.matcherTest'); +goog.setTestOnly('goog.dom.pattern.matcherTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.pattern.EndTag'); +goog.require('goog.dom.pattern.FullTag'); +goog.require('goog.dom.pattern.Matcher'); +goog.require('goog.dom.pattern.Repeat'); +goog.require('goog.dom.pattern.Sequence'); +goog.require('goog.dom.pattern.StartTag'); +goog.require('goog.dom.pattern.callback.Counter'); +goog.require('goog.dom.pattern.callback.Test'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.testing.jsunit'); + +function testMatcherAndStartTag() { + var pattern = new goog.dom.pattern.StartTag('P'); + + var counter = new goog.dom.pattern.callback.Counter(); + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, counter.getCallback()); + matcher.match(document.body); + + assertEquals('StartTag(p) should match 5 times in body', 5, + counter.count); +} + +function testMatcherAndStartTagTwice() { + var pattern = new goog.dom.pattern.StartTag('P'); + + var counter = new goog.dom.pattern.callback.Counter(); + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, counter.getCallback()); + matcher.match(document.body); + + assertEquals('StartTag(p) should match 5 times in body', 5, + counter.count); + + // Make sure no state got mangled. + counter.reset(); + matcher.match(document.body); + + assertEquals('StartTag(p) should match 5 times in body again', 5, + counter.count); +} + +function testMatcherAndStartTagAttributes() { + var pattern = new goog.dom.pattern.StartTag('SPAN', {id: /./}); + + var counter = new goog.dom.pattern.callback.Counter(); + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, counter.getCallback()); + matcher.match(document.body); + + assertEquals('StartTag(span,id) should match 2 times in body', 2, + counter.count); +} + +function testMatcherWithTwoPatterns() { + var pattern1 = new goog.dom.pattern.StartTag('SPAN'); + var pattern2 = new goog.dom.pattern.StartTag('P'); + + var counter = new goog.dom.pattern.callback.Counter(); + + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern1, counter.getCallback()); + matcher.addPattern(pattern2, counter.getCallback()); + + matcher.match(document.body); + + assertEquals('StartTag(span|p) should match 8 times in body', 8, + counter.count); +} + +function testMatcherWithQuit() { + var pattern1 = new goog.dom.pattern.StartTag('SPAN'); + var pattern2 = new goog.dom.pattern.StartTag('P'); + + var count = 0; + var callback = function(node, position) { + if (node.nodeName == 'SPAN') { + throw goog.iter.StopIteration; + return true; + } + count++; + }; + + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern1, callback); + matcher.addPattern(pattern2, callback); + + matcher.match(document.body); + + assertEquals('Stopped span|p should match 1 time in body', 1, count); +} + +function testMatcherWithReplace() { + var pattern1 = new goog.dom.pattern.StartTag('B'); + var pattern2 = new goog.dom.pattern.StartTag('I'); + + var count = 0; + var callback = function(node, position) { + count++; + if (node.nodeName == 'B') { + var i = goog.dom.createDom('I'); + node.parentNode.insertBefore(i, node); + goog.dom.removeNode(node); + + position.setPosition(i); + + return true; + } + }; + + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern1, callback); + matcher.addPattern(pattern2, callback); + + matcher.match(goog.dom.getElement('div1')); + + assertEquals('i|b->i should match 5 times in div1', 5, count); +} + +function testMatcherAndFullTag() { + var pattern = new goog.dom.pattern.FullTag('P'); + + var test = new goog.dom.pattern.callback.Test(); + + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, test.getCallback()); + + matcher.match(goog.dom.getElement('p1')); + + assert('FullTag(p) should match on p1', test.matched); + + test.reset(); + matcher.match(goog.dom.getElement('div1')); + + assert('FullTag(p) should not match on div1', !test.matched); +} + +function testMatcherAndSequence() { + var pattern = new goog.dom.pattern.Sequence([ + new goog.dom.pattern.StartTag('P'), + new goog.dom.pattern.StartTag('SPAN'), + new goog.dom.pattern.EndTag('SPAN'), + new goog.dom.pattern.EndTag('P') + ], true); + + var counter = new goog.dom.pattern.callback.Counter(); + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, counter.getCallback()); + matcher.match(document.body); + + assertEquals('Sequence should match 1 times in body', 1, counter.count); +} + +function testMatcherAndRepeatFullTag() { + var pattern = new goog.dom.pattern.Repeat( + new goog.dom.pattern.FullTag('P'), 1); + + var count = 0; + var tcount = 0; + var matcher = new goog.dom.pattern.Matcher(); + matcher.addPattern(pattern, function() { + count++; + tcount += pattern.count; + }); + matcher.match(document.body); + + assertEquals('Repeated p should match 2 times in body', 2, count); + assertEquals('Repeated p should match 5 total times in body', 5, tcount); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/nodetype.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/nodetype.js new file mode 100644 index 0000000000..a12c9a1cf3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/nodetype.js @@ -0,0 +1,59 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a node of the given type. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.NodeType'); + +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches any node of the given type. + * @param {goog.dom.NodeType} nodeType The node type to match. + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + * @final + */ +goog.dom.pattern.NodeType = function(nodeType) { + /** + * The node type to match. + * @type {goog.dom.NodeType} + * @private + */ + this.nodeType_ = nodeType; +}; +goog.inherits(goog.dom.pattern.NodeType, goog.dom.pattern.AbstractPattern); + + +/** + * Test whether the given token is a text token which matches the string or + * regular expression provided in the constructor. + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH if the pattern + * matches, NO_MATCH otherwise. + * @override + */ +goog.dom.pattern.NodeType.prototype.matchToken = function(token, type) { + return token.nodeType == this.nodeType_ ? + goog.dom.pattern.MatchType.MATCH : + goog.dom.pattern.MatchType.NO_MATCH; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern.js new file mode 100644 index 0000000000..19f4d1b794 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern.js @@ -0,0 +1,93 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM patterns. Allows for description of complex DOM patterns + * using regular expression like constructs. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern'); +goog.provide('goog.dom.pattern.MatchType'); + + +/** + * Regular expression for breaking text nodes. + * @type {RegExp} + */ +goog.dom.pattern.BREAKING_TEXTNODE_RE = /^\s*$/; + + +/** + * Utility function to match a string against either a string or a regular + * expression. + * + * @param {string|RegExp} obj Either a string or a regular expression. + * @param {string} str The string to match. + * @return {boolean} Whether the strings are equal, or if the string matches + * the regular expression. + */ +goog.dom.pattern.matchStringOrRegex = function(obj, str) { + if (goog.isString(obj)) { + // Match a string + return str == obj; + } else { + // Match a regular expression + return !!(str && str.match(obj)); + } +}; + + +/** + * Utility function to match a DOM attribute against either a string or a + * regular expression. Conforms to the interface spec for + * {@link goog.object#every}. + * + * @param {string|RegExp} elem Either a string or a regular expression. + * @param {string} index The attribute name to match. + * @param {Object} orig The original map of matches to test. + * @return {boolean} Whether the strings are equal, or if the attribute matches + * the regular expression. + * @this {Element} Called using goog.object every on an Element. + */ +goog.dom.pattern.matchStringOrRegexMap = function(elem, index, orig) { + return goog.dom.pattern.matchStringOrRegex(elem, + index in this ? this[index] : + (this.getAttribute ? this.getAttribute(index) : null)); +}; + + +/** + * When matched to a token, a pattern may return any of the following statuses: + *
      + *
    1. NO_MATCH - The pattern does not match. This is the only + * value that evaluates to false in a boolean context. + *
    2. MATCHING - The token is part of an incomplete match. + *
    3. MATCH - The token completes a match. + *
    4. BACKTRACK_MATCH - The token does not match, but indicates + * the end of a repetitive match. For instance, in regular expressions, + * the pattern /a+/ would match 'aaaaaaaab'. + * Every 'a' token would give a status of + * MATCHING while the 'b' token would give a + * status of BACKTRACK_MATCH. + *
    + * @enum {number} + */ +goog.dom.pattern.MatchType = { + NO_MATCH: 0, + MATCHING: 1, + MATCH: 2, + BACKTRACK_MATCH: 3 +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.html b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.html new file mode 100644 index 0000000000..7a30c067f1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.html @@ -0,0 +1,39 @@ + + + + + + + +goog.dom.pattern Tests + + + + +
    + +
    +
    + x +
    +
    Text
    +
    Other Text
    + + + +

    xyz

    + +
    xyz
    + + X + +
    Text
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.js new file mode 100644 index 0000000000..dfa7e3a12a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/pattern_test.js @@ -0,0 +1,592 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.patternTest'); +goog.setTestOnly('goog.dom.patternTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.dom.pattern.AllChildren'); +goog.require('goog.dom.pattern.ChildMatches'); +goog.require('goog.dom.pattern.EndTag'); +goog.require('goog.dom.pattern.FullTag'); +goog.require('goog.dom.pattern.MatchType'); +goog.require('goog.dom.pattern.NodeType'); +goog.require('goog.dom.pattern.Repeat'); +goog.require('goog.dom.pattern.Sequence'); +goog.require('goog.dom.pattern.StartTag'); +goog.require('goog.dom.pattern.Text'); +goog.require('goog.testing.jsunit'); + +// TODO(robbyw): write a test that checks if backtracking works in Sequence + +function testStartTag() { + var pattern = new goog.dom.pattern.StartTag('DIV'); + assertEquals( + 'StartTag(div) should match div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(div) should not match span', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(div) should not match /div', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); +} + +function testStartTagCase() { + var pattern = new goog.dom.pattern.StartTag('diV'); + assertEquals( + 'StartTag(diV) should match div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(diV) should not match span', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); +} + +function testStartTagRegex() { + var pattern = new goog.dom.pattern.StartTag(/D/); + assertEquals( + 'StartTag(/D/) should match div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(/D/) should not match span', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(/D/) should not match /div', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); +} + +function testStartTagAttributes() { + var pattern = new goog.dom.pattern.StartTag('DIV', {id: 'div1'}); + assertEquals( + 'StartTag(div,id:div1) should match div1', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals('StartTag(div,id:div2) should not match div1', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div2'), + goog.dom.TagWalkType.START_TAG)); +} + +function testStartTagStyle() { + var pattern = new goog.dom.pattern.StartTag('SPAN', null, {color: 'red'}); + assertEquals( + 'StartTag(span,null,color:red) should match span1', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(span,null,color:blue) should not match span1', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('span2'), + goog.dom.TagWalkType.START_TAG)); +} + +function testStartTagAttributeRegex() { + var pattern = new goog.dom.pattern.StartTag('SPAN', {id: /span\d/}); + assertEquals( + 'StartTag(span,id:/span\\d/) should match span1', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'StartTag(span,id:/span\\d/) should match span2', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); +} + +function testEndTag() { + var pattern = new goog.dom.pattern.EndTag('DIV'); + assertEquals( + 'EndTag should match div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); +} + +function testEndTagRegex() { + var pattern = new goog.dom.pattern.EndTag(/D/); + assertEquals( + 'EndTag(/D/) should match /div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'EndTag(/D/) should not match /span', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'EndTag(/D/) should not match div', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); +} + +function testChildMatches() { + var pattern = new goog.dom.pattern.ChildMatches( + new goog.dom.pattern.StartTag('DIV'), 2); + + assertEquals( + 'ChildMatches should match div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'ChildMatches should match /div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'ChildMatches should match div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div2'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'ChildMatches should match /div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div2'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'ChildMatches should finish match at /body', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + document.body, + goog.dom.TagWalkType.END_TAG)); + + assertEquals( + 'ChildMatches should match div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div2'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'ChildMatches should match /div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div2'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'ChildMatches should fail to match at /body: not enough child matches', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + document.body, + goog.dom.TagWalkType.END_TAG)); +} + +function testFullTag() { + var pattern = new goog.dom.pattern.FullTag('DIV'); + assertEquals( + 'FullTag(div) should match div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'FullTag(div) should match /div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + + assertEquals( + 'FullTag(div) should start match at div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'FullTag(div) should continue to match span', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'FullTag(div) should continue to match /span', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'FullTag(div) should finish match at /div', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); +} + +function testAllChildren() { + var pattern = new goog.dom.pattern.AllChildren(); + assertEquals( + 'AllChildren(div) should match div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'AllChildren(div) should match /div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'AllChildren(div) should match at /body', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + document.body, + goog.dom.TagWalkType.END_TAG)); + + assertEquals( + 'AllChildren(div) should start match at div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'AllChildren(div) should continue to match span', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'AllChildren(div) should continue to match /span', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'AllChildren(div) should continue to match at /div', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'AllChildren(div) should finish match at /body', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + document.body, + goog.dom.TagWalkType.END_TAG)); +} + +function testText() { + var pattern = new goog.dom.pattern.Text('Text'); + assertEquals( + 'Text should match div3/text()', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div3').firstChild, + goog.dom.TagWalkType.OTHER)); + assertEquals( + 'Text should not match div4/text()', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div4').firstChild, + goog.dom.TagWalkType.OTHER)); + assertEquals( + 'Text should not match div3', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div3'), + goog.dom.TagWalkType.START_TAG)); + +} + +function testTextRegex() { + var pattern = new goog.dom.pattern.Text(/Text/); + assertEquals( + 'Text(regex) should match div3/text()', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div3').firstChild, + goog.dom.TagWalkType.OTHER)); + assertEquals( + 'Text(regex) should match div4/text()', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div4').firstChild, + goog.dom.TagWalkType.OTHER)); +} + +function testNodeType() { + var pattern = new goog.dom.pattern.NodeType(goog.dom.NodeType.COMMENT); + assertEquals('Comment matcher should match a comment', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('nodeTypes').firstChild, + goog.dom.TagWalkType.OTHER)); + assertEquals('Comment matcher should not match a text node', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('nodeTypes').lastChild, + goog.dom.TagWalkType.OTHER)); +} + +function testSequence() { + var pattern = new goog.dom.pattern.Sequence([ + new goog.dom.pattern.StartTag('DIV'), + new goog.dom.pattern.StartTag('SPAN'), + new goog.dom.pattern.EndTag('SPAN'), + new goog.dom.pattern.EndTag('DIV')]); + + assertEquals( + 'Sequence[0] should match div1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[1] should match span1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[2] should match /span1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'Sequence[3] should match /div1', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + + assertEquals( + 'Sequence[0] should match div1 again', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[1] should match span1 again', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[2] should match /span1 again', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'Sequence[3] should match /div1 again', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); + + assertEquals( + 'Sequence[0] should match div1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[1] should not match div1', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + + assertEquals( + 'Sequence[0] should match div1 after failure', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[1] should match span1 after failure', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[2] should match /span1 after failure', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('span1'), + goog.dom.TagWalkType.END_TAG)); + assertEquals( + 'Sequence[3] should match /div1 after failure', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('div1'), + goog.dom.TagWalkType.END_TAG)); +} + +function testRepeat() { + var pattern = new goog.dom.pattern.Repeat( + new goog.dom.pattern.StartTag('B')); + + // Note: this test does not mimic an actual matcher because it is only + // passing the START_TAG events. + + assertEquals( + 'Repeat[B] should match b1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('b1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B] should match b2', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('b2'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B] should backtrack match i1', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + goog.dom.getElement('i1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B] should have match count of 2', + 2, + pattern.count); + + assertEquals( + 'Repeat[B] should backtrack match i1 even with no b matches', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + goog.dom.getElement('i1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B] should have match count of 0', + 0, + pattern.count); +} + +function testRepeatWithMinimum() { + var pattern = new goog.dom.pattern.Repeat( + new goog.dom.pattern.StartTag('B'), 1); + + // Note: this test does not mimic an actual matcher because it is only + // passing the START_TAG events. + + assertEquals( + 'Repeat[B,1] should match b1', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('b1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B,1] should match b2', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken( + goog.dom.getElement('b2'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B,1] should backtrack match i1', + goog.dom.pattern.MatchType.BACKTRACK_MATCH, + pattern.matchToken( + goog.dom.getElement('i1'), + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Repeat[B,1] should have match count of 2', + 2, + pattern.count); + + assertEquals( + 'Repeat[B,1] should not match i1', + goog.dom.pattern.MatchType.NO_MATCH, + pattern.matchToken( + goog.dom.getElement('i1'), + goog.dom.TagWalkType.START_TAG)); +} + +function testRepeatWithMaximum() { + var pattern = new goog.dom.pattern.Repeat( + new goog.dom.pattern.StartTag('B'), 1, 1); + + // Note: this test does not mimic an actual matcher because it is only + // passing the START_TAG events. + + assertEquals( + 'Repeat[B,1] should match b1', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken( + goog.dom.getElement('b1'), + goog.dom.TagWalkType.START_TAG)); +} + +function testSequenceBacktrack() { + var pattern = new goog.dom.pattern.Sequence([ + new goog.dom.pattern.Repeat(new goog.dom.pattern.StartTag('SPAN')), + new goog.dom.pattern.Text('X')]); + + var root = goog.dom.getElement('span3'); + assertEquals( + 'Sequence[Repeat[SPAN],"X"] should match span3', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken(root, goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[Repeat[SPAN],"X"] should match span3.firstChild', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken(root.firstChild, + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[Repeat[SPAN],"X"] should match span3.firstChild.firstChild', + goog.dom.pattern.MatchType.MATCHING, + pattern.matchToken(root.firstChild.firstChild, + goog.dom.TagWalkType.START_TAG)); + assertEquals( + 'Sequence[Repeat[SPAN],"X"] should finish match text node', + goog.dom.pattern.MatchType.MATCH, + pattern.matchToken(root.firstChild.firstChild.firstChild, + goog.dom.TagWalkType.OTHER)); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/repeat.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/repeat.js new file mode 100644 index 0000000000..6872ca198c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/repeat.js @@ -0,0 +1,191 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a tag and all of its children. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.Repeat'); + +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches a repetition of another pattern. + * @param {goog.dom.pattern.AbstractPattern} pattern The pattern to + * repetitively match. + * @param {number=} opt_minimum The minimum number of times to match. Defaults + * to 0. + * @param {number=} opt_maximum The maximum number of times to match. Defaults + * to unlimited. + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + * @final + */ +goog.dom.pattern.Repeat = function(pattern, + opt_minimum, + opt_maximum) { + this.pattern_ = pattern; + this.minimum_ = opt_minimum || 0; + this.maximum_ = opt_maximum || null; + this.matches = []; +}; +goog.inherits(goog.dom.pattern.Repeat, goog.dom.pattern.AbstractPattern); + + +/** + * Pattern to repetitively match. + * + * @type {goog.dom.pattern.AbstractPattern} + * @private + */ +goog.dom.pattern.Repeat.prototype.pattern_; + + +/** + * Minimum number of times to match the pattern. + * + * @private + */ +goog.dom.pattern.Repeat.prototype.minimum_ = 0; + + +/** + * Optional maximum number of times to match the pattern. A {@code null} value + * will be treated as infinity. + * + * @type {?number} + * @private + */ +goog.dom.pattern.Repeat.prototype.maximum_ = 0; + + +/** + * Number of times the pattern has matched. + * + * @type {number} + */ +goog.dom.pattern.Repeat.prototype.count = 0; + + +/** + * Whether the pattern has recently matched or failed to match and will need to + * be reset when starting a new round of matches. + * + * @type {boolean} + * @private + */ +goog.dom.pattern.Repeat.prototype.needsReset_ = false; + + +/** + * The matched nodes. + * + * @type {Array} + */ +goog.dom.pattern.Repeat.prototype.matches; + + +/** + * Test whether the given token continues a repeated series of matches of the + * pattern given in the constructor. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH if the pattern + * matches, BACKTRACK_MATCH if the pattern does not match + * but already had accumulated matches, MATCHING if the pattern + * starts a match, and NO_MATCH if the pattern does not match. + * @suppress {missingProperties} See the broken line below. + * @override + */ +goog.dom.pattern.Repeat.prototype.matchToken = function(token, type) { + // Reset if we're starting a new match + if (this.needsReset_) { + this.reset(); + } + + // If the option is set, ignore any whitespace only text nodes + if (token.nodeType == goog.dom.NodeType.TEXT && + token.nodeValue.match(/^\s+$/)) { + return goog.dom.pattern.MatchType.MATCHING; + } + + switch (this.pattern_.matchToken(token, type)) { + case goog.dom.pattern.MatchType.MATCH: + // Record the first token we match. + if (this.count == 0) { + this.matchedNode = token; + } + + // Mark the match + this.count++; + + // Add to the list + this.matches.push(this.pattern_.matchedNode); + + // Check if this match hits our maximum + if (this.maximum_ !== null && this.count == this.maximum_) { + this.needsReset_ = true; + return goog.dom.pattern.MatchType.MATCH; + } else { + return goog.dom.pattern.MatchType.MATCHING; + } + + case goog.dom.pattern.MatchType.MATCHING: + // This can happen when our child pattern is a sequence or a repetition. + return goog.dom.pattern.MatchType.MATCHING; + + case goog.dom.pattern.MatchType.BACKTRACK_MATCH: + // This happens if our child pattern is repetitive too. + // TODO(robbyw): Backtrack further if necessary. + this.count++; + + // NOTE(nicksantos): This line of code is broken. this.patterns_ doesn't + // exist, and this.currentPosition_ doesn't exit. When this is fixed, + // remove the missingProperties suppression above. + if (this.currentPosition_ == this.patterns_.length) { + this.needsReset_ = true; + return goog.dom.pattern.MatchType.BACKTRACK_MATCH; + } else { + // Retry the same token on the next iteration of the child pattern. + return this.matchToken(token, type); + } + + default: + this.needsReset_ = true; + if (this.count >= this.minimum_) { + return goog.dom.pattern.MatchType.BACKTRACK_MATCH; + } else { + return goog.dom.pattern.MatchType.NO_MATCH; + } + } +}; + + +/** + * Reset any internal state this pattern keeps. + * @override + */ +goog.dom.pattern.Repeat.prototype.reset = function() { + this.pattern_.reset(); + this.count = 0; + this.needsReset_ = false; + this.matches.length = 0; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/sequence.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/sequence.js new file mode 100644 index 0000000000..8fa0314d97 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/sequence.js @@ -0,0 +1,143 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a sequence of other patterns. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.Sequence'); + +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.pattern'); +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches a sequence of other patterns. + * + * @param {Array} patterns Ordered array of + * patterns to match. + * @param {boolean=} opt_ignoreWhitespace Optional flag to ignore text nodes + * consisting entirely of whitespace. The default is to not ignore them. + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + * @final + */ +goog.dom.pattern.Sequence = function(patterns, opt_ignoreWhitespace) { + this.patterns = patterns; + this.ignoreWhitespace_ = !!opt_ignoreWhitespace; +}; +goog.inherits(goog.dom.pattern.Sequence, goog.dom.pattern.AbstractPattern); + + +/** + * Ordered array of patterns to match. + * + * @type {Array} + */ +goog.dom.pattern.Sequence.prototype.patterns; + + +/** + * Position in the patterns array we have reached by successful matches. + * + * @type {number} + * @private + */ +goog.dom.pattern.Sequence.prototype.currentPosition_ = 0; + + +/** + * Whether or not to ignore whitespace only Text nodes. + * + * @type {boolean} + * @private + */ +goog.dom.pattern.Sequence.prototype.ignoreWhitespace_ = false; + + +/** + * Test whether the given token starts, continues, or finishes the sequence + * of patterns given in the constructor. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH if the pattern + * matches, MATCHING if the pattern starts a match, and + * NO_MATCH if the pattern does not match. + * @override + */ +goog.dom.pattern.Sequence.prototype.matchToken = function(token, type) { + // If the option is set, ignore any whitespace only text nodes + if (this.ignoreWhitespace_ && token.nodeType == goog.dom.NodeType.TEXT && + goog.dom.pattern.BREAKING_TEXTNODE_RE.test(token.nodeValue)) { + return goog.dom.pattern.MatchType.MATCHING; + } + + switch (this.patterns[this.currentPosition_].matchToken(token, type)) { + case goog.dom.pattern.MatchType.MATCH: + // Record the first token we match. + if (this.currentPosition_ == 0) { + this.matchedNode = token; + } + + // Move forward one position. + this.currentPosition_++; + + // Check if this is the last position. + if (this.currentPosition_ == this.patterns.length) { + this.reset(); + return goog.dom.pattern.MatchType.MATCH; + } else { + return goog.dom.pattern.MatchType.MATCHING; + } + + case goog.dom.pattern.MatchType.MATCHING: + // This can happen when our child pattern is a sequence or a repetition. + return goog.dom.pattern.MatchType.MATCHING; + + case goog.dom.pattern.MatchType.BACKTRACK_MATCH: + // This means a repetitive match succeeded 1 token ago. + // TODO(robbyw): Backtrack further if necessary. + this.currentPosition_++; + + if (this.currentPosition_ == this.patterns.length) { + this.reset(); + return goog.dom.pattern.MatchType.BACKTRACK_MATCH; + } else { + // Retry the same token on the next pattern. + return this.matchToken(token, type); + } + + default: + this.reset(); + return goog.dom.pattern.MatchType.NO_MATCH; + } +}; + + +/** + * Reset any internal state this pattern keeps. + * @override + */ +goog.dom.pattern.Sequence.prototype.reset = function() { + if (this.patterns[this.currentPosition_]) { + this.patterns[this.currentPosition_].reset(); + } + this.currentPosition_ = 0; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/starttag.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/starttag.js new file mode 100644 index 0000000000..4ce01135ea --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/starttag.js @@ -0,0 +1,53 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match the start of a tag. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.StartTag'); + +goog.require('goog.dom.TagWalkType'); +goog.require('goog.dom.pattern.Tag'); + + + +/** + * Pattern object that matches an opening tag. + * + * @param {string|RegExp} tag Name of the tag. Also will accept a regular + * expression to match against the tag name. + * @param {Object=} opt_attrs Optional map of attribute names to desired values. + * This pattern will only match when all attributes are present and match + * the string or regular expression value provided here. + * @param {Object=} opt_styles Optional map of CSS style names to desired + * values. This pattern will only match when all styles are present and + * match the string or regular expression value provided here. + * @param {Function=} opt_test Optional function that takes the element as a + * parameter and returns true if this pattern should match it. + * @constructor + * @extends {goog.dom.pattern.Tag} + */ +goog.dom.pattern.StartTag = function(tag, opt_attrs, opt_styles, opt_test) { + goog.dom.pattern.Tag.call( + this, + tag, + goog.dom.TagWalkType.START_TAG, + opt_attrs, + opt_styles, + opt_test); +}; +goog.inherits(goog.dom.pattern.StartTag, goog.dom.pattern.Tag); diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/tag.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/tag.js new file mode 100644 index 0000000000..d04ccd3a3f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/tag.js @@ -0,0 +1,150 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a tag. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.Tag'); + +goog.require('goog.dom.pattern'); +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); +goog.require('goog.object'); + + + +/** + * Pattern object that matches an tag. + * + * @param {string|RegExp} tag Name of the tag. Also will accept a regular + * expression to match against the tag name. + * @param {goog.dom.TagWalkType} type Type of token to match. + * @param {Object=} opt_attrs Optional map of attribute names to desired values. + * This pattern will only match when all attributes are present and match + * the string or regular expression value provided here. + * @param {Object=} opt_styles Optional map of CSS style names to desired + * values. This pattern will only match when all styles are present and + * match the string or regular expression value provided here. + * @param {Function=} opt_test Optional function that takes the element as a + * parameter and returns true if this pattern should match it. + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + */ +goog.dom.pattern.Tag = function(tag, type, opt_attrs, opt_styles, opt_test) { + if (goog.isString(tag)) { + this.tag_ = tag.toUpperCase(); + } else { + this.tag_ = tag; + } + + this.type_ = type; + + this.attrs_ = opt_attrs || null; + this.styles_ = opt_styles || null; + this.test_ = opt_test || null; +}; +goog.inherits(goog.dom.pattern.Tag, goog.dom.pattern.AbstractPattern); + + +/** + * The tag to match. + * + * @type {string|RegExp} + * @private + */ +goog.dom.pattern.Tag.prototype.tag_; + + +/** + * The type of token to match. + * + * @type {goog.dom.TagWalkType} + * @private + */ +goog.dom.pattern.Tag.prototype.type_; + + +/** + * The attributes to test for. + * + * @type {Object} + * @private + */ +goog.dom.pattern.Tag.prototype.attrs_ = null; + + +/** + * The styles to test for. + * + * @type {Object} + * @private + */ +goog.dom.pattern.Tag.prototype.styles_ = null; + + +/** + * Function that takes the element as a parameter and returns true if this + * pattern should match it. + * + * @type {Function} + * @private + */ +goog.dom.pattern.Tag.prototype.test_ = null; + + +/** + * Test whether the given token is a tag token which matches the tag name, + * style, and attributes provided in the constructor. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH if the pattern + * matches, NO_MATCH otherwise. + * @override + */ +goog.dom.pattern.Tag.prototype.matchToken = function(token, type) { + // Check the direction and tag name. + if (type == this.type_ && + goog.dom.pattern.matchStringOrRegex(this.tag_, token.nodeName)) { + // Check the attributes. + if (this.attrs_ && + !goog.object.every( + this.attrs_, + goog.dom.pattern.matchStringOrRegexMap, + token)) { + return goog.dom.pattern.MatchType.NO_MATCH; + } + // Check the styles. + if (this.styles_ && + !goog.object.every( + this.styles_, + goog.dom.pattern.matchStringOrRegexMap, + token.style)) { + return goog.dom.pattern.MatchType.NO_MATCH; + } + + if (this.test_ && !this.test_(token)) { + return goog.dom.pattern.MatchType.NO_MATCH; + } + + // If we reach this point, we have a match and should save it. + this.matchedNode = token; + return goog.dom.pattern.MatchType.MATCH; + } + + return goog.dom.pattern.MatchType.NO_MATCH; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/pattern/text.js b/mapbox-gl/closure-library/closure/goog/dom/pattern/text.js new file mode 100644 index 0000000000..c4519608c5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/pattern/text.js @@ -0,0 +1,71 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview DOM pattern to match a text node. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.pattern.Text'); + +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.pattern'); +goog.require('goog.dom.pattern.AbstractPattern'); +goog.require('goog.dom.pattern.MatchType'); + + + +/** + * Pattern object that matches text by exact matching or regular expressions. + * + * @param {string|RegExp} match String or regular expression to match against. + * @constructor + * @extends {goog.dom.pattern.AbstractPattern} + * @final + */ +goog.dom.pattern.Text = function(match) { + this.match_ = match; +}; +goog.inherits(goog.dom.pattern.Text, goog.dom.pattern.AbstractPattern); + + +/** + * The text or regular expression to match. + * + * @type {string|RegExp} + * @private + */ +goog.dom.pattern.Text.prototype.match_; + + +/** + * Test whether the given token is a text token which matches the string or + * regular expression provided in the constructor. + * + * @param {Node} token Token to match against. + * @param {goog.dom.TagWalkType} type The type of token. + * @return {goog.dom.pattern.MatchType} MATCH if the pattern + * matches, NO_MATCH otherwise. + * @override + */ +goog.dom.pattern.Text.prototype.matchToken = function(token, type) { + if (token.nodeType == goog.dom.NodeType.TEXT && + goog.dom.pattern.matchStringOrRegex(this.match_, token.nodeValue)) { + this.matchedNode = token; + return goog.dom.pattern.MatchType.MATCH; + } + + return goog.dom.pattern.MatchType.NO_MATCH; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/range.js b/mapbox-gl/closure-library/closure/goog/dom/range.js new file mode 100644 index 0000000000..f71374cfc5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/range.js @@ -0,0 +1,227 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with ranges in HTML documents. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.Range'); + +goog.require('goog.dom'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.BrowserFeature'); +goog.require('goog.dom.ControlRange'); +goog.require('goog.dom.MultiRange'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TextRange'); +goog.require('goog.userAgent'); + + +/** + * Create a new selection from the given browser window's current selection. + * Note that this object does not auto-update if the user changes their + * selection and should be used as a snapshot. + * @param {Window=} opt_win The window to get the selection of. Defaults to the + * window this class was defined in. + * @return {goog.dom.AbstractRange?} A range wrapper object, or null if there + * was an error. + */ +goog.dom.Range.createFromWindow = function(opt_win) { + var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow( + opt_win || window); + return sel && goog.dom.Range.createFromBrowserSelection(sel); +}; + + +/** + * Create a new range wrapper from the given browser selection object. Note + * that this object does not auto-update if the user changes their selection and + * should be used as a snapshot. + * @param {!Object} selection The browser selection object. + * @return {goog.dom.AbstractRange?} A range wrapper object or null if there + * was an error. + */ +goog.dom.Range.createFromBrowserSelection = function(selection) { + var range; + var isReversed = false; + if (selection.createRange) { + /** @preserveTry */ + try { + range = selection.createRange(); + } catch (e) { + // Access denied errors can be thrown here in IE if the selection was + // a flash obj or if there are cross domain issues + return null; + } + } else if (selection.rangeCount) { + if (selection.rangeCount > 1) { + return goog.dom.MultiRange.createFromBrowserSelection( + /** @type {!Selection} */ (selection)); + } else { + range = selection.getRangeAt(0); + isReversed = goog.dom.Range.isReversed(selection.anchorNode, + selection.anchorOffset, selection.focusNode, selection.focusOffset); + } + } else { + return null; + } + + return goog.dom.Range.createFromBrowserRange(range, isReversed); +}; + + +/** + * Create a new range wrapper from the given browser range object. + * @param {Range|TextRange} range The browser range object. + * @param {boolean=} opt_isReversed Whether the focus node is before the anchor + * node. + * @return {!goog.dom.AbstractRange} A range wrapper object. + */ +goog.dom.Range.createFromBrowserRange = function(range, opt_isReversed) { + // Create an IE control range when appropriate. + return goog.dom.AbstractRange.isNativeControlRange(range) ? + goog.dom.ControlRange.createFromBrowserRange(range) : + goog.dom.TextRange.createFromBrowserRange(range, opt_isReversed); +}; + + +/** + * Create a new range wrapper that selects the given node's text. + * @param {Node} node The node to select. + * @param {boolean=} opt_isReversed Whether the focus node is before the anchor + * node. + * @return {!goog.dom.AbstractRange} A range wrapper object. + */ +goog.dom.Range.createFromNodeContents = function(node, opt_isReversed) { + return goog.dom.TextRange.createFromNodeContents(node, opt_isReversed); +}; + + +/** + * Create a new range wrapper that represents a caret at the given node, + * accounting for the given offset. This always creates a TextRange, regardless + * of whether node is an image node or other control range type node. + * @param {Node} node The node to place a caret at. + * @param {number} offset The offset within the node to place the caret at. + * @return {!goog.dom.AbstractRange} A range wrapper object. + */ +goog.dom.Range.createCaret = function(node, offset) { + return goog.dom.TextRange.createFromNodes(node, offset, node, offset); +}; + + +/** + * Create a new range wrapper that selects the area between the given nodes, + * accounting for the given offsets. + * @param {Node} anchorNode The node to anchor on. + * @param {number} anchorOffset The offset within the node to anchor on. + * @param {Node} focusNode The node to focus on. + * @param {number} focusOffset The offset within the node to focus on. + * @return {!goog.dom.AbstractRange} A range wrapper object. + */ +goog.dom.Range.createFromNodes = function(anchorNode, anchorOffset, focusNode, + focusOffset) { + return goog.dom.TextRange.createFromNodes(anchorNode, anchorOffset, focusNode, + focusOffset); +}; + + +/** + * Clears the window's selection. + * @param {Window=} opt_win The window to get the selection of. Defaults to the + * window this class was defined in. + */ +goog.dom.Range.clearSelection = function(opt_win) { + var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow( + opt_win || window); + if (!sel) { + return; + } + if (sel.empty) { + // We can't just check that the selection is empty, becuase IE + // sometimes gets confused. + try { + sel.empty(); + } catch (e) { + // Emptying an already empty selection throws an exception in IE + } + } else { + try { + sel.removeAllRanges(); + } catch (e) { + // This throws in IE9 if the range has been invalidated; for example, if + // the user clicked on an element which disappeared during the event + // handler. + } + } +}; + + +/** + * Tests if the window has a selection. + * @param {Window=} opt_win The window to check the selection of. Defaults to + * the window this class was defined in. + * @return {boolean} Whether the window has a selection. + */ +goog.dom.Range.hasSelection = function(opt_win) { + var sel = goog.dom.AbstractRange.getBrowserSelectionForWindow( + opt_win || window); + return !!sel && + (goog.dom.BrowserFeature.LEGACY_IE_RANGES ? + sel.type != 'None' : !!sel.rangeCount); +}; + + +/** + * Returns whether the focus position occurs before the anchor position. + * @param {Node} anchorNode The node to anchor on. + * @param {number} anchorOffset The offset within the node to anchor on. + * @param {Node} focusNode The node to focus on. + * @param {number} focusOffset The offset within the node to focus on. + * @return {boolean} Whether the focus position occurs before the anchor + * position. + */ +goog.dom.Range.isReversed = function(anchorNode, anchorOffset, focusNode, + focusOffset) { + if (anchorNode == focusNode) { + return focusOffset < anchorOffset; + } + var child; + if (anchorNode.nodeType == goog.dom.NodeType.ELEMENT && anchorOffset) { + child = anchorNode.childNodes[anchorOffset]; + if (child) { + anchorNode = child; + anchorOffset = 0; + } else if (goog.dom.contains(anchorNode, focusNode)) { + // If focus node is contained in anchorNode, it must be before the + // end of the node. Hence we are reversed. + return true; + } + } + if (focusNode.nodeType == goog.dom.NodeType.ELEMENT && focusOffset) { + child = focusNode.childNodes[focusOffset]; + if (child) { + focusNode = child; + focusOffset = 0; + } else if (goog.dom.contains(focusNode, anchorNode)) { + // If anchor node is contained in focusNode, it must be before the + // end of the node. Hence we are not reversed. + return false; + } + } + return (goog.dom.compareNodeOrder(anchorNode, focusNode) || + anchorOffset - focusOffset) > 0; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/range_test.html b/mapbox-gl/closure-library/closure/goog/dom/range_test.html new file mode 100644 index 0000000000..2612a6e68e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/range_test.html @@ -0,0 +1,44 @@ + + + + + +Closure Unit Tests - goog.dom.Range + + + + +
    Text
    +
    abc
    def
    +
    +
    +
    Text that
    will be deleted
    +
    +
    +
    +
    012345
    +
    12
    +
    • 1
    • 2
    +
    1. 1
    2. 2
    + +
    Will be removed
    + +
    +
    hello world !
    +
    +
    abcd
    e
    +

    abcde
    +
    + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/range_test.js b/mapbox-gl/closure-library/closure/goog/dom/range_test.js new file mode 100644 index 0000000000..68f9df7dbe --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/range_test.js @@ -0,0 +1,722 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.RangeTest'); +goog.setTestOnly('goog.dom.RangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.TextRange'); +goog.require('goog.dom.browserrange'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var assertRangeEquals = goog.testing.dom.assertRangeEquals; + +function setUp() { + // Reset the focus; some tests may invalidate the focus to exercise various + // browser bugs. + var focusableElement = goog.dom.getElement('focusableElement'); + focusableElement.focus(); + focusableElement.blur(); +} + +function normalizeHtml(str) { + return str.toLowerCase().replace(/[\n\r\f"]/g, '') + .replace(/<\/li>/g, ''); // " for emacs +} + +function testCreate() { + assertNotNull('Browser range object can be created for node', + goog.dom.Range.createFromNodeContents(goog.dom.getElement('test1'))); +} + +function testTableRange() { + var tr = goog.dom.getElement('cell').parentNode; + var range = goog.dom.Range.createFromNodeContents(tr); + assertEquals('Selection should have correct text', '12', + range.getText()); + assertEquals('Selection should have correct html fragment', + '12', normalizeHtml(range.getHtmlFragment())); + + // TODO(robbyw): On IE the TR is included, on FF it is not. + //assertEquals('Selection should have correct valid html', + // '12', + // normalizeHtml(range.getValidHtml())); + + assertEquals('Selection should have correct pastable html', + '
    12
    ', + normalizeHtml(range.getPastableHtml())); +} + +function testUnorderedListRange() { + var ul = goog.dom.getElement('ulTest').firstChild; + var range = goog.dom.Range.createFromNodeContents(ul); + assertEquals('Selection should have correct html fragment', + '1
  • 2', normalizeHtml(range.getHtmlFragment())); + + // TODO(robbyw): On IE the UL is included, on FF it is not. + //assertEquals('Selection should have correct valid html', + // '
  • 1
  • 2
  • ', normalizeHtml(range.getValidHtml())); + + assertEquals('Selection should have correct pastable html', + '
    • 1
    • 2
    ', + normalizeHtml(range.getPastableHtml())); +} + +function testOrderedListRange() { + var ol = goog.dom.getElement('olTest').firstChild; + var range = goog.dom.Range.createFromNodeContents(ol); + assertEquals('Selection should have correct html fragment', + '1
  • 2', normalizeHtml(range.getHtmlFragment())); + + // TODO(robbyw): On IE the OL is included, on FF it is not. + //assertEquals('Selection should have correct valid html', + // '
  • 1
  • 2
  • ', normalizeHtml(range.getValidHtml())); + + assertEquals('Selection should have correct pastable html', + '
    1. 1
    2. 2
    ', + normalizeHtml(range.getPastableHtml())); +} + +function testCreateFromNodes() { + var start = goog.dom.getElement('test1').firstChild; + var end = goog.dom.getElement('br'); + var range = goog.dom.Range.createFromNodes(start, 2, end, 0); + assertNotNull('Browser range object can be created for W3C node range', + range); + + assertEquals('Start node should be selected at start endpoint', start, + range.getStartNode()); + assertEquals('Selection should start at offset 2', 2, + range.getStartOffset()); + assertEquals('Start node should be selected at anchor endpoint', start, + range.getAnchorNode()); + assertEquals('Selection should be anchored at offset 2', 2, + range.getAnchorOffset()); + + var div = goog.dom.getElement('test2'); + assertEquals('DIV node should be selected at end endpoint', div, + range.getEndNode()); + assertEquals('Selection should end at offset 1', 1, range.getEndOffset()); + assertEquals('DIV node should be selected at focus endpoint', div, + range.getFocusNode()); + assertEquals('Selection should be focused at offset 1', 1, + range.getFocusOffset()); + + + assertTrue('Text content should be "xt\\s*abc"', + /xt\s*abc/.test(range.getText())); + assertFalse('Nodes range is not collapsed', range.isCollapsed()); +} + + +function testCreateControlRange() { + if (!goog.userAgent.IE) { + return; + } + var cr = document.body.createControlRange(); + cr.addElement(goog.dom.getElement('logo')); + + var range = goog.dom.Range.createFromBrowserRange(cr); + assertNotNull('Control range object can be created from browser range', + range); + assertEquals('Created range is a control range', goog.dom.RangeType.CONTROL, + range.getType()); +} + + +function testTextNode() { + var range = goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test1').firstChild); + + assertEquals('Created range is a text range', goog.dom.RangeType.TEXT, + range.getType()); + assertEquals('Text node should be selected at start endpoint', 'Text', + range.getStartNode().nodeValue); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('Text node should be selected at end endpoint', 'Text', + range.getEndNode().nodeValue); + assertEquals('Selection should end at offset 4', 'Text'.length, + range.getEndOffset()); + + assertEquals('Container should be text node', goog.dom.NodeType.TEXT, + range.getContainer().nodeType); + + assertEquals('Text content should be "Text"', 'Text', range.getText()); + assertFalse('Text range is not collapsed', range.isCollapsed()); +} + + +function testDiv() { + var range = goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test2')); + + assertEquals('Text node "abc" should be selected at start endpoint', 'abc', + range.getStartNode().nodeValue); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('Text node "def" should be selected at end endpoint', 'def', + range.getEndNode().nodeValue); + assertEquals('Selection should end at offset 3', 'def'.length, + range.getEndOffset()); + + assertEquals('Container should be DIV', goog.dom.getElement('test2'), + range.getContainer()); + + assertTrue('Div text content should be "abc\\s*def"', + /abc\s*def/.test(range.getText())); + assertFalse('Div range is not collapsed', range.isCollapsed()); +} + + +function testEmptyNode() { + var range = goog.dom.Range.createFromNodeContents( + goog.dom.getElement('empty')); + + assertEquals('DIV be selected at start endpoint', + goog.dom.getElement('empty'), range.getStartNode()); + assertEquals('Selection should start at offset 0', 0, + range.getStartOffset()); + + assertEquals('DIV should be selected at end endpoint', + goog.dom.getElement('empty'), range.getEndNode()); + assertEquals('Selection should end at offset 0', 0, + range.getEndOffset()); + + assertEquals('Container should be DIV', goog.dom.getElement('empty'), + range.getContainer()); + + assertEquals('Empty text content should be ""', '', range.getText()); + assertTrue('Empty range is collapsed', range.isCollapsed()); +} + + +function testCollapse() { + var range = goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test2')); + assertFalse('Div range is not collapsed', range.isCollapsed()); + range.collapse(); + assertTrue('Div range is collapsed after call to empty()', + range.isCollapsed()); + + range = goog.dom.Range.createFromNodeContents(goog.dom.getElement('empty')); + assertTrue('Empty range is collapsed', range.isCollapsed()); + range.collapse(); + assertTrue('Empty range is still collapsed', range.isCollapsed()); +} + +// TODO(robbyw): Test iteration over a strange document fragment. + +function testIterator() { + goog.testing.dom.assertNodesMatch(goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test2')), ['abc', '#br', '#br', 'def']); +} + +function testReversedNodes() { + var node = goog.dom.getElement('test1').firstChild; + var range = goog.dom.Range.createFromNodes(node, 4, node, 0); + assertTrue('Range is reversed', range.isReversed()); + node = goog.dom.getElement('test3'); + range = goog.dom.Range.createFromNodes(node, 0, node, 1); + assertFalse('Range is not reversed', range.isReversed()); +} + +function testReversedContents() { + var range = goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test1'), true); + assertTrue('Range is reversed', range.isReversed()); + assertEquals('Range should select "Text"', 'Text', + range.getText()); + assertEquals('Range start offset should be 0', 0, range.getStartOffset()); + assertEquals('Range end offset should be 4', 4, range.getEndOffset()); + assertEquals('Range anchor offset should be 4', 4, range.getAnchorOffset()); + assertEquals('Range focus offset should be 0', 0, range.getFocusOffset()); + + var range2 = range.clone(); + + range.collapse(true); + assertTrue('Range is collapsed', range.isCollapsed()); + assertFalse('Collapsed range is not reversed', range.isReversed()); + assertEquals('Post collapse start offset should be 4', 4, + range.getStartOffset()); + + range2.collapse(false); + assertTrue('Range 2 is collapsed', range2.isCollapsed()); + assertFalse('Collapsed range 2 is not reversed', range2.isReversed()); + assertEquals('Post collapse start offset 2 should be 0', 0, + range2.getStartOffset()); +} + +function testRemoveContents() { + var outer = goog.dom.getElement('removeTest'); + var range = goog.dom.Range.createFromNodeContents(outer.firstChild); + + range.removeContents(); + + assertEquals('Removed range content should be ""', '', range.getText()); + assertTrue('Removed range should be collapsed', range.isCollapsed()); + assertEquals('Outer div should have 1 child now', 1, + outer.childNodes.length); + assertEquals('Inner div should be empty', 0, + outer.firstChild.childNodes.length); +} + +function testRemovePartialContents() { + var outer = goog.dom.getElement('removePartialTest'); + var originalText = goog.dom.getTextContent(outer); + + try { + var range = goog.dom.Range.createFromNodes(outer.firstChild, 2, + outer.firstChild, 4); + removeHelper(1, range, outer, 1, '0145'); + + range = goog.dom.Range.createFromNodes(outer.firstChild, 0, + outer.firstChild, 1); + removeHelper(2, range, outer, 1, '145'); + + range = goog.dom.Range.createFromNodes(outer.firstChild, 2, + outer.firstChild, 3); + removeHelper(3, range, outer, 1, '14'); + + var br = goog.dom.createDom('BR'); + outer.appendChild(br); + range = goog.dom.Range.createFromNodes(outer.firstChild, 1, + outer, 1); + removeHelper(4, range, outer, 2, '1
    '); + + outer.innerHTML = '
    123'; + range = goog.dom.Range.createFromNodes(outer, 0, outer.lastChild, 2); + removeHelper(5, range, outer, 1, '3'); + + outer.innerHTML = '123
    456'; + range = goog.dom.Range.createFromNodes(outer.firstChild, 1, outer.lastChild, + 2); + removeHelper(6, range, outer, 2, '16'); + + outer.innerHTML = '123
    456'; + range = goog.dom.Range.createFromNodes(outer.firstChild, 0, outer.lastChild, + 2); + removeHelper(7, range, outer, 1, '6'); + + outer.innerHTML = '
    '; + range = goog.dom.Range.createFromNodeContents(outer.firstChild); + removeHelper(8, range, outer, 1, '
    '); + } finally { + // Restore the original text state for repeated runs. + goog.dom.setTextContent(outer, originalText); + } + + // TODO(robbyw): Fix the following edge cases: + // * Selecting contents of a node containing multiply empty divs + // * Selecting via createFromNodes(x, 0, x, x.childNodes.length) + // * Consistent handling of nodeContents(
    ).remove +} + +function removeHelper(testNumber, range, outer, expectedChildCount, + expectedContent) { + range.removeContents(); + assertTrue(testNumber + ': Removed range should now be collapsed', + range.isCollapsed()); + assertEquals(testNumber + ': Removed range content should be ""', '', + range.getText()); + assertEquals(testNumber + ': Outer div should contain correct text', + expectedContent, outer.innerHTML.toLowerCase()); + assertEquals(testNumber + ': Outer div should have ' + expectedChildCount + + ' children now', expectedChildCount, outer.childNodes.length); + assertNotNull(testNumber + ': Empty node should still exist', + goog.dom.getElement('empty')); +} + +function testSurroundContents() { + var outer = goog.dom.getElement('surroundTest'); + outer.innerHTML = '---Text that
    will be surrounded---'; + var range = goog.dom.Range.createFromNodes(outer.firstChild, 3, + outer.lastChild, outer.lastChild.nodeValue.length - 3); + + var div = goog.dom.createDom(goog.dom.TagName.DIV, {'style': 'color: red'}); + var output = range.surroundContents(div); + + assertEquals('Outer element should contain new element', outer, + output.parentNode); + assertFalse('New element should have no id', !!output.id); + assertEquals('New element should be red', 'red', output.style.color); + assertEquals('Outer element should have three children', 3, + outer.childNodes.length); + assertEquals('New element should have three children', 3, + output.childNodes.length); + + // TODO(robbyw): Ensure the range stays in a reasonable state. +} + + +/** + * Given two offsets into the 'foobar' node, make sure that inserting + * nodes at those offsets doesn't change a selection of 'oba'. + * @bug 1480638 + */ +function assertSurroundDoesntChangeSelectionWithOffsets( + offset1, offset2, expectedHtml) { + var div = goog.dom.getElement('bug1480638'); + div.innerHTML = 'foobar'; + var rangeToSelect = goog.dom.Range.createFromNodes( + div.firstChild, 2, div.firstChild, 5); + rangeToSelect.select(); + + var rangeToSurround = goog.dom.Range.createFromNodes( + div.firstChild, offset1, div.firstChild, offset2); + rangeToSurround.surroundWithNodes(goog.dom.createDom('span'), + goog.dom.createDom('span')); + + // Make sure that the selection didn't change. + assertHTMLEquals('Selection must not change when contents are surrounded.', + expectedHtml, goog.dom.Range.createFromWindow().getHtmlFragment()); +} + +function testSurroundWithNodesDoesntChangeSelection1() { + assertSurroundDoesntChangeSelectionWithOffsets(3, 4, + 'oba'); +} + +function testSurroundWithNodesDoesntChangeSelection2() { + assertSurroundDoesntChangeSelectionWithOffsets(3, 6, + 'oba'); +} + +function testSurroundWithNodesDoesntChangeSelection3() { + assertSurroundDoesntChangeSelectionWithOffsets(1, 3, + 'oba'); +} + +function testSurroundWithNodesDoesntChangeSelection4() { + assertSurroundDoesntChangeSelectionWithOffsets(1, 6, + 'oba'); +} + +function testInsertNode() { + var outer = goog.dom.getElement('insertTest'); + outer.innerHTML = 'ACD'; + + var range = goog.dom.Range.createFromNodes(outer.firstChild, 1, + outer.firstChild, 2); + range.insertNode(goog.dom.createTextNode('B'), true); + assertEquals('Element should have correct innerHTML', 'ABCD', + outer.innerHTML); + + outer.innerHTML = '12'; + range = goog.dom.Range.createFromNodes(outer.firstChild, 0, + outer.firstChild, 1); + var br = range.insertNode(goog.dom.createDom(goog.dom.TagName.BR), false); + assertEquals('New element should have correct innerHTML', '1
    2', + outer.innerHTML.toLowerCase()); + assertEquals('BR should be in outer', outer, br.parentNode); +} + +function testReplaceContentsWithNode() { + var outer = goog.dom.getElement('insertTest'); + outer.innerHTML = 'AXC'; + + var range = goog.dom.Range.createFromNodes(outer.firstChild, 1, + outer.firstChild, 2); + range.replaceContentsWithNode(goog.dom.createTextNode('B')); + assertEquals('Element should have correct innerHTML', 'ABC', + outer.innerHTML); + + outer.innerHTML = 'ABC'; + range = goog.dom.Range.createFromNodes(outer.firstChild, 3, + outer.firstChild, 3); + range.replaceContentsWithNode(goog.dom.createTextNode('D')); + assertEquals( + 'Element should have correct innerHTML after collapsed replace', + 'ABCD', outer.innerHTML); + + outer.innerHTML = 'AXXXC'; + range = goog.dom.Range.createFromNodes(outer.firstChild, 1, + outer.lastChild, 1); + range.replaceContentsWithNode(goog.dom.createTextNode('B')); + goog.testing.dom.assertHtmlContentsMatch('ABC', outer); +} + +function testSurroundWithNodes() { + var outer = goog.dom.getElement('insertTest'); + outer.innerHTML = 'ACE'; + var range = goog.dom.Range.createFromNodes(outer.firstChild, 1, + outer.firstChild, 2); + + range.surroundWithNodes(goog.dom.createTextNode('B'), + goog.dom.createTextNode('D')); + + assertEquals('New element should have correct innerHTML', 'ABCDE', + outer.innerHTML); +} + +function testIsRangeInDocument() { + var outer = goog.dom.getElement('insertTest'); + outer.innerHTML = '
    ABC'; + var range = goog.dom.Range.createCaret(outer.lastChild, 1); + + assertEquals('Should get correct start element', 'ABC', + range.getStartNode().nodeValue); + assertTrue('Should be considered in document', range.isRangeInDocument()); + + outer.innerHTML = 'DEF'; + + assertFalse('Should be marked as out of document', + range.isRangeInDocument()); +} + +function testRemovedNode() { + var node = goog.dom.getElement('removeNodeTest'); + var range = goog.dom.browserrange.createRangeFromNodeContents(node); + range.select(); + goog.dom.removeNode(node); + + var newRange = goog.dom.Range.createFromWindow(window); + + // In Chrome 14 and below (<= Webkit 535.1), newRange will be null. + // In Chrome 16 and above (>= Webkit 535.7), newRange will be collapsed + // like on other browsers. + // We didn't bother testing in between. + if (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('535.7')) { + assertNull('Webkit supports rangeCount == 0', newRange); + } else { + assertTrue('The other browsers will just have an empty range.', + newRange.isCollapsed()); + } +} + +function testReversedRange() { + goog.dom.Range.createFromNodes(goog.dom.getElement('test2'), 0, + goog.dom.getElement('test1'), 0).select(); + + var range = goog.dom.Range.createFromWindow(window); + assertTrue('Range should be reversed', + goog.userAgent.IE || range.isReversed()); +} + +function testUnreversedRange() { + goog.dom.Range.createFromNodes(goog.dom.getElement('test1'), 0, + goog.dom.getElement('test2'), 0).select(); + + var range = goog.dom.Range.createFromWindow(window); + assertFalse('Range should not be reversed', range.isReversed()); +} + +function testReversedThenUnreversedRange() { + // This tests a workaround for a webkit bug where webkit caches selections + // incorrectly. + goog.dom.Range.createFromNodes(goog.dom.getElement('test2'), 0, + goog.dom.getElement('test1'), 0).select(); + goog.dom.Range.createFromNodes(goog.dom.getElement('test1'), 0, + goog.dom.getElement('test2'), 0).select(); + + var range = goog.dom.Range.createFromWindow(window); + assertFalse('Range should not be reversed', range.isReversed()); +} + +function testHasAndClearSelection() { + goog.dom.Range.createFromNodeContents( + goog.dom.getElement('test1')).select(); + + assertTrue('Selection should exist', goog.dom.Range.hasSelection()); + + goog.dom.Range.clearSelection(); + + assertFalse('Selection should not exist', goog.dom.Range.hasSelection()); +} + +function assertForward(string, startNode, startOffset, endNode, endOffset) { + var root = goog.dom.getElement('test2'); + var originalInnerHtml = root.innerHTML; + + assertFalse(string, goog.dom.Range.isReversed(startNode, startOffset, + endNode, endOffset)); + assertTrue(string, goog.dom.Range.isReversed(endNode, endOffset, + startNode, startOffset)); + assertEquals('Contents should be unaffected after: ' + string, + root.innerHTML, originalInnerHtml); +} + +function testIsReversed() { + var root = goog.dom.getElement('test2'); + var text1 = root.firstChild; // Text content: 'abc'. + var br = root.childNodes[1]; + var text2 = root.lastChild; // Text content: 'def'. + + assertFalse('Same element position gives false', goog.dom.Range.isReversed( + root, 0, root, 0)); + assertFalse('Same text position gives false', goog.dom.Range.isReversed( + text1, 0, text2, 0)); + assertForward('Element offsets should compare against each other', + root, 0, root, 2); + assertForward('Text node offsets should compare against each other', + text1, 0, text2, 2); + assertForward('Text nodes should compare correctly', + text1, 0, text2, 0); + assertForward('Text nodes should compare to later elements', + text1, 0, br, 0); + assertForward('Text nodes should compare to earlier elements', + br, 0, text2, 0); + assertForward('Parent is before element child', root, 0, br, 0); + assertForward('Parent is before text child', root, 0, text1, 0); + assertFalse('Equivalent position gives false', goog.dom.Range.isReversed( + root, 0, text1, 0)); + assertFalse('Equivalent position gives false', goog.dom.Range.isReversed( + root, 1, br, 0)); + assertForward('End of element is after children', text1, 0, root, 3); + assertForward('End of element is after children', br, 0, root, 3); + assertForward('End of element is after children', text2, 0, root, 3); + assertForward('End of element is after end of last child', + text2, 3, root, 3); +} + +function testSelectAroundSpaces() { + // set the selection + var textNode = goog.dom.getElement('textWithSpaces').firstChild; + goog.dom.TextRange.createFromNodes( + textNode, 5, textNode, 12).select(); + + // get the selection and check that it matches what we set it to + var range = goog.dom.Range.createFromWindow(); + assertEquals(' world ', range.getText()); + assertEquals(5, range.getStartOffset()); + assertEquals(12, range.getEndOffset()); + assertEquals(textNode, range.getContainer()); + + // Check the contents again, because there used to be a bug where + // it changed after calling getContainer(). + assertEquals(' world ', range.getText()); +} + +function testSelectInsideSpaces() { + // set the selection + var textNode = goog.dom.getElement('textWithSpaces').firstChild; + goog.dom.TextRange.createFromNodes( + textNode, 6, textNode, 11).select(); + + // get the selection and check that it matches what we set it to + var range = goog.dom.Range.createFromWindow(); + assertEquals('world', range.getText()); + assertEquals(6, range.getStartOffset()); + assertEquals(11, range.getEndOffset()); + assertEquals(textNode, range.getContainer()); + + // Check the contents again, because there used to be a bug where + // it changed after calling getContainer(). + assertEquals('world', range.getText()); +} + +function testRangeBeforeBreak() { + var container = goog.dom.getElement('rangeAroundBreaks'); + var text = container.firstChild; + var offset = text.length; + assertEquals(4, offset); + + var br = container.childNodes[1]; + var caret = goog.dom.Range.createCaret(text, offset); + caret.select(); + assertEquals(offset, caret.getStartOffset()); + + var range = goog.dom.Range.createFromWindow(); + assertFalse('Should not contain whole
    ', + range.containsNode(br, false)); + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + assertTrue('Range over
    is adjacent to the immediate range before it', + range.containsNode(br, true)); + } else { + assertFalse('Should not contain partial
    ', + range.containsNode(br, true)); + } + + assertEquals(offset, range.getStartOffset()); + assertEquals(text, range.getStartNode()); +} + +function testRangeAfterBreak() { + var container = goog.dom.getElement('rangeAroundBreaks'); + var br = container.childNodes[1]; + var caret = goog.dom.Range.createCaret(container.lastChild, 0); + caret.select(); + assertEquals(0, caret.getStartOffset()); + + var range = goog.dom.Range.createFromWindow(); + assertFalse('Should not contain whole
    ', + range.containsNode(br, false)); + var isSafari3 = + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528'); + + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) || + isSafari3) { + assertTrue('Range over
    is adjacent to the immediate range after it', + range.containsNode(br, true)); + } else { + assertFalse('Should not contain partial
    ', + range.containsNode(br, true)); + } + + if (isSafari3) { + assertEquals(2, range.getStartOffset()); + assertEquals(container, range.getStartNode()); + } else { + assertEquals(0, range.getStartOffset()); + assertEquals(container.lastChild, range.getStartNode()); + } +} + +function testRangeAtBreakAtStart() { + var container = goog.dom.getElement('breaksAroundNode'); + var br = container.firstChild; + var caret = goog.dom.Range.createCaret(container.firstChild, 0); + caret.select(); + assertEquals(0, caret.getStartOffset()); + + var range = goog.dom.Range.createFromWindow(); + assertTrue('Range over
    is adjacent to the immediate range before it', + range.containsNode(br, true)); + assertFalse('Should not contain whole
    ', + range.containsNode(br, false)); + + assertRangeEquals(container, 0, container, 0, range); +} + +function testFocusedElementDisappears() { + // This reproduces a failure case specific to Gecko, where an element is + // created, contentEditable is set, is focused, and removed. After that + // happens, calling selection.collapse fails. + // https://bugzilla.mozilla.org/show_bug.cgi?id=773137 + var disappearingElement = goog.dom.createDom('div'); + document.body.appendChild(disappearingElement); + disappearingElement.contentEditable = true; + disappearingElement.focus(); + document.body.removeChild(disappearingElement); + var container = goog.dom.getElement('empty'); + var caret = goog.dom.Range.createCaret(container, 0); + // This should not throw. + caret.select(); + assertEquals(0, caret.getStartOffset()); +} + +function assertNodeEquals(expected, actual) { + assertEquals( + 'Expected: ' + goog.testing.dom.exposeNode(expected) + + '\nActual: ' + goog.testing.dom.exposeNode(actual), + expected, actual); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/rangeendpoint.js b/mapbox-gl/closure-library/closure/goog/dom/rangeendpoint.js new file mode 100644 index 0000000000..f8d0fe446c --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/rangeendpoint.js @@ -0,0 +1,32 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Simple struct for endpoints of a range. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.RangeEndpoint'); + + +/** + * Constants for selection endpoints. + * @enum {number} + */ +goog.dom.RangeEndpoint = { + START: 1, + END: 0 +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/safe.js b/mapbox-gl/closure-library/closure/goog/dom/safe.js new file mode 100644 index 0000000000..0d236bdab7 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/safe.js @@ -0,0 +1,135 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Type-safe wrappers for unsafe DOM APIs. + * + * This file provides type-safe wrappers for DOM APIs that can result in + * cross-site scripting (XSS) vulnerabilities, if the API is supplied with + * untrusted (attacker-controlled) input. Instead of plain strings, the type + * safe wrappers consume values of types from the goog.html package whose + * contract promises that values are safe to use in the corresponding context. + * + * Hence, a program that exclusively uses the wrappers in this file (i.e., whose + * only reference to security-sensitive raw DOM APIs are in this file) is + * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo + * correctness of code that produces values of the respective goog.html types, + * and absent code that violates type safety). + * + * For example, assigning to an element's .innerHTML property a string that is + * derived (even partially) from untrusted input typically results in an XSS + * vulnerability. The type-safe wrapper goog.html.setInnerHtml consumes a value + * of type goog.html.SafeHtml, whose contract states that using its values in a + * HTML context will not result in XSS. Hence a program that is free of direct + * assignments to any element's innerHTML property (with the exception of the + * assignment to .innerHTML in this file) is guaranteed to be free of XSS due to + * assignment of untrusted strings to the innerHTML property. + */ + +goog.provide('goog.dom.safe'); + +goog.require('goog.html.SafeHtml'); +goog.require('goog.html.SafeUrl'); + + +/** + * Assigns known-safe HTML to an element's innerHTML property. + * @param {!Element} elem The element whose innerHTML is to be assigned to. + * @param {!goog.html.SafeHtml} html The known-safe HTML to assign. + */ +goog.dom.safe.setInnerHtml = function(elem, html) { + elem.innerHTML = goog.html.SafeHtml.unwrap(html); +}; + + +/** + * Assigns known-safe HTML to an element's outerHTML property. + * @param {!Element} elem The element whose outerHTML is to be assigned to. + * @param {!goog.html.SafeHtml} html The known-safe HTML to assign. + */ +goog.dom.safe.setOuterHtml = function(elem, html) { + elem.outerHTML = goog.html.SafeHtml.unwrap(html); +}; + + +/** + * Writes known-safe HTML to a document. + * @param {!Document} doc The document to be written to. + * @param {!goog.html.SafeHtml} html The known-safe HTML to assign. + */ +goog.dom.safe.documentWrite = function(doc, html) { + doc.write(goog.html.SafeHtml.unwrap(html)); +}; + + +/** + * Safely assigns a URL to an anchor element's href property. + * + * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to + * anchor's href property. If url is of type string however, it is first + * sanitized using goog.html.SafeUrl.sanitize. + * + * Example usage: + * goog.dom.safe.setAnchorHref(anchorEl, url); + * which is a safe alternative to + * anchorEl.href = url; + * The latter can result in XSS vulnerabilities if url is a + * user-/attacker-controlled value. + * + * @param {!HTMLAnchorElement} anchor The anchor element whose href property + * is to be assigned to. + * @param {string|!goog.html.SafeUrl} url The URL to assign. + * @see goog.html.SafeUrl#sanitize + */ +goog.dom.safe.setAnchorHref = function(anchor, url) { + /** @type {!goog.html.SafeUrl} */ + var safeUrl; + if (url instanceof goog.html.SafeUrl) { + safeUrl = url; + } else { + safeUrl = goog.html.SafeUrl.sanitize(url); + } + anchor.href = goog.html.SafeUrl.unwrap(safeUrl); +}; + + +/** + * Safely assigns a URL to a Location object's href property. + * + * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to + * loc's href property. If url is of type string however, it is first sanitized + * using goog.html.SafeUrl.sanitize. + * + * Example usage: + * goog.dom.safe.setLocationHref(document.location, redirectUrl); + * which is a safe alternative to + * document.location.href = redirectUrl; + * The latter can result in XSS vulnerabilities if redirectUrl is a + * user-/attacker-controlled value. + * + * @param {!Location} loc The Location object whose href property is to be + * assigned to. + * @param {string|!goog.html.SafeUrl} url The URL to assign. + * @see goog.html.SafeUrl#sanitize + */ +goog.dom.safe.setLocationHref = function(loc, url) { + /** @type {!goog.html.SafeUrl} */ + var safeUrl; + if (url instanceof goog.html.SafeUrl) { + safeUrl = url; + } else { + safeUrl = goog.html.SafeUrl.sanitize(url); + } + loc.href = goog.html.SafeUrl.unwrap(safeUrl); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/safe_test.html b/mapbox-gl/closure-library/closure/goog/dom/safe_test.html new file mode 100644 index 0000000000..467f3dd3aa --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/safe_test.html @@ -0,0 +1,19 @@ + + + + + +Closure Unit Tests - goog.dom.safe + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/safe_test.js b/mapbox-gl/closure-library/closure/goog/dom/safe_test.js new file mode 100644 index 0000000000..b96567d4d5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/safe_test.js @@ -0,0 +1,80 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.safeTest'); +goog.setTestOnly('goog.dom.safeTest'); + +goog.require('goog.dom.safe'); +goog.require('goog.html.SafeUrl'); +goog.require('goog.html.testing'); +goog.require('goog.string.Const'); +goog.require('goog.testing.jsunit'); + +function testSetInnerHtml() { + var mockElement = { + 'innerHTML': 'blarg' + }; + var html = ' + + + +
    + abc +
    def
    + ghi +
    jkl
    + mno +
    pqr
    + stu +
    + +
    + abc +
    def
    + ghi +
    jkl
    + mno +
    pqr
    + stu +
    + +
    + abc +
    def
    + ghi +
    jkl
    + mno +
    pqr
    + stu +
    + +
    + abc +
    def
    + ghi +
    jkl
    + mno +
    pqr
    + stu +
    + +
    + abc +
    def
    + ghi +
    jkl
    + mno +
    pqr
    + stu +
    + +
    foo
    bar
    baz
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/savedcaretrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/savedcaretrange_test.js new file mode 100644 index 0000000000..577d49741e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/savedcaretrange_test.js @@ -0,0 +1,224 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.SavedCaretRangeTest'); +goog.setTestOnly('goog.dom.SavedCaretRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.SavedCaretRange'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function setUp() { + document.body.normalize(); +} + + +/** @bug 1480638 */ +function testSavedCaretRangeDoesntChangeSelection() { + // NOTE(nicksantos): We cannot detect this bug programatically. The only + // way to detect it is to run this test manually and look at the selection + // when it ends. + var div = goog.dom.getElement('bug1480638'); + var range = goog.dom.Range.createFromNodes( + div.firstChild, 0, div.lastChild, 1); + range.select(); + + // Observe visible selection. Then move to next line and see it change. + // If the bug exists, it starts with "foo" selected and ends with + // it not selected. + //debugger; + var saved = range.saveUsingCarets(); +} + +function testSavedCaretRange() { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) { + // testSavedCaretRange fails in IE7 unless the source files are loaded in a + // certain order. Adding goog.require('goog.dom.classes') to dom.js or + // goog.require('goog.array') to savedcaretrange_test.js after the + // goog.require('goog.dom') line fixes the test, but it's better to not + // rely on such hacks without understanding the reason of the failure. + return; + } + + var parent = goog.dom.getElement('caretRangeTest'); + var def = goog.dom.getElement('def'); + var jkl = goog.dom.getElement('jkl'); + + var range = goog.dom.Range.createFromNodes( + def.firstChild, 1, jkl.firstChild, 2); + assertFalse(range.isReversed()); + range.select(); + + var saved = range.saveUsingCarets(); + assertHTMLEquals( + 'def', def.innerHTML); + assertHTMLEquals( + 'jkl', jkl.innerHTML); + + goog.testing.dom.assertRangeEquals( + def.childNodes[1], 0, jkl.childNodes[1], 0, + saved.toAbstractRange()); + + def = goog.dom.getElement('def'); + jkl = goog.dom.getElement('jkl'); + + var restoredRange = clearSelectionAndRestoreSaved(parent, saved); + assertFalse(restoredRange.isReversed()); + goog.testing.dom.assertRangeEquals(def, 1, jkl, 1, restoredRange); + + var selection = goog.dom.Range.createFromWindow(window); + assertHTMLEquals('def', def.innerHTML); + assertHTMLEquals('jkl', jkl.innerHTML); + + // def and jkl now contain fragmented text nodes. + if (goog.userAgent.WEBKIT || + (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'))) { + goog.testing.dom.assertRangeEquals( + def.childNodes[1], 0, jkl.childNodes[0], 2, selection); + } else if (goog.userAgent.OPERA) { + goog.testing.dom.assertRangeEquals( + def.childNodes[1], 0, jkl.childNodes[1], 0, selection); + } else { + goog.testing.dom.assertRangeEquals( + def, 1, jkl, 1, selection); + } +} + +function testReversedSavedCaretRange() { + var parent = goog.dom.getElement('caretRangeTest'); + var def = goog.dom.getElement('def-5'); + var jkl = goog.dom.getElement('jkl-5'); + + var range = goog.dom.Range.createFromNodes( + jkl.firstChild, 1, def.firstChild, 2); + assertTrue(range.isReversed()); + range.select(); + + var saved = range.saveUsingCarets(); + var restoredRange = clearSelectionAndRestoreSaved(parent, saved); + assertTrue(restoredRange.isReversed()); + goog.testing.dom.assertRangeEquals(def, 1, jkl, 1, restoredRange); +} + +/* + TODO(user): Look into why removeCarets test doesn't pass. + function testRemoveCarets() { + var def = goog.dom.getElement('def'); + var jkl = goog.dom.getElement('jkl'); + + var range = goog.dom.Range.createFromNodes( + def.firstChild, 1, jkl.firstChild, 2); + range.select(); + + var saved = range.saveUsingCarets(); + assertHTMLEquals( + "def", def.innerHTML); + assertHTMLEquals( + "jkl", jkl.innerHTML); + + saved.removeCarets(); + assertHTMLEquals("def", def.innerHTML); + assertHTMLEquals("jkl", jkl.innerHTML); + + var selection = goog.dom.Range.createFromWindow(window); + + assertEquals('Wrong start node', def.firstChild, selection.getStartNode()); + assertEquals('Wrong end node', jkl.firstChild, selection.getEndNode()); + assertEquals('Wrong start offset', 1, selection.getStartOffset()); + assertEquals('Wrong end offset', 2, selection.getEndOffset()); + } + */ + +function testRemoveContents() { + var def = goog.dom.getElement('def-4'); + var jkl = goog.dom.getElement('jkl-4'); + + // Sanity check. + var container = goog.dom.getElement('removeContentsTest'); + assertEquals(7, container.childNodes.length); + assertEquals('def', def.innerHTML); + assertEquals('jkl', jkl.innerHTML); + + var range = goog.dom.Range.createFromNodes( + def.firstChild, 1, jkl.firstChild, 2); + range.select(); + + var saved = range.saveUsingCarets(); + var restored = saved.restore(); + restored.removeContents(); + + assertEquals(6, container.childNodes.length); + assertEquals('d', def.innerHTML); + assertEquals('l', jkl.innerHTML); +} + +function testHtmlEqual() { + var parent = goog.dom.getElement('caretRangeTest-2'); + var def = goog.dom.getElement('def-2'); + var jkl = goog.dom.getElement('jkl-2'); + + var range = goog.dom.Range.createFromNodes( + def.firstChild, 1, jkl.firstChild, 2); + range.select(); + var saved = range.saveUsingCarets(); + var html1 = parent.innerHTML; + saved.removeCarets(); + + var saved2 = range.saveUsingCarets(); + var html2 = parent.innerHTML; + saved2.removeCarets(); + + assertNotEquals('Same selection with different saved caret range carets ' + + 'must have different html.', html1, html2); + + assertTrue('Same selection with different saved caret range carets must ' + + 'be considered equal by htmlEqual', + goog.dom.SavedCaretRange.htmlEqual(html1, html2)); + + saved.dispose(); + saved2.dispose(); +} + +function testStartCaretIsAtEndOfParent() { + var parent = goog.dom.getElement('caretRangeTest-3'); + var def = goog.dom.getElement('def-3'); + var jkl = goog.dom.getElement('jkl-3'); + + var range = goog.dom.Range.createFromNodes( + def, 1, jkl, 1); + range.select(); + var saved = range.saveUsingCarets(); + clearSelectionAndRestoreSaved(parent, saved); + range = goog.dom.Range.createFromWindow(); + assertEquals('ghijkl', range.getText().replace(/\s/g, '')); +} + + +/** + * Clear the selection by re-parsing the DOM. Then restore the saved + * selection. + * @param {Node} parent The node containing the current selection. + * @param {goog.dom.SavedRange} saved The saved range. + * @return {goog.dom.AbstractRange} Restored range. + */ +function clearSelectionAndRestoreSaved(parent, saved) { + goog.dom.Range.clearSelection(); + assertFalse(goog.dom.Range.hasSelection(window)); + var range = saved.restore(); + assertTrue(goog.dom.Range.hasSelection(window)); + return range; +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/savedrange.js b/mapbox-gl/closure-library/closure/goog/dom/savedrange.js new file mode 100644 index 0000000000..5a7e951347 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/savedrange.js @@ -0,0 +1,74 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A generic interface for saving and restoring ranges. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.SavedRange'); + +goog.require('goog.Disposable'); +goog.require('goog.log'); + + + +/** + * Abstract interface for a saved range. + * @constructor + * @extends {goog.Disposable} + */ +goog.dom.SavedRange = function() { + goog.Disposable.call(this); +}; +goog.inherits(goog.dom.SavedRange, goog.Disposable); + + +/** + * Logging object. + * @type {goog.log.Logger} + * @private + */ +goog.dom.SavedRange.logger_ = + goog.log.getLogger('goog.dom.SavedRange'); + + +/** + * Restores the range and by default disposes of the saved copy. Take note: + * this means the by default SavedRange objects are single use objects. + * @param {boolean=} opt_stayAlive Whether this SavedRange should stay alive + * (not be disposed) after restoring the range. Defaults to false (dispose). + * @return {goog.dom.AbstractRange} The restored range. + */ +goog.dom.SavedRange.prototype.restore = function(opt_stayAlive) { + if (this.isDisposed()) { + goog.log.error(goog.dom.SavedRange.logger_, + 'Disposed SavedRange objects cannot be restored.'); + } + + var range = this.restoreInternal(); + if (!opt_stayAlive) { + this.dispose(); + } + return range; +}; + + +/** + * Internal method to restore the saved range. + * @return {goog.dom.AbstractRange} The restored range. + */ +goog.dom.SavedRange.prototype.restoreInternal = goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.html b/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.html new file mode 100644 index 0000000000..8856ae20bf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.html @@ -0,0 +1,20 @@ + + + + + +Closure Unit Tests - goog.dom.SavedRange + + + + +
    Text
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.js new file mode 100644 index 0000000000..bc9be67fbb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/savedrange_test.js @@ -0,0 +1,56 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.SavedRangeTest'); +goog.setTestOnly('goog.dom.SavedRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function testSaved() { + var node = goog.dom.getElement('test1'); + var range = goog.dom.Range.createFromNodeContents(node); + var savedRange = range.saveUsingDom(); + + range = savedRange.restore(true); + assertEquals('Restored range should select "Text"', 'Text', + range.getText()); + assertFalse('Restored range should not be reversed.', range.isReversed()); + assertFalse('Range should not have disposed itself.', + savedRange.isDisposed()); + + goog.dom.Range.clearSelection(); + assertFalse(goog.dom.Range.hasSelection(window)); + + range = savedRange.restore(); + assertTrue('Range should have auto-disposed.', savedRange.isDisposed()); + assertEquals('Restored range should select "Text"', 'Text', + range.getText()); + assertFalse('Restored range should not be reversed.', range.isReversed()); +} + +function testReversedSave() { + var node = goog.dom.getElement('test1').firstChild; + var range = goog.dom.Range.createFromNodes(node, 4, node, 0); + var savedRange = range.saveUsingDom(); + + range = savedRange.restore(); + assertEquals('Restored range should select "Text"', 'Text', + range.getText()); + if (!goog.userAgent.IE) { + assertTrue('Restored range should be reversed.', range.isReversed()); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/selection.js b/mapbox-gl/closure-library/closure/goog/dom/selection.js new file mode 100644 index 0000000000..85937e7a75 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/selection.js @@ -0,0 +1,471 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with selections in input boxes and text + * areas. + * + * @author arv@google.com (Erik Arvidsson) + * @see ../demos/dom_selection.html + */ + + +goog.provide('goog.dom.selection'); + +goog.require('goog.string'); +goog.require('goog.userAgent'); + + +/** + * Sets the place where the selection should start inside a textarea or a text + * input + * @param {Element} textfield A textarea or text input. + * @param {number} pos The position to set the start of the selection at. + */ +goog.dom.selection.setStart = function(textfield, pos) { + if (goog.dom.selection.useSelectionProperties_(textfield)) { + textfield.selectionStart = pos; + } else if (goog.userAgent.IE) { + // destructuring assignment would have been sweet + var tmp = goog.dom.selection.getRangeIe_(textfield); + var range = tmp[0]; + var selectionRange = tmp[1]; + + if (range.inRange(selectionRange)) { + pos = goog.dom.selection.canonicalizePositionIe_(textfield, pos); + + range.collapse(true); + range.move('character', pos); + range.select(); + } + } +}; + + +/** + * Return the place where the selection starts inside a textarea or a text + * input + * @param {Element} textfield A textarea or text input. + * @return {number} The position where the selection starts or 0 if it was + * unable to find the position or no selection exists. Note that we can't + * reliably tell the difference between an element that has no selection and + * one where it starts at 0. + */ +goog.dom.selection.getStart = function(textfield) { + return goog.dom.selection.getEndPoints_(textfield, true)[0]; +}; + + +/** + * Returns the start and end points of the selection within a textarea in IE. + * IE treats newline characters as \r\n characters, and we need to check for + * these characters at the edge of our selection, to ensure that we return the + * right cursor position. + * @param {TextRange} range Complete range object, e.g., "Hello\r\n". + * @param {TextRange} selRange Selected range object. + * @param {boolean} getOnlyStart Value indicating if only start + * cursor position is to be returned. In IE, obtaining the end position + * involves extra work, hence we have this parameter for calls which need + * only start position. + * @return {!Array} An array with the start and end positions where the + * selection starts and ends or [0,0] if it was unable to find the + * positions or no selection exists. Note that we can't reliably tell the + * difference between an element that has no selection and one where + * it starts and ends at 0. If getOnlyStart was true, we return + * -1 as end offset. + * @private + */ +goog.dom.selection.getEndPointsTextareaIe_ = function( + range, selRange, getOnlyStart) { + // Create a duplicate of the selected range object to perform our actions + // against. Example of selectionRange = "" (assuming that the cursor is + // just after the \r\n combination) + var selectionRange = selRange.duplicate(); + + // Text before the selection start, e.g.,"Hello" (notice how range.text + // excludes the \r\n sequence) + var beforeSelectionText = range.text; + // Text before the selection start, e.g., "Hello" (this will later include + // the \r\n sequences also) + var untrimmedBeforeSelectionText = beforeSelectionText; + // Text within the selection , e.g. "" assuming that the cursor is just after + // the \r\n combination. + var selectionText = selectionRange.text; + // Text within the selection, e.g., "" (this will later include the \r\n + // sequences also) + var untrimmedSelectionText = selectionText; + + // Boolean indicating whether we are done dealing with the text before the + // selection's beginning. + var isRangeEndTrimmed = false; + // Go over the range until it becomes a 0-lengthed range or until the range + // text starts changing when we move the end back by one character. + // If after moving the end back by one character, the text remains the same, + // then we need to add a "\r\n" at the end to get the actual text. + while (!isRangeEndTrimmed) { + if (range.compareEndPoints('StartToEnd', range) == 0) { + isRangeEndTrimmed = true; + } else { + range.moveEnd('character', -1); + if (range.text == beforeSelectionText) { + // If the start position of the cursor was after a \r\n string, + // we would skip over it in one go with the moveEnd call, but + // range.text will still show "Hello" (because of the IE range.text + // bug) - this implies that we should add a \r\n to our + // untrimmedBeforeSelectionText string. + untrimmedBeforeSelectionText += '\r\n'; + } else { + isRangeEndTrimmed = true; + } + } + } + + if (getOnlyStart) { + // We return -1 as end, since the caller is only interested in the start + // value. + return [untrimmedBeforeSelectionText.length, -1]; + } + // Boolean indicating whether we are done dealing with the text inside the + // selection. + var isSelectionRangeEndTrimmed = false; + // Go over the selected range until it becomes a 0-lengthed range or until + // the range text starts changing when we move the end back by one character. + // If after moving the end back by one character, the text remains the same, + // then we need to add a "\r\n" at the end to get the actual text. + while (!isSelectionRangeEndTrimmed) { + if (selectionRange.compareEndPoints('StartToEnd', selectionRange) == 0) { + isSelectionRangeEndTrimmed = true; + } else { + selectionRange.moveEnd('character', -1); + if (selectionRange.text == selectionText) { + // If the selection was not empty, and the end point of the selection + // was just after a \r\n, we would have skipped it in one go with the + // moveEnd call, and this implies that we should add a \r\n to the + // untrimmedSelectionText string. + untrimmedSelectionText += '\r\n'; + } else { + isSelectionRangeEndTrimmed = true; + } + } + } + return [ + untrimmedBeforeSelectionText.length, + untrimmedBeforeSelectionText.length + untrimmedSelectionText.length]; +}; + + +/** + * Returns the start and end points of the selection inside a textarea or a + * text input. + * @param {Element} textfield A textarea or text input. + * @return {!Array} An array with the start and end positions where the + * selection starts and ends or [0,0] if it was unable to find the + * positions or no selection exists. Note that we can't reliably tell the + * difference between an element that has no selection and one where + * it starts and ends at 0. + */ +goog.dom.selection.getEndPoints = function(textfield) { + return goog.dom.selection.getEndPoints_(textfield, false); +}; + + +/** + * Returns the start and end points of the selection inside a textarea or a + * text input. + * @param {Element} textfield A textarea or text input. + * @param {boolean} getOnlyStart Value indicating if only start + * cursor position is to be returned. In IE, obtaining the end position + * involves extra work, hence we have this parameter. In FF, there is not + * much extra effort involved. + * @return {!Array} An array with the start and end positions where the + * selection starts and ends or [0,0] if it was unable to find the + * positions or no selection exists. Note that we can't reliably tell the + * difference between an element that has no selection and one where + * it starts and ends at 0. If getOnlyStart was true, we return + * -1 as end offset. + * @private + */ +goog.dom.selection.getEndPoints_ = function(textfield, getOnlyStart) { + var startPos = 0; + var endPos = 0; + if (goog.dom.selection.useSelectionProperties_(textfield)) { + startPos = textfield.selectionStart; + endPos = getOnlyStart ? -1 : textfield.selectionEnd; + } else if (goog.userAgent.IE) { + var tmp = goog.dom.selection.getRangeIe_(textfield); + var range = tmp[0]; + var selectionRange = tmp[1]; + + if (range.inRange(selectionRange)) { + range.setEndPoint('EndToStart', selectionRange); + if (textfield.type == 'textarea') { + return goog.dom.selection.getEndPointsTextareaIe_( + range, selectionRange, getOnlyStart); + } + startPos = range.text.length; + if (!getOnlyStart) { + endPos = range.text.length + selectionRange.text.length; + } else { + endPos = -1; // caller did not ask for end position + } + } + } + return [startPos, endPos]; +}; + + +/** + * Sets the place where the selection should end inside a text area or a text + * input + * @param {Element} textfield A textarea or text input. + * @param {number} pos The position to end the selection at. + */ +goog.dom.selection.setEnd = function(textfield, pos) { + if (goog.dom.selection.useSelectionProperties_(textfield)) { + textfield.selectionEnd = pos; + } else if (goog.userAgent.IE) { + var tmp = goog.dom.selection.getRangeIe_(textfield); + var range = tmp[0]; + var selectionRange = tmp[1]; + + if (range.inRange(selectionRange)) { + // Both the current position and the start cursor position need + // to be canonicalized to take care of possible \r\n miscounts. + pos = goog.dom.selection.canonicalizePositionIe_(textfield, pos); + var startCursorPos = goog.dom.selection.canonicalizePositionIe_( + textfield, goog.dom.selection.getStart(textfield)); + + selectionRange.collapse(true); + selectionRange.moveEnd('character', pos - startCursorPos); + selectionRange.select(); + } + } +}; + + +/** + * Returns the place where the selection ends inside a textarea or a text input + * @param {Element} textfield A textarea or text input. + * @return {number} The position where the selection ends or 0 if it was + * unable to find the position or no selection exists. + */ +goog.dom.selection.getEnd = function(textfield) { + return goog.dom.selection.getEndPoints_(textfield, false)[1]; +}; + + +/** + * Sets the cursor position within a textfield. + * @param {Element} textfield A textarea or text input. + * @param {number} pos The position within the text field. + */ +goog.dom.selection.setCursorPosition = function(textfield, pos) { + if (goog.dom.selection.useSelectionProperties_(textfield)) { + // Mozilla directly supports this + textfield.selectionStart = pos; + textfield.selectionEnd = pos; + + } else if (goog.userAgent.IE) { + pos = goog.dom.selection.canonicalizePositionIe_(textfield, pos); + + // IE has textranges. A textfield's textrange encompasses the + // entire textfield's text by default + var sel = textfield.createTextRange(); + + sel.collapse(true); + sel.move('character', pos); + sel.select(); + } +}; + + +/** + * Sets the selected text inside a textarea or a text input + * @param {Element} textfield A textarea or text input. + * @param {string} text The text to change the selection to. + */ +goog.dom.selection.setText = function(textfield, text) { + if (goog.dom.selection.useSelectionProperties_(textfield)) { + var value = textfield.value; + var oldSelectionStart = textfield.selectionStart; + var before = value.substr(0, oldSelectionStart); + var after = value.substr(textfield.selectionEnd); + textfield.value = before + text + after; + textfield.selectionStart = oldSelectionStart; + textfield.selectionEnd = oldSelectionStart + text.length; + } else if (goog.userAgent.IE) { + var tmp = goog.dom.selection.getRangeIe_(textfield); + var range = tmp[0]; + var selectionRange = tmp[1]; + + if (!range.inRange(selectionRange)) { + return; + } + // When we set the selection text the selection range is collapsed to the + // end. We therefore duplicate the current selection so we know where it + // started. Once we've set the selection text we move the start of the + // selection range to the old start + var range2 = selectionRange.duplicate(); + selectionRange.text = text; + selectionRange.setEndPoint('StartToStart', range2); + selectionRange.select(); + } else { + throw Error('Cannot set the selection end'); + } +}; + + +/** + * Returns the selected text inside a textarea or a text input + * @param {Element} textfield A textarea or text input. + * @return {string} The selected text. + */ +goog.dom.selection.getText = function(textfield) { + if (goog.dom.selection.useSelectionProperties_(textfield)) { + var s = textfield.value; + return s.substring(textfield.selectionStart, textfield.selectionEnd); + } + + if (goog.userAgent.IE) { + var tmp = goog.dom.selection.getRangeIe_(textfield); + var range = tmp[0]; + var selectionRange = tmp[1]; + + if (!range.inRange(selectionRange)) { + return ''; + } else if (textfield.type == 'textarea') { + return goog.dom.selection.getSelectionRangeText_(selectionRange); + } + return selectionRange.text; + } + + throw Error('Cannot get the selection text'); +}; + + +/** + * Returns the selected text within a textarea in IE. + * IE treats newline characters as \r\n characters, and we need to check for + * these characters at the edge of our selection, to ensure that we return the + * right string. + * @param {TextRange} selRange Selected range object. + * @return {string} Selected text in the textarea. + * @private + */ +goog.dom.selection.getSelectionRangeText_ = function(selRange) { + // Create a duplicate of the selected range object to perform our actions + // against. Suppose the text in the textarea is "Hello\r\nWorld" and the + // selection encompasses the "o\r\n" bit, initial selectionRange will be "o" + // (assuming that the cursor is just after the \r\n combination) + var selectionRange = selRange.duplicate(); + + // Text within the selection , e.g. "o" assuming that the cursor is just after + // the \r\n combination. + var selectionText = selectionRange.text; + // Text within the selection, e.g., "o" (this will later include the \r\n + // sequences also) + var untrimmedSelectionText = selectionText; + + // Boolean indicating whether we are done dealing with the text inside the + // selection. + var isSelectionRangeEndTrimmed = false; + // Go over the selected range until it becomes a 0-lengthed range or until + // the range text starts changing when we move the end back by one character. + // If after moving the end back by one character, the text remains the same, + // then we need to add a "\r\n" at the end to get the actual text. + while (!isSelectionRangeEndTrimmed) { + if (selectionRange.compareEndPoints('StartToEnd', selectionRange) == 0) { + isSelectionRangeEndTrimmed = true; + } else { + selectionRange.moveEnd('character', -1); + if (selectionRange.text == selectionText) { + // If the selection was not empty, and the end point of the selection + // was just after a \r\n, we would have skipped it in one go with the + // moveEnd call, and this implies that we should add a \r\n to the + // untrimmedSelectionText string. + untrimmedSelectionText += '\r\n'; + } else { + isSelectionRangeEndTrimmed = true; + } + } + } + return untrimmedSelectionText; +}; + + +/** + * Helper function for returning the range for an object as well as the + * selection range + * @private + * @param {Element} el The element to get the range for. + * @return {!Array} Range of object and selection range in two + * element array. + */ +goog.dom.selection.getRangeIe_ = function(el) { + var doc = el.ownerDocument || el.document; + + var selectionRange = doc.selection.createRange(); + // el.createTextRange() doesn't work on textareas + var range; + + if (el.type == 'textarea') { + range = doc.body.createTextRange(); + range.moveToElementText(el); + } else { + range = el.createTextRange(); + } + + return [range, selectionRange]; +}; + + +/** + * Helper function for canonicalizing a position inside a textfield in IE. + * Deals with the issue that \r\n counts as 2 characters, but + * move('character', n) passes over both characters in one move. + * @private + * @param {Element} textfield The text element. + * @param {number} pos The position desired in that element. + * @return {number} The canonicalized position that will work properly with + * move('character', pos). + */ +goog.dom.selection.canonicalizePositionIe_ = function(textfield, pos) { + if (textfield.type == 'textarea') { + // We do this only for textarea because it is the only one which can + // have a \r\n (input cannot have this). + var value = textfield.value.substring(0, pos); + pos = goog.string.canonicalizeNewlines(value).length; + } + return pos; +}; + + +/** + * Helper function to determine whether it's okay to use + * selectionStart/selectionEnd. + * + * @param {Element} el The element to check for. + * @return {boolean} Whether it's okay to use the selectionStart and + * selectionEnd properties on {@code el}. + * @private + */ +goog.dom.selection.useSelectionProperties_ = function(el) { + try { + return typeof el.selectionStart == 'number'; + } catch (e) { + // Firefox throws an exception if you try to access selectionStart + // on an element with display: none. + return false; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/selection_test.html b/mapbox-gl/closure-library/closure/goog/dom/selection_test.html new file mode 100644 index 0000000000..076e31bc26 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/selection_test.html @@ -0,0 +1,19 @@ + + + + + +Closure Unit Tests - goog.dom.selection + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/selection_test.js b/mapbox-gl/closure-library/closure/goog/dom/selection_test.js new file mode 100644 index 0000000000..17f799aa85 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/selection_test.js @@ -0,0 +1,343 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.selectionTest'); +goog.setTestOnly('goog.dom.selectionTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.selection'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var input; +var hiddenInput; +var textarea; +var hiddenTextarea; + +function setUp() { + input = goog.dom.createDom('input', {type: 'text'}); + textarea = goog.dom.createDom('textarea'); + hiddenInput = goog.dom.createDom( + 'input', {type: 'text', style: 'display: none'}); + hiddenTextarea = goog.dom.createDom( + 'textarea', {style: 'display: none'}); + + document.body.appendChild(input); + document.body.appendChild(textarea); + document.body.appendChild(hiddenInput); + document.body.appendChild(hiddenTextarea); +} + +function tearDown() { + goog.dom.removeNode(input); + goog.dom.removeNode(textarea); + goog.dom.removeNode(hiddenInput); + goog.dom.removeNode(hiddenTextarea); +} + + +/** + * Tests getStart routine in both input and textarea. + */ +function testGetStartInput() { + getStartHelper(input, hiddenInput); +} + +function testGetStartTextarea() { + getStartHelper(textarea, hiddenTextarea); +} + +function getStartHelper(field, hiddenField) { + assertEquals(0, goog.dom.selection.getStart(field)); + assertEquals(0, goog.dom.selection.getStart(hiddenField)); + + field.focus(); + assertEquals(0, goog.dom.selection.getStart(field)); +} + + +/** + * Tests the setText routine for both input and textarea + * with a single line of text. + */ +function testSetTextInput() { + setTextHelper(input); +} + +function testSetTextTextarea() { + setTextHelper(textarea); +} + +function setTextHelper(field) { + // Test one line string only + select(field); + assertEquals('', goog.dom.selection.getText(field)); + + goog.dom.selection.setText(field, 'Get Behind Me Satan'); + assertEquals('Get Behind Me Satan', goog.dom.selection.getText(field)); +} + + +/** + * Tests the setText routine for textarea with multiple lines of text. + */ +function testSetTextMultipleLines() { + select(textarea); + assertEquals('', goog.dom.selection.getText(textarea)); + var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'); + var message = isLegacyIE ? + 'Get Behind Me\r\nSatan' : + 'Get Behind Me\nSatan'; + goog.dom.selection.setText(textarea, message); + assertEquals(message, goog.dom.selection.getText(textarea)); + + // Select the text upto the point just after the \r\n combination + // or \n in GECKO. + var endOfNewline = isLegacyIE ? 15 : 14; + var selectedMessage = message.substring(0, endOfNewline); + goog.dom.selection.setStart(textarea, 0); + goog.dom.selection.setEnd(textarea, endOfNewline); + assertEquals(selectedMessage, goog.dom.selection.getText(textarea)); + + selectedMessage = isLegacyIE ? '\r\n' : '\n'; + goog.dom.selection.setStart(textarea, 13); + goog.dom.selection.setEnd(textarea, endOfNewline); + assertEquals(selectedMessage, goog.dom.selection.getText(textarea)); +} + + +/** + * Tests the setCursor routine for both input and textarea. + */ +function testSetCursorInput() { + setCursorHelper(input); +} + +function testSetCursorTextarea() { + setCursorHelper(textarea); +} + +function setCursorHelper(field) { + select(field); + // try to set the cursor beyond the length of the content + goog.dom.selection.setStart(field, 5); + goog.dom.selection.setEnd(field, 15); + assertEquals(0, goog.dom.selection.getStart(field)); + assertEquals(0, goog.dom.selection.getEnd(field)); + + select(field); + var message = 'Get Behind Me Satan'; + goog.dom.selection.setText(field, message); + goog.dom.selection.setStart(field, 5); + goog.dom.selection.setEnd(field, message.length); + assertEquals(5, goog.dom.selection.getStart(field)); + assertEquals(message.length, goog.dom.selection.getEnd(field)); + + // Set the end before the start, and see if getEnd returns the start + // position itself. + goog.dom.selection.setStart(field, 5); + goog.dom.selection.setEnd(field, 3); + assertEquals(3, goog.dom.selection.getEnd(field)); +} + + +/** + * Tests the getText and setText routines acting on selected text in + * both input and textarea. + */ +function testGetAndSetSelectedTextInput() { + getAndSetSelectedTextHelper(input); +} + +function testGetAndSetSelectedTextTextarea() { + getAndSetSelectedTextHelper(textarea); +} + +function getAndSetSelectedTextHelper(field) { + select(field); + goog.dom.selection.setText(field, 'Get Behind Me Satan'); + + // select 'Behind' + goog.dom.selection.setStart(field, 4); + goog.dom.selection.setEnd(field, 10); + assertEquals('Behind', goog.dom.selection.getText(field)); + + goog.dom.selection.setText(field, 'In Front Of'); + goog.dom.selection.setStart(field, 0); + goog.dom.selection.setEnd(field, 100); + assertEquals('Get In Front Of Me Satan', goog.dom.selection.getText(field)); +} + + +/** + * Test setStart on hidden input and hidden textarea. + */ +function testSetCursorOnHiddenInput() { + setCursorOnHiddenInputHelper(hiddenInput); +} + +function testSetCursorOnHiddenTextarea() { + setCursorOnHiddenInputHelper(hiddenTextarea); +} + +function setCursorOnHiddenInputHelper(hiddenField) { + goog.dom.selection.setStart(hiddenField, 0); + assertEquals(0, goog.dom.selection.getStart(hiddenField)); +} + + +/** + * Test setStart, setEnd, getStart and getEnd in textarea with text + * containing line breaks. + */ +function testSetAndGetCursorWithLineBreaks() { + select(textarea); + var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'); + var newline = isLegacyIE ? '\r\n' : '\n'; + var message = 'Hello' + newline + 'World'; + goog.dom.selection.setText(textarea, message); + + // Test setEnd and getEnd, by setting the cursor somewhere after the + // \r\n combination. + goog.dom.selection.setEnd(textarea, 9); + assertEquals(9, goog.dom.selection.getEnd(textarea)); + + // Test basic setStart and getStart + goog.dom.selection.setStart(textarea, 10); + assertEquals(10, goog.dom.selection.getStart(textarea)); + + // Test setEnd and getEnd, by setting the cursor exactly after the + // \r\n combination in IE or after \n in GECKO. + var endOfNewline = isLegacyIE ? 7 : 6; + checkSetAndGetTextarea(endOfNewline, endOfNewline); + + // Select a \r\n combination in IE or \n in GECKO and see if + // getStart and getEnd work correctly. + clearField(textarea); + message = 'Hello' + newline + newline + 'World'; + goog.dom.selection.setText(textarea, message); + var startOfNewline = isLegacyIE ? 7 : 6; + endOfNewline = isLegacyIE ? 9 : 7; + checkSetAndGetTextarea(startOfNewline, endOfNewline); + + // Select 2 \r\n combinations in IE or 2 \ns in GECKO and see if getStart + // and getEnd work correctly. + checkSetAndGetTextarea(5, endOfNewline); + + // Position cursor b/w 2 \r\n combinations in IE or 2 \ns in GECKO and see + // if getStart and getEnd work correctly. + clearField(textarea); + message = 'Hello' + newline + newline + newline + newline + 'World'; + goog.dom.selection.setText(textarea, message); + var middleOfNewlines = isLegacyIE ? 9 : 7; + checkSetAndGetTextarea(middleOfNewlines, middleOfNewlines); + + // Position cursor at end of a textarea which ends with \r\n in IE or \n in + // GECKO. + if (!goog.userAgent.IE || !goog.userAgent.isVersionOrHigher('11')) { + // TODO(johnlenz): investigate why this fails in IE 11. + clearField(textarea); + message = 'Hello' + newline + newline; + goog.dom.selection.setText(textarea, message); + var endOfTextarea = message.length; + checkSetAndGetTextarea(endOfTextarea, endOfTextarea); + } + + // Position cursor at the end of the 2 starting \r\ns in IE or \ns in GECKO + // within a textarea. + clearField(textarea); + message = newline + newline + 'World'; + goog.dom.selection.setText(textarea, message); + var endOfTwoNewlines = isLegacyIE ? 4 : 2; + checkSetAndGetTextarea(endOfTwoNewlines, endOfTwoNewlines); + + // Position cursor at the end of the first \r\n in IE or \n in + // GECKO within a textarea. + endOfOneNewline = isLegacyIE ? 2 : 1; + checkSetAndGetTextarea(endOfOneNewline, endOfOneNewline); +} + + +/** + * Test to make sure there's no error when getting the range of an unselected + * textarea. See bug 1274027. + */ +function testGetStartOnUnfocusedTextarea() { + input.value = 'White Blood Cells'; + input.focus(); + goog.dom.selection.setCursorPosition(input, 5); + + assertEquals('getStart on input should return where we put the cursor', + 5, goog.dom.selection.getStart(input)); + + assertEquals('getStart on unfocused textarea should succeed without error', + 0, goog.dom.selection.getStart(textarea)); +} + + +/** + * Test to make sure there's no error setting cursor position within a + * textarea after a newline. This is problematic on IE because of the + * '\r\n' vs '\n' issue. + */ +function testSetCursorPositionTextareaWithNewlines() { + textarea.value = 'Hello\nWorld'; + textarea.focus(); + + // Set the selection point between 'W' and 'o'. Position is computed this + // way instead of being hard-coded because it's different in IE due to \r\n + // vs \n. + goog.dom.selection.setCursorPosition(textarea, textarea.value.length - 4); + + var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'); + var linebreak = isLegacyIE ? '\r\n' : '\n'; + var expectedLeftString = 'Hello' + linebreak + 'W'; + + assertEquals('getStart on input should return after the newline', + expectedLeftString.length, goog.dom.selection.getStart(textarea)); + assertEquals('getEnd on input should return after the newline', + expectedLeftString.length, goog.dom.selection.getEnd(textarea)); + + goog.dom.selection.setEnd(textarea, textarea.value.length); + assertEquals('orld', goog.dom.selection.getText(textarea)); +} + + +/** + * Helper function to clear the textfield contents. + */ +function clearField(field) { + field.value = ''; +} + + +/** + * Helper function to set the start and end and assert the getter values. + */ +function checkSetAndGetTextarea(start, end) { + goog.dom.selection.setStart(textarea, start); + goog.dom.selection.setEnd(textarea, end); + assertEquals(start, goog.dom.selection.getStart(textarea)); + assertEquals(end, goog.dom.selection.getEnd(textarea)); +} + + +/** + * Helper function to focus and select a field. In IE8, selected + * fields need focus. + */ +function select(field) { + field.focus(); + field.select(); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagiterator.js b/mapbox-gl/closure-library/closure/goog/dom/tagiterator.js new file mode 100644 index 0000000000..212604d777 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagiterator.js @@ -0,0 +1,368 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Iterator subclass for DOM tree traversal. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.TagIterator'); +goog.provide('goog.dom.TagWalkType'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.iter.Iterator'); +goog.require('goog.iter.StopIteration'); + + +/** + * There are three types of token: + *
      + *
    1. {@code START_TAG} - The beginning of a tag. + *
    2. {@code OTHER} - Any non-element node position. + *
    3. {@code END_TAG} - The end of a tag. + *
    + * Users of this enumeration can rely on {@code START_TAG + END_TAG = 0} and + * that {@code OTHER = 0}. + * + * @enum {number} + */ +goog.dom.TagWalkType = { + START_TAG: 1, + OTHER: 0, + END_TAG: -1 +}; + + + +/** + * A DOM tree traversal iterator. + * + * Starting with the given node, the iterator walks the DOM in order, reporting + * events for the start and end of Elements, and the presence of text nodes. For + * example: + * + *
    + * <div>1<span>2</span>3</div>
    + * 
    + * + * Will return the following nodes: + * + * [div, 1, span, 2, span, 3, div] + * + * With the following states: + * + * [START, OTHER, START, OTHER, END, OTHER, END] + * + * And the following depths + * + * [1, 1, 2, 2, 1, 1, 0] + * + * Imagining | represents iterator position, the traversal stops at + * each of the following locations: + * + *
    + * <div>|1|<span>|2|</span>|3|</div>|
    + * 
    + * + * The iterator can also be used in reverse mode, which will return the nodes + * and states in the opposite order. The depths will be slightly different + * since, like in normal mode, the depth is computed *after* the given node. + * + * Lastly, it is possible to create an iterator that is unconstrained, meaning + * that it will continue iterating until the end of the document instead of + * until exiting the start node. + * + * @param {Node=} opt_node The start node. If unspecified or null, defaults to + * an empty iterator. + * @param {boolean=} opt_reversed Whether to traverse the tree in reverse. + * @param {boolean=} opt_unconstrained Whether the iterator is not constrained + * to the starting node and its children. + * @param {goog.dom.TagWalkType?=} opt_tagType The type of the position. + * Defaults to the start of the given node for forward iterators, and + * the end of the node for reverse iterators. + * @param {number=} opt_depth The starting tree depth. + * @constructor + * @extends {goog.iter.Iterator} + */ +goog.dom.TagIterator = function(opt_node, opt_reversed, + opt_unconstrained, opt_tagType, opt_depth) { + this.reversed = !!opt_reversed; + if (opt_node) { + this.setPosition(opt_node, opt_tagType); + } + this.depth = opt_depth != undefined ? opt_depth : this.tagType || 0; + if (this.reversed) { + this.depth *= -1; + } + this.constrained = !opt_unconstrained; +}; +goog.inherits(goog.dom.TagIterator, goog.iter.Iterator); + + +/** + * The node this position is located on. + * @type {Node} + */ +goog.dom.TagIterator.prototype.node = null; + + +/** + * The type of this position. + * @type {goog.dom.TagWalkType} + */ +goog.dom.TagIterator.prototype.tagType = goog.dom.TagWalkType.OTHER; + + +/** + * The tree depth of this position relative to where the iterator started. The + * depth is considered to be the tree depth just past the current node, so if an + * iterator is at position
    + *     
    |
    + *
    + * (i.e. the node is the div and the type is START_TAG) its depth will be 1. + * @type {number} + */ +goog.dom.TagIterator.prototype.depth; + + +/** + * Whether the node iterator is moving in reverse. + * @type {boolean} + */ +goog.dom.TagIterator.prototype.reversed; + + +/** + * Whether the iterator is constrained to the starting node and its children. + * @type {boolean} + */ +goog.dom.TagIterator.prototype.constrained; + + +/** + * Whether iteration has started. + * @type {boolean} + * @private + */ +goog.dom.TagIterator.prototype.started_ = false; + + +/** + * Set the position of the iterator. Overwrite the tree node and the position + * type which can be one of the {@link goog.dom.TagWalkType} token types. + * Only overwrites the tree depth when the parameter is specified. + * @param {Node} node The node to set the position to. + * @param {goog.dom.TagWalkType?=} opt_tagType The type of the position + * Defaults to the start of the given node. + * @param {number=} opt_depth The tree depth. + */ +goog.dom.TagIterator.prototype.setPosition = function(node, + opt_tagType, opt_depth) { + this.node = node; + + if (node) { + if (goog.isNumber(opt_tagType)) { + this.tagType = opt_tagType; + } else { + // Auto-determine the proper type + this.tagType = this.node.nodeType != goog.dom.NodeType.ELEMENT ? + goog.dom.TagWalkType.OTHER : + this.reversed ? goog.dom.TagWalkType.END_TAG : + goog.dom.TagWalkType.START_TAG; + } + } + + if (goog.isNumber(opt_depth)) { + this.depth = opt_depth; + } +}; + + +/** + * Replace this iterator's values with values from another. The two iterators + * must be of the same type. + * @param {goog.dom.TagIterator} other The iterator to copy. + * @protected + */ +goog.dom.TagIterator.prototype.copyFrom = function(other) { + this.node = other.node; + this.tagType = other.tagType; + this.depth = other.depth; + this.reversed = other.reversed; + this.constrained = other.constrained; +}; + + +/** + * @return {!goog.dom.TagIterator} A copy of this iterator. + */ +goog.dom.TagIterator.prototype.clone = function() { + return new goog.dom.TagIterator(this.node, this.reversed, + !this.constrained, this.tagType, this.depth); +}; + + +/** + * Skip the current tag. + */ +goog.dom.TagIterator.prototype.skipTag = function() { + var check = this.reversed ? goog.dom.TagWalkType.END_TAG : + goog.dom.TagWalkType.START_TAG; + if (this.tagType == check) { + this.tagType = /** @type {goog.dom.TagWalkType} */ (check * -1); + this.depth += this.tagType * (this.reversed ? -1 : 1); + } +}; + + +/** + * Restart the current tag. + */ +goog.dom.TagIterator.prototype.restartTag = function() { + var check = this.reversed ? goog.dom.TagWalkType.START_TAG : + goog.dom.TagWalkType.END_TAG; + if (this.tagType == check) { + this.tagType = /** @type {goog.dom.TagWalkType} */ (check * -1); + this.depth += this.tagType * (this.reversed ? -1 : 1); + } +}; + + +/** + * Move to the next position in the DOM tree. + * @return {Node} Returns the next node, or throws a goog.iter.StopIteration + * exception if the end of the iterator's range has been reached. + * @override + */ +goog.dom.TagIterator.prototype.next = function() { + var node; + + if (this.started_) { + if (!this.node || this.constrained && this.depth == 0) { + throw goog.iter.StopIteration; + } + node = this.node; + + var startType = this.reversed ? goog.dom.TagWalkType.END_TAG : + goog.dom.TagWalkType.START_TAG; + + if (this.tagType == startType) { + // If we have entered the tag, test if there are any children to move to. + var child = this.reversed ? node.lastChild : node.firstChild; + if (child) { + this.setPosition(child); + } else { + // If not, move on to exiting this tag. + this.setPosition(node, + /** @type {goog.dom.TagWalkType} */ (startType * -1)); + } + } else { + var sibling = this.reversed ? node.previousSibling : node.nextSibling; + if (sibling) { + // Try to move to the next node. + this.setPosition(sibling); + } else { + // If no such node exists, exit our parent. + this.setPosition(node.parentNode, + /** @type {goog.dom.TagWalkType} */ (startType * -1)); + } + } + + this.depth += this.tagType * (this.reversed ? -1 : 1); + } else { + this.started_ = true; + } + + // Check the new position for being last, and return it if it's not. + node = this.node; + if (!this.node) { + throw goog.iter.StopIteration; + } + return node; +}; + + +/** + * @return {boolean} Whether next has ever been called on this iterator. + * @protected + */ +goog.dom.TagIterator.prototype.isStarted = function() { + return this.started_; +}; + + +/** + * @return {boolean} Whether this iterator's position is a start tag position. + */ +goog.dom.TagIterator.prototype.isStartTag = function() { + return this.tagType == goog.dom.TagWalkType.START_TAG; +}; + + +/** + * @return {boolean} Whether this iterator's position is an end tag position. + */ +goog.dom.TagIterator.prototype.isEndTag = function() { + return this.tagType == goog.dom.TagWalkType.END_TAG; +}; + + +/** + * @return {boolean} Whether this iterator's position is not at an element node. + */ +goog.dom.TagIterator.prototype.isNonElement = function() { + return this.tagType == goog.dom.TagWalkType.OTHER; +}; + + +/** + * Test if two iterators are at the same position - i.e. if the node and tagType + * is the same. This will still return true if the two iterators are moving in + * opposite directions or have different constraints. + * @param {goog.dom.TagIterator} other The iterator to compare to. + * @return {boolean} Whether the two iterators are at the same position. + */ +goog.dom.TagIterator.prototype.equals = function(other) { + // Nodes must be equal, and we must either have reached the end of our tree + // or be at the same position. + return other.node == this.node && (!this.node || + other.tagType == this.tagType); +}; + + +/** + * Replace the current node with the list of nodes. Reset the iterator so that + * it visits the first of the nodes next. + * @param {...Object} var_args A list of nodes to replace the current node with. + * If the first argument is array-like, it will be used, otherwise all the + * arguments are assumed to be nodes. + */ +goog.dom.TagIterator.prototype.splice = function(var_args) { + // Reset the iterator so that it iterates over the first replacement node in + // the arguments on the next iteration. + var node = this.node; + this.restartTag(); + this.reversed = !this.reversed; + goog.dom.TagIterator.prototype.next.call(this); + this.reversed = !this.reversed; + + // Replace the node with the arguments. + var arr = goog.isArrayLike(arguments[0]) ? arguments[0] : arguments; + for (var i = arr.length - 1; i >= 0; i--) { + goog.dom.insertSiblingAfter(arr[i], node); + } + goog.dom.removeNode(node); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.html b/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.html new file mode 100644 index 0000000000..de3280daa5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.html @@ -0,0 +1,25 @@ + + + + + +goog.dom.TagIterator Tests + + + + + +
    Text

    Text

    +
    • Not
    • Closed
    +
    text
    +
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.js b/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.js new file mode 100644 index 0000000000..6a625c0225 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagiterator_test.js @@ -0,0 +1,584 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.TagIteratorTest'); +goog.setTestOnly('goog.dom.TagIteratorTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagIterator'); +goog.require('goog.dom.TagWalkType'); +goog.require('goog.iter'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); + +var it; +var pos; + +function assertStartTag(type) { + assertEquals('Position ' + pos + ' should be start tag', + goog.dom.TagWalkType.START_TAG, it.tagType); + assertTrue('isStartTag should return true', it.isStartTag()); + assertFalse('isEndTag should return false', it.isEndTag()); + assertFalse('isNonElement should return false', it.isNonElement()); + assertEquals('Position ' + pos + ' should be ' + type, type, + it.node.tagName); +} + +function assertEndTag(type) { + assertEquals('Position ' + pos + ' should be end tag', + goog.dom.TagWalkType.END_TAG, it.tagType); + assertFalse('isStartTag should return false', it.isStartTag()); + assertTrue('isEndTag should return true', it.isEndTag()); + assertFalse('isNonElement should return false', it.isNonElement()); + assertEquals('Position ' + pos + ' should be ' + type, type, + it.node.tagName); +} + +function assertTextNode(value) { + assertEquals('Position ' + pos + ' should be text node', + goog.dom.TagWalkType.OTHER, it.tagType); + assertFalse('isStartTag should return false', it.isStartTag()); + assertFalse('isEndTag should return false', it.isEndTag()); + assertTrue('isNonElement should return true', it.isNonElement()); + assertEquals('Position ' + pos + ' should be "' + value + '"', value, + it.node.nodeValue); +} + +function testBasicHTML() { + it = new goog.dom.TagIterator(goog.dom.getElement('test')); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertStartTag('A'); + break; + case 3: + assertTextNode('T'); + break; + case 4: + assertStartTag('B'); + assertEquals('Depth at should be 3', 3, it.depth); + break; + case 5: + assertTextNode('e'); + break; + case 6: + assertEndTag('B'); + break; + case 7: + assertTextNode('xt'); + break; + case 8: + assertEndTag('A'); + break; + case 9: + assertStartTag('SPAN'); + break; + case 10: + assertEndTag('SPAN'); + break; + case 11: + assertStartTag('P'); + break; + case 12: + assertTextNode('Text'); + break; + case 13: + assertEndTag('P'); + break; + case 14: + assertEndTag('DIV'); + assertEquals('Depth at end should be 0', 0, it.depth); + break; + default: + throw goog.iter.StopIteration; + } + }); +} + +function testSkipTag() { + it = new goog.dom.TagIterator(goog.dom.getElement('test')); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertStartTag('A'); + it.skipTag(); + break; + case 3: + assertStartTag('SPAN'); + break; + case 4: + assertEndTag('SPAN'); + break; + case 5: + assertStartTag('P'); + break; + case 6: + assertTextNode('Text'); + break; + case 7: + assertEndTag('P'); + break; + case 8: + assertEndTag('DIV'); + assertEquals('Depth at end should be 0', 0, it.depth); + break; + default: + throw goog.iter.StopIteration; + } + }); +} + +function testRestartTag() { + it = new goog.dom.TagIterator(goog.dom.getElement('test')); + pos = 0; + var done = false; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertStartTag('A'); + it.skipTag(); + break; + case 3: + assertStartTag('SPAN'); + break; + case 4: + assertEndTag('SPAN'); + break; + case 5: + assertStartTag('P'); + break; + case 6: + assertTextNode('Text'); + break; + case 7: + assertEndTag('P'); + break; + case 8: + assertEndTag('DIV'); + assertEquals('Depth at end should be 0', 0, it.depth); + + // Do them all again, starting after this element. + if (!done) { + pos = 1; + it.restartTag(); + done = true; + } + break; + default: + throw goog.iter.StopIteration; + } + }); +} + + +function testSkipTagReverse() { + it = new goog.dom.TagIterator(goog.dom.getElement('test'), true); + pos = 9; + + goog.iter.forEach(it, function() { + pos--; + switch (pos) { + case 1: + assertStartTag('DIV'); + assertEquals('Depth at end should be 0', 0, it.depth); + break; + case 2: + assertEndTag('A'); + it.skipTag(); + break; + case 3: + assertStartTag('SPAN'); + break; + case 4: + assertEndTag('SPAN'); + break; + case 5: + assertStartTag('P'); + break; + case 6: + assertTextNode('Text'); + break; + case 7: + assertEndTag('P'); + break; + case 8: + assertEndTag('DIV'); + break; + default: + throw goog.iter.StopIteration; + } + }); +} + + +function testUnclosedLI() { + it = new goog.dom.TagIterator(goog.dom.getElement('test2')); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('UL'); + break; + case 2: + assertStartTag('LI'); + assertEquals('Depth at
  • should be 2', 2, it.depth); + break; + case 3: + assertTextNode('Not'); + break; + case 4: + assertEndTag('LI'); + break; + case 5: + assertStartTag('LI'); + assertEquals('Depth at second
  • should be 2', 2, it.depth); + break; + case 6: + assertTextNode('Closed'); + break; + case 7: + assertEndTag('LI'); + break; + case 8: + assertEndTag('UL'); + assertEquals('Depth at end should be 0', 0, it.depth); + break; + default: + throw goog.iter.StopIteration; + } + }); +} + +function testReversedUnclosedLI() { + it = new goog.dom.TagIterator(goog.dom.getElement('test2'), true); + pos = 9; + + goog.iter.forEach(it, function() { + pos--; + switch (pos) { + case 1: + assertStartTag('UL'); + assertEquals('Depth at start should be 0', 0, it.depth); + break; + case 2: + assertStartTag('LI'); + break; + case 3: + assertTextNode('Not'); + break; + case 4: + assertEndTag('LI'); + assertEquals('Depth at
  • should be 2', 2, it.depth); + break; + case 5: + assertStartTag('LI'); + break; + case 6: + assertTextNode('Closed'); + break; + case 7: + assertEndTag('LI'); + assertEquals('Depth at second
  • should be 2', 2, it.depth); + break; + case 8: + assertEndTag('UL'); + break; + default: + throw goog.iter.StopIteration; + } + }); +} + +function testConstrained() { + it = new goog.dom.TagIterator(goog.dom.getElement('test3'), false, false); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertTextNode('text'); + break; + case 3: + assertEndTag('DIV'); + break; + } + }); + + assertEquals('Constrained iterator should stop at position 3.', 3, pos); +} + +function testUnconstrained() { + it = new goog.dom.TagIterator(goog.dom.getElement('test3'), false, true); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertTextNode('text'); + break; + case 3: + assertEndTag('DIV'); + break; + } + }); + + assertNotEquals('Unonstrained iterator should not stop at position 3.', 3, + pos); +} + +function testConstrainedText() { + it = new goog.dom.TagIterator(goog.dom.getElement('test3').firstChild, + false, false); + pos = 0; + + goog.iter.forEach(it, function() { + pos++; + switch (pos) { + case 1: + assertTextNode('text'); + break; + } + }); + + assertEquals('Constrained text iterator should stop at position 1.', 1, + pos); +} + +function testReverseConstrained() { + it = new goog.dom.TagIterator(goog.dom.getElement('test3'), true, false); + pos = 4; + + goog.iter.forEach(it, function() { + pos--; + switch (pos) { + case 1: + assertStartTag('DIV'); + break; + case 2: + assertTextNode('text'); + break; + case 3: + assertEndTag('DIV'); + break; + } + }); + + assertEquals('Constrained reversed iterator should stop at position 1.', 1, + pos); +} + +function testSpliceRemoveSingleNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = '
    '; + it = new goog.dom.TagIterator(testDiv.firstChild); + + goog.iter.forEach(it, function(node, dummy, i) { + i.splice(); + }); + + assertEquals('Node not removed', 0, testDiv.childNodes.length); +} + +function testSpliceRemoveFirstTextNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'helloworldgoodbye'; + it = new goog.dom.TagIterator(testDiv.firstChild, false, true); + + goog.iter.forEach(it, function(node, dummy, i) { + if (node.nodeType == 3 && node.data == 'hello') { + i.splice(); + } + if (node.nodeName == 'EM') { + i.splice(goog.dom.createDom('I', null, node.childNodes)); + } + }); + + goog.testing.dom.assertHtmlMatches('worldgoodbye', + testDiv.innerHTML); +} + +function testSpliceReplaceFirstTextNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'helloworld'; + it = new goog.dom.TagIterator(testDiv.firstChild, false, true); + + goog.iter.forEach(it, function(node, dummy, i) { + if (node.nodeType == 3 && node.data == 'hello') { + i.splice(goog.dom.createDom('EM', null, 'HELLO')); + } else if (node.nodeName == 'EM') { + i.splice(goog.dom.createDom('I', null, node.childNodes)); + } + }); + + goog.testing.dom.assertHtmlMatches('HELLOworld', + testDiv.innerHTML); +} + +function testSpliceReplaceSingleNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = '
    '; + it = new goog.dom.TagIterator(testDiv.firstChild); + + goog.iter.forEach(it, function(node, dummy, i) { + i.splice(goog.dom.createDom('link'), goog.dom.createDom('img')); + }); + + goog.testing.dom.assertHtmlMatches('', testDiv.innerHTML); +} + +function testSpliceFlattenSingleNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = '
    onetwothree
    '; + it = new goog.dom.TagIterator(testDiv.firstChild); + + goog.iter.forEach(it, function(node, dummy, i) { + i.splice(node.childNodes); + }); + + goog.testing.dom.assertHtmlMatches('onetwothree', + testDiv.innerHTML); +} + +function testSpliceMiddleNode() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'ahelloworldc'; + it = new goog.dom.TagIterator(testDiv); + + goog.iter.forEach(it, function(node, dummy, i) { + if (node.nodeName == 'B') { + i.splice(goog.dom.createDom('IMG')); + } + }); + + goog.testing.dom.assertHtmlMatches('ac', testDiv.innerHTML); +} + +function testSpliceMiddleNodeReversed() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'ahelloworldc'; + it = new goog.dom.TagIterator(testDiv, true); + + goog.iter.forEach(it, function(node, dummy, i) { + if (node.nodeName == 'B') { + i.splice(goog.dom.createDom('IMG')); + } + }); + + goog.testing.dom.assertHtmlMatches('ac', testDiv.innerHTML); +} + +function testSpliceMiddleNodeAtEndTag() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'ahelloworldc'; + it = new goog.dom.TagIterator(testDiv); + + goog.iter.forEach(it, function(node, dummy, i) { + if (node.tagName == 'B' && i.isEndTag()) { + i.splice(goog.dom.createDom('IMG')); + } + }); + + goog.testing.dom.assertHtmlMatches('ac', testDiv.innerHTML); +} + +function testSpliceMultipleNodes() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'this is from IE'; + it = new goog.dom.TagIterator(testDiv); + + goog.iter.forEach(it, function(node, dummy, i) { + var replace = null; + if (node.nodeName == 'STRONG') { + replace = goog.dom.createDom('B', null, node.childNodes); + } else if (node.nodeName == 'EM') { + replace = goog.dom.createDom('I', null, node.childNodes); + } + if (replace) { + i.splice(replace); + } + }); + + goog.testing.dom.assertHtmlMatches('this is from IE', + testDiv.innerHTML); +} + +function testSpliceMultipleNodesAtEnd() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'this is from IE'; + it = new goog.dom.TagIterator(testDiv); + + goog.iter.forEach(it, function(node, dummy, i) { + var replace = null; + if (node.nodeName == 'STRONG' && i.isEndTag()) { + replace = goog.dom.createDom('B', null, node.childNodes); + } else if (node.nodeName == 'EM' && i.isEndTag()) { + replace = goog.dom.createDom('I', null, node.childNodes); + } + if (replace) { + i.splice(replace); + } + }); + + goog.testing.dom.assertHtmlMatches('this is from IE', + testDiv.innerHTML); +} + +function testSpliceMultipleNodesReversed() { + var testDiv = goog.dom.getElement('testSplice'); + testDiv.innerHTML = 'this is from IE'; + it = new goog.dom.TagIterator(testDiv, true); + + goog.iter.forEach(it, function(node, dummy, i) { + var replace = null; + if (node.nodeName == 'STRONG') { + replace = goog.dom.createDom('B', null, node.childNodes); + } else if (node.nodeName == 'EM') { + replace = goog.dom.createDom('I', null, node.childNodes); + } + if (replace) { + i.splice(replace); + } + }); + + goog.testing.dom.assertHtmlMatches('this is from IE', + testDiv.innerHTML); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagname.js b/mapbox-gl/closure-library/closure/goog/dom/tagname.js new file mode 100644 index 0000000000..77a9b475a9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagname.js @@ -0,0 +1,159 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines the goog.dom.TagName enum. This enumerates + * all HTML tag names specified in either the the W3C HTML 4.01 index of + * elements or the HTML5 draft specification. + * + * References: + * http://www.w3.org/TR/html401/index/elements.html + * http://dev.w3.org/html5/spec/section-index.html + * + */ +goog.provide('goog.dom.TagName'); + + +/** + * Enum of all html tag names specified by the W3C HTML4.01 and HTML5 + * specifications. + * @enum {string} + */ +goog.dom.TagName = { + A: 'A', + ABBR: 'ABBR', + ACRONYM: 'ACRONYM', + ADDRESS: 'ADDRESS', + APPLET: 'APPLET', + AREA: 'AREA', + ARTICLE: 'ARTICLE', + ASIDE: 'ASIDE', + AUDIO: 'AUDIO', + B: 'B', + BASE: 'BASE', + BASEFONT: 'BASEFONT', + BDI: 'BDI', + BDO: 'BDO', + BIG: 'BIG', + BLOCKQUOTE: 'BLOCKQUOTE', + BODY: 'BODY', + BR: 'BR', + BUTTON: 'BUTTON', + CANVAS: 'CANVAS', + CAPTION: 'CAPTION', + CENTER: 'CENTER', + CITE: 'CITE', + CODE: 'CODE', + COL: 'COL', + COLGROUP: 'COLGROUP', + COMMAND: 'COMMAND', + DATA: 'DATA', + DATALIST: 'DATALIST', + DD: 'DD', + DEL: 'DEL', + DETAILS: 'DETAILS', + DFN: 'DFN', + DIALOG: 'DIALOG', + DIR: 'DIR', + DIV: 'DIV', + DL: 'DL', + DT: 'DT', + EM: 'EM', + EMBED: 'EMBED', + FIELDSET: 'FIELDSET', + FIGCAPTION: 'FIGCAPTION', + FIGURE: 'FIGURE', + FONT: 'FONT', + FOOTER: 'FOOTER', + FORM: 'FORM', + FRAME: 'FRAME', + FRAMESET: 'FRAMESET', + H1: 'H1', + H2: 'H2', + H3: 'H3', + H4: 'H4', + H5: 'H5', + H6: 'H6', + HEAD: 'HEAD', + HEADER: 'HEADER', + HGROUP: 'HGROUP', + HR: 'HR', + HTML: 'HTML', + I: 'I', + IFRAME: 'IFRAME', + IMG: 'IMG', + INPUT: 'INPUT', + INS: 'INS', + ISINDEX: 'ISINDEX', + KBD: 'KBD', + KEYGEN: 'KEYGEN', + LABEL: 'LABEL', + LEGEND: 'LEGEND', + LI: 'LI', + LINK: 'LINK', + MAP: 'MAP', + MARK: 'MARK', + MATH: 'MATH', + MENU: 'MENU', + META: 'META', + METER: 'METER', + NAV: 'NAV', + NOFRAMES: 'NOFRAMES', + NOSCRIPT: 'NOSCRIPT', + OBJECT: 'OBJECT', + OL: 'OL', + OPTGROUP: 'OPTGROUP', + OPTION: 'OPTION', + OUTPUT: 'OUTPUT', + P: 'P', + PARAM: 'PARAM', + PRE: 'PRE', + PROGRESS: 'PROGRESS', + Q: 'Q', + RP: 'RP', + RT: 'RT', + RUBY: 'RUBY', + S: 'S', + SAMP: 'SAMP', + SCRIPT: 'SCRIPT', + SECTION: 'SECTION', + SELECT: 'SELECT', + SMALL: 'SMALL', + SOURCE: 'SOURCE', + SPAN: 'SPAN', + STRIKE: 'STRIKE', + STRONG: 'STRONG', + STYLE: 'STYLE', + SUB: 'SUB', + SUMMARY: 'SUMMARY', + SUP: 'SUP', + SVG: 'SVG', + TABLE: 'TABLE', + TBODY: 'TBODY', + TD: 'TD', + TEXTAREA: 'TEXTAREA', + TFOOT: 'TFOOT', + TH: 'TH', + THEAD: 'THEAD', + TIME: 'TIME', + TITLE: 'TITLE', + TR: 'TR', + TRACK: 'TRACK', + TT: 'TT', + U: 'U', + UL: 'UL', + VAR: 'VAR', + VIDEO: 'VIDEO', + WBR: 'WBR' +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagname_test.html b/mapbox-gl/closure-library/closure/goog/dom/tagname_test.html new file mode 100644 index 0000000000..5755806ce3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagname_test.html @@ -0,0 +1,22 @@ + + + + + + +Closure Unit Tests - goog.dom.TagName + + + + + dom_test.html relies on this not being empty. + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/tagname_test.js b/mapbox-gl/closure-library/closure/goog/dom/tagname_test.js new file mode 100644 index 0000000000..f0578571f6 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tagname_test.js @@ -0,0 +1,30 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.TagNameTest'); +goog.setTestOnly('goog.dom.TagNameTest'); + +goog.require('goog.dom.TagName'); +goog.require('goog.object'); +goog.require('goog.testing.jsunit'); + +function testCorrectNumberOfTagNames() { + assertEquals(125, goog.object.getCount(goog.dom.TagName)); +} + +function testPropertyNamesEqualValues() { + for (var propertyName in goog.dom.TagName) { + assertEquals(propertyName, goog.dom.TagName[propertyName]); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/tags.js b/mapbox-gl/closure-library/closure/goog/dom/tags.js new file mode 100644 index 0000000000..159abe0cee --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tags.js @@ -0,0 +1,42 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for HTML element tag names. + */ +goog.provide('goog.dom.tags'); + +goog.require('goog.object'); + + +/** + * The void elements specified by + * http://www.w3.org/TR/html-markup/syntax.html#void-elements. + * @const + * @type {!Object} + * @private + */ +goog.dom.tags.VOID_TAGS_ = goog.object.createSet(('area,base,br,col,command,' + + 'embed,hr,img,input,keygen,link,meta,param,source,track,wbr').split(',')); + + +/** + * Checks whether the tag is void (with no contents allowed and no legal end + * tag), for example 'br'. + * @param {string} tagName The tag name in lower case. + * @return {boolean} + */ +goog.dom.tags.isVoidTag = function(tagName) { + return goog.dom.tags.VOID_TAGS_[tagName] === true; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/tags_test.js b/mapbox-gl/closure-library/closure/goog/dom/tags_test.js new file mode 100644 index 0000000000..cf6b624409 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/tags_test.js @@ -0,0 +1,26 @@ +// Copyright 2014 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.tagsTest'); +goog.setTestOnly('goog.dom.tagsTest'); + +goog.require('goog.dom.tags'); +goog.require('goog.testing.jsunit'); + + +function testIsVoidTag() { + assertTrue(goog.dom.tags.isVoidTag('br')); + assertFalse(goog.dom.tags.isVoidTag('a')); + assertFalse(goog.dom.tags.isVoidTag('constructor')); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrange.js b/mapbox-gl/closure-library/closure/goog/dom/textrange.js new file mode 100644 index 0000000000..21ba2118de --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrange.js @@ -0,0 +1,634 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for working with text ranges in HTML documents. + * + * @author robbyw@google.com (Robby Walker) + */ + + +goog.provide('goog.dom.TextRange'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.AbstractRange'); +goog.require('goog.dom.RangeType'); +goog.require('goog.dom.SavedRange'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.TextRangeIterator'); +goog.require('goog.dom.browserrange'); +goog.require('goog.string'); +goog.require('goog.userAgent'); + + + +/** + * Create a new text selection with no properties. Do not use this constructor: + * use one of the goog.dom.Range.createFrom* methods instead. + * @constructor + * @extends {goog.dom.AbstractRange} + * @final + */ +goog.dom.TextRange = function() { +}; +goog.inherits(goog.dom.TextRange, goog.dom.AbstractRange); + + +/** + * Create a new range wrapper from the given browser range object. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Range|TextRange} range The browser range object. + * @param {boolean=} opt_isReversed Whether the focus node is before the anchor + * node. + * @return {!goog.dom.TextRange} A range wrapper object. + */ +goog.dom.TextRange.createFromBrowserRange = function(range, opt_isReversed) { + return goog.dom.TextRange.createFromBrowserRangeWrapper_( + goog.dom.browserrange.createRange(range), opt_isReversed); +}; + + +/** + * Create a new range wrapper from the given browser range wrapper. + * @param {goog.dom.browserrange.AbstractRange} browserRange The browser range + * wrapper. + * @param {boolean=} opt_isReversed Whether the focus node is before the anchor + * node. + * @return {!goog.dom.TextRange} A range wrapper object. + * @private + */ +goog.dom.TextRange.createFromBrowserRangeWrapper_ = function(browserRange, + opt_isReversed) { + var range = new goog.dom.TextRange(); + + // Initialize the range as a browser range wrapper type range. + range.browserRangeWrapper_ = browserRange; + range.isReversed_ = !!opt_isReversed; + + return range; +}; + + +/** + * Create a new range wrapper that selects the given node's text. Do not use + * this method directly - please use goog.dom.Range.createFrom* instead. + * @param {Node} node The node to select. + * @param {boolean=} opt_isReversed Whether the focus node is before the anchor + * node. + * @return {!goog.dom.TextRange} A range wrapper object. + */ +goog.dom.TextRange.createFromNodeContents = function(node, opt_isReversed) { + return goog.dom.TextRange.createFromBrowserRangeWrapper_( + goog.dom.browserrange.createRangeFromNodeContents(node), + opt_isReversed); +}; + + +/** + * Create a new range wrapper that selects the area between the given nodes, + * accounting for the given offsets. Do not use this method directly - please + * use goog.dom.Range.createFrom* instead. + * @param {Node} anchorNode The node to start with. + * @param {number} anchorOffset The offset within the node to start. + * @param {Node} focusNode The node to end with. + * @param {number} focusOffset The offset within the node to end. + * @return {!goog.dom.TextRange} A range wrapper object. + */ +goog.dom.TextRange.createFromNodes = function(anchorNode, anchorOffset, + focusNode, focusOffset) { + var range = new goog.dom.TextRange(); + range.isReversed_ = /** @suppress {missingRequire} */ ( + goog.dom.Range.isReversed(anchorNode, anchorOffset, + focusNode, focusOffset)); + + // Avoid selecting terminal elements directly + if (goog.dom.isElement(anchorNode) && !goog.dom.canHaveChildren(anchorNode)) { + var parent = anchorNode.parentNode; + anchorOffset = goog.array.indexOf(parent.childNodes, anchorNode); + anchorNode = parent; + } + + if (goog.dom.isElement(focusNode) && !goog.dom.canHaveChildren(focusNode)) { + var parent = focusNode.parentNode; + focusOffset = goog.array.indexOf(parent.childNodes, focusNode); + focusNode = parent; + } + + // Initialize the range as a W3C style range. + if (range.isReversed_) { + range.startNode_ = focusNode; + range.startOffset_ = focusOffset; + range.endNode_ = anchorNode; + range.endOffset_ = anchorOffset; + } else { + range.startNode_ = anchorNode; + range.startOffset_ = anchorOffset; + range.endNode_ = focusNode; + range.endOffset_ = focusOffset; + } + + return range; +}; + + +// Representation 1: a browser range wrapper. + + +/** + * The browser specific range wrapper. This can be null if one of the other + * representations of the range is specified. + * @type {goog.dom.browserrange.AbstractRange?} + * @private + */ +goog.dom.TextRange.prototype.browserRangeWrapper_ = null; + + +// Representation 2: two endpoints specified as nodes + offsets + + +/** + * The start node of the range. This can be null if one of the other + * representations of the range is specified. + * @type {Node} + * @private + */ +goog.dom.TextRange.prototype.startNode_ = null; + + +/** + * The start offset of the range. This can be null if one of the other + * representations of the range is specified. + * @type {?number} + * @private + */ +goog.dom.TextRange.prototype.startOffset_ = null; + + +/** + * The end node of the range. This can be null if one of the other + * representations of the range is specified. + * @type {Node} + * @private + */ +goog.dom.TextRange.prototype.endNode_ = null; + + +/** + * The end offset of the range. This can be null if one of the other + * representations of the range is specified. + * @type {?number} + * @private + */ +goog.dom.TextRange.prototype.endOffset_ = null; + + +/** + * Whether the focus node is before the anchor node. + * @type {boolean} + * @private + */ +goog.dom.TextRange.prototype.isReversed_ = false; + + +// Method implementations + + +/** + * @return {!goog.dom.TextRange} A clone of this range. + * @override + */ +goog.dom.TextRange.prototype.clone = function() { + var range = new goog.dom.TextRange(); + range.browserRangeWrapper_ = + this.browserRangeWrapper_ && this.browserRangeWrapper_.clone(); + range.startNode_ = this.startNode_; + range.startOffset_ = this.startOffset_; + range.endNode_ = this.endNode_; + range.endOffset_ = this.endOffset_; + range.isReversed_ = this.isReversed_; + + return range; +}; + + +/** @override */ +goog.dom.TextRange.prototype.getType = function() { + return goog.dom.RangeType.TEXT; +}; + + +/** @override */ +goog.dom.TextRange.prototype.getBrowserRangeObject = function() { + return this.getBrowserRangeWrapper_().getBrowserRange(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.setBrowserRangeObject = function(nativeRange) { + // Test if it's a control range by seeing if a control range only method + // exists. + if (goog.dom.AbstractRange.isNativeControlRange(nativeRange)) { + return false; + } + this.browserRangeWrapper_ = goog.dom.browserrange.createRange( + nativeRange); + this.clearCachedValues_(); + return true; +}; + + +/** + * Clear all cached values. + * @private + */ +goog.dom.TextRange.prototype.clearCachedValues_ = function() { + this.startNode_ = this.startOffset_ = this.endNode_ = this.endOffset_ = null; +}; + + +/** @override */ +goog.dom.TextRange.prototype.getTextRangeCount = function() { + return 1; +}; + + +/** @override */ +goog.dom.TextRange.prototype.getTextRange = function(i) { + return this; +}; + + +/** + * @return {!goog.dom.browserrange.AbstractRange} The range wrapper object. + * @private + */ +goog.dom.TextRange.prototype.getBrowserRangeWrapper_ = function() { + return this.browserRangeWrapper_ || + (this.browserRangeWrapper_ = goog.dom.browserrange.createRangeFromNodes( + this.getStartNode(), this.getStartOffset(), + this.getEndNode(), this.getEndOffset())); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getContainer = function() { + return this.getBrowserRangeWrapper_().getContainer(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getStartNode = function() { + return this.startNode_ || + (this.startNode_ = this.getBrowserRangeWrapper_().getStartNode()); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getStartOffset = function() { + return this.startOffset_ != null ? this.startOffset_ : + (this.startOffset_ = this.getBrowserRangeWrapper_().getStartOffset()); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getStartPosition = function() { + return this.isReversed() ? + this.getBrowserRangeWrapper_().getEndPosition() : + this.getBrowserRangeWrapper_().getStartPosition(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getEndNode = function() { + return this.endNode_ || + (this.endNode_ = this.getBrowserRangeWrapper_().getEndNode()); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getEndOffset = function() { + return this.endOffset_ != null ? this.endOffset_ : + (this.endOffset_ = this.getBrowserRangeWrapper_().getEndOffset()); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getEndPosition = function() { + return this.isReversed() ? + this.getBrowserRangeWrapper_().getStartPosition() : + this.getBrowserRangeWrapper_().getEndPosition(); +}; + + +/** + * Moves a TextRange to the provided nodes and offsets. + * @param {Node} startNode The node to start with. + * @param {number} startOffset The offset within the node to start. + * @param {Node} endNode The node to end with. + * @param {number} endOffset The offset within the node to end. + * @param {boolean} isReversed Whether the range is reversed. + */ +goog.dom.TextRange.prototype.moveToNodes = function(startNode, startOffset, + endNode, endOffset, + isReversed) { + this.startNode_ = startNode; + this.startOffset_ = startOffset; + this.endNode_ = endNode; + this.endOffset_ = endOffset; + this.isReversed_ = isReversed; + this.browserRangeWrapper_ = null; +}; + + +/** @override */ +goog.dom.TextRange.prototype.isReversed = function() { + return this.isReversed_; +}; + + +/** @override */ +goog.dom.TextRange.prototype.containsRange = function(otherRange, + opt_allowPartial) { + var otherRangeType = otherRange.getType(); + if (otherRangeType == goog.dom.RangeType.TEXT) { + return this.getBrowserRangeWrapper_().containsRange( + otherRange.getBrowserRangeWrapper_(), opt_allowPartial); + } else if (otherRangeType == goog.dom.RangeType.CONTROL) { + var elements = otherRange.getElements(); + var fn = opt_allowPartial ? goog.array.some : goog.array.every; + return fn(elements, function(el) { + return this.containsNode(el, opt_allowPartial); + }, this); + } + return false; +}; + + +/** + * Tests if the given node is in a document. + * @param {Node} node The node to check. + * @return {boolean} Whether the given node is in the given document. + */ +goog.dom.TextRange.isAttachedNode = function(node) { + if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { + var returnValue = false; + /** @preserveTry */ + try { + returnValue = node.parentNode; + } catch (e) { + // IE sometimes throws Invalid Argument errors when a node is detached. + // Note: trying to return a value from the above try block can cause IE + // to crash. It is necessary to use the local returnValue + } + return !!returnValue; + } else { + return goog.dom.contains(node.ownerDocument.body, node); + } +}; + + +/** @override */ +goog.dom.TextRange.prototype.isRangeInDocument = function() { + // Ensure any cached nodes are in the document. IE also allows ranges to + // become detached, so we check if the range is still in the document as + // well for IE. + return (!this.startNode_ || + goog.dom.TextRange.isAttachedNode(this.startNode_)) && + (!this.endNode_ || + goog.dom.TextRange.isAttachedNode(this.endNode_)) && + (!(goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) || + this.getBrowserRangeWrapper_().isRangeInDocument()); +}; + + +/** @override */ +goog.dom.TextRange.prototype.isCollapsed = function() { + return this.getBrowserRangeWrapper_().isCollapsed(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getText = function() { + return this.getBrowserRangeWrapper_().getText(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getHtmlFragment = function() { + // TODO(robbyw): Generalize the code in browserrange so it is static and + // just takes an iterator. This would mean we don't always have to create a + // browser range. + return this.getBrowserRangeWrapper_().getHtmlFragment(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getValidHtml = function() { + return this.getBrowserRangeWrapper_().getValidHtml(); +}; + + +/** @override */ +goog.dom.TextRange.prototype.getPastableHtml = function() { + // TODO(robbyw): Get any attributes the table or tr has. + + var html = this.getValidHtml(); + + if (html.match(/^\s*'; + } else if (html.match(/^\s*'; + } else if (html.match(/^\s*'; + } else if (html.match(/^\s*', html, ''); + } + + return html; +}; + + +/** + * Returns a TextRangeIterator over the contents of the range. Regardless of + * the direction of the range, the iterator will move in document order. + * @param {boolean=} opt_keys Unused for this iterator. + * @return {!goog.dom.TextRangeIterator} An iterator over tags in the range. + * @override + */ +goog.dom.TextRange.prototype.__iterator__ = function(opt_keys) { + return new goog.dom.TextRangeIterator(this.getStartNode(), + this.getStartOffset(), this.getEndNode(), this.getEndOffset()); +}; + + +// RANGE ACTIONS + + +/** @override */ +goog.dom.TextRange.prototype.select = function() { + this.getBrowserRangeWrapper_().select(this.isReversed_); +}; + + +/** @override */ +goog.dom.TextRange.prototype.removeContents = function() { + this.getBrowserRangeWrapper_().removeContents(); + this.clearCachedValues_(); +}; + + +/** + * Surrounds the text range with the specified element (on Mozilla) or with a + * clone of the specified element (on IE). Returns a reference to the + * surrounding element if the operation was successful; returns null if the + * operation failed. + * @param {Element} element The element with which the selection is to be + * surrounded. + * @return {Element} The surrounding element (same as the argument on Mozilla, + * but not on IE), or null if unsuccessful. + */ +goog.dom.TextRange.prototype.surroundContents = function(element) { + var output = this.getBrowserRangeWrapper_().surroundContents(element); + this.clearCachedValues_(); + return output; +}; + + +/** @override */ +goog.dom.TextRange.prototype.insertNode = function(node, before) { + var output = this.getBrowserRangeWrapper_().insertNode(node, before); + this.clearCachedValues_(); + return output; +}; + + +/** @override */ +goog.dom.TextRange.prototype.surroundWithNodes = function(startNode, endNode) { + this.getBrowserRangeWrapper_().surroundWithNodes(startNode, endNode); + this.clearCachedValues_(); +}; + + +// SAVE/RESTORE + + +/** @override */ +goog.dom.TextRange.prototype.saveUsingDom = function() { + return new goog.dom.DomSavedTextRange_(this); +}; + + +// RANGE MODIFICATION + + +/** @override */ +goog.dom.TextRange.prototype.collapse = function(toAnchor) { + var toStart = this.isReversed() ? !toAnchor : toAnchor; + + if (this.browserRangeWrapper_) { + this.browserRangeWrapper_.collapse(toStart); + } + + if (toStart) { + this.endNode_ = this.startNode_; + this.endOffset_ = this.startOffset_; + } else { + this.startNode_ = this.endNode_; + this.startOffset_ = this.endOffset_; + } + + // Collapsed ranges can't be reversed + this.isReversed_ = false; +}; + + +// SAVED RANGE OBJECTS + + + +/** + * A SavedRange implementation using DOM endpoints. + * @param {goog.dom.AbstractRange} range The range to save. + * @constructor + * @extends {goog.dom.SavedRange} + * @private + */ +goog.dom.DomSavedTextRange_ = function(range) { + goog.dom.DomSavedTextRange_.base(this, 'constructor'); + + /** + * The anchor node. + * @type {Node} + * @private + */ + this.anchorNode_ = range.getAnchorNode(); + + /** + * The anchor node offset. + * @type {number} + * @private + */ + this.anchorOffset_ = range.getAnchorOffset(); + + /** + * The focus node. + * @type {Node} + * @private + */ + this.focusNode_ = range.getFocusNode(); + + /** + * The focus node offset. + * @type {number} + * @private + */ + this.focusOffset_ = range.getFocusOffset(); +}; +goog.inherits(goog.dom.DomSavedTextRange_, goog.dom.SavedRange); + + +/** + * @return {!goog.dom.AbstractRange} The restored range. + * @override + */ +goog.dom.DomSavedTextRange_.prototype.restoreInternal = function() { + return /** @suppress {missingRequire} */ ( + goog.dom.Range.createFromNodes(this.anchorNode_, this.anchorOffset_, + this.focusNode_, this.focusOffset_)); +}; + + +/** @override */ +goog.dom.DomSavedTextRange_.prototype.disposeInternal = function() { + goog.dom.DomSavedTextRange_.superClass_.disposeInternal.call(this); + + this.anchorNode_ = null; + this.focusNode_ = null; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrange_test.html b/mapbox-gl/closure-library/closure/goog/dom/textrange_test.html new file mode 100644 index 0000000000..0983555d6e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrange_test.html @@ -0,0 +1,46 @@ + + + + + +Closure Unit Tests - goog.dom.TextRange + + + + +
    +
    + +
    + +
    ab
    cd
    + + + + + + + +
    moof
    +
    foobar
    +
    + +
    positiontest
    + +
    positiontest
    + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrange_test.js b/mapbox-gl/closure-library/closure/goog/dom/textrange_test.js new file mode 100644 index 0000000000..d0465b9bbe --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrange_test.js @@ -0,0 +1,333 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.TextRangeTest'); +goog.setTestOnly('goog.dom.TextRangeTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.ControlRange'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TextRange'); +goog.require('goog.math.Coordinate'); +goog.require('goog.style'); +goog.require('goog.testing.ExpectedFailures'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var logo; +var logo2; +var logo3; +var logo3Rtl; +var table; +var table2; +var table2div; +var test3; +var test3Rtl; +var expectedFailures; + +function setUpPage() { + logo = goog.dom.getElement('logo'); + logo2 = goog.dom.getElement('logo2'); + logo3 = goog.dom.getElement('logo3'); + logo3Rtl = goog.dom.getElement('logo3Rtl'); + table = goog.dom.getElement('table'); + table2 = goog.dom.getElement('table2'); + table2div = goog.dom.getElement('table2div'); + test3 = goog.dom.getElement('test3'); + test3Rtl = goog.dom.getElement('test3Rtl'); + expectedFailures = new goog.testing.ExpectedFailures(); +} + +function tearDown() { + expectedFailures.handleTearDown(); +} + +function testCreateFromNodeContents() { + assertNotNull('Text range object can be created for element node', + goog.dom.TextRange.createFromNodeContents(logo)); + assertNotNull('Text range object can be created for text node', + goog.dom.TextRange.createFromNodeContents(logo2.previousSibling)); +} + +function testMoveToNodes() { + var range = goog.dom.TextRange.createFromNodeContents(table2); + range.moveToNodes(table2div, 0, table2div, 1, false); + assertEquals('Range should start in table2div', + table2div, + range.getStartNode()); + assertEquals('Range should end in table2div', + table2div, + range.getEndNode()); + assertEquals('Range start offset should be 0', + 0, + range.getStartOffset()); + assertEquals('Range end offset should be 0', + 1, + range.getEndOffset()); + assertFalse('Range should not be reversed', + range.isReversed()); + range.moveToNodes(table2div, 0, table2div, 1, true); + assertTrue('Range should be reversed', + range.isReversed()); + assertEquals('Range text should be "foo"', + 'foo', + range.getText()); +} + +function testContainsTextRange() { + var range = goog.dom.TextRange.createFromNodeContents(table2); + var range2 = goog.dom.TextRange.createFromNodeContents(table2div); + assertTrue('TextRange contains other TextRange', + range.containsRange(range2)); + assertFalse('TextRange does not contain other TextRange', + range2.containsRange(range)); + + range = goog.dom.Range.createFromNodes( + table2div.firstChild, 1, table2div.lastChild, 1); + range2 = goog.dom.TextRange.createFromNodes( + table2div.firstChild, 0, table2div.lastChild, 0); + assertTrue('TextRange partially contains other TextRange', + range2.containsRange(range, true)); + assertFalse('TextRange does not fully contain other TextRange', + range2.containsRange(range, false)); +} + +function testContainsControlRange() { + if (goog.userAgent.IE) { + var range = goog.dom.ControlRange.createFromElements(table2); + var range2 = goog.dom.TextRange.createFromNodeContents(table2div); + assertFalse('TextRange does not contain ControlRange', + range2.containsRange(range)); + range = goog.dom.ControlRange.createFromElements(logo2); + assertTrue('TextRange contains ControlRange', + range2.containsRange(range)); + range = goog.dom.TextRange.createFromNodeContents(table2); + range2 = goog.dom.ControlRange.createFromElements(logo, logo2); + assertTrue('TextRange partially contains ControlRange', + range2.containsRange(range, true)); + assertFalse('TextRange does not fully contain ControlRange', + range2.containsRange(range, false)); + } +} + +function testGetStartPosition() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + + // The start node is in the top left. + var range = goog.dom.TextRange.createFromNodeContents(test3); + var topLeft = goog.style.getPageOffset(test3.firstChild); + + if (goog.userAgent.IE) { + // On IE the selection is as tall as its tallest element. + var logoPosition = goog.style.getPageOffset(logo3); + topLeft.y = logoPosition.y; + + if (!goog.userAgent.isVersionOrHigher('8')) { + topLeft.x += 2; + topLeft.y += 2; + } + } + + try { + var result = assertNotThrows(goog.bind(range.getStartPosition, range)); + assertObjectEquals(topLeft, result); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testGetStartPositionNotInDocument() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + expectedFailures.expectFailureFor(goog.userAgent.IE && + !goog.userAgent.isVersionOrHigher('8')); + + var range = goog.dom.TextRange.createFromNodeContents(test3); + + goog.dom.removeNode(test3); + try { + var result = assertNotThrows(goog.bind(range.getStartPosition, range)); + assertNull(result); + } catch (e) { + expectedFailures.handleException(e); + } finally { + goog.dom.appendChild(document.body, test3); + } +} + +function testGetStartPositionReversed() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + + // Simulate the user selecting backwards from right-to-left. + // The start node is now in the bottom right. + var firstNode = test3.firstChild.firstChild; + var lastNode = test3.lastChild.lastChild; + var range = goog.dom.TextRange.createFromNodes( + lastNode, lastNode.nodeValue.length, firstNode, 0); + var pageOffset = goog.style.getPageOffset(test3.lastChild); + var bottomRight = new goog.math.Coordinate( + pageOffset.x + test3.lastChild.offsetWidth, + pageOffset.y + test3.lastChild.offsetHeight); + + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { + bottomRight.x += 2; + bottomRight.y += 2; + } + + try { + var result = assertNotThrows(goog.bind(range.getStartPosition, range)); + assertObjectRoughlyEquals(bottomRight, result, 1); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testGetStartPositionRightToLeft() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + + // Even in RTL content the start node is still in the top left. + var range = goog.dom.TextRange.createFromNodeContents(test3Rtl); + var topLeft = goog.style.getPageOffset(test3Rtl.firstChild); + + if (goog.userAgent.IE) { + // On IE the selection is as tall as its tallest element. + var logoPosition = goog.style.getPageOffset(logo3Rtl); + topLeft.y = logoPosition.y; + + if (!goog.userAgent.isVersionOrHigher('8')) { + topLeft.x += 2; + topLeft.y += 2; + } + } + + try { + var result = assertNotThrows(goog.bind(range.getStartPosition, range)); + assertObjectRoughlyEquals(topLeft, result, 0.1); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testGetEndPosition() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + + // The end node is in the bottom right. + var range = goog.dom.TextRange.createFromNodeContents(test3); + var pageOffset = goog.style.getPageOffset(test3.lastChild); + var bottomRight = new goog.math.Coordinate( + pageOffset.x + test3.lastChild.offsetWidth, + pageOffset.y + test3.lastChild.offsetHeight); + + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { + bottomRight.x += 6; + bottomRight.y += 2; + } + + try { + var result = assertNotThrows(goog.bind(range.getEndPosition, range)); + assertObjectRoughlyEquals(bottomRight, result, 1); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testGetEndPositionNotInDocument() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + expectedFailures.expectFailureFor(goog.userAgent.IE && + !goog.userAgent.isVersionOrHigher('8')); + + var range = goog.dom.TextRange.createFromNodeContents(test3); + + goog.dom.removeNode(test3); + try { + var result = assertNotThrows(goog.bind(range.getEndPosition, range)); + assertNull(result); + } catch (e) { + expectedFailures.handleException(e); + } finally { + goog.dom.appendChild(document.body, test3); + } +} + +function testGetEndPositionReversed() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + + // Simulate the user selecting backwards from right-to-left. + // The end node is now in the top left. + var firstNode = test3.firstChild.firstChild; + var lastNode = test3.lastChild.lastChild; + var range = goog.dom.TextRange.createFromNodes( + lastNode, lastNode.nodeValue.length, firstNode, 0); + var topLeft = goog.style.getPageOffset(test3.firstChild); + + if (goog.userAgent.IE) { + // On IE the selection is as tall as its tallest element. + var logoPosition = goog.style.getPageOffset(logo3); + topLeft.y = logoPosition.y; + + if (!goog.userAgent.isVersionOrHigher('8')) { + topLeft.x += 2; + topLeft.y += 2; + } + } + + try { + var result = assertNotThrows(goog.bind(range.getEndPosition, range)); + assertObjectEquals(topLeft, result); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testGetEndPositionRightToLeft() { + expectedFailures.expectFailureFor(goog.userAgent.GECKO && + !goog.userAgent.isVersionOrHigher('2')); + expectedFailures.expectFailureFor(goog.userAgent.IE && + !goog.userAgent.isVersionOrHigher('8')); + + // Even in RTL content the end node is still in the bottom right. + var range = goog.dom.TextRange.createFromNodeContents(test3Rtl); + var pageOffset = goog.style.getPageOffset(test3Rtl.lastChild); + var bottomRight = new goog.math.Coordinate( + pageOffset.x + test3Rtl.lastChild.offsetWidth, + pageOffset.y + test3Rtl.lastChild.offsetHeight); + + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { + bottomRight.x += 2; + bottomRight.y += 2; + } + + try { + var result = assertNotThrows(goog.bind(range.getEndPosition, range)); + assertObjectRoughlyEquals(bottomRight, result, 1); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testCloneRangeDeep() { + var range = goog.dom.TextRange.createFromNodeContents(logo); + assertFalse(range.isCollapsed()); + + var cloned = range.clone(); + cloned.collapse(); + assertTrue(cloned.isCollapsed()); + assertFalse(range.isCollapsed()); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator.js b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator.js new file mode 100644 index 0000000000..7759b37174 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator.js @@ -0,0 +1,246 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Iterator between two DOM text range positions. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.dom.TextRangeIterator'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.RangeIterator'); +goog.require('goog.dom.TagName'); +goog.require('goog.iter.StopIteration'); + + + +/** + * Subclass of goog.dom.TagIterator that iterates over a DOM range. It + * adds functions to determine the portion of each text node that is selected. + * + * @param {Node} startNode The starting node position. + * @param {number} startOffset The offset in to startNode. If startNode is + * an element, indicates an offset in to childNodes. If startNode is a + * text node, indicates an offset in to nodeValue. + * @param {Node} endNode The ending node position. + * @param {number} endOffset The offset in to endNode. If endNode is + * an element, indicates an offset in to childNodes. If endNode is a + * text node, indicates an offset in to nodeValue. + * @param {boolean=} opt_reverse Whether to traverse nodes in reverse. + * @constructor + * @extends {goog.dom.RangeIterator} + * @final + */ +goog.dom.TextRangeIterator = function(startNode, startOffset, endNode, + endOffset, opt_reverse) { + var goNext; + + if (startNode) { + this.startNode_ = startNode; + this.startOffset_ = startOffset; + this.endNode_ = endNode; + this.endOffset_ = endOffset; + + // Skip to the offset nodes - being careful to special case BRs since these + // have no children but still can appear as the startContainer of a range. + if (startNode.nodeType == goog.dom.NodeType.ELEMENT && + startNode.tagName != goog.dom.TagName.BR) { + var startChildren = startNode.childNodes; + var candidate = startChildren[startOffset]; + if (candidate) { + this.startNode_ = candidate; + this.startOffset_ = 0; + } else { + if (startChildren.length) { + this.startNode_ = + /** @type {Node} */ (goog.array.peek(startChildren)); + } + goNext = true; + } + } + + if (endNode.nodeType == goog.dom.NodeType.ELEMENT) { + this.endNode_ = endNode.childNodes[endOffset]; + if (this.endNode_) { + this.endOffset_ = 0; + } else { + // The offset was past the last element. + this.endNode_ = endNode; + } + } + } + + goog.dom.RangeIterator.call(this, opt_reverse ? this.endNode_ : + this.startNode_, opt_reverse); + + if (goNext) { + try { + this.next(); + } catch (e) { + if (e != goog.iter.StopIteration) { + throw e; + } + } + } +}; +goog.inherits(goog.dom.TextRangeIterator, goog.dom.RangeIterator); + + +/** + * The first node in the selection. + * @type {Node} + * @private + */ +goog.dom.TextRangeIterator.prototype.startNode_ = null; + + +/** + * The last node in the selection. + * @type {Node} + * @private + */ +goog.dom.TextRangeIterator.prototype.endNode_ = null; + + +/** + * The offset within the first node in the selection. + * @type {number} + * @private + */ +goog.dom.TextRangeIterator.prototype.startOffset_ = 0; + + +/** + * The offset within the last node in the selection. + * @type {number} + * @private + */ +goog.dom.TextRangeIterator.prototype.endOffset_ = 0; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.getStartTextOffset = function() { + // Offsets only apply to text nodes. If our current node is the start node, + // return the saved offset. Otherwise, return 0. + return this.node.nodeType != goog.dom.NodeType.TEXT ? -1 : + this.node == this.startNode_ ? this.startOffset_ : 0; +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.getEndTextOffset = function() { + // Offsets only apply to text nodes. If our current node is the end node, + // return the saved offset. Otherwise, return the length of the node. + return this.node.nodeType != goog.dom.NodeType.TEXT ? -1 : + this.node == this.endNode_ ? this.endOffset_ : this.node.nodeValue.length; +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.getStartNode = function() { + return this.startNode_; +}; + + +/** + * Change the start node of the iterator. + * @param {Node} node The new start node. + */ +goog.dom.TextRangeIterator.prototype.setStartNode = function(node) { + if (!this.isStarted()) { + this.setPosition(node); + } + + this.startNode_ = node; + this.startOffset_ = 0; +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.getEndNode = function() { + return this.endNode_; +}; + + +/** + * Change the end node of the iterator. + * @param {Node} node The new end node. + */ +goog.dom.TextRangeIterator.prototype.setEndNode = function(node) { + this.endNode_ = node; + this.endOffset_ = 0; +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.isLast = function() { + return this.isStarted() && this.node == this.endNode_ && + (!this.endOffset_ || !this.isStartTag()); +}; + + +/** + * Move to the next position in the selection. + * Throws {@code goog.iter.StopIteration} when it passes the end of the range. + * @return {Node} The node at the next position. + * @override + */ +goog.dom.TextRangeIterator.prototype.next = function() { + if (this.isLast()) { + throw goog.iter.StopIteration; + } + + // Call the super function. + return goog.dom.TextRangeIterator.superClass_.next.call(this); +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.skipTag = function() { + goog.dom.TextRangeIterator.superClass_.skipTag.apply(this); + + // If the node we are skipping contains the end node, we just skipped past + // the end, so we stop the iteration. + if (goog.dom.contains(this.node, this.endNode_)) { + throw goog.iter.StopIteration; + } +}; + + +/** @override */ +goog.dom.TextRangeIterator.prototype.copyFrom = function(other) { + this.startNode_ = other.startNode_; + this.endNode_ = other.endNode_; + this.startOffset_ = other.startOffset_; + this.endOffset_ = other.endOffset_; + this.isReversed_ = other.isReversed_; + + goog.dom.TextRangeIterator.superClass_.copyFrom.call(this, other); +}; + + +/** + * @return {!goog.dom.TextRangeIterator} An identical iterator. + * @override + */ +goog.dom.TextRangeIterator.prototype.clone = function() { + var copy = new goog.dom.TextRangeIterator(this.startNode_, + this.startOffset_, this.endNode_, this.endOffset_, this.isReversed_); + copy.copyFrom(this); + return copy; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.html b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.html new file mode 100644 index 0000000000..5546948729 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.html @@ -0,0 +1,27 @@ + + + + + +goog.dom.TextRangeIterator Tests + + + + + + +
    Text

    Text

    +
      foo
      bar
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.js b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.js new file mode 100644 index 0000000000..af4af6de79 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/textrangeiterator_test.js @@ -0,0 +1,138 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.TextRangeIteratorTest'); +goog.setTestOnly('goog.dom.TextRangeIteratorTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.TextRangeIterator'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); + +var test; +var test2; + +function setUpPage() { + test = goog.dom.getElement('test'); + test2 = goog.dom.getElement('test2'); +} + +function testBasic() { + goog.testing.dom.assertNodesMatch( + new goog.dom.TextRangeIterator(test, 0, test, 2), + ['#a1', 'T', '#b1', 'e', '#b1', 'xt', '#a1', '#span1', + '#span1', '#p1']); +} + +function testAdjustStart() { + var iterator = new goog.dom.TextRangeIterator(test, 0, test, 2); + iterator.setStartNode(goog.dom.getElement('span1')); + + goog.testing.dom.assertNodesMatch(iterator, + ['#span1', '#span1', '#p1']); +} + +function testAdjustEnd() { + var iterator = new goog.dom.TextRangeIterator(test, 0, test, 2); + iterator.setEndNode(goog.dom.getElement('span1')); + + goog.testing.dom.assertNodesMatch(iterator, + ['#a1', 'T', '#b1', 'e', '#b1', 'xt', '#a1', '#span1']); +} + +function testOffsets() { + var iterator = new goog.dom.TextRangeIterator(test2.firstChild, 1, + test2.lastChild, 2); + + // foo + var node = iterator.next(); + assertEquals('Should have start offset at iteration step 1', 1, + iterator.getStartTextOffset()); + assertEquals('Should not have end offset at iteration step 1', + node.nodeValue.length, iterator.getEndTextOffset()); + + //
    + node = iterator.next(); + assertEquals('Should not have start offset at iteration step 2', -1, + iterator.getStartTextOffset()); + assertEquals('Should not have end offset at iteration step 2', -1, + iterator.getEndTextOffset()); + + //
    + node = iterator.next(); + assertEquals('Should not have start offset at iteration step 3', -1, + iterator.getStartTextOffset()); + assertEquals('Should not have end offset at iteration step 3', -1, + iterator.getEndTextOffset()); + + // bar + node = iterator.next(); + assertEquals('Should not have start offset at iteration step 4', 0, + iterator.getStartTextOffset()); + assertEquals('Should have end offset at iteration step 4', 2, + iterator.getEndTextOffset()); +} + +function testSingleNodeOffsets() { + var iterator = new goog.dom.TextRangeIterator(test2.firstChild, 1, + test2.firstChild, 2); + + iterator.next(); + assertEquals('Should have start offset', 1, iterator.getStartTextOffset()); + assertEquals('Should have end offset', 2, iterator.getEndTextOffset()); +} + +function testEndNodeOffsetAtEnd() { + var iterator = new goog.dom.TextRangeIterator( + goog.dom.getElement('b1').firstChild, 0, goog.dom.getElement('b1'), 1); + goog.testing.dom.assertNodesMatch(iterator, ['e', '#b1']); +} + +function testSkipTagDoesNotSkipEnd() { + // Iterate over 'Tex'. + var iterator = new goog.dom.TextRangeIterator( + test.firstChild.firstChild, 0, + test.firstChild.lastChild, 1); + + var node = iterator.next(); + assertEquals('T', node.nodeValue); + + node = iterator.next(); + assertEquals(goog.dom.TagName.B, node.tagName); + + iterator.skipTag(); + + node = iterator.next(); + assertEquals('xt', node.nodeValue); +} + +function testSkipTagSkipsEnd() { + // Iterate over 'Te'. + var iterator = new goog.dom.TextRangeIterator( + test.firstChild.firstChild, 0, + test.getElementsByTagName(goog.dom.TagName.B)[0].firstChild, 1); + + var node = iterator.next(); + assertEquals('T', node.nodeValue); + + node = iterator.next(); + assertEquals(goog.dom.TagName.B, node.tagName); + + var ex = assertThrows('Should stop iteration when skipping B', function() { + iterator.skipTag(); + }); + assertEquals(goog.iter.StopIteration, ex); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/vendor.js b/mapbox-gl/closure-library/closure/goog/dom/vendor.js new file mode 100644 index 0000000000..7c1123ec4a --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/vendor.js @@ -0,0 +1,96 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Vendor prefix getters. + */ + +goog.provide('goog.dom.vendor'); + +goog.require('goog.string'); +goog.require('goog.userAgent'); + + +/** + * Returns the JS vendor prefix used in CSS properties. Different vendors + * use different methods of changing the case of the property names. + * + * @return {?string} The JS vendor prefix or null if there is none. + */ +goog.dom.vendor.getVendorJsPrefix = function() { + if (goog.userAgent.WEBKIT) { + return 'Webkit'; + } else if (goog.userAgent.GECKO) { + return 'Moz'; + } else if (goog.userAgent.IE) { + return 'ms'; + } else if (goog.userAgent.OPERA) { + return 'O'; + } + + return null; +}; + + +/** + * Returns the vendor prefix used in CSS properties. + * + * @return {?string} The vendor prefix or null if there is none. + */ +goog.dom.vendor.getVendorPrefix = function() { + if (goog.userAgent.WEBKIT) { + return '-webkit'; + } else if (goog.userAgent.GECKO) { + return '-moz'; + } else if (goog.userAgent.IE) { + return '-ms'; + } else if (goog.userAgent.OPERA) { + return '-o'; + } + + return null; +}; + + +/** + * @param {string} propertyName A property name. + * @param {!Object=} opt_object If provided, we verify if the property exists in + * the object. + * @return {?string} A vendor prefixed property name, or null if it does not + * exist. + */ +goog.dom.vendor.getPrefixedPropertyName = function(propertyName, opt_object) { + // We first check for a non-prefixed property, if available. + if (opt_object && propertyName in opt_object) { + return propertyName; + } + var prefix = goog.dom.vendor.getVendorJsPrefix(); + if (prefix) { + prefix = prefix.toLowerCase(); + var prefixedPropertyName = prefix + goog.string.toTitleCase(propertyName); + return (!goog.isDef(opt_object) || prefixedPropertyName in opt_object) ? + prefixedPropertyName : null; + } + return null; +}; + + +/** + * @param {string} eventType An event type. + * @return {string} A lower-cased vendor prefixed event type. + */ +goog.dom.vendor.getPrefixedEventType = function(eventType) { + var prefix = goog.dom.vendor.getVendorJsPrefix() || ''; + return (prefix + eventType).toLowerCase(); +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/vendor_test.html b/mapbox-gl/closure-library/closure/goog/dom/vendor_test.html new file mode 100644 index 0000000000..a2df5f5b98 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/vendor_test.html @@ -0,0 +1,19 @@ + + + + + +Closure Unit Tests - goog.dom.vendor + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/vendor_test.js b/mapbox-gl/closure-library/closure/goog/dom/vendor_test.js new file mode 100644 index 0000000000..00dddbfd4d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/vendor_test.js @@ -0,0 +1,273 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.vendorTest'); +goog.setTestOnly('goog.dom.vendorTest'); + +goog.require('goog.array'); +goog.require('goog.dom.vendor'); +goog.require('goog.labs.userAgent.util'); +goog.require('goog.testing.MockUserAgent'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('goog.userAgentTestUtil'); + +var documentMode; +var mockUserAgent; +var propertyReplacer = new goog.testing.PropertyReplacer(); + +function setUp() { + mockUserAgent = new goog.testing.MockUserAgent(); + mockUserAgent.install(); +} + +function tearDown() { + goog.dispose(mockUserAgent); + documentMode = undefined; + propertyReplacer.reset(); +} + +goog.userAgent.getDocumentMode_ = function() { + return documentMode; +}; + + +var UserAgents = { + GECKO: 'GECKO', + IE: 'IE', + OPERA: 'OPERA', + WEBKIT: 'WEBKIT' +}; + + +/** + * Return whether a given user agent has been detected. + * @param {number} agent Value in UserAgents. + * @return {boolean} Whether the user agent has been detected. + */ +function getUserAgentDetected_(agent) { + switch (agent) { + case UserAgents.GECKO: + return goog.userAgent.GECKO; + case UserAgents.IE: + return goog.userAgent.IE; + case UserAgents.OPERA: + return goog.userAgent.OPERA; + case UserAgents.WEBKIT: + return goog.userAgent.WEBKIT; + } + return null; +} + + +/** + * Test browser detection for a user agent configuration. + * @param {Array} expectedAgents Array of expected userAgents. + * @param {string} uaString User agent string. + * @param {string=} opt_product Navigator product string. + * @param {string=} opt_vendor Navigator vendor string. + */ +function assertUserAgent(expectedAgents, uaString, opt_product, opt_vendor) { + var mockNavigator = { + 'userAgent': uaString, + 'product': opt_product, + 'vendor': opt_vendor + }; + + mockUserAgent.setNavigator(mockNavigator); + mockUserAgent.setUserAgentString(uaString); + + // Force reread of navigator.userAgent; + goog.labs.userAgent.util.setUserAgent(null); + goog.userAgentTestUtil.reinitializeUserAgent(); + for (var ua in UserAgents) { + var isExpected = goog.array.contains(expectedAgents, UserAgents[ua]); + assertEquals(isExpected, getUserAgentDetected_(UserAgents[ua])); + } +} + + +/** + * Tests for the vendor prefix for Webkit. + */ +function testVendorPrefixWebkit() { + assertUserAgent([UserAgents.WEBKIT], 'WebKit'); + assertEquals('-webkit', goog.dom.vendor.getVendorPrefix()); +} + + +/** + * Tests for the vendor prefix for Mozilla/Gecko. + */ +function testVendorPrefixGecko() { + assertUserAgent([UserAgents.GECKO], 'Gecko', 'Gecko'); + assertEquals('-moz', goog.dom.vendor.getVendorPrefix()); +} + + +/** + * Tests for the vendor prefix for Opera. + */ +function testVendorPrefixOpera() { + assertUserAgent([UserAgents.OPERA], 'Opera'); + assertEquals('-o', goog.dom.vendor.getVendorPrefix()); +} + + +/** + * Tests for the vendor prefix for IE. + */ +function testVendorPrefixIE() { + assertUserAgent([UserAgents.IE], 'MSIE'); + assertEquals('-ms', goog.dom.vendor.getVendorPrefix()); +} + + +/** + * Tests for the vendor Js prefix for Webkit. + */ +function testVendorJsPrefixWebkit() { + assertUserAgent([UserAgents.WEBKIT], 'WebKit'); + assertEquals('Webkit', goog.dom.vendor.getVendorJsPrefix()); +} + + +/** + * Tests for the vendor Js prefix for Mozilla/Gecko. + */ +function testVendorJsPrefixGecko() { + assertUserAgent([UserAgents.GECKO], 'Gecko', 'Gecko'); + assertEquals('Moz', goog.dom.vendor.getVendorJsPrefix()); +} + + +/** + * Tests for the vendor Js prefix for Opera. + */ +function testVendorJsPrefixOpera() { + assertUserAgent([UserAgents.OPERA], 'Opera'); + assertEquals('O', goog.dom.vendor.getVendorJsPrefix()); +} + + +/** + * Tests for the vendor Js prefix for IE. + */ +function testVendorJsPrefixIE() { + assertUserAgent([UserAgents.IE], 'MSIE'); + assertEquals('ms', goog.dom.vendor.getVendorJsPrefix()); +} + + +/** + * Tests for the vendor Js prefix if no UA detected. + */ +function testVendorJsPrefixNone() { + assertUserAgent([], ''); + assertNull(goog.dom.vendor.getVendorJsPrefix()); +} + + +/** + * Tests for the prefixed property name on Webkit. + */ +function testPrefixedPropertyNameWebkit() { + assertUserAgent([UserAgents.WEBKIT], 'WebKit'); + assertEquals('webkitFoobar', + goog.dom.vendor.getPrefixedPropertyName('foobar')); +} + + +/** + * Tests for the prefixed property name on Webkit in an object. + */ +function testPrefixedPropertyNameWebkitAndObject() { + var mockDocument = { + // setting a value of 0 on purpose, to ensure we only look for property + // names, not their values. + 'webkitFoobar': 0 + }; + assertUserAgent([UserAgents.WEBKIT], 'WebKit'); + assertEquals('webkitFoobar', + goog.dom.vendor.getPrefixedPropertyName('foobar', mockDocument)); +} + + +/** + * Tests for the prefixed property name. + */ +function testPrefixedPropertyName() { + assertUserAgent([], ''); + assertNull(goog.dom.vendor.getPrefixedPropertyName('foobar')); +} + + +/** + * Tests for the prefixed property name in an object. + */ +function testPrefixedPropertyNameAndObject() { + var mockDocument = { + 'foobar': 0 + }; + assertUserAgent([], ''); + assertEquals('foobar', + goog.dom.vendor.getPrefixedPropertyName('foobar', mockDocument)); +} + + +/** + * Tests for the prefixed property name when it doesn't exist. + */ +function testPrefixedPropertyNameAndObjectIsEmpty() { + var mockDocument = {}; + assertUserAgent([], ''); + assertNull(goog.dom.vendor.getPrefixedPropertyName('foobar', mockDocument)); +} + + +/** + * Test for prefixed event type. + */ +function testPrefixedEventType() { + assertUserAgent([], ''); + assertEquals('foobar', goog.dom.vendor.getPrefixedEventType('foobar')); +} + + +/** + * Test for browser-specific prefixed event type. + */ +function testPrefixedEventTypeForBrowser() { + assertUserAgent([UserAgents.WEBKIT], 'WebKit'); + assertEquals('webkitfoobar', goog.dom.vendor.getPrefixedEventType('foobar')); +} + + +function assertIe(uaString, expectedVersion) { + assertUserAgent([UserAgents.IE], uaString); + assertEquals('User agent ' + uaString + ' should have had version ' + + expectedVersion + ' but had ' + goog.userAgent.VERSION, + expectedVersion, + goog.userAgent.VERSION); +} + + +function assertGecko(uaString, expectedVersion) { + assertUserAgent([UserAgents.GECKO], uaString, 'Gecko'); + assertEquals('User agent ' + uaString + ' should have had version ' + + expectedVersion + ' but had ' + goog.userAgent.VERSION, + expectedVersion, + goog.userAgent.VERSION); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor.js b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor.js new file mode 100644 index 0000000000..8e0aedca8f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor.js @@ -0,0 +1,180 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utility class that monitors viewport size changes. + * + * @author attila@google.com (Attila Bodis) + * @see ../demos/viewportsizemonitor.html + */ + +goog.provide('goog.dom.ViewportSizeMonitor'); + +goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Size'); + + + +/** + * This class can be used to monitor changes in the viewport size. Instances + * dispatch a {@link goog.events.EventType.RESIZE} event when the viewport size + * changes. Handlers can call {@link goog.dom.ViewportSizeMonitor#getSize} to + * get the new viewport size. + * + * Use this class if you want to execute resize/reflow logic each time the + * user resizes the browser window. This class is guaranteed to only dispatch + * {@code RESIZE} events when the pixel dimensions of the viewport change. + * (Internet Explorer fires resize events if any element on the page is resized, + * even if the viewport dimensions are unchanged, which can lead to infinite + * resize loops.) + * + * Example usage: + *
    + *    var vsm = new goog.dom.ViewportSizeMonitor();
    + *    goog.events.listen(vsm, goog.events.EventType.RESIZE, function(e) {
    + *      alert('Viewport size changed to ' + vsm.getSize());
    + *    });
    + *  
    + * + * Manually verified on IE6, IE7, FF2, Opera 11, Safari 4 and Chrome. + * + * @param {Window=} opt_window The window to monitor; defaults to the window in + * which this code is executing. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.dom.ViewportSizeMonitor = function(opt_window) { + goog.events.EventTarget.call(this); + + // Default the window to the current window if unspecified. + this.window_ = opt_window || window; + + // Listen for window resize events. + this.listenerKey_ = goog.events.listen(this.window_, + goog.events.EventType.RESIZE, this.handleResize_, false, this); + + // Set the initial size. + this.size_ = goog.dom.getViewportSize(this.window_); +}; +goog.inherits(goog.dom.ViewportSizeMonitor, goog.events.EventTarget); + + +/** + * Returns a viewport size monitor for the given window. A new one is created + * if it doesn't exist already. This prevents the unnecessary creation of + * multiple spooling monitors for a window. + * @param {Window=} opt_window The window to monitor; defaults to the window in + * which this code is executing. + * @return {!goog.dom.ViewportSizeMonitor} Monitor for the given window. + */ +goog.dom.ViewportSizeMonitor.getInstanceForWindow = function(opt_window) { + var currentWindow = opt_window || window; + var uid = goog.getUid(currentWindow); + + return goog.dom.ViewportSizeMonitor.windowInstanceMap_[uid] = + goog.dom.ViewportSizeMonitor.windowInstanceMap_[uid] || + new goog.dom.ViewportSizeMonitor(currentWindow); +}; + + +/** + * Removes and disposes a viewport size monitor for the given window if one + * exists. + * @param {Window=} opt_window The window whose monitor should be removed; + * defaults to the window in which this code is executing. + */ +goog.dom.ViewportSizeMonitor.removeInstanceForWindow = function(opt_window) { + var uid = goog.getUid(opt_window || window); + + goog.dispose(goog.dom.ViewportSizeMonitor.windowInstanceMap_[uid]); + delete goog.dom.ViewportSizeMonitor.windowInstanceMap_[uid]; +}; + + +/** + * Map of window hash code to viewport size monitor for that window, if + * created. + * @type {Object} + * @private + */ +goog.dom.ViewportSizeMonitor.windowInstanceMap_ = {}; + + +/** + * Event listener key for window the window resize handler, as returned by + * {@link goog.events.listen}. + * @type {goog.events.Key} + * @private + */ +goog.dom.ViewportSizeMonitor.prototype.listenerKey_ = null; + + +/** + * The window to monitor. Defaults to the window in which the code is running. + * @type {Window} + * @private + */ +goog.dom.ViewportSizeMonitor.prototype.window_ = null; + + +/** + * The most recently recorded size of the viewport, in pixels. + * @type {goog.math.Size?} + * @private + */ +goog.dom.ViewportSizeMonitor.prototype.size_ = null; + + +/** + * Returns the most recently recorded size of the viewport, in pixels. May + * return null if no window resize event has been handled yet. + * @return {goog.math.Size} The viewport dimensions, in pixels. + */ +goog.dom.ViewportSizeMonitor.prototype.getSize = function() { + // Return a clone instead of the original to preserve encapsulation. + return this.size_ ? this.size_.clone() : null; +}; + + +/** @override */ +goog.dom.ViewportSizeMonitor.prototype.disposeInternal = function() { + goog.dom.ViewportSizeMonitor.superClass_.disposeInternal.call(this); + + if (this.listenerKey_) { + goog.events.unlistenByKey(this.listenerKey_); + this.listenerKey_ = null; + } + + this.window_ = null; + this.size_ = null; +}; + + +/** + * Handles window resize events by measuring the dimensions of the + * viewport and dispatching a {@link goog.events.EventType.RESIZE} event if the + * current dimensions are different from the previous ones. + * @param {goog.events.Event} event The window resize event to handle. + * @private + */ +goog.dom.ViewportSizeMonitor.prototype.handleResize_ = function(event) { + var size = goog.dom.getViewportSize(this.window_); + if (!goog.math.Size.equals(size, this.size_)) { + this.size_ = size; + this.dispatchEvent(goog.events.EventType.RESIZE); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.html b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.html new file mode 100644 index 0000000000..5ec740bc34 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.html @@ -0,0 +1,19 @@ + + + + + +Closure Unit Tests - goog.dom.ViewportSizeMonitor + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.js b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.js new file mode 100644 index 0000000000..aba768ab17 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/viewportsizemonitor_test.js @@ -0,0 +1,137 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.ViewportSizeMonitorTest'); +goog.setTestOnly('goog.dom.ViewportSizeMonitorTest'); + +goog.require('goog.dom.ViewportSizeMonitor'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Size'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); + +var propertyReplacer, fakeWindow, viewportSizeMonitor, mockClock; + + +function FakeWindow() { + goog.base(this); +} +goog.inherits(FakeWindow, goog.events.EventTarget); + + +FakeWindow.prototype.fireResize = function() { + return this.dispatchEvent(new FakeResizeEvent()); +}; + + +function FakeResizeEvent(obj) { + this.type = goog.events.EventType.RESIZE; +} +goog.inherits(FakeResizeEvent, goog.events.Event); + + +function getViewportSize() { + return viewportSize; +} + + +function setViewportSize(w, h, fireEvent) { + this.viewportSize = new goog.math.Size(w, h); + if (fireEvent) { + fakeWindow.fireResize(); + } +} + + +var eventWasFired = {}; +function getListenerFn(id) { + return function() { + propertyReplacer.set(eventWasFired, id, true); + }; +} + + +function listenerWasCalled(id) { + return !!eventWasFired[id]; +} + + +function setUp() { + propertyReplacer = new goog.testing.PropertyReplacer(); + propertyReplacer.set(goog.dom, 'getViewportSize', getViewportSize); + mockClock = new goog.testing.MockClock(); + mockClock.install(); + fakeWindow = new FakeWindow(); + setViewportSize(300, 300); + viewportSizeMonitor = new goog.dom.ViewportSizeMonitor(fakeWindow); +} + + +function tearDown() { + propertyReplacer.reset(); + mockClock.uninstall(); +} + + +function testResizeEvent() { + goog.events.listen(viewportSizeMonitor, goog.events.EventType.RESIZE, + getListenerFn(1)); + assertFalse('Listener should not be called if window was not resized', + listenerWasCalled(1)); + setViewportSize(300, 300, true); + assertFalse('Listener should not be called for bogus resize event', + listenerWasCalled(1)); + setViewportSize(301, 301, true); + assertTrue('Listener should be called for valid resize event', + listenerWasCalled(1)); +} + + +function testInstanceGetter() { + var fakeWindow1 = new FakeWindow(); + var monitor1 = goog.dom.ViewportSizeMonitor.getInstanceForWindow( + fakeWindow1); + var monitor2 = goog.dom.ViewportSizeMonitor.getInstanceForWindow( + fakeWindow1); + assertEquals('The same window should give us the same instance monitor', + monitor1, monitor2); + + var fakeWindow2 = new FakeWindow(); + var monitor3 = goog.dom.ViewportSizeMonitor.getInstanceForWindow( + fakeWindow2); + assertNotEquals('Different windows should give different instances', + monitor1, monitor3); + + assertEquals('Monitors should match if opt_window is not provided', + goog.dom.ViewportSizeMonitor.getInstanceForWindow(), + goog.dom.ViewportSizeMonitor.getInstanceForWindow()); +} + + +function testRemoveInstanceForWindow() { + var fakeWindow1 = new FakeWindow(); + var monitor1 = goog.dom.ViewportSizeMonitor.getInstanceForWindow( + fakeWindow1); + + goog.dom.ViewportSizeMonitor.removeInstanceForWindow(fakeWindow1); + assertTrue(monitor1.isDisposed()); + + var monitor2 = goog.dom.ViewportSizeMonitor.getInstanceForWindow( + fakeWindow1); + assertNotEquals(monitor1, monitor2); +} diff --git a/mapbox-gl/closure-library/closure/goog/dom/xml.js b/mapbox-gl/closure-library/closure/goog/dom/xml.js new file mode 100644 index 0000000000..59f123aff2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/xml.js @@ -0,0 +1,204 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview + * XML utilities. + * + */ + +goog.provide('goog.dom.xml'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); + + +/** + * Max XML size for MSXML2. Used to prevent potential DoS attacks. + * @type {number} + */ +goog.dom.xml.MAX_XML_SIZE_KB = 2 * 1024; // In kB + + +/** + * Max XML size for MSXML2. Used to prevent potential DoS attacks. + * @type {number} + */ +goog.dom.xml.MAX_ELEMENT_DEPTH = 256; // Same default as MSXML6. + + +/** + * Creates an XML document appropriate for the current JS runtime + * @param {string=} opt_rootTagName The root tag name. + * @param {string=} opt_namespaceUri Namespace URI of the document element. + * @return {Document} The new document. + */ +goog.dom.xml.createDocument = function(opt_rootTagName, opt_namespaceUri) { + if (opt_namespaceUri && !opt_rootTagName) { + throw Error("Can't create document with namespace and no root tag"); + } + if (document.implementation && document.implementation.createDocument) { + return document.implementation.createDocument(opt_namespaceUri || '', + opt_rootTagName || '', + null); + } else if (typeof ActiveXObject != 'undefined') { + var doc = goog.dom.xml.createMsXmlDocument_(); + if (doc) { + if (opt_rootTagName) { + doc.appendChild(doc.createNode(goog.dom.NodeType.ELEMENT, + opt_rootTagName, + opt_namespaceUri || '')); + } + return doc; + } + } + throw Error('Your browser does not support creating new documents'); +}; + + +/** + * Creates an XML document from a string + * @param {string} xml The text. + * @return {Document} XML document from the text. + */ +goog.dom.xml.loadXml = function(xml) { + if (typeof DOMParser != 'undefined') { + return new DOMParser().parseFromString(xml, 'application/xml'); + } else if (typeof ActiveXObject != 'undefined') { + var doc = goog.dom.xml.createMsXmlDocument_(); + doc.loadXML(xml); + return doc; + } + throw Error('Your browser does not support loading xml documents'); +}; + + +/** + * Serializes an XML document or subtree to string. + * @param {Document|Element} xml The document or the root node of the subtree. + * @return {string} The serialized XML. + */ +goog.dom.xml.serialize = function(xml) { + // Compatible with Firefox, Opera and WebKit. + if (typeof XMLSerializer != 'undefined') { + return new XMLSerializer().serializeToString(xml); + } + // Compatible with Internet Explorer. + var text = xml.xml; + if (text) { + return text; + } + throw Error('Your browser does not support serializing XML documents'); +}; + + +/** + * Selects a single node using an Xpath expression and a root node + * @param {Node} node The root node. + * @param {string} path Xpath selector. + * @return {Node} The selected node, or null if no matching node. + */ +goog.dom.xml.selectSingleNode = function(node, path) { + if (typeof node.selectSingleNode != 'undefined') { + var doc = goog.dom.getOwnerDocument(node); + if (typeof doc.setProperty != 'undefined') { + doc.setProperty('SelectionLanguage', 'XPath'); + } + return node.selectSingleNode(path); + } else if (document.implementation.hasFeature('XPath', '3.0')) { + var doc = goog.dom.getOwnerDocument(node); + var resolver = doc.createNSResolver(doc.documentElement); + var result = doc.evaluate(path, node, resolver, + XPathResult.FIRST_ORDERED_NODE_TYPE, null); + return result.singleNodeValue; + } + return null; +}; + + +/** + * Selects multiple nodes using an Xpath expression and a root node + * @param {Node} node The root node. + * @param {string} path Xpath selector. + * @return {(NodeList|Array)} The selected nodes, or empty array if no + * matching nodes. + */ +goog.dom.xml.selectNodes = function(node, path) { + if (typeof node.selectNodes != 'undefined') { + var doc = goog.dom.getOwnerDocument(node); + if (typeof doc.setProperty != 'undefined') { + doc.setProperty('SelectionLanguage', 'XPath'); + } + return node.selectNodes(path); + } else if (document.implementation.hasFeature('XPath', '3.0')) { + var doc = goog.dom.getOwnerDocument(node); + var resolver = doc.createNSResolver(doc.documentElement); + var nodes = doc.evaluate(path, node, resolver, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var results = []; + var count = nodes.snapshotLength; + for (var i = 0; i < count; i++) { + results.push(nodes.snapshotItem(i)); + } + return results; + } else { + return []; + } +}; + + +/** + * Sets multiple attributes on an element. Differs from goog.dom.setProperties + * in that it exclusively uses the element's setAttributes method. Use this + * when you need to ensure that the exact property is available as an attribute + * and can be read later by the native getAttribute method. + * @param {!Element} element XML or DOM element to set attributes on. + * @param {!Object} attributes Map of property:value pairs. + */ +goog.dom.xml.setAttributes = function(element, attributes) { + for (var key in attributes) { + if (attributes.hasOwnProperty(key)) { + element.setAttribute(key, attributes[key]); + } + } +}; + + +/** + * Creates an instance of the MSXML2.DOMDocument. + * @return {Document} The new document. + * @private + */ +goog.dom.xml.createMsXmlDocument_ = function() { + var doc = new ActiveXObject('MSXML2.DOMDocument'); + if (doc) { + // Prevent potential vulnerabilities exposed by MSXML2, see + // http://b/1707300 and http://wiki/Main/ISETeamXMLAttacks for details. + doc.resolveExternals = false; + doc.validateOnParse = false; + // Add a try catch block because accessing these properties will throw an + // error on unsupported MSXML versions. This affects Windows machines + // running IE6 or IE7 that are on XP SP2 or earlier without MSXML updates. + // See http://msdn.microsoft.com/en-us/library/ms766391(VS.85).aspx for + // specific details on which MSXML versions support these properties. + try { + doc.setProperty('ProhibitDTD', true); + doc.setProperty('MaxXMLSize', goog.dom.xml.MAX_XML_SIZE_KB); + doc.setProperty('MaxElementDepth', goog.dom.xml.MAX_ELEMENT_DEPTH); + } catch (e) { + // No-op. + } + } + return doc; +}; diff --git a/mapbox-gl/closure-library/closure/goog/dom/xml_test.html b/mapbox-gl/closure-library/closure/goog/dom/xml_test.html new file mode 100644 index 0000000000..254371b863 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/xml_test.html @@ -0,0 +1,19 @@ + + + + + +Closure Unit Tests - goog.dom.xml + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/dom/xml_test.js b/mapbox-gl/closure-library/closure/goog/dom/xml_test.js new file mode 100644 index 0000000000..918a2574e4 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/dom/xml_test.js @@ -0,0 +1,102 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.dom.xmlTest'); +goog.setTestOnly('goog.dom.xmlTest'); + +goog.require('goog.dom.xml'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +function testSerialize() { + var doc = goog.dom.xml.createDocument(); + var node = doc.createElement('root'); + doc.appendChild(node); + + var serializedNode = goog.dom.xml.serialize(node); + assertTrue(//.test(serializedNode)); + + var serializedDoc = goog.dom.xml.serialize(doc); + assertTrue(/(<\?xml version="1.0"\?>)?/.test(serializedDoc)); +} + +function testBelowMaxDepthInIE() { + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) { + // This value is only effective in IE8 and below + goog.dom.xml.MAX_ELEMENT_DEPTH = 5; + var junk = 'Hello'; + var doc = goog.dom.xml.loadXml(junk); + assertEquals('Should not have caused a parse error', 0, + Number(doc.parseError)); + } +} + +function testAboveMaxDepthInIE() { + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) { + // This value is only effective in IE8 and below + goog.dom.xml.MAX_ELEMENT_DEPTH = 4; + var junk = 'Hello'; + var doc = goog.dom.xml.loadXml(junk); + assertNotEquals('Should have caused a parse error', 0, + Number(doc.parseError)); + } +} + +function testBelowMaxSizeInIE() { + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) { + // This value is only effective in IE8 and below + goog.dom.xml.MAX_XML_SIZE_KB = 1; + var junk = '' + new Array(50).join('junk') + ''; + var doc = goog.dom.xml.loadXml(junk); + assertEquals('Should not have caused a parse error', + 0, Number(doc.parseError)); + } +} + +function testMaxSizeInIE() { + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) { + // This value is only effective in IE8 and below + goog.dom.xml.MAX_XML_SIZE_KB = 1; + var junk = '' + new Array(1000).join('junk') + ''; + var doc = goog.dom.xml.loadXml(junk); + assertNotEquals('Should have caused a parse error', 0, + Number(doc.parseError)); + } +} + +function testSetAttributes() { + var xmlElement = goog.dom.xml.createDocument().createElement('root'); + var domElement = document.createElement('div'); + var attrs = { + name: 'test3', + title: 'A title', + random: 'woop', + cellpadding: '123' + }; + + goog.dom.xml.setAttributes(xmlElement, attrs); + goog.dom.xml.setAttributes(domElement, attrs); + + assertEquals('test3', xmlElement.getAttribute('name')); + assertEquals('test3', domElement.getAttribute('name')); + + assertEquals('A title', xmlElement.getAttribute('title')); + assertEquals('A title', domElement.getAttribute('title')); + + assertEquals('woop', xmlElement.getAttribute('random')); + assertEquals('woop', domElement.getAttribute('random')); + + assertEquals('123', xmlElement.getAttribute('cellpadding')); + assertEquals('123', domElement.getAttribute('cellpadding')); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/browserfeature.js b/mapbox-gl/closure-library/closure/goog/editor/browserfeature.js new file mode 100644 index 0000000000..10ac05ee06 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/browserfeature.js @@ -0,0 +1,273 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Trogedit constants for browser features and quirks that should + * be used by the rich text editor. + */ + +goog.provide('goog.editor.BrowserFeature'); + +goog.require('goog.editor.defines'); +goog.require('goog.userAgent'); +goog.require('goog.userAgent.product'); +goog.require('goog.userAgent.product.isVersion'); + + +/** + * Maps browser quirks to boolean values, detailing what the current + * browser supports. + * @const + */ +goog.editor.BrowserFeature = { + // Whether this browser uses the IE TextRange object. + HAS_IE_RANGES: goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9), + + // Whether this browser uses the W3C standard Range object. + // Assumes IE higher versions will be compliance with W3C standard. + HAS_W3C_RANGES: goog.userAgent.GECKO || goog.userAgent.WEBKIT || + goog.userAgent.OPERA || + (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9)), + + // Has the contentEditable attribute, which makes nodes editable. + // + // NOTE(nicksantos): FF3 has contentEditable, but there are 3 major reasons + // why we don't use it: + // 1) In FF3, we listen for key events on the document, and we'd have to + // filter them properly. See TR_Browser.USE_DOCUMENT_FOR_KEY_EVENTS. + // 2) In FF3, we listen for focus/blur events on the document, which + // simply doesn't make sense in contentEditable. focus/blur + // on contentEditable elements still has some quirks, which we're + // talking to Firefox-team about. + // 3) We currently use Mutation events in FF3 to detect changes, + // and these are dispatched on the document only. + // If we ever hope to support FF3/contentEditable, all 3 of these issues + // will need answers. Most just involve refactoring at our end. + HAS_CONTENT_EDITABLE: goog.userAgent.IE || goog.userAgent.WEBKIT || + goog.userAgent.OPERA || + (goog.editor.defines.USE_CONTENTEDITABLE_IN_FIREFOX_3 && + goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9')), + + // Whether to use mutation event types to detect changes + // in the field contents. + USE_MUTATION_EVENTS: goog.userAgent.GECKO, + + // Whether the browser has a functional DOMSubtreeModified event. + // TODO(user): Enable for all FF3 once we're confident this event fires + // reliably. Currently it's only enabled if using contentEditable in FF as + // we have no other choice in that case but to use this event. + HAS_DOM_SUBTREE_MODIFIED_EVENT: goog.userAgent.WEBKIT || + (goog.editor.defines.USE_CONTENTEDITABLE_IN_FIREFOX_3 && + goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9')), + + // Whether nodes can be copied from one document to another + HAS_DOCUMENT_INDEPENDENT_NODES: goog.userAgent.GECKO, + + // Whether the cursor goes before or inside the first block element on + // focus, e.g.,

    foo

    . FF will put the cursor before the + // paragraph on focus, which is wrong. + PUTS_CURSOR_BEFORE_FIRST_BLOCK_ELEMENT_ON_FOCUS: goog.userAgent.GECKO, + + // Whether the selection of one frame is cleared when another frame + // is focused. + CLEARS_SELECTION_WHEN_FOCUS_LEAVES: + goog.userAgent.IE || goog.userAgent.WEBKIT || goog.userAgent.OPERA, + + // Whether "unselectable" is supported as an element style. + HAS_UNSELECTABLE_STYLE: goog.userAgent.GECKO || goog.userAgent.WEBKIT, + + // Whether this browser's "FormatBlock" command does not suck. + FORMAT_BLOCK_WORKS_FOR_BLOCKQUOTES: goog.userAgent.GECKO || + goog.userAgent.WEBKIT || goog.userAgent.OPERA, + + // Whether this browser's "FormatBlock" command may create multiple + // blockquotes. + CREATES_MULTIPLE_BLOCKQUOTES: + (goog.userAgent.WEBKIT && + !goog.userAgent.isVersionOrHigher('534.16')) || + goog.userAgent.OPERA, + + // Whether this browser's "FormatBlock" command will wrap blockquotes + // inside of divs, instead of replacing divs with blockquotes. + WRAPS_BLOCKQUOTE_IN_DIVS: goog.userAgent.OPERA, + + // Whether the readystatechange event is more reliable than load. + PREFERS_READY_STATE_CHANGE_EVENT: goog.userAgent.IE, + + // Whether hitting the tab key will fire a keypress event. + // see http://www.quirksmode.org/js/keys.html + TAB_FIRES_KEYPRESS: !goog.userAgent.IE, + + // Has a standards mode quirk where width=100% doesn't do the right thing, + // but width=99% does. + // TODO(user|user): This should be fixable by less hacky means + NEEDS_99_WIDTH_IN_STANDARDS_MODE: goog.userAgent.IE, + + // Whether keyboard events only reliably fire on the document. + // On Gecko without contentEditable, keyboard events only fire reliably on the + // document element. With contentEditable, the field itself is focusable, + // which means that it will fire key events. This does not apply if + // application is using ContentEditableField or otherwise overriding Field + // not to use an iframe. + USE_DOCUMENT_FOR_KEY_EVENTS: goog.userAgent.GECKO && + !goog.editor.defines.USE_CONTENTEDITABLE_IN_FIREFOX_3, + + // Whether this browser shows non-standard attributes in innerHTML. + SHOWS_CUSTOM_ATTRS_IN_INNER_HTML: goog.userAgent.IE, + + // Whether this browser shrinks empty nodes away to nothing. + // (If so, we need to insert some space characters into nodes that + // shouldn't be collapsed) + COLLAPSES_EMPTY_NODES: + goog.userAgent.GECKO || goog.userAgent.WEBKIT || goog.userAgent.OPERA, + + // Whether we must convert and tags to , . + CONVERT_TO_B_AND_I_TAGS: goog.userAgent.GECKO || goog.userAgent.OPERA, + + // Whether this browser likes to tab through images in contentEditable mode, + // and we like to disable this feature. + TABS_THROUGH_IMAGES: goog.userAgent.IE, + + // Whether this browser unescapes urls when you extract it from the href tag. + UNESCAPES_URLS_WITHOUT_ASKING: goog.userAgent.IE && + !goog.userAgent.isVersionOrHigher('7.0'), + + // Whether this browser supports execCommand("styleWithCSS") to toggle between + // inserting html tags or inline styling for things like bold, italic, etc. + HAS_STYLE_WITH_CSS: + goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.8') || + goog.userAgent.WEBKIT || goog.userAgent.OPERA, + + // Whether clicking on an editable link will take you to that site. + FOLLOWS_EDITABLE_LINKS: goog.userAgent.WEBKIT || + goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9'), + + // Whether this browser has document.activeElement available. + HAS_ACTIVE_ELEMENT: + goog.userAgent.IE || goog.userAgent.OPERA || + goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9'), + + // Whether this browser supports the setCapture method on DOM elements. + HAS_SET_CAPTURE: goog.userAgent.IE, + + // Whether this browser can't set background color when the selection + // is collapsed. + EATS_EMPTY_BACKGROUND_COLOR: goog.userAgent.GECKO || + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('527'), + + // Whether this browser supports the "focusin" or "DOMFocusIn" event + // consistently. + // NOTE(nicksantos): FF supports DOMFocusIn, but doesn't seem to do so + // consistently. + SUPPORTS_FOCUSIN: goog.userAgent.IE || goog.userAgent.OPERA, + + // Whether clicking on an image will cause the selection to move to the image. + // Note: Gecko moves the selection, but it won't always go to the image. + // For example, if the image is wrapped in a div, and you click on the img, + // anchorNode = focusNode = div, anchorOffset = 0, focusOffset = 1, so this + // is another way of "selecting" the image, but there are too many special + // cases like this so we will do the work manually. + SELECTS_IMAGES_ON_CLICK: goog.userAgent.IE || goog.userAgent.OPERA, + + // Whether this browser moves '); + + // + // Hidefocus is needed to ensure that IE7 doesn't show the dotted, focus + // border when you tab into the field. + html.push('', bodyHtml, ''); + + return html.join(''); +}; + + +/** + * Write the initial iframe content in normal mode. + * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about + * the field. + * @param {string} bodyHtml The HTML to insert as the iframe body. + * @param {goog.editor.icontent.FieldStyleInfo?} style Style info about + * the field, if needed. + * @param {HTMLIFrameElement} iframe The iframe. + */ +goog.editor.icontent.writeNormalInitialBlendedIframe = + function(info, bodyHtml, style, iframe) { + // Firefox blended needs to inherit all the css from the original page. + // Firefox standards mode needs to set extra style for images. + if (info.blended_) { + var field = style.wrapper_; + // If there is padding on the original field, then the iFrame will be + // positioned inside the padding by default. We don't want this, as it + // causes the contents to appear to shift, and also causes the + // scrollbars to appear inside the padding. + // + // To compensate, we set the iframe margins to offset the padding. + var paddingBox = goog.style.getPaddingBox(field); + if (paddingBox.top || paddingBox.left || + paddingBox.right || paddingBox.bottom) { + goog.style.setStyle(iframe, 'margin', + (-paddingBox.top) + 'px ' + + (-paddingBox.right) + 'px ' + + (-paddingBox.bottom) + 'px ' + + (-paddingBox.left) + 'px'); + } + } + + goog.editor.icontent.writeNormalInitialIframe( + info, bodyHtml, style, iframe); +}; + + +/** + * Write the initial iframe content in normal mode. + * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about + * the field. + * @param {string} bodyHtml The HTML to insert as the iframe body. + * @param {goog.editor.icontent.FieldStyleInfo?} style Style info about + * the field, if needed. + * @param {HTMLIFrameElement} iframe The iframe. + */ +goog.editor.icontent.writeNormalInitialIframe = + function(info, bodyHtml, style, iframe) { + + var html = goog.editor.icontent.getInitialIframeContent_( + info, bodyHtml, style); + + var doc = goog.dom.getFrameContentDocument(iframe); + doc.open(); + doc.write(html); + doc.close(); +}; + + +/** + * Write the initial iframe content in IE/HTTPS mode. + * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about + * the field. + * @param {Document} doc The iframe document. + * @param {string} bodyHtml The HTML to insert as the iframe body. + */ +goog.editor.icontent.writeHttpsInitialIframe = function(info, doc, bodyHtml) { + var body = doc.body; + + // For HTTPS we already have a document with a doc type and a body element + // and don't want to create a new history entry which can cause data loss if + // the user clicks the back button. + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + body.contentEditable = true; + } + body.className = 'editable'; + body.setAttribute('g_editable', true); + body.hideFocus = true; + body.id = info.fieldId_; + + goog.style.setStyle(body, info.extraStyles_); + body.innerHTML = bodyHtml; +}; + diff --git a/mapbox-gl/closure-library/closure/goog/editor/icontent_test.html b/mapbox-gl/closure-library/closure/goog/editor/icontent_test.html new file mode 100644 index 0000000000..4cd0f89f0b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/icontent_test.html @@ -0,0 +1,36 @@ + + + + + + + + Trogedit Unit Tests - goog.editor.icontent + + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/icontent_test.js b/mapbox-gl/closure-library/closure/goog/editor/icontent_test.js new file mode 100644 index 0000000000..c767f3478e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/icontent_test.js @@ -0,0 +1,216 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.icontentTest'); +goog.setTestOnly('goog.editor.icontentTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.icontent'); +goog.require('goog.editor.icontent.FieldFormatInfo'); +goog.require('goog.editor.icontent.FieldStyleInfo'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var wrapperDiv; +var realIframe; +var realIframeDoc; +var propertyReplacer; + +function setUp() { + wrapperDiv = goog.dom.createDom('div', null, + realIframe = goog.dom.createDom('iframe')); + goog.dom.appendChild(document.body, wrapperDiv); + realIframeDoc = realIframe.contentWindow.document; + propertyReplacer = new goog.testing.PropertyReplacer(); +} + +function tearDown() { + goog.dom.removeNode(wrapperDiv); + propertyReplacer.reset(); +} + +function testWriteHttpsInitialIframeContent() { + // This is not a particularly useful test; it's just a sanity check to make + // sure nothing explodes + var info = + new goog.editor.icontent.FieldFormatInfo('id', false, false, false); + var doc = createMockDocument(); + goog.editor.icontent.writeHttpsInitialIframe(info, doc, 'some html'); + assertBodyCorrect(doc.body, 'id', 'some html'); +} + +function testWriteHttpsInitialIframeContentRtl() { + var info = new goog.editor.icontent.FieldFormatInfo('id', false, false, true); + var doc = createMockDocument(); + goog.editor.icontent.writeHttpsInitialIframe(info, doc, 'some html'); + assertBodyCorrect(doc.body, 'id', 'some html', true); +} + +function testWriteInitialIframeContentBlendedStandardsGrowing() { + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + return; // only executes when using an iframe + } + + var info = new goog.editor.icontent.FieldFormatInfo('id', true, true, false); + var styleInfo = new goog.editor.icontent.FieldStyleInfo(wrapperDiv, + '.MyClass { position: absolute; top: 500px; }'); + var doc = realIframeDoc; + var html = '
    Some Html
    '; + goog.editor.icontent.writeNormalInitialBlendedIframe(info, html, + styleInfo, realIframe); + + assertBodyCorrect(doc.body, 'id', html); + assertEquals('CSS1Compat', doc.compatMode); // standards + assertEquals('auto', doc.documentElement.style.height); // growing + assertEquals('100%', doc.body.style.height); // standards + assertEquals('hidden', doc.body.style.overflowY); // growing + assertEquals('', realIframe.style.position); // no padding on wrapper + + assertEquals(500, doc.body.firstChild.offsetTop); + assert(doc.getElementsByTagName('style')[0].innerHTML.indexOf( + '-moz-force-broken-image-icon') != -1); // standards +} + +function testWriteInitialIframeContentBlendedQuirksFixedRtl() { + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + return; // only executes when using an iframe + } + + var info = new goog.editor.icontent.FieldFormatInfo('id', false, true, true); + var styleInfo = new goog.editor.icontent.FieldStyleInfo(wrapperDiv, ''); + wrapperDiv.style.padding = '2px 5px'; + var doc = realIframeDoc; + var html = 'Some Html'; + goog.editor.icontent.writeNormalInitialBlendedIframe(info, html, + styleInfo, realIframe); + + assertBodyCorrect(doc.body, 'id', html, true); + assertEquals('BackCompat', doc.compatMode); // quirks + assertEquals('100%', doc.documentElement.style.height); // fixed height + assertEquals('auto', doc.body.style.height); // quirks + assertEquals('auto', doc.body.style.overflow); // fixed height + + assertEquals('-2px', realIframe.style.marginTop); + assertEquals('-5px', realIframe.style.marginLeft); + assert(doc.getElementsByTagName('style')[0].innerHTML.indexOf( + '-moz-force-broken-image-icon') == -1); // quirks +} + +function testWhiteboxStandardsFixedRtl() { + var info = new goog.editor.icontent.FieldFormatInfo('id', true, false, true); + var styleInfo = null; + var doc = realIframeDoc; + var html = 'Some Html'; + goog.editor.icontent.writeNormalInitialBlendedIframe(info, html, + styleInfo, realIframe); + assertBodyCorrect(doc.body, 'id', html, true); + + // TODO(nicksantos): on Safari, there's a bug where all written iframes + // are CSS1Compat. It's fixed in the nightlies as of 3/31/08, so remove + // this guard when the latest version of Safari is on the farm. + if (!goog.userAgent.WEBKIT) { + assertEquals('BackCompat', doc.compatMode); // always use quirks in whitebox + } +} + +function testGetInitialIframeContent() { + var info = new goog.editor.icontent.FieldFormatInfo( + 'id', true, false, false); + var styleInfo = null; + var html = 'Some Html'; + propertyReplacer.set(goog.editor.BrowserFeature, + 'HAS_CONTENT_EDITABLE', false); + var htmlOut = goog.editor.icontent.getInitialIframeContent_( + info, html, styleInfo); + assertEquals(/contentEditable/i.test(htmlOut), false); + propertyReplacer.set(goog.editor.BrowserFeature, + 'HAS_CONTENT_EDITABLE', true); + htmlOut = goog.editor.icontent.getInitialIframeContent_( + info, html, styleInfo); + assertEquals(/]+?contentEditable/i.test(htmlOut), true); + assertEquals(/]+?style="[^>"]*min-width:\s*0/i.test(htmlOut), true); + assertEquals(/]+?style="[^>"]*min-width:\s*0/i.test(htmlOut), true); +} + +function testIframeMinWidthOverride() { + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + return; // only executes when using an iframe + } + + var info = new goog.editor.icontent.FieldFormatInfo('id', true, true, false); + var styleInfo = new goog.editor.icontent.FieldStyleInfo(wrapperDiv, + '.MyClass { position: absolute; top: 500px; }'); + var doc = realIframeDoc; + var html = '
    Some Html
    '; + goog.editor.icontent.writeNormalInitialBlendedIframe(info, html, + styleInfo, realIframe); + + // Make sure that the minimum width isn't being inherited from the parent + // document's style. + assertTrue(doc.body.offsetWidth < 700); +} + +function testBlendedStandardsGrowingMatchesComparisonDiv() { + // TODO(nicksantos): If we ever move + // TR_EditableUtil.prototype.makeIframeField_ + // into goog.editor.icontent (and I think we should), we could actually run + // functional tests to ensure that the iframed field matches the dimensions + // of the equivalent uneditable div. Functional tests would help a lot here. +} + + +/** + * Check a given body for the most basic properties that all iframes must have. + * + * @param {Element} body The actual body element + * @param {string} id The expected id + * @param {string} bodyHTML The expected innerHTML + * @param {boolean=} opt_rtl If true, expect RTL directionality + */ +function assertBodyCorrect(body, id, bodyHTML, opt_rtl) { + assertEquals(bodyHTML, body.innerHTML); + // We can't just check + // assert(HAS_CONTENTE_EDITABLE, !!body.contentEditable) since in + // FF 3 we don't currently use contentEditable, but body.contentEditable + // = 'inherit' and !!'inherit' = true. + if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) { + assertEquals('true', String(body.contentEditable)); + } else { + assertNotEquals('true', String(body.contentEditable)); + } + assertContains('editable', body.className.match(/\S+/g)); + assertEquals('true', String(body.getAttribute('g_editable'))); + assertEquals('true', + // IE has bugs with getAttribute('hideFocus'), and + // Webkit has bugs with normal .hideFocus access. + String(goog.userAgent.IE ? body.hideFocus : + body.getAttribute('hideFocus'))); + assertEquals(id, body.id); +} + + +/** + * @return {Object} A mock document + */ +function createMockDocument() { + return { + body: { + setAttribute: function(key, val) { this[key] = val; }, + getAttribute: function(key) { return this[key]; }, + style: { direction: '' } + } + }; +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/link.js b/mapbox-gl/closure-library/closure/goog/editor/link.js new file mode 100644 index 0000000000..72f6c52263 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/link.js @@ -0,0 +1,390 @@ +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A utility class for managing editable links. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.Link'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.node'); +goog.require('goog.editor.range'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.uri.utils'); +goog.require('goog.uri.utils.ComponentIndex'); + + + +/** + * Wrap an editable link. + * @param {HTMLAnchorElement} anchor The anchor element. + * @param {boolean} isNew Whether this is a new link. + * @constructor + * @final + */ +goog.editor.Link = function(anchor, isNew) { + /** + * The link DOM element. + * @type {HTMLAnchorElement} + * @private + */ + this.anchor_ = anchor; + + /** + * Whether this link represents a link just added to the document. + * @type {boolean} + * @private + */ + this.isNew_ = isNew; + + + /** + * Any extra anchors created by the browser from a selection in the same + * operation that created the primary link + * @type {!Array} + * @private + */ + this.extraAnchors_ = []; +}; + + +/** + * @return {HTMLAnchorElement} The anchor element. + */ +goog.editor.Link.prototype.getAnchor = function() { + return this.anchor_; +}; + + +/** + * @return {!Array} The extra anchor elements, if any, + * created by the browser from a selection. + */ +goog.editor.Link.prototype.getExtraAnchors = function() { + return this.extraAnchors_; +}; + + +/** + * @return {string} The inner text for the anchor. + */ +goog.editor.Link.prototype.getCurrentText = function() { + if (!this.currentText_) { + var anchor = this.getAnchor(); + + var leaf = goog.editor.node.getLeftMostLeaf(anchor); + if (leaf.tagName && leaf.tagName == goog.dom.TagName.IMG) { + this.currentText_ = leaf.getAttribute('alt'); + } else { + this.currentText_ = goog.dom.getRawTextContent(this.getAnchor()); + } + } + return this.currentText_; +}; + + +/** + * @return {boolean} Whether the link is new. + */ +goog.editor.Link.prototype.isNew = function() { + return this.isNew_; +}; + + +/** + * Set the url without affecting the isNew() status of the link. + * @param {string} url A URL. + */ +goog.editor.Link.prototype.initializeUrl = function(url) { + this.getAnchor().href = url; +}; + + +/** + * Removes the link, leaving its contents in the document. Note that this + * object will no longer be usable/useful after this call. + */ +goog.editor.Link.prototype.removeLink = function() { + goog.dom.flattenElement(this.anchor_); + this.anchor_ = null; + while (this.extraAnchors_.length) { + goog.dom.flattenElement(/** @type {Element} */(this.extraAnchors_.pop())); + } +}; + + +/** + * Change the link. + * @param {string} newText New text for the link. If the link contains all its + * text in one descendent, newText will only replace the text in that + * one node. Otherwise, we'll change the innerHTML of the whole + * link to newText. + * @param {string} newUrl A new URL. + */ +goog.editor.Link.prototype.setTextAndUrl = function(newText, newUrl) { + var anchor = this.getAnchor(); + anchor.href = newUrl; + + // If the text did not change, don't update link text. + var currentText = this.getCurrentText(); + if (newText != currentText) { + var leaf = goog.editor.node.getLeftMostLeaf(anchor); + + if (leaf.tagName && leaf.tagName == goog.dom.TagName.IMG) { + leaf.setAttribute('alt', newText ? newText : ''); + } else { + if (leaf.nodeType == goog.dom.NodeType.TEXT) { + leaf = leaf.parentNode; + } + + if (goog.dom.getRawTextContent(leaf) != currentText) { + leaf = anchor; + } + + goog.dom.removeChildren(leaf); + var domHelper = goog.dom.getDomHelper(leaf); + goog.dom.appendChild(leaf, domHelper.createTextNode(newText)); + } + + // The text changed, so force getCurrentText to recompute. + this.currentText_ = null; + } + + this.isNew_ = false; +}; + + +/** + * Places the cursor to the right of the anchor. + * Note that this is different from goog.editor.range's placeCursorNextTo + * in that it specifically handles the placement of a cursor in browsers + * that trap you in links, by adding a space when necessary and placing the + * cursor after that space. + */ +goog.editor.Link.prototype.placeCursorRightOf = function() { + var anchor = this.getAnchor(); + // If the browser gets stuck in a link if we place the cursor next to it, + // we'll place the cursor after a space instead. + if (goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS) { + var spaceNode; + var nextSibling = anchor.nextSibling; + + // Check if there is already a space after the link. Only handle the + // simple case - the next node is a text node that starts with a space. + if (nextSibling && + nextSibling.nodeType == goog.dom.NodeType.TEXT && + (goog.string.startsWith(nextSibling.data, goog.string.Unicode.NBSP) || + goog.string.startsWith(nextSibling.data, ' '))) { + spaceNode = nextSibling; + } else { + // If there isn't an obvious space to use, create one after the link. + var dh = goog.dom.getDomHelper(anchor); + spaceNode = dh.createTextNode(goog.string.Unicode.NBSP); + goog.dom.insertSiblingAfter(spaceNode, anchor); + } + + // Move the selection after the space. + var range = goog.dom.Range.createCaret(spaceNode, 1); + range.select(); + } else { + goog.editor.range.placeCursorNextTo(anchor, false); + } +}; + + +/** + * Updates the cursor position and link bubble for this link. + * @param {goog.editor.Field} field The field in which the link is created. + * @param {string} url The link url. + * @private + */ +goog.editor.Link.prototype.updateLinkDisplay_ = function(field, url) { + this.initializeUrl(url); + this.placeCursorRightOf(); + field.execCommand(goog.editor.Command.UPDATE_LINK_BUBBLE); +}; + + +/** + * @return {string?} The modified string for the link if the link + * text appears to be a valid link. Returns null if this is not + * a valid link address. + */ +goog.editor.Link.prototype.getValidLinkFromText = function() { + var text = goog.string.trim(this.getCurrentText()); + if (goog.editor.Link.isLikelyUrl(text)) { + if (text.search(/:/) < 0) { + return 'http://' + goog.string.trimLeft(text); + } + return text; + } else if (goog.editor.Link.isLikelyEmailAddress(text)) { + return 'mailto:' + text; + } + return null; +}; + + +/** + * After link creation, finish creating the link depending on the type + * of link being created. + * @param {goog.editor.Field} field The field where this link is being created. + */ +goog.editor.Link.prototype.finishLinkCreation = function(field) { + var linkFromText = this.getValidLinkFromText(); + if (linkFromText) { + this.updateLinkDisplay_(field, linkFromText); + } else { + field.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, this); + } +}; + + +/** + * Initialize a new link. + * @param {HTMLAnchorElement} anchor The anchor element. + * @param {string} url The initial URL. + * @param {string=} opt_target The target. + * @param {Array=} opt_extraAnchors Extra anchors created + * by the browser when parsing a selection. + * @return {!goog.editor.Link} The link. + */ +goog.editor.Link.createNewLink = function(anchor, url, opt_target, + opt_extraAnchors) { + var link = new goog.editor.Link(anchor, true); + link.initializeUrl(url); + + if (opt_target) { + anchor.target = opt_target; + } + if (opt_extraAnchors) { + link.extraAnchors_ = opt_extraAnchors; + } + + return link; +}; + + +/** + * Initialize a new link using text in anchor, or empty string if there is no + * likely url in the anchor. + * @param {HTMLAnchorElement} anchor The anchor element with likely url content. + * @param {string=} opt_target The target. + * @return {!goog.editor.Link} The link. + */ +goog.editor.Link.createNewLinkFromText = function(anchor, opt_target) { + var link = new goog.editor.Link(anchor, true); + var text = link.getValidLinkFromText(); + link.initializeUrl(text ? text : ''); + if (opt_target) { + anchor.target = opt_target; + } + return link; +}; + + +/** + * Returns true if str could be a URL, false otherwise + * + * Ex: TR_Util.isLikelyUrl_("http://www.google.com") == true + * TR_Util.isLikelyUrl_("www.google.com") == true + * + * @param {string} str String to check if it looks like a URL. + * @return {boolean} Whether str could be a URL. + */ +goog.editor.Link.isLikelyUrl = function(str) { + // Whitespace means this isn't a domain. + if (/\s/.test(str)) { + return false; + } + + if (goog.editor.Link.isLikelyEmailAddress(str)) { + return false; + } + + // Add a scheme if the url doesn't have one - this helps the parser. + var addedScheme = false; + if (!/^[^:\/?#.]+:/.test(str)) { + str = 'http://' + str; + addedScheme = true; + } + + // Parse the domain. + var parts = goog.uri.utils.split(str); + + // Relax the rules for special schemes. + var scheme = parts[goog.uri.utils.ComponentIndex.SCHEME]; + if (goog.array.indexOf(['mailto', 'aim'], scheme) != -1) { + return true; + } + + // Require domains to contain a '.', unless the domain is fully qualified and + // forbids domains from containing invalid characters. + var domain = parts[goog.uri.utils.ComponentIndex.DOMAIN]; + if (!domain || (addedScheme && domain.indexOf('.') == -1) || + (/[^\w\d\-\u0100-\uffff.%]/.test(domain))) { + return false; + } + + // Require http and ftp paths to start with '/'. + var path = parts[goog.uri.utils.ComponentIndex.PATH]; + return !path || path.indexOf('/') == 0; +}; + + +/** + * Regular expression that matches strings that could be an email address. + * @type {RegExp} + * @private + */ +goog.editor.Link.LIKELY_EMAIL_ADDRESS_ = new RegExp( + '^' + // Test from start of string + '[\\w-]+(\\.[\\w-]+)*' + // Dot-delimited alphanumerics and dashes (name) + '\\@' + // @ + '([\\w-]+\\.)+' + // Alphanumerics, dashes and dots (domain) + '(\\d+|\\w\\w+)$', // Domain ends in at least one number or 2 letters + 'i'); + + +/** + * Returns true if str could be an email address, false otherwise + * + * Ex: goog.editor.Link.isLikelyEmailAddress_("some word") == false + * goog.editor.Link.isLikelyEmailAddress_("foo@foo.com") == true + * + * @param {string} str String to test for being email address. + * @return {boolean} Whether "str" looks like an email address. + */ +goog.editor.Link.isLikelyEmailAddress = function(str) { + return goog.editor.Link.LIKELY_EMAIL_ADDRESS_.test(str); +}; + + +/** + * Determines whether or not a url is an email link. + * @param {string} url A url. + * @return {boolean} Whether the url is a mailto link. + */ +goog.editor.Link.isMailto = function(url) { + return !!url && goog.string.startsWith(url, 'mailto:'); +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/link_test.html b/mapbox-gl/closure-library/closure/goog/editor/link_test.html new file mode 100644 index 0000000000..9d1f794c43 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/link_test.html @@ -0,0 +1,27 @@ + + + + + + + + Editor Unit Tests - goog.editor.Link + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/link_test.js b/mapbox-gl/closure-library/closure/goog/editor/link_test.js new file mode 100644 index 0000000000..5362aeda19 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/link_test.js @@ -0,0 +1,290 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.LinkTest'); +goog.setTestOnly('goog.editor.LinkTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Link'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var anchor; + +function setUp() { + anchor = goog.dom.createDom('A'); + document.body.appendChild(anchor); +} + +function tearDown() { + goog.dom.removeNode(anchor); +} + +function testCreateNew() { + var link = new goog.editor.Link(anchor, true); + assertNotNull('Should have created object', link); + assertTrue('Should be new', link.isNew()); + assertEquals('Should have correct anchor', anchor, link.getAnchor()); + assertEquals('Should be empty', '', link.getCurrentText()); +} + +function testCreateNotNew() { + var link = new goog.editor.Link(anchor, false); + assertNotNull('Should have created object', link); + assertFalse('Should not be new', link.isNew()); + assertEquals('Should have correct anchor', anchor, link.getAnchor()); + assertEquals('Should be empty', '', link.getCurrentText()); +} + +function testCreateNewLinkFromText() { + var url = 'http://www.google.com/'; + anchor.innerHTML = url; + var link = goog.editor.Link.createNewLinkFromText(anchor); + assertNotNull('Should have created object', link); + assertEquals('Should have url in anchor', url, anchor.href); +} + +function testCreateNewLinkFromTextLeadingTrailingWhitespace() { + var url = 'http://www.google.com/'; + var urlWithSpaces = ' ' + url + ' '; + anchor.innerHTML = urlWithSpaces; + var urlWithSpacesUpdatedByBrowser = anchor.innerHTML; + var link = goog.editor.Link.createNewLinkFromText(anchor); + assertNotNull('Should have created object', link); + assertEquals('Should have url in anchor', url, anchor.href); + assertEquals('The text should still have spaces', + urlWithSpacesUpdatedByBrowser, link.getCurrentText()); +} + +function testCreateNewLinkFromTextWithAnchor() { + var url = 'https://www.google.com/'; + anchor.innerHTML = url; + var link = goog.editor.Link.createNewLinkFromText(anchor, '_blank'); + assertNotNull('Should have created object', link); + assertEquals('Should have url in anchor', url, anchor.href); + assertEquals('Should have _blank target', '_blank', anchor.target); +} + +function testInitialize() { + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com'); + assertNotNull('Should have created object', link); + assertTrue('Should be new', link.isNew()); + assertEquals('Should have correct anchor', anchor, link.getAnchor()); + assertEquals('Should be empty', '', link.getCurrentText()); +} + +function testInitializeWithTarget() { + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com', + '_blank'); + assertNotNull('Should have created object', link); + assertTrue('Should be new', link.isNew()); + assertEquals('Should have correct anchor', anchor, link.getAnchor()); + assertEquals('Should be empty', '', link.getCurrentText()); + assertEquals('Should have _blank target', '_blank', anchor.target); +} + +function testSetText() { + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com', + '_blank'); + assertEquals('Should be empty', '', link.getCurrentText()); + link.setTextAndUrl('Text', 'http://docs.google.com/'); + assertEquals('Should point to http://docs.google.com/', + 'http://docs.google.com/', anchor.href); + assertEquals('Should have correct text', 'Text', link.getCurrentText()); +} + +function testSetBoldText() { + anchor.innerHTML = ''; + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com', + '_blank'); + assertEquals('Should be empty', '', link.getCurrentText()); + link.setTextAndUrl('Text', 'http://docs.google.com/'); + assertEquals('Should point to http://docs.google.com/', + 'http://docs.google.com/', anchor.href); + assertEquals('Should have correct text', 'Text', link.getCurrentText()); + assertEquals('Should still be bold', goog.dom.TagName.B, + anchor.firstChild.tagName); +} + +function testLinkImgTag() { + anchor.innerHTML = 'alt_txt'; + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com', + '_blank'); + assertEquals('Test getCurrentText', 'alt_txt', link.getCurrentText()); + link.setTextAndUrl('newText', 'http://docs.google.com/'); + assertEquals('Test getCurrentText', 'newText', link.getCurrentText()); + assertEquals('Should point to http://docs.google.com/', + 'http://docs.google.com/', anchor.href); + + assertEquals('Should still have img tag', goog.dom.TagName.IMG, + anchor.firstChild.tagName); + + assertEquals('Alt should equal "newText"', 'newText', + anchor.firstChild.getAttribute('alt')); +} + +function testSetMixed() { + anchor.innerHTML = 'AB'; + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com', + '_blank'); + assertEquals('Should have text: AB', 'AB', link.getCurrentText()); + link.setTextAndUrl('Text', 'http://docs.google.com/'); + assertEquals('Should point to http://docs.google.com/', + 'http://docs.google.com/', anchor.href); + assertEquals('Should have correct text', 'Text', link.getCurrentText()); + assertEquals('Should not be bold', goog.dom.NodeType.TEXT, + anchor.firstChild.nodeType); +} + +function testPlaceCursorRightOf() { + // IE can only do selections properly if the region is editable. + var ed = goog.dom.createDom('div'); + goog.dom.replaceNode(ed, anchor); + ed.contentEditable = true; + ed.appendChild(anchor); + + // In order to test the cursor placement properly, we need to have + // link text. See more details in the test below. + anchor.innerHTML = 'I am text'; + + var link = goog.editor.Link.createNewLink(anchor, 'http://www.google.com'); + link.placeCursorRightOf(); + + var range = goog.dom.Range.createFromWindow(); + assertTrue('Range should be collapsed', range.isCollapsed()); + var startNode = range.getStartNode(); + + if (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528')) { + assertEquals('Selection should be to the right of the anchor', + anchor, startNode.previousSibling); + } else { + // Check that the selection is the "right" place. + // + // If you query the selection, it is actually still inside the anchor, + // but if you type, it types outside the anchor. + // + // Best we can do is test that it is at the end of the anchor text. + assertEquals('Selection should be in anchor text', + anchor.firstChild, startNode); + assertEquals('Selection should be at the end of the text', + anchor.firstChild.length, range.getStartOffset()); + } + + if (ed) { + goog.dom.removeNode(ed); + } +} + +function testIsLikelyUrl() { + var good = [ + // Proper URLs + 'http://google.com', 'http://google.com/', 'http://192.168.1.103', + 'http://www.google.com:8083', 'https://antoine', 'https://foo.foo.net', + 'ftp://google.com:22/', 'http://user@site.com', + 'ftp://user:pass@ftp.site.com', 'http://google.com/search?q=laser%20cats', + 'aim:goim?screenname=en2es', 'mailto:x@y.com', + + // Bad URLs a browser will accept + 'www.google.com', 'www.amazon.co.uk', 'amazon.co.uk', 'foo2.foo3.com', + 'pandora.tv', 'marketing.us', 'del.icio.us', 'bridge-line.com', + 'www.frigid.net:80', 'www.google.com?q=foo', 'www.foo.com/j%20.txt', + 'foodtv.net', 'google.com', 'slashdot.org', '192.168.1.1', + 'justin.edu?kumar something', 'google.com/search?q=hot%20pockets', + + // Due to TLD explosion, these could be URLs either now or soon. + 'ww.jester', 'juicer.fake', 'abs.nonsense.something', 'filename.txt' + ]; + for (var i = 0; i < good.length; i++) { + assertTrue(good[i] + ' should be good', + goog.editor.Link.isLikelyUrl(good[i])); + } + + var bad = [ + // Definitely not URLs + 'bananas', 'http google com', '', 'Sad :/', '*garbage!.123', + 'ftp', 'http', '/', 'https', 'this is', '*!&.banana!*&!', + 'www.jester is gone.com', 'ftp .nospaces.net', 'www_foo_net', + "www.'jester'.net", 'www:8080', + 'www . notnsense.com', 'email@address.com', + + // URL-ish but not quite + ' http://www.google.com', 'http://www.google.com:8081 ', + 'www.google.com foo bar', 'google.com/search?q=not quite' + ]; + + for (i = 0; i < bad.length; i++) { + assertFalse(bad[i] + ' should be bad', + goog.editor.Link.isLikelyUrl(bad[i])); + } +} + +function testIsLikelyEmailAddress() { + var good = [ + // Valid email addresses + 'foo@foo.com', 'foo1@foo2.foo3.com', 'f45_1@goog13.org', 'user@gmail.co.uk', + 'jon-smith@crazy.net', 'roland1@capuchino.gov', 'ernir@gshi.nl', + 'JOON@jno.COM', 'media@meDIa.fREnology.FR', 'john.mail4me@del.icio.us', + 'www9@wc3.madeup1.org', 'hi@192.168.1.103', 'hi@192.168.1.1' + ]; + for (var i = 0; i < good.length; i++) { + assertTrue(goog.editor.Link.isLikelyEmailAddress(good[i])); + } + + var bad = [ + // Malformed/incomplete email addresses + 'user', '@gmail.com', 'user@gmail', 'user@.com', 'user@gmail.c', + 'user@gmail.co.u', '@ya.com', '.@hi3.nl', 'jim.com', + 'ed:@gmail.com', '*!&.banana!*&!', ':jon@gmail.com', + '3g?@bil.com', 'adam be@hi.net', 'john\nsmith@test.com', + "www.'jester'.net", "'james'@covald.net", 'ftp://user@site.com/', + 'aim:goim?screenname=en2es', 'user:pass@site.com', 'user@site.com yay' + ]; + + for (i = 0; i < bad.length; i++) { + assertFalse(goog.editor.Link.isLikelyEmailAddress(bad[i])); + } +} + +function testIsMailToLink() { + assertFalse(goog.editor.Link.isMailto()); + assertFalse(goog.editor.Link.isMailto(null)); + assertFalse(goog.editor.Link.isMailto('')); + assertFalse(goog.editor.Link.isMailto('http://foo.com')); + assertFalse(goog.editor.Link.isMailto('http://mailto:80')); + + assertTrue(goog.editor.Link.isMailto('mailto:')); + assertTrue(goog.editor.Link.isMailto('mailto://')); + assertTrue(goog.editor.Link.isMailto('mailto://ptucker@gmail.com')); +} + +function testGetValidLinkFromText() { + var textLinkPairs = [ + // input text, expected link output + 'www.foo.com', 'http://www.foo.com', + 'user@gmail.com', 'mailto:user@gmail.com', + 'http://www.foo.com', 'http://www.foo.com', + 'https://this.that.edu', 'https://this.that.edu', + 'nothing to see here', null + ]; + var link = new goog.editor.Link(anchor, true); + + for (var i = 0; i < textLinkPairs.length; i += 2) { + link.currentText_ = textLinkPairs[i]; + var result = link.getValidLinkFromText(); + assertEquals(textLinkPairs[i + 1], result); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/node.js b/mapbox-gl/closure-library/closure/goog/editor/node.js new file mode 100644 index 0000000000..006936d1c2 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/node.js @@ -0,0 +1,484 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilties for working with DOM nodes related to rich text + * editing. Many of these are not general enough to go into goog.dom. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.node'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.iter.ChildIterator'); +goog.require('goog.dom.iter.SiblingIterator'); +goog.require('goog.iter'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.userAgent'); + + +/** + * Names of all block-level tags + * @type {Object} + * @private + */ +goog.editor.node.BLOCK_TAG_NAMES_ = goog.object.createSet( + goog.dom.TagName.ADDRESS, + goog.dom.TagName.ARTICLE, + goog.dom.TagName.ASIDE, + goog.dom.TagName.BLOCKQUOTE, + goog.dom.TagName.BODY, + goog.dom.TagName.CAPTION, + goog.dom.TagName.CENTER, + goog.dom.TagName.COL, + goog.dom.TagName.COLGROUP, + goog.dom.TagName.DETAILS, + goog.dom.TagName.DIR, + goog.dom.TagName.DIV, + goog.dom.TagName.DL, + goog.dom.TagName.DD, + goog.dom.TagName.DT, + goog.dom.TagName.FIELDSET, + goog.dom.TagName.FIGCAPTION, + goog.dom.TagName.FIGURE, + goog.dom.TagName.FOOTER, + goog.dom.TagName.FORM, + goog.dom.TagName.H1, + goog.dom.TagName.H2, + goog.dom.TagName.H3, + goog.dom.TagName.H4, + goog.dom.TagName.H5, + goog.dom.TagName.H6, + goog.dom.TagName.HEADER, + goog.dom.TagName.HGROUP, + goog.dom.TagName.HR, + goog.dom.TagName.ISINDEX, + goog.dom.TagName.OL, + goog.dom.TagName.LI, + goog.dom.TagName.MAP, + goog.dom.TagName.MENU, + goog.dom.TagName.NAV, + goog.dom.TagName.OPTGROUP, + goog.dom.TagName.OPTION, + goog.dom.TagName.P, + goog.dom.TagName.PRE, + goog.dom.TagName.SECTION, + goog.dom.TagName.SUMMARY, + goog.dom.TagName.TABLE, + goog.dom.TagName.TBODY, + goog.dom.TagName.TD, + goog.dom.TagName.TFOOT, + goog.dom.TagName.TH, + goog.dom.TagName.THEAD, + goog.dom.TagName.TR, + goog.dom.TagName.UL); + + +/** + * Names of tags that have intrinsic content. + * TODO(robbyw): What about object, br, input, textarea, button, isindex, + * hr, keygen, select, table, tr, td? + * @type {Object} + * @private + */ +goog.editor.node.NON_EMPTY_TAGS_ = goog.object.createSet( + goog.dom.TagName.IMG, goog.dom.TagName.IFRAME, goog.dom.TagName.EMBED); + + +/** + * Check if the node is in a standards mode document. + * @param {Node} node The node to test. + * @return {boolean} Whether the node is in a standards mode document. + */ +goog.editor.node.isStandardsMode = function(node) { + return goog.dom.getDomHelper(node).isCss1CompatMode(); +}; + + +/** + * Get the right-most non-ignorable leaf node of the given node. + * @param {Node} parent The parent ndoe. + * @return {Node} The right-most non-ignorable leaf node. + */ +goog.editor.node.getRightMostLeaf = function(parent) { + var temp; + while (temp = goog.editor.node.getLastChild(parent)) { + parent = temp; + } + return parent; +}; + + +/** + * Get the left-most non-ignorable leaf node of the given node. + * @param {Node} parent The parent ndoe. + * @return {Node} The left-most non-ignorable leaf node. + */ +goog.editor.node.getLeftMostLeaf = function(parent) { + var temp; + while (temp = goog.editor.node.getFirstChild(parent)) { + parent = temp; + } + return parent; +}; + + +/** + * Version of firstChild that skips nodes that are entirely + * whitespace and comments. + * @param {Node} parent The reference node. + * @return {Node} The first child of sibling that is important according to + * goog.editor.node.isImportant, or null if no such node exists. + */ +goog.editor.node.getFirstChild = function(parent) { + return goog.editor.node.getChildHelper_(parent, false); +}; + + +/** + * Version of lastChild that skips nodes that are entirely whitespace or + * comments. (Normally lastChild is a property of all DOM nodes that gives the + * last of the nodes contained directly in the reference node.) + * @param {Node} parent The reference node. + * @return {Node} The last child of sibling that is important according to + * goog.editor.node.isImportant, or null if no such node exists. + */ +goog.editor.node.getLastChild = function(parent) { + return goog.editor.node.getChildHelper_(parent, true); +}; + + +/** + * Version of previoussibling that skips nodes that are entirely + * whitespace or comments. (Normally previousSibling is a property + * of all DOM nodes that gives the sibling node, the node that is + * a child of the same parent, that occurs immediately before the + * reference node.) + * @param {Node} sibling The reference node. + * @return {Node} The closest previous sibling to sibling that is + * important according to goog.editor.node.isImportant, or null if no such + * node exists. + */ +goog.editor.node.getPreviousSibling = function(sibling) { + return /** @type {Node} */ (goog.editor.node.getFirstValue_( + goog.iter.filter(new goog.dom.iter.SiblingIterator(sibling, false, true), + goog.editor.node.isImportant))); +}; + + +/** + * Version of nextSibling that skips nodes that are entirely whitespace or + * comments. + * @param {Node} sibling The reference node. + * @return {Node} The closest next sibling to sibling that is important + * according to goog.editor.node.isImportant, or null if no + * such node exists. + */ +goog.editor.node.getNextSibling = function(sibling) { + return /** @type {Node} */ (goog.editor.node.getFirstValue_( + goog.iter.filter(new goog.dom.iter.SiblingIterator(sibling), + goog.editor.node.isImportant))); +}; + + +/** + * Internal helper for lastChild/firstChild that skips nodes that are entirely + * whitespace or comments. + * @param {Node} parent The reference node. + * @param {boolean} isReversed Whether children should be traversed forward + * or backward. + * @return {Node} The first/last child of sibling that is important according + * to goog.editor.node.isImportant, or null if no such node exists. + * @private + */ +goog.editor.node.getChildHelper_ = function(parent, isReversed) { + return (!parent || parent.nodeType != goog.dom.NodeType.ELEMENT) ? null : + /** @type {Node} */ (goog.editor.node.getFirstValue_(goog.iter.filter( + new goog.dom.iter.ChildIterator( + /** @type {!Element} */ (parent), isReversed), + goog.editor.node.isImportant))); +}; + + +/** + * Utility function that returns the first value from an iterator or null if + * the iterator is empty. + * @param {goog.iter.Iterator} iterator The iterator to get a value from. + * @return {*} The first value from the iterator. + * @private + */ +goog.editor.node.getFirstValue_ = function(iterator) { + /** @preserveTry */ + try { + return iterator.next(); + } catch (e) { + return null; + } +}; + + +/** + * Determine if a node should be returned by the iterator functions. + * @param {Node} node An object implementing the DOM1 Node interface. + * @return {boolean} Whether the node is an element, or a text node that + * is not all whitespace. + */ +goog.editor.node.isImportant = function(node) { + // Return true if the node is not either a TextNode or an ElementNode. + return node.nodeType == goog.dom.NodeType.ELEMENT || + node.nodeType == goog.dom.NodeType.TEXT && + !goog.editor.node.isAllNonNbspWhiteSpace(node); +}; + + +/** + * Determine whether a node's text content is entirely whitespace. + * @param {Node} textNode A node implementing the CharacterData interface (i.e., + * a Text, Comment, or CDATASection node. + * @return {boolean} Whether the text content of node is whitespace, + * otherwise false. + */ +goog.editor.node.isAllNonNbspWhiteSpace = function(textNode) { + return goog.string.isBreakingWhitespace(textNode.nodeValue); +}; + + +/** + * Returns true if the node contains only whitespace and is not and does not + * contain any images, iframes or embed tags. + * @param {Node} node The node to check. + * @param {boolean=} opt_prohibitSingleNbsp By default, this function treats a + * single nbsp as empty. Set this to true to treat this case as non-empty. + * @return {boolean} Whether the node contains only whitespace. + */ +goog.editor.node.isEmpty = function(node, opt_prohibitSingleNbsp) { + var nodeData = goog.dom.getRawTextContent(node); + + if (node.getElementsByTagName) { + for (var tag in goog.editor.node.NON_EMPTY_TAGS_) { + if (node.tagName == tag || node.getElementsByTagName(tag).length > 0) { + return false; + } + } + } + return (!opt_prohibitSingleNbsp && nodeData == goog.string.Unicode.NBSP) || + goog.string.isBreakingWhitespace(nodeData); +}; + + +/** + * Returns the length of the text in node if it is a text node, or the number + * of children of the node, if it is an element. Useful for range-manipulation + * code where you need to know the offset for the right side of the node. + * @param {Node} node The node to get the length of. + * @return {number} The length of the node. + */ +goog.editor.node.getLength = function(node) { + return node.length || node.childNodes.length; +}; + + +/** + * Search child nodes using a predicate function and return the first node that + * satisfies the condition. + * @param {Node} parent The parent node to search. + * @param {function(Node):boolean} hasProperty A function that takes a child + * node as a parameter and returns true if it meets the criteria. + * @return {?number} The index of the node found, or null if no node is found. + */ +goog.editor.node.findInChildren = function(parent, hasProperty) { + for (var i = 0, len = parent.childNodes.length; i < len; i++) { + if (hasProperty(parent.childNodes[i])) { + return i; + } + } + return null; +}; + + +/** + * Search ancestor nodes using a predicate function and returns the topmost + * ancestor in the chain of consecutive ancestors that satisfies the condition. + * + * @param {Node} node The node whose ancestors have to be searched. + * @param {function(Node): boolean} hasProperty A function that takes a parent + * node as a parameter and returns true if it meets the criteria. + * @return {Node} The topmost ancestor or null if no ancestor satisfies the + * predicate function. + */ +goog.editor.node.findHighestMatchingAncestor = function(node, hasProperty) { + var parent = node.parentNode; + var ancestor = null; + while (parent && hasProperty(parent)) { + ancestor = parent; + parent = parent.parentNode; + } + return ancestor; +}; + + +/** +* Checks if node is a block-level html element. The display css + * property is ignored. + * @param {Node} node The node to test. + * @return {boolean} Whether the node is a block-level node. + */ +goog.editor.node.isBlockTag = function(node) { + return !!goog.editor.node.BLOCK_TAG_NAMES_[node.tagName]; +}; + + +/** + * Skips siblings of a node that are empty text nodes. + * @param {Node} node A node. May be null. + * @return {Node} The node or the first sibling of the node that is not an + * empty text node. May be null. + */ +goog.editor.node.skipEmptyTextNodes = function(node) { + while (node && node.nodeType == goog.dom.NodeType.TEXT && + !node.nodeValue) { + node = node.nextSibling; + } + return node; +}; + + +/** + * Checks if an element is a top-level editable container (meaning that + * it itself is not editable, but all its child nodes are editable). + * @param {Node} element The element to test. + * @return {boolean} Whether the element is a top-level editable container. + */ +goog.editor.node.isEditableContainer = function(element) { + return element.getAttribute && + element.getAttribute('g_editable') == 'true'; +}; + + +/** + * Checks if a node is inside an editable container. + * @param {Node} node The node to test. + * @return {boolean} Whether the node is in an editable container. + */ +goog.editor.node.isEditable = function(node) { + return !!goog.dom.getAncestor(node, goog.editor.node.isEditableContainer); +}; + + +/** + * Finds the top-most DOM node inside an editable field that is an ancestor + * (or self) of a given DOM node and meets the specified criteria. + * @param {Node} node The DOM node where the search starts. + * @param {function(Node) : boolean} criteria A function that takes a DOM node + * as a parameter and returns a boolean to indicate whether the node meets + * the criteria or not. + * @return {Node} The DOM node if found, or null. + */ +goog.editor.node.findTopMostEditableAncestor = function(node, criteria) { + var targetNode = null; + while (node && !goog.editor.node.isEditableContainer(node)) { + if (criteria(node)) { + targetNode = node; + } + node = node.parentNode; + } + return targetNode; +}; + + +/** + * Splits off a subtree. + * @param {!Node} currentNode The starting splitting point. + * @param {Node=} opt_secondHalf The initial leftmost leaf the new subtree. + * If null, siblings after currentNode will be placed in the subtree, but + * no additional node will be. + * @param {Node=} opt_root The top of the tree where splitting stops at. + * @return {!Node} The new subtree. + */ +goog.editor.node.splitDomTreeAt = function(currentNode, + opt_secondHalf, opt_root) { + var parent; + while (currentNode != opt_root && (parent = currentNode.parentNode)) { + opt_secondHalf = goog.editor.node.getSecondHalfOfNode_(parent, currentNode, + opt_secondHalf); + currentNode = parent; + } + return /** @type {!Node} */(opt_secondHalf); +}; + + +/** + * Creates a clone of node, moving all children after startNode to it. + * When firstChild is not null or undefined, it is also appended to the clone + * as the first child. + * @param {!Node} node The node to clone. + * @param {!Node} startNode All siblings after this node will be moved to the + * clone. + * @param {Node|undefined} firstChild The first child of the new cloned element. + * @return {!Node} The cloned node that now contains the children after + * startNode. + * @private + */ +goog.editor.node.getSecondHalfOfNode_ = function(node, startNode, firstChild) { + var secondHalf = /** @type {!Node} */(node.cloneNode(false)); + while (startNode.nextSibling) { + goog.dom.appendChild(secondHalf, startNode.nextSibling); + } + if (firstChild) { + secondHalf.insertBefore(firstChild, secondHalf.firstChild); + } + return secondHalf; +}; + + +/** + * Appends all of oldNode's children to newNode. This removes all children from + * oldNode and appends them to newNode. oldNode is left with no children. + * @param {!Node} newNode Node to transfer children to. + * @param {Node} oldNode Node to transfer children from. + * @deprecated Use goog.dom.append directly instead. + */ +goog.editor.node.transferChildren = function(newNode, oldNode) { + goog.dom.append(newNode, oldNode.childNodes); +}; + + +/** + * Replaces the innerHTML of a node. + * + * IE has serious problems if you try to set innerHTML of an editable node with + * any selection. Early versions of IE tear up the old internal tree storage, to + * help avoid ref-counting loops. But this sometimes leaves the selection object + * in a bad state and leads to segfaults. + * + * Removing the nodes first prevents IE from tearing them up. This is not + * strictly necessary in nodes that do not have the selection. You should always + * use this function when setting innerHTML inside of a field. + * + * @param {Node} node A node. + * @param {string} html The innerHTML to set on the node. + */ +goog.editor.node.replaceInnerHtml = function(node, html) { + // Only do this IE. On gecko, we use element change events, and don't + // want to trigger spurious events. + if (goog.userAgent.IE) { + goog.dom.removeChildren(node); + } + node.innerHTML = html; +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/node_test.html b/mapbox-gl/closure-library/closure/goog/editor/node_test.html new file mode 100644 index 0000000000..7f33878f42 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/node_test.html @@ -0,0 +1,29 @@ + + + + + + + + Closure Unit Tests - goog.editor.Node + + + + + +
    + Foo +
    nodeelement
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/node_test.js b/mapbox-gl/closure-library/closure/goog/editor/node_test.js new file mode 100644 index 0000000000..c260793bd1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/node_test.js @@ -0,0 +1,645 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.nodeTest'); +goog.setTestOnly('goog.editor.nodeTest'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.node'); +goog.require('goog.style'); +goog.require('goog.testing.ExpectedFailures'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var expectedFailures; +var parentNode; +var childNode1; +var childNode2; +var childNode3; + +var gChildWsNode1 = null; +var gChildTextNode1 = null; +var gChildNbspNode1 = null; +var gChildMixedNode1 = null; +var gChildWsNode2a = null; +var gChildWsNode2b = null; +var gChildTextNode3a = null; +var gChildWsNode3 = null; +var gChildTextNode3b = null; + +function setUpPage() { + expectedFailures = new goog.testing.ExpectedFailures(); + parentNode = document.getElementById('parentNode'); + childNode1 = parentNode.childNodes[0]; + childNode2 = parentNode.childNodes[1]; + childNode3 = parentNode.childNodes[2]; +} + + +function tearDown() { + expectedFailures.handleTearDown(); +} + +function setUpDomTree() { + gChildWsNode1 = document.createTextNode(' \t\r\n'); + gChildTextNode1 = document.createTextNode('Child node'); + gChildNbspNode1 = document.createTextNode('\u00a0'); + gChildMixedNode1 = document.createTextNode('Text\n plus\u00a0'); + gChildWsNode2a = document.createTextNode(''); + gChildWsNode2b = document.createTextNode(' '); + gChildTextNode3a = document.createTextNode('I am a grand child'); + gChildWsNode3 = document.createTextNode(' \t \r \n'); + gChildTextNode3b = document.createTextNode('I am also a grand child'); + + childNode3.appendChild(gChildTextNode3a); + childNode3.appendChild(gChildWsNode3); + childNode3.appendChild(gChildTextNode3b); + + childNode1.appendChild(gChildMixedNode1); + childNode1.appendChild(gChildWsNode1); + childNode1.appendChild(gChildNbspNode1); + childNode1.appendChild(gChildTextNode1); + + childNode2.appendChild(gChildWsNode2a); + childNode2.appendChild(gChildWsNode2b); + document.body.appendChild(parentNode); +} + +function tearDownDomTree() { + childNode1.innerHTML = childNode2.innerHTML = childNode3.innerHTML = ''; + gChildWsNode1 = null; + gChildTextNode1 = null; + gChildNbspNode1 = null; + gChildMixedNode1 = null; + gChildWsNode2a = null; + gChildWsNode2b = null; + gChildTextNode3a = null; + gChildWsNode3 = null; + gChildTextNode3b = null; +} + +function testGetCompatModeQuirks() { + var quirksIfr = document.createElement('iframe'); + document.body.appendChild(quirksIfr); + // Webkit used to default to standards mode, but fixed this in + // Safari 4/Chrome 2, aka, WebKit 530. + // Also IE10 fails here. + // TODO(johnlenz): IE10+ inherit quirks mode from the owner document + // according to: + // http://msdn.microsoft.com/en-us/library/ff955402(v=vs.85).aspx + // but this test shows different behavior for IE10 and 11. If we discover + // that we care about quirks mode documents we should investigate + // this failure. + expectedFailures.expectFailureFor( + (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530')) || + (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') && + !goog.userAgent.isVersionOrHigher('11'))); + expectedFailures.run(function() { + assertFalse('Empty sourceless iframe is quirks mode, not standards mode', + goog.editor.node.isStandardsMode( + goog.dom.getFrameContentDocument(quirksIfr))); + }); + document.body.removeChild(quirksIfr); +} + +function testGetCompatModeStandards() { + var standardsIfr = document.createElement('iframe'); + document.body.appendChild(standardsIfr); + var doc = goog.dom.getFrameContentDocument(standardsIfr); + doc.open(); + doc.write(' '); + doc.close(); + assertTrue('Iframe with DOCTYPE written in is standards mode', + goog.editor.node.isStandardsMode(doc)); + document.body.removeChild(standardsIfr); +} + + +/** + * Creates a DOM tree and tests that getLeftMostLeaf returns proper node + */ +function testGetLeftMostLeaf() { + setUpDomTree(); + + assertEquals('Should skip ws node', gChildMixedNode1, + goog.editor.node.getLeftMostLeaf(parentNode)); + assertEquals('Should skip ws node', gChildMixedNode1, + goog.editor.node.getLeftMostLeaf(childNode1)); + assertEquals('Has no non ws leaves', childNode2, + goog.editor.node.getLeftMostLeaf(childNode2)); + assertEquals('Should return first child', gChildTextNode3a, + goog.editor.node.getLeftMostLeaf(childNode3)); + assertEquals('Has no children', gChildTextNode1, + goog.editor.node.getLeftMostLeaf(gChildTextNode1)); + + tearDownDomTree(); +} + + +/** + * Creates a DOM tree and tests that getRightMostLeaf returns proper node + */ +function testGetRightMostLeaf() { + setUpDomTree(); + + assertEquals("Should return child3's rightmost child", gChildTextNode3b, + goog.editor.node.getRightMostLeaf(parentNode)); + assertEquals('Should skip ws node', gChildTextNode1, + goog.editor.node.getRightMostLeaf(childNode1)); + assertEquals('Has no non ws leaves', childNode2, + goog.editor.node.getRightMostLeaf(childNode2)); + assertEquals('Should return last child', gChildTextNode3b, + goog.editor.node.getRightMostLeaf(childNode3)); + assertEquals('Has no children', gChildTextNode1, + goog.editor.node.getRightMostLeaf(gChildTextNode1)); + + tearDownDomTree(); +} + + +/** + * Creates a DOM tree and tests that getFirstChild properly ignores + * ignorable nodes + */ +function testGetFirstChild() { + setUpDomTree(); + + assertNull('Has no none ws children', + goog.editor.node.getFirstChild(childNode2)); + assertEquals('Should skip first child, as it is ws', gChildMixedNode1, + goog.editor.node.getFirstChild(childNode1)); + assertEquals('Should just return first child', gChildTextNode3a, + goog.editor.node.getFirstChild(childNode3)); + assertEquals('Should return first child', childNode1, + goog.editor.node.getFirstChild(parentNode)); + + assertNull('First child of a text node should return null', + goog.editor.node.getFirstChild(gChildTextNode1)); + assertNull('First child of null should return null', + goog.editor.node.getFirstChild(null)); + + tearDownDomTree(); +} + + +/** + * Create a DOM tree and test that getLastChild properly ignores + * ignorable nodes + */ +function testGetLastChild() { + setUpDomTree(); + + assertNull('Has no none ws children', + goog.editor.node.getLastChild(childNode2)); + assertEquals('Should skip last child, as it is ws', gChildTextNode1, + goog.editor.node.getLastChild(childNode1)); + assertEquals('Should just return last child', gChildTextNode3b, + goog.editor.node.getLastChild(childNode3)); + assertEquals('Should return last child', childNode3, + goog.editor.node.getLastChild(parentNode)); + + assertNull('Last child of a text node should return null', + goog.editor.node.getLastChild(gChildTextNode1)); + assertNull('Last child of null should return null', + goog.editor.node.getLastChild(gChildTextNode1)); + + tearDownDomTree(); +} + + +/** + * Test if nodes that should be ignorable return false and nodes that should + * not be ignored return true. + */ +function testIsImportant() { + var wsNode = document.createTextNode(' \t\r\n'); + assertFalse('White space node is ignorable', + goog.editor.node.isImportant(wsNode)); + var textNode = document.createTextNode('Hello'); + assertTrue('Text node is important', goog.editor.node.isImportant(textNode)); + var nbspNode = document.createTextNode('\u00a0'); + assertTrue('Node with nbsp is important', + goog.editor.node.isImportant(nbspNode)); + var imageNode = document.createElement('img'); + assertTrue('Image node is important', + goog.editor.node.isImportant(imageNode)); +} + + +/** + * Test that isAllNonNbspWhiteSpace returns true if node contains only + * whitespace that is not nbsp and false otherwise + */ +function testIsAllNonNbspWhiteSpace() { + var wsNode = document.createTextNode(' \t\r\n'); + assertTrue('String is all non nbsp', + goog.editor.node.isAllNonNbspWhiteSpace(wsNode)); + var textNode = document.createTextNode('Hello'); + assertFalse('String should not be whitespace', + goog.editor.node.isAllNonNbspWhiteSpace(textNode)); + var nbspNode = document.createTextNode('\u00a0'); + assertFalse('String has nbsp', + goog.editor.node.isAllNonNbspWhiteSpace(nbspNode)); +} + + +/** + * Creates a DOM tree and Test that getPreviousSibling properly ignores + * ignorable nodes + */ +function testGetPreviousSibling() { + setUpDomTree(); + + assertNull('No previous sibling', + goog.editor.node.getPreviousSibling(gChildTextNode3a)); + assertEquals('Should have text sibling', gChildTextNode3a, + goog.editor.node.getPreviousSibling(gChildWsNode3)); + assertEquals('Should skip over white space sibling', gChildTextNode3a, + goog.editor.node.getPreviousSibling(gChildTextNode3b)); + assertNull('No previous sibling', + goog.editor.node.getPreviousSibling(gChildMixedNode1)); + assertEquals('Should have mixed text sibling', gChildMixedNode1, + goog.editor.node.getPreviousSibling(gChildWsNode1)); + assertEquals('Should skip over white space sibling', gChildMixedNode1, + goog.editor.node.getPreviousSibling(gChildNbspNode1)); + assertNotEquals('Should not move past ws and nbsp', gChildMixedNode1, + goog.editor.node.getPreviousSibling(gChildTextNode1)); + assertEquals('Should go to child 2', childNode2, + goog.editor.node.getPreviousSibling(childNode3)); + assertEquals('Should go to child 1', childNode1, + goog.editor.node.getPreviousSibling(childNode2)); + assertNull('Only has white space siblings', + goog.editor.node.getPreviousSibling(gChildWsNode2b)); + + tearDownDomTree(); +} + + +/** + * Creates a DOM tree and tests that getNextSibling properly ignores igrnorable + * nodes when determining the next sibling + */ +function testGetNextSibling() { + setUpDomTree(); + + assertEquals('Child 1 should have Child 2', childNode2, + goog.editor.node.getNextSibling(childNode1)); + assertEquals('Child 2 should have child 3', childNode3, + goog.editor.node.getNextSibling(childNode2)); + assertNull('Child 3 has no next sibling', + goog.editor.node.getNextSibling(childNode3)); + assertNotEquals('Should not skip ws and nbsp nodes', gChildTextNode1, + goog.editor.node.getNextSibling(gChildMixedNode1)); + assertNotEquals('Should not skip nbsp node', gChildTextNode1, + goog.editor.node.getNextSibling(gChildWsNode1)); + assertEquals('Should have sibling', gChildTextNode1, + goog.editor.node.getNextSibling(gChildNbspNode1)); + assertNull('Should have no next sibling', + goog.editor.node.getNextSibling(gChildTextNode1)); + assertNull('Only has ws sibling', + goog.editor.node.getNextSibling(gChildWsNode2a)); + assertNull('Has no next sibling', + goog.editor.node.getNextSibling(gChildWsNode2b)); + assertEquals('Should skip ws node', gChildTextNode3b, + goog.editor.node.getNextSibling(gChildTextNode3a)); + + tearDownDomTree(); +} + + +function testIsEmpty() { + var textNode = document.createTextNode(''); + assertTrue('Text node with no content should be empty', + goog.editor.node.isEmpty(textNode)); + textNode.data = '\xa0'; + assertTrue('Text node with nbsp should be empty', + goog.editor.node.isEmpty(textNode)); + assertFalse('Text node with nbsp should not be empty when prohibited', + goog.editor.node.isEmpty(textNode, true)); + + textNode.data = ' '; + assertTrue('Text node with whitespace should be empty', + goog.editor.node.isEmpty(textNode)); + textNode.data = 'notEmpty'; + assertFalse('Text node with text should not be empty', + goog.editor.node.isEmpty(textNode)); + + var div = document.createElement('div'); + assertTrue('Empty div should be empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = ''; + assertFalse('Div containing an iframe is not empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = ''; + assertFalse('Div containing an image is not empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = ''; + assertFalse('Div containing an embed is not empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = '
    '; + assertTrue('Div containing other empty tags is empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = '
    '; + assertTrue('Div containing other empty tags and whitespace is empty', + goog.editor.node.isEmpty(div)); + div.innerHTML = '
    Not empty
    '; + assertFalse('Div containing tags and text is not empty', + goog.editor.node.isEmpty(div)); + + var img = document.createElement(goog.dom.TagName.IMG); + assertFalse('Empty img should not be empty', + goog.editor.node.isEmpty(img)); + + var iframe = document.createElement(goog.dom.TagName.IFRAME); + assertFalse('Empty iframe should not be empty', + goog.editor.node.isEmpty(iframe)); + + var embed = document.createElement('embed'); + assertFalse('Empty embed should not be empty', + goog.editor.node.isEmpty(embed)); +} + + +/** + * Test that getLength returns 0 if the node has no length and no children, + * the # of children if the node has no length but does have children, + * and the length of the node if the node does have length + */ +function testGetLength() { + var parentNode = document.createElement('p'); + + assertEquals('Length 0 and no children', 0, + goog.editor.node.getLength(parentNode)); + + var childNode1 = document.createTextNode('node 1'); + var childNode2 = document.createTextNode('node number 2'); + var childNode3 = document.createTextNode(''); + parentNode.appendChild(childNode1); + parentNode.appendChild(childNode2); + parentNode.appendChild(childNode3); + assertEquals('Length 0 and 3 children', 3, + goog.editor.node.getLength(parentNode)); + assertEquals('Text node, length 6', 6, + goog.editor.node.getLength(childNode1)); + assertEquals('Text node, length 0', 0, + goog.editor.node.getLength(childNode3)); +} + +function testFindInChildrenSuccess() { + var parentNode = document.createElement('div'); + parentNode.innerHTML = '
    foo
    foo2'; + + var index = goog.editor.node.findInChildren(parentNode, + function(node) { + return node.tagName == 'B'; + }); + assertEquals('Should find second child', index, 1); +} + +function testFindInChildrenFailure() { + var parentNode = document.createElement('div'); + parentNode.innerHTML = '
    foo
    foo2'; + + var index = goog.editor.node.findInChildren(parentNode, + function(node) { + return false; + }); + assertNull("Shouldn't find a child", index); +} + +function testFindHighestMatchingAncestor() { + setUpDomTree(); + var predicateFunc = function(node) { + return node.tagName == 'DIV'; + }; + var node = goog.editor.node.findHighestMatchingAncestor( + gChildTextNode3a, predicateFunc); + assertNotNull('Should return an ancestor', node); + assertEquals('Should have found "parentNode" as the last ' + + 'ancestor matching the predicate', + parentNode, + node); + + predicateFunc = function(node) { + return node.childNodes.length == 1; + }; + node = goog.editor.node.findHighestMatchingAncestor(gChildTextNode3a, + predicateFunc); + assertNull("Shouldn't return an ancestor", node); + + tearDownDomTree(); +} + +function testIsBlock() { + var blockDisplays = ['block', 'list-item', 'table', 'table-caption', + 'table-cell', 'table-column', 'table-column-group', 'table-footer', + 'table-footer-group', 'table-header-group', 'table-row', + 'table-row-group']; + + var structuralTags = [ + goog.dom.TagName.BODY, + goog.dom.TagName.FRAME, + goog.dom.TagName.FRAMESET, + goog.dom.TagName.HEAD, + goog.dom.TagName.HTML + ]; + + // The following tags are considered inline in IE, except LEGEND which is + // only a block element in WEBKIT. + var ambiguousTags = [ + goog.dom.TagName.DETAILS, + goog.dom.TagName.HR, + goog.dom.TagName.ISINDEX, + goog.dom.TagName.LEGEND, + goog.dom.TagName.MAP, + goog.dom.TagName.NOFRAMES, + goog.dom.TagName.OPTGROUP, + goog.dom.TagName.OPTION, + goog.dom.TagName.SUMMARY + ]; + + // Older versions of IE and Gecko consider the following elements to be + // inline, but IE9+ and Gecko 2.0+ recognize the new elements. + var legacyAmbiguousTags = [ + goog.dom.TagName.ARTICLE, + goog.dom.TagName.ASIDE, + goog.dom.TagName.FIGCAPTION, + goog.dom.TagName.FIGURE, + goog.dom.TagName.FOOTER, + goog.dom.TagName.HEADER, + goog.dom.TagName.HGROUP, + goog.dom.TagName.NAV, + goog.dom.TagName.SECTION + ]; + + var tagsToIgnore = goog.array.flatten(structuralTags, ambiguousTags); + + if ((goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) || + (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('2'))) { + goog.array.extend(tagsToIgnore, legacyAmbiguousTags); + } + + // Appending an applet tag can cause the test to hang if Java is blocked on + // the system. + tagsToIgnore.push(goog.dom.TagName.APPLET); + + // Appending an embed tag to the page in IE brings up a warning dialog about + // loading Java content. + if (goog.userAgent.IE) { + tagsToIgnore.push(goog.dom.TagName.EMBED); + } + + for (var tag in goog.dom.TagName) { + if (goog.array.contains(tagsToIgnore, tag)) { + continue; + } + + var el = goog.dom.createElement(tag); + document.body.appendChild(el); + var display = goog.style.getCascadedStyle(el, 'display') || + goog.style.getComputedStyle(el, 'display'); + goog.dom.removeNode(el); + + if (goog.editor.node.isBlockTag(el)) { + assertContains('Display for ' + tag + ' should be block-like', + display, blockDisplays); + } else { + assertNotContains('Display for ' + tag + ' should not be block-like', + display, blockDisplays); + } + } +} + +function createDivWithTextNodes(var_args) { + var dom = goog.dom.createDom('div'); + for (var i = 0; i < arguments.length; i++) { + goog.dom.appendChild(dom, goog.dom.createTextNode(arguments[i])); + } + return dom; +} + +function testSkipEmptyTextNodes() { + assertNull('skipEmptyTextNodes should gracefully handle null', + goog.editor.node.skipEmptyTextNodes(null)); + + var dom1 = createDivWithTextNodes('abc', '', 'xyz', '', ''); + assertEquals('expected not to skip first child', dom1.firstChild, + goog.editor.node.skipEmptyTextNodes(dom1.firstChild)); + assertEquals('expected to skip second child', dom1.childNodes[2], + goog.editor.node.skipEmptyTextNodes(dom1.childNodes[1])); + assertNull('expected to skip all the rest of the children', + goog.editor.node.skipEmptyTextNodes(dom1.childNodes[3])); +} + +function testIsEditableContainer() { + var editableContainerElement = document.getElementById('editableTest'); + assertTrue('Container element should be considered editable container', + goog.editor.node.isEditableContainer(editableContainerElement)); + + var nonEditableContainerElement = document.getElementById('parentNode'); + assertFalse('Other element should not be considered editable container', + goog.editor.node.isEditableContainer(nonEditableContainerElement)); +} + +function testIsEditable() { + var editableContainerElement = document.getElementById('editableTest'); + var childNode = editableContainerElement.firstChild; + var childElement = editableContainerElement.getElementsByTagName('span')[0]; + + assertFalse('Container element should not be considered editable', + goog.editor.node.isEditable(editableContainerElement)); + assertTrue('Child text node should be considered editable', + goog.editor.node.isEditable(childNode)); + assertTrue('Child element should be considered editable', + goog.editor.node.isEditable(childElement)); + assertTrue('Grandchild node should be considered editable', + goog.editor.node.isEditable(childElement.firstChild)); + assertFalse('Other element should not be considered editable', + goog.editor.node.isEditable(document.getElementById('parentNode'))); +} + +function testFindTopMostEditableAncestor() { + var root = document.getElementById('editableTest'); + var span = root.getElementsByTagName(goog.dom.TagName.SPAN)[0]; + var textNode = span.firstChild; + + assertEquals('Should return self if self is matched.', + textNode, goog.editor.node.findTopMostEditableAncestor(textNode, + function(node) { + return node.nodeType == goog.dom.NodeType.TEXT; + })); + assertEquals('Should not walk out of editable node.', + null, goog.editor.node.findTopMostEditableAncestor(textNode, + function(node) { + return node.tagName == goog.dom.TagName.BODY; + })); + assertEquals('Should not match editable container.', + null, goog.editor.node.findTopMostEditableAncestor(textNode, + function(node) { + return node.tagName == goog.dom.TagName.DIV; + })); + assertEquals('Should find node in editable container.', + span, goog.editor.node.findTopMostEditableAncestor(textNode, + function(node) { + return node.tagName == goog.dom.TagName.SPAN; + })); +} + +function testSplitDomTreeAt() { + var innerHTML = '

    123

    '; + var root = goog.dom.createElement(goog.dom.TagName.DIV); + + root.innerHTML = innerHTML; + var result = goog.editor.node.splitDomTreeAt( + root.getElementsByTagName(goog.dom.TagName.B)[0], null, root); + goog.testing.dom.assertHtmlContentsMatch('

    12

    ', root); + goog.testing.dom.assertHtmlContentsMatch('

    3

    ', result); + + root.innerHTML = innerHTML; + result = goog.editor.node.splitDomTreeAt( + root.getElementsByTagName(goog.dom.TagName.B)[0], + goog.dom.createTextNode('and'), + root); + goog.testing.dom.assertHtmlContentsMatch('

    12

    ', root); + goog.testing.dom.assertHtmlContentsMatch('

    and3

    ', result); +} + +function testTransferChildren() { + var prefix = 'Bold 1'; + var innerHTML = 'Bold
    • Item 1
    • Item 2
    '; + + var root1 = goog.dom.createElement(goog.dom.TagName.DIV); + root1.innerHTML = innerHTML; + + var root2 = goog.dom.createElement(goog.dom.TagName.P); + root2.innerHTML = prefix; + + var b = root1.getElementsByTagName(goog.dom.TagName.B)[0]; + + // Transfer the children. + goog.editor.node.transferChildren(root2, root1); + assertEquals(0, root1.childNodes.length); + goog.testing.dom.assertHtmlContentsMatch(prefix + innerHTML, root2); + assertEquals(b, root2.getElementsByTagName(goog.dom.TagName.B)[1]); + + // Transfer them back. + goog.editor.node.transferChildren(root1, root2); + assertEquals(0, root2.childNodes.length); + goog.testing.dom.assertHtmlContentsMatch(prefix + innerHTML, root1); + assertEquals(b, root1.getElementsByTagName(goog.dom.TagName.B)[1]); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugin.js b/mapbox-gl/closure-library/closure/goog/editor/plugin.js new file mode 100644 index 0000000000..d823ccb1d3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugin.js @@ -0,0 +1,463 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved. + +/** + * @fileoverview Abstract API for TrogEdit plugins. + * + * @see ../demos/editor/editor.html + */ + +goog.provide('goog.editor.Plugin'); + +// TODO(user): Remove the dependency on goog.editor.Command asap. Currently only +// needed for execCommand issues with links. +goog.require('goog.events.EventTarget'); +goog.require('goog.functions'); +goog.require('goog.log'); +goog.require('goog.object'); +goog.require('goog.reflect'); +goog.require('goog.userAgent'); + + + +/** + * Abstract API for trogedit plugins. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.editor.Plugin = function() { + goog.events.EventTarget.call(this); + + /** + * Whether this plugin is enabled for the registered field object. + * @type {boolean} + * @private + */ + this.enabled_ = this.activeOnUneditableFields(); +}; +goog.inherits(goog.editor.Plugin, goog.events.EventTarget); + + +/** + * The field object this plugin is attached to. + * @type {goog.editor.Field} + * @protected + * @deprecated Use goog.editor.Plugin.getFieldObject and + * goog.editor.Plugin.setFieldObject. + */ +goog.editor.Plugin.prototype.fieldObject = null; + + +/** + * @return {goog.dom.DomHelper?} The dom helper object associated with the + * currently active field. + */ +goog.editor.Plugin.prototype.getFieldDomHelper = function() { + return this.getFieldObject() && this.getFieldObject().getEditableDomHelper(); +}; + + +/** + * Indicates if this plugin should be automatically disposed when the + * registered field is disposed. This should be changed to false for + * plugins used as multi-field plugins. + * @type {boolean} + * @private + */ +goog.editor.Plugin.prototype.autoDispose_ = true; + + +/** + * The logger for this plugin. + * @type {goog.log.Logger} + * @protected + */ +goog.editor.Plugin.prototype.logger = + goog.log.getLogger('goog.editor.Plugin'); + + +/** + * Sets the field object for use with this plugin. + * @return {goog.editor.Field} The editable field object. + * @protected + * @suppress {deprecated} Until fieldObject can be made private. + */ +goog.editor.Plugin.prototype.getFieldObject = function() { + return this.fieldObject; +}; + + +/** + * Sets the field object for use with this plugin. + * @param {goog.editor.Field} fieldObject The editable field object. + * @protected + * @suppress {deprecated} Until fieldObject can be made private. + */ +goog.editor.Plugin.prototype.setFieldObject = function(fieldObject) { + this.fieldObject = fieldObject; +}; + + +/** + * Registers the field object for use with this plugin. + * @param {goog.editor.Field} fieldObject The editable field object. + */ +goog.editor.Plugin.prototype.registerFieldObject = function(fieldObject) { + this.setFieldObject(fieldObject); +}; + + +/** + * Unregisters and disables this plugin for the current field object. + * @param {goog.editor.Field} fieldObj The field object. For single-field + * plugins, this parameter is ignored. + */ +goog.editor.Plugin.prototype.unregisterFieldObject = function(fieldObj) { + if (this.getFieldObject()) { + this.disable(this.getFieldObject()); + this.setFieldObject(null); + } +}; + + +/** + * Enables this plugin for the specified, registered field object. A field + * object should only be enabled when it is loaded. + * @param {goog.editor.Field} fieldObject The field object. + */ +goog.editor.Plugin.prototype.enable = function(fieldObject) { + if (this.getFieldObject() == fieldObject) { + this.enabled_ = true; + } else { + goog.log.error(this.logger, 'Trying to enable an unregistered field with ' + + 'this plugin.'); + } +}; + + +/** + * Disables this plugin for the specified, registered field object. + * @param {goog.editor.Field} fieldObject The field object. + */ +goog.editor.Plugin.prototype.disable = function(fieldObject) { + if (this.getFieldObject() == fieldObject) { + this.enabled_ = false; + } else { + goog.log.error(this.logger, 'Trying to disable an unregistered field ' + + 'with this plugin.'); + } +}; + + +/** + * Returns whether this plugin is enabled for the field object. + * + * @param {goog.editor.Field} fieldObject The field object. + * @return {boolean} Whether this plugin is enabled for the field object. + */ +goog.editor.Plugin.prototype.isEnabled = function(fieldObject) { + return this.getFieldObject() == fieldObject ? this.enabled_ : false; +}; + + +/** + * Set if this plugin should automatically be disposed when the registered + * field is disposed. + * @param {boolean} autoDispose Whether to autoDispose. + */ +goog.editor.Plugin.prototype.setAutoDispose = function(autoDispose) { + this.autoDispose_ = autoDispose; +}; + + +/** + * @return {boolean} Whether or not this plugin should automatically be disposed + * when it's registered field is disposed. + */ +goog.editor.Plugin.prototype.isAutoDispose = function() { + return this.autoDispose_; +}; + + +/** + * @return {boolean} If true, field will not disable the command + * when the field becomes uneditable. + */ +goog.editor.Plugin.prototype.activeOnUneditableFields = goog.functions.FALSE; + + +/** + * @param {string} command The command to check. + * @return {boolean} If true, field will not dispatch change events + * for commands of this type. This is useful for "seamless" plugins like + * dialogs and lorem ipsum. + */ +goog.editor.Plugin.prototype.isSilentCommand = goog.functions.FALSE; + + +/** @override */ +goog.editor.Plugin.prototype.disposeInternal = function() { + if (this.getFieldObject()) { + this.unregisterFieldObject(this.getFieldObject()); + } + + goog.editor.Plugin.superClass_.disposeInternal.call(this); +}; + + +/** + * @return {string} The ID unique to this plugin class. Note that different + * instances off the plugin share the same classId. + */ +goog.editor.Plugin.prototype.getTrogClassId; + + +/** + * An enum of operations that plugins may support. + * @enum {number} + */ +goog.editor.Plugin.Op = { + KEYDOWN: 1, + KEYPRESS: 2, + KEYUP: 3, + SELECTION: 4, + SHORTCUT: 5, + EXEC_COMMAND: 6, + QUERY_COMMAND: 7, + PREPARE_CONTENTS_HTML: 8, + CLEAN_CONTENTS_HTML: 10, + CLEAN_CONTENTS_DOM: 11 +}; + + +/** + * A map from plugin operations to the names of the methods that + * invoke those operations. + */ +goog.editor.Plugin.OPCODE = goog.object.transpose( + goog.reflect.object(goog.editor.Plugin, { + handleKeyDown: goog.editor.Plugin.Op.KEYDOWN, + handleKeyPress: goog.editor.Plugin.Op.KEYPRESS, + handleKeyUp: goog.editor.Plugin.Op.KEYUP, + handleSelectionChange: goog.editor.Plugin.Op.SELECTION, + handleKeyboardShortcut: goog.editor.Plugin.Op.SHORTCUT, + execCommand: goog.editor.Plugin.Op.EXEC_COMMAND, + queryCommandValue: goog.editor.Plugin.Op.QUERY_COMMAND, + prepareContentsHtml: goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, + cleanContentsHtml: goog.editor.Plugin.Op.CLEAN_CONTENTS_HTML, + cleanContentsDom: goog.editor.Plugin.Op.CLEAN_CONTENTS_DOM + })); + + +/** + * A set of op codes that run even on disabled plugins. + */ +goog.editor.Plugin.IRREPRESSIBLE_OPS = goog.object.createSet( + goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, + goog.editor.Plugin.Op.CLEAN_CONTENTS_HTML, + goog.editor.Plugin.Op.CLEAN_CONTENTS_DOM); + + +/** + * Handles keydown. It is run before handleKeyboardShortcut and if it returns + * true handleKeyboardShortcut will not be called. + * @param {!goog.events.BrowserEvent} e The browser event. + * @return {boolean} Whether the event was handled and thus should *not* be + * propagated to other plugins or handleKeyboardShortcut. + */ +goog.editor.Plugin.prototype.handleKeyDown; + + +/** + * Handles keypress. It is run before handleKeyboardShortcut and if it returns + * true handleKeyboardShortcut will not be called. + * @param {!goog.events.BrowserEvent} e The browser event. + * @return {boolean} Whether the event was handled and thus should *not* be + * propagated to other plugins or handleKeyboardShortcut. + */ +goog.editor.Plugin.prototype.handleKeyPress; + + +/** + * Handles keyup. + * @param {!goog.events.BrowserEvent} e The browser event. + * @return {boolean} Whether the event was handled and thus should *not* be + * propagated to other plugins. + */ +goog.editor.Plugin.prototype.handleKeyUp; + + +/** + * Handles selection change. + * @param {!goog.events.BrowserEvent=} opt_e The browser event. + * @param {!Node=} opt_target The node the selection changed to. + * @return {boolean} Whether the event was handled and thus should *not* be + * propagated to other plugins. + */ +goog.editor.Plugin.prototype.handleSelectionChange; + + +/** + * Handles keyboard shortcuts. Preferred to using handleKey* as it will use + * the proper event based on browser and will be more performant. If + * handleKeyPress/handleKeyDown returns true, this will not be called. If the + * plugin handles the shortcut, it is responsible for dispatching appropriate + * events (change, selection change at the time of this comment). If the plugin + * calls execCommand on the editable field, then execCommand already takes care + * of dispatching events. + * NOTE: For performance reasons this is only called when any key is pressed + * in conjunction with ctrl/meta keys OR when a small subset of keys (defined + * in goog.editor.Field.POTENTIAL_SHORTCUT_KEYCODES_) are pressed without + * ctrl/meta keys. We specifically don't invoke it when altKey is pressed since + * alt key is used in many i8n UIs to enter certain characters. + * @param {!goog.events.BrowserEvent} e The browser event. + * @param {string} key The key pressed. + * @param {boolean} isModifierPressed Whether the ctrl/meta key was pressed or + * not. + * @return {boolean} Whether the event was handled and thus should *not* be + * propagated to other plugins. We also call preventDefault on the event if + * the return value is true. + */ +goog.editor.Plugin.prototype.handleKeyboardShortcut; + + +/** + * Handles execCommand. This default implementation handles dispatching + * BEFORECHANGE, CHANGE, and SELECTIONCHANGE events, and calls + * execCommandInternal to perform the actual command. Plugins that want to + * do their own event dispatching should override execCommand, otherwise + * it is preferred to only override execCommandInternal. + * + * This version of execCommand will only work for single field plugins. + * Multi-field plugins must override execCommand. + * + * @param {string} command The command to execute. + * @param {...*} var_args Any additional parameters needed to + * execute the command. + * @return {*} The result of the execCommand, if any. + */ +goog.editor.Plugin.prototype.execCommand = function(command, var_args) { + // TODO(user): Replace all uses of isSilentCommand with plugins that just + // override this base execCommand method. + var silent = this.isSilentCommand(command); + if (!silent) { + // Stop listening to mutation events in Firefox while text formatting + // is happening. This prevents us from trying to size the field in the + // middle of an execCommand, catching the field in a strange intermediary + // state where both replacement nodes and original nodes are appended to + // the dom. Note that change events get turned back on by + // fieldObj.dispatchChange. + if (goog.userAgent.GECKO) { + this.getFieldObject().stopChangeEvents(true, true); + } + + this.getFieldObject().dispatchBeforeChange(); + } + + try { + var result = this.execCommandInternal.apply(this, arguments); + } finally { + // If the above execCommandInternal call throws an exception, we still need + // to turn change events back on (see http://b/issue?id=1471355). + // NOTE: If if you add to or change the methods called in this finally + // block, please add them as expected calls to the unit test function + // testExecCommandException(). + if (!silent) { + // dispatchChange includes a call to startChangeEvents, which unwinds the + // call to stopChangeEvents made before the try block. + this.getFieldObject().dispatchChange(); + this.getFieldObject().dispatchSelectionChangeEvent(); + } + } + + return result; +}; + + +/** + * Handles execCommand. This default implementation does nothing, and is + * called by execCommand, which handles event dispatching. This method should + * be overriden by plugins that don't need to do their own event dispatching. + * If custom event dispatching is needed, execCommand shoul be overriden + * instead. + * + * @param {string} command The command to execute. + * @param {...*} var_args Any additional parameters needed to + * execute the command. + * @return {*} The result of the execCommand, if any. + * @protected + */ +goog.editor.Plugin.prototype.execCommandInternal; + + +/** + * Gets the state of this command if this plugin serves that command. + * @param {string} command The command to check. + * @return {*} The value of the command. + */ +goog.editor.Plugin.prototype.queryCommandValue; + + +/** + * Prepares the given HTML for editing. Strips out content that should not + * appear in an editor, and normalizes content as appropriate. The inverse + * of cleanContentsHtml. + * + * This op is invoked even on disabled plugins. + * + * @param {string} originalHtml The original HTML. + * @param {Object} styles A map of strings. If the plugin wants to add + * any styles to the field element, it should add them as key-value + * pairs to this object. + * @return {string} New HTML that's ok for editing. + */ +goog.editor.Plugin.prototype.prepareContentsHtml; + + +/** + * Cleans the contents of the node passed to it. The node contents are modified + * directly, and the modifications will subsequently be used, for operations + * such as saving the innerHTML of the editor etc. Since the plugins act on + * the DOM directly, this method can be very expensive. + * + * This op is invoked even on disabled plugins. + * + * @param {!Element} fieldCopy The copy of the editable field which + * needs to be cleaned up. + */ +goog.editor.Plugin.prototype.cleanContentsDom; + + +/** + * Cleans the html contents of Trogedit. Both cleanContentsDom and + * and cleanContentsHtml will be called on contents extracted from Trogedit. + * The inverse of prepareContentsHtml. + * + * This op is invoked even on disabled plugins. + * + * @param {string} originalHtml The trogedit HTML. + * @return {string} Cleaned-up HTML. + */ +goog.editor.Plugin.prototype.cleanContentsHtml; + + +/** + * Whether the string corresponds to a command this plugin handles. + * @param {string} command Command string to check. + * @return {boolean} Whether the plugin handles this type of command. + */ +goog.editor.Plugin.prototype.isSupportedCommand = function(command) { + return false; +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugin_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugin_test.html new file mode 100644 index 0000000000..9e97402d6e --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugin_test.html @@ -0,0 +1,30 @@ + + + + + + + Editor Unit Tests - goog.editor.Plugin + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugin_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugin_test.js new file mode 100644 index 0000000000..6bac70049f --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugin_test.js @@ -0,0 +1,177 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.PluginTest'); +goog.setTestOnly('goog.editor.PluginTest'); + +goog.require('goog.editor.Field'); +goog.require('goog.editor.Plugin'); +goog.require('goog.functions'); +goog.require('goog.testing.StrictMock'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var plugin; +var fieldObject; + + +function setUp() { + plugin = new goog.editor.Plugin(); + fieldObject = {}; +} + + +function tearDown() { + plugin.dispose(); +} + + +function testRegisterFieldObject() { + plugin.registerFieldObject(fieldObject); + assertEquals('Register field object must be stored in protected field.', + fieldObject, plugin.fieldObject); + + assertFalse('Newly registered plugin must not be enabled.', + plugin.isEnabled(fieldObject)); +} + + +function testUnregisterFieldObject() { + plugin.registerFieldObject(fieldObject); + plugin.enable(fieldObject); + plugin.unregisterFieldObject(fieldObject); + + assertNull('fieldObject property must be undefined after ' + + 'unregistering a field object.', plugin.fieldObject); + assertFalse('Unregistered field object must not be enabled', + plugin.isEnabled(fieldObject)); +} + + +function testEnable() { + plugin.registerFieldObject(fieldObject); + plugin.enable(fieldObject); + + assertTrue('Enabled field object must be enabled according to isEnabled().', + plugin.isEnabled(fieldObject)); +} + + +function testDisable() { + plugin.registerFieldObject(fieldObject); + plugin.enable(fieldObject); + plugin.disable(fieldObject); + + assertFalse('Disabled field object must be disabled according to ' + + 'isEnabled().', plugin.isEnabled(fieldObject)); +} + + +function testIsEnabled() { + // Other base cases covered while testing enable() and disable(). + + assertFalse('Unregistered field object must be disabled according ' + + 'to isEnabled().', plugin.isEnabled(fieldObject)); +} + + +function testIsSupportedCommand() { + assertFalse('Base plugin class must not support any commands.', + plugin.isSupportedCommand('+indent')); +} + +function testExecCommand() { + var mockField = new goog.testing.StrictMock(goog.editor.Field); + plugin.registerFieldObject(mockField); + + if (goog.userAgent.GECKO) { + mockField.stopChangeEvents(true, true); + } + mockField.dispatchBeforeChange(); + // Note(user): dispatch change turns back on (delayed) change events. + mockField.dispatchChange(); + mockField.dispatchSelectionChangeEvent(); + mockField.$replay(); + + var passedCommand, passedArg; + plugin.execCommandInternal = function(command, arg) { + passedCommand = command; + passedArg = arg; + }; + plugin.execCommand('+indent', true); + + // Verify that execCommand dispatched the expected events. + mockField.$verify(); + mockField.$reset(); + // Verify that execCommandInternal was called with the correct arguments. + assertEquals('+indent', passedCommand); + assertTrue(passedArg); + + plugin.isSilentCommand = goog.functions.constant(true); + mockField.$replay(); + plugin.execCommand('+outdent', false); + // Verify that execCommand on a silent plugin dispatched no events. + mockField.$verify(); + // Verify that execCommandInternal was called with the correct arguments. + assertEquals('+outdent', passedCommand); + assertFalse(passedArg); +} + + +/** + * Regression test for http://b/issue?id=1471355 . + */ +function testExecCommandException() { + var mockField = new goog.testing.StrictMock(goog.editor.Field); + plugin.registerFieldObject(mockField); + plugin.execCommandInternal = function() { + throw 1; + }; + + if (goog.userAgent.GECKO) { + mockField.stopChangeEvents(true, true); + } + mockField.dispatchBeforeChange(); + // Note(user): dispatch change turns back on (delayed) change events. + mockField.dispatchChange(); + mockField.dispatchSelectionChangeEvent(); + mockField.$replay(); + + assertThrows('Exception should not be swallowed', function() { + plugin.execCommand(); + }); + + // Verifies that cleanup is done despite the exception. + mockField.$verify(); +} + +function testDisposed() { + plugin.registerFieldObject(fieldObject); + plugin.dispose(); + assert(plugin.getDisposed()); + assertNull('Disposed plugin must not have a field object.', + plugin.fieldObject); + assertFalse('Disposed plugin must not have an enabled field object.', + plugin.isEnabled(fieldObject)); +} + +function testIsAndSetAutoDispose() { + assertTrue('Plugin must start auto-disposable', plugin.isAutoDispose()); + + plugin.setAutoDispose(false); + assertFalse(plugin.isAutoDispose()); + + plugin.setAutoDispose(true); + assertTrue(plugin.isAutoDispose()); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin.js new file mode 100644 index 0000000000..4951ed68e0 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin.js @@ -0,0 +1,712 @@ +// Copyright 2005 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for bubble plugins. + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.editor.plugins.AbstractBubblePlugin'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.classlist'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.style'); +goog.require('goog.events'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.events.actionEventWrapper'); +goog.require('goog.functions'); +goog.require('goog.string.Unicode'); +goog.require('goog.ui.Component'); +goog.require('goog.ui.editor.Bubble'); +goog.require('goog.userAgent'); + + + +/** + * Base class for bubble plugins. This is used for to connect user behavior + * in the editor to a goog.ui.editor.Bubble UI element that allows + * the user to modify the properties of an element on their page (e.g. the alt + * text of an image tag). + * + * Subclasses should override the abstract method getBubbleTargetFromSelection() + * with code to determine if the current selection should activate the bubble + * type. The other abstract method createBubbleContents() should be overriden + * with code to create the inside markup of the bubble. The base class creates + * the rest of the bubble. + * + * @constructor + * @extends {goog.editor.Plugin} + */ +goog.editor.plugins.AbstractBubblePlugin = function() { + goog.editor.plugins.AbstractBubblePlugin.base(this, 'constructor'); + + /** + * Place to register events the plugin listens to. + * @type {goog.events.EventHandler< + * !goog.editor.plugins.AbstractBubblePlugin>} + * @protected + */ + this.eventRegister = new goog.events.EventHandler(this); + + /** + * Instance factory function that creates a bubble UI component. If set to a + * non-null value, this function will be used to create a bubble instead of + * the global factory function. It takes as parameters the bubble parent + * element and the z index to draw the bubble at. + * @type {?function(!Element, number): !goog.ui.editor.Bubble} + * @private + */ + this.bubbleFactory_ = null; +}; +goog.inherits(goog.editor.plugins.AbstractBubblePlugin, goog.editor.Plugin); + + +/** + * The css class name of option link elements. + * @type {string} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.OPTION_LINK_CLASSNAME_ = + goog.getCssName('tr_option-link'); + + +/** + * The css class name of link elements. + * @type {string} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_ = + goog.getCssName('tr_bubble_link'); + + +/** + * A class name to mark elements that should be reachable by keyboard tabbing. + * @type {string} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_ = + goog.getCssName('tr_bubble_tabbable'); + + +/** + * The constant string used to separate option links. + * @type {string} + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.DASH_NBSP_STRING = + goog.string.Unicode.NBSP + '-' + goog.string.Unicode.NBSP; + + +/** + * Default factory function for creating a bubble UI component. + * @param {!Element} parent The parent element for the bubble. + * @param {number} zIndex The z index to draw the bubble at. + * @return {!goog.ui.editor.Bubble} The new bubble component. + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.defaultBubbleFactory_ = function( + parent, zIndex) { + return new goog.ui.editor.Bubble(parent, zIndex); +}; + + +/** + * Global factory function that creates a bubble UI component. It takes as + * parameters the bubble parent element and the z index to draw the bubble at. + * @type {function(!Element, number): !goog.ui.editor.Bubble} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_ = + goog.editor.plugins.AbstractBubblePlugin.defaultBubbleFactory_; + + +/** + * Sets the global bubble factory function. + * @param {function(!Element, number): !goog.ui.editor.Bubble} + * bubbleFactory Function that creates a bubble for the given bubble parent + * element and z index. + */ +goog.editor.plugins.AbstractBubblePlugin.setBubbleFactory = function( + bubbleFactory) { + goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_ = bubbleFactory; +}; + + +/** + * Map from field id to shared bubble object. + * @type {!Object} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {}; + + +/** + * The optional parent of the bubble. If null or not set, we will use the + * application document. This is useful when you have an editor embedded in + * a scrolling DIV. + * @type {Element|undefined} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.bubbleParent_; + + +/** + * The id of the panel this plugin added to the shared bubble. Null when + * this plugin doesn't currently have a panel in a bubble. + * @type {string?} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.panelId_ = null; + + +/** + * Whether this bubble should support tabbing through elements. False + * by default. + * @type {boolean} + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.keyboardNavigationEnabled_ = + false; + + +/** + * Sets the instance bubble factory function. If set to a non-null value, this + * function will be used to create a bubble instead of the global factory + * function. + * @param {?function(!Element, number): !goog.ui.editor.Bubble} bubbleFactory + * Function that creates a bubble for the given bubble parent element and z + * index. Null to reset the factory function. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.setBubbleFactory = function( + bubbleFactory) { + this.bubbleFactory_ = bubbleFactory; +}; + + +/** + * Sets whether the bubble should support tabbing through elements. + * @param {boolean} keyboardNavigationEnabled + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.enableKeyboardNavigation = + function(keyboardNavigationEnabled) { + this.keyboardNavigationEnabled_ = keyboardNavigationEnabled; +}; + + +/** + * Sets the bubble parent. + * @param {Element} bubbleParent An element where the bubble will be + * anchored. If null, we will use the application document. This + * is useful when you have an editor embedded in a scrolling div. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.setBubbleParent = function( + bubbleParent) { + this.bubbleParent_ = bubbleParent; +}; + + +/** + * Returns the bubble map. Subclasses may override to use a separate map. + * @return {!Object} + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleMap = function() { + return goog.editor.plugins.AbstractBubblePlugin.bubbleMap_; +}; + + +/** + * @return {goog.dom.DomHelper} The dom helper for the bubble window. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleDom = function() { + return this.dom_; +}; + + +/** @override */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getTrogClassId = + goog.functions.constant('AbstractBubblePlugin'); + + +/** + * Returns the element whose properties the bubble manipulates. + * @return {Element} The target element. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getTargetElement = + function() { + return this.targetElement_; +}; + + +/** @override */ +goog.editor.plugins.AbstractBubblePlugin.prototype.handleKeyUp = function(e) { + // For example, when an image is selected, pressing any key overwrites + // the image and the panel should be hidden. + // Therefore we need to track key presses when the bubble is showing. + if (this.isVisible()) { + this.handleSelectionChange(); + } + return false; +}; + + +/** + * Pops up a property bubble for the given selection if appropriate and closes + * open property bubbles if no longer needed. This should not be overridden. + * @override + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.handleSelectionChange = + function(opt_e, opt_target) { + var selectedElement; + if (opt_e) { + selectedElement = /** @type {Element} */ (opt_e.target); + } else if (opt_target) { + selectedElement = /** @type {Element} */ (opt_target); + } else { + var range = this.getFieldObject().getRange(); + if (range) { + var startNode = range.getStartNode(); + var endNode = range.getEndNode(); + var startOffset = range.getStartOffset(); + var endOffset = range.getEndOffset(); + // Sometimes in IE, the range will be collapsed, but think the end node + // and start node are different (although in the same visible position). + // In this case, favor the position IE thinks is the start node. + if (goog.userAgent.IE && range.isCollapsed() && startNode != endNode) { + range = goog.dom.Range.createCaret(startNode, startOffset); + } + if (startNode.nodeType == goog.dom.NodeType.ELEMENT && + startNode == endNode && startOffset == endOffset - 1) { + var element = startNode.childNodes[startOffset]; + if (element.nodeType == goog.dom.NodeType.ELEMENT) { + selectedElement = element; + } + } + } + selectedElement = selectedElement || range && range.getContainerElement(); + } + return this.handleSelectionChangeInternal(selectedElement); +}; + + +/** + * Pops up a property bubble for the given selection if appropriate and closes + * open property bubbles if no longer needed. + * @param {Element?} selectedElement The selected element. + * @return {boolean} Always false, allowing every bubble plugin to handle the + * event. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype. + handleSelectionChangeInternal = function(selectedElement) { + if (selectedElement) { + var bubbleTarget = this.getBubbleTargetFromSelection(selectedElement); + if (bubbleTarget) { + if (bubbleTarget != this.targetElement_ || !this.panelId_) { + // Make sure any existing panel of the same type is closed before + // creating a new one. + if (this.panelId_) { + this.closeBubble(); + } + this.createBubble(bubbleTarget); + } + return false; + } + } + + if (this.panelId_) { + this.closeBubble(); + } + + return false; +}; + + +/** + * Should be overriden by subclasses to return the bubble target element or + * null if an element of their required type isn't found. + * @param {Element} selectedElement The target of the selection change event or + * the parent container of the current entire selection. + * @return {Element?} The HTML bubble target element or null if no element of + * the required type is not found. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype. + getBubbleTargetFromSelection = goog.abstractMethod; + + +/** @override */ +goog.editor.plugins.AbstractBubblePlugin.prototype.disable = function(field) { + // When the field is made uneditable, dispose of the bubble. We do this + // because the next time the field is made editable again it may be in + // a different document / iframe. + if (field.isUneditable()) { + var bubbleMap = this.getBubbleMap(); + var bubble = bubbleMap[field.id]; + if (bubble) { + if (field == this.getFieldObject()) { + this.closeBubble(); + } + bubble.dispose(); + delete bubbleMap[field.id]; + } + } +}; + + +/** + * @return {!goog.ui.editor.Bubble} The shared bubble object for the field this + * plugin is registered on. Creates it if necessary. + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getSharedBubble_ = + function() { + var bubbleParent = /** @type {!Element} */ (this.bubbleParent_ || + this.getFieldObject().getAppWindow().document.body); + this.dom_ = goog.dom.getDomHelper(bubbleParent); + + var bubbleMap = this.getBubbleMap(); + var bubble = bubbleMap[this.getFieldObject().id]; + if (!bubble) { + var factory = this.bubbleFactory_ || + goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_; + bubble = factory.call(null, bubbleParent, + this.getFieldObject().getBaseZindex()); + bubbleMap[this.getFieldObject().id] = bubble; + } + return bubble; +}; + + +/** + * Creates and shows the property bubble. + * @param {Element} targetElement The target element of the bubble. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.createBubble = function( + targetElement) { + var bubble = this.getSharedBubble_(); + if (!bubble.hasPanelOfType(this.getBubbleType())) { + this.targetElement_ = targetElement; + + this.panelId_ = bubble.addPanel(this.getBubbleType(), this.getBubbleTitle(), + targetElement, + goog.bind(this.createBubbleContents, this), + this.shouldPreferBubbleAboveElement()); + this.eventRegister.listen(bubble, goog.ui.Component.EventType.HIDE, + this.handlePanelClosed_); + + this.onShow(); + + if (this.keyboardNavigationEnabled_) { + this.eventRegister.listen(bubble.getContentElement(), + goog.events.EventType.KEYDOWN, this.onBubbleKey_); + } + } +}; + + +/** + * @return {string} The type of bubble shown by this plugin. Usually the tag + * name of the element this bubble targets. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleType = function() { + return ''; +}; + + +/** + * @return {string} The title for bubble shown by this plugin. Defaults to no + * title. Should be overridden by subclasses. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleTitle = function() { + return ''; +}; + + +/** + * @return {boolean} Whether the bubble should prefer placement above the + * target element. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype. + shouldPreferBubbleAboveElement = goog.functions.FALSE; + + +/** + * Should be overriden by subclasses to add the type specific contents to the + * bubble. + * @param {Element} bubbleContainer The container element of the bubble to + * which the contents should be added. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.createBubbleContents = + goog.abstractMethod; + + +/** + * Register the handler for the target's CLICK event. + * @param {Element} target The event source element. + * @param {Function} handler The event handler. + * @protected + * @deprecated Use goog.editor.plugins.AbstractBubblePlugin. + * registerActionHandler to register click and enter events. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.registerClickHandler = + function(target, handler) { + this.registerActionHandler(target, handler); +}; + + +/** + * Register the handler for the target's CLICK and ENTER key events. + * @param {Element} target The event source element. + * @param {Function} handler The event handler. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.registerActionHandler = + function(target, handler) { + this.eventRegister.listenWithWrapper(target, goog.events.actionEventWrapper, + handler); +}; + + +/** + * Closes the bubble. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.closeBubble = function() { + if (this.panelId_) { + this.getSharedBubble_().removePanel(this.panelId_); + this.handlePanelClosed_(); + } +}; + + +/** + * Called after the bubble is shown. The default implementation does nothing. + * Override it to provide your own one. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.onShow = goog.nullFunction; + + +/** + * Called when the bubble is closed or hidden. The default implementation does + * nothing. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.cleanOnBubbleClose = + goog.nullFunction; + + +/** + * Handles when the bubble panel is closed. Invoked when the entire bubble is + * hidden and also directly when the panel is closed manually. + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.handlePanelClosed_ = + function() { + this.targetElement_ = null; + this.panelId_ = null; + this.eventRegister.removeAll(); + this.cleanOnBubbleClose(); +}; + + +/** + * In case the keyboard navigation is enabled, this will set focus on the first + * tabbable element in the bubble when TAB is clicked. + * @override + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.handleKeyDown = function(e) { + if (this.keyboardNavigationEnabled_ && + this.isVisible() && + e.keyCode == goog.events.KeyCodes.TAB && !e.shiftKey) { + var bubbleEl = this.getSharedBubble_().getContentElement(); + var tabbable = goog.dom.getElementByClass( + goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_, bubbleEl); + if (tabbable) { + tabbable.focus(); + e.preventDefault(); + return true; + } + } + return false; +}; + + +/** + * Handles a key event on the bubble. This ensures that the focus loops through + * the tabbable elements found in the bubble and then the focus is got by the + * field element. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.onBubbleKey_ = function(e) { + if (this.isVisible() && + e.keyCode == goog.events.KeyCodes.TAB) { + var bubbleEl = this.getSharedBubble_().getContentElement(); + var tabbables = goog.dom.getElementsByClass( + goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_, bubbleEl); + var tabbable = e.shiftKey ? tabbables[0] : goog.array.peek(tabbables); + var tabbingOutOfBubble = tabbable == e.target; + if (tabbingOutOfBubble) { + this.getFieldObject().focus(); + e.preventDefault(); + } + } +}; + + +/** + * @return {boolean} Whether the bubble is visible. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.isVisible = function() { + return !!this.panelId_; +}; + + +/** + * Reposition the property bubble. + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.reposition = function() { + var bubble = this.getSharedBubble_(); + if (bubble) { + bubble.reposition(); + } +}; + + +/** + * Helper method that creates option links (such as edit, test, remove) + * @param {string} id String id for the span id. + * @return {Element} The option link element. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.createLinkOption = function( + id) { + // Dash plus link are together in a span so we can hide/show them easily + return this.dom_.createDom(goog.dom.TagName.SPAN, + { + id: id, + className: + goog.editor.plugins.AbstractBubblePlugin.OPTION_LINK_CLASSNAME_ + }, + this.dom_.createTextNode( + goog.editor.plugins.AbstractBubblePlugin.DASH_NBSP_STRING)); +}; + + +/** + * Helper method that creates a link with text set to linkText and optionally + * wires up a listener for the CLICK event or the link. The link is navigable by + * tabs if {@code enableKeyboardNavigation(true)} was called. + * @param {string} linkId The id of the link. + * @param {string} linkText Text of the link. + * @param {Function=} opt_onClick Optional function to call when the link is + * clicked. + * @param {Element=} opt_container If specified, location to insert link. If no + * container is specified, the old link is removed and replaced. + * @return {Element} The link element. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.createLink = function( + linkId, linkText, opt_onClick, opt_container) { + var link = this.createLinkHelper(linkId, linkText, false, opt_container); + if (opt_onClick) { + this.registerActionHandler(link, opt_onClick); + } + return link; +}; + + +/** + * Helper method to create a link to insert into the bubble. The link is + * navigable by tabs if {@code enableKeyboardNavigation(true)} was called. + * @param {string} linkId The id of the link. + * @param {string} linkText Text of the link. + * @param {boolean} isAnchor Set to true to create an actual anchor tag + * instead of a span. Actual links are right clickable (e.g. to open in + * a new window) and also update window status on hover. + * @param {Element=} opt_container If specified, location to insert link. If no + * container is specified, the old link is removed and replaced. + * @return {Element} The link element. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.createLinkHelper = function( + linkId, linkText, isAnchor, opt_container) { + var link = this.dom_.createDom( + isAnchor ? goog.dom.TagName.A : goog.dom.TagName.SPAN, + {className: goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_}, + linkText); + if (this.keyboardNavigationEnabled_) { + this.setTabbable(link); + } + link.setAttribute('role', 'link'); + this.setupLink(link, linkId, opt_container); + goog.editor.style.makeUnselectable(link, this.eventRegister); + return link; +}; + + +/** + * Makes the given element tabbable. + * + *

    Elements created by createLink[Helper] are tabbable even without + * calling this method. Call it for other elements if needed. + * + *

    If tabindex is not already set in the element, this function sets it to 0. + * You'll usually want to also call {@code enableKeyboardNavigation(true)}. + * + * @param {!Element} element + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.setTabbable = + function(element) { + if (!element.hasAttribute('tabindex')) { + element.setAttribute('tabindex', 0); + } + goog.dom.classlist.add(element, + goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_); +}; + + +/** + * Inserts a link in the given container if it is specified or removes + * the old link with this id and replaces it with the new link + * @param {Element} link Html element to insert. + * @param {string} linkId Id of the link. + * @param {Element=} opt_container If specified, location to insert link. + * @protected + */ +goog.editor.plugins.AbstractBubblePlugin.prototype.setupLink = function( + link, linkId, opt_container) { + if (opt_container) { + opt_container.appendChild(link); + } else { + var oldLink = this.dom_.getElement(linkId); + if (oldLink) { + goog.dom.replaceNode(link, oldLink); + } + } + + link.id = linkId; +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.html new file mode 100644 index 0000000000..80e552ca60 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.html @@ -0,0 +1,31 @@ + + + + + + + + Closure Unit Tests - goog.editor.plugins.AbstractBubblePlugin + + + + + + +

    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.js new file mode 100644 index 0000000000..3073be4097 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractbubbleplugin_test.js @@ -0,0 +1,436 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.AbstractBubblePluginTest'); +goog.setTestOnly('goog.editor.plugins.AbstractBubblePluginTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.plugins.AbstractBubblePlugin'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.EventType'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.functions'); +goog.require('goog.style'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.events'); +goog.require('goog.testing.events.Event'); +goog.require('goog.testing.jsunit'); +goog.require('goog.ui.editor.Bubble'); +goog.require('goog.userAgent'); + +var testHelper; +var fieldDiv; +var COMMAND = 'base'; +var fieldMock; +var bubblePlugin; +var link; +var link2; + +function setUpPage() { + fieldDiv = goog.dom.getElement('field'); + var viewportSize = goog.dom.getViewportSize(); + // Some tests depends on enough size of viewport. + if (viewportSize.width < 600 || viewportSize.height < 440) { + window.moveTo(0, 0); + window.resizeTo(640, 480); + } +} + +function setUp() { + testHelper = new goog.testing.editor.TestHelper(fieldDiv); + testHelper.setUpEditableElement(); + fieldMock = new goog.testing.editor.FieldMock(); + + bubblePlugin = new goog.editor.plugins.AbstractBubblePlugin(COMMAND); + bubblePlugin.fieldObject = fieldMock; + + fieldDiv.innerHTML = 'Google' + + 'Google2'; + link = fieldDiv.firstChild; + link2 = fieldDiv.lastChild; + + window.scrollTo(0, 0); + goog.style.setStyle(document.body, 'direction', 'ltr'); + goog.style.setStyle(document.getElementById('field'), 'position', 'static'); +} + +function tearDown() { + bubblePlugin.closeBubble(); + testHelper.tearDownEditableElement(); +} + + +/** + * This is a helper function for setting up the targetElement with a + * given direction. + * + * @param {string} dir The direction of the targetElement, 'ltr' or 'rtl'. + */ +function prepareTargetWithGivenDirection(dir) { + goog.style.setStyle(document.body, 'direction', dir); + + fieldDiv.style.direction = dir; + fieldDiv.innerHTML = 'Google'; + link = fieldDiv.firstChild; + + fieldMock.$replay(); + bubblePlugin.createBubbleContents = function(bubbleContainer) { + bubbleContainer.innerHTML = '
    B
    '; + goog.style.setStyle(bubbleContainer, 'border', '1px solid white'); + }; + bubblePlugin.registerFieldObject(fieldMock); + bubblePlugin.enable(fieldMock); + bubblePlugin.createBubble(link); +} + + +/** + * Similar in intent to mock reset, but implemented by recreating the mock + * variable. $reset() can't work because it will reset general any-time + * expectations done in the fieldMock constructor. + */ +function resetFieldMock() { + fieldMock = new goog.testing.editor.FieldMock(); + bubblePlugin.fieldObject = fieldMock; +} + +function helpTestCreateBubble(opt_fn) { + fieldMock.$replay(); + var numCalled = 0; + bubblePlugin.createBubbleContents = function(bubbleContainer) { + numCalled++; + assertNotNull('bubbleContainer should not be null', bubbleContainer); + }; + if (opt_fn) { + opt_fn(); + } + bubblePlugin.createBubble(link); + assertEquals('createBubbleContents should be called', 1, numCalled); + fieldMock.$verify(); +} + +function testCreateBubble(opt_fn) { + helpTestCreateBubble(opt_fn); + assertTrue(bubblePlugin.getSharedBubble_() instanceof goog.ui.editor.Bubble); + + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); +} + +function testOpeningBubbleCallsOnShow() { + var numCalled = 0; + testCreateBubble(function() { + bubblePlugin.onShow = function() { + numCalled++; + }; + }); + + assertEquals('onShow should be called', 1, numCalled); + fieldMock.$verify(); +} + +function testCloseBubble() { + testCreateBubble(); + + bubblePlugin.closeBubble(); + assertFalse('Bubble should not be visible', bubblePlugin.isVisible()); + fieldMock.$verify(); +} + +function testZindexBehavior() { + // Don't use the default return values. + fieldMock.$reset(); + fieldMock.getAppWindow().$anyTimes().$returns(window); + fieldMock.getEditableDomHelper().$anyTimes() + .$returns(goog.dom.getDomHelper(document)); + fieldMock.getBaseZindex().$returns(2); + bubblePlugin.createBubbleContents = goog.nullFunction; + fieldMock.$replay(); + + bubblePlugin.createBubble(link); + assertEquals('2', + '' + bubblePlugin.getSharedBubble_().bubbleContainer_.style.zIndex); + + fieldMock.$verify(); +} + +function testNoTwoBubblesOpenAtSameTime() { + fieldMock.$replay(); + var origClose = goog.bind(bubblePlugin.closeBubble, bubblePlugin); + var numTimesCloseCalled = 0; + bubblePlugin.closeBubble = function() { + numTimesCloseCalled++; + origClose(); + }; + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + bubblePlugin.createBubbleContents = goog.nullFunction; + + bubblePlugin.handleSelectionChangeInternal(link); + assertEquals(0, numTimesCloseCalled); + assertEquals(link, bubblePlugin.targetElement_); + fieldMock.$verify(); + + bubblePlugin.handleSelectionChangeInternal(link2); + assertEquals(1, numTimesCloseCalled); + assertEquals(link2, bubblePlugin.targetElement_); + fieldMock.$verify(); +} + +function testHandleSelectionChangeWithEvent() { + fieldMock.$replay(); + var fakeEvent = + new goog.events.BrowserEvent({type: 'mouseup', target: link}); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + bubblePlugin.createBubbleContents = goog.nullFunction; + bubblePlugin.handleSelectionChange(fakeEvent); + assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); + assertEquals('Bubble target should be provided event\'s target', + link, bubblePlugin.targetElement_); +} + +function testHandleSelectionChangeWithTarget() { + fieldMock.$replay(); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + bubblePlugin.createBubbleContents = goog.nullFunction; + bubblePlugin.handleSelectionChange(undefined, link2); + assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); + assertEquals('Bubble target should be provided target', + link2, bubblePlugin.targetElement_); +} + + +/** + * Regression test for @bug 2945341 + */ +function testSelectOneTextCharacterNoError() { + fieldMock.$replay(); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + bubblePlugin.createBubbleContents = goog.nullFunction; + // Select first char of first link's text node. + testHelper.select(link.firstChild, 0, link.firstChild, 1); + // This should execute without js errors. + bubblePlugin.handleSelectionChange(); + assertTrue('Bubble should have been opened', bubblePlugin.isVisible()); + fieldMock.$verify(); +} + +function testTabKeyEvents() { + fieldMock.$replay(); + bubblePlugin.enableKeyboardNavigation(true); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + var nonTabbable1, tabbable1, tabbable2, nonTabbable2; + bubblePlugin.createBubbleContents = function(container) { + nonTabbable1 = goog.dom.createDom('div'); + tabbable1 = goog.dom.createDom('div'); + tabbable2 = goog.dom.createDom('div'); + nonTabbable2 = goog.dom.createDom('div'); + goog.dom.append( + container, nonTabbable1, tabbable1, tabbable2, nonTabbable2); + bubblePlugin.setTabbable(tabbable1); + bubblePlugin.setTabbable(tabbable2); + }; + bubblePlugin.handleSelectionChangeInternal(link); + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); + + var tabHandledByBubble = simulateTabKeyOnBubble(); + assertTrue('The action should be handled by the plugin', tabHandledByBubble); + assertFocused(tabbable1); + + // Tab on the first tabbable. The test framework doesn't easily let us verify + // the desired behavior - namely, that the second tabbable gets focused - but + // we verify that the field doesn't get the focus. + goog.testing.events.fireKeySequence(tabbable1, goog.events.KeyCodes.TAB); + + fieldMock.$verify(); + + // Tabbing on the last tabbable should trigger focus() of the target field. + resetFieldMock(); + fieldMock.focus(); + fieldMock.$replay(); + goog.testing.events.fireKeySequence(tabbable2, goog.events.KeyCodes.TAB); + fieldMock.$verify(); +} + +function testTabKeyEventsWithShiftKey() { + fieldMock.$replay(); + bubblePlugin.enableKeyboardNavigation(true); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + var nonTabbable, tabbable1, tabbable2; + bubblePlugin.createBubbleContents = function(container) { + nonTabbable = goog.dom.createDom('div'); + tabbable1 = goog.dom.createDom('div'); + // The test acts only on one tabbable, but we give another one to make sure + // that the tabbable we act on is not also the last. + tabbable2 = goog.dom.createDom('div'); + goog.dom.append(container, nonTabbable, tabbable1, tabbable2); + bubblePlugin.setTabbable(tabbable1); + bubblePlugin.setTabbable(tabbable2); + }; + bubblePlugin.handleSelectionChangeInternal(link); + + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); + + var tabHandledByBubble = simulateTabKeyOnBubble(); + assertTrue('The action should be handled by the plugin', tabHandledByBubble); + assertFocused(tabbable1); + fieldMock.$verify(); + + // Shift-tabbing on the first tabbable should trigger focus() of the target + // field. + resetFieldMock(); + fieldMock.focus(); + fieldMock.$replay(); + goog.testing.events.fireKeySequence( + tabbable1, goog.events.KeyCodes.TAB, {shiftKey: true}); + fieldMock.$verify(); +} + +function testLinksAreTabbable() { + fieldMock.$replay(); + bubblePlugin.enableKeyboardNavigation(true); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + var nonTabbable1, link1, link2, nonTabbable2; + bubblePlugin.createBubbleContents = function(container) { + nonTabbable1 = goog.dom.createDom('div'); + goog.dom.appendChild(container, nonTabbable1); + bubbleLink1 = this.createLink('linkInBubble1', 'Foo', false, container); + bubbleLink2 = this.createLink('linkInBubble2', 'Bar', false, container); + nonTabbable2 = goog.dom.createDom('div'); + goog.dom.appendChild(container, nonTabbable2); + }; + bubblePlugin.handleSelectionChangeInternal(link); + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); + + var tabHandledByBubble = simulateTabKeyOnBubble(); + assertTrue('The action should be handled by the plugin', tabHandledByBubble); + assertFocused(bubbleLink1); + + fieldMock.$verify(); + + // Tabbing on the last link should trigger focus() of the target field. + resetFieldMock(); + fieldMock.focus(); + fieldMock.$replay(); + goog.testing.events.fireKeySequence(bubbleLink2, goog.events.KeyCodes.TAB); + fieldMock.$verify(); +} + +function testTabKeyNoEffectKeyboardNavDisabled() { + fieldMock.$replay(); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + var bubbleLink; + bubblePlugin.createBubbleContents = function(container) { + bubbleLink = this.createLink('linkInBubble', 'Foo', false, container); + }; + bubblePlugin.handleSelectionChangeInternal(link); + + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); + + var tabHandledByBubble = simulateTabKeyOnBubble(); + assertFalse('The action should not be handled by the plugin', + tabHandledByBubble); + assertNotFocused(bubbleLink); + + // Verify that tabbing the link doesn't cause focus of the field. + goog.testing.events.fireKeySequence(bubbleLink, goog.events.KeyCodes.TAB); + + fieldMock.$verify(); +} + +function testOtherKeyEventNoEffectKeyboardNavEnabled() { + fieldMock.$replay(); + bubblePlugin.enableKeyboardNavigation(true); + bubblePlugin.getBubbleTargetFromSelection = goog.functions.identity; + var bubbleLink; + bubblePlugin.createBubbleContents = function(container) { + bubbleLink = this.createLink('linkInBubble', 'Foo', false, container); + }; + bubblePlugin.handleSelectionChangeInternal(link); + + assertTrue('Bubble should be visible', bubblePlugin.isVisible()); + + // Test pressing CTRL + B: this should not have any effect. + var keyHandledByBubble = + simulateKeyDownOnBubble(goog.events.KeyCodes.B, true); + + assertFalse('The action should not be handled by the plugin', + keyHandledByBubble); + assertNotFocused(bubbleLink); + + fieldMock.$verify(); +} + +function testSetTabbableSetsTabIndex() { + var element1 = goog.dom.createDom('div'); + var element2 = goog.dom.createDom('div'); + element1.setAttribute('tabIndex', '1'); + + bubblePlugin.setTabbable(element1); + bubblePlugin.setTabbable(element2); + + assertEquals('1', element1.getAttribute('tabIndex')); + assertEquals('0', element2.getAttribute('tabIndex')); +} + +function testDisable() { + testCreateBubble(); + fieldMock.setUneditable(true); + bubblePlugin.disable(fieldMock); + bubblePlugin.closeBubble(); +} + + +/** + * Sends a tab key event to the bubble. + * @return {boolean} whether the bubble hanlded the event. + */ +function simulateTabKeyOnBubble() { + return simulateKeyDownOnBubble(goog.events.KeyCodes.TAB, false); +} + + +/** + * Sends a key event to the bubble. + * @param {number} keyCode + * @param {boolean} isCtrl + * @return {boolean} whether the bubble hanlded the event. + */ +function simulateKeyDownOnBubble(keyCode, isCtrl) { + // In some browsers (e.g. FireFox) the editable field is marked with + // designMode on. In the test setting (and not in production setting), the + // bubble element shares the same window and hence the designMode. In this + // mode, activeElement remains the and isn't changed along with the + // focus as a result of tab key. + bubblePlugin.getSharedBubble_().getContentElement(). + ownerDocument.designMode = 'off'; + + var event = + new goog.testing.events.Event(goog.events.EventType.KEYDOWN, null); + event.keyCode = keyCode; + event.ctrlKey = isCtrl; + return bubblePlugin.handleKeyDown(event); +} + +function assertFocused(element) { + // The activeElement assertion below doesn't work in IE7. At this time IE7 is + // no longer supported by any client product, so we don't care. + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(8)) { + return; + } + assertEquals('unexpected focus', element, document.activeElement); +} + +function assertNotFocused(element) { + assertNotEquals('unexpected focus', element, document.activeElement); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin.js new file mode 100644 index 0000000000..278277ee38 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin.js @@ -0,0 +1,333 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An abstract superclass for TrogEdit dialog plugins. Each + * Trogedit dialog has its own plugin. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.plugins.AbstractDialogPlugin'); +goog.provide('goog.editor.plugins.AbstractDialogPlugin.EventType'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.range'); +goog.require('goog.events'); +goog.require('goog.ui.editor.AbstractDialog'); + + +// *** Public interface ***************************************************** // + + + +/** + * An abstract superclass for a Trogedit plugin that creates exactly one + * dialog. By default dialogs are not reused -- each time execCommand is called, + * a new instance of the dialog object is created (and the old one disposed of). + * To enable reusing of the dialog object, subclasses should call + * setReuseDialog() after calling the superclass constructor. + * @param {string} command The command that this plugin handles. + * @constructor + * @extends {goog.editor.Plugin} + */ +goog.editor.plugins.AbstractDialogPlugin = function(command) { + goog.editor.Plugin.call(this); + this.command_ = command; +}; +goog.inherits(goog.editor.plugins.AbstractDialogPlugin, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.AbstractDialogPlugin.prototype.isSupportedCommand = + function(command) { + return command == this.command_; +}; + + +/** + * Handles execCommand. Dialog plugins don't make any changes when they open a + * dialog, just when the dialog closes (because only modal dialogs are + * supported). Hence this method does not dispatch the change events that the + * superclass method does. + * @param {string} command The command to execute. + * @param {...*} var_args Any additional parameters needed to + * execute the command. + * @return {*} The result of the execCommand, if any. + * @override + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.execCommand = function( + command, var_args) { + return this.execCommandInternal.apply(this, arguments); +}; + + +// *** Events *************************************************************** // + + +/** + * Event type constants for events the dialog plugins fire. + * @enum {string} + */ +goog.editor.plugins.AbstractDialogPlugin.EventType = { + // This event is fired when a dialog has been opened. + OPENED: 'dialogOpened', + // This event is fired when a dialog has been closed. + CLOSED: 'dialogClosed' +}; + + +// *** Protected interface ************************************************** // + + +/** + * Creates a new instance of this plugin's dialog. Must be overridden by + * subclasses. + * @param {!goog.dom.DomHelper} dialogDomHelper The dom helper to be used to + * create the dialog. + * @param {*=} opt_arg The dialog specific argument. Concrete subclasses should + * declare a specific type. + * @return {goog.ui.editor.AbstractDialog} The newly created dialog. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.createDialog = + goog.abstractMethod; + + +/** + * Returns the current dialog that was created and opened by this plugin. + * @return {goog.ui.editor.AbstractDialog} The current dialog that was created + * and opened by this plugin. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.getDialog = function() { + return this.dialog_; +}; + + +/** + * Sets whether this plugin should reuse the same instance of the dialog each + * time execCommand is called or create a new one. This is intended for use by + * subclasses only, hence protected. + * @param {boolean} reuse Whether to reuse the dialog. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.setReuseDialog = + function(reuse) { + this.reuseDialog_ = reuse; +}; + + +/** + * Handles execCommand by opening the dialog. Dispatches + * {@link goog.editor.plugins.AbstractDialogPlugin.EventType.OPENED} after the + * dialog is shown. + * @param {string} command The command to execute. + * @param {*=} opt_arg The dialog specific argument. Should be the same as + * {@link createDialog}. + * @return {*} Always returns true, indicating the dialog was shown. + * @protected + * @override + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.execCommandInternal = + function(command, opt_arg) { + // If this plugin should not reuse dialog instances, first dispose of the + // previous dialog. + if (!this.reuseDialog_) { + this.disposeDialog_(); + } + // If there is no dialog yet (or we aren't reusing the previous one), create + // one. + if (!this.dialog_) { + this.dialog_ = this.createDialog( + // TODO(user): Add Field.getAppDomHelper. (Note dom helper will + // need to be updated if setAppWindow is called by clients.) + goog.dom.getDomHelper(this.getFieldObject().getAppWindow()), + opt_arg); + } + + // Since we're opening a dialog, we need to clear the selection because the + // focus will be going to the dialog, and if we leave an selection in the + // editor while another selection is active in the dialog as the user is + // typing, some browsers will screw up the original selection. But first we + // save it so we can restore it when the dialog closes. + // getRange may return null if there is no selection in the field. + var tempRange = this.getFieldObject().getRange(); + // saveUsingDom() did not work as well as saveUsingNormalizedCarets(), + // not sure why. + this.savedRange_ = tempRange && goog.editor.range.saveUsingNormalizedCarets( + tempRange); + goog.dom.Range.clearSelection( + this.getFieldObject().getEditableDomHelper().getWindow()); + + // Listen for the dialog closing so we can clean up. + goog.events.listenOnce(this.dialog_, + goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE, + this.handleAfterHide, + false, + this); + + this.getFieldObject().setModalMode(true); + this.dialog_.show(); + this.dispatchEvent(goog.editor.plugins.AbstractDialogPlugin.EventType.OPENED); + + // Since the selection has left the document, dispatch a selection + // change event. + this.getFieldObject().dispatchSelectionChangeEvent(); + + return true; +}; + + +/** + * Cleans up after the dialog has closed, including restoring the selection to + * what it was before the dialog was opened. If a subclass modifies the editable + * field's content such that the original selection is no longer valid (usually + * the case when the user clicks OK, and sometimes also on Cancel), it is that + * subclass' responsibility to place the selection in the desired place during + * the OK or Cancel (or other) handler. In that case, this method will leave the + * selection in place. + * @param {goog.events.Event} e The AFTER_HIDE event object. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.handleAfterHide = function( + e) { + this.getFieldObject().setModalMode(false); + this.restoreOriginalSelection(); + + if (!this.reuseDialog_) { + this.disposeDialog_(); + } + + this.dispatchEvent(goog.editor.plugins.AbstractDialogPlugin.EventType.CLOSED); + + // Since the selection has returned to the document, dispatch a selection + // change event. + this.getFieldObject().dispatchSelectionChangeEvent(); + + // When the dialog closes due to pressing enter or escape, that happens on the + // keydown event. But the browser will still fire a keyup event after that, + // which is caught by the editable field and causes it to try to fire a + // selection change event. To avoid that, we "debounce" the selection change + // event, meaning the editable field will not fire that event if the keyup + // that caused it immediately after this dialog was hidden ("immediately" + // means a small number of milliseconds defined by the editable field). + this.getFieldObject().debounceEvent( + goog.editor.Field.EventType.SELECTIONCHANGE); +}; + + +/** + * Restores the selection in the editable field to what it was before the dialog + * was opened. This is not guaranteed to work if the contents of the field + * have changed. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.restoreOriginalSelection = + function() { + this.getFieldObject().restoreSavedRange(this.savedRange_); + this.savedRange_ = null; +}; + + +/** + * Cleans up the structure used to save the original selection before the dialog + * was opened. Should be used by subclasses that don't restore the original + * selection via restoreOriginalSelection. + * @protected + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.disposeOriginalSelection = + function() { + if (this.savedRange_) { + this.savedRange_.dispose(); + this.savedRange_ = null; + } +}; + + +/** @override */ +goog.editor.plugins.AbstractDialogPlugin.prototype.disposeInternal = + function() { + this.disposeDialog_(); + goog.editor.plugins.AbstractDialogPlugin.base(this, 'disposeInternal'); +}; + + +// *** Private implementation *********************************************** // + + +/** + * The command that this plugin handles. + * @type {string} + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.command_; + + +/** + * The current dialog that was created and opened by this plugin. + * @type {goog.ui.editor.AbstractDialog} + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.dialog_; + + +/** + * Whether this plugin should reuse the same instance of the dialog each time + * execCommand is called or create a new one. + * @type {boolean} + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.reuseDialog_ = false; + + +/** + * Mutex to prevent recursive calls to disposeDialog_. + * @type {boolean} + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.isDisposingDialog_ = false; + + +/** + * SavedRange representing the selection before the dialog was opened. + * @type {goog.dom.SavedRange} + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.savedRange_; + + +/** + * Disposes of the dialog if needed. It is this abstract class' responsibility + * to dispose of the dialog. The "if needed" refers to the fact this method + * might be called twice (nested calls, not sequential) in the dispose flow, so + * if the dialog was already disposed once it should not be disposed again. + * @private + */ +goog.editor.plugins.AbstractDialogPlugin.prototype.disposeDialog_ = function() { + // Wrap disposing the dialog in a mutex. Otherwise disposing it would cause it + // to get hidden (if it is still open) and fire AFTER_HIDE, which in + // turn would cause the dialog to be disposed again (closure only flags an + // object as disposed after the dispose call chain completes, so it doesn't + // prevent recursive dispose calls). + if (this.dialog_ && !this.isDisposingDialog_) { + this.isDisposingDialog_ = true; + this.dialog_.dispose(); + this.dialog_ = null; + this.isDisposingDialog_ = false; + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.html new file mode 100644 index 0000000000..ef1415fc14 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.html @@ -0,0 +1,26 @@ + + + + + + + + Closure Unit Tests - goog.editor.plugins.AbstractDialogPlugin + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.js new file mode 100644 index 0000000000..5bc4f33927 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstractdialogplugin_test.js @@ -0,0 +1,403 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.AbstractDialogPluginTest'); +goog.setTestOnly('goog.editor.plugins.AbstractDialogPluginTest'); + +goog.require('goog.dom.SavedRange'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.AbstractDialogPlugin'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.functions'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.mockmatchers.ArgumentMatcher'); +goog.require('goog.ui.editor.AbstractDialog'); +goog.require('goog.userAgent'); + +var plugin; +var mockCtrl; +var mockField; +var mockSavedRange; +var mockOpenedHandler; +var mockClosedHandler; + +var COMMAND = 'myCommand'; +var stubs = new goog.testing.PropertyReplacer(); + +var mockClock; +var fieldObj; +var fieldElem; +var mockHandler; + +function setUp() { + mockCtrl = new goog.testing.MockControl(); + mockOpenedHandler = mockCtrl.createLooseMock(goog.events.EventHandler); + mockClosedHandler = mockCtrl.createLooseMock(goog.events.EventHandler); + + mockField = new goog.testing.editor.FieldMock(undefined, undefined, {}); + mockCtrl.addMock(mockField); + mockField.focus(); + + plugin = createDialogPlugin(); +} + +function setUpMockRange() { + mockSavedRange = mockCtrl.createLooseMock(goog.dom.SavedRange); + mockSavedRange.restore(); + + stubs.setPath('goog.editor.range.saveUsingNormalizedCarets', + goog.functions.constant(mockSavedRange)); +} + +function tearDown() { + stubs.reset(); + tearDownRealEditableField(); + if (mockClock) { + // Crucial to letting time operations work normally in the rest of tests. + mockClock.dispose(); + } + if (plugin) { + mockField.$setIgnoreUnexpectedCalls(true); + plugin.dispose(); + } +} + + +/** + * Creates a concrete instance of goog.ui.editor.AbstractDialog by adding + * a plain implementation of createDialogControl(). + * @param {goog.dom.DomHelper} dialogDomHelper The dom helper to be used to + * create the dialog. + * @return {goog.ui.editor.AbstractDialog} The created dialog. + */ +function createDialog(domHelper) { + var dialog = new goog.ui.editor.AbstractDialog(domHelper); + dialog.createDialogControl = function() { + return new goog.ui.editor.AbstractDialog.Builder(dialog).build(); + }; + return dialog; +} + + +/** + * Creates a concrete instance of the abstract class + * goog.editor.plugins.AbstractDialogPlugin + * and registers it with the mock editable field being used. + * @return {goog.editor.plugins.AbstractDialogPlugin} The created plugin. + */ +function createDialogPlugin() { + var plugin = new goog.editor.plugins.AbstractDialogPlugin(COMMAND); + plugin.createDialog = createDialog; + plugin.returnControlToEditableField = plugin.restoreOriginalSelection; + plugin.registerFieldObject(mockField); + plugin.addEventListener( + goog.editor.plugins.AbstractDialogPlugin.EventType.OPENED, + mockOpenedHandler); + plugin.addEventListener( + goog.editor.plugins.AbstractDialogPlugin.EventType.CLOSED, + mockClosedHandler); + return plugin; +} + + +/** + * Sets up the mock event handler to expect an OPENED event. + */ +function expectOpened(opt_times) { + mockOpenedHandler.handleEvent(new goog.testing.mockmatchers.ArgumentMatcher( + function(arg) { + return arg.type == + goog.editor.plugins.AbstractDialogPlugin.EventType.OPENED; + })); + mockField.dispatchSelectionChangeEvent(); + if (opt_times) { + mockOpenedHandler.$times(opt_times); + mockField.$times(opt_times); + } +} + + +/** + * Sets up the mock event handler to expect a CLOSED event. + */ +function expectClosed(opt_times) { + mockClosedHandler.handleEvent(new goog.testing.mockmatchers.ArgumentMatcher( + function(arg) { + return arg.type == + goog.editor.plugins.AbstractDialogPlugin.EventType.CLOSED; + })); + mockField.dispatchSelectionChangeEvent(); + if (opt_times) { + mockClosedHandler.$times(opt_times); + mockField.$times(opt_times); + } +} + + +/** + * Tests the simple flow of calling execCommand (which opens the + * dialog) and immediately disposing of the plugin (which closes the dialog). + * @param {boolean=} opt_reuse Whether to set the plugin to reuse its dialog. + */ +function testExecAndDispose(opt_reuse) { + setUpMockRange(); + expectOpened(); + expectClosed(); + mockField.debounceEvent(goog.editor.Field.EventType.SELECTIONCHANGE); + mockCtrl.$replayAll(); + if (opt_reuse) { + plugin.setReuseDialog(true); + } + assertFalse('Dialog should not be open yet', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + plugin.execCommand(COMMAND); + assertTrue('Dialog should be open now', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + var tempDialog = plugin.getDialog(); + plugin.dispose(); + assertFalse('Dialog should not still be open after disposal', + tempDialog.isOpen()); + mockCtrl.$verifyAll(); +} + + +/** + * Tests execCommand and dispose while reusing the dialog. + */ +function testExecAndDisposeReuse() { + testExecAndDispose(true); +} + + +/** + * Tests the flow of calling execCommand (which opens the dialog) and + * then hiding it (simulating that a user did somthing to cause the dialog to + * close). + * @param {boolean} reuse Whether to set the plugin to reuse its dialog. + */ +function testExecAndHide(opt_reuse) { + setUpMockRange(); + expectOpened(); + expectClosed(); + mockField.debounceEvent(goog.editor.Field.EventType.SELECTIONCHANGE); + mockCtrl.$replayAll(); + if (opt_reuse) { + plugin.setReuseDialog(true); + } + assertFalse('Dialog should not be open yet', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + plugin.execCommand(COMMAND); + assertTrue('Dialog should be open now', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + var tempDialog = plugin.getDialog(); + plugin.getDialog().hide(); + assertFalse('Dialog should not still be open after hiding', + tempDialog.isOpen()); + if (opt_reuse) { + assertFalse('Dialog should not be disposed after hiding (will be reused)', + tempDialog.isDisposed()); + } else { + assertTrue('Dialog should be disposed after hiding', + tempDialog.isDisposed()); + } + plugin.dispose(); + mockCtrl.$verifyAll(); +} + + +/** + * Tests execCommand and hide while reusing the dialog. + */ +function testExecAndHideReuse() { + testExecAndHide(true); +} + + +/** + * Tests the flow of calling execCommand (which opens a dialog) and + * then calling it again before the first dialog is closed. This is not + * something anyone should be doing since dialogs are (usually?) modal so the + * user can't do another execCommand before closing the first dialog. But + * since the API makes it possible, I thought it would be good to guard + * against and unit test. + * @param {boolean} reuse Whether to set the plugin to reuse its dialog. + */ +function testExecTwice(opt_reuse) { + setUpMockRange(); + if (opt_reuse) { + expectOpened(2); // The second exec should cause a second OPENED event. + // But the dialog was not closed between exec calls, so only one CLOSED is + // expected. + expectClosed(); + plugin.setReuseDialog(true); + mockField.debounceEvent(goog.editor.Field.EventType.SELECTIONCHANGE); + } else { + expectOpened(2); // The second exec should cause a second OPENED event. + // The first dialog will be disposed so there should be two CLOSED events. + expectClosed(2); + mockSavedRange.restore(); // Expected 2x, once already recorded in setup. + mockField.focus(); // Expected 2x, once already recorded in setup. + mockField.debounceEvent(goog.editor.Field.EventType.SELECTIONCHANGE); + mockField.$times(2); + } + mockCtrl.$replayAll(); + + assertFalse('Dialog should not be open yet', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + plugin.execCommand(COMMAND); + assertTrue('Dialog should be open now', + !!plugin.getDialog() && plugin.getDialog().isOpen()); + + var tempDialog = plugin.getDialog(); + plugin.execCommand(COMMAND); + if (opt_reuse) { + assertTrue('Reused dialog should still be open after second exec', + tempDialog.isOpen()); + assertFalse('Reused dialog should not be disposed after second exec', + tempDialog.isDisposed()); + } else { + assertFalse('First dialog should not still be open after opening second', + tempDialog.isOpen()); + assertTrue('First dialog should be disposed after opening second', + tempDialog.isDisposed()); + } + plugin.dispose(); + mockCtrl.$verifyAll(); +} + + +/** + * Tests execCommand twice while reusing the dialog. + */ +function testExecTwiceReuse() { + // Test is failing with an out-of-memory error in IE7. + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { + return; + } + + testExecTwice(true); +} + + +/** + * Tests that the selection is cleared when the dialog opens and is + * correctly restored after it closes. + */ +function testRestoreSelection() { + setUpRealEditableField(); + + fieldObj.setHtml(false, '12345'); + var elem = fieldObj.getElement(); + var helper = new goog.testing.editor.TestHelper(elem); + helper.select('12345', 1, '12345', 4); // Selects '234'. + + assertEquals('Incorrect text selected before dialog is opened', + '234', + fieldObj.getRange().getText()); + plugin.execCommand(COMMAND); + if (!goog.userAgent.IE && !goog.userAgent.OPERA) { + // IE returns some bogus range when field doesn't have selection. + // Opera can't remove the selection from a whitebox field. + assertNull('There should be no selection while dialog is open', + fieldObj.getRange()); + } + plugin.getDialog().hide(); + assertEquals('Incorrect text selected after dialog is closed', + '234', + fieldObj.getRange().getText()); +} + + +/** + * Setup a real editable field (instead of a mock) and register the plugin to + * it. + */ +function setUpRealEditableField() { + fieldElem = document.createElement('div'); + fieldElem.id = 'myField'; + document.body.appendChild(fieldElem); + fieldObj = new goog.editor.Field('myField', document); + fieldObj.makeEditable(); + // Register the plugin to that field. + plugin.getTrogClassId = goog.functions.constant('myClassId'); + fieldObj.registerPlugin(plugin); +} + + +/** + * Tear down the real editable field. + */ +function tearDownRealEditableField() { + if (fieldObj) { + fieldObj.makeUneditable(); + fieldObj.dispose(); + fieldObj = null; + } + if (fieldElem && fieldElem.parentNode == document.body) { + document.body.removeChild(fieldElem); + } +} + + +/** + * Tests that after the dialog is hidden via a keystroke, the editable field + * doesn't fire an extra SELECTIONCHANGE event due to the keyup from that + * keystroke. + * There is also a robot test in dialog_robot.html to test debouncing the + * SELECTIONCHANGE event when the dialog closes. + */ +function testDebounceSelectionChange() { + mockClock = new goog.testing.MockClock(true); + // Initial time is 0 which evaluates to false in debouncing implementation. + mockClock.tick(1); + + setUpRealEditableField(); + + // Set up a mock event handler to make sure selection change isn't fired + // more than once on close and a second time on close. + var count = 0; + fieldObj.addEventListener(goog.editor.Field.EventType.SELECTIONCHANGE, + function(e) { + count++; + }); + + assertEquals(0, count); + plugin.execCommand(COMMAND); + assertEquals(1, count); + plugin.getDialog().hide(); + assertEquals(2, count); + + // Fake the keyup event firing on the field after the dialog closes. + var e = new goog.events.Event('keyup', plugin.fieldObject.getElement()); + e.keyCode = 13; + goog.testing.events.fireBrowserEvent(e); + + // Tick the mock clock so that selection change tries to fire. + mockClock.tick(goog.editor.Field.SELECTION_CHANGE_FREQUENCY_ + 1); + + // Ensure the handler did not fire again. + assertEquals(2, count); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler.js new file mode 100644 index 0000000000..de1a13ad8b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler.js @@ -0,0 +1,78 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Abstract Editor plugin class to handle tab keys. Has one + * abstract method which should be overriden to handle a tab key press. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.editor.plugins.AbstractTabHandler'); + +goog.require('goog.editor.Plugin'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.userAgent'); + + + +/** + * Plugin to handle tab keys. Specific tab behavior defined by subclasses. + * + * @constructor + * @extends {goog.editor.Plugin} + */ +goog.editor.plugins.AbstractTabHandler = function() { + goog.editor.Plugin.call(this); +}; +goog.inherits(goog.editor.plugins.AbstractTabHandler, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.AbstractTabHandler.prototype.getTrogClassId = + goog.abstractMethod; + + +/** @override */ +goog.editor.plugins.AbstractTabHandler.prototype.handleKeyboardShortcut = + function(e, key, isModifierPressed) { + // If a dialog doesn't have selectable field, Moz grabs the event and + // performs actions in editor window. This solves that problem and allows + // the event to be passed on to proper handlers. + if (goog.userAgent.GECKO && this.getFieldObject().inModalMode()) { + return false; + } + + // Don't handle Ctrl+Tab since the user is most likely trying to switch + // browser tabs. See bug 1305086. + // FF3 on Mac sends Ctrl-Tab to trogedit and we end up inserting a tab, but + // then it also switches the tabs. See bug 1511681. Note that we don't use + // isModifierPressed here since isModifierPressed is true only if metaKey + // is true on Mac. + if (e.keyCode == goog.events.KeyCodes.TAB && !e.metaKey && !e.ctrlKey) { + return this.handleTabKey(e); + } + + return false; +}; + + +/** + * Handle a tab key press. + * @param {goog.events.Event} e The key event. + * @return {boolean} Whether this event was handled by this plugin. + * @protected + */ +goog.editor.plugins.AbstractTabHandler.prototype.handleTabKey = + goog.abstractMethod; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.html new file mode 100644 index 0000000000..c6cb94050b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.html @@ -0,0 +1,28 @@ + + + + + + + + Closure Unit Tests - goog.editor.plugins.AbstractTabHandler + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.js new file mode 100644 index 0000000000..a02469f358 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/abstracttabhandler_test.js @@ -0,0 +1,81 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.AbstractTabHandlerTest'); +goog.setTestOnly('goog.editor.plugins.AbstractTabHandlerTest'); + +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.AbstractTabHandler'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.testing.StrictMock'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var tabHandler; +var editableField; +var handleTabKeyCalled = false; + +function setUp() { + editableField = new goog.testing.editor.FieldMock(); + editableField.inModalMode = goog.editor.Field.prototype.inModalMode; + editableField.setModalMode = goog.editor.Field.prototype.setModalMode; + + tabHandler = new goog.editor.plugins.AbstractTabHandler(); + tabHandler.registerFieldObject(editableField); + tabHandler.handleTabKey = function(e) { + handleTabKeyCalled = true; + return true; + }; +} + +function tearDown() { + tabHandler.dispose(); +} + +function testHandleKey() { + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.ctrlKey = false; + event.metaKey = false; + + assertTrue('Event must be handled when no modifier keys are pressed.', + tabHandler.handleKeyboardShortcut(event, '', false)); + assertTrue(handleTabKeyCalled); + handleTabKeyCalled = false; + + editableField.setModalMode(true); + if (goog.userAgent.GECKO) { + assertFalse('Event must not be handled when in modal mode', + tabHandler.handleKeyboardShortcut(event, '', false)); + assertFalse(handleTabKeyCalled); + } else { + assertTrue('Event must be handled when in modal mode', + tabHandler.handleKeyboardShortcut(event, '', false)); + assertTrue(handleTabKeyCalled); + handleTabKeyCalled = false; + } + + event.ctrlKey = true; + assertFalse('Plugin must never handle tab key press when ctrlKey is pressed.', + tabHandler.handleKeyboardShortcut(event, '', false)); + assertFalse(handleTabKeyCalled); + + event.ctrlKey = false; + event.metaKey = true; + assertFalse('Plugin must never handle tab key press when metaKey is pressed.', + tabHandler.handleKeyboardShortcut(event, '', false)); + assertFalse(handleTabKeyCalled); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter.js new file mode 100644 index 0000000000..c19a660a39 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter.js @@ -0,0 +1,1769 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Functions to style text. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.plugins.BasicTextFormatter'); +goog.provide('goog.editor.plugins.BasicTextFormatter.COMMAND'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Link'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.node'); +goog.require('goog.editor.range'); +goog.require('goog.editor.style'); +goog.require('goog.iter'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.log'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.style'); +goog.require('goog.ui.editor.messages'); +goog.require('goog.userAgent'); + + + +/** + * Functions to style text (e.g. underline, make bold, etc.) + * @constructor + * @extends {goog.editor.Plugin} + */ +goog.editor.plugins.BasicTextFormatter = function() { + goog.editor.Plugin.call(this); +}; +goog.inherits(goog.editor.plugins.BasicTextFormatter, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.BasicTextFormatter.prototype.getTrogClassId = function() { + return 'BTF'; +}; + + +/** + * Logging object. + * @type {goog.log.Logger} + * @protected + * @override + */ +goog.editor.plugins.BasicTextFormatter.prototype.logger = + goog.log.getLogger('goog.editor.plugins.BasicTextFormatter'); + + +/** + * Commands implemented by this plugin. + * @enum {string} + */ +goog.editor.plugins.BasicTextFormatter.COMMAND = { + LINK: '+link', + FORMAT_BLOCK: '+formatBlock', + INDENT: '+indent', + OUTDENT: '+outdent', + STRIKE_THROUGH: '+strikeThrough', + HORIZONTAL_RULE: '+insertHorizontalRule', + SUBSCRIPT: '+subscript', + SUPERSCRIPT: '+superscript', + UNDERLINE: '+underline', + BOLD: '+bold', + ITALIC: '+italic', + FONT_SIZE: '+fontSize', + FONT_FACE: '+fontName', + FONT_COLOR: '+foreColor', + BACKGROUND_COLOR: '+backColor', + ORDERED_LIST: '+insertOrderedList', + UNORDERED_LIST: '+insertUnorderedList', + JUSTIFY_CENTER: '+justifyCenter', + JUSTIFY_FULL: '+justifyFull', + JUSTIFY_RIGHT: '+justifyRight', + JUSTIFY_LEFT: '+justifyLeft' +}; + + +/** + * Inverse map of execCommand strings to + * {@link goog.editor.plugins.BasicTextFormatter.COMMAND} constants. Used to + * determine whether a string corresponds to a command this plugin + * handles in O(1) time. + * @type {Object} + * @private + */ +goog.editor.plugins.BasicTextFormatter.SUPPORTED_COMMANDS_ = + goog.object.transpose(goog.editor.plugins.BasicTextFormatter.COMMAND); + + +/** + * Whether the string corresponds to a command this plugin handles. + * @param {string} command Command string to check. + * @return {boolean} Whether the string corresponds to a command + * this plugin handles. + * @override + */ +goog.editor.plugins.BasicTextFormatter.prototype.isSupportedCommand = function( + command) { + // TODO(user): restore this to simple check once table editing + // is moved out into its own plugin + return command in goog.editor.plugins.BasicTextFormatter.SUPPORTED_COMMANDS_; +}; + + +/** + * @return {goog.dom.AbstractRange} The closure range object that wraps the + * current user selection. + * @private + */ +goog.editor.plugins.BasicTextFormatter.prototype.getRange_ = function() { + return this.getFieldObject().getRange(); +}; + + +/** + * @return {!Document} The document object associated with the currently active + * field. + * @private + */ +goog.editor.plugins.BasicTextFormatter.prototype.getDocument_ = function() { + return this.getFieldDomHelper().getDocument(); +}; + + +/** + * Execute a user-initiated command. + * @param {string} command Command to execute. + * @param {...*} var_args For color commands, this + * should be the hex color (with the #). For FORMAT_BLOCK, this should be + * the goog.editor.plugins.BasicTextFormatter.BLOCK_COMMAND. + * It will be unused for other commands. + * @return {Object|undefined} The result of the command. + * @override + */ +goog.editor.plugins.BasicTextFormatter.prototype.execCommandInternal = function( + command, var_args) { + var preserveDir, styleWithCss, needsFormatBlockDiv, hasDummySelection; + var result; + var opt_arg = arguments[1]; + + switch (command) { + case goog.editor.plugins.BasicTextFormatter.COMMAND.BACKGROUND_COLOR: + // Don't bother for no color selected, color picker is resetting itself. + if (!goog.isNull(opt_arg)) { + if (goog.editor.BrowserFeature.EATS_EMPTY_BACKGROUND_COLOR) { + this.applyBgColorManually_(opt_arg); + } else if (goog.userAgent.OPERA) { + // backColor will color the block level element instead of + // the selected span of text in Opera. + this.execCommandHelper_('hiliteColor', opt_arg); + } else { + this.execCommandHelper_(command, opt_arg); + } + } + break; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.LINK: + result = this.toggleLink_(opt_arg); + break; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT: + this.justify_(command); + break; + + default: + if (goog.userAgent.IE && + command == + goog.editor.plugins.BasicTextFormatter.COMMAND.FORMAT_BLOCK && + opt_arg) { + // IE requires that the argument be in the form of an opening + // tag, like

    , including angle brackets. WebKit will accept + // the arguemnt with or without brackets, and Firefox pre-3 supports + // only a fixed subset of tags with brackets, and prefers without. + // So we only add them IE only. + opt_arg = '<' + opt_arg + '>'; + } + + if (command == + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_COLOR && + goog.isNull(opt_arg)) { + // If we don't have a color, then FONT_COLOR is a no-op. + break; + } + + switch (command) { + case goog.editor.plugins.BasicTextFormatter.COMMAND.INDENT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.OUTDENT: + if (goog.editor.BrowserFeature.HAS_STYLE_WITH_CSS) { + if (goog.userAgent.GECKO) { + styleWithCss = true; + } + if (goog.userAgent.OPERA) { + if (command == + goog.editor.plugins.BasicTextFormatter.COMMAND.OUTDENT) { + // styleWithCSS actually sets negative margins on
    + // to outdent them. If the command is enabled without + // styleWithCSS flipped on, then the caret is in a blockquote so + // styleWithCSS must not be used. But if the command is not + // enabled, styleWithCSS should be used so that elements such as + // a
    with a margin-left style can still be outdented. + // (Opera bug: CORE-21118) + styleWithCss = + !this.getDocument_().queryCommandEnabled('outdent'); + } else { + // Always use styleWithCSS for indenting. Otherwise, Opera will + // make separate
    s around *each* indented line, + // which adds big default
    margins between each + // indented line. + styleWithCss = true; + } + } + } + // Fall through. + + case goog.editor.plugins.BasicTextFormatter.COMMAND.ORDERED_LIST: + case goog.editor.plugins.BasicTextFormatter.COMMAND.UNORDERED_LIST: + if (goog.editor.BrowserFeature.LEAVES_P_WHEN_REMOVING_LISTS && + this.queryCommandStateInternal_(this.getDocument_(), + command)) { + // IE leaves behind P tags when unapplying lists. + // If we're not in P-mode, then we want divs + // So, unlistify, then convert the Ps into divs. + needsFormatBlockDiv = this.getFieldObject().queryCommandValue( + goog.editor.Command.DEFAULT_TAG) != goog.dom.TagName.P; + } else if (!goog.editor.BrowserFeature.CAN_LISTIFY_BR) { + // IE doesn't convert BRed line breaks into separate list items. + // So convert the BRs to divs, then do the listify. + this.convertBreaksToDivs_(); + } + + // This fix only works in Gecko. + if (goog.userAgent.GECKO && + goog.editor.BrowserFeature.FORGETS_FORMATTING_WHEN_LISTIFYING && + !this.queryCommandValue(command)) { + hasDummySelection |= this.beforeInsertListGecko_(); + } + // Fall through to preserveDir block + + case goog.editor.plugins.BasicTextFormatter.COMMAND.FORMAT_BLOCK: + // Both FF & IE may lose directionality info. Save/restore it. + // TODO(user): Does Safari also need this? + // TODO (gmark, jparent): This isn't ideal because it uses a string + // literal, so if the plugin name changes, it would break. We need a + // better solution. See also other places in code that use + // this.getPluginByClassId('Bidi'). + preserveDir = !!this.getFieldObject().getPluginByClassId('Bidi'); + break; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT: + if (goog.editor.BrowserFeature.NESTS_SUBSCRIPT_SUPERSCRIPT) { + // This browser nests subscript and superscript when both are + // applied, instead of canceling out the first when applying the + // second. + this.applySubscriptSuperscriptWorkarounds_(command); + } + break; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.UNDERLINE: + case goog.editor.plugins.BasicTextFormatter.COMMAND.BOLD: + case goog.editor.plugins.BasicTextFormatter.COMMAND.ITALIC: + // If we are applying the formatting, then we want to have + // styleWithCSS false so that we generate html tags (like ). If we + // are unformatting something, we want to have styleWithCSS true so + // that we can unformat both html tags and inline styling. + // TODO(user): What about WebKit and Opera? + styleWithCss = goog.userAgent.GECKO && + goog.editor.BrowserFeature.HAS_STYLE_WITH_CSS && + this.queryCommandValue(command); + break; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_COLOR: + case goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_FACE: + // It is very expensive in FF (order of magnitude difference) to use + // font tags instead of styled spans. Whenever possible, + // force FF to use spans. + // Font size is very expensive too, but FF always uses font tags, + // regardless of which styleWithCSS value you use. + styleWithCss = goog.editor.BrowserFeature.HAS_STYLE_WITH_CSS && + goog.userAgent.GECKO; + } + + /** + * Cases where we just use the default execCommand (in addition + * to the above fall-throughs) + * goog.editor.plugins.BasicTextFormatter.COMMAND.STRIKE_THROUGH: + * goog.editor.plugins.BasicTextFormatter.COMMAND.HORIZONTAL_RULE: + * goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT: + * goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT: + * goog.editor.plugins.BasicTextFormatter.COMMAND.UNDERLINE: + * goog.editor.plugins.BasicTextFormatter.COMMAND.BOLD: + * goog.editor.plugins.BasicTextFormatter.COMMAND.ITALIC: + * goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE: + * goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_FACE: + */ + this.execCommandHelper_(command, opt_arg, preserveDir, !!styleWithCss); + + if (hasDummySelection) { + this.getDocument_().execCommand('Delete', false, true); + } + + if (needsFormatBlockDiv) { + this.getDocument_().execCommand('FormatBlock', false, '
    '); + } + } + // FF loses focus, so we have to set the focus back to the document or the + // user can't type after selecting from menu. In IE, focus is set correctly + // and resetting it here messes it up. + if (goog.userAgent.GECKO && !this.getFieldObject().inModalMode()) { + this.focusField_(); + } + return result; +}; + + +/** + * Focuses on the field. + * @private + */ +goog.editor.plugins.BasicTextFormatter.prototype.focusField_ = function() { + this.getFieldDomHelper().getWindow().focus(); +}; + + +/** + * Gets the command value. + * @param {string} command The command value to get. + * @return {string|boolean|null} The current value of the command in the given + * selection. NOTE: This return type list is not documented in MSDN or MDC + * and has been constructed from experience. Please update it + * if necessary. + * @override + */ +goog.editor.plugins.BasicTextFormatter.prototype.queryCommandValue = function( + command) { + var styleWithCss; + switch (command) { + case goog.editor.plugins.BasicTextFormatter.COMMAND.LINK: + return this.isNodeInState_(goog.dom.TagName.A); + + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT: + return this.isJustification_(command); + + case goog.editor.plugins.BasicTextFormatter.COMMAND.FORMAT_BLOCK: + // TODO(nicksantos): See if we can use queryCommandValue here. + return goog.editor.plugins.BasicTextFormatter.getSelectionBlockState_( + this.getFieldObject().getRange()); + + case goog.editor.plugins.BasicTextFormatter.COMMAND.INDENT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.OUTDENT: + case goog.editor.plugins.BasicTextFormatter.COMMAND.HORIZONTAL_RULE: + // TODO: See if there are reasonable results to return for + // these commands. + return false; + + case goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE: + case goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_FACE: + case goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_COLOR: + case goog.editor.plugins.BasicTextFormatter.COMMAND.BACKGROUND_COLOR: + // We use queryCommandValue here since we don't just want to know if a + // color/fontface/fontsize is applied, we want to know WHICH one it is. + return this.queryCommandValueInternal_(this.getDocument_(), command, + goog.editor.BrowserFeature.HAS_STYLE_WITH_CSS && + goog.userAgent.GECKO); + + case goog.editor.plugins.BasicTextFormatter.COMMAND.UNDERLINE: + case goog.editor.plugins.BasicTextFormatter.COMMAND.BOLD: + case goog.editor.plugins.BasicTextFormatter.COMMAND.ITALIC: + styleWithCss = goog.editor.BrowserFeature.HAS_STYLE_WITH_CSS && + goog.userAgent.GECKO; + + default: + /** + * goog.editor.plugins.BasicTextFormatter.COMMAND.STRIKE_THROUGH + * goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT + * goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT + * goog.editor.plugins.BasicTextFormatter.COMMAND.UNDERLINE + * goog.editor.plugins.BasicTextFormatter.COMMAND.BOLD + * goog.editor.plugins.BasicTextFormatter.COMMAND.ITALIC + * goog.editor.plugins.BasicTextFormatter.COMMAND.ORDERED_LIST + * goog.editor.plugins.BasicTextFormatter.COMMAND.UNORDERED_LIST + */ + // This only works for commands that use the default execCommand + return this.queryCommandStateInternal_(this.getDocument_(), command, + styleWithCss); + } +}; + + +/** + * @override + */ +goog.editor.plugins.BasicTextFormatter.prototype.prepareContentsHtml = + function(html) { + // If the browser collapses empty nodes and the field has only a script + // tag in it, then it will collapse this node. Which will mean the user + // can't click into it to edit it. + if (goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES && + html.match(/^\s* + + + + + + +
    +
      +
    • foo
    • +
        +
      • foo
      • +
      • bar
      • +
      • baz
      • +
      +
    • bar
    • +
    • baz
    • +
    + ... +
      +
    • foo
    • +
        +
      • foo
      • +
      • bar
      • +
      • baz
      • +
      +
        +
      • foo
      • +
      • bar
      • +
      • baz
      • +
      +
    • bar
    • +
    • baz
    • +
    + +
      +
    1. foo
    2. +
    3. bar
    4. +
    5. baz
    6. +
    + +
    +
      +
    1. switch
    2. +
    3. list
    4. +
    5. type
    6. +
    +
    + +

    before Foo. Bar, baz. after

    +

    before

    Foo.

    Bar,

    baz.

    after

    + +
    lorem
    ipsum
    dolor
    + +

    test

    + + + + + + + + + + +
    head1head2
    one two
    three four
    five
    + + +
    + +
    + +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter_test.js new file mode 100644 index 0000000000..c00a029b90 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/basictextformatter_test.js @@ -0,0 +1,1212 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.BasicTextFormatterTest'); +goog.setTestOnly('goog.editor.plugins.BasicTextFormatterTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.plugins.BasicTextFormatter'); +goog.require('goog.object'); +goog.require('goog.style'); +goog.require('goog.testing.ExpectedFailures'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.mockmatchers'); +goog.require('goog.userAgent'); + +var stubs; + +var SAVED_HTML; +var FIELDMOCK; +var FORMATTER; +var ROOT; +var HELPER; +var OPEN_SUB; +var CLOSE_SUB; +var OPEN_SUPER; +var CLOSE_SUPER; +var MOCK_BLOCKQUOTE_STYLE = 'border-left: 1px solid gray;'; +var MOCK_GET_BLOCKQUOTE_STYLES = function() { + return MOCK_BLOCKQUOTE_STYLE; +}; + +var REAL_FIELD; +var REAL_PLUGIN; + +var expectedFailures; + +function setUpPage() { + stubs = new goog.testing.PropertyReplacer(); + SAVED_HTML = goog.dom.getElement('html').innerHTML; + FIELDMOCK; + FORMATTER; + ROOT = goog.dom.getElement('root'); + HELPER; + OPEN_SUB = + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? + '' : + ''; + CLOSE_SUB = + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? + '' : ''; + OPEN_SUPER = + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? + '' : + ''; + CLOSE_SUPER = + goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('530') ? + '' : ''; + expectedFailures = new goog.testing.ExpectedFailures(); +} + +function setUp() { + FIELDMOCK = new goog.testing.editor.FieldMock(); + + FORMATTER = new goog.editor.plugins.BasicTextFormatter(); + FORMATTER.fieldObject = FIELDMOCK; +} + +function setUpRealField() { + REAL_FIELD = new goog.editor.Field('real-field'); + REAL_PLUGIN = new goog.editor.plugins.BasicTextFormatter(); + REAL_FIELD.registerPlugin(REAL_PLUGIN); + REAL_FIELD.makeEditable(); +} + +function setUpRealFieldIframe() { + REAL_FIELD = new goog.editor.Field('iframe'); + FORMATTER = new goog.editor.plugins.BasicTextFormatter(); + REAL_FIELD.registerPlugin(FORMATTER); + REAL_FIELD.makeEditable(); +} + +function selectRealField() { + goog.dom.Range.createFromNodeContents(REAL_FIELD.getElement()).select(); + REAL_FIELD.dispatchSelectionChangeEvent(); +} + +function tearDown() { + tearDownFontSizeTests(); + + if (REAL_FIELD) { + REAL_FIELD.makeUneditable(); + REAL_FIELD.dispose(); + REAL_FIELD = null; + } + + expectedFailures.handleTearDown(); + stubs.reset(); + + goog.dom.getElement('html').innerHTML = SAVED_HTML; +} + +function setUpListAndBlockquoteTests() { + var htmlDiv = document.getElementById('html'); + HELPER = new goog.testing.editor.TestHelper(htmlDiv); + HELPER.setUpEditableElement(); + + FIELDMOCK.getElement(); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(htmlDiv); +} + +function tearDownHelper() { + HELPER.tearDownEditableElement(); + HELPER.dispose(); + HELPER = null; +} + +function tearDownListAndBlockquoteTests() { + tearDownHelper(); +} + +function testIEList() { + if (goog.userAgent.IE) { + setUpListAndBlockquoteTests(); + + FIELDMOCK.queryCommandValue('rtl').$returns(null); + + FIELDMOCK.$replay(); + var ul = goog.dom.getElement('outerUL'); + goog.dom.Range.createFromNodeContents( + goog.dom.getFirstElementChild(ul).firstChild).select(); + FORMATTER.fixIELists_(); + assertFalse('Unordered list must not have ordered type', ul.type == '1'); + var ol = goog.dom.getElement('ol'); + ol.type = 'disc'; + goog.dom.Range.createFromNodeContents( + goog.dom.getFirstElementChild(ul).firstChild).select(); + FORMATTER.fixIELists_(); + assertFalse('Ordered list must not have unordered type', + ol.type == 'disc'); + ol.type = '1'; + goog.dom.Range.createFromNodeContents( + goog.dom.getFirstElementChild(ul).firstChild).select(); + FORMATTER.fixIELists_(); + assertTrue('Ordered list must retain ordered list type', + ol.type == '1'); + tearDownListAndBlockquoteTests(); + } +} + +function testWebKitList() { + if (goog.userAgent.WEBKIT) { + setUpListAndBlockquoteTests(); + + FIELDMOCK.queryCommandValue('rtl').$returns(null); + + FIELDMOCK.$replay(); + var ul = document.getElementById('outerUL'); + var html = ul.innerHTML; + goog.dom.Range.createFromNodeContents(ul).select(); + + FORMATTER.fixSafariLists_(); + assertEquals('Contents of UL shouldn\'t change', + html, ul.innerHTML); + + ul = document.getElementById('outerUL2'); + goog.dom.Range.createFromNodeContents(ul).select(); + + FORMATTER.fixSafariLists_(); + var childULs = ul.getElementsByTagName('ul'); + assertEquals('UL should have one child UL', + 1, childULs.length); + tearDownListAndBlockquoteTests(); + } +} + +function testGeckoListFont() { + if (goog.userAgent.GECKO) { + setUpListAndBlockquoteTests(); + FIELDMOCK.queryCommandValue( + goog.editor.Command.DEFAULT_TAG).$returns('BR').$times(2); + + FIELDMOCK.$replay(); + var p = goog.dom.getElement('geckolist'); + var font = p.firstChild; + goog.dom.Range.createFromNodeContents(font).select(); + retVal = FORMATTER.beforeInsertListGecko_(); + assertFalse('Workaround shouldn\'t be applied when not needed', retVal); + + font.innerHTML = ''; + goog.dom.Range.createFromNodeContents(font).select(); + var retVal = FORMATTER.beforeInsertListGecko_(); + assertTrue('Workaround should be applied when needed', retVal); + document.execCommand('insertorderedlist', false, true); + assertTrue('Font should be Courier', + /courier/i.test(document.queryCommandValue('fontname'))); + tearDownListAndBlockquoteTests(); + } +} + +function testSwitchListType() { + if (!goog.userAgent.WEBKIT) { + return; + } + // Test that we're not seeing https://bugs.webkit.org/show_bug.cgi?id=19539, + // the type of multi-item lists. + setUpListAndBlockquoteTests(); + + FIELDMOCK.$replay(); + var list = goog.dom.getElement('switchListType'); + var parent = goog.dom.getParentElement(list); + + goog.dom.Range.createFromNodeContents(list).select(); + FORMATTER.execCommandInternal('insertunorderedlist'); + list = goog.dom.getFirstElementChild(parent); + assertEquals(goog.dom.TagName.UL, list.tagName); + assertEquals(3, goog.dom.getElementsByTagNameAndClass( + goog.dom.TagName.LI, null, list).length); + + goog.dom.Range.createFromNodeContents(list).select(); + FORMATTER.execCommandInternal('insertorderedlist'); + list = goog.dom.getFirstElementChild(parent); + assertEquals(goog.dom.TagName.OL, list.tagName); + assertEquals(3, goog.dom.getElementsByTagNameAndClass( + goog.dom.TagName.LI, null, list).length); + + tearDownListAndBlockquoteTests(); +} + +function setUpSubSuperTests() { + ROOT.innerHTML = '12345'; + HELPER = new goog.testing.editor.TestHelper(ROOT); + HELPER.setUpEditableElement(); +} + +function tearDownSubSuperTests() { + tearDownHelper(); +} + +function testSubscriptRemovesSuperscript() { + setUpSubSuperTests(); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 4); // Selects '234'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5'); + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5'); + + FIELDMOCK.$verify(); + tearDownSubSuperTests(); +} + +function testSuperscriptRemovesSubscript() { + setUpSubSuperTests(); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 4); // Selects '234'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUB + '234' + CLOSE_SUB + '5'); + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUPER + '234' + CLOSE_SUPER + '5'); + + FIELDMOCK.$verify(); + tearDownSubSuperTests(); +} + +function testSubscriptRemovesSuperscriptIntersecting() { + // Tests: 12345 , sup(23) , sub(34) ==> 1+sup(2)+sub(34)+5 + // This is more complex because the sub and sup calls are made on separate + // fields which intersect each other and queryCommandValue seems to return + // false if the command is only applied to part of the field. + setUpSubSuperTests(); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 3); // Selects '23'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUPER + '23' + CLOSE_SUPER + '45'); + HELPER.select('23', 1, '45', 1); // Selects '34'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUPER + '2' + CLOSE_SUPER + + OPEN_SUB + '34' + CLOSE_SUB + '5'); + + FIELDMOCK.$verify(); + tearDownSubSuperTests(); +} + +function testSuperscriptRemovesSubscriptIntersecting() { + // Tests: 12345 , sub(23) , sup(34) ==> 1+sub(2)+sup(34)+5 + setUpSubSuperTests(); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 3); // Selects '23'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUBSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUB + '23' + CLOSE_SUB + '45'); + HELPER.select('23', 1, '45', 1); // Selects '34'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.SUPERSCRIPT); + HELPER.assertHtmlMatches('1' + OPEN_SUB + '2' + CLOSE_SUB + + OPEN_SUPER + '34' + CLOSE_SUPER + '5'); + + FIELDMOCK.$verify(); + tearDownSubSuperTests(); +} + +function setUpLinkTests(text, url, isEditable) { + stubs.set(window, 'prompt', function() { + return url; + }); + + ROOT.innerHTML = text; + HELPER = new goog.testing.editor.TestHelper(ROOT); + if (isEditable) { + HELPER.setUpEditableElement(); + FIELDMOCK.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, + goog.testing.mockmatchers.isObject).$returns(undefined); + FORMATTER.focusField_ = function() { + throw 'Field should not be re-focused'; + }; + } + + FIELDMOCK.getElement().$anyTimes().$returns(ROOT); + + FIELDMOCK.setModalMode(true); + + FIELDMOCK.isSelectionEditable().$anyTimes().$returns(isEditable); +} + +function tearDownLinkTests() { + tearDownHelper(); +} + +function testLink() { + setUpLinkTests('12345', 'http://www.x.com/', true); + FIELDMOCK.$replay(); + + HELPER.select('12345', 3); + FORMATTER.execCommandInternal(goog.editor.Command.LINK); + HELPER.assertHtmlMatches( + goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ? + '123http://www.x.com/ 45' : + '123http://www.x.com/45'); + + FIELDMOCK.$verify(); + tearDownLinkTests(); +} + +function testLinks() { + var url1 = 'http://google.com/1'; + var url2 = 'http://google.com/2'; + var dialogUrl = 'http://google.com/3'; + var html = '

    ' + url1 + '

    ' + url2 + '

    '; + setUpLinkTests(html, dialogUrl, true); + FIELDMOCK.$replay(); + + HELPER.select(url1, 0, url2, url2.length); + FORMATTER.execCommandInternal(goog.editor.Command.LINK); + HELPER.assertHtmlMatches('

    ' + url1 + '

    ' + + '' + (goog.userAgent.IE ? dialogUrl : url2) + + '

    '); +} + +function testSelectedLink() { + setUpLinkTests('12345', 'http://www.x.com/', true); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 4); + FORMATTER.execCommandInternal(goog.editor.Command.LINK); + HELPER.assertHtmlMatches( + goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ? + '1234 5' : + '12345'); + + FIELDMOCK.$verify(); + tearDownLinkTests(); +} + +function testCanceledLink() { + setUpLinkTests('12345', undefined, true); + FIELDMOCK.$replay(); + + HELPER.select('12345', 1, '12345', 4); + FORMATTER.execCommandInternal(goog.editor.Command.LINK); + HELPER.assertHtmlMatches('12345'); + + assertEquals('234', FIELDMOCK.getRange().getText()); + + FIELDMOCK.$verify(); + tearDownLinkTests(); +} + +function testUnfocusedLink() { + FIELDMOCK.$reset(); + FIELDMOCK.getEditableDomHelper(). + $anyTimes(). + $returns(goog.dom.getDomHelper(window.document)); + setUpLinkTests('12345', undefined, false); + FIELDMOCK.getRange().$anyTimes().$returns(null); + FIELDMOCK.$replay(); + + FORMATTER.execCommandInternal(goog.editor.Command.LINK); + HELPER.assertHtmlMatches('12345'); + + FIELDMOCK.$verify(); + tearDownLinkTests(); +} + +function setUpJustifyTests(html) { + ROOT.innerHTML = html; + HELPER = new goog.testing.editor.TestHelper(ROOT); + HELPER.setUpEditableElement(ROOT); + + FIELDMOCK.getElement(); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(ROOT); + + FIELDMOCK.getElement(); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(ROOT); +} + +function tearDownJustifyTests() { + tearDownHelper(); +} + +function testJustify() { + setUpJustifyTests('
    abc

    def

    ghi
    '); + FIELDMOCK.$replay(); + + HELPER.select('abc', 1, 'def', 1); // Selects 'bcd'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); + HELPER.assertHtmlMatches( + '
    abc
    ' + + '

    def

    ' + + '
    ghi
    '); + + FIELDMOCK.$verify(); + tearDownJustifyTests(); +} + +function testJustifyInInline() { + setUpJustifyTests('
    abc
    d
    '); + FIELDMOCK.$replay(); + + HELPER.select('b', 0, 'b', 1); // Selects 'b'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); + HELPER.assertHtmlMatches( + '
    abc
    d
    '); + + FIELDMOCK.$verify(); + tearDownJustifyTests(); +} + +function testJustifyInBlock() { + setUpJustifyTests('
    a
    b
    c
    '); + FIELDMOCK.$replay(); + + HELPER.select('b', 0, 'b', 1); // Selects 'h'. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); + HELPER.assertHtmlMatches( + '
    a
    b
    c
    '); + + FIELDMOCK.$verify(); + tearDownJustifyTests(); +} + +var isFontSizeTest = false; +var defaultFontSizeMap; + +function setUpFontSizeTests() { + isFontSizeTest = true; + ROOT.innerHTML = '1234' + + '567'; + HELPER = new goog.testing.editor.TestHelper(ROOT); + HELPER.setUpEditableElement(); + FIELDMOCK.getElement().$returns(ROOT).$anyTimes(); + + // Map representing the sizes of the text in the HTML snippet used in these + // tests. The key is the exact text content of each text node, and the value + // is the initial size of the font in pixels. Since tests may cause a text + // node to be split in two, this also contains keys that initially don't + // match any text node, but may match one later if an existing node is + // split. The value for these keys is null, signifying no text node should + // exist with that content. + defaultFontSizeMap = { + '1': 16, + '2': null, + '23': 2, + '3': null, + '4': 16, + '5': null, + '56': 5, + '6': null, + '7': 16 + }; + assertFontSizes('Assertion failed on default font sizes!', {}); +} + +function tearDownFontSizeTests() { + if (isFontSizeTest) { + tearDownHelper(); + isFontSizeTest = false; + } +} + + +/** + * Asserts that the text nodes set up by setUpFontSizeTests() have had their + * font sizes changed as described by sizeChangesMap. + * @param {string} msg Assertion error message. + * @param {Object} sizeChangesMap Maps the text content + * of a text node to be measured to its expected font size in pixels, or + * null if that text node should not be present in the document (i.e. + * because it was split into two). Only the text nodes that have changed + * from their default need to be specified. + */ +function assertFontSizes(msg, sizeChangesMap) { + goog.object.extend(defaultFontSizeMap, sizeChangesMap); + for (var k in defaultFontSizeMap) { + var node = HELPER.findTextNode(k); + var expected = defaultFontSizeMap[k]; + if (expected) { + assertNotNull(msg + ' [couldn\'t find text node "' + k + '"]', node); + assertEquals(msg + ' [incorrect font size for "' + k + '"]', + expected, goog.style.getFontSize(node.parentNode)); + } else { + assertNull(msg + ' [unexpected text node "' + k + '"]', node); + } + } +} + + +/** + * Regression test for {@bug 1286408}. Tests that when you change the font + * size of a selection, any font size styles that were nested inside are + * removed. + */ +function testFontSizeOverridesStyleAttr() { + setUpFontSizeTests(); + FIELDMOCK.$replay(); + + HELPER.select('1', 0, '4', 1); // Selects 1234. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6); + + assertFontSizes('New font size should override existing font size', + {'1': 32, '23': 32, '4': 32}); + + if (goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR) { + var span = HELPER.findTextNode('23').parentNode; + assertFalse('Style attribute should be gone', + span.getAttributeNode('style') != null && + span.getAttributeNode('style').specified); + } + + FIELDMOCK.$verify(); +} + + +/** + * Make sure the style stripping works when the selection starts and stops in + * different nodes that both contain font size styles. + */ +function testFontSizeOverridesStyleAttrMultiNode() { + setUpFontSizeTests(); + FIELDMOCK.$replay(); + + HELPER.select('23', 0, '56', 2); // Selects 23456. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6); + var span = HELPER.findTextNode('23').parentNode; + var span2 = HELPER.findTextNode('56').parentNode; + + assertFontSizes( + 'New font size should override existing font size in all spans', + {'23': 32, '4': 32, '56': 32}); + var whiteSpace = goog.userAgent.IE ? + goog.style.getCascadedStyle(span2, 'whiteSpace') : + goog.style.getComputedStyle(span2, 'whiteSpace'); + assertEquals('Whitespace style in last span should have been left', + 'pre', whiteSpace); + + if (goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR) { + assertFalse('Style attribute should be gone from first span', + span.getAttributeNode('style') != null && + span.getAttributeNode('style').specified); + assertTrue('Style attribute should not be gone from last span', + span2.getAttributeNode('style') != null && + span2.getAttributeNode('style').specified); + } + + FIELDMOCK.$verify(); +} + + +/** + * Makes sure the font size style is not removed when only a part of the + * element with font size style is selected during the font size command. + */ +function testFontSizeDoesntOverrideStyleAttr() { + setUpFontSizeTests(); + FIELDMOCK.$replay(); + + HELPER.select('23', 1, '4', 1); // Selects 34 (half of span with font size). + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6); + + assertFontSizes( + 'New font size shouldn\'t override existing font size before selection', + {'2': 2, '23': null, '3': 32, '4': 32}); + + FIELDMOCK.$verify(); +} + + +/** + * Makes sure the font size style is not removed when only a part of the + * element with font size style is selected during the font size command, but + * is removed for another element that is fully selected. + */ +function testFontSizeDoesntOverrideStyleAttrMultiNode() { + setUpFontSizeTests(); + FIELDMOCK.$replay(); + + HELPER.select('23', 1, '56', 2); // Selects 3456. + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 6); + + assertFontSizes( + 'New font size shouldn\'t override existing font size before ' + + 'selection, but still override existing font size in last span', + {'2': 2, '23': null, '3': 32, '4': 32, '56': 32}); + + FIELDMOCK.$verify(); +} + + +/** + * Helper to make sure the precondition that executing the font size command + * wraps the content in font tags instead of modifying the style attribute is + * maintained by the browser even if the selection is already text that is + * wrapped in a tag with a font size style. We test this with several + * permutations of how the selection looks: selecting the text in the text + * node, selecting the whole text node as a unit, or selecting the whole span + * node as a unit. Sometimes the browser wraps the text node with the font + * tag, sometimes it wraps the span with the font tag. Either one is ok as + * long as a font tag is actually being used instead of just modifying the + * span's style, because our fix for {@bug 1286408} would remove that style. + * @param {function} doSelect Function to select the "23" text in the test + * content. + */ +function doTestFontSizeStyledSpan(doSelect) { + // Make sure no new browsers start getting this bad behavior. If they do, + // this test will unexpectedly pass. + expectedFailures.expectFailureFor( + !goog.editor.BrowserFeature.DOESNT_OVERRIDE_FONT_SIZE_IN_STYLE_ATTR); + + try { + setUpFontSizeTests(); + FIELDMOCK.$replay(); + + doSelect(); + FORMATTER.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FONT_SIZE, 7); + var parentNode = HELPER.findTextNode('23').parentNode; + var grandparentNode = parentNode.parentNode; + var fontNode = goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.FONT, + undefined, ROOT)[0]; + var spanNode = goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.SPAN, + undefined, ROOT)[0]; + assertTrue('A font tag should have been added either outside or inside' + + ' the existing span', + parentNode == spanNode && grandparentNode == fontNode || + parentNode == fontNode && grandparentNode == spanNode); + + FIELDMOCK.$verify(); + } catch (e) { + expectedFailures.handleException(e); + } +} + +function testFontSizeStyledSpanSelectingText() { + doTestFontSizeStyledSpan(function() { + HELPER.select('23', 0, '23', 2); + }); +} + +function testFontSizeStyledSpanSelectingTextNode() { + doTestFontSizeStyledSpan(function() { + var textNode = HELPER.findTextNode('23'); + HELPER.select(textNode.parentNode, 0, textNode.parentNode, 1); + }); +} + +function testFontSizeStyledSpanSelectingSpanNode() { + doTestFontSizeStyledSpan(function() { + var spanNode = HELPER.findTextNode('23').parentNode; + HELPER.select(spanNode.parentNode, 1, spanNode.parentNode, 2); + }); +} + +function setUpIframeField(content) { + var ifr = document.getElementById('iframe'); + var body = ifr.contentWindow.document.body; + body.innerHTML = content; + + HELPER = new goog.testing.editor.TestHelper(body); + HELPER.setUpEditableElement(); + FIELDMOCK = new goog.testing.editor.FieldMock(ifr.contentWindow); + FIELDMOCK.getElement(); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(body); + FIELDMOCK.queryCommandValue('rtl'); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(null); + FORMATTER.fieldObject = FIELDMOCK; +} + +function tearDownIframeField() { + tearDownHelper(); +} + +function setUpConvertBreaksToDivTests() { + ROOT.innerHTML = '

    paragraph

    one
    two


    three'; + HELPER = new goog.testing.editor.TestHelper(ROOT); + HELPER.setUpEditableElement(); + + FIELDMOCK.getElement(); + FIELDMOCK.$anyTimes(); + FIELDMOCK.$returns(ROOT); +} + +function tearDownConvertBreaksToDivTests() { + tearDownHelper(); +} + + +/** @bug 1414941 */ +function testConvertBreaksToDivsKeepsP() { + if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) { + return; + } + setUpConvertBreaksToDivTests(); + FIELDMOCK.$replay(); + + HELPER.select('three', 0); + FORMATTER.convertBreaksToDivs_(); + assertEquals('There should still be a

    tag', + 1, FIELDMOCK.getElement().getElementsByTagName('p').length); + var html = FIELDMOCK.getElement().innerHTML.toLowerCase(); + assertNotBadBrElements(html); + assertNotContains('There should not be empty

    elements', + '
    <\/div>', html); + + FIELDMOCK.$verify(); + tearDownConvertBreaksToDivTests(); +} + + +/** +* @bug 1414937 +* @bug 934535 +*/ +function testConvertBreaksToDivsDoesntCollapseBR() { + if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) { + return; + } + setUpConvertBreaksToDivTests(); + FIELDMOCK.$replay(); + + HELPER.select('three', 0); + FORMATTER.convertBreaksToDivs_(); + var html = FIELDMOCK.getElement().innerHTML.toLowerCase(); + assertNotBadBrElements(html); + assertNotContains('There should not be empty
    elements', + '
    <\/div>', html); + if (goog.userAgent.IE) { + //

    misbehaves in IE + assertNotContains( + '
    should not be used to prevent
    from collapsing', + '

    <\/div>', html); + } + + FIELDMOCK.$verify(); + tearDownConvertBreaksToDivTests(); +} + +function testConvertBreaksToDivsSelection() { + if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) { + return; + } + setUpConvertBreaksToDivTests(); + FIELDMOCK.$replay(); + + HELPER.select('two', 1, 'three', 3); + var before = FIELDMOCK.getRange().getText().replace(/\s/g, ''); + FORMATTER.convertBreaksToDivs_(); + assertEquals('Selection must not be changed', + before, FIELDMOCK.getRange().getText().replace(/\s/g, '')); + + FIELDMOCK.$verify(); + tearDownConvertBreaksToDivTests(); +} + + +/** @bug 1414937 */ +function testConvertBreaksToDivsInsertList() { + setUpConvertBreaksToDivTests(); + FIELDMOCK.$replay(); + + HELPER.select('three', 0); + FORMATTER.execCommandInternal('insertorderedlist'); + assertTrue('Ordered list must be inserted', + FIELDMOCK.getEditableDomHelper().getDocument().queryCommandState( + 'insertorderedlist')); + tearDownConvertBreaksToDivTests(); +} + + +/** + * Regression test for {@bug 1939883}, where if a br has an id, it causes + * the convert br code to throw a js error. This goes a step further and + * ensures that the id is preserved in the resulting div element. + */ +function testConvertBreaksToDivsKeepsId() { + if (goog.editor.BrowserFeature.CAN_LISTIFY_BR) { + return; + } + setUpConvertBreaksToDivTests(); + FIELDMOCK.$replay(); + + HELPER.select('one', 0, 'two', 0); + FORMATTER.convertBreaksToDivs_(); + var html = FIELDMOCK.getElement().innerHTML.toLowerCase(); + assertNotBadBrElements(html); + var idBr = document.getElementById('br1'); + assertNotNull('There should still be a tag with id="br1"', idBr); + assertEquals('The tag with id="br1" should be a
    now', + goog.dom.TagName.DIV, + idBr.tagName); + assertNull('There should not be any tag with id="temp_br"', + document.getElementById('temp_br')); + + FIELDMOCK.$verify(); + tearDownConvertBreaksToDivTests(); +} + + +/** + * @bug 2420054 + */ +var JUSTIFICATION_COMMANDS = [ + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT, + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT, + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER, + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL +]; +function doTestIsJustification(command) { + setUpRealField(); + REAL_FIELD.setHtml(false, 'foo'); + selectRealField(); + REAL_FIELD.execCommand(command); + + for (var i = 0; i < JUSTIFICATION_COMMANDS.length; i++) { + if (JUSTIFICATION_COMMANDS[i] == command) { + assertTrue('queryCommandValue(' + JUSTIFICATION_COMMANDS[i] + + ') should be true after execCommand(' + command + ')', + REAL_FIELD.queryCommandValue(JUSTIFICATION_COMMANDS[i])); + } else { + assertFalse('queryCommandValue(' + JUSTIFICATION_COMMANDS[i] + + ') should be false after execCommand(' + command + ')', + REAL_FIELD.queryCommandValue(JUSTIFICATION_COMMANDS[i])); + } + } +} +function testIsJustificationLeft() { + doTestIsJustification( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT); +} +function testIsJustificationRight() { + doTestIsJustification( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT); +} +function testIsJustificationCenter() { + doTestIsJustification( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); +} +function testIsJustificationFull() { + doTestIsJustification( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL); +} + + +/** + * Regression test for {@bug 1414813}, where all 3 justification buttons are + * considered "on" when you first tab into the editable field. In this + * situation, when lorem ipsum is the only node in the editable field iframe + * body, mockField.getRange() returns an empty range. + */ +function testIsJustificationEmptySelection() { + var mockField = new goog.testing.LooseMock(goog.editor.Field); + + mockField.getRange(); + mockField.$anyTimes(); + mockField.$returns(null); + mockField.getPluginByClassId('Bidi'); + mockField.$anyTimes(); + mockField.$returns(null); + FORMATTER.fieldObject = mockField; + + mockField.$replay(); + + assertFalse('Empty selection should not be justified', + FORMATTER.isJustification_( + goog.editor.plugins.BasicTextFormatter. + COMMAND.JUSTIFY_CENTER)); + assertFalse('Empty selection should not be justified', + FORMATTER.isJustification_( + goog.editor.plugins.BasicTextFormatter. + COMMAND.JUSTIFY_FULL)); + assertFalse('Empty selection should not be justified', + FORMATTER.isJustification_( + goog.editor.plugins.BasicTextFormatter. + COMMAND.JUSTIFY_RIGHT)); + assertFalse('Empty selection should not be justified', + FORMATTER.isJustification_( + goog.editor.plugins.BasicTextFormatter. + COMMAND.JUSTIFY_LEFT)); + + mockField.$verify(); +} + +function testIsJustificationSimple1() { + setUpRealField(); + REAL_FIELD.setHtml(false, '
    foo
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertTrue(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationSimple2() { + setUpRealField(); + REAL_FIELD.setHtml(false, '
    foo
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertTrue(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationComplete1() { + setUpRealField(); + REAL_FIELD.setHtml(false, + '
    a
    b
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationComplete2() { + setUpRealField(); + REAL_FIELD.setHtml(false, + '
    a
    b
    '); + selectRealField(); + + assertTrue(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationComplete3() { + setUpRealField(); + REAL_FIELD.setHtml(false, + '
    a
    b
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertTrue(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationComplete4() { + setUpRealField(); + REAL_FIELD.setHtml(false, + '
    a
    ' + + '
    b
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertTrue(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + +function testIsJustificationComplete5() { + setUpRealField(); + REAL_FIELD.setHtml(false, + '
    a
    b' + + '
    c
    '); + selectRealField(); + + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT)); + assertFalse(REAL_FIELD.queryCommandValue( + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT)); +} + + +/** @bug 2472589 */ +function doTestIsJustificationPInDiv(useCss, align, command) { + setUpRealField(); + var html = '

    foo

    '; + + REAL_FIELD.setHtml(false, html); + selectRealField(); + assertTrue( + 'P inside ' + align + ' aligned' + (useCss ? ' (using CSS)' : '') + + ' DIV should be considered ' + align + ' aligned', + REAL_FIELD.queryCommandValue(command)); +} +function testIsJustificationPInDivLeft() { + doTestIsJustificationPInDiv(false, 'left', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT); +} +function testIsJustificationPInDivRight() { + doTestIsJustificationPInDiv(false, 'right', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT); +} +function testIsJustificationPInDivCenter() { + doTestIsJustificationPInDiv(false, 'center', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); +} +function testIsJustificationPInDivJustify() { + doTestIsJustificationPInDiv(false, 'justify', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL); +} +function testIsJustificationPInDivLeftCss() { + doTestIsJustificationPInDiv(true, 'left', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_LEFT); +} +function testIsJustificationPInDivRightCss() { + doTestIsJustificationPInDiv(true, 'right', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_RIGHT); +} +function testIsJustificationPInDivCenterCss() { + doTestIsJustificationPInDiv(true, 'center', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_CENTER); +} +function testIsJustificationPInDivJustifyCss() { + doTestIsJustificationPInDiv(true, 'justify', + goog.editor.plugins.BasicTextFormatter.COMMAND.JUSTIFY_FULL); +} + + +function testPrepareContent() { + setUpRealField(); + assertPreparedContents('\n', '\n'); + + if (goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES) { + assertPreparedContents("  + + + +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote_test.js new file mode 100644 index 0000000000..4fa12681ba --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/blockquote_test.js @@ -0,0 +1,209 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.BlockquoteTest'); +goog.setTestOnly('goog.editor.plugins.BlockquoteTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.plugins.Blockquote'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.jsunit'); + +var SPLIT = ''; +var root, helper, field, plugin; + +function setUp() { + root = goog.dom.getElement('root'); + helper = new goog.testing.editor.TestHelper(root); + field = new goog.testing.editor.FieldMock(); + + helper.setUpEditableElement(); +} + +function tearDown() { + field.$verify(); + helper.tearDownEditableElement(); +} + +function createPlugin(requireClassname, opt_paragraphMode) { + field.queryCommandValue('+defaultTag').$anyTimes().$returns( + opt_paragraphMode ? goog.dom.TagName.P : undefined); + + plugin = new goog.editor.plugins.Blockquote(requireClassname); + plugin.registerFieldObject(field); + plugin.enable(field); +} + +function execCommand() { + field.$replay(); + + // With splitPoint we try to mimic the behavior of EnterHandler's + // deleteCursorSelection_. + var splitPoint = goog.dom.getElement('split-point'); + var position = goog.editor.BrowserFeature.HAS_W3C_RANGES ? + {node: splitPoint.nextSibling, offset: 0} : splitPoint; + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + goog.dom.removeNode(splitPoint); + goog.dom.Range.createCaret(position.node, 0).select(); + } else { + goog.dom.Range.createCaret(position, 0).select(); + } + + var result = plugin.execCommand(goog.editor.plugins.Blockquote.SPLIT_COMMAND, + position); + if (!goog.editor.BrowserFeature.HAS_W3C_RANGES) { + goog.dom.removeNode(splitPoint); + } + + return result; +} + +function testSplitBlockquoteDoesNothingWhenNotInBlockquote() { + root.innerHTML = '
    Test' + SPLIT + 'ing
    '; + + createPlugin(false); + assertFalse(execCommand()); + helper.assertHtmlMatches('
    Testing
    '); +} + +function testSplitBlockquoteDoesNothingWhenNotInBlockquoteWithClass() { + root.innerHTML = '
    Test' + SPLIT + 'ing
    '; + + createPlugin(true); + assertFalse(execCommand()); + helper.assertHtmlMatches('
    Testing
    '); +} + +function testSplitBlockquoteInBlockquoteWithoutClass() { + root.innerHTML = '
    Test' + SPLIT + 'ing
    '; + + createPlugin(false); + assertTrue(execCommand()); + helper.assertHtmlMatches( + '
    Test
    ' + + '
    ' + + (goog.editor.BrowserFeature.HAS_W3C_RANGES ? ' ' : '') + + '
    ' + + '
    ing
    '); +} + +function testSplitBlockquoteInBlockquoteWithoutClassInParagraphMode() { + root.innerHTML = '
    Test' + SPLIT + 'ing
    '; + + createPlugin(false, true); + assertTrue(execCommand()); + helper.assertHtmlMatches( + '
    Test
    ' + + '

    ' + + (goog.editor.BrowserFeature.HAS_W3C_RANGES ? ' ' : '') + + '

    ' + + '
    ing
    '); +} + +function testSplitBlockquoteInBlockquoteWithClass() { + root.innerHTML = + '
    Test' + SPLIT + 'ing
    '; + + createPlugin(true); + assertTrue(execCommand()); + + helper.assertHtmlMatches( + '
    Test
    ' + + '
    ' + + (goog.editor.BrowserFeature.HAS_W3C_RANGES ? ' ' : '') + + '
    ' + + '
    ing
    '); +} + +function testSplitBlockquoteInBlockquoteWithClassInParagraphMode() { + root.innerHTML = + '
    Test' + SPLIT + 'ing
    '; + + createPlugin(true, true); + assertTrue(execCommand()); + helper.assertHtmlMatches( + '
    Test
    ' + + '

    ' + + (goog.editor.BrowserFeature.HAS_W3C_RANGES ? ' ' : '') + + '

    ' + + '
    ing
    '); +} + +function testIsSplittableBlockquoteWhenRequiresClassNameToSplit() { + createPlugin(true); + + var blockquoteWithClassName = goog.dom.createDom('blockquote', 'tr_bq'); + assertTrue('blockquote should be detected as splittable', + plugin.isSplittableBlockquote(blockquoteWithClassName)); + + var blockquoteWithoutClassName = goog.dom.createDom('blockquote', 'foo'); + assertFalse('blockquote should not be detected as splittable', + plugin.isSplittableBlockquote(blockquoteWithoutClassName)); + + var nonBlockquote = goog.dom.createDom('span', 'tr_bq'); + assertFalse('element should not be detected as splittable', + plugin.isSplittableBlockquote(nonBlockquote)); +} + +function testIsSplittableBlockquoteWhenNotRequiresClassNameToSplit() { + createPlugin(false); + + var blockquoteWithClassName = goog.dom.createDom('blockquote', 'tr_bq'); + assertTrue('blockquote should be detected as splittable', + plugin.isSplittableBlockquote(blockquoteWithClassName)); + + var blockquoteWithoutClassName = goog.dom.createDom('blockquote', 'foo'); + assertTrue('blockquote should be detected as splittable', + plugin.isSplittableBlockquote(blockquoteWithoutClassName)); + + var nonBlockquote = goog.dom.createDom('span', 'tr_bq'); + assertFalse('element should not be detected as splittable', + plugin.isSplittableBlockquote(nonBlockquote)); +} + +function testIsSetupBlockquote() { + createPlugin(false); + + var blockquoteWithClassName = goog.dom.createDom('blockquote', 'tr_bq'); + assertTrue('blockquote should be detected as setup', + plugin.isSetupBlockquote(blockquoteWithClassName)); + + var blockquoteWithoutClassName = goog.dom.createDom('blockquote', 'foo'); + assertFalse('blockquote should not be detected as setup', + plugin.isSetupBlockquote(blockquoteWithoutClassName)); + + var nonBlockquote = goog.dom.createDom('span', 'tr_bq'); + assertFalse('element should not be detected as setup', + plugin.isSetupBlockquote(nonBlockquote)); +} + +function testIsUnsetupBlockquote() { + createPlugin(false); + + var blockquoteWithClassName = goog.dom.createDom('blockquote', 'tr_bq'); + assertFalse('blockquote should not be detected as unsetup', + plugin.isUnsetupBlockquote(blockquoteWithClassName)); + + var blockquoteWithoutClassName = goog.dom.createDom('blockquote', 'foo'); + assertTrue('blockquote should be detected as unsetup', + plugin.isUnsetupBlockquote(blockquoteWithoutClassName)); + + var nonBlockquote = goog.dom.createDom('span', 'tr_bq'); + assertFalse('element should not be detected as unsetup', + plugin.isUnsetupBlockquote(nonBlockquote)); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons.js new file mode 100644 index 0000000000..4d0c065812 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons.js @@ -0,0 +1,89 @@ +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved + +/** + * @fileoverview Plugin for generating emoticons. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.plugins.Emoticons'); + +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.range'); +goog.require('goog.functions'); +goog.require('goog.ui.emoji.Emoji'); +goog.require('goog.userAgent'); + + + +/** + * Plugin for generating emoticons. + * + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.Emoticons = function() { + goog.editor.plugins.Emoticons.base(this, 'constructor'); +}; +goog.inherits(goog.editor.plugins.Emoticons, goog.editor.Plugin); + + +/** The emoticon command. */ +goog.editor.plugins.Emoticons.COMMAND = '+emoticon'; + + +/** @override */ +goog.editor.plugins.Emoticons.prototype.getTrogClassId = + goog.functions.constant(goog.editor.plugins.Emoticons.COMMAND); + + +/** @override */ +goog.editor.plugins.Emoticons.prototype.isSupportedCommand = function( + command) { + return command == goog.editor.plugins.Emoticons.COMMAND; +}; + + +/** + * Inserts an emoticon into the editor at the cursor location. Places the + * cursor to the right of the inserted emoticon. + * @param {string} command Command to execute. + * @param {*=} opt_arg Emoji to insert. + * @return {!Object|undefined} The result of the command. + * @override + */ +goog.editor.plugins.Emoticons.prototype.execCommandInternal = function( + command, opt_arg) { + var emoji = /** @type {goog.ui.emoji.Emoji} */ (opt_arg); + var dom = this.getFieldDomHelper(); + var img = dom.createDom(goog.dom.TagName.IMG, { + 'src': emoji.getUrl(), + 'style': 'margin:0 0.2ex;vertical-align:middle' + }); + img.setAttribute(goog.ui.emoji.Emoji.ATTRIBUTE, emoji.getId()); + + this.getFieldObject().getRange().replaceContentsWithNode(img); + + // IE8 does the right thing with the cursor, and has a js error when we try + // to place the cursor manually. + // IE9 loses the cursor when the window is focused, so focus first. + if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9)) { + this.getFieldObject().focus(); + goog.editor.range.placeCursorNextTo(img, false); + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.html new file mode 100644 index 0000000000..d7e696dc41 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.html @@ -0,0 +1,23 @@ + + + + + + + +
    +
    + I am text. +
    +
    + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.js new file mode 100644 index 0000000000..1ea8576fb3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/emoticons_test.js @@ -0,0 +1,84 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.EmoticonsTest'); +goog.setTestOnly('goog.editor.plugins.EmoticonsTest'); + +goog.require('goog.Uri'); +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.Emoticons'); +goog.require('goog.testing.jsunit'); +goog.require('goog.ui.emoji.Emoji'); +goog.require('goog.userAgent'); + +var HTML; + +function setUp() { + HTML = goog.dom.getElement('parent').innerHTML; +} + +function tearDown() { + goog.dom.getElement('parent').innerHTML = HTML; +} + +function testEmojiWithEmoticonsPlugin() { + runEmojiTestWithPlugin(new goog.editor.plugins.Emoticons()); +} + +function runEmojiTestWithPlugin(plugin) { + var field = new goog.editor.Field('testField'); + field.registerPlugin(plugin); + field.makeEditable(); + field.focusAndPlaceCursorAtStart(); + + var src = 'testdata/emoji/4F4.gif'; + var id = '4F4'; + var emoji = new goog.ui.emoji.Emoji(src, id); + field.execCommand(goog.editor.plugins.Emoticons.COMMAND, emoji); + + // The url may be relative or absolute. + var imgs = field.getEditableDomHelper(). + getElementsByTagNameAndClass(goog.dom.TagName.IMG); + assertEquals(1, imgs.length); + + var img = imgs[0]; + assertUriEquals(src, img.getAttribute('src')); + assertEquals(id, img.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE)); + + var range = field.getRange(); + assertNotNull('must have a selection', range); + assertTrue('range must be a cursor', range.isCollapsed()); + if (goog.userAgent.WEBKIT) { + assertEquals('range starts after image', + 2, range.getStartOffset()); + } else if (goog.userAgent.GECKO) { + assertEquals('range starts after image', + 2, goog.array.indexOf(range.getContainerElement().childNodes, + range.getStartNode())); + } + // Firefox 3.6 is still tested, and would fail here - treitel December 2012 + if (!(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher(2))) { + assertEquals('range must be around image', + img.parentElement, range.getContainerElement()); + } +} + +function assertUriEquals(expected, actual) { + var winUri = new goog.Uri(window.location); + assertEquals(winUri.resolve(new goog.Uri(expected)).toString(), + winUri.resolve(new goog.Uri(actual)).toString()); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler.js new file mode 100644 index 0000000000..b6ebc1db90 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler.js @@ -0,0 +1,768 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Plugin to handle enter keys. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.editor.plugins.EnterHandler'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeOffset'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.node'); +goog.require('goog.editor.plugins.Blockquote'); +goog.require('goog.editor.range'); +goog.require('goog.editor.style'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.functions'); +goog.require('goog.object'); +goog.require('goog.string'); +goog.require('goog.userAgent'); + + + +/** + * Plugin to handle enter keys. This does all the crazy to normalize (as much as + * is reasonable) what happens when you hit enter. This also handles the + * special casing of hitting enter in a blockquote. + * + * In IE, Webkit, and Opera, the resulting HTML uses one DIV tag per line. In + * Firefox, the resulting HTML uses BR tags at the end of each line. + * + * @constructor + * @extends {goog.editor.Plugin} + */ +goog.editor.plugins.EnterHandler = function() { + goog.editor.Plugin.call(this); +}; +goog.inherits(goog.editor.plugins.EnterHandler, goog.editor.Plugin); + + +/** + * The type of block level tag to add on enter, for browsers that support + * specifying the default block-level tag. Can be overriden by subclasses; must + * be either DIV or P. + * @type {goog.dom.TagName} + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.tag = goog.dom.TagName.DIV; + + +/** @override */ +goog.editor.plugins.EnterHandler.prototype.getTrogClassId = function() { + return 'EnterHandler'; +}; + + +/** @override */ +goog.editor.plugins.EnterHandler.prototype.enable = function(fieldObject) { + goog.editor.plugins.EnterHandler.base(this, 'enable', fieldObject); + + if (goog.editor.BrowserFeature.SUPPORTS_OPERA_DEFAULTBLOCK_COMMAND && + (this.tag == goog.dom.TagName.P || this.tag == goog.dom.TagName.DIV)) { + var doc = this.getFieldDomHelper().getDocument(); + doc.execCommand('opera-defaultBlock', false, this.tag); + } +}; + + +/** + * If the contents are empty, return the 'default' html for the field. + * The 'default' contents depend on the enter handling mode, so it + * makes the most sense in this plugin. + * @param {string} html The html to prepare. + * @return {string} The original HTML, or default contents if that + * html is empty. + * @override + */ +goog.editor.plugins.EnterHandler.prototype.prepareContentsHtml = function( + html) { + if (!html || goog.string.isBreakingWhitespace(html)) { + return goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? + this.getNonCollapsingBlankHtml() : ''; + } + return html; +}; + + +/** + * Gets HTML with no contents that won't collapse, for browsers that + * collapse the empty string. + * @return {string} Blank html. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.getNonCollapsingBlankHtml = + goog.functions.constant('
    '); + + +/** + * Internal backspace handler. + * @param {goog.events.Event} e The keypress event. + * @param {goog.dom.AbstractRange} range The closure range object. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.handleBackspaceInternal = function(e, + range) { + var field = this.getFieldObject().getElement(); + var container = range && range.getStartNode(); + + if (field.firstChild == container && goog.editor.node.isEmpty(container)) { + e.preventDefault(); + // TODO(user): I think we probably don't need to stopPropagation here + e.stopPropagation(); + } +}; + + +/** + * Fix paragraphs to be the correct type of node. + * @param {goog.events.Event} e The key event. + * @param {boolean} split Whether we already split up a blockquote by + * manually inserting elements. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.processParagraphTagsInternal = + function(e, split) { + // Force IE to turn the node we are leaving into a DIV. If we do turn + // it into a DIV, the node IE creates in response to ENTER will also be + // a DIV. If we don't, it will be a P. We handle that case + // in handleKeyUpIE_ + if (goog.userAgent.IE || goog.userAgent.OPERA) { + this.ensureBlockIeOpera(goog.dom.TagName.DIV); + } else if (!split && goog.userAgent.WEBKIT) { + // WebKit duplicates a blockquote when the user hits enter. Let's cancel + // this and insert a BR instead, to make it more consistent with the other + // browsers. + var range = this.getFieldObject().getRange(); + if (!range || !goog.editor.plugins.EnterHandler.isDirectlyInBlockquote( + range.getContainerElement())) { + return; + } + + var dh = this.getFieldDomHelper(); + var br = dh.createElement(goog.dom.TagName.BR); + range.insertNode(br, true); + + // If the BR is at the end of a block element, Safari still thinks there is + // only one line instead of two, so we need to add another BR in that case. + if (goog.editor.node.isBlockTag(br.parentNode) && + !goog.editor.node.skipEmptyTextNodes(br.nextSibling)) { + goog.dom.insertSiblingBefore( + dh.createElement(goog.dom.TagName.BR), br); + } + + goog.editor.range.placeCursorNextTo(br, false); + e.preventDefault(); + } +}; + + +/** + * Determines whether the lowest containing block node is a blockquote. + * @param {Node} n The node. + * @return {boolean} Whether the deepest block ancestor of n is a blockquote. + */ +goog.editor.plugins.EnterHandler.isDirectlyInBlockquote = function(n) { + for (var current = n; current; current = current.parentNode) { + if (goog.editor.node.isBlockTag(current)) { + return current.tagName == goog.dom.TagName.BLOCKQUOTE; + } + } + + return false; +}; + + +/** + * Internal delete key handler. + * @param {goog.events.Event} e The keypress event. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.handleDeleteGecko = function(e) { + this.deleteBrGecko(e); +}; + + +/** + * Deletes the element at the cursor if it is a BR node, and if it does, calls + * e.preventDefault to stop the browser from deleting. Only necessary in Gecko + * as a workaround for mozilla bug 205350 where deleting a BR that is followed + * by a block element doesn't work (the BR gets immediately replaced). We also + * need to account for an ill-formed cursor which occurs from us trying to + * stop the browser from deleting. + * + * @param {goog.events.Event} e The DELETE keypress event. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.deleteBrGecko = function(e) { + var range = this.getFieldObject().getRange(); + if (range.isCollapsed()) { + var container = range.getEndNode(); + if (container.nodeType == goog.dom.NodeType.ELEMENT) { + var nextNode = container.childNodes[range.getEndOffset()]; + if (nextNode && nextNode.tagName == goog.dom.TagName.BR) { + // We want to retrieve the first non-whitespace previous sibling + // as we could have added an empty text node below and want to + // properly handle deleting a sequence of BR's. + var previousSibling = goog.editor.node.getPreviousSibling(nextNode); + var nextSibling = nextNode.nextSibling; + + container.removeChild(nextNode); + e.preventDefault(); + + // When we delete a BR followed by a block level element, the cursor + // has a line-height which spans the height of the block level element. + // e.g. If we delete a BR followed by a UL, the resulting HTML will + // appear to the end user like:- + // + // | * one + // | * two + // | * three + // + // There are a couple of cases that we have to account for in order to + // properly conform to what the user expects when DELETE is pressed. + // + // 1. If the BR has a previous sibling and the previous sibling is + // not a block level element or a BR, we place the cursor at the + // end of that. + // 2. If the BR doesn't have a previous sibling or the previous sibling + // is a block level element or a BR, we place the cursor at the + // beginning of the leftmost leaf of its next sibling. + if (nextSibling && goog.editor.node.isBlockTag(nextSibling)) { + if (previousSibling && + !(previousSibling.tagName == goog.dom.TagName.BR || + goog.editor.node.isBlockTag(previousSibling))) { + goog.dom.Range.createCaret( + previousSibling, + goog.editor.node.getLength(previousSibling)).select(); + } else { + var leftMostLeaf = goog.editor.node.getLeftMostLeaf(nextSibling); + goog.dom.Range.createCaret(leftMostLeaf, 0).select(); + } + } + } + } + } +}; + + +/** @override */ +goog.editor.plugins.EnterHandler.prototype.handleKeyPress = function(e) { + // If a dialog doesn't have selectable field, Gecko grabs the event and + // performs actions in editor window. This solves that problem and allows + // the event to be passed on to proper handlers. + if (goog.userAgent.GECKO && this.getFieldObject().inModalMode()) { + return false; + } + + // Firefox will allow the first node in an iframe to be deleted + // on a backspace. Disallow it if the node is empty. + if (e.keyCode == goog.events.KeyCodes.BACKSPACE) { + this.handleBackspaceInternal(e, this.getFieldObject().getRange()); + + } else if (e.keyCode == goog.events.KeyCodes.ENTER) { + if (goog.userAgent.GECKO) { + if (!e.shiftKey) { + // Behave similarly to IE's content editable return carriage: + // If the shift key is down or specified by the application, insert a + // BR, otherwise split paragraphs + this.handleEnterGecko_(e); + } + } else { + // In Gecko-based browsers, this is handled in the handleEnterGecko_ + // method. + this.getFieldObject().dispatchBeforeChange(); + var cursorPosition = this.deleteCursorSelection_(); + + var split = !!this.getFieldObject().execCommand( + goog.editor.plugins.Blockquote.SPLIT_COMMAND, cursorPosition); + if (split) { + // TODO(user): I think we probably don't need to stopPropagation here + e.preventDefault(); + e.stopPropagation(); + } + + this.releasePositionObject_(cursorPosition); + + if (goog.userAgent.WEBKIT) { + this.handleEnterWebkitInternal(e); + } + + this.processParagraphTagsInternal(e, split); + this.getFieldObject().dispatchChange(); + } + + } else if (goog.userAgent.GECKO && e.keyCode == goog.events.KeyCodes.DELETE) { + this.handleDeleteGecko(e); + } + + return false; +}; + + +/** @override */ +goog.editor.plugins.EnterHandler.prototype.handleKeyUp = function(e) { + // If a dialog doesn't have selectable field, Gecko grabs the event and + // performs actions in editor window. This solves that problem and allows + // the event to be passed on to proper handlers. + if (goog.userAgent.GECKO && this.getFieldObject().inModalMode()) { + return false; + } + this.handleKeyUpInternal(e); + return false; +}; + + +/** + * Internal handler for keyup events. + * @param {goog.events.Event} e The key event. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.handleKeyUpInternal = function(e) { + if ((goog.userAgent.IE || goog.userAgent.OPERA) && + e.keyCode == goog.events.KeyCodes.ENTER) { + this.ensureBlockIeOpera(goog.dom.TagName.DIV, true); + } +}; + + +/** + * Handles an enter keypress event on fields in Gecko. + * @param {goog.events.BrowserEvent} e The key event. + * @private + */ +goog.editor.plugins.EnterHandler.prototype.handleEnterGecko_ = function(e) { + // Retrieve whether the selection is collapsed before we delete it. + var range = this.getFieldObject().getRange(); + var wasCollapsed = !range || range.isCollapsed(); + var cursorPosition = this.deleteCursorSelection_(); + + var handled = this.getFieldObject().execCommand( + goog.editor.plugins.Blockquote.SPLIT_COMMAND, cursorPosition); + if (handled) { + // TODO(user): I think we probably don't need to stopPropagation here + e.preventDefault(); + e.stopPropagation(); + } + + this.releasePositionObject_(cursorPosition); + if (!handled) { + this.handleEnterAtCursorGeckoInternal(e, wasCollapsed, range); + } +}; + + +/** + * Handle an enter key press in WebKit. + * @param {goog.events.BrowserEvent} e The key press event. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.handleEnterWebkitInternal = + goog.nullFunction; + + +/** + * Handle an enter key press on collapsed selection. handleEnterGecko_ ensures + * the selection is collapsed by deleting its contents if it is not. The + * default implementation does nothing. + * @param {goog.events.BrowserEvent} e The key press event. + * @param {boolean} wasCollapsed Whether the selection was collapsed before + * the key press. If it was not, code before this function has already + * cleared the contents of the selection. + * @param {goog.dom.AbstractRange} range Object representing the selection. + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.handleEnterAtCursorGeckoInternal = + goog.nullFunction; + + +/** + * Names of all the nodes that we don't want to turn into block nodes in IE when + * the user hits enter. + * @type {Object} + * @private + */ +goog.editor.plugins.EnterHandler.DO_NOT_ENSURE_BLOCK_NODES_ = + goog.object.createSet( + goog.dom.TagName.LI, goog.dom.TagName.DIV, goog.dom.TagName.H1, + goog.dom.TagName.H2, goog.dom.TagName.H3, goog.dom.TagName.H4, + goog.dom.TagName.H5, goog.dom.TagName.H6); + + +/** + * Whether this is a node that contains a single BR tag and non-nbsp + * whitespace. + * @param {Node} node Node to check. + * @return {boolean} Whether this is an element that only contains a BR. + * @protected + */ +goog.editor.plugins.EnterHandler.isBrElem = function(node) { + return goog.editor.node.isEmpty(node) && + node.getElementsByTagName(goog.dom.TagName.BR).length == 1; +}; + + +/** + * Ensures all text in IE and Opera to be in the given tag in order to control + * Enter spacing. Call this when Enter is pressed if desired. + * + * We want to make sure the user is always inside of a block (or other nodes + * listed in goog.editor.plugins.EnterHandler.IGNORE_ENSURE_BLOCK_NODES_). We + * listen to keypress to force nodes that the user is leaving to turn into + * blocks, but we also need to listen to keyup to force nodes that the user is + * entering to turn into blocks. + * Example: html is: "

    foo[cursor]

    ", and the user hits enter. We + * don't want to format the h2, but we do want to format the P that is + * created on enter. The P node is not available until keyup. + * @param {goog.dom.TagName} tag The tag name to convert to. + * @param {boolean=} opt_keyUp Whether the function is being called on key up. + * When called on key up, the cursor is in the newly created node, so the + * semantics for when to change it to a block are different. Specifically, + * if the resulting node contains only a BR, it is converted to . + * @protected + */ +goog.editor.plugins.EnterHandler.prototype.ensureBlockIeOpera = function(tag, + opt_keyUp) { + var range = this.getFieldObject().getRange(); + var container = range.getContainer(); + var field = this.getFieldObject().getElement(); + + var paragraph; + while (container && container != field) { + // We don't need to ensure a block if we are already in the same block, or + // in another block level node that we don't want to change the format of + // (unless we're handling keyUp and that block node just contains a BR). + var nodeName = container.nodeName; + // Due to @bug 2455389, the call to isBrElem needs to be inlined in the if + // instead of done before and saved in a variable, so that it can be + // short-circuited and avoid a weird IE edge case. + if (nodeName == tag || + (goog.editor.plugins.EnterHandler. + DO_NOT_ENSURE_BLOCK_NODES_[nodeName] && !(opt_keyUp && + goog.editor.plugins.EnterHandler.isBrElem(container)))) { + // Opera can create a

    inside of a

    in some situations, + // such as when breaking out of a list that is contained in a
    . + if (goog.userAgent.OPERA && paragraph) { + if (nodeName == tag && + paragraph == container.lastChild && + goog.editor.node.isEmpty(paragraph)) { + goog.dom.insertSiblingAfter(paragraph, container); + goog.dom.Range.createFromNodeContents(paragraph).select(); + } + break; + } + return; + } + if (goog.userAgent.OPERA && opt_keyUp && nodeName == goog.dom.TagName.P && + nodeName != tag) { + paragraph = container; + } + + container = container.parentNode; + } + + + if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9)) { + // IE (before IE9) has a bug where if the cursor is directly before a block + // node (e.g., the content is "foo[cursor]
    bar
    "), + // the FormatBlock command actually formats the "bar" instead of the "foo". + // This is just wrong. To work-around this, we want to move the + // selection back one character, and then restore it to its prior position. + // NOTE: We use the following "range math" to detect this situation because + // using Closure ranges here triggers a bug in IE that causes a crash. + // parent2 != parent3 ensures moving the cursor forward one character + // crosses at least 1 element boundary, and therefore tests if the cursor is + // at such a boundary. The second check, parent3 != range.parentElement() + // weeds out some cases where the elements are siblings instead of cousins. + var needsHelp = false; + range = range.getBrowserRangeObject(); + var range2 = range.duplicate(); + range2.moveEnd('character', 1); + // In whitebox mode, when the cursor is at the end of the field, trying to + // move the end of the range will do nothing, and hence the range's text + // will be empty. In this case, the cursor clearly isn't sitting just + // before a block node, since it isn't before anything. + if (range2.text.length) { + var parent2 = range2.parentElement(); + + var range3 = range2.duplicate(); + range3.collapse(false); + var parent3 = range3.parentElement(); + + if ((needsHelp = parent2 != parent3 && + parent3 != range.parentElement())) { + range.move('character', -1); + range.select(); + } + } + } + + this.getFieldObject().getEditableDomHelper().getDocument().execCommand( + 'FormatBlock', false, '<' + tag + '>'); + + if (needsHelp) { + range.move('character', 1); + range.select(); + } +}; + + +/** + * Deletes the content at the current cursor position. + * @return {!Node|!Object} Something representing the current cursor position. + * See deleteCursorSelectionIE_ and deleteCursorSelectionW3C_ for details. + * Should be passed to releasePositionObject_ when no longer in use. + * @private + */ +goog.editor.plugins.EnterHandler.prototype.deleteCursorSelection_ = function() { + return goog.editor.BrowserFeature.HAS_W3C_RANGES ? + this.deleteCursorSelectionW3C_() : this.deleteCursorSelectionIE_(); +}; + + +/** + * Releases the object returned by deleteCursorSelection_. + * @param {Node|Object} position The object returned by deleteCursorSelection_. + * @private + */ +goog.editor.plugins.EnterHandler.prototype.releasePositionObject_ = + function(position) { + if (!goog.editor.BrowserFeature.HAS_W3C_RANGES) { + (/** @type {Node} */ (position)).removeNode(true); + } +}; + + +/** + * Delete the selection at the current cursor position, then returns a temporary + * node at the current position. + * @return {!Node} A temporary node marking the current cursor position. This + * node should eventually be removed from the DOM. + * @private + */ +goog.editor.plugins.EnterHandler.prototype.deleteCursorSelectionIE_ = + function() { + var doc = this.getFieldDomHelper().getDocument(); + var range = doc.selection.createRange(); + + var id = goog.string.createUniqueString(); + range.pasteHTML(''); + var splitNode = doc.getElementById(id); + splitNode.id = ''; + return splitNode; +}; + + +/** + * Delete the selection at the current cursor position, then returns the node + * at the current position. + * @return {!goog.editor.range.Point} The current cursor position. Note that + * unlike simulateEnterIE_, this should not be removed from the DOM. + * @private + */ +goog.editor.plugins.EnterHandler.prototype.deleteCursorSelectionW3C_ = + function() { + var range = this.getFieldObject().getRange(); + + // Delete the current selection if it's is non-collapsed. + // Although this is redundant in FF, it's necessary for Safari + if (!range.isCollapsed()) { + var shouldDelete = true; + // Opera selects the
    in an empty block if there is no text node + // preceding it. To preserve inline formatting when pressing [enter] inside + // an empty block, don't delete the selection if it only selects a
    at + // the end of the block. + // TODO(user): Move this into goog.dom.Range. It should detect this state + // when creating a range from the window selection and fix it in the created + // range. + if (goog.userAgent.OPERA) { + var startNode = range.getStartNode(); + var startOffset = range.getStartOffset(); + if (startNode == range.getEndNode() && + // This weeds out cases where startNode is a text node. + startNode.lastChild && + startNode.lastChild.tagName == goog.dom.TagName.BR && + // If this check is true, then endOffset is implied to be + // startOffset + 1, because the selection is not collapsed and + // it starts and ends within the same element. + startOffset == startNode.childNodes.length - 1) { + shouldDelete = false; + } + } + if (shouldDelete) { + goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + } + } + + return goog.editor.range.getDeepEndPoint(range, true); +}; + + +/** + * Deletes the contents of the selection from the DOM. + * @param {goog.dom.AbstractRange} range The range to remove contents from. + * @return {goog.dom.AbstractRange} The resulting range. Used for testing. + * @private + */ +goog.editor.plugins.EnterHandler.deleteW3cRange_ = function(range) { + if (range && !range.isCollapsed()) { + var reselect = true; + var baseNode = range.getContainerElement(); + var nodeOffset = new goog.dom.NodeOffset(range.getStartNode(), baseNode); + var rangeOffset = range.getStartOffset(); + + // Whether the selection crosses no container boundaries. + var isInOneContainer = + goog.editor.plugins.EnterHandler.isInOneContainerW3c_(range); + + // Whether the selection ends in a container it doesn't fully select. + var isPartialEnd = !isInOneContainer && + goog.editor.plugins.EnterHandler.isPartialEndW3c_(range); + + // Remove The range contents, and ensure the correct content stays selected. + range.removeContents(); + var node = nodeOffset.findTargetNode(baseNode); + if (node) { + range = goog.dom.Range.createCaret(node, rangeOffset); + } else { + // This occurs when the node that would have been referenced has now been + // deleted and there are no other nodes in the baseNode. Thus need to + // set the caret to the end of the base node. + range = + goog.dom.Range.createCaret(baseNode, baseNode.childNodes.length); + reselect = false; + } + range.select(); + + // If we just deleted everything from the container, add an nbsp + // to the container, and leave the cursor inside of it + if (isInOneContainer) { + var container = goog.editor.style.getContainer(range.getStartNode()); + if (goog.editor.node.isEmpty(container, true)) { + var html = ' '; + if (goog.userAgent.OPERA && + container.tagName == goog.dom.TagName.LI) { + // Don't break Opera's native break-out-of-lists behavior. + html = '
    '; + } + goog.editor.node.replaceInnerHtml(container, html); + goog.editor.range.selectNodeStart(container.firstChild); + reselect = false; + } + } + + if (isPartialEnd) { + /* + This code handles the following, where | is the cursor: +
    a|b
    c|d
    + After removeContents, the remaining HTML is +
    a
    d
    + which means the line break between the two divs remains. This block + moves children of the second div in to the first div to get the correct + result: +
    ad
    + + TODO(robbyw): Should we wrap the second div's contents in a span if they + have inline style? + */ + var rangeStart = goog.editor.style.getContainer(range.getStartNode()); + var redundantContainer = goog.editor.node.getNextSibling(rangeStart); + if (rangeStart && redundantContainer) { + goog.dom.append(rangeStart, redundantContainer.childNodes); + goog.dom.removeNode(redundantContainer); + } + } + + if (reselect) { + // The contents of the original range are gone, so restore the cursor + // position at the start of where the range once was. + range = goog.dom.Range.createCaret(nodeOffset.findTargetNode(baseNode), + rangeOffset); + range.select(); + } + } + + return range; +}; + + +/** + * Checks whether the whole range is in a single block-level element. + * @param {goog.dom.AbstractRange} range The range to check. + * @return {boolean} Whether the whole range is in a single block-level element. + * @private + */ +goog.editor.plugins.EnterHandler.isInOneContainerW3c_ = function(range) { + // Find the block element containing the start of the selection. + var startContainer = range.getStartNode(); + if (goog.editor.style.isContainer(startContainer)) { + startContainer = startContainer.childNodes[range.getStartOffset()] || + startContainer; + } + startContainer = goog.editor.style.getContainer(startContainer); + + // Find the block element containing the end of the selection. + var endContainer = range.getEndNode(); + if (goog.editor.style.isContainer(endContainer)) { + endContainer = endContainer.childNodes[range.getEndOffset()] || + endContainer; + } + endContainer = goog.editor.style.getContainer(endContainer); + + // Compare the two. + return startContainer == endContainer; +}; + + +/** + * Checks whether the end of the range is not at the end of a block-level + * element. + * @param {goog.dom.AbstractRange} range The range to check. + * @return {boolean} Whether the end of the range is not at the end of a + * block-level element. + * @private + */ +goog.editor.plugins.EnterHandler.isPartialEndW3c_ = function(range) { + var endContainer = range.getEndNode(); + var endOffset = range.getEndOffset(); + var node = endContainer; + if (goog.editor.style.isContainer(node)) { + var child = node.childNodes[endOffset]; + // Child is null when end offset is >= length, which indicates the entire + // container is selected. Otherwise, we also know the entire container + // is selected if the selection ends at a new container. + if (!child || + child.nodeType == goog.dom.NodeType.ELEMENT && + goog.editor.style.isContainer(child)) { + return false; + } + } + + var container = goog.editor.style.getContainer(node); + while (container != node) { + if (goog.editor.node.getNextSibling(node)) { + return true; + } + node = node.parentNode; + } + + return endOffset != goog.editor.node.getLength(endContainer); +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.html new file mode 100644 index 0000000000..5a25a19d9d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.html @@ -0,0 +1,68 @@ + + + + + + + + Closure Unit Tests - goog.editor.plugins.EnterHandler + + + + + + + +

    +

    + This is used to test static utility functions. +
    +
    +
    +
    + This is an + + selection + + unsetup blockquote +
    +
    +

    +

    +
    + This is a + + selection + + setup blockquote +
    +
    +

    +

    +

    +
    +

    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.js new file mode 100644 index 0000000000..37b8967973 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/enterhandler_test.js @@ -0,0 +1,741 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.EnterHandlerTest'); +goog.setTestOnly('goog.editor.plugins.EnterHandlerTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.plugins.Blockquote'); +goog.require('goog.editor.plugins.EnterHandler'); +goog.require('goog.editor.range'); +goog.require('goog.events'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.testing.ExpectedFailures'); +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var savedHtml; + +var field1; +var field2; +var firedDelayedChange; +var firedBeforeChange; +var clock; +var container; +var EXPECTEDFAILURES; + +function setUpPage() { + container = goog.dom.getElement('container'); +} + +function setUp() { + EXPECTEDFAILURES = new goog.testing.ExpectedFailures(); + savedHtml = goog.dom.getElement('root').innerHTML; + clock = new goog.testing.MockClock(true); +} + +function setUpFields(classnameRequiredToSplitBlockquote) { + field1 = makeField('field1', classnameRequiredToSplitBlockquote); + field2 = makeField('field2', classnameRequiredToSplitBlockquote); + + field1.makeEditable(); + field2.makeEditable(); +} + +function tearDown() { + clock.dispose(); + + EXPECTEDFAILURES.handleTearDown(); + + goog.dom.getElement('root').innerHTML = savedHtml; +} + +function testEnterInNonSetupBlockquote() { + setUpFields(true); + resetChangeFlags(); + var prevented = !selectNodeAndHitEnter(field1, 'field1cursor'); + waitForChangeEvents(); + assertChangeFlags(); + + // make sure there's just one blockquote, and that the text has been deleted. + var elem = field1.getElement(); + var dom = field1.getEditableDomHelper(); + EXPECTEDFAILURES.expectFailureFor(goog.userAgent.OPERA, + 'The blockquote is overwritten with DIV due to CORE-22104 -- Opera ' + + 'overwrites the BLOCKQUOTE ancestor with DIV when doing FormatBlock ' + + 'for DIV'); + try { + assertEquals('Blockquote should not be split', + 1, dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem).length); + } catch (e) { + EXPECTEDFAILURES.handleException(e); + } + assert('Selection should be deleted', + -1 == elem.innerHTML.indexOf('selection')); + + assertEquals('The event should have been prevented only on webkit', + prevented, goog.userAgent.WEBKIT); +} + +function testEnterInSetupBlockquote() { + setUpFields(true); + resetChangeFlags(); + var prevented = !selectNodeAndHitEnter(field2, 'field2cursor'); + waitForChangeEvents(); + assertChangeFlags(); + + // make sure there are two blockquotes, and a DIV with nbsp in the middle. + var elem = field2.getElement(); + var dom = field2.getEditableDomHelper(); + assertEquals('Blockquote should be split', 2, + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem).length); + assert('Selection should be deleted', + -1 == elem.innerHTML.indexOf('selection')); + + assert('should have div with  ', + -1 != elem.innerHTML.indexOf('>' + getNbsp() + '<')); + assert('event should have been prevented', prevented); +} + +function testEnterInNonSetupBlockquoteWhenClassnameIsNotRequired() { + setUpFields(false); + + resetChangeFlags(); + var prevented = !selectNodeAndHitEnter(field1, 'field1cursor'); + waitForChangeEvents(); + assertChangeFlags(); + + // make sure there are two blockquotes, and a DIV with nbsp in the middle. + var elem = field1.getElement(); + var dom = field1.getEditableDomHelper(); + assertEquals('Blockquote should be split', 2, + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem).length); + assert('Selection should be deleted', + -1 == elem.innerHTML.indexOf('selection')); + + assert('should have div with  ', + -1 != elem.innerHTML.indexOf('>' + getNbsp() + '<')); + assert('event should have been prevented', prevented); +} + +function testEnterInBlockquoteCreatesDivInBrMode() { + setUpFields(true); + selectNodeAndHitEnter(field2, 'field2cursor'); + var elem = field2.getElement(); + var dom = field2.getEditableDomHelper(); + + var firstBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[0]; + var div = dom.getNextElementSibling(firstBlockquote); + assertEquals('Element after blockquote should be a div', 'DIV', div.tagName); + assertEquals('Element after div should be second blockquote', + 'BLOCKQUOTE', dom.getNextElementSibling(div).tagName); +} + + +/** + * Tests that breaking after a BR doesn't result in unnecessary newlines. + * @bug 1471047 + */ +function testEnterInBlockquoteRemovesUnnecessaryBrWithCursorAfterBr() { + setUpFields(true); + + // Assume the following HTML snippet:- + //
    one
    |two
    + // + // After enter on the cursor position without the fix, the resulting HTML + // after the blockquote split was:- + //
    one
    + //
     
    + //

    two
    + // + // This creates the impression on an unnecessary newline. The resulting HTML + // after the fix is:- + // + //
    one
    + //
     
    + //
    two
    + field1.setHtml(false, + '
    one
    ' + + 'two
    '); + var dom = field1.getEditableDomHelper(); + goog.dom.Range.createCaret(dom.getElement('quote'), 2).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + var elem = field1.getElement(); + var secondBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[1]; + assertHTMLEquals('two
    ', secondBlockquote.innerHTML); + + // Verifies that a blockquote split doesn't happen if it doesn't need to. + field1.setHtml(false, + '
    one
    '); + selectNodeAndHitEnter(field1, 'brcursor'); + assertEquals( + 1, dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem).length); +} + + +/** + * Tests that breaking in a text node before a BR doesn't result in unnecessary + * newlines. + * @bug 1471047 + */ +function testEnterInBlockquoteRemovesUnnecessaryBrWithCursorBeforeBr() { + setUpFields(true); + + // Assume the following HTML snippet:- + //
    one|
    two
    + // + // After enter on the cursor position, the resulting HTML should be. + //
    one
    + //
     
    + //
    two
    + field1.setHtml(false, + '
    one
    ' + + 'two
    '); + var dom = field1.getEditableDomHelper(); + var cursor = dom.getElement('quote').firstChild; + goog.dom.Range.createCaret(cursor, 3).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + var elem = field1.getElement(); + var secondBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[1]; + assertHTMLEquals('two
    ', secondBlockquote.innerHTML); + + // Ensures that standard text node split works as expected with the new + // change. + field1.setHtml(false, + '
    onetwo
    '); + cursor = dom.getElement('quote').firstChild; + goog.dom.Range.createCaret(cursor, 3).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + secondBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[1]; + assertHTMLEquals('two
    ', secondBlockquote.innerHTML); +} + + +/** + * Tests that pressing enter in a blockquote doesn't create unnecessary + * DOM subtrees. + * + * @bug 1991539 + * @bug 1991392 + */ +function testEnterInBlockquoteRemovesExtraNodes() { + setUpFields(true); + + // Let's assume we have the following DOM structure and the + // cursor is placed after the first numbered list item "one". + // + //
    + //
    a
    1. one|
    + //
    two
    + //
    + // + // After pressing enter, we have the following structure. + // + //
    + //
    a
    1. one|
    + //
    + //
     
    + //
    + //
    + //
    two
    + //
    + // + // This appears to the user as an empty list. After the fix, the HTML + // will be + // + //
    + //
    a
    1. one|
    + //
    + //
     
    + //
    + //
    two
    + //
    + // + field1.setHtml(false, + '
    ' + + '
    a
    1. one
    ' + + '
    b
    ' + + '
    '); + var dom = field1.getEditableDomHelper(); + goog.dom.Range.createCaret(dom.getElement('cursor').firstChild, 3).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + var elem = field1.getElement(); + var secondBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[1]; + assertHTMLEquals('
    b
    ', secondBlockquote.innerHTML); + + // Ensure that we remove only unnecessary subtrees. + field1.setHtml(false, + '
    ' + + '
    a
    one
    two
    ' + + '
    c
    ' + + '
    '); + goog.dom.Range.createCaret(dom.getElement('cursor').firstChild, 3).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + secondBlockquote = + dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem)[1]; + var expectedHTML = '
    two
    ' + + '
    c
    '; + assertHTMLEquals(expectedHTML, secondBlockquote.innerHTML); + + // Place the cursor in the middle of a line. + field1.setHtml(false, + '
    ' + + '
    one
    two
    ' + + '
    '); + goog.dom.Range.createCaret( + dom.getElement('quote').firstChild.firstChild, 1).select(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + var blockquotes = dom.getElementsByTagNameAndClass('BLOCKQUOTE', null, elem); + assertEquals(2, blockquotes.length); + assertHTMLEquals('
    o
    ', blockquotes[0].innerHTML); + assertHTMLEquals('
    ne
    two
    ', blockquotes[1].innerHTML); +} + +function testEnterInList() { + setUpFields(true); + + // in a list should *never* be handled by custom code. Lists are + // just way too complicated to get right. + field1.setHtml(false, + '
    1. hi!
    '); + if (goog.userAgent.OPERA) { + // Opera doesn't actually place the selection in the empty span + // unless we add a text node first. + var dom = field1.getEditableDomHelper(); + dom.getElement('field1cursor').appendChild(dom.createTextNode('')); + } + var prevented = !selectNodeAndHitEnter(field1, 'field1cursor'); + assertFalse(' in a list should not be prevented', prevented); +} + +function testEnterAtEndOfBlockInWebkit() { + setUpFields(true); + + if (goog.userAgent.WEBKIT) { + field1.setHtml(false, + '
    hi!
    '); + + var cursor = field1.getEditableDomHelper().getElement('field1cursor'); + goog.editor.range.placeCursorNextTo(cursor, false); + goog.dom.removeNode(cursor); + + var prevented = !goog.testing.events.fireKeySequence( + field1.getElement(), goog.events.KeyCodes.ENTER); + waitForChangeEvents(); + assertChangeFlags(); + assert('event should have been prevented', prevented); + + // Make sure that the block now has two brs. + var elem = field1.getElement(); + assertEquals('should have inserted two br tags: ' + elem.innerHTML, + 2, goog.dom.getElementsByTagNameAndClass('BR', null, elem).length); + } +} + + +/** + * Tests that deleting a BR that comes right before a block element works. + * @bug 1471096 + * @bug 2056376 + */ +function testDeleteBrBeforeBlock() { + setUpFields(true); + + // This test only works on Gecko, because it's testing for manual deletion of + // BR tags, which is done only for Gecko. For other browsers we fall through + // and let the browser do the delete, which can only be tested with a robot + // test (see javascript/apps/editor/tests/delete_br_robot.html). + if (goog.userAgent.GECKO) { + field1.setHtml(false, 'one

    two
    '); + var helper = new goog.testing.editor.TestHelper(field1.getElement()); + helper.select(field1.getElement(), 2); // Between the two BR's. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted exactly one
    ', + 'one
    two
    ', + field1.getElement().innerHTML); + + // We test the case where the BR has a previous sibling which is not + // a block level element. + field1.setHtml(false, 'one
    • two
    '); + helper.select(field1.getElement(), 1); // Between one and BR. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted the
    ', + 'one
    • two
    ', + field1.getElement().innerHTML); + // Verify that the cursor is placed at the end of the text node "one". + var range = field1.getRange(); + var focusNode = range.getFocusNode(); + assertTrue('The selected range should be collapsed', range.isCollapsed()); + assertTrue('The focus node should be the text node "one"', + focusNode.nodeType == goog.dom.NodeType.TEXT && + focusNode.data == 'one'); + assertEquals('The focus offset should be at the end of the text node "one"', + focusNode.length, + range.getFocusOffset()); + assertTrue('The next sibling of the focus node should be the UL', + focusNode.nextSibling && + focusNode.nextSibling.tagName == goog.dom.TagName.UL); + + // We test the case where the previous sibling of the BR is a block + // level element. + field1.setHtml(false, '
    foo

    bar
    '); + helper.select(field1.getElement(), 1); // Before the BR. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted the
    ', + '
    foo
    bar
    ', + field1.getElement().innerHTML); + range = field1.getRange(); + assertEquals('The selected range should be contained within the ', + goog.dom.TagName.SPAN, + range.getContainerElement().tagName); + assertTrue('The selected range should be collapsed', range.isCollapsed()); + // Verify that the cursor is placed inside the span at the beginning of bar. + focusNode = range.getFocusNode(); + assertTrue('The focus node should be the text node "bar"', + focusNode.nodeType == goog.dom.NodeType.TEXT && + focusNode.data == 'bar'); + assertEquals('The focus offset should be at the beginning ' + + 'of the text node "bar"', + 0, + range.getFocusOffset()); + + // We test the case where the BR does not have a previous sibling. + field1.setHtml(false, '
    • one
    '); + helper.select(field1.getElement(), 0); // Before the BR. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted the
    ', + '
    • one
    ', + field1.getElement().innerHTML); + range = field1.getRange(); + // Verify that the cursor is placed inside the LI at the text node "one". + assertEquals('The selected range should be contained within the
  • ', + goog.dom.TagName.LI, + range.getContainerElement().tagName); + assertTrue('The selected range should be collapsed', range.isCollapsed()); + focusNode = range.getFocusNode(); + assertTrue('The focus node should be the text node "one"', + (focusNode.nodeType == goog.dom.NodeType.TEXT && + focusNode.data == 'one')); + assertEquals('The focus offset should be at the beginning of ' + + 'the text node "one"', + 0, + range.getFocusOffset()); + + // Testing deleting a BR followed by a block level element and preceded + // by a BR. + field1.setHtml(false, '

    • one
    '); + helper.select(field1.getElement(), 1); // Between the BR's. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted the
    ', + '
    • one
    ', + field1.getElement().innerHTML); + // Verify that the cursor is placed inside the LI at the text node "one". + range = field1.getRange(); + assertEquals('The selected range should be contained within the
  • ', + goog.dom.TagName.LI, + range.getContainerElement().tagName); + assertTrue('The selected range should be collapsed', range.isCollapsed()); + focusNode = range.getFocusNode(); + assertTrue('The focus node should be the text node "one"', + (focusNode.nodeType == goog.dom.NodeType.TEXT && + focusNode.data == 'one')); + assertEquals('The focus offset should be at the beginning of ' + + 'the text node "one"', + 0, + range.getFocusOffset()); + } // End if GECKO +} + + +/** + * Tests that deleting a BR before a blockquote doesn't remove quoted text. + * @bug 1471075 + */ +function testDeleteBeforeBlockquote() { + setUpFields(true); + + if (goog.userAgent.GECKO) { + field1.setHtml(false, + '


    foo
    '); + var helper = new goog.testing.editor.TestHelper(field1.getElement()); + helper.select(field1.getElement(), 0); // Before the first BR. + // Fire three deletes in quick succession. + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted all the
    \'s and the blockquote ' + + 'isn\'t affected', + '
    foo
    ', + field1.getElement().innerHTML); + var range = field1.getRange(); + assertEquals('The selected range should be contained within the ' + + '
    ', + goog.dom.TagName.BLOCKQUOTE, + range.getContainerElement().tagName); + assertTrue('The selected range should be collapsed', range.isCollapsed()); + var focusNode = range.getFocusNode(); + assertTrue('The focus node should be the text node "foo"', + (focusNode.nodeType == goog.dom.NodeType.TEXT && + focusNode.data == 'foo')); + assertEquals('The focus offset should be at the ' + + 'beginning of the text node "foo"', + 0, + range.getFocusOffset()); + } +} + + +/** + * Tests that deleting a BR is working normally (that the workaround for the + * bug is not causing double deletes). + * @bug 1471096 + */ +function testDeleteBrNormal() { + setUpFields(true); + + // This test only works on Gecko, because it's testing for manual deletion of + // BR tags, which is done only for Gecko. For other browsers we fall through + // and let the browser do the delete, which can only be tested with a robot + // test (see javascript/apps/editor/tests/delete_br_robot.html). + if (goog.userAgent.GECKO) { + + field1.setHtml(false, 'one


    two'); + var helper = new goog.testing.editor.TestHelper(field1.getElement()); + helper.select(field1.getElement(), 2); // Between the first and second BR's. + field1.getElement().focus(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.DELETE); + assertEquals('Should have deleted exactly one
    ', + 'one

    two', + field1.getElement().innerHTML); + + } // End if GECKO +} + + +/** + * Tests that deleteCursorSelectionW3C_ correctly recognizes visually + * collapsed selections in Opera even if they contain a
    . + * See the deleteCursorSelectionW3C_ comment in enterhandler.js. + */ +function testCollapsedSelectionKeepsBrOpera() { + setUpFields(true); + + if (goog.userAgent.OPERA) { + field1.setHtml(false, '

    '); + field1.focus(); + goog.testing.events.fireKeySequence(field1.getElement(), + goog.events.KeyCodes.ENTER); + assertNotNull('The
    must not have been deleted', + goog.dom.getElement('pleasedontdeleteme')); + } +} + + +/** + * Selects the node at the given id, and simulates an ENTER keypress. + * @param {goog.editor.Field} field The field with the node. + * @param {string} id A DOM id. + * @return {boolean} Whether preventDefault was called on the event. + */ +function selectNodeAndHitEnter(field, id) { + var dom = field.getEditableDomHelper(); + var cursor = dom.getElement(id); + goog.dom.Range.createFromNodeContents(cursor).select(); + return goog.testing.events.fireKeySequence( + cursor, goog.events.KeyCodes.ENTER); +} + + +/** + * Creates a field with only the enter handler plugged in, for testing. + * @param {string} id A DOM id. + * @return {goog.editor.Field} A field. + */ +function makeField(id, classnameRequiredToSplitBlockquote) { + var field = new goog.editor.Field(id); + field.registerPlugin(new goog.editor.plugins.EnterHandler()); + field.registerPlugin(new goog.editor.plugins.Blockquote( + classnameRequiredToSplitBlockquote)); + + goog.events.listen(field, goog.editor.Field.EventType.BEFORECHANGE, + function() { + // set the global flag that beforechange was fired. + firedBeforeChange = true; + }); + goog.events.listen(field, goog.editor.Field.EventType.DELAYEDCHANGE, + function() { + // set the global flag that delayed change was fired. + firedDelayedChange = true; + }); + + return field; +} + + +/** + * Reset all the global flags related to change events. + */ +function resetChangeFlags() { + waitForChangeEvents(); + firedBeforeChange = firedDelayedChange = false; +} + + +/** + * Asserts that both change flags were fired since the last reset. + */ +function assertChangeFlags() { + assert('Beforechange should have fired', firedBeforeChange); + assert('Delayedchange should have fired', firedDelayedChange); +} + + +/** + * Wait for delayedchange to propagate. + */ +function waitForChangeEvents() { + clock.tick(goog.editor.Field.DELAYED_CHANGE_FREQUENCY + + goog.editor.Field.CHANGE_FREQUENCY); +} + +function getNbsp() { + // On WebKit (pre-528) and Opera,   shows up as its unicode character in + // innerHTML under some circumstances. + return (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528')) || + goog.userAgent.OPERA ? '\u00a0' : ' '; +} + + +function testPrepareContent() { + setUpFields(true); + assertPreparedContents('hi', 'hi'); + assertPreparedContents( + goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? '
    ' : '', ' '); +} + + +/** + * Assert that the prepared contents matches the expected. + */ +function assertPreparedContents(expected, original) { + assertEquals(expected, + field1.reduceOp_( + goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, original)); +} + +// UTILITY FUNCTION TESTS. + +function testDeleteW3CSimple() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = '
    abcd
    '; + var range = goog.dom.Range.createFromNodes(container.firstChild.firstChild, + 1, container.firstChild.firstChild, 3); + range.select(); + goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + + goog.testing.dom.assertHtmlContentsMatch('
    ad
    ', container); + } +} + +function testDeleteW3CAll() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = '
    abcd
    '; + var range = goog.dom.Range.createFromNodes(container.firstChild.firstChild, + 0, container.firstChild.firstChild, 4); + range.select(); + goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + + goog.testing.dom.assertHtmlContentsMatch('
     
    ', container); + } +} + +function testDeleteW3CPartialEnd() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = '
    ab
    cd
    '; + var range = goog.dom.Range.createFromNodes(container.firstChild.firstChild, + 1, container.lastChild.firstChild, 1); + range.select(); + goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + + goog.testing.dom.assertHtmlContentsMatch('
    ad
    ', container); + } +} + +function testDeleteW3CNonPartialEnd() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = '
    ab
    cd
    '; + var range = goog.dom.Range.createFromNodes(container.firstChild.firstChild, + 1, container.lastChild.firstChild, 2); + range.select(); + goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + + goog.testing.dom.assertHtmlContentsMatch('
    a
    ', container); + } +} + +function testIsInOneContainer() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = '

    '; + var div = container.firstChild; + var range = goog.dom.Range.createFromNodes(div, 0, div, 1); + range.select(); + assertTrue('Selection must be recognized as being in one container', + goog.editor.plugins.EnterHandler.isInOneContainerW3c_(range)); + } +} + +function testDeletingEndNodesWithNoNewLine() { + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + container.innerHTML = + 'a
    b

    c
    d
    '; + var range = goog.dom.Range.createFromNodes( + container.childNodes[2], 0, container.childNodes[4].childNodes[0], 1); + range.select(); + var newRange = goog.editor.plugins.EnterHandler.deleteW3cRange_(range); + goog.testing.dom.assertHtmlContentsMatch('a
    b
    ', container); + assertTrue(newRange.isCollapsed()); + assertEquals(container, newRange.getStartNode()); + assertEquals(2, newRange.getStartOffset()); + } +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong.js new file mode 100644 index 0000000000..721ced37e5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong.js @@ -0,0 +1,327 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A plugin to enable the First Strong Bidi algorithm. The First + * Strong algorithm as a heuristic used to automatically set paragraph direction + * depending on its content. + * + * In the documentation below, a 'paragraph' is the local element which we + * evaluate as a whole for purposes of determining directionality. It may be a + * block-level element (e.g. <div>) or a whole list (e.g. <ul>). + * + * This implementation is based on, but is not identical to, the original + * First Strong algorithm defined in Unicode + * @see http://www.unicode.org/reports/tr9/ + * The central difference from the original First Strong algorithm is that this + * implementation decides the paragraph direction based on the first strong + * character that is typed into the paragraph, regardless of its + * location in the paragraph, as opposed to the original algorithm where it is + * the first character in the paragraph by location, regardless of + * whether other strong characters already appear in the paragraph, further its + * start. + * + * Please note that this plugin does not perform the direction change + * itself. Rather, it fires editor commands upon the key up event when a + * direction change needs to be performed; {@code goog.editor.Command.DIR_RTL} + * or {@code goog.editor.Command.DIR_RTL}. + * + */ + +goog.provide('goog.editor.plugins.FirstStrong'); + +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagIterator'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.node'); +goog.require('goog.editor.range'); +goog.require('goog.i18n.bidi'); +goog.require('goog.i18n.uChar'); +goog.require('goog.iter'); +goog.require('goog.userAgent'); + + + +/** + * First Strong plugin. + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.FirstStrong = function() { + goog.editor.plugins.FirstStrong.base(this, 'constructor'); + + /** + * Indicates whether or not the cursor is in a paragraph we have not yet + * finished evaluating for directionality. This is set to true whenever the + * cursor is moved, and set to false after seeing a strong character in the + * paragraph the cursor is currently in. + * + * @type {boolean} + * @private + */ + this.isNewBlock_ = true; + + /** + * Indicates whether or not the current paragraph the cursor is in should be + * set to Right-To-Left directionality. + * + * @type {boolean} + * @private + */ + this.switchToRtl_ = false; + + /** + * Indicates whether or not the current paragraph the cursor is in should be + * set to Left-To-Right directionality. + * + * @type {boolean} + * @private + */ + this.switchToLtr_ = false; +}; +goog.inherits(goog.editor.plugins.FirstStrong, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.FirstStrong.prototype.getTrogClassId = function() { + return 'FirstStrong'; +}; + + +/** @override */ +goog.editor.plugins.FirstStrong.prototype.queryCommandValue = + function(command) { + return false; +}; + + +/** @override */ +goog.editor.plugins.FirstStrong.prototype.handleSelectionChange = + function(e, node) { + this.isNewBlock_ = true; + return false; +}; + + +/** + * The name of the attribute which records the input text. + * + * @type {string} + * @const + */ +goog.editor.plugins.FirstStrong.INPUT_ATTRIBUTE = 'fs-input'; + + +/** @override */ +goog.editor.plugins.FirstStrong.prototype.handleKeyPress = function(e) { + if (!this.isNewBlock_) { + return false; // We've already determined this paragraph's direction. + } + // Ignore non-character key press events. + if (e.ctrlKey || e.metaKey) { + return false; + } + var newInput = goog.i18n.uChar.fromCharCode(e.charCode); + + // IME's may return 0 for the charCode, which is a legitimate, non-Strong + // charCode, or they may return an illegal charCode (for which newInput will + // be false). + if (!newInput || !e.charCode) { + var browserEvent = e.getBrowserEvent(); + if (browserEvent) { + if (goog.userAgent.IE && browserEvent['getAttribute']) { + newInput = browserEvent['getAttribute']( + goog.editor.plugins.FirstStrong.INPUT_ATTRIBUTE); + } else { + newInput = browserEvent[ + goog.editor.plugins.FirstStrong.INPUT_ATTRIBUTE]; + } + } + } + + if (!newInput) { + return false; // Unrecognized key. + } + + var isLtr = goog.i18n.bidi.isLtrChar(newInput); + var isRtl = !isLtr && goog.i18n.bidi.isRtlChar(newInput); + if (!isLtr && !isRtl) { + return false; // This character cannot change anything (it is not Strong). + } + // This character is Strongly LTR or Strongly RTL. We might switch direction + // on it now, but in any case we do not need to check any more characters in + // this paragraph after it. + this.isNewBlock_ = false; + + // Are there no Strong characters already in the paragraph? + if (this.isNeutralBlock_()) { + this.switchToRtl_ = isRtl; + this.switchToLtr_ = isLtr; + } + return false; +}; + + +/** + * Calls the flip directionality commands. This is done here so things go into + * the redo-undo stack at the expected order; fist enter the input, then flip + * directionality. + * @override + */ +goog.editor.plugins.FirstStrong.prototype.handleKeyUp = function(e) { + if (this.switchToRtl_) { + var field = this.getFieldObject(); + field.dispatchChange(true); + field.execCommand(goog.editor.Command.DIR_RTL); + this.switchToRtl_ = false; + } else if (this.switchToLtr_) { + var field = this.getFieldObject(); + field.dispatchChange(true); + field.execCommand(goog.editor.Command.DIR_LTR); + this.switchToLtr_ = false; + } + return false; +}; + + +/** + * @return {Element} The lowest Block element ancestor of the node where the + * next character will be placed. + * @private + */ +goog.editor.plugins.FirstStrong.prototype.getBlockAncestor_ = function() { + var start = this.getFieldObject().getRange().getStartNode(); + // Go up in the DOM until we reach a Block element. + while (!goog.editor.plugins.FirstStrong.isBlock_(start)) { + start = start.parentNode; + } + return /** @type {Element} */ (start); +}; + + +/** + * @return {boolean} Whether the paragraph where the next character will be + * entered contains only non-Strong characters. + * @private + */ +goog.editor.plugins.FirstStrong.prototype.isNeutralBlock_ = function() { + var root = this.getBlockAncestor_(); + // The exact node with the cursor location. Simply calling getStartNode() on + // the range only returns the containing block node. + var cursor = goog.editor.range.getDeepEndPoint( + this.getFieldObject().getRange(), false).node; + + // In FireFox the BR tag also represents a change in paragraph if not inside a + // list. So we need special handling to only look at the sub-block between + // BR elements. + var blockFunction = (goog.userAgent.GECKO && + !this.isList_(root)) ? + goog.editor.plugins.FirstStrong.isGeckoBlock_ : + goog.editor.plugins.FirstStrong.isBlock_; + var paragraph = this.getTextAround_(root, cursor, + blockFunction); + // Not using {@code goog.i18n.bidi.isNeutralText} as it contains additional, + // unwanted checks to the content. + return !goog.i18n.bidi.hasAnyLtr(paragraph) && + !goog.i18n.bidi.hasAnyRtl(paragraph); +}; + + +/** + * Checks if an element is a list element ('UL' or 'OL'). + * + * @param {Element} element The element to test. + * @return {boolean} Whether the element is a list element ('UL' or 'OL'). + * @private + */ +goog.editor.plugins.FirstStrong.prototype.isList_ = function(element) { + if (!element) { + return false; + } + var tagName = element.tagName; + return tagName == goog.dom.TagName.UL || tagName == goog.dom.TagName.OL; +}; + + +/** + * Returns the text within the local paragraph around the cursor. + * Notice that for GECKO a BR represents a pargraph change despite not being a + * block element. + * + * @param {Element} root The first block element ancestor of the node the cursor + * is in. + * @param {Node} cursorLocation Node where the cursor currently is, marking the + * paragraph whose text we will return. + * @param {function(Node): boolean} isParagraphBoundary The function to + * determine if a node represents the start or end of the paragraph. + * @return {string} the text in the paragraph around the cursor location. + * @private + */ +goog.editor.plugins.FirstStrong.prototype.getTextAround_ = function(root, + cursorLocation, isParagraphBoundary) { + // The buffer where we're collecting the text. + var buffer = []; + // Have we reached the cursor yet, or are we still before it? + var pastCursorLocation = false; + + if (root && cursorLocation) { + goog.iter.some(new goog.dom.TagIterator(root), function(node) { + if (node == cursorLocation) { + pastCursorLocation = true; + } else if (isParagraphBoundary(node)) { + if (pastCursorLocation) { + // This is the end of the paragraph containing the cursor. We're done. + return true; + } else { + // All we collected so far does not count; it was in a previous + // paragraph that did not contain the cursor. + buffer = []; + } + } + if (node.nodeType == goog.dom.NodeType.TEXT) { + buffer.push(node.nodeValue); + } + return false; // Keep going. + }); + } + return buffer.join(''); +}; + + +/** + * @param {Node} node Node to check. + * @return {boolean} Does the given node represent a Block element? Notice we do + * not consider list items as Block elements in the algorithm. + * @private + */ +goog.editor.plugins.FirstStrong.isBlock_ = function(node) { + return !!node && goog.editor.node.isBlockTag(node) && + node.tagName != goog.dom.TagName.LI; +}; + + +/** + * @param {Node} node Node to check. + * @return {boolean} Does the given node represent a Block element from the + * point of view of FireFox? Notice we do not consider list items as Block + * elements in the algorithm. + * @private + */ +goog.editor.plugins.FirstStrong.isGeckoBlock_ = function(node) { + return !!node && (node.tagName == goog.dom.TagName.BR || + goog.editor.plugins.FirstStrong.isBlock_(node)); +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.html new file mode 100644 index 0000000000..ad18959922 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.html @@ -0,0 +1,26 @@ + + + + + + + Trogedit Unit Tests - goog.editor.plugins.FirstStrong + + + + + +
    +
    +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.js new file mode 100644 index 0000000000..8435887fd3 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/firststrong_test.js @@ -0,0 +1,406 @@ +// Copyright 2012 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.FirstStrongTest'); +goog.setTestOnly('goog.editor.plugins.FirstStrongTest'); + +goog.require('goog.dom.Range'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.FirstStrong'); +goog.require('goog.editor.range'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +// The key code for the Hebrew א, a strongly RTL letter. +var ALEPH_KEYCODE = 1488; + +var field; +var fieldElement; +var dom; +var helper; +var triggeredCommand = null; + +function setUp() { + field = new goog.editor.Field('field'); + field.registerPlugin(new goog.editor.plugins.FirstStrong()); + field.makeEditable(); + + fieldElement = field.getElement(); + + helper = new goog.testing.editor.TestHelper(fieldElement); + + dom = field.getEditableDomHelper(); + + // Mock out execCommand to see if a direction change has been triggered. + field.execCommand = function(command) { + if (command == goog.editor.Command.DIR_LTR || + command == goog.editor.Command.DIR_RTL) + triggeredCommand = command; + }; +} + +function tearDown() { + goog.dispose(field); + goog.dispose(helper); + triggeredCommand = null; +} + +function testFirstCharacter_RTL() { + field.setHtml(false, '
     
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstCharacter_LTR() { + field.setHtml(false, '
     
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + +function testFirstStrongCharacter_RTL() { + field.setHtml(false, '
    123.7 3121, <++{}> - $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacter_LTR() { + field.setHtml(false, + '
    123.7 3121, <++{}> - $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + +function testNotStrongCharacter_RTL() { + field.setHtml(false, '
    123.7 3121, - $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, goog.events.KeyCodes.NINE); + assertNoCommand(); +} + +function testNotStrongCharacter_LTR() { + field.setHtml(false, '
    123.7 3121 $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, goog.events.KeyCodes.NINE); + assertNoCommand(); +} + +function testNotFirstStrongCharacter_RTL() { + field.setHtml(false, '
    123.7 3121, English - $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertNoCommand(); +} + +function testNotFirstStrongCharacter_LTR() { + field.setHtml(false, + '
    123.7 3121, עברית - $45
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertNoCommand(); +} + +function testFirstStrongCharacterWithInnerDiv_RTL() { + field.setHtml(false, + '
    123.7 3121, <++{}>' + + '
    English
    ' + + '
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterWithInnerDiv_LTR() { + field.setHtml(false, + '
    123.7 3121, <++{}>' + + '
    English
    ' + + '
    '); + field.focusAndPlaceCursorAtStart(); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + + +/** + * Regression for {@link http://b/7549696} + */ +function testFirstStrongCharacterInNewLine_RTL() { + field.setHtml(false, '
    English
    1
    '); + goog.dom.Range.createCaret(dom.$('cur'), 2).select(); + + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + // Only GECKO treats
    as a new paragraph. + if (goog.userAgent.GECKO) { + assertRTL(); + } else { + assertNoCommand(); + } +} + +function testFirstStrongCharacterInParagraph_RTL() { + field.setHtml(false, + '
    1> English
    ' + + '
    2>
    ' + + '
    3>
    '); + goog.dom.Range.createCaret(dom.$('text2'), 0).select(); + + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterInParagraph_LTR() { + field.setHtml(false, + '
    1> עברית
    ' + + '
    2>
    ' + + '
    3>
    '); + goog.dom.Range.createCaret(dom.$('text2'), 0).select(); + + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + +function testFirstStrongCharacterInList_RTL() { + field.setHtml(false, + '
    1> English
    ' + + '
      ' + + '
    • 10>
    • ' + + '
    • ' + + '
    • 30
    • ' + + '
    ' + + '
    3>
    '); + goog.editor.range.placeCursorNextTo(dom.$('li2'), true); + + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterInList_LTR() { + field.setHtml(false, + '
    1> English
    ' + + '
      ' + + '
    • 10>
    • ' + + '
    • ' + + '
    • 30
    • ' + + '
    ' + + '
    3>
    '); + goog.editor.range.placeCursorNextTo(dom.$('li2'), true); + + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + +function testNotFirstStrongCharacterInList_RTL() { + field.setHtml(false, + '
    1
    ' + + '
      ' + + '
    • 10>
    • ' + + '
    • ' + + '
    • 303Hidden English32
    • ' + + '
    ' + + '
    3>
    '); + goog.editor.range.placeCursorNextTo(dom.$('li2'), true); + + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertNoCommand(); +} + +function testNotFirstStrongCharacterInList_LTR() { + field.setHtml(false, + '
    1> English
    ' + + '
      ' + + '
    • 10>
    • ' + + '
    • ' + + '
    • 303עברית סמויה32
    • ' + + '
    ' + + '
    3>
    '); + goog.editor.range.placeCursorNextTo(dom.$('li2'), true); + + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertNoCommand(); +} + +function testFirstStrongCharacterWithBR_RTL() { + field.setHtml(false, + '
    ' + + '
    ABC
    ' + + '
    ' + + '1
    ' + + '2345
    ' + + '6
    7
    89
    ' + + '10' + + '
    ' + + '
    11
    ' + + '
    '); + + goog.editor.range.placeCursorNextTo(dom.$('inner'), true); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterWithBR_LTR() { + field.setHtml(false, + '
    ' + + '
    אבג
    ' + + '
    ' + + '1
    ' + + '2345
    ' + + '6
    7
    8
    9
    ' + + '10' + + '
    ' + + '
    11
    ' + + '
    '); + + goog.editor.range.placeCursorNextTo(dom.$('inner'), true); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertLTR(); +} + +function testNotFirstStrongCharacterInBR_RTL() { + field.setHtml(false, + '
    ' + + '
    ABC
    ' + + '
    ' + + '1
    ' + + '234G5
    ' + + '6
    7
    89
    ' + + '10' + + '
    ' + + '
    11
    ' + + '
    '); + + goog.editor.range.placeCursorNextTo(dom.$('inner'), true); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertNoCommand(); +} + +function testNotFirstStrongCharacterInBR_LTR() { + field.setHtml(false, + '
    ' + + '
    ABC
    ' + + '
    ' + + '1
    ' + + '234G5
    ' + + '6
    7
    89
    ' + + '10' + + '
    ' + + '
    11
    ' + + '
    '); + + goog.editor.range.placeCursorNextTo(dom.$('inner'), true); + goog.testing.events.fireKeySequence(fieldElement, + goog.events.KeyCodes.A); + assertNoCommand(); +} + + +/** + * Regression for {@link http://b/7530985} + */ +function testFirstStrongCharacterWithPreviousBlockSibling_RTL() { + field.setHtml(false, '
    Te
    xt
    123
    '); + goog.editor.range.placeCursorNextTo(dom.$('cur'), true); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterWithPreviousBlockSibling_LTR() { + field.setHtml( + false, '
    טק
    סט
    123
    '); + goog.editor.range.placeCursorNextTo(dom.$('cur'), true); + goog.testing.events.fireKeySequence(fieldElement, goog.events.KeyCodes.A); + assertLTR(); +} + +function testFirstStrongCharacterWithFollowingBlockSibling_RTL() { + field.setHtml(false, '
    123
    Te
    xt
    '); + goog.editor.range.placeCursorNextTo(dom.$('cur'), true); + goog.testing.events.fireNonAsciiKeySequence(fieldElement, + goog.events.KeyCodes.T, ALEPH_KEYCODE); + assertRTL(); +} + +function testFirstStrongCharacterWithFollowingBlockSibling_RTL() { + field.setHtml(false, '
    123
    א
    ב
    '); + goog.editor.range.placeCursorNextTo(dom.$('cur'), true); + goog.testing.events.fireKeySequence(fieldElement, goog.events.KeyCodes.A); + assertLTR(); +} + +function testFirstStrongCharacterFromIME_RTL() { + field.setHtml(false, '
    123.7 3121,
    '); + field.focusAndPlaceCursorAtStart(); + var attributes = {}; + attributes[goog.editor.plugins.FirstStrong.INPUT_ATTRIBUTE] = 'אבג'; + goog.testing.events.fireNonAsciiKeySequence(fieldElement, 0, 0, attributes); + if (goog.userAgent.IE) { + // goog.testing.events.fireNonAsciiKeySequence doesn't send KEYPRESS event + // so no command is expected. + assertNoCommand(); + } else { + assertRTL(); + } +} + +function testFirstCharacterFromIME_LTR() { + field.setHtml(false, '
    1234
    '); + field.focusAndPlaceCursorAtStart(); + var attributes = {}; + attributes[goog.editor.plugins.FirstStrong.INPUT_ATTRIBUTE] = 'ABC'; + goog.testing.events.fireNonAsciiKeySequence(fieldElement, 0, 0, attributes); + if (goog.userAgent.IE) { + // goog.testing.events.fireNonAsciiKeySequence doesn't send KEYPRESS event + // so no command is expected. + assertNoCommand(); + } else { + assertLTR(); + } +} + +function assertRTL() { + assertEquals(goog.editor.Command.DIR_RTL, triggeredCommand); +} + +function assertLTR() { + assertEquals(goog.editor.Command.DIR_LTR, triggeredCommand); +} + +function assertNoCommand() { + assertNull(triggeredCommand); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter.js new file mode 100644 index 0000000000..fa4bb1f76d --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter.js @@ -0,0 +1,96 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Handles applying header styles to text. + * + */ + +goog.provide('goog.editor.plugins.HeaderFormatter'); + +goog.require('goog.editor.Command'); +goog.require('goog.editor.Plugin'); +goog.require('goog.userAgent'); + + + +/** + * Applies header styles to text. + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.HeaderFormatter = function() { + goog.editor.Plugin.call(this); +}; +goog.inherits(goog.editor.plugins.HeaderFormatter, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.HeaderFormatter.prototype.getTrogClassId = function() { + return 'HeaderFormatter'; +}; + +// TODO(user): Move execCommand functionality from basictextformatter into +// here for headers. I'm not doing this now because it depends on the +// switch statements in basictextformatter and we'll need to abstract that out +// in order to seperate out any of the functions from basictextformatter. + + +/** + * Commands that can be passed as the optional argument to execCommand. + * @enum {string} + */ +goog.editor.plugins.HeaderFormatter.HEADER_COMMAND = { + H1: 'H1', + H2: 'H2', + H3: 'H3', + H4: 'H4' +}; + + +/** + * @override + */ +goog.editor.plugins.HeaderFormatter.prototype.handleKeyboardShortcut = function( + e, key, isModifierPressed) { + if (!isModifierPressed) { + return false; + } + var command = null; + switch (key) { + case '1': + command = goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H1; + break; + case '2': + command = goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H2; + break; + case '3': + command = goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H3; + break; + case '4': + command = goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H4; + break; + } + if (command) { + this.getFieldObject().execCommand( + goog.editor.Command.FORMAT_BLOCK, command); + // Prevent default isn't enough to cancel tab navigation in FF. + if (goog.userAgent.GECKO) { + e.stopPropagation(); + } + return true; + } + return false; +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.html new file mode 100644 index 0000000000..128dba4a7b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.html @@ -0,0 +1,27 @@ + + + + + + + + goog.editor.plugins.HeaderFormatter Tests + + + + + +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.js new file mode 100644 index 0000000000..71688ee2ed --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/headerformatter_test.js @@ -0,0 +1,95 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.HeaderFormatterTest'); +goog.setTestOnly('goog.editor.plugins.HeaderFormatterTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.plugins.BasicTextFormatter'); +goog.require('goog.editor.plugins.HeaderFormatter'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.testing.LooseMock'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var field; +var editableField; +var headerFormatter; +var btf; +var testHelper; + +function setUpPage() { + field = goog.dom.getElement('field'); + testHelper = new goog.testing.editor.TestHelper(field); +} + +function setUp() { + testHelper.setUpEditableElement(); + editableField = new goog.testing.editor.FieldMock(); + headerFormatter = new goog.editor.plugins.HeaderFormatter(); + headerFormatter.registerFieldObject(editableField); + btf = new goog.editor.plugins.BasicTextFormatter(); + btf.registerFieldObject(editableField); +} + +function tearDown() { + editableField = null; + headerFormatter.dispose(); + testHelper.tearDownEditableElement(); +} + + +function testHeaderShortcuts() { + field.innerHTML = 'myText'; + + var textNode = field.firstChild; + testHelper.select(textNode, 0, textNode, textNode.length); + + editableField.getElement(); + editableField.$anyTimes(); + editableField.$returns(field); + + editableField.getPluginByClassId('Bidi'); + editableField.$anyTimes(); + editableField.$returns(null); + + editableField.execCommand( + goog.editor.Command.FORMAT_BLOCK, + goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H1); + // Bypass EditableField's execCommand and directly call + // basicTextFormatter's. Future version of headerformatter will include + // that code in its own execCommand. + editableField.$does(function() { + btf.execCommandInternal( + goog.editor.plugins.BasicTextFormatter.COMMAND.FORMAT_BLOCK, + goog.editor.plugins.HeaderFormatter.HEADER_COMMAND.H1); }); + + var event = new goog.testing.LooseMock(goog.events.BrowserEvent); + if (goog.userAgent.GECKO) { + event.stopPropagation(); + } + + editableField.$replay(); + event.$replay(); + + assertTrue('Event handled', + headerFormatter.handleKeyboardShortcut(event, '1', true)); + assertEquals('Field contains a header', 'H1', field.firstChild.nodeName); + + editableField.$verify(); + event.$verify(); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble.js new file mode 100644 index 0000000000..01c84f3f11 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble.js @@ -0,0 +1,585 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Base class for bubble plugins. + * + */ + +goog.provide('goog.editor.plugins.LinkBubble'); +goog.provide('goog.editor.plugins.LinkBubble.Action'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Link'); +goog.require('goog.editor.plugins.AbstractBubblePlugin'); +goog.require('goog.editor.range'); +goog.require('goog.functions'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.ui.editor.messages'); +goog.require('goog.uri.utils'); +goog.require('goog.window'); + + + +/** + * Property bubble plugin for links. + * @param {...!goog.editor.plugins.LinkBubble.Action} var_args List of + * extra actions supported by the bubble. + * @constructor + * @extends {goog.editor.plugins.AbstractBubblePlugin} + */ +goog.editor.plugins.LinkBubble = function(var_args) { + goog.editor.plugins.LinkBubble.base(this, 'constructor'); + + /** + * List of extra actions supported by the bubble. + * @type {Array} + * @private + */ + this.extraActions_ = goog.array.toArray(arguments); + + /** + * List of spans corresponding to the extra actions. + * @type {Array} + * @private + */ + this.actionSpans_ = []; + + /** + * A list of whitelisted URL schemes which are safe to open. + * @type {Array} + * @private + */ + this.safeToOpenSchemes_ = ['http', 'https', 'ftp']; +}; +goog.inherits(goog.editor.plugins.LinkBubble, + goog.editor.plugins.AbstractBubblePlugin); + + +/** + * Element id for the link text. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.LINK_TEXT_ID_ = 'tr_link-text'; + + +/** + * Element id for the test link span. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.TEST_LINK_SPAN_ID_ = 'tr_test-link-span'; + + +/** + * Element id for the test link. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.TEST_LINK_ID_ = 'tr_test-link'; + + +/** + * Element id for the change link span. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.CHANGE_LINK_SPAN_ID_ = 'tr_change-link-span'; + + +/** + * Element id for the link. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.CHANGE_LINK_ID_ = 'tr_change-link'; + + +/** + * Element id for the delete link span. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.DELETE_LINK_SPAN_ID_ = 'tr_delete-link-span'; + + +/** + * Element id for the delete link. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.DELETE_LINK_ID_ = 'tr_delete-link'; + + +/** + * Element id for the link bubble wrapper div. + * type {string} + * @private + */ +goog.editor.plugins.LinkBubble.LINK_DIV_ID_ = 'tr_link-div'; + + +/** + * @desc Text label for link that lets the user click it to see where the link + * this bubble is for point to. + */ +goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_TEST_LINK = goog.getMsg( + 'Go to link: '); + + +/** + * @desc Label that pops up a dialog to change the link. + */ +goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_CHANGE = goog.getMsg( + 'Change'); + + +/** + * @desc Label that allow the user to remove this link. + */ +goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_REMOVE = goog.getMsg( + 'Remove'); + + +/** + * @desc Message shown in a link bubble when the link is not a valid url. + */ +goog.editor.plugins.LinkBubble.MSG_INVALID_URL_LINK_BUBBLE = goog.getMsg( + 'invalid url'); + + +/** + * Whether to stop leaking the page's url via the referrer header when the + * link text link is clicked. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkBubble.prototype.stopReferrerLeaks_ = false; + + +/** + * Whether to block opening links with a non-whitelisted URL scheme. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkBubble.prototype.blockOpeningUnsafeSchemes_ = + true; + + +/** + * Tells the plugin to stop leaking the page's url via the referrer header when + * the link text link is clicked. When the user clicks on a link, the + * browser makes a request for the link url, passing the url of the current page + * in the request headers. If the user wants the current url to be kept secret + * (e.g. an unpublished document), the owner of the url that was clicked will + * see the secret url in the request headers, and it will no longer be a secret. + * Calling this method will not send a referrer header in the request, just as + * if the user had opened a blank window and typed the url in themselves. + */ +goog.editor.plugins.LinkBubble.prototype.stopReferrerLeaks = function() { + // TODO(user): Right now only 2 plugins have this API to stop + // referrer leaks. If more plugins need to do this, come up with a way to + // enable the functionality in all plugins at once. Same thing for + // setBlockOpeningUnsafeSchemes and associated functionality. + this.stopReferrerLeaks_ = true; +}; + + +/** + * Tells the plugin whether to block URLs with schemes not in the whitelist. + * If blocking is enabled, this plugin will not linkify the link in the bubble + * popup. + * @param {boolean} blockOpeningUnsafeSchemes Whether to block non-whitelisted + * schemes. + */ +goog.editor.plugins.LinkBubble.prototype.setBlockOpeningUnsafeSchemes = + function(blockOpeningUnsafeSchemes) { + this.blockOpeningUnsafeSchemes_ = blockOpeningUnsafeSchemes; +}; + + +/** + * Sets a whitelist of allowed URL schemes that are safe to open. + * Schemes should all be in lowercase. If the plugin is set to block opening + * unsafe schemes, user-entered URLs will be converted to lowercase and checked + * against this list. The whitelist has no effect if blocking is not enabled. + * @param {Array} schemes String array of URL schemes to allow (http, + * https, etc.). + */ +goog.editor.plugins.LinkBubble.prototype.setSafeToOpenSchemes = + function(schemes) { + this.safeToOpenSchemes_ = schemes; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.getTrogClassId = function() { + return 'LinkBubble'; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.isSupportedCommand = + function(command) { + return command == goog.editor.Command.UPDATE_LINK_BUBBLE; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.execCommandInternal = + function(command, var_args) { + if (command == goog.editor.Command.UPDATE_LINK_BUBBLE) { + this.updateLink_(); + } +}; + + +/** + * Updates the href in the link bubble with a new link. + * @private + */ +goog.editor.plugins.LinkBubble.prototype.updateLink_ = function() { + var targetEl = this.getTargetElement(); + if (targetEl) { + this.closeBubble(); + this.createBubble(targetEl); + } +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.getBubbleTargetFromSelection = + function(selectedElement) { + var bubbleTarget = goog.dom.getAncestorByTagNameAndClass(selectedElement, + goog.dom.TagName.A); + + if (!bubbleTarget) { + // See if the selection is touching the right side of a link, and if so, + // show a bubble for that link. The check for "touching" is very brittle, + // and currently only guarantees that it will pop up a bubble at the + // position the cursor is placed at after the link dialog is closed. + // NOTE(robbyw): This assumes this method is always called with + // selected element = range.getContainerElement(). Right now this is true, + // but attempts to re-use this method for other purposes could cause issues. + // TODO(robbyw): Refactor this method to also take a range, and use that. + var range = this.getFieldObject().getRange(); + if (range && range.isCollapsed() && range.getStartOffset() == 0) { + var startNode = range.getStartNode(); + var previous = startNode.previousSibling; + if (previous && previous.tagName == goog.dom.TagName.A) { + bubbleTarget = previous; + } + } + } + + return /** @type {Element} */ (bubbleTarget); +}; + + +/** + * Set the optional function for getting the "test" link of a url. + * @param {function(string) : string} func The function to use. + */ +goog.editor.plugins.LinkBubble.prototype.setTestLinkUrlFn = function(func) { + this.testLinkUrlFn_ = func; +}; + + +/** + * Returns the target element url for the bubble. + * @return {string} The url href. + * @protected + */ +goog.editor.plugins.LinkBubble.prototype.getTargetUrl = function() { + // Get the href-attribute through getAttribute() rather than the href property + // because Google-Toolbar on Firefox with "Send with Gmail" turned on + // modifies the href-property of 'mailto:' links but leaves the attribute + // untouched. + return this.getTargetElement().getAttribute('href') || ''; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.getBubbleType = function() { + return goog.dom.TagName.A; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.getBubbleTitle = function() { + return goog.ui.editor.messages.MSG_LINK_CAPTION; +}; + + +/** + * Returns the message to display for testing a link. + * @return {string} The message for testing a link. + * @protected + */ +goog.editor.plugins.LinkBubble.prototype.getTestLinkMessage = function() { + return goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_TEST_LINK; +}; + + +/** @override */ +goog.editor.plugins.LinkBubble.prototype.createBubbleContents = function( + bubbleContainer) { + var linkObj = this.getLinkToTextObj_(); + + // Create linkTextSpan, show plain text for e-mail address or truncate the + // text to <= 48 characters so that property bubbles don't grow too wide and + // create a link if URL. Only linkify valid links. + // TODO(robbyw): Repalce this color with a CSS class. + var color = linkObj.valid ? 'black' : 'red'; + var shouldOpenUrl = this.shouldOpenUrl(linkObj.linkText); + var linkTextSpan; + if (goog.editor.Link.isLikelyEmailAddress(linkObj.linkText) || + !linkObj.valid || !shouldOpenUrl) { + linkTextSpan = this.dom_.createDom(goog.dom.TagName.SPAN, + { + id: goog.editor.plugins.LinkBubble.LINK_TEXT_ID_, + style: 'color:' + color + }, this.dom_.createTextNode(linkObj.linkText)); + } else { + var testMsgSpan = this.dom_.createDom(goog.dom.TagName.SPAN, + {id: goog.editor.plugins.LinkBubble.TEST_LINK_SPAN_ID_}, + this.getTestLinkMessage()); + linkTextSpan = this.dom_.createDom(goog.dom.TagName.SPAN, + { + id: goog.editor.plugins.LinkBubble.LINK_TEXT_ID_, + style: 'color:' + color + }, ''); + var linkText = goog.string.truncateMiddle(linkObj.linkText, 48); + // Actually creates a pseudo-link that can't be right-clicked to open in a + // new tab, because that would avoid the logic to stop referrer leaks. + this.createLink(goog.editor.plugins.LinkBubble.TEST_LINK_ID_, + this.dom_.createTextNode(linkText).data, + this.testLink, + linkTextSpan); + } + + var changeLinkSpan = this.createLinkOption( + goog.editor.plugins.LinkBubble.CHANGE_LINK_SPAN_ID_); + this.createLink(goog.editor.plugins.LinkBubble.CHANGE_LINK_ID_, + goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_CHANGE, + this.showLinkDialog_, changeLinkSpan); + + // This function is called multiple times - we have to reset the array. + this.actionSpans_ = []; + for (var i = 0; i < this.extraActions_.length; i++) { + var action = this.extraActions_[i]; + var actionSpan = this.createLinkOption(action.spanId_); + this.actionSpans_.push(actionSpan); + this.createLink(action.linkId_, action.message_, + function() { + action.actionFn_(this.getTargetUrl()); + }, + actionSpan); + } + + var removeLinkSpan = this.createLinkOption( + goog.editor.plugins.LinkBubble.DELETE_LINK_SPAN_ID_); + this.createLink(goog.editor.plugins.LinkBubble.DELETE_LINK_ID_, + goog.editor.plugins.LinkBubble.MSG_LINK_BUBBLE_REMOVE, + this.deleteLink_, removeLinkSpan); + + this.onShow(); + + var bubbleContents = this.dom_.createDom(goog.dom.TagName.DIV, + {id: goog.editor.plugins.LinkBubble.LINK_DIV_ID_}, + testMsgSpan || '', linkTextSpan, changeLinkSpan); + + for (i = 0; i < this.actionSpans_.length; i++) { + bubbleContents.appendChild(this.actionSpans_[i]); + } + bubbleContents.appendChild(removeLinkSpan); + + goog.dom.appendChild(bubbleContainer, bubbleContents); +}; + + +/** + * Tests the link by opening it in a new tab/window. Should be used as the + * click event handler for the test pseudo-link. + * @protected + */ +goog.editor.plugins.LinkBubble.prototype.testLink = function() { + goog.window.open(this.getTestLinkAction_(), + { + 'target': '_blank', + 'noreferrer': this.stopReferrerLeaks_ + }, this.getFieldObject().getAppWindow()); +}; + + +/** + * Returns whether the URL should be considered invalid. This always returns + * false in the base class, and should be overridden by subclasses that wish + * to impose validity rules on URLs. + * @param {string} url The url to check. + * @return {boolean} Whether the URL should be considered invalid. + */ +goog.editor.plugins.LinkBubble.prototype.isInvalidUrl = goog.functions.FALSE; + + +/** + * Gets the text to display for a link, based on the type of link + * @return {!Object} Returns an object of the form: + * {linkText: displayTextForLinkTarget, valid: ifTheLinkIsValid}. + * @private + */ +goog.editor.plugins.LinkBubble.prototype.getLinkToTextObj_ = function() { + var isError; + var targetUrl = this.getTargetUrl(); + + if (this.isInvalidUrl(targetUrl)) { + + targetUrl = goog.editor.plugins.LinkBubble.MSG_INVALID_URL_LINK_BUBBLE; + isError = true; + } else if (goog.editor.Link.isMailto(targetUrl)) { + targetUrl = targetUrl.substring(7); // 7 == "mailto:".length + } + + return {linkText: targetUrl, valid: !isError}; +}; + + +/** + * Shows the link dialog. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.editor.plugins.LinkBubble.prototype.showLinkDialog_ = function(e) { + // Needed when this occurs due to an ENTER key event, else the newly created + // dialog manages to have its OK button pressed, causing it to disappear. + e.preventDefault(); + + this.getFieldObject().execCommand(goog.editor.Command.MODAL_LINK_EDITOR, + new goog.editor.Link( + /** @type {HTMLAnchorElement} */ (this.getTargetElement()), + false)); + this.closeBubble(); +}; + + +/** + * Deletes the link associated with the bubble + * @private + */ +goog.editor.plugins.LinkBubble.prototype.deleteLink_ = function() { + this.getFieldObject().dispatchBeforeChange(); + + var link = this.getTargetElement(); + var child = link.lastChild; + goog.dom.flattenElement(link); + goog.editor.range.placeCursorNextTo(child, false); + + this.closeBubble(); + + this.getFieldObject().dispatchChange(); + this.getFieldObject().focus(); +}; + + +/** + * Sets the proper state for the action links. + * @protected + * @override + */ +goog.editor.plugins.LinkBubble.prototype.onShow = function() { + var linkDiv = this.dom_.getElement( + goog.editor.plugins.LinkBubble.LINK_DIV_ID_); + if (linkDiv) { + var testLinkSpan = this.dom_.getElement( + goog.editor.plugins.LinkBubble.TEST_LINK_SPAN_ID_); + if (testLinkSpan) { + var url = this.getTargetUrl(); + goog.style.setElementShown(testLinkSpan, !goog.editor.Link.isMailto(url)); + } + + for (var i = 0; i < this.extraActions_.length; i++) { + var action = this.extraActions_[i]; + var actionSpan = this.dom_.getElement(action.spanId_); + if (actionSpan) { + goog.style.setElementShown(actionSpan, action.toShowFn_( + this.getTargetUrl())); + } + } + } +}; + + +/** + * Gets the url for the bubble test link. The test link is the link in the + * bubble the user can click on to make sure the link they entered is correct. + * @return {string} The url for the bubble link href. + * @private + */ +goog.editor.plugins.LinkBubble.prototype.getTestLinkAction_ = function() { + var targetUrl = this.getTargetUrl(); + return this.testLinkUrlFn_ ? this.testLinkUrlFn_(targetUrl) : targetUrl; +}; + + +/** + * Checks whether the plugin should open the given url in a new window. + * @param {string} url The url to check. + * @return {boolean} If the plugin should open the given url in a new window. + * @protected + */ +goog.editor.plugins.LinkBubble.prototype.shouldOpenUrl = function(url) { + return !this.blockOpeningUnsafeSchemes_ || this.isSafeSchemeToOpen_(url); +}; + + +/** + * Determines whether or not a url has a scheme which is safe to open. + * Schemes like javascript are unsafe due to the possibility of XSS. + * @param {string} url A url. + * @return {boolean} Whether the url has a safe scheme. + * @private + */ +goog.editor.plugins.LinkBubble.prototype.isSafeSchemeToOpen_ = + function(url) { + var scheme = goog.uri.utils.getScheme(url) || 'http'; + return goog.array.contains(this.safeToOpenSchemes_, scheme.toLowerCase()); +}; + + + +/** + * Constructor for extra actions that can be added to the link bubble. + * @param {string} spanId The ID for the span showing the action. + * @param {string} linkId The ID for the link showing the action. + * @param {string} message The text for the link showing the action. + * @param {function(string):boolean} toShowFn Test function to determine whether + * to show the action for the given URL. + * @param {function(string):void} actionFn Action function to run when the + * action is clicked. Takes the current target URL as a parameter. + * @constructor + * @final + */ +goog.editor.plugins.LinkBubble.Action = function(spanId, linkId, message, + toShowFn, actionFn) { + this.spanId_ = spanId; + this.linkId_ = linkId; + this.message_ = message; + this.toShowFn_ = toShowFn; + this.actionFn_ = actionFn; +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.html new file mode 100644 index 0000000000..7d41385081 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.html @@ -0,0 +1,28 @@ + + + + + + + + goog.editor.plugins.LinkBubble Tests + + + + + + + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.js new file mode 100644 index 0000000000..c878c9bebf --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkbubble_test.js @@ -0,0 +1,396 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.LinkBubbleTest'); +goog.setTestOnly('goog.editor.plugins.LinkBubbleTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Link'); +goog.require('goog.editor.plugins.LinkBubble'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); +goog.require('goog.string'); +goog.require('goog.style'); +goog.require('goog.testing.FunctionMock'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var fieldDiv; +var FIELDMOCK; +var linkBubble; +var link; +var mockWindowOpen; +var stubs; +var testHelper; + +function setUpPage() { + fieldDiv = goog.dom.$('field'); + stubs = new goog.testing.PropertyReplacer(); + testHelper = new goog.testing.editor.TestHelper(goog.dom.getElement('field')); +} + +function setUp() { + testHelper.setUpEditableElement(); + FIELDMOCK = new goog.testing.editor.FieldMock(); + + linkBubble = new goog.editor.plugins.LinkBubble(); + linkBubble.fieldObject = FIELDMOCK; + + link = fieldDiv.firstChild; + + mockWindowOpen = new goog.testing.FunctionMock('open'); + stubs.set(window, 'open', mockWindowOpen); +} + +function tearDown() { + linkBubble.closeBubble(); + testHelper.tearDownEditableElement(); + stubs.reset(); +} + +function testLinkSelected() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + goog.dom.Range.createFromNodeContents(link).select(); + linkBubble.handleSelectionChange(); + assertBubble(); + FIELDMOCK.$verify(); +} + +function testLinkClicked() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + FIELDMOCK.$verify(); +} + +function testImageLink() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + link.setAttribute('imageanchor', 1); + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + FIELDMOCK.$verify(); +} + +function closeBox() { + var closeBox = goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.DIV, + 'tr_bubble_closebox'); + assertEquals('Should find only one close box', 1, closeBox.length); + assertNotNull('Found close box', closeBox[0]); + goog.testing.events.fireClickSequence(closeBox[0]); +} + +function testCloseBox() { + testLinkClicked(); + closeBox(); + assertNoBubble(); + FIELDMOCK.$verify(); +} + +function testChangeClicked() { + FIELDMOCK.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, + new goog.editor.Link(link, false)); + FIELDMOCK.$registerArgumentListVerifier('execCommand', function(arr1, arr2) { + return arr1.length == arr2.length && + arr1.length == 2 && + arr1[0] == goog.editor.Command.MODAL_LINK_EDITOR && + arr2[0] == goog.editor.Command.MODAL_LINK_EDITOR && + arr1[1] instanceof goog.editor.Link && + arr2[1] instanceof goog.editor.Link; + }); + FIELDMOCK.$times(1); + FIELDMOCK.$returns(true); + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + + goog.testing.events.fireClickSequence( + goog.dom.$(goog.editor.plugins.LinkBubble.CHANGE_LINK_ID_)); + assertNoBubble(); + FIELDMOCK.$verify(); +} + +function testDeleteClicked() { + FIELDMOCK.dispatchBeforeChange(); + FIELDMOCK.$times(1); + FIELDMOCK.dispatchChange(); + FIELDMOCK.$times(1); + FIELDMOCK.focus(); + FIELDMOCK.$times(1); + FIELDMOCK.$replay(); + + linkBubble.enable(FIELDMOCK); + + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + + goog.testing.events.fireClickSequence( + goog.dom.$(goog.editor.plugins.LinkBubble.DELETE_LINK_ID_)); + var element = goog.userAgent.GECKO ? document.body : fieldDiv; + assertNotEquals('Link removed', element.firstChild.nodeName, + goog.dom.TagName.A); + assertNoBubble(); + FIELDMOCK.$verify(); +} + +function testActionClicked() { + var SPAN = 'actionSpanId'; + var LINK = 'actionLinkId'; + var toShowCount = 0; + var actionCount = 0; + + var linkAction = new goog.editor.plugins.LinkBubble.Action( + SPAN, LINK, 'message', + function() { + toShowCount++; + return toShowCount == 1; // Show it the first time. + }, + function() { + actionCount++; + }); + + linkBubble = new goog.editor.plugins.LinkBubble(linkAction); + linkBubble.fieldObject = FIELDMOCK; + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + // The first time the bubble is shown, show our custom action. + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + assertEquals('Should check showing the action', 1, toShowCount); + assertEquals('Action should not have fired yet', 0, actionCount); + + assertTrue('Action should be visible 1st time', goog.style.isElementShown( + goog.dom.$(SPAN))); + goog.testing.events.fireClickSequence(goog.dom.$(LINK)); + + assertEquals('Should not check showing again yet', 1, toShowCount); + assertEquals('Action should be fired', 1, actionCount); + + closeBox(); + assertNoBubble(); + + // The action won't be shown the second time around. + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + assertEquals('Should check showing again', 2, toShowCount); + assertEquals('Action should not fire again', 1, actionCount); + assertFalse('Action should not be shown 2nd time', goog.style.isElementShown( + goog.dom.$(SPAN))); + + FIELDMOCK.$verify(); +} + +function testLinkTextClicked() { + mockWindowOpen('http://www.google.com/', '_blank', ''); + mockWindowOpen.$replay(); + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + + goog.testing.events.fireClickSequence( + goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_)); + + assertBubble(); + mockWindowOpen.$verify(); + FIELDMOCK.$verify(); +} + +function testLinkTextClickedCustomUrlFn() { + mockWindowOpen('http://images.google.com/', '_blank', ''); + mockWindowOpen.$replay(); + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + linkBubble.setTestLinkUrlFn(function(url) { + return url.replace('www', 'images'); + }); + + linkBubble.handleSelectionChange(createMouseEvent(link)); + assertBubble(); + + goog.testing.events.fireClickSequence( + goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_)); + + assertBubble(); + mockWindowOpen.$verify(); + FIELDMOCK.$verify(); +} + + +/** + * Urls with invalid schemes shouldn't be linkified. + * @bug 2585360 + */ +function testDontLinkifyInvalidScheme() { + mockWindowOpen.$replay(); + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + var badLink = document.createElement('a'); + badLink.href = 'javascript:alert(1)'; + badLink.innerHTML = 'bad link'; + + linkBubble.handleSelectionChange(createMouseEvent(badLink)); + assertBubble(); + + // The link shouldn't exist at all + assertNull(goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_)); + + assertBubble(); + mockWindowOpen.$verify(); + FIELDMOCK.$verify(); +} + +function testIsSafeSchemeToOpen() { + // Urls with no scheme at all are ok too since 'http://' will be prepended. + var good = [ + 'http://google.com', 'http://google.com/', 'https://google.com', + 'null@google.com', 'http://www.google.com', 'http://site.com', + 'google.com', 'google', 'http://google', 'HTTP://GOOGLE.COM', + 'HtTp://www.google.com' + ]; + + var bad = [ + 'javascript:google.com', 'httpp://google.com', 'data:foo', + 'javascript:alert(\'hi\');', 'abc:def' + ]; + + for (var i = 0; i < good.length; i++) { + assertTrue(good[i] + ' should have a safe scheme', + linkBubble.isSafeSchemeToOpen_(good[i])); + } + + for (i = 0; i < bad.length; i++) { + assertFalse(bad[i] + ' should have an unsafe scheme', + linkBubble.isSafeSchemeToOpen_(bad[i])); + } +} + +function testShouldOpenWithWhitelist() { + linkBubble.setSafeToOpenSchemes(['abc']); + + assertTrue('Scheme should be safe', + linkBubble.shouldOpenUrl('abc://google.com')); + assertFalse('Scheme should be unsafe', + linkBubble.shouldOpenUrl('http://google.com')); + + linkBubble.setBlockOpeningUnsafeSchemes(false); + assertTrue('Non-whitelisted should now be safe after disabling blocking', + linkBubble.shouldOpenUrl('http://google.com')); +} + + +/** + * @bug 763211 + * @bug 2182147 + */ +function testLongUrlTestLinkAnchorTextCorrect() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + var longUrl = 'http://www.reallylonglinkthatshouldbetruncated' + + 'becauseitistoolong.com'; + var truncatedLongUrl = goog.string.truncateMiddle(longUrl, 48); + + var longLink = document.createElement('a'); + longLink.href = longUrl; + longLink.innerHTML = 'Google'; + fieldDiv.appendChild(longLink); + + linkBubble.handleSelectionChange(createMouseEvent(longLink)); + assertBubble(); + + var testLinkEl = goog.dom.$(goog.editor.plugins.LinkBubble.TEST_LINK_ID_); + assertEquals( + 'The test link\'s anchor text should be the truncated URL.', + truncatedLongUrl, + testLinkEl.innerHTML); + + fieldDiv.removeChild(longLink); + FIELDMOCK.$verify(); +} + + +/** + * @bug 2416024 + */ +function testOverridingCreateBubbleContentsDoesntNpeGetTargetUrl() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + stubs.set(linkBubble, 'createBubbleContents', + function(elem) { + // getTargetUrl would cause an NPE if urlUtil_ wasn't defined yet. + linkBubble.getTargetUrl(); + }); + assertNotThrows('Accessing this.urlUtil_ should not NPE', + goog.bind(linkBubble.handleSelectionChange, + linkBubble, createMouseEvent(link))); + + FIELDMOCK.$verify(); +} + + +/** + * @bug 15379294 + */ +function testUpdateLinkCommandDoesNotTriggerAnException() { + FIELDMOCK.$replay(); + linkBubble.enable(FIELDMOCK); + + // At this point, the bubble was not created yet using its createBubble + // public method. + assertNotThrows( + 'Executing goog.editor.Command.UPDATE_LINK_BUBBLE should not trigger ' + + 'an exception even if the bubble was not created yet using its ' + + 'createBubble method.', + goog.bind(linkBubble.execCommandInternal, linkBubble, + goog.editor.Command.UPDATE_LINK_BUBBLE)); + + FIELDMOCK.$verify(); +} + +function assertBubble() { + assertTrue('Link bubble visible', linkBubble.isVisible()); + assertNotNull('Link bubble created', + goog.dom.$(goog.editor.plugins.LinkBubble.LINK_DIV_ID_)); +} + +function assertNoBubble() { + assertFalse('Link bubble not visible', linkBubble.isVisible()); + assertNull('Link bubble not created', + goog.dom.$(goog.editor.plugins.LinkBubble.LINK_DIV_ID_)); +} + +function createMouseEvent(target) { + var eventObj = new goog.events.Event(goog.events.EventType.MOUSEUP, target); + eventObj.button = goog.events.BrowserEvent.MouseButton.LEFT; + + return new goog.events.BrowserEvent(eventObj, target); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin.js new file mode 100644 index 0000000000..9c804a6fc1 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin.js @@ -0,0 +1,438 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A plugin for the LinkDialog. + * + * @author nicksantos@google.com (Nick Santos) + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.editor.plugins.LinkDialogPlugin'); + +goog.require('goog.array'); +goog.require('goog.dom'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.plugins.AbstractDialogPlugin'); +goog.require('goog.events.EventHandler'); +goog.require('goog.functions'); +goog.require('goog.ui.editor.AbstractDialog'); +goog.require('goog.ui.editor.LinkDialog'); +goog.require('goog.uri.utils'); + + + +/** + * A plugin that opens the link dialog. + * @constructor + * @extends {goog.editor.plugins.AbstractDialogPlugin} + */ +goog.editor.plugins.LinkDialogPlugin = function() { + goog.editor.plugins.LinkDialogPlugin.base( + this, 'constructor', goog.editor.Command.MODAL_LINK_EDITOR); + + /** + * Event handler for this object. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + + /** + * A list of whitelisted URL schemes which are safe to open. + * @type {Array} + * @private + */ + this.safeToOpenSchemes_ = ['http', 'https', 'ftp']; +}; +goog.inherits(goog.editor.plugins.LinkDialogPlugin, + goog.editor.plugins.AbstractDialogPlugin); + + +/** + * Link object that the dialog is editing. + * @type {goog.editor.Link} + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.currentLink_; + + +/** + * Optional warning to show about email addresses. + * @type {goog.html.SafeHtml} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.emailWarning_; + + +/** + * Whether to show a checkbox where the user can choose to have the link open in + * a new window. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.showOpenLinkInNewWindow_ = false; + + +/** + * Whether the "open link in new window" checkbox should be checked when the + * dialog is shown, and also whether it was checked last time the dialog was + * closed. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.isOpenLinkInNewWindowChecked_ = + false; + + +/** + * Weather to show a checkbox where the user can choose to add 'rel=nofollow' + * attribute added to the link. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.showRelNoFollow_ = false; + + +/** + * Whether to stop referrer leaks. Defaults to false. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.stopReferrerLeaks_ = false; + + +/** + * Whether to block opening links with a non-whitelisted URL scheme. + * @type {boolean} + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.blockOpeningUnsafeSchemes_ = + true; + + +/** @override */ +goog.editor.plugins.LinkDialogPlugin.prototype.getTrogClassId = + goog.functions.constant('LinkDialogPlugin'); + + +/** + * Tells the plugin whether to block URLs with schemes not in the whitelist. + * If blocking is enabled, this plugin will stop the 'Test Link' popup + * window from being created. Blocking doesn't affect link creation--if the + * user clicks the 'OK' button with an unsafe URL, the link will still be + * created as normal. + * @param {boolean} blockOpeningUnsafeSchemes Whether to block non-whitelisted + * schemes. + */ +goog.editor.plugins.LinkDialogPlugin.prototype.setBlockOpeningUnsafeSchemes = + function(blockOpeningUnsafeSchemes) { + this.blockOpeningUnsafeSchemes_ = blockOpeningUnsafeSchemes; +}; + + +/** + * Sets a whitelist of allowed URL schemes that are safe to open. + * Schemes should all be in lowercase. If the plugin is set to block opening + * unsafe schemes, user-entered URLs will be converted to lowercase and checked + * against this list. The whitelist has no effect if blocking is not enabled. + * @param {Array} schemes String array of URL schemes to allow (http, + * https, etc.). + */ +goog.editor.plugins.LinkDialogPlugin.prototype.setSafeToOpenSchemes = + function(schemes) { + this.safeToOpenSchemes_ = schemes; +}; + + +/** + * Tells the dialog to show a checkbox where the user can choose to have the + * link open in a new window. + * @param {boolean} startChecked Whether to check the checkbox the first + * time the dialog is shown. Subesquent times the checkbox will remember its + * previous state. + */ +goog.editor.plugins.LinkDialogPlugin.prototype.showOpenLinkInNewWindow = + function(startChecked) { + this.showOpenLinkInNewWindow_ = true; + this.isOpenLinkInNewWindowChecked_ = startChecked; +}; + + +/** + * Tells the dialog to show a checkbox where the user can choose to have + * 'rel=nofollow' attribute added to the link. + */ +goog.editor.plugins.LinkDialogPlugin.prototype.showRelNoFollow = function() { + this.showRelNoFollow_ = true; +}; + + +/** + * Returns whether the"open link in new window" checkbox was checked last time + * the dialog was closed. + * @return {boolean} Whether the"open link in new window" checkbox was checked + * last time the dialog was closed. + */ +goog.editor.plugins.LinkDialogPlugin.prototype. + getOpenLinkInNewWindowCheckedState = function() { + return this.isOpenLinkInNewWindowChecked_; +}; + + +/** + * Tells the plugin to stop leaking the page's url via the referrer header when + * the "test this link" link is clicked. When the user clicks on a link, the + * browser makes a request for the link url, passing the url of the current page + * in the request headers. If the user wants the current url to be kept secret + * (e.g. an unpublished document), the owner of the url that was clicked will + * see the secret url in the request headers, and it will no longer be a secret. + * Calling this method will not send a referrer header in the request, just as + * if the user had opened a blank window and typed the url in themselves. + */ +goog.editor.plugins.LinkDialogPlugin.prototype.stopReferrerLeaks = function() { + this.stopReferrerLeaks_ = true; +}; + + +/** + * Sets the warning message to show to users about including email addresses on + * public web pages. + * @param {!goog.html.SafeHtml} emailWarning Warning message to show users about + * including email addresses on the web. + */ +goog.editor.plugins.LinkDialogPlugin.prototype.setEmailWarning = function( + emailWarning) { + this.emailWarning_ = emailWarning; +}; + + +/** + * Handles execCommand by opening the dialog. + * @param {string} command The command to execute. + * @param {*=} opt_arg {@link A goog.editor.Link} object representing the link + * being edited. + * @return {*} Always returns true, indicating the dialog was shown. + * @protected + * @override + */ +goog.editor.plugins.LinkDialogPlugin.prototype.execCommandInternal = function( + command, opt_arg) { + this.currentLink_ = /** @type {goog.editor.Link} */(opt_arg); + return goog.editor.plugins.LinkDialogPlugin.base( + this, 'execCommandInternal', command, opt_arg); +}; + + +/** + * Handles when the dialog closes. + * @param {goog.events.Event} e The AFTER_HIDE event object. + * @override + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.handleAfterHide = function(e) { + goog.editor.plugins.LinkDialogPlugin.base(this, 'handleAfterHide', e); + this.currentLink_ = null; +}; + + +/** + * @return {goog.events.EventHandler} The event handler. + * @protected + * @this T + * @template T + */ +goog.editor.plugins.LinkDialogPlugin.prototype.getEventHandler = function() { + return this.eventHandler_; +}; + + +/** + * @return {goog.editor.Link} The link being edited. + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.getCurrentLink = function() { + return this.currentLink_; +}; + + +/** + * Creates a new instance of the dialog and registers for the relevant events. + * @param {goog.dom.DomHelper} dialogDomHelper The dom helper to be used to + * create the dialog. + * @param {*=} opt_link The target link (should be a goog.editor.Link). + * @return {!goog.ui.editor.LinkDialog} The dialog. + * @override + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.createDialog = function( + dialogDomHelper, opt_link) { + var dialog = new goog.ui.editor.LinkDialog(dialogDomHelper, + /** @type {goog.editor.Link} */ (opt_link)); + if (this.emailWarning_) { + dialog.setEmailWarning(this.emailWarning_); + } + if (this.showOpenLinkInNewWindow_) { + dialog.showOpenLinkInNewWindow(this.isOpenLinkInNewWindowChecked_); + } + if (this.showRelNoFollow_) { + dialog.showRelNoFollow(); + } + dialog.setStopReferrerLeaks(this.stopReferrerLeaks_); + this.eventHandler_. + listen(dialog, goog.ui.editor.AbstractDialog.EventType.OK, + this.handleOk). + listen(dialog, goog.ui.editor.AbstractDialog.EventType.CANCEL, + this.handleCancel_). + listen(dialog, goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK, + this.handleBeforeTestLink); + return dialog; +}; + + +/** @override */ +goog.editor.plugins.LinkDialogPlugin.prototype.disposeInternal = function() { + goog.editor.plugins.LinkDialogPlugin.base(this, 'disposeInternal'); + this.eventHandler_.dispose(); +}; + + +/** + * Handles the OK event from the dialog by updating the link in the field. + * @param {goog.ui.editor.LinkDialog.OkEvent} e OK event object. + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.handleOk = function(e) { + // We're not restoring the original selection, so clear it out. + this.disposeOriginalSelection(); + + this.currentLink_.setTextAndUrl(e.linkText, e.linkUrl); + if (this.showOpenLinkInNewWindow_) { + // Save checkbox state for next time. + this.isOpenLinkInNewWindowChecked_ = e.openInNewWindow; + } + + var anchor = this.currentLink_.getAnchor(); + this.touchUpAnchorOnOk_(anchor, e); + var extraAnchors = this.currentLink_.getExtraAnchors(); + for (var i = 0; i < extraAnchors.length; ++i) { + extraAnchors[i].href = anchor.href; + this.touchUpAnchorOnOk_(extraAnchors[i], e); + } + + // Place cursor to the right of the modified link. + this.currentLink_.placeCursorRightOf(); + + this.getFieldObject().focus(); + + this.getFieldObject().dispatchSelectionChangeEvent(); + this.getFieldObject().dispatchChange(); + + this.eventHandler_.removeAll(); +}; + + +/** + * Apply the necessary properties to a link upon Ok being clicked in the dialog. + * @param {HTMLAnchorElement} anchor The anchor to set properties on. + * @param {goog.events.Event} e Event object. + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.touchUpAnchorOnOk_ = + function(anchor, e) { + if (this.showOpenLinkInNewWindow_) { + if (e.openInNewWindow) { + anchor.target = '_blank'; + } else { + if (anchor.target == '_blank') { + anchor.target = ''; + } + // If user didn't indicate to open in a new window but the link already + // had a target other than '_blank', let's leave what they had before. + } + } + + if (this.showRelNoFollow_) { + var alreadyPresent = goog.ui.editor.LinkDialog.hasNoFollow(anchor.rel); + if (alreadyPresent && !e.noFollow) { + anchor.rel = goog.ui.editor.LinkDialog.removeNoFollow(anchor.rel); + } else if (!alreadyPresent && e.noFollow) { + anchor.rel = anchor.rel ? anchor.rel + ' nofollow' : 'nofollow'; + } + } +}; + + +/** + * Handles the CANCEL event from the dialog by clearing the anchor if needed. + * @param {goog.events.Event} e Event object. + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.handleCancel_ = function(e) { + if (this.currentLink_.isNew()) { + goog.dom.flattenElement(this.currentLink_.getAnchor()); + var extraAnchors = this.currentLink_.getExtraAnchors(); + for (var i = 0; i < extraAnchors.length; ++i) { + goog.dom.flattenElement(extraAnchors[i]); + } + // Make sure listeners know the anchor was flattened out. + this.getFieldObject().dispatchChange(); + } + + this.eventHandler_.removeAll(); +}; + + +/** + * Handles the BeforeTestLink event fired when the 'test' link is clicked. + * @param {goog.ui.editor.LinkDialog.BeforeTestLinkEvent} e BeforeTestLink event + * object. + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.handleBeforeTestLink = + function(e) { + if (!this.shouldOpenUrl(e.url)) { + /** @desc Message when the user tries to test (preview) a link, but the + * link cannot be tested. */ + var MSG_UNSAFE_LINK = goog.getMsg('This link cannot be tested.'); + alert(MSG_UNSAFE_LINK); + e.preventDefault(); + } +}; + + +/** + * Checks whether the plugin should open the given url in a new window. + * @param {string} url The url to check. + * @return {boolean} If the plugin should open the given url in a new window. + * @protected + */ +goog.editor.plugins.LinkDialogPlugin.prototype.shouldOpenUrl = function(url) { + return !this.blockOpeningUnsafeSchemes_ || this.isSafeSchemeToOpen_(url); +}; + + +/** + * Determines whether or not a url has a scheme which is safe to open. + * Schemes like javascript are unsafe due to the possibility of XSS. + * @param {string} url A url. + * @return {boolean} Whether the url has a safe scheme. + * @private + */ +goog.editor.plugins.LinkDialogPlugin.prototype.isSafeSchemeToOpen_ = + function(url) { + var scheme = goog.uri.utils.getScheme(url) || 'http'; + return goog.array.contains(this.safeToOpenSchemes_, scheme.toLowerCase()); +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.html new file mode 100644 index 0000000000..d8962fbccb --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.html @@ -0,0 +1,29 @@ + + + + + + + + goog.editor.plugins.LinkDialogPlugin Tests + + + + + + + + +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.js new file mode 100644 index 0000000000..b7a2e87553 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkdialogplugin_test.js @@ -0,0 +1,749 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.ui.editor.plugins.LinkDialogTest'); +goog.setTestOnly('goog.ui.editor.plugins.LinkDialogTest'); + +goog.require('goog.dom'); +goog.require('goog.dom.DomHelper'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.Link'); +goog.require('goog.editor.plugins.LinkDialogPlugin'); +goog.require('goog.string'); +goog.require('goog.string.Unicode'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.editor.dom'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.mockmatchers'); +goog.require('goog.ui.editor.AbstractDialog'); +goog.require('goog.ui.editor.LinkDialog'); +goog.require('goog.userAgent'); + +var plugin; +var anchorElem; +var extraAnchors; +var isNew; +var testDiv; + +var mockCtrl; +var mockField; +var mockLink; +var mockAlert; + +var OLD_LINK_TEXT = 'old text'; +var OLD_LINK_URL = 'http://old.url/'; +var NEW_LINK_TEXT = 'My Link Text'; +var NEW_LINK_URL = 'http://my.link/url/'; + +var fieldElem; +var fieldObj; +var linkObj; + +function setUp() { + testDiv = goog.dom.getDocument().getElementById('test'); + testDiv.innerHTML = 'Some preceeding text'; + + anchorElem = goog.dom.createElement(goog.dom.TagName.A); + anchorElem.href = 'http://www.google.com/'; + anchorElem.innerHTML = 'anchor text'; + goog.dom.appendChild(testDiv, anchorElem); + extraAnchors = []; + + mockCtrl = new goog.testing.MockControl(); + mockField = new goog.testing.editor.FieldMock(); + mockCtrl.addMock(mockField); + mockLink = mockCtrl.createLooseMock(goog.editor.Link); + mockAlert = mockCtrl.createGlobalFunctionMock('alert'); + + isNew = false; + mockLink.isNew().$anyTimes().$does(function() { + return isNew; + }); + mockLink. + setTextAndUrl(goog.testing.mockmatchers.isString, + goog.testing.mockmatchers.isString). + $anyTimes(). + $does(function(text, url) { + anchorElem.innerHTML = text; + anchorElem.href = url; + }); + mockLink.getAnchor().$anyTimes().$returns(anchorElem); + mockLink.getExtraAnchors().$anyTimes().$returns(extraAnchors); +} + +function tearDown() { + plugin.dispose(); + tearDownRealEditableField(); + testDiv.innerHTML = ''; + mockCtrl.$tearDown(); +} + +function setUpAnchor(text, href, opt_isNew, opt_target, opt_rel) { + setUpGivenAnchor(anchorElem, text, href, opt_isNew, opt_target, opt_rel); +} + +function setUpGivenAnchor( + anchor, text, href, opt_isNew, opt_target, opt_rel) { + anchor.innerHTML = text; + anchor.href = href; + isNew = !!opt_isNew; + if (opt_target) { + anchor.target = opt_target; + } + if (opt_rel) { + anchor.rel = opt_rel; + } +} + + +/** + * Tests that the plugin's dialog is properly created. + */ +function testCreateDialog() { + // Note: this tests simply creating the dialog because that's the only + // functionality added to this class. Opening or closing effects (editing + // the actual link) is tested in linkdialog_test.html, but should be moved + // here if that functionality gets refactored from the dialog to the plugin. + mockCtrl.$replayAll(); + + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + assertTrue('Dialog should be of type goog.ui.editor.LinkDialog', + dialog instanceof goog.ui.editor.LinkDialog); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that when the OK event fires the link is properly updated. + */ +function testOk() { + mockLink.placeCursorRightOf(); + mockField.dispatchSelectionChangeEvent(); + mockField.dispatchChange(); + mockField.focus(); + mockCtrl.$replayAll(); + + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL); + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + clicking OK without actually opening the dialog. + plugin.currentLink_ = mockLink; + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent(NEW_LINK_TEXT, + NEW_LINK_URL)); + + assertEquals('Display text incorrect', + NEW_LINK_TEXT, + anchorElem.innerHTML); + assertEquals('Anchor element href incorrect', + NEW_LINK_URL, + anchorElem.href); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that when the Cancel event fires the link is unchanged. + */ +function testCancel() { + mockCtrl.$replayAll(); + + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL); + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + cancel without actually opening the dialog. + plugin.currentLink_ = mockLink; + dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL); + + assertEquals('Display text should not be changed', + OLD_LINK_TEXT, + anchorElem.innerHTML); + assertEquals('Anchor element href should not be changed', + OLD_LINK_URL, + anchorElem.href); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that when the Cancel event fires for a new link it gets removed. + */ +function testCancelNew() { + mockField.dispatchChange(); // Should be fired because link was removed. + mockCtrl.$replayAll(); + + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, true); + var prevSib = anchorElem.previousSibling; + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + cancel without actually opening the dialog. + plugin.currentLink_ = mockLink; + dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL); + + assertNotEquals('Anchor element should be removed from document body', + testDiv, + anchorElem.parentNode); + var newElem = prevSib.nextSibling; + assertEquals('Link should be replaced by text node', + goog.dom.NodeType.TEXT, + newElem.nodeType); + assertEquals('Original text should be left behind', + OLD_LINK_TEXT, + newElem.nodeValue); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that when the Cancel event fires for a new link it gets removed. + */ +function testCancelNewMultiple() { + mockField.dispatchChange(); // Should be fired because link was removed. + mockCtrl.$replayAll(); + + var anchorElem1 = anchorElem; + var parent1 = goog.dom.createDom(goog.dom.TagName.DIV, null, + anchorElem1); + goog.dom.appendChild(testDiv, parent1); + setUpGivenAnchor(anchorElem1, OLD_LINK_TEXT + '1', OLD_LINK_URL + '1', + true); + + anchorElem2 = goog.dom.createDom(goog.dom.TagName.A); + var parent2 = goog.dom.createDom(goog.dom.TagName.DIV, null, + anchorElem2); + goog.dom.appendChild(testDiv, parent2); + setUpGivenAnchor(anchorElem2, OLD_LINK_TEXT + '2', OLD_LINK_URL + '2', + true); + extraAnchors.push(anchorElem2); + + anchorElem3 = goog.dom.createDom(goog.dom.TagName.A); + var parent3 = goog.dom.createDom(goog.dom.TagName.DIV, null, + anchorElem3); + goog.dom.appendChild(testDiv, parent3); + setUpGivenAnchor(anchorElem3, OLD_LINK_TEXT + '3', OLD_LINK_URL + '3', + true); + extraAnchors.push(anchorElem3); + + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + cancel without actually opening the dialog. + plugin.currentLink_ = mockLink; + dialog.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL); + + assertNotEquals('Anchor 1 element should be removed from document body', + parent1, + anchorElem1.parentNode); + assertNotEquals('Anchor 2 element should be removed from document body', + parent2, + anchorElem2.parentNode); + assertNotEquals('Anchor 3 element should be removed from document body', + parent3, + anchorElem3.parentNode); + + assertEquals('Link 1 should be replaced by text node', + goog.dom.NodeType.TEXT, + parent1.firstChild.nodeType); + assertEquals('Link 2 should be replaced by text node', + goog.dom.NodeType.TEXT, + parent2.firstChild.nodeType); + assertEquals('Link 3 should be replaced by text node', + goog.dom.NodeType.TEXT, + parent3.firstChild.nodeType); + + assertEquals('Original text 1 should be left behind', + OLD_LINK_TEXT + '1', + parent1.firstChild.nodeValue); + assertEquals('Original text 2 should be left behind', + OLD_LINK_TEXT + '2', + parent2.firstChild.nodeValue); + assertEquals('Original text 3 should be left behind', + OLD_LINK_TEXT + '3', + parent3.firstChild.nodeValue); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that when the Cancel event fires for a new link it gets removed. + */ +function testOkNewMultiple() { + mockLink.placeCursorRightOf(); + mockField.dispatchSelectionChangeEvent(); + mockField.dispatchChange(); + mockField.focus(); + mockCtrl.$replayAll(); + + var anchorElem1 = anchorElem; + setUpGivenAnchor(anchorElem1, OLD_LINK_TEXT + '1', OLD_LINK_URL + '1', + true); + + anchorElem2 = goog.dom.createElement(goog.dom.TagName.A); + goog.dom.appendChild(testDiv, anchorElem2); + setUpGivenAnchor(anchorElem2, OLD_LINK_TEXT + '2', OLD_LINK_URL + '2', + true); + extraAnchors.push(anchorElem2); + + anchorElem3 = goog.dom.createElement(goog.dom.TagName.A); + goog.dom.appendChild(testDiv, anchorElem3); + setUpGivenAnchor(anchorElem3, OLD_LINK_TEXT + '3', OLD_LINK_URL + '3', + true); + extraAnchors.push(anchorElem3); + + var prevSib = anchorElem1.previousSibling; + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + clicking OK without actually opening the dialog. + plugin.currentLink_ = mockLink; + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent(NEW_LINK_TEXT, + NEW_LINK_URL)); + + assertEquals('Display text 1 must update', NEW_LINK_TEXT, + anchorElem1.innerHTML); + assertEquals('Display text 2 must not update', OLD_LINK_TEXT + '2', + anchorElem2.innerHTML); + assertEquals('Display text 3 must not update', OLD_LINK_TEXT + '3', + anchorElem3.innerHTML); + + assertEquals('Anchor element 1 href must update', NEW_LINK_URL, + anchorElem1.href); + assertEquals('Anchor element 2 href must update', NEW_LINK_URL, + anchorElem2.href); + assertEquals('Anchor element 3 href must update', NEW_LINK_URL, + anchorElem3.href); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests the anchor's target is correctly modified with the "open in new + * window" feature on. + */ +function testOkOpenInNewWindow() { + mockLink.placeCursorRightOf().$anyTimes(); + mockField.dispatchSelectionChangeEvent().$anyTimes(); + mockField.dispatchChange().$anyTimes(); + mockField.focus().$anyTimes(); + mockCtrl.$replayAll(); + + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + plugin.showOpenLinkInNewWindow(false); + plugin.currentLink_ = mockLink; + + // Edit a link that doesn't open in a new window and leave it as such. + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent( + NEW_LINK_TEXT, NEW_LINK_URL, false, false)); + assertEquals( + 'Target should not be set for link that doesn\'t open in new window', + '', anchorElem.target); + assertFalse('Checked state should stay false', + plugin.getOpenLinkInNewWindowCheckedState()); + + // Edit a link that doesn't open in a new window and toggle it on. + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL); + dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent( + NEW_LINK_TEXT, NEW_LINK_URL, true)); + assertEquals( + 'Target should be set to _blank for link that opens in new window', + '_blank', anchorElem.target); + assertTrue('Checked state should be true after toggling a link on', + plugin.getOpenLinkInNewWindowCheckedState()); + + // Edit a link that doesn't open in a named window and don't touch it. + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, false, 'named'); + dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent( + NEW_LINK_TEXT, NEW_LINK_URL, false)); + assertEquals( + 'Target should keep its original value', + 'named', anchorElem.target); + assertFalse('Checked state should be false again', + plugin.getOpenLinkInNewWindowCheckedState()); + + // Edit a link that opens in a new window and toggle it off. + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, false, '_blank'); + dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent( + NEW_LINK_TEXT, NEW_LINK_URL, false)); + assertEquals( + 'Target should not be set for link that doesn\'t open in new window', + '', anchorElem.target); + + mockCtrl.$verifyAll(); +} + +function testOkNoFollowEnabled() { + verifyRelNoFollow(true, null, 'nofollow'); +} + +function testOkNoFollowInUppercase() { + verifyRelNoFollow(true, 'NOFOLLOW', 'NOFOLLOW'); +} + +function testOkNoFollowEnabledHasMoreRelValues() { + verifyRelNoFollow(true, 'author', 'author nofollow'); +} + +function testOkNoFollowDisabled() { + verifyRelNoFollow(false, null, ''); +} + +function testOkNoFollowDisabledHasMoreRelValues() { + verifyRelNoFollow(false, 'author', 'author'); +} + +function testOkNoFollowDisabledHasMoreRelValues() { + verifyRelNoFollow(false, 'author nofollow', 'author '); +} + +function testOkNoFollowInUppercaseWithMoreValues() { + verifyRelNoFollow(true, 'NOFOLLOW author', 'NOFOLLOW author'); +} + +function verifyRelNoFollow(noFollow, originalRel, expectedRel) { + mockLink.placeCursorRightOf(); + mockField.dispatchSelectionChangeEvent(); + mockField.dispatchChange(); + mockField.focus(); + mockCtrl.$replayAll(); + + plugin = new goog.editor.plugins.LinkDialogPlugin(); + plugin.registerFieldObject(mockField); + plugin.showRelNoFollow(); + plugin.currentLink_ = mockLink; + + setUpAnchor(OLD_LINK_TEXT, OLD_LINK_URL, true, null, originalRel); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + dialog.dispatchEvent(new goog.ui.editor.LinkDialog.OkEvent( + NEW_LINK_TEXT, NEW_LINK_URL, false, noFollow)); + assertEquals(expectedRel, anchorElem.rel); + + mockCtrl.$verifyAll(); +} + + +/** + * Tests that the selection is cleared when the dialog opens and is + * correctly restored after cancel is clicked. + */ +function testRestoreSelectionOnOk() { + setUpAnchor('12345', '/'); + setUpRealEditableField(); + + var elem = fieldObj.getElement(); + var helper = new goog.testing.editor.TestHelper(elem); + helper.select('12345', 1, '12345', 4); // Selects '234'. + + assertEquals('Incorrect text selected before dialog is opened', + '234', + fieldObj.getRange().getText()); + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + if (!goog.userAgent.IE && !goog.userAgent.OPERA) { + // IE returns some bogus range when field doesn't have selection. + // You can't remove the selection from a whitebox field in Opera. + assertNull('There should be no selection while dialog is open', + fieldObj.getRange()); + } + goog.testing.events.fireClickSequence( + plugin.dialog_.getOkButtonElement()); + assertEquals('No text should be selected after clicking ok', + '', + fieldObj.getRange().getText()); + + // Test that the caret is placed at the end of the link text. + goog.testing.editor.dom.assertRangeBetweenText( + // If the browser gets stuck in links, an nbsp was added after the link + // to avoid that, otherwise we just look for the 5. + goog.editor.BrowserFeature.GETS_STUCK_IN_LINKS ? + goog.string.Unicode.NBSP : '5', + '', + fieldObj.getRange()); + + // NOTE(user): The functionality to avoid getting stuck in links is + // tested in editablelink_test.html::testPlaceCursorRightOf(). +} + + +/** + * Tests that the selection is cleared when the dialog opens and is + * correctly restored after cancel is clicked. + * @param {boolean=} opt_isNew Whether to test behavior when creating a new + * link (cancelling will flatten it). + */ +function testRestoreSelectionOnCancel(opt_isNew) { + setUpAnchor('12345', '/', opt_isNew); + setUpRealEditableField(); + + var elem = fieldObj.getElement(); + var helper = new goog.testing.editor.TestHelper(elem); + helper.select('12345', 1, '12345', 4); // Selects '234'. + + assertEquals('Incorrect text selected before dialog is opened', + '234', + fieldObj.getRange().getText()); + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + if (!goog.userAgent.IE && !goog.userAgent.OPERA) { + // IE returns some bogus range when field doesn't have selection. + // You can't remove the selection from a whitebox field in Opera. + assertNull('There should be no selection while dialog is open', + fieldObj.getRange()); + } + goog.testing.events.fireClickSequence( + plugin.dialog_.getCancelButtonElement()); + assertEquals('Incorrect text selected after clicking cancel', + '234', + fieldObj.getRange().getText()); +} + + +/** + * Tests that the selection is cleared when the dialog opens and is + * correctly restored after cancel is clicked and the new link is removed. + */ +function testRestoreSelectionOnCancelNew() { + testRestoreSelectionOnCancel(true); +} + + +/** + * Tests that the BeforeTestLink event is suppressed for invalid url schemes. + */ +function testTestLinkDisabledForInvalidScheme() { + mockAlert(goog.testing.mockmatchers.isString); + mockCtrl.$replayAll(); + + var invalidUrl = 'javascript:document.write(\'hello\');'; + + plugin = new goog.editor.plugins.LinkDialogPlugin(); + var dialog = plugin.createDialog(new goog.dom.DomHelper(), mockLink); + + // Mock of execCommand + clicking test without actually opening the dialog. + var dispatched = dialog.dispatchEvent( + new goog.ui.editor.LinkDialog.BeforeTestLinkEvent(invalidUrl)); + + assertFalse(dispatched); + mockCtrl.$verifyAll(); +} + +function testIsSafeSchemeToOpen() { + plugin = new goog.editor.plugins.LinkDialogPlugin(); + // Urls with no scheme at all are ok too since 'http://' will be prepended. + var good = [ + 'http://google.com', 'http://google.com/', 'https://google.com', + 'null@google.com', 'http://www.google.com', 'http://site.com', + 'google.com', 'google', 'http://google', 'HTTP://GOOGLE.COM', + 'HtTp://www.google.com' + ]; + + var bad = [ + 'javascript:google.com', 'httpp://google.com', 'data:foo', + 'javascript:alert(\'hi\');', 'abc:def' + ]; + + for (var i = 0; i < good.length; i++) { + assertTrue(good[i] + ' should have a safe scheme', + plugin.isSafeSchemeToOpen_(good[i])); + } + + for (i = 0; i < bad.length; i++) { + assertFalse(bad[i] + ' should have an unsafe scheme', + plugin.isSafeSchemeToOpen_(bad[i])); + } +} + +function testShouldOpenWithWhitelist() { + plugin.setSafeToOpenSchemes(['abc']); + + assertTrue('Scheme should be safe', + plugin.shouldOpenUrl('abc://google.com')); + assertFalse('Scheme should be unsafe', + plugin.shouldOpenUrl('http://google.com')); + + plugin.setBlockOpeningUnsafeSchemes(false); + assertTrue('Non-whitelisted should now be safe after disabling blocking', + plugin.shouldOpenUrl('http://google.com')); +} + + +/** + * Regression test for http://b/issue?id=1607766 . Without the fix, this + * should give an Invalid Argument error in IE, because the editable field + * caches a selection util that has a reference to the node of the link text + * before it is edited (which gets replaced by a new node for the new text + * after editing). + */ +function testBug1607766() { + setUpAnchor('abc', 'def'); + setUpRealEditableField(); + + var elem = fieldObj.getElement(); + var helper = new goog.testing.editor.TestHelper(elem); + helper.select('abc', 1, 'abc', 2); // Selects 'b'. + // Dispatching a selection event causes the field to cache a selection + // util, which is the root of the bug. + plugin.fieldObject.dispatchSelectionChangeEvent(); + + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value = 'Abc'; + goog.testing.events.fireClickSequence(plugin.dialog_.getOkButtonElement()); + + // In IE the unit test somehow doesn't cause a browser focus event, so we + // need to manually invoke this, which is where the bug happens. + plugin.fieldObject.dispatchFocus_(); +} + + +/** + * Regression test for http://b/issue?id=2215546 . + */ +function testBug2215546() { + setUpRealEditableField(); + + var elem = fieldObj.getElement(); + fieldObj.setHtml(false, '
    '); + anchorElem = elem.firstChild.firstChild; + linkObj = new goog.editor.Link(anchorElem, true); + + var helper = new goog.testing.editor.TestHelper(elem); + // Select "" in a way, simulating what IE does if you hit enter twice, + // arrow up into the blank line and open the link dialog. + helper.select(anchorElem, 0, elem.firstChild, 1); + + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value = 'foo'; + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo'; + var okButton = plugin.dialog_.getOkButtonElement(); + okButton.disabled = false; + goog.testing.events.fireClickSequence(okButton); + + assertEquals('Link text should have been inserted', + 'foo', anchorElem.innerHTML); +} + + +/** + * Test that link insertion doesn't scroll the field to the top + * after clicking Cancel or OK. + */ +function testBug7279077ScrollOnFocus() { + if (goog.userAgent.IE) { + return; // TODO(user): take this out once b/7279077 fixed for IE too. + } + setUpAnchor('12345', '/'); + setUpRealEditableField(); + + // Make the field scrollable and kinda small. + var elem = fieldObj.getElement(); + elem.style.overflow = 'auto'; + elem.style.height = '40px'; + elem.style.width = '200px'; + elem.style.contenteditable = 'true'; + + // Add a bunch of text before the anchor tag. + var longTextElem = document.createElement('span'); + longTextElem.innerHTML = goog.string.repeat('All work and no play.

    ', 20); + elem.insertBefore(longTextElem, elem.firstChild); + + var helper = new goog.testing.editor.TestHelper(elem); + helper.select('12345', 1, '12345', 4); // Selects '234'. + + // Scroll down. + elem.scrollTop = 60; + + // Bring up the link insertion dialog, then cancel. + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value = 'foo'; + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo'; + var cancelButton = plugin.dialog_.getCancelButtonElement(); + goog.testing.events.fireClickSequence(cancelButton); + + assertEquals('Field should not have scrolled after cancel', + 60, elem.scrollTop); + + // Now let's try it with clicking the OK button. + plugin.execCommand(goog.editor.Command.MODAL_LINK_EDITOR, linkObj); + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY).value = 'foo'; + goog.dom.getElement( + goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value = 'foo'; + var okButton = plugin.dialog_.getOkButtonElement(); + goog.testing.events.fireClickSequence(okButton); + + assertEquals('Field should not have scrolled after OK', + 60, elem.scrollTop); +} + + +/** + * Setup a real editable field (instead of a mock) and register the plugin to + * it. + */ +function setUpRealEditableField() { + fieldElem = document.createElement('div'); + fieldElem.id = 'myField'; + document.body.appendChild(fieldElem); + fieldElem.appendChild(anchorElem); + fieldObj = new goog.editor.Field('myField', document); + fieldObj.makeEditable(); + linkObj = new goog.editor.Link(fieldObj.getElement().firstChild, isNew); + // Register the plugin to that field. + plugin = new goog.editor.plugins.LinkDialogPlugin(); + fieldObj.registerPlugin(plugin); +} + + +/** + * Tear down the real editable field. + */ +function tearDownRealEditableField() { + if (fieldObj) { + fieldObj.makeUneditable(); + fieldObj.dispose(); + fieldObj = null; + } + goog.dom.removeNode(fieldElem); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin.js new file mode 100644 index 0000000000..724fb3e678 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin.js @@ -0,0 +1,62 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Adds a keyboard shortcut for the link command. + * + */ + +goog.provide('goog.editor.plugins.LinkShortcutPlugin'); + +goog.require('goog.editor.Command'); +goog.require('goog.editor.Plugin'); + + + +/** + * Plugin to add a keyboard shortcut for the link command + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.LinkShortcutPlugin = function() { + goog.editor.plugins.LinkShortcutPlugin.base(this, 'constructor'); +}; +goog.inherits(goog.editor.plugins.LinkShortcutPlugin, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.LinkShortcutPlugin.prototype.getTrogClassId = function() { + return 'LinkShortcutPlugin'; +}; + + +/** + * @override + */ +goog.editor.plugins.LinkShortcutPlugin.prototype.handleKeyboardShortcut = + function(e, key, isModifierPressed) { + var command; + if (isModifierPressed && key == 'k' && !e.shiftKey) { + var link = /** @type {goog.editor.Link?} */ ( + this.getFieldObject().execCommand(goog.editor.Command.LINK)); + if (link) { + link.finishLinkCreation(this.getFieldObject()); + } + return true; + } + + return false; +}; + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.html new file mode 100644 index 0000000000..af5c004911 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.html @@ -0,0 +1,27 @@ + + + + + + + goog.editor.plugins.LinkShortcutPlugin Tests + + + + + +

    +
    + http://www.google.com/ +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.js new file mode 100644 index 0000000000..273ef508de --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/linkshortcutplugin_test.js @@ -0,0 +1,65 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.LinkShortcutPluginTest'); +goog.setTestOnly('goog.editor.plugins.LinkShortcutPluginTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.BasicTextFormatter'); +goog.require('goog.editor.plugins.LinkBubble'); +goog.require('goog.editor.plugins.LinkShortcutPlugin'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.dom'); +goog.require('goog.testing.events'); +goog.require('goog.testing.jsunit'); + +var propertyReplacer; + +function setUp() { + propertyReplacer = new goog.testing.PropertyReplacer(); +} + +function tearDown() { + propertyReplacer.reset(); + var field = document.getElementById('cleanup'); + goog.dom.removeChildren(field); + field.innerHTML = '
    http://www.google.com/
    '; +} + +function testShortcutCreatesALink() { + propertyReplacer.set(window, 'prompt', function() { + return 'http://www.google.com/'; }); + var linkBubble = new goog.editor.plugins.LinkBubble(); + var formatter = new goog.editor.plugins.BasicTextFormatter(); + var plugin = new goog.editor.plugins.LinkShortcutPlugin(); + var fieldEl = document.getElementById('field'); + var field = new goog.editor.Field('field'); + field.registerPlugin(formatter); + field.registerPlugin(linkBubble); + field.registerPlugin(plugin); + field.makeEditable(); + field.focusAndPlaceCursorAtStart(); + var textNode = goog.testing.dom.findTextNode('http://www.google.com/', + fieldEl); + goog.testing.events.fireKeySequence( + field.getElement(), goog.events.KeyCodes.K, { ctrlKey: true }); + + var href = field.getElement().getElementsByTagName('A')[0]; + assertEquals('http://www.google.com/', href.href); + var bubbleLink = + document.getElementById(goog.editor.plugins.LinkBubble.TEST_LINK_ID_); + assertEquals('http://www.google.com/', bubbleLink.innerHTML); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler.js new file mode 100644 index 0000000000..03f78ca3d9 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler.js @@ -0,0 +1,68 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Editor plugin to handle tab keys in lists to indent and + * outdent. + * + * @author robbyw@google.com (Robby Walker) + */ + +goog.provide('goog.editor.plugins.ListTabHandler'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.plugins.AbstractTabHandler'); +goog.require('goog.iter'); + + + +/** + * Plugin to handle tab keys in lists to indent and outdent. + * @constructor + * @extends {goog.editor.plugins.AbstractTabHandler} + * @final + */ +goog.editor.plugins.ListTabHandler = function() { + goog.editor.plugins.AbstractTabHandler.call(this); +}; +goog.inherits(goog.editor.plugins.ListTabHandler, + goog.editor.plugins.AbstractTabHandler); + + +/** @override */ +goog.editor.plugins.ListTabHandler.prototype.getTrogClassId = function() { + return 'ListTabHandler'; +}; + + +/** @override */ +goog.editor.plugins.ListTabHandler.prototype.handleTabKey = function(e) { + var range = this.getFieldObject().getRange(); + if (goog.dom.getAncestorByTagNameAndClass(range.getContainerElement(), + goog.dom.TagName.LI) || + goog.iter.some(range, function(node) { + return node.tagName == goog.dom.TagName.LI; + })) { + this.getFieldObject().execCommand(e.shiftKey ? + goog.editor.Command.OUTDENT : + goog.editor.Command.INDENT); + e.preventDefault(); + return true; + } + + return false; +}; + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.html new file mode 100644 index 0000000000..90abef4803 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.html @@ -0,0 +1,30 @@ + + + + + + + + Closure Unit Tests - goog.editor.plugins.ListTabHandler + + + + + + +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.js new file mode 100644 index 0000000000..394a82ccd5 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/listtabhandler_test.js @@ -0,0 +1,167 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.ListTabHandlerTest'); +goog.setTestOnly('goog.editor.plugins.ListTabHandlerTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.plugins.ListTabHandler'); +goog.require('goog.events.BrowserEvent'); +goog.require('goog.events.KeyCodes'); +goog.require('goog.functions'); +goog.require('goog.testing.StrictMock'); +goog.require('goog.testing.editor.FieldMock'); +goog.require('goog.testing.editor.TestHelper'); +goog.require('goog.testing.jsunit'); + +var field; +var editableField; +var tabHandler; +var testHelper; + +function setUpPage() { + field = goog.dom.getElement('field'); +} + +function setUp() { + editableField = new goog.testing.editor.FieldMock(); + // Modal mode behavior tested as part of AbstractTabHandler tests. + editableField.inModalMode = goog.functions.FALSE; + + tabHandler = new goog.editor.plugins.ListTabHandler(); + tabHandler.registerFieldObject(editableField); + + testHelper = new goog.testing.editor.TestHelper(field); + testHelper.setUpEditableElement(); +} + +function tearDown() { + editableField = null; + testHelper.tearDownEditableElement(); + tabHandler.dispose(); +} + +function testListIndentInLi() { + field.innerHTML = '
    • Text
    '; + + var testText = field.firstChild.firstChild.firstChild; // div ul li Test + testHelper.select(testText, 0, testText, 4); + + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.shiftKey = false; + + editableField.execCommand(goog.editor.Command.INDENT); + event.preventDefault(); + + editableField.$replay(); + event.$replay(); + + assertTrue('Event must be handled', + tabHandler.handleKeyboardShortcut(event, '', false)); + + editableField.$verify(); + event.$verify(); +} + +function testListIndentContainLi() { + field.innerHTML = '
    • Text
    '; + + var testText = field.firstChild.firstChild.firstChild; // div ul li Test + testHelper.select(field.firstChild, 0, testText, 4); + + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.shiftKey = false; + + editableField.execCommand(goog.editor.Command.INDENT); + event.preventDefault(); + + editableField.$replay(); + event.$replay(); + + assertTrue('Event must be handled', + tabHandler.handleKeyboardShortcut(event, '', false)); + + editableField.$verify(); + event.$verify(); +} + +function testListOutdentInLi() { + field.innerHTML = '
    • Text
    '; + + var testText = field.firstChild.firstChild.firstChild; // div ul li Test + testHelper.select(testText, 0, testText, 4); + + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.shiftKey = true; + + editableField.execCommand(goog.editor.Command.OUTDENT); + event.preventDefault(); + + editableField.$replay(); + event.$replay(); + + assertTrue('Event must be handled', + tabHandler.handleKeyboardShortcut(event, '', false)); + + editableField.$verify(); + event.$verify(); +} + +function testListOutdentContainLi() { + field.innerHTML = '
    • Text
    '; + + var testText = field.firstChild.firstChild.firstChild; // div ul li Test + testHelper.select(field.firstChild, 0, testText, 4); + + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.shiftKey = true; + + editableField.execCommand(goog.editor.Command.OUTDENT); + event.preventDefault(); + + editableField.$replay(); + event.$replay(); + + assertTrue('Event must be handled', + tabHandler.handleKeyboardShortcut(event, '', false)); + + editableField.$verify(); + event.$verify(); +} + + +function testNoOp() { + field.innerHTML = 'Text'; + + var testText = field.firstChild; + testHelper.select(testText, 0, testText, 4); + + var event = new goog.testing.StrictMock(goog.events.BrowserEvent); + event.keyCode = goog.events.KeyCodes.TAB; + event.shiftKey = true; + + editableField.$replay(); + event.$replay(); + + assertFalse('Event must not be handled', + tabHandler.handleKeyboardShortcut(event, '', false)); + + editableField.$verify(); + event.$verify(); +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum.js new file mode 100644 index 0000000000..90ba31dc94 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum.js @@ -0,0 +1,192 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A plugin that fills the field with lorem ipsum text when it's + * empty and does not have the focus. Applies to both editable and uneditable + * fields. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.editor.plugins.LoremIpsum'); + +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.node'); +goog.require('goog.functions'); +goog.require('goog.userAgent'); + + + +/** + * A plugin that manages lorem ipsum state of editable fields. + * @param {string} message The lorem ipsum message. + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.LoremIpsum = function(message) { + goog.editor.Plugin.call(this); + + /** + * The lorem ipsum message. + * @type {string} + * @private + */ + this.message_ = message; +}; +goog.inherits(goog.editor.plugins.LoremIpsum, goog.editor.Plugin); + + +/** @override */ +goog.editor.plugins.LoremIpsum.prototype.getTrogClassId = + goog.functions.constant('LoremIpsum'); + + +/** @override */ +goog.editor.plugins.LoremIpsum.prototype.activeOnUneditableFields = + goog.functions.TRUE; + + +/** + * Whether the field is currently filled with lorem ipsum text. + * @type {boolean} + * @private + */ +goog.editor.plugins.LoremIpsum.prototype.usingLorem_ = false; + + +/** + * Handles queryCommandValue. + * @param {string} command The command to query. + * @return {boolean} The result. + * @override + */ +goog.editor.plugins.LoremIpsum.prototype.queryCommandValue = function(command) { + return command == goog.editor.Command.USING_LOREM && this.usingLorem_; +}; + + +/** + * Handles execCommand. + * @param {string} command The command to execute. + * Should be CLEAR_LOREM or UPDATE_LOREM. + * @param {*=} opt_placeCursor Whether to place the cursor in the field + * after clearing lorem. Should be a boolean. + * @override + */ +goog.editor.plugins.LoremIpsum.prototype.execCommand = function(command, + opt_placeCursor) { + if (command == goog.editor.Command.CLEAR_LOREM) { + this.clearLorem_(!!opt_placeCursor); + } else if (command == goog.editor.Command.UPDATE_LOREM) { + this.updateLorem_(); + } +}; + + +/** @override */ +goog.editor.plugins.LoremIpsum.prototype.isSupportedCommand = + function(command) { + return command == goog.editor.Command.CLEAR_LOREM || + command == goog.editor.Command.UPDATE_LOREM || + command == goog.editor.Command.USING_LOREM; +}; + + +/** + * Set the lorem ipsum text in a goog.editor.Field if needed. + * @private + */ +goog.editor.plugins.LoremIpsum.prototype.updateLorem_ = function() { + // Try to apply lorem ipsum if: + // 1) We have lorem ipsum text + // 2) There's not a dialog open, as that screws + // with the dialog's ability to properly restore the selection + // on dialog close (since the DOM nodes would get clobbered in FF) + // 3) We're not using lorem already + // 4) The field is not currently active (doesn't have focus). + var fieldObj = this.getFieldObject(); + if (!this.usingLorem_ && + !fieldObj.inModalMode() && + goog.editor.Field.getActiveFieldId() != fieldObj.id) { + var field = fieldObj.getElement(); + if (!field) { + // Fallback on the original element. This is needed by + // fields managed by click-to-edit. + field = fieldObj.getOriginalElement(); + } + + goog.asserts.assert(field); + if (goog.editor.node.isEmpty(field)) { + this.usingLorem_ = true; + + // Save the old font style so it can be restored when we + // clear the lorem ipsum style. + this.oldFontStyle_ = field.style.fontStyle; + field.style.fontStyle = 'italic'; + fieldObj.setHtml(true, this.message_, true); + } + } +}; + + +/** + * Clear an EditableField's lorem ipsum and put in initial text if needed. + * + * If using click-to-edit mode (where Trogedit manages whether the field + * is editable), this works for both editable and uneditable fields. + * + * TODO(user): Is this really necessary? See TODO below. + * @param {boolean=} opt_placeCursor Whether to place the cursor in the field + * after clearing lorem. + * @private + */ +goog.editor.plugins.LoremIpsum.prototype.clearLorem_ = function( + opt_placeCursor) { + // Don't mess with lorem state when a dialog is open as that screws + // with the dialog's ability to properly restore the selection + // on dialog close (since the DOM nodes would get clobbered) + var fieldObj = this.getFieldObject(); + if (this.usingLorem_ && !fieldObj.inModalMode()) { + var field = fieldObj.getElement(); + if (!field) { + // Fallback on the original element. This is needed by + // fields managed by click-to-edit. + field = fieldObj.getOriginalElement(); + } + + goog.asserts.assert(field); + this.usingLorem_ = false; + field.style.fontStyle = this.oldFontStyle_; + fieldObj.setHtml(true, null, true); + + // TODO(nicksantos): I'm pretty sure that this is a hack, but talk to + // Julie about why this is necessary and what to do with it. Really, + // we need to figure out where it's necessary and remove it where it's + // not. Safari never places the cursor on its own willpower. + if (opt_placeCursor && fieldObj.isLoaded()) { + if (goog.userAgent.WEBKIT) { + goog.dom.getOwnerDocument(fieldObj.getElement()).body.focus(); + fieldObj.focusAndPlaceCursorAtStart(); + } else if (goog.userAgent.OPERA) { + fieldObj.placeCursorAtStart(); + } + } + } +}; diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.html new file mode 100644 index 0000000000..5e68271b17 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.html @@ -0,0 +1,31 @@ + + + + + + + + goog.editor.plugins.LoremIpsum Tests + + + + + +
    +
    +
    +
    + + diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.js new file mode 100644 index 0000000000..ce2aa55d4b --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/loremipsum_test.js @@ -0,0 +1,156 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +goog.provide('goog.editor.plugins.LoremIpsumTest'); +goog.setTestOnly('goog.editor.plugins.LoremIpsumTest'); + +goog.require('goog.dom'); +goog.require('goog.editor.Command'); +goog.require('goog.editor.Field'); +goog.require('goog.editor.plugins.LoremIpsum'); +goog.require('goog.string.Unicode'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); + +var FIELD; +var PLUGIN; +var HTML; +var UPPERCASE_CONTENTS = '

    THE OWLS ARE NOT WHAT THEY SEEM.

    '; + +function setUp() { + HTML = goog.dom.getElement('root').innerHTML; + + FIELD = new goog.editor.Field('field'); + + PLUGIN = new goog.editor.plugins.LoremIpsum( + 'The owls are not what they seem.'); + FIELD.registerPlugin(PLUGIN); +} + +function tearDown() { + FIELD.dispose(); + goog.dom.getElement('root').innerHTML = HTML; +} + +function testQueryUsingLorem() { + FIELD.makeEditable(); + + assertTrue(FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + FIELD.setHtml(true, 'fresh content', false, true); + assertFalse(FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); +} + +function testUpdateLoremIpsum() { + goog.dom.getElement('field').innerHTML = 'stuff'; + + var loremPlugin = FIELD.getPluginByClassId('LoremIpsum'); + FIELD.makeEditable(); + var content = '
    foo
    '; + + FIELD.setHtml(false, '', false, /* Don't update lorem */ false); + assertFalse('Field started with content, lorem must not be enabled.', + FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + FIELD.execCommand(goog.editor.Command.UPDATE_LOREM); + assertTrue('Field was set to empty, update must turn on lorem ipsum', + FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + + FIELD.unregisterPlugin(loremPlugin); + FIELD.setHtml(false, content, false, + /* Update (turn off) lorem */ true); + FIELD.setHtml(false, '', false, /* Don't update lorem */ false); + FIELD.execCommand(goog.editor.Command.UPDATE_LOREM); + assertFalse('Field with no lorem message must not use lorem ipsum', + FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + FIELD.registerPlugin(loremPlugin); + + FIELD.setHtml(false, content, false, true); + FIELD.setHtml(false, '', false, false); + goog.editor.Field.setActiveFieldId(FIELD.id); + FIELD.execCommand(goog.editor.Command.UPDATE_LOREM); + assertFalse('Active field must not use lorem ipsum', + FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + goog.editor.Field.setActiveFieldId(null); + + FIELD.setHtml(false, content, false, true); + FIELD.setHtml(false, '', false, false); + FIELD.setModalMode(true); + FIELD.execCommand(goog.editor.Command.UPDATE_LOREM); + assertFalse('Must not turn on lorem ipsum while a dialog is open.', + FIELD.queryCommandValue(goog.editor.Command.USING_LOREM)); + FIELD.setModalMode(true); + + FIELD.dispose(); +} + +function testLoremIpsumAndGetCleanContents() { + goog.dom.getElement('field').innerHTML = 'This is a field'; + FIELD.makeEditable(); + + // test direct getCleanContents + assertEquals('field reported wrong contents', 'This is a field', + FIELD.getCleanContents()); + + // test indirect getCleanContents + var contents = FIELD.getCleanContents(); + assertEquals('field reported wrong contents', 'This is a field', contents); + + // set field html, but explicitly forbid converting to lorem ipsum text + FIELD.setHtml(false, ' ', true, false /* no lorem */); + assertEquals('field contains unexpected contents', getNbsp(), + FIELD.getElement().innerHTML); + assertEquals('field reported wrong contents', getNbsp(), + FIELD.getCleanContents()); + + // now set field html allowing lorem + FIELD.setHtml(false, ' ', true, true /* lorem */); + assertEquals('field reported wrong contents', goog.string.Unicode.NBSP, + FIELD.getCleanContents()); + assertEquals('field contains unexpected contents', UPPERCASE_CONTENTS, + FIELD.getElement().innerHTML.toUpperCase()); +} + +function testLoremIpsumAndGetCleanContents2() { + // make a field blank before we make it editable, and then check + // that making it editable activates lorem. + assert('field is editable', FIELD.isUneditable()); + goog.dom.getElement('field').innerHTML = ' '; + + FIELD.makeEditable(); + assertEquals('field contains unexpected contents', + UPPERCASE_CONTENTS, FIELD.getElement().innerHTML.toUpperCase()); + + FIELD.makeUneditable(); + assertEquals('field contains unexpected contents', + UPPERCASE_CONTENTS, goog.dom.getElement('field').innerHTML.toUpperCase()); +} + +function testLoremIpsumInClickToEditMode() { + // in click-to-edit mode, trogedit manages the editable state of the editor, + // so we must manage lorem ipsum in uneditable mode too. + FIELD.makeEditable(); + + assertEquals('field contains unexpected contents', + UPPERCASE_CONTENTS, FIELD.getElement().innerHTML.toUpperCase()); + + FIELD.makeUneditable(); + assertEquals('field contains unexpected contents', + UPPERCASE_CONTENTS, goog.dom.getElement('field').innerHTML.toUpperCase()); +} + +function getNbsp() { + // On WebKit (pre-528) and Opera,   shows up as its unicode character in + // innerHTML under some circumstances. + return (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('528')) || + goog.userAgent.OPERA ? '\u00a0' : ' '; +} diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting.js new file mode 100644 index 0000000000..bbfb760416 --- /dev/null +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting.js @@ -0,0 +1,780 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// All Rights Reserved. + +/** + * @fileoverview Plugin to handle Remove Formatting. + * + */ + +goog.provide('goog.editor.plugins.RemoveFormatting'); + +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.Range'); +goog.require('goog.dom.TagName'); +goog.require('goog.editor.BrowserFeature'); +goog.require('goog.editor.Plugin'); +goog.require('goog.editor.node'); +goog.require('goog.editor.range'); +goog.require('goog.string'); +goog.require('goog.userAgent'); + + + +/** + * A plugin to handle removing formatting from selected text. + * @constructor + * @extends {goog.editor.Plugin} + * @final + */ +goog.editor.plugins.RemoveFormatting = function() { + goog.editor.Plugin.call(this); + + /** + * Optional function to perform remove formatting in place of the + * provided removeFormattingWorker_. + * @type {?function(string): string} + * @private + */ + this.optRemoveFormattingFunc_ = null; +}; +goog.inherits(goog.editor.plugins.RemoveFormatting, goog.editor.Plugin); + + +/** + * The editor command this plugin in handling. + * @type {string} + */ +goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND = + '+removeFormat'; + + +/** + * Regular expression that matches a block tag name. + * @type {RegExp} + * @private + */ +goog.editor.plugins.RemoveFormatting.BLOCK_RE_ = + /^(DIV|TR|LI|BLOCKQUOTE|H\d|PRE|XMP)/; + + +/** + * Appends a new line to a string buffer. + * @param {Array} sb The string buffer to add to. + * @private + */ +goog.editor.plugins.RemoveFormatting.appendNewline_ = function(sb) { + sb.push('
    '); +}; + + +/** + * Create a new range delimited by the start point of the first range and + * the end point of the second range. + * @param {goog.dom.AbstractRange} startRange Use the start point of this + * range as the beginning of the new range. + * @param {goog.dom.AbstractRange} endRange Use the end point of this + * range as the end of the new range. + * @return {!goog.dom.AbstractRange} The new range. + * @private + */ +goog.editor.plugins.RemoveFormatting.createRangeDelimitedByRanges_ = function( + startRange, endRange) { + return goog.dom.Range.createFromNodes( + startRange.getStartNode(), startRange.getStartOffset(), + endRange.getEndNode(), endRange.getEndOffset()); +}; + + +/** @override */ +goog.editor.plugins.RemoveFormatting.prototype.getTrogClassId = function() { + return 'RemoveFormatting'; +}; + + +/** @override */ +goog.editor.plugins.RemoveFormatting.prototype.isSupportedCommand = function( + command) { + return command == + goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND; +}; + + +/** @override */ +goog.editor.plugins.RemoveFormatting.prototype.execCommandInternal = + function(command, var_args) { + if (command == + goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND) { + this.removeFormatting_(); + } +}; + + +/** @override */ +goog.editor.plugins.RemoveFormatting.prototype.handleKeyboardShortcut = + function(e, key, isModifierPressed) { + if (!isModifierPressed) { + return false; + } + + if (key == ' ') { + this.getFieldObject().execCommand( + goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND); + return true; + } + + return false; +}; + + +/** + * Removes formatting from the current selection. Removes basic formatting + * (B/I/U) using the browser's execCommand. Then extracts the html from the + * selection to convert, calls either a client's specified removeFormattingFunc + * callback or trogedit's general built-in removeFormattingWorker_, + * and then replaces the current selection with the converted text. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.removeFormatting_ = function() { + var range = this.getFieldObject().getRange(); + if (range.isCollapsed()) { + return; + } + + // Get the html to format and send it off for formatting. Built in + // removeFormat only strips some inline elements and some inline CSS styles + var convFunc = this.optRemoveFormattingFunc_ || + goog.bind(this.removeFormattingWorker_, this); + this.convertSelectedHtmlText_(convFunc); + + // Do the execCommand last as it needs block elements removed to work + // properly on background/fontColor in FF. There are, unfortunately, still + // cases where background/fontColor are not removed here. + var doc = this.getFieldDomHelper().getDocument(); + doc.execCommand('RemoveFormat', false, undefined); + + if (goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT) { + // WebKit converts spaces to non-breaking spaces when doing a RemoveFormat. + // See: https://bugs.webkit.org/show_bug.cgi?id=14062 + this.convertSelectedHtmlText_(function(text) { + // This loses anything that might have legitimately been a non-breaking + // space, but that's better than the alternative of only having non- + // breaking spaces. + // Old versions of WebKit (Safari 3, Chrome 1) incorrectly match /u00A0 + // and newer versions properly match  . + var nbspRegExp = + goog.userAgent.isVersionOrHigher('528') ? / /g : /\u00A0/g; + return text.replace(nbspRegExp, ' '); + }); + } +}; + + +/** + * Finds the nearest ancestor of the node that is a table. + * @param {Node} nodeToCheck Node to search from. + * @return {Node} The table, or null if one was not found. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.getTableAncestor_ = function( + nodeToCheck) { + var fieldElement = this.getFieldObject().getElement(); + while (nodeToCheck && nodeToCheck != fieldElement) { + if (nodeToCheck.tagName == goog.dom.TagName.TABLE) { + return nodeToCheck; + } + nodeToCheck = nodeToCheck.parentNode; + } + return null; +}; + + +/** + * Replaces the contents of the selection with html. Does its best to maintain + * the original selection. Also does its best to result in a valid DOM. + * + * TODO(user): See if there's any way to make this work on Ranges, and then + * move it into goog.editor.range. The Firefox implementation uses execCommand + * on the document, so must work on the actual selection. + * + * @param {string} html The html string to insert into the range. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.pasteHtml_ = function(html) { + var range = this.getFieldObject().getRange(); + + var dh = this.getFieldDomHelper(); + // Use markers to set the extent of the selection so that we can reselect it + // afterwards. This works better than builtin range manipulation in FF and IE + // because their implementations are so self-inconsistent and buggy. + var startSpanId = goog.string.createUniqueString(); + var endSpanId = goog.string.createUniqueString(); + html = '' + html + + ''; + var dummyNodeId = goog.string.createUniqueString(); + var dummySpanText = ''; + + if (goog.editor.BrowserFeature.HAS_IE_RANGES) { + // IE's selection often doesn't include the outermost tags. + // We want to use pasteHTML to replace the range contents with the newly + // unformatted text, so we have to check to make sure we aren't just + // pasting into some stray tags. To do this, we first clear out the + // contents of the range and then delete all empty nodes parenting the now + // empty range. This way, the pasted contents are never re-embedded into + // formated nodes. Pasting purely empty html does not work, since IE moves + // the selection inside the next node, so we insert a dummy span. + var textRange = range.getTextRange(0).getBrowserRangeObject(); + textRange.pasteHTML(dummySpanText); + var parent; + while ((parent = textRange.parentElement()) && + goog.editor.node.isEmpty(parent) && + !goog.editor.node.isEditableContainer(parent)) { + var tag = parent.nodeName; + // We can't remove these table tags as it will invalidate the table dom. + if (tag == goog.dom.TagName.TD || + tag == goog.dom.TagName.TR || + tag == goog.dom.TagName.TH) { + break; + } + + goog.dom.removeNode(parent); + } + textRange.pasteHTML(html); + var dummySpan = dh.getElement(dummyNodeId); + // If we entered the while loop above, the node has already been removed + // since it was a child of parent and parent was removed. + if (dummySpan) { + goog.dom.removeNode(dummySpan); + } + } else if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + // insertHtml and range.insertNode don't merge blocks correctly. + // (e.g. if your selection spans two paragraphs) + dh.getDocument().execCommand('insertImage', false, dummyNodeId); + var dummyImageNodePattern = new RegExp('<[^<]*' + dummyNodeId + '[^>]*>'); + var parent = this.getFieldObject().getRange().getContainerElement(); + if (parent.nodeType == goog.dom.NodeType.TEXT) { + // Opera sometimes returns a text node here. + // TODO(user): perhaps we should modify getParentContainer? + parent = parent.parentNode; + } + + // We have to search up the DOM because in some cases, notably when + // selecting li's within a list, execCommand('insertImage') actually splits + // tags in such a way that parent that used to contain the selection does + // not contain inserted image. + while (!dummyImageNodePattern.test(parent.innerHTML)) { + parent = parent.parentNode; + } + + // Like the IE case above, sometimes the selection does not include the + // outermost tags. For Gecko, we have already expanded the range so that + // it does, so we can just replace the dummy image with the final html. + // For WebKit, we use the same approach as we do with IE - we + // inject a dummy span where we will eventually place the contents, and + // remove parentNodes of the span while they are empty. + + if (goog.userAgent.GECKO) { + goog.editor.node.replaceInnerHtml(parent, + parent.innerHTML.replace(dummyImageNodePattern, html)); + } else { + goog.editor.node.replaceInnerHtml(parent, + parent.innerHTML.replace(dummyImageNodePattern, dummySpanText)); + var dummySpan = dh.getElement(dummyNodeId); + parent = dummySpan; + while ((parent = dummySpan.parentNode) && + goog.editor.node.isEmpty(parent) && + !goog.editor.node.isEditableContainer(parent)) { + var tag = parent.nodeName; + // We can't remove these table tags as it will invalidate the table dom. + if (tag == goog.dom.TagName.TD || + tag == goog.dom.TagName.TR || + tag == goog.dom.TagName.TH) { + break; + } + + // We can't just remove parent since dummySpan is inside it, and we need + // to keep dummy span around for the replacement. So we move the + // dummySpan up as we go. + goog.dom.insertSiblingAfter(dummySpan, parent); + goog.dom.removeNode(parent); + } + goog.editor.node.replaceInnerHtml(parent, + parent.innerHTML.replace(new RegExp(dummySpanText, 'i'), html)); + } + } + + var startSpan = dh.getElement(startSpanId); + var endSpan = dh.getElement(endSpanId); + goog.dom.Range.createFromNodes(startSpan, 0, endSpan, + endSpan.childNodes.length).select(); + goog.dom.removeNode(startSpan); + goog.dom.removeNode(endSpan); +}; + + +/** + * Gets the html inside the selection to send off for further processing. + * + * TODO(user): Make this general so that it can be moved into + * goog.editor.range. The main reason it can't be moved is becuase we need to + * get the range before we do the execCommand and continue to operate on that + * same range (reasons are documented above). + * + * @param {goog.dom.AbstractRange} range The selection. + * @return {string} The html string to format. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.getHtmlText_ = function(range) { + var div = this.getFieldDomHelper().createDom('div'); + var textRange = range.getBrowserRangeObject(); + + if (goog.editor.BrowserFeature.HAS_W3C_RANGES) { + // Get the text to convert. + div.appendChild(textRange.cloneContents()); + } else if (goog.editor.BrowserFeature.HAS_IE_RANGES) { + // Trim the whitespace on the ends of the range, so that it the container + // will be the container of only the text content that we are changing. + // This gets around issues in IE where the spaces are included in the + // selection, but ignored sometimes by execCommand, and left orphaned. + var rngText = range.getText(); + + // BRs get reported as \r\n, but only count as one character for moves. + // Adjust the string so our move counter is correct. + rngText = rngText.replace(/\r\n/g, '\r'); + + var rngTextLength = rngText.length; + var left = rngTextLength - goog.string.trimLeft(rngText).length; + var right = rngTextLength - goog.string.trimRight(rngText).length; + + textRange.moveStart('character', left); + textRange.moveEnd('character', -right); + + var htmlText = textRange.htmlText; + // Check if in pretag and fix up formatting so that new lines are preserved. + if (textRange.queryCommandValue('formatBlock') == 'Formatted') { + htmlText = goog.string.newLineToBr(textRange.htmlText); + } + div.innerHTML = htmlText; + } + + // Get the innerHTML of the node instead of just returning the text above + // so that its properly html escaped. + return div.innerHTML; +}; + + +/** + * Move the range so that it doesn't include any partially selected tables. + * @param {goog.dom.AbstractRange} range The range to adjust. + * @param {Node} startInTable Table node that the range starts in. + * @param {Node} endInTable Table node that the range ends in. + * @return {!goog.dom.SavedCaretRange} Range to use to restore the + * selection after we run our custom remove formatting. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.adjustRangeForTables_ = + function(range, startInTable, endInTable) { + // Create placeholders for the current selection so we can restore it + // later. + var savedCaretRange = goog.editor.range.saveUsingNormalizedCarets(range); + + var startNode = range.getStartNode(); + var startOffset = range.getStartOffset(); + var endNode = range.getEndNode(); + var endOffset = range.getEndOffset(); + var dh = this.getFieldDomHelper(); + + // Move start after the table. + if (startInTable) { + var textNode = dh.createTextNode(''); + goog.dom.insertSiblingAfter(textNode, startInTable); + startNode = textNode; + startOffset = 0; + } + // Move end before the table. + if (endInTable) { + var textNode = dh.createTextNode(''); + goog.dom.insertSiblingBefore(textNode, endInTable); + endNode = textNode; + endOffset = 0; + } + + goog.dom.Range.createFromNodes(startNode, startOffset, + endNode, endOffset).select(); + + return savedCaretRange; +}; + + +/** + * Remove a caret from the dom and hide it in a safe place, so it can + * be restored later via restoreCaretsFromCave. + * @param {goog.dom.SavedCaretRange} caretRange The caret range to + * get the carets from. + * @param {boolean} isStart Whether this is the start or end caret. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.putCaretInCave_ = function( + caretRange, isStart) { + var cavedCaret = goog.dom.removeNode(caretRange.getCaret(isStart)); + if (isStart) { + this.startCaretInCave_ = cavedCaret; + } else { + this.endCaretInCave_ = cavedCaret; + } +}; + + +/** + * Restore carets that were hidden away by adding them back into the dom. + * Note: this does not restore to the original dom location, as that + * will likely have been modified with remove formatting. The only + * guarentees here are that start will still be before end, and that + * they will be in the editable region. This should only be used when + * you don't actually intend to USE the caret again. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.restoreCaretsFromCave_ = + function() { + // To keep start before end, we put the end caret at the bottom of the field + // and the start caret at the start of the field. + var field = this.getFieldObject().getElement(); + if (this.startCaretInCave_) { + field.insertBefore(this.startCaretInCave_, field.firstChild); + this.startCaretInCave_ = null; + } + if (this.endCaretInCave_) { + field.appendChild(this.endCaretInCave_); + this.endCaretInCave_ = null; + } +}; + + +/** + * Gets the html inside the current selection, passes it through the given + * conversion function, and puts it back into the selection. + * + * @param {function(string): string} convertFunc A conversion function that + * transforms an html string to new html string. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.convertSelectedHtmlText_ = + function(convertFunc) { + var range = this.getFieldObject().getRange(); + + // For multiple ranges, it is really hard to do our custom remove formatting + // without invalidating other ranges. So instead of always losing the + // content, this solution at least lets the browser do its own remove + // formatting which works correctly most of the time. + if (range.getTextRangeCount() > 1) { + return; + } + + if (goog.userAgent.GECKO) { + // Determine if we need to handle tables, since they are special cases. + // If the selection is entirely within a table, there is no extra + // formatting removal we can do. If a table is fully selected, we will + // just blow it away. If a table is only partially selected, we can + // perform custom remove formatting only on the non table parts, since we + // we can't just remove the parts and paste back into it (eg. we can't + // inject html where a TR used to be). + // If the selection contains the table and more, this is automatically + // handled, but if just the table is selected, it can be tricky to figure + // this case out, because of the numerous ways selections can be formed - + // ex. if a table has a single tr with a single td with a single text node + // in it, and the selection is (textNode: 0), (textNode: nextNode.length) + // then the entire table is selected, even though the start and end aren't + // the table itself. We are truly inside a table if the expanded endpoints + // are still inside the table. + + // Expand the selection to include any outermost tags that weren't included + // in the selection, but have the same visible selection. Stop expanding + // if we reach the top level field. + var expandedRange = goog.editor.range.expand(range, + this.getFieldObject().getElement()); + + var startInTable = this.getTableAncestor_(expandedRange.getStartNode()); + var endInTable = this.getTableAncestor_(expandedRange.getEndNode()); + + if (startInTable || endInTable) { + if (startInTable == endInTable) { + // We are fully contained in the same table, there is no extra + // remove formatting that we can do, just return and run browser + // formatting only. + return; + } + + // Adjust the range to not contain any partially selected tables, since + // we don't want to run our custom remove formatting on them. + var savedCaretRange = this.adjustRangeForTables_(range, + startInTable, endInTable); + + // Hack alert!! + // If start is not in a table, then the saved caret will get sent out + // for uber remove formatting, and it will get blown away. This is + // fine, except that we need to be able to re-create a range from the + // savedCaretRange later on. So, we just remove it from the dom, and + // put it back later so we can create a range later (not exactly in the + // same spot, but don't worry we don't actually try to use it later) + // and then it will be removed when we dispose the range. + if (!startInTable) { + this.putCaretInCave_(savedCaretRange, true); + } + if (!endInTable) { + this.putCaretInCave_(savedCaretRange, false); + } + + // Re-fetch the range, and re-expand it, since we just modified it. + range = this.getFieldObject().getRange(); + expandedRange = goog.editor.range.expand(range, + this.getFieldObject().getElement()); + } + + expandedRange.select(); + range = expandedRange; + } + + // Convert the selected text to the format-less version, paste back into + // the selection. + var text = this.getHtmlText_(range); + this.pasteHtml_(convertFunc(text)); + + if (goog.userAgent.GECKO && savedCaretRange) { + // If we moved the selection, move it back so the user can't tell we did + // anything crazy and so the browser removeFormat that we call next + // will operate on the entire originally selected range. + range = this.getFieldObject().getRange(); + this.restoreCaretsFromCave_(); + var realSavedCaretRange = savedCaretRange.toAbstractRange(); + var startRange = startInTable ? realSavedCaretRange : range; + var endRange = endInTable ? realSavedCaretRange : range; + var restoredRange = + goog.editor.plugins.RemoveFormatting.createRangeDelimitedByRanges_( + startRange, endRange); + restoredRange.select(); + savedCaretRange.dispose(); + } +}; + + +/** + * Does a best-effort attempt at clobbering all formatting that the + * browser's execCommand couldn't clobber without being totally inefficient. + * Attempts to convert visual line breaks to BRs. Leaves anchors that contain an + * href and images. + * Adapted from Gmail's MessageUtil's htmlToPlainText. http://go/messageutil.js + * @param {string} html The original html of the message. + * @return {string} The unformatted html, which is just text, br's, anchors and + * images. + * @private + */ +goog.editor.plugins.RemoveFormatting.prototype.removeFormattingWorker_ = + function(html) { + var el = goog.dom.createElement('div'); + el.innerHTML = html; + + // Put everything into a string buffer to avoid lots of expensive string + // concatenation along the way. + var sb = []; + var stack = [el.childNodes, 0]; + + // Keep separate stacks for places where we need to keep track of + // how deeply embedded we are. These are analogous to the general stack. + var preTagStack = []; + var preTagLevel = 0; // Length of the prestack. + var tableStack = []; + var tableLevel = 0; + + // sp = stack pointer, pointing to the stack array. + // decrement by 2 since the stack alternates node lists and + // processed node counts + for (var sp = 0; sp >= 0; sp -= 2) { + // Check if we should pop the table level. + var changedLevel = false; + while (tableLevel > 0 && sp <= tableStack[tableLevel - 1]) { + tableLevel--; + changedLevel = true; + } + if (changedLevel) { + goog.editor.plugins.RemoveFormatting.appendNewline_(sb); + } + + + // Check if we should pop the
    / level.
    +    changedLevel = false;
    +    while (preTagLevel > 0 && sp <= preTagStack[preTagLevel - 1]) {
    +      preTagLevel--;
    +      changedLevel = true;
    +    }
    +    if (changedLevel) {
    +      goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +    }
    +
    +    // The list of of nodes to process at the current stack level.
    +    var nodeList = stack[sp];
    +    // The number of nodes processed so far, stored in the stack immediately
    +    // following the node list for that stack level.
    +    var numNodesProcessed = stack[sp + 1];
    +
    +    while (numNodesProcessed < nodeList.length) {
    +      var node = nodeList[numNodesProcessed++];
    +      var nodeName = node.nodeName;
    +
    +      var formatted = this.getValueForNode(node);
    +      if (goog.isDefAndNotNull(formatted)) {
    +        sb.push(formatted);
    +        continue;
    +      }
    +
    +      // TODO(user): Handle case 'EMBED' and case 'OBJECT'.
    +      switch (nodeName) {
    +        case '#text':
    +          // Note that IE does not preserve whitespace in the dom
    +          // values, even in a pre tag, so this is useless for IE.
    +          var nodeValue = preTagLevel > 0 ?
    +              node.nodeValue :
    +              goog.string.stripNewlines(node.nodeValue);
    +          nodeValue = goog.string.htmlEscape(nodeValue);
    +          sb.push(nodeValue);
    +          continue;
    +
    +        case goog.dom.TagName.P:
    +          goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          break;  // break (not continue) so that child nodes are processed.
    +
    +        case goog.dom.TagName.BR:
    +          goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          continue;
    +
    +        case goog.dom.TagName.TABLE:
    +          goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          tableStack[tableLevel++] = sp;
    +          break;
    +
    +        case goog.dom.TagName.PRE:
    +        case 'XMP':
    +          // This doesn't fully handle xmp, since
    +          // it doesn't actually ignore tags within the xmp tag.
    +          preTagStack[preTagLevel++] = sp;
    +          break;
    +
    +        case goog.dom.TagName.STYLE:
    +        case goog.dom.TagName.SCRIPT:
    +        case goog.dom.TagName.SELECT:
    +          continue;
    +
    +        case goog.dom.TagName.A:
    +          if (node.href && node.href != '') {
    +            sb.push("<a href='");
    +            sb.push(node.href);
    +            sb.push("'>");
    +            sb.push(this.removeFormattingWorker_(node.innerHTML));
    +            sb.push('</a>');
    +            continue; // Children taken care of.
    +          } else {
    +            break; // Take care of the children.
    +          }
    +
    +        case goog.dom.TagName.IMG:
    +          sb.push("<img src='");
    +          sb.push(node.src);
    +          sb.push("'");
    +          // border=0 is a common way to not show a blue border around an image
    +          // that is wrapped by a link. If we remove that, the blue border will
    +          // show up, which to the user looks like adding format, not removing.
    +          if (node.border == '0') {
    +            sb.push(" border='0'");
    +          }
    +          sb.push('>');
    +          continue;
    +
    +        case goog.dom.TagName.TD:
    +          // Don't add a space for the first TD, we only want spaces to
    +          // separate td's.
    +          if (node.previousSibling) {
    +            sb.push(' ');
    +          }
    +          break;
    +
    +        case goog.dom.TagName.TR:
    +          // Don't add a newline for the first TR.
    +          if (node.previousSibling) {
    +            goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          }
    +          break;
    +
    +        case goog.dom.TagName.DIV:
    +          var parent = node.parentNode;
    +          if (parent.firstChild == node &&
    +              goog.editor.plugins.RemoveFormatting.BLOCK_RE_.test(
    +                  parent.tagName)) {
    +            // If a DIV is the first child of another element that itself is a
    +            // block element, the DIV does not add a new line.
    +            break;
    +          }
    +          // Otherwise, the DIV does add a new line.  Fall through.
    +
    +        default:
    +          if (goog.editor.plugins.RemoveFormatting.BLOCK_RE_.test(nodeName)) {
    +            goog.editor.plugins.RemoveFormatting.appendNewline_(sb);
    +          }
    +      }
    +
    +      // Recurse down the node.
    +      var children = node.childNodes;
    +      if (children.length > 0) {
    +        // Push the current state on the stack.
    +        stack[sp++] = nodeList;
    +        stack[sp++] = numNodesProcessed;
    +
    +        // Iterate through the children nodes.
    +        nodeList = children;
    +        numNodesProcessed = 0;
    +      }
    +    }
    +  }
    +
    +  // Replace &nbsp; with white space.
    +  return goog.string.normalizeSpaces(sb.join(''));
    +};
    +
    +
    +/**
    + * Handle per node special processing if neccessary. If this function returns
    + * null then standard cleanup is applied. Otherwise this node and all children
    + * are assumed to be cleaned.
    + * NOTE(user): If an alternate RemoveFormatting processor is provided
    + * (setRemoveFormattingFunc()), this will no longer work.
    + * @param {Element} node The node to clean.
    + * @return {?string} The HTML strig representation of the cleaned data.
    + */
    +goog.editor.plugins.RemoveFormatting.prototype.getValueForNode = function(
    +    node) {
    +  return null;
    +};
    +
    +
    +/**
    + * Sets a function to be used for remove formatting.
    + * @param {function(string): string} removeFormattingFunc - A function that
    + *     takes  a string of html and returns a string of html that does any other
    + *     formatting changes desired.  Use this only if trogedit's behavior doesn't
    + *     meet your needs.
    + */
    +goog.editor.plugins.RemoveFormatting.prototype.setRemoveFormattingFunc =
    +    function(removeFormattingFunc) {
    +  this.optRemoveFormattingFunc_ = removeFormattingFunc;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.html
    new file mode 100644
    index 0000000000..fc34c961be
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.html
    @@ -0,0 +1,42 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <!--
    +This test has not yet been updated to run on IE8. See http://b/hotlist?id=36311
    +-->
    +  <!--meta http-equiv="X-UA-Compatible" content="IE=edge"-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    +  <title>
    +   goog.editor.plugins.RemoveFormatting Tests
    +  </title>
    +  <script type="text/javascript" src="../../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.editor.plugins.RemoveFormattingTest');
    +  </script>
    + </head>
    + <body>
    +  <!--
    +This wrapper table is outside the mock editor and only used ensure that it is
    +ignored by the tests.
    +-->
    +  <table>
    +   <tr>
    +    <td>
    +     <div id="html">
    +     </div>
    +    </td>
    +   </tr>
    +  </table>
    +  <div id="abcde">abcde</div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.js
    new file mode 100644
    index 0000000000..d0db24d194
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/removeformatting_test.js
    @@ -0,0 +1,955 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.RemoveFormattingTest');
    +goog.setTestOnly('goog.editor.plugins.RemoveFormattingTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.plugins.RemoveFormatting');
    +goog.require('goog.string');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.editor.FieldMock');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var SAVED_HTML;
    +var FIELDMOCK;
    +var FORMATTER;
    +var testHelper;
    +var WEBKIT_BEFORE_CHROME_8;
    +var WEBKIT_AFTER_CHROME_16;
    +var WEBKIT_AFTER_CHROME_21;
    +var insertImageBoldGarbage = '';
    +var insertImageFontGarbage = '';
    +var controlHtml;
    +var controlCleanHtml;
    +var expectedFailures;
    +
    +function setUpPage() {
    +  WEBKIT_BEFORE_CHROME_8 = goog.userAgent.WEBKIT &&
    +      !goog.userAgent.isVersionOrHigher('534.10');
    +
    +  WEBKIT_AFTER_CHROME_16 = goog.userAgent.WEBKIT &&
    +      goog.userAgent.isVersionOrHigher('535.7');
    +
    +  WEBKIT_AFTER_CHROME_21 = goog.userAgent.WEBKIT &&
    +      goog.userAgent.isVersionOrHigher('537.1');
    +  // On Chrome 16, execCommand('insertImage') inserts a garbage BR
    +  // after the image that we insert. We use this command to paste HTML
    +  // in-place, because it has better paragraph-preserving semantics.
    +  //
    +  // TODO(nicksantos): Figure out if there are better chrome APIs that we
    +  // should be using, or if insertImage should just be fixed.
    +  if (WEBKIT_AFTER_CHROME_21) {
    +    insertImageBoldGarbage = '<br>';
    +    insertImageFontGarbage = '<br>';
    +  } else if (WEBKIT_AFTER_CHROME_16) {
    +    insertImageBoldGarbage = '<b><br/></b>';
    +    insertImageFontGarbage = '<font size="1"><br/></font>';
    +  }
    +  // Extra html to add to test html to make sure removeformatting is actually
    +  // getting called when you're testing if it leaves certain styles alone
    +  // (instead of not even running at all due to some other bug). However, adding
    +  // this extra text into the node to be selected screws up IE.
    +  // (e.g. <a><img></a><b>t</b> --> <a></a><a><img></a>t )
    +  // TODO(user): Remove this special casing once http://b/3131117 is
    +  // fixed.
    +  controlHtml = goog.userAgent.IE ? '' : '<u>control</u>';
    +  controlCleanHtml = goog.userAgent.IE ? '' : 'control';
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  testHelper = new goog.testing.editor.TestHelper(
    +      document.getElementById('html'));
    +  testHelper.setUpEditableElement();
    +
    +  FIELDMOCK = new goog.testing.editor.FieldMock();
    +  FIELDMOCK.getElement();
    +  FIELDMOCK.$anyTimes();
    +  FIELDMOCK.$returns(document.getElementById('html'));
    +
    +  FORMATTER = new goog.editor.plugins.RemoveFormatting();
    +  FORMATTER.fieldObject = FIELDMOCK;
    +
    +  FIELDMOCK.$replay();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  testHelper.tearDownEditableElement();
    +}
    +
    +function setUpTableTests() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<table><tr> <th> head1</th><th id= "outerTh">' +
    +      '<span id="emptyTh">head2</span></th> </tr><tr> <td> one </td> <td>' +
    +      'two </td> </tr><tr><td> three</td><td id="outerTd"> ' +
    +      '<span id="emptyTd"><strong>four</strong></span></td></tr>' +
    +      '<tr id="outerTr"><td><span id="emptyTr"> five </span></td></tr>' +
    +      '<tr id="outerTr2"><td id="cell1"><b>seven</b></td><td id="cell2">' +
    +      '<u>eight</u><span id="cellspan2"> foo</span></td></tr></table>';
    +}
    +
    +function testTableTagsAreNotRemoved() {
    +  setUpTableTests();
    +  var span;
    +
    +  // TD
    +  span = document.getElementById('emptyTd');
    +  goog.dom.Range.createFromNodeContents(span).select();
    +  FORMATTER.removeFormatting_();
    +
    +  var elem = document.getElementById('outerTd');
    +  assert('TD should not be removed', !!elem);
    +  if (!goog.userAgent.WEBKIT) {
    +    // webkit seems to have an Apple-style-span
    +    assertEquals('TD should be clean', 'four',
    +        goog.string.trim(elem.innerHTML));
    +  }
    +
    +  // TR
    +  span = document.getElementById('outerTr');
    +  goog.dom.Range.createFromNodeContents(span).select();
    +  FORMATTER.removeFormatting_();
    +
    +  var elem = document.getElementById('outerTr');
    +  assert('TR should not be removed', !!elem);
    +
    +  // TH
    +  span = document.getElementById('emptyTh');
    +  goog.dom.Range.createFromNodeContents(span).select();
    +  FORMATTER.removeFormatting_();
    +
    +  var elem = document.getElementById('outerTh');
    +  assert('TH should not be removed', !!elem);
    +  if (!goog.userAgent.WEBKIT) {
    +    // webkit seems to have an Apple-style-span
    +    assertEquals('TH should be clean', 'head2', elem.innerHTML);
    +  }
    +
    +}
    +
    +
    +/**
    + * We select two cells from the table and then make sure that there is no
    + * data loss and basic formatting is removed from each cell.
    + */
    +function testTableDataIsNotRemoved() {
    +  setUpTableTests();
    +  if (goog.userAgent.IE) {
    +    // IE returns an "unspecified error" which seems to be beyond
    +    // ExpectedFailures' ability to catch.
    +    return;
    +  }
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'The content moves out of the table in WEBKIT.');
    +
    +  if (goog.userAgent.IE) {
    +    // Not used since we bail out early for IE, but this is there so that
    +    // developers can easily reproduce IE error.
    +    goog.dom.Range.createFromNodeContents(
    +        document.getElementById('outerTr2')).select();
    +  } else {
    +    var selection = window.getSelection();
    +    if (selection.rangeCount > 0) selection.removeAllRanges();
    +    var range = document.createRange();
    +    range.selectNode(document.getElementById('cell1'));
    +    selection.addRange(range);
    +    range = document.createRange();
    +    range.selectNode(document.getElementById('cell2'));
    +    selection.addRange(range);
    +  }
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +
    +    span = document.getElementById('outerTr2');
    +    assertEquals('Table data should not be removed',
    +        '<td id="cell1">seven</td><td id="cell2">eight foo</td>',
    +        span.innerHTML);
    +  });
    +}
    +
    +function testLinksAreNotRemoved() {
    +  expectedFailures.expectFailureFor(WEBKIT_BEFORE_CHROME_8,
    +      'WebKit\'s removeFormatting command removes links.');
    +
    +  var anchor;
    +  var div = document.getElementById('html');
    +  div.innerHTML = 'Foo<span id="link">Pre<a href="http://www.google.com">' +
    +      'Outside Span<span style="font-size:15pt">Inside Span' +
    +      '</span></a></span>';
    +
    +  anchor = document.getElementById('link');
    +  goog.dom.Range.createFromNodeContents(anchor).select();
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('link should not be removed',
    +        'FooPre<a href="http://www.google.com/">Outside SpanInside Span</a>',
    +        div.innerHTML);
    +  });
    +}
    +
    +
    +/**
    + * A short formatting removal function for use with the RemoveFormatting
    + * plugin. Does enough that we can tell this function was run over the
    + * document.
    + * @param {string} text The HTML in from the document.
    + * @return {string} The "cleaned" HTML out.
    + */
    +function replacementFormattingFunc(text) {
    +  // Really basic so that we can just see this is executing.
    +  return text.replace(/Foo/gi, 'Bar').replace(/<[\/]*span[^>]*>/gi, '');
    +}
    +
    +function testAlternateRemoveFormattingFunction() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = 'Start<span id="remFormat">Foo<pre>Bar</pre>Baz</span>';
    +
    +  FORMATTER.setRemoveFormattingFunc(replacementFormattingFunc);
    +  var area = document.getElementById('remFormat');
    +  goog.dom.Range.createFromNodeContents(area).select();
    +  FORMATTER.removeFormatting_();
    +  // Webkit will change all tags to non-formatted ones anyway.
    +  // Make sure 'Foo' was changed to 'Bar'
    +  if (WEBKIT_BEFORE_CHROME_8) {
    +    assertHTMLEquals('regular cleaner should not have run',
    +        'StartBar<br>Bar<br>Baz',
    +        div.innerHTML);
    +  } else {
    +    assertHTMLEquals('regular cleaner should not have run',
    +        'StartBar<pre>Bar</pre>Baz',
    +        div.innerHTML);
    +  }
    +}
    +
    +function testGetValueForNode() {
    +  // Override getValueForNode to keep bold tags.
    +  var oldGetValue =
    +      goog.editor.plugins.RemoveFormatting.prototype.getValueForNode;
    +  goog.editor.plugins.RemoveFormatting.prototype.getValueForNode =
    +      function(node) {
    +    if (node.nodeName == goog.dom.TagName.B) {
    +      return '<b>' + this.removeFormattingWorker_(node.innerHTML) + '</b>';
    +    }
    +    return null;
    +  };
    +
    +  var html = FORMATTER.removeFormattingWorker_('<div>foo<b>bar</b></div>');
    +  assertHTMLEquals('B tags should remain', 'foo<b>bar</b>', html);
    +
    +  // Override getValueForNode to throw out bold tags, and their contents.
    +  goog.editor.plugins.RemoveFormatting.prototype.getValueForNode =
    +      function(node) {
    +    if (node.nodeName == goog.dom.TagName.B) {
    +      return '';
    +    }
    +    return null;
    +  };
    +
    +  html = FORMATTER.removeFormattingWorker_('<div>foo<b>bar</b></div>');
    +  assertHTMLEquals('B tag and its contents should be removed', 'foo', html);
    +
    +  FIELDMOCK.$verify();
    +  goog.editor.plugins.RemoveFormatting.prototype.getValueForNode =
    +      oldGetValue;
    +}
    +
    +function testRemoveFormattingAddsNoNbsps() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '"<span id="toStrip">Twin <b>Cinema</b></span>"';
    +
    +  var span = document.getElementById('toStrip');
    +  goog.dom.Range.createFromNodeContents(span).select();
    +
    +  FORMATTER.removeFormatting_();
    +
    +  assertEquals('Text should be the same, with no non-breaking spaces',
    +      '"Twin Cinema"', div.innerHTML);
    +
    +  FIELDMOCK.$verify();
    +}
    +
    +
    +/**
    + * @bug 992795
    + */
    +function testRemoveFormattingNestedDivs() {
    +  var html = FORMATTER.removeFormattingWorker_(
    +      '<div>1</div><div><div>2</div></div>');
    +
    +  goog.testing.dom.assertHtmlMatches('1<br>2', html);
    +}
    +
    +
    +/**
    + * Test that when we perform remove formatting on an entire table,
    + * that the visual look is similiar to as if there was a table there.
    + */
    +function testRemoveFormattingForTableFormatting() {
    +  // We preserve the table formatting as much as possible.
    +  // Spaces separate TD's, <br>'s separate TR's.
    +  // <br>'s separate the start and end of a table.
    +  var html = '<table><tr><td>cell00</td><td>cell01</td></tr>' +
    +      '<tr><td>cell10</td><td>cell11</td></tr></table>';
    +  html = FORMATTER.removeFormattingWorker_(html);
    +  assertHTMLEquals('<br>cell00 cell01<br>cell10 cell11<br>', html);
    +}
    +
    +
    +/**
    + * @bug 1319715
    + */
    +function testRemoveFormattingDoesNotShrinkSelection() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<div>l </div><div><br><b>a</b>foo bar</div>';
    +  var div2 = div.lastChild;
    +
    +  goog.dom.Range.createFromNodes(div2.firstChild, 0,
    +      div2.lastChild, 7).select();
    +
    +  FORMATTER.removeFormatting_();
    +
    +  var range = goog.dom.Range.createFromWindow();
    +  assertEquals('Correct text should be selected', 'afoo bar',
    +      range.getText());
    +
    +  // We have to trim out the leading BR in IE due to execCommand issues,
    +  // so it isn't sent off to the removeFormattingWorker.
    +  // Workaround for broken removeFormat in old webkit added an extra
    +  // <br> to the end of the html.
    +  var html = '<div>l </div><br class="GECKO WEBKIT">afoo bar' +
    +      (goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT ? '<br>' : '');
    +
    +  goog.testing.dom.assertHtmlContentsMatch(html, div);
    +  FIELDMOCK.$verify();
    +}
    +
    +
    +/**
    + *  @bug 1447374
    + */
    +function testInsideListRemoveFormat() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<ul><li>one</li><li><b>two</b></li><li>three</li></ul>';
    +
    +  var twoLi = div.firstChild.childNodes[1];
    +  goog.dom.Range.createFromNodeContents(twoLi).select();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'IE adds the "two" to the "three" li, and leaves empty B tags.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'WebKit leave the "two" orphaned outside of an li but ' +
    +      'inside the ul (invalid HTML).');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    // Test that we split the list.
    +    assertHTMLEquals('<ul><li>one</li></ul><br>two<ul><li>three</li></ul>',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testFullListRemoveFormat() {
    +  var div = document.getElementById('html');
    +  div.innerHTML =
    +      '<ul><li>one</li><li><b>two</b></li><li>three</li></ul>after';
    +
    +  goog.dom.Range.createFromNodeContents(div.firstChild).select();
    +
    +  //  Note: This may just be a createFromNodeContents issue, as
    +  //  I can't ever make this happen with real user selection.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'IE combines everything into a single LI and leaves the UL.');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    // Test that we completely remove the list.
    +    assertHTMLEquals('<br>one<br>two<br>threeafter',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + *  @bug 1440935
    + */
    +function testPartialListRemoveFormat() {
    +  var div = document.getElementById('html');
    +  div.innerHTML =
    +      '<ul><li>one</li><li>two</li><li>three</li></ul>after';
    +
    +  // Select "two three after".
    +  goog.dom.Range.createFromNodes(div.firstChild.childNodes[1], 0,
    +      div.lastChild, 5).select();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'IE leaves behind an empty LI.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'WebKit completely loses the "one".');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    // Test that we leave the list start alone.
    +    assertHTMLEquals('<ul><li>one</li></ul><br>two<br>threeafter',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testBasicRemoveFormatting() {
    +  // IE will clobber the editable div.
    +  // Note: I can't repro this using normal user selections.
    +  if (goog.userAgent.IE) {
    +    return;
    +  }
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<b>bold<i>italic</i></b>';
    +
    +  goog.dom.Range.createFromNodeContents(div).select();
    +
    +  expectedFailures.expectFailureFor(
    +      goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT,
    +      'The workaround for the nbsp bug adds an extra br at the end.');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('bolditalic' + insertImageBoldGarbage,
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * @bug 1480260
    + */
    +function testPartialBasicRemoveFormatting() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<b>bold<i>italic</i></b>';
    +
    +  goog.dom.Range.createFromNodes(div.firstChild.firstChild, 2,
    +      div.firstChild.lastChild.firstChild, 3).select();
    +
    +  expectedFailures.expectFailureFor(WEBKIT_BEFORE_CHROME_8,
    +      'WebKit just gets this all wrong.  Everything stays bold and ' +
    +      '"lditalic" gets italicised.');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('<b>bo</b>ldita<b><i>lic</i></b>',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * @bug 3075557
    + */
    +function testRemoveFormattingLinkedImageBorderZero() {
    +  var testHtml = '<a href="http://www.google.com/">' +
    +      '<img src="http://www.google.com/images/logo.gif" border="0"></a>';
    +  var div = document.getElementById('html');
    +  div.innerHTML = testHtml + controlHtml;
    +  goog.dom.Range.createFromNodeContents(div).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'WebKit removes the image entirely, see ' +
    +      'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
    +
    +  expectedFailures.run(function() {
    +    assertHTMLEquals(
    +        'Image\'s border=0 should not be removed during remove formatting',
    +        testHtml + controlCleanHtml, div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * @bug 3075557
    + */
    +function testRemoveFormattingLinkedImageBorderNonzero() {
    +  var testHtml = '<a href="http://www.google.com/">' +
    +      '<img src="http://www.google.com/images/logo.gif" border="1"></a>';
    +  var div = document.getElementById('html');
    +  div.innerHTML = testHtml + controlHtml;
    +  goog.dom.Range.createFromNodeContents(div).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'WebKit removes the image entirely, see ' +
    +      'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
    +
    +  expectedFailures.run(function() {
    +    assertHTMLEquals(
    +        'Image\'s border should be removed during remove formatting' +
    +        ' if non-zero',
    +        testHtml.replace(' border="1"', '') + controlCleanHtml,
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * @bug 3075557
    + */
    +function testRemoveFormattingUnlinkedImage() {
    +  var testHtml =
    +      '<img src="http://www.google.com/images/logo.gif" border="0">';
    +  var div = document.getElementById('html');
    +  div.innerHTML = testHtml + controlHtml;
    +  goog.dom.Range.createFromNodeContents(div).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'WebKit removes the image entirely, see ' +
    +      'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
    +
    +  expectedFailures.run(function() {
    +    assertHTMLEquals(
    +        'Image\'s border=0 should not be removed during remove formatting' +
    +        ' even if not wrapped by a link',
    +        testHtml + controlCleanHtml, div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * @bug 3075557
    + */
    +function testRemoveFormattingLinkedImageDeep() {
    +  var testHtml = '<a href="http://www.google.com/"><b>hello' +
    +      '<img src="http://www.google.com/images/logo.gif" border="0">' +
    +      'world</b></a>';
    +  var div = document.getElementById('html');
    +  div.innerHTML = testHtml + controlHtml;
    +  goog.dom.Range.createFromNodeContents(div).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(WEBKIT_BEFORE_CHROME_8,
    +      'WebKit removes the image entirely, see ' +
    +      'https://bugs.webkit.org/show_bug.cgi?id=13125 .');
    +
    +  expectedFailures.run(function() {
    +    assertHTMLEquals(
    +        'Image\'s border=0 should not be removed during remove formatting' +
    +        ' even if deep inside anchor tag',
    +        testHtml.replace(/<\/?b>/g, '') +
    +        controlCleanHtml + insertImageBoldGarbage,
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testFullTableRemoveFormatting() {
    +  // Something goes horrible wrong in case 1 below.  It was crashing all
    +  // WebKit browsers, and now seems to be giving errors as it is trying
    +  // to perform remove formatting on the little expected failures window
    +  // instead of the dom we select.  WTF.  Since I'm gutting this code,
    +  // I'm not going to look into this anymore right now.  For what its worth,
    +  // I can't repro any issues in standalone TrogEdit.
    +  if (goog.userAgent.WEBKIT) {
    +    return;
    +  }
    +
    +  var div = document.getElementById('html');
    +
    +  // WebKit has an extra BR in case 2.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'IE clobbers the editable node in case 2 (can\'t repro with real ' +
    +      'user selections). IE doesn\'t remove the table in case 1.');
    +
    +  expectedFailures.run(function() {
    +
    +    // When a full table is selected, we remove it completely.
    +    div.innerHTML = 'foo<table><tr><td>bar</td></tr></table>baz1';
    +    goog.dom.Range.createFromNodeContents(div.childNodes[1]).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('foo<br>bar<br>baz1', div.innerHTML);
    +    FIELDMOCK.$verify();
    +
    +    // Remove the full table when it is selected with additional
    +    // contents too.
    +    div.innerHTML = 'foo<table><tr><td>bar</td></tr></table>baz2';
    +    goog.dom.Range.createFromNodes(div.firstChild, 0,
    +        div.lastChild, 1).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('foo<br>bar<br>baz2', div.innerHTML);
    +    FIELDMOCK.$verify();
    +
    +    // We should still remove the table, even if the selection is inside the
    +    // table and it is fully selected.
    +    div.innerHTML = 'foo<table><tr><td id=\'td\'>bar</td></tr></table>baz3';
    +    goog.dom.Range.createFromNodeContents(
    +        goog.dom.getElement('td').firstChild).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('foo<br>bar<br>baz3', div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testInsideTableRemoveFormatting() {
    +  var div = document.getElementById('html');
    +  div.innerHTML =
    +      '<table><tr><td><b id="b">foo</b></td></tr><tr><td>ba</td></tr></table>';
    +
    +  goog.dom.Range.createFromNodeContents(goog.dom.getElement('b')).select();
    +
    +  // Webkit adds some apple style span crap during execCommand("removeFormat")
    +  // Our workaround for the nbsp bug removes these, but causes worse problems.
    +  // See bugs.webkit.org/show_bug.cgi?id=29164 for more details.
    +  expectedFailures.expectFailureFor(
    +      WEBKIT_BEFORE_CHROME_8 &&
    +      !goog.editor.BrowserFeature.ADDS_NBSPS_IN_REMOVE_FORMAT,
    +      'Extra apple-style-spans');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +
    +    // Only remove styling from inside tables.
    +    assertHTMLEquals(
    +        '<table><tr><td>foo' + insertImageBoldGarbage +
    +        '</td></tr><tr><td>ba</td></tr></table>',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +
    +}
    +
    +function testPartialTableRemoveFormatting() {
    +  if (goog.userAgent.IE) {
    +    // IE returns an "unspecified error" which seems to be beyond
    +    // ExpectedFailures' ability to catch.
    +    return;
    +  }
    +
    +  var div = document.getElementById('html');
    +  div.innerHTML = 'bar<table><tr><td><b id="b">foo</b></td></tr>' +
    +                  '<tr><td><i>banana</i></td></tr></table><div id="baz">' +
    +                  'baz</div>';
    +
    +  // Select from the "oo" inside the b tag to the end of "baz".
    +  goog.dom.Range.createFromNodes(goog.dom.getElement('b').firstChild, 1,
    +      goog.dom.getElement('baz').firstChild, 3).select();
    +
    +  // All browsers currently clobber the table cells that are selected.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    // Only remove styling from inside tables.
    +    assertHTMLEquals('bar<table><tr><td><b id="b">f</b>oo</td></tr>' +
    +                     '<tr><td>banana</td></tr></table>baz', div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +// Runs tests knowing some browsers will fail, because the new
    +// table functionality hasn't been implemented in them yet.
    +function runExpectingFailuresForUnimplementedBrowsers(func) {
    +  if (goog.userAgent.IE) {
    +    // IE returns an "unspecified error" which seems to be beyond
    +    // ExpectedFailures' ability to catch.
    +    return;
    +  }
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'Proper behavior not yet implemented for IE.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'Proper behavior not yet implemented for WebKit.');
    +
    +  expectedFailures.run(func);
    +}
    +
    +
    +function testTwoTablesSelectedFullyRemoveFormatting() {
    +  runExpectingFailuresForUnimplementedBrowsers(function() {
    +    var div = document.getElementById('html');
    +    // When two tables are fully selected, we remove them completely.
    +    div.innerHTML = '<table><tr><td>foo</td></tr></table>' +
    +                    '<table><tr><td>bar</td></tr></table>';
    +    goog.dom.Range.createFromNodes(div.firstChild, 0,
    +        div.lastChild, 1).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('<br>foo<br><br>bar<br>', div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testTwoTablesSelectedFullyInsideRemoveFormatting() {
    +  if (goog.userAgent.WEBKIT) {
    +    // Something goes very wrong here, but it did before
    +    // Julie started writing v2.  Will address when converting
    +    // safari to v2.
    +    return;
    +  }
    +
    +  runExpectingFailuresForUnimplementedBrowsers(function() {
    +    var div = document.getElementById('html');
    +    // When two tables are selected from inside but fully,
    +    // also remove them completely.
    +    div.innerHTML = '<table><tr><td id="td1">foo</td></tr></table>' +
    +                    '<table><tr><td id="td2">bar</td></tr></table>';
    +    goog.dom.Range.createFromNodes(goog.dom.getElement('td1').firstChild, 0,
    +        goog.dom.getElement('td2').firstChild, 3).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('<br>foo<br><br>bar<br>', div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testTwoTablesSelectedFullyAndPartiallyRemoveFormatting() {
    +  runExpectingFailuresForUnimplementedBrowsers(function() {
    +    var div = document.getElementById('html');
    +    // Two tables selected, one fully, one partially. Remove
    +    // only the fully selected one and remove styles only from
    +    // partially selected one.
    +    div.innerHTML = '<table><tr><td id="td1">foo</td></tr></table>' +
    +                    '<table><tr><td id="td2"><b>bar<b></td></tr></table>';
    +    goog.dom.Range.createFromNodes(goog.dom.getElement('td1').firstChild, 0,
    +        goog.dom.getElement('td2').firstChild.firstChild, 2).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('<br>foo<br>' +
    +                     '<table><tr><td id="td2">ba<b>r</b></td></tr></table>',
    +        div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testTwoTablesSelectedPartiallyRemoveFormatting() {
    +  runExpectingFailuresForUnimplementedBrowsers(function() {
    +    var div = document.getElementById('html');
    +    // Two tables selected, both partially.  Don't remove tables,
    +    // but remove styles.
    +    div.innerHTML = '<table><tr><td id="td1">f<i>o</i>o</td></tr></table>' +
    +                    '<table><tr><td id="td2">b<b>a</b>r</td></tr></table>';
    +    goog.dom.Range.createFromNodes(goog.dom.getElement('td1').firstChild, 1,
    +        goog.dom.getElement('td2').childNodes[1], 1).select();
    +    FORMATTER.removeFormatting_();
    +    assertHTMLEquals('<table><tr><td id="td1">foo</td></tr></table>' +
    +                     '<table><tr><td id="td2">bar</td></tr></table>',
    +                     div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +
    +/**
    + * Test a random snippet from Google News (Google News has complicated
    + * dom structure, including tables, links, images, etc).
    + */
    +function testRandomGoogleNewsSnippetRemoveFormatting() {
    +  if (goog.userAgent.IE) {
    +    // IE returns an "unspecified error" which seems to be beyond
    +    // ExpectedFailures' ability to catch.
    +    return;
    +  }
    +
    +  var div = document.getElementById('html');
    +  div.innerHTML =
    +      '<font size="-3"><br></font><table align="right" border="0" ' +
    +      'cellpadding="0" cellspacing="0"><tbody><tr><td style="padding-left:' +
    +      '6px;" valign="top" width="80" align="center"><a href="http://www.wash' +
    +      'ingtonpost.com/wp-dyn/content/article/2008/11/11/AR2008111101090.htm' +
    +      'l" + id="s-skHRvWH7ryqkcA4caGv0QQ:u-AFQjCNG3vx1HJOxKxMQPzCvYOVRE0JUDe' +
    +      'Q:r-1-0i_1268233361_6_H0_MH20_PL60"><img src="http://news.google.com/' +
    +      'news?imgefp=4LFiNNP62TgJ&amp;imgurl=media3.washingtonpost.com/wp-dyn/' +
    +      'content/photo/2008/11/11/PH2008111101091.jpg" alt="" width="60" ' +
    +      'border="1" height="80"><br><font size="-2">Washington Post</font></a>' +
    +      '</td></tr></tbody></table><a href="http://www.nme.com/news/britney-' +
    +      'spears/40995" id="s-xZUO-t0c1IpsVjyJj0rgxw:u-AFQjCNEZAMQCseEW6uTgXI' +
    +      'iPvAMHe_0B4A:r-1-0_1268233361_6_H0_MH20_PL60"><b>Britney\'s son ' +
    +      'released from hospital</b></a><br><font size="-1"><b><font color=' +
    +      '"#6f6f6f">NME.com&nbsp;-</font> <nobr>53 minutes ago</nobr></b>' +
    +      '</font><br><font size="-1">Britney Spears� youngest son Jayden James ' +
    +      'has been released from hospital, having been admitted on Sunday after' +
    +      ' suffering a severe reaction to something he ingested.</font><br><fon' +
    +      'tsize="-1"><a href="http://www.celebrity-gossip.net/celebrities/holly' +
    +      'wood/britney-and-jamie-lynn-spears-alligator-alley-208944/" id="s-nM' +
    +      'PzHclcMG0J2WZkw9gnVQ:u-AFQjCNHal08usOQ5e5CAQsck2yGsTYeGVQ">Britney ' +
    +      'and Jamie Lynn Spears: Alligator Alley!</a> <font size="-1" color=' +
    +      '"#6f6f6f"><nobr>The Gossip Girls</nobr></font></font><br><font size=' +
    +      '"-1"><a href="http://foodconsumer.org/7777/8888/Other_N_ews_51/111101' +
    +      '362008_Allergy_incident_could_spell_custody_trouble_for_Britney_Spear' +
    +      's.shtml" id="s-2lMNDY4joOprVvkkY_b-6A:u-AFQjCNGAeFNutMEbSg5zAvrh5reBF' +
    +      'lqUmA">Allergy incident could spell trouble for Britney Spears</a> ' +
    +      '<font size="-1" color="#6f6f6f"><nobr>Food Consumer</nobr></font>' +
    +      '</font><br><font class="p" size="-1"><a href="http://www.people.com/' +
    +      'people/article/0,,20239458,00.html" id="s-x9thwVUYVET0ZJOnkkcsjw:u-A' +
    +      'FQjCNE99eijVIrezr9AFRjLkmo5j_Jr7A"><nobr>People Magazine</nobr></a>&nb' +
    +      'sp;- <a href="http://www.eonline.com/uberblog/b68226_hospital_run_cou' +
    +      'ld_cost_britney_custody.html" id="s-kYt5LHDhlDnhUL9kRLuuwA:u-AFQjCNF8' +
    +      '8eOy2utriYuF0icNrZQPzwK8gg"><nobr>E! Online</nobr></a>&nbsp;- <a href' +
    +      '="http://justjared.buzznet.com/2008/11/11/britney-spears-alligator-fa' +
    +      'rm/" id="s--VDy1fyacNvaRo_aXb02Dw:u-AFQjCNEn0Rz3wg0PMwDdzKTDug-9k5W6y' +
    +      'g"><nobr>Just Jared</nobr></a>&nbsp;- <a href="http://www.efluxmedia.' +
    +      'com/news_Britney_Spears_Son_Released_from_Hospital_28696.html" id="s-' +
    +      '8oX6hVDe4Qbcl1x5Rua_EA:u-AFQjCNEpn3nOHA8EB0pxJAPf6diOicMRDg"><nobr>eF' +
    +      'luxMedia</nobr></a></font><br><font class="p" size="-1"><a class="p" ' +
    +      'href="http://news.google.com/news?ncl=1268233361&amp;hl=en"><nobr><b>' +
    +      'all 950 news articles&nbsp;�</b></nobr></a></font>';
    +  // Select it all.
    +  goog.dom.Range.createFromNodeContents(div).select();
    +
    +  expectedFailures.expectFailureFor(WEBKIT_BEFORE_CHROME_8,
    +      'WebKit barfs apple-style-spans all over the place, and removes links.');
    +
    +  expectedFailures.run(function() {
    +    FORMATTER.removeFormatting_();
    +    // Leave links and images alone, remove all other formatting.
    +    assertHTMLEquals('<br><br><a href="http://www.washingtonpost.com/wp-dyn/' +
    +        'content/article/2008/11/11/AR2008111101090.html"><img src="http://n' +
    +        'ews.google.com/news?imgefp=4LFiNNP62TgJ&amp;imgurl=media3.washingto' +
    +        'npost.com/wp-dyn/content/photo/2008/11/11/PH2008111101091.jpg"><br>' +
    +        'Washington Post</a><br><a href="http://www.nme.com/news/britney-spe' +
    +        'ars/40995">Britney\'s son released from hospital</a><br>NME.com - 5' +
    +        '3 minutes ago<br>Britney Spears� youngest son Jayden James has been' +
    +        ' released from hospital, having been admitted on Sunday after suffe' +
    +        'ring a severe reaction to something he ingested.<br><a href="http:/' +
    +        '/www.celebrity-gossip.net/celebrities/hollywood/britney-and-jamie-l' +
    +        'ynn-spears-alligator-alley-208944/">Britney and Jamie Lynn Spears: ' +
    +        'Alligator Alley!</a> The Gossip Girls<br><a href="http://foodconsum' +
    +        'er.org/7777/8888/Other_N_ews_51/111101362008_Allergy_incident_could' +
    +        '_spell_custody_trouble_for_Britney_Spears.shtml">Allergy incident c' +
    +        'ould spell trouble for Britney Spears</a> Food Consumer<br><a href=' +
    +        '"http://www.people.com/people/article/0,,20239458,00.html">People M' +
    +        'agazine</a> - <a href="http://www.eonline.com/uberblog/b68226_hospi' +
    +        'tal_run_could_cost_britney_custody.html">E! Online</a> - <a href="h' +
    +        'ttp://justjared.buzznet.com/2008/11/11/britney-spears-alligator-far' +
    +        'm/">Just Jared</a> - <a href="http://www.efluxmedia.com/news_Britne' +
    +        'y_Spears_Son_Released_from_Hospital_28696.html">eFluxMedia</a><br><' +
    +        'a href="http://news.google.com/news?ncl=1268233361&amp;hl=en">all 9' +
    +        '50 news articles �</a>' +
    +        insertImageFontGarbage, div.innerHTML);
    +    FIELDMOCK.$verify();
    +  });
    +}
    +
    +function testRangeDelimitedByRanges() {
    +  var abcde = goog.dom.getElement('abcde').firstChild;
    +  var start = goog.dom.Range.createFromNodes(abcde, 1, abcde, 2);
    +  var end = goog.dom.Range.createFromNodes(abcde, 3, abcde, 4);
    +
    +  goog.testing.dom.assertRangeEquals(abcde, 1, abcde, 4,
    +      goog.editor.plugins.RemoveFormatting.createRangeDelimitedByRanges_(
    +          start, end));
    +}
    +
    +function testGetTableAncestor() {
    +  var div = document.getElementById('html');
    +
    +  div.innerHTML = 'foo<table><tr><td>foo</td></tr></table>bar';
    +  assertTrue('Full table is in table',
    +      !!FORMATTER.getTableAncestor_(div.childNodes[1]));
    +
    +  assertFalse('Outside of table',
    +      !!FORMATTER.getTableAncestor_(div.firstChild));
    +
    +  assertTrue('Table cell is in table',
    +      !!FORMATTER.getTableAncestor_(
    +          div.childNodes[1].firstChild.firstChild.firstChild));
    +
    +  div.innerHTML = 'foo';
    +  assertNull('No table inside field.',
    +      FORMATTER.getTableAncestor_(div.childNodes[0]));
    +}
    +
    +
    +/**
    + * @bug 1272905
    + */
    +function testHardReturnsInHeadersPreserved() {
    +  var div = document.getElementById('html');
    +  div.innerHTML = '<h1>abcd</h1><h2>efgh</h2><h3>ijkl</h3>';
    +
    +  // Select efgh.
    +  goog.dom.Range.createFromNodeContents(div.childNodes[1]).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'Proper behavior not yet implemented for IE.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'Proper behavior not yet implemented for WebKit.');
    +  expectedFailures.run(function() {
    +    assertHTMLEquals('<h1>abcd</h1><br>efgh<h3>ijkl</h3>', div.innerHTML);
    +  });
    +
    +  // Select ijkl.
    +  goog.dom.Range.createFromNodeContents(div.lastChild).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'Proper behavior not yet implemented for IE.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'Proper behavior not yet implemented for WebKit.');
    +  expectedFailures.run(function() {
    +    assertHTMLEquals('<h1>abcd</h1><br>efgh<br>ijkl', div.innerHTML);
    +  });
    +
    +  // Select abcd.
    +  goog.dom.Range.createFromNodeContents(div.firstChild).select();
    +  FORMATTER.removeFormatting_();
    +
    +  expectedFailures.expectFailureFor(goog.userAgent.IE,
    +      'Proper behavior not yet implemented for IE.');
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT,
    +      'Proper behavior not yet implemented for WebKit.');
    +  expectedFailures.run(function() {
    +    assertHTMLEquals('<br>abcd<br>efgh<br>ijkl', div.innerHTML);
    +  });
    +}
    +
    +function testKeyboardShortcut_space() {
    +  FIELDMOCK.$reset();
    +
    +  FIELDMOCK.execCommand(
    +      goog.editor.plugins.RemoveFormatting.REMOVE_FORMATTING_COMMAND);
    +
    +  FIELDMOCK.$replay();
    +
    +  var e = {};
    +  var key = ' ';
    +  var result = FORMATTER.handleKeyboardShortcut(e, key, true);
    +  assertTrue(result);
    +
    +  FIELDMOCK.$verify();
    +}
    +
    +function testKeyboardShortcut_other() {
    +  FIELDMOCK.$reset();
    +  FIELDMOCK.$replay();
    +
    +  var e = {};
    +  var key = 'a';
    +  var result = FORMATTER.handleKeyboardShortcut(e, key, true);
    +  assertFalse(result);
    +
    +  FIELDMOCK.$verify();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler.js
    new file mode 100644
    index 0000000000..3cb703e5f7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler.js
    @@ -0,0 +1,92 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Editor plugin to handle tab keys not in lists to add 4 spaces.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.editor.plugins.SpacesTabHandler');
    +
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.plugins.AbstractTabHandler');
    +goog.require('goog.editor.range');
    +
    +
    +
    +/**
    + * Plugin to handle tab keys when not in lists to add 4 spaces.
    + * @constructor
    + * @extends {goog.editor.plugins.AbstractTabHandler}
    + * @final
    + */
    +goog.editor.plugins.SpacesTabHandler = function() {
    +  goog.editor.plugins.AbstractTabHandler.call(this);
    +};
    +goog.inherits(goog.editor.plugins.SpacesTabHandler,
    +    goog.editor.plugins.AbstractTabHandler);
    +
    +
    +/** @override */
    +goog.editor.plugins.SpacesTabHandler.prototype.getTrogClassId = function() {
    +  return 'SpacesTabHandler';
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.SpacesTabHandler.prototype.handleTabKey = function(e) {
    +  var dh = this.getFieldDomHelper();
    +  var range = this.getFieldObject().getRange();
    +  if (!goog.editor.range.intersectsTag(range, goog.dom.TagName.LI)) {
    +    // In the shift + tab case we don't want to insert spaces, but we don't
    +    // want focus to move either so skip the spacing logic and just prevent
    +    // default.
    +    if (!e.shiftKey) {
    +      // Not in a list but we want to insert 4 spaces.
    +
    +      // Stop change events while we make multiple field changes.
    +      this.getFieldObject().stopChangeEvents(true, true);
    +
    +      // Inserting nodes below completely messes up the selection, doing the
    +      // deletion here before it's messed up. Only delete if text is selected,
    +      // otherwise we would remove the character to the right of the cursor.
    +      if (!range.isCollapsed()) {
    +        dh.getDocument().execCommand('delete', false, null);
    +        // Safari 3 has some DOM exceptions if we don't reget the range here,
    +        // doing it all the time just to be safe.
    +        range = this.getFieldObject().getRange();
    +      }
    +
    +      // Emulate tab by removing selection and inserting 4 spaces
    +      // Two breaking spaces in a row can be collapsed by the browser into one
    +      // space. Inserting the string below because it is guaranteed to never
    +      // collapse to less than four spaces, regardless of what is adjacent to
    +      // the inserted spaces. This might make line wrapping slightly
    +      // sub-optimal around a grouping of non-breaking spaces.
    +      var elem = dh.createDom('span', null, '\u00a0\u00a0 \u00a0');
    +      elem = range.insertNode(elem, false);
    +
    +      this.getFieldObject().dispatchChange();
    +      goog.editor.range.placeCursorNextTo(elem, false);
    +      this.getFieldObject().dispatchSelectionChangeEvent();
    +    }
    +
    +    e.preventDefault();
    +    return true;
    +  }
    +
    +  return false;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.html
    new file mode 100644
    index 0000000000..f811ab72cc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  @author robbyw@google.com (Robby Walker)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.editor.plugins.SpacesTabHandler
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script src="../../deps.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.SpacesTabHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="field">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.js
    new file mode 100644
    index 0000000000..79f744c0b3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/spacestabhandler_test.js
    @@ -0,0 +1,174 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.SpacesTabHandlerTest');
    +goog.setTestOnly('goog.editor.plugins.SpacesTabHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.plugins.SpacesTabHandler');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.functions');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.editor.FieldMock');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.jsunit');
    +
    +var field;
    +var editableField;
    +var tabHandler;
    +var testHelper;
    +
    +function setUp() {
    +  field = goog.dom.getElement('field');
    +  editableField = new goog.testing.editor.FieldMock();
    +  // Modal mode behavior tested in AbstractTabHandler.
    +  editableField.inModalMode = goog.functions.FALSE;
    +  testHelper = new goog.testing.editor.TestHelper(field);
    +  testHelper.setUpEditableElement();
    +
    +  tabHandler = new goog.editor.plugins.SpacesTabHandler();
    +  tabHandler.registerFieldObject(editableField);
    +}
    +
    +function tearDown() {
    +  editableField = null;
    +  testHelper.tearDownEditableElement();
    +  tabHandler.dispose();
    +}
    +
    +function testSelectedTextIndent() {
    +  field.innerHTML = 'Test';
    +
    +  var testText = field.firstChild;
    +  testHelper.select(testText, 0, testText, 4);
    +
    +  var event = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  event.keyCode = goog.events.KeyCodes.TAB;
    +  event.shiftKey = false;
    +
    +  editableField.stopChangeEvents(true, true);
    +  editableField.dispatchChange();
    +  editableField.dispatchSelectionChangeEvent();
    +  event.preventDefault();
    +
    +  editableField.$replay();
    +  event.$replay();
    +
    +  assertTrue('Event marked as handled',
    +      tabHandler.handleKeyboardShortcut(event, '', false));
    +  var contents = field.textContent || field.innerText;
    +  // Chrome doesn't treat \u00a0 as a space.
    +  assertTrue('Text should be replaced with 4 spaces but was: "' +
    +      contents + '"',
    +      /^(\s|\u00a0){4}$/.test(contents));
    +
    +  editableField.$verify();
    +  event.$verify();
    +}
    +
    +function testCursorIndent() {
    +  field.innerHTML = 'Test';
    +
    +  var testText = field.firstChild;
    +  testHelper.select(testText, 2, testText, 2);
    +
    +  var event = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  event.keyCode = goog.events.KeyCodes.TAB;
    +  event.shiftKey = false;
    +
    +  editableField.stopChangeEvents(true, true);
    +  editableField.dispatchChange();
    +  editableField.dispatchSelectionChangeEvent();
    +  event.preventDefault();
    +
    +  editableField.$replay();
    +  event.$replay();
    +
    +  assertTrue('Event marked as handled',
    +      tabHandler.handleKeyboardShortcut(event, '', false));
    +  var contents = field.textContent || field.innerText;
    +  assertTrue('Expected contents "Te    st" but was: "' + contents + '"',
    +      /Te[\s|\u00a0]{4}st/.test(contents));
    +
    +  editableField.$verify();
    +  event.$verify();
    +}
    +
    +function testShiftTabNoOp() {
    +  field.innerHTML = 'Test';
    +
    +  range = goog.dom.Range.createFromNodeContents(field);
    +  range.collapse();
    +  range.select();
    +
    +  var event = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  event.keyCode = goog.events.KeyCodes.TAB;
    +  event.shiftKey = true;
    +
    +  event.preventDefault();
    +  editableField.$replay();
    +  event.$replay();
    +
    +  assertTrue('Event marked as handled',
    +      tabHandler.handleKeyboardShortcut(event, '', false));
    +  var contents = field.textContent || field.innerText;
    +  assertEquals('Shift+tab should not change contents', 'Test', contents);
    +
    +  editableField.$verify();
    +  event.$verify();
    +}
    +
    +function testInListNoOp() {
    +  field.innerHTML = '<ul><li>Test</li></ul>';
    +
    +  var testText = field.firstChild.firstChild.firstChild; // div ul li Test
    +  testHelper.select(testText, 2, testText, 2);
    +
    +  var event = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  event.keyCode = goog.events.KeyCodes.TAB;
    +  event.shiftKey = false;
    +
    +  editableField.$replay();
    +  event.$replay();
    +
    +  assertFalse('Event must not be handled when selection inside list.',
    +      tabHandler.handleKeyboardShortcut(event, '', false));
    +  testHelper.assertHtmlMatches('<ul><li>Test</li></ul>');
    +
    +  editableField.$verify();
    +  event.$verify();
    +}
    +
    +function testContainsListNoOp() {
    +  field.innerHTML = '<ul><li>Test</li></ul>';
    +
    +  var testText = field.firstChild.firstChild.firstChild; // div ul li Test
    +  testHelper.select(field.firstChild, 0, testText, 2);
    +
    +  var event = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  event.keyCode = goog.events.KeyCodes.TAB;
    +  event.shiftKey = false;
    +
    +  editableField.$replay();
    +  event.$replay();
    +
    +  assertFalse('Event must not be handled when selection inside list.',
    +      tabHandler.handleKeyboardShortcut(event, '', false));
    +  testHelper.assertHtmlMatches('<ul><li>Test</li></ul>');
    +
    +  editableField.$verify();
    +  event.$verify();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor.js
    new file mode 100644
    index 0000000000..f83cb9687c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor.js
    @@ -0,0 +1,475 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Plugin that enables table editing.
    + *
    + * @see ../../demos/editor/tableeditor.html
    + */
    +
    +goog.provide('goog.editor.plugins.TableEditor');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.Plugin');
    +goog.require('goog.editor.Table');
    +goog.require('goog.editor.node');
    +goog.require('goog.editor.range');
    +goog.require('goog.object');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Plugin that adds support for table creation and editing commands.
    + * @constructor
    + * @extends {goog.editor.Plugin}
    + * @final
    + */
    +goog.editor.plugins.TableEditor = function() {
    +  goog.editor.plugins.TableEditor.base(this, 'constructor');
    +
    +  /**
    +   * The array of functions that decide whether a table element could be
    +   * editable by the user or not.
    +   * @type {Array<function(Element):boolean>}
    +   * @private
    +   */
    +  this.isTableEditableFunctions_ = [];
    +
    +  /**
    +   * The pre-bound function that decides whether a table element could be
    +   * editable by the user or not overall.
    +   * @type {function(Node):boolean}
    +   * @private
    +   */
    +  this.isUserEditableTableBound_ = goog.bind(this.isUserEditableTable_, this);
    +};
    +goog.inherits(goog.editor.plugins.TableEditor, goog.editor.Plugin);
    +
    +
    +/** @override */
    +// TODO(user): remove this once there's a sensible default
    +// implementation in the base Plugin.
    +goog.editor.plugins.TableEditor.prototype.getTrogClassId = function() {
    +  return String(goog.getUid(this.constructor));
    +};
    +
    +
    +/**
    + * Commands supported by goog.editor.plugins.TableEditor.
    + * @enum {string}
    + */
    +goog.editor.plugins.TableEditor.COMMAND = {
    +  TABLE: '+table',
    +  INSERT_ROW_AFTER: '+insertRowAfter',
    +  INSERT_ROW_BEFORE: '+insertRowBefore',
    +  INSERT_COLUMN_AFTER: '+insertColumnAfter',
    +  INSERT_COLUMN_BEFORE: '+insertColumnBefore',
    +  REMOVE_ROWS: '+removeRows',
    +  REMOVE_COLUMNS: '+removeColumns',
    +  SPLIT_CELL: '+splitCell',
    +  MERGE_CELLS: '+mergeCells',
    +  REMOVE_TABLE: '+removeTable'
    +};
    +
    +
    +/**
    + * Inverse map of execCommand strings to
    + * {@link goog.editor.plugins.TableEditor.COMMAND} constants. Used to
    + * determine whether a string corresponds to a command this plugin handles
    + * in O(1) time.
    + * @type {Object}
    + * @private
    + */
    +goog.editor.plugins.TableEditor.SUPPORTED_COMMANDS_ =
    +    goog.object.transpose(goog.editor.plugins.TableEditor.COMMAND);
    +
    +
    +/**
    + * Whether the string corresponds to a command this plugin handles.
    + * @param {string} command Command string to check.
    + * @return {boolean} Whether the string corresponds to a command
    + *     this plugin handles.
    + * @override
    + */
    +goog.editor.plugins.TableEditor.prototype.isSupportedCommand =
    +    function(command) {
    +  return command in goog.editor.plugins.TableEditor.SUPPORTED_COMMANDS_;
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TableEditor.prototype.enable = function(fieldObject) {
    +  goog.editor.plugins.TableEditor.base(this, 'enable', fieldObject);
    +
    +  // enableObjectResizing is supported only for Gecko.
    +  // You can refer to http://qooxdoo.org/contrib/project/htmlarea/html_editing
    +  // for a compatibility chart.
    +  if (goog.userAgent.GECKO) {
    +    var doc = this.getFieldDomHelper().getDocument();
    +    doc.execCommand('enableObjectResizing', false, 'true');
    +  }
    +};
    +
    +
    +/**
    + * Returns the currently selected table.
    + * @return {Element?} The table in which the current selection is
    + *     contained, or null if there isn't such a table.
    + * @private
    + */
    +goog.editor.plugins.TableEditor.prototype.getCurrentTable_ = function() {
    +  var selectedElement = this.getFieldObject().getRange().getContainer();
    +  return this.getAncestorTable_(selectedElement);
    +};
    +
    +
    +/**
    + * Finds the first user-editable table element in the input node's ancestors.
    + * @param {Node?} node The node to start with.
    + * @return {Element?} The table element that is closest ancestor of the node.
    + * @private
    + */
    +goog.editor.plugins.TableEditor.prototype.getAncestorTable_ = function(node) {
    +  var ancestor = goog.dom.getAncestor(node, this.isUserEditableTableBound_,
    +      true);
    +  if (goog.editor.node.isEditable(ancestor)) {
    +    return /** @type {Element?} */(ancestor);
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Returns the current value of a given command. Currently this plugin
    + * only returns a value for goog.editor.plugins.TableEditor.COMMAND.TABLE.
    + * @override
    + */
    +goog.editor.plugins.TableEditor.prototype.queryCommandValue =
    +    function(command) {
    +  if (command == goog.editor.plugins.TableEditor.COMMAND.TABLE) {
    +    return !!this.getCurrentTable_();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TableEditor.prototype.execCommandInternal = function(
    +    command, opt_arg) {
    +  var result = null;
    +  // TD/TH in which to place the cursor, if the command destroys the current
    +  // cursor position.
    +  var cursorCell = null;
    +  var range = this.getFieldObject().getRange();
    +  if (command == goog.editor.plugins.TableEditor.COMMAND.TABLE) {
    +    // Don't create a table if the cursor isn't in an editable region.
    +    if (!goog.editor.range.isEditable(range)) {
    +      return null;
    +    }
    +    // Create the table.
    +    var tableProps = opt_arg || {width: 4, height: 2};
    +    var doc = this.getFieldDomHelper().getDocument();
    +    var table = goog.editor.Table.createDomTable(
    +        doc, tableProps.width, tableProps.height);
    +    range.replaceContentsWithNode(table);
    +    // In IE, replaceContentsWithNode uses pasteHTML, so we lose our reference
    +    // to the inserted table.
    +    // TODO(user): use the reference to the table element returned from
    +    // replaceContentsWithNode.
    +    if (!goog.userAgent.IE) {
    +      cursorCell = table.getElementsByTagName('td')[0];
    +    }
    +  } else {
    +    var cellSelection = new goog.editor.plugins.TableEditor.CellSelection_(
    +        range, goog.bind(this.getAncestorTable_, this));
    +    var table = cellSelection.getTable();
    +    if (!table) {
    +      return null;
    +    }
    +    switch (command) {
    +      case goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_BEFORE:
    +        table.insertRow(cellSelection.getFirstRowIndex());
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_AFTER:
    +        table.insertRow(cellSelection.getLastRowIndex() + 1);
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_BEFORE:
    +        table.insertColumn(cellSelection.getFirstColumnIndex());
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_AFTER:
    +        table.insertColumn(cellSelection.getLastColumnIndex() + 1);
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.REMOVE_ROWS:
    +        var startRow = cellSelection.getFirstRowIndex();
    +        var endRow = cellSelection.getLastRowIndex();
    +        if (startRow == 0 && endRow == (table.rows.length - 1)) {
    +          // Instead of deleting all rows, delete the entire table.
    +          return this.execCommandInternal(
    +              goog.editor.plugins.TableEditor.COMMAND.REMOVE_TABLE);
    +        }
    +        var startColumn = cellSelection.getFirstColumnIndex();
    +        var rowCount = (endRow - startRow) + 1;
    +        for (var i = 0; i < rowCount; i++) {
    +          table.removeRow(startRow);
    +        }
    +        if (table.rows.length > 0) {
    +          // Place cursor in the previous/first row.
    +          var closestRow = Math.min(startRow, table.rows.length - 1);
    +          cursorCell = table.rows[closestRow].columns[startColumn].element;
    +        }
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.REMOVE_COLUMNS:
    +        var startCol = cellSelection.getFirstColumnIndex();
    +        var endCol = cellSelection.getLastColumnIndex();
    +        if (startCol == 0 && endCol == (table.rows[0].columns.length - 1)) {
    +          // Instead of deleting all columns, delete the entire table.
    +          return this.execCommandInternal(
    +              goog.editor.plugins.TableEditor.COMMAND.REMOVE_TABLE);
    +        }
    +        var startRow = cellSelection.getFirstRowIndex();
    +        var removeCount = (endCol - startCol) + 1;
    +        for (var i = 0; i < removeCount; i++) {
    +          table.removeColumn(startCol);
    +        }
    +        var currentRow = table.rows[startRow];
    +        if (currentRow) {
    +          // Place cursor in the previous/first column.
    +          var closestCol = Math.min(startCol, currentRow.columns.length - 1);
    +          cursorCell = currentRow.columns[closestCol].element;
    +        }
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.MERGE_CELLS:
    +        if (cellSelection.isRectangle()) {
    +          table.mergeCells(cellSelection.getFirstRowIndex(),
    +                           cellSelection.getFirstColumnIndex(),
    +                           cellSelection.getLastRowIndex(),
    +                           cellSelection.getLastColumnIndex());
    +        }
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.SPLIT_CELL:
    +        if (cellSelection.containsSingleCell()) {
    +          table.splitCell(cellSelection.getFirstRowIndex(),
    +                          cellSelection.getFirstColumnIndex());
    +        }
    +        break;
    +      case goog.editor.plugins.TableEditor.COMMAND.REMOVE_TABLE:
    +        table.element.parentNode.removeChild(table.element);
    +        break;
    +      default:
    +    }
    +  }
    +  if (cursorCell) {
    +    range = goog.dom.Range.createFromNodeContents(cursorCell);
    +    range.collapse(false);
    +    range.select();
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * Checks whether the element is a table editable by the user.
    + * @param {Node} element The element in question.
    + * @return {boolean} Whether the element is a table editable by the user.
    + * @private
    + */
    +goog.editor.plugins.TableEditor.prototype.isUserEditableTable_ =
    +    function(element) {
    +  // Default implementation.
    +  if (element.tagName != goog.dom.TagName.TABLE) {
    +    return false;
    +  }
    +
    +  // Check for extra user-editable filters.
    +  return goog.array.every(this.isTableEditableFunctions_, function(func) {
    +    return func(/** @type {Element} */ (element));
    +  });
    +};
    +
    +
    +/**
    + * Adds a function to filter out non-user-editable tables.
    + * @param {function(Element):boolean} func A function to decide whether the
    + *   table element could be editable by the user or not.
    + */
    +goog.editor.plugins.TableEditor.prototype.addIsTableEditableFunction =
    +    function(func) {
    +  goog.array.insert(this.isTableEditableFunctions_, func);
    +};
    +
    +
    +
    +/**
    + * Class representing the selected cell objects within a single  table.
    + * @param {goog.dom.AbstractRange} range Selected range from which to calculate
    + *     selected cells.
    + * @param {function(Element):Element?} getParentTableFunction A function that
    + *     finds the user-editable table from a given element.
    + * @constructor
    + * @private
    + */
    +goog.editor.plugins.TableEditor.CellSelection_ =
    +    function(range, getParentTableFunction) {
    +  this.cells_ = [];
    +
    +  // Mozilla lets users select groups of cells, with each cell showing
    +  // up as a separate range in the selection. goog.dom.Range doesn't
    +  // currently support this.
    +  // TODO(user): support this case in range.js
    +  var selectionContainer = range.getContainerElement();
    +  var elementInSelection = function(node) {
    +    // TODO(user): revert to the more liberal containsNode(node, true),
    +    // which will match partially-selected cells. We're using
    +    // containsNode(node, false) at the moment because otherwise it's
    +    // broken in WebKit due to a closure range bug.
    +    return selectionContainer == node ||
    +        selectionContainer.parentNode == node ||
    +        range.containsNode(node, false);
    +  };
    +
    +  var parentTableElement = selectionContainer &&
    +      getParentTableFunction(selectionContainer);
    +  if (!parentTableElement) {
    +    return;
    +  }
    +
    +  var parentTable = new goog.editor.Table(parentTableElement);
    +  // It's probably not possible to select a table with no cells, but
    +  // do a sanity check anyway.
    +  if (!parentTable.rows.length || !parentTable.rows[0].columns.length) {
    +    return;
    +  }
    +  // Loop through cells to calculate dimensions for this CellSelection.
    +  for (var i = 0, row; row = parentTable.rows[i]; i++) {
    +    for (var j = 0, cell; cell = row.columns[j]; j++) {
    +      if (elementInSelection(cell.element)) {
    +        // Update dimensions based on cell.
    +        if (!this.cells_.length) {
    +          this.firstRowIndex_ = cell.startRow;
    +          this.lastRowIndex_ = cell.endRow;
    +          this.firstColIndex_ = cell.startCol;
    +          this.lastColIndex_ = cell.endCol;
    +        } else {
    +          this.firstRowIndex_ = Math.min(this.firstRowIndex_, cell.startRow);
    +          this.lastRowIndex_ = Math.max(this.lastRowIndex_, cell.endRow);
    +          this.firstColIndex_ = Math.min(this.firstColIndex_, cell.startCol);
    +          this.lastColIndex_ = Math.max(this.lastColIndex_, cell.endCol);
    +        }
    +        this.cells_.push(cell);
    +      }
    +    }
    +  }
    +  this.parentTable_ = parentTable;
    +};
    +
    +
    +/**
    + * Returns the EditableTable object of which this selection's cells are a
    + * subset.
    + * @return {!goog.editor.Table} the table.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getTable =
    +    function() {
    +  return this.parentTable_;
    +};
    +
    +
    +/**
    + * Returns the row index of the uppermost cell in this selection.
    + * @return {number} The row index.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getFirstRowIndex =
    +    function() {
    +  return this.firstRowIndex_;
    +};
    +
    +
    +/**
    + * Returns the row index of the lowermost cell in this selection.
    + * @return {number} The row index.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getLastRowIndex =
    +    function() {
    +  return this.lastRowIndex_;
    +};
    +
    +
    +/**
    + * Returns the column index of the farthest left cell in this selection.
    + * @return {number} The column index.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getFirstColumnIndex =
    +    function() {
    +  return this.firstColIndex_;
    +};
    +
    +
    +/**
    + * Returns the column index of the farthest right cell in this selection.
    + * @return {number} The column index.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getLastColumnIndex =
    +    function() {
    +  return this.lastColIndex_;
    +};
    +
    +
    +/**
    + * Returns the cells in this selection.
    + * @return {!Array<Element>} Cells in this selection.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.getCells = function() {
    +  return this.cells_;
    +};
    +
    +
    +/**
    + * Returns a boolean value indicating whether or not the cells in this
    + * selection form a rectangle.
    + * @return {boolean} Whether the selection forms a rectangle.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.isRectangle =
    +    function() {
    +  // TODO(user): check for missing cells. Right now this returns
    +  // whether all cells in the selection are in the rectangle, but doesn't
    +  // verify that every expected cell is present.
    +  if (!this.cells_.length) {
    +    return false;
    +  }
    +  var firstCell = this.cells_[0];
    +  var lastCell = this.cells_[this.cells_.length - 1];
    +  return !(this.firstRowIndex_ < firstCell.startRow ||
    +           this.lastRowIndex_ > lastCell.endRow ||
    +           this.firstColIndex_ < firstCell.startCol ||
    +           this.lastColIndex_ > lastCell.endCol);
    +};
    +
    +
    +/**
    + * Returns a boolean value indicating whether or not there is exactly
    + * one cell in this selection. Note that this may not be the same as checking
    + * whether getCells().length == 1; if there is a single cell with
    + * rowSpan/colSpan set it will appear multiple times.
    + * @return {boolean} Whether there is exatly one cell in this selection.
    + */
    +goog.editor.plugins.TableEditor.CellSelection_.prototype.containsSingleCell =
    +    function() {
    +  var cellCount = this.cells_.length;
    +  return cellCount > 0 &&
    +      (this.cells_[0] == this.cells_[cellCount - 1]);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.html
    new file mode 100644
    index 0000000000..3d08ac1671
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.editor.plugins.TableEditor Tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.TableEditorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="field">
    +   <div>
    +    lorem ipsum
    +   </div>
    +   <div>
    +    ipsum lorem
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.js
    new file mode 100644
    index 0000000000..d9acf4bcfb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tableeditor_test.js
    @@ -0,0 +1,303 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.TableEditorTest');
    +goog.setTestOnly('goog.editor.plugins.TableEditorTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.plugins.TableEditor');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.JsUnitException');
    +goog.require('goog.testing.editor.FieldMock');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var field;
    +var plugin;
    +var fieldMock;
    +var expectedFailures;
    +var testHelper;
    +
    +function setUpPage() {
    +  field = goog.dom.getElement('field');
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  testHelper = new goog.testing.editor.TestHelper(
    +      goog.dom.getElement('field'));
    +  testHelper.setUpEditableElement();
    +  field.focus();
    +  plugin = new goog.editor.plugins.TableEditor();
    +  fieldMock = new goog.testing.editor.FieldMock();
    +  plugin.registerFieldObject(fieldMock);
    +  if (goog.userAgent.IE &&
    +      (goog.userAgent.compare(goog.userAgent.VERSION, '7.0') >= 0)) {
    +    goog.testing.TestCase.protectedTimeout_ = window.setTimeout;
    +  }
    +}
    +
    +function tearDown() {
    +  testHelper.tearDownEditableElement();
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testEnable() {
    +  fieldMock.$replay();
    +
    +  plugin.enable(fieldMock);
    +  assertTrue('Plugin should be enabled', plugin.isEnabled(fieldMock));
    +
    +  if (goog.userAgent.GECKO) {
    +    // This code path is executed only for GECKO browsers but we can't
    +    // verify it because of a GECKO bug while reading the value of the
    +    // command "enableObjectResizing".
    +    // See https://bugzilla.mozilla.org/show_bug.cgi?id=506368
    +    expectedFailures.expectFailureFor(goog.userAgent.GECKO);
    +    try {
    +      var doc = plugin.getFieldDomHelper().getDocument();
    +      assertTrue('Object resizing should be enabled',
    +                 doc.queryCommandValue('enableObjectResizing'));
    +    } catch (e) {
    +      // We need to marshal our exception in order for it to be handled
    +      // properly.
    +      expectedFailures.handleException(new goog.testing.JsUnitException(e));
    +    }
    +  }
    +  fieldMock.$verify();
    +}
    +
    +function testIsSupportedCommand() {
    +  goog.object.forEach(goog.editor.plugins.TableEditor.COMMAND,
    +      function(command) {
    +        assertTrue(goog.string.subs('Plugin should support %s', command),
    +            plugin.isSupportedCommand(command));
    +      });
    +  assertFalse('Plugin shouldn\'t support a bogus command',
    +              plugin.isSupportedCommand('+fable'));
    +}
    +
    +function testCreateTable() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell();
    +  var table = plugin.getCurrentTable_();
    +  assertNotNull('Table should not be null', table);
    +  assertEquals('Table should have the default number of rows',
    +               2,
    +               table.rows.length);
    +  assertEquals('Table should have the default number of cells',
    +               8,
    +               getCellCount(table));
    +  fieldMock.$verify();
    +}
    +
    +function testInsertRowBefore() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell();
    +  var table = plugin.getCurrentTable_();
    +  var selectedRow = fieldMock.getRange().getContainerElement().parentNode;
    +  assertNull('Selected row shouldn\'t have a previous sibling',
    +             selectedRow.previousSibling);
    +  assertEquals('Table should have two rows', 2, table.rows.length);
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_BEFORE);
    +  assertEquals('A row should have been inserted', 3, table.rows.length);
    +
    +  // Assert that we inserted a row above the currently selected row.
    +  assertNotNull('Selected row should have a previous sibling',
    +                selectedRow.previousSibling);
    +  fieldMock.$verify();
    +}
    +
    +function testInsertRowAfter() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 2, height: 1});
    +  var selectedRow = fieldMock.getRange().getContainerElement().parentNode;
    +  var table = plugin.getCurrentTable_();
    +  assertEquals('Table should have one row', 1, table.rows.length);
    +  assertNull('Selected row shouldn\'t have a next sibling',
    +             selectedRow.nextSibling);
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.INSERT_ROW_AFTER);
    +  assertEquals('A row should have been inserted', 2, table.rows.length);
    +  // Assert that we inserted a row after the currently selected row.
    +  assertNotNull('Selected row should have a next sibling',
    +                selectedRow.nextSibling);
    +  fieldMock.$verify();
    +}
    +
    +function testInsertColumnBefore() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 1, height: 1});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  assertEquals('Table should have one cell', 1, getCellCount(table));
    +  assertNull('Selected cell shouldn\'t have a previous sibling',
    +             selectedCell.previousSibling);
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_BEFORE);
    +  assertEquals('A cell should have been inserted', 2, getCellCount(table));
    +  assertNotNull('Selected cell should have a previous sibling',
    +                selectedCell.previousSibling);
    +  fieldMock.$verify();
    +}
    +
    +function testInsertColumnAfter() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 1, height: 1});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  assertEquals('Table should have one cell', 1, getCellCount(table));
    +  assertNull('Selected cell shouldn\'t have a next sibling',
    +             selectedCell.nextSibling);
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.INSERT_COLUMN_AFTER);
    +  assertEquals('A cell should have been inserted', 2, getCellCount(table));
    +  assertNotNull('Selected cell should have a next sibling',
    +                selectedCell.nextSibling);
    +  fieldMock.$verify();
    +}
    +
    +function testRemoveRows() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 1, height: 2});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  selectedCell.id = 'selected';
    +  assertEquals('Table should have two rows', 2, table.rows.length);
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.REMOVE_ROWS);
    +  assertEquals('A row should have been removed', 1, table.rows.length);
    +  assertNull('The correct row should have been removed',
    +             goog.dom.getElement('selected'));
    +
    +  // Verify that the table is removed if we don't have any rows.
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.REMOVE_ROWS);
    +  assertEquals('The table should have been removed',
    +               0,
    +               field.getElementsByTagName('table').length);
    +  fieldMock.$verify();
    +}
    +
    +function testRemoveColumns() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 2, height: 1});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  selectedCell.id = 'selected';
    +  assertEquals('Table should have two cells', 2, getCellCount(table));
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.REMOVE_COLUMNS);
    +  assertEquals('A cell should have been removed', 1, getCellCount(table));
    +  assertNull('The correct cell should have been removed',
    +             goog.dom.getElement('selected'));
    +
    +  // Verify that the table is removed if we don't have any columns.
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.REMOVE_COLUMNS);
    +  assertEquals('The table should have been removed',
    +               0,
    +               field.getElementsByTagName('table').length);
    +  fieldMock.$verify();
    +}
    +
    +function testSplitCell() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 1, height: 1});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  // Splitting is only supported if we set these attributes.
    +  selectedCell.rowSpan = '1';
    +  selectedCell.colSpan = '2';
    +  selectedCell.innerHTML = 'foo';
    +  goog.dom.Range.createFromNodeContents(selectedCell).select();
    +  assertEquals('Table should have one cell', 1, getCellCount(table));
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.SPLIT_CELL);
    +  assertEquals('The cell should have been split', 2, getCellCount(table));
    +  assertEquals('The cell content should be intact',
    +               'foo',
    +               selectedCell.innerHTML);
    +  assertNotNull('The new cell should be inserted before',
    +      selectedCell.previousSibling);
    +  fieldMock.$verify();
    +}
    +
    +function testMergeCells() {
    +  fieldMock.$replay();
    +  createTableAndSelectCell({width: 2, height: 1});
    +  var table = plugin.getCurrentTable_();
    +  var selectedCell = fieldMock.getRange().getContainerElement();
    +  selectedCell.innerHTML = 'foo';
    +  selectedCell.nextSibling.innerHTML = 'bar';
    +  var range = goog.dom.Range.createFromNodeContents(
    +      table.getElementsByTagName('tr')[0]);
    +  range.select();
    +  plugin.execCommandInternal(
    +      goog.editor.plugins.TableEditor.COMMAND.MERGE_CELLS);
    +  expectedFailures.expectFailureFor(
    +      goog.userAgent.IE &&
    +      goog.userAgent.isVersionOrHigher('8'));
    +  try {
    +    // In IE8, even after explicitly setting the range to span
    +    // multiple cells, the browser selection only contains the first TD
    +    // which causes the merge operation to fail.
    +    assertEquals('The cells should be merged', 1, getCellCount(table));
    +    assertEquals('The cell should have expected colspan',
    +                 2,
    +                 selectedCell.colSpan);
    +    assertHTMLEquals('The content should be merged',
    +                     'foo bar',
    +                     selectedCell.innerHTML);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  fieldMock.$verify();
    +}
    +
    +
    +/**
    + * Helper routine which returns the number of cells in the table.
    + *
    + * @param {Element} table The table in question.
    + * @return {number} Number of cells.
    + */
    +function getCellCount(table) {
    +  return table.cells ? table.cells.length :
    +      table.rows[0].cells.length * table.rows.length;
    +}
    +
    +
    +/**
    + * Helper method which creates a table and puts the cursor on the first TD.
    + * In IE, the cursor isn't positioned in the first cell (TD) and we simulate
    + * that behavior explicitly to be consistent across all browsers.
    + *
    + * @param {Object} op_tableProps Optional table properties.
    + */
    +function createTableAndSelectCell(opt_tableProps) {
    +  goog.dom.Range.createCaret(field, 1).select();
    +  plugin.execCommandInternal(goog.editor.plugins.TableEditor.COMMAND.TABLE,
    +                             opt_tableProps);
    +  if (goog.userAgent.IE) {
    +    var range = goog.dom.Range.createFromNodeContents(
    +        field.getElementsByTagName('td')[0]);
    +    range.select();
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler.js
    new file mode 100644
    index 0000000000..e5776fdbb4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler.js
    @@ -0,0 +1,744 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview TrogEdit plugin to handle enter keys by inserting the
    + * specified block level tag.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.editor.plugins.TagOnEnterHandler');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.Command');
    +goog.require('goog.editor.node');
    +goog.require('goog.editor.plugins.EnterHandler');
    +goog.require('goog.editor.range');
    +goog.require('goog.editor.style');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.functions');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Plugin to handle enter keys. This subclass normalizes all browsers to use
    + * the given block tag on enter.
    + * @param {goog.dom.TagName} tag The type of tag to add on enter.
    + * @constructor
    + * @extends {goog.editor.plugins.EnterHandler}
    + */
    +goog.editor.plugins.TagOnEnterHandler = function(tag) {
    +  this.tag = tag;
    +
    +  goog.editor.plugins.EnterHandler.call(this);
    +};
    +goog.inherits(goog.editor.plugins.TagOnEnterHandler,
    +    goog.editor.plugins.EnterHandler);
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.getTrogClassId = function() {
    +  return 'TagOnEnterHandler';
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.getNonCollapsingBlankHtml =
    +    function() {
    +  if (this.tag == goog.dom.TagName.P) {
    +    return '<p>&nbsp;</p>';
    +  } else if (this.tag == goog.dom.TagName.DIV) {
    +    return '<div><br></div>';
    +  }
    +  return '<br>';
    +};
    +
    +
    +/**
    + * This plugin is active on uneditable fields so it can provide a value for
    + * queryCommandValue calls asking for goog.editor.Command.BLOCKQUOTE.
    + * @return {boolean} True.
    + * @override
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.activeOnUneditableFields =
    +    goog.functions.TRUE;
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.isSupportedCommand = function(
    +    command) {
    +  return command == goog.editor.Command.DEFAULT_TAG;
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.queryCommandValue = function(
    +    command) {
    +  return command == goog.editor.Command.DEFAULT_TAG ? this.tag : null;
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.handleBackspaceInternal =
    +    function(e, range) {
    +  goog.editor.plugins.TagOnEnterHandler.superClass_.handleBackspaceInternal.
    +      call(this, e, range);
    +
    +  if (goog.userAgent.GECKO) {
    +    this.markBrToNotBeRemoved_(range, true);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.processParagraphTagsInternal =
    +    function(e, split) {
    +  if ((goog.userAgent.OPERA || goog.userAgent.IE) &&
    +      this.tag != goog.dom.TagName.P) {
    +    this.ensureBlockIeOpera(this.tag);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.handleDeleteGecko = function(
    +    e) {
    +  var range = this.getFieldObject().getRange();
    +  var container = goog.editor.style.getContainer(
    +      range && range.getContainerElement());
    +  if (this.getFieldObject().getElement().lastChild == container &&
    +      goog.editor.plugins.EnterHandler.isBrElem(container)) {
    +    // Don't delete if it's the last node in the field and just has a BR.
    +    e.preventDefault();
    +    // TODO(user): I think we probably don't need to stopPropagation here
    +    e.stopPropagation();
    +  } else {
    +    // Go ahead with deletion.
    +    // Prevent an existing BR immediately following the selection being deleted
    +    // from being removed in the keyup stage (as opposed to a BR added by FF
    +    // after deletion, which we do remove).
    +    this.markBrToNotBeRemoved_(range, false);
    +    // Manually delete the selection if it's at a BR.
    +    this.deleteBrGecko(e);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.handleKeyUpInternal = function(
    +    e) {
    +  if (goog.userAgent.GECKO) {
    +    if (e.keyCode == goog.events.KeyCodes.DELETE) {
    +      this.removeBrIfNecessary_(false);
    +    } else if (e.keyCode == goog.events.KeyCodes.BACKSPACE) {
    +      this.removeBrIfNecessary_(true);
    +    }
    +  } else if ((goog.userAgent.IE || goog.userAgent.OPERA) &&
    +             e.keyCode == goog.events.KeyCodes.ENTER) {
    +    this.ensureBlockIeOpera(this.tag, true);
    +  }
    +  // Safari uses DIVs by default.
    +};
    +
    +
    +/**
    + * String that matches a single BR tag or NBSP surrounded by non-breaking
    + * whitespace
    + * @type {string}
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.BrOrNbspSurroundedWithWhiteSpace_ =
    +    '[\t\n\r ]*(<br[^>]*\/?>|&nbsp;)[\t\n\r ]*';
    +
    +
    +/**
    + * String that matches a single BR tag or NBSP surrounded by non-breaking
    + * whitespace
    + * @type {RegExp}
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.emptyLiRegExp_ = new RegExp('^' +
    +    goog.editor.plugins.TagOnEnterHandler.BrOrNbspSurroundedWithWhiteSpace_ +
    +    '$');
    +
    +
    +/**
    + * Ensures the current node is wrapped in the tag.
    + * @param {Node} node The node to ensure gets wrapped.
    + * @param {Element} container Element containing the selection.
    + * @return {Element} Element containing the selection, after the wrapping.
    +  * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.ensureNodeIsWrappedW3c_ =
    +    function(node, container) {
    +  if (container == this.getFieldObject().getElement()) {
    +    // If the first block-level ancestor of cursor is the field,
    +    // don't split the tree. Find all the text from the cursor
    +    // to both block-level elements surrounding it (if they exist)
    +    // and split the text into two elements.
    +    // This is the IE contentEditable behavior.
    +
    +    // The easy way to do this is to wrap all the text in an element
    +    // and then split the element as if the user had hit enter
    +    // in the paragraph
    +
    +    // However, simply wrapping the text into an element creates problems
    +    // if the text was already wrapped using some other element such as an
    +    // anchor.  For example, wrapping the text of
    +    //   <a href="">Text</a>
    +    // would produce
    +    //   <a href=""><p>Text</p></a>
    +    // which is not what we want.  What we really want is
    +    //   <p><a href="">Text</a></p>
    +    // So we need to search for an ancestor of position.node to be wrapped.
    +    // We do this by iterating up the hierarchy of postiion.node until we've
    +    // reached the node that's just under the container.
    +    var isChildOfFn = function(child) {
    +      return container == child.parentNode; };
    +    var nodeToWrap = goog.dom.getAncestor(node, isChildOfFn, true);
    +    container = goog.editor.plugins.TagOnEnterHandler.wrapInContainerW3c_(
    +        this.tag, {node: nodeToWrap, offset: 0}, container);
    +  }
    +  return container;
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.handleEnterWebkitInternal =
    +    function(e) {
    +  if (this.tag == goog.dom.TagName.DIV) {
    +    var range = this.getFieldObject().getRange();
    +    var container =
    +        goog.editor.style.getContainer(range.getContainerElement());
    +
    +    var position = goog.editor.range.getDeepEndPoint(range, true);
    +    container = this.ensureNodeIsWrappedW3c_(position.node, container);
    +    goog.dom.Range.createCaret(position.node, position.offset).select();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.TagOnEnterHandler.prototype.
    +    handleEnterAtCursorGeckoInternal = function(e, wasCollapsed, range) {
    +  // We use this because there are a few cases where FF default
    +  // implementation doesn't follow IE's:
    +  //   -Inserts BRs into empty elements instead of NBSP which has nasty
    +  //    side effects w/ making/deleting selections
    +  //   -Hitting enter when your cursor is in the field itself. IE will
    +  //    create two elements. FF just inserts a BR.
    +  //   -Hitting enter inside an empty list-item doesn't create a block
    +  //    tag. It just splits the list and puts your cursor in the middle.
    +  var li = null;
    +  if (wasCollapsed) {
    +    // Only break out of lists for collapsed selections.
    +    li = goog.dom.getAncestorByTagNameAndClass(
    +        range && range.getContainerElement(), goog.dom.TagName.LI);
    +  }
    +  var isEmptyLi = (li &&
    +      li.innerHTML.match(
    +          goog.editor.plugins.TagOnEnterHandler.emptyLiRegExp_));
    +  var elementAfterCursor = isEmptyLi ?
    +      this.breakOutOfEmptyListItemGecko_(li) :
    +      this.handleRegularEnterGecko_();
    +
    +  // Move the cursor in front of "nodeAfterCursor", and make sure it
    +  // is visible
    +  this.scrollCursorIntoViewGecko_(elementAfterCursor);
    +
    +  // Fix for http://b/1991234 :
    +  if (goog.editor.plugins.EnterHandler.isBrElem(elementAfterCursor)) {
    +    // The first element in the new line is a line with just a BR and maybe some
    +    // whitespace.
    +    // Calling normalize() is needed because there might be empty text nodes
    +    // before BR and empty text nodes cause the cursor position bug in Firefox.
    +    // See http://b/5220858
    +    elementAfterCursor.normalize();
    +    var br = elementAfterCursor.getElementsByTagName(goog.dom.TagName.BR)[0];
    +    if (br.previousSibling &&
    +        br.previousSibling.nodeType == goog.dom.NodeType.TEXT) {
    +      // If there is some whitespace before the BR, don't put the selection on
    +      // the BR, put it in the text node that's there, otherwise when you type
    +      // it will create adjacent text nodes.
    +      elementAfterCursor = br.previousSibling;
    +    }
    +  }
    +
    +  goog.editor.range.selectNodeStart(elementAfterCursor);
    +
    +  e.preventDefault();
    +  // TODO(user): I think we probably don't need to stopPropagation here
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * If The cursor is in an empty LI then break out of the list like in IE
    + * @param {Node} li LI to break out of.
    + * @return {!Element} Element to put the cursor after.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.breakOutOfEmptyListItemGecko_ =
    +    function(li) {
    +  // Do this as follows:
    +  // 1. <ul>...<li>&nbsp;</li>...</ul>
    +  // 2. <ul id='foo1'>...<li id='foo2'>&nbsp;</li>...</ul>
    +  // 3. <ul id='foo1'>...</ul><p id='foo3'>&nbsp;</p><ul id='foo2'>...</ul>
    +  // 4. <ul>...</ul><p>&nbsp;</p><ul>...</ul>
    +  //
    +  // There are a couple caveats to the above. If the UL is contained in
    +  // a list, then the new node inserted is an LI, not a P.
    +  // For an OL, it's all the same, except the tagname of course.
    +  // Finally, it's possible that with the LI at the beginning or the end
    +  // of the list that we'll end up with an empty list. So we special case
    +  // those cases.
    +
    +  var listNode = li.parentNode;
    +  var grandparent = listNode.parentNode;
    +  var inSubList = grandparent.tagName == goog.dom.TagName.OL ||
    +      grandparent.tagName == goog.dom.TagName.UL;
    +
    +  // TODO(robbyw): Should we apply the list or list item styles to the new node?
    +  var newNode = goog.dom.getDomHelper(li).createElement(
    +      inSubList ? goog.dom.TagName.LI : this.tag);
    +
    +  if (!li.previousSibling) {
    +    goog.dom.insertSiblingBefore(newNode, listNode);
    +  } else {
    +    if (li.nextSibling) {
    +      var listClone = listNode.cloneNode(false);
    +      while (li.nextSibling) {
    +        listClone.appendChild(li.nextSibling);
    +      }
    +      goog.dom.insertSiblingAfter(listClone, listNode);
    +    }
    +    goog.dom.insertSiblingAfter(newNode, listNode);
    +  }
    +  if (goog.editor.node.isEmpty(listNode)) {
    +    goog.dom.removeNode(listNode);
    +  }
    +  goog.dom.removeNode(li);
    +  newNode.innerHTML = '&nbsp;';
    +
    +  return newNode;
    +};
    +
    +
    +/**
    + * Wrap the text indicated by "position" in an HTML container of type
    + * "nodeName".
    + * @param {string} nodeName Type of container, e.g. "p" (paragraph).
    + * @param {Object} position The W3C cursor position object
    + *     (from getCursorPositionW3c).
    + * @param {Node} container The field containing position.
    + * @return {!Element} The container element that holds the contents from
    + *     position.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.wrapInContainerW3c_ = function(nodeName,
    +    position, container) {
    +  var start = position.node;
    +  while (start.previousSibling &&
    +         !goog.editor.style.isContainer(start.previousSibling)) {
    +    start = start.previousSibling;
    +  }
    +
    +  var end = position.node;
    +  while (end.nextSibling &&
    +         !goog.editor.style.isContainer(end.nextSibling)) {
    +    end = end.nextSibling;
    +  }
    +
    +  var para = container.ownerDocument.createElement(nodeName);
    +  while (start != end) {
    +    var newStart = start.nextSibling;
    +    goog.dom.appendChild(para, start);
    +    start = newStart;
    +  }
    +  var nextSibling = end.nextSibling;
    +  goog.dom.appendChild(para, end);
    +  container.insertBefore(para, nextSibling);
    +
    +  return para;
    +};
    +
    +
    +/**
    + * When we delete an element, FF inserts a BR. We want to strip that
    + * BR after the fact, but in the case where your cursor is at a character
    + * right before a BR and you delete that character, we don't want to
    + * strip it. So we detect this case on keydown and mark the BR as not needing
    + * removal.
    + * @param {goog.dom.AbstractRange} range The closure range object.
    + * @param {boolean} isBackspace Whether this is handling the backspace key.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.markBrToNotBeRemoved_ =
    +    function(range, isBackspace) {
    +  var focusNode = range.getFocusNode();
    +  var focusOffset = range.getFocusOffset();
    +  var newEndOffset = isBackspace ? focusOffset : focusOffset + 1;
    +
    +  if (goog.editor.node.getLength(focusNode) == newEndOffset) {
    +    var sibling = focusNode.nextSibling;
    +    if (sibling && sibling.tagName == goog.dom.TagName.BR) {
    +      this.brToKeep_ = sibling;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * If we hit delete/backspace to merge elements, FF inserts a BR.
    + * We want to strip that BR. In markBrToNotBeRemoved, we detect if
    + * there was already a BR there before the delete/backspace so that
    + * we don't accidentally remove a user-inserted BR.
    + * @param {boolean} isBackSpace Whether this is handling the backspace key.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.removeBrIfNecessary_ = function(
    +    isBackSpace) {
    +  var range = this.getFieldObject().getRange();
    +  var focusNode = range.getFocusNode();
    +  var focusOffset = range.getFocusOffset();
    +
    +  var sibling;
    +  if (isBackSpace && focusNode.data == '') {
    +    // nasty hack. sometimes firefox will backspace a paragraph and put
    +    // the cursor before the BR. when it does this, the focusNode is
    +    // an empty textnode.
    +    sibling = focusNode.nextSibling;
    +  } else if (isBackSpace && focusOffset == 0) {
    +    var node = focusNode;
    +    while (node && !node.previousSibling &&
    +           node.parentNode != this.getFieldObject().getElement()) {
    +      node = node.parentNode;
    +    }
    +    sibling = node.previousSibling;
    +  } else if (focusNode.length == focusOffset) {
    +    sibling = focusNode.nextSibling;
    +  }
    +
    +  if (!sibling || sibling.tagName != goog.dom.TagName.BR ||
    +      this.brToKeep_ == sibling) {
    +    return;
    +  }
    +
    +  goog.dom.removeNode(sibling);
    +  if (focusNode.nodeType == goog.dom.NodeType.TEXT) {
    +    // Sometimes firefox inserts extra whitespace. Do our best to deal.
    +    // This is buggy though.
    +    focusNode.data =
    +        goog.editor.plugins.TagOnEnterHandler.trimTabsAndLineBreaks_(
    +            focusNode.data);
    +    // When we strip whitespace, make sure that our cursor is still at
    +    // the end of the textnode.
    +    goog.dom.Range.createCaret(focusNode,
    +        Math.min(focusOffset, focusNode.length)).select();
    +  }
    +};
    +
    +
    +/**
    + * Trim the tabs and line breaks from a string.
    + * @param {string} string String to trim.
    + * @return {string} Trimmed string.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.trimTabsAndLineBreaks_ = function(
    +    string) {
    +  return string.replace(/^[\t\n\r]|[\t\n\r]$/g, '');
    +};
    +
    +
    +/**
    + * Called in response to a normal enter keystroke. It has the action of
    + * splitting elements.
    + * @return {Element} The node that the cursor should be before.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.handleRegularEnterGecko_ =
    +    function() {
    +  var range = this.getFieldObject().getRange();
    +  var container =
    +      goog.editor.style.getContainer(range.getContainerElement());
    +  var newNode;
    +  if (goog.editor.plugins.EnterHandler.isBrElem(container)) {
    +    if (container.tagName == goog.dom.TagName.BODY) {
    +      // If the field contains only a single BR, this code ensures we don't
    +      // try to clone the body tag.
    +      container = this.ensureNodeIsWrappedW3c_(
    +          container.getElementsByTagName(goog.dom.TagName.BR)[0],
    +          container);
    +    }
    +
    +    newNode = container.cloneNode(true);
    +    goog.dom.insertSiblingAfter(newNode, container);
    +  } else {
    +    if (!container.firstChild) {
    +      container.innerHTML = '&nbsp;';
    +    }
    +
    +    var position = goog.editor.range.getDeepEndPoint(range, true);
    +    container = this.ensureNodeIsWrappedW3c_(position.node, container);
    +
    +    newNode = goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_(
    +        position.node, position.offset, container);
    +
    +    // If the left half and right half of the splitted node are anchors then
    +    // that means the user pressed enter while the caret was inside
    +    // an anchor tag and split it.  The left half is the first anchor
    +    // found while traversing the right branch of container.  The right half
    +    // is the first anchor found while traversing the left branch of newNode.
    +    var leftAnchor =
    +        goog.editor.plugins.TagOnEnterHandler.findAnchorInTraversal_(
    +            container);
    +    var rightAnchor =
    +        goog.editor.plugins.TagOnEnterHandler.findAnchorInTraversal_(
    +            newNode, true);
    +    if (leftAnchor && rightAnchor &&
    +        leftAnchor.tagName == goog.dom.TagName.A &&
    +        rightAnchor.tagName == goog.dom.TagName.A) {
    +      // If the original anchor (left anchor) is now empty, that means
    +      // the user pressed [Enter] at the beginning of the anchor,
    +      // in which case we we
    +      // want to replace that anchor with its child nodes
    +      // Otherwise, we take the second half of the splitted text and break
    +      // it out of the anchor.
    +      var anchorToRemove = goog.editor.node.isEmpty(leftAnchor, false) ?
    +          leftAnchor : rightAnchor;
    +      goog.dom.flattenElement(/** @type {!Element} */ (anchorToRemove));
    +    }
    +  }
    +  return /** @type {!Element} */ (newNode);
    +};
    +
    +
    +/**
    + * Scroll the cursor into view, resulting from splitting the paragraph/adding
    + * a br. It behaves differently than scrollIntoView
    + * @param {Element} element The element immediately following the cursor. Will
    + *     be used to determine how to scroll in order to make the cursor visible.
    + *     CANNOT be a BR, as they do not have offsetHeight/offsetTop.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.prototype.scrollCursorIntoViewGecko_ =
    +    function(element) {
    +  if (!this.getFieldObject().isFixedHeight()) {
    +    return; // Only need to scroll fixed height fields.
    +  }
    +
    +  var field = this.getFieldObject().getElement();
    +
    +  // Get the y position of the element we want to scroll to
    +  var elementY = goog.style.getPageOffsetTop(element);
    +
    +  // Determine the height of that element, since we want the bottom of the
    +  // element to be in view.
    +  var bottomOfNode = elementY + element.offsetHeight;
    +
    +  var dom = this.getFieldDomHelper();
    +  var win = this.getFieldDomHelper().getWindow();
    +  var scrollY = dom.getDocumentScroll().y;
    +  var viewportHeight = goog.dom.getViewportSize(win).height;
    +
    +  // If the botom of the element is outside the viewport, move it into view
    +  if (bottomOfNode > viewportHeight + scrollY) {
    +    // In standards mode, use the html element and not the body
    +    if (field.tagName == goog.dom.TagName.BODY &&
    +        goog.editor.node.isStandardsMode(field)) {
    +      field = field.parentNode;
    +    }
    +    field.scrollTop = bottomOfNode - viewportHeight;
    +  }
    +};
    +
    +
    +/**
    + * Splits the DOM tree around the given node and returns the node
    + * containing the second half of the tree. The first half of the tree
    + * is modified, but not removed from the DOM.
    + * @param {Node} positionNode Node to split at.
    + * @param {number} positionOffset Offset into positionNode to split at.  If
    + *     positionNode is a text node, this offset is an offset in to the text
    + *     content of that node.  Otherwise, positionOffset is an offset in to
    + *     the childNodes array.  All elements with child index of  positionOffset
    + *     or greater will be moved to the second half.  If positionNode is an
    + *     empty element, the dom will be split at that element, with positionNode
    + *     ending up in the second half.  positionOffset must be 0 in this case.
    + * @param {Node=} opt_root Node at which to stop splitting the dom (the root
    + *     is also split).
    + * @return {!Node} The node containing the second half of the tree.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.splitDom_ = function(
    +    positionNode, positionOffset, opt_root) {
    +  if (!opt_root) opt_root = positionNode.ownerDocument.body;
    +
    +  // Split the node.
    +  var textSplit = positionNode.nodeType == goog.dom.NodeType.TEXT;
    +  var secondHalfOfSplitNode;
    +  if (textSplit) {
    +    if (goog.userAgent.IE &&
    +        positionOffset == positionNode.nodeValue.length) {
    +      // Since splitText fails in IE at the end of a node, we split it manually.
    +      secondHalfOfSplitNode = goog.dom.getDomHelper(positionNode).
    +          createTextNode('');
    +      goog.dom.insertSiblingAfter(secondHalfOfSplitNode, positionNode);
    +    } else {
    +      secondHalfOfSplitNode = positionNode.splitText(positionOffset);
    +    }
    +  } else {
    +    // Here we ensure positionNode is the last node in the first half of the
    +    // resulting tree.
    +    if (positionOffset) {
    +      // Use offset as an index in to childNodes.
    +      positionNode = positionNode.childNodes[positionOffset - 1];
    +    } else {
    +      // In this case, positionNode would be the last node in the first half
    +      // of the tree, but we actually want to move it to the second half.
    +      // Therefore we set secondHalfOfSplitNode to the same node.
    +      positionNode = secondHalfOfSplitNode = positionNode.firstChild ||
    +          positionNode;
    +    }
    +  }
    +
    +  // Create second half of the tree.
    +  var secondHalf = goog.editor.node.splitDomTreeAt(
    +      positionNode, secondHalfOfSplitNode, opt_root);
    +
    +  if (textSplit) {
    +    // Join secondHalfOfSplitNode and its right text siblings together and
    +    // then replace leading NonNbspWhiteSpace with a Nbsp.  If
    +    // secondHalfOfSplitNode has a right sibling that isn't a text node,
    +    // then we can leave secondHalfOfSplitNode empty.
    +    secondHalfOfSplitNode =
    +        goog.editor.plugins.TagOnEnterHandler.joinTextNodes_(
    +            secondHalfOfSplitNode, true);
    +    goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +        secondHalfOfSplitNode, true, !!secondHalfOfSplitNode.nextSibling);
    +
    +    // Join positionNode and its left text siblings together and then replace
    +    // trailing NonNbspWhiteSpace with a Nbsp.
    +    var firstHalf = goog.editor.plugins.TagOnEnterHandler.joinTextNodes_(
    +        positionNode, false);
    +    goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +        firstHalf, false, false);
    +  }
    +
    +  return secondHalf;
    +};
    +
    +
    +/**
    + * Splits the DOM tree around the given node and returns the node containing
    + * second half of the tree, which is appended after the old node.  The first
    + * half of the tree is modified, but not removed from the DOM.
    + * @param {Node} positionNode Node to split at.
    + * @param {number} positionOffset Offset into positionNode to split at.  If
    + *     positionNode is a text node, this offset is an offset in to the text
    + *     content of that node.  Otherwise, positionOffset is an offset in to
    + *     the childNodes array.  All elements with child index of  positionOffset
    + *     or greater will be moved to the second half.  If positionNode is an
    + *     empty element, the dom will be split at that element, with positionNode
    + *     ending up in the second half.  positionOffset must be 0 in this case.
    + * @param {Node} node Node to split.
    + * @return {!Node} The node containing the second half of the tree.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_ = function(
    +    positionNode, positionOffset, node) {
    +  var newNode = goog.editor.plugins.TagOnEnterHandler.splitDom_(
    +      positionNode, positionOffset, node);
    +  goog.dom.insertSiblingAfter(newNode, node);
    +  return newNode;
    +};
    +
    +
    +/**
    + * Joins node and its adjacent text nodes together.
    + * @param {Node} node The node to start joining.
    + * @param {boolean} moveForward Determines whether to join left siblings (false)
    + *     or right siblings (true).
    + * @return {Node} The joined text node.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.joinTextNodes_ = function(node,
    +    moveForward) {
    +  if (node && node.nodeName == '#text') {
    +    var nextNodeFn = moveForward ? 'nextSibling' : 'previousSibling';
    +    var prevNodeFn = moveForward ? 'previousSibling' : 'nextSibling';
    +    var nodeValues = [node.nodeValue];
    +    while (node[nextNodeFn] &&
    +           node[nextNodeFn].nodeType == goog.dom.NodeType.TEXT) {
    +      node = node[nextNodeFn];
    +      nodeValues.push(node.nodeValue);
    +      goog.dom.removeNode(node[prevNodeFn]);
    +    }
    +    if (!moveForward) {
    +      nodeValues.reverse();
    +    }
    +    node.nodeValue = nodeValues.join('');
    +  }
    +  return node;
    +};
    +
    +
    +/**
    + * Replaces leading or trailing spaces of a text node to a single Nbsp.
    + * @param {Node} textNode The text node to search and replace white spaces.
    + * @param {boolean} fromStart Set to true to replace leading spaces, false to
    + *     replace trailing spaces.
    + * @param {boolean} isLeaveEmpty Set to true to leave the node empty if the
    + *     text node was empty in the first place, otherwise put a Nbsp into the
    + *     text node.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_ = function(
    +    textNode, fromStart, isLeaveEmpty) {
    +  var regExp = fromStart ? /^[ \t\r\n]+/ : /[ \t\r\n]+$/;
    +  textNode.nodeValue = textNode.nodeValue.replace(regExp,
    +                                                  goog.string.Unicode.NBSP);
    +
    +  if (!isLeaveEmpty && textNode.nodeValue == '') {
    +    textNode.nodeValue = goog.string.Unicode.NBSP;
    +  }
    +};
    +
    +
    +/**
    + * Finds the first A element in a traversal from the input node.  The input
    + * node itself is not included in the search.
    + * @param {Node} node The node to start searching from.
    + * @param {boolean=} opt_useFirstChild Whether to traverse along the first child
    + *     (true) or last child (false).
    + * @return {Node} The first anchor node found in the search, or null if none
    + *     was found.
    + * @private
    + */
    +goog.editor.plugins.TagOnEnterHandler.findAnchorInTraversal_ = function(node,
    +    opt_useFirstChild) {
    +  while ((node = opt_useFirstChild ? node.firstChild : node.lastChild) &&
    +         node.tagName != goog.dom.TagName.A) {
    +    // Do nothing - advancement is handled in the condition.
    +  }
    +  return node;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.html
    new file mode 100644
    index 0000000000..0b05f2c088
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.html
    @@ -0,0 +1,32 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  Tests for goog.editor.plugins.TagOnEnterHandler
    +
    +  @author marcosalmeida@google.com
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.editor.plugins.TagOnEnterHandler jsunit tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.TagOnEnterHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <div id="field1" class="tr-field">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.js
    new file mode 100644
    index 0000000000..20f8397214
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/tagonenterhandler_test.js
    @@ -0,0 +1,546 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.TagOnEnterHandlerTest');
    +goog.setTestOnly('goog.editor.plugins.TagOnEnterHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Field');
    +goog.require('goog.editor.Plugin');
    +goog.require('goog.editor.plugins.TagOnEnterHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var savedHtml;
    +
    +var editor;
    +var field1;
    +
    +function setUp() {
    +  field1 = makeField('field1');
    +  field1.makeEditable();
    +}
    +
    +
    +/**
    + * Tests that deleting a BR that comes right before a block element works.
    + * @bug 1471096
    + */
    +function testDeleteBrBeforeBlock() {
    +  // This test only works on Gecko, because it's testing for manual deletion of
    +  // BR tags, which is done only for Gecko. For other browsers we fall through
    +  // and let the browser do the delete, which can only be tested with a robot
    +  // test (see javascript/apps/editor/tests/delete_br_robot.html).
    +  if (goog.userAgent.GECKO) {
    +
    +    field1.setHtml(false, 'one<br><br><div>two</div>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    helper.select(field1.getElement(), 2); // Between the two BR's.
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.DELETE);
    +    assertEquals('Should have deleted exactly one <br>',
    +                 'one<br><div>two</div>',
    +                 field1.getElement().innerHTML);
    +
    +  } // End if GECKO
    +}
    +
    +
    +/**
    + * Tests that deleting a BR is working normally (that the workaround for the
    + * bug is not causing double deletes).
    + * @bug 1471096
    + */
    +function testDeleteBrNormal() {
    +  // This test only works on Gecko, because it's testing for manual deletion of
    +  // BR tags, which is done only for Gecko. For other browsers we fall through
    +  // and let the browser do the delete, which can only be tested with a robot
    +  // test (see javascript/apps/editor/tests/delete_br_robot.html).
    +  if (goog.userAgent.GECKO) {
    +
    +    field1.setHtml(false, 'one<br><br><br>two');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    helper.select(field1.getElement(), 2); // Between the first and second BR's.
    +    field1.getElement().focus();
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.DELETE);
    +    assertEquals('Should have deleted exactly one <br>',
    +                 'one<br><br>two',
    +                 field1.getElement().innerHTML);
    +
    +  } // End if GECKO
    +}
    +
    +
    +/**
    + * Regression test for http://b/1991234 . Tests that when you hit enter and it
    + * creates a blank line with whitespace and a BR, the cursor is placed in the
    + * whitespace text node instead of the BR, otherwise continuing to type will
    + * create adjacent text nodes, which causes browsers to mess up some
    + * execcommands. Fix is in a Gecko-only codepath, thus test runs only for Gecko.
    + * A full test for the entire sequence that reproed the bug is in
    + * javascript/apps/editor/tests/ponenter_robot.html .
    + */
    +function testEnterCreatesBlankLine() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false, '<p>one <br></p>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    // Place caret after 'one' but keeping a space and a BR as FF does.
    +    helper.select('one ', 3);
    +    field1.getElement().focus();
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    var range = field1.getRange();
    +    assertFalse('Selection should not be in BR tag',
    +                range.getStartNode().nodeType == goog.dom.NodeType.ELEMENT &&
    +                range.getStartNode().tagName == goog.dom.TagName.BR);
    +    assertEquals('Selection should be in text node to avoid creating adjacent' +
    +                 ' text nodes',
    +        goog.dom.NodeType.TEXT, range.getStartNode().nodeType);
    +    var rangeStartNode =
    +        goog.dom.Range.createFromNodeContents(range.getStartNode());
    +    assertHTMLEquals('The value of selected text node should be replaced with' +
    +        '&nbsp;',
    +        '&nbsp;', rangeStartNode.getHtmlFragment());
    +  }
    +}
    +
    +
    +/**
    + * Regression test for http://b/3051179 . Tests that when you hit enter and it
    + * creates a blank line with a BR and the cursor is placed in P.
    + * Splitting DOM causes to make an empty text node. Then if the cursor is placed
    + * at the text node the cursor is shown at wrong location.
    + * Therefore this test checks that the cursor is not placed at an empty node.
    + * Fix is in a Gecko-only codepath, thus test runs only for Gecko.
    + */
    +function testEnterNormalizeNodes() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false, '<p>one<br></p>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    // Place caret after 'one' but keeping a BR as FF does.
    +    helper.select('one', 3);
    +    field1.getElement().focus();
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    var range = field1.getRange();
    +    assertTrue('Selection should be in P tag',
    +        range.getStartNode().nodeType == goog.dom.NodeType.ELEMENT &&
    +        range.getStartNode().tagName == goog.dom.TagName.P);
    +    assertTrue('Selection should be at the head and collapsed',
    +        range.getStartOffset() == 0 && range.isCollapsed());
    +  }
    +}
    +
    +
    +/**
    + * Verifies
    + * goog.editor.plugins.TagOnEnterHandler.prototype.handleRegularEnterGecko_
    + * when we explicitly split anchor elements. This test runs only for Gecko
    + * since this is a Gecko-only codepath.
    + */
    +function testEnterAtBeginningOfLink() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false, '<a href="/">b<br></a>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    field1.focusAndPlaceCursorAtStart();
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<p>&nbsp;</p><p><a href="/">b<br></a></p>');
    +  }
    +}
    +
    +
    +/**
    + * Verifies correct handling of pressing enter in an empty list item.
    + */
    +function testEnterInEmptyListItemInEmptyList() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false, '<ul><li>&nbsp;</li></ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[0];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches('<p>&nbsp;</p>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemAtBeginningOfList() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul style="font-weight: bold">' +
    +            '<li>&nbsp;</li>' +
    +            '<li>1</li>' +
    +            '<li>2</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[0];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<p>&nbsp;</p><ul style="font-weight: bold"><li>1</li><li>2</li></ul>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemAtEndOfList() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul style="font-weight: bold">' +
    +            '<li>1</li>' +
    +            '<li>2</li>' +
    +            '<li>&nbsp;</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[2];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<ul style="font-weight: bold"><li>1</li><li>2</li></ul><p>&nbsp;</p>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemInMiddleOfList() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul style="font-weight: bold">' +
    +            '<li>1</li>' +
    +            '<li>&nbsp;</li>' +
    +            '<li>2</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[1];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<ul style="font-weight: bold"><li>1</li></ul>' +
    +        '<p>&nbsp;</p>' +
    +        '<ul style="font-weight: bold"><li>2</li></ul>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemInSublist() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<ul style="font-weight: bold">' +
    +        '<li>1</li>' +
    +        '<li>&nbsp;</li>' +
    +        '<li>2</li>' +
    +        '</ul>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[2];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<ul style="font-weight: bold"><li>1</li></ul>' +
    +        '<li>&nbsp;</li>' +
    +        '<ul style="font-weight: bold"><li>2</li></ul>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemAtBeginningOfSublist() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<ul style="font-weight: bold">' +
    +        '<li>&nbsp;</li>' +
    +        '<li>1</li>' +
    +        '<li>2</li>' +
    +        '</ul>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[1];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<li>&nbsp;</li>' +
    +        '<ul style="font-weight: bold"><li>1</li><li>2</li></ul>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +  }
    +}
    +
    +
    +function testEnterInEmptyListItemAtEndOfSublist() {
    +  if (goog.userAgent.GECKO) {
    +    field1.setHtml(false,
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<ul style="font-weight: bold">' +
    +        '<li>1</li>' +
    +        '<li>2</li>' +
    +        '<li>&nbsp;</li>' +
    +        '</ul>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +    var helper = new goog.testing.editor.TestHelper(field1.getElement());
    +    var li = field1.getElement().getElementsByTagName(goog.dom.TagName.LI)[3];
    +    helper.select(li.firstChild, 0);
    +    goog.testing.events.fireKeySequence(field1.getElement(),
    +                                        goog.events.KeyCodes.ENTER);
    +    helper.assertHtmlMatches(
    +        '<ul>' +
    +        '<li>A</li>' +
    +        '<ul style="font-weight: bold"><li>1</li><li>2</li></ul>' +
    +        '<li>&nbsp;</li>' +
    +        '<li>B</li>' +
    +        '</ul>');
    +  }
    +}
    +
    +
    +function testPrepareContentForPOnEnter() {
    +  assertPreparedContents('hi', 'hi');
    +  assertPreparedContents(
    +      goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? '<p>&nbsp;</p>' : '',
    +      '   ');
    +}
    +
    +
    +function testPrepareContentForDivOnEnter() {
    +  assertPreparedContents('hi', 'hi', goog.dom.TagName.DIV);
    +  assertPreparedContents(
    +      goog.editor.BrowserFeature.COLLAPSES_EMPTY_NODES ? '<div><br></div>' : '',
    +      '   ',
    +      goog.dom.TagName.DIV);
    +}
    +
    +
    +/**
    + * Assert that the prepared contents matches the expected.
    + */
    +function assertPreparedContents(expected, original, opt_tag) {
    +  var field = makeField('field1', opt_tag);
    +  field.makeEditable();
    +  assertEquals(expected,
    +      field.reduceOp_(
    +          goog.editor.Plugin.Op.PREPARE_CONTENTS_HTML, original));
    +}
    +
    +
    +/**
    + * Selects the node at the given id, and simulates an ENTER keypress.
    + * @param {googe.editor.Field} field The field with the node.
    + * @param {string} id A DOM id.
    + * @return {boolean} Whether preventDefault was called on the event.
    + */
    +function selectNodeAndHitEnter(field, id) {
    +  var cursor = field.getEditableDomHelper().getElement(id);
    +  goog.dom.Range.createFromNodeContents(cursor).select();
    +  return goog.testing.events.fireKeySequence(
    +      cursor, goog.events.KeyCodes.ENTER);
    +}
    +
    +
    +/**
    + * Creates a field with only the enter handler plugged in, for testing.
    + * @param {string} id A DOM id.
    + * @param {boolean=} opt_tag The block tag to use.  Defaults to P.
    + * @return {goog.editor.Field} A field.
    + */
    +function makeField(id, opt_tag) {
    +  var field = new goog.editor.Field(id);
    +  field.registerPlugin(
    +      new goog.editor.plugins.TagOnEnterHandler(opt_tag || goog.dom.TagName.P));
    +  return field;
    +}
    +
    +
    +/**
    + * Runs a test for splitting the dom.
    + * @param {number} offset Index into the text node to split.
    + * @param {string} firstHalfString What the html of the first half of the DOM
    + *     should be.
    + * @param {string} secondHalfString What the html of the 2nd half of the DOM
    + *     should be.
    + * @param {boolean} isAppend True if the second half should be appended to the
    + *     DOM.
    + * @param {boolean=} opt_goToRoot True if the root argument for splitDom should
    + *     be excluded.
    + */
    +function helpTestSplit_(offset, firstHalfString, secondHalfString, isAppend,
    +    opt_goToBody) {
    +  var node = document.createElement('div');
    +  node.innerHTML = '<b>begin bold<i>italic</i>end bold</b>';
    +  document.body.appendChild(node);
    +
    +  var italic = node.getElementsByTagName('i')[0].firstChild;
    +
    +  var splitFn = isAppend ?
    +      goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_ :
    +      goog.editor.plugins.TagOnEnterHandler.splitDom_;
    +  var secondHalf = splitFn(italic, offset, opt_goToBody ? undefined : node);
    +
    +  if (opt_goToBody) {
    +    secondHalfString = '<div>' + secondHalfString + '</div>';
    +  }
    +
    +  assertEquals('original node should have first half of the html',
    +               firstHalfString,
    +               node.innerHTML.toLowerCase().
    +      replace(goog.string.Unicode.NBSP, '&nbsp;'));
    +  assertEquals('new node should have second half of the html',
    +               secondHalfString,
    +               secondHalf.innerHTML.toLowerCase().
    +                   replace(goog.string.Unicode.NBSP, '&nbsp;'));
    +
    +  if (isAppend) {
    +    assertTrue('second half of dom should be the original node\'s next' +
    +               'sibling', node.nextSibling == secondHalf);
    +    goog.dom.removeNode(secondHalf);
    +  }
    +
    +  goog.dom.removeNode(node);
    +}
    +
    +
    +/**
    + * Runs different cases of splitting the DOM.
    + * @param {function(number, string, string)} testFn Function that takes an
    + *     offset, firstHalfString and secondHalfString as parameters.
    + */
    +function splitDomCases_(testFn) {
    +  testFn(3, '<b>begin bold<i>ita</i></b>', '<b><i>lic</i>end bold</b>');
    +  testFn(0, '<b>begin bold<i>&nbsp;</i></b>', '<b><i>italic</i>end bold</b>');
    +  testFn(6, '<b>begin bold<i>italic</i></b>', '<b><i>&nbsp;</i>end bold</b>');
    +}
    +
    +
    +function testSplitDom() {
    +  splitDomCases_(function(offset, firstHalfString, secondHalfString) {
    +    helpTestSplit_(offset, firstHalfString, secondHalfString, false, true);
    +    helpTestSplit_(offset, firstHalfString, secondHalfString, false, false);
    +  });
    +}
    +
    +
    +function testSplitDomAndAppend() {
    +  splitDomCases_(function(offset, firstHalfString, secondHalfString) {
    +    helpTestSplit_(offset, firstHalfString, secondHalfString, true, false);
    +  });
    +}
    +
    +
    +function testSplitDomAtElement() {
    +  var node = document.createElement('div');
    +  node.innerHTML = '<div>abc<br>def</div>';
    +  document.body.appendChild(node);
    +
    +  goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_(node.firstChild, 1,
    +      node.firstChild);
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div>abc</div><div><br>def</div>',
    +      node);
    +
    +  goog.dom.removeNode(node);
    +}
    +
    +
    +function testSplitDomAtElementStart() {
    +  var node = document.createElement('div');
    +  node.innerHTML = '<div>abc<br>def</div>';
    +  document.body.appendChild(node);
    +
    +  goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_(node.firstChild, 0,
    +      node.firstChild);
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div></div><div>abc<br>def</div>',
    +      node);
    +
    +  goog.dom.removeNode(node);
    +}
    +
    +
    +function testSplitDomAtChildlessElement() {
    +  var node = document.createElement('div');
    +  node.innerHTML = '<div>abc<br>def</div>';
    +  document.body.appendChild(node);
    +
    +  var br = node.getElementsByTagName(goog.dom.TagName.BR)[0];
    +  goog.editor.plugins.TagOnEnterHandler.splitDomAndAppend_(
    +      br, 0, node.firstChild);
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div>abc</div><div><br>def</div>',
    +      node);
    +
    +  goog.dom.removeNode(node);
    +}
    +
    +function testReplaceWhiteSpaceWithNbsp() {
    +  var node = document.createElement('div');
    +  var textNode = document.createTextNode('');
    +  node.appendChild(textNode);
    +
    +  textNode.nodeValue = ' test ';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, true, false);
    +  assertHTMLEquals('&nbsp;test ', node.innerHTML);
    +
    +  textNode.nodeValue = '  test ';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, true, false);
    +  assertHTMLEquals('&nbsp;test ', node.innerHTML);
    +
    +  textNode.nodeValue = ' test ';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, false, false);
    +  assertHTMLEquals(' test&nbsp;', node.innerHTML);
    +
    +  textNode.nodeValue = ' test  ';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, false, false);
    +  assertHTMLEquals(' test&nbsp;', node.innerHTML);
    +
    +  textNode.nodeValue = '';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, false, false);
    +  assertHTMLEquals('&nbsp;', node.innerHTML);
    +
    +  textNode.nodeValue = '';
    +  goog.editor.plugins.TagOnEnterHandler.replaceWhiteSpaceWithNbsp_(
    +      node.firstChild, false, true);
    +  assertHTMLEquals('', node.innerHTML);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo.js
    new file mode 100644
    index 0000000000..d273a4c153
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo.js
    @@ -0,0 +1,1016 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Code for handling edit history (undo/redo).
    + *
    + */
    +
    +
    +goog.provide('goog.editor.plugins.UndoRedo');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeOffset');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Command');
    +goog.require('goog.editor.Field');
    +goog.require('goog.editor.Plugin');
    +goog.require('goog.editor.node');
    +goog.require('goog.editor.plugins.UndoRedoManager');
    +goog.require('goog.editor.plugins.UndoRedoState');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.log');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Encapsulates undo/redo logic using a custom undo stack (i.e. not browser
    + * built-in). Browser built-in undo stacks are too flaky (e.g. IE's gets
    + * clobbered on DOM modifications). Also, this allows interleaving non-editing
    + * commands into the undo stack via the UndoRedoManager.
    + *
    + * @param {goog.editor.plugins.UndoRedoManager=} opt_manager An undo redo
    + *    manager to be used by this plugin. If none is provided one is created.
    + * @constructor
    + * @extends {goog.editor.Plugin}
    + */
    +goog.editor.plugins.UndoRedo = function(opt_manager) {
    +  goog.editor.Plugin.call(this);
    +
    +  this.setUndoRedoManager(opt_manager ||
    +      new goog.editor.plugins.UndoRedoManager());
    +
    +  // Map of goog.editor.Field hashcode to goog.events.EventHandler
    +  this.eventHandlers_ = {};
    +
    +  this.currentStates_ = {};
    +
    +  /**
    +   * @type {?string}
    +   * @private
    +   */
    +  this.initialFieldChange_ = null;
    +
    +  /**
    +   * A copy of {@code goog.editor.plugins.UndoRedo.restoreState} bound to this,
    +   * used by undo-redo state objects to restore the state of an editable field.
    +   * @type {Function}
    +   * @see goog.editor.plugins.UndoRedo#restoreState
    +   * @private
    +   */
    +  this.boundRestoreState_ = goog.bind(this.restoreState, this);
    +};
    +goog.inherits(goog.editor.plugins.UndoRedo, goog.editor.Plugin);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.prototype.logger =
    +    goog.log.getLogger('goog.editor.plugins.UndoRedo');
    +
    +
    +/**
    + * The {@code UndoState_} whose change is in progress, null if an undo or redo
    + * is not in progress.
    + *
    + * @type {goog.editor.plugins.UndoRedo.UndoState_?}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.inProgressUndo_ = null;
    +
    +
    +/**
    + * The undo-redo stack manager used by this plugin.
    + * @type {goog.editor.plugins.UndoRedoManager}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.undoManager_;
    +
    +
    +/**
    + * The key for the event listener handling state change events from the
    + * undo-redo manager.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.managerStateChangeKey_;
    +
    +
    +/**
    + * Commands implemented by this plugin.
    + * @enum {string}
    + */
    +goog.editor.plugins.UndoRedo.COMMAND = {
    +  UNDO: '+undo',
    +  REDO: '+redo'
    +};
    +
    +
    +/**
    + * Inverse map of execCommand strings to
    + * {@link goog.editor.plugins.UndoRedo.COMMAND} constants. Used to determine
    + * whether a string corresponds to a command this plugin handles in O(1) time.
    + * @type {Object}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.SUPPORTED_COMMANDS_ =
    +    goog.object.transpose(goog.editor.plugins.UndoRedo.COMMAND);
    +
    +
    +/**
    + * Set the max undo stack depth (not the real memory usage).
    + * @param {number} depth Depth of the stack.
    + */
    +goog.editor.plugins.UndoRedo.prototype.setMaxUndoDepth = function(depth) {
    +  this.undoManager_.setMaxUndoDepth(depth);
    +};
    +
    +
    +/**
    + * Set the undo-redo manager used by this plugin. Any state on a previous
    + * undo-redo manager is lost.
    + * @param {goog.editor.plugins.UndoRedoManager} manager The undo-redo manager.
    + */
    +goog.editor.plugins.UndoRedo.prototype.setUndoRedoManager = function(manager) {
    +  if (this.managerStateChangeKey_) {
    +    goog.events.unlistenByKey(this.managerStateChangeKey_);
    +  }
    +
    +  this.undoManager_ = manager;
    +  this.managerStateChangeKey_ =
    +      goog.events.listen(this.undoManager_,
    +          goog.editor.plugins.UndoRedoManager.EventType.STATE_CHANGE,
    +          this.dispatchCommandValueChange_,
    +          false,
    +          this);
    +};
    +
    +
    +/**
    + * Whether the string corresponds to a command this plugin handles.
    + * @param {string} command Command string to check.
    + * @return {boolean} Whether the string corresponds to a command
    + *     this plugin handles.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.prototype.isSupportedCommand = function(command) {
    +  return command in goog.editor.plugins.UndoRedo.SUPPORTED_COMMANDS_;
    +};
    +
    +
    +/**
    + * Unregisters and disables the fieldObject with this plugin. Thie does *not*
    + * clobber the undo stack for the fieldObject though.
    + * TODO(user): For the multifield version, we really should add a way to
    + * ignore undo actions on field's that have been made uneditable.
    + * This is probably as simple as skipping over entries in the undo stack
    + * that have a hashcode of an uneditable field.
    + * @param {goog.editor.Field} fieldObject The field to register with the plugin.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.prototype.unregisterFieldObject = function(
    +    fieldObject) {
    +  this.disable(fieldObject);
    +  this.setFieldObject(null);
    +};
    +
    +
    +/**
    + * This is so subclasses can deal with multifield undo-redo.
    + * @return {goog.editor.Field} The active field object for this field. This is
    + *     the one registered field object for the single-plugin case and the
    + *     focused field for the multi-field plugin case.
    + */
    +goog.editor.plugins.UndoRedo.prototype.getCurrentFieldObject = function() {
    +  return this.getFieldObject();
    +};
    +
    +
    +/**
    + * This is so subclasses can deal with multifield undo-redo.
    + * @param {string} fieldHashCode The Field's hashcode.
    + * @return {goog.editor.Field} The field object with the hashcode.
    + */
    +goog.editor.plugins.UndoRedo.prototype.getFieldObjectForHash = function(
    +    fieldHashCode) {
    +  // With single field undoredo, there's only one Field involved.
    +  return this.getFieldObject();
    +};
    +
    +
    +/**
    + * This is so subclasses can deal with multifield undo-redo.
    + * @return {goog.editor.Field} Target for COMMAND_VALUE_CHANGE events.
    + */
    +goog.editor.plugins.UndoRedo.prototype.getCurrentEventTarget = function() {
    +  return this.getFieldObject();
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.enable = function(fieldObject) {
    +  if (this.isEnabled(fieldObject)) {
    +    return;
    +  }
    +
    +  // Don't want pending delayed changes from when undo-redo was disabled
    +  // firing after undo-redo is enabled since they might cause undo-redo stack
    +  // updates.
    +  fieldObject.clearDelayedChange();
    +
    +  var eventHandler = new goog.events.EventHandler(this);
    +
    +  // TODO(user): From ojan during a code review:
    +  // The beforechange handler is meant to be there so you can grab the cursor
    +  // position *before* the change is made as that's where you want the cursor to
    +  // be after an undo.
    +  //
    +  // It kinda looks like updateCurrentState_ doesn't do that correctly right
    +  // now, but it really should be fixed to do so. The cursor position stored in
    +  // the state should be the cursor position before any changes are made, not
    +  // the cursor position when the change finishes.
    +  //
    +  // It also seems like the if check below is just a bad one. We should do this
    +  // for browsers that use mutation events as well even though the beforechange
    +  // happens too late...maybe not. I don't know about this.
    +  if (!goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
    +    // We don't listen to beforechange in mutation-event browsers because
    +    // there we fire beforechange, then syncronously file change. The point
    +    // of before change is to capture before the user has changed anything.
    +    eventHandler.listen(fieldObject,
    +        goog.editor.Field.EventType.BEFORECHANGE, this.handleBeforeChange_);
    +  }
    +  eventHandler.listen(fieldObject,
    +      goog.editor.Field.EventType.DELAYEDCHANGE, this.handleDelayedChange_);
    +  eventHandler.listen(fieldObject, goog.editor.Field.EventType.BLUR,
    +      this.handleBlur_);
    +
    +  this.eventHandlers_[fieldObject.getHashCode()] = eventHandler;
    +
    +  // We want to capture the initial state of a Trogedit field before any
    +  // editing has happened. This is necessary so that we can undo the first
    +  // change to a field, even if we don't handle beforeChange.
    +  this.updateCurrentState_(fieldObject);
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.disable = function(fieldObject) {
    +  // Process any pending changes so we don't lose any undo-redo states that we
    +  // want prior to disabling undo-redo.
    +  fieldObject.clearDelayedChange();
    +
    +  var eventHandler = this.eventHandlers_[fieldObject.getHashCode()];
    +  if (eventHandler) {
    +    eventHandler.dispose();
    +    delete this.eventHandlers_[fieldObject.getHashCode()];
    +  }
    +
    +  // We delete the current state of the field on disable. When we re-enable
    +  // the state will be re-fetched. In most cases the content will be the same,
    +  // but this allows us to pick up changes while not editable. That way, when
    +  // undoing after starting an editable session, you can always undo to the
    +  // state you started in. Given this sequence of events:
    +  // Make editable
    +  // Type 'anakin'
    +  // Make not editable
    +  // Set HTML to be 'padme'
    +  // Make editable
    +  // Type 'dark side'
    +  // Undo
    +  // Without re-snapshoting current state on enable, the undo would go from
    +  // 'dark-side' -> 'anakin', rather than 'dark-side' -> 'padme'. You couldn't
    +  // undo the field to the state that existed immediately after it was made
    +  // editable for the second time.
    +  if (this.currentStates_[fieldObject.getHashCode()]) {
    +    delete this.currentStates_[fieldObject.getHashCode()];
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.isEnabled = function(fieldObject) {
    +  // All enabled plugins have a eventHandler so reuse that map rather than
    +  // storing additional enabled state.
    +  return !!this.eventHandlers_[fieldObject.getHashCode()];
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.disposeInternal = function() {
    +  goog.editor.plugins.UndoRedo.superClass_.disposeInternal.call(this);
    +
    +  for (var hashcode in this.eventHandlers_) {
    +    this.eventHandlers_[hashcode].dispose();
    +    delete this.eventHandlers_[hashcode];
    +  }
    +  this.setFieldObject(null);
    +
    +  if (this.undoManager_) {
    +    this.undoManager_.dispose();
    +    delete this.undoManager_;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.getTrogClassId = function() {
    +  return 'UndoRedo';
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.execCommand = function(command,
    +    var_args) {
    +  if (command == goog.editor.plugins.UndoRedo.COMMAND.UNDO) {
    +    this.undoManager_.undo();
    +  } else if (command == goog.editor.plugins.UndoRedo.COMMAND.REDO) {
    +    this.undoManager_.redo();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.plugins.UndoRedo.prototype.queryCommandValue = function(command) {
    +  var state = null;
    +  if (command == goog.editor.plugins.UndoRedo.COMMAND.UNDO) {
    +    state = this.undoManager_.hasUndoState();
    +  } else if (command == goog.editor.plugins.UndoRedo.COMMAND.REDO) {
    +    state = this.undoManager_.hasRedoState();
    +  }
    +  return state;
    +};
    +
    +
    +/**
    + * Dispatches the COMMAND_VALUE_CHANGE event on the editable field or the field
    + * manager, as appropriate.
    + * Note: Really, people using multi field mode should be listening directly
    + * to the undo-redo manager for events.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.dispatchCommandValueChange_ =
    +    function() {
    +  var eventTarget = this.getCurrentEventTarget();
    +  eventTarget.dispatchEvent({
    +    type: goog.editor.Field.EventType.COMMAND_VALUE_CHANGE,
    +    commands: [goog.editor.plugins.UndoRedo.COMMAND.REDO,
    +      goog.editor.plugins.UndoRedo.COMMAND.UNDO]});
    +};
    +
    +
    +/**
    + * Restores the state of the editable field.
    + * @param {goog.editor.plugins.UndoRedo.UndoState_} state The state initiating
    + *    the restore.
    + * @param {string} content The content to restore.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_?} cursorPosition
    + *     The cursor position within the content.
    + */
    +goog.editor.plugins.UndoRedo.prototype.restoreState = function(
    +    state, content, cursorPosition) {
    +  // Fire any pending changes to get the current field state up to date and
    +  // then stop listening to changes while doing the undo/redo.
    +  var fieldObj = this.getFieldObjectForHash(state.fieldHashCode);
    +  if (!fieldObj) {
    +    return;
    +  }
    +
    +  // Fires any pending changes, and stops the change events. Still want to
    +  // dispatch before change, as a change is being made and the change event
    +  // will be manually dispatched below after the new content has been restored
    +  // (also restarting change events).
    +  fieldObj.stopChangeEvents(true, true);
    +
    +  // To prevent the situation where we stop change events and then an exception
    +  // happens before we can restart change events, the following code must be in
    +  // a try-finally block.
    +  try {
    +    fieldObj.dispatchBeforeChange();
    +
    +    // Restore the state
    +    fieldObj.execCommand(goog.editor.Command.CLEAR_LOREM, true);
    +
    +    // We specifically set the raw innerHTML of the field here as that's what
    +    // we get from the field when we save an undo/redo state. There's
    +    // no need to clean/unclean the contents in either direction.
    +    goog.editor.node.replaceInnerHtml(fieldObj.getElement(), content);
    +
    +    if (cursorPosition) {
    +      cursorPosition.select();
    +    }
    +
    +    var previousFieldObject = this.getCurrentFieldObject();
    +    fieldObj.focus();
    +
    +    // Apps that integrate their undo-redo with Trogedit may be
    +    // in a state where there is no previous field object (no field focused at
    +    // the time of undo), so check for existence first.
    +    if (previousFieldObject &&
    +        previousFieldObject.getHashCode() != state.fieldHashCode) {
    +      previousFieldObject.execCommand(goog.editor.Command.UPDATE_LOREM);
    +    }
    +
    +    // We need to update currentState_ to reflect the change.
    +    this.currentStates_[state.fieldHashCode].setUndoState(
    +        content, cursorPosition);
    +  } catch (e) {
    +    goog.log.error(this.logger, 'Error while restoring undo state', e);
    +  } finally {
    +    // Clear the delayed change event, set flag so we know not to act on it.
    +    this.inProgressUndo_ = state;
    +    // Notify the editor that we've changed (fire autosave).
    +    // Note that this starts up change events again, so we don't have to
    +    // manually do so even though we stopped change events above.
    +    fieldObj.dispatchChange();
    +    fieldObj.dispatchSelectionChangeEvent();
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.prototype.handleKeyboardShortcut = function(e, key,
    +    isModifierPressed) {
    +  if (isModifierPressed) {
    +    var command;
    +    if (key == 'z') {
    +      command = e.shiftKey ? goog.editor.plugins.UndoRedo.COMMAND.REDO :
    +          goog.editor.plugins.UndoRedo.COMMAND.UNDO;
    +    } else if (key == 'y') {
    +      command = goog.editor.plugins.UndoRedo.COMMAND.REDO;
    +    }
    +
    +    if (command) {
    +      // In the case where Trogedit shares its undo redo stack with another
    +      // application it's possible that an undo or redo will not be for an
    +      // goog.editor.Field. In this case we don't want to go through the
    +      // goog.editor.Field execCommand flow which stops and restarts events on
    +      // the current field. Only Trogedit UndoState's have a fieldHashCode so
    +      // use that to distinguish between Trogedit and other states.
    +      var state = command == goog.editor.plugins.UndoRedo.COMMAND.UNDO ?
    +          this.undoManager_.undoPeek() : this.undoManager_.redoPeek();
    +      if (state && state.fieldHashCode) {
    +        this.getCurrentFieldObject().execCommand(command);
    +      } else {
    +        this.execCommand(command);
    +      }
    +
    +      return true;
    +    }
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Clear the undo/redo stack.
    + */
    +goog.editor.plugins.UndoRedo.prototype.clearHistory = function() {
    +  // Fire all pending change events, so that they don't come back
    +  // asynchronously to fill the queue.
    +  this.getFieldObject().stopChangeEvents(true, true);
    +  this.undoManager_.clearHistory();
    +  this.getFieldObject().startChangeEvents();
    +};
    +
    +
    +/**
    + * Refreshes the current state of the editable field as maintained by undo-redo,
    + * without adding any undo-redo states to the stack.
    + * @param {goog.editor.Field} fieldObject The editable field.
    + */
    +goog.editor.plugins.UndoRedo.prototype.refreshCurrentState = function(
    +    fieldObject) {
    +  if (this.isEnabled(fieldObject)) {
    +    if (this.currentStates_[fieldObject.getHashCode()]) {
    +      delete this.currentStates_[fieldObject.getHashCode()];
    +    }
    +    this.updateCurrentState_(fieldObject);
    +  }
    +};
    +
    +
    +/**
    + * Before the field changes, we want to save the state.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.handleBeforeChange_ = function(e) {
    +  if (this.inProgressUndo_) {
    +    // We are in between a previous undo and its delayed change event.
    +    // Continuing here clobbers the redo stack.
    +    // This does mean that if you are trying to undo/redo really quickly, it
    +    // will be gated by the speed of delayed change events.
    +    return;
    +  }
    +
    +  var fieldObj = /** @type {goog.editor.Field} */ (e.target);
    +  var fieldHashCode = fieldObj.getHashCode();
    +
    +  if (this.initialFieldChange_ != fieldHashCode) {
    +    this.initialFieldChange_ = fieldHashCode;
    +    this.updateCurrentState_(fieldObj);
    +  }
    +};
    +
    +
    +/**
    + * After some idle time, we want to save the state.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.handleDelayedChange_ = function(e) {
    +  // This was undo making a change, don't add it BACK into the history
    +  if (this.inProgressUndo_) {
    +    // Must clear this.inProgressUndo_ before dispatching event because the
    +    // dispatch can cause another, queued undo that should be allowed to go
    +    // through.
    +    var state = this.inProgressUndo_;
    +    this.inProgressUndo_ = null;
    +    state.dispatchEvent(goog.editor.plugins.UndoRedoState.ACTION_COMPLETED);
    +    return;
    +  }
    +
    +  this.updateCurrentState_(/** @type {goog.editor.Field} */ (e.target));
    +};
    +
    +
    +/**
    + * When the user blurs away, we need to save the state on that field.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.handleBlur_ = function(e) {
    +  var fieldObj = /** @type {goog.editor.Field} */ (e.target);
    +  if (fieldObj) {
    +    fieldObj.clearDelayedChange();
    +  }
    +};
    +
    +
    +/**
    + * Returns the goog.editor.plugins.UndoRedo.CursorPosition_ for the current
    + * selection in the given Field.
    + * @param {goog.editor.Field} fieldObj The field object.
    + * @return {goog.editor.plugins.UndoRedo.CursorPosition_} The CursorPosition_ or
    + *    null if there is no valid selection.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.getCursorPosition_ = function(fieldObj) {
    +  var cursorPos = new goog.editor.plugins.UndoRedo.CursorPosition_(fieldObj);
    +  if (!cursorPos.isValid()) {
    +    return null;
    +  }
    +  return cursorPos;
    +};
    +
    +
    +/**
    + * Helper method for saving state.
    + * @param {goog.editor.Field} fieldObj The field object.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.prototype.updateCurrentState_ = function(
    +    fieldObj) {
    +  var fieldHashCode = fieldObj.getHashCode();
    +  // We specifically grab the raw innerHTML of the field here as that's what
    +  // we would set on the field in the case of an undo/redo operation. There's
    +  // no need to clean/unclean the contents in either direction. In the case of
    +  // lorem ipsum being used, we want to capture the effective state (empty, no
    +  // cursor position) rather than capturing the lorem html.
    +  var content, cursorPos;
    +  if (fieldObj.queryCommandValue(goog.editor.Command.USING_LOREM)) {
    +    content = '';
    +    cursorPos = null;
    +  } else {
    +    content = fieldObj.getElement().innerHTML;
    +    cursorPos = this.getCursorPosition_(fieldObj);
    +  }
    +
    +  var currentState = this.currentStates_[fieldHashCode];
    +  if (currentState) {
    +    // Don't create states if the content hasn't changed (spurious
    +    // delayed change). This can happen when lorem is cleared, for example.
    +    if (currentState.undoContent_ == content) {
    +      return;
    +    } else if (content == '' || currentState.undoContent_ == '') {
    +      // If lorem ipsum is on we say the contents are the empty string. However,
    +      // for an empty text shape with focus, the empty contents might not be
    +      // the same, depending on plugins. We want these two empty states to be
    +      // considered identical because to the user they are indistinguishable,
    +      // so we use fieldObj.getInjectableContents to map between them.
    +      // We cannot use getInjectableContents when first creating the undo
    +      // content for a field with lorem, because on enable when this is first
    +      // called we can't guarantee plugin registration order, so the
    +      // injectableContents at that time might not match the final
    +      // injectableContents.
    +      var emptyContents = fieldObj.getInjectableContents('', {});
    +      if (content == emptyContents && currentState.undoContent_ == '' ||
    +          currentState.undoContent_ == emptyContents && content == '') {
    +        return;
    +      }
    +    }
    +
    +    currentState.setRedoState(content, cursorPos);
    +    this.undoManager_.addState(currentState);
    +  }
    +
    +  this.currentStates_[fieldHashCode] =
    +      new goog.editor.plugins.UndoRedo.UndoState_(fieldHashCode, content,
    +          cursorPos, this.boundRestoreState_);
    +};
    +
    +
    +
    +/**
    + * This object encapsulates the state of an editable field.
    + *
    + * @param {string} fieldHashCode String the id of the field we're saving the
    + *     content of.
    + * @param {string} content String the actual text we're saving.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_?} cursorPosition
    + *     CursorPosLite object for the cursor position in the field.
    + * @param {Function} restore The function used to restore editable field state.
    + * @private
    + * @constructor
    + * @extends {goog.editor.plugins.UndoRedoState}
    + */
    +goog.editor.plugins.UndoRedo.UndoState_ = function(fieldHashCode, content,
    +    cursorPosition, restore) {
    +  goog.editor.plugins.UndoRedoState.call(this, true);
    +
    +  /**
    +   * The hash code for the field whose content is being saved.
    +   * @type {string}
    +   */
    +  this.fieldHashCode = fieldHashCode;
    +
    +  /**
    +   * The bound copy of {@code goog.editor.plugins.UndoRedo.restoreState} used by
    +   * this state.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.restore_ = restore;
    +
    +  this.setUndoState(content, cursorPosition);
    +};
    +goog.inherits(goog.editor.plugins.UndoRedo.UndoState_,
    +    goog.editor.plugins.UndoRedoState);
    +
    +
    +/**
    + * The content to restore on undo.
    + * @type {string}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.undoContent_;
    +
    +
    +/**
    + * The cursor position to restore on undo.
    + * @type {goog.editor.plugins.UndoRedo.CursorPosition_?}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.undoCursorPosition_;
    +
    +
    +/**
    + * The content to restore on redo, undefined until the state is pushed onto the
    + * undo stack.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.redoContent_;
    +
    +
    +/**
    + * The cursor position to restore on redo, undefined until the state is pushed
    + * onto the undo stack.
    + * @type {goog.editor.plugins.UndoRedo.CursorPosition_|null|undefined}
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.redoCursorPosition_;
    +
    +
    +/**
    + * Performs the undo operation represented by this state.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.undo = function() {
    +  this.restore_(this, this.undoContent_,
    +      this.undoCursorPosition_);
    +};
    +
    +
    +/**
    + * Performs the redo operation represented by this state.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.redo = function() {
    +  this.restore_(this, this.redoContent_,
    +      this.redoCursorPosition_);
    +};
    +
    +
    +/**
    + * Updates the undo portion of this state. Should only be used to update the
    + * current state of an editable field, which is not yet on the undo stack after
    + * an undo or redo operation. You should never be modifying states on the stack!
    + * @param {string} content The current content.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_?} cursorPosition
    + *     The current cursor position.
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.setUndoState = function(
    +    content, cursorPosition) {
    +  this.undoContent_ = content;
    +  this.undoCursorPosition_ = cursorPosition;
    +};
    +
    +
    +/**
    + * Adds redo information to this state. This method should be called before the
    + * state is added onto the undo stack.
    + *
    + * @param {string} content The content to restore on a redo.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_?} cursorPosition
    + *     The cursor position to restore on a redo.
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.setRedoState = function(
    +    content, cursorPosition) {
    +  this.redoContent_ = content;
    +  this.redoCursorPosition_ = cursorPosition;
    +};
    +
    +
    +/**
    + * Checks if the *contents* of two
    + * {@code goog.editor.plugins.UndoRedo.UndoState_}s are the same.  We don't
    + * bother checking the cursor position (that's not something we'd want to save
    + * anyway).
    + * @param {goog.editor.plugins.UndoRedoState} rhs The state to compare.
    + * @return {boolean} Whether the contents are the same.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.UndoState_.prototype.equals = function(rhs) {
    +  return this.fieldHashCode == rhs.fieldHashCode &&
    +      this.undoContent_ == rhs.undoContent_ &&
    +      this.redoContent_ == rhs.redoContent_;
    +};
    +
    +
    +
    +/**
    + * Stores the state of the selection in a way the survives DOM modifications
    + * that don't modify the user-interactable content (e.g. making something bold
    + * vs. typing a character).
    + *
    + * TODO(user): Completely get rid of this and use goog.dom.SavedCaretRange.
    + *
    + * @param {goog.editor.Field} field The field the selection is in.
    + * @private
    + * @constructor
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_ = function(field) {
    +  this.field_ = field;
    +
    +  var win = field.getEditableDomHelper().getWindow();
    +  var range = field.getRange();
    +  var isValidRange = !!range && range.isRangeInDocument() &&
    +      range.getWindow() == win;
    +  range = isValidRange ? range : null;
    +
    +  if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
    +    this.initW3C_(range);
    +  } else if (goog.editor.BrowserFeature.HAS_IE_RANGES) {
    +    this.initIE_(range);
    +  }
    +};
    +
    +
    +/**
    + * The standards compliant version keeps a list of childNode offsets.
    + * @param {goog.dom.AbstractRange?} range The range to save.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.initW3C_ = function(
    +    range) {
    +  this.isValid_ = false;
    +
    +  // TODO: Check if the range is in the field before trying to save it
    +  // for FF 3 contentEditable.
    +  if (!range) {
    +    return;
    +  }
    +
    +  var anchorNode = range.getAnchorNode();
    +  var focusNode = range.getFocusNode();
    +  if (!anchorNode || !focusNode) {
    +    return;
    +  }
    +
    +  var anchorOffset = range.getAnchorOffset();
    +  var anchor = new goog.dom.NodeOffset(anchorNode, this.field_.getElement());
    +
    +  var focusOffset = range.getFocusOffset();
    +  var focus = new goog.dom.NodeOffset(focusNode, this.field_.getElement());
    +
    +  // Test range direction.
    +  if (range.isReversed()) {
    +    this.startOffset_ = focus;
    +    this.startChildOffset_ = focusOffset;
    +    this.endOffset_ = anchor;
    +    this.endChildOffset_ = anchorOffset;
    +  } else {
    +    this.startOffset_ = anchor;
    +    this.startChildOffset_ = anchorOffset;
    +    this.endOffset_ = focus;
    +    this.endChildOffset_ = focusOffset;
    +  }
    +
    +  this.isValid_ = true;
    +};
    +
    +
    +/**
    + * In IE, we just keep track of the text offset (number of characters).
    + * @param {goog.dom.AbstractRange?} range The range to save.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.initIE_ = function(
    +    range) {
    +  this.isValid_ = false;
    +
    +  if (!range) {
    +    return;
    +  }
    +
    +  var ieRange = range.getTextRange(0).getBrowserRangeObject();
    +
    +  if (!goog.dom.contains(this.field_.getElement(), ieRange.parentElement())) {
    +    return;
    +  }
    +
    +  // Create a range that encompasses the contentEditable region to serve
    +  // as a reference to form ranges below.
    +  var contentEditableRange =
    +      this.field_.getEditableDomHelper().getDocument().body.createTextRange();
    +  contentEditableRange.moveToElementText(this.field_.getElement());
    +
    +  // startMarker is a range from the start of the contentEditable node to the
    +  // start of the current selection.
    +  var startMarker = ieRange.duplicate();
    +  startMarker.collapse(true);
    +  startMarker.setEndPoint('StartToStart', contentEditableRange);
    +  this.startOffset_ =
    +      goog.editor.plugins.UndoRedo.CursorPosition_.computeEndOffsetIE_(
    +          startMarker);
    +
    +  // endMarker is a range from the start of teh contentEditable node to the
    +  // end of the current selection.
    +  var endMarker = ieRange.duplicate();
    +  endMarker.setEndPoint('StartToStart', contentEditableRange);
    +  this.endOffset_ =
    +      goog.editor.plugins.UndoRedo.CursorPosition_.computeEndOffsetIE_(
    +          endMarker);
    +
    +  this.isValid_ = true;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this object is valid.
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.isValid = function() {
    +  return this.isValid_;
    +};
    +
    +
    +/**
    + * @return {string} A string representation of this object.
    + * @override
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.toString = function() {
    +  if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
    +    return 'W3C:' + this.startOffset_.toString() + '\n' +
    +        this.startChildOffset_ + ':' + this.endOffset_.toString() + '\n' +
    +        this.endChildOffset_;
    +  }
    +  return 'IE:' + this.startOffset_ + ',' + this.endOffset_;
    +};
    +
    +
    +/**
    + * Makes the browser's selection match the cursor position.
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.select = function() {
    +  var range = this.getRange_(this.field_.getElement());
    +  if (range) {
    +    if (goog.editor.BrowserFeature.HAS_IE_RANGES) {
    +      this.field_.getElement().focus();
    +    }
    +    goog.dom.Range.createFromBrowserRange(range).select();
    +  }
    +};
    +
    +
    +/**
    + * Get the range that encompases the the cursor position relative to a given
    + * base node.
    + * @param {Element} baseNode The node to get the cursor position relative to.
    + * @return {Range|TextRange|null} The browser range for this position.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.prototype.getRange_ =
    +    function(baseNode) {
    +  if (goog.editor.BrowserFeature.HAS_W3C_RANGES) {
    +    var startNode = this.startOffset_.findTargetNode(baseNode);
    +    var endNode = this.endOffset_.findTargetNode(baseNode);
    +    if (!startNode || !endNode) {
    +      return null;
    +    }
    +
    +    // Create range.
    +    return /** @type {Range} */ (
    +        goog.dom.Range.createFromNodes(startNode, this.startChildOffset_,
    +            endNode, this.endChildOffset_).getBrowserRangeObject());
    +  }
    +
    +  // Create a collapsed selection at the start of the contentEditable region,
    +  // which the offsets were calculated relative to before.  Note that we force
    +  // a text range here so we can use moveToElementText.
    +  var sel = baseNode.ownerDocument.body.createTextRange();
    +  sel.moveToElementText(baseNode);
    +  sel.collapse(true);
    +  sel.moveEnd('character', this.endOffset_);
    +  sel.moveStart('character', this.startOffset_);
    +  return sel;
    +};
    +
    +
    +/**
    + * Compute the number of characters to the end of the range in IE.
    + * @param {TextRange} range The range to compute an offset for.
    + * @return {number} The number of characters to the end of the range.
    + * @private
    + */
    +goog.editor.plugins.UndoRedo.CursorPosition_.computeEndOffsetIE_ =
    +    function(range) {
    +  var testRange = range.duplicate();
    +
    +  // The number of offset characters is a little off depending on
    +  // what type of block elements happen to be between the start of the
    +  // textedit and the cursor position.  We fudge the offset until the
    +  // two ranges match.
    +  var text = range.text;
    +  var guess = text.length;
    +
    +  testRange.collapse(true);
    +  testRange.moveEnd('character', guess);
    +
    +  // Adjust the range until the end points match.  This doesn't quite
    +  // work if we're at the end of the field so we give up after a few
    +  // iterations.
    +  var diff;
    +  var numTries = 10;
    +  while (diff = testRange.compareEndPoints('EndToEnd', range)) {
    +    guess -= diff;
    +    testRange.moveEnd('character', -diff);
    +    --numTries;
    +    if (0 == numTries) {
    +      break;
    +    }
    +  }
    +  // When we set innerHTML, blank lines become a single space, causing
    +  // the cursor position to be off by one.  So we accommodate for blank
    +  // lines.
    +  var offset = 0;
    +  var pos = text.indexOf('\n\r');
    +  while (pos != -1) {
    +    ++offset;
    +    pos = text.indexOf('\n\r', pos + 1);
    +  }
    +  return guess + offset;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.html
    new file mode 100644
    index 0000000000..a83e108c16
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.html
    @@ -0,0 +1,30 @@
    +goog.editor.plugins
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author ajp@google.com (Andy Perelson)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Trogedit Unit Tests - goog.editor.plugins.UndoRedo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.UndoRedoTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="testField">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.js
    new file mode 100644
    index 0000000000..80f2111207
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredo_test.js
    @@ -0,0 +1,516 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.UndoRedoTest');
    +goog.setTestOnly('goog.editor.plugins.UndoRedoTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.browserrange');
    +goog.require('goog.editor.Field');
    +goog.require('goog.editor.plugins.LoremIpsum');
    +goog.require('goog.editor.plugins.UndoRedo');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.jsunit');
    +
    +var mockEditableField;
    +var editableField;
    +var fieldHashCode;
    +var undoPlugin;
    +var state;
    +var mockState;
    +var commands;
    +var clock;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +function setUp() {
    +  mockEditableField = new goog.testing.StrictMock(goog.editor.Field);
    +
    +  // Update the arg list verifier for dispatchCommandValueChange to
    +  // correctly compare arguments that are arrays (or other complex objects).
    +  mockEditableField.$registerArgumentListVerifier('dispatchEvent',
    +      function(expected, args) {
    +        return goog.array.equals(expected, args,
    +            function(a, b) { assertObjectEquals(a, b); return true; });
    +      });
    +  mockEditableField.getHashCode = function() {
    +    return 'fieldId';
    +  };
    +
    +  undoPlugin = new goog.editor.plugins.UndoRedo();
    +  undoPlugin.registerFieldObject(mockEditableField);
    +  mockState = new goog.testing.StrictMock(
    +      goog.editor.plugins.UndoRedo.UndoState_);
    +  mockState.fieldHashCode = 'fieldId';
    +  mockState.isAsynchronous = function() {
    +    return false;
    +  };
    +  // Don't bother mocking the inherited event target pieces of the state.
    +  // If we don't do this, then mocked asynchronous undos are a lot harder and
    +  // that behavior is tested as part of the UndoRedoManager tests.
    +  mockState.addEventListener = goog.nullFunction;
    +
    +  commands = [
    +    goog.editor.plugins.UndoRedo.COMMAND.REDO,
    +    goog.editor.plugins.UndoRedo.COMMAND.UNDO
    +  ];
    +  state = new goog.editor.plugins.UndoRedo.UndoState_('1', '', null,
    +      goog.nullFunction);
    +
    +  clock = new goog.testing.MockClock(true);
    +
    +  editableField = new goog.editor.Field('testField');
    +  fieldHashCode = editableField.getHashCode();
    +}
    +
    +
    +function tearDown() {
    +  // Reset field so any attempted access during disposes don't cause errors.
    +  mockEditableField.$reset();
    +  clock.dispose();
    +  undoPlugin.dispose();
    +
    +  // NOTE(nicksantos): I think IE is blowing up on this call because
    +  // it is lame. It manifests its lameness by throwing an exception.
    +  // Kudos to XT for helping me to figure this out.
    +  try {
    +  } catch (e) {}
    +
    +  if (!editableField.isUneditable()) {
    +    editableField.makeUneditable();
    +  }
    +  editableField.dispose();
    +  goog.dom.getElement('testField').innerHTML = '';
    +  stubs.reset();
    +}
    +
    +
    +// undo-redo plugin tests
    +
    +
    +function testQueryCommandValue() {
    +  assertFalse('Must return false for empty undo stack.',
    +      undoPlugin.queryCommandValue(goog.editor.plugins.UndoRedo.COMMAND.UNDO));
    +
    +  assertFalse('Must return false for empty redo stack.',
    +      undoPlugin.queryCommandValue(goog.editor.plugins.UndoRedo.COMMAND.REDO));
    +
    +  undoPlugin.undoManager_.addState(mockState);
    +
    +  assertTrue('Must return true for a non-empty undo stack.',
    +      undoPlugin.queryCommandValue(goog.editor.plugins.UndoRedo.COMMAND.UNDO));
    +}
    +
    +
    +function testExecCommand() {
    +  undoPlugin.undoManager_.addState(mockState);
    +
    +  mockState.undo();
    +  mockState.$replay();
    +
    +  undoPlugin.execCommand(goog.editor.plugins.UndoRedo.COMMAND.UNDO);
    +  // Second undo should do nothing since only one item on stack.
    +  undoPlugin.execCommand(goog.editor.plugins.UndoRedo.COMMAND.UNDO);
    +  mockState.$verify();
    +
    +  mockState.$reset();
    +  mockState.redo();
    +  mockState.$replay();
    +  undoPlugin.execCommand(goog.editor.plugins.UndoRedo.COMMAND.REDO);
    +  // Second redo should do nothing since only one item on stack.
    +  undoPlugin.execCommand(goog.editor.plugins.UndoRedo.COMMAND.REDO);
    +  mockState.$verify();
    +}
    +
    +function testHandleKeyboardShortcut_TrogStates() {
    +  undoPlugin.undoManager_.addState(mockState);
    +  undoPlugin.undoManager_.addState(state);
    +  undoPlugin.undoManager_.undo();
    +  mockEditableField.$reset();
    +
    +  var stubUndoEvent = {ctrlKey: true, altKey: false, shiftKey: false};
    +  var stubRedoEvent = {ctrlKey: true, altKey: false, shiftKey: true};
    +  var stubRedoEvent2 = {ctrlKey: true, altKey: false, shiftKey: false};
    +  var result;
    +
    +  // Test handling Trogedit undos. Should always call EditableField's
    +  // execCommand. Since EditableField is mocked, this will not result in a call
    +  // to the mockState's undo and redo methods.
    +  mockEditableField.execCommand(goog.editor.plugins.UndoRedo.COMMAND.UNDO);
    +  mockEditableField.$replay();
    +  result = undoPlugin.handleKeyboardShortcut(stubUndoEvent, 'z', true);
    +  assertTrue('Plugin must return true when it handles shortcut.', result);
    +  mockEditableField.$verify();
    +  mockEditableField.$reset();
    +
    +  mockEditableField.execCommand(goog.editor.plugins.UndoRedo.COMMAND.REDO);
    +  mockEditableField.$replay();
    +  result = undoPlugin.handleKeyboardShortcut(stubRedoEvent, 'z', true);
    +  assertTrue('Plugin must return true when it handles shortcut.', result);
    +  mockEditableField.$verify();
    +  mockEditableField.$reset();
    +
    +  mockEditableField.execCommand(goog.editor.plugins.UndoRedo.COMMAND.REDO);
    +  mockEditableField.$replay();
    +  result = undoPlugin.handleKeyboardShortcut(stubRedoEvent2, 'y', true);
    +  assertTrue('Plugin must return true when it handles shortcut.', result);
    +  mockEditableField.$verify();
    +  mockEditableField.$reset();
    +
    +  mockEditableField.$replay();
    +  result = undoPlugin.handleKeyboardShortcut(stubRedoEvent2, 'y', false);
    +  assertFalse('Plugin must return false when modifier is not pressed.', result);
    +  mockEditableField.$verify();
    +  mockEditableField.$reset();
    +
    +  mockEditableField.$replay();
    +  result = undoPlugin.handleKeyboardShortcut(stubUndoEvent, 'f', true);
    +  assertFalse('Plugin must return false when it doesn\'t handle shortcut.',
    +      result);
    +  mockEditableField.$verify();
    +}
    +
    +function testHandleKeyboardShortcut_NotTrogStates() {
    +  var stubUndoEvent = {ctrlKey: true, altKey: false, shiftKey: false};
    +
    +  // Trogedit undo states all have a fieldHashCode, nulling that out makes this
    +  // state be treated as a non-Trogedit undo-redo state.
    +  state.fieldHashCode = null;
    +  undoPlugin.undoManager_.addState(state);
    +  mockEditableField.$reset();
    +
    +  // Non-trog state shouldn't go through EditableField.execCommand, however,
    +  // we still exect command value change dispatch since undo-redo plugin
    +  // redispatches those anytime manager's state changes.
    +  mockEditableField.dispatchEvent({
    +    type: goog.editor.Field.EventType.COMMAND_VALUE_CHANGE,
    +    commands: commands});
    +  mockEditableField.$replay();
    +  var result = undoPlugin.handleKeyboardShortcut(stubUndoEvent, 'z', true);
    +  assertTrue('Plugin must return true when it handles shortcut.' , result);
    +  mockEditableField.$verify();
    +}
    +
    +function testEnable() {
    +  assertFalse('Plugin must start disabled.',
    +      undoPlugin.isEnabled(editableField));
    +
    +  editableField.makeEditable(editableField);
    +  editableField.setHtml(false, '<div>a</div>');
    +  undoPlugin.enable(editableField);
    +
    +  assertTrue(undoPlugin.isEnabled(editableField));
    +  assertNotNull('Must have an event handler for enabled field.',
    +      undoPlugin.eventHandlers_[fieldHashCode]);
    +
    +  var currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertNotNull('Enabled plugin must have a current state.', currentState);
    +  assertEquals('After enable, undo content must match the field content.',
    +      editableField.getElement().innerHTML, currentState.undoContent_);
    +
    +  assertTrue('After enable, undo cursorPosition must match the field cursor' +
    +      'position.', cursorPositionsEqual(getCurrentCursorPosition(),
    +          currentState.undoCursorPosition_));
    +
    +  assertUndefined('Current state must never have redo content.',
    +      currentState.redoContent_);
    +  assertUndefined('Current state must never have redo cursor position.',
    +      currentState.redoCursorPosition_);
    +}
    +
    +function testDisable() {
    +  editableField.makeEditable(editableField);
    +  undoPlugin.enable(editableField);
    +  assertTrue('Plugin must be enabled so we can test disabling.',
    +      undoPlugin.isEnabled(editableField));
    +
    +  var delayedChangeFired = false;
    +  goog.events.listenOnce(editableField,
    +      goog.editor.Field.EventType.DELAYEDCHANGE,
    +      function(e) {
    +        delayedChangeFired = true;
    +      });
    +  editableField.setHtml(false, 'foo');
    +
    +  undoPlugin.disable(editableField);
    +  assertTrue('disable must fire pending delayed changes.', delayedChangeFired);
    +  assertEquals('disable must add undo state from pending change.',
    +      1, undoPlugin.undoManager_.undoStack_.length);
    +
    +  assertFalse(undoPlugin.isEnabled(editableField));
    +  assertUndefined('Disabled plugin must not have current state.',
    +      undoPlugin.eventHandlers_[fieldHashCode]);
    +  assertUndefined('Disabled plugin must not have event handlers.',
    +      undoPlugin.eventHandlers_[fieldHashCode]);
    +}
    +
    +function testUpdateCurrentState_() {
    +  editableField.registerPlugin(new goog.editor.plugins.LoremIpsum('LOREM'));
    +  editableField.makeEditable(editableField);
    +  editableField.getPluginByClassId('LoremIpsum').usingLorem_ = true;
    +  undoPlugin.updateCurrentState_(editableField);
    +  var currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertNotUndefined('Must create empty states for field using lorem ipsum.',
    +      undoPlugin.currentStates_[fieldHashCode]);
    +  assertEquals('', currentState.undoContent_);
    +  assertNull(currentState.undoCursorPosition_);
    +
    +  editableField.getPluginByClassId('LoremIpsum').usingLorem_ = false;
    +
    +  // Pretend foo is the default contents to test '' == default contents
    +  // behavior.
    +  editableField.getInjectableContents = function(contents, styles) {
    +    return contents == '' ? 'foo' : contents;
    +  };
    +  editableField.setHtml(false, 'foo');
    +  undoPlugin.updateCurrentState_(editableField);
    +  assertEquals(currentState, undoPlugin.currentStates_[fieldHashCode]);
    +
    +  // NOTE(user): Because there is already a current state, this setHtml will add
    +  // a state to the undo stack.
    +  editableField.setHtml(false, '<div>a</div>');
    +  // Select some text so we have a valid selection that gets saved in the
    +  // UndoState.
    +  goog.dom.browserrange.createRangeFromNodeContents(
    +      editableField.getElement()).select();
    +
    +  undoPlugin.updateCurrentState_(editableField);
    +  currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertNotNull('Must create state for field not using lorem ipsum',
    +      currentState);
    +  assertEquals(fieldHashCode, currentState.fieldHashCode);
    +  var content = editableField.getElement().innerHTML;
    +  var cursorPosition = getCurrentCursorPosition();
    +  assertEquals(content, currentState.undoContent_);
    +  assertTrue(cursorPositionsEqual(
    +      cursorPosition, currentState.undoCursorPosition_));
    +  assertUndefined(currentState.redoContent_);
    +  assertUndefined(currentState.redoCursorPosition_);
    +
    +  undoPlugin.updateCurrentState_(editableField);
    +  assertEquals('Updating state when state has not changed must not add undo ' +
    +      'state to stack.', 1, undoPlugin.undoManager_.undoStack_.length);
    +  assertEquals('Updating state when state has not changed must not create ' +
    +      'a new state.', currentState, undoPlugin.currentStates_[fieldHashCode]);
    +  assertUndefined('Updating state when state has not changed must not add ' +
    +      'redo content.', currentState.redoContent_);
    +  assertUndefined('Updating state when state has not changed must not add ' +
    +      'redo cursor position.', currentState.redoCursorPosition_);
    +
    +  editableField.setHtml(false, '<div>b</div>');
    +  undoPlugin.updateCurrentState_(editableField);
    +  currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertNotNull('Must create state for field not using lorem ipsum',
    +      currentState);
    +  assertEquals(fieldHashCode, currentState.fieldHashCode);
    +  var newContent = editableField.getElement().innerHTML;
    +  var newCursorPosition = getCurrentCursorPosition();
    +  assertEquals(newContent, currentState.undoContent_);
    +  assertTrue(cursorPositionsEqual(
    +      newCursorPosition, currentState.undoCursorPosition_));
    +  assertUndefined(currentState.redoContent_);
    +  assertUndefined(currentState.redoCursorPosition_);
    +
    +  var undoState = goog.array.peek(undoPlugin.undoManager_.undoStack_);
    +  assertNotNull('Must create state for field not using lorem ipsum',
    +      currentState);
    +  assertEquals(fieldHashCode, currentState.fieldHashCode);
    +  assertEquals(content, undoState.undoContent_);
    +  assertTrue(cursorPositionsEqual(
    +      cursorPosition, undoState.undoCursorPosition_));
    +  assertEquals(newContent, undoState.redoContent_);
    +  assertTrue(cursorPositionsEqual(
    +      newCursorPosition, undoState.redoCursorPosition_));
    +}
    +
    +
    +/**
    + * Tests that change events get restarted properly after an undo call despite
    + * an exception being thrown in the process (see bug/1991234).
    + */
    +function testUndoRestartsChangeEvents() {
    +  undoPlugin.registerFieldObject(editableField);
    +  editableField.makeEditable(editableField);
    +  editableField.setHtml(false, '<div>a</div>');
    +  clock.tick(1000);
    +  undoPlugin.enable(editableField);
    +
    +  // Change content so we can undo it.
    +  editableField.setHtml(false, '<div>b</div>');
    +  clock.tick(1000);
    +
    +  var currentState = undoPlugin.currentStates_[fieldHashCode];
    +  stubs.set(editableField, 'setCursorPosition',
    +      goog.functions.error('Faking exception during setCursorPosition()'));
    +  try {
    +    currentState.undo();
    +  } catch (e) {
    +    fail('Exception should not have been thrown during undo()');
    +  }
    +  assertEquals('Change events should be on', 0,
    +      editableField.stoppedEvents_[goog.editor.Field.EventType.CHANGE]);
    +  assertEquals('Delayed change events should be on', 0,
    +      editableField.stoppedEvents_[goog.editor.Field.EventType.DELAYEDCHANGE]);
    +}
    +
    +function testRefreshCurrentState() {
    +  editableField.makeEditable(editableField);
    +  editableField.setHtml(false, '<div>a</div>');
    +  clock.tick(1000);
    +  undoPlugin.enable(editableField);
    +
    +  // Create current state and verify it.
    +  var currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertEquals(fieldHashCode, currentState.fieldHashCode);
    +  var content = editableField.getElement().innerHTML;
    +  var cursorPosition = getCurrentCursorPosition();
    +  assertEquals(content, currentState.undoContent_);
    +  assertTrue(cursorPositionsEqual(
    +      cursorPosition, currentState.undoCursorPosition_));
    +
    +  // Update the field w/o dispatching delayed change, and verify that the
    +  // current state hasn't changed to reflect new values.
    +  editableField.setHtml(false, '<div>b</div>', true);
    +  clock.tick(1000);
    +  currentState = undoPlugin.currentStates_[fieldHashCode];
    +  assertEquals('Content must match old state.',
    +      content, currentState.undoContent_);
    +  assertTrue('Cursor position must match old state.',
    +      cursorPositionsEqual(
    +      cursorPosition, currentState.undoCursorPosition_));
    +
    +  undoPlugin.refreshCurrentState(editableField);
    +  assertFalse('Refresh must not cause states to go on the undo-redo stack.',
    +      undoPlugin.undoManager_.hasUndoState());
    +  currentState = undoPlugin.currentStates_[fieldHashCode];
    +  content = editableField.getElement().innerHTML;
    +  cursorPosition = getCurrentCursorPosition();
    +  assertEquals('Content must match current field state.',
    +      content, currentState.undoContent_);
    +  assertTrue('Cursor position must match current field state.',
    +      cursorPositionsEqual(cursorPosition, currentState.undoCursorPosition_));
    +
    +  undoPlugin.disable(editableField);
    +  assertUndefined(undoPlugin.currentStates_[fieldHashCode]);
    +  undoPlugin.refreshCurrentState(editableField);
    +  assertUndefined('Must not refresh current state of fields that do not have ' +
    +      'undo-redo enabled.', undoPlugin.currentStates_[fieldHashCode]);
    +}
    +
    +
    +/**
    + * Returns the CursorPosition for the selection currently in the Field.
    + * @return {goog.editor.plugins.UndoRedo.CursorPosition_}
    + */
    +function getCurrentCursorPosition() {
    +  return undoPlugin.getCursorPosition_(editableField);
    +}
    +
    +
    +/**
    + * Compares two cursor positions and returns whether they are equal.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_} a
    + *     A cursor position.
    + * @param {goog.editor.plugins.UndoRedo.CursorPosition_} b
    + *     A cursor position.
    + * @return {boolean} Whether the positions are equal.
    + */
    +function cursorPositionsEqual(a, b) {
    +  if (!a && !b) {
    +    return true;
    +  } else if (a && b) {
    +    return a.toString() == b.toString();
    +  }
    +  // Only one cursor position is an object, can't be equal.
    +  return false;
    +}
    +// Undo state tests
    +
    +
    +function testSetUndoState() {
    +  state.setUndoState('content', 'position');
    +  assertEquals('Undo content incorrectly set', 'content', state.undoContent_);
    +  assertEquals('Undo cursor position incorrectly set', 'position',
    +      state.undoCursorPosition_);
    +}
    +
    +function testSetRedoState() {
    +  state.setRedoState('content', 'position');
    +  assertEquals('Redo content incorrectly set', 'content', state.redoContent_);
    +  assertEquals('Redo cursor position incorrectly set', 'position',
    +      state.redoCursorPosition_);
    +}
    +
    +function testEquals() {
    +  assertTrue('A state must equal itself', state.equals(state));
    +
    +  var state2 = new goog.editor.plugins.UndoRedo.UndoState_('1', '', null);
    +  assertTrue('A state must equal a state with the same hash code and content.',
    +      state.equals(state2));
    +
    +  state2 = new goog.editor.plugins.UndoRedo.UndoState_('1', '', 'foo');
    +  assertTrue('States with different cursor positions must be equal',
    +      state.equals(state2));
    +
    +  state2.setRedoState('bar', null);
    +  assertFalse('States with different redo content must not be equal',
    +      state.equals(state2));
    +
    +  state2 = new goog.editor.plugins.UndoRedo.UndoState_('3', '', null);
    +  assertFalse('States with different field hash codes must not be equal',
    +      state.equals(state2));
    +
    +  state2 = new goog.editor.plugins.UndoRedo.UndoState_('1', 'baz', null);
    +  assertFalse('States with different undoContent must not be equal',
    +      state.equals(state2));
    +}
    +
    +
    +/** @bug 1359214 */
    +function testClearUndoHistory() {
    +  var undoRedoPlugin = new goog.editor.plugins.UndoRedo();
    +  editableField.registerPlugin(undoRedoPlugin);
    +  editableField.makeEditable(editableField);
    +
    +  editableField.dispatchChange();
    +  clock.tick(10000);
    +
    +  editableField.getElement().innerHTML = 'y';
    +  editableField.dispatchChange();
    +  assertFalse(undoRedoPlugin.undoManager_.hasUndoState());
    +
    +  clock.tick(10000);
    +  assertTrue(undoRedoPlugin.undoManager_.hasUndoState());
    +
    +  editableField.getElement().innerHTML = 'z';
    +  editableField.dispatchChange();
    +
    +  var numCalls = 0;
    +  goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
    +      function() {
    +        numCalls++;
    +      });
    +  undoRedoPlugin.clearHistory();
    +  // 1 call from stopChangeEvents(). 0 calls from startChangeEvents().
    +  assertEquals('clearHistory must not cause delayed change when none pending',
    +      1, numCalls);
    +
    +  clock.tick(10000);
    +  assertFalse(undoRedoPlugin.undoManager_.hasUndoState());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager.js
    new file mode 100644
    index 0000000000..5e054fdbd4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager.js
    @@ -0,0 +1,338 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Code for managing series of undo-redo actions in the form of
    + * {@link goog.editor.plugins.UndoRedoState}s.
    + *
    + */
    +
    +
    +goog.provide('goog.editor.plugins.UndoRedoManager');
    +goog.provide('goog.editor.plugins.UndoRedoManager.EventType');
    +
    +goog.require('goog.editor.plugins.UndoRedoState');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +
    +
    +
    +/**
    + * Manages undo and redo operations through a series of {@code UndoRedoState}s
    + * maintained on undo and redo stacks.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.editor.plugins.UndoRedoManager = function() {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * The maximum number of states on the undo stack at any time. Used to limit
    +   * the memory footprint of the undo-redo stack.
    +   * TODO(user) have a separate memory size based limit.
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxUndoDepth_ = 100;
    +
    +  /**
    +   * The undo stack.
    +   * @type {Array<goog.editor.plugins.UndoRedoState>}
    +   * @private
    +   */
    +  this.undoStack_ = [];
    +
    +  /**
    +   * The redo stack.
    +   * @type {Array<goog.editor.plugins.UndoRedoState>}
    +   * @private
    +   */
    +  this.redoStack_ = [];
    +
    +  /**
    +   * A queue of pending undo or redo actions. Stored as objects with two
    +   * properties: func and state. The func property stores the undo or redo
    +   * function to be called, the state property stores the state that method
    +   * came from.
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.pendingActions_ = [];
    +};
    +goog.inherits(goog.editor.plugins.UndoRedoManager, goog.events.EventTarget);
    +
    +
    +/**
    + * Event types for the events dispatched by undo-redo manager.
    + * @enum {string}
    + */
    +goog.editor.plugins.UndoRedoManager.EventType = {
    +  /**
    +   * Signifies that he undo or redo stack transitioned between 0 and 1 states,
    +   * meaning that the ability to peform undo or redo operations has changed.
    +   */
    +  STATE_CHANGE: 'state_change',
    +
    +  /**
    +   * Signifies that a state was just added to the undo stack. Events of this
    +   * type will have a {@code state} property whose value is the state that
    +   * was just added.
    +   */
    +  STATE_ADDED: 'state_added',
    +
    +  /**
    +   * Signifies that the undo method of a state is about to be called.
    +   * Events of this type will have a {@code state} property whose value is the
    +   * state whose undo action is about to be performed. If the event is cancelled
    +   * the action does not proceed, but the state will still transition between
    +   * stacks.
    +   */
    +  BEFORE_UNDO: 'before_undo',
    +
    +  /**
    +   * Signifies that the redo method of a state is about to be called.
    +   * Events of this type will have a {@code state} property whose value is the
    +   * state whose redo action is about to be performed. If the event is cancelled
    +   * the action does not proceed, but the state will still transition between
    +   * stacks.
    +   */
    +  BEFORE_REDO: 'before_redo'
    +};
    +
    +
    +/**
    + * The key for the listener for the completion of the asynchronous state whose
    + * undo or redo action is in progress. Null if no action is in progress.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.inProgressActionKey_ = null;
    +
    +
    +/**
    + * Set the max undo stack depth (not the real memory usage).
    + * @param {number} depth Depth of the stack.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.setMaxUndoDepth =
    +    function(depth) {
    +  this.maxUndoDepth_ = depth;
    +};
    +
    +
    +/**
    + * Add state to the undo stack. This clears the redo stack.
    + *
    + * @param {goog.editor.plugins.UndoRedoState} state The state to add to the undo
    + *     stack.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.addState = function(state) {
    +  // TODO: is the state.equals check necessary?
    +  if (this.undoStack_.length == 0 ||
    +      !state.equals(this.undoStack_[this.undoStack_.length - 1])) {
    +    this.undoStack_.push(state);
    +    if (this.undoStack_.length > this.maxUndoDepth_) {
    +      this.undoStack_.shift();
    +    }
    +    // Clobber the redo stack.
    +    var redoLength = this.redoStack_.length;
    +    this.redoStack_.length = 0;
    +
    +    this.dispatchEvent({
    +      type: goog.editor.plugins.UndoRedoManager.EventType.STATE_ADDED,
    +      state: state
    +    });
    +
    +    // If the redo state had states on it, then clobbering the redo stack above
    +    // has caused a state change.
    +    if (this.undoStack_.length == 1 || redoLength) {
    +      this.dispatchStateChange_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Dispatches a STATE_CHANGE event with this manager as the target.
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.dispatchStateChange_ =
    +    function() {
    +  this.dispatchEvent(
    +      goog.editor.plugins.UndoRedoManager.EventType.STATE_CHANGE);
    +};
    +
    +
    +/**
    + * Performs the undo operation of the state at the top of the undo stack, moving
    + * that state to the top of the redo stack. If the undo stack is empty, does
    + * nothing.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.undo = function() {
    +  this.shiftState_(this.undoStack_, this.redoStack_);
    +};
    +
    +
    +/**
    + * Performs the redo operation of the state at the top of the redo stack, moving
    + * that state to the top of the undo stack. If redo undo stack is empty, does
    + * nothing.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.redo = function() {
    +  this.shiftState_(this.redoStack_, this.undoStack_);
    +};
    +
    +
    +/**
    + * @return {boolean} Wether the undo stack has items on it, i.e., if it is
    + *     possible to perform an undo operation.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.hasUndoState = function() {
    +  return this.undoStack_.length > 0;
    +};
    +
    +
    +/**
    + * @return {boolean} Wether the redo stack has items on it, i.e., if it is
    + *     possible to perform a redo operation.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.hasRedoState = function() {
    +  return this.redoStack_.length > 0;
    +};
    +
    +
    +/**
    + * Move a state from one stack to the other, performing the appropriate undo
    + * or redo action.
    + *
    + * @param {Array<goog.editor.plugins.UndoRedoState>} fromStack Stack to move
    + *     the state from.
    + * @param {Array<goog.editor.plugins.UndoRedoState>} toStack Stack to move
    + *     the state to.
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.shiftState_ = function(
    +    fromStack, toStack) {
    +  if (fromStack.length) {
    +    var state = fromStack.pop();
    +
    +    // Push the current state into the redo stack.
    +    toStack.push(state);
    +
    +    this.addAction_({
    +      type: fromStack == this.undoStack_ ?
    +          goog.editor.plugins.UndoRedoManager.EventType.BEFORE_UNDO :
    +          goog.editor.plugins.UndoRedoManager.EventType.BEFORE_REDO,
    +      func: fromStack == this.undoStack_ ? state.undo : state.redo,
    +      state: state
    +    });
    +
    +    // If either stack transitioned between 0 and 1 in size then the ability
    +    // to do an undo or redo has changed and we must dispatch a state change.
    +    if (fromStack.length == 0 || toStack.length == 1) {
    +      this.dispatchStateChange_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adds an action to the queue of pending undo or redo actions. If no actions
    + * are pending, immediately performs the action.
    + *
    + * @param {Object} action An undo or redo action. Stored as an object with two
    + *     properties: func and state. The func property stores the undo or redo
    + *     function to be called, the state property stores the state that method
    + *     came from.
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.addAction_ = function(action) {
    +  this.pendingActions_.push(action);
    +  if (this.pendingActions_.length == 1) {
    +    this.doAction_();
    +  }
    +};
    +
    +
    +/**
    + * Executes the action at the front of the pending actions queue. If an action
    + * is already in progress or the queue is empty, does nothing.
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.doAction_ = function() {
    +  if (this.inProgressActionKey_ || this.pendingActions_.length == 0) {
    +    return;
    +  }
    +
    +  var action = this.pendingActions_.shift();
    +
    +  var e = {
    +    type: action.type,
    +    state: action.state
    +  };
    +
    +  if (this.dispatchEvent(e)) {
    +    if (action.state.isAsynchronous()) {
    +      this.inProgressActionKey_ = goog.events.listen(action.state,
    +          goog.editor.plugins.UndoRedoState.ACTION_COMPLETED,
    +          this.finishAction_, false, this);
    +      action.func.call(action.state);
    +    } else {
    +      action.func.call(action.state);
    +      this.doAction_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Finishes processing the current in progress action, starting the next queued
    + * action if one exists.
    + * @private
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.finishAction_ = function() {
    +  goog.events.unlistenByKey(/** @type {number} */ (this.inProgressActionKey_));
    +  this.inProgressActionKey_ = null;
    +  this.doAction_();
    +};
    +
    +
    +/**
    + * Clears the undo and redo stacks.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.clearHistory = function() {
    +  if (this.undoStack_.length > 0 || this.redoStack_.length > 0) {
    +    this.undoStack_.length = 0;
    +    this.redoStack_.length = 0;
    +    this.dispatchStateChange_();
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.editor.plugins.UndoRedoState|undefined} The state at the top of
    + *     the undo stack without removing it from the stack.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.undoPeek = function() {
    +  return this.undoStack_[this.undoStack_.length - 1];
    +};
    +
    +
    +/**
    + * @return {goog.editor.plugins.UndoRedoState|undefined} The state at the top of
    + *     the redo stack without removing it from the stack.
    + */
    +goog.editor.plugins.UndoRedoManager.prototype.redoPeek = function() {
    +  return this.redoStack_[this.redoStack_.length - 1];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.html
    new file mode 100644
    index 0000000000..b5cecbb0aa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author ajp@google.com (Andy Perelson)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Trogedit Unit Tests - goog.editor.plugins.UndoRedoManager
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.UndoRedoManagerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.js
    new file mode 100644
    index 0000000000..4ea8a28860
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredomanager_test.js
    @@ -0,0 +1,387 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.UndoRedoManagerTest');
    +goog.setTestOnly('goog.editor.plugins.UndoRedoManagerTest');
    +
    +goog.require('goog.editor.plugins.UndoRedoManager');
    +goog.require('goog.editor.plugins.UndoRedoState');
    +goog.require('goog.events');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.jsunit');
    +
    +var mockState1;
    +var mockState2;
    +var mockState3;
    +var states;
    +var manager;
    +var stateChangeCount;
    +var beforeUndoCount;
    +var beforeRedoCount;
    +var preventDefault;
    +
    +function setUp() {
    +  manager = new goog.editor.plugins.UndoRedoManager();
    +  stateChangeCount = 0;
    +  goog.events.listen(manager,
    +      goog.editor.plugins.UndoRedoManager.EventType.STATE_CHANGE,
    +      function() {
    +        stateChangeCount++;
    +      });
    +
    +  beforeUndoCount = 0;
    +  preventDefault = false;
    +  goog.events.listen(manager,
    +      goog.editor.plugins.UndoRedoManager.EventType.BEFORE_UNDO,
    +      function(e) {
    +        beforeUndoCount++;
    +        if (preventDefault) {
    +          e.preventDefault();
    +        }
    +      });
    +
    +  beforeRedoCount = 0;
    +  goog.events.listen(manager,
    +      goog.editor.plugins.UndoRedoManager.EventType.BEFORE_REDO,
    +      function(e) {
    +        beforeRedoCount++;
    +        if (preventDefault) {
    +          e.preventDefault();
    +        }
    +      });
    +
    +  mockState1 = new goog.testing.StrictMock(goog.editor.plugins.UndoRedoState);
    +  mockState2 = new goog.testing.StrictMock(goog.editor.plugins.UndoRedoState);
    +  mockState3 = new goog.testing.StrictMock(goog.editor.plugins.UndoRedoState);
    +  states = [mockState1, mockState2, mockState3];
    +
    +  mockState1.equals = mockState2.equals = mockState3.equals = function(state) {
    +    return this == state;
    +  };
    +
    +  mockState1.isAsynchronous = mockState2.isAsynchronous =
    +      mockState3.isAsynchronous = function() {
    +    return false;
    +  };
    +}
    +
    +
    +function tearDown() {
    +  goog.events.removeAll(manager);
    +  manager.dispose();
    +}
    +
    +
    +/**
    + * Adds all the mock states to the undo-redo manager.
    + */
    +function addStatesToManager() {
    +  manager.addState(states[0]);
    +
    +  for (var i = 1; i < states.length; i++) {
    +    var state = states[i];
    +    manager.addState(state);
    +  }
    +
    +  stateChangeCount = 0;
    +}
    +
    +
    +/**
    + * Resets all mock states so that they are ready for testing.
    + */
    +function resetStates() {
    +  for (var i = 0; i < states.length; i++) {
    +    states[i].$reset();
    +  }
    +}
    +
    +
    +function testSetMaxUndoDepth() {
    +  manager.setMaxUndoDepth(2);
    +  addStatesToManager();
    +  assertArrayEquals('Undo stack must contain only the two most recent states.',
    +      [mockState2, mockState3], manager.undoStack_);
    +}
    +
    +
    +function testAddState() {
    +  var stateAddedCount = 0;
    +  goog.events.listen(manager,
    +      goog.editor.plugins.UndoRedoManager.EventType.STATE_ADDED,
    +      function() {
    +        stateAddedCount++;
    +      });
    +
    +  manager.addState(mockState1);
    +  assertArrayEquals('Undo stack must contain added state.',
    +      [mockState1], manager.undoStack_);
    +  assertEquals('Manager must dispatch one state change event on ' +
    +      'undo stack 0->1 transition.', 1, stateChangeCount);
    +  assertEquals('State added must have dispatched once.', 1, stateAddedCount);
    +  mockState1.$reset();
    +
    +  // Test adding same state twice.
    +  manager.addState(mockState1);
    +  assertArrayEquals('Undo stack must not contain two equal, sequential states.',
    +      [mockState1], manager.undoStack_);
    +  assertEquals('Manager must not dispatch state change event when nothing is ' +
    +      'added to the stack.', 1, stateChangeCount);
    +  assertEquals('State added must have dispatched once.', 1, stateAddedCount);
    +
    +  // Test adding a second state.
    +  manager.addState(mockState2);
    +  assertArrayEquals('Undo stack must contain both states.',
    +      [mockState1, mockState2], manager.undoStack_);
    +  assertEquals('Manager must not dispatch state change event when second ' +
    +      'state is added to the stack.', 1, stateChangeCount);
    +  assertEquals('State added must have dispatched twice.', 2, stateAddedCount);
    +
    +  // Test adding a state when there is state on the redo stack.
    +  manager.undo();
    +  assertEquals('Manager must dispatch state change when redo stack goes to 1.',
    +      2, stateChangeCount);
    +
    +  manager.addState(mockState3);
    +  assertArrayEquals('Undo stack must contain states 1 and 3.',
    +      [mockState1, mockState3], manager.undoStack_);
    +  assertEquals('Manager must dispatch state change event when redo stack ' +
    +      'goes to zero.', 3, stateChangeCount);
    +  assertEquals('State added must have dispatched three times.',
    +      3, stateAddedCount);
    +}
    +
    +
    +function testHasState() {
    +  assertFalse('New manager must have no undo state.', manager.hasUndoState());
    +  assertFalse('New manager must have no redo state.', manager.hasRedoState());
    +
    +  manager.addState(mockState1);
    +  assertTrue('Manager must have only undo state.', manager.hasUndoState());
    +  assertFalse('Manager must have no redo state.', manager.hasRedoState());
    +
    +  manager.undo();
    +  assertFalse('Manager must have no undo state.', manager.hasUndoState());
    +  assertTrue('Manager must have only redo state.', manager.hasRedoState());
    +}
    +
    +
    +function testClearHistory() {
    +  addStatesToManager();
    +  manager.undo();
    +  stateChangeCount = 0;
    +
    +  manager.clearHistory();
    +  assertFalse('Undo stack must be empty.', manager.hasUndoState());
    +  assertFalse('Redo stack must be empty.', manager.hasRedoState());
    +  assertEquals('State change count must be 1 after clear history.',
    +      1, stateChangeCount);
    +
    +  manager.clearHistory();
    +  assertEquals('Repeated clearHistory must not change state change count.',
    +      1, stateChangeCount);
    +}
    +
    +
    +function testUndo() {
    +  addStatesToManager();
    +
    +  mockState3.undo();
    +  mockState3.$replay();
    +  manager.undo();
    +  assertEquals('Adding first item to redo stack must dispatch state change.',
    +      1, stateChangeCount);
    +  assertEquals('Undo must cause before action to dispatch',
    +      1, beforeUndoCount);
    +  mockState3.$verify();
    +
    +  preventDefault = true;
    +  mockState2.$replay();
    +  manager.undo();
    +  assertEquals('No stack transitions between 0 and 1, must not dispatch ' +
    +      'state change.', 1, stateChangeCount);
    +  assertEquals('Undo must cause before action to dispatch',
    +      2, beforeUndoCount);
    +  mockState2.$verify(); // Verify that undo was prevented.
    +
    +  preventDefault = false;
    +  mockState1.undo();
    +  mockState1.$replay();
    +  manager.undo();
    +  assertEquals('Doing last undo operation must dispatch state change.',
    +      2, stateChangeCount);
    +  assertEquals('Undo must cause before action to dispatch',
    +      3, beforeUndoCount);
    +  mockState1.$verify();
    +}
    +
    +
    +function testUndo_Asynchronous() {
    +  // Using a stub instead of a mock here so that the state can behave as an
    +  // EventTarget and dispatch events.
    +  var stubState = new goog.editor.plugins.UndoRedoState(true);
    +  var undoCalled = false;
    +  stubState.undo = function() {
    +    undoCalled = true;
    +  };
    +  stubState.redo = goog.nullFunction;
    +  stubState.equals = function() {
    +    return false;
    +  };
    +
    +  manager.addState(mockState2);
    +  manager.addState(mockState1);
    +  manager.addState(stubState);
    +
    +  manager.undo();
    +  assertTrue('undoCalled must be true (undo must be called).', undoCalled);
    +  assertEquals('Undo must cause before action to dispatch',
    +      1, beforeUndoCount);
    +
    +  // Calling undo shouldn't actually undo since the first async undo hasn't
    +  // fired an event yet.
    +  mockState1.$replay();
    +  manager.undo();
    +  mockState1.$verify();
    +  assertEquals('Before action must not dispatch for pending undo.',
    +      1, beforeUndoCount);
    +
    +  // Dispatching undo completed on first undo, should cause the second pending
    +  // undo to happen.
    +  mockState1.$reset();
    +  mockState1.undo();
    +  mockState1.$replay();
    +  mockState2.$replay(); // Nothing should happen to mockState2.
    +  stubState.dispatchEvent(goog.editor.plugins.UndoRedoState.ACTION_COMPLETED);
    +  mockState1.$verify();
    +  mockState2.$verify();
    +  assertEquals('Second undo must cause before action to dispatch',
    +      2, beforeUndoCount);
    +
    +  // Test last undo.
    +  mockState2.$reset();
    +  mockState2.undo();
    +  mockState2.$replay();
    +  manager.undo();
    +  mockState2.$verify();
    +  assertEquals('Third undo must cause before action to dispatch',
    +      3, beforeUndoCount);
    +}
    +
    +
    +function testRedo() {
    +  addStatesToManager();
    +  manager.undo();
    +  manager.undo();
    +  manager.undo();
    +  resetStates();
    +  stateChangeCount = 0;
    +
    +  mockState1.redo();
    +  mockState1.$replay();
    +  manager.redo();
    +  assertEquals('Pushing first item onto undo stack during redo must dispatch ' +
    +               'state change.', 1, stateChangeCount);
    +  assertEquals('First redo must cause before action to dispatch',
    +      1, beforeRedoCount);
    +  mockState1.$verify();
    +
    +  preventDefault = true;
    +  mockState2.$replay();
    +  manager.redo();
    +  assertEquals('No stack transitions between 0 and 1, must not dispatch ' +
    +      'state change.', 1, stateChangeCount);
    +  assertEquals('Second redo must cause before action to dispatch',
    +      2, beforeRedoCount);
    +  mockState2.$verify(); // Verify that redo was prevented.
    +
    +  preventDefault = false;
    +  mockState3.redo();
    +  mockState3.$replay();
    +  manager.redo();
    +  assertEquals('Removing last item from redo stack must dispatch state change.',
    +      2, stateChangeCount);
    +  assertEquals('Third redo must cause before action to dispatch',
    +      3, beforeRedoCount);
    +  mockState3.$verify();
    +  mockState3.$reset();
    +
    +  mockState3.undo();
    +  mockState3.$replay();
    +  manager.undo();
    +  assertEquals('Putting item on redo stack must dispatch state change.',
    +      3, stateChangeCount);
    +  assertEquals('Undo must cause before action to dispatch',
    +      4, beforeUndoCount);
    +  mockState3.$verify();
    +}
    +
    +
    +function testRedo_Asynchronous() {
    +  var stubState = new goog.editor.plugins.UndoRedoState(true);
    +  var redoCalled = false;
    +  stubState.redo = function() {
    +    redoCalled = true;
    +  };
    +  stubState.undo = goog.nullFunction;
    +  stubState.equals = function() {
    +    return false;
    +  };
    +
    +  manager.addState(stubState);
    +  manager.addState(mockState1);
    +  manager.addState(mockState2);
    +
    +  manager.undo();
    +  manager.undo();
    +  manager.undo();
    +  stubState.dispatchEvent(goog.editor.plugins.UndoRedoState.ACTION_COMPLETED);
    +  resetStates();
    +
    +  manager.redo();
    +  assertTrue('redoCalled must be true (redo must be called).', redoCalled);
    +
    +  // Calling redo shouldn't actually redo since the first async redo hasn't
    +  // fired an event yet.
    +  mockState1.$replay();
    +  manager.redo();
    +  mockState1.$verify();
    +
    +  // Dispatching redo completed on first redo, should cause the second pending
    +  // redo to happen.
    +  mockState1.$reset();
    +  mockState1.redo();
    +  mockState1.$replay();
    +  mockState2.$replay(); // Nothing should happen to mockState1.
    +  stubState.dispatchEvent(goog.editor.plugins.UndoRedoState.ACTION_COMPLETED);
    +  mockState1.$verify();
    +  mockState2.$verify();
    +
    +  // Test last redo.
    +  mockState2.$reset();
    +  mockState2.redo();
    +  mockState2.$replay();
    +  manager.redo();
    +  mockState2.$verify();
    +}
    +
    +function testUndoAndRedoPeek() {
    +  addStatesToManager();
    +  manager.undo();
    +
    +  assertEquals('redoPeek must return the top of the redo stack.',
    +      manager.redoStack_[manager.redoStack_.length - 1], manager.redoPeek());
    +  assertEquals('undoPeek must return the top of the undo stack.',
    +      manager.undoStack_[manager.undoStack_.length - 1], manager.undoPeek());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate.js
    new file mode 100644
    index 0000000000..9a772dd438
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate.js
    @@ -0,0 +1,86 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Code for an UndoRedoState interface representing an undo and
    + * redo action for a particular state change. To be used by
    + * {@link goog.editor.plugins.UndoRedoManager}.
    + *
    + */
    +
    +
    +goog.provide('goog.editor.plugins.UndoRedoState');
    +
    +goog.require('goog.events.EventTarget');
    +
    +
    +
    +/**
    + * Represents an undo and redo action for a particular state transition.
    + *
    + * @param {boolean} asynchronous Whether the undo or redo actions for this
    + *     state complete asynchronously. If true, then this state must fire
    + *     an ACTION_COMPLETED event when undo or redo is complete.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.editor.plugins.UndoRedoState = function(asynchronous) {
    +  goog.editor.plugins.UndoRedoState.base(this, 'constructor');
    +
    +  /**
    +   * Indicates if the undo or redo actions for this state complete
    +   * asynchronously.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.asynchronous_ = asynchronous;
    +};
    +goog.inherits(goog.editor.plugins.UndoRedoState, goog.events.EventTarget);
    +
    +
    +/**
    + * Event type for events indicating that this state has completed an undo or
    + * redo operation.
    + */
    +goog.editor.plugins.UndoRedoState.ACTION_COMPLETED = 'action_completed';
    +
    +
    +/**
    + * @return {boolean} Whether or not the undo and redo actions of this state
    + *     complete asynchronously. If true, the state will fire an ACTION_COMPLETED
    + *     event when an undo or redo action is complete.
    + */
    +goog.editor.plugins.UndoRedoState.prototype.isAsynchronous = function() {
    +  return this.asynchronous_;
    +};
    +
    +
    +/**
    + * Undoes the action represented by this state.
    + */
    +goog.editor.plugins.UndoRedoState.prototype.undo = goog.abstractMethod;
    +
    +
    +/**
    + * Redoes the action represented by this state.
    + */
    +goog.editor.plugins.UndoRedoState.prototype.redo = goog.abstractMethod;
    +
    +
    +/**
    + * Checks if two undo-redo states are the same.
    + * @param {goog.editor.plugins.UndoRedoState} state The state to compare.
    + * @return {boolean} Wether the two states are equal.
    + */
    +goog.editor.plugins.UndoRedoState.prototype.equals = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.html b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.html
    new file mode 100644
    index 0000000000..6c63cb67c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.html
    @@ -0,0 +1,28 @@
    +goog.editor.plugins
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author ajp@google.com (Andy Perelson)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Trogedit Unit Tests - goog.editor.plugins.UndoRedoState
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.plugins.UndoRedoStateTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.js b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.js
    new file mode 100644
    index 0000000000..4026964cfb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/plugins/undoredostate_test.js
    @@ -0,0 +1,34 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.plugins.UndoRedoStateTest');
    +goog.setTestOnly('goog.editor.plugins.UndoRedoStateTest');
    +
    +goog.require('goog.editor.plugins.UndoRedoState');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncState;
    +var syncState;
    +
    +function setUp() {
    +  asyncState = new goog.editor.plugins.UndoRedoState(true);
    +  syncState = new goog.editor.plugins.UndoRedoState(false);
    +}
    +
    +function testIsAsynchronous() {
    +  assertTrue('Must return true for asynchronous state',
    +      asyncState.isAsynchronous());
    +  assertFalse('Must return false for synchronous state',
    +      syncState.isAsynchronous());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/range.js b/mapbox-gl/closure-library/closure/goog/editor/range.js
    new file mode 100644
    index 0000000000..ec1a6a706d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/range.js
    @@ -0,0 +1,632 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilties for working with ranges.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.editor.range');
    +goog.provide('goog.editor.range.Point');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.RangeEndpoint');
    +goog.require('goog.dom.SavedCaretRange');
    +goog.require('goog.editor.node');
    +goog.require('goog.editor.style');
    +goog.require('goog.iter');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Given a range and an element, create a narrower range that is limited to the
    + * boundaries of the element. If the range starts (or ends) outside the
    + * element, the narrowed range's start point (or end point) will be the
    + * leftmost (or rightmost) leaf of the element.
    + * @param {goog.dom.AbstractRange} range The range.
    + * @param {Element} el The element to limit the range to.
    + * @return {goog.dom.AbstractRange} A new narrowed range, or null if the
    + *     element does not contain any part of the given range.
    + */
    +goog.editor.range.narrow = function(range, el) {
    +  var startContainer = range.getStartNode();
    +  var endContainer = range.getEndNode();
    +
    +  if (startContainer && endContainer) {
    +    var isElement = function(node) {
    +      return node == el;
    +    };
    +    var hasStart = goog.dom.getAncestor(startContainer, isElement, true);
    +    var hasEnd = goog.dom.getAncestor(endContainer, isElement, true);
    +
    +    if (hasStart && hasEnd) {
    +      // The range is contained entirely within this element.
    +      return range.clone();
    +    } else if (hasStart) {
    +      // The range starts inside the element, but ends outside it.
    +      var leaf = goog.editor.node.getRightMostLeaf(el);
    +      return goog.dom.Range.createFromNodes(
    +          range.getStartNode(), range.getStartOffset(),
    +          leaf, goog.editor.node.getLength(leaf));
    +    } else if (hasEnd) {
    +      // The range starts outside the element, but ends inside it.
    +      return goog.dom.Range.createFromNodes(
    +          goog.editor.node.getLeftMostLeaf(el), 0,
    +          range.getEndNode(), range.getEndOffset());
    +    }
    +  }
    +
    +  // The selection starts and ends outside the element.
    +  return null;
    +};
    +
    +
    +/**
    + * Given a range, expand the range to include outer tags if the full contents of
    + * those tags are entirely selected.  This essentially changes the dom position,
    + * but not the visible position of the range.
    + * Ex. <li>foo</li> if "foo" is selected, instead of returning start and end
    + * nodes as the foo text node, return the li.
    + * @param {goog.dom.AbstractRange} range The range.
    + * @param {Node=} opt_stopNode Optional node to stop expanding past.
    + * @return {!goog.dom.AbstractRange} The expanded range.
    + */
    +goog.editor.range.expand = function(range, opt_stopNode) {
    +  // Expand the start out to the common container.
    +  var expandedRange = goog.editor.range.expandEndPointToContainer_(
    +      range, goog.dom.RangeEndpoint.START, opt_stopNode);
    +  // Expand the end out to the common container.
    +  expandedRange = goog.editor.range.expandEndPointToContainer_(
    +      expandedRange, goog.dom.RangeEndpoint.END, opt_stopNode);
    +
    +  var startNode = expandedRange.getStartNode();
    +  var endNode = expandedRange.getEndNode();
    +  var startOffset = expandedRange.getStartOffset();
    +  var endOffset = expandedRange.getEndOffset();
    +
    +  // If we have reached a common container, now expand out.
    +  if (startNode == endNode) {
    +    while (endNode != opt_stopNode &&
    +           startOffset == 0 &&
    +           endOffset == goog.editor.node.getLength(endNode)) {
    +      // Select the parent instead.
    +      var parentNode = endNode.parentNode;
    +      startOffset = goog.array.indexOf(parentNode.childNodes, endNode);
    +      endOffset = startOffset + 1;
    +      endNode = parentNode;
    +    }
    +    startNode = endNode;
    +  }
    +
    +  return goog.dom.Range.createFromNodes(startNode, startOffset,
    +      endNode, endOffset);
    +};
    +
    +
    +/**
    + * Given a range, expands the start or end points as far out towards the
    + * range's common container (or stopNode, if provided) as possible, while
    + * perserving the same visible position.
    + *
    + * @param {goog.dom.AbstractRange} range The range to expand.
    + * @param {goog.dom.RangeEndpoint} endpoint The endpoint to expand.
    + * @param {Node=} opt_stopNode Optional node to stop expanding past.
    + * @return {!goog.dom.AbstractRange} The expanded range.
    + * @private
    + */
    +goog.editor.range.expandEndPointToContainer_ = function(range, endpoint,
    +                                                        opt_stopNode) {
    +  var expandStart = endpoint == goog.dom.RangeEndpoint.START;
    +  var node = expandStart ? range.getStartNode() : range.getEndNode();
    +  var offset = expandStart ? range.getStartOffset() : range.getEndOffset();
    +  var container = range.getContainerElement();
    +
    +  // Expand the node out until we reach the container or the stop node.
    +  while (node != container && node != opt_stopNode) {
    +    // It is only valid to expand the start if we are at the start of a node
    +    // (offset 0) or expand the end if we are at the end of a node
    +    // (offset length).
    +    if (expandStart && offset != 0 ||
    +        !expandStart && offset != goog.editor.node.getLength(node)) {
    +      break;
    +    }
    +
    +    var parentNode = node.parentNode;
    +    var index = goog.array.indexOf(parentNode.childNodes, node);
    +    offset = expandStart ? index : index + 1;
    +    node = parentNode;
    +  }
    +
    +  return goog.dom.Range.createFromNodes(
    +      expandStart ? node : range.getStartNode(),
    +      expandStart ? offset : range.getStartOffset(),
    +      expandStart ? range.getEndNode() : node,
    +      expandStart ? range.getEndOffset() : offset);
    +};
    +
    +
    +/**
    + * Cause the window's selection to be the start of this node.
    + * @param {Node} node The node to select the start of.
    + */
    +goog.editor.range.selectNodeStart = function(node) {
    +  goog.dom.Range.createCaret(goog.editor.node.getLeftMostLeaf(node), 0).
    +      select();
    +};
    +
    +
    +/**
    + * Position the cursor immediately to the left or right of "node".
    + * In Firefox, the selection parent is outside of "node", so the cursor can
    + * effectively be moved to the end of a link node, without being considered
    + * inside of it.
    + * Note: This does not always work in WebKit. In particular, if you try to
    + * place a cursor to the right of a link, typing still puts you in the link.
    + * Bug: http://bugs.webkit.org/show_bug.cgi?id=17697
    + * @param {Node} node The node to position the cursor relative to.
    + * @param {boolean} toLeft True to place it to the left, false to the right.
    + * @return {!goog.dom.AbstractRange} The newly selected range.
    + */
    +goog.editor.range.placeCursorNextTo = function(node, toLeft) {
    +  var parent = node.parentNode;
    +  var offset = goog.array.indexOf(parent.childNodes, node) +
    +      (toLeft ? 0 : 1);
    +  var point = goog.editor.range.Point.createDeepestPoint(
    +      parent, offset, toLeft, true);
    +  var range = goog.dom.Range.createCaret(point.node, point.offset);
    +  range.select();
    +  return range;
    +};
    +
    +
    +/**
    + * Normalizes the node, preserving the selection of the document.
    + *
    + * May also normalize things outside the node, if it is more efficient to do so.
    + *
    + * @param {Node} node The node to normalize.
    + */
    +goog.editor.range.selectionPreservingNormalize = function(node) {
    +  var doc = goog.dom.getOwnerDocument(node);
    +  var selection = goog.dom.Range.createFromWindow(goog.dom.getWindow(doc));
    +  var normalizedRange =
    +      goog.editor.range.rangePreservingNormalize(node, selection);
    +  if (normalizedRange) {
    +    normalizedRange.select();
    +  }
    +};
    +
    +
    +/**
    + * Manually normalizes the node in IE, since native normalize in IE causes
    + * transient problems.
    + * @param {Node} node The node to normalize.
    + * @private
    + */
    +goog.editor.range.normalizeNodeIe_ = function(node) {
    +  var lastText = null;
    +  var child = node.firstChild;
    +  while (child) {
    +    var next = child.nextSibling;
    +    if (child.nodeType == goog.dom.NodeType.TEXT) {
    +      if (child.nodeValue == '') {
    +        node.removeChild(child);
    +      } else if (lastText) {
    +        lastText.nodeValue += child.nodeValue;
    +        node.removeChild(child);
    +      } else {
    +        lastText = child;
    +      }
    +    } else {
    +      goog.editor.range.normalizeNodeIe_(child);
    +      lastText = null;
    +    }
    +    child = next;
    +  }
    +};
    +
    +
    +/**
    + * Normalizes the given node.
    + * @param {Node} node The node to normalize.
    + */
    +goog.editor.range.normalizeNode = function(node) {
    +  if (goog.userAgent.IE) {
    +    goog.editor.range.normalizeNodeIe_(node);
    +  } else {
    +    node.normalize();
    +  }
    +};
    +
    +
    +/**
    + * Normalizes the node, preserving a range of the document.
    + *
    + * May also normalize things outside the node, if it is more efficient to do so.
    + *
    + * @param {Node} node The node to normalize.
    + * @param {goog.dom.AbstractRange?} range The range to normalize.
    + * @return {goog.dom.AbstractRange?} The range, adjusted for normalization.
    + */
    +goog.editor.range.rangePreservingNormalize = function(node, range) {
    +  if (range) {
    +    var rangeFactory = goog.editor.range.normalize(range);
    +    // WebKit has broken selection affinity, so carets tend to jump out of the
    +    // beginning of inline elements. This means that if we're doing the
    +    // normalize as the result of a range that will later become the selection,
    +    // we might not normalize something in the range after it is read back from
    +    // the selection. We can't just normalize the parentNode here because WebKit
    +    // can move the selection range out of multiple inline parents.
    +    var container = goog.editor.style.getContainer(range.getContainerElement());
    +  }
    +
    +  if (container) {
    +    goog.editor.range.normalizeNode(
    +        goog.dom.findCommonAncestor(container, node));
    +  } else if (node) {
    +    goog.editor.range.normalizeNode(node);
    +  }
    +
    +  if (rangeFactory) {
    +    return rangeFactory();
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Get the deepest point in the DOM that's equivalent to the endpoint of the
    + * given range.
    + *
    + * @param {goog.dom.AbstractRange} range A range.
    + * @param {boolean} atStart True for the start point, false for the end point.
    + * @return {!goog.editor.range.Point} The end point, expressed as a node
    + *    and an offset.
    + */
    +goog.editor.range.getDeepEndPoint = function(range, atStart) {
    +  return atStart ?
    +      goog.editor.range.Point.createDeepestPoint(
    +          range.getStartNode(), range.getStartOffset()) :
    +      goog.editor.range.Point.createDeepestPoint(
    +          range.getEndNode(), range.getEndOffset());
    +};
    +
    +
    +/**
    + * Given a range in the current DOM, create a factory for a range that
    + * represents the same selection in a normalized DOM. The factory function
    + * should be invoked after the DOM is normalized.
    + *
    + * All browsers do a bad job preserving ranges across DOM normalization.
    + * The issue is best described in this 5-year-old bug report:
    + * https://bugzilla.mozilla.org/show_bug.cgi?id=191864
    + * For most applications, this isn't a problem. The browsers do a good job
    + * handling un-normalized text, so there's usually no reason to normalize.
    + *
    + * The exception to this rule is the rich text editing commands
    + * execCommand and queryCommandValue, which will fail often if there are
    + * un-normalized text nodes.
    + *
    + * The factory function creates new ranges so that we can normalize the DOM
    + * without problems. It must be created before any normalization happens,
    + * and invoked after normalization happens.
    + *
    + * @param {goog.dom.AbstractRange} range The range to normalize. It may
    + *    become invalid after body.normalize() is called.
    + * @return {function(): goog.dom.AbstractRange} A factory for a normalized
    + *    range. Should be called after body.normalize() is called.
    + */
    +goog.editor.range.normalize = function(range) {
    +  var isReversed = range.isReversed();
    +  var anchorPoint = goog.editor.range.normalizePoint_(
    +      goog.editor.range.getDeepEndPoint(range, !isReversed));
    +  var anchorParent = anchorPoint.getParentPoint();
    +  var anchorPreviousSibling = anchorPoint.node.previousSibling;
    +  if (anchorPoint.node.nodeType == goog.dom.NodeType.TEXT) {
    +    anchorPoint.node = null;
    +  }
    +
    +  var focusPoint = goog.editor.range.normalizePoint_(
    +      goog.editor.range.getDeepEndPoint(range, isReversed));
    +  var focusParent = focusPoint.getParentPoint();
    +  var focusPreviousSibling = focusPoint.node.previousSibling;
    +  if (focusPoint.node.nodeType == goog.dom.NodeType.TEXT) {
    +    focusPoint.node = null;
    +  }
    +
    +  return function() {
    +    if (!anchorPoint.node && anchorPreviousSibling) {
    +      // If anchorPoint.node was previously an empty text node with no siblings,
    +      // anchorPreviousSibling may not have a nextSibling since that node will
    +      // no longer exist.  Do our best and point to the end of the previous
    +      // element.
    +      anchorPoint.node = anchorPreviousSibling.nextSibling;
    +      if (!anchorPoint.node) {
    +        anchorPoint = goog.editor.range.Point.getPointAtEndOfNode(
    +            anchorPreviousSibling);
    +      }
    +    }
    +
    +    if (!focusPoint.node && focusPreviousSibling) {
    +      // If focusPoint.node was previously an empty text node with no siblings,
    +      // focusPreviousSibling may not have a nextSibling since that node will no
    +      // longer exist.  Do our best and point to the end of the previous
    +      // element.
    +      focusPoint.node = focusPreviousSibling.nextSibling;
    +      if (!focusPoint.node) {
    +        focusPoint = goog.editor.range.Point.getPointAtEndOfNode(
    +            focusPreviousSibling);
    +      }
    +    }
    +
    +    return goog.dom.Range.createFromNodes(
    +        anchorPoint.node || anchorParent.node.firstChild || anchorParent.node,
    +        anchorPoint.offset,
    +        focusPoint.node || focusParent.node.firstChild || focusParent.node,
    +        focusPoint.offset);
    +  };
    +};
    +
    +
    +/**
    + * Given a point in the current DOM, adjust it to represent the same point in
    + * a normalized DOM.
    + *
    + * See the comments on goog.editor.range.normalize for more context.
    + *
    + * @param {goog.editor.range.Point} point A point in the document.
    + * @return {!goog.editor.range.Point} The same point, for easy chaining.
    + * @private
    + */
    +goog.editor.range.normalizePoint_ = function(point) {
    +  var previous;
    +  if (point.node.nodeType == goog.dom.NodeType.TEXT) {
    +    // If the cursor position is in a text node,
    +    // look at all the previous text siblings of the text node,
    +    // and set the offset relative to the earliest text sibling.
    +    for (var current = point.node.previousSibling;
    +         current && current.nodeType == goog.dom.NodeType.TEXT;
    +         current = current.previousSibling) {
    +      point.offset += goog.editor.node.getLength(current);
    +    }
    +
    +    previous = current;
    +  } else {
    +    previous = point.node.previousSibling;
    +  }
    +
    +  var parent = point.node.parentNode;
    +  point.node = previous ? previous.nextSibling : parent.firstChild;
    +  return point;
    +};
    +
    +
    +/**
    + * Checks if a range is completely inside an editable region.
    + * @param {goog.dom.AbstractRange} range The range to test.
    + * @return {boolean} Whether the range is completely inside an editable region.
    + */
    +goog.editor.range.isEditable = function(range) {
    +  var rangeContainer = range.getContainerElement();
    +
    +  // Closure's implementation of getContainerElement() is a little too
    +  // smart in IE when exactly one element is contained in the range.
    +  // It assumes that there's a user whose intent was actually to select
    +  // all that element's children, so it returns the element itself as its
    +  // own containing element.
    +  // This little sanity check detects this condition so we can account for it.
    +  var rangeContainerIsOutsideRange =
    +      range.getStartNode() != rangeContainer.parentElement;
    +
    +  return (rangeContainerIsOutsideRange &&
    +          goog.editor.node.isEditableContainer(rangeContainer)) ||
    +      goog.editor.node.isEditable(rangeContainer);
    +};
    +
    +
    +/**
    + * Returns whether the given range intersects with any instance of the given
    + * tag.
    + * @param {goog.dom.AbstractRange} range The range to check.
    + * @param {goog.dom.TagName} tagName The name of the tag.
    + * @return {boolean} Whether the given range intersects with any instance of
    + *     the given tag.
    + */
    +goog.editor.range.intersectsTag = function(range, tagName) {
    +  if (goog.dom.getAncestorByTagNameAndClass(range.getContainerElement(),
    +                                            tagName)) {
    +    return true;
    +  }
    +
    +  return goog.iter.some(range, function(node) {
    +    return node.tagName == tagName;
    +  });
    +};
    +
    +
    +
    +/**
    + * One endpoint of a range, represented as a Node and and offset.
    + * @param {Node} node The node containing the point.
    + * @param {number} offset The offset of the point into the node.
    + * @constructor
    + * @final
    + */
    +goog.editor.range.Point = function(node, offset) {
    +  /**
    +   * The node containing the point.
    +   * @type {Node}
    +   */
    +  this.node = node;
    +
    +  /**
    +   * The offset of the point into the node.
    +   * @type {number}
    +   */
    +  this.offset = offset;
    +};
    +
    +
    +/**
    + * Gets the point of this point's node in the DOM.
    + * @return {!goog.editor.range.Point} The node's point.
    + */
    +goog.editor.range.Point.prototype.getParentPoint = function() {
    +  var parent = this.node.parentNode;
    +  return new goog.editor.range.Point(
    +      parent, goog.array.indexOf(parent.childNodes, this.node));
    +};
    +
    +
    +/**
    + * Construct the deepest possible point in the DOM that's equivalent
    + * to the given point, expressed as a node and an offset.
    + * @param {Node} node The node containing the point.
    + * @param {number} offset The offset of the point from the node.
    + * @param {boolean=} opt_trendLeft Notice that a (node, offset) pair may be
    + *     equivalent to more than one descendent (node, offset) pair in the DOM.
    + *     By default, we trend rightward. If this parameter is true, then we
    + *     trend leftward. The tendency to fall rightward by default is for
    + *     consistency with other range APIs (like placeCursorNextTo).
    + * @param {boolean=} opt_stopOnChildlessElement If true, and we encounter
    + *     a Node which is an Element that cannot have children, we return a Point
    + *     based on its parent rather than that Node itself.
    + * @return {!goog.editor.range.Point} A new point.
    + */
    +goog.editor.range.Point.createDeepestPoint =
    +    function(node, offset, opt_trendLeft, opt_stopOnChildlessElement) {
    +  while (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +    var child = node.childNodes[offset];
    +    if (!child && !node.lastChild) {
    +      break;
    +    } else if (child) {
    +      var prevSibling = child.previousSibling;
    +      if (opt_trendLeft && prevSibling) {
    +        if (opt_stopOnChildlessElement &&
    +            goog.editor.range.Point.isTerminalElement_(prevSibling)) {
    +          break;
    +        }
    +        node = prevSibling;
    +        offset = goog.editor.node.getLength(node);
    +      } else {
    +        if (opt_stopOnChildlessElement &&
    +            goog.editor.range.Point.isTerminalElement_(child)) {
    +          break;
    +        }
    +        node = child;
    +        offset = 0;
    +      }
    +    } else {
    +      if (opt_stopOnChildlessElement &&
    +          goog.editor.range.Point.isTerminalElement_(node.lastChild)) {
    +        break;
    +      }
    +      node = node.lastChild;
    +      offset = goog.editor.node.getLength(node);
    +    }
    +  }
    +
    +  return new goog.editor.range.Point(node, offset);
    +};
    +
    +
    +/**
    + * Return true if the specified node is an Element that is not expected to have
    + * children. The createDeepestPoint() method should not traverse into
    + * such elements.
    + * @param {Node} node .
    + * @return {boolean} True if the node is an Element that does not contain
    + *     child nodes (e.g. BR, IMG).
    + * @private
    + */
    +goog.editor.range.Point.isTerminalElement_ = function(node) {
    +  return (node.nodeType == goog.dom.NodeType.ELEMENT &&
    +          !goog.dom.canHaveChildren(node));
    +};
    +
    +
    +/**
    + * Construct a point at the very end of the given node.
    + * @param {Node} node The node to create a point for.
    + * @return {!goog.editor.range.Point} A new point.
    + */
    +goog.editor.range.Point.getPointAtEndOfNode = function(node) {
    +  return new goog.editor.range.Point(node, goog.editor.node.getLength(node));
    +};
    +
    +
    +/**
    + * Saves the range by inserting carets into the HTML.
    + *
    + * Unlike the regular saveUsingCarets, this SavedRange normalizes text nodes.
    + * Browsers have other bugs where they don't handle split text nodes in
    + * contentEditable regions right.
    + *
    + * @param {goog.dom.AbstractRange} range The abstract range object.
    + * @return {!goog.dom.SavedCaretRange} A saved caret range that normalizes
    + *     text nodes.
    + */
    +goog.editor.range.saveUsingNormalizedCarets = function(range) {
    +  return new goog.editor.range.NormalizedCaretRange_(range);
    +};
    +
    +
    +
    +/**
    + * Saves the range using carets, but normalizes text nodes when carets
    + * are removed.
    + * @see goog.editor.range.saveUsingNormalizedCarets
    + * @param {goog.dom.AbstractRange} range The range being saved.
    + * @constructor
    + * @extends {goog.dom.SavedCaretRange}
    + * @private
    + */
    +goog.editor.range.NormalizedCaretRange_ = function(range) {
    +  goog.dom.SavedCaretRange.call(this, range);
    +};
    +goog.inherits(goog.editor.range.NormalizedCaretRange_,
    +    goog.dom.SavedCaretRange);
    +
    +
    +/**
    + * Normalizes text nodes whenever carets are removed from the document.
    + * @param {goog.dom.AbstractRange=} opt_range A range whose offsets have already
    + *     been adjusted for caret removal; it will be adjusted and returned if it
    + *     is also affected by post-removal operations, such as text node
    + *     normalization.
    + * @return {goog.dom.AbstractRange|undefined} The adjusted range, if opt_range
    + *     was provided.
    + * @override
    + */
    +goog.editor.range.NormalizedCaretRange_.prototype.removeCarets =
    +    function(opt_range) {
    +  var startCaret = this.getCaret(true);
    +  var endCaret = this.getCaret(false);
    +  var node = startCaret && endCaret ?
    +      goog.dom.findCommonAncestor(startCaret, endCaret) :
    +      startCaret || endCaret;
    +
    +  goog.editor.range.NormalizedCaretRange_.superClass_.removeCarets.call(this);
    +
    +  if (opt_range) {
    +    return goog.editor.range.rangePreservingNormalize(node, opt_range);
    +  } else if (node) {
    +    goog.editor.range.selectionPreservingNormalize(node);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/range_test.html b/mapbox-gl/closure-library/closure/goog/editor/range_test.html
    new file mode 100644
    index 0000000000..6a004adeca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/range_test.html
    @@ -0,0 +1,92 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.editor.range
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.editor.rangeTest');
    +  </script>
    + </head>
    + <body>
    +  <div id='root'>
    +  <div id='parentNode'>
    +    abc
    +    <div id='def'>def</div>
    +    ghi
    +    <div id='jkl'>jkl</div>
    +    mno
    +    <div id='pqr'>pqr</div>
    +    stu
    +  </div>
    +
    +  <div id='caretRangeTest-1'>
    +    abc
    +    <div id='def-1'>def</div>
    +    ghi
    +    <div id='jkl-1'>jkl</div>
    +    mno
    +    <div id='pqr-1'>pqr</div>
    +    stu
    +  </div>
    +
    +  <div id='normalizeTest-with-br'>abc<br/>def</div>
    +  <div id='normalizeTest-with-div'><div>abc</div></div>
    +  <div id='normalizeTest-with-empty-text-nodes'></div>
    +
    +  <div id='normalizeTest-2'>
    +    abc
    +    <div id='def-2'>def</div>
    +    ghi
    +    <div id='jkl-2'>jkl</div>
    +    mno
    +    <div id='pqr-2'>pqr</div>
    +    stu
    +  </div>
    +
    +  <div id='normalizeTest-3'>
    +    abc
    +    <div id='def-3'>def</div>
    +    ghi
    +    <div id='jkl-3'>jkl</div>
    +    mno
    +    <div id='pqr-3'>pqr</div>
    +    stu
    +  </div>
    +
    +  <div id='normalizeTest-4'>
    +    abc
    +    <div id='def-4'>def</div>
    +    ghi
    +    <div id='jkl-4'>jkl</div>
    +    mno
    +    <div id='pqr-4'>pqr</div>
    +    stu
    +  </div>
    +
    +  <div id='editableTest' g_editable='true'>
    +    abc
    +    <div>def</div>
    +    ghi
    +    <div>jkl</div>
    +    mno
    +    <div>pqr</div>
    +    stu
    +  </div>
    +
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/range_test.js b/mapbox-gl/closure-library/closure/goog/editor/range_test.js
    new file mode 100644
    index 0000000000..e365997a98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/range_test.js
    @@ -0,0 +1,942 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.rangeTest');
    +goog.setTestOnly('goog.editor.rangeTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.range');
    +goog.require('goog.editor.range.Point');
    +goog.require('goog.string');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var savedHtml;
    +var $;
    +
    +function setUpPage() {
    +  $ = goog.dom.getElement;
    +}
    +
    +function setUp() {
    +  savedHtml = $('root').innerHTML;
    +}
    +
    +function tearDown() {
    +  $('root').innerHTML = savedHtml;
    +}
    +
    +function testNoNarrow() {
    +  var def = $('def');
    +  var jkl = $('jkl');
    +  var range = goog.dom.Range.createFromNodes(
    +      def.firstChild, 1, jkl.firstChild, 2);
    +
    +  range = goog.editor.range.narrow(range, $('parentNode'));
    +  goog.testing.dom.assertRangeEquals(
    +      def.firstChild, 1, jkl.firstChild, 2, range);
    +}
    +
    +function testNarrowAtEndEdge() {
    +  var def = $('def');
    +  var jkl = $('jkl');
    +  var range = goog.dom.Range.createFromNodes(
    +      def.firstChild, 1, jkl.firstChild, 2);
    +
    +  range = goog.editor.range.narrow(range, def);
    +  goog.testing.dom.assertRangeEquals(
    +      def.firstChild, 1, def.firstChild, 3, range);
    +}
    +
    +function testNarrowAtStartEdge() {
    +  var def = $('def');
    +  var jkl = $('jkl');
    +  var range = goog.dom.Range.createFromNodes(
    +      def.firstChild, 1, jkl.firstChild, 2);
    +
    +  range = goog.editor.range.narrow(range, jkl);
    +
    +  goog.testing.dom.assertRangeEquals(
    +      jkl.firstChild, 0, jkl.firstChild, 2, range);
    +}
    +
    +function testNarrowOutsideElement() {
    +  var def = $('def');
    +  var jkl = $('jkl');
    +  var range = goog.dom.Range.createFromNodes(
    +      def.firstChild, 1, jkl.firstChild, 2);
    +
    +  range = goog.editor.range.narrow(range, $('pqr'));
    +  assertNull(range);
    +}
    +
    +function testNoExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div>longword</div>';
    +  // Select "ongwo" and make sure we don't expand since this is not
    +  // a full container.
    +  var textNode = div.firstChild.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 1, textNode, 6);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(textNode, 1, textNode, 6, range);
    +}
    +
    +function testSimpleExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div>longword</div>foo';
    +  // Select "longword" and make sure we do expand to include the div since
    +  // the full container text is selected.
    +  var textNode = div.firstChild.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 8);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +
    +  // Select "foo" and make sure we expand out to the parent div.
    +  var fooNode = div.lastChild;
    +  range = goog.dom.Range.createFromNodes(fooNode, 0, fooNode, 3);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(div, 1, div, 2, range);
    +}
    +
    +function testDoubleExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div><span>longword</span></div>foo';
    +  // Select "longword" and make sure we do expand to include the span
    +  // and the div since both of their full contents are selected.
    +  var textNode = div.firstChild.firstChild.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 8);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +
    +  // Same visible position, different dom position.
    +  // Start in text node, end in span.
    +  range = goog.dom.Range.createFromNodes(textNode, 0, textNode.parentNode, 1);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +}
    +
    +function testMultipleChildrenExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<ol><li>one</li><li>two</li><li>three</li></ol>';
    +  // Select "two" and make sure we expand to the li, but not the ol.
    +  var li = div.firstChild.childNodes[1];
    +  var textNode = li.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 3);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(
    +      li.parentNode, 1, li.parentNode, 2, range);
    +
    +  // Make the same visible selection, only slightly different dom position.
    +  // Select starting from the text node, but ending in the li.
    +  range = goog.dom.Range.createFromNodes(textNode, 0, li, 1);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(
    +      li.parentNode, 1, li.parentNode, 2, range);
    +}
    +
    +function testSimpleDifferentContainersExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<ol><li>1</li><li><b>bold</b><i>italic</i></li></ol>';
    +  // Select all of "bold" and "italic" at the text node level, and
    +  // make sure we expand to the li.
    +  var li = div.firstChild.childNodes[1];
    +  var boldNode = li.childNodes[0];
    +  var italicNode = li.childNodes[1];
    +  var range = goog.dom.Range.createFromNodes(boldNode.firstChild, 0,
    +      italicNode.firstChild, 6);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(
    +      li.parentNode, 1, li.parentNode, 2, range);
    +
    +  // Make the same visible selection, only slightly different dom position.
    +  // Select "bold" at the b node level and "italic" at the text node level.
    +  range = goog.dom.Range.createFromNodes(boldNode, 0,
    +      italicNode.firstChild, 6);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(
    +      li.parentNode, 1, li.parentNode, 2, range);
    +}
    +
    +function testSimpleDifferentContainersSmallExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<ol><li>1</li><li><b>bold</b><i>italic</i>' +
    +      '<u>under</u></li></ol>';
    +  // Select all of "bold" and "italic", but we can't expand to the
    +  // entire li since we didn't select "under".
    +  var li = div.firstChild.childNodes[1];
    +  var boldNode = li.childNodes[0];
    +  var italicNode = li.childNodes[1];
    +  var range = goog.dom.Range.createFromNodes(boldNode.firstChild, 0,
    +      italicNode.firstChild, 6);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(li, 0, li, 2, range);
    +
    +  // Same visible position, different dom position.
    +  // Select "bold" starting in text node, "italic" at i node.
    +  range = goog.dom.Range.createFromNodes(boldNode.firstChild, 0,
    +      italicNode, 1);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(li, 0, li, 2, range);
    +}
    +
    +function testEmbeddedDifferentContainersExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div><b><i>italic</i>after</b><u>under</u></div>foo';
    +  // Select "italic" "after" "under", should expand all the way to parent.
    +  var boldNode = div.firstChild.childNodes[0];
    +  var italicNode = boldNode.childNodes[0];
    +  var underNode = div.firstChild.childNodes[1];
    +  var range = goog.dom.Range.createFromNodes(italicNode.firstChild, 0,
    +      underNode.firstChild, 5);
    +
    +  range = goog.editor.range.expand(range);
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +}
    +
    +function testReverseSimpleExpand() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div>longword</div>foo';
    +  // Select "longword" and make sure we do expand to include the div since
    +  // the full container text is selected.
    +  var textNode = div.firstChild.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 8, textNode, 0);
    +
    +  range = goog.editor.range.expand(range);
    +
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +}
    +
    +function testExpandWithStopNode() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div><span>word</span></div>foo';
    +  // Select "word".
    +  var span = div.firstChild.firstChild;
    +  var textNode = span.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 4);
    +
    +  range = goog.editor.range.expand(range);
    +
    +  goog.testing.dom.assertRangeEquals(div, 0, div, 1, range);
    +
    +  // Same selection, but force stop at the span.
    +  range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 4);
    +
    +  range = goog.editor.range.expand(range, span);
    +
    +  goog.testing.dom.assertRangeEquals(span, 0, span, 1, range);
    +}
    +
    +// Ojan didn't believe this code worked, this was the case he
    +// thought was broken.  Keeping just as a regression test.
    +function testOjanCase() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<em><i><b>foo</b>bar</i></em>';
    +  // Select "foo", at text node level.
    +  var iNode = div.firstChild.firstChild;
    +  var textNode = iNode.firstChild.firstChild;
    +  var range = goog.dom.Range.createFromNodes(textNode, 0, textNode, 3);
    +
    +  range = goog.editor.range.expand(range);
    +
    +  goog.testing.dom.assertRangeEquals(iNode, 0, iNode, 1, range);
    +
    +  // Same selection, at b node level.
    +  range = goog.dom.Range.createFromNodes(iNode.firstChild, 0,
    +      iNode.firstChild, 1);
    +  range = goog.editor.range.expand(range);
    +
    +  goog.testing.dom.assertRangeEquals(iNode, 0, iNode, 1, range);
    +}
    +
    +function testPlaceCursorNextToLeft() {
    +  var div = $('parentNode');
    +  div.innerHTML = 'foo<div id="bar">bar</div>baz';
    +  var node = $('bar');
    +  var range = goog.editor.range.placeCursorNextTo(node, true);
    +
    +  var expose = goog.testing.dom.exposeNode;
    +  assertEquals('Selection should be to the left of the node ' +
    +      expose(node) + ',' + expose(range.getStartNode().nextSibling),
    +      node, range.getStartNode().nextSibling);
    +  assertEquals('Selection should be collapsed',
    +      true, range.isCollapsed());
    +}
    +
    +
    +function testPlaceCursorNextToRight() {
    +  var div = $('parentNode');
    +  div.innerHTML = 'foo<div id="bar">bar</div>baz';
    +  var node = $('bar');
    +  var range = goog.editor.range.placeCursorNextTo(node, false);
    +
    +  assertEquals('Selection should be to the right of the node',
    +      node, range.getStartNode().previousSibling);
    +  assertEquals('Selection should be collapsed',
    +      true, range.isCollapsed());
    +}
    +
    +function testPlaceCursorNextTo_rightOfLineBreak() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<div contentEditable="true">hhhh<br />h</div>';
    +  var children = div.firstChild.childNodes;
    +  assertEquals(3, children.length);
    +  var node = children[1];
    +  var range = goog.editor.range.placeCursorNextTo(node, false);
    +  assertEquals(node.nextSibling, range.getStartNode());
    +}
    +
    +function testPlaceCursorNextTo_leftOfHr() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<hr />aaa';
    +  var children = div.childNodes;
    +  assertEquals(2, children.length);
    +  var node = children[0];
    +  var range = goog.editor.range.placeCursorNextTo(node, true);
    +
    +  assertEquals(div, range.getStartNode());
    +  assertEquals(0, range.getStartOffset());
    +}
    +
    +function testPlaceCursorNextTo_rightOfHr() {
    +  var div = $('parentNode');
    +  div.innerHTML = 'aaa<hr>';
    +  var children = div.childNodes;
    +  assertEquals(2, children.length);
    +  var node = children[1];
    +  var range = goog.editor.range.placeCursorNextTo(node, false);
    +
    +  assertEquals(div, range.getStartNode());
    +  assertEquals(2, range.getStartOffset());
    +}
    +
    +function testPlaceCursorNextTo_rightOfImg() {
    +  var div = $('parentNode');
    +  div.innerHTML =
    +      'aaa<img src="https://www.google.com/images/srpr/logo3w.png">bbb';
    +  var children = div.childNodes;
    +  assertEquals(3, children.length);
    +  var imgNode = children[1];
    +  var range = goog.editor.range.placeCursorNextTo(imgNode, false);
    +
    +  assertEquals('range node should be the right sibling of img tag',
    +      children[2], range.getStartNode());
    +  assertEquals(0, range.getStartOffset());
    +
    +}
    +
    +function testPlaceCursorNextTo_rightOfImgAtEnd() {
    +  var div = $('parentNode');
    +  div.innerHTML =
    +      'aaa<img src="https://www.google.com/images/srpr/logo3w.png">';
    +  var children = div.childNodes;
    +  assertEquals(2, children.length);
    +  var imgNode = children[1];
    +  var range = goog.editor.range.placeCursorNextTo(imgNode, false);
    +
    +  assertEquals('range node should be the parent of img',
    +      div, range.getStartNode());
    +  assertEquals('offset should be right after the img tag',
    +      2, range.getStartOffset());
    +
    +}
    +
    +function testPlaceCursorNextTo_leftOfImg() {
    +  var div = $('parentNode');
    +  div.innerHTML =
    +      '<img src="https://www.google.com/images/srpr/logo3w.png">xxx';
    +  var children = div.childNodes;
    +  assertEquals(2, children.length);
    +  var imgNode = children[0];
    +  var range = goog.editor.range.placeCursorNextTo(imgNode, true);
    +
    +  assertEquals('range node should be the parent of img',
    +      div, range.getStartNode());
    +  assertEquals('offset should point to the img tag',
    +      0, range.getStartOffset());
    +}
    +
    +function testPlaceCursorNextTo_rightOfFirstOfTwoImgTags() {
    +  var div = $('parentNode');
    +  div.innerHTML =
    +      'aaa<img src="https://www.google.com/images/srpr/logo3w.png">' +
    +      '<img src="https://www.google.com/images/srpr/logo3w.png">';
    +  var children = div.childNodes;
    +  assertEquals(3, children.length);
    +  var imgNode = children[1];  // First of two IMG nodes
    +  var range = goog.editor.range.placeCursorNextTo(imgNode, false);
    +
    +  assertEquals('range node should be the parent of img instead of ' +
    +      'node with innerHTML=' + range.getStartNode().innerHTML,
    +      div, range.getStartNode());
    +  assertEquals('offset should be right after the img tag',
    +      2, range.getStartOffset());
    +}
    +
    +function testGetDeepEndPoint() {
    +  var div = $('parentNode');
    +  var def = $('def');
    +  var jkl = $('jkl');
    +
    +  assertPointEquals(div.firstChild, 0,
    +      goog.editor.range.getDeepEndPoint(
    +          goog.dom.Range.createFromNodeContents(div), true));
    +  assertPointEquals(div.lastChild, div.lastChild.length,
    +      goog.editor.range.getDeepEndPoint(
    +          goog.dom.Range.createFromNodeContents(div), false));
    +
    +  assertPointEquals(def.firstChild, 0,
    +      goog.editor.range.getDeepEndPoint(
    +          goog.dom.Range.createCaret(div, 1), true));
    +  assertPointEquals(def.nextSibling, 0,
    +      goog.editor.range.getDeepEndPoint(
    +          goog.dom.Range.createCaret(div, 2), true));
    +
    +}
    +
    +function testNormalizeOnNormalizedDom() {
    +  var defText = $('def').firstChild;
    +  var jklText = $('jkl').firstChild;
    +  var range = goog.dom.Range.createFromNodes(defText, 1, jklText, 2);
    +
    +  var newRange = normalizeBody(range);
    +  goog.testing.dom.assertRangeEquals(defText, 1, jklText, 2, newRange);
    +}
    +
    +function testDeepPointFindingOnNormalizedDom() {
    +  var def = $('def');
    +  var jkl = $('jkl');
    +  var range = goog.dom.Range.createFromNodes(def, 0, jkl, 1);
    +
    +  var newRange = normalizeBody(range);
    +
    +  // Make sure that newRange is measured relative to the text nodes,
    +  // not the DIV elements.
    +  goog.testing.dom.assertRangeEquals(
    +      def.firstChild, 0, jkl.firstChild, 3, newRange);
    +}
    +
    +function testNormalizeOnVeryFragmentedDom() {
    +  var defText = $('def').firstChild;
    +  var jklText = $('jkl').firstChild;
    +  var range = goog.dom.Range.createFromNodes(defText, 1, jklText, 2);
    +
    +  // Fragment the DOM a bunch.
    +  fragmentText(defText);
    +  fragmentText(jklText);
    +
    +  var newRange = normalizeBody(range);
    +
    +  // our old text nodes may not be valid anymore. find new ones.
    +  defText = $('def').firstChild;
    +  jklText = $('jkl').firstChild;
    +
    +  goog.testing.dom.assertRangeEquals(defText, 1, jklText, 2, newRange);
    +}
    +
    +function testNormalizeOnDivWithEmptyTextNodes() {
    +  var emptyDiv = $('normalizeTest-with-empty-text-nodes');
    +
    +  // Append empty text nodes to the emptyDiv.
    +  var tnode1 = goog.dom.createTextNode('');
    +  var tnode2 = goog.dom.createTextNode('');
    +  var tnode3 = goog.dom.createTextNode('');
    +
    +  goog.dom.appendChild(emptyDiv, tnode1);
    +  goog.dom.appendChild(emptyDiv, tnode2);
    +  goog.dom.appendChild(emptyDiv, tnode3);
    +
    +  var range = goog.dom.Range.createFromNodes(emptyDiv, 1, emptyDiv, 2);
    +
    +  // Cannot use document.body.normalize() as it fails to normalize the div
    +  // (in IE) if it has nothing but empty text nodes.
    +  var newRange = goog.editor.range.rangePreservingNormalize(emptyDiv, range);
    +
    +  if (goog.userAgent.GECKO &&
    +      goog.string.compareVersions(goog.userAgent.VERSION, '1.9') == -1) {
    +    // In FF2, node.normalize() leaves an empty textNode in the div, unlike
    +    // other browsers where the div is left with no children.
    +    goog.testing.dom.assertRangeEquals(
    +        emptyDiv.firstChild, 0, emptyDiv.firstChild, 0, newRange);
    +  } else {
    +    goog.testing.dom.assertRangeEquals(emptyDiv, 0, emptyDiv, 0, newRange);
    +  }
    +}
    +
    +function testRangeCreatedInVeryFragmentedDom() {
    +  var def = $('def');
    +  var defText = def.firstChild;
    +  var jkl = $('jkl');
    +  var jklText = jkl.firstChild;
    +
    +  // Fragment the DOM a bunch.
    +  fragmentText(defText);
    +  fragmentText(jklText);
    +
    +  // Notice that there are two empty text nodes at the beginning of each
    +  // fragmented node.
    +  var range = goog.dom.Range.createFromNodes(def, 3, jkl, 4);
    +
    +  var newRange = normalizeBody(range);
    +
    +  // our old text nodes may not be valid anymore. find new ones.
    +  defText = $('def').firstChild;
    +  jklText = $('jkl').firstChild;
    +  goog.testing.dom.assertRangeEquals(defText, 1, jklText, 2, newRange);
    +}
    +
    +function testNormalizeInFragmentedDomWithPreviousSiblings() {
    +  var ghiText = $('def').nextSibling;
    +  var mnoText = $('jkl').nextSibling;
    +  var range = goog.dom.Range.createFromNodes(ghiText, 1, mnoText, 2);
    +
    +  // Fragment the DOM a bunch.
    +  fragmentText($('def').previousSibling); // fragment abc
    +  fragmentText(ghiText);
    +  fragmentText(mnoText);
    +
    +  var newRange = normalizeBody(range);
    +
    +  // our old text nodes may not be valid anymore. find new ones.
    +  ghiText = $('def').nextSibling;
    +  mnoText = $('jkl').nextSibling;
    +
    +  goog.testing.dom.assertRangeEquals(ghiText, 1, mnoText, 2, newRange);
    +}
    +
    +function testRangeCreatedInFragmentedDomWithPreviousSiblings() {
    +  var def = $('def');
    +  var ghiText = $('def').nextSibling;
    +  var jkl = $('jkl');
    +  var mnoText = $('jkl').nextSibling;
    +
    +  // Fragment the DOM a bunch.
    +  fragmentText($('def').previousSibling); // fragment abc
    +  fragmentText(ghiText);
    +  fragmentText(mnoText);
    +
    +  // Notice that there are two empty text nodes at the beginning of each
    +  // fragmented node.
    +  var root = $('parentNode');
    +  var range = goog.dom.Range.createFromNodes(root, 9, root, 16);
    +
    +  var newRange = normalizeBody(range);
    +
    +  // our old text nodes may not be valid anymore. find new ones.
    +  ghiText = $('def').nextSibling;
    +  mnoText = $('jkl').nextSibling;
    +  goog.testing.dom.assertRangeEquals(ghiText, 1, mnoText, 2, newRange);
    +}
    +
    +
    +/**
    + * Branched from the tests for goog.dom.SavedCaretRange.
    + */
    +function testSavedCaretRange() {
    +  var def = $('def-1');
    +  var jkl = $('jkl-1');
    +
    +  var range = goog.dom.Range.createFromNodes(
    +      def.firstChild, 1, jkl.firstChild, 2);
    +  range.select();
    +
    +  var saved = goog.editor.range.saveUsingNormalizedCarets(range);
    +  assertHTMLEquals(
    +      'd<span id="' + saved.startCaretId_ + '"></span>ef', def.innerHTML);
    +  assertHTMLEquals(
    +      'jk<span id="' + saved.endCaretId_ + '"></span>l', jkl.innerHTML);
    +
    +  clearSelectionAndRestoreSaved(saved);
    +
    +  var selection = goog.dom.Range.createFromWindow(window);
    +  def = $('def-1');
    +  jkl = $('jkl-1');
    +  assertHTMLEquals('def', def.innerHTML);
    +  assertHTMLEquals('jkl', jkl.innerHTML);
    +
    +  // Check that everything was normalized ok.
    +  assertEquals(1, def.childNodes.length);
    +  assertEquals(1, jkl.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(
    +      def.firstChild, 1, jkl.firstChild, 2, selection);
    +}
    +
    +function testRangePreservingNormalize() {
    +  var parent = $('normalizeTest-4');
    +  var def = $('def-4');
    +  var jkl = $('jkl-4');
    +  fragmentText(def.firstChild);
    +  fragmentText(jkl.firstChild);
    +
    +  var range = goog.dom.Range.createFromNodes(def, 3, jkl, 4);
    +  var oldRangeDescription = goog.testing.dom.exposeRange(range);
    +  range = goog.editor.range.rangePreservingNormalize(parent, range);
    +
    +  // Check that everything was normalized ok.
    +  assertEquals('def should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, def.childNodes.length);
    +  assertEquals('jkl should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, jkl.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(def.firstChild, 1, jkl.firstChild, 2,
    +                                     range);
    +}
    +
    +function testRangePreservingNormalizeWhereEndNodePreviousSiblingIsSplit() {
    +  var parent = $('normalizeTest-with-br');
    +  var br = parent.childNodes[1];
    +  fragmentText(parent.firstChild);
    +
    +  var range = goog.dom.Range.createFromNodes(parent, 3, br, 0);
    +  range = goog.editor.range.rangePreservingNormalize(parent, range);
    +
    +  // Code used to throw an error here.
    +
    +  assertEquals('parent should have 3 children', 3, parent.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(parent.firstChild, 1, parent, 1, range);
    +}
    +
    +function testRangePreservingNormalizeWhereStartNodePreviousSiblingIsSplit() {
    +  var parent = $('normalizeTest-with-br');
    +  var br = parent.childNodes[1];
    +  fragmentText(parent.firstChild);
    +  fragmentText(parent.lastChild);
    +
    +  var range = goog.dom.Range.createFromNodes(br, 0, parent, 9);
    +  range = goog.editor.range.rangePreservingNormalize(parent, range);
    +
    +  // Code used to throw an error here.
    +
    +  assertEquals('parent should have 3 children', 3, parent.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(parent, 1, parent.lastChild, 1, range);
    +}
    +
    +function testSelectionPreservingNormalize1() {
    +  var parent = $('normalizeTest-2');
    +  var def = $('def-2');
    +  var jkl = $('jkl-2');
    +  fragmentText(def.firstChild);
    +  fragmentText(jkl.firstChild);
    +
    +  goog.dom.Range.createFromNodes(def, 3, jkl, 4).select();
    +  assertFalse(goog.dom.Range.createFromWindow(window).isReversed());
    +
    +  var oldRangeDescription = goog.testing.dom.exposeRange(
    +      goog.dom.Range.createFromWindow(window));
    +  goog.editor.range.selectionPreservingNormalize(parent);
    +
    +  // Check that everything was normalized ok.
    +  var range = goog.dom.Range.createFromWindow(window);
    +  assertFalse(range.isReversed());
    +
    +  assertEquals('def should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, def.childNodes.length);
    +  assertEquals('jkl should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, jkl.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(def.firstChild, 1, jkl.firstChild, 2,
    +      range);
    +}
    +
    +
    +/**
    + * Make sure that selectionPreservingNormalize doesn't explode with no
    + * selection in the document.
    + */
    +function testSelectionPreservingNormalize2() {
    +  var parent = $('normalizeTest-3');
    +  var def = $('def-3');
    +  var jkl = $('jkl-3');
    +  def.firstChild.splitText(1);
    +  jkl.firstChild.splitText(2);
    +
    +  goog.dom.Range.clearSelection(window);
    +  goog.editor.range.selectionPreservingNormalize(parent);
    +
    +  // Check that everything was normalized ok.
    +  assertEquals(1, def.childNodes.length);
    +  assertEquals(1, jkl.childNodes.length);
    +  assertFalse(goog.dom.Range.hasSelection(window));
    +}
    +
    +function testSelectionPreservingNormalize3() {
    +  if (goog.userAgent.IE) {
    +    return;
    +  }
    +  var parent = $('normalizeTest-2');
    +  var def = $('def-2');
    +  var jkl = $('jkl-2');
    +  fragmentText(def.firstChild);
    +  fragmentText(jkl.firstChild);
    +
    +  goog.dom.Range.createFromNodes(jkl, 4, def, 3).select();
    +  assertTrue(goog.dom.Range.createFromWindow(window).isReversed());
    +
    +  var oldRangeDescription = goog.testing.dom.exposeRange(
    +      goog.dom.Range.createFromWindow(window));
    +  goog.editor.range.selectionPreservingNormalize(parent);
    +
    +  // Check that everything was normalized ok.
    +  var range = goog.dom.Range.createFromWindow(window);
    +  assertTrue(range.isReversed());
    +
    +  assertEquals('def should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, def.childNodes.length);
    +  assertEquals('jkl should have 1 child; range is ' +
    +      goog.testing.dom.exposeRange(range) +
    +      ', range was ' + oldRangeDescription,
    +      1, jkl.childNodes.length);
    +  goog.testing.dom.assertRangeEquals(def.firstChild, 1, jkl.firstChild, 2,
    +      range);
    +}
    +
    +function testSelectionPreservingNormalizeAfterPlaceCursorNextTo() {
    +  var parent = $('normalizeTest-with-div');
    +  goog.editor.range.placeCursorNextTo(parent.firstChild);
    +  goog.editor.range.selectionPreservingNormalize(parent);
    +
    +  // Code used to throw an exception here.
    +}
    +
    +
    +/** Normalize the body and return the normalized range. */
    +function normalizeBody(range) {
    +  var rangeFactory = goog.editor.range.normalize(range);
    +  document.body.normalize();
    +  return rangeFactory();
    +}
    +
    +
    +/** Break a text node up into lots of little fragments. */
    +function fragmentText(text) {
    +  // NOTE(nicksantos): For some reason, splitText makes IE deeply
    +  // unhappy to the point where normalize and other normal DOM operations
    +  // start failing. It's a useful test for Firefox though, because different
    +  // versions of FireFox handle empty text nodes differently.
    +  // See goog.editor.BrowserFeature.
    +  if (goog.userAgent.IE) {
    +    manualSplitText(text, 2);
    +    manualSplitText(text, 1);
    +    manualSplitText(text, 0);
    +    manualSplitText(text, 0);
    +  } else {
    +    text.splitText(2);
    +    text.splitText(1);
    +
    +    text.splitText(0);
    +    text.splitText(0);
    +  }
    +}
    +
    +
    +/**
    + * Clear the selection by re-parsing the DOM. Then restore the saved
    + * selection.
    + * @param {goog.dom.SavedRange} saved The saved range.
    + */
    +function clearSelectionAndRestoreSaved(saved) {
    +  goog.dom.Range.clearSelection(window);
    +  assertFalse(goog.dom.Range.hasSelection(window));
    +  saved.restore();
    +  assertTrue(goog.dom.Range.hasSelection(window));
    +}
    +
    +function manualSplitText(node, pos) {
    +  var newNodeString = node.nodeValue.substr(pos);
    +  node.nodeValue = node.nodeValue.substr(0, pos);
    +  goog.dom.insertSiblingAfter(document.createTextNode(newNodeString), node);
    +}
    +
    +function testSelectNodeStartSimple() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<p>Cursor should go in here</p>';
    +
    +  goog.editor.range.selectNodeStart(div);
    +  var range = goog.dom.Range.createFromWindow(window);
    +  // Gotta love browsers and their inconsistencies with selection
    +  // representations.  What we are trying to achieve is that when we type
    +  // the text will go into the P node.  In Gecko, the selection is at the start
    +  // of the text node, as you'd expect, but in pre-530 Webkit, it has been
    +  // normalized to the visible position of P:0.
    +  if (goog.userAgent.GECKO || goog.userAgent.IE ||
    +      (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('530'))) {
    +    goog.testing.dom.assertRangeEquals(div.firstChild.firstChild, 0,
    +        div.firstChild.firstChild, 0, range);
    +  } else {
    +    goog.testing.dom.assertRangeEquals(div.firstChild, 0,
    +        div.firstChild, 0, range);
    +  }
    +}
    +
    +function testSelectNodeStartBr() {
    +  var div = $('parentNode');
    +  div.innerHTML = '<p><br>Cursor should go in here</p>';
    +
    +  goog.editor.range.selectNodeStart(div);
    +  var range = goog.dom.Range.createFromWindow(window);
    +  // We have to skip the BR since Gecko can't render a cursor at a BR.
    +  goog.testing.dom.assertRangeEquals(div.firstChild, 0,
    +      div.firstChild, 0, range);
    +}
    +
    +function testIsEditable() {
    +  var containerElement = document.getElementById('editableTest');
    +  // Find editable container element's index.
    +  var containerIndex = 0;
    +  var currentSibling = containerElement;
    +  while (currentSibling = currentSibling.previousSibling) {
    +    containerIndex++;
    +  }
    +
    +  var editableContainer = goog.dom.Range.createFromNodes(
    +      containerElement.parentNode, containerIndex,
    +      containerElement.parentNode, containerIndex + 1);
    +  assertFalse('Range containing container element not considered editable',
    +      goog.editor.range.isEditable(editableContainer));
    +
    +  var allEditableChildren = goog.dom.Range.createFromNodes(
    +      containerElement, 0, containerElement,
    +      containerElement.childNodes.length);
    +  assertTrue('Range of all of container element children considered editable',
    +      goog.editor.range.isEditable(allEditableChildren));
    +
    +  var someEditableChildren = goog.dom.Range.createFromNodes(
    +      containerElement, 2, containerElement, 6);
    +  assertTrue('Range of some container element children considered editable',
    +      goog.editor.range.isEditable(someEditableChildren));
    +
    +
    +  var mixedEditableNonEditable = goog.dom.Range.createFromNodes(
    +      containerElement.previousSibling, 0, containerElement, 2);
    +  assertFalse('Range overlapping some content not considered editable',
    +      goog.editor.range.isEditable(mixedEditableNonEditable));
    +}
    +
    +function testIntersectsTag() {
    +  var root = $('root');
    +  root.innerHTML =
    +      '<b>Bold</b><p><span><code>x</code></span></p><p>y</p><i>Italic</i>';
    +
    +  // Select the whole thing.
    +  var range = goog.dom.Range.createFromNodeContents(root);
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.DIV));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.B));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.I));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.CODE));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.U));
    +
    +  // Just select italic.
    +  range = goog.dom.Range.createFromNodes(root, 3, root, 4);
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.DIV));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.B));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.I));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.CODE));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.U));
    +
    +  // Select "ld x y".
    +  range = goog.dom.Range.createFromNodes(root.firstChild.firstChild, 2,
    +      root.childNodes[2], 1);
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.DIV));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.B));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.I));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.CODE));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.U));
    +
    +  // Select ol.
    +  range = goog.dom.Range.createFromNodes(root.firstChild.firstChild, 1,
    +      root.firstChild.firstChild, 3);
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.DIV));
    +  assertTrue(goog.editor.range.intersectsTag(range, goog.dom.TagName.B));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.I));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.CODE));
    +  assertFalse(goog.editor.range.intersectsTag(range, goog.dom.TagName.U));
    +}
    +
    +function testNormalizeNode() {
    +  var div = goog.dom.createDom('DIV', null, 'a', 'b', 'c');
    +  assertEquals(3, div.childNodes.length);
    +  goog.editor.range.normalizeNode(div);
    +  assertEquals(1, div.childNodes.length);
    +  assertEquals('abc', div.firstChild.nodeValue);
    +
    +  div = goog.dom.createDom('DIV', null,
    +      goog.dom.createDom('SPAN', null, '1', '2'),
    +      goog.dom.createTextNode(''),
    +      goog.dom.createDom('BR'),
    +      'b',
    +      'c');
    +  assertEquals(5, div.childNodes.length);
    +  assertEquals(2, div.firstChild.childNodes.length);
    +  goog.editor.range.normalizeNode(div);
    +  if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher(1.9) ||
    +      goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher(526)) {
    +    // Old Gecko and Webkit versions don't delete the empty node.
    +    assertEquals(4, div.childNodes.length);
    +  } else {
    +    assertEquals(3, div.childNodes.length);
    +  }
    +  assertEquals(1, div.firstChild.childNodes.length);
    +  assertEquals('12', div.firstChild.firstChild.nodeValue);
    +  assertEquals('bc', div.lastChild.nodeValue);
    +  assertEquals('BR', div.lastChild.previousSibling.tagName);
    +}
    +
    +function testDeepestPoint() {
    +  var parent = $('parentNode');
    +  var def = $('def');
    +
    +  assertEquals(def, parent.childNodes[1]);
    +
    +  var deepestPoint = goog.editor.range.Point.createDeepestPoint;
    +
    +  var defStartLeft = deepestPoint(parent, 1, true);
    +  assertPointEquals(def.previousSibling, def.previousSibling.nodeValue.length,
    +      defStartLeft);
    +
    +  var defStartRight = deepestPoint(parent, 1, false);
    +  assertPointEquals(def.firstChild, 0, defStartRight);
    +
    +  var defEndLeft = deepestPoint(parent, 2, true);
    +  assertPointEquals(def.firstChild, def.firstChild.nodeValue.length,
    +      defEndLeft);
    +
    +  var defEndRight = deepestPoint(parent, 2, false);
    +  assertPointEquals(def.nextSibling, 0, defEndRight);
    +}
    +
    +function assertPointEquals(node, offset, actualPoint) {
    +  assertEquals('Point has wrong node', node, actualPoint.node);
    +  assertEquals('Point has wrong offset', offset, actualPoint.offset);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/seamlessfield.js b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield.js
    new file mode 100644
    index 0000000000..c069df1876
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield.js
    @@ -0,0 +1,739 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class to encapsulate an editable field that blends in with
    + * the style of the page. The field can be fixed height, grow with its
    + * contents, or have a min height after which it grows to its contents.
    + * This is a goog.editor.Field, but with blending and sizing capabilities,
    + * and avoids using an iframe whenever possible.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + * @see ../demos/editor/seamlessfield.html
    + */
    +
    +
    +goog.provide('goog.editor.SeamlessField');
    +
    +goog.require('goog.cssom.iframe.style');
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Field');
    +goog.require('goog.editor.icontent');
    +goog.require('goog.editor.icontent.FieldFormatInfo');
    +goog.require('goog.editor.icontent.FieldStyleInfo');
    +goog.require('goog.editor.node');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.log');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * This class encapsulates an editable field that blends in with the
    + * surrounding page.
    + * To see events fired by this object, please see the base class.
    + *
    + * @param {string} id An identifer for the field. This is used to find the
    + *     field and the element associated with this field.
    + * @param {Document=} opt_doc The document that the element with the given
    + *     id can be found it.
    + * @constructor
    + * @extends {goog.editor.Field}
    + */
    +goog.editor.SeamlessField = function(id, opt_doc) {
    +  goog.editor.Field.call(this, id, opt_doc);
    +};
    +goog.inherits(goog.editor.SeamlessField, goog.editor.Field);
    +
    +
    +/**
    + * @override
    + */
    +goog.editor.SeamlessField.prototype.logger =
    +    goog.log.getLogger('goog.editor.SeamlessField');
    +
    +// Functions dealing with field sizing.
    +
    +
    +/**
    + * The key used for listening for the "dragover" event.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.listenForDragOverEventKey_;
    +
    +
    +/**
    + * The key used for listening for the iframe "load" event.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.listenForIframeLoadEventKey_;
    +
    +
    +/**
    + * Sets the min height of this editable field's iframe. Only used in growing
    + * mode when an iframe is used. This will cause an immediate field sizing to
    + * update the field if necessary based on the new min height.
    + * @param {number} height The min height specified as a number of pixels,
    + *    e.g., 75.
    + */
    +goog.editor.SeamlessField.prototype.setMinHeight = function(height) {
    +  if (height == this.minHeight_) {
    +    // Do nothing if the min height isn't changing.
    +    return;
    +  }
    +  this.minHeight_ = height;
    +  if (this.usesIframe()) {
    +    this.doFieldSizingGecko();
    +  }
    +};
    +
    +
    +/**
    + * Whether the field should be rendered with a fixed height, or should expand
    + * to fit its contents.
    + * @type {boolean}
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.isFixedHeight_ = false;
    +
    +
    +/**
    + * Whether the fixed-height handling has been overridden manually.
    + * @type {boolean}
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.isFixedHeightOverridden_ = false;
    +
    +
    +/**
    + * @return {boolean} Whether the field should be rendered with a fixed
    + *    height, or should expand to fit its contents.
    + * @override
    + */
    +goog.editor.SeamlessField.prototype.isFixedHeight = function() {
    +  return this.isFixedHeight_;
    +};
    +
    +
    +/**
    + * @param {boolean} newVal Explicitly set whether the field should be
    + *    of a fixed-height. This overrides auto-detection.
    + */
    +goog.editor.SeamlessField.prototype.overrideFixedHeight = function(newVal) {
    +  this.isFixedHeight_ = newVal;
    +  this.isFixedHeightOverridden_ = true;
    +};
    +
    +
    +/**
    + * Auto-detect whether the current field should have a fixed height.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.autoDetectFixedHeight_ = function() {
    +  if (!this.isFixedHeightOverridden_) {
    +    var originalElement = this.getOriginalElement();
    +    if (originalElement) {
    +      this.isFixedHeight_ =
    +          goog.style.getComputedOverflowY(originalElement) == 'auto';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Resize the iframe in response to the wrapper div changing size.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.handleOuterDocChange_ = function() {
    +  if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) {
    +    return;
    +  }
    +  this.sizeIframeToWrapperGecko_();
    +};
    +
    +
    +/**
    + * Sizes the iframe to its body's height.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.sizeIframeToBodyHeightGecko_ = function() {
    +  if (this.acquireSizeIframeLockGecko_()) {
    +    var resized = false;
    +    var ifr = this.getEditableIframe();
    +    if (ifr) {
    +      var fieldHeight = this.getIframeBodyHeightGecko_();
    +
    +      if (this.minHeight_) {
    +        fieldHeight = Math.max(fieldHeight, this.minHeight_);
    +      }
    +      if (parseInt(goog.style.getStyle(ifr, 'height'), 10) != fieldHeight) {
    +        ifr.style.height = fieldHeight + 'px';
    +        resized = true;
    +      }
    +    }
    +    this.releaseSizeIframeLockGecko_();
    +    if (resized) {
    +      this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The height of the editable iframe's body.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.getIframeBodyHeightGecko_ = function() {
    +  var ifr = this.getEditableIframe();
    +  var body = ifr.contentDocument.body;
    +  var htmlElement = body.parentNode;
    +
    +
    +  // If the iframe's height is 0, then the offsetHeight/scrollHeight of the
    +  // HTML element in the iframe can be totally wack (i.e. too large
    +  // by 50-500px). Also, in standard's mode the clientHeight is 0.
    +  if (parseInt(goog.style.getStyle(ifr, 'height'), 10) === 0) {
    +    goog.style.setStyle(ifr, 'height', 1 + 'px');
    +  }
    +
    +  var fieldHeight;
    +  if (goog.editor.node.isStandardsMode(body)) {
    +
    +    // If in standards-mode,
    +    // grab the HTML element as it will contain all the field's
    +    // contents. The body's height, for example, will not include that of
    +    // floated images at the bottom in standards mode.
    +    // Note that this value include all scrollbars *except* for scrollbars
    +    // on the HTML element itself.
    +    fieldHeight = htmlElement.offsetHeight;
    +  } else {
    +    // In quirks-mode, the body-element always seems
    +    // to size to the containing window.  The html-element however,
    +    // sizes to the content, and can thus end up with a value smaller
    +    // than its child body-element if the content is shrinking.
    +    // We want to make the iframe shrink too when the content shrinks,
    +    // so rather than size the iframe to the body-element, size it to
    +    // the html-element.
    +    fieldHeight = htmlElement.scrollHeight;
    +
    +    // If there is a horizontal scroll, add in the thickness of the
    +    // scrollbar.
    +    if (htmlElement.clientHeight != htmlElement.offsetHeight) {
    +      fieldHeight += goog.editor.SeamlessField.getScrollbarWidth_();
    +    }
    +  }
    +
    +  return fieldHeight;
    +};
    +
    +
    +/**
    + * Grabs the width of a scrollbar from the browser and caches the result.
    + * @return {number} The scrollbar width in pixels.
    + * @private
    + */
    +goog.editor.SeamlessField.getScrollbarWidth_ = function() {
    +  return goog.editor.SeamlessField.scrollbarWidth_ ||
    +      (goog.editor.SeamlessField.scrollbarWidth_ =
    +          goog.style.getScrollbarWidth());
    +};
    +
    +
    +/**
    + * Sizes the iframe to its container div's width. The width of the div
    + * is controlled by its containing context, not by its contents.
    + * if it extends outside of it's contents, then it gets a horizontal scroll.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.sizeIframeToWrapperGecko_ = function() {
    +  if (this.acquireSizeIframeLockGecko_()) {
    +    var ifr = this.getEditableIframe();
    +    var field = this.getElement();
    +    var resized = false;
    +    if (ifr && field) {
    +      var fieldPaddingBox;
    +      var widthDiv = ifr.parentNode;
    +
    +      var width = widthDiv.offsetWidth;
    +      if (parseInt(goog.style.getStyle(ifr, 'width'), 10) != width) {
    +        fieldPaddingBox = goog.style.getPaddingBox(field);
    +        ifr.style.width = width + 'px';
    +        field.style.width =
    +            width - fieldPaddingBox.left - fieldPaddingBox.right + 'px';
    +        resized = true;
    +      }
    +
    +      var height = widthDiv.offsetHeight;
    +      if (this.isFixedHeight() &&
    +          parseInt(goog.style.getStyle(ifr, 'height'), 10) != height) {
    +        if (!fieldPaddingBox) {
    +          fieldPaddingBox = goog.style.getPaddingBox(field);
    +        }
    +        ifr.style.height = height + 'px';
    +        field.style.height =
    +            height - fieldPaddingBox.top - fieldPaddingBox.bottom + 'px';
    +        resized = true;
    +      }
    +
    +    }
    +    this.releaseSizeIframeLockGecko_();
    +    if (resized) {
    +      this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Perform all the sizing immediately.
    + */
    +goog.editor.SeamlessField.prototype.doFieldSizingGecko = function() {
    +  // Because doFieldSizingGecko can be called after a setTimeout
    +  // it is possible that the field has been destroyed before this call
    +  // to do the sizing is executed. Check for field existence and do nothing
    +  // if it has already been destroyed.
    +  if (this.getElement()) {
    +    // The order of operations is important here.  Sizing the iframe to the
    +    // wrapper could cause the width to change, which could change the line
    +    // wrapping, which could change the body height.  So we need to do that
    +    // first, then size the iframe to fit the body height.
    +    this.sizeIframeToWrapperGecko_();
    +    if (!this.isFixedHeight()) {
    +      this.sizeIframeToBodyHeightGecko_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Acquires a lock on resizing the field iframe. This is used to ensure that
    + * modifications we make while in a mutation event handler don't cause
    + * infinite loops.
    + * @return {boolean} False if the lock is already acquired.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.acquireSizeIframeLockGecko_ = function() {
    +  if (this.sizeIframeLock_) {
    +    return false;
    +  }
    +  return this.sizeIframeLock_ = true;
    +};
    +
    +
    +/**
    + * Releases a lock on resizing the field iframe. This is used to ensure that
    + * modifications we make while in a mutation event handler don't cause
    + * infinite loops.
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.releaseSizeIframeLockGecko_ = function() {
    +  this.sizeIframeLock_ = false;
    +};
    +
    +
    +// Functions dealing with blending in with the surrounding page.
    +
    +
    +/**
    + * String containing the css rules that, if applied to a document's body,
    + * would style that body as if it were the original element we made editable.
    + * See goog.cssom.iframe.style.getElementContext for more details.
    + * @type {string}
    + * @private
    + */
    +goog.editor.SeamlessField.prototype.iframeableCss_ = '';
    +
    +
    +/**
    + * Gets the css rules that should be used to style an iframe's body as if it
    + * were the original element that we made editable.
    + * @param {boolean=} opt_forceRegeneration Set to true to not read the cached
    + * copy and instead completely regenerate the css rules.
    + * @return {string} The string containing the css rules to use.
    + */
    +goog.editor.SeamlessField.prototype.getIframeableCss = function(
    +    opt_forceRegeneration) {
    +  if (!this.iframeableCss_ || opt_forceRegeneration) {
    +    var originalElement = this.getOriginalElement();
    +    if (originalElement) {
    +      this.iframeableCss_ =
    +          goog.cssom.iframe.style.getElementContext(originalElement,
    +          opt_forceRegeneration);
    +    }
    +  }
    +  return this.iframeableCss_;
    +};
    +
    +
    +/**
    + * Sets the css rules that should be used inside the editable iframe.
    + * Note: to clear the css cache between makeNotEditable/makeEditable,
    + * call this with "" as iframeableCss.
    + * TODO(user): Unify all these css setting methods + Nick's open
    + * CL.  This is getting ridiculous.
    + * @param {string} iframeableCss String containing the css rules to use.
    + */
    +goog.editor.SeamlessField.prototype.setIframeableCss = function(iframeableCss) {
    +  this.iframeableCss_ = iframeableCss;
    +};
    +
    +
    +/**
    + * Used to ensure that CSS stylings are only installed once for none
    + * iframe seamless mode.
    + * TODO(user): Make it a formal part of the API that you can only
    + * set one set of styles globally.
    + * In seamless, non-iframe mode, all the stylings would go in the
    + * same document and conflict.
    + * @type {boolean}
    + * @private
    + */
    +goog.editor.SeamlessField.haveInstalledCss_ = false;
    +
    +
    +/**
    + * Applies CSS from the wrapper-div to the field iframe.
    + */
    +goog.editor.SeamlessField.prototype.inheritBlendedCSS = function() {
    +  // No-op if the field isn't using an iframe.
    +  if (!this.usesIframe()) {
    +    return;
    +  }
    +  var field = this.getElement();
    +  var head = goog.dom.getDomHelper(field).getElementsByTagNameAndClass(
    +      'head')[0];
    +  if (head) {
    +    // We created this <head>, and we know the only thing we put in there
    +    // is a <style> block.  So it's safe to blow away all the children
    +    // as part of rewriting the styles.
    +    goog.dom.removeChildren(head);
    +  }
    +
    +  // Force a cache-clearing in CssUtil - this function was called because
    +  // we're applying the 'blend' for the first time, or because we
    +  // *need* to recompute the blend.
    +  var newCSS = this.getIframeableCss(true);
    +  goog.style.installStyles(newCSS, field);
    +};
    +
    +
    +// Overridden methods.
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.usesIframe = function() {
    +  // TODO(user): Switch Firefox to using contentEditable
    +  // rather than designMode iframe once contentEditable support
    +  // is less buggy.
    +  return !goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE;
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.setupMutationEventHandlersGecko =
    +    function() {
    +  goog.editor.SeamlessField.superClass_.setupMutationEventHandlersGecko.call(
    +      this);
    +
    +  if (this.usesIframe()) {
    +    var iframe = this.getEditableIframe();
    +    var outerDoc = iframe.ownerDocument;
    +    this.eventRegister.listen(outerDoc,
    +        goog.editor.Field.MUTATION_EVENTS_GECKO,
    +        this.handleOuterDocChange_, true);
    +
    +    // If the images load after we do the initial sizing, then this will
    +    // force a field resize.
    +    this.listenForIframeLoadEventKey_ = goog.events.listenOnce(
    +        this.getEditableDomHelper().getWindow(),
    +        goog.events.EventType.LOAD, this.sizeIframeToBodyHeightGecko_,
    +        true, this);
    +
    +    this.eventRegister.listen(outerDoc,
    +        'DOMAttrModified',
    +        goog.bind(this.handleDomAttrChange, this, this.handleOuterDocChange_),
    +        true);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.handleChange = function() {
    +  if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) {
    +    return;
    +  }
    +
    +  goog.editor.SeamlessField.superClass_.handleChange.call(this);
    +
    +  if (this.usesIframe()) {
    +    this.sizeIframeToBodyHeightGecko_();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.dispatchBlur = function() {
    +  if (this.isEventStopped(goog.editor.Field.EventType.BLUR)) {
    +    return;
    +  }
    +
    +  goog.editor.SeamlessField.superClass_.dispatchBlur.call(this);
    +
    +  // Clear the selection and restore the current range back after collapsing
    +  // it. The ideal solution would have been to just leave the range intact; but
    +  // when there are multiple fields present on the page, its important that
    +  // the selection isn't retained when we switch between the fields. We also
    +  // have to make sure that the cursor position is retained when we tab in and
    +  // out of a field and our approach addresses both these issues.
    +  // Another point to note is that we do it on a setTimeout to allow for
    +  // DOM modifications on blur. Otherwise, something like setLoremIpsum will
    +  // leave a blinking cursor in the field even though it's blurred.
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE &&
    +      !goog.editor.BrowserFeature.CLEARS_SELECTION_WHEN_FOCUS_LEAVES) {
    +    var win = this.getEditableDomHelper().getWindow();
    +    var dragging = false;
    +    goog.events.unlistenByKey(this.listenForDragOverEventKey_);
    +    this.listenForDragOverEventKey_ = goog.events.listenOnce(
    +        win.document.body, 'dragover',
    +        function() {
    +          dragging = true;
    +        });
    +    goog.global.setTimeout(goog.bind(function() {
    +      // Do not clear the selection if we're only dragging text.
    +      // This addresses a bug on FF1.5/linux where dragging fires a blur,
    +      // but clearing the selection confuses Firefox's drag-and-drop
    +      // implementation. For more info, see http://b/1061064
    +      if (!dragging) {
    +        if (this.editableDomHelper) {
    +          var rng = this.getRange();
    +
    +          // If there are multiple fields on a page, we need to make sure that
    +          // the selection isn't retained when we switch between fields. We
    +          // could have collapsed the range but there is a bug in GECKO where
    +          // the selection stays highlighted even though its backing range is
    +          // collapsed (http://b/1390115). To get around this, we clear the
    +          // selection and restore the collapsed range back in. Restoring the
    +          // range is important so that the cursor stays intact when we tab out
    +          // and into a field (See http://b/1790301 for additional details on
    +          // this).
    +          var iframeWindow = this.editableDomHelper.getWindow();
    +          goog.dom.Range.clearSelection(iframeWindow);
    +
    +          if (rng) {
    +            rng.collapse(true);
    +            rng.select();
    +          }
    +        }
    +      }
    +    }, this), 0);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.turnOnDesignModeGecko = function() {
    +  goog.editor.SeamlessField.superClass_.turnOnDesignModeGecko.call(this);
    +  var doc = this.getEditableDomHelper().getDocument();
    +
    +  doc.execCommand('enableInlineTableEditing', false, 'false');
    +  doc.execCommand('enableObjectResizing', false, 'false');
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.installStyles = function() {
    +  if (!this.usesIframe()) {
    +    if (!goog.editor.SeamlessField.haveInstalledCss_) {
    +      if (this.cssStyles) {
    +        goog.style.installStyles(this.cssStyles, this.getElement());
    +      }
    +
    +      // TODO(user): this should be reset to false when the editor is quit.
    +      // In non-iframe mode, CSS styles should only be instaled once.
    +      goog.editor.SeamlessField.haveInstalledCss_ = true;
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.makeEditableInternal = function(
    +    opt_iframeSrc) {
    +  if (this.usesIframe()) {
    +    goog.editor.SeamlessField.superClass_.makeEditableInternal.call(this,
    +        opt_iframeSrc);
    +  } else {
    +    var field = this.getOriginalElement();
    +    if (field) {
    +      this.setupFieldObject(field);
    +      field.contentEditable = true;
    +
    +      this.injectContents(field.innerHTML, field);
    +
    +      this.handleFieldLoad();
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.handleFieldLoad = function() {
    +  if (this.usesIframe()) {
    +    // If the CSS inheriting code screws up (e.g. makes fonts too large) and
    +    // the field is sized off in goog.editor.Field.makeIframeField, then we need
    +    // to size it correctly, but it needs to be visible for the browser
    +    // to have fully rendered it. We need to put this on a timeout to give
    +    // the browser time to render.
    +    var self = this;
    +    goog.global.setTimeout(function() {
    +      self.doFieldSizingGecko();
    +    }, 0);
    +  }
    +  goog.editor.SeamlessField.superClass_.handleFieldLoad.call(this);
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.getIframeAttributes = function() {
    +  return { 'frameBorder': 0, 'style': 'padding:0;' };
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.attachIframe = function(iframe) {
    +  this.autoDetectFixedHeight_();
    +  var field = this.getOriginalElement();
    +  var dh = goog.dom.getDomHelper(field);
    +
    +  // Grab the width/height values of the field before modifying any CSS
    +  // as some of the modifications affect its size (e.g. innerHTML='')
    +  // Here, we set the size of the field to fixed so there's not too much
    +  // jiggling when we set the innerHTML of the field.
    +  var oldWidth = field.style.width;
    +  var oldHeight = field.style.height;
    +  goog.style.setStyle(field, 'visibility', 'hidden');
    +
    +  // If there is a floated element at the bottom of the field,
    +  // then it needs a clearing div at the end to cause the clientHeight
    +  // to contain the entire field.
    +  // Also, with css re-writing, the margins of the first/last
    +  // paragraph don't seem to get included in the clientHeight. Specifically,
    +  // the extra divs below force the field's clientHeight to include the
    +  // margins on the first and last elements contained within it.
    +  var startDiv = dh.createDom(goog.dom.TagName.DIV,
    +      {'style': 'height:0;clear:both', 'innerHTML': '&nbsp;'});
    +  var endDiv = startDiv.cloneNode(true);
    +  field.insertBefore(startDiv, field.firstChild);
    +  goog.dom.appendChild(field, endDiv);
    +
    +  var contentBox = goog.style.getContentBoxSize(field);
    +  var width = contentBox.width;
    +  var height = contentBox.height;
    +
    +  var html = '';
    +  if (this.isFixedHeight()) {
    +    html = '&nbsp;';
    +
    +    goog.style.setStyle(field, 'position', 'relative');
    +    goog.style.setStyle(field, 'overflow', 'visible');
    +
    +    goog.style.setStyle(iframe, 'position', 'absolute');
    +    goog.style.setStyle(iframe, 'top', '0');
    +    goog.style.setStyle(iframe, 'left', '0');
    +  }
    +  goog.style.setSize(field, width, height);
    +
    +  // In strict mode, browsers put blank space at the bottom and right
    +  // if a field when it has an iframe child, to fill up the remaining line
    +  // height. So make the line height = 0.
    +  if (goog.editor.node.isStandardsMode(field)) {
    +    this.originalFieldLineHeight_ = field.style.lineHeight;
    +    goog.style.setStyle(field, 'lineHeight', '0');
    +  }
    +
    +  goog.editor.node.replaceInnerHtml(field, html);
    +  // Set the initial size
    +  goog.style.setSize(iframe, width, height);
    +  goog.style.setSize(field, oldWidth, oldHeight);
    +  goog.style.setStyle(field, 'visibility', '');
    +  goog.dom.appendChild(field, iframe);
    +
    +  // Only write if its not IE HTTPS in which case we're waiting for load.
    +  if (!this.shouldLoadAsynchronously()) {
    +    var doc = iframe.contentWindow.document;
    +    if (goog.editor.node.isStandardsMode(iframe.ownerDocument)) {
    +      doc.open();
    +      doc.write('<!DOCTYPE HTML><html></html>');
    +      doc.close();
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.getFieldFormatInfo = function(
    +    extraStyles) {
    +  var originalElement = this.getOriginalElement();
    +  if (originalElement) {
    +    return new goog.editor.icontent.FieldFormatInfo(
    +        this.id,
    +        goog.editor.node.isStandardsMode(originalElement),
    +        true,
    +        this.isFixedHeight(),
    +        extraStyles);
    +  }
    +  throw Error('no field');
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.writeIframeContent = function(
    +    iframe, innerHtml, extraStyles) {
    +  // For seamless iframes, hide the iframe while we're laying it out to
    +  // prevent the flicker.
    +  goog.style.setStyle(iframe, 'visibility', 'hidden');
    +  var formatInfo = this.getFieldFormatInfo(extraStyles);
    +  var styleInfo = new goog.editor.icontent.FieldStyleInfo(
    +      this.getOriginalElement(),
    +      this.cssStyles + this.getIframeableCss());
    +  goog.editor.icontent.writeNormalInitialBlendedIframe(
    +      formatInfo, innerHtml, styleInfo, iframe);
    +  this.doFieldSizingGecko();
    +  goog.style.setStyle(iframe, 'visibility', 'visible');
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.restoreDom = function() {
    +  // TODO(user): Consider only removing the iframe if we are
    +  // restoring the original node.
    +  if (this.usesIframe()) {
    +    goog.dom.removeNode(this.getEditableIframe());
    +  }
    +};
    +
    +
    +/** @override */
    +goog.editor.SeamlessField.prototype.clearListeners = function() {
    +  goog.events.unlistenByKey(this.listenForDragOverEventKey_);
    +  goog.events.unlistenByKey(this.listenForIframeLoadEventKey_);
    +
    +  goog.editor.SeamlessField.base(this, 'clearListeners');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_quirks_test.html b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_quirks_test.html
    new file mode 100644
    index 0000000000..33ecea0efb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_quirks_test.html
    @@ -0,0 +1,28 @@
    +<!--
    +  All Rights Reserved.
    +
    +  NOTE: This file should be an exact copy of seamlessfield_test.html,
    +  except for this comment and that the <!DOCTYPE> tag should be removed from
    +  the top. If seamlessfield_test.html is changed, this file should be
    +  changed accordingly.
    +
    +  @author nicksantos@google.com (Nick Santos)
    +--><html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Trogedit Unit Tests - goog.editor.SeamlessField</title>
    +<script src="../base.js"></script>
    +</head>
    +<body>
    +
    +<div id='field'>
    +</div>
    +
    +<script src="seamlessfield_test.js"></script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.html b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.html
    new file mode 100644
    index 0000000000..36d4e0fca5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.html
    @@ -0,0 +1,32 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  NOTE: If any changes are made to this file, please keep
    +  seamlessfield_quirks_test.html synced with this. That file should
    +  be an exact copy of this one, except for this comment and that the
    +  <!DOCTYPE> tag should be removed from the top. All tests should be added
    +  to seamlessfield_test.js so that they will be run by both
    +  seamlessfield*_test.html files.
    +  
    +  @author nicksantos@google.com (Nick Santos)
    +--><html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Trogedit Unit Tests - goog.editor.SeamlessField</title>
    +<script src="../base.js"></script>
    +</head>
    +<body>
    +
    +<div id='field'>
    +</div>
    +
    +<script src="seamlessfield_test.js"></script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.js b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.js
    new file mode 100644
    index 0000000000..65e5cdfaaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/seamlessfield_test.js
    @@ -0,0 +1,469 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Trogedit unit tests for goog.editor.SeamlessField.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + * @suppress {missingProperties} There are many mocks in this unit test,
    + *     and the mocks don't fit well in the type system.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.editor.seamlessfield_test');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Field');
    +goog.require('goog.editor.SeamlessField');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockRange');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('seamlessfield_test');
    +
    +var fieldElem;
    +var fieldElemClone;
    +
    +function setUp() {
    +  fieldElem = goog.dom.getElement('field');
    +  fieldElemClone = fieldElem.cloneNode(true);
    +}
    +
    +function tearDown() {
    +  fieldElem.parentNode.replaceChild(fieldElemClone, fieldElem);
    +}
    +
    +// the following tests check for blended iframe positioning. They really
    +// only make sense on browsers without contentEditable.
    +function testBlankField() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField('&nbsp;', {}), createSeamlessIframe());
    +  }
    +}
    +
    +function testFieldWithContent() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField('Hi!', {}), createSeamlessIframe());
    +  }
    +}
    +
    +function testFieldWithPadding() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField('Hi!', {'padding': '2px 5px'}),
    +        createSeamlessIframe());
    +  }
    +}
    +
    +function testFieldWithMargin() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField('Hi!', {'margin': '2px 5px'}),
    +        createSeamlessIframe());
    +  }
    +}
    +
    +function testFieldWithBorder() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField('Hi!', {'border': '2px 5px'}),
    +        createSeamlessIframe());
    +  }
    +}
    +
    +function testFieldWithOverflow() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    assertAttachSeamlessIframeSizesCorrectly(
    +        initSeamlessField(['1', '2', '3', '4', '5', '6', '7'].join('<p/>'),
    +        {'overflow': 'auto', 'position': 'relative', 'height': '20px'}),
    +        createSeamlessIframe());
    +    assertEquals(20, fieldElem.offsetHeight);
    +  }
    +}
    +
    +function testFieldWithOverflowAndPadding() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    var blendedField = initSeamlessField(
    +        ['1', '2', '3', '4', '5', '6', '7'].join('<p/>'),
    +        {
    +          'overflow': 'auto',
    +          'position': 'relative',
    +          'height': '20px',
    +          'padding': '2px 3px'
    +        });
    +    var blendedIframe = createSeamlessIframe();
    +    assertAttachSeamlessIframeSizesCorrectly(blendedField, blendedIframe);
    +    assertEquals(24, fieldElem.offsetHeight);
    +  }
    +}
    +
    +function testIframeHeightGrowsOnWrap() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    var clock = new goog.testing.MockClock(true);
    +    var blendedField;
    +    try {
    +      blendedField = initSeamlessField('',
    +          {'border': '1px solid black', 'height': '20px'});
    +      blendedField.makeEditable();
    +      blendedField.setHtml(false, 'Content that should wrap after resize.');
    +
    +      // Ensure that the field was fully loaded and sized before measuring.
    +      clock.tick(1);
    +
    +      // Capture starting heights.
    +      var unwrappedIframeHeight = blendedField.getEditableIframe().offsetHeight;
    +
    +      // Resize the field such that the text should wrap.
    +      fieldElem.style.width = '200px';
    +      blendedField.doFieldSizingGecko();
    +
    +      // Iframe should grow as a result.
    +      var wrappedIframeHeight = blendedField.getEditableIframe().offsetHeight;
    +      assertTrue('Wrapped text should cause iframe to grow - initial height: ' +
    +          unwrappedIframeHeight + ', wrapped height: ' + wrappedIframeHeight,
    +          wrappedIframeHeight > unwrappedIframeHeight);
    +    } finally {
    +      blendedField.dispose();
    +      clock.dispose();
    +    }
    +  }
    +}
    +
    +function testDispatchIframeResizedForWrapperHeight() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    var clock = new goog.testing.MockClock(true);
    +    var blendedField = initSeamlessField('Hi!', {'border': '2px 5px'});
    +    var iframe = createSeamlessIframe();
    +    blendedField.attachIframe(iframe);
    +
    +    var resizeCalled = false;
    +    goog.events.listenOnce(
    +        blendedField,
    +        goog.editor.Field.EventType.IFRAME_RESIZED,
    +        function() {
    +          resizeCalled = true;
    +        });
    +
    +    try {
    +      blendedField.makeEditable();
    +      blendedField.setHtml(false, 'Content that should wrap after resize.');
    +
    +      // Ensure that the field was fully loaded and sized before measuring.
    +      clock.tick(1);
    +
    +      assertFalse('Iframe resize must not be dispatched yet', resizeCalled);
    +
    +      // Resize the field such that the text should wrap.
    +      fieldElem.style.width = '200px';
    +      blendedField.sizeIframeToWrapperGecko_();
    +      assertTrue('Iframe resize must be dispatched for Wrapper', resizeCalled);
    +    } finally {
    +      blendedField.dispose();
    +      clock.dispose();
    +    }
    +  }
    +}
    +
    +function testDispatchIframeResizedForBodyHeight() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    var clock = new goog.testing.MockClock(true);
    +    var blendedField = initSeamlessField('Hi!', {'border': '2px 5px'});
    +    var iframe = createSeamlessIframe();
    +    blendedField.attachIframe(iframe);
    +
    +    var resizeCalled = false;
    +    goog.events.listenOnce(
    +        blendedField,
    +        goog.editor.Field.EventType.IFRAME_RESIZED,
    +        function() {
    +          resizeCalled = true;
    +        });
    +
    +    try {
    +      blendedField.makeEditable();
    +      blendedField.setHtml(false, 'Content that should wrap after resize.');
    +
    +      // Ensure that the field was fully loaded and sized before measuring.
    +      clock.tick(1);
    +
    +      assertFalse('Iframe resize must not be dispatched yet', resizeCalled);
    +
    +      // Resize the field to a different body height.
    +      var bodyHeight = blendedField.getIframeBodyHeightGecko_();
    +      blendedField.getIframeBodyHeightGecko_ = function() {
    +        return bodyHeight + 1;
    +      };
    +      blendedField.sizeIframeToBodyHeightGecko_();
    +      assertTrue('Iframe resize must be dispatched for Body', resizeCalled);
    +    } finally {
    +      blendedField.dispose();
    +      clock.dispose();
    +    }
    +  }
    +}
    +
    +function testDispatchBlur() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE &&
    +      !goog.editor.BrowserFeature.CLEARS_SELECTION_WHEN_FOCUS_LEAVES) {
    +    var blendedField = initSeamlessField('Hi!', {'border': '2px 5px'});
    +    var iframe = createSeamlessIframe();
    +    blendedField.attachIframe(iframe);
    +
    +    var blurCalled = false;
    +    goog.events.listenOnce(blendedField, goog.editor.Field.EventType.BLUR,
    +        function() {
    +          blurCalled = true;
    +        });
    +
    +    var clearSelection = goog.dom.Range.clearSelection;
    +    var cleared = false;
    +    var clearedWindow;
    +    blendedField.editableDomHelper = new goog.dom.DomHelper();
    +    blendedField.editableDomHelper.getWindow =
    +        goog.functions.constant(iframe.contentWindow);
    +    var mockRange = new goog.testing.MockRange();
    +    blendedField.getRange = function() {
    +      return mockRange;
    +    };
    +    goog.dom.Range.clearSelection = function(opt_window) {
    +      clearSelection(opt_window);
    +      cleared = true;
    +      clearedWindow = opt_window;
    +    };
    +    var clock = new goog.testing.MockClock(true);
    +
    +    mockRange.collapse(true);
    +    mockRange.select();
    +    mockRange.$replay();
    +    blendedField.dispatchBlur();
    +    clock.tick(1);
    +
    +    assertTrue('Blur must be dispatched.', blurCalled);
    +    assertTrue('Selection must be cleared.', cleared);
    +    assertEquals('Selection must be cleared in iframe',
    +        iframe.contentWindow, clearedWindow);
    +    mockRange.$verify();
    +    clock.dispose();
    +  }
    +}
    +
    +function testSetMinHeight() {
    +  if (!goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    var clock = new goog.testing.MockClock(true);
    +    try {
    +      var field = initSeamlessField(
    +          ['1', '2', '3', '4', '5', '6', '7'].join('<p/>'),
    +          {'position': 'relative', 'height': '60px'});
    +
    +      // Initially create and size iframe.
    +      var iframe = createSeamlessIframe();
    +      field.attachIframe(iframe);
    +      field.iframeFieldLoadHandler(iframe, '', {});
    +      // Need to process timeouts set by load handlers.
    +      clock.tick(1000);
    +
    +      var normalHeight = goog.style.getSize(iframe).height;
    +
    +      var delayedChangeCalled = false;
    +      goog.events.listen(field, goog.editor.Field.EventType.DELAYEDCHANGE,
    +          function() {
    +            delayedChangeCalled = true;
    +          });
    +
    +      // Test that min height is obeyed.
    +      field.setMinHeight(30);
    +      clock.tick(1000);
    +      assertEquals('Iframe height must match min height.',
    +          30, goog.style.getSize(iframe).height);
    +      assertFalse('Setting min height must not cause delayed change event.',
    +          delayedChangeCalled);
    +
    +      // Test that min height doesn't shrink field.
    +      field.setMinHeight(0);
    +      clock.tick(1000);
    +      assertEquals(normalHeight, goog.style.getSize(iframe).height);
    +      assertFalse('Setting min height must not cause delayed change event.',
    +          delayedChangeCalled);
    +    } finally {
    +      field.dispose();
    +      clock.dispose();
    +    }
    +  }
    +}
    +
    +
    +/**
    + * @bug 1649967 This code used to throw a Javascript error.
    + */
    +function testSetMinHeightWithNoIframe() {
    +  if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    try {
    +      var field = initSeamlessField('&nbsp;', {});
    +      field.makeEditable();
    +      field.setMinHeight(30);
    +    } finally {
    +      field.dispose();
    +    }
    +  }
    +}
    +
    +function testStartChangeEvents() {
    +  if (goog.editor.BrowserFeature.USE_MUTATION_EVENTS) {
    +    var clock = new goog.testing.MockClock(true);
    +
    +    try {
    +      var field = initSeamlessField('&nbsp;', {});
    +      field.makeEditable();
    +
    +      var changeCalled = false;
    +      goog.events.listenOnce(field, goog.editor.Field.EventType.CHANGE,
    +          function() {
    +            changeCalled = true;
    +          });
    +
    +      var delayedChangeCalled = false;
    +      goog.events.listenOnce(field, goog.editor.Field.EventType.CHANGE,
    +          function() {
    +            delayedChangeCalled = true;
    +          });
    +
    +      field.stopChangeEvents(true, true);
    +      if (field.changeTimerGecko_) {
    +        field.changeTimerGecko_.start();
    +      }
    +
    +      field.startChangeEvents();
    +      clock.tick(1000);
    +
    +      assertFalse(changeCalled);
    +      assertFalse(delayedChangeCalled);
    +    } finally {
    +      clock.dispose();
    +      field.dispose();
    +    }
    +  }
    +}
    +
    +function testManipulateDom() {
    +  // Test in blended field since that is what fires change events.
    +  var editableField = initSeamlessField('&nbsp;', {});
    +  var clock = new goog.testing.MockClock(true);
    +
    +  var delayedChangeCalled = 0;
    +  goog.events.listen(editableField, goog.editor.Field.EventType.DELAYEDCHANGE,
    +      function() {
    +        delayedChangeCalled++;
    +      });
    +
    +  assertFalse(editableField.isLoaded());
    +  editableField.manipulateDom(goog.nullFunction);
    +  clock.tick(1000);
    +  assertEquals('Must not fire delayed change events if field is not loaded.',
    +      0, delayedChangeCalled);
    +
    +  editableField.makeEditable();
    +  var usesIframe = editableField.usesIframe();
    +
    +  try {
    +    editableField.manipulateDom(goog.nullFunction);
    +    clock.tick(1000); // Wait for delayed change to fire.
    +    assertEquals('By default must fire a single delayed change event.',
    +        1, delayedChangeCalled);
    +
    +    editableField.manipulateDom(goog.nullFunction, true);
    +    clock.tick(1000); // Wait for delayed change to fire.
    +    assertEquals('Must prevent all delayed change events.',
    +        1, delayedChangeCalled);
    +
    +    editableField.manipulateDom(function() {
    +      this.handleChange();
    +      this.handleChange();
    +      if (this.changeTimerGecko_) {
    +        this.changeTimerGecko_.fire();
    +      }
    +
    +      this.dispatchDelayedChange_();
    +      this.delayedChangeTimer_.fire();
    +    }, false, editableField);
    +    clock.tick(1000); // Wait for delayed change to fire.
    +    assertEquals('Must ignore dispatch delayed change called within func.',
    +        2, delayedChangeCalled);
    +  } finally {
    +    // Ensure we always uninstall the mock clock and dispose of everything.
    +    editableField.dispose();
    +    clock.dispose();
    +  }
    +}
    +
    +function testAttachIframe() {
    +  var blendedField = initSeamlessField('Hi!', {});
    +  var iframe = createSeamlessIframe();
    +  try {
    +    blendedField.attachIframe(iframe);
    +  } catch (err) {
    +    fail('Error occurred while attaching iframe.');
    +  }
    +}
    +
    +
    +function createSeamlessIframe() {
    +  // NOTE(nicksantos): This is a reimplementation of
    +  // TR_EditableUtil.getIframeAttributes, but untangled for tests, and
    +  // specifically with what we need for blended mode.
    +  return goog.dom.createDom('IFRAME',
    +      { 'frameBorder': '0', 'style': 'padding:0;' });
    +}
    +
    +
    +/**
    + * Initialize a new editable field for the field id 'field', with the given
    + * innerHTML and styles.
    + *
    + * @param {string} innerHTML html for the field contents.
    + * @param {Object} styles Key-value pairs for styles on the field.
    + * @return {goog.editor.SeamlessField} The field.
    + */
    +function initSeamlessField(innerHTML, styles) {
    +  var field = new goog.editor.SeamlessField('field');
    +  fieldElem.innerHTML = innerHTML;
    +  goog.style.setStyle(fieldElem, styles);
    +  return field;
    +}
    +
    +
    +/**
    + * Make sure that the original field element for the given goog.editor.Field has
    + * the same size before and after attaching the given iframe. If this is not
    + * true, then the field will fidget while we're initializing the field,
    + * and that's not what we want.
    + *
    + * @param {goog.editor.Field} fieldObj The field.
    + * @param {HTMLIFrameElement} iframe The iframe.
    + */
    +function assertAttachSeamlessIframeSizesCorrectly(fieldObj, iframe) {
    +  var size = goog.style.getSize(fieldObj.getOriginalElement());
    +  fieldObj.attachIframe(iframe);
    +  var newSize = goog.style.getSize(fieldObj.getOriginalElement());
    +
    +  assertEquals(size.width, newSize.width);
    +  assertEquals(size.height, newSize.height);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/style.js b/mapbox-gl/closure-library/closure/goog/editor/style.js
    new file mode 100644
    index 0000000000..53a8b41613
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/style.js
    @@ -0,0 +1,225 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilties for working with the styles of DOM nodes, and
    + * related to rich text editing.
    + *
    + * Many of these are not general enough to go into goog.style, and use
    + * constructs (like "isContainer") that only really make sense inside
    + * of an HTML editor.
    + *
    + * The API has been optimized for iterating over large, irregular DOM
    + * structures (with lots of text nodes), and so the API tends to be a bit
    + * more permissive than the goog.style API should be. For example,
    + * goog.style.getComputedStyle will throw an exception if you give it a
    + * text node.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.editor.style');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Gets the computed or cascaded style.
    + *
    + * This is different than goog.style.getStyle_ because it returns null
    + * for text nodes (instead of throwing an exception), and never reads
    + * inline style. These two functions may need to be reconciled.
    + *
    + * @param {!Node} node Node to get style of.
    + * @param {string} stylePropertyName Property to get (must be camelCase,
    + *     not css-style).
    + * @return {?string} Style value, or null if this is not an element node.
    + * @private
    + */
    +goog.editor.style.getComputedOrCascadedStyle_ = function(
    +    node, stylePropertyName) {
    +  if (node.nodeType != goog.dom.NodeType.ELEMENT) {
    +    // Only element nodes have style.
    +    return null;
    +  }
    +  return goog.userAgent.IE ?
    +      goog.style.getCascadedStyle(/** @type {!Element} */ (node),
    +          stylePropertyName) :
    +      goog.style.getComputedStyle(/** @type {!Element} */ (node),
    +          stylePropertyName);
    +};
    +
    +
    +/**
    + * Checks whether the given element inherits display: block.
    + * @param {!Node} node The Node to check.
    + * @return {boolean} Whether the element inherits CSS display: block.
    + */
    +goog.editor.style.isDisplayBlock = function(node) {
    +  return goog.editor.style.getComputedOrCascadedStyle_(
    +      node, 'display') == 'block';
    +};
    +
    +
    +/**
    + * Returns true if the element is a container of other non-inline HTML
    + * Note that span, strong and em tags, being inline can only contain
    + * other inline elements and are thus, not containers. Containers are elements
    + * that should not be broken up when wrapping selections with a node of an
    + * inline block styling.
    + * @param {Node} element The element to check.
    + * @return {boolean} Whether the element is a container.
    + */
    +goog.editor.style.isContainer = function(element) {
    +  var nodeName = element && element.nodeName.toLowerCase();
    +  return !!(element &&
    +      (goog.editor.style.isDisplayBlock(element) ||
    +          nodeName == 'td' ||
    +          nodeName == 'table' ||
    +          nodeName == 'li'));
    +};
    +
    +
    +/**
    + * Return the first ancestor of this node that is a container, inclusive.
    + * @see isContainer
    + * @param {Node} node Node to find the container of.
    + * @return {Element} The element which contains node.
    + */
    +goog.editor.style.getContainer = function(node) {
    +  // We assume that every node must have a container.
    +  return /** @type {Element} */ (
    +      goog.dom.getAncestor(node, goog.editor.style.isContainer, true));
    +};
    +
    +
    +/**
    + * Set of input types that should be kept selectable even when their ancestors
    + * are made unselectable.
    + * @type {Object}
    + * @private
    + */
    +goog.editor.style.SELECTABLE_INPUT_TYPES_ = goog.object.createSet(
    +    'text', 'file', 'url');
    +
    +
    +/**
    + * Prevent the default action on mousedown events.
    + * @param {goog.events.Event} e The mouse down event.
    + * @private
    + */
    +goog.editor.style.cancelMouseDownHelper_ = function(e) {
    +  var targetTagName = e.target.tagName;
    +  if (targetTagName != goog.dom.TagName.TEXTAREA &&
    +      targetTagName != goog.dom.TagName.INPUT) {
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Makes the given element unselectable, as well as all of its children, except
    + * for text areas, text, file and url inputs.
    + * @param {Element} element The element to make unselectable.
    + * @param {goog.events.EventHandler} eventHandler An EventHandler to register
    + *     the event with. Assumes when the node is destroyed, the eventHandler's
    + *     listeners are destroyed as well.
    + */
    +goog.editor.style.makeUnselectable = function(element, eventHandler) {
    +  if (goog.editor.BrowserFeature.HAS_UNSELECTABLE_STYLE) {
    +    // The mousing down on a node should not blur the focused node.
    +    // This is consistent with how IE works.
    +    // TODO: Consider using just the mousedown handler and not the css property.
    +    eventHandler.listen(element, goog.events.EventType.MOUSEDOWN,
    +        goog.editor.style.cancelMouseDownHelper_, true);
    +  }
    +
    +  goog.style.setUnselectable(element, true);
    +
    +  // Make inputs and text areas selectable.
    +  var inputs = element.getElementsByTagName(goog.dom.TagName.INPUT);
    +  for (var i = 0, len = inputs.length; i < len; i++) {
    +    var input = inputs[i];
    +    if (input.type in goog.editor.style.SELECTABLE_INPUT_TYPES_) {
    +      goog.editor.style.makeSelectable(input);
    +    }
    +  }
    +  goog.array.forEach(element.getElementsByTagName(goog.dom.TagName.TEXTAREA),
    +      goog.editor.style.makeSelectable);
    +};
    +
    +
    +/**
    + * Make the given element selectable.
    + *
    + * For IE this simply turns off the "unselectable" property.
    + *
    + * Under FF no descendent of an unselectable node can be selectable:
    + *
    + * https://bugzilla.mozilla.org/show_bug.cgi?id=203291
    + *
    + * So we make each ancestor of node selectable, while trying to preserve the
    + * unselectability of other nodes along that path
    + *
    + * This may cause certain text nodes which should be unselectable, to become
    + * selectable. For example:
    + *
    + * <div id=div1 style="-moz-user-select: none">
    + *   Text1
    + *   <span id=span1>Text2</span>
    + * </div>
    + *
    + * If we call makeSelectable on span1, then it will cause "Text1" to become
    + * selectable, since it had to make div1 selectable in order for span1 to be
    + * selectable.
    + *
    + * If "Text1" were enclosed within a <p> or <span>, then this problem would
    + * not arise.  Text nodes do not have styles, so its style can't be set to
    + * unselectable.
    + *
    + * @param {Element} element The element to make selectable.
    + */
    +goog.editor.style.makeSelectable = function(element) {
    +  goog.style.setUnselectable(element, false);
    +  if (goog.editor.BrowserFeature.HAS_UNSELECTABLE_STYLE) {
    +    // Go up ancestor chain, searching for nodes that are unselectable.
    +    // If such a node exists, mark it as selectable but mark its other children
    +    // as unselectable so the minimum set of nodes is changed.
    +    var child = element;
    +    var current = /** @type {Element} */ (element.parentNode);
    +    while (current && current.tagName != goog.dom.TagName.HTML) {
    +      if (goog.style.isUnselectable(current)) {
    +        goog.style.setUnselectable(current, false, true);
    +
    +        for (var i = 0, len = current.childNodes.length; i < len; i++) {
    +          var node = current.childNodes[i];
    +          if (node != child && node.nodeType == goog.dom.NodeType.ELEMENT) {
    +            goog.style.setUnselectable(current.childNodes[i], true);
    +          }
    +        }
    +      }
    +
    +      child = current;
    +      current = /** @type {Element} */ (current.parentNode);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/style_test.html b/mapbox-gl/closure-library/closure/goog/editor/style_test.html
    new file mode 100644
    index 0000000000..98779546ef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/style_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  Author: nicksantos@google.com (Nick Santos)
    +-->
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.editor.style
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.styleTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/style_test.js b/mapbox-gl/closure-library/closure/goog/editor/style_test.js
    new file mode 100644
    index 0000000000..300795d37f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/style_test.js
    @@ -0,0 +1,174 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.styleTest');
    +goog.setTestOnly('goog.editor.styleTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.style');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +
    +var parentNode = null;
    +var childNode1 = null;
    +var childNode2 = null;
    +var childNode3 = null;
    +var gChildWsNode1 = null;
    +var gChildTextNode1 = null;
    +var gChildNbspNode1 = null;
    +var gChildMixedNode1 = null;
    +var gChildWsNode2a = null;
    +var gChildWsNode2b = null;
    +var gChildTextNode3a = null;
    +var gChildWsNode3 = null;
    +var gChildTextNode3b = null;
    +
    +var $dom = goog.dom.createDom;
    +var $text = goog.dom.createTextNode;
    +
    +function setUpGetNodeFunctions() {
    +  parentNode = $dom(
    +      'p', {id: 'parentNode'},
    +      childNode1 = $dom('div', null,
    +          gChildWsNode1 = $text(' \t\r\n'),
    +          gChildTextNode1 = $text('Child node'),
    +          gChildNbspNode1 = $text('\u00a0'),
    +          gChildMixedNode1 = $text('Text\n plus\u00a0')),
    +      childNode2 = $dom('div', null,
    +          gChildWsNode2a = $text(''),
    +          gChildWsNode2b = $text(' ')),
    +      childNode3 = $dom('div', null,
    +          gChildTextNode3a = $text('I am a grand child'),
    +          gChildWsNode3 = $text('   \t  \r   \n'),
    +          gChildTextNode3b = $text('I am also a grand child')));
    +
    +  document.body.appendChild(parentNode);
    +}
    +
    +function tearDownGetNodeFunctions() {
    +  document.body.removeChild(parentNode);
    +
    +  parentNode = null;
    +  childNode1 = null;
    +  childNode2 = null;
    +  childNode3 = null;
    +  gChildWsNode1 = null;
    +  gChildTextNode1 = null;
    +  gChildNbspNode1 = null;
    +  gChildMixedNode1 = null;
    +  gChildWsNode2a = null;
    +  gChildWsNode2b = null;
    +  gChildTextNode3a = null;
    +  gChildWsNode3 = null;
    +  gChildTextNode3b = null;
    +}
    +
    +
    +/**
    + * Test isBlockLevel with a node that is block style and a node that is not
    + */
    +function testIsDisplayBlock() {
    +  assertTrue('Body is block style',
    +      goog.editor.style.isDisplayBlock(document.body));
    +  var tableNode = $dom('table');
    +  assertFalse('Table is not block style',
    +      goog.editor.style.isDisplayBlock(tableNode));
    +}
    +
    +
    +/**
    + * Test that isContainer returns true when the node is of non-inline HTML and
    + * false when it is not
    + */
    +function testIsContainer() {
    +  var tableNode = $dom('table');
    +  var liNode = $dom('li');
    +  var textNode = $text('I am text');
    +  document.body.appendChild(textNode);
    +
    +  assertTrue('Table is a container',
    +      goog.editor.style.isContainer(tableNode));
    +  assertTrue('Body is a container',
    +      goog.editor.style.isContainer(document.body));
    +  assertTrue('List item is a container',
    +      goog.editor.style.isContainer(liNode));
    +  assertFalse('Text node is not a container',
    +      goog.editor.style.isContainer(textNode));
    +}
    +
    +
    +/**
    + * Test that getContainer properly returns the node itself if it is a
    + * container, an ancestor node if it is a container, and null otherwise
    + */
    +function testGetContainer() {
    +  setUpGetNodeFunctions();
    +  assertEquals('Should return self', childNode1,
    +      goog.editor.style.getContainer(childNode1));
    +  assertEquals('Should return parent', childNode1,
    +      goog.editor.style.getContainer(gChildWsNode1));
    +  assertNull('Document has no ancestors',
    +      goog.editor.style.getContainer(document));
    +  tearDownGetNodeFunctions();
    +}
    +
    +
    +function testMakeUnselectable() {
    +  var div = goog.dom.createElement(goog.dom.TagName.DIV);
    +  div.innerHTML =
    +      '<div>No input</div>' +
    +      '<p><input type="checkbox">Checkbox</p>' +
    +      '<span><input type="text"></span>';
    +  document.body.appendChild(div);
    +
    +  var eventHandler = new goog.testing.LooseMock(goog.events.EventHandler);
    +  if (goog.editor.BrowserFeature.HAS_UNSELECTABLE_STYLE) {
    +    eventHandler.listen(div, goog.events.EventType.MOUSEDOWN,
    +        goog.testing.mockmatchers.isFunction, true);
    +  }
    +  eventHandler.$replay();
    +
    +
    +  var childDiv = div.firstChild;
    +  var p = div.childNodes[1];
    +  var span = div.lastChild;
    +  var checkbox = p.firstChild;
    +  var text = span.firstChild;
    +
    +  goog.editor.style.makeUnselectable(div, eventHandler);
    +
    +  assertEquals(
    +      'For browsers with non-overridable selectability, the root should be ' +
    +      'selectable.  Otherwise it should be unselectable.',
    +      !goog.editor.BrowserFeature.HAS_UNSELECTABLE_STYLE,
    +      goog.style.isUnselectable(div));
    +  assertTrue(goog.style.isUnselectable(childDiv));
    +  assertTrue(goog.style.isUnselectable(p));
    +  assertTrue(goog.style.isUnselectable(checkbox));
    +
    +  assertEquals(
    +      'For browsers with non-overridable selectability, the span will be ' +
    +      'selectable.  Otherwise it will be unselectable. ',
    +      !goog.editor.BrowserFeature.HAS_UNSELECTABLE_STYLE,
    +      goog.style.isUnselectable(span));
    +  assertFalse(goog.style.isUnselectable(text));
    +
    +  eventHandler.$verify();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/table.js b/mapbox-gl/closure-library/closure/goog/editor/table.js
    new file mode 100644
    index 0000000000..602e5f1045
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/table.js
    @@ -0,0 +1,570 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Table editing support.
    + * This file provides the class goog.editor.Table and two
    + * supporting classes, goog.editor.TableRow and
    + * goog.editor.TableCell. Together these provide support for
    + * high level table modifications: Adding and deleting rows and columns,
    + * and merging and splitting cells.
    + *
    + * @supported IE6+, WebKit 525+, Firefox 2+.
    + */
    +
    +goog.provide('goog.editor.Table');
    +goog.provide('goog.editor.TableCell');
    +goog.provide('goog.editor.TableRow');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.log');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Class providing high level table editing functions.
    + * @param {Element} node Element that is a table or descendant of a table.
    + * @constructor
    + * @final
    + */
    +goog.editor.Table = function(node) {
    +  this.element = goog.dom.getAncestorByTagNameAndClass(node,
    +      goog.dom.TagName.TABLE);
    +  if (!this.element) {
    +    goog.log.error(this.logger_,
    +        "Can't create Table based on a node " +
    +        "that isn't a table, or descended from a table.");
    +  }
    +  this.dom_ = goog.dom.getDomHelper(this.element);
    +  this.refresh();
    +};
    +
    +
    +/**
    + * Logger object for debugging and error messages.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.editor.Table.prototype.logger_ =
    +    goog.log.getLogger('goog.editor.Table');
    +
    +
    +/**
    + * Walks the dom structure of this object's table element and populates
    + * this.rows with goog.editor.TableRow objects. This is done initially
    + * to populate the internal data structures, and also after each time the
    + * DOM structure is modified. Currently this means that the all existing
    + * information is discarded and re-read from the DOM.
    + */
    +// TODO(user): support partial refresh to save cost of full update
    +// every time there is a change to the DOM.
    +goog.editor.Table.prototype.refresh = function() {
    +  var rows = this.rows = [];
    +  var tbody = this.element.getElementsByTagName(goog.dom.TagName.TBODY)[0];
    +  if (!tbody) {
    +    return;
    +  }
    +  var trs = [];
    +  for (var child = tbody.firstChild; child; child = child.nextSibling) {
    +    if (child.nodeName == goog.dom.TagName.TR) {
    +      trs.push(child);
    +    }
    +  }
    +
    +  for (var rowNum = 0, tr; tr = trs[rowNum]; rowNum++) {
    +    var existingRow = rows[rowNum];
    +    var tds = goog.editor.Table.getChildCellElements(tr);
    +    var columnNum = 0;
    +    // A note on cellNum vs. columnNum: A cell is a td/th element. Cells may
    +    // use colspan/rowspan to extend over multiple rows/columns. cellNum
    +    // is the dom element number, columnNum is the logical column number.
    +    for (var cellNum = 0, td; td = tds[cellNum]; cellNum++) {
    +      // If there's already a cell extending into this column
    +      // (due to that cell's colspan/rowspan), increment the column counter.
    +      while (existingRow && existingRow.columns[columnNum]) {
    +        columnNum++;
    +      }
    +      var cell = new goog.editor.TableCell(td, rowNum, columnNum);
    +      // Place this cell in every row and column into which it extends.
    +      for (var i = 0; i < cell.rowSpan; i++) {
    +        var cellRowNum = rowNum + i;
    +        // Create TableRow objects in this.rows as needed.
    +        var cellRow = rows[cellRowNum];
    +        if (!cellRow) {
    +          // TODO(user): try to avoid second trs[] lookup.
    +          rows.push(
    +              cellRow = new goog.editor.TableRow(trs[cellRowNum], cellRowNum));
    +        }
    +        // Extend length of column array to make room for this cell.
    +        var minimumColumnLength = columnNum + cell.colSpan;
    +        if (cellRow.columns.length < minimumColumnLength) {
    +          cellRow.columns.length = minimumColumnLength;
    +        }
    +        for (var j = 0; j < cell.colSpan; j++) {
    +          var cellColumnNum = columnNum + j;
    +          cellRow.columns[cellColumnNum] = cell;
    +        }
    +      }
    +      columnNum += cell.colSpan;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns all child elements of a TR element that are of type TD or TH.
    + * @param {Element} tr TR element in which to find children.
    + * @return {!Array<Element>} array of child cell elements.
    + */
    +goog.editor.Table.getChildCellElements = function(tr) {
    +  var cells = [];
    +  for (var i = 0, cell; cell = tr.childNodes[i]; i++) {
    +    if (cell.nodeName == goog.dom.TagName.TD ||
    +        cell.nodeName == goog.dom.TagName.TH) {
    +      cells.push(cell);
    +    }
    +  }
    +  return cells;
    +};
    +
    +
    +/**
    + * Inserts a new row in the table. The row will be populated with new
    + * cells, and existing rowspanned cells that overlap the new row will
    + * be extended.
    + * @param {number=} opt_rowIndex Index at which to insert the row. If
    + *     this is omitted the row will be appended to the end of the table.
    + * @return {!Element} The new row.
    + */
    +goog.editor.Table.prototype.insertRow = function(opt_rowIndex) {
    +  var rowIndex = goog.isDefAndNotNull(opt_rowIndex) ?
    +      opt_rowIndex : this.rows.length;
    +  var refRow;
    +  var insertAfter;
    +  if (rowIndex == 0) {
    +    refRow = this.rows[0];
    +    insertAfter = false;
    +  } else {
    +    refRow = this.rows[rowIndex - 1];
    +    insertAfter = true;
    +  }
    +  var newTr = this.dom_.createElement('tr');
    +  for (var i = 0, cell; cell = refRow.columns[i]; i += 1) {
    +    // Check whether the existing cell will span this new row.
    +    // If so, instead of creating a new cell, extend
    +    // the rowspan of the existing cell.
    +    if ((insertAfter && cell.endRow > rowIndex) ||
    +        (!insertAfter && cell.startRow < rowIndex)) {
    +      cell.setRowSpan(cell.rowSpan + 1);
    +      if (cell.colSpan > 1) {
    +        i += cell.colSpan - 1;
    +      }
    +    } else {
    +      newTr.appendChild(this.createEmptyTd());
    +    }
    +    if (insertAfter) {
    +      goog.dom.insertSiblingAfter(newTr, refRow.element);
    +    } else {
    +      goog.dom.insertSiblingBefore(newTr, refRow.element);
    +    }
    +  }
    +  this.refresh();
    +  return newTr;
    +};
    +
    +
    +/**
    + * Inserts a new column in the table. The column will be created by
    + * inserting new TD elements in each row, or extending the colspan
    + * of existing TD elements.
    + * @param {number=} opt_colIndex Index at which to insert the column. If
    + *     this is omitted the column will be appended to the right side of
    + *     the table.
    + * @return {!Array<Element>} Array of new cell elements that were created
    + *     to populate the new column.
    + */
    +goog.editor.Table.prototype.insertColumn = function(opt_colIndex) {
    +  // TODO(user): set column widths in a way that makes sense.
    +  var colIndex = goog.isDefAndNotNull(opt_colIndex) ?
    +      opt_colIndex :
    +      (this.rows[0] && this.rows[0].columns.length) || 0;
    +  var newTds = [];
    +  for (var rowNum = 0, row; row = this.rows[rowNum]; rowNum++) {
    +    var existingCell = row.columns[colIndex];
    +    if (existingCell && existingCell.endCol >= colIndex &&
    +        existingCell.startCol < colIndex) {
    +      existingCell.setColSpan(existingCell.colSpan + 1);
    +      rowNum += existingCell.rowSpan - 1;
    +    } else {
    +      var newTd = this.createEmptyTd();
    +      // TODO(user): figure out a way to intelligently size new columns.
    +      newTd.style.width = goog.editor.Table.OPTIMUM_EMPTY_CELL_WIDTH + 'px';
    +      this.insertCellElement(newTd, rowNum, colIndex);
    +      newTds.push(newTd);
    +    }
    +  }
    +  this.refresh();
    +  return newTds;
    +};
    +
    +
    +/**
    + * Removes a row from the table, removing the TR element and
    + * decrementing the rowspan of any cells in other rows that overlap the row.
    + * @param {number} rowIndex Index of the row to delete.
    + */
    +goog.editor.Table.prototype.removeRow = function(rowIndex) {
    +  var row = this.rows[rowIndex];
    +  if (!row) {
    +    goog.log.warning(this.logger_,
    +        "Can't remove row at position " + rowIndex + ': no such row.');
    +  }
    +  for (var i = 0, cell; cell = row.columns[i]; i += cell.colSpan) {
    +    if (cell.rowSpan > 1) {
    +      cell.setRowSpan(cell.rowSpan - 1);
    +      if (cell.startRow == rowIndex) {
    +        // Rowspanned cell started in this row - move it down to the next row.
    +        this.insertCellElement(cell.element, rowIndex + 1, cell.startCol);
    +      }
    +    }
    +  }
    +  row.element.parentNode.removeChild(row.element);
    +  this.refresh();
    +};
    +
    +
    +/**
    + * Removes a column from the table. This is done by removing cell elements,
    + * or shrinking the colspan of elements that span multiple columns.
    + * @param {number} colIndex Index of the column to delete.
    + */
    +goog.editor.Table.prototype.removeColumn = function(colIndex) {
    +  for (var i = 0, row; row = this.rows[i]; i++) {
    +    var cell = row.columns[colIndex];
    +    if (!cell) {
    +      goog.log.error(this.logger_,
    +          "Can't remove cell at position " + i + ', ' + colIndex +
    +          ': no such cell.');
    +    }
    +    if (cell.colSpan > 1) {
    +      cell.setColSpan(cell.colSpan - 1);
    +    } else {
    +      cell.element.parentNode.removeChild(cell.element);
    +    }
    +    // Skip over following rows that contain this same cell.
    +    i += cell.rowSpan - 1;
    +  }
    +  this.refresh();
    +};
    +
    +
    +/**
    + * Merges multiple cells into a single cell, and sets the rowSpan and colSpan
    + * attributes of the cell to take up the same space as the original cells.
    + * @param {number} startRowIndex Top coordinate of the cells to merge.
    + * @param {number} startColIndex Left coordinate of the cells to merge.
    + * @param {number} endRowIndex Bottom coordinate of the cells to merge.
    + * @param {number} endColIndex Right coordinate of the cells to merge.
    + * @return {boolean} Whether or not the merge was possible. If the cells
    + *     in the supplied coordinates can't be merged this will return false.
    + */
    +goog.editor.Table.prototype.mergeCells = function(
    +    startRowIndex, startColIndex, endRowIndex, endColIndex) {
    +  // TODO(user): take a single goog.math.Rect parameter instead?
    +  var cells = [];
    +  var cell;
    +  if (startRowIndex == endRowIndex && startColIndex == endColIndex) {
    +    goog.log.warning(this.logger_, "Can't merge single cell");
    +    return false;
    +  }
    +  // Gather cells and do sanity check.
    +  for (var i = startRowIndex; i <= endRowIndex; i++) {
    +    for (var j = startColIndex; j <= endColIndex; j++) {
    +      cell = this.rows[i].columns[j];
    +      if (cell.startRow < startRowIndex ||
    +          cell.endRow > endRowIndex ||
    +          cell.startCol < startColIndex ||
    +          cell.endCol > endColIndex) {
    +        goog.log.warning(this.logger_,
    +            "Can't merge cells: the cell in row " + i + ', column ' + j +
    +            'extends outside the supplied rectangle.');
    +        return false;
    +      }
    +      // TODO(user): this is somewhat inefficient, as we will add
    +      // a reference for a cell for each position, even if it's a single
    +      // cell with row/colspan.
    +      cells.push(cell);
    +    }
    +  }
    +  var targetCell = cells[0];
    +  var targetTd = targetCell.element;
    +  var doc = this.dom_.getDocument();
    +
    +  // Merge cell contents and discard other cells.
    +  for (var i = 1; cell = cells[i]; i++) {
    +    var td = cell.element;
    +    if (!td.parentNode || td == targetTd) {
    +      // We've already handled this cell at one of its previous positions.
    +      continue;
    +    }
    +    // Add a space if needed, to keep merged content from getting squished
    +    // together.
    +    if (targetTd.lastChild &&
    +        targetTd.lastChild.nodeType == goog.dom.NodeType.TEXT) {
    +      targetTd.appendChild(doc.createTextNode(' '));
    +    }
    +    var childNode;
    +    while ((childNode = td.firstChild)) {
    +      targetTd.appendChild(childNode);
    +    }
    +    td.parentNode.removeChild(td);
    +  }
    +  targetCell.setColSpan((endColIndex - startColIndex) + 1);
    +  targetCell.setRowSpan((endRowIndex - startRowIndex) + 1);
    +  if (endColIndex > startColIndex) {
    +    // Clear width on target cell.
    +    // TODO(user): instead of clearing width, calculate width
    +    // based on width of input cells
    +    targetTd.removeAttribute('width');
    +    targetTd.style.width = null;
    +  }
    +  this.refresh();
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Splits a cell with colspans or rowspans into multiple descrete cells.
    + * @param {number} rowIndex y coordinate of the cell to split.
    + * @param {number} colIndex x coordinate of the cell to split.
    + * @return {!Array<Element>} Array of new cell elements created by splitting
    + *     the cell.
    + */
    +// TODO(user): support splitting only horizontally or vertically,
    +// support splitting cells that aren't already row/colspanned.
    +goog.editor.Table.prototype.splitCell = function(rowIndex, colIndex) {
    +  var row = this.rows[rowIndex];
    +  var cell = row.columns[colIndex];
    +  var newTds = [];
    +  for (var i = 0; i < cell.rowSpan; i++) {
    +    for (var j = 0; j < cell.colSpan; j++) {
    +      if (i > 0 || j > 0) {
    +        var newTd = this.createEmptyTd();
    +        this.insertCellElement(newTd, rowIndex + i, colIndex + j);
    +        newTds.push(newTd);
    +      }
    +    }
    +  }
    +  cell.setColSpan(1);
    +  cell.setRowSpan(1);
    +  this.refresh();
    +  return newTds;
    +};
    +
    +
    +/**
    + * Inserts a cell element at the given position. The colIndex is the logical
    + * column index, not the position in the dom. This takes into consideration
    + * that cells in a given logical  row may actually be children of a previous
    + * DOM row that have used rowSpan to extend into the row.
    + * @param {Element} td The new cell element to insert.
    + * @param {number} rowIndex Row in which to insert the element.
    + * @param {number} colIndex Column in which to insert the element.
    + */
    +goog.editor.Table.prototype.insertCellElement = function(
    +    td, rowIndex, colIndex) {
    +  var row = this.rows[rowIndex];
    +  var nextSiblingElement = null;
    +  for (var i = colIndex, cell; cell = row.columns[i]; i += cell.colSpan) {
    +    if (cell.startRow == rowIndex) {
    +      nextSiblingElement = cell.element;
    +      break;
    +    }
    +  }
    +  row.element.insertBefore(td, nextSiblingElement);
    +};
    +
    +
    +/**
    + * Creates an empty TD element and fill it with some empty content so it will
    + * show up with borders even in IE pre-7 or if empty-cells is set to 'hide'
    + * @return {!Element} a new TD element.
    + */
    +goog.editor.Table.prototype.createEmptyTd = function() {
    +  // TODO(user): more cross-browser testing to determine best
    +  // and least annoying filler content.
    +  return this.dom_.createDom(goog.dom.TagName.TD, {}, goog.string.Unicode.NBSP);
    +};
    +
    +
    +
    +/**
    + * Class representing a logical table row: a tr element and any cells
    + * that appear in that row.
    + * @param {Element} trElement This rows's underlying TR element.
    + * @param {number} rowIndex This row's index in its parent table.
    + * @constructor
    + * @final
    + */
    +goog.editor.TableRow = function(trElement, rowIndex) {
    +  this.index = rowIndex;
    +  this.element = trElement;
    +  this.columns = [];
    +};
    +
    +
    +
    +/**
    + * Class representing a table cell, which may span across multiple
    + * rows and columns
    + * @param {Element} td This cell's underlying TD or TH element.
    + * @param {number} startRow Index of the row where this cell begins.
    + * @param {number} startCol Index of the column where this cell begins.
    + * @constructor
    + * @final
    + */
    +goog.editor.TableCell = function(td, startRow, startCol) {
    +  this.element = td;
    +  this.colSpan = parseInt(td.colSpan, 10) || 1;
    +  this.rowSpan = parseInt(td.rowSpan, 10) || 1;
    +  this.startRow = startRow;
    +  this.startCol = startCol;
    +  this.updateCoordinates_();
    +};
    +
    +
    +/**
    + * Calculates this cell's endRow/endCol coordinates based on rowSpan/colSpan
    + * @private
    + */
    +goog.editor.TableCell.prototype.updateCoordinates_ = function() {
    +  this.endCol = this.startCol + this.colSpan - 1;
    +  this.endRow = this.startRow + this.rowSpan - 1;
    +};
    +
    +
    +/**
    + * Set this cell's colSpan, updating both its colSpan property and the
    + * underlying element's colSpan attribute.
    + * @param {number} colSpan The new colSpan.
    + */
    +goog.editor.TableCell.prototype.setColSpan = function(colSpan) {
    +  if (colSpan != this.colSpan) {
    +    if (colSpan > 1) {
    +      this.element.colSpan = colSpan;
    +    } else {
    +      this.element.colSpan = 1,
    +      this.element.removeAttribute('colSpan');
    +    }
    +    this.colSpan = colSpan;
    +    this.updateCoordinates_();
    +  }
    +};
    +
    +
    +/**
    + * Set this cell's rowSpan, updating both its rowSpan property and the
    + * underlying element's rowSpan attribute.
    + * @param {number} rowSpan The new rowSpan.
    + */
    +goog.editor.TableCell.prototype.setRowSpan = function(rowSpan) {
    +  if (rowSpan != this.rowSpan) {
    +    if (rowSpan > 1) {
    +      this.element.rowSpan = rowSpan.toString();
    +    } else {
    +      this.element.rowSpan = '1';
    +      this.element.removeAttribute('rowSpan');
    +    }
    +    this.rowSpan = rowSpan;
    +    this.updateCoordinates_();
    +  }
    +};
    +
    +
    +/**
    + * Optimum size of empty cells (in pixels), if possible.
    + * @type {number}
    + */
    +goog.editor.Table.OPTIMUM_EMPTY_CELL_WIDTH = 60;
    +
    +
    +/**
    + * Maximum width for new tables.
    + * @type {number}
    + */
    +goog.editor.Table.OPTIMUM_MAX_NEW_TABLE_WIDTH = 600;
    +
    +
    +/**
    + * Default color for table borders.
    + * @type {string}
    + */
    +goog.editor.Table.DEFAULT_BORDER_COLOR = '#888';
    +
    +
    +/**
    + * Creates a new table element, populated with cells and formatted.
    + * @param {Document} doc Document in which to create the table element.
    + * @param {number} columns Number of columns in the table.
    + * @param {number} rows Number of rows in the table.
    + * @param {Object=} opt_tableStyle Object containing borderWidth and borderColor
    + *    properties, used to set the inital style of the table.
    + * @return {!Element} a table element.
    + */
    +goog.editor.Table.createDomTable = function(
    +    doc, columns, rows, opt_tableStyle) {
    +  // TODO(user): define formatting properties as constants,
    +  // make separate formatTable() function
    +  var style = {
    +    borderWidth: '1',
    +    borderColor: goog.editor.Table.DEFAULT_BORDER_COLOR
    +  };
    +  for (var prop in opt_tableStyle) {
    +    style[prop] = opt_tableStyle[prop];
    +  }
    +  var dom = new goog.dom.DomHelper(doc);
    +  var tableElement = dom.createTable(rows, columns, true);
    +
    +  var minimumCellWidth = 10;
    +  // Calculate a good cell width.
    +  var cellWidth = Math.max(
    +      minimumCellWidth,
    +      Math.min(goog.editor.Table.OPTIMUM_EMPTY_CELL_WIDTH,
    +               goog.editor.Table.OPTIMUM_MAX_NEW_TABLE_WIDTH / columns));
    +
    +  var tds = tableElement.getElementsByTagName(goog.dom.TagName.TD);
    +  for (var i = 0, td; td = tds[i]; i++) {
    +    td.style.width = cellWidth + 'px';
    +  }
    +
    +  // Set border somewhat redundantly to make sure they show
    +  // up correctly in all browsers.
    +  goog.style.setStyle(
    +      tableElement, {
    +        'borderCollapse': 'collapse',
    +        'borderColor': style.borderColor,
    +        'borderWidth': style.borderWidth + 'px'
    +      });
    +  tableElement.border = style.borderWidth;
    +  tableElement.setAttribute('bordercolor', style.borderColor);
    +  tableElement.setAttribute('cellspacing', '0');
    +
    +  return tableElement;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/table_test.html b/mapbox-gl/closure-library/closure/goog/editor/table_test.html
    new file mode 100644
    index 0000000000..936a78ca1c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/table_test.html
    @@ -0,0 +1,185 @@
    +<html>
    +</html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   table.js Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.editor.TableTest');
    +  </script>
    +  <style>
    +   table {
    +      border:1px outset #777;
    +      margin-top: 1em;
    +      empty-cells: show;
    +    }
    +    td, th {
    +      border:1px inset #777;
    +    }
    +  </style>
    + </head>
    + <body>
    +  <table id="test-basic">
    +   <tr>
    +    <th>
    +     Number
    +    </th>
    +    <th>
    +     Color
    +    </th>
    +    <th>
    +     Speed
    +    </th>
    +   </tr>
    +   <tr>
    +    <td>
    +     One
    +    </td>
    +    <td>
    +     Red
    +    </td>
    +    <td>
    +     60
    +    </td>
    +   </tr>
    +   <tr>
    +    <td>
    +     Two
    +    </td>
    +    <td>
    +     Blue
    +    </td>
    +    <td>
    +     75
    +    </td>
    +   </tr>
    +   <tr>
    +    <td>
    +     Three
    +    </td>
    +    <td>
    +     Orange
    +    </td>
    +    <td>
    +     88
    +    </td>
    +   </tr>
    +  </table>
    +  <table id="test-torture">
    +   <tr>
    +    <th rowspan="2">
    +     Left 0+1
    +     <br />
    +     &nbsp;
    +    </th>
    +    <th colspan="2">
    +     Middle, Right 0
    +    </th>
    +   </tr>
    +   <tr>
    +    <td colspan="2" rowspan="2">
    +     Middle, Right 1 + 2
    +     <br />
    +     &nbsp;
    +    </td>
    +   </tr>
    +   <tr>
    +    <th>
    +     Left 2
    +    </th>
    +   </tr>
    +   <tr>
    +    <th>
    +     Left 3
    +    </th>
    +    <td>
    +     Middle 3
    +    </td>
    +    <td>
    +     Right 3
    +    </td>
    +   </tr>
    +   <tr>
    +    <th colspan="3">
    +     Left, Middle, Right 4
    +    </th>
    +   </tr>
    +   <tr>
    +    <th colspan="2" rowspan="2">
    +     Left, Middle 5+6
    +     <br />
    +     &nbsp;
    +    </th>
    +    <td rowspan="4">
    +     Right 5+6+7+8
    +    </td>
    +   </tr>
    +   <tr>
    +   </tr>
    +   <tr>
    +    <th rowspan="2">
    +     Left 7+8
    +    </th>
    +    <td>
    +     Middle 7
    +    </td>
    +   </tr>
    +   <tr>
    +    <td>
    +     Middle 8
    +    </td>
    +   </tr>
    +  </table>
    +  <table id="test-nested">
    +   <tr>
    +    <td>
    +     One
    +    </td>
    +    <td>
    +     Two
    +    </td>
    +    <td>
    +     <table>
    +      <tr>
    +       <td>
    +        Nested cell 1
    +       </td>
    +       <td>
    +        Nested cell 2
    +       </td>
    +      </tr>
    +      <tr>
    +       <td>
    +        Nested cell 2
    +       </td>
    +       <td>
    +        Nested cell 3
    +       </td>
    +      </tr>
    +     </table>
    +    </td>
    +   </tr>
    +   <tr>
    +    <td>
    +     Four
    +    </td>
    +    <td>
    +     Five
    +    </td>
    +    <td>
    +     Six
    +    </td>
    +   </tr>
    +  </table>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/editor/table_test.js b/mapbox-gl/closure-library/closure/goog/editor/table_test.js
    new file mode 100644
    index 0000000000..52fc654601
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/editor/table_test.js
    @@ -0,0 +1,482 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.editor.TableTest');
    +goog.setTestOnly('goog.editor.TableTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.editor.Table');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  var inputTables = document.getElementsByTagName('table');
    +  testElements = {};
    +  testObjects = {};
    +  for (var i = 0; i < inputTables.length; i++) {
    +    var originalTable = inputTables[i];
    +    if (originalTable.id.substring(0, 5) == 'test-') {
    +      var tableName = originalTable.id.substring(5);
    +      var testTable = originalTable.cloneNode(true);
    +      testTable.id = tableName;
    +      testElements[tableName] = testTable;
    +      document.body.appendChild(testTable);
    +      testObjects[tableName] = new goog.editor.Table(testTable);
    +    }
    +  }
    +}
    +
    +function tearDown() {
    +  for (var tableName in testElements) {
    +    document.body.removeChild(testElements[tableName]);
    +    delete testElements[tableName];
    +    delete testObjects[tableName];
    +  }
    +  testElements = null;
    +  testObjects = null;
    +}
    +
    +// These tests don't pass on Safari, because the table editor only works
    +// on IE and FF right now.
    +// TODO(nicksantos): Fix this.
    +if (!goog.userAgent.WEBKIT) {
    +
    +  function tableSanityCheck(editableTable, rowCount, colCount) {
    +    assertEquals('Table has expected number of rows',
    +        rowCount, editableTable.rows.length);
    +    for (var i = 0, row; row = editableTable.rows[i]; i++) {
    +      assertEquals('Row ' + i + ' has expected number of columns',
    +          colCount, row.columns.length);
    +    }
    +  }
    +
    +  function testBasicTable() {
    +    // Do some basic sanity checking on the editable table structure
    +    tableSanityCheck(testObjects.basic, 4, 3);
    +    var originalRows = testElements.basic.getElementsByTagName('tr');
    +    assertEquals('Basic table row count, compared to source',
    +        originalRows.length, testObjects.basic.rows.length);
    +    assertEquals('Basic table row count, known value',
    +        4, testObjects.basic.rows.length);
    +    assertEquals('Basic table first row element',
    +        originalRows[0], testObjects.basic.rows[0].element);
    +    assertEquals('Basic table last row element',
    +        originalRows[3], testObjects.basic.rows[3].element);
    +    assertEquals('Basic table first row length',
    +        3, testObjects.basic.rows[0].columns.length);
    +    assertEquals('Basic table last row length',
    +        3, testObjects.basic.rows[3].columns.length);
    +  }
    +
    +  function testTortureTable() {
    +    // Do basic sanity checking on torture table structure
    +    tableSanityCheck(testObjects.torture, 9, 3);
    +    var originalRows = testElements.torture.getElementsByTagName('tr');
    +    assertEquals('Torture table row count, compared to source',
    +        originalRows.length, testObjects.torture.rows.length);
    +    assertEquals('Torture table row count, known value',
    +        9, testObjects.torture.rows.length);
    +  }
    +
    +  function _testInsertRowResult(element, editableTable,  newTr, index) {
    +    if (element == testElements.basic) {
    +      originalRowCount = 4;
    +    } else if (element == testElements.torture) {
    +      originalRowCount = 9;
    +    }
    +
    +    assertEquals('Row was added to table',
    +        originalRowCount + 1, element.getElementsByTagName('tr').length);
    +    assertEquals('Row was added at position ' + index,
    +        element.getElementsByTagName('tr')[index], newTr);
    +    assertEquals('Row knows its own position',
    +        index, editableTable.rows[index].index);
    +    assertEquals('EditableTable shows row at position ' + index,
    +        newTr, editableTable.rows[index].element);
    +    assertEquals('New row has correct number of TDs',
    +        3, newTr.getElementsByTagName('td').length);
    +  }
    +
    +  function testInsertRowAtBeginning() {
    +    var tr = testObjects.basic.insertRow(0);
    +    _testInsertRowResult(testElements.basic, testObjects.basic, tr, 0);
    +  }
    +
    +  function testInsertRowInMiddle() {
    +    var tr = testObjects.basic.insertRow(2);
    +    _testInsertRowResult(testElements.basic, testObjects.basic, tr, 2);
    +  }
    +
    +  function testInsertRowAtEnd() {
    +    assertEquals('Table has expected number of existing rows',
    +        4, testObjects.basic.rows.length);
    +    var tr = testObjects.basic.insertRow(4);
    +    _testInsertRowResult(testElements.basic, testObjects.basic, tr, 4);
    +  }
    +
    +  function testInsertRowAtEndNoIndexArgument() {
    +    assertEquals('Table has expected number of existing rows',
    +        4, testObjects.basic.rows.length);
    +    var tr = testObjects.basic.insertRow();
    +    _testInsertRowResult(testElements.basic, testObjects.basic, tr, 4);
    +  }
    +
    +  function testInsertRowAtBeginningRowspan() {
    +    // Test inserting a row when the existing DOM row at that index has
    +    // a cell with a rowspan. This should be just like a regular insert -
    +    // the rowspan shouldn't have any effect.
    +    assertEquals('Cell has starting rowspan',
    +        2,
    +        goog.dom.getFirstElementChild(
    +        testElements.torture.getElementsByTagName('tr')[0]).rowSpan);
    +    var tr = testObjects.torture.insertRow(0);
    +    // Among other things this verifies that the new row has 3 child TDs.
    +    _testInsertRowResult(testElements.torture, testObjects.torture, tr, 0);
    +  }
    +
    +  function testInsertRowAtEndingRowspan() {
    +    // Test inserting a row when there's a cell in a previous DOM row
    +    // with a rowspan that extends into the row with the given index
    +    // and ends there. This should be just like a regular insert -
    +    // the rowspan shouldn't have any effect.
    +    assertEquals('Cell has ending rowspan',
    +        4,
    +        goog.dom.getLastElementChild(
    +        testElements.torture.getElementsByTagName('tr')[5]).rowSpan);
    +    var tr = testObjects.torture.insertRow();
    +    // Among other things this verifies that the new row has 3 child TDs.
    +    _testInsertRowResult(testElements.torture, testObjects.torture, tr, 9);
    +  }
    +
    +  function testInsertRowAtSpanningRowspan() {
    +    // Test inserting a row at an index where there's a cell with a rowspan
    +    // that begins in a previous row and continues into the next row. In this
    +    // case the existing cell's rowspan should be extended, and the new
    +    // tr should have one less child element.
    +    var rowSpannedCell = testObjects.torture.rows[7].columns[2];
    +    assertTrue('Existing cell has overlapping rowspan',
    +        rowSpannedCell.startRow == 5 && rowSpannedCell.endRow == 8);
    +    var tr = testObjects.torture.insertRow(7);
    +    assertEquals('New DOM row has one less cell',
    +        2, tr.getElementsByTagName('td').length);
    +    assertEquals('Rowspanned cell listed in new EditableRow\'s columns',
    +        testObjects.torture.rows[6].columns[2].element,
    +        testObjects.torture.rows[7].columns[2].element);
    +  }
    +
    +  function _testInsertColumnResult(newCells, element, editableTable, index) {
    +    for (var rowNo = 0, row; row = editableTable.rows[rowNo]; rowNo++) {
    +      assertEquals('Row includes new column',
    +          4, row.columns.length);
    +    }
    +    assertEquals('New cell in correct position',
    +        newCells[0], editableTable.rows[0].columns[index].element);
    +  }
    +
    +  function testInsertColumnAtBeginning() {
    +    var startColCount = testObjects.basic.rows[0].columns.length;
    +    var newCells = testObjects.basic.insertColumn(0);
    +    assertEquals('New cell added for each row',
    +        testObjects.basic.rows.length, newCells.length);
    +    assertEquals('Insert column incremented column length',
    +        startColCount + 1, testObjects.basic.rows[0].columns.length);
    +    _testInsertColumnResult(newCells, testElements.basic, testObjects.basic, 0);
    +  }
    +
    +  function testInsertColumnAtEnd() {
    +    var startColCount = testObjects.basic.rows[0].columns.length;
    +    var newCells = testObjects.basic.insertColumn(3);
    +    assertEquals('New cell added for each row',
    +        testObjects.basic.rows.length, newCells.length);
    +    assertEquals('Insert column incremented column length',
    +        startColCount + 1, testObjects.basic.rows[0].columns.length);
    +    _testInsertColumnResult(newCells, testElements.basic, testObjects.basic, 3);
    +  }
    +
    +  function testInsertColumnAtEndNoIndexArgument() {
    +    var startColCount = testObjects.basic.rows[0].columns.length;
    +    var newCells = testObjects.basic.insertColumn();
    +    assertEquals('New cell added for each row',
    +        testObjects.basic.rows.length, newCells.length);
    +    assertEquals('Insert column incremented column length',
    +        startColCount + 1, testObjects.basic.rows[0].columns.length);
    +    _testInsertColumnResult(newCells, testElements.basic, testObjects.basic, 3);
    +  }
    +
    +  function testInsertColumnInMiddle() {
    +    var startColCount = testObjects.basic.rows[0].columns.length;
    +    var newCells = testObjects.basic.insertColumn(2);
    +    assertEquals('New cell added for each row',
    +        testObjects.basic.rows.length, newCells.length);
    +    assertEquals('Insert column incremented column length',
    +        startColCount + 1, testObjects.basic.rows[0].columns.length);
    +    _testInsertColumnResult(newCells, testElements.basic, testObjects.basic, 2);
    +  }
    +
    +  function testInsertColumnAtBeginning() {
    +    var startColCount = testObjects.basic.rows[0].columns.length;
    +    var newCells = testObjects.basic.insertColumn(0);
    +    assertEquals('Insert column incremented column length',
    +        startColCount + 1, testObjects.basic.rows[0].columns.length);
    +    _testInsertColumnResult(newCells, testElements.basic, testObjects.basic, 0);
    +  }
    +
    +  function testInsertColumnAtBeginningColSpan() {
    +    var cells = testObjects.torture.insertColumn(0);
    +    tableSanityCheck(testObjects.torture, 9, 4);
    +    assertEquals('New cell was added before colspanned cell',
    +        1, testObjects.torture.rows[3].columns[0].colSpan);
    +    assertEquals('New cell was added and returned',
    +        testObjects.torture.rows[3].columns[0].element,
    +        cells[3]);
    +  }
    +
    +  function testInsertColumnAtEndingColSpan() {
    +    var cells = testObjects.torture.insertColumn();
    +    tableSanityCheck(testObjects.torture, 9, 4);
    +    assertEquals('New cell was added after colspanned cell',
    +        1, testObjects.torture.rows[0].columns[3].colSpan);
    +    assertEquals('New cell was added and returned',
    +        testObjects.torture.rows[0].columns[3].element,
    +        cells[0]);
    +  }
    +
    +  function testInsertColumnAtSpanningColSpan() {
    +    assertEquals('Existing cell has expected colspan',
    +        3, testObjects.torture.rows[4].columns[1].colSpan);
    +    var cells = testObjects.torture.insertColumn(1);
    +    tableSanityCheck(testObjects.torture, 9, 4);
    +    assertEquals('Existing cell increased colspan',
    +        4, testObjects.torture.rows[4].columns[1].colSpan);
    +    assertEquals('3 cells weren\'t created due to existing colspans',
    +        6, cells.length);
    +  }
    +
    +  function testRemoveFirstRow() {
    +    var originalRow = testElements.basic.getElementsByTagName('tr')[0];
    +    testObjects.basic.removeRow(0);
    +    tableSanityCheck(testObjects.basic, 3, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[0]);
    +  }
    +
    +  function testRemoveLastRow() {
    +    var originalRow = testElements.basic.getElementsByTagName('tr')[3];
    +    testObjects.basic.removeRow(3);
    +    tableSanityCheck(testObjects.basic, 3, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[3]);
    +  }
    +
    +  function testRemoveMiddleRow() {
    +    var originalRow = testElements.basic.getElementsByTagName('tr')[2];
    +    testObjects.basic.removeRow(2);
    +    tableSanityCheck(testObjects.basic, 3, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[2]);
    +  }
    +
    +  function testRemoveRowAtBeginingRowSpan() {
    +    var originalRow = testObjects.torture.removeRow(0);
    +    tableSanityCheck(testObjects.torture, 8, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[0]);
    +    assertEquals('Rowspan correctly adjusted',
    +        1, testObjects.torture.rows[0].columns[0].rowSpan);
    +  }
    +
    +  function testRemoveRowAtEndingRowSpan() {
    +    var originalRow = testElements.torture.getElementsByTagName('tr')[8];
    +    testObjects.torture.removeRow(8);
    +    tableSanityCheck(testObjects.torture, 8, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[8]);
    +    assertEquals('Rowspan correctly adjusted',
    +        3, testObjects.torture.rows[7].columns[2].rowSpan);
    +  }
    +
    +  function testRemoveRowAtSpanningRowSpan() {
    +    var originalRow = testElements.torture.getElementsByTagName('tr')[7];
    +    testObjects.torture.removeRow(7);
    +    tableSanityCheck(testObjects.torture, 8, 3);
    +    assertNotEquals('Row was removed from table element',
    +        originalRow, testElements.basic.getElementsByTagName('tr')[7]);
    +    assertEquals('Rowspan correctly adjusted',
    +        3, testObjects.torture.rows[6].columns[2].rowSpan);
    +  }
    +
    +  function _testRemoveColumn(index) {
    +    var sampleCell = testElements.basic.getElementsByTagName(
    +        'tr')[0].getElementsByTagName('th')[index];
    +    testObjects.basic.removeColumn(index);
    +    tableSanityCheck(testObjects.basic, 4, 2);
    +    assertNotEquals(
    +        'Test cell removed from column',
    +        sampleCell,
    +        testElements.basic.getElementsByTagName(
    +        'tr')[0].getElementsByTagName('th')[index]);
    +  }
    +
    +  function testRemoveFirstColumn() {
    +    _testRemoveColumn(0);
    +  }
    +
    +  function testRemoveMiddleColumn() {
    +    _testRemoveColumn(1);
    +  }
    +
    +  function testRemoveLastColumn() {
    +    _testRemoveColumn(2);
    +  }
    +
    +  function testRemoveColumnAtStartingColSpan() {
    +    testObjects.torture.removeColumn(0);
    +    tableSanityCheck(testObjects.torture, 9, 2);
    +    assertEquals('Colspan was decremented correctly',
    +        1,
    +        testElements.torture.getElementsByTagName(
    +        'tr')[5].getElementsByTagName('th')[0].colSpan);
    +  }
    +
    +  function testRemoveColumnAtEndingColSpan() {
    +    testObjects.torture.removeColumn(2);
    +    tableSanityCheck(testObjects.torture, 9, 2);
    +    assertEquals('Colspan was decremented correctly',
    +        1,
    +        testElements.torture.getElementsByTagName(
    +        'tr')[1].getElementsByTagName('td')[0].colSpan);
    +  }
    +
    +  function testRemoveColumnAtSpanningColSpan() {
    +    testObjects.torture.removeColumn(2);
    +    tableSanityCheck(testObjects.torture, 9, 2);
    +    assertEquals('Colspan was decremented correctly',
    +        2,
    +        testElements.torture.getElementsByTagName(
    +        'tr')[4].getElementsByTagName('th')[0].colSpan);
    +  }
    +
    +  function testMergeCellsInRow() {
    +    testObjects.basic.mergeCells(0, 0, 0, 2);
    +    tableSanityCheck(testObjects.basic, 4, 3);
    +    var trs = testElements.basic.getElementsByTagName('tr');
    +    assertEquals('Cells merged',
    +        1, trs[0].getElementsByTagName('th').length);
    +    assertEquals('Merged cell has correct colspan',
    +        3, trs[0].getElementsByTagName('th')[0].colSpan);
    +    assertEquals('Merged cell has correct rowspan',
    +        1, trs[0].getElementsByTagName('th')[0].rowSpan);
    +  }
    +
    +  function testMergeCellsInColumn() {
    +    testObjects.basic.mergeCells(0, 0, 2, 0);
    +    tableSanityCheck(testObjects.basic, 4, 3);
    +    var trs = testElements.basic.getElementsByTagName('tr');
    +    assertEquals('Other cells still in row',
    +        3, trs[0].getElementsByTagName('th').length);
    +    assertEquals('Merged cell has correct colspan',
    +        1, trs[0].getElementsByTagName('th')[0].colSpan);
    +    assertEquals('Merged cell has correct rowspan',
    +        3, trs[0].getElementsByTagName('th')[0].rowSpan);
    +    assert('Cell appears in multiple rows after merge',
    +        testObjects.basic.rows[0].columns[0] ==
    +        testObjects.basic.rows[2].columns[0]);
    +  }
    +
    +  function testMergeCellsInRowAndColumn() {
    +    testObjects.basic.mergeCells(1, 1, 3, 2);
    +    tableSanityCheck(testObjects.basic, 4, 3);
    +    var trs = testElements.basic.getElementsByTagName('tr');
    +    var mergedCell = trs[1].getElementsByTagName('td')[1];
    +    assertEquals('Merged cell has correct rowspan',
    +        3, mergedCell.rowSpan);
    +    assertEquals('Merged cell has correct colspan',
    +        2, mergedCell.colSpan);
    +  }
    +
    +  function testMergeCellsAlreadyMerged() {
    +    testObjects.torture.mergeCells(5, 0, 8, 2);
    +    tableSanityCheck(testObjects.torture, 9, 3);
    +    var trs = testElements.torture.getElementsByTagName('tr');
    +    var mergedCell = trs[5].getElementsByTagName('th')[0];
    +    assertEquals('Merged cell has correct rowspan',
    +        4, mergedCell.rowSpan);
    +    assertEquals('Merged cell has correct colspan',
    +        3, mergedCell.colSpan);
    +  }
    +
    +  function testIllegalMergeNonRectangular() {
    +    // This should fail because it involves trying to merge two parts
    +    // of a 3-colspan cell with other cells
    +    var mergeSucceeded = testObjects.torture.mergeCells(3, 1, 5, 2);
    +    if (mergeSucceeded) {
    +      throw 'EditableTable allowed impossible merge!';
    +    }
    +    tableSanityCheck(testObjects.torture, 9, 3);
    +  }
    +
    +  function testIllegalMergeSingleCell() {
    +    // This should fail because it involves merging a single cell
    +    var mergeSucceeded = testObjects.torture.mergeCells(0, 1, 0, 1);
    +    if (mergeSucceeded) {
    +      throw 'EditableTable allowed impossible merge!';
    +    }
    +    tableSanityCheck(testObjects.torture, 9, 3);
    +  }
    +
    +  function testSplitCell() {
    +    testObjects.torture.splitCell(1, 1);
    +    tableSanityCheck(testObjects.torture, 9, 3);
    +    var trs = testElements.torture.getElementsByTagName('tr');
    +    assertEquals('Cell was split into multiple columns in row 1',
    +        3, trs[1].getElementsByTagName('*').length);
    +    assertEquals('Cell was split into multiple columns in row 2',
    +        3, trs[2].getElementsByTagName('*').length);
    +  }
    +
    +  function testChildTableRowsNotCountedInParentTable() {
    +    tableSanityCheck(testObjects.nested, 2, 3);
    +    for (var i = 0; i < testObjects.nested.rows.length; i++) {
    +      var tr = testObjects.nested.rows[i].element;
    +      // A tr's parent is tbody, parent of that is table - check to
    +      // make sure the ancestor table is as expected. This means
    +      // that none of the child table's rows have been erroneously
    +      // loaded into the EditableTable.
    +      assertEquals('Row is child of parent table',
    +          testElements.nested,
    +          tr.parentNode.parentNode);
    +    }
    +  }
    +
    +}
    +/*
    +  // TODO(user): write more unit tests for selection stuff.
    +  // The following code is left in here for reference in implementing
    +  // this TODO.
    +
    +    var tds = goog.dom.getElement('test1').getElementsByTagName('td');
    +    var range = goog.dom.Range.createFromNodes(tds[7], tds[9]);
    +    range.select();
    +    var cellSelection = new goog.editor.Table.CellSelection(range);
    +    assertEquals(0, cellSelection.getFirstColumnIndex());
    +    assertEquals(2, cellSelection.getLastColumnIndex());
    +    assertEquals(2, cellSelection.getFirstRowIndex());
    +    assertEquals(2, cellSelection.getLastRowIndex());
    +    assertTrue(cellSelection.isRectangle());
    +
    +    range = goog.dom.Range.createFromNodes(tds[7], tds[12]);
    +    range.select();
    +    var cellSelection2 = new goog.editor.Table.CellSelection(range);
    +    assertFalse(cellSelection2.isRectangle());
    +*/
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper.js b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper.js
    new file mode 100644
    index 0000000000..779150f058
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper.js
    @@ -0,0 +1,151 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Action event wrapper implementation.
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.events.actionEventWrapper');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +/** @suppress {extraRequire} */
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.EventWrapper');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Event wrapper for action handling. Fires when an element is activated either
    + * by clicking it or by focusing it and pressing Enter.
    + *
    + * @constructor
    + * @implements {goog.events.EventWrapper}
    + * @private
    + */
    +goog.events.ActionEventWrapper_ = function() {
    +};
    +
    +
    +/**
    + * Singleton instance of ActionEventWrapper_.
    + * @type {goog.events.ActionEventWrapper_}
    + */
    +goog.events.actionEventWrapper = new goog.events.ActionEventWrapper_();
    +
    +
    +/**
    + * Event types used by the wrapper.
    + *
    + * @type {Array<goog.events.EventType>}
    + * @private
    + */
    +goog.events.ActionEventWrapper_.EVENT_TYPES_ = [
    +  goog.events.EventType.CLICK,
    +  goog.userAgent.GECKO ?
    +      goog.events.EventType.KEYPRESS : goog.events.EventType.KEYDOWN,
    +  goog.events.EventType.KEYUP
    +];
    +
    +
    +/**
    + * Adds an event listener using the wrapper on a DOM Node or an object that has
    + * implemented {@link goog.events.EventTarget}. A listener can only be added
    + * once to an object.
    + *
    + * @param {goog.events.ListenableType} target The target to listen to events on.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
    + *     method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @param {goog.events.EventHandler=} opt_eventHandler Event handler to add
    + *     listener to.
    + * @override
    + */
    +goog.events.ActionEventWrapper_.prototype.listen = function(target, listener,
    +    opt_capt, opt_scope, opt_eventHandler) {
    +  var callback = function(e) {
    +    var listenerFn = goog.events.wrapListener(listener);
    +    var role = goog.dom.isElement(e.target) ?
    +        goog.a11y.aria.getRole(/** @type {!Element} */ (e.target)) : null;
    +    if (e.type == goog.events.EventType.CLICK && e.isMouseActionButton()) {
    +      listenerFn.call(opt_scope, e);
    +    } else if ((e.keyCode == goog.events.KeyCodes.ENTER ||
    +        e.keyCode == goog.events.KeyCodes.MAC_ENTER) &&
    +        e.type != goog.events.EventType.KEYUP) {
    +      // convert keydown to keypress for backward compatibility.
    +      e.type = goog.events.EventType.KEYPRESS;
    +      listenerFn.call(opt_scope, e);
    +    } else if (e.keyCode == goog.events.KeyCodes.SPACE &&
    +        e.type == goog.events.EventType.KEYUP &&
    +        (role == goog.a11y.aria.Role.BUTTON ||
    +            role == goog.a11y.aria.Role.TAB)) {
    +      listenerFn.call(opt_scope, e);
    +      e.preventDefault();
    +    }
    +  };
    +  callback.listener_ = listener;
    +  callback.scope_ = opt_scope;
    +
    +  if (opt_eventHandler) {
    +    opt_eventHandler.listen(target,
    +        goog.events.ActionEventWrapper_.EVENT_TYPES_,
    +        callback, opt_capt);
    +  } else {
    +    goog.events.listen(target,
    +        goog.events.ActionEventWrapper_.EVENT_TYPES_,
    +        callback, opt_capt);
    +  }
    +};
    +
    +
    +/**
    + * Removes an event listener added using goog.events.EventWrapper.listen.
    + *
    + * @param {goog.events.ListenableType} target The node to remove listener from.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
    + *     method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @param {goog.events.EventHandler=} opt_eventHandler Event handler to remove
    + *     listener from.
    + * @override
    + */
    +goog.events.ActionEventWrapper_.prototype.unlisten = function(target, listener,
    +    opt_capt, opt_scope, opt_eventHandler) {
    +  for (var type, j = 0; type = goog.events.ActionEventWrapper_.EVENT_TYPES_[j];
    +      j++) {
    +    var listeners = goog.events.getListeners(target, type, !!opt_capt);
    +    for (var obj, i = 0; obj = listeners[i]; i++) {
    +      if (obj.listener.listener_ == listener &&
    +          obj.listener.scope_ == opt_scope) {
    +        if (opt_eventHandler) {
    +          opt_eventHandler.unlisten(target, type, obj.listener, opt_capt,
    +              opt_scope);
    +        } else {
    +          goog.events.unlisten(target, type, obj.listener, opt_capt, opt_scope);
    +        }
    +        break;
    +      }
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.html b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.html
    new file mode 100644
    index 0000000000..debe5e9480
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.actionEventWrapper
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.events.actionEventWrapperTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="a" tabindex="0">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.js b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.js
    new file mode 100644
    index 0000000000..94c25b3472
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actioneventwrapper_test.js
    @@ -0,0 +1,270 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.actionEventWrapperTest');
    +goog.setTestOnly('goog.events.actionEventWrapperTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.actionEventWrapper');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +var a, eh, events;
    +
    +function setUpPage() {
    +  a = document.getElementById('a');
    +}
    +
    +function setUp() {
    +  events = [];
    +  eh = new goog.events.EventHandler();
    +
    +  assertEquals('No listeners registered yet', 0,
    +      goog.events.getListeners(a).length);
    +}
    +
    +
    +function tearDown() {
    +  eh.dispose();
    +}
    +
    +var Foo = function() {};
    +Foo.prototype.test = function(e) {
    +  events.push(e);
    +};
    +
    +function assertListenersExist(el, listenerCount, capt) {
    +  var EVENT_TYPES = goog.events.ActionEventWrapper_.EVENT_TYPES_;
    +  for (var i = 0; i < EVENT_TYPES.length; ++i) {
    +    assertEquals(listenerCount, goog.events.getListeners(
    +        el, EVENT_TYPES[i], capt).length);
    +  }
    +}
    +
    +function testAddActionListener() {
    +  var listener = function(e) { events.push(e);};
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper, listener);
    +
    +  assertListenersExist(a, 1, false);
    +
    +  goog.testing.events.fireClickSequence(a);
    +  assertEquals('1 event should have been dispatched', 1, events.length);
    +  assertEquals('Should be an click event', 'click', events[0].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[1].type);
    +
    +  goog.a11y.aria.setRole(
    +      /** @type {!Element} */ (a), goog.a11y.aria.Role.BUTTON);
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +  assertEquals('Should be a keyup event', 'keyup', events[2].type);
    +  assertTrue('Should be default prevented.', events[2].defaultPrevented);
    +  goog.a11y.aria.removeRole(/** @type {!Element} */ (a));
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +
    +  goog.a11y.aria.setRole(
    +      /** @type {!Element} */ (a), goog.a11y.aria.Role.TAB);
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +  assertEquals('Should be a keyup event', 'keyup', events[2].type);
    +  assertTrue('Should be default prevented.', events[2].defaultPrevented);
    +  goog.a11y.aria.removeRole(/** @type {!Element} */ (a));
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      listener);
    +  assertListenersExist(a, 0, false);
    +}
    +
    +
    +function testAddActionListenerForHandleEvent() {
    +  var listener = {
    +    handleEvent: function(e) { events.push(e); }
    +  };
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper, listener);
    +
    +  assertListenersExist(a, 1, false);
    +
    +  goog.testing.events.fireClickSequence(a);
    +  assertEquals('1 event should have been dispatched', 1, events.length);
    +  assertEquals('Should be an click event', 'click', events[0].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[1].type);
    +
    +  goog.a11y.aria.setRole(
    +      /** @type {!Element} */ (a), goog.a11y.aria.Role.BUTTON);
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +  assertEquals('Should be a keyup event', 'keyup', events[2].type);
    +  assertTrue('Should be default prevented.', events[2].defaultPrevented);
    +  goog.a11y.aria.removeRole(/** @type {!Element} */ (a));
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('3 events should have been dispatched', 3, events.length);
    +
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      listener);
    +  assertListenersExist(a, 0, false);
    +}
    +
    +
    +function testAddActionListenerInCaptPhase() {
    +  var count = 0;
    +  var captListener = function(e) {
    +    events.push(e);
    +    assertEquals(0, count);
    +    count++;
    +  };
    +
    +  var bubbleListener = function(e) {
    +    events.push(e);
    +    assertEquals(1, count);
    +    count = 0;
    +  };
    +
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper,
    +      captListener, true);
    +
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper,
    +      bubbleListener);
    +
    +  assertListenersExist(a, 1, false);
    +  assertListenersExist(a, 1, true);
    +
    +  goog.testing.events.fireClickSequence(a);
    +  assertEquals('2 event should have been dispatched', 2, events.length);
    +  assertEquals('Should be an click event', 'click', events[0].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[2].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('4 events should have been dispatched', 4, events.length);
    +
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      captListener, true);
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      bubbleListener);
    +
    +  assertListenersExist(a, 0, false);
    +  assertListenersExist(a, 0, true);
    +}
    +
    +
    +function testRemoveActionListener() {
    +  var listener1 = function(e) { events.push(e); };
    +  var listener2 = function(e) { events.push({type: 'err'}); };
    +
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper, listener1);
    +  assertListenersExist(a, 1, false);
    +
    +  goog.events.listenWithWrapper(a, goog.events.actionEventWrapper, listener2);
    +  assertListenersExist(a, 2, false);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[0].type);
    +  assertEquals('Should be an err event', 'err', events[1].type);
    +
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      listener2);
    +  assertListenersExist(a, 1, false);
    +
    +  events = [];
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('1 event should have been dispatched', 1, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[0].type);
    +
    +  goog.events.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      listener1);
    +  assertListenersExist(a, 0, false);
    +}
    +
    +
    +function testEventHandlerActionListener() {
    +  var listener = function(e) { events.push(e); };
    +  eh.listenWithWrapper(a, goog.events.actionEventWrapper, listener);
    +
    +  assertListenersExist(a, 1, false);
    +
    +  goog.testing.events.fireClickSequence(a);
    +  assertEquals('1 event should have been dispatched', 1, events.length);
    +  assertEquals('Should be an click event', 'click', events[0].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[1].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +
    +  eh.unlistenWithWrapper(a, goog.events.actionEventWrapper,
    +      listener);
    +  assertListenersExist(a, 0, false);
    +}
    +
    +
    +function testEventHandlerActionListenerWithScope() {
    +  var foo = new Foo();
    +  var eh2 = new goog.events.EventHandler(foo);
    +
    +  eh2.listenWithWrapper(a, goog.events.actionEventWrapper, foo.test);
    +
    +  assertListenersExist(a, 1, false);
    +
    +  goog.testing.events.fireClickSequence(a);
    +  assertEquals('1 event should have been dispatched', 1, events.length);
    +  assertEquals('Should be an click event', 'click', events[0].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ENTER);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +  assertEquals('Should be a keypress event', 'keypress', events[1].type);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.SPACE);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +
    +  goog.testing.events.fireKeySequence(a, goog.events.KeyCodes.ESC);
    +  assertEquals('2 events should have been dispatched', 2, events.length);
    +
    +  eh2.dispose();
    +  assertListenersExist(a, 0, false);
    +  delete foo;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actionhandler.js b/mapbox-gl/closure-library/closure/goog/events/actionhandler.js
    new file mode 100644
    index 0000000000..190cc26046
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actionhandler.js
    @@ -0,0 +1,184 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file contains a class to provide a unified mechanism for
    + * CLICK and enter KEYDOWN events. This provides better accessibility by
    + * providing the given functionality to a keyboard user which is otherwise
    + * would be available only via a mouse click.
    + *
    + * If there is an existing CLICK listener or planning to be added as below -
    + *
    + * <code>this.eventHandler_.listen(el, CLICK, this.onClick_);<code>
    + *
    + * it can be replaced with an ACTION listener as follows:
    + *
    + * <code>this.eventHandler_.listen(
    + *    new goog.events.ActionHandler(el),
    + *    ACTION,
    + *    this.onAction_);<code>
    + *
    + */
    +
    +goog.provide('goog.events.ActionEvent');
    +goog.provide('goog.events.ActionHandler');
    +goog.provide('goog.events.ActionHandler.EventType');
    +goog.provide('goog.events.BeforeActionEvent');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A wrapper around an element that you want to listen to ACTION events on.
    + * @param {Element|Document} element The element or document to listen on.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.events.ActionHandler = function(element) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * This is the element that we will listen to events on.
    +   * @type {Element|Document}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  goog.events.listen(element, goog.events.ActionHandler.KEY_EVENT_TYPE_,
    +      this.handleKeyDown_, false, this);
    +  goog.events.listen(element, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +};
    +goog.inherits(goog.events.ActionHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum type for the events fired by the action handler
    + * @enum {string}
    + */
    +goog.events.ActionHandler.EventType = {
    +  ACTION: 'action',
    +  BEFOREACTION: 'beforeaction'
    +};
    +
    +
    +/**
    + * Key event type to listen for.
    + * @type {string}
    + * @private
    + */
    +goog.events.ActionHandler.KEY_EVENT_TYPE_ = goog.userAgent.GECKO ?
    +    goog.events.EventType.KEYPRESS :
    +    goog.events.EventType.KEYDOWN;
    +
    +
    +/**
    + * Handles key press events.
    + * @param {!goog.events.BrowserEvent} e The key press event.
    + * @private
    + */
    +goog.events.ActionHandler.prototype.handleKeyDown_ = function(e) {
    +  if (e.keyCode == goog.events.KeyCodes.ENTER ||
    +      goog.userAgent.WEBKIT && e.keyCode == goog.events.KeyCodes.MAC_ENTER) {
    +    this.dispatchEvents_(e);
    +  }
    +};
    +
    +
    +/**
    + * Handles mouse events.
    + * @param {!goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.events.ActionHandler.prototype.handleClick_ = function(e) {
    +  this.dispatchEvents_(e);
    +};
    +
    +
    +/**
    + * Dispatches BeforeAction and Action events to the element
    + * @param {!goog.events.BrowserEvent} e The event causing dispatches.
    + * @private
    + */
    +goog.events.ActionHandler.prototype.dispatchEvents_ = function(e) {
    +  var beforeActionEvent = new goog.events.BeforeActionEvent(e);
    +
    +  // Allow application specific logic here before the ACTION event.
    +  // For example, Gmail uses this event to restore keyboard focus
    +  if (!this.dispatchEvent(beforeActionEvent)) {
    +    // If the listener swallowed the BEFOREACTION event, don't dispatch the
    +    // ACTION event.
    +    return;
    +  }
    +
    +
    +  // Wrap up original event and send it off
    +  var actionEvent = new goog.events.ActionEvent(e);
    +  try {
    +    this.dispatchEvent(actionEvent);
    +  } finally {
    +    // Stop propagating the event
    +    e.stopPropagation();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.events.ActionHandler.prototype.disposeInternal = function() {
    +  goog.events.ActionHandler.superClass_.disposeInternal.call(this);
    +  goog.events.unlisten(this.element_, goog.events.ActionHandler.KEY_EVENT_TYPE_,
    +      this.handleKeyDown_, false, this);
    +  goog.events.unlisten(this.element_, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +  delete this.element_;
    +};
    +
    +
    +
    +/**
    + * This class is used for the goog.events.ActionHandler.EventType.ACTION event.
    + * @param {!goog.events.BrowserEvent} browserEvent Browser event object.
    + * @constructor
    + * @extends {goog.events.BrowserEvent}
    + * @final
    + */
    +goog.events.ActionEvent = function(browserEvent) {
    +  goog.events.BrowserEvent.call(this, browserEvent.getBrowserEvent());
    +  this.type = goog.events.ActionHandler.EventType.ACTION;
    +};
    +goog.inherits(goog.events.ActionEvent, goog.events.BrowserEvent);
    +
    +
    +
    +/**
    + * This class is used for the goog.events.ActionHandler.EventType.BEFOREACTION
    + * event. BEFOREACTION gives a chance to the application so the keyboard focus
    + * can be restored back, if required.
    + * @param {!goog.events.BrowserEvent} browserEvent Browser event object.
    + * @constructor
    + * @extends {goog.events.BrowserEvent}
    + * @final
    + */
    +goog.events.BeforeActionEvent = function(browserEvent) {
    +  goog.events.BrowserEvent.call(this, browserEvent.getBrowserEvent());
    +  this.type = goog.events.ActionHandler.EventType.BEFOREACTION;
    +};
    +goog.inherits(goog.events.BeforeActionEvent, goog.events.BrowserEvent);
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.html
    new file mode 100644
    index 0000000000..d0a35188d5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.ActionHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.events.ActionHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="actionDiv">
    +   action
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.js
    new file mode 100644
    index 0000000000..db76a5e358
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/actionhandler_test.js
    @@ -0,0 +1,80 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.ActionHandlerTest');
    +goog.setTestOnly('goog.events.ActionHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.ActionHandler');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +
    +var actionHandler;
    +function setUp() {
    +  actionHandler = new goog.events.ActionHandler(
    +      goog.dom.getElement('actionDiv'));
    +}
    +function tearDown() {
    +  actionHandler.dispose();
    +}
    +
    +// Tests to see that both the BEFOREACTION and ACTION events are fired
    +function testActionHandlerWithBeforeActionHandler() {
    +  var actionEventFired = false;
    +  var beforeActionFired = false;
    +  goog.events.listen(actionHandler,
    +      goog.events.ActionHandler.EventType.ACTION,
    +      function(e) {
    +        actionEventFired = true;
    +      });
    +  goog.events.listen(actionHandler,
    +      goog.events.ActionHandler.EventType.BEFOREACTION,
    +      function(e) {
    +        beforeActionFired = true;
    +      });
    +  goog.testing.events.fireClickSequence(goog.dom.getElement('actionDiv'));
    +  assertTrue('BEFOREACTION event was not fired', beforeActionFired);
    +  assertTrue('ACTION event was not fired', actionEventFired);
    +}
    +
    +// Tests to see that the ACTION event is fired, even if there is no
    +// BEFOREACTION handler.
    +function testActionHandlerWithoutBeforeActionHandler() {
    +  var actionEventFired = false;
    +  goog.events.listen(actionHandler,
    +      goog.events.ActionHandler.EventType.ACTION,
    +      function(e) {actionEventFired = true;});
    +  goog.testing.events.fireClickSequence(goog.dom.getElement('actionDiv'));
    +  assertTrue('ACTION event was not fired', actionEventFired);
    +}
    +
    +// If the BEFOREACTION listener swallows the event, it should cancel the
    +// ACTION event.
    +function testBeforeActionCancel() {
    +  var actionEventFired = false;
    +  var beforeActionFired = false;
    +  goog.events.listen(actionHandler,
    +      goog.events.ActionHandler.EventType.ACTION,
    +      function(e) {actionEvent = e;});
    +  goog.events.listen(actionHandler,
    +      goog.events.ActionHandler.EventType.BEFOREACTION,
    +      function(e) {
    +        beforeActionFired = true;
    +        e.preventDefault();
    +      });
    +  goog.testing.events.fireClickSequence(goog.dom.getElement('actionDiv'));
    +  assertTrue(beforeActionFired);
    +  assertFalse(actionEventFired);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/browserevent.js b/mapbox-gl/closure-library/closure/goog/events/browserevent.js
    new file mode 100644
    index 0000000000..78230dbc90
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/browserevent.js
    @@ -0,0 +1,446 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A patched, standardized event object for browser events.
    + *
    + * <pre>
    + * The patched event object contains the following members:
    + * - type           {string}    Event type, e.g. 'click'
    + * - target         {Object}    The element that actually triggered the event
    + * - currentTarget  {Object}    The element the listener is attached to
    + * - relatedTarget  {Object}    For mouseover and mouseout, the previous object
    + * - offsetX        {number}    X-coordinate relative to target
    + * - offsetY        {number}    Y-coordinate relative to target
    + * - clientX        {number}    X-coordinate relative to viewport
    + * - clientY        {number}    Y-coordinate relative to viewport
    + * - screenX        {number}    X-coordinate relative to the edge of the screen
    + * - screenY        {number}    Y-coordinate relative to the edge of the screen
    + * - button         {number}    Mouse button. Use isButton() to test.
    + * - keyCode        {number}    Key-code
    + * - ctrlKey        {boolean}   Was ctrl key depressed
    + * - altKey         {boolean}   Was alt key depressed
    + * - shiftKey       {boolean}   Was shift key depressed
    + * - metaKey        {boolean}   Was meta key depressed
    + * - defaultPrevented {boolean} Whether the default action has been prevented
    + * - state          {Object}    History state object
    + *
    + * NOTE: The keyCode member contains the raw browser keyCode. For normalized
    + * key and character code use {@link goog.events.KeyHandler}.
    + * </pre>
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.events.BrowserEvent');
    +goog.provide('goog.events.BrowserEvent.MouseButton');
    +
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.reflect');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Accepts a browser event object and creates a patched, cross browser event
    + * object.
    + * The content of this object will not be initialized if no event object is
    + * provided. If this is the case, init() needs to be invoked separately.
    + * @param {Event=} opt_e Browser event object.
    + * @param {EventTarget=} opt_currentTarget Current target for event.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
    +  goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
    +
    +  /**
    +   * Target that fired the event.
    +   * @override
    +   * @type {Node}
    +   */
    +  this.target = null;
    +
    +  /**
    +   * Node that had the listener attached.
    +   * @override
    +   * @type {Node|undefined}
    +   */
    +  this.currentTarget = null;
    +
    +  /**
    +   * For mouseover and mouseout events, the related object for the event.
    +   * @type {Node}
    +   */
    +  this.relatedTarget = null;
    +
    +  /**
    +   * X-coordinate relative to target.
    +   * @type {number}
    +   */
    +  this.offsetX = 0;
    +
    +  /**
    +   * Y-coordinate relative to target.
    +   * @type {number}
    +   */
    +  this.offsetY = 0;
    +
    +  /**
    +   * X-coordinate relative to the window.
    +   * @type {number}
    +   */
    +  this.clientX = 0;
    +
    +  /**
    +   * Y-coordinate relative to the window.
    +   * @type {number}
    +   */
    +  this.clientY = 0;
    +
    +  /**
    +   * X-coordinate relative to the monitor.
    +   * @type {number}
    +   */
    +  this.screenX = 0;
    +
    +  /**
    +   * Y-coordinate relative to the monitor.
    +   * @type {number}
    +   */
    +  this.screenY = 0;
    +
    +  /**
    +   * Which mouse button was pressed.
    +   * @type {number}
    +   */
    +  this.button = 0;
    +
    +  /**
    +   * Keycode of key press.
    +   * @type {number}
    +   */
    +  this.keyCode = 0;
    +
    +  /**
    +   * Keycode of key press.
    +   * @type {number}
    +   */
    +  this.charCode = 0;
    +
    +  /**
    +   * Whether control was pressed at time of event.
    +   * @type {boolean}
    +   */
    +  this.ctrlKey = false;
    +
    +  /**
    +   * Whether alt was pressed at time of event.
    +   * @type {boolean}
    +   */
    +  this.altKey = false;
    +
    +  /**
    +   * Whether shift was pressed at time of event.
    +   * @type {boolean}
    +   */
    +  this.shiftKey = false;
    +
    +  /**
    +   * Whether the meta key was pressed at time of event.
    +   * @type {boolean}
    +   */
    +  this.metaKey = false;
    +
    +  /**
    +   * History state object, only set for PopState events where it's a copy of the
    +   * state object provided to pushState or replaceState.
    +   * @type {Object}
    +   */
    +  this.state = null;
    +
    +  /**
    +   * Whether the default platform modifier key was pressed at time of event.
    +   * (This is control for all platforms except Mac, where it's Meta.)
    +   * @type {boolean}
    +   */
    +  this.platformModifierKey = false;
    +
    +  /**
    +   * The browser event object.
    +   * @private {Event}
    +   */
    +  this.event_ = null;
    +
    +  if (opt_e) {
    +    this.init(opt_e, opt_currentTarget);
    +  }
    +};
    +goog.inherits(goog.events.BrowserEvent, goog.events.Event);
    +
    +
    +/**
    + * Normalized button constants for the mouse.
    + * @enum {number}
    + */
    +goog.events.BrowserEvent.MouseButton = {
    +  LEFT: 0,
    +  MIDDLE: 1,
    +  RIGHT: 2
    +};
    +
    +
    +/**
    + * Static data for mapping mouse buttons.
    + * @type {!Array<number>}
    + */
    +goog.events.BrowserEvent.IEButtonMap = [
    +  1, // LEFT
    +  4, // MIDDLE
    +  2  // RIGHT
    +];
    +
    +
    +/**
    + * Accepts a browser event object and creates a patched, cross browser event
    + * object.
    + * @param {Event} e Browser event object.
    + * @param {EventTarget=} opt_currentTarget Current target for event.
    + */
    +goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
    +  this.event_ = e;
    +  var type = this.type = e.type;
    +
    +  // TODO(nicksantos): Change this.target to type EventTarget.
    +  this.target = /** @type {Node} */ (e.target) || e.srcElement;
    +
    +  // TODO(nicksantos): Change this.currentTarget to type EventTarget.
    +  this.currentTarget = /** @type {Node} */ (opt_currentTarget);
    +
    +  var relatedTarget = /** @type {Node} */ (e.relatedTarget);
    +  if (relatedTarget) {
    +    // There's a bug in FireFox where sometimes, relatedTarget will be a
    +    // chrome element, and accessing any property of it will get a permission
    +    // denied exception. See:
    +    // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
    +    if (goog.userAgent.GECKO) {
    +      if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
    +        relatedTarget = null;
    +      }
    +    }
    +    // TODO(arv): Use goog.events.EventType when it has been refactored into its
    +    // own file.
    +  } else if (type == goog.events.EventType.MOUSEOVER) {
    +    relatedTarget = e.fromElement;
    +  } else if (type == goog.events.EventType.MOUSEOUT) {
    +    relatedTarget = e.toElement;
    +  }
    +
    +  this.relatedTarget = relatedTarget;
    +
    +  // Lazily calculate the offsetX/Y properties because they trigger style
    +  // recalc and layout. The original event's getters are themselves lazy.
    +  if (Object.defineProperties) {
    +    Object.defineProperties(this,
    +        /** @lends {goog.events.BrowserEvent.prototype} */ ({
    +          offsetX: {
    +            configurable: true,
    +            enumerable: true,
    +            get: this.getOffsetX_,
    +            set: this.setOffsetX_
    +          },
    +          offsetY: {
    +            configurable: true,
    +            enumerable: true,
    +            get: this.getOffsetY_,
    +            set: this.setOffsetY_
    +          }
    +        }));
    +  } else {
    +    this.offsetX = this.getOffsetX_();
    +    this.offsetY = this.getOffsetY_();
    +  }
    +
    +  this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
    +  this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
    +  this.screenX = e.screenX || 0;
    +  this.screenY = e.screenY || 0;
    +
    +  this.button = e.button;
    +
    +  this.keyCode = e.keyCode || 0;
    +  this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
    +  this.ctrlKey = e.ctrlKey;
    +  this.altKey = e.altKey;
    +  this.shiftKey = e.shiftKey;
    +  this.metaKey = e.metaKey;
    +  this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
    +  this.state = e.state;
    +  if (e.defaultPrevented) {
    +    this.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Tests to see which button was pressed during the event. This is really only
    + * useful in IE and Gecko browsers. And in IE, it's only useful for
    + * mousedown/mouseup events, because click only fires for the left mouse button.
    + *
    + * Safari 2 only reports the left button being clicked, and uses the value '1'
    + * instead of 0. Opera only reports a mousedown event for the middle button, and
    + * no mouse events for the right button. Opera has default behavior for left and
    + * middle click that can only be overridden via a configuration setting.
    + *
    + * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
    + *
    + * @param {goog.events.BrowserEvent.MouseButton} button The button
    + *     to test for.
    + * @return {boolean} True if button was pressed.
    + */
    +goog.events.BrowserEvent.prototype.isButton = function(button) {
    +  if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
    +    if (this.type == 'click') {
    +      return button == goog.events.BrowserEvent.MouseButton.LEFT;
    +    } else {
    +      return !!(this.event_.button &
    +          goog.events.BrowserEvent.IEButtonMap[button]);
    +    }
    +  } else {
    +    return this.event_.button == button;
    +  }
    +};
    +
    +
    +/**
    + * Whether this has an "action"-producing mouse button.
    + *
    + * By definition, this includes left-click on windows/linux, and left-click
    + * without the ctrl key on Macs.
    + *
    + * @return {boolean} The result.
    + */
    +goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
    +  // Webkit does not ctrl+click to be a right-click, so we
    +  // normalize it to behave like Gecko and Opera.
    +  return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
    +      !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.events.BrowserEvent.prototype.stopPropagation = function() {
    +  goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
    +  if (this.event_.stopPropagation) {
    +    this.event_.stopPropagation();
    +  } else {
    +    this.event_.cancelBubble = true;
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.events.BrowserEvent.prototype.preventDefault = function() {
    +  goog.events.BrowserEvent.superClass_.preventDefault.call(this);
    +  var be = this.event_;
    +  if (!be.preventDefault) {
    +    be.returnValue = false;
    +    if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
    +      /** @preserveTry */
    +      try {
    +        // Most keys can be prevented using returnValue. Some special keys
    +        // require setting the keyCode to -1 as well:
    +        //
    +        // In IE7:
    +        // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
    +        //
    +        // In IE8:
    +        // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
    +        //
    +        // We therefore do this for all function keys as well as when Ctrl key
    +        // is pressed.
    +        var VK_F1 = 112;
    +        var VK_F12 = 123;
    +        if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
    +          be.keyCode = -1;
    +        }
    +      } catch (ex) {
    +        // IE throws an 'access denied' exception when trying to change
    +        // keyCode in some situations (e.g. srcElement is input[type=file],
    +        // or srcElement is an anchor tag rewritten by parent's innerHTML).
    +        // Do nothing in this case.
    +      }
    +    }
    +  } else {
    +    be.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * @return {Event} The underlying browser event object.
    + */
    +goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
    +  return this.event_;
    +};
    +
    +
    +/** @private @return {number} */
    +goog.events.BrowserEvent.prototype.getOffsetX_ = function() {
    +  // Webkit emits a lame warning whenever layerX/layerY is accessed.
    +  // http://code.google.com/p/chromium/issues/detail?id=101733
    +  return (goog.userAgent.WEBKIT || this.event_.offsetX !== undefined) ?
    +      this.event_.offsetX : this.event_.layerX;
    +};
    +
    +
    +/** @private @param {number} offset */
    +goog.events.BrowserEvent.prototype.setOffsetX_ = function(offset) {
    +  Object.defineProperties(this,
    +      /** @lends {goog.events.BrowserEvent.prototype} */ ({
    +        offsetX: {
    +          writable: true,
    +          enumerable: true,
    +          configurable: true,
    +          value: offset
    +        }
    +      }));
    +};
    +
    +
    +/** @private @return {number} */
    +goog.events.BrowserEvent.prototype.getOffsetY_ = function() {
    +  return (goog.userAgent.WEBKIT || this.event_.offsetY !== undefined) ?
    +      this.event_.offsetY : this.event_.layerY;
    +};
    +
    +
    +/** @private @param {number} offset */
    +goog.events.BrowserEvent.prototype.setOffsetY_ = function(offset) {
    +  Object.defineProperties(this,
    +      /** @lends {goog.events.BrowserEvent.prototype} */ ({
    +        offsetY: {
    +          writable: true,
    +          enumerable: true,
    +          configurable: true,
    +          value: offset
    +        }
    +      }));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/browserevent_test.html b/mapbox-gl/closure-library/closure/goog/events/browserevent_test.html
    new file mode 100644
    index 0000000000..2c2c1f6709
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/browserevent_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  Author: nicksantos@google.com (Nick Santos)
    +-->
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   JsUnit tests for goog.events.BrowserEvent
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.events.BrowserEventTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/browserevent_test.js b/mapbox-gl/closure-library/closure/goog/events/browserevent_test.js
    new file mode 100644
    index 0000000000..241caf56cf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/browserevent_test.js
    @@ -0,0 +1,165 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.BrowserEventTest');
    +goog.setTestOnly('goog.events.BrowserEventTest');
    +
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +var Button = goog.events.BrowserEvent.MouseButton;
    +
    +function setUp() {
    +  stubs.reset();
    +}
    +
    +
    +/**
    + * @see https://bugzilla.mozilla.org/show_bug.cgi?id=497780
    + */
    +function testInvalidNodeBug() {
    +  if (!goog.userAgent.GECKO) return;
    +
    +  var event = {};
    +  event.relatedTarget = {};
    +  event.relatedTarget.__defineGetter__(
    +      'nodeName',
    +      function() {
    +        throw Error('https://bugzilla.mozilla.org/show_bug.cgi?id=497780');
    +      });
    +  assertThrows(function() { return event.relatedTarget.nodeName; });
    +
    +  var bEvent = new goog.events.BrowserEvent(event);
    +  assertEquals(event, bEvent.event_);
    +  assertNull(bEvent.relatedTarget);
    +}
    +
    +function testPreventDefault() {
    +  var event = {};
    +  event.defaultPrevented = false;
    +  var bEvent = new goog.events.BrowserEvent(event);
    +  assertFalse(bEvent.defaultPrevented);
    +  bEvent.preventDefault();
    +  assertTrue(bEvent.defaultPrevented);
    +}
    +
    +function testDefaultPrevented() {
    +  var event = {};
    +  event.defaultPrevented = true;
    +  var bEvent = new goog.events.BrowserEvent(event);
    +  assertTrue(bEvent.defaultPrevented);
    +}
    +
    +function testIsButtonIe() {
    +  stubs.set(goog.events.BrowserFeature, 'HAS_W3C_BUTTON', false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 1),
    +      Button.LEFT,
    +      true);
    +  assertIsButton(
    +      createBrowserEvent('click', 0),
    +      Button.LEFT,
    +      true);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 2),
    +      Button.RIGHT,
    +      false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 4),
    +      Button.MIDDLE,
    +      false);
    +}
    +
    +function testIsButtonWebkitMac() {
    +  stubs.set(goog.events.BrowserFeature, 'HAS_W3C_BUTTON', true);
    +  stubs.set(goog.userAgent, 'WEBKIT', true);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 0),
    +      Button.LEFT,
    +      true);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 0, true),
    +      Button.LEFT,
    +      false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 2),
    +      Button.RIGHT,
    +      false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 2, true),
    +      Button.RIGHT,
    +      false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 1),
    +      Button.MIDDLE,
    +      false);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 1, true),
    +      Button.MIDDLE,
    +      false);
    +}
    +
    +function testIsButtonGecko() {
    +  stubs.set(goog.events.BrowserFeature, 'HAS_W3C_BUTTON', true);
    +  stubs.set(goog.userAgent, 'GECKO', true);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 0),
    +      Button.LEFT,
    +      true);
    +  assertIsButton(
    +      createBrowserEvent('mousedown', 2, true),
    +      Button.RIGHT,
    +      false);
    +}
    +
    +function testOffsets() {
    +  var e = createBrowserEvent('mousedown', 0);
    +  assertEquals(10, e.offsetX);
    +  assertEquals(10, e.offsetY);
    +  e.offsetX = 20;
    +  e.offsetY = 20;
    +  assertEquals(20, e.offsetX);
    +  assertEquals(20, e.offsetY);
    +  e.offsetX = 30;
    +  e.offsetY = 30;
    +  assertEquals(30, e.offsetX);
    +  assertEquals(30, e.offsetY);
    +}
    +
    +function createBrowserEvent(type, button, opt_ctrlKey) {
    +  return new goog.events.BrowserEvent({
    +    type: type,
    +    button: button,
    +    ctrlKey: !!opt_ctrlKey,
    +    offsetX: 10,
    +    offsetY: 10
    +  });
    +}
    +
    +function assertIsButton(event, button, isActionButton) {
    +  for (var key in Button) {
    +    assertEquals(
    +        'Testing isButton(' + key + ') against ' +
    +        button + ' and type ' + event.type,
    +        Button[key] == button, event.isButton(Button[key]));
    +  }
    +
    +  assertEquals(isActionButton, event.isMouseActionButton());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/browserfeature.js b/mapbox-gl/closure-library/closure/goog/events/browserfeature.js
    new file mode 100644
    index 0000000000..61b9d609a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/browserfeature.js
    @@ -0,0 +1,85 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Browser capability checks for the events package.
    + *
    + */
    +
    +
    +goog.provide('goog.events.BrowserFeature');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Enum of browser capabilities.
    + * @enum {boolean}
    + */
    +goog.events.BrowserFeature = {
    +  /**
    +   * Whether the button attribute of the event is W3C compliant.  False in
    +   * Internet Explorer prior to version 9; document-version dependent.
    +   */
    +  HAS_W3C_BUTTON: !goog.userAgent.IE ||
    +      goog.userAgent.isDocumentModeOrHigher(9),
    +
    +  /**
    +   * Whether the browser supports full W3C event model.
    +   */
    +  HAS_W3C_EVENT_SUPPORT: !goog.userAgent.IE ||
    +      goog.userAgent.isDocumentModeOrHigher(9),
    +
    +  /**
    +   * To prevent default in IE7-8 for certain keydown events we need set the
    +   * keyCode to -1.
    +   */
    +  SET_KEY_CODE_TO_PREVENT_DEFAULT: goog.userAgent.IE &&
    +      !goog.userAgent.isVersionOrHigher('9'),
    +
    +  /**
    +   * Whether the {@code navigator.onLine} property is supported.
    +   */
    +  HAS_NAVIGATOR_ONLINE_PROPERTY: !goog.userAgent.WEBKIT ||
    +      goog.userAgent.isVersionOrHigher('528'),
    +
    +  /**
    +   * Whether HTML5 network online/offline events are supported.
    +   */
    +  HAS_HTML5_NETWORK_EVENT_SUPPORT:
    +      goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
    +      goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
    +      goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
    +      goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
    +
    +  /**
    +   * Whether HTML5 network events fire on document.body, or otherwise the
    +   * window.
    +   */
    +  HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
    +      goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
    +      goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
    +
    +  /**
    +   * Whether touch is enabled in the browser.
    +   */
    +  TOUCH_ENABLED:
    +      ('ontouchstart' in goog.global ||
    +          !!(goog.global['document'] &&
    +             document.documentElement &&
    +             'ontouchstart' in document.documentElement) ||
    +          // IE10 uses non-standard touch events, so it has a different check.
    +          !!(goog.global['navigator'] &&
    +              goog.global['navigator']['msMaxTouchPoints']))
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/event.js b/mapbox-gl/closure-library/closure/goog/events/event.js
    new file mode 100644
    index 0000000000..b671289c20
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/event.js
    @@ -0,0 +1,143 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A base class for event objects.
    + *
    + */
    +
    +
    +goog.provide('goog.events.Event');
    +goog.provide('goog.events.EventLike');
    +
    +/**
    + * goog.events.Event no longer depends on goog.Disposable. Keep requiring
    + * goog.Disposable here to not break projects which assume this dependency.
    + * @suppress {extraRequire}
    + */
    +goog.require('goog.Disposable');
    +goog.require('goog.events.EventId');
    +
    +
    +/**
    + * A typedef for event like objects that are dispatchable via the
    + * goog.events.dispatchEvent function. strings are treated as the type for a
    + * goog.events.Event. Objects are treated as an extension of a new
    + * goog.events.Event with the type property of the object being used as the type
    + * of the Event.
    + * @typedef {string|Object|goog.events.Event|goog.events.EventId}
    + */
    +goog.events.EventLike;
    +
    +
    +
    +/**
    + * A base class for event objects, so that they can support preventDefault and
    + * stopPropagation.
    + *
    + * @param {string|!goog.events.EventId} type Event Type.
    + * @param {Object=} opt_target Reference to the object that is the target of
    + *     this event. It has to implement the {@code EventTarget} interface
    + *     declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
    + * @constructor
    + */
    +goog.events.Event = function(type, opt_target) {
    +  /**
    +   * Event type.
    +   * @type {string}
    +   */
    +  this.type = type instanceof goog.events.EventId ? String(type) : type;
    +
    +  /**
    +   * TODO(tbreisacher): The type should probably be
    +   * EventTarget|goog.events.EventTarget.
    +   *
    +   * Target of the event.
    +   * @type {Object|undefined}
    +   */
    +  this.target = opt_target;
    +
    +  /**
    +   * Object that had the listener attached.
    +   * @type {Object|undefined}
    +   */
    +  this.currentTarget = this.target;
    +
    +  /**
    +   * Whether to cancel the event in internal capture/bubble processing for IE.
    +   * @type {boolean}
    +   * @public
    +   * @suppress {underscore|visibility} Technically public, but referencing this
    +   *     outside this package is strongly discouraged.
    +   */
    +  this.propagationStopped_ = false;
    +
    +  /**
    +   * Whether the default action has been prevented.
    +   * This is a property to match the W3C specification at
    +   * {@link http://www.w3.org/TR/DOM-Level-3-Events/
    +   * #events-event-type-defaultPrevented}.
    +   * Must be treated as read-only outside the class.
    +   * @type {boolean}
    +   */
    +  this.defaultPrevented = false;
    +
    +  /**
    +   * Return value for in internal capture/bubble processing for IE.
    +   * @type {boolean}
    +   * @public
    +   * @suppress {underscore|visibility} Technically public, but referencing this
    +   *     outside this package is strongly discouraged.
    +   */
    +  this.returnValue_ = true;
    +};
    +
    +
    +/**
    + * Stops event propagation.
    + */
    +goog.events.Event.prototype.stopPropagation = function() {
    +  this.propagationStopped_ = true;
    +};
    +
    +
    +/**
    + * Prevents the default action, for example a link redirecting to a url.
    + */
    +goog.events.Event.prototype.preventDefault = function() {
    +  this.defaultPrevented = true;
    +  this.returnValue_ = false;
    +};
    +
    +
    +/**
    + * Stops the propagation of the event. It is equivalent to
    + * {@code e.stopPropagation()}, but can be used as the callback argument of
    + * {@link goog.events.listen} without declaring another function.
    + * @param {!goog.events.Event} e An event.
    + */
    +goog.events.Event.stopPropagation = function(e) {
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * Prevents the default action. It is equivalent to
    + * {@code e.preventDefault()}, but can be used as the callback argument of
    + * {@link goog.events.listen} without declaring another function.
    + * @param {!goog.events.Event} e An event.
    + */
    +goog.events.Event.preventDefault = function(e) {
    +  e.preventDefault();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/event_test.html b/mapbox-gl/closure-library/closure/goog/events/event_test.html
    new file mode 100644
    index 0000000000..744fe8a267
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/event_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!-- Author:  attila@google.com (Attila Bodis) -->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.Event
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.events.EventTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/event_test.js b/mapbox-gl/closure-library/closure/goog/events/event_test.js
    new file mode 100644
    index 0000000000..826fe876ae
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/event_test.js
    @@ -0,0 +1,68 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventTest');
    +goog.setTestOnly('goog.events.EventTest');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventId');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.jsunit');
    +
    +var e, target;
    +
    +function setUp() {
    +  target = new goog.events.EventTarget();
    +  e = new goog.events.Event('eventType', target);
    +}
    +
    +function tearDown() {
    +  target.dispose();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Event must not be null', e);
    +  assertEquals('Event type must be as expected', 'eventType', e.type);
    +  assertEquals('Event target must be as expected', target, e.target);
    +  assertEquals('Current target must be as expected', target,
    +      e.currentTarget);
    +}
    +
    +function testStopPropagation() {
    +  // This test breaks encapsulation because there is no public getter for
    +  // propagationStopped_.
    +  assertFalse('Propagation must not have been stopped',
    +      e.propagationStopped_);
    +  e.stopPropagation();
    +  assertTrue('Propagation must have been stopped', e.propagationStopped_);
    +}
    +
    +function testPreventDefault() {
    +  // This test breaks encapsulation because there is no public getter for
    +  // returnValue_.
    +  assertTrue('Return value must be true', e.returnValue_);
    +  e.preventDefault();
    +  assertFalse('Return value must be false', e.returnValue_);
    +}
    +
    +function testDefaultPrevented() {
    +  assertFalse('Default action must not be prevented', e.defaultPrevented);
    +  e.preventDefault();
    +  assertTrue('Default action must be prevented', e.defaultPrevented);
    +}
    +
    +function testEventId() {
    +  e = new goog.events.Event(new goog.events.EventId('eventType'));
    +  assertEquals('Event type must be as expected', 'eventType', e.type);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventhandler.js b/mapbox-gl/closure-library/closure/goog/events/eventhandler.js
    new file mode 100644
    index 0000000000..16b1ad0ff7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventhandler.js
    @@ -0,0 +1,459 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class to create objects which want to handle multiple events
    + * and have their listeners easily cleaned up via a dispose method.
    + *
    + * Example:
    + * <pre>
    + * function Something() {
    + *   Something.base(this);
    + *
    + *   ... set up object ...
    + *
    + *   // Add event listeners
    + *   this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);
    + *   this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);
    + *   this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);
    + *   this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);
    + *   this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);
    + * }
    + * goog.inherits(Something, goog.events.EventHandler);
    + *
    + * Something.prototype.disposeInternal = function() {
    + *   Something.base(this, 'disposeInternal');
    + *   goog.dom.removeNode(this.container);
    + * };
    + *
    + *
    + * // Then elsewhere:
    + *
    + * var activeSomething = null;
    + * function openSomething() {
    + *   activeSomething = new Something();
    + * }
    + *
    + * function closeSomething() {
    + *   if (activeSomething) {
    + *     activeSomething.dispose();  // Remove event listeners
    + *     activeSomething = null;
    + *   }
    + * }
    + * </pre>
    + *
    + */
    +
    +goog.provide('goog.events.EventHandler');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.events');
    +goog.require('goog.object');
    +
    +goog.forwardDeclare('goog.events.EventWrapper');
    +
    +
    +
    +/**
    + * Super class for objects that want to easily manage a number of event
    + * listeners.  It allows a short cut to listen and also provides a quick way
    + * to remove all events listeners belonging to this object.
    + * @param {SCOPE=} opt_scope Object in whose scope to call the listeners.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @template SCOPE
    + */
    +goog.events.EventHandler = function(opt_scope) {
    +  goog.Disposable.call(this);
    +  // TODO(mknichel): Rename this to this.scope_ and fix the classes in google3
    +  // that access this private variable. :(
    +  this.handler_ = opt_scope;
    +
    +  /**
    +   * Keys for events that are being listened to.
    +   * @type {!Object<!goog.events.Key>}
    +   * @private
    +   */
    +  this.keys_ = {};
    +};
    +goog.inherits(goog.events.EventHandler, goog.Disposable);
    +
    +
    +/**
    + * Utility array used to unify the cases of listening for an array of types
    + * and listening for a single event, without using recursion or allocating
    + * an array each time.
    + * @type {!Array<string>}
    + * @const
    + * @private
    + */
    +goog.events.EventHandler.typeArray_ = [];
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted then the
    + * EventHandler's handleEvent method will be used.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}
    + *     opt_fn Optional callback function to be used as the listener or an object
    + *     with handleEvent function.
    + * @param {boolean=} opt_capture Optional whether to use capture phase.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template EVENTOBJ
    + */
    +goog.events.EventHandler.prototype.listen = function(
    +    src, type, opt_fn, opt_capture) {
    +  return this.listen_(src, type, opt_fn, opt_capture);
    +};
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted then the
    + * EventHandler's handleEvent method will be used.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|
    + *     null|undefined} fn Optional callback function to be used as the
    + *     listener or an object with handleEvent function.
    + * @param {boolean|undefined} capture Optional whether to use capture phase.
    + * @param {T} scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template T,EVENTOBJ
    + */
    +goog.events.EventHandler.prototype.listenWithScope = function(
    +    src, type, fn, capture, scope) {
    +  // TODO(mknichel): Deprecate this function.
    +  return this.listen_(src, type, fn, capture, scope);
    +};
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted then the
    + * EventHandler's handleEvent method will be used.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
    + *     Optional callback function to be used as the listener or an object with
    + *     handleEvent function.
    + * @param {boolean=} opt_capture Optional whether to use capture phase.
    + * @param {Object=} opt_scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template EVENTOBJ
    + * @private
    + */
    +goog.events.EventHandler.prototype.listen_ = function(src, type, opt_fn,
    +                                                      opt_capture,
    +                                                      opt_scope) {
    +  if (!goog.isArray(type)) {
    +    if (type) {
    +      goog.events.EventHandler.typeArray_[0] = type.toString();
    +    }
    +    type = goog.events.EventHandler.typeArray_;
    +  }
    +  for (var i = 0; i < type.length; i++) {
    +    var listenerObj = goog.events.listen(
    +        src, type[i], opt_fn || this.handleEvent,
    +        opt_capture || false,
    +        opt_scope || this.handler_ || this);
    +
    +    if (!listenerObj) {
    +      // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT
    +      // (goog.events.CaptureSimulationMode) in IE8-, it will return null
    +      // value.
    +      return this;
    +    }
    +
    +    var key = listenerObj.key;
    +    this.keys_[key] = listenerObj;
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted, then the
    + * EventHandler's handleEvent method will be used. After the event has fired the
    + * event listener is removed from the target. If an array of event types is
    + * provided, each event type will be listened to once.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
    + *    Optional callback function to be used as the listener or an object with
    + *    handleEvent function.
    + * @param {boolean=} opt_capture Optional whether to use capture phase.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template EVENTOBJ
    + */
    +goog.events.EventHandler.prototype.listenOnce = function(
    +    src, type, opt_fn, opt_capture) {
    +  return this.listenOnce_(src, type, opt_fn, opt_capture);
    +};
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted, then the
    + * EventHandler's handleEvent method will be used. After the event has fired the
    + * event listener is removed from the target. If an array of event types is
    + * provided, each event type will be listened to once.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|
    + *     null|undefined} fn Optional callback function to be used as the
    + *     listener or an object with handleEvent function.
    + * @param {boolean|undefined} capture Optional whether to use capture phase.
    + * @param {T} scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template T,EVENTOBJ
    + */
    +goog.events.EventHandler.prototype.listenOnceWithScope = function(
    +    src, type, fn, capture, scope) {
    +  // TODO(mknichel): Deprecate this function.
    +  return this.listenOnce_(src, type, fn, capture, scope);
    +};
    +
    +
    +/**
    + * Listen to an event on a Listenable.  If the function is omitted, then the
    + * EventHandler's handleEvent method will be used. After the event has fired
    + * the event listener is removed from the target. If an array of event types is
    + * provided, each event type will be listened to once.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type to listen for or array of event types.
    + * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
    + *    Optional callback function to be used as the listener or an object with
    + *    handleEvent function.
    + * @param {boolean=} opt_capture Optional whether to use capture phase.
    + * @param {Object=} opt_scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template EVENTOBJ
    + * @private
    + */
    +goog.events.EventHandler.prototype.listenOnce_ = function(
    +    src, type, opt_fn, opt_capture, opt_scope) {
    +  if (goog.isArray(type)) {
    +    for (var i = 0; i < type.length; i++) {
    +      this.listenOnce_(src, type[i], opt_fn, opt_capture, opt_scope);
    +    }
    +  } else {
    +    var listenerObj = goog.events.listenOnce(
    +        src, type, opt_fn || this.handleEvent, opt_capture,
    +        opt_scope || this.handler_ || this);
    +    if (!listenerObj) {
    +      // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT
    +      // (goog.events.CaptureSimulationMode) in IE8-, it will return null
    +      // value.
    +      return this;
    +    }
    +
    +    var key = listenerObj.key;
    +    this.keys_[key] = listenerObj;
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Adds an event listener with a specific event wrapper on a DOM Node or an
    + * object that has implemented {@link goog.events.EventTarget}. A listener can
    + * only be added once to an object.
    + *
    + * @param {EventTarget|goog.events.EventTarget} src The node to listen to
    + *     events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(this:SCOPE, ?):?|{handleEvent:function(?):?}|null} listener
    + *     Callback method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + */
    +goog.events.EventHandler.prototype.listenWithWrapper = function(
    +    src, wrapper, listener, opt_capt) {
    +  // TODO(mknichel): Remove the opt_scope from this function and then
    +  // templatize it.
    +  return this.listenWithWrapper_(src, wrapper, listener, opt_capt);
    +};
    +
    +
    +/**
    + * Adds an event listener with a specific event wrapper on a DOM Node or an
    + * object that has implemented {@link goog.events.EventTarget}. A listener can
    + * only be added once to an object.
    + *
    + * @param {EventTarget|goog.events.EventTarget} src The node to listen to
    + *     events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(this:T, ?):?|{handleEvent:function(this:T, ?):?}|null}
    + *     listener Optional callback function to be used as the
    + *     listener or an object with handleEvent function.
    + * @param {boolean|undefined} capture Optional whether to use capture phase.
    + * @param {T} scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @template T
    + */
    +goog.events.EventHandler.prototype.listenWithWrapperAndScope = function(
    +    src, wrapper, listener, capture, scope) {
    +  // TODO(mknichel): Deprecate this function.
    +  return this.listenWithWrapper_(src, wrapper, listener, capture, scope);
    +};
    +
    +
    +/**
    + * Adds an event listener with a specific event wrapper on a DOM Node or an
    + * object that has implemented {@link goog.events.EventTarget}. A listener can
    + * only be added once to an object.
    + *
    + * @param {EventTarget|goog.events.EventTarget} src The node to listen to
    + *     events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
    + *     method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @return {!goog.events.EventHandler<SCOPE>} This object, allowing for
    + *     chaining of calls.
    + * @private
    + */
    +goog.events.EventHandler.prototype.listenWithWrapper_ = function(
    +    src, wrapper, listener, opt_capt, opt_scope) {
    +  wrapper.listen(src, listener, opt_capt, opt_scope || this.handler_ || this,
    +                 this);
    +  return this;
    +};
    +
    +
    +/**
    + * @return {number} Number of listeners registered by this handler.
    + */
    +goog.events.EventHandler.prototype.getListenerCount = function() {
    +  var count = 0;
    +  for (var key in this.keys_) {
    +    if (Object.prototype.hasOwnProperty.call(this.keys_, key)) {
    +      count++;
    +    }
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Unlistens on an event.
    + * @param {goog.events.ListenableType} src Event source.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type or array of event types to unlisten to.
    + * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
    + *     Optional callback function to be used as the listener or an object with
    + *     handleEvent function.
    + * @param {boolean=} opt_capture Optional whether to use capture phase.
    + * @param {Object=} opt_scope Object in whose scope to call the listener.
    + * @return {!goog.events.EventHandler} This object, allowing for chaining of
    + *     calls.
    + * @template EVENTOBJ
    + */
    +goog.events.EventHandler.prototype.unlisten = function(src, type, opt_fn,
    +                                                       opt_capture,
    +                                                       opt_scope) {
    +  if (goog.isArray(type)) {
    +    for (var i = 0; i < type.length; i++) {
    +      this.unlisten(src, type[i], opt_fn, opt_capture, opt_scope);
    +    }
    +  } else {
    +    var listener = goog.events.getListener(src, type,
    +        opt_fn || this.handleEvent,
    +        opt_capture, opt_scope || this.handler_ || this);
    +
    +    if (listener) {
    +      goog.events.unlistenByKey(listener);
    +      delete this.keys_[listener.key];
    +    }
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Removes an event listener which was added with listenWithWrapper().
    + *
    + * @param {EventTarget|goog.events.EventTarget} src The target to stop
    + *     listening to events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
    + *     listener function to remove.
    + * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    + *     whether the listener is fired during the capture or bubble phase of the
    + *     event.
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @return {!goog.events.EventHandler} This object, allowing for chaining of
    + *     calls.
    + */
    +goog.events.EventHandler.prototype.unlistenWithWrapper = function(src, wrapper,
    +    listener, opt_capt, opt_scope) {
    +  wrapper.unlisten(src, listener, opt_capt,
    +                   opt_scope || this.handler_ || this, this);
    +  return this;
    +};
    +
    +
    +/**
    + * Unlistens to all events.
    + */
    +goog.events.EventHandler.prototype.removeAll = function() {
    +  goog.object.forEach(this.keys_, goog.events.unlistenByKey);
    +  this.keys_ = {};
    +};
    +
    +
    +/**
    + * Disposes of this EventHandler and removes all listeners that it registered.
    + * @override
    + * @protected
    + */
    +goog.events.EventHandler.prototype.disposeInternal = function() {
    +  goog.events.EventHandler.superClass_.disposeInternal.call(this);
    +  this.removeAll();
    +};
    +
    +
    +/**
    + * Default event handler
    + * @param {goog.events.Event} e Event object.
    + */
    +goog.events.EventHandler.prototype.handleEvent = function(e) {
    +  throw Error('EventHandler.handleEvent not implemented');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.html
    new file mode 100644
    index 0000000000..eb470fc031
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.EventHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.EventHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="a">
    +  </div>
    +  <div id="b">
    +  </div>
    +  <div id="c">
    +  </div>
    +  <div id="d">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.js
    new file mode 100644
    index 0000000000..f85aaea929
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventhandler_test.js
    @@ -0,0 +1,247 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventHandlerTest');
    +goog.setTestOnly('goog.events.EventHandlerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var a, b, c, d, eh;
    +
    +function setUpPage() {
    +  a = document.getElementById('a');
    +  b = document.getElementById('b');
    +  c = document.getElementById('c');
    +  d = document.getElementById('d');
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(eh);
    +}
    +
    +function testEventHandlerClearsListeners() {
    +  function tmp() {}
    +
    +  goog.events.listen(a, 'click', tmp);
    +
    +  assertEquals(1, goog.events.getListeners(a, 'click', false).length);
    +
    +  eh = new goog.events.EventHandler();
    +  eh.listen(a, 'click');
    +  eh.listen(a, 'keypress');
    +  eh.listen(b, 'mouseover');
    +  eh.listen(c, 'mousedown');
    +  eh.listen(d, 'click');
    +  eh.listen(d, 'mousedown');
    +
    +  assertEquals(2, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'keypress', false).length);
    +  assertEquals(1, goog.events.getListeners(b, 'mouseover', false).length);
    +  assertEquals(1, goog.events.getListeners(c, 'mousedown', false).length);
    +  assertEquals(1, goog.events.getListeners(d, 'click', false).length);
    +  assertEquals(1, goog.events.getListeners(d, 'mousedown', false).length);
    +
    +  eh.unlisten(d, 'mousedown');
    +
    +  assertEquals(2, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'keypress', false).length);
    +  assertEquals(1, goog.events.getListeners(b, 'mouseover', false).length);
    +  assertEquals(1, goog.events.getListeners(c, 'mousedown', false).length);
    +  assertEquals(1, goog.events.getListeners(d, 'click', false).length);
    +  assertEquals(0, goog.events.getListeners(d, 'mousedown', false).length);
    +
    +  eh.dispose();
    +
    +  assertEquals(1, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(0, goog.events.getListeners(a, 'keypress', false).length);
    +  assertEquals(0, goog.events.getListeners(b, 'mouseover', false).length);
    +  assertEquals(0, goog.events.getListeners(c, 'mousedown', false).length);
    +  assertEquals(0, goog.events.getListeners(d, 'click', false).length);
    +  assertEquals(0, goog.events.getListeners(d, 'mousedown', false).length);
    +
    +  goog.events.unlisten(a, 'click', tmp);
    +  assertEquals(0, goog.events.getListeners(a, 'click', false).length);
    +}
    +
    +function testListenArray() {
    +  eh = new goog.events.EventHandler();
    +
    +  eh.listen(a, ['click', 'mousedown', 'mouseup']);
    +
    +  assertEquals(1, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'mousedown', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'mouseup', false).length);
    +
    +  eh.unlisten(a, ['click', 'mousedown', 'mouseup']);
    +
    +  assertEquals(0, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(0, goog.events.getListeners(a, 'mousedown', false).length);
    +  assertEquals(0, goog.events.getListeners(a, 'mouseup', false).length);
    +
    +  eh.listen(a, ['click', 'mousedown', 'mouseup']);
    +
    +  assertEquals(1, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'mousedown', false).length);
    +  assertEquals(1, goog.events.getListeners(a, 'mouseup', false).length);
    +
    +  eh.removeAll();
    +
    +  assertEquals(0, goog.events.getListeners(a, 'click', false).length);
    +  assertEquals(0, goog.events.getListeners(a, 'mousedown', false).length);
    +  assertEquals(0, goog.events.getListeners(a, 'mouseup', false).length);
    +}
    +
    +function testListenOnceRemovesListenerWhenFired() {
    +  var target = new goog.events.EventTarget();
    +  eh = new goog.events.EventHandler();
    +  var handler = goog.testing.recordFunction();
    +  eh.listenOnce(target, 'click', handler);
    +
    +  target.dispatchEvent('click');
    +  assertEquals('One event should have been dispatched',
    +      1, handler.getCallCount());
    +
    +  target.dispatchEvent('click');
    +  assertEquals('No event should have been dispatched',
    +      1, handler.getCallCount());
    +}
    +
    +function testListenOnceListenerIsCleanedUp() {
    +  var target = new goog.events.EventTarget();
    +  eh = new goog.events.EventHandler();
    +  var handler = goog.testing.recordFunction();
    +  eh.listenOnce(target, 'click', handler);
    +
    +  eh.removeAll();
    +
    +  target.dispatchEvent('click');
    +  assertEquals(0, handler.getCallCount());
    +}
    +
    +function testClearListenersWithListenOnceListenerRemoved() {
    +  var target = new goog.events.EventTarget();
    +  eh = new goog.events.EventHandler();
    +
    +  var handler = goog.testing.recordFunction();
    +  eh.listenOnce(target, 'click', handler);
    +
    +  assertNotNull(goog.events.getListener(target, 'click', handler, false, eh));
    +
    +  target.dispatchEvent('click');
    +  assertEquals('One event should have been dispatched',
    +      1, handler.getCallCount());
    +
    +  assertNull(goog.events.getListener(target, 'click', handler, false, eh));
    +
    +  eh.removeAll();
    +
    +  target.dispatchEvent('click');
    +  assertEquals('No event should have been dispatched',
    +      1, handler.getCallCount());
    +}
    +
    +function testListenOnceArray() {
    +  var target = new goog.events.EventTarget();
    +
    +  eh = new goog.events.EventHandler();
    +  var handler = goog.testing.recordFunction();
    +  eh.listenOnce(target, ['click', 'mousedown', 'mouseup'], handler);
    +
    +  target.dispatchEvent('click');
    +  assertEquals('1 event should have been dispatched',
    +      1, handler.getCallCount());
    +  assertEquals('Should be a click event',
    +      'click', handler.getLastCall().getArgument(0).type);
    +
    +  target.dispatchEvent('click');
    +  assertEquals('No event should be dispatched',
    +      1, handler.getCallCount());
    +
    +  target.dispatchEvent('mouseup');
    +  assertEquals('1 event should have been dispatched',
    +      2, handler.getCallCount());
    +  assertEquals('Should be a mouseup event',
    +      'mouseup', handler.getLastCall().getArgument(0).type);
    +
    +  target.dispatchEvent('mouseup');
    +  assertEquals('No event should be dispatched',
    +      2, handler.getCallCount());
    +
    +  target.dispatchEvent('mousedown');
    +  assertEquals('1 event should have been dispatched',
    +      3, handler.getCallCount());
    +  assertEquals('Should be a mousedown event',
    +      'mousedown', handler.getLastCall().getArgument(0).type);
    +
    +  target.dispatchEvent('mousedown');
    +  assertEquals('No event should be dispatched',
    +      3, handler.getCallCount());
    +}
    +
    +function testListenUnlistenWithObjectHandler() {
    +  var target = new goog.events.EventTarget();
    +  eh = new goog.events.EventHandler();
    +  var handlerObj = {
    +    handleEvent: goog.testing.recordFunction()
    +  };
    +  eh.listen(target, 'click', handlerObj);
    +
    +  target.dispatchEvent('click');
    +  assertEquals('One event should have been dispatched',
    +      1, handlerObj.handleEvent.getCallCount());
    +
    +  target.dispatchEvent('click');
    +  assertEquals('One event should have been dispatched',
    +      2, handlerObj.handleEvent.getCallCount());
    +
    +  eh.unlisten(target, 'click', handlerObj);
    +  target.dispatchEvent('click');
    +  assertEquals('No event should have been dispatched',
    +      2, handlerObj.handleEvent.getCallCount());
    +}
    +
    +function testListenOnceWithObjectHandler() {
    +  var target = new goog.events.EventTarget();
    +  eh = new goog.events.EventHandler();
    +  var handlerObj = {
    +    handleEvent: goog.testing.recordFunction()
    +  };
    +  eh.listenOnce(target, 'click', handlerObj);
    +
    +  target.dispatchEvent('click');
    +  assertEquals('One event should have been dispatched',
    +      1, handlerObj.handleEvent.getCallCount());
    +
    +  target.dispatchEvent('click');
    +  assertEquals('No event should have been dispatched',
    +      1, handlerObj.handleEvent.getCallCount());
    +}
    +
    +function testGetListenerCount() {
    +  eh = new goog.events.EventHandler();
    +  assertEquals('0 listeners registered initially', 0, eh.getListenerCount());
    +  var target = new goog.events.EventTarget();
    +  eh.listen(target, 'click', goog.nullFunction, false);
    +  eh.listen(target, 'click', goog.nullFunction, true);
    +  assertEquals('2 listeners registered', 2, eh.getListenerCount());
    +  eh.unlisten(target, 'click', goog.nullFunction, true);
    +  assertEquals('1 listener removed, 1 left', 1, eh.getListenerCount());
    +  eh.removeAll();
    +  assertEquals('all listeners removed', 0, eh.getListenerCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventid.js b/mapbox-gl/closure-library/closure/goog/events/eventid.js
    new file mode 100644
    index 0000000000..9a4822e5f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventid.js
    @@ -0,0 +1,47 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventId');
    +
    +
    +
    +/**
    + * A templated class that is used when registering for events. Typical usage:
    + * <code>
    + *   /** @type {goog.events.EventId<MyEventObj>}
    + *   var myEventId = new goog.events.EventId(
    + *       goog.events.getUniqueId(('someEvent'));
    + *
    + *   // No need to cast or declare here since the compiler knows the correct
    + *   // type of 'evt' (MyEventObj).
    + *   something.listen(myEventId, function(evt) {});
    + * </code>
    + *
    + * @param {string} eventId
    + * @template T
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.events.EventId = function(eventId) {
    +  /** @const */ this.id = eventId;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.events.EventId.prototype.toString = function() {
    +  return this.id;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/events.js b/mapbox-gl/closure-library/closure/goog/events/events.js
    new file mode 100644
    index 0000000000..39cc405ccf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/events.js
    @@ -0,0 +1,983 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An event manager for both native browser event
    + * targets and custom JavaScript event targets
    + * ({@code goog.events.Listenable}). This provides an abstraction
    + * over browsers' event systems.
    + *
    + * It also provides a simulation of W3C event model's capture phase in
    + * Internet Explorer (IE 8 and below). Caveat: the simulation does not
    + * interact well with listeners registered directly on the elements
    + * (bypassing goog.events) or even with listeners registered via
    + * goog.events in a separate JS binary. In these cases, we provide
    + * no ordering guarantees.
    + *
    + * The listeners will receive a "patched" event object. Such event object
    + * contains normalized values for certain event properties that differs in
    + * different browsers.
    + *
    + * Example usage:
    + * <pre>
    + * goog.events.listen(myNode, 'click', function(e) { alert('woo') });
    + * goog.events.listen(myNode, 'mouseover', mouseHandler, true);
    + * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
    + * goog.events.removeAll(myNode);
    + * </pre>
    + *
    + *                                            in IE and event object patching]
    + * @author arv@google.com (Erik Arvidsson)
    + *
    + * @see ../demos/events.html
    + * @see ../demos/event-propagation.html
    + * @see ../demos/stopevent.html
    + */
    +
    +// IMPLEMENTATION NOTES:
    +// goog.events stores an auxiliary data structure on each EventTarget
    +// source being listened on. This allows us to take advantage of GC,
    +// having the data structure GC'd when the EventTarget is GC'd. This
    +// GC behavior is equivalent to using W3C DOM Events directly.
    +
    +goog.provide('goog.events');
    +goog.provide('goog.events.CaptureSimulationMode');
    +goog.provide('goog.events.Key');
    +goog.provide('goog.events.ListenableType');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.Listenable');
    +goog.require('goog.events.ListenerMap');
    +
    +goog.forwardDeclare('goog.debug.ErrorHandler');
    +goog.forwardDeclare('goog.events.EventWrapper');
    +
    +
    +/**
    + * @typedef {number|goog.events.ListenableKey}
    + */
    +goog.events.Key;
    +
    +
    +/**
    + * @typedef {EventTarget|goog.events.Listenable}
    + */
    +goog.events.ListenableType;
    +
    +
    +/**
    + * Property name on a native event target for the listener map
    + * associated with the event target.
    + * @private @const {string}
    + */
    +goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
    +
    +
    +/**
    + * String used to prepend to IE event types.
    + * @const
    + * @private
    + */
    +goog.events.onString_ = 'on';
    +
    +
    +/**
    + * Map of computed "on<eventname>" strings for IE event types. Caching
    + * this removes an extra object allocation in goog.events.listen which
    + * improves IE6 performance.
    + * @const
    + * @dict
    + * @private
    + */
    +goog.events.onStringMap_ = {};
    +
    +
    +/**
    + * @enum {number} Different capture simulation mode for IE8-.
    + */
    +goog.events.CaptureSimulationMode = {
    +  /**
    +   * Does not perform capture simulation. Will asserts in IE8- when you
    +   * add capture listeners.
    +   */
    +  OFF_AND_FAIL: 0,
    +
    +  /**
    +   * Does not perform capture simulation, silently ignore capture
    +   * listeners.
    +   */
    +  OFF_AND_SILENT: 1,
    +
    +  /**
    +   * Performs capture simulation.
    +   */
    +  ON: 2
    +};
    +
    +
    +/**
    + * @define {number} The capture simulation mode for IE8-. By default,
    + *     this is ON.
    + */
    +goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);
    +
    +
    +/**
    + * Estimated count of total native listeners.
    + * @private {number}
    + */
    +goog.events.listenerCountEstimate_ = 0;
    +
    +
    +/**
    + * Adds an event listener for a specific event on a native event
    + * target (such as a DOM element) or an object that has implemented
    + * {@link goog.events.Listenable}. A listener can only be added once
    + * to an object and if it is added again the key for the listener is
    + * returned. Note that if the existing listener is a one-off listener
    + * (registered via listenOnce), it will no longer be a one-off
    + * listener after a call to listen().
    + *
    + * @param {EventTarget|goog.events.Listenable} src The node to listen
    + *     to events on.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type or array of event types.
    + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
    + *     listener Callback method, or an object with a handleEvent function.
    + *     WARNING: passing an Object is now softly deprecated.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {T=} opt_handler Element in whose scope to call the listener.
    + * @return {goog.events.Key} Unique key for the listener.
    + * @template T,EVENTOBJ
    + */
    +goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
    +  if (goog.isArray(type)) {
    +    for (var i = 0; i < type.length; i++) {
    +      goog.events.listen(src, type[i], listener, opt_capt, opt_handler);
    +    }
    +    return null;
    +  }
    +
    +  listener = goog.events.wrapListener(listener);
    +  if (goog.events.Listenable.isImplementedBy(src)) {
    +    return src.listen(
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, opt_capt, opt_handler);
    +  } else {
    +    return goog.events.listen_(
    +        /** @type {!EventTarget} */ (src),
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, /* callOnce */ false, opt_capt, opt_handler);
    +  }
    +};
    +
    +
    +/**
    + * Adds an event listener for a specific event on a native event
    + * target. A listener can only be added once to an object and if it
    + * is added again the key for the listener is returned.
    + *
    + * Note that a one-off listener will not change an existing listener,
    + * if any. On the other hand a normal listener will change existing
    + * one-off listener to become a normal listener.
    + *
    + * @param {EventTarget} src The node to listen to events on.
    + * @param {string|!goog.events.EventId} type Event type.
    + * @param {!Function} listener Callback function.
    + * @param {boolean} callOnce Whether the listener is a one-off
    + *     listener or otherwise.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_handler Element in whose scope to call the listener.
    + * @return {goog.events.ListenableKey} Unique key for the listener.
    + * @private
    + */
    +goog.events.listen_ = function(
    +    src, type, listener, callOnce, opt_capt, opt_handler) {
    +  if (!type) {
    +    throw Error('Invalid event type');
    +  }
    +
    +  var capture = !!opt_capt;
    +  if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    +    if (goog.events.CAPTURE_SIMULATION_MODE ==
    +        goog.events.CaptureSimulationMode.OFF_AND_FAIL) {
    +      goog.asserts.fail('Can not register capture listener in IE8-.');
    +      return null;
    +    } else if (goog.events.CAPTURE_SIMULATION_MODE ==
    +        goog.events.CaptureSimulationMode.OFF_AND_SILENT) {
    +      return null;
    +    }
    +  }
    +
    +  var listenerMap = goog.events.getListenerMap_(src);
    +  if (!listenerMap) {
    +    src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
    +        new goog.events.ListenerMap(src);
    +  }
    +
    +  var listenerObj = listenerMap.add(
    +      type, listener, callOnce, opt_capt, opt_handler);
    +
    +  // If the listenerObj already has a proxy, it has been set up
    +  // previously. We simply return.
    +  if (listenerObj.proxy) {
    +    return listenerObj;
    +  }
    +
    +  var proxy = goog.events.getProxy();
    +  listenerObj.proxy = proxy;
    +
    +  proxy.src = src;
    +  proxy.listener = listenerObj;
    +
    +  // Attach the proxy through the browser's API
    +  if (src.addEventListener) {
    +    src.addEventListener(type.toString(), proxy, capture);
    +  } else {
    +    // The else above used to be else if (src.attachEvent) and then there was
    +    // another else statement that threw an exception warning the developer
    +    // they made a mistake. This resulted in an extra object allocation in IE6
    +    // due to a wrapper object that had to be implemented around the element
    +    // and so was removed.
    +    src.attachEvent(goog.events.getOnString_(type.toString()), proxy);
    +  }
    +
    +  goog.events.listenerCountEstimate_++;
    +  return listenerObj;
    +};
    +
    +
    +/**
    + * Helper function for returning a proxy function.
    + * @return {!Function} A new or reused function object.
    + */
    +goog.events.getProxy = function() {
    +  var proxyCallbackFunction = goog.events.handleBrowserEvent_;
    +  // Use a local var f to prevent one allocation.
    +  var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ?
    +      function(eventObject) {
    +        return proxyCallbackFunction.call(f.src, f.listener, eventObject);
    +      } :
    +      function(eventObject) {
    +        var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
    +        // NOTE(chrishenry): In IE, we hack in a capture phase. However, if
    +        // there is inline event handler which tries to prevent default (for
    +        // example <a href="..." onclick="return false">...</a>) in a
    +        // descendant element, the prevent default will be overridden
    +        // by this listener if this listener were to return true. Hence, we
    +        // return undefined.
    +        if (!v) return v;
    +      };
    +  return f;
    +};
    +
    +
    +/**
    + * Adds an event listener for a specific event on a native event
    + * target (such as a DOM element) or an object that has implemented
    + * {@link goog.events.Listenable}. After the event has fired the event
    + * listener is removed from the target.
    + *
    + * If an existing listener already exists, listenOnce will do
    + * nothing. In particular, if the listener was previously registered
    + * via listen(), listenOnce() will not turn the listener into a
    + * one-off listener. Similarly, if there is already an existing
    + * one-off listener, listenOnce does not modify the listeners (it is
    + * still a once listener).
    + *
    + * @param {EventTarget|goog.events.Listenable} src The node to listen
    + *     to events on.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type or array of event types.
    + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
    + *     listener Callback method.
    + * @param {boolean=} opt_capt Fire in capture phase?.
    + * @param {T=} opt_handler Element in whose scope to call the listener.
    + * @return {goog.events.Key} Unique key for the listener.
    + * @template T,EVENTOBJ
    + */
    +goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
    +  if (goog.isArray(type)) {
    +    for (var i = 0; i < type.length; i++) {
    +      goog.events.listenOnce(src, type[i], listener, opt_capt, opt_handler);
    +    }
    +    return null;
    +  }
    +
    +  listener = goog.events.wrapListener(listener);
    +  if (goog.events.Listenable.isImplementedBy(src)) {
    +    return src.listenOnce(
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, opt_capt, opt_handler);
    +  } else {
    +    return goog.events.listen_(
    +        /** @type {!EventTarget} */ (src),
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, /* callOnce */ true, opt_capt, opt_handler);
    +  }
    +};
    +
    +
    +/**
    + * Adds an event listener with a specific event wrapper on a DOM Node or an
    + * object that has implemented {@link goog.events.Listenable}. A listener can
    + * only be added once to an object.
    + *
    + * @param {EventTarget|goog.events.Listenable} src The target to
    + *     listen to events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
    + *     Callback method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {T=} opt_handler Element in whose scope to call the listener.
    + * @template T
    + */
    +goog.events.listenWithWrapper = function(src, wrapper, listener, opt_capt,
    +    opt_handler) {
    +  wrapper.listen(src, listener, opt_capt, opt_handler);
    +};
    +
    +
    +/**
    + * Removes an event listener which was added with listen().
    + *
    + * @param {EventTarget|goog.events.Listenable} src The target to stop
    + *     listening to events on.
    + * @param {string|Array<string>|
    + *     !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
    + *     type Event type or array of event types to unlisten to.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
    + *     listener function to remove.
    + * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    + *     whether the listener is fired during the capture or bubble phase of the
    + *     event.
    + * @param {Object=} opt_handler Element in whose scope to call the listener.
    + * @return {?boolean} indicating whether the listener was there to remove.
    + * @template EVENTOBJ
    + */
    +goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
    +  if (goog.isArray(type)) {
    +    for (var i = 0; i < type.length; i++) {
    +      goog.events.unlisten(src, type[i], listener, opt_capt, opt_handler);
    +    }
    +    return null;
    +  }
    +
    +  listener = goog.events.wrapListener(listener);
    +  if (goog.events.Listenable.isImplementedBy(src)) {
    +    return src.unlisten(
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, opt_capt, opt_handler);
    +  }
    +
    +  if (!src) {
    +    // TODO(chrishenry): We should tighten the API to only accept
    +    // non-null objects, or add an assertion here.
    +    return false;
    +  }
    +
    +  var capture = !!opt_capt;
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {!EventTarget} */ (src));
    +  if (listenerMap) {
    +    var listenerObj = listenerMap.getListener(
    +        /** @type {string|!goog.events.EventId} */ (type),
    +        listener, capture, opt_handler);
    +    if (listenerObj) {
    +      return goog.events.unlistenByKey(listenerObj);
    +    }
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Removes an event listener which was added with listen() by the key
    + * returned by listen().
    + *
    + * @param {goog.events.Key} key The key returned by listen() for this
    + *     event listener.
    + * @return {boolean} indicating whether the listener was there to remove.
    + */
    +goog.events.unlistenByKey = function(key) {
    +  // TODO(chrishenry): Remove this check when tests that rely on this
    +  // are fixed.
    +  if (goog.isNumber(key)) {
    +    return false;
    +  }
    +
    +  var listener = /** @type {goog.events.ListenableKey} */ (key);
    +  if (!listener || listener.removed) {
    +    return false;
    +  }
    +
    +  var src = listener.src;
    +  if (goog.events.Listenable.isImplementedBy(src)) {
    +    return src.unlistenByKey(listener);
    +  }
    +
    +  var type = listener.type;
    +  var proxy = listener.proxy;
    +  if (src.removeEventListener) {
    +    src.removeEventListener(type, proxy, listener.capture);
    +  } else if (src.detachEvent) {
    +    src.detachEvent(goog.events.getOnString_(type), proxy);
    +  }
    +  goog.events.listenerCountEstimate_--;
    +
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {!EventTarget} */ (src));
    +  // TODO(chrishenry): Try to remove this conditional and execute the
    +  // first branch always. This should be safe.
    +  if (listenerMap) {
    +    listenerMap.removeByKey(listener);
    +    if (listenerMap.getTypeCount() == 0) {
    +      // Null the src, just because this is simple to do (and useful
    +      // for IE <= 7).
    +      listenerMap.src = null;
    +      // We don't use delete here because IE does not allow delete
    +      // on a window object.
    +      src[goog.events.LISTENER_MAP_PROP_] = null;
    +    }
    +  } else {
    +    listener.markAsRemoved();
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Removes an event listener which was added with listenWithWrapper().
    + *
    + * @param {EventTarget|goog.events.Listenable} src The target to stop
    + *     listening to events on.
    + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
    + *     listener function to remove.
    + * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    + *     whether the listener is fired during the capture or bubble phase of the
    + *     event.
    + * @param {Object=} opt_handler Element in whose scope to call the listener.
    + */
    +goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
    +    opt_handler) {
    +  wrapper.unlisten(src, listener, opt_capt, opt_handler);
    +};
    +
    +
    +/**
    + * Removes all listeners from an object. You can also optionally
    + * remove listeners of a particular type.
    + *
    + * @param {Object|undefined} obj Object to remove listeners from. Must be an
    + *     EventTarget or a goog.events.Listenable.
    + * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
    + *     Default is all types.
    + * @return {number} Number of listeners removed.
    + */
    +goog.events.removeAll = function(obj, opt_type) {
    +  // TODO(chrishenry): Change the type of obj to
    +  // (!EventTarget|!goog.events.Listenable).
    +
    +  if (!obj) {
    +    return 0;
    +  }
    +
    +  if (goog.events.Listenable.isImplementedBy(obj)) {
    +    return obj.removeAllListeners(opt_type);
    +  }
    +
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {!EventTarget} */ (obj));
    +  if (!listenerMap) {
    +    return 0;
    +  }
    +
    +  var count = 0;
    +  var typeStr = opt_type && opt_type.toString();
    +  for (var type in listenerMap.listeners) {
    +    if (!typeStr || type == typeStr) {
    +      // Clone so that we don't need to worry about unlistenByKey
    +      // changing the content of the ListenerMap.
    +      var listeners = listenerMap.listeners[type].concat();
    +      for (var i = 0; i < listeners.length; ++i) {
    +        if (goog.events.unlistenByKey(listeners[i])) {
    +          ++count;
    +        }
    +      }
    +    }
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Gets the listeners for a given object, type and capture phase.
    + *
    + * @param {Object} obj Object to get listeners for.
    + * @param {string|!goog.events.EventId} type Event type.
    + * @param {boolean} capture Capture phase?.
    + * @return {Array<goog.events.Listener>} Array of listener objects.
    + */
    +goog.events.getListeners = function(obj, type, capture) {
    +  if (goog.events.Listenable.isImplementedBy(obj)) {
    +    return obj.getListeners(type, capture);
    +  } else {
    +    if (!obj) {
    +      // TODO(chrishenry): We should tighten the API to accept
    +      // !EventTarget|goog.events.Listenable, and add an assertion here.
    +      return [];
    +    }
    +
    +    var listenerMap = goog.events.getListenerMap_(
    +        /** @type {!EventTarget} */ (obj));
    +    return listenerMap ? listenerMap.getListeners(type, capture) : [];
    +  }
    +};
    +
    +
    +/**
    + * Gets the goog.events.Listener for the event or null if no such listener is
    + * in use.
    + *
    + * @param {EventTarget|goog.events.Listenable} src The target from
    + *     which to get listeners.
    + * @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.
    + * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The
    + *     listener function to get.
    + * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
    + *                            whether the listener is fired during the
    + *                            capture or bubble phase of the event.
    + * @param {Object=} opt_handler Element in whose scope to call the listener.
    + * @return {goog.events.ListenableKey} the found listener or null if not found.
    + * @template EVENTOBJ
    + */
    +goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
    +  // TODO(chrishenry): Change type from ?string to string, or add assertion.
    +  type = /** @type {string} */ (type);
    +  listener = goog.events.wrapListener(listener);
    +  var capture = !!opt_capt;
    +  if (goog.events.Listenable.isImplementedBy(src)) {
    +    return src.getListener(type, listener, capture, opt_handler);
    +  }
    +
    +  if (!src) {
    +    // TODO(chrishenry): We should tighten the API to only accept
    +    // non-null objects, or add an assertion here.
    +    return null;
    +  }
    +
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {!EventTarget} */ (src));
    +  if (listenerMap) {
    +    return listenerMap.getListener(type, listener, capture, opt_handler);
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns whether an event target has any active listeners matching the
    + * specified signature. If either the type or capture parameters are
    + * unspecified, the function will match on the remaining criteria.
    + *
    + * @param {EventTarget|goog.events.Listenable} obj Target to get
    + *     listeners for.
    + * @param {string|!goog.events.EventId=} opt_type Event type.
    + * @param {boolean=} opt_capture Whether to check for capture or bubble-phase
    + *     listeners.
    + * @return {boolean} Whether an event target has one or more listeners matching
    + *     the requested type and/or capture phase.
    + */
    +goog.events.hasListener = function(obj, opt_type, opt_capture) {
    +  if (goog.events.Listenable.isImplementedBy(obj)) {
    +    return obj.hasListener(opt_type, opt_capture);
    +  }
    +
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {!EventTarget} */ (obj));
    +  return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
    +};
    +
    +
    +/**
    + * Provides a nice string showing the normalized event objects public members
    + * @param {Object} e Event Object.
    + * @return {string} String of the public members of the normalized event object.
    + */
    +goog.events.expose = function(e) {
    +  var str = [];
    +  for (var key in e) {
    +    if (e[key] && e[key].id) {
    +      str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
    +    } else {
    +      str.push(key + ' = ' + e[key]);
    +    }
    +  }
    +  return str.join('\n');
    +};
    +
    +
    +/**
    + * Returns a string with on prepended to the specified type. This is used for IE
    + * which expects "on" to be prepended. This function caches the string in order
    + * to avoid extra allocations in steady state.
    + * @param {string} type Event type.
    + * @return {string} The type string with 'on' prepended.
    + * @private
    + */
    +goog.events.getOnString_ = function(type) {
    +  if (type in goog.events.onStringMap_) {
    +    return goog.events.onStringMap_[type];
    +  }
    +  return goog.events.onStringMap_[type] = goog.events.onString_ + type;
    +};
    +
    +
    +/**
    + * Fires an object's listeners of a particular type and phase
    + *
    + * @param {Object} obj Object whose listeners to call.
    + * @param {string|!goog.events.EventId} type Event type.
    + * @param {boolean} capture Which event phase.
    + * @param {Object} eventObject Event object to be passed to listener.
    + * @return {boolean} True if all listeners returned true else false.
    + */
    +goog.events.fireListeners = function(obj, type, capture, eventObject) {
    +  if (goog.events.Listenable.isImplementedBy(obj)) {
    +    return obj.fireListeners(type, capture, eventObject);
    +  }
    +
    +  return goog.events.fireListeners_(obj, type, capture, eventObject);
    +};
    +
    +
    +/**
    + * Fires an object's listeners of a particular type and phase.
    + * @param {Object} obj Object whose listeners to call.
    + * @param {string|!goog.events.EventId} type Event type.
    + * @param {boolean} capture Which event phase.
    + * @param {Object} eventObject Event object to be passed to listener.
    + * @return {boolean} True if all listeners returned true else false.
    + * @private
    + */
    +goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
    +  /** @type {boolean} */
    +  var retval = true;
    +
    +  var listenerMap = goog.events.getListenerMap_(
    +      /** @type {EventTarget} */ (obj));
    +  if (listenerMap) {
    +    // TODO(chrishenry): Original code avoids array creation when there
    +    // is no listener, so we do the same. If this optimization turns
    +    // out to be not required, we can replace this with
    +    // listenerMap.getListeners(type, capture) instead, which is simpler.
    +    var listenerArray = listenerMap.listeners[type.toString()];
    +    if (listenerArray) {
    +      listenerArray = listenerArray.concat();
    +      for (var i = 0; i < listenerArray.length; i++) {
    +        var listener = listenerArray[i];
    +        // We might not have a listener if the listener was removed.
    +        if (listener && listener.capture == capture && !listener.removed) {
    +          var result = goog.events.fireListener(listener, eventObject);
    +          retval = retval && (result !== false);
    +        }
    +      }
    +    }
    +  }
    +  return retval;
    +};
    +
    +
    +/**
    + * Fires a listener with a set of arguments
    + *
    + * @param {goog.events.Listener} listener The listener object to call.
    + * @param {Object} eventObject The event object to pass to the listener.
    + * @return {boolean} Result of listener.
    + */
    +goog.events.fireListener = function(listener, eventObject) {
    +  var listenerFn = listener.listener;
    +  var listenerHandler = listener.handler || listener.src;
    +
    +  if (listener.callOnce) {
    +    goog.events.unlistenByKey(listener);
    +  }
    +  return listenerFn.call(listenerHandler, eventObject);
    +};
    +
    +
    +/**
    + * Gets the total number of listeners currently in the system.
    + * @return {number} Number of listeners.
    + * @deprecated This returns estimated count, now that Closure no longer
    + * stores a central listener registry. We still return an estimation
    + * to keep existing listener-related tests passing. In the near future,
    + * this function will be removed.
    + */
    +goog.events.getTotalListenerCount = function() {
    +  return goog.events.listenerCountEstimate_;
    +};
    +
    +
    +/**
    + * Dispatches an event (or event like object) and calls all listeners
    + * listening for events of this type. The type of the event is decided by the
    + * type property on the event object.
    + *
    + * If any of the listeners returns false OR calls preventDefault then this
    + * function will return false.  If one of the capture listeners calls
    + * stopPropagation, then the bubble listeners won't fire.
    + *
    + * @param {goog.events.Listenable} src The event target.
    + * @param {goog.events.EventLike} e Event object.
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the handlers returns false) this will also return false.
    + *     If there are no handlers, or if all handlers return true, this returns
    + *     true.
    + */
    +goog.events.dispatchEvent = function(src, e) {
    +  goog.asserts.assert(
    +      goog.events.Listenable.isImplementedBy(src),
    +      'Can not use goog.events.dispatchEvent with ' +
    +      'non-goog.events.Listenable instance.');
    +  return src.dispatchEvent(e);
    +};
    +
    +
    +/**
    + * Installs exception protection for the browser event entry point using the
    + * given error handler.
    + *
    + * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
    + *     protect the entry point.
    + */
    +goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
    +  goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
    +      goog.events.handleBrowserEvent_);
    +};
    +
    +
    +/**
    + * Handles an event and dispatches it to the correct listeners. This
    + * function is a proxy for the real listener the user specified.
    + *
    + * @param {goog.events.Listener} listener The listener object.
    + * @param {Event=} opt_evt Optional event object that gets passed in via the
    + *     native event handlers.
    + * @return {boolean} Result of the event handler.
    + * @this {EventTarget} The object or Element that fired the event.
    + * @private
    + */
    +goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
    +  if (listener.removed) {
    +    return true;
    +  }
    +
    +  // Synthesize event propagation if the browser does not support W3C
    +  // event model.
    +  if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    +    var ieEvent = opt_evt ||
    +        /** @type {Event} */ (goog.getObjectByName('window.event'));
    +    var evt = new goog.events.BrowserEvent(ieEvent, this);
    +    /** @type {boolean} */
    +    var retval = true;
    +
    +    if (goog.events.CAPTURE_SIMULATION_MODE ==
    +            goog.events.CaptureSimulationMode.ON) {
    +      // If we have not marked this event yet, we should perform capture
    +      // simulation.
    +      if (!goog.events.isMarkedIeEvent_(ieEvent)) {
    +        goog.events.markIeEvent_(ieEvent);
    +
    +        var ancestors = [];
    +        for (var parent = evt.currentTarget; parent;
    +             parent = parent.parentNode) {
    +          ancestors.push(parent);
    +        }
    +
    +        // Fire capture listeners.
    +        var type = listener.type;
    +        for (var i = ancestors.length - 1; !evt.propagationStopped_ && i >= 0;
    +             i--) {
    +          evt.currentTarget = ancestors[i];
    +          var result = goog.events.fireListeners_(ancestors[i], type, true, evt);
    +          retval = retval && result;
    +        }
    +
    +        // Fire bubble listeners.
    +        //
    +        // We can technically rely on IE to perform bubble event
    +        // propagation. However, it turns out that IE fires events in
    +        // opposite order of attachEvent registration, which broke
    +        // some code and tests that rely on the order. (While W3C DOM
    +        // Level 2 Events TR leaves the event ordering unspecified,
    +        // modern browsers and W3C DOM Level 3 Events Working Draft
    +        // actually specify the order as the registration order.)
    +        for (var i = 0; !evt.propagationStopped_ && i < ancestors.length; i++) {
    +          evt.currentTarget = ancestors[i];
    +          var result = goog.events.fireListeners_(ancestors[i], type, false, evt);
    +          retval = retval && result;
    +        }
    +      }
    +    } else {
    +      retval = goog.events.fireListener(listener, evt);
    +    }
    +    return retval;
    +  }
    +
    +  // Otherwise, simply fire the listener.
    +  return goog.events.fireListener(
    +      listener, new goog.events.BrowserEvent(opt_evt, this));
    +};
    +
    +
    +/**
    + * This is used to mark the IE event object so we do not do the Closure pass
    + * twice for a bubbling event.
    + * @param {Event} e The IE browser event.
    + * @private
    + */
    +goog.events.markIeEvent_ = function(e) {
    +  // Only the keyCode and the returnValue can be changed. We use keyCode for
    +  // non keyboard events.
    +  // event.returnValue is a bit more tricky. It is undefined by default. A
    +  // boolean false prevents the default action. In a window.onbeforeunload and
    +  // the returnValue is non undefined it will be alerted. However, we will only
    +  // modify the returnValue for keyboard events. We can get a problem if non
    +  // closure events sets the keyCode or the returnValue
    +
    +  var useReturnValue = false;
    +
    +  if (e.keyCode == 0) {
    +    // We cannot change the keyCode in case that srcElement is input[type=file].
    +    // We could test that that is the case but that would allocate 3 objects.
    +    // If we use try/catch we will only allocate extra objects in the case of a
    +    // failure.
    +    /** @preserveTry */
    +    try {
    +      e.keyCode = -1;
    +      return;
    +    } catch (ex) {
    +      useReturnValue = true;
    +    }
    +  }
    +
    +  if (useReturnValue ||
    +      /** @type {boolean|undefined} */ (e.returnValue) == undefined) {
    +    e.returnValue = true;
    +  }
    +};
    +
    +
    +/**
    + * This is used to check if an IE event has already been handled by the Closure
    + * system so we do not do the Closure pass twice for a bubbling event.
    + * @param {Event} e  The IE browser event.
    + * @return {boolean} True if the event object has been marked.
    + * @private
    + */
    +goog.events.isMarkedIeEvent_ = function(e) {
    +  return e.keyCode < 0 || e.returnValue != undefined;
    +};
    +
    +
    +/**
    + * Counter to create unique event ids.
    + * @private {number}
    + */
    +goog.events.uniqueIdCounter_ = 0;
    +
    +
    +/**
    + * Creates a unique event id.
    + *
    + * @param {string} identifier The identifier.
    + * @return {string} A unique identifier.
    + * @idGenerator
    + */
    +goog.events.getUniqueId = function(identifier) {
    +  return identifier + '_' + goog.events.uniqueIdCounter_++;
    +};
    +
    +
    +/**
    + * @param {EventTarget} src The source object.
    + * @return {goog.events.ListenerMap} A listener map for the given
    + *     source object, or null if none exists.
    + * @private
    + */
    +goog.events.getListenerMap_ = function(src) {
    +  var listenerMap = src[goog.events.LISTENER_MAP_PROP_];
    +  // IE serializes the property as well (e.g. when serializing outer
    +  // HTML). So we must check that the value is of the correct type.
    +  return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;
    +};
    +
    +
    +/**
    + * Expando property for listener function wrapper for Object with
    + * handleEvent.
    + * @private @const {string}
    + */
    +goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
    +    ((Math.random() * 1e9) >>> 0);
    +
    +
    +/**
    + * @param {Object|Function} listener The listener function or an
    + *     object that contains handleEvent method.
    + * @return {!Function} Either the original function or a function that
    + *     calls obj.handleEvent. If the same listener is passed to this
    + *     function more than once, the same function is guaranteed to be
    + *     returned.
    + */
    +goog.events.wrapListener = function(listener) {
    +  goog.asserts.assert(listener, 'Listener can not be null.');
    +
    +  if (goog.isFunction(listener)) {
    +    return listener;
    +  }
    +
    +  goog.asserts.assert(
    +      listener.handleEvent, 'An object listener must have handleEvent method.');
    +  if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
    +    listener[goog.events.LISTENER_WRAPPER_PROP_] =
    +        function(e) { return listener.handleEvent(e); };
    +  }
    +  return listener[goog.events.LISTENER_WRAPPER_PROP_];
    +};
    +
    +
    +// Register the browser event handler as an entry point, so that
    +// it can be monitored for exception handling, etc.
    +goog.debug.entryPointRegistry.register(
    +    /**
    +     * @param {function(!Function): !Function} transformer The transforming
    +     *     function.
    +     */
    +    function(transformer) {
    +      goog.events.handleBrowserEvent_ = transformer(
    +          goog.events.handleBrowserEvent_);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/events/events_test.html b/mapbox-gl/closure-library/closure/goog/events/events_test.html
    new file mode 100644
    index 0000000000..a3d8e26fb0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/events_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.eventsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/events_test.js b/mapbox-gl/closure-library/closure/goog/events/events_test.js
    new file mode 100644
    index 0000000000..dde672990a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/events_test.js
    @@ -0,0 +1,745 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.eventsTest');
    +goog.setTestOnly('goog.eventsTest');
    +
    +goog.require('goog.asserts.AssertionError');
    +goog.require('goog.debug.EntryPointMonitor');
    +goog.require('goog.debug.ErrorHandler');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.CaptureSimulationMode');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.Listener');
    +goog.require('goog.functions');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var originalHandleBrowserEvent = goog.events.handleBrowserEvent_;
    +var propertyReplacer;
    +var et1, et2, et3;
    +
    +function setUp() {
    +  et1 = new goog.events.EventTarget();
    +  et2 = new goog.events.EventTarget();
    +  et3 = new goog.events.EventTarget();
    +  propertyReplacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  goog.events.CAPTURE_SIMULATION_MODE =
    +      goog.events.CaptureSimulationMode.ON;
    +  goog.events.handleBrowserEvent_ = originalHandleBrowserEvent;
    +  goog.disposeAll(et1, et2, et3);
    +  goog.events.removeAll(document.body);
    +  propertyReplacer.reset();
    +}
    +
    +function testProtectBrowserEventEntryPoint() {
    +  var errorHandlerFn = goog.testing.recordFunction();
    +  var errorHandler = new goog.debug.ErrorHandler(errorHandlerFn);
    +
    +  goog.events.protectBrowserEventEntryPoint(errorHandler);
    +
    +  var browserEventHandler =
    +      goog.testing.recordFunction(goog.events.handleBrowserEvent_);
    +  goog.events.handleBrowserEvent_ = function() {
    +    try {
    +      browserEventHandler.apply(this, arguments);
    +    } catch (e) {
    +      // Ignored.
    +    }
    +  };
    +
    +  var err = Error('test');
    +  var body = document.body;
    +  goog.events.listen(body, goog.events.EventType.CLICK, function() {
    +    throw err;
    +  });
    +
    +  dispatchClick(body);
    +
    +  assertEquals('Error handler callback should be called.',
    +      1, errorHandlerFn.getCallCount());
    +  assertEquals(err, errorHandlerFn.getLastCall().getArgument(0));
    +
    +  assertEquals(1, browserEventHandler.getCallCount());
    +  var err2 = browserEventHandler.getLastCall().getError();
    +  assertNotNull(err2);
    +  assertTrue(
    +      err2 instanceof goog.debug.ErrorHandler.ProtectedFunctionError);
    +}
    +
    +function testSelfRemove() {
    +  var callback = function() {
    +    // This listener removes itself during event dispatching, so it
    +    // is marked as 'removed' but not actually removed until after event
    +    // dispatching ends.
    +    goog.events.removeAll(et1, 'click');
    +
    +    // Test that goog.events.getListener ignores events marked as 'removed'.
    +    assertNull(goog.events.getListener(et1, 'click', callback));
    +  };
    +  var key = goog.events.listen(et1, 'click', callback);
    +  goog.events.dispatchEvent(et1, 'click');
    +}
    +
    +function testHasListener() {
    +  var div = document.createElement('div');
    +  assertFalse(goog.events.hasListener(div));
    +
    +  var key = goog.events.listen(div, 'click', function() {});
    +  assertTrue(goog.events.hasListener(div));
    +  assertTrue(goog.events.hasListener(div, 'click'));
    +  assertTrue(goog.events.hasListener(div, 'click', false));
    +  assertTrue(goog.events.hasListener(div, undefined, false));
    +
    +  assertFalse(goog.events.hasListener(div, 'click', true));
    +  assertFalse(goog.events.hasListener(div, undefined, true));
    +  assertFalse(goog.events.hasListener(div, 'mouseup'));
    +
    +  // Test that hasListener returns false when all listeners are removed.
    +  goog.events.unlistenByKey(key);
    +  assertFalse(goog.events.hasListener(div));
    +}
    +
    +function testHasListenerWithEventTarget() {
    +  assertFalse(goog.events.hasListener(et1));
    +
    +  function callback() {};
    +  goog.events.listen(et1, 'test', callback, true);
    +  assertTrue(goog.events.hasListener(et1));
    +  assertTrue(goog.events.hasListener(et1, 'test'));
    +  assertTrue(goog.events.hasListener(et1, 'test', true));
    +  assertTrue(goog.events.hasListener(et1, undefined, true));
    +
    +  assertFalse(goog.events.hasListener(et1, 'click'));
    +  assertFalse(goog.events.hasListener(et1, 'test', false));
    +
    +  goog.events.unlisten(et1, 'test', callback, true);
    +  assertFalse(goog.events.hasListener(et1));
    +}
    +
    +function testHasListenerWithMultipleTargets() {
    +  function callback() {};
    +
    +  goog.events.listen(et1, 'test1', callback, true);
    +  goog.events.listen(et2, 'test2', callback, true);
    +
    +  assertTrue(goog.events.hasListener(et1));
    +  assertTrue(goog.events.hasListener(et2));
    +  assertTrue(goog.events.hasListener(et1, 'test1'));
    +  assertTrue(goog.events.hasListener(et2, 'test2'));
    +
    +  assertFalse(goog.events.hasListener(et1, 'et2'));
    +  assertFalse(goog.events.hasListener(et2, 'et1'));
    +
    +  goog.events.removeAll(et1);
    +  goog.events.removeAll(et2);
    +}
    +
    +function testBubbleSingle() {
    +  et1.setParentEventTarget(et2);
    +  et2.setParentEventTarget(et3);
    +
    +  var count = 0;
    +  function callback() {
    +    count++;
    +  }
    +
    +  goog.events.listen(et3, 'test', callback, false);
    +
    +  et1.dispatchEvent('test');
    +
    +  assertEquals(1, count);
    +
    +  goog.events.removeAll(et1);
    +  goog.events.removeAll(et2);
    +  goog.events.removeAll(et3);
    +}
    +
    +function testCaptureSingle() {
    +  et1.setParentEventTarget(et2);
    +  et2.setParentEventTarget(et3);
    +
    +  var count = 0;
    +  function callback() {
    +    count++;
    +  }
    +
    +  goog.events.listen(et3, 'test', callback, true);
    +
    +  et1.dispatchEvent('test');
    +
    +  assertEquals(1, count);
    +
    +  goog.events.removeAll(et1);
    +  goog.events.removeAll(et2);
    +  goog.events.removeAll(et3);
    +}
    +
    +function testCaptureAndBubble() {
    +  et1.setParentEventTarget(et2);
    +  et2.setParentEventTarget(et3);
    +
    +  var count = 0;
    +  function callbackCapture1() {
    +    count++;
    +    assertEquals(3, count);
    +  }
    +  function callbackBubble1() {
    +    count++;
    +    assertEquals(4, count);
    +  }
    +
    +  function callbackCapture2() {
    +    count++;
    +    assertEquals(2, count);
    +  }
    +  function callbackBubble2() {
    +    count++;
    +    assertEquals(5, count);
    +  }
    +
    +  function callbackCapture3() {
    +    count++;
    +    assertEquals(1, count);
    +  }
    +  function callbackBubble3() {
    +    count++;
    +    assertEquals(6, count);
    +  }
    +
    +  goog.events.listen(et1, 'test', callbackCapture1, true);
    +  goog.events.listen(et1, 'test', callbackBubble1, false);
    +  goog.events.listen(et2, 'test', callbackCapture2, true);
    +  goog.events.listen(et2, 'test', callbackBubble2, false);
    +  goog.events.listen(et3, 'test', callbackCapture3, true);
    +  goog.events.listen(et3, 'test', callbackBubble3, false);
    +
    +  et1.dispatchEvent('test');
    +
    +  assertEquals(6, count);
    +
    +  goog.events.removeAll(et1);
    +  goog.events.removeAll(et2);
    +  goog.events.removeAll(et3);
    +}
    +
    +function testCapturingRemovesBubblingListener() {
    +  var bubbleCount = 0;
    +  function callbackBubble() {
    +    bubbleCount++;
    +  }
    +
    +  var captureCount = 0;
    +  function callbackCapture() {
    +    captureCount++;
    +    goog.events.removeAll(et1);
    +  }
    +
    +  goog.events.listen(et1, 'test', callbackCapture, true);
    +  goog.events.listen(et1, 'test', callbackBubble, false);
    +
    +  et1.dispatchEvent('test');
    +  assertEquals(1, captureCount);
    +  assertEquals(0, bubbleCount);
    +}
    +
    +function dispatchClick(target) {
    +  if (target.click) {
    +    target.click();
    +  } else {
    +    var e = document.createEvent('MouseEvents');
    +    e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false,
    +        false, false, false, 0, null);
    +    target.dispatchEvent(e);
    +  }
    +}
    +
    +function testHandleBrowserEventBubblingListener() {
    +  var count = 0;
    +  var body = document.body;
    +  goog.events.listen(body, 'click', function() {
    +    count++;
    +  });
    +  dispatchClick(body);
    +  assertEquals(1, count);
    +}
    +
    +function testHandleBrowserEventCapturingListener() {
    +  var count = 0;
    +  var body = document.body;
    +  goog.events.listen(body, 'click', function() {
    +    count++;
    +  }, true);
    +  dispatchClick(body);
    +  assertEquals(1, count);
    +}
    +
    +function testHandleBrowserEventCapturingAndBubblingListener() {
    +  var count = 1;
    +  var body = document.body;
    +  goog.events.listen(body, 'click', function() {
    +    count += 3;
    +  }, true);
    +  goog.events.listen(body, 'click', function() {
    +    count *= 5;
    +  }, false);
    +  dispatchClick(body);
    +  assertEquals(20, count);
    +}
    +
    +function testHandleBrowserEventCapturingRemovesBubblingListener() {
    +  var body = document.body;
    +
    +  var bubbleCount = 0;
    +  function callbackBubble() {
    +    bubbleCount++;
    +  }
    +
    +  var captureCount = 0;
    +  function callbackCapture() {
    +    captureCount++;
    +    goog.events.removeAll(body);
    +  }
    +
    +  goog.events.listen(body, 'click', callbackCapture, true);
    +  goog.events.listen(body, 'click', callbackBubble, false);
    +
    +  dispatchClick(body);
    +  assertEquals(1, captureCount);
    +  assertEquals(0, bubbleCount);
    +}
    +
    +function testHandleEventPropagationOnParentElement() {
    +  var count = 1;
    +  goog.events.listen(document.documentElement, 'click', function() {
    +    count += 3;
    +  }, true);
    +  goog.events.listen(document.documentElement, 'click', function() {
    +    count *= 5;
    +  }, false);
    +  dispatchClick(document.body);
    +  assertEquals(20, count);
    +}
    +
    +function testEntryPointRegistry() {
    +  var monitor = new goog.debug.EntryPointMonitor();
    +  var replacement = function() {};
    +  monitor.wrap = goog.testing.recordFunction(
    +      goog.functions.constant(replacement));
    +
    +  goog.debug.entryPointRegistry.monitorAll(monitor);
    +  assertTrue(monitor.wrap.getCallCount() >= 1);
    +  assertEquals(replacement, goog.events.handleBrowserEvent_);
    +}
    +
    +// Fixes bug http://b/6434926
    +function testListenOnceHandlerDispatchCausingInfiniteLoop() {
    +  var handleFoo = goog.testing.recordFunction(function() {
    +    et1.dispatchEvent('foo');
    +  });
    +
    +  goog.events.listenOnce(et1, 'foo', handleFoo);
    +
    +  et1.dispatchEvent('foo');
    +
    +  assertEquals('Handler should be called only once.',
    +               1, handleFoo.getCallCount());
    +}
    +
    +function testCreationStack() {
    +  if (!new Error().stack)
    +    return;
    +  propertyReplacer.replace(goog.events.Listener, 'ENABLE_MONITORING', true);
    +
    +  var div = document.createElement('div');
    +  var key = goog.events.listen(
    +      div, goog.events.EventType.CLICK, goog.nullFunction);
    +  var listenerStack = key.creationStack;
    +
    +  // Check that the name of this test function occurs in the stack trace.
    +  assertContains('testCreationStack', listenerStack);
    +  goog.events.unlistenByKey(key);
    +}
    +
    +function testListenOnceAfterListenDoesNotChangeExistingListener() {
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listen(document.body, 'click', listener);
    +  goog.events.listenOnce(document.body, 'click', listener);
    +
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +
    +  assertEquals(3, listener.getCallCount());
    +}
    +
    +function testListenOnceAfterListenOnceDoesNotChangeExistingListener() {
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.listenOnce(document.body, 'click', listener);
    +
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +
    +  assertEquals(1, listener.getCallCount());
    +}
    +
    +function testListenAfterListenOnceRemoveOnceness() {
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.listen(document.body, 'click', listener);
    +
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +  dispatchClick(document.body);
    +
    +  assertEquals(3, listener.getCallCount());
    +}
    +
    +function testUnlistenAfterListenOnce() {
    +  var listener = goog.testing.recordFunction();
    +
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.unlisten(document.body, 'click', listener);
    +  dispatchClick(document.body);
    +
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.listen(document.body, 'click', listener);
    +  goog.events.unlisten(document.body, 'click', listener);
    +  dispatchClick(document.body);
    +
    +  goog.events.listen(document.body, 'click', listener);
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.unlisten(document.body, 'click', listener);
    +  dispatchClick(document.body);
    +
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.listenOnce(document.body, 'click', listener);
    +  goog.events.unlisten(document.body, 'click', listener);
    +  dispatchClick(document.body);
    +
    +  assertEquals(0, listener.getCallCount());
    +}
    +
    +function testEventBubblingWithReentrantDispatch_bubbling() {
    +  runEventPropogationWithReentrantDispatch(false);
    +}
    +
    +function testEventBubblingWithReentrantDispatch_capture() {
    +  runEventPropogationWithReentrantDispatch(true);
    +}
    +
    +function runEventPropogationWithReentrantDispatch(useCapture) {
    +  var eventType = 'test-event-type';
    +
    +  var child = et1;
    +  var parent = et2;
    +  child.setParentEventTarget(parent);
    +
    +  var firstTarget = useCapture ? parent : child;
    +  var secondTarget = useCapture ? child : parent;
    +
    +  var firstListener = function(evt) {
    +    if (evt.isFirstEvent) {
    +      // Fires another event of the same type the first time it is invoked.
    +      child.dispatchEvent(new goog.events.Event(eventType));
    +    }
    +  };
    +  goog.events.listen(firstTarget, eventType, firstListener, useCapture);
    +
    +  var secondListener = goog.testing.recordFunction();
    +  goog.events.listen(secondTarget, eventType, secondListener, useCapture);
    +
    +  // Fire the first event.
    +  var firstEvent = new goog.events.Event(eventType);
    +  firstEvent.isFirstEvent = true;
    +  child.dispatchEvent(firstEvent);
    +
    +  assertEquals(2, secondListener.getCallCount());
    +}
    +
    +function testEventPropogationWhenListenerRemoved_bubbling() {
    +  runEventPropogationWhenListenerRemoved(false);
    +}
    +
    +function testEventPropogationWhenListenerRemoved_capture() {
    +  runEventPropogationWhenListenerRemoved(true);
    +}
    +
    +function runEventPropogationWhenListenerRemoved(useCapture) {
    +  var eventType = 'test-event-type';
    +
    +  var child = et1;
    +  var parent = et2;
    +  child.setParentEventTarget(parent);
    +
    +  var firstTarget = useCapture ? parent : child;
    +  var secondTarget = useCapture ? child : parent;
    +
    +  var firstListener = goog.testing.recordFunction();
    +  var secondListener = goog.testing.recordFunction();
    +  goog.events.listenOnce(firstTarget, eventType, firstListener, useCapture);
    +  goog.events.listen(secondTarget, eventType, secondListener, useCapture);
    +
    +  child.dispatchEvent(new goog.events.Event(eventType));
    +
    +  assertEquals(1, secondListener.getCallCount());
    +}
    +
    +function testEventPropogationWhenListenerAdded_bubbling() {
    +  runEventPropogationWhenListenerAdded(false);
    +}
    +
    +function testEventPropogationWhenListenerAdded_capture() {
    +  runEventPropogationWhenListenerAdded(true);
    +}
    +
    +function runEventPropogationWhenListenerAdded(useCapture) {
    +  var eventType = 'test-event-type';
    +
    +  var child = et1;
    +  var parent = et2;
    +  child.setParentEventTarget(parent);
    +
    +  var firstTarget = useCapture ? parent : child;
    +  var secondTarget = useCapture ? child : parent;
    +
    +  var firstListener = function() {
    +    goog.events.listen(secondTarget, eventType, secondListener, useCapture);
    +  };
    +  var secondListener = goog.testing.recordFunction();
    +  goog.events.listen(firstTarget, eventType, firstListener, useCapture);
    +
    +  child.dispatchEvent(new goog.events.Event(eventType));
    +
    +  assertEquals(1, secondListener.getCallCount());
    +}
    +
    +function testEventPropogationWhenListenerAddedAndRemoved_bubbling() {
    +  runEventPropogationWhenListenerAddedAndRemoved(false);
    +}
    +
    +function testEventPropogationWhenListenerAddedAndRemoved_capture() {
    +  runEventPropogationWhenListenerAddedAndRemoved(true);
    +}
    +
    +function runEventPropogationWhenListenerAddedAndRemoved(useCapture) {
    +  var eventType = 'test-event-type';
    +
    +  var child = et1;
    +  var parent = et2;
    +  child.setParentEventTarget(parent);
    +
    +  var firstTarget = useCapture ? parent : child;
    +  var secondTarget = useCapture ? child : parent;
    +
    +  var firstListener = function() {
    +    goog.events.listen(secondTarget, eventType, secondListener, useCapture);
    +  };
    +  var secondListener = goog.testing.recordFunction();
    +  goog.events.listenOnce(firstTarget, eventType, firstListener, useCapture);
    +
    +  child.dispatchEvent(new goog.events.Event(eventType));
    +
    +  assertEquals(1, secondListener.getCallCount());
    +}
    +
    +function testAssertWhenUsedWithUninitializedCustomEventTarget() {
    +  var SubClass = function() { /* does not call superclass ctor */ };
    +  goog.inherits(SubClass, goog.events.EventTarget);
    +
    +  var instance = new SubClass();
    +
    +  var e;
    +  e = assertThrows(function() {
    +    goog.events.listen(instance, 'test1', function() {});
    +  });
    +  assertTrue(e instanceof goog.asserts.AssertionError);
    +  e = assertThrows(function() {
    +    goog.events.dispatchEvent(instance, 'test1');
    +  });
    +  assertTrue(e instanceof goog.asserts.AssertionError);
    +  e = assertThrows(function() {
    +    instance.dispatchEvent('test1');
    +  });
    +  assertTrue(e instanceof goog.asserts.AssertionError);
    +}
    +
    +function testAssertWhenDispatchEventIsUsedWithNonCustomEventTarget() {
    +  var obj = {};
    +  e = assertThrows(function() {
    +    goog.events.dispatchEvent(obj, 'test1');
    +  });
    +  assertTrue(e instanceof goog.asserts.AssertionError);
    +}
    +
    +
    +function testPropagationStoppedDuringCapture() {
    +  var captureHandler = goog.testing.recordFunction(function(e) {
    +    e.stopPropagation();
    +  });
    +  var bubbleHandler = goog.testing.recordFunction();
    +
    +  var body = document.body;
    +  var div = document.createElement('div');
    +  body.appendChild(div);
    +  try {
    +    goog.events.listen(body, 'click', captureHandler, true);
    +    goog.events.listen(div, 'click', bubbleHandler, false);
    +    goog.events.listen(body, 'click', bubbleHandler, false);
    +
    +    dispatchClick(div);
    +    assertEquals(1, captureHandler.getCallCount());
    +    assertEquals(0, bubbleHandler.getCallCount());
    +
    +    goog.events.unlisten(body, 'click', captureHandler, true);
    +
    +    dispatchClick(div);
    +    assertEquals(2, bubbleHandler.getCallCount());
    +  } finally {
    +    goog.dom.removeNode(div);
    +    goog.events.removeAll(body);
    +    goog.events.removeAll(div);
    +  }
    +}
    +
    +function testPropagationStoppedDuringBubble() {
    +  var captureHandler = goog.testing.recordFunction();
    +  var bubbleHandler1 = goog.testing.recordFunction(function(e) {
    +    e.stopPropagation();
    +  });
    +  var bubbleHandler2 = goog.testing.recordFunction();
    +
    +  var body = document.body;
    +  var div = document.createElement('div');
    +  body.appendChild(div);
    +  try {
    +    goog.events.listen(body, 'click', captureHandler, true);
    +    goog.events.listen(div, 'click', bubbleHandler1, false);
    +    goog.events.listen(body, 'click', bubbleHandler2, false);
    +
    +    dispatchClick(div);
    +    assertEquals(1, captureHandler.getCallCount());
    +    assertEquals(1, bubbleHandler1.getCallCount());
    +    assertEquals(0, bubbleHandler2.getCallCount());
    +  } finally {
    +    goog.dom.removeNode(div);
    +    goog.events.removeAll(body);
    +    goog.events.removeAll(div);
    +  }
    +}
    +
    +function testAddingCaptureListenerDuringBubbleShouldNotFireTheListener() {
    +  var body = document.body;
    +  var div = document.createElement('div');
    +  body.appendChild(div);
    +
    +  var captureHandler1 = goog.testing.recordFunction();
    +  var captureHandler2 = goog.testing.recordFunction();
    +  var bubbleHandler = goog.testing.recordFunction(function(e) {
    +    goog.events.listen(body, 'click', captureHandler1, true);
    +    goog.events.listen(div, 'click', captureHandler2, true);
    +  });
    +
    +  try {
    +    goog.events.listen(div, 'click', bubbleHandler, false);
    +
    +    dispatchClick(div);
    +
    +    // These verify that the capture handlers registered in the bubble
    +    // handler is not invoked in the same event propagation phase.
    +    assertEquals(0, captureHandler1.getCallCount());
    +    assertEquals(0, captureHandler2.getCallCount());
    +    assertEquals(1, bubbleHandler.getCallCount());
    +  } finally {
    +    goog.dom.removeNode(div);
    +    goog.events.removeAll(body);
    +    goog.events.removeAll(div);
    +  }
    +}
    +
    +function testRemovingCaptureListenerDuringBubbleWouldNotFireListenerTwice() {
    +  var body = document.body;
    +  var div = document.createElement('div');
    +  body.appendChild(div);
    +
    +  var captureHandler = goog.testing.recordFunction();
    +  var bubbleHandler1 = goog.testing.recordFunction(function(e) {
    +    goog.events.unlisten(body, 'click', captureHandler, true);
    +  });
    +  var bubbleHandler2 = goog.testing.recordFunction();
    +
    +  try {
    +    goog.events.listen(body, 'click', captureHandler, true);
    +    goog.events.listen(div, 'click', bubbleHandler1, false);
    +    goog.events.listen(body, 'click', bubbleHandler2, false);
    +
    +    dispatchClick(div);
    +    assertEquals(1, captureHandler.getCallCount());
    +
    +    // Verify that neither of these handlers are called more than once.
    +    assertEquals(1, bubbleHandler1.getCallCount());
    +    assertEquals(1, bubbleHandler2.getCallCount());
    +  } finally {
    +    goog.dom.removeNode(div);
    +    goog.events.removeAll(body);
    +    goog.events.removeAll(div);
    +  }
    +}
    +
    +function testCaptureSimulationModeOffAndFail() {
    +  goog.events.CAPTURE_SIMULATION_MODE =
    +      goog.events.CaptureSimulationMode.OFF_AND_FAIL;
    +  var captureHandler = goog.testing.recordFunction();
    +
    +  if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    +    var err = assertThrows(function() {
    +      goog.events.listen(document.body, 'click', captureHandler, true);
    +    });
    +    assertTrue(err instanceof goog.asserts.AssertionError);
    +
    +    // Sanity tests.
    +    dispatchClick(document.body);
    +    assertEquals(0, captureHandler.getCallCount());
    +  } else {
    +    goog.events.listen(document.body, 'click', captureHandler, true);
    +    dispatchClick(document.body);
    +    assertEquals(1, captureHandler.getCallCount());
    +  }
    +}
    +
    +function testCaptureSimulationModeOffAndSilent() {
    +  goog.events.CAPTURE_SIMULATION_MODE =
    +      goog.events.CaptureSimulationMode.OFF_AND_SILENT;
    +  var captureHandler = goog.testing.recordFunction();
    +
    +  goog.events.listen(document.body, 'click', captureHandler, true);
    +  if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
    +    dispatchClick(document.body);
    +    assertEquals(0, captureHandler.getCallCount());
    +  } else {
    +    dispatchClick(document.body);
    +    assertEquals(1, captureHandler.getCallCount());
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget.js b/mapbox-gl/closure-library/closure/goog/events/eventtarget.js
    new file mode 100644
    index 0000000000..7408c7e096
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget.js
    @@ -0,0 +1,394 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A disposable implementation of a custom
    + * listenable/event target. See also: documentation for
    + * {@code goog.events.Listenable}.
    + *
    + * @author arv@google.com (Erik Arvidsson) [Original implementation]
    + * @see ../demos/eventtarget.html
    + * @see goog.events.Listenable
    + */
    +
    +goog.provide('goog.events.EventTarget');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.Listenable');
    +goog.require('goog.events.ListenerMap');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * An implementation of {@code goog.events.Listenable} with full W3C
    + * EventTarget-like support (capture/bubble mechanism, stopping event
    + * propagation, preventing default actions).
    + *
    + * You may subclass this class to turn your class into a Listenable.
    + *
    + * Unless propagation is stopped, an event dispatched by an
    + * EventTarget will bubble to the parent returned by
    + * {@code getParentEventTarget}. To set the parent, call
    + * {@code setParentEventTarget}. Subclasses that don't support
    + * changing the parent can override the setter to throw an error.
    + *
    + * Example usage:
    + * <pre>
    + *   var source = new goog.events.EventTarget();
    + *   function handleEvent(e) {
    + *     alert('Type: ' + e.type + '; Target: ' + e.target);
    + *   }
    + *   source.listen('foo', handleEvent);
    + *   // Or: goog.events.listen(source, 'foo', handleEvent);
    + *   ...
    + *   source.dispatchEvent('foo');  // will call handleEvent
    + *   ...
    + *   source.unlisten('foo', handleEvent);
    + *   // Or: goog.events.unlisten(source, 'foo', handleEvent);
    + * </pre>
    + *
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.events.Listenable}
    + */
    +goog.events.EventTarget = function() {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Maps of event type to an array of listeners.
    +   * @private {!goog.events.ListenerMap}
    +   */
    +  this.eventTargetListeners_ = new goog.events.ListenerMap(this);
    +
    +  /**
    +   * The object to use for event.target. Useful when mixing in an
    +   * EventTarget to another object.
    +   * @private {!Object}
    +   */
    +  this.actualEventTarget_ = this;
    +
    +  /**
    +   * Parent event target, used during event bubbling.
    +   *
    +   * TODO(chrishenry): Change this to goog.events.Listenable. This
    +   * currently breaks people who expect getParentEventTarget to return
    +   * goog.events.EventTarget.
    +   *
    +   * @private {goog.events.EventTarget}
    +   */
    +  this.parentEventTarget_ = null;
    +};
    +goog.inherits(goog.events.EventTarget, goog.Disposable);
    +goog.events.Listenable.addImplementation(goog.events.EventTarget);
    +
    +
    +/**
    + * An artificial cap on the number of ancestors you can have. This is mainly
    + * for loop detection.
    + * @const {number}
    + * @private
    + */
    +goog.events.EventTarget.MAX_ANCESTORS_ = 1000;
    +
    +
    +/**
    + * Returns the parent of this event target to use for bubbling.
    + *
    + * @return {goog.events.EventTarget} The parent EventTarget or null if
    + *     there is no parent.
    + * @override
    + */
    +goog.events.EventTarget.prototype.getParentEventTarget = function() {
    +  return this.parentEventTarget_;
    +};
    +
    +
    +/**
    + * Sets the parent of this event target to use for capture/bubble
    + * mechanism.
    + * @param {goog.events.EventTarget} parent Parent listenable (null if none).
    + */
    +goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
    +  this.parentEventTarget_ = parent;
    +};
    +
    +
    +/**
    + * Adds an event listener to the event target. The same handler can only be
    + * added once per the type. Even if you add the same handler multiple times
    + * using the same type then it will only be called once when the event is
    + * dispatched.
    + *
    + * @param {string} type The type of the event to listen for.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
    + *     to handle the event. The handler can also be an object that implements
    + *     the handleEvent method which takes the event object as argument.
    + * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
    + *     whether the listener is fired during the capture or bubble phase
    + *     of the event.
    + * @param {Object=} opt_handlerScope Object in whose scope to call
    + *     the listener.
    + * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
    + *     {@code goog.events.listen} if you are passing Object
    + *     (instead of Function) as handler.
    + */
    +goog.events.EventTarget.prototype.addEventListener = function(
    +    type, handler, opt_capture, opt_handlerScope) {
    +  goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
    +};
    +
    +
    +/**
    + * Removes an event listener from the event target. The handler must be the
    + * same object as the one added. If the handler has not been added then
    + * nothing is done.
    + *
    + * @param {string} type The type of the event to listen for.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
    + *     to handle the event. The handler can also be an object that implements
    + *     the handleEvent method which takes the event object as argument.
    + * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
    + *     whether the listener is fired during the capture or bubble phase
    + *     of the event.
    + * @param {Object=} opt_handlerScope Object in whose scope to call
    + *     the listener.
    + * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
    + *     {@code goog.events.unlisten} if you are passing Object
    + *     (instead of Function) as handler.
    + */
    +goog.events.EventTarget.prototype.removeEventListener = function(
    +    type, handler, opt_capture, opt_handlerScope) {
    +  goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.dispatchEvent = function(e) {
    +  this.assertInitialized_();
    +
    +  var ancestorsTree, ancestor = this.getParentEventTarget();
    +  if (ancestor) {
    +    ancestorsTree = [];
    +    var ancestorCount = 1;
    +    for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
    +      ancestorsTree.push(ancestor);
    +      goog.asserts.assert(
    +          (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
    +          'infinite loop');
    +    }
    +  }
    +
    +  return goog.events.EventTarget.dispatchEventInternal_(
    +      this.actualEventTarget_, e, ancestorsTree);
    +};
    +
    +
    +/**
    + * Removes listeners from this object.  Classes that extend EventTarget may
    + * need to override this method in order to remove references to DOM Elements
    + * and additional listeners.
    + * @override
    + */
    +goog.events.EventTarget.prototype.disposeInternal = function() {
    +  goog.events.EventTarget.superClass_.disposeInternal.call(this);
    +
    +  this.removeAllListeners();
    +  this.parentEventTarget_ = null;
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.listen = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  this.assertInitialized_();
    +  return this.eventTargetListeners_.add(
    +      String(type), listener, false /* callOnce */, opt_useCapture,
    +      opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.listenOnce = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  return this.eventTargetListeners_.add(
    +      String(type), listener, true /* callOnce */, opt_useCapture,
    +      opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.unlisten = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  return this.eventTargetListeners_.remove(
    +      String(type), listener, opt_useCapture, opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.unlistenByKey = function(key) {
    +  return this.eventTargetListeners_.removeByKey(key);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
    +  // TODO(chrishenry): Previously, removeAllListeners can be called on
    +  // uninitialized EventTarget, so we preserve that behavior. We
    +  // should remove this when usages that rely on that fact are purged.
    +  if (!this.eventTargetListeners_) {
    +    return 0;
    +  }
    +  return this.eventTargetListeners_.removeAll(opt_type);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.fireListeners = function(
    +    type, capture, eventObject) {
    +  // TODO(chrishenry): Original code avoids array creation when there
    +  // is no listener, so we do the same. If this optimization turns
    +  // out to be not required, we can replace this with
    +  // getListeners(type, capture) instead, which is simpler.
    +  var listenerArray = this.eventTargetListeners_.listeners[String(type)];
    +  if (!listenerArray) {
    +    return true;
    +  }
    +  listenerArray = listenerArray.concat();
    +
    +  var rv = true;
    +  for (var i = 0; i < listenerArray.length; ++i) {
    +    var listener = listenerArray[i];
    +    // We might not have a listener if the listener was removed.
    +    if (listener && !listener.removed && listener.capture == capture) {
    +      var listenerFn = listener.listener;
    +      var listenerHandler = listener.handler || listener.src;
    +
    +      if (listener.callOnce) {
    +        this.unlistenByKey(listener);
    +      }
    +      rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
    +    }
    +  }
    +
    +  return rv && eventObject.returnValue_ != false;
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.getListeners = function(type, capture) {
    +  return this.eventTargetListeners_.getListeners(String(type), capture);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.getListener = function(
    +    type, listener, capture, opt_listenerScope) {
    +  return this.eventTargetListeners_.getListener(
    +      String(type), listener, capture, opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.events.EventTarget.prototype.hasListener = function(
    +    opt_type, opt_capture) {
    +  var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
    +  return this.eventTargetListeners_.hasListener(id, opt_capture);
    +};
    +
    +
    +/**
    + * Sets the target to be used for {@code event.target} when firing
    + * event. Mainly used for testing. For example, see
    + * {@code goog.testing.events.mixinListenable}.
    + * @param {!Object} target The target.
    + */
    +goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
    +  this.actualEventTarget_ = target;
    +};
    +
    +
    +/**
    + * Asserts that the event target instance is initialized properly.
    + * @private
    + */
    +goog.events.EventTarget.prototype.assertInitialized_ = function() {
    +  goog.asserts.assert(
    +      this.eventTargetListeners_,
    +      'Event target is not initialized. Did you call the superclass ' +
    +      '(goog.events.EventTarget) constructor?');
    +};
    +
    +
    +/**
    + * Dispatches the given event on the ancestorsTree.
    + *
    + * @param {!Object} target The target to dispatch on.
    + * @param {goog.events.Event|Object|string} e The event object.
    + * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
    + *     tree of the target, in reverse order from the closest ancestor
    + *     to the root event target. May be null if the target has no ancestor.
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the listeners returns false) this will also return false.
    + * @private
    + */
    +goog.events.EventTarget.dispatchEventInternal_ = function(
    +    target, e, opt_ancestorsTree) {
    +  var type = e.type || /** @type {string} */ (e);
    +
    +  // If accepting a string or object, create a custom event object so that
    +  // preventDefault and stopPropagation work with the event.
    +  if (goog.isString(e)) {
    +    e = new goog.events.Event(e, target);
    +  } else if (!(e instanceof goog.events.Event)) {
    +    var oldEvent = e;
    +    e = new goog.events.Event(type, target);
    +    goog.object.extend(e, oldEvent);
    +  } else {
    +    e.target = e.target || target;
    +  }
    +
    +  var rv = true, currentTarget;
    +
    +  // Executes all capture listeners on the ancestors, if any.
    +  if (opt_ancestorsTree) {
    +    for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
    +         i--) {
    +      currentTarget = e.currentTarget = opt_ancestorsTree[i];
    +      rv = currentTarget.fireListeners(type, true, e) && rv;
    +    }
    +  }
    +
    +  // Executes capture and bubble listeners on the target.
    +  if (!e.propagationStopped_) {
    +    currentTarget = e.currentTarget = target;
    +    rv = currentTarget.fireListeners(type, true, e) && rv;
    +    if (!e.propagationStopped_) {
    +      rv = currentTarget.fireListeners(type, false, e) && rv;
    +    }
    +  }
    +
    +  // Executes all bubble listeners on the ancestors, if any.
    +  if (opt_ancestorsTree) {
    +    for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
    +      currentTarget = e.currentTarget = opt_ancestorsTree[i];
    +      rv = currentTarget.fireListeners(type, false, e) && rv;
    +    }
    +  }
    +
    +  return rv;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.html b/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.html
    new file mode 100644
    index 0000000000..190951ce9b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.EventTarget
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.EventTargetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.js b/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.js
    new file mode 100644
    index 0000000000..bdb0bda285
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_test.js
    @@ -0,0 +1,72 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventTargetTest');
    +goog.setTestOnly('goog.events.EventTargetTest');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.Listenable');
    +goog.require('goog.events.eventTargetTester');
    +goog.require('goog.events.eventTargetTester.KeyType');
    +goog.require('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  var newListenableFn = function() {
    +    return new goog.events.EventTarget();
    +  };
    +  var listenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.listen(type, listener, opt_capt, opt_handler);
    +  };
    +  var unlistenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.unlisten(type, listener, opt_capt, opt_handler);
    +  };
    +  var unlistenByKeyFn = function(src, key) {
    +    return src.unlistenByKey(key);
    +  };
    +  var listenOnceFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.listenOnce(type, listener, opt_capt, opt_handler);
    +  };
    +  var dispatchEventFn = function(src, e) {
    +    return src.dispatchEvent(e);
    +  };
    +  var removeAllFn = function(src, opt_type, opt_capture) {
    +    return src.removeAllListeners(opt_type, opt_capture);
    +  };
    +  var getListenersFn = function(src, type, capture) {
    +    return src.getListeners(type, capture);
    +  };
    +  var getListenerFn = function(src, type, listener, capture, opt_handler) {
    +    return src.getListener(type, listener, capture, opt_handler);
    +  };
    +  var hasListenerFn = function(src, opt_type, opt_capture) {
    +    return src.hasListener(opt_type, opt_capture);
    +  };
    +
    +  goog.events.eventTargetTester.setUp(
    +      newListenableFn, listenFn, unlistenFn, unlistenByKeyFn,
    +      listenOnceFn, dispatchEventFn,
    +      removeAllFn, getListenersFn, getListenerFn, hasListenerFn,
    +      goog.events.eventTargetTester.KeyType.NUMBER,
    +      goog.events.eventTargetTester.UnlistenReturnType.BOOLEAN, false);
    +}
    +
    +function tearDown() {
    +  goog.events.eventTargetTester.tearDown();
    +}
    +
    +function testRuntimeTypeIsCorrect() {
    +  var target = new goog.events.EventTarget();
    +  assertTrue(goog.events.Listenable.isImplementedBy(target));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.html b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.html
    new file mode 100644
    index 0000000000..c47655ff09
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.EventTarget
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.EventTargetGoogEventsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.js b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.js
    new file mode 100644
    index 0000000000..dccad97641
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_googevents_test.js
    @@ -0,0 +1,77 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventTargetGoogEventsTest');
    +goog.setTestOnly('goog.events.EventTargetGoogEventsTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.eventTargetTester');
    +goog.require('goog.events.eventTargetTester.KeyType');
    +goog.require('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.require('goog.testing');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  var newListenableFn = function() {
    +    return new goog.events.EventTarget();
    +  };
    +  var unlistenByKeyFn = function(src, key) {
    +    return goog.events.unlistenByKey(key);
    +  };
    +  goog.events.eventTargetTester.setUp(
    +      newListenableFn, goog.events.listen, goog.events.unlisten,
    +      unlistenByKeyFn, goog.events.listenOnce, goog.events.dispatchEvent,
    +      goog.events.removeAll, goog.events.getListeners,
    +      goog.events.getListener, goog.events.hasListener,
    +      goog.events.eventTargetTester.KeyType.NUMBER,
    +      goog.events.eventTargetTester.UnlistenReturnType.BOOLEAN, true);
    +}
    +
    +function tearDown() {
    +  goog.events.eventTargetTester.tearDown();
    +}
    +
    +function testUnlistenProperCleanup() {
    +  goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.events.unlisten(eventTargets[0], EventType.A, listeners[0]);
    +
    +  goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  eventTargets[0].unlisten(EventType.A, listeners[0]);
    +}
    +
    +function testUnlistenByKeyProperCleanup() {
    +  var keyNum = goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.events.unlistenByKey(keyNum);
    +}
    +
    +function testListenOnceProperCleanup() {
    +  goog.events.listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  eventTargets[0].dispatchEvent(EventType.A);
    +}
    +
    +function testListenWithObject() {
    +  var obj = {};
    +  obj.handleEvent = goog.testing.recordFunction();
    +  goog.events.listen(eventTargets[0], EventType.A, obj);
    +  eventTargets[0].dispatchEvent(EventType.A);
    +  assertEquals(1, obj.handleEvent.getCallCount());
    +}
    +
    +function testListenWithObjectHandleEventReturningFalse() {
    +  var obj = {};
    +  obj.handleEvent = function() { return false; };
    +  goog.events.listen(eventTargets[0], EventType.A, obj);
    +  assertFalse(eventTargets[0].dispatchEvent(EventType.A));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.html b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.html
    new file mode 100644
    index 0000000000..c908950ad3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.EventTarget
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.EventTargetW3CTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.js b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.js
    new file mode 100644
    index 0000000000..3778b8653c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtarget_via_w3cinterface_test.js
    @@ -0,0 +1,49 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.EventTargetW3CTest');
    +goog.setTestOnly('goog.events.EventTargetW3CTest');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.eventTargetTester');
    +goog.require('goog.events.eventTargetTester.KeyType');
    +goog.require('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  var newListenableFn = function() {
    +    return new goog.events.EventTarget();
    +  };
    +  var listenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    src.addEventListener(type, listener, opt_capt, opt_handler);
    +  };
    +  var unlistenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    src.removeEventListener(type, listener, opt_capt, opt_handler);
    +  };
    +  var dispatchEventFn = function(src, e) {
    +    return src.dispatchEvent(e);
    +  };
    +
    +  goog.events.eventTargetTester.setUp(
    +      newListenableFn, listenFn, unlistenFn, null /* unlistenByKeyFn */,
    +      null /* listenOnceFn */, dispatchEventFn, null /* removeAllFn */,
    +      null /* getListenersFn */, null /* getListenerFn */,
    +      null /* hasListenerFn */,
    +      goog.events.eventTargetTester.KeyType.UNDEFINED,
    +      goog.events.eventTargetTester.UnlistenReturnType.UNDEFINED, true);
    +}
    +
    +function tearDown() {
    +  goog.events.eventTargetTester.tearDown();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtargettester.js b/mapbox-gl/closure-library/closure/goog/events/eventtargettester.js
    new file mode 100644
    index 0000000000..f5bd41bffa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtargettester.js
    @@ -0,0 +1,1063 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview goog.events.EventTarget tester.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.events.eventTargetTester');
    +goog.setTestOnly('goog.events.eventTargetTester');
    +goog.provide('goog.events.eventTargetTester.KeyType');
    +goog.setTestOnly('goog.events.eventTargetTester.KeyType');
    +goog.provide('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.setTestOnly('goog.events.eventTargetTester.UnlistenReturnType');
    +
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.recordFunction');
    +
    +
    +/**
    + * Setup step for the test functions. This needs to be called from the
    + * test setUp.
    + * @param {function():!goog.events.Listenable} listenableFactoryFn Function
    + *     that will return a new Listenable instance each time it is called.
    + * @param {Function} listenFn Function that, given the same signature
    + *     as goog.events.listen, will add listener to the given event
    + *     target.
    + * @param {Function} unlistenFn Function that, given the same
    + *     signature as goog.events.unlisten, will remove listener from
    + *     the given event target.
    + * @param {Function} unlistenByKeyFn Function that, given 2
    + *     parameters: src and key, will remove the corresponding
    + *     listener.
    + * @param {Function} listenOnceFn Function that, given the same
    + *     signature as goog.events.listenOnce, will add a one-time
    + *     listener to the given event target.
    + * @param {Function} dispatchEventFn Function that, given the same
    + *     signature as goog.events.dispatchEvent, will dispatch the event
    + *     on the given event target.
    + * @param {Function} removeAllFn Function that, given the same
    + *     signature as goog.events.removeAll, will remove all listeners
    + *     according to the contract of goog.events.removeAll.
    + * @param {Function} getListenersFn Function that, given the same
    + *     signature as goog.events.getListeners, will retrieve listeners.
    + * @param {Function} getListenerFn Function that, given the same
    + *     signature as goog.events.getListener, will retrieve the
    + *     listener object.
    + * @param {Function} hasListenerFn Function that, given the same
    + *     signature as goog.events.hasListener, will determine whether
    + *     listeners exist.
    + * @param {goog.events.eventTargetTester.KeyType} listenKeyType The
    + *     key type returned by listen call.
    + * @param {goog.events.eventTargetTester.UnlistenReturnType}
    + *     unlistenFnReturnType
    + *     Whether we should check return value from
    + *     unlisten call. If unlisten does not return a value, this should
    + *     be set to false.
    + * @param {boolean} objectListenerSupported Whether listener of type
    + *     Object is supported.
    + */
    +goog.events.eventTargetTester.setUp = function(
    +    listenableFactoryFn,
    +    listenFn, unlistenFn, unlistenByKeyFn, listenOnceFn,
    +    dispatchEventFn, removeAllFn,
    +    getListenersFn, getListenerFn, hasListenerFn,
    +    listenKeyType, unlistenFnReturnType, objectListenerSupported) {
    +  listenableFactory = listenableFactoryFn;
    +  listen = listenFn;
    +  unlisten = unlistenFn;
    +  unlistenByKey = unlistenByKeyFn;
    +  listenOnce = listenOnceFn;
    +  dispatchEvent = dispatchEventFn;
    +  removeAll = removeAllFn;
    +  getListeners = getListenersFn;
    +  getListener = getListenerFn;
    +  hasListener = hasListenerFn;
    +  keyType = listenKeyType;
    +  unlistenReturnType = unlistenFnReturnType;
    +  objectTypeListenerSupported = objectListenerSupported;
    +
    +  listeners = [];
    +  for (var i = 0; i < goog.events.eventTargetTester.MAX_; i++) {
    +    listeners[i] = createListener();
    +  }
    +
    +  eventTargets = [];
    +  for (i = 0; i < goog.events.eventTargetTester.MAX_; i++) {
    +    eventTargets[i] = listenableFactory();
    +  }
    +};
    +
    +
    +/**
    + * Teardown step for the test functions. This needs to be called from
    + * test teardown.
    + */
    +goog.events.eventTargetTester.tearDown = function() {
    +  for (var i = 0; i < goog.events.eventTargetTester.MAX_; i++) {
    +    goog.dispose(eventTargets[i]);
    +  }
    +};
    +
    +
    +/**
    + * The type of key returned by key-returning functions (listen).
    + * @enum {number}
    + */
    +goog.events.eventTargetTester.KeyType = {
    +  /**
    +   * Returns number for key.
    +   */
    +  NUMBER: 0,
    +
    +  /**
    +   * Returns undefined (no return value).
    +   */
    +  UNDEFINED: 1
    +};
    +
    +
    +/**
    + * The type of unlisten function's return value.
    + */
    +goog.events.eventTargetTester.UnlistenReturnType = {
    +  /**
    +   * Returns boolean indicating whether unlisten is successful.
    +   */
    +  BOOLEAN: 0,
    +
    +  /**
    +   * Returns undefind (no return value).
    +   */
    +  UNDEFINED: 1
    +};
    +
    +
    +/**
    + * Expando property used on "listener" function to determine if a
    + * listener has already been checked. This is what allows us to
    + * implement assertNoOtherListenerIsCalled.
    + * @type {string}
    + */
    +goog.events.eventTargetTester.ALREADY_CHECKED_PROP = '__alreadyChecked';
    +
    +
    +/**
    + * Expando property used on "listener" function to record the number
    + * of times it has been called the last time assertListenerIsCalled is
    + * done. This allows us to verify that it has not been called more
    + * times in assertNoOtherListenerIsCalled.
    + */
    +goog.events.eventTargetTester.NUM_CALLED_PROP = '__numCalled';
    +
    +
    +/**
    + * The maximum number of initialized event targets (in eventTargets
    + * array) and listeners (in listeners array).
    + * @type {number}
    + * @private
    + */
    +goog.events.eventTargetTester.MAX_ = 10;
    +
    +
    +/**
    + * Contains test event types.
    + * @enum {string}
    + */
    +var EventType = {
    +  A: goog.events.getUniqueId('a'),
    +  B: goog.events.getUniqueId('b'),
    +  C: goog.events.getUniqueId('c')
    +};
    +
    +
    +var listenableFactory, listen, unlisten, unlistenByKey, listenOnce;
    +var dispatchEvent, removeAll, getListeners, getListener, hasListener;
    +var keyType, unlistenReturnType, objectTypeListenerSupported;
    +var eventTargets, listeners;
    +
    +
    +
    +/**
    + * Custom event object for testing.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +var TestEvent = function() {
    +  TestEvent.base(this, 'constructor', EventType.A);
    +};
    +goog.inherits(TestEvent, goog.events.Event);
    +
    +
    +/**
    + * Creates a listener that executes the given function (optional).
    + * @param {!Function=} opt_listenerFn The optional function to execute.
    + * @return {!Function} The listener function.
    + */
    +function createListener(opt_listenerFn) {
    +  return goog.testing.recordFunction(opt_listenerFn);
    +}
    +
    +
    +/**
    + * Asserts that the given listener is called numCount number of times.
    + * @param {!Function} listener The listener to check.
    + * @param {number} numCount The number of times. See also the times()
    + *     function below.
    + */
    +function assertListenerIsCalled(listener, numCount) {
    +  assertEquals('Listeners is not called the correct number of times.',
    +               numCount, listener.getCallCount());
    +  listener[goog.events.eventTargetTester.ALREADY_CHECKED_PROP] = true;
    +  listener[goog.events.eventTargetTester.NUM_CALLED_PROP] = numCount;
    +}
    +
    +
    +/**
    + * Asserts that no other listeners, other than those verified via
    + * assertListenerIsCalled, have been called since the last
    + * resetListeners().
    + */
    +function assertNoOtherListenerIsCalled() {
    +  goog.array.forEach(listeners, function(l, index) {
    +    if (!l[goog.events.eventTargetTester.ALREADY_CHECKED_PROP]) {
    +      assertEquals(
    +          'Listeners ' + index + ' is unexpectedly called.',
    +          0, l.getCallCount());
    +    } else {
    +      assertEquals(
    +          'Listeners ' + index + ' is unexpectedly called.',
    +          l[goog.events.eventTargetTester.NUM_CALLED_PROP], l.getCallCount());
    +    }
    +  });
    +}
    +
    +
    +/**
    + * Resets all listeners call count to 0.
    + */
    +function resetListeners() {
    +  goog.array.forEach(listeners, function(l) {
    +    l.reset();
    +    l[goog.events.eventTargetTester.ALREADY_CHECKED_PROP] = false;
    +  });
    +}
    +
    +
    +/**
    + * The number of times a listener should have been executed. This
    + * exists to make assertListenerIsCalled more readable.  This is used
    + * like so: assertListenerIsCalled(listener, times(2));
    + * @param {number} n The number of times a listener should have been
    + *     executed.
    + * @return {number} The number n.
    + */
    +function times(n) {
    +  return n;
    +}
    +
    +
    +function testNoListener() {
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testOneListener() {
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertNoOtherListenerIsCalled();
    +
    +  resetListeners();
    +
    +  dispatchEvent(eventTargets[0], EventType.B);
    +  dispatchEvent(eventTargets[0], EventType.C);
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testTwoListenersOfSameType() {
    +  var key1 = listen(eventTargets[0], EventType.A, listeners[0]);
    +  var key2 = listen(eventTargets[0], EventType.A, listeners[1]);
    +
    +  if (keyType == goog.events.eventTargetTester.KeyType.NUMBER) {
    +    assertNotEquals(key1, key2);
    +  } else {
    +    assertUndefined(key1);
    +    assertUndefined(key2);
    +  }
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testInstallingSameListeners() {
    +  var key1 = listen(eventTargets[0], EventType.A, listeners[0]);
    +  var key2 = listen(eventTargets[0], EventType.A, listeners[0]);
    +  var key3 = listen(eventTargets[0], EventType.B, listeners[0]);
    +
    +  if (keyType == goog.events.eventTargetTester.KeyType.NUMBER) {
    +    assertEquals(key1, key2);
    +    assertNotEquals(key1, key3);
    +  } else {
    +    assertUndefined(key1);
    +    assertUndefined(key2);
    +    assertUndefined(key3);
    +  }
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +
    +  dispatchEvent(eventTargets[0], EventType.B);
    +  assertListenerIsCalled(listeners[0], times(2));
    +
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testScope() {
    +  listeners[0] = createListener(function(e) {
    +    assertEquals('Wrong scope with undefined scope', eventTargets[0], this);
    +  });
    +  listeners[1] = createListener(function(e) {
    +    assertEquals('Wrong scope with null scope', eventTargets[0], this);
    +  });
    +  var scope = {};
    +  listeners[2] = createListener(function(e) {
    +    assertEquals('Wrong scope with specific scope object', scope, this);
    +  });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1], false, null);
    +  listen(eventTargets[0], EventType.A, listeners[2], false, scope);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +}
    +
    +
    +function testDispatchEventDoesNotThrowWithDisposedEventTarget() {
    +  goog.dispose(eventTargets[0]);
    +  assertTrue(dispatchEvent(eventTargets[0], EventType.A));
    +}
    +
    +
    +function testDispatchEventWithObjectLiteral() {
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +
    +  assertTrue(dispatchEvent(eventTargets[0], {type: EventType.A}));
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testDispatchEventWithCustomEventObject() {
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +
    +  var e = new TestEvent();
    +  assertTrue(dispatchEvent(eventTargets[0], e));
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertNoOtherListenerIsCalled();
    +
    +  var actualEvent = listeners[0].getLastCall().getArgument(0);
    +
    +  assertEquals(e, actualEvent);
    +  assertEquals(eventTargets[0], actualEvent.target);
    +}
    +
    +
    +function testDisposingEventTargetRemovesListeners() {
    +  if (!(listenableFactory() instanceof goog.events.EventTarget)) {
    +    return;
    +  }
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.dispose(eventTargets[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +/**
    + * Unlisten/unlistenByKey should still work after disposal. There are
    + * many circumstances when this is actually necessary. For example, a
    + * user may have listened to an event target and stored the key
    + * (e.g. in a goog.events.EventHandler) and only unlisten after the
    + * target has been disposed.
    + */
    +function testUnlistenWorksAfterDisposal() {
    +  var key = listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.dispose(eventTargets[0]);
    +  unlisten(eventTargets[0], EventType.A, listeners[1]);
    +  if (unlistenByKey) {
    +    unlistenByKey(eventTargets[0], key);
    +  }
    +}
    +
    +
    +function testRemovingListener() {
    +  var ret1 = unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  var ret2 = unlisten(eventTargets[0], EventType.A, listeners[1]);
    +  var ret3 = unlisten(eventTargets[0], EventType.B, listeners[0]);
    +  var ret4 = unlisten(eventTargets[1], EventType.A, listeners[0]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +
    +  var ret5 = unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  var ret6 = unlisten(eventTargets[0], EventType.A, listeners[0]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +
    +  assertNoOtherListenerIsCalled();
    +
    +  if (unlistenReturnType ==
    +      goog.events.eventTargetTester.UnlistenReturnType.BOOLEAN) {
    +    assertFalse(ret1);
    +    assertFalse(ret2);
    +    assertFalse(ret3);
    +    assertFalse(ret4);
    +    assertTrue(ret5);
    +    assertFalse(ret6);
    +  } else {
    +    assertUndefined(ret1);
    +    assertUndefined(ret2);
    +    assertUndefined(ret3);
    +    assertUndefined(ret4);
    +    assertUndefined(ret5);
    +    assertUndefined(ret6);
    +  }
    +}
    +
    +
    +function testCapture() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  eventTargets[9].setParentEventTarget(eventTargets[0]);
    +
    +  var ordering = 0;
    +  listeners[0] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[2], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('First capture listener is not called first', 0, ordering);
    +        ordering++;
    +      });
    +  listeners[1] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[1], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('2nd capture listener is not called 2nd', 1, ordering);
    +        ordering++;
    +      });
    +  listeners[2] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[0], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('3rd capture listener is not called 3rd', 2, ordering);
    +        ordering++;
    +      });
    +
    +  listen(eventTargets[2], EventType.A, listeners[0], true);
    +  listen(eventTargets[1], EventType.A, listeners[1], true);
    +  listen(eventTargets[0], EventType.A, listeners[2], true);
    +
    +  // These should not be called.
    +  listen(eventTargets[3], EventType.A, listeners[3], true);
    +
    +  listen(eventTargets[0], EventType.B, listeners[4], true);
    +  listen(eventTargets[0], EventType.C, listeners[5], true);
    +  listen(eventTargets[1], EventType.B, listeners[6], true);
    +  listen(eventTargets[1], EventType.C, listeners[7], true);
    +  listen(eventTargets[2], EventType.B, listeners[8], true);
    +  listen(eventTargets[2], EventType.C, listeners[9], true);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testBubble() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  eventTargets[9].setParentEventTarget(eventTargets[0]);
    +
    +  var ordering = 0;
    +  listeners[0] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[0], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('First bubble listener is not called first', 0, ordering);
    +        ordering++;
    +      });
    +  listeners[1] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[1], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('2nd bubble listener is not called 2nd', 1, ordering);
    +        ordering++;
    +      });
    +  listeners[2] = createListener(
    +      function(e) {
    +        assertEquals(eventTargets[2], e.currentTarget);
    +        assertEquals(eventTargets[0], e.target);
    +        assertEquals('3rd bubble listener is not called 3rd', 2, ordering);
    +        ordering++;
    +      });
    +
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[1], EventType.A, listeners[1]);
    +  listen(eventTargets[2], EventType.A, listeners[2]);
    +
    +  // These should not be called.
    +  listen(eventTargets[3], EventType.A, listeners[3]);
    +
    +  listen(eventTargets[0], EventType.B, listeners[4]);
    +  listen(eventTargets[0], EventType.C, listeners[5]);
    +  listen(eventTargets[1], EventType.B, listeners[6]);
    +  listen(eventTargets[1], EventType.C, listeners[7]);
    +  listen(eventTargets[2], EventType.B, listeners[8]);
    +  listen(eventTargets[2], EventType.C, listeners[9]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testCaptureAndBubble() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +  listen(eventTargets[1], EventType.A, listeners[1], true);
    +  listen(eventTargets[2], EventType.A, listeners[2], true);
    +
    +  listen(eventTargets[0], EventType.A, listeners[3]);
    +  listen(eventTargets[1], EventType.A, listeners[4]);
    +  listen(eventTargets[2], EventType.A, listeners[5]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertListenerIsCalled(listeners[4], times(1));
    +  assertListenerIsCalled(listeners[5], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testPreventDefaultByReturningFalse() {
    +  listeners[0] = createListener(function(e) { return false; });
    +  listeners[1] = createListener(function(e) { return true; });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +
    +  var result = dispatchEvent(eventTargets[0], EventType.A);
    +  assertFalse(result);
    +}
    +
    +
    +function testPreventDefault() {
    +  listeners[0] = createListener(function(e) { e.preventDefault(); });
    +  listeners[1] = createListener(function(e) { return true; });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +
    +  var result = dispatchEvent(eventTargets[0], EventType.A);
    +  assertFalse(result);
    +}
    +
    +
    +function testPreventDefaultAtCapture() {
    +  listeners[0] = createListener(function(e) { e.preventDefault(); });
    +  listeners[1] = createListener(function(e) { return true; });
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +  listen(eventTargets[0], EventType.A, listeners[1], true);
    +
    +  var result = dispatchEvent(eventTargets[0], EventType.A);
    +  assertFalse(result);
    +}
    +
    +
    +function testStopPropagation() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  listeners[0] = createListener(function(e) { e.stopPropagation(); });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[1], EventType.A, listeners[2]);
    +  listen(eventTargets[2], EventType.A, listeners[3]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testStopPropagation2() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  listeners[1] = createListener(function(e) { e.stopPropagation(); });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[1], EventType.A, listeners[2]);
    +  listen(eventTargets[2], EventType.A, listeners[3]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testStopPropagation3() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  listeners[2] = createListener(function(e) { e.stopPropagation(); });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[1], EventType.A, listeners[2]);
    +  listen(eventTargets[2], EventType.A, listeners[3]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testStopPropagationAtCapture() {
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  eventTargets[1].setParentEventTarget(eventTargets[2]);
    +
    +  listeners[0] = createListener(function(e) { e.stopPropagation(); });
    +  listen(eventTargets[2], EventType.A, listeners[0], true);
    +  listen(eventTargets[1], EventType.A, listeners[1], true);
    +  listen(eventTargets[0], EventType.A, listeners[2], true);
    +  listen(eventTargets[0], EventType.A, listeners[3]);
    +  listen(eventTargets[1], EventType.A, listeners[4]);
    +  listen(eventTargets[2], EventType.A, listeners[5]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testHandleEvent() {
    +  if (!objectTypeListenerSupported) {
    +    return;
    +  }
    +
    +  var obj = {};
    +  obj.handleEvent = goog.testing.recordFunction();
    +
    +  listen(eventTargets[0], EventType.A, obj);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertEquals(1, obj.handleEvent.getCallCount());
    +}
    +
    +
    +function testListenOnce() {
    +  if (!listenOnce) {
    +    return;
    +  }
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0], true);
    +  listenOnce(eventTargets[0], EventType.A, listeners[1]);
    +  listenOnce(eventTargets[0], EventType.B, listeners[2]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(0));
    +  assertNoOtherListenerIsCalled();
    +  resetListeners();
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(0));
    +  assertListenerIsCalled(listeners[1], times(0));
    +  assertListenerIsCalled(listeners[2], times(0));
    +
    +  dispatchEvent(eventTargets[0], EventType.B);
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testUnlistenInListen() {
    +  listeners[1] = createListener(
    +      function(e) {
    +        unlisten(eventTargets[0], EventType.A, listeners[1]);
    +        unlisten(eventTargets[0], EventType.A, listeners[2]);
    +      });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[0], EventType.A, listeners[2]);
    +  listen(eventTargets[0], EventType.A, listeners[3]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(0));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertNoOtherListenerIsCalled();
    +  resetListeners();
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(0));
    +  assertListenerIsCalled(listeners[2], times(0));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testUnlistenByKeyInListen() {
    +  if (!unlistenByKey) {
    +    return;
    +  }
    +
    +  var key1, key2;
    +  listeners[1] = createListener(
    +      function(e) {
    +        unlistenByKey(eventTargets[0], key1);
    +        unlistenByKey(eventTargets[0], key2);
    +      });
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  key1 = listen(eventTargets[0], EventType.A, listeners[1]);
    +  key2 = listen(eventTargets[0], EventType.A, listeners[2]);
    +  listen(eventTargets[0], EventType.A, listeners[3]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(0));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertNoOtherListenerIsCalled();
    +  resetListeners();
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(0));
    +  assertListenerIsCalled(listeners[2], times(0));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testSetParentEventTarget() {
    +  assertNull(eventTargets[0].getParentEventTarget());
    +
    +  eventTargets[0].setParentEventTarget(eventTargets[1]);
    +  assertEquals(eventTargets[1], eventTargets[0].getParentEventTarget());
    +  assertNull(eventTargets[1].getParentEventTarget());
    +
    +  eventTargets[0].setParentEventTarget(null);
    +  assertNull(eventTargets[0].getParentEventTarget());
    +}
    +
    +
    +function testListenOnceAfterListenDoesNotChangeExistingListener() {
    +  if (!listenOnce) {
    +    return;
    +  }
    +
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(3));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testListenOnceAfterListenOnceDoesNotChangeExistingListener() {
    +  if (!listenOnce) {
    +    return;
    +  }
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testListenAfterListenOnceRemoveOnceness() {
    +  if (!listenOnce) {
    +    return;
    +  }
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertListenerIsCalled(listeners[0], times(3));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testUnlistenAfterListenOnce() {
    +  if (!listenOnce) {
    +    return;
    +  }
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  listen(eventTargets[0], EventType.A, listeners[0]);
    +  unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  unlisten(eventTargets[0], EventType.A, listeners[0]);
    +  dispatchEvent(eventTargets[0], EventType.A);
    +
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testRemoveAllWithType() {
    +  if (!removeAll) {
    +    return;
    +  }
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[0], EventType.C, listeners[2], true);
    +  listen(eventTargets[0], EventType.C, listeners[3]);
    +  listen(eventTargets[0], EventType.B, listeners[4], true);
    +  listen(eventTargets[0], EventType.B, listeners[5], true);
    +  listen(eventTargets[0], EventType.B, listeners[6]);
    +  listen(eventTargets[0], EventType.B, listeners[7]);
    +
    +  assertEquals(4, removeAll(eventTargets[0], EventType.B));
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.B);
    +  dispatchEvent(eventTargets[0], EventType.C);
    +
    +  assertListenerIsCalled(listeners[0], times(1));
    +  assertListenerIsCalled(listeners[1], times(1));
    +  assertListenerIsCalled(listeners[2], times(1));
    +  assertListenerIsCalled(listeners[3], times(1));
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testRemoveAll() {
    +  if (!removeAll) {
    +    return;
    +  }
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +  listen(eventTargets[0], EventType.A, listeners[1]);
    +  listen(eventTargets[0], EventType.C, listeners[2], true);
    +  listen(eventTargets[0], EventType.C, listeners[3]);
    +  listen(eventTargets[0], EventType.B, listeners[4], true);
    +  listen(eventTargets[0], EventType.B, listeners[5], true);
    +  listen(eventTargets[0], EventType.B, listeners[6]);
    +  listen(eventTargets[0], EventType.B, listeners[7]);
    +
    +  assertEquals(8, removeAll(eventTargets[0]));
    +
    +  dispatchEvent(eventTargets[0], EventType.A);
    +  dispatchEvent(eventTargets[0], EventType.B);
    +  dispatchEvent(eventTargets[0], EventType.C);
    +
    +  assertNoOtherListenerIsCalled();
    +}
    +
    +
    +function testRemoveAllCallsMarkAsRemoved() {
    +  if (!removeAll) {
    +    return;
    +  }
    +
    +  var key0 = listen(eventTargets[0], EventType.A, listeners[0]);
    +  var key1 = listen(eventTargets[1], EventType.A, listeners[1]);
    +
    +  assertNotNullNorUndefined(key0.listener);
    +  assertFalse(key0.removed);
    +  assertNotNullNorUndefined(key1.listener);
    +  assertFalse(key1.removed);
    +
    +  assertEquals(1, removeAll(eventTargets[0]));
    +  assertNull(key0.listener);
    +  assertTrue(key0.removed);
    +  assertNotNullNorUndefined(key1.listener);
    +  assertFalse(key1.removed);
    +
    +  assertEquals(1, removeAll(eventTargets[1]));
    +  assertNull(key1.listener);
    +  assertTrue(key1.removed);
    +}
    +
    +
    +function testGetListeners() {
    +  if (!getListeners) {
    +    return;
    +  }
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +  listen(eventTargets[0], EventType.A, listeners[1], true);
    +  listen(eventTargets[0], EventType.A, listeners[2]);
    +  listen(eventTargets[0], EventType.A, listeners[3]);
    +
    +  var l = getListeners(eventTargets[0], EventType.A, true);
    +  assertEquals(2, l.length);
    +  assertEquals(listeners[0], l[0].listener);
    +  assertEquals(listeners[1], l[1].listener);
    +
    +  l = getListeners(eventTargets[0], EventType.A, false);
    +  assertEquals(2, l.length);
    +  assertEquals(listeners[2], l[0].listener);
    +  assertEquals(listeners[3], l[1].listener);
    +
    +  l = getListeners(eventTargets[0], EventType.B, true);
    +  assertEquals(0, l.length);
    +}
    +
    +
    +function testGetListener() {
    +  if (!getListener) {
    +    return;
    +  }
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +
    +  assertNotNull(getListener(eventTargets[0], EventType.A, listeners[0], true));
    +  assertNull(
    +      getListener(eventTargets[0], EventType.A, listeners[0], true, {}));
    +  assertNull(getListener(eventTargets[1], EventType.A, listeners[0], true));
    +  assertNull(getListener(eventTargets[0], EventType.B, listeners[0], true));
    +  assertNull(getListener(eventTargets[0], EventType.A, listeners[1], true));
    +}
    +
    +
    +function testHasListener() {
    +  if (!hasListener) {
    +    return;
    +  }
    +
    +  assertFalse(hasListener(eventTargets[0]));
    +
    +  listen(eventTargets[0], EventType.A, listeners[0], true);
    +
    +  assertTrue(hasListener(eventTargets[0]));
    +  assertTrue(hasListener(eventTargets[0], EventType.A));
    +  assertTrue(hasListener(eventTargets[0], EventType.A, true));
    +  assertTrue(hasListener(eventTargets[0], undefined, true));
    +  assertFalse(hasListener(eventTargets[0], EventType.A, false));
    +  assertFalse(hasListener(eventTargets[0], undefined, false));
    +  assertFalse(hasListener(eventTargets[0], EventType.B));
    +  assertFalse(hasListener(eventTargets[0], EventType.B, true));
    +  assertFalse(hasListener(eventTargets[1]));
    +}
    +
    +
    +function testFiringEventBeforeDisposeInternalWorks() {
    +  /**
    +   * @extends {goog.events.EventTarget}
    +   * @constructor
    +   * @final
    +   */
    +  var MockTarget = function() {
    +    MockTarget.base(this, 'constructor');
    +  };
    +  goog.inherits(MockTarget, goog.events.EventTarget);
    +
    +  MockTarget.prototype.disposeInternal = function() {
    +    dispatchEvent(this, EventType.A);
    +    MockTarget.base(this, 'disposeInternal');
    +  };
    +
    +  var t = new MockTarget();
    +  try {
    +    listen(t, EventType.A, listeners[0]);
    +    t.dispose();
    +    assertListenerIsCalled(listeners[0], times(1));
    +  } catch (e) {
    +    goog.dispose(t);
    +  }
    +}
    +
    +
    +function testLoopDetection() {
    +  var target = listenableFactory();
    +  target.setParentEventTarget(target);
    +
    +  try {
    +    target.dispatchEvent('string');
    +    fail('expected error');
    +  } catch (e) {
    +    assertContains('infinite', e.message);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventtype.js b/mapbox-gl/closure-library/closure/goog/events/eventtype.js
    new file mode 100644
    index 0000000000..67c1da6837
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventtype.js
    @@ -0,0 +1,232 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Event Types.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.events.EventType');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Returns a prefixed event name for the current browser.
    + * @param {string} eventName The name of the event.
    + * @return {string} The prefixed event name.
    + * @suppress {missingRequire|missingProvide}
    + * @private
    + */
    +goog.events.getVendorPrefixedName_ = function(eventName) {
    +  return goog.userAgent.WEBKIT ? 'webkit' + eventName :
    +      (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
    +          eventName.toLowerCase());
    +};
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +goog.events.EventType = {
    +  // Mouse events
    +  CLICK: 'click',
    +  RIGHTCLICK: 'rightclick',
    +  DBLCLICK: 'dblclick',
    +  MOUSEDOWN: 'mousedown',
    +  MOUSEUP: 'mouseup',
    +  MOUSEOVER: 'mouseover',
    +  MOUSEOUT: 'mouseout',
    +  MOUSEMOVE: 'mousemove',
    +  MOUSEENTER: 'mouseenter',
    +  MOUSELEAVE: 'mouseleave',
    +  // Select start is non-standard.
    +  // See http://msdn.microsoft.com/en-us/library/ie/ms536969(v=vs.85).aspx.
    +  SELECTSTART: 'selectstart', // IE, Safari, Chrome
    +
    +  // Wheel events
    +  // http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
    +  WHEEL: 'wheel',
    +
    +  // Key events
    +  KEYPRESS: 'keypress',
    +  KEYDOWN: 'keydown',
    +  KEYUP: 'keyup',
    +
    +  // Focus
    +  BLUR: 'blur',
    +  FOCUS: 'focus',
    +  DEACTIVATE: 'deactivate', // IE only
    +  // NOTE: The following two events are not stable in cross-browser usage.
    +  //     WebKit and Opera implement DOMFocusIn/Out.
    +  //     IE implements focusin/out.
    +  //     Gecko implements neither see bug at
    +  //     https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
    +  // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
    +  //     http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
    +  // You can use FOCUS in Capture phase until implementations converge.
    +  FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
    +  FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
    +
    +  // Forms
    +  CHANGE: 'change',
    +  SELECT: 'select',
    +  SUBMIT: 'submit',
    +  INPUT: 'input',
    +  PROPERTYCHANGE: 'propertychange', // IE only
    +
    +  // Drag and drop
    +  DRAGSTART: 'dragstart',
    +  DRAG: 'drag',
    +  DRAGENTER: 'dragenter',
    +  DRAGOVER: 'dragover',
    +  DRAGLEAVE: 'dragleave',
    +  DROP: 'drop',
    +  DRAGEND: 'dragend',
    +
    +  // Touch events
    +  // Note that other touch events exist, but we should follow the W3C list here.
    +  // http://www.w3.org/TR/touch-events/#list-of-touchevent-types
    +  TOUCHSTART: 'touchstart',
    +  TOUCHMOVE: 'touchmove',
    +  TOUCHEND: 'touchend',
    +  TOUCHCANCEL: 'touchcancel',
    +
    +  // Misc
    +  BEFOREUNLOAD: 'beforeunload',
    +  CONSOLEMESSAGE: 'consolemessage',
    +  CONTEXTMENU: 'contextmenu',
    +  DOMCONTENTLOADED: 'DOMContentLoaded',
    +  ERROR: 'error',
    +  HELP: 'help',
    +  LOAD: 'load',
    +  LOSECAPTURE: 'losecapture',
    +  ORIENTATIONCHANGE: 'orientationchange',
    +  READYSTATECHANGE: 'readystatechange',
    +  RESIZE: 'resize',
    +  SCROLL: 'scroll',
    +  UNLOAD: 'unload',
    +
    +  // HTML 5 History events
    +  // See http://www.w3.org/TR/html5/history.html#event-definitions
    +  HASHCHANGE: 'hashchange',
    +  PAGEHIDE: 'pagehide',
    +  PAGESHOW: 'pageshow',
    +  POPSTATE: 'popstate',
    +
    +  // Copy and Paste
    +  // Support is limited. Make sure it works on your favorite browser
    +  // before using.
    +  // http://www.quirksmode.org/dom/events/cutcopypaste.html
    +  COPY: 'copy',
    +  PASTE: 'paste',
    +  CUT: 'cut',
    +  BEFORECOPY: 'beforecopy',
    +  BEFORECUT: 'beforecut',
    +  BEFOREPASTE: 'beforepaste',
    +
    +  // HTML5 online/offline events.
    +  // http://www.w3.org/TR/offline-webapps/#related
    +  ONLINE: 'online',
    +  OFFLINE: 'offline',
    +
    +  // HTML 5 worker events
    +  MESSAGE: 'message',
    +  CONNECT: 'connect',
    +
    +  // CSS animation events.
    +  /** @suppress {missingRequire} */
    +  ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
    +  /** @suppress {missingRequire} */
    +  ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
    +  /** @suppress {missingRequire} */
    +  ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
    +
    +  // CSS transition events. Based on the browser support described at:
    +  // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
    +  /** @suppress {missingRequire} */
    +  TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
    +
    +  // W3C Pointer Events
    +  // http://www.w3.org/TR/pointerevents/
    +  POINTERDOWN: 'pointerdown',
    +  POINTERUP: 'pointerup',
    +  POINTERCANCEL: 'pointercancel',
    +  POINTERMOVE: 'pointermove',
    +  POINTEROVER: 'pointerover',
    +  POINTEROUT: 'pointerout',
    +  POINTERENTER: 'pointerenter',
    +  POINTERLEAVE: 'pointerleave',
    +  GOTPOINTERCAPTURE: 'gotpointercapture',
    +  LOSTPOINTERCAPTURE: 'lostpointercapture',
    +
    +  // IE specific events.
    +  // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
    +  // Note: these events will be supplanted in IE11.
    +  MSGESTURECHANGE: 'MSGestureChange',
    +  MSGESTUREEND: 'MSGestureEnd',
    +  MSGESTUREHOLD: 'MSGestureHold',
    +  MSGESTURESTART: 'MSGestureStart',
    +  MSGESTURETAP: 'MSGestureTap',
    +  MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
    +  MSINERTIASTART: 'MSInertiaStart',
    +  MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
    +  MSPOINTERCANCEL: 'MSPointerCancel',
    +  MSPOINTERDOWN: 'MSPointerDown',
    +  MSPOINTERENTER: 'MSPointerEnter',
    +  MSPOINTERHOVER: 'MSPointerHover',
    +  MSPOINTERLEAVE: 'MSPointerLeave',
    +  MSPOINTERMOVE: 'MSPointerMove',
    +  MSPOINTEROUT: 'MSPointerOut',
    +  MSPOINTEROVER: 'MSPointerOver',
    +  MSPOINTERUP: 'MSPointerUp',
    +
    +  // Native IMEs/input tools events.
    +  TEXT: 'text',
    +  TEXTINPUT: 'textInput',
    +  COMPOSITIONSTART: 'compositionstart',
    +  COMPOSITIONUPDATE: 'compositionupdate',
    +  COMPOSITIONEND: 'compositionend',
    +
    +  // Webview tag events
    +  // See http://developer.chrome.com/dev/apps/webview_tag.html
    +  EXIT: 'exit',
    +  LOADABORT: 'loadabort',
    +  LOADCOMMIT: 'loadcommit',
    +  LOADREDIRECT: 'loadredirect',
    +  LOADSTART: 'loadstart',
    +  LOADSTOP: 'loadstop',
    +  RESPONSIVE: 'responsive',
    +  SIZECHANGED: 'sizechanged',
    +  UNRESPONSIVE: 'unresponsive',
    +
    +  // HTML5 Page Visibility API.  See details at
    +  // {@code goog.labs.dom.PageVisibilityMonitor}.
    +  VISIBILITYCHANGE: 'visibilitychange',
    +
    +  // LocalStorage event.
    +  STORAGE: 'storage',
    +
    +  // DOM Level 2 mutation events (deprecated).
    +  DOMSUBTREEMODIFIED: 'DOMSubtreeModified',
    +  DOMNODEINSERTED: 'DOMNodeInserted',
    +  DOMNODEREMOVED: 'DOMNodeRemoved',
    +  DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',
    +  DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',
    +  DOMATTRMODIFIED: 'DOMAttrModified',
    +  DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/eventwrapper.js b/mapbox-gl/closure-library/closure/goog/events/eventwrapper.js
    new file mode 100644
    index 0000000000..1581774254
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/eventwrapper.js
    @@ -0,0 +1,66 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.events.EventWrapper interface.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.events.EventWrapper');
    +
    +
    +
    +/**
    + * Interface for event wrappers.
    + * @interface
    + */
    +goog.events.EventWrapper = function() {
    +};
    +
    +
    +/**
    + * Adds an event listener using the wrapper on a DOM Node or an object that has
    + * implemented {@link goog.events.EventTarget}. A listener can only be added
    + * once to an object.
    + *
    + * @param {goog.events.ListenableType} src The node to listen to events on.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
    + *     method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @param {goog.events.EventHandler=} opt_eventHandler Event handler to add
    + *     listener to.
    + */
    +goog.events.EventWrapper.prototype.listen = function(src, listener, opt_capt,
    +    opt_scope, opt_eventHandler) {
    +};
    +
    +
    +/**
    + * Removes an event listener added using goog.events.EventWrapper.listen.
    + *
    + * @param {goog.events.ListenableType} src The node to remove listener from.
    + * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
    + *     method, or an object with a handleEvent function.
    + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
    + *     false).
    + * @param {Object=} opt_scope Element in whose scope to call the listener.
    + * @param {goog.events.EventHandler=} opt_eventHandler Event handler to remove
    + *     listener from.
    + */
    +goog.events.EventWrapper.prototype.unlisten = function(src, listener, opt_capt,
    +    opt_scope, opt_eventHandler) {
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/filedrophandler.js b/mapbox-gl/closure-library/closure/goog/events/filedrophandler.js
    new file mode 100644
    index 0000000000..5678fe3ab5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/filedrophandler.js
    @@ -0,0 +1,222 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a files drag and drop event detector. It works on
    + * HTML5 browsers.
    + *
    + * @see ../demos/filedrophandler.html
    + */
    +
    +goog.provide('goog.events.FileDropHandler');
    +goog.provide('goog.events.FileDropHandler.EventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +
    +
    +
    +/**
    + * A files drag and drop event detector. Gets an {@code element} as parameter
    + * and fires {@code goog.events.FileDropHandler.EventType.DROP} event when files
    + * are dropped in the {@code element}.
    + *
    + * @param {Element|Document} element The element or document to listen on.
    + * @param {boolean=} opt_preventDropOutside Whether to prevent a drop on the
    + *     area outside the {@code element}. Default false.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.events.FileDropHandler = function(element, opt_preventDropOutside) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Handler for drag/drop events.
    +   * @type {!goog.events.EventHandler<!goog.events.FileDropHandler>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  var doc = element;
    +  if (opt_preventDropOutside) {
    +    doc = goog.dom.getOwnerDocument(element);
    +  }
    +
    +  // Add dragenter listener to the owner document of the element.
    +  this.eventHandler_.listen(doc,
    +                            goog.events.EventType.DRAGENTER,
    +                            this.onDocDragEnter_);
    +
    +  // Add dragover listener to the owner document of the element only if the
    +  // document is not the element itself.
    +  if (doc != element) {
    +    this.eventHandler_.listen(doc,
    +                              goog.events.EventType.DRAGOVER,
    +                              this.onDocDragOver_);
    +  }
    +
    +  // Add dragover and drop listeners to the element.
    +  this.eventHandler_.listen(element,
    +                            goog.events.EventType.DRAGOVER,
    +                            this.onElemDragOver_);
    +  this.eventHandler_.listen(element,
    +                            goog.events.EventType.DROP,
    +                            this.onElemDrop_);
    +};
    +goog.inherits(goog.events.FileDropHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Whether the drag event contains files. It is initialized only in the
    + * dragenter event. It is used in all the drag events to prevent default actions
    + * only if the drag contains files. Preventing default actions is necessary to
    + * go from dragenter to dragover and from dragover to drop. However we do not
    + * always want to prevent default actions, e.g. when the user drags text or
    + * links on a text area we should not prevent the browser default action that
    + * inserts the text in the text area. It is also necessary to stop propagation
    + * when handling drag events on the element to prevent them from propagating
    + * to the document.
    + * @private
    + * @type {boolean}
    + */
    +goog.events.FileDropHandler.prototype.dndContainsFiles_ = false;
    +
    +
    +/**
    + * A logger, used to help us debug the algorithm.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.logger_ =
    +    goog.log.getLogger('goog.events.FileDropHandler');
    +
    +
    +/**
    + * The types of events fired by this class.
    + * @enum {string}
    + */
    +goog.events.FileDropHandler.EventType = {
    +  DROP: goog.events.EventType.DROP
    +};
    +
    +
    +/** @override */
    +goog.events.FileDropHandler.prototype.disposeInternal = function() {
    +  goog.events.FileDropHandler.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +};
    +
    +
    +/**
    + * Dispatches the DROP event.
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.dispatch_ = function(e) {
    +  goog.log.fine(this.logger_, 'Firing DROP event...');
    +  var event = new goog.events.BrowserEvent(e.getBrowserEvent());
    +  event.type = goog.events.FileDropHandler.EventType.DROP;
    +  this.dispatchEvent(event);
    +};
    +
    +
    +/**
    + * Handles dragenter on the document.
    + * @param {goog.events.BrowserEvent} e The dragenter event.
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.onDocDragEnter_ = function(e) {
    +  goog.log.log(this.logger_, goog.log.Level.FINER,
    +      '"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
    +  var dt = e.getBrowserEvent().dataTransfer;
    +  // Check whether the drag event contains files.
    +  this.dndContainsFiles_ = !!(dt &&
    +      ((dt.types &&
    +          (goog.array.contains(dt.types, 'Files') ||
    +          goog.array.contains(dt.types, 'public.file-url'))) ||
    +      (dt.files && dt.files.length > 0)));
    +  // If it does
    +  if (this.dndContainsFiles_) {
    +    // Prevent default actions.
    +    e.preventDefault();
    +  }
    +  goog.log.log(this.logger_, goog.log.Level.FINER,
    +      'dndContainsFiles_: ' + this.dndContainsFiles_);
    +};
    +
    +
    +/**
    + * Handles dragging something over the document.
    + * @param {goog.events.BrowserEvent} e The dragover event.
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.onDocDragOver_ = function(e) {
    +  goog.log.log(this.logger_, goog.log.Level.FINEST,
    +      '"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
    +  if (this.dndContainsFiles_) {
    +    // Prevent default actions.
    +    e.preventDefault();
    +    // Disable the drop on the document outside the drop zone.
    +    var dt = e.getBrowserEvent().dataTransfer;
    +    dt.dropEffect = 'none';
    +  }
    +};
    +
    +
    +/**
    + * Handles dragging something over the element (drop zone).
    + * @param {goog.events.BrowserEvent} e The dragover event.
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.onElemDragOver_ = function(e) {
    +  goog.log.log(this.logger_, goog.log.Level.FINEST,
    +      '"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
    +  if (this.dndContainsFiles_) {
    +    // Prevent default actions and stop the event from propagating further to
    +    // the document. Both lines are needed! (See comment above).
    +    e.preventDefault();
    +    e.stopPropagation();
    +    // Allow the drop on the drop zone.
    +    var dt = e.getBrowserEvent().dataTransfer;
    +    dt.effectAllowed = 'all';
    +    dt.dropEffect = 'copy';
    +  }
    +};
    +
    +
    +/**
    + * Handles dropping something onto the element (drop zone).
    + * @param {goog.events.BrowserEvent} e The drop event.
    + * @private
    + */
    +goog.events.FileDropHandler.prototype.onElemDrop_ = function(e) {
    +  goog.log.log(this.logger_, goog.log.Level.FINER,
    +      '"' + e.target.id + '" (' + e.target + ') dispatched: ' + e.type);
    +  // If the drag and drop event contains files.
    +  if (this.dndContainsFiles_) {
    +    // Prevent default actions and stop the event from propagating further to
    +    // the document. Both lines are needed! (See comment above).
    +    e.preventDefault();
    +    e.stopPropagation();
    +    // Dispatch DROP event.
    +    this.dispatch_(e);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.html b/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.html
    new file mode 100644
    index 0000000000..14b9495884
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.FileDropHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.FileDropHandlerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.js b/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.js
    new file mode 100644
    index 0000000000..80015c8d02
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/filedrophandler_test.js
    @@ -0,0 +1,250 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.FileDropHandlerTest');
    +goog.setTestOnly('goog.events.FileDropHandlerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.FileDropHandler');
    +goog.require('goog.testing.jsunit');
    +
    +var textarea;
    +var doc;
    +var handler;
    +var dnd;
    +var files;
    +
    +function setUp() {
    +  textarea = new goog.events.EventTarget();
    +  doc = new goog.events.EventTarget();
    +  textarea.ownerDocument = doc;
    +  handler = new goog.events.FileDropHandler(textarea);
    +  dnd = false;
    +  files = null;
    +  goog.events.listen(handler, goog.events.FileDropHandler.EventType.DROP,
    +      function(e) {
    +        dnd = true;
    +        files =
    +            e.getBrowserEvent().dataTransfer.files;
    +      });
    +}
    +
    +function tearDown() {
    +  textarea.dispose();
    +  doc.dispose();
    +  handler.dispose();
    +}
    +
    +function testOneFile() {
    +  var preventDefault = false;
    +  var expectedfiles = [{ fileName: 'file1.jpg' }];
    +  var dt = { types: ['Files'], files: expectedfiles };
    +
    +  // Assert that default actions are prevented on dragenter.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are prevented on dragover.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGOVER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +  // Assert that the drop effect is set to 'copy'.
    +  assertEquals('copy', dt.dropEffect);
    +
    +  // Assert that default actions are prevented on drop.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DROP,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +
    +  // Assert that DROP has been fired.
    +  assertTrue(dnd);
    +  assertEquals(1, files.length);
    +  assertEquals(expectedfiles[0].fileName, files[0].fileName);
    +}
    +
    +function testMultipleFiles() {
    +  var preventDefault = false;
    +  var expectedfiles = [{ fileName: 'file1.jpg' }, { fileName: 'file2.jpg' }];
    +  var dt = { types: ['Files', 'text'], files: expectedfiles };
    +
    +  // Assert that default actions are prevented on dragenter.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are prevented on dragover.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGOVER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +  // Assert that the drop effect is set to 'copy'.
    +  assertEquals('copy', dt.dropEffect);
    +
    +  // Assert that default actions are prevented on drop.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DROP,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +
    +  // Assert that DROP has been fired.
    +  assertTrue(dnd);
    +  assertEquals(2, files.length);
    +  assertEquals(expectedfiles[0].fileName, files[0].fileName);
    +  assertEquals(expectedfiles[1].fileName, files[1].fileName);
    +}
    +
    +function testNoFiles() {
    +  var preventDefault = false;
    +  var dt = { types: ['text'] };
    +
    +  // Assert that default actions are not prevented on dragenter.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: dt
    +  }));
    +  assertFalse(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are not prevented on dragover.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGOVER,
    +    dataTransfer: dt
    +  }));
    +  assertFalse(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are not prevented on drop.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DROP,
    +    dataTransfer: dt
    +  }));
    +  assertFalse(preventDefault);
    +
    +  // Assert that DROP has not been fired.
    +  assertFalse(dnd);
    +  assertNull(files);
    +}
    +
    +function testDragEnter() {
    +  var preventDefault = false;
    +
    +  // Assert that default actions are prevented on dragenter.
    +  // In Chrome the dragenter event has an empty file list and the types is
    +  // set to 'Files'.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: { types: ['Files'], files: [] }
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are prevented on dragenter.
    +  // In Safari 4 the dragenter event has an empty file list and the types is
    +  // set to 'public.file-url'.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: { types: ['public.file-url'], files: [] }
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are not prevented on dragenter
    +  // when the drag contains no files.
    +  textarea.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: { types: ['text'], files: [] }
    +  }));
    +  assertFalse(preventDefault);
    +}
    +
    +function testPreventDropOutside() {
    +  var preventDefault = false;
    +  var dt = { types: ['Files'], files: [{ fileName: 'file1.jpg' }] };
    +
    +  // Assert that default actions are not prevented on dragenter on the
    +  // document outside the text area.
    +  doc.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: dt
    +  }));
    +  assertFalse(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are not prevented on dragover on the
    +  // document outside the text area.
    +  doc.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGOVER,
    +    dataTransfer: dt
    +  }));
    +  assertFalse(preventDefault);
    +  preventDefault = false;
    +
    +  handler.dispose();
    +  // Create a new FileDropHandler that prevents drops outside the text area.
    +  handler = new goog.events.FileDropHandler(textarea, true);
    +
    +  // Assert that default actions are now prevented on dragenter on the
    +  // document outside the text area.
    +  doc.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGENTER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +
    +  // Assert that default actions are now prevented on dragover on the
    +  // document outside the text area.
    +  doc.dispatchEvent(new goog.events.BrowserEvent({
    +    preventDefault: function() { preventDefault = true; },
    +    type: goog.events.EventType.DRAGOVER,
    +    dataTransfer: dt
    +  }));
    +  assertTrue(preventDefault);
    +  preventDefault = false;
    +  // Assert also that the drop effect is set to 'none'.
    +  assertEquals('none', dt.dropEffect);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/focushandler.js b/mapbox-gl/closure-library/closure/goog/events/focushandler.js
    new file mode 100644
    index 0000000000..f4e1000b3c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/focushandler.js
    @@ -0,0 +1,107 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This event handler allows you to catch focusin and focusout
    + * events on  descendants. Unlike the "focus" and "blur" events which do not
    + * propagate consistently, and therefore must be added to the element that is
    + * focused, this allows you to attach one listener to an ancester and you will
    + * be notified when the focus state changes of ony of its descendants.
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/focushandler.html
    + */
    +
    +goog.provide('goog.events.FocusHandler');
    +goog.provide('goog.events.FocusHandler.EventType');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This event handler allows you to catch focus events when descendants gain or
    + * loses focus.
    + * @param {Element|Document} element  The node to listen on.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.events.FocusHandler = function(element) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * This is the element that we will listen to the real focus events on.
    +   * @type {Element|Document}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  // In IE we use focusin/focusout and in other browsers we use a capturing
    +  // listner for focus/blur
    +  var typeIn = goog.userAgent.IE ? 'focusin' : 'focus';
    +  var typeOut = goog.userAgent.IE ? 'focusout' : 'blur';
    +
    +  /**
    +   * Store the listen key so it easier to unlisten in dispose.
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.listenKeyIn_ =
    +      goog.events.listen(this.element_, typeIn, this, !goog.userAgent.IE);
    +
    +  /**
    +   * Store the listen key so it easier to unlisten in dispose.
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.listenKeyOut_ =
    +      goog.events.listen(this.element_, typeOut, this, !goog.userAgent.IE);
    +};
    +goog.inherits(goog.events.FocusHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum type for the events fired by the focus handler
    + * @enum {string}
    + */
    +goog.events.FocusHandler.EventType = {
    +  FOCUSIN: 'focusin',
    +  FOCUSOUT: 'focusout'
    +};
    +
    +
    +/**
    + * This handles the underlying events and dispatches a new event.
    + * @param {goog.events.BrowserEvent} e  The underlying browser event.
    + */
    +goog.events.FocusHandler.prototype.handleEvent = function(e) {
    +  var be = e.getBrowserEvent();
    +  var event = new goog.events.BrowserEvent(be);
    +  event.type = e.type == 'focusin' || e.type == 'focus' ?
    +      goog.events.FocusHandler.EventType.FOCUSIN :
    +      goog.events.FocusHandler.EventType.FOCUSOUT;
    +  this.dispatchEvent(event);
    +};
    +
    +
    +/** @override */
    +goog.events.FocusHandler.prototype.disposeInternal = function() {
    +  goog.events.FocusHandler.superClass_.disposeInternal.call(this);
    +  goog.events.unlistenByKey(this.listenKeyIn_);
    +  goog.events.unlistenByKey(this.listenKeyOut_);
    +  delete this.element_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/imehandler.js b/mapbox-gl/closure-library/closure/goog/events/imehandler.js
    new file mode 100644
    index 0000000000..661f91f7cb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/imehandler.js
    @@ -0,0 +1,369 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Input Method Editors (IMEs) are OS-level widgets that make
    + * it easier to type non-ascii characters on ascii keyboards (in particular,
    + * characters that require more than one keystroke).
    + *
    + * When the user wants to type such a character, a modal menu pops up and
    + * suggests possible "next" characters in the IME character sequence. After
    + * typing N characters, the user hits "enter" to commit the IME to the field.
    + * N differs from language to language.
    + *
    + * This class offers high-level events for how the user is interacting with the
    + * IME in editable regions.
    + *
    + * Known Issues:
    + *
    + * Firefox always fires an extra pair of compositionstart/compositionend events.
    + * We do not normalize for this.
    + *
    + * Opera does not fire any IME events.
    + *
    + * Spurious UPDATE events are common on all browsers.
    + *
    + * We currently do a bad job detecting when the IME closes on IE, and
    + * make a "best effort" guess on when we know it's closed.
    + *
    + * @author nicksantos@google.com (Nick Santos) (Ported to Closure)
    + */
    +
    +goog.provide('goog.events.ImeHandler');
    +goog.provide('goog.events.ImeHandler.Event');
    +goog.provide('goog.events.ImeHandler.EventType');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Dispatches high-level events for IMEs.
    + * @param {Element} el The element to listen on.
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + * @final
    + */
    +goog.events.ImeHandler = function(el) {
    +  goog.events.ImeHandler.base(this, 'constructor');
    +
    +  /**
    +   * The element to listen on.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.el_ = el;
    +
    +  /**
    +   * Tracks the keyup event only, because it has a different life-cycle from
    +   * other events.
    +   * @type {goog.events.EventHandler<!goog.events.ImeHandler>}
    +   * @private
    +   */
    +  this.keyUpHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Tracks all the browser events.
    +   * @type {goog.events.EventHandler<!goog.events.ImeHandler>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +
    +  if (goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
    +    this.handler_.
    +        listen(el, goog.events.EventType.COMPOSITIONSTART,
    +            this.handleCompositionStart_).
    +        listen(el, goog.events.EventType.COMPOSITIONEND,
    +            this.handleCompositionEnd_).
    +        listen(el, goog.events.EventType.COMPOSITIONUPDATE,
    +            this.handleTextModifyingInput_);
    +  }
    +
    +  this.handler_.
    +      listen(el, goog.events.EventType.TEXTINPUT, this.handleTextInput_).
    +      listen(el, goog.events.EventType.TEXT, this.handleTextModifyingInput_).
    +      listen(el, goog.events.EventType.KEYDOWN, this.handleKeyDown_);
    +};
    +goog.inherits(goog.events.ImeHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Event types fired by ImeHandler. These events do not make any guarantees
    + * about whether they were fired before or after the event in question.
    + * @enum {string}
    + */
    +goog.events.ImeHandler.EventType = {
    +  // After the IME opens.
    +  START: 'startIme',
    +
    +  // An update to the state of the IME. An 'update' does not necessarily mean
    +  // that the text contents of the field were modified in any way.
    +  UPDATE: 'updateIme',
    +
    +  // After the IME closes.
    +  END: 'endIme'
    +};
    +
    +
    +
    +/**
    + * An event fired by ImeHandler.
    + * @param {goog.events.ImeHandler.EventType} type The type.
    + * @param {goog.events.BrowserEvent} reason The trigger for this event.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.events.ImeHandler.Event = function(type, reason) {
    +  goog.events.ImeHandler.Event.base(this, 'constructor', type);
    +
    +  /**
    +   * The event that triggered this.
    +   * @type {goog.events.BrowserEvent}
    +   */
    +  this.reason = reason;
    +};
    +goog.inherits(goog.events.ImeHandler.Event, goog.events.Event);
    +
    +
    +/**
    + * Whether to use the composition events.
    + * @type {boolean}
    + */
    +goog.events.ImeHandler.USES_COMPOSITION_EVENTS =
    +    goog.userAgent.GECKO ||
    +    (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher(532));
    +
    +
    +/**
    + * Stores whether IME mode is active.
    + * @type {boolean}
    + * @private
    + */
    +goog.events.ImeHandler.prototype.imeMode_ = false;
    +
    +
    +/**
    + * The keyCode value of the last keyDown event. This value is used for
    + * identiying whether or not a textInput event is sent by an IME.
    + * @type {number}
    + * @private
    + */
    +goog.events.ImeHandler.prototype.lastKeyCode_ = 0;
    +
    +
    +/**
    + * @return {boolean} Whether an IME is active.
    + */
    +goog.events.ImeHandler.prototype.isImeMode = function() {
    +  return this.imeMode_;
    +};
    +
    +
    +/**
    + * Handles the compositionstart event.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleCompositionStart_ =
    +    function(e) {
    +  this.handleImeActivate_(e);
    +};
    +
    +
    +/**
    + * Handles the compositionend event.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleCompositionEnd_ = function(e) {
    +  this.handleImeDeactivate_(e);
    +};
    +
    +
    +/**
    + * Handles the compositionupdate and text events.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleTextModifyingInput_ =
    +    function(e) {
    +  if (this.isImeMode()) {
    +    this.processImeComposition_(e);
    +  }
    +};
    +
    +
    +/**
    + * Handles IME activation.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleImeActivate_ = function(e) {
    +  if (this.imeMode_) {
    +    return;
    +  }
    +
    +  // Listens for keyup events to handle unexpected IME keydown events on older
    +  // versions of webkit.
    +  //
    +  // In those versions, we currently use textInput events deactivate IME
    +  // (see handleTextInput_() for the reason). However,
    +  // Safari fires a keydown event (as a result of pressing keys to commit IME
    +  // text) with keyCode == WIN_IME after textInput event. This activates IME
    +  // mode again unnecessarily. To prevent this problem, listens keyup events
    +  // which can use to determine whether IME text has been committed.
    +  if (goog.userAgent.WEBKIT &&
    +      !goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
    +    this.keyUpHandler_.listen(this.el_,
    +        goog.events.EventType.KEYUP, this.handleKeyUpSafari4_);
    +  }
    +
    +  this.imeMode_ = true;
    +  this.dispatchEvent(
    +      new goog.events.ImeHandler.Event(
    +          goog.events.ImeHandler.EventType.START, e));
    +};
    +
    +
    +/**
    + * Handles the IME compose changes.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.processImeComposition_ = function(e) {
    +  this.dispatchEvent(
    +      new goog.events.ImeHandler.Event(
    +          goog.events.ImeHandler.EventType.UPDATE, e));
    +};
    +
    +
    +/**
    + * Handles IME deactivation.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleImeDeactivate_ = function(e) {
    +  this.imeMode_ = false;
    +  this.keyUpHandler_.removeAll();
    +  this.dispatchEvent(
    +      new goog.events.ImeHandler.Event(
    +          goog.events.ImeHandler.EventType.END, e));
    +};
    +
    +
    +/**
    + * Handles a key down event.
    + * @param {!goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleKeyDown_ = function(e) {
    +  // Firefox and Chrome have a separate event for IME composition ('text'
    +  // and 'compositionupdate', respectively), other browsers do not.
    +  if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS) {
    +    var imeMode = this.isImeMode();
    +    // If we're in IE and we detect an IME input on keyDown then activate
    +    // the IME, otherwise if the imeMode was previously active, deactivate.
    +    if (!imeMode && e.keyCode == goog.events.KeyCodes.WIN_IME) {
    +      this.handleImeActivate_(e);
    +    } else if (imeMode && e.keyCode != goog.events.KeyCodes.WIN_IME) {
    +      if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
    +        this.handleImeDeactivate_(e);
    +      }
    +    } else if (imeMode) {
    +      this.processImeComposition_(e);
    +    }
    +  }
    +
    +  // Safari on Mac doesn't send IME events in the right order so that we must
    +  // ignore some modifier key events to insert IME text correctly.
    +  if (goog.events.ImeHandler.isImeDeactivateKeyEvent_(e)) {
    +    this.lastKeyCode_ = e.keyCode;
    +  }
    +};
    +
    +
    +/**
    + * Handles a textInput event.
    + * @param {!goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleTextInput_ = function(e) {
    +  // Some WebKit-based browsers including Safari 4 don't send composition
    +  // events. So, we turn down IME mode when it's still there.
    +  if (!goog.events.ImeHandler.USES_COMPOSITION_EVENTS &&
    +      goog.userAgent.WEBKIT &&
    +      this.lastKeyCode_ == goog.events.KeyCodes.WIN_IME &&
    +      this.isImeMode()) {
    +    this.handleImeDeactivate_(e);
    +  }
    +};
    +
    +
    +/**
    + * Handles the key up event for any IME activity. This handler is just used to
    + * prevent activating IME unnecessary in Safari at this time.
    + * @param {!goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.events.ImeHandler.prototype.handleKeyUpSafari4_ = function(e) {
    +  if (this.isImeMode()) {
    +    switch (e.keyCode) {
    +      // These keyup events indicates that IME text has been committed or
    +      // cancelled. We should turn off IME mode when these keyup events
    +      // received.
    +      case goog.events.KeyCodes.ENTER:
    +      case goog.events.KeyCodes.TAB:
    +      case goog.events.KeyCodes.ESC:
    +        this.handleImeDeactivate_(e);
    +        break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns whether the given event should be treated as an IME
    + * deactivation trigger.
    + * @param {!goog.events.Event} e The event.
    + * @return {boolean} Whether the given event is an IME deactivate trigger.
    + * @private
    + */
    +goog.events.ImeHandler.isImeDeactivateKeyEvent_ = function(e) {
    +  // Which key events involve IME deactivation depends on the user's
    +  // environment (i.e. browsers, platforms, and IMEs). Usually Shift key
    +  // and Ctrl key does not involve IME deactivation, so we currently assume
    +  // that these keys are not IME deactivation trigger.
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.SHIFT:
    +    case goog.events.KeyCodes.CTRL:
    +      return false;
    +    default:
    +      return true;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.events.ImeHandler.prototype.disposeInternal = function() {
    +  this.handler_.dispose();
    +  this.keyUpHandler_.dispose();
    +  this.el_ = null;
    +  goog.events.ImeHandler.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/imehandler_test.html b/mapbox-gl/closure-library/closure/goog/events/imehandler_test.html
    new file mode 100644
    index 0000000000..b798575074
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/imehandler_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos) (Ported to Closure)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   JsUnit tests for goog.events.ImeHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.ImeHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    +  <div>
    +   <b>
    +    Last 10 events:
    +   </b>
    +   <div id="logger" style="padding: 0.5em;">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/imehandler_test.js b/mapbox-gl/closure-library/closure/goog/events/imehandler_test.js
    new file mode 100644
    index 0000000000..4e770c8b1c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/imehandler_test.js
    @@ -0,0 +1,266 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.ImeHandlerTest');
    +goog.setTestOnly('goog.events.ImeHandlerTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.ImeHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var sandbox;
    +var imeHandler;
    +var eventsFired;
    +var stubs = new goog.testing.PropertyReplacer();
    +var eventTypes = goog.events.ImeHandler.EventType;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +}
    +
    +function initImeHandler() {
    +  goog.events.ImeHandler.USES_COMPOSITION_EVENTS =
    +      goog.userAgent.GECKO ||
    +      (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher(532));
    +  imeHandler = new goog.events.ImeHandler(sandbox);
    +  eventsFired = [];
    +  goog.events.listen(
    +      imeHandler,
    +      goog.object.getValues(goog.events.ImeHandler.EventType),
    +      function(e) {
    +        eventsFired.push(e.type);
    +      });
    +}
    +
    +function tearDown() {
    +  imeHandler.dispose();
    +  imeHandler = null;
    +
    +  stubs.reset();
    +}
    +
    +function tearDownPage() {
    +  // Set up a test bed.
    +  sandbox.innerHTML = '<div contentEditable="true">hello world</div>';
    +  initImeHandler();
    +
    +  function unshiftEvent(e) {
    +    last10Events.unshift(e.type + ':' + e.keyCode + ':' +
    +        goog.string.htmlEscape(goog.dom.getTextContent(sandbox)));
    +    last10Events.length = Math.min(last10Events.length, 10);
    +    goog.dom.getElement('logger').innerHTML = last10Events.join('<br>');
    +  }
    +
    +  var last10Events = [];
    +  goog.events.listen(
    +      imeHandler,
    +      goog.object.getValues(goog.events.ImeHandler.EventType),
    +      unshiftEvent);
    +  goog.events.listen(
    +      sandbox,
    +      ['keydown', 'textInput'],
    +      unshiftEvent);
    +}
    +
    +function assertEventsFired(var_args) {
    +  assertArrayEquals(
    +      goog.array.clone(arguments), eventsFired);
    +}
    +
    +function fireInputEvent(type) {
    +  return goog.testing.events.fireBrowserEvent(
    +      new goog.testing.events.Event(type, sandbox));
    +}
    +
    +function fireImeKeySequence() {
    +  return fireKeySequence(goog.events.KeyCodes.WIN_IME);
    +}
    +
    +function fireKeySequence(keyCode) {
    +  return (
    +      goog.testing.events.fireBrowserEvent(
    +          new goog.testing.events.Event('textInput', sandbox)) &
    +      goog.testing.events.fireKeySequence(
    +          sandbox, keyCode));
    +}
    +
    +function testHandleKeyDown_GeckoCompositionEvents() {
    +  // This test verifies that our IME functions can dispatch IME events to
    +  // InputHandler in the expected order on Gecko.
    +
    +  // Set the userAgent used for this test to Firefox.
    +  setUserAgent('GECKO');
    +  stubs.set(goog.userAgent, 'MAC', false);
    +  initImeHandler();
    +
    +  fireInputEvent('compositionstart');
    +  assertImeMode();
    +
    +  fireInputEvent('compositionupdate');
    +  fireInputEvent('compositionupdate');
    +
    +  fireInputEvent('compositionend');
    +
    +  assertEventsFired(
    +      eventTypes.START, eventTypes.UPDATE, eventTypes.UPDATE, eventTypes.END);
    +  assertNotImeMode();
    +}
    +
    +
    +/**
    + * Verifies that our IME functions can dispatch IME events to the input handler
    + * in the expected order on Chrome. jsUnitFarm does not have Linux Chrome or
    + * Mac Chrome. So, we manually change the platform and run this test three
    + * times.
    + */
    +function testChromeCompositionEventsLinux() {
    +  runChromeCompositionEvents('LINUX');
    +}
    +
    +function testChromeCompositionEventsMac() {
    +  runChromeCompositionEvents('MAC');
    +}
    +
    +function testChromeCompositionEventsWindows() {
    +  runChromeCompositionEvents('WINDOWS');
    +}
    +
    +function runChromeCompositionEvents(platform) {
    +  setUserAgent('WEBKIT');
    +  setVersion(532);
    +  stubs.set(goog.userAgent, platform, true);
    +  initImeHandler();
    +
    +  fireImeKeySequence();
    +
    +  fireInputEvent('compositionstart');
    +  assertImeMode();
    +
    +  fireInputEvent('compositionupdate');
    +  fireInputEvent('compositionupdate');
    +
    +  fireInputEvent('compositionend');
    +  assertEventsFired(
    +      eventTypes.START, eventTypes.UPDATE, eventTypes.UPDATE, eventTypes.END);
    +  assertNotImeMode();
    +}
    +
    +
    +/**
    + * Ensures that the IME mode turn on/off correctly.
    + */
    +function testHandlerKeyDownForIme_imeOnOff() {
    +  setUserAgent('IE');
    +  initImeHandler();
    +
    +  // Send a WIN_IME keyDown event and see whether IME mode turns on.
    +  fireImeKeySequence();
    +  assertImeMode();
    +
    +  // Send keyDown events which should not turn off IME mode and see whether
    +  // IME mode holds on.
    +  fireKeySequence(goog.events.KeyCodes.SHIFT);
    +  assertImeMode();
    +
    +  fireKeySequence(goog.events.KeyCodes.CTRL);
    +  assertImeMode();
    +
    +  // Send a keyDown event with keyCode = ENTER and see whether IME mode
    +  // turns off.
    +  fireKeySequence(goog.events.KeyCodes.ENTER);
    +  assertNotImeMode();
    +
    +  assertEventsFired(
    +      eventTypes.START, eventTypes.END);
    +}
    +
    +
    +/**
    + * Ensures that IME mode turns off when keyup events which are involved
    + * in commiting IME text occurred in Safari.
    + */
    +function testHandleKeyUpForSafari() {
    +  setUserAgent('WEBKIT');
    +  setVersion(531);
    +  initImeHandler();
    +
    +  fireImeKeySequence();
    +  assertImeMode();
    +
    +  fireKeySequence(goog.events.KeyCodes.ENTER);
    +  assertNotImeMode();
    +}
    +
    +
    +/**
    + * SCIM on Linux will fire WIN_IME keycodes for random characters.
    + * Fortunately, all Linux-based browsers use composition events.
    + * This test just verifies that we ignore the WIN_IME keycodes.
    + */
    +function testScimFiresWinImeKeycodesGeckoLinux() {
    +  setUserAgent('GECKO');
    +  assertScimInputIgnored();
    +}
    +
    +function testScimFiresWinImeKeycodesChromeLinux() {
    +  setUserAgent('WEBKIT');
    +  setVersion(532);
    +  assertScimInputIgnored();
    +}
    +
    +function assertScimInputIgnored() {
    +  initImeHandler();
    +
    +  fireImeKeySequence();
    +  assertNotImeMode();
    +
    +  fireInputEvent('compositionstart');
    +  assertImeMode();
    +
    +  fireImeKeySequence();
    +  assertImeMode();
    +
    +  fireInputEvent('compositionend');
    +  assertNotImeMode();
    +}
    +
    +var userAgents = ['IE', 'GECKO', 'WEBKIT'];
    +
    +function setUserAgent(userAgent) {
    +  for (var i = 0; i < userAgents.length; i++) {
    +    stubs.set(goog.userAgent, userAgents[i], userAgents[i] == userAgent);
    +  }
    +}
    +
    +function setVersion(version) {
    +  goog.userAgent.VERSION = version;
    +  goog.userAgent.isVersionOrHigherCache_ = {};
    +}
    +
    +function assertImeMode() {
    +  assertTrue('Should be in IME mode.', imeHandler.isImeMode());
    +}
    +
    +function assertNotImeMode() {
    +  assertFalse('Should not be in IME mode.', imeHandler.isImeMode());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/inputhandler.js b/mapbox-gl/closure-library/closure/goog/events/inputhandler.js
    new file mode 100644
    index 0000000000..2c82c02844
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/inputhandler.js
    @@ -0,0 +1,220 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An object that encapsulates text changed events for textareas
    + * and input element of type text and password. The event occurs after the value
    + * has been changed. The event does not occur if value was changed
    + * programmatically.<br>
    + * <br>
    + * Note: this does not guarantee the correctness of {@code keyCode} or
    + * {@code charCode}, or attempt to unify them across browsers. See
    + * {@code goog.events.KeyHandler} for that functionality<br>
    + * <br>
    + * Known issues:
    + * <ul>
    + * <li>Does not trigger for drop events on Opera due to browser bug.
    + * <li>IE doesn't have native support for input event. WebKit before version 531
    + *     doesn't have support for textareas. For those browsers an emulation mode
    + *     based on key, clipboard and drop events is used. Thus this event won't
    + *     trigger in emulation mode if text was modified by context menu commands
    + *     such as 'Undo' and 'Delete'.
    + * </ul>
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/inputhandler.html
    + */
    +
    +goog.provide('goog.events.InputHandler');
    +goog.provide('goog.events.InputHandler.EventType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This event handler will dispatch events when the user types into a text
    + * input, password input or a textarea
    + * @param {Element} element  The element that you want to listen for input
    + *     events on.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.events.InputHandler = function(element) {
    +  goog.events.InputHandler.base(this, 'constructor');
    +
    +  /**
    +   * Id of a timer used to postpone firing input event in emulation mode.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.timer_ = null;
    +
    +  /**
    +   * The element that you want to listen for input events on.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  // Determine whether input event should be emulated.
    +  // IE8 doesn't support input events. We could use property change events but
    +  // they are broken in many ways:
    +  // - Fire even if value was changed programmatically.
    +  // - Aren't always delivered. For example, if you change value or even width
    +  //   of input programmatically, next value change made by user won't fire an
    +  //   event.
    +  // IE9 supports input events when characters are inserted, but not deleted.
    +  // WebKit before version 531 did not support input events for textareas.
    +  var emulateInputEvents = goog.userAgent.IE ||
    +      (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('531') &&
    +          element.tagName == 'TEXTAREA');
    +
    +  /**
    +   * @type {goog.events.EventHandler<!goog.events.InputHandler>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  // Even if input event emulation is enabled, still listen for input events
    +  // since they may be partially supported by the browser (such as IE9).
    +  // If the input event does fire, we will be able to dispatch synchronously.
    +  // (InputHandler events being asynchronous for IE is a common issue for
    +  // cases like auto-grow textareas where they result in a quick flash of
    +  // scrollbars between the textarea content growing and it being resized to
    +  // fit.)
    +  this.eventHandler_.listen(
    +      this.element_,
    +      emulateInputEvents ?
    +          ['keydown', 'paste', 'cut', 'drop', 'input'] :
    +          'input',
    +      this);
    +};
    +goog.inherits(goog.events.InputHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum type for the events fired by the input handler
    + * @enum {string}
    + */
    +goog.events.InputHandler.EventType = {
    +  INPUT: 'input'
    +};
    +
    +
    +/**
    + * This handles the underlying events and dispatches a new event as needed.
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + */
    +goog.events.InputHandler.prototype.handleEvent = function(e) {
    +  if (e.type == 'input') {
    +    // http://stackoverflow.com/questions/18389732/changing-placeholder-triggers-input-event-in-ie-10
    +    // IE 10+ fires an input event when there are inputs with placeholders.
    +    // It fires the event with keycode 0, so if we detect it we don't
    +    // propagate the input event.
    +    if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher(10) &&
    +        e.keyCode == 0 && e.charCode == 0) {
    +      return;
    +    }
    +    // This event happens after all the other events we listen to, so cancel
    +    // an asynchronous event dispatch if we have it queued up.  Otherwise, we
    +    // will end up firing an extra event.
    +    this.cancelTimerIfSet_();
    +
    +    // Unlike other browsers, Opera fires an extra input event when an element
    +    // is blurred after the user has input into it. Since Opera doesn't fire
    +    // input event on drop, it's enough to check whether element still has focus
    +    // to suppress bogus notification.
    +    if (!goog.userAgent.OPERA || this.element_ ==
    +        goog.dom.getOwnerDocument(this.element_).activeElement) {
    +      this.dispatchEvent(this.createInputEvent_(e));
    +    }
    +  } else {
    +    // Filter out key events that don't modify text.
    +    if (e.type == 'keydown' &&
    +        !goog.events.KeyCodes.isTextModifyingKeyEvent(e)) {
    +      return;
    +    }
    +
    +    // It is still possible that pressed key won't modify the value of an
    +    // element. Storing old value will help us to detect modification but is
    +    // also a little bit dangerous. If value is changed programmatically in
    +    // another key down handler, we will detect it as user-initiated change.
    +    var valueBeforeKey = e.type == 'keydown' ? this.element_.value : null;
    +
    +    // In IE on XP, IME the element's value has already changed when we get
    +    // keydown events when the user is using an IME. In this case, we can't
    +    // check the current value normally, so we assume that it's a modifying key
    +    // event. This means that ENTER when used to commit will fire a spurious
    +    // input event, but it's better to have a false positive than let some input
    +    // slip through the cracks.
    +    if (goog.userAgent.IE && e.keyCode == goog.events.KeyCodes.WIN_IME) {
    +      valueBeforeKey = null;
    +    }
    +
    +    // Create an input event now, because when we fire it on timer, the
    +    // underlying event will already be disposed.
    +    var inputEvent = this.createInputEvent_(e);
    +
    +    // Since key down, paste, cut and drop events are fired before actual value
    +    // of the element has changed, we need to postpone dispatching input event
    +    // until value is updated.
    +    this.cancelTimerIfSet_();
    +    this.timer_ = goog.Timer.callOnce(function() {
    +      this.timer_ = null;
    +      if (this.element_.value != valueBeforeKey) {
    +        this.dispatchEvent(inputEvent);
    +      }
    +    }, 0, this);
    +  }
    +};
    +
    +
    +/**
    + * Cancels timer if it is set, does nothing otherwise.
    + * @private
    + */
    +goog.events.InputHandler.prototype.cancelTimerIfSet_ = function() {
    +  if (this.timer_ != null) {
    +    goog.Timer.clear(this.timer_);
    +    this.timer_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Creates an input event from the browser event.
    + * @param {goog.events.BrowserEvent} be A browser event.
    + * @return {!goog.events.BrowserEvent} An input event.
    + * @private
    + */
    +goog.events.InputHandler.prototype.createInputEvent_ = function(be) {
    +  var e = new goog.events.BrowserEvent(be.getBrowserEvent());
    +  e.type = goog.events.InputHandler.EventType.INPUT;
    +  return e;
    +};
    +
    +
    +/** @override */
    +goog.events.InputHandler.prototype.disposeInternal = function() {
    +  goog.events.InputHandler.base(this, 'disposeInternal');
    +  this.eventHandler_.dispose();
    +  this.cancelTimerIfSet_();
    +  delete this.element_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.html
    new file mode 100644
    index 0000000000..a03bcf9eb5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Input Handler Test
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.InputHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <input type="text" placeholder="Foo" id="input-w-placeholder" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.js
    new file mode 100644
    index 0000000000..067d407e2a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/inputhandler_test.js
    @@ -0,0 +1,95 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.InputHandlerTest');
    +goog.setTestOnly('goog.events.InputHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +var inputHandler;
    +var eventHandler;
    +
    +function setUp() {
    +  eventHandler = new goog.events.EventHandler();
    +}
    +
    +function tearDown() {
    +  goog.dispose(inputHandler);
    +  goog.dispose(eventHandler);
    +}
    +
    +function testInputWithPlaceholder() {
    +  var input = goog.dom.getElement('input-w-placeholder');
    +  inputHandler = new goog.events.InputHandler(input);
    +  var callback = listenToInput(inputHandler);
    +  fireFakeInputEvent(input);
    +  assertEquals(0, callback.getCallCount());
    +}
    +
    +function testInputWithPlaceholder_withValue() {
    +  var input = goog.dom.getElement('input-w-placeholder');
    +  inputHandler = new goog.events.InputHandler(input);
    +  var callback = listenToInput(inputHandler);
    +  input.value = 'foo';
    +  fireFakeInputEvent(input);
    +  assertEquals(0, callback.getCallCount());
    +}
    +
    +function testInputWithPlaceholder_someKeys() {
    +  var input = goog.dom.getElement('input-w-placeholder');
    +  inputHandler = new goog.events.InputHandler(input);
    +  var callback = listenToInput(inputHandler);
    +  input.focus();
    +  input.value = 'foo';
    +
    +  fireInputEvent(input, goog.events.KeyCodes.M);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function listenToInput(inputHandler) {
    +  var callback = goog.testing.recordFunction();
    +  eventHandler.listen(
    +      inputHandler,
    +      goog.events.InputHandler.EventType.INPUT,
    +      callback);
    +  return callback;
    +}
    +
    +function fireFakeInputEvent(input) {
    +  // Simulate the input event that IE fires on focus when a placeholder
    +  // is present.
    +  input.focus();
    +  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher(10)) {
    +    // IE fires an input event with keycode 0
    +    fireInputEvent(input, 0);
    +  }
    +}
    +
    +function fireInputEvent(input, keyCode) {
    +  var inputEvent = new goog.testing.events.Event(
    +      goog.events.EventType.INPUT,
    +      input);
    +  inputEvent.keyCode = keyCode;
    +  inputEvent.charCode = keyCode;
    +  goog.testing.events.fireBrowserEvent(inputEvent);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keycodes.js b/mapbox-gl/closure-library/closure/goog/events/keycodes.js
    new file mode 100644
    index 0000000000..d0ad82c495
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keycodes.js
    @@ -0,0 +1,415 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Constant declarations for common key codes.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/keyhandler.html
    + */
    +
    +goog.provide('goog.events.KeyCodes');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Key codes for common characters.
    + *
    + * This list is not localized and therefore some of the key codes are not
    + * correct for non US keyboard layouts. See comments below.
    + *
    + * @enum {number}
    + */
    +goog.events.KeyCodes = {
    +  WIN_KEY_FF_LINUX: 0,
    +  MAC_ENTER: 3,
    +  BACKSPACE: 8,
    +  TAB: 9,
    +  NUM_CENTER: 12,  // NUMLOCK on FF/Safari Mac
    +  ENTER: 13,
    +  SHIFT: 16,
    +  CTRL: 17,
    +  ALT: 18,
    +  PAUSE: 19,
    +  CAPS_LOCK: 20,
    +  ESC: 27,
    +  SPACE: 32,
    +  PAGE_UP: 33,     // also NUM_NORTH_EAST
    +  PAGE_DOWN: 34,   // also NUM_SOUTH_EAST
    +  END: 35,         // also NUM_SOUTH_WEST
    +  HOME: 36,        // also NUM_NORTH_WEST
    +  LEFT: 37,        // also NUM_WEST
    +  UP: 38,          // also NUM_NORTH
    +  RIGHT: 39,       // also NUM_EAST
    +  DOWN: 40,        // also NUM_SOUTH
    +  PRINT_SCREEN: 44,
    +  INSERT: 45,      // also NUM_INSERT
    +  DELETE: 46,      // also NUM_DELETE
    +  ZERO: 48,
    +  ONE: 49,
    +  TWO: 50,
    +  THREE: 51,
    +  FOUR: 52,
    +  FIVE: 53,
    +  SIX: 54,
    +  SEVEN: 55,
    +  EIGHT: 56,
    +  NINE: 57,
    +  FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
    +  FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
    +  FF_DASH: 173, // Firefox (Gecko) fires this for dash instead of 189
    +  QUESTION_MARK: 63, // needs localization
    +  A: 65,
    +  B: 66,
    +  C: 67,
    +  D: 68,
    +  E: 69,
    +  F: 70,
    +  G: 71,
    +  H: 72,
    +  I: 73,
    +  J: 74,
    +  K: 75,
    +  L: 76,
    +  M: 77,
    +  N: 78,
    +  O: 79,
    +  P: 80,
    +  Q: 81,
    +  R: 82,
    +  S: 83,
    +  T: 84,
    +  U: 85,
    +  V: 86,
    +  W: 87,
    +  X: 88,
    +  Y: 89,
    +  Z: 90,
    +  META: 91, // WIN_KEY_LEFT
    +  WIN_KEY_RIGHT: 92,
    +  CONTEXT_MENU: 93,
    +  NUM_ZERO: 96,
    +  NUM_ONE: 97,
    +  NUM_TWO: 98,
    +  NUM_THREE: 99,
    +  NUM_FOUR: 100,
    +  NUM_FIVE: 101,
    +  NUM_SIX: 102,
    +  NUM_SEVEN: 103,
    +  NUM_EIGHT: 104,
    +  NUM_NINE: 105,
    +  NUM_MULTIPLY: 106,
    +  NUM_PLUS: 107,
    +  NUM_MINUS: 109,
    +  NUM_PERIOD: 110,
    +  NUM_DIVISION: 111,
    +  F1: 112,
    +  F2: 113,
    +  F3: 114,
    +  F4: 115,
    +  F5: 116,
    +  F6: 117,
    +  F7: 118,
    +  F8: 119,
    +  F9: 120,
    +  F10: 121,
    +  F11: 122,
    +  F12: 123,
    +  NUMLOCK: 144,
    +  SCROLL_LOCK: 145,
    +
    +  // OS-specific media keys like volume controls and browser controls.
    +  FIRST_MEDIA_KEY: 166,
    +  LAST_MEDIA_KEY: 183,
    +
    +  SEMICOLON: 186,            // needs localization
    +  DASH: 189,                 // needs localization
    +  EQUALS: 187,               // needs localization
    +  COMMA: 188,                // needs localization
    +  PERIOD: 190,               // needs localization
    +  SLASH: 191,                // needs localization
    +  APOSTROPHE: 192,           // needs localization
    +  TILDE: 192,                // needs localization
    +  SINGLE_QUOTE: 222,         // needs localization
    +  OPEN_SQUARE_BRACKET: 219,  // needs localization
    +  BACKSLASH: 220,            // needs localization
    +  CLOSE_SQUARE_BRACKET: 221, // needs localization
    +  WIN_KEY: 224,
    +  MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
    +  MAC_WK_CMD_LEFT: 91,  // WebKit Left Command key fired, same as META
    +  MAC_WK_CMD_RIGHT: 93, // WebKit Right Command key fired, different from META
    +  WIN_IME: 229,
    +
    +  // We've seen users whose machines fire this keycode at regular one
    +  // second intervals. The common thread among these users is that
    +  // they're all using Dell Inspiron laptops, so we suspect that this
    +  // indicates a hardware/bios problem.
    +  // http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
    +  PHANTOM: 255
    +};
    +
    +
    +/**
    + * Returns true if the event contains a text modifying key.
    + * @param {goog.events.BrowserEvent} e A key event.
    + * @return {boolean} Whether it's a text modifying key.
    + */
    +goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
    +  if (e.altKey && !e.ctrlKey ||
    +      e.metaKey ||
    +      // Function keys don't generate text
    +      e.keyCode >= goog.events.KeyCodes.F1 &&
    +      e.keyCode <= goog.events.KeyCodes.F12) {
    +    return false;
    +  }
    +
    +  // The following keys are quite harmless, even in combination with
    +  // CTRL, ALT or SHIFT.
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.ALT:
    +    case goog.events.KeyCodes.CAPS_LOCK:
    +    case goog.events.KeyCodes.CONTEXT_MENU:
    +    case goog.events.KeyCodes.CTRL:
    +    case goog.events.KeyCodes.DOWN:
    +    case goog.events.KeyCodes.END:
    +    case goog.events.KeyCodes.ESC:
    +    case goog.events.KeyCodes.HOME:
    +    case goog.events.KeyCodes.INSERT:
    +    case goog.events.KeyCodes.LEFT:
    +    case goog.events.KeyCodes.MAC_FF_META:
    +    case goog.events.KeyCodes.META:
    +    case goog.events.KeyCodes.NUMLOCK:
    +    case goog.events.KeyCodes.NUM_CENTER:
    +    case goog.events.KeyCodes.PAGE_DOWN:
    +    case goog.events.KeyCodes.PAGE_UP:
    +    case goog.events.KeyCodes.PAUSE:
    +    case goog.events.KeyCodes.PHANTOM:
    +    case goog.events.KeyCodes.PRINT_SCREEN:
    +    case goog.events.KeyCodes.RIGHT:
    +    case goog.events.KeyCodes.SCROLL_LOCK:
    +    case goog.events.KeyCodes.SHIFT:
    +    case goog.events.KeyCodes.UP:
    +    case goog.events.KeyCodes.WIN_KEY:
    +    case goog.events.KeyCodes.WIN_KEY_RIGHT:
    +      return false;
    +    case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    +      return !goog.userAgent.GECKO;
    +    default:
    +      return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
    +          e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the key fires a keypress event in the current browser.
    + *
    + * Accoridng to MSDN [1] IE only fires keypress events for the following keys:
    + * - Letters: A - Z (uppercase and lowercase)
    + * - Numerals: 0 - 9
    + * - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
    + * - System: ESC, SPACEBAR, ENTER
    + *
    + * That's not entirely correct though, for instance there's no distinction
    + * between upper and lower case letters.
    + *
    + * [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
    + *
    + * Safari is similar to IE, but does not fire keypress for ESC.
    + *
    + * Additionally, IE6 does not fire keydown or keypress events for letters when
    + * the control or alt keys are held down and the shift key is not. IE7 does
    + * fire keydown in these cases, though, but not keypress.
    + *
    + * @param {number} keyCode A key code.
    + * @param {number=} opt_heldKeyCode Key code of a currently-held key.
    + * @param {boolean=} opt_shiftKey Whether the shift key is held down.
    + * @param {boolean=} opt_ctrlKey Whether the control key is held down.
    + * @param {boolean=} opt_altKey Whether the alt key is held down.
    + * @return {boolean} Whether it's a key that fires a keypress event.
    + */
    +goog.events.KeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode,
    +    opt_shiftKey, opt_ctrlKey, opt_altKey) {
    +  if (!goog.userAgent.IE &&
    +      !(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
    +    return true;
    +  }
    +
    +  if (goog.userAgent.MAC && opt_altKey) {
    +    return goog.events.KeyCodes.isCharacterKey(keyCode);
    +  }
    +
    +  // Alt but not AltGr which is represented as Alt+Ctrl.
    +  if (opt_altKey && !opt_ctrlKey) {
    +    return false;
    +  }
    +
    +  // Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
    +  // Non-IE browsers and WebKit prior to 525 won't get this far so no need to
    +  // check the user agent.
    +  if (goog.isNumber(opt_heldKeyCode)) {
    +    opt_heldKeyCode = goog.events.KeyCodes.normalizeKeyCode(opt_heldKeyCode);
    +  }
    +  if (!opt_shiftKey &&
    +      (opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
    +       opt_heldKeyCode == goog.events.KeyCodes.ALT ||
    +       goog.userAgent.MAC &&
    +       opt_heldKeyCode == goog.events.KeyCodes.META)) {
    +    return false;
    +  }
    +
    +  // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
    +  if (goog.userAgent.WEBKIT && opt_ctrlKey && opt_shiftKey) {
    +    switch (keyCode) {
    +      case goog.events.KeyCodes.BACKSLASH:
    +      case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    +      case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    +      case goog.events.KeyCodes.TILDE:
    +      case goog.events.KeyCodes.SEMICOLON:
    +      case goog.events.KeyCodes.DASH:
    +      case goog.events.KeyCodes.EQUALS:
    +      case goog.events.KeyCodes.COMMA:
    +      case goog.events.KeyCodes.PERIOD:
    +      case goog.events.KeyCodes.SLASH:
    +      case goog.events.KeyCodes.APOSTROPHE:
    +      case goog.events.KeyCodes.SINGLE_QUOTE:
    +        return false;
    +    }
    +  }
    +
    +  // When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
    +  // continues to fire keydown events as the event repeats.
    +  if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
    +    return false;
    +  }
    +
    +  switch (keyCode) {
    +    case goog.events.KeyCodes.ENTER:
    +      return true;
    +    case goog.events.KeyCodes.ESC:
    +      return !goog.userAgent.WEBKIT;
    +  }
    +
    +  return goog.events.KeyCodes.isCharacterKey(keyCode);
    +};
    +
    +
    +/**
    + * Returns true if the key produces a character.
    + * This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
    + *
    + * @param {number} keyCode A key code.
    + * @return {boolean} Whether it's a character key.
    + */
    +goog.events.KeyCodes.isCharacterKey = function(keyCode) {
    +  if (keyCode >= goog.events.KeyCodes.ZERO &&
    +      keyCode <= goog.events.KeyCodes.NINE) {
    +    return true;
    +  }
    +
    +  if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
    +      keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
    +    return true;
    +  }
    +
    +  if (keyCode >= goog.events.KeyCodes.A &&
    +      keyCode <= goog.events.KeyCodes.Z) {
    +    return true;
    +  }
    +
    +  // Safari sends zero key code for non-latin characters.
    +  if (goog.userAgent.WEBKIT && keyCode == 0) {
    +    return true;
    +  }
    +
    +  switch (keyCode) {
    +    case goog.events.KeyCodes.SPACE:
    +    case goog.events.KeyCodes.QUESTION_MARK:
    +    case goog.events.KeyCodes.NUM_PLUS:
    +    case goog.events.KeyCodes.NUM_MINUS:
    +    case goog.events.KeyCodes.NUM_PERIOD:
    +    case goog.events.KeyCodes.NUM_DIVISION:
    +    case goog.events.KeyCodes.SEMICOLON:
    +    case goog.events.KeyCodes.FF_SEMICOLON:
    +    case goog.events.KeyCodes.DASH:
    +    case goog.events.KeyCodes.EQUALS:
    +    case goog.events.KeyCodes.FF_EQUALS:
    +    case goog.events.KeyCodes.COMMA:
    +    case goog.events.KeyCodes.PERIOD:
    +    case goog.events.KeyCodes.SLASH:
    +    case goog.events.KeyCodes.APOSTROPHE:
    +    case goog.events.KeyCodes.SINGLE_QUOTE:
    +    case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
    +    case goog.events.KeyCodes.BACKSLASH:
    +    case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
    +      return true;
    +    default:
    +      return false;
    +  }
    +};
    +
    +
    +/**
    + * Normalizes key codes from OS/Browser-specific value to the general one.
    + * @param {number} keyCode The native key code.
    + * @return {number} The normalized key code.
    + */
    +goog.events.KeyCodes.normalizeKeyCode = function(keyCode) {
    +  if (goog.userAgent.GECKO) {
    +    return goog.events.KeyCodes.normalizeGeckoKeyCode(keyCode);
    +  } else if (goog.userAgent.MAC && goog.userAgent.WEBKIT) {
    +    return goog.events.KeyCodes.normalizeMacWebKitKeyCode(keyCode);
    +  } else {
    +    return keyCode;
    +  }
    +};
    +
    +
    +/**
    + * Normalizes key codes from their Gecko-specific value to the general one.
    + * @param {number} keyCode The native key code.
    + * @return {number} The normalized key code.
    + */
    +goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
    +  switch (keyCode) {
    +    case goog.events.KeyCodes.FF_EQUALS:
    +      return goog.events.KeyCodes.EQUALS;
    +    case goog.events.KeyCodes.FF_SEMICOLON:
    +      return goog.events.KeyCodes.SEMICOLON;
    +    case goog.events.KeyCodes.FF_DASH:
    +      return goog.events.KeyCodes.DASH;
    +    case goog.events.KeyCodes.MAC_FF_META:
    +      return goog.events.KeyCodes.META;
    +    case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
    +      return goog.events.KeyCodes.WIN_KEY;
    +    default:
    +      return keyCode;
    +  }
    +};
    +
    +
    +/**
    + * Normalizes key codes from their Mac WebKit-specific value to the general one.
    + * @param {number} keyCode The native key code.
    + * @return {number} The normalized key code.
    + */
    +goog.events.KeyCodes.normalizeMacWebKitKeyCode = function(keyCode) {
    +  switch (keyCode) {
    +    case goog.events.KeyCodes.MAC_WK_CMD_RIGHT:  // 93
    +      return goog.events.KeyCodes.META;          // 91
    +    default:
    +      return keyCode;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keycodes_test.html b/mapbox-gl/closure-library/closure/goog/events/keycodes_test.html
    new file mode 100644
    index 0000000000..c377d44d25
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keycodes_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   JsUnit tests for goog.events.KeyCodes
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.KeyCodesTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keycodes_test.js b/mapbox-gl/closure-library/closure/goog/events/keycodes_test.js
    new file mode 100644
    index 0000000000..ad26c25b84
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keycodes_test.js
    @@ -0,0 +1,174 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.KeyCodesTest');
    +goog.setTestOnly('goog.events.KeyCodesTest');
    +
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var KeyCodes;
    +var stubs;
    +
    +function setUpPage() {
    +  KeyCodes = goog.events.KeyCodes;
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testTextModifyingKeys() {
    +  var specialTextModifiers = goog.object.createSet(
    +      KeyCodes.BACKSPACE,
    +      KeyCodes.DELETE,
    +      KeyCodes.ENTER,
    +      KeyCodes.MAC_ENTER,
    +      KeyCodes.TAB,
    +      KeyCodes.WIN_IME);
    +
    +  if (!goog.userAgent.GECKO) {
    +    specialTextModifiers[KeyCodes.WIN_KEY_FF_LINUX] = 1;
    +  }
    +
    +  for (var keyId in KeyCodes) {
    +    var key = KeyCodes[keyId];
    +    if (goog.isFunction(key)) {
    +      // skip static methods
    +      continue;
    +    }
    +
    +    var fakeEvent = createEventWithKeyCode(key);
    +
    +    if (KeyCodes.isCharacterKey(key) || (key in specialTextModifiers)) {
    +      assertTrue('Expected key to modify text: ' + keyId,
    +          KeyCodes.isTextModifyingKeyEvent(fakeEvent));
    +    } else {
    +      assertFalse('Expected key to not modify text: ' + keyId,
    +          KeyCodes.isTextModifyingKeyEvent(fakeEvent));
    +    }
    +  }
    +
    +  for (var i = KeyCodes.FIRST_MEDIA_KEY; i <= KeyCodes.LAST_MEDIA_KEY; i++) {
    +    var fakeEvent = createEventWithKeyCode(i);
    +    assertFalse('Expected key to not modify text: ' + i,
    +        KeyCodes.isTextModifyingKeyEvent(fakeEvent));
    +  }
    +}
    +
    +function testKeyCodeZero() {
    +  var zeroEvent = createEventWithKeyCode(0);
    +  assertEquals(
    +      !goog.userAgent.GECKO,
    +      KeyCodes.isTextModifyingKeyEvent(zeroEvent));
    +  assertEquals(
    +      goog.userAgent.WEBKIT,
    +      KeyCodes.isCharacterKey(0));
    +}
    +
    +function testPhantomKey() {
    +  // KeyCode 255 deserves its own test to make sure this does not regress,
    +  // because it's so weird. See the comments in the KeyCode enum.
    +  var fakeEvent = createEventWithKeyCode(goog.events.KeyCodes.PHANTOM);
    +  assertFalse('Expected phantom key to not modify text',
    +      KeyCodes.isTextModifyingKeyEvent(fakeEvent));
    +  assertFalse(KeyCodes.isCharacterKey(fakeEvent));
    +}
    +
    +function testNonUsKeyboards() {
    +  var fakeEvent = createEventWithKeyCode(1092 /* Russian a */);
    +  assertTrue('Expected key to not modify text: 1092',
    +      KeyCodes.isTextModifyingKeyEvent(fakeEvent));
    +}
    +
    +function createEventWithKeyCode(i) {
    +  var fakeEvent = new goog.events.BrowserEvent('keydown');
    +  fakeEvent.keyCode = i;
    +  return fakeEvent;
    +}
    +
    +function testNormalizeGeckoKeyCode() {
    +  stubs.set(goog.userAgent, 'GECKO', true);
    +
    +  // Test Gecko-specific key codes.
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeGeckoKeyCode(KeyCodes.FF_EQUALS),
    +      KeyCodes.EQUALS);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.FF_EQUALS),
    +      KeyCodes.EQUALS);
    +
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeGeckoKeyCode(KeyCodes.FF_SEMICOLON),
    +      KeyCodes.SEMICOLON);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.FF_SEMICOLON),
    +      KeyCodes.SEMICOLON);
    +
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeGeckoKeyCode(KeyCodes.MAC_FF_META),
    +      KeyCodes.META);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.MAC_FF_META),
    +      KeyCodes.META);
    +
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeGeckoKeyCode(KeyCodes.WIN_KEY_FF_LINUX),
    +      KeyCodes.WIN_KEY);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.WIN_KEY_FF_LINUX),
    +      KeyCodes.WIN_KEY);
    +
    +  // Test general key codes.
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeGeckoKeyCode(KeyCodes.COMMA),
    +      KeyCodes.COMMA);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.COMMA),
    +      KeyCodes.COMMA);
    +}
    +
    +function testNormalizeMacWebKitKeyCode() {
    +  stubs.set(goog.userAgent, 'GECKO', false);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +  stubs.set(goog.userAgent, 'WEBKIT', true);
    +
    +  // Test Mac WebKit specific key codes.
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeMacWebKitKeyCode(KeyCodes.MAC_WK_CMD_LEFT),
    +      KeyCodes.META);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.MAC_WK_CMD_LEFT),
    +      KeyCodes.META);
    +
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeMacWebKitKeyCode(KeyCodes.MAC_WK_CMD_RIGHT),
    +      KeyCodes.META);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.MAC_WK_CMD_RIGHT),
    +      KeyCodes.META);
    +
    +  // Test general key codes.
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeMacWebKitKeyCode(KeyCodes.COMMA),
    +      KeyCodes.COMMA);
    +  assertEquals(
    +      goog.events.KeyCodes.normalizeKeyCode(KeyCodes.COMMA),
    +      KeyCodes.COMMA);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keyhandler.js b/mapbox-gl/closure-library/closure/goog/events/keyhandler.js
    new file mode 100644
    index 0000000000..6a7662f539
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keyhandler.js
    @@ -0,0 +1,556 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file contains a class for working with keyboard events
    + * that repeat consistently across browsers and platforms. It also unifies the
    + * key code so that it is the same in all browsers and platforms.
    + *
    + * Different web browsers have very different keyboard event handling. Most
    + * importantly is that only certain browsers repeat keydown events:
    + * IE, Opera, FF/Win32, and Safari 3 repeat keydown events.
    + * FF/Mac and Safari 2 do not.
    + *
    + * For the purposes of this code, "Safari 3" means WebKit 525+, when WebKit
    + * decided that they should try to match IE's key handling behavior.
    + * Safari 3.0.4, which shipped with Leopard (WebKit 523), has the
    + * Safari 2 behavior.
    + *
    + * Firefox, Safari, Opera prevent on keypress
    + *
    + * IE prevents on keydown
    + *
    + * Firefox does not fire keypress for shift, ctrl, alt
    + * Firefox does fire keydown for shift, ctrl, alt, meta
    + * Firefox does not repeat keydown for shift, ctrl, alt, meta
    + *
    + * Firefox does not fire keypress for up and down in an input
    + *
    + * Opera fires keypress for shift, ctrl, alt, meta
    + * Opera does not repeat keypress for shift, ctrl, alt, meta
    + *
    + * Safari 2 and 3 do not fire keypress for shift, ctrl, alt
    + * Safari 2 does not fire keydown for shift, ctrl, alt
    + * Safari 3 *does* fire keydown for shift, ctrl, alt
    + *
    + * IE provides the keycode for keyup/down events and the charcode (in the
    + * keycode field) for keypress.
    + *
    + * Mozilla provides the keycode for keyup/down and the charcode for keypress
    + * unless it's a non text modifying key in which case the keycode is provided.
    + *
    + * Safari 3 provides the keycode and charcode for all events.
    + *
    + * Opera provides the keycode for keyup/down event and either the charcode or
    + * the keycode (in the keycode field) for keypress events.
    + *
    + * Firefox x11 doesn't fire keydown events if a another key is already held down
    + * until the first key is released. This can cause a key event to be fired with
    + * a keyCode for the first key and a charCode for the second key.
    + *
    + * Safari in keypress
    + *
    + *        charCode keyCode which
    + * ENTER:       13      13    13
    + * F1:       63236   63236 63236
    + * F8:       63243   63243 63243
    + * ...
    + * p:          112     112   112
    + * P:           80      80    80
    + *
    + * Firefox, keypress:
    + *
    + *        charCode keyCode which
    + * ENTER:        0      13    13
    + * F1:           0     112     0
    + * F8:           0     119     0
    + * ...
    + * p:          112       0   112
    + * P:           80       0    80
    + *
    + * Opera, Mac+Win32, keypress:
    + *
    + *         charCode keyCode which
    + * ENTER: undefined      13    13
    + * F1:    undefined     112     0
    + * F8:    undefined     119     0
    + * ...
    + * p:     undefined     112   112
    + * P:     undefined      80    80
    + *
    + * IE7, keydown
    + *
    + *         charCode keyCode     which
    + * ENTER: undefined      13 undefined
    + * F1:    undefined     112 undefined
    + * F8:    undefined     119 undefined
    + * ...
    + * p:     undefined      80 undefined
    + * P:     undefined      80 undefined
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/keyhandler.html
    + */
    +
    +goog.provide('goog.events.KeyEvent');
    +goog.provide('goog.events.KeyHandler');
    +goog.provide('goog.events.KeyHandler.EventType');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A wrapper around an element that you want to listen to keyboard events on.
    + * @param {Element|Document=} opt_element The element or document to listen on.
    + * @param {boolean=} opt_capture Whether to listen for browser events in
    + *     capture phase (defaults to false).
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.events.KeyHandler = function(opt_element, opt_capture) {
    +  goog.events.EventTarget.call(this);
    +
    +  if (opt_element) {
    +    this.attach(opt_element, opt_capture);
    +  }
    +};
    +goog.inherits(goog.events.KeyHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * This is the element that we will listen to the real keyboard events on.
    + * @type {Element|Document|null}
    + * @private
    + */
    +goog.events.KeyHandler.prototype.element_ = null;
    +
    +
    +/**
    + * The key for the key press listener.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.events.KeyHandler.prototype.keyPressKey_ = null;
    +
    +
    +/**
    + * The key for the key down listener.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.events.KeyHandler.prototype.keyDownKey_ = null;
    +
    +
    +/**
    + * The key for the key up listener.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.events.KeyHandler.prototype.keyUpKey_ = null;
    +
    +
    +/**
    + * Used to detect keyboard repeat events.
    + * @private
    + * @type {number}
    + */
    +goog.events.KeyHandler.prototype.lastKey_ = -1;
    +
    +
    +/**
    + * Keycode recorded for key down events. As most browsers don't report the
    + * keycode in the key press event we need to record it in the key down phase.
    + * @private
    + * @type {number}
    + */
    +goog.events.KeyHandler.prototype.keyCode_ = -1;
    +
    +
    +/**
    + * Alt key recorded for key down events. FF on Mac does not report the alt key
    + * flag in the key press event, we need to record it in the key down phase.
    + * @type {boolean}
    + * @private
    + */
    +goog.events.KeyHandler.prototype.altKey_ = false;
    +
    +
    +/**
    + * Enum type for the events fired by the key handler
    + * @enum {string}
    + */
    +goog.events.KeyHandler.EventType = {
    +  KEY: 'key'
    +};
    +
    +
    +/**
    + * An enumeration of key codes that Safari 2 does incorrectly
    + * @type {Object}
    + * @private
    + */
    +goog.events.KeyHandler.safariKey_ = {
    +  '3': goog.events.KeyCodes.ENTER, // 13
    +  '12': goog.events.KeyCodes.NUMLOCK, // 144
    +  '63232': goog.events.KeyCodes.UP, // 38
    +  '63233': goog.events.KeyCodes.DOWN, // 40
    +  '63234': goog.events.KeyCodes.LEFT, // 37
    +  '63235': goog.events.KeyCodes.RIGHT, // 39
    +  '63236': goog.events.KeyCodes.F1, // 112
    +  '63237': goog.events.KeyCodes.F2, // 113
    +  '63238': goog.events.KeyCodes.F3, // 114
    +  '63239': goog.events.KeyCodes.F4, // 115
    +  '63240': goog.events.KeyCodes.F5, // 116
    +  '63241': goog.events.KeyCodes.F6, // 117
    +  '63242': goog.events.KeyCodes.F7, // 118
    +  '63243': goog.events.KeyCodes.F8, // 119
    +  '63244': goog.events.KeyCodes.F9, // 120
    +  '63245': goog.events.KeyCodes.F10, // 121
    +  '63246': goog.events.KeyCodes.F11, // 122
    +  '63247': goog.events.KeyCodes.F12, // 123
    +  '63248': goog.events.KeyCodes.PRINT_SCREEN, // 44
    +  '63272': goog.events.KeyCodes.DELETE, // 46
    +  '63273': goog.events.KeyCodes.HOME, // 36
    +  '63275': goog.events.KeyCodes.END, // 35
    +  '63276': goog.events.KeyCodes.PAGE_UP, // 33
    +  '63277': goog.events.KeyCodes.PAGE_DOWN, // 34
    +  '63289': goog.events.KeyCodes.NUMLOCK, // 144
    +  '63302': goog.events.KeyCodes.INSERT // 45
    +};
    +
    +
    +/**
    + * An enumeration of key identifiers currently part of the W3C draft for DOM3
    + * and their mappings to keyCodes.
    + * http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set
    + * This is currently supported in Safari and should be platform independent.
    + * @type {Object}
    + * @private
    + */
    +goog.events.KeyHandler.keyIdentifier_ = {
    +  'Up': goog.events.KeyCodes.UP, // 38
    +  'Down': goog.events.KeyCodes.DOWN, // 40
    +  'Left': goog.events.KeyCodes.LEFT, // 37
    +  'Right': goog.events.KeyCodes.RIGHT, // 39
    +  'Enter': goog.events.KeyCodes.ENTER, // 13
    +  'F1': goog.events.KeyCodes.F1, // 112
    +  'F2': goog.events.KeyCodes.F2, // 113
    +  'F3': goog.events.KeyCodes.F3, // 114
    +  'F4': goog.events.KeyCodes.F4, // 115
    +  'F5': goog.events.KeyCodes.F5, // 116
    +  'F6': goog.events.KeyCodes.F6, // 117
    +  'F7': goog.events.KeyCodes.F7, // 118
    +  'F8': goog.events.KeyCodes.F8, // 119
    +  'F9': goog.events.KeyCodes.F9, // 120
    +  'F10': goog.events.KeyCodes.F10, // 121
    +  'F11': goog.events.KeyCodes.F11, // 122
    +  'F12': goog.events.KeyCodes.F12, // 123
    +  'U+007F': goog.events.KeyCodes.DELETE, // 46
    +  'Home': goog.events.KeyCodes.HOME, // 36
    +  'End': goog.events.KeyCodes.END, // 35
    +  'PageUp': goog.events.KeyCodes.PAGE_UP, // 33
    +  'PageDown': goog.events.KeyCodes.PAGE_DOWN, // 34
    +  'Insert': goog.events.KeyCodes.INSERT // 45
    +};
    +
    +
    +/**
    + * If true, the KeyEvent fires on keydown. Otherwise, it fires on keypress.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.events.KeyHandler.USES_KEYDOWN_ = goog.userAgent.IE ||
    +    goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525');
    +
    +
    +/**
    + * If true, the alt key flag is saved during the key down and reused when
    + * handling the key press. FF on Mac does not set the alt flag in the key press
    + * event.
    + * @type {boolean}
    + * @private
    + */
    +goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_ = goog.userAgent.MAC &&
    +    goog.userAgent.GECKO;
    +
    +
    +/**
    + * Records the keycode for browsers that only returns the keycode for key up/
    + * down events. For browser/key combinations that doesn't trigger a key pressed
    + * event it also fires the patched key event.
    + * @param {goog.events.BrowserEvent} e The key down event.
    + * @private
    + */
    +goog.events.KeyHandler.prototype.handleKeyDown_ = function(e) {
    +  // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window
    +  // before we've caught a key-up event.  If the last-key was one of these we
    +  // reset the state.
    +  if (goog.userAgent.WEBKIT) {
    +    if (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||
    +        this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey ||
    +        goog.userAgent.MAC &&
    +        this.lastKey_ == goog.events.KeyCodes.META && !e.metaKey) {
    +      this.lastKey_ = -1;
    +      this.keyCode_ = -1;
    +    }
    +  }
    +
    +  if (this.lastKey_ == -1) {
    +    if (e.ctrlKey && e.keyCode != goog.events.KeyCodes.CTRL) {
    +      this.lastKey_ = goog.events.KeyCodes.CTRL;
    +    } else if (e.altKey && e.keyCode != goog.events.KeyCodes.ALT) {
    +      this.lastKey_ = goog.events.KeyCodes.ALT;
    +    } else if (e.metaKey && e.keyCode != goog.events.KeyCodes.META) {
    +      this.lastKey_ = goog.events.KeyCodes.META;
    +    }
    +  }
    +
    +  if (goog.events.KeyHandler.USES_KEYDOWN_ &&
    +      !goog.events.KeyCodes.firesKeyPressEvent(e.keyCode,
    +          this.lastKey_, e.shiftKey, e.ctrlKey, e.altKey)) {
    +    this.handleEvent(e);
    +  } else {
    +    this.keyCode_ = goog.events.KeyCodes.normalizeKeyCode(e.keyCode);
    +    if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
    +      this.altKey_ = e.altKey;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Resets the stored previous values. Needed to be called for webkit which will
    + * not generate a key up for meta key operations. This should only be called
    + * when having finished with repeat key possiblities.
    + */
    +goog.events.KeyHandler.prototype.resetState = function() {
    +  this.lastKey_ = -1;
    +  this.keyCode_ = -1;
    +};
    +
    +
    +/**
    + * Clears the stored previous key value, resetting the key repeat status. Uses
    + * -1 because the Safari 3 Windows beta reports 0 for certain keys (like Home
    + * and End.)
    + * @param {goog.events.BrowserEvent} e The keyup event.
    + * @private
    + */
    +goog.events.KeyHandler.prototype.handleKeyup_ = function(e) {
    +  this.resetState();
    +  this.altKey_ = e.altKey;
    +};
    +
    +
    +/**
    + * Handles the events on the element.
    + * @param {goog.events.BrowserEvent} e  The keyboard event sent from the
    + *     browser.
    + */
    +goog.events.KeyHandler.prototype.handleEvent = function(e) {
    +  var be = e.getBrowserEvent();
    +  var keyCode, charCode;
    +  var altKey = be.altKey;
    +
    +  // IE reports the character code in the keyCode field for keypress events.
    +  // There are two exceptions however, Enter and Escape.
    +  if (goog.userAgent.IE && e.type == goog.events.EventType.KEYPRESS) {
    +    keyCode = this.keyCode_;
    +    charCode = keyCode != goog.events.KeyCodes.ENTER &&
    +        keyCode != goog.events.KeyCodes.ESC ?
    +            be.keyCode : 0;
    +
    +  // Safari reports the character code in the keyCode field for keypress
    +  // events but also has a charCode field.
    +  } else if (goog.userAgent.WEBKIT &&
    +      e.type == goog.events.EventType.KEYPRESS) {
    +    keyCode = this.keyCode_;
    +    charCode = be.charCode >= 0 && be.charCode < 63232 &&
    +        goog.events.KeyCodes.isCharacterKey(keyCode) ?
    +            be.charCode : 0;
    +
    +  // Opera reports the keycode or the character code in the keyCode field.
    +  } else if (goog.userAgent.OPERA) {
    +    keyCode = this.keyCode_;
    +    charCode = goog.events.KeyCodes.isCharacterKey(keyCode) ?
    +        be.keyCode : 0;
    +
    +  // Mozilla reports the character code in the charCode field.
    +  } else {
    +    keyCode = be.keyCode || this.keyCode_;
    +    charCode = be.charCode || 0;
    +    if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
    +      altKey = this.altKey_;
    +    }
    +    // On the Mac, shift-/ triggers a question mark char code and no key code
    +    // (normalized to WIN_KEY), so we synthesize the latter.
    +    if (goog.userAgent.MAC &&
    +        charCode == goog.events.KeyCodes.QUESTION_MARK &&
    +        keyCode == goog.events.KeyCodes.WIN_KEY) {
    +      keyCode = goog.events.KeyCodes.SLASH;
    +    }
    +  }
    +
    +  keyCode = goog.events.KeyCodes.normalizeKeyCode(keyCode);
    +  var key = keyCode;
    +  var keyIdentifier = be.keyIdentifier;
    +
    +  // Correct the key value for certain browser-specific quirks.
    +  if (keyCode) {
    +    if (keyCode >= 63232 && keyCode in goog.events.KeyHandler.safariKey_) {
    +      // NOTE(nicksantos): Safari 3 has fixed this problem,
    +      // this is only needed for Safari 2.
    +      key = goog.events.KeyHandler.safariKey_[keyCode];
    +    } else {
    +
    +      // Safari returns 25 for Shift+Tab instead of 9.
    +      if (keyCode == 25 && e.shiftKey) {
    +        key = 9;
    +      }
    +    }
    +  } else if (keyIdentifier &&
    +             keyIdentifier in goog.events.KeyHandler.keyIdentifier_) {
    +    // This is needed for Safari Windows because it currently doesn't give a
    +    // keyCode/which for non printable keys.
    +    key = goog.events.KeyHandler.keyIdentifier_[keyIdentifier];
    +  }
    +
    +  // If we get the same keycode as a keydown/keypress without having seen a
    +  // keyup event, then this event was caused by key repeat.
    +  var repeat = key == this.lastKey_;
    +  this.lastKey_ = key;
    +
    +  var event = new goog.events.KeyEvent(key, charCode, repeat, be);
    +  event.altKey = altKey;
    +  this.dispatchEvent(event);
    +};
    +
    +
    +/**
    + * Returns the element listened on for the real keyboard events.
    + * @return {Element|Document|null} The element listened on for the real
    + *     keyboard events.
    + */
    +goog.events.KeyHandler.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Adds the proper key event listeners to the element.
    + * @param {Element|Document} element The element to listen on.
    + * @param {boolean=} opt_capture Whether to listen for browser events in
    + *     capture phase (defaults to false).
    + */
    +goog.events.KeyHandler.prototype.attach = function(element, opt_capture) {
    +  if (this.keyUpKey_) {
    +    this.detach();
    +  }
    +
    +  this.element_ = element;
    +
    +  this.keyPressKey_ = goog.events.listen(this.element_,
    +                                         goog.events.EventType.KEYPRESS,
    +                                         this,
    +                                         opt_capture);
    +
    +  // Most browsers (Safari 2 being the notable exception) doesn't include the
    +  // keyCode in keypress events (IE has the char code in the keyCode field and
    +  // Mozilla only included the keyCode if there's no charCode). Thus we have to
    +  // listen for keydown to capture the keycode.
    +  this.keyDownKey_ = goog.events.listen(this.element_,
    +                                        goog.events.EventType.KEYDOWN,
    +                                        this.handleKeyDown_,
    +                                        opt_capture,
    +                                        this);
    +
    +
    +  this.keyUpKey_ = goog.events.listen(this.element_,
    +                                      goog.events.EventType.KEYUP,
    +                                      this.handleKeyup_,
    +                                      opt_capture,
    +                                      this);
    +};
    +
    +
    +/**
    + * Removes the listeners that may exist.
    + */
    +goog.events.KeyHandler.prototype.detach = function() {
    +  if (this.keyPressKey_) {
    +    goog.events.unlistenByKey(this.keyPressKey_);
    +    goog.events.unlistenByKey(this.keyDownKey_);
    +    goog.events.unlistenByKey(this.keyUpKey_);
    +    this.keyPressKey_ = null;
    +    this.keyDownKey_ = null;
    +    this.keyUpKey_ = null;
    +  }
    +  this.element_ = null;
    +  this.lastKey_ = -1;
    +  this.keyCode_ = -1;
    +};
    +
    +
    +/** @override */
    +goog.events.KeyHandler.prototype.disposeInternal = function() {
    +  goog.events.KeyHandler.superClass_.disposeInternal.call(this);
    +  this.detach();
    +};
    +
    +
    +
    +/**
    + * This class is used for the goog.events.KeyHandler.EventType.KEY event and
    + * it overrides the key code with the fixed key code.
    + * @param {number} keyCode The adjusted key code.
    + * @param {number} charCode The unicode character code.
    + * @param {boolean} repeat Whether this event was generated by keyboard repeat.
    + * @param {Event} browserEvent Browser event object.
    + * @constructor
    + * @extends {goog.events.BrowserEvent}
    + * @final
    + */
    +goog.events.KeyEvent = function(keyCode, charCode, repeat, browserEvent) {
    +  goog.events.BrowserEvent.call(this, browserEvent);
    +  this.type = goog.events.KeyHandler.EventType.KEY;
    +
    +  /**
    +   * Keycode of key press.
    +   * @type {number}
    +   */
    +  this.keyCode = keyCode;
    +
    +  /**
    +   * Unicode character code.
    +   * @type {number}
    +   */
    +  this.charCode = charCode;
    +
    +  /**
    +   * True if this event was generated by keyboard auto-repeat (i.e., the user is
    +   * holding the key down.)
    +   * @type {boolean}
    +   */
    +  this.repeat = repeat;
    +};
    +goog.inherits(goog.events.KeyEvent, goog.events.BrowserEvent);
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.html
    new file mode 100644
    index 0000000000..08ef05a1e9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.KeyHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.KeyEventTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.js
    new file mode 100644
    index 0000000000..87467beb8e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keyhandler_test.js
    @@ -0,0 +1,728 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.KeyEventTest');
    +goog.setTestOnly('goog.events.KeyEventTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  // Have this based on a fictitious DOCUMENT_MODE constant.
    +  goog.userAgent.isDocumentMode = function(mode) {
    +    return mode <= goog.userAgent.DOCUMENT_MODE;
    +  };
    +}
    +
    +
    +/**
    + * Tests the key handler for the IE 8 and lower behavior.
    + */
    +function testIe8StyleKeyHandling() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = true;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.VERSION = 8;
    +  goog.userAgent.DOCUMENT_MODE = 8;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +
    +  assertIe8StyleKeyHandling();
    +}
    +
    +
    +/**
    + * Tests the key handler for the IE 8 and lower behavior.
    + */
    +function testIe8StyleKeyHandlingInIe9DocumentMode() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = true;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.VERSION = 9; // Try IE9 in IE8 document mode.
    +  goog.userAgent.DOCUMENT_MODE = 8;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +
    +  assertIe8StyleKeyHandling();
    +}
    +
    +function assertIe8StyleKeyHandling() {
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ENTER);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter should fire a key event with the keycode 13',
    +               goog.events.KeyCodes.ENTER,
    +               keyEvent.keyCode);
    +  assertEquals('Enter should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ESC);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ESC);
    +  assertEquals('Esc should fire a key event with the keycode 27',
    +               goog.events.KeyCodes.ESC,
    +               keyEvent.keyCode);
    +  assertEquals('Esc should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.UP);
    +  assertEquals('Up should fire a key event with the keycode 38',
    +               goog.events.KeyCodes.UP,
    +               keyEvent.keyCode);
    +  assertEquals('Up should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.SEVEN, undefined, undefined,
    +      undefined, undefined, true);
    +  fireKeyPress(keyHandler, 38, undefined, undefined, undefined, undefined,
    +      true);
    +  assertEquals('Shift+7 should fire a key event with the keycode 55',
    +               goog.events.KeyCodes.SEVEN,
    +               keyEvent.keyCode);
    +  assertEquals('Shift+7 should fire a key event with the charcode 38',
    +               38,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 97);
    +  assertEquals('Lower case a should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Lower case a should fire a key event with the charcode 97',
    +               97,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 65);
    +  assertEquals('Upper case A should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Upper case A should fire a key event with the charcode 65',
    +               65,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.DELETE);
    +  assertEquals('Delete should fire a key event with the keycode 46',
    +               goog.events.KeyCodes.DELETE,
    +               keyEvent.keyCode);
    +  assertEquals('Delete should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.PERIOD);
    +  fireKeyPress(keyHandler, 46);
    +  assertEquals('Period should fire a key event with the keycode 190',
    +               goog.events.KeyCodes.PERIOD,
    +               keyEvent.keyCode);
    +  assertEquals('Period should fire a key event with the charcode 46',
    +               46,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.CTRL);
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  assertEquals('A with control down should fire a key event',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +
    +  // On IE, when Ctrl+<key> is held down, there is a KEYDOWN, a KEYPRESS, and
    +  // then a series of KEYDOWN events for each repeat.
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.B, undefined, undefined, true);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.B, undefined, undefined,
    +      true);
    +  assertEquals('B with control down should fire a key event',
    +               goog.events.KeyCodes.B,
    +               keyEvent.keyCode);
    +  assertTrue('Ctrl should be down.', keyEvent.ctrlKey);
    +  assertFalse('Should not have repeat=true on the first key press.',
    +      keyEvent.repeat);
    +  // Fire one repeated keydown event.
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.B, undefined, undefined, true);
    +  assertEquals('A with control down should fire a key event',
    +               goog.events.KeyCodes.B,
    +               keyEvent.keyCode);
    +  assertTrue('Should have repeat=true on key repeat.',
    +      keyEvent.repeat);
    +  assertTrue('Ctrl should be down.', keyEvent.ctrlKey);
    +}
    +
    +
    +/**
    + * Tests special cases for IE9.
    + */
    +function testIe9StyleKeyHandling() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = true;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.VERSION = 9;
    +  goog.userAgent.DOCUMENT_MODE = 9;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ENTER);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter should fire a key event with the keycode 13',
    +               goog.events.KeyCodes.ENTER,
    +               keyEvent.keyCode);
    +  assertEquals('Enter should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +}
    +
    +
    +/**
    + * Tests the key handler for the Gecko behavior.
    + */
    +function testGeckoStyleKeyHandling() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = false;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ENTER);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter should fire a key event with the keycode 13',
    +               goog.events.KeyCodes.ENTER,
    +               keyEvent.keyCode);
    +  assertEquals('Enter should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ESC);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ESC);
    +  assertEquals('Esc should fire a key event with the keycode 27',
    +               goog.events.KeyCodes.ESC,
    +               keyEvent.keyCode);
    +  assertEquals('Esc should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.UP);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.UP);
    +  assertEquals('Up should fire a key event with the keycode 38',
    +               goog.events.KeyCodes.UP,
    +               keyEvent.keyCode);
    +  assertEquals('Up should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.SEVEN, undefined, undefined,
    +      undefined, undefined, true);
    +  fireKeyPress(keyHandler, undefined, 38, undefined, undefined, undefined,
    +      true);
    +  assertEquals('Shift+7 should fire a key event with the keycode 55',
    +               goog.events.KeyCodes.SEVEN,
    +               keyEvent.keyCode);
    +  assertEquals('Shift+7 should fire a key event with the charcode 38',
    +               38,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, undefined, 97);
    +  assertEquals('Lower case a should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Lower case a should fire a key event with the charcode 97',
    +               97,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, undefined, 65);
    +  assertEquals('Upper case A should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Upper case A should fire a key event with the charcode 65',
    +               65,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.DELETE);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.DELETE);
    +  assertEquals('Delete should fire a key event with the keycode 46',
    +               goog.events.KeyCodes.DELETE,
    +               keyEvent.keyCode);
    +  assertEquals('Delete should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.PERIOD);
    +  fireKeyPress(keyHandler, undefined, 46);
    +  assertEquals('Period should fire a key event with the keycode 190',
    +               goog.events.KeyCodes.PERIOD,
    +               keyEvent.keyCode);
    +  assertEquals('Period should fire a key event with the charcode 46',
    +               46,
    +               keyEvent.charCode);
    +}
    +
    +
    +/**
    + * Tests the key handler for the Safari 3 behavior.
    + */
    +function testSafari3StyleKeyHandling() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = true;
    +  goog.userAgent.MAC = true;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +  goog.userAgent.VERSION = 525.3;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  // Make sure all events are caught while testing
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ENTER);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter should fire a key event with the keycode 13',
    +               goog.events.KeyCodes.ENTER,
    +               keyEvent.keyCode);
    +  assertEquals('Enter should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +  fireKeyUp(keyHandler, goog.events.KeyCodes.ENTER);
    +
    +  // Add a listener to ensure that an extra ENTER event is not dispatched
    +  // by a subsequent keypress.
    +  var enterCheck = goog.events.listen(keyHandler,
    +      goog.events.KeyHandler.EventType.KEY,
    +      function(e) {
    +        assertNotEquals('Unexpected ENTER keypress dispatched',
    +            e.keyCode, goog.events.KeyCodes.ENTER);
    +      });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ESC);
    +  assertEquals('Esc should fire a key event with the keycode 27',
    +               goog.events.KeyCodes.ESC,
    +               keyEvent.keyCode);
    +  assertEquals('Esc should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ESC);
    +  goog.events.unlistenByKey(enterCheck);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.UP);
    +  assertEquals('Up should fire a key event with the keycode 38',
    +               goog.events.KeyCodes.UP,
    +               keyEvent.keyCode);
    +  assertEquals('Up should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.SEVEN, undefined, undefined,
    +      undefined, undefined, true);
    +  fireKeyPress(keyHandler, 38, 38, undefined, undefined, undefined, true);
    +  assertEquals('Shift+7 should fire a key event with the keycode 55',
    +               goog.events.KeyCodes.SEVEN,
    +               keyEvent.keyCode);
    +  assertEquals('Shift+7 should fire a key event with the charcode 38',
    +               38,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 97, 97);
    +  assertEquals('Lower case a should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Lower case a should fire a key event with the charcode 97',
    +               97,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 65, 65);
    +  assertEquals('Upper case A should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Upper case A should fire a key event with the charcode 65',
    +               65,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.CTRL);
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A, null, null, true /*ctrl*/);
    +  assertEquals('A with control down should fire a key event',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +
    +  // Test that Alt-Tab outside the window doesn't break things.
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ALT);
    +  keyEvent.keyCode = -1;  // Reset the event.
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  assertEquals('Should not have dispatched an Alt-A', -1, keyEvent.keyCode);
    +  fireKeyPress(keyHandler, 65, 65);
    +  assertEquals('Alt should be ignored since it isn\'t currently depressed',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.DELETE);
    +  assertEquals('Delete should fire a key event with the keycode 46',
    +               goog.events.KeyCodes.DELETE,
    +               keyEvent.keyCode);
    +  assertEquals('Delete should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.PERIOD);
    +  fireKeyPress(keyHandler, 46, 46);
    +  assertEquals('Period should fire a key event with the keycode 190',
    +               goog.events.KeyCodes.PERIOD,
    +               keyEvent.keyCode);
    +  assertEquals('Period should fire a key event with the charcode 46',
    +               46,
    +               keyEvent.charCode);
    +
    +  // Safari sends zero key code for non-latin characters.
    +  fireKeyDown(keyHandler, 0, 0);
    +  fireKeyPress(keyHandler, 1092, 1092);
    +  assertEquals('Cyrillic small letter "Ef" should fire a key event with ' +
    +                   'the keycode 0',
    +               0,
    +               keyEvent.keyCode);
    +  assertEquals('Cyrillic small letter "Ef" should fire a key event with ' +
    +                   'the charcode 1092',
    +               1092,
    +               keyEvent.charCode);
    +}
    +
    +
    +/**
    + * Tests the key handler for the Opera behavior.
    + */
    +function testOperaStyleKeyHandling() {
    +  goog.userAgent.OPERA = true;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = false;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ENTER);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter should fire a key event with the keycode 13',
    +               goog.events.KeyCodes.ENTER,
    +               keyEvent.keyCode);
    +  assertEquals('Enter should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.ESC);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.ESC);
    +  assertEquals('Esc should fire a key event with the keycode 27',
    +               goog.events.KeyCodes.ESC,
    +               keyEvent.keyCode);
    +  assertEquals('Esc should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.UP);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.UP);
    +  assertEquals('Up should fire a key event with the keycode 38',
    +               goog.events.KeyCodes.UP,
    +               keyEvent.keyCode);
    +  assertEquals('Up should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.SEVEN, undefined, undefined,
    +      undefined, undefined, true);
    +  fireKeyPress(keyHandler, 38, undefined, undefined, undefined, undefined,
    +      true);
    +  assertEquals('Shift+7 should fire a key event with the keycode 55',
    +               goog.events.KeyCodes.SEVEN,
    +               keyEvent.keyCode);
    +  assertEquals('Shift+7 should fire a key event with the charcode 38',
    +               38,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 97);
    +  assertEquals('Lower case a should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Lower case a should fire a key event with the charcode 97',
    +               97,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A);
    +  fireKeyPress(keyHandler, 65);
    +  assertEquals('Upper case A should fire a key event with the keycode 65',
    +               goog.events.KeyCodes.A,
    +               keyEvent.keyCode);
    +  assertEquals('Upper case A should fire a key event with the charcode 65',
    +               65,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.DELETE);
    +  fireKeyPress(keyHandler, goog.events.KeyCodes.DELETE);
    +  assertEquals('Delete should fire a key event with the keycode 46',
    +               goog.events.KeyCodes.DELETE,
    +               keyEvent.keyCode);
    +  assertEquals('Delete should fire a key event with the charcode 0',
    +               0,
    +               keyEvent.charCode);
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.PERIOD);
    +  fireKeyPress(keyHandler, 46);
    +  assertEquals('Period should fire a key event with the keycode 190',
    +               goog.events.KeyCodes.PERIOD,
    +               keyEvent.keyCode);
    +  assertEquals('Period should fire a key event with the charcode 46',
    +               46,
    +               keyEvent.charCode);
    +}
    +
    +function testGeckoOnMacAltHandling() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = true;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_ = true;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.COMMA, 0, null, false,
    +      true, false);
    +  fireKeyPress(keyHandler, 0, 8804, null, false, false, false);
    +  assertEquals('should fire a key event with COMMA',
    +      goog.events.KeyCodes.COMMA,
    +      keyEvent.keyCode);
    +  assertEquals('should fire a key event with alt key set',
    +      true,
    +      keyEvent.altKey);
    +
    +  // Scenario: alt down, a down, a press, a up (should say alt is true),
    +  // alt up.
    +  keyEvent = undefined;
    +  fireKeyDown(keyHandler, 18, 0, null, false, true, false);
    +  fireKeyDown(keyHandler, goog.events.KeyCodes.A, 0, null, false, true,
    +      false);
    +  fireKeyPress(keyHandler, 0, 229, null, false, false, false);
    +  assertEquals('should fire a key event with alt key set',
    +      true,
    +      keyEvent.altKey);
    +  fireKeyUp(keyHandler, 0, 229, null, false, true, false);
    +  assertEquals('alt key should still be set',
    +      true,
    +      keyEvent.altKey);
    +  fireKeyUp(keyHandler, 18, 0, null, false, false, false);
    +}
    +
    +function testGeckoEqualSign() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = false;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, 61, 0);
    +  fireKeyPress(keyHandler, 0, 61);
    +  assertEquals('= should fire should fire a key event with the keyCode 187',
    +               goog.events.KeyCodes.EQUALS,
    +               keyEvent.keyCode);
    +  assertEquals('= should fire a key event with the charCode 61',
    +               goog.events.KeyCodes.FF_EQUALS,
    +               keyEvent.charCode);
    +}
    +
    +function testMacGeckoSlash() {
    +  goog.userAgent.OPERA = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.CAMINO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.MAC = true;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = false;
    +
    +  var keyEvent, keyHandler = new goog.events.KeyHandler();
    +  goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +      function(e) { keyEvent = e; });
    +
    +  fireKeyDown(keyHandler, 0, 63, null, false, false, true);
    +  fireKeyPress(keyHandler, 0, 63, null, false, false, true);
    +  assertEquals('/ should fire a key event with the keyCode 191',
    +               goog.events.KeyCodes.SLASH,
    +               keyEvent.keyCode);
    +  assertEquals('? should fire a key event with the charCode 63',
    +               goog.events.KeyCodes.QUESTION_MARK,
    +               keyEvent.charCode);
    +}
    +
    +function testGetElement() {
    +  var target = goog.dom.createDom('div');
    +  var target2 = goog.dom.createDom('div');
    +  var keyHandler = new goog.events.KeyHandler();
    +  assertNull(keyHandler.getElement());
    +
    +  keyHandler.attach(target);
    +  assertEquals(target, keyHandler.getElement());
    +
    +  keyHandler.attach(target2);
    +  assertNotEquals(target, keyHandler.getElement());
    +  assertEquals(target2, keyHandler.getElement());
    +
    +  var doc = goog.dom.getDocument();
    +  keyHandler.attach(doc);
    +  assertEquals(doc, keyHandler.getElement());
    +
    +  keyHandler = new goog.events.KeyHandler(doc);
    +  assertEquals(doc, keyHandler.getElement());
    +
    +  keyHandler = new goog.events.KeyHandler(target);
    +  assertEquals(target, keyHandler.getElement());
    +}
    +
    +function testDetach() {
    +  var target = goog.dom.createDom('div');
    +  var keyHandler = new goog.events.KeyHandler(target);
    +  assertEquals(target, keyHandler.getElement());
    +
    +  fireKeyDown(keyHandler, 0, 63, null, false, false, true);
    +  fireKeyPress(keyHandler, 0, 63, null, false, false, true);
    +  keyHandler.detach();
    +
    +  assertNull(keyHandler.getElement());
    +  // All listeners should be cleared.
    +  assertNull(keyHandler.keyDownKey_);
    +  assertNull(keyHandler.keyPressKey_);
    +  assertNull(keyHandler.keyUpKey_);
    +  // All key related state should be cleared.
    +  assertEquals('Last key should be -1', -1, keyHandler.lastKey_);
    +  assertEquals('keycode should be -1', -1, keyHandler.keyCode_);
    +}
    +
    +function testCapturePhase() {
    +  var gotInCapturePhase;
    +  var gotInBubblePhase;
    +
    +  var target = goog.dom.createDom('div');
    +  goog.events.listen(
    +      new goog.events.KeyHandler(target, false /* bubble */),
    +      goog.events.KeyHandler.EventType.KEY,
    +      function() {
    +        gotInBubblePhase = true;
    +        assertTrue(gotInCapturePhase);
    +      });
    +  goog.events.listen(
    +      new goog.events.KeyHandler(target, true /* capture */),
    +      goog.events.KeyHandler.EventType.KEY,
    +      function() {
    +        gotInCapturePhase = true;
    +      });
    +
    +  goog.testing.events.fireKeySequence(target, goog.events.KeyCodes.ESC);
    +  assertTrue(gotInBubblePhase);
    +}
    +
    +function fireKeyDown(keyHandler, keyCode, opt_charCode, opt_keyIdentifier,
    +    opt_ctrlKey, opt_altKey, opt_shiftKey) {
    +  var fakeEvent = createFakeKeyEvent(
    +      goog.events.EventType.KEYDOWN, keyCode, opt_charCode, opt_keyIdentifier,
    +      opt_ctrlKey, opt_altKey, opt_shiftKey);
    +  keyHandler.handleKeyDown_(fakeEvent);
    +  return fakeEvent.returnValue_;
    +}
    +
    +function fireKeyPress(keyHandler, keyCode, opt_charCode, opt_keyIdentifier,
    +    opt_ctrlKey, opt_altKey, opt_shiftKey) {
    +  var fakeEvent = createFakeKeyEvent(
    +      goog.events.EventType.KEYPRESS, keyCode, opt_charCode,
    +      opt_keyIdentifier, opt_ctrlKey, opt_altKey, opt_shiftKey);
    +  keyHandler.handleEvent(fakeEvent);
    +  return fakeEvent.returnValue_;
    +}
    +
    +function fireKeyUp(keyHandler, keyCode, opt_charCode, opt_keyIdentifier,
    +    opt_ctrlKey, opt_altKey, opt_shiftKey) {
    +  var fakeEvent = createFakeKeyEvent(
    +      goog.events.EventType.KEYUP, keyCode, opt_charCode,
    +      opt_keyIdentifier, opt_ctrlKey, opt_altKey, opt_shiftKey);
    +  keyHandler.handleKeyup_(fakeEvent);
    +  return fakeEvent.returnValue_;
    +}
    +
    +function createFakeKeyEvent(type, keyCode, opt_charCode, opt_keyIdentifier,
    +    opt_ctrlKey, opt_altKey, opt_shiftKey) {
    +  var event = {
    +    type: type,
    +    keyCode: keyCode,
    +    charCode: opt_charCode || undefined,
    +    keyIdentifier: opt_keyIdentifier || undefined,
    +    ctrlKey: opt_ctrlKey || false,
    +    altKey: opt_altKey || false,
    +    shiftKey: opt_shiftKey || false,
    +    timeStamp: goog.now()
    +  };
    +  return new goog.events.BrowserEvent(event);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/keynames.js b/mapbox-gl/closure-library/closure/goog/events/keynames.js
    new file mode 100644
    index 0000000000..b8e36af0ba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/keynames.js
    @@ -0,0 +1,139 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Constant declarations for common key codes.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.events.KeyNames');
    +
    +
    +/**
    + * Key names for common characters. These should be used with keyup/keydown
    + * events, since the .keyCode property on those is meant to indicate the
    + * *physical key* the user held down on the keyboard. Hence the mapping uses
    + * only the unshifted version of each key (e.g. no '#', since that's shift+3).
    + * Keypress events on the other hand generate (mostly) ASCII codes since they
    + * correspond to *characters* the user typed.
    + *
    + * For further reference: http://unixpapa.com/js/key.html
    + *
    + * This list is not localized and therefore some of the key codes are not
    + * correct for non-US keyboard layouts.
    + *
    + * @see goog.events.KeyCodes
    + * @enum {string}
    + */
    +goog.events.KeyNames = {
    +  8: 'backspace',
    +  9: 'tab',
    +  13: 'enter',
    +  16: 'shift',
    +  17: 'ctrl',
    +  18: 'alt',
    +  19: 'pause',
    +  20: 'caps-lock',
    +  27: 'esc',
    +  32: 'space',
    +  33: 'pg-up',
    +  34: 'pg-down',
    +  35: 'end',
    +  36: 'home',
    +  37: 'left',
    +  38: 'up',
    +  39: 'right',
    +  40: 'down',
    +  45: 'insert',
    +  46: 'delete',
    +  48: '0',
    +  49: '1',
    +  50: '2',
    +  51: '3',
    +  52: '4',
    +  53: '5',
    +  54: '6',
    +  55: '7',
    +  56: '8',
    +  57: '9',
    +  59: 'semicolon',
    +  61: 'equals',
    +  65: 'a',
    +  66: 'b',
    +  67: 'c',
    +  68: 'd',
    +  69: 'e',
    +  70: 'f',
    +  71: 'g',
    +  72: 'h',
    +  73: 'i',
    +  74: 'j',
    +  75: 'k',
    +  76: 'l',
    +  77: 'm',
    +  78: 'n',
    +  79: 'o',
    +  80: 'p',
    +  81: 'q',
    +  82: 'r',
    +  83: 's',
    +  84: 't',
    +  85: 'u',
    +  86: 'v',
    +  87: 'w',
    +  88: 'x',
    +  89: 'y',
    +  90: 'z',
    +  93: 'context',
    +  96: 'num-0',
    +  97: 'num-1',
    +  98: 'num-2',
    +  99: 'num-3',
    +  100: 'num-4',
    +  101: 'num-5',
    +  102: 'num-6',
    +  103: 'num-7',
    +  104: 'num-8',
    +  105: 'num-9',
    +  106: 'num-multiply',
    +  107: 'num-plus',
    +  109: 'num-minus',
    +  110: 'num-period',
    +  111: 'num-division',
    +  112: 'f1',
    +  113: 'f2',
    +  114: 'f3',
    +  115: 'f4',
    +  116: 'f5',
    +  117: 'f6',
    +  118: 'f7',
    +  119: 'f8',
    +  120: 'f9',
    +  121: 'f10',
    +  122: 'f11',
    +  123: 'f12',
    +  186: 'semicolon',
    +  187: 'equals',
    +  189: 'dash',
    +  188: ',',
    +  190: '.',
    +  191: '/',
    +  192: '`',
    +  219: 'open-square-bracket',
    +  220: '\\',
    +  221: 'close-square-bracket',
    +  222: 'single-quote',
    +  224: 'win'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenable.js b/mapbox-gl/closure-library/closure/goog/events/listenable.js
    new file mode 100644
    index 0000000000..a05b348275
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenable.js
    @@ -0,0 +1,335 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for a listenable JavaScript object.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.events.Listenable');
    +goog.provide('goog.events.ListenableKey');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.events.EventId');
    +
    +
    +
    +/**
    + * A listenable interface. A listenable is an object with the ability
    + * to dispatch/broadcast events to "event listeners" registered via
    + * listen/listenOnce.
    + *
    + * The interface allows for an event propagation mechanism similar
    + * to one offered by native browser event targets, such as
    + * capture/bubble mechanism, stopping propagation, and preventing
    + * default actions. Capture/bubble mechanism depends on the ancestor
    + * tree constructed via {@code #getParentEventTarget}; this tree
    + * must be directed acyclic graph. The meaning of default action(s)
    + * in preventDefault is specific to a particular use case.
    + *
    + * Implementations that do not support capture/bubble or can not have
    + * a parent listenable can simply not implement any ability to set the
    + * parent listenable (and have {@code #getParentEventTarget} return
    + * null).
    + *
    + * Implementation of this class can be used with or independently from
    + * goog.events.
    + *
    + * Implementation must call {@code #addImplementation(implClass)}.
    + *
    + * @interface
    + * @see goog.events
    + * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
    + */
    +goog.events.Listenable = function() {};
    +
    +
    +/**
    + * An expando property to indicate that an object implements
    + * goog.events.Listenable.
    + *
    + * See addImplementation/isImplementedBy.
    + *
    + * @type {string}
    + * @const
    + */
    +goog.events.Listenable.IMPLEMENTED_BY_PROP =
    +    'closure_listenable_' + ((Math.random() * 1e6) | 0);
    +
    +
    +/**
    + * Marks a given class (constructor) as an implementation of
    + * Listenable, do that we can query that fact at runtime. The class
    + * must have already implemented the interface.
    + * @param {!Function} cls The class constructor. The corresponding
    + *     class must have already implemented the interface.
    + */
    +goog.events.Listenable.addImplementation = function(cls) {
    +  cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
    +};
    +
    +
    +/**
    + * @param {Object} obj The object to check.
    + * @return {boolean} Whether a given instance implements Listenable. The
    + *     class/superclass of the instance must call addImplementation.
    + */
    +goog.events.Listenable.isImplementedBy = function(obj) {
    +  return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
    +};
    +
    +
    +/**
    + * Adds an event listener. A listener can only be added once to an
    + * object and if it is added again the key for the listener is
    + * returned. Note that if the existing listener is a one-off listener
    + * (registered via listenOnce), it will no longer be a one-off
    + * listener after a call to listen().
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
    + * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    + *     method.
    + * @param {boolean=} opt_useCapture Whether to fire in capture phase
    + *     (defaults to false).
    + * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {goog.events.ListenableKey} Unique key for the listener.
    + * @template SCOPE,EVENTOBJ
    + */
    +goog.events.Listenable.prototype.listen;
    +
    +
    +/**
    + * Adds an event listener that is removed automatically after the
    + * listener fired once.
    + *
    + * If an existing listener already exists, listenOnce will do
    + * nothing. In particular, if the listener was previously registered
    + * via listen(), listenOnce() will not turn the listener into a
    + * one-off listener. Similarly, if there is already an existing
    + * one-off listener, listenOnce does not modify the listeners (it is
    + * still a once listener).
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
    + * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    + *     method.
    + * @param {boolean=} opt_useCapture Whether to fire in capture phase
    + *     (defaults to false).
    + * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {goog.events.ListenableKey} Unique key for the listener.
    + * @template SCOPE,EVENTOBJ
    + */
    +goog.events.Listenable.prototype.listenOnce;
    +
    +
    +/**
    + * Removes an event listener which was added with listen() or listenOnce().
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
    + * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
    + *     method.
    + * @param {boolean=} opt_useCapture Whether to fire in capture phase
    + *     (defaults to false).
    + * @param {SCOPE=} opt_listenerScope Object in whose scope to call
    + *     the listener.
    + * @return {boolean} Whether any listener was removed.
    + * @template SCOPE,EVENTOBJ
    + */
    +goog.events.Listenable.prototype.unlisten;
    +
    +
    +/**
    + * Removes an event listener which was added with listen() by the key
    + * returned by listen().
    + *
    + * @param {goog.events.ListenableKey} key The key returned by
    + *     listen() or listenOnce().
    + * @return {boolean} Whether any listener was removed.
    + */
    +goog.events.Listenable.prototype.unlistenByKey;
    +
    +
    +/**
    + * Dispatches an event (or event like object) and calls all listeners
    + * listening for events of this type. The type of the event is decided by the
    + * type property on the event object.
    + *
    + * If any of the listeners returns false OR calls preventDefault then this
    + * function will return false.  If one of the capture listeners calls
    + * stopPropagation, then the bubble listeners won't fire.
    + *
    + * @param {goog.events.EventLike} e Event object.
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the listeners returns false) this will also return false.
    + */
    +goog.events.Listenable.prototype.dispatchEvent;
    +
    +
    +/**
    + * Removes all listeners from this listenable. If type is specified,
    + * it will only remove listeners of the particular type. otherwise all
    + * registered listeners will be removed.
    + *
    + * @param {string=} opt_type Type of event to remove, default is to
    + *     remove all types.
    + * @return {number} Number of listeners removed.
    + */
    +goog.events.Listenable.prototype.removeAllListeners;
    +
    +
    +/**
    + * Returns the parent of this event target to use for capture/bubble
    + * mechanism.
    + *
    + * NOTE(chrishenry): The name reflects the original implementation of
    + * custom event target ({@code goog.events.EventTarget}). We decided
    + * that changing the name is not worth it.
    + *
    + * @return {goog.events.Listenable} The parent EventTarget or null if
    + *     there is no parent.
    + */
    +goog.events.Listenable.prototype.getParentEventTarget;
    +
    +
    +/**
    + * Fires all registered listeners in this listenable for the given
    + * type and capture mode, passing them the given eventObject. This
    + * does not perform actual capture/bubble. Only implementors of the
    + * interface should be using this.
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
    + *     listeners to fire.
    + * @param {boolean} capture The capture mode of the listeners to fire.
    + * @param {EVENTOBJ} eventObject The event object to fire.
    + * @return {boolean} Whether all listeners succeeded without
    + *     attempting to prevent default behavior. If any listener returns
    + *     false or called goog.events.Event#preventDefault, this returns
    + *     false.
    + * @template EVENTOBJ
    + */
    +goog.events.Listenable.prototype.fireListeners;
    +
    +
    +/**
    + * Gets all listeners in this listenable for the given type and
    + * capture mode.
    + *
    + * @param {string|!goog.events.EventId} type The type of the listeners to fire.
    + * @param {boolean} capture The capture mode of the listeners to fire.
    + * @return {!Array<goog.events.ListenableKey>} An array of registered
    + *     listeners.
    + * @template EVENTOBJ
    + */
    +goog.events.Listenable.prototype.getListeners;
    +
    +
    +/**
    + * Gets the goog.events.ListenableKey for the event or null if no such
    + * listener is in use.
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event
    + *     without the 'on' prefix.
    + * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
    + *     listener function to get.
    + * @param {boolean} capture Whether the listener is a capturing listener.
    + * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {goog.events.ListenableKey} the found listener or null if not found.
    + * @template SCOPE,EVENTOBJ
    + */
    +goog.events.Listenable.prototype.getListener;
    +
    +
    +/**
    + * Whether there is any active listeners matching the specified
    + * signature. If either the type or capture parameters are
    + * unspecified, the function will match on the remaining criteria.
    + *
    + * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.
    + * @param {boolean=} opt_capture Whether to check for capture or bubble
    + *     listeners.
    + * @return {boolean} Whether there is any active listeners matching
    + *     the requested type and/or capture phase.
    + * @template EVENTOBJ
    + */
    +goog.events.Listenable.prototype.hasListener;
    +
    +
    +
    +/**
    + * An interface that describes a single registered listener.
    + * @interface
    + */
    +goog.events.ListenableKey = function() {};
    +
    +
    +/**
    + * Counter used to create a unique key
    + * @type {number}
    + * @private
    + */
    +goog.events.ListenableKey.counter_ = 0;
    +
    +
    +/**
    + * Reserves a key to be used for ListenableKey#key field.
    + * @return {number} A number to be used to fill ListenableKey#key
    + *     field.
    + */
    +goog.events.ListenableKey.reserveKey = function() {
    +  return ++goog.events.ListenableKey.counter_;
    +};
    +
    +
    +/**
    + * The source event target.
    + * @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
    + */
    +goog.events.ListenableKey.prototype.src;
    +
    +
    +/**
    + * The event type the listener is listening to.
    + * @type {string}
    + */
    +goog.events.ListenableKey.prototype.type;
    +
    +
    +/**
    + * The listener function.
    + * @type {function(?):?|{handleEvent:function(?):?}|null}
    + */
    +goog.events.ListenableKey.prototype.listener;
    +
    +
    +/**
    + * Whether the listener works on capture phase.
    + * @type {boolean}
    + */
    +goog.events.ListenableKey.prototype.capture;
    +
    +
    +/**
    + * The 'this' object for the listener function's scope.
    + * @type {Object}
    + */
    +goog.events.ListenableKey.prototype.handler;
    +
    +
    +/**
    + * A globally unique number to identify the key.
    + * @type {number}
    + */
    +goog.events.ListenableKey.prototype.key;
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenable_test.html b/mapbox-gl/closure-library/closure/goog/events/listenable_test.html
    new file mode 100644
    index 0000000000..55307ab715
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenable_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.Listenable
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.ListenableTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenable_test.js b/mapbox-gl/closure-library/closure/goog/events/listenable_test.js
    new file mode 100644
    index 0000000000..538fb78d93
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenable_test.js
    @@ -0,0 +1,29 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.ListenableTest');
    +goog.setTestOnly('goog.events.ListenableTest');
    +
    +goog.require('goog.events.Listenable');
    +goog.require('goog.testing.jsunit');
    +
    +function testIsImplementedBy() {
    +  var ListenableClass = function() {};
    +  goog.events.Listenable.addImplementation(ListenableClass);
    +
    +  var NonListenableClass = function() {};
    +
    +  assertTrue(goog.events.Listenable.isImplementedBy(new ListenableClass()));
    +  assertFalse(goog.events.Listenable.isImplementedBy(new NonListenableClass()));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listener.js b/mapbox-gl/closure-library/closure/goog/events/listener.js
    new file mode 100644
    index 0000000000..60c737021b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listener.js
    @@ -0,0 +1,131 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Listener object.
    + * @see ../demos/events.html
    + */
    +
    +goog.provide('goog.events.Listener');
    +
    +goog.require('goog.events.ListenableKey');
    +
    +
    +
    +/**
    + * Simple class that stores information about a listener
    + * @param {!Function} listener Callback function.
    + * @param {Function} proxy Wrapper for the listener that patches the event.
    + * @param {EventTarget|goog.events.Listenable} src Source object for
    + *     the event.
    + * @param {string} type Event type.
    + * @param {boolean} capture Whether in capture or bubble phase.
    + * @param {Object=} opt_handler Object in whose context to execute the callback.
    + * @implements {goog.events.ListenableKey}
    + * @constructor
    + */
    +goog.events.Listener = function(
    +    listener, proxy, src, type, capture, opt_handler) {
    +  if (goog.events.Listener.ENABLE_MONITORING) {
    +    this.creationStack = new Error().stack;
    +  }
    +
    +  /**
    +   * Callback function.
    +   * @type {Function}
    +   */
    +  this.listener = listener;
    +
    +  /**
    +   * A wrapper over the original listener. This is used solely to
    +   * handle native browser events (it is used to simulate the capture
    +   * phase and to patch the event object).
    +   * @type {Function}
    +   */
    +  this.proxy = proxy;
    +
    +  /**
    +   * Object or node that callback is listening to
    +   * @type {EventTarget|goog.events.Listenable}
    +   */
    +  this.src = src;
    +
    +  /**
    +   * The event type.
    +   * @const {string}
    +   */
    +  this.type = type;
    +
    +  /**
    +   * Whether the listener is being called in the capture or bubble phase
    +   * @const {boolean}
    +   */
    +  this.capture = !!capture;
    +
    +  /**
    +   * Optional object whose context to execute the listener in
    +   * @type {Object|undefined}
    +   */
    +  this.handler = opt_handler;
    +
    +  /**
    +   * The key of the listener.
    +   * @const {number}
    +   * @override
    +   */
    +  this.key = goog.events.ListenableKey.reserveKey();
    +
    +  /**
    +   * Whether to remove the listener after it has been called.
    +   * @type {boolean}
    +   */
    +  this.callOnce = false;
    +
    +  /**
    +   * Whether the listener has been removed.
    +   * @type {boolean}
    +   */
    +  this.removed = false;
    +};
    +
    +
    +/**
    + * @define {boolean} Whether to enable the monitoring of the
    + *     goog.events.Listener instances. Switching on the monitoring is only
    + *     recommended for debugging because it has a significant impact on
    + *     performance and memory usage. If switched off, the monitoring code
    + *     compiles down to 0 bytes.
    + */
    +goog.define('goog.events.Listener.ENABLE_MONITORING', false);
    +
    +
    +/**
    + * If monitoring the goog.events.Listener instances is enabled, stores the
    + * creation stack trace of the Disposable instance.
    + * @type {string}
    + */
    +goog.events.Listener.prototype.creationStack;
    +
    +
    +/**
    + * Marks this listener as removed. This also remove references held by
    + * this listener object (such as listener and event source).
    + */
    +goog.events.Listener.prototype.markAsRemoved = function() {
    +  this.removed = true;
    +  this.listener = null;
    +  this.proxy = null;
    +  this.src = null;
    +  this.handler = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenermap.js b/mapbox-gl/closure-library/closure/goog/events/listenermap.js
    new file mode 100644
    index 0000000000..c20cdb9738
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenermap.js
    @@ -0,0 +1,308 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A map of listeners that provides utility functions to
    + * deal with listeners on an event target. Used by
    + * {@code goog.events.EventTarget}.
    + *
    + * WARNING: Do not use this class from outside goog.events package.
    + *
    + * @visibility {//closure/goog/bin/sizetests:__pkg__}
    + * @visibility {//closure/goog/events:__pkg__}
    + * @visibility {//closure/goog/labs/events:__pkg__}
    + */
    +
    +goog.provide('goog.events.ListenerMap');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.Listener');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Creates a new listener map.
    + * @param {EventTarget|goog.events.Listenable} src The src object.
    + * @constructor
    + * @final
    + */
    +goog.events.ListenerMap = function(src) {
    +  /** @type {EventTarget|goog.events.Listenable} */
    +  this.src = src;
    +
    +  /**
    +   * Maps of event type to an array of listeners.
    +   * @type {Object<string, !Array<!goog.events.Listener>>}
    +   */
    +  this.listeners = {};
    +
    +  /**
    +   * The count of types in this map that have registered listeners.
    +   * @private {number}
    +   */
    +  this.typeCount_ = 0;
    +};
    +
    +
    +/**
    + * @return {number} The count of event types in this map that actually
    + *     have registered listeners.
    + */
    +goog.events.ListenerMap.prototype.getTypeCount = function() {
    +  return this.typeCount_;
    +};
    +
    +
    +/**
    + * @return {number} Total number of registered listeners.
    + */
    +goog.events.ListenerMap.prototype.getListenerCount = function() {
    +  var count = 0;
    +  for (var type in this.listeners) {
    +    count += this.listeners[type].length;
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Adds an event listener. A listener can only be added once to an
    + * object and if it is added again the key for the listener is
    + * returned.
    + *
    + * Note that a one-off listener will not change an existing listener,
    + * if any. On the other hand a normal listener will change existing
    + * one-off listener to become a normal listener.
    + *
    + * @param {string|!goog.events.EventId} type The listener event type.
    + * @param {!Function} listener This listener callback method.
    + * @param {boolean} callOnce Whether the listener is a one-off
    + *     listener.
    + * @param {boolean=} opt_useCapture The capture mode of the listener.
    + * @param {Object=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {goog.events.ListenableKey} Unique key for the listener.
    + */
    +goog.events.ListenerMap.prototype.add = function(
    +    type, listener, callOnce, opt_useCapture, opt_listenerScope) {
    +  var typeStr = type.toString();
    +  var listenerArray = this.listeners[typeStr];
    +  if (!listenerArray) {
    +    listenerArray = this.listeners[typeStr] = [];
    +    this.typeCount_++;
    +  }
    +
    +  var listenerObj;
    +  var index = goog.events.ListenerMap.findListenerIndex_(
    +      listenerArray, listener, opt_useCapture, opt_listenerScope);
    +  if (index > -1) {
    +    listenerObj = listenerArray[index];
    +    if (!callOnce) {
    +      // Ensure that, if there is an existing callOnce listener, it is no
    +      // longer a callOnce listener.
    +      listenerObj.callOnce = false;
    +    }
    +  } else {
    +    listenerObj = new goog.events.Listener(
    +        listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
    +    listenerObj.callOnce = callOnce;
    +    listenerArray.push(listenerObj);
    +  }
    +  return listenerObj;
    +};
    +
    +
    +/**
    + * Removes a matching listener.
    + * @param {string|!goog.events.EventId} type The listener event type.
    + * @param {!Function} listener This listener callback method.
    + * @param {boolean=} opt_useCapture The capture mode of the listener.
    + * @param {Object=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {boolean} Whether any listener was removed.
    + */
    +goog.events.ListenerMap.prototype.remove = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  var typeStr = type.toString();
    +  if (!(typeStr in this.listeners)) {
    +    return false;
    +  }
    +
    +  var listenerArray = this.listeners[typeStr];
    +  var index = goog.events.ListenerMap.findListenerIndex_(
    +      listenerArray, listener, opt_useCapture, opt_listenerScope);
    +  if (index > -1) {
    +    var listenerObj = listenerArray[index];
    +    listenerObj.markAsRemoved();
    +    goog.array.removeAt(listenerArray, index);
    +    if (listenerArray.length == 0) {
    +      delete this.listeners[typeStr];
    +      this.typeCount_--;
    +    }
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Removes the given listener object.
    + * @param {goog.events.ListenableKey} listener The listener to remove.
    + * @return {boolean} Whether the listener is removed.
    + */
    +goog.events.ListenerMap.prototype.removeByKey = function(listener) {
    +  var type = listener.type;
    +  if (!(type in this.listeners)) {
    +    return false;
    +  }
    +
    +  var removed = goog.array.remove(this.listeners[type], listener);
    +  if (removed) {
    +    listener.markAsRemoved();
    +    if (this.listeners[type].length == 0) {
    +      delete this.listeners[type];
    +      this.typeCount_--;
    +    }
    +  }
    +  return removed;
    +};
    +
    +
    +/**
    + * Removes all listeners from this map. If opt_type is provided, only
    + * listeners that match the given type are removed.
    + * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
    + * @return {number} Number of listeners removed.
    + */
    +goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
    +  var typeStr = opt_type && opt_type.toString();
    +  var count = 0;
    +  for (var type in this.listeners) {
    +    if (!typeStr || type == typeStr) {
    +      var listenerArray = this.listeners[type];
    +      for (var i = 0; i < listenerArray.length; i++) {
    +        ++count;
    +        listenerArray[i].markAsRemoved();
    +      }
    +      delete this.listeners[type];
    +      this.typeCount_--;
    +    }
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Gets all listeners that match the given type and capture mode. The
    + * returned array is a copy (but the listener objects are not).
    + * @param {string|!goog.events.EventId} type The type of the listeners
    + *     to retrieve.
    + * @param {boolean} capture The capture mode of the listeners to retrieve.
    + * @return {!Array<goog.events.ListenableKey>} An array of matching
    + *     listeners.
    + */
    +goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
    +  var listenerArray = this.listeners[type.toString()];
    +  var rv = [];
    +  if (listenerArray) {
    +    for (var i = 0; i < listenerArray.length; ++i) {
    +      var listenerObj = listenerArray[i];
    +      if (listenerObj.capture == capture) {
    +        rv.push(listenerObj);
    +      }
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Gets the goog.events.ListenableKey for the event or null if no such
    + * listener is in use.
    + *
    + * @param {string|!goog.events.EventId} type The type of the listener
    + *     to retrieve.
    + * @param {!Function} listener The listener function to get.
    + * @param {boolean} capture Whether the listener is a capturing listener.
    + * @param {Object=} opt_listenerScope Object in whose scope to call the
    + *     listener.
    + * @return {goog.events.ListenableKey} the found listener or null if not found.
    + */
    +goog.events.ListenerMap.prototype.getListener = function(
    +    type, listener, capture, opt_listenerScope) {
    +  var listenerArray = this.listeners[type.toString()];
    +  var i = -1;
    +  if (listenerArray) {
    +    i = goog.events.ListenerMap.findListenerIndex_(
    +        listenerArray, listener, capture, opt_listenerScope);
    +  }
    +  return i > -1 ? listenerArray[i] : null;
    +};
    +
    +
    +/**
    + * Whether there is a matching listener. If either the type or capture
    + * parameters are unspecified, the function will match on the
    + * remaining criteria.
    + *
    + * @param {string|!goog.events.EventId=} opt_type The type of the listener.
    + * @param {boolean=} opt_capture The capture mode of the listener.
    + * @return {boolean} Whether there is an active listener matching
    + *     the requested type and/or capture phase.
    + */
    +goog.events.ListenerMap.prototype.hasListener = function(
    +    opt_type, opt_capture) {
    +  var hasType = goog.isDef(opt_type);
    +  var typeStr = hasType ? opt_type.toString() : '';
    +  var hasCapture = goog.isDef(opt_capture);
    +
    +  return goog.object.some(
    +      this.listeners, function(listenerArray, type) {
    +        for (var i = 0; i < listenerArray.length; ++i) {
    +          if ((!hasType || listenerArray[i].type == typeStr) &&
    +              (!hasCapture || listenerArray[i].capture == opt_capture)) {
    +            return true;
    +          }
    +        }
    +
    +        return false;
    +      });
    +};
    +
    +
    +/**
    + * Finds the index of a matching goog.events.Listener in the given
    + * listenerArray.
    + * @param {!Array<!goog.events.Listener>} listenerArray Array of listener.
    + * @param {!Function} listener The listener function.
    + * @param {boolean=} opt_useCapture The capture flag for the listener.
    + * @param {Object=} opt_listenerScope The listener scope.
    + * @return {number} The index of the matching listener within the
    + *     listenerArray.
    + * @private
    + */
    +goog.events.ListenerMap.findListenerIndex_ = function(
    +    listenerArray, listener, opt_useCapture, opt_listenerScope) {
    +  for (var i = 0; i < listenerArray.length; ++i) {
    +    var listenerObj = listenerArray[i];
    +    if (!listenerObj.removed &&
    +        listenerObj.listener == listener &&
    +        listenerObj.capture == !!opt_useCapture &&
    +        listenerObj.handler == opt_listenerScope) {
    +      return i;
    +    }
    +  }
    +  return -1;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenermap_test.html b/mapbox-gl/closure-library/closure/goog/events/listenermap_test.html
    new file mode 100644
    index 0000000000..1f544697c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenermap_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.events.ListenerMap</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.events.ListenerMapTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/listenermap_test.js b/mapbox-gl/closure-library/closure/goog/events/listenermap_test.js
    new file mode 100644
    index 0000000000..73c6772fec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/listenermap_test.js
    @@ -0,0 +1,161 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests for listenermap.js.
    + *
    + * Most of this class functionality is already tested by
    + * goog.events.EventTarget tests. This test file only provides tests
    + * for features that are not direct duplicates of tests in
    + * goog.events.EventTarget.
    + */
    +
    +goog.provide('goog.events.ListenerMapTest');
    +goog.setTestOnly('goog.events.ListenerMapTest');
    +
    +goog.require('goog.dispose');
    +goog.require('goog.events');
    +goog.require('goog.events.EventId');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.ListenerMap');
    +goog.require('goog.testing.jsunit');
    +
    +
    +var et, map;
    +var handler1 = function() {};
    +var handler2 = function() {};
    +var handler3 = function() {};
    +var CLICK_EVENT_ID = new goog.events.EventId(goog.events.getUniqueId('click'));
    +
    +
    +function setUp() {
    +  et = new goog.events.EventTarget();
    +  map = new goog.events.ListenerMap(et);
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(et);
    +}
    +
    +
    +function testGetTypeCount() {
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add('click', handler1, false);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove('click', handler1);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove(CLICK_EVENT_ID, handler1);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add('click', handler1, false, true);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove('click', handler1, true);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false, true);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove(CLICK_EVENT_ID, handler1, true);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add('click', handler1, false);
    +  map.add('click', handler1, false, true);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove('click', handler1);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove('click', handler1, true);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false);
    +  map.add(CLICK_EVENT_ID, handler1, false, true);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove(CLICK_EVENT_ID, handler1);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove(CLICK_EVENT_ID, handler1, true);
    +  assertEquals(0, map.getTypeCount());
    +
    +  map.add('click', handler1, false);
    +  map.add('touchstart', handler2, false);
    +  map.add(CLICK_EVENT_ID, handler3, false);
    +  assertEquals(3, map.getTypeCount());
    +  map.remove(CLICK_EVENT_ID, handler3);
    +  assertEquals(2, map.getTypeCount());
    +  map.remove('touchstart', handler2);
    +  assertEquals(1, map.getTypeCount());
    +  map.remove('click', handler1);
    +  assertEquals(0, map.getTypeCount());
    +}
    +
    +
    +function testGetListenerCount() {
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add('click', handler1, false);
    +  assertEquals(1, map.getListenerCount());
    +  map.remove('click', handler1);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false);
    +  assertEquals(1, map.getListenerCount());
    +  map.remove(CLICK_EVENT_ID, handler1);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add('click', handler1, false, true);
    +  assertEquals(1, map.getListenerCount());
    +  map.remove('click', handler1, true);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false, true);
    +  assertEquals(1, map.getListenerCount());
    +  map.remove(CLICK_EVENT_ID, handler1, true);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add('click', handler1, false);
    +  map.add('click', handler1, false, true);
    +  assertEquals(2, map.getListenerCount());
    +  map.remove('click', handler1);
    +  map.remove('click', handler1, true);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add(CLICK_EVENT_ID, handler1, false);
    +  map.add(CLICK_EVENT_ID, handler1, false, true);
    +  assertEquals(2, map.getListenerCount());
    +  map.remove(CLICK_EVENT_ID, handler1);
    +  map.remove(CLICK_EVENT_ID, handler1, true);
    +  assertEquals(0, map.getListenerCount());
    +
    +  map.add('click', handler1, false);
    +  map.add('touchstart', handler2, false);
    +  map.add(CLICK_EVENT_ID, handler3, false);
    +  assertEquals(3, map.getListenerCount());
    +  map.remove(CLICK_EVENT_ID, handler3);
    +  map.remove('touchstart', handler2);
    +  map.remove('click', handler1);
    +  assertEquals(0, map.getListenerCount());
    +}
    +
    +
    +function testListenerSourceIsSetCorrectly() {
    +  map.add('click', handler1, false);
    +  var listener = map.getListener('click', handler1);
    +  assertEquals(et, listener.src);
    +
    +  map.add(CLICK_EVENT_ID, handler2, false);
    +  listener = map.getListener(CLICK_EVENT_ID, handler2);
    +  assertEquals(et, listener.src);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler.js b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler.js
    new file mode 100644
    index 0000000000..6729064c83
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler.js
    @@ -0,0 +1,296 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This event wrapper will dispatch an event when the user uses
    + * the mouse wheel to scroll an element. You can get the direction by checking
    + * the deltaX and deltaY properties of the event.
    + *
    + * This class aims to smooth out inconsistencies between browser platforms with
    + * regards to mousewheel events, but we do not cover every possible
    + * software/hardware combination out there, some of which occasionally produce
    + * very large deltas in mousewheel events. If your application wants to guard
    + * against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
    + * to set maximum values that make sense for your application.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/mousewheelhandler.html
    + */
    +
    +goog.provide('goog.events.MouseWheelEvent');
    +goog.provide('goog.events.MouseWheelHandler');
    +goog.provide('goog.events.MouseWheelHandler.EventType');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.math');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This event handler allows you to catch mouse wheel events in a consistent
    + * manner.
    + * @param {Element|Document} element The element to listen to the mouse wheel
    + *     event on.
    + * @param {boolean=} opt_capture Whether to handle the mouse wheel event in
    + *     capture phase.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.events.MouseWheelHandler = function(element, opt_capture) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * This is the element that we will listen to the real mouse wheel events on.
    +   * @type {Element|Document}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  var rtlElement = goog.dom.isElement(this.element_) ?
    +      /** @type {Element} */ (this.element_) :
    +      (this.element_ ? /** @type {Document} */ (this.element_).body : null);
    +
    +  /**
    +   * True if the element exists and is RTL, false otherwise.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);
    +
    +  var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';
    +
    +  /**
    +   * The key returned from the goog.events.listen.
    +   * @type {goog.events.Key}
    +   * @private
    +   */
    +  this.listenKey_ = goog.events.listen(this.element_, type, this, opt_capture);
    +};
    +goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum type for the events fired by the mouse wheel handler.
    + * @enum {string}
    + */
    +goog.events.MouseWheelHandler.EventType = {
    +  MOUSEWHEEL: 'mousewheel'
    +};
    +
    +
    +/**
    + * Optional maximum magnitude for x delta on each mousewheel event.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.events.MouseWheelHandler.prototype.maxDeltaX_;
    +
    +
    +/**
    + * Optional maximum magnitude for y delta on each mousewheel event.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.events.MouseWheelHandler.prototype.maxDeltaY_;
    +
    +
    +/**
    + * @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
    + *     event. Should be non-negative.
    + */
    +goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
    +  this.maxDeltaX_ = maxDeltaX;
    +};
    +
    +
    +/**
    + * @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
    + *     event. Should be non-negative.
    + */
    +goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
    +  this.maxDeltaY_ = maxDeltaY;
    +};
    +
    +
    +/**
    + * Handles the events on the element.
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + */
    +goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
    +  var deltaX = 0;
    +  var deltaY = 0;
    +  var detail = 0;
    +  var be = e.getBrowserEvent();
    +  if (be.type == 'mousewheel') {
    +    var wheelDeltaScaleFactor = 1;
    +    if (goog.userAgent.IE ||
    +        goog.userAgent.WEBKIT &&
    +        (goog.userAgent.WINDOWS || goog.userAgent.isVersionOrHigher('532.0'))) {
    +      // In IE we get a multiple of 120; we adjust to a multiple of 3 to
    +      // represent number of lines scrolled (like Gecko).
    +      // Newer versions of Webkit match IE behavior, and WebKit on
    +      // Windows also matches IE behavior.
    +      // See bug https://bugs.webkit.org/show_bug.cgi?id=24368
    +      wheelDeltaScaleFactor = 40;
    +    }
    +
    +    detail = goog.events.MouseWheelHandler.smartScale_(
    +        -be.wheelDelta, wheelDeltaScaleFactor);
    +    if (goog.isDef(be.wheelDeltaX)) {
    +      // Webkit has two properties to indicate directional scroll, and
    +      // can scroll both directions at once.
    +      deltaX = goog.events.MouseWheelHandler.smartScale_(
    +          -be.wheelDeltaX, wheelDeltaScaleFactor);
    +      deltaY = goog.events.MouseWheelHandler.smartScale_(
    +          -be.wheelDeltaY, wheelDeltaScaleFactor);
    +    } else {
    +      deltaY = detail;
    +    }
    +
    +    // Historical note: Opera (pre 9.5) used to negate the detail value.
    +  } else { // Gecko
    +    // Gecko returns multiple of 3 (representing the number of lines scrolled)
    +    detail = be.detail;
    +
    +    // Gecko sometimes returns really big values if the user changes settings to
    +    // scroll a whole page per scroll
    +    if (detail > 100) {
    +      detail = 3;
    +    } else if (detail < -100) {
    +      detail = -3;
    +    }
    +
    +    // Firefox 3.1 adds an axis field to the event to indicate direction of
    +    // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
    +    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
    +      deltaX = detail;
    +    } else {
    +      deltaY = detail;
    +    }
    +  }
    +
    +  if (goog.isNumber(this.maxDeltaX_)) {
    +    deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
    +  }
    +  if (goog.isNumber(this.maxDeltaY_)) {
    +    deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
    +  }
    +  // Don't clamp 'detail', since it could be ambiguous which axis it refers to
    +  // and because it's informally deprecated anyways.
    +
    +  // For horizontal scrolling we need to flip the value for RTL grids.
    +  if (this.isRtl_) {
    +    deltaX = -deltaX;
    +  }
    +  var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
    +  this.dispatchEvent(newEvent);
    +};
    +
    +
    +/**
    + * Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
    + * @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
    + *     be an integer.
    + * @param {number} scaleFactor Factor to scale the delta down by. Expected to
    + *     be an integer.
    + * @return {number} Scaled-down delta value, or the original delta if the
    + *     scaleFactor does not appear to be applicable.
    + * @private
    + */
    +goog.events.MouseWheelHandler.smartScale_ = function(mouseWheelDelta,
    +    scaleFactor) {
    +  // The basic problem here is that in Webkit on Mac and Linux, we can get two
    +  // very different types of mousewheel events: from continuous devices
    +  // (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
    +  //
    +  // Non-continuous devices in Webkit get their wheel deltas scaled up to
    +  // behave like IE. Continuous devices return much smaller unscaled values
    +  // (which most of the time will not be cleanly divisible by the IE scale
    +  // factor), so we should not try to normalize them down.
    +  //
    +  // Detailed discussion:
    +  //   https://bugs.webkit.org/show_bug.cgi?id=29601
    +  //   http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
    +  if (goog.userAgent.WEBKIT &&
    +      (goog.userAgent.MAC || goog.userAgent.LINUX) &&
    +      (mouseWheelDelta % scaleFactor) != 0) {
    +    return mouseWheelDelta;
    +  } else {
    +    return mouseWheelDelta / scaleFactor;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
    +  goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
    +  goog.events.unlistenByKey(this.listenKey_);
    +  this.listenKey_ = null;
    +};
    +
    +
    +
    +/**
    + * A base class for mouse wheel events. This is used with the
    + * MouseWheelHandler.
    + *
    + * @param {number} detail The number of rows the user scrolled.
    + * @param {Event} browserEvent Browser event object.
    + * @param {number} deltaX The number of rows the user scrolled in the X
    + *     direction.
    + * @param {number} deltaY The number of rows the user scrolled in the Y
    + *     direction.
    + * @constructor
    + * @extends {goog.events.BrowserEvent}
    + * @final
    + */
    +goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
    +  goog.events.BrowserEvent.call(this, browserEvent);
    +
    +  this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;
    +
    +  /**
    +   * The number of lines the user scrolled
    +   * @type {number}
    +   * NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
    +   * more information.
    +   */
    +  this.detail = detail;
    +
    +  /**
    +   * The number of "lines" scrolled in the X direction.
    +   *
    +   * Note that not all browsers provide enough information to distinguish
    +   * horizontal and vertical scroll events, so for these unsupported browsers,
    +   * we will always have a deltaX of 0, even if the user scrolled their mouse
    +   * wheel or trackpad sideways.
    +   *
    +   * Currently supported browsers are Webkit and Firefox 3.1 or later.
    +   *
    +   * @type {number}
    +   */
    +  this.deltaX = deltaX;
    +
    +  /**
    +   * The number of lines scrolled in the Y direction.
    +   * @type {number}
    +   */
    +  this.deltaY = deltaY;
    +};
    +goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
    diff --git a/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.html
    new file mode 100644
    index 0000000000..237498e6b9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.MouseWheelHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.MouseWheelHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="foo">
    +  </div>
    +  <div id="fooRtl" dir="rtl">
    +  </div>
    +  <div id="log" style="position:absolute;right:0;top:0">
    +   Logged events:
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.js
    new file mode 100644
    index 0000000000..49fff0e09f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/mousewheelhandler_test.js
    @@ -0,0 +1,384 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.MouseWheelHandlerTest');
    +goog.setTestOnly('goog.events.MouseWheelHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.MouseWheelEvent');
    +goog.require('goog.events.MouseWheelHandler');
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var log;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var DEFAULT_TYPE = 'mousewheel';
    +var GECKO_TYPE = 'DOMMouseScroll';
    +
    +var HORIZONTAL = 'h';
    +var VERTICAL = 'v';
    +
    +var mouseWheelEvent;
    +var mouseWheelEventRtl;
    +var mouseWheelHandler;
    +var mouseWheelHandlerRtl;
    +
    +function setUpPage() {
    +  log = goog.dom.getElement('log');
    +}
    +
    +function setUp() {
    +  stubs.remove(goog, 'userAgent');
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +  goog.dispose(mouseWheelHandler);
    +  goog.dispose(mouseWheelHandlerRtl);
    +  mouseWheelHandlerRtl = null;
    +  mouseWheelHandler = null;
    +  mouseWheelEvent = null;
    +  mouseWheelEventRtl = null;
    +}
    +
    +function tearDownPage() {
    +  // Create interactive demo.
    +  mouseWheelHandler = new goog.events.MouseWheelHandler(document.body);
    +
    +  goog.events.listen(mouseWheelHandler,
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +      function(e) {
    +        log.innerHTML += goog.string.subs('<br />(deltaX, deltaY): (%s, %s)',
    +            e.deltaX, e.deltaY);
    +      });
    +}
    +
    +function testIeStyleMouseWheel() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: true,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: false
    +  };
    +
    +  createHandlerAndListen();
    +
    +  // Non-gecko, non-webkit events get wheelDelta divided by -40 to get detail.
    +  handleEvent(createFakeMouseWheelEvent(DEFAULT_TYPE, 120));
    +  assertMouseWheelEvent(-3, 0, -3);
    +
    +  handleEvent(createFakeMouseWheelEvent(DEFAULT_TYPE, -120));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  handleEvent(createFakeMouseWheelEvent(DEFAULT_TYPE, 1200));
    +  assertMouseWheelEvent(-30, 0, -30);
    +}
    +
    +function testNullBody() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: true,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: false
    +  };
    +  var documentObjectWithNoBody = { };
    +  goog.testing.events.mixinListenable(documentObjectWithNoBody);
    +  mouseWheelHandler =
    +      new goog.events.MouseWheelHandler(documentObjectWithNoBody);
    +}
    +
    +function testGeckoStyleMouseWheel() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: true,
    +    CAMINO: false,
    +    WEBKIT: false
    +  };
    +
    +  createHandlerAndListen();
    +
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, 3));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, -12));
    +  assertMouseWheelEvent(-12, 0, -12);
    +
    +  // Really big values should get truncated to +-3.
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, 1200));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, -1200));
    +  assertMouseWheelEvent(-3, 0, -3);
    +
    +  // Test scrolling with the additional axis property.
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, 3, VERTICAL));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, 3, HORIZONTAL));
    +  assertMouseWheelEvent(3, 3, 0);
    +
    +  handleEvent(createFakeMouseWheelEvent(GECKO_TYPE, null, -3, HORIZONTAL));
    +  assertMouseWheelEvent(-3, -3, 0);
    +}
    +
    +function testWebkitStyleMouseWheel_ieStyle() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: true
    +  };
    +
    +  createHandlerAndListen();
    +
    +  // IE-style Webkit events get wheelDelta divided by -40 to get detail.
    +  handleEvent(createFakeWebkitMouseWheelEvent(-40, 0));
    +  assertMouseWheelEvent(1, 1, 0);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(120, 0));
    +  assertMouseWheelEvent(-3, -3, 0);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, 120));
    +  assertMouseWheelEvent(-3, 0, -3);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -40));
    +  assertMouseWheelEvent(1, 0, 1);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(80, -40));
    +  assertMouseWheelEvent(-2, -2, 1);
    +}
    +
    +function testWebkitStyleMouseWheel_ieStyleOnLinux() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: false,
    +    LINUX: true
    +  };
    +  runWebKitContinousAndDiscreteEventsTest();
    +}
    +
    +function testWebkitStyleMouseWheel_ieStyleOnMac() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: false,
    +    MAC: true
    +  };
    +  runWebKitContinousAndDiscreteEventsTest();
    +}
    +
    +function runWebKitContinousAndDiscreteEventsTest() {
    +  goog.userAgent.isVersionOrHigher = goog.functions.TRUE;
    +
    +  createHandlerAndListen();
    +
    +  // IE-style wheel events.
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -40));
    +  assertMouseWheelEvent(1, 0, 1);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(80, -40));
    +  assertMouseWheelEvent(-2, -2, 1);
    +
    +  // Even in Webkit versions that usually behave in IE style, sometimes wheel
    +  // events don't behave; this has been observed for instance with Macbook
    +  // and Chrome OS touchpads in Webkit 534.10+.
    +  handleEvent(createFakeWebkitMouseWheelEvent(-3, 5));
    +  assertMouseWheelEvent(-5, 3, -5);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(4, -7));
    +  assertMouseWheelEvent(7, -4, 7);
    +}
    +
    +function testWebkitStyleMouseWheel_nonIeStyle() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: false
    +  };
    +
    +  goog.userAgent.isVersionOrHigher = goog.functions.FALSE;
    +
    +  createHandlerAndListen();
    +
    +  // non-IE-style Webkit events do not get wheelDelta scaled
    +  handleEvent(createFakeWebkitMouseWheelEvent(-1, 0));
    +  assertMouseWheelEvent(1, 1, 0);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(3, 0));
    +  assertMouseWheelEvent(-3, -3, 0);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, 3));
    +  assertMouseWheelEvent(-3, 0, -3);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -1));
    +  assertMouseWheelEvent(1, 0, 1);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(2, -1));
    +  assertMouseWheelEvent(-2, -2, 1);
    +}
    +
    +function testMaxDeltaX() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: true
    +  };
    +
    +  createHandlerAndListen();
    +
    +  // IE-style Webkit events get wheelDelta divided by -40 to get detail.
    +  handleEvent(createFakeWebkitMouseWheelEvent(-120, 0));
    +  assertMouseWheelEvent(3, 3, 0);
    +
    +  mouseWheelHandler.setMaxDeltaX(3);
    +  mouseWheelHandlerRtl.setMaxDeltaX(3);
    +  handleEvent(createFakeWebkitMouseWheelEvent(-120, 0));
    +  assertMouseWheelEvent(3, 3, 0);
    +
    +  mouseWheelHandler.setMaxDeltaX(2);
    +  mouseWheelHandlerRtl.setMaxDeltaX(2);
    +  handleEvent(createFakeWebkitMouseWheelEvent(-120, 0));
    +  assertMouseWheelEvent(3, 2, 0);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -120));
    +  assertMouseWheelEvent(3, 0, 3);
    +}
    +
    +function testMaxDeltaY() {
    +  goog.userAgent = {
    +    OPERA: false,
    +    IE: false,
    +    GECKO: false,
    +    CAMINO: false,
    +    WEBKIT: true,
    +    WINDOWS: true
    +  };
    +
    +  createHandlerAndListen();
    +
    +  // IE-style Webkit events get wheelDelta divided by -40 to get detail.
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -120));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  mouseWheelHandler.setMaxDeltaY(3);
    +  mouseWheelHandlerRtl.setMaxDeltaY(3);
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -120));
    +  assertMouseWheelEvent(3, 0, 3);
    +
    +  mouseWheelHandler.setMaxDeltaY(2);
    +  mouseWheelHandlerRtl.setMaxDeltaY(2);
    +  handleEvent(createFakeWebkitMouseWheelEvent(0, -120));
    +  assertMouseWheelEvent(3, 0, 2);
    +
    +  handleEvent(createFakeWebkitMouseWheelEvent(-120, 0));
    +  assertMouseWheelEvent(3, 3, 0);
    +}
    +
    +// Be sure to call this after setting up goog.userAgent mock and not before.
    +function createHandlerAndListen() {
    +  mouseWheelHandler = new goog.events.MouseWheelHandler(
    +      goog.dom.getElement('foo'));
    +
    +  goog.events.listen(mouseWheelHandler,
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +      function(e) { mouseWheelEvent = e; });
    +
    +  mouseWheelHandlerRtl = new goog.events.MouseWheelHandler(
    +      goog.dom.getElement('fooRtl'));
    +
    +  goog.events.listen(mouseWheelHandlerRtl,
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +      function(e) { mouseWheelEventRtl = e; });
    +}
    +
    +function handleEvent(event) {
    +  mouseWheelHandler.handleEvent(event);
    +  mouseWheelHandlerRtl.handleEvent(event);
    +}
    +
    +function assertMouseWheelEvent(expectedDetail, expectedDeltaX,
    +    expectedDeltaY) {
    +  assertTrue('event should be non-null', !!mouseWheelEvent);
    +  assertTrue('event should have correct JS type',
    +      mouseWheelEvent instanceof goog.events.MouseWheelEvent);
    +  assertEquals('event should have correct detail property',
    +      expectedDetail, mouseWheelEvent.detail);
    +  assertEquals('event should have correct deltaX property',
    +      expectedDeltaX, mouseWheelEvent.deltaX);
    +  assertEquals('event should have correct deltaY property',
    +      expectedDeltaY, mouseWheelEvent.deltaY);
    +
    +  // RTL
    +  assertTrue('event should be non-null', !!mouseWheelEventRtl);
    +  assertTrue('event should have correct JS type',
    +      mouseWheelEventRtl instanceof goog.events.MouseWheelEvent);
    +  assertEquals('event should have correct detail property',
    +      expectedDetail, mouseWheelEventRtl.detail);
    +  assertEquals('event should have correct deltaX property',
    +      -expectedDeltaX, mouseWheelEventRtl.deltaX);
    +  assertEquals('event should have correct deltaY property',
    +      expectedDeltaY, mouseWheelEventRtl.deltaY);
    +
    +}
    +
    +function createFakeMouseWheelEvent(type, opt_wheelDelta, opt_detail,
    +    opt_axis, opt_wheelDeltaX, opt_wheelDeltaY) {
    +  var event = {
    +    type: type,
    +    wheelDelta: goog.isDef(opt_wheelDelta) ? opt_wheelDelta : undefined,
    +    detail: goog.isDef(opt_detail) ? opt_detail : undefined,
    +    axis: opt_axis || undefined,
    +    wheelDeltaX: goog.isDef(opt_wheelDeltaX) ? opt_wheelDeltaX : undefined,
    +    wheelDeltaY: goog.isDef(opt_wheelDeltaY) ? opt_wheelDeltaY : undefined,
    +
    +    // These two are constants defined on the event in FF3.1 and later.
    +    // It doesn't matter exactly what they are, and it doesn't affect
    +    // our simulations of other browsers.
    +    HORIZONTAL_AXIS: HORIZONTAL,
    +    VERTICAL_AXIS: VERTICAL
    +  };
    +  return new goog.events.BrowserEvent(event);
    +}
    +
    +function createFakeWebkitMouseWheelEvent(wheelDeltaX, wheelDeltaY) {
    +  return createFakeMouseWheelEvent(DEFAULT_TYPE,
    +      Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY) ?
    +          wheelDeltaX : wheelDeltaY,
    +      undefined, undefined, wheelDeltaX, wheelDeltaY);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/onlinehandler.js b/mapbox-gl/closure-library/closure/goog/events/onlinehandler.js
    new file mode 100644
    index 0000000000..5c9fb1620b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/onlinehandler.js
    @@ -0,0 +1,159 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This event handler will dispatch events when
    + * {@code navigator.onLine} changes.  HTML5 defines two events, online and
    + * offline that is fired on the window.  As of today 3 browsers support these
    + * events: Firefox 3 (Gecko 1.9), Opera 9.5, and IE8.  If we have any of these
    + * we listen to the 'online' and 'offline' events on the current window
    + * object.  Otherwise we poll the navigator.onLine property to detect changes.
    + *
    + * Note that this class only reflects what the browser tells us and this usually
    + * only reflects changes to the File -> Work Offline menu item.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/onlinehandler.html
    + */
    +
    +// TODO(arv): We should probably implement some kind of polling service and/or
    +// a poll for changes event handler that can be used to fire events when a state
    +// changes.
    +
    +goog.provide('goog.events.OnlineHandler');
    +goog.provide('goog.events.OnlineHandler.EventType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.NetworkStatusMonitor');
    +
    +
    +
    +/**
    + * Basic object for detecting whether the online state changes.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @implements {goog.net.NetworkStatusMonitor}
    + */
    +goog.events.OnlineHandler = function() {
    +  goog.events.OnlineHandler.base(this, 'constructor');
    +
    +  /**
    +   * @private {goog.events.EventHandler<!goog.events.OnlineHandler>}
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  // Some browsers do not support navigator.onLine and therefore we don't
    +  // bother setting up events or timers.
    +  if (!goog.events.BrowserFeature.HAS_NAVIGATOR_ONLINE_PROPERTY) {
    +    return;
    +  }
    +
    +  if (goog.events.BrowserFeature.HAS_HTML5_NETWORK_EVENT_SUPPORT) {
    +    var target =
    +        goog.events.BrowserFeature.HTML5_NETWORK_EVENTS_FIRE_ON_BODY ?
    +        document.body : window;
    +    this.eventHandler_.listen(target,
    +        [goog.events.EventType.ONLINE, goog.events.EventType.OFFLINE],
    +        this.handleChange_);
    +  } else {
    +    this.online_ = this.isOnline();
    +    this.timer_ = new goog.Timer(goog.events.OnlineHandler.POLL_INTERVAL_);
    +    this.eventHandler_.listen(this.timer_, goog.Timer.TICK, this.handleTick_);
    +    this.timer_.start();
    +  }
    +};
    +goog.inherits(goog.events.OnlineHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum for the events dispatched by the OnlineHandler.
    + * @enum {string}
    + * @deprecated Use goog.net.NetworkStatusMonitor.EventType instead.
    + */
    +goog.events.OnlineHandler.EventType = goog.net.NetworkStatusMonitor.EventType;
    +
    +
    +/**
    + * The time to wait before checking the {@code navigator.onLine} again.
    + * @type {number}
    + * @private
    + */
    +goog.events.OnlineHandler.POLL_INTERVAL_ = 250;
    +
    +
    +/**
    + * Stores the last value of the online state so we can detect if this has
    + * changed.
    + * @type {boolean}
    + * @private
    + */
    +goog.events.OnlineHandler.prototype.online_;
    +
    +
    +/**
    + * The timer object used to poll the online state.
    + * @type {goog.Timer}
    + * @private
    + */
    +goog.events.OnlineHandler.prototype.timer_;
    +
    +
    +/** @override */
    +goog.events.OnlineHandler.prototype.isOnline = function() {
    +  return goog.events.BrowserFeature.HAS_NAVIGATOR_ONLINE_PROPERTY ?
    +      navigator.onLine : true;
    +};
    +
    +
    +/**
    + * Called every time the timer ticks to see if the state has changed and when
    + * the online state changes the method handleChange_ is called.
    + * @private
    + */
    +goog.events.OnlineHandler.prototype.handleTick_ = function() {
    +  var online = this.isOnline();
    +  if (online != this.online_) {
    +    this.online_ = online;
    +    this.handleChange_();
    +  }
    +};
    +
    +
    +/**
    + * Called when the online state changes.  This dispatches the
    + * {@code ONLINE} and {@code OFFLINE} events respectively.
    + * @private
    + */
    +goog.events.OnlineHandler.prototype.handleChange_ = function() {
    +  var type = this.isOnline() ?
    +      goog.net.NetworkStatusMonitor.EventType.ONLINE :
    +      goog.net.NetworkStatusMonitor.EventType.OFFLINE;
    +  this.dispatchEvent(type);
    +};
    +
    +
    +/** @override */
    +goog.events.OnlineHandler.prototype.disposeInternal = function() {
    +  goog.events.OnlineHandler.base(this, 'disposeInternal');
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +  if (this.timer_) {
    +    this.timer_.dispose();
    +    this.timer_ = null;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.html b/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.html
    new file mode 100644
    index 0000000000..4d243cbef6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.OnlineListener
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.OnlineHandlerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.js b/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.js
    new file mode 100644
    index 0000000000..fa427507e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/onlinelistener_test.js
    @@ -0,0 +1,154 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.OnlineHandlerTest');
    +goog.setTestOnly('goog.events.OnlineHandlerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.OnlineHandler');
    +goog.require('goog.net.NetworkStatusMonitor');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +var clock = new goog.testing.MockClock();
    +var online = true;
    +var onlineCount;
    +var offlineCount;
    +
    +function listenToEvents(oh) {
    +  onlineCount = 0;
    +  offlineCount = 0;
    +
    +  goog.events.listen(oh, goog.net.NetworkStatusMonitor.EventType.ONLINE,
    +                     function(e) {
    +                       assertTrue(oh.isOnline());
    +                       onlineCount++;
    +                     });
    +  goog.events.listen(oh, goog.net.NetworkStatusMonitor.EventType.OFFLINE,
    +                     function(e) {
    +                       assertFalse(oh.isOnline());
    +                       offlineCount++;
    +                     });
    +}
    +
    +function setUp() {
    +  stubs.set(goog.events.OnlineHandler.prototype, 'isOnline', function() {
    +    return online;
    +  });
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +  clock.uninstall();
    +}
    +
    +function testConstructAndDispose() {
    +  var oh = new goog.events.OnlineHandler();
    +  oh.dispose();
    +}
    +
    +function testNoOnlineProperty() {
    +  stubs.set(goog.events.BrowserFeature,
    +      'HAS_NAVIGATOR_ONLINE_PROPERTY', false);
    +  stubs.set(goog.events.EventHandler.prototype, 'listen',
    +      goog.testing.recordFunction());
    +
    +  var oh = new goog.events.OnlineHandler();
    +
    +  assertEquals(0, oh.eventHandler_.listen.getCallCount());
    +
    +  oh.dispose();
    +}
    +
    +function testNonHtml5() {
    +  clock.install();
    +  stubs.set(goog.events.BrowserFeature,
    +      'HAS_HTML5_NETWORK_EVENT_SUPPORT', false);
    +
    +  var oh = new goog.events.OnlineHandler();
    +  listenToEvents(oh);
    +
    +  clock.tick(500);
    +  online = false;
    +  clock.tick(500);
    +
    +  assertEquals(0, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  online = true;
    +  clock.tick(500);
    +
    +  assertEquals(1, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  oh.dispose();
    +  clock.dispose();
    +}
    +
    +function testHtml5() {
    +  stubs.set(goog.events.BrowserFeature,
    +      'HAS_HTML5_NETWORK_EVENT_SUPPORT', true);
    +
    +  // Test for browsers that fire network events on document.body.
    +  stubs.set(goog.events.BrowserFeature,
    +      'HTML5_NETWORK_EVENTS_FIRE_ON_BODY', true);
    +
    +  var oh = new goog.events.OnlineHandler();
    +  listenToEvents(oh);
    +
    +  online = false;
    +  var e = new goog.events.Event('offline');
    +  goog.events.fireListeners(document.body, e.type, false, e);
    +
    +  assertEquals(0, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  online = true;
    +  e = new goog.events.Event('online');
    +  goog.events.fireListeners(document.body, e.type, false, e);
    +
    +  assertEquals(1, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  oh.dispose();
    +
    +  // Test for browsers that fire network events on window.
    +  stubs.set(goog.events.BrowserFeature,
    +      'HTML5_NETWORK_EVENTS_FIRE_ON_BODY', false);
    +
    +  oh = new goog.events.OnlineHandler();
    +  listenToEvents(oh);
    +
    +  online = false;
    +  e = new goog.events.Event('offline');
    +  goog.events.fireListeners(window, e.type, false, e);
    +
    +  assertEquals(0, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  online = true;
    +  e = new goog.events.Event('online');
    +  goog.events.fireListeners(window, e.type, false, e);
    +
    +  assertEquals(1, onlineCount);
    +  assertEquals(1, offlineCount);
    +
    +  oh.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/pastehandler.js b/mapbox-gl/closure-library/closure/goog/events/pastehandler.js
    new file mode 100644
    index 0000000000..4992ff0fe0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/pastehandler.js
    @@ -0,0 +1,517 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a 'paste' event detector that works consistently
    + * across different browsers.
    + *
    + * IE5, IE6, IE7, Safari3.0 and FF3.0 all fire 'paste' events on textareas.
    + * FF2 doesn't. This class uses 'paste' events when they are available
    + * and uses heuristics to detect the 'paste' event when they are not available.
    + *
    + * Known issue: will not detect paste events in FF2 if you pasted exactly the
    + * same existing text.
    + * Known issue: Opera + Mac doesn't work properly because of the meta key. We
    + * can probably fix that. TODO(user): {@link KeyboardShortcutHandler} does not
    + * work either very well with opera + mac. fix that.
    + *
    + * @supported IE5, IE6, IE7, Safari3.0, Chrome, FF2.0 (linux) and FF3.0 and
    + * Opera (mac and windows).
    + *
    + * @see ../demos/pastehandler.html
    + */
    +
    +goog.provide('goog.events.PasteHandler');
    +goog.provide('goog.events.PasteHandler.EventType');
    +goog.provide('goog.events.PasteHandler.State');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.async.ConditionalDelay');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.log');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A paste event detector. Gets an {@code element} as parameter and fires
    + * {@code goog.events.PasteHandler.EventType.PASTE} events when text is
    + * pasted in the {@code element}. Uses heuristics to detect paste events in FF2.
    + * See more details of the heuristic on {@link #handleEvent_}.
    + *
    + * @param {Element} element The textarea element we are listening on.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.events.PasteHandler = function(element) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * The element that you want to listen for paste events on.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  /**
    +   * The last known value of the element. Kept to check if things changed. See
    +   * more details on {@link #handleEvent_}.
    +   * @type {string}
    +   * @private
    +   */
    +  this.oldValue_ = this.element_.value;
    +
    +  /**
    +   * Handler for events.
    +   * @type {goog.events.EventHandler<!goog.events.PasteHandler>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The last time an event occurred on the element. Kept to check whether the
    +   * last event was generated by two input events or by multiple fast key events
    +   * that got swallowed. See more details on {@link #handleEvent_}.
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastTime_ = goog.now();
    +
    +  if (goog.userAgent.WEBKIT ||
    +      goog.userAgent.IE ||
    +      goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9')) {
    +    // Most modern browsers support the paste event.
    +    this.eventHandler_.listen(element, goog.events.EventType.PASTE,
    +        this.dispatch_);
    +  } else {
    +    // But FF2 and Opera doesn't. we listen for a series of events to try to
    +    // find out if a paste occurred. We enumerate and cover all known ways to
    +    // paste text on textareas.  See more details on {@link #handleEvent_}.
    +    var events = [
    +      goog.events.EventType.KEYDOWN,
    +      goog.events.EventType.BLUR,
    +      goog.events.EventType.FOCUS,
    +      goog.events.EventType.MOUSEOVER,
    +      'input'
    +    ];
    +    this.eventHandler_.listen(element, events, this.handleEvent_);
    +  }
    +
    +  /**
    +   * ConditionalDelay used to poll for changes in the text element once users
    +   * paste text. Browsers fire paste events BEFORE the text is actually present
    +   * in the element.value property.
    +   * @type {goog.async.ConditionalDelay}
    +   * @private
    +   */
    +  this.delay_ = new goog.async.ConditionalDelay(
    +      goog.bind(this.checkUpdatedText_, this));
    +
    +};
    +goog.inherits(goog.events.PasteHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * The types of events fired by this class.
    + * @enum {string}
    + */
    +goog.events.PasteHandler.EventType = {
    +  /**
    +   * Dispatched as soon as the paste event is detected, but before the pasted
    +   * text has been added to the text element we're listening to.
    +   */
    +  PASTE: 'paste',
    +
    +  /**
    +   * Dispatched after detecting a change to the value of text element
    +   * (within 200msec of receiving the PASTE event).
    +   */
    +  AFTER_PASTE: 'after_paste'
    +};
    +
    +
    +/**
    + * The mandatory delay we expect between two {@code input} events, used to
    + * differentiated between non key paste events and key events.
    + * @type {number}
    + */
    +goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER =
    +    400;
    +
    +
    +/**
    + * The period between each time we check whether the pasted text appears in the
    + * text element or not.
    + * @type {number}
    + * @private
    + */
    +goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_ = 50;
    +
    +
    +/**
    + * The maximum amount of time we want to poll for changes.
    + * @type {number}
    + * @private
    + */
    +goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_ = 200;
    +
    +
    +/**
    + * The states that this class can be found, on the paste detection algorithm.
    + * @enum {string}
    + */
    +goog.events.PasteHandler.State = {
    +  INIT: 'init',
    +  FOCUSED: 'focused',
    +  TYPING: 'typing'
    +};
    +
    +
    +/**
    + * The initial state of the paste detection algorithm.
    + * @type {goog.events.PasteHandler.State}
    + * @private
    + */
    +goog.events.PasteHandler.prototype.state_ =
    +    goog.events.PasteHandler.State.INIT;
    +
    +
    +/**
    + * The previous event that caused us to be on the current state.
    + * @type {?string}
    + * @private
    + */
    +goog.events.PasteHandler.prototype.previousEvent_;
    +
    +
    +/**
    + * A logger, used to help us debug the algorithm.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.events.PasteHandler.prototype.logger_ =
    +    goog.log.getLogger('goog.events.PasteHandler');
    +
    +
    +/** @override */
    +goog.events.PasteHandler.prototype.disposeInternal = function() {
    +  goog.events.PasteHandler.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +  this.delay_.dispose();
    +  this.delay_ = null;
    +};
    +
    +
    +/**
    + * Returns the current state of the paste detection algorithm. Used mostly for
    + * testing.
    + * @return {goog.events.PasteHandler.State} The current state of the class.
    + */
    +goog.events.PasteHandler.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * Returns the event handler.
    + * @return {goog.events.EventHandler<T>} The event handler.
    + * @protected
    + * @this T
    + * @template T
    + */
    +goog.events.PasteHandler.prototype.getEventHandler = function() {
    +  return this.eventHandler_;
    +};
    +
    +
    +/**
    + * Checks whether the element.value property was updated, and if so, dispatches
    + * the event that let clients know that the text is available.
    + * @return {boolean} Whether the polling should stop or not, based on whether
    + *     we found a text change or not.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.checkUpdatedText_ = function() {
    +  if (this.oldValue_ == this.element_.value) {
    +    return false;
    +  }
    +  goog.log.info(this.logger_, 'detected textchange after paste');
    +  this.dispatchEvent(goog.events.PasteHandler.EventType.AFTER_PASTE);
    +  return true;
    +};
    +
    +
    +/**
    + * Dispatches the paste event.
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.dispatch_ = function(e) {
    +  var event = new goog.events.BrowserEvent(e.getBrowserEvent());
    +  event.type = goog.events.PasteHandler.EventType.PASTE;
    +  this.dispatchEvent(event);
    +
    +  // Starts polling for updates in the element.value property so we can tell
    +  // when do dispatch the AFTER_PASTE event. (We do an initial check after an
    +  // async delay of 0 msec since some browsers update the text right away and
    +  // our poller will always wait one period before checking).
    +  goog.Timer.callOnce(function() {
    +    if (!this.checkUpdatedText_()) {
    +      this.delay_.start(
    +          goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_,
    +          goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
    +    }
    +  }, 0, this);
    +};
    +
    +
    +/**
    + * The main event handler which implements a state machine.
    + *
    + * To handle FF2, we enumerate and cover all the known ways a user can paste:
    + *
    + * 1) ctrl+v, shift+insert, cmd+v
    + * 2) right click -> paste
    + * 3) edit menu -> paste
    + * 4) drag and drop
    + * 5) middle click
    + *
    + * (1) is easy and can be detected by listening for key events and finding out
    + * which keys are pressed. (2), (3), (4) and (5) do not generate a key event,
    + * so we need to listen for more than that. (2-5) all generate 'input' events,
    + * but so does key events. So we need to have some sort of 'how did the input
    + * event was generated' history algorithm.
    + *
    + * (2) is an interesting case in Opera on a Mac: since Macs does not have two
    + * buttons, right clicking involves pressing the CTRL key. Even more interesting
    + * is the fact that opera does NOT set the e.ctrlKey bit. Instead, it sets
    + * e.keyCode = 0.
    + * {@link http://www.quirksmode.org/js/keys.html}
    + *
    + * (1) is also an interesting case in Opera on a Mac: Opera is the only browser
    + * covered by this class that can detect the cmd key (FF2 can't apparently). And
    + * it fires e.keyCode = 17, which is the CTRL key code.
    + * {@link http://www.quirksmode.org/js/keys.html}
    + *
    + * NOTE(user, pbarry): There is an interesting thing about (5): on Linux, (5)
    + * pastes the last thing that you highlighted, not the last thing that you
    + * ctrl+c'ed. This code will still generate a {@code PASTE} event though.
    + *
    + * We enumerate all the possible steps a user can take to paste text and we
    + * implemented the transition between the steps in a state machine. The
    + * following is the design of the state machine:
    + *
    + * matching paths:
    + *
    + * (1) happens on INIT -> FOCUSED -> TYPING -> [e.ctrlKey & e.keyCode = 'v']
    + * (2-3) happens on INIT -> FOCUSED -> [input event happened]
    + * (4) happens on INIT -> [mouseover && text changed]
    + *
    + * non matching paths:
    + *
    + * user is typing normally
    + * INIT -> FOCUS -> TYPING -> INPUT -> INIT
    + *
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.handleEvent_ = function(e) {
    +  // transition between states happen at each browser event, and depend on the
    +  // current state, the event that led to this state, and the event input.
    +  switch (this.state_) {
    +    case goog.events.PasteHandler.State.INIT: {
    +      this.handleUnderInit_(e);
    +      break;
    +    }
    +    case goog.events.PasteHandler.State.FOCUSED: {
    +      this.handleUnderFocused_(e);
    +      break;
    +    }
    +    case goog.events.PasteHandler.State.TYPING: {
    +      this.handleUnderTyping_(e);
    +      break;
    +    }
    +    default: {
    +      goog.log.error(this.logger_, 'invalid ' + this.state_ + ' state');
    +    }
    +  }
    +  this.lastTime_ = goog.now();
    +  this.oldValue_ = this.element_.value;
    +  goog.log.info(this.logger_, e.type + ' -> ' + this.state_);
    +  this.previousEvent_ = e.type;
    +};
    +
    +
    +/**
    + * {@code goog.events.PasteHandler.EventType.INIT} is the first initial state
    + * the textarea is found. You can only leave this state by setting focus on the
    + * textarea, which is how users will input text. You can also paste things using
    + * drag and drop, which will not generate a {@code goog.events.EventType.FOCUS}
    + * event, but will generate a {@code goog.events.EventType.MOUSEOVER}.
    + *
    + * For browsers that support the 'paste' event, we match it and stay on the same
    + * state.
    + *
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.handleUnderInit_ = function(e) {
    +  switch (e.type) {
    +    case goog.events.EventType.BLUR: {
    +      this.state_ = goog.events.PasteHandler.State.INIT;
    +      break;
    +    }
    +    case goog.events.EventType.FOCUS: {
    +      this.state_ = goog.events.PasteHandler.State.FOCUSED;
    +      break;
    +    }
    +    case goog.events.EventType.MOUSEOVER: {
    +      this.state_ = goog.events.PasteHandler.State.INIT;
    +      if (this.element_.value != this.oldValue_) {
    +        goog.log.info(this.logger_, 'paste by dragdrop while on init!');
    +        this.dispatch_(e);
    +      }
    +      break;
    +    }
    +    default: {
    +      goog.log.error(this.logger_,
    +          'unexpected event ' + e.type + 'during init');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * {@code goog.events.PasteHandler.EventType.FOCUSED} is typically the second
    + * state the textarea will be, which is followed by the {@code INIT} state. On
    + * this state, users can paste in three different ways: edit -> paste,
    + * right click -> paste and drag and drop.
    + *
    + * The latter will generate a {@code goog.events.EventType.MOUSEOVER} event,
    + * which we match by making sure the textarea text changed. The first two will
    + * generate an 'input', which we match by making sure it was NOT generated by a
    + * key event (which also generates an 'input' event).
    + *
    + * Unfortunately, in Firefox, if you type fast, some KEYDOWN events are
    + * swallowed but an INPUT event may still happen. That means we need to
    + * differentiate between two consecutive INPUT events being generated either by
    + * swallowed key events OR by a valid edit -> paste -> edit -> paste action. We
    + * do this by checking a minimum time between the two events. This heuristic
    + * seems to work well, but it is obviously a heuristic :).
    + *
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.handleUnderFocused_ = function(e) {
    +  switch (e.type) {
    +    case 'input' : {
    +      // there are two different events that happen in practice that involves
    +      // consecutive 'input' events. we use a heuristic to differentiate
    +      // between the one that generates a valid paste action and the one that
    +      // doesn't.
    +      // @see testTypingReallyFastDispatchesTwoInputEventsBeforeTheKEYDOWNEvent
    +      // and
    +      // @see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents
    +      // Notice that an 'input' event may be also triggered by a 'middle click'
    +      // paste event, which is described in
    +      // @see testMiddleClickWithoutFocusTriggersPasteEvent
    +      var minimumMilisecondsBetweenInputEvents = this.lastTime_ +
    +          goog.events.PasteHandler.
    +              MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER;
    +      if (goog.now() > minimumMilisecondsBetweenInputEvents ||
    +          this.previousEvent_ == goog.events.EventType.FOCUS) {
    +        goog.log.info(this.logger_, 'paste by textchange while focused!');
    +        this.dispatch_(e);
    +      }
    +      break;
    +    }
    +    case goog.events.EventType.BLUR: {
    +      this.state_ = goog.events.PasteHandler.State.INIT;
    +      break;
    +    }
    +    case goog.events.EventType.KEYDOWN: {
    +      goog.log.info(this.logger_, 'key down ... looking for ctrl+v');
    +      // Opera + MAC does not set e.ctrlKey. Instead, it gives me e.keyCode = 0.
    +      // http://www.quirksmode.org/js/keys.html
    +      if (goog.userAgent.MAC && goog.userAgent.OPERA && e.keyCode == 0 ||
    +          goog.userAgent.MAC && goog.userAgent.OPERA && e.keyCode == 17) {
    +        break;
    +      }
    +      this.state_ = goog.events.PasteHandler.State.TYPING;
    +      break;
    +    }
    +    case goog.events.EventType.MOUSEOVER: {
    +      if (this.element_.value != this.oldValue_) {
    +        goog.log.info(this.logger_, 'paste by dragdrop while focused!');
    +        this.dispatch_(e);
    +      }
    +      break;
    +    }
    +    default: {
    +      goog.log.error(this.logger_,
    +          'unexpected event ' + e.type + ' during focused');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * {@code goog.events.PasteHandler.EventType.TYPING} is the third state
    + * this class can be. It exists because each KEYPRESS event will ALSO generate
    + * an INPUT event (because the textarea value changes), and we need to
    + * differentiate between an INPUT event generated by a key event and an INPUT
    + * event generated by edit -> paste actions.
    + *
    + * This is the state that we match the ctrl+v pattern.
    + *
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +goog.events.PasteHandler.prototype.handleUnderTyping_ = function(e) {
    +  switch (e.type) {
    +    case 'input' : {
    +      this.state_ = goog.events.PasteHandler.State.FOCUSED;
    +      break;
    +    }
    +    case goog.events.EventType.BLUR: {
    +      this.state_ = goog.events.PasteHandler.State.INIT;
    +      break;
    +    }
    +    case goog.events.EventType.KEYDOWN: {
    +      if (e.ctrlKey && e.keyCode == goog.events.KeyCodes.V ||
    +          e.shiftKey && e.keyCode == goog.events.KeyCodes.INSERT ||
    +          e.metaKey && e.keyCode == goog.events.KeyCodes.V) {
    +        goog.log.info(this.logger_, 'paste by ctrl+v while keypressed!');
    +        this.dispatch_(e);
    +      }
    +      break;
    +    }
    +    case goog.events.EventType.MOUSEOVER: {
    +      if (this.element_.value != this.oldValue_) {
    +        goog.log.info(this.logger_, 'paste by dragdrop while keypressed!');
    +        this.dispatch_(e);
    +      }
    +      break;
    +    }
    +    default: {
    +      goog.log.error(this.logger_,
    +          'unexpected event ' + e.type + ' during keypressed');
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.html b/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.html
    new file mode 100644
    index 0000000000..d1ce327cd7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.PasteHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.PasteHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <textarea id="foo">
    +  </textarea>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.js b/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.js
    new file mode 100644
    index 0000000000..7072f1fac3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/pastehandler_test.js
    @@ -0,0 +1,409 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.PasteHandlerTest');
    +goog.setTestOnly('goog.events.PasteHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.PasteHandler');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  // TODO(user): fix {@code goog.testing.MockUserAgent} to do the right thing.
    +  // the code doesn't seem to be updating the variables with
    +  // goog.userAgent.init_(), which means it is not allowing me to mock the
    +  // user agent variables.
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.VERSION = '1.8';
    +
    +  textarea = new goog.events.EventTarget();
    +  textarea.value = '';
    +  clock = new goog.testing.MockClock(true);
    +  mockUserAgent = new goog.testing.MockUserAgent();
    +  handler = new goog.events.PasteHandler(textarea);
    +  pasted = false;
    +  goog.events.listen(handler, goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +}
    +
    +function tearDown() {
    +  textarea.dispose();
    +  handler.dispose();
    +  clock.dispose();
    +  mockUserAgent.dispose();
    +}
    +
    +function newBrowserEvent(type) {
    +  if (goog.isString(type)) {
    +    return new goog.events.BrowserEvent({type: type});
    +  } else {
    +    return new goog.events.BrowserEvent(type);
    +  }
    +}
    +
    +function testDispatchingPasteEventSupportedByAFewBrowsersWork() {
    +  goog.userAgent.IE = true;
    +  var handlerThatSupportsPasteEvents =
    +      new goog.events.PasteHandler(textarea);
    +  // user clicks on the textarea and give it focus
    +  goog.events.listen(handlerThatSupportsPasteEvents,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  textarea.dispatchEvent(newBrowserEvent('paste'));
    +  assertTrue(pasted);
    +}
    +
    +function testJustTypingDoesntFirePasteEvent() {
    +  // user clicks on the textarea and give it focus
    +  textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
    +  assertFalse(pasted);
    +  // user starts typing
    +  textarea.dispatchEvent(newBrowserEvent({
    +    type: goog.events.EventType.KEYDOWN,
    +    keyCode: goog.events.KeyCodes.A
    +  }));
    +  textarea.value = 'a';
    +  assertFalse(pasted);
    +
    +  // still typing
    +  textarea.dispatchEvent({
    +    type: goog.events.EventType.KEYDOWN,
    +    keyCode: goog.events.KeyCodes.B
    +  });
    +  textarea.value = 'ab';
    +  assertFalse(pasted);
    +
    +  // ends typing
    +  textarea.dispatchEvent({
    +    type: goog.events.EventType.KEYDOWN,
    +    keyCode: goog.events.KeyCodes.C
    +  });
    +  textarea.value = 'abc';
    +  assertFalse(pasted);
    +}
    +
    +function testStartsOnInitialState() {
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
    +  assertFalse(pasted);
    +}
    +
    +function testBlurOnInit() {
    +  textarea.dispatchEvent(goog.events.EventType.BLUR);
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
    +  assertFalse(pasted);
    +}
    +
    +function testFocusOnInit() {
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
    +  assertFalse(pasted);
    +}
    +
    +function testInputOnFocus() {
    +  // user clicks on the textarea
    +  textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  // and right click -> paste a text!
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.FOCUSED);
    +  // make sure we detected it
    +  assertTrue(pasted);
    +}
    +
    +function testKeyPressOnFocus() {
    +  // user clicks on the textarea
    +  textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.FOCUS));
    +
    +  // starts typing something
    +  textarea.dispatchEvent(newBrowserEvent({
    +    type: goog.events.EventType.KEYDOWN,
    +    keyCode: goog.events.KeyCodes.A
    +  }));
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
    +  assertFalse(pasted);
    +
    +  // and then presses ctrl+v
    +  textarea.dispatchEvent(newBrowserEvent({
    +    type: goog.events.EventType.KEYDOWN,
    +    keyCode: goog.events.KeyCodes.V,
    +    ctrlKey: true
    +  }));
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.TYPING);
    +
    +  // makes sure we detected it
    +  assertTrue(pasted);
    +}
    +
    +function testMouseOverOnInit() {
    +  // user has something on the events
    +  textarea.value = 'pasted string';
    +  // and right click -> paste it on the textarea, WITHOUT giving focus
    +  textarea.dispatchEvent(newBrowserEvent(goog.events.EventType.MOUSEOVER));
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
    +  // makes sure we detect it
    +  assertTrue(pasted);
    +
    +  pasted = false;
    +
    +  // user normaly mouseovers the textarea, with no text change
    +  textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
    +  assertTrue(handler.getState() == goog.events.PasteHandler.State.INIT);
    +  // text area value doesnt change
    +  assertFalse(pasted);
    +}
    +
    +function testMouseOverAfterTyping() {
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +  assertFalse(pasted);
    +  textarea.dispatchEvent(
    +      {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
    +  assertFalse(pasted);
    +  textarea.value = 'a';
    +  textarea.dispatchEvent('input');
    +  assertFalse(pasted);
    +  assertEquals('a', handler.oldValue_);
    +  textarea.dispatchEvent(goog.events.EventType.MOUSEOVER);
    +  assertFalse(pasted);
    +}
    +
    +function testTypingAndThenRightClickPaste() {
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +
    +  textarea.dispatchEvent(
    +      {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
    +  assertFalse(pasted);
    +  textarea.value = 'a';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  textarea.dispatchEvent('input');
    +  assertFalse(pasted);
    +
    +  assertEquals('a', handler.oldValue_);
    +
    +  textarea.value = 'ab';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +}
    +
    +function testTypingReallyFastDispatchesTwoInputEventsBeforeTheKeyDownEvent() {
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +
    +  // keydown and input events seems to be fired indepently: even though input
    +  // should happen after the key event, it doens't if the user types fast
    +  // enough. FF2 + linux doesn't fire keydown events for every key pressed when
    +  // you type fast enough. if one of the keydown events gets swallowed, two
    +  // input events are fired consecutively. notice that there is a similar
    +  // scenario, that actually does produce a valid paste action.
    +  // {@see testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents}
    +
    +  textarea.dispatchEvent(
    +      {type: goog.events.EventType.KEYDOWN, keyCode: goog.events.KeyCodes.A});
    +  assertFalse(pasted);
    +  textarea.value = 'a';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
    +      1);
    +  textarea.dispatchEvent('input');
    +  assertFalse(pasted);
    +
    +  // second key down events gets fired on a different order
    +  textarea.value = 'ab';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER -
    +      1);
    +  textarea.dispatchEvent('input');
    +  assertFalse(pasted);
    +}
    +
    +function testRightClickRightClickAlsoDispatchesTwoConsecutiveInputEvents() {
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +
    +  // there is also another case that two consecutive INPUT events are fired,
    +  // but in a valid paste action: if the user edit -> paste -> edit -> paste,
    +  // it is a valid paste action.
    +
    +  textarea.value = 'a';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +
    +  // second key down events gets fired on a different order
    +  textarea.value = 'ab';
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +}
    +
    +function testMiddleClickWithoutFocusTriggersPasteEvent() {
    +  // if the textarea is NOT selected, and then we use the middle button,
    +  // FF2+linux pastes what was last highlighted, causing a paste action.
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +}
    +
    +
    +function testMacRightClickPasteRequiresCtrlBecauseItHasOneButton() {
    +  // Macs don't have two buttons mouse: this means that you need to press
    +  // ctrl + click to get to the menu, and then you can click paste.
    +  // The sequences of events fired on Opera are:
    +  // focus -> keydown (keyCode == 0, not e.ctrlKey) -> input
    +  goog.userAgent.OPERA = true;
    +  goog.userAgent.MAC = true;
    +  var handler = new goog.events.PasteHandler(textarea);
    +  // user clicks on the textarea and give it focus
    +  goog.events.listen(handler,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +  assertFalse(pasted);
    +  textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 0});
    +  assertFalse(pasted);
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +}
    +
    +function testOperaMacFiresKeyCode17WhenAppleKeyPressedButDoesNotFireKeyDown() {
    +  // Opera on Macs fires keycode 17 when apple key is pressed, and then it does
    +  // not fire a keydown event when the V is pressed.
    +  goog.userAgent.OPERA = true;
    +  goog.userAgent.MAC = true;
    +  var handler = new goog.events.PasteHandler(textarea);
    +  // user clicks on the textarea and give it focus
    +  goog.events.listen(handler,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  textarea.dispatchEvent(goog.events.EventType.FOCUS);
    +  assertFalse(pasted);
    +  // apple key is pressed, generating a keydown event
    +  textarea.dispatchEvent({type: goog.events.EventType.KEYDOWN, keyCode: 17});
    +  assertFalse(pasted);
    +  clock.tick(
    +      goog.events.PasteHandler.MANDATORY_MS_BETWEEN_INPUT_EVENTS_TIE_BREAKER +
    +      1);
    +  // and then text is added magically without any extra keydown events.
    +  textarea.dispatchEvent(newBrowserEvent('input'));
    +  assertTrue(pasted);
    +}
    +
    +function testScriptingDoesntTriggerPasteEvents() {
    +  var handlerUsedToListenForScriptingChanges =
    +      new goog.events.PasteHandler(textarea);
    +  pasted = false;
    +  // user clicks on the textarea and give it focus
    +  goog.events.listen(handlerUsedToListenForScriptingChanges,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  goog.dom.getElement('foo').value = 'dear paste handler,';
    +  assertFalse(pasted);
    +  goog.dom.getElement('foo').value = 'please dont misunderstand script changes';
    +  assertFalse(pasted);
    +  goog.dom.getElement('foo').value = 'with user generated paste events';
    +  assertFalse(pasted);
    +  goog.dom.getElement('foo').value = 'thanks!';
    +  assertFalse(pasted);
    +}
    +
    +function testAfterPaste() {
    +  goog.userAgent.IE = true;
    +  var handlerThatSupportsPasteEvents =
    +      new goog.events.PasteHandler(textarea);
    +  pasted = false;
    +  goog.events.listen(handlerThatSupportsPasteEvents,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  var afterPasteFired = false;
    +  goog.events.listen(handlerThatSupportsPasteEvents,
    +      goog.events.PasteHandler.EventType.AFTER_PASTE,
    +      function() {
    +        afterPasteFired = true;
    +      });
    +
    +  // Initial paste event comes before AFTER_PASTE has fired.
    +  textarea.dispatchEvent(newBrowserEvent('paste'));
    +  assertTrue(pasted);
    +  assertFalse(afterPasteFired);
    +
    +  // Once text is pasted, it takes a bit to detect it, at which point
    +  // AFTER_PASTE is fired.
    +  clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
    +  textarea.value = 'text';
    +  clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
    +  assertTrue(afterPasteFired);
    +}
    +
    +
    +function testAfterPasteNotFiredIfDelayTooLong() {
    +  goog.userAgent.IE = true;
    +  var handlerThatSupportsPasteEvents =
    +      new goog.events.PasteHandler(textarea);
    +  pasted = false;
    +  goog.events.listen(handlerThatSupportsPasteEvents,
    +      goog.events.PasteHandler.EventType.PASTE,
    +      function() {
    +        pasted = true;
    +      });
    +  var afterPasteFired = false;
    +  goog.events.listen(handlerThatSupportsPasteEvents,
    +      goog.events.PasteHandler.EventType.AFTER_PASTE,
    +      function() {
    +        afterPasteFired = true;
    +      });
    +
    +  // Initial paste event comes before AFTER_PASTE has fired.
    +  textarea.dispatchEvent(newBrowserEvent('paste'));
    +  assertTrue(pasted);
    +  assertFalse(afterPasteFired);
    +
    +  // If the new text doesn't show up in time, we never fire AFTER_PASTE.
    +  clock.tick(goog.events.PasteHandler.PASTE_POLLING_TIMEOUT_MS_);
    +  textarea.value = 'text';
    +  clock.tick(goog.events.PasteHandler.PASTE_POLLING_PERIOD_MS_);
    +  assertFalse(afterPasteFired);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/events/wheelevent.js b/mapbox-gl/closure-library/closure/goog/events/wheelevent.js
    new file mode 100644
    index 0000000000..1f172eb067
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/wheelevent.js
    @@ -0,0 +1,169 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This class aims to smooth out inconsistencies between browser
    + * handling of wheel events by providing an event that is similar to that
    + * defined in the standard, but also easier to consume.
    + *
    + * It is based upon the WheelEvent, which allows for up to 3 dimensional
    + * scrolling events that come in units of either pixels, lines or pages.
    + * http://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#interface-WheelEvent
    + *
    + * The significant difference here is that it also provides reasonable pixel
    + * deltas for clients that do not want to treat line and page scrolling events
    + * specially.
    + *
    + * Clients of this code should be aware that some input devices only fire a few
    + * discrete events (such as a mouse wheel without acceleration) whereas some can
    + * generate a large number of events for a single interaction (such as a
    + * touchpad with acceleration). There is no signal in the events to reliably
    + * distinguish between these.
    + *
    + * @see ../demos/wheelhandler.html
    + */
    +
    +goog.provide('goog.events.WheelEvent');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.BrowserEvent');
    +
    +
    +
    +/**
    + * A common class for wheel events. This is used with the WheelHandler.
    + *
    + * @param {Event} browserEvent Browser event object.
    + * @param {goog.events.WheelEvent.DeltaMode} deltaMode The delta mode units of
    + *     the wheel event.
    + * @param {number} deltaX The number of delta units the user in the X axis.
    + * @param {number} deltaY The number of delta units the user in the Y axis.
    + * @param {number} deltaZ The number of delta units the user in the Z axis.
    + * @constructor
    + * @extends {goog.events.BrowserEvent}
    + * @final
    + */
    +goog.events.WheelEvent = function(
    +    browserEvent, deltaMode, deltaX, deltaY, deltaZ) {
    +  goog.events.WheelEvent.base(this, 'constructor', browserEvent);
    +  goog.asserts.assert(browserEvent, 'Expecting a non-null browserEvent');
    +
    +  /** @type {goog.events.WheelEvent.EventType} */
    +  this.type = goog.events.WheelEvent.EventType.WHEEL;
    +
    +  /**
    +   * An enum corresponding to the units of this event.
    +   * @type {goog.events.WheelEvent.DeltaMode}
    +   */
    +  this.deltaMode = deltaMode;
    +
    +  /**
    +   * The number of delta units in the X axis.
    +   * @type {number}
    +   */
    +  this.deltaX = deltaX;
    +
    +  /**
    +   * The number of delta units in the Y axis.
    +   * @type {number}
    +   */
    +  this.deltaY = deltaY;
    +
    +  /**
    +   * The number of delta units in the Z axis.
    +   * @type {number}
    +   */
    +  this.deltaZ = deltaZ;
    +
    +  // Ratio between delta and pixel values.
    +  var pixelRatio = 1;  // Value for DeltaMode.PIXEL
    +  switch (deltaMode) {
    +    case goog.events.WheelEvent.DeltaMode.PAGE:
    +      pixelRatio *= goog.events.WheelEvent.PIXELS_PER_PAGE_;
    +      break;
    +    case goog.events.WheelEvent.DeltaMode.LINE:
    +      pixelRatio *= goog.events.WheelEvent.PIXELS_PER_LINE_;
    +      break;
    +  }
    +
    +  /**
    +   * The number of delta pixels in the X axis. Code that doesn't want to handle
    +   * different deltaMode units can just look here.
    +   * @type {number}
    +   */
    +  this.pixelDeltaX = this.deltaX * pixelRatio;
    +
    +  /**
    +   * The number of pixels in the Y axis. Code that doesn't want to
    +   * handle different deltaMode units can just look here.
    +   * @type {number}
    +   */
    +  this.pixelDeltaY = this.deltaY * pixelRatio;
    +
    +  /**
    +   * The number of pixels scrolled in the Z axis. Code that doesn't want to
    +   * handle different deltaMode units can just look here.
    +   * @type {number}
    +   */
    +  this.pixelDeltaZ = this.deltaZ * pixelRatio;
    +};
    +goog.inherits(goog.events.WheelEvent, goog.events.BrowserEvent);
    +
    +
    +/**
    + * Enum type for the events fired by the wheel handler.
    + * @enum {string}
    + */
    +goog.events.WheelEvent.EventType = {
    +  /** The user has provided wheel-based input. */
    +  WHEEL: 'wheel'
    +};
    +
    +
    +/**
    + * Units for the deltas in a WheelEvent.
    + * @enum {number}
    + */
    +goog.events.WheelEvent.DeltaMode = {
    +  /** The units are in pixels. From DOM_DELTA_PIXEL. */
    +  PIXEL: 0,
    +  /** The units are in lines. From DOM_DELTA_LINE. */
    +  LINE: 1,
    +  /** The units are in pages. From DOM_DELTA_PAGE. */
    +  PAGE: 2
    +};
    +
    +
    +/**
    + * A conversion number between line scroll units and pixel scroll units. The
    + * actual value per line can vary a lot between devices and font sizes. This
    + * number can not be perfect, but it should be reasonable for converting lines
    + * scroll events into pixels.
    + * @const {number}
    + * @private
    + */
    +goog.events.WheelEvent.PIXELS_PER_LINE_ = 15;
    +
    +
    +/**
    + * A conversion number between page scroll units and pixel scroll units. The
    + * actual value per page can vary a lot as many different devices have different
    + * screen sizes, and the window might not be taking up the full screen. This
    + * number can not be perfect, but it should be reasonable for converting page
    + * scroll events into pixels.
    + * @const {number}
    + * @private
    + */
    +goog.events.WheelEvent.PIXELS_PER_PAGE_ = 30 *
    +    goog.events.WheelEvent.PIXELS_PER_LINE_;
    diff --git a/mapbox-gl/closure-library/closure/goog/events/wheelhandler.js b/mapbox-gl/closure-library/closure/goog/events/wheelhandler.js
    new file mode 100644
    index 0000000000..9d0f1e33fe
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/wheelhandler.js
    @@ -0,0 +1,159 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This event wrapper will dispatch an event when the user uses
    + * the wheel on an element. The event provides details of the unit type (pixel /
    + * line / page) and deltas in those units in up to 3 dimensions. Additionally,
    + * simplified pixel deltas are provided for code that doesn't need to handle the
    + * different units differently. This is not to be confused with the scroll
    + * event, where an element in the dom can report that it was scrolled.
    + *
    + * This class aims to smooth out inconsistencies between browser platforms with
    + * regards to wheel events, but we do not cover every possible software/hardware
    + * combination out there, some of which occasionally produce very large deltas
    + * in wheel events, especially when the device supports acceleration.
    + *
    + * Relevant standard:
    + * http://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#interface-WheelEvent
    + *
    + * Clients of this code should be aware that some input devices only fire a few
    + * discrete events (such as a mouse wheel without acceleration) whereas some can
    + * generate a large number of events for a single interaction (such as a
    + * touchpad with acceleration). There is no signal in the events to reliably
    + * distinguish between these.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/wheelhandler.html
    + */
    +
    +goog.provide('goog.events.WheelHandler');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.WheelEvent');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +
    +
    +/**
    + * This event handler allows you to catch wheel events in a consistent manner.
    + * @param {!Element|!Document} element The element to listen to the wheel event
    + *     on.
    + * @param {boolean=} opt_capture Whether to handle the wheel event in capture
    + *     phase.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.events.WheelHandler = function(element, opt_capture) {
    +  goog.events.WheelHandler.base(this, 'constructor');
    +
    +  /**
    +   * This is the element that we will listen to the real wheel events on.
    +   * @private {!Element|!Document}
    +   */
    +  this.element_ = element;
    +
    +  var rtlElement = goog.dom.isElement(this.element_) ?
    +      /** @type {!Element} */ (this.element_) :
    +      /** @type {!Document} */ (this.element_).body;
    +
    +  /**
    +   * True if the element exists and is RTL, false otherwise.
    +   * @private {boolean}
    +   */
    +  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);
    +
    +  /**
    +   * The key returned from the goog.events.listen.
    +   * @private {goog.events.Key}
    +   */
    +  this.listenKey_ = goog.events.listen(
    +      this.element_, goog.events.WheelHandler.getDomEventType(),
    +      this, opt_capture);
    +};
    +goog.inherits(goog.events.WheelHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Returns the dom event type.
    + * @return {string} The dom event type.
    + */
    +goog.events.WheelHandler.getDomEventType = function() {
    +  // Prefer to use wheel events whenever supported.
    +  if (goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher(17) ||
    +      goog.userAgent.IE && goog.userAgent.isVersionOrHigher(9) ||
    +      goog.userAgent.product.CHROME && goog.userAgent.product.isVersion(31)) {
    +    return 'wheel';
    +  }
    +
    +  // Legacy events. Still the best we have on Opera and Safari.
    +  return goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';
    +};
    +
    +
    +/**
    + * Handles the events on the element.
    + * @param {!goog.events.BrowserEvent} e The underlying browser event.
    + */
    +goog.events.WheelHandler.prototype.handleEvent = function(e) {
    +  var deltaMode = goog.events.WheelEvent.DeltaMode.PIXEL;
    +  var deltaX = 0;
    +  var deltaY = 0;
    +  var deltaZ = 0;
    +  var be = e.getBrowserEvent();
    +  if (be.type == 'wheel') {
    +    deltaMode = be.deltaMode;
    +    deltaX = be.deltaX;
    +    deltaY = be.deltaY;
    +    deltaZ = be.deltaZ;
    +  } else if (be.type == 'mousewheel') {
    +    // Assume that these are still comparable to pixels. This may not be true
    +    // for all old browsers.
    +    if (goog.isDef(be.wheelDeltaX)) {
    +      deltaX = -be.wheelDeltaX;
    +      deltaY = -be.wheelDeltaY;
    +    } else {
    +      deltaY = -be.wheelDelta;
    +    }
    +  } else { // Historical Gecko
    +    // Gecko returns multiple of 3 (representing the number of lines)
    +    deltaMode = goog.events.WheelEvent.DeltaMode.LINE;
    +    // Firefox 3.1 adds an axis field to the event to indicate axis.
    +    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
    +      deltaX = be.detail;
    +    } else {
    +      deltaY = be.detail;
    +    }
    +  }
    +  // For horizontal deltas we need to flip the value for RTL grids.
    +  if (this.isRtl_) {
    +    deltaX = -deltaX;
    +  }
    +  var newEvent = new goog.events.WheelEvent(
    +      be, deltaMode, deltaX, deltaY, deltaZ);
    +  this.dispatchEvent(newEvent);
    +};
    +
    +
    +/** @override */
    +goog.events.WheelHandler.prototype.disposeInternal = function() {
    +  goog.events.WheelHandler.superClass_.disposeInternal.call(this);
    +  goog.events.unlistenByKey(this.listenKey_);
    +  this.listenKey_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.html b/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.html
    new file mode 100644
    index 0000000000..97994b3ba0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.events.WheelHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.events.WheelHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="foo">
    +  </div>
    +  <div id="fooRtl" dir="rtl">
    +  </div>
    +  <div id="log" style="position:absolute;right:0;top:0">
    +   Logged events:
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.js b/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.js
    new file mode 100644
    index 0000000000..179667d662
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/events/wheelhandler_test.js
    @@ -0,0 +1,296 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.events.WheelHandlerTest');
    +goog.setTestOnly('goog.events.WheelHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.WheelEvent');
    +goog.require('goog.events.WheelHandler');
    +goog.require('goog.string');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +var log;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var PREFERRED_TYPE = 'wheel';
    +var LEGACY_TYPE = 'mousewheel';
    +var GECKO_TYPE = 'DOMMouseScroll';
    +
    +var HORIZONTAL = 'h';
    +var VERTICAL = 'v';
    +
    +var DeltaMode = goog.events.WheelEvent.DeltaMode;
    +
    +var mouseWheelEvent;
    +var mouseWheelEventRtl;
    +var mouseWheelHandler;
    +var mouseWheelHandlerRtl;
    +
    +function setUpPage() {
    +  log = goog.dom.getElement('log');
    +}
    +
    +function setUp() {
    +  stubs.remove(goog, 'userAgent');
    +  goog.userAgent = {
    +    product: {
    +      CHROME: false,
    +      version: 0,
    +      isVersion: function(version) {
    +        return goog.string.compareVersions(this.version, version) >= 0;
    +      }
    +    },
    +    GECKO: false,
    +    IE: false,
    +    version: 0,
    +    isVersionOrHigher: function(version) {
    +      return goog.string.compareVersions(this.version, version) >= 0;
    +    }
    +  };
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +  goog.dispose(mouseWheelHandler);
    +  goog.dispose(mouseWheelHandlerRtl);
    +  mouseWheelHandlerRtl = null;
    +  mouseWheelHandler = null;
    +  mouseWheelEvent = null;
    +  mouseWheelEventRtl = null;
    +}
    +
    +function tearDownPage() {
    +  // Create interactive demo.
    +  mouseWheelHandler = new goog.events.WheelHandler(document.body);
    +
    +  goog.events.listen(mouseWheelHandler,
    +      goog.events.WheelEvent.EventType.WHEEL,
    +      function(e) {
    +        log.innerHTML += goog.string.subs('<br />(deltaX, deltaY): (%s, %s)',
    +            e.deltaX, e.deltaY);
    +      });
    +}
    +
    +function testGetDomEventType() {
    +  // Defaults to legacy non-gecko event.
    +  assertEquals(LEGACY_TYPE, goog.events.WheelHandler.getDomEventType());
    +
    +  // Gecko start to support wheel with version 17.
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.version = 16;
    +  assertEquals(GECKO_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.version = 17;
    +  assertEquals(PREFERRED_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.GECKO = false;
    +
    +  // IE started with version 9.
    +  goog.userAgent.IE = true;
    +  goog.userAgent.version = 8;
    +  assertEquals(LEGACY_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.version = 9;
    +  assertEquals(PREFERRED_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.IE = false;
    +
    +  // Chrome started with version 31.
    +  goog.userAgent.product.CHROME = true;
    +  goog.userAgent.product.version = 30;
    +  assertEquals(LEGACY_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.product.version = 31;
    +  assertEquals(PREFERRED_TYPE, goog.events.WheelHandler.getDomEventType());
    +  goog.userAgent.product.CHROME = false;
    +}
    +
    +function testPreferredStyleWheel() {
    +  // Enable 'wheel'
    +  goog.userAgent.IE = true;
    +  goog.userAgent.version = 9;
    +  createHandlerAndListen();
    +
    +  handleEvent(createFakePreferredEvent(DeltaMode.PIXEL, 10, 20, 30));
    +  assertWheelEvent(DeltaMode.PIXEL, 10, 20, 30);
    +  assertPixelDeltas(1);
    +
    +  handleEvent(createFakePreferredEvent(DeltaMode.LINE, 10, 20, 30));
    +  assertWheelEvent(DeltaMode.LINE, 10, 20, 30);
    +  assertPixelDeltas(15);
    +
    +  handleEvent(createFakePreferredEvent(DeltaMode.PAGE, 10, 20, 30));
    +  assertWheelEvent(DeltaMode.PAGE, 10, 20, 30);
    +  assertPixelDeltas(30 * 15);
    +}
    +
    +function testLegacyStyleWheel() {
    +  // 'mousewheel' enabled by default
    +  createHandlerAndListen();
    +
    +  // Test one dimensional.
    +  handleEvent(createFakeLegacyEvent(10));
    +  assertWheelEvent(DeltaMode.PIXEL, 0, -10, 0);
    +  assertPixelDeltas(1);
    +
    +  // Test two dimensional.
    +  handleEvent(createFakeLegacyEvent(/* ignored */ 10, 20, 30));
    +  assertWheelEvent(DeltaMode.PIXEL, -20, -30, 0);
    +  assertPixelDeltas(1);
    +}
    +
    +function testLegacyGeckoStyleWheel() {
    +  goog.userAgent.GECKO = true;
    +  createHandlerAndListen();
    +
    +  // Test no axis.
    +  handleEvent(createFakeGeckoEvent(10));
    +  assertWheelEvent(DeltaMode.LINE, 0, 10, 0);
    +  assertPixelDeltas(15);
    +
    +  // Vertical axis.
    +  handleEvent(createFakeGeckoEvent(10, VERTICAL));
    +  assertWheelEvent(DeltaMode.LINE, 0, 10, 0);
    +  assertPixelDeltas(15);
    +
    +  // Horizontal axis.
    +  handleEvent(createFakeGeckoEvent(10, HORIZONTAL));
    +  assertWheelEvent(DeltaMode.LINE, 10, 0, 0);
    +  assertPixelDeltas(15);
    +}
    +
    +function testLegacyIeStyleWheel() {
    +  goog.userAgent.IE = true;
    +
    +  createHandlerAndListen();
    +
    +  // Non-gecko, non-webkit events get wheelDelta divided by -40 to get detail.
    +  handleEvent(createFakeLegacyEvent(120));
    +  assertWheelEvent(DeltaMode.PIXEL, 0, -120, 0);
    +
    +  handleEvent(createFakeLegacyEvent(-120));
    +  assertWheelEvent(DeltaMode.PIXEL, 0, 120, 0);
    +
    +  handleEvent(createFakeLegacyEvent(1200));
    +  assertWheelEvent(DeltaMode.PIXEL, 0, -1200, 0);
    +}
    +
    +function testNullBody() {
    +  goog.userAgent.IE = true;
    +  var documentObjectWithNoBody = { };
    +  goog.testing.events.mixinListenable(documentObjectWithNoBody);
    +  mouseWheelHandler =
    +      new goog.events.WheelHandler(documentObjectWithNoBody);
    +}
    +
    +// Be sure to call this after setting up goog.userAgent mock and not before.
    +function createHandlerAndListen() {
    +  mouseWheelHandler = new goog.events.WheelHandler(
    +      goog.dom.getElement('foo'));
    +
    +  goog.events.listen(mouseWheelHandler,
    +      goog.events.WheelEvent.EventType.WHEEL,
    +      function(e) { mouseWheelEvent = e; });
    +
    +  mouseWheelHandlerRtl = new goog.events.WheelHandler(
    +      goog.dom.getElement('fooRtl'));
    +
    +  goog.events.listen(mouseWheelHandlerRtl,
    +      goog.events.WheelEvent.EventType.WHEEL,
    +      function(e) { mouseWheelEventRtl = e; });
    +}
    +
    +function handleEvent(event) {
    +  mouseWheelHandler.handleEvent(event);
    +  mouseWheelHandlerRtl.handleEvent(event);
    +}
    +
    +function assertWheelEvent(deltaMode, deltaX, deltaY, deltaZ) {
    +  assertTrue('event should be non-null', !!mouseWheelEvent);
    +  assertTrue('event should have correct JS type',
    +      mouseWheelEvent instanceof goog.events.WheelEvent);
    +  assertEquals('event should have correct deltaMode property',
    +      deltaMode, mouseWheelEvent.deltaMode);
    +  assertEquals('event should have correct deltaX property',
    +      deltaX, mouseWheelEvent.deltaX);
    +  assertEquals('event should have correct deltaY property',
    +      deltaY, mouseWheelEvent.deltaY);
    +  assertEquals('event should have correct deltaZ property',
    +      deltaZ, mouseWheelEvent.deltaZ);
    +
    +  // RTL
    +  assertTrue('event should be non-null', !!mouseWheelEventRtl);
    +  assertTrue('event should have correct JS type',
    +      mouseWheelEventRtl instanceof goog.events.WheelEvent);
    +  assertEquals('event should have correct deltaMode property',
    +      deltaMode, mouseWheelEventRtl.deltaMode);
    +  assertEquals('event should have correct deltaX property',
    +      -deltaX, mouseWheelEventRtl.deltaX);
    +  assertEquals('event should have correct deltaY property',
    +      deltaY, mouseWheelEventRtl.deltaY);
    +  assertEquals('event should have correct deltaZ property',
    +      deltaZ, mouseWheelEventRtl.deltaZ);
    +
    +}
    +
    +function assertPixelDeltas(scale) {
    +  assertEquals(mouseWheelEvent.deltaX * scale, mouseWheelEvent.pixelDeltaX);
    +  assertEquals(mouseWheelEvent.deltaY * scale, mouseWheelEvent.pixelDeltaY);
    +  assertEquals(mouseWheelEvent.deltaZ * scale, mouseWheelEvent.pixelDeltaZ);
    +
    +  // RTL
    +  assertEquals(mouseWheelEventRtl.deltaX * scale,
    +      mouseWheelEventRtl.pixelDeltaX);
    +  assertEquals(mouseWheelEventRtl.deltaY * scale,
    +      mouseWheelEventRtl.pixelDeltaY);
    +  assertEquals(mouseWheelEventRtl.deltaZ * scale,
    +      mouseWheelEventRtl.pixelDeltaZ);
    +}
    +
    +function createFakePreferredEvent(
    +    opt_deltaMode, opt_deltaX, opt_deltaY, opt_deltaZ) {
    +  var event = {
    +    type: PREFERRED_TYPE,
    +    deltaMode: opt_deltaMode,
    +    deltaX: opt_deltaX,
    +    deltaY: opt_deltaY,
    +    deltaZ: opt_deltaZ
    +  };
    +  return new goog.events.BrowserEvent(event);
    +}
    +
    +
    +function createFakeLegacyEvent(
    +    opt_wheelDelta, opt_wheelDeltaX, opt_wheelDeltaY) {
    +  var event = {
    +    type: LEGACY_TYPE,
    +    wheelDelta: opt_wheelDelta,
    +    wheelDeltaX: opt_wheelDeltaX,
    +    wheelDeltaY: opt_wheelDeltaY
    +  };
    +  return new goog.events.BrowserEvent(event);
    +}
    +
    +function createFakeGeckoEvent(opt_detail, opt_axis) {
    +  var event = {
    +    type: GECKO_TYPE,
    +    detail: opt_detail,
    +    axis: opt_axis,
    +    HORIZONTAL_AXIS: HORIZONTAL,
    +    VERTICAL_AXIS: VERTICAL
    +  };
    +  return new goog.events.BrowserEvent(event);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/format/emailaddress.js b/mapbox-gl/closure-library/closure/goog/format/emailaddress.js
    new file mode 100644
    index 0000000000..670bc33c78
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/emailaddress.js
    @@ -0,0 +1,499 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides functions to parse and manipulate email addresses.
    + *
    + */
    +
    +goog.provide('goog.format.EmailAddress');
    +
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Formats an email address string for display, and allows for extraction of
    + * the individual components of the address.
    + * @param {string=} opt_address The email address.
    + * @param {string=} opt_name The name associated with the email address.
    + * @constructor
    + */
    +goog.format.EmailAddress = function(opt_address, opt_name) {
    +  /**
    +   * The name or personal string associated with the address.
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = opt_name || '';
    +
    +  /**
    +   * The email address.
    +   * @type {string}
    +   * @protected
    +   */
    +  this.address = opt_address || '';
    +};
    +
    +
    +/**
    + * Match string for opening tokens.
    + * @type {string}
    + * @private
    + */
    +goog.format.EmailAddress.OPENERS_ = '"<([';
    +
    +
    +/**
    + * Match string for closing tokens.
    + * @type {string}
    + * @private
    + */
    +goog.format.EmailAddress.CLOSERS_ = '">)]';
    +
    +
    +/**
    + * Match string for characters that require display names to be quoted and are
    + * not address separators.
    + * @type {string}
    + * @const
    + * @package
    + */
    +goog.format.EmailAddress.SPECIAL_CHARS = '()<>@:\\\".[]';
    +
    +
    +/**
    + * Match string for address separators.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.format.EmailAddress.ADDRESS_SEPARATORS_ = ',;';
    +
    +
    +/**
    + * Match string for characters that, when in a display name, require it to be
    + * quoted.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_ =
    +    goog.format.EmailAddress.SPECIAL_CHARS +
    +    goog.format.EmailAddress.ADDRESS_SEPARATORS_;
    +
    +
    +/**
    + * A RegExp to match all double quotes.  Used in cleanAddress().
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.EmailAddress.ALL_DOUBLE_QUOTES_ = /\"/g;
    +
    +
    +/**
    + * A RegExp to match escaped double quotes.  Used in parse().
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_ = /\\\"/g;
    +
    +
    +/**
    + * A RegExp to match all backslashes.  Used in cleanAddress().
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.EmailAddress.ALL_BACKSLASHES_ = /\\/g;
    +
    +
    +/**
    + * A RegExp to match escaped backslashes.  Used in parse().
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.EmailAddress.ESCAPED_BACKSLASHES_ = /\\\\/g;
    +
    +
    +/**
    + * A string representing the RegExp for the local part of an email address.
    + * @private {string}
    + */
    +goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ =
    +    '[+a-zA-Z0-9_.!#$%&\'*\\/=?^`{|}~-]+';
    +
    +
    +/**
    + * A string representing the RegExp for the domain part of an email address.
    + * @private {string}
    + */
    +goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ =
    +    '([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{2,63}';
    +
    +
    +/**
    + * A RegExp to match the local part of an email address.
    + * @private {!RegExp}
    + */
    +goog.format.EmailAddress.LOCAL_PART_ =
    +    new RegExp('^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '$');
    +
    +
    +/**
    + * A RegExp to match the domain part of an email address.
    + * @private {!RegExp}
    + */
    +goog.format.EmailAddress.DOMAIN_PART_ =
    +    new RegExp('^' + goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');
    +
    +
    +/**
    + * A RegExp to match an email address.
    + * @private {!RegExp}
    + */
    +goog.format.EmailAddress.EMAIL_ADDRESS_ =
    +    new RegExp('^' + goog.format.EmailAddress.LOCAL_PART_REGEXP_STR_ + '@' +
    +        goog.format.EmailAddress.DOMAIN_PART_REGEXP_STR_ + '$');
    +
    +
    +/**
    + * Get the name associated with the email address.
    + * @return {string} The name or personal portion of the address.
    + * @final
    + */
    +goog.format.EmailAddress.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * Get the email address.
    + * @return {string} The email address.
    + * @final
    + */
    +goog.format.EmailAddress.prototype.getAddress = function() {
    +  return this.address;
    +};
    +
    +
    +/**
    + * Set the name associated with the email address.
    + * @param {string} name The name to associate.
    + * @final
    + */
    +goog.format.EmailAddress.prototype.setName = function(name) {
    +  this.name_ = name;
    +};
    +
    +
    +/**
    + * Set the email address.
    + * @param {string} address The email address.
    + * @final
    + */
    +goog.format.EmailAddress.prototype.setAddress = function(address) {
    +  this.address = address;
    +};
    +
    +
    +/**
    + * Return the address in a standard format:
    + *  - remove extra spaces.
    + *  - Surround name with quotes if it contains special characters.
    + * @return {string} The cleaned address.
    + * @override
    + */
    +goog.format.EmailAddress.prototype.toString = function() {
    +  return this.toStringInternal(
    +      goog.format.EmailAddress.CHARS_REQUIRE_QUOTES_);
    +};
    +
    +
    +/**
    + * Check if a display name requires quoting.
    + * @param {string} name The display name
    + * @param {string} specialChars String that contains the characters that require
    + *  the display name to be quoted. This may change based in whereas we are
    + *  in EAI context or not.
    + * @return {boolean}
    + * @private
    + */
    +goog.format.EmailAddress.isQuoteNeeded_ = function(name, specialChars) {
    +  for (var i = 0; i < specialChars.length; i++) {
    +    var specialChar = specialChars[i];
    +    if (goog.string.contains(name, specialChar)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Return the address in a standard format:
    + *  - remove extra spaces.
    + *  - Surround name with quotes if it contains special characters.
    + * @param {string} specialChars String that contains the characters that require
    + *  the display name to be quoted.
    + * @return {string} The cleaned address.
    + * @protected
    + */
    +goog.format.EmailAddress.prototype.toStringInternal = function(specialChars) {
    +  var name = this.getName();
    +
    +  // We intentionally remove double quotes in the name because escaping
    +  // them to \" looks ugly.
    +  name = name.replace(goog.format.EmailAddress.ALL_DOUBLE_QUOTES_, '');
    +
    +  // If the name has special characters, we need to quote it and escape \'s.
    +  if (goog.format.EmailAddress.isQuoteNeeded_(name, specialChars)) {
    +    name = '"' +
    +        name.replace(goog.format.EmailAddress.ALL_BACKSLASHES_, '\\\\') + '"';
    +  }
    +
    +  if (name == '') {
    +    return this.address;
    +  }
    +  if (this.address == '') {
    +    return name;
    +  }
    +  return name + ' <' + this.address + '>';
    +};
    +
    +
    +/**
    + * Determines is the current object is a valid email address.
    + * @return {boolean} Whether the email address is valid.
    + */
    +goog.format.EmailAddress.prototype.isValid = function() {
    +  return goog.format.EmailAddress.isValidAddrSpec(this.address);
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid email address. Supports both
    + * simple email addresses (address specs) and addresses that contain display
    + * names.
    + * @param {string} str The email address to check.
    + * @return {boolean} Whether the provided string is a valid address.
    + */
    +goog.format.EmailAddress.isValidAddress = function(str) {
    +  return goog.format.EmailAddress.parse(str).isValid();
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid address spec (local@domain.com).
    + * @param {string} str The email address to check.
    + * @return {boolean} Whether the provided string is a valid address spec.
    + */
    +goog.format.EmailAddress.isValidAddrSpec = function(str) {
    +  // This is a fairly naive implementation, but it covers 99% of use cases.
    +  // For more details, see http://en.wikipedia.org/wiki/Email_address#Syntax
    +  return goog.format.EmailAddress.EMAIL_ADDRESS_.test(str);
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid local part (part before the '@') of
    + * an email address.
    + * @param {string} str The local part to check.
    + * @return {boolean} Whether the provided string is a valid local part.
    + */
    +goog.format.EmailAddress.isValidLocalPartSpec = function(str) {
    +  return goog.format.EmailAddress.LOCAL_PART_.test(str);
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid domain part (part after the '@') of
    + * an email address.
    + * @param {string} str The domain part to check.
    + * @return {boolean} Whether the provided string is a valid domain part.
    + */
    +goog.format.EmailAddress.isValidDomainPartSpec = function(str) {
    +  return goog.format.EmailAddress.DOMAIN_PART_.test(str);
    +};
    +
    +
    +/**
    + * Parses an email address of the form "name" &lt;address&gt; ("name" is
    + * optional) into an email address.
    + * @param {string} addr The address string.
    + * @param {function(new: goog.format.EmailAddress, string=,string=)} ctor
    + *     EmailAddress constructor to instantiate the output address.
    + * @return {!goog.format.EmailAddress} The parsed address.
    + * @protected
    + */
    +goog.format.EmailAddress.parseInternal = function(addr, ctor) {
    +  // TODO(ecattell): Strip bidi markers.
    +  var name = '';
    +  var address = '';
    +  for (var i = 0; i < addr.length;) {
    +    var token = goog.format.EmailAddress.getToken_(addr, i);
    +    if (token.charAt(0) == '<' && token.indexOf('>') != -1) {
    +      var end = token.indexOf('>');
    +      address = token.substring(1, end);
    +    } else if (address == '') {
    +      name += token;
    +    }
    +    i += token.length;
    +  }
    +
    +  // Check if it's a simple email address of the form "jlim@google.com".
    +  if (address == '' && name.indexOf('@') != -1) {
    +    address = name;
    +    name = '';
    +  }
    +
    +  name = goog.string.collapseWhitespace(name);
    +  name = goog.string.stripQuotes(name, '\'');
    +  name = goog.string.stripQuotes(name, '"');
    +  // Replace escaped quotes and slashes.
    +  name = name.replace(goog.format.EmailAddress.ESCAPED_DOUBLE_QUOTES_, '"');
    +  name = name.replace(goog.format.EmailAddress.ESCAPED_BACKSLASHES_, '\\');
    +  address = goog.string.collapseWhitespace(address);
    +  return new ctor(address, name);
    +};
    +
    +
    +/**
    + * Parses an email address of the form "name" &lt;address&gt; into
    + * an email address.
    + * @param {string} addr The address string.
    + * @return {!goog.format.EmailAddress} The parsed address.
    + */
    +goog.format.EmailAddress.parse = function(addr) {
    +  return goog.format.EmailAddress.parseInternal(
    +      addr, goog.format.EmailAddress);
    +};
    +
    +
    +/**
    + * Parse a string containing email addresses of the form
    + * "name" &lt;address&gt; into an array of email addresses.
    + * @param {string} str The address list.
    + * @param {function(string)} parser The parser to employ.
    + * @param {function(string):boolean} separatorChecker Accepts a character and
    + *    returns whether it should be considered an address separator.
    + * @return {!Array<!goog.format.EmailAddress>} The parsed emails.
    + * @protected
    + */
    +goog.format.EmailAddress.parseListInternal = function(
    +    str, parser, separatorChecker) {
    +  var result = [];
    +  var email = '';
    +  var token;
    +
    +  // Remove non-UNIX-style newlines that would otherwise cause getToken_ to
    +  // choke. Remove multiple consecutive whitespace characters for the same
    +  // reason.
    +  str = goog.string.collapseWhitespace(str);
    +
    +  for (var i = 0; i < str.length; ) {
    +    token = goog.format.EmailAddress.getToken_(str, i);
    +    if (separatorChecker(token) ||
    +        (token == ' ' && parser(email).isValid())) {
    +      if (!goog.string.isEmptyOrWhitespace(email)) {
    +        result.push(parser(email));
    +      }
    +      email = '';
    +      i++;
    +      continue;
    +    }
    +    email += token;
    +    i += token.length;
    +  }
    +
    +  // Add the final token.
    +  if (!goog.string.isEmptyOrWhitespace(email)) {
    +    result.push(parser(email));
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * Parses a string containing email addresses of the form
    + * "name" &lt;address&gt; into an array of email addresses.
    + * @param {string} str The address list.
    + * @return {!Array<!goog.format.EmailAddress>} The parsed emails.
    + */
    +goog.format.EmailAddress.parseList = function(str) {
    +  return goog.format.EmailAddress.parseListInternal(
    +      str, goog.format.EmailAddress.parse,
    +      goog.format.EmailAddress.isAddressSeparator);
    +};
    +
    +
    +/**
    + * Get the next token from a position in an address string.
    + * @param {string} str the string.
    + * @param {number} pos the position.
    + * @return {string} the token.
    + * @private
    + */
    +goog.format.EmailAddress.getToken_ = function(str, pos) {
    +  var ch = str.charAt(pos);
    +  var p = goog.format.EmailAddress.OPENERS_.indexOf(ch);
    +  if (p == -1) {
    +    return ch;
    +  }
    +  if (goog.format.EmailAddress.isEscapedDlQuote_(str, pos)) {
    +
    +    // If an opener is an escaped quote we do not treat it as a real opener
    +    // and keep accumulating the token.
    +    return ch;
    +  }
    +  var closerChar = goog.format.EmailAddress.CLOSERS_.charAt(p);
    +  var endPos = str.indexOf(closerChar, pos + 1);
    +
    +  // If the closer is a quote we go forward skipping escaped quotes until we
    +  // hit the real closing one.
    +  while (endPos >= 0 &&
    +         goog.format.EmailAddress.isEscapedDlQuote_(str, endPos)) {
    +    endPos = str.indexOf(closerChar, endPos + 1);
    +  }
    +  var token = (endPos >= 0) ? str.substring(pos, endPos + 1) : ch;
    +  return token;
    +};
    +
    +
    +/**
    + * Checks if the character in the current position is an escaped double quote
    + * ( \" ).
    + * @param {string} str the string.
    + * @param {number} pos the position.
    + * @return {boolean} true if the char is escaped double quote.
    + * @private
    + */
    +goog.format.EmailAddress.isEscapedDlQuote_ = function(str, pos) {
    +  if (str.charAt(pos) != '"') {
    +    return false;
    +  }
    +  var slashCount = 0;
    +  for (var idx = pos - 1; idx >= 0 && str.charAt(idx) == '\\'; idx--) {
    +    slashCount++;
    +  }
    +  return ((slashCount % 2) != 0);
    +};
    +
    +
    +/**
    + * @param {string} ch The character to test.
    + * @return {boolean} Whether the provided character is an address separator.
    + */
    +goog.format.EmailAddress.isAddressSeparator = function(ch) {
    +  return goog.string.contains(goog.format.EmailAddress.ADDRESS_SEPARATORS_, ch);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.html b/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.html
    new file mode 100644
    index 0000000000..1e60ff0689
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.format.EmailAddress
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.format.EmailAddressTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.js b/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.js
    new file mode 100644
    index 0000000000..004938e2ab
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/emailaddress_test.js
    @@ -0,0 +1,229 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.format.EmailAddressTest');
    +goog.setTestOnly('goog.format.EmailAddressTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.format.EmailAddress');
    +goog.require('goog.testing.jsunit');
    +
    +function testparseList() {
    +  assertParsedList('', [], 'Failed to parse empty stringy');
    +  assertParsedList(',,', [], 'Failed to parse string with commas only');
    +
    +  assertParsedList('<foo@gmail.com>', ['foo@gmail.com']);
    +
    +  assertParsedList('<foo@gmail.com>,', ['foo@gmail.com'],
    +      'Failed to parse 1 address with trailing comma');
    +
    +  assertParsedList('<foo@gmail.com>, ', ['foo@gmail.com'],
    +      'Failed to parse 1 address with trailing whitespace and comma');
    +
    +  assertParsedList(',<foo@gmail.com>', ['foo@gmail.com'],
    +      'Failed to parse 1 address with leading comma');
    +
    +  assertParsedList(' ,<foo@gmail.com>', ['foo@gmail.com'],
    +      'Failed to parse 1 address with leading whitespace and comma');
    +
    +  assertParsedList('<foo@gmail.com>, <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses');
    +
    +  assertParsedList('<foo@gmail.com>, <bar@gmail.com>,',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses and trailing comma');
    +
    +  assertParsedList('<foo@gmail.com>, <bar@gmail.com>, ',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses, trailing comma and whitespace');
    +
    +  assertParsedList(
    +      'John Doe <john@gmail.com>; Jane Doe <jane@gmail.com>, ' +
    +          '<jerry@gmail.com>',
    +      ['john@gmail.com', 'jane@gmail.com', 'jerry@gmail.com'],
    +      'Failed to parse addresses with semicolon separator');
    +}
    +
    +function testparseListOpenersAndClosers() {
    +  assertParsedList(
    +      'aaa@gmail.com, "bbb@gmail.com", <ccc@gmail.com>, ' +
    +          '(ddd@gmail.com), [eee@gmail.com]',
    +      ['aaa@gmail.com', '"bbb@gmail.com"', 'ccc@gmail.com',
    +        '(ddd@gmail.com)', '[eee@gmail.com]'],
    +      'Failed to handle all 5 opener/closer characters');
    +}
    +
    +function testparseListIdn() {
    +  var idnaddr = 'mailtest@\u4F8B\u3048.\u30C6\u30B9\u30C8';
    +  assertParsedList(idnaddr, [idnaddr]);
    +}
    +
    +function testparseListWithQuotedSpecialChars() {
    +  var res = assertParsedList(
    +      'a\\"b\\"c <d@e.f>,"g\\"h\\"i\\\\" <j@k.l>',
    +      ['d@e.f', 'j@k.l']);
    +  assertEquals('Wrong name 0', 'a"b"c', res[0].getName());
    +  assertEquals('Wrong name 1', 'g"h"i\\', res[1].getName());
    +}
    +
    +function testparseListWithCommaInLocalPart() {
    +  var res = assertParsedList(
    +      '"Doe, John" <doe.john@gmail.com>, <someone@gmail.com>',
    +      ['doe.john@gmail.com', 'someone@gmail.com']);
    +
    +  assertEquals('Doe, John', res[0].getName());
    +  assertEquals('', res[1].getName());
    +}
    +
    +function testparseListWithWhitespaceSeparatedEmails() {
    +  var res = assertParsedList(
    +      'a@b.com <c@d.com> e@f.com "G H" <g@h.com> i@j.com',
    +      ['a@b.com', 'c@d.com', 'e@f.com', 'g@h.com', 'i@j.com']);
    +  assertEquals('G H', res[3].getName());
    +}
    +
    +function testparseListSystemNewlines() {
    +  // These Windows newlines can be inserted in IE8, or copied-and-pasted from
    +  // bad data on a Mac, as seen in bug 11081852.
    +  assertParsedList('a@b.com\r\nc@d.com', ['a@b.com', 'c@d.com'],
    +      'Failed to parse Windows newlines');
    +  assertParsedList('a@b.com\nc@d.com', ['a@b.com', 'c@d.com'],
    +      'Failed to parse *nix newlines');
    +  assertParsedList('a@b.com\n\rc@d.com', ['a@b.com', 'c@d.com'],
    +      'Failed to parse obsolete newlines');
    +  assertParsedList('a@b.com\rc@d.com', ['a@b.com', 'c@d.com'],
    +      'Failed to parse pre-OS X Mac newlines');
    +}
    +
    +function testToString() {
    +  var f = function(str) {
    +    return goog.format.EmailAddress.parse(str).toString();
    +  };
    +
    +  // No modification.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('JOHN Doe <john@gmail.com>'));
    +
    +  // Extra spaces.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f(' JOHN  Doe  <john@gmail.com> '));
    +
    +  // No name.
    +  assertEquals('john@gmail.com', f('<john@gmail.com>'));
    +  assertEquals('john@gmail.com', f('john@gmail.com'));
    +
    +  // No address.
    +  assertEquals('JOHN Doe', f('JOHN Doe <>'));
    +
    +  // Special chars in the name.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, Doe <john@gmail.com>'));
    +  assertEquals('"JOHN(Johnny) Doe" <john@gmail.com>',
    +               f('JOHN(Johnny) Doe <john@gmail.com>'));
    +  assertEquals('"JOHN[Johnny] Doe" <john@gmail.com>',
    +               f('JOHN[Johnny] Doe <john@gmail.com>'));
    +  assertEquals('"JOHN@work Doe" <john@gmail.com>',
    +               f('JOHN@work Doe <john@gmail.com>'));
    +  assertEquals('"JOHN:theking Doe" <john@gmail.com>',
    +               f('JOHN:theking Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\\\\ Doe" <john@gmail.com>',
    +               f('JOHN\\ Doe <john@gmail.com>'));
    +  assertEquals('"JOHN.com Doe" <john@gmail.com>',
    +               f('JOHN.com Doe <john@gmail.com>'));
    +
    +  // Already quoted.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('"JOHN, Doe" <john@gmail.com>'));
    +
    +  // Needless quotes.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('"JOHN Doe" <john@gmail.com>'));
    +  // Not quoted-string, but has double quotes.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, "Doe" <john@gmail.com>'));
    +
    +  // No special characters other than quotes.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('JOHN "Doe" <john@gmail.com>'));
    +
    +  // Escaped quotes are also removed.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, \\"Doe\\" <john@gmail.com>'));
    +}
    +
    +function doIsValidTest(testFunc, valid, invalid) {
    +  goog.array.forEach(valid, function(str) {
    +    assertTrue('"' + str + '" should be valid.', testFunc(str));
    +  });
    +  goog.array.forEach(invalid, function(str) {
    +    assertFalse('"' + str + '" should be invalid.', testFunc(str));
    +  });
    +}
    +
    +function testIsValid() {
    +  var valid = [
    +    'e@b.eu', '<a.b+foo@c.com>', 'eric <e@b.com>', '"e" <e@b.com>',
    +    'a@FOO.MUSEUM', 'bla@b.co.ac.uk', 'bla@a.b.com', 'o\'hara@gm.com',
    +    'plus+is+allowed@gmail.com', '!/#$%&\'*+-=~|`{}?^_@expample.com',
    +    'confirm-bhk=modulo.org@yahoogroups.com'];
    +  var invalid = [
    +    'e', '', 'e @c.com', 'a@b', 'foo.com', 'foo@c..com', 'test@gma=il.com',
    +    'aaa@gmail', 'has some spaces@gmail.com', 'has@three@at@signs.com',
    +    '@no-local-part.com', 'み.ん-あ@みんあ.みんあ',
    +    'みんあ@test.com', 'test@test.みんあ', 'test@みんあ.com',
    +    'fullwidthfullstop@sld' + '\uff0e' + 'tld',
    +    'ideographicfullstop@sld' + '\u3002' + 'tld',
    +    'halfwidthideographicfullstop@sld' + '\uff61' + 'tld'];
    +  doIsValidTest(goog.format.EmailAddress.isValidAddress, valid, invalid);
    +}
    +
    +function testIsValidLocalPart() {
    +  var valid = [
    +    'e', 'a.b+foo', 'o\'hara', 'user+someone', '!/#$%&\'*+-=~|`{}?^_',
    +    'confirm-bhk=modulo.org'];
    +  var invalid = [
    +    'A@b@c', 'a"b(c)d,e:f;g<h>i[j\\k]l', 'just"not"right',
    +    'this is"not\\allowed', 'this\\ still\"not\\\\allowed', 'has some spaces'];
    +  doIsValidTest(goog.format.EmailAddress.isValidLocalPartSpec, valid, invalid);
    +}
    +
    +function testIsValidDomainPart() {
    +  var valid = [
    +    'example.com', 'dept.example.org', 'long.domain.with.lots.of.dots'];
    +  var invalid = ['', '@has.an.at.sign', '..has.leading.dots', 'gma=il.com',
    +    'DoesNotHaveADot', 'sld' + '\uff0e' + 'tld', 'sld' + '\u3002' + 'tld',
    +    'sld' + '\uff61' + 'tld'];
    +  doIsValidTest(goog.format.EmailAddress.isValidDomainPartSpec, valid, invalid);
    +}
    +
    +
    +/**
    + * Asserts that parsing the inputString produces a list of email addresses
    + * containing the specified address strings, irrespective of their order.
    + * @param {string} inputString A raw address list.
    + * @param {Array<string>} expectedList The expected results.
    + * @param {string=} opt_message An assertion message.
    + * @return {string} the resulting email address objects.
    + */
    +function assertParsedList(inputString, expectedList, opt_message) {
    +  var message = opt_message || 'Should parse address correctly';
    +  var result = goog.format.EmailAddress.parseList(inputString);
    +  assertEquals(
    +      'Should have correct # of addresses', expectedList.length, result.length);
    +  for (var i = 0; i < expectedList.length; ++i) {
    +    assertEquals(message, expectedList[i], result[i].getAddress());
    +  }
    +  return result;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/format/format.js b/mapbox-gl/closure-library/closure/goog/format/format.js
    new file mode 100644
    index 0000000000..f78067d665
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/format.js
    @@ -0,0 +1,502 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides utility functions for formatting strings, numbers etc.
    + *
    + */
    +
    +goog.provide('goog.format');
    +
    +goog.require('goog.i18n.GraphemeBreak');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Formats a number of bytes in human readable form.
    + * 54, 450K, 1.3M, 5G etc.
    + * @param {number} bytes The number of bytes to show.
    + * @param {number=} opt_decimals The number of decimals to use.  Defaults to 2.
    + * @return {string} The human readable form of the byte size.
    + */
    +goog.format.fileSize = function(bytes, opt_decimals) {
    +  return goog.format.numBytesToString(bytes, opt_decimals, false);
    +};
    +
    +
    +/**
    + * Checks whether string value containing scaling units (K, M, G, T, P, m,
    + * u, n) can be converted to a number.
    + *
    + * Where there is a decimal, there must be a digit to the left of the
    + * decimal point.
    + *
    + * Negative numbers are valid.
    + *
    + * Examples:
    + *   0, 1, 1.0, 10.4K, 2.3M, -0.3P, 1.2m
    + *
    + * @param {string} val String value to check.
    + * @return {boolean} True if string could be converted to a numeric value.
    + */
    +goog.format.isConvertableScaledNumber = function(val) {
    +  return goog.format.SCALED_NUMERIC_RE_.test(val);
    +};
    +
    +
    +/**
    + * Converts a string to numeric value, taking into account the units.
    + * If string ends in 'B', use binary conversion.
    + * @param {string} stringValue String to be converted to numeric value.
    + * @return {number} Numeric value for string.
    + */
    +goog.format.stringToNumericValue = function(stringValue) {
    +  if (goog.string.endsWith(stringValue, 'B')) {
    +    return goog.format.stringToNumericValue_(
    +        stringValue, goog.format.NUMERIC_SCALES_BINARY_);
    +  }
    +  return goog.format.stringToNumericValue_(
    +      stringValue, goog.format.NUMERIC_SCALES_SI_);
    +};
    +
    +
    +/**
    + * Converts a string to number of bytes, taking into account the units.
    + * Binary conversion.
    + * @param {string} stringValue String to be converted to numeric value.
    + * @return {number} Numeric value for string.
    + */
    +goog.format.stringToNumBytes = function(stringValue) {
    +  return goog.format.stringToNumericValue_(
    +      stringValue, goog.format.NUMERIC_SCALES_BINARY_);
    +};
    +
    +
    +/**
    + * Converts a numeric value to string representation. SI conversion.
    + * @param {number} val Value to be converted.
    + * @param {number=} opt_decimals The number of decimals to use.  Defaults to 2.
    + * @return {string} String representation of number.
    + */
    +goog.format.numericValueToString = function(val, opt_decimals) {
    +  return goog.format.numericValueToString_(
    +      val, goog.format.NUMERIC_SCALES_SI_, opt_decimals);
    +};
    +
    +
    +/**
    + * Converts number of bytes to string representation. Binary conversion.
    + * Default is to return the additional 'B' suffix, e.g. '10.5KB' to minimize
    + * confusion with counts that are scaled by powers of 1000.
    + * @param {number} val Value to be converted.
    + * @param {number=} opt_decimals The number of decimals to use.  Defaults to 2.
    + * @param {boolean=} opt_suffix If true, include trailing 'B' in returned
    + *     string.  Default is true.
    + * @param {boolean=} opt_useSeparator If true, number and scale will be
    + *     separated by a no break space. Default is false.
    + * @return {string} String representation of number of bytes.
    + */
    +goog.format.numBytesToString = function(val, opt_decimals, opt_suffix,
    +    opt_useSeparator) {
    +  var suffix = '';
    +  if (!goog.isDef(opt_suffix) || opt_suffix) {
    +    suffix = 'B';
    +  }
    +  return goog.format.numericValueToString_(
    +      val, goog.format.NUMERIC_SCALES_BINARY_, opt_decimals, suffix,
    +      opt_useSeparator);
    +};
    +
    +
    +/**
    + * Converts a string to numeric value, taking into account the units.
    + * @param {string} stringValue String to be converted to numeric value.
    + * @param {Object} conversion Dictionary of conversion scales.
    + * @return {number} Numeric value for string.  If it cannot be converted,
    + *    returns NaN.
    + * @private
    + */
    +goog.format.stringToNumericValue_ = function(stringValue, conversion) {
    +  var match = stringValue.match(goog.format.SCALED_NUMERIC_RE_);
    +  if (!match) {
    +    return NaN;
    +  }
    +  var val = match[1] * conversion[match[2]];
    +  return val;
    +};
    +
    +
    +/**
    + * Converts a numeric value to string, using specified conversion
    + * scales.
    + * @param {number} val Value to be converted.
    + * @param {Object} conversion Dictionary of scaling factors.
    + * @param {number=} opt_decimals The number of decimals to use.  Default is 2.
    + * @param {string=} opt_suffix Optional suffix to append.
    + * @param {boolean=} opt_useSeparator If true, number and scale will be
    + *     separated by a space. Default is false.
    + * @return {string} The human readable form of the byte size.
    + * @private
    + */
    +goog.format.numericValueToString_ = function(val, conversion,
    +    opt_decimals, opt_suffix, opt_useSeparator) {
    +  var prefixes = goog.format.NUMERIC_SCALE_PREFIXES_;
    +  var orig_val = val;
    +  var symbol = '';
    +  var separator = '';
    +  var scale = 1;
    +  if (val < 0) {
    +    val = -val;
    +  }
    +  for (var i = 0; i < prefixes.length; i++) {
    +    var unit = prefixes[i];
    +    scale = conversion[unit];
    +    if (val >= scale || (scale <= 1 && val > 0.1 * scale)) {
    +      // Treat values less than 1 differently, allowing 0.5 to be "0.5" rather
    +      // than "500m"
    +      symbol = unit;
    +      break;
    +    }
    +  }
    +  if (!symbol) {
    +    scale = 1;
    +  } else {
    +    if (opt_suffix) {
    +      symbol += opt_suffix;
    +    }
    +    if (opt_useSeparator) {
    +      separator = ' ';
    +    }
    +  }
    +  var ex = Math.pow(10, goog.isDef(opt_decimals) ? opt_decimals : 2);
    +  return Math.round(orig_val / scale * ex) / ex + separator + symbol;
    +};
    +
    +
    +/**
    + * Regular expression for detecting scaling units, such as K, M, G, etc. for
    + * converting a string representation to a numeric value.
    + *
    + * Also allow 'k' to be aliased to 'K'.  These could be used for SI (powers
    + * of 1000) or Binary (powers of 1024) conversions.
    + *
    + * Also allow final 'B' to be interpreted as byte-count, implicitly triggering
    + * binary conversion (e.g., '10.2MB').
    + *
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.SCALED_NUMERIC_RE_ = /^([-]?\d+\.?\d*)([K,M,G,T,P,k,m,u,n]?)[B]?$/;
    +
    +
    +/**
    + * Ordered list of scaling prefixes in decreasing order.
    + * @private {Array<string>}
    + */
    +goog.format.NUMERIC_SCALE_PREFIXES_ = [
    +  'P', 'T', 'G', 'M', 'K', '', 'm', 'u', 'n'
    +];
    +
    +
    +/**
    + * Scaling factors for conversion of numeric value to string.  SI conversion.
    + * @type {Object}
    + * @private
    + */
    +goog.format.NUMERIC_SCALES_SI_ = {
    +  '': 1,
    +  'n': 1e-9,
    +  'u': 1e-6,
    +  'm': 1e-3,
    +  'k': 1e3,
    +  'K': 1e3,
    +  'M': 1e6,
    +  'G': 1e9,
    +  'T': 1e12,
    +  'P': 1e15
    +};
    +
    +
    +/**
    + * Scaling factors for conversion of numeric value to string.  Binary
    + * conversion.
    + * @type {Object}
    + * @private
    + */
    +goog.format.NUMERIC_SCALES_BINARY_ = {
    +  '': 1,
    +  'n': Math.pow(1024, -3),
    +  'u': Math.pow(1024, -2),
    +  'm': 1.0 / 1024,
    +  'k': 1024,
    +  'K': 1024,
    +  'M': Math.pow(1024, 2),
    +  'G': Math.pow(1024, 3),
    +  'T': Math.pow(1024, 4),
    +  'P': Math.pow(1024, 5)
    +};
    +
    +
    +/**
    + * First Unicode code point that has the Mark property.
    + * @type {number}
    + * @private
    + */
    +goog.format.FIRST_GRAPHEME_EXTEND_ = 0x300;
    +
    +
    +/**
    + * Returns true if and only if given character should be treated as a breaking
    + * space. All ASCII control characters, the main Unicode range of spacing
    + * characters (U+2000 to U+200B inclusive except for U+2007), and several other
    + * Unicode space characters are treated as breaking spaces.
    + * @param {number} charCode The character code under consideration.
    + * @return {boolean} True if the character is a breaking space.
    + * @private
    + */
    +goog.format.isTreatedAsBreakingSpace_ = function(charCode) {
    +  return (charCode <= goog.format.WbrToken_.SPACE) ||
    +         (charCode >= 0x1000 &&
    +          ((charCode >= 0x2000 && charCode <= 0x2006) ||
    +           (charCode >= 0x2008 && charCode <= 0x200B) ||
    +           charCode == 0x1680 ||
    +           charCode == 0x180E ||
    +           charCode == 0x2028 ||
    +           charCode == 0x2029 ||
    +           charCode == 0x205f ||
    +           charCode == 0x3000));
    +};
    +
    +
    +/**
    + * Returns true if and only if given character is an invisible formatting
    + * character.
    + * @param {number} charCode The character code under consideration.
    + * @return {boolean} True if the character is an invisible formatting character.
    + * @private
    + */
    +goog.format.isInvisibleFormattingCharacter_ = function(charCode) {
    +  // See: http://unicode.org/charts/PDF/U2000.pdf
    +  return (charCode >= 0x200C && charCode <= 0x200F) ||
    +         (charCode >= 0x202A && charCode <= 0x202E);
    +};
    +
    +
    +/**
    + * Inserts word breaks into an HTML string at a given interval.  The counter is
    + * reset if a space or a character which behaves like a space is encountered,
    + * but it isn't incremented if an invisible formatting character is encountered.
    + * WBRs aren't inserted into HTML tags or entities.  Entities count towards the
    + * character count, HTML tags do not.
    + *
    + * With common strings aliased, objects allocations are constant based on the
    + * length of the string: N + 3. This guarantee does not hold if the string
    + * contains an element >= U+0300 and hasGraphemeBreak is non-trivial.
    + *
    + * @param {string} str HTML to insert word breaks into.
    + * @param {function(number, number, boolean): boolean} hasGraphemeBreak A
    + *     function determining if there is a grapheme break between two characters,
    + *     in the same signature as goog.i18n.GraphemeBreak.hasGraphemeBreak.
    + * @param {number=} opt_maxlen Maximum length after which to ensure
    + *     there is a break.  Default is 10 characters.
    + * @return {string} The string including word breaks.
    + * @private
    + */
    +goog.format.insertWordBreaksGeneric_ = function(str, hasGraphemeBreak,
    +    opt_maxlen) {
    +  var maxlen = opt_maxlen || 10;
    +  if (maxlen > str.length) return str;
    +
    +  var rv = [];
    +  var n = 0; // The length of the current token
    +
    +  // This will contain the ampersand or less-than character if one of the
    +  // two has been seen; otherwise, the value is zero.
    +  var nestingCharCode = 0;
    +
    +  // First character position from input string that has not been outputted.
    +  var lastDumpPosition = 0;
    +
    +  var charCode = 0;
    +  for (var i = 0; i < str.length; i++) {
    +    // Using charCodeAt versus charAt avoids allocating new string objects.
    +    var lastCharCode = charCode;
    +    charCode = str.charCodeAt(i);
    +
    +    // Don't add a WBR before characters that might be grapheme extending.
    +    var isPotentiallyGraphemeExtending =
    +        charCode >= goog.format.FIRST_GRAPHEME_EXTEND_ &&
    +        !hasGraphemeBreak(lastCharCode, charCode, true);
    +
    +    // Don't add a WBR at the end of a word. For the purposes of determining
    +    // work breaks, all ASCII control characters and some commonly encountered
    +    // Unicode spacing characters are treated as breaking spaces.
    +    if (n >= maxlen &&
    +        !goog.format.isTreatedAsBreakingSpace_(charCode) &&
    +        !isPotentiallyGraphemeExtending) {
    +      // Flush everything seen so far, and append a word break.
    +      rv.push(str.substring(lastDumpPosition, i), goog.format.WORD_BREAK_HTML);
    +      lastDumpPosition = i;
    +      n = 0;
    +    }
    +
    +    if (!nestingCharCode) {
    +      // Not currently within an HTML tag or entity
    +
    +      if (charCode == goog.format.WbrToken_.LT ||
    +          charCode == goog.format.WbrToken_.AMP) {
    +
    +        // Entering an HTML Entity '&' or open tag '<'
    +        nestingCharCode = charCode;
    +      } else if (goog.format.isTreatedAsBreakingSpace_(charCode)) {
    +
    +        // A space or control character -- reset the token length
    +        n = 0;
    +      } else if (!goog.format.isInvisibleFormattingCharacter_(charCode)) {
    +
    +        // A normal flow character - increment.  For grapheme extending
    +        // characters, this is not *technically* a new character.  However,
    +        // since the grapheme break detector might be overly conservative,
    +        // we have to continue incrementing, or else we won't even be able
    +        // to add breaks when we get to things like punctuation.  For the
    +        // case where we have a full grapheme break detector, it is okay if
    +        // we occasionally break slightly early.
    +        n++;
    +      }
    +    } else if (charCode == goog.format.WbrToken_.GT &&
    +        nestingCharCode == goog.format.WbrToken_.LT) {
    +
    +      // Leaving an HTML tag, treat the tag as zero-length
    +      nestingCharCode = 0;
    +    } else if (charCode == goog.format.WbrToken_.SEMI_COLON &&
    +        nestingCharCode == goog.format.WbrToken_.AMP) {
    +
    +      // Leaving an HTML entity, treat it as length one
    +      nestingCharCode = 0;
    +      n++;
    +    }
    +  }
    +
    +  // Take care of anything we haven't flushed so far.
    +  rv.push(str.substr(lastDumpPosition));
    +
    +  return rv.join('');
    +};
    +
    +
    +/**
    + * Inserts word breaks into an HTML string at a given interval.
    + *
    + * This method is as aggressive as possible, using a full table of Unicode
    + * characters where it is legal to insert word breaks; however, this table
    + * comes at a 2.5k pre-gzip (~1k post-gzip) size cost.  Consider using
    + * insertWordBreaksBasic to minimize the size impact.
    + *
    + * @param {string} str HTML to insert word breaks into.
    + * @param {number=} opt_maxlen Maximum length after which to ensure there is a
    + *     break.  Default is 10 characters.
    + * @return {string} The string including word breaks.
    + */
    +goog.format.insertWordBreaks = function(str, opt_maxlen) {
    +  return goog.format.insertWordBreaksGeneric_(str,
    +      goog.i18n.GraphemeBreak.hasGraphemeBreak, opt_maxlen);
    +};
    +
    +
    +/**
    + * Determines conservatively if a character has a Grapheme break.
    + *
    + * Conforms to a similar signature as goog.i18n.GraphemeBreak, but is overly
    + * conservative, returning true only for characters in common scripts that
    + * are simple to account for.
    + *
    + * @param {number} lastCharCode The previous character code.  Ignored.
    + * @param {number} charCode The character code under consideration.  It must be
    + *     at least \u0300 as a precondition -- this case is covered by
    + *     insertWordBreaksGeneric_.
    + * @param {boolean=} opt_extended Ignored, to conform with the interface.
    + * @return {boolean} Whether it is one of the recognized subsets of characters
    + *     with a grapheme break.
    + * @private
    + */
    +goog.format.conservativelyHasGraphemeBreak_ = function(
    +    lastCharCode, charCode, opt_extended) {
    +  // Return false for everything except the most common Cyrillic characters.
    +  // Don't worry about Latin characters, because insertWordBreaksGeneric_
    +  // itself already handles those.
    +  // TODO(gboyer): Also account for Greek, Armenian, and Georgian if it is
    +  // simple to do so.
    +  return charCode >= 0x400 && charCode < 0x523;
    +};
    +
    +
    +// TODO(gboyer): Consider using a compile-time flag to switch implementations
    +// rather than relying on the developers to toggle implementations.
    +/**
    + * Inserts word breaks into an HTML string at a given interval.
    + *
    + * This method is less aggressive than insertWordBreaks, only inserting
    + * breaks next to punctuation and between Latin or Cyrillic characters.
    + * However, this is good enough for the common case of URLs.  It also
    + * works for all Latin and Cyrillic languages, plus CJK has no need for word
    + * breaks.  When this method is used, goog.i18n.GraphemeBreak may be dead
    + * code eliminated.
    + *
    + * @param {string} str HTML to insert word breaks into.
    + * @param {number=} opt_maxlen Maximum length after which to ensure there is a
    + *     break.  Default is 10 characters.
    + * @return {string} The string including word breaks.
    + */
    +goog.format.insertWordBreaksBasic = function(str, opt_maxlen) {
    +  return goog.format.insertWordBreaksGeneric_(str,
    +      goog.format.conservativelyHasGraphemeBreak_, opt_maxlen);
    +};
    +
    +
    +/**
    + * True iff the current userAgent is IE8 or above.
    + * @type {boolean}
    + * @private
    + */
    +goog.format.IS_IE8_OR_ABOVE_ = goog.userAgent.IE &&
    +    goog.userAgent.isVersionOrHigher(8);
    +
    +
    +/**
    + * Constant for the WBR replacement used by insertWordBreaks.  Safari requires
    + * <wbr></wbr>, Opera needs the &shy; entity, though this will give a visible
    + * hyphen at breaks.  IE8 uses a zero width space.
    + * Other browsers just use <wbr>.
    + * @type {string}
    + */
    +goog.format.WORD_BREAK_HTML =
    +    goog.userAgent.WEBKIT ?
    +        '<wbr></wbr>' : goog.userAgent.OPERA ?
    +            '&shy;' : goog.format.IS_IE8_OR_ABOVE_ ?
    +                '&#8203;' : '<wbr>';
    +
    +
    +/**
    + * Tokens used within insertWordBreaks.
    + * @private
    + * @enum {number}
    + */
    +goog.format.WbrToken_ = {
    +  LT: 60, // '<'.charCodeAt(0)
    +  GT: 62, // '>'.charCodeAt(0)
    +  AMP: 38, // '&'.charCodeAt(0)
    +  SEMI_COLON: 59, // ';'.charCodeAt(0)
    +  SPACE: 32 // ' '.charCodeAt(0)
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/format/format_test.html b/mapbox-gl/closure-library/closure/goog/format/format_test.html
    new file mode 100644
    index 0000000000..0e639dafc2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/format_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.format
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.formatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/format/format_test.js b/mapbox-gl/closure-library/closure/goog/format/format_test.js
    new file mode 100644
    index 0000000000..161e680e1b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/format_test.js
    @@ -0,0 +1,303 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.formatTest');
    +goog.setTestOnly('goog.formatTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.format');
    +goog.require('goog.string');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  // set wordBreakHtml back to the original value (some tests edit this member).
    +  propertyReplacer.reset();
    +}
    +
    +function testFormatFileSize() {
    +  var fileSize = goog.format.fileSize;
    +
    +  assertEquals('45', fileSize(45));
    +  assertEquals('45', fileSize(45, 0));
    +  assertEquals('45', fileSize(45, 1));
    +  assertEquals('45', fileSize(45, 3));
    +  assertEquals('454', fileSize(454));
    +  assertEquals('600', fileSize(600));
    +
    +  assertEquals('1K', fileSize(1024));
    +  assertEquals('2K', fileSize(2 * 1024));
    +  assertEquals('5K', fileSize(5 * 1024));
    +  assertEquals('5.123K', fileSize(5.12345 * 1024, 3));
    +  assertEquals('5.68K', fileSize(5.678 * 1024, 2));
    +
    +  assertEquals('1M', fileSize(1024 * 1024));
    +  assertEquals('1.5M', fileSize(1.5 * 1024 * 1024));
    +  assertEquals('2M', fileSize(1.5 * 1024 * 1024, 0));
    +  assertEquals('1.5M', fileSize(1.51 * 1024 * 1024, 1));
    +  assertEquals('1.56M', fileSize(1.56 * 1024 * 1024, 2));
    +
    +  assertEquals('1G', fileSize(1024 * 1024 * 1024));
    +  assertEquals('6G', fileSize(6 * 1024 * 1024 * 1024));
    +  assertEquals('12.06T', fileSize(12345.6789 * 1024 * 1024 * 1024));
    +}
    +
    +function testIsConvertableScaledNumber() {
    +  var isConvertableScaledNumber = goog.format.isConvertableScaledNumber;
    +
    +  assertTrue(isConvertableScaledNumber('0'));
    +  assertTrue(isConvertableScaledNumber('45'));
    +  assertTrue(isConvertableScaledNumber('45K'));
    +  assertTrue(isConvertableScaledNumber('45MB'));
    +  assertTrue(isConvertableScaledNumber('45GB'));
    +  assertTrue(isConvertableScaledNumber('45T'));
    +  assertTrue(isConvertableScaledNumber('2.33P'));
    +  assertTrue(isConvertableScaledNumber('45m'));
    +  assertTrue(isConvertableScaledNumber('45u'));
    +  assertTrue(isConvertableScaledNumber('-5.0n'));
    +
    +  assertFalse(isConvertableScaledNumber('45x'));
    +  assertFalse(isConvertableScaledNumber('ux'));
    +  assertFalse(isConvertableScaledNumber('K'));
    +}
    +
    +function testNumericValueToString() {
    +  var numericValueToString = goog.format.numericValueToString;
    +
    +  assertEquals('0', numericValueToString(0.0));
    +  assertEquals('45', numericValueToString(45));
    +  assertEquals('454', numericValueToString(454));
    +  assertEquals('600', numericValueToString(600));
    +
    +  assertEquals('1.02K', numericValueToString(1024));
    +  assertEquals('2.05K', numericValueToString(2 * 1024));
    +  assertEquals('5.12K', numericValueToString(5 * 1024));
    +  assertEquals('5.246K', numericValueToString(5.12345 * 1024, 3));
    +  assertEquals('5.81K', numericValueToString(5.678 * 1024, 2));
    +
    +  assertEquals('1.05M', numericValueToString(1024 * 1024));
    +  assertEquals('1.57M', numericValueToString(1.5 * 1024 * 1024));
    +  assertEquals('2M', numericValueToString(1.5 * 1024 * 1024, 0));
    +  assertEquals('1.6M', numericValueToString(1.51 * 1024 * 1024, 1));
    +  assertEquals('1.64M', numericValueToString(1.56 * 1024 * 1024, 2));
    +
    +  assertEquals('1.07G', numericValueToString(1024 * 1024 * 1024));
    +  assertEquals('6.44G', numericValueToString(6 * 1024 * 1024 * 1024));
    +  assertEquals('13.26T', numericValueToString(12345.6789 * 1024 * 1024 * 1024));
    +
    +  assertEquals('23.4m', numericValueToString(0.0234));
    +  assertEquals('1.23u', numericValueToString(0.00000123));
    +  assertEquals('15.78n', numericValueToString(0.000000015784));
    +  assertEquals('0.58u', numericValueToString(0.0000005784));
    +  assertEquals('0.5', numericValueToString(0.5));
    +
    +  assertEquals('-45', numericValueToString(-45.3, 0));
    +  assertEquals('-45', numericValueToString(-45.5, 0));
    +  assertEquals('-46', numericValueToString(-45.51, 0));
    +}
    +
    +function testFormatNumBytes() {
    +  var numBytesToString = goog.format.numBytesToString;
    +
    +  assertEquals('45', numBytesToString(45));
    +  assertEquals('454', numBytesToString(454));
    +
    +  assertEquals('5KB', numBytesToString(5 * 1024));
    +  assertEquals('1MB', numBytesToString(1024 * 1024));
    +  assertEquals('6GB', numBytesToString(6 * 1024 * 1024 * 1024));
    +  assertEquals('12.06TB', numBytesToString(12345.6789 * 1024 * 1024 * 1024));
    +
    +  assertEquals('454', numBytesToString(454, 2, true, true));
    +  assertEquals('5 KB', numBytesToString(5 * 1024, 2, true, true));
    +}
    +
    +function testStringToNumeric() {
    +  var stringToNumericValue = goog.format.stringToNumericValue;
    +  var epsilon = Math.pow(10, -10);
    +
    +  assertNaN(stringToNumericValue('foo'));
    +
    +  assertEquals(45, stringToNumericValue('45'));
    +  assertEquals(-45, stringToNumericValue('-45'));
    +  assertEquals(-45, stringToNumericValue('-45'));
    +  assertEquals(454, stringToNumericValue('454'));
    +
    +  assertEquals(5 * 1024, stringToNumericValue('5KB'));
    +  assertEquals(1024 * 1024, stringToNumericValue('1MB'));
    +  assertEquals(6 * 1024 * 1024 * 1024, stringToNumericValue('6GB'));
    +  assertEquals(13260110230978.56, stringToNumericValue('12.06TB'));
    +
    +  assertEquals(5010, stringToNumericValue('5.01K'));
    +  assertEquals(5100000, stringToNumericValue('5.1M'));
    +  assertTrue(Math.abs(0.051 - stringToNumericValue('51.0m')) < epsilon);
    +  assertTrue(Math.abs(0.000051 - stringToNumericValue('51.0u')) < epsilon);
    +}
    +
    +function testStringToNumBytes() {
    +  var stringToNumBytes = goog.format.stringToNumBytes;
    +
    +  assertEquals(45, stringToNumBytes('45'));
    +  assertEquals(454, stringToNumBytes('454'));
    +
    +  assertEquals(5 * 1024, stringToNumBytes('5K'));
    +  assertEquals(1024 * 1024, stringToNumBytes('1M'));
    +  assertEquals(6 * 1024 * 1024 * 1024, stringToNumBytes('6G'));
    +  assertEquals(13260110230978.56, stringToNumBytes('12.06T'));
    +}
    +
    +function testInsertWordBreaks() {
    +  // HTML that gets inserted is browser dependent, ensure for the test it is
    +  // a constant - browser dependent HTML is for display purposes only.
    +  propertyReplacer.set(goog.format, 'WORD_BREAK_HTML', '<wbr>');
    +
    +  var insertWordBreaks = goog.format.insertWordBreaks;
    +
    +  assertEquals('abcdef', insertWordBreaks('abcdef', 10));
    +  assertEquals('ab<wbr>cd<wbr>ef', insertWordBreaks('abcdef', 2));
    +  assertEquals(
    +      'a<wbr>b<wbr>c<wbr>d<wbr>e<wbr>f', insertWordBreaks('abcdef', 1));
    +
    +  assertEquals(
    +      'a&amp;b=<wbr>=fal<wbr>se', insertWordBreaks('a&amp;b==false', 4));
    +  assertEquals('&lt;&amp;&gt;&raquo;<wbr>&laquo;',
    +               insertWordBreaks('&lt;&amp;&gt;&raquo;&laquo;', 4));
    +
    +  assertEquals('a<wbr>b<wbr>c d<wbr>e<wbr>f', insertWordBreaks('abc def', 1));
    +  assertEquals('ab<wbr>c de<wbr>f', insertWordBreaks('abc def', 2));
    +  assertEquals('abc def', insertWordBreaks('abc def', 3));
    +  assertEquals('abc def', insertWordBreaks('abc def', 4));
    +
    +  assertEquals('a<b>cd</b>e<wbr>f', insertWordBreaks('a<b>cd</b>ef', 4));
    +  assertEquals('Thi<wbr>s is a <a href="">lin<wbr>k</a>.',
    +               insertWordBreaks('This is a <a href="">link</a>.', 3));
    +  assertEquals('<abc a="&amp;&amp;&amp;&amp;&amp;">a<wbr>b',
    +      insertWordBreaks('<abc a="&amp;&amp;&amp;&amp;&amp;">ab', 1));
    +
    +  assertEquals('ab\u0300<wbr>cd', insertWordBreaks('ab\u0300cd', 2));
    +  assertEquals('ab\u036F<wbr>cd', insertWordBreaks('ab\u036Fcd', 2));
    +  assertEquals('ab<wbr>\u0370c<wbr>d', insertWordBreaks('ab\u0370cd', 2));
    +  assertEquals('ab<wbr>\uFE1Fc<wbr>d', insertWordBreaks('ab\uFE1Fcd', 2));
    +  assertEquals('ab\u0300<wbr>c\u0301<wbr>de<wbr>f',
    +      insertWordBreaks('ab\u0300c\u0301def', 2));
    +}
    +
    +function testInsertWordBreaksWithFormattingCharacters() {
    +  // HTML that gets inserted is browser dependent, ensure for the test it is
    +  // a constant - browser dependent HTML is for display purposes only.
    +  propertyReplacer.set(goog.format, 'WORD_BREAK_HTML', '<wbr>');
    +  var insertWordBreaks = goog.format.insertWordBreaks;
    +
    +  // A date in Arabic-Indic digits with Right-to-Left Marks (U+200F).
    +  // The date is "11<RLM>/01<RLM>/2012".
    +  var textWithRLMs = 'This is a date - ' +
    +      '\u0661\u0661\u200f/\u0660\u0661\u200f/\u0662\u0660\u0661\u0662';
    +  // A string of 10 Xs with invisible formatting characters in between.
    +  // These characters are in the ranges U+200C to U+200F and U+202A to
    +  // U+202E, inclusive. See: http://unicode.org/charts/PDF/U2000.pdf
    +  var stringWithInvisibleFormatting = 'X\u200cX\u200dX\u200eX\u200fX\u202a' +
    +      'X\u202bX\u202cX\u202dX\u202eX';
    +  // A string formed by concatenating copies of the previous string alternating
    +  // with characters which behave like breaking spaces. Besides the space
    +  // character itself, the other characters are in the range U+2000 to U+200B
    +  // inclusive, except for the exclusion of U+2007 and inclusion of U+2029.
    +  // See: http://unicode.org/charts/PDF/U2000.pdf
    +  var stringWithInvisibleFormattingAndSpacelikeCharacters =
    +      stringWithInvisibleFormatting + ' ' +
    +      stringWithInvisibleFormatting + '\u2000' +
    +      stringWithInvisibleFormatting + '\u2001' +
    +      stringWithInvisibleFormatting + '\u2002' +
    +      stringWithInvisibleFormatting + '\u2003' +
    +      stringWithInvisibleFormatting + '\u2005' +
    +      stringWithInvisibleFormatting + '\u2006' +
    +      stringWithInvisibleFormatting + '\u2008' +
    +      stringWithInvisibleFormatting + '\u2009' +
    +      stringWithInvisibleFormatting + '\u200A' +
    +      stringWithInvisibleFormatting + '\u200B' +
    +      stringWithInvisibleFormatting + '\u2029' +
    +      stringWithInvisibleFormatting;
    +
    +  // Test that the word break algorithm does not count RLMs towards word
    +  // length, and therefore does not insert word breaks into a typical date
    +  // written in Arabic-Indic digits with RTMs (b/5853915).
    +  assertEquals(textWithRLMs, insertWordBreaks(textWithRLMs, 10));
    +
    +  // Test that invisible formatting characters are not counted towards word
    +  // length, and that characters which are treated as breaking spaces behave as
    +  // breaking spaces.
    +  assertEquals(stringWithInvisibleFormattingAndSpacelikeCharacters,
    +      insertWordBreaks(stringWithInvisibleFormattingAndSpacelikeCharacters,
    +      10));
    +}
    +
    +function testInsertWordBreaksBasic() {
    +  // HTML that gets inserted is browser dependent, ensure for the test it is
    +  // a constant - browser dependent HTML is for display purposes only.
    +  propertyReplacer.set(goog.format, 'WORD_BREAK_HTML', '<wbr>');
    +  var insertWordBreaksBasic = goog.format.insertWordBreaksBasic;
    +
    +  assertEquals('abcdef', insertWordBreaksBasic('abcdef', 10));
    +  assertEquals('ab<wbr>cd<wbr>ef', insertWordBreaksBasic('abcdef', 2));
    +  assertEquals(
    +      'a<wbr>b<wbr>c<wbr>d<wbr>e<wbr>f', insertWordBreaksBasic('abcdef', 1));
    +  assertEquals('ab\u0300<wbr>c\u0301<wbr>de<wbr>f',
    +      insertWordBreaksBasic('ab\u0300c\u0301def', 2));
    +
    +  assertEquals(
    +      'Inserting word breaks into the word "Russia" should work fine.',
    +      '\u0420\u043E<wbr>\u0441\u0441<wbr>\u0438\u044F',
    +      insertWordBreaksBasic('\u0420\u043E\u0441\u0441\u0438\u044F', 2));
    +
    +  // The word 'Internet' in Hindi.
    +  var hindiInternet = '\u0907\u0902\u091F\u0930\u0928\u0947\u091F';
    +  assertEquals('The basic algorithm is not good enough to insert word ' +
    +      'breaks into Hindi.',
    +      hindiInternet, insertWordBreaksBasic(hindiInternet, 2));
    +  // The word 'Internet' in Hindi broken into slashes.
    +  assertEquals('Hindi can have word breaks inserted between slashes',
    +      hindiInternet + '<wbr>/' + hindiInternet + '<wbr>.' + hindiInternet,
    +      insertWordBreaksBasic(hindiInternet + '/' + hindiInternet + '.' +
    +          hindiInternet, 2));
    +}
    +
    +function testWordBreaksWorking() {
    +  var text = goog.string.repeat('test', 20);
    +  var textWbr = goog.string.repeat('test' + goog.format.WORD_BREAK_HTML, 20);
    +
    +  var overflowEl = goog.dom.createDom('div',
    +      {'style': 'width: 100px; overflow: hidden; margin 5px'});
    +  var wbrEl = goog.dom.createDom('div',
    +      {'style': 'width: 100px; overflow: hidden; margin-top: 15px'});
    +  goog.dom.appendChild(goog.global.document.body, overflowEl);
    +  goog.dom.appendChild(goog.global.document.body, wbrEl);
    +
    +  overflowEl.innerHTML = text;
    +  wbrEl.innerHTML = textWbr;
    +
    +  assertTrue('Text should overflow', overflowEl.scrollWidth > 100);
    +  assertTrue('Text should not overflow', wbrEl.scrollWidth <= 100);
    +}
    +
    +function testWordBreaksRemovedFromTextContent() {
    +  var expectedText = goog.string.repeat('test', 20);
    +  var textWbr = goog.string.repeat('test' + goog.format.WORD_BREAK_HTML, 20);
    +
    +  var wbrEl = goog.dom.createDom('div', null);
    +  wbrEl.innerHTML = textWbr;
    +
    +  assertEquals('text content should have wbr character removed', expectedText,
    +      goog.dom.getTextContent(wbrEl));
    +
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter.js b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter.js
    new file mode 100644
    index 0000000000..3fbb5bdf52
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter.js
    @@ -0,0 +1,409 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides functions to parse and pretty-print HTML strings.
    + *
    + */
    +
    +goog.provide('goog.format.HtmlPrettyPrinter');
    +goog.provide('goog.format.HtmlPrettyPrinter.Buffer');
    +
    +goog.require('goog.object');
    +goog.require('goog.string.StringBuffer');
    +
    +
    +
    +/**
    + * This class formats HTML to be more human-readable.
    + * TODO(user): Add hierarchical indentation.
    + * @param {number=} opt_timeOutMillis Max # milliseconds to spend on #format. If
    + *     this time is exceeded, return partially formatted. 0 or negative number
    + *     indicates no timeout.
    + * @constructor
    + * @final
    + */
    +goog.format.HtmlPrettyPrinter = function(opt_timeOutMillis) {
    +  /**
    +   * Max # milliseconds to spend on #format.
    +   * @type {number}
    +   * @private
    +   */
    +  this.timeOutMillis_ = opt_timeOutMillis && opt_timeOutMillis > 0 ?
    +      opt_timeOutMillis : 0;
    +};
    +
    +
    +/**
    + * Singleton.
    + * @type {goog.format.HtmlPrettyPrinter?}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.instance_ = null;
    +
    +
    +/**
    + * Singleton lazy initializer.
    + * @return {!goog.format.HtmlPrettyPrinter} Singleton.
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.getInstance_ = function() {
    +  if (!goog.format.HtmlPrettyPrinter.instance_) {
    +    goog.format.HtmlPrettyPrinter.instance_ =
    +        new goog.format.HtmlPrettyPrinter();
    +  }
    +  return goog.format.HtmlPrettyPrinter.instance_;
    +};
    +
    +
    +/**
    + * Static utility function. See prototype #format.
    + * @param {string} html The HTML text to pretty print.
    + * @return {string} Formatted result.
    + */
    +goog.format.HtmlPrettyPrinter.format = function(html) {
    +  return goog.format.HtmlPrettyPrinter.getInstance_().format(html);
    +};
    +
    +
    +/**
    + * List of patterns used to tokenize HTML for pretty printing. Cache
    + * subexpression for tag name.
    + * comment|meta-tag|tag|text|other-less-than-characters
    + * @type {RegExp}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ =
    +    /(?:<!--.*?-->|<!.*?>|<(\/?)(\w+)[^>]*>|[^<]+|<)/g;
    +
    +
    +/**
    + * Tags whose contents we don't want pretty printed.
    + * @type {Object}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.NON_PRETTY_PRINTED_TAGS_ = goog.object.createSet(
    +    'script',
    +    'style',
    +    'pre',
    +    'xmp');
    +
    +
    +/**
    + * 'Block' tags. We should add newlines before and after these tags during
    + * pretty printing. Tags drawn mostly from HTML4 definitions for block and other
    + * non-online tags, excepting the ones in
    + * #goog.format.HtmlPrettyPrinter.NON_PRETTY_PRINTED_TAGS_.
    + *
    + * @type {Object}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.BLOCK_TAGS_ = goog.object.createSet(
    +    'address',
    +    'applet',
    +    'area',
    +    'base',
    +    'basefont',
    +    'blockquote',
    +    'body',
    +    'caption',
    +    'center',
    +    'col',
    +    'colgroup',
    +    'dir',
    +    'div',
    +    'dl',
    +    'fieldset',
    +    'form',
    +    'frame',
    +    'frameset',
    +    'h1',
    +    'h2',
    +    'h3',
    +    'h4',
    +    'h5',
    +    'h6',
    +    'head',
    +    'hr',
    +    'html',
    +    'iframe',
    +    'isindex',
    +    'legend',
    +    'link',
    +    'menu',
    +    'meta',
    +    'noframes',
    +    'noscript',
    +    'ol',
    +    'optgroup',
    +    'option',
    +    'p',
    +    'param',
    +    'table',
    +    'tbody',
    +    'td',
    +    'tfoot',
    +    'th',
    +    'thead',
    +    'title',
    +    'tr',
    +    'ul');
    +
    +
    +/**
    + * Non-block tags that break flow. We insert a line break after, but not before
    + * these. Tags drawn from HTML4 definitions.
    + * @type {Object}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.BREAKS_FLOW_TAGS_ = goog.object.createSet(
    +    'br',
    +    'dd',
    +    'dt',
    +    'br',
    +    'li',
    +    'noframes');
    +
    +
    +/**
    + * Empty tags. These are treated as both start and end tags.
    + * @type {Object}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.EMPTY_TAGS_ = goog.object.createSet(
    +    'br',
    +    'hr',
    +    'isindex');
    +
    +
    +/**
    + * Breaks up HTML so it's easily readable by the user.
    + * @param {string} html The HTML text to pretty print.
    + * @return {string} Formatted result.
    + * @throws {Error} Regex error, data loss, or endless loop detected.
    + */
    +goog.format.HtmlPrettyPrinter.prototype.format = function(html) {
    +  // Trim leading whitespace, but preserve first indent; in other words, keep
    +  // any spaces immediately before the first non-whitespace character (that's
    +  // what $1 is), but remove all other leading whitespace. This adjustment
    +  // historically had been made in Docs. The motivation is that some
    +  // browsers prepend several line breaks in designMode.
    +  html = html.replace(/^\s*?( *\S)/, '$1');
    +
    +  // Trim trailing whitespace.
    +  html = html.replace(/\s+$/, '');
    +
    +  // Keep track of how much time we've used.
    +  var timeOutMillis = this.timeOutMillis_;
    +  var startMillis = timeOutMillis ? goog.now() : 0;
    +
    +  // Handles concatenation of the result and required line breaks.
    +  var buffer = new goog.format.HtmlPrettyPrinter.Buffer();
    +
    +  // Declare these for efficiency since we access them in a loop.
    +  var tokenRegex = goog.format.HtmlPrettyPrinter.TOKEN_REGEX_;
    +  var nonPpTags = goog.format.HtmlPrettyPrinter.NON_PRETTY_PRINTED_TAGS_;
    +  var blockTags = goog.format.HtmlPrettyPrinter.BLOCK_TAGS_;
    +  var breaksFlowTags = goog.format.HtmlPrettyPrinter.BREAKS_FLOW_TAGS_;
    +  var emptyTags = goog.format.HtmlPrettyPrinter.EMPTY_TAGS_;
    +
    +  // Used to verify we're making progress through our regex tokenization.
    +  var lastIndex = 0;
    +
    +  // Use this to track non-pretty-printed tags and childen.
    +  var nonPpTagStack = [];
    +
    +  // Loop through each matched token.
    +  var match;
    +  while (match = tokenRegex.exec(html)) {
    +    // Get token.
    +    var token = match[0];
    +
    +    // Is this token a tag? match.length == 3 for tags, 1 for all others.
    +    if (match.length == 3) {
    +      var tagName = match[2];
    +      if (tagName) {
    +        tagName = tagName.toLowerCase();
    +      }
    +
    +      // Non-pretty-printed tags?
    +      if (nonPpTags.hasOwnProperty(tagName)) {
    +        // End tag?
    +        if (match[1] == '/') {
    +          // Do we have a matching start tag?
    +          var stackSize = nonPpTagStack.length;
    +          var startTagName = stackSize ? nonPpTagStack[stackSize - 1] : null;
    +          if (startTagName == tagName) {
    +            // End of non-pretty-printed block. Line break after.
    +            nonPpTagStack.pop();
    +            buffer.pushToken(false, token, !nonPpTagStack.length);
    +          } else {
    +            // Malformed HTML. No line breaks.
    +            buffer.pushToken(false, token, false);
    +          }
    +        } else {
    +          // Start of non-pretty-printed block. Line break before.
    +          buffer.pushToken(!nonPpTagStack.length, token, false);
    +          nonPpTagStack.push(tagName);
    +        }
    +      } else if (nonPpTagStack.length) {
    +        // Inside non-pretty-printed block, no new line breaks.
    +        buffer.pushToken(false, token, false);
    +      } else if (blockTags.hasOwnProperty(tagName)) {
    +        // Put line break before start block and after end block tags.
    +        var isEmpty = emptyTags.hasOwnProperty(tagName);
    +        var isEndTag = match[1] == '/';
    +        buffer.pushToken(isEmpty || !isEndTag, token, isEmpty || isEndTag);
    +      } else if (breaksFlowTags.hasOwnProperty(tagName)) {
    +        var isEmpty = emptyTags.hasOwnProperty(tagName);
    +        var isEndTag = match[1] == '/';
    +        // Put line break after end flow-breaking tags.
    +        buffer.pushToken(false, token, isEndTag || isEmpty);
    +      } else {
    +        // All other tags, no line break.
    +        buffer.pushToken(false, token, false);
    +      }
    +    } else {
    +      // Non-tags, no line break.
    +      buffer.pushToken(false, token, false);
    +    }
    +
    +    // Double check that we're making progress.
    +    var newLastIndex = tokenRegex.lastIndex;
    +    if (!token || newLastIndex <= lastIndex) {
    +      throw Error('Regex failed to make progress through source html.');
    +    }
    +    lastIndex = newLastIndex;
    +
    +    // Out of time?
    +    if (timeOutMillis) {
    +      if (goog.now() - startMillis > timeOutMillis) {
    +        // Push unprocessed data as one big token and reset regex object.
    +        buffer.pushToken(false, html.substring(tokenRegex.lastIndex), false);
    +        tokenRegex.lastIndex = 0;
    +        break;
    +      }
    +    }
    +  }
    +
    +  // Ensure we end in a line break.
    +  buffer.lineBreak();
    +
    +  // Construct result string.
    +  var result = String(buffer);
    +
    +  // Length should be original length plus # line breaks added.
    +  var expectedLength = html.length + buffer.breakCount;
    +  if (result.length != expectedLength) {
    +    throw Error('Lost data pretty printing html.');
    +  }
    +
    +  return result;
    +};
    +
    +
    +
    +/**
    + * This class is a buffer to which we push our output. It tracks line breaks to
    + * make sure we don't add unnecessary ones.
    + * @constructor
    + * @final
    + */
    +goog.format.HtmlPrettyPrinter.Buffer = function() {
    +  /**
    +   * Tokens to be output in #toString.
    +   * @type {goog.string.StringBuffer}
    +   * @private
    +   */
    +  this.out_ = new goog.string.StringBuffer();
    +};
    +
    +
    +/**
    + * Tracks number of line breaks added.
    + * @type {number}
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.breakCount = 0;
    +
    +
    +/**
    + * Tracks if we are at the start of a new line.
    + * @type {boolean}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.isBeginningOfNewLine_ = true;
    +
    +
    +/**
    + * Tracks if we need a new line before the next token.
    + * @type {boolean}
    + * @private
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.needsNewLine_ = false;
    +
    +
    +/**
    + * Adds token and necessary line breaks to output buffer.
    + * @param {boolean} breakBefore If true, add line break before token if
    + *     necessary.
    + * @param {string} token Token to push.
    + * @param {boolean} breakAfter If true, add line break after token if
    + *     necessary.
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.pushToken = function(
    +    breakBefore, token, breakAfter) {
    +  // If this token needs a preceeding line break, and
    +  // we haven't already added a line break, and
    +  // this token does not start with a line break,
    +  // then add line break.
    +  // Due to FF3.0 bug with lists, we don't insert a /n
    +  // right before </ul>. See bug 1520665.
    +  if ((this.needsNewLine_ || breakBefore) &&
    +      !/^\r?\n/.test(token) &&
    +      !/\/ul/i.test(token)) {
    +    this.lineBreak();
    +  }
    +
    +  // Token.
    +  this.out_.append(token);
    +
    +  // Remember if this string ended with a line break so we know we don't have to
    +  // insert another one before the next token.
    +  this.isBeginningOfNewLine_ = /\r?\n$/.test(token);
    +
    +  // Remember if this token requires a line break after it. We don't insert it
    +  // here because we might not have to if the next token starts with a line
    +  // break.
    +  this.needsNewLine_ = breakAfter && !this.isBeginningOfNewLine_;
    +};
    +
    +
    +/**
    + * Append line break if we need one.
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.lineBreak = function() {
    +  if (!this.isBeginningOfNewLine_) {
    +    this.out_.append('\n');
    +    ++this.breakCount;
    +  }
    +};
    +
    +
    +/**
    + * @return {string} String representation of tokens.
    + * @override
    + */
    +goog.format.HtmlPrettyPrinter.Buffer.prototype.toString = function() {
    +  return this.out_.toString();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.html b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.html
    new file mode 100644
    index 0000000000..03cf1ff002
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.format.HtmlPrettyPrinter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.format.HtmlPrettyPrinterTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.js b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.js
    new file mode 100644
    index 0000000000..3519e92317
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/htmlprettyprinter_test.js
    @@ -0,0 +1,205 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.format.HtmlPrettyPrinterTest');
    +goog.setTestOnly('goog.format.HtmlPrettyPrinterTest');
    +
    +goog.require('goog.format.HtmlPrettyPrinter');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var COMPLEX_HTML = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    +    '<!-- internal declarations -->]>' +
    +    '<html><head><title>My HTML</title><!-- my comment --></head>' +
    +    '<body><h1>My Header</h1>My text.<br><b>My bold text.</b><hr>' +
    +    '<pre>My\npreformatted <br> HTML.</pre>5 < 10</body>' +
    +    '</html>';
    +var mockClock;
    +var mockClockTicks;
    +
    +function setUp() {
    +  mockClockTicks = 0;
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.getCurrentTime = function() {
    +    return mockClockTicks++;
    +  };
    +  mockClock.install();
    +}
    +
    +function tearDown() {
    +  if (mockClock) {
    +    mockClock.uninstall();
    +  }
    +}
    +
    +function testSimpleHtml() {
    +  var actual = goog.format.HtmlPrettyPrinter.format('<br><b>bold</b>');
    +  assertEquals('<br>\n<b>bold</b>\n', actual);
    +  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
    +}
    +
    +function testSimpleHtmlMixedCase() {
    +  var actual = goog.format.HtmlPrettyPrinter.format('<BR><b>bold</b>');
    +  assertEquals('<BR>\n<b>bold</b>\n', actual);
    +  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
    +}
    +
    +function testComplexHtml() {
    +  var actual = goog.format.HtmlPrettyPrinter.format(COMPLEX_HTML);
    +  var expected = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    +      '<!-- internal declarations -->]>\n' +
    +      '<html>\n' +
    +      '<head>\n' +
    +      '<title>My HTML</title>\n' +
    +      '<!-- my comment -->' +
    +      '</head>\n' +
    +      '<body>\n' +
    +      '<h1>My Header</h1>\n' +
    +      'My text.<br>\n' +
    +      '<b>My bold text.</b>\n' +
    +      '<hr>\n' +
    +      '<pre>My\npreformatted <br> HTML.</pre>\n' +
    +      '5 < 10' +
    +      '</body>\n' +
    +      '</html>\n';
    +  assertEquals(expected, actual);
    +  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
    +}
    +
    +function testTimeout() {
    +  var pp = new goog.format.HtmlPrettyPrinter(3);
    +  var actual = pp.format(COMPLEX_HTML);
    +  var expected = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    +      '<!-- internal declarations -->]>\n' +
    +      '<html>\n' +
    +      '<head><title>My HTML</title><!-- my comment --></head>' +
    +      '<body><h1>My Header</h1>My text.<br><b>My bold text.</b><hr>' +
    +      '<pre>My\npreformatted <br> HTML.</pre>5 < 10</body>' +
    +      '</html>\n';
    +  assertEquals(expected, actual);
    +}
    +
    +function testKeepLeadingIndent() {
    +  var original = ' <b>Bold</b> <i>Ital</i> ';
    +  var expected = ' <b>Bold</b> <i>Ital</i>\n';
    +  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +function testTrimLeadingLineBreaks() {
    +  var original = '\n \t\r\n  \n <b>Bold</b> <i>Ital</i> ';
    +  var expected = ' <b>Bold</b> <i>Ital</i>\n';
    +  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +function testExtraLines() {
    +  var original = '<br>\ntombrat';
    +  assertEquals(original + '\n', goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +function testCrlf() {
    +  var original = '<br>\r\none\r\ntwo<br>';
    +  assertEquals(original + '\n', goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +function testEndInLineBreak() {
    +  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo'));
    +  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo\n'));
    +  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo\n\n'));
    +  assertEquals('foo<br>\n', goog.format.HtmlPrettyPrinter.format('foo<br>'));
    +  assertEquals('foo<br>\n', goog.format.HtmlPrettyPrinter.format('foo<br>\n'));
    +}
    +
    +function testTable() {
    +  var original = '<table>' +
    +      '<tr><td>one.one</td><td>one.two</td></tr>' +
    +      '<tr><td>two.one</td><td>two.two</td></tr>' +
    +      '</table>';
    +  var expected = '<table>\n' +
    +      '<tr>\n<td>one.one</td>\n<td>one.two</td>\n</tr>\n' +
    +      '<tr>\n<td>two.one</td>\n<td>two.two</td>\n</tr>\n' +
    +      '</table>\n';
    +  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +
    +/**
    + * We have a sanity check in HtmlPrettyPrinter to make sure the regex index
    + * advances after every match. We should never hit this, but we include it on
    + * the chance there is some corner case where the pattern would match but not
    + * process a new token. It's not generally a good idea to break the
    + * implementation to test behavior, but this is the easiest way to mimic a
    + * bad internal state.
    + */
    +function testRegexMakesProgress() {
    +  var original = goog.format.HtmlPrettyPrinter.TOKEN_REGEX_;
    +
    +  try {
    +    // This regex matches \B, an index between 2 word characters, so the regex
    +    // index does not advance when matching this.
    +    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ =
    +        /(?:\B|<!--.*?-->|<!.*?>|<(\/?)(\w+)[^>]*>|[^<]+|<)/g;
    +
    +    // It would work on this string.
    +    assertEquals('f o o\n', goog.format.HtmlPrettyPrinter.format('f o o'));
    +
    +    // But not this one.
    +    var ex = assertThrows('should have failed for invalid regex - endless loop',
    +        goog.partial(goog.format.HtmlPrettyPrinter.format, COMPLEX_HTML));
    +    assertEquals('Regex failed to make progress through source html.',
    +        ex.message);
    +  } finally {
    +    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ = original;
    +  }
    +}
    +
    +
    +/**
    + * FF3.0 doesn't like \n between </li> and </ul>. See bug 1520665.
    + */
    +function testLists() {
    +  var original = '<ul><li>one</li><ul><li>two</li></UL><li>three</li></ul>';
    +  var expected =
    +      '<ul><li>one</li>\n<ul><li>two</li></UL>\n<li>three</li></ul>\n';
    +  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
    +}
    +
    +
    +/**
    + * We have a sanity check in HtmlPrettyPrinter to make sure the regex fully
    + * tokenizes the string. We should never hit this, but we include it on the
    + * chance there is some corner case where the pattern would miss a section of
    + * original string. It's not generally a good idea to break the
    + * implementation to test behavior, but this is the easiest way to mimic a
    + * bad internal state.
    + */
    +function testAvoidDataLoss() {
    +  var original = goog.format.HtmlPrettyPrinter.TOKEN_REGEX_;
    +
    +  try {
    +    // This regex does not match stranded '<' characters, so does not fully
    +    // tokenize the string.
    +    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ =
    +        /(?:<!--.*?-->|<!.*?>|<(\/?)(\w+)[^>]*>|[^<]+)/g;
    +
    +    // It would work on this string.
    +    assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo'));
    +
    +    // But not this one.
    +    var ex = assertThrows('should have failed for invalid regex - data loss',
    +        goog.partial(goog.format.HtmlPrettyPrinter.format, COMPLEX_HTML));
    +    assertEquals('Lost data pretty printing html.', ex.message);
    +  } finally {
    +    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ = original;
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress.js b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress.js
    new file mode 100644
    index 0000000000..fd1dfe6cf0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress.js
    @@ -0,0 +1,256 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides functions to parse and manipulate internationalized
    + * email addresses. This is useful in the context of Email Address
    + * Internationalization (EAI) as defined by RFC6530.
    + *
    + */
    +
    +goog.provide('goog.format.InternationalizedEmailAddress');
    +
    +goog.require('goog.format.EmailAddress');
    +
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Formats an email address string for display, and allows for extraction of
    + * the individual components of the address.
    + * @param {string=} opt_address The email address.
    + * @param {string=} opt_name The name associated with the email address.
    + * @constructor
    + * @extends {goog.format.EmailAddress}
    + */
    +goog.format.InternationalizedEmailAddress = function(opt_address, opt_name) {
    +  goog.format.InternationalizedEmailAddress.base(
    +      this, 'constructor', opt_address, opt_name);
    +};
    +goog.inherits(
    +    goog.format.InternationalizedEmailAddress, goog.format.EmailAddress);
    +
    +
    +/**
    + * A string representing the RegExp for the local part of an EAI email address.
    + * @private
    + */
    +goog.format.InternationalizedEmailAddress.EAI_LOCAL_PART_REGEXP_STR_ =
    +    '((?!\\s)[+a-zA-Z0-9_.!#$%&\'*\\/=?^`{|}~\u0080-\uFFFFFF-])+';
    +
    +
    +/**
    + * A string representing the RegExp for a label in the domain part of an EAI
    + * email address.
    + * @private
    + */
    +goog.format.InternationalizedEmailAddress.EAI_LABEL_CHAR_REGEXP_STR_ =
    +    '(?!\\s)[a-zA-Z0-9\u0080-\u3001\u3003-\uFF0D\uFF0F-\uFF60\uFF62-\uFFFFFF-]';
    +
    +
    +/**
    + * A string representing the RegExp for the domain part of an EAI email address.
    + * @private
    + */
    +goog.format.InternationalizedEmailAddress.EAI_DOMAIN_PART_REGEXP_STR_ =
    +    // A unicode character (ASCII or Unicode excluding periods)
    +    '(' + goog.format.InternationalizedEmailAddress.EAI_LABEL_CHAR_REGEXP_STR_ +
    +    // Such character 1+ times, followed by a Unicode period. All 1+ times.
    +    '+[\\.\\uFF0E\\u3002\\uFF61])+' +
    +    // And same thing but without a period in the end
    +    goog.format.InternationalizedEmailAddress.EAI_LABEL_CHAR_REGEXP_STR_ +
    +    '{2,63}';
    +
    +
    +/**
    + * Match string for address separators. This list is the result of the
    + * discussion in b/16241003.
    + * @type {string}
    + * @private
    + */
    +goog.format.InternationalizedEmailAddress.ADDRESS_SEPARATORS_ =
    +    ',' + // U+002C ( , ) COMMA
    +    ';' + // U+003B ( ; ) SEMICOLON
    +    '\u055D' + // ( ՝ ) ARMENIAN COMMA
    +    '\u060C' + // ( ، ) ARABIC COMMA
    +    '\u1363' + // ( ፣ ) ETHIOPIC COMMA
    +    '\u1802' + // ( ᠂ ) MONGOLIAN COMMA
    +    '\u1808' + // ( ᠈ ) MONGOLIAN MANCHU COMMA
    +    '\u2E41' + // ( ⹁ ) REVERSED COMMA
    +    '\u3001' + // ( 、 ) IDEOGRAPHIC COMMA
    +    '\uFF0C' + // ( , ) FULLWIDTH COMMA
    +    '\u061B' + // ( ‎؛‎ ) ARABIC SEMICOLON
    +    '\u1364' + // ( ፤ ) ETHIOPIC SEMICOLON
    +    '\uFF1B' + // ( ; ) FULLWIDTH SEMICOLON
    +    '\uFF64' + // ( 、 ) HALFWIDTH IDEOGRAPHIC COMMA
    +    '\u104A'; // ( ၊ ) MYANMAR SIGN LITTLE SECTION
    +
    +
    +/**
    + * Match string for characters that, when in a display name, require it to be
    + * quoted.
    + * @type {string}
    + * @private
    + */
    +goog.format.InternationalizedEmailAddress.CHARS_REQUIRE_QUOTES_ =
    +    goog.format.EmailAddress.SPECIAL_CHARS +
    +    goog.format.InternationalizedEmailAddress.ADDRESS_SEPARATORS_;
    +
    +
    +/**
    + * A RegExp to match the local part of an EAI email address.
    + * @private {!RegExp}
    + */
    +goog.format.InternationalizedEmailAddress.EAI_LOCAL_PART_ =
    +    new RegExp('^' +
    +        goog.format.InternationalizedEmailAddress.EAI_LOCAL_PART_REGEXP_STR_ +
    +        '$');
    +
    +
    +/**
    + * A RegExp to match the domain part of an EAI email address.
    + * @private {!RegExp}
    + */
    +goog.format.InternationalizedEmailAddress.EAI_DOMAIN_PART_ =
    +    new RegExp('^' +
    +        goog.format.InternationalizedEmailAddress.EAI_DOMAIN_PART_REGEXP_STR_ +
    +        '$');
    +
    +
    +/**
    + * A RegExp to match an EAI email address.
    + * @private {!RegExp}
    + */
    +goog.format.InternationalizedEmailAddress.EAI_EMAIL_ADDRESS_ =
    +    new RegExp('^' +
    +        goog.format.InternationalizedEmailAddress.EAI_LOCAL_PART_REGEXP_STR_ +
    +        '@' +
    +        goog.format.InternationalizedEmailAddress.EAI_DOMAIN_PART_REGEXP_STR_ +
    +        '$');
    +
    +
    +/**
    + * Checks if the provided string is a valid local part (part before the '@') of
    + * an EAI email address.
    + * @param {string} str The local part to check.
    + * @return {boolean} Whether the provided string is a valid local part.
    + */
    +goog.format.InternationalizedEmailAddress.isValidLocalPartSpec = function(str) {
    +  if (!goog.isDefAndNotNull(str)) {
    +    return false;
    +  }
    +  return goog.format.InternationalizedEmailAddress.EAI_LOCAL_PART_.test(str);
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid domain part (part after the '@') of
    + * an EAI email address.
    + * @param {string} str The domain part to check.
    + * @return {boolean} Whether the provided string is a valid domain part.
    + */
    +goog.format.InternationalizedEmailAddress.isValidDomainPartSpec =
    +    function(str) {
    +  if (!goog.isDefAndNotNull(str)) {
    +    return false;
    +  }
    +  return goog.format.InternationalizedEmailAddress.EAI_DOMAIN_PART_.test(str);
    +};
    +
    +
    +/** @override */
    +goog.format.InternationalizedEmailAddress.prototype.isValid = function() {
    +  return goog.format.InternationalizedEmailAddress.isValidAddrSpec(
    +      this.address);
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid email address. Supports both
    + * simple email addresses (address specs) and addresses that contain display
    + * names.
    + * @param {string} str The email address to check.
    + * @return {boolean} Whether the provided string is a valid address.
    + */
    +goog.format.InternationalizedEmailAddress.isValidAddress = function(str) {
    +  if (!goog.isDefAndNotNull(str)) {
    +    return false;
    +  }
    +  return goog.format.InternationalizedEmailAddress.parse(str).isValid();
    +};
    +
    +
    +/**
    + * Checks if the provided string is a valid address spec (local@domain.com).
    + * @param {string} str The email address to check.
    + * @return {boolean} Whether the provided string is a valid address spec.
    + */
    +goog.format.InternationalizedEmailAddress.isValidAddrSpec = function(str) {
    +  if (!goog.isDefAndNotNull(str)) {
    +    return false;
    +  }
    +
    +  // This is a fairly naive implementation, but it covers 99% of use cases.
    +  // For more details, see http://en.wikipedia.org/wiki/Email_address#Syntax
    +  return goog.format.InternationalizedEmailAddress.EAI_EMAIL_ADDRESS_.test(str);
    +};
    +
    +
    +/**
    + * Parses a string containing email addresses of the form
    + * "name" &lt;address&gt; into an array of email addresses.
    + * @param {string} str The address list.
    + * @return {!Array<!goog.format.EmailAddress>} The parsed emails.
    + */
    +goog.format.InternationalizedEmailAddress.parseList = function(str) {
    +  return goog.format.EmailAddress.parseListInternal(
    +      str, goog.format.InternationalizedEmailAddress.parse,
    +      goog.format.InternationalizedEmailAddress.isAddressSeparator);
    +};
    +
    +
    +/**
    + * Parses an email address of the form "name" &lt;address&gt; into
    + * an email address.
    + * @param {string} addr The address string.
    + * @return {!goog.format.EmailAddress} The parsed address.
    + */
    +goog.format.InternationalizedEmailAddress.parse = function(addr) {
    +  return goog.format.EmailAddress.parseInternal(
    +      addr, goog.format.InternationalizedEmailAddress);
    +};
    +
    +
    +/**
    + * @param {string} ch The character to test.
    + * @return {boolean} Whether the provided character is an address separator.
    + */
    +goog.format.InternationalizedEmailAddress.isAddressSeparator = function(ch) {
    +  return goog.string.contains(
    +      goog.format.InternationalizedEmailAddress.ADDRESS_SEPARATORS_, ch);
    +};
    +
    +
    +/**
    + * Return the address in a standard format:
    + *  - remove extra spaces.
    + *  - Surround name with quotes if it contains special characters.
    + * @return {string} The cleaned address.
    + * @override
    + */
    +goog.format.InternationalizedEmailAddress.prototype.toString = function() {
    +  return this.toStringInternal(
    +      goog.format.InternationalizedEmailAddress.CHARS_REQUIRE_QUOTES_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.html b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.html
    new file mode 100644
    index 0000000000..c07e2137a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.format.InternationalizedEmailAddress
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.format.InternationalizedEmailAddressTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.js b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.js
    new file mode 100644
    index 0000000000..51450ee57f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/internationalizedemailaddress_test.js
    @@ -0,0 +1,335 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.format.InternationalizedEmailAddressTest');
    +goog.setTestOnly('goog.format.InternationalizedEmailAddressTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.format.InternationalizedEmailAddress');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Asserts that the given validation function generates the expected outcome for
    + * a set of expected valid and a second set of expected invalid addresses.
    + * containing the specified address strings, irrespective of their order.
    + * @param {function(string):boolean} testFunc Validation function to be tested.
    + * @param {!Array<string>} valid List of addresses that should be valid.
    + * @param {!Array<string>} invalid List of addresses that should be invalid.
    + * @private
    + */
    +function doIsValidTest(testFunc, valid, invalid) {
    +  goog.array.forEach(valid, function(str) {
    +    assertTrue('"' + str + '" should be valid.', testFunc(str));
    +  });
    +  goog.array.forEach(invalid, function(str) {
    +    assertFalse('"' + str + '" should be invalid.', testFunc(str));
    +  });
    +}
    +
    +
    +/**
    + * Asserts that parsing the inputString produces a list of email addresses
    + * containing the specified address strings, irrespective of their order.
    + * @param {string} inputString A raw address list.
    + * @param {!Array<string>} expectedList The expected results.
    + * @param {string=} opt_message An assertion message.
    + * @return {string} the resulting email address objects.
    + */
    +function assertParsedList(inputString, expectedList, opt_message) {
    +  var message = opt_message || 'Should parse address correctly';
    +  var result = goog.format.InternationalizedEmailAddress.parseList(inputString);
    +  assertEquals(
    +      'Should have correct # of addresses', expectedList.length, result.length);
    +  for (var i = 0; i < expectedList.length; ++i) {
    +    assertEquals(message, expectedList[i], result[i].getAddress());
    +  }
    +  return result;
    +}
    +
    +function testParseList() {
    +  // Test only the new cases added by EAI (other cases covered in parent
    +  // class test)
    +  assertParsedList('<me.みんあ@me.xn--l8jtg9b>', ['me.みんあ@me.xn--l8jtg9b']);
    +}
    +
    +function testIsEaiValid() {
    +  var valid = [
    +    'e@b.eu',
    +    '<a.b+foo@c.com>',
    +    'eric <e@b.com>',
    +    '"e" <e@b.com>',
    +    'a@FOO.MUSEUM',
    +    'bla@b.co.ac.uk',
    +    'bla@a.b.com',
    +    'o\'hara@gm.com',
    +    'plus+is+allowed@gmail.com',
    +    '!/#$%&\'*+-=~|`{}?^_@expample.com',
    +    'confirm-bhk=modulo.org@yahoogroups.com',
    +    'み.ん-あ@みんあ.みんあ',
    +    'みんあ@test.com',
    +    'test@test.みんあ',
    +    'test@みんあ.com',
    +    'me.みんあ@me.xn--l8jtg9b',
    +    'みんあ@me.xn--l8jtg9b',
    +    'fullwidthfullstop@sld' + '\uff0e' + 'tld',
    +    'ideographicfullstop@sld' + '\u3002' + 'tld',
    +    'halfwidthideographicfullstop@sld' + '\uff61' + 'tld'
    +  ];
    +  var invalid = [
    +    null,
    +    undefined,
    +    'e',
    +    '',
    +    'e @c.com',
    +    'a@b',
    +    'foo.com',
    +    'foo@c..com',
    +    'test@gma=il.com',
    +    'aaa@gmail',
    +    'has some spaces@gmail.com',
    +    'has@three@at@signs.com',
    +    '@no-local-part.com'
    +  ];
    +  doIsValidTest(
    +      goog.format.InternationalizedEmailAddress.isValidAddress, valid, invalid);
    +}
    +
    +function testIsValidLocalPart() {
    +  var valid = [
    +    'e',
    +    'a.b+foo',
    +    'o\'hara',
    +    'user+someone',
    +    '!/#$%&\'*+-=~|`{}?^_',
    +    'confirm-bhk=modulo.org',
    +    'me.みんあ',
    +    'みんあ'
    +  ];
    +  var invalid = [
    +    null,
    +    undefined,
    +    'A@b@c',
    +    'a"b(c)d,e:f;g<h>i[j\\k]l',
    +    'just"not"right',
    +    'this is"not\\allowed',
    +    'this\\ still\"not\\\\allowed',
    +    'has some spaces'
    +  ];
    +  doIsValidTest(goog.format.InternationalizedEmailAddress.isValidLocalPartSpec,
    +      valid, invalid);
    +}
    +
    +function testIsValidDomainPart() {
    +  var valid = [
    +    'example.com',
    +    'dept.example.org',
    +    'long.domain.with.lots.of.dots',
    +    'me.xn--l8jtg9b',
    +    'me.みんあ',
    +    'sld.looooooongtld',
    +    'sld' + '\uff0e' + 'tld',
    +    'sld' + '\u3002' + 'tld',
    +    'sld' + '\uff61' + 'tld'
    +  ];
    +  var invalid = [
    +    null,
    +    undefined,
    +    '',
    +    '@has.an.at.sign',
    +    '..has.leading.dots',
    +    'gma=il.com',
    +    'DoesNotHaveADot',
    +    'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffffgggggggggg'
    +  ];
    +  doIsValidTest(goog.format.InternationalizedEmailAddress.isValidDomainPartSpec,
    +      valid, invalid);
    +}
    +
    +
    +function testparseListWithAdditionalSeparators() {
    +  assertParsedList('<foo@gmail.com>\u055D <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+055D');
    +  assertParsedList('<foo@gmail.com>\u055D <bar@gmail.com>\u055D',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+055D');
    +
    +  assertParsedList('<foo@gmail.com>\u060C <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+060C');
    +  assertParsedList('<foo@gmail.com>\u060C <bar@gmail.com>\u060C',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+060C');
    +
    +  assertParsedList('<foo@gmail.com>\u1363 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+1363');
    +  assertParsedList('<foo@gmail.com>\u1363 <bar@gmail.com>\u1363',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+1363');
    +
    +  assertParsedList('<foo@gmail.com>\u1802 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+1802');
    +  assertParsedList('<foo@gmail.com>\u1802 <bar@gmail.com>\u1802',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+1802');
    +
    +  assertParsedList('<foo@gmail.com>\u1808 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+1808');
    +  assertParsedList('<foo@gmail.com>\u1808 <bar@gmail.com>\u1808',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+1808');
    +
    +  assertParsedList('<foo@gmail.com>\u2E41 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+2E41');
    +  assertParsedList('<foo@gmail.com>\u2E41 <bar@gmail.com>\u2E41',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+2E41');
    +
    +  assertParsedList('<foo@gmail.com>\u3001 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+3001');
    +  assertParsedList('<foo@gmail.com>\u3001 <bar@gmail.com>\u3001',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+3001');
    +
    +  assertParsedList('<foo@gmail.com>\uFF0C <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+FF0C');
    +  assertParsedList('<foo@gmail.com>\uFF0C <bar@gmail.com>\uFF0C',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+FF0C');
    +
    +  assertParsedList('<foo@gmail.com>\u0613 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+0613');
    +  assertParsedList('<foo@gmail.com>\u0613 <bar@gmail.com>\u0613',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+0613');
    +
    +  assertParsedList('<foo@gmail.com>\u1364 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+1364');
    +  assertParsedList('<foo@gmail.com>\u1364 <bar@gmail.com>\u1364',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+1364');
    +
    +  assertParsedList('<foo@gmail.com>\uFF1B <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+FF1B');
    +  assertParsedList('<foo@gmail.com>\uFF1B <bar@gmail.com>\uFF1B',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+FF1B');
    +
    +  assertParsedList('<foo@gmail.com>\uFF64 <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+FF64');
    +  assertParsedList('<foo@gmail.com>\uFF64 <bar@gmail.com>\uFF64',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+FF64');
    +
    +  assertParsedList('<foo@gmail.com>\u104A <bar@gmail.com>',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with U+104A');
    +  assertParsedList('<foo@gmail.com>\u104A <bar@gmail.com>\u104A',
    +      ['foo@gmail.com', 'bar@gmail.com'],
    +      'Failed to parse 2 email addresses with trailing U+104A');
    +}
    +
    +function testToString() {
    +  var f = function(str) {
    +    return goog.format.InternationalizedEmailAddress.parse(str).toString();
    +  };
    +
    +  // No modification.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('JOHN Doe <john@gmail.com>'));
    +
    +  // Extra spaces.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f(' JOHN  Doe  <john@gmail.com> '));
    +
    +  // No name.
    +  assertEquals('john@gmail.com', f('<john@gmail.com>'));
    +  assertEquals('john@gmail.com', f('john@gmail.com'));
    +
    +  // No address.
    +  assertEquals('JOHN Doe', f('JOHN Doe <>'));
    +
    +  // Already quoted.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('"JOHN, Doe" <john@gmail.com>'));
    +
    +  // Needless quotes.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('"JOHN Doe" <john@gmail.com>'));
    +  // Not quoted-string, but has double quotes.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, "Doe" <john@gmail.com>'));
    +
    +  // No special characters other than quotes.
    +  assertEquals('JOHN Doe <john@gmail.com>',
    +               f('JOHN "Doe" <john@gmail.com>'));
    +
    +  // Escaped quotes are also removed.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, \\"Doe\\" <john@gmail.com>'));
    +
    +  // Characters that require quoting for the display name.
    +  assertEquals('"JOHN, Doe" <john@gmail.com>',
    +               f('JOHN, Doe <john@gmail.com>'));
    +  assertEquals('"JOHN; Doe" <john@gmail.com>',
    +               f('JOHN; Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u055D Doe" <john@gmail.com>',
    +               f('JOHN\u055D Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u060C Doe" <john@gmail.com>',
    +               f('JOHN\u060C Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u1363 Doe" <john@gmail.com>',
    +               f('JOHN\u1363 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u1802 Doe" <john@gmail.com>',
    +               f('JOHN\u1802 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u1808 Doe" <john@gmail.com>',
    +               f('JOHN\u1808 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u2E41 Doe" <john@gmail.com>',
    +               f('JOHN\u2E41 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u3001 Doe" <john@gmail.com>',
    +               f('JOHN\u3001 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\uFF0C Doe" <john@gmail.com>',
    +               f('JOHN\uFF0C Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u061B Doe" <john@gmail.com>',
    +               f('JOHN\u061B Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\u1364 Doe" <john@gmail.com>',
    +               f('JOHN\u1364 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\uFF1B Doe" <john@gmail.com>',
    +               f('JOHN\uFF1B Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\uFF64 Doe" <john@gmail.com>',
    +               f('JOHN\uFF64 Doe <john@gmail.com>'));
    +  assertEquals('"JOHN(Johnny) Doe" <john@gmail.com>',
    +               f('JOHN(Johnny) Doe <john@gmail.com>'));
    +  assertEquals('"JOHN[Johnny] Doe" <john@gmail.com>',
    +               f('JOHN[Johnny] Doe <john@gmail.com>'));
    +  assertEquals('"JOHN@work Doe" <john@gmail.com>',
    +               f('JOHN@work Doe <john@gmail.com>'));
    +  assertEquals('"JOHN:theking Doe" <john@gmail.com>',
    +               f('JOHN:theking Doe <john@gmail.com>'));
    +  assertEquals('"JOHN\\\\ Doe" <john@gmail.com>',
    +               f('JOHN\\ Doe <john@gmail.com>'));
    +  assertEquals('"JOHN.com Doe" <john@gmail.com>',
    +               f('JOHN.com Doe <john@gmail.com>'));
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter.js b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter.js
    new file mode 100644
    index 0000000000..15e2cd2aaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter.js
    @@ -0,0 +1,414 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Creates a string of a JSON object, properly indented for
    + * display.
    + *
    + */
    +
    +goog.provide('goog.format.JsonPrettyPrinter');
    +goog.provide('goog.format.JsonPrettyPrinter.HtmlDelimiters');
    +goog.provide('goog.format.JsonPrettyPrinter.TextDelimiters');
    +
    +goog.require('goog.json');
    +goog.require('goog.json.Serializer');
    +goog.require('goog.string');
    +goog.require('goog.string.StringBuffer');
    +goog.require('goog.string.format');
    +
    +
    +
    +/**
    + * Formats a JSON object as a string, properly indented for display.  Supports
    + * displaying the string as text or html.  Users can also specify their own
    + * set of delimiters for different environments.  For example, the JSON object:
    + *
    + * <code>{"a": 1, "b": {"c": null, "d": true, "e": [1, 2]}}</code>
    + *
    + * Will be displayed like this:
    + *
    + * <code>{
    + *   "a": 1,
    + *   "b": {
    + *     "c": null,
    + *     "d": true,
    + *     "e": [
    + *       1,
    + *       2
    + *     ]
    + *   }
    + * }</code>
    + * @param {goog.format.JsonPrettyPrinter.TextDelimiters} delimiters Container
    + *     for the various strings to use to delimit objects, arrays, newlines, and
    + *     other pieces of the output.
    + * @constructor
    + */
    +goog.format.JsonPrettyPrinter = function(delimiters) {
    +
    +  /**
    +   * The set of characters to use as delimiters.
    +   * @type {goog.format.JsonPrettyPrinter.TextDelimiters}
    +   * @private
    +   */
    +  this.delimiters_ = delimiters ||
    +      new goog.format.JsonPrettyPrinter.TextDelimiters();
    +
    +  /**
    +   * Used to serialize property names and values.
    +   * @type {goog.json.Serializer}
    +   * @private
    +   */
    +  this.jsonSerializer_ = new goog.json.Serializer();
    +};
    +
    +
    +/**
    + * Formats a JSON object as a string, properly indented for display.
    + * @param {*} json The object to pretty print. It could be a JSON object, a
    + *     string representing a JSON object, or any other type.
    + * @return {string} Returns a string of the JSON object, properly indented for
    + *     display.
    + */
    +goog.format.JsonPrettyPrinter.prototype.format = function(json) {
    +  // If input is undefined, null, or empty, return an empty string.
    +  if (!goog.isDefAndNotNull(json)) {
    +    return '';
    +  }
    +  if (goog.isString(json)) {
    +    if (goog.string.isEmptyOrWhitespace(json)) {
    +      return '';
    +    }
    +    // Try to coerce a string into a JSON object.
    +    json = goog.json.parse(json);
    +  }
    +  var outputBuffer = new goog.string.StringBuffer();
    +  this.printObject_(json, outputBuffer, 0);
    +  return outputBuffer.toString();
    +};
    +
    +
    +/**
    + * Formats a property value based on the type of the propery.
    + * @param {*} val The object to format.
    + * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
    + *     response to.
    + * @param {number} indent The number of spaces to indent each line of the
    + *     output.
    + * @private
    + */
    +goog.format.JsonPrettyPrinter.prototype.printObject_ = function(val,
    +    outputBuffer, indent) {
    +  var typeOf = goog.typeOf(val);
    +  switch (typeOf) {
    +    case 'null':
    +    case 'boolean':
    +    case 'number':
    +    case 'string':
    +      // "null", "boolean", "number" and "string" properties are printed
    +      // directly to the output.
    +      this.printValue_(
    +          /** @type {null|string|boolean|number} */ (val),
    +          typeOf, outputBuffer);
    +      break;
    +    case 'array':
    +      // Example of how an array looks when formatted
    +      // (using the default delimiters):
    +      // [
    +      //   1,
    +      //   2,
    +      //   3
    +      // ]
    +      outputBuffer.append(this.delimiters_.arrayStart);
    +      var i = 0;
    +      // Iterate through the array and format each element.
    +      for (i = 0; i < val.length; i++) {
    +        if (i > 0) {
    +          // There are multiple elements, add a comma to separate them.
    +          outputBuffer.append(this.delimiters_.propertySeparator);
    +        }
    +        outputBuffer.append(this.delimiters_.lineBreak);
    +        this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
    +        this.printObject_(val[i], outputBuffer,
    +            indent + this.delimiters_.indent);
    +      }
    +      // If there are no properties in this object, don't put a line break
    +      // between the beginning "[" and ending "]", so the output of an empty
    +      // array looks like <code>[]</code>.
    +      if (i > 0) {
    +        outputBuffer.append(this.delimiters_.lineBreak);
    +        this.printSpaces_(indent, outputBuffer);
    +      }
    +      outputBuffer.append(this.delimiters_.arrayEnd);
    +      break;
    +    case 'object':
    +      // Example of how an object looks when formatted
    +      // (using the default delimiters):
    +      // {
    +      //   "a": 1,
    +      //   "b": 2,
    +      //   "c": "3"
    +      // }
    +      outputBuffer.append(this.delimiters_.objectStart);
    +      var propertyCount = 0;
    +      // Iterate through the object and display each property.
    +      for (var name in val) {
    +        if (!val.hasOwnProperty(name)) {
    +          continue;
    +        }
    +        if (propertyCount > 0) {
    +          // There are multiple properties, add a comma to separate them.
    +          outputBuffer.append(this.delimiters_.propertySeparator);
    +        }
    +        outputBuffer.append(this.delimiters_.lineBreak);
    +        this.printSpaces_(indent + this.delimiters_.indent, outputBuffer);
    +        this.printName_(name, outputBuffer);
    +        outputBuffer.append(this.delimiters_.nameValueSeparator,
    +            this.delimiters_.space);
    +        this.printObject_(val[name], outputBuffer,
    +            indent + this.delimiters_.indent);
    +        propertyCount++;
    +      }
    +      // If there are no properties in this object, don't put a line break
    +      // between the beginning "{" and ending "}", so the output of an empty
    +      // object looks like <code>{}</code>.
    +      if (propertyCount > 0) {
    +        outputBuffer.append(this.delimiters_.lineBreak);
    +        this.printSpaces_(indent, outputBuffer);
    +      }
    +      outputBuffer.append(this.delimiters_.objectEnd);
    +      break;
    +    // Other types, such as "function", aren't expected in JSON, and their
    +    // behavior is undefined.  In these cases, just print an empty string to the
    +    // output buffer.  This allows the pretty printer to continue while still
    +    // outputing well-formed JSON.
    +    default:
    +      this.printValue_('', 'unknown', outputBuffer);
    +  }
    +};
    +
    +
    +/**
    + * Prints a property name to the output.
    + * @param {string} name The property name.
    + * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
    + *     response to.
    + * @private
    + */
    +goog.format.JsonPrettyPrinter.prototype.printName_ = function(name,
    +    outputBuffer) {
    +  outputBuffer.append(this.delimiters_.preName,
    +      this.jsonSerializer_.serialize(name), this.delimiters_.postName);
    +};
    +
    +
    +/**
    + * Prints a property name to the output.
    + * @param {string|boolean|number|null} val The property value.
    + * @param {string} typeOf The type of the value.  Used to customize
    + *     value-specific css in the display.  This allows clients to distinguish
    + *     between different types in css.  For example, the client may define two
    + *     classes: "goog-jsonprettyprinter-propertyvalue-string" and
    + *     "goog-jsonprettyprinter-propertyvalue-number" to assign a different color
    + *     to string and number values.
    + * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
    + *     response to.
    + * @private
    + */
    +goog.format.JsonPrettyPrinter.prototype.printValue_ = function(val,
    +    typeOf, outputBuffer) {
    +  outputBuffer.append(goog.string.format(this.delimiters_.preValue, typeOf),
    +      this.jsonSerializer_.serialize(val),
    +      goog.string.format(this.delimiters_.postValue, typeOf));
    +};
    +
    +
    +/**
    + * Print a number of space characters to the output.
    + * @param {number} indent The number of spaces to indent the line.
    + * @param {goog.string.StringBuffer} outputBuffer The buffer to write the
    + *     response to.
    + * @private
    + */
    +goog.format.JsonPrettyPrinter.prototype.printSpaces_ = function(indent,
    +    outputBuffer) {
    +  outputBuffer.append(goog.string.repeat(this.delimiters_.space, indent));
    +};
    +
    +
    +
    +/**
    + * A container for the delimiting characters used to display the JSON string
    + * to a text display.  Each delimiter is a publicly accessible property of
    + * the object, which makes it easy to tweak delimiters to specific environments.
    + * @constructor
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters = function() {
    +};
    +
    +
    +/**
    + * Represents a space character in the output.  Used to indent properties a
    + * certain number of spaces, and to separate property names from property
    + * values.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.space = ' ';
    +
    +
    +/**
    + * Represents a newline character in the output.  Used to begin a new line.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.lineBreak = '\n';
    +
    +
    +/**
    + * Represents the start of an object in the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectStart = '{';
    +
    +
    +/**
    + * Represents the end of an object in the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.objectEnd = '}';
    +
    +
    +/**
    + * Represents the start of an array in the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayStart = '[';
    +
    +
    +/**
    + * Represents the end of an array in the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.arrayEnd = ']';
    +
    +
    +/**
    + * Represents the string used to separate properties in the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.propertySeparator = ',';
    +
    +
    +/**
    + * Represents the string used to separate property names from property values in
    + * the output.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.nameValueSeparator = ':';
    +
    +
    +/**
    + * A string that's placed before a property name in the output.  Useful for
    + * wrapping a property name in an html tag.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preName = '';
    +
    +
    +/**
    + * A string that's placed after a property name in the output.  Useful for
    + * wrapping a property name in an html tag.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postName = '';
    +
    +
    +/**
    + * A string that's placed before a property value in the output.  Useful for
    + * wrapping a property value in an html tag.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.preValue = '';
    +
    +
    +/**
    + * A string that's placed after a property value in the output.  Useful for
    + * wrapping a property value in an html tag.
    + * @type {string}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.postValue = '';
    +
    +
    +/**
    + * Represents the number of spaces to indent each sub-property of the JSON.
    + * @type {number}
    + */
    +goog.format.JsonPrettyPrinter.TextDelimiters.prototype.indent = 2;
    +
    +
    +
    +/**
    + * A container for the delimiting characters used to display the JSON string
    + * to an HTML <code>&lt;pre&gt;</code> or <code>&lt;code&gt;</code> element.
    + * @constructor
    + * @extends {goog.format.JsonPrettyPrinter.TextDelimiters}
    + * @final
    + */
    +goog.format.JsonPrettyPrinter.HtmlDelimiters = function() {
    +  goog.format.JsonPrettyPrinter.TextDelimiters.call(this);
    +};
    +goog.inherits(goog.format.JsonPrettyPrinter.HtmlDelimiters,
    +    goog.format.JsonPrettyPrinter.TextDelimiters);
    +
    +
    +/**
    + * A <code>span</code> tag thats placed before a property name.  Used to style
    + * property names with CSS.
    + * @type {string}
    + * @override
    + */
    +goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preName =
    +    '<span class="' +
    +    goog.getCssName('goog-jsonprettyprinter-propertyname') +
    +    '">';
    +
    +
    +/**
    + * A closing <code>span</code> tag that's placed after a property name.
    + * @type {string}
    + * @override
    + */
    +goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postName = '</span>';
    +
    +
    +/**
    + * A <code>span</code> tag thats placed before a property value.  Used to style
    + * property value with CSS.  The span tag's class is in the format
    + * goog-jsonprettyprinter-propertyvalue-{TYPE}, where {TYPE} is the JavaScript
    + * type of the object (the {TYPE} parameter is obtained from goog.typeOf).  This
    + * can be used to style different value types.
    + * @type {string}
    + * @override
    + */
    +goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.preValue =
    +    '<span class="' +
    +    goog.getCssName('goog-jsonprettyprinter-propertyvalue') +
    +    '-%s">';
    +
    +
    +/**
    + * A closing <code>span</code> tag that's placed after a property value.
    + * @type {string}
    + * @override
    + */
    +goog.format.JsonPrettyPrinter.HtmlDelimiters.prototype.postValue = '</span>';
    diff --git a/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.html b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.html
    new file mode 100644
    index 0000000000..4b2d6667e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.format.JsonPrettyPrinter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.format.JsonPrettyPrinterTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.js b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.js
    new file mode 100644
    index 0000000000..ae308cc9bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/format/jsonprettyprinter_test.js
    @@ -0,0 +1,109 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.format.JsonPrettyPrinterTest');
    +goog.setTestOnly('goog.format.JsonPrettyPrinterTest');
    +
    +goog.require('goog.format.JsonPrettyPrinter');
    +goog.require('goog.testing.jsunit');
    +
    +var formatter;
    +
    +
    +function setUp() {
    +  formatter = new goog.format.JsonPrettyPrinter();
    +}
    +
    +
    +function testUndefined() {
    +  assertEquals('', formatter.format());
    +}
    +
    +
    +function testNull() {
    +  assertEquals('', formatter.format(null));
    +}
    +
    +
    +function testBoolean() {
    +  assertEquals('true', formatter.format(true));
    +}
    +
    +
    +function testNumber() {
    +  assertEquals('1', formatter.format(1));
    +}
    +
    +
    +function testEmptyString() {
    +  assertEquals('', formatter.format(''));
    +}
    +
    +
    +function testWhitespaceString() {
    +  assertEquals('', formatter.format('   '));
    +}
    +
    +
    +function testString() {
    +  assertEquals('{}', formatter.format('{}'));
    +}
    +
    +
    +function testEmptyArray() {
    +  assertEquals('[]', formatter.format([]));
    +}
    +
    +
    +function testArrayOneElement() {
    +  assertEquals('[\n  1\n]', formatter.format([1]));
    +}
    +
    +
    +function testArrayMultipleElements() {
    +  assertEquals('[\n  1,\n  2,\n  3\n]', formatter.format([1, 2, 3]));
    +}
    +
    +
    +function testFunction() {
    +  assertEquals('{\n  "a": "1",\n  "b": ""\n}',
    +      formatter.format({'a': '1', 'b': function() { return null; }}));
    +}
    +
    +
    +function testObject() {
    +  assertEquals('{}', formatter.format({}));
    +}
    +
    +
    +function testObjectMultipleProperties() {
    +  assertEquals('{\n  "a": null,\n  "b": true,\n  "c": 1,\n  "d": "d",\n  "e":' +
    +      ' [\n    1,\n    2,\n    3\n  ],\n  "f": {\n    "g": 1,\n    "h": "h"\n' +
    +      '  }\n}',
    +      formatter.format({'a': null, 'b': true, 'c': 1, 'd': 'd', 'e': [1, 2, 3],
    +        'f': {'g': 1, 'h': 'h'}}));
    +}
    +
    +
    +function testHtmlDelimiters() {
    +  var htmlFormatter = new goog.format.JsonPrettyPrinter(
    +      new goog.format.JsonPrettyPrinter.HtmlDelimiters());
    +  assertEquals('{\n  <span class="goog-jsonprettyprinter-propertyname">"a"</s' +
    +      'pan>: <span class="goog-jsonprettyprinter-propertyvalue-number">1</spa' +
    +      'n>,\n  <span class="goog-jsonprettyprinter-propertyname">"b"</span>: <' +
    +      'span class="goog-jsonprettyprinter-propertyvalue-string">"2"</span>,\n' +
    +      '  <span class="goog-jsonprettyprinter-propertyname">"c"</span>: <span ' +
    +      'class="goog-jsonprettyprinter-propertyvalue-unknown">""</span>\n}',
    +      htmlFormatter.format({'a': 1, 'b': '2', 'c': function() {}}));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/entry.js b/mapbox-gl/closure-library/closure/goog/fs/entry.js
    new file mode 100644
    index 0000000000..8143daa28d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/entry.js
    @@ -0,0 +1,272 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wrappers for HTML5 Entry objects. These are all in the same
    + * file to avoid circular dependency issues.
    + *
    + * When adding or modifying functionality in this namespace, be sure to update
    + * the mock counterparts in goog.testing.fs.
    + *
    + */
    +goog.provide('goog.fs.DirectoryEntry');
    +goog.provide('goog.fs.DirectoryEntry.Behavior');
    +goog.provide('goog.fs.Entry');
    +goog.provide('goog.fs.FileEntry');
    +
    +
    +
    +/**
    + * The interface for entries in the filesystem.
    + * @interface
    + */
    +goog.fs.Entry = function() {};
    +
    +
    +/**
    + * @return {boolean} Whether or not this entry is a file.
    + */
    +goog.fs.Entry.prototype.isFile = function() {};
    +
    +
    +/**
    + * @return {boolean} Whether or not this entry is a directory.
    + */
    +goog.fs.Entry.prototype.isDirectory = function() {};
    +
    +
    +/**
    + * @return {string} The name of this entry.
    + */
    +goog.fs.Entry.prototype.getName = function() {};
    +
    +
    +/**
    + * @return {string} The full path to this entry.
    + */
    +goog.fs.Entry.prototype.getFullPath = function() {};
    +
    +
    +/**
    + * @return {!goog.fs.FileSystem} The filesystem backing this entry.
    + */
    +goog.fs.Entry.prototype.getFileSystem = function() {};
    +
    +
    +/**
    + * Retrieves the last modified date for this entry.
    + *
    + * @return {!goog.async.Deferred} The deferred Date for this entry. If an error
    + *     occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.getLastModified = function() {};
    +
    +
    +/**
    + * Retrieves the metadata for this entry.
    + *
    + * @return {!goog.async.Deferred} The deferred Metadata for this entry. If an
    + *     error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.getMetadata = function() {};
    +
    +
    +/**
    + * Move this entry to a new location.
    + *
    + * @param {!goog.fs.DirectoryEntry} parent The new parent directory.
    + * @param {string=} opt_newName The new name of the entry. If omitted, the entry
    + *     retains its original name.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry} or
    + *     {@link goog.fs.DirectoryEntry} for the new entry. If an error occurs, the
    + *     errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.moveTo = function(parent, opt_newName) {};
    +
    +
    +/**
    + * Copy this entry to a new location.
    + *
    + * @param {!goog.fs.DirectoryEntry} parent The new parent directory.
    + * @param {string=} opt_newName The name of the new entry. If omitted, the new
    + *     entry has the same name as the original.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry} or
    + *     {@link goog.fs.DirectoryEntry} for the new entry. If an error occurs, the
    + *     errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.copyTo = function(parent, opt_newName) {};
    +
    +
    +/**
    + * Wrap an HTML5 entry object in an appropriate subclass instance.
    + *
    + * @param {!Entry} entry The underlying Entry object.
    + * @return {!goog.fs.Entry} The appropriate subclass wrapper.
    + * @protected
    + */
    +goog.fs.Entry.prototype.wrapEntry = function(entry) {};
    +
    +
    +/**
    + * Get the URL for this file.
    + *
    + * @param {string=} opt_mimeType The MIME type that will be served for the URL.
    + * @return {string} The URL.
    + */
    +goog.fs.Entry.prototype.toUrl = function(opt_mimeType) {};
    +
    +
    +/**
    + * Get the URI for this file.
    + *
    + * @deprecated Use {@link #toUrl} instead.
    + * @param {string=} opt_mimeType The MIME type that will be served for the URI.
    + * @return {string} The URI.
    + */
    +goog.fs.Entry.prototype.toUri = function(opt_mimeType) {};
    +
    +
    +/**
    + * Remove this entry.
    + *
    + * @return {!goog.async.Deferred} A deferred object. If the removal succeeds,
    + *     the callback is called with true. If an error occurs, the errback is
    + *     called a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.remove = function() {};
    +
    +
    +/**
    + * Gets the parent directory.
    + *
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.DirectoryEntry}.
    + *     If an error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.Entry.prototype.getParent = function() {};
    +
    +
    +
    +/**
    + * A directory in a local FileSystem.
    + *
    + * @interface
    + * @extends {goog.fs.Entry}
    + */
    +goog.fs.DirectoryEntry = function() {};
    +
    +
    +/**
    + * Behaviors for getting files and directories.
    + * @enum {number}
    + */
    +goog.fs.DirectoryEntry.Behavior = {
    +  /**
    +   * Get the file if it exists, error out if it doesn't.
    +   */
    +  DEFAULT: 1,
    +  /**
    +   * Get the file if it exists, create it if it doesn't.
    +   */
    +  CREATE: 2,
    +  /**
    +   * Error out if the file exists, create it if it doesn't.
    +   */
    +  CREATE_EXCLUSIVE: 3
    +};
    +
    +
    +/**
    + * Get a file in the directory.
    + *
    + * @param {string} path The path to the file, relative to this directory.
    + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
    + *     handling an existing file, or the lack thereof.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileEntry}. If an
    + *     error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.DirectoryEntry.prototype.getFile = function(path, opt_behavior) {};
    +
    +
    +/**
    + * Get a directory within this directory.
    + *
    + * @param {string} path The path to the directory, relative to this directory.
    + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
    + *     handling an existing directory, or the lack thereof.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.DirectoryEntry}.
    + *     If an error occurs, the errback is called a {@link goog.fs.Error}.
    + */
    +goog.fs.DirectoryEntry.prototype.getDirectory = function(path, opt_behavior) {};
    +
    +
    +/**
    + * Opens the directory for the specified path, creating the directory and any
    + * intermediate directories as necessary.
    + *
    + * @param {string} path The directory path to create. May be absolute or
    + *     relative to the current directory. The parent directory ".." and current
    + *     directory "." are supported.
    + * @return {!goog.async.Deferred} A deferred {@link goog.fs.DirectoryEntry} for
    + *     the requested path. If an error occurs, the errback is called with a
    + *     {@link goog.fs.Error}.
    + */
    +goog.fs.DirectoryEntry.prototype.createPath = function(path) {};
    +
    +
    +/**
    + * Gets a list of all entries in this directory.
    + *
    + * @return {!goog.async.Deferred} The deferred list of {@link goog.fs.Entry}
    + *     results. If an error occurs, the errback is called with a
    + *     {@link goog.fs.Error}.
    + */
    +goog.fs.DirectoryEntry.prototype.listDirectory = function() {};
    +
    +
    +/**
    + * Removes this directory and all its contents.
    + *
    + * @return {!goog.async.Deferred} A deferred object. If the removal succeeds,
    + *     the callback is called with true. If an error occurs, the errback is
    + *     called a {@link goog.fs.Error}.
    + */
    +goog.fs.DirectoryEntry.prototype.removeRecursively = function() {};
    +
    +
    +
    +/**
    + * A file in a local filesystem.
    + *
    + * @interface
    + * @extends {goog.fs.Entry}
    + */
    +goog.fs.FileEntry = function() {};
    +
    +
    +/**
    + * Create a writer for writing to the file.
    + *
    + * @return {!goog.async.Deferred<!goog.fs.FileWriter>} If an error occurs, the
    + *     errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileEntry.prototype.createWriter = function() {};
    +
    +
    +/**
    + * Get the file contents as a File blob.
    + *
    + * @return {!goog.async.Deferred<!File>} If an error occurs, the errback is
    + *     called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileEntry.prototype.file = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/entryimpl.js b/mapbox-gl/closure-library/closure/goog/fs/entryimpl.js
    new file mode 100644
    index 0000000000..7f48bbaeac
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/entryimpl.js
    @@ -0,0 +1,405 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Concrete implementations of the
    + *     goog.fs.DirectoryEntry, and goog.fs.FileEntry interfaces.
    + */
    +goog.provide('goog.fs.DirectoryEntryImpl');
    +goog.provide('goog.fs.EntryImpl');
    +goog.provide('goog.fs.FileEntryImpl');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Entry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileEntry');
    +goog.require('goog.fs.FileWriter');
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Base class for concrete implementations of goog.fs.Entry.
    + * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
    + * @param {!Entry} entry The underlying Entry object.
    + * @constructor
    + * @implements {goog.fs.Entry}
    + */
    +goog.fs.EntryImpl = function(fs, entry) {
    +  /**
    +   * The wrapped filesystem.
    +   *
    +   * @type {!goog.fs.FileSystem}
    +   * @private
    +   */
    +  this.fs_ = fs;
    +
    +  /**
    +   * The underlying Entry object.
    +   *
    +   * @type {!Entry}
    +   * @private
    +   */
    +  this.entry_ = entry;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.isFile = function() {
    +  return this.entry_.isFile;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.isDirectory = function() {
    +  return this.entry_.isDirectory;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getName = function() {
    +  return this.entry_.name;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getFullPath = function() {
    +  return this.entry_.fullPath;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getFileSystem = function() {
    +  return this.fs_;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getLastModified = function() {
    +  return this.getMetadata().addCallback(function(metadata) {
    +    return metadata.modificationTime;
    +  });
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getMetadata = function() {
    +  var d = new goog.async.Deferred();
    +
    +  this.entry_.getMetadata(
    +      function(metadata) { d.callback(metadata); },
    +      goog.bind(function(err) {
    +        var msg = 'retrieving metadata for ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.moveTo = function(parent, opt_newName) {
    +  var d = new goog.async.Deferred();
    +  this.entry_.moveTo(
    +      parent.dir_, opt_newName,
    +      goog.bind(function(entry) { d.callback(this.wrapEntry(entry)); }, this),
    +      goog.bind(function(err) {
    +        var msg = 'moving ' + this.getFullPath() + ' into ' +
    +            parent.getFullPath() +
    +            (opt_newName ? ', renaming to ' + opt_newName : '');
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.copyTo = function(parent, opt_newName) {
    +  var d = new goog.async.Deferred();
    +  this.entry_.copyTo(
    +      parent.dir_, opt_newName,
    +      goog.bind(function(entry) { d.callback(this.wrapEntry(entry)); }, this),
    +      goog.bind(function(err) {
    +        var msg = 'copying ' + this.getFullPath() + ' into ' +
    +            parent.getFullPath() +
    +            (opt_newName ? ', renaming to ' + opt_newName : '');
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.wrapEntry = function(entry) {
    +  return entry.isFile ?
    +      new goog.fs.FileEntryImpl(this.fs_, /** @type {!FileEntry} */ (entry)) :
    +      new goog.fs.DirectoryEntryImpl(
    +          this.fs_, /** @type {!DirectoryEntry} */ (entry));
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.toUrl = function(opt_mimeType) {
    +  return this.entry_.toURL(opt_mimeType);
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.toUri = goog.fs.EntryImpl.prototype.toUrl;
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.remove = function() {
    +  var d = new goog.async.Deferred();
    +  this.entry_.remove(
    +      goog.bind(d.callback, d, true /* result */),
    +      goog.bind(function(err) {
    +        var msg = 'removing ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.EntryImpl.prototype.getParent = function() {
    +  var d = new goog.async.Deferred();
    +  this.entry_.getParent(
    +      goog.bind(function(parent) {
    +        d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, parent));
    +      }, this),
    +      goog.bind(function(err) {
    +        var msg = 'getting parent of ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +
    +/**
    + * A directory in a local FileSystem.
    + *
    + * This should not be instantiated directly. Instead, it should be accessed via
    + * {@link goog.fs.FileSystem#getRoot} or
    + * {@link goog.fs.DirectoryEntry#getDirectoryEntry}.
    + *
    + * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
    + * @param {!DirectoryEntry} dir The underlying DirectoryEntry object.
    + * @constructor
    + * @extends {goog.fs.EntryImpl}
    + * @implements {goog.fs.DirectoryEntry}
    + * @final
    + */
    +goog.fs.DirectoryEntryImpl = function(fs, dir) {
    +  goog.fs.DirectoryEntryImpl.base(this, 'constructor', fs, dir);
    +
    +  /**
    +   * The underlying DirectoryEntry object.
    +   *
    +   * @type {!DirectoryEntry}
    +   * @private
    +   */
    +  this.dir_ = dir;
    +};
    +goog.inherits(goog.fs.DirectoryEntryImpl, goog.fs.EntryImpl);
    +
    +
    +/** @override */
    +goog.fs.DirectoryEntryImpl.prototype.getFile = function(path, opt_behavior) {
    +  var d = new goog.async.Deferred();
    +  this.dir_.getFile(
    +      path, this.getOptions_(opt_behavior),
    +      goog.bind(function(entry) {
    +        d.callback(new goog.fs.FileEntryImpl(this.fs_, entry));
    +      }, this),
    +      goog.bind(function(err) {
    +        var msg = 'loading file ' + path + ' from ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.DirectoryEntryImpl.prototype.getDirectory =
    +    function(path, opt_behavior) {
    +  var d = new goog.async.Deferred();
    +  this.dir_.getDirectory(
    +      path, this.getOptions_(opt_behavior),
    +      goog.bind(function(entry) {
    +        d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, entry));
    +      }, this),
    +      goog.bind(function(err) {
    +        var msg = 'loading directory ' + path + ' from ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.DirectoryEntryImpl.prototype.createPath = function(path) {
    +  // If the path begins at the root, reinvoke createPath on the root directory.
    +  if (goog.string.startsWith(path, '/')) {
    +    var root = this.getFileSystem().getRoot();
    +    if (this.getFullPath() != root.getFullPath()) {
    +      return root.createPath(path);
    +    }
    +  }
    +
    +  // Filter out any empty path components caused by '//' or a leading slash.
    +  var parts = goog.array.filter(path.split('/'), goog.functions.identity);
    +  var existed = [];
    +
    +  /**
    +   * @param {goog.fs.DirectoryEntryImpl} dir
    +   * @return {!goog.async.Deferred}
    +   */
    +  function getNextDirectory(dir) {
    +    if (!parts.length) {
    +      return goog.async.Deferred.succeed(dir);
    +    }
    +
    +    var def;
    +    var nextDir = parts.shift();
    +
    +    if (nextDir == '..') {
    +      def = dir.getParent();
    +    } else if (nextDir == '.') {
    +      def = goog.async.Deferred.succeed(dir);
    +    } else {
    +      def = dir.getDirectory(nextDir, goog.fs.DirectoryEntry.Behavior.CREATE);
    +    }
    +    return def.addCallback(getNextDirectory);
    +  }
    +
    +  return getNextDirectory(this);
    +};
    +
    +
    +/** @override */
    +goog.fs.DirectoryEntryImpl.prototype.listDirectory = function() {
    +  var d = new goog.async.Deferred();
    +  var reader = this.dir_.createReader();
    +  var results = [];
    +
    +  var errorCallback = goog.bind(function(err) {
    +    var msg = 'listing directory ' + this.getFullPath();
    +    d.errback(new goog.fs.Error(err, msg));
    +  }, this);
    +
    +  var successCallback = goog.bind(function(entries) {
    +    if (entries.length) {
    +      for (var i = 0, entry; entry = entries[i]; i++) {
    +        results.push(this.wrapEntry(entry));
    +      }
    +      reader.readEntries(successCallback, errorCallback);
    +    } else {
    +      d.callback(results);
    +    }
    +  }, this);
    +
    +  reader.readEntries(successCallback, errorCallback);
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.DirectoryEntryImpl.prototype.removeRecursively = function() {
    +  var d = new goog.async.Deferred();
    +  this.dir_.removeRecursively(
    +      goog.bind(d.callback, d, true /* result */),
    +      goog.bind(function(err) {
    +        var msg = 'removing ' + this.getFullPath() + ' recursively';
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/**
    + * Converts a value in the Behavior enum into an options object expected by the
    + * File API.
    + *
    + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
    + *     existing files.
    + * @return {!Object<boolean>} The options object expected by the File API.
    + * @private
    + */
    +goog.fs.DirectoryEntryImpl.prototype.getOptions_ = function(opt_behavior) {
    +  if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE) {
    +    return {'create': true};
    +  } else if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) {
    +    return {'create': true, 'exclusive': true};
    +  } else {
    +    return {};
    +  }
    +};
    +
    +
    +
    +/**
    + * A file in a local filesystem.
    + *
    + * This should not be instantiated directly. Instead, it should be accessed via
    + * {@link goog.fs.DirectoryEntry#getFile}.
    + *
    + * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
    + * @param {!FileEntry} file The underlying FileEntry object.
    + * @constructor
    + * @extends {goog.fs.EntryImpl}
    + * @implements {goog.fs.FileEntry}
    + * @final
    + */
    +goog.fs.FileEntryImpl = function(fs, file) {
    +  goog.fs.FileEntryImpl.base(this, 'constructor', fs, file);
    +
    +  /**
    +   * The underlying FileEntry object.
    +   *
    +   * @type {!FileEntry}
    +   * @private
    +   */
    +  this.file_ = file;
    +};
    +goog.inherits(goog.fs.FileEntryImpl, goog.fs.EntryImpl);
    +
    +
    +/** @override */
    +goog.fs.FileEntryImpl.prototype.createWriter = function() {
    +  var d = new goog.async.Deferred();
    +  this.file_.createWriter(
    +      function(w) { d.callback(new goog.fs.FileWriter(w)); },
    +      goog.bind(function(err) {
    +        var msg = 'creating writer for ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.fs.FileEntryImpl.prototype.file = function() {
    +  var d = new goog.async.Deferred();
    +  this.file_.file(
    +      function(f) { d.callback(f); },
    +      goog.bind(function(err) {
    +        var msg = 'getting file for ' + this.getFullPath();
    +        d.errback(new goog.fs.Error(err, msg));
    +      }, this));
    +  return d;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/error.js b/mapbox-gl/closure-library/closure/goog/fs/error.js
    new file mode 100644
    index 0000000000..3a54f28084
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/error.js
    @@ -0,0 +1,181 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 FileError object.
    + *
    + */
    +
    +goog.provide('goog.fs.Error');
    +goog.provide('goog.fs.Error.ErrorCode');
    +
    +goog.require('goog.debug.Error');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * A filesystem error. Since the filesystem API is asynchronous, stack traces
    + * are less useful for identifying where errors come from, so this includes a
    + * large amount of metadata in the message.
    + *
    + * @param {!DOMError} error
    + * @param {string} action The action being undertaken when the error was raised.
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.fs.Error = function(error, action) {
    +  /** @type {string} */
    +  this.name;
    +
    +  /**
    +   * @type {goog.fs.Error.ErrorCode}
    +   * @deprecated Use the 'name' or 'message' field instead.
    +   */
    +  this.code;
    +
    +  if (goog.isDef(error.name)) {
    +    this.name = error.name;
    +    // TODO(user): Remove warning suppression after JSCompiler stops
    +    // firing a spurious warning here.
    +    /** @suppress {deprecated} */
    +    this.code = goog.fs.Error.getCodeFromName_(error.name);
    +  } else {
    +    this.code = error.code;
    +    this.name = goog.fs.Error.getNameFromCode_(error.code);
    +  }
    +  goog.fs.Error.base(this, 'constructor',
    +      goog.string.subs('%s %s', this.name, action));
    +};
    +goog.inherits(goog.fs.Error, goog.debug.Error);
    +
    +
    +/**
    + * Names of errors that may be thrown by the File API, the File System API, or
    + * the File Writer API.
    + *
    + * @see http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException
    + * @see http://www.w3.org/TR/file-system-api/#definitions
    + * @see http://dev.w3.org/2009/dap/file-system/file-writer.html#definitions
    + * @enum {string}
    + */
    +goog.fs.Error.ErrorName = {
    +  ABORT: 'AbortError',
    +  ENCODING: 'EncodingError',
    +  INVALID_MODIFICATION: 'InvalidModificationError',
    +  INVALID_STATE: 'InvalidStateError',
    +  NOT_FOUND: 'NotFoundError',
    +  NOT_READABLE: 'NotReadableError',
    +  NO_MODIFICATION_ALLOWED: 'NoModificationAllowedError',
    +  PATH_EXISTS: 'PathExistsError',
    +  QUOTA_EXCEEDED: 'QuotaExceededError',
    +  SECURITY: 'SecurityError',
    +  SYNTAX: 'SyntaxError',
    +  TYPE_MISMATCH: 'TypeMismatchError'
    +};
    +
    +
    +/**
    + * Error codes for file errors.
    + * @see http://www.w3.org/TR/file-system-api/#idl-def-FileException
    + *
    + * @enum {number}
    + * @deprecated Use the 'name' or 'message' attribute instead.
    + */
    +goog.fs.Error.ErrorCode = {
    +  NOT_FOUND: 1,
    +  SECURITY: 2,
    +  ABORT: 3,
    +  NOT_READABLE: 4,
    +  ENCODING: 5,
    +  NO_MODIFICATION_ALLOWED: 6,
    +  INVALID_STATE: 7,
    +  SYNTAX: 8,
    +  INVALID_MODIFICATION: 9,
    +  QUOTA_EXCEEDED: 10,
    +  TYPE_MISMATCH: 11,
    +  PATH_EXISTS: 12
    +};
    +
    +
    +/**
    + * @param {goog.fs.Error.ErrorCode} code
    + * @return {string} name
    + * @private
    + */
    +goog.fs.Error.getNameFromCode_ = function(code) {
    +  var name = goog.object.findKey(goog.fs.Error.NameToCodeMap_, function(c) {
    +    return code == c;
    +  });
    +  if (!goog.isDef(name)) {
    +    throw new Error('Invalid code: ' + code);
    +  }
    +  return name;
    +};
    +
    +
    +/**
    + * Returns the code that corresponds to the given name.
    + * @param {string} name
    + * @return {goog.fs.Error.ErrorCode} code
    + * @private
    + */
    +goog.fs.Error.getCodeFromName_ = function(name) {
    +  return goog.fs.Error.NameToCodeMap_[name];
    +};
    +
    +
    +/**
    + * Mapping from error names to values from the ErrorCode enum.
    + * @see http://www.w3.org/TR/file-system-api/#definitions.
    + * @private {!Object<string, goog.fs.Error.ErrorCode>}
    + */
    +goog.fs.Error.NameToCodeMap_ = goog.object.create(
    +    goog.fs.Error.ErrorName.ABORT,
    +    goog.fs.Error.ErrorCode.ABORT,
    +
    +    goog.fs.Error.ErrorName.ENCODING,
    +    goog.fs.Error.ErrorCode.ENCODING,
    +
    +    goog.fs.Error.ErrorName.INVALID_MODIFICATION,
    +    goog.fs.Error.ErrorCode.INVALID_MODIFICATION,
    +
    +    goog.fs.Error.ErrorName.INVALID_STATE,
    +    goog.fs.Error.ErrorCode.INVALID_STATE,
    +
    +    goog.fs.Error.ErrorName.NOT_FOUND,
    +    goog.fs.Error.ErrorCode.NOT_FOUND,
    +
    +    goog.fs.Error.ErrorName.NOT_READABLE,
    +    goog.fs.Error.ErrorCode.NOT_READABLE,
    +
    +    goog.fs.Error.ErrorName.NO_MODIFICATION_ALLOWED,
    +    goog.fs.Error.ErrorCode.NO_MODIFICATION_ALLOWED,
    +
    +    goog.fs.Error.ErrorName.PATH_EXISTS,
    +    goog.fs.Error.ErrorCode.PATH_EXISTS,
    +
    +    goog.fs.Error.ErrorName.QUOTA_EXCEEDED,
    +    goog.fs.Error.ErrorCode.QUOTA_EXCEEDED,
    +
    +    goog.fs.Error.ErrorName.SECURITY,
    +    goog.fs.Error.ErrorCode.SECURITY,
    +
    +    goog.fs.Error.ErrorName.SYNTAX,
    +    goog.fs.Error.ErrorCode.SYNTAX,
    +
    +    goog.fs.Error.ErrorName.TYPE_MISMATCH,
    +    goog.fs.Error.ErrorCode.TYPE_MISMATCH);
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/filereader.js b/mapbox-gl/closure-library/closure/goog/fs/filereader.js
    new file mode 100644
    index 0000000000..14d5245f35
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/filereader.js
    @@ -0,0 +1,288 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 FileReader object.
    + *
    + */
    +
    +goog.provide('goog.fs.FileReader');
    +goog.provide('goog.fs.FileReader.EventType');
    +goog.provide('goog.fs.FileReader.ReadyState');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.ProgressEvent');
    +
    +
    +
    +/**
    + * An object for monitoring the reading of files. This emits ProgressEvents of
    + * the types listed in {@link goog.fs.FileReader.EventType}.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.fs.FileReader = function() {
    +  goog.fs.FileReader.base(this, 'constructor');
    +
    +  /**
    +   * The underlying FileReader object.
    +   *
    +   * @type {!FileReader}
    +   * @private
    +   */
    +  this.reader_ = new FileReader();
    +
    +  this.reader_.onloadstart = goog.bind(this.dispatchProgressEvent_, this);
    +  this.reader_.onprogress = goog.bind(this.dispatchProgressEvent_, this);
    +  this.reader_.onload = goog.bind(this.dispatchProgressEvent_, this);
    +  this.reader_.onabort = goog.bind(this.dispatchProgressEvent_, this);
    +  this.reader_.onerror = goog.bind(this.dispatchProgressEvent_, this);
    +  this.reader_.onloadend = goog.bind(this.dispatchProgressEvent_, this);
    +};
    +goog.inherits(goog.fs.FileReader, goog.events.EventTarget);
    +
    +
    +/**
    + * Possible states for a FileReader.
    + *
    + * @enum {number}
    + */
    +goog.fs.FileReader.ReadyState = {
    +  /**
    +   * The object has been constructed, but there is no pending read.
    +   */
    +  INIT: 0,
    +  /**
    +   * Data is being read.
    +   */
    +  LOADING: 1,
    +  /**
    +   * The data has been read from the file, the read was aborted, or an error
    +   * occurred.
    +   */
    +  DONE: 2
    +};
    +
    +
    +/**
    + * Events emitted by a FileReader.
    + *
    + * @enum {string}
    + */
    +goog.fs.FileReader.EventType = {
    +  /**
    +   * Emitted when the reading begins. readyState will be LOADING.
    +   */
    +  LOAD_START: 'loadstart',
    +  /**
    +   * Emitted when progress has been made in reading the file. readyState will be
    +   * LOADING.
    +   */
    +  PROGRESS: 'progress',
    +  /**
    +   * Emitted when the data has been successfully read. readyState will be
    +   * LOADING.
    +   */
    +  LOAD: 'load',
    +  /**
    +   * Emitted when the reading has been aborted. readyState will be LOADING.
    +   */
    +  ABORT: 'abort',
    +  /**
    +   * Emitted when an error is encountered or the reading has been aborted.
    +   * readyState will be LOADING.
    +   */
    +  ERROR: 'error',
    +  /**
    +   * Emitted when the reading is finished, whether successfully or not.
    +   * readyState will be DONE.
    +   */
    +  LOAD_END: 'loadend'
    +};
    +
    +
    +/**
    + * Abort the reading of the file.
    + */
    +goog.fs.FileReader.prototype.abort = function() {
    +  try {
    +    this.reader_.abort();
    +  } catch (e) {
    +    throw new goog.fs.Error(e, 'aborting read');
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.fs.FileReader.ReadyState} The current state of the FileReader.
    + */
    +goog.fs.FileReader.prototype.getReadyState = function() {
    +  return /** @type {goog.fs.FileReader.ReadyState} */ (this.reader_.readyState);
    +};
    +
    +
    +/**
    + * @return {*} The result of the file read.
    + */
    +goog.fs.FileReader.prototype.getResult = function() {
    +  return this.reader_.result;
    +};
    +
    +
    +/**
    + * @return {goog.fs.Error} The error encountered while reading, if any.
    + */
    +goog.fs.FileReader.prototype.getError = function() {
    +  return this.reader_.error &&
    +      new goog.fs.Error(this.reader_.error, 'reading file');
    +};
    +
    +
    +/**
    + * Wrap a progress event emitted by the underlying file reader and re-emit it.
    + *
    + * @param {!ProgressEvent} event The underlying event.
    + * @private
    + */
    +goog.fs.FileReader.prototype.dispatchProgressEvent_ = function(event) {
    +  this.dispatchEvent(new goog.fs.ProgressEvent(event, this));
    +};
    +
    +
    +/** @override */
    +goog.fs.FileReader.prototype.disposeInternal = function() {
    +  goog.fs.FileReader.base(this, 'disposeInternal');
    +  delete this.reader_;
    +};
    +
    +
    +/**
    + * Starts reading a blob as a binary string.
    + * @param {!Blob} blob The blob to read.
    + */
    +goog.fs.FileReader.prototype.readAsBinaryString = function(blob) {
    +  this.reader_.readAsBinaryString(blob);
    +};
    +
    +
    +/**
    + * Reads a blob as a binary string.
    + * @param {!Blob} blob The blob to read.
    + * @return {!goog.async.Deferred} The deferred Blob contents as a binary string.
    + *     If an error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileReader.readAsBinaryString = function(blob) {
    +  var reader = new goog.fs.FileReader();
    +  var d = goog.fs.FileReader.createDeferred_(reader);
    +  reader.readAsBinaryString(blob);
    +  return d;
    +};
    +
    +
    +/**
    + * Starts reading a blob as an array buffer.
    + * @param {!Blob} blob The blob to read.
    + */
    +goog.fs.FileReader.prototype.readAsArrayBuffer = function(blob) {
    +  this.reader_.readAsArrayBuffer(blob);
    +};
    +
    +
    +/**
    + * Reads a blob as an array buffer.
    + * @param {!Blob} blob The blob to read.
    + * @return {!goog.async.Deferred} The deferred Blob contents as an array buffer.
    + *     If an error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileReader.readAsArrayBuffer = function(blob) {
    +  var reader = new goog.fs.FileReader();
    +  var d = goog.fs.FileReader.createDeferred_(reader);
    +  reader.readAsArrayBuffer(blob);
    +  return d;
    +};
    +
    +
    +/**
    + * Starts reading a blob as text.
    + * @param {!Blob} blob The blob to read.
    + * @param {string=} opt_encoding The name of the encoding to use.
    + */
    +goog.fs.FileReader.prototype.readAsText = function(blob, opt_encoding) {
    +  this.reader_.readAsText(blob, opt_encoding);
    +};
    +
    +
    +/**
    + * Reads a blob as text.
    + * @param {!Blob} blob The blob to read.
    + * @param {string=} opt_encoding The name of the encoding to use.
    + * @return {!goog.async.Deferred} The deferred Blob contents as text.
    + *     If an error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileReader.readAsText = function(blob, opt_encoding) {
    +  var reader = new goog.fs.FileReader();
    +  var d = goog.fs.FileReader.createDeferred_(reader);
    +  reader.readAsText(blob, opt_encoding);
    +  return d;
    +};
    +
    +
    +/**
    + * Starts reading a blob as a data URL.
    + * @param {!Blob} blob The blob to read.
    + */
    +goog.fs.FileReader.prototype.readAsDataUrl = function(blob) {
    +  this.reader_.readAsDataURL(blob);
    +};
    +
    +
    +/**
    + * Reads a blob as a data URL.
    + * @param {!Blob} blob The blob to read.
    + * @return {!goog.async.Deferred} The deferred Blob contents as a data URL.
    + *     If an error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.FileReader.readAsDataUrl = function(blob) {
    +  var reader = new goog.fs.FileReader();
    +  var d = goog.fs.FileReader.createDeferred_(reader);
    +  reader.readAsDataUrl(blob);
    +  return d;
    +};
    +
    +
    +/**
    + * Creates a new deferred object for the results of a read method.
    + * @param {goog.fs.FileReader} reader The reader to create a deferred for.
    + * @return {!goog.async.Deferred} The deferred results.
    + * @private
    + */
    +goog.fs.FileReader.createDeferred_ = function(reader) {
    +  var deferred = new goog.async.Deferred();
    +  reader.listen(goog.fs.FileReader.EventType.LOAD_END,
    +      goog.partial(function(d, r, e) {
    +        var result = r.getResult();
    +        var error = r.getError();
    +        if (result != null && !error) {
    +          d.callback(result);
    +        } else {
    +          d.errback(error);
    +        }
    +        r.dispose();
    +      }, deferred, reader));
    +  return deferred;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/filesaver.js b/mapbox-gl/closure-library/closure/goog/fs/filesaver.js
    new file mode 100644
    index 0000000000..8d441c4e23
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/filesaver.js
    @@ -0,0 +1,166 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 FileSaver object.
    + *
    + */
    +
    +goog.provide('goog.fs.FileSaver');
    +goog.provide('goog.fs.FileSaver.EventType');
    +goog.provide('goog.fs.FileSaver.ReadyState');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.ProgressEvent');
    +
    +
    +
    +/**
    + * An object for monitoring the saving of files. This emits ProgressEvents of
    + * the types listed in {@link goog.fs.FileSaver.EventType}.
    + *
    + * This should not be instantiated directly. Instead, its subclass
    + * {@link goog.fs.FileWriter} should be accessed via
    + * {@link goog.fs.FileEntry#createWriter}.
    + *
    + * @param {!FileSaver} fileSaver The underlying FileSaver object.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.fs.FileSaver = function(fileSaver) {
    +  goog.fs.FileSaver.base(this, 'constructor');
    +
    +  /**
    +   * The underlying FileSaver object.
    +   *
    +   * @type {!FileSaver}
    +   * @private
    +   */
    +  this.saver_ = fileSaver;
    +
    +  this.saver_.onwritestart = goog.bind(this.dispatchProgressEvent_, this);
    +  this.saver_.onprogress = goog.bind(this.dispatchProgressEvent_, this);
    +  this.saver_.onwrite = goog.bind(this.dispatchProgressEvent_, this);
    +  this.saver_.onabort = goog.bind(this.dispatchProgressEvent_, this);
    +  this.saver_.onerror = goog.bind(this.dispatchProgressEvent_, this);
    +  this.saver_.onwriteend = goog.bind(this.dispatchProgressEvent_, this);
    +};
    +goog.inherits(goog.fs.FileSaver, goog.events.EventTarget);
    +
    +
    +/**
    + * Possible states for a FileSaver.
    + *
    + * @enum {number}
    + */
    +goog.fs.FileSaver.ReadyState = {
    +  /**
    +   * The object has been constructed, but there is no pending write.
    +   */
    +  INIT: 0,
    +  /**
    +   * Data is being written.
    +   */
    +  WRITING: 1,
    +  /**
    +   * The data has been written to the file, the write was aborted, or an error
    +   * occurred.
    +   */
    +  DONE: 2
    +};
    +
    +
    +/**
    + * Events emitted by a FileSaver.
    + *
    + * @enum {string}
    + */
    +goog.fs.FileSaver.EventType = {
    +  /**
    +   * Emitted when the writing begins. readyState will be WRITING.
    +   */
    +  WRITE_START: 'writestart',
    +  /**
    +   * Emitted when progress has been made in saving the file. readyState will be
    +   * WRITING.
    +   */
    +  PROGRESS: 'progress',
    +  /**
    +   * Emitted when the data has been successfully written. readyState will be
    +   * WRITING.
    +   */
    +  WRITE: 'write',
    +  /**
    +   * Emitted when the writing has been aborted. readyState will be WRITING.
    +   */
    +  ABORT: 'abort',
    +  /**
    +   * Emitted when an error is encountered or the writing has been aborted.
    +   * readyState will be WRITING.
    +   */
    +  ERROR: 'error',
    +  /**
    +   * Emitted when the writing is finished, whether successfully or not.
    +   * readyState will be DONE.
    +   */
    +  WRITE_END: 'writeend'
    +};
    +
    +
    +/**
    + * Abort the writing of the file.
    + */
    +goog.fs.FileSaver.prototype.abort = function() {
    +  try {
    +    this.saver_.abort();
    +  } catch (e) {
    +    throw new goog.fs.Error(e, 'aborting save');
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.fs.FileSaver.ReadyState} The current state of the FileSaver.
    + */
    +goog.fs.FileSaver.prototype.getReadyState = function() {
    +  return /** @type {goog.fs.FileSaver.ReadyState} */ (this.saver_.readyState);
    +};
    +
    +
    +/**
    + * @return {goog.fs.Error} The error encountered while writing, if any.
    + */
    +goog.fs.FileSaver.prototype.getError = function() {
    +  return this.saver_.error &&
    +      new goog.fs.Error(this.saver_.error, 'saving file');
    +};
    +
    +
    +/**
    + * Wrap a progress event emitted by the underlying file saver and re-emit it.
    + *
    + * @param {!ProgressEvent} event The underlying event.
    + * @private
    + */
    +goog.fs.FileSaver.prototype.dispatchProgressEvent_ = function(event) {
    +  this.dispatchEvent(new goog.fs.ProgressEvent(event, this));
    +};
    +
    +
    +/** @override */
    +goog.fs.FileSaver.prototype.disposeInternal = function() {
    +  delete this.saver_;
    +  goog.fs.FileSaver.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/filesystem.js b/mapbox-gl/closure-library/closure/goog/fs/filesystem.js
    new file mode 100644
    index 0000000000..b120b92dd5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/filesystem.js
    @@ -0,0 +1,41 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 FileSystem object.
    + *
    + */
    +
    +goog.provide('goog.fs.FileSystem');
    +
    +
    +
    +/**
    + * A local filesystem.
    + *
    + * @interface
    + */
    +goog.fs.FileSystem = function() {};
    +
    +
    +/**
    + * @return {string} The name of the filesystem.
    + */
    +goog.fs.FileSystem.prototype.getName = function() {};
    +
    +
    +/**
    + * @return {!goog.fs.DirectoryEntry} The root directory of the filesystem.
    + */
    +goog.fs.FileSystem.prototype.getRoot = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/filesystemimpl.js b/mapbox-gl/closure-library/closure/goog/fs/filesystemimpl.js
    new file mode 100644
    index 0000000000..b5ebb33b00
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/filesystemimpl.js
    @@ -0,0 +1,65 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Concrete implementation of the goog.fs.FileSystem interface
    + *     using an HTML FileSystem object.
    + */
    +goog.provide('goog.fs.FileSystemImpl');
    +
    +goog.require('goog.fs.DirectoryEntryImpl');
    +goog.require('goog.fs.FileSystem');
    +
    +
    +
    +/**
    + * A local filesystem.
    + *
    + * This shouldn't be instantiated directly. Instead, it should be accessed via
    + * {@link goog.fs.getTemporary} or {@link goog.fs.getPersistent}.
    + *
    + * @param {!FileSystem} fs The underlying FileSystem object.
    + * @constructor
    + * @implements {goog.fs.FileSystem}
    + * @final
    + */
    +goog.fs.FileSystemImpl = function(fs) {
    +  /**
    +   * The underlying FileSystem object.
    +   *
    +   * @type {!FileSystem}
    +   * @private
    +   */
    +  this.fs_ = fs;
    +};
    +
    +
    +/** @override */
    +goog.fs.FileSystemImpl.prototype.getName = function() {
    +  return this.fs_.name;
    +};
    +
    +
    +/** @override */
    +goog.fs.FileSystemImpl.prototype.getRoot = function() {
    +  return new goog.fs.DirectoryEntryImpl(this, this.fs_.root);
    +};
    +
    +
    +/**
    + * @return {!FileSystem} The underlying FileSystem object.
    + */
    +goog.fs.FileSystemImpl.prototype.getBrowserFileSystem = function() {
    +  return this.fs_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/filewriter.js b/mapbox-gl/closure-library/closure/goog/fs/filewriter.js
    new file mode 100644
    index 0000000000..170984651d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/filewriter.js
    @@ -0,0 +1,111 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 FileWriter object.
    + *
    + * When adding or modifying functionality in this namespace, be sure to update
    + * the mock counterparts in goog.testing.fs.
    + *
    + */
    +
    +goog.provide('goog.fs.FileWriter');
    +
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileSaver');
    +
    +
    +
    +/**
    + * An object for monitoring the saving of files, as well as other fine-grained
    + * writing operations.
    + *
    + * This should not be instantiated directly. Instead, it should be accessed via
    + * {@link goog.fs.FileEntry#createWriter}.
    + *
    + * @param {!FileWriter} writer The underlying FileWriter object.
    + * @constructor
    + * @extends {goog.fs.FileSaver}
    + * @final
    + */
    +goog.fs.FileWriter = function(writer) {
    +  goog.fs.FileWriter.base(this, 'constructor', writer);
    +
    +  /**
    +   * The underlying FileWriter object.
    +   *
    +   * @type {!FileWriter}
    +   * @private
    +   */
    +  this.writer_ = writer;
    +};
    +goog.inherits(goog.fs.FileWriter, goog.fs.FileSaver);
    +
    +
    +/**
    + * @return {number} The byte offset at which the next write will occur.
    + */
    +goog.fs.FileWriter.prototype.getPosition = function() {
    +  return this.writer_.position;
    +};
    +
    +
    +/**
    + * @return {number} The length of the file.
    + */
    +goog.fs.FileWriter.prototype.getLength = function() {
    +  return this.writer_.length;
    +};
    +
    +
    +/**
    + * Write data to the file.
    + *
    + * @param {!Blob} blob The data to write.
    + */
    +goog.fs.FileWriter.prototype.write = function(blob) {
    +  try {
    +    this.writer_.write(blob);
    +  } catch (e) {
    +    throw new goog.fs.Error(e, 'writing file');
    +  }
    +};
    +
    +
    +/**
    + * Set the file position at which the next write will occur.
    + *
    + * @param {number} offset An absolute byte offset into the file.
    + */
    +goog.fs.FileWriter.prototype.seek = function(offset) {
    +  try {
    +    this.writer_.seek(offset);
    +  } catch (e) {
    +    throw new goog.fs.Error(e, 'seeking in file');
    +  }
    +};
    +
    +
    +/**
    + * Changes the length of the file to that specified.
    + *
    + * @param {number} size The new size of the file, in bytes.
    + */
    +goog.fs.FileWriter.prototype.truncate = function(size) {
    +  try {
    +    this.writer_.truncate(size);
    +  } catch (e) {
    +    throw new goog.fs.Error(e, 'truncating file');
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/fs.js b/mapbox-gl/closure-library/closure/goog/fs/fs.js
    new file mode 100644
    index 0000000000..25fb0cd5ee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/fs.js
    @@ -0,0 +1,319 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wrappers for the HTML5 File API. These wrappers closely mirror
    + * the underlying APIs, but use Closure-style events and Deferred return values.
    + * Their existence also makes it possible to mock the FileSystem API for testing
    + * in browsers that don't support it natively.
    + *
    + * When adding public functions to anything under this namespace, be sure to add
    + * its mock counterpart to goog.testing.fs.
    + *
    + */
    +
    +goog.provide('goog.fs');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileReader');
    +goog.require('goog.fs.FileSystemImpl');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Get a wrapped FileSystem object.
    + *
    + * @param {goog.fs.FileSystemType_} type The type of the filesystem to get.
    + * @param {number} size The size requested for the filesystem, in bytes.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
    + *     error occurs, the errback is called with a {@link goog.fs.Error}.
    + * @private
    + */
    +goog.fs.get_ = function(type, size) {
    +  var requestFileSystem = goog.global.requestFileSystem ||
    +      goog.global.webkitRequestFileSystem;
    +
    +  if (!goog.isFunction(requestFileSystem)) {
    +    return goog.async.Deferred.fail(new Error('File API unsupported'));
    +  }
    +
    +  var d = new goog.async.Deferred();
    +  requestFileSystem(type, size, function(fs) {
    +    d.callback(new goog.fs.FileSystemImpl(fs));
    +  }, function(err) {
    +    d.errback(new goog.fs.Error(err, 'requesting filesystem'));
    +  });
    +  return d;
    +};
    +
    +
    +/**
    + * The two types of filesystem.
    + *
    + * @enum {number}
    + * @private
    + */
    +goog.fs.FileSystemType_ = {
    +  /**
    +   * A temporary filesystem may be deleted by the user agent at its discretion.
    +   */
    +  TEMPORARY: 0,
    +  /**
    +   * A persistent filesystem will never be deleted without the user's or
    +   * application's authorization.
    +   */
    +  PERSISTENT: 1
    +};
    +
    +
    +/**
    + * Returns a temporary FileSystem object. A temporary filesystem may be deleted
    + * by the user agent at its discretion.
    + *
    + * @param {number} size The size requested for the filesystem, in bytes.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
    + *     error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.getTemporary = function(size) {
    +  return goog.fs.get_(goog.fs.FileSystemType_.TEMPORARY, size);
    +};
    +
    +
    +/**
    + * Returns a persistent FileSystem object. A persistent filesystem will never be
    + * deleted without the user's or application's authorization.
    + *
    + * @param {number} size The size requested for the filesystem, in bytes.
    + * @return {!goog.async.Deferred} The deferred {@link goog.fs.FileSystem}. If an
    + *     error occurs, the errback is called with a {@link goog.fs.Error}.
    + */
    +goog.fs.getPersistent = function(size) {
    +  return goog.fs.get_(goog.fs.FileSystemType_.PERSISTENT, size);
    +};
    +
    +
    +/**
    + * Creates a blob URL for a blob object.
    + * Throws an error if the browser does not support Object Urls.
    + *
    + * @param {!Blob} blob The object for which to create the URL.
    + * @return {string} The URL for the object.
    + */
    +goog.fs.createObjectUrl = function(blob) {
    +  return goog.fs.getUrlObject_().createObjectURL(blob);
    +};
    +
    +
    +/**
    + * Revokes a URL created by {@link goog.fs.createObjectUrl}.
    + * Throws an error if the browser does not support Object Urls.
    + *
    + * @param {string} url The URL to revoke.
    + */
    +goog.fs.revokeObjectUrl = function(url) {
    +  goog.fs.getUrlObject_().revokeObjectURL(url);
    +};
    +
    +
    +/**
    + * @typedef {{createObjectURL: (function(!Blob): string),
    + *            revokeObjectURL: function(string): void}}
    + */
    +goog.fs.UrlObject_;
    +
    +
    +/**
    + * Get the object that has the createObjectURL and revokeObjectURL functions for
    + * this browser.
    + *
    + * @return {goog.fs.UrlObject_} The object for this browser.
    + * @private
    + */
    +goog.fs.getUrlObject_ = function() {
    +  var urlObject = goog.fs.findUrlObject_();
    +  if (urlObject != null) {
    +    return urlObject;
    +  } else {
    +    throw Error('This browser doesn\'t seem to support blob URLs');
    +  }
    +};
    +
    +
    +/**
    + * Finds the object that has the createObjectURL and revokeObjectURL functions
    + * for this browser.
    + *
    + * @return {?goog.fs.UrlObject_} The object for this browser or null if the
    + *     browser does not support Object Urls.
    + * @private
    + */
    +goog.fs.findUrlObject_ = function() {
    +  // This is what the spec says to do
    +  // http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
    +  if (goog.isDef(goog.global.URL) &&
    +      goog.isDef(goog.global.URL.createObjectURL)) {
    +    return /** @type {goog.fs.UrlObject_} */ (goog.global.URL);
    +  // This is what Chrome does (as of 10.0.648.6 dev)
    +  } else if (goog.isDef(goog.global.webkitURL) &&
    +             goog.isDef(goog.global.webkitURL.createObjectURL)) {
    +    return /** @type {goog.fs.UrlObject_} */ (goog.global.webkitURL);
    +  // This is what the spec used to say to do
    +  } else if (goog.isDef(goog.global.createObjectURL)) {
    +    return /** @type {goog.fs.UrlObject_} */ (goog.global);
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Checks whether this browser supports Object Urls. If not, calls to
    + * createObjectUrl and revokeObjectUrl will result in an error.
    + *
    + * @return {boolean} True if this browser supports Object Urls.
    + */
    +goog.fs.browserSupportsObjectUrls = function() {
    +  return goog.fs.findUrlObject_() != null;
    +};
    +
    +
    +/**
    + * Concatenates one or more values together and converts them to a Blob.
    + *
    + * @param {...(string|!Blob|!ArrayBuffer)} var_args The values that will make up
    + *     the resulting blob.
    + * @return {!Blob} The blob.
    + */
    +goog.fs.getBlob = function(var_args) {
    +  var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;
    +
    +  if (goog.isDef(BlobBuilder)) {
    +    var bb = new BlobBuilder();
    +    for (var i = 0; i < arguments.length; i++) {
    +      bb.append(arguments[i]);
    +    }
    +    return bb.getBlob();
    +  } else {
    +    return goog.fs.getBlobWithProperties(goog.array.toArray(arguments));
    +  }
    +};
    +
    +
    +/**
    + * Creates a blob with the given properties.
    + * See https://developer.mozilla.org/en-US/docs/Web/API/Blob for more details.
    + *
    + * @param {Array<string|!Blob>} parts The values that will make up the
    + *     resulting blob.
    + * @param {string=} opt_type The MIME type of the Blob.
    + * @param {string=} opt_endings Specifies how strings containing newlines are to
    + *     be written out.
    + * @return {!Blob} The blob.
    + */
    +goog.fs.getBlobWithProperties = function(parts, opt_type, opt_endings) {
    +  var BlobBuilder = goog.global.BlobBuilder || goog.global.WebKitBlobBuilder;
    +
    +  if (goog.isDef(BlobBuilder)) {
    +    var bb = new BlobBuilder();
    +    for (var i = 0; i < parts.length; i++) {
    +      bb.append(parts[i], opt_endings);
    +    }
    +    return bb.getBlob(opt_type);
    +  } else if (goog.isDef(goog.global.Blob)) {
    +    var properties = {};
    +    if (opt_type) {
    +      properties['type'] = opt_type;
    +    }
    +    if (opt_endings) {
    +      properties['endings'] = opt_endings;
    +    }
    +    return new Blob(parts, properties);
    +  } else {
    +    throw Error('This browser doesn\'t seem to support creating Blobs');
    +  }
    +};
    +
    +
    +/**
    + * Converts a Blob or a File into a string. This should only be used when the
    + * blob is known to be small.
    + *
    + * @param {!Blob} blob The blob to convert.
    + * @param {string=} opt_encoding The name of the encoding to use.
    + * @return {!goog.async.Deferred} The deferred string. If an error occurrs, the
    + *     errback is called with a {@link goog.fs.Error}.
    + * @deprecated Use {@link goog.fs.FileReader.readAsText} instead.
    + */
    +goog.fs.blobToString = function(blob, opt_encoding) {
    +  return goog.fs.FileReader.readAsText(blob, opt_encoding);
    +};
    +
    +
    +/**
    + * Slices the blob. The returned blob contains data from the start byte
    + * (inclusive) till the end byte (exclusive). Negative indices can be used
    + * to count bytes from the end of the blob (-1 == blob.size - 1). Indices
    + * are always clamped to blob range. If end is omitted, all the data till
    + * the end of the blob is taken.
    + *
    + * @param {!Blob} blob The blob to be sliced.
    + * @param {number} start Index of the starting byte.
    + * @param {number=} opt_end Index of the ending byte.
    + * @return {Blob} The blob slice or null if not supported.
    + */
    +goog.fs.sliceBlob = function(blob, start, opt_end) {
    +  if (!goog.isDef(opt_end)) {
    +    opt_end = blob.size;
    +  }
    +  if (blob.webkitSlice) {
    +    // Natively accepts negative indices, clamping to the blob range and
    +    // range end is optional. See http://trac.webkit.org/changeset/83873
    +    return blob.webkitSlice(start, opt_end);
    +  } else if (blob.mozSlice) {
    +    // Natively accepts negative indices, clamping to the blob range and
    +    // range end is optional. See https://developer.mozilla.org/en/DOM/Blob
    +    // and http://hg.mozilla.org/mozilla-central/rev/dae833f4d934
    +    return blob.mozSlice(start, opt_end);
    +  } else if (blob.slice) {
    +    // Old versions of Firefox and Chrome use the original specification.
    +    // Negative indices are not accepted, only range end is clamped and
    +    // range end specification is obligatory.
    +    // See http://www.w3.org/TR/2009/WD-FileAPI-20091117/
    +    if ((goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('13.0')) ||
    +        (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('537.1'))) {
    +      if (start < 0) {
    +        start += blob.size;
    +      }
    +      if (start < 0) {
    +        start = 0;
    +      }
    +      if (opt_end < 0) {
    +        opt_end += blob.size;
    +      }
    +      if (opt_end < start) {
    +        opt_end = start;
    +      }
    +      return blob.slice(start, opt_end - start);
    +    }
    +    // IE and the latest versions of Firefox and Chrome use the new
    +    // specification. Natively accepts negative indices, clamping to the blob
    +    // range and range end is optional.
    +    // See http://dev.w3.org/2006/webapi/FileAPI/
    +    return blob.slice(start, opt_end);
    +  }
    +  return null;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/fs_test.html b/mapbox-gl/closure-library/closure/goog/fs/fs_test.html
    new file mode 100644
    index 0000000000..a76ee2a703
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/fs_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <title>
    +   Closure Integration Tests - goog.fs
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fsTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="closureTestRunnerLog">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/fs_test.js b/mapbox-gl/closure-library/closure/goog/fs/fs_test.js
    new file mode 100644
    index 0000000000..6d3e74f7d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/fs_test.js
    @@ -0,0 +1,842 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fsTest');
    +goog.setTestOnly('goog.fsTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.async.DeferredList');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.fs');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileReader');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.string');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_DIR = 'goog-fs-test-dir';
    +
    +var fsExists = goog.isDef(goog.global.requestFileSystem) ||
    +    goog.isDef(goog.global.webkitRequestFileSystem);
    +var deferredFs = fsExists ? goog.fs.getTemporary() : null;
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +asyncTestCase.stepTimeout = 5000;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUpPage() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().addErrback(function(err) {
    +    var msg;
    +    if (err.code == goog.fs.Error.ErrorCode.QUOTA_EXCEEDED) {
    +      msg = err.message + '. If you\'re using Chrome, you probably need to ' +
    +          'pass --unlimited-quota-for-files on the command line.';
    +    } else if (err.code == goog.fs.Error.ErrorCode.SECURITY &&
    +               window.location.href.match(/^file:/)) {
    +      msg = err.message + '. file:// URLs can\'t access the filesystem API.';
    +    } else {
    +      msg = err.message;
    +    }
    +    var body = goog.dom.getDocument().body;
    +    goog.dom.insertSiblingBefore(
    +        goog.dom.createDom('h1', {}, msg), body.childNodes[0]);
    +  });
    +}
    +
    +function tearDown() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      addCallback(function(dir) { return dir.removeRecursively(); }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('removing filesystem');
    +}
    +
    +function testUnavailableTemporaryFilesystem() {
    +  stubs.set(goog.global, 'requestFileSystem', null);
    +  stubs.set(goog.global, 'webkitRequestFileSystem', null);
    +  asyncTestCase.waitForAsync('testUnavailableTemporaryFilesystem');
    +
    +  goog.fs.getTemporary(1024).addErrback(function(e) {
    +    assertEquals('File API unsupported', e.message);
    +    continueTesting();
    +  });
    +}
    +
    +
    +function testUnavailablePersistentFilesystem() {
    +  stubs.set(goog.global, 'requestFileSystem', null);
    +  stubs.set(goog.global, 'webkitRequestFileSystem', null);
    +  asyncTestCase.waitForAsync('testUnavailablePersistentFilesystem');
    +
    +  goog.fs.getPersistent(2048).addErrback(function(e) {
    +    assertEquals('File API unsupported', e.message);
    +    continueTesting();
    +  });
    +}
    +
    +
    +function testIsFile() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(fileEntry) {
    +        assertFalse(fileEntry.isDirectory());
    +        assertTrue(fileEntry.isFile());
    +      }).addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testIsFile');
    +}
    +
    +function testIsDirectory() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadDirectory('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(fileEntry) {
    +        assertTrue(fileEntry.isDirectory());
    +        assertFalse(fileEntry.isFile());
    +      }).addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testIsDirectory');
    +}
    +
    +function testReadFileUtf16() {
    +  if (!fsExists) {
    +    return;
    +  }
    +  var str = 'test content';
    +  var buf = new ArrayBuffer(str.length * 2);
    +  var arr = new Uint16Array(buf);
    +  for (var i = 0; i < str.length; i++) {
    +    arr[i] = str.charCodeAt(i);
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, arr.buffer)).
    +      addCallback(goog.partial(checkFileContentWithEncoding, str, 'UTF-16')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testReadFile');
    +}
    +
    +function testReadFileUtf8() {
    +  if (!fsExists) {
    +    return;
    +  }
    +  var str = 'test content';
    +  var buf = new ArrayBuffer(str.length);
    +  var arr = new Uint8Array(buf);
    +  for (var i = 0; i < str.length; i++) {
    +    arr[i] = str.charCodeAt(i) & 0xff;
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, arr.buffer)).
    +      addCallback(goog.partial(checkFileContentWithEncoding, str, 'UTF-8')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testReadFileUtf8');
    +}
    +
    +function testReadFileAsArrayBuffer() {
    +  if (!fsExists) {
    +    return;
    +  }
    +  var str = 'test content';
    +  var buf = new ArrayBuffer(str.length);
    +  var arr = new Uint8Array(buf);
    +  for (var i = 0; i < str.length; i++) {
    +    arr[i] = str.charCodeAt(i) & 0xff;
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, arr.buffer)).
    +      addCallback(goog.partial(checkFileContentAs, arr.buffer, 'ArrayBuffer',
    +          undefined)).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testReadFileAsArrayBuffer');
    +}
    +
    +function testReadFileAsBinaryString() {
    +  if (!fsExists) {
    +    return;
    +  }
    +  var str = 'test content';
    +  var buf = new ArrayBuffer(str.length);
    +  var arr = new Uint8Array(buf);
    +  for (var i = 0; i < str.length; i++) {
    +    arr[i] = str.charCodeAt(i);
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, arr.buffer)).
    +      addCallback(goog.partial(checkFileContentAs, str, 'BinaryString',
    +          undefined)).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testReadFileAsArrayBuffer');
    +}
    +
    +function testWriteFile() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testWriteFile');
    +}
    +
    +function testRemoveFile() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(file) { return file.remove(); }).
    +      addCallback(goog.partial(checkFileRemoved, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testRemoveFile');
    +}
    +
    +function testMoveFile() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  var deferredSubdir = loadDirectory(
    +      'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredWrittenFile =
    +      loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content'));
    +  goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
    +      addCallback(splitArgs(function(dir, file) { return file.moveTo(dir); })).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addCallback(goog.partial(checkFileRemoved, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testMoveFile');
    +}
    +
    +function testCopyFile() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredSubdir = loadDirectory(
    +      'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredWrittenFile = deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content'));
    +  goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
    +      addCallback(splitArgs(function(dir, file) { return file.copyTo(dir); })).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testMoveFile');
    +}
    +
    +function testAbortWrite() {
    +  // TODO(nicksantos): This test is broken in newer versions of chrome.
    +  // We don't know why yet.
    +  if (true) return;
    +
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(startWrite, 'test content')).
    +      addCallback(function(writer) { writer.abort(); }).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.ABORT)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, '')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testWriteFile');
    +}
    +
    +function testSeek() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(file) { return file.createWriter(); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) {
    +        writer.seek(5);
    +        writer.write(goog.fs.getBlob('stuff and things'));
    +      }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test stuff and things')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testWriteFile');
    +}
    +
    +function testTruncate() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(file) { return file.createWriter(); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) { writer.truncate(4); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testWriteFile');
    +}
    +
    +function testGetLastModified() {
    +  if (!fsExists) {
    +    return;
    +  }
    +  var now = goog.now();
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(entry) {
    +        return entry.getLastModified();
    +      }).addCallback(function(date) {
    +        assertRoughlyEquals('Expected the last modified date to be within ' +
    +                            'a few milliseconds of the test start time.',
    +                            now, date.getTime(), 2000);
    +      }).addCallback(continueTesting);
    +
    +  asyncTestCase.waitForAsync('testGetLastModified');
    +}
    +
    +function testCreatePath() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      addCallback(function(testDir) {
    +        return testDir.createPath('foo');
    +      }).
    +      addCallback(function(fooDir) {
    +        assertEquals('/goog-fs-test-dir/foo', fooDir.getFullPath());
    +        return fooDir.createPath('bar/baz/bat');
    +      }).
    +      addCallback(function(batDir) {
    +        assertEquals('/goog-fs-test-dir/foo/bar/baz/bat', batDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testCreatePath');
    +}
    +
    +function testCreateAbsolutePath() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      addCallback(function(testDir) {
    +        return testDir.createPath('/' + TEST_DIR + '/fee/fi/fo/fum');
    +      }).
    +      addCallback(function(absDir) {
    +        assertEquals('/goog-fs-test-dir/fee/fi/fo/fum', absDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testCreateAbsolutePath');
    +}
    +
    +function testCreateRelativePath() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      addCallback(function(dir) {
    +        return dir.createPath('../' + TEST_DIR + '/dir');
    +      }).
    +      addCallback(function(relDir) {
    +        assertEquals('/goog-fs-test-dir/dir', relDir.getFullPath());
    +        return relDir.createPath('.');
    +      }).
    +      addCallback(function(sameDir) {
    +        assertEquals('/goog-fs-test-dir/dir', sameDir.getFullPath());
    +        return sameDir.createPath('./././.');
    +      }).
    +      addCallback(function(reallySameDir) {
    +        assertEquals('/goog-fs-test-dir/dir', reallySameDir.getFullPath());
    +        return reallySameDir.createPath('./new/../..//dir/./new////.');
    +      }).
    +      addCallback(function(newDir) {
    +        assertEquals('/goog-fs-test-dir/dir/new', newDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testCreateRelativePath');
    +
    +}
    +
    +function testCreateBadPath() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      awaitDeferred(loadTestDir()).
    +      addCallback(function(dir) {
    +        // There is only one layer of parent directory from the test dir.
    +        return dir.createPath('../../../../' + TEST_DIR + '/baz/bat');
    +      }).
    +      addCallback(function(batDir) {
    +        assertEquals('The parent directory of the root directory should ' +
    +                     'point back to the root directory.',
    +                     '/goog-fs-test-dir/baz/bat', batDir.getFullPath());
    +      }).
    +
    +      awaitDeferred(loadTestDir()).
    +      addCallback(function(dir) {
    +        // An empty path should return the same as the input directory.
    +        return dir.createPath('');
    +      }).
    +      addCallback(function(testDir) {
    +        assertEquals('/goog-fs-test-dir', testDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testCreateBadPath');
    +}
    +
    +function testGetAbsolutePaths() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadFile('foo', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      awaitDeferred(loadTestDir()).
    +      addCallback(function(testDir) {
    +        return testDir.getDirectory('/');
    +      }).
    +      addCallback(function(root) {
    +        assertEquals('/', root.getFullPath());
    +        return root.getDirectory('/' + TEST_DIR);
    +      }).
    +      addCallback(function(testDir) {
    +        assertEquals('/goog-fs-test-dir', testDir.getFullPath());
    +        return testDir.getDirectory('//' + TEST_DIR + '////');
    +      }).
    +      addCallback(function(testDir) {
    +        assertEquals('/goog-fs-test-dir', testDir.getFullPath());
    +        return testDir.getDirectory('////');
    +      }).
    +      addCallback(function(testDir) {
    +        assertEquals('/', testDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testGetAbsolutePaths');
    +}
    +
    +
    +function continueTesting(result) {
    +  asyncTestCase.continueTesting();
    +  if (result instanceof Error) {
    +    throw result;
    +  }
    +}
    +
    +function testListEmptyDirectory() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  loadTestDir().
    +      addCallback(function(dir) { return dir.listDirectory(); }).
    +      addCallback(function(entries) { assertArrayEquals([], entries); }).
    +      addCallback(continueTesting);
    +  asyncTestCase.waitForAsync('testListEmptyDirectory');
    +}
    +
    +
    +function testListDirectory() {
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  goog.async.Deferred.succeed().
    +      // Create the test directory and test entries.
    +      awaitDeferred(
    +          loadDirectory('testDir', goog.fs.DirectoryEntry.Behavior.CREATE)).
    +      awaitDeferred(
    +          loadFile('testFile', goog.fs.DirectoryEntry.Behavior.CREATE)).
    +      awaitDeferred(loadTestDir()).
    +
    +      // Verify the contents of the directory listing.
    +      addCallback(function(testDir) { return testDir.listDirectory(); }).
    +      addCallback(function(entries) {
    +        assertEquals(2, entries.length);
    +
    +        var dir = goog.array.find(entries, function(entry) {
    +          return entry.getName() == 'testDir';
    +        });
    +        assertNotNull(dir);
    +        assertTrue(dir.isDirectory());
    +
    +        var file = goog.array.find(entries, function(entry) {
    +          return entry.getName() == 'testFile';
    +        });
    +        assertNotNull(file);
    +        assertTrue(file.isFile());
    +      }).
    +      addCallback(continueTesting);
    +
    +  asyncTestCase.waitForAsync('testListDirectory');
    +}
    +
    +
    +function testListBigDirectory() {
    +  // TODO(nicksantos): This test is broken in newer versions of chrome.
    +  // We don't know why yet.
    +  if (true) return;
    +
    +  if (!fsExists) {
    +    return;
    +  }
    +
    +  function getFileName(i) {
    +    return 'file' + goog.string.padNumber(i, String(count).length);
    +  }
    +
    +  // NOTE: This was intended to verify that the results from repeated
    +  // DirectoryReader.readEntries() callbacks are appropriately concatenated.
    +  // In current versions of Chrome (March 2011), all results are returned in the
    +  // first callback regardless of directory size. The count can be increased in
    +  // the future to test batched result lists once they are implemented.
    +  var count = 100;
    +
    +  var expectedNames = [];
    +
    +  var def = goog.async.Deferred.succeed();
    +  for (var i = 0; i < count; i++) {
    +    var name = getFileName(i);
    +    expectedNames.push(name);
    +
    +    def.awaitDeferred(
    +        loadFile(name, goog.fs.DirectoryEntry.Behavior.CREATE));
    +  }
    +
    +  def.awaitDeferred(loadTestDir()).
    +      addCallback(function(testDir) { return testDir.listDirectory(); }).
    +      addCallback(function(entries) {
    +        assertEquals(count, entries.length);
    +
    +        assertSameElements(expectedNames,
    +                           goog.array.map(entries, function(entry) {
    +                             return entry.getName();
    +                           }));
    +        assertTrue(goog.array.every(entries, function(entry) {
    +          return entry.isFile();
    +        }));
    +      }).
    +      addCallback(continueTesting);
    +
    +  asyncTestCase.waitForAsync('testListBigDirectory');
    +}
    +
    +
    +function testSliceBlob() {
    +  // A mock blob object whose slice returns the parameters it was called with.
    +  var blob = {
    +    'size': 10,
    +    'slice': function(start, end) {
    +      return [start, end];
    +    }
    +  };
    +
    +  // Simulate Firefox 13 that implements the new slice.
    +  var tmpStubs = new goog.testing.PropertyReplacer();
    +  tmpStubs.set(goog.userAgent, 'GECKO', true);
    +  tmpStubs.set(goog.userAgent, 'WEBKIT', false);
    +  tmpStubs.set(goog.userAgent, 'IE', false);
    +  tmpStubs.set(goog.userAgent, 'VERSION', '13.0');
    +  tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
    +
    +  // Expect slice to be called with no change to parameters
    +  assertArrayEquals([2, 10], goog.fs.sliceBlob(blob, 2));
    +  assertArrayEquals([-2, 10], goog.fs.sliceBlob(blob, -2));
    +  assertArrayEquals([3, 6], goog.fs.sliceBlob(blob, 3, 6));
    +  assertArrayEquals([3, -6], goog.fs.sliceBlob(blob, 3, -6));
    +
    +  // Simulate IE 10 that implements the new slice.
    +  var tmpStubs = new goog.testing.PropertyReplacer();
    +  tmpStubs.set(goog.userAgent, 'GECKO', false);
    +  tmpStubs.set(goog.userAgent, 'WEBKIT', false);
    +  tmpStubs.set(goog.userAgent, 'IE', true);
    +  tmpStubs.set(goog.userAgent, 'VERSION', '10.0');
    +  tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
    +
    +  // Expect slice to be called with no change to parameters
    +  assertArrayEquals([2, 10], goog.fs.sliceBlob(blob, 2));
    +  assertArrayEquals([-2, 10], goog.fs.sliceBlob(blob, -2));
    +  assertArrayEquals([3, 6], goog.fs.sliceBlob(blob, 3, 6));
    +  assertArrayEquals([3, -6], goog.fs.sliceBlob(blob, 3, -6));
    +
    +  // Simulate Firefox 4 that implements the old slice.
    +  tmpStubs.set(goog.userAgent, 'GECKO', true);
    +  tmpStubs.set(goog.userAgent, 'WEBKIT', false);
    +  tmpStubs.set(goog.userAgent, 'IE', false);
    +  tmpStubs.set(goog.userAgent, 'VERSION', '2.0');
    +  tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
    +
    +  // Expect slice to be called with transformed parameters.
    +  assertArrayEquals([2, 8], goog.fs.sliceBlob(blob, 2));
    +  assertArrayEquals([8, 2], goog.fs.sliceBlob(blob, -2));
    +  assertArrayEquals([3, 3], goog.fs.sliceBlob(blob, 3, 6));
    +  assertArrayEquals([3, 1], goog.fs.sliceBlob(blob, 3, -6));
    +
    +  // Simulate Firefox 5 that implements mozSlice (new spec).
    +  delete blob.slice;
    +  blob.mozSlice = function(start, end) {
    +    return ['moz', start, end];
    +  };
    +  tmpStubs.set(goog.userAgent, 'GECKO', true);
    +  tmpStubs.set(goog.userAgent, 'WEBKIT', false);
    +  tmpStubs.set(goog.userAgent, 'IE', false);
    +  tmpStubs.set(goog.userAgent, 'VERSION', '5.0');
    +  tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
    +
    +  // Expect mozSlice to be called with no change to parameters.
    +  assertArrayEquals(['moz', 2, 10], goog.fs.sliceBlob(blob, 2));
    +  assertArrayEquals(['moz', -2, 10], goog.fs.sliceBlob(blob, -2));
    +  assertArrayEquals(['moz', 3, 6], goog.fs.sliceBlob(blob, 3, 6));
    +  assertArrayEquals(['moz', 3, -6], goog.fs.sliceBlob(blob, 3, -6));
    +
    +  // Simulate Chrome 20 that implements webkitSlice (new spec).
    +  delete blob.mozSlice;
    +  blob.webkitSlice = function(start, end) {
    +    return ['webkit', start, end];
    +  };
    +  tmpStubs.set(goog.userAgent, 'GECKO', false);
    +  tmpStubs.set(goog.userAgent, 'WEBKIT', true);
    +  tmpStubs.set(goog.userAgent, 'IE', false);
    +  tmpStubs.set(goog.userAgent, 'VERSION', '536.10');
    +  tmpStubs.set(goog.userAgent, 'isVersionOrHigherCache_', {});
    +
    +  // Expect webkitSlice to be called with no change to parameters.
    +  assertArrayEquals(['webkit', 2, 10], goog.fs.sliceBlob(blob, 2));
    +  assertArrayEquals(['webkit', -2, 10], goog.fs.sliceBlob(blob, -2));
    +  assertArrayEquals(['webkit', 3, 6], goog.fs.sliceBlob(blob, 3, 6));
    +  assertArrayEquals(['webkit', 3, -6], goog.fs.sliceBlob(blob, 3, -6));
    +
    +  tmpStubs.reset();
    +}
    +
    +
    +function testBrowserSupportsObjectUrls() {
    +  stubs.remove(goog.global, 'URL');
    +  stubs.remove(goog.global, 'webkitURL');
    +  stubs.remove(goog.global, 'createObjectURL');
    +
    +  assertFalse(goog.fs.browserSupportsObjectUrls());
    +  try {
    +    goog.fs.createObjectUrl();
    +    fail();
    +  } catch (e) {
    +    assertEquals('This browser doesn\'t seem to support blob URLs', e.message);
    +  }
    +
    +  var objectUrl = {};
    +  function createObjectURL() { return objectUrl; }
    +  stubs.set(goog.global, 'createObjectURL', createObjectURL);
    +
    +  assertTrue(goog.fs.browserSupportsObjectUrls());
    +  assertEquals(objectUrl, goog.fs.createObjectUrl());
    +
    +  stubs.reset();
    +}
    +
    +
    +function testGetBlobThrowsError() {
    +  stubs.remove(goog.global, 'BlobBuilder');
    +  stubs.remove(goog.global, 'WebKitBlobBuilder');
    +  stubs.remove(goog.global, 'Blob');
    +
    +  try {
    +    goog.fs.getBlob();
    +    fail();
    +  } catch (e) {
    +    assertEquals('This browser doesn\'t seem to support creating Blobs',
    +        e.message);
    +  }
    +
    +  stubs.reset();
    +}
    +
    +
    +function testGetBlobWithProperties() {
    +  // Skip test if browser doesn't support Blob API.
    +  if (typeof(goog.global.Blob) != 'function') {
    +    return;
    +  }
    +
    +  var blob = goog.fs.getBlobWithProperties(['test'], 'text/test', 'native');
    +  assertEquals('text/test', blob.type);
    +}
    +
    +
    +function testGetBlobWithPropertiesThrowsError() {
    +  stubs.remove(goog.global, 'BlobBuilder');
    +  stubs.remove(goog.global, 'WebKitBlobBuilder');
    +  stubs.remove(goog.global, 'Blob');
    +
    +  try {
    +    goog.fs.getBlobWithProperties();
    +    fail();
    +  } catch (e) {
    +    assertEquals('This browser doesn\'t seem to support creating Blobs',
    +        e.message);
    +  }
    +
    +  stubs.reset();
    +}
    +
    +
    +function testGetBlobWithPropertiesUsingBlobBuilder() {
    +  function BlobBuilder() {
    +    this.parts = [];
    +    this.append = function(value, endings) {
    +      this.parts.push({value: value, endings: endings});
    +    };
    +    this.getBlob = function(type) {
    +      return {type: type, builder: this};
    +    };
    +  }
    +  stubs.set(goog.global, 'BlobBuilder', BlobBuilder);
    +
    +  var blob = goog.fs.getBlobWithProperties(['test'], 'text/test', 'native');
    +  assertEquals('text/test', blob.type);
    +  assertEquals('test', blob.builder.parts[0].value);
    +  assertEquals('native', blob.builder.parts[0].endings);
    +
    +  stubs.reset();
    +}
    +
    +
    +function loadTestDir() {
    +  return deferredFs.branch().addCallback(function(fs) {
    +    return fs.getRoot().getDirectory(
    +        TEST_DIR, goog.fs.DirectoryEntry.Behavior.CREATE);
    +  });
    +}
    +
    +function loadFile(filename, behavior) {
    +  return loadTestDir().addCallback(function(dir) {
    +    return dir.getFile(filename, behavior);
    +  });
    +}
    +
    +function loadDirectory(filename, behavior) {
    +  return loadTestDir().addCallback(function(dir) {
    +    return dir.getDirectory(filename, behavior);
    +  });
    +}
    +
    +function startWrite(content, file) {
    +  return file.createWriter().
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) {
    +        writer.write(goog.fs.getBlob(content));
    +        return writer;
    +      }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING));
    +}
    +
    +function waitForEvent(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(target, type, d.callback, false, d);
    +  return d;
    +}
    +
    +function writeToFile(content, file) {
    +  return startWrite(content, file).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      addCallback(function() { return file; });
    +}
    +
    +function checkFileContent(content, file) {
    +  return checkFileContentAs(content, 'Text', undefined, file);
    +}
    +
    +function checkFileContentWithEncoding(content, encoding, file) {
    +  checkFileContentAs(content, 'Text', encoding, file);
    +}
    +
    +function checkFileContentAs(content, filetype, encoding, file) {
    +  return file.file().
    +      addCallback(function(blob) {
    +        return goog.fs.FileReader['readAs' + filetype](blob, encoding);
    +      }).
    +      addCallback(goog.partial(checkEquals, content));
    +}
    +
    +function checkEquals(a, b) {
    +  if (a instanceof ArrayBuffer && b instanceof ArrayBuffer) {
    +    assertEquals(a.byteLength, b.byteLength);
    +    var viewA = new DataView(a);
    +    var viewB = new DataView(b);
    +    for (var i = 0; i < a.byteLength; i++) {
    +      assertEquals(viewA.getUint8(i), viewB.getUint8(i));
    +    }
    +  } else {
    +    assertEquals(a, b);
    +  }
    +}
    +
    +function checkFileRemoved(filename) {
    +  return loadFile(filename).
    +      addCallback(goog.partial(fail, 'expected file to be removed')).
    +      addErrback(function(err) {
    +        assertEquals(err.code, goog.fs.Error.ErrorCode.NOT_FOUND);
    +        return true; // Go back to callback path
    +      });
    +}
    +
    +function checkReadyState(expectedState, writer) {
    +  assertEquals(expectedState, writer.getReadyState());
    +}
    +
    +function splitArgs(fn) {
    +  return function(args) { return fn(args[0], args[1]); };
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fs/progressevent.js b/mapbox-gl/closure-library/closure/goog/fs/progressevent.js
    new file mode 100644
    index 0000000000..b0695bed9d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fs/progressevent.js
    @@ -0,0 +1,69 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for the HTML5 File ProgressEvent objects.
    + *
    + */
    +goog.provide('goog.fs.ProgressEvent');
    +
    +goog.require('goog.events.Event');
    +
    +
    +
    +/**
    + * A wrapper for the progress events emitted by the File APIs.
    + *
    + * @param {!ProgressEvent} event The underlying event object.
    + * @param {!Object} target The file access object emitting the event.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.fs.ProgressEvent = function(event, target) {
    +  goog.fs.ProgressEvent.base(this, 'constructor', event.type, target);
    +
    +  /**
    +   * The underlying event object.
    +   * @type {!ProgressEvent}
    +   * @private
    +   */
    +  this.event_ = event;
    +};
    +goog.inherits(goog.fs.ProgressEvent, goog.events.Event);
    +
    +
    +/**
    + * @return {boolean} Whether or not the total size of the of the file being
    + *     saved is known.
    + */
    +goog.fs.ProgressEvent.prototype.isLengthComputable = function() {
    +  return this.event_.lengthComputable;
    +};
    +
    +
    +/**
    + * @return {number} The number of bytes saved so far.
    + */
    +goog.fs.ProgressEvent.prototype.getLoaded = function() {
    +  return this.event_.loaded;
    +};
    +
    +
    +/**
    + * @return {number} The total number of bytes in the file being saved.
    + */
    +goog.fs.ProgressEvent.prototype.getTotal = function() {
    +  return this.event_.total;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/functions/functions.js b/mapbox-gl/closure-library/closure/goog/functions/functions.js
    new file mode 100644
    index 0000000000..d7ccf403f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/functions/functions.js
    @@ -0,0 +1,332 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for creating functions. Loosely inspired by the
    + * java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +
    +goog.provide('goog.functions');
    +
    +
    +/**
    + * Creates a function that always returns the same value.
    + * @param {T} retValue The value to return.
    + * @return {function():T} The new function.
    + * @template T
    + */
    +goog.functions.constant = function(retValue) {
    +  return function() {
    +    return retValue;
    +  };
    +};
    +
    +
    +/**
    + * Always returns false.
    + * @type {function(...): boolean}
    + */
    +goog.functions.FALSE = goog.functions.constant(false);
    +
    +
    +/**
    + * Always returns true.
    + * @type {function(...): boolean}
    + */
    +goog.functions.TRUE = goog.functions.constant(true);
    +
    +
    +/**
    + * Always returns NULL.
    + * @type {function(...): null}
    + */
    +goog.functions.NULL = goog.functions.constant(null);
    +
    +
    +/**
    + * A simple function that returns the first argument of whatever is passed
    + * into it.
    + * @param {T=} opt_returnValue The single value that will be returned.
    + * @param {...*} var_args Optional trailing arguments. These are ignored.
    + * @return {T} The first argument passed in, or undefined if nothing was passed.
    + * @template T
    + */
    +goog.functions.identity = function(opt_returnValue, var_args) {
    +  return opt_returnValue;
    +};
    +
    +
    +/**
    + * Creates a function that always throws an error with the given message.
    + * @param {string} message The error message.
    + * @return {!Function} The error-throwing function.
    + */
    +goog.functions.error = function(message) {
    +  return function() {
    +    throw Error(message);
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that throws the given object.
    + * @param {*} err An object to be thrown.
    + * @return {!Function} The error-throwing function.
    + */
    +goog.functions.fail = function(err) {
    +  return function() {
    +    throw err;
    +  }
    +};
    +
    +
    +/**
    + * Given a function, create a function that keeps opt_numArgs arguments and
    + * silently discards all additional arguments.
    + * @param {Function} f The original function.
    + * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.
    + * @return {!Function} A version of f that only keeps the first opt_numArgs
    + *     arguments.
    + */
    +goog.functions.lock = function(f, opt_numArgs) {
    +  opt_numArgs = opt_numArgs || 0;
    +  return function() {
    +    return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs));
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that returns its nth argument.
    + * @param {number} n The position of the return argument.
    + * @return {!Function} A new function.
    + */
    +goog.functions.nth = function(n) {
    +  return function() {
    +    return arguments[n];
    +  };
    +};
    +
    +
    +/**
    + * Given a function, create a new function that swallows its return value
    + * and replaces it with a new one.
    + * @param {Function} f A function.
    + * @param {T} retValue A new return value.
    + * @return {function(...?):T} A new function.
    + * @template T
    + */
    +goog.functions.withReturnValue = function(f, retValue) {
    +  return goog.functions.sequence(f, goog.functions.constant(retValue));
    +};
    +
    +
    +/**
    + * Creates a function that returns whether its arguement equals the given value.
    + *
    + * Example:
    + * var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));
    + *
    + * @param {*} value The value to compare to.
    + * @param {boolean=} opt_useLooseComparison Whether to use a loose (==)
    + *     comparison rather than a strict (===) one. Defaults to false.
    + * @return {function(*):boolean} The new function.
    + */
    +goog.functions.equalTo = function(value, opt_useLooseComparison) {
    +  return function(other) {
    +    return opt_useLooseComparison ? (value == other) : (value === other);
    +  };
    +};
    +
    +
    +/**
    + * Creates the composition of the functions passed in.
    + * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
    + * @param {function(...?):T} fn The final function.
    + * @param {...Function} var_args A list of functions.
    + * @return {function(...?):T} The composition of all inputs.
    + * @template T
    + */
    +goog.functions.compose = function(fn, var_args) {
    +  var functions = arguments;
    +  var length = functions.length;
    +  return function() {
    +    var result;
    +    if (length) {
    +      result = functions[length - 1].apply(this, arguments);
    +    }
    +
    +    for (var i = length - 2; i >= 0; i--) {
    +      result = functions[i].call(this, result);
    +    }
    +    return result;
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that calls the functions passed in in sequence, and
    + * returns the value of the last function. For example,
    + * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
    + * @param {...Function} var_args A list of functions.
    + * @return {!Function} A function that calls all inputs in sequence.
    + */
    +goog.functions.sequence = function(var_args) {
    +  var functions = arguments;
    +  var length = functions.length;
    +  return function() {
    +    var result;
    +    for (var i = 0; i < length; i++) {
    +      result = functions[i].apply(this, arguments);
    +    }
    +    return result;
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that returns true if each of its components evaluates
    + * to true. The components are evaluated in order, and the evaluation will be
    + * short-circuited as soon as a function returns false.
    + * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
    + * @param {...Function} var_args A list of functions.
    + * @return {function(...?):boolean} A function that ANDs its component
    + *      functions.
    + */
    +goog.functions.and = function(var_args) {
    +  var functions = arguments;
    +  var length = functions.length;
    +  return function() {
    +    for (var i = 0; i < length; i++) {
    +      if (!functions[i].apply(this, arguments)) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that returns true if any of its components evaluates
    + * to true. The components are evaluated in order, and the evaluation will be
    + * short-circuited as soon as a function returns true.
    + * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
    + * @param {...Function} var_args A list of functions.
    + * @return {function(...?):boolean} A function that ORs its component
    + *    functions.
    + */
    +goog.functions.or = function(var_args) {
    +  var functions = arguments;
    +  var length = functions.length;
    +  return function() {
    +    for (var i = 0; i < length; i++) {
    +      if (functions[i].apply(this, arguments)) {
    +        return true;
    +      }
    +    }
    +    return false;
    +  };
    +};
    +
    +
    +/**
    + * Creates a function that returns the Boolean opposite of a provided function.
    + * For example, (goog.functions.not(f))(x) is equivalent to !f(x).
    + * @param {!Function} f The original function.
    + * @return {function(...?):boolean} A function that delegates to f and returns
    + * opposite.
    + */
    +goog.functions.not = function(f) {
    +  return function() {
    +    return !f.apply(this, arguments);
    +  };
    +};
    +
    +
    +/**
    + * Generic factory function to construct an object given the constructor
    + * and the arguments. Intended to be bound to create object factories.
    + *
    + * Example:
    + *
    + * var factory = goog.partial(goog.functions.create, Class);
    + *
    + * @param {function(new:T, ...)} constructor The constructor for the Object.
    + * @param {...*} var_args The arguments to be passed to the constructor.
    + * @return {T} A new instance of the class given in {@code constructor}.
    + * @template T
    + */
    +goog.functions.create = function(constructor, var_args) {
    +  /**
    +   * @constructor
    +   * @final
    +   */
    +  var temp = function() {};
    +  temp.prototype = constructor.prototype;
    +
    +  // obj will have constructor's prototype in its chain and
    +  // 'obj instanceof constructor' will be true.
    +  var obj = new temp();
    +
    +  // obj is initialized by constructor.
    +  // arguments is only array-like so lacks shift(), but can be used with
    +  // the Array prototype function.
    +  constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
    +  return obj;
    +};
    +
    +
    +/**
    + * @define {boolean} Whether the return value cache should be used.
    + *    This should only be used to disable caches when testing.
    + */
    +goog.define('goog.functions.CACHE_RETURN_VALUE', true);
    +
    +
    +/**
    + * Gives a wrapper function that caches the return value of a parameterless
    + * function when first called.
    + *
    + * When called for the first time, the given function is called and its
    + * return value is cached (thus this is only appropriate for idempotent
    + * functions).  Subsequent calls will return the cached return value. This
    + * allows the evaluation of expensive functions to be delayed until first used.
    + *
    + * To cache the return values of functions with parameters, see goog.memoize.
    + *
    + * @param {!function():T} fn A function to lazily evaluate.
    + * @return {!function():T} A wrapped version the function.
    + * @template T
    + */
    +goog.functions.cacheReturnValue = function(fn) {
    +  var called = false;
    +  var value;
    +
    +  return function() {
    +    if (!goog.functions.CACHE_RETURN_VALUE) {
    +      return fn();
    +    }
    +
    +    if (!called) {
    +      value = fn();
    +      called = true;
    +    }
    +
    +    return value;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/functions/functions_test.html b/mapbox-gl/closure-library/closure/goog/functions/functions_test.html
    new file mode 100644
    index 0000000000..0b45c154fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/functions/functions_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.functions</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.functionsTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/functions/functions_test.js b/mapbox-gl/closure-library/closure/goog/functions/functions_test.js
    new file mode 100644
    index 0000000000..411f1b3c0a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/functions/functions_test.js
    @@ -0,0 +1,313 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.functions.
    + */
    +
    +goog.provide('goog.functionsTest');
    +goog.setTestOnly('goog.functionsTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +
    +var fTrue = makeCallOrderLogger('fTrue', true);
    +var gFalse = makeCallOrderLogger('gFalse', false);
    +var hTrue = makeCallOrderLogger('hTrue', true);
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  callOrder = [];
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testTrue() {
    +  assertTrue(goog.functions.TRUE());
    +}
    +
    +function testFalse() {
    +  assertFalse(goog.functions.FALSE());
    +}
    +
    +function testLock() {
    +  function add(var_args) {
    +    var result = 0;
    +    for (var i = 0; i < arguments.length; i++) {
    +      result += arguments[i];
    +    }
    +    return result;
    +  }
    +
    +  assertEquals(6, add(1, 2, 3));
    +  assertEquals(0, goog.functions.lock(add)(1, 2, 3));
    +  assertEquals(3, goog.functions.lock(add, 2)(1, 2, 3));
    +  assertEquals(6, goog.partial(add, 1, 2)(3));
    +  assertEquals(3, goog.functions.lock(goog.partial(add, 1, 2))(3));
    +}
    +
    +function testNth() {
    +  assertEquals(1, goog.functions.nth(0)(1));
    +  assertEquals(2, goog.functions.nth(1)(1, 2));
    +  assertEquals('a', goog.functions.nth(0)('a', 'b'));
    +  assertEquals(undefined, goog.functions.nth(0)());
    +  assertEquals(undefined, goog.functions.nth(1)(true));
    +  assertEquals(undefined, goog.functions.nth(-1)());
    +}
    +
    +function testIdentity() {
    +  assertEquals(3, goog.functions.identity(3));
    +  assertEquals(3, goog.functions.identity(3, 4, 5, 6));
    +  assertEquals('Hi there', goog.functions.identity('Hi there'));
    +  assertEquals(null, goog.functions.identity(null));
    +  assertEquals(undefined, goog.functions.identity());
    +
    +  var arr = [1, 'b', null];
    +  assertEquals(arr, goog.functions.identity(arr));
    +  var obj = {a: 'ay', b: 'bee', c: 'see'};
    +  assertEquals(obj, goog.functions.identity(obj));
    +}
    +
    +function testConstant() {
    +  assertEquals(3, goog.functions.constant(3)());
    +  assertEquals(undefined, goog.functions.constant()());
    +}
    +
    +function testError() {
    +  var f = goog.functions.error('x');
    +  var e = assertThrows(
    +      'A function created by goog.functions.error must throw an error', f);
    +  assertEquals('x', e.message);
    +}
    +
    +function testFail() {
    +  var obj = {};
    +  var f = goog.functions.fail(obj);
    +  var e = assertThrows(
    +      'A function created by goog.functions.raise must throw its input', f);
    +  assertEquals(obj, e);
    +}
    +
    +function testCompose() {
    +  var add2 = function(x) {
    +    return x + 2;
    +  };
    +
    +  var doubleValue = function(x) {
    +    return x * 2;
    +  };
    +
    +  assertEquals(6, goog.functions.compose(doubleValue, add2)(1));
    +  assertEquals(4, goog.functions.compose(add2, doubleValue)(1));
    +  assertEquals(6, goog.functions.compose(add2, add2, doubleValue)(1));
    +  assertEquals(12,
    +      goog.functions.compose(doubleValue, add2, add2, doubleValue)(1));
    +  assertUndefined(goog.functions.compose()(1));
    +  assertEquals(3, goog.functions.compose(add2)(1));
    +
    +  var add2Numbers = function(x, y) {
    +    return x + y;
    +  };
    +  assertEquals(17, goog.functions.compose(add2Numbers)(10, 7));
    +  assertEquals(34, goog.functions.compose(doubleValue, add2Numbers)(10, 7));
    +}
    +
    +function testAdd() {
    +  assertUndefined(goog.functions.sequence()());
    +  assertCallOrderAndReset([]);
    +
    +  assert(goog.functions.sequence(fTrue)());
    +  assertCallOrderAndReset(['fTrue']);
    +
    +  assertFalse(goog.functions.sequence(fTrue, gFalse)());
    +  assertCallOrderAndReset(['fTrue', 'gFalse']);
    +
    +  assert(goog.functions.sequence(fTrue, gFalse, hTrue)());
    +  assertCallOrderAndReset(['fTrue', 'gFalse', 'hTrue']);
    +
    +  assert(goog.functions.sequence(goog.functions.identity)(true));
    +  assertFalse(goog.functions.sequence(goog.functions.identity)(false));
    +}
    +
    +function testAnd() {
    +  // the return value is unspecified for an empty and
    +  goog.functions.and()();
    +  assertCallOrderAndReset([]);
    +
    +  assert(goog.functions.and(fTrue)());
    +  assertCallOrderAndReset(['fTrue']);
    +
    +  assertFalse(goog.functions.and(fTrue, gFalse)());
    +  assertCallOrderAndReset(['fTrue', 'gFalse']);
    +
    +  assertFalse(goog.functions.and(fTrue, gFalse, hTrue)());
    +  assertCallOrderAndReset(['fTrue', 'gFalse']);
    +
    +  assert(goog.functions.and(goog.functions.identity)(true));
    +  assertFalse(goog.functions.and(goog.functions.identity)(false));
    +}
    +
    +function testOr() {
    +  // the return value is unspecified for an empty or
    +  goog.functions.or()();
    +  assertCallOrderAndReset([]);
    +
    +  assert(goog.functions.or(fTrue)());
    +  assertCallOrderAndReset(['fTrue']);
    +
    +  assert(goog.functions.or(fTrue, gFalse)());
    +  assertCallOrderAndReset(['fTrue']);
    +
    +  assert(goog.functions.or(fTrue, gFalse, hTrue)());
    +  assertCallOrderAndReset(['fTrue']);
    +
    +  assert(goog.functions.or(goog.functions.identity)(true));
    +  assertFalse(goog.functions.or(goog.functions.identity)(false));
    +}
    +
    +function testNot() {
    +  assertTrue(goog.functions.not(gFalse)());
    +  assertCallOrderAndReset(['gFalse']);
    +
    +  assertTrue(goog.functions.not(goog.functions.identity)(false));
    +  assertFalse(goog.functions.not(goog.functions.identity)(true));
    +
    +  var f = function(a, b) {
    +    assertEquals(1, a);
    +    assertEquals(2, b);
    +    return false;
    +  };
    +
    +  assertTrue(goog.functions.not(f)(1, 2));
    +}
    +
    +function testCreate(expectedArray) {
    +  var tempConstructor = function(a, b) {
    +    this.foo = a;
    +    this.bar = b;
    +  };
    +
    +  var factory = goog.partial(goog.functions.create, tempConstructor, 'baz');
    +  var instance = factory('qux');
    +
    +  assert(instance instanceof tempConstructor);
    +  assertEquals(instance.foo, 'baz');
    +  assertEquals(instance.bar, 'qux');
    +}
    +
    +function testWithReturnValue() {
    +  var obj = {};
    +  var f = function(a, b) {
    +    assertEquals(obj, this);
    +    assertEquals(1, a);
    +    assertEquals(2, b);
    +  };
    +  assertTrue(goog.functions.withReturnValue(f, true).call(obj, 1, 2));
    +  assertFalse(goog.functions.withReturnValue(f, false).call(obj, 1, 2));
    +}
    +
    +function testEqualTo() {
    +  assertTrue(goog.functions.equalTo(42)(42));
    +  assertFalse(goog.functions.equalTo(42)(13));
    +  assertFalse(goog.functions.equalTo(42)('a string'));
    +
    +  assertFalse(goog.functions.equalTo(42)('42'));
    +  assertTrue(goog.functions.equalTo(42, true)('42'));
    +
    +  assertTrue(goog.functions.equalTo(0)(0));
    +  assertFalse(goog.functions.equalTo(0)(''));
    +  assertFalse(goog.functions.equalTo(0)(1));
    +
    +  assertTrue(goog.functions.equalTo(0, true)(0));
    +  assertTrue(goog.functions.equalTo(0, true)(''));
    +  assertFalse(goog.functions.equalTo(0, true)(1));
    +}
    +
    +function makeCallOrderLogger(name, returnValue) {
    +  return function() {
    +    callOrder.push(name);
    +    return returnValue;
    +  };
    +}
    +
    +function assertCallOrderAndReset(expectedArray) {
    +  assertArrayEquals(expectedArray, callOrder);
    +  callOrder = [];
    +}
    +
    +function testCacheReturnValue() {
    +  var returnFive = function() {
    +    return 5;
    +  };
    +
    +  var recordedReturnFive = goog.testing.recordFunction(returnFive);
    +  var cachedRecordedReturnFive = goog.functions.cacheReturnValue(
    +      recordedReturnFive);
    +
    +  assertEquals(0, recordedReturnFive.getCallCount());
    +  assertEquals(5, cachedRecordedReturnFive());
    +  assertEquals(1, recordedReturnFive.getCallCount());
    +  assertEquals(5, cachedRecordedReturnFive());
    +  assertEquals(1, recordedReturnFive.getCallCount());
    +}
    +
    +
    +function testCacheReturnValueFlagEnabled() {
    +  var count = 0;
    +  var returnIncrementingInteger = function() {
    +    count++;
    +    return count;
    +  };
    +
    +  var recordedFunction = goog.testing.recordFunction(
    +      returnIncrementingInteger);
    +  var cachedRecordedFunction = goog.functions.cacheReturnValue(
    +      recordedFunction);
    +
    +  assertEquals(0, recordedFunction.getCallCount());
    +  assertEquals(1, cachedRecordedFunction());
    +  assertEquals(1, recordedFunction.getCallCount());
    +  assertEquals(1, cachedRecordedFunction());
    +  assertEquals(1, recordedFunction.getCallCount());
    +  assertEquals(1, cachedRecordedFunction());
    +}
    +
    +
    +function testCacheReturnValueFlagDisabled() {
    +  stubs.set(goog.functions, 'CACHE_RETURN_VALUE', false);
    +
    +  var count = 0;
    +  var returnIncrementingInteger = function() {
    +    count++;
    +    return count;
    +  };
    +
    +  var recordedFunction = goog.testing.recordFunction(
    +      returnIncrementingInteger);
    +  var cachedRecordedFunction = goog.functions.cacheReturnValue(
    +      recordedFunction);
    +
    +  assertEquals(0, recordedFunction.getCallCount());
    +  assertEquals(1, cachedRecordedFunction());
    +  assertEquals(1, recordedFunction.getCallCount());
    +  assertEquals(2, cachedRecordedFunction());
    +  assertEquals(2, recordedFunction.getCallCount());
    +  assertEquals(3, cachedRecordedFunction());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop.js b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop.js
    new file mode 100644
    index 0000000000..afd4af8c9f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop.js
    @@ -0,0 +1,1540 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Abstract Base Class for Drag and Drop.
    + *
    + * Provides functionality for implementing drag and drop classes. Also provides
    + * support classes and events.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.fx.AbstractDragDrop');
    +goog.provide('goog.fx.AbstractDragDrop.EventType');
    +goog.provide('goog.fx.DragDropEvent');
    +goog.provide('goog.fx.DragDropItem');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Abstract class that provides reusable functionality for implementing drag
    + * and drop functionality.
    + *
    + * This class also allows clients to define their own subtargeting function
    + * so that drop areas can have finer granularity then a singe element. This is
    + * accomplished by using a client provided function to map from element and
    + * coordinates to a subregion id.
    + *
    + * This class can also be made aware of scrollable containers that contain
    + * drop targets by calling addScrollableContainer. This will cause dnd to
    + * take changing scroll positions into account while a drag is occuring.
    + *
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.fx.AbstractDragDrop = function() {
    +  goog.fx.AbstractDragDrop.base(this, 'constructor');
    +
    +  /**
    +   * List of items that makes up the drag source or drop target.
    +   * @type {Array<goog.fx.DragDropItem>}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.items_ = [];
    +
    +  /**
    +   * List of associated drop targets.
    +   * @type {Array<goog.fx.AbstractDragDrop>}
    +   * @private
    +   */
    +  this.targets_ = [];
    +
    +  /**
    +   * Scrollable containers to account for during drag
    +   * @type {Array<goog.fx.ScrollableContainer_>}
    +   * @private
    +   */
    +  this.scrollableContainers_ = [];
    +
    +};
    +goog.inherits(goog.fx.AbstractDragDrop, goog.events.EventTarget);
    +
    +
    +/**
    + * Minimum size (in pixels) for a dummy target. If the box for the target is
    + * less than the specified size it's not created.
    + * @type {number}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ = 10;
    +
    +
    +/**
    + * Flag indicating if it's a drag source, set by addTarget.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.isSource_ = false;
    +
    +
    +/**
    + * Flag indicating if it's a drop target, set when added as target to another
    + * DragDrop object.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.isTarget_ = false;
    +
    +
    +/**
    + * Subtargeting function accepting args:
    + * (goog.fx.DragDropItem, goog.math.Box, number, number)
    + * @type {Function}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.subtargetFunction_;
    +
    +
    +/**
    + * Last active subtarget.
    + * @type {Object}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.activeSubtarget_;
    +
    +
    +/**
    + * Class name to add to source elements being dragged. Set by setDragClass.
    + * @type {?string}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.dragClass_;
    +
    +
    +/**
    + * Class name to add to source elements. Set by setSourceClass.
    + * @type {?string}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.sourceClass_;
    +
    +
    +/**
    + * Class name to add to target elements. Set by setTargetClass.
    + * @type {?string}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.targetClass_;
    +
    +
    +/**
    + * The SCROLL event target used to make drag element follow scrolling.
    + * @type {EventTarget}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.scrollTarget_;
    +
    +
    +/**
    + * Dummy target, {@see maybeCreateDummyTargetForPosition_}.
    + * @type {goog.fx.ActiveDropTarget_}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.dummyTarget_;
    +
    +
    +/**
    + * Whether the object has been initialized.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.initialized_ = false;
    +
    +
    +/**
    + * Constants for event names
    + * @const
    + */
    +goog.fx.AbstractDragDrop.EventType = {
    +  DRAGOVER: 'dragover',
    +  DRAGOUT: 'dragout',
    +  DRAG: 'drag',
    +  DROP: 'drop',
    +  DRAGSTART: 'dragstart',
    +  DRAGEND: 'dragend'
    +};
    +
    +
    +/**
    + * Constant for distance threshold, in pixels, an element has to be moved to
    + * initiate a drag operation.
    + * @type {number}
    + */
    +goog.fx.AbstractDragDrop.initDragDistanceThreshold = 5;
    +
    +
    +/**
    + * Set class to add to source elements being dragged.
    + *
    + * @param {string} className Class to be added.  Must be a single, valid
    + *     classname.
    + */
    +goog.fx.AbstractDragDrop.prototype.setDragClass = function(className) {
    +  this.dragClass_ = className;
    +};
    +
    +
    +/**
    + * Set class to add to source elements.
    + *
    + * @param {string} className Class to be added.  Must be a single, valid
    + *     classname.
    + */
    +goog.fx.AbstractDragDrop.prototype.setSourceClass = function(className) {
    +  this.sourceClass_ = className;
    +};
    +
    +
    +/**
    + * Set class to add to target elements.
    + *
    + * @param {string} className Class to be added.  Must be a single, valid
    + *     classname.
    + */
    +goog.fx.AbstractDragDrop.prototype.setTargetClass = function(className) {
    +  this.targetClass_ = className;
    +};
    +
    +
    +/**
    + * Whether the control has been initialized.
    + *
    + * @return {boolean} True if it's been initialized.
    + */
    +goog.fx.AbstractDragDrop.prototype.isInitialized = function() {
    +  return this.initialized_;
    +};
    +
    +
    +/**
    + * Add item to drag object.
    + *
    + * @param {Element|string} element Dom Node, or string representation of node
    + *     id, to be used as drag source/drop target.
    + * @throws Error Thrown if called on instance of abstract class
    + */
    +goog.fx.AbstractDragDrop.prototype.addItem = goog.abstractMethod;
    +
    +
    +/**
    + * Associate drop target with drag element.
    + *
    + * @param {goog.fx.AbstractDragDrop} target Target to add.
    + */
    +goog.fx.AbstractDragDrop.prototype.addTarget = function(target) {
    +  this.targets_.push(target);
    +  target.isTarget_ = true;
    +  this.isSource_ = true;
    +};
    +
    +
    +/**
    + * Sets the SCROLL event target to make drag element follow scrolling.
    + *
    + * @param {EventTarget} scrollTarget The element that dispatches SCROLL events.
    + */
    +goog.fx.AbstractDragDrop.prototype.setScrollTarget = function(scrollTarget) {
    +  this.scrollTarget_ = scrollTarget;
    +};
    +
    +
    +/**
    + * Initialize drag and drop functionality for sources/targets already added.
    + * Sources/targets added after init has been called will initialize themselves
    + * one by one.
    + */
    +goog.fx.AbstractDragDrop.prototype.init = function() {
    +  if (this.initialized_) {
    +    return;
    +  }
    +  for (var item, i = 0; item = this.items_[i]; i++) {
    +    this.initItem(item);
    +  }
    +
    +  this.initialized_ = true;
    +};
    +
    +
    +/**
    + * Initializes a single item.
    + *
    + * @param {goog.fx.DragDropItem} item Item to initialize.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.initItem = function(item) {
    +  if (this.isSource_) {
    +    goog.events.listen(item.element, goog.events.EventType.MOUSEDOWN,
    +                       item.mouseDown_, false, item);
    +    if (this.sourceClass_) {
    +      goog.dom.classlist.add(
    +          goog.asserts.assert(item.element), this.sourceClass_);
    +    }
    +  }
    +
    +  if (this.isTarget_ && this.targetClass_) {
    +    goog.dom.classlist.add(
    +        goog.asserts.assert(item.element), this.targetClass_);
    +  }
    +};
    +
    +
    +/**
    + * Called when removing an item. Removes event listeners and classes.
    + *
    + * @param {goog.fx.DragDropItem} item Item to dispose.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.disposeItem = function(item) {
    +  if (this.isSource_) {
    +    goog.events.unlisten(item.element, goog.events.EventType.MOUSEDOWN,
    +                         item.mouseDown_, false, item);
    +    if (this.sourceClass_) {
    +      goog.dom.classlist.remove(
    +          goog.asserts.assert(item.element), this.sourceClass_);
    +    }
    +  }
    +  if (this.isTarget_ && this.targetClass_) {
    +    goog.dom.classlist.remove(
    +        goog.asserts.assert(item.element), this.targetClass_);
    +  }
    +  item.dispose();
    +};
    +
    +
    +/**
    + * Removes all items.
    + */
    +goog.fx.AbstractDragDrop.prototype.removeItems = function() {
    +  for (var item, i = 0; item = this.items_[i]; i++) {
    +    this.disposeItem(item);
    +  }
    +  this.items_.length = 0;
    +};
    +
    +
    +/**
    + * Starts a drag event for an item if the mouse button stays pressed and the
    + * cursor moves a few pixels. Allows dragging of items without first having to
    + * register them with addItem.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse down event.
    + * @param {goog.fx.DragDropItem} item Item that's being dragged.
    + */
    +goog.fx.AbstractDragDrop.prototype.maybeStartDrag = function(event, item) {
    +  item.maybeStartDrag_(event, item.element);
    +};
    +
    +
    +/**
    + * Event handler that's used to start drag.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse move event.
    + * @param {goog.fx.DragDropItem} item Item that's being dragged.
    + */
    +goog.fx.AbstractDragDrop.prototype.startDrag = function(event, item) {
    +
    +  // Prevent a new drag operation from being started if another one is already
    +  // in progress (could happen if the mouse was released outside of the
    +  // document).
    +  if (this.dragItem_) {
    +    return;
    +  }
    +
    +  this.dragItem_ = item;
    +
    +  // Dispatch DRAGSTART event
    +  var dragStartEvent = new goog.fx.DragDropEvent(
    +      goog.fx.AbstractDragDrop.EventType.DRAGSTART, this, this.dragItem_);
    +  if (this.dispatchEvent(dragStartEvent) == false) {
    +    this.dragItem_ = null;
    +    return;
    +  }
    +
    +  // Get the source element and create a drag element for it.
    +  var el = item.getCurrentDragElement();
    +  this.dragEl_ = this.createDragElement(el);
    +  var doc = goog.dom.getOwnerDocument(el);
    +  doc.body.appendChild(this.dragEl_);
    +
    +  this.dragger_ = this.createDraggerFor(el, this.dragEl_, event);
    +  this.dragger_.setScrollTarget(this.scrollTarget_);
    +
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG,
    +                     this.moveDrag_, false, this);
    +
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END,
    +                     this.endDrag, false, this);
    +
    +  // IE may issue a 'selectstart' event when dragging over an iframe even when
    +  // default mousemove behavior is suppressed. If the default selectstart
    +  // behavior is not suppressed, elements dragged over will show as selected.
    +  goog.events.listen(doc.body, goog.events.EventType.SELECTSTART,
    +                     this.suppressSelect_);
    +
    +  this.recalculateDragTargets();
    +  this.recalculateScrollableContainers();
    +  this.activeTarget_ = null;
    +  this.initScrollableContainerListeners_();
    +  this.dragger_.startDrag(event);
    +
    +  event.preventDefault();
    +};
    +
    +
    +/**
    + * Recalculates the geometry of this source's drag targets.  Call this
    + * if the position or visibility of a drag target has changed during
    + * a drag, or if targets are added or removed.
    + *
    + * TODO(user): this is an expensive operation;  more efficient APIs
    + * may be necessary.
    + */
    +goog.fx.AbstractDragDrop.prototype.recalculateDragTargets = function() {
    +  this.targetList_ = [];
    +  for (var target, i = 0; target = this.targets_[i]; i++) {
    +    for (var itm, j = 0; itm = target.items_[j]; j++) {
    +      this.addDragTarget_(target, itm);
    +    }
    +  }
    +  if (!this.targetBox_) {
    +    this.targetBox_ = new goog.math.Box(0, 0, 0, 0);
    +  }
    +};
    +
    +
    +/**
    + * Recalculates the current scroll positions of scrollable containers and
    + * allocates targets. Call this if the position of a container changed or if
    + * targets are added or removed.
    + */
    +goog.fx.AbstractDragDrop.prototype.recalculateScrollableContainers =
    +    function() {
    +  var container, i, j, target;
    +  for (i = 0; container = this.scrollableContainers_[i]; i++) {
    +    container.containedTargets_ = [];
    +    container.savedScrollLeft_ = container.element_.scrollLeft;
    +    container.savedScrollTop_ = container.element_.scrollTop;
    +    var pos = goog.style.getPageOffset(container.element_);
    +    var size = goog.style.getSize(container.element_);
    +    container.box_ = new goog.math.Box(pos.y, pos.x + size.width,
    +                                       pos.y + size.height, pos.x);
    +  }
    +
    +  for (i = 0; target = this.targetList_[i]; i++) {
    +    for (j = 0; container = this.scrollableContainers_[j]; j++) {
    +      if (goog.dom.contains(container.element_, target.element_)) {
    +        container.containedTargets_.push(target);
    +        target.scrollableContainer_ = container;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Creates the Dragger for the drag element.
    + * @param {Element} sourceEl Drag source element.
    + * @param {Element} el the element created by createDragElement().
    + * @param {goog.events.BrowserEvent} event Mouse down event for start of drag.
    + * @return {!goog.fx.Dragger} The new Dragger.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.createDraggerFor =
    +    function(sourceEl, el, event) {
    +  // Position the drag element.
    +  var pos = this.getDragElementPosition(sourceEl, el, event);
    +  el.style.position = 'absolute';
    +  el.style.left = pos.x + 'px';
    +  el.style.top = pos.y + 'px';
    +  return new goog.fx.Dragger(el);
    +};
    +
    +
    +/**
    + * Event handler that's used to stop drag. Fires a drop event if over a valid
    + * target.
    + *
    + * @param {goog.fx.DragEvent} event Drag event.
    + */
    +goog.fx.AbstractDragDrop.prototype.endDrag = function(event) {
    +  var activeTarget = event.dragCanceled ? null : this.activeTarget_;
    +  if (activeTarget && activeTarget.target_) {
    +    var clientX = event.clientX;
    +    var clientY = event.clientY;
    +    var scroll = this.getScrollPos();
    +    var x = clientX + scroll.x;
    +    var y = clientY + scroll.y;
    +
    +    var subtarget;
    +    // If a subtargeting function is enabled get the current subtarget
    +    if (this.subtargetFunction_) {
    +      subtarget = this.subtargetFunction_(activeTarget.item_,
    +          activeTarget.box_, x, y);
    +    }
    +
    +    var dragEvent = new goog.fx.DragDropEvent(
    +        goog.fx.AbstractDragDrop.EventType.DRAG, this, this.dragItem_,
    +        activeTarget.target_, activeTarget.item_, activeTarget.element_,
    +        clientX, clientY, x, y);
    +    this.dispatchEvent(dragEvent);
    +
    +    var dropEvent = new goog.fx.DragDropEvent(
    +        goog.fx.AbstractDragDrop.EventType.DROP, this, this.dragItem_,
    +        activeTarget.target_, activeTarget.item_, activeTarget.element_,
    +        clientX, clientY, x, y, subtarget);
    +    activeTarget.target_.dispatchEvent(dropEvent);
    +  }
    +
    +  var dragEndEvent = new goog.fx.DragDropEvent(
    +      goog.fx.AbstractDragDrop.EventType.DRAGEND, this, this.dragItem_);
    +  this.dispatchEvent(dragEndEvent);
    +
    +  goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.DRAG,
    +                       this.moveDrag_, false, this);
    +  goog.events.unlisten(this.dragger_, goog.fx.Dragger.EventType.END,
    +                       this.endDrag, false, this);
    +  var doc = goog.dom.getOwnerDocument(this.dragItem_.getCurrentDragElement());
    +  goog.events.unlisten(doc.body, goog.events.EventType.SELECTSTART,
    +                       this.suppressSelect_);
    +
    +
    +  this.afterEndDrag(this.activeTarget_ ? this.activeTarget_.item_ : null);
    +};
    +
    +
    +/**
    + * Called after a drag operation has finished.
    + *
    + * @param {goog.fx.DragDropItem=} opt_dropTarget Target for successful drop.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.afterEndDrag = function(opt_dropTarget) {
    +  this.disposeDrag();
    +};
    +
    +
    +/**
    + * Called once a drag operation has finished. Removes event listeners and
    + * elements.
    + *
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.disposeDrag = function() {
    +  this.disposeScrollableContainerListeners_();
    +  this.dragger_.dispose();
    +
    +  goog.dom.removeNode(this.dragEl_);
    +  delete this.dragItem_;
    +  delete this.dragEl_;
    +  delete this.dragger_;
    +  delete this.targetList_;
    +  delete this.activeTarget_;
    +};
    +
    +
    +/**
    + * Event handler for drag events. Determines the active drop target, if any, and
    + * fires dragover and dragout events appropriately.
    + *
    + * @param {goog.fx.DragEvent} event Drag event.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.moveDrag_ = function(event) {
    +  var position = this.getEventPosition(event);
    +  var x = position.x;
    +  var y = position.y;
    +
    +  // Check if we're still inside the bounds of the active target, if not fire
    +  // a dragout event and proceed to find a new target.
    +  var activeTarget = this.activeTarget_;
    +
    +  var subtarget;
    +  if (activeTarget) {
    +    // If a subtargeting function is enabled get the current subtarget
    +    if (this.subtargetFunction_ && activeTarget.target_) {
    +      subtarget = this.subtargetFunction_(activeTarget.item_,
    +          activeTarget.box_, x, y);
    +    }
    +
    +    if (activeTarget.box_.contains(position) &&
    +        subtarget == this.activeSubtarget_) {
    +      return;
    +    }
    +
    +    if (activeTarget.target_) {
    +      var sourceDragOutEvent = new goog.fx.DragDropEvent(
    +          goog.fx.AbstractDragDrop.EventType.DRAGOUT, this, this.dragItem_,
    +          activeTarget.target_, activeTarget.item_, activeTarget.element_);
    +      this.dispatchEvent(sourceDragOutEvent);
    +
    +      // The event should be dispatched the by target DragDrop so that the
    +      // target DragDrop can manage these events without having to know what
    +      // sources this is a target for.
    +      var targetDragOutEvent = new goog.fx.DragDropEvent(
    +          goog.fx.AbstractDragDrop.EventType.DRAGOUT,
    +          this,
    +          this.dragItem_,
    +          activeTarget.target_,
    +          activeTarget.item_,
    +          activeTarget.element_,
    +          undefined,
    +          undefined,
    +          undefined,
    +          undefined,
    +          this.activeSubtarget_);
    +      activeTarget.target_.dispatchEvent(targetDragOutEvent);
    +    }
    +    this.activeSubtarget_ = subtarget;
    +    this.activeTarget_ = null;
    +  }
    +
    +  // Check if inside target box
    +  if (this.targetBox_.contains(position)) {
    +    // Search for target and fire a dragover event if found
    +    activeTarget = this.activeTarget_ = this.getTargetFromPosition_(position);
    +    if (activeTarget && activeTarget.target_) {
    +      // If a subtargeting function is enabled get the current subtarget
    +      if (this.subtargetFunction_) {
    +        subtarget = this.subtargetFunction_(activeTarget.item_,
    +            activeTarget.box_, x, y);
    +      }
    +      var sourceDragOverEvent = new goog.fx.DragDropEvent(
    +          goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_,
    +          activeTarget.target_, activeTarget.item_, activeTarget.element_);
    +      sourceDragOverEvent.subtarget = subtarget;
    +      this.dispatchEvent(sourceDragOverEvent);
    +
    +      // The event should be dispatched by the target DragDrop so that the
    +      // target DragDrop can manage these events without having to know what
    +      // sources this is a target for.
    +      var targetDragOverEvent = new goog.fx.DragDropEvent(
    +          goog.fx.AbstractDragDrop.EventType.DRAGOVER, this, this.dragItem_,
    +          activeTarget.target_, activeTarget.item_, activeTarget.element_,
    +          event.clientX, event.clientY, undefined, undefined, subtarget);
    +      activeTarget.target_.dispatchEvent(targetDragOverEvent);
    +
    +    } else if (!activeTarget) {
    +      // If no target was found create a dummy one so we won't have to iterate
    +      // over all possible targets for every move event.
    +      this.activeTarget_ = this.maybeCreateDummyTargetForPosition_(x, y);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Event handler for suppressing selectstart events. Selecting should be
    + * disabled while dragging.
    + *
    + * @param {goog.events.Event} event The selectstart event to suppress.
    + * @return {boolean} Whether to perform default behavior.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.suppressSelect_ = function(event) {
    +  return false;
    +};
    +
    +
    +/**
    + * Sets up listeners for the scrollable containers that keep track of their
    + * scroll positions.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.initScrollableContainerListeners_ =
    +    function() {
    +  var container, i;
    +  for (i = 0; container = this.scrollableContainers_[i]; i++) {
    +    goog.events.listen(container.element_, goog.events.EventType.SCROLL,
    +        this.containerScrollHandler_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Cleans up the scrollable container listeners.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.disposeScrollableContainerListeners_ =
    +    function() {
    +  for (var i = 0, container; container = this.scrollableContainers_[i]; i++) {
    +    goog.events.unlisten(container.element_, 'scroll',
    +        this.containerScrollHandler_, false, this);
    +    container.containedTargets_ = [];
    +  }
    +};
    +
    +
    +/**
    + * Makes drag and drop aware of a target container that could scroll mid drag.
    + * @param {Element} element The scroll container.
    + */
    +goog.fx.AbstractDragDrop.prototype.addScrollableContainer = function(element) {
    +  this.scrollableContainers_.push(new goog.fx.ScrollableContainer_(element));
    +};
    +
    +
    +/**
    + * Removes all scrollable containers.
    + */
    +goog.fx.AbstractDragDrop.prototype.removeAllScrollableContainers = function() {
    +  this.disposeScrollableContainerListeners_();
    +  this.scrollableContainers_ = [];
    +};
    +
    +
    +/**
    + * Event handler for containers scrolling.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.containerScrollHandler_ = function(e) {
    +  for (var i = 0, container; container = this.scrollableContainers_[i]; i++) {
    +    if (e.target == container.element_) {
    +      var deltaTop = container.savedScrollTop_ - container.element_.scrollTop;
    +      var deltaLeft =
    +          container.savedScrollLeft_ - container.element_.scrollLeft;
    +      container.savedScrollTop_ = container.element_.scrollTop;
    +      container.savedScrollLeft_ = container.element_.scrollLeft;
    +
    +      // When the container scrolls, it's possible that one of the targets will
    +      // move to the region contained by the dummy target. Since we don't know
    +      // which sides (if any) of the dummy target are defined by targets
    +      // contained by this container, we are conservative and just shrink it.
    +      if (this.dummyTarget_ && this.activeTarget_ == this.dummyTarget_) {
    +        if (deltaTop > 0) {
    +          this.dummyTarget_.box_.top += deltaTop;
    +        } else {
    +          this.dummyTarget_.box_.bottom += deltaTop;
    +        }
    +        if (deltaLeft > 0) {
    +          this.dummyTarget_.box_.left += deltaLeft;
    +        } else {
    +          this.dummyTarget_.box_.right += deltaLeft;
    +        }
    +      }
    +      for (var j = 0, target; target = container.containedTargets_[j]; j++) {
    +        var box = target.box_;
    +        box.top += deltaTop;
    +        box.left += deltaLeft;
    +        box.bottom += deltaTop;
    +        box.right += deltaLeft;
    +
    +        this.calculateTargetBox_(box);
    +      }
    +    }
    +  }
    +  this.dragger_.onScroll_(e);
    +};
    +
    +
    +/**
    + * Set a function that provides subtargets. A subtargeting function
    + * returns an arbitrary identifier for each subtarget of an element.
    + * DnD code will generate additional drag over / out events when
    + * switching from subtarget to subtarget. This is useful for instance
    + * if you are interested if you are on the top half or the bottom half
    + * of the element.
    + * The provided function will be given the DragDropItem, box, x, y
    + * box is the current window coordinates occupied by element
    + * x, y is the mouse position in window coordinates
    + *
    + * @param {Function} f The new subtarget function.
    + */
    +goog.fx.AbstractDragDrop.prototype.setSubtargetFunction = function(f) {
    +  this.subtargetFunction_ = f;
    +};
    +
    +
    +/**
    + * Creates an element for the item being dragged.
    + *
    + * @param {Element} sourceEl Drag source element.
    + * @return {Element} The new drag element.
    + */
    +goog.fx.AbstractDragDrop.prototype.createDragElement = function(sourceEl) {
    +  var dragEl = this.createDragElementInternal(sourceEl);
    +  goog.asserts.assert(dragEl);
    +  if (this.dragClass_) {
    +    goog.dom.classlist.add(dragEl, this.dragClass_);
    +  }
    +
    +  return dragEl;
    +};
    +
    +
    +/**
    + * Returns the position for the drag element.
    + *
    + * @param {Element} el Drag source element.
    + * @param {Element} dragEl The dragged element created by createDragElement().
    + * @param {goog.events.BrowserEvent} event Mouse down event for start of drag.
    + * @return {!goog.math.Coordinate} The position for the drag element.
    + */
    +goog.fx.AbstractDragDrop.prototype.getDragElementPosition =
    +    function(el, dragEl, event) {
    +  var pos = goog.style.getPageOffset(el);
    +
    +  // Subtract margin from drag element position twice, once to adjust the
    +  // position given by the original node and once for the drag node.
    +  var marginBox = goog.style.getMarginBox(el);
    +  pos.x -= (marginBox.left || 0) * 2;
    +  pos.y -= (marginBox.top || 0) * 2;
    +
    +  return pos;
    +};
    +
    +
    +/**
    + * Returns the dragger object.
    + *
    + * @return {goog.fx.Dragger} The dragger object used by this drag and drop
    + *     instance.
    + */
    +goog.fx.AbstractDragDrop.prototype.getDragger = function() {
    +  return this.dragger_;
    +};
    +
    +
    +/**
    + * Creates copy of node being dragged.
    + *
    + * @param {Element} sourceEl Element to copy.
    + * @return {!Element} The clone of {@code sourceEl}.
    + * @deprecated Use goog.fx.Dragger.cloneNode().
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.cloneNode_ = function(sourceEl) {
    +  return goog.fx.Dragger.cloneNode(sourceEl);
    +};
    +
    +
    +/**
    + * Generates an element to follow the cursor during dragging, given a drag
    + * source element.  The default behavior is simply to clone the source element,
    + * but this may be overridden in subclasses.  This method is called by
    + * {@code createDragElement()} before the drag class is added.
    + *
    + * @param {Element} sourceEl Drag source element.
    + * @return {!Element} The new drag element.
    + * @protected
    + * @suppress {deprecated}
    + */
    +goog.fx.AbstractDragDrop.prototype.createDragElementInternal =
    +    function(sourceEl) {
    +  return this.cloneNode_(sourceEl);
    +};
    +
    +
    +/**
    + * Add possible drop target for current drag operation.
    + *
    + * @param {goog.fx.AbstractDragDrop} target Drag handler.
    + * @param {goog.fx.DragDropItem} item Item that's being dragged.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.addDragTarget_ = function(target, item) {
    +
    +  // Get all the draggable elements and add each one.
    +  var draggableElements = item.getDraggableElements();
    +  var targetList = this.targetList_;
    +  for (var i = 0; i < draggableElements.length; i++) {
    +    var draggableElement = draggableElements[i];
    +
    +    // Determine target position and dimension
    +    var box = this.getElementBox(item, draggableElement);
    +
    +    targetList.push(
    +        new goog.fx.ActiveDropTarget_(box, target, item, draggableElement));
    +
    +    this.calculateTargetBox_(box);
    +  }
    +};
    +
    +
    +/**
    + * Calculates the position and dimension of a draggable element.
    + *
    + * @param {goog.fx.DragDropItem} item Item that's being dragged.
    + * @param {Element} element The element to calculate the box.
    + *
    + * @return {!goog.math.Box} Box describing the position and dimension
    + *     of element.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.getElementBox = function(item, element) {
    +  var pos = goog.style.getPageOffset(element);
    +  var size = goog.style.getSize(element);
    +  return new goog.math.Box(pos.y, pos.x + size.width, pos.y + size.height,
    +      pos.x);
    +};
    +
    +
    +/**
    + * Calculate the outer bounds (the region all targets are inside).
    + *
    + * @param {goog.math.Box} box Box describing the position and dimension
    + *     of a drag target.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.calculateTargetBox_ = function(box) {
    +  if (this.targetList_.length == 1) {
    +    this.targetBox_ = new goog.math.Box(box.top, box.right,
    +                                        box.bottom, box.left);
    +  } else {
    +    var tb = this.targetBox_;
    +    tb.left = Math.min(box.left, tb.left);
    +    tb.right = Math.max(box.right, tb.right);
    +    tb.top = Math.min(box.top, tb.top);
    +    tb.bottom = Math.max(box.bottom, tb.bottom);
    +  }
    +};
    +
    +
    +/**
    + * Creates a dummy target for the given cursor position. The assumption is to
    + * create as big dummy target box as possible, the only constraints are:
    + * - The dummy target box cannot overlap any of real target boxes.
    + * - The dummy target has to contain a point with current mouse coordinates.
    + *
    + * NOTE: For performance reasons the box construction algorithm is kept simple
    + * and it is not optimal (see example below). Currently it is O(n) in regard to
    + * the number of real drop target boxes, but its result depends on the order
    + * of those boxes being processed (the order in which they're added to the
    + * targetList_ collection).
    + *
    + * The algorithm.
    + * a) Assumptions
    + * - Mouse pointer is in the bounding box of real target boxes.
    + * - None of the boxes have negative coordinate values.
    + * - Mouse pointer is not contained by any of "real target" boxes.
    + * - For targets inside a scrollable container, the box used is the
    + *   intersection of the scrollable container's box and the target's box.
    + *   This is because the part of the target that extends outside the scrollable
    + *   container should not be used in the clipping calculations.
    + *
    + * b) Outline
    + * - Initialize the fake target to the bounding box of real targets.
    + * - For each real target box - clip the fake target box so it does not contain
    + *   that target box, but does contain the mouse pointer.
    + *   -- Project the real target box, mouse pointer and fake target box onto
    + *      both axes and calculate the clipping coordinates.
    + *   -- Only one coordinate is used to clip the fake target box to keep the
    + *      fake target as big as possible.
    + *   -- If the projection of the real target box contains the mouse pointer,
    + *      clipping for a given axis is not possible.
    + *   -- If both clippings are possible, the clipping more distant from the
    + *      mouse pointer is selected to keep bigger fake target area.
    + * - Save the created fake target only if it has a big enough area.
    + *
    + *
    + * c) Example
    + * <pre>
    + *        Input:           Algorithm created box:        Maximum box:
    + * +---------------------+ +---------------------+ +---------------------+
    + * | B1      |        B2 | | B1               B2 | | B1               B2 |
    + * |         |           | |   +-------------+   | |+-------------------+|
    + * |---------x-----------| |   |             |   | ||                   ||
    + * |         |           | |   |             |   | ||                   ||
    + * |         |           | |   |             |   | ||                   ||
    + * |         |           | |   |             |   | ||                   ||
    + * |         |           | |   |             |   | ||                   ||
    + * |         |           | |   +-------------+   | |+-------------------+|
    + * | B4      |        B3 | | B4               B3 | | B4               B3 |
    + * +---------------------+ +---------------------+ +---------------------+
    + * </pre>
    + *
    + * @param {number} x Cursor position on the x-axis.
    + * @param {number} y Cursor position on the y-axis.
    + * @return {goog.fx.ActiveDropTarget_} Dummy drop target.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.maybeCreateDummyTargetForPosition_ =
    +    function(x, y) {
    +  if (!this.dummyTarget_) {
    +    this.dummyTarget_ = new goog.fx.ActiveDropTarget_(this.targetBox_.clone());
    +  }
    +  var fakeTargetBox = this.dummyTarget_.box_;
    +
    +  // Initialize the fake target box to the bounding box of DnD targets.
    +  fakeTargetBox.top = this.targetBox_.top;
    +  fakeTargetBox.right = this.targetBox_.right;
    +  fakeTargetBox.bottom = this.targetBox_.bottom;
    +  fakeTargetBox.left = this.targetBox_.left;
    +
    +  // Clip the fake target based on mouse position and DnD target boxes.
    +  for (var i = 0, target; target = this.targetList_[i]; i++) {
    +    var box = target.box_;
    +
    +    if (target.scrollableContainer_) {
    +      // If the target has a scrollable container, use the intersection of that
    +      // container's box and the target's box.
    +      var scrollBox = target.scrollableContainer_.box_;
    +
    +      box = new goog.math.Box(
    +          Math.max(box.top, scrollBox.top),
    +          Math.min(box.right, scrollBox.right),
    +          Math.min(box.bottom, scrollBox.bottom),
    +          Math.max(box.left, scrollBox.left));
    +    }
    +
    +    // Calculate clipping coordinates for horizontal and vertical axis.
    +    // The clipping coordinate is calculated by projecting fake target box,
    +    // the mouse pointer and DnD target box onto an axis and checking how
    +    // box projections overlap and if the projected DnD target box contains
    +    // mouse pointer. The clipping coordinate cannot be computed and is set to
    +    // a negative value if the projected DnD target contains the mouse pointer.
    +
    +    var horizontalClip = null; // Assume mouse is above or below the DnD box.
    +    if (x >= box.right) { // Mouse is to the right of the DnD box.
    +      // Clip the fake box only if the DnD box overlaps it.
    +      horizontalClip = box.right > fakeTargetBox.left ?
    +          box.right : fakeTargetBox.left;
    +    } else if (x < box.left) { // Mouse is to the left of the DnD box.
    +      // Clip the fake box only if the DnD box overlaps it.
    +      horizontalClip = box.left < fakeTargetBox.right ?
    +          box.left : fakeTargetBox.right;
    +    }
    +    var verticalClip = null;
    +    if (y >= box.bottom) {
    +      verticalClip = box.bottom > fakeTargetBox.top ?
    +          box.bottom : fakeTargetBox.top;
    +    } else if (y < box.top) {
    +      verticalClip = box.top < fakeTargetBox.bottom ?
    +          box.top : fakeTargetBox.bottom;
    +    }
    +
    +    // If both clippings are possible, choose one that gives us larger distance
    +    // to mouse pointer (mark the shorter clipping as impossible, by setting it
    +    // to null).
    +    if (!goog.isNull(horizontalClip) && !goog.isNull(verticalClip)) {
    +      if (Math.abs(horizontalClip - x) > Math.abs(verticalClip - y)) {
    +        verticalClip = null;
    +      } else {
    +        horizontalClip = null;
    +      }
    +    }
    +
    +    // Clip none or one of fake target box sides (at most one clipping
    +    // coordinate can be active).
    +    if (!goog.isNull(horizontalClip)) {
    +      if (horizontalClip <= x) {
    +        fakeTargetBox.left = horizontalClip;
    +      } else {
    +        fakeTargetBox.right = horizontalClip;
    +      }
    +    } else if (!goog.isNull(verticalClip)) {
    +      if (verticalClip <= y) {
    +        fakeTargetBox.top = verticalClip;
    +      } else {
    +        fakeTargetBox.bottom = verticalClip;
    +      }
    +    }
    +  }
    +
    +  // Only return the new fake target if it is big enough.
    +  return (fakeTargetBox.right - fakeTargetBox.left) *
    +         (fakeTargetBox.bottom - fakeTargetBox.top) >=
    +         goog.fx.AbstractDragDrop.DUMMY_TARGET_MIN_SIZE_ ?
    +      this.dummyTarget_ : null;
    +};
    +
    +
    +/**
    + * Returns the target for a given cursor position.
    + *
    + * @param {goog.math.Coordinate} position Cursor position.
    + * @return {Object} Target for position or null if no target was defined
    + *     for the given position.
    + * @private
    + */
    +goog.fx.AbstractDragDrop.prototype.getTargetFromPosition_ = function(position) {
    +  for (var target, i = 0; target = this.targetList_[i]; i++) {
    +    if (target.box_.contains(position)) {
    +      if (target.scrollableContainer_) {
    +        // If we have a scrollable container we will need to make sure
    +        // we account for clipping of the scroll area
    +        var box = target.scrollableContainer_.box_;
    +        if (box.contains(position)) {
    +          return target;
    +        }
    +      } else {
    +        return target;
    +      }
    +    }
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Checks whatever a given point is inside a given box.
    + *
    + * @param {number} x Cursor position on the x-axis.
    + * @param {number} y Cursor position on the y-axis.
    + * @param {goog.math.Box} box Box to check position against.
    + * @return {boolean} Whether the given point is inside {@code box}.
    + * @protected
    + * @deprecated Use goog.math.Box.contains.
    + */
    +goog.fx.AbstractDragDrop.prototype.isInside = function(x, y, box) {
    +  return x >= box.left &&
    +         x < box.right &&
    +         y >= box.top &&
    +         y < box.bottom;
    +};
    +
    +
    +/**
    + * Gets the scroll distance as a coordinate object, using
    + * the window of the current drag element's dom.
    + * @return {!goog.math.Coordinate} Object with scroll offsets 'x' and 'y'.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.getScrollPos = function() {
    +  return goog.dom.getDomHelper(this.dragEl_).getDocumentScroll();
    +};
    +
    +
    +/**
    + * Get the position of a drag event.
    + * @param {goog.fx.DragEvent} event Drag event.
    + * @return {!goog.math.Coordinate} Position of the event.
    + * @protected
    + */
    +goog.fx.AbstractDragDrop.prototype.getEventPosition = function(event) {
    +  var scroll = this.getScrollPos();
    +  return new goog.math.Coordinate(event.clientX + scroll.x,
    +                                  event.clientY + scroll.y);
    +};
    +
    +
    +/** @override */
    +goog.fx.AbstractDragDrop.prototype.disposeInternal = function() {
    +  goog.fx.AbstractDragDrop.base(this, 'disposeInternal');
    +  this.removeItems();
    +};
    +
    +
    +
    +/**
    + * Object representing a drag and drop event.
    + *
    + * @param {string} type Event type.
    + * @param {goog.fx.AbstractDragDrop} source Source drag drop object.
    + * @param {goog.fx.DragDropItem} sourceItem Source item.
    + * @param {goog.fx.AbstractDragDrop=} opt_target Target drag drop object.
    + * @param {goog.fx.DragDropItem=} opt_targetItem Target item.
    + * @param {Element=} opt_targetElement Target element.
    + * @param {number=} opt_clientX X-Position relative to the screen.
    + * @param {number=} opt_clientY Y-Position relative to the screen.
    + * @param {number=} opt_x X-Position relative to the viewport.
    + * @param {number=} opt_y Y-Position relative to the viewport.
    + * @param {Object=} opt_subtarget The currently active subtarget.
    + * @extends {goog.events.Event}
    + * @constructor
    + */
    +goog.fx.DragDropEvent = function(type, source, sourceItem,
    +                                 opt_target, opt_targetItem, opt_targetElement,
    +                                 opt_clientX, opt_clientY, opt_x, opt_y,
    +                                 opt_subtarget) {
    +  // TODO(eae): Get rid of all the optional parameters and have the caller set
    +  // the fields directly instead.
    +  goog.fx.DragDropEvent.base(this, 'constructor', type);
    +
    +  /**
    +   * Reference to the source goog.fx.AbstractDragDrop object.
    +   * @type {goog.fx.AbstractDragDrop}
    +   */
    +  this.dragSource = source;
    +
    +  /**
    +   * Reference to the source goog.fx.DragDropItem object.
    +   * @type {goog.fx.DragDropItem}
    +   */
    +  this.dragSourceItem = sourceItem;
    +
    +  /**
    +   * Reference to the target goog.fx.AbstractDragDrop object.
    +   * @type {goog.fx.AbstractDragDrop|undefined}
    +   */
    +  this.dropTarget = opt_target;
    +
    +  /**
    +   * Reference to the target goog.fx.DragDropItem object.
    +   * @type {goog.fx.DragDropItem|undefined}
    +   */
    +  this.dropTargetItem = opt_targetItem;
    +
    +  /**
    +   * The actual element of the drop target that is the target for this event.
    +   * @type {Element|undefined}
    +   */
    +  this.dropTargetElement = opt_targetElement;
    +
    +  /**
    +   * X-Position relative to the screen.
    +   * @type {number|undefined}
    +   */
    +  this.clientX = opt_clientX;
    +
    +  /**
    +   * Y-Position relative to the screen.
    +   * @type {number|undefined}
    +   */
    +  this.clientY = opt_clientY;
    +
    +  /**
    +   * X-Position relative to the viewport.
    +   * @type {number|undefined}
    +   */
    +  this.viewportX = opt_x;
    +
    +  /**
    +   * Y-Position relative to the viewport.
    +   * @type {number|undefined}
    +   */
    +  this.viewportY = opt_y;
    +
    +  /**
    +   * The subtarget that is currently active if a subtargeting function
    +   * is supplied.
    +   * @type {Object|undefined}
    +   */
    +  this.subtarget = opt_subtarget;
    +};
    +goog.inherits(goog.fx.DragDropEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * Class representing a source or target element for drag and drop operations.
    + *
    + * @param {Element|string} element Dom Node, or string representation of node
    + *     id, to be used as drag source/drop target.
    + * @param {Object=} opt_data Data associated with the source/target.
    + * @throws Error If no element argument is provided or if the type is invalid
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.fx.DragDropItem = function(element, opt_data) {
    +  goog.fx.DragDropItem.base(this, 'constructor');
    +
    +  /**
    +   * Reference to drag source/target element
    +   * @type {Element}
    +   */
    +  this.element = goog.dom.getElement(element);
    +
    +  /**
    +   * Data associated with element.
    +   * @type {Object|undefined}
    +   */
    +  this.data = opt_data;
    +
    +  /**
    +   * Drag object the item belongs to.
    +   * @type {goog.fx.AbstractDragDrop?}
    +   * @private
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * Event handler for listeners on events that can initiate a drag.
    +   * @type {!goog.events.EventHandler<!goog.fx.DragDropItem>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  if (!this.element) {
    +    throw Error('Invalid argument');
    +  }
    +};
    +goog.inherits(goog.fx.DragDropItem, goog.events.EventTarget);
    +
    +
    +/**
    + * The current element being dragged. This is needed because a DragDropItem can
    + * have multiple elements that can be dragged.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragDropItem.prototype.currentDragElement_ = null;
    +
    +
    +/**
    + * Get the data associated with the source/target.
    + * @return {Object|null|undefined} Data associated with the source/target.
    + */
    +goog.fx.DragDropItem.prototype.getData = function() {
    +  return this.data;
    +};
    +
    +
    +/**
    + * Gets the element that is actually draggable given that the given target was
    + * attempted to be dragged. This should be overriden when the element that was
    + * given actually contains many items that can be dragged. From the target, you
    + * can determine what element should actually be dragged.
    + *
    + * @param {Element} target The target that was attempted to be dragged.
    + * @return {Element} The element that is draggable given the target. If
    + *     none are draggable, this will return null.
    + */
    +goog.fx.DragDropItem.prototype.getDraggableElement = function(target) {
    +  return target;
    +};
    +
    +
    +/**
    + * Gets the element that is currently being dragged.
    + *
    + * @return {Element} The element that is currently being dragged.
    + */
    +goog.fx.DragDropItem.prototype.getCurrentDragElement = function() {
    +  return this.currentDragElement_;
    +};
    +
    +
    +/**
    + * Gets all the elements of this item that are potentially draggable/
    + *
    + * @return {!Array<Element>} The draggable elements.
    + */
    +goog.fx.DragDropItem.prototype.getDraggableElements = function() {
    +  return [this.element];
    +};
    +
    +
    +/**
    + * Event handler for mouse down.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse down event.
    + * @private
    + */
    +goog.fx.DragDropItem.prototype.mouseDown_ = function(event) {
    +  if (!event.isMouseActionButton()) {
    +    return;
    +  }
    +
    +  // Get the draggable element for the target.
    +  var element = this.getDraggableElement(/** @type {Element} */ (event.target));
    +  if (element) {
    +    this.maybeStartDrag_(event, element);
    +  }
    +};
    +
    +
    +/**
    + * Sets the dragdrop to which this item belongs.
    + * @param {goog.fx.AbstractDragDrop} parent The parent dragdrop.
    + */
    +goog.fx.DragDropItem.prototype.setParent = function(parent) {
    +  this.parent_ = parent;
    +};
    +
    +
    +/**
    + * Adds mouse move, mouse out and mouse up handlers.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse down event.
    + * @param {Element} element Element.
    + * @private
    + */
    +goog.fx.DragDropItem.prototype.maybeStartDrag_ = function(event, element) {
    +  var eventType = goog.events.EventType;
    +  this.eventHandler_.
    +      listen(element, eventType.MOUSEMOVE, this.mouseMove_, false).
    +      listen(element, eventType.MOUSEOUT, this.mouseMove_, false);
    +
    +  // Capture the MOUSEUP on the document to ensure that we cancel the start
    +  // drag handlers even if the mouse up occurs on some other element. This can
    +  // happen for instance when the mouse down changes the geometry of the element
    +  // clicked on (e.g. through changes in activation styling) such that the mouse
    +  // up occurs outside the original element.
    +  var doc = goog.dom.getOwnerDocument(element);
    +  this.eventHandler_.listen(doc, eventType.MOUSEUP, this.mouseUp_, true);
    +
    +  this.currentDragElement_ = element;
    +
    +  this.startPosition_ = new goog.math.Coordinate(
    +      event.clientX, event.clientY);
    +
    +  event.preventDefault();
    +};
    +
    +
    +/**
    + * Event handler for mouse move. Starts drag operation if moved more than the
    + * threshold value.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse move or mouse out event.
    + * @private
    + */
    +goog.fx.DragDropItem.prototype.mouseMove_ = function(event) {
    +  var distance = Math.abs(event.clientX - this.startPosition_.x) +
    +      Math.abs(event.clientY - this.startPosition_.y);
    +  // Fire dragStart event if the drag distance exceeds the threshold or if the
    +  // mouse leave the dragged element.
    +  // TODO(user): Consider using the goog.fx.Dragger to track the distance
    +  // even after the mouse leaves the dragged element.
    +  var currentDragElement = this.currentDragElement_;
    +  var distanceAboveThreshold =
    +      distance > goog.fx.AbstractDragDrop.initDragDistanceThreshold;
    +  var mouseOutOnDragElement = event.type == goog.events.EventType.MOUSEOUT &&
    +      event.target == currentDragElement;
    +  if (distanceAboveThreshold || mouseOutOnDragElement) {
    +    this.eventHandler_.removeAll();
    +    this.parent_.startDrag(event, this);
    +  }
    +};
    +
    +
    +/**
    + * Event handler for mouse up. Removes mouse move, mouse out and mouse up event
    + * handlers.
    + *
    + * @param {goog.events.BrowserEvent} event Mouse up event.
    + * @private
    + */
    +goog.fx.DragDropItem.prototype.mouseUp_ = function(event) {
    +  this.eventHandler_.removeAll();
    +  delete this.startPosition_;
    +  this.currentDragElement_ = null;
    +};
    +
    +
    +
    +/**
    + * Class representing an active drop target
    + *
    + * @param {goog.math.Box} box Box describing the position and dimension of the
    + *     target item.
    + * @param {goog.fx.AbstractDragDrop=} opt_target Target that contains the item
    +       associated with position.
    + * @param {goog.fx.DragDropItem=} opt_item Item associated with position.
    + * @param {Element=} opt_element Element of item associated with position.
    + * @constructor
    + * @private
    + */
    +goog.fx.ActiveDropTarget_ = function(box, opt_target, opt_item, opt_element) {
    +
    +  /**
    +   * Box describing the position and dimension of the target item
    +   * @type {goog.math.Box}
    +   * @private
    +   */
    +  this.box_ = box;
    +
    +  /**
    +   * Target that contains the item associated with position
    +   * @type {goog.fx.AbstractDragDrop|undefined}
    +   * @private
    +   */
    +  this.target_ = opt_target;
    +
    +  /**
    +   * Item associated with position
    +   * @type {goog.fx.DragDropItem|undefined}
    +   * @private
    +   */
    +  this.item_ = opt_item;
    +
    +  /**
    +   * The draggable element of the item associated with position.
    +   * @type {Element|undefined}
    +   * @private
    +   */
    +  this.element_ = opt_element;
    +};
    +
    +
    +/**
    + * If this target is in a scrollable container this is it.
    + * @type {goog.fx.ScrollableContainer_}
    + * @private
    + */
    +goog.fx.ActiveDropTarget_.prototype.scrollableContainer_ = null;
    +
    +
    +
    +/**
    + * Class for representing a scrollable container
    + * @param {Element} element the scrollable element.
    + * @constructor
    + * @private
    + */
    +goog.fx.ScrollableContainer_ = function(element) {
    +
    +  /**
    +   * The targets that lie within this container.
    +   * @type {Array<goog.fx.ActiveDropTarget_>}
    +   * @private
    +   */
    +  this.containedTargets_ = [];
    +
    +  /**
    +   * The element that is this container
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  /**
    +   * The saved scroll left location for calculating deltas.
    +   * @type {number}
    +   * @private
    +   */
    +  this.savedScrollLeft_ = 0;
    +
    +  /**
    +   * The saved scroll top location for calculating deltas.
    +   * @type {number}
    +   * @private
    +   */
    +  this.savedScrollTop_ = 0;
    +
    +  /**
    +   * The space occupied by the container.
    +   * @type {goog.math.Box}
    +   * @private
    +   */
    +  this.box_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.html b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.html
    new file mode 100644
    index 0000000000..72e023e4d5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.html
    @@ -0,0 +1,57 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: eae@google.com (Emil A Eklund)
    +  Author: dgajda@google.com (Damian Gajda)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.AbstractDragDrop
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.AbstractDragDropTest');
    +  </script>
    +  <style>
    +   #cont {
    +    position: relative;
    +    border: 1px solid black;
    +    height: 100px;
    +    width: 100px;
    +  }
    +  #cont div {
    +    position: absolute;
    +    overflow: hidden;
    +  }
    +  </style>
    + </head>
    + <body>
    +  <div id="cont" style="">
    +  </div>
    +  <button onclick="drawTargets(targets, 10)">
    +   draw targets 1
    +  </button>
    +  <br />
    +  <button onclick="drawTargets(targets2, 1)">
    +   draw targets 2
    +  </button>
    +  <br />
    +  <button onclick="drawTargets(targets3, 10)">
    +   draw targets 3
    +  </button>
    +  <div id="container1">
    +   <div id="child1">
    +   </div>
    +   <div id="child2">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.js b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.js
    new file mode 100644
    index 0000000000..bcd46a6d07
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/abstractdragdrop_test.js
    @@ -0,0 +1,636 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.AbstractDragDropTest');
    +goog.setTestOnly('goog.fx.AbstractDragDropTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.fx.AbstractDragDrop');
    +goog.require('goog.fx.DragDropItem');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +
    +var targets = [
    +  { box_: new goog.math.Box(0, 3, 1, 1) },
    +  { box_: new goog.math.Box(0, 7, 2, 6) },
    +  { box_: new goog.math.Box(2, 2, 3, 1) },
    +  { box_: new goog.math.Box(4, 1, 6, 1) },
    +  { box_: new goog.math.Box(4, 9, 7, 6) },
    +  { box_: new goog.math.Box(9, 9, 10, 1) }
    +];
    +
    +var targets2 = [
    +  { box_: new goog.math.Box(10, 50, 20, 10) },
    +  { box_: new goog.math.Box(20, 50, 30, 10) },
    +  { box_: new goog.math.Box(60, 50, 70, 10) },
    +  { box_: new goog.math.Box(70, 50, 80, 10) }
    +];
    +
    +var targets3 = [
    +  { box_: new goog.math.Box(0, 4, 1, 1) },
    +  { box_: new goog.math.Box(1, 6, 4, 5) },
    +  { box_: new goog.math.Box(5, 5, 6, 2) },
    +  { box_: new goog.math.Box(2, 1, 5, 0) }
    +];
    +
    +
    +/**
    + * Test the utility function which tells how two one dimensional ranges
    + * overlap.
    + */
    +function testRangeOverlap() {
    +  assertEquals(RangeOverlap.LEFT, rangeOverlap(1, 2, 3, 4));
    +  assertEquals(RangeOverlap.LEFT, rangeOverlap(2, 3, 3, 4));
    +  assertEquals(RangeOverlap.LEFT_IN, rangeOverlap(1, 3, 2, 4));
    +  assertEquals(RangeOverlap.IN, rangeOverlap(1, 3, 1, 4));
    +  assertEquals(RangeOverlap.IN, rangeOverlap(2, 3, 1, 4));
    +  assertEquals(RangeOverlap.IN, rangeOverlap(3, 4, 1, 4));
    +  assertEquals(RangeOverlap.RIGHT_IN, rangeOverlap(2, 4, 1, 3));
    +  assertEquals(RangeOverlap.RIGHT, rangeOverlap(2, 3, 1, 2));
    +  assertEquals(RangeOverlap.RIGHT, rangeOverlap(3, 4, 1, 2));
    +  assertEquals(RangeOverlap.CONTAINS, rangeOverlap(1, 4, 2, 3));
    +}
    +
    +
    +/**
    + * An enum describing how two ranges overlap (non-symmetrical relation).
    + * @enum {number}
    + */
    +RangeOverlap = {
    +  LEFT: 1,      // First range is placed to the left of the second.
    +  LEFT_IN: 2,   // First range overlaps on the left side of the second.
    +  IN: 3,        // First range is completely contained in the second.
    +  RIGHT_IN: 4,  // First range overlaps on the right side of the second.
    +  RIGHT: 5,     // First range is placed to the right side of the second.
    +  CONTAINS: 6   // First range contains the second.
    +};
    +
    +
    +/**
    + * Computes how two one dimentional ranges overlap.
    + *
    + * @param {number} left1 Left inclusive bound of the first range.
    + * @param {number} right1 Right exclusive bound of the first range.
    + * @param {number} left2 Left inclusive bound of the second range.
    + * @param {number} right2 Right exclusive bound of the second range.
    + * @return {RangeOverlap} The enum value describing the type of the overlap.
    + */
    +function rangeOverlap(left1, right1, left2, right2) {
    +  if (right1 <= left2) return RangeOverlap.LEFT;
    +  if (left1 >= right2) return RangeOverlap.RIGHT;
    +  var leftIn = left1 >= left2;
    +  var rightIn = right1 <= right2;
    +  if (leftIn && rightIn) return RangeOverlap.IN;
    +  if (leftIn) return RangeOverlap.RIGHT_IN;
    +  if (rightIn) return RangeOverlap.LEFT_IN;
    +  return RangeOverlap.CONTAINS;
    +}
    +
    +
    +/**
    + * Tells whether two boxes overlap.
    + *
    + * @param {goog.math.Box} box1 First box in question.
    + * @param {goog.math.Box} box2 Second box in question.
    + * @return {boolean} Whether boxes overlap in any way.
    + */
    +function boxOverlaps(box1, box2) {
    +  var horizontalOverlap = rangeOverlap(
    +      box1.left, box1.right, box2.left, box2.right);
    +  var verticalOverlap = rangeOverlap(
    +      box1.top, box1.bottom, box2.top, box2.bottom);
    +  return horizontalOverlap != RangeOverlap.LEFT &&
    +      horizontalOverlap != RangeOverlap.RIGHT &&
    +      verticalOverlap != RangeOverlap.LEFT &&
    +      verticalOverlap != RangeOverlap.RIGHT;
    +}
    +
    +
    +/**
    + * Tests if the utility function to compute box overlapping functions properly.
    + */
    +function testBoxOverlaps() {
    +  // Overlapping tests.
    +  var box2 = new goog.math.Box(1, 4, 4, 1);
    +
    +  // Corner overlaps.
    +  assertTrue('NW overlap', boxOverlaps(new goog.math.Box(0, 2, 2, 0), box2));
    +  assertTrue('NE overlap', boxOverlaps(new goog.math.Box(0, 5, 2, 3), box2));
    +  assertTrue('SE overlap', boxOverlaps(new goog.math.Box(3, 5, 5, 3), box2));
    +  assertTrue('SW overlap', boxOverlaps(new goog.math.Box(3, 2, 5, 0), box2));
    +
    +  // Inside.
    +  assertTrue('Inside overlap',
    +      boxOverlaps(new goog.math.Box(2, 3, 3, 2), box2));
    +
    +  // Around.
    +  assertTrue('Outside overlap',
    +      boxOverlaps(new goog.math.Box(0, 5, 5, 0), box2));
    +
    +  // Edge overlaps.
    +  assertTrue('N overlap', boxOverlaps(new goog.math.Box(0, 3, 2, 2), box2));
    +  assertTrue('E overlap', boxOverlaps(new goog.math.Box(2, 5, 3, 3), box2));
    +  assertTrue('S overlap', boxOverlaps(new goog.math.Box(3, 3, 5, 2), box2));
    +  assertTrue('W overlap', boxOverlaps(new goog.math.Box(2, 2, 3, 0), box2));
    +
    +  assertTrue('N-in overlap', boxOverlaps(new goog.math.Box(0, 5, 2, 0), box2));
    +  assertTrue('E-in overlap', boxOverlaps(new goog.math.Box(0, 5, 5, 3), box2));
    +  assertTrue('S-in overlap', boxOverlaps(new goog.math.Box(3, 5, 5, 0), box2));
    +  assertTrue('W-in overlap', boxOverlaps(new goog.math.Box(0, 2, 5, 0), box2));
    +
    +  // Does not overlap.
    +  var box2 = new goog.math.Box(3, 6, 6, 3);
    +
    +  // Along the edge - shorter.
    +  assertFalse('N-in no overlap',
    +      boxOverlaps(new goog.math.Box(1, 5, 2, 4), box2));
    +  assertFalse('E-in no overlap',
    +      boxOverlaps(new goog.math.Box(4, 8, 5, 7), box2));
    +  assertFalse('S-in no overlap',
    +      boxOverlaps(new goog.math.Box(7, 5, 8, 4), box2));
    +  assertFalse('N-in no overlap',
    +      boxOverlaps(new goog.math.Box(4, 2, 5, 1), box2));
    +
    +  // By the corner.
    +  assertFalse('NE no overlap',
    +      boxOverlaps(new goog.math.Box(1, 8, 2, 7), box2));
    +  assertFalse('SE no overlap',
    +      boxOverlaps(new goog.math.Box(7, 8, 8, 7), box2));
    +  assertFalse('SW no overlap',
    +      boxOverlaps(new goog.math.Box(7, 2, 8, 1), box2));
    +  assertFalse('NW no overlap',
    +      boxOverlaps(new goog.math.Box(1, 2, 2, 1), box2));
    +
    +  // Perpendicular to an edge.
    +  assertFalse('NNE no overlap',
    +      boxOverlaps(new goog.math.Box(1, 7, 2, 5), box2));
    +  assertFalse('NEE no overlap',
    +      boxOverlaps(new goog.math.Box(2, 8, 4, 7), box2));
    +  assertFalse('SEE no overlap',
    +      boxOverlaps(new goog.math.Box(5, 8, 7, 7), box2));
    +  assertFalse('SSE no overlap',
    +      boxOverlaps(new goog.math.Box(7, 7, 8, 5), box2));
    +  assertFalse('SSW no overlap',
    +      boxOverlaps(new goog.math.Box(7, 4, 8, 2), box2));
    +  assertFalse('SWW no overlap',
    +      boxOverlaps(new goog.math.Box(5, 2, 7, 1), box2));
    +  assertFalse('NWW no overlap',
    +      boxOverlaps(new goog.math.Box(2, 2, 4, 1), box2));
    +  assertFalse('NNW no overlap',
    +      boxOverlaps(new goog.math.Box(1, 4, 2, 2), box2));
    +
    +  // Along the edge - longer.
    +  assertFalse('N no overlap',
    +      boxOverlaps(new goog.math.Box(0, 7, 1, 2), box2));
    +  assertFalse('E no overlap',
    +      boxOverlaps(new goog.math.Box(2, 9, 7, 8), box2));
    +  assertFalse('S no overlap',
    +      boxOverlaps(new goog.math.Box(8, 7, 9, 2), box2));
    +  assertFalse('W no overlap',
    +      boxOverlaps(new goog.math.Box(2, 1, 7, 0), box2));
    +}
    +
    +
    +/**
    + * Checks whether a given box overlaps any of given DnD target boxes.
    + *
    + * @param {goog.math.Box} box The box to check.
    + * @param {Array<Object>} targets The array of targets with boxes to check
    + *     if they overlap with the given box.
    + * @return {boolean} Whether the box overlaps any of the target boxes.
    + */
    +function boxOverlapsTargets(box, targets) {
    +  return goog.array.some(targets, function(target) {
    +    return boxOverlaps(box, target.box_);
    +  });
    +}
    +
    +
    +function testMaybeCreateDummyTargetForPosition() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets;
    +  testGroup.targetBox_ = new goog.math.Box(0, 9, 10, 1);
    +
    +  var target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(3, 3, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(2, 4);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(2, 4, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(2, 7, target.box_));
    +
    +  testGroup.targetList_.push({ box_: new goog.math.Box(5, 6, 6, 0) });
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(3, 3, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(2, 7, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(6, 3);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(6, 3, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(0, 3);
    +  assertNull(target);
    +  target = testGroup.maybeCreateDummyTargetForPosition_(9, 0);
    +  assertNull(target);
    +}
    +
    +
    +function testMaybeCreateDummyTargetForPosition2() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets2;
    +  testGroup.targetBox_ = new goog.math.Box(10, 50, 80, 10);
    +
    +  var target = testGroup.maybeCreateDummyTargetForPosition_(30, 40);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(30, 40, target.box_));
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(45, 40);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(45, 40, target.box_));
    +
    +  testGroup.targetList_.push({ box_: new goog.math.Box(40, 50, 50, 40) });
    +
    +  target = testGroup.maybeCreateDummyTargetForPosition_(30, 40);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  target = testGroup.maybeCreateDummyTargetForPosition_(45, 35);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +}
    +
    +
    +function testMaybeCreateDummyTargetForPosition3BoxHasDecentSize() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets3;
    +  testGroup.targetBox_ = new goog.math.Box(0, 6, 6, 0);
    +
    +  var target = testGroup.maybeCreateDummyTargetForPosition_(3, 3);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(3, 3, target.box_));
    +  assertEquals('(1t, 5r, 5b, 1l)', target.box_.toString());
    +}
    +
    +
    +function testMaybeCreateDummyTargetForPosition4() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets;
    +  testGroup.targetBox_ = new goog.math.Box(0, 9, 10, 1);
    +
    +  for (var x = testGroup.targetBox_.left;
    +       x < testGroup.targetBox_.right;
    +       x++) {
    +    for (var y = testGroup.targetBox_.top;
    +        y < testGroup.targetBox_.bottom;
    +        y++) {
    +      var inRealTarget = false;
    +      for (var i = 0; i < testGroup.targetList_.length; i++) {
    +        if (testGroup.isInside(x, y, testGroup.targetList_[i].box_)) {
    +          inRealTarget = true;
    +          break;
    +        }
    +      }
    +      if (!inRealTarget) {
    +        var target = testGroup.maybeCreateDummyTargetForPosition_(x, y);
    +        if (target) {
    +          assertFalse('Fake target for point(' + x + ',' + y + ') should ' +
    +              'not overlap any real targets.',
    +              boxOverlapsTargets(target.box_, testGroup.targetList_));
    +          assertTrue(testGroup.isInside(x, y, target.box_));
    +        }
    +      }
    +    }
    +  }
    +}
    +
    +function testMaybeCreateDummyTargetForPosition_NegativePositions() {
    +  var negTargets = [
    +    { box_: new goog.math.Box(-20, 10, -5, 1) },
    +    { box_: new goog.math.Box(20, 10, 30, 1) }
    +  ];
    +
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = negTargets;
    +  testGroup.targetBox_ = new goog.math.Box(-20, 10, 30, 1);
    +
    +  var target = testGroup.maybeCreateDummyTargetForPosition_(1, 5);
    +  assertFalse(boxOverlapsTargets(target.box_, testGroup.targetList_));
    +  assertTrue(testGroup.isInside(1, 5, target.box_));
    +}
    +
    +function testMaybeCreateDummyTargetOutsideScrollableContainer() {
    +  var targets = [
    +    { box_: new goog.math.Box(0, 3, 10, 1) },
    +    { box_: new goog.math.Box(20, 3, 30, 1) }
    +  ];
    +  var target = targets[0];
    +
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets;
    +  testGroup.targetBox_ = new goog.math.Box(0, 3, 30, 1);
    +
    +  testGroup.addScrollableContainer(document.getElementById('container1'));
    +  var container = testGroup.scrollableContainers_[0];
    +  container.containedTargets_.push(target);
    +  container.box_ = new goog.math.Box(0, 3, 5, 1); // shorter than target
    +  target.scrollableContainer_ = container;
    +
    +  // mouse cursor is below scrollable target but not the actual target
    +  var dummyTarget = testGroup.maybeCreateDummyTargetForPosition_(2, 7);
    +  // dummy target should not overlap the scrollable container
    +  assertFalse(boxOverlaps(dummyTarget.box_, container.box_));
    +  // but should overlap the actual target, since not all of it is visible
    +  assertTrue(boxOverlaps(dummyTarget.box_, target.box_));
    +}
    +
    +function testMaybeCreateDummyTargetInsideScrollableContainer() {
    +  var targets = [
    +    { box_: new goog.math.Box(0, 3, 10, 1) },
    +    { box_: new goog.math.Box(20, 3, 30, 1) }
    +  ];
    +  var target = targets[0];
    +
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = targets;
    +  testGroup.targetBox_ = new goog.math.Box(0, 3, 30, 1);
    +
    +  testGroup.addScrollableContainer(document.getElementById('container1'));
    +  var container = testGroup.scrollableContainers_[0];
    +  container.containedTargets_.push(target);
    +  container.box_ = new goog.math.Box(0, 3, 20, 1); // longer than target
    +  target.scrollableContainer_ = container;
    +
    +  // mouse cursor is below both the scrollable and the actual target
    +  var dummyTarget = testGroup.maybeCreateDummyTargetForPosition_(2, 15);
    +  // dummy target should overlap the scrollable container
    +  assertTrue(boxOverlaps(dummyTarget.box_, container.box_));
    +  // but not overlap the actual target
    +  assertFalse(boxOverlaps(dummyTarget.box_, target.box_));
    +}
    +
    +function testCalculateTargetBox() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = [];
    +  goog.array.forEach(targets, function(target) {
    +    testGroup.targetList_.push(target);
    +    testGroup.calculateTargetBox_(target.box_);
    +  });
    +  assertTrue(goog.math.Box.equals(testGroup.targetBox_,
    +      new goog.math.Box(0, 9, 10, 1)));
    +
    +  testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = [];
    +  goog.array.forEach(targets2, function(target) {
    +    testGroup.targetList_.push(target);
    +    testGroup.calculateTargetBox_(target.box_);
    +  });
    +  assertTrue(goog.math.Box.equals(testGroup.targetBox_,
    +      new goog.math.Box(10, 50, 80, 10)));
    +
    +  testGroup = new goog.fx.AbstractDragDrop();
    +  testGroup.targetList_ = [];
    +  goog.array.forEach(targets3, function(target) {
    +    testGroup.targetList_.push(target);
    +    testGroup.calculateTargetBox_(target.box_);
    +  });
    +  assertTrue(goog.math.Box.equals(testGroup.targetBox_,
    +      new goog.math.Box(0, 6, 6, 0)));
    +}
    +
    +
    +function testIsInside() {
    +  var add = new goog.fx.AbstractDragDrop();
    +  // The box in question.
    +  // 10,20+++++20,20
    +  //   +         |
    +  // 10,30-----20,30
    +  var box = new goog.math.Box(20, 20, 30, 10);
    +
    +  assertTrue('A point somewhere in the middle of the box should be inside.',
    +      add.isInside(15, 25, box));
    +
    +  assertTrue('A point in top-left corner should be inside the box.',
    +      add.isInside(10, 20, box));
    +
    +  assertTrue('A point on top border should be inside the box.',
    +      add.isInside(15, 20, box));
    +
    +  assertFalse('A point in top-right corner should be outside the box.',
    +      add.isInside(20, 20, box));
    +
    +  assertFalse('A point on right border should be outside the box.',
    +      add.isInside(20, 25, box));
    +
    +  assertFalse('A point in bottom-right corner should be outside the box.',
    +      add.isInside(20, 30, box));
    +
    +  assertFalse('A point on bottom border should be outside the box.',
    +      add.isInside(15, 30, box));
    +
    +  assertFalse('A point in bottom-left corner should be outside the box.',
    +      add.isInside(10, 30, box));
    +
    +  assertTrue('A point on left border should be inside the box.',
    +      add.isInside(10, 25, box));
    +
    +  add.dispose();
    +}
    +
    +
    +function testAddingRemovingScrollableContainers() {
    +  var group = new goog.fx.AbstractDragDrop();
    +  var el1 = document.createElement('div');
    +  var el2 = document.createElement('div');
    +
    +  assertEquals(0, group.scrollableContainers_.length);
    +
    +  group.addScrollableContainer(el1);
    +  assertEquals(1, group.scrollableContainers_.length);
    +
    +  group.addScrollableContainer(el2);
    +  assertEquals(2, group.scrollableContainers_.length);
    +
    +  group.removeAllScrollableContainers();
    +  assertEquals(0, group.scrollableContainers_.length);
    +}
    +
    +
    +function testScrollableContainersCalculation() {
    +  var group = new goog.fx.AbstractDragDrop();
    +  var target = new goog.fx.AbstractDragDrop();
    +
    +  group.addTarget(target);
    +  group.addScrollableContainer(document.getElementById('container1'));
    +  var container = group.scrollableContainers_[0];
    +
    +  var item1 = new goog.fx.DragDropItem(document.getElementById('child1'));
    +  var item2 = new goog.fx.DragDropItem(document.getElementById('child2'));
    +
    +  target.items_.push(item1);
    +  group.recalculateDragTargets();
    +  group.recalculateScrollableContainers();
    +
    +  assertEquals(1, container.containedTargets_.length);
    +  assertEquals(container, group.targetList_[0].scrollableContainer_);
    +
    +  target.items_.push(item2);
    +  group.recalculateDragTargets();
    +  assertEquals(1, container.containedTargets_.length);
    +  assertNull(group.targetList_[0].scrollableContainer_);
    +
    +  group.recalculateScrollableContainers();
    +  assertEquals(2, container.containedTargets_.length);
    +  assertEquals(container, group.targetList_[1].scrollableContainer_);
    +}
    +
    +// See http://b/7494613.
    +function testMouseUpOutsideElement() {
    +  var group = new goog.fx.AbstractDragDrop();
    +  var target = new goog.fx.AbstractDragDrop();
    +  group.addTarget(target);
    +  var item1 = new goog.fx.DragDropItem(document.getElementById('child1'));
    +  group.items_.push(item1);
    +  item1.setParent(group);
    +  group.init();
    +
    +  group.startDrag = goog.functions.error('startDrag should not be called.');
    +
    +  goog.testing.events.fireMouseDownEvent(item1.element);
    +  goog.testing.events.fireMouseUpEvent(item1.element.parentNode);
    +  // This should have no effect (not start a drag) since the previous event
    +  // should have cleared the listeners.
    +  goog.testing.events.fireMouseOutEvent(item1.element);
    +
    +  group.dispose();
    +  target.dispose();
    +}
    +
    +function testScrollBeforeMoveDrag() {
    +  var group = new goog.fx.AbstractDragDrop();
    +  var target = new goog.fx.AbstractDragDrop();
    +
    +  group.addTarget(target);
    +  var container = document.getElementById('container1');
    +  group.addScrollableContainer(container);
    +
    +  var childEl = document.getElementById('child1');
    +  var item = new goog.fx.DragDropItem(childEl);
    +  item.currentDragElement_ = childEl;
    +
    +  target.items_.push(item);
    +  group.recalculateDragTargets();
    +  group.recalculateScrollableContainers();
    +
    +  // Simulare starting a drag.
    +  var moveEvent = {
    +    'clientX': 8,
    +    'clientY': 10,
    +    'type': goog.events.EventType.MOUSEMOVE,
    +    'relatedTarget': childEl,
    +    'preventDefault': function() {}
    +  };
    +  group.startDrag(moveEvent, item);
    +
    +  // Simulate scrolling before the first move drag event.
    +  var scrollEvent = {
    +    'target': container
    +  };
    +  assertNotThrows(goog.bind(group.containerScrollHandler_, group, scrollEvent));
    +}
    +
    +
    +function testMouseMove_mouseOutBeforeThreshold() {
    +  // Setup dragdrop and item
    +  var itemEl = document.createElement('div');
    +  var childEl = document.createElement('div');
    +  itemEl.appendChild(childEl);
    +  var add = new goog.fx.AbstractDragDrop();
    +  var item = new goog.fx.DragDropItem(itemEl);
    +  item.setParent(add);
    +  add.items_.push(item);
    +
    +  // Simulate maybeStartDrag
    +  item.startPosition_ = new goog.math.Coordinate(10, 10);
    +  item.currentDragElement_ = itemEl;
    +
    +  // Test
    +  var draggedItem = null;
    +  add.startDrag = function(event, item) {
    +    draggedItem = item;
    +  };
    +
    +  var event = {'clientX': 8, 'clientY': 10, // Drag distance is only 2
    +    'type': goog.events.EventType.MOUSEOUT, 'target': childEl};
    +  item.mouseMove_(event);
    +  assertEquals('DragStart should not be fired for mouseout on child element.',
    +      null, draggedItem);
    +
    +  var event = {'clientX': 8, 'clientY': 10, // Drag distance is only 2
    +    'type': goog.events.EventType.MOUSEOUT, 'target': itemEl};
    +  item.mouseMove_(event);
    +  assertEquals('DragStart should be fired for mouseout on main element.',
    +      item, draggedItem);
    +}
    +
    +
    +function testGetDragElementPosition() {
    +  var testGroup = new goog.fx.AbstractDragDrop();
    +  var sourceEl = document.createElement('div');
    +  document.body.appendChild(sourceEl);
    +
    +  var pageOffset = goog.style.getPageOffset(sourceEl);
    +  var pos = testGroup.getDragElementPosition(sourceEl);
    +  assertEquals('Drag element position should be source element page offset',
    +      pageOffset.x, pos.x);
    +  assertEquals('Drag element position should be source element page offset',
    +      pageOffset.y, pos.y);
    +
    +  sourceEl.style.marginLeft = '5px';
    +  sourceEl.style.marginTop = '7px';
    +  pageOffset = goog.style.getPageOffset(sourceEl);
    +  pos = testGroup.getDragElementPosition(sourceEl);
    +  assertEquals('Drag element position should be adjusted for source element ' +
    +      'margins', pageOffset.x - 10, pos.x);
    +  assertEquals('Drag element position should be adjusted for source element ' +
    +      'margins', pageOffset.y - 14, pos.y);
    +}
    +
    +
    +// Helper function for manual debugging.
    +function drawTargets(targets, multiplier) {
    +  var colors = ['green', 'blue', 'red', 'lime', 'pink', 'silver', 'orange'];
    +  var cont = document.getElementById('cont');
    +  cont.innerHTML = '';
    +  for (var i = 0; i < targets.length; i++) {
    +    var box = targets[i].box_;
    +    var el = document.createElement('div');
    +    el.style.top = (box.top * multiplier) + 'px';
    +    el.style.left = (box.left * multiplier) + 'px';
    +    el.style.width = ((box.right - box.left) * multiplier) + 'px';
    +    el.style.height = ((box.bottom - box.top) * multiplier) + 'px';
    +    el.style.backgroundColor = colors[i];
    +    cont.appendChild(el);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/anim/anim.js b/mapbox-gl/closure-library/closure/goog/fx/anim/anim.js
    new file mode 100644
    index 0000000000..fdce5136d0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/anim/anim.js
    @@ -0,0 +1,211 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Basic animation controls.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +goog.provide('goog.fx.anim');
    +goog.provide('goog.fx.anim.Animated');
    +
    +goog.require('goog.async.AnimationDelay');
    +goog.require('goog.async.Delay');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * An interface for programatically animated objects. I.e. rendered in
    + * javascript frame by frame.
    + *
    + * @interface
    + */
    +goog.fx.anim.Animated = function() {};
    +
    +
    +/**
    + * Function called when a frame is requested for the animation.
    + *
    + * @param {number} now Current time in milliseconds.
    + */
    +goog.fx.anim.Animated.prototype.onAnimationFrame;
    +
    +
    +/**
    + * Default wait timeout for animations (in milliseconds).  Only used for timed
    + * animation, which uses a timer (setTimeout) to schedule animation.
    + *
    + * @type {number}
    + * @const
    + */
    +goog.fx.anim.TIMEOUT = goog.async.AnimationDelay.TIMEOUT;
    +
    +
    +/**
    + * A map of animations which should be cycled on the global timer.
    + *
    + * @type {Object<number, goog.fx.anim.Animated>}
    + * @private
    + */
    +goog.fx.anim.activeAnimations_ = {};
    +
    +
    +/**
    + * An optional animation window.
    + * @type {Window}
    + * @private
    + */
    +goog.fx.anim.animationWindow_ = null;
    +
    +
    +/**
    + * An interval ID for the global timer or event handler uid.
    + * @type {goog.async.Delay|goog.async.AnimationDelay}
    + * @private
    + */
    +goog.fx.anim.animationDelay_ = null;
    +
    +
    +/**
    + * Registers an animation to be cycled on the global timer.
    + * @param {goog.fx.anim.Animated} animation The animation to register.
    + */
    +goog.fx.anim.registerAnimation = function(animation) {
    +  var uid = goog.getUid(animation);
    +  if (!(uid in goog.fx.anim.activeAnimations_)) {
    +    goog.fx.anim.activeAnimations_[uid] = animation;
    +  }
    +
    +  // If the timer is not already started, start it now.
    +  goog.fx.anim.requestAnimationFrame_();
    +};
    +
    +
    +/**
    + * Removes an animation from the list of animations which are cycled on the
    + * global timer.
    + * @param {goog.fx.anim.Animated} animation The animation to unregister.
    + */
    +goog.fx.anim.unregisterAnimation = function(animation) {
    +  var uid = goog.getUid(animation);
    +  delete goog.fx.anim.activeAnimations_[uid];
    +
    +  // If a timer is running and we no longer have any active timers we stop the
    +  // timers.
    +  if (goog.object.isEmpty(goog.fx.anim.activeAnimations_)) {
    +    goog.fx.anim.cancelAnimationFrame_();
    +  }
    +};
    +
    +
    +/**
    + * Tears down this module. Useful for testing.
    + */
    +// TODO(nicksantos): Wow, this api is pretty broken. This should be fixed.
    +goog.fx.anim.tearDown = function() {
    +  goog.fx.anim.animationWindow_ = null;
    +  goog.dispose(goog.fx.anim.animationDelay_);
    +  goog.fx.anim.animationDelay_ = null;
    +  goog.fx.anim.activeAnimations_ = {};
    +};
    +
    +
    +/**
    + * Registers an animation window. This allows usage of the timing control API
    + * for animations. Note that this window must be visible, as non-visible
    + * windows can potentially stop animating. This window does not necessarily
    + * need to be the window inside which animation occurs, but must remain visible.
    + * See: https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame.
    + *
    + * @param {Window} animationWindow The window in which to animate elements.
    + */
    +goog.fx.anim.setAnimationWindow = function(animationWindow) {
    +  // If a timer is currently running, reset it and restart with new functions
    +  // after a timeout. This is to avoid mismatching timer UIDs if we change the
    +  // animation window during a running animation.
    +  //
    +  // In practice this cannot happen before some animation window and timer
    +  // control functions has already been set.
    +  var hasTimer =
    +      goog.fx.anim.animationDelay_ && goog.fx.anim.animationDelay_.isActive();
    +
    +  goog.dispose(goog.fx.anim.animationDelay_);
    +  goog.fx.anim.animationDelay_ = null;
    +  goog.fx.anim.animationWindow_ = animationWindow;
    +
    +  // If the timer was running, start it again.
    +  if (hasTimer) {
    +    goog.fx.anim.requestAnimationFrame_();
    +  }
    +};
    +
    +
    +/**
    + * Requests an animation frame based on the requestAnimationFrame and
    + * cancelRequestAnimationFrame function pair.
    + * @private
    + */
    +goog.fx.anim.requestAnimationFrame_ = function() {
    +  if (!goog.fx.anim.animationDelay_) {
    +    // We cannot guarantee that the global window will be one that fires
    +    // requestAnimationFrame events (consider off-screen chrome extension
    +    // windows). Default to use goog.async.Delay, unless
    +    // the client has explicitly set an animation window.
    +    if (goog.fx.anim.animationWindow_) {
    +      // requestAnimationFrame will call cycleAnimations_ with the current
    +      // time in ms, as returned from goog.now().
    +      goog.fx.anim.animationDelay_ = new goog.async.AnimationDelay(
    +          function(now) {
    +            goog.fx.anim.cycleAnimations_(now);
    +          }, goog.fx.anim.animationWindow_);
    +    } else {
    +      goog.fx.anim.animationDelay_ = new goog.async.Delay(function() {
    +        goog.fx.anim.cycleAnimations_(goog.now());
    +      }, goog.fx.anim.TIMEOUT);
    +    }
    +  }
    +
    +  var delay = goog.fx.anim.animationDelay_;
    +  if (!delay.isActive()) {
    +    delay.start();
    +  }
    +};
    +
    +
    +/**
    + * Cancels an animation frame created by requestAnimationFrame_().
    + * @private
    + */
    +goog.fx.anim.cancelAnimationFrame_ = function() {
    +  if (goog.fx.anim.animationDelay_) {
    +    goog.fx.anim.animationDelay_.stop();
    +  }
    +};
    +
    +
    +/**
    + * Cycles through all registered animations.
    + * @param {number} now Current time in milliseconds.
    + * @private
    + */
    +goog.fx.anim.cycleAnimations_ = function(now) {
    +  goog.object.forEach(goog.fx.anim.activeAnimations_, function(anim) {
    +    anim.onAnimationFrame(now);
    +  });
    +
    +  if (!goog.object.isEmpty(goog.fx.anim.activeAnimations_)) {
    +    goog.fx.anim.requestAnimationFrame_();
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.html b/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.html
    new file mode 100644
    index 0000000000..6c2fa205e3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.animTest');
    +  </script>
    +  <style>
    +  </style>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.js b/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.js
    new file mode 100644
    index 0000000000..b9fe8a2b7e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/anim/anim_test.js
    @@ -0,0 +1,218 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.animTest');
    +goog.setTestOnly('goog.fx.animTest');
    +
    +goog.require('goog.async.AnimationDelay');
    +goog.require('goog.async.Delay');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.anim');
    +goog.require('goog.object');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +var clock, replacer;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function setUp() {
    +  replacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  replacer.reset();
    +  goog.fx.anim.tearDown();
    +}
    +
    +function testDelayWithMocks() {
    +  goog.fx.anim.setAnimationWindow(null);
    +  registerAndUnregisterAnimationWithMocks(goog.async.Delay);
    +}
    +
    +function testAnimationDelayWithMocks() {
    +  goog.fx.anim.setAnimationWindow(window);
    +  registerAndUnregisterAnimationWithMocks(goog.async.AnimationDelay);
    +}
    +
    +
    +/**
    + * @param {!Function} delayType The constructor for Delay or AnimationDelay.
    + *     The methods will be mocked out.
    + */
    +function registerAndUnregisterAnimationWithMocks(delayType) {
    +  var timerCount = 0;
    +
    +  replacer.set(delayType.prototype, 'start', function() {
    +    timerCount++;
    +  });
    +  replacer.set(delayType.prototype, 'stop', function() {
    +    timerCount--;
    +  });
    +  replacer.set(delayType.prototype, 'isActive', function() {
    +    return timerCount > 0;
    +  });
    +
    +  var forbiddenDelayType = delayType == goog.async.AnimationDelay ?
    +      goog.async.Delay : goog.async.AnimationDelay;
    +  replacer.set(forbiddenDelayType.prototype,
    +      'start', goog.functions.error());
    +  replacer.set(forbiddenDelayType.prototype,
    +      'stop', goog.functions.error());
    +  replacer.set(forbiddenDelayType.prototype,
    +      'isActive', goog.functions.error());
    +
    +  var anim = new goog.fx.Animation([0], [1], 1000);
    +  var anim2 = new goog.fx.Animation([0], [1], 1000);
    +
    +  goog.fx.anim.registerAnimation(anim);
    +
    +  assertTrue('Should contain the animation',
    +             goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                       anim));
    +  assertEquals('Should have called start once', 1, timerCount);
    +
    +  goog.fx.anim.registerAnimation(anim2);
    +
    +  assertEquals('Should not have called start again', 1, timerCount);
    +
    +  // Add anim again.
    +  goog.fx.anim.registerAnimation(anim);
    +  assertTrue('Should contain the animation',
    +             goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                       anim));
    +  assertEquals('Should not have called start again', 1, timerCount);
    +
    +  goog.fx.anim.unregisterAnimation(anim);
    +  assertFalse('Should not contain the animation',
    +              goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                        anim));
    +  assertEquals('clearTimeout should not have been called', 1, timerCount);
    +
    +  goog.fx.anim.unregisterAnimation(anim2);
    +  assertEquals('There should be no remaining timers', 0, timerCount);
    +
    +  // Make sure we don't trigger setTimeout or setInterval.
    +  clock.tick(1000);
    +  goog.fx.anim.cycleAnimations_(goog.now());
    +
    +  assertEquals('There should be no remaining timers', 0, timerCount);
    +
    +  anim.dispose();
    +  anim2.dispose();
    +}
    +
    +function testRegisterAndUnregisterAnimationWithRequestAnimationFrameGecko() {
    +  // Only FF4 onwards support requestAnimationFrame.
    +  if (!goog.userAgent.GECKO || !goog.userAgent.isVersionOrHigher('2.0') ||
    +      goog.userAgent.isVersionOrHigher('17')) {
    +    return;
    +  }
    +
    +  goog.fx.anim.setAnimationWindow(window);
    +
    +  var anim = new goog.fx.Animation([0], [1], 1000);
    +  var anim2 = new goog.fx.Animation([0], [1], 1000);
    +
    +  goog.fx.anim.registerAnimation(anim);
    +
    +  assertTrue('Should contain the animation',
    +             goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                       anim));
    +
    +  assertEquals('Should have listen to MozBeforePaint once', 1,
    +      goog.events.getListeners(window, 'MozBeforePaint', false).length);
    +
    +  goog.fx.anim.registerAnimation(anim2);
    +
    +  assertEquals('Should not add more listener for MozBeforePaint', 1,
    +      goog.events.getListeners(window, 'MozBeforePaint', false).length);
    +
    +  // Add anim again.
    +  goog.fx.anim.registerAnimation(anim);
    +  assertTrue('Should contain the animation',
    +             goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                       anim));
    +  assertEquals('Should not add more listener for MozBeforePaint', 1,
    +      goog.events.getListeners(window, 'MozBeforePaint', false).length);
    +
    +  goog.fx.anim.unregisterAnimation(anim);
    +  assertFalse('Should not contain the animation',
    +              goog.object.containsValue(goog.fx.anim.activeAnimations_,
    +                                        anim));
    +  assertEquals('Should not clear listener for MozBeforePaint yet', 1,
    +      goog.events.getListeners(window, 'MozBeforePaint', false).length);
    +
    +  goog.fx.anim.unregisterAnimation(anim2);
    +  assertEquals('There should be no more listener for MozBeforePaint', 0,
    +      goog.events.getListeners(window, 'MozBeforePaint', false).length);
    +
    +  anim.dispose();
    +  anim2.dispose();
    +
    +  goog.fx.anim.setAnimationWindow(null);
    +}
    +
    +function testRegisterUnregisterAnimation() {
    +  var anim = new goog.fx.Animation([0], [1], 1000);
    +
    +  goog.fx.anim.registerAnimation(anim);
    +
    +  assertTrue('There should be an active timer',
    +      goog.fx.anim.animationDelay_ && goog.fx.anim.animationDelay_.isActive());
    +  assertEquals('There should be an active animations',
    +      1, goog.object.getCount(goog.fx.anim.activeAnimations_));
    +
    +  goog.fx.anim.unregisterAnimation(anim);
    +
    +  assertTrue('There should be no active animations',
    +      goog.object.isEmpty(goog.fx.anim.activeAnimations_));
    +  assertFalse('There should be no active timer',
    +      goog.fx.anim.animationDelay_ && goog.fx.anim.animationDelay_.isActive());
    +
    +  anim.dispose();
    +}
    +
    +function testCycleWithMockClock() {
    +  goog.fx.anim.setAnimationWindow(null);
    +  var anim = new goog.fx.Animation([0], [1], 1000);
    +  anim.onAnimationFrame = goog.testing.recordFunction();
    +
    +  goog.fx.anim.registerAnimation(anim);
    +  clock.tick(goog.fx.anim.TIMEOUT);
    +
    +  assertEquals(1, anim.onAnimationFrame.getCallCount());
    +}
    +
    +function testCycleWithMockClockAndAnimationWindow() {
    +  goog.fx.anim.setAnimationWindow(window);
    +  var anim = new goog.fx.Animation([0], [1], 1000);
    +  anim.onAnimationFrame = goog.testing.recordFunction();
    +
    +  goog.fx.anim.registerAnimation(anim);
    +  clock.tick(goog.fx.anim.TIMEOUT);
    +
    +  assertEquals(1, anim.onAnimationFrame.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animation.js b/mapbox-gl/closure-library/closure/goog/fx/animation.js
    new file mode 100644
    index 0000000000..0a4401b219
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animation.js
    @@ -0,0 +1,524 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Classes for doing animations and visual effects.
    + *
    + * (Based loosly on my animation code for 13thparallel.org, with extra
    + * inspiration from the DojoToolkit's modifications to my code)
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.fx.Animation');
    +goog.provide('goog.fx.Animation.EventType');
    +goog.provide('goog.fx.Animation.State');
    +goog.provide('goog.fx.AnimationEvent');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.Event');
    +goog.require('goog.fx.Transition');  // Unreferenced: interface
    +goog.require('goog.fx.TransitionBase');
    +goog.require('goog.fx.anim');
    +goog.require('goog.fx.anim.Animated');  // Unreferenced: interface
    +
    +
    +
    +/**
    + * Constructor for an animation object.
    + * @param {Array<number>} start Array for start coordinates.
    + * @param {Array<number>} end Array for end coordinates.
    + * @param {number} duration Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @constructor
    + * @implements {goog.fx.anim.Animated}
    + * @implements {goog.fx.Transition}
    + * @extends {goog.fx.TransitionBase}
    + */
    +goog.fx.Animation = function(start, end, duration, opt_acc) {
    +  goog.fx.Animation.base(this, 'constructor');
    +
    +  if (!goog.isArray(start) || !goog.isArray(end)) {
    +    throw Error('Start and end parameters must be arrays');
    +  }
    +
    +  if (start.length != end.length) {
    +    throw Error('Start and end points must be the same length');
    +  }
    +
    +  /**
    +   * Start point.
    +   * @type {Array<number>}
    +   * @protected
    +   */
    +  this.startPoint = start;
    +
    +  /**
    +   * End point.
    +   * @type {Array<number>}
    +   * @protected
    +   */
    +  this.endPoint = end;
    +
    +  /**
    +   * Duration of animation in milliseconds.
    +   * @type {number}
    +   * @protected
    +   */
    +  this.duration = duration;
    +
    +  /**
    +   * Acceleration function, which must return a number between 0 and 1 for
    +   * inputs between 0 and 1.
    +   * @type {Function|undefined}
    +   * @private
    +   */
    +  this.accel_ = opt_acc;
    +
    +  /**
    +   * Current coordinate for animation.
    +   * @type {Array<number>}
    +   * @protected
    +   */
    +  this.coords = [];
    +
    +  /**
    +   * Whether the animation should use "right" rather than "left" to position
    +   * elements in RTL.  This is a temporary flag to allow clients to transition
    +   * to the new behavior at their convenience.  At some point it will be the
    +   * default.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useRightPositioningForRtl_ = false;
    +
    +  /**
    +   * Current frame rate.
    +   * @private {number}
    +   */
    +  this.fps_ = 0;
    +
    +  /**
    +   * Percent of the way through the animation.
    +   * @protected {number}
    +   */
    +  this.progress = 0;
    +
    +  /**
    +   * Timestamp for when last frame was run.
    +   * @protected {?number}
    +   */
    +  this.lastFrame = null;
    +};
    +goog.inherits(goog.fx.Animation, goog.fx.TransitionBase);
    +
    +
    +/**
    + * Sets whether the animation should use "right" rather than "left" to position
    + * elements.  This is a temporary flag to allow clients to transition
    + * to the new component at their convenience.  At some point "right" will be
    + * used for RTL elements by default.
    + * @param {boolean} useRightPositioningForRtl True if "right" should be used for
    + *     positioning, false if "left" should be used for positioning.
    + */
    +goog.fx.Animation.prototype.enableRightPositioningForRtl =
    +    function(useRightPositioningForRtl) {
    +  this.useRightPositioningForRtl_ = useRightPositioningForRtl;
    +};
    +
    +
    +/**
    + * Whether the animation should use "right" rather than "left" to position
    + * elements.  This is a temporary flag to allow clients to transition
    + * to the new component at their convenience.  At some point "right" will be
    + * used for RTL elements by default.
    + * @return {boolean} True if "right" should be used for positioning, false if
    + *     "left" should be used for positioning.
    + */
    +goog.fx.Animation.prototype.isRightPositioningForRtlEnabled = function() {
    +  return this.useRightPositioningForRtl_;
    +};
    +
    +
    +/**
    + * Events fired by the animation.
    + * @enum {string}
    + */
    +goog.fx.Animation.EventType = {
    +  /**
    +   * Dispatched when played for the first time OR when it is resumed.
    +   * @deprecated Use goog.fx.Transition.EventType.PLAY.
    +   */
    +  PLAY: goog.fx.Transition.EventType.PLAY,
    +
    +  /**
    +   * Dispatched only when the animation starts from the beginning.
    +   * @deprecated Use goog.fx.Transition.EventType.BEGIN.
    +   */
    +  BEGIN: goog.fx.Transition.EventType.BEGIN,
    +
    +  /**
    +   * Dispatched only when animation is restarted after a pause.
    +   * @deprecated Use goog.fx.Transition.EventType.RESUME.
    +   */
    +  RESUME: goog.fx.Transition.EventType.RESUME,
    +
    +  /**
    +   * Dispatched when animation comes to the end of its duration OR stop
    +   * is called.
    +   * @deprecated Use goog.fx.Transition.EventType.END.
    +   */
    +  END: goog.fx.Transition.EventType.END,
    +
    +  /**
    +   * Dispatched only when stop is called.
    +   * @deprecated Use goog.fx.Transition.EventType.STOP.
    +   */
    +  STOP: goog.fx.Transition.EventType.STOP,
    +
    +  /**
    +   * Dispatched only when animation comes to its end naturally.
    +   * @deprecated Use goog.fx.Transition.EventType.FINISH.
    +   */
    +  FINISH: goog.fx.Transition.EventType.FINISH,
    +
    +  /**
    +   * Dispatched when an animation is paused.
    +   * @deprecated Use goog.fx.Transition.EventType.PAUSE.
    +   */
    +  PAUSE: goog.fx.Transition.EventType.PAUSE,
    +
    +  /**
    +   * Dispatched each frame of the animation.  This is where the actual animator
    +   * will listen.
    +   */
    +  ANIMATE: 'animate',
    +
    +  /**
    +   * Dispatched when the animation is destroyed.
    +   */
    +  DESTROY: 'destroy'
    +};
    +
    +
    +/**
    + * @deprecated Use goog.fx.anim.TIMEOUT.
    + */
    +goog.fx.Animation.TIMEOUT = goog.fx.anim.TIMEOUT;
    +
    +
    +/**
    + * Enum for the possible states of an animation.
    + * @deprecated Use goog.fx.Transition.State instead.
    + * @enum {number}
    + */
    +goog.fx.Animation.State = goog.fx.TransitionBase.State;
    +
    +
    +/**
    + * @deprecated Use goog.fx.anim.setAnimationWindow.
    + * @param {Window} animationWindow The window in which to animate elements.
    + */
    +goog.fx.Animation.setAnimationWindow = function(animationWindow) {
    +  goog.fx.anim.setAnimationWindow(animationWindow);
    +};
    +
    +
    +/**
    + * Starts or resumes an animation.
    + * @param {boolean=} opt_restart Whether to restart the
    + *     animation from the beginning if it has been paused.
    + * @return {boolean} Whether animation was started.
    + * @override
    + */
    +goog.fx.Animation.prototype.play = function(opt_restart) {
    +  if (opt_restart || this.isStopped()) {
    +    this.progress = 0;
    +    this.coords = this.startPoint;
    +  } else if (this.isPlaying()) {
    +    return false;
    +  }
    +
    +  goog.fx.anim.unregisterAnimation(this);
    +
    +  var now = /** @type {number} */ (goog.now());
    +
    +  this.startTime = now;
    +  if (this.isPaused()) {
    +    this.startTime -= this.duration * this.progress;
    +  }
    +
    +  this.endTime = this.startTime + this.duration;
    +  this.lastFrame = this.startTime;
    +
    +  if (!this.progress) {
    +    this.onBegin();
    +  }
    +
    +  this.onPlay();
    +
    +  if (this.isPaused()) {
    +    this.onResume();
    +  }
    +
    +  this.setStatePlaying();
    +
    +  goog.fx.anim.registerAnimation(this);
    +  this.cycle(now);
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Stops the animation.
    + * @param {boolean=} opt_gotoEnd If true the animation will move to the
    + *     end coords.
    + * @override
    + */
    +goog.fx.Animation.prototype.stop = function(opt_gotoEnd) {
    +  goog.fx.anim.unregisterAnimation(this);
    +  this.setStateStopped();
    +
    +  if (!!opt_gotoEnd) {
    +    this.progress = 1;
    +  }
    +
    +  this.updateCoords_(this.progress);
    +
    +  this.onStop();
    +  this.onEnd();
    +};
    +
    +
    +/**
    + * Pauses the animation (iff it's playing).
    + * @override
    + */
    +goog.fx.Animation.prototype.pause = function() {
    +  if (this.isPlaying()) {
    +    goog.fx.anim.unregisterAnimation(this);
    +    this.setStatePaused();
    +    this.onPause();
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The current progress of the animation, the number
    + *     is between 0 and 1 inclusive.
    + */
    +goog.fx.Animation.prototype.getProgress = function() {
    +  return this.progress;
    +};
    +
    +
    +/**
    + * Sets the progress of the animation.
    + * @param {number} progress The new progress of the animation.
    + */
    +goog.fx.Animation.prototype.setProgress = function(progress) {
    +  this.progress = progress;
    +  if (this.isPlaying()) {
    +    var now = goog.now();
    +    // If the animation is already playing, we recompute startTime and endTime
    +    // such that the animation plays consistently, that is:
    +    // now = startTime + progress * duration.
    +    this.startTime = now - this.duration * this.progress;
    +    this.endTime = this.startTime + this.duration;
    +  }
    +};
    +
    +
    +/**
    + * Disposes of the animation.  Stops an animation, fires a 'destroy' event and
    + * then removes all the event handlers to clean up memory.
    + * @override
    + * @protected
    + */
    +goog.fx.Animation.prototype.disposeInternal = function() {
    +  if (!this.isStopped()) {
    +    this.stop(false);
    +  }
    +  this.onDestroy();
    +  goog.fx.Animation.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Stops an animation, fires a 'destroy' event and then removes all the event
    + * handlers to clean up memory.
    + * @deprecated Use dispose() instead.
    + */
    +goog.fx.Animation.prototype.destroy = function() {
    +  this.dispose();
    +};
    +
    +
    +/** @override */
    +goog.fx.Animation.prototype.onAnimationFrame = function(now) {
    +  this.cycle(now);
    +};
    +
    +
    +/**
    + * Handles the actual iteration of the animation in a timeout
    + * @param {number} now The current time.
    + */
    +goog.fx.Animation.prototype.cycle = function(now) {
    +  this.progress = (now - this.startTime) / (this.endTime - this.startTime);
    +
    +  if (this.progress >= 1) {
    +    this.progress = 1;
    +  }
    +
    +  this.fps_ = 1000 / (now - this.lastFrame);
    +  this.lastFrame = now;
    +
    +  this.updateCoords_(this.progress);
    +
    +  // Animation has finished.
    +  if (this.progress == 1) {
    +    this.setStateStopped();
    +    goog.fx.anim.unregisterAnimation(this);
    +
    +    this.onFinish();
    +    this.onEnd();
    +
    +  // Animation is still under way.
    +  } else if (this.isPlaying()) {
    +    this.onAnimate();
    +  }
    +};
    +
    +
    +/**
    + * Calculates current coordinates, based on the current state.  Applies
    + * the accelleration function if it exists.
    + * @param {number} t Percentage of the way through the animation as a decimal.
    + * @private
    + */
    +goog.fx.Animation.prototype.updateCoords_ = function(t) {
    +  if (goog.isFunction(this.accel_)) {
    +    t = this.accel_(t);
    +  }
    +  this.coords = new Array(this.startPoint.length);
    +  for (var i = 0; i < this.startPoint.length; i++) {
    +    this.coords[i] = (this.endPoint[i] - this.startPoint[i]) * t +
    +        this.startPoint[i];
    +  }
    +};
    +
    +
    +/**
    + * Dispatches the ANIMATE event. Sub classes should override this instead
    + * of listening to the event.
    + * @protected
    + */
    +goog.fx.Animation.prototype.onAnimate = function() {
    +  this.dispatchAnimationEvent(goog.fx.Animation.EventType.ANIMATE);
    +};
    +
    +
    +/**
    + * Dispatches the DESTROY event. Sub classes should override this instead
    + * of listening to the event.
    + * @protected
    + */
    +goog.fx.Animation.prototype.onDestroy = function() {
    +  this.dispatchAnimationEvent(goog.fx.Animation.EventType.DESTROY);
    +};
    +
    +
    +/** @override */
    +goog.fx.Animation.prototype.dispatchAnimationEvent = function(type) {
    +  this.dispatchEvent(new goog.fx.AnimationEvent(type, this));
    +};
    +
    +
    +
    +/**
    + * Class for an animation event object.
    + * @param {string} type Event type.
    + * @param {goog.fx.Animation} anim An animation object.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.fx.AnimationEvent = function(type, anim) {
    +  goog.fx.AnimationEvent.base(this, 'constructor', type);
    +
    +  /**
    +   * The current coordinates.
    +   * @type {Array<number>}
    +   */
    +  this.coords = anim.coords;
    +
    +  /**
    +   * The x coordinate.
    +   * @type {number}
    +   */
    +  this.x = anim.coords[0];
    +
    +  /**
    +   * The y coordinate.
    +   * @type {number}
    +   */
    +  this.y = anim.coords[1];
    +
    +  /**
    +   * The z coordinate.
    +   * @type {number}
    +   */
    +  this.z = anim.coords[2];
    +
    +  /**
    +   * The current duration.
    +   * @type {number}
    +   */
    +  this.duration = anim.duration;
    +
    +  /**
    +   * The current progress.
    +   * @type {number}
    +   */
    +  this.progress = anim.getProgress();
    +
    +  /**
    +   * Frames per second so far.
    +   */
    +  this.fps = anim.fps_;
    +
    +  /**
    +   * The state of the animation.
    +   * @type {number}
    +   */
    +  this.state = anim.getStateInternal();
    +
    +  /**
    +   * The animation object.
    +   * @type {goog.fx.Animation}
    +   */
    +  // TODO(arv): This can be removed as this is the same as the target
    +  this.anim = anim;
    +};
    +goog.inherits(goog.fx.AnimationEvent, goog.events.Event);
    +
    +
    +/**
    + * Returns the coordinates as integers (rounded to nearest integer).
    + * @return {!Array<number>} An array of the coordinates rounded to
    + *     the nearest integer.
    + */
    +goog.fx.AnimationEvent.prototype.coordsAsInts = function() {
    +  return goog.array.map(this.coords, Math.round);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animation_test.html b/mapbox-gl/closure-library/closure/goog/fx/animation_test.html
    new file mode 100644
    index 0000000000..e365b285e6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animation_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.Animation
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.AnimationTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animation_test.js b/mapbox-gl/closure-library/closure/goog/fx/animation_test.js
    new file mode 100644
    index 0000000000..185763ae79
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animation_test.js
    @@ -0,0 +1,116 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.AnimationTest');
    +goog.setTestOnly('goog.fx.AnimationTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var clock;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function testPauseLogic() {
    +  var anim = new goog.fx.Animation([], [], 3000);
    +  var nFrames = 0;
    +  goog.events.listen(anim, goog.fx.Animation.EventType.ANIMATE, function(e) {
    +    assertRoughlyEquals(e.progress, progress, 1e-6);
    +    nFrames++;
    +  });
    +  goog.events.listen(anim, goog.fx.Animation.EventType.END, function(e) {
    +    nFrames++;
    +  });
    +  var nSteps = 10;
    +  for (var i = 0; i < nSteps; i++) {
    +    progress = i / (nSteps - 1);
    +    anim.setProgress(progress);
    +    anim.play();
    +    anim.pause();
    +  }
    +  assertEquals(nSteps, nFrames);
    +}
    +
    +function testPauseOffset() {
    +  var anim = new goog.fx.Animation([0], [1000], 1000);
    +  anim.play();
    +
    +  assertEquals(0, anim.coords[0]);
    +  assertRoughlyEquals(0, anim.progress, 1e-4);
    +
    +  clock.tick(300);
    +
    +  assertEquals(300, anim.coords[0]);
    +  assertRoughlyEquals(0.3, anim.progress, 1e-4);
    +
    +  anim.pause();
    +
    +  clock.tick(400);
    +
    +  assertEquals(300, anim.coords[0]);
    +  assertRoughlyEquals(0.3, anim.progress, 1e-4);
    +
    +  anim.play();
    +
    +  assertEquals(300, anim.coords[0]);
    +  assertRoughlyEquals(0.3, anim.progress, 1e-4);
    +
    +  clock.tick(400);
    +
    +  assertEquals(700, anim.coords[0]);
    +  assertRoughlyEquals(0.7, anim.progress, 1e-4);
    +
    +  anim.pause();
    +
    +  clock.tick(300);
    +
    +  assertEquals(700, anim.coords[0]);
    +  assertRoughlyEquals(0.7, anim.progress, 1e-4);
    +
    +  anim.play();
    +
    +  var lastPlay = goog.now();
    +
    +  assertEquals(700, anim.coords[0]);
    +  assertRoughlyEquals(0.7, anim.progress, 1e-4);
    +
    +  clock.tick(300);
    +
    +  assertEquals(1000, anim.coords[0]);
    +  assertRoughlyEquals(1, anim.progress, 1e-4);
    +  assertEquals(goog.fx.Animation.State.STOPPED, anim.getStateInternal());
    +}
    +
    +function testSetProgress() {
    +  var anim = new goog.fx.Animation([0], [1000], 3000);
    +  var nFrames = 0;
    +  anim.play();
    +  anim.setProgress(0.5);
    +  goog.events.listen(anim, goog.fx.Animation.EventType.ANIMATE, function(e) {
    +    assertEquals(500, e.coords[0]);
    +    assertRoughlyEquals(0.5, e.progress, 1e-4);
    +    nFrames++;
    +  });
    +  anim.cycle(goog.now());
    +  anim.stop();
    +  assertEquals(1, nFrames);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animationqueue.js b/mapbox-gl/closure-library/closure/goog/fx/animationqueue.js
    new file mode 100644
    index 0000000000..2ad74ab867
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animationqueue.js
    @@ -0,0 +1,310 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class which automatically plays through a queue of
    + * animations.  AnimationParallelQueue and AnimationSerialQueue provide
    + * specific implementations of the abstract class AnimationQueue.
    + *
    + * @see ../demos/animationqueue.html
    + */
    +
    +goog.provide('goog.fx.AnimationParallelQueue');
    +goog.provide('goog.fx.AnimationQueue');
    +goog.provide('goog.fx.AnimationSerialQueue');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.TransitionBase');
    +
    +
    +
    +/**
    + * Constructor for AnimationQueue object.
    + *
    + * @constructor
    + * @extends {goog.fx.TransitionBase}
    + * @struct
    + * @suppress {checkStructDictInheritance}
    + */
    +goog.fx.AnimationQueue = function() {
    +  goog.fx.AnimationQueue.base(this, 'constructor');
    +
    +  /**
    +   * An array holding all animations in the queue.
    +   * @type {Array<goog.fx.TransitionBase>}
    +   * @protected
    +   */
    +  this.queue = [];
    +};
    +goog.inherits(goog.fx.AnimationQueue, goog.fx.TransitionBase);
    +
    +
    +/**
    + * Pushes an Animation to the end of the queue.
    + * @param {goog.fx.TransitionBase} animation The animation to add to the queue.
    + */
    +goog.fx.AnimationQueue.prototype.add = function(animation) {
    +  goog.asserts.assert(this.isStopped(),
    +      'Not allowed to add animations to a running animation queue.');
    +
    +  if (goog.array.contains(this.queue, animation)) {
    +    return;
    +  }
    +
    +  this.queue.push(animation);
    +  goog.events.listen(animation, goog.fx.Transition.EventType.FINISH,
    +                     this.onAnimationFinish, false, this);
    +};
    +
    +
    +/**
    + * Removes an Animation from the queue.
    + * @param {goog.fx.Animation} animation The animation to remove.
    + */
    +goog.fx.AnimationQueue.prototype.remove = function(animation) {
    +  goog.asserts.assert(this.isStopped(),
    +      'Not allowed to remove animations from a running animation queue.');
    +
    +  if (goog.array.remove(this.queue, animation)) {
    +    goog.events.unlisten(animation, goog.fx.Transition.EventType.FINISH,
    +                         this.onAnimationFinish, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Handles the event that an animation has finished.
    + * @param {goog.events.Event} e The finishing event.
    + * @protected
    + */
    +goog.fx.AnimationQueue.prototype.onAnimationFinish = goog.abstractMethod;
    +
    +
    +/**
    + * Disposes of the animations.
    + * @override
    + */
    +goog.fx.AnimationQueue.prototype.disposeInternal = function() {
    +  goog.array.forEach(this.queue, function(animation) {
    +    animation.dispose();
    +  });
    +  this.queue.length = 0;
    +
    +  goog.fx.AnimationQueue.base(this, 'disposeInternal');
    +};
    +
    +
    +
    +/**
    + * Constructor for AnimationParallelQueue object.
    + * @constructor
    + * @extends {goog.fx.AnimationQueue}
    + * @struct
    + */
    +goog.fx.AnimationParallelQueue = function() {
    +  goog.fx.AnimationParallelQueue.base(this, 'constructor');
    +
    +  /**
    +   * Number of finished animations.
    +   * @type {number}
    +   * @private
    +   */
    +  this.finishedCounter_ = 0;
    +};
    +goog.inherits(goog.fx.AnimationParallelQueue, goog.fx.AnimationQueue);
    +
    +
    +/** @override */
    +goog.fx.AnimationParallelQueue.prototype.play = function(opt_restart) {
    +  if (this.queue.length == 0) {
    +    return false;
    +  }
    +
    +  if (opt_restart || this.isStopped()) {
    +    this.finishedCounter_ = 0;
    +    this.onBegin();
    +  } else if (this.isPlaying()) {
    +    return false;
    +  }
    +
    +  this.onPlay();
    +  if (this.isPaused()) {
    +    this.onResume();
    +  }
    +  var resuming = this.isPaused() && !opt_restart;
    +
    +  this.startTime = goog.now();
    +  this.endTime = null;
    +  this.setStatePlaying();
    +
    +  goog.array.forEach(this.queue, function(anim) {
    +    if (!resuming || anim.isPaused()) {
    +      anim.play(opt_restart);
    +    }
    +  });
    +
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationParallelQueue.prototype.pause = function() {
    +  if (this.isPlaying()) {
    +    goog.array.forEach(this.queue, function(anim) {
    +      if (anim.isPlaying()) {
    +        anim.pause();
    +      }
    +    });
    +
    +    this.setStatePaused();
    +    this.onPause();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationParallelQueue.prototype.stop = function(opt_gotoEnd) {
    +  goog.array.forEach(this.queue, function(anim) {
    +    if (!anim.isStopped()) {
    +      anim.stop(opt_gotoEnd);
    +    }
    +  });
    +
    +  this.setStateStopped();
    +  this.endTime = goog.now();
    +
    +  this.onStop();
    +  this.onEnd();
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationParallelQueue.prototype.onAnimationFinish = function(e) {
    +  this.finishedCounter_++;
    +  if (this.finishedCounter_ == this.queue.length) {
    +    this.endTime = goog.now();
    +
    +    this.setStateStopped();
    +
    +    this.onFinish();
    +    this.onEnd();
    +  }
    +};
    +
    +
    +
    +/**
    + * Constructor for AnimationSerialQueue object.
    + * @constructor
    + * @extends {goog.fx.AnimationQueue}
    + * @struct
    + */
    +goog.fx.AnimationSerialQueue = function() {
    +  goog.fx.AnimationSerialQueue.base(this, 'constructor');
    +
    +  /**
    +   * Current animation in queue currently active.
    +   * @type {number}
    +   * @private
    +   */
    +  this.current_ = 0;
    +};
    +goog.inherits(goog.fx.AnimationSerialQueue, goog.fx.AnimationQueue);
    +
    +
    +/** @override */
    +goog.fx.AnimationSerialQueue.prototype.play = function(opt_restart) {
    +  if (this.queue.length == 0) {
    +    return false;
    +  }
    +
    +  if (opt_restart || this.isStopped()) {
    +    if (this.current_ < this.queue.length &&
    +        !this.queue[this.current_].isStopped()) {
    +      this.queue[this.current_].stop(false);
    +    }
    +
    +    this.current_ = 0;
    +    this.onBegin();
    +  } else if (this.isPlaying()) {
    +    return false;
    +  }
    +
    +  this.onPlay();
    +  if (this.isPaused()) {
    +    this.onResume();
    +  }
    +
    +  this.startTime = goog.now();
    +  this.endTime = null;
    +  this.setStatePlaying();
    +
    +  this.queue[this.current_].play(opt_restart);
    +
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationSerialQueue.prototype.pause = function() {
    +  if (this.isPlaying()) {
    +    this.queue[this.current_].pause();
    +    this.setStatePaused();
    +    this.onPause();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationSerialQueue.prototype.stop = function(opt_gotoEnd) {
    +  this.setStateStopped();
    +  this.endTime = goog.now();
    +
    +  if (opt_gotoEnd) {
    +    for (var i = this.current_; i < this.queue.length; ++i) {
    +      var anim = this.queue[i];
    +      // If the animation is stopped, start it to initiate rendering.  This
    +      // might be needed to make the next line work.
    +      if (anim.isStopped()) anim.play();
    +      // If the animation is not done, stop it and go to the end state of the
    +      // animation.
    +      if (!anim.isStopped()) anim.stop(true);
    +    }
    +  } else if (this.current_ < this.queue.length) {
    +    this.queue[this.current_].stop(false);
    +  }
    +
    +  this.onStop();
    +  this.onEnd();
    +};
    +
    +
    +/** @override */
    +goog.fx.AnimationSerialQueue.prototype.onAnimationFinish = function(e) {
    +  if (this.isPlaying()) {
    +    this.current_++;
    +    if (this.current_ < this.queue.length) {
    +      this.queue[this.current_].play();
    +    } else {
    +      this.endTime = goog.now();
    +      this.setStateStopped();
    +
    +      this.onFinish();
    +      this.onEnd();
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.html b/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.html
    new file mode 100644
    index 0000000000..408fce428b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.Animation
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.fx.AnimationQueueTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.js b/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.js
    new file mode 100644
    index 0000000000..5f788fef76
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/animationqueue_test.js
    @@ -0,0 +1,315 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.AnimationQueueTest');
    +goog.setTestOnly('goog.fx.AnimationQueueTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.AnimationParallelQueue');
    +goog.require('goog.fx.AnimationSerialQueue');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.anim');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var clock;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +  goog.fx.anim.setAnimationWindow(null);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function testParallelEvents() {
    +  var anim = new goog.fx.AnimationParallelQueue();
    +  anim.add(new goog.fx.Animation([0], [100], 200));
    +  anim.add(new goog.fx.Animation([0], [100], 400));
    +  anim.add(new goog.fx.Animation([0], [100], 600));
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +
    +  var playEvents = 0, beginEvents = 0, resumeEvents = 0, pauseEvents = 0;
    +  var endEvents = 0, stopEvents = 0, finishEvents = 0;
    +
    +  goog.events.listen(anim, goog.fx.Transition.EventType.PLAY, function() {
    +    ++playEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.BEGIN, function() {
    +    ++beginEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.RESUME, function() {
    +    ++resumeEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.PAUSE, function() {
    +    ++pauseEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.END, function() {
    +    ++endEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.STOP, function() {
    +    ++stopEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.FINISH, function() {
    +    ++finishEvents; });
    +
    +  // PLAY, BEGIN
    +  anim.play();
    +  // No queue events.
    +  clock.tick(100);
    +  // PAUSE
    +  anim.pause();
    +  // No queue events
    +  clock.tick(200);
    +  // PLAY, RESUME
    +  anim.play();
    +  // No queue events.
    +  clock.tick(400);
    +  // END, STOP
    +  anim.stop();
    +  // PLAY, BEGIN
    +  anim.play();
    +  // No queue events.
    +  clock.tick(400);
    +  // END, FINISH
    +  clock.tick(200);
    +
    +  // Make sure the event counts are right.
    +  assertEquals(3, playEvents);
    +  assertEquals(2, beginEvents);
    +  assertEquals(1, resumeEvents);
    +  assertEquals(1, pauseEvents);
    +  assertEquals(2, endEvents);
    +  assertEquals(1, stopEvents);
    +  assertEquals(1, finishEvents);
    +}
    +
    +function testSerialEvents() {
    +  var anim = new goog.fx.AnimationSerialQueue();
    +  anim.add(new goog.fx.Animation([0], [100], 100));
    +  anim.add(new goog.fx.Animation([0], [100], 200));
    +  anim.add(new goog.fx.Animation([0], [100], 300));
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +
    +  var playEvents = 0, beginEvents = 0, resumeEvents = 0, pauseEvents = 0;
    +  var endEvents = 0, stopEvents = 0, finishEvents = 0;
    +
    +  goog.events.listen(anim, goog.fx.Transition.EventType.PLAY, function() {
    +    ++playEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.BEGIN, function() {
    +    ++beginEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.RESUME, function() {
    +    ++resumeEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.PAUSE, function() {
    +    ++pauseEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.END, function() {
    +    ++endEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.STOP, function() {
    +    ++stopEvents; });
    +  goog.events.listen(anim, goog.fx.Transition.EventType.FINISH, function() {
    +    ++finishEvents; });
    +
    +  // PLAY, BEGIN
    +  anim.play();
    +  // No queue events.
    +  clock.tick(100);
    +  // PAUSE
    +  anim.pause();
    +  // No queue events
    +  clock.tick(200);
    +  // PLAY, RESUME
    +  anim.play();
    +  // No queue events.
    +  clock.tick(400);
    +  // END, STOP
    +  anim.stop();
    +  // PLAY, BEGIN
    +  anim.play(true);
    +  // No queue events.
    +  clock.tick(400);
    +  // END, FINISH
    +  clock.tick(200);
    +
    +  // Make sure the event counts are right.
    +  assertEquals(3, playEvents);
    +  assertEquals(2, beginEvents);
    +  assertEquals(1, resumeEvents);
    +  assertEquals(1, pauseEvents);
    +  assertEquals(2, endEvents);
    +  assertEquals(1, stopEvents);
    +  assertEquals(1, finishEvents);
    +}
    +
    +function testParallelPause() {
    +  var anim = new goog.fx.AnimationParallelQueue();
    +  anim.add(new goog.fx.Animation([0], [100], 100));
    +  anim.add(new goog.fx.Animation([0], [100], 200));
    +  anim.add(new goog.fx.Animation([0], [100], 300));
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +
    +  anim.play();
    +
    +  assertTrue(anim.queue[0].isPlaying());
    +  assertTrue(anim.queue[1].isPlaying());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  clock.tick(100);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPlaying());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  anim.pause();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPaused());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  clock.tick(200);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPaused());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  anim.play();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPlaying());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  clock.tick(100);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  anim.pause();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  clock.tick(200);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  anim.play();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  clock.tick(100);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +}
    +
    +function testSerialPause() {
    +  var anim = new goog.fx.AnimationSerialQueue();
    +  anim.add(new goog.fx.Animation([0], [100], 100));
    +  anim.add(new goog.fx.Animation([0], [100], 200));
    +  anim.add(new goog.fx.Animation([0], [100], 300));
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +
    +  anim.play();
    +
    +  assertTrue(anim.queue[0].isPlaying());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isPlaying());
    +
    +  clock.tick(100);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPlaying());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isPlaying());
    +
    +  anim.pause();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPaused());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isPaused());
    +
    +  clock.tick(400);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPaused());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isPaused());
    +
    +  anim.play();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isPlaying());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isPlaying());
    +
    +  clock.tick(200);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPlaying());
    +  assertTrue(anim.isPlaying());
    +
    +  anim.pause();
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  clock.tick(300);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isPaused());
    +  assertTrue(anim.isPaused());
    +
    +  anim.play();
    +
    +  clock.tick(300);
    +
    +  assertTrue(anim.queue[0].isStopped());
    +  assertTrue(anim.queue[1].isStopped());
    +  assertTrue(anim.queue[2].isStopped());
    +  assertTrue(anim.isStopped());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/css3/fx.js b/mapbox-gl/closure-library/closure/goog/fx/css3/fx.js
    new file mode 100644
    index 0000000000..267c78a4c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/css3/fx.js
    @@ -0,0 +1,63 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A collection of CSS3 targeted animation, based on
    + * {@code goog.fx.css3.Transition}.
    + *
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.fx.css3');
    +
    +goog.require('goog.fx.css3.Transition');
    +
    +
    +/**
    + * Creates a transition to fade the element.
    + * @param {Element} element The element to fade.
    + * @param {number} duration Duration in seconds.
    + * @param {string} timing The CSS3 timing function.
    + * @param {number} startOpacity Starting opacity.
    + * @param {number} endOpacity Ending opacity.
    + * @return {!goog.fx.css3.Transition} The transition object.
    + */
    +goog.fx.css3.fade = function(
    +    element, duration, timing,  startOpacity, endOpacity) {
    +  return new goog.fx.css3.Transition(
    +      element, duration, {'opacity': startOpacity}, {'opacity': endOpacity},
    +      {property: 'opacity', duration: duration, timing: timing, delay: 0});
    +};
    +
    +
    +/**
    + * Creates a transition to fade in the element.
    + * @param {Element} element The element to fade in.
    + * @param {number} duration Duration in seconds.
    + * @return {!goog.fx.css3.Transition} The transition object.
    + */
    +goog.fx.css3.fadeIn = function(element, duration) {
    +  return goog.fx.css3.fade(element, duration, 'ease-out', 0, 1);
    +};
    +
    +
    +/**
    + * Creates a transition to fade out the element.
    + * @param {Element} element The element to fade out.
    + * @param {number} duration Duration in seconds.
    + * @return {!goog.fx.css3.Transition} The transition object.
    + */
    +goog.fx.css3.fadeOut = function(element, duration) {
    +  return goog.fx.css3.fade(element, duration, 'ease-in', 1, 0);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/css3/transition.js b/mapbox-gl/closure-library/closure/goog/fx/css3/transition.js
    new file mode 100644
    index 0000000000..59ec3f7860
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/css3/transition.js
    @@ -0,0 +1,201 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview CSS3 transition base library.
    + *
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.fx.css3.Transition');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.fx.TransitionBase');
    +goog.require('goog.style');
    +goog.require('goog.style.transition');
    +
    +
    +
    +/**
    + * A class to handle targeted CSS3 transition. This class
    + * handles common features required for targeted CSS3 transition.
    + *
    + * Browser that does not support CSS3 transition will still receive all
    + * the events fired by the transition object, but will not have any transition
    + * played. If the browser supports the final state as set in setFinalState
    + * method, the element will ends in the final state.
    + *
    + * Transitioning multiple properties with the same setting is possible
    + * by setting Css3Property's property to 'all'. Performing multiple
    + * transitions can be done via setting multiple initialStyle,
    + * finalStyle and transitions. Css3Property's delay can be used to
    + * delay one of the transition. Here is an example for a transition
    + * that expands on the width and then followed by the height:
    + *
    + * <pre>
    + *   initialStyle: {width: 10px, height: 10px}
    + *   finalStyle: {width: 100px, height: 100px}
    + *   transitions: [
    + *     {property: width, duration: 1, timing: 'ease-in', delay: 0},
    + *     {property: height, duration: 1, timing: 'ease-in', delay: 1}
    + *   ]
    + * </pre>
    + *
    + * @param {Element} element The element to be transitioned.
    + * @param {number} duration The duration of the transition in seconds.
    + *     This should be the longest of all transitions.
    + * @param {Object} initialStyle Initial style properties of the element before
    + *     animating. Set using {@code goog.style.setStyle}.
    + * @param {Object} finalStyle Final style properties of the element after
    + *     animating. Set using {@code goog.style.setStyle}.
    + * @param {goog.style.transition.Css3Property|
    + *     Array<goog.style.transition.Css3Property>} transitions A single CSS3
    + *     transition property or an array of it.
    + * @extends {goog.fx.TransitionBase}
    + * @constructor
    + */
    +goog.fx.css3.Transition = function(
    +    element, duration, initialStyle, finalStyle, transitions) {
    +  goog.fx.css3.Transition.base(this, 'constructor');
    +
    +  /**
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.duration_ = duration;
    +
    +  /**
    +   * @type {Object}
    +   * @private
    +   */
    +  this.initialStyle_ = initialStyle;
    +
    +  /**
    +   * @type {Object}
    +   * @private
    +   */
    +  this.finalStyle_ = finalStyle;
    +
    +  /**
    +   * @type {Array<goog.style.transition.Css3Property>}
    +   * @private
    +   */
    +  this.transitions_ = goog.isArray(transitions) ? transitions : [transitions];
    +};
    +goog.inherits(goog.fx.css3.Transition, goog.fx.TransitionBase);
    +
    +
    +/**
    + * Timer id to be used to cancel animation part-way.
    + * @type {number}
    + * @private
    + */
    +goog.fx.css3.Transition.prototype.timerId_;
    +
    +
    +/** @override */
    +goog.fx.css3.Transition.prototype.play = function() {
    +  if (this.isPlaying()) {
    +    return false;
    +  }
    +
    +  this.onBegin();
    +  this.onPlay();
    +
    +  this.startTime = goog.now();
    +  this.setStatePlaying();
    +
    +  if (goog.style.transition.isSupported()) {
    +    goog.style.setStyle(this.element_, this.initialStyle_);
    +    // Allow element to get updated to its initial state before installing
    +    // CSS3 transition.
    +    this.timerId_ = goog.Timer.callOnce(this.play_, undefined, this);
    +    return true;
    +  } else {
    +    this.stop_(false);
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Helper method for play method. This needs to be executed on a timer.
    + * @private
    + */
    +goog.fx.css3.Transition.prototype.play_ = function() {
    +  // This measurement of the DOM element causes the browser to recalculate its
    +  // initial state before the transition starts.
    +  goog.style.getSize(this.element_);
    +  goog.style.transition.set(this.element_, this.transitions_);
    +  goog.style.setStyle(this.element_, this.finalStyle_);
    +  this.timerId_ = goog.Timer.callOnce(
    +      goog.bind(this.stop_, this, false), this.duration_ * 1000);
    +};
    +
    +
    +/** @override */
    +goog.fx.css3.Transition.prototype.stop = function() {
    +  if (!this.isPlaying()) return;
    +
    +  this.stop_(true);
    +};
    +
    +
    +/**
    + * Helper method for stop method.
    + * @param {boolean} stopped If the transition was stopped.
    + * @private
    + */
    +goog.fx.css3.Transition.prototype.stop_ = function(stopped) {
    +  goog.style.transition.removeAll(this.element_);
    +
    +  // Clear the timer.
    +  goog.Timer.clear(this.timerId_);
    +
    +  // Make sure that we have reached the final style.
    +  goog.style.setStyle(this.element_, this.finalStyle_);
    +
    +  this.endTime = goog.now();
    +  this.setStateStopped();
    +
    +  if (stopped) {
    +    this.onStop();
    +  } else {
    +    this.onFinish();
    +  }
    +  this.onEnd();
    +};
    +
    +
    +/** @override */
    +goog.fx.css3.Transition.prototype.disposeInternal = function() {
    +  this.stop();
    +  goog.fx.css3.Transition.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Pausing CSS3 Transitions in not supported.
    + * @override
    + */
    +goog.fx.css3.Transition.prototype.pause = function() {
    +  goog.asserts.assert(false, 'Css3 transitions does not support pause action.');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.html b/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.html
    new file mode 100644
    index 0000000000..936426a467
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.css3.Transition
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.css3.TransitionTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.js b/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.js
    new file mode 100644
    index 0000000000..03ccc16aec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/css3/transition_test.js
    @@ -0,0 +1,219 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.css3.TransitionTest');
    +goog.setTestOnly('goog.fx.css3.TransitionTest');
    +
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.css3.Transition');
    +goog.require('goog.style.transition');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var transition;
    +var element;
    +var mockClock;
    +
    +
    +function createTransition(element, duration) {
    +  return new goog.fx.css3.Transition(
    +      element, duration, {'opacity': 0}, {'opacity': 1},
    +      {property: 'opacity', duration: duration, timing: 'ease-in', delay: 0});
    +}
    +
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +  element = goog.dom.createElement('div');
    +  document.body.appendChild(element);
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(transition);
    +  goog.dispose(mockClock);
    +  goog.dom.removeNode(element);
    +}
    +
    +
    +function testPlayEventFiredOnPlay() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +  var handlerCalled = false;
    +  goog.events.listen(transition, goog.fx.Transition.EventType.PLAY,
    +      function() {
    +        handlerCalled = true;
    +      });
    +
    +  transition.play();
    +  assertTrue(handlerCalled);
    +}
    +
    +
    +function testBeginEventFiredOnPlay() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +  var handlerCalled = false;
    +  goog.events.listen(transition, goog.fx.Transition.EventType.BEGIN,
    +      function() {
    +        handlerCalled = true;
    +      });
    +
    +  transition.play();
    +  assertTrue(handlerCalled);
    +}
    +
    +
    +function testFinishEventsFiredAfterFinish() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +  var finishHandlerCalled = false;
    +  var endHandlerCalled = false;
    +  goog.events.listen(transition, goog.fx.Transition.EventType.FINISH,
    +      function() {
    +        finishHandlerCalled = true;
    +      });
    +  goog.events.listen(transition, goog.fx.Transition.EventType.END,
    +      function() {
    +        endHandlerCalled = true;
    +      });
    +
    +  transition.play();
    +
    +  mockClock.tick(10000);
    +
    +  assertTrue(finishHandlerCalled);
    +  assertTrue(endHandlerCalled);
    +}
    +
    +
    +function testEventsWhenTransitionIsUnsupported() {
    +  if (goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +
    +  var stopHandlerCalled = false;
    +  var finishHandlerCalled = false, endHandlerCalled = false;
    +  var beginHandlerCalled = false, playHandlerCalled = false;
    +  goog.events.listen(transition, goog.fx.Transition.EventType.BEGIN,
    +      function() {
    +        beginHandlerCalled = true;
    +      });
    +  goog.events.listen(transition, goog.fx.Transition.EventType.PLAY,
    +      function() {
    +        playHandlerCalled = true;
    +      });
    +  goog.events.listen(transition, goog.fx.Transition.EventType.FINISH,
    +      function() {
    +        finishHandlerCalled = true;
    +      });
    +  goog.events.listen(transition, goog.fx.Transition.EventType.END,
    +      function() {
    +        endHandlerCalled = true;
    +      });
    +  goog.events.listen(transition, goog.fx.Transition.EventType.STOP,
    +      function() {
    +        stopHandlerCalled = true;
    +      });
    +
    +  assertFalse(transition.play());
    +
    +  assertTrue(beginHandlerCalled);
    +  assertTrue(playHandlerCalled);
    +  assertTrue(endHandlerCalled);
    +  assertTrue(finishHandlerCalled);
    +
    +  transition.stop();
    +
    +  assertFalse(stopHandlerCalled);
    +}
    +
    +
    +function testCallingStopDuringAnimationWorks() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +
    +  var stopHandler = goog.testing.recordFunction();
    +  var endHandler = goog.testing.recordFunction();
    +  var finishHandler = goog.testing.recordFunction();
    +  goog.events.listen(transition, goog.fx.Transition.EventType.STOP,
    +      stopHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.END,
    +      endHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.FINISH,
    +      finishHandler);
    +
    +  transition.play();
    +  mockClock.tick(1);
    +  transition.stop();
    +  assertEquals(1, stopHandler.getCallCount());
    +  assertEquals(1, endHandler.getCallCount());
    +  mockClock.tick(10000);
    +  assertEquals(0, finishHandler.getCallCount());
    +}
    +
    +
    +function testCallingStopImmediatelyWorks() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +
    +  var stopHandler = goog.testing.recordFunction();
    +  var endHandler = goog.testing.recordFunction();
    +  var finishHandler = goog.testing.recordFunction();
    +  goog.events.listen(transition, goog.fx.Transition.EventType.STOP,
    +      stopHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.END,
    +      endHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.FINISH,
    +      finishHandler);
    +
    +  transition.play();
    +  transition.stop();
    +  assertEquals(1, stopHandler.getCallCount());
    +  assertEquals(1, endHandler.getCallCount());
    +  mockClock.tick(10000);
    +  assertEquals(0, finishHandler.getCallCount());
    +}
    +
    +function testCallingStopAfterAnimationDoesNothing() {
    +  if (!goog.style.transition.isSupported()) return;
    +
    +  transition = createTransition(element, 10);
    +
    +  var stopHandler = goog.testing.recordFunction();
    +  var endHandler = goog.testing.recordFunction();
    +  var finishHandler = goog.testing.recordFunction();
    +  goog.events.listen(transition, goog.fx.Transition.EventType.STOP,
    +      stopHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.END,
    +      endHandler);
    +  goog.events.listen(transition, goog.fx.Transition.EventType.FINISH,
    +      finishHandler);
    +
    +  transition.play();
    +  mockClock.tick(10000);
    +  transition.stop();
    +  assertEquals(0, stopHandler.getCallCount());
    +  assertEquals(1, endHandler.getCallCount());
    +  assertEquals(1, finishHandler.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation.js b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation.js
    new file mode 100644
    index 0000000000..9813f7de28
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation.js
    @@ -0,0 +1,130 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An animation class that animates CSS sprites by changing the
    + * CSS background-position.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/cssspriteanimation.html
    + */
    +
    +goog.provide('goog.fx.CssSpriteAnimation');
    +
    +goog.require('goog.fx.Animation');
    +
    +
    +
    +/**
    + * This animation class is used to animate a CSS sprite (moving a background
    + * image).  This moves through a series of images in a single image sprite. By
    + * default, the animation loops when done.  Looping can be disabled by setting
    + * {@code opt_disableLoop} and results in the animation stopping on the last
    + * image in the image sprite.  You should set up the {@code background-image}
    + * and size in a CSS rule for the relevant element.
    + *
    + * @param {Element} element The HTML element to animate the background for.
    + * @param {goog.math.Size} size The size of one image in the image sprite.
    + * @param {goog.math.Box} box The box describing the layout of the sprites to
    + *     use in the large image.  The sprites can be position horizontally or
    + *     vertically and using a box here allows the implementation to know which
    + *     way to go.
    + * @param {number} time The duration in milliseconds for one iteration of the
    + *     animation.  For example, if the sprite contains 4 images and the duration
    + *     is set to 400ms then each sprite will be displayed for 100ms.
    + * @param {function(number) : number=} opt_acc Acceleration function,
    + *    returns 0-1 for inputs 0-1.  This can be used to make certain frames be
    + *    shown for a longer period of time.
    + * @param {boolean=} opt_disableLoop Whether the animation should be halted
    + *    after a single loop of the images in the sprite.
    + *
    + * @constructor
    + * @extends {goog.fx.Animation}
    + * @final
    + */
    +goog.fx.CssSpriteAnimation = function(element, size, box, time, opt_acc,
    +    opt_disableLoop) {
    +  var start = [box.left, box.top];
    +  // We never draw for the end so we do not need to subtract for the size
    +  var end = [box.right, box.bottom];
    +  goog.fx.CssSpriteAnimation.base(
    +      this, 'constructor', start, end, time, opt_acc);
    +
    +  /**
    +   * HTML element that will be used in the animation.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = element;
    +
    +  /**
    +   * The size of an individual sprite in the image sprite.
    +   * @type {goog.math.Size}
    +   * @private
    +   */
    +  this.size_ = size;
    +
    +  /**
    +   * Whether the animation should be halted after a single loop of the images
    +   * in the sprite.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.disableLoop_ = !!opt_disableLoop;
    +};
    +goog.inherits(goog.fx.CssSpriteAnimation, goog.fx.Animation);
    +
    +
    +/** @override */
    +goog.fx.CssSpriteAnimation.prototype.onAnimate = function() {
    +  // Round to nearest sprite.
    +  var x = -Math.floor(this.coords[0] / this.size_.width) * this.size_.width;
    +  var y = -Math.floor(this.coords[1] / this.size_.height) * this.size_.height;
    +  this.element_.style.backgroundPosition = x + 'px ' + y + 'px';
    +
    +  goog.fx.CssSpriteAnimation.base(this, 'onAnimate');
    +};
    +
    +
    +/** @override */
    +goog.fx.CssSpriteAnimation.prototype.onFinish = function() {
    +  if (!this.disableLoop_) {
    +    this.play(true);
    +  }
    +  goog.fx.CssSpriteAnimation.base(this, 'onFinish');
    +};
    +
    +
    +/**
    + * Clears the background position style set directly on the element
    + * by the animation. Allows to apply CSS styling for background position on the
    + * same element when the sprite animation is not runniing.
    + */
    +goog.fx.CssSpriteAnimation.prototype.clearSpritePosition = function() {
    +  var style = this.element_.style;
    +  style.backgroundPosition = '';
    +
    +  if (typeof style.backgroundPositionX != 'undefined') {
    +    // IE needs to clear x and y to actually clear the position
    +    style.backgroundPositionX = '';
    +    style.backgroundPositionY = '';
    +  }
    +};
    +
    +
    +/** @override */
    +goog.fx.CssSpriteAnimation.prototype.disposeInternal = function() {
    +  goog.fx.CssSpriteAnimation.superClass_.disposeInternal.call(this);
    +  this.element_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.html b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.html
    new file mode 100644
    index 0000000000..48306d9656
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.CssSpriteAnimation
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.CssSpriteAnimationTest');
    +  </script>
    +  <style>
    +   #test {
    +  width: 10px;
    +  height: 10px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="test">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.js b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.js
    new file mode 100644
    index 0000000000..b29959eb04
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/cssspriteanimation_test.js
    @@ -0,0 +1,153 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.CssSpriteAnimationTest');
    +goog.setTestOnly('goog.fx.CssSpriteAnimationTest');
    +
    +goog.require('goog.fx.CssSpriteAnimation');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Size');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var el;
    +var size;
    +var box;
    +var time = 1000;
    +var anim, clock;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +  el = document.getElementById('test');
    +  size = new goog.math.Size(10, 10);
    +  box = new goog.math.Box(0, 10, 100, 0);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function tearDown() {
    +  anim.clearSpritePosition();
    +  anim.dispose();
    +}
    +
    +function assertBackgroundPosition(x, y) {
    +  if (typeof el.style.backgroundPositionX != 'undefined') {
    +    assertEquals(x + 'px', el.style.backgroundPositionX);
    +    assertEquals(y + 'px', el.style.backgroundPositionY);
    +  } else {
    +    var bgPos = el.style.backgroundPosition;
    +    var message = 'Expected <' + x + 'px ' + y + 'px>, found <' + bgPos + '>';
    +    if (x == y) {
    +      // when x and y are the same the browser sometimes collapse the prop
    +      assertTrue(message,
    +                 bgPos == x || // in case of 0 without a unit
    +                 bgPos == x + 'px' ||
    +                 bgPos == x + ' ' + y ||
    +                 bgPos == x + 'px ' + y + 'px');
    +    } else {
    +      assertTrue(message,
    +                 bgPos == x + ' ' + y ||
    +                 bgPos == x + 'px ' + y ||
    +                 bgPos == x + ' ' + y + 'px' ||
    +                 bgPos == x + 'px ' + y + 'px');
    +    }
    +  }
    +}
    +
    +function testAnimation() {
    +  anim = new goog.fx.CssSpriteAnimation(el, size, box, time);
    +  anim.play();
    +
    +  assertBackgroundPosition(0, 0);
    +
    +  clock.tick(5);
    +  assertBackgroundPosition(0, 0);
    +
    +  clock.tick(95);
    +  assertBackgroundPosition(0, -10);
    +
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -20);
    +
    +  clock.tick(300);
    +  assertBackgroundPosition(0, -50);
    +
    +  clock.tick(400);
    +  assertBackgroundPosition(0, -90);
    +
    +  // loop around to starting position
    +  clock.tick(100);
    +  assertBackgroundPosition(0, 0);
    +
    +  assertTrue(anim.isPlaying());
    +  assertFalse(anim.isStopped());
    +
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -10);
    +}
    +
    +
    +function testAnimation_disableLoop() {
    +  anim = new goog.fx.CssSpriteAnimation(el, size, box, time, undefined,
    +      true /* opt_disableLoop */);
    +  anim.play();
    +
    +  assertBackgroundPosition(0, 0);
    +
    +  clock.tick(5);
    +  assertBackgroundPosition(0, 0);
    +
    +  clock.tick(95);
    +  assertBackgroundPosition(0, -10);
    +
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -20);
    +
    +  clock.tick(300);
    +  assertBackgroundPosition(0, -50);
    +
    +  clock.tick(400);
    +  assertBackgroundPosition(0, -90);
    +
    +  // loop around to starting position
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -90);
    +
    +  assertTrue(anim.isStopped());
    +  assertFalse(anim.isPlaying());
    +
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -90);
    +}
    +
    +
    +function testClearSpritePosition() {
    +  anim = new goog.fx.CssSpriteAnimation(el, size, box, time);
    +  anim.play();
    +
    +  assertBackgroundPosition(0, 0);
    +
    +  clock.tick(100);
    +  assertBackgroundPosition(0, -10);
    +  anim.clearSpritePosition();
    +
    +  if (typeof el.style.backgroundPositionX != 'undefined') {
    +    assertEquals('', el.style.backgroundPositionX);
    +    assertEquals('', el.style.backgroundPositionY);
    +  }
    +
    +  assertEquals('', el.style.backgroundPosition);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dom.js b/mapbox-gl/closure-library/closure/goog/fx/dom.js
    new file mode 100644
    index 0000000000..8430fba8e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dom.js
    @@ -0,0 +1,686 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Predefined DHTML animations such as slide, resize and fade.
    + *
    + * @see ../demos/effects.html
    + */
    +
    +goog.provide('goog.fx.dom');
    +goog.provide('goog.fx.dom.BgColorTransform');
    +goog.provide('goog.fx.dom.ColorTransform');
    +goog.provide('goog.fx.dom.Fade');
    +goog.provide('goog.fx.dom.FadeIn');
    +goog.provide('goog.fx.dom.FadeInAndShow');
    +goog.provide('goog.fx.dom.FadeOut');
    +goog.provide('goog.fx.dom.FadeOutAndHide');
    +goog.provide('goog.fx.dom.PredefinedEffect');
    +goog.provide('goog.fx.dom.Resize');
    +goog.provide('goog.fx.dom.ResizeHeight');
    +goog.provide('goog.fx.dom.ResizeWidth');
    +goog.provide('goog.fx.dom.Scroll');
    +goog.provide('goog.fx.dom.Slide');
    +goog.provide('goog.fx.dom.SlideFrom');
    +goog.provide('goog.fx.dom.Swipe');
    +
    +goog.require('goog.color');
    +goog.require('goog.events');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +
    +
    +
    +/**
    + * Abstract class that provides reusable functionality for predefined animations
    + * that manipulate a single DOM element
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start Array for start coordinates.
    + * @param {Array<number>} end Array for end coordinates.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.Animation}
    + * @constructor
    + */
    +goog.fx.dom.PredefinedEffect = function(element, start, end, time, opt_acc) {
    +  goog.fx.Animation.call(this, start, end, time, opt_acc);
    +
    +  /**
    +   * DOM Node that will be used in the animation
    +   * @type {Element}
    +   */
    +  this.element = element;
    +
    +  /**
    +   * Whether the element is rendered right-to-left. We cache this here for
    +   * efficiency.
    +   * @private {boolean|undefined}
    +   */
    +  this.rightToLeft_;
    +};
    +goog.inherits(goog.fx.dom.PredefinedEffect, goog.fx.Animation);
    +
    +
    +/**
    + * Called to update the style of the element.
    + * @protected
    + */
    +goog.fx.dom.PredefinedEffect.prototype.updateStyle = goog.nullFunction;
    +
    +
    +/**
    + * Whether the DOM element being manipulated is rendered right-to-left.
    + * @return {boolean} True if the DOM element is rendered right-to-left, false
    + *     otherwise.
    + */
    +goog.fx.dom.PredefinedEffect.prototype.isRightToLeft = function() {
    +  if (!goog.isDef(this.rightToLeft_)) {
    +    this.rightToLeft_ = goog.style.isRightToLeft(this.element);
    +  }
    +  return this.rightToLeft_;
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.PredefinedEffect.prototype.onAnimate = function() {
    +  this.updateStyle();
    +  goog.fx.dom.PredefinedEffect.superClass_.onAnimate.call(this);
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.PredefinedEffect.prototype.onEnd = function() {
    +  this.updateStyle();
    +  goog.fx.dom.PredefinedEffect.superClass_.onEnd.call(this);
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.PredefinedEffect.prototype.onBegin = function() {
    +  this.updateStyle();
    +  goog.fx.dom.PredefinedEffect.superClass_.onBegin.call(this);
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will slide an element from A to B.  (This
    + * in effect automatically sets up the onanimate event for an Animation object)
    + *
    + * Start and End should be 2 dimensional arrays
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 2D array for start coordinates (X, Y).
    + * @param {Array<number>} end 2D array for end coordinates (X, Y).
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.Slide = function(element, start, end, time, opt_acc) {
    +  if (start.length != 2 || end.length != 2) {
    +    throw Error('Start and end points must be 2D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +};
    +goog.inherits(goog.fx.dom.Slide, goog.fx.dom.PredefinedEffect);
    +
    +
    +/** @override */
    +goog.fx.dom.Slide.prototype.updateStyle = function() {
    +  var pos = (this.isRightPositioningForRtlEnabled() && this.isRightToLeft()) ?
    +      'right' : 'left';
    +  this.element.style[pos] = Math.round(this.coords[0]) + 'px';
    +  this.element.style.top = Math.round(this.coords[1]) + 'px';
    +};
    +
    +
    +
    +/**
    + * Slides an element from its current position.
    + *
    + * @param {Element} element DOM node to be used in the animation.
    + * @param {Array<number>} end 2D array for end coordinates (X, Y).
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.Slide}
    + * @constructor
    + */
    +goog.fx.dom.SlideFrom = function(element, end, time, opt_acc) {
    +  var offsetLeft = this.isRightPositioningForRtlEnabled() ?
    +      goog.style.bidi.getOffsetStart(element) : element.offsetLeft;
    +  var start = [offsetLeft, element.offsetTop];
    +  goog.fx.dom.Slide.call(this, element, start, end, time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.SlideFrom, goog.fx.dom.Slide);
    +
    +
    +/** @override */
    +goog.fx.dom.SlideFrom.prototype.onBegin = function() {
    +  var offsetLeft = this.isRightPositioningForRtlEnabled() ?
    +      goog.style.bidi.getOffsetStart(this.element) : this.element.offsetLeft;
    +  this.startPoint = [offsetLeft, this.element.offsetTop];
    +  goog.fx.dom.SlideFrom.superClass_.onBegin.call(this);
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will slide an element into its final size.
    + * Requires that the element is absolutely positioned.
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 2D array for start size (W, H).
    + * @param {Array<number>} end 2D array for end size (W, H).
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.Swipe = function(element, start, end, time, opt_acc) {
    +  if (start.length != 2 || end.length != 2) {
    +    throw Error('Start and end points must be 2D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +
    +  /**
    +   * Maximum width for element.
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxWidth_ = Math.max(this.endPoint[0], this.startPoint[0]);
    +
    +  /**
    +   * Maximum height for element.
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxHeight_ = Math.max(this.endPoint[1], this.startPoint[1]);
    +};
    +goog.inherits(goog.fx.dom.Swipe, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will resize an element by setting its width,
    + * height and clipping.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.Swipe.prototype.updateStyle = function() {
    +  var x = this.coords[0];
    +  var y = this.coords[1];
    +  this.clip_(Math.round(x), Math.round(y), this.maxWidth_, this.maxHeight_);
    +  this.element.style.width = Math.round(x) + 'px';
    +  var marginX = (this.isRightPositioningForRtlEnabled() &&
    +      this.isRightToLeft()) ? 'marginRight' : 'marginLeft';
    +
    +  this.element.style[marginX] = Math.round(x) - this.maxWidth_ + 'px';
    +  this.element.style.marginTop = Math.round(y) - this.maxHeight_ + 'px';
    +};
    +
    +
    +/**
    + * Helper function for setting element clipping.
    + * @param {number} x Current element width.
    + * @param {number} y Current element height.
    + * @param {number} w Maximum element width.
    + * @param {number} h Maximum element height.
    + * @private
    + */
    +goog.fx.dom.Swipe.prototype.clip_ = function(x, y, w, h) {
    +  this.element.style.clip =
    +      'rect(' + (h - y) + 'px ' + w + 'px ' + h + 'px ' + (w - x) + 'px)';
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will scroll an element from A to B.
    + *
    + * Start and End should be 2 dimensional arrays
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 2D array for start scroll left and top.
    + * @param {Array<number>} end 2D array for end scroll left and top.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.Scroll = function(element, start, end, time, opt_acc) {
    +  if (start.length != 2 || end.length != 2) {
    +    throw Error('Start and end points must be 2D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +};
    +goog.inherits(goog.fx.dom.Scroll, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will set the scroll position of an element.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.Scroll.prototype.updateStyle = function() {
    +  if (this.isRightPositioningForRtlEnabled()) {
    +    goog.style.bidi.setScrollOffset(this.element, Math.round(this.coords[0]));
    +  } else {
    +    this.element.scrollLeft = Math.round(this.coords[0]);
    +  }
    +  this.element.scrollTop = Math.round(this.coords[1]);
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will resize an element between two widths
    + * and heights.
    + *
    + * Start and End should be 2 dimensional arrays
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 2D array for start width and height.
    + * @param {Array<number>} end 2D array for end width and height.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.Resize = function(element, start, end, time, opt_acc) {
    +  if (start.length != 2 || end.length != 2) {
    +    throw Error('Start and end points must be 2D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +};
    +goog.inherits(goog.fx.dom.Resize, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will resize an element by setting its width and
    + * height.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.Resize.prototype.updateStyle = function() {
    +  this.element.style.width = Math.round(this.coords[0]) + 'px';
    +  this.element.style.height = Math.round(this.coords[1]) + 'px';
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will resize an element between two widths
    + *
    + * Start and End should be numbers
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} start Start width.
    + * @param {number} end End width.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.ResizeWidth = function(element, start, end, time, opt_acc) {
    +  goog.fx.dom.PredefinedEffect.call(this, element, [start],
    +                                    [end], time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.ResizeWidth, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will resize an element by setting its width.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.ResizeWidth.prototype.updateStyle = function() {
    +  this.element.style.width = Math.round(this.coords[0]) + 'px';
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that will resize an element between two heights
    + *
    + * Start and End should be numbers
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} start Start height.
    + * @param {number} end End height.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.ResizeHeight = function(element, start, end, time, opt_acc) {
    +  goog.fx.dom.PredefinedEffect.call(this, element, [start],
    +                                    [end], time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.ResizeHeight, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will resize an element by setting its height.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.ResizeHeight.prototype.updateStyle = function() {
    +  this.element.style.height = Math.round(this.coords[0]) + 'px';
    +};
    +
    +
    +
    +/**
    + * Creates an animation object that fades the opacity of an element between two
    + * limits.
    + *
    + * Start and End should be floats between 0 and 1
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>|number} start 1D Array or Number with start opacity.
    + * @param {Array<number>|number} end 1D Array or Number for end opacity.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.Fade = function(element, start, end, time, opt_acc) {
    +  if (goog.isNumber(start)) start = [start];
    +  if (goog.isNumber(end)) end = [end];
    +
    +  goog.fx.dom.Fade.base(this, 'constructor',
    +      element, start, end, time, opt_acc);
    +
    +  if (start.length != 1 || end.length != 1) {
    +    throw Error('Start and end points must be 1D');
    +  }
    +
    +  /**
    +   * The last opacity we set, or -1 for not set.
    +   * @private {number}
    +   */
    +  this.lastOpacityUpdate_ = goog.fx.dom.Fade.OPACITY_UNSET_;
    +};
    +goog.inherits(goog.fx.dom.Fade, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * The quantization of opacity values to use.
    + * @private {number}
    + */
    +goog.fx.dom.Fade.TOLERANCE_ = 1.0 / 0x400;  // 10-bit color
    +
    +
    +/**
    + * Value indicating that the opacity must be set on next update.
    + * @private {number}
    + */
    +goog.fx.dom.Fade.OPACITY_UNSET_ = -1;
    +
    +
    +/**
    + * Animation event handler that will set the opacity of an element.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.Fade.prototype.updateStyle = function() {
    +  var opacity = this.coords[0];
    +  var delta = Math.abs(opacity - this.lastOpacityUpdate_);
    +  // In order to keep eager browsers from over-rendering, only update
    +  // on a potentially visible change in opacity.
    +  if (delta >= goog.fx.dom.Fade.TOLERANCE_) {
    +    goog.style.setOpacity(this.element, opacity);
    +    this.lastOpacityUpdate_ = opacity;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.Fade.prototype.onBegin = function() {
    +  this.lastOpacityUpdate_ = goog.fx.dom.Fade.OPACITY_UNSET_;
    +  goog.fx.dom.Fade.base(this, 'onBegin');
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.Fade.prototype.onEnd = function() {
    +  this.lastOpacityUpdate_ = goog.fx.dom.Fade.OPACITY_UNSET_;
    +  goog.fx.dom.Fade.base(this, 'onEnd');
    +};
    +
    +
    +/**
    + * Animation event handler that will show the element.
    + */
    +goog.fx.dom.Fade.prototype.show = function() {
    +  this.element.style.display = '';
    +};
    +
    +
    +/**
    + * Animation event handler that will hide the element
    + */
    +goog.fx.dom.Fade.prototype.hide = function() {
    +  this.element.style.display = 'none';
    +};
    +
    +
    +
    +/**
    + * Fades an element out from full opacity to completely transparent.
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.Fade}
    + * @constructor
    + */
    +goog.fx.dom.FadeOut = function(element, time, opt_acc) {
    +  goog.fx.dom.Fade.call(this, element, 1, 0, time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.FadeOut, goog.fx.dom.Fade);
    +
    +
    +
    +/**
    + * Fades an element in from completely transparent to fully opacity.
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.Fade}
    + * @constructor
    + */
    +goog.fx.dom.FadeIn = function(element, time, opt_acc) {
    +  goog.fx.dom.Fade.call(this, element, 0, 1, time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.FadeIn, goog.fx.dom.Fade);
    +
    +
    +
    +/**
    + * Fades an element out from full opacity to completely transparent and then
    + * sets the display to 'none'
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.Fade}
    + * @constructor
    + */
    +goog.fx.dom.FadeOutAndHide = function(element, time, opt_acc) {
    +  goog.fx.dom.Fade.call(this, element, 1, 0, time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.FadeOutAndHide, goog.fx.dom.Fade);
    +
    +
    +/** @override */
    +goog.fx.dom.FadeOutAndHide.prototype.onBegin = function() {
    +  this.show();
    +  goog.fx.dom.FadeOutAndHide.superClass_.onBegin.call(this);
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.FadeOutAndHide.prototype.onEnd = function() {
    +  this.hide();
    +  goog.fx.dom.FadeOutAndHide.superClass_.onEnd.call(this);
    +};
    +
    +
    +
    +/**
    + * Sets an element's display to be visible and then fades an element in from
    + * completely transparent to fully opaque.
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.Fade}
    + * @constructor
    + */
    +goog.fx.dom.FadeInAndShow = function(element, time, opt_acc) {
    +  goog.fx.dom.Fade.call(this, element, 0, 1, time, opt_acc);
    +};
    +goog.inherits(goog.fx.dom.FadeInAndShow, goog.fx.dom.Fade);
    +
    +
    +/** @override */
    +goog.fx.dom.FadeInAndShow.prototype.onBegin = function() {
    +  this.show();
    +  goog.fx.dom.FadeInAndShow.superClass_.onBegin.call(this);
    +};
    +
    +
    +
    +/**
    + * Provides a transformation of an elements background-color.
    + *
    + * Start and End should be 3D arrays representing R,G,B
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 3D Array for RGB of start color.
    + * @param {Array<number>} end 3D Array for RGB of end color.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @extends {goog.fx.dom.PredefinedEffect}
    + * @constructor
    + */
    +goog.fx.dom.BgColorTransform = function(element, start, end, time, opt_acc) {
    +  if (start.length != 3 || end.length != 3) {
    +    throw Error('Start and end points must be 3D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +};
    +goog.inherits(goog.fx.dom.BgColorTransform, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will set the background-color of an element
    + */
    +goog.fx.dom.BgColorTransform.prototype.setColor = function() {
    +  var coordsAsInts = [];
    +  for (var i = 0; i < this.coords.length; i++) {
    +    coordsAsInts[i] = Math.round(this.coords[i]);
    +  }
    +  var color = 'rgb(' + coordsAsInts.join(',') + ')';
    +  this.element.style.backgroundColor = color;
    +};
    +
    +
    +/** @override */
    +goog.fx.dom.BgColorTransform.prototype.updateStyle = function() {
    +  this.setColor();
    +};
    +
    +
    +/**
    + * Fade elements background color from start color to the element's current
    + * background color.
    + *
    + * Start should be a 3D array representing R,G,B
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 3D Array for RGB of start color.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {goog.events.EventHandler=} opt_eventHandler Optional event handler
    + *     to use when listening for events.
    + */
    +goog.fx.dom.bgColorFadeIn = function(element, start, time, opt_eventHandler) {
    +  var initialBgColor = element.style.backgroundColor || '';
    +  var computedBgColor = goog.style.getBackgroundColor(element);
    +  var end;
    +
    +  if (computedBgColor && computedBgColor != 'transparent' &&
    +      computedBgColor != 'rgba(0, 0, 0, 0)') {
    +    end = goog.color.hexToRgb(goog.color.parse(computedBgColor).hex);
    +  } else {
    +    end = [255, 255, 255];
    +  }
    +
    +  var anim = new goog.fx.dom.BgColorTransform(element, start, end, time);
    +
    +  function setBgColor() {
    +    element.style.backgroundColor = initialBgColor;
    +  }
    +
    +  if (opt_eventHandler) {
    +    opt_eventHandler.listen(
    +        anim, goog.fx.Transition.EventType.END, setBgColor);
    +  } else {
    +    goog.events.listen(
    +        anim, goog.fx.Transition.EventType.END, setBgColor);
    +  }
    +
    +  anim.play();
    +};
    +
    +
    +
    +/**
    + * Provides a transformation of an elements color.
    + *
    + * @param {Element} element Dom Node to be used in the animation.
    + * @param {Array<number>} start 3D Array representing R,G,B.
    + * @param {Array<number>} end 3D Array representing R,G,B.
    + * @param {number} time Length of animation in milliseconds.
    + * @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
    + * @constructor
    + * @extends {goog.fx.dom.PredefinedEffect}
    + */
    +goog.fx.dom.ColorTransform = function(element, start, end, time, opt_acc) {
    +  if (start.length != 3 || end.length != 3) {
    +    throw Error('Start and end points must be 3D');
    +  }
    +  goog.fx.dom.PredefinedEffect.apply(this, arguments);
    +};
    +goog.inherits(goog.fx.dom.ColorTransform, goog.fx.dom.PredefinedEffect);
    +
    +
    +/**
    + * Animation event handler that will set the color of an element.
    + * @protected
    + * @override
    + */
    +goog.fx.dom.ColorTransform.prototype.updateStyle = function() {
    +  var coordsAsInts = [];
    +  for (var i = 0; i < this.coords.length; i++) {
    +    coordsAsInts[i] = Math.round(this.coords[i]);
    +  }
    +  var color = 'rgb(' + coordsAsInts.join(',') + ')';
    +  this.element.style.color = color;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragdrop.js b/mapbox-gl/closure-library/closure/goog/fx/dragdrop.js
    new file mode 100644
    index 0000000000..7fe95455f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragdrop.js
    @@ -0,0 +1,50 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Single Element Drag and Drop.
    + *
    + * Drag and drop implementation for sources/targets consisting of a single
    + * element.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/dragdrop.html
    + */
    +
    +goog.provide('goog.fx.DragDrop');
    +
    +goog.require('goog.fx.AbstractDragDrop');
    +goog.require('goog.fx.DragDropItem');
    +
    +
    +
    +/**
    + * Drag/drop implementation for creating drag sources/drop targets consisting of
    + * a single HTML Element.
    + *
    + * @param {Element|string} element Dom Node, or string representation of node
    + *     id, to be used as drag source/drop target.
    + * @param {Object=} opt_data Data associated with the source/target.
    + * @throws Error If no element argument is provided or if the type is invalid
    + * @extends {goog.fx.AbstractDragDrop}
    + * @constructor
    + */
    +goog.fx.DragDrop = function(element, opt_data) {
    +  goog.fx.AbstractDragDrop.call(this);
    +
    +  var item = new goog.fx.DragDropItem(element, opt_data);
    +  item.setParent(this);
    +  this.items_.push(item);
    +};
    +goog.inherits(goog.fx.DragDrop, goog.fx.AbstractDragDrop);
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup.js b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup.js
    new file mode 100644
    index 0000000000..01aab56a61
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup.js
    @@ -0,0 +1,109 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Multiple Element Drag and Drop.
    + *
    + * Drag and drop implementation for sources/targets consisting of multiple
    + * elements.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/dragdrop.html
    + */
    +
    +goog.provide('goog.fx.DragDropGroup');
    +
    +goog.require('goog.dom');
    +goog.require('goog.fx.AbstractDragDrop');
    +goog.require('goog.fx.DragDropItem');
    +
    +
    +
    +/**
    + * Drag/drop implementation for creating drag sources/drop targets consisting of
    + * multiple HTML Elements (items). All items share the same drop target(s) but
    + * can be dragged individually.
    + *
    + * @extends {goog.fx.AbstractDragDrop}
    + * @constructor
    + */
    +goog.fx.DragDropGroup = function() {
    +  goog.fx.AbstractDragDrop.call(this);
    +};
    +goog.inherits(goog.fx.DragDropGroup, goog.fx.AbstractDragDrop);
    +
    +
    +/**
    + * Add item to drag object.
    + *
    + * @param {Element|string} element Dom Node, or string representation of node
    + *     id, to be used as drag source/drop target.
    + * @param {Object=} opt_data Data associated with the source/target.
    + * @throws Error If no element argument is provided or if the type is
    + *     invalid
    + * @override
    + */
    +goog.fx.DragDropGroup.prototype.addItem = function(element, opt_data) {
    +  var item = new goog.fx.DragDropItem(element, opt_data);
    +  this.addDragDropItem(item);
    +};
    +
    +
    +/**
    + * Add DragDropItem to drag object.
    + *
    + * @param {goog.fx.DragDropItem} item DragDropItem being added to the
    + *     drag object.
    + * @throws Error If no element argument is provided or if the type is
    + *     invalid
    + */
    +goog.fx.DragDropGroup.prototype.addDragDropItem = function(item) {
    +  item.setParent(this);
    +  this.items_.push(item);
    +  if (this.isInitialized()) {
    +    this.initItem(item);
    +  }
    +};
    +
    +
    +/**
    + * Remove item from drag object.
    + *
    + * @param {Element|string} element Dom Node, or string representation of node
    + *     id, that was previously added with addItem().
    + */
    +goog.fx.DragDropGroup.prototype.removeItem = function(element) {
    +  element = goog.dom.getElement(element);
    +  for (var item, i = 0; item = this.items_[i]; i++) {
    +    if (item.element == element) {
    +      this.items_.splice(i, 1);
    +      this.disposeItem(item);
    +      break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Marks the supplied list of items as selected. A drag operation for any of the
    + * selected items will affect all of them.
    + *
    + * @param {Array<goog.fx.DragDropItem>} list List of items to select or null to
    + *     clear selection.
    + *
    + * TODO(eae): Not yet implemented.
    + */
    +goog.fx.DragDropGroup.prototype.setSelection = function(list) {
    +
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.html b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.html
    new file mode 100644
    index 0000000000..38a8fa5520
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.html
    @@ -0,0 +1,32 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.DragDropGroup
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.DragDropGroupTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="s1" class="s">
    +  </div>
    +  <div id="s2" class="s">
    +  </div>
    +  <div id="t1" class="t">
    +  </div>
    +  <div id="t2" class="t">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.js b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.js
    new file mode 100644
    index 0000000000..f53fc177f7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragdropgroup_test.js
    @@ -0,0 +1,229 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.DragDropGroupTest');
    +goog.setTestOnly('goog.fx.DragDropGroupTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.fx.DragDropGroup');
    +goog.require('goog.testing.jsunit');
    +
    +var s1;
    +var s2;
    +var t1;
    +var t2;
    +
    +var source = null;
    +var target = null;
    +
    +function setUpPage() {
    +  s1 = document.getElementById('s1');
    +  s2 = document.getElementById('s2');
    +  t1 = document.getElementById('t1');
    +  t2 = document.getElementById('t2');
    +}
    +
    +
    +function setUp() {
    +  source = new goog.fx.DragDropGroup();
    +  source.setSourceClass('ss');
    +  source.setTargetClass('st');
    +
    +  target = new goog.fx.DragDropGroup();
    +  target.setSourceClass('ts');
    +  target.setTargetClass('tt');
    +
    +  source.addTarget(target);
    +}
    +
    +
    +function tearDown() {
    +  source.removeItems();
    +  target.removeItems();
    +}
    +
    +
    +function addElementsToGroups() {
    +  source.addItem(s1);
    +  source.addItem(s2);
    +  target.addItem(t1);
    +  target.addItem(t2);
    +}
    +
    +
    +function testAddItemsBeforeInit() {
    +  addElementsToGroups();
    +  source.init();
    +  target.init();
    +
    +  assertEquals(2, source.items_.length);
    +  assertEquals(2, target.items_.length);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertEquals('t tt', t1.className);
    +  assertEquals('t tt', t2.className);
    +
    +  assertTrue(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +}
    +
    +function testAddItemsAfterInit() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, source.items_.length);
    +  assertEquals(2, target.items_.length);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertEquals('t tt', t1.className);
    +  assertEquals('t tt', t2.className);
    +
    +  assertTrue(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +}
    +
    +
    +function testRemoveItems() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, source.items_.length);
    +  assertEquals(s1, source.items_[0].element);
    +  assertEquals(s2, source.items_[1].element);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertTrue(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +
    +  source.removeItems();
    +
    +  assertEquals(0, source.items_.length);
    +
    +  assertEquals('s', s1.className);
    +  assertEquals('s', s2.className);
    +  assertFalse(goog.events.hasListener(s1));
    +  assertFalse(goog.events.hasListener(s2));
    +}
    +
    +function testRemoveSourceItem1() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, source.items_.length);
    +  assertEquals(s1, source.items_[0].element);
    +  assertEquals(s2, source.items_[1].element);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertTrue(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +
    +  source.removeItem(s1);
    +
    +  assertEquals(1, source.items_.length);
    +  assertEquals(s2, source.items_[0].element);
    +
    +  assertEquals('s', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertFalse(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +}
    +
    +
    +function testRemoveSourceItem2() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, source.items_.length);
    +  assertEquals(s1, source.items_[0].element);
    +  assertEquals(s2, source.items_[1].element);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s ss', s2.className);
    +  assertTrue(goog.events.hasListener(s1));
    +  assertTrue(goog.events.hasListener(s2));
    +
    +  source.removeItem(s2);
    +
    +  assertEquals(1, source.items_.length);
    +  assertEquals(s1, source.items_[0].element);
    +
    +  assertEquals('s ss', s1.className);
    +  assertEquals('s', s2.className);
    +  assertTrue(goog.events.hasListener(s1));
    +  assertFalse(goog.events.hasListener(s2));
    +}
    +
    +
    +function testRemoveTargetItem1() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, target.items_.length);
    +  assertEquals(t1, target.items_[0].element);
    +  assertEquals(t2, target.items_[1].element);
    +
    +  assertEquals('t tt', t1.className);
    +  assertEquals('t tt', t2.className);
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +
    +  target.removeItem(t1);
    +
    +  assertEquals(1, target.items_.length);
    +  assertEquals(t2, target.items_[0].element);
    +
    +  assertEquals('t', t1.className);
    +  assertEquals('t tt', t2.className);
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +}
    +
    +
    +function testRemoveTargetItem2() {
    +  source.init();
    +  target.init();
    +  addElementsToGroups();
    +
    +  assertEquals(2, target.items_.length);
    +  assertEquals(t1, target.items_[0].element);
    +  assertEquals(t2, target.items_[1].element);
    +
    +  assertEquals('t tt', t1.className);
    +  assertEquals('t tt', t2.className);
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +
    +  target.removeItem(t2);
    +
    +  assertEquals(1, target.items_.length);
    +  assertEquals(t1, target.items_[0].element);
    +
    +  assertEquals('t tt', t1.className);
    +  assertEquals('t', t2.className);
    +  assertFalse(goog.events.hasListener(t1));
    +  assertFalse(goog.events.hasListener(t2));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragger.js b/mapbox-gl/closure-library/closure/goog/fx/dragger.js
    new file mode 100644
    index 0000000000..5e2cb1060a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragger.js
    @@ -0,0 +1,847 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Drag Utilities.
    + *
    + * Provides extensible functionality for drag & drop behaviour.
    + *
    + * @see ../demos/drag.html
    + * @see ../demos/dragger.html
    + */
    +
    +
    +goog.provide('goog.fx.DragEvent');
    +goog.provide('goog.fx.Dragger');
    +goog.provide('goog.fx.Dragger.EventType');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Rect');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A class that allows mouse or touch-based dragging (moving) of an element
    + *
    + * @param {Element} target The element that will be dragged.
    + * @param {Element=} opt_handle An optional handle to control the drag, if null
    + *     the target is used.
    + * @param {goog.math.Rect=} opt_limits Object containing left, top, width,
    + *     and height.
    + *
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.fx.Dragger = function(target, opt_handle, opt_limits) {
    +  goog.events.EventTarget.call(this);
    +  this.target = target;
    +  this.handle = opt_handle || target;
    +  this.limits = opt_limits || new goog.math.Rect(NaN, NaN, NaN, NaN);
    +
    +  this.document_ = goog.dom.getOwnerDocument(target);
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  // Add listener. Do not use the event handler here since the event handler is
    +  // used for listeners added and removed during the drag operation.
    +  goog.events.listen(this.handle,
    +      [goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN],
    +      this.startDrag, false, this);
    +};
    +goog.inherits(goog.fx.Dragger, goog.events.EventTarget);
    +// Dragger is meant to be extended, but defines most properties on its
    +// prototype, thus making it unsuitable for sealing.
    +goog.tagUnsealableClass(goog.fx.Dragger);
    +
    +
    +/**
    + * Whether setCapture is supported by the browser.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.Dragger.HAS_SET_CAPTURE_ =
    +    // IE and Gecko after 1.9.3 has setCapture
    +    // WebKit does not yet: https://bugs.webkit.org/show_bug.cgi?id=27330
    +    goog.userAgent.IE ||
    +    goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.3');
    +
    +
    +/**
    + * Creates copy of node being dragged.  This is a utility function to be used
    + * wherever it is inappropriate for the original source to follow the mouse
    + * cursor itself.
    + *
    + * @param {Element} sourceEl Element to copy.
    + * @return {!Element} The clone of {@code sourceEl}.
    + */
    +goog.fx.Dragger.cloneNode = function(sourceEl) {
    +  var clonedEl = /** @type {Element} */ (sourceEl.cloneNode(true)),
    +      origTexts = sourceEl.getElementsByTagName('textarea'),
    +      dragTexts = clonedEl.getElementsByTagName('textarea');
    +  // Cloning does not copy the current value of textarea elements, so correct
    +  // this manually.
    +  for (var i = 0; i < origTexts.length; i++) {
    +    dragTexts[i].value = origTexts[i].value;
    +  }
    +  switch (sourceEl.tagName.toLowerCase()) {
    +    case 'tr':
    +      return goog.dom.createDom(
    +          'table', null, goog.dom.createDom('tbody', null, clonedEl));
    +    case 'td':
    +    case 'th':
    +      return goog.dom.createDom(
    +          'table', null, goog.dom.createDom('tbody', null, goog.dom.createDom(
    +          'tr', null, clonedEl)));
    +    case 'textarea':
    +      clonedEl.value = sourceEl.value;
    +    default:
    +      return clonedEl;
    +  }
    +};
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +goog.fx.Dragger.EventType = {
    +  // The drag action was canceled before the START event. Possible reasons:
    +  // disabled dragger, dragging with the right mouse button or releasing the
    +  // button before reaching the hysteresis distance.
    +  EARLY_CANCEL: 'earlycancel',
    +  START: 'start',
    +  BEFOREDRAG: 'beforedrag',
    +  DRAG: 'drag',
    +  END: 'end'
    +};
    +
    +
    +/**
    + * Reference to drag target element.
    + * @type {Element}
    + */
    +goog.fx.Dragger.prototype.target;
    +
    +
    +/**
    + * Reference to the handler that initiates the drag.
    + * @type {Element}
    + */
    +goog.fx.Dragger.prototype.handle;
    +
    +
    +/**
    + * Object representing the limits of the drag region.
    + * @type {goog.math.Rect}
    + */
    +goog.fx.Dragger.prototype.limits;
    +
    +
    +/**
    + * Whether the element is rendered right-to-left. We initialize this lazily.
    + * @type {boolean|undefined}}
    + * @private
    + */
    +goog.fx.Dragger.prototype.rightToLeft_;
    +
    +
    +/**
    + * Current x position of mouse or touch relative to viewport.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.clientX = 0;
    +
    +
    +/**
    + * Current y position of mouse or touch relative to viewport.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.clientY = 0;
    +
    +
    +/**
    + * Current x position of mouse or touch relative to screen. Deprecated because
    + * it doesn't take into affect zoom level or pixel density.
    + * @type {number}
    + * @deprecated Consider switching to clientX instead.
    + */
    +goog.fx.Dragger.prototype.screenX = 0;
    +
    +
    +/**
    + * Current y position of mouse or touch relative to screen. Deprecated because
    + * it doesn't take into affect zoom level or pixel density.
    + * @type {number}
    + * @deprecated Consider switching to clientY instead.
    + */
    +goog.fx.Dragger.prototype.screenY = 0;
    +
    +
    +/**
    + * The x position where the first mousedown or touchstart occurred.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.startX = 0;
    +
    +
    +/**
    + * The y position where the first mousedown or touchstart occurred.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.startY = 0;
    +
    +
    +/**
    + * Current x position of drag relative to target's parent.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.deltaX = 0;
    +
    +
    +/**
    + * Current y position of drag relative to target's parent.
    + * @type {number}
    + */
    +goog.fx.Dragger.prototype.deltaY = 0;
    +
    +
    +/**
    + * The current page scroll value.
    + * @type {goog.math.Coordinate}
    + */
    +goog.fx.Dragger.prototype.pageScroll;
    +
    +
    +/**
    + * Whether dragging is currently enabled.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.Dragger.prototype.enabled_ = true;
    +
    +
    +/**
    + * Whether object is currently being dragged.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.Dragger.prototype.dragging_ = false;
    +
    +
    +/**
    + * The amount of distance, in pixels, after which a mousedown or touchstart is
    + * considered a drag.
    + * @type {number}
    + * @private
    + */
    +goog.fx.Dragger.prototype.hysteresisDistanceSquared_ = 0;
    +
    +
    +/**
    + * Timestamp of when the mousedown or touchstart occurred.
    + * @type {number}
    + * @private
    + */
    +goog.fx.Dragger.prototype.mouseDownTime_ = 0;
    +
    +
    +/**
    + * Reference to a document object to use for the events.
    + * @type {Document}
    + * @private
    + */
    +goog.fx.Dragger.prototype.document_;
    +
    +
    +/**
    + * The SCROLL event target used to make drag element follow scrolling.
    + * @type {EventTarget}
    + * @private
    + */
    +goog.fx.Dragger.prototype.scrollTarget_;
    +
    +
    +/**
    + * Whether IE drag events cancelling is on.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.Dragger.prototype.ieDragStartCancellingOn_ = false;
    +
    +
    +/**
    + * Whether the dragger implements the changes described in http://b/6324964,
    + * making it truly RTL.  This is a temporary flag to allow clients to transition
    + * to the new behavior at their convenience.  At some point it will be the
    + * default.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.Dragger.prototype.useRightPositioningForRtl_ = false;
    +
    +
    +/**
    + * Turns on/off true RTL behavior.  This should be called immediately after
    + * construction.  This is a temporary flag to allow clients to transition
    + * to the new component at their convenience.  At some point true will be the
    + * default.
    + * @param {boolean} useRightPositioningForRtl True if "right" should be used for
    + *     positioning, false if "left" should be used for positioning.
    + */
    +goog.fx.Dragger.prototype.enableRightPositioningForRtl =
    +    function(useRightPositioningForRtl) {
    +  this.useRightPositioningForRtl_ = useRightPositioningForRtl;
    +};
    +
    +
    +/**
    + * Returns the event handler, intended for subclass use.
    + * @return {!goog.events.EventHandler<T>} The event handler.
    + * @this T
    + * @template T
    + */
    +goog.fx.Dragger.prototype.getHandler = function() {
    +  // TODO(user): templated "this" values currently result in "this" being
    +  // "unknown" in the body of the function.
    +  var self = /** @type {goog.fx.Dragger} */ (this);
    +  return self.eventHandler_;
    +};
    +
    +
    +/**
    + * Sets (or reset) the Drag limits after a Dragger is created.
    + * @param {goog.math.Rect?} limits Object containing left, top, width,
    + *     height for new Dragger limits. If target is right-to-left and
    + *     enableRightPositioningForRtl(true) is called, then rect is interpreted as
    + *     right, top, width, and height.
    + */
    +goog.fx.Dragger.prototype.setLimits = function(limits) {
    +  this.limits = limits || new goog.math.Rect(NaN, NaN, NaN, NaN);
    +};
    +
    +
    +/**
    + * Sets the distance the user has to drag the element before a drag operation is
    + * started.
    + * @param {number} distance The number of pixels after which a mousedown and
    + *     move is considered a drag.
    + */
    +goog.fx.Dragger.prototype.setHysteresis = function(distance) {
    +  this.hysteresisDistanceSquared_ = Math.pow(distance, 2);
    +};
    +
    +
    +/**
    + * Gets the distance the user has to drag the element before a drag operation is
    + * started.
    + * @return {number} distance The number of pixels after which a mousedown and
    + *     move is considered a drag.
    + */
    +goog.fx.Dragger.prototype.getHysteresis = function() {
    +  return Math.sqrt(this.hysteresisDistanceSquared_);
    +};
    +
    +
    +/**
    + * Sets the SCROLL event target to make drag element follow scrolling.
    + *
    + * @param {EventTarget} scrollTarget The event target that dispatches SCROLL
    + *     events.
    + */
    +goog.fx.Dragger.prototype.setScrollTarget = function(scrollTarget) {
    +  this.scrollTarget_ = scrollTarget;
    +};
    +
    +
    +/**
    + * Enables cancelling of built-in IE drag events.
    + * @param {boolean} cancelIeDragStart Whether to enable cancelling of IE
    + *     dragstart event.
    + */
    +goog.fx.Dragger.prototype.setCancelIeDragStart = function(cancelIeDragStart) {
    +  this.ieDragStartCancellingOn_ = cancelIeDragStart;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the dragger is enabled.
    + */
    +goog.fx.Dragger.prototype.getEnabled = function() {
    +  return this.enabled_;
    +};
    +
    +
    +/**
    + * Set whether dragger is enabled
    + * @param {boolean} enabled Whether dragger is enabled.
    + */
    +goog.fx.Dragger.prototype.setEnabled = function(enabled) {
    +  this.enabled_ = enabled;
    +};
    +
    +
    +/** @override */
    +goog.fx.Dragger.prototype.disposeInternal = function() {
    +  goog.fx.Dragger.superClass_.disposeInternal.call(this);
    +  goog.events.unlisten(this.handle,
    +      [goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN],
    +      this.startDrag, false, this);
    +  this.cleanUpAfterDragging_();
    +
    +  this.target = null;
    +  this.handle = null;
    +};
    +
    +
    +/**
    + * Whether the DOM element being manipulated is rendered right-to-left.
    + * @return {boolean} True if the DOM element is rendered right-to-left, false
    + *     otherwise.
    + * @private
    + */
    +goog.fx.Dragger.prototype.isRightToLeft_ = function() {
    +  if (!goog.isDef(this.rightToLeft_)) {
    +    this.rightToLeft_ = goog.style.isRightToLeft(this.target);
    +  }
    +  return this.rightToLeft_;
    +};
    +
    +
    +/**
    + * Event handler that is used to start the drag
    + * @param {goog.events.BrowserEvent} e Event object.
    + */
    +goog.fx.Dragger.prototype.startDrag = function(e) {
    +  var isMouseDown = e.type == goog.events.EventType.MOUSEDOWN;
    +
    +  // Dragger.startDrag() can be called by AbstractDragDrop with a mousemove
    +  // event and IE does not report pressed mouse buttons on mousemove. Also,
    +  // it does not make sense to check for the button if the user is already
    +  // dragging.
    +
    +  if (this.enabled_ && !this.dragging_ &&
    +      (!isMouseDown || e.isMouseActionButton())) {
    +    this.maybeReinitTouchEvent_(e);
    +    if (this.hysteresisDistanceSquared_ == 0) {
    +      if (this.fireDragStart_(e)) {
    +        this.dragging_ = true;
    +        e.preventDefault();
    +      } else {
    +        // If the start drag is cancelled, don't setup for a drag.
    +        return;
    +      }
    +    } else {
    +      // Need to preventDefault for hysteresis to prevent page getting selected.
    +      e.preventDefault();
    +    }
    +    this.setupDragHandlers();
    +
    +    this.clientX = this.startX = e.clientX;
    +    this.clientY = this.startY = e.clientY;
    +    this.screenX = e.screenX;
    +    this.screenY = e.screenY;
    +    this.computeInitialPosition();
    +    this.pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll();
    +
    +    this.mouseDownTime_ = goog.now();
    +  } else {
    +    this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL);
    +  }
    +};
    +
    +
    +/**
    + * Sets up event handlers when dragging starts.
    + * @protected
    + */
    +goog.fx.Dragger.prototype.setupDragHandlers = function() {
    +  var doc = this.document_;
    +  var docEl = doc.documentElement;
    +  // Use bubbling when we have setCapture since we got reports that IE has
    +  // problems with the capturing events in combination with setCapture.
    +  var useCapture = !goog.fx.Dragger.HAS_SET_CAPTURE_;
    +
    +  this.eventHandler_.listen(doc,
    +      [goog.events.EventType.TOUCHMOVE, goog.events.EventType.MOUSEMOVE],
    +      this.handleMove_, useCapture);
    +  this.eventHandler_.listen(doc,
    +      [goog.events.EventType.TOUCHEND, goog.events.EventType.MOUSEUP],
    +      this.endDrag, useCapture);
    +
    +  if (goog.fx.Dragger.HAS_SET_CAPTURE_) {
    +    docEl.setCapture(false);
    +    this.eventHandler_.listen(docEl,
    +                              goog.events.EventType.LOSECAPTURE,
    +                              this.endDrag);
    +  } else {
    +    // Make sure we stop the dragging if the window loses focus.
    +    // Don't use capture in this listener because we only want to end the drag
    +    // if the actual window loses focus. Since blur events do not bubble we use
    +    // a bubbling listener on the window.
    +    this.eventHandler_.listen(goog.dom.getWindow(doc),
    +                              goog.events.EventType.BLUR,
    +                              this.endDrag);
    +  }
    +
    +  if (goog.userAgent.IE && this.ieDragStartCancellingOn_) {
    +    // Cancel IE's 'ondragstart' event.
    +    this.eventHandler_.listen(doc, goog.events.EventType.DRAGSTART,
    +                              goog.events.Event.preventDefault);
    +  }
    +
    +  if (this.scrollTarget_) {
    +    this.eventHandler_.listen(this.scrollTarget_, goog.events.EventType.SCROLL,
    +                              this.onScroll_, useCapture);
    +  }
    +};
    +
    +
    +/**
    + * Fires a goog.fx.Dragger.EventType.START event.
    + * @param {goog.events.BrowserEvent} e Browser event that triggered the drag.
    + * @return {boolean} False iff preventDefault was called on the DragEvent.
    + * @private
    + */
    +goog.fx.Dragger.prototype.fireDragStart_ = function(e) {
    +  return this.dispatchEvent(new goog.fx.DragEvent(
    +      goog.fx.Dragger.EventType.START, this, e.clientX, e.clientY, e));
    +};
    +
    +
    +/**
    + * Unregisters the event handlers that are only active during dragging, and
    + * releases mouse capture.
    + * @private
    + */
    +goog.fx.Dragger.prototype.cleanUpAfterDragging_ = function() {
    +  this.eventHandler_.removeAll();
    +  if (goog.fx.Dragger.HAS_SET_CAPTURE_) {
    +    this.document_.releaseCapture();
    +  }
    +};
    +
    +
    +/**
    + * Event handler that is used to end the drag.
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @param {boolean=} opt_dragCanceled Whether the drag has been canceled.
    + */
    +goog.fx.Dragger.prototype.endDrag = function(e, opt_dragCanceled) {
    +  this.cleanUpAfterDragging_();
    +
    +  if (this.dragging_) {
    +    this.maybeReinitTouchEvent_(e);
    +    this.dragging_ = false;
    +
    +    var x = this.limitX(this.deltaX);
    +    var y = this.limitY(this.deltaY);
    +    var dragCanceled = opt_dragCanceled ||
    +        e.type == goog.events.EventType.TOUCHCANCEL;
    +    this.dispatchEvent(new goog.fx.DragEvent(
    +        goog.fx.Dragger.EventType.END, this, e.clientX, e.clientY, e, x, y,
    +        dragCanceled));
    +  } else {
    +    this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL);
    +  }
    +};
    +
    +
    +/**
    + * Event handler that is used to end the drag by cancelling it.
    + * @param {goog.events.BrowserEvent} e Event object.
    + */
    +goog.fx.Dragger.prototype.endDragCancel = function(e) {
    +  this.endDrag(e, true);
    +};
    +
    +
    +/**
    + * Re-initializes the event with the first target touch event or, in the case
    + * of a stop event, the last changed touch.
    + * @param {goog.events.BrowserEvent} e A TOUCH... event.
    + * @private
    + */
    +goog.fx.Dragger.prototype.maybeReinitTouchEvent_ = function(e) {
    +  var type = e.type;
    +
    +  if (type == goog.events.EventType.TOUCHSTART ||
    +      type == goog.events.EventType.TOUCHMOVE) {
    +    e.init(e.getBrowserEvent().targetTouches[0], e.currentTarget);
    +  } else if (type == goog.events.EventType.TOUCHEND ||
    +             type == goog.events.EventType.TOUCHCANCEL) {
    +    e.init(e.getBrowserEvent().changedTouches[0], e.currentTarget);
    +  }
    +};
    +
    +
    +/**
    + * Event handler that is used on mouse / touch move to update the drag
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @private
    + */
    +goog.fx.Dragger.prototype.handleMove_ = function(e) {
    +  if (this.enabled_) {
    +    this.maybeReinitTouchEvent_(e);
    +    // dx in right-to-left cases is relative to the right.
    +    var sign = this.useRightPositioningForRtl_ &&
    +        this.isRightToLeft_() ? -1 : 1;
    +    var dx = sign * (e.clientX - this.clientX);
    +    var dy = e.clientY - this.clientY;
    +    this.clientX = e.clientX;
    +    this.clientY = e.clientY;
    +    this.screenX = e.screenX;
    +    this.screenY = e.screenY;
    +
    +    if (!this.dragging_) {
    +      var diffX = this.startX - this.clientX;
    +      var diffY = this.startY - this.clientY;
    +      var distance = diffX * diffX + diffY * diffY;
    +      if (distance > this.hysteresisDistanceSquared_) {
    +        if (this.fireDragStart_(e)) {
    +          this.dragging_ = true;
    +        } else {
    +          // DragListGroup disposes of the dragger if BEFOREDRAGSTART is
    +          // canceled.
    +          if (!this.isDisposed()) {
    +            this.endDrag(e);
    +          }
    +          return;
    +        }
    +      }
    +    }
    +
    +    var pos = this.calculatePosition_(dx, dy);
    +    var x = pos.x;
    +    var y = pos.y;
    +
    +    if (this.dragging_) {
    +
    +      var rv = this.dispatchEvent(new goog.fx.DragEvent(
    +          goog.fx.Dragger.EventType.BEFOREDRAG, this, e.clientX, e.clientY,
    +          e, x, y));
    +
    +      // Only do the defaultAction and dispatch drag event if predrag didn't
    +      // prevent default
    +      if (rv) {
    +        this.doDrag(e, x, y, false);
    +        e.preventDefault();
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calculates the drag position.
    + *
    + * @param {number} dx The horizontal movement delta.
    + * @param {number} dy The vertical movement delta.
    + * @return {!goog.math.Coordinate} The newly calculated drag element position.
    + * @private
    + */
    +goog.fx.Dragger.prototype.calculatePosition_ = function(dx, dy) {
    +  // Update the position for any change in body scrolling
    +  var pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll();
    +  dx += pageScroll.x - this.pageScroll.x;
    +  dy += pageScroll.y - this.pageScroll.y;
    +  this.pageScroll = pageScroll;
    +
    +  this.deltaX += dx;
    +  this.deltaY += dy;
    +
    +  var x = this.limitX(this.deltaX);
    +  var y = this.limitY(this.deltaY);
    +  return new goog.math.Coordinate(x, y);
    +};
    +
    +
    +/**
    + * Event handler for scroll target scrolling.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.fx.Dragger.prototype.onScroll_ = function(e) {
    +  var pos = this.calculatePosition_(0, 0);
    +  e.clientX = this.clientX;
    +  e.clientY = this.clientY;
    +  this.doDrag(e, pos.x, pos.y, true);
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} e The closure object
    + *     representing the browser event that caused a drag event.
    + * @param {number} x The new horizontal position for the drag element.
    + * @param {number} y The new vertical position for the drag element.
    + * @param {boolean} dragFromScroll Whether dragging was caused by scrolling
    + *     the associated scroll target.
    + * @protected
    + */
    +goog.fx.Dragger.prototype.doDrag = function(e, x, y, dragFromScroll) {
    +  this.defaultAction(x, y);
    +  this.dispatchEvent(new goog.fx.DragEvent(
    +      goog.fx.Dragger.EventType.DRAG, this, e.clientX, e.clientY, e, x, y));
    +};
    +
    +
    +/**
    + * Returns the 'real' x after limits are applied (allows for some
    + * limits to be undefined).
    + * @param {number} x X-coordinate to limit.
    + * @return {number} The 'real' X-coordinate after limits are applied.
    + */
    +goog.fx.Dragger.prototype.limitX = function(x) {
    +  var rect = this.limits;
    +  var left = !isNaN(rect.left) ? rect.left : null;
    +  var width = !isNaN(rect.width) ? rect.width : 0;
    +  var maxX = left != null ? left + width : Infinity;
    +  var minX = left != null ? left : -Infinity;
    +  return Math.min(maxX, Math.max(minX, x));
    +};
    +
    +
    +/**
    + * Returns the 'real' y after limits are applied (allows for some
    + * limits to be undefined).
    + * @param {number} y Y-coordinate to limit.
    + * @return {number} The 'real' Y-coordinate after limits are applied.
    + */
    +goog.fx.Dragger.prototype.limitY = function(y) {
    +  var rect = this.limits;
    +  var top = !isNaN(rect.top) ? rect.top : null;
    +  var height = !isNaN(rect.height) ? rect.height : 0;
    +  var maxY = top != null ? top + height : Infinity;
    +  var minY = top != null ? top : -Infinity;
    +  return Math.min(maxY, Math.max(minY, y));
    +};
    +
    +
    +/**
    + * Overridable function for computing the initial position of the target
    + * before dragging begins.
    + * @protected
    + */
    +goog.fx.Dragger.prototype.computeInitialPosition = function() {
    +  this.deltaX = this.useRightPositioningForRtl_ ?
    +      goog.style.bidi.getOffsetStart(this.target) : this.target.offsetLeft;
    +  this.deltaY = this.target.offsetTop;
    +};
    +
    +
    +/**
    + * Overridable function for handling the default action of the drag behaviour.
    + * Normally this is simply moving the element to x,y though in some cases it
    + * might be used to resize the layer.  This is basically a shortcut to
    + * implementing a default ondrag event handler.
    + * @param {number} x X-coordinate for target element. In right-to-left, x this
    + *     is the number of pixels the target should be moved to from the right.
    + * @param {number} y Y-coordinate for target element.
    + */
    +goog.fx.Dragger.prototype.defaultAction = function(x, y) {
    +  if (this.useRightPositioningForRtl_ && this.isRightToLeft_()) {
    +    this.target.style.right = x + 'px';
    +  } else {
    +    this.target.style.left = x + 'px';
    +  }
    +  this.target.style.top = y + 'px';
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the dragger is currently in the midst of a drag.
    + */
    +goog.fx.Dragger.prototype.isDragging = function() {
    +  return this.dragging_;
    +};
    +
    +
    +
    +/**
    + * Object representing a drag event
    + * @param {string} type Event type.
    + * @param {goog.fx.Dragger} dragobj Drag object initiating event.
    + * @param {number} clientX X-coordinate relative to the viewport.
    + * @param {number} clientY Y-coordinate relative to the viewport.
    + * @param {goog.events.BrowserEvent} browserEvent The closure object
    + *   representing the browser event that caused this drag event.
    + * @param {number=} opt_actX Optional actual x for drag if it has been limited.
    + * @param {number=} opt_actY Optional actual y for drag if it has been limited.
    + * @param {boolean=} opt_dragCanceled Whether the drag has been canceled.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.fx.DragEvent = function(type, dragobj, clientX, clientY, browserEvent,
    +                             opt_actX, opt_actY, opt_dragCanceled) {
    +  goog.events.Event.call(this, type);
    +
    +  /**
    +   * X-coordinate relative to the viewport
    +   * @type {number}
    +   */
    +  this.clientX = clientX;
    +
    +  /**
    +   * Y-coordinate relative to the viewport
    +   * @type {number}
    +   */
    +  this.clientY = clientY;
    +
    +  /**
    +   * The closure object representing the browser event that caused this drag
    +   * event.
    +   * @type {goog.events.BrowserEvent}
    +   */
    +  this.browserEvent = browserEvent;
    +
    +  /**
    +   * The real x-position of the drag if it has been limited
    +   * @type {number}
    +   */
    +  this.left = goog.isDef(opt_actX) ? opt_actX : dragobj.deltaX;
    +
    +  /**
    +   * The real y-position of the drag if it has been limited
    +   * @type {number}
    +   */
    +  this.top = goog.isDef(opt_actY) ? opt_actY : dragobj.deltaY;
    +
    +  /**
    +   * Reference to the drag object for this event
    +   * @type {goog.fx.Dragger}
    +   */
    +  this.dragger = dragobj;
    +
    +  /**
    +   * Whether drag was canceled with this event. Used to differentiate between
    +   * a legitimate drag END that can result in an action and a drag END which is
    +   * a result of a drag cancelation. For now it can happen 1) with drag END
    +   * event on FireFox when user drags the mouse out of the window, 2) with
    +   * drag END event on IE7 which is generated on MOUSEMOVE event when user
    +   * moves the mouse into the document after the mouse button has been
    +   * released, 3) when TOUCHCANCEL is raised instead of TOUCHEND (on touch
    +   * events).
    +   * @type {boolean}
    +   */
    +  this.dragCanceled = !!opt_dragCanceled;
    +};
    +goog.inherits(goog.fx.DragEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragger_test.html b/mapbox-gl/closure-library/closure/goog/fx/dragger_test.html
    new file mode 100644
    index 0000000000..bcf5e42382
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragger_test.html
    @@ -0,0 +1,37 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.Dragger
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.DraggerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    +  <div id="sandbox_rtl" dir="rtl" style="overflow: auto; position:absolute; top:20px; left: 20px;
    +         width: 100px; height: 100px; background: red;">
    +  </div>
    +  <br />
    +  <br />
    +  <br />
    +  <br />
    +  <br />
    +  <br />
    +  <br />
    +  <br />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragger_test.js b/mapbox-gl/closure-library/closure/goog/fx/dragger_test.js
    new file mode 100644
    index 0000000000..a9ac058ed2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragger_test.js
    @@ -0,0 +1,459 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.DraggerTest');
    +goog.setTestOnly('goog.fx.DraggerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.math.Rect');
    +goog.require('goog.style.bidi');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var HAS_SET_CAPTURE = goog.fx.Dragger.HAS_SET_CAPTURE_;
    +
    +var target;
    +var targetRtl;
    +
    +function setUp() {
    +  var sandbox = goog.dom.getElement('sandbox');
    +  target = goog.dom.createDom('div', {
    +    'id': 'target',
    +    'style': 'display:none;position:absolute;top:15px;left:10px'
    +  });
    +  sandbox.appendChild(target);
    +  sandbox.appendChild(goog.dom.createDom('div', {'id': 'handle'}));
    +
    +  var sandboxRtl = goog.dom.getElement('sandbox_rtl');
    +  targetRtl = goog.dom.createDom('div', {
    +    'id': 'target_rtl',
    +    'style': 'position:absolute; top:15px; right:10px; width:10px; ' +
    +        'height: 10px; background: green;'
    +  });
    +  sandboxRtl.appendChild(targetRtl);
    +  sandboxRtl.appendChild(goog.dom.createDom('div', {
    +    'id': 'background_rtl',
    +    'style': 'width: 10000px;height:50px;position:absolute;color:blue;'
    +  }));
    +  sandboxRtl.appendChild(goog.dom.createDom('div', {'id': 'handle_rtl'}));
    +}
    +
    +function tearDown() {
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +  goog.dom.getElement('sandbox_rtl').innerHTML = '';
    +  goog.events.removeAll(document);
    +}
    +
    +function testStartDrag() {
    +  runStartDragTest('handle', target);
    +}
    +
    +function testStartDrag_rtl() {
    +  runStartDragTest('handle_rtl', targetRtl);
    +}
    +
    +function runStartDragTest(handleId, targetElement) {
    +  var dragger =
    +      new goog.fx.Dragger(targetElement, goog.dom.getElement(handleId));
    +  if (handleId == 'handle_rtl') {
    +    dragger.enableRightPositioningForRtl(true);
    +  }
    +  var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  e.type = goog.events.EventType.MOUSEDOWN;
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.isMouseActionButton().$returns(true);
    +  e.preventDefault();
    +  e.isMouseActionButton().$returns(true);
    +  e.preventDefault();
    +  e.$replay();
    +
    +  goog.events.listen(dragger, goog.fx.Dragger.EventType.START, function() {
    +    targetElement.style.display = 'block';
    +  });
    +
    +  dragger.startDrag(e);
    +
    +  assertTrue('Start drag with no hysteresis must actually start the drag.',
    +      dragger.isDragging());
    +  if (handleId == 'handle_rtl') {
    +    assertEquals(10, goog.style.bidi.getOffsetStart(targetElement));
    +  }
    +  assertEquals('Dragger startX must match event\'s clientX.',
    +      1, dragger.startX);
    +  assertEquals('Dragger clientX must match event\'s clientX',
    +      1, dragger.clientX);
    +  assertEquals('Dragger startY must match event\'s clientY.',
    +      2, dragger.startY);
    +  assertEquals('Dragger clientY must match event\'s clientY',
    +      2, dragger.clientY);
    +  assertEquals('Dragger deltaX must match target\'s offsetLeft',
    +      10, dragger.deltaX);
    +  assertEquals('Dragger deltaY must match target\'s offsetTop',
    +      15, dragger.deltaY);
    +
    +  dragger = new goog.fx.Dragger(targetElement, goog.dom.getElement(handleId));
    +  dragger.setHysteresis(1);
    +  dragger.startDrag(e);
    +  assertFalse('Start drag with a valid non-zero hysteresis should not start ' +
    +      'the drag.', dragger.isDragging());
    +  e.$verify();
    +}
    +
    +
    +/**
    + * @bug 1381317 Cancelling start drag didn't end the attempt to drag.
    + */
    +function testStartDrag_Cancel() {
    +  var dragger = new goog.fx.Dragger(target);
    +
    +  var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  e.type = goog.events.EventType.MOUSEDOWN;
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.isMouseActionButton().$returns(true);
    +  e.$replay();
    +
    +  goog.events.listen(dragger, goog.fx.Dragger.EventType.START, function(e) {
    +    // Cancel drag.
    +    e.preventDefault();
    +  });
    +
    +  dragger.startDrag(e);
    +
    +  assertFalse('Start drag must have been cancelled.',
    +      dragger.isDragging());
    +  assertFalse('Dragger must not have registered mousemove handlers.',
    +      goog.events.hasListener(dragger.document_,
    +          goog.events.EventType.MOUSEMOVE, !HAS_SET_CAPTURE));
    +  assertFalse('Dragger must not have registered mouseup handlers.',
    +      goog.events.hasListener(dragger.document_, goog.events.EventType.MOUSEUP,
    +          !HAS_SET_CAPTURE));
    +  e.$verify();
    +}
    +
    +
    +/**
    + * Tests that start drag happens on left mousedown.
    + */
    +function testStartDrag_LeftMouseDownOnly() {
    +  var dragger = new goog.fx.Dragger(target);
    +
    +  var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  e.type = goog.events.EventType.MOUSEDOWN;
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.isMouseActionButton().$returns(false);
    +  e.$replay();
    +
    +  goog.events.listen(dragger, goog.fx.Dragger.EventType.START, function(e) {
    +    fail('No drag START event should have been dispatched');
    +  });
    +
    +  dragger.startDrag(e);
    +
    +  assertFalse('Start drag must have been cancelled.',
    +      dragger.isDragging());
    +  assertFalse('Dragger must not have registered mousemove handlers.',
    +      goog.events.hasListener(dragger.document_,
    +          goog.events.EventType.MOUSEMOVE, true));
    +  assertFalse('Dragger must not have registered mouseup handlers.',
    +      goog.events.hasListener(dragger.document_, goog.events.EventType.MOUSEUP,
    +          true));
    +  e.$verify();
    +}
    +
    +
    +/**
    + * Tests that start drag happens on other event type than MOUSEDOWN.
    + */
    +function testStartDrag_MouseMove() {
    +  var dragger = new goog.fx.Dragger(target);
    +
    +  var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  e.type = goog.events.EventType.MOUSEMOVE;
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.preventDefault();
    +  e.$replay();
    +
    +  var startDragFired = false;
    +  goog.events.listen(dragger, goog.fx.Dragger.EventType.START, function(e) {
    +    startDragFired = true;
    +  });
    +
    +  dragger.startDrag(e);
    +
    +  assertTrue('Dragging should be in progress.', dragger.isDragging());
    +  assertTrue('Start drag event should have fired.', startDragFired);
    +  assertTrue('Dragger must have registered mousemove handlers.',
    +      goog.events.hasListener(dragger.document_,
    +          goog.events.EventType.MOUSEMOVE, !HAS_SET_CAPTURE));
    +  assertTrue('Dragger must have registered mouseup handlers.',
    +      goog.events.hasListener(dragger.document_, goog.events.EventType.MOUSEUP,
    +          !HAS_SET_CAPTURE));
    +  e.$verify();
    +}
    +
    +
    +/**
    + * @bug 1381317 Cancelling start drag didn't end the attempt to drag.
    + */
    +function testHandleMove_Cancel() {
    +  var dragger = new goog.fx.Dragger(target);
    +  dragger.setHysteresis(5);
    +
    +  goog.events.listen(dragger, goog.fx.Dragger.EventType.START, function(e) {
    +    // Cancel drag.
    +    e.preventDefault();
    +  });
    +
    +  var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.isMouseActionButton().$returns(true).
    +      $anyTimes();
    +  e.preventDefault();
    +  e.$replay();
    +  dragger.startDrag(e);
    +  assertFalse('Start drag must not start drag because of hysterisis.',
    +      dragger.isDragging());
    +  assertTrue('Dragger must have registered mousemove handlers.',
    +      goog.events.hasListener(dragger.document_,
    +          goog.events.EventType.MOUSEMOVE, !HAS_SET_CAPTURE));
    +  assertTrue('Dragger must have registered mouseup handlers.',
    +      goog.events.hasListener(dragger.document_, goog.events.EventType.MOUSEUP,
    +          !HAS_SET_CAPTURE));
    +
    +  e.clientX = 10;
    +  e.clientY = 10;
    +  dragger.handleMove_(e);
    +  assertFalse('Drag must be cancelled.', dragger.isDragging());
    +  assertFalse('Dragger must unregistered mousemove handlers.',
    +      goog.events.hasListener(dragger.document_,
    +          goog.events.EventType.MOUSEMOVE, true));
    +  assertFalse('Dragger must unregistered mouseup handlers.',
    +      goog.events.hasListener(dragger.document_, goog.events.EventType.MOUSEUP,
    +          true));
    +  e.$verify();
    +}
    +
    +
    +/**
    + * @bug 1714667 IE<9 built in drag and drop handling stops dragging.
    + */
    +function testIeDragStartCancelling() {
    +  // Testing only IE<9.
    +  if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(9)) {
    +    return;
    +  }
    +
    +  // Built in 'dragstart' cancelling not enabled.
    +  var dragger = new goog.fx.Dragger(target);
    +
    +  var e = new goog.events.Event(goog.events.EventType.MOUSEDOWN);
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.button = 1; // IE only constant for left button.
    +  var be = new goog.events.BrowserEvent(e);
    +  dragger.startDrag(be);
    +  assertTrue('The drag should have started.', dragger.isDragging());
    +
    +  e = new goog.events.Event(goog.events.EventType.DRAGSTART);
    +  e.target = dragger.document_.documentElement;
    +  assertTrue('The event should not be canceled.',
    +      goog.testing.events.fireBrowserEvent(e));
    +
    +  dragger.dispose();
    +
    +  // Built in 'dragstart' cancelling enabled.
    +  dragger = new goog.fx.Dragger(target);
    +  dragger.setCancelIeDragStart(true);
    +
    +  e = new goog.events.Event(goog.events.EventType.MOUSEDOWN);
    +  e.clientX = 1;
    +  e.clientY = 2;
    +  e.button = 1; // IE only constant for left button.
    +  be = new goog.events.BrowserEvent(e);
    +  dragger.startDrag(be);
    +  assertTrue('The drag should have started.', dragger.isDragging());
    +
    +  e = new goog.events.Event(goog.events.EventType.DRAGSTART);
    +  e.target = dragger.document_.documentElement;
    +  assertFalse('The event should be canceled.',
    +      goog.testing.events.fireBrowserEvent(e));
    +
    +  dragger.dispose();
    +}
    +
    +
    +/** @bug 1680770 */
    +function testOnWindowMouseOut() {
    +  // Test older Gecko browsers - FireFox 2.
    +  if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9a')) {
    +    var dragger = new goog.fx.Dragger(target);
    +
    +    var dragCanceled = false;
    +    goog.events.listen(dragger, goog.fx.Dragger.EventType.END, function(e) {
    +      dragCanceled = e.dragCanceled;
    +    });
    +
    +    var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +    e.type = goog.events.EventType.MOUSEDOWN;
    +    e.clientX = 1;
    +    e.clientY = 2;
    +    e.isMouseActionButton().$returns(true);
    +    e.preventDefault();
    +    e.$replay();
    +    dragger.startDrag(e);
    +    e.$verify();
    +
    +    assertTrue(dragger.isDragging());
    +
    +    e = new goog.events.BrowserEvent();
    +    e.type = goog.events.EventType.MOUSEOUT;
    +    e.target = goog.dom.getElement('sandbox');
    +    e.currentTarget = window.top;
    +    e.relatedTarget = target;
    +    dragger.onWindowMouseOut_(e);
    +
    +    assertFalse('Drag is not canceled for normal in-window mouseout.',
    +        dragCanceled);
    +    assertTrue('Must not stop dragging for normal in-window mouseout.',
    +        dragger.isDragging());
    +
    +    dragCanceled = false;
    +    delete e.relatedTarget;
    +    e.target = goog.dom.createDom('iframe');
    +    dragger.onWindowMouseOut_(e);
    +    assertFalse('Drag is not canceled for mousing into iframe.',
    +        dragCanceled);
    +    assertTrue('Must not stop dragging for mousing into iframe.',
    +        dragger.isDragging());
    +
    +    dragCanceled = false;
    +    e.target = target;
    +    dragger.onWindowMouseOut_(e);
    +    assertTrue('Drag is canceled for real mouse out of top window.',
    +        dragCanceled);
    +    assertFalse('Must stop dragging for real mouse out of top window.',
    +        dragger.isDragging());
    +  }
    +}
    +
    +function testLimits() {
    +  var dragger = new goog.fx.Dragger(target);
    +
    +  assertEquals(100, dragger.limitX(100));
    +  assertEquals(100, dragger.limitY(100));
    +
    +  dragger.setLimits(new goog.math.Rect(10, 20, 30, 40));
    +
    +  assertEquals(10, dragger.limitX(0));
    +  assertEquals(40, dragger.limitX(100));
    +  assertEquals(20, dragger.limitY(0));
    +  assertEquals(60, dragger.limitY(100));
    +}
    +
    +function testWindowBlur() {
    +  if (!goog.fx.Dragger.HAS_SET_CAPTURE_) {
    +    var dragger = new goog.fx.Dragger(target);
    +
    +    var dragEnded = false;
    +    goog.events.listen(dragger, goog.fx.Dragger.EventType.END, function(e) {
    +      dragEnded = true;
    +    });
    +
    +    var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +    e.type = goog.events.EventType.MOUSEDOWN;
    +    e.clientX = 1;
    +    e.clientY = 2;
    +    e.isMouseActionButton().$returns(true);
    +    e.preventDefault();
    +    e.$replay();
    +    dragger.startDrag(e);
    +    e.$verify();
    +
    +    assertTrue(dragger.isDragging());
    +
    +    e = new goog.events.BrowserEvent();
    +    e.type = goog.events.EventType.BLUR;
    +    e.target = window;
    +    e.currentTarget = window;
    +    goog.testing.events.fireBrowserEvent(e);
    +
    +    assertTrue(dragEnded);
    +  }
    +}
    +
    +function testBlur() {
    +  if (!goog.fx.Dragger.HAS_SET_CAPTURE_) {
    +    var dragger = new goog.fx.Dragger(target);
    +
    +    var dragEnded = false;
    +    goog.events.listen(dragger, goog.fx.Dragger.EventType.END, function(e) {
    +      dragEnded = true;
    +    });
    +
    +    var e = new goog.testing.StrictMock(goog.events.BrowserEvent);
    +    e.type = goog.events.EventType.MOUSEDOWN;
    +    e.clientX = 1;
    +    e.clientY = 2;
    +    e.isMouseActionButton().$returns(true);
    +    e.preventDefault();
    +    e.$replay();
    +    dragger.startDrag(e);
    +    e.$verify();
    +
    +    assertTrue(dragger.isDragging());
    +
    +    e = new goog.events.BrowserEvent();
    +    e.type = goog.events.EventType.BLUR;
    +    e.target = document.body;
    +    e.currentTarget = document.body;
    +    // Blur events do not bubble but the test event system does not emulate that
    +    // part so we add a capturing listener on the target and stops the
    +    // propagation at the target, preventing any event from bubbling.
    +    goog.events.listen(document.body, goog.events.EventType.BLUR, function(e) {
    +      e.propagationStopped_ = true;
    +    }, true);
    +    goog.testing.events.fireBrowserEvent(e);
    +
    +    assertFalse(dragEnded);
    +  }
    +}
    +
    +function testCloneNode() {
    +  var element = goog.dom.createDom('div');
    +  element.innerHTML =
    +      '<input type="hidden" value="v0">' +
    +      '<textarea>v1</textarea>' +
    +      '<textarea>v2</textarea>';
    +  element.childNodes[0].value = '\'new\'\n"value"';
    +  element.childNodes[1].value = '<' + '/textarea>&lt;3';
    +  element.childNodes[2].value = '<script>\n\talert("oops!");<' + '/script>';
    +  var clone = goog.fx.Dragger.cloneNode(element);
    +  assertEquals(element.childNodes[0].value, clone.childNodes[0].value);
    +  assertEquals(element.childNodes[1].value, clone.childNodes[1].value);
    +  assertEquals(element.childNodes[2].value, clone.childNodes[2].value);
    +  clone = goog.fx.Dragger.cloneNode(element.childNodes[2]);
    +  assertEquals(element.childNodes[2].value, clone.value);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/draglistgroup.js b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup.js
    new file mode 100644
    index 0000000000..dc20ec02a8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup.js
    @@ -0,0 +1,1312 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A DragListGroup is a class representing a group of one or more
    + * "drag lists" with items that can be dragged within them and between them.
    + *
    + * @see ../demos/draglistgroup.html
    + */
    +
    +
    +goog.provide('goog.fx.DragListDirection');
    +goog.provide('goog.fx.DragListGroup');
    +goog.provide('goog.fx.DragListGroup.EventType');
    +goog.provide('goog.fx.DragListGroupEvent');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * A class representing a group of one or more "drag lists" with items that can
    + * be dragged within them and between them.
    + *
    + * Example usage:
    + *   var dragListGroup = new goog.fx.DragListGroup();
    + *   dragListGroup.setDragItemHandleHoverClass(className1, className2);
    + *   dragListGroup.setDraggerElClass(className3);
    + *   dragListGroup.addDragList(vertList, goog.fx.DragListDirection.DOWN);
    + *   dragListGroup.addDragList(horizList, goog.fx.DragListDirection.RIGHT);
    + *   dragListGroup.init();
    + *
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.fx.DragListGroup = function() {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * The drag lists.
    +   * @type {Array<Element>}
    +   * @private
    +   */
    +  this.dragLists_ = [];
    +
    +  /**
    +   * All the drag items. Set by init().
    +   * @type {Array<Element>}
    +   * @private
    +   */
    +  this.dragItems_ = [];
    +
    +  /**
    +   * Which drag item corresponds to a given handle.  Set by init().
    +   * Specifically, this maps from the unique ID (as given by goog.getUid)
    +   * of the handle to the drag item.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.dragItemForHandle_ = {};
    +
    +  /**
    +   * The event handler for this instance.
    +   * @type {goog.events.EventHandler<!goog.fx.DragListGroup>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Whether the setup has been done to make all items in all lists draggable.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isInitialized_ = false;
    +
    +  /**
    +   * Whether the currDragItem is always displayed. By default the list
    +   * collapses, the currDragItem's display is set to none, when we do not
    +   * hover over a draglist.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isCurrDragItemAlwaysDisplayed_ = false;
    +
    +  /**
    +   * Whether to update the position of the currDragItem as we drag, i.e.,
    +   * insert the currDragItem each time to the position where it would land if
    +   * we were to end the drag at that point. Defaults to true.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.updateWhileDragging_ = true;
    +};
    +goog.inherits(goog.fx.DragListGroup, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum to indicate the direction that a drag list grows.
    + * @enum {number}
    + */
    +goog.fx.DragListDirection = {
    +  DOWN: 0,  // common
    +  RIGHT: 2,  // common
    +  LEFT: 3,  // uncommon (except perhaps for right-to-left interfaces)
    +  RIGHT_2D: 4, // common + handles multiple lines if items are wrapped
    +  LEFT_2D: 5 // for rtl languages
    +};
    +
    +
    +/**
    + * Events dispatched by this class.
    + * @const
    + */
    +goog.fx.DragListGroup.EventType = {
    +  BEFOREDRAGSTART: 'beforedragstart',
    +  DRAGSTART: 'dragstart',
    +  BEFOREDRAGMOVE: 'beforedragmove',
    +  DRAGMOVE: 'dragmove',
    +  BEFOREDRAGEND: 'beforedragend',
    +  DRAGEND: 'dragend'
    +};
    +
    +
    +// The next 4 are user-supplied CSS classes.
    +
    +
    +/**
    + * The user-supplied CSS classes to add to a drag item on hover (not during a
    + * drag action).
    + * @type {Array|undefined}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.dragItemHoverClasses_;
    +
    +
    +/**
    + * The user-supplied CSS classes to add to a drag item handle on hover (not
    + * during a drag action).
    + * @type {Array|undefined}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.dragItemHandleHoverClasses_;
    +
    +
    +/**
    + * The user-supplied CSS classes to add to the current drag item (during a drag
    + * action).
    + * @type {Array|undefined}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.currDragItemClasses_;
    +
    +
    +/**
    + * The user-supplied CSS classes to add to the clone of the current drag item
    + * that's actually being dragged around (during a drag action).
    + * @type {Array<string>|undefined}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.draggerElClasses_;
    +
    +
    +// The next 5 are info applicable during a drag action.
    +
    +
    +/**
    + * The current drag item being moved.
    + * Note: This is only defined while a drag action is happening.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.currDragItem_;
    +
    +
    +/**
    + * The drag list that {@code this.currDragItem_} is currently hovering over, or
    + * null if it is not hovering over a list.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.currHoverList_;
    +
    +
    +/**
    + * The original drag list that the current drag item came from. We need to
    + * remember this in case the user drops the item outside of any lists, in which
    + * case we return the item to its original location.
    + * Note: This is only defined while a drag action is happening.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.origList_;
    +
    +
    +/**
    + * The original next item in the original list that the current drag item came
    + * from. We need to remember this in case the user drops the item outside of
    + * any lists, in which case we return the item to its original location.
    + * Note: This is only defined while a drag action is happening.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.origNextItem_;
    +
    +
    +/**
    + * The current item in the list we are hovering over. We need to remember
    + * this in case we do not update the position of the current drag item while
    + * dragging (see {@code updateWhileDragging_}). In this case the current drag
    + * item will be inserted into the list before this element when the drag ends.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.currHoverItem_;
    +
    +
    +/**
    + * The clone of the current drag item that's actually being dragged around.
    + * Note: This is only defined while a drag action is happening.
    + * @type {Element}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.draggerEl_;
    +
    +
    +/**
    + * The dragger object.
    + * Note: This is only defined while a drag action is happening.
    + * @type {goog.fx.Dragger}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.dragger_;
    +
    +
    +/**
    + * The amount of distance, in pixels, after which a mousedown or touchstart is
    + * considered a drag.
    + * @type {number}
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.hysteresisDistance_ = 0;
    +
    +
    +/**
    + * Sets the property of the currDragItem that it is always displayed in the
    + * list.
    + */
    +goog.fx.DragListGroup.prototype.setIsCurrDragItemAlwaysDisplayed = function() {
    +  this.isCurrDragItemAlwaysDisplayed_ = true;
    +};
    +
    +
    +/**
    + * Sets the private property updateWhileDragging_ to false. This disables the
    + * update of the position of the currDragItem while dragging. It will only be
    + * placed to its new location once the drag ends.
    + */
    +goog.fx.DragListGroup.prototype.setNoUpdateWhileDragging = function() {
    +  this.updateWhileDragging_ = false;
    +};
    +
    +
    +/**
    + * Sets the distance the user has to drag the element before a drag operation
    + * is started.
    + * @param {number} distance The number of pixels after which a mousedown and
    + *     move is considered a drag.
    + */
    +goog.fx.DragListGroup.prototype.setHysteresis = function(distance) {
    +  this.hysteresisDistance_ = distance;
    +};
    +
    +
    +/**
    + * @return {number} distance The number of pixels after which a mousedown and
    + *     move is considered a drag.
    + */
    +goog.fx.DragListGroup.prototype.getHysteresis = function() {
    +  return this.hysteresisDistance_;
    +};
    +
    +
    +/**
    + * Adds a drag list to this DragListGroup.
    + * All calls to this method must happen before the call to init().
    + * Remember that all child nodes (except text nodes) will be made draggable to
    + * any other drag list in this group.
    + *
    + * @param {Element} dragListElement Must be a container for a list of items
    + *     that should all be made draggable.
    + * @param {goog.fx.DragListDirection} growthDirection The direction that this
    + *     drag list grows in (i.e. if an item is appended to the DOM, the list's
    + *     bounding box expands in this direction).
    + * @param {boolean=} opt_unused Unused argument.
    + * @param {string=} opt_dragHoverClass CSS class to apply to this drag list when
    + *     the draggerEl hovers over it during a drag action.  If present, must be a
    + *     single, valid classname (not a string of space-separated classnames).
    + */
    +goog.fx.DragListGroup.prototype.addDragList = function(
    +    dragListElement, growthDirection, opt_unused, opt_dragHoverClass) {
    +  goog.asserts.assert(!this.isInitialized_);
    +
    +  dragListElement.dlgGrowthDirection_ = growthDirection;
    +  dragListElement.dlgDragHoverClass_ = opt_dragHoverClass;
    +  this.dragLists_.push(dragListElement);
    +};
    +
    +
    +/**
    + * Sets a user-supplied function used to get the "handle" element for a drag
    + * item. The function must accept exactly one argument. The argument may be
    + * any drag item element.
    + *
    + * If not set, the default implementation uses the whole drag item as the
    + * handle.
    + *
    + * @param {function(Element): Element} getHandleForDragItemFn A function that,
    + *     given any drag item, returns a reference to its "handle" element
    + *     (which may be the drag item element itself).
    + */
    +goog.fx.DragListGroup.prototype.setFunctionToGetHandleForDragItem = function(
    +    getHandleForDragItemFn) {
    +  goog.asserts.assert(!this.isInitialized_);
    +  this.getHandleForDragItem_ = getHandleForDragItemFn;
    +};
    +
    +
    +/**
    + * Sets a user-supplied CSS class to add to a drag item on hover (not during a
    + * drag action).
    + * @param {...!string} var_args The CSS class or classes.
    + */
    +goog.fx.DragListGroup.prototype.setDragItemHoverClass = function(var_args) {
    +  goog.asserts.assert(!this.isInitialized_);
    +  this.dragItemHoverClasses_ = goog.array.slice(arguments, 0);
    +};
    +
    +
    +/**
    + * Sets a user-supplied CSS class to add to a drag item handle on hover (not
    + * during a drag action).
    + * @param {...!string} var_args The CSS class or classes.
    + */
    +goog.fx.DragListGroup.prototype.setDragItemHandleHoverClass = function(
    +    var_args) {
    +  goog.asserts.assert(!this.isInitialized_);
    +  this.dragItemHandleHoverClasses_ = goog.array.slice(arguments, 0);
    +};
    +
    +
    +/**
    + * Sets a user-supplied CSS class to add to the current drag item (during a
    + * drag action).
    + *
    + * If not set, the default behavior adds visibility:hidden to the current drag
    + * item so that it is a block of empty space in the hover drag list (if any).
    + * If this class is set by the user, then the default behavior does not happen
    + * (unless, of course, the class also contains visibility:hidden).
    + *
    + * @param {...!string} var_args The CSS class or classes.
    + */
    +goog.fx.DragListGroup.prototype.setCurrDragItemClass = function(var_args) {
    +  goog.asserts.assert(!this.isInitialized_);
    +  this.currDragItemClasses_ = goog.array.slice(arguments, 0);
    +};
    +
    +
    +/**
    + * Sets a user-supplied CSS class to add to the clone of the current drag item
    + * that's actually being dragged around (during a drag action).
    + * @param {string} draggerElClass The CSS class.
    + */
    +goog.fx.DragListGroup.prototype.setDraggerElClass = function(draggerElClass) {
    +  goog.asserts.assert(!this.isInitialized_);
    +  // Split space-separated classes up into an array.
    +  this.draggerElClasses_ = goog.string.trim(draggerElClass).split(' ');
    +};
    +
    +
    +/**
    + * Performs the initial setup to make all items in all lists draggable.
    + */
    +goog.fx.DragListGroup.prototype.init = function() {
    +  if (this.isInitialized_) {
    +    return;
    +  }
    +
    +  for (var i = 0, numLists = this.dragLists_.length; i < numLists; i++) {
    +    var dragList = this.dragLists_[i];
    +
    +    var dragItems = goog.dom.getChildren(dragList);
    +    for (var j = 0, numItems = dragItems.length; j < numItems; ++j) {
    +      this.listenForDragEvents(dragItems[j]);
    +    }
    +  }
    +
    +  this.isInitialized_ = true;
    +};
    +
    +
    +/**
    + * Adds a single item to the given drag list and sets up the drag listeners for
    + * it.
    + * If opt_index is specified the item is inserted at this index, otherwise the
    + * item is added as the last child of the list.
    + *
    + * @param {!Element} list The drag list where to add item to.
    + * @param {!Element} item The new element to add.
    + * @param {number=} opt_index Index where to insert the item in the list. If not
    + * specified item is inserted as the last child of list.
    + */
    +goog.fx.DragListGroup.prototype.addItemToDragList = function(list, item,
    +    opt_index) {
    +  if (goog.isDef(opt_index)) {
    +    goog.dom.insertChildAt(list, item, opt_index);
    +  } else {
    +    goog.dom.appendChild(list, item);
    +  }
    +  this.listenForDragEvents(item);
    +};
    +
    +
    +/** @override */
    +goog.fx.DragListGroup.prototype.disposeInternal = function() {
    +  this.eventHandler_.dispose();
    +
    +  for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +    var dragList = this.dragLists_[i];
    +    // Note: IE doesn't allow 'delete' for fields on HTML elements (because
    +    // they're not real JS objects in IE), so we just set them to undefined.
    +    dragList.dlgGrowthDirection_ = undefined;
    +    dragList.dlgDragHoverClass_ = undefined;
    +  }
    +
    +  this.dragLists_.length = 0;
    +  this.dragItems_.length = 0;
    +  this.dragItemForHandle_ = null;
    +
    +  // In the case where a drag event is currently in-progress and dispose is
    +  // called, this cleans up the extra state.
    +  this.cleanupDragDom_();
    +
    +  goog.fx.DragListGroup.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Caches the heights of each drag list and drag item, except for the current
    + * drag item.
    + *
    + * @param {Element} currDragItem The item currently being dragged.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.recacheListAndItemBounds_ = function(
    +    currDragItem) {
    +  for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +    var dragList = this.dragLists_[i];
    +    dragList.dlgBounds_ = goog.style.getBounds(dragList);
    +  }
    +
    +  for (var i = 0, n = this.dragItems_.length; i < n; i++) {
    +    var dragItem = this.dragItems_[i];
    +    if (dragItem != currDragItem) {
    +      dragItem.dlgBounds_ = goog.style.getBounds(dragItem);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Listens for drag events on the given drag item. This method is currently used
    + * to initialize drag items.
    + *
    + * @param {Element} dragItem the element to initialize. This element has to be
    + * in one of the drag lists.
    + * @protected
    + */
    +goog.fx.DragListGroup.prototype.listenForDragEvents = function(dragItem) {
    +  var dragItemHandle = this.getHandleForDragItem_(dragItem);
    +  var uid = goog.getUid(dragItemHandle);
    +  this.dragItemForHandle_[uid] = dragItem;
    +
    +  if (this.dragItemHoverClasses_) {
    +    this.eventHandler_.listen(
    +        dragItem, goog.events.EventType.MOUSEOVER,
    +        this.handleDragItemMouseover_);
    +    this.eventHandler_.listen(
    +        dragItem, goog.events.EventType.MOUSEOUT,
    +        this.handleDragItemMouseout_);
    +  }
    +  if (this.dragItemHandleHoverClasses_) {
    +    this.eventHandler_.listen(
    +        dragItemHandle, goog.events.EventType.MOUSEOVER,
    +        this.handleDragItemHandleMouseover_);
    +    this.eventHandler_.listen(
    +        dragItemHandle, goog.events.EventType.MOUSEOUT,
    +        this.handleDragItemHandleMouseout_);
    +  }
    +
    +  this.dragItems_.push(dragItem);
    +  this.eventHandler_.listen(dragItemHandle,
    +      [goog.events.EventType.MOUSEDOWN, goog.events.EventType.TOUCHSTART],
    +      this.handlePotentialDragStart_);
    +};
    +
    +
    +/**
    + * Handles mouse and touch events which may start a drag action.
    + * @param {!goog.events.BrowserEvent} e MOUSEDOWN or TOUCHSTART event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handlePotentialDragStart_ = function(e) {
    +  var uid = goog.getUid(/** @type {Node} */ (e.currentTarget));
    +  this.currDragItem_ = /** @type {Element} */ (this.dragItemForHandle_[uid]);
    +
    +  this.draggerEl_ = this.createDragElementInternal(this.currDragItem_);
    +  if (this.draggerElClasses_) {
    +    // Add CSS class for the clone, if any.
    +    goog.dom.classlist.addAll(
    +        goog.asserts.assert(this.draggerEl_), this.draggerElClasses_ || []);
    +  }
    +
    +  // Place the clone (i.e. draggerEl) at the same position as the actual
    +  // current drag item. This is a bit tricky since
    +  //   goog.style.getPageOffset() gets the left-top pos of the border, but
    +  //   goog.style.setPageOffset() sets the left-top pos of the margin.
    +  // It's difficult to adjust for the margins of the clone because it's
    +  // difficult to read it: goog.style.getComputedStyle() doesn't work for IE.
    +  // Instead, our workaround is simply to set the clone's margins to 0px.
    +  this.draggerEl_.style.margin = '0';
    +  this.draggerEl_.style.position = 'absolute';
    +  this.draggerEl_.style.visibility = 'hidden';
    +  var doc = goog.dom.getOwnerDocument(this.currDragItem_);
    +  doc.body.appendChild(this.draggerEl_);
    +
    +  // Important: goog.style.setPageOffset() only works correctly for IE when the
    +  // element is already in the document.
    +  var currDragItemPos = goog.style.getPageOffset(this.currDragItem_);
    +  goog.style.setPageOffset(this.draggerEl_, currDragItemPos);
    +
    +  this.dragger_ = new goog.fx.Dragger(this.draggerEl_);
    +  this.dragger_.setHysteresis(this.hysteresisDistance_);
    +
    +  // Listen to events on the dragger. These handlers will be unregistered at
    +  // DRAGEND, when the dragger is disposed of. We can't use eventHandler_,
    +  // because it creates new references to the handler functions at each
    +  // dragging action, and keeps them until DragListGroup is disposed of.
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.START,
    +      this.handleDragStart_, false, this);
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END,
    +      this.handleDragEnd_, false, this);
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.EARLY_CANCEL,
    +      this.cleanup_, false, this);
    +  this.dragger_.startDrag(e);
    +};
    +
    +
    +/**
    + * Creates copy of node being dragged.
    + *
    + * @param {Element} sourceEl Element to copy.
    + * @return {!Element} The clone of {@code sourceEl}.
    + * @deprecated Use goog.fx.Dragger.cloneNode().
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.cloneNode_ = function(sourceEl) {
    +  return goog.fx.Dragger.cloneNode(sourceEl);
    +};
    +
    +
    +/**
    + * Generates an element to follow the cursor during dragging, given a drag
    + * source element.  The default behavior is simply to clone the source element,
    + * but this may be overridden in subclasses.  This method is called by
    + * {@code createDragElement()} before the drag class is added.
    + *
    + * @param {Element} sourceEl Drag source element.
    + * @return {!Element} The new drag element.
    + * @protected
    + * @suppress {deprecated}
    + */
    +goog.fx.DragListGroup.prototype.createDragElementInternal =
    +    function(sourceEl) {
    +  return this.cloneNode_(sourceEl);
    +};
    +
    +
    +/**
    + * Handles the start of a drag action.
    + * @param {!goog.fx.DragEvent} e goog.fx.Dragger.EventType.START event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragStart_ = function(e) {
    +  if (!this.dispatchEvent(new goog.fx.DragListGroupEvent(
    +      goog.fx.DragListGroup.EventType.BEFOREDRAGSTART, this, e.browserEvent,
    +      this.currDragItem_, null, null))) {
    +    e.preventDefault();
    +    this.cleanup_();
    +    return;
    +  }
    +
    +  // Record the original location of the current drag item.
    +  // Note: this.origNextItem_ may be null.
    +  this.origList_ = /** @type {Element} */ (this.currDragItem_.parentNode);
    +  this.origNextItem_ = goog.dom.getNextElementSibling(this.currDragItem_);
    +  this.currHoverItem_ = this.origNextItem_;
    +  this.currHoverList_ = this.origList_;
    +
    +  // If there's a CSS class specified for the current drag item, add it.
    +  // Otherwise, make the actual current drag item hidden (takes up space).
    +  if (this.currDragItemClasses_) {
    +    goog.dom.classlist.addAll(
    +        goog.asserts.assert(this.currDragItem_),
    +        this.currDragItemClasses_ || []);
    +  } else {
    +    this.currDragItem_.style.visibility = 'hidden';
    +  }
    +
    +  // Precompute distances from top-left corner to center for efficiency.
    +  var draggerElSize = goog.style.getSize(this.draggerEl_);
    +  this.draggerEl_.halfWidth = draggerElSize.width / 2;
    +  this.draggerEl_.halfHeight = draggerElSize.height / 2;
    +
    +  this.draggerEl_.style.visibility = '';
    +
    +  // Record the bounds of all the drag lists and all the other drag items. This
    +  // caching is for efficiency, so that we don't have to recompute the bounds on
    +  // each drag move. Do this in the state where the current drag item is not in
    +  // any of the lists, except when update while dragging is disabled, as in this
    +  // case the current drag item does not get removed until drag ends.
    +  if (this.updateWhileDragging_) {
    +    this.currDragItem_.style.display = 'none';
    +  }
    +  this.recacheListAndItemBounds_(this.currDragItem_);
    +  this.currDragItem_.style.display = '';
    +
    +  // Listen to events on the dragger.
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG,
    +      this.handleDragMove_, false, this);
    +
    +  this.dispatchEvent(
    +      new goog.fx.DragListGroupEvent(
    +          goog.fx.DragListGroup.EventType.DRAGSTART, this, e.browserEvent,
    +          this.currDragItem_, this.draggerEl_, this.dragger_));
    +};
    +
    +
    +/**
    + * Handles a drag movement (i.e. DRAG event fired by the dragger).
    + *
    + * @param {goog.fx.DragEvent} dragEvent Event object fired by the dragger.
    + * @return {boolean} The return value for the event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragMove_ = function(dragEvent) {
    +
    +  // Compute the center of the dragger element (i.e. the cloned drag item).
    +  var draggerElPos = goog.style.getPageOffset(this.draggerEl_);
    +  var draggerElCenter = new goog.math.Coordinate(
    +      draggerElPos.x + this.draggerEl_.halfWidth,
    +      draggerElPos.y + this.draggerEl_.halfHeight);
    +
    +  // Check whether the center is hovering over one of the drag lists.
    +  var hoverList = this.getHoverDragList_(draggerElCenter);
    +
    +  // If hovering over a list, find the next item (if drag were to end now).
    +  var hoverNextItem =
    +      hoverList ? this.getHoverNextItem_(hoverList, draggerElCenter) : null;
    +
    +  var rv = this.dispatchEvent(
    +      new goog.fx.DragListGroupEvent(
    +          goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE, this, dragEvent,
    +          this.currDragItem_, this.draggerEl_, this.dragger_,
    +          draggerElCenter, hoverList, hoverNextItem));
    +  if (!rv) {
    +    return false;
    +  }
    +
    +  if (hoverList) {
    +    if (this.updateWhileDragging_) {
    +      this.insertCurrDragItem_(hoverList, hoverNextItem);
    +    } else {
    +      // If update while dragging is disabled do not insert
    +      // the dragged item, but update the hovered item instead.
    +      this.updateCurrHoverItem(hoverNextItem, draggerElCenter);
    +    }
    +    this.currDragItem_.style.display = '';
    +    // Add drag list's hover class (if any).
    +    if (hoverList.dlgDragHoverClass_) {
    +      goog.dom.classlist.add(
    +          goog.asserts.assert(hoverList), hoverList.dlgDragHoverClass_);
    +    }
    +
    +  } else {
    +    // Not hovering over a drag list, so remove the item altogether unless
    +    // specified otherwise by the user.
    +    if (!this.isCurrDragItemAlwaysDisplayed_) {
    +      this.currDragItem_.style.display = 'none';
    +    }
    +
    +    // Remove hover classes (if any) from all drag lists.
    +    for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +      var dragList = this.dragLists_[i];
    +      if (dragList.dlgDragHoverClass_) {
    +        goog.dom.classlist.remove(
    +            goog.asserts.assert(dragList), dragList.dlgDragHoverClass_);
    +      }
    +    }
    +  }
    +
    +  // If the current hover list is different than the last, the lists may have
    +  // shrunk, so we should recache the bounds.
    +  if (hoverList != this.currHoverList_) {
    +    this.currHoverList_ = hoverList;
    +    this.recacheListAndItemBounds_(this.currDragItem_);
    +  }
    +
    +  this.dispatchEvent(
    +      new goog.fx.DragListGroupEvent(
    +          goog.fx.DragListGroup.EventType.DRAGMOVE, this, dragEvent,
    +          /** @type {Element} */ (this.currDragItem_),
    +          this.draggerEl_, this.dragger_,
    +          draggerElCenter, hoverList, hoverNextItem));
    +
    +  // Return false to prevent selection due to mouse drag.
    +  return false;
    +};
    +
    +
    +/**
    + * Clear all our temporary fields that are only defined while dragging, and
    + * all the bounds info stored on the drag lists and drag elements.
    + * @param {!goog.events.Event=} opt_e EARLY_CANCEL event from the dragger if
    + *     cleanup_ was called as an event handler.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.cleanup_ = function(opt_e) {
    +  this.cleanupDragDom_();
    +
    +  this.currDragItem_ = null;
    +  this.currHoverList_ = null;
    +  this.origList_ = null;
    +  this.origNextItem_ = null;
    +  this.draggerEl_ = null;
    +  this.dragger_ = null;
    +
    +  // Note: IE doesn't allow 'delete' for fields on HTML elements (because
    +  // they're not real JS objects in IE), so we just set them to null.
    +  for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +    this.dragLists_[i].dlgBounds_ = null;
    +  }
    +  for (var i = 0, n = this.dragItems_.length; i < n; i++) {
    +    this.dragItems_[i].dlgBounds_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Handles the end or the cancellation of a drag action, i.e. END or CLEANUP
    + * event fired by the dragger.
    + *
    + * @param {!goog.fx.DragEvent} dragEvent Event object fired by the dragger.
    + * @return {boolean} Whether the event was handled.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragEnd_ = function(dragEvent) {
    +  var rv = this.dispatchEvent(
    +      new goog.fx.DragListGroupEvent(
    +          goog.fx.DragListGroup.EventType.BEFOREDRAGEND, this, dragEvent,
    +          /** @type {Element} */ (this.currDragItem_),
    +          this.draggerEl_, this.dragger_));
    +  if (!rv) {
    +    return false;
    +  }
    +
    +  // If update while dragging is disabled insert the current drag item into
    +  // its intended location.
    +  if (!this.updateWhileDragging_) {
    +    this.insertCurrHoverItem();
    +  }
    +
    +  // The DRAGEND handler may need the new order of the list items. Clean up the
    +  // garbage.
    +  // TODO(user): Regression test.
    +  this.cleanupDragDom_();
    +
    +  this.dispatchEvent(
    +      new goog.fx.DragListGroupEvent(
    +          goog.fx.DragListGroup.EventType.DRAGEND, this, dragEvent,
    +          this.currDragItem_, this.draggerEl_, this.dragger_));
    +
    +  this.cleanup_();
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Cleans up DOM changes that are made by the {@code handleDrag*} methods.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.cleanupDragDom_ = function() {
    +  // Disposes of the dragger and remove the cloned drag item.
    +  goog.dispose(this.dragger_);
    +  if (this.draggerEl_) {
    +    goog.dom.removeNode(this.draggerEl_);
    +  }
    +
    +  // If the current drag item is not in any list, put it back in its original
    +  // location.
    +  if (this.currDragItem_ && this.currDragItem_.style.display == 'none') {
    +    // Note: this.origNextItem_ may be null, but insertBefore() still works.
    +    this.origList_.insertBefore(this.currDragItem_, this.origNextItem_);
    +    this.currDragItem_.style.display = '';
    +  }
    +
    +  // If there's a CSS class specified for the current drag item, remove it.
    +  // Otherwise, make the current drag item visible (instead of empty space).
    +  if (this.currDragItemClasses_ && this.currDragItem_) {
    +    goog.dom.classlist.removeAll(
    +        goog.asserts.assert(this.currDragItem_),
    +        this.currDragItemClasses_ || []);
    +  } else if (this.currDragItem_) {
    +    this.currDragItem_.style.visibility = 'visible';
    +  }
    +
    +  // Remove hover classes (if any) from all drag lists.
    +  for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +    var dragList = this.dragLists_[i];
    +    if (dragList.dlgDragHoverClass_) {
    +      goog.dom.classlist.remove(
    +          goog.asserts.assert(dragList), dragList.dlgDragHoverClass_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Default implementation of the function to get the "handle" element for a
    + * drag item. By default, we use the whole drag item as the handle. Users can
    + * change this by calling setFunctionToGetHandleForDragItem().
    + *
    + * @param {Element} dragItem The drag item to get the handle for.
    + * @return {Element} The dragItem element itself.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.getHandleForDragItem_ = function(dragItem) {
    +  return dragItem;
    +};
    +
    +
    +/**
    + * Handles a MOUSEOVER event fired on a drag item.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragItemMouseover_ = function(e) {
    +  var targetEl = goog.asserts.assertElement(e.currentTarget);
    +  goog.dom.classlist.addAll(targetEl, this.dragItemHoverClasses_ || []);
    +};
    +
    +
    +/**
    + * Handles a MOUSEOUT event fired on a drag item.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragItemMouseout_ = function(e) {
    +  var targetEl = goog.asserts.assertElement(e.currentTarget);
    +  goog.dom.classlist.removeAll(targetEl, this.dragItemHoverClasses_ || []);
    +};
    +
    +
    +/**
    + * Handles a MOUSEOVER event fired on the handle element of a drag item.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragItemHandleMouseover_ = function(e) {
    +  var targetEl = goog.asserts.assertElement(e.currentTarget);
    +  goog.dom.classlist.addAll(targetEl, this.dragItemHandleHoverClasses_ || []);
    +};
    +
    +
    +/**
    + * Handles a MOUSEOUT event fired on the handle element of a drag item.
    + * @param {goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.handleDragItemHandleMouseout_ = function(e) {
    +  var targetEl = goog.asserts.assertElement(e.currentTarget);
    +  goog.dom.classlist.removeAll(targetEl,
    +      this.dragItemHandleHoverClasses_ || []);
    +};
    +
    +
    +/**
    + * Helper for handleDragMove_().
    + * Given the position of the center of the dragger element, figures out whether
    + * it's currently hovering over any of the drag lists.
    + *
    + * @param {goog.math.Coordinate} draggerElCenter The center position of the
    + *     dragger element.
    + * @return {Element} If currently hovering over a drag list, returns the drag
    + *     list element. Else returns null.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.getHoverDragList_ = function(draggerElCenter) {
    +
    +  // If the current drag item was in a list last time we did this, then check
    +  // that same list first.
    +  var prevHoverList = null;
    +  if (this.currDragItem_.style.display != 'none') {
    +    prevHoverList = /** @type {Element} */ (this.currDragItem_.parentNode);
    +    // Important: We can't use the cached bounds for this list because the
    +    // cached bounds are based on the case where the current drag item is not
    +    // in the list. Since the current drag item is known to be in this list, we
    +    // must recompute the list's bounds.
    +    var prevHoverListBounds = goog.style.getBounds(prevHoverList);
    +    if (this.isInRect_(draggerElCenter, prevHoverListBounds)) {
    +      return prevHoverList;
    +    }
    +  }
    +
    +  for (var i = 0, n = this.dragLists_.length; i < n; i++) {
    +    var dragList = this.dragLists_[i];
    +    if (dragList == prevHoverList) {
    +      continue;
    +    }
    +    if (this.isInRect_(draggerElCenter, dragList.dlgBounds_)) {
    +      return dragList;
    +    }
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Checks whether a coordinate position resides inside a rectangle.
    + * @param {goog.math.Coordinate} pos The coordinate position.
    + * @param {goog.math.Rect} rect The rectangle.
    + * @return {boolean} True if 'pos' is within the bounds of 'rect'.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.isInRect_ = function(pos, rect) {
    +  return pos.x > rect.left && pos.x < rect.left + rect.width &&
    +         pos.y > rect.top && pos.y < rect.top + rect.height;
    +};
    +
    +
    +/**
    + * Updates the value of currHoverItem_.
    + *
    + * This method is used for insertion only when updateWhileDragging_ is false.
    + * The below implementation is the basic one. This method can be extended by
    + * a subclass to support changes to hovered item (eg: highlighting). Parametr
    + * opt_draggerElCenter can be used for more sophisticated effects.
    + *
    + * @param {Element} hoverNextItem element of the list that is hovered over.
    + * @param {goog.math.Coordinate=} opt_draggerElCenter current position of
    + *     the dragged element.
    + * @protected
    + */
    +goog.fx.DragListGroup.prototype.updateCurrHoverItem = function(
    +    hoverNextItem, opt_draggerElCenter) {
    +  if (hoverNextItem) {
    +    this.currHoverItem_ = hoverNextItem;
    +  }
    +};
    +
    +
    +/**
    + * Inserts the currently dragged item in its new place.
    + *
    + * This method is used for insertion only when updateWhileDragging_ is false
    + * (otherwise there is no need for that). In the basic implementation
    + * the element is inserted before the currently hovered over item (this can
    + * be changed by overriding the method in subclasses).
    + *
    + * @protected
    + */
    +goog.fx.DragListGroup.prototype.insertCurrHoverItem = function() {
    +  this.origList_.insertBefore(this.currDragItem_, this.currHoverItem_);
    +};
    +
    +
    +/**
    + * Helper for handleDragMove_().
    + * Given the position of the center of the dragger element, plus the drag list
    + * that it's currently hovering over, figures out the next drag item in the
    + * list that follows the current position of the dragger element. (I.e. if
    + * the drag action ends right now, it would become the item after the current
    + * drag item.)
    + *
    + * @param {Element} hoverList The drag list that we're hovering over.
    + * @param {goog.math.Coordinate} draggerElCenter The center position of the
    + *     dragger element.
    + * @return {Element} Returns the earliest item in the hover list that belongs
    + *     after the current position of the dragger element. If all items in the
    + *     list should come before the current drag item, then returns null.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.getHoverNextItem_ = function(
    +    hoverList, draggerElCenter) {
    +  if (hoverList == null) {
    +    throw Error('getHoverNextItem_ called with null hoverList.');
    +  }
    +
    +  // The definition of what it means for the draggerEl to be "before" a given
    +  // item in the hover drag list is not always the same. It changes based on
    +  // the growth direction of the hover drag list in question.
    +  /** @type {number} */
    +  var relevantCoord;
    +  var getRelevantBoundFn;
    +  var isBeforeFn;
    +  var pickClosestRow = false;
    +  var distanceToClosestRow = undefined;
    +  switch (hoverList.dlgGrowthDirection_) {
    +    case goog.fx.DragListDirection.DOWN:
    +      // "Before" means draggerElCenter.y is less than item's bottom y-value.
    +      relevantCoord = draggerElCenter.y;
    +      getRelevantBoundFn = goog.fx.DragListGroup.getBottomBound_;
    +      isBeforeFn = goog.fx.DragListGroup.isLessThan_;
    +      break;
    +    case goog.fx.DragListDirection.RIGHT_2D:
    +      pickClosestRow = true;
    +    case goog.fx.DragListDirection.RIGHT:
    +      // "Before" means draggerElCenter.x is less than item's right x-value.
    +      relevantCoord = draggerElCenter.x;
    +      getRelevantBoundFn = goog.fx.DragListGroup.getRightBound_;
    +      isBeforeFn = goog.fx.DragListGroup.isLessThan_;
    +      break;
    +    case goog.fx.DragListDirection.LEFT_2D:
    +      pickClosestRow = true;
    +    case goog.fx.DragListDirection.LEFT:
    +      // "Before" means draggerElCenter.x is greater than item's left x-value.
    +      relevantCoord = draggerElCenter.x;
    +      getRelevantBoundFn = goog.fx.DragListGroup.getLeftBound_;
    +      isBeforeFn = goog.fx.DragListGroup.isGreaterThan_;
    +      break;
    +  }
    +
    +  // This holds the earliest drag item found so far that should come after
    +  // this.currDragItem_ in the hover drag list (based on draggerElCenter).
    +  var earliestAfterItem = null;
    +  // This is the position of the relevant bound for the earliestAfterItem,
    +  // where "relevant" is determined by the growth direction of hoverList.
    +  var earliestAfterItemRelevantBound;
    +
    +  var hoverListItems = goog.dom.getChildren(hoverList);
    +  for (var i = 0, n = hoverListItems.length; i < n; i++) {
    +    var item = hoverListItems[i];
    +    if (item == this.currDragItem_) {
    +      continue;
    +    }
    +
    +    var relevantBound = getRelevantBoundFn(item.dlgBounds_);
    +    // When the hoverlist is broken into multiple rows (i.e., in the case of
    +    // LEFT_2D and RIGHT_2D) it is no longer enough to only look at the
    +    // x-coordinate alone in order to find the {@earliestAfterItem} in the
    +    // hoverlist. Make sure it is chosen from the row closest to the
    +    // {@code draggerElCenter}.
    +    if (pickClosestRow) {
    +      var distanceToRow = goog.fx.DragListGroup.verticalDistanceFromItem_(item,
    +          draggerElCenter);
    +      // Initialize the distance to the closest row to the current value if
    +      // undefined.
    +      if (!goog.isDef(distanceToClosestRow)) {
    +        distanceToClosestRow = distanceToRow;
    +      }
    +      if (isBeforeFn(relevantCoord, relevantBound) &&
    +          (earliestAfterItemRelevantBound == undefined ||
    +           (distanceToRow < distanceToClosestRow) ||
    +           ((distanceToRow == distanceToClosestRow) &&
    +            (isBeforeFn(relevantBound, earliestAfterItemRelevantBound) ||
    +            relevantBound == earliestAfterItemRelevantBound)))) {
    +        earliestAfterItem = item;
    +        earliestAfterItemRelevantBound = relevantBound;
    +      }
    +      // Update distance to closest row.
    +      if (distanceToRow < distanceToClosestRow) {
    +        distanceToClosestRow = distanceToRow;
    +      }
    +    } else if (isBeforeFn(relevantCoord, relevantBound) &&
    +        (earliestAfterItemRelevantBound == undefined ||
    +         isBeforeFn(relevantBound, earliestAfterItemRelevantBound))) {
    +      earliestAfterItem = item;
    +      earliestAfterItemRelevantBound = relevantBound;
    +    }
    +  }
    +  // If we ended up picking an element that is not in the closest row it can
    +  // only happen if we should have picked the last one in which case there is
    +  // no consecutive element.
    +  if (!goog.isNull(earliestAfterItem) &&
    +      goog.fx.DragListGroup.verticalDistanceFromItem_(
    +          earliestAfterItem, draggerElCenter) > distanceToClosestRow) {
    +    return null;
    +  } else {
    +    return earliestAfterItem;
    +  }
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem().
    + * Given an item and a target determine the vertical distance from the item's
    + * center to the target.
    + * @param {Element} item The item to measure the distance from.
    + * @param {goog.math.Coordinate} target The (x,y) coordinate of the target
    + *     to measure the distance to.
    + * @return {number} The vertical distance between the center of the item and
    + *     the target.
    + * @private
    + */
    +goog.fx.DragListGroup.verticalDistanceFromItem_ = function(item, target) {
    +  var itemBounds = item.dlgBounds_;
    +  var itemCenterY = itemBounds.top + (itemBounds.height - 1) / 2;
    +  return Math.abs(target.y - itemCenterY);
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem_().
    + * Given the bounds of an item, computes the item's bottom y-value.
    + * @param {goog.math.Rect} itemBounds The bounds of the item.
    + * @return {number} The item's bottom y-value.
    + * @private
    + */
    +goog.fx.DragListGroup.getBottomBound_ = function(itemBounds) {
    +  return itemBounds.top + itemBounds.height - 1;
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem_().
    + * Given the bounds of an item, computes the item's right x-value.
    + * @param {goog.math.Rect} itemBounds The bounds of the item.
    + * @return {number} The item's right x-value.
    + * @private
    + */
    +goog.fx.DragListGroup.getRightBound_ = function(itemBounds) {
    +  return itemBounds.left + itemBounds.width - 1;
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem_().
    + * Given the bounds of an item, computes the item's left x-value.
    + * @param {goog.math.Rect} itemBounds The bounds of the item.
    + * @return {number} The item's left x-value.
    + * @private
    + */
    +goog.fx.DragListGroup.getLeftBound_ = function(itemBounds) {
    +  return itemBounds.left || 0;
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem_().
    + * @param {number} a Number to compare.
    + * @param {number} b Number to compare.
    + * @return {boolean} Whether a is less than b.
    + * @private
    + */
    +goog.fx.DragListGroup.isLessThan_ = function(a, b) {
    +  return a < b;
    +};
    +
    +
    +/**
    + * Private helper for getHoverNextItem_().
    + * @param {number} a Number to compare.
    + * @param {number} b Number to compare.
    + * @return {boolean} Whether a is greater than b.
    + * @private
    + */
    +goog.fx.DragListGroup.isGreaterThan_ = function(a, b) {
    +  return a > b;
    +};
    +
    +
    +/**
    + * Inserts the current drag item to the appropriate location in the drag list
    + * that we're hovering over (if the current drag item is not already there).
    + *
    + * @param {Element} hoverList The drag list we're hovering over.
    + * @param {Element} hoverNextItem The next item in the hover drag list.
    + * @private
    + */
    +goog.fx.DragListGroup.prototype.insertCurrDragItem_ = function(
    +    hoverList, hoverNextItem) {
    +  if (this.currDragItem_.parentNode != hoverList ||
    +      goog.dom.getNextElementSibling(this.currDragItem_) != hoverNextItem) {
    +    // The current drag item is not in the correct location, so we move it.
    +    // Note: hoverNextItem may be null, but insertBefore() still works.
    +    hoverList.insertBefore(this.currDragItem_, hoverNextItem);
    +  }
    +};
    +
    +
    +
    +/**
    + * The event object dispatched by DragListGroup.
    + * The fields draggerElCenter, hoverList, and hoverNextItem are only available
    + * for the BEFOREDRAGMOVE and DRAGMOVE events.
    + *
    + * @param {string} type The event type string.
    + * @param {goog.fx.DragListGroup} dragListGroup A reference to the associated
    + *     DragListGroup object.
    + * @param {goog.events.BrowserEvent|goog.fx.DragEvent} event The event fired
    + *     by the browser or fired by the dragger.
    + * @param {Element} currDragItem The current drag item being moved.
    + * @param {Element} draggerEl The clone of the current drag item that's actually
    + *     being dragged around.
    + * @param {goog.fx.Dragger} dragger The dragger object.
    + * @param {goog.math.Coordinate=} opt_draggerElCenter The current center
    + *     position of the draggerEl.
    + * @param {Element=} opt_hoverList The current drag list that's being hovered
    + *     over, or null if the center of draggerEl is outside of any drag lists.
    + *     If not null and the drag action ends right now, then currDragItem will
    + *     end up in this list.
    + * @param {Element=} opt_hoverNextItem The current next item in the hoverList
    + *     that the draggerEl is hovering over. (I.e. If the drag action ends
    + *     right now, then this item would become the next item after the new
    + *     location of currDragItem.) May be null if not applicable or if
    + *     currDragItem would be added to the end of hoverList.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.fx.DragListGroupEvent = function(
    +    type, dragListGroup, event, currDragItem, draggerEl, dragger,
    +    opt_draggerElCenter, opt_hoverList, opt_hoverNextItem) {
    +  goog.events.Event.call(this, type);
    +
    +  /**
    +   * A reference to the associated DragListGroup object.
    +   * @type {goog.fx.DragListGroup}
    +   */
    +  this.dragListGroup = dragListGroup;
    +
    +  /**
    +   * The event fired by the browser or fired by the dragger.
    +   * @type {goog.events.BrowserEvent|goog.fx.DragEvent}
    +   */
    +  this.event = event;
    +
    +  /**
    +   * The current drag item being move.
    +   * @type {Element}
    +   */
    +  this.currDragItem = currDragItem;
    +
    +  /**
    +   * The clone of the current drag item that's actually being dragged around.
    +   * @type {Element}
    +   */
    +  this.draggerEl = draggerEl;
    +
    +  /**
    +   * The dragger object.
    +   * @type {goog.fx.Dragger}
    +   */
    +  this.dragger = dragger;
    +
    +  /**
    +   * The current center position of the draggerEl.
    +   * @type {goog.math.Coordinate|undefined}
    +   */
    +  this.draggerElCenter = opt_draggerElCenter;
    +
    +  /**
    +   * The current drag list that's being hovered over, or null if the center of
    +   * draggerEl is outside of any drag lists. (I.e. If not null and the drag
    +   * action ends right now, then currDragItem will end up in this list.)
    +   * @type {Element|undefined}
    +   */
    +  this.hoverList = opt_hoverList;
    +
    +  /**
    +   * The current next item in the hoverList that the draggerEl is hovering over.
    +   * (I.e. If the drag action ends right now, then this item would become the
    +   * next item after the new location of currDragItem.) May be null if not
    +   * applicable or if currDragItem would be added to the end of hoverList.
    +   * @type {Element|undefined}
    +   */
    +  this.hoverNextItem = opt_hoverNextItem;
    +};
    +goog.inherits(goog.fx.DragListGroupEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.html b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.html
    new file mode 100644
    index 0000000000..868ee62e9b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.html
    @@ -0,0 +1,42 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.fx.DragListGroup
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.DragListGroupTest');
    +  </script>
    +  <style>
    +   .opacity-40 {
    +    opacity: 0.4;
    +    -moz-opacity: 0.4;
    +    filter: alpha(opacity=40);
    +  }
    +  .cursor_move {
    +    cursor: move;
    +    -moz-user-select: none;
    +    -webkit-user-select: none;
    +  }
    +  .cursor_pointer {
    +    cursor: pointer;
    +  }
    +  .blue_bg {
    +    background-color: #0000CC;
    +  }
    +  </style>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.js b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.js
    new file mode 100644
    index 0000000000..8fc0664e35
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/draglistgroup_test.js
    @@ -0,0 +1,397 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.DragListGroupTest');
    +goog.setTestOnly('goog.fx.DragListGroupTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.DragEvent');
    +goog.require('goog.fx.DragListDirection');
    +goog.require('goog.fx.DragListGroup');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.object');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/** @type {goog.fx.DragListGroup} */
    +var dlg;
    +
    +
    +/** @type {goog.dom.Element} */
    +var list;
    +
    +
    +/** @type {goog.events.BrowserEvent} */
    +var event;
    +
    +
    +/**
    + * The number of event listeners registered by the DragListGroup after the
    + * init() call.
    + * @type {number}
    + */
    +var initialListenerCount;
    +
    +
    +/**
    + * Type of events fired by the DragListGroup.
    + * @type {!Array<string>}
    + */
    +var firedEventTypes;
    +
    +function setUp() {
    +  var sandbox = goog.dom.getElement('sandbox');
    +  list = goog.dom.createDom('div', {'id': 'horiz_div'});
    +  list.appendChild(
    +      goog.dom.createDom('div', null, goog.dom.createTextNode('1')));
    +  list.appendChild(
    +      goog.dom.createDom('div', null, goog.dom.createTextNode('2')));
    +  list.appendChild(
    +      goog.dom.createDom('div', null, goog.dom.createTextNode('3')));
    +  sandbox.appendChild(list);
    +
    +  dlg = new goog.fx.DragListGroup();
    +  dlg.setDragItemHoverClass('opacity_40', 'cursor_move');
    +  dlg.setDragItemHandleHoverClass('opacity_40', 'cursor_pointer');
    +  dlg.setCurrDragItemClass('blue_bg', 'opacity_40');
    +  dlg.setDraggerElClass('cursor_move', 'blue_bg');
    +  dlg.addDragList(list, goog.fx.DragListDirection.RIGHT);
    +  dlg.init();
    +
    +  initialListenerCount = goog.object.getCount(dlg.eventHandler_.keys_);
    +
    +  event = new goog.events.BrowserEvent();
    +  event.currentTarget = list.getElementsByTagName('div')[0];
    +
    +  firedEventTypes = [];
    +  goog.events.listen(dlg,
    +      goog.object.getValues(goog.fx.DragListGroup.EventType),
    +      function(e) {
    +        firedEventTypes.push(e.type);
    +      });
    +}
    +
    +function tearDown() {
    +  dlg.dispose();
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +
    +/**
    + * Test the initial assumptions.
    + *
    + * Verify that the setter methods work properly, i.e., the CSS classes are
    + * stored in the private arrays after init() but are not added yet to target.
    + * (Since initially, we are not yet hovering over any list, in particular,
    + * over this target.)
    + */
    +function testSettersAfterInit() {
    +  assertTrue(goog.array.equals(dlg.dragItemHoverClasses_,
    +      ['opacity_40', 'cursor_move']));
    +  assertTrue(goog.array.equals(dlg.dragItemHandleHoverClasses_,
    +      ['opacity_40', 'cursor_pointer']));
    +  assertTrue(goog.array.equals(dlg.currDragItemClasses_,
    +      ['blue_bg', 'opacity_40']));
    +
    +  assertFalse('Should have no cursor_move class after init',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
    +  assertFalse('Should have no cursor_pointer class after init',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
    +  assertFalse('Should have no opacity_40 class after init',
    +      goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
    +  assertFalse('Should not have blue_bg class after init',
    +      goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
    +}
    +
    +
    +/**
    + * Test the effect of hovering over a list.
    + *
    + * Check that after the MOUSEOVER browser event these classes are added to
    + * the current target of the event.
    + */
    +function testAddDragItemHoverClasses() {
    +  dlg.handleDragItemMouseover_(event);
    +
    +  assertTrue('Should have cursor_move class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
    +  assertTrue('Should have opacity_40 class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
    +  assertFalse('Should not have cursor_pointer class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
    +  assertFalse('Should not have blue_bg class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
    +}
    +
    +function testAddDragItemHandleHoverClasses() {
    +  dlg.handleDragItemHandleMouseover_(event);
    +
    +  assertFalse('Should not have cursor_move class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
    +  assertTrue('Should have opacity_40 class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
    +  assertTrue('Should have cursor_pointer class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
    +  assertFalse('Should not have blue_bg class after MOUSEOVER',
    +      goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
    +}
    +
    +
    +/**
    + * Test the effect of stopping hovering over a list.
    + *
    + * Check that after the MOUSEOUT browser event all CSS classes are removed
    + * from the target (as we are no longer hovering over the it).
    + */
    +function testRemoveDragItemHoverClasses() {
    +  dlg.handleDragItemMouseover_(event);
    +  dlg.handleDragItemMouseout_(event);
    +
    +  assertFalse('Should have no cursor_move class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
    +  assertFalse('Should have no cursor_pointer class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
    +  assertFalse('Should have no opacity_40 class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
    +  assertFalse('Should have no blue_bg class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
    +}
    +
    +function testRemoveDragItemHandleHoverClasses() {
    +  dlg.handleDragItemHandleMouseover_(event);
    +  dlg.handleDragItemHandleMouseout_(event);
    +
    +  assertFalse('Should have no cursor_move class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_move'));
    +  assertFalse('Should have no cursor_pointer class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'cursor_pointer'));
    +  assertFalse('Should have no opacity_40 class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'opacity_40'));
    +  assertFalse('Should have no blue_bg class after MOUSEOUT',
    +      goog.dom.classlist.contains(event.currentTarget, 'blue_bg'));
    +}
    +
    +
    +/**
    + * Test the effect of dragging an item. (DRAGSTART event.)
    + *
    + * Check that after the MOUSEDOWN browser event is handled by the
    + * handlePotentialDragStart_() method the currDragItem has the CSS classes
    + * set by the setter method.
    + */
    +function testAddCurrentDragItemClasses() {
    +  var be = new goog.events.BrowserEvent({
    +    type: goog.events.EventType.MOUSEDOWN,
    +    button: goog.events.BrowserFeature.HAS_W3C_BUTTON ? 0 : 1
    +  });
    +  event.event_ = be;
    +
    +  dlg.handlePotentialDragStart_(event);
    +
    +  assertFalse('Should have no cursor_move class after MOUSEDOWN',
    +      goog.dom.classlist.contains(dlg.currDragItem_, 'cursor_move'));
    +  assertFalse('Should have no cursor_pointer class after MOUSEDOWN',
    +      goog.dom.classlist.contains(dlg.currDragItem_, 'cursor_pointer'));
    +  assertTrue('Should have opacity_40 class after MOUSEDOWN',
    +      goog.dom.classlist.contains(dlg.currDragItem_, 'opacity_40'));
    +  assertTrue('Should have blue_bg class after MOUSEDOWN',
    +      goog.dom.classlist.contains(dlg.currDragItem_, 'blue_bg'));
    +}
    +
    +
    +/**
    + * Test the effect of dragging an item. (DRAGEND event.)
    + *
    + * Check that after the MOUSEUP browser event handled by the handleDragEnd_()
    + * method the currDragItem has no CSS classes set in the dispatched event.
    + */
    +function testRemoveCurrentDragItemClasses() {
    +  var be = new goog.events.BrowserEvent({
    +    type: goog.events.EventType.MOUSEDOWN,
    +    button: goog.events.BrowserFeature.HAS_W3C_BUTTON ? 0 : 1
    +  });
    +  event.event_ = be;
    +  dlg.handlePotentialDragStart_(event);
    +
    +  // Need to catch the dispatched event because the temporary fields
    +  // including dlg.currentDragItem_ are cleared after the dragging has ended.
    +  var currDragItem = goog.dom.createDom(
    +      'div', ['cursor_move', 'blue_bg'], goog.dom.createTextNode('4'));
    +  goog.events.listen(dlg, goog.fx.DragListGroup.EventType.DRAGEND,
    +      function(e) {currDragItem = dlg.currDragItem_;});
    +
    +  var dragger = new goog.fx.Dragger(event.currentTarget);
    +  be.type = goog.events.EventType.MOUSEUP;
    +  be.clientX = 1;
    +  be.clientY = 2;
    +  var dragEvent = new goog.fx.DragEvent(
    +      goog.fx.Dragger.EventType.END, dragger, be.clientX, be.clientY, be);
    +  dlg.handleDragEnd_(dragEvent); // this method dispatches the DRAGEND event
    +  dragger.dispose();
    +
    +  assertFalse('Should have no cursor_move class after MOUSEUP',
    +      goog.dom.classlist.contains(currDragItem, 'cursor_move'));
    +  assertFalse('Should have no cursor_pointer class after MOUSEUP',
    +      goog.dom.classlist.contains(currDragItem, 'cursor_pointer'));
    +  assertFalse('Should have no opacity_40 class after MOUSEUP',
    +      goog.dom.classlist.contains(currDragItem, 'opacity_40'));
    +  assertFalse('Should have no blue_bg class after MOUSEUP',
    +      goog.dom.classlist.contains(currDragItem, 'blue_bg'));
    +}
    +
    +
    +/**
    + * Asserts that the DragListGroup is in idle state.
    + * @param {!goog.fx.DragListGroup} dlg The DragListGroup to examine.
    + */
    +function assertIdle(dlg) {
    +  assertNull('dragger element has been cleaned up', dlg.draggerEl_);
    +  assertNull('dragger has been cleaned up', dlg.dragger_);
    +  assertEquals('the additional event listeners have been removed',
    +      initialListenerCount, goog.object.getCount(dlg.eventHandler_.keys_));
    +}
    +
    +function testFiredEvents() {
    +  goog.testing.events.fireClickSequence(list.firstChild);
    +  assertArrayEquals('event types in case of zero distance dragging', [
    +    goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +    goog.fx.DragListGroup.EventType.DRAGSTART,
    +    goog.fx.DragListGroup.EventType.BEFOREDRAGEND,
    +    goog.fx.DragListGroup.EventType.DRAGEND
    +  ], firedEventTypes);
    +  assertIdle(dlg);
    +}
    +
    +function testFiredEventsWithHysteresis() {
    +  dlg.setHysteresis(2);
    +
    +  goog.testing.events.fireClickSequence(list.firstChild);
    +  assertArrayEquals('no events fired on click if hysteresis is enabled', [],
    +      firedEventTypes);
    +  assertIdle(dlg);
    +
    +  goog.testing.events.fireMouseDownEvent(list.firstChild, null,
    +      new goog.math.Coordinate(0, 0));
    +  goog.testing.events.fireMouseMoveEvent(list.firstChild,
    +      new goog.math.Coordinate(1, 0));
    +  assertArrayEquals('no events fired below hysteresis distance', [],
    +      firedEventTypes);
    +
    +  goog.testing.events.fireMouseMoveEvent(list.firstChild,
    +      new goog.math.Coordinate(3, 0));
    +  assertArrayEquals('start+move events are fired over hysteresis distance', [
    +    goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +    goog.fx.DragListGroup.EventType.DRAGSTART,
    +    goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE,
    +    goog.fx.DragListGroup.EventType.DRAGMOVE
    +  ], firedEventTypes);
    +
    +  firedEventTypes.length = 0;
    +  goog.testing.events.fireMouseUpEvent(list.firstChild, null,
    +      new goog.math.Coordinate(3, 0));
    +  assertArrayEquals('end events are fired on mouseup', [
    +    goog.fx.DragListGroup.EventType.BEFOREDRAGEND,
    +    goog.fx.DragListGroup.EventType.DRAGEND
    +  ], firedEventTypes);
    +  assertIdle(dlg);
    +}
    +
    +function testPreventDefaultBeforeDragStart() {
    +  goog.events.listen(dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +      goog.events.Event.preventDefault);
    +
    +  goog.testing.events.fireMouseDownEvent(list.firstChild);
    +  assertArrayEquals('event types if dragging is prevented',
    +      [goog.fx.DragListGroup.EventType.BEFOREDRAGSTART], firedEventTypes);
    +  assertIdle(dlg);
    +}
    +
    +function testPreventDefaultBeforeDragStartWithHysteresis() {
    +  dlg.setHysteresis(5);
    +  goog.events.listen(dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +      goog.events.Event.preventDefault);
    +
    +  goog.testing.events.fireMouseDownEvent(list.firstChild, null,
    +      new goog.math.Coordinate(0, 0));
    +  goog.testing.events.fireMouseMoveEvent(list.firstChild,
    +      new goog.math.Coordinate(10, 0));
    +  assertArrayEquals('event types if dragging is prevented',
    +      [goog.fx.DragListGroup.EventType.BEFOREDRAGSTART], firedEventTypes);
    +  assertIdle(dlg);
    +}
    +
    +function testRightClick() {
    +  goog.testing.events.fireMouseDownEvent(list.firstChild,
    +      goog.events.BrowserEvent.MouseButton.RIGHT);
    +  goog.testing.events.fireMouseUpEvent(list.firstChild,
    +      goog.events.BrowserEvent.MouseButton.RIGHT);
    +
    +  assertArrayEquals('no events fired', [], firedEventTypes);
    +  assertIdle(dlg);
    +}
    +
    +
    +/**
    + * Tests that a new item can be added to a drag list after the control has
    + * been initialized.
    + */
    +function testAddItemToDragList() {
    +  var item =
    +      goog.dom.createDom('div', null, goog.dom.createTextNode('newItem'));
    +
    +  dlg.addItemToDragList(list, item);
    +
    +  assertEquals(item, list.lastChild);
    +  assertEquals(4, goog.dom.getChildren(list).length);
    +
    +  goog.events.listen(dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +      goog.events.Event.preventDefault);
    +
    +  goog.testing.events.fireMouseDownEvent(item);
    +  assertTrue('Should fire beforedragstart event when clicked',
    +      goog.array.equals([goog.fx.DragListGroup.EventType.BEFOREDRAGSTART],
    +      firedEventTypes));
    +}
    +
    +
    +/**
    + * Tests that a new item added to a drag list after the control has been
    + * initialized is inserted at the correct position.
    + */
    +function testInsertItemInDragList() {
    +  var item =
    +      goog.dom.createDom('div', null, goog.dom.createTextNode('newItem'));
    +
    +  dlg.addItemToDragList(list, item, 0);
    +
    +  assertEquals(item, list.firstChild);
    +  assertEquals(4, goog.dom.getChildren(list).length);
    +
    +  goog.events.listen(dlg, goog.fx.DragListGroup.EventType.BEFOREDRAGSTART,
    +      goog.events.Event.preventDefault);
    +
    +  goog.testing.events.fireMouseDownEvent(item);
    +  assertTrue('Should fire beforedragstart event when clicked',
    +      goog.array.equals([goog.fx.DragListGroup.EventType.BEFOREDRAGSTART],
    +      firedEventTypes));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport.js b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport.js
    new file mode 100644
    index 0000000000..66072e8d23
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport.js
    @@ -0,0 +1,300 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class to support scrollable containers for drag and drop.
    + *
    + * @author dgajda@google.com (Damian Gajda)
    + */
    +
    +goog.provide('goog.fx.DragScrollSupport');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * A scroll support class. Currently this class will automatically scroll
    + * a scrollable container node and scroll it by a fixed amount at a timed
    + * interval when the mouse is moved above or below the container or in vertical
    + * margin areas. Intended for use in drag and drop. This could potentially be
    + * made more general and could support horizontal scrolling.
    + *
    + * @param {Element} containerNode A container that can be scrolled.
    + * @param {number=} opt_margin Optional margin to use while scrolling.
    + * @param {boolean=} opt_externalMouseMoveTracking Whether mouse move events
    + *     are tracked externally by the client object which calls the mouse move
    + *     event handler, useful when events are generated for more than one source
    + *     element and/or are not real mousemove events.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @see ../demos/dragscrollsupport.html
    + */
    +goog.fx.DragScrollSupport = function(containerNode, opt_margin,
    +                                     opt_externalMouseMoveTracking) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The container to be scrolled.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.containerNode_ = containerNode;
    +
    +  /**
    +   * Scroll timer that will scroll the container until it is stopped.
    +   * It will scroll when the mouse is outside the scrolling area of the
    +   * container.
    +   *
    +   * @type {goog.Timer}
    +   * @private
    +   */
    +  this.scrollTimer_ = new goog.Timer(goog.fx.DragScrollSupport.TIMER_STEP_);
    +
    +  /**
    +   * EventHandler used to set up and tear down listeners.
    +   * @type {goog.events.EventHandler<!goog.fx.DragScrollSupport>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The current scroll delta.
    +   * @type {goog.math.Coordinate}
    +   * @private
    +   */
    +  this.scrollDelta_ = new goog.math.Coordinate();
    +
    +  /**
    +   * The container bounds.
    +   * @type {goog.math.Rect}
    +   * @private
    +   */
    +  this.containerBounds_ = goog.style.getBounds(containerNode);
    +
    +  /**
    +   * The margin for triggering a scroll.
    +   * @type {number}
    +   * @private
    +   */
    +  this.margin_ = opt_margin || 0;
    +
    +  /**
    +   * The bounding rectangle which if left triggers scrolling.
    +   * @type {goog.math.Rect}
    +   * @private
    +   */
    +  this.scrollBounds_ = opt_margin ?
    +      this.constrainBounds_(this.containerBounds_.clone()) :
    +      this.containerBounds_;
    +
    +  this.setupListeners_(!!opt_externalMouseMoveTracking);
    +};
    +goog.inherits(goog.fx.DragScrollSupport, goog.Disposable);
    +
    +
    +/**
    + * The scroll timer step in ms.
    + * @type {number}
    + * @private
    + */
    +goog.fx.DragScrollSupport.TIMER_STEP_ = 50;
    +
    +
    +/**
    + * The scroll step in pixels.
    + * @type {number}
    + * @private
    + */
    +goog.fx.DragScrollSupport.SCROLL_STEP_ = 8;
    +
    +
    +/**
    + * The suggested scrolling margin.
    + * @type {number}
    + */
    +goog.fx.DragScrollSupport.MARGIN = 32;
    +
    +
    +/**
    + * Whether scrolling should be constrained to happen only when the cursor is
    + * inside the container node.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.constrainScroll_ = false;
    +
    +
    +/**
    + * Whether horizontal scrolling is allowed.
    + * @type {boolean}
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.horizontalScrolling_ = true;
    +
    +
    +/**
    + * Sets whether scrolling should be constrained to happen only when the cursor
    + * is inside the container node.
    + * NOTE: If a margin is not set, then it does not make sense to
    + * contain the scroll, because in that case scroll will never be triggered.
    + * @param {boolean} constrain Whether scrolling should be constrained to happen
    + *     only when the cursor is inside the container node.
    + */
    +goog.fx.DragScrollSupport.prototype.setConstrainScroll = function(constrain) {
    +  this.constrainScroll_ = !!this.margin_ && constrain;
    +};
    +
    +
    +/**
    + * Sets whether horizontal scrolling is allowed.
    + * @param {boolean} scrolling Whether horizontal scrolling is allowed.
    + */
    +goog.fx.DragScrollSupport.prototype.setHorizontalScrolling =
    +    function(scrolling) {
    +  this.horizontalScrolling_ = scrolling;
    +};
    +
    +
    +/**
    + * Constrains the container bounds with respect to the margin.
    + *
    + * @param {goog.math.Rect} bounds The container element.
    + * @return {goog.math.Rect} The bounding rectangle used to calculate scrolling
    + *     direction.
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.constrainBounds_ = function(bounds) {
    +  var margin = this.margin_;
    +  if (margin) {
    +    var quarterHeight = bounds.height * 0.25;
    +    var yMargin = Math.min(margin, quarterHeight);
    +    bounds.top += yMargin;
    +    bounds.height -= 2 * yMargin;
    +
    +    var quarterWidth = bounds.width * 0.25;
    +    var xMargin = Math.min(margin, quarterWidth);
    +    bounds.top += xMargin;
    +    bounds.height -= 2 * xMargin;
    +  }
    +  return bounds;
    +};
    +
    +
    +/**
    + * Attaches listeners and activates automatic scrolling.
    + * @param {boolean} externalMouseMoveTracking Whether to enable internal
    + *     mouse move event handling.
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.setupListeners_ = function(
    +    externalMouseMoveTracking) {
    +  if (!externalMouseMoveTracking) {
    +    // Track mouse pointer position to determine scroll direction.
    +    this.eventHandler_.listen(goog.dom.getOwnerDocument(this.containerNode_),
    +        goog.events.EventType.MOUSEMOVE, this.onMouseMove);
    +  }
    +
    +  // Scroll with a constant speed.
    +  this.eventHandler_.listen(this.scrollTimer_, goog.Timer.TICK, this.onTick_);
    +};
    +
    +
    +/**
    + * Handler for timer tick event, scrolls the container by one scroll step if
    + * needed.
    + * @param {goog.events.Event} event Timer tick event.
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.onTick_ = function(event) {
    +  this.containerNode_.scrollTop += this.scrollDelta_.y;
    +  this.containerNode_.scrollLeft += this.scrollDelta_.x;
    +};
    +
    +
    +/**
    + * Handler for mouse moves events.
    + * @param {goog.events.Event} event Mouse move event.
    + */
    +goog.fx.DragScrollSupport.prototype.onMouseMove = function(event) {
    +  var deltaX = this.horizontalScrolling_ ? this.calculateScrollDelta(
    +      event.clientX, this.scrollBounds_.left, this.scrollBounds_.width) : 0;
    +  var deltaY = this.calculateScrollDelta(event.clientY,
    +      this.scrollBounds_.top, this.scrollBounds_.height);
    +  this.scrollDelta_.x = deltaX;
    +  this.scrollDelta_.y = deltaY;
    +
    +  // If the scroll data is 0 or the event fired outside of the
    +  // bounds of the container node.
    +  if ((!deltaX && !deltaY) ||
    +      (this.constrainScroll_ &&
    +       !this.isInContainerBounds_(event.clientX, event.clientY))) {
    +    this.scrollTimer_.stop();
    +  } else if (!this.scrollTimer_.enabled) {
    +    this.scrollTimer_.start();
    +  }
    +};
    +
    +
    +/**
    + * Gets whether the input coordinate is in the container bounds.
    + * @param {number} x The x coordinate.
    + * @param {number} y The y coordinate.
    + * @return {boolean} Whether the input coordinate is in the container bounds.
    + * @private
    + */
    +goog.fx.DragScrollSupport.prototype.isInContainerBounds_ = function(x, y) {
    +  var containerBounds = this.containerBounds_;
    +  return containerBounds.left <= x &&
    +         containerBounds.left + containerBounds.width >= x &&
    +         containerBounds.top <= y &&
    +         containerBounds.top + containerBounds.height >= y;
    +};
    +
    +
    +/**
    + * Calculates scroll delta.
    + *
    + * @param {number} coordinate Current mouse pointer coordinate.
    + * @param {number} min The coordinate value below which scrolling up should be
    + *     started.
    + * @param {number} rangeLength The length of the range in which scrolling should
    + *     be disabled and above which scrolling down should be started.
    + * @return {number} The calculated scroll delta.
    + * @protected
    + */
    +goog.fx.DragScrollSupport.prototype.calculateScrollDelta = function(
    +    coordinate, min, rangeLength) {
    +  var delta = 0;
    +  if (coordinate < min) {
    +    delta = -goog.fx.DragScrollSupport.SCROLL_STEP_;
    +  } else if (coordinate > min + rangeLength) {
    +    delta = goog.fx.DragScrollSupport.SCROLL_STEP_;
    +  }
    +  return delta;
    +};
    +
    +
    +/** @override */
    +goog.fx.DragScrollSupport.prototype.disposeInternal = function() {
    +  goog.fx.DragScrollSupport.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.scrollTimer_.dispose();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.html b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.html
    new file mode 100644
    index 0000000000..5ef4c37d4b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.html
    @@ -0,0 +1,63 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: dgajda@google.com (Damian Gajda)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx.DragScrollSupport
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fx.DragScrollSupportTest');
    +  </script>
    +  <style>
    +   #vContainerDiv {
    +  position: absolute;
    +  top: 20px;
    +  overflow-y: scroll;
    +  width: 100px;
    +  height: 100px;
    +  visibility: hidden;
    +}
    +
    +#vContentDiv {
    +  height: 200px;
    +}
    +
    +#hContainerDiv {
    +  position: absolute;
    +  top: 20px;
    +  left: 200px;
    +  overflow-x: scroll;
    +  width: 100px;
    +  height: 100px;
    +  visibility: hidden;
    +}
    +
    +#hContentDiv {
    +  width: 200px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="vContainerDiv">
    +   <div id="vContentDiv">
    +    Sample text
    +   </div>
    +  </div>
    +  <div id="hContainerDiv">
    +   <div id="hContentDiv">
    +    Sample text
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.js b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.js
    new file mode 100644
    index 0000000000..94338fa55a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/dragscrollsupport_test.js
    @@ -0,0 +1,315 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fx.DragScrollSupportTest');
    +goog.setTestOnly('goog.fx.DragScrollSupportTest');
    +
    +goog.require('goog.fx.DragScrollSupport');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +
    +var vContainerDiv;
    +var vContentDiv;
    +var hContainerDiv;
    +var hContentDiv;
    +var clock;
    +
    +function setUpPage() {
    +  vContainerDiv = document.getElementById('vContainerDiv');
    +  vContentDiv = document.getElementById('vContentDiv');
    +  hContainerDiv = document.getElementById('hContainerDiv');
    +  hContentDiv = document.getElementById('hContentDiv');
    +}
    +
    +function setUp() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +
    +function tearDown() {
    +  clock.dispose();
    +}
    +
    +
    +function testDragZeroMarginDivVContainer() {
    +  var dsc = new goog.fx.DragScrollSupport(vContainerDiv);
    +
    +  // Set initial scroll state.
    +  var scrollTop = 50;
    +  vContainerDiv.scrollTop = scrollTop;
    +
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the vContainer should not trigger scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +  assertEquals('Scroll timer should not tick yet', 0, clock.getTimeoutsMade());
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 10));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the vContainer should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the vContainer should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 110));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing below the vContainer should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing below the vContainer should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the vContainer should stop scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +
    +  dsc.dispose();
    +}
    +
    +function testDragZeroMarginDivHContainer() {
    +  var dsc = new goog.fx.DragScrollSupport(hContainerDiv);
    +
    +  // Set initial scroll state.
    +  var scrollLeft = 50;
    +  hContainerDiv.scrollLeft = scrollLeft;
    +
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 + 50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the hContainer should not trigger scrolling.',
    +      scrollLeft, hContainerDiv.scrollLeft);
    +  assertEquals('Scroll timer should not tick yet', 0, clock.getTimeoutsMade());
    +
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 - 10, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing left of the hContainer should trigger scrolling left.',
    +      scrollLeft > hContainerDiv.scrollLeft);
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing left of the hContainer should trigger scrolling left.',
    +      scrollLeft > hContainerDiv.scrollLeft);
    +
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 + 110, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing right of the hContainer should trigger scrolling right.',
    +      scrollLeft < hContainerDiv.scrollLeft);
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing right of the hContainer should trigger scrolling right.',
    +      scrollLeft < hContainerDiv.scrollLeft);
    +
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 + 50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the hContainer should stop scrolling.',
    +      scrollLeft, hContainerDiv.scrollLeft);
    +
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +
    +  dsc.dispose();
    +}
    +
    +
    +function testDragMarginDivVContainer() {
    +  var dsc = new goog.fx.DragScrollSupport(vContainerDiv, 20);
    +
    +  // Set initial scroll state.
    +  var scrollTop = 50;
    +  vContainerDiv.scrollTop = scrollTop;
    +
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the vContainer should not trigger scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +  assertEquals('Scroll timer should not tick yet', 0, clock.getTimeoutsMade());
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 30));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 90));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing below the margin should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the margin should stop scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +
    +  assertEquals('Scroll timer should have ticked 5 times',
    +      5, clock.getTimeoutsMade());
    +
    +  dsc.dispose();
    +}
    +
    +
    +function testDragMarginScrollConstrainedDivVContainer() {
    +  var dsc = new goog.fx.DragScrollSupport(vContainerDiv, 20);
    +  dsc.setConstrainScroll(true);
    +
    +  // Set initial scroll state.
    +  var scrollTop = 50;
    +  vContainerDiv.scrollTop = scrollTop;
    +
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the vContainer should not trigger scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +  assertEquals('Scroll timer should not tick yet', 0, clock.getTimeoutsMade());
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 30));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling up.',
    +      scrollTop > vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 90));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing below the margin should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing above the margin should trigger scrolling down.',
    +      scrollTop < vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing inside the margin should stop scrolling.',
    +      scrollTop, vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 10));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing above the vContainer should not trigger scrolling up.',
    +      scrollTop, vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing above the vContainer should not trigger scrolling up.',
    +      scrollTop, vContainerDiv.scrollTop);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(50, 20 + 110));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals(
    +      'Mousing below the vContainer should not trigger scrolling down.',
    +      scrollTop, vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals(
    +      'Mousing below the vContainer should not trigger scrolling down.',
    +      scrollTop, vContainerDiv.scrollTop);
    +
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +
    +  scrollTop = vContainerDiv.scrollTop;
    +  goog.testing.events.fireMouseMoveEvent(vContainerDiv,
    +      new goog.math.Coordinate(150, 20 + 90));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing to the right of the vContainer should not trigger ' +
    +      'scrolling up.', scrollTop, vContainerDiv.scrollTop);
    +  scrollTop = vContainerDiv.scrollTop;
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Mousing to the right of the vContainer should not trigger ' +
    +      'scrolling up.', scrollTop, vContainerDiv.scrollTop);
    +
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +
    +  assertEquals('Scroll timer should have ticked 5 times',
    +      5, clock.getTimeoutsMade());
    +
    +  dsc.dispose();
    +}
    +
    +
    +function testSetHorizontalScrolling() {
    +  var dsc = new goog.fx.DragScrollSupport(hContainerDiv);
    +  dsc.setHorizontalScrolling(false);
    +
    +  // Set initial scroll state.
    +  var scrollLeft = 50;
    +  hContainerDiv.scrollLeft = scrollLeft;
    +
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 - 10, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Horizontal scrolling should be turned off',
    +      0, clock.getTimeoutsMade());
    +
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 + 110, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertEquals('Horizontal scrolling should be turned off',
    +      0, clock.getTimeoutsMade());
    +
    +  dsc.setHorizontalScrolling(true);
    +  scrollLeft = hContainerDiv.scrollLeft;
    +  goog.testing.events.fireMouseMoveEvent(hContainerDiv,
    +      new goog.math.Coordinate(200 - 10, 20 + 50));
    +  clock.tick(goog.fx.DragScrollSupport.TIMER_STEP_ + 1);
    +  assertTrue('Mousing left of the hContainer should trigger scrolling left.',
    +      scrollLeft > hContainerDiv.scrollLeft);
    +
    +  dsc.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/easing.js b/mapbox-gl/closure-library/closure/goog/fx/easing.js
    new file mode 100644
    index 0000000000..fda5122c8b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/easing.js
    @@ -0,0 +1,85 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Easing functions for animations.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.fx.easing');
    +
    +
    +/**
    + * Ease in - Start slow and speed up.
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + */
    +goog.fx.easing.easeIn = function(t) {
    +  return goog.fx.easing.easeInInternal_(t, 3);
    +};
    +
    +
    +/**
    + * Ease in with specifiable exponent.
    + * @param {number} t Input between 0 and 1.
    + * @param {number} exp Ease exponent.
    + * @return {number} Output between 0 and 1.
    + * @private
    + */
    +goog.fx.easing.easeInInternal_ = function(t, exp) {
    +  return Math.pow(t, exp);
    +};
    +
    +
    +/**
    + * Ease out - Start fastest and slows to a stop.
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + */
    +goog.fx.easing.easeOut = function(t) {
    +  return goog.fx.easing.easeOutInternal_(t, 3);
    +};
    +
    +
    +/**
    + * Ease out with specifiable exponent.
    + * @param {number} t Input between 0 and 1.
    + * @param {number} exp Ease exponent.
    + * @return {number} Output between 0 and 1.
    + * @private
    + */
    +goog.fx.easing.easeOutInternal_ = function(t, exp) {
    +  return 1 - goog.fx.easing.easeInInternal_(1 - t, exp);
    +};
    +
    +
    +/**
    + * Ease out long - Start fastest and slows to a stop with a long ease.
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + */
    +goog.fx.easing.easeOutLong = function(t) {
    +  return goog.fx.easing.easeOutInternal_(t, 4);
    +};
    +
    +
    +/**
    + * Ease in and out - Start slow, speed up, then slow down.
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + */
    +goog.fx.easing.inAndOut = function(t) {
    +  return 3 * t * t - 2 * t * t * t;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/easing_test.js b/mapbox-gl/closure-library/closure/goog/fx/easing_test.js
    new file mode 100644
    index 0000000000..db0ae35dbb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/easing_test.js
    @@ -0,0 +1,33 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.fx.easingTest');
    +goog.setTestOnly('goog.fx.easingTest');
    +
    +goog.require('goog.fx.easing');
    +goog.require('goog.testing.jsunit');
    +
    +
    +function testEaseIn() {
    +  assertEquals(0, goog.fx.easing.easeIn(0));
    +  assertEquals(1, goog.fx.easing.easeIn(1));
    +  assertRoughlyEquals(Math.pow(0.5, 3), goog.fx.easing.easeIn(0.5), 0.01);
    +}
    +
    +function testEaseOut() {
    +  assertEquals(0, goog.fx.easing.easeOut(0));
    +  assertEquals(1, goog.fx.easing.easeOut(1));
    +  assertRoughlyEquals(1 - Math.pow(0.5, 3), goog.fx.easing.easeOut(0.5), 0.01);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/fx.js b/mapbox-gl/closure-library/closure/goog/fx/fx.js
    new file mode 100644
    index 0000000000..f654354377
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/fx.js
    @@ -0,0 +1,32 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Legacy stub for the goog.fx namespace.  Requires the moved
    + * namespaces. Animation and easing have been moved to animation.js and
    + * easing.js.  Users of this stub should move off so we may remove it in the
    + * future.
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.fx');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.Animation.EventType');
    +goog.require('goog.fx.Animation.State');
    +goog.require('goog.fx.AnimationEvent');
    +goog.require('goog.fx.Transition.EventType');
    +goog.require('goog.fx.easing');
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/fx_test.html b/mapbox-gl/closure-library/closure/goog/fx/fx_test.html
    new file mode 100644
    index 0000000000..6974cd3e6a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/fx_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.fx
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.fxTest');
    +  </script>
    +  <style>
    +  </style>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/fx_test.js b/mapbox-gl/closure-library/closure/goog/fx/fx_test.js
    new file mode 100644
    index 0000000000..70e1ea1ee7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/fx_test.js
    @@ -0,0 +1,86 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.fxTest');
    +goog.setTestOnly('goog.fxTest');
    +
    +goog.require('goog.fx.Animation');
    +goog.require('goog.object');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +// TODO(arv): Add tests for the event dispatches.
    +// TODO(arv): Add tests for the calculation of the coordinates.
    +
    +var clock, replacer, anim, anim2;
    +var Animation = goog.fx.Animation;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function setUp() {
    +  replacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  replacer.reset();
    +
    +  if (anim && anim.dispose) {
    +    anim.dispose();
    +  }
    +
    +  if (anim2 && anim2.dispose) {
    +    anim2.dispose();
    +  }
    +}
    +
    +function testAnimationConstructor() {
    +  assertThrows('Should throw since first arg is not an array', function() {
    +    new Animation(1, [2], 3);
    +  });
    +  assertThrows('Should throw since second arg is not an array', function() {
    +    new Animation([1], 2, 3);
    +  });
    +  assertThrows('Should throw since the length are different', function() {
    +    new Animation([0, 1], [2], 3);
    +  });
    +}
    +
    +function testPlayAndStopDoesNotLeaveAnyActiveAnimations() {
    +  anim = new Animation([0], [1], 1000);
    +
    +  assertTrue('There should be no active animations',
    +             goog.object.isEmpty(goog.fx.anim.activeAnimations_));
    +
    +  anim.play();
    +  assertEquals('There should be one active animations',
    +               1, goog.object.getCount(goog.fx.anim.activeAnimations_));
    +
    +  anim.stop();
    +  assertTrue('There should be no active animations',
    +             goog.object.isEmpty(goog.fx.anim.activeAnimations_));
    +
    +  anim.play();
    +  assertEquals('There should be one active animations',
    +               1, goog.object.getCount(goog.fx.anim.activeAnimations_));
    +
    +  anim.pause();
    +  assertTrue('There should be no active animations',
    +             goog.object.isEmpty(goog.fx.anim.activeAnimations_));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/transition.js b/mapbox-gl/closure-library/closure/goog/fx/transition.js
    new file mode 100644
    index 0000000000..57c4304d09
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/transition.js
    @@ -0,0 +1,76 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for transition animation. This is a simple
    + * interface that allows for playing and stopping a transition. It adds
    + * a simple event model with BEGIN and END event.
    + *
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.fx.Transition');
    +goog.provide('goog.fx.Transition.EventType');
    +
    +
    +
    +/**
    + * An interface for programmatic transition. Must extend
    + * {@code goog.events.EventTarget}.
    + * @interface
    + */
    +goog.fx.Transition = function() {};
    +
    +
    +/**
    + * Transition event types.
    + * @enum {string}
    + */
    +goog.fx.Transition.EventType = {
    +  /** Dispatched when played for the first time OR when it is resumed. */
    +  PLAY: 'play',
    +
    +  /** Dispatched only when the animation starts from the beginning. */
    +  BEGIN: 'begin',
    +
    +  /** Dispatched only when animation is restarted after a pause. */
    +  RESUME: 'resume',
    +
    +  /**
    +   * Dispatched when animation comes to the end of its duration OR stop
    +   * is called.
    +   */
    +  END: 'end',
    +
    +  /** Dispatched only when stop is called. */
    +  STOP: 'stop',
    +
    +  /** Dispatched only when animation comes to its end naturally. */
    +  FINISH: 'finish',
    +
    +  /** Dispatched when an animation is paused. */
    +  PAUSE: 'pause'
    +};
    +
    +
    +/**
    + * Plays the transition.
    + */
    +goog.fx.Transition.prototype.play;
    +
    +
    +/**
    + * Stops the transition.
    + */
    +goog.fx.Transition.prototype.stop;
    diff --git a/mapbox-gl/closure-library/closure/goog/fx/transitionbase.js b/mapbox-gl/closure-library/closure/goog/fx/transitionbase.js
    new file mode 100644
    index 0000000000..0a2c184e6e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/fx/transitionbase.js
    @@ -0,0 +1,236 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An abstract base class for transitions. This is a simple
    + * interface that allows for playing, pausing and stopping an animation. It adds
    + * a simple event model, and animation status.
    + */
    +goog.provide('goog.fx.TransitionBase');
    +goog.provide('goog.fx.TransitionBase.State');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fx.Transition');  // Unreferenced: interface
    +
    +
    +
    +/**
    + * Constructor for a transition object.
    + *
    + * @constructor
    + * @implements {goog.fx.Transition}
    + * @extends {goog.events.EventTarget}
    + */
    +goog.fx.TransitionBase = function() {
    +  goog.fx.TransitionBase.base(this, 'constructor');
    +
    +  /**
    +   * The internal state of the animation.
    +   * @type {goog.fx.TransitionBase.State}
    +   * @private
    +   */
    +  this.state_ = goog.fx.TransitionBase.State.STOPPED;
    +
    +  /**
    +   * Timestamp for when the animation was started.
    +   * @type {?number}
    +   * @protected
    +   */
    +  this.startTime = null;
    +
    +  /**
    +   * Timestamp for when the animation finished or was stopped.
    +   * @type {?number}
    +   * @protected
    +   */
    +  this.endTime = null;
    +};
    +goog.inherits(goog.fx.TransitionBase, goog.events.EventTarget);
    +
    +
    +/**
    + * Enum for the possible states of an animation.
    + * @enum {number}
    + */
    +goog.fx.TransitionBase.State = {
    +  STOPPED: 0,
    +  PAUSED: -1,
    +  PLAYING: 1
    +};
    +
    +
    +/**
    + * Plays the animation.
    + *
    + * @param {boolean=} opt_restart Optional parameter to restart the animation.
    + * @return {boolean} True iff the animation was started.
    + * @override
    + */
    +goog.fx.TransitionBase.prototype.play = goog.abstractMethod;
    +
    +
    +/**
    + * Stops the animation.
    + *
    + * @param {boolean=} opt_gotoEnd Optional boolean parameter to go the the end of
    + *     the animation.
    + * @override
    + */
    +goog.fx.TransitionBase.prototype.stop = goog.abstractMethod;
    +
    +
    +/**
    + * Pauses the animation.
    + */
    +goog.fx.TransitionBase.prototype.pause = goog.abstractMethod;
    +
    +
    +/**
    + * Returns the current state of the animation.
    + * @return {goog.fx.TransitionBase.State} State of the animation.
    + */
    +goog.fx.TransitionBase.prototype.getStateInternal = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * Sets the current state of the animation to playing.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.setStatePlaying = function() {
    +  this.state_ = goog.fx.TransitionBase.State.PLAYING;
    +};
    +
    +
    +/**
    + * Sets the current state of the animation to paused.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.setStatePaused = function() {
    +  this.state_ = goog.fx.TransitionBase.State.PAUSED;
    +};
    +
    +
    +/**
    + * Sets the current state of the animation to stopped.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.setStateStopped = function() {
    +  this.state_ = goog.fx.TransitionBase.State.STOPPED;
    +};
    +
    +
    +/**
    + * @return {boolean} True iff the current state of the animation is playing.
    + */
    +goog.fx.TransitionBase.prototype.isPlaying = function() {
    +  return this.state_ == goog.fx.TransitionBase.State.PLAYING;
    +};
    +
    +
    +/**
    + * @return {boolean} True iff the current state of the animation is paused.
    + */
    +goog.fx.TransitionBase.prototype.isPaused = function() {
    +  return this.state_ == goog.fx.TransitionBase.State.PAUSED;
    +};
    +
    +
    +/**
    + * @return {boolean} True iff the current state of the animation is stopped.
    + */
    +goog.fx.TransitionBase.prototype.isStopped = function() {
    +  return this.state_ == goog.fx.TransitionBase.State.STOPPED;
    +};
    +
    +
    +/**
    + * Dispatches the BEGIN event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onBegin = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.BEGIN);
    +};
    +
    +
    +/**
    + * Dispatches the END event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onEnd = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.END);
    +};
    +
    +
    +/**
    + * Dispatches the FINISH event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onFinish = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.FINISH);
    +};
    +
    +
    +/**
    + * Dispatches the PAUSE event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onPause = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.PAUSE);
    +};
    +
    +
    +/**
    + * Dispatches the PLAY event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onPlay = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.PLAY);
    +};
    +
    +
    +/**
    + * Dispatches the RESUME event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onResume = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.RESUME);
    +};
    +
    +
    +/**
    + * Dispatches the STOP event. Sub classes should override this instead
    + * of listening to the event, and call this instead of dispatching the event.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.onStop = function() {
    +  this.dispatchAnimationEvent(goog.fx.Transition.EventType.STOP);
    +};
    +
    +
    +/**
    + * Dispatches an event object for the current animation.
    + * @param {string} type Event type that will be dispatched.
    + * @protected
    + */
    +goog.fx.TransitionBase.prototype.dispatchAnimationEvent = function(type) {
    +  this.dispatchEvent(type);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/abstractgraphics.js b/mapbox-gl/closure-library/closure/goog/graphics/abstractgraphics.js
    new file mode 100644
    index 0000000000..0ae1776839
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/abstractgraphics.js
    @@ -0,0 +1,454 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Graphics utility functions and factory methods.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.AbstractGraphics');
    +
    +goog.require('goog.dom');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Base class for the different graphics. You should never construct objects
    + * of this class. Instead us goog.graphics.createGraphics
    + * @param {number|string} width The width in pixels or percent.
    + * @param {number|string} height The height in pixels or percent.
    + * @param {?number=} opt_coordWidth Optional coordinate system width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight Optional coordinate system height - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.graphics.AbstractGraphics = function(width, height,
    +                                          opt_coordWidth, opt_coordHeight,
    +                                          opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Width of graphics in pixels or percentage points.
    +   * @type {number|string}
    +   * @protected
    +   */
    +  this.width = width;
    +
    +  /**
    +   * Height of graphics in pixels or precentage points.
    +   * @type {number|string}
    +   * @protected
    +   */
    +  this.height = height;
    +
    +  /**
    +   * Width of coordinate system in units.
    +   * @type {?number}
    +   * @protected
    +   */
    +  this.coordWidth = opt_coordWidth || null;
    +
    +  /**
    +   * Height of coordinate system in units.
    +   * @type {?number}
    +   * @protected
    +   */
    +  this.coordHeight = opt_coordHeight || null;
    +};
    +goog.inherits(goog.graphics.AbstractGraphics, goog.ui.Component);
    +
    +
    +/**
    + * The root level group element.
    + * @type {goog.graphics.GroupElement?}
    + * @protected
    + */
    +goog.graphics.AbstractGraphics.prototype.canvasElement = null;
    +
    +
    +/**
    + * Left coordinate of the view box
    + * @type {number}
    + * @protected
    + */
    +goog.graphics.AbstractGraphics.prototype.coordLeft = 0;
    +
    +
    +/**
    + * Top coordinate of the view box
    + * @type {number}
    + * @protected
    + */
    +goog.graphics.AbstractGraphics.prototype.coordTop = 0;
    +
    +
    +/**
    + * @return {goog.graphics.GroupElement} The root level canvas element.
    + */
    +goog.graphics.AbstractGraphics.prototype.getCanvasElement = function() {
    +  return this.canvasElement;
    +};
    +
    +
    +/**
    + * Changes the coordinate size.
    + * @param {number} coordWidth  The coordinate width.
    + * @param {number} coordHeight  The coordinate height.
    + */
    +goog.graphics.AbstractGraphics.prototype.setCoordSize = function(coordWidth,
    +                                                                 coordHeight) {
    +  this.coordWidth = coordWidth;
    +  this.coordHeight = coordHeight;
    +};
    +
    +
    +/**
    + * @return {goog.math.Size} The coordinate size.
    + */
    +goog.graphics.AbstractGraphics.prototype.getCoordSize = function() {
    +  if (this.coordWidth) {
    +    return new goog.math.Size(this.coordWidth,
    +        /** @type {number} */ (this.coordHeight));
    +  } else {
    +    return this.getPixelSize();
    +  }
    +};
    +
    +
    +/**
    + * Changes the coordinate system position.
    + * @param {number} left  The coordinate system left bound.
    + * @param {number} top  The coordinate system top bound.
    + */
    +goog.graphics.AbstractGraphics.prototype.setCoordOrigin = goog.abstractMethod;
    +
    +
    +/**
    + * @return {!goog.math.Coordinate} The coordinate system position.
    + */
    +goog.graphics.AbstractGraphics.prototype.getCoordOrigin = function() {
    +  return new goog.math.Coordinate(this.coordLeft, this.coordTop);
    +};
    +
    +
    +/**
    + * Change the size of the canvas.
    + * @param {number} pixelWidth  The width in pixels.
    + * @param {number} pixelHeight  The height in pixels.
    + */
    +goog.graphics.AbstractGraphics.prototype.setSize = goog.abstractMethod;
    +
    +
    +/**
    + * @return {goog.math.Size} The size of canvas.
    + * @deprecated Use getPixelSize.
    + */
    +goog.graphics.AbstractGraphics.prototype.getSize = function() {
    +  return this.getPixelSize();
    +};
    +
    +
    +/**
    + * @return {goog.math.Size?} Returns the number of pixels spanned by the
    + *     surface, or null if the size could not be computed due to the size being
    + *     specified in percentage points and the component not being in the
    + *     document.
    + */
    +goog.graphics.AbstractGraphics.prototype.getPixelSize = function() {
    +  if (this.isInDocument()) {
    +    return goog.style.getSize(this.getElement());
    +  }
    +  if (goog.isNumber(this.width) && goog.isNumber(this.height)) {
    +    return new goog.math.Size(this.width, this.height);
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the x direction.
    + */
    +goog.graphics.AbstractGraphics.prototype.getPixelScaleX = function() {
    +  var pixelSize = this.getPixelSize();
    +  return pixelSize ? pixelSize.width / this.getCoordSize().width : 0;
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the y direction.
    + */
    +goog.graphics.AbstractGraphics.prototype.getPixelScaleY = function() {
    +  var pixelSize = this.getPixelSize();
    +  return pixelSize ? pixelSize.height / this.getCoordSize().height : 0;
    +};
    +
    +
    +/**
    + * Remove all drawing elements from the graphics.
    + */
    +goog.graphics.AbstractGraphics.prototype.clear = goog.abstractMethod;
    +
    +
    +/**
    + * Remove a single drawing element from the surface.  The default implementation
    + * assumes a DOM based drawing surface.
    + * @param {goog.graphics.Element} element The element to remove.
    + */
    +goog.graphics.AbstractGraphics.prototype.removeElement = function(element) {
    +  goog.dom.removeNode(element.getElement());
    +};
    +
    +
    +/**
    + * Sets the fill for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Fill?} fill The fill object.
    + */
    +goog.graphics.AbstractGraphics.prototype.setElementFill = goog.abstractMethod;
    +
    +
    +/**
    + * Sets the stroke for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Stroke?} stroke The stroke object.
    + */
    +goog.graphics.AbstractGraphics.prototype.setElementStroke = goog.abstractMethod;
    +
    +
    +/**
    + * Set the transformation of an element.
    + *
    + * If a more general affine transform is needed than this provides
    + * (e.g. skew and scale) then use setElementAffineTransform.
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {number} x The x coordinate of the translation transform.
    + * @param {number} y The y coordinate of the translation transform.
    + * @param {number} angle The angle of the rotation transform.
    + * @param {number} centerX The horizontal center of the rotation transform.
    + * @param {number} centerY The vertical center of the rotation transform.
    + */
    +goog.graphics.AbstractGraphics.prototype.setElementTransform =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * Set the affine transform of an element.
    + * @param {!goog.graphics.Element} element The element wrapper.
    + * @param {!goog.graphics.AffineTransform} affineTransform The
    + *     transformation applied to this element.
    + */
    +goog.graphics.AbstractGraphics.prototype.setElementAffineTransform =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * Draw a circle
    + *
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} r Radius length.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.EllipseElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawCircle = function(
    +    cx, cy, r, stroke, fill, opt_group) {
    +  return this.drawEllipse(cx, cy, r, r, stroke, fill, opt_group);
    +};
    +
    +
    +/**
    + * Draw an ellipse
    + *
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.EllipseElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawEllipse = goog.abstractMethod;
    +
    +
    +/**
    + * Draw a rectangle
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.RectElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawRect = goog.abstractMethod;
    +
    +
    +/**
    + * Draw a text string within a rectangle (drawing is horizontal)
    + *
    + * @param {string} text The text to draw.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @param {string} align Horizontal alignment: left (default), center, right.
    + * @param {string} vAlign Vertical alignment: top (default), center, bottom.
    + * @param {goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill  Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.TextElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawText = function(
    +    text, x, y, width, height, align, vAlign, font, stroke, fill, opt_group) {
    +  var baseline = font.size / 2; // Baseline is middle of line
    +  var textY;
    +  if (vAlign == 'bottom') {
    +    textY = y + height - baseline;
    +  } else if (vAlign == 'center') {
    +    textY = y + height / 2;
    +  } else {
    +    textY = y + baseline;
    +  }
    +
    +  return this.drawTextOnLine(text, x, textY, x + width, textY, align,
    +      font, stroke, fill, opt_group);
    +};
    +
    +
    +/**
    + * Draw a text string vertically centered on a given line.
    + *
    + * @param {string} text  The text to draw.
    + * @param {number} x1 X coordinate of start of line.
    + * @param {number} y1 Y coordinate of start of line.
    + * @param {number} x2 X coordinate of end of line.
    + * @param {number} y2 Y coordinate of end of line.
    + * @param {string} align Horizontal alingnment: left (default), center, right.
    + * @param {goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.TextElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawTextOnLine = goog.abstractMethod;
    +
    +
    +/**
    + * Draw a path.
    + *
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.PathElement} The newly created element.
    + */
    +goog.graphics.AbstractGraphics.prototype.drawPath = goog.abstractMethod;
    +
    +
    +/**
    + * Create an empty group of drawing elements.
    + *
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element to
    + *     append to. If not specified, appends to the main canvas.
    + *
    + * @return {goog.graphics.GroupElement} The newly created group.
    + */
    +goog.graphics.AbstractGraphics.prototype.createGroup = goog.abstractMethod;
    +
    +
    +/**
    + * Create an empty path.
    + *
    + * @return {!goog.graphics.Path} The path.
    + * @deprecated Use {@code new goog.graphics.Path()}.
    + */
    +goog.graphics.AbstractGraphics.prototype.createPath = function() {
    +  return new goog.graphics.Path();
    +};
    +
    +
    +/**
    + * Measure and return the width (in pixels) of a given text string.
    + * Text measurement is needed to make sure a text can fit in the allocated
    + * area. The way text length is measured is by writing it into a div that is
    + * after the visible area, measure the div width, and immediatly erase the
    + * written value.
    + *
    + * @param {string} text The text string to measure.
    + * @param {goog.graphics.Font} font The font object describing the font style.
    + *
    + * @return {number} The width in pixels of the text strings.
    + */
    +goog.graphics.AbstractGraphics.prototype.getTextWidth = goog.abstractMethod;
    +
    +
    +/**
    + * @return {boolean} Whether the underlying element can be cloned resulting in
    + *     an accurate reproduction of the graphics contents.
    + */
    +goog.graphics.AbstractGraphics.prototype.isDomClonable = function() {
    +  return false;
    +};
    +
    +
    +/**
    + * Start preventing redraws - useful for chaining large numbers of changes
    + * together.  Not guaranteed to do anything - i.e. only use this for
    + * optimization of a single code path.
    + */
    +goog.graphics.AbstractGraphics.prototype.suspend = function() {
    +};
    +
    +
    +/**
    + * Stop preventing redraws.  If any redraws had been prevented, a redraw will
    + * be done now.
    + */
    +goog.graphics.AbstractGraphics.prototype.resume = function() {
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/affinetransform.js b/mapbox-gl/closure-library/closure/goog/graphics/affinetransform.js
    new file mode 100644
    index 0000000000..ec328f2892
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/affinetransform.js
    @@ -0,0 +1,588 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Provides an object representation of an AffineTransform and
    + * methods for working with it.
    + */
    +
    +
    +goog.provide('goog.graphics.AffineTransform');
    +
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Creates a 2D affine transform. An affine transform performs a linear
    + * mapping from 2D coordinates to other 2D coordinates that preserves the
    + * "straightness" and "parallelness" of lines.
    + *
    + * Such a coordinate transformation can be represented by a 3 row by 3 column
    + * matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source
    + * coordinates (x,y) into destination coordinates (x',y') by considering them
    + * to be a column vector and multiplying the coordinate vector by the matrix
    + * according to the following process:
    + * <pre>
    + *      [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
    + *      [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
    + *      [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]
    + * </pre>
    + *
    + * This class is optimized for speed and minimizes calculations based on its
    + * knowledge of the underlying matrix (as opposed to say simply performing
    + * matrix multiplication).
    + *
    + * @param {number=} opt_m00 The m00 coordinate of the transform.
    + * @param {number=} opt_m10 The m10 coordinate of the transform.
    + * @param {number=} opt_m01 The m01 coordinate of the transform.
    + * @param {number=} opt_m11 The m11 coordinate of the transform.
    + * @param {number=} opt_m02 The m02 coordinate of the transform.
    + * @param {number=} opt_m12 The m12 coordinate of the transform.
    + * @constructor
    + * @final
    + */
    +goog.graphics.AffineTransform = function(opt_m00, opt_m10, opt_m01,
    +    opt_m11, opt_m02, opt_m12) {
    +  if (arguments.length == 6) {
    +    this.setTransform(/** @type {number} */ (opt_m00),
    +                      /** @type {number} */ (opt_m10),
    +                      /** @type {number} */ (opt_m01),
    +                      /** @type {number} */ (opt_m11),
    +                      /** @type {number} */ (opt_m02),
    +                      /** @type {number} */ (opt_m12));
    +  } else if (arguments.length != 0) {
    +    throw Error('Insufficient matrix parameters');
    +  } else {
    +    this.m00_ = this.m11_ = 1;
    +    this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this transform is the identity transform.
    + */
    +goog.graphics.AffineTransform.prototype.isIdentity = function() {
    +  return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 &&
    +      this.m11_ == 1 && this.m02_ == 0 && this.m12_ == 0;
    +};
    +
    +
    +/**
    + * @return {!goog.graphics.AffineTransform} A copy of this transform.
    + */
    +goog.graphics.AffineTransform.prototype.clone = function() {
    +  return new goog.graphics.AffineTransform(this.m00_, this.m10_, this.m01_,
    +      this.m11_, this.m02_, this.m12_);
    +};
    +
    +
    +/**
    + * Sets this transform to the matrix specified by the 6 values.
    + *
    + * @param {number} m00 The m00 coordinate of the transform.
    + * @param {number} m10 The m10 coordinate of the transform.
    + * @param {number} m01 The m01 coordinate of the transform.
    + * @param {number} m11 The m11 coordinate of the transform.
    + * @param {number} m02 The m02 coordinate of the transform.
    + * @param {number} m12 The m12 coordinate of the transform.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.setTransform = function(m00, m10, m01,
    +    m11, m02, m12) {
    +  if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||
    +      !goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {
    +    throw Error('Invalid transform parameters');
    +  }
    +  this.m00_ = m00;
    +  this.m10_ = m10;
    +  this.m01_ = m01;
    +  this.m11_ = m11;
    +  this.m02_ = m02;
    +  this.m12_ = m12;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets this transform to be identical to the given transform.
    + *
    + * @param {!goog.graphics.AffineTransform} tx The transform to copy.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.copyFrom = function(tx) {
    +  this.m00_ = tx.m00_;
    +  this.m10_ = tx.m10_;
    +  this.m01_ = tx.m01_;
    +  this.m11_ = tx.m11_;
    +  this.m02_ = tx.m02_;
    +  this.m12_ = tx.m12_;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.scale = function(sx, sy) {
    +  this.m00_ *= sx;
    +  this.m10_ *= sx;
    +  this.m01_ *= sy;
    +  this.m11_ *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a scaling transformation,
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [sx  0 0] [m00 m01 m02]
    + * [ 0 sy 0] [m10 m11 m12]
    + * [ 0  0 1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.preScale = function(sx, sy) {
    +  this.m00_ *= sx;
    +  this.m01_ *= sx;
    +  this.m02_ *= sx;
    +  this.m10_ *= sy;
    +  this.m11_ *= sy;
    +  this.m12_ *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a translate transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.translate = function(dx, dy) {
    +  this.m02_ += dx * this.m00_ + dy * this.m01_;
    +  this.m12_ += dx * this.m10_ + dy * this.m11_;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a translate transformation,
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [1 0 dx] [m00 m01 m02]
    + * [0 1 dy] [m10 m11 m12]
    + * [0 0  1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.preTranslate = function(dx, dy) {
    +  this.m02_ += dx;
    +  this.m12_ += dy;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a rotation transformation around an anchor
    + * point.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.rotate = function(theta, x, y) {
    +  return this.concatenate(
    +      goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a rotation transformation around an
    + * anchor point.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.preRotate = function(theta, x, y) {
    +  return this.preConcatenate(
    +      goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
    +};
    +
    +
    +/**
    + * Concatenates this transform with a shear transformation.
    + *
    + * @param {number} shx The x shear factor.
    + * @param {number} shy The y shear factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.shear = function(shx, shy) {
    +  var m00 = this.m00_;
    +  var m10 = this.m10_;
    +  this.m00_ += shy * this.m01_;
    +  this.m10_ += shy * this.m11_;
    +  this.m01_ += shx * m00;
    +  this.m11_ += shx * m10;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a shear transformation.
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [  1 shx 0] [m00 m01 m02]
    + * [shy   1 0] [m10 m11 m12]
    + * [  0   0 1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} shx The x shear factor.
    + * @param {number} shy The y shear factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.preShear = function(shx, shy) {
    +  var m00 = this.m00_;
    +  var m01 = this.m01_;
    +  var m02 = this.m02_;
    +  this.m00_ += shx * this.m10_;
    +  this.m01_ += shx * this.m11_;
    +  this.m02_ += shx * this.m12_;
    +  this.m10_ += shy * m00;
    +  this.m11_ += shy * m01;
    +  this.m12_ += shy * m02;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {string} A string representation of this transform. The format of
    + *     of the string is compatible with SVG matrix notation, i.e.
    + *     "matrix(a,b,c,d,e,f)".
    + * @override
    + */
    +goog.graphics.AffineTransform.prototype.toString = function() {
    +  return 'matrix(' +
    +      [this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(
    +          ',') +
    +      ')';
    +};
    +
    +
    +/**
    + * @return {number} The scaling factor in the x-direction (m00).
    + */
    +goog.graphics.AffineTransform.prototype.getScaleX = function() {
    +  return this.m00_;
    +};
    +
    +
    +/**
    + * @return {number} The scaling factor in the y-direction (m11).
    + */
    +goog.graphics.AffineTransform.prototype.getScaleY = function() {
    +  return this.m11_;
    +};
    +
    +
    +/**
    + * @return {number} The translation in the x-direction (m02).
    + */
    +goog.graphics.AffineTransform.prototype.getTranslateX = function() {
    +  return this.m02_;
    +};
    +
    +
    +/**
    + * @return {number} The translation in the y-direction (m12).
    + */
    +goog.graphics.AffineTransform.prototype.getTranslateY = function() {
    +  return this.m12_;
    +};
    +
    +
    +/**
    + * @return {number} The shear factor in the x-direction (m01).
    + */
    +goog.graphics.AffineTransform.prototype.getShearX = function() {
    +  return this.m01_;
    +};
    +
    +
    +/**
    + * @return {number} The shear factor in the y-direction (m10).
    + */
    +goog.graphics.AffineTransform.prototype.getShearY = function() {
    +  return this.m10_;
    +};
    +
    +
    +/**
    + * Concatenates an affine transform to this transform.
    + *
    + * @param {!goog.graphics.AffineTransform} tx The transform to concatenate.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.concatenate = function(tx) {
    +  var m0 = this.m00_;
    +  var m1 = this.m01_;
    +  this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;
    +  this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;
    +  this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;
    +
    +  m0 = this.m10_;
    +  m1 = this.m11_;
    +  this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;
    +  this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;
    +  this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates an affine transform to this transform.
    + *
    + * @param {!goog.graphics.AffineTransform} tx The transform to preconcatenate.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.preConcatenate = function(tx) {
    +  var m0 = this.m00_;
    +  var m1 = this.m10_;
    +  this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;
    +  this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;
    +
    +  m0 = this.m01_;
    +  m1 = this.m11_;
    +  this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;
    +  this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;
    +
    +  m0 = this.m02_;
    +  m1 = this.m12_;
    +  this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;
    +  this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;
    +  return this;
    +};
    +
    +
    +/**
    + * Transforms an array of coordinates by this transform and stores the result
    + * into a destination array.
    + *
    + * @param {!Array<number>} src The array containing the source points
    + *     as x, y value pairs.
    + * @param {number} srcOff The offset to the first point to be transformed.
    + * @param {!Array<number>} dst The array into which to store the transformed
    + *     point pairs.
    + * @param {number} dstOff The offset of the location of the first transformed
    + *     point in the destination array.
    + * @param {number} numPts The number of points to tranform.
    + */
    +goog.graphics.AffineTransform.prototype.transform = function(src, srcOff, dst,
    +    dstOff, numPts) {
    +  var i = srcOff;
    +  var j = dstOff;
    +  var srcEnd = srcOff + 2 * numPts;
    +  while (i < srcEnd) {
    +    var x = src[i++];
    +    var y = src[i++];
    +    dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;
    +    dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The determinant of this transform.
    + */
    +goog.graphics.AffineTransform.prototype.getDeterminant = function() {
    +  return this.m00_ * this.m11_ - this.m01_ * this.m10_;
    +};
    +
    +
    +/**
    + * Returns whether the transform is invertible. A transform is not invertible
    + * if the determinant is 0 or any value is non-finite or NaN.
    + *
    + * @return {boolean} Whether the transform is invertible.
    + */
    +goog.graphics.AffineTransform.prototype.isInvertible = function() {
    +  var det = this.getDeterminant();
    +  return goog.math.isFiniteNumber(det) &&
    +      goog.math.isFiniteNumber(this.m02_) &&
    +      goog.math.isFiniteNumber(this.m12_) &&
    +      det != 0;
    +};
    +
    +
    +/**
    + * @return {!goog.graphics.AffineTransform} An AffineTransform object
    + *     representing the inverse transformation.
    + */
    +goog.graphics.AffineTransform.prototype.createInverse = function() {
    +  var det = this.getDeterminant();
    +  return new goog.graphics.AffineTransform(
    +      this.m11_ / det,
    +      -this.m10_ / det,
    +      -this.m01_ / det,
    +      this.m00_ / det,
    +      (this.m01_ * this.m12_ - this.m11_ * this.m02_) / det,
    +      (this.m10_ * this.m02_ - this.m00_ * this.m12_) / det);
    +};
    +
    +
    +/**
    + * Creates a transform representing a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.graphics.AffineTransform} A transform representing a scaling
    + *     transformation.
    + */
    +goog.graphics.AffineTransform.getScaleInstance = function(sx, sy) {
    +  return new goog.graphics.AffineTransform().setToScale(sx, sy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a translation transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.graphics.AffineTransform} A transform representing a
    + *     translation transformation.
    + */
    +goog.graphics.AffineTransform.getTranslateInstance = function(dx, dy) {
    +  return new goog.graphics.AffineTransform().setToTranslation(dx, dy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a shearing transformation.
    + *
    + * @param {number} shx The x-axis shear factor.
    + * @param {number} shy The y-axis shear factor.
    + * @return {!goog.graphics.AffineTransform} A transform representing a shearing
    + *     transformation.
    + */
    +goog.graphics.AffineTransform.getShearInstance = function(shx, shy) {
    +  return new goog.graphics.AffineTransform().setToShear(shx, shy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a rotation transformation.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.graphics.AffineTransform} A transform representing a rotation
    + *     transformation.
    + */
    +goog.graphics.AffineTransform.getRotateInstance = function(theta, x, y) {
    +  return new goog.graphics.AffineTransform().setToRotation(theta, x, y);
    +};
    +
    +
    +/**
    + * Sets this transform to a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.setToScale = function(sx, sy) {
    +  return this.setTransform(sx, 0, 0, sy, 0, 0);
    +};
    +
    +
    +/**
    + * Sets this transform to a translation transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.setToTranslation = function(dx, dy) {
    +  return this.setTransform(1, 0, 0, 1, dx, dy);
    +};
    +
    +
    +/**
    + * Sets this transform to a shearing transformation.
    + *
    + * @param {number} shx The x-axis shear factor.
    + * @param {number} shy The y-axis shear factor.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.setToShear = function(shx, shy) {
    +  return this.setTransform(1, shy, shx, 1, 0, 0);
    +};
    +
    +
    +/**
    + * Sets this transform to a rotation transformation.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.graphics.AffineTransform} This affine transform.
    + */
    +goog.graphics.AffineTransform.prototype.setToRotation = function(theta, x, y) {
    +  var cos = Math.cos(theta);
    +  var sin = Math.sin(theta);
    +  return this.setTransform(cos, sin, -sin, cos,
    +      x - x * cos + y * sin, y - x * sin - y * cos);
    +};
    +
    +
    +/**
    + * Compares two affine transforms for equality.
    + *
    + * @param {goog.graphics.AffineTransform} tx The other affine transform.
    + * @return {boolean} whether the two transforms are equal.
    + */
    +goog.graphics.AffineTransform.prototype.equals = function(tx) {
    +  if (this == tx) {
    +    return true;
    +  }
    +  if (!tx) {
    +    return false;
    +  }
    +  return this.m00_ == tx.m00_ &&
    +      this.m01_ == tx.m01_ &&
    +      this.m02_ == tx.m02_ &&
    +      this.m10_ == tx.m10_ &&
    +      this.m11_ == tx.m11_ &&
    +      this.m12_ == tx.m12_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/affinetransform_test.html b/mapbox-gl/closure-library/closure/goog/graphics/affinetransform_test.html
    new file mode 100644
    index 0000000000..79e7b12406
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/affinetransform_test.html
    @@ -0,0 +1,360 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.AffineTransform</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.graphics');
    +  goog.require('goog.graphics.AffineTransform');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +
    +<script>
    +  function testGetTranslateInstance() {
    +    var tx = goog.graphics.AffineTransform.getTranslateInstance(2, 4);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(0, tx.getShearY());
    +    assertEquals(0, tx.getShearX());
    +    assertEquals(1, tx.getScaleY());
    +    assertEquals(2, tx.getTranslateX());
    +    assertEquals(4, tx.getTranslateY());
    +  }
    +
    +  function testGetScaleInstance() {
    +    var tx = goog.graphics.AffineTransform.getScaleInstance(2, 4);
    +    assertEquals(2, tx.getScaleX());
    +    assertEquals(0, tx.getShearY());
    +    assertEquals(0, tx.getShearX());
    +    assertEquals(4, tx.getScaleY());
    +    assertEquals(0, tx.getTranslateX());
    +    assertEquals(0, tx.getTranslateY());
    +  }
    +
    +  function testGetRotateInstance() {
    +    var tx = goog.graphics.AffineTransform.getRotateInstance(Math.PI / 2, 1, 2);
    +    assertRoughlyEquals(0, tx.getScaleX(), 1e-9);
    +    assertRoughlyEquals(1, tx.getShearY(), 1e-9);
    +    assertRoughlyEquals(-1, tx.getShearX(), 1e-9);
    +    assertRoughlyEquals(0, tx.getScaleY(), 1e-9);
    +    assertRoughlyEquals(3, tx.getTranslateX(), 1e-9);
    +    assertRoughlyEquals(1, tx.getTranslateY(), 1e-9);
    +  }
    +
    +  function testGetShearInstance() {
    +    var tx = goog.graphics.AffineTransform.getShearInstance(2, 4);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(4, tx.getShearY());
    +    assertEquals(2, tx.getShearX());
    +    assertEquals(1, tx.getScaleY());
    +    assertEquals(0, tx.getTranslateX());
    +    assertEquals(0, tx.getTranslateY());
    +  }
    +
    +  function testConstructor() {
    +    assertThrows(function() {
    +      new goog.graphics.AffineTransform([0, 0]);
    +    });
    +    assertThrows(function() {
    +      new goog.graphics.AffineTransform({});
    +    });
    +    assertThrows(function() {
    +      new goog.graphics.AffineTransform(0, 0, 0, 'a', 0, 0);
    +    });
    +
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(2, tx.getShearY());
    +    assertEquals(3, tx.getShearX());
    +    assertEquals(4, tx.getScaleY());
    +    assertEquals(5, tx.getTranslateX());
    +    assertEquals(6, tx.getTranslateY());
    +
    +    tx = new goog.graphics.AffineTransform();
    +    assert(tx.isIdentity());
    +  }
    +
    +  function testIsIdentity() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    assertFalse(tx.isIdentity());
    +    tx.setTransform(1, 0, 0, 1, 0, 0);
    +    assert(tx.isIdentity());
    +  }
    +
    +  function testClone() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    var copy = tx.clone();
    +    assertEquals(copy.getScaleX(), tx.getScaleX());
    +    assertEquals(copy.getShearY(), tx.getShearY());
    +    assertEquals(copy.getShearX(), tx.getShearX());
    +    assertEquals(copy.getScaleY(), tx.getScaleY());
    +    assertEquals(copy.getTranslateX(), tx.getTranslateX());
    +    assertEquals(copy.getTranslateY(), tx.getTranslateY());
    +  }
    +
    +  function testSetTransform() {
    +    var tx = new goog.graphics.AffineTransform();
    +    assertThrows(function() {
    +      tx.setTransform(1, 2, 3, 4, 6);
    +    });
    +    assertThrows(function() {
    +      tx.setTransform('a', 2, 3, 4, 5, 6);
    +    });
    +
    +    tx.setTransform(1, 2, 3, 4, 5, 6);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(2, tx.getShearY());
    +    assertEquals(3, tx.getShearX());
    +    assertEquals(4, tx.getScaleY());
    +    assertEquals(5, tx.getTranslateX());
    +    assertEquals(6, tx.getTranslateY());
    +  }
    +
    +  function testScale() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.scale(2, 3);
    +    assertEquals(2, tx.getScaleX());
    +    assertEquals(4, tx.getShearY());
    +    assertEquals(9, tx.getShearX());
    +    assertEquals(12, tx.getScaleY());
    +    assertEquals(5, tx.getTranslateX());
    +    assertEquals(6, tx.getTranslateY());
    +  }
    +
    +  function testPreScale() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.preScale(2, 3);
    +    assertEquals(2, tx.getScaleX());
    +    assertEquals(6, tx.getShearY());
    +    assertEquals(6, tx.getShearX());
    +    assertEquals(12, tx.getScaleY());
    +    assertEquals(10, tx.getTranslateX());
    +    assertEquals(18, tx.getTranslateY());
    +  }
    +
    +  function testTranslate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.translate(2, 3);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(2, tx.getShearY());
    +    assertEquals(3, tx.getShearX());
    +    assertEquals(4, tx.getScaleY());
    +    assertEquals(16, tx.getTranslateX());
    +    assertEquals(22, tx.getTranslateY());
    +  }
    +
    +  function testPreTranslate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.preTranslate(2, 3);
    +    assertEquals(1, tx.getScaleX());
    +    assertEquals(2, tx.getShearY());
    +    assertEquals(3, tx.getShearX());
    +    assertEquals(4, tx.getScaleY());
    +    assertEquals(7, tx.getTranslateX());
    +    assertEquals(9, tx.getTranslateY());
    +  }
    +
    +  function testRotate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.rotate(Math.PI / 2, 1, 1);
    +    assertRoughlyEquals(3, tx.getScaleX(), 1e-9);
    +    assertRoughlyEquals(4, tx.getShearY(), 1e-9);
    +    assertRoughlyEquals(-1, tx.getShearX(), 1e-9);
    +    assertRoughlyEquals(-2, tx.getScaleY(), 1e-9);
    +    assertRoughlyEquals(7, tx.getTranslateX(), 1e-9);
    +    assertRoughlyEquals(10, tx.getTranslateY(), 1e-9);
    +  }
    +
    +  function testPreRotate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.preRotate(Math.PI / 2, 1, 1);
    +    assertRoughlyEquals(-2, tx.getScaleX(), 1e-9);
    +    assertRoughlyEquals(1, tx.getShearY(), 1e-9);
    +    assertRoughlyEquals(-4, tx.getShearX(), 1e-9);
    +    assertRoughlyEquals(3, tx.getScaleY(), 1e-9);
    +    assertRoughlyEquals(-4, tx.getTranslateX(), 1e-9);
    +    assertRoughlyEquals(5, tx.getTranslateY(), 1e-9);
    +  }
    +
    +  function testShear() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.shear(2, 3);
    +    assertEquals(10, tx.getScaleX());
    +    assertEquals(14, tx.getShearY());
    +    assertEquals(5, tx.getShearX());
    +    assertEquals(8, tx.getScaleY());
    +    assertEquals(5, tx.getTranslateX());
    +    assertEquals(6, tx.getTranslateY());
    +  }
    +
    +  function testPreShear() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.preShear(2, 3);
    +    assertEquals(5, tx.getScaleX());
    +    assertEquals(5, tx.getShearY());
    +    assertEquals(11, tx.getShearX());
    +    assertEquals(13, tx.getScaleY());
    +    assertEquals(17, tx.getTranslateX());
    +    assertEquals(21, tx.getTranslateY());
    +  }
    +
    +  function testConcatentate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.concatenate(new goog.graphics.AffineTransform(2, 1, 6, 5, 4, 3));
    +    assertEquals(5, tx.getScaleX());
    +    assertEquals(8, tx.getShearY());
    +    assertEquals(21, tx.getShearX());
    +    assertEquals(32, tx.getScaleY());
    +    assertEquals(18, tx.getTranslateX());
    +    assertEquals(26, tx.getTranslateY());
    +  }
    +
    +  function testPreConcatentate() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    tx.preConcatenate(new goog.graphics.AffineTransform(2, 1, 6, 5, 4, 3));
    +    assertEquals(14, tx.getScaleX());
    +    assertEquals(11, tx.getShearY());
    +    assertEquals(30, tx.getShearX());
    +    assertEquals(23, tx.getScaleY());
    +    assertEquals(50, tx.getTranslateX());
    +    assertEquals(38, tx.getTranslateY());
    +  }
    +
    +  function testAssociativeConcatenate() {
    +    var x = new goog.graphics.AffineTransform(2, 3, 5, 7, 11, 13).concatenate(
    +        new goog.graphics.AffineTransform(17, 19, 23, 29, 31, 37));
    +    var y = new goog.graphics.AffineTransform(17, 19, 23, 29, 31, 37)
    +        .preConcatenate(new goog.graphics.AffineTransform(2, 3, 5, 7, 11, 13));
    +    assertEquals(x.getScaleX(), y.getScaleX());
    +    assertEquals(x.getShearY(), y.getShearY());
    +    assertEquals(x.getShearX(), y.getShearX());
    +    assertEquals(x.getScaleY(), y.getScaleY());
    +    assertEquals(x.getTranslateX(), y.getTranslateX());
    +    assertEquals(x.getTranslateY(), y.getTranslateY());
    +  };
    +
    +  function testTransform() {
    +    var srcPts = [0, 0, 1, 0, 1, 1, 0, 1];
    +    var dstPts = [];
    +    var tx = goog.graphics.AffineTransform.getScaleInstance(2, 3);
    +    tx.translate(5, 10);
    +    tx.rotate(Math.PI / 4, 5, 10);
    +    tx.transform(srcPts, 0, dstPts, 0, 4);
    +    assert(goog.array.equals(
    +        [27.071068, 28.180195, 28.485281, 30.301516,
    +        27.071068, 32.422836, 25.656855, 30.301516],
    +        dstPts,
    +        goog.math.nearlyEquals));
    +  }
    +
    +  function testGetDeterminant() {
    +    var tx = goog.graphics.AffineTransform.getScaleInstance(2, 3);
    +    tx.translate(5, 10);
    +    tx.rotate(Math.PI / 4, 5, 10);
    +    assertRoughlyEquals(6, tx.getDeterminant(), 0.001);
    +  }
    +
    +  function testIsInvertible() {
    +    assertTrue(new goog.graphics.AffineTransform(2, 3, 4, 5, 6, 7).
    +        isInvertible());
    +    assertTrue(new goog.graphics.AffineTransform(1, 0, 0, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(NaN, 0, 0, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, NaN, 0, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, NaN, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, NaN, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, 1, NaN, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, 1, 0, NaN).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(Infinity, 0, 0, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, Infinity, 0, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, Infinity, 1, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, Infinity, 0, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, 1, Infinity, 0).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(1, 0, 0, 1, 0, Infinity).
    +        isInvertible());
    +    assertFalse(new goog.graphics.AffineTransform(0, 0, 0, 0, 1, 0).
    +        isInvertible());
    +  }
    +
    +  function testCreateInverse() {
    +    var tx = goog.graphics.AffineTransform.getScaleInstance(2, 3);
    +    tx.translate(5, 10);
    +    tx.rotate(Math.PI / 4, 5, 10);
    +    var inverse = tx.createInverse();
    +    assert(goog.math.nearlyEquals(0.353553, inverse.getScaleX()));
    +    assert(goog.math.nearlyEquals(-0.353553, inverse.getShearY()));
    +    assert(goog.math.nearlyEquals(0.235702, inverse.getShearX()));
    +    assert(goog.math.nearlyEquals(0.235702, inverse.getScaleY()));
    +    assert(goog.math.nearlyEquals(-16.213203, inverse.getTranslateX()));
    +    assert(goog.math.nearlyEquals(2.928932, inverse.getTranslateY()));
    +  }
    +
    +  function testCopyFrom() {
    +    var from = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    var to = new goog.graphics.AffineTransform();
    +    to.copyFrom(from);
    +    assertEquals(from.getScaleX(), to.getScaleX());
    +    assertEquals(from.getShearY(), to.getShearY());
    +    assertEquals(from.getShearX(), to.getShearX());
    +    assertEquals(from.getScaleY(), to.getScaleY());
    +    assertEquals(from.getTranslateX(), to.getTranslateX());
    +    assertEquals(from.getTranslateY(), to.getTranslateY());
    +  }
    +
    +  function testToString() {
    +    var tx = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    assertEquals("matrix(1,2,3,4,5,6)", tx.toString());
    +  }
    +
    +  function testEquals() {
    +    var tx1 = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    var tx2 = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, 6);
    +    assertEqualsMethod(tx1, tx2, true);
    +
    +    tx2 = new goog.graphics.AffineTransform(-1, 2, 3, 4, 5, 6);
    +    assertEqualsMethod(tx1, tx2, false);
    +
    +    tx2 = new goog.graphics.AffineTransform(1, -1, 3, 4, 5, 6);
    +    assertEqualsMethod(tx1, tx2, false);
    +
    +    tx2 = new goog.graphics.AffineTransform(1, 2, -3, 4, 5, 6);
    +    assertEqualsMethod(tx1, tx2, false);
    +
    +    tx2 = new goog.graphics.AffineTransform(1, 2, 3, -4, 5, 6);
    +    assertEqualsMethod(tx1, tx2, false);
    +
    +    tx2 = new goog.graphics.AffineTransform(1, 2, 3, 4, -5, 6);
    +    assertEqualsMethod(tx1, tx2, false);
    +
    +    tx2 = new goog.graphics.AffineTransform(1, 2, 3, 4, 5, -6);
    +    assertEqualsMethod(tx1, tx2, false);
    +  }
    +
    +  function assertEqualsMethod(tx1, tx2, expected) {
    +    assertEquals(expected, tx1.equals(tx2));
    +    assertEquals(expected, tx2.equals(tx1));
    +    assertEquals(true, tx1.equals(tx1));
    +    assertEquals(true, tx2.equals(tx2));
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/canvaselement.js b/mapbox-gl/closure-library/closure/goog/graphics/canvaselement.js
    new file mode 100644
    index 0000000000..8e9cda7490
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/canvaselement.js
    @@ -0,0 +1,797 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Objects representing shapes drawn on a canvas.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.graphics.CanvasEllipseElement');
    +goog.provide('goog.graphics.CanvasGroupElement');
    +goog.provide('goog.graphics.CanvasImageElement');
    +goog.provide('goog.graphics.CanvasPathElement');
    +goog.provide('goog.graphics.CanvasRectElement');
    +goog.provide('goog.graphics.CanvasTextElement');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.graphics.EllipseElement');
    +goog.require('goog.graphics.GroupElement');
    +goog.require('goog.graphics.ImageElement');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.graphics.PathElement');
    +goog.require('goog.graphics.RectElement');
    +goog.require('goog.graphics.TextElement');
    +goog.require('goog.math');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Object representing a group of objects in a canvas.
    + * This is an implementation of the goog.graphics.GroupElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {goog.graphics.CanvasGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.GroupElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.CanvasGroupElement = function(graphics) {
    +  goog.graphics.GroupElement.call(this, null, graphics);
    +
    +
    +  /**
    +   * Children contained by this group.
    +   * @type {Array<goog.graphics.Element>}
    +   * @private
    +   */
    +  this.children_ = [];
    +};
    +goog.inherits(goog.graphics.CanvasGroupElement, goog.graphics.GroupElement);
    +
    +
    +/**
    + * Remove all drawing elements from the group.
    + * @override
    + */
    +goog.graphics.CanvasGroupElement.prototype.clear = function() {
    +  if (this.children_.length) {
    +    this.children_.length = 0;
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Set the size of the group element.
    + * @param {number|string} width The width of the group element.
    + * @param {number|string} height The height of the group element.
    + * @override
    + */
    +goog.graphics.CanvasGroupElement.prototype.setSize = function(width, height) {
    +  // Do nothing.
    +};
    +
    +
    +/**
    + * Append a child to the group.  Does not draw it
    + * @param {goog.graphics.Element} element The child to append.
    + */
    +goog.graphics.CanvasGroupElement.prototype.appendChild = function(element) {
    +  this.children_.push(element);
    +};
    +
    +
    +/**
    + * Draw the group.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + */
    +goog.graphics.CanvasGroupElement.prototype.draw = function(ctx) {
    +  for (var i = 0, len = this.children_.length; i < len; i++) {
    +    this.getGraphics().drawElement(this.children_[i]);
    +  }
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for canvas ellipse elements.
    + * This is an implementation of the goog.graphics.EllipseElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.CanvasGraphics} graphics  The graphics creating
    + *     this element.
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.EllipseElement}
    + * @final
    + */
    +goog.graphics.CanvasEllipseElement = function(element, graphics,
    +    cx, cy, rx, ry, stroke, fill) {
    +  goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
    +
    +  /**
    +   * X coordinate of the ellipse center.
    +   * @type {number}
    +   * @private
    +   */
    +  this.cx_ = cx;
    +
    +
    +  /**
    +   * Y coordinate of the ellipse center.
    +   * @type {number}
    +   * @private
    +   */
    +  this.cy_ = cy;
    +
    +
    +  /**
    +   * Radius length for the x-axis.
    +   * @type {number}
    +   * @private
    +   */
    +  this.rx_ = rx;
    +
    +
    +  /**
    +   * Radius length for the y-axis.
    +   * @type {number}
    +   * @private
    +   */
    +  this.ry_ = ry;
    +
    +
    +  /**
    +   * Internal path approximating an ellipse.
    +   * @type {goog.graphics.Path}
    +   * @private
    +   */
    +  this.path_ = new goog.graphics.Path();
    +  this.setUpPath_();
    +
    +  /**
    +   * Internal path element that actually does the drawing.
    +   * @type {goog.graphics.CanvasPathElement}
    +   * @private
    +   */
    +  this.pathElement_ = new goog.graphics.CanvasPathElement(null, graphics,
    +      this.path_, stroke, fill);
    +};
    +goog.inherits(goog.graphics.CanvasEllipseElement, goog.graphics.EllipseElement);
    +
    +
    +/**
    + * Sets up the path.
    + * @private
    + */
    +goog.graphics.CanvasEllipseElement.prototype.setUpPath_ = function() {
    +  this.path_.clear();
    +  this.path_.moveTo(this.cx_ + goog.math.angleDx(0, this.rx_),
    +                    this.cy_ + goog.math.angleDy(0, this.ry_));
    +  this.path_.arcTo(this.rx_, this.ry_, 0, 360);
    +  this.path_.close();
    +};
    +
    +
    +/**
    + * Update the center point of the ellipse.
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @override
    + */
    +goog.graphics.CanvasEllipseElement.prototype.setCenter = function(cx, cy) {
    +  this.cx_ = cx;
    +  this.cy_ = cy;
    +  this.setUpPath_();
    +  this.pathElement_.setPath(/** @type {!goog.graphics.Path} */ (this.path_));
    +};
    +
    +
    +/**
    + * Update the radius of the ellipse.
    + * @param {number} rx Center X coordinate.
    + * @param {number} ry Center Y coordinate.
    + * @override
    + */
    +goog.graphics.CanvasEllipseElement.prototype.setRadius = function(rx, ry) {
    +  this.rx_ = rx;
    +  this.ry_ = ry;
    +  this.setUpPath_();
    +  this.pathElement_.setPath(/** @type {!goog.graphics.Path} */ (this.path_));
    +};
    +
    +
    +/**
    + * Draw the ellipse.  Should be treated as package scope.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + */
    +goog.graphics.CanvasEllipseElement.prototype.draw = function(ctx) {
    +  this.pathElement_.draw(ctx);
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for canvas rectangle elements.
    + * This is an implementation of the goog.graphics.RectElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.CanvasGraphics} graphics The graphics creating
    + *     this element.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} w Width of rectangle.
    + * @param {number} h Height of rectangle.
    + * @param {goog.graphics.Stroke} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.RectElement}
    + * @final
    + */
    +goog.graphics.CanvasRectElement = function(element, graphics, x, y, w, h,
    +    stroke, fill) {
    +  goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
    +
    +  /**
    +   * X coordinate of the top left corner.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x_ = x;
    +
    +
    +  /**
    +   * Y coordinate of the top left corner.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y_ = y;
    +
    +
    +  /**
    +   * Width of the rectangle.
    +   * @type {number}
    +   * @private
    +   */
    +  this.w_ = w;
    +
    +
    +  /**
    +   * Height of the rectangle.
    +   * @type {number}
    +   * @private
    +   */
    +  this.h_ = h;
    +};
    +goog.inherits(goog.graphics.CanvasRectElement, goog.graphics.RectElement);
    +
    +
    +/**
    + * Update the position of the rectangle.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.CanvasRectElement.prototype.setPosition = function(x, y) {
    +  this.x_ = x;
    +  this.y_ = y;
    +  if (this.drawn_) {
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Whether the rectangle has been drawn yet.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.CanvasRectElement.prototype.drawn_ = false;
    +
    +
    +/**
    + * Update the size of the rectangle.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @override
    + */
    +goog.graphics.CanvasRectElement.prototype.setSize = function(width, height) {
    +  this.w_ = width;
    +  this.h_ = height;
    +  if (this.drawn_) {
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Draw the rectangle.  Should be treated as package scope.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + */
    +goog.graphics.CanvasRectElement.prototype.draw = function(ctx) {
    +  this.drawn_ = true;
    +  ctx.beginPath();
    +  ctx.moveTo(this.x_, this.y_);
    +  ctx.lineTo(this.x_, this.y_ + this.h_);
    +  ctx.lineTo(this.x_ + this.w_, this.y_ + this.h_);
    +  ctx.lineTo(this.x_ + this.w_, this.y_);
    +  ctx.closePath();
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for canvas path elements.
    + * This is an implementation of the goog.graphics.PathElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.CanvasGraphics} graphics The graphics creating
    + *     this element.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @param {goog.graphics.Stroke} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.PathElement}
    + * @final
    + */
    +goog.graphics.CanvasPathElement = function(element, graphics, path, stroke,
    +    fill) {
    +  goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
    +
    +  this.setPath(path);
    +};
    +goog.inherits(goog.graphics.CanvasPathElement, goog.graphics.PathElement);
    +
    +
    +/**
    + * Whether the shape has been drawn yet.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.CanvasPathElement.prototype.drawn_ = false;
    +
    +
    +/**
    + * The path to draw.
    + * @type {goog.graphics.Path}
    + * @private
    + */
    +goog.graphics.CanvasPathElement.prototype.path_;
    +
    +
    +/**
    + * Update the underlying path.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @override
    + */
    +goog.graphics.CanvasPathElement.prototype.setPath = function(path) {
    +  this.path_ = path.isSimple() ? path :
    +      goog.graphics.Path.createSimplifiedPath(path);
    +  if (this.drawn_) {
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Draw the path.  Should be treated as package scope.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + * @suppress {deprecated} goog.graphics is deprecated.
    + */
    +goog.graphics.CanvasPathElement.prototype.draw = function(ctx) {
    +  this.drawn_ = true;
    +
    +  ctx.beginPath();
    +  this.path_.forEachSegment(function(segment, args) {
    +    switch (segment) {
    +      case goog.graphics.Path.Segment.MOVETO:
    +        ctx.moveTo(args[0], args[1]);
    +        break;
    +      case goog.graphics.Path.Segment.LINETO:
    +        for (var i = 0; i < args.length; i += 2) {
    +          ctx.lineTo(args[i], args[i + 1]);
    +        }
    +        break;
    +      case goog.graphics.Path.Segment.CURVETO:
    +        for (var i = 0; i < args.length; i += 6) {
    +          ctx.bezierCurveTo(args[i], args[i + 1], args[i + 2],
    +              args[i + 3], args[i + 4], args[i + 5]);
    +        }
    +        break;
    +      case goog.graphics.Path.Segment.ARCTO:
    +        throw Error('Canvas paths cannot contain arcs');
    +      case goog.graphics.Path.Segment.CLOSE:
    +        ctx.closePath();
    +        break;
    +    }
    +  });
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for canvas text elements.
    + * This is an implementation of the goog.graphics.TextElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {!goog.graphics.CanvasGraphics} graphics The graphics creating
    + *     this element.
    + * @param {string} text The text to draw.
    + * @param {number} x1 X coordinate of start of line.
    + * @param {number} y1 Y coordinate of start of line.
    + * @param {number} x2 X coordinate of end of line.
    + * @param {number} y2 Y coordinate of end of line.
    + * @param {?string} align Horizontal alignment: left (default), center, right.
    + * @param {!goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.TextElement}
    + * @final
    + */
    +goog.graphics.CanvasTextElement = function(graphics, text, x1, y1, x2, y2,
    +    align, font, stroke, fill) {
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, {
    +    'style': 'display:table;position:absolute;padding:0;margin:0;border:0'
    +  });
    +  goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
    +
    +  /**
    +   * The text to draw.
    +   * @type {string}
    +   * @private
    +   */
    +  this.text_ = text;
    +
    +  /**
    +   * X coordinate of the start of the line the text is drawn on.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x1_ = x1;
    +
    +  /**
    +   * Y coordinate of the start of the line the text is drawn on.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y1_ = y1;
    +
    +  /**
    +   * X coordinate of the end of the line the text is drawn on.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x2_ = x2;
    +
    +  /**
    +   * Y coordinate of the end of the line the text is drawn on.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y2_ = y2;
    +
    +  /**
    +   * Horizontal alignment: left (default), center, right.
    +   * @type {string}
    +   * @private
    +   */
    +  this.align_ = align || 'left';
    +
    +  /**
    +   * Font object describing the font properties.
    +   * @type {goog.graphics.Font}
    +   * @private
    +   */
    +  this.font_ = font;
    +
    +  /**
    +   * The inner element that contains the text.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.innerElement_ = goog.dom.createDom('DIV', {
    +    'style': 'display:table-cell;padding: 0;margin: 0;border: 0'
    +  });
    +
    +  this.updateStyle_();
    +  this.updateText_();
    +
    +  // Append to the DOM.
    +  graphics.getElement().appendChild(element);
    +  element.appendChild(this.innerElement_);
    +};
    +goog.inherits(goog.graphics.CanvasTextElement, goog.graphics.TextElement);
    +
    +
    +/**
    + * Update the displayed text of the element.
    + * @param {string} text The text to draw.
    + * @override
    + */
    +goog.graphics.CanvasTextElement.prototype.setText = function(text) {
    +  this.text_ = text;
    +  this.updateText_();
    +};
    +
    +
    +/**
    + * Sets the fill for this element.
    + * @param {goog.graphics.Fill} fill The fill object.
    + * @override
    + */
    +goog.graphics.CanvasTextElement.prototype.setFill = function(fill) {
    +  this.fill = fill;
    +  var element = this.getElement();
    +  if (element) {
    +    element.style.color = fill.getColor() || fill.getColor1();
    +  }
    +};
    +
    +
    +/**
    + * Sets the stroke for this element.
    + * @param {goog.graphics.Stroke} stroke The stroke object.
    + * @override
    + */
    +goog.graphics.CanvasTextElement.prototype.setStroke = function(stroke) {
    +  // Ignore stroke
    +};
    +
    +
    +/**
    + * Draw the text.  Should be treated as package scope.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + */
    +goog.graphics.CanvasTextElement.prototype.draw = function(ctx) {
    +  // Do nothing - the text is already drawn.
    +};
    +
    +
    +/**
    + * Update the styles of the DIVs.
    + * @private
    + */
    +goog.graphics.CanvasTextElement.prototype.updateStyle_ = function() {
    +  var x1 = this.x1_;
    +  var x2 = this.x2_;
    +  var y1 = this.y1_;
    +  var y2 = this.y2_;
    +  var align = this.align_;
    +  var font = this.font_;
    +  var style = this.getElement().style;
    +  var scaleX = this.getGraphics().getPixelScaleX();
    +  var scaleY = this.getGraphics().getPixelScaleY();
    +
    +  if (x1 == x2) {
    +    // Special case vertical text
    +    style.lineHeight = '90%';
    +
    +    this.innerElement_.style.verticalAlign = align == 'center' ? 'middle' :
    +        align == 'left' ? (y1 < y2 ? 'top' : 'bottom') :
    +        y1 < y2 ? 'bottom' : 'top';
    +    style.textAlign = 'center';
    +
    +    var w = font.size * scaleX;
    +    style.top = Math.round(Math.min(y1, y2) * scaleY) + 'px';
    +    style.left = Math.round((x1 - w / 2) * scaleX) + 'px';
    +    style.width = Math.round(w) + 'px';
    +    style.height = Math.abs(y1 - y2) * scaleY + 'px';
    +
    +    style.fontSize = font.size * 0.6 * scaleY + 'pt';
    +  } else {
    +    style.lineHeight = '100%';
    +    this.innerElement_.style.verticalAlign = 'top';
    +    style.textAlign = align;
    +
    +    style.top = Math.round(((y1 + y2) / 2 - font.size * 2 / 3) * scaleY) + 'px';
    +    style.left = Math.round(x1 * scaleX) + 'px';
    +    style.width = Math.round(Math.abs(x2 - x1) * scaleX) + 'px';
    +    style.height = 'auto';
    +
    +    style.fontSize = font.size * scaleY + 'pt';
    +  }
    +
    +  style.fontWeight = font.bold ? 'bold' : 'normal';
    +  style.fontStyle = font.italic ? 'italic' : 'normal';
    +  style.fontFamily = font.family;
    +
    +  var fill = this.getFill();
    +  style.color = fill.getColor() || fill.getColor1();
    +};
    +
    +
    +/**
    + * Update the text content.
    + * @private
    + */
    +goog.graphics.CanvasTextElement.prototype.updateText_ = function() {
    +  if (this.x1_ == this.x2_) {
    +    // Special case vertical text
    +    this.innerElement_.innerHTML =
    +        goog.array.map(this.text_.split(''),
    +            function(entry) { return goog.string.htmlEscape(entry); }).
    +            join('<br>');
    +  } else {
    +    this.innerElement_.innerHTML = goog.string.htmlEscape(this.text_);
    +  }
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for canvas image elements.
    + * This is an implementation of the goog.graphics.ImageElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.CanvasGraphics} graphics The graphics creating
    + *     this element.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} w Width of rectangle.
    + * @param {number} h Height of rectangle.
    + * @param {string} src Source of the image.
    + * @constructor
    + * @extends {goog.graphics.ImageElement}
    + * @final
    + */
    +goog.graphics.CanvasImageElement = function(element, graphics, x, y, w, h,
    +    src) {
    +  goog.graphics.ImageElement.call(this, element, graphics);
    +
    +  /**
    +   * X coordinate of the top left corner.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x_ = x;
    +
    +
    +  /**
    +   * Y coordinate of the top left corner.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y_ = y;
    +
    +
    +  /**
    +   * Width of the rectangle.
    +   * @type {number}
    +   * @private
    +   */
    +  this.w_ = w;
    +
    +
    +  /**
    +   * Height of the rectangle.
    +   * @type {number}
    +   * @private
    +   */
    +  this.h_ = h;
    +
    +
    +  /**
    +   * URL of the image source.
    +   * @type {string}
    +   * @private
    +   */
    +  this.src_ = src;
    +};
    +goog.inherits(goog.graphics.CanvasImageElement, goog.graphics.ImageElement);
    +
    +
    +/**
    + * Whether the image has been drawn yet.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.CanvasImageElement.prototype.drawn_ = false;
    +
    +
    +/**
    + * Update the position of the image.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.CanvasImageElement.prototype.setPosition = function(x, y) {
    +  this.x_ = x;
    +  this.y_ = y;
    +  if (this.drawn_) {
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Update the size of the image.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @override
    + */
    +goog.graphics.CanvasImageElement.prototype.setSize = function(width, height) {
    +  this.w_ = width;
    +  this.h_ = height;
    +  if (this.drawn_) {
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Update the source of the image.
    + * @param {string} src Source of the image.
    + * @override
    + */
    +goog.graphics.CanvasImageElement.prototype.setSource = function(src) {
    +  this.src_ = src;
    +  if (this.drawn_) {
    +    // TODO(robbyw): Probably need to reload the image here.
    +    this.getGraphics().redraw();
    +  }
    +};
    +
    +
    +/**
    + * Draw the image.  Should be treated as package scope.
    + * @param {CanvasRenderingContext2D} ctx The context to draw the element in.
    + */
    +goog.graphics.CanvasImageElement.prototype.draw = function(ctx) {
    +  if (this.img_) {
    +    if (this.w_ && this.h_) {
    +      // If the image is already loaded, draw it.
    +      ctx.drawImage(this.img_, this.x_, this.y_, this.w_, this.h_);
    +    }
    +    this.drawn_ = true;
    +
    +  } else {
    +    // Otherwise, load it.
    +    var img = new Image();
    +    img.onload = goog.bind(this.handleImageLoad_, this, img);
    +    // TODO(robbyw): Handle image load errors.
    +    img.src = this.src_;
    +  }
    +};
    +
    +
    +/**
    + * Handle an image load.
    + * @param {Element} img The image element that finished loading.
    + * @private
    + */
    +goog.graphics.CanvasImageElement.prototype.handleImageLoad_ = function(img) {
    +  this.img_ = img;
    +
    +  // TODO(robbyw): Add a small delay to catch batched images
    +  this.getGraphics().redraw();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/canvasgraphics.js b/mapbox-gl/closure-library/closure/goog/graphics/canvasgraphics.js
    new file mode 100644
    index 0000000000..932e9d03ea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/canvasgraphics.js
    @@ -0,0 +1,683 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview CanvasGraphics sub class that uses the canvas tag for drawing.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.CanvasGraphics');
    +
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.graphics.AbstractGraphics');
    +goog.require('goog.graphics.CanvasEllipseElement');
    +goog.require('goog.graphics.CanvasGroupElement');
    +goog.require('goog.graphics.CanvasImageElement');
    +goog.require('goog.graphics.CanvasPathElement');
    +goog.require('goog.graphics.CanvasRectElement');
    +goog.require('goog.graphics.CanvasTextElement');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * A Graphics implementation for drawing using canvas.
    + * @param {string|number} width The (non-zero) width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The (non-zero) height in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The coordinate width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight The coordinate height - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @constructor
    + * @extends {goog.graphics.AbstractGraphics}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.CanvasGraphics = function(width, height,
    +                                        opt_coordWidth, opt_coordHeight,
    +                                        opt_domHelper) {
    +  goog.graphics.AbstractGraphics.call(this, width, height,
    +                                      opt_coordWidth, opt_coordHeight,
    +                                      opt_domHelper);
    +};
    +goog.inherits(goog.graphics.CanvasGraphics, goog.graphics.AbstractGraphics);
    +
    +
    +/**
    + * Sets the fill for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element
    + *     wrapper.
    + * @param {goog.graphics.Fill} fill The fill object.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setElementFill = function(element,
    +    fill) {
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Sets the stroke for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element
    + *     wrapper.
    + * @param {goog.graphics.Stroke} stroke The stroke object.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setElementStroke = function(
    +    element, stroke) {
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Set the translation and rotation of an element.
    + *
    + * If a more general affine transform is needed than this provides
    + * (e.g. skew and scale) then use setElementAffineTransform.
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {number} x The x coordinate of the translation transform.
    + * @param {number} y The y coordinate of the translation transform.
    + * @param {number} angle The angle of the rotation transform.
    + * @param {number} centerX The horizontal center of the rotation transform.
    + * @param {number} centerY The vertical center of the rotation transform.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setElementTransform = function(element,
    +    x, y, angle, centerX, centerY) {
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Set the transformation of an element.
    + *
    + * Note that in this implementation this method just calls this.redraw()
    + * and the affineTransform param is unused.
    + * @param {!goog.graphics.Element} element The element wrapper.
    + * @param {!goog.graphics.AffineTransform} affineTransform The
    + *     transformation applied to this element.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setElementAffineTransform =
    +    function(element, affineTransform) {
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Push an element transform on to the transform stack.
    + * @param {goog.graphics.Element} element The transformed element.
    + */
    +goog.graphics.CanvasGraphics.prototype.pushElementTransform = function(
    +    element) {
    +  var ctx = this.getContext();
    +  ctx.save();
    +
    +  var transform = element.getTransform();
    +
    +  // TODO(robbyw): Test for unsupported transforms i.e. skews.
    +  var tx = transform.getTranslateX();
    +  var ty = transform.getTranslateY();
    +  if (tx || ty) {
    +    ctx.translate(tx, ty);
    +  }
    +
    +  var sinTheta = transform.getShearY();
    +  if (sinTheta) {
    +    ctx.rotate(Math.asin(sinTheta));
    +  }
    +};
    +
    +
    +/**
    + * Pop an element transform off of the transform stack.
    + */
    +goog.graphics.CanvasGraphics.prototype.popElementTransform = function() {
    +  this.getContext().restore();
    +};
    +
    +
    +/**
    + * Creates the DOM representation of the graphics area.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.createDom = function() {
    +  var element = this.dom_.createDom('div',
    +      {'style': 'position:relative;overflow:hidden'});
    +  this.setElementInternal(element);
    +
    +  this.canvas_ = this.dom_.createDom('canvas');
    +  element.appendChild(this.canvas_);
    +
    +  /**
    +   * The main canvas element.
    +   * @type {goog.graphics.CanvasGroupElement}
    +   */
    +  this.canvasElement = new goog.graphics.CanvasGroupElement(this);
    +
    +  this.lastGroup_ = this.canvasElement;
    +  this.redrawTimeout_ = 0;
    +
    +  this.updateSize();
    +};
    +
    +
    +/**
    + * Clears the drawing context object in response to actions that make the old
    + * context invalid - namely resize of the canvas element.
    + * @private
    + */
    +goog.graphics.CanvasGraphics.prototype.clearContext_ = function() {
    +  this.context_ = null;
    +};
    +
    +
    +/**
    + * Returns the drawing context.
    + * @return {Object} The canvas element rendering context.
    + */
    +goog.graphics.CanvasGraphics.prototype.getContext = function() {
    +  if (!this.getElement()) {
    +    this.createDom();
    +  }
    +  if (!this.context_) {
    +    this.context_ = this.canvas_.getContext('2d');
    +    this.context_.save();
    +  }
    +  return this.context_;
    +};
    +
    +
    +/**
    + * Changes the coordinate system position.
    + * @param {number} left The coordinate system left bound.
    + * @param {number} top The coordinate system top bound.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setCoordOrigin = function(left, top) {
    +  this.coordLeft = left;
    +  this.coordTop = top;
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Changes the coordinate size.
    + * @param {number} coordWidth The coordinate width.
    + * @param {number} coordHeight The coordinate height.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setCoordSize = function(coordWidth,
    +                                                               coordHeight) {
    +  goog.graphics.CanvasGraphics.superClass_.setCoordSize.apply(this, arguments);
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Change the size of the canvas.
    + * @param {number} pixelWidth The width in pixels.
    + * @param {number} pixelHeight The height in pixels.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.setSize = function(pixelWidth,
    +    pixelHeight) {
    +  this.width = pixelWidth;
    +  this.height = pixelHeight;
    +
    +  this.updateSize();
    +  this.redraw();
    +};
    +
    +
    +/** @override */
    +goog.graphics.CanvasGraphics.prototype.getPixelSize = function() {
    +  // goog.style.getSize does not work for Canvas elements.  We
    +  // have to compute the size manually if it is percentage based.
    +  var width = this.width;
    +  var height = this.height;
    +  var computeWidth = goog.isString(width) && width.indexOf('%') != -1;
    +  var computeHeight = goog.isString(height) && height.indexOf('%') != -1;
    +
    +  if (!this.isInDocument() && (computeWidth || computeHeight)) {
    +    return null;
    +  }
    +
    +  var parent;
    +  var parentSize;
    +
    +  if (computeWidth) {
    +    parent = /** @type {Element} */ (this.getElement().parentNode);
    +    parentSize = goog.style.getSize(parent);
    +    width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100;
    +  }
    +
    +  if (computeHeight) {
    +    parent = parent || /** @type {Element} */ (this.getElement().parentNode);
    +    parentSize = parentSize || goog.style.getSize(parent);
    +    height = parseFloat(/** @type {string} */ (height)) * parentSize.height /
    +        100;
    +  }
    +
    +  return new goog.math.Size(/** @type {number} */ (width),
    +      /** @type {number} */ (height));
    +};
    +
    +
    +/**
    + * Update the size of the canvas.
    + */
    +goog.graphics.CanvasGraphics.prototype.updateSize = function() {
    +  goog.style.setSize(this.getElement(), this.width, this.height);
    +
    +  var pixels = this.getPixelSize();
    +  if (pixels) {
    +    goog.style.setSize(this.canvas_,
    +        /** @type {number} */ (pixels.width),
    +        /** @type {number} */ (pixels.height));
    +    this.canvas_.width = pixels.width;
    +    this.canvas_.height = pixels.height;
    +    this.clearContext_();
    +  }
    +};
    +
    +
    +/**
    + * Reset the canvas.
    + */
    +goog.graphics.CanvasGraphics.prototype.reset = function() {
    +  var ctx = this.getContext();
    +  ctx.restore();
    +  var size = this.getPixelSize();
    +  if (size.width && size.height) {
    +    ctx.clearRect(0, 0, size.width, size.height);
    +  }
    +  ctx.save();
    +};
    +
    +
    +/**
    + * Remove all drawing elements from the graphics.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.clear = function() {
    +  this.reset();
    +  this.canvasElement.clear();
    +  var el = this.getElement();
    +
    +  // Remove all children (text nodes) except the canvas (which is at index 0)
    +  while (el.childNodes.length > 1) {
    +    el.removeChild(el.lastChild);
    +  }
    +};
    +
    +
    +/**
    + * Redraw the entire canvas.
    + */
    +goog.graphics.CanvasGraphics.prototype.redraw = function() {
    +  if (this.preventRedraw_) {
    +    this.needsRedraw_ = true;
    +    return;
    +  }
    +
    +  if (this.isInDocument()) {
    +    this.reset();
    +
    +    if (this.coordWidth) {
    +      var pixels = this.getPixelSize();
    +      this.getContext().scale(pixels.width / this.coordWidth,
    +          pixels.height / this.coordHeight);
    +    }
    +    if (this.coordLeft || this.coordTop) {
    +      this.getContext().translate(-this.coordLeft, -this.coordTop);
    +    }
    +    this.pushElementTransform(this.canvasElement);
    +    this.canvasElement.draw(this.context_);
    +    this.popElementTransform();
    +  }
    +};
    +
    +
    +/**
    + * Draw an element, including any stroke or fill.
    + * @param {goog.graphics.Element} element The element to draw.
    + */
    +goog.graphics.CanvasGraphics.prototype.drawElement = function(element) {
    +  if (element instanceof goog.graphics.CanvasTextElement) {
    +    // Don't draw text since that is not implemented using canvas.
    +    return;
    +  }
    +
    +  var ctx = this.getContext();
    +  this.pushElementTransform(element);
    +
    +  if (!element.getFill || !element.getStroke) {
    +    // Draw without stroke or fill (e.g. the element is an image or group).
    +    element.draw(ctx);
    +    this.popElementTransform();
    +    return;
    +  }
    +
    +  var fill = element.getFill();
    +  if (fill) {
    +    if (fill instanceof goog.graphics.SolidFill) {
    +      if (fill.getOpacity() != 0) {
    +        ctx.globalAlpha = fill.getOpacity();
    +        ctx.fillStyle = fill.getColor();
    +        element.draw(ctx);
    +        ctx.fill();
    +        ctx.globalAlpha = 1;
    +      }
    +    } else { // (fill instanceof goog.graphics.LinearGradient)
    +      var linearGradient = ctx.createLinearGradient(fill.getX1(), fill.getY1(),
    +          fill.getX2(), fill.getY2());
    +      linearGradient.addColorStop(0.0, fill.getColor1());
    +      linearGradient.addColorStop(1.0, fill.getColor2());
    +
    +      ctx.fillStyle = linearGradient;
    +      element.draw(ctx);
    +      ctx.fill();
    +    }
    +  }
    +
    +  var stroke = element.getStroke();
    +  if (stroke) {
    +    element.draw(ctx);
    +    ctx.strokeStyle = stroke.getColor();
    +
    +    var width = stroke.getWidth();
    +    if (goog.isString(width) && width.indexOf('px') != -1) {
    +      width = parseFloat(width) / this.getPixelScaleX();
    +    }
    +    ctx.lineWidth = width;
    +
    +    ctx.stroke();
    +  }
    +
    +  this.popElementTransform();
    +};
    +
    +
    +/**
    + * Append an element.
    + *
    + * @param {goog.graphics.Element} element The element to draw.
    + * @param {goog.graphics.CanvasGroupElement|undefined} group The group to draw
    + *     it in. If null or undefined, defaults to the root group.
    + * @private
    + * @deprecated Use append instead.
    + */
    +goog.graphics.CanvasGraphics.prototype.append_ = function(element, group) {
    +  this.append(element, group);
    +};
    +
    +
    +/**
    + * Append an element.
    + *
    + * @param {goog.graphics.Element} element The element to draw.
    + * @param {goog.graphics.GroupElement|undefined} group The group to draw
    + *     it in. If null or undefined, defaults to the root group.
    + * @protected
    + */
    +goog.graphics.CanvasGraphics.prototype.append = function(element, group) {
    +  group = group || this.canvasElement;
    +  group.appendChild(element);
    +
    +  if (this.isDrawable(group)) {
    +    this.drawElement(element);
    +  }
    +};
    +
    +
    +/**
    + * Draw an ellipse.
    + *
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to.  If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.EllipseElement} The newly created element.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.drawEllipse = function(cx, cy, rx, ry,
    +    stroke, fill, opt_group) {
    +  var element = new goog.graphics.CanvasEllipseElement(null, this,
    +      cx, cy, rx, ry, stroke, fill);
    +  this.append(element, opt_group);
    +  return element;
    +};
    +
    +
    +/**
    + * Draw a rectangle.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @param {goog.graphics.Stroke} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.RectElement} The newly created element.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.drawRect = function(x, y, width, height,
    +    stroke, fill, opt_group) {
    +  var element = new goog.graphics.CanvasRectElement(null, this,
    +      x, y, width, height, stroke, fill);
    +  this.append(element, opt_group);
    +  return element;
    +};
    +
    +
    +/**
    + * Draw an image.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of image.
    + * @param {number} height Height of image.
    + * @param {string} src Source of the image.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.ImageElement} The newly created element.
    + */
    +goog.graphics.CanvasGraphics.prototype.drawImage = function(x, y, width, height,
    +    src, opt_group) {
    +  var element = new goog.graphics.CanvasImageElement(null, this, x, y, width,
    +      height, src);
    +  this.append(element, opt_group);
    +  return element;
    +};
    +
    +
    +/**
    + * Draw a text string vertically centered on a given line.
    + *
    + * @param {string} text The text to draw.
    + * @param {number} x1 X coordinate of start of line.
    + * @param {number} y1 Y coordinate of start of line.
    + * @param {number} x2 X coordinate of end of line.
    + * @param {number} y2 Y coordinate of end of line.
    + * @param {?string} align Horizontal alignment: left (default), center, right.
    + * @param {goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke} stroke Stroke object describing the stroke.
    + * @param {goog.graphics.Fill} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.TextElement} The newly created element.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.drawTextOnLine = function(
    +    text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
    +  var element = new goog.graphics.CanvasTextElement(this,
    +      text, x1, y1, x2, y2, align, /** @type {!goog.graphics.Font} */ (font),
    +      stroke, fill);
    +  this.append(element, opt_group);
    +  return element;
    +};
    +
    +
    +/**
    + * Draw a path.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @param {goog.graphics.Stroke} stroke Stroke object describing the stroke.
    + * @param {goog.graphics.Fill} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.PathElement} The newly created element.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.drawPath = function(path, stroke, fill,
    +    opt_group) {
    +  var element = new goog.graphics.CanvasPathElement(null, this,
    +      path, stroke, fill);
    +  this.append(element, opt_group);
    +  return element;
    +};
    +
    +
    +/**
    + * @param {goog.graphics.GroupElement} group The group to possibly
    + *     draw to.
    + * @return {boolean} Whether drawing can occur now.
    + */
    +goog.graphics.CanvasGraphics.prototype.isDrawable = function(group) {
    +  return this.isInDocument() && !this.redrawTimeout_ &&
    +      !this.isRedrawRequired(group);
    +};
    +
    +
    +/**
    + * Returns true if drawing to the given group means a redraw is required.
    + * @param {goog.graphics.GroupElement} group The group to draw to.
    + * @return {boolean} Whether drawing to this group should force a redraw.
    + */
    +goog.graphics.CanvasGraphics.prototype.isRedrawRequired = function(group) {
    +  // TODO(robbyw): Moving up to any parent of lastGroup should not force redraw.
    +  return group != this.canvasElement && group != this.lastGroup_;
    +};
    +
    +
    +/**
    + * Create an empty group of drawing elements.
    + *
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper
    + *     element to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.CanvasGroupElement} The newly created group.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.createGroup = function(opt_group) {
    +  var group = new goog.graphics.CanvasGroupElement(this);
    +
    +  opt_group = opt_group || this.canvasElement;
    +
    +  // TODO(robbyw): Moving up to any parent group should not force redraw.
    +  if (opt_group == this.canvasElement || opt_group == this.lastGroup_) {
    +    this.lastGroup_ = group;
    +  }
    +
    +  this.append(group, opt_group);
    +
    +  return group;
    +};
    +
    +
    +/**
    + * Measure and return the width (in pixels) of a given text string.
    + * Text measurement is needed to make sure a text can fit in the allocated
    + * area. The way text length is measured is by writing it into a div that is
    + * after the visible area, measure the div width, and immediatly erase the
    + * written value.
    + *
    + * @param {string} text The text string to measure.
    + * @param {goog.graphics.Font} font The font object describing the font style.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.getTextWidth = goog.abstractMethod;
    +
    +
    +/**
    + * Disposes of the component by removing event handlers, detacing DOM nodes from
    + * the document body, and removing references to them.
    + * @override
    + * @protected
    + */
    +goog.graphics.CanvasGraphics.prototype.disposeInternal = function() {
    +  this.context_ = null;
    +  goog.graphics.CanvasGraphics.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/** @override */
    +goog.graphics.CanvasGraphics.prototype.enterDocument = function() {
    +  var oldPixelSize = this.getPixelSize();
    +  goog.graphics.CanvasGraphics.superClass_.enterDocument.call(this);
    +  if (!oldPixelSize) {
    +    this.updateSize();
    +    this.dispatchEvent(goog.events.EventType.RESIZE);
    +  }
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Start preventing redraws - useful for chaining large numbers of changes
    + * together.  Not guaranteed to do anything - i.e. only use this for
    + * optimization of a single code path.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.suspend = function() {
    +  this.preventRedraw_ = true;
    +};
    +
    +
    +/**
    + * Stop preventing redraws.  If any redraws had been prevented, a redraw will
    + * be done now.
    + * @override
    + */
    +goog.graphics.CanvasGraphics.prototype.resume = function() {
    +  this.preventRedraw_ = false;
    +
    +  if (this.needsRedraw_) {
    +    this.redraw();
    +    this.needsRedraw_ = false;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/element.js b/mapbox-gl/closure-library/closure/goog/graphics/element.js
    new file mode 100644
    index 0000000000..5a7bc8fd20
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/element.js
    @@ -0,0 +1,164 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element returned from
    + * the different draw methods of the graphics implementation, and
    + * all interfaces that the various element types support.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.Element');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.Listenable');
    +goog.require('goog.graphics.AffineTransform');
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Base class for a thin wrapper around the DOM element returned from
    + * the different draw methods of the graphics.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element  The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics  The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.Element = function(element, graphics) {
    +  goog.events.EventTarget.call(this);
    +  this.element_ = element;
    +  this.graphics_ = graphics;
    +  // Overloading EventTarget field to state that this is not a custom event.
    +  // TODO(user) Should be handled in EventTarget.js (see bug 846824).
    +  this[goog.events.Listenable.IMPLEMENTED_BY_PROP] = false;
    +};
    +goog.inherits(goog.graphics.Element, goog.events.EventTarget);
    +
    +
    +/**
    + * The graphics object that contains this element.
    + * @type {goog.graphics.AbstractGraphics?}
    + * @private
    + */
    +goog.graphics.Element.prototype.graphics_ = null;
    +
    +
    +/**
    + * The native browser element this class wraps.
    + * @type {Element}
    + * @private
    + */
    +goog.graphics.Element.prototype.element_ = null;
    +
    +
    +/**
    + * The transformation applied to this element.
    + * @type {goog.graphics.AffineTransform?}
    + * @private
    + */
    +goog.graphics.Element.prototype.transform_ = null;
    +
    +
    +/**
    + * Returns the underlying object.
    + * @return {Element} The underlying element.
    + */
    +goog.graphics.Element.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Returns the graphics.
    + * @return {goog.graphics.AbstractGraphics} The graphics that created the
    + *     element.
    + */
    +goog.graphics.Element.prototype.getGraphics = function() {
    +  return this.graphics_;
    +};
    +
    +
    +/**
    + * Set the translation and rotation of the element.
    + *
    + * If a more general affine transform is needed than this provides
    + * (e.g. skew and scale) then use setTransform.
    + * @param {number} x The x coordinate of the translation transform.
    + * @param {number} y The y coordinate of the translation transform.
    + * @param {number} rotate The angle of the rotation transform.
    + * @param {number} centerX The horizontal center of the rotation transform.
    + * @param {number} centerY The vertical center of the rotation transform.
    + */
    +goog.graphics.Element.prototype.setTransformation = function(x, y, rotate,
    +    centerX, centerY) {
    +  this.transform_ = goog.graphics.AffineTransform.getRotateInstance(
    +      goog.math.toRadians(rotate), centerX, centerY).translate(x, y);
    +  this.getGraphics().setElementTransform(this, x, y, rotate, centerX, centerY);
    +};
    +
    +
    +/**
    + * @return {!goog.graphics.AffineTransform} The transformation applied to
    + *     this element.
    + */
    +goog.graphics.Element.prototype.getTransform = function() {
    +  return this.transform_ ? this.transform_.clone() :
    +      new goog.graphics.AffineTransform();
    +};
    +
    +
    +/**
    + * Set the affine transform of the element.
    + * @param {!goog.graphics.AffineTransform} affineTransform The
    + *     transformation applied to this element.
    + */
    +goog.graphics.Element.prototype.setTransform = function(affineTransform) {
    +  this.transform_ = affineTransform.clone();
    +  this.getGraphics().setElementAffineTransform(this, affineTransform);
    +};
    +
    +
    +/** @override */
    +goog.graphics.Element.prototype.addEventListener = function(
    +    type, handler, opt_capture, opt_handlerScope) {
    +  goog.events.listen(this.element_, type, handler, opt_capture,
    +      opt_handlerScope);
    +};
    +
    +
    +/** @override */
    +goog.graphics.Element.prototype.removeEventListener = function(
    +    type, handler, opt_capture, opt_handlerScope) {
    +  goog.events.unlisten(this.element_, type, handler, opt_capture,
    +      opt_handlerScope);
    +};
    +
    +
    +/** @override */
    +goog.graphics.Element.prototype.disposeInternal = function() {
    +  goog.graphics.Element.superClass_.disposeInternal.call(this);
    +  goog.asserts.assert(this.element_);
    +  goog.events.removeAll(this.element_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ellipseelement.js b/mapbox-gl/closure-library/closure/goog/graphics/ellipseelement.js
    new file mode 100644
    index 0000000000..975b462e37
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ellipseelement.js
    @@ -0,0 +1,63 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for ellipses.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.EllipseElement');
    +
    +goog.require('goog.graphics.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Interface for a graphics ellipse element.
    + * You should not construct objects from this constructor. The graphics
    + * will return an implementation of this interface for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.StrokeAndFillElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.EllipseElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
    +      fill);
    +};
    +goog.inherits(goog.graphics.EllipseElement, goog.graphics.StrokeAndFillElement);
    +
    +
    +/**
    + * Update the center point of the ellipse.
    + * @param {number} cx  Center X coordinate.
    + * @param {number} cy  Center Y coordinate.
    + */
    +goog.graphics.EllipseElement.prototype.setCenter = goog.abstractMethod;
    +
    +
    +/**
    + * Update the radius of the ellipse.
    + * @param {number} rx  Radius length for the x-axis.
    + * @param {number} ry  Radius length for the y-axis.
    + */
    +goog.graphics.EllipseElement.prototype.setRadius = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates.js
    new file mode 100644
    index 0000000000..42385fea60
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates.js
    @@ -0,0 +1,159 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Graphics utility functions for advanced coordinates.
    + *
    + * This file assists the use of advanced coordinates in goog.graphics.  Coords
    + * can be specified as simple numbers which will correspond to units in the
    + * graphics element's coordinate space.  Alternately, coords can be expressed
    + * in pixels, meaning no matter what tranformations or coordinate system changes
    + * are present, the number of pixel changes will remain constant.  Coords can
    + * also be expressed as percentages of their parent's size.
    + *
    + * This file also allows for elements to have margins, expressable in any of
    + * the ways described above.
    + *
    + * Additional pieces of advanced coordinate functionality can (soon) be found in
    + * element.js and groupelement.js.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.graphics.ext.coordinates');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * Cache of boolean values.  For a given string (key), is it special? (value)
    + * @type {Object}
    + * @private
    + */
    +goog.graphics.ext.coordinates.specialCoordinateCache_ = {};
    +
    +
    +/**
    + * Determines if the given coordinate is a percent based coordinate or an
    + * expression with a percent based component.
    + * @param {string} coord The coordinate to test.
    + * @return {boolean} Whether the coordinate contains the string '%'.
    + * @private
    + */
    +goog.graphics.ext.coordinates.isPercent_ = function(coord) {
    +  return goog.string.contains(coord, '%');
    +};
    +
    +
    +/**
    + * Determines if the given coordinate is a pixel based coordinate or an
    + * expression with a pixel based component.
    + * @param {string} coord The coordinate to test.
    + * @return {boolean} Whether the coordinate contains the string 'px'.
    + * @private
    + */
    +goog.graphics.ext.coordinates.isPixels_ = function(coord) {
    +  return goog.string.contains(coord, 'px');
    +};
    +
    +
    +/**
    + * Determines if the given coordinate is special - i.e. not just a number.
    + * @param {string|number|null} coord The coordinate to test.
    + * @return {boolean} Whether the coordinate is special.
    + */
    +goog.graphics.ext.coordinates.isSpecial = function(coord) {
    +  var cache = goog.graphics.ext.coordinates.specialCoordinateCache_;
    +
    +  if (!(coord in cache)) {
    +    cache[coord] = goog.isString(coord) && (
    +        goog.graphics.ext.coordinates.isPercent_(coord) ||
    +        goog.graphics.ext.coordinates.isPixels_(coord));
    +  }
    +
    +  return cache[coord];
    +};
    +
    +
    +/**
    + * Returns the value of the given expression in the given context.
    + *
    + * Should be treated as package scope.
    + *
    + * @param {string|number} coord The coordinate to convert.
    + * @param {number} size The size of the parent element.
    + * @param {number} scale The ratio of pixels to units.
    + * @return {number} The number of coordinate space units that corresponds to
    + *     this coordinate.
    + */
    +goog.graphics.ext.coordinates.computeValue = function(coord, size, scale) {
    +  var number = parseFloat(String(coord));
    +  if (goog.isString(coord)) {
    +    if (goog.graphics.ext.coordinates.isPercent_(coord)) {
    +      return number * size / 100;
    +    } else if (goog.graphics.ext.coordinates.isPixels_(coord)) {
    +      return number / scale;
    +    }
    +  }
    +
    +  return number;
    +};
    +
    +
    +/**
    + * Converts the given coordinate to a number value in units.
    + *
    + * Should be treated as package scope.
    + *
    + * @param {string|number} coord The coordinate to retrieve the value for.
    + * @param {boolean|undefined} forMaximum Whether we are computing the largest
    + *     value this coordinate would be in a parent of no size.  The container
    + *     size in this case should be set to the size of the current element.
    + * @param {number} containerSize The unit value of the size of the container of
    + *     this element.  Should be set to the minimum width of this element if
    + *     forMaximum is true.
    + * @param {number} scale The ratio of pixels to units.
    + * @param {Object=} opt_cache Optional (but highly recommend) object to store
    + *     cached computations in.  The calling class should manage clearing out
    + *     the cache when the scale or containerSize changes.
    + * @return {number} The correct number of coordinate space units.
    + */
    +goog.graphics.ext.coordinates.getValue = function(coord, forMaximum,
    +    containerSize, scale, opt_cache) {
    +  if (!goog.isNumber(coord)) {
    +    var cacheString = opt_cache && ((forMaximum ? 'X' : '') + coord);
    +
    +    if (opt_cache && cacheString in opt_cache) {
    +      coord = opt_cache[cacheString];
    +    } else {
    +      if (goog.graphics.ext.coordinates.isSpecial(
    +          /** @type {string} */ (coord))) {
    +        coord = goog.graphics.ext.coordinates.computeValue(coord,
    +            containerSize, scale);
    +      } else {
    +        // Simple coordinates just need to be converted from a string to a
    +        // number.
    +        coord = parseFloat(/** @type {string} */ (coord));
    +      }
    +
    +      // Cache the result.
    +      if (opt_cache) {
    +        opt_cache[cacheString] = coord;
    +      }
    +    }
    +  }
    +
    +  return coord;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates_test.html b/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates_test.html
    new file mode 100644
    index 0000000000..14b5447677
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/coordinates_test.html
    @@ -0,0 +1,74 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.ext.coordinates</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.graphics');
    +  goog.require('goog.graphics.ext.coordinates');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +
    +<script>
    +  function testIsPercent() {
    +    assert('50% is a percent',
    +        goog.graphics.ext.coordinates.isPercent_('50%'));
    +    assert('50 is not a percent',
    +        !goog.graphics.ext.coordinates.isPercent_('50'));
    +  }
    +
    +  function testIsPixels() {
    +    assert('50px is pixels', goog.graphics.ext.coordinates.isPixels_('50px'));
    +    assert('50 is not pixels', !goog.graphics.ext.coordinates.isPixels_('50'));
    +  }
    +
    +  function testIsSpecial() {
    +    assert('50px is special', goog.graphics.ext.coordinates.isSpecial('50px'));
    +    assert('50% is special', goog.graphics.ext.coordinates.isSpecial('50%'));
    +    assert('50 is not special', !goog.graphics.ext.coordinates.isSpecial('50'));
    +  }
    +
    +  function testComputeValue() {
    +    assertEquals('50% of 100 is 50', 50,
    +        goog.graphics.ext.coordinates.computeValue('50%', 100, null));
    +    assertEquals('50.5% of 200 is 101', 101,
    +        goog.graphics.ext.coordinates.computeValue('50.5%', 200, null));
    +    assertEquals('50px = 25 units when in 2x view', 25,
    +        goog.graphics.ext.coordinates.computeValue('50px', null, 2));
    +  }
    +
    +  function testGenericGetValue() {
    +    var getValue = goog.graphics.ext.coordinates.getValue;
    +
    +    var cache = {};
    +
    +    assertEquals('Testing 50%', 50,
    +        getValue('50%', false, 100, 2, cache));
    +
    +    var count = 0;
    +    for (var x in cache) {
    +      count++;
    +      cache[x] = 'OVERWRITE';
    +    }
    +
    +    assertEquals('Testing cache size', 1, count);
    +    assertEquals('Testing cache usage', 'OVERWRITE',
    +        getValue('50%', false, 100, 2, cache));
    +
    +    cache = {};
    +
    +    assertEquals('Testing 0%', 0,
    +        getValue('0%', false, 100, 2, cache));
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/element.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/element.js
    new file mode 100644
    index 0000000000..546c8c34a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/element.js
    @@ -0,0 +1,963 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A thicker wrapper around the DOM element returned from
    + * the different draw methods of the graphics implementation, and
    + * all interfaces that the various element types support.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.graphics.ext.Element');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.functions');
    +goog.require('goog.graphics.ext.coordinates');
    +
    +
    +
    +/**
    + * Base class for a wrapper around the goog.graphics wrapper that enables
    + * more advanced functionality.
    + * @param {goog.graphics.ext.Group?} group Parent for this element.
    + * @param {goog.graphics.Element} wrapper The thin wrapper to wrap.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.graphics.ext.Element = function(group, wrapper) {
    +  goog.events.EventTarget.call(this);
    +  this.wrapper_ = wrapper;
    +  this.graphics_ = group ? group.getGraphics() : this;
    +
    +  this.xPosition_ = new goog.graphics.ext.Element.Position_(this, true);
    +  this.yPosition_ = new goog.graphics.ext.Element.Position_(this, false);
    +
    +  // Handle parent / child relationships.
    +  if (group) {
    +    this.parent_ = group;
    +    this.parent_.addChild(this);
    +  }
    +};
    +goog.inherits(goog.graphics.ext.Element, goog.events.EventTarget);
    +
    +
    +/**
    + * The graphics object that contains this element.
    + * @type {goog.graphics.ext.Graphics|goog.graphics.ext.Element}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.graphics_;
    +
    +
    +/**
    + * The goog.graphics wrapper this class wraps.
    + * @type {goog.graphics.Element}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.wrapper_;
    +
    +
    +/**
    + * The group or surface containing this element.
    + * @type {goog.graphics.ext.Group|undefined}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.parent_;
    +
    +
    +/**
    + * Whether or not computation of this element's position or size depends on its
    + * parent's size.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.parentDependent_ = false;
    +
    +
    +/**
    + * Whether the element has pending transformations.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.needsTransform_ = false;
    +
    +
    +/**
    + * The current angle of rotation, expressed in degrees.
    + * @type {number}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.rotation_ = 0;
    +
    +
    +/**
    + * Object representing the x position and size of the element.
    + * @type {goog.graphics.ext.Element.Position_}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.xPosition_;
    +
    +
    +/**
    + * Object representing the y position and size of the element.
    + * @type {goog.graphics.ext.Element.Position_}
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.yPosition_;
    +
    +
    +/** @return {goog.graphics.Element} The underlying thin wrapper. */
    +goog.graphics.ext.Element.prototype.getWrapper = function() {
    +  return this.wrapper_;
    +};
    +
    +
    +/**
    + * @return {goog.graphics.ext.Element|goog.graphics.ext.Graphics} The graphics
    + *     surface the element is a part of.
    + */
    +goog.graphics.ext.Element.prototype.getGraphics = function() {
    +  return this.graphics_;
    +};
    +
    +
    +/**
    + * Returns the graphics implementation.
    + * @return {goog.graphics.AbstractGraphics} The underlying graphics
    + *     implementation drawing this element's wrapper.
    + * @protected
    + */
    +goog.graphics.ext.Element.prototype.getGraphicsImplementation = function() {
    +  return this.graphics_.getImplementation();
    +};
    +
    +
    +/**
    + * @return {goog.graphics.ext.Group|undefined} The parent of this element.
    + */
    +goog.graphics.ext.Element.prototype.getParent = function() {
    +  return this.parent_;
    +};
    +
    +
    +// GENERAL POSITIONING
    +
    +
    +/**
    + * Internal convenience method for setting position - either as a left/top,
    + * center/middle, or right/bottom value.  Only one should be specified.
    + * @param {goog.graphics.ext.Element.Position_} position The position object to
    + *     set the value on.
    + * @param {number|string} value The value of the coordinate.
    + * @param {goog.graphics.ext.Element.PositionType_} type The type of the
    + *     coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.setPosition_ = function(position, value,
    +    type, opt_chain) {
    +  position.setPosition(value, type);
    +  this.computeIsParentDependent_(position);
    +
    +  this.needsTransform_ = true;
    +  if (!opt_chain) {
    +    this.transform();
    +  }
    +};
    +
    +
    +/**
    + * Sets the width/height of the element.
    + * @param {goog.graphics.ext.Element.Position_} position The position object to
    + *     set the value on.
    + * @param {string|number} size The new width/height value.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.setSize_ = function(position, size,
    +    opt_chain) {
    +  if (position.setSize(size)) {
    +    this.needsTransform_ = true;
    +
    +    this.computeIsParentDependent_(position);
    +
    +    if (!opt_chain) {
    +      this.reset();
    +    }
    +  } else if (!opt_chain && this.isPendingTransform()) {
    +    this.reset();
    +  }
    +};
    +
    +
    +/**
    + * Sets the minimum width/height of the element.
    + * @param {goog.graphics.ext.Element.Position_} position The position object to
    + *     set the value on.
    + * @param {string|number} minSize The minimum width/height of the element.
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.setMinSize_ = function(position, minSize) {
    +  position.setMinSize(minSize);
    +  this.needsTransform_ = true;
    +  this.computeIsParentDependent_(position);
    +};
    +
    +
    +// HORIZONTAL POSITIONING
    +
    +
    +/**
    + * @return {number} The distance from the left edge of this element to the left
    + *     edge of its parent, specified in units of the parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getLeft = function() {
    +  return this.xPosition_.getStart();
    +};
    +
    +
    +/**
    + * Sets the left coordinate of the element.  Overwrites any previous value of
    + * left, center, or right for this element.
    + * @param {string|number} left The left coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setLeft = function(left, opt_chain) {
    +  this.setPosition_(this.xPosition_,
    +      left,
    +      goog.graphics.ext.Element.PositionType_.START,
    +      opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The right coordinate of the element, in units of the
    + *     parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getRight = function() {
    +  return this.xPosition_.getEnd();
    +};
    +
    +
    +/**
    + * Sets the right coordinate of the element.  Overwrites any previous value of
    + * left, center, or right for this element.
    + * @param {string|number} right The right coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setRight = function(right, opt_chain) {
    +  this.setPosition_(this.xPosition_,
    +      right,
    +      goog.graphics.ext.Element.PositionType_.END,
    +      opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The center coordinate of the element, in units of the
    + * parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getCenter = function() {
    +  return this.xPosition_.getMiddle();
    +};
    +
    +
    +/**
    + * Sets the center coordinate of the element.  Overwrites any previous value of
    + * left, center, or right for this element.
    + * @param {string|number} center The center coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setCenter = function(center, opt_chain) {
    +  this.setPosition_(this.xPosition_,
    +      center,
    +      goog.graphics.ext.Element.PositionType_.MIDDLE,
    +      opt_chain);
    +};
    +
    +
    +// VERTICAL POSITIONING
    +
    +
    +/**
    + * @return {number} The distance from the top edge of this element to the top
    + *     edge of its parent, specified in units of the parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getTop = function() {
    +  return this.yPosition_.getStart();
    +};
    +
    +
    +/**
    + * Sets the top coordinate of the element.  Overwrites any previous value of
    + * top, middle, or bottom for this element.
    + * @param {string|number} top The top coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setTop = function(top, opt_chain) {
    +  this.setPosition_(this.yPosition_,
    +      top,
    +      goog.graphics.ext.Element.PositionType_.START,
    +      opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The bottom coordinate of the element, in units of the
    + *     parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getBottom = function() {
    +  return this.yPosition_.getEnd();
    +};
    +
    +
    +/**
    + * Sets the bottom coordinate of the element.  Overwrites any previous value of
    + * top, middle, or bottom for this element.
    + * @param {string|number} bottom The bottom coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setBottom = function(bottom, opt_chain) {
    +  this.setPosition_(this.yPosition_,
    +      bottom,
    +      goog.graphics.ext.Element.PositionType_.END,
    +      opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The middle coordinate of the element, in units of the
    + *     parent's coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getMiddle = function() {
    +  return this.yPosition_.getMiddle();
    +};
    +
    +
    +/**
    + * Sets the middle coordinate of the element.  Overwrites any previous value of
    + * top, middle, or bottom for this element
    + * @param {string|number} middle The middle coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setMiddle = function(middle, opt_chain) {
    +  this.setPosition_(this.yPosition_,
    +      middle,
    +      goog.graphics.ext.Element.PositionType_.MIDDLE,
    +      opt_chain);
    +};
    +
    +
    +// DIMENSIONS
    +
    +
    +/**
    + * @return {number} The width of the element, in units of the parent's
    + *     coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getWidth = function() {
    +  return this.xPosition_.getSize();
    +};
    +
    +
    +/**
    + * Sets the width of the element.
    + * @param {string|number} width The new width value.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setWidth = function(width, opt_chain) {
    +  this.setSize_(this.xPosition_, width, opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The minimum width of the element, in units of the parent's
    + *     coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getMinWidth = function() {
    +  return this.xPosition_.getMinSize();
    +};
    +
    +
    +/**
    + * Sets the minimum width of the element.
    + * @param {string|number} minWidth The minimum width of the element.
    + */
    +goog.graphics.ext.Element.prototype.setMinWidth = function(minWidth) {
    +  this.setMinSize_(this.xPosition_, minWidth);
    +};
    +
    +
    +/**
    + * @return {number} The height of the element, in units of the parent's
    + *     coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getHeight = function() {
    +  return this.yPosition_.getSize();
    +};
    +
    +
    +/**
    + * Sets the height of the element.
    + * @param {string|number} height The new height value.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setHeight = function(height, opt_chain) {
    +  this.setSize_(this.yPosition_, height, opt_chain);
    +};
    +
    +
    +/**
    + * @return {number} The minimum height of the element, in units of the parent's
    + *     coordinate system.
    + */
    +goog.graphics.ext.Element.prototype.getMinHeight = function() {
    +  return this.yPosition_.getMinSize();
    +};
    +
    +
    +/**
    + * Sets the minimum height of the element.
    + * @param {string|number} minHeight The minimum height of the element.
    + */
    +goog.graphics.ext.Element.prototype.setMinHeight = function(minHeight) {
    +  this.setMinSize_(this.yPosition_, minHeight);
    +};
    +
    +
    +// BOUNDS SHORTCUTS
    +
    +
    +/**
    + * Shortcut for setting the left and top position.
    + * @param {string|number} left The left coordinate.
    + * @param {string|number} top The top coordinate.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setPosition = function(left, top,
    +                                                           opt_chain) {
    +  this.setLeft(left, true);
    +  this.setTop(top, opt_chain);
    +};
    +
    +
    +/**
    + * Shortcut for setting the width and height.
    + * @param {string|number} width The new width value.
    + * @param {string|number} height The new height value.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setSize = function(width, height,
    +                                                       opt_chain) {
    +  this.setWidth(width, true);
    +  this.setHeight(height, opt_chain);
    +};
    +
    +
    +/**
    + * Shortcut for setting the left, top, width, and height.
    + * @param {string|number} left The left coordinate.
    + * @param {string|number} top The top coordinate.
    + * @param {string|number} width The new width value.
    + * @param {string|number} height The new height value.
    + * @param {boolean=} opt_chain Optional flag to specify this function is part
    + *     of a chain of calls and therefore transformations should be set as
    + *     pending but not yet performed.
    + */
    +goog.graphics.ext.Element.prototype.setBounds = function(left, top, width,
    +                                                         height, opt_chain) {
    +  this.setLeft(left, true);
    +  this.setTop(top, true);
    +  this.setWidth(width, true);
    +  this.setHeight(height, opt_chain);
    +};
    +
    +
    +// MAXIMUM BOUNDS
    +
    +
    +/**
    + * @return {number} An estimate of the maximum x extent this element would have
    + *     in a parent of no width.
    + */
    +goog.graphics.ext.Element.prototype.getMaxX = function() {
    +  return this.xPosition_.getMaxPosition();
    +};
    +
    +
    +/**
    + * @return {number} An estimate of the maximum y extent this element would have
    + *     in a parent of no height.
    + */
    +goog.graphics.ext.Element.prototype.getMaxY = function() {
    +  return this.yPosition_.getMaxPosition();
    +};
    +
    +
    +// RESET
    +
    +
    +/**
    + * Reset the element.  This is called when the element changes size, or when
    + * the coordinate system changes in a way that would affect pixel based
    + * rendering
    + */
    +goog.graphics.ext.Element.prototype.reset = function() {
    +  this.xPosition_.resetCache();
    +  this.yPosition_.resetCache();
    +
    +  this.redraw();
    +
    +  this.needsTransform_ = true;
    +  this.transform();
    +};
    +
    +
    +/**
    + * Overridable function for subclass specific reset.
    + * @protected
    + */
    +goog.graphics.ext.Element.prototype.redraw = goog.nullFunction;
    +
    +
    +// PARENT DEPENDENCY
    +
    +
    +/**
    + * Computes whether the element is still parent dependent.
    + * @param {goog.graphics.ext.Element.Position_} position The recently changed
    + *     position object.
    + * @private
    + */
    +goog.graphics.ext.Element.prototype.computeIsParentDependent_ = function(
    +    position) {
    +  this.parentDependent_ = position.isParentDependent() ||
    +      this.xPosition_.isParentDependent() ||
    +      this.yPosition_.isParentDependent() ||
    +      this.checkParentDependent();
    +};
    +
    +
    +/**
    + * Returns whether this element's bounds depend on its parents.
    + *
    + * This function should be treated as if it has package scope.
    + * @return {boolean} Whether this element's bounds depend on its parents.
    + */
    +goog.graphics.ext.Element.prototype.isParentDependent = function() {
    +  return this.parentDependent_;
    +};
    +
    +
    +/**
    + * Overridable function for subclass specific parent dependency.
    + * @return {boolean} Whether this shape's bounds depends on its parent's.
    + * @protected
    + */
    +goog.graphics.ext.Element.prototype.checkParentDependent =
    +    goog.functions.FALSE;
    +
    +
    +// ROTATION
    +
    +
    +/**
    + * Set the rotation of this element.
    + * @param {number} angle The angle of rotation, in degrees.
    + */
    +goog.graphics.ext.Element.prototype.setRotation = function(angle) {
    +  if (this.rotation_ != angle) {
    +    this.rotation_ = angle;
    +
    +    this.needsTransform_ = true;
    +    this.transform();
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The angle of rotation of this element, in degrees.
    + */
    +goog.graphics.ext.Element.prototype.getRotation = function() {
    +  return this.rotation_;
    +};
    +
    +
    +// TRANSFORMS
    +
    +
    +/**
    + * Called by the parent when the parent has transformed.
    + *
    + * Should be treated as package scope.
    + */
    +goog.graphics.ext.Element.prototype.parentTransform = function() {
    +  this.needsTransform_ = this.needsTransform_ || this.parentDependent_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this element has pending transforms.
    + */
    +goog.graphics.ext.Element.prototype.isPendingTransform = function() {
    +  return this.needsTransform_;
    +};
    +
    +
    +/**
    + * Performs a pending transform.
    + * @protected
    + */
    +goog.graphics.ext.Element.prototype.transform = function() {
    +  if (this.isPendingTransform()) {
    +    this.needsTransform_ = false;
    +
    +    this.wrapper_.setTransformation(
    +        this.getLeft(),
    +        this.getTop(),
    +        this.rotation_,
    +        (this.getWidth() || 1) / 2,
    +        (this.getHeight() || 1) / 2);
    +
    +    // TODO(robbyw): this._fireEvent('transform', [ this ]);
    +  }
    +};
    +
    +
    +// PIXEL SCALE
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the x direction.
    + */
    +goog.graphics.ext.Element.prototype.getPixelScaleX = function() {
    +  return this.getGraphics().getPixelScaleX();
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the y direction.
    + */
    +goog.graphics.ext.Element.prototype.getPixelScaleY = function() {
    +  return this.getGraphics().getPixelScaleY();
    +};
    +
    +
    +// EVENT HANDLING
    +
    +
    +/** @override */
    +goog.graphics.ext.Element.prototype.disposeInternal = function() {
    +  goog.graphics.ext.Element.superClass_.disposeInternal.call();
    +  this.wrapper_.dispose();
    +};
    +
    +
    +// INTERNAL POSITION OBJECT
    +
    +
    +/**
    + * Position specification types.  Start corresponds to left/top, middle to
    + * center/middle, and end to right/bottom.
    + * @enum {number}
    + * @private
    + */
    +goog.graphics.ext.Element.PositionType_ = {
    +  START: 0,
    +  MIDDLE: 1,
    +  END: 2
    +};
    +
    +
    +
    +/**
    + * Manages a position and size, either horizontal or vertical.
    + * @param {goog.graphics.ext.Element} element The element the position applies
    + *     to.
    + * @param {boolean} horizontal Whether the position is horizontal or vertical.
    + * @constructor
    + * @private
    + */
    +goog.graphics.ext.Element.Position_ = function(element, horizontal) {
    +  this.element_ = element;
    +  this.horizontal_ = horizontal;
    +};
    +
    +
    +/**
    + * @return {!Object} The coordinate value computation cache.
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.getCoordinateCache_ = function() {
    +  return this.coordinateCache_ || (this.coordinateCache_ = {});
    +};
    +
    +
    +/**
    + * @return {number} The size of the parent's coordinate space.
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.getParentSize_ = function() {
    +  var parent = this.element_.getParent();
    +  return this.horizontal_ ?
    +      parent.getCoordinateWidth() :
    +      parent.getCoordinateHeight();
    +};
    +
    +
    +/**
    + * @return {number} The minimum width/height of the element.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getMinSize = function() {
    +  return this.getValue_(this.minSize_);
    +};
    +
    +
    +/**
    + * Sets the minimum width/height of the element.
    + * @param {string|number} minSize The minimum width/height of the element.
    + */
    +goog.graphics.ext.Element.Position_.prototype.setMinSize = function(minSize) {
    +  this.minSize_ = minSize;
    +  this.resetCache();
    +};
    +
    +
    +/**
    + * @return {number} The width/height of the element.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getSize = function() {
    +  return Math.max(this.getValue_(this.size_), this.getMinSize());
    +};
    +
    +
    +/**
    + * Sets the width/height of the element.
    + * @param {string|number} size The width/height of the element.
    + * @return {boolean} Whether the value was changed.
    + */
    +goog.graphics.ext.Element.Position_.prototype.setSize = function(size) {
    +  if (size != this.size_) {
    +    this.size_ = size;
    +    this.resetCache();
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Converts the given x coordinate to a number value in units.
    + * @param {string|number} v The coordinate to retrieve the value for.
    + * @param {boolean=} opt_forMaximum Whether we are computing the largest value
    + *     this coordinate would be in a parent of no size.
    + * @return {number} The correct number of coordinate space units.
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.getValue_ = function(v,
    +    opt_forMaximum) {
    +  if (!goog.graphics.ext.coordinates.isSpecial(v)) {
    +    return parseFloat(String(v));
    +  }
    +
    +  var cache = this.getCoordinateCache_();
    +  var scale = this.horizontal_ ?
    +      this.element_.getPixelScaleX() :
    +      this.element_.getPixelScaleY();
    +
    +  var containerSize;
    +  if (opt_forMaximum) {
    +    containerSize = goog.graphics.ext.coordinates.computeValue(
    +        this.size_ || 0, 0, scale);
    +  } else {
    +    var parent = this.element_.getParent();
    +    containerSize = this.horizontal_ ? parent.getWidth() : parent.getHeight();
    +  }
    +
    +  return goog.graphics.ext.coordinates.getValue(v, opt_forMaximum,
    +      containerSize, scale, cache);
    +};
    +
    +
    +/**
    + * @return {number} The distance from the left/top edge of this element to the
    + *     left/top edge of its parent, specified in units of the parent's
    + *     coordinate system.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getStart = function() {
    +  if (this.cachedValue_ == null) {
    +    var value = this.getValue_(this.distance_);
    +    if (this.distanceType_ == goog.graphics.ext.Element.PositionType_.START) {
    +      this.cachedValue_ = value;
    +    } else if (this.distanceType_ ==
    +               goog.graphics.ext.Element.PositionType_.MIDDLE) {
    +      this.cachedValue_ = value + (this.getParentSize_() - this.getSize()) / 2;
    +    } else {
    +      this.cachedValue_ = this.getParentSize_() - value - this.getSize();
    +    }
    +  }
    +
    +  return this.cachedValue_;
    +};
    +
    +
    +/**
    + * @return {number} The middle coordinate of the element, in units of the
    + *     parent's coordinate system.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getMiddle = function() {
    +  return this.distanceType_ == goog.graphics.ext.Element.PositionType_.MIDDLE ?
    +      this.getValue_(this.distance_) :
    +      (this.getParentSize_() - this.getSize()) / 2 - this.getStart();
    +};
    +
    +
    +/**
    + * @return {number} The end coordinate of the element, in units of the
    + *     parent's coordinate system.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getEnd = function() {
    +  return this.distanceType_ == goog.graphics.ext.Element.PositionType_.END ?
    +      this.getValue_(this.distance_) :
    +      this.getParentSize_() - this.getStart() - this.getSize();
    +};
    +
    +
    +/**
    + * Sets the position, either as a left/top, center/middle, or right/bottom
    + * value.
    + * @param {number|string} value The value of the coordinate.
    + * @param {goog.graphics.ext.Element.PositionType_} type The type of the
    + *     coordinate.
    + */
    +goog.graphics.ext.Element.Position_.prototype.setPosition = function(value,
    +    type) {
    +  this.distance_ = value;
    +  this.distanceType_ = type;
    +
    +  // Clear cached value.
    +  this.cachedValue_ = null;
    +};
    +
    +
    +/**
    + * @return {number} An estimate of the maximum x/y extent this element would
    + *     have in a parent of no width/height.
    + */
    +goog.graphics.ext.Element.Position_.prototype.getMaxPosition = function() {
    +  // TODO(robbyw): Handle transformed or rotated coordinates
    +  // TODO(robbyw): Handle pixel based sizes?
    +
    +  return this.getValue_(this.distance_ || 0) + (
    +      goog.graphics.ext.coordinates.isSpecial(this.size_) ? 0 : this.getSize());
    +};
    +
    +
    +/**
    + * Resets the caches of position values and coordinate values.
    + */
    +goog.graphics.ext.Element.Position_.prototype.resetCache = function() {
    +  this.coordinateCache_ = null;
    +  this.cachedValue_ = null;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the size or position of this element depends on
    + *     the size of the parent element.
    + */
    +goog.graphics.ext.Element.Position_.prototype.isParentDependent = function() {
    +  return this.distanceType_ != goog.graphics.ext.Element.PositionType_.START ||
    +      goog.graphics.ext.coordinates.isSpecial(this.size_) ||
    +      goog.graphics.ext.coordinates.isSpecial(this.minSize_) ||
    +      goog.graphics.ext.coordinates.isSpecial(this.distance_);
    +};
    +
    +
    +/**
    + * The lazy loaded distance from the parent's top/left edge to this element's
    + * top/left edge expressed in the parent's coordinate system.  We cache this
    + * because it is most freqeuently requested by the element and it is easy to
    + * compute middle and end values from it.
    + * @type {?number}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.cachedValue_ = null;
    +
    +
    +/**
    + * A cache of computed x coordinates.
    + * @type {Object}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.coordinateCache_ = null;
    +
    +
    +/**
    + * The minimum width/height of this element, as specified by the caller.
    + * @type {string|number}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.minSize_ = 0;
    +
    +
    +/**
    + * The width/height of this object, as specified by the caller.
    + * @type {string|number}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.size_ = 0;
    +
    +
    +/**
    + * The coordinate of this object, as specified by the caller.  The type of
    + * coordinate is specified by distanceType_.
    + * @type {string|number}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.distance_ = 0;
    +
    +
    +/**
    + * The coordinate type specified by distance_.
    + * @type {goog.graphics.ext.Element.PositionType_}
    + * @private
    + */
    +goog.graphics.ext.Element.Position_.prototype.distanceType_ =
    +    goog.graphics.ext.Element.PositionType_.START;
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/element_test.html b/mapbox-gl/closure-library/closure/goog/graphics/ext/element_test.html
    new file mode 100644
    index 0000000000..293cb34548
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/element_test.html
    @@ -0,0 +1,144 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.ext.Element</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.graphics');
    +  goog.require('goog.graphics.ext');
    +  goog.require('goog.testing.StrictMock');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<div id="root" style="display: none"></div>
    +<script>
    +  var el, graphics, mockWrapper;
    +
    +  function setUp() {
    +    var div = document.getElementById('root');
    +    graphics = new goog.graphics.ext.Graphics(100, 100, 200, 200);
    +    div.innerHTML = '';
    +    graphics.render(div);
    +
    +    mockWrapper = new goog.testing.StrictMock(goog.graphics.Element);
    +  }
    +
    +  function tearDown() {
    +    mockWrapper.$verify();
    +  }
    +
    +  function assertPosition(fn, left, top, opt_width, opt_height) {
    +    mockWrapper.setTransformation(0, 0, 0, 5, 5);
    +    mockWrapper.setTransformation(left, top, 0,
    +        (opt_width || 10) / 2, (opt_height || 10) / 2);
    +    mockWrapper.$replay();
    +
    +    el = new goog.graphics.ext.Element(graphics, mockWrapper);
    +    el.setSize(10, 10);
    +    fn();
    +  }
    +
    +  function testLeft() {
    +    assertPosition(function() {
    +      el.setLeft(10);
    +    }, 10, 0);
    +    assertFalse(el.isParentDependent());
    +  }
    +
    +  function testLeftPercent() {
    +    assertPosition(function() {
    +      el.setLeft('10%');
    +    }, 20, 0);
    +  }
    +
    +  function testCenter() {
    +    assertPosition(function() {
    +      el.setCenter(0);
    +    }, 95, 0);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testCenterPercent() {
    +    assertPosition(function() {
    +      el.setCenter('10%');
    +    }, 115, 0);
    +  }
    +
    +  function testRight() {
    +    assertPosition(function() {
    +      el.setRight(10);
    +    }, 180, 0);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testRightPercent() {
    +    assertPosition(function() {
    +      el.setRight('10%');
    +    }, 170, 0);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testTop() {
    +    assertPosition(function() {
    +      el.setTop(10);
    +    }, 0, 10);
    +    assertFalse(el.isParentDependent());
    +  }
    +
    +  function testTopPercent() {
    +    assertPosition(function() {
    +      el.setTop('10%');
    +    }, 0, 20);
    +  }
    +
    +  function testMiddle() {
    +    assertPosition(function() {
    +      el.setMiddle(0);
    +    }, 0, 95);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testMiddlePercent() {
    +    assertPosition(function() {
    +      el.setMiddle('10%');
    +    }, 0, 115);
    +  }
    +
    +  function testBottom() {
    +    assertPosition(function() {
    +      el.setBottom(10);
    +    }, 0, 180);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testBottomPercent() {
    +    assertPosition(function() {
    +      el.setBottom('10%');
    +    }, 0, 170);
    +    assertTrue(el.isParentDependent());
    +  }
    +
    +  function testSize() {
    +    assertPosition(function() {
    +      el.setSize(100, 100);
    +    }, 0, 0, 100, 100);
    +    assertFalse(el.isParentDependent());
    +  }
    +
    +  function testSizePercent() {
    +    assertPosition(function() {
    +      el.setSize('10%', '20%');
    +    }, 0, 0, 20, 40);
    +    assertTrue(el.isParentDependent());
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/ellipse.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/ellipse.js
    new file mode 100644
    index 0000000000..db77524a02
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/ellipse.js
    @@ -0,0 +1,60 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around ellipses.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Ellipse');
    +
    +goog.require('goog.graphics.ext.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Wrapper for a graphics ellipse element.
    + * @param {goog.graphics.ext.Group} group Parent for this element.
    + * @constructor
    + * @extends {goog.graphics.ext.StrokeAndFillElement}
    + * @final
    + */
    +goog.graphics.ext.Ellipse = function(group) {
    +  // Initialize with some stock values.
    +  var wrapper = group.getGraphicsImplementation().drawEllipse(1, 1, 2, 2, null,
    +      null, group.getWrapper());
    +  goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
    +};
    +goog.inherits(goog.graphics.ext.Ellipse,
    +              goog.graphics.ext.StrokeAndFillElement);
    +
    +
    +/**
    + * Redraw the ellipse.  Called when the coordinate system is changed.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Ellipse.prototype.redraw = function() {
    +  goog.graphics.ext.Ellipse.superClass_.redraw.call(this);
    +
    +  // Our position is already transformed in transform_, but because this is an
    +  // ellipse we need to position the center.
    +  var xRadius = this.getWidth() / 2;
    +  var yRadius = this.getHeight() / 2;
    +  var wrapper = this.getWrapper();
    +  wrapper.setCenter(xRadius, yRadius);
    +  wrapper.setRadius(xRadius, yRadius);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/ext.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/ext.js
    new file mode 100644
    index 0000000000..c991b48731
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/ext.js
    @@ -0,0 +1,29 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Extended graphics namespace.
    + */
    +
    +
    +goog.provide('goog.graphics.ext');
    +
    +goog.require('goog.graphics.ext.Ellipse');
    +goog.require('goog.graphics.ext.Graphics');
    +goog.require('goog.graphics.ext.Group');
    +goog.require('goog.graphics.ext.Image');
    +goog.require('goog.graphics.ext.Rectangle');
    +goog.require('goog.graphics.ext.Shape');
    +goog.require('goog.graphics.ext.coordinates');
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/graphics.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/graphics.js
    new file mode 100644
    index 0000000000..a3c1a54517
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/graphics.js
    @@ -0,0 +1,218 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Graphics surface type.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Graphics');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.graphics');
    +goog.require('goog.graphics.ext.Group');
    +
    +
    +
    +/**
    + * Wrapper for a graphics surface.
    + * @param {string|number} width The width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The height in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The coordinate width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight The coordinate height. - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @param {boolean=} opt_isSimple Flag used to indicate the graphics object will
    + *     be drawn to in a single pass, and the fastest implementation for this
    + *     scenario should be favored.  NOTE: Setting to true may result in
    + *     degradation of text support.
    + * @constructor
    + * @extends {goog.graphics.ext.Group}
    + * @final
    + */
    +goog.graphics.ext.Graphics = function(width, height, opt_coordWidth,
    +    opt_coordHeight, opt_domHelper, opt_isSimple) {
    +  var surface = opt_isSimple ?
    +      goog.graphics.createSimpleGraphics(width, height,
    +          opt_coordWidth, opt_coordHeight, opt_domHelper) :
    +      goog.graphics.createGraphics(width, height,
    +          opt_coordWidth, opt_coordHeight, opt_domHelper);
    +  this.implementation_ = surface;
    +
    +  goog.graphics.ext.Group.call(this, null, surface.getCanvasElement());
    +
    +  goog.events.listen(surface, goog.events.EventType.RESIZE,
    +      this.updateChildren, false, this);
    +};
    +goog.inherits(goog.graphics.ext.Graphics, goog.graphics.ext.Group);
    +
    +
    +/**
    + * The root level graphics implementation.
    + * @type {goog.graphics.AbstractGraphics}
    + * @private
    + */
    +goog.graphics.ext.Graphics.prototype.implementation_;
    +
    +
    +/**
    + * @return {goog.graphics.AbstractGraphics} The graphics implementation layer.
    + */
    +goog.graphics.ext.Graphics.prototype.getImplementation = function() {
    +  return this.implementation_;
    +};
    +
    +
    +/**
    + * Changes the coordinate size.
    + * @param {number} coordWidth The coordinate width.
    + * @param {number} coordHeight The coordinate height.
    + */
    +goog.graphics.ext.Graphics.prototype.setCoordSize = function(coordWidth,
    +                                                             coordHeight) {
    +  this.implementation_.setCoordSize(coordWidth, coordHeight);
    +  goog.graphics.ext.Graphics.superClass_.setSize.call(this, coordWidth,
    +      coordHeight);
    +};
    +
    +
    +/**
    + * @return {goog.math.Size} The coordinate size.
    + */
    +goog.graphics.ext.Graphics.prototype.getCoordSize = function() {
    +  return this.implementation_.getCoordSize();
    +};
    +
    +
    +/**
    + * Changes the coordinate system position.
    + * @param {number} left The coordinate system left bound.
    + * @param {number} top The coordinate system top bound.
    + */
    +goog.graphics.ext.Graphics.prototype.setCoordOrigin = function(left, top) {
    +  this.implementation_.setCoordOrigin(left, top);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Coordinate} The coordinate system position.
    + */
    +goog.graphics.ext.Graphics.prototype.getCoordOrigin = function() {
    +  return this.implementation_.getCoordOrigin();
    +};
    +
    +
    +/**
    + * Change the size of the canvas.
    + * @param {number} pixelWidth The width in pixels.
    + * @param {number} pixelHeight The height in pixels.
    + */
    +goog.graphics.ext.Graphics.prototype.setPixelSize = function(pixelWidth,
    +                                                        pixelHeight) {
    +  this.implementation_.setSize(pixelWidth, pixelHeight);
    +
    +  var coordSize = this.getCoordSize();
    +  goog.graphics.ext.Graphics.superClass_.setSize.call(this, coordSize.width,
    +      coordSize.height);
    +};
    +
    +
    +/**
    + * @return {goog.math.Size?} Returns the number of pixels spanned by the
    + *     surface, or null if the size could not be computed due to the size being
    + *     specified in percentage points and the component not being in the
    + *     document.
    + */
    +goog.graphics.ext.Graphics.prototype.getPixelSize = function() {
    +  return this.implementation_.getPixelSize();
    +};
    +
    +
    +/**
    + * @return {number} The coordinate width of the canvas.
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.getWidth = function() {
    +  return this.implementation_.getCoordSize().width;
    +};
    +
    +
    +/**
    + * @return {number} The coordinate width of the canvas.
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.getHeight = function() {
    +  return this.implementation_.getCoordSize().height;
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the x direction.
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.getPixelScaleX = function() {
    +  return this.implementation_.getPixelScaleX();
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of pixels per unit in the y direction.
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.getPixelScaleY = function() {
    +  return this.implementation_.getPixelScaleY();
    +};
    +
    +
    +/**
    + * @return {Element} The root element of the graphics surface.
    + */
    +goog.graphics.ext.Graphics.prototype.getElement = function() {
    +  return this.implementation_.getElement();
    +};
    +
    +
    +/**
    + * Renders the underlying graphics.
    + *
    + * @param {Element} parentElement Parent element to render the component into.
    + */
    +goog.graphics.ext.Graphics.prototype.render = function(parentElement) {
    +  this.implementation_.render(parentElement);
    +};
    +
    +
    +/**
    + * Never transform a surface.
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.transform = goog.nullFunction;
    +
    +
    +/**
    + * Called from the parent class, this method resets any pre-computed positions
    + * and sizes.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Graphics.prototype.redraw = function() {
    +  this.transformChildren();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/group.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/group.js
    new file mode 100644
    index 0000000000..03479ad4bc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/group.js
    @@ -0,0 +1,216 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thicker wrapper around graphics groups.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Group');
    +
    +goog.require('goog.array');
    +goog.require('goog.graphics.ext.Element');
    +
    +
    +
    +/**
    + * Wrapper for a graphics group.
    + * @param {goog.graphics.ext.Group} group Parent for this element. Can
    + *     be null if this is a Graphics instance.
    + * @param {goog.graphics.GroupElement=} opt_wrapper The thin wrapper
    + *     to wrap. If omitted, a new group will be created. Must be included
    + *     when group is null.
    + * @constructor
    + * @extends {goog.graphics.ext.Element}
    + */
    +goog.graphics.ext.Group = function(group, opt_wrapper) {
    +  opt_wrapper = opt_wrapper || group.getGraphicsImplementation().createGroup(
    +      group.getWrapper());
    +  goog.graphics.ext.Element.call(this, group, opt_wrapper);
    +
    +  /**
    +   * Array of child elements this group contains.
    +   * @type {Array<goog.graphics.ext.Element>}
    +   * @private
    +   */
    +  this.children_ = [];
    +};
    +goog.inherits(goog.graphics.ext.Group, goog.graphics.ext.Element);
    +
    +
    +/**
    + * Add an element to the group.  This should be treated as package local, as
    + * it is called by the draw* methods.
    + * @param {!goog.graphics.ext.Element} element The element to add.
    + * @param {boolean=} opt_chain Whether this addition is part of a longer set
    + *     of element additions.
    + */
    +goog.graphics.ext.Group.prototype.addChild = function(element, opt_chain) {
    +  if (!goog.array.contains(this.children_, element)) {
    +    this.children_.push(element);
    +  }
    +
    +  var transformed = this.growToFit_(element);
    +
    +  if (element.isParentDependent()) {
    +    element.parentTransform();
    +  }
    +
    +  if (!opt_chain && element.isPendingTransform()) {
    +    element.reset();
    +  }
    +
    +  if (transformed) {
    +    this.reset();
    +  }
    +};
    +
    +
    +/**
    + * Remove an element from the group.
    + * @param {goog.graphics.ext.Element} element The element to remove.
    + */
    +goog.graphics.ext.Group.prototype.removeChild = function(element) {
    +  goog.array.remove(this.children_, element);
    +
    +  // TODO(robbyw): shape.fireEvent('delete')
    +
    +  this.getGraphicsImplementation().removeElement(element.getWrapper());
    +};
    +
    +
    +/**
    + * Calls the given function on each of this component's children in order.  If
    + * {@code opt_obj} is provided, it will be used as the 'this' object in the
    + * function when called.  The function should take two arguments:  the child
    + * component and its 0-based index.  The return value is ignored.
    + * @param {Function} f The function to call for every child component; should
    + *    take 2 arguments (the child and its index).
    + * @param {Object=} opt_obj Used as the 'this' object in f when called.
    + */
    +goog.graphics.ext.Group.prototype.forEachChild = function(f, opt_obj) {
    +  if (this.children_) {
    +    goog.array.forEach(this.children_, f, opt_obj);
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.graphics.GroupElement} The underlying thin wrapper.
    + * @override
    + */
    +goog.graphics.ext.Group.prototype.getWrapper;
    +
    +
    +/**
    + * Reset the element.
    + * @override
    + */
    +goog.graphics.ext.Group.prototype.reset = function() {
    +  goog.graphics.ext.Group.superClass_.reset.call(this);
    +
    +  this.updateChildren();
    +};
    +
    +
    +/**
    + * Called from the parent class, this method resets any pre-computed positions
    + * and sizes.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Group.prototype.redraw = function() {
    +  this.getWrapper().setSize(this.getWidth(), this.getHeight());
    +  this.transformChildren();
    +};
    +
    +
    +/**
    + * Transform the children that need to be transformed.
    + * @protected
    + */
    +goog.graphics.ext.Group.prototype.transformChildren = function() {
    +  this.forEachChild(function(child) {
    +    if (child.isParentDependent()) {
    +      child.parentTransform();
    +    }
    +  });
    +};
    +
    +
    +/**
    + * As part of the reset process, update child elements.
    + */
    +goog.graphics.ext.Group.prototype.updateChildren = function() {
    +  this.forEachChild(function(child) {
    +    if (child.isParentDependent() || child.isPendingTransform()) {
    +      child.reset();
    +    } else if (child.updateChildren) {
    +      child.updateChildren();
    +    }
    +  });
    +};
    +
    +
    +/**
    + * When adding an element, grow this group's bounds to fit it.
    + * @param {!goog.graphics.ext.Element} element The added element.
    + * @return {boolean} Whether the size of this group changed.
    + * @private
    + */
    +goog.graphics.ext.Group.prototype.growToFit_ = function(element) {
    +  var transformed = false;
    +
    +  var x = element.getMaxX();
    +  if (x > this.getWidth()) {
    +    this.setMinWidth(x);
    +    transformed = true;
    +  }
    +
    +  var y = element.getMaxY();
    +  if (y > this.getHeight()) {
    +    this.setMinHeight(y);
    +    transformed = true;
    +  }
    +
    +  return transformed;
    +};
    +
    +
    +/**
    + * @return {number} The width of the element's coordinate space.
    + */
    +goog.graphics.ext.Group.prototype.getCoordinateWidth = function() {
    +  return this.getWidth();
    +};
    +
    +
    +/**
    + * @return {number} The height of the element's coordinate space.
    + */
    +goog.graphics.ext.Group.prototype.getCoordinateHeight = function() {
    +  return this.getHeight();
    +};
    +
    +
    +/**
    + * Remove all drawing elements from the group.
    + */
    +goog.graphics.ext.Group.prototype.clear = function() {
    +  while (this.children_.length) {
    +    this.removeChild(this.children_[0]);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/image.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/image.js
    new file mode 100644
    index 0000000000..ec24e1d85e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/image.js
    @@ -0,0 +1,64 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around images.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Image');
    +
    +goog.require('goog.graphics.ext.Element');
    +
    +
    +
    +/**
    + * Wrapper for a graphics image element.
    + * @param {goog.graphics.ext.Group} group Parent for this element.
    + * @param {string} src The path to the image to display.
    + * @constructor
    + * @extends {goog.graphics.ext.Element}
    + * @final
    + */
    +goog.graphics.ext.Image = function(group, src) {
    +  // Initialize with some stock values.
    +  var wrapper = group.getGraphicsImplementation().drawImage(0, 0, 1, 1, src,
    +      group.getWrapper());
    +  goog.graphics.ext.Element.call(this, group, wrapper);
    +};
    +goog.inherits(goog.graphics.ext.Image, goog.graphics.ext.Element);
    +
    +
    +/**
    + * Redraw the image.  Called when the coordinate system is changed.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Image.prototype.redraw = function() {
    +  goog.graphics.ext.Image.superClass_.redraw.call(this);
    +
    +  // Our position is already handled bu transform_.
    +  this.getWrapper().setSize(this.getWidth(), this.getHeight());
    +};
    +
    +
    +/**
    + * Update the source of the image.
    + * @param {string} src  Source of the image.
    + */
    +goog.graphics.ext.Image.prototype.setSource = function(src) {
    +  this.getWrapper().setSource(src);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/path.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/path.js
    new file mode 100644
    index 0000000000..de550aa497
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/path.js
    @@ -0,0 +1,142 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around paths.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Path');
    +
    +goog.require('goog.graphics.AffineTransform');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.math.Rect');
    +
    +
    +
    +/**
    + * Creates a path object
    + * @constructor
    + * @extends {goog.graphics.Path}
    + * @final
    + */
    +goog.graphics.ext.Path = function() {
    +  goog.graphics.Path.call(this);
    +};
    +goog.inherits(goog.graphics.ext.Path, goog.graphics.Path);
    +
    +
    +/**
    + * Optional cached or user specified bounding box.  A user may wish to
    + * precompute a bounding box to save time and include more accurate
    + * computations.
    + * @type {goog.math.Rect?}
    + * @private
    + */
    +goog.graphics.ext.Path.prototype.bounds_ = null;
    +
    +
    +/**
    + * Clones the path.
    + * @return {!goog.graphics.ext.Path} A clone of this path.
    + * @override
    + */
    +goog.graphics.ext.Path.prototype.clone = function() {
    +  var output = /** @type {goog.graphics.ext.Path} */
    +      (goog.graphics.ext.Path.superClass_.clone.call(this));
    +  output.bounds_ = this.bounds_ && this.bounds_.clone();
    +  return output;
    +};
    +
    +
    +/**
    + * Transforms the path. Only simple paths are transformable. Attempting
    + * to transform a non-simple path will throw an error.
    + * @param {!goog.graphics.AffineTransform} tx The transformation to perform.
    + * @return {!goog.graphics.ext.Path} The path itself.
    + * @override
    + */
    +goog.graphics.ext.Path.prototype.transform = function(tx) {
    +  goog.graphics.ext.Path.superClass_.transform.call(this, tx);
    +
    +  // Make sure the precomputed bounds are cleared when the path is transformed.
    +  this.bounds_ = null;
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Modify the bounding box of the path.  This may cause the path to be
    + * simplified (i.e. arcs converted to curves) as a side-effect.
    + * @param {number} deltaX How far to translate the x coordinates.
    + * @param {number} deltaY How far to translate the y coordinates.
    + * @param {number} xFactor After translation, all x coordinates are multiplied
    + *     by this number.
    + * @param {number} yFactor After translation, all y coordinates are multiplied
    + *     by this number.
    + * @return {!goog.graphics.ext.Path} The path itself.
    + */
    +goog.graphics.ext.Path.prototype.modifyBounds = function(deltaX, deltaY,
    +    xFactor, yFactor) {
    +  if (!this.isSimple()) {
    +    var simple = goog.graphics.Path.createSimplifiedPath(this);
    +    this.clear();
    +    this.appendPath(simple);
    +  }
    +
    +  return this.transform(goog.graphics.AffineTransform.getScaleInstance(
    +      xFactor, yFactor).translate(deltaX, deltaY));
    +};
    +
    +
    +/**
    + * Set the precomputed bounds.
    + * @param {goog.math.Rect?} bounds The bounds to use, or set to null to clear
    + *     and recompute on the next call to getBoundingBox.
    + */
    +goog.graphics.ext.Path.prototype.useBoundingBox = function(bounds) {
    +  this.bounds_ = bounds && bounds.clone();
    +};
    +
    +
    +/**
    + * @return {goog.math.Rect?} The bounding box of the path, or null if the
    + *     path is empty.
    + */
    +goog.graphics.ext.Path.prototype.getBoundingBox = function() {
    +  if (!this.bounds_ && !this.isEmpty()) {
    +    var minY;
    +    var minX = minY = Number.POSITIVE_INFINITY;
    +    var maxY;
    +    var maxX = maxY = Number.NEGATIVE_INFINITY;
    +
    +    var simplePath = this.isSimple() ? this :
    +        goog.graphics.Path.createSimplifiedPath(this);
    +    simplePath.forEachSegment(function(type, points) {
    +      for (var i = 0, len = points.length; i < len; i += 2) {
    +        minX = Math.min(minX, points[i]);
    +        maxX = Math.max(maxX, points[i]);
    +        minY = Math.min(minY, points[i + 1]);
    +        maxY = Math.max(maxY, points[i + 1]);
    +      }
    +    });
    +
    +    this.bounds_ = new goog.math.Rect(minX, minY, maxX - minX, maxY - minY);
    +  }
    +
    +  return this.bounds_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/path_test.html b/mapbox-gl/closure-library/closure/goog/graphics/ext/path_test.html
    new file mode 100644
    index 0000000000..61dc55ce89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/path_test.html
    @@ -0,0 +1,46 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.ext.Path</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.graphics');
    +  goog.require('goog.graphics.ext.Path');
    +  goog.require('goog.testing.graphics');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +
    +<script>
    +  function testClone() {
    +    var path = new goog.graphics.ext.Path().moveTo(0, 0).lineTo(1, 1).
    +        curveTo(2, 2, 3, 3, 4, 4).arc(5, 5, 6, 6, 0, 90, false).close();
    +    assertTrue('Cloned path is a goog.graphics.ext.Path',
    +        path instanceof goog.graphics.ext.Path);
    +  }
    +
    +  function testBoundingBox() {
    +    var path = new goog.graphics.ext.Path().moveTo(0, 0).lineTo(1, 1).
    +        curveTo(2, 2, 3, 3, 4, 4).close();
    +    assertTrue('Bounding box is correct', goog.math.Rect.equals(
    +        path.getBoundingBox(), new goog.math.Rect(0, 0, 4, 4)));
    +  }
    +
    +  function testModifyBounds() {
    +    var path1 = new goog.graphics.ext.Path().moveTo(0, 0).lineTo(1, 1).
    +        curveTo(2, 2, 3, 3, 4, 4).close();
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', -2, -2, 'L', 0, 0, 'C', 2, 2, 4, 4, 6, 6, 'X'],
    +        path1.modifyBounds(-1, -1, 2, 2));
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/rectangle.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/rectangle.js
    new file mode 100644
    index 0000000000..d05c8b1bda
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/rectangle.js
    @@ -0,0 +1,55 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around rectangles.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Rectangle');
    +
    +goog.require('goog.graphics.ext.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Wrapper for a graphics rectangle element.
    + * @param {goog.graphics.ext.Group} group Parent for this element.
    + * @constructor
    + * @extends {goog.graphics.ext.StrokeAndFillElement}
    + * @final
    + */
    +goog.graphics.ext.Rectangle = function(group) {
    +  // Initialize with some stock values.
    +  var wrapper = group.getGraphicsImplementation().drawRect(0, 0, 1, 1, null,
    +      null, group.getWrapper());
    +  goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
    +};
    +goog.inherits(goog.graphics.ext.Rectangle,
    +              goog.graphics.ext.StrokeAndFillElement);
    +
    +
    +/**
    + * Redraw the rectangle.  Called when the coordinate system is changed.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Rectangle.prototype.redraw = function() {
    +  goog.graphics.ext.Rectangle.superClass_.redraw.call(this);
    +
    +  // Our position is already handled by transform_.
    +  this.getWrapper().setSize(this.getWidth(), this.getHeight());
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/shape.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/shape.js
    new file mode 100644
    index 0000000000..3f80e82227
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/shape.js
    @@ -0,0 +1,145 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around shapes with custom paths.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.Shape');
    +
    +goog.require('goog.graphics.ext.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Wrapper for a graphics shape element.
    + * @param {goog.graphics.ext.Group} group Parent for this element.
    + * @param {!goog.graphics.ext.Path} path  The path to draw.
    + * @param {boolean=} opt_autoSize Optional flag to specify the path should
    + *     automatically resize to fit the element.  Defaults to false.
    + * @constructor
    + * @extends {goog.graphics.ext.StrokeAndFillElement}
    + * @final
    + */
    +goog.graphics.ext.Shape = function(group, path, opt_autoSize) {
    +  this.autoSize_ = !!opt_autoSize;
    +
    +  var graphics = group.getGraphicsImplementation();
    +  var wrapper = graphics.drawPath(path, null, null,
    +      group.getWrapper());
    +  goog.graphics.ext.StrokeAndFillElement.call(this, group, wrapper);
    +  this.setPath(path);
    +};
    +goog.inherits(goog.graphics.ext.Shape, goog.graphics.ext.StrokeAndFillElement);
    +
    +
    +/**
    + * Whether or not to automatically resize the shape's path when the element
    + * itself is resized.
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.ext.Shape.prototype.autoSize_ = false;
    +
    +
    +/**
    + * The original path, specified by the caller.
    + * @type {goog.graphics.Path}
    + * @private
    + */
    +goog.graphics.ext.Shape.prototype.path_;
    +
    +
    +/**
    + * The bounding box of the original path.
    + * @type {goog.math.Rect?}
    + * @private
    + */
    +goog.graphics.ext.Shape.prototype.boundingBox_ = null;
    +
    +
    +/**
    + * The scaled path.
    + * @type {goog.graphics.Path}
    + * @private
    + */
    +goog.graphics.ext.Shape.prototype.scaledPath_;
    +
    +
    +/**
    + * Get the path drawn by this shape.
    + * @return {goog.graphics.Path?} The path drawn by this shape.
    + */
    +goog.graphics.ext.Shape.prototype.getPath = function() {
    +  return this.path_;
    +};
    +
    +
    +/**
    + * Set the path to draw.
    + * @param {goog.graphics.ext.Path} path The path to draw.
    + */
    +goog.graphics.ext.Shape.prototype.setPath = function(path) {
    +  this.path_ = path;
    +
    +  if (this.autoSize_) {
    +    this.boundingBox_ = path.getBoundingBox();
    +  }
    +
    +  this.scaleAndSetPath_();
    +};
    +
    +
    +/**
    + * Scale the internal path to fit.
    + * @private
    + */
    +goog.graphics.ext.Shape.prototype.scaleAndSetPath_ = function() {
    +  this.scaledPath_ = this.boundingBox_ ? this.path_.clone().modifyBounds(
    +      -this.boundingBox_.left, -this.boundingBox_.top,
    +      this.getWidth() / (this.boundingBox_.width || 1),
    +      this.getHeight() / (this.boundingBox_.height || 1)) : this.path_;
    +
    +  var wrapper = this.getWrapper();
    +  if (wrapper) {
    +    wrapper.setPath(this.scaledPath_);
    +  }
    +};
    +
    +
    +/**
    + * Redraw the ellipse.  Called when the coordinate system is changed.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Shape.prototype.redraw = function() {
    +  goog.graphics.ext.Shape.superClass_.redraw.call(this);
    +  if (this.autoSize_) {
    +    this.scaleAndSetPath_();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the shape is parent dependent.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.Shape.prototype.checkParentDependent = function() {
    +  return this.autoSize_ ||
    +      goog.graphics.ext.Shape.superClass_.checkParentDependent.call(this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/ext/strokeandfillelement.js b/mapbox-gl/closure-library/closure/goog/graphics/ext/strokeandfillelement.js
    new file mode 100644
    index 0000000000..4ad69f316a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/ext/strokeandfillelement.js
    @@ -0,0 +1,70 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thick wrapper around elements with stroke and fill.
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +
    +goog.provide('goog.graphics.ext.StrokeAndFillElement');
    +
    +goog.require('goog.graphics.ext.Element');
    +
    +
    +
    +/**
    + * Interface for a graphics element that has a stroke and fill.
    + * This is the base interface for ellipse, rectangle and other
    + * shape interfaces.
    + * You should not construct objects from this constructor. Use a subclass.
    + * @param {goog.graphics.ext.Group} group Parent for this element.
    + * @param {goog.graphics.StrokeAndFillElement} wrapper The thin wrapper to wrap.
    + * @constructor
    + * @extends {goog.graphics.ext.Element}
    + */
    +goog.graphics.ext.StrokeAndFillElement = function(group, wrapper) {
    +  goog.graphics.ext.Element.call(this, group, wrapper);
    +};
    +goog.inherits(goog.graphics.ext.StrokeAndFillElement,
    +    goog.graphics.ext.Element);
    +
    +
    +/**
    + * Sets the fill for this element.
    + * @param {goog.graphics.Fill?} fill The fill object.
    + */
    +goog.graphics.ext.StrokeAndFillElement.prototype.setFill = function(fill) {
    +  this.getWrapper().setFill(fill);
    +};
    +
    +
    +/**
    + * Sets the stroke for this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke object.
    + */
    +goog.graphics.ext.StrokeAndFillElement.prototype.setStroke = function(stroke) {
    +  this.getWrapper().setStroke(stroke);
    +};
    +
    +
    +/**
    + * Redraw the rectangle.  Called when the coordinate system is changed.
    + * @protected
    + * @override
    + */
    +goog.graphics.ext.StrokeAndFillElement.prototype.redraw = function() {
    +  this.getWrapper().reapplyStroke();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/fill.js b/mapbox-gl/closure-library/closure/goog/graphics/fill.js
    new file mode 100644
    index 0000000000..92e460e253
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/fill.js
    @@ -0,0 +1,46 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a fill goog.graphics.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.Fill');
    +
    +
    +
    +/**
    + * Creates a fill object
    + * @constructor
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.Fill = function() {};
    +
    +
    +/**
    + * @return {string} The start color of a gradient fill.
    + */
    +goog.graphics.Fill.prototype.getColor1 = goog.abstractMethod;
    +
    +
    +/**
    + * @return {string} The end color of a gradient fill.
    + */
    +goog.graphics.Fill.prototype.getColor2 = goog.abstractMethod;
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/font.js b/mapbox-gl/closure-library/closure/goog/graphics/font.js
    new file mode 100644
    index 0000000000..f58bf41ad0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/font.js
    @@ -0,0 +1,64 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a font to be used with a Renderer.
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/graphics/basicelements.html
    + */
    +
    +
    +goog.provide('goog.graphics.Font');
    +
    +
    +
    +/**
    + * This class represents a font to be used with a renderer.
    + * @param {number} size  The font size.
    + * @param {string} family  The font family.
    + * @constructor
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.Font = function(size, family) {
    +  /**
    +   * Font size.
    +   * @type {number}
    +   */
    +  this.size = size;
    +  // TODO(arv): Is this in pixels or drawing units based on the coord size?
    +
    +  /**
    +   * The name of the font family to use, can be a comma separated string.
    +   * @type {string}
    +   */
    +  this.family = family;
    +};
    +
    +
    +/**
    + * Indication if text should be bolded
    + * @type {boolean}
    + */
    +goog.graphics.Font.prototype.bold = false;
    +
    +
    +/**
    + * Indication if text should be in italics
    + * @type {boolean}
    + */
    +goog.graphics.Font.prototype.italic = false;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/graphics.js b/mapbox-gl/closure-library/closure/goog/graphics/graphics.js
    new file mode 100644
    index 0000000000..0bde5b5b3d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/graphics.js
    @@ -0,0 +1,142 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Graphics utility functions and factory methods.
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/graphics/advancedcoordinates.html
    + * @see ../demos/graphics/advancedcoordinates2.html
    + * @see ../demos/graphics/basicelements.html
    + * @see ../demos/graphics/events.html
    + * @see ../demos/graphics/modifyelements.html
    + * @see ../demos/graphics/tiger.html
    + */
    +
    +
    +goog.provide('goog.graphics');
    +
    +goog.require('goog.dom');
    +goog.require('goog.graphics.CanvasGraphics');
    +goog.require('goog.graphics.SvgGraphics');
    +goog.require('goog.graphics.VmlGraphics');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Returns an instance of goog.graphics.AbstractGraphics that knows how to draw
    + * for the current platform (A factory for the proper Graphics implementation)
    + * @param {string|number} width The width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The height in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The optional coordinate width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight The optional coordinate height - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @return {!goog.graphics.AbstractGraphics} The created instance.
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.createGraphics = function(width, height, opt_coordWidth,
    +    opt_coordHeight, opt_domHelper) {
    +  var graphics;
    +  // On IE9 and above, SVG is available, except in compatibility mode.
    +  // We check createElementNS on document object that is not exist in
    +  // compatibility mode.
    +  if (goog.userAgent.IE &&
    +      (!goog.userAgent.isVersionOrHigher('9') ||
    +       !(opt_domHelper || goog.dom.getDomHelper()).
    +           getDocument().createElementNS)) {
    +    graphics = new goog.graphics.VmlGraphics(width, height,
    +        opt_coordWidth, opt_coordHeight, opt_domHelper);
    +  } else if (goog.userAgent.WEBKIT &&
    +             (!goog.userAgent.isVersionOrHigher('420') ||
    +              goog.userAgent.MOBILE)) {
    +    graphics = new goog.graphics.CanvasGraphics(width, height,
    +        opt_coordWidth, opt_coordHeight, opt_domHelper);
    +  } else {
    +    graphics = new goog.graphics.SvgGraphics(width, height,
    +        opt_coordWidth, opt_coordHeight, opt_domHelper);
    +  }
    +
    +  // Create the dom now, because all drawing methods require that the
    +  // main dom element (the canvas) has been already created.
    +  graphics.createDom();
    +
    +  return graphics;
    +};
    +
    +
    +/**
    + * Returns an instance of goog.graphics.AbstractGraphics that knows how to draw
    + * for the current platform (A factory for the proper Graphics implementation)
    + * @param {string|number} width The width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The height in pixels.   Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The optional coordinate width, defaults to
    + *     same as width.
    + * @param {?number=} opt_coordHeight The optional coordinate height, defaults to
    + *     same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @return {!goog.graphics.AbstractGraphics} The created instance.
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.createSimpleGraphics = function(width, height,
    +    opt_coordWidth, opt_coordHeight, opt_domHelper) {
    +  if (goog.userAgent.MAC && goog.userAgent.GECKO &&
    +      !goog.userAgent.isVersionOrHigher('1.9a')) {
    +    // Canvas is 6x faster than SVG on Mac FF 2.0
    +    var graphics = new goog.graphics.CanvasGraphics(
    +        width, height, opt_coordWidth, opt_coordHeight,
    +        opt_domHelper);
    +    graphics.createDom();
    +    return graphics;
    +  }
    +
    +  // Otherwise, defer to normal graphics object creation.
    +  return goog.graphics.createGraphics(width, height, opt_coordWidth,
    +      opt_coordHeight, opt_domHelper);
    +};
    +
    +
    +/**
    + * Static function to check if the current browser has Graphics support.
    + * @return {boolean} True if the current browser has Graphics support.
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.isBrowserSupported = function() {
    +  if (goog.userAgent.IE) {
    +    return goog.userAgent.isVersionOrHigher('5.5');
    +  }
    +  if (goog.userAgent.GECKO) {
    +    return goog.userAgent.isVersionOrHigher('1.8');
    +  }
    +  if (goog.userAgent.OPERA) {
    +    return goog.userAgent.isVersionOrHigher('9.0');
    +  }
    +  if (goog.userAgent.WEBKIT) {
    +    return goog.userAgent.isVersionOrHigher('412');
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/groupelement.js b/mapbox-gl/closure-library/closure/goog/graphics/groupelement.js
    new file mode 100644
    index 0000000000..9e60cd7ef7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/groupelement.js
    @@ -0,0 +1,58 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for graphics groups.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.GroupElement');
    +
    +goog.require('goog.graphics.Element');
    +
    +
    +
    +/**
    + * Interface for a graphics group element.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.Element}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.GroupElement = function(element, graphics) {
    +  goog.graphics.Element.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.GroupElement, goog.graphics.Element);
    +
    +
    +/**
    + * Remove all drawing elements from the group.
    + */
    +goog.graphics.GroupElement.prototype.clear = goog.abstractMethod;
    +
    +
    +/**
    + * Set the size of the group element.
    + * @param {number|string} width The width of the group element.
    + * @param {number|string} height The height of the group element.
    + */
    +goog.graphics.GroupElement.prototype.setSize = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/imageelement.js b/mapbox-gl/closure-library/closure/goog/graphics/imageelement.js
    new file mode 100644
    index 0000000000..2f2d9b792c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/imageelement.js
    @@ -0,0 +1,70 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for images.
    + */
    +
    +
    +goog.provide('goog.graphics.ImageElement');
    +
    +goog.require('goog.graphics.Element');
    +
    +
    +
    +/**
    + * Interface for a graphics image element.
    + * You should not construct objects from this constructor. Instead,
    + * you should use {@code goog.graphics.Graphics.drawImage} and it
    + * will return an implementation of this interface for you.
    + *
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.Element}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.ImageElement = function(element, graphics) {
    +  goog.graphics.Element.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.ImageElement, goog.graphics.Element);
    +
    +
    +/**
    + * Update the position of the image.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + */
    +goog.graphics.ImageElement.prototype.setPosition = goog.abstractMethod;
    +
    +
    +/**
    + * Update the size of the image.
    + *
    + * @param {number} width Width of image.
    + * @param {number} height Height of image.
    + */
    +goog.graphics.ImageElement.prototype.setSize = goog.abstractMethod;
    +
    +
    +/**
    + * Update the source of the image.
    + * @param {string} src Source of the image.
    + */
    +goog.graphics.ImageElement.prototype.setSource = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/lineargradient.js b/mapbox-gl/closure-library/closure/goog/graphics/lineargradient.js
    new file mode 100644
    index 0000000000..df59cbfe89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/lineargradient.js
    @@ -0,0 +1,175 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a gradient to be used with a Graphics implementor.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.LinearGradient');
    +
    +
    +goog.require('goog.asserts');
    +goog.require('goog.graphics.Fill');
    +
    +
    +
    +/**
    + * Creates an immutable linear gradient fill object.
    + *
    + * @param {number} x1 Start X position of the gradient.
    + * @param {number} y1 Start Y position of the gradient.
    + * @param {number} x2 End X position of the gradient.
    + * @param {number} y2 End Y position of the gradient.
    + * @param {string} color1 Start color of the gradient.
    + * @param {string} color2 End color of the gradient.
    + * @param {?number=} opt_opacity1 Start opacity of the gradient, both or neither
    + *     of opt_opacity1 and opt_opacity2 have to be set.
    + * @param {?number=} opt_opacity2 End opacity of the gradient.
    + * @constructor
    + * @extends {goog.graphics.Fill}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.LinearGradient =
    +    function(x1, y1, x2, y2, color1, color2, opt_opacity1, opt_opacity2) {
    +  /**
    +   * Start X position of the gradient.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x1_ = x1;
    +
    +  /**
    +   * Start Y position of the gradient.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y1_ = y1;
    +
    +  /**
    +   * End X position of the gradient.
    +   * @type {number}
    +   * @private
    +   */
    +  this.x2_ = x2;
    +
    +  /**
    +   * End Y position of the gradient.
    +   * @type {number}
    +   * @private
    +   */
    +  this.y2_ = y2;
    +
    +  /**
    +   * Start color of the gradient.
    +   * @type {string}
    +   * @private
    +   */
    +  this.color1_ = color1;
    +
    +  /**
    +   * End color of the gradient.
    +   * @type {string}
    +   * @private
    +   */
    +  this.color2_ = color2;
    +
    +  goog.asserts.assert(
    +      goog.isNumber(opt_opacity1) == goog.isNumber(opt_opacity2),
    +      'Both or neither of opt_opacity1 and opt_opacity2 have to be set.');
    +
    +  /**
    +   * Start opacity of the gradient.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.opacity1_ = goog.isDef(opt_opacity1) ? opt_opacity1 : null;
    +
    +  /**
    +   * End opacity of the gradient.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.opacity2_ = goog.isDef(opt_opacity2) ? opt_opacity2 : null;
    +};
    +goog.inherits(goog.graphics.LinearGradient, goog.graphics.Fill);
    +
    +
    +/**
    + * @return {number} The start X position of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getX1 = function() {
    +  return this.x1_;
    +};
    +
    +
    +/**
    + * @return {number} The start Y position of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getY1 = function() {
    +  return this.y1_;
    +};
    +
    +
    +/**
    + * @return {number} The end X position of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getX2 = function() {
    +  return this.x2_;
    +};
    +
    +
    +/**
    + * @return {number} The end Y position of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getY2 = function() {
    +  return this.y2_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.graphics.LinearGradient.prototype.getColor1 = function() {
    +  return this.color1_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.graphics.LinearGradient.prototype.getColor2 = function() {
    +  return this.color2_;
    +};
    +
    +
    +/**
    + * @return {?number} The start opacity of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getOpacity1 = function() {
    +  return this.opacity1_;
    +};
    +
    +
    +/**
    + * @return {?number} The end opacity of the gradient.
    + */
    +goog.graphics.LinearGradient.prototype.getOpacity2 = function() {
    +  return this.opacity2_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/path.js b/mapbox-gl/closure-library/closure/goog/graphics/path.js
    new file mode 100644
    index 0000000000..c19f2d9db3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/path.js
    @@ -0,0 +1,511 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a path used with a Graphics implementation.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.graphics.Path');
    +goog.provide('goog.graphics.Path.Segment');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Creates a path object. A path is a sequence of segments and may be open or
    + * closed. Path uses the EVEN-ODD fill rule for determining the interior of the
    + * path. A path must start with a moveTo command.
    + *
    + * A "simple" path does not contain any arcs and may be transformed using
    + * the {@code transform} method.
    + *
    + * @constructor
    + */
    +goog.graphics.Path = function() {
    +  /**
    +   * The segment types that constitute this path.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.segments_ = [];
    +
    +  /**
    +   * The number of repeated segments of the current type.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.count_ = [];
    +
    +  /**
    +   * The arguments corresponding to each of the segments.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.arguments_ = [];
    +};
    +
    +
    +/**
    + * The coordinates of the point which closes the path (the point of the
    + * last moveTo command).
    + * @type {Array<number>?}
    + * @private
    + */
    +goog.graphics.Path.prototype.closePoint_ = null;
    +
    +
    +/**
    + * The coordinates most recently added to the end of the path.
    + * @type {Array<number>?}
    + * @private
    + */
    +goog.graphics.Path.prototype.currentPoint_ = null;
    +
    +
    +/**
    + * Flag for whether this is a simple path (contains no arc segments).
    + * @type {boolean}
    + * @private
    + */
    +goog.graphics.Path.prototype.simple_ = true;
    +
    +
    +/**
    + * Path segment types.
    + * @enum {number}
    + */
    +goog.graphics.Path.Segment = {
    +  MOVETO: 0,
    +  LINETO: 1,
    +  CURVETO: 2,
    +  ARCTO: 3,
    +  CLOSE: 4
    +};
    +
    +
    +/**
    + * The number of points for each segment type.
    + * @type {!Array<number>}
    + * @private
    + */
    +goog.graphics.Path.segmentArgCounts_ = (function() {
    +  var counts = [];
    +  counts[goog.graphics.Path.Segment.MOVETO] = 2;
    +  counts[goog.graphics.Path.Segment.LINETO] = 2;
    +  counts[goog.graphics.Path.Segment.CURVETO] = 6;
    +  counts[goog.graphics.Path.Segment.ARCTO] = 6;
    +  counts[goog.graphics.Path.Segment.CLOSE] = 0;
    +  return counts;
    +})();
    +
    +
    +/**
    + * Returns the number of points for a segment type.
    + *
    + * @param {number} segment The segment type.
    + * @return {number} The number of points.
    + */
    +goog.graphics.Path.getSegmentCount = function(segment) {
    +  return goog.graphics.Path.segmentArgCounts_[segment];
    +};
    +
    +
    +/**
    + * Appends another path to the end of this path.
    + *
    + * @param {!goog.graphics.Path} path The path to append.
    + * @return {!goog.graphics.Path} This path.
    + */
    +goog.graphics.Path.prototype.appendPath = function(path) {
    +  if (path.currentPoint_) {
    +    Array.prototype.push.apply(this.segments_, path.segments_);
    +    Array.prototype.push.apply(this.count_, path.count_);
    +    Array.prototype.push.apply(this.arguments_, path.arguments_);
    +    this.currentPoint_ = path.currentPoint_.concat();
    +    this.closePoint_ = path.closePoint_.concat();
    +    this.simple_ = this.simple_ && path.simple_;
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Clears the path.
    + *
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.clear = function() {
    +  this.segments_.length = 0;
    +  this.count_.length = 0;
    +  this.arguments_.length = 0;
    +  delete this.closePoint_;
    +  delete this.currentPoint_;
    +  delete this.simple_;
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a point to the path by moving to the specified point. Repeated moveTo
    + * commands are collapsed into a single moveTo.
    + *
    + * @param {number} x X coordinate of destination point.
    + * @param {number} y Y coordinate of destination point.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.moveTo = function(x, y) {
    +  if (goog.array.peek(this.segments_) == goog.graphics.Path.Segment.MOVETO) {
    +    this.arguments_.length -= 2;
    +  } else {
    +    this.segments_.push(goog.graphics.Path.Segment.MOVETO);
    +    this.count_.push(1);
    +  }
    +  this.arguments_.push(x, y);
    +  this.currentPoint_ = this.closePoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing a straight line to each point.
    + *
    + * @param {...number} var_args The coordinates of each destination point as x, y
    + *     value pairs.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.lineTo = function(var_args) {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with lineTo');
    +  }
    +  if (lastSegment != goog.graphics.Path.Segment.LINETO) {
    +    this.segments_.push(goog.graphics.Path.Segment.LINETO);
    +    this.count_.push(0);
    +  }
    +  for (var i = 0; i < arguments.length; i += 2) {
    +    var x = arguments[i];
    +    var y = arguments[i + 1];
    +    this.arguments_.push(x, y);
    +  }
    +  this.count_[this.count_.length - 1] += i / 2;
    +  this.currentPoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing cubic Bezier curves. Each curve is
    + * specified using 3 points (6 coordinates) - two control points and the end
    + * point of the curve.
    + *
    + * @param {...number} var_args The coordinates specifiying each curve in sets of
    + *     6 points: {@code [x1, y1]} the first control point, {@code [x2, y2]} the
    + *     second control point and {@code [x, y]} the end point.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.curveTo = function(var_args) {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with curve');
    +  }
    +  if (lastSegment != goog.graphics.Path.Segment.CURVETO) {
    +    this.segments_.push(goog.graphics.Path.Segment.CURVETO);
    +    this.count_.push(0);
    +  }
    +  for (var i = 0; i < arguments.length; i += 6) {
    +    var x = arguments[i + 4];
    +    var y = arguments[i + 5];
    +    this.arguments_.push(arguments[i], arguments[i + 1],
    +        arguments[i + 2], arguments[i + 3], x, y);
    +  }
    +  this.count_[this.count_.length - 1] += i / 6;
    +  this.currentPoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a path command to close the path by connecting the
    + * last point to the first point.
    + *
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.close = function() {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with close');
    +  }
    +  if (lastSegment != goog.graphics.Path.Segment.CLOSE) {
    +    this.segments_.push(goog.graphics.Path.Segment.CLOSE);
    +    this.count_.push(1);
    +    this.currentPoint_ = this.closePoint_;
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a path command to draw an arc centered at the point {@code (cx, cy)}
    + * with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
    + * {@code startAngle} through {@code extent} degrees. Positive rotation is in
    + * the direction from positive x-axis to positive y-axis.
    + *
    + * @param {number} cx X coordinate of center of ellipse.
    + * @param {number} cy Y coordinate of center of ellipse.
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @param {boolean} connect If true, the starting point of the arc is connected
    + *     to the current point.
    + * @return {!goog.graphics.Path} The path itself.
    + * @deprecated Use {@code arcTo} or {@code arcToAsCurves} instead.
    + */
    +goog.graphics.Path.prototype.arc = function(cx, cy, rx, ry,
    +    fromAngle, extent, connect) {
    +  var startX = cx + goog.math.angleDx(fromAngle, rx);
    +  var startY = cy + goog.math.angleDy(fromAngle, ry);
    +  if (connect) {
    +    if (!this.currentPoint_ || startX != this.currentPoint_[0] ||
    +        startY != this.currentPoint_[1]) {
    +      this.lineTo(startX, startY);
    +    }
    +  } else {
    +    this.moveTo(startX, startY);
    +  }
    +  return this.arcTo(rx, ry, fromAngle, extent);
    +};
    +
    +
    +/**
    + * Adds a path command to draw an arc starting at the path's current point,
    + * with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
    + * {@code startAngle} through {@code extent} degrees. Positive rotation is in
    + * the direction from positive x-axis to positive y-axis.
    + *
    + * This method makes the path non-simple.
    + *
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.arcTo = function(rx, ry, fromAngle, extent) {
    +  var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
    +  var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
    +  var ex = cx + goog.math.angleDx(fromAngle + extent, rx);
    +  var ey = cy + goog.math.angleDy(fromAngle + extent, ry);
    +  this.segments_.push(goog.graphics.Path.Segment.ARCTO);
    +  this.count_.push(1);
    +  this.arguments_.push(rx, ry, fromAngle, extent, ex, ey);
    +  this.simple_ = false;
    +  this.currentPoint_ = [ex, ey];
    +  return this;
    +};
    +
    +
    +/**
    + * Same as {@code arcTo}, but approximates the arc using bezier curves.
    +.* As a result, this method does not affect the simplified status of this path.
    + * The algorithm is adapted from {@code java.awt.geom.ArcIterator}.
    + *
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.arcToAsCurves = function(
    +    rx, ry, fromAngle, extent) {
    +  var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
    +  var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
    +  var extentRad = goog.math.toRadians(extent);
    +  var arcSegs = Math.ceil(Math.abs(extentRad) / Math.PI * 2);
    +  var inc = extentRad / arcSegs;
    +  var angle = goog.math.toRadians(fromAngle);
    +  for (var j = 0; j < arcSegs; j++) {
    +    var relX = Math.cos(angle);
    +    var relY = Math.sin(angle);
    +    var z = 4 / 3 * Math.sin(inc / 2) / (1 + Math.cos(inc / 2));
    +    var c0 = cx + (relX - z * relY) * rx;
    +    var c1 = cy + (relY + z * relX) * ry;
    +    angle += inc;
    +    relX = Math.cos(angle);
    +    relY = Math.sin(angle);
    +    this.curveTo(c0, c1,
    +        cx + (relX + z * relY) * rx,
    +        cy + (relY - z * relX) * ry,
    +        cx + relX * rx,
    +        cy + relY * ry);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Iterates over the path calling the supplied callback once for each path
    + * segment. The arguments to the callback function are the segment type and
    + * an array of its arguments.
    + *
    + * The {@code LINETO} and {@code CURVETO} arrays can contain multiple
    + * segments of the same type. The number of segments is the length of the
    + * array divided by the segment length (2 for lines, 6 for  curves).
    + *
    + * As a convenience the {@code ARCTO} segment also includes the end point as the
    + * last two arguments: {@code rx, ry, fromAngle, extent, x, y}.
    + *
    + * @param {function(number, Array)} callback The function to call with each
    + *     path segment.
    + */
    +goog.graphics.Path.prototype.forEachSegment = function(callback) {
    +  var points = this.arguments_;
    +  var index = 0;
    +  for (var i = 0, length = this.segments_.length; i < length; i++) {
    +    var seg = this.segments_[i];
    +    var n = goog.graphics.Path.segmentArgCounts_[seg] * this.count_[i];
    +    callback(seg, points.slice(index, index + n));
    +    index += n;
    +  }
    +};
    +
    +
    +/**
    + * Returns the coordinates most recently added to the end of the path.
    + *
    + * @return {Array<number>?} An array containing the ending coordinates of the
    + *     path of the form {@code [x, y]}.
    + */
    +goog.graphics.Path.prototype.getCurrentPoint = function() {
    +  return this.currentPoint_ && this.currentPoint_.concat();
    +};
    +
    +
    +/**
    + * @return {!goog.graphics.Path} A copy of this path.
    + */
    +goog.graphics.Path.prototype.clone = function() {
    +  var path = new this.constructor();
    +  path.segments_ = this.segments_.concat();
    +  path.count_ = this.count_.concat();
    +  path.arguments_ = this.arguments_.concat();
    +  path.closePoint_ = this.closePoint_ && this.closePoint_.concat();
    +  path.currentPoint_ = this.currentPoint_ && this.currentPoint_.concat();
    +  path.simple_ = this.simple_;
    +  return path;
    +};
    +
    +
    +/**
    + * Returns true if this path contains no arcs. Simplified paths can be
    + * created using {@code createSimplifiedPath}.
    + *
    + * @return {boolean} True if the path contains no arcs.
    + */
    +goog.graphics.Path.prototype.isSimple = function() {
    +  return this.simple_;
    +};
    +
    +
    +/**
    + * A map from segment type to the path function to call to simplify a path.
    + * @type {!Object}
    + * @private
    + * @suppress {deprecated} goog.graphics.Path is deprecated.
    + */
    +goog.graphics.Path.simplifySegmentMap_ = (function() {
    +  var map = {};
    +  map[goog.graphics.Path.Segment.MOVETO] = goog.graphics.Path.prototype.moveTo;
    +  map[goog.graphics.Path.Segment.LINETO] = goog.graphics.Path.prototype.lineTo;
    +  map[goog.graphics.Path.Segment.CLOSE] = goog.graphics.Path.prototype.close;
    +  map[goog.graphics.Path.Segment.CURVETO] =
    +      goog.graphics.Path.prototype.curveTo;
    +  map[goog.graphics.Path.Segment.ARCTO] =
    +      goog.graphics.Path.prototype.arcToAsCurves;
    +  return map;
    +})();
    +
    +
    +/**
    + * Creates a copy of the given path, replacing {@code arcTo} with
    + * {@code arcToAsCurves}. The resulting path is simplified and can
    + * be transformed.
    + *
    + * @param {!goog.graphics.Path} src The path to simplify.
    + * @return {!goog.graphics.Path} A new simplified path.
    + * @suppress {deprecated} goog.graphics is deprecated.
    + */
    +goog.graphics.Path.createSimplifiedPath = function(src) {
    +  if (src.isSimple()) {
    +    return src.clone();
    +  }
    +  var path = new goog.graphics.Path();
    +  src.forEachSegment(function(segment, args) {
    +    goog.graphics.Path.simplifySegmentMap_[segment].apply(path, args);
    +  });
    +  return path;
    +};
    +
    +
    +// TODO(chrisn): Delete this method
    +/**
    + * Creates a transformed copy of this path. The path is simplified
    + * {@see #createSimplifiedPath} prior to transformation.
    + *
    + * @param {!goog.graphics.AffineTransform} tx The transformation to perform.
    + * @return {!goog.graphics.Path} A new, transformed path.
    + */
    +goog.graphics.Path.prototype.createTransformedPath = function(tx) {
    +  var path = goog.graphics.Path.createSimplifiedPath(this);
    +  path.transform(tx);
    +  return path;
    +};
    +
    +
    +/**
    + * Transforms the path. Only simple paths are transformable. Attempting
    + * to transform a non-simple path will throw an error.
    + *
    + * @param {!goog.graphics.AffineTransform} tx The transformation to perform.
    + * @return {!goog.graphics.Path} The path itself.
    + */
    +goog.graphics.Path.prototype.transform = function(tx) {
    +  if (!this.isSimple()) {
    +    throw Error('Non-simple path');
    +  }
    +  tx.transform(this.arguments_, 0, this.arguments_, 0,
    +      this.arguments_.length / 2);
    +  if (this.closePoint_) {
    +    tx.transform(this.closePoint_, 0, this.closePoint_, 0, 1);
    +  }
    +  if (this.currentPoint_ && this.closePoint_ != this.currentPoint_) {
    +    tx.transform(this.currentPoint_, 0, this.currentPoint_, 0, 1);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the path is empty.
    + */
    +goog.graphics.Path.prototype.isEmpty = function() {
    +  return this.segments_.length == 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/path_test.html b/mapbox-gl/closure-library/closure/goog/graphics/path_test.html
    new file mode 100644
    index 0000000000..b353c6decb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/path_test.html
    @@ -0,0 +1,359 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.Path</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.array');
    +  goog.require('goog.math');
    +  goog.require('goog.graphics.Path');
    +  goog.require('goog.graphics.AffineTransform');
    +  goog.require('goog.testing.graphics');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +
    +<script>
    +  function testConstructor() {
    +    var path = new goog.graphics.Path();
    +    assertTrue(path.isSimple());
    +    assertNull(path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals([], path);
    +  }
    +
    +
    +  function testGetSegmentCount() {
    +    assertArrayEquals([2, 2, 6, 6, 0], goog.array.map([
    +      goog.graphics.Path.Segment.MOVETO,
    +      goog.graphics.Path.Segment.LINETO,
    +      goog.graphics.Path.Segment.CURVETO,
    +      goog.graphics.Path.Segment.ARCTO,
    +      goog.graphics.Path.Segment.CLOSE
    +    ], goog.graphics.Path.getSegmentCount));
    +  }
    +
    +
    +  function testSimpleMoveTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(30, 50);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([30, 50], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 30, 50], path);
    +  }
    +
    +
    +  function testRepeatedMoveTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(30, 50);
    +    path.moveTo(40, 60);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([40, 60], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 40, 60], path);
    +  }
    +
    +
    +  function testSimpleLineTo() {
    +    var path = new goog.graphics.Path();
    +    var e = assertThrows(function() {
    +      path.lineTo(30, 50);
    +    });
    +    assertEquals('Path cannot start with lineTo', e.message);
    +    path.moveTo(0, 0);
    +    path.lineTo(30, 50);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([30, 50], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 0, 0, 'L', 30, 50], path);
    +  }
    +
    +
    +  function testMultiArgLineTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.lineTo(30, 50, 40 , 60);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([40, 60], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60],
    +        path);
    +  }
    +
    +
    +  function testRepeatedLineTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.lineTo(30, 50);
    +    path.lineTo(40, 60);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([40, 60], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60],
    +        path);
    +  }
    +
    +
    +  function testSimpleCurveTo() {
    +    var path = new goog.graphics.Path();
    +    var e = assertThrows(function() {
    +      path.curveTo(10, 20, 30, 40, 50, 60);
    +    });
    +    assertEquals('Path cannot start with curve', e.message);
    +    path.moveTo(0, 0);
    +    path.curveTo(10, 20, 30, 40, 50, 60);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([50, 60], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60], path);
    +  }
    +
    +
    +  function testMultiCurveTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.curveTo(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([110, 120], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +        path);
    +  }
    +
    +
    +  function testRepeatedCurveTo() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.curveTo(10, 20, 30, 40, 50, 60);
    +    path.curveTo(70, 80, 90, 100, 110, 120);
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([110, 120], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +        path);
    +  }
    +
    +
    +  function testSimpleArc() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    assertFalse(path.isSimple());
    +    var p = path.getCurrentPoint();
    +    assertEquals(55, p[0]);
    +    assertRoughlyEquals(77.32, p[1], 0.01);
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32], path);
    +  }
    +
    +
    +  function testArcNonConnectClose() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.arc(10, 10, 10, 10, -90, 180);
    +    assertObjectEquals([10, 20], path.getCurrentPoint());
    +    path.close();
    +    assertObjectEquals([10, 0], path.getCurrentPoint());
    +  }
    +
    +
    +  function testRepeatedArc() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    path.arc(50, 60, 10, 20, 60, 30, false);
    +    assertFalse(path.isSimple());
    +    assertObjectEquals([50, 80], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'A', 10, 20, 30, 30, 55, 77.32,
    +        'M', 55, 77.32,
    +        'A', 10, 20, 60, 30, 50, 80], path);
    +  }
    +
    +
    +  function testRepeatedArc2() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    path.arc(50, 60, 10, 20, 60, 30, true);
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'A', 10, 20, 30, 30, 55, 77.32,
    +        'A', 10, 20, 60, 30, 50, 80], path);
    +  }
    +
    +
    +  function testCompleteCircle() {
    +    var path = new goog.graphics.Path();
    +    path.arc(0, 0, 10, 10, 0, 360, false);
    +    assertFalse(path.isSimple());
    +    var p = path.getCurrentPoint();
    +    assertRoughlyEquals(10, p[0], 0.01);
    +    assertRoughlyEquals(0, p[1], 0.01);
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 10, 0, 'A', 10, 10, 0, 360, 10, 0], path);
    +  }
    +
    +
    +  function testClose() {
    +    var path = new goog.graphics.Path();
    +    try {
    +      path.close();
    +      fail();
    +    } catch (e) {
    +      // Expected
    +      assertEquals('Path cannot start with close', e.message);
    +    }
    +    path.moveTo(0, 0);
    +    path.lineTo(10, 20, 30, 40, 50, 60);
    +    path.close()
    +    assertTrue(path.isSimple());
    +    assertObjectEquals([0, 0], path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 0, 0, 'L', 10, 20, 30, 40, 50, 60, 'X'], path);
    +  }
    +
    +
    +  function testClear() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    path.clear();
    +    assertTrue(path.isSimple());
    +    assertNull(path.getCurrentPoint());
    +    goog.testing.graphics.assertPathEquals([], path);
    +  }
    +
    +
    +  function testCreateSimplifiedPath() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    assertFalse(path.isSimple());
    +    path = goog.graphics.Path.createSimplifiedPath(path);
    +    assertTrue(path.isSimple());
    +    var p = path.getCurrentPoint();
    +    assertEquals(55, p[0]);
    +    assertRoughlyEquals(77.32, p[1], 0.01);
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
    +  }
    +
    +
    +  function testCreateSimplifiedPath2() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    path.arc(50, 60, 10, 20, 60, 30, false);
    +    assertFalse(path.isSimple());
    +    path = goog.graphics.Path.createSimplifiedPath(path);
    +    assertTrue(path.isSimple());
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32,
    +        'M', 55, 77.32,
    +        'C', 53.48, 79.08, 51.76, 80, 50, 80], path);
    +  }
    +
    +
    +  function testCreateSimplifiedPath3() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    path.arc(50, 60, 10, 20, 60, 30, true);
    +    path.close();
    +    path = goog.graphics.Path.createSimplifiedPath(path);
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32,
    +        53.48, 79.08, 51.76, 80, 50, 80, 'X'], path);
    +    var p = path.getCurrentPoint();
    +    assertRoughlyEquals(58.66, p[0], 0.01);
    +    assertRoughlyEquals(70, p[1], 0.01);
    +  }
    +
    +
    +  function testArcToAsCurves() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(58.66, 70);
    +    path.arcToAsCurves(10, 20, 30, 30, false);
    +    goog.testing.graphics.assertPathEquals(['M', 58.66, 70,
    +        'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
    +  }
    +
    +
    +  function testCreateTransformedPath() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.lineTo(0, 10, 10, 10, 10, 0);
    +    path.close();
    +    var tx = new goog.graphics.AffineTransform(2, 0, 0, 3, 10, 20);
    +    var path2 = path.createTransformedPath(tx);
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 0, 0, 'L', 0, 10, 10, 10, 10, 0, 'X'], path);
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
    +  }
    +
    +
    +  function testTransform() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    path.lineTo(0, 10, 10, 10, 10, 0);
    +    path.close();
    +    var tx = new goog.graphics.AffineTransform(2, 0, 0, 3, 10, 20);
    +    var path2 = path.transform(tx);
    +    assertTrue(path === path2);
    +    goog.testing.graphics.assertPathEquals(
    +        ['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
    +  }
    +
    +
    +  function testTransformCurrentAndClosePoints() {
    +    var path = new goog.graphics.Path();
    +    path.moveTo(0, 0);
    +    assertObjectEquals([0, 0], path.getCurrentPoint());
    +    path.transform(new goog.graphics.AffineTransform(1, 0, 0, 1, 10, 20));
    +    assertObjectEquals([10, 20], path.getCurrentPoint());
    +    path.lineTo(50, 50);
    +    path.close();
    +    assertObjectEquals([10, 20], path.getCurrentPoint());
    +  }
    +
    +
    +  function testTransformNonSimple() {
    +    var path = new goog.graphics.Path();
    +    path.arc(50, 60, 10, 20, 30, 30, false);
    +    assertThrows(function() {
    +      path.transform(new goog.graphics.AffineTransform(1, 0, 0, 1, 10, 20));
    +    });
    +  }
    +
    +
    +  function testAppendPath() {
    +    var path1 = new goog.graphics.Path();
    +    path1.moveTo(0, 0);
    +    path1.lineTo(0, 10, 10, 10, 10, 0);
    +    path1.close();
    +
    +    var path2 = new goog.graphics.Path();
    +    path2.arc(50, 60, 10, 20, 30, 30, false);
    +
    +    assertTrue(path1.isSimple());
    +    path1.appendPath(path2);
    +    assertFalse(path1.isSimple());
    +    goog.testing.graphics.assertPathEquals([
    +        'M', 0, 0, 'L', 0, 10, 10, 10, 10, 0, 'X',
    +        'M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32
    +    ], path1);
    +  }
    +
    +
    +  function testIsEmpty() {
    +    var path = new goog.graphics.Path();
    +    assertTrue('Initially path is empty', path.isEmpty());
    +
    +    path.moveTo(0, 0);
    +    assertFalse('After command addition, path is not empty', path.isEmpty());
    +
    +    path.clear();
    +    assertTrue('After clear, path is empty again', path.isEmpty());
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/pathelement.js b/mapbox-gl/closure-library/closure/goog/graphics/pathelement.js
    new file mode 100644
    index 0000000000..b58b8c61f3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/pathelement.js
    @@ -0,0 +1,54 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for paths.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.PathElement');
    +
    +goog.require('goog.graphics.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Interface for a graphics path element.
    + * You should not construct objects from this constructor. The graphics
    + * will return an implementation of this interface for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.StrokeAndFillElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.PathElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
    +      fill);
    +};
    +goog.inherits(goog.graphics.PathElement, goog.graphics.StrokeAndFillElement);
    +
    +
    +/**
    + * Update the underlying path.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + */
    +goog.graphics.PathElement.prototype.setPath = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/paths.js b/mapbox-gl/closure-library/closure/goog/graphics/paths.js
    new file mode 100644
    index 0000000000..37b53d92ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/paths.js
    @@ -0,0 +1,86 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Factories for common path types.
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +
    +goog.provide('goog.graphics.paths');
    +
    +goog.require('goog.graphics.Path');
    +goog.require('goog.math.Coordinate');
    +
    +
    +/**
    + * Defines a regular n-gon by specifing the center, a vertex, and the total
    + * number of vertices.
    + * @param {goog.math.Coordinate} center The center point.
    + * @param {goog.math.Coordinate} vertex The vertex, which implicitly defines
    + *     a radius as well.
    + * @param {number} n The number of vertices.
    + * @return {!goog.graphics.Path} The path.
    + */
    +goog.graphics.paths.createRegularNGon = function(center, vertex, n) {
    +  var path = new goog.graphics.Path();
    +  path.moveTo(vertex.x, vertex.y);
    +
    +  var startAngle = Math.atan2(vertex.y - center.y, vertex.x - center.x);
    +  var radius = goog.math.Coordinate.distance(center, vertex);
    +  for (var i = 1; i < n; i++) {
    +    var angle = startAngle + 2 * Math.PI * (i / n);
    +    path.lineTo(center.x + radius * Math.cos(angle),
    +                center.y + radius * Math.sin(angle));
    +  }
    +  path.close();
    +  return path;
    +};
    +
    +
    +/**
    + * Defines an arrow.
    + * @param {goog.math.Coordinate} a Point A.
    + * @param {goog.math.Coordinate} b Point B.
    + * @param {?number} aHead The size of the arrow head at point A.
    + *     0 omits the head.
    + * @param {?number} bHead The size of the arrow head at point B.
    + *     0 omits the head.
    + * @return {!goog.graphics.Path} The path.
    + */
    +goog.graphics.paths.createArrow = function(a, b, aHead, bHead) {
    +  var path = new goog.graphics.Path();
    +  path.moveTo(a.x, a.y);
    +  path.lineTo(b.x, b.y);
    +
    +  var angle = Math.atan2(b.y - a.y, b.x - a.x);
    +  if (aHead) {
    +    path.appendPath(
    +        goog.graphics.paths.createRegularNGon(
    +            new goog.math.Coordinate(
    +                a.x + aHead * Math.cos(angle),
    +                a.y + aHead * Math.sin(angle)),
    +            a, 3));
    +  }
    +  if (bHead) {
    +    path.appendPath(
    +        goog.graphics.paths.createRegularNGon(
    +            new goog.math.Coordinate(
    +                b.x + bHead * Math.cos(angle + Math.PI),
    +                b.y + bHead * Math.sin(angle + Math.PI)),
    +            b, 3));
    +  }
    +  return path;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/paths_test.html b/mapbox-gl/closure-library/closure/goog/graphics/paths_test.html
    new file mode 100644
    index 0000000000..e6e062b5b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/paths_test.html
    @@ -0,0 +1,98 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>JsUnit tests for goog.graphics.paths</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.dom');
    +goog.require('goog.graphics');
    +goog.require('goog.graphics.paths');
    +goog.require('goog.testing.jsunit');
    +
    +
    +</script>
    +</head>
    +<body>
    +
    +<div id="root"></div>
    +
    +<script type='text/javascript'>
    +
    +// The purpose of this test is less about the actual unit test, and
    +// more for drawing demos of shapes.
    +var regularNGon = goog.graphics.paths.createRegularNGon;
    +var arrow = goog.graphics.paths.createArrow;
    +
    +function setUp() {
    +  goog.dom.removeChildren(goog.dom.getElement('root'));
    +}
    +
    +function testSquare() {
    +  var square = regularNGon(
    +      $coord(10, 10), $coord(0, 10), 4);
    +  assertArrayRoughlyEquals(
    +      [0, 10, 10, 0, 20, 10, 10, 20], square.arguments_, 0.05);
    +}
    +
    +function assertArrayRoughlyEquals(expected, actual, delta) {
    +  var message = 'Expected: ' + expected + ', Actual: ' + actual;
    +  assertEquals('Wrong length. ' + message, expected.length, actual.length);
    +  for (var i = 0; i < expected.length; i++) {
    +    assertRoughlyEquals(
    +        'Wrong item at ' + i + '. ' + message,
    +        expected[i], actual[i], delta);
    +  }
    +}
    +
    +function tearDownPage() {
    +  var root = goog.dom.getElement('root');
    +  var graphics = goog.graphics.createGraphics(800, 600);
    +
    +  var blueFill = new goog.graphics.SolidFill('blue');
    +  var blackStroke = new goog.graphics.Stroke(1, 'black');
    +  graphics.drawPath(
    +      regularNGon($coord(20, 50), $coord(0, 20), 3),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      regularNGon($coord(120, 50), $coord(100, 20), 4),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      regularNGon($coord(220, 50), $coord(200, 20), 5),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      regularNGon($coord(320, 50), $coord(300, 20), 6),
    +      blackStroke, blueFill);
    +
    +  graphics.drawPath(
    +      arrow($coord(0, 300), $coord(100, 400), 0, 0),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      arrow($coord(120, 400), $coord(200, 300), 0, 10),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      arrow($coord(220, 300), $coord(300, 400), 10, 0),
    +      blackStroke, blueFill);
    +  graphics.drawPath(
    +      arrow($coord(320, 400), $coord(400, 300), 10, 10),
    +      blackStroke, blueFill);
    +
    +  root.appendChild(graphics.getElement());
    +}
    +
    +function $coord(x, y) {
    +  return new goog.math.Coordinate(x, y);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/rectelement.js b/mapbox-gl/closure-library/closure/goog/graphics/rectelement.js
    new file mode 100644
    index 0000000000..9a6e9a1718
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/rectelement.js
    @@ -0,0 +1,63 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for rectangles.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.RectElement');
    +
    +goog.require('goog.graphics.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Interface for a graphics rectangle element.
    + * You should not construct objects from this constructor. The graphics
    + * will return an implementation of this interface for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.StrokeAndFillElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.RectElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
    +      fill);
    +};
    +goog.inherits(goog.graphics.RectElement, goog.graphics.StrokeAndFillElement);
    +
    +
    +/**
    + * Update the position of the rectangle.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + */
    +goog.graphics.RectElement.prototype.setPosition = goog.abstractMethod;
    +
    +
    +/**
    + * Update the size of the rectangle.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + */
    +goog.graphics.RectElement.prototype.setSize = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/solidfill.js b/mapbox-gl/closure-library/closure/goog/graphics/solidfill.js
    new file mode 100644
    index 0000000000..fae3fc4248
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/solidfill.js
    @@ -0,0 +1,74 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a solid color fill goog.graphics.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.SolidFill');
    +
    +
    +goog.require('goog.graphics.Fill');
    +
    +
    +
    +/**
    + * Creates an immutable solid color fill object.
    + *
    + * @param {string} color The color of the background.
    + * @param {number=} opt_opacity The opacity of the background fill. The value
    + *    must be greater than or equal to zero (transparent) and less than or
    + *    equal to 1 (opaque).
    + * @constructor
    + * @extends {goog.graphics.Fill}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.SolidFill = function(color, opt_opacity) {
    +  /**
    +   * The color with which to fill.
    +   * @type {string}
    +   * @private
    +   */
    +  this.color_ = color;
    +
    +
    +  /**
    +   * The opacity of the fill.
    +   * @type {number}
    +   * @private
    +   */
    +  this.opacity_ = opt_opacity == null ? 1.0 : opt_opacity;
    +};
    +goog.inherits(goog.graphics.SolidFill, goog.graphics.Fill);
    +
    +
    +/**
    + * @return {string} The color of this fill.
    + */
    +goog.graphics.SolidFill.prototype.getColor = function() {
    +  return this.color_;
    +};
    +
    +
    +/**
    + * @return {number} The opacity of this fill.
    + */
    +goog.graphics.SolidFill.prototype.getOpacity = function() {
    +  return this.opacity_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/solidfill_test.html b/mapbox-gl/closure-library/closure/goog/graphics/solidfill_test.html
    new file mode 100644
    index 0000000000..0282698b89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/solidfill_test.html
    @@ -0,0 +1,53 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.SolidFill</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.graphics.SolidFill');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +
    +<script>
    +  function testGetColor() {
    +    var fill = new goog.graphics.SolidFill('#123');
    +    assertEquals('#123', fill.getColor());
    +    fill = new goog.graphics.SolidFill('#abcdef');
    +    assertEquals('#abcdef', fill.getColor());
    +
    +    fill = new goog.graphics.SolidFill('#123', 0.5);
    +    assertEquals('#123', fill.getColor());
    +    fill = new goog.graphics.SolidFill('#abcdef', 0.5);
    +    assertEquals('#abcdef', fill.getColor());
    +  }
    +
    +  function testGetOpacity() {
    +    // Default opacity
    +    var fill = new goog.graphics.SolidFill('#123');
    +    assertEquals(1, fill.getOpacity());
    +
    +    // Opaque
    +    var fill = new goog.graphics.SolidFill('#123', 1);
    +    assertEquals(1, fill.getOpacity());
    +
    +    // Semi-transparent
    +    fill = new goog.graphics.SolidFill('#123', 0.5);
    +    assertEquals(0.5, fill.getOpacity());
    +
    +    // Fully transparent
    +    fill = new goog.graphics.SolidFill('#123', 0);
    +    assertEquals(0, fill.getOpacity());
    +  }
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/stroke.js b/mapbox-gl/closure-library/closure/goog/graphics/stroke.js
    new file mode 100644
    index 0000000000..ae1eb8e919
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/stroke.js
    @@ -0,0 +1,86 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a stroke object for goog.graphics.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.Stroke');
    +
    +
    +
    +/**
    + * Creates an immutable stroke object.
    + *
    + * @param {number|string} width The width of the stroke.
    + * @param {string} color The color of the stroke.
    + * @param {number=} opt_opacity The opacity of the background fill. The value
    + *    must be greater than or equal to zero (transparent) and less than or
    + *    equal to 1 (opaque).
    + * @constructor
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.Stroke = function(width, color, opt_opacity) {
    +  /**
    +   * The width of the stroke.
    +   * @type {number|string}
    +   * @private
    +   */
    +  this.width_ = width;
    +
    +
    +  /**
    +   * The color with which to fill.
    +   * @type {string}
    +   * @private
    +   */
    +  this.color_ = color;
    +
    +
    +  /**
    +   * The opacity of the fill.
    +   * @type {number}
    +   * @private
    +   */
    +  this.opacity_ = opt_opacity == null ? 1.0 : opt_opacity;
    +};
    +
    +
    +/**
    + * @return {number|string} The width of this stroke.
    + */
    +goog.graphics.Stroke.prototype.getWidth = function() {
    +  return this.width_;
    +};
    +
    +
    +/**
    + * @return {string} The color of this stroke.
    + */
    +goog.graphics.Stroke.prototype.getColor = function() {
    +  return this.color_;
    +};
    +
    +
    +/**
    + * @return {number} The opacity of this fill.
    + */
    +goog.graphics.Stroke.prototype.getOpacity = function() {
    +  return this.opacity_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/strokeandfillelement.js b/mapbox-gl/closure-library/closure/goog/graphics/strokeandfillelement.js
    new file mode 100644
    index 0000000000..e3b50f99e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/strokeandfillelement.js
    @@ -0,0 +1,114 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for elements with a
    + * stroke and fill.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.StrokeAndFillElement');
    +
    +goog.require('goog.graphics.Element');
    +
    +
    +
    +/**
    + * Interface for a graphics element with a stroke and fill.
    + * This is the base interface for ellipse, rectangle and other
    + * shape interfaces.
    + * You should not construct objects from this constructor. The graphics
    + * will return an implementation of this interface for you.
    + *
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.Element}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.StrokeAndFillElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.Element.call(this, element, graphics);
    +  this.setStroke(stroke);
    +  this.setFill(fill);
    +};
    +goog.inherits(goog.graphics.StrokeAndFillElement, goog.graphics.Element);
    +
    +
    +/**
    + * The latest fill applied to this element.
    + * @type {goog.graphics.Fill?}
    + * @protected
    + */
    +goog.graphics.StrokeAndFillElement.prototype.fill = null;
    +
    +
    +/**
    + * The latest stroke applied to this element.
    + * @type {goog.graphics.Stroke?}
    + * @private
    + */
    +goog.graphics.StrokeAndFillElement.prototype.stroke_ = null;
    +
    +
    +/**
    + * Sets the fill for this element.
    + * @param {goog.graphics.Fill?} fill The fill object.
    + */
    +goog.graphics.StrokeAndFillElement.prototype.setFill = function(fill) {
    +  this.fill = fill;
    +  this.getGraphics().setElementFill(this, fill);
    +};
    +
    +
    +/**
    + * @return {goog.graphics.Fill?} fill The fill object.
    + */
    +goog.graphics.StrokeAndFillElement.prototype.getFill = function() {
    +  return this.fill;
    +};
    +
    +
    +/**
    + * Sets the stroke for this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke object.
    + */
    +goog.graphics.StrokeAndFillElement.prototype.setStroke = function(stroke) {
    +  this.stroke_ = stroke;
    +  this.getGraphics().setElementStroke(this, stroke);
    +};
    +
    +
    +/**
    + * @return {goog.graphics.Stroke?} stroke The stroke object.
    + */
    +goog.graphics.StrokeAndFillElement.prototype.getStroke = function() {
    +  return this.stroke_;
    +};
    +
    +
    +/**
    + * Re-strokes the element to react to coordinate size changes.
    + */
    +goog.graphics.StrokeAndFillElement.prototype.reapplyStroke = function() {
    +  if (this.stroke_) {
    +    this.setStroke(this.stroke_);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/svgelement.js b/mapbox-gl/closure-library/closure/goog/graphics/svgelement.js
    new file mode 100644
    index 0000000000..eddcbb5230
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/svgelement.js
    @@ -0,0 +1,284 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Thin wrappers around the DOM element returned from
    + * the different draw methods of the graphics. This is the SVG implementation.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.graphics.SvgEllipseElement');
    +goog.provide('goog.graphics.SvgGroupElement');
    +goog.provide('goog.graphics.SvgImageElement');
    +goog.provide('goog.graphics.SvgPathElement');
    +goog.provide('goog.graphics.SvgRectElement');
    +goog.provide('goog.graphics.SvgTextElement');
    +
    +
    +goog.require('goog.dom');
    +goog.require('goog.graphics.EllipseElement');
    +goog.require('goog.graphics.GroupElement');
    +goog.require('goog.graphics.ImageElement');
    +goog.require('goog.graphics.PathElement');
    +goog.require('goog.graphics.RectElement');
    +goog.require('goog.graphics.TextElement');
    +
    +
    +
    +/**
    + * Thin wrapper for SVG group elements.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.GroupElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.SvgGroupElement = function(element, graphics) {
    +  goog.graphics.GroupElement.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.SvgGroupElement, goog.graphics.GroupElement);
    +
    +
    +/**
    + * Remove all drawing elements from the group.
    + * @override
    + */
    +goog.graphics.SvgGroupElement.prototype.clear = function() {
    +  goog.dom.removeChildren(this.getElement());
    +};
    +
    +
    +/**
    + * Set the size of the group element.
    + * @param {number|string} width The width of the group element.
    + * @param {number|string} height The height of the group element.
    + * @override
    + */
    +goog.graphics.SvgGroupElement.prototype.setSize = function(width, height) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'width': width, 'height': height});
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for SVG ellipse elements.
    + * This is an implementation of the goog.graphics.EllipseElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.EllipseElement}
    + * @final
    + */
    +goog.graphics.SvgEllipseElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.SvgEllipseElement, goog.graphics.EllipseElement);
    +
    +
    +/**
    + * Update the center point of the ellipse.
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @override
    + */
    +goog.graphics.SvgEllipseElement.prototype.setCenter = function(cx, cy) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'cx': cx, 'cy': cy});
    +};
    +
    +
    +/**
    + * Update the radius of the ellipse.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @override
    + */
    +goog.graphics.SvgEllipseElement.prototype.setRadius = function(rx, ry) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'rx': rx, 'ry': ry});
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for SVG rectangle elements.
    + * This is an implementation of the goog.graphics.RectElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.RectElement}
    + * @final
    + */
    +goog.graphics.SvgRectElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.SvgRectElement, goog.graphics.RectElement);
    +
    +
    +/**
    + * Update the position of the rectangle.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.SvgRectElement.prototype.setPosition = function(x, y) {
    +  this.getGraphics().setElementAttributes(this.getElement(), {'x': x, 'y': y});
    +};
    +
    +
    +/**
    + * Update the size of the rectangle.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @override
    + */
    +goog.graphics.SvgRectElement.prototype.setSize = function(width, height) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'width': width, 'height': height});
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for SVG path elements.
    + * This is an implementation of the goog.graphics.PathElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.PathElement}
    + * @final
    + */
    +goog.graphics.SvgPathElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.SvgPathElement, goog.graphics.PathElement);
    +
    +
    +/**
    + * Update the underlying path.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @override
    + */
    +goog.graphics.SvgPathElement.prototype.setPath = function(path) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'d': /** @suppress {missingRequire} */
    +            goog.graphics.SvgGraphics.getSvgPath(path)});
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for SVG text elements.
    + * This is an implementation of the goog.graphics.TextElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.TextElement}
    + * @final
    + */
    +goog.graphics.SvgTextElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.SvgTextElement, goog.graphics.TextElement);
    +
    +
    +/**
    + * Update the displayed text of the element.
    + * @param {string} text The text to draw.
    + * @override
    + */
    +goog.graphics.SvgTextElement.prototype.setText = function(text) {
    +  this.getElement().firstChild.data = text;
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for SVG image elements.
    + * This is an implementation of the goog.graphics.ImageElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.SvgGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.ImageElement}
    + * @final
    + */
    +goog.graphics.SvgImageElement = function(element, graphics) {
    +  goog.graphics.ImageElement.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.SvgImageElement, goog.graphics.ImageElement);
    +
    +
    +/**
    + * Update the position of the image.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.SvgImageElement.prototype.setPosition = function(x, y) {
    +  this.getGraphics().setElementAttributes(this.getElement(), {'x': x, 'y': y});
    +};
    +
    +
    +/**
    + * Update the size of the image.
    + * @param {number} width Width of image.
    + * @param {number} height Height of image.
    + * @override
    + */
    +goog.graphics.SvgImageElement.prototype.setSize = function(width, height) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'width': width, 'height': height});
    +};
    +
    +
    +/**
    + * Update the source of the image.
    + * @param {string} src Source of the image.
    + * @override
    + */
    +goog.graphics.SvgImageElement.prototype.setSource = function(src) {
    +  this.getGraphics().setElementAttributes(this.getElement(),
    +      {'xlink:href': src});
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/svggraphics.js b/mapbox-gl/closure-library/closure/goog/graphics/svggraphics.js
    new file mode 100644
    index 0000000000..f78cc3b12e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/svggraphics.js
    @@ -0,0 +1,879 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview SvgGraphics sub class that uses SVG to draw the graphics.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.graphics.SvgGraphics');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.graphics.AbstractGraphics');
    +goog.require('goog.graphics.LinearGradient');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.graphics.Stroke');
    +goog.require('goog.graphics.SvgEllipseElement');
    +goog.require('goog.graphics.SvgGroupElement');
    +goog.require('goog.graphics.SvgImageElement');
    +goog.require('goog.graphics.SvgPathElement');
    +goog.require('goog.graphics.SvgRectElement');
    +goog.require('goog.graphics.SvgTextElement');
    +goog.require('goog.math');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A Graphics implementation for drawing using SVG.
    + * @param {string|number} width The width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The height in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The coordinate width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight The coordinate height - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @constructor
    + * @extends {goog.graphics.AbstractGraphics}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.SvgGraphics = function(width, height,
    +                                     opt_coordWidth, opt_coordHeight,
    +                                     opt_domHelper) {
    +  goog.graphics.AbstractGraphics.call(this, width, height,
    +                                      opt_coordWidth, opt_coordHeight,
    +                                      opt_domHelper);
    +
    +  /**
    +   * Map from def key to id of def root element.
    +   * Defs are global "defines" of svg that are used to share common attributes,
    +   * for example gradients.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.defs_ = {};
    +
    +  /**
    +   * Whether to manually implement viewBox by using a coordinate transform.
    +   * As of 1/11/08 this is necessary for Safari 3 but not for the nightly
    +   * WebKit build. Apply to webkit versions < 526. 525 is the
    +   * last version used by Safari 3.1.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useManualViewbox_ = goog.userAgent.WEBKIT &&
    +                           !goog.userAgent.isVersionOrHigher(526);
    +
    +  /**
    +   * Event handler.
    +   * @type {goog.events.EventHandler<!goog.graphics.SvgGraphics>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +};
    +goog.inherits(goog.graphics.SvgGraphics, goog.graphics.AbstractGraphics);
    +
    +
    +/**
    + * The SVG namespace URN
    + * @private
    + * @type {string}
    + */
    +goog.graphics.SvgGraphics.SVG_NS_ = 'http://www.w3.org/2000/svg';
    +
    +
    +/**
    + * The name prefix for def entries
    + * @private
    + * @type {string}
    + */
    +goog.graphics.SvgGraphics.DEF_ID_PREFIX_ = '_svgdef_';
    +
    +
    +/**
    + * The next available unique identifier for a def entry.
    + * This is a static variable, so that when multiple graphics are used in one
    + * document, the same def id can not be re-defined by another SvgGraphics.
    + * @type {number}
    + * @private
    + */
    +goog.graphics.SvgGraphics.nextDefId_ = 0;
    +
    +
    +/**
    + * Svg element for definitions for other elements, e.g. linear gradients.
    + * @type {Element}
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.defsElement_;
    +
    +
    +/**
    + * Creates an SVG element. Used internally and by different SVG classes.
    + * @param {string} tagName The type of element to create.
    + * @param {Object=} opt_attributes Map of name-value pairs for attributes.
    + * @return {!Element} The created element.
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.createSvgElement_ = function(tagName,
    +    opt_attributes) {
    +  var element = this.dom_.getDocument().createElementNS(
    +      goog.graphics.SvgGraphics.SVG_NS_, tagName);
    +
    +  if (opt_attributes) {
    +    this.setElementAttributes(element, opt_attributes);
    +  }
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Sets properties to an SVG element. Used internally and by different
    + * SVG elements.
    + * @param {Element} element The svg element.
    + * @param {Object} attributes Map of name-value pairs for attributes.
    + */
    +goog.graphics.SvgGraphics.prototype.setElementAttributes = function(element,
    +    attributes) {
    +  for (var key in attributes) {
    +    element.setAttribute(key, attributes[key]);
    +  }
    +};
    +
    +
    +/**
    + * Appends an element.
    + *
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.append_ = function(element, opt_group) {
    +  var parent = opt_group || this.canvasElement;
    +  parent.getElement().appendChild(element.getElement());
    +};
    +
    +
    +/**
    + * Sets the fill of the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Fill?} fill The fill object.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setElementFill = function(element, fill) {
    +  var svgElement = element.getElement();
    +  if (fill instanceof goog.graphics.SolidFill) {
    +    svgElement.setAttribute('fill', fill.getColor());
    +    svgElement.setAttribute('fill-opacity', fill.getOpacity());
    +  } else if (fill instanceof goog.graphics.LinearGradient) {
    +    // create a def key which is just a concat of all the relevant fields
    +    var defKey = 'lg-' +
    +                 fill.getX1() + '-' + fill.getY1() + '-' +
    +                 fill.getX2() + '-' + fill.getY2() + '-' +
    +                 fill.getColor1() + '-' + fill.getColor2();
    +    // It seems that the SVG version accepts opacity where the VML does not
    +
    +    var id = this.getDef(defKey);
    +
    +    if (!id) { // No def for this yet, create it
    +      // Create the gradient def entry (only linear gradient are supported)
    +      var gradient = this.createSvgElement_('linearGradient', {
    +        'x1': fill.getX1(),
    +        'y1': fill.getY1(),
    +        'x2': fill.getX2(),
    +        'y2': fill.getY2(),
    +        'gradientUnits': 'userSpaceOnUse'
    +      });
    +
    +      var gstyle = 'stop-color:' + fill.getColor1();
    +      if (goog.isNumber(fill.getOpacity1())) {
    +        gstyle += ';stop-opacity:' + fill.getOpacity1();
    +      }
    +      var stop1 = this.createSvgElement_(
    +          'stop', {'offset': '0%', 'style': gstyle});
    +      gradient.appendChild(stop1);
    +
    +      // LinearGradients don't have opacity in VML so implement that before
    +      // enabling the following code.
    +      // if (fill.getOpacity() != null) {
    +      //   gstyles += 'opacity:' + fill.getOpacity() + ';'
    +      // }
    +      gstyle = 'stop-color:' + fill.getColor2();
    +      if (goog.isNumber(fill.getOpacity2())) {
    +        gstyle += ';stop-opacity:' + fill.getOpacity2();
    +      }
    +      var stop2 = this.createSvgElement_(
    +          'stop', {'offset': '100%', 'style': gstyle});
    +      gradient.appendChild(stop2);
    +
    +      // LinearGradients don't have opacity in VML so implement that before
    +      // enabling the following code.
    +      // if (fill.getOpacity() != null) {
    +      //   gstyles += 'opacity:' + fill.getOpacity() + ';'
    +      // }
    +
    +      id = this.addDef(defKey, gradient);
    +    }
    +
    +    // Link element to linearGradient definition
    +    svgElement.setAttribute('fill', 'url(#' + id + ')');
    +  } else {
    +    svgElement.setAttribute('fill', 'none');
    +  }
    +};
    +
    +
    +/**
    + * Sets the stroke of the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Stroke?} stroke The stroke object.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setElementStroke = function(element,
    +    stroke) {
    +  var svgElement = element.getElement();
    +  if (stroke) {
    +    svgElement.setAttribute('stroke', stroke.getColor());
    +    svgElement.setAttribute('stroke-opacity', stroke.getOpacity());
    +
    +    var width = stroke.getWidth();
    +    if (goog.isString(width) && width.indexOf('px') != -1) {
    +      svgElement.setAttribute('stroke-width',
    +          parseFloat(width) / this.getPixelScaleX());
    +    } else {
    +      svgElement.setAttribute('stroke-width', width);
    +    }
    +  } else {
    +    svgElement.setAttribute('stroke', 'none');
    +  }
    +};
    +
    +
    +/**
    + * Set the translation and rotation of an element.
    + *
    + * If a more general affine transform is needed than this provides
    + * (e.g. skew and scale) then use setElementAffineTransform.
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {number} x The x coordinate of the translation transform.
    + * @param {number} y The y coordinate of the translation transform.
    + * @param {number} angle The angle of the rotation transform.
    + * @param {number} centerX The horizontal center of the rotation transform.
    + * @param {number} centerY The vertical center of the rotation transform.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setElementTransform = function(element, x,
    +    y, angle, centerX, centerY) {
    +  element.getElement().setAttribute('transform', 'translate(' + x + ',' + y +
    +      ') rotate(' + angle + ' ' + centerX + ' ' + centerY + ')');
    +};
    +
    +
    +/**
    + * Set the transformation of an element.
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {!goog.graphics.AffineTransform} affineTransform The
    + *     transformation applied to this element.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setElementAffineTransform = function(
    +    element, affineTransform) {
    +  var t = affineTransform;
    +  var substr = [t.getScaleX(), t.getShearY(), t.getShearX(), t.getScaleY(),
    +                t.getTranslateX(), t.getTranslateY()].join(',');
    +  element.getElement().setAttribute('transform', 'matrix(' + substr + ')');
    +};
    +
    +
    +/**
    + * Creates the DOM representation of the graphics area.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.createDom = function() {
    +  // Set up the standard attributes.
    +  var attributes = {
    +    'width': this.width,
    +    'height': this.height,
    +    'overflow': 'hidden'
    +  };
    +
    +  var svgElement = this.createSvgElement_('svg', attributes);
    +
    +  var groupElement = this.createSvgElement_('g');
    +
    +  this.defsElement_ = this.createSvgElement_('defs');
    +  this.canvasElement = new goog.graphics.SvgGroupElement(groupElement, this);
    +
    +  svgElement.appendChild(this.defsElement_);
    +  svgElement.appendChild(groupElement);
    +
    +  // Use the svgElement as the root element.
    +  this.setElementInternal(svgElement);
    +
    +  // Set up the coordinate system.
    +  this.setViewBox_();
    +};
    +
    +
    +/**
    + * Changes the coordinate system position.
    + * @param {number} left The coordinate system left bound.
    + * @param {number} top The coordinate system top bound.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setCoordOrigin = function(left, top) {
    +  this.coordLeft = left;
    +  this.coordTop = top;
    +
    +  this.setViewBox_();
    +};
    +
    +
    +/**
    + * Changes the coordinate size.
    + * @param {number} coordWidth The coordinate width.
    + * @param {number} coordHeight The coordinate height.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setCoordSize = function(coordWidth,
    +    coordHeight) {
    +  goog.graphics.SvgGraphics.superClass_.setCoordSize.apply(
    +      this, arguments);
    +  this.setViewBox_();
    +};
    +
    +
    +/**
    + * @return {string} The view box string.
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.getViewBox_ = function() {
    +  return this.coordLeft + ' ' + this.coordTop + ' ' +
    +      (this.coordWidth ? this.coordWidth + ' ' + this.coordHeight : '');
    +};
    +
    +
    +/**
    + * Sets up the view box.
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.setViewBox_ = function() {
    +  if (this.coordWidth || this.coordLeft || this.coordTop) {
    +    this.getElement().setAttribute('preserveAspectRatio', 'none');
    +    if (this.useManualViewbox_) {
    +      this.updateManualViewBox_();
    +    } else {
    +      this.getElement().setAttribute('viewBox', this.getViewBox_());
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Updates the transform of the root element to fake a viewBox.  Should only
    + * be called when useManualViewbox_ is set.
    + * @private
    + */
    +goog.graphics.SvgGraphics.prototype.updateManualViewBox_ = function() {
    +  if (!this.isInDocument() ||
    +      !(this.coordWidth || this.coordLeft || !this.coordTop)) {
    +    return;
    +  }
    +
    +  var size = this.getPixelSize();
    +  if (size.width == 0) {
    +    // In Safari, invisible SVG is sometimes shown.  Explicitly hide it.
    +    this.getElement().style.visibility = 'hidden';
    +    return;
    +  }
    +
    +  this.getElement().style.visibility = '';
    +
    +  var offsetX = - this.coordLeft;
    +  var offsetY = - this.coordTop;
    +  var scaleX = size.width / this.coordWidth;
    +  var scaleY = size.height / this.coordHeight;
    +
    +  this.canvasElement.getElement().setAttribute('transform',
    +      'scale(' + scaleX + ' ' + scaleY + ') ' +
    +      'translate(' + offsetX + ' ' + offsetY + ')');
    +};
    +
    +
    +/**
    + * Change the size of the canvas.
    + * @param {number} pixelWidth The width in pixels.
    + * @param {number} pixelHeight The height in pixels.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.setSize = function(pixelWidth,
    +    pixelHeight) {
    +  goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);
    +};
    +
    +
    +/** @override */
    +goog.graphics.SvgGraphics.prototype.getPixelSize = function() {
    +  if (!goog.userAgent.GECKO) {
    +    return this.isInDocument() ?
    +        goog.style.getSize(this.getElement()) :
    +        goog.graphics.SvgGraphics.base(this, 'getPixelSize');
    +  }
    +
    +  // In Gecko, goog.style.getSize does not work for SVG elements.  We have to
    +  // compute the size manually if it is percentage based.
    +  var width = this.width;
    +  var height = this.height;
    +  var computeWidth = goog.isString(width) && width.indexOf('%') != -1;
    +  var computeHeight = goog.isString(height) && height.indexOf('%') != -1;
    +
    +  if (!this.isInDocument() && (computeWidth || computeHeight)) {
    +    return null;
    +  }
    +
    +  var parent;
    +  var parentSize;
    +
    +  if (computeWidth) {
    +    parent = /** @type {Element} */ (this.getElement().parentNode);
    +    parentSize = goog.style.getSize(parent);
    +    width = parseFloat(/** @type {string} */ (width)) * parentSize.width / 100;
    +  }
    +
    +  if (computeHeight) {
    +    parent = parent || /** @type {Element} */ (this.getElement().parentNode);
    +    parentSize = parentSize || goog.style.getSize(parent);
    +    height = parseFloat(/** @type {string} */ (height)) * parentSize.height /
    +        100;
    +  }
    +
    +  return new goog.math.Size(/** @type {number} */ (width),
    +      /** @type {number} */ (height));
    +};
    +
    +
    +/**
    + * Remove all drawing elements from the graphics.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.clear = function() {
    +  this.canvasElement.clear();
    +  goog.dom.removeChildren(this.defsElement_);
    +  this.defs_ = {};
    +};
    +
    +
    +/**
    + * Draw an ellipse.
    + *
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.EllipseElement} The newly created element.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.drawEllipse = function(
    +    cx, cy, rx, ry, stroke, fill, opt_group) {
    +  var element = this.createSvgElement_('ellipse',
    +      {'cx': cx, 'cy': cy, 'rx': rx, 'ry': ry});
    +  var wrapper = new goog.graphics.SvgEllipseElement(element, this, stroke,
    +      fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a rectangle.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.RectElement} The newly created element.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.drawRect = function(x, y, width, height,
    +    stroke, fill, opt_group) {
    +  var element = this.createSvgElement_('rect',
    +      {'x': x, 'y': y, 'width': width, 'height': height});
    +  var wrapper = new goog.graphics.SvgRectElement(element, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw an image.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of the image.
    + * @param {number} height Height of the image.
    + * @param {string} src The source fo the image.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.ImageElement} The newly created image wrapped in a
    + *     rectangle element.
    + */
    +goog.graphics.SvgGraphics.prototype.drawImage = function(x, y, width, height,
    +    src, opt_group) {
    +  var element = this.createSvgElement_('image', {
    +    'x': x,
    +    'y': y,
    +    'width': width,
    +    'height': height,
    +    'image-rendering': 'optimizeQuality',
    +    'preserveAspectRatio': 'none'
    +  });
    +  element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', src);
    +  var wrapper = new goog.graphics.SvgImageElement(element, this);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a text string vertically centered on a given line.
    + *
    + * @param {string} text The text to draw.
    + * @param {number} x1 X coordinate of start of line.
    + * @param {number} y1 Y coordinate of start of line.
    + * @param {number} x2 X coordinate of end of line.
    + * @param {number} y2 Y coordinate of end of line.
    + * @param {string} align Horizontal alignment: left (default), center, right.
    + * @param {goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.TextElement} The newly created element.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.drawTextOnLine = function(
    +    text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
    +  var angle = Math.round(goog.math.angle(x1, y1, x2, y2));
    +  var dx = x2 - x1;
    +  var dy = y2 - y1;
    +  var lineLength = Math.round(Math.sqrt(dx * dx + dy * dy)); // Length of line
    +
    +  // SVG baseline is on the glyph's base line. We estimate it as 85% of the
    +  // font height. This is just a rough estimate, but do not have a better way.
    +  var fontSize = font.size;
    +  var attributes = {'font-family': font.family, 'font-size': fontSize};
    +  var baseline = Math.round(fontSize * 0.85);
    +  var textY = Math.round(y1 - (fontSize / 2) + baseline);
    +  var textX = x1;
    +  if (align == 'center') {
    +    textX += Math.round(lineLength / 2);
    +    attributes['text-anchor'] = 'middle';
    +  } else if (align == 'right') {
    +    textX += lineLength;
    +    attributes['text-anchor'] = 'end';
    +  }
    +  attributes['x'] = textX;
    +  attributes['y'] = textY;
    +  if (font.bold) {
    +    attributes['font-weight'] = 'bold';
    +  }
    +  if (font.italic) {
    +    attributes['font-style'] = 'italic';
    +  }
    +  if (angle != 0) {
    +    attributes['transform'] = 'rotate(' + angle + ' ' + x1 + ' ' + y1 + ')';
    +  }
    +
    +  var element = this.createSvgElement_('text', attributes);
    +  element.appendChild(this.dom_.getDocument().createTextNode(text));
    +
    +  // Bypass a Firefox-Mac bug where text fill is ignored. If text has no stroke,
    +  // set a stroke, otherwise the text will not be visible.
    +  if (stroke == null && goog.userAgent.GECKO && goog.userAgent.MAC) {
    +    var color = 'black';
    +    // For solid fills, use the fill color
    +    if (fill instanceof goog.graphics.SolidFill) {
    +      color = fill.getColor();
    +    }
    +    stroke = new goog.graphics.Stroke(1, color);
    +  }
    +
    +  var wrapper = new goog.graphics.SvgTextElement(element, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a path.
    + *
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.PathElement} The newly created element.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.drawPath = function(
    +    path, stroke, fill, opt_group) {
    +
    +  var element = this.createSvgElement_('path',
    +      {'d': goog.graphics.SvgGraphics.getSvgPath(path)});
    +  var wrapper = new goog.graphics.SvgPathElement(element, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Returns a string representation of a logical path suitable for use in
    + * an SVG element.
    + *
    + * @param {goog.graphics.Path} path The logical path.
    + * @return {string} The SVG path representation.
    + * @suppress {deprecated} goog.graphics is deprecated.
    + */
    +goog.graphics.SvgGraphics.getSvgPath = function(path) {
    +  var list = [];
    +  path.forEachSegment(function(segment, args) {
    +    switch (segment) {
    +      case goog.graphics.Path.Segment.MOVETO:
    +        list.push('M');
    +        Array.prototype.push.apply(list, args);
    +        break;
    +      case goog.graphics.Path.Segment.LINETO:
    +        list.push('L');
    +        Array.prototype.push.apply(list, args);
    +        break;
    +      case goog.graphics.Path.Segment.CURVETO:
    +        list.push('C');
    +        Array.prototype.push.apply(list, args);
    +        break;
    +      case goog.graphics.Path.Segment.ARCTO:
    +        var extent = args[3];
    +        var toAngle = args[2] + extent;
    +        list.push('A', args[0], args[1],
    +            0, Math.abs(extent) > 180 ? 1 : 0, extent > 0 ? 1 : 0,
    +            args[4], args[5]);
    +        break;
    +      case goog.graphics.Path.Segment.CLOSE:
    +        list.push('Z');
    +        break;
    +    }
    +  });
    +  return list.join(' ');
    +};
    +
    +
    +/**
    + * Create an empty group of drawing elements.
    + *
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.GroupElement} The newly created group.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.createGroup = function(opt_group) {
    +  var element = this.createSvgElement_('g');
    +  var parent = opt_group || this.canvasElement;
    +  parent.getElement().appendChild(element);
    +  return new goog.graphics.SvgGroupElement(element, this);
    +};
    +
    +
    +/**
    + * Measure and return the width (in pixels) of a given text string.
    + * Text measurement is needed to make sure a text can fit in the allocated area.
    + * The way text length is measured is by writing it into a div that is after
    + * the visible area, measure the div width, and immediatly erase the written
    + * value.
    + *
    + * @param {string} text The text string to measure.
    + * @param {goog.graphics.Font} font The font object describing the font style.
    + * @override
    + */
    +goog.graphics.SvgGraphics.prototype.getTextWidth = function(text, font) {
    +  // TODO(user) Implement
    +};
    +
    +
    +/**
    + * Adds a defintion of an element to the global definitions.
    + * @param {string} defKey This is a key that should be unique in a way that
    + *     if two definitions are equal the should have the same key.
    + * @param {Element} defElement DOM element to add as a definition. It must
    + *     have an id attribute set.
    + * @return {string} The assigned id of the defElement.
    + */
    +goog.graphics.SvgGraphics.prototype.addDef = function(defKey, defElement) {
    +  if (defKey in this.defs_) {
    +    return this.defs_[defKey];
    +  }
    +  var id = goog.graphics.SvgGraphics.DEF_ID_PREFIX_ +
    +      goog.graphics.SvgGraphics.nextDefId_++;
    +  defElement.setAttribute('id', id);
    +  this.defs_[defKey] = id;
    +
    +  // Add the def defElement of the defs list.
    +  var defs = this.defsElement_;
    +  defs.appendChild(defElement);
    +  return id;
    +};
    +
    +
    +/**
    + * Returns the id of a definition element.
    + * @param {string} defKey This is a key that should be unique in a way that
    + *     if two definitions are equal the should have the same key.
    + * @return {?string} The id of the found definition element or null if
    + *     not found.
    + */
    +goog.graphics.SvgGraphics.prototype.getDef = function(defKey) {
    +  return defKey in this.defs_ ? this.defs_[defKey] : null;
    +};
    +
    +
    +/**
    + * Removes a definition of an elemnt from the global definitions.
    + * @param {string} defKey This is a key that should be unique in a way that
    + *     if two definitions are equal they should have the same key.
    + */
    +goog.graphics.SvgGraphics.prototype.removeDef = function(defKey) {
    +  var id = this.getDef(defKey);
    +  if (id) {
    +    var element = this.dom_.getElement(id);
    +    this.defsElement_.removeChild(element);
    +    delete this.defs_[defKey];
    +  }
    +};
    +
    +
    +/** @override */
    +goog.graphics.SvgGraphics.prototype.enterDocument = function() {
    +  var oldPixelSize = this.getPixelSize();
    +  goog.graphics.SvgGraphics.superClass_.enterDocument.call(this);
    +
    +  // Dispatch a resize if this is the first time the size value is accurate.
    +  if (!oldPixelSize) {
    +    this.dispatchEvent(goog.events.EventType.RESIZE);
    +  }
    +
    +
    +  // For percentage based heights, listen for changes to size.
    +  if (this.useManualViewbox_) {
    +    var width = this.width;
    +    var height = this.height;
    +
    +    if (typeof width == 'string' && width.indexOf('%') != -1 &&
    +        typeof height == 'string' && height.indexOf('%') != -1) {
    +      // SVG elements don't behave well with respect to size events, so we
    +      // resort to polling.
    +      this.handler_.listen(goog.graphics.SvgGraphics.getResizeCheckTimer_(),
    +          goog.Timer.TICK, this.updateManualViewBox_);
    +    }
    +
    +    this.updateManualViewBox_();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.graphics.SvgGraphics.prototype.exitDocument = function() {
    +  goog.graphics.SvgGraphics.superClass_.exitDocument.call(this);
    +
    +  // Stop polling.
    +  if (this.useManualViewbox_) {
    +    this.handler_.unlisten(goog.graphics.SvgGraphics.getResizeCheckTimer_(),
    +        goog.Timer.TICK, this.updateManualViewBox_);
    +  }
    +};
    +
    +
    +/**
    + * Disposes of the component by removing event handlers, detacing DOM nodes from
    + * the document body, and removing references to them.
    + * @override
    + * @protected
    + */
    +goog.graphics.SvgGraphics.prototype.disposeInternal = function() {
    +  delete this.defs_;
    +  delete this.defsElement_;
    +  delete this.canvasElement;
    +  this.handler_.dispose();
    +  delete this.handler_;
    +  goog.graphics.SvgGraphics.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * The centralized resize checking timer.
    + * @type {goog.Timer|undefined}
    + * @private
    + */
    +goog.graphics.SvgGraphics.resizeCheckTimer_;
    +
    +
    +/**
    + * @return {goog.Timer} The centralized timer object used for interval timing.
    + * @private
    + */
    +goog.graphics.SvgGraphics.getResizeCheckTimer_ = function() {
    +  if (!goog.graphics.SvgGraphics.resizeCheckTimer_) {
    +    goog.graphics.SvgGraphics.resizeCheckTimer_ = new goog.Timer(400);
    +    goog.graphics.SvgGraphics.resizeCheckTimer_.start();
    +  }
    +
    +  return /** @type {goog.Timer} */ (
    +      goog.graphics.SvgGraphics.resizeCheckTimer_);
    +};
    +
    +
    +/** @override */
    +goog.graphics.SvgGraphics.prototype.isDomClonable = function() {
    +  return true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/svggraphics_test.html b/mapbox-gl/closure-library/closure/goog/graphics/svggraphics_test.html
    new file mode 100644
    index 0000000000..bb23ffbeff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/svggraphics_test.html
    @@ -0,0 +1,109 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.graphics.SvgGraphics</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.dom');
    +  goog.require('goog.graphics.SvgGraphics');
    +  goog.require('goog.testing.graphics');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<div id="root"> </div>
    +
    +<script>
    +  var graphics;
    +
    +  function setUp() {
    +    if (!document.createElementNS) {
    +      // Some browsers don't support document.createElementNS and this test
    +      // should not be run on those browsers (IE7,8).
    +      return;
    +    }
    +    graphics = new goog.graphics.SvgGraphics('100px', '100px');
    +    graphics.createDom();
    +    goog.dom.getElement('root').appendChild(graphics.getElement());
    +  }
    +
    +  function testAddDef() {
    +    if (!graphics) {
    +      // setUp has failed (no browser support), we should not run this test.
    +      return;
    +    }
    +    var defElement1 = document.createElement('div');
    +    var defElement2 = document.createElement('div');
    +    var defKey1 = 'def1';
    +    var defKey2 = 'def2';
    +    var id = graphics.addDef(defKey1, defElement1);
    +    assertEquals('_svgdef_0', id);
    +    id = graphics.addDef(defKey1, defElement2);
    +    assertEquals('_svgdef_0', id);
    +    id = graphics.addDef(defKey2, defElement2);
    +    assertEquals('_svgdef_1', id);
    +  }
    +
    +  function testGetDef() {
    +    if (!graphics) {
    +      // setUp has failed (no browser support), we should not run this test.
    +      return;
    +    }
    +    var defElement = document.createElement('div');
    +    var defKey = 'def';
    +    var id = graphics.addDef(defKey, defElement);
    +    assertEquals(id, graphics.getDef(defKey));
    +    assertNull(graphics.getDef('randomKey'));
    +  }
    +
    +  function testRemoveDef() {
    +    if (!graphics) {
    +      // setUp has failed (no browser support), we should not run this test.
    +      return;
    +    }
    +    var defElement = document.createElement('div');
    +    var defKey = 'def';
    +    var addedId = graphics.addDef(defKey, defElement);
    +    graphics.removeDef('randomKey');
    +    assertEquals(addedId, graphics.getDef(defKey));
    +    graphics.removeDef(defKey);
    +    assertNull(graphics.getDef(defKey));
    +  }
    +
    +  function testSetElementAffineTransform() {
    +    if (!graphics) {
    +      // setUp has failed (no browser support), we should not run this test.
    +      return;
    +    }
    +    var fill = new goog.graphics.SolidFill('blue');
    +    var stroke = null;
    +    var rad = -3.1415926 / 6;
    +    var costheta = Math.cos(rad);
    +    var sintheta = Math.sin(rad);
    +    var dx = 10;
    +    var dy = -20;
    +    var affine = new goog.graphics.AffineTransform(
    +      costheta, -sintheta + 1, sintheta, costheta, dx, dy);
    +    var rect = graphics.drawRect(10, 20, 30, 40, stroke, fill);
    +    rect.setTransform(affine);
    +    graphics.render();
    +    var svgMatrix = rect.getElement().getTransformToElement(graphics.getElement());
    +    assertRoughlyEquals(svgMatrix.a, costheta, 0.001);
    +    assertRoughlyEquals(svgMatrix.b, -sintheta + 1, 0.001);
    +    assertRoughlyEquals(svgMatrix.c, sintheta, 0.001);
    +    assertRoughlyEquals(svgMatrix.d, costheta, 0.001);
    +    assertRoughlyEquals(svgMatrix.e, dx, 0.001);
    +    assertRoughlyEquals(svgMatrix.f, dy, 0.001);
    +  }
    +</script>
    +</body>
    +</html>
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/textelement.js b/mapbox-gl/closure-library/closure/goog/graphics/textelement.js
    new file mode 100644
    index 0000000000..c96ae6d1f2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/textelement.js
    @@ -0,0 +1,55 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A thin wrapper around the DOM element for text elements.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.TextElement');
    +
    +goog.require('goog.graphics.StrokeAndFillElement');
    +
    +
    +
    +/**
    + * Interface for a graphics text element.
    + * You should not construct objects from this constructor. The graphics
    + * will return an implementation of this interface for you.
    + *
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.AbstractGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.StrokeAndFillElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + */
    +goog.graphics.TextElement = function(element, graphics, stroke, fill) {
    +  goog.graphics.StrokeAndFillElement.call(this, element, graphics, stroke,
    +      fill);
    +};
    +goog.inherits(goog.graphics.TextElement, goog.graphics.StrokeAndFillElement);
    +
    +
    +/**
    + * Update the displayed text of the element.
    + * @param {string} text The text to draw.
    + */
    +goog.graphics.TextElement.prototype.setText = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/vmlelement.js b/mapbox-gl/closure-library/closure/goog/graphics/vmlelement.js
    new file mode 100644
    index 0000000000..9e72b13273
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/vmlelement.js
    @@ -0,0 +1,438 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Thin wrappers around the DOM element returned from
    + * the different draw methods of the graphics. This is the VML implementation.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.graphics.VmlEllipseElement');
    +goog.provide('goog.graphics.VmlGroupElement');
    +goog.provide('goog.graphics.VmlImageElement');
    +goog.provide('goog.graphics.VmlPathElement');
    +goog.provide('goog.graphics.VmlRectElement');
    +goog.provide('goog.graphics.VmlTextElement');
    +
    +
    +goog.require('goog.dom');
    +goog.require('goog.graphics.EllipseElement');
    +goog.require('goog.graphics.GroupElement');
    +goog.require('goog.graphics.ImageElement');
    +goog.require('goog.graphics.PathElement');
    +goog.require('goog.graphics.RectElement');
    +goog.require('goog.graphics.TextElement');
    +
    +
    +/**
    + * Returns the VML element corresponding to this object.  This method is added
    + * to several classes below.  Note that the return value of this method may
    + * change frequently in IE8, so it should not be cached externally.
    + * @return {Element} The VML element corresponding to this object.
    + * @this {goog.graphics.VmlGroupElement|goog.graphics.VmlEllipseElement|
    + *     goog.graphics.VmlRectElement|goog.graphics.VmlPathElement|
    + *     goog.graphics.VmlTextElement|goog.graphics.VmlImageElement}
    + * @private
    + */
    +goog.graphics.vmlGetElement_ = function() {
    +  this.element_ = this.getGraphics().getVmlElement(this.id_) || this.element_;
    +  return this.element_;
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML group elements.
    + * This is an implementation of the goog.graphics.GroupElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.GroupElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlGroupElement = function(element, graphics) {
    +  this.id_ = element.id;
    +  goog.graphics.GroupElement.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.VmlGroupElement, goog.graphics.GroupElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlGroupElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Remove all drawing elements from the group.
    + * @override
    + */
    +goog.graphics.VmlGroupElement.prototype.clear = function() {
    +  goog.dom.removeChildren(this.getElement());
    +};
    +
    +
    +/**
    + * @return {boolean} True if this group is the root canvas element.
    + * @private
    + */
    +goog.graphics.VmlGroupElement.prototype.isRootElement_ = function() {
    +  return this.getGraphics().getCanvasElement() == this;
    +};
    +
    +
    +/**
    + * Set the size of the group element.
    + * @param {number|string} width The width of the group element.
    + * @param {number|string} height The height of the group element.
    + * @override
    + */
    +goog.graphics.VmlGroupElement.prototype.setSize = function(width, height) {
    +  var element = this.getElement();
    +
    +  var style = element.style;
    +  style.width = /** @suppress {missingRequire} */ (
    +      goog.graphics.VmlGraphics.toSizePx(width));
    +  style.height = /** @suppress {missingRequire} */ (
    +      goog.graphics.VmlGraphics.toSizePx(height));
    +
    +  element.coordsize = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toSizeCoord(width) +
    +      ' ' +
    +      /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toSizeCoord(height);
    +
    +  // Don't overwrite the root element's origin.
    +  if (!this.isRootElement_()) {
    +    element.coordorigin = '0 0';
    +  }
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML ellipse elements.
    + * This is an implementation of the goog.graphics.EllipseElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics  The graphics creating
    + *     this element.
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.EllipseElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlEllipseElement = function(element, graphics,
    +    cx, cy, rx, ry, stroke, fill) {
    +  this.id_ = element.id;
    +
    +  goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill);
    +
    +  // Store center and radius for future calls to setRadius or setCenter.
    +
    +  /**
    +   * X coordinate of the ellipse center.
    +   * @type {number}
    +   */
    +  this.cx = cx;
    +
    +
    +  /**
    +   * Y coordinate of the ellipse center.
    +   * @type {number}
    +   */
    +  this.cy = cy;
    +
    +
    +  /**
    +   * Radius length for the x-axis.
    +   * @type {number}
    +   */
    +  this.rx = rx;
    +
    +
    +  /**
    +   * Radius length for the y-axis.
    +   * @type {number}
    +   */
    +  this.ry = ry;
    +};
    +goog.inherits(goog.graphics.VmlEllipseElement, goog.graphics.EllipseElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlEllipseElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Update the center point of the ellipse.
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @override
    + */
    +goog.graphics.VmlEllipseElement.prototype.setCenter = function(cx, cy) {
    +  this.cx = cx;
    +  this.cy = cy;
    +  /** @suppress {missingRequire} */
    +  goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(),
    +      cx - this.rx, cy - this.ry, this.rx * 2, this.ry * 2);
    +};
    +
    +
    +/**
    + * Update the radius of the ellipse.
    + * @param {number} rx Center X coordinate.
    + * @param {number} ry Center Y coordinate.
    + * @override
    + */
    +goog.graphics.VmlEllipseElement.prototype.setRadius = function(rx, ry) {
    +  this.rx = rx;
    +  this.ry = ry;
    +  /** @suppress {missingRequire} */
    +  goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(),
    +      this.cx - rx, this.cy - ry, rx * 2, ry * 2);
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML rectangle elements.
    + * This is an implementation of the goog.graphics.RectElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.RectElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlRectElement = function(element, graphics, stroke, fill) {
    +  this.id_ = element.id;
    +  goog.graphics.RectElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.VmlRectElement, goog.graphics.RectElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlRectElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Update the position of the rectangle.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.VmlRectElement.prototype.setPosition = function(x, y) {
    +  var style = this.getElement().style;
    +
    +  style.left = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(x);
    +  style.top = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(y);
    +};
    +
    +
    +/**
    + * Update the size of the rectangle.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @override
    + */
    +goog.graphics.VmlRectElement.prototype.setSize = function(width, height) {
    +  var style = this.getElement().style;
    +  style.width = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toSizePx(width);
    +  style.height = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toSizePx(height);
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML path elements.
    + * This is an implementation of the goog.graphics.PathElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.PathElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlPathElement = function(element, graphics, stroke, fill) {
    +  this.id_ = element.id;
    +  goog.graphics.PathElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.VmlPathElement, goog.graphics.PathElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlPathElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Update the underlying path.
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @override
    + */
    +goog.graphics.VmlPathElement.prototype.setPath = function(path) {
    +  /** @suppress {missingRequire} */
    +  goog.graphics.VmlGraphics.setAttribute(
    +      this.getElement(), 'path',
    +      /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.getVmlPath(path));
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML text elements.
    + * This is an implementation of the goog.graphics.TextElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics The graphics creating
    + *     this element.
    + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element.
    + * @param {goog.graphics.Fill?} fill The fill to use for this element.
    + * @constructor
    + * @extends {goog.graphics.TextElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlTextElement = function(element, graphics, stroke, fill) {
    +  this.id_ = element.id;
    +  goog.graphics.TextElement.call(this, element, graphics, stroke, fill);
    +};
    +goog.inherits(goog.graphics.VmlTextElement, goog.graphics.TextElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlTextElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Update the displayed text of the element.
    + * @param {string} text The text to draw.
    + * @override
    + */
    +goog.graphics.VmlTextElement.prototype.setText = function(text) {
    +  /** @suppress {missingRequire} */
    +  goog.graphics.VmlGraphics.setAttribute(this.getElement().childNodes[1],
    +      'string', text);
    +};
    +
    +
    +
    +/**
    + * Thin wrapper for VML image elements.
    + * This is an implementation of the goog.graphics.ImageElement interface.
    + * You should not construct objects from this constructor. The graphics
    + * will return the object for you.
    + * @param {Element} element The DOM element to wrap.
    + * @param {goog.graphics.VmlGraphics} graphics The graphics creating
    + *     this element.
    + * @constructor
    + * @extends {goog.graphics.ImageElement}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlImageElement = function(element, graphics) {
    +  this.id_ = element.id;
    +  goog.graphics.ImageElement.call(this, element, graphics);
    +};
    +goog.inherits(goog.graphics.VmlImageElement, goog.graphics.ImageElement);
    +
    +
    +/** @override */
    +goog.graphics.VmlImageElement.prototype.getElement =
    +    goog.graphics.vmlGetElement_;
    +
    +
    +/**
    + * Update the position of the image.
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @override
    + */
    +goog.graphics.VmlImageElement.prototype.setPosition = function(x, y) {
    +  var style = this.getElement().style;
    +
    +  style.left = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(x);
    +  style.top = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(y);
    +};
    +
    +
    +/**
    + * Update the size of the image.
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @override
    + */
    +goog.graphics.VmlImageElement.prototype.setSize = function(width, height) {
    +  var style = this.getElement().style;
    +  style.width = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(width);
    +  style.height = /** @suppress {missingRequire} */
    +      goog.graphics.VmlGraphics.toPosPx(height);
    +};
    +
    +
    +/**
    + * Update the source of the image.
    + * @param {string} src Source of the image.
    + * @override
    + */
    +goog.graphics.VmlImageElement.prototype.setSource = function(src) {
    +  /** @suppress {missingRequire} */
    +  goog.graphics.VmlGraphics.setAttribute(this.getElement(), 'src', src);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/graphics/vmlgraphics.js b/mapbox-gl/closure-library/closure/goog/graphics/vmlgraphics.js
    new file mode 100644
    index 0000000000..27e7599abc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/graphics/vmlgraphics.js
    @@ -0,0 +1,934 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview VmlGraphics sub class that uses VML to draw the graphics.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.graphics.VmlGraphics');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.graphics.AbstractGraphics');
    +goog.require('goog.graphics.LinearGradient');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.graphics.VmlEllipseElement');
    +goog.require('goog.graphics.VmlGroupElement');
    +goog.require('goog.graphics.VmlImageElement');
    +goog.require('goog.graphics.VmlPathElement');
    +goog.require('goog.graphics.VmlRectElement');
    +goog.require('goog.graphics.VmlTextElement');
    +goog.require('goog.math');
    +goog.require('goog.math.Size');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * A Graphics implementation for drawing using VML.
    + * @param {string|number} width The (non-zero) width in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {string|number} height The (non-zero) height in pixels.  Strings
    + *     expressing percentages of parent with (e.g. '80%') are also accepted.
    + * @param {?number=} opt_coordWidth The coordinate width - if
    + *     omitted or null, defaults to same as width.
    + * @param {?number=} opt_coordHeight The coordinate height - if
    + *     omitted or null, defaults to same as height.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @constructor
    + * @extends {goog.graphics.AbstractGraphics}
    + * @deprecated goog.graphics is deprecated. It existed to abstract over browser
    + *     differences before the canvas tag was widely supported.  See
    + *     http://en.wikipedia.org/wiki/Canvas_element for details.
    + * @final
    + */
    +goog.graphics.VmlGraphics = function(width, height,
    +                                     opt_coordWidth, opt_coordHeight,
    +                                     opt_domHelper) {
    +  goog.graphics.AbstractGraphics.call(this, width, height,
    +                                      opt_coordWidth, opt_coordHeight,
    +                                      opt_domHelper);
    +  this.handler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.handler_);
    +};
    +goog.inherits(goog.graphics.VmlGraphics, goog.graphics.AbstractGraphics);
    +
    +
    +/**
    + * The prefix to use for VML elements
    + * @private
    + * @type {string}
    + */
    +goog.graphics.VmlGraphics.VML_PREFIX_ = 'g_vml_';
    +
    +
    +/**
    + * The VML namespace URN
    + * @private
    + * @type {string}
    + */
    +goog.graphics.VmlGraphics.VML_NS_ = 'urn:schemas-microsoft-com:vml';
    +
    +
    +/**
    + * The VML behavior URL.
    + * @private
    + * @type {string}
    + */
    +goog.graphics.VmlGraphics.VML_IMPORT_ = '#default#VML';
    +
    +
    +/**
    + * Whether the document is using IE8 standards mode, and therefore needs hacks.
    + * @private
    + * @type {boolean}
    + */
    +goog.graphics.VmlGraphics.IE8_MODE_ = document.documentMode &&
    +    document.documentMode >= 8;
    +
    +
    +/**
    + * The coordinate multiplier to allow sub-pixel rendering
    + * @type {number}
    + */
    +goog.graphics.VmlGraphics.COORD_MULTIPLIER = 100;
    +
    +
    +/**
    + * Converts the given size to a css size.  If it is a percentage, leaves it
    + * alone.  Otherwise assumes px.
    + *
    + * @param {number|string} size The size to use.
    + * @return {string} The position adjusted for COORD_MULTIPLIER.
    + */
    +goog.graphics.VmlGraphics.toCssSize = function(size) {
    +  return goog.isString(size) && goog.string.endsWith(size, '%') ?
    +         size : parseFloat(size.toString()) + 'px';
    +};
    +
    +
    +/**
    + * Multiplies positioning coordinates by COORD_MULTIPLIER to allow sub-pixel
    + * coordinates.  Also adds a half pixel offset to match SVG.
    + *
    + * This function is internal for the VML supporting classes, and
    + * should not be used externally.
    + *
    + * @param {number|string} number A position in pixels.
    + * @return {number} The position adjusted for COORD_MULTIPLIER.
    + */
    +goog.graphics.VmlGraphics.toPosCoord = function(number) {
    +  return Math.round((parseFloat(number.toString()) - 0.5) *
    +      goog.graphics.VmlGraphics.COORD_MULTIPLIER);
    +};
    +
    +
    +/**
    + * Add a "px" suffix to a number of pixels, and multiplies all coordinates by
    + * COORD_MULTIPLIER to allow sub-pixel coordinates.
    + *
    + * This function is internal for the VML supporting classes, and
    + * should not be used externally.
    + *
    + * @param {number|string} number A position in pixels.
    + * @return {string} The position with suffix 'px'.
    + */
    +goog.graphics.VmlGraphics.toPosPx = function(number) {
    +  return goog.graphics.VmlGraphics.toPosCoord(number) + 'px';
    +};
    +
    +
    +/**
    + * Multiplies the width or height coordinate by COORD_MULTIPLIER to allow
    + * sub-pixel coordinates.
    + *
    + * This function is internal for the VML supporting classes, and
    + * should not be used externally.
    + *
    + * @param {string|number} number A size in units.
    + * @return {number} The size multiplied by the correct factor.
    + */
    +goog.graphics.VmlGraphics.toSizeCoord = function(number) {
    +  return Math.round(parseFloat(number.toString()) *
    +      goog.graphics.VmlGraphics.COORD_MULTIPLIER);
    +};
    +
    +
    +/**
    + * Add a "px" suffix to a number of pixels, and multiplies all coordinates by
    + * COORD_MULTIPLIER to allow sub-pixel coordinates.
    + *
    + * This function is internal for the VML supporting classes, and
    + * should not be used externally.
    + *
    + * @param {number|string} number A size in pixels.
    + * @return {string} The size with suffix 'px'.
    + */
    +goog.graphics.VmlGraphics.toSizePx = function(number) {
    +  return goog.graphics.VmlGraphics.toSizeCoord(number) + 'px';
    +};
    +
    +
    +/**
    + * Sets an attribute on the given VML element, in the way best suited to the
    + * current version of IE.  Should only be used in the goog.graphics package.
    + * @param {Element} element The element to set an attribute
    + *     on.
    + * @param {string} name The name of the attribute to set.
    + * @param {string} value The value to set it to.
    + */
    +goog.graphics.VmlGraphics.setAttribute = function(element, name, value) {
    +  if (goog.graphics.VmlGraphics.IE8_MODE_) {
    +    element[name] = value;
    +  } else {
    +    element.setAttribute(name, value);
    +  }
    +};
    +
    +
    +/**
    + * Event handler.
    + * @type {goog.events.EventHandler}
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.handler_;
    +
    +
    +/**
    + * Creates a VML element. Used internally and by different VML classes.
    + * @param {string} tagName The type of element to create.
    + * @return {!Element} The created element.
    + */
    +goog.graphics.VmlGraphics.prototype.createVmlElement = function(tagName) {
    +  var element =
    +      this.dom_.createElement(goog.graphics.VmlGraphics.VML_PREFIX_ + ':' +
    +                              tagName);
    +  element.id = goog.string.createUniqueString();
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the VML element with the given id that is a child of this graphics
    + * object.
    + * Should be considered package private, and not used externally.
    + * @param {string} id The element id to find.
    + * @return {Element} The element with the given id, or null if none is found.
    + */
    +goog.graphics.VmlGraphics.prototype.getVmlElement = function(id) {
    +  return this.dom_.getElement(id);
    +};
    +
    +
    +/**
    + * Resets the graphics so they will display properly on IE8.  Noop in older
    + * versions.
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.updateGraphics_ = function() {
    +  if (goog.graphics.VmlGraphics.IE8_MODE_ && this.isInDocument()) {
    +    this.getElement().innerHTML = this.getElement().innerHTML;
    +  }
    +};
    +
    +
    +/**
    + * Appends an element.
    + *
    + * @param {goog.graphics.Element} element The element wrapper.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.append_ = function(element, opt_group) {
    +  var parent = opt_group || this.canvasElement;
    +  parent.getElement().appendChild(element.getElement());
    +  this.updateGraphics_();
    +};
    +
    +
    +/**
    + * Sets the fill for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Fill?} fill The fill object.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setElementFill = function(element, fill) {
    +  var vmlElement = element.getElement();
    +  goog.graphics.VmlGraphics.removeFill_(vmlElement);
    +  if (fill instanceof goog.graphics.SolidFill) {
    +    // NOTE(arv): VML does not understand 'transparent' so hard code support
    +    // for it.
    +    if (fill.getColor() == 'transparent') {
    +      vmlElement.filled = false;
    +    } else if (fill.getOpacity() != 1) {
    +      vmlElement.filled = true;
    +      // Set opacity (number 0-1 is translated to percent)
    +      var fillNode = this.createVmlElement('fill');
    +      fillNode.opacity = Math.round(fill.getOpacity() * 100) + '%';
    +      fillNode.color = fill.getColor();
    +      vmlElement.appendChild(fillNode);
    +    } else {
    +      vmlElement.filled = true;
    +      vmlElement.fillcolor = fill.getColor();
    +    }
    +  } else if (fill instanceof goog.graphics.LinearGradient) {
    +    vmlElement.filled = true;
    +    // Add a 'fill' element
    +    var gradient = this.createVmlElement('fill');
    +    gradient.color = fill.getColor1();
    +    gradient.color2 = fill.getColor2();
    +    if (goog.isNumber(fill.getOpacity1())) {
    +      gradient.opacity = fill.getOpacity1();
    +    }
    +    if (goog.isNumber(fill.getOpacity2())) {
    +      gradient.opacity2 = fill.getOpacity2();
    +    }
    +    var angle = goog.math.angle(fill.getX1(), fill.getY1(),
    +        fill.getX2(), fill.getY2());
    +    // Our angles start from 0 to the right, and grow clockwise.
    +    // MSIE starts from 0 to top, and grows anti-clockwise.
    +    angle = Math.round(goog.math.standardAngle(270 - angle));
    +    gradient.angle = angle;
    +    gradient.type = 'gradient';
    +    vmlElement.appendChild(gradient);
    +  } else {
    +    vmlElement.filled = false;
    +  }
    +  this.updateGraphics_();
    +};
    +
    +
    +/**
    + * Sets the stroke for the given element.
    + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper.
    + * @param {goog.graphics.Stroke?} stroke The stroke object.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setElementStroke = function(element,
    +    stroke) {
    +  var vmlElement = element.getElement();
    +  if (stroke) {
    +    vmlElement.stroked = true;
    +
    +    var width = stroke.getWidth();
    +    if (goog.isString(width) && width.indexOf('px') == -1) {
    +      width = parseFloat(width);
    +    } else {
    +      width = width * this.getPixelScaleX();
    +    }
    +
    +    var strokeElement = vmlElement.getElementsByTagName('stroke')[0];
    +    if (!strokeElement) {
    +      strokeElement = strokeElement || this.createVmlElement('stroke');
    +      vmlElement.appendChild(strokeElement);
    +    }
    +    strokeElement.opacity = stroke.getOpacity();
    +    strokeElement.weight = width + 'px';
    +    strokeElement.color = stroke.getColor();
    +  } else {
    +    vmlElement.stroked = false;
    +  }
    +  this.updateGraphics_();
    +};
    +
    +
    +/**
    + * Set the translation and rotation of an element.
    + *
    + * If a more general affine transform is needed than this provides
    + * (e.g. skew and scale) then use setElementAffineTransform.
    + * @param {number} x The x coordinate of the translation transform.
    + * @param {number} y The y coordinate of the translation transform.
    + * @param {number} angle The angle of the rotation transform.
    + * @param {number} centerX The horizontal center of the rotation transform.
    + * @param {number} centerY The vertical center of the rotation transform.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setElementTransform = function(element, x,
    +    y, angle, centerX, centerY) {
    +  var el = element.getElement();
    +
    +  el.style.left = goog.graphics.VmlGraphics.toPosPx(x);
    +  el.style.top = goog.graphics.VmlGraphics.toPosPx(y);
    +  if (angle || el.rotation) {
    +    el.rotation = angle;
    +    el.coordsize = goog.graphics.VmlGraphics.toSizeCoord(centerX * 2) + ' ' +
    +        goog.graphics.VmlGraphics.toSizeCoord(centerY * 2);
    +  }
    +};
    +
    +
    +/**
    + * Set the transformation of an element.
    + * @param {!goog.graphics.Element} element The element wrapper.
    + * @param {!goog.graphics.AffineTransform} affineTransform The
    + *     transformation applied to this element.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setElementAffineTransform = function(
    +    element, affineTransform) {
    +  var t = affineTransform;
    +  var vmlElement = element.getElement();
    +  goog.graphics.VmlGraphics.removeSkew_(vmlElement);
    +  var skewNode = this.createVmlElement('skew');
    +  skewNode.on = 'true';
    +  // Move the transform origin to 0px,0px of the graphics.
    +  // In VML, 0,0 means the center of the element, -0.5,-0.5 left top conner of
    +  // it.
    +  skewNode.origin =
    +      (-vmlElement.style.pixelLeft / vmlElement.style.pixelWidth - 0.5) + ',' +
    +      (-vmlElement.style.pixelTop / vmlElement.style.pixelHeight - 0.5);
    +  skewNode.offset = t.getTranslateX().toFixed(1) + 'px,' +
    +                    t.getTranslateY().toFixed(1) + 'px';
    +  skewNode.matrix = [t.getScaleX().toFixed(6), t.getShearX().toFixed(6),
    +                     t.getShearY().toFixed(6), t.getScaleY().toFixed(6),
    +                     0, 0].join(',');
    +  vmlElement.appendChild(skewNode);
    +  this.updateGraphics_();
    +};
    +
    +
    +/**
    + * Removes the skew information from a dom element.
    + * @param {Element} element DOM element.
    + * @private
    + */
    +goog.graphics.VmlGraphics.removeSkew_ = function(element) {
    +  goog.array.forEach(element.childNodes, function(child) {
    +    if (child.tagName == 'skew') {
    +      element.removeChild(child);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Removes the fill information from a dom element.
    + * @param {Element} element DOM element.
    + * @private
    + */
    +goog.graphics.VmlGraphics.removeFill_ = function(element) {
    +  element.fillcolor = '';
    +  goog.array.forEach(element.childNodes, function(child) {
    +    if (child.tagName == 'fill') {
    +      element.removeChild(child);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Set top, left, width and height for an element.
    + * This function is internal for the VML supporting classes, and
    + * should not be used externally.
    + *
    + * @param {Element} element DOM element.
    + * @param {number} left Left ccordinate in pixels.
    + * @param {number} top Top ccordinate in pixels.
    + * @param {number} width Width in pixels.
    + * @param {number} height Height in pixels.
    + */
    +goog.graphics.VmlGraphics.setPositionAndSize = function(
    +    element, left, top, width, height) {
    +  var style = element.style;
    +  style.position = 'absolute';
    +  style.left = goog.graphics.VmlGraphics.toPosPx(left);
    +  style.top = goog.graphics.VmlGraphics.toPosPx(top);
    +  style.width = goog.graphics.VmlGraphics.toSizePx(width);
    +  style.height = goog.graphics.VmlGraphics.toSizePx(height);
    +
    +  if (element.tagName == 'shape') {
    +    element.coordsize = goog.graphics.VmlGraphics.toSizeCoord(width) + ' ' +
    +                        goog.graphics.VmlGraphics.toSizeCoord(height);
    +  }
    +};
    +
    +
    +/**
    + * Creates an element spanning the surface.
    + *
    + * @param {string} type The type of element to create.
    + * @return {!Element} The created, positioned, and sized element.
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.createFullSizeElement_ = function(type) {
    +  var element = this.createVmlElement(type);
    +  var size = this.getCoordSize();
    +  goog.graphics.VmlGraphics.setPositionAndSize(element, 0, 0, size.width,
    +      size.height);
    +  return element;
    +};
    +
    +
    +/**
    + * IE magic - if this "no-op" line is not here, the if statement below will
    + * fail intermittently.  The eval is used to prevent the JsCompiler from
    + * stripping this piece of code, which it quite reasonably thinks is doing
    + * nothing. Put it in try-catch block to prevent "Unspecified Error" when
    + * this statement is executed in a defer JS in IE.
    + * More info here:
    + * http://www.mail-archive.com/users@openlayers.org/msg01838.html
    + */
    +try {
    +  eval('document.namespaces');
    +} catch (ex) {}
    +
    +
    +/**
    + * Creates the DOM representation of the graphics area.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.createDom = function() {
    +  var doc = this.dom_.getDocument();
    +
    +  // Add the namespace.
    +  if (!doc.namespaces[goog.graphics.VmlGraphics.VML_PREFIX_]) {
    +    if (goog.graphics.VmlGraphics.IE8_MODE_) {
    +      doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_,
    +                         goog.graphics.VmlGraphics.VML_NS_,
    +                         goog.graphics.VmlGraphics.VML_IMPORT_);
    +    } else {
    +      doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_,
    +                         goog.graphics.VmlGraphics.VML_NS_);
    +    }
    +
    +    // We assume that we only need to add the CSS if the namespace was not
    +    // present
    +    var ss = doc.createStyleSheet();
    +    ss.cssText = goog.graphics.VmlGraphics.VML_PREFIX_ + '\\:*' +
    +                 '{behavior:url(#default#VML)}';
    +  }
    +
    +  // Outer a DIV with overflow hidden for clipping.
    +  // All inner elements are absolutly positioned on-top of this div.
    +  var pixelWidth = this.width;
    +  var pixelHeight = this.height;
    +  var divElement = this.dom_.createDom('div', {
    +    'style': 'overflow:hidden;position:relative;width:' +
    +        goog.graphics.VmlGraphics.toCssSize(pixelWidth) + ';height:' +
    +        goog.graphics.VmlGraphics.toCssSize(pixelHeight)
    +  });
    +
    +  this.setElementInternal(divElement);
    +
    +  var group = this.createVmlElement('group');
    +  var style = group.style;
    +
    +  style.position = 'absolute';
    +  style.left = style.top = 0;
    +  style.width = this.width;
    +  style.height = this.height;
    +  if (this.coordWidth) {
    +    group.coordsize =
    +        goog.graphics.VmlGraphics.toSizeCoord(this.coordWidth) + ' ' +
    +        goog.graphics.VmlGraphics.toSizeCoord(
    +            /** @type {number} */ (this.coordHeight));
    +  } else {
    +    group.coordsize = goog.graphics.VmlGraphics.toSizeCoord(pixelWidth) + ' ' +
    +        goog.graphics.VmlGraphics.toSizeCoord(pixelHeight);
    +  }
    +
    +  if (goog.isDef(this.coordLeft)) {
    +    group.coordorigin = goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) +
    +        ' ' + goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);
    +  } else {
    +    group.coordorigin = '0 0';
    +  }
    +  divElement.appendChild(group);
    +
    +  this.canvasElement = new goog.graphics.VmlGroupElement(group, this);
    +
    +  goog.events.listen(divElement, goog.events.EventType.RESIZE, goog.bind(
    +      this.handleContainerResize_, this));
    +};
    +
    +
    +/**
    + * Changes the canvas element size to match the container element size.
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.handleContainerResize_ = function() {
    +  var size = goog.style.getSize(this.getElement());
    +  var style = this.canvasElement.getElement().style;
    +
    +  if (size.width) {
    +    style.width = size.width + 'px';
    +    style.height = size.height + 'px';
    +  } else {
    +    var current = this.getElement();
    +    while (current && current.currentStyle &&
    +        current.currentStyle.display != 'none') {
    +      current = current.parentNode;
    +    }
    +    if (current && current.currentStyle) {
    +      this.handler_.listen(current, 'propertychange',
    +          this.handleContainerResize_);
    +    }
    +  }
    +
    +  this.dispatchEvent(goog.events.EventType.RESIZE);
    +};
    +
    +
    +/**
    + * Handle property changes on hidden ancestors.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.graphics.VmlGraphics.prototype.handlePropertyChange_ = function(e) {
    +  var prop = e.getBrowserEvent().propertyName;
    +  if (prop == 'display' || prop == 'className') {
    +    this.handler_.unlisten(/** @type {Element} */(e.target),
    +        'propertychange', this.handlePropertyChange_);
    +    this.handleContainerResize_();
    +  }
    +};
    +
    +
    +/**
    + * Changes the coordinate system position.
    + * @param {number} left The coordinate system left bound.
    + * @param {number} top The coordinate system top bound.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setCoordOrigin = function(left, top) {
    +  this.coordLeft = left;
    +  this.coordTop = top;
    +
    +  this.canvasElement.getElement().coordorigin =
    +      goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) + ' ' +
    +      goog.graphics.VmlGraphics.toSizeCoord(this.coordTop);
    +};
    +
    +
    +/**
    + * Changes the coordinate size.
    + * @param {number} coordWidth The coordinate width.
    + * @param {number} coordHeight The coordinate height.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setCoordSize = function(coordWidth,
    +                                                            coordHeight) {
    +  goog.graphics.VmlGraphics.superClass_.setCoordSize.apply(this, arguments);
    +
    +  this.canvasElement.getElement().coordsize =
    +      goog.graphics.VmlGraphics.toSizeCoord(coordWidth) + ' ' +
    +      goog.graphics.VmlGraphics.toSizeCoord(coordHeight);
    +};
    +
    +
    +/**
    + * Change the size of the canvas.
    + * @param {number} pixelWidth The width in pixels.
    + * @param {number} pixelHeight The height in pixels.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.setSize = function(pixelWidth,
    +    pixelHeight) {
    +  goog.style.setSize(this.getElement(), pixelWidth, pixelHeight);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Size} Returns the number of pixels spanned by the
    + *     surface.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.getPixelSize = function() {
    +  var el = this.getElement();
    +  // The following relies on the fact that the size can never be 0.
    +  return new goog.math.Size(el.style.pixelWidth || el.offsetWidth || 1,
    +      el.style.pixelHeight || el.offsetHeight || 1);
    +};
    +
    +
    +/**
    + * Remove all drawing elements from the graphics.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.clear = function() {
    +  this.canvasElement.clear();
    +};
    +
    +
    +/**
    + * Draw an ellipse.
    + *
    + * @param {number} cx Center X coordinate.
    + * @param {number} cy Center Y coordinate.
    + * @param {number} rx Radius length for the x-axis.
    + * @param {number} ry Radius length for the y-axis.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.EllipseElement} The newly created element.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.drawEllipse = function(cx, cy, rx, ry,
    +    stroke, fill, opt_group) {
    +  var element = this.createVmlElement('oval');
    +  goog.graphics.VmlGraphics.setPositionAndSize(element, cx - rx, cy - ry,
    +      rx * 2, ry * 2);
    +  var wrapper = new goog.graphics.VmlEllipseElement(element, this,
    +      cx, cy, rx, ry, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a rectangle.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of rectangle.
    + * @param {number} height Height of rectangle.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the
    + *    stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.RectElement} The newly created element.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.drawRect = function(x, y, width, height,
    +    stroke, fill, opt_group) {
    +  var element = this.createVmlElement('rect');
    +  goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);
    +  var wrapper = new goog.graphics.VmlRectElement(element, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw an image.
    + *
    + * @param {number} x X coordinate (left).
    + * @param {number} y Y coordinate (top).
    + * @param {number} width Width of image.
    + * @param {number} height Height of image.
    + * @param {string} src Source of the image.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.ImageElement} The newly created element.
    + */
    +goog.graphics.VmlGraphics.prototype.drawImage = function(x, y, width, height,
    +    src, opt_group) {
    +  var element = this.createVmlElement('image');
    +  goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height);
    +  goog.graphics.VmlGraphics.setAttribute(element, 'src', src);
    +  var wrapper = new goog.graphics.VmlImageElement(element, this);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a text string vertically centered on a given line.
    + *
    + * @param {string} text The text to draw.
    + * @param {number} x1 X coordinate of start of line.
    + * @param {number} y1 Y coordinate of start of line.
    + * @param {number} x2 X coordinate of end of line.
    + * @param {number} y2 Y coordinate of end of line.
    + * @param {?string} align Horizontal alignment: left (default), center, right.
    + * @param {goog.graphics.Font} font Font describing the font properties.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.TextElement} The newly created element.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.drawTextOnLine = function(
    +    text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) {
    +  var shape = this.createFullSizeElement_('shape');
    +
    +  var pathElement = this.createVmlElement('path');
    +  var path = 'M' + goog.graphics.VmlGraphics.toPosCoord(x1) + ',' +
    +             goog.graphics.VmlGraphics.toPosCoord(y1) + 'L' +
    +             goog.graphics.VmlGraphics.toPosCoord(x2) + ',' +
    +             goog.graphics.VmlGraphics.toPosCoord(y2) + 'E';
    +  goog.graphics.VmlGraphics.setAttribute(pathElement, 'v', path);
    +  goog.graphics.VmlGraphics.setAttribute(pathElement, 'textpathok', 'true');
    +
    +  var textPathElement = this.createVmlElement('textpath');
    +  textPathElement.setAttribute('on', 'true');
    +  var style = textPathElement.style;
    +  style.fontSize = font.size * this.getPixelScaleX();
    +  style.fontFamily = font.family;
    +  if (align != null) {
    +    style['v-text-align'] = align;
    +  }
    +  if (font.bold) {
    +    style.fontWeight = 'bold';
    +  }
    +  if (font.italic) {
    +    style.fontStyle = 'italic';
    +  }
    +  goog.graphics.VmlGraphics.setAttribute(textPathElement, 'string', text);
    +
    +  shape.appendChild(pathElement);
    +  shape.appendChild(textPathElement);
    +  var wrapper = new goog.graphics.VmlTextElement(shape, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Draw a path.
    + *
    + * @param {!goog.graphics.Path} path The path object to draw.
    + * @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke.
    + * @param {goog.graphics.Fill?} fill Fill object describing the fill.
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.PathElement} The newly created element.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.drawPath = function(path, stroke, fill,
    +    opt_group) {
    +  var element = this.createFullSizeElement_('shape');
    +  goog.graphics.VmlGraphics.setAttribute(element, 'path',
    +      goog.graphics.VmlGraphics.getVmlPath(path));
    +
    +  var wrapper = new goog.graphics.VmlPathElement(element, this, stroke, fill);
    +  this.append_(wrapper, opt_group);
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Returns a string representation of a logical path suitable for use in
    + * a VML element.
    + *
    + * @param {goog.graphics.Path} path The logical path.
    + * @return {string} The VML path representation.
    + * @suppress {deprecated} goog.graphics is deprecated.
    + */
    +goog.graphics.VmlGraphics.getVmlPath = function(path) {
    +  var list = [];
    +  path.forEachSegment(function(segment, args) {
    +    switch (segment) {
    +      case goog.graphics.Path.Segment.MOVETO:
    +        list.push('m');
    +        Array.prototype.push.apply(list, goog.array.map(args,
    +            goog.graphics.VmlGraphics.toSizeCoord));
    +        break;
    +      case goog.graphics.Path.Segment.LINETO:
    +        list.push('l');
    +        Array.prototype.push.apply(list, goog.array.map(args,
    +            goog.graphics.VmlGraphics.toSizeCoord));
    +        break;
    +      case goog.graphics.Path.Segment.CURVETO:
    +        list.push('c');
    +        Array.prototype.push.apply(list, goog.array.map(args,
    +            goog.graphics.VmlGraphics.toSizeCoord));
    +        break;
    +      case goog.graphics.Path.Segment.CLOSE:
    +        list.push('x');
    +        break;
    +      case goog.graphics.Path.Segment.ARCTO:
    +        var toAngle = args[2] + args[3];
    +        var cx = goog.graphics.VmlGraphics.toSizeCoord(
    +            args[4] - goog.math.angleDx(toAngle, args[0]));
    +        var cy = goog.graphics.VmlGraphics.toSizeCoord(
    +            args[5] - goog.math.angleDy(toAngle, args[1]));
    +        var rx = goog.graphics.VmlGraphics.toSizeCoord(args[0]);
    +        var ry = goog.graphics.VmlGraphics.toSizeCoord(args[1]);
    +        // VML angles are in fd units (see http://www.w3.org/TR/NOTE-VML) and
    +        // are positive counter-clockwise.
    +        var fromAngle = Math.round(args[2] * -65536);
    +        var extent = Math.round(args[3] * -65536);
    +        list.push('ae', cx, cy, rx, ry, fromAngle, extent);
    +        break;
    +    }
    +  });
    +  return list.join(' ');
    +};
    +
    +
    +/**
    + * Create an empty group of drawing elements.
    + *
    + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element
    + *     to append to. If not specified, appends to the main canvas.
    + *
    + * @return {!goog.graphics.GroupElement} The newly created group.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.createGroup = function(opt_group) {
    +  var element = this.createFullSizeElement_('group');
    +  var parent = opt_group || this.canvasElement;
    +  parent.getElement().appendChild(element);
    +  return new goog.graphics.VmlGroupElement(element, this);
    +};
    +
    +
    +/**
    + * Measure and return the width (in pixels) of a given text string.
    + * Text measurement is needed to make sure a text can fit in the allocated
    + * area. The way text length is measured is by writing it into a div that is
    + * after the visible area, measure the div width, and immediatly erase the
    + * written value.
    + *
    + * @param {string} text The text string to measure.
    + * @param {goog.graphics.Font} font The font object describing the font style.
    + *
    + * @return {number} The width in pixels of the text strings.
    + * @override
    + */
    +goog.graphics.VmlGraphics.prototype.getTextWidth = function(text, font) {
    +  // TODO(arv): Implement
    +  return 0;
    +};
    +
    +
    +/** @override */
    +goog.graphics.VmlGraphics.prototype.enterDocument = function() {
    +  goog.graphics.VmlGraphics.superClass_.enterDocument.call(this);
    +  this.handleContainerResize_();
    +  this.updateGraphics_();
    +};
    +
    +
    +/**
    + * Disposes of the component by removing event handlers, detacing DOM nodes from
    + * the document body, and removing references to them.
    + * @override
    + * @protected
    + */
    +goog.graphics.VmlGraphics.prototype.disposeInternal = function() {
    +  this.canvasElement = null;
    +  goog.graphics.VmlGraphics.superClass_.disposeInternal.call(this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/history/event.js b/mapbox-gl/closure-library/closure/goog/history/event.js
    new file mode 100644
    index 0000000000..19250df4c3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/event.js
    @@ -0,0 +1,55 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The event object dispatched when the history changes.
    + *
    + */
    +
    +
    +goog.provide('goog.history.Event');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.history.EventType');
    +
    +
    +
    +/**
    + * Event object dispatched after the history state has changed.
    + * @param {string} token The string identifying the new history state.
    + * @param {boolean} isNavigation True if the event was triggered by a browser
    + *     action, such as forward or back, clicking on a link, editing the URL, or
    + *     calling {@code window.history.(go|back|forward)}.
    + *     False if the token has been changed by a {@code setToken} or
    + *     {@code replaceToken} call.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.history.Event = function(token, isNavigation) {
    +  goog.events.Event.call(this, goog.history.EventType.NAVIGATE);
    +
    +  /**
    +   * The current history state.
    +   * @type {string}
    +   */
    +  this.token = token;
    +
    +  /**
    +   * Whether the event was triggered by browser navigation.
    +   * @type {boolean}
    +   */
    +  this.isNavigation = isNavigation;
    +};
    +goog.inherits(goog.history.Event, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/history/eventtype.js b/mapbox-gl/closure-library/closure/goog/history/eventtype.js
    new file mode 100644
    index 0000000000..4268df38ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/eventtype.js
    @@ -0,0 +1,30 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Event types for goog.history.
    + *
    + */
    +
    +
    +goog.provide('goog.history.EventType');
    +
    +
    +/**
    + * Event types for goog.history.
    + * @enum {string}
    + */
    +goog.history.EventType = {
    +  NAVIGATE: 'navigate'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/history/history.js b/mapbox-gl/closure-library/closure/goog/history/history.js
    new file mode 100644
    index 0000000000..6704529e44
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/history.js
    @@ -0,0 +1,1001 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Browser history stack management class.
    + *
    + * The goog.History object allows a page to create history state without leaving
    + * the current document. This allows users to, for example, hit the browser's
    + * back button without leaving the current page.
    + *
    + * The history object can be instantiated in one of two modes. In user visible
    + * mode, the current history state is shown in the browser address bar as a
    + * document location fragment (the portion of the URL after the '#'). These
    + * addresses can be bookmarked, copied and pasted into another browser, and
    + * modified directly by the user like any other URL.
    + *
    + * If the history object is created in invisible mode, the user can still
    + * affect the state using the browser forward and back buttons, but the current
    + * state is not displayed in the browser address bar. These states are not
    + * bookmarkable or editable.
    + *
    + * It is possible to use both types of history object on the same page, but not
    + * currently recommended due to browser deficiencies.
    + *
    + * Tested to work in:
    + * <ul>
    + *   <li>Firefox 1.0-4.0
    + *   <li>Internet Explorer 5.5-9.0
    + *   <li>Opera 9+
    + *   <li>Safari 4+
    + * </ul>
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + * @see ../demos/history1.html
    + * @see ../demos/history2.html
    + */
    +
    +/* Some browser specific implementation notes:
    + *
    + * Firefox (through version 2.0.0.1):
    + *
    + * Ideally, navigating inside the hidden iframe could be done using
    + * about:blank#state instead of a real page on the server. Setting the hash on
    + * about:blank creates history entries, but the hash is not recorded and is lost
    + * when the user hits the back button. This is true in Opera as well. A blank
    + * HTML page must be provided for invisible states to be recorded in the iframe
    + * hash.
    + *
    + * After leaving the page with the History object and returning to it (by
    + * hitting the back button from another site), the last state of the iframe is
    + * overwritten. The most recent state is saved in a hidden input field so the
    + * previous state can be restored.
    + *
    + * Firefox does not store the previous value of dynamically generated input
    + * elements. To save the state, the hidden element must be in the HTML document,
    + * either in the original source or added with document.write. If a reference
    + * to the input element is not provided as a constructor argument, then the
    + * history object creates one using document.write, in which case the history
    + * object must be created from a script in the body element of the page.
    + *
    + * Manually editing the address field to a different hash link prevents further
    + * updates to the address bar. The page continues to work as normal, but the
    + * address shown will be incorrect until the page is reloaded.
    + *
    + * NOTE(user): It should be noted that Firefox will URL encode any non-regular
    + * ascii character, along with |space|, ", <, and >, when added to the fragment.
    + * If you expect these characters in your tokens you should consider that
    + * setToken('<b>') would result in the history fragment "%3Cb%3E", and
    + * "esp&eacute;re" would show "esp%E8re".  (IE allows unicode characters in the
    + * fragment)
    + *
    + * TODO(user): Should we encapsulate this escaping into the API for visible
    + * history and encode all characters that aren't supported by Firefox?  It also
    + * needs to be optional so apps can elect to handle the escaping themselves.
    + *
    + *
    + * Internet Explorer (through version 7.0):
    + *
    + * IE does not modify the history stack when the document fragment is changed.
    + * We create history entries instead by using document.open and document.write
    + * into a hidden iframe.
    + *
    + * IE destroys the history stack when navigating from /foo.html#someFragment to
    + * /foo.html. The workaround is to always append the # to the URL. This is
    + * somewhat unfortunate when loading the page without any # specified, because
    + * a second "click" sound will play on load as the fragment is automatically
    + * appended. If the hash is always present, this can be avoided.
    + *
    + * Manually editing the hash in the address bar in IE6 and then hitting the back
    + * button can replace the page with a blank page. This is a Bad User Experience,
    + * but probably not preventable.
    + *
    + * IE also has a bug when the page is loaded via a server redirect, setting
    + * a new hash value on the window location will force a page reload. This will
    + * happen the first time setToken is called with a new token. The only known
    + * workaround is to force a client reload early, for example by setting
    + * window.location.hash = window.location.hash, which will otherwise be a no-op.
    + *
    + * Internet Explorer 8.0, Webkit 532.1 and Gecko 1.9.2:
    + *
    + * IE8 has introduced the support to the HTML5 onhashchange event, which means
    + * we don't have to do any polling to detect fragment changes. Chrome and
    + * Firefox have added it on their newer builds, wekbit 532.1 and gecko 1.9.2.
    + * http://www.w3.org/TR/html5/history.html
    + * NOTE(goto): it is important to note that the document needs to have the
    + * <!DOCTYPE html> tag to enable the IE8 HTML5 mode. If the tag is not present,
    + * IE8 will enter IE7 compatibility mode (which can also be enabled manually).
    + *
    + * Opera (through version 9.02):
    + *
    + * Navigating through pages at a rate faster than some threshhold causes Opera
    + * to cancel all outstanding timeouts and intervals, including the location
    + * polling loop. Since this condition cannot be detected, common input events
    + * are captured to cause the loop to restart.
    + *
    + * location.replace is adding a history entry inside setHash_, despite
    + * documentation that suggests it should not.
    + *
    + *
    + * Safari (through version 2.0.4):
    + *
    + * After hitting the back button, the location.hash property is no longer
    + * readable from JavaScript. This is fixed in later WebKit builds, but not in
    + * currently shipping Safari. For now, the only recourse is to disable history
    + * states in Safari. Pages are still navigable via the History object, but the
    + * back button cannot restore previous states.
    + *
    + * Safari sets history states on navigation to a hashlink, but doesn't allow
    + * polling of the hash, so following actual anchor links in the page will create
    + * useless history entries. Using location.replace does not seem to prevent
    + * this. Not a terribly good user experience, but fixed in later Webkits.
    + *
    + *
    + * WebKit (nightly version 420+):
    + *
    + * This almost works. Returning to a page with an invisible history object does
    + * not restore the old state, however, and there is no pageshow event that fires
    + * in this browser. Holding off on finding a solution for now.
    + *
    + *
    + * HTML5 capable browsers (Firefox 4, Chrome, Safari 5)
    + *
    + * No known issues. The goog.history.Html5History class provides a simpler
    + * implementation more suitable for recent browsers. These implementations
    + * should be merged so the history class automatically invokes the correct
    + * implementation.
    + */
    +
    +
    +goog.provide('goog.History');
    +goog.provide('goog.History.Event');
    +goog.provide('goog.History.EventType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.history.Event');
    +goog.require('goog.history.EventType');
    +goog.require('goog.labs.userAgent.device');
    +goog.require('goog.memoize');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A history management object. Can be instantiated in user-visible mode (uses
    + * the address fragment to manage state) or in hidden mode. This object should
    + * be created from a script in the document body before the document has
    + * finished loading.
    + *
    + * To store the hidden states in browsers other than IE, a hidden iframe is
    + * used. It must point to a valid html page on the same domain (which can and
    + * probably should be blank.)
    + *
    + * Sample instantiation and usage:
    + *
    + * <pre>
    + * // Instantiate history to use the address bar for state.
    + * var h = new goog.History();
    + * goog.events.listen(h, goog.history.EventType.NAVIGATE, navCallback);
    + * h.setEnabled(true);
    + *
    + * // Any changes to the location hash will call the following function.
    + * function navCallback(e) {
    + *   alert('Navigated to state "' + e.token + '"');
    + * }
    + *
    + * // The history token can also be set from code directly.
    + * h.setToken('foo');
    + * </pre>
    + *
    + * @param {boolean=} opt_invisible True to use hidden history states instead of
    + *     the user-visible location hash.
    + * @param {string=} opt_blankPageUrl A URL to a blank page on the same server.
    + *     Required if opt_invisible is true.  This URL is also used as the src
    + *     for the iframe used to track history state in IE (if not specified the
    + *     iframe is not given a src attribute).  Access is Denied error may
    + *     occur in IE7 if the window's URL's scheme is https, and this URL is
    + *     not specified.
    + * @param {HTMLInputElement=} opt_input The hidden input element to be used to
    + *     store the history token.  If not provided, a hidden input element will
    + *     be created using document.write.
    + * @param {HTMLIFrameElement=} opt_iframe The hidden iframe that will be used by
    + *     IE for pushing history state changes, or by all browsers if opt_invisible
    + *     is true. If not provided, a hidden iframe element will be created using
    + *     document.write.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.History = function(opt_invisible, opt_blankPageUrl, opt_input,
    +                        opt_iframe) {
    +  goog.events.EventTarget.call(this);
    +
    +  if (opt_invisible && !opt_blankPageUrl) {
    +    throw Error('Can\'t use invisible history without providing a blank page.');
    +  }
    +
    +  var input;
    +  if (opt_input) {
    +    input = opt_input;
    +  } else {
    +    var inputId = 'history_state' + goog.History.historyCount_;
    +    document.write(goog.string.subs(goog.History.INPUT_TEMPLATE_,
    +                                    inputId, inputId));
    +    input = goog.dom.getElement(inputId);
    +  }
    +
    +  /**
    +   * An input element that stores the current iframe state. Used to restore
    +   * the state when returning to the page on non-IE browsers.
    +   * @type {HTMLInputElement}
    +   * @private
    +   */
    +  this.hiddenInput_ = /** @type {HTMLInputElement} */ (input);
    +
    +  /**
    +   * The window whose location contains the history token fragment. This is
    +   * the window that contains the hidden input. It's typically the top window.
    +   * It is not necessarily the same window that the js code is loaded in.
    +   * @type {Window}
    +   * @private
    +   */
    +  this.window_ = opt_input ?
    +      goog.dom.getWindow(goog.dom.getOwnerDocument(opt_input)) : window;
    +
    +  /**
    +   * The base URL for the hidden iframe. Must refer to a document in the
    +   * same domain as the main page.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.iframeSrc_ = opt_blankPageUrl;
    +
    +  if (goog.userAgent.IE && !opt_blankPageUrl) {
    +    this.iframeSrc_ = window.location.protocol == 'https' ? 'https:///' :
    +                                                            'javascript:""';
    +  }
    +
    +  /**
    +   * A timer for polling the current history state for changes.
    +   * @type {goog.Timer}
    +   * @private
    +   */
    +  this.timer_ = new goog.Timer(goog.History.PollingType.NORMAL);
    +  this.registerDisposable(this.timer_);
    +
    +  /**
    +   * True if the state tokens are displayed in the address bar, false for hidden
    +   * history states.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.userVisible_ = !opt_invisible;
    +
    +  /**
    +   * An object to keep track of the history event listeners.
    +   * @type {goog.events.EventHandler<!goog.History>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  if (opt_invisible || goog.History.LEGACY_IE) {
    +    var iframe;
    +    if (opt_iframe) {
    +      iframe = opt_iframe;
    +    } else {
    +      var iframeId = 'history_iframe' + goog.History.historyCount_;
    +      var srcAttribute = this.iframeSrc_ ?
    +          'src="' + goog.string.htmlEscape(this.iframeSrc_) + '"' :
    +          '';
    +      document.write(goog.string.subs(goog.History.IFRAME_TEMPLATE_,
    +                                      iframeId,
    +                                      srcAttribute));
    +      iframe = goog.dom.getElement(iframeId);
    +    }
    +
    +    /**
    +     * Internet Explorer uses a hidden iframe for all history changes. Other
    +     * browsers use the iframe only for pushing invisible states.
    +     * @type {HTMLIFrameElement}
    +     * @private
    +     */
    +    this.iframe_ = /** @type {HTMLIFrameElement} */ (iframe);
    +
    +    /**
    +     * Whether the hidden iframe has had a document written to it yet in this
    +     * session.
    +     * @type {boolean}
    +     * @private
    +     */
    +    this.unsetIframe_ = true;
    +  }
    +
    +  if (goog.History.LEGACY_IE) {
    +    // IE relies on the hidden input to restore the history state from previous
    +    // sessions, but input values are only restored after window.onload. Set up
    +    // a callback to poll the value after the onload event.
    +    this.eventHandler_.listen(this.window_,
    +                              goog.events.EventType.LOAD,
    +                              this.onDocumentLoaded);
    +
    +    /**
    +     * IE-only variable for determining if the document has loaded.
    +     * @type {boolean}
    +     * @protected
    +     */
    +    this.documentLoaded = false;
    +
    +    /**
    +     * IE-only variable for storing whether the history object should be enabled
    +     * once the document finishes loading.
    +     * @type {boolean}
    +     * @private
    +     */
    +    this.shouldEnable_ = false;
    +  }
    +
    +  // Set the initial history state.
    +  if (this.userVisible_) {
    +    this.setHash_(this.getToken(), true);
    +  } else {
    +    this.setIframeToken_(this.hiddenInput_.value);
    +  }
    +
    +  goog.History.historyCount_++;
    +};
    +goog.inherits(goog.History, goog.events.EventTarget);
    +
    +
    +/**
    + * Status of when the object is active and dispatching events.
    + * @type {boolean}
    + * @private
    + */
    +goog.History.prototype.enabled_ = false;
    +
    +
    +/**
    + * Whether the object is performing polling with longer intervals. This can
    + * occur for instance when setting the location of the iframe when in invisible
    + * mode and the server that is hosting the blank html page is down. In FF, this
    + * will cause the location of the iframe to no longer be accessible, with
    + * permision denied exceptions being thrown on every access of the history
    + * token. When this occurs, the polling interval is elongated. This causes
    + * exceptions to be thrown at a lesser rate while allowing for the history
    + * object to resurrect itself when the html page becomes accessible.
    + * @type {boolean}
    + * @private
    + */
    +goog.History.prototype.longerPolling_ = false;
    +
    +
    +/**
    + * The last token set by the history object, used to poll for changes.
    + * @type {?string}
    + * @private
    + */
    +goog.History.prototype.lastToken_ = null;
    +
    +
    +/**
    + * Whether the browser supports HTML5 history management's onhashchange event.
    + * {@link http://www.w3.org/TR/html5/history.html}. IE 9 in compatibility mode
    + * indicates that onhashchange is in window, but testing reveals the event
    + * isn't actually fired.
    + * @return {boolean} Whether onhashchange is supported.
    + */
    +goog.History.isOnHashChangeSupported = goog.memoize(function() {
    +  return goog.userAgent.IE ?
    +      document.documentMode >= 8 :
    +      'onhashchange' in goog.global;
    +});
    +
    +
    +/**
    + * Whether the current browser is Internet Explorer prior to version 8. Many IE
    + * specific workarounds developed before version 8 are unnecessary in more
    + * current versions.
    + * @type {boolean}
    + */
    +goog.History.LEGACY_IE = goog.userAgent.IE &&
    +    !goog.userAgent.isDocumentModeOrHigher(8);
    +
    +
    +/**
    + * Whether the browser always requires the hash to be present. Internet Explorer
    + * before version 8 will reload the HTML page if the hash is omitted.
    + * @type {boolean}
    + */
    +goog.History.HASH_ALWAYS_REQUIRED = goog.History.LEGACY_IE;
    +
    +
    +/**
    + * If not null, polling in the user invisible mode will be disabled until this
    + * token is seen. This is used to prevent a race condition where the iframe
    + * hangs temporarily while the location is changed.
    + * @type {?string}
    + * @private
    + */
    +goog.History.prototype.lockedToken_ = null;
    +
    +
    +/** @override */
    +goog.History.prototype.disposeInternal = function() {
    +  goog.History.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.setEnabled(false);
    +};
    +
    +
    +/**
    + * Starts or stops the History polling loop. When enabled, the History object
    + * will immediately fire an event for the current location. The caller can set
    + * up event listeners between the call to the constructor and the call to
    + * setEnabled.
    + *
    + * On IE, actual startup may be delayed until the iframe and hidden input
    + * element have been loaded and can be polled. This behavior is transparent to
    + * the caller.
    + *
    + * @param {boolean} enable Whether to enable the history polling loop.
    + */
    +goog.History.prototype.setEnabled = function(enable) {
    +
    +  if (enable == this.enabled_) {
    +    return;
    +  }
    +
    +  if (goog.History.LEGACY_IE && !this.documentLoaded) {
    +    // Wait until the document has actually loaded before enabling the
    +    // object or any saved state from a previous session will be lost.
    +    this.shouldEnable_ = enable;
    +    return;
    +  }
    +
    +  if (enable) {
    +    if (goog.userAgent.OPERA) {
    +      // Capture events for common user input so we can restart the timer in
    +      // Opera if it fails. Yes, this is distasteful. See operaDefibrillator_.
    +      this.eventHandler_.listen(this.window_.document,
    +                                goog.History.INPUT_EVENTS_,
    +                                this.operaDefibrillator_);
    +    } else if (goog.userAgent.GECKO) {
    +      // Firefox will not restore the correct state after navigating away from
    +      // and then back to the page with the history object. This can be fixed
    +      // by restarting the history object on the pageshow event.
    +      this.eventHandler_.listen(this.window_, 'pageshow', this.onShow_);
    +    }
    +
    +    // TODO(user): make HTML5 and invisible history work by listening to the
    +    // iframe # changes instead of the window.
    +    if (goog.History.isOnHashChangeSupported() &&
    +        this.userVisible_) {
    +      this.eventHandler_.listen(
    +          this.window_, goog.events.EventType.HASHCHANGE, this.onHashChange_);
    +      this.enabled_ = true;
    +      this.dispatchEvent(new goog.history.Event(this.getToken(), false));
    +    } else if (!(goog.userAgent.IE && !goog.labs.userAgent.device.isMobile()) ||
    +               this.documentLoaded) {
    +      // Start dispatching history events if all necessary loading has
    +      // completed (always true for browsers other than IE.)
    +      this.eventHandler_.listen(this.timer_, goog.Timer.TICK,
    +          goog.bind(this.check_, this, true));
    +
    +      this.enabled_ = true;
    +
    +      // Initialize last token at startup except on IE < 8, where the last token
    +      // must only be set in conjunction with IFRAME updates, or the IFRAME will
    +      // start out of sync and remove any pre-existing URI fragment.
    +      if (!goog.History.LEGACY_IE) {
    +        this.lastToken_ = this.getToken();
    +        this.dispatchEvent(new goog.history.Event(this.getToken(), false));
    +      }
    +
    +      this.timer_.start();
    +    }
    +
    +  } else {
    +    this.enabled_ = false;
    +    this.eventHandler_.removeAll();
    +    this.timer_.stop();
    +  }
    +};
    +
    +
    +/**
    + * Callback for the window onload event in IE. This is necessary to read the
    + * value of the hidden input after restoring a history session. The value of
    + * input elements is not viewable until after window onload for some reason (the
    + * iframe state is similarly unavailable during the loading phase.)  If
    + * setEnabled is called before the iframe has completed loading, the history
    + * object will actually be enabled at this point.
    + * @protected
    + */
    +goog.History.prototype.onDocumentLoaded = function() {
    +  this.documentLoaded = true;
    +
    +  if (this.hiddenInput_.value) {
    +    // Any saved value in the hidden input can only be read after the document
    +    // has been loaded due to an IE limitation. Restore the previous state if
    +    // it has been set.
    +    this.setIframeToken_(this.hiddenInput_.value, true);
    +  }
    +
    +  this.setEnabled(this.shouldEnable_);
    +};
    +
    +
    +/**
    + * Handler for the Gecko pageshow event. Restarts the history object so that the
    + * correct state can be restored in the hash or iframe.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.History.prototype.onShow_ = function(e) {
    +  // NOTE(user): persisted is a property passed in the pageshow event that
    +  // indicates whether the page is being persisted from the cache or is being
    +  // loaded for the first time.
    +  if (e.getBrowserEvent()['persisted']) {
    +    this.setEnabled(false);
    +    this.setEnabled(true);
    +  }
    +};
    +
    +
    +/**
    + * Handles HTML5 onhashchange events on browsers where it is supported.
    + * This is very similar to {@link #check_}, except that it is not executed
    + * continuously. It is only used when
    + * {@code goog.History.isOnHashChangeSupported()} is true.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.History.prototype.onHashChange_ = function(e) {
    +  var hash = this.getLocationFragment_(this.window_);
    +  if (hash != this.lastToken_) {
    +    this.update_(hash, true);
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The current token.
    + */
    +goog.History.prototype.getToken = function() {
    +  if (this.lockedToken_ != null) {
    +    return this.lockedToken_;
    +  } else if (this.userVisible_) {
    +    return this.getLocationFragment_(this.window_);
    +  } else {
    +    return this.getIframeToken_() || '';
    +  }
    +};
    +
    +
    +/**
    + * Sets the history state. When user visible states are used, the URL fragment
    + * will be set to the provided token.  Sometimes it is necessary to set the
    + * history token before the document title has changed, in this case IE's
    + * history drop down can be out of sync with the token.  To get around this
    + * problem, the app can pass in a title to use with the hidden iframe.
    + * @param {string} token The history state identifier.
    + * @param {string=} opt_title Optional title used when setting the hidden iframe
    + *     title in IE.
    + */
    +goog.History.prototype.setToken = function(token, opt_title) {
    +  this.setHistoryState_(token, false, opt_title);
    +};
    +
    +
    +/**
    + * Replaces the current history state without affecting the rest of the history
    + * stack.
    + * @param {string} token The history state identifier.
    + * @param {string=} opt_title Optional title used when setting the hidden iframe
    + *     title in IE.
    + */
    +goog.History.prototype.replaceToken = function(token, opt_title) {
    +  this.setHistoryState_(token, true, opt_title);
    +};
    +
    +
    +/**
    + * Gets the location fragment for the current URL.  We don't use location.hash
    + * directly as the browser helpfully urlDecodes the string for us which can
    + * corrupt the tokens.  For example, if we want to store: label/%2Froot it would
    + * be returned as label//root.
    + * @param {Window} win The window object to use.
    + * @return {string} The fragment.
    + * @private
    + */
    +goog.History.prototype.getLocationFragment_ = function(win) {
    +  var href = win.location.href;
    +  var index = href.indexOf('#');
    +  return index < 0 ? '' : href.substring(index + 1);
    +};
    +
    +
    +/**
    + * Sets the history state. When user visible states are used, the URL fragment
    + * will be set to the provided token. Setting opt_replace to true will cause the
    + * navigation to occur, but will replace the current history entry without
    + * affecting the length of the stack.
    + *
    + * @param {string} token The history state identifier.
    + * @param {boolean} replace Set to replace the current history entry instead of
    + *    appending a new history state.
    + * @param {string=} opt_title Optional title used when setting the hidden iframe
    + *     title in IE.
    + * @private
    + */
    +goog.History.prototype.setHistoryState_ = function(token, replace, opt_title) {
    +  if (this.getToken() != token) {
    +    if (this.userVisible_) {
    +      this.setHash_(token, replace);
    +
    +      if (!goog.History.isOnHashChangeSupported()) {
    +        if (goog.userAgent.IE && !goog.labs.userAgent.device.isMobile()) {
    +          // IE must save state using the iframe.
    +          this.setIframeToken_(token, replace, opt_title);
    +        }
    +      }
    +
    +      // This condition needs to be called even if
    +      // goog.History.isOnHashChangeSupported() is true so the NAVIGATE event
    +      // fires sychronously.
    +      if (this.enabled_) {
    +        this.check_(false);
    +      }
    +    } else {
    +      // Fire the event immediately so that setting history is synchronous, but
    +      // set a suspendToken so that polling doesn't trigger a 'back'.
    +      this.setIframeToken_(token, replace);
    +      this.lockedToken_ = this.lastToken_ = this.hiddenInput_.value = token;
    +      this.dispatchEvent(new goog.history.Event(token, false));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets or replaces the URL fragment. The token does not need to be URL encoded
    + * according to the URL specification, though certain characters (like newline)
    + * are automatically stripped.
    + *
    + * If opt_replace is not set, non-IE browsers will append a new entry to the
    + * history list. Setting the hash does not affect the history stack in IE
    + * (unless there is a pre-existing named anchor for that hash.)
    + *
    + * Older versions of Webkit cannot query the location hash, but it still can be
    + * set. If we detect one of these versions, always replace instead of creating
    + * new history entries.
    + *
    + * window.location.replace replaces the current state from the history stack.
    + * http://www.whatwg.org/specs/web-apps/current-work/#dom-location-replace
    + * http://www.whatwg.org/specs/web-apps/current-work/#replacement-enabled
    + *
    + * @param {string} token The new string to set.
    + * @param {boolean=} opt_replace Set to true to replace the current token
    + *    without appending a history entry.
    + * @private
    + */
    +goog.History.prototype.setHash_ = function(token, opt_replace) {
    +  // If the page uses a BASE element, setting location.hash directly will
    +  // navigate away from the current document. Also, the original URL path may
    +  // possibly change from HTML5 history pushState. To account for these, the
    +  // full path is always specified.
    +  var loc = this.window_.location;
    +  var url = loc.href.split('#')[0];
    +
    +  // If a hash has already been set, then removing it programmatically will
    +  // reload the page. Once there is a hash, we won't remove it.
    +  var hasHash = goog.string.contains(loc.href, '#');
    +
    +  if (goog.History.HASH_ALWAYS_REQUIRED || hasHash || token) {
    +    url += '#' + token;
    +  }
    +
    +  if (url != loc.href) {
    +    if (opt_replace) {
    +      loc.replace(url);
    +    } else {
    +      loc.href = url;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the hidden iframe state. On IE, this is accomplished by writing a new
    + * document into the iframe. In Firefox, the iframe's URL fragment stores the
    + * state instead.
    + *
    + * Older versions of webkit cannot set the iframe, so ignore those browsers.
    + *
    + * @param {string} token The new string to set.
    + * @param {boolean=} opt_replace Set to true to replace the current iframe state
    + *     without appending a new history entry.
    + * @param {string=} opt_title Optional title used when setting the hidden iframe
    + *     title in IE.
    + * @private
    + */
    +goog.History.prototype.setIframeToken_ = function(token,
    +                                                  opt_replace,
    +                                                  opt_title) {
    +  if (this.unsetIframe_ || token != this.getIframeToken_()) {
    +
    +    this.unsetIframe_ = false;
    +    token = goog.string.urlEncode(token);
    +
    +    if (goog.userAgent.IE) {
    +      // Caching the iframe document results in document permission errors after
    +      // leaving the page and returning. Access it anew each time instead.
    +      var doc = goog.dom.getFrameContentDocument(this.iframe_);
    +
    +      doc.open('text/html', opt_replace ? 'replace' : undefined);
    +      doc.write(goog.string.subs(
    +          goog.History.IFRAME_SOURCE_TEMPLATE_,
    +          goog.string.htmlEscape(
    +              /** @type {string} */ (opt_title || this.window_.document.title)),
    +          token));
    +      doc.close();
    +    } else {
    +      var url = this.iframeSrc_ + '#' + token;
    +
    +      // In Safari, it is possible for the contentWindow of the iframe to not
    +      // be present when the page is loading after a reload.
    +      var contentWindow = this.iframe_.contentWindow;
    +      if (contentWindow) {
    +        if (opt_replace) {
    +          contentWindow.location.replace(url);
    +        } else {
    +          contentWindow.location.href = url;
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Return the current state string from the hidden iframe. On internet explorer,
    + * this is stored as a string in the document body. Other browsers use the
    + * location hash of the hidden iframe.
    + *
    + * Older versions of webkit cannot access the iframe location, so always return
    + * null in that case.
    + *
    + * @return {?string} The state token saved in the iframe (possibly null if the
    + *     iframe has never loaded.).
    + * @private
    + */
    +goog.History.prototype.getIframeToken_ = function() {
    +  if (goog.userAgent.IE) {
    +    var doc = goog.dom.getFrameContentDocument(this.iframe_);
    +    return doc.body ? goog.string.urlDecode(doc.body.innerHTML) : null;
    +  } else {
    +    // In Safari, it is possible for the contentWindow of the iframe to not
    +    // be present when the page is loading after a reload.
    +    var contentWindow = this.iframe_.contentWindow;
    +    if (contentWindow) {
    +      var hash;
    +      /** @preserveTry */
    +      try {
    +        // Iframe tokens are urlEncoded
    +        hash = goog.string.urlDecode(this.getLocationFragment_(contentWindow));
    +      } catch (e) {
    +        // An exception will be thrown if the location of the iframe can not be
    +        // accessed (permission denied). This can occur in FF if the the server
    +        // that is hosting the blank html page goes down and then a new history
    +        // token is set. The iframe will navigate to an error page, and the
    +        // location of the iframe can no longer be accessed. Due to the polling,
    +        // this will cause constant exceptions to be thrown. In this case,
    +        // we enable longer polling. We do not have to attempt to reset the
    +        // iframe token because (a) we already fired the NAVIGATE event when
    +        // setting the token, (b) we can rely on the locked token for current
    +        // state, and (c) the token is still in the history and
    +        // accesible on forward/back.
    +        if (!this.longerPolling_) {
    +          this.setLongerPolling_(true);
    +        }
    +
    +        return null;
    +      }
    +
    +      // There was no exception when getting the hash so turn off longer polling
    +      // if it is on.
    +      if (this.longerPolling_) {
    +        this.setLongerPolling_(false);
    +      }
    +
    +      return hash || null;
    +    } else {
    +      return null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Checks the state of the document fragment and the iframe title to detect
    + * navigation changes. If {@code goog.HistoryisOnHashChangeSupported()} is
    + * {@code false}, then this runs approximately twenty times per second.
    + * @param {boolean} isNavigation True if the event was initiated by a browser
    + *     action, false if it was caused by a setToken call. See
    + *     {@link goog.history.Event}.
    + * @private
    + */
    +goog.History.prototype.check_ = function(isNavigation) {
    +  if (this.userVisible_) {
    +    var hash = this.getLocationFragment_(this.window_);
    +    if (hash != this.lastToken_) {
    +      this.update_(hash, isNavigation);
    +    }
    +  }
    +
    +  // Old IE uses the iframe for both visible and non-visible versions.
    +  if (!this.userVisible_ || goog.History.LEGACY_IE) {
    +    var token = this.getIframeToken_() || '';
    +    if (this.lockedToken_ == null || token == this.lockedToken_) {
    +      this.lockedToken_ = null;
    +      if (token != this.lastToken_) {
    +        this.update_(token, isNavigation);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Updates the current history state with a given token. Called after a change
    + * to the location or the iframe state is detected by poll_.
    + *
    + * @param {string} token The new history state.
    + * @param {boolean} isNavigation True if the event was initiated by a browser
    + *     action, false if it was caused by a setToken call. See
    + *     {@link goog.history.Event}.
    + * @private
    + */
    +goog.History.prototype.update_ = function(token, isNavigation) {
    +  this.lastToken_ = this.hiddenInput_.value = token;
    +
    +  if (this.userVisible_) {
    +    if (goog.History.LEGACY_IE) {
    +      this.setIframeToken_(token);
    +    }
    +
    +    this.setHash_(token);
    +  } else {
    +    this.setIframeToken_(token);
    +  }
    +
    +  this.dispatchEvent(new goog.history.Event(this.getToken(), isNavigation));
    +};
    +
    +
    +/**
    + * Sets if the history oject should use longer intervals when polling.
    + *
    + * @param {boolean} longerPolling Whether to enable longer polling.
    + * @private
    + */
    +goog.History.prototype.setLongerPolling_ = function(longerPolling) {
    +  if (this.longerPolling_ != longerPolling) {
    +    this.timer_.setInterval(longerPolling ?
    +        goog.History.PollingType.LONG : goog.History.PollingType.NORMAL);
    +  }
    +  this.longerPolling_ = longerPolling;
    +};
    +
    +
    +/**
    + * Opera cancels all outstanding timeouts and intervals after any rapid
    + * succession of navigation events, including the interval used to detect
    + * navigation events. This function restarts the interval so that navigation can
    + * continue. Ideally, only events which would be likely to cause a navigation
    + * change (mousedown and keydown) would be bound to this function. Since Opera
    + * seems to ignore keydown events while the alt key is pressed (such as
    + * alt-left or right arrow), this function is also bound to the much more
    + * frequent mousemove event. This way, when the update loop freezes, it will
    + * unstick itself as the user wiggles the mouse in frustration.
    + * @private
    + */
    +goog.History.prototype.operaDefibrillator_ = function() {
    +  this.timer_.stop();
    +  this.timer_.start();
    +};
    +
    +
    +/**
    + * List of user input event types registered in Opera to restart the history
    + * timer (@see goog.History#operaDefibrillator_).
    + * @type {Array<string>}
    + * @private
    + */
    +goog.History.INPUT_EVENTS_ = [
    +  goog.events.EventType.MOUSEDOWN,
    +  goog.events.EventType.KEYDOWN,
    +  goog.events.EventType.MOUSEMOVE
    +];
    +
    +
    +/**
    + * Minimal HTML page used to populate the iframe in Internet Explorer. The title
    + * is visible in the history dropdown menu, the iframe state is stored as the
    + * body innerHTML.
    + * @type {string}
    + * @private
    + */
    +goog.History.IFRAME_SOURCE_TEMPLATE_ = '<title>%s</title><body>%s</body>';
    +
    +
    +/**
    + * HTML template for an invisible iframe.
    + * @type {string}
    + * @private
    + */
    +goog.History.IFRAME_TEMPLATE_ =
    +    '<iframe id="%s" style="display:none" %s></iframe>';
    +
    +
    +/**
    + * HTML template for an invisible named input element.
    + * @type {string}
    + * @private
    + */
    +goog.History.INPUT_TEMPLATE_ =
    +    '<input type="text" name="%s" id="%s" style="display:none">';
    +
    +
    +/**
    + * Counter for the number of goog.History objects that have been instantiated.
    + * Used to create unique IDs.
    + * @type {number}
    + * @private
    + */
    +goog.History.historyCount_ = 0;
    +
    +
    +/**
    + * Types of polling. The values are in ms of the polling interval.
    + * @enum {number}
    + */
    +goog.History.PollingType = {
    +  NORMAL: 150,
    +  LONG: 10000
    +};
    +
    +
    +/**
    + * Constant for the history change event type.
    + * @enum {string}
    + * @deprecated Use goog.history.EventType.
    + */
    +goog.History.EventType = goog.history.EventType;
    +
    +
    +
    +/**
    + * Constant for the history change event type.
    + * @param {string} token The string identifying the new history state.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @deprecated Use goog.history.Event.
    + * @final
    + */
    +goog.History.Event = goog.history.Event;
    diff --git a/mapbox-gl/closure-library/closure/goog/history/history_test.html b/mapbox-gl/closure-library/closure/goog/history/history_test.html
    new file mode 100644
    index 0000000000..d13286e4da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/history_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.history</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.HistoryTest');
    +</script>
    +</head>
    +<body>
    +<input id="hidden-input" type="hidden">
    +<iframe id="hidden-iframe" style="display:none">
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/history/history_test.js b/mapbox-gl/closure-library/closure/goog/history/history_test.js
    new file mode 100644
    index 0000000000..614d0047a9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/history_test.js
    @@ -0,0 +1,55 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.history.History.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.HistoryTest');
    +
    +goog.require('goog.History');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +goog.setTestOnly('goog.HistoryTest');
    +
    +
    +// Mimimal function to exercise construction.
    +function testCreation() {
    +  var input = goog.dom.getElement('hidden-input');
    +  var iframe = goog.dom.getElement('hidden-iframe');
    +
    +  try {
    +    var history = new goog.History(undefined, undefined, input, iframe);
    +  } finally {
    +    goog.dispose(history);
    +  }
    +}
    +
    +function testIsHashChangeSupported() {
    +
    +  // This is the policy currently implemented.
    +  var supportsOnHashChange = (goog.userAgent.IE ?
    +      document.documentMode >= 8 :
    +      'onhashchange' in window);
    +
    +  assertEquals(
    +      supportsOnHashChange,
    +      goog.History.isOnHashChangeSupported());
    +}
    +
    +// TODO(nnaze): Test additional behavior.
    diff --git a/mapbox-gl/closure-library/closure/goog/history/html5history.js b/mapbox-gl/closure-library/closure/goog/history/html5history.js
    new file mode 100644
    index 0000000000..038d711e3c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/html5history.js
    @@ -0,0 +1,303 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview HTML5 based history implementation, compatible with
    + * goog.History.
    + *
    + * TODO(user): There should really be a history interface and multiple
    + * implementations.
    + *
    + */
    +
    +
    +goog.provide('goog.history.Html5History');
    +goog.provide('goog.history.Html5History.TokenTransformer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.history.Event');
    +
    +
    +
    +/**
    + * An implementation compatible with goog.History that uses the HTML5
    + * history APIs.
    + *
    + * @param {Window=} opt_win The window to listen/dispatch history events on.
    + * @param {goog.history.Html5History.TokenTransformer=} opt_transformer
    + *     The token transformer that is used to create URL from the token
    + *     when storing token without using hash fragment.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.history.Html5History = function(opt_win, opt_transformer) {
    +  goog.events.EventTarget.call(this);
    +  goog.asserts.assert(goog.history.Html5History.isSupported(opt_win),
    +      'HTML5 history is not supported.');
    +
    +  /**
    +   * The window object to use for history tokens.  Typically the top window.
    +   * @type {Window}
    +   * @private
    +   */
    +  this.window_ = opt_win || window;
    +
    +  /**
    +   * The token transformer that is used to create URL from the token
    +   * when storing token without using hash fragment.
    +   * @type {goog.history.Html5History.TokenTransformer}
    +   * @private
    +   */
    +  this.transformer_ = opt_transformer || null;
    +
    +  goog.events.listen(this.window_, goog.events.EventType.POPSTATE,
    +      this.onHistoryEvent_, false, this);
    +  goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
    +      this.onHistoryEvent_, false, this);
    +};
    +goog.inherits(goog.history.Html5History, goog.events.EventTarget);
    +
    +
    +/**
    + * Returns whether Html5History is supported.
    + * @param {Window=} opt_win Optional window to check.
    + * @return {boolean} Whether html5 history is supported.
    + */
    +goog.history.Html5History.isSupported = function(opt_win) {
    +  var win = opt_win || window;
    +  return !!(win.history && win.history.pushState);
    +};
    +
    +
    +/**
    + * Status of when the object is active and dispatching events.
    + * @type {boolean}
    + * @private
    + */
    +goog.history.Html5History.prototype.enabled_ = false;
    +
    +
    +/**
    + * Whether to use the fragment to store the token, defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.history.Html5History.prototype.useFragment_ = true;
    +
    +
    +/**
    + * If useFragment is false the path will be used, the path prefix will be
    + * prepended to all tokens. Defaults to '/'.
    + * @type {string}
    + * @private
    + */
    +goog.history.Html5History.prototype.pathPrefix_ = '/';
    +
    +
    +/**
    + * Starts or stops the History.  When enabled, the History object
    + * will immediately fire an event for the current location. The caller can set
    + * up event listeners between the call to the constructor and the call to
    + * setEnabled.
    + *
    + * @param {boolean} enable Whether to enable history.
    + */
    +goog.history.Html5History.prototype.setEnabled = function(enable) {
    +  if (enable == this.enabled_) {
    +    return;
    +  }
    +
    +  this.enabled_ = enable;
    +
    +  if (enable) {
    +    this.dispatchEvent(new goog.history.Event(this.getToken(), false));
    +  }
    +};
    +
    +
    +/**
    + * Returns the current token.
    + * @return {string} The current token.
    + */
    +goog.history.Html5History.prototype.getToken = function() {
    +  if (this.useFragment_) {
    +    var loc = this.window_.location.href;
    +    var index = loc.indexOf('#');
    +    return index < 0 ? '' : loc.substring(index + 1);
    +  } else {
    +    return this.transformer_ ?
    +        this.transformer_.retrieveToken(
    +            this.pathPrefix_, this.window_.location) :
    +        this.window_.location.pathname.substr(this.pathPrefix_.length);
    +  }
    +};
    +
    +
    +/**
    + * Sets the history state.
    + * @param {string} token The history state identifier.
    + * @param {string=} opt_title Optional title to associate with history entry.
    + */
    +goog.history.Html5History.prototype.setToken = function(token, opt_title) {
    +  if (token == this.getToken()) {
    +    return;
    +  }
    +
    +  // Per externs/gecko_dom.js document.title can be null.
    +  this.window_.history.pushState(null,
    +      opt_title || this.window_.document.title || '', this.getUrl_(token));
    +  this.dispatchEvent(new goog.history.Event(token, false));
    +};
    +
    +
    +/**
    + * Replaces the current history state without affecting the rest of the history
    + * stack.
    + * @param {string} token The history state identifier.
    + * @param {string=} opt_title Optional title to associate with history entry.
    + */
    +goog.history.Html5History.prototype.replaceToken = function(token, opt_title) {
    +  // Per externs/gecko_dom.js document.title can be null.
    +  this.window_.history.replaceState(null,
    +      opt_title || this.window_.document.title || '', this.getUrl_(token));
    +  this.dispatchEvent(new goog.history.Event(token, false));
    +};
    +
    +
    +/** @override */
    +goog.history.Html5History.prototype.disposeInternal = function() {
    +  goog.events.unlisten(this.window_, goog.events.EventType.POPSTATE,
    +      this.onHistoryEvent_, false, this);
    +  if (this.useFragment_) {
    +    goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
    +        this.onHistoryEvent_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Sets whether to use the fragment to store tokens.
    + * @param {boolean} useFragment Whether to use the fragment.
    + */
    +goog.history.Html5History.prototype.setUseFragment = function(useFragment) {
    +  if (this.useFragment_ != useFragment) {
    +    if (useFragment) {
    +      goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
    +          this.onHistoryEvent_, false, this);
    +    } else {
    +      goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
    +          this.onHistoryEvent_, false, this);
    +    }
    +    this.useFragment_ = useFragment;
    +  }
    +};
    +
    +
    +/**
    + * Sets the path prefix to use if storing tokens in the path. The path
    + * prefix should start and end with slash.
    + * @param {string} pathPrefix Sets the path prefix.
    + */
    +goog.history.Html5History.prototype.setPathPrefix = function(pathPrefix) {
    +  this.pathPrefix_ = pathPrefix;
    +};
    +
    +
    +/**
    + * Gets the path prefix.
    + * @return {string} The path prefix.
    + */
    +goog.history.Html5History.prototype.getPathPrefix = function() {
    +  return this.pathPrefix_;
    +};
    +
    +
    +/**
    + * Gets the URL to set when calling history.pushState
    + * @param {string} token The history token.
    + * @return {string} The URL.
    + * @private
    + */
    +goog.history.Html5History.prototype.getUrl_ = function(token) {
    +  if (this.useFragment_) {
    +    return '#' + token;
    +  } else {
    +    return this.transformer_ ?
    +        this.transformer_.createUrl(
    +            token, this.pathPrefix_, this.window_.location) :
    +        this.pathPrefix_ + token + this.window_.location.search;
    +  }
    +};
    +
    +
    +/**
    + * Handles history events dispatched by the browser.
    + * @param {goog.events.BrowserEvent} e The browser event object.
    + * @private
    + */
    +goog.history.Html5History.prototype.onHistoryEvent_ = function(e) {
    +  if (this.enabled_) {
    +    this.dispatchEvent(new goog.history.Event(this.getToken(), true));
    +  }
    +};
    +
    +
    +
    +/**
    + * A token transformer that can create a URL from a history
    + * token. This is used by {@code goog.history.Html5History} to create
    + * URL when storing token without the hash fragment.
    + *
    + * Given a {@code window.location} object containing the location
    + * created by {@code createUrl}, the token transformer allows
    + * retrieval of the token back via {@code retrieveToken}.
    + *
    + * @interface
    + */
    +goog.history.Html5History.TokenTransformer = function() {};
    +
    +
    +/**
    + * Retrieves a history token given the path prefix and
    + * {@code window.location} object.
    + *
    + * @param {string} pathPrefix The path prefix to use when storing token
    + *     in a path; always begin with a slash.
    + * @param {Location} location The {@code window.location} object.
    + *     Treat this object as read-only.
    + * @return {string} token The history token.
    + */
    +goog.history.Html5History.TokenTransformer.prototype.retrieveToken = function(
    +    pathPrefix, location) {};
    +
    +
    +/**
    + * Creates a URL to be pushed into HTML5 history stack when storing
    + * token without using hash fragment.
    + *
    + * @param {string} token The history token.
    + * @param {string} pathPrefix The path prefix to use when storing token
    + *     in a path; always begin with a slash.
    + * @param {Location} location The {@code window.location} object.
    + *     Treat this object as read-only.
    + * @return {string} url The complete URL string from path onwards
    + *     (without {@code protocol://host:port} part); must begin with a
    + *     slash.
    + */
    +goog.history.Html5History.TokenTransformer.prototype.createUrl = function(
    +    token, pathPrefix, location) {};
    diff --git a/mapbox-gl/closure-library/closure/goog/history/html5history_test.html b/mapbox-gl/closure-library/closure/goog/history/html5history_test.html
    new file mode 100644
    index 0000000000..13ef94e214
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/html5history_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.history.Html5History
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.history.Html5HistoryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/history/html5history_test.js b/mapbox-gl/closure-library/closure/goog/history/html5history_test.js
    new file mode 100644
    index 0000000000..955f28a4a1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/history/html5history_test.js
    @@ -0,0 +1,159 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.history.Html5HistoryTest');
    +goog.setTestOnly('goog.history.Html5HistoryTest');
    +
    +goog.require('goog.history.Html5History');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +
    +var mockControl;
    +var mockWindow;
    +
    +var html5History;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +
    +  mockWindow = {
    +    location: {}
    +  };
    +  mockWindow.attachEvent = mockControl.createFunctionMock();
    +  mockWindow.attachEvent(
    +      goog.testing.mockmatchers.ignoreArgument,
    +      goog.testing.mockmatchers.ignoreArgument).$anyTimes();
    +  var mockHistoryIsSupportedMethod = mockControl.createMethodMock(
    +      goog.history.Html5History, 'isSupported');
    +  mockHistoryIsSupportedMethod(mockWindow).$returns(true).$anyTimes();
    +}
    +
    +function tearDown() {
    +  if (html5History) {
    +    html5History.dispose();
    +    html5History = null;
    +  }
    +  mockControl.$tearDown();
    +}
    +
    +function testGetTokenWithoutUsingFragment() {
    +  mockWindow.location.pathname = '/test/something';
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow);
    +  html5History.setUseFragment(false);
    +
    +  assertEquals('test/something', html5History.getToken());
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetTokenWithoutUsingFragmentWithCustomPathPrefix() {
    +  mockWindow.location.pathname = '/test/something';
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow);
    +  html5History.setUseFragment(false);
    +  html5History.setPathPrefix('/test/');
    +
    +  assertEquals('something', html5History.getToken());
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetTokenWithoutUsingFragmentWithCustomTransformer() {
    +  mockWindow.location.pathname = '/test/something';
    +  var mockTransformer = mockControl.createLooseMock(
    +      goog.history.Html5History.TokenTransformer);
    +  mockTransformer.retrieveToken('/', mockWindow.location).$returns('abc/1');
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow, mockTransformer);
    +  html5History.setUseFragment(false);
    +
    +  assertEquals('abc/1', html5History.getToken());
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetTokenWithoutUsingFragmentWithCustomTransformerAndPrefix() {
    +  mockWindow.location.pathname = '/test/something';
    +  var mockTransformer = mockControl.createLooseMock(
    +      goog.history.Html5History.TokenTransformer);
    +  mockTransformer.retrieveToken('/test/', mockWindow.location).
    +      $returns('abc/1');
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow, mockTransformer);
    +  html5History.setUseFragment(false);
    +  html5History.setPathPrefix('/test/');
    +
    +  assertEquals('abc/1', html5History.getToken());
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetUrlWithoutUsingFragment() {
    +  mockWindow.location.search = '?q=something';
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow);
    +  html5History.setUseFragment(false);
    +
    +  assertEquals('/some/token?q=something', html5History.getUrl_('some/token'));
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetUrlWithoutUsingFragmentWithCustomPathPrefix() {
    +  mockWindow.location.search = '?q=something';
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow);
    +  html5History.setUseFragment(false);
    +  html5History.setPathPrefix('/test/');
    +
    +  assertEquals('/test/some/token?q=something',
    +               html5History.getUrl_('some/token'));
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetUrlWithoutUsingFragmentWithCustomTransformer() {
    +  mockWindow.location.search = '?q=something';
    +  var mockTransformer = mockControl.createLooseMock(
    +      goog.history.Html5History.TokenTransformer);
    +  mockTransformer.createUrl('some/token', '/', mockWindow.location).
    +      $returns('/something/else/?different');
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow, mockTransformer);
    +  html5History.setUseFragment(false);
    +
    +  assertEquals('/something/else/?different',
    +               html5History.getUrl_('some/token'));
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetUrlWithoutUsingFragmentWithCustomTransformerAndPrefix() {
    +  mockWindow.location.search = '?q=something';
    +  var mockTransformer = mockControl.createLooseMock(
    +      goog.history.Html5History.TokenTransformer);
    +  mockTransformer.createUrl('some/token', '/test/', mockWindow.location).
    +      $returns('/something/else/?different');
    +
    +  mockControl.$replayAll();
    +  html5History = new goog.history.Html5History(mockWindow, mockTransformer);
    +  html5History.setUseFragment(false);
    +  html5History.setPathPrefix('/test/');
    +
    +  assertEquals('/something/else/?different',
    +               html5History.getUrl_('some/token'));
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/flash.js b/mapbox-gl/closure-library/closure/goog/html/flash.js
    new file mode 100644
    index 0000000000..9f72665db6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/flash.js
    @@ -0,0 +1,177 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview SafeHtml factory methods for creating object and embed tags
    + * for loading Flash files.
    + */
    +
    +goog.provide('goog.html.flash');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.html.SafeHtml');
    +
    +
    +/**
    + * Attributes and param tag name attributes not allowed to be overriden
    + * when calling createObject() and createObjectForOldIe().
    + *
    + * While values that should be specified as params are probably not
    + * recognized as attributes, we block them anyway just to be sure.
    + * @const {!Array<string>}
    + * @private
    + */
    +goog.html.flash.FORBIDDEN_ATTRS_AND_PARAMS_ON_FLASH_ = [
    +  'classid',  // Used on old IE.
    +  'data',  // Used in <object> to specify a URL.
    +  'movie',  // Used on old IE.
    +  'type',  // Used in <object> on for non-IE/modern IE.
    +  'typemustmatch'  // Always set to a fixed value.
    +];
    +
    +
    +goog.html.flash.createEmbed = function(src, opt_attributes) {
    +  var fixedAttributes = {
    +    'src': src,
    +    'type': 'application/x-shockwave-flash',
    +    'pluginspage': 'https://www.macromedia.com/go/getflashplayer'
    +  };
    +  var defaultAttributes = {
    +    'allownetworking': 'none',
    +    'allowscriptaccess': 'never'
    +  };
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, defaultAttributes, opt_attributes);
    +  return goog.html.SafeHtml.
    +      createSafeHtmlTagSecurityPrivateDoNotAccessOrElse('embed', attributes);
    +};
    +
    +
    +goog.html.flash.createObject = function(
    +    data, opt_params, opt_attributes) {
    +  goog.html.flash.verifyKeysNotInMaps(
    +      goog.html.flash.FORBIDDEN_ATTRS_AND_PARAMS_ON_FLASH_,
    +      opt_attributes,
    +      opt_params);
    +
    +  var paramTags = goog.html.flash.combineParams(
    +      {
    +        'allownetworking': 'none',
    +        'allowscriptaccess': 'never'
    +      },
    +      opt_params);
    +  var fixedAttributes = {
    +    'data': data,
    +    'type': 'application/x-shockwave-flash',
    +    'typemustmatch': ''
    +  };
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, {}, opt_attributes);
    +
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      'object', attributes, paramTags);
    +};
    +
    +
    +goog.html.flash.createObjectForOldIe = function(
    +    movie, opt_params, opt_attributes) {
    +  goog.html.flash.verifyKeysNotInMaps(
    +      goog.html.flash.FORBIDDEN_ATTRS_AND_PARAMS_ON_FLASH_,
    +      opt_attributes,
    +      opt_params);
    +
    +  var paramTags = goog.html.flash.combineParams(
    +      {
    +        'allownetworking': 'none',
    +        'allowscriptaccess': 'never',
    +        'movie': movie
    +      },
    +      opt_params);
    +  var fixedAttributes =
    +      {'classid': 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'};
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, {}, opt_attributes);
    +
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      'object', attributes, paramTags);
    +};
    +
    +
    +/**
    + * @param {!Object<string, string|!goog.string.TypedString>} defaultParams
    + * @param {!Object<string, string>=}
    + *     opt_params Optional params passed to create*().
    + * @return {!Array<!goog.html.SafeHtml>} Combined params.
    + * @throws {Error} If opt_attributes contains an attribute with the same name
    + *     as an attribute in fixedAttributes.
    + * @package
    + */
    +goog.html.flash.combineParams = function(defaultParams, opt_params) {
    +  var combinedParams = {};
    +  var name;
    +
    +  for (name in defaultParams) {
    +    goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    +    combinedParams[name] = defaultParams[name];
    +  }
    +  for (name in opt_params) {
    +    var nameLower = name.toLowerCase();
    +    if (nameLower in defaultParams) {
    +      delete combinedParams[nameLower];
    +    }
    +    combinedParams[name] = opt_params[name];
    +  }
    +
    +  var paramTags = [];
    +  for (name in combinedParams) {
    +    paramTags.push(
    +        goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +            'param', {'name': name, 'value': combinedParams[name]}));
    +
    +  }
    +  return paramTags;
    +};
    +
    +
    +/**
    + * Checks that keys are not present as keys in maps.
    + * @param {!Array<string>} keys Keys that must not be present, lower-case.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Optional attributes passed to create*().
    + * @param {!Object<string, string>=}  opt_params Optional params passed to
    + *     createObject*().
    + * @throws {Error} If any of keys exist as a key, ignoring case, in
    + *     opt_attributes or opt_params.
    + * @package
    + */
    +goog.html.flash.verifyKeysNotInMaps = function(
    +    keys, opt_attributes, opt_params) {
    +  var verifyNotInMap = function(keys, map, type) {
    +    for (var keyMap in map) {
    +      var keyMapLower = keyMap.toLowerCase();
    +      for (var i = 0; i < keys.length; i++) {
    +        var keyToCheck = keys[i];
    +        goog.asserts.assert(keyToCheck.toLowerCase() == keyToCheck);
    +        if (keyMapLower == keyToCheck) {
    +          throw Error('Cannot override "' + keyToCheck + '" ' + type +
    +              ', got "' + keyMap + '" with value "' + map[keyMap] + '"');
    +        }
    +      }
    +    }
    +  };
    +
    +  verifyNotInMap(keys, opt_attributes, 'attribute');
    +  verifyNotInMap(keys, opt_params, 'param');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/flash_test.html b/mapbox-gl/closure-library/closure/goog/html/flash_test.html
    new file mode 100644
    index 0000000000..0477c48361
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/flash_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html.flash</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.flashTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/flash_test.js b/mapbox-gl/closure-library/closure/goog/html/flash_test.js
    new file mode 100644
    index 0000000000..3ba31fe87c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/flash_test.js
    @@ -0,0 +1,111 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.flash.
    + */
    +
    +goog.provide('goog.html.flashTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.flash');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.flashTest');
    +
    +
    +function testCreateEmbed() {
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted&'));
    +  assertSameHtml(
    +      '<embed ' +
    +          'src="https://google.com/trusted&amp;" ' +
    +          'type="application/x-shockwave-flash" ' +
    +          'pluginspage="https://www.macromedia.com/go/getflashplayer" ' +
    +          'allownetworking="none" ' +
    +          'allowScriptAccess="always&lt;" ' +
    +          'class="test&lt;">',
    +      goog.html.flash.createEmbed(
    +          trustedResourceUrl,
    +          {'allowScriptAccess': 'always<', 'class': 'test<'}));
    +
    +  // Cannot override attributes, case insensitive.
    +  assertThrows(function() {
    +    goog.html.flash.createEmbed(
    +        trustedResourceUrl, {'Type': 'cannotdothis'});
    +  });
    +}
    +
    +
    +function testCreateObject() {
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted&'));
    +  assertSameHtml(
    +      '<object data="https://google.com/trusted&amp;" ' +
    +          'type="application/x-shockwave-flash" typemustmatch="" ' +
    +          'class="test&lt;">' +
    +          '<param name="allownetworking" value="none">' +
    +          '<param name="allowScriptAccess" value="always&lt;">' +
    +          '</object>',
    +      goog.html.flash.createObject(
    +          trustedResourceUrl,
    +          {'allowScriptAccess': 'always<'}, {'class': 'test<'}));
    +
    +  // Cannot override params, case insensitive.
    +  assertThrows(function() {
    +    goog.html.flash.createObject(
    +        trustedResourceUrl, {'datA': 'cantdothis'});
    +  });
    +
    +  // Cannot override attributes, case insensitive.
    +  assertThrows(function() {
    +    goog.html.flash.createObject(
    +        trustedResourceUrl, {}, {'datA': 'cantdothis'});
    +  });
    +}
    +
    +
    +function testCreateObjectForOldIe() {
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted&'));
    +  assertSameHtml(
    +      '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' +
    +          'class="test&lt;">' +
    +          '<param name="allownetworking" value="none">' +
    +          '<param name="movie" value="https://google.com/trusted&amp;">' +
    +          '<param name="allowScriptAccess" value="always&lt;">' +
    +          '</object>',
    +      goog.html.flash.createObjectForOldIe(
    +          trustedResourceUrl,
    +          {'allowScriptAccess': 'always<'}, {'class': 'test<'}));
    +
    +  // Cannot override params, case insensitive.
    +  assertThrows(function() {
    +    goog.html.flash.createObjectForOldIe(
    +        trustedResourceUrl, {'datA': 'cantdothis'});
    +  });
    +
    +  // Cannot override attributes, case insensitive.
    +  assertThrows(function() {
    +    goog.html.flash.createObjectForOldIe(
    +        trustedResourceUrl, {}, {'datA': 'cantdothis'});
    +  });
    +}
    +
    +
    +function assertSameHtml(expected, html) {
    +  assertEquals(expected, goog.html.SafeHtml.unwrap(html));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/legacyconversions.js b/mapbox-gl/closure-library/closure/goog/html/legacyconversions.js
    new file mode 100644
    index 0000000000..e56b42cce7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/legacyconversions.js
    @@ -0,0 +1,180 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Conversions from plain string to goog.html types for use in
    + * legacy APIs that do not use goog.html types.
    + *
    + * This file provides conversions to create values of goog.html types from plain
    + * strings.  These conversions are intended for use in legacy APIs that consume
    + * HTML in the form of plain string types, but whose implementations use
    + * goog.html types internally (and expose such types in an augmented, HTML-type-
    + * safe API).
    + *
    + * IMPORTANT: No new code should use the conversion functions in this file.
    + *
    + * The conversion functions in this file are guarded with global flag
    + * (goog.html.legacyconversions.ALLOW_LEGACY_CONVERSIONS). If set to false, it
    + * effectively "locks in" an entire application to only use HTML-type-safe APIs.
    + *
    + * Intended use of the functions in this file are as follows:
    + *
    + * Many Closure and application-specific classes expose methods that consume
    + * values that in the class' implementation are forwarded to DOM APIs that can
    + * result in security vulnerabilities.  For example, goog.ui.Dialog's setContent
    + * method consumes a string that is assigned to an element's innerHTML property;
    + * if this string contains untrusted (attacker-controlled) data, this can result
    + * in a cross-site-scripting vulnerability.
    + *
    + * Widgets such as goog.ui.Dialog are being augmented to expose safe APIs
    + * expressed in terms of goog.html types.  For instance, goog.ui.Dialog has a
    + * method setSafeHtmlContent that consumes an object of type goog.html.SafeHtml,
    + * a type whose contract guarantees that its value is safe to use in HTML
    + * context, i.e. can be safely assigned to .innerHTML. An application that only
    + * uses this API is forced to only supply values of this type, i.e. values that
    + * are safe.
    + *
    + * However, the legacy method setContent cannot (for the time being) be removed
    + * from goog.ui.Dialog, due to a large number of existing callers.  The
    + * implementation of goog.ui.Dialog has been refactored to use
    + * goog.html.SafeHtml throughout.  This in turn requires that the value consumed
    + * by its setContent method is converted to goog.html.SafeHtml in an unchecked
    + * conversion. The conversion function is provided by this file:
    + * goog.html.legacyconversions.safeHtmlFromString.
    + *
    + * Note that the semantics of the conversions in goog.html.legacyconversions are
    + * very different from the ones provided by goog.html.uncheckedconversions:  The
    + * latter are for use in code where it has been established through manual
    + * security review that the value produced by a piece of code must always
    + * satisfy the SafeHtml contract (e.g., the output of a secure HTML sanitizer).
    + * In uses of goog.html.legacyconversions, this guarantee is not given -- the
    + * value in question originates in unreviewed legacy code and there is no
    + * guarantee that it satisfies the SafeHtml contract.
    + *
    + * To establish correctness with confidence, application code should be
    + * refactored to use SafeHtml instead of plain string to represent HTML markup,
    + * and to use goog.html-typed APIs (e.g., goog.ui.Dialog#setSafeHtmlContent
    + * instead of goog.ui.Dialog#setContent).
    + *
    + * To prevent introduction of new vulnerabilities, application owners can
    + * effectively disable unsafe legacy APIs by compiling with the define
    + * goog.html.legacyconversions.ALLOW_LEGACY_CONVERSIONS set to false.  When
    + * set, this define causes the conversion methods in this file to
    + * unconditionally throw an exception.
    + *
    + * Note that new code should always be compiled with
    + * ALLOW_LEGACY_CONVERSIONS=false.  At some future point, the default for this
    + * define may change to false.
    + */
    +
    +
    +goog.provide('goog.html.legacyconversions');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +
    +
    +/**
    + * @define {boolean} Whether conversion from string to goog.html types for
    + * legacy API purposes is permitted.
    + *
    + * If false, the conversion functions in this file unconditionally throw an
    + * exception.
    + */
    +goog.define('goog.html.legacyconversions.ALLOW_LEGACY_CONVERSIONS', true);
    +
    +
    +/**
    + * Performs an "unchecked conversion" from string to SafeHtml for legacy API
    + * purposes.
    + *
    + * Unchecked conversion will not proceed if ALLOW_LEGACY_CONVERSIONS is false,
    + * and instead this function unconditionally throws an exception.
    + *
    + * @param {string} html A string to be converted to SafeHtml.
    + * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
    + *     object.
    + */
    +goog.html.legacyconversions.safeHtmlFromString = function(html) {
    +  goog.html.legacyconversions.throwIfConversionDisallowed_();
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      html, null /* dir */);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" from string to TrustedResourceUrl for
    + * legacy API purposes.
    + *
    + * Unchecked conversion will not proceed if ALLOW_LEGACY_CONVERSIONS is false,
    + * and instead this function unconditionally throws an exception.
    + *
    + * @param {string} url A string to be converted to TrustedResourceUrl.
    + * @return {!goog.html.TrustedResourceUrl} The value of url, wrapped in a
    + *     TrustedResourceUrl object.
    + */
    +goog.html.legacyconversions.trustedResourceUrlFromString = function(url) {
    +  goog.html.legacyconversions.throwIfConversionDisallowed_();
    +  return goog.html.TrustedResourceUrl.
    +      createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" from string to SafeUrl for legacy API
    + * purposes.
    + *
    + * Unchecked conversion will not proceed if ALLOW_LEGACY_CONVERSIONS is false,
    + * and instead this function unconditionally throws an exception.
    + *
    + * @param {string} url A string to be converted to SafeUrl.
    + * @return {!goog.html.SafeUrl} The value of url, wrapped in a SafeUrl
    + *     object.
    + */
    +goog.html.legacyconversions.safeUrlFromString = function(url) {
    +  goog.html.legacyconversions.throwIfConversionDisallowed_();
    +  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    +
    +
    +/**
    + * @private {function(): undefined}
    + */
    +goog.html.legacyconversions.reportCallback_ = goog.nullFunction;
    +
    +
    +/**
    + * Sets a function that will be called every time a legacy conversion is
    + * performed. The function is called with no parameters but it can use
    + * goog.debug.getStacktrace to get a stacktrace.
    + *
    + * @param {function(): undefined} callback Error callback as defined above.
    + */
    +goog.html.legacyconversions.setReportCallback = function(callback) {
    +  goog.html.legacyconversions.reportCallback_ = callback;
    +};
    +
    +
    +/**
    + * Checks whether legacy conversion is allowed. Throws an exception if not.
    + * @private
    + */
    +goog.html.legacyconversions.throwIfConversionDisallowed_ = function() {
    +  if (!goog.html.legacyconversions.ALLOW_LEGACY_CONVERSIONS) {
    +    throw Error(
    +        'Error: Legacy conversion from string to goog.html types is disabled');
    +  }
    +  goog.html.legacyconversions.reportCallback_();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.html b/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.html
    new file mode 100644
    index 0000000000..7af17d734c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.legacyconversionsTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.js b/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.js
    new file mode 100644
    index 0000000000..9e8841b38a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/legacyconversions_test.js
    @@ -0,0 +1,95 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.legacyconversions.
    + */
    +
    +goog.provide('goog.html.legacyconversionsTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.legacyconversionsTest');
    +
    +
    +/** @type {!goog.testing.PropertyReplacer} */
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +function setUp() {
    +  // Reset goog.html.legacyconveresions global defines for each test case.
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', true);
    +}
    +
    +
    +function testSafeHtmlFromString_allowedIfNotGloballyDisabled() {
    +  var helloWorld = 'Hello <em>World</em>';
    +  var safeHtml = goog.html.legacyconversions.safeHtmlFromString(helloWorld);
    +  assertEquals(helloWorld, goog.html.SafeHtml.unwrap(safeHtml));
    +  assertNull(safeHtml.getDirection());
    +}
    +
    +
    +function testSafeHtmlFromString_guardedByGlobalFlag() {
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', false);
    +  assertEquals(
    +      'Error: Legacy conversion from string to goog.html types is disabled',
    +      assertThrows(function() {
    +        goog.html.legacyconversions.safeHtmlFromString(
    +            'Possibly untrusted <html>');
    +      }).message);
    +}
    +
    +
    +function testSafeHtmlFromString_reports() {
    +  var reported = false;
    +  goog.html.legacyconversions.setReportCallback(function() {
    +    reported = true;
    +  });
    +  goog.html.legacyconversions.safeHtmlFromString('<html>');
    +  assertTrue('Expected legacy conversion to be reported.', reported);
    +
    +  reported = false;
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', false);
    +  try {
    +    goog.html.legacyconversions.safeHtmlFromString('<html>');
    +  } catch (expected) {
    +  }
    +  assertFalse('Expected legacy conversion to not be reported.', reported);
    +
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', true);
    +  goog.html.legacyconversions.setReportCallback(goog.nullFunction);
    +  goog.html.legacyconversions.safeHtmlFromString('<html>');
    +  assertFalse('Expected legacy conversion to not be reported.', reported);
    +}
    +
    +
    +function testSafeUrlFromString() {
    +  var url = 'https://www.google.com';
    +  var safeUrl = goog.html.legacyconversions.safeUrlFromString(url);
    +  assertEquals(url, goog.html.SafeUrl.unwrap(safeUrl));
    +}
    +
    +
    +function testTrustedResourceUrlFromString() {
    +  var url = 'https://www.google.com/script.js';
    +  var trustedResourceUrl =
    +      goog.html.legacyconversions.trustedResourceUrlFromString(url);
    +  assertEquals(url, goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safehtml.js b/mapbox-gl/closure-library/closure/goog/html/safehtml.js
    new file mode 100644
    index 0000000000..4e084e737e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safehtml.js
    @@ -0,0 +1,729 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview The SafeHtml type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.SafeHtml');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.tags');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.i18n.bidi.DirectionalString');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A string that is safe to use in HTML context in DOM APIs and HTML documents.
    + *
    + * A SafeHtml is a string-like object that carries the security type contract
    + * that its value as a string will not cause untrusted script execution when
    + * evaluated as HTML in a browser.
    + *
    + * Values of this type are guaranteed to be safe to use in HTML contexts,
    + * such as, assignment to the innerHTML DOM property, or interpolation into
    + * a HTML template in HTML PC_DATA context, in the sense that the use will not
    + * result in a Cross-Site-Scripting vulnerability.
    + *
    + * Instances of this type must be created via the factory methods
    + * ({@code goog.html.SafeHtml.create}, {@code goog.html.SafeHtml.htmlEscape}),
    + * etc and not by invoking its constructor.  The constructor intentionally
    + * takes no parameters and the type is immutable; hence only a default instance
    + * corresponding to the empty string can be obtained via constructor invocation.
    + *
    + * @see goog.html.SafeHtml#create
    + * @see goog.html.SafeHtml#htmlEscape
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.i18n.bidi.DirectionalString}
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.SafeHtml = function() {
    +  /**
    +   * The contained value of this SafeHtml.  The field has a purposely ugly
    +   * name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.SafeHtml#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +
    +  /**
    +   * This SafeHtml's directionality, or null if unknown.
    +   * @private {?goog.i18n.bidi.Dir}
    +   */
    +  this.dir_ = null;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;
    +
    +
    +/** @override */
    +goog.html.SafeHtml.prototype.getDirection = function() {
    +  return this.dir_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Returns this SafeHtml's value a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code SafeHtml}, use {@code goog.html.SafeHtml.unwrap} instead of
    + * this method. If in doubt, assume that it's security relevant. In particular,
    + * note that goog.html functions which return a goog.html type do not guarantee
    + * that the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
    + * // instanceof goog.html.SafeHtml.
    + * </pre>
    + *
    + * @see goog.html.SafeHtml#unwrap
    + * @override
    + */
    +goog.html.SafeHtml.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a SafeHtml, use
    +   * {@code goog.html.SafeHtml.unwrap}.
    +   *
    +   * @see goog.html.SafeHtml#unwrap
    +   * @override
    +   */
    +  goog.html.SafeHtml.prototype.toString = function() {
    +    return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
    +        '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a SafeHtml
    + * object, and returns its value.
    + * @param {!goog.html.SafeHtml} safeHtml The object to extract from.
    + * @return {string} The SafeHtml object's contained string, unless the run-time
    + *     type check fails. In that case, {@code unwrap} returns an innocuous
    + *     string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.SafeHtml.unwrap = function(safeHtml) {
    +  // Perform additional run-time type-checking to ensure that safeHtml is indeed
    +  // an instance of the expected type.  This provides some additional protection
    +  // against security bugs due to application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (safeHtml instanceof goog.html.SafeHtml &&
    +      safeHtml.constructor === goog.html.SafeHtml &&
    +      safeHtml.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +          goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
    +  } else {
    +    goog.asserts.fail('expected object of type SafeHtml, got \'' +
    +                      safeHtml + '\'');
    +    return 'type_error:SafeHtml';
    +  }
    +};
    +
    +
    +/**
    + * Shorthand for union of types that can sensibly be converted to strings
    + * or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).
    + * @private
    + * @typedef {string|number|boolean|!goog.string.TypedString|
    + *           !goog.i18n.bidi.DirectionalString}
    + */
    +goog.html.SafeHtml.TextOrHtml_;
    +
    +
    +/**
    + * Returns HTML-escaped text as a SafeHtml object.
    + *
    + * If text is of a type that implements
    + * {@code goog.i18n.bidi.DirectionalString}, the directionality of the new
    + * {@code SafeHtml} object is set to {@code text}'s directionality, if known.
    + * Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,
    + * {@code null}).
    + *
    + * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
    + *     the parameter is of type SafeHtml it is returned directly (no escaping
    + *     is done).
    + * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
    + */
    +goog.html.SafeHtml.htmlEscape = function(textOrHtml) {
    +  if (textOrHtml instanceof goog.html.SafeHtml) {
    +    return textOrHtml;
    +  }
    +  var dir = null;
    +  if (textOrHtml.implementsGoogI18nBidiDirectionalString) {
    +    dir = textOrHtml.getDirection();
    +  }
    +  var textAsString;
    +  if (textOrHtml.implementsGoogStringTypedString) {
    +    textAsString = textOrHtml.getTypedStringValue();
    +  } else {
    +    textAsString = String(textOrHtml);
    +  }
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      goog.string.htmlEscape(textAsString), dir);
    +};
    +
    +
    +/**
    + * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
    + * &lt;br&gt;.
    + * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
    + *     the parameter is of type SafeHtml it is returned directly (no escaping
    + *     is done).
    + * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
    + */
    +goog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {
    +  if (textOrHtml instanceof goog.html.SafeHtml) {
    +    return textOrHtml;
    +  }
    +  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      goog.string.newLineToBr(goog.html.SafeHtml.unwrap(html)),
    +      html.getDirection());
    +};
    +
    +
    +/**
    + * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
    + * &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character
    + * entity #160 is used to make it safer for XML.
    + * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
    + *     the parameter is of type SafeHtml it is returned directly (no escaping
    + *     is done).
    + * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
    + */
    +goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(
    +    textOrHtml) {
    +  if (textOrHtml instanceof goog.html.SafeHtml) {
    +    return textOrHtml;
    +  }
    +  var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      goog.string.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),
    +      html.getDirection());
    +};
    +
    +
    +/**
    + * Coerces an arbitrary object into a SafeHtml object.
    + *
    + * If {@code textOrHtml} is already of type {@code goog.html.SafeHtml}, the same
    + * object is returned. Otherwise, {@code textOrHtml} is coerced to string, and
    + * HTML-escaped. If {@code textOrHtml} is of a type that implements
    + * {@code goog.i18n.bidi.DirectionalString}, its directionality, if known, is
    + * preserved.
    + *
    + * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to
    + *     coerce.
    + * @return {!goog.html.SafeHtml} The resulting SafeHtml object.
    + * @deprecated Use goog.html.SafeHtml.htmlEscape.
    + */
    +goog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;
    +
    +
    +/**
    + * @const
    + * @private
    + */
    +goog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;
    +
    +
    +/**
    + * Set of attributes containing URL as defined at
    + * http://www.w3.org/TR/html5/index.html#attributes-1.
    + * @private @const {!Object<string,boolean>}
    + */
    +goog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet('action', 'cite',
    +    'data', 'formaction', 'href', 'manifest', 'poster', 'src');
    +
    +
    +/**
    + * Tags which are unsupported via create(). They might be supported via a
    + * tag-specific create method. These are tags which might require a
    + * TrustedResourceUrl in one of their attributes or a restricted type for
    + * their content.
    + * @private @const {!Object<string,boolean>}
    + */
    +goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(
    +    'embed', 'iframe', 'link', 'object', 'script', 'style', 'template');
    +
    +
    +/**
    + * @typedef {string|number|goog.string.TypedString|
    + *     goog.html.SafeStyle.PropertyMap}
    + * @private
    + */
    +goog.html.SafeHtml.AttributeValue_;
    +
    +
    +/**
    + * Creates a SafeHtml content consisting of a tag with optional attributes and
    + * optional content.
    + *
    + * For convenience tag names and attribute names are accepted as regular
    + * strings, instead of goog.string.Const. Nevertheless, you should not pass
    + * user-controlled values to these parameters. Note that these parameters are
    + * syntactically validated at runtime, and invalid values will result in
    + * an exception.
    + *
    + * Example usage:
    + *
    + * goog.html.SafeHtml.create('br');
    + * goog.html.SafeHtml.create('div', {'class': 'a'});
    + * goog.html.SafeHtml.create('p', {}, 'a');
    + * goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));
    + *
    + * goog.html.SafeHtml.create('span', {
    + *   'style': {'margin': '0'}
    + * });
    + *
    + * To guarantee SafeHtml's type contract is upheld there are restrictions on
    + * attribute values and tag names.
    + *
    + * - For attributes which contain script code (on*), a goog.string.Const is
    + *   required.
    + * - For attributes which contain style (style), a goog.html.SafeStyle or a
    + *   goog.html.SafeStyle.PropertyMap is required.
    + * - For attributes which are interpreted as URLs (e.g. src, href) a
    + *   goog.html.SafeUrl or goog.string.Const is required.
    + * - For tags which can load code, more specific goog.html.SafeHtml.create*()
    + *   functions must be used. Tags which can load code and are not supported by
    + *   this function are embed, iframe, link, object, script, style, and template.
    + *
    + * @param {string} tagName The name of the tag. Only tag names consisting of
    + *     [a-zA-Z0-9-] are allowed. Tag names documented above are disallowed.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Mapping from attribute names to their values. Only
    + *     attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    + *     undefined causes the attribute to be omitted.
    + * @param {!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
    + *     HTML-escape and put inside the tag. This must be empty for void tags
    + *     like <br>. Array elements are concatenated.
    + * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    + * @throws {Error} If invalid tag name, attribute name, or attribute value is
    + *     provided.
    + * @throws {goog.asserts.AssertionError} If content for void tag is provided.
    + */
    +goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
    +  if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
    +    throw Error('Invalid tag name <' + tagName + '>.');
    +  }
    +  if (tagName.toLowerCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
    +    throw Error('Tag name <' + tagName + '> is not allowed for SafeHtml.');
    +  }
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      tagName, opt_attributes, opt_content);
    +};
    +
    +
    +/**
    + * Creates a SafeHtml representing an iframe tag.
    + *
    + * By default the sandbox attribute is set to an empty value, which is the most
    + * secure option, as it confers the iframe the least privileges. If this
    + * is too restrictive then granting individual privileges is the preferable
    + * option. Unsetting the attribute entirely is the least secure option and
    + * should never be done unless it's stricly necessary.
    + *
    + * @param {goog.html.TrustedResourceUrl=} opt_src The value of the src
    + *     attribute. If null or undefined src will not be set.
    + * @param {goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
    + *     If null or undefined srcdoc will not be set.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Mapping from attribute names to their values. Only
    + *     attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    + *     undefined causes the attribute to be omitted.
    + * @param {!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
    + *     HTML-escape and put inside the tag. Array elements are concatenated.
    + * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    + * @throws {Error} If invalid tag name, attribute name, or attribute value is
    + *     provided. If opt_attributes contains the src or srcdoc attributes.
    + */
    +goog.html.SafeHtml.createIframe = function(
    +    opt_src, opt_srcdoc, opt_attributes, opt_content) {
    +  var fixedAttributes = {};
    +  fixedAttributes['src'] = opt_src || null;
    +  fixedAttributes['srcdoc'] = opt_srcdoc || null;
    +  var defaultAttributes = {'sandbox': ''};
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, defaultAttributes, opt_attributes);
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      'iframe', attributes, opt_content);
    +};
    +
    +
    +/**
    + * Creates a SafeHtml representing a style tag. The type attribute is set
    + * to "text/css".
    + * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
    + *     styleSheet Content to put inside the tag. Array elements are
    + *     concatenated.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Mapping from attribute names to their values. Only
    + *     attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    + *     undefined causes the attribute to be omitted.
    + * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    + * @throws {Error} If invalid attribute name or attribute value is provided. If
    + *     opt_attributes contains the type attribute.
    + */
    +goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
    +  var fixedAttributes = {'type': 'text/css'};
    +  var defaultAttributes = {};
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, defaultAttributes, opt_attributes);
    +
    +  var content = '';
    +  styleSheet = goog.array.concat(styleSheet);
    +  for (var i = 0; i < styleSheet.length; i++) {
    +    content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
    +  }
    +  // Convert to SafeHtml so that it's not HTML-escaped.
    +  var htmlContent = goog.html.SafeHtml
    +      .createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +          content, goog.i18n.bidi.Dir.NEUTRAL);
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      'style', attributes, htmlContent);
    +};
    +
    +
    +/**
    + * @param {string} tagName The tag name.
    + * @param {string} name The attribute name.
    + * @param {!goog.html.SafeHtml.AttributeValue_} value The attribute value.
    + * @return {string} A "name=value" string.
    + * @throws {Error} If attribute value is unsafe for the given tag and attribute.
    + * @private
    + */
    +goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
    +  // If it's goog.string.Const, allow any valid attribute name.
    +  if (value instanceof goog.string.Const) {
    +    value = goog.string.Const.unwrap(value);
    +  } else if (name.toLowerCase() == 'style') {
    +    value = goog.html.SafeHtml.getStyleValue_(value);
    +  } else if (/^on/i.test(name)) {
    +    // TODO(jakubvrana): Disallow more attributes with a special meaning.
    +    throw Error('Attribute "' + name +
    +        '" requires goog.string.Const value, "' + value + '" given.');
    +  // URL attributes handled differently accroding to tag.
    +  } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
    +    if (value instanceof goog.html.TrustedResourceUrl) {
    +      value = goog.html.TrustedResourceUrl.unwrap(value);
    +    } else if (value instanceof goog.html.SafeUrl) {
    +      value = goog.html.SafeUrl.unwrap(value);
    +    } else {
    +      // TODO(user): Allow strings and sanitize them automatically,
    +      // so that it's consistent with accepting a map directly for "style".
    +      throw Error('Attribute "' + name + '" on tag "' + tagName +
    +          '" requires goog.html.SafeUrl or goog.string.Const value, "' +
    +          value + '" given.');
    +    }
    +  }
    +
    +  // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
    +  // HTML-escaping.
    +  if (value.implementsGoogStringTypedString) {
    +    // Ok to call getTypedStringValue() since there's no reliance on the type
    +    // contract for security here.
    +    value = value.getTypedStringValue();
    +  }
    +
    +  goog.asserts.assert(goog.isString(value) || goog.isNumber(value),
    +      'String or number value expected, got ' +
    +      (typeof value) + ' with value: ' + value);
    +  return name + '="' + goog.string.htmlEscape(String(value)) + '"';
    +};
    +
    +
    +/**
    + * Gets value allowed in "style" attribute.
    + * @param {goog.html.SafeHtml.AttributeValue_} value It could be SafeStyle or a
    + *     map which will be passed to goog.html.SafeStyle.create.
    + * @return {string} Unwrapped value.
    + * @throws {Error} If string value is given.
    + * @private
    + */
    +goog.html.SafeHtml.getStyleValue_ = function(value) {
    +  if (!goog.isObject(value)) {
    +    throw Error('The "style" attribute requires goog.html.SafeStyle or map ' +
    +        'of style properties, ' + (typeof value) + ' given: ' + value);
    +  }
    +  if (!(value instanceof goog.html.SafeStyle)) {
    +    // Process the property bag into a style object.
    +    value = goog.html.SafeStyle.create(value);
    +  }
    +  return goog.html.SafeStyle.unwrap(value);
    +};
    +
    +
    +/**
    + * Creates a SafeHtml content with known directionality consisting of a tag with
    + * optional attributes and optional content.
    + * @param {!goog.i18n.bidi.Dir} dir Directionality.
    + * @param {string} tagName
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
    + * @param {!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
    + * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
    + */
    +goog.html.SafeHtml.createWithDir = function(dir, tagName, opt_attributes,
    +    opt_content) {
    +  var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
    +  html.dir_ = dir;
    +  return html;
    +};
    +
    +
    +/**
    + * Creates a new SafeHtml object by concatenating values.
    + * @param {...(!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
    + * @return {!goog.html.SafeHtml}
    + */
    +goog.html.SafeHtml.concat = function(var_args) {
    +  var dir = goog.i18n.bidi.Dir.NEUTRAL;
    +  var content = '';
    +
    +  /**
    +   * @param {!goog.html.SafeHtml.TextOrHtml_|
    +   *     !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
    +   */
    +  var addArgument = function(argument) {
    +    if (goog.isArray(argument)) {
    +      goog.array.forEach(argument, addArgument);
    +    } else {
    +      var html = goog.html.SafeHtml.htmlEscape(argument);
    +      content += goog.html.SafeHtml.unwrap(html);
    +      var htmlDir = html.getDirection();
    +      if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
    +        dir = htmlDir;
    +      } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
    +        dir = null;
    +      }
    +    }
    +  };
    +
    +  goog.array.forEach(arguments, addArgument);
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      content, dir);
    +};
    +
    +
    +/**
    + * Creates a new SafeHtml object with known directionality by concatenating the
    + * values.
    + * @param {!goog.i18n.bidi.Dir} dir Directionality.
    + * @param {...(!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
    + *     arguments would be processed recursively.
    + * @return {!goog.html.SafeHtml}
    + */
    +goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
    +  var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
    +  html.dir_ = dir;
    +  return html;
    +};
    +
    +
    +/**
    + * Type marker for the SafeHtml type, used to implement additional run-time
    + * type checking.
    + * @const
    + * @private
    + */
    +goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Package-internal utility method to create SafeHtml instances.
    + *
    + * @param {string} html The string to initialize the SafeHtml object with.
    + * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
    + *     constructed, or null if unknown.
    + * @return {!goog.html.SafeHtml} The initialized SafeHtml object.
    + * @package
    + */
    +goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
    +    html, dir) {
    +  var safeHtml = new goog.html.SafeHtml();
    +  safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = html;
    +  safeHtml.dir_ = dir;
    +  return safeHtml;
    +};
    +
    +
    +/**
    + * Like create() but does not restrict which tags can be constructed.
    + *
    + * @param {string} tagName Tag name. Set or validated by caller.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
    + * @param {(!goog.html.SafeHtml.TextOrHtml_|
    + *     !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
    + * @return {!goog.html.SafeHtml}
    + * @throws {Error} If invalid or unsafe attribute name or value is provided.
    + * @throws {goog.asserts.AssertionError} If content for void tag is provided.
    + * @package
    + */
    +goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse =
    +    function(tagName, opt_attributes, opt_content) {
    +  var dir = null;
    +  var result = '<' + tagName;
    +
    +  if (opt_attributes) {
    +    for (var name in opt_attributes) {
    +      if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
    +        throw Error('Invalid attribute name "' + name + '".');
    +      }
    +      var value = opt_attributes[name];
    +      if (!goog.isDefAndNotNull(value)) {
    +        continue;
    +      }
    +      result += ' ' +
    +          goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
    +    }
    +  }
    +
    +  var content = opt_content;
    +  if (!goog.isDef(content)) {
    +    content = [];
    +  } else if (!goog.isArray(content)) {
    +    content = [content];
    +  }
    +
    +  if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
    +    goog.asserts.assert(!content.length,
    +        'Void tag <' + tagName + '> does not allow content.');
    +    result += '>';
    +  } else {
    +    var html = goog.html.SafeHtml.concat(content);
    +    result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
    +    dir = html.getDirection();
    +  }
    +
    +  var dirAttribute = opt_attributes && opt_attributes['dir'];
    +  if (dirAttribute) {
    +    if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
    +      // If the tag has the "dir" attribute specified then its direction is
    +      // neutral because it can be safely used in any context.
    +      dir = goog.i18n.bidi.Dir.NEUTRAL;
    +    } else {
    +      dir = null;
    +    }
    +  }
    +
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      result, dir);
    +};
    +
    +
    +/**
    + * @param {!Object<string, string>} fixedAttributes
    + * @param {!Object<string, string>} defaultAttributes
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Optional attributes passed to create*().
    + * @return {!Object<string, goog.html.SafeHtml.AttributeValue_>}
    + * @throws {Error} If opt_attributes contains an attribute with the same name
    + *     as an attribute in fixedAttributes.
    + * @package
    + */
    +goog.html.SafeHtml.combineAttributes = function(
    +    fixedAttributes, defaultAttributes, opt_attributes) {
    +  var combinedAttributes = {};
    +  var name;
    +
    +  for (name in fixedAttributes) {
    +    goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    +    combinedAttributes[name] = fixedAttributes[name];
    +  }
    +  for (name in defaultAttributes) {
    +    goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
    +    combinedAttributes[name] = defaultAttributes[name];
    +  }
    +
    +  for (name in opt_attributes) {
    +    var nameLower = name.toLowerCase();
    +    if (nameLower in fixedAttributes) {
    +      throw Error('Cannot override "' + nameLower + '" attribute, got "' +
    +          name + '" with value "' + opt_attributes[name] + '"');
    +    }
    +    if (nameLower in defaultAttributes) {
    +      delete combinedAttributes[nameLower];
    +    }
    +    combinedAttributes[name] = opt_attributes[name];
    +  }
    +
    +  return combinedAttributes;
    +};
    +
    +
    +/**
    + * A SafeHtml instance corresponding to the empty string.
    + * @const {!goog.html.SafeHtml}
    + */
    +goog.html.SafeHtml.EMPTY =
    +    goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +        '', goog.i18n.bidi.Dir.NEUTRAL);
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safehtml_test.html b/mapbox-gl/closure-library/closure/goog/html/safehtml_test.html
    new file mode 100644
    index 0000000000..9eeec19fc1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safehtml_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.safeHtmlTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safehtml_test.js b/mapbox-gl/closure-library/closure/goog/html/safehtml_test.js
    new file mode 100644
    index 0000000000..c3cadeb936
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safehtml_test.js
    @@ -0,0 +1,387 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.SafeHtml and its builders.
    + */
    +
    +goog.provide('goog.html.safeHtmlTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.testing');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.safeHtmlTest');
    +
    +
    +
    +function testSafeHtml() {
    +  // TODO(user): Consider using SafeHtmlBuilder instead of newSafeHtmlForTest,
    +  // when available.
    +  var safeHtml = goog.html.testing.newSafeHtmlForTest('Hello <em>World</em>');
    +  assertSameHtml('Hello <em>World</em>', safeHtml);
    +  assertEquals('Hello <em>World</em>', goog.html.SafeHtml.unwrap(safeHtml));
    +  assertEquals('SafeHtml{Hello <em>World</em>}', String(safeHtml));
    +  assertNull(safeHtml.getDirection());
    +
    +  safeHtml = goog.html.testing.newSafeHtmlForTest(
    +      'World <em>Hello</em>', goog.i18n.bidi.Dir.RTL);
    +  assertSameHtml('World <em>Hello</em>', safeHtml);
    +  assertEquals('World <em>Hello</em>', goog.html.SafeHtml.unwrap(safeHtml));
    +  assertEquals('SafeHtml{World <em>Hello</em>}', String(safeHtml));
    +  assertEquals(goog.i18n.bidi.Dir.RTL, safeHtml.getDirection());
    +
    +  // Interface markers are present.
    +  assertTrue(safeHtml.implementsGoogStringTypedString);
    +  assertTrue(safeHtml.implementsGoogI18nBidiDirectionalString);
    +
    +  // Pre-defined constant.
    +  assertSameHtml('', goog.html.SafeHtml.EMPTY);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.safeHtmlValueWithSecurityContract__googHtmlSecurityPrivate_ =
    +      '<script>evil()</script';
    +  evil.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.SafeHtml.unwrap(evil);
    +  });
    +  assertTrue(exception.message.indexOf('expected object of type SafeHtml') > 0);
    +}
    +
    +
    +function testHtmlEscape() {
    +  // goog.html.SafeHtml passes through unchanged.
    +  var safeHtmlIn = goog.html.SafeHtml.htmlEscape('<b>in</b>');
    +  assertTrue(safeHtmlIn === goog.html.SafeHtml.htmlEscape(safeHtmlIn));
    +
    +  // Plain strings are escaped.
    +  var safeHtml = goog.html.SafeHtml.htmlEscape('Hello <em>"\'&World</em>');
    +  assertSameHtml('Hello &lt;em&gt;&quot;&#39;&amp;World&lt;/em&gt;', safeHtml);
    +  assertEquals('SafeHtml{Hello &lt;em&gt;&quot;&#39;&amp;World&lt;/em&gt;}',
    +      String(safeHtml));
    +
    +  // Creating from a SafeUrl escapes and retains the known direction (which is
    +  // fixed to RTL for URLs).
    +  var safeUrl = goog.html.SafeUrl.fromConstant(
    +      goog.string.Const.from('http://example.com/?foo&bar'));
    +  var escapedUrl = goog.html.SafeHtml.htmlEscape(safeUrl);
    +  assertSameHtml('http://example.com/?foo&amp;bar', escapedUrl);
    +  assertEquals(goog.i18n.bidi.Dir.LTR, escapedUrl.getDirection());
    +
    +  // Creating SafeHtml from a goog.string.Const escapes as well (i.e., the
    +  // value is treated like any other string). To create HTML markup from
    +  // program literals, SafeHtmlBuilder should be used.
    +  assertSameHtml('this &amp; that',
    +      goog.html.SafeHtml.htmlEscape(goog.string.Const.from('this & that')));
    +}
    +
    +
    +function testSafeHtmlCreate() {
    +  var br = goog.html.SafeHtml.create('br');
    +
    +  assertSameHtml('<br>', br);
    +
    +  assertSameHtml('<span title="&quot;"></span>',
    +      goog.html.SafeHtml.create('span', {'title': '"'}));
    +
    +  assertSameHtml('<span>&lt;</span>',
    +      goog.html.SafeHtml.create('span', {}, '<'));
    +
    +  assertSameHtml('<span><br></span>',
    +      goog.html.SafeHtml.create('span', {}, br));
    +
    +  assertSameHtml('<span></span>', goog.html.SafeHtml.create('span', {}, []));
    +
    +  assertSameHtml('<span></span>',
    +      goog.html.SafeHtml.create('span', {'title': null, 'class': undefined}));
    +
    +  assertSameHtml('<span>x<br>y</span>',
    +      goog.html.SafeHtml.create('span', {}, ['x', br, 'y']));
    +
    +  assertSameHtml('<table border="0"></table>',
    +      goog.html.SafeHtml.create('table', {'border': 0}));
    +
    +  var onclick = goog.string.Const.from('alert(/"/)');
    +  assertSameHtml('<span onclick="alert(/&quot;/)"></span>',
    +      goog.html.SafeHtml.create('span', {'onclick': onclick}));
    +
    +  var href = goog.html.testing.newSafeUrlForTest('?a&b');
    +  assertSameHtml('<a href="?a&amp;b"></a>',
    +      goog.html.SafeHtml.create('a', {'href': href}));
    +
    +  var style = goog.html.testing.newSafeStyleForTest('border: /* " */ 0;');
    +  assertSameHtml('<hr style="border: /* &quot; */ 0;">',
    +      goog.html.SafeHtml.create('hr', {'style': style}));
    +
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +      goog.html.SafeHtml.create('span').getDirection());
    +  assertNull(goog.html.SafeHtml.create('span', {'dir': 'x'}).getDirection());
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +      goog.html.SafeHtml.create('span', {'dir': 'ltr'}, 'a').getDirection());
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('script');
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('br', {}, 'x');
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('img', {'onerror': ''});
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('img', {'OnError': ''});
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('a', {'href': 'javascript:alert(1)'});
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('a href=""');
    +  });
    +
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('a', {'title="" href': ''});
    +  });
    +}
    +
    +
    +function testSafeHtmlCreate_styleAttribute() {
    +  var style = 'color:red;';
    +  var expected = '<hr style="' + style + '">';
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('hr', {'style': style});
    +  });
    +  assertSameHtml(expected, goog.html.SafeHtml.create('hr', {
    +    'style': goog.html.SafeStyle.fromConstant(goog.string.Const.from(style))
    +  }));
    +  assertSameHtml(expected, goog.html.SafeHtml.create('hr', {
    +    'style': {'color': 'red'}
    +  }));
    +}
    +
    +
    +function testSafeHtmlCreate_urlAttributes() {
    +  // TrustedResourceUrl is allowed.
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted'));
    +  assertSameHtml(
    +      '<img src="https://google.com/trusted">',
    +      goog.html.SafeHtml.create('img', {'src': trustedResourceUrl}));
    +  // SafeUrl is allowed.
    +  var safeUrl = goog.html.SafeUrl.sanitize('https://google.com/safe');
    +  assertSameHtml(
    +      '<imG src="https://google.com/safe">',
    +      goog.html.SafeHtml.create('imG', {'src': safeUrl}));
    +  // Const is allowed.
    +  var constUrl = goog.string.Const.from('https://google.com/const');
    +  assertSameHtml(
    +      '<a href="https://google.com/const"></a>',
    +      goog.html.SafeHtml.create('a', {'href': constUrl}));
    +
    +  // string is not allowed.
    +  assertThrows(function() {
    +    goog.html.SafeHtml.create('imG', {'src': 'https://google.com'});
    +  });
    +}
    +
    +
    +function testSafeHtmlCreateIframe() {
    +  // Setting src and srcdoc.
    +  var url = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted<'));
    +  assertSameHtml(
    +      '<iframe src="https://google.com/trusted&lt;"></iframe>',
    +      goog.html.SafeHtml.createIframe(url, null, {'sandbox': null}));
    +  var srcdoc = goog.html.SafeHtml.create('br');
    +  assertSameHtml(
    +      '<iframe srcdoc="&lt;br&gt;"></iframe>',
    +      goog.html.SafeHtml.createIframe(null, srcdoc, {'sandbox': null}));
    +
    +  // sandbox default and overriding it.
    +  assertSameHtml(
    +      '<iframe sandbox=""></iframe>',
    +      goog.html.SafeHtml.createIframe());
    +  assertSameHtml(
    +      '<iframe Sandbox="allow-same-origin allow-top-navigation"></iframe>',
    +      goog.html.SafeHtml.createIframe(
    +          null, null, {'Sandbox': 'allow-same-origin allow-top-navigation'}));
    +
    +  // Cannot override src and srddoc.
    +  assertThrows(function() {
    +    goog.html.SafeHtml.createIframe(null, null, {'Src': url});
    +  });
    +  assertThrows(function() {
    +    goog.html.SafeHtml.createIframe(null, null, {'Srcdoc': url});
    +  });
    +
    +  // Can set content.
    +  assertSameHtml(
    +      '<iframe>&lt;</iframe>',
    +      goog.html.SafeHtml.createIframe(null, null, {'sandbox': null}, '<'));
    +}
    +
    +
    +function testSafeHtmlCreateStyle() {
    +  var styleSheet = goog.html.SafeStyleSheet.fromConstant(
    +      goog.string.Const.from('P.special { color:"red" ; }'));
    +  var styleHtml = goog.html.SafeHtml.createStyle(styleSheet);
    +  assertSameHtml(
    +      '<style type="text/css">P.special { color:"red" ; }</style>', styleHtml);
    +
    +  // Two stylesheets.
    +  var otherStyleSheet = goog.html.SafeStyleSheet.fromConstant(
    +      goog.string.Const.from('P.regular { color:blue ; }'));
    +  styleHtml = goog.html.SafeHtml.createStyle([styleSheet, otherStyleSheet]);
    +  assertSameHtml(
    +      '<style type="text/css">P.special { color:"red" ; }' +
    +          'P.regular { color:blue ; }</style>',
    +      styleHtml);
    +
    +  // Set attribute.
    +  styleHtml = goog.html.SafeHtml.createStyle(styleSheet, {'id': 'test'});
    +  var styleHtmlString = goog.html.SafeHtml.unwrap(styleHtml);
    +  assertTrue(styleHtmlString, styleHtmlString.indexOf('id="test"') != -1);
    +  assertTrue(styleHtmlString, styleHtmlString.indexOf('type="text/css"') != -1);
    +
    +  // Set attribute to null.
    +  styleHtml = goog.html.SafeHtml.createStyle(
    +      goog.html.SafeStyleSheet.EMPTY, {'id': null});
    +  assertSameHtml('<style type="text/css"></style>', styleHtml);
    +
    +  // Set attribute to invalid value.
    +  assertThrows(function() {
    +    styleHtml = goog.html.SafeHtml.createStyle(
    +        goog.html.SafeStyleSheet.EMPTY, {'invalid.': 'cantdothis'});
    +  });
    +
    +  // Cannot override type attribute.
    +  assertThrows(function() {
    +    styleHtml = goog.html.SafeHtml.createStyle(
    +        goog.html.SafeStyleSheet.EMPTY, {'Type': 'cantdothis'});
    +  });
    +
    +  // Directionality.
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL, styleHtml.getDirection());
    +}
    +
    +
    +function testSafeHtmlCreateWithDir() {
    +  var ltr = goog.i18n.bidi.Dir.LTR;
    +
    +  assertEquals(ltr, goog.html.SafeHtml.createWithDir(ltr, 'br').getDirection());
    +}
    +
    +
    +function testSafeHtmlConcat() {
    +  var br = goog.html.testing.newSafeHtmlForTest('<br>');
    +
    +  var html = goog.html.SafeHtml.htmlEscape('Hello');
    +  assertSameHtml('Hello<br>', goog.html.SafeHtml.concat(html, br));
    +
    +  assertSameHtml('', goog.html.SafeHtml.concat());
    +  assertSameHtml('', goog.html.SafeHtml.concat([]));
    +
    +  assertSameHtml('a<br>c', goog.html.SafeHtml.concat('a', br, 'c'));
    +  assertSameHtml('a<br>c', goog.html.SafeHtml.concat(['a', br, 'c']));
    +  assertSameHtml('a<br>c', goog.html.SafeHtml.concat('a', [br, 'c']));
    +  assertSameHtml('a<br>c', goog.html.SafeHtml.concat(['a'], br, ['c']));
    +
    +  var ltr = goog.html.testing.newSafeHtmlForTest('', goog.i18n.bidi.Dir.LTR);
    +  var rtl = goog.html.testing.newSafeHtmlForTest('', goog.i18n.bidi.Dir.RTL);
    +  var neutral = goog.html.testing.newSafeHtmlForTest('',
    +      goog.i18n.bidi.Dir.NEUTRAL);
    +  var unknown = goog.html.testing.newSafeHtmlForTest('');
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +      goog.html.SafeHtml.concat().getDirection());
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +      goog.html.SafeHtml.concat(ltr, ltr).getDirection());
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +      goog.html.SafeHtml.concat(ltr, neutral, ltr).getDirection());
    +  assertNull(goog.html.SafeHtml.concat(ltr, unknown).getDirection());
    +  assertNull(goog.html.SafeHtml.concat(ltr, rtl).getDirection());
    +  assertNull(goog.html.SafeHtml.concat(ltr, [rtl]).getDirection());
    +}
    +
    +
    +function testHtmlEscapePreservingNewlines() {
    +  // goog.html.SafeHtml passes through unchanged.
    +  var safeHtmlIn = goog.html.SafeHtml.htmlEscapePreservingNewlines('<b>in</b>');
    +  assertTrue(safeHtmlIn ===
    +      goog.html.SafeHtml.htmlEscapePreservingNewlines(safeHtmlIn));
    +
    +  assertSameHtml('a<br>c',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlines('a\nc'));
    +  assertSameHtml('&lt;<br>',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlines('<\n'));
    +  assertSameHtml('<br>',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlines('\r\n'));
    +  assertSameHtml('<br>', goog.html.SafeHtml.htmlEscapePreservingNewlines('\r'));
    +  assertSameHtml('', goog.html.SafeHtml.htmlEscapePreservingNewlines(''));
    +}
    +
    +
    +function testHtmlEscapePreservingNewlinesAndSpaces() {
    +  // goog.html.SafeHtml passes through unchanged.
    +  var safeHtmlIn = goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(
    +      '<b>in</b>');
    +  assertTrue(safeHtmlIn ===
    +      goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(safeHtmlIn));
    +
    +  assertSameHtml('a<br>c',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces('a\nc'));
    +  assertSameHtml('&lt;<br>',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces('<\n'));
    +  assertSameHtml(
    +      '<br>', goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces('\r\n'));
    +  assertSameHtml(
    +      '<br>', goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces('\r'));
    +  assertSameHtml(
    +      '', goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(''));
    +
    +  assertSameHtml('a &#160;b',
    +      goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces('a  b'));
    +}
    +
    +
    +function testSafeHtmlConcatWithDir() {
    +  var ltr = goog.i18n.bidi.Dir.LTR;
    +  var rtl = goog.i18n.bidi.Dir.RTL;
    +  var br = goog.html.testing.newSafeHtmlForTest('<br>');
    +
    +  assertEquals(ltr, goog.html.SafeHtml.concatWithDir(ltr).getDirection());
    +  assertEquals(ltr, goog.html.SafeHtml.concatWithDir(ltr,
    +      goog.html.testing.newSafeHtmlForTest('', rtl)).getDirection());
    +
    +  assertSameHtml('a<br>c', goog.html.SafeHtml.concatWithDir(ltr, 'a', br, 'c'));
    +}
    +
    +
    +function assertSameHtml(expected, html) {
    +  assertEquals(expected, goog.html.SafeHtml.unwrap(html));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safescript.js b/mapbox-gl/closure-library/closure/goog/html/safescript.js
    new file mode 100644
    index 0000000000..2b3423cda2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safescript.js
    @@ -0,0 +1,220 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The SafeScript type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.SafeScript');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A string-like object which represents JavaScript code and that carries the
    + * security type contract that its value, as a string, will not cause execution
    + * of unconstrained attacker controlled code (XSS) when evaluated as JavaScript
    + * in a browser.
    + *
    + * Instances of this type must be created via the factory method
    + * {@code goog.html.SafeScript.fromConstant} and not by invoking its
    + * constructor. The constructor intentionally takes no parameters and the type
    + * is immutable; hence only a default instance corresponding to the empty string
    + * can be obtained via constructor invocation.
    + *
    + * A SafeScript's string representation can safely be interpolated as the
    + * content of a script element within HTML. The SafeScript string should not be
    + * escaped before interpolation.
    + *
    + * Note that the SafeScript might contain text that is attacker-controlled but
    + * that text should have been interpolated with appropriate escaping,
    + * sanitization and/or validation into the right location in the script, such
    + * that it is highly constrained in its effect (for example, it had to match a
    + * set of whitelisted words).
    + *
    + * A SafeScript can be constructed via security-reviewed unchecked
    + * conversions. In this case producers of SafeScript must ensure themselves that
    + * the SafeScript does not contain unsafe script. Note in particular that
    + * {@code &lt;} is dangerous, even when inside JavaScript strings, and so should
    + * always be forbidden or JavaScript escaped in user controlled input. For
    + * example, if {@code &lt;/script&gt;&lt;script&gt;evil&lt;/script&gt;"} were
    + * interpolated inside a JavaScript string, it would break out of the context
    + * of the original script element and {@code evil} would execute. Also note
    + * that within an HTML script (raw text) element, HTML character references,
    + * such as "&lt;" are not allowed. See
    + * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements.
    + *
    + * @see goog.html.SafeScript#fromConstant
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.SafeScript = function() {
    +  /**
    +   * The contained value of this SafeScript.  The field has a purposely
    +   * ugly name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.SafeScript#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeScript.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Type marker for the SafeScript type, used to implement additional
    + * run-time type checking.
    + * @const
    + * @private
    + */
    +goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Creates a SafeScript object from a compile-time constant string.
    + *
    + * @param {!goog.string.Const} script A compile-time-constant string from which
    + *     to create a SafeScript.
    + * @return {!goog.html.SafeScript} A SafeScript object initialized to
    + *     {@code script}.
    + */
    +goog.html.SafeScript.fromConstant = function(script) {
    +  var scriptString = goog.string.Const.unwrap(script);
    +  if (scriptString.length === 0) {
    +    return goog.html.SafeScript.EMPTY;
    +  }
    +  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
    +      scriptString);
    +};
    +
    +
    +/**
    + * Returns this SafeScript's value as a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code SafeScript}, use {@code goog.html.SafeScript.unwrap} instead of
    + * this method. If in doubt, assume that it's security relevant. In particular,
    + * note that goog.html functions which return a goog.html type do not guarantee
    + * the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
    + * // instanceof goog.html.SafeHtml.
    + * </pre>
    + *
    + * @see goog.html.SafeScript#unwrap
    + * @override
    + */
    +goog.html.SafeScript.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseSafeScriptWrappedValue_;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a SafeScript, use
    +   * {@code goog.html.SafeScript.unwrap}.
    +   *
    +   * @see goog.html.SafeScript#unwrap
    +   * @override
    +   */
    +  goog.html.SafeScript.prototype.toString = function() {
    +    return 'SafeScript{' +
    +        this.privateDoNotAccessOrElseSafeScriptWrappedValue_ + '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a
    + * SafeScript object, and returns its value.
    + *
    + * @param {!goog.html.SafeScript} safeScript The object to extract from.
    + * @return {string} The safeScript object's contained string, unless
    + *     the run-time type check fails. In that case, {@code unwrap} returns an
    + *     innocuous string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.SafeScript.unwrap = function(safeScript) {
    +  // Perform additional Run-time type-checking to ensure that
    +  // safeScript is indeed an instance of the expected type.  This
    +  // provides some additional protection against security bugs due to
    +  // application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (safeScript instanceof goog.html.SafeScript &&
    +      safeScript.constructor === goog.html.SafeScript &&
    +      safeScript.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +          goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_;
    +  } else {
    +    goog.asserts.fail(
    +        'expected object of type SafeScript, got \'' + safeScript + '\'');
    +    return 'type_error:SafeScript';
    +  }
    +};
    +
    +
    +/**
    + * Package-internal utility method to create SafeScript instances.
    + *
    + * @param {string} script The string to initialize the SafeScript object with.
    + * @return {!goog.html.SafeScript} The initialized SafeScript object.
    + * @package
    + */
    +goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse =
    +    function(script) {
    +  var safeScript = new goog.html.SafeScript();
    +  safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_ = script;
    +  return safeScript;
    +};
    +
    +
    +/**
    + * A SafeScript instance corresponding to the empty string.
    + * @const {!goog.html.SafeScript}
    + */
    +goog.html.SafeScript.EMPTY =
    +    goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse('');
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safescript_test.html b/mapbox-gl/closure-library/closure/goog/html/safescript_test.html
    new file mode 100644
    index 0000000000..f8a7df60b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safescript_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.safeScriptTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safescript_test.js b/mapbox-gl/closure-library/closure/goog/html/safescript_test.js
    new file mode 100644
    index 0000000000..0441aa62f7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safescript_test.js
    @@ -0,0 +1,66 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.SafeScript and its builders.
    + */
    +
    +goog.provide('goog.html.safeScriptTest');
    +
    +goog.require('goog.html.SafeScript');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.safeScriptTest');
    +
    +
    +function testSafeScript() {
    +  var script = 'var string = \'hello\';';
    +  var safeScript =
    +      goog.html.SafeScript.fromConstant(goog.string.Const.from(script));
    +  var extracted = goog.html.SafeScript.unwrap(safeScript);
    +  assertEquals(script, extracted);
    +  assertEquals(script, safeScript.getTypedStringValue());
    +  assertEquals('SafeScript{' + script + '}', String(safeScript));
    +
    +  // Interface marker is present.
    +  assertTrue(safeScript.implementsGoogStringTypedString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.safeScriptValueWithSecurityContract__googHtmlSecurityPrivate_ =
    +      'var string = \'evil\';';
    +  evil.SAFE_STYLE_TYPE_MARKER__GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.SafeScript.unwrap(evil);
    +  });
    +  assertTrue(
    +      exception.message.indexOf('expected object of type SafeScript') > 0);
    +}
    +
    +
    +function testFromConstant_allowsEmptyString() {
    +  assertEquals(
    +      goog.html.SafeScript.EMPTY,
    +      goog.html.SafeScript.fromConstant(goog.string.Const.from('')));
    +}
    +
    +
    +function testEmpty() {
    +  assertEquals('', goog.html.SafeScript.unwrap(goog.html.SafeScript.EMPTY));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestyle.js b/mapbox-gl/closure-library/closure/goog/html/safestyle.js
    new file mode 100644
    index 0000000000..e8a89a8f16
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestyle.js
    @@ -0,0 +1,393 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The SafeStyle type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.SafeStyle');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A string-like object which represents a sequence of CSS declarations
    + * ({@code propertyName1: propertyvalue1; propertyName2: propertyValue2; ...})
    + * and that carries the security type contract that its value, as a string,
    + * will not cause untrusted script execution (XSS) when evaluated as CSS in a
    + * browser.
    + *
    + * Instances of this type must be created via the factory methods
    + * ({@code goog.html.SafeStyle.create} or
    + * {@code goog.html.SafeStyle.fromConstant}) and not by invoking its
    + * constructor. The constructor intentionally takes no parameters and the type
    + * is immutable; hence only a default instance corresponding to the empty string
    + * can be obtained via constructor invocation.
    + *
    + * A SafeStyle's string representation ({@link #getSafeStyleString()}) can
    + * safely:
    + * <ul>
    + *   <li>Be interpolated as the entire content of a *quoted* HTML style
    + *       attribute, or before already existing properties. The SafeStyle string
    + *       *must be HTML-attribute-escaped* (where " and ' are escaped) before
    + *       interpolation.
    + *   <li>Be interpolated as the entire content of a {}-wrapped block within a
    + *       stylesheet, or before already existing properties. The SafeStyle string
    + *       should not be escaped before interpolation. SafeStyle's contract also
    + *       guarantees that the string will not be able to introduce new properties
    + *       or elide existing ones.
    + *   <li>Be assigned to the style property of a DOM node. The SafeStyle string
    + *       should not be escaped before being assigned to the property.
    + * </ul>
    + *
    + * A SafeStyle may never contain literal angle brackets. Otherwise, it could
    + * be unsafe to place a SafeStyle into a &lt;style&gt; tag (where it can't
    + * be HTML escaped). For example, if the SafeStyle containing
    + * "{@code font: 'foo &lt;style/&gt;&lt;script&gt;evil&lt;/script&gt;'}" were
    + * interpolated within a &lt;style&gt; tag, this would then break out of the
    + * style context into HTML.
    + *
    + * A SafeStyle may contain literal single or double quotes, and as such the
    + * entire style string must be escaped when used in a style attribute (if
    + * this were not the case, the string could contain a matching quote that
    + * would escape from the style attribute).
    + *
    + * Values of this type must be composable, i.e. for any two values
    + * {@code style1} and {@code style2} of this type,
    + * {@code goog.html.SafeStyle.unwrap(style1) +
    + * goog.html.SafeStyle.unwrap(style2)} must itself be a value that satisfies
    + * the SafeStyle type constraint. This requirement implies that for any value
    + * {@code style} of this type, {@code goog.html.SafeStyle.unwrap(style)} must
    + * not end in a "property value" or "property name" context. For example,
    + * a value of {@code background:url("} or {@code font-} would not satisfy the
    + * SafeStyle contract. This is because concatenating such strings with a
    + * second value that itself does not contain unsafe CSS can result in an
    + * overall string that does. For example, if {@code javascript:evil())"} is
    + * appended to {@code background:url("}, the resulting string may result in
    + * the execution of a malicious script.
    + *
    + * TODO(user): Consider whether we should implement UTF-8 interchange
    + * validity checks and blacklisting of newlines (including Unicode ones) and
    + * other whitespace characters (\t, \f). Document here if so and also update
    + * SafeStyle.fromConstant().
    + *
    + * The following example values comply with this type's contract:
    + * <ul>
    + *   <li><pre>width: 1em;</pre>
    + *   <li><pre>height:1em;</pre>
    + *   <li><pre>width: 1em;height: 1em;</pre>
    + *   <li><pre>background:url('http://url');</pre>
    + * </ul>
    + * In addition, the empty string is safe for use in a CSS attribute.
    + *
    + * The following example values do NOT comply with this type's contract:
    + * <ul>
    + *   <li><pre>background: red</pre> (missing a trailing semi-colon)
    + *   <li><pre>background:</pre> (missing a value and a trailing semi-colon)
    + *   <li><pre>1em</pre> (missing an attribute name, which provides context for
    + *       the value)
    + * </ul>
    + *
    + * @see goog.html.SafeStyle#create
    + * @see goog.html.SafeStyle#fromConstant
    + * @see http://www.w3.org/TR/css3-syntax/
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.SafeStyle = function() {
    +  /**
    +   * The contained value of this SafeStyle.  The field has a purposely
    +   * ugly name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.SafeStyle#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeStyle.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Type marker for the SafeStyle type, used to implement additional
    + * run-time type checking.
    + * @const
    + * @private
    + */
    +goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Creates a SafeStyle object from a compile-time constant string.
    + *
    + * {@code style} should be in the format
    + * {@code name: value; [name: value; ...]} and must not have any < or >
    + * characters in it. This is so that SafeStyle's contract is preserved,
    + * allowing the SafeStyle to correctly be interpreted as a sequence of CSS
    + * declarations and without affecting the syntactic structure of any
    + * surrounding CSS and HTML.
    + *
    + * This method performs basic sanity checks on the format of {@code style}
    + * but does not constrain the format of {@code name} and {@code value}, except
    + * for disallowing tag characters.
    + *
    + * @param {!goog.string.Const} style A compile-time-constant string from which
    + *     to create a SafeStyle.
    + * @return {!goog.html.SafeStyle} A SafeStyle object initialized to
    + *     {@code style}.
    + */
    +goog.html.SafeStyle.fromConstant = function(style) {
    +  var styleString = goog.string.Const.unwrap(style);
    +  if (styleString.length === 0) {
    +    return goog.html.SafeStyle.EMPTY;
    +  }
    +  goog.html.SafeStyle.checkStyle_(styleString);
    +  goog.asserts.assert(goog.string.endsWith(styleString, ';'),
    +      'Last character of style string is not \';\': ' + styleString);
    +  goog.asserts.assert(goog.string.contains(styleString, ':'),
    +      'Style string must contain at least one \':\', to ' +
    +      'specify a "name: value" pair: ' + styleString);
    +  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    +      styleString);
    +};
    +
    +
    +/**
    + * Checks if the style definition is valid.
    + * @param {string} style
    + * @private
    + */
    +goog.html.SafeStyle.checkStyle_ = function(style) {
    +  goog.asserts.assert(!/[<>]/.test(style),
    +      'Forbidden characters in style string: ' + style);
    +};
    +
    +
    +/**
    + * Returns this SafeStyle's value as a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code SafeStyle}, use {@code goog.html.SafeStyle.unwrap} instead of
    + * this method. If in doubt, assume that it's security relevant. In particular,
    + * note that goog.html functions which return a goog.html type do not guarantee
    + * the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
    + * // instanceof goog.html.SafeHtml.
    + * </pre>
    + *
    + * @see goog.html.SafeStyle#unwrap
    + * @override
    + */
    +goog.html.SafeStyle.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseSafeStyleWrappedValue_;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a SafeStyle, use
    +   * {@code goog.html.SafeStyle.unwrap}.
    +   *
    +   * @see goog.html.SafeStyle#unwrap
    +   * @override
    +   */
    +  goog.html.SafeStyle.prototype.toString = function() {
    +    return 'SafeStyle{' +
    +        this.privateDoNotAccessOrElseSafeStyleWrappedValue_ + '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a
    + * SafeStyle object, and returns its value.
    + *
    + * @param {!goog.html.SafeStyle} safeStyle The object to extract from.
    + * @return {string} The safeStyle object's contained string, unless
    + *     the run-time type check fails. In that case, {@code unwrap} returns an
    + *     innocuous string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.SafeStyle.unwrap = function(safeStyle) {
    +  // Perform additional Run-time type-checking to ensure that
    +  // safeStyle is indeed an instance of the expected type.  This
    +  // provides some additional protection against security bugs due to
    +  // application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (safeStyle instanceof goog.html.SafeStyle &&
    +      safeStyle.constructor === goog.html.SafeStyle &&
    +      safeStyle.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +          goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_;
    +  } else {
    +    goog.asserts.fail(
    +        'expected object of type SafeStyle, got \'' + safeStyle + '\'');
    +    return 'type_error:SafeStyle';
    +  }
    +};
    +
    +
    +/**
    + * Package-internal utility method to create SafeStyle instances.
    + *
    + * @param {string} style The string to initialize the SafeStyle object with.
    + * @return {!goog.html.SafeStyle} The initialized SafeStyle object.
    + * @package
    + */
    +goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse =
    +    function(style) {
    +  var safeStyle = new goog.html.SafeStyle();
    +  safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_ = style;
    +  return safeStyle;
    +};
    +
    +
    +/**
    + * A SafeStyle instance corresponding to the empty string.
    + * @const {!goog.html.SafeStyle}
    + */
    +goog.html.SafeStyle.EMPTY =
    +    goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse('');
    +
    +
    +/**
    + * The innocuous string generated by goog.html.SafeUrl.create when passed
    + * an unsafe value.
    + * @const {string}
    + */
    +goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez';
    +
    +
    +/**
    + * Mapping of property names to their values.
    + * @typedef {!Object<string, goog.string.Const|string>}
    + */
    +goog.html.SafeStyle.PropertyMap;
    +
    +
    +/**
    + * Creates a new SafeStyle object from the properties specified in the map.
    + * @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to
    + *     their values, for example {'margin': '1px'}. Names must consist of
    + *     [-_a-zA-Z0-9]. Values might be strings consisting of [-.%_!# a-zA-Z0-9].
    + *     Other values must be wrapped in goog.string.Const. Null value causes
    + *     skipping the property.
    + * @return {!goog.html.SafeStyle}
    + * @throws {Error} If invalid name is provided.
    + * @throws {goog.asserts.AssertionError} If invalid value is provided. With
    + *     disabled assertions, invalid value is replaced by
    + *     goog.html.SafeStyle.INNOCUOUS_STRING.
    + */
    +goog.html.SafeStyle.create = function(map) {
    +  var style = '';
    +  for (var name in map) {
    +    if (!/^[-_a-zA-Z0-9]+$/.test(name)) {
    +      throw Error('Name allows only [-_a-zA-Z0-9], got: ' + name);
    +    }
    +    var value = map[name];
    +    if (value == null) {
    +      continue;
    +    }
    +    if (value instanceof goog.string.Const) {
    +      value = goog.string.Const.unwrap(value);
    +      // These characters can be used to change context and we don't want that
    +      // even with const values.
    +      goog.asserts.assert(!/[{;}]/.test(value), 'Value does not allow [{;}].');
    +    } else if (!goog.html.SafeStyle.VALUE_RE_.test(value)) {
    +      goog.asserts.fail(
    +          'String value allows only [-.%_!# a-zA-Z0-9], got: ' + value);
    +      value = goog.html.SafeStyle.INNOCUOUS_STRING;
    +    }
    +    style += name + ':' + value + ';';
    +  }
    +  if (!style) {
    +    return goog.html.SafeStyle.EMPTY;
    +  }
    +  goog.html.SafeStyle.checkStyle_(style);
    +  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    +      style);
    +};
    +
    +
    +// Keep in sync with the error string in create().
    +/**
    + * Regular expression for safe values.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.html.SafeStyle.VALUE_RE_ = /^[-.%_!# a-zA-Z0-9]+$/;
    +
    +
    +/**
    + * Creates a new SafeStyle object by concatenating the values.
    + * @param {...(!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>)} var_args
    + *     SafeStyles to concatenate.
    + * @return {!goog.html.SafeStyle}
    + */
    +goog.html.SafeStyle.concat = function(var_args) {
    +  var style = '';
    +
    +  /**
    +   * @param {!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>} argument
    +   */
    +  var addArgument = function(argument) {
    +    if (goog.isArray(argument)) {
    +      goog.array.forEach(argument, addArgument);
    +    } else {
    +      style += goog.html.SafeStyle.unwrap(argument);
    +    }
    +  };
    +
    +  goog.array.forEach(arguments, addArgument);
    +  if (!style) {
    +    return goog.html.SafeStyle.EMPTY;
    +  }
    +  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    +      style);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestyle_test.html b/mapbox-gl/closure-library/closure/goog/html/safestyle_test.html
    new file mode 100644
    index 0000000000..5c594499dc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestyle_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.safeStyleTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestyle_test.js b/mapbox-gl/closure-library/closure/goog/html/safestyle_test.js
    new file mode 100644
    index 0000000000..f51f71c980
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestyle_test.js
    @@ -0,0 +1,185 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.SafeStyle and its builders.
    + */
    +
    +goog.provide('goog.html.safeStyleTest');
    +
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.safeStyleTest');
    +
    +
    +function testSafeStyle() {
    +  var style = 'width: 1em;height: 1em;';
    +  var safeStyle =
    +      goog.html.SafeStyle.fromConstant(goog.string.Const.from(style));
    +  var extracted = goog.html.SafeStyle.unwrap(safeStyle);
    +  assertEquals(style, extracted);
    +  assertEquals(style, safeStyle.getTypedStringValue());
    +  assertEquals('SafeStyle{' + style + '}', String(safeStyle));
    +
    +  // Interface marker is present.
    +  assertTrue(safeStyle.implementsGoogStringTypedString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.safeStyleValueWithSecurityContract__googHtmlSecurityPrivate_ =
    +      'width: expression(evil);';
    +  evil.SAFE_STYLE_TYPE_MARKER__GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.SafeStyle.unwrap(evil);
    +  });
    +  assertTrue(
    +      exception.message.indexOf('expected object of type SafeStyle') > 0);
    +}
    +
    +
    +function testFromConstant_allowsEmptyString() {
    +  assertEquals(
    +      goog.html.SafeStyle.EMPTY,
    +      goog.html.SafeStyle.fromConstant(goog.string.Const.from('')));
    +}
    +
    +function testFromConstant_throwsOnForbiddenCharacters() {
    +  assertThrows(function() {
    +    goog.html.SafeStyle.fromConstant(goog.string.Const.from('width: x<;'));
    +  });
    +  assertThrows(function() {
    +    goog.html.SafeStyle.fromConstant(goog.string.Const.from('width: x>;'));
    +  });
    +}
    +
    +
    +function testFromConstant_throwsIfNoFinalSemicolon() {
    +  assertThrows(function() {
    +    goog.html.SafeStyle.fromConstant(goog.string.Const.from('width: 1em'));
    +  });
    +}
    +
    +
    +function testFromConstant_throwsIfNoColon() {
    +  assertThrows(function() {
    +    goog.html.SafeStyle.fromConstant(goog.string.Const.from('width= 1em;'));
    +  });
    +}
    +
    +
    +function testEmpty() {
    +  assertEquals('', goog.html.SafeStyle.unwrap(goog.html.SafeStyle.EMPTY));
    +}
    +
    +
    +function testCreate() {
    +  var style = goog.html.SafeStyle.create({
    +    'background': goog.string.Const.from('url(i.png)'),
    +    'margin': '0'
    +  });
    +  assertEquals('background:url(i.png);margin:0;',
    +      goog.html.SafeStyle.unwrap(style));
    +}
    +
    +
    +function testCreate_allowsEmpty() {
    +  assertEquals(goog.html.SafeStyle.EMPTY, goog.html.SafeStyle.create({}));
    +}
    +
    +
    +function testCreate_skipsNull() {
    +  var style = goog.html.SafeStyle.create({'background': null});
    +  assertEquals(goog.html.SafeStyle.EMPTY, style);
    +}
    +
    +
    +function testCreate_allowsLengths() {
    +  var style = goog.html.SafeStyle.create({'padding': '0 1px .2% 3.4em'});
    +  assertEquals('padding:0 1px .2% 3.4em;', goog.html.SafeStyle.unwrap(style));
    +}
    +
    +
    +function testCreate_throwsOnForbiddenCharacters() {
    +  assertThrows(function() {
    +    goog.html.SafeStyle.create({'<': '0'});
    +  });
    +  assertThrows(function() {
    +    goog.html.SafeStyle.create({'color': goog.string.Const.from('<')});
    +  });
    +}
    +
    +
    +function testCreate_values() {
    +  var valids = [
    +    '0',
    +    '0 0',
    +    '1px',
    +    '100%',
    +    '2.3px',
    +    '.1em',
    +    'red',
    +    '#f00',
    +    'red !important'
    +  ];
    +  for (var i = 0; i < valids.length; i++) {
    +    var value = valids[i];
    +    assertEquals('background:' + value + ';', goog.html.SafeStyle.unwrap(
    +        goog.html.SafeStyle.create({'background': value})));
    +  }
    +
    +  var invalids = [
    +    '',
    +    'expression(alert(1))',
    +    'url(i.png)',
    +    goog.string.Const.from('red;')
    +  ];
    +  for (var i = 0; i < invalids.length; i++) {
    +    var value = invalids[i];
    +    assertThrows(function() {
    +      goog.html.SafeStyle.create({'background': value});
    +    });
    +  }
    +}
    +
    +
    +function testConcat() {
    +  var width = goog.html.SafeStyle.fromConstant(
    +      goog.string.Const.from('width: 1em;'));
    +  var margin = goog.html.SafeStyle.create({'margin': '0'});
    +  var padding = goog.html.SafeStyle.create({'padding': '0'});
    +
    +  var style = goog.html.SafeStyle.concat(width, margin);
    +  assertEquals('width: 1em;margin:0;', goog.html.SafeStyle.unwrap(style));
    +
    +  style = goog.html.SafeStyle.concat([width, margin]);
    +  assertEquals('width: 1em;margin:0;', goog.html.SafeStyle.unwrap(style));
    +
    +  style = goog.html.SafeStyle.concat([width], [padding, margin]);
    +  assertEquals('width: 1em;padding:0;margin:0;',
    +      goog.html.SafeStyle.unwrap(style));
    +}
    +
    +
    +function testConcat_allowsEmpty() {
    +  var empty = goog.html.SafeStyle.EMPTY;
    +  assertEquals(empty, goog.html.SafeStyle.concat());
    +  assertEquals(empty, goog.html.SafeStyle.concat([]));
    +  assertEquals(empty, goog.html.SafeStyle.concat(empty));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestylesheet.js b/mapbox-gl/closure-library/closure/goog/html/safestylesheet.js
    new file mode 100644
    index 0000000000..5325d1e743
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestylesheet.js
    @@ -0,0 +1,263 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The SafeStyleSheet type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.SafeStyleSheet');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A string-like object which represents a CSS style sheet and that carries the
    + * security type contract that its value, as a string, will not cause untrusted
    + * script execution (XSS) when evaluated as CSS in a browser.
    + *
    + * Instances of this type must be created via the factory method
    + * {@code goog.html.SafeStyleSheet.fromConstant} and not by invoking its
    + * constructor. The constructor intentionally takes no parameters and the type
    + * is immutable; hence only a default instance corresponding to the empty string
    + * can be obtained via constructor invocation.
    + *
    + * A SafeStyleSheet's string representation can safely be interpolated as the
    + * content of a style element within HTML. The SafeStyleSheet string should
    + * not be escaped before interpolation.
    + *
    + * Values of this type must be composable, i.e. for any two values
    + * {@code styleSheet1} and {@code styleSheet2} of this type,
    + * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +
    + * goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that
    + * satisfies the SafeStyleSheet type constraint. This requirement implies that
    + * for any value {@code styleSheet} of this type,
    + * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1)} must end in
    + * "beginning of rule" context.
    +
    + * A SafeStyleSheet can be constructed via security-reviewed unchecked
    + * conversions. In this case producers of SafeStyleSheet must ensure themselves
    + * that the SafeStyleSheet does not contain unsafe script. Note in particular
    + * that {@code &lt;} is dangerous, even when inside CSS strings, and so should
    + * always be forbidden or CSS-escaped in user controlled input. For example, if
    + * {@code &lt;/style&gt;&lt;script&gt;evil&lt;/script&gt;"} were interpolated
    + * inside a CSS string, it would break out of the context of the original
    + * style element and {@code evil} would execute. Also note that within an HTML
    + * style (raw text) element, HTML character references, such as
    + * {@code &amp;lt;}, are not allowed. See
    + * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
    + * (similar considerations apply to the style element).
    + *
    + * @see goog.html.SafeStyleSheet#fromConstant
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.SafeStyleSheet = function() {
    +  /**
    +   * The contained value of this SafeStyleSheet.  The field has a purposely
    +   * ugly name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.SafeStyleSheet#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Type marker for the SafeStyleSheet type, used to implement additional
    + * run-time type checking.
    + * @const
    + * @private
    + */
    +goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Creates a new SafeStyleSheet object by concatenating values.
    + * @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}
    + *     var_args Values to concatenate.
    + * @return {!goog.html.SafeStyleSheet}
    + */
    +goog.html.SafeStyleSheet.concat = function(var_args) {
    +  var result = '';
    +
    +  /**
    +   * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
    +   *     argument
    +   */
    +  var addArgument = function(argument) {
    +    if (goog.isArray(argument)) {
    +      goog.array.forEach(argument, addArgument);
    +    } else {
    +      result += goog.html.SafeStyleSheet.unwrap(argument);
    +    }
    +  };
    +
    +  goog.array.forEach(arguments, addArgument);
    +  return goog.html.SafeStyleSheet
    +      .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);
    +};
    +
    +
    +/**
    + * Creates a SafeStyleSheet object from a compile-time constant string.
    + *
    + * {@code styleSheet} must not have any &lt; characters in it, so that
    + * the syntactic structure of the surrounding HTML is not affected.
    + *
    + * @param {!goog.string.Const} styleSheet A compile-time-constant string from
    + *     which to create a SafeStyleSheet.
    + * @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to
    + *     {@code styleSheet}.
    + */
    +goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {
    +  var styleSheetString = goog.string.Const.unwrap(styleSheet);
    +  if (styleSheetString.length === 0) {
    +    return goog.html.SafeStyleSheet.EMPTY;
    +  }
    +  // > is a valid character in CSS selectors and there's no strict need to
    +  // block it if we already block <.
    +  goog.asserts.assert(!goog.string.contains(styleSheetString, '<'),
    +      "Forbidden '<' character in style sheet string: " + styleSheetString);
    +  return goog.html.SafeStyleSheet.
    +      createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);
    +};
    +
    +
    +/**
    + * Returns this SafeStyleSheet's value as a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code SafeStyleSheet}, use {@code goog.html.SafeStyleSheet.unwrap}
    + * instead of this method. If in doubt, assume that it's security relevant. In
    + * particular, note that goog.html functions which return a goog.html type do
    + * not guarantee the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
    + * // instanceof goog.html.SafeHtml.
    + * </pre>
    + *
    + * @see goog.html.SafeStyleSheet#unwrap
    + * @override
    + */
    +goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a SafeStyleSheet, use
    +   * {@code goog.html.SafeStyleSheet.unwrap}.
    +   *
    +   * @see goog.html.SafeStyleSheet#unwrap
    +   * @override
    +   */
    +  goog.html.SafeStyleSheet.prototype.toString = function() {
    +    return 'SafeStyleSheet{' +
    +        this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a
    + * SafeStyleSheet object, and returns its value.
    + *
    + * @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.
    + * @return {string} The safeStyleSheet object's contained string, unless
    + *     the run-time type check fails. In that case, {@code unwrap} returns an
    + *     innocuous string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {
    +  // Perform additional Run-time type-checking to ensure that
    +  // safeStyleSheet is indeed an instance of the expected type.  This
    +  // provides some additional protection against security bugs due to
    +  // application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&
    +      safeStyleSheet.constructor === goog.html.SafeStyleSheet &&
    +      safeStyleSheet.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +          goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
    +  } else {
    +    goog.asserts.fail(
    +        "expected object of type SafeStyleSheet, got '" + safeStyleSheet +
    +        "'");
    +    return 'type_error:SafeStyleSheet';
    +  }
    +};
    +
    +
    +/**
    + * Package-internal utility method to create SafeStyleSheet instances.
    + *
    + * @param {string} styleSheet The string to initialize the SafeStyleSheet
    + *     object with.
    + * @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.
    + * @package
    + */
    +goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =
    +    function(styleSheet) {
    +  var safeStyleSheet = new goog.html.SafeStyleSheet();
    +  safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ =
    +      styleSheet;
    +  return safeStyleSheet;
    +};
    +
    +
    +/**
    + * A SafeStyleSheet instance corresponding to the empty string.
    + * @const {!goog.html.SafeStyleSheet}
    + */
    +goog.html.SafeStyleSheet.EMPTY =
    +    goog.html.SafeStyleSheet.
    +        createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.html b/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.html
    new file mode 100644
    index 0000000000..1eee7f33cb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.safeStyleSheetTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.js b/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.js
    new file mode 100644
    index 0000000000..084f91d8f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safestylesheet_test.js
    @@ -0,0 +1,97 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.SafeStyleSheet and its builders.
    + */
    +
    +goog.provide('goog.html.safeStyleSheetTest');
    +
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.safeStyleSheetTest');
    +
    +
    +function testSafeStyleSheet() {
    +  var styleSheet = 'P.special { color:red ; }';
    +  var safeStyleSheet =
    +      goog.html.SafeStyleSheet.fromConstant(goog.string.Const.from(styleSheet));
    +  var extracted = goog.html.SafeStyleSheet.unwrap(safeStyleSheet);
    +  assertEquals(styleSheet, extracted);
    +  assertEquals(styleSheet, safeStyleSheet.getTypedStringValue());
    +  assertEquals('SafeStyleSheet{' + styleSheet + '}', String(safeStyleSheet));
    +
    +  // Interface marker is present.
    +  assertTrue(safeStyleSheet.implementsGoogStringTypedString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.safeStyleSheetValueWithSecurityContract__googHtmlSecurityPrivate_ =
    +      'P.special { color:expression(evil) ; }';
    +  evil.SAFE_STYLE_TYPE_MARKER__GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.SafeStyleSheet.unwrap(evil);
    +  });
    +  assertTrue(goog.string.contains(
    +      exception.message,
    +      'expected object of type SafeStyleSheet'));
    +}
    +
    +
    +function testFromConstant_allowsEmptyString() {
    +  assertEquals(
    +      goog.html.SafeStyleSheet.EMPTY,
    +      goog.html.SafeStyleSheet.fromConstant(goog.string.Const.from('')));
    +}
    +
    +
    +function testFromConstant_throwsOnLessThanCharacter() {
    +  assertThrows(function() {
    +    goog.html.SafeStyleSheet.fromConstant(goog.string.Const.from('x<x'));
    +  });
    +}
    +
    +
    +function testConcat() {
    +  var styleSheet1 = goog.html.SafeStyleSheet.fromConstant(
    +      goog.string.Const.from('P.special { color:red ; }'));
    +  var styleSheet2 = goog.html.SafeStyleSheet.fromConstant(
    +      goog.string.Const.from('P.regular { color:blue ; }'));
    +  var expected = 'P.special { color:red ; }P.special { color:red ; }' +
    +      'P.regular { color:blue ; }P.regular { color:blue ; }';
    +
    +  var concatStyleSheet = goog.html.SafeStyleSheet.concat(
    +      styleSheet1, [styleSheet1, styleSheet2], styleSheet2);
    +  assertEquals(
    +      expected, goog.html.SafeStyleSheet.unwrap(concatStyleSheet));
    +
    +  // Empty.
    +  concatStyleSheet = goog.html.SafeStyleSheet.concat();
    +  assertEquals('', goog.html.SafeStyleSheet.unwrap(concatStyleSheet));
    +  concatStyleSheet = goog.html.SafeStyleSheet.concat([]);
    +  assertEquals('', goog.html.SafeStyleSheet.unwrap(concatStyleSheet));
    +}
    +
    +
    +function testEmpty() {
    +  assertEquals(
    +      '', goog.html.SafeStyleSheet.unwrap(goog.html.SafeStyleSheet.EMPTY));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safeurl.js b/mapbox-gl/closure-library/closure/goog/html/safeurl.js
    new file mode 100644
    index 0000000000..87385854a7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safeurl.js
    @@ -0,0 +1,402 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The SafeUrl type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.SafeUrl');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.i18n.bidi.DirectionalString');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A string that is safe to use in URL context in DOM APIs and HTML documents.
    + *
    + * A SafeUrl is a string-like object that carries the security type contract
    + * that its value as a string will not cause untrusted script execution
    + * when evaluated as a hyperlink URL in a browser.
    + *
    + * Values of this type are guaranteed to be safe to use in URL/hyperlink
    + * contexts, such as, assignment to URL-valued DOM properties, or
    + * interpolation into a HTML template in URL context (e.g., inside a href
    + * attribute), in the sense that the use will not result in a
    + * Cross-Site-Scripting vulnerability.
    + *
    + * Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's
    + * contract does not guarantee that instances are safe to interpolate into HTML
    + * without appropriate escaping.
    + *
    + * Note also that this type's contract does not imply any guarantees regarding
    + * the resource the URL refers to.  In particular, SafeUrls are <b>not</b>
    + * safe to use in a context where the referred-to resource is interpreted as
    + * trusted code, e.g., as the src of a script tag.
    + *
    + * Instances of this type must be created via the factory methods
    + * ({@code goog.html.SafeUrl.fromConstant}, {@code goog.html.SafeUrl.sanitize}),
    + * etc and not by invoking its constructor.  The constructor intentionally
    + * takes no parameters and the type is immutable; hence only a default instance
    + * corresponding to the empty string can be obtained via constructor invocation.
    + *
    + * @see goog.html.SafeUrl#fromConstant
    + * @see goog.html.SafeUrl#from
    + * @see goog.html.SafeUrl#sanitize
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.i18n.bidi.DirectionalString}
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.SafeUrl = function() {
    +  /**
    +   * The contained value of this SafeUrl.  The field has a purposely ugly
    +   * name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.SafeUrl#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +};
    +
    +
    +/**
    + * The innocuous string generated by goog.html.SafeUrl.sanitize when passed
    + * an unsafe URL.
    + *
    + * about:invalid is registered in
    + * http://www.w3.org/TR/css3-values/#about-invalid.
    + * http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
    + * contain a fragment, which is not to be considered when determining if an
    + * about URL is well-known.
    + *
    + * Using about:invalid seems preferable to using a fixed data URL, since
    + * browsers might choose to not report CSP violations on it, as legitimate
    + * CSS function calls to attr() can result in this URL being produced. It is
    + * also a standard URL which matches exactly the semantics we need:
    + * "The about:invalid URI references a non-existent document with a generic
    + * error condition. It can be used when a URI is necessary, but the default
    + * value shouldn't be resolveable as any type of document".
    + *
    + * @const {string}
    + */
    +goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Returns this SafeUrl's value a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code SafeUrl}, use {@code goog.html.SafeUrl.unwrap} instead of this
    + * method. If in doubt, assume that it's security relevant. In particular, note
    + * that goog.html functions which return a goog.html type do not guarantee that
    + * the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
    + * // goog.html.SafeHtml.
    + * </pre>
    + *
    + * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
    + * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
    + * be appropriately escaped before embedding in a HTML document. Note that the
    + * required escaping is context-sensitive (e.g. a different escaping is
    + * required for embedding a URL in a style property within a style
    + * attribute, as opposed to embedding in a href attribute).
    + *
    + * @see goog.html.SafeUrl#unwrap
    + * @override
    + */
    +goog.html.SafeUrl.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;
    +
    +
    +/**
    + * Returns this URLs directionality, which is always {@code LTR}.
    + * @override
    + */
    +goog.html.SafeUrl.prototype.getDirection = function() {
    +  return goog.i18n.bidi.Dir.LTR;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a SafeUrl, use
    +   * {@code goog.html.SafeUrl.unwrap}.
    +   *
    +   * @see goog.html.SafeUrl#unwrap
    +   * @override
    +   */
    +  goog.html.SafeUrl.prototype.toString = function() {
    +    return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
    +        '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a SafeUrl
    + * object, and returns its value.
    + *
    + * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
    + * behavior of  browsers when interpreting URLs. Values of SafeUrl objects MUST
    + * be appropriately escaped before embedding in a HTML document. Note that the
    + * required escaping is context-sensitive (e.g. a different escaping is
    + * required for embedding a URL in a style property within a style
    + * attribute, as opposed to embedding in a href attribute).
    + *
    + * Note that the returned value does not necessarily correspond to the string
    + * with which the SafeUrl was constructed, since goog.html.SafeUrl.sanitize
    + * will percent-encode many characters.
    + *
    + * @param {!goog.html.SafeUrl} safeUrl The object to extract from.
    + * @return {string} The SafeUrl object's contained string, unless the run-time
    + *     type check fails. In that case, {@code unwrap} returns an innocuous
    + *     string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.SafeUrl.unwrap = function(safeUrl) {
    +  // Perform additional Run-time type-checking to ensure that safeUrl is indeed
    +  // an instance of the expected type.  This provides some additional protection
    +  // against security bugs due to application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (safeUrl instanceof goog.html.SafeUrl &&
    +      safeUrl.constructor === goog.html.SafeUrl &&
    +      safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +          goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
    +  } else {
    +    goog.asserts.fail('expected object of type SafeUrl, got \'' +
    +                      safeUrl + '\'');
    +    return 'type_error:SafeUrl';
    +
    +  }
    +};
    +
    +
    +/**
    + * Creates a SafeUrl object from a compile-time constant string.
    + *
    + * Compile-time constant strings are inherently program-controlled and hence
    + * trusted.
    + *
    + * @param {!goog.string.Const} url A compile-time-constant string from which to
    + *         create a SafeUrl.
    + * @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
    + */
    +goog.html.SafeUrl.fromConstant = function(url) {
    +  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
    +      goog.string.Const.unwrap(url));
    +};
    +
    +
    +/**
    + * A pattern that recognizes a commonly useful subset of URLs that satisfy
    + * the SafeUrl contract.
    + *
    + * This regular expression matches a subset of URLs that will not cause script
    + * execution if used in URL context within a HTML document. Specifically, this
    + * regular expression matches if (comment from here on and regex copied from
    + * Soy's EscapingConventions):
    + * (1) Either a protocol in a whitelist (http, https, mailto).
    + * (2) or no protocol.  A protocol must be followed by a colon. The below
    + *     allows that by allowing colons only after one of the characters [/?#].
    + *     A colon after a hash (#) must be in the fragment.
    + *     Otherwise, a colon after a (?) must be in a query.
    + *     Otherwise, a colon after a single solidus (/) must be in a path.
    + *     Otherwise, a colon after a double solidus (//) must be in the authority
    + *     (before port).
    + *
    + * The pattern disallows &, used in HTML entity declarations before
    + * one of the characters in [/?#]. This disallows HTML entities used in the
    + * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
    + * It also disallows HTML entities in the first path part of a relative path,
    + * e.g. "foo&lt;bar/baz".  Our existing escaping functions should not produce
    + * that. More importantly, it disallows masking of a colon,
    + * e.g. "javascript&#58;...".
    + *
    + * @private
    + * @const {!RegExp}
    + */
    +goog.html.SAFE_URL_PATTERN_ = /^(?:(?:https?|mailto):|[^&:/?#]*(?:[/?#]|$))/i;
    +
    +
    +/**
    + * Creates a SafeUrl object from {@code url}. If {@code url} is a
    + * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
    + * validated to match a pattern of commonly used safe URLs. The string is
    + * converted to UTF-8 and non-whitelisted characters are percent-encoded. The
    + * string wrapped by the created SafeUrl will thus contain only ASCII printable
    + * characters.
    + *
    + * {@code url} may be a URL with the http, https, or mailto scheme,
    + * or a relative URL (i.e., a URL without a scheme; specifically, a
    + * scheme-relative, absolute-path-relative, or path-relative URL).
    + *
    + * {@code url} is converted to UTF-8 and non-whitelisted characters are
    + * percent-encoded. Whitelisted characters are '%' and, from RFC 3986,
    + * unreserved characters and reserved characters, with the exception of '\'',
    + * '(' and ')'. This ensures the the SafeUrl contains only ASCII-printable
    + * characters and reduces the chance of security bugs were it to be
    + * interpolated into a specific context without the necessary escaping.
    + *
    + * If {@code url} fails validation or does not UTF-16 decode correctly
    + * (JavaScript strings are UTF-16 encoded), this function returns a SafeUrl
    + * object containing an innocuous string, goog.html.SafeUrl.INNOCUOUS_STRING.
    + *
    + * @see http://url.spec.whatwg.org/#concept-relative-url
    + * @param {string|!goog.string.TypedString} url The URL to validate.
    + * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
    + */
    +goog.html.SafeUrl.sanitize = function(url) {
    +  if (url instanceof goog.html.SafeUrl) {
    +    return url;
    +  }
    +  else if (url.implementsGoogStringTypedString) {
    +    url = url.getTypedStringValue();
    +  } else {
    +    url = String(url);
    +  }
    +  if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
    +    url = goog.html.SafeUrl.INNOCUOUS_STRING;
    +  } else {
    +    url = goog.html.SafeUrl.normalize_(url);
    +  }
    +  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    +
    +
    +/**
    + * Normalizes {@code url} the UTF-8 encoding of url, using a whitelist of
    + * characters. Whitelisted characters are not percent-encoded.
    + * @param {string} url The URL to normalize.
    + * @return {string} The normalized URL.
    + * @private
    + */
    +goog.html.SafeUrl.normalize_ = function(url) {
    +  try {
    +    var normalized = encodeURI(url);
    +  } catch (e) {  // Happens if url contains invalid surrogate sequences.
    +    return goog.html.SafeUrl.INNOCUOUS_STRING;
    +  }
    +
    +  return normalized.replace(
    +      goog.html.SafeUrl.NORMALIZE_MATCHER_,
    +      function(match) {
    +        return goog.html.SafeUrl.NORMALIZE_REPLACER_MAP_[match];
    +      });
    +};
    +
    +
    +/**
    + * Matches characters and strings which need to be replaced in the string
    + * generated by encodeURI. Specifically:
    + *
    + * - '\'', '(' and ')' are not encoded. They are part of the reserved
    + *   characters group in RFC 3986 but only appear in the obsolete mark
    + *   production in Appendix D.2 of RFC 3986, so they can be encoded without
    + *   changing semantics.
    + * - '[' and ']' are encoded by encodeURI, despite being reserved characters
    + *   which can be used to represent IPv6 addresses. So they need to be decoded.
    + * - '%' is encoded by encodeURI. However, encoding '%' characters that are
    + *   already part of a valid percent-encoded sequence changes the semantics of a
    + *   URL, and hence we need to preserve them. Note that this may allow
    + *   non-encoded '%' characters to remain in the URL (i.e., occurrences of '%'
    + *   that are not part of a valid percent-encoded sequence, for example,
    + *   'ab%xy').
    + *
    + * @const {!RegExp}
    + * @private
    + */
    +goog.html.SafeUrl.NORMALIZE_MATCHER_ = /[()']|%5B|%5D|%25/g;
    +
    +
    +/**
    + * Map of replacements to be done in string generated by encodeURI.
    + * @const {!Object<string, string>}
    + * @private
    + */
    +goog.html.SafeUrl.NORMALIZE_REPLACER_MAP_ = {
    +  '\'': '%27',
    +  '(': '%28',
    +  ')': '%29',
    +  '%5B': '[',
    +  '%5D': ']',
    +  '%25': '%'
    +};
    +
    +
    +/**
    + * Type marker for the SafeUrl type, used to implement additional run-time
    + * type checking.
    + * @const
    + * @private
    + */
    +goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Package-internal utility method to create SafeUrl instances.
    + *
    + * @param {string} url The string to initialize the SafeUrl object with.
    + * @return {!goog.html.SafeUrl} The initialized SafeUrl object.
    + * @package
    + */
    +goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
    +    url) {
    +  var safeUrl = new goog.html.SafeUrl();
    +  safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
    +  return safeUrl;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safeurl_test.html b/mapbox-gl/closure-library/closure/goog/html/safeurl_test.html
    new file mode 100644
    index 0000000000..bc341ab5a9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safeurl_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.safeUrlTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/safeurl_test.js b/mapbox-gl/closure-library/closure/goog/html/safeurl_test.js
    new file mode 100644
    index 0000000000..6bca2f73de
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/safeurl_test.js
    @@ -0,0 +1,201 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.SafeUrl and its builders.
    + */
    +
    +goog.provide('goog.html.safeUrlTest');
    +
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.safeUrlTest');
    +
    +
    +
    +function testSafeUrl() {
    +  var safeUrl = goog.html.SafeUrl.fromConstant(
    +      goog.string.Const.from('javascript:trusted();'));
    +  var extracted = goog.html.SafeUrl.unwrap(safeUrl);
    +  assertEquals('javascript:trusted();', extracted);
    +  assertEquals('javascript:trusted();', goog.html.SafeUrl.unwrap(safeUrl));
    +  assertEquals('SafeUrl{javascript:trusted();}', String(safeUrl));
    +
    +  // URLs are always LTR.
    +  assertEquals(goog.i18n.bidi.Dir.LTR, safeUrl.getDirection());
    +
    +  // Interface markers are present.
    +  assertTrue(safeUrl.implementsGoogStringTypedString);
    +  assertTrue(safeUrl.implementsGoogI18nBidiDirectionalString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.safeUrlValueWithSecurityContract_googHtmlSecurityPrivate_ =
    +      '<script>evil()</script';
    +  evil.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.SafeUrl.unwrap(evil);
    +  });
    +  assertTrue(exception.message.indexOf('expected object of type SafeUrl') > 0);
    +}
    +
    +
    +/**
    + * Assert that url passes through sanitization unchanged.
    + * @param {string|!goog.string.TypedString} url The URL to sanitize.
    + */
    +function assertGoodUrl(url) {
    +  var expected = url;
    +  if (url.implementsGoogStringTypedString) {
    +    expected = url.getTypedStringValue();
    +  }
    +  var safeUrl = goog.html.SafeUrl.sanitize(url);
    +  var extracted = goog.html.SafeUrl.unwrap(safeUrl);
    +  assertEquals(expected, extracted);
    +}
    +
    +
    +/**
    + * Assert that url fails sanitization.
    + * @param {string|!goog.string.TypedString} url The URL to sanitize.
    + */
    +function assertBadUrl(url) {
    +  assertEquals(
    +      goog.html.SafeUrl.INNOCUOUS_STRING,
    +      goog.html.SafeUrl.unwrap(
    +          goog.html.SafeUrl.sanitize(url)));
    +}
    +
    +
    +function testSafeUrlSanitize_validatesUrl() {
    +  // Whitelisted schemes.
    +  assertGoodUrl('http://example.com/');
    +  assertGoodUrl('https://example.com');
    +  assertGoodUrl('mailto:foo@example.com');
    +  // Scheme is case-insensitive
    +  assertGoodUrl('HTtp://example.com/');
    +  // Different URL components go through.
    +  assertGoodUrl('https://example.com/path?foo=bar#baz');
    +  // Scheme-less URL with authority.
    +  assertGoodUrl('//example.com/path');
    +  // Absolute path with no authority.
    +  assertGoodUrl('/path');
    +  assertGoodUrl('/path?foo=bar#baz');
    +  // Relative path.
    +  assertGoodUrl('path');
    +  assertGoodUrl('path?foo=bar#baz');
    +  assertGoodUrl('p//ath');
    +  assertGoodUrl('p//ath?foo=bar#baz');
    +  // Restricted characters ('&', ':', \') after [/?#].
    +  assertGoodUrl('/&');
    +  assertGoodUrl('?:');
    +
    +  // .sanitize() works on program constants.
    +  assertGoodUrl(goog.string.Const.from('http://example.com/'));
    +
    +  // Non-whitelisted schemes.
    +  assertBadUrl('javascript:evil();');
    +  assertBadUrl('javascript:evil();//\nhttp://good.com/');
    +  assertBadUrl('data:blah');
    +  // Restricted characters before [/?#].
    +  assertBadUrl('&');
    +  assertBadUrl(':');
    +  // '\' is not treated like '/': no restricted characters allowed after it.
    +  assertBadUrl('\\:');
    +  // Regex anchored to the left: doesn't match on '/:'.
    +  assertBadUrl(':/:');
    +  // Regex multiline not enabled: first line would match but second one
    +  // wouldn't.
    +  assertBadUrl('path\n:');
    +
    +  // .sanitize() does not exempt values known to be program constants.
    +  assertBadUrl(goog.string.Const.from('data:blah'));
    +}
    +
    +
    +/**
    + * Asserts that goog.html.SafeUrl.unwrap returns the expected string when the
    + * SafeUrl has been constructed by passing the given url to
    + * goog.html.SafeUrl.sanitize.
    + * @param {string} url The string to pass to goog.html.SafeUrl.sanitize.
    + * @param {string} expected The string representation that
    + *         goog.html.SafeUrl.unwrap should return.
    + */
    +function assertSanitizeEncodesTo(url, expected) {
    +  var safeUrl = goog.html.SafeUrl.sanitize(url);
    +  var actual = goog.html.SafeUrl.unwrap(safeUrl);
    +  assertEquals(
    +      'SafeUrl.sanitize().unwrap() doesn\'t return expected ' +
    +          'percent-encoded string',
    +      expected,
    +      actual);
    +}
    +
    +
    +function testSafeUrlSanitize_percentEncodesUrl() {
    +  // '%' is preserved.
    +  assertSanitizeEncodesTo('%', '%');
    +  assertSanitizeEncodesTo('%2F', '%2F');
    +
    +  // Unreserved characters, RFC 3986.
    +  assertSanitizeEncodesTo('aA1-._~', 'aA1-._~');
    +
    +  // Reserved characters, RFC 3986. Only '\'', '(' and ')' are encoded.
    +  assertSanitizeEncodesTo('/:?#[]@!$&\'()*+,;=', '/:?#[]@!$&%27%28%29*+,;=');
    +
    +
    +  // Other ASCII characters, printable and non-printable.
    +  assertSanitizeEncodesTo('^"\\`\x00\n\r\x7f', '%5E%22%5C%60%00%0A%0D%7F');
    +
    +  // Codepoints which UTF-8 encode to 2 bytes.
    +  assertSanitizeEncodesTo('\u0080\u07ff', '%C2%80%DF%BF');
    +
    +  // Highest codepoint which can be UTF-16 encoded using two bytes
    +  // (one code unit). Highest codepoint in basic multilingual plane and highest
    +  // that JavaScript can represent using \u.
    +  assertSanitizeEncodesTo('\uffff', '%EF%BF%BF');
    +
    +  // Supplementary plane codepoint which UTF-16 and UTF-8 encode to 4 bytes.
    +  // Valid surrogate sequence.
    +  assertSanitizeEncodesTo('\ud800\udc00', '%F0%90%80%80');
    +
    +  // Invalid lead/high surrogate.
    +  assertSanitizeEncodesTo('\udc00', goog.html.SafeUrl.INNOCUOUS_STRING);
    +
    +  // Invalid trail/low surrogate.
    +  assertSanitizeEncodesTo('\ud800\ud800', goog.html.SafeUrl.INNOCUOUS_STRING);
    +}
    +
    +
    +function testSafeUrlSanitize_idempotentForSafeUrlArgument() {
    +  // This goes through percent-encoding.
    +  var safeUrl = goog.html.SafeUrl.sanitize('%11"');
    +  var safeUrl2 = goog.html.SafeUrl.sanitize(safeUrl);
    +  assertEquals(
    +      goog.html.SafeUrl.unwrap(safeUrl), goog.html.SafeUrl.unwrap(safeUrl2));
    +
    +  // This doesn't match the safe prefix, getting converted into an innocuous
    +  // string.
    +  safeUrl = goog.html.SafeUrl.sanitize('disallowed:foo');
    +  safeUrl2 = goog.html.SafeUrl.sanitize(safeUrl);
    +  assertEquals(
    +      goog.html.SafeUrl.unwrap(safeUrl), goog.html.SafeUrl.unwrap(safeUrl2));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/silverlight.js b/mapbox-gl/closure-library/closure/goog/html/silverlight.js
    new file mode 100644
    index 0000000000..6aed4e2aa3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/silverlight.js
    @@ -0,0 +1,92 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview SafeHtml factory methods for creating object tags for
    + * loading Silverlight files.
    + */
    +
    +goog.provide('goog.html.silverlight');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.flash');
    +goog.require('goog.string.Const');
    +
    +
    +/**
    + * Attributes and param tag name attributes not allowed to be overriden
    + * when calling createObjectForSilverlight().
    + *
    + * While values that should be specified as params are probably not
    + * recognized as attributes, we block them anyway just to be sure.
    + * @const {!Array<string>}
    + * @private
    + */
    +goog.html.silverlight.FORBIDDEN_ATTRS_AND_PARAMS_ON_SILVERLIGHT_ = [
    +  'data',  // Always set to a fixed value.
    +  'source',  // Specifies the URL for the Silverlight file.
    +  'type',  // Always set to a fixed value.
    +  'typemustmatch'  // Always set to a fixed value.
    +];
    +
    +
    +/**
    + * Creates a SafeHtml representing an object tag, for loading Silverlight files.
    + *
    + * The following attributes are set to these fixed values:
    + * - data: data:application/x-silverlight-2,
    + * - type: application/x-silverlight-2
    + * - typemustmatch: "" (the empty string, meaning true for a boolean attribute)
    + *
    + * @param {!goog.html.TrustedResourceUrl} source The value of the source param.
    + * @param {!Object<string, string>=} opt_params Mapping used to generate child
    + *     param tags. Each tag has a name and value attribute, as defined in
    + *     mapping. Only names consisting of [a-zA-Z0-9-] are allowed. Value of
    + *     null or undefined causes the param tag to be omitted.
    + * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
    + *     opt_attributes Mapping from other attribute names to their values. Only
    + *     attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
    + *     undefined causes the attribute to be omitted.
    + * @return {!goog.html.SafeHtml} The SafeHtml content with the object tag.
    + * @throws {Error} If invalid attribute or param name, or attribute or param
    + *     value is provided. Also if opt_attributes or opt_params contains any of
    + *     the attributes set to fixed values, documented above, or contains source.
    + *
    + */
    +goog.html.silverlight.createObject = function(
    +    source, opt_params, opt_attributes) {
    +  goog.html.flash.verifyKeysNotInMaps(
    +      goog.html.silverlight.FORBIDDEN_ATTRS_AND_PARAMS_ON_SILVERLIGHT_,
    +      opt_attributes,
    +      opt_params);
    +
    +  // We don't set default for Silverlight's EnableHtmlAccess and
    +  // AllowHtmlPopupwindow because their default changes depending on whether
    +  // a file loaded from the same domain.
    +  var paramTags = goog.html.flash.combineParams(
    +      {'source': source}, opt_params);
    +  var fixedAttributes = {
    +    'data': goog.html.TrustedResourceUrl.fromConstant(
    +        goog.string.Const.from('data:application/x-silverlight-2,')),
    +    'type': 'application/x-silverlight-2',
    +    'typemustmatch': ''
    +  };
    +  var attributes = goog.html.SafeHtml.combineAttributes(
    +      fixedAttributes, {}, opt_attributes);
    +
    +  return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
    +      'object', attributes, paramTags);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/silverlight_test.html b/mapbox-gl/closure-library/closure/goog/html/silverlight_test.html
    new file mode 100644
    index 0000000000..695fd32746
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/silverlight_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html.silverlight</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.silverlightTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/silverlight_test.js b/mapbox-gl/closure-library/closure/goog/html/silverlight_test.js
    new file mode 100644
    index 0000000000..b038b212e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/silverlight_test.js
    @@ -0,0 +1,60 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.silverlight.
    + */
    +
    +goog.provide('goog.html.silverlightTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.silverlight');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.silverlightTest');
    +
    +
    +function testCreateObjectForSilverlight() {
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from('https://google.com/trusted&'));
    +  assertSameHtml(
    +      '<object data="data:application/x-silverlight-2," ' +
    +          'type="application/x-silverlight-2" typemustmatch="" ' +
    +          'class="test&lt;">' +
    +          '<param name="source" value="https://google.com/trusted&amp;">' +
    +          '<param name="onload" value="onload&lt;">' +
    +          '</object>',
    +      goog.html.silverlight.createObject(
    +          trustedResourceUrl,
    +          {'onload': 'onload<'}, {'class': 'test<'}));
    +
    +  // Cannot override params, case insensitive.
    +  assertThrows(function() {
    +    goog.html.silverlight.createObject(
    +        trustedResourceUrl, {'datA': 'cantdothis'});
    +  });
    +
    +  // Cannot override attributes, case insensitive.
    +  assertThrows(function() {
    +    goog.html.silverlight.createObject(
    +        trustedResourceUrl, {}, {'datA': 'cantdothis'});
    +  });
    +}
    +
    +
    +function assertSameHtml(expected, html) {
    +  assertEquals(expected, goog.html.SafeHtml.unwrap(html));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/testing.js b/mapbox-gl/closure-library/closure/goog/html/testing.js
    new file mode 100644
    index 0000000000..882f9832ac
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/testing.js
    @@ -0,0 +1,129 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities to create arbitrary values of goog.html types for
    + * testing purposes. These utility methods perform no validation, and the
    + * resulting instances may violate type contracts.
    + *
    + * These methods are useful when types are constructed in a manner where using
    + * the production API is too inconvenient. Please do use the production API
    + * whenever possible; there is value in having tests reflect common usage and it
    + * avoids, by design, non-contract complying instances from being created.
    + */
    +
    +
    +goog.provide('goog.html.testing');
    +goog.setTestOnly();
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeScript');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +
    +
    +/**
    + * Creates a SafeHtml wrapping the given value. No validation is performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} html The string to wrap into a SafeHtml.
    + * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
    + *     SafeHtml to be constructed. A null or undefined value signifies an
    + *     unknown directionality.
    + * @return {!goog.html.SafeHtml}
    + */
    +goog.html.testing.newSafeHtmlForTest = function(html, opt_dir) {
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      html, (opt_dir == undefined ? null : opt_dir));
    +};
    +
    +
    +/**
    + * Creates a SafeScript wrapping the given value. No validation is performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} script The string to wrap into a SafeScript.
    + * @return {!goog.html.SafeScript}
    + */
    +goog.html.testing.newSafeScriptForTest = function(script) {
    +  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
    +      script);
    +};
    +
    +
    +/**
    + * Creates a SafeStyle wrapping the given value. No validation is performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} style String to wrap into a SafeStyle.
    + * @return {!goog.html.SafeStyle}
    + */
    +goog.html.testing.newSafeStyleForTest = function(style) {
    +  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    +      style);
    +};
    +
    +
    +/**
    + * Creates a SafeStyleSheet wrapping the given value. No validation is
    + * performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} styleSheet String to wrap into a SafeStyleSheet.
    + * @return {!goog.html.SafeStyleSheet}
    + */
    +goog.html.testing.newSafeStyleSheetForTest = function(styleSheet) {
    +  return goog.html.SafeStyleSheet.
    +      createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
    +};
    +
    +
    +/**
    + * Creates a SafeUrl wrapping the given value. No validation is performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} url String to wrap into a SafeUrl.
    + * @return {!goog.html.SafeUrl}
    + */
    +goog.html.testing.newSafeUrlForTest = function(url) {
    +  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    +
    +
    +/**
    + * Creates a TrustedResourceUrl wrapping the given value. No validation is
    + * performed.
    + *
    + * This function is for use in tests only and must never be used in production
    + * code.
    + *
    + * @param {string} url String to wrap into a TrustedResourceUrl.
    + * @return {!goog.html.TrustedResourceUrl}
    + */
    +goog.html.testing.newTrustedResourceUrlForTest = function(url) {
    +  return goog.html.TrustedResourceUrl.
    +      createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl.js b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl.js
    new file mode 100644
    index 0000000000..e7c7bf5f64
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl.js
    @@ -0,0 +1,224 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The TrustedResourceUrl type and its builders.
    + *
    + * TODO(user): Link to document stating type contract.
    + */
    +
    +goog.provide('goog.html.TrustedResourceUrl');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.i18n.bidi.DirectionalString');
    +goog.require('goog.string.Const');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * A URL which is under application control and from which script, CSS, and
    + * other resources that represent executable code, can be fetched.
    + *
    + * Given that the URL can only be constructed from strings under application
    + * control and is used to load resources, bugs resulting in a malformed URL
    + * should not have a security impact and are likely to be easily detectable
    + * during testing. Given the wide number of non-RFC compliant URLs in use,
    + * stricter validation could prevent some applications from being able to use
    + * this type.
    + *
    + * Instances of this type must be created via the factory method,
    + * ({@code goog.html.TrustedResourceUrl.fromConstant}), and not by invoking its
    + * constructor. The constructor intentionally takes no parameters and the type
    + * is immutable; hence only a default instance corresponding to the empty
    + * string can be obtained via constructor invocation.
    + *
    + * @see goog.html.TrustedResourceUrl#fromConstant
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.i18n.bidi.DirectionalString}
    + * @implements {goog.string.TypedString}
    + */
    +goog.html.TrustedResourceUrl = function() {
    +  /**
    +   * The contained value of this TrustedResourceUrl.  The field has a purposely
    +   * ugly name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.html.TrustedResourceUrl#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
    +      goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Returns this TrustedResourceUrl's value as a string.
    + *
    + * IMPORTANT: In code where it is security relevant that an object's type is
    + * indeed {@code TrustedResourceUrl}, use
    + * {@code goog.html.TrustedResourceUrl.unwrap} instead of this method. If in
    + * doubt, assume that it's security relevant. In particular, note that
    + * goog.html functions which return a goog.html type do not guarantee that
    + * the returned instance is of the right type. For example:
    + *
    + * <pre>
    + * var fakeSafeHtml = new String('fake');
    + * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
    + * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
    + * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
    + * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
    + * // goog.html.SafeHtml.
    + * </pre>
    + *
    + * @see goog.html.TrustedResourceUrl#unwrap
    + * @override
    + */
    +goog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {
    +  return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =
    +    true;
    +
    +
    +/**
    + * Returns this URLs directionality, which is always {@code LTR}.
    + * @override
    + */
    +goog.html.TrustedResourceUrl.prototype.getDirection = function() {
    +  return goog.i18n.bidi.Dir.LTR;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a debug string-representation of this value.
    +   *
    +   * To obtain the actual string value wrapped in a TrustedResourceUrl, use
    +   * {@code goog.html.TrustedResourceUrl.unwrap}.
    +   *
    +   * @see goog.html.TrustedResourceUrl#unwrap
    +   * @override
    +   */
    +  goog.html.TrustedResourceUrl.prototype.toString = function() {
    +    return 'TrustedResourceUrl{' +
    +        this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';
    +  };
    +}
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed a
    + * TrustedResourceUrl object, and returns its value.
    + *
    + * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to
    + *     extract from.
    + * @return {string} The trustedResourceUrl object's contained string, unless
    + *     the run-time type check fails. In that case, {@code unwrap} returns an
    + *     innocuous string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {
    +  // Perform additional Run-time type-checking to ensure that
    +  // trustedResourceUrl is indeed an instance of the expected type.  This
    +  // provides some additional protection against security bugs due to
    +  // application code that disables type checks.
    +  // Specifically, the following checks are performed:
    +  // 1. The object is an instance of the expected type.
    +  // 2. The object is not an instance of a subclass.
    +  // 3. The object carries a type marker for the expected type. "Faking" an
    +  // object requires a reference to the type marker, which has names intended
    +  // to stand out in code reviews.
    +  if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&
    +      trustedResourceUrl.constructor === goog.html.TrustedResourceUrl &&
    +      trustedResourceUrl
    +          .TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
    +              goog.html.TrustedResourceUrl
    +                  .TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
    +    return trustedResourceUrl
    +        .privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
    +  } else {
    +    goog.asserts.fail('expected object of type TrustedResourceUrl, got \'' +
    +                      trustedResourceUrl + '\'');
    +    return 'type_error:TrustedResourceUrl';
    +
    +  }
    +};
    +
    +
    +/**
    + * Creates a TrustedResourceUrl object from a compile-time constant string.
    + *
    + * Compile-time constant strings are inherently program-controlled and hence
    + * trusted.
    + *
    + * @param {!goog.string.Const} url A compile-time-constant string from which to
    + *     create a TrustedResourceUrl.
    + * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
    + *     initialized to {@code url}.
    + */
    +goog.html.TrustedResourceUrl.fromConstant = function(url) {
    +  return goog.html.TrustedResourceUrl
    +      .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
    +          goog.string.Const.unwrap(url));
    +};
    +
    +
    +/**
    + * Type marker for the TrustedResourceUrl type, used to implement additional
    + * run-time type checking.
    + * @const
    + * @private
    + */
    +goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +
    +/**
    + * Package-internal utility method to create TrustedResourceUrl instances.
    + *
    + * @param {string} url The string to initialize the TrustedResourceUrl object
    + *     with.
    + * @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl
    + *     object.
    + * @package
    + */
    +goog.html.TrustedResourceUrl.
    +    createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {
    +  var trustedResourceUrl = new goog.html.TrustedResourceUrl();
    +  trustedResourceUrl.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =
    +      url;
    +  return trustedResourceUrl;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.html b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.html
    new file mode 100644
    index 0000000000..9c0c6b2f04
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.trustedResourceUrlTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.js b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.js
    new file mode 100644
    index 0000000000..151578fd17
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/trustedresourceurl_test.js
    @@ -0,0 +1,60 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.TrustedResourceUrl and its builders.
    + */
    +
    +goog.provide('goog.html.trustedResourceUrlTest');
    +
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.trustedResourceUrlTest');
    +
    +
    +function testTrustedResourceUrl() {
    +  var url = 'javascript:trusted();';
    +  var trustedResourceUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from(url));
    +  var extracted = goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl);
    +  assertEquals(url, extracted);
    +  assertEquals(url, trustedResourceUrl.getTypedStringValue());
    +  assertEquals(
    +      'TrustedResourceUrl{javascript:trusted();}', String(trustedResourceUrl));
    +
    +  // URLs are always LTR.
    +  assertEquals(goog.i18n.bidi.Dir.LTR, trustedResourceUrl.getDirection());
    +
    +  // Interface markers are present.
    +  assertTrue(trustedResourceUrl.implementsGoogStringTypedString);
    +  assertTrue(trustedResourceUrl.implementsGoogI18nBidiDirectionalString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.trustedResourceUrlValueWithSecurityContract_googHtmlSecurityPrivate_ =
    +      '<script>evil()</script';
    +  evil.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.html.TrustedResourceUrl.unwrap(evil);
    +  });
    +  assertTrue(exception.message.indexOf(
    +      'expected object of type TrustedResourceUrl') > 0);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions.js b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions.js
    new file mode 100644
    index 0000000000..a1a5a9a7e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions.js
    @@ -0,0 +1,231 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unchecked conversions to create values of goog.html types from
    + * plain strings.  Use of these functions could potentially result in instances
    + * of goog.html types that violate their type contracts, and hence result in
    + * security vulnerabilties.
    + *
    + * Therefore, all uses of the methods herein must be carefully security
    + * reviewed.  Avoid use of the methods in this file whenever possible; instead
    + * prefer to create instances of goog.html types using inherently safe builders
    + * or template systems.
    + *
    + *
    + * @visibility {//closure/goog/html:approved_for_unchecked_conversion}
    + * @visibility {//closure/goog/bin/sizetests:__pkg__}
    + */
    +
    +
    +goog.provide('goog.html.uncheckedconversions');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeScript');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +
    +
    +/**
    + * Performs an "unchecked conversion" to SafeHtml from a plain string that is
    + * known to satisfy the SafeHtml type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code html} satisfies the SafeHtml type contract in all
    + * possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} html A string that is claimed to adhere to the SafeHtml
    + *     contract.
    + * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
    + *     SafeHtml to be constructed. A null or undefined value signifies an
    + *     unknown directionality.
    + * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
    + *     object.
    + * @suppress {visibility} For access to SafeHtml.create...  Note that this
    + *     use is appropriate since this method is intended to be "package private"
    + *     withing goog.html.  DO NOT call SafeHtml.create... from outside this
    + *     package; use appropriate wrappers instead.
    + */
    +goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
    +    function(justification, html, opt_dir) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
    +      html, opt_dir || null);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" to SafeScript from a plain string that is
    + * known to satisfy the SafeScript type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code script} satisfies the SafeScript type contract in
    + * all possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} script The string to wrap as a SafeScript.
    + * @return {!goog.html.SafeScript} The value of {@code script}, wrapped in a
    + *     SafeScript object.
    + */
    +goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
    +    function(justification, script) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmpty(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
    +      script);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" to SafeStyle from a plain string that is
    + * known to satisfy the SafeStyle type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code style} satisfies the SafeUrl type contract in all
    + * possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} style The string to wrap as a SafeStyle.
    + * @return {!goog.html.SafeStyle} The value of {@code style}, wrapped in a
    + *     SafeStyle object.
    + */
    +goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
    +    function(justification, style) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
    +      style);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" to SafeStyleSheet from a plain string
    + * that is known to satisfy the SafeStyleSheet type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code styleSheet} satisfies the SafeUrl type contract in
    + * all possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} styleSheet The string to wrap as a SafeStyleSheet.
    + * @return {!goog.html.SafeStyleSheet} The value of {@code styleSheet}, wrapped
    + *     in a SafeStyleSheet object.
    + */
    +goog.html.uncheckedconversions.
    +    safeStyleSheetFromStringKnownToSatisfyTypeContract =
    +    function(justification, styleSheet) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.SafeStyleSheet.
    +      createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" to SafeUrl from a plain string that is
    + * known to satisfy the SafeUrl type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code url} satisfies the SafeUrl type contract in all
    + * possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} url The string to wrap as a SafeUrl.
    + * @return {!goog.html.SafeUrl} The value of {@code url}, wrapped in a SafeUrl
    + *     object.
    + */
    +goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
    +    function(justification, url) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    +
    +
    +/**
    + * Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
    + * that is known to satisfy the TrustedResourceUrl type contract.
    + *
    + * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
    + * that the value of {@code url} satisfies the TrustedResourceUrl type contract
    + * in all possible program states.
    + *
    + *
    + * @param {!goog.string.Const} justification A constant string explaining why
    + *     this use of this method is safe. May include a security review ticket
    + *     number.
    + * @param {string} url The string to wrap as a TrustedResourceUrl.
    + * @return {!goog.html.TrustedResourceUrl} The value of {@code url}, wrapped in
    + *     a TrustedResourceUrl object.
    + */
    +goog.html.uncheckedconversions.
    +    trustedResourceUrlFromStringKnownToSatisfyTypeContract =
    +    function(justification, url) {
    +  // unwrap() called inside an assert so that justification can be optimized
    +  // away in production code.
    +  goog.asserts.assertString(goog.string.Const.unwrap(justification),
    +                            'must provide justification');
    +  goog.asserts.assert(
    +      !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
    +      'must provide non-empty justification');
    +  return goog.html.TrustedResourceUrl.
    +      createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.html b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.html
    new file mode 100644
    index 0000000000..b42f1da499
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.uncheckedconversionsTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.js b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.js
    new file mode 100644
    index 0000000000..b06e98372d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/uncheckedconversions_test.js
    @@ -0,0 +1,159 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.uncheckedconversions.
    + */
    +
    +goog.provide('goog.html.uncheckedconversionsTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeScript');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.SafeStyleSheet');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.uncheckedconversions');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.uncheckedconversionsTest');
    +
    +
    +function testSafeHtmlFromStringKnownToSatisfyTypeContract_ok() {
    +  var html = '<div>irrelevant</div>';
    +  var safeHtml = goog.html.uncheckedconversions.
    +      safeHtmlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from('Test'),
    +          html,
    +          goog.i18n.bidi.Dir.LTR);
    +  assertEquals(html, goog.html.SafeHtml.unwrap(safeHtml));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, safeHtml.getDirection());
    +}
    +
    +
    +function testSafeHtmlFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        safeHtmlFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'irrelevant');
    +  });
    +}
    +
    +
    +function testSafeScriptFromStringKnownToSatisfyTypeContract_ok() {
    +  var script = 'functionCall(\'irrelevant\');';
    +  var safeScript = goog.html.uncheckedconversions.
    +      safeScriptFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Safe because value is constant. Security review: b/7685625.'),
    +          script);
    +  assertEquals(script, goog.html.SafeScript.unwrap(safeScript));
    +}
    +
    +
    +function testSafeScriptFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        safeScriptFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'irrelevant');
    +  });
    +}
    +
    +
    +function testSafeStyleFromStringKnownToSatisfyTypeContract_ok() {
    +  var style = 'P.special { color:red ; }';
    +  var safeStyle = goog.html.uncheckedconversions.
    +      safeStyleFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Safe because value is constant. Security review: b/7685625.'),
    +          style);
    +  assertEquals(style, goog.html.SafeStyle.unwrap(safeStyle));
    +}
    +
    +
    +function testSafeStyleFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        safeStyleFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'irrelevant');
    +  });
    +}
    +
    +
    +function testSafeStyleSheetFromStringKnownToSatisfyTypeContract_ok() {
    +  var styleSheet = 'P.special { color:red ; }';
    +  var safeStyleSheet = goog.html.uncheckedconversions.
    +      safeStyleSheetFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Safe because value is constant. Security review: b/7685625.'),
    +          styleSheet);
    +  assertEquals(styleSheet, goog.html.SafeStyleSheet.unwrap(safeStyleSheet));
    +}
    +
    +
    +function testSafeStyleSheetFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        safeStyleSheetFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'irrelevant');
    +  });
    +}
    +
    +
    +function testSafeUrlFromStringKnownToSatisfyTypeContract_ok() {
    +  var url = 'http://www.irrelevant.com';
    +  var safeUrl = goog.html.uncheckedconversions.
    +      safeUrlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Safe because value is constant. Security review: b/7685625.'),
    +              url);
    +  assertEquals(url, goog.html.SafeUrl.unwrap(safeUrl));
    +}
    +
    +
    +function testSafeUrlFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        safeUrlFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'http://irrelevant.com');
    +  });
    +}
    +
    +
    +function testTrustedResourceUrlFromStringKnownToSatisfyTypeContract_ok() {
    +  var url = 'http://www.irrelevant.com';
    +  var trustedResourceUrl = goog.html.uncheckedconversions.
    +      trustedResourceUrlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Safe because value is constant. Security review: b/7685625.'),
    +              url);
    +  assertEquals(url, goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
    +}
    +
    +
    +function testTrustedResourceFromStringKnownToSatisfyTypeContract_error() {
    +  assertThrows(function() {
    +    goog.html.uncheckedconversions.
    +        trustedResourceUrlFromStringKnownToSatisfyTypeContract(
    +            goog.string.Const.from(''),
    +            'http://irrelevant.com');
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/html/utils.js b/mapbox-gl/closure-library/closure/goog/html/utils.js
    new file mode 100644
    index 0000000000..c54381bf28
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/utils.js
    @@ -0,0 +1,67 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview HTML processing utilities for HTML in string form.
    + */
    +
    +goog.provide('goog.html.utils');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * Extracts plain text from HTML.
    + *
    + * This behaves similarly to extracting textContent from a hypothetical DOM
    + * element containing the specified HTML.  Block-level elements such as div are
    + * surrounded with whitespace, but inline elements are not.  Span is treated as
    + * a block level element because it is often used as a container.  Breaking
    + * spaces are compressed and trimmed.
    + *
    + * @param {string} value The input HTML to have tags removed.
    + * @return {string} The plain text of value without tags, HTML comments, or
    + *     other non-text content.  Does NOT return safe HTML!
    + */
    +goog.html.utils.stripHtmlTags = function(value) {
    +  // TODO(user): Make a version that extracts text attributes such as alt.
    +  return goog.string.unescapeEntities(goog.string.trim(value.replace(
    +      goog.html.utils.HTML_TAG_REGEX_, function(fullMatch, tagName) {
    +        return goog.html.utils.INLINE_HTML_TAG_REGEX_.test(tagName) ? '' : ' ';
    +      }).
    +      replace(/[\t\n ]+/g, ' ')));
    +};
    +
    +
    +/**
    + * Matches all tags that do not require extra space.
    + *
    + * @const
    + * @private {RegExp}
    + */
    +goog.html.utils.INLINE_HTML_TAG_REGEX_ =
    +    /^(?:abbr|acronym|address|b|em|i|small|strong|su[bp]|u)$/i;
    +
    +
    +/**
    + * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
    + * By removing these, and replacing any '<' or '>' characters with
    + * entities we guarantee that the result can be embedded into
    + * an attribute without introducing a tag boundary.
    + *
    + * @private {RegExp}
    + * @const
    + */
    +goog.html.utils.HTML_TAG_REGEX_ = /<[!\/]?([a-z0-9]+)([\/ ][^>]*)?>/gi;
    diff --git a/mapbox-gl/closure-library/closure/goog/html/utils_test.html b/mapbox-gl/closure-library/closure/goog/html/utils_test.html
    new file mode 100644
    index 0000000000..7aa69815ef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/utils_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.html.utils</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.html.UtilsTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/html/utils_test.js b/mapbox-gl/closure-library/closure/goog/html/utils_test.js
    new file mode 100644
    index 0000000000..c9173f702a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/html/utils_test.js
    @@ -0,0 +1,122 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.html.util.
    + */
    +
    +goog.provide('goog.html.UtilsTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.html.utils');
    +goog.require('goog.object');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.UtilsTest');
    +
    +
    +var FAILURE_MESSAGE = 'Failed to strip all HTML.';
    +var STRIP = 'Hello world!';
    +var result;
    +
    +
    +function tearDown() {
    +  result = null;
    +}
    +
    +
    +function testStripAllHtmlTagsSingle() {
    +  goog.object.forEach(goog.dom.TagName, function(tag) {
    +    result = goog.html.utils.stripHtmlTags(makeHtml_(tag, STRIP));
    +    assertEquals(FAILURE_MESSAGE, STRIP, result);
    +  });
    +}
    +
    +
    +function testStripAllHtmlTagsAttribute() {
    +  goog.object.forEach(goog.dom.TagName, function(tag) {
    +    result = goog.html.utils.stripHtmlTags(makeHtml_(tag, STRIP, 1, 0, 'a'));
    +    assertEquals(FAILURE_MESSAGE, STRIP, result);
    +  });
    +}
    +
    +
    +function testStripAllHtmlTagsDouble() {
    +  var tag1 = goog.dom.TagName.B;
    +  var tag2 = goog.dom.TagName.DIV;
    +  result = goog.html.utils.stripHtmlTags(makeHtml_(tag1, STRIP, 2));
    +  assertEquals(FAILURE_MESSAGE, STRIP + STRIP, result);
    +  result = goog.html.utils.stripHtmlTags(makeHtml_(tag2, STRIP, 2));
    +  assertEquals(FAILURE_MESSAGE, STRIP + ' ' + STRIP, result);
    +}
    +
    +
    +function testComplex() {
    +  var html = '<h1 id=\"life\">Life at Google</h1>' +
    +      '<p>Read and interact with the information below to learn about ' +
    +      'life at <u>Google</u>.</p>' +
    +      '<h2 id=\"food\">Food at Google</h2>' +
    +      '<p>Google has <em>the best food in the world</em>.</p>' +
    +      '<h2 id=\"transportation\">Transportation at Google</h2>' +
    +      '<p>Google provides <i>free transportation</i>.</p>' +
    +      // Some text with symbols to make sure that it does not get stripped
    +      '<3i><x>\n-10<x<10 3cat < 3dog &amp;&lt;&gt;&quot;';
    +  result = goog.html.utils.stripHtmlTags(html);
    +  var expected = 'Life at Google ' +
    +      'Read and interact with the information below to learn about ' +
    +      'life at Google. ' +
    +      'Food at Google ' +
    +      'Google has the best food in the world. ' +
    +      'Transportation at Google ' +
    +      'Google provides free transportation. ' +
    +      '-10<x<10 3cat < 3dog &<>\"';
    +  assertEquals(FAILURE_MESSAGE, expected, result);
    +}
    +
    +
    +function testInteresting() {
    +  result = goog.html.utils.stripHtmlTags(
    +      '<img/src="bogus"onerror=alert(13) style="display:none">');
    +  assertEquals(FAILURE_MESSAGE, '', result);
    +  result = goog.html.utils.stripHtmlTags(
    +      '<img o\'reilly blob src=bogus onerror=alert(1337)>');
    +  assertEquals(FAILURE_MESSAGE, '', result);
    +}
    +
    +
    +/**
    + * Constructs the HTML of an element from the given tag and content.
    + * @param {goog.dom.TagName} tag The HTML tagName for the element.
    + * @param {string} content The content.
    + * @param {number=} opt_copies Optional number of copies to make.
    + * @param {number=} opt_tabIndex Optional tabIndex to give the element.
    + * @param {string=} opt_id Optional id to give the element.
    + * @return {string} The HTML of an element from the given tag and content.
    + */
    +function makeHtml_(tag, content, opt_copies, opt_tabIndex, opt_id) {
    +  var html = ['<' + tag, '>' + content + '</' + tag + '>'];
    +  if (goog.isNumber(opt_tabIndex)) {
    +    goog.array.insertAt(html, ' tabIndex=\"' + opt_tabIndex + '\"', 1);
    +  }
    +  if (goog.isString(opt_id)) {
    +    goog.array.insertAt(html, ' id=\"' + opt_id + '\"', 1);
    +  }
    +  html = html.join('');
    +  var array = [];
    +  for (var i = 0, length = opt_copies || 1; i < length; i++) {
    +    array[i] = html;
    +  }
    +  return array.join('');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidi.js b/mapbox-gl/closure-library/closure/goog/i18n/bidi.js
    new file mode 100644
    index 0000000000..771ea3be27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidi.js
    @@ -0,0 +1,877 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility functions for supporting Bidi issues.
    + */
    +
    +
    +/**
    + * Namespace for bidi supporting functions.
    + */
    +goog.provide('goog.i18n.bidi');
    +goog.provide('goog.i18n.bidi.Dir');
    +goog.provide('goog.i18n.bidi.DirectionalString');
    +goog.provide('goog.i18n.bidi.Format');
    +
    +
    +/**
    + * @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
    + * to say that the current locale is a RTL locale.  This should only be used
    + * if you want to override the default behavior for deciding whether the
    + * current locale is RTL or not.
    + *
    + * {@see goog.i18n.bidi.IS_RTL}
    + */
    +goog.define('goog.i18n.bidi.FORCE_RTL', false);
    +
    +
    +/**
    + * Constant that defines whether or not the current locale is a RTL locale.
    + * If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
    + * to check that {@link goog.LOCALE} is one of a few major RTL locales.
    + *
    + * <p>This is designed to be a maximally efficient compile-time constant. For
    + * example, for the default goog.LOCALE, compiling
    + * "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
    + * is this design consideration that limits the implementation to only
    + * supporting a few major RTL locales, as opposed to the broader repertoire of
    + * something like goog.i18n.bidi.isRtlLanguage.
    + *
    + * <p>Since this constant refers to the directionality of the locale, it is up
    + * to the caller to determine if this constant should also be used for the
    + * direction of the UI.
    + *
    + * {@see goog.LOCALE}
    + *
    + * @type {boolean}
    + *
    + * TODO(user): write a test that checks that this is a compile-time constant.
    + */
    +goog.i18n.bidi.IS_RTL = goog.i18n.bidi.FORCE_RTL ||
    +    (
    +        (goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'ps' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'sd' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'ug' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
    +         goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
    +        (goog.LOCALE.length == 2 ||
    +         goog.LOCALE.substring(2, 3) == '-' ||
    +         goog.LOCALE.substring(2, 3) == '_')
    +    ) || (
    +        goog.LOCALE.length >= 3 &&
    +        goog.LOCALE.substring(0, 3).toLowerCase() == 'ckb' &&
    +        (goog.LOCALE.length == 3 ||
    +         goog.LOCALE.substring(3, 4) == '-' ||
    +         goog.LOCALE.substring(3, 4) == '_')
    +    );
    +
    +
    +/**
    + * Unicode formatting characters and directionality string constants.
    + * @enum {string}
    + */
    +goog.i18n.bidi.Format = {
    +  /** Unicode "Left-To-Right Embedding" (LRE) character. */
    +  LRE: '\u202A',
    +  /** Unicode "Right-To-Left Embedding" (RLE) character. */
    +  RLE: '\u202B',
    +  /** Unicode "Pop Directional Formatting" (PDF) character. */
    +  PDF: '\u202C',
    +  /** Unicode "Left-To-Right Mark" (LRM) character. */
    +  LRM: '\u200E',
    +  /** Unicode "Right-To-Left Mark" (RLM) character. */
    +  RLM: '\u200F'
    +};
    +
    +
    +/**
    + * Directionality enum.
    + * @enum {number}
    + */
    +goog.i18n.bidi.Dir = {
    +  /**
    +   * Left-to-right.
    +   */
    +  LTR: 1,
    +
    +  /**
    +   * Right-to-left.
    +   */
    +  RTL: -1,
    +
    +  /**
    +   * Neither left-to-right nor right-to-left.
    +   */
    +  NEUTRAL: 0
    +};
    +
    +
    +/**
    + * 'right' string constant.
    + * @type {string}
    + */
    +goog.i18n.bidi.RIGHT = 'right';
    +
    +
    +/**
    + * 'left' string constant.
    + * @type {string}
    + */
    +goog.i18n.bidi.LEFT = 'left';
    +
    +
    +/**
    + * 'left' if locale is RTL, 'right' if not.
    + * @type {string}
    + */
    +goog.i18n.bidi.I18N_RIGHT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT :
    +    goog.i18n.bidi.RIGHT;
    +
    +
    +/**
    + * 'right' if locale is RTL, 'left' if not.
    + * @type {string}
    + */
    +goog.i18n.bidi.I18N_LEFT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT :
    +    goog.i18n.bidi.LEFT;
    +
    +
    +/**
    + * Convert a directionality given in various formats to a goog.i18n.bidi.Dir
    + * constant. Useful for interaction with different standards of directionality
    + * representation.
    + *
    + * @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
    + *     in one of the following formats:
    + *     1. A goog.i18n.bidi.Dir constant.
    + *     2. A number (positive = LTR, negative = RTL, 0 = neutral).
    + *     3. A boolean (true = RTL, false = LTR).
    + *     4. A null for unknown directionality.
    + * @param {boolean=} opt_noNeutral Whether a givenDir of zero or
    + *     goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
    + *     order to preserve legacy behavior.
    + * @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
    + *     given directionality. If given null, returns null (i.e. unknown).
    + */
    +goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
    +  if (typeof givenDir == 'number') {
    +    // This includes the non-null goog.i18n.bidi.Dir case.
    +    return givenDir > 0 ? goog.i18n.bidi.Dir.LTR :
    +        givenDir < 0 ? goog.i18n.bidi.Dir.RTL :
    +        opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
    +  } else if (givenDir == null) {
    +    return null;
    +  } else {
    +    // Must be typeof givenDir == 'boolean'.
    +    return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
    +  }
    +};
    +
    +
    +/**
    + * A practical pattern to identify strong LTR characters. This pattern is not
    + * theoretically correct according to the Unicode standard. It is simplified for
    + * performance and small code size.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.bidi.ltrChars_ =
    +    'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
    +    '\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
    +
    +
    +/**
    + * A practical pattern to identify strong RTL character. This pattern is not
    + * theoretically correct according to the Unicode standard. It is simplified
    + * for performance and small code size.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.bidi.rtlChars_ = '\u0591-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
    +
    +
    +/**
    + * Simplified regular expression for an HTML tag (opening or closing) or an HTML
    + * escape. We might want to skip over such expressions when estimating the text
    + * directionality.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;
    +
    +
    +/**
    + * Returns the input text with spaces instead of HTML tags or HTML escapes, if
    + * opt_isStripNeeded is true. Else returns the input as is.
    + * Useful for text directionality estimation.
    + * Note: the function should not be used in other contexts; it is not 100%
    + * correct, but rather a good-enough implementation for directionality
    + * estimation purposes.
    + * @param {string} str The given string.
    + * @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
    + *     Default: false (to retain consistency with calling functions).
    + * @return {string} The given string cleaned of HTML tags / escapes.
    + * @private
    + */
    +goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
    +  return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, '') :
    +      str;
    +};
    +
    +
    +/**
    + * Regular expression to check for RTL characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');
    +
    +
    +/**
    + * Regular expression to check for LTR characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');
    +
    +
    +/**
    + * Test whether the given string has any RTL characters in it.
    + * @param {string} str The given string that need to be tested.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether the string contains RTL characters.
    + */
    +goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.rtlCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
    +      str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Test whether the given string has any RTL characters in it.
    + * @param {string} str The given string that need to be tested.
    + * @return {boolean} Whether the string contains RTL characters.
    + * @deprecated Use hasAnyRtl.
    + */
    +goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;
    +
    +
    +/**
    + * Test whether the given string has any LTR characters in it.
    + * @param {string} str The given string that need to be tested.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether the string contains LTR characters.
    + */
    +goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.ltrCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
    +      str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Regular expression pattern to check if the first character in the string
    + * is LTR.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');
    +
    +
    +/**
    + * Regular expression pattern to check if the first character in the string
    + * is RTL.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');
    +
    +
    +/**
    + * Check if the first character in the string is RTL or not.
    + * @param {string} str The given string that need to be tested.
    + * @return {boolean} Whether the first character in str is an RTL char.
    + */
    +goog.i18n.bidi.isRtlChar = function(str) {
    +  return goog.i18n.bidi.rtlRe_.test(str);
    +};
    +
    +
    +/**
    + * Check if the first character in the string is LTR or not.
    + * @param {string} str The given string that need to be tested.
    + * @return {boolean} Whether the first character in str is an LTR char.
    + */
    +goog.i18n.bidi.isLtrChar = function(str) {
    +  return goog.i18n.bidi.ltrRe_.test(str);
    +};
    +
    +
    +/**
    + * Check if the first character in the string is neutral or not.
    + * @param {string} str The given string that need to be tested.
    + * @return {boolean} Whether the first character in str is a neutral char.
    + */
    +goog.i18n.bidi.isNeutralChar = function(str) {
    +  return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
    +};
    +
    +
    +/**
    + * Regular expressions to check if a piece of text is of LTR directionality
    + * on first character with strong directionality.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
    +    '^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');
    +
    +
    +/**
    + * Regular expressions to check if a piece of text is of RTL directionality
    + * on first character with strong directionality.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
    +    '^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');
    +
    +
    +/**
    + * Check whether the first strongly directional character (if any) is RTL.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether RTL directionality is detected using the first
    + *     strongly-directional character method.
    + */
    +goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.rtlDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
    +      str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Check whether the first strongly directional character (if any) is RTL.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether RTL directionality is detected using the first
    + *     strongly-directional character method.
    + * @deprecated Use startsWithRtl.
    + */
    +goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;
    +
    +
    +/**
    + * Check whether the first strongly directional character (if any) is LTR.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether LTR directionality is detected using the first
    + *     strongly-directional character method.
    + */
    +goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.ltrDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
    +      str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Check whether the first strongly directional character (if any) is LTR.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether LTR directionality is detected using the first
    + *     strongly-directional character method.
    + * @deprecated Use startsWithLtr.
    + */
    +goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;
    +
    +
    +/**
    + * Regular expression to check if a string looks like something that must
    + * always be LTR even in RTL text, e.g. a URL. When estimating the
    + * directionality of text containing these, we treat these as weakly LTR,
    + * like numbers.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;
    +
    +
    +/**
    + * Check whether the input string either contains no strongly directional
    + * characters or looks like a url.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether neutral directionality is detected.
    + */
    +goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
    +  str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
    +  return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
    +      !goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
    +};
    +
    +
    +/**
    + * Regular expressions to check if the last strongly-directional character in a
    + * piece of text is LTR.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
    +    '[' + goog.i18n.bidi.ltrChars_ + '][^' + goog.i18n.bidi.rtlChars_ + ']*$');
    +
    +
    +/**
    + * Regular expressions to check if the last strongly-directional character in a
    + * piece of text is RTL.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
    +    '[' + goog.i18n.bidi.rtlChars_ + '][^' + goog.i18n.bidi.ltrChars_ + ']*$');
    +
    +
    +/**
    + * Check if the exit directionality a piece of text is LTR, i.e. if the last
    + * strongly-directional character in the string is LTR.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether LTR exit directionality was detected.
    + */
    +goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.ltrExitDirCheckRe_.test(
    +      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Check if the exit directionality a piece of text is LTR, i.e. if the last
    + * strongly-directional character in the string is LTR.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether LTR exit directionality was detected.
    + * @deprecated Use endsWithLtr.
    + */
    +goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;
    +
    +
    +/**
    + * Check if the exit directionality a piece of text is RTL, i.e. if the last
    + * strongly-directional character in the string is RTL.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether RTL exit directionality was detected.
    + */
    +goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.rtlExitDirCheckRe_.test(
    +      goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Check if the exit directionality a piece of text is RTL, i.e. if the last
    + * strongly-directional character in the string is RTL.
    + * @param {string} str String being checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether RTL exit directionality was detected.
    + * @deprecated Use endsWithRtl.
    + */
    +goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;
    +
    +
    +/**
    + * A regular expression for matching right-to-left language codes.
    + * See {@link #isRtlLanguage} for the design.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
    +    '^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|' +
    +    '.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))' +
    +    '(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
    +    'i');
    +
    +
    +/**
    + * Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
    + * - a language code explicitly specifying one of the right-to-left scripts,
    + *   e.g. "az-Arab", or<p>
    + * - a language code specifying one of the languages normally written in a
    + *   right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
    + *   Latin or Cyrillic script (which are the usual LTR alternatives).<p>
    + * The list of right-to-left scripts appears in the 100-199 range in
    + * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
    + * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
    + * Tifinagh, which also have significant modern usage. The rest (Syriac,
    + * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
    + * and are not recognized to save on code size.
    + * The languages usually written in a right-to-left script are taken as those
    + * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng  in
    + * http://www.iana.org/assignments/language-subtag-registry,
    + * as well as Central (or Sorani) Kurdish (ckb), Sindhi (sd) and Uyghur (ug).
    + * Other subtags of the language code, e.g. regions like EG (Egypt), are
    + * ignored.
    + * @param {string} lang BCP 47 (a.k.a III) language code.
    + * @return {boolean} Whether the language code is an RTL language.
    + */
    +goog.i18n.bidi.isRtlLanguage = function(lang) {
    +  return goog.i18n.bidi.rtlLocalesRe_.test(lang);
    +};
    +
    +
    +/**
    + * Regular expression for bracket guard replacement in html.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.bracketGuardHtmlRe_ =
    +    /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)/g;
    +
    +
    +/**
    + * Regular expression for bracket guard replacement in text.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.bracketGuardTextRe_ =
    +    /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;
    +
    +
    +/**
    + * Apply bracket guard using html span tag. This is to address the problem of
    + * messy bracket display frequently happens in RTL layout.
    + * @param {string} s The string that need to be processed.
    + * @param {boolean=} opt_isRtlContext specifies default direction (usually
    + *     direction of the UI).
    + * @return {string} The processed string, with all bracket guarded.
    + */
    +goog.i18n.bidi.guardBracketInHtml = function(s, opt_isRtlContext) {
    +  var useRtl = opt_isRtlContext === undefined ?
    +      goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
    +  if (useRtl) {
    +    return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
    +        '<span dir=rtl>$&</span>');
    +  }
    +  return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
    +      '<span dir=ltr>$&</span>');
    +};
    +
    +
    +/**
    + * Apply bracket guard using LRM and RLM. This is to address the problem of
    + * messy bracket display frequently happens in RTL layout.
    + * This version works for both plain text and html. But it does not work as
    + * good as guardBracketInHtml in some cases.
    + * @param {string} s The string that need to be processed.
    + * @param {boolean=} opt_isRtlContext specifies default direction (usually
    + *     direction of the UI).
    + * @return {string} The processed string, with all bracket guarded.
    + */
    +goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
    +  var useRtl = opt_isRtlContext === undefined ?
    +      goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
    +  var mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
    +  return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
    +};
    +
    +
    +/**
    + * Enforce the html snippet in RTL directionality regardless overall context.
    + * If the html piece was enclosed by tag, dir will be applied to existing
    + * tag, otherwise a span tag will be added as wrapper. For this reason, if
    + * html snippet start with with tag, this tag must enclose the whole piece. If
    + * the tag already has a dir specified, this new one will override existing
    + * one in behavior (tested on FF and IE).
    + * @param {string} html The string that need to be processed.
    + * @return {string} The processed string, with directionality enforced to RTL.
    + */
    +goog.i18n.bidi.enforceRtlInHtml = function(html) {
    +  if (html.charAt(0) == '<') {
    +    return html.replace(/<\w+/, '$& dir=rtl');
    +  }
    +  // '\n' is important for FF so that it won't incorrectly merge span groups
    +  return '\n<span dir=rtl>' + html + '</span>';
    +};
    +
    +
    +/**
    + * Enforce RTL on both end of the given text piece using unicode BiDi formatting
    + * characters RLE and PDF.
    + * @param {string} text The piece of text that need to be wrapped.
    + * @return {string} The wrapped string after process.
    + */
    +goog.i18n.bidi.enforceRtlInText = function(text) {
    +  return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
    +};
    +
    +
    +/**
    + * Enforce the html snippet in RTL directionality regardless overall context.
    + * If the html piece was enclosed by tag, dir will be applied to existing
    + * tag, otherwise a span tag will be added as wrapper. For this reason, if
    + * html snippet start with with tag, this tag must enclose the whole piece. If
    + * the tag already has a dir specified, this new one will override existing
    + * one in behavior (tested on FF and IE).
    + * @param {string} html The string that need to be processed.
    + * @return {string} The processed string, with directionality enforced to RTL.
    + */
    +goog.i18n.bidi.enforceLtrInHtml = function(html) {
    +  if (html.charAt(0) == '<') {
    +    return html.replace(/<\w+/, '$& dir=ltr');
    +  }
    +  // '\n' is important for FF so that it won't incorrectly merge span groups
    +  return '\n<span dir=ltr>' + html + '</span>';
    +};
    +
    +
    +/**
    + * Enforce LTR on both end of the given text piece using unicode BiDi formatting
    + * characters LRE and PDF.
    + * @param {string} text The piece of text that need to be wrapped.
    + * @return {string} The wrapped string after process.
    + */
    +goog.i18n.bidi.enforceLtrInText = function(text) {
    +  return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
    +};
    +
    +
    +/**
    + * Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.dimensionsRe_ =
    +    /:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;
    +
    +
    +/**
    + * Regular expression for left.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.leftRe_ = /left/gi;
    +
    +
    +/**
    + * Regular expression for right.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.rightRe_ = /right/gi;
    +
    +
    +/**
    + * Placeholder regular expression for swapping.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.tempRe_ = /%%%%/g;
    +
    +
    +/**
    + * Swap location parameters and 'left'/'right' in CSS specification. The
    + * processed string will be suited for RTL layout. Though this function can
    + * cover most cases, there are always exceptions. It is suggested to put
    + * those exceptions in separate group of CSS string.
    + * @param {string} cssStr CSS spefication string.
    + * @return {string} Processed CSS specification string.
    + */
    +goog.i18n.bidi.mirrorCSS = function(cssStr) {
    +  return cssStr.
    +      // reverse dimensions
    +      replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2').
    +      replace(goog.i18n.bidi.leftRe_, '%%%%').          // swap left and right
    +      replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT).
    +      replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
    +};
    +
    +
    +/**
    + * Regular expression for hebrew double quote substitution, finding quote
    + * directly after hebrew characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;
    +
    +
    +/**
    + * Regular expression for hebrew single quote substitution, finding quote
    + * directly after hebrew characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;
    +
    +
    +/**
    + * Replace the double and single quote directly after a Hebrew character with
    + * GERESH and GERSHAYIM. In such case, most likely that's user intention.
    + * @param {string} str String that need to be processed.
    + * @return {string} Processed string with double/single quote replaced.
    + */
    +goog.i18n.bidi.normalizeHebrewQuote = function(str) {
    +  return str.
    +      replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4').
    +      replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
    +};
    +
    +
    +/**
    + * Regular expression to split a string into "words" for directionality
    + * estimation based on relative word counts.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.wordSeparatorRe_ = /\s+/;
    +
    +
    +/**
    + * Regular expression to check if a string contains any numerals. Used to
    + * differentiate between completely neutral strings and those containing
    + * numbers, which are weakly LTR.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.bidi.hasNumeralsRe_ = /\d/;
    +
    +
    +/**
    + * This constant controls threshold of RTL directionality.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;
    +
    +
    +/**
    + * Estimates the directionality of a string based on relative word counts.
    + * If the number of RTL words is above a certain percentage of the total number
    + * of strongly directional words, returns RTL.
    + * Otherwise, if any words are strongly or weakly LTR, returns LTR.
    + * Otherwise, returns UNKNOWN, which is used to mean "neutral".
    + * Numbers are counted as weakly LTR.
    + * @param {string} str The string to be checked.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
    + */
    +goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
    +  var rtlCount = 0;
    +  var totalCount = 0;
    +  var hasWeaklyLtr = false;
    +  var tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml).
    +      split(goog.i18n.bidi.wordSeparatorRe_);
    +  for (var i = 0; i < tokens.length; i++) {
    +    var token = tokens[i];
    +    if (goog.i18n.bidi.startsWithRtl(token)) {
    +      rtlCount++;
    +      totalCount++;
    +    } else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
    +      hasWeaklyLtr = true;
    +    } else if (goog.i18n.bidi.hasAnyLtr(token)) {
    +      totalCount++;
    +    } else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
    +      hasWeaklyLtr = true;
    +    }
    +  }
    +
    +  return totalCount == 0 ?
    +      (hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
    +      (rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
    +          goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR);
    +};
    +
    +
    +/**
    + * Check the directionality of a piece of text, return true if the piece of
    + * text should be laid out in RTL direction.
    + * @param {string} str The piece of text that need to be detected.
    + * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {boolean} Whether this piece of text should be laid out in RTL.
    + */
    +goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
    +  return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
    +      goog.i18n.bidi.Dir.RTL;
    +};
    +
    +
    +/**
    + * Sets text input element's directionality and text alignment based on a
    + * given directionality. Does nothing if the given directionality is unknown or
    + * neutral.
    + * @param {Element} element Input field element to set directionality to.
    + * @param {goog.i18n.bidi.Dir|number|boolean|null} dir Desired directionality,
    + *     given in one of the following formats:
    + *     1. A goog.i18n.bidi.Dir constant.
    + *     2. A number (positive = LRT, negative = RTL, 0 = neutral).
    + *     3. A boolean (true = RTL, false = LTR).
    + *     4. A null for unknown directionality.
    + */
    +goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
    +  if (element) {
    +    dir = goog.i18n.bidi.toDir(dir);
    +    if (dir) {
    +      element.style.textAlign =
    +          dir == goog.i18n.bidi.Dir.RTL ?
    +          goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
    +      element.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
    +    }
    +  }
    +};
    +
    +
    +
    +/**
    + * Strings that have an (optional) known direction.
    + *
    + * Implementations of this interface are string-like objects that carry an
    + * attached direction, if known.
    + * @interface
    + */
    +goog.i18n.bidi.DirectionalString = function() {};
    +
    +
    +/**
    + * Interface marker of the DirectionalString interface.
    + *
    + * This property can be used to determine at runtime whether or not an object
    + * implements this interface.  All implementations of this interface set this
    + * property to {@code true}.
    + * @type {boolean}
    + */
    +goog.i18n.bidi.DirectionalString.prototype.
    +    implementsGoogI18nBidiDirectionalString;
    +
    +
    +/**
    + * Retrieves this object's known direction (if any).
    + * @return {?goog.i18n.bidi.Dir} The known direction. Null if unknown.
    + */
    +goog.i18n.bidi.DirectionalString.prototype.getDirection;
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.html b/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.html
    new file mode 100644
    index 0000000000..26f7982349
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.bidi
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.bidiTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.js b/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.js
    new file mode 100644
    index 0000000000..b095459d09
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidi_test.js
    @@ -0,0 +1,481 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.bidiTest');
    +goog.setTestOnly('goog.i18n.bidiTest');
    +
    +goog.require('goog.i18n.bidi');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.testing.jsunit');
    +
    +var LRE = '\u202A';
    +var RLE = '\u202B';
    +var PDF = '\u202C';
    +var LRM = '\u200E';
    +var RLM = '\u200F';
    +
    +function testToDir() {
    +  assertEquals(null, goog.i18n.bidi.toDir(null));
    +  assertEquals(null, goog.i18n.bidi.toDir(null, true));
    +
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +      goog.i18n.bidi.toDir(goog.i18n.bidi.Dir.NEUTRAL));
    +  assertEquals(null, goog.i18n.bidi.toDir(0, true));
    +
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +      goog.i18n.bidi.toDir(goog.i18n.bidi.Dir.LTR));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +      goog.i18n.bidi.toDir(goog.i18n.bidi.Dir.LTR, true));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, goog.i18n.bidi.toDir(100));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, goog.i18n.bidi.toDir(100, true));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, goog.i18n.bidi.toDir(false));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, goog.i18n.bidi.toDir(false, true));
    +
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +      goog.i18n.bidi.toDir(goog.i18n.bidi.Dir.RTL));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +      goog.i18n.bidi.toDir(goog.i18n.bidi.Dir.RTL, true));
    +  assertEquals(goog.i18n.bidi.Dir.RTL, goog.i18n.bidi.toDir(-100));
    +  assertEquals(goog.i18n.bidi.Dir.RTL, goog.i18n.bidi.toDir(-100, true));
    +  assertEquals(goog.i18n.bidi.Dir.RTL, goog.i18n.bidi.toDir(true));
    +  assertEquals(goog.i18n.bidi.Dir.RTL, goog.i18n.bidi.toDir(true, true));
    +}
    +
    +function testIsRtlLang() {
    +  assert(!goog.i18n.bidi.isRtlLanguage('en'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('fr'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('zh-CN'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('fil'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('az'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('iw-Latn'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('iw-LATN'));
    +  assert(!goog.i18n.bidi.isRtlLanguage('iw_latn'));
    +  assert(goog.i18n.bidi.isRtlLanguage('ar'));
    +  assert(goog.i18n.bidi.isRtlLanguage('AR'));
    +  assert(goog.i18n.bidi.isRtlLanguage('iw'));
    +  assert(goog.i18n.bidi.isRtlLanguage('he'));
    +  assert(goog.i18n.bidi.isRtlLanguage('fa'));
    +  assert(goog.i18n.bidi.isRtlLanguage('ckb'));
    +  assert(goog.i18n.bidi.isRtlLanguage('ar-EG'));
    +  assert(goog.i18n.bidi.isRtlLanguage('az-Arab'));
    +  assert(goog.i18n.bidi.isRtlLanguage('az-ARAB-IR'));
    +  assert(goog.i18n.bidi.isRtlLanguage('az_arab_IR'));
    +}
    +
    +function testIsLtrChar() {
    +  assert(goog.i18n.bidi.isLtrChar('a'));
    +  assert(!goog.i18n.bidi.isLtrChar('\u05e0'));
    +  var str = 'a\u05e0z';
    +  assert(goog.i18n.bidi.isLtrChar(str.charAt(0)));
    +  assert(!goog.i18n.bidi.isLtrChar(str.charAt(1)));
    +  assert(goog.i18n.bidi.isLtrChar(str.charAt(2)));
    +}
    +
    +function testIsRtlChar() {
    +  assert(!goog.i18n.bidi.isRtlChar('a'));
    +  assert(goog.i18n.bidi.isRtlChar('\u05e0'));
    +  var str = 'a\u05e0z';
    +  assert(!goog.i18n.bidi.isRtlChar(str.charAt(0)));
    +  assert(goog.i18n.bidi.isRtlChar(str.charAt(1)));
    +  assert(!goog.i18n.bidi.isRtlChar(str.charAt(2)));
    +}
    +
    +function testIsNeutralChar() {
    +  assert(goog.i18n.bidi.isNeutralChar('\u0000'));
    +  assert(goog.i18n.bidi.isNeutralChar('\u0020'));
    +  assert(!goog.i18n.bidi.isNeutralChar('a'));
    +  assert(goog.i18n.bidi.isNeutralChar('!'));
    +  assert(goog.i18n.bidi.isNeutralChar('@'));
    +  assert(goog.i18n.bidi.isNeutralChar('['));
    +  assert(goog.i18n.bidi.isNeutralChar('`'));
    +  assert(goog.i18n.bidi.isNeutralChar('0'));
    +  assert(!goog.i18n.bidi.isNeutralChar('\u05e0'));
    +}
    +
    +function testIsNeutralText() {
    +  assert(goog.i18n.bidi.isNeutralText('123'));
    +  assert(!goog.i18n.bidi.isNeutralText('abc'));
    +  assert(goog.i18n.bidi.isNeutralText('http://abc'));
    +  assert(goog.i18n.bidi.isNeutralText(' 123-()'));
    +  assert(!goog.i18n.bidi.isNeutralText('123a456'));
    +  assert(!goog.i18n.bidi.isNeutralText('123\u05e0456'));
    +  assert(!goog.i18n.bidi.isNeutralText('<input value=\u05e0>123&lt;', false));
    +  assert(goog.i18n.bidi.isNeutralText('<input value=\u05e0>123&lt;', true));
    +}
    +
    +function testHasAnyLtr() {
    +  assert(!goog.i18n.bidi.hasAnyLtr(''));
    +  assert(!goog.i18n.bidi.hasAnyLtr('\u05e0\u05e1\u05e2'));
    +  assert(goog.i18n.bidi.hasAnyLtr('\u05e0\u05e1z\u05e2'));
    +  assert(!goog.i18n.bidi.hasAnyLtr('123\t...  \n'));
    +  assert(goog.i18n.bidi.hasAnyLtr('<br>123&lt;', false));
    +  assert(!goog.i18n.bidi.hasAnyLtr('<br>123&lt;', true));
    +}
    +
    +function testHasAnyRtl() {
    +  assert(!goog.i18n.bidi.hasAnyRtl(''));
    +  assert(!goog.i18n.bidi.hasAnyRtl('abc'));
    +  assert(goog.i18n.bidi.hasAnyRtl('ab\u05e0c'));
    +  assert(!goog.i18n.bidi.hasAnyRtl('123\t...  \n'));
    +  assert(goog.i18n.bidi.hasAnyRtl('<input value=\u05e0>123', false));
    +  assert(!goog.i18n.bidi.hasAnyRtl('<input value=\u05e0>123', true));
    +}
    +
    +function testEndsWithLtr() {
    +  assert(goog.i18n.bidi.endsWithLtr('a'));
    +  assert(goog.i18n.bidi.endsWithLtr('abc'));
    +  assert(goog.i18n.bidi.endsWithLtr('a (!)'));
    +  assert(goog.i18n.bidi.endsWithLtr('a.1'));
    +  assert(goog.i18n.bidi.endsWithLtr('http://www.google.com '));
    +  assert(goog.i18n.bidi.endsWithLtr('\u05e0a'));
    +  assert(goog.i18n.bidi.endsWithLtr(' \u05e0\u05e1a\u05e2\u05e3 a (!)'));
    +  assert(goog.i18n.bidi.endsWithLtr('\u202b\u05d0!\u202c\u200e'));
    +  assert(!goog.i18n.bidi.endsWithLtr(''));
    +  assert(!goog.i18n.bidi.endsWithLtr(' '));
    +  assert(!goog.i18n.bidi.endsWithLtr('1'));
    +  assert(!goog.i18n.bidi.endsWithLtr('\u05e0'));
    +  assert(!goog.i18n.bidi.endsWithLtr('\u05e0 1(!)'));
    +  assert(!goog.i18n.bidi.endsWithLtr('a\u05e0'));
    +  assert(!goog.i18n.bidi.endsWithLtr('a abc\u05e0\u05e1def\u05e2. 1'));
    +  assert(!goog.i18n.bidi.endsWithLtr('\u200f\u202eArtielish\u202c\u200f'));
    +  assert(!goog.i18n.bidi.endsWithLtr(' \u05e0\u05e1a\u05e2 &lt;', true));
    +  assert(goog.i18n.bidi.endsWithLtr(' \u05e0\u05e1a\u05e2 &lt;', false));
    +}
    +
    +function testEndsWithRtl() {
    +  assert(goog.i18n.bidi.endsWithRtl('\u05e0'));
    +  assert(goog.i18n.bidi.endsWithRtl('\u05e0\u05e1\u05e2'));
    +  assert(goog.i18n.bidi.endsWithRtl('\u05e0 (!)'));
    +  assert(goog.i18n.bidi.endsWithRtl('\u05e0.1'));
    +  assert(goog.i18n.bidi.endsWithRtl('http://www.google.com/\u05e0 '));
    +  assert(goog.i18n.bidi.endsWithRtl('a\u05e0'));
    +  assert(goog.i18n.bidi.endsWithRtl(' a abc\u05e0def\u05e3. 1'));
    +  assert(goog.i18n.bidi.endsWithRtl('\u200f\u202eArtielish\u202c\u200f'));
    +  assert(!goog.i18n.bidi.endsWithRtl(''));
    +  assert(!goog.i18n.bidi.endsWithRtl(' '));
    +  assert(!goog.i18n.bidi.endsWithRtl('1'));
    +  assert(!goog.i18n.bidi.endsWithRtl('a'));
    +  assert(!goog.i18n.bidi.endsWithRtl('a 1(!)'));
    +  assert(!goog.i18n.bidi.endsWithRtl('\u05e0a'));
    +  assert(!goog.i18n.bidi.endsWithRtl('\u202b\u05d0!\u202c\u200e'));
    +  assert(!goog.i18n.bidi.endsWithRtl('\u05e0 \u05e0\u05e1ab\u05e2 a (!)'));
    +  assert(goog.i18n.bidi.endsWithRtl(' \u05e0\u05e1a\u05e2 &lt;', true));
    +  assert(!goog.i18n.bidi.endsWithRtl(' \u05e0\u05e1a\u05e2 &lt;', false));
    +}
    +
    +function testGuardBracketInHtml() {
    +  var strWithRtl = 'asc \u05d0 (\u05d0\u05d0\u05d0)';
    +  assertEquals('asc \u05d0 <span dir=rtl>(\u05d0\u05d0\u05d0)</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl));
    +  assertEquals('asc \u05d0 <span dir=rtl>(\u05d0\u05d0\u05d0)</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl, true));
    +  assertEquals('asc \u05d0 <span dir=ltr>(\u05d0\u05d0\u05d0)</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl, false));
    +
    +  var strWithRtl2 = '\u05d0 a (asc:))';
    +  assertEquals('\u05d0 a <span dir=rtl>(asc:))</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl2));
    +  assertEquals('\u05d0 a <span dir=rtl>(asc:))</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl2, true));
    +  assertEquals('\u05d0 a <span dir=ltr>(asc:))</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithRtl2, false));
    +
    +  var strWithoutRtl = 'a (asc) {{123}}';
    +  assertEquals('a <span dir=ltr>(asc)</span> <span dir=ltr>{{123}}</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithoutRtl));
    +  assertEquals('a <span dir=rtl>(asc)</span> <span dir=rtl>{{123}}</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithoutRtl, true));
    +  assertEquals('a <span dir=ltr>(asc)</span> <span dir=ltr>{{123}}</span>',
    +      goog.i18n.bidi.guardBracketInHtml(strWithoutRtl, false));
    +
    +}
    +
    +function testGuardBracketInText() {
    +  var strWithRtl = 'asc \u05d0 (\u05d0\u05d0\u05d0)';
    +  assertEquals('asc \u05d0 \u200f(\u05d0\u05d0\u05d0)\u200f',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl));
    +  assertEquals('asc \u05d0 \u200f(\u05d0\u05d0\u05d0)\u200f',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl, true));
    +  assertEquals('asc \u05d0 \u200e(\u05d0\u05d0\u05d0)\u200e',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl, false));
    +
    +  var strWithRtl2 = '\u05d0 a (asc:))';
    +  assertEquals('\u05d0 a \u200f(asc:))\u200f',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl2));
    +  assertEquals('\u05d0 a \u200f(asc:))\u200f',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl2, true));
    +  assertEquals('\u05d0 a \u200e(asc:))\u200e',
    +      goog.i18n.bidi.guardBracketInText(strWithRtl2, false));
    +
    +  var strWithoutRtl = 'a (asc) {{123}}';
    +  assertEquals('a \u200e(asc)\u200e \u200e{{123}}\u200e',
    +      goog.i18n.bidi.guardBracketInText(strWithoutRtl));
    +  assertEquals('a \u200f(asc)\u200f \u200f{{123}}\u200f',
    +      goog.i18n.bidi.guardBracketInText(strWithoutRtl, true));
    +  assertEquals('a \u200e(asc)\u200e \u200e{{123}}\u200e',
    +      goog.i18n.bidi.guardBracketInText(strWithoutRtl, false));
    +
    +}
    +
    +function testEnforceRtlInHtml() {
    +  var str = '<div> first <br> second </div>';
    +  assertEquals('<div dir=rtl> first <br> second </div>',
    +               goog.i18n.bidi.enforceRtlInHtml(str));
    +  str = 'first second';
    +  assertEquals('\n<span dir=rtl>first second</span>',
    +               goog.i18n.bidi.enforceRtlInHtml(str));
    +}
    +
    +function testEnforceRtlInText() {
    +  var str = 'first second';
    +  assertEquals(RLE + 'first second' + PDF,
    +               goog.i18n.bidi.enforceRtlInText(str));
    +}
    +
    +function testEnforceLtrInHtml() {
    +  var str = '<div> first <br> second </div>';
    +  assertEquals('<div dir=ltr> first <br> second </div>',
    +               goog.i18n.bidi.enforceLtrInHtml(str));
    +  str = 'first second';
    +  assertEquals('\n<span dir=ltr>first second</span>',
    +               goog.i18n.bidi.enforceLtrInHtml(str));
    +}
    +
    +function testEnforceLtrInText() {
    +  var str = 'first second';
    +  assertEquals(LRE + 'first second' + PDF,
    +               goog.i18n.bidi.enforceLtrInText(str));
    +}
    +
    +function testNormalizeHebrewQuote() {
    +  assertEquals('\u05d0\u05f4', goog.i18n.bidi.normalizeHebrewQuote('\u05d0"'));
    +  assertEquals('\u05d0\u05f3', goog.i18n.bidi.normalizeHebrewQuote('\u05d0\''));
    +  assertEquals('\u05d0\u05f4\u05d0\u05f3',
    +               goog.i18n.bidi.normalizeHebrewQuote('\u05d0"\u05d0\''));
    +}
    +
    +function testMirrorCSS() {
    +  var str = 'left:10px;right:20px';
    +  assertEquals('right:10px;left:20px',
    +               goog.i18n.bidi.mirrorCSS(str));
    +  str = 'border:10px 20px 30px 40px';
    +  assertEquals('border:10px 40px 30px 20px',
    +               goog.i18n.bidi.mirrorCSS(str));
    +}
    +
    +function testEstimateDirection() {
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +               goog.i18n.bidi.estimateDirection('', false));
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +               goog.i18n.bidi.estimateDirection(' ', false));
    +  assertEquals(goog.i18n.bidi.Dir.NEUTRAL,
    +               goog.i18n.bidi.estimateDirection('! (...)', false));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection('All-Ascii content', false));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection('-17.0%', false));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection('http://foo/bar/', false));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection(
    +                   'http://foo/bar/?s=\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0' +
    +                   '\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0' +
    +                   '\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0\u05d0',
    +                   false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection('\u05d0', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '9 \u05d0 -> 17.5, 23, 45, 19', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   'http://foo/bar/ \u05d0 http://foo2/bar2/ ' +
    +                   'http://foo3/bar3/', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d0\u05d9\u05df \u05de\u05de\u05e9 ' +
    +                   '\u05de\u05d4 \u05dc\u05e8\u05d0\u05d5\u05ea: ' +
    +                   '\u05dc\u05d0 \u05e6\u05d9\u05dc\u05de\u05ea\u05d9 ' +
    +                   '\u05d4\u05e8\u05d1\u05d4 \u05d5\u05d2\u05dd \u05d0' +
    +                   '\u05dd \u05d4\u05d9\u05d9\u05ea\u05d9 \u05de\u05e6' +
    +                   '\u05dc\u05dd, \u05d4\u05d9\u05d4 \u05e9\u05dd', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05db\u05d0 - http://geek.co.il/gallery/v/2007-06' +
    +                   ' - \u05d0\u05d9\u05df \u05de\u05de\u05e9 \u05de\u05d4 ' +
    +                   '\u05dc\u05e8\u05d0\u05d5\u05ea: \u05dc\u05d0 \u05e6' +
    +                   '\u05d9\u05dc\u05de\u05ea\u05d9 \u05d4\u05e8\u05d1 ' +
    +                   '\u05d5\u05d2\u05dd \u05d0\u05dd \u05d4\u05d9\u05d9' +
    +                   '\u05d9 \u05de\u05e6\u05dc\u05dd, \u05d4\u05d9\u05d4 ' +
    +                   '\u05e9\u05dd \u05d1\u05e2\u05d9\u05e7 \u05d4\u05e8' +
    +                   '\u05d1\u05d4 \u05d0\u05e0\u05e9\u05d9\u05dd. \u05de' +
    +                   '\u05d4 \u05e9\u05db\u05df - \u05d0\u05e4\u05e9\u05e8 ' +
    +                   '\u05dc\u05e0\u05e6\u05dc \u05d0\u05ea \u05d4\u05d4 ' +
    +                   '\u05d3\u05d6\u05de\u05e0\u05d5 \u05dc\u05d4\u05e1' +
    +                   '\u05ea\u05db\u05dc \u05e2\u05dc \u05db\u05de\u05d4 ' +
    +                   '\u05ea\u05de\u05d5\u05e0\u05d5\u05ea \u05de\u05e9' +
    +                   '\u05e9\u05e2\u05d5\u05ea \u05d9\u05e9\u05e0\u05d5 ' +
    +                   '\u05d9\u05d5\u05ea\u05e8 \u05e9\u05d9\u05e9 \u05dc' +
    +                   '\u05d9 \u05d1\u05d0\u05ea\u05e8', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   'CAPTCHA \u05de\u05e9\u05d5\u05db\u05dc\u05dc ' +
    +                   '\u05de\u05d3\u05d9?', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   'Yes Prime Minister \u05e2\u05d3\u05db\u05d5\u05df. ' +
    +                   '\u05e9\u05d0\u05dc\u05d5 \u05d0\u05d5\u05ea\u05d9 ' +
    +                   '\u05de\u05d4 \u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6' +
    +                   '\u05d4 \u05de\u05ea\u05e0\u05d4 \u05dc\u05d7\u05d2',
    +                   false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '17.4.02 \u05e9\u05e2\u05d4:13-20 .15-00 .\u05dc\u05d0 ' +
    +                   '\u05d4\u05d9\u05d9\u05ea\u05d9 \u05db\u05d0\u05df.',
    +                   false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '5710 5720 5730. \u05d4\u05d3\u05dc\u05ea. ' +
    +                   '\u05d4\u05e0\u05e9\u05d9\u05e7\u05d4', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d4\u05d3\u05dc\u05ea http://www.google.com ' +
    +                   'http://www.gmail.com', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u200f\u202eArtielish\u202c\u200f'));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d4\u05d3\u05dc <some quite nasty html mark up>',
    +                   false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d4\u05d3\u05dc <some quite nasty html mark up>',
    +                   true));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d4\u05d3\u05dc\u05ea &amp; &lt; &gt;', false));
    +  assertEquals(goog.i18n.bidi.Dir.RTL,
    +               goog.i18n.bidi.estimateDirection(
    +                   '\u05d4\u05d3\u05dc\u05ea &amp; &lt; &gt;', true));
    +  assertEquals(goog.i18n.bidi.Dir.LTR,
    +               goog.i18n.bidi.estimateDirection(
    +                   'foo/<b>\u05d0</b>', true));
    +}
    +
    +var bidi_text = [];
    +
    +function testDetectRtlDirectionality() {
    +  InitializeSamples();
    +  for (var i = 0; i < bidi_text.length; i++) {
    +    //alert(bidi_text[i].text);
    +    var is_rtl = goog.i18n.bidi.detectRtlDirectionality(bidi_text[i].text,
    +                                                        bidi_text[i].isHtml);
    +    if (is_rtl != bidi_text[i].isRtl) {
    +      var str = '"' + bidi_text[i].text + '" should be ' +
    +                (bidi_text[i].isRtl ? 'rtl' : 'ltr') + ' but detected as ' +
    +                (is_rtl ? 'rtl' : 'ltr');
    +      alert(str);
    +    }
    +    assertEquals(bidi_text[i].isRtl, is_rtl);
    +  }
    +}
    +
    +
    +function SampleItem() {
    +  this.text = '';
    +  this.isRtl = false;
    +}
    +
    +function InitializeSamples() {
    +  var item = new SampleItem;
    +  item.text = 'Pure Ascii content';
    +  item.isRtl = false;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '\u05d0\u05d9\u05df \u05de\u05de\u05e9 \u05de\u05d4 ' +
    +      '\u05dc\u05e8\u05d0\u05d5\u05ea: \u05dc\u05d0 ' +
    +      '\u05e6\u05d9\u05dc\u05de\u05ea\u05d9 \u05d4\u05e8\u05d1\u05d4 ' +
    +      '\u05d5\u05d2\u05dd \u05d0\u05dd \u05d4\u05d9\u05d9\u05ea\u05d9 ' +
    +      '\u05de\u05e6\u05dc\u05dd, \u05d4\u05d9\u05d4 \u05e9\u05dd';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '\u05db\u05d0\u05df - http://geek.co.il/gallery/v/2007-06 - ' +
    +      '\u05d0\u05d9\u05df \u05de\u05de\u05e9 \u05de\u05d4 ' +
    +      '\u05dc\u05e8\u05d0\u05d5\u05ea: ' +
    +      '\u05dc\u05d0 \u05e6\u05d9\u05dc\u05de\u05ea\u05d9 ' +
    +      '\u05d4\u05e8\u05d1\u05d4 \u05d5\u05d2\u05dd \u05d0\u05dd ' +
    +      '\u05d4\u05d9\u05d9\u05ea\u05d9 \u05de\u05e6\u05dc\u05dd, ' +
    +      '\u05d4\u05d9\u05d4 \u05e9\u05dd \u05d1\u05e2\u05d9\u05e7\u05e8 ' +
    +      '\u05d4\u05e8\u05d1\u05d4 \u05d0\u05e0\u05e9\u05d9\u05dd. ' +
    +      '\u05de\u05d4 \u05e9\u05db\u05df - \u05d0\u05e4\u05e9\u05e8 ' +
    +      '\u05dc\u05e0\u05e6\u05dc \u05d0\u05ea ' +
    +      '\u05d4\u05d4\u05d3\u05d6\u05de\u05e0\u05d5\u05ea ' +
    +      '\u05dc\u05d4\u05e1\u05ea\u05db\u05dc \u05e2\u05dc \u05db\u05de\u05d4 ' +
    +      '\u05ea\u05de\u05d5\u05e0\u05d5\u05ea ' +
    +      '\u05de\u05e9\u05e2\u05e9\u05e2\u05d5\u05ea ' +
    +      '\u05d9\u05e9\u05e0\u05d5\u05ea \u05d9\u05d5\u05ea\u05e8 ' +
    +      '\u05e9\u05d9\u05e9 \u05dc\u05d9 \u05d1\u05d0\u05ea\u05e8';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text =
    +      'CAPTCHA \u05de\u05e9\u05d5\u05db\u05dc\u05dc \u05de\u05d3\u05d9?';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +
    +  item = new SampleItem;
    +  item.text = 'Yes Prime Minister \u05e2\u05d3\u05db\u05d5\u05df. ' +
    +      '\u05e9\u05d0\u05dc\u05d5 \u05d0\u05d5\u05ea\u05d9 \u05de\u05d4 ' +
    +      '\u05d0\u05e0\u05d9 \u05e8\u05d5\u05e6\u05d4 \u05de\u05ea\u05e0\u05d4 ' +
    +      '\u05dc\u05d7\u05d2';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '17.4.02 \u05e9\u05e2\u05d4:13-20 .15-00 .\u05dc\u05d0 ' +
    +      '\u05d4\u05d9\u05d9\u05ea\u05d9 \u05db\u05d0\u05df.';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '5710 5720 5730. \u05d4\u05d3\u05dc\u05ea. ' +
    +      '\u05d4\u05e0\u05e9\u05d9\u05e7\u05d4';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text =
    +      '\u05d4\u05d3\u05dc\u05ea http://www.google.com http://www.gmail.com';
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '&gt;\u05d4&lt;';
    +  item.isHtml = true;
    +  item.isRtl = true;
    +  bidi_text.push(item);
    +
    +  item = new SampleItem;
    +  item.text = '&gt;\u05d4&lt;';
    +  item.isHtml = false;
    +  item.isRtl = false;
    +  bidi_text.push(item);
    +
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter.js b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter.js
    new file mode 100644
    index 0000000000..0583f5b2ee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter.js
    @@ -0,0 +1,596 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility for formatting text for display in a potentially
    + * opposite-directionality context without garbling.
    + * Mostly a port of http://go/formatter.cc.
    + */
    +
    +
    +goog.provide('goog.i18n.BidiFormatter');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.i18n.bidi');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.i18n.bidi.Format');
    +
    +
    +
    +/**
    + * Utility class for formatting text for display in a potentially
    + * opposite-directionality context without garbling. Provides the following
    + * functionality:
    + *
    + * 1. BiDi Wrapping
    + * When text in one language is mixed into a document in another, opposite-
    + * directionality language, e.g. when an English business name is embedded in a
    + * Hebrew web page, both the inserted string and the text following it may be
    + * displayed incorrectly unless the inserted string is explicitly separated
    + * from the surrounding text in a "wrapper" that declares its directionality at
    + * the start and then resets it back at the end. This wrapping can be done in
    + * HTML mark-up (e.g. a 'span dir="rtl"' tag) or - only in contexts where
    + * mark-up can not be used - in Unicode BiDi formatting codes (LRE|RLE and PDF).
    + * Providing such wrapping services is the basic purpose of the BiDi formatter.
    + *
    + * 2. Directionality estimation
    + * How does one know whether a string about to be inserted into surrounding
    + * text has the same directionality? Well, in many cases, one knows that this
    + * must be the case when writing the code doing the insertion, e.g. when a
    + * localized message is inserted into a localized page. In such cases there is
    + * no need to involve the BiDi formatter at all. In the remaining cases, e.g.
    + * when the string is user-entered or comes from a database, the language of
    + * the string (and thus its directionality) is not known a priori, and must be
    + * estimated at run-time. The BiDi formatter does this automatically.
    + *
    + * 3. Escaping
    + * When wrapping plain text - i.e. text that is not already HTML or HTML-
    + * escaped - in HTML mark-up, the text must first be HTML-escaped to prevent XSS
    + * attacks and other nasty business. This of course is always true, but the
    + * escaping can not be done after the string has already been wrapped in
    + * mark-up, so the BiDi formatter also serves as a last chance and includes
    + * escaping services.
    + *
    + * Thus, in a single call, the formatter will escape the input string as
    + * specified, determine its directionality, and wrap it as necessary. It is
    + * then up to the caller to insert the return value in the output.
    + *
    + * See http://wiki/Main/TemplatesAndBiDi for more information.
    + *
    + * @param {goog.i18n.bidi.Dir|number|boolean|null} contextDir The context
    + *     directionality, in one of the following formats:
    + *     1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null,
    + *        i.e. unknown, for backward compatibility with legacy calls.
    + *     2. A number (positive = LTR, negative = RTL, 0 = unknown).
    + *     3. A boolean (true = RTL, false = LTR).
    + *     4. A null for unknown directionality.
    + * @param {boolean=} opt_alwaysSpan Whether {@link #spanWrap} should always
    + *     use a 'span' tag, even when the input directionality is neutral or
    + *     matches the context, so that the DOM structure of the output does not
    + *     depend on the combination of directionalities. Default: false.
    + * @constructor
    + * @final
    + */
    +goog.i18n.BidiFormatter = function(contextDir, opt_alwaysSpan) {
    +  /**
    +   * The overall directionality of the context in which the formatter is being
    +   * used.
    +   * @type {?goog.i18n.bidi.Dir}
    +   * @private
    +   */
    +  this.contextDir_ = goog.i18n.bidi.toDir(contextDir, true /* opt_noNeutral */);
    +
    +  /**
    +   * Whether {@link #spanWrap} and similar methods should always use the same
    +   * span structure, regardless of the combination of directionalities, for a
    +   * stable DOM structure.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.alwaysSpan_ = !!opt_alwaysSpan;
    +};
    +
    +
    +/**
    + * @return {?goog.i18n.bidi.Dir} The context directionality.
    + */
    +goog.i18n.BidiFormatter.prototype.getContextDir = function() {
    +  return this.contextDir_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether alwaysSpan is set.
    + */
    +goog.i18n.BidiFormatter.prototype.getAlwaysSpan = function() {
    +  return this.alwaysSpan_;
    +};
    +
    +
    +/**
    + * @param {goog.i18n.bidi.Dir|number|boolean|null} contextDir The context
    + *     directionality, in one of the following formats:
    + *     1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null,
    + *        i.e. unknown.
    + *     2. A number (positive = LTR, negative = RTL, 0 = unknown).
    + *     3. A boolean (true = RTL, false = LTR).
    + *     4. A null for unknown directionality.
    + */
    +goog.i18n.BidiFormatter.prototype.setContextDir = function(contextDir) {
    +  this.contextDir_ = goog.i18n.bidi.toDir(contextDir, true /* opt_noNeutral */);
    +};
    +
    +
    +/**
    + * @param {boolean} alwaysSpan Whether {@link #spanWrap} should always use a
    + *     'span' tag, even when the input directionality is neutral or matches the
    + *     context, so that the DOM structure of the output does not depend on the
    + *     combination of directionalities.
    + */
    +goog.i18n.BidiFormatter.prototype.setAlwaysSpan = function(alwaysSpan) {
    +  this.alwaysSpan_ = alwaysSpan;
    +};
    +
    +
    +/**
    + * Returns the directionality of input argument {@code str}.
    + * Identical to {@link goog.i18n.bidi.estimateDirection}.
    + *
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
    + */
    +goog.i18n.BidiFormatter.prototype.estimateDirection =
    +    goog.i18n.bidi.estimateDirection;
    +
    +
    +/**
    + * Returns true if two given directionalities are opposite.
    + * Note: the implementation is based on the numeric values of the Dir enum.
    + *
    + * @param {?goog.i18n.bidi.Dir} dir1 1st directionality.
    + * @param {?goog.i18n.bidi.Dir} dir2 2nd directionality.
    + * @return {boolean} Whether the directionalities are opposite.
    + * @private
    + */
    +goog.i18n.BidiFormatter.prototype.areDirectionalitiesOpposite_ = function(dir1,
    +    dir2) {
    +  return dir1 * dir2 < 0;
    +};
    +
    +
    +/**
    + * Returns a unicode BiDi mark matching the context directionality (LRM or
    + * RLM) if {@code opt_dirReset}, and if either the directionality or the exit
    + * directionality of {@code str} is opposite to the context directionality.
    + * Otherwise returns the empty string.
    + *
    + * @param {string} str The input text.
    + * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to perform the reset. Default: false.
    + * @return {string} A unicode BiDi mark or the empty string.
    + * @private
    + */
    +goog.i18n.BidiFormatter.prototype.dirResetIfNeeded_ = function(str, dir,
    +    opt_isHtml, opt_dirReset) {
    +  // endsWithRtl and endsWithLtr are called only if needed (short-circuit).
    +  if (opt_dirReset &&
    +      (this.areDirectionalitiesOpposite_(dir, this.contextDir_) ||
    +       (this.contextDir_ == goog.i18n.bidi.Dir.LTR &&
    +        goog.i18n.bidi.endsWithRtl(str, opt_isHtml)) ||
    +       (this.contextDir_ == goog.i18n.bidi.Dir.RTL &&
    +        goog.i18n.bidi.endsWithLtr(str, opt_isHtml)))) {
    +    return this.contextDir_ == goog.i18n.bidi.Dir.LTR ?
    +        goog.i18n.bidi.Format.LRM : goog.i18n.bidi.Format.RLM;
    +  } else {
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * Returns "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" if
    + * it is LTR. In case it's NEUTRAL, returns "rtl" if the context directionality
    + * is RTL, and "ltr" otherwise.
    + * Needed for GXP, which can't handle dirAttr.
    + * Example use case:
    + * &lt;td expr:dir='bidiFormatter.dirAttrValue(foo)'&gt;
    + *   &lt;gxp:eval expr='foo'&gt;
    + * &lt;/td&gt;
    + *
    + * @param {string} str Text whose directionality is to be estimated.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {string} "rtl" or "ltr", according to the logic described above.
    + */
    +goog.i18n.BidiFormatter.prototype.dirAttrValue = function(str, opt_isHtml) {
    +  return this.knownDirAttrValue(this.estimateDirection(str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Returns "rtl" if the given directionality is RTL, and "ltr" if it is LTR. In
    + * case it's NEUTRAL, returns "rtl" if the context directionality is RTL, and
    + * "ltr" otherwise.
    + *
    + * @param {goog.i18n.bidi.Dir} dir A directionality.
    + * @return {string} "rtl" or "ltr", according to the logic described above.
    + */
    +goog.i18n.BidiFormatter.prototype.knownDirAttrValue = function(dir) {
    +  var resolvedDir = dir == goog.i18n.bidi.Dir.NEUTRAL ? this.contextDir_ : dir;
    +  return resolvedDir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
    +};
    +
    +
    +/**
    + * Returns 'dir="ltr"' or 'dir="rtl"', depending on {@code str}'s estimated
    + * directionality, if it is not the same as the context directionality.
    + * Otherwise, returns the empty string.
    + *
    + * @param {string} str Text whose directionality is to be estimated.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
    + *     LTR text in non-LTR context; else, the empty string.
    + */
    +goog.i18n.BidiFormatter.prototype.dirAttr = function(str, opt_isHtml) {
    +  return this.knownDirAttr(this.estimateDirection(str, opt_isHtml));
    +};
    +
    +
    +/**
    + * Returns 'dir="ltr"' or 'dir="rtl"', depending on the given directionality, if
    + * it is not the same as the context directionality. Otherwise, returns the
    + * empty string.
    + *
    + * @param {goog.i18n.bidi.Dir} dir A directionality.
    + * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
    + *     LTR text in non-LTR context; else, the empty string.
    + */
    +goog.i18n.BidiFormatter.prototype.knownDirAttr = function(dir) {
    +  if (dir != this.contextDir_) {
    +    return dir == goog.i18n.bidi.Dir.RTL ? 'dir="rtl"' :
    +        dir == goog.i18n.bidi.Dir.LTR ? 'dir="ltr"' : '';
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Formats a string of unknown directionality for use in HTML output of the
    + * context directionality, so an opposite-directionality string is neither
    + * garbled nor garbles what follows it.
    + * The algorithm: estimates the directionality of input argument {@code html}.
    + * In case its directionality doesn't match the context directionality, wraps it
    + * with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"' or
    + * 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
    + * with 'span', skipping just the dir attribute when it's not needed.
    + *
    + * If {@code opt_dirReset}, and if the overall directionality or the exit
    + * directionality of {@code str} are opposite to the context directionality, a
    + * trailing unicode BiDi mark matching the context directionality is appened
    + * (LRM or RLM).
    + *
    + * @param {!goog.html.SafeHtml} html The input HTML.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code html}. Default: true.
    + * @return {!goog.html.SafeHtml} Input text after applying the processing.
    + */
    +goog.i18n.BidiFormatter.prototype.spanWrapSafeHtml = function(html,
    +    opt_dirReset) {
    +  return this.spanWrapSafeHtmlWithKnownDir(null, html, opt_dirReset);
    +};
    +
    +
    +/**
    + * String version of {@link #spanWrapSafeHtml}.
    + *
    + * If !{@code opt_isHtml}, HTML-escapes {@code str} regardless of wrapping.
    + *
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {string} Input text after applying the above processing.
    + */
    +goog.i18n.BidiFormatter.prototype.spanWrap = function(str, opt_isHtml,
    +    opt_dirReset) {
    +  return this.spanWrapWithKnownDir(null, str, opt_isHtml, opt_dirReset);
    +};
    +
    +
    +/**
    + * Formats a string of given directionality for use in HTML output of the
    + * context directionality, so an opposite-directionality string is neither
    + * garbled nor garbles what follows it.
    + * The algorithm: If {@code dir} doesn't match the context directionality, wraps
    + * {@code html} with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"'
    + * or 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
    + * with 'span', skipping just the dir attribute when it's not needed.
    + *
    + * If {@code opt_dirReset}, and if {@code dir} or the exit directionality of
    + * {@code html} are opposite to the context directionality, a trailing unicode
    + * BiDi mark matching the context directionality is appened (LRM or RLM).
    + *
    + * @param {?goog.i18n.bidi.Dir} dir {@code html}'s overall directionality, or
    + *     null if unknown and needs to be estimated.
    + * @param {!goog.html.SafeHtml} html The input HTML.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code html}. Default: true.
    + * @return {!goog.html.SafeHtml} Input text after applying the processing.
    + */
    +goog.i18n.BidiFormatter.prototype.spanWrapSafeHtmlWithKnownDir = function(dir,
    +    html, opt_dirReset) {
    +  if (dir == null) {
    +    dir = this.estimateDirection(goog.html.SafeHtml.unwrap(html), true);
    +  }
    +  return this.spanWrapWithKnownDir_(dir, html, opt_dirReset);
    +};
    +
    +
    +/**
    + * String version of {@link #spanWrapSafeHtmlWithKnownDir}.
    + *
    + * If !{@code opt_isHtml}, HTML-escapes {@code str} regardless of wrapping.
    + *
    + * @param {?goog.i18n.bidi.Dir} dir {@code str}'s overall directionality, or
    + *     null if unknown and needs to be estimated.
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {string} Input text after applying the above processing.
    + */
    +goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir = function(dir, str,
    +    opt_isHtml, opt_dirReset) {
    +  // We're calling legacy conversions, but quickly unwrapping it.
    +  var html = opt_isHtml ? goog.html.legacyconversions.safeHtmlFromString(str) :
    +      goog.html.SafeHtml.htmlEscape(str);
    +  return goog.html.SafeHtml.unwrap(
    +      this.spanWrapSafeHtmlWithKnownDir(dir, html, opt_dirReset));
    +};
    +
    +
    +/**
    + * The internal implementation of spanWrapSafeHtmlWithKnownDir for non-null dir,
    + * to help the compiler optimize.
    + *
    + * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
    + * @param {!goog.html.SafeHtml} html The input HTML.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {!goog.html.SafeHtml} Input text after applying the above processing.
    + * @private
    + */
    +goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir_ = function(dir, html,
    +    opt_dirReset) {
    +  opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
    +
    +  var result;
    +  // Whether to add the "dir" attribute.
    +  var dirCondition =
    +      dir != goog.i18n.bidi.Dir.NEUTRAL && dir != this.contextDir_;
    +  if (this.alwaysSpan_ || dirCondition) {  // Wrap is needed
    +    var dirAttribute;
    +    if (dirCondition) {
    +      dirAttribute = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
    +    }
    +    result = goog.html.SafeHtml.create('span', {'dir': dirAttribute}, html);
    +  } else {
    +    result = html;
    +  }
    +  var str = goog.html.SafeHtml.unwrap(html);
    +  result = goog.html.SafeHtml.concatWithDir(goog.i18n.bidi.Dir.NEUTRAL, result,
    +      this.dirResetIfNeeded_(str, dir, true, opt_dirReset));
    +  return result;
    +};
    +
    +
    +/**
    + * Formats a string of unknown directionality for use in plain-text output of
    + * the context directionality, so an opposite-directionality string is neither
    + * garbled nor garbles what follows it.
    + * As opposed to {@link #spanWrap}, this makes use of unicode BiDi formatting
    + * characters. In HTML, its *only* valid use is inside of elements that do not
    + * allow mark-up, e.g. an 'option' tag.
    + * The algorithm: estimates the directionality of input argument {@code str}.
    + * In case it doesn't match  the context directionality, wraps it with Unicode
    + * BiDi formatting characters: RLE{@code str}PDF for RTL text, and
    + * LRE{@code str}PDF for LTR text.
    + *
    + * If {@code opt_dirReset}, and if the overall directionality or the exit
    + * directionality of {@code str} are opposite to the context directionality, a
    + * trailing unicode BiDi mark matching the context directionality is appended
    + * (LRM or RLM).
    + *
    + * Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
    + * The return value can be HTML-escaped as necessary.
    + *
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {string} Input text after applying the above processing.
    + */
    +goog.i18n.BidiFormatter.prototype.unicodeWrap = function(str, opt_isHtml,
    +    opt_dirReset) {
    +  return this.unicodeWrapWithKnownDir(null, str, opt_isHtml, opt_dirReset);
    +};
    +
    +
    +/**
    + * Formats a string of given directionality for use in plain-text output of the
    + * context directionality, so an opposite-directionality string is neither
    + * garbled nor garbles what follows it.
    + * As opposed to {@link #spanWrapWithKnownDir}, makes use of unicode BiDi
    + * formatting characters. In HTML, its *only* valid use is inside of elements
    + * that do not allow mark-up, e.g. an 'option' tag.
    + * The algorithm: If {@code dir} doesn't match the context directionality, wraps
    + * {@code str} with Unicode BiDi formatting characters: RLE{@code str}PDF for
    + * RTL text, and LRE{@code str}PDF for LTR text.
    + *
    + * If {@code opt_dirReset}, and if the overall directionality or the exit
    + * directionality of {@code str} are opposite to the context directionality, a
    + * trailing unicode BiDi mark matching the context directionality is appended
    + * (LRM or RLM).
    + *
    + * Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
    + * The return value can be HTML-escaped as necessary.
    + *
    + * @param {?goog.i18n.bidi.Dir} dir {@code str}'s overall directionality, or
    + *     null if unknown and needs to be estimated.
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {string} Input text after applying the above processing.
    + */
    +goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir = function(dir, str,
    +    opt_isHtml, opt_dirReset) {
    +  if (dir == null) {
    +    dir = this.estimateDirection(str, opt_isHtml);
    +  }
    +  return this.unicodeWrapWithKnownDir_(dir, str, opt_isHtml, opt_dirReset);
    +};
    +
    +
    +/**
    + * The internal implementation of unicodeWrapWithKnownDir for non-null dir, to
    + * help the compiler optimize.
    + *
    + * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
    + *     matching the context directionality, when needed, to prevent the possible
    + *     garbling of whatever may follow {@code str}. Default: true.
    + * @return {string} Input text after applying the above processing.
    + * @private
    + */
    +goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir_ = function(dir, str,
    +    opt_isHtml, opt_dirReset) {
    +  opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
    +  var result = [];
    +  if (dir != goog.i18n.bidi.Dir.NEUTRAL && dir != this.contextDir_) {
    +    result.push(dir == goog.i18n.bidi.Dir.RTL ? goog.i18n.bidi.Format.RLE :
    +                                                goog.i18n.bidi.Format.LRE);
    +    result.push(str);
    +    result.push(goog.i18n.bidi.Format.PDF);
    +  } else {
    +    result.push(str);
    +  }
    +
    +  result.push(this.dirResetIfNeeded_(str, dir, opt_isHtml, opt_dirReset));
    +  return result.join('');
    +};
    +
    +
    +/**
    + * Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
    + * if the directionality or the exit directionality of {@code str} are opposite
    + * to the context directionality. Otherwise returns the empty string.
    + *
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {string} A Unicode bidi mark matching the global directionality or
    + *     the empty string.
    + */
    +goog.i18n.BidiFormatter.prototype.markAfter = function(str, opt_isHtml) {
    +  return this.markAfterKnownDir(null, str, opt_isHtml);
    +};
    +
    +
    +/**
    + * Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
    + * if the given directionality or the exit directionality of {@code str} are
    + * opposite to the context directionality. Otherwise returns the empty string.
    + *
    + * @param {?goog.i18n.bidi.Dir} dir {@code str}'s overall directionality, or
    + *     null if unknown and needs to be estimated.
    + * @param {string} str The input text.
    + * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
    + *     Default: false.
    + * @return {string} A Unicode bidi mark matching the global directionality or
    + *     the empty string.
    + */
    +goog.i18n.BidiFormatter.prototype.markAfterKnownDir = function(
    +    dir, str, opt_isHtml) {
    +  if (dir == null) {
    +    dir = this.estimateDirection(str, opt_isHtml);
    +  }
    +  return this.dirResetIfNeeded_(str, dir, opt_isHtml, true);
    +};
    +
    +
    +/**
    + * Returns the Unicode BiDi mark matching the context directionality (LRM for
    + * LTR context directionality, RLM for RTL context directionality), or the
    + * empty string for neutral / unknown context directionality.
    + *
    + * @return {string} LRM for LTR context directionality and RLM for RTL context
    + *     directionality.
    + */
    +goog.i18n.BidiFormatter.prototype.mark = function() {
    +  switch (this.contextDir_) {
    +    case (goog.i18n.bidi.Dir.LTR):
    +      return goog.i18n.bidi.Format.LRM;
    +    case (goog.i18n.bidi.Dir.RTL):
    +      return goog.i18n.bidi.Format.RLM;
    +    default:
    +      return '';
    +  }
    +};
    +
    +
    +/**
    + * Returns 'right' for RTL context directionality. Otherwise (LTR or neutral /
    + * unknown context directionality) returns 'left'.
    + *
    + * @return {string} 'right' for RTL context directionality and 'left' for other
    + *     context directionality.
    + */
    +goog.i18n.BidiFormatter.prototype.startEdge = function() {
    +  return this.contextDir_ == goog.i18n.bidi.Dir.RTL ?
    +      goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
    +};
    +
    +
    +/**
    + * Returns 'left' for RTL context directionality. Otherwise (LTR or neutral /
    + * unknown context directionality) returns 'right'.
    + *
    + * @return {string} 'left' for RTL context directionality and 'right' for other
    + *     context directionality.
    + */
    +goog.i18n.BidiFormatter.prototype.endEdge = function() {
    +  return this.contextDir_ == goog.i18n.bidi.Dir.RTL ?
    +      goog.i18n.bidi.LEFT : goog.i18n.bidi.RIGHT;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.html b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.html
    new file mode 100644
    index 0000000000..752ac30ee3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.BidiFormatter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.BidiFormatterTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.js b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.js
    new file mode 100644
    index 0000000000..9129e31ccd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/bidiformatter_test.js
    @@ -0,0 +1,535 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.BidiFormatterTest');
    +goog.setTestOnly('goog.i18n.BidiFormatterTest');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.i18n.BidiFormatter');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.i18n.bidi.Format');
    +goog.require('goog.testing.jsunit');
    +
    +var LRM = goog.i18n.bidi.Format.LRM;
    +var RLM = goog.i18n.bidi.Format.RLM;
    +var LRE = goog.i18n.bidi.Format.LRE;
    +var RLE = goog.i18n.bidi.Format.RLE;
    +var PDF = goog.i18n.bidi.Format.PDF;
    +var LTR = goog.i18n.bidi.Dir.LTR;
    +var RTL = goog.i18n.bidi.Dir.RTL;
    +var NEUTRAL = goog.i18n.bidi.Dir.NEUTRAL;
    +var he = '\u05e0\u05e1';
    +var en = 'abba';
    +var html = '&lt;';
    +var longEn = 'abba sabba gabba ';
    +var longHe = '\u05e0 \u05e1 \u05e0 ';
    +var ltrFmt = new goog.i18n.BidiFormatter(LTR, false);  // LTR context
    +var rtlFmt = new goog.i18n.BidiFormatter(RTL, false);  // RTL context
    +var unkFmt = new goog.i18n.BidiFormatter(null, false);  // unknown context
    +
    +function testGetContextDir() {
    +  assertEquals(null, unkFmt.getContextDir());
    +  assertEquals(null, new goog.i18n.BidiFormatter(NEUTRAL).getContextDir());
    +  assertEquals(LTR, ltrFmt.getContextDir());
    +  assertEquals(RTL, rtlFmt.getContextDir());
    +}
    +
    +function testEstimateDirection() {
    +  assertEquals(NEUTRAL, ltrFmt.estimateDirection(''));
    +  assertEquals(NEUTRAL, rtlFmt.estimateDirection(''));
    +  assertEquals(NEUTRAL, unkFmt.estimateDirection(''));
    +  assertEquals(LTR, ltrFmt.estimateDirection(en));
    +  assertEquals(LTR, rtlFmt.estimateDirection(en));
    +  assertEquals(LTR, unkFmt.estimateDirection(en));
    +  assertEquals(RTL, ltrFmt.estimateDirection(he));
    +  assertEquals(RTL, rtlFmt.estimateDirection(he));
    +  assertEquals(RTL, unkFmt.estimateDirection(he));
    +
    +  // Text contains HTML or HTML-escaping.
    +  assertEquals(LTR, ltrFmt.estimateDirection(
    +      '<some sort of tag/>' + he + ' &amp;', false));
    +  assertEquals(RTL, ltrFmt.estimateDirection(
    +      '<some sort of tag/>' + he + ' &amp;', true));
    +}
    +
    +function testDirAttrValue() {
    +  assertEquals('overall dir is RTL, context dir is LTR',
    +      'rtl', ltrFmt.dirAttrValue(he, true));
    +  assertEquals('overall dir and context dir are RTL',
    +      'rtl', rtlFmt.dirAttrValue(he, true));
    +  assertEquals('overall dir is LTR, context dir is RTL',
    +      'ltr', rtlFmt.dirAttrValue(en, true));
    +  assertEquals('overall dir and context dir are LTR',
    +      'ltr', ltrFmt.dirAttrValue(en, true));
    +
    +  // Input's directionality is neutral.
    +  assertEquals('ltr', ltrFmt.dirAttrValue('', true));
    +  assertEquals('rtl', rtlFmt.dirAttrValue('', true));
    +  assertEquals('ltr', unkFmt.dirAttrValue('', true));
    +
    +  // Text contains HTML or HTML-escaping:
    +  assertEquals('rtl',
    +      ltrFmt.dirAttrValue(he + '<some sort of an HTML tag>', true));
    +  assertEquals('ltr',
    +      ltrFmt.dirAttrValue(he + '<some sort of an HTML tag>', false));
    +}
    +
    +function testKnownDirAttrValue() {
    +  assertEquals('rtl', ltrFmt.knownDirAttrValue(RTL));
    +  assertEquals('rtl', rtlFmt.knownDirAttrValue(RTL));
    +  assertEquals('rtl', unkFmt.knownDirAttrValue(RTL));
    +  assertEquals('ltr', rtlFmt.knownDirAttrValue(LTR));
    +  assertEquals('ltr', ltrFmt.knownDirAttrValue(LTR));
    +  assertEquals('ltr', unkFmt.knownDirAttrValue(LTR));
    +
    +  // Input directionality is neutral.
    +  assertEquals('ltr', ltrFmt.knownDirAttrValue(NEUTRAL));
    +  assertEquals('rtl', rtlFmt.knownDirAttrValue(NEUTRAL));
    +  assertEquals('ltr', unkFmt.knownDirAttrValue(NEUTRAL));
    +}
    +
    +function testDirAttr() {
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR)',
    +      'dir="rtl"', ltrFmt.dirAttr(he, true));
    +  assertEquals('overall dir (RTL) doesnt match context dir (unknown)',
    +      'dir="rtl"', unkFmt.dirAttr(he, true));
    +  assertEquals('overall dir matches context dir (RTL)',
    +      '', rtlFmt.dirAttr(he, true));
    +
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL)',
    +      'dir="ltr"', rtlFmt.dirAttr(en, true));
    +  assertEquals('overall dir (LTR) doesnt match context dir (unknown)',
    +      'dir="ltr"', unkFmt.dirAttr(en, true));
    +  assertEquals('overall dir matches context dir (LTR)',
    +      '', ltrFmt.dirAttr(en, true));
    +
    +  assertEquals('neutral in RTL context',
    +      '', rtlFmt.dirAttr('.', true));
    +  assertEquals('neutral in LTR context',
    +      '', ltrFmt.dirAttr('.', true));
    +  assertEquals('neutral in unknown context',
    +      '', unkFmt.dirAttr('.', true));
    +
    +  // Text contains HTML or HTML-escaping:
    +  assertEquals('dir="rtl"',
    +      ltrFmt.dirAttr(he + '<some sort of an HTML tag>', true));
    +  assertEquals('',
    +      ltrFmt.dirAttr(he + '<some sort of an HTML tag>', false));
    +}
    +
    +function testKnownDirAttr() {
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR)',
    +      'dir="rtl"', ltrFmt.knownDirAttr(RTL));
    +  assertEquals('overall dir matches context dir (RTL)',
    +      '', rtlFmt.knownDirAttr(RTL));
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL)',
    +      'dir="ltr"', rtlFmt.knownDirAttr(LTR));
    +  assertEquals('overall dir matches context dir (LTR)',
    +      '', ltrFmt.knownDirAttr(LTR));
    +}
    +
    +function testSpanWrap() {
    +  // alwaysSpan is false and opt_isHtml is true, unless specified otherwise.
    +  assertEquals('overall dir matches context dir (LTR), no dirReset',
    +      en, ltrFmt.spanWrap(en, true, false));
    +  assertEquals('overall dir matches context dir (LTR), dirReset',
    +      en, ltrFmt.spanWrap(en, true, true));
    +  assertEquals('overall dir matches context dir (RTL), no dirReset',
    +      he, rtlFmt.spanWrap(he, true, false));
    +  assertEquals('overall dir matches context dir (RTL), dirReset',
    +      he, rtlFmt.spanWrap(he, true, true));
    +
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR), ' +
    +      'no dirReset',
    +      '<span dir="rtl">' + he + '<\/span>', ltrFmt.spanWrap(he, true, false));
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR), dirReset',
    +      '<span dir="rtl">' + he + '<\/span>' + LRM,
    +      ltrFmt.spanWrap(he, true, true));
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL), ' +
    +      'no dirReset',
    +      '<span dir="ltr">' + en + '<\/span>', rtlFmt.spanWrap(en, true, false));
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL), dirReset',
    +      '<span dir="ltr">' + en + '<\/span>' + RLM,
    +      rtlFmt.spanWrap(en, true, true));
    +  assertEquals('overall dir (LTR) doesnt match context dir (unknown), ' +
    +      'no dirReset',
    +      '<span dir="ltr">' + en + '<\/span>', unkFmt.spanWrap(en, true, false));
    +  assertEquals('overall dir (RTL) doesnt match context dir (unknown), ' +
    +      'dirReset',
    +      '<span dir="rtl">' + he + '<\/span>', unkFmt.spanWrap(he, true, true));
    +  assertEquals('overall dir (neutral) doesnt match context dir (LTR), ' +
    +      'dirReset',
    +      '',
    +      ltrFmt.spanWrap('', true, true));
    +
    +  assertEquals('exit dir (but not overall dir) is opposite to context dir, ' +
    +      'dirReset',
    +      longEn + he + html + LRM,
    +      ltrFmt.spanWrap(longEn + he + html, true, true));
    +  assertEquals('overall dir (but not exit dir) is opposite to context dir, ' +
    +      'dirReset',
    +      '<span dir="ltr">' + longEn + he + '<\/span>' + RLM,
    +      rtlFmt.spanWrap(longEn + he, true, true));
    +
    +  assertEquals('input is plain text (not escaped)',
    +      '&lt;br&gt;' + en,
    +      ltrFmt.spanWrap('<br>' + en, false, false));
    +
    +  var ltrAlwaysSpanFmt = new goog.i18n.BidiFormatter(LTR, true);
    +  var rtlAlwaysSpanFmt = new goog.i18n.BidiFormatter(RTL, true);
    +  var unkAlwaysSpanFmt = new goog.i18n.BidiFormatter(null, true);
    +
    +  assertEquals('alwaysSpan, overall dir matches context dir (LTR), ' +
    +      'no dirReset',
    +      '<span>' + en + '<\/span>', ltrAlwaysSpanFmt.spanWrap(en, true, false));
    +  assertEquals('alwaysSpan, overall dir matches context dir (LTR), dirReset',
    +      '<span>' + en + '<\/span>', ltrAlwaysSpanFmt.spanWrap(en, true, true));
    +  assertEquals('alwaysSpan, overall dir matches context dir (RTL), ' +
    +      'no dirReset',
    +      '<span>' + he + '<\/span>', rtlAlwaysSpanFmt.spanWrap(he, true, false));
    +  assertEquals('alwaysSpan, overall dir matches context dir (RTL), dirReset',
    +      '<span>' + he + '<\/span>', rtlAlwaysSpanFmt.spanWrap(he, true, true));
    +
    +  assertEquals('alwaysSpan, overall dir (RTL) doesnt match ' +
    +      'context dir (LTR), no dirReset',
    +      '<span dir="rtl">' + he + '<\/span>',
    +      ltrAlwaysSpanFmt.spanWrap(he, true, false));
    +  assertEquals('alwaysSpan, overall dir (RTL) doesnt match ' +
    +      'context dir (LTR), dirReset',
    +      '<span dir="rtl">' + he + '<\/span>' + LRM,
    +      ltrAlwaysSpanFmt.spanWrap(he, true, true));
    +  assertEquals('alwaysSpan, overall dir (neutral) doesnt match ' +
    +      'context dir (LTR), dirReset',
    +      '<span></span>',
    +      ltrAlwaysSpanFmt.spanWrap('', true, true));
    +}
    +
    +function testSpanWrapSafeHtml() {
    +  var html = goog.html.SafeHtml.htmlEscape('a');
    +  var wrapped = rtlFmt.spanWrapSafeHtml(html, false);
    +  assertHtmlEquals('<span dir="ltr">a</span>', wrapped);
    +  assertEquals(NEUTRAL, wrapped.getDirection());
    +}
    +
    +function testSpanWrapWithKnownDir() {
    +  assertEquals('known LTR in LTR context',
    +      en, ltrFmt.spanWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in LTR context',
    +      en, ltrFmt.spanWrapWithKnownDir(null, en));
    +  assertEquals('overall LTR but exit RTL in LTR context',
    +      he + LRM, ltrFmt.spanWrapWithKnownDir(LTR, he));
    +  assertEquals('known RTL in LTR context',
    +      '<span dir="rtl">' + he + '<\/span>' + LRM,
    +      ltrFmt.spanWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in LTR context',
    +      '<span dir="rtl">' + he + '<\/span>' + LRM,
    +      ltrFmt.spanWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in LTR context',
    +      '<span dir="rtl">' + en + '<\/span>' + LRM,
    +      ltrFmt.spanWrapWithKnownDir(RTL, en));
    +  assertEquals('known neutral in LTR context',
    +      '.', ltrFmt.spanWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in LTR context',
    +      '.', ltrFmt.spanWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      en, ltrFmt.spanWrapWithKnownDir(NEUTRAL, en));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      he + LRM, ltrFmt.spanWrapWithKnownDir(NEUTRAL, he));
    +
    +  assertEquals('known RTL in RTL context',
    +      he, rtlFmt.spanWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in RTL context',
    +      he, rtlFmt.spanWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in RTL context',
    +      en + RLM, rtlFmt.spanWrapWithKnownDir(RTL, en));
    +  assertEquals('known LTR in RTL context',
    +      '<span dir="ltr">' + en + '<\/span>' + RLM,
    +      rtlFmt.spanWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in RTL context',
    +      '<span dir="ltr">' + en + '<\/span>' + RLM,
    +      rtlFmt.spanWrapWithKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in RTL context',
    +      '<span dir="ltr">' + he + '<\/span>' + RLM,
    +      rtlFmt.spanWrapWithKnownDir(LTR, he));
    +  assertEquals('known neutral in RTL context',
    +      '.', rtlFmt.spanWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in RTL context',
    +      '.', rtlFmt.spanWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      he, rtlFmt.spanWrapWithKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      en + RLM, rtlFmt.spanWrapWithKnownDir(NEUTRAL, en));
    +
    +  assertEquals('known RTL in unknown context',
    +      '<span dir="rtl">' + he + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in unknown context',
    +      '<span dir="rtl">' + he + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in unknown context',
    +      '<span dir="rtl">' + en + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(RTL, en));
    +  assertEquals('known LTR in unknown context',
    +      '<span dir="ltr">' + en + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in unknown context',
    +      '<span dir="ltr">' + en + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in unknown context',
    +      '<span dir="ltr">' + he + '<\/span>',
    +      unkFmt.spanWrapWithKnownDir(LTR, he));
    +  assertEquals('known neutral in unknown context',
    +      '.', unkFmt.spanWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in unknown context',
    +      '.', unkFmt.spanWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in unknown context',
    +      he, unkFmt.spanWrapWithKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in unknown context',
    +      en, unkFmt.spanWrapWithKnownDir(NEUTRAL, en));
    +}
    +
    +function testSpanWrapSafeHtmlWithKnownDir() {
    +  var html = goog.html.SafeHtml.htmlEscape('a');
    +  assertHtmlEquals('<span dir="ltr">a</span>',
    +      rtlFmt.spanWrapSafeHtmlWithKnownDir(LTR, html, false));
    +}
    +
    +function testUnicodeWrap() {
    +  // opt_isHtml is true, unless specified otherwise.
    +  assertEquals('overall dir matches context dir (LTR), no dirReset',
    +      en, ltrFmt.unicodeWrap(en, true, false));
    +  assertEquals('overall dir matches context dir (LTR), dirReset',
    +      en, ltrFmt.unicodeWrap(en, true, true));
    +  assertEquals('overall dir matches context dir (RTL), no dirReset',
    +      he, rtlFmt.unicodeWrap(he, true, false));
    +  assertEquals('overall dir matches context dir (RTL), dirReset',
    +      he, rtlFmt.unicodeWrap(he, true, true));
    +
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR), ' +
    +      'no dirReset',
    +      RLE + he + PDF, ltrFmt.unicodeWrap(he, true, false));
    +  assertEquals('overall dir (RTL) doesnt match context dir (LTR), dirReset',
    +      RLE + he + PDF + LRM, ltrFmt.unicodeWrap(he, true, true));
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL), ' +
    +      'no dirReset',
    +      LRE + en + PDF, rtlFmt.unicodeWrap(en, true, false));
    +  assertEquals('overall dir (LTR) doesnt match context dir (RTL), dirReset',
    +      LRE + en + PDF + RLM, rtlFmt.unicodeWrap(en, true, true));
    +  assertEquals('overall dir (LTR) doesnt match context dir (unknown), ' +
    +      'no dirReset',
    +      LRE + en + PDF, unkFmt.unicodeWrap(en, true, false));
    +  assertEquals('overall dir (RTL) doesnt match context dir (unknown), ' +
    +      'dirReset',
    +      RLE + he + PDF, unkFmt.unicodeWrap(he, true, true));
    +  assertEquals('overall dir (neutral) doesnt match context dir (LTR), ' +
    +      'dirReset',
    +      '',
    +      ltrFmt.unicodeWrap('', true, true));
    +
    +  assertEquals('exit dir (but not overall dir) is opposite to context dir, ' +
    +      'dirReset',
    +      longEn + he + html + LRM,
    +      ltrFmt.unicodeWrap(longEn + he + html, true, true));
    +  assertEquals('overall dir (but not exit dir) is opposite to context dir, ' +
    +      'dirReset',
    +      LRE + longEn + he + PDF + RLM,
    +      rtlFmt.unicodeWrap(longEn + he, true, true));
    +}
    +
    +function testUnicodeWrapWithKnownDir() {
    +  assertEquals('known LTR in LTR context',
    +      en, ltrFmt.unicodeWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in LTR context',
    +      en, ltrFmt.unicodeWrapWithKnownDir(null, en));
    +  assertEquals('overall LTR but exit RTL in LTR context',
    +      he + LRM, ltrFmt.unicodeWrapWithKnownDir(LTR, he));
    +  assertEquals('known RTL in LTR context',
    +      RLE + he + PDF + LRM,
    +      ltrFmt.unicodeWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in LTR context',
    +      RLE + he + PDF + LRM,
    +      ltrFmt.unicodeWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in LTR context',
    +      RLE + en + PDF + LRM,
    +      ltrFmt.unicodeWrapWithKnownDir(RTL, en));
    +  assertEquals('known neutral in LTR context',
    +      '.', ltrFmt.unicodeWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in LTR context',
    +      '.', ltrFmt.unicodeWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      en, ltrFmt.unicodeWrapWithKnownDir(NEUTRAL, en));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      he + LRM,
    +      ltrFmt.unicodeWrapWithKnownDir(NEUTRAL, he));
    +
    +  assertEquals('known RTL in RTL context',
    +      he, rtlFmt.unicodeWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in RTL context',
    +      he, rtlFmt.unicodeWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in RTL context',
    +      en + RLM, rtlFmt.unicodeWrapWithKnownDir(RTL, en));
    +  assertEquals('known LTR in RTL context',
    +      LRE + en + PDF + RLM,
    +      rtlFmt.unicodeWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in RTL context',
    +      LRE + en + PDF + RLM,
    +      rtlFmt.unicodeWrapWithKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in RTL context',
    +      LRE + he + PDF + RLM,
    +      rtlFmt.unicodeWrapWithKnownDir(LTR, he));
    +  assertEquals('known neutral in RTL context',
    +      '.', rtlFmt.unicodeWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in RTL context',
    +      '.', rtlFmt.unicodeWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      he, rtlFmt.unicodeWrapWithKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      en + RLM,
    +      rtlFmt.unicodeWrapWithKnownDir(NEUTRAL, en));
    +
    +  assertEquals('known RTL in unknown context',
    +      RLE + he + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(RTL, he));
    +  assertEquals('unknown RTL in unknown context',
    +      RLE + he + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in unknown context',
    +      RLE + en + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(RTL, en));
    +  assertEquals('known LTR in unknown context',
    +      LRE + en + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(LTR, en));
    +  assertEquals('unknown LTR in unknown context',
    +      LRE + en + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in unknown context',
    +      LRE + he + PDF,
    +      unkFmt.unicodeWrapWithKnownDir(LTR, he));
    +  assertEquals('known neutral in unknown context',
    +      '.', unkFmt.unicodeWrapWithKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in unknown context',
    +      '.', unkFmt.unicodeWrapWithKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in unknown context',
    +      he, unkFmt.unicodeWrapWithKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in unknown context',
    +      en, unkFmt.unicodeWrapWithKnownDir(NEUTRAL, en));
    +}
    +
    +function testMarkAfter() {
    +  assertEquals('exit dir (RTL) is opposite to context dir (LTR)',
    +      LRM, ltrFmt.markAfter(longEn + he + html, true));
    +  assertEquals('exit dir (LTR) is opposite to context dir (RTL)',
    +      RLM, rtlFmt.markAfter(longHe + en, true));
    +  assertEquals('exit dir (LTR) doesnt match context dir (unknown)',
    +      '', unkFmt.markAfter(longEn + en, true));
    +  assertEquals('overall dir (RTL) is opposite to context dir (LTR)',
    +      LRM, ltrFmt.markAfter(longHe + en, true));
    +  assertEquals('overall dir (LTR) is opposite to context dir (RTL)',
    +      RLM, rtlFmt.markAfter(longEn + he, true));
    +  assertEquals('exit dir and overall dir match context dir (LTR)',
    +      '', ltrFmt.markAfter(longEn + he + html, false));
    +  assertEquals('exit dir and overall dir matches context dir (RTL)',
    +      '', rtlFmt.markAfter(longHe + he, true));
    +}
    +
    +function testMarkAfterKnownDir() {
    +  assertEquals('known LTR in LTR context',
    +      '', ltrFmt.markAfterKnownDir(LTR, en));
    +  assertEquals('unknown LTR in LTR context',
    +      '', ltrFmt.markAfterKnownDir(null, en));
    +  assertEquals('overall LTR but exit RTL in LTR context',
    +      LRM, ltrFmt.markAfterKnownDir(LTR, he));
    +  assertEquals('known RTL in LTR context',
    +      LRM, ltrFmt.markAfterKnownDir(RTL, he));
    +  assertEquals('unknown RTL in LTR context',
    +      LRM, ltrFmt.markAfterKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in LTR context',
    +      LRM, ltrFmt.markAfterKnownDir(RTL, en));
    +  assertEquals('known neutral in LTR context',
    +      '', ltrFmt.markAfterKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in LTR context',
    +      '', ltrFmt.markAfterKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      '', ltrFmt.markAfterKnownDir(NEUTRAL, en));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      LRM, ltrFmt.markAfterKnownDir(NEUTRAL, he));
    +
    +  assertEquals('known RTL in RTL context',
    +      '', rtlFmt.markAfterKnownDir(RTL, he));
    +  assertEquals('unknown RTL in RTL context',
    +      '', rtlFmt.markAfterKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in RTL context',
    +      RLM, rtlFmt.markAfterKnownDir(RTL, en));
    +  assertEquals('known LTR in RTL context',
    +      RLM, rtlFmt.markAfterKnownDir(LTR, en));
    +  assertEquals('unknown LTR in RTL context',
    +      RLM, rtlFmt.markAfterKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in RTL context',
    +      RLM, rtlFmt.markAfterKnownDir(LTR, he));
    +  assertEquals('known neutral in RTL context',
    +      '', rtlFmt.markAfterKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in RTL context',
    +      '', rtlFmt.markAfterKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in LTR context',
    +      '', rtlFmt.markAfterKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in LTR context',
    +      RLM, rtlFmt.markAfterKnownDir(NEUTRAL, en));
    +
    +  assertEquals('known RTL in unknown context',
    +      '', unkFmt.markAfterKnownDir(RTL, he));
    +  assertEquals('unknown RTL in unknown context',
    +      '', unkFmt.markAfterKnownDir(null, he));
    +  assertEquals('overall RTL but exit LTR in unknown context',
    +      '', unkFmt.markAfterKnownDir(RTL, en));
    +  assertEquals('known LTR in unknown context',
    +      '', unkFmt.markAfterKnownDir(LTR, en));
    +  assertEquals('unknown LTR in unknown context',
    +      '', unkFmt.markAfterKnownDir(null, en));
    +  assertEquals('LTR but exit RTL in unknown context',
    +      '', unkFmt.markAfterKnownDir(LTR, he));
    +  assertEquals('known neutral in unknown context',
    +      '', unkFmt.markAfterKnownDir(NEUTRAL, '.'));
    +  assertEquals('unknown neutral in unknown context',
    +      '', unkFmt.markAfterKnownDir(null, '.'));
    +  assertEquals('overall neutral but exit LTR in unknown context',
    +      '', unkFmt.markAfterKnownDir(NEUTRAL, he));
    +  assertEquals('overall neutral but exit RTL in unknown context',
    +      '', unkFmt.markAfterKnownDir(NEUTRAL, en));
    +}
    +
    +function testMark() {
    +  // Implicitly, also tests the constructor.
    +  assertEquals(LRM, (new goog.i18n.BidiFormatter(LTR)).mark());
    +  assertEquals('', (new goog.i18n.BidiFormatter(null)).mark());
    +  assertEquals('', (new goog.i18n.BidiFormatter(NEUTRAL)).mark());
    +  assertEquals(RLM, (new goog.i18n.BidiFormatter(RTL)).mark());
    +  assertEquals(RLM, (new goog.i18n.BidiFormatter(true)).mark());
    +  assertEquals(LRM, (new goog.i18n.BidiFormatter(false)).mark());
    +}
    +
    +function testStartEdge() {
    +  assertEquals('left', ltrFmt.startEdge());
    +  assertEquals('left', unkFmt.startEdge());
    +  assertEquals('right', rtlFmt.startEdge());
    +}
    +
    +function testEndEdge() {
    +  assertEquals('right', ltrFmt.endEdge());
    +  assertEquals('right', unkFmt.endEdge());
    +  assertEquals('left', rtlFmt.endEdge());
    +}
    +
    +function assertHtmlEquals(expected, html) {
    +  assertEquals(expected, goog.html.SafeHtml.unwrap(html));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor.js b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor.js
    new file mode 100644
    index 0000000000..48fae13d13
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor.js
    @@ -0,0 +1,158 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The decompressor for Base88 compressed character lists.
    + *
    + * The compression is by base 88 encoding the delta between two adjacent
    + * characters in ths list. The deltas can be positive or negative. Also, there
    + * would be character ranges. These three types of values
    + * are given enum values 0, 1 and 2 respectively. Initial 3 bits are used for
    + * encoding the type and total length of the encoded value. Length enums 0, 1
    + * and 2 represents lengths 1, 2 and 4. So (value * 8 + type * 3 + length enum)
    + * is encoded in base 88 by following characters for numbers from 0 to 87:
    + * 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ (continued in next line)
    + * abcdefghijklmnopqrstuvwxyz!#$%()*+,-.:;<=>?@[]^_`{|}~
    + *
    + * Value uses 0 based counting. That is value for the range [a, b] is 0 and
    + * that of [a, c] is 1. Simillarly, the delta of "ab" is 0.
    + *
    + * Following python script can be used to compress character lists taken
    + * standard input: http://go/charlistcompressor.py
    + *
    + */
    +
    +goog.provide('goog.i18n.CharListDecompressor');
    +
    +goog.require('goog.array');
    +goog.require('goog.i18n.uChar');
    +
    +
    +
    +/**
    + * Class to decompress base88 compressed character list.
    + * @constructor
    + * @final
    + */
    +goog.i18n.CharListDecompressor = function() {
    +  this.buildCharMap_('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr' +
    +      'stuvwxyz!#$%()*+,-.:;<=>?@[]^_`{|}~');
    +};
    +
    +
    +/**
    + * 1-1 mapping from ascii characters used in encoding to an integer in the
    + * range 0 to 87.
    + * @type {Object}
    + * @private
    + */
    +goog.i18n.CharListDecompressor.prototype.charMap_ = null;
    +
    +
    +/**
    + * Builds the map from ascii characters used for the base88 scheme to number
    + * each character represents.
    + * @param {string} str The string of characters used in base88 scheme.
    + * @private
    + */
    +goog.i18n.CharListDecompressor.prototype.buildCharMap_ = function(str) {
    +  if (!this.charMap_) {
    +    this.charMap_ = {};
    +    for (var i = 0; i < str.length; i++) {
    +      this.charMap_[str.charAt(i)] = i;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the number encoded in base88 scheme by a substring of given length
    + * and placed at the a given position of the string.
    + * @param {string} str String containing sequence of characters encoding a
    + *     number in base 88 scheme.
    + * @param {number} start Starting position of substring encoding the number.
    + * @param {number} leng Length of the substring encoding the number.
    + * @return {number} The encoded number.
    + * @private
    + */
    +goog.i18n.CharListDecompressor.prototype.getCodeAt_ = function(str, start,
    +    leng) {
    +  var result = 0;
    +  for (var i = 0; i < leng; i++) {
    +    var c = this.charMap_[str.charAt(start + i)];
    +    result += c * Math.pow(88, i);
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * Add character(s) specified by the value and type to given list and return
    + * the next character in the sequence.
    + * @param {Array<string>} list The list of characters to which the specified
    + *     characters are appended.
    + * @param {number} lastcode The last codepoint that was added to the list.
    + * @param {number} value The value component that representing the delta or
    + *      range.
    + * @param {number} type The type component that representing whether the value
    + *      is a positive or negative delta or range.
    + * @return {number} Last codepoint that is added to the list.
    + * @private
    + */
    +goog.i18n.CharListDecompressor.prototype.addChars_ = function(list, lastcode,
    +    value, type) {
    +   if (type == 0) {
    +     lastcode += value + 1;
    +     goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
    +   } else if (type == 1) {
    +     lastcode -= value + 1;
    +     goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
    +   } else if (type == 2) {
    +     for (var i = 0; i <= value; i++) {
    +       lastcode++;
    +       goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
    +     }
    +   }
    +  return lastcode;
    +};
    +
    +
    +/**
    + * Gets the list of characters specified in the given string by base 88 scheme.
    + * @param {string} str The string encoding character list.
    + * @return {!Array<string>} The list of characters specified by the given
    + *     string in base 88 scheme.
    + */
    +goog.i18n.CharListDecompressor.prototype.toCharList = function(str) {
    +  var metasize = 8;
    +  var result = [];
    +  var lastcode = 0;
    +  var i = 0;
    +  while (i < str.length) {
    +    var c = this.charMap_[str.charAt(i)];
    +    var meta = c % metasize;
    +    var type = Math.floor(meta / 3);
    +    var leng = (meta % 3) + 1;
    +    if (leng == 3) {
    +      leng++;
    +    }
    +    var code = this.getCodeAt_(str, i, leng);
    +    var value = Math.floor(code / metasize);
    +    lastcode = this.addChars_(result, lastcode, value, type);
    +
    +    i += leng;
    +  }
    +  return result;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.html b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.html
    new file mode 100644
    index 0000000000..4541a9735c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.CharListDecompressor
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.CharListDecompressorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.js b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.js
    new file mode 100644
    index 0000000000..e9913e2da3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/charlistdecompressor_test.js
    @@ -0,0 +1,58 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.CharListDecompressorTest');
    +goog.setTestOnly('goog.i18n.CharListDecompressorTest');
    +
    +goog.require('goog.i18n.CharListDecompressor');
    +goog.require('goog.testing.jsunit');
    +
    +var decompressor = new goog.i18n.CharListDecompressor();
    +
    +function testBuildCharMap() {
    +  assertEquals(0, decompressor.charMap_['0']);
    +  assertEquals(10, decompressor.charMap_['A']);
    +  assertEquals(87, decompressor.charMap_['}']);
    +}
    +
    +function testGetCodeAt() {
    +  var code = decompressor.getCodeAt_('321', 1, 2);
    +  assertEquals(90, code);
    +}
    +
    +function testAddCharsForType0() {
    +  var list = ['a'];
    +  var lastcode = decompressor.addChars_(list, 97, 0, 0);
    +  assertArrayEquals(['a', 'b'], list);
    +  assertEquals(98, lastcode);
    +}
    +
    +function testAddCharsForType1() {
    +  var list = ['a'];
    +  var lastcode = decompressor.addChars_(list, 98, 0, 1);
    +  assertArrayEquals(['a', 'a'], list);
    +  assertEquals(97, lastcode);
    +}
    +
    +function testAddCharsForType2() {
    +  var list = ['a'];
    +  var lastcode = decompressor.addChars_(list, 97, 1, 2);
    +  assertArrayEquals(['a', 'b', 'c'], list);
    +  assertEquals(99, lastcode);
    +}
    +
    +function testToCharList() {
    +  var list = decompressor.toCharList('%812E<E'); // a, x-z, p-r
    +  assertArrayEquals(['a', 'x', 'y', 'z', 'p', 'q', 'r'], list);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/charpickerdata.js b/mapbox-gl/closure-library/closure/goog/i18n/charpickerdata.js
    new file mode 100644
    index 0000000000..4e34f0627f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/charpickerdata.js
    @@ -0,0 +1,3667 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Character lists and their classifications used by character
    + * picker widget. Autogenerated from Unicode data:
    + * https://sites/cibu/character-picker.
    + *
    + */
    +
    +goog.provide('goog.i18n.CharPickerData');
    +
    +
    +
    +/**
    + * Object holding two level character organization and character listing.
    + * @constructor
    + */
    +goog.i18n.CharPickerData = function() {};
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SYMBOL = goog.getMsg('Symbol');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ARROWS = goog.getMsg('Arrows');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BRAILLE = goog.getMsg('Braille');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CONTROL_PICTURES =
    +    goog.getMsg('Control Pictures');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CURRENCY = goog.getMsg('Currency');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_EMOTICONS = goog.getMsg('Emoticons');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GAME_PIECES = goog.getMsg('Game Pieces');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GENDER_AND_GENEALOGICAL =
    +    goog.getMsg('Gender and Genealogical');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GEOMETRIC_SHAPES =
    +    goog.getMsg('Geometric Shapes');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KEYBOARD_AND_UI =
    +    goog.getMsg('Keyboard and UI');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LATIN_1_SUPPLEMENT =
    +    goog.getMsg('Latin 1 Supplement');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MATH = goog.getMsg('Math');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MATH_ALPHANUMERIC =
    +    goog.getMsg('Math Alphanumeric');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MISCELLANEOUS = goog.getMsg('Miscellaneous');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MUSICAL = goog.getMsg('Musical');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_STARS_ASTERISKS =
    +    goog.getMsg('Stars/Asterisks');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SUBSCRIPT = goog.getMsg('Subscript');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SUPERSCRIPT = goog.getMsg('Superscript');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TECHNICAL = goog.getMsg('Technical');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TRANSPORT_AND_MAP =
    +    goog.getMsg('Transport And Map');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_WEATHER_AND_ASTROLOGICAL =
    +    goog.getMsg('Weather and Astrological');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_YIJING_TAI_XUAN_JING =
    +    goog.getMsg('Yijing / Tai Xuan Jing');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HISTORIC = goog.getMsg('Historic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY = goog.getMsg('Compatibility');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_EMOJI = goog.getMsg('Emoji');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PEOPLE_AND_EMOTIONS =
    +    goog.getMsg('People and Emotions');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ANIMALS_PLANTS_AND_FOOD =
    +    goog.getMsg('Animals, Plants and Food');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OBJECTS = goog.getMsg('Objects');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SPORTS_CELEBRATIONS_AND_ACTIVITIES =
    +    goog.getMsg('Sports, Celebrations and Activities');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TRANSPORT_MAPS_AND_SIGNAGE =
    +    goog.getMsg('Transport, Maps and Signage');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_WEATHER_SCENES_AND_ZODIAC_SIGNS =
    +    goog.getMsg('Weather, Scenes and Zodiac signs');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ENCLOSED = goog.getMsg('Enclosed');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MARKS = goog.getMsg('Marks');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SYMBOLS = goog.getMsg('Symbols');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PUNCTUATION = goog.getMsg('Punctuation');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ASCII_BASED = goog.getMsg('ASCII Based');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_DASH_CONNECTOR = goog.getMsg('Dash/Connector');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OTHER = goog.getMsg('Other');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PAIRED = goog.getMsg('Paired');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_NUMBER = goog.getMsg('Number');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_DECIMAL = goog.getMsg('Decimal');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ENCLOSED_DOTTED =
    +    goog.getMsg('Enclosed/Dotted');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_FRACTIONS_RELATED =
    +    goog.getMsg('Fractions/Related');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_FORMAT_WHITESPACE =
    +    goog.getMsg('Format & Whitespace');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_FORMAT = goog.getMsg('Format');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_VARIATION_SELECTOR =
    +    goog.getMsg('Variation Selector');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_WHITESPACE = goog.getMsg('Whitespace');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MODIFIER = goog.getMsg('Modifier');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ENCLOSING = goog.getMsg('Enclosing');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_NONSPACING = goog.getMsg('Nonspacing');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SPACING = goog.getMsg('Spacing');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LATIN = goog.getMsg('Latin');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_COMMON = goog.getMsg('Common');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_FLIPPED_MIRRORED =
    +    goog.getMsg('Flipped/Mirrored');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PHONETICS_IPA = goog.getMsg('Phonetics (IPA)');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PHONETICS_X_IPA =
    +    goog.getMsg('Phonetics (X-IPA)');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OTHER_EUROPEAN_SCRIPTS =
    +    goog.getMsg('Other European Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ARMENIAN = goog.getMsg('Armenian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CYRILLIC = goog.getMsg('Cyrillic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GEORGIAN = goog.getMsg('Georgian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GREEK = goog.getMsg('Greek');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CYPRIOT = goog.getMsg('Cypriot');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GLAGOLITIC = goog.getMsg('Glagolitic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GOTHIC = goog.getMsg('Gothic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LINEAR_B = goog.getMsg('Linear B');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OGHAM = goog.getMsg('Ogham');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OLD_ITALIC = goog.getMsg('Old Italic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_RUNIC = goog.getMsg('Runic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SHAVIAN = goog.getMsg('Shavian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_AMERICAN_SCRIPTS =
    +    goog.getMsg('American Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CANADIAN_ABORIGINAL =
    +    goog.getMsg('Canadian Aboriginal');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CHEROKEE = goog.getMsg('Cherokee');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_DESERET = goog.getMsg('Deseret');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_AFRICAN_SCRIPTS =
    +    goog.getMsg('African Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_EGYPTIAN_HIEROGLYPHS =
    +    goog.getMsg('Egyptian Hieroglyphs');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ETHIOPIC = goog.getMsg('Ethiopic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MEROITIC_CURSIVE =
    +    goog.getMsg('Meroitic Cursive');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MEROITIC_HIEROGLYPHS =
    +    goog.getMsg('Meroitic Hieroglyphs');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_NKO = goog.getMsg('Nko');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TIFINAGH = goog.getMsg('Tifinagh');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_VAI = goog.getMsg('Vai');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BAMUM = goog.getMsg('Bamum');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_COPTIC = goog.getMsg('Coptic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OSMANYA = goog.getMsg('Osmanya');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MIDDLE_EASTERN_SCRIPTS =
    +    goog.getMsg('Middle Eastern Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ARABIC = goog.getMsg('Arabic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HEBREW = goog.getMsg('Hebrew');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_IMPERIAL_ARAMAIC =
    +    goog.getMsg('Imperial Aramaic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_INSCRIPTIONAL_PAHLAVI =
    +    goog.getMsg('Inscriptional Pahlavi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_INSCRIPTIONAL_PARTHIAN =
    +    goog.getMsg('Inscriptional Parthian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MANDAIC = goog.getMsg('Mandaic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OLD_SOUTH_ARABIAN =
    +    goog.getMsg('Old South Arabian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SAMARITAN = goog.getMsg('Samaritan');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SYRIAC = goog.getMsg('Syriac');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_AVESTAN = goog.getMsg('Avestan');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CARIAN = goog.getMsg('Carian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CUNEIFORM = goog.getMsg('Cuneiform');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LYCIAN = goog.getMsg('Lycian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LYDIAN = goog.getMsg('Lydian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OLD_PERSIAN = goog.getMsg('Old Persian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PHOENICIAN = goog.getMsg('Phoenician');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_UGARITIC = goog.getMsg('Ugaritic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SOUTH_ASIAN_SCRIPTS =
    +    goog.getMsg('South Asian Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BENGALI = goog.getMsg('Bengali');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CHAKMA = goog.getMsg('Chakma');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_DEVANAGARI = goog.getMsg('Devanagari');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GUJARATI = goog.getMsg('Gujarati');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_GURMUKHI = goog.getMsg('Gurmukhi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KANNADA = goog.getMsg('Kannada');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LEPCHA = goog.getMsg('Lepcha');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LIMBU = goog.getMsg('Limbu');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MALAYALAM = goog.getMsg('Malayalam');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MEETEI_MAYEK = goog.getMsg('Meetei Mayek');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OL_CHIKI = goog.getMsg('Ol Chiki');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_ORIYA = goog.getMsg('Oriya');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SAURASHTRA = goog.getMsg('Saurashtra');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SINHALA = goog.getMsg('Sinhala');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SORA_SOMPENG = goog.getMsg('Sora Sompeng');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAMIL = goog.getMsg('Tamil');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TELUGU = goog.getMsg('Telugu');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_THAANA = goog.getMsg('Thaana');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TIBETAN = goog.getMsg('Tibetan');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BRAHMI = goog.getMsg('Brahmi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KAITHI = goog.getMsg('Kaithi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KHAROSHTHI = goog.getMsg('Kharoshthi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SHARADA = goog.getMsg('Sharada');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SYLOTI_NAGRI = goog.getMsg('Syloti Nagri');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAKRI = goog.getMsg('Takri');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SOUTHEAST_ASIAN_SCRIPTS =
    +    goog.getMsg('Southeast Asian Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BALINESE = goog.getMsg('Balinese');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BATAK = goog.getMsg('Batak');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CHAM = goog.getMsg('Cham');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_JAVANESE = goog.getMsg('Javanese');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KAYAH_LI = goog.getMsg('Kayah Li');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KHMER = goog.getMsg('Khmer');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LAO = goog.getMsg('Lao');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MYANMAR = goog.getMsg('Myanmar');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_NEW_TAI_LUE = goog.getMsg('New Tai Lue');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAI_LE = goog.getMsg('Tai Le');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAI_THAM = goog.getMsg('Tai Tham');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAI_VIET = goog.getMsg('Tai Viet');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_THAI = goog.getMsg('Thai');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BUGINESE = goog.getMsg('Buginese');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BUHID = goog.getMsg('Buhid');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HANUNOO = goog.getMsg('Hanunoo');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_REJANG = goog.getMsg('Rejang');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_SUNDANESE = goog.getMsg('Sundanese');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAGALOG = goog.getMsg('Tagalog');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_TAGBANWA = goog.getMsg('Tagbanwa');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HANGUL = goog.getMsg('Hangul');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OTHER_EAST_ASIAN_SCRIPTS =
    +    goog.getMsg('Other East Asian Scripts');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_BOPOMOFO = goog.getMsg('Bopomofo');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HIRAGANA = goog.getMsg('Hiragana');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_KATAKANA = goog.getMsg('Katakana');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LISU = goog.getMsg('Lisu');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MIAO = goog.getMsg('Miao');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_MONGOLIAN = goog.getMsg('Mongolian');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_OLD_TURKIC = goog.getMsg('Old Turkic');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_PHAGS_PA = goog.getMsg('Phags Pa');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_YI = goog.getMsg('Yi');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_1_STROKE_RADICALS =
    +    goog.getMsg('Han 1-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_LESS_COMMON = goog.getMsg('Less Common');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_2_STROKE_RADICALS =
    +    goog.getMsg('Han 2-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_3_STROKE_RADICALS =
    +    goog.getMsg('Han 3-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_4_STROKE_RADICALS =
    +    goog.getMsg('Han 4-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_5_STROKE_RADICALS =
    +    goog.getMsg('Han 5-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_6_STROKE_RADICALS =
    +    goog.getMsg('Han 6-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_7_STROKE_RADICALS =
    +    goog.getMsg('Han 7-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_8_STROKE_RADICALS =
    +    goog.getMsg('Han 8-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_9_STROKE_RADICALS =
    +    goog.getMsg('Han 9-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_10_STROKE_RADICALS =
    +    goog.getMsg('Han 10-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_11_17_STROKE_RADICALS =
    +    goog.getMsg('Han 11..17-Stroke Radicals');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_HAN_OTHER = goog.getMsg('Han - Other');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_CJK_STROKES = goog.getMsg('CJK Strokes');
    +
    +
    +/**
    + * @desc Name for a symbol or character category. Used in a pull-down list
    + *   shown to a  document editing user trying to insert a special character.
    + *   Newlines are not allowed; translation should be a noun and as consise as
    + *   possible. More details:
    + *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    + * @type {string}
    + */
    +goog.i18n.CharPickerData.MSG_CP_IDEOGRAPHIC_DESCRIPTION =
    +    goog.getMsg('Ideographic Description');
    +
    +
    +/**
    + * Top catagory names of character organization.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.prototype.categories = [
    +  goog.i18n.CharPickerData.MSG_CP_SYMBOL,
    +  goog.i18n.CharPickerData.MSG_CP_EMOJI,
    +  goog.i18n.CharPickerData.MSG_CP_PUNCTUATION,
    +  goog.i18n.CharPickerData.MSG_CP_NUMBER,
    +  goog.i18n.CharPickerData.MSG_CP_FORMAT_WHITESPACE,
    +  goog.i18n.CharPickerData.MSG_CP_MODIFIER,
    +  goog.i18n.CharPickerData.MSG_CP_LATIN,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER_EUROPEAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_AMERICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_AFRICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_MIDDLE_EASTERN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_SOUTH_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_SOUTHEAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_HANGUL,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER_EAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_1_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_2_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_3_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_4_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_5_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_6_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_7_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_8_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_9_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_10_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_11_17_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.MSG_CP_HAN_OTHER
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SYMBOL = [
    +  goog.i18n.CharPickerData.MSG_CP_ARROWS,
    +  goog.i18n.CharPickerData.MSG_CP_BRAILLE,
    +  goog.i18n.CharPickerData.MSG_CP_CONTROL_PICTURES,
    +  goog.i18n.CharPickerData.MSG_CP_CURRENCY,
    +  goog.i18n.CharPickerData.MSG_CP_EMOTICONS,
    +  goog.i18n.CharPickerData.MSG_CP_GAME_PIECES,
    +  goog.i18n.CharPickerData.MSG_CP_GENDER_AND_GENEALOGICAL,
    +  goog.i18n.CharPickerData.MSG_CP_GEOMETRIC_SHAPES,
    +  goog.i18n.CharPickerData.MSG_CP_KEYBOARD_AND_UI,
    +  goog.i18n.CharPickerData.MSG_CP_LATIN_1_SUPPLEMENT,
    +  goog.i18n.CharPickerData.MSG_CP_MATH,
    +  goog.i18n.CharPickerData.MSG_CP_MATH_ALPHANUMERIC,
    +  goog.i18n.CharPickerData.MSG_CP_MISCELLANEOUS,
    +  goog.i18n.CharPickerData.MSG_CP_MUSICAL,
    +  goog.i18n.CharPickerData.MSG_CP_STARS_ASTERISKS,
    +  goog.i18n.CharPickerData.MSG_CP_SUBSCRIPT,
    +  goog.i18n.CharPickerData.MSG_CP_SUPERSCRIPT,
    +  goog.i18n.CharPickerData.MSG_CP_TECHNICAL,
    +  goog.i18n.CharPickerData.MSG_CP_TRANSPORT_AND_MAP,
    +  goog.i18n.CharPickerData.MSG_CP_WEATHER_AND_ASTROLOGICAL,
    +  goog.i18n.CharPickerData.MSG_CP_YIJING_TAI_XUAN_JING,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_SYMBOL = [
    +  '2>807AnTMm6e6HDk%`O728F1f4V1PNF2WF1G}58?]514M]Ol1%2l2^3X1U:1Un2Mb>$0MD-(068k11I3706:%MwiZ06',
    +  ';oA0FN',
    +  '(j90d3',
    +  'H3XBMQQ10HB(2106uPM]N:qol202S20V2I:Z0^xM0:91E]J6O6',
    +  ';(i1-5W?',
    +  'Q6A06f5#1H2,]4MeEY[W1@3W}891N1GF1GN18N1P%k',
    +  '2JA0sOc',
    +  'oG90nMcPTFNfFEQE10t2H3kQ7X1sj>$0OW6*F%E',
    +  '(P90UGv771.Uv46%7Y^Y1F2mc]1M+<Z1',
    +  '9FP1',
    +  ':3f1En5894WX3:2v+]lEQ?60f2E11OH1P1M]1U11UfCf111MuUmH6Ue6WGGu:26G8:2NO$M:16H8%2V28H211cvg.]4s9AnU#5PNdkX4-1Gc24P1P2:2P2:2P2:2P2:2P2g>50M8V2868G8,8M88mW888E868G8888868GM8k8M8M88,8d1eE8U8d1%46bf$0:;c8%Ef1Ev2:28]BmMbp)02p8071WO6WUw+w0',
    +  '9G6e:-EGX26G6(k70Ocm,]AWG,8OUmOO68E86uMeU^`Q1t78V686GG6GM8|88k8-58MGs8k8d28M8U8Ok8-UGF28F28#28F28#28F28#28F28#28F28sGd4rLS1H1',
    +  '1FGW8Y040Mg%50EHB686WU8l1$Uv4?8En1E8|:29168U8718k8kG8M868688686e686888,148MO8|8E]7wV10k2tN1cYf806813692W]3%68X2f2|O6G86%1P5m6%5$6%468e[E8c11126v1MH2|%F9DuM8E86m8UTN%06',
    +  ';DA0k2mO1NM[d3GV5eEms$6ut2WN493@5OA;80sD790UOc$sGk%2MfDE',
    +  ';OA0v5-3g510E^jW1WV1:l',
    +  'Qq80N1871QC30',
    +  'XFu6e6^X80O?vE82+Y16T+g1Ug2709+H12F30QjW0PC6',
    +  'gM90sW#1G6$l7H1!%2N2O?ml1]6?',
    +  'g?i1N6',
    +  'Q4A0F1mv3}1v8,uUe^zX171',
    +  'w8A0sf7c2WA0#5A>E1-7',
    +  'I{)0%4!P7|%4}3A,$0dA',
    +  '(PD0M(ZU16H1-3e!u6'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_EMOJI = [
    +  goog.i18n.CharPickerData.MSG_CP_PEOPLE_AND_EMOTIONS,
    +  goog.i18n.CharPickerData.MSG_CP_ANIMALS_PLANTS_AND_FOOD,
    +  goog.i18n.CharPickerData.MSG_CP_OBJECTS,
    +  goog.i18n.CharPickerData.MSG_CP_SPORTS_CELEBRATIONS_AND_ACTIVITIES,
    +  goog.i18n.CharPickerData.MSG_CP_TRANSPORT_MAPS_AND_SIGNAGE,
    +  goog.i18n.CharPickerData.MSG_CP_WEATHER_SCENES_AND_ZODIAC_SIGNS,
    +  goog.i18n.CharPickerData.MSG_CP_ENCLOSED,
    +  goog.i18n.CharPickerData.MSG_CP_MARKS,
    +  goog.i18n.CharPickerData.MSG_CP_SYMBOLS
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_EMOJI = [
    +  '^6A0n2:IE]7Y>X18N1%1-28EOO8871G|%U-5W?',
    +  'I6A0A_X1c8N6eXBt5',
    +  ';O906PJG]m1C1Amew)X16:It1]2W68E8X168[8d68MP171P1!1372',
    +  '2DA0s%76o]W1@3nAN1GF1GN18N1Xzd191N38U9I',
    +  '(DA0v1O]2694t1m72$2>X1d1%DvXUvBN6',
    +  'Q4A0F1mv4|HAUe98(rX1@2]k',
    +  'Y#90;v308ICU1d2W-3H9EH1-3e!u6',
    +  ';5A09M9188:48WE8n5EH2',
    +  'Y%C0(wV1P7N3[EP1M'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_PUNCTUATION = [
    +  goog.i18n.CharPickerData.MSG_CP_ASCII_BASED,
    +  goog.i18n.CharPickerData.MSG_CP_DASH_CONNECTOR,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER,
    +  goog.i18n.CharPickerData.MSG_CP_PAIRED,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_PUNCTUATION = [
    +  ':2M8EG886[6O6f2H6eP16u',
    +  '14f4gX80c%36%1gu30:26Q3t0XG',
    +  '(s70:<.MOEmEGGG8OEms88Iu3068G6n1!',
    +  'n36f48v2894X1;P80sP26[6]46P16nvMPF6f3c1^F1H76:2,va@1%5M]26;7106G,fh,Gs2Ms06nPcXF6f48v288686',
    +  'gm808kQT30MnN72v1U8U(%t0Eb(t0',
    +  'Ig80e91E91686W8$EH1X36P162pw0,12-1G|8F18W86nDE8c8M[6O6X2E8f2886'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_NUMBER = [
    +  goog.i18n.CharPickerData.MSG_CP_DECIMAL,
    +  goog.i18n.CharPickerData.MSG_CP_ENCLOSED_DOTTED,
    +  goog.i18n.CharPickerData.MSG_CP_FRACTIONS_RELATED,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_NUMBER = [
    +  'P4,]A6egh10,HC,1I,fb,%A,%A,%A,%A,%A,%A,%A,%A,XK,%A,X6,PP,X6,Q]10,f3,PR,vB,9F,m,nG,]K,m,A710Ocm,^SZ0,vz,f3,1I,%A,]a,AnQ0,vB,f5,9D,2Q10,5O60,',
    +  'gs90#7%4@1Pvt2g+20,%2s8N1]2,n3N1',
    +  '9G6eGEoX80Ocm,1IV1%3',
    +  'ot20cHYc]AE9Ck]Lcvd,^910#1oF10,vh2}1073GMQ:30P2!P1EHVMI2V0,9Ts8^aP0sHn6%JsH2s](#2fg#1wnp0l1;-70?',
    +  'o560EgM10,Yk10EGMo230w6u0}39175n1:aMv2$HCUXI,^E10cnQso,60@8',
    +  'w.80-2o?30EHVMoSU1?b}#0,'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_FORMAT_WHITESPACE = [
    +  goog.i18n.CharPickerData.MSG_CP_FORMAT,
    +  goog.i18n.CharPickerData.MSG_CP_VARIATION_SELECTOR,
    +  goog.i18n.CharPickerData.MSG_CP_WHITESPACE,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_FORMAT_WHITESPACE = [
    +  'vF;Z10U92fHf4gh40;920UX2Uf4U8M2n#0;`o0sbwt0vME',
    +  ']=oY506%7E^$zA#LDF1AV1',
    +  'fEIH602920,H3P4wB40;#s0',
    +  'w-10f4^#206IV10(970ols0',
    +  'fEAQ80?P3P4wB40^@s0'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_MODIFIER = [
    +  goog.i18n.CharPickerData.MSG_CP_ENCLOSING,
    +  goog.i18n.CharPickerData.MSG_CP_NONSPACING,
    +  goog.i18n.CharPickerData.MSG_CP_SPACING,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_MODIFIER = [
    +  '(y80M8E',
    +  '%+#5GG,8t1(#60E8718kWm:I,H46v%71WO|oWQ1En1sGk%2MT_t0k',
    +  'f!!.M%3M91gz30(C30f1695E8?8l18d2X4N32D40XH',
    +  '%?71HP62x60M[F2926^Py0',
    +  'n<686'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_LATIN = [
    +  goog.i18n.CharPickerData.MSG_CP_COMMON,
    +  goog.i18n.CharPickerData.MSG_CP_ENCLOSED,
    +  goog.i18n.CharPickerData.MSG_CP_FLIPPED_MIRRORED,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER,
    +  goog.i18n.CharPickerData.MSG_CP_PHONETICS_IPA,
    +  goog.i18n.CharPickerData.MSG_CP_PHONETICS_X_IPA,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_LATIN = [
    +  ':5N2mN2P6}18#28V1Gl1GcG|W68cGs8|GMGMG6G}1GWG6OU8GEOG6H168E11M.s$$6f16%2Mv3P168688uW.128$IN706126H26W6:16m6$6P16Gc916[878QAa06zph0696U8EOP3o2706',
    +  '^x90}6^yX1#28F5m-3:6N2',
    +  'X4X1m6OEWku8WGc88M8H6%1nFmu11916X16H3H1%4P3[8EOmeWW.euWM918HMH6%512]I1Q^+20f+.%2X8]cfBg*10I710P1681H]E^BZ01BE',
    +  ']N6v16m6P16Gv26W6W6G6H286O6G6m86OE86GUGGEGOEv2s8sG!OEOt2mV38?A570@3%5718}2H9|G@1G72GMG#1GcGsGF1G6m|GcHuO11G6e6O88mOuX18Eo]20}1u62cW0F1v6N1e68M91?H7zSi081s868EG?8E8EGcu8E8UGEw^60t5H193N3v!H1f171QmZ072f9E]96',
    +  '%8N2%96$uH4H3u:9M%CF28718M868UO?86G68E8868GHOeP1I>70EO6LF80E8GW11OO6918Of26868886OV3WU%2W',
    +  '1uH1WGeE11G6GO8G868s',
    +  'HZ6uP268691s15P36Al7068H8cHw!Y?20UwdW0#58s:BUbvh0d1g{A06AZW0sH2697',
    +  'XFX1:A6116v5H6!P3E(o706vtM8E8?86GUGE8O8M8E86W8.U12-2Qd40HBMvE,et8:2Qtq0kg710N2mN2bV)0mWOXnc'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_OTHER_EUROPEAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_ARMENIAN,
    +  goog.i18n.CharPickerData.MSG_CP_CYRILLIC,
    +  goog.i18n.CharPickerData.MSG_CP_GEORGIAN,
    +  goog.i18n.CharPickerData.MSG_CP_GREEK,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_CYPRIOT,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_CYRILLIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GEORGIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GLAGOLITIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GOTHIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GREEK,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_LINEAR_B,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_OGHAM,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_OLD_ITALIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_RUNIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_SHAVIAN,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_ARMENIAN,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GREEK
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_OTHER_EUROPEAN_SCRIPTS = [
    +  '(W10V3Oc8V3G6W=4',
    +  '2510-BuNEKuvfE',
    +  '(e10o{20eG@2mMGEJ',
    +  ']]8E88#18@3P3$wC70@1GcGV3GcGs8888l1888888O#48U8eE8E88OEOUeE8k8eE8E88{l706W',
    +  '^-+0cG8@386OG',
    +  '2{h0F4W[872{<g06g^A0-2;;V0M8,8:2',
    +  ';Y40V3]3cW2a70V38e',
    +  '^tB0F48F4',
    +  '^l*0V2',
    +  ']@MG6OEX7EO71f18GU8E;{(0#6YBt0@5OJE',
    +  '(z)0|8N28t1868N1GF1937B',
    +  'o_50l2',
    +  'oh*0#28M',
    +  'g|50N7',
    +  'A;*0N4',
    +  'oe10g^$0U',
    +  'XG%$$%6Ef26OoN70888888n5G[8uuuuH189Rr:706we708E11EH1EH1EH16'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_AMERICAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_CANADIAN_ABORIGINAL,
    +  goog.i18n.CharPickerData.MSG_CP_CHEROKEE,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_DESERET
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_AMERICAN_SCRIPTS = [
    +  'YP507w]oN6',
    +  'wG50t7',
    +  ';(*0F7'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_AFRICAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_EGYPTIAN_HIEROGLYPHS,
    +  goog.i18n.CharPickerData.MSG_CP_ETHIOPIC,
    +  goog.i18n.CharPickerData.MSG_CP_MEROITIC_CURSIVE,
    +  goog.i18n.CharPickerData.MSG_CP_MEROITIC_HIEROGLYPHS,
    +  goog.i18n.CharPickerData.MSG_CP_NKO,
    +  goog.i18n.CharPickerData.MSG_CP_TIFINAGH,
    +  goog.i18n.CharPickerData.MSG_CP_VAI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BAMUM,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_COPTIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_NKO,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_OSMANYA
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_AFRICAN_SCRIPTS = [
    +  ';Y[0}}N9',
    +  ';(40l68MGk88MGt38MG@28MGk88MGN18758MG}5el2ON2(;60}1.k8k8k8k8k8k8k8kI8X0cGcGc.k8kDDe0E',
    +  '(L,072m6',
    +  ';I,0-2',
    +  'Q420l3P1MK1?W',
    +  'o_B0}4$3X1',
    +  '^th0NO8#2*2',
    +  '(5i0F7GcY4p0tpzup06',
    +  'Q210F12$A0}9O6eka1E',
    +  '^720E',
    +  'g?*0t2G,'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_MIDDLE_EASTERN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_ARABIC,
    +  goog.i18n.CharPickerData.MSG_CP_HEBREW,
    +  goog.i18n.CharPickerData.MSG_CP_IMPERIAL_ARAMAIC,
    +  goog.i18n.CharPickerData.MSG_CP_INSCRIPTIONAL_PAHLAVI,
    +  goog.i18n.CharPickerData.MSG_CP_INSCRIPTIONAL_PARTHIAN,
    +  goog.i18n.CharPickerData.MSG_CP_MANDAIC,
    +  goog.i18n.CharPickerData.MSG_CP_OLD_SOUTH_ARABIAN,
    +  goog.i18n.CharPickerData.MSG_CP_SAMARITAN,
    +  goog.i18n.CharPickerData.MSG_CP_SYRIAC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_ARABIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_AVESTAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_CARIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_CUNEIFORM,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_HEBREW,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_LYCIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_LYDIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_OLD_PERSIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_PHOENICIAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_SYRIAC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_UGARITIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_ARABIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_HEBREW
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_MIDDLE_EASTERN_SCRIPTS = [
    +  'op10U8,11Gl2m,]1F1O68W-18V6H2l1P774XQ8?^F60g2#0#2YVx06r##0vAry%0U]3[-1f11vV2QG$0V1',
    +  'oj108G91V2eUC6F1886A?$0',
    +  '(>+0@18!',
    +  'g!,0t1es',
    +  'ox,0@1Gs',
    +  '^F20F2eZE',
    +  'Id,0-2',
    +  'AA20@1X2N1Qt60{w6072',
    +  'wq10P1O]2[?X2',
    +  '^u10UH46%2H7[fD6=Wc1HkG,8M',
    +  '(r,0-4Ok',
    +  ';Y*0V4',
    +  'gE=0-@HD@8H1M',
    +  'Qk10^>$0!f35}$0#2:168',
    +  '^V*0l2',
    +  'AA,0N2e',
    +  'Aw*0F3WF1',
    +  'I7,0d2O',
    +  ';;10F1868t2v2Eq5%2V2',
    +  'It*0t28',
    +  'I!10MA^e1M8V2868G8,8M88mW888E868G8888868GM8k8M8M88,8d1eE8U8d1{W$0-813@Wv1#5G-4v371fAE88FC',
    +  '2a(08.F18U886868!'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SOUTH_ASIAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_BENGALI,
    +  goog.i18n.CharPickerData.MSG_CP_CHAKMA,
    +  goog.i18n.CharPickerData.MSG_CP_DEVANAGARI,
    +  goog.i18n.CharPickerData.MSG_CP_GUJARATI,
    +  goog.i18n.CharPickerData.MSG_CP_GURMUKHI,
    +  goog.i18n.CharPickerData.MSG_CP_KANNADA,
    +  goog.i18n.CharPickerData.MSG_CP_LEPCHA,
    +  goog.i18n.CharPickerData.MSG_CP_LIMBU,
    +  goog.i18n.CharPickerData.MSG_CP_MALAYALAM,
    +  goog.i18n.CharPickerData.MSG_CP_MEETEI_MAYEK,
    +  goog.i18n.CharPickerData.MSG_CP_OL_CHIKI,
    +  goog.i18n.CharPickerData.MSG_CP_ORIYA,
    +  goog.i18n.CharPickerData.MSG_CP_SAURASHTRA,
    +  goog.i18n.CharPickerData.MSG_CP_SINHALA,
    +  goog.i18n.CharPickerData.MSG_CP_SORA_SOMPENG,
    +  goog.i18n.CharPickerData.MSG_CP_TAMIL,
    +  goog.i18n.CharPickerData.MSG_CP_TELUGU,
    +  goog.i18n.CharPickerData.MSG_CP_THAANA,
    +  goog.i18n.CharPickerData.MSG_CP_TIBETAN,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BRAHMI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_KAITHI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_KANNADA,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_KHAROSHTHI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_SHARADA,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_SYLOTI_NAGRI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_TAKRI,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BENGALI,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_DEVANAGARI,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_GURMUKHI,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_ORIYA,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_TIBETAN
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_SOUTH_ASIAN_SCRIPTS = [
    +  'gg206:2sG6G@18k8OMOf1n16W@1:*64[E958kG6GE.[6',
    +  'wH.0F3X1F146EP3F1',
    +  '(X20-4Ov1X16G718c8k9[6gMf0,DRg0M]4E8l18k[6H1YEg0l1',
    +  '(*20!8E8@18k868UOv1X16W|fk6*uE958s8E8E:16',
    +  'gg206fEcW6G@18k8GG693.,GE:v6a*E958UW6GEO%26O',
    +  'QR30s8E8}18,8UO936W,86CA6958k8E8Mu6116',
    +  'oZ70F392N1OE=3#1',
    +  '(r60l2H3O|K4|W|',
    +  '^c30s8E8t3Gf1n16WV1Okw@4053506P5k8E8M.[6',
    +  'wGj0?eEvI73$W,iOUOMfLs86',
    +  ';g70l3m6pc',
    +  'gg206%bsG6G@18k868UO13EWl1PY6CjE958kG6GE$6[6',
    +  'oni0d4X2|486n4d1',
    +  'oo30l1O728!8Gk94A+40j@406X6Wc88sv16',
    +  '2D.0F2u,',
    +  ';3308cOE8MO6886O6OEO|12]1-1=AX5UOE8M.',
    +  'wF30s8E8}18,8UOX26m6W,$sPA6=LEP5k8E8Mu6116',
    +  'wq10P1O:5,PPV311_?',
    +  '2{30|8?GV2888MGE8M8M8M8M8M8|8EH2GUf4s8c8kW6Ii806e,GsL$806f288W6f468ek8E86ec8M8M8M8M8M8|8E.',
    +  'YG40M2e30M8MO6',
    +  'Y^-0#4X1kWt24AE:4N1',
    +  '26.0}311k=5E94?',
    +  'YZ30',
    +  'gU,0X1M8E8V291s$!=7E86eMv3EW',
    +  'QT.0N4P1su,48EX4F1',
    +  '(bi068E8M8}1eMy3OW92U',
    +  'Yv:0-3]1,y271',
    +  'Yr2068',
    +  'Yf20s',
    +  'Qz20G93EG',
    +  'Q0306',
    +  'A|30]4.WWW91we#0M5e#0868$n1.WWW91'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SOUTHEAST_ASIAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_BALINESE,
    +  goog.i18n.CharPickerData.MSG_CP_BATAK,
    +  goog.i18n.CharPickerData.MSG_CP_CHAM,
    +  goog.i18n.CharPickerData.MSG_CP_JAVANESE,
    +  goog.i18n.CharPickerData.MSG_CP_KAYAH_LI,
    +  goog.i18n.CharPickerData.MSG_CP_KHMER,
    +  goog.i18n.CharPickerData.MSG_CP_LAO,
    +  goog.i18n.CharPickerData.MSG_CP_MYANMAR,
    +  goog.i18n.CharPickerData.MSG_CP_NEW_TAI_LUE,
    +  goog.i18n.CharPickerData.MSG_CP_TAI_LE,
    +  goog.i18n.CharPickerData.MSG_CP_TAI_THAM,
    +  goog.i18n.CharPickerData.MSG_CP_TAI_VIET,
    +  goog.i18n.CharPickerData.MSG_CP_THAI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BUGINESE,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BUHID,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_HANUNOO,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_KHMER,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_REJANG,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_SUNDANESE,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_TAGALOG,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_TAGBANWA
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_SOUTHEAST_ASIAN_SCRIPTS = [
    +  '(C70F4n1kWV2.!KBUP4d1f3!',
    +  '(T70V312MK2F1',
    +  'Q`i0t392E8sW,GM=4F191$6',
    +  '2:i0F4P171G,W6q8MP4F1P1',
    +  '2zi0V3$6)s',
    +  ';I6073GE8?]2E8UO,m,Hi-2Srl286O',
    +  'g:3068G68GmM8k8E88G68M86.GU11,GMC4Gc86.8c',
    +  'QK40-3:1}1WMOO6uEW71918,W6Iwe0V18,D-e0#192MWE8EGkOMH1|8[M;xe0[',
    +  'Y%60@3]1k$?O6K4d1u6',
    +  '2z60t2GU',
    +  '^@60#4]3,m,mk8c`7,8l2Gn3',
    +  '^7j0N48O6GUG8H2686K48EG6e68f2',
    +  ';z30N48691c.71*3Gk11!',
    +  '2>60}1u6xU',
    +  '2C606.#1',
    +  'AA60l1O6RE',
    +  'gM60v311',
    +  'Y%i0}1H2C271',
    +  'IO70t2H1l1PNsyTE%271',
    +  'I760718MH36K3E',
    +  '2C606%3718E86'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HANGUL = [
    +  goog.i18n.CharPickerData.MSG_CP_OTHER,
    +  '\u1100',
    +  '\u1102',
    +  '\u1103',
    +  '\u1105',
    +  '\u1106',
    +  '\u1107',
    +  '\u1109',
    +  '\u110B',
    +  '\u110C',
    +  '\u110E',
    +  '\u110F',
    +  '\u1110',
    +  '\u1111',
    +  '\u1112',
    +  '\u1159',
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HANGUL = [
    +  'AzC0M88,8F1X1mWMPqYyh0}1WV42BA06Tis06',
    +  ';gj0}}-I',
    +  '(zk0Vr',
    +  '(+i0MAj20}}-I',
    +  'A,i0?2#30Vr',
    +  'A-i0EIS40Vr',
    +  'Y-i0EY]40}}-I',
    +  'w-i0IC60}}-I',
    +  '(-i06^U70Vr',
    +  '^-i0Q`70}}-I',
    +  'I}r0Vr',
    +  'wqs0Vr',
    +  '2.i02YA0Vr',
    +  'A.i0Y}A0Vr',
    +  'I.i0(qB0Vr',
    +  'Q.i0',
    +  'oh40FN^L80d8',
    +  'oJD0#2]5#2IGs0MX5#2OcGcGcGE'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_OTHER_EAST_ASIAN_SCRIPTS = [
    +  goog.i18n.CharPickerData.MSG_CP_BOPOMOFO,
    +  goog.i18n.CharPickerData.MSG_CP_HIRAGANA,
    +  goog.i18n.CharPickerData.MSG_CP_KATAKANA,
    +  goog.i18n.CharPickerData.MSG_CP_LISU,
    +  goog.i18n.CharPickerData.MSG_CP_MIAO,
    +  goog.i18n.CharPickerData.MSG_CP_MONGOLIAN,
    +  goog.i18n.CharPickerData.MSG_CP_OLD_TURKIC,
    +  goog.i18n.CharPickerData.MSG_CP_PHAGS_PA,
    +  goog.i18n.CharPickerData.MSG_CP_YI,
    +  goog.i18n.CharPickerData.MSG_CP_HISTORIC + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_PHAGS_PA,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_BOPOMOFO,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_HIRAGANA,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_KATAKANA,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_PHAGS_PA,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY + ' - ' +
    +      goog.i18n.CharPickerData.MSG_CP_YI
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_OTHER_EAST_ASIAN_SCRIPTS = [
    +  'AzC0M88,8F1X1mWM]Ht3XAV2I8s06+f(06^`B0M',
    +  'AzC0M88F2X1mWM8#7.H8fD6QCD1T0l065is0U196G6f8wqs0946',
    +  'AzC0M88F2X1mWM%8N8fD6n8V1I2D1L0l065is0U196:8Egqs0946',
    +  'oph0l3m6pc',
    +  '2591F611F4f1d1',
    +  'gU60?O8,m738t4$t38aEE:4H9',
    +  '2>,0l6',
    +  'wU6068AU606e,Gs',
    +  'AzC06e,Gs2qT0-18}}-FO@4DL10',
    +  'ohi0}4',
    +  'Ql)0M',
    +  '^%C0f91MF1^oU1bE$0Ujys06',
    +  '^%C0HIPDF1vRF48@7g`r0N18}3r%s06',
    +  'Ql)0M',
    +  'Ql)0M'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_1_STROKE_RADICALS = [
    +  '\u4E00',
    +  '\u4E28',
    +  '\u4E36',
    +  '\u4E3F',
    +  '\u4E59',
    +  '\u4E85',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_1_STROKE_RADICALS = [
    +  'ItK0l3]1f7YL10',
    +  ';wK0M8!',
    +  'AyK0k8[',
    +  '^yK0,8N1^w30',
    +  'Q#K0sG}2YfL0',
    +  'Q)K0k',
    +  '(bC0c]R]q8O8f2EgqB2E5Cl1]116$f7fG',
    +  'A(D0t3(rX1V288k8!8k8868|8l188U8718M8N48E88GE8#48MG@3oA20]G2P60;QB0]9^(20^7L0t2'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_2_STROKE_RADICALS = [
    +  '\u4E8C',
    +  '\u4EA0',
    +  '\u4EBA',
    +  '\u513F',
    +  '\u5165',
    +  '\u516B',
    +  '\u5182',
    +  '\u5196',
    +  '\u51AB',
    +  '\u51E0',
    +  '\u51F5',
    +  '\u5200',
    +  '\u529B',
    +  '\u52F9',
    +  '\u5315',
    +  '\u531A',
    +  '\u5338',
    +  '\u5341',
    +  '\u535C',
    +  '\u5369',
    +  '\u5382',
    +  '\u53B6',
    +  '\u53C8',
    +  '\u8BA0',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_2_STROKE_RADICALS = [
    +  '^)K0M8N1',
    +  '(+K0N2',
    +  'A.K0lww)K0',
    +  '(gL0V3',
    +  'IkL0cI870',
    +  '(kL0}1QyK0',
    +  ';mL0#1Yw50',
    +  'woL0-1',
    +  'oqL0#4',
    +  'YvL0-1',
    +  'QxL0?',
    +  'QyL0}D',
    +  'Y;L0V58@2',
    +  '^^L0d2',
    +  'g{L0U',
    +  '^{L0t2^IK0',
    +  'w0M0!',
    +  'g1M0E8}1QHK0',
    +  '^3M071',
    +  'A5M0F2',
    +  'Y7M0t4;XD0',
    +  'ACM0l1',
    +  '(DM0V2IS10',
    +  'Y]a0tD',
    +  'QcC0}1%P8]qG688P1W6G6mO8^pB2F28d292%B6f6%A15P1ODrl1f1E9386H18e11Ee[n16[91e11.G$H1n18611$X2cX5k',
    +  ';+D0tN8l49H2i40kAsS1uH3v1H788]9@18}2872Gk8E8|8s88E8G-18778@28lF8-6G,8@48#486GF28d28t18t48N3874868-78F58V18}28F48l48lG868d18N18#18!8FN8@98FP8s8}F8N28,8VG8F18tF8}2(s30%U;@101bI-50QE60^{40;X60IhB0}Oo_20d3j%S1'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_3_STROKE_RADICALS = [
    +  '\u53E3',
    +  '\u56D7',
    +  '\u571F',
    +  '\u58EB',
    +  '\u5902',
    +  '\u590A',
    +  '\u5915',
    +  '\u5927',
    +  '\u5973',
    +  '\u5B50',
    +  '\u5B80',
    +  '\u5BF8',
    +  '\u5C0F',
    +  '\u5C22',
    +  '\u5C38',
    +  '\u5C6E',
    +  '\u5C71',
    +  '\u5DDB',
    +  '\u5DE5',
    +  '\u5DF1',
    +  '\u5DFE',
    +  '\u5E72',
    +  '\u5E7A',
    +  '\u5E7F',
    +  '\u5EF4',
    +  '\u5EFE',
    +  '\u5F0B',
    +  '\u5F13',
    +  '\u5F50',
    +  '\u5F61',
    +  '\u5F73',
    +  '\u7E9F',
    +  '\u95E8',
    +  '\u98DE',
    +  '\u9963',
    +  '\u9A6C',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_3_STROKE_RADICALS = [
    +  'IGM0dY8FM8tB',
    +  '^`M0d6^GJ0',
    +  'g3N0tZ8}48!Q#I0Ge',
    +  'gCN0%W}1',
    +  'YlN0k',
    +  '2mN0|',
    +  'AnN0l1',
    +  '(oN0-6',
    +  'wvN07D8}T',
    +  '2DO0N4',
    +  'YHO0-Aw]50',
    +  'QSO0}1',
    +  'YUO0t1^=H0',
    +  'AWO0@1',
    +  'AYO08t4',
    +  '2dO0E',
    +  'A$K0A#30-W',
    +  'I.O0c8E',
    +  'A:O0|',
    +  'I;O071',
    +  'Y<O0V78@2',
    +  '^{O0s',
    +  '2$K0oM40U',
    +  'A}O0lAw7H0',
    +  '(9P0,',
    +  'wAP071',
    +  ';BP0s',
    +  'oCP0d5',
    +  'AIP0d1',
    +  'wJP0!8s',
    +  'QLP0F7^]G06',
    +  '(gX0tD',
    +  'wud0t4',
    +  'obe0',
    +  'wne0l4',
    +  '(:e0V5',
    +  'YeC0#2P=11Wm11686W(uB2}18l58E8EGMP8:5]6]9Lvl1G86:1mP26m6%1me%1E11X1OmEf1692Ge6H1%1Gm8GX3kX4[F1',
    +  'YAE0@G8V(I!20|I!10E:5fX18EwYR1%1u8Gn3v11B1693P2uO91$8OH2H713vMXG%1%K:6]SG13%2H@vX93tU8F587w8}V8-68tA8dO8db8V38758V28t58F18k8#C8t!8V78V98tU8lT8de8}}V98lB8}B8#387987H8#38NJ8@78U8N18U8kgE10(L10v_X4ngA6109Nn2v2Ac101O1}HSQ*1094^.50N2:BP6Ay10Q<40]5;s20AE20V1H9^j20l1%g-3YY20YU10}zAv10@2;310F1]E72X3}1DeT18'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_4_STROKE_RADICALS = [
    +  '\u5FC3',
    +  '\u6208',
    +  '\u6236',
    +  '\u624B',
    +  '\u652F',
    +  '\u6534',
    +  '\u6587',
    +  '\u6597',
    +  '\u65A4',
    +  '\u65B9',
    +  '\u65E0',
    +  '\u65E5',
    +  '\u66F0',
    +  '\u6708',
    +  '\u6728',
    +  '\u6B20',
    +  '\u6B62',
    +  '\u6B79',
    +  '\u6BB3',
    +  '\u6BCB',
    +  '\u6BD4',
    +  '\u6BDB',
    +  '\u6C0F',
    +  '\u6C14',
    +  '\u6C34',
    +  '\u706B',
    +  '\u722A',
    +  '\u7236',
    +  '\u723B',
    +  '\u723F',
    +  '\u7247',
    +  '\u7259',
    +  '\u725B',
    +  '\u72AC',
    +  '\u89C1',
    +  '\u8D1D',
    +  '\u8F66',
    +  '\u97E6',
    +  '\u98CE',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_4_STROKE_RADICALS = [
    +  'oSP0#q',
    +  'Y]P074',
    +  'o{P0-1',
    +  'g}P0F)2gF0',
    +  '((Q0U',
    +  '(oM0YG40d7(cA0',
    +  '(;Q0V1YNF0',
    +  'I=Q071',
    +  'Y>Q0-1',
    +  'Q@Q0d3',
    +  ';^Q0U',
    +  ';@L0Y350FOH1os602W80',
    +  ';@L0wR5071868k',
    +  '(LR0-2Y070',
    +  'wOR0}}N4:+IBD0',
    +  '2TS0@5',
    +  '2ZS0}1I-D0',
    +  'AbS0F5',
    +  'YgS072',
    +  'oiS0!',
    +  'YjS0k',
    +  '2kS0t4',
    +  '(oS0U',
    +  'IpS0-2',
    +  'AsS0dH8V[',
    +  'I$T0le;@402[60nI12',
    +  'A:M0wV70|',
    +  '^HU0U',
    +  'YIU0M',
    +  'IxK0gl90s',
    +  'gJU0l1',
    +  'ALU06',
    +  'QLU0N7',
    +  'wSU0lJ',
    +  ';ba0U8?',
    +  '2Sb0V6',
    +  'I]b0#4',
    +  '2Fe0k',
    +  'Aae071',
    +  '^SC0HE}2::MGEG.OovB2:8e#4G6G-28}2871]7$65ml1G$mEm6OGOEWE%1eE916Ou6m868W$6m6GU11OE8W91WEWGMmOG6eM$8e6W6mG611Of371136P2}18EH4M',
    +  '^aE0]uFq8#@^U20U%LEwSS1f7HLfkX2vCH4vM(a10gv10IO10Yg30Hz}}VE8to8-w8@J8-28tK8td8N48FC8E8l68cGNM8V#8#98lK8-A8-A8|8728E8l287N8}}#E8@N8V%8tC88V88-88lC8N18@48t38l`;Y20(>101dYk201)XQ6nUv^Xao940kAi10cv3QF40UHdXG|fe8o^40}}l3YD10c]Ak]7@19YcX4UjUT16'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_5_STROKE_RADICALS = [
    +  '\u7384',
    +  '\u7389',
    +  '\u74DC',
    +  '\u74E6',
    +  '\u7518',
    +  '\u751F',
    +  '\u7528',
    +  '\u7530',
    +  '\u758B',
    +  '\u7592',
    +  '\u7676',
    +  '\u767D',
    +  '\u76AE',
    +  '\u76BF',
    +  '\u76EE',
    +  '\u77DB',
    +  '\u77E2',
    +  '\u77F3',
    +  '\u793A',
    +  '\u79B8',
    +  '\u79BE',
    +  '\u7A74',
    +  '\u7ACB',
    +  '\u9485',
    +  '\u957F',
    +  '\u9E1F',
    +  '\u9F99',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_5_STROKE_RADICALS = [
    +  'QmU0U',
    +  '(mU0#U',
    +  'o@U0,',
    +  'g[U0d4',
    +  '2{U0k',
    +  'w{U0!',
    +  'g|U0s',
    +  'I}U0N48k8}2',
    +  'g7V0k',
    +  'A8V0tK',
    +  ';SV0k',
    +  'o3V0:PV4',
    +  '^XV0d1A;A0',
    +  'gZV0F4',
    +  '(dV0dL(mA0',
    +  'QzV0k',
    +  '^zV0d1',
    +  'w-S0(@20tT',
    +  'I5W0VBQH40P4;-506',
    +  'wGW0c((20',
    +  'IHW0dG',
    +  '(XW0-7',
    +  'wfW0#1G72Ai70',
    +  'YOd0@L',
    +  'Ald0',
    +  ';-f0#7',
    +  'IIg0E',
    +  'QkC0}1n.O86n1^?B2V18V3{gl1$f2u6[P1[68$$P1P16926u[[E91$6.u:2UH4|f6O|11X1[E',
    +  'AoG0@:;12071n^kXD6I4R1:4WnB9d[15:49lHkX.1pP5Hw]nf]^H20()109d;u101@]2%KY!10:9f.;(307k8dL8}38@88-98?8V?WdA8}S87Q8748l!8-T8#d8d28lI8FK8#12@30nQI,10w^402B20F22,50-1AQ30}b(F10V49f}3]3'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_6_STROKE_RADICALS = [
    +  '\u7AF9',
    +  '\u7C73',
    +  '\u7CF8',
    +  '\u7F36',
    +  '\u7F51',
    +  '\u7F8A',
    +  '\u7FBD',
    +  '\u8001',
    +  '\u800C',
    +  '\u8012',
    +  '\u8033',
    +  '\u807F',
    +  '\u8089',
    +  '\u81E3',
    +  '\u81EA',
    +  '\u81F3',
    +  '\u81FC',
    +  '\u820C',
    +  '\u821B',
    +  '\u821F',
    +  '\u826E',
    +  '\u8272',
    +  '\u8278',
    +  '\u864D',
    +  '\u866B',
    +  '\u8840',
    +  '\u884C',
    +  '\u8863',
    +  '\u897E',
    +  '\u9875',
    +  '\u9F50',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_6_STROKE_RADICALS = [
    +  ';jW0NY',
    +  ';,N0YL70;<10}B',
    +  'Q4X0Vc',
    +  'guX0V2',
    +  '^wX075',
    +  'IlV0;G20l4',
    +  '(*X076',
    +  '^;X0?',
    +  '^<X0c',
    +  'g=X0@2',
    +  'g@X0-6',
    +  'Y|X0,',
    +  ';^O0Y490tP8l5',
    +  '(UY0k',
    +  'YVY0!',
    +  'IWY0!',
    +  '2XY0V1',
    +  'gYY0N1',
    +  ';ZY0M',
    +  'IaY077',
    +  'YhY0M',
    +  '(hY0c',
    +  'QiY0ld8lC8taI!60H1u6.',
    +  'gKP0^OA0U872',
    +  'ImZ0lg',
    +  ';2a0|',
    +  '^3a0}1',
    +  '^!L0(K10IAD0tP2@50',
    +  '(Va071',
    +  '2Se0l4',
    +  'oBg06',
    +  'YmC0l2onC2Wn56XC-28U86G68M8@4jql1MemO68691Em6e6.6GO6n1Oem6P268me$6n19112Eue86WWW:168:4?v6G?%2',
    +  'o5E0oq10;%10VE8VH91l;P^w0S1Q0101Io3102E20XZoi10n>2;10XUPN18e]1;n30v6m6(L40vHvCX1:8;g10A{30HM}}N@X2#B8F68@D8VI8@(8NQG#L8#68t18tO8#v8Na8##8VC8#^8tt(j10wB30YE30E(870NF13#hfxd1>RT18'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_7_STROKE_RADICALS = [
    +  '\u5364',
    +  '\u898B',
    +  '\u89D2',
    +  '\u8A00',
    +  '\u8C37',
    +  '\u8C46',
    +  '\u8C55',
    +  '\u8C78',
    +  '\u8C9D',
    +  '\u8D64',
    +  '\u8D70',
    +  '\u8DB3',
    +  '\u8EAB',
    +  '\u8ECA',
    +  '\u8F9B',
    +  '\u8FB0',
    +  '\u8FB5',
    +  '\u9091',
    +  '\u9149',
    +  '\u91C6',
    +  '\u91CC',
    +  '\u9F9F',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_7_STROKE_RADICALS = [
    +  'w4M0(<J0',
    +  '^Wa0?8#3',
    +  'Yda074QB50',
    +  'oha0#b^R50e',
    +  'A7b0N1',
    +  'g8b0N1',
    +  ';9b073',
    +  '2Db0N3',
    +  'YGb0}88V2',
    +  'gYb0|',
    +  'oZb0}5A(40',
    +  'wfb0dM',
    +  'I$b0#2',
    +  '2)b07EwQ4012',
    +  '2|b0-1',
    +  '^}b0U',
    +  '(.O0oFD0@J',
    +  'YKc0tG',
    +  'Abc0NB',
    +  'gmc0c',
    +  '2nc0U',
    +  '(Ig0',
    +  'oaC0XE#1X*en1;}B2n5F18E8!jul186X1ev1[.mn1Gn18116P1[8m]111%1n1v1[G92G6un4kX7|v1',
    +  'QAE0gj40lFu-8etLO#D^DT1PL9,AY30v9]_A^60Yl10;N50Az10oi10(I80F`8M8V58Nh8lCu}}}hml3Glb8N@;820o{80|m-3n3V3u-712#9nwv3+zT16'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_8_STROKE_RADICALS = [
    +  '\u91D1',
    +  '\u9577',
    +  '\u9580',
    +  '\u961C',
    +  '\u96B6',
    +  '\u96B9',
    +  '\u96E8',
    +  '\u9751',
    +  '\u975E',
    +  '\u9C7C',
    +  '\u9F7F',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_8_STROKE_RADICALS = [
    +  'wVS0(HA0-!o_20GG',
    +  'Ykd0s',
    +  'Ild0V9',
    +  'Yzd0@D',
    +  'Y<d0E',
    +  'w<d0F4',
    +  '^@d0d9',
    +  'g1e071',
    +  'w2e0M',
    +  '(Xf0d9',
    +  ';Fg0F1',
    +  ';qC0!:(IvB2vf71865vl194m.uu14:1]1EWH191$H1m92v1v195X8M',
    +  'QTJ0l8H1F4OV68-5:ssQMR1AQ50Q>U0#88@yP2dcf1798N#8FJQn30@1^;106;y30l8f4@1P1N61OV39B!DzT1E'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_9_STROKE_RADICALS = [
    +  '\u9762',
    +  '\u9769',
    +  '\u97CB',
    +  '\u97ED',
    +  '\u97F3',
    +  '\u9801',
    +  '\u98A8',
    +  '\u98DB',
    +  '\u98DF',
    +  '\u9996',
    +  '\u9999',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_9_STROKE_RADICALS = [
    +  '23e0k',
    +  'w3e0-8',
    +  'oCe0V2',
    +  'wFe0c',
    +  'ghW06oy70F1',
    +  'gHe0dA',
    +  'wWe0V3',
    +  'Qbe0E',
    +  'wbe0@B',
    +  'Qse0E',
    +  'ose0t1',
    +  'wrC0?f)(AC2N1{gl1f298Ef56n8M',
    +  'ooH0g520-Q8!IHS1:_P32-30ARC0YA40](^b70gd807Y8lBelaW728NG91}Zv1t288-4Iz70d1mt1n1|el1H2N1'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_10_STROKE_RADICALS = [
    +  '\u99AC',
    +  '\u9AA8',
    +  '\u9AD8',
    +  '\u9ADF',
    +  '\u9B25',
    +  '\u9B2F',
    +  '\u9B32',
    +  '\u9B3C',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_10_STROKE_RADICALS = [
    +  'Que0VHQY106',
    +  'I@e0N4',
    +  'o_e0k',
    +  'I`e0N6',
    +  'o2f0,',
    +  'g3f0E',
    +  '(3f0,',
    +  'w4f0t2',
    +  'wsC0s^?C2Ubvl1:9nT',
    +  '^_J077O#9wM(1gQ10#Y]3};gl60@192l2'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_11_17_STROKE_RADICALS = [
    +  '\u9B5A',
    +  '\u9CE5',
    +  '\u9E75',
    +  '\u9E7F',
    +  '\u9EA5',
    +  '\u9EA6',
    +  '\u9EBB',
    +  '\u9EC3',
    +  '\u9ECD',
    +  '\u9ED1',
    +  '\u9EF9',
    +  '\u9EFD',
    +  '\u9EFE',
    +  '\u9F0E',
    +  '\u9F13',
    +  '\u9F20',
    +  '\u9F3B',
    +  '\u9F4A',
    +  '\u9F52',
    +  '\u9F8D',
    +  '\u9F9C',
    +  '\u9FA0',
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_11_17_STROKE_RADICALS = [
    +  'Y7f0NQXWPh',
    +  'Qhf0dB87B8l59c',
    +  'w@f0!',
    +  'o[f0V3',
    +  '2`f08d1',
    +  'A`f0n1E',
    +  '2|f0s',
    +  '(|f0,',
    +  'w}f0M',
    +  'IdN0(mI0s8#2',
    +  'w3g0M',
    +  '24g08|',
    +  'A4g091E',
    +  'o5g0U',
    +  '26g071',
    +  'I7g0V2',
    +  'w9g0N1',
    +  '2Bg0c',
    +  '(Bg0}3',
    +  'AHg0E8s',
    +  'gIg0E',
    +  ';Ig0c',
    +  'YtC0#12hC2fYt1>yl1692H26ef66P5946H5nE.6',
    +  'IDK0t9$@9uNDGkoOR1fk^x102.20nDQf301=^N50;g202j30M^>90od80g320to12t!]1-H8F[GN6284075f3@394E8l2.G'
    +];
    +
    +
    +/**
    + * Names of subcategories. Each message this array is the
    + * name for the corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_OTHER = [
    +  goog.i18n.CharPickerData.MSG_CP_CJK_STROKES,
    +  goog.i18n.CharPickerData.MSG_CP_IDEOGRAPHIC_DESCRIPTION,
    +  goog.i18n.CharPickerData.MSG_CP_OTHER,
    +  goog.i18n.CharPickerData.MSG_CP_COMPATIBILITY,
    +  goog.i18n.CharPickerData.MSG_CP_LESS_COMMON
    +];
    +
    +
    +/**
    + * List of characters in base88 encoding scheme. Each base88 encoded
    + * charater string represents corresponding subcategory specified in
    + * {@code goog.i18n.CharPickerData.subcategories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<string>}
    + */
    +goog.i18n.CharPickerData.CHARLIST_OF_HAN_OTHER = [
    +  'AQC0N28M8d7H%F3',
    +  'oxC0|',
    +  'AzC0M8|8}1mmWM2iT0o|O065ms0P3MH1',
    +  'gMD0F3PB|%CF2[U%8#2Q+r0M',
    +  'Q=727K'
    +];
    +
    +
    +/**
    + * Subcategory names. Each subarray in this array is a list of subcategory
    + * names for the corresponding category specified in
    + * {@code goog.i18n.CharPickerData.categories}.
    + * @type {Array<Array<string>>}
    + */
    +goog.i18n.CharPickerData.prototype.subcategories = [
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SYMBOL,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_EMOJI,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_PUNCTUATION,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_NUMBER,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_FORMAT_WHITESPACE,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_MODIFIER,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_LATIN,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_OTHER_EUROPEAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_AMERICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_AFRICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_MIDDLE_EASTERN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SOUTH_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_SOUTHEAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HANGUL,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_OTHER_EAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_1_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_2_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_3_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_4_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_5_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_6_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_7_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_8_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_9_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_10_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_11_17_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.SUBCATEGORY_NAMES_OF_HAN_OTHER
    +];
    +
    +
    +/**
    + * Character lists in base88 encoding scheme. Each subarray is a list of
    + * base88 encoded charater strings representing corresponding subcategory
    + * specified in {@code goog.i18n.CharPickerData.categories}. Encoding
    + * scheme is described in {@code goog.i18n.CharListDecompressor}.
    + * @type {Array<Array<string>>}
    + */
    +goog.i18n.CharPickerData.prototype.charList = [
    +  goog.i18n.CharPickerData.CHARLIST_OF_SYMBOL,
    +  goog.i18n.CharPickerData.CHARLIST_OF_EMOJI,
    +  goog.i18n.CharPickerData.CHARLIST_OF_PUNCTUATION,
    +  goog.i18n.CharPickerData.CHARLIST_OF_NUMBER,
    +  goog.i18n.CharPickerData.CHARLIST_OF_FORMAT_WHITESPACE,
    +  goog.i18n.CharPickerData.CHARLIST_OF_MODIFIER,
    +  goog.i18n.CharPickerData.CHARLIST_OF_LATIN,
    +  goog.i18n.CharPickerData.CHARLIST_OF_OTHER_EUROPEAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_AMERICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_AFRICAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_MIDDLE_EASTERN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_SOUTH_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_SOUTHEAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HANGUL,
    +  goog.i18n.CharPickerData.CHARLIST_OF_OTHER_EAST_ASIAN_SCRIPTS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_1_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_2_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_3_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_4_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_5_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_6_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_7_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_8_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_9_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_10_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_11_17_STROKE_RADICALS,
    +  goog.i18n.CharPickerData.CHARLIST_OF_HAN_OTHER
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/collation.js b/mapbox-gl/closure-library/closure/goog/i18n/collation.js
    new file mode 100644
    index 0000000000..7314912b88
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/collation.js
    @@ -0,0 +1,58 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Contains helper functions for performing locale-sensitive
    + *     collation.
    + */
    +
    +
    +goog.provide('goog.i18n.collation');
    +
    +
    +/**
    + * Returns the comparator for a locale. If a locale is not explicitly specified,
    + * a comparator for the user's locale will be returned. Note that if the browser
    + * does not support locale-sensitive string comparisons, the comparator returned
    + * will be a simple codepoint comparator.
    + *
    + * @param {string=} opt_locale the locale that the comparator is used for.
    + * @return {function(string, string): number} The locale-specific comparator.
    + */
    +goog.i18n.collation.createComparator = function(opt_locale) {
    +  // See http://code.google.com/p/v8-i18n.
    +  if (goog.i18n.collation.hasNativeComparator()) {
    +    var intl = goog.global.Intl;
    +    return new intl.Collator([opt_locale || goog.LOCALE]).compare;
    +  } else {
    +    return function(arg1, arg2) {
    +      return arg1.localeCompare(arg2);
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Returns true if a locale-sensitive comparator is available for a locale. If
    + * a locale is not explicitly specified, the user's locale is used instead.
    + *
    + * @param {string=} opt_locale The locale to be checked.
    + * @return {boolean} Whether there is a locale-sensitive comparator available
    + *     for the locale.
    + */
    +goog.i18n.collation.hasNativeComparator = function(opt_locale) {
    +  var intl = goog.global.Intl;
    +  return !!(intl && intl.Collator);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/collation_test.html b/mapbox-gl/closure-library/closure/goog/i18n/collation_test.html
    new file mode 100644
    index 0000000000..550b24610a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/collation_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.collation
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.collationTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/collation_test.js b/mapbox-gl/closure-library/closure/goog/i18n/collation_test.js
    new file mode 100644
    index 0000000000..d2cbe4e026
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/collation_test.js
    @@ -0,0 +1,64 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.i18n.collationTest');
    +goog.setTestOnly('goog.i18n.collationTest');
    +
    +goog.require('goog.i18n.collation');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var expectedFailures;
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testGetEnComparator() {
    +  goog.LOCALE = 'en';
    +  var compare = goog.i18n.collation.createComparator();
    +  // The côte/coté comparison fails in FF/Linux (v19.0) because
    +  // calling 'côte'.localeCompare('coté')  gives a negative number (wrong)
    +  // when the test is run but a positive number (correct) when calling
    +  // it later in the web console. FF/OSX doesn't have this problem.
    +  // Mozilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=856115
    +  expectedFailures.expectFailureFor(
    +      goog.userAgent.GECKO && goog.userAgent.LINUX);
    +  try {
    +    assertTrue(compare('côte', 'coté') > 0);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testGetFrComparator() {
    +  goog.LOCALE = 'fr-CA';
    +  var compare = goog.i18n.collation.createComparator();
    +  if (!goog.i18n.collation.hasNativeComparator()) return;
    +  assertTrue(compare('côte', 'coté') < 0);
    +}
    +
    +function testGetComparatorForSpecificLocale() {
    +  goog.LOCALE = 'en';
    +  var compare = goog.i18n.collation.createComparator('fr-CA');
    +  if (!goog.i18n.collation.hasNativeComparator('fr-CA')) return;
    +  // 'côte' and 'coté' sort differently for en and fr-CA.
    +  assertTrue(compare('côte', 'coté') < 0);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols.js b/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols.js
    new file mode 100644
    index 0000000000..979ceb3e8e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols.js
    @@ -0,0 +1,9631 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    +// implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Compact number formatting symbols.
    + *
    + * This file is autogenerated by script:
    + * http://go/generate_number_constants.py
    + * using the --for_closure flag.
    + * File generated from CLDR ver. 26
    + *
    + * To reduce the file size (which may cause issues in some JS
    + * developing environments), this file will only contain locales
    + * that are frequently used by web applications. This is defined as
    + * closure_tier1_locales and will change (most likely addition)
    + * over time.  Rest of the data can be found in another file named
    + * "compactnumberformatsymbols_ext.js", which will be generated at
    + * the same time together with this file.
    + *
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could fix CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.i18n.CompactNumberFormatSymbols');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_af');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_af_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_am');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_am_ET');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_001');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_az');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_az_Latn_AZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bg_BG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bn_BD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_br');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_br_FR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca_AD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca_ES');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca_ES_VALENCIA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca_FR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ca_IT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_chr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_chr_US');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cs');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cs_CZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cy');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cy_GB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_da');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_da_DK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_da_GL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_AT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_BE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_DE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_LU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_el');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_el_GR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_001');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_AS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_AU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_DG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_FM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_IE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_IO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MP');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_UM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_US');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_VG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_VI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_ZW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_419');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_EA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_ES');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_IC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_et');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_et_EE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_eu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_eu_ES');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fa');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fa_IR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fi');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fi_FI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fil');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fil_PH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_BL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_FR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_GF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_GP');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_PM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_RE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_YT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ga');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ga_IE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gl_ES');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gsw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gsw_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gsw_LI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gu_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_haw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_haw_US');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_he');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_he_IL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hi');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hi_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hr_HR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hu_HU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hy');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hy_AM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_id');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_id_ID');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_in');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_is');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_is_IS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_it');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_it_IT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_it_SM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_iw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ja');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ja_JP');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ka');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ka_GE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kk');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kk_Cyrl_KZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_km');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_km_KH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kn_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ko');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ko_KR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ky');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ky_Cyrl_KG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ln');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ln_CD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lo_LA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lt');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lt_LT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lv');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lv_LV');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mk');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mk_MK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ml');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ml_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mn_Cyrl_MN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mr_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ms');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ms_Latn_MY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mt');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mt_MT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_my');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_my_MM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nb_NO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nb_SJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ne');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ne_NP');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_NL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_no');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_no_NO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_or');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_or_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pa');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pa_Guru_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pl_PL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_BR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_PT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ro');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ro_RO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_RU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_si');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_si_LK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sk');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sk_SK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sl_SI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sq_AL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_RS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sv');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sv_SE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sw_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ta');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ta_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_te');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_te_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_th');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_th_TH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tr_TR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uk');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uk_UA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ur');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ur_PK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Latn_UZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vi');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vi_VN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_CN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_HK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hans_CN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_TW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zu_ZA');
    +
    +
    +/**
    + * Compact number formatting symbols for locale af.
    + */
    +goog.i18n.CompactNumberFormatSymbols_af = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '0'
    +    },
    +    '100000': {
    +      'other': '0'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0m'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0m'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0m'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mjd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mjd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mjd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duisend'
    +    },
    +    '10000': {
    +      'other': '00 duisend'
    +    },
    +    '100000': {
    +      'other': '000 duisend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale af_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_af_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_af;
    +
    +
    +/**
    + * Compact number formatting symbols for locale am.
    + */
    +goog.i18n.CompactNumberFormatSymbols_am = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u123A'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u123A'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u123A'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u121C\u1275\u122D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u121C\u1275\u122D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u121C\u1275\u122D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u1262'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u1262'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u1262'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u1275'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u1275'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u1275'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u123A'
    +    },
    +    '10000': {
    +      'other': '00 \u123A'
    +    },
    +    '100000': {
    +      'other': '000 \u123A'
    +    },
    +    '1000000': {
    +      'other': '0 \u121A\u120A\u12EE\u1295'
    +    },
    +    '10000000': {
    +      'other': '00 \u121A\u120A\u12EE\u1295'
    +    },
    +    '100000000': {
    +      'other': '000 \u121A\u120A\u12EE\u1295'
    +    },
    +    '1000000000': {
    +      'other': '0 \u1262\u120A\u12EE\u1295'
    +    },
    +    '10000000000': {
    +      'other': '00 \u1262\u120A\u12EE\u1295'
    +    },
    +    '100000000000': {
    +      'other': '000 \u1262\u120A\u12EE\u1295'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u1275\u122A\u120A\u12EE\u1295'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u1275\u122A\u120A\u12EE\u1295'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u1275\u122A\u120A\u12EE\u1295'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale am_ET.
    + */
    +goog.i18n.CompactNumberFormatSymbols_am_ET =
    +    goog.i18n.CompactNumberFormatSymbols_am;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_001.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_001 =
    +    goog.i18n.CompactNumberFormatSymbols_ar;
    +
    +
    +/**
    + * Compact number formatting symbols for locale az.
    + */
    +goog.i18n.CompactNumberFormatSymbols_az = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale az_Latn_AZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_az_Latn_AZ =
    +    goog.i18n.CompactNumberFormatSymbols_az;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0445\u0438\u043B.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u043B.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u043B.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0445\u0438\u043B\u044F\u0434\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u043B\u044F\u0434\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u043B\u044F\u0434\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bg_BG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bg_BG =
    +    goog.i18n.CompactNumberFormatSymbols_bg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u09B2\u09BE\u0996'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '10000': {
    +      'other': '00 \u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '100000': {
    +      'other': '0 \u09B2\u09BE\u0996'
    +    },
    +    '1000000': {
    +      'other': '0 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000': {
    +      'other': '00 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000': {
    +      'other': '000 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '1000000000': {
    +      'other': '0 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000000': {
    +      'other': '00 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000000': {
    +      'other': '000 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bn_BD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bn_BD =
    +    goog.i18n.CompactNumberFormatSymbols_bn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale br.
    + */
    +goog.i18n.CompactNumberFormatSymbols_br = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale br_FR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_br_FR =
    +    goog.i18n.CompactNumberFormatSymbols_br;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0m'
    +    },
    +    '10000': {
    +      'other': '00m'
    +    },
    +    '100000': {
    +      'other': '000m'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0000\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00mM'
    +    },
    +    '100000000000': {
    +      'other': '000mM'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 milers'
    +    },
    +    '10000': {
    +      'other': '00 milers'
    +    },
    +    '100000': {
    +      'other': '000 milers'
    +    },
    +    '1000000': {
    +      'other': '0 milions'
    +    },
    +    '10000000': {
    +      'other': '00 milions'
    +    },
    +    '100000000': {
    +      'other': '000 milions'
    +    },
    +    '1000000000': {
    +      'other': '0 milers de milions'
    +    },
    +    '10000000000': {
    +      'other': '00 milers de milions'
    +    },
    +    '100000000000': {
    +      'other': '000 milers de milions'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilions'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilions'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca_AD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca_AD =
    +    goog.i18n.CompactNumberFormatSymbols_ca;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca_ES.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca_ES =
    +    goog.i18n.CompactNumberFormatSymbols_ca;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca_ES_VALENCIA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca_ES_VALENCIA =
    +    goog.i18n.CompactNumberFormatSymbols_ca;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca_FR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca_FR =
    +    goog.i18n.CompactNumberFormatSymbols_ca;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ca_IT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ca_IT =
    +    goog.i18n.CompactNumberFormatSymbols_ca;
    +
    +
    +/**
    + * Compact number formatting symbols for locale chr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_chr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale chr_US.
    + */
    +goog.i18n.CompactNumberFormatSymbols_chr_US =
    +    goog.i18n.CompactNumberFormatSymbols_chr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale cs.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cs = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tis.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tis.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tis.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tis\u00EDc'
    +    },
    +    '10000': {
    +      'other': '00 tis\u00EDc'
    +    },
    +    '100000': {
    +      'other': '000 tis\u00EDc'
    +    },
    +    '1000000': {
    +      'other': '0 milion\u016F'
    +    },
    +    '10000000': {
    +      'other': '00 milion\u016F'
    +    },
    +    '100000000': {
    +      'other': '000 milion\u016F'
    +    },
    +    '1000000000': {
    +      'other': '0 miliard'
    +    },
    +    '10000000000': {
    +      'other': '00 miliard'
    +    },
    +    '100000000000': {
    +      'other': '000 miliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilion\u016F'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilion\u016F'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilion\u016F'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale cs_CZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cs_CZ =
    +    goog.i18n.CompactNumberFormatSymbols_cs;
    +
    +
    +/**
    + * Compact number formatting symbols for locale cy.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cy = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 miliwn'
    +    },
    +    '10000000': {
    +      'other': '00 miliwn'
    +    },
    +    '100000000': {
    +      'other': '000 miliwn'
    +    },
    +    '1000000000': {
    +      'other': '0 biliwn'
    +    },
    +    '10000000000': {
    +      'other': '00 biliwn'
    +    },
    +    '100000000000': {
    +      'other': '000 biliwn'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliwn'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliwn'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliwn'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale cy_GB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cy_GB =
    +    goog.i18n.CompactNumberFormatSymbols_cy;
    +
    +
    +/**
    + * Compact number formatting symbols for locale da.
    + */
    +goog.i18n.CompactNumberFormatSymbols_da = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0td'
    +    },
    +    '10000': {
    +      'other': '00\u00A0td'
    +    },
    +    '100000': {
    +      'other': '000\u00A0td'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mia'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mia'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mia'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bill'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bill'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bill'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusind'
    +    },
    +    '10000': {
    +      'other': '00 tusind'
    +    },
    +    '100000': {
    +      'other': '000 tusind'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale da_DK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_da_DK =
    +    goog.i18n.CompactNumberFormatSymbols_da;
    +
    +
    +/**
    + * Compact number formatting symbols for locale da_GL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_da_GL =
    +    goog.i18n.CompactNumberFormatSymbols_da;
    +
    +
    +/**
    + * Compact number formatting symbols for locale de.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0Tsd.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0Tsd.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0Tsd.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Tausend'
    +    },
    +    '10000': {
    +      'other': '00 Tausend'
    +    },
    +    '100000': {
    +      'other': '000 Tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_AT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_AT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0Tsd.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0Tsd.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0Tsd.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Tausend'
    +    },
    +    '10000': {
    +      'other': '00 Tausend'
    +    },
    +    '100000': {
    +      'other': '000 Tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_BE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_BE =
    +    goog.i18n.CompactNumberFormatSymbols_de;
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_CH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0Tsd.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0Tsd.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0Tsd.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Tausend'
    +    },
    +    '10000': {
    +      'other': '00 Tausend'
    +    },
    +    '100000': {
    +      'other': '000 Tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_DE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_DE =
    +    goog.i18n.CompactNumberFormatSymbols_de;
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_LU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_LU =
    +    goog.i18n.CompactNumberFormatSymbols_de;
    +
    +
    +/**
    + * Compact number formatting symbols for locale el.
    + */
    +goog.i18n.CompactNumberFormatSymbols_el = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u03B5\u03BA.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u03B5\u03BA.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u03B5\u03BA.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '10000': {
    +      'other': '00 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '100000': {
    +      'other': '000 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '1000000': {
    +      'other': '0 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000': {
    +      'other': '00 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000': {
    +      'other': '000 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '1000000000': {
    +      'other': '0 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000000': {
    +      'other': '00 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000000': {
    +      'other': '000 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale el_GR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_el_GR =
    +    goog.i18n.CompactNumberFormatSymbols_el;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_001.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_001 =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_AS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_AS =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_AU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_AU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_DG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_DG =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_FM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_FM =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GB = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GU =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_IE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_IE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_IN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_IO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_IO =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MH =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MP.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MP =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PR =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PW =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TC =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_UM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_UM =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_US.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_US =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_VG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_VG =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_VI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_VI =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_ZA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_ZW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_ZW =
    +    goog.i18n.CompactNumberFormatSymbols_en;
    +
    +
    +/**
    + * Compact number formatting symbols for locale es.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0000M'
    +    },
    +    '10000000000': {
    +      'other': '00MRD'
    +    },
    +    '100000000000': {
    +      'other': '000MRD'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000000': {
    +      'other': '000B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_419.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_419 = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_EA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_EA =
    +    goog.i18n.CompactNumberFormatSymbols_es;
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_ES.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_ES =
    +    goog.i18n.CompactNumberFormatSymbols_es;
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_IC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_IC =
    +    goog.i18n.CompactNumberFormatSymbols_es;
    +
    +
    +/**
    + * Compact number formatting symbols for locale et.
    + */
    +goog.i18n.CompactNumberFormatSymbols_et = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tuh'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tuh'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tuh'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0trl'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0trl'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0trl'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tuhat'
    +    },
    +    '10000': {
    +      'other': '00 tuhat'
    +    },
    +    '100000': {
    +      'other': '000 tuhat'
    +    },
    +    '1000000': {
    +      'other': '0 miljonit'
    +    },
    +    '10000000': {
    +      'other': '00 miljonit'
    +    },
    +    '100000000': {
    +      'other': '000 miljonit'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardit'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardit'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardit'
    +    },
    +    '1000000000000': {
    +      'other': '0 triljonit'
    +    },
    +    '10000000000000': {
    +      'other': '00 triljonit'
    +    },
    +    '100000000000000': {
    +      'other': '000 triljonit'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale et_EE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_et_EE =
    +    goog.i18n.CompactNumberFormatSymbols_et;
    +
    +
    +/**
    + * Compact number formatting symbols for locale eu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_eu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '00000'
    +    },
    +    '100000': {
    +      'other': '000000'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0000\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00000\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000000\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '00000'
    +    },
    +    '100000': {
    +      'other': '000000'
    +    },
    +    '1000000': {
    +      'other': '0 milioi'
    +    },
    +    '10000000': {
    +      'other': '00 milioi'
    +    },
    +    '100000000': {
    +      'other': '000 milioi'
    +    },
    +    '1000000000': {
    +      'other': '0000 milioi'
    +    },
    +    '10000000000': {
    +      'other': '00000 milioi'
    +    },
    +    '100000000000': {
    +      'other': '000000 milioi'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilioi'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilioi'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilioi'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale eu_ES.
    + */
    +goog.i18n.CompactNumberFormatSymbols_eu_ES =
    +    goog.i18n.CompactNumberFormatSymbols_eu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fa.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fa = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0647\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00 \u0647\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '000 \u0647\u0632\u0627\u0631'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fa_IR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fa_IR =
    +    goog.i18n.CompactNumberFormatSymbols_fa;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fi.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fi = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0t.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0t.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0t.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0milj.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0milj.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0milj.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bilj.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bilj.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bilj.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tuhatta'
    +    },
    +    '10000': {
    +      'other': '00 tuhatta'
    +    },
    +    '100000': {
    +      'other': '000 tuhatta'
    +    },
    +    '1000000': {
    +      'other': '0 miljoonaa'
    +    },
    +    '10000000': {
    +      'other': '00 miljoonaa'
    +    },
    +    '100000000': {
    +      'other': '000 miljoonaa'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardia'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardia'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardia'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoonaa'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoonaa'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoonaa'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fi_FI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fi_FI =
    +    goog.i18n.CompactNumberFormatSymbols_fi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fil.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fil = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 libo'
    +    },
    +    '10000': {
    +      'other': '00 libo'
    +    },
    +    '100000': {
    +      'other': '000 libo'
    +    },
    +    '1000000': {
    +      'other': '0 milyon'
    +    },
    +    '10000000': {
    +      'other': '00 milyon'
    +    },
    +    '100000000': {
    +      'other': '000 milyon'
    +    },
    +    '1000000000': {
    +      'other': '0 bilyon'
    +    },
    +    '10000000000': {
    +      'other': '00 bilyon'
    +    },
    +    '100000000000': {
    +      'other': '000 bilyon'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilyon'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilyon'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilyon'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fil_PH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fil_PH =
    +    goog.i18n.CompactNumberFormatSymbols_fil;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_BL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_BL =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0G'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0G'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0G'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0T'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0T'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_FR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_FR =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_GF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_GF =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_GP.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_GP =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MC =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MF =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MQ =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_PM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_PM =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_RE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_RE =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_YT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_YT =
    +    goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ga.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ga = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0k'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 m\u00EDle'
    +    },
    +    '10000': {
    +      'other': '00 m\u00EDle'
    +    },
    +    '100000': {
    +      'other': '000 m\u00EDle'
    +    },
    +    '1000000': {
    +      'other': '0 milli\u00FAn'
    +    },
    +    '10000000': {
    +      'other': '00 milli\u00FAn'
    +    },
    +    '100000000': {
    +      'other': '000 milli\u00FAn'
    +    },
    +    '1000000000': {
    +      'other': '0 billi\u00FAn'
    +    },
    +    '10000000000': {
    +      'other': '00 billi\u00FAn'
    +    },
    +    '100000000000': {
    +      'other': '000 billi\u00FAn'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilli\u00FAn'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilli\u00FAn'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilli\u00FAn'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ga_IE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ga_IE =
    +    goog.i18n.CompactNumberFormatSymbols_ga;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 mill\u00F3ns'
    +    },
    +    '10000000': {
    +      'other': '00 mill\u00F3ns'
    +    },
    +    '100000000': {
    +      'other': '000 mill\u00F3ns'
    +    },
    +    '1000000000': {
    +      'other': '0 mil mill\u00F3ns'
    +    },
    +    '10000000000': {
    +      'other': '00 mil mill\u00F3ns'
    +    },
    +    '100000000000': {
    +      'other': '000 mil mill\u00F3ns'
    +    },
    +    '1000000000000': {
    +      'other': '0 bill\u00F3ns'
    +    },
    +    '10000000000000': {
    +      'other': '00 bill\u00F3ns'
    +    },
    +    '100000000000000': {
    +      'other': '000 bill\u00F3ns'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale gl_ES.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gl_ES =
    +    goog.i18n.CompactNumberFormatSymbols_gl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gsw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gsw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tsd'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tsd'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tsd'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tausend'
    +    },
    +    '10000': {
    +      'other': '00 tausend'
    +    },
    +    '100000': {
    +      'other': '000 tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale gsw_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gsw_CH =
    +    goog.i18n.CompactNumberFormatSymbols_gsw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gsw_LI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gsw_LI =
    +    goog.i18n.CompactNumberFormatSymbols_gsw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0AB9\u0A9C\u0ABE\u0AB0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0AB9\u0A9C\u0ABE\u0AB0'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0AB2\u0ABE\u0A96'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0AB2\u0ABE\u0A96'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0A95\u0AB0\u0ACB\u0AA1'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0A95\u0AB0\u0ACB\u0AA1'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0A85\u0AAC\u0A9C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0A85\u0AAC\u0A9C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0AA8\u0ABF\u0A96\u0AB0\u0ACD\u0AB5'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0AAE\u0AB9\u0ABE\u0AAA\u0AA6\u0ACD\u0AAE'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0AB6\u0A82\u0A95\u0AC1'
    +    },
    +    '100000000000000': {
    +      'other': '0\u00A0\u0A9C\u0AB2\u0AA7\u0ABF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0AB9\u0A9C\u0ABE\u0AB0'
    +    },
    +    '10000': {
    +      'other': '00 \u0AB9\u0A9C\u0ABE\u0AB0'
    +    },
    +    '100000': {
    +      'other': '0 \u0AB2\u0ABE\u0A96'
    +    },
    +    '1000000': {
    +      'other': '00 \u0AB2\u0ABE\u0A96'
    +    },
    +    '10000000': {
    +      'other': '0 \u0A95\u0AB0\u0ACB\u0AA1'
    +    },
    +    '100000000': {
    +      'other': '00 \u0A95\u0AB0\u0ACB\u0AA1'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0A85\u0AAC\u0A9C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0A85\u0AAC\u0A9C'
    +    },
    +    '100000000000': {
    +      'other': '0 \u0AA8\u0ABF\u0A96\u0AB0\u0ACD\u0AB5'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0AAE\u0AB9\u0ABE\u0AAA\u0AA6\u0ACD\u0AAE'
    +    },
    +    '10000000000000': {
    +      'other': '0 \u0AB6\u0A82\u0A95\u0AC1'
    +    },
    +    '100000000000000': {
    +      'other': '0 \u0A9C\u0AB2\u0AA7\u0ABF'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale gu_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gu_IN =
    +    goog.i18n.CompactNumberFormatSymbols_gu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale haw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_haw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale haw_US.
    + */
    +goog.i18n.CompactNumberFormatSymbols_haw_US =
    +    goog.i18n.CompactNumberFormatSymbols_haw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale he.
    + */
    +goog.i18n.CompactNumberFormatSymbols_he = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '\u200F0\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '10000': {
    +      'other': '\u200F00\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '100000': {
    +      'other': '\u200F000\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '1000000': {
    +      'other': '\u200F0\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '10000000': {
    +      'other': '\u200F00\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '100000000': {
    +      'other': '\u200F000\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '1000000000': {
    +      'other': '\u200F0\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '10000000000': {
    +      'other': '\u200F00\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '100000000000': {
    +      'other': '\u200F000\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '1000000000000': {
    +      'other': '\u200F0\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    },
    +    '10000000000000': {
    +      'other': '\u200F00\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    },
    +    '100000000000000': {
    +      'other': '\u200F000\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '\u200F0 \u05D0\u05DC\u05E3'
    +    },
    +    '10000': {
    +      'other': '\u200F00 \u05D0\u05DC\u05E3'
    +    },
    +    '100000': {
    +      'other': '\u200F000 \u05D0\u05DC\u05E3'
    +    },
    +    '1000000': {
    +      'other': '\u200F0 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '10000000': {
    +      'other': '\u200F00 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '100000000': {
    +      'other': '\u200F000 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '1000000000': {
    +      'other': '\u200F0 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '10000000000': {
    +      'other': '\u200F00 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '100000000000': {
    +      'other': '\u200F000 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '1000000000000': {
    +      'other': '\u200F0 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '10000000000000': {
    +      'other': '\u200F00 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '100000000000000': {
    +      'other': '\u200F000 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale he_IL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_he_IL =
    +    goog.i18n.CompactNumberFormatSymbols_he;
    +
    +
    +/**
    + * Compact number formatting symbols for locale hi.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hi = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0939\u091C\u093C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0939\u091C\u093C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0932\u093E\u0916'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0915.'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0915.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0905.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0905.'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0916.'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0916.'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0928\u0940\u0932'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u0928\u0940\u0932'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0939\u091C\u093C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00 \u0939\u091C\u093C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0 \u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '00 \u0932\u093E\u0916'
    +    },
    +    '10000000': {
    +      'other': '0 \u0915\u0930\u094B\u0921\u093C'
    +    },
    +    '100000000': {
    +      'other': '00 \u0915\u0930\u094B\u0921\u093C'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0905\u0930\u092C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0905\u0930\u092C'
    +    },
    +    '100000000000': {
    +      'other': '0 \u0916\u0930\u092C'
    +    },
    +    '1000000000000': {
    +      'other': '00 \u0916\u0930\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '000 \u0916\u0930\u092C'
    +    },
    +    '100000000000000': {
    +      'other': '0000 \u0916\u0930\u092C'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hi_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hi_IN =
    +    goog.i18n.CompactNumberFormatSymbols_hi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale hr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tis.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tis.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tis.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlr.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlr.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlr.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tisu\u0107a'
    +    },
    +    '10000': {
    +      'other': '00 tisu\u0107a'
    +    },
    +    '100000': {
    +      'other': '000 tisu\u0107a'
    +    },
    +    '1000000': {
    +      'other': '0 milijuna'
    +    },
    +    '10000000': {
    +      'other': '00 milijuna'
    +    },
    +    '100000000': {
    +      'other': '000 milijuna'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilijuna'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilijuna'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilijuna'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hr_HR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hr_HR =
    +    goog.i18n.CompactNumberFormatSymbols_hr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale hu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0E'
    +    },
    +    '10000': {
    +      'other': '00\u00A0E'
    +    },
    +    '100000': {
    +      'other': '000\u00A0E'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ezer'
    +    },
    +    '10000': {
    +      'other': '00 ezer'
    +    },
    +    '100000': {
    +      'other': '000 ezer'
    +    },
    +    '1000000': {
    +      'other': '0 milli\u00F3'
    +    },
    +    '10000000': {
    +      'other': '00 milli\u00F3'
    +    },
    +    '100000000': {
    +      'other': '000 milli\u00F3'
    +    },
    +    '1000000000': {
    +      'other': '0 milli\u00E1rd'
    +    },
    +    '10000000000': {
    +      'other': '00 milli\u00E1rd'
    +    },
    +    '100000000000': {
    +      'other': '000 milli\u00E1rd'
    +    },
    +    '1000000000000': {
    +      'other': '0 billi\u00F3'
    +    },
    +    '10000000000000': {
    +      'other': '00 billi\u00F3'
    +    },
    +    '100000000000000': {
    +      'other': '000 billi\u00F3'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hu_HU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hu_HU =
    +    goog.i18n.CompactNumberFormatSymbols_hu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale hy.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hy = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0570\u0566\u0580'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0570\u0566\u0580'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0570\u0566\u0580'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0574\u056C\u0576'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0574\u056C\u0576'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0574\u056C\u0576'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0574\u056C\u0580\u0564'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0574\u056C\u0580\u0564'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0574\u056C\u0580\u0564'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u057F\u0580\u056C\u0576'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u057F\u0580\u056C\u0576'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u057F\u0580\u056C\u0576'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0570\u0561\u0566\u0561\u0580'
    +    },
    +    '10000': {
    +      'other': '00 \u0570\u0561\u0566\u0561\u0580'
    +    },
    +    '100000': {
    +      'other': '000 \u0570\u0561\u0566\u0561\u0580'
    +    },
    +    '1000000': {
    +      'other': '0 \u0574\u056B\u056C\u056B\u0578\u0576'
    +    },
    +    '10000000': {
    +      'other': '00 \u0574\u056B\u056C\u056B\u0578\u0576'
    +    },
    +    '100000000': {
    +      'other': '000 \u0574\u056B\u056C\u056B\u0578\u0576'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0574\u056B\u056C\u056B\u0561\u0580\u0564'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0574\u056B\u056C\u056B\u0561\u0580\u0564'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0574\u056B\u056C\u056B\u0561\u0580\u0564'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u057F\u0580\u056B\u056C\u056B\u0578\u0576'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u057F\u0580\u056B\u056C\u056B\u0578\u0576'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u057F\u0580\u056B\u056C\u056B\u0578\u0576'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hy_AM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hy_AM =
    +    goog.i18n.CompactNumberFormatSymbols_hy;
    +
    +
    +/**
    + * Compact number formatting symbols for locale id.
    + */
    +goog.i18n.CompactNumberFormatSymbols_id = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0rb'
    +    },
    +    '100000': {
    +      'other': '000\u00A0rb'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0jt'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0jt'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0jt'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0T'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0T'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 miliar'
    +    },
    +    '10000000000': {
    +      'other': '00 miliar'
    +    },
    +    '100000000000': {
    +      'other': '000 miliar'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliun'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliun'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliun'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale id_ID.
    + */
    +goog.i18n.CompactNumberFormatSymbols_id_ID =
    +    goog.i18n.CompactNumberFormatSymbols_id;
    +
    +
    +/**
    + * Compact number formatting symbols for locale in.
    + */
    +goog.i18n.CompactNumberFormatSymbols_in = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0rb'
    +    },
    +    '100000': {
    +      'other': '000\u00A0rb'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0jt'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0jt'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0jt'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0T'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0T'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 miliar'
    +    },
    +    '10000000000': {
    +      'other': '00 miliar'
    +    },
    +    '100000000000': {
    +      'other': '000 miliar'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliun'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliun'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliun'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale is.
    + */
    +goog.i18n.CompactNumberFormatSymbols_is = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u00FE.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u00FE.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u00FE.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0m.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0m.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0m.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0ma.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0ma.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0ma.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u00FE\u00FAsund'
    +    },
    +    '10000': {
    +      'other': '00 \u00FE\u00FAsund'
    +    },
    +    '100000': {
    +      'other': '000 \u00FE\u00FAsund'
    +    },
    +    '1000000': {
    +      'other': '0 millj\u00F3nir'
    +    },
    +    '10000000': {
    +      'other': '00 millj\u00F3nir'
    +    },
    +    '100000000': {
    +      'other': '000 millj\u00F3nir'
    +    },
    +    '1000000000': {
    +      'other': '0 milljar\u00F0ar'
    +    },
    +    '10000000000': {
    +      'other': '00 milljar\u00F0ar'
    +    },
    +    '100000000000': {
    +      'other': '000 milljar\u00F0ar'
    +    },
    +    '1000000000000': {
    +      'other': '0 billj\u00F3nir'
    +    },
    +    '10000000000000': {
    +      'other': '00 billj\u00F3nir'
    +    },
    +    '100000000000000': {
    +      'other': '000 billj\u00F3nir'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale is_IS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_is_IS =
    +    goog.i18n.CompactNumberFormatSymbols_is;
    +
    +
    +/**
    + * Compact number formatting symbols for locale it.
    + */
    +goog.i18n.CompactNumberFormatSymbols_it = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '0'
    +    },
    +    '100000': {
    +      'other': '0'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 migliaia'
    +    },
    +    '10000': {
    +      'other': '00 migliaia'
    +    },
    +    '100000': {
    +      'other': '000 migliaia'
    +    },
    +    '1000000': {
    +      'other': '0 milioni'
    +    },
    +    '10000000': {
    +      'other': '00 milioni'
    +    },
    +    '100000000': {
    +      'other': '000 milioni'
    +    },
    +    '1000000000': {
    +      'other': '0 miliardi'
    +    },
    +    '10000000000': {
    +      'other': '00 miliardi'
    +    },
    +    '100000000000': {
    +      'other': '000 miliardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 migliaia di miliardi'
    +    },
    +    '10000000000000': {
    +      'other': '00 migliaia di miliardi'
    +    },
    +    '100000000000000': {
    +      'other': '000 migliaia di miliardi'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale it_IT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_it_IT =
    +    goog.i18n.CompactNumberFormatSymbols_it;
    +
    +
    +/**
    + * Compact number formatting symbols for locale it_SM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_it_SM =
    +    goog.i18n.CompactNumberFormatSymbols_it;
    +
    +
    +/**
    + * Compact number formatting symbols for locale iw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_iw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '\u200F0\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '10000': {
    +      'other': '\u200F00\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '100000': {
    +      'other': '\u200F000\u00A0\u05D0\u05DC\u05E3'
    +    },
    +    '1000000': {
    +      'other': '\u200F0\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '10000000': {
    +      'other': '\u200F00\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '100000000': {
    +      'other': '\u200F000\u00A0\u05DE\u05D9\u05DC\u05F3'
    +    },
    +    '1000000000': {
    +      'other': '\u200F0\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '10000000000': {
    +      'other': '\u200F00\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '100000000000': {
    +      'other': '\u200F000\u00A0\u05DE\u05DC\u05E8\u05D3'
    +    },
    +    '1000000000000': {
    +      'other': '\u200F0\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    },
    +    '10000000000000': {
    +      'other': '\u200F00\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    },
    +    '100000000000000': {
    +      'other': '\u200F000\u00A0\u05D1\u05D9\u05DC\u05F3'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '\u200F0 \u05D0\u05DC\u05E3'
    +    },
    +    '10000': {
    +      'other': '\u200F00 \u05D0\u05DC\u05E3'
    +    },
    +    '100000': {
    +      'other': '\u200F000 \u05D0\u05DC\u05E3'
    +    },
    +    '1000000': {
    +      'other': '\u200F0 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '10000000': {
    +      'other': '\u200F00 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '100000000': {
    +      'other': '\u200F000 \u05DE\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '1000000000': {
    +      'other': '\u200F0 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '10000000000': {
    +      'other': '\u200F00 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '100000000000': {
    +      'other': '\u200F000 \u05DE\u05D9\u05DC\u05D9\u05D0\u05E8\u05D3'
    +    },
    +    '1000000000000': {
    +      'other': '\u200F0 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '10000000000000': {
    +      'other': '\u200F00 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    },
    +    '100000000000000': {
    +      'other': '\u200F000 \u05D8\u05E8\u05D9\u05DC\u05D9\u05D5\u05DF'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ja.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ja = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ja_JP.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ja_JP =
    +    goog.i18n.CompactNumberFormatSymbols_ja;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ka.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ka = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u10D0\u10D7.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u10D0\u10D7.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u10D0\u10D7.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u10DB\u10DA\u10DC.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u10DB\u10DA\u10DC.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u10DB\u10DA\u10DC.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u10DB\u10DA\u10E0\u10D3.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u10DB\u10DA\u10E0\u10D3.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u10DB\u10DA\u10E0.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u10E2\u10E0\u10DA.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u10E2\u10E0\u10DA.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u10E2\u10E0\u10DA.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u10D0\u10D7\u10D0\u10E1\u10D8'
    +    },
    +    '10000': {
    +      'other': '00 \u10D0\u10D7\u10D0\u10E1\u10D8'
    +    },
    +    '100000': {
    +      'other': '000 \u10D0\u10D7\u10D0\u10E1\u10D8'
    +    },
    +    '1000000': {
    +      'other': '0 \u10DB\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    },
    +    '10000000': {
    +      'other': '00 \u10DB\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    },
    +    '100000000': {
    +      'other': '000 \u10DB\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    },
    +    '1000000000': {
    +      'other': '0 \u10DB\u10D8\u10DA\u10D8\u10D0\u10E0\u10D3\u10D8'
    +    },
    +    '10000000000': {
    +      'other': '00 \u10DB\u10D8\u10DA\u10D8\u10D0\u10E0\u10D3\u10D8'
    +    },
    +    '100000000000': {
    +      'other': '000 \u10DB\u10D8\u10DA\u10D8\u10D0\u10E0\u10D3\u10D8'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u10E2\u10E0\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u10E2\u10E0\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u10E2\u10E0\u10D8\u10DA\u10D8\u10DD\u10DC\u10D8'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ka_GE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ka_GE =
    +    goog.i18n.CompactNumberFormatSymbols_ka;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kk.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kk = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u043C\u044B\u04A3'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u043C\u044B\u04A3'
    +    },
    +    '100000': {
    +      'other': '000\u041C'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u0411'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kk_Cyrl_KZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kk_Cyrl_KZ =
    +    goog.i18n.CompactNumberFormatSymbols_kk;
    +
    +
    +/**
    + * Compact number formatting symbols for locale km.
    + */
    +goog.i18n.CompactNumberFormatSymbols_km = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u1796\u17B6\u1793\u17CB'
    +    },
    +    '10000': {
    +      'other': '0\u200B\u1798\u17BA\u17BB\u1793'
    +    },
    +    '100000': {
    +      'other': '0\u179F\u17C2\u1793'
    +    },
    +    '1000000': {
    +      'other': '0\u179B\u17B6\u1793'
    +    },
    +    '10000000': {
    +      'other': '0\u200B\u178A\u1794\u17CB\u200B\u179B\u17B6\u1793'
    +    },
    +    '100000000': {
    +      'other': '0\u200B\u179A\u1799\u179B\u17B6\u1793'
    +    },
    +    '1000000000': {
    +      'other': '0\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '10000000000': {
    +      'other': '0\u200B\u178A\u1794\u17CB\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '100000000000': {
    +      'other': '0\u200B\u179A\u1799\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '1000000000000': {
    +      'other': '0\u200B\u1796\u17B6\u1793\u17CB\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '10000000000000': {
    +      'other': '0\u200B\u1798\u17BA\u17BB\u1793\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '100000000000000': {
    +      'other': '0\u200B\u179F\u17C2\u1793\u200B\u1780\u17C4\u178A\u17B7'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u1796\u17B6\u1793\u17CB'
    +    },
    +    '10000': {
    +      'other': '0\u200B\u1798\u17BA\u17BB\u1793'
    +    },
    +    '100000': {
    +      'other': '0\u179F\u17C2\u1793'
    +    },
    +    '1000000': {
    +      'other': '0\u179B\u17B6\u1793'
    +    },
    +    '10000000': {
    +      'other': '0\u200B\u178A\u1794\u17CB\u200B\u179B\u17B6\u1793'
    +    },
    +    '100000000': {
    +      'other': '0\u200B\u179A\u1799\u179B\u17B6\u1793'
    +    },
    +    '1000000000': {
    +      'other': '0\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '10000000000': {
    +      'other': '0\u200B\u178A\u1794\u17CB\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '100000000000': {
    +      'other': '0\u200B\u179A\u1799\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '1000000000000': {
    +      'other': '0\u200B\u1796\u17B6\u1793\u17CB\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '10000000000000': {
    +      'other': '0\u200B\u1798\u17BA\u17BB\u1793\u200B\u1780\u17C4\u178A\u17B7'
    +    },
    +    '100000000000000': {
    +      'other': '0\u200B\u179F\u17C2\u1793\u200B\u1780\u17C4\u178A\u17B7'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale km_KH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_km_KH =
    +    goog.i18n.CompactNumberFormatSymbols_km;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0CB8\u0CBE'
    +    },
    +    '10000': {
    +      'other': '00\u0CB8\u0CBE'
    +    },
    +    '100000': {
    +      'other': '000\u0CB8\u0CBE'
    +    },
    +    '1000000': {
    +      'other': '0\u0CAE\u0CBF'
    +    },
    +    '10000000': {
    +      'other': '00\u0CAE\u0CBF'
    +    },
    +    '100000000': {
    +      'other': '000\u0CAE\u0CBF'
    +    },
    +    '1000000000': {
    +      'other': '0\u0CAC\u0CBF'
    +    },
    +    '10000000000': {
    +      'other': '00\u0CAC\u0CBF'
    +    },
    +    '100000000000': {
    +      'other': '000\u0CAC\u0CBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0C9F\u0CCD\u0CB0\u0CBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0C9F\u0CCD\u0CB0\u0CBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0C9F\u0CCD\u0CB0\u0CBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0CB8\u0CBE\u0CB5\u0CBF\u0CB0'
    +    },
    +    '10000': {
    +      'other': '00 \u0CB8\u0CBE\u0CB5\u0CBF\u0CB0'
    +    },
    +    '100000': {
    +      'other': '000 \u0CB8\u0CBE\u0CB5\u0CBF\u0CB0'
    +    },
    +    '1000000': {
    +      'other': '0 \u0CAE\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '10000000': {
    +      'other': '00 \u0CAE\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '100000000': {
    +      'other': '000 \u0CAE\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0CAC\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0CAC\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0CAC\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0C9F\u0CCD\u0CB0\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD\u200C'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0C9F\u0CCD\u0CB0\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD\u200C'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0C9F\u0CCD\u0CB0\u0CBF\u0CB2\u0CBF\u0CAF\u0CA8\u0CCD\u200C'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kn_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kn_IN =
    +    goog.i18n.CompactNumberFormatSymbols_kn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ko.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ko = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '0\uB9CC'
    +    },
    +    '100000': {
    +      'other': '00\uB9CC'
    +    },
    +    '1000000': {
    +      'other': '000\uB9CC'
    +    },
    +    '10000000': {
    +      'other': '0000\uB9CC'
    +    },
    +    '100000000': {
    +      'other': '0\uC5B5'
    +    },
    +    '1000000000': {
    +      'other': '00\uC5B5'
    +    },
    +    '10000000000': {
    +      'other': '000\uC5B5'
    +    },
    +    '100000000000': {
    +      'other': '0000\uC5B5'
    +    },
    +    '1000000000000': {
    +      'other': '0\uC870'
    +    },
    +    '10000000000000': {
    +      'other': '00\uC870'
    +    },
    +    '100000000000000': {
    +      'other': '000\uC870'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '0\uB9CC'
    +    },
    +    '100000': {
    +      'other': '00\uB9CC'
    +    },
    +    '1000000': {
    +      'other': '000\uB9CC'
    +    },
    +    '10000000': {
    +      'other': '0000\uB9CC'
    +    },
    +    '100000000': {
    +      'other': '0\uC5B5'
    +    },
    +    '1000000000': {
    +      'other': '00\uC5B5'
    +    },
    +    '10000000000': {
    +      'other': '000\uC5B5'
    +    },
    +    '100000000000': {
    +      'other': '0000\uC5B5'
    +    },
    +    '1000000000000': {
    +      'other': '0\uC870'
    +    },
    +    '10000000000000': {
    +      'other': '00\uC870'
    +    },
    +    '100000000000000': {
    +      'other': '000\uC870'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ko_KR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ko_KR =
    +    goog.i18n.CompactNumberFormatSymbols_ko;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ky.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ky = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u043C\u0438\u04CA'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u043C\u0438\u04CA'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u043C\u0438\u04CA'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u0438\u04CA'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u0438\u04CA'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u0438\u04CA'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ky_Cyrl_KG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ky_Cyrl_KG =
    +    goog.i18n.CompactNumberFormatSymbols_ky;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ln.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ln = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ln_CD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ln_CD =
    +    goog.i18n.CompactNumberFormatSymbols_ln;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0E9E\u0EB1\u0E99'
    +    },
    +    '10000': {
    +      'other': '00\u0E9E\u0EB1\u0E99'
    +    },
    +    '100000': {
    +      'other': '000\u0E9E\u0EB1\u0E99'
    +    },
    +    '1000000': {
    +      'other': '0\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '10000000': {
    +      'other': '00\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '100000000': {
    +      'other': '000\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '1000000000': {
    +      'other': '0\u0E95\u0EB7\u0EC9'
    +    },
    +    '10000000000': {
    +      'other': '00\u0E95\u0EB7\u0EC9'
    +    },
    +    '100000000000': {
    +      'other': '000\u0E95\u0EB7\u0EC9'
    +    },
    +    '1000000000000': {
    +      'other': '0000\u0E95\u0EB7\u0EC9'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0E9E\u0EB1\u0E99\u0E95\u0EB7\u0EC9'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0E9E\u0EB1\u0E99\u0E95\u0EB7\u0EC9'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u0E9E\u0EB1\u0E99'
    +    },
    +    '10000': {
    +      'other': '00\u0E9E\u0EB1\u0E99'
    +    },
    +    '100000': {
    +      'other': '000\u0E9E\u0EB1\u0E99'
    +    },
    +    '1000000': {
    +      'other': '0\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '10000000': {
    +      'other': '00\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '100000000': {
    +      'other': '000\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '1000000000': {
    +      'other': '0\u0E9E\u0EB1\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '10000000000': {
    +      'other': '00\u0E9E\u0EB1\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '100000000000': {
    +      'other': '000\u0E9E\u0EB1\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '1000000000000': {
    +      'other': '0000\u0E9E\u0EB1\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0EA5\u0EC9\u0EB2\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0EA5\u0EC9\u0EB2\u0E99\u0EA5\u0EC9\u0EB2\u0E99'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lo_LA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lo_LA =
    +    goog.i18n.CompactNumberFormatSymbols_lo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lt.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lt = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0t\u016Bkst.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0t\u016Bkst.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0t\u016Bkst.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0trln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0trln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0trln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 t\u016Bkstan\u010Di\u0173'
    +    },
    +    '10000': {
    +      'other': '00 t\u016Bkstan\u010Di\u0173'
    +    },
    +    '100000': {
    +      'other': '000 t\u016Bkstan\u010Di\u0173'
    +    },
    +    '1000000': {
    +      'other': '0 milijon\u0173'
    +    },
    +    '10000000': {
    +      'other': '00 milijon\u0173'
    +    },
    +    '100000000': {
    +      'other': '000 milijon\u0173'
    +    },
    +    '1000000000': {
    +      'other': '0 milijard\u0173'
    +    },
    +    '10000000000': {
    +      'other': '00 milijard\u0173'
    +    },
    +    '100000000000': {
    +      'other': '000 milijard\u0173'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilijon\u0173'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilijon\u0173'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilijon\u0173'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lt_LT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lt_LT =
    +    goog.i18n.CompactNumberFormatSymbols_lt;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lv.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lv = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0t\u016Bkst.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0t\u016Bkst.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0t\u016Bkst.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0milj.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0milj.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0milj.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mljrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mljrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mljrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0trilj.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0trilj.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0trilj.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 t\u016Bksto\u0161i'
    +    },
    +    '10000': {
    +      'other': '00 t\u016Bksto\u0161i'
    +    },
    +    '100000': {
    +      'other': '000 t\u016Bksto\u0161i'
    +    },
    +    '1000000': {
    +      'other': '0 miljoni'
    +    },
    +    '10000000': {
    +      'other': '00 miljoni'
    +    },
    +    '100000000': {
    +      'other': '000 miljoni'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardi'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardi'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 triljoni'
    +    },
    +    '10000000000000': {
    +      'other': '00 triljoni'
    +    },
    +    '100000000000000': {
    +      'other': '000 triljoni'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lv_LV.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lv_LV =
    +    goog.i18n.CompactNumberFormatSymbols_lv;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mk.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mk = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0438\u043B\u0458.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0438\u043B\u0458.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0438\u043B\u0458.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u041C'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B\u0458.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B\u0458.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B\u0458.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u0438\u043B.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u0438\u043B.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u0438\u043B.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0438\u043B\u0458\u0430\u0434\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0438\u043B\u0458\u0430\u0434\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0438\u043B\u0458\u0430\u0434\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u043E\u043D\u0438'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u043E\u043D\u0438'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u043E\u043D\u0438'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0438'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0438'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0438'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mk_MK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mk_MK =
    +    goog.i18n.CompactNumberFormatSymbols_mk;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ml.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ml = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0D06\u0D2F\u0D3F\u0D30\u0D02'
    +    },
    +    '10000': {
    +      'other': '00 \u0D06\u0D2F\u0D3F\u0D30\u0D02'
    +    },
    +    '100000': {
    +      'other': '000 \u0D06\u0D2F\u0D3F\u0D30\u0D02'
    +    },
    +    '1000000': {
    +      'other': '0 \u0D26\u0D36\u0D32\u0D15\u0D4D\u0D37\u0D02'
    +    },
    +    '10000000': {
    +      'other': '00 \u0D26\u0D36\u0D32\u0D15\u0D4D\u0D37\u0D02'
    +    },
    +    '100000000': {
    +      'other': '000 \u0D26\u0D36\u0D32\u0D15\u0D4D\u0D37\u0D02'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0D32\u0D15\u0D4D\u0D37\u0D02 \u0D15\u0D4B\u0D1F\u0D3F'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0D32\u0D15\u0D4D\u0D37\u0D02 \u0D15\u0D4B\u0D1F\u0D3F'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0D32\u0D15\u0D4D\u0D37\u0D02 \u0D15\u0D4B\u0D1F\u0D3F'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0D1F\u0D4D\u0D30\u0D3F\u0D32\u0D4D\u0D2F\u0D7A'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0D1F\u0D4D\u0D30\u0D3F\u0D32\u0D4D\u0D2F\u0D7A'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0D1F\u0D4D\u0D30\u0D3F\u0D32\u0D4D\u0D2F\u0D7A'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ml_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ml_IN =
    +    goog.i18n.CompactNumberFormatSymbols_ml;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u041C'
    +    },
    +    '10000': {
    +      'other': '00\u041C'
    +    },
    +    '100000': {
    +      'other': '000\u041C'
    +    },
    +    '1000000': {
    +      'other': '0\u0421'
    +    },
    +    '10000000': {
    +      'other': '00\u0421'
    +    },
    +    '100000000': {
    +      'other': '000\u0421'
    +    },
    +    '1000000000': {
    +      'other': '0\u0422'
    +    },
    +    '10000000000': {
    +      'other': '00\u0422'
    +    },
    +    '100000000000': {
    +      'other': '000\u0422'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0418\u041D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0418\u041D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0418\u041D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '1000000': {
    +      'other': '0 \u0441\u0430\u044F'
    +    },
    +    '10000000': {
    +      'other': '00 \u0441\u0430\u044F'
    +    },
    +    '100000000': {
    +      'other': '000 \u0441\u0430\u044F'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mn_Cyrl_MN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mn_Cyrl_MN =
    +    goog.i18n.CompactNumberFormatSymbols_mn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0939'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0939'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0932\u093E\u0916'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0915\u094B\u091F\u0940'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0915\u094B\u091F\u0940'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0905\u092C\u094D\u091C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0905\u092C\u094D\u091C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0916\u0930\u094D\u0935'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0916\u0930\u094D\u0935'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u092A\u0926\u094D\u092E'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u092A\u0926\u094D\u092E'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0939\u091C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00 \u0939\u091C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '000 \u0939\u091C\u093E\u0930'
    +    },
    +    '1000000': {
    +      'other': '0 \u0926\u0936\u0932\u0915\u094D\u0937'
    +    },
    +    '10000000': {
    +      'other': '00 \u0926\u0936\u0932\u0915\u094D\u0937'
    +    },
    +    '100000000': {
    +      'other': '000 \u0926\u0936\u0932\u0915\u094D\u0937'
    +    },
    +    '1000000000': {
    +      'other': '0 \u092E\u0939\u093E\u092A\u0926\u094D\u092E'
    +    },
    +    '10000000000': {
    +      'other': '00 \u092E\u0939\u093E\u092A\u0926\u094D\u092E'
    +    },
    +    '100000000000': {
    +      'other': '000 \u092E\u0939\u093E\u092A\u0926\u094D\u092E'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0916\u0930\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0916\u0930\u092C'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0916\u0930\u092C'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mr_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mr_IN =
    +    goog.i18n.CompactNumberFormatSymbols_mr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ms.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ms = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0J'
    +    },
    +    '10000000': {
    +      'other': '00J'
    +    },
    +    '100000000': {
    +      'other': '000J'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000': {
    +      'other': '000 bilion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ms_Latn_MY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ms_Latn_MY =
    +    goog.i18n.CompactNumberFormatSymbols_ms;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mt.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mt = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mt_MT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mt_MT =
    +    goog.i18n.CompactNumberFormatSymbols_mt;
    +
    +
    +/**
    + * Compact number formatting symbols for locale my.
    + */
    +goog.i18n.CompactNumberFormatSymbols_my = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u1011\u1031\u102C\u1004\u103A'
    +    },
    +    '10000': {
    +      'other': '0\u101E\u1031\u102C\u1004\u103A\u1038'
    +    },
    +    '100000': {
    +      'other': '0\u101E\u102D\u1014\u103A\u1038'
    +    },
    +    '1000000': {
    +      'other': '0\u101E\u1014\u103A\u1038'
    +    },
    +    '10000000': {
    +      'other': '0\u1000\u102F\u100B\u1031'
    +    },
    +    '100000000': {
    +      'other': '00\u1000\u102F\u100B\u1031'
    +    },
    +    '1000000000': {
    +      'other': '\u1000\u102F\u100B\u1031000'
    +    },
    +    '10000000000': {
    +      'other': '\u1000\u102F\u100B\u10310000'
    +    },
    +    '100000000000': {
    +      'other': '0000\u1000\u102F\u100B\u1031'
    +    },
    +    '1000000000000': {
    +      'other': '\u1000\u102F\u100B\u10310\u101E\u102D\u1014\u103A\u1038'
    +    },
    +    '10000000000000': {
    +      'other': '\u1000\u102F\u100B\u10310\u101E\u1014\u103A\u1038'
    +    },
    +    '100000000000000': {
    +      'other': '0\u1000\u1031\u102C\u100B\u102D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u1011\u1031\u102C\u1004\u103A'
    +    },
    +    '10000': {
    +      'other': '0\u101E\u1031\u102C\u1004\u103A\u1038'
    +    },
    +    '100000': {
    +      'other': '0\u101E\u102D\u1014\u103A\u1038'
    +    },
    +    '1000000': {
    +      'other': '0\u101E\u1014\u103A\u1038'
    +    },
    +    '10000000': {
    +      'other': '0\u1000\u102F\u100B\u1031'
    +    },
    +    '100000000': {
    +      'other': '00\u1000\u102F\u100B\u1031'
    +    },
    +    '1000000000': {
    +      'other': '000\u1000\u102F\u100B\u1031'
    +    },
    +    '10000000000': {
    +      'other': '0000\u1000\u102F\u100B\u1031'
    +    },
    +    '100000000000': {
    +      'other': '00000\u1000\u102F\u100B\u1031'
    +    },
    +    '1000000000000': {
    +      'other': '000000\u1000\u102F\u100B\u1031'
    +    },
    +    '10000000000000': {
    +      'other': '0000000\u1000\u102F\u100B\u1031'
    +    },
    +    '100000000000000': {
    +      'other': '0\u1000\u1031\u102C\u100B\u102D'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale my_MM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_my_MM =
    +    goog.i18n.CompactNumberFormatSymbols_my;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0K'
    +    },
    +    '10000': {
    +      'other': '00\u00A0K'
    +    },
    +    '100000': {
    +      'other': '000\u00A0K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mill'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mill'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mill'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bill'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bill'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bill'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nb_NO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nb_NO =
    +    goog.i18n.CompactNumberFormatSymbols_nb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nb_SJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nb_SJ =
    +    goog.i18n.CompactNumberFormatSymbols_nb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ne.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ne = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0939\u091C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0939\u091C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0932\u093E\u0916'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0915\u0930\u094B\u0921'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0915\u0930\u094B\u0921'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0905\u0930\u092C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0905\u0930\u092C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0916\u0930\u092C'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0916\u0930\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0936\u0902\u0916'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u0936\u0902\u0916'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0939\u091C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00 \u0939\u091C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0 \u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '0 \u0915\u0930\u094B\u0921'
    +    },
    +    '10000000': {
    +      'other': '00 \u0915\u0930\u094B\u0921'
    +    },
    +    '100000000': {
    +      'other': '000 \u0915\u0930\u094B\u0921'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0905\u0930\u094D\u092C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0905\u0930\u094D\u092C'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0905\u0930\u092C'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0916\u0930\u094D\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '0 \u0936\u0902\u0916'
    +    },
    +    '100000000000000': {
    +      'other': '00 \u0936\u0902\u0916'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ne_NP.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ne_NP =
    +    goog.i18n.CompactNumberFormatSymbols_ne;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_NL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_NL =
    +    goog.i18n.CompactNumberFormatSymbols_nl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale no.
    + */
    +goog.i18n.CompactNumberFormatSymbols_no = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0K'
    +    },
    +    '10000': {
    +      'other': '00\u00A0K'
    +    },
    +    '100000': {
    +      'other': '000\u00A0K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mill'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mill'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mill'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bill'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bill'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bill'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale no_NO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_no_NO =
    +    goog.i18n.CompactNumberFormatSymbols_no;
    +
    +
    +/**
    + * Compact number formatting symbols for locale or.
    + */
    +goog.i18n.CompactNumberFormatSymbols_or = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale or_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_or_IN =
    +    goog.i18n.CompactNumberFormatSymbols_or;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pa.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pa = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0A32\u0A71\u0A16'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0A32\u0A71\u0A16'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0A05\u0A30\u0A2C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0A05\u0A30\u0A2C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0A16\u0A30\u0A2C'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0A16\u0A30\u0A2C'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0A28\u0A40\u0A32'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u0A28\u0A40\u0A32'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '10000': {
    +      'other': '00 \u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '100000': {
    +      'other': '0 \u0A32\u0A71\u0A16'
    +    },
    +    '1000000': {
    +      'other': '00 \u0A32\u0A71\u0A16'
    +    },
    +    '10000000': {
    +      'other': '0 \u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '100000000': {
    +      'other': '00 \u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0A05\u0A30\u0A2C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0A05\u0A30\u0A2C'
    +    },
    +    '100000000000': {
    +      'other': '0 \u0A16\u0A30\u0A2C'
    +    },
    +    '1000000000000': {
    +      'other': '00 \u0A16\u0A30\u0A2C'
    +    },
    +    '10000000000000': {
    +      'other': '0 \u0A28\u0A40\u0A32'
    +    },
    +    '100000000000000': {
    +      'other': '00 \u0A28\u0A40\u0A32'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pa_Guru_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pa_Guru_IN =
    +    goog.i18n.CompactNumberFormatSymbols_pa;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tys.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tys.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tys.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tysi\u0105ca'
    +    },
    +    '10000': {
    +      'other': '00 tysi\u0105ca'
    +    },
    +    '100000': {
    +      'other': '000 tysi\u0105ca'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 miliarda'
    +    },
    +    '10000000000': {
    +      'other': '00 miliarda'
    +    },
    +    '100000000000': {
    +      'other': '000 miliarda'
    +    },
    +    '1000000000000': {
    +      'other': '0 biliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 biliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 biliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pl_PL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pl_PL =
    +    goog.i18n.CompactNumberFormatSymbols_pl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mi'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mi'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mi'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0bi'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0bi'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0bi'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0tri'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0tri'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0tri'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 bilh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 bilh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 bilh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilh\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilh\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilh\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_BR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_BR =
    +    goog.i18n.CompactNumberFormatSymbols_pt;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_PT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_PT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ro.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ro = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0K'
    +    },
    +    '10000': {
    +      'other': '00\u00A0K'
    +    },
    +    '100000': {
    +      'other': '000\u00A0K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0tril.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0tril.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0tril.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 de mii'
    +    },
    +    '10000': {
    +      'other': '00 de mii'
    +    },
    +    '100000': {
    +      'other': '000 de mii'
    +    },
    +    '1000000': {
    +      'other': '0 de milioane'
    +    },
    +    '10000000': {
    +      'other': '00 de milioane'
    +    },
    +    '100000000': {
    +      'other': '000 de milioane'
    +    },
    +    '1000000000': {
    +      'other': '0 de miliarde'
    +    },
    +    '10000000000': {
    +      'other': '00 de miliarde'
    +    },
    +    '100000000000': {
    +      'other': '000 de miliarde'
    +    },
    +    '1000000000000': {
    +      'other': '0 de trilioane'
    +    },
    +    '10000000000000': {
    +      'other': '00 de trilioane'
    +    },
    +    '100000000000000': {
    +      'other': '000 de trilioane'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ro_RO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ro_RO =
    +    goog.i18n.CompactNumberFormatSymbols_ro;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_RU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_RU =
    +    goog.i18n.CompactNumberFormatSymbols_ru;
    +
    +
    +/**
    + * Compact number formatting symbols for locale si.
    + */
    +goog.i18n.CompactNumberFormatSymbols_si = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '\u0DAF0'
    +    },
    +    '10000': {
    +      'other': '\u0DAF00'
    +    },
    +    '100000': {
    +      'other': '\u0DAF000'
    +    },
    +    '1000000': {
    +      'other': '\u0DB8\u0DD20'
    +    },
    +    '10000000': {
    +      'other': '\u0DB8\u0DD200'
    +    },
    +    '100000000': {
    +      'other': '\u0DB8\u0DD2000'
    +    },
    +    '1000000000': {
    +      'other': '\u0DB6\u0DD20'
    +    },
    +    '10000000000': {
    +      'other': '\u0DB6\u0DD200'
    +    },
    +    '100000000000': {
    +      'other': '\u0DB6\u0DD2000'
    +    },
    +    '1000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD20'
    +    },
    +    '10000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD200'
    +    },
    +    '100000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD2000'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '\u0DAF\u0DC4\u0DC3 0'
    +    },
    +    '10000': {
    +      'other': '\u0DAF\u0DC4\u0DC3 00'
    +    },
    +    '100000': {
    +      'other': '\u0DAF\u0DC4\u0DC3 000'
    +    },
    +    '1000000': {
    +      'other': '\u0DB8\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 0'
    +    },
    +    '10000000': {
    +      'other': '\u0DB8\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 00'
    +    },
    +    '100000000': {
    +      'other': '\u0DB8\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 000'
    +    },
    +    '1000000000': {
    +      'other': '\u0DB6\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 0'
    +    },
    +    '10000000000': {
    +      'other': '\u0DB6\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 00'
    +    },
    +    '100000000000': {
    +      'other': '\u0DB6\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 000'
    +    },
    +    '1000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 0'
    +    },
    +    '10000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 00'
    +    },
    +    '100000000000000': {
    +      'other': '\u0DA7\u0DCA\u200D\u0DBB\u0DD2\u0DBD\u0DD2\u0DBA\u0DB1 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale si_LK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_si_LK =
    +    goog.i18n.CompactNumberFormatSymbols_si;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sk.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sk = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tis.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tis.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tis.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tis\u00EDc'
    +    },
    +    '10000': {
    +      'other': '00 tis\u00EDc'
    +    },
    +    '100000': {
    +      'other': '000 tis\u00EDc'
    +    },
    +    '1000000': {
    +      'other': '0 mili\u00F3nov'
    +    },
    +    '10000000': {
    +      'other': '00 mili\u00F3nov'
    +    },
    +    '100000000': {
    +      'other': '000 mili\u00F3nov'
    +    },
    +    '1000000000': {
    +      'other': '0 miliard'
    +    },
    +    '10000000000': {
    +      'other': '00 mili\u00E1rd'
    +    },
    +    '100000000000': {
    +      'other': '000 mili\u00E1rd'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F3nov'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F3nov'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F3nov'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sk_SK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sk_SK =
    +    goog.i18n.CompactNumberFormatSymbols_sk;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tis.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tis.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tis.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mio.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mio.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mio.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tiso\u010D'
    +    },
    +    '10000': {
    +      'other': '00 tiso\u010D'
    +    },
    +    '100000': {
    +      'other': '000 tiso\u010D'
    +    },
    +    '1000000': {
    +      'other': '0 milijonov'
    +    },
    +    '10000000': {
    +      'other': '00 milijonov'
    +    },
    +    '100000000': {
    +      'other': '000 milijonov'
    +    },
    +    '1000000000': {
    +      'other': '0 milijard'
    +    },
    +    '10000000000': {
    +      'other': '00 milijard'
    +    },
    +    '100000000000': {
    +      'other': '000 milijard'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilijonov'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilijonov'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilijonov'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sl_SI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sl_SI =
    +    goog.i18n.CompactNumberFormatSymbols_sl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00 mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000 mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0 milion'
    +    },
    +    '10000000': {
    +      'other': '00 milion'
    +    },
    +    '100000000': {
    +      'other': '000 milion'
    +    },
    +    '1000000000': {
    +      'other': '0 miliard'
    +    },
    +    '10000000000': {
    +      'other': '00 miliard'
    +    },
    +    '100000000000': {
    +      'other': '000 miliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sq_AL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sq_AL =
    +    goog.i18n.CompactNumberFormatSymbols_sq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0445\u0438\u0459.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u0459.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u0459.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0431\u0438\u043B.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0431\u0438\u043B.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0431\u0438\u043B.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Cyrl_RS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_RS =
    +    goog.i18n.CompactNumberFormatSymbols_sr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sv.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sv = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tn'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tn'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tn'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 miljoner'
    +    },
    +    '10000000': {
    +      'other': '00 miljoner'
    +    },
    +    '100000000': {
    +      'other': '000 miljoner'
    +    },
    +    '1000000000': {
    +      'other': '0 miljarder'
    +    },
    +    '10000000000': {
    +      'other': '00 miljarder'
    +    },
    +    '100000000000': {
    +      'other': '000 miljarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoner'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoner'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sv_SE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sv_SE =
    +    goog.i18n.CompactNumberFormatSymbols_sv;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': 'elfu\u00A00'
    +    },
    +    '10000': {
    +      'other': 'elfu\u00A000'
    +    },
    +    '100000': {
    +      'other': 'elfu\u00A0000'
    +    },
    +    '1000000': {
    +      'other': 'M0'
    +    },
    +    '10000000': {
    +      'other': 'M00'
    +    },
    +    '100000000': {
    +      'other': 'M000'
    +    },
    +    '1000000000': {
    +      'other': 'B0'
    +    },
    +    '10000000000': {
    +      'other': 'B00'
    +    },
    +    '100000000000': {
    +      'other': 'B000'
    +    },
    +    '1000000000000': {
    +      'other': 'T0'
    +    },
    +    '10000000000000': {
    +      'other': 'T00'
    +    },
    +    '100000000000000': {
    +      'other': 'T000'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': 'Elfu 0'
    +    },
    +    '10000': {
    +      'other': 'Elfu 00'
    +    },
    +    '100000': {
    +      'other': 'Elfu 000'
    +    },
    +    '1000000': {
    +      'other': 'Milioni 0'
    +    },
    +    '10000000': {
    +      'other': 'Milioni 00'
    +    },
    +    '100000000': {
    +      'other': 'Milioni 000'
    +    },
    +    '1000000000': {
    +      'other': 'Bilioni 0'
    +    },
    +    '10000000000': {
    +      'other': 'Bilioni 00'
    +    },
    +    '100000000000': {
    +      'other': 'Bilioni 000'
    +    },
    +    '1000000000000': {
    +      'other': 'Trilioni 0'
    +    },
    +    '10000000000000': {
    +      'other': 'Trilioni 00'
    +    },
    +    '100000000000000': {
    +      'other': 'Trilioni 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sw_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sw_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_sw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ta.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ta = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0B86'
    +    },
    +    '10000': {
    +      'other': '00\u0B86'
    +    },
    +    '100000': {
    +      'other': '000\u0B86'
    +    },
    +    '1000000': {
    +      'other': '0\u0BAE\u0BBF'
    +    },
    +    '10000000': {
    +      'other': '00\u0BAE\u0BBF'
    +    },
    +    '100000000': {
    +      'other': '000\u0BAE\u0BBF'
    +    },
    +    '1000000000': {
    +      'other': '0\u0BAA\u0BBF'
    +    },
    +    '10000000000': {
    +      'other': '00\u0BAA\u0BBF'
    +    },
    +    '100000000000': {
    +      'other': '000\u0BAA\u0BBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0B9F\u0BBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0B9F\u0BBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0B9F\u0BBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '10000': {
    +      'other': '00 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '100000': {
    +      'other': '000 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000': {
    +      'other': '00 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000': {
    +      'other': '000 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ta_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ta_IN =
    +    goog.i18n.CompactNumberFormatSymbols_ta;
    +
    +
    +/**
    + * Compact number formatting symbols for locale te.
    + */
    +goog.i18n.CompactNumberFormatSymbols_te = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0C35\u0C47'
    +    },
    +    '10000': {
    +      'other': '00\u0C35\u0C47'
    +    },
    +    '100000': {
    +      'other': '000\u0C35\u0C47'
    +    },
    +    '1000000': {
    +      'other': '0\u0C2E\u0C3F'
    +    },
    +    '10000000': {
    +      'other': '00\u0C2E\u0C3F'
    +    },
    +    '100000000': {
    +      'other': '000\u0C2E\u0C3F'
    +    },
    +    '1000000000': {
    +      'other': '0\u0C2C\u0C3F'
    +    },
    +    '10000000000': {
    +      'other': '00\u0C2C\u0C3F'
    +    },
    +    '100000000000': {
    +      'other': '000\u0C2C\u0C3F'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0C1F\u0C4D\u0C30\u0C3F'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0C1F\u0C4D\u0C30\u0C3F'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0C1F\u0C4D\u0C30\u0C3F'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0C35\u0C47\u0C32\u0C41'
    +    },
    +    '10000': {
    +      'other': '00 \u0C35\u0C47\u0C32\u0C41'
    +    },
    +    '100000': {
    +      'other': '000 \u0C35\u0C47\u0C32\u0C41'
    +    },
    +    '1000000': {
    +      'other': '0 \u0C2E\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '10000000': {
    +      'other': '00 \u0C2E\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '100000000': {
    +      'other': '000 \u0C2E\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0C2C\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0C2C\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0C2C\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0C1F\u0C4D\u0C30\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0C1F\u0C4D\u0C30\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0C1F\u0C4D\u0C30\u0C3F\u0C32\u0C3F\u0C2F\u0C28\u0C4D\u0C32\u0C41'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale te_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_te_IN =
    +    goog.i18n.CompactNumberFormatSymbols_te;
    +
    +
    +/**
    + * Compact number formatting symbols for locale th.
    + */
    +goog.i18n.CompactNumberFormatSymbols_th = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0E1E.'
    +    },
    +    '10000': {
    +      'other': '0\u00A0\u0E21.'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0E2A.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0E25.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0E25.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0E25.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0E1E.\u0E25.'
    +    },
    +    '10000000000': {
    +      'other': '0\u00A0\u0E21.\u0E25.'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0E2A.\u0E25.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0E25.\u0E25.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0E25.\u0E25.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0E25.\u0E25.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0E1E\u0E31\u0E19'
    +    },
    +    '10000': {
    +      'other': '0 \u0E2B\u0E21\u0E37\u0E48\u0E19'
    +    },
    +    '100000': {
    +      'other': '0 \u0E41\u0E2A\u0E19'
    +    },
    +    '1000000': {
    +      'other': '0 \u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '10000000': {
    +      'other': '00 \u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '100000000': {
    +      'other': '000 \u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0E1E\u0E31\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '10000000000': {
    +      'other': '0 \u0E2B\u0E21\u0E37\u0E48\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '100000000000': {
    +      'other': '0 \u0E41\u0E2A\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0E25\u0E49\u0E32\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0E25\u0E49\u0E32\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0E25\u0E49\u0E32\u0E19\u0E25\u0E49\u0E32\u0E19'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale th_TH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_th_TH =
    +    goog.i18n.CompactNumberFormatSymbols_th;
    +
    +
    +/**
    + * Compact number formatting symbols for locale tl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 libo'
    +    },
    +    '10000': {
    +      'other': '00 libo'
    +    },
    +    '100000': {
    +      'other': '000 libo'
    +    },
    +    '1000000': {
    +      'other': '0 milyon'
    +    },
    +    '10000000': {
    +      'other': '00 milyon'
    +    },
    +    '100000000': {
    +      'other': '000 milyon'
    +    },
    +    '1000000000': {
    +      'other': '0 bilyon'
    +    },
    +    '10000000000': {
    +      'other': '00 bilyon'
    +    },
    +    '100000000000': {
    +      'other': '000 bilyon'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilyon'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilyon'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilyon'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000': {
    +      'other': '000\u00A0B'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mr'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mr'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mr'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Tn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Tn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Tn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 bin'
    +    },
    +    '10000': {
    +      'other': '00 bin'
    +    },
    +    '100000': {
    +      'other': '000 bin'
    +    },
    +    '1000000': {
    +      'other': '0 milyon'
    +    },
    +    '10000000': {
    +      'other': '00 milyon'
    +    },
    +    '100000000': {
    +      'other': '000 milyon'
    +    },
    +    '1000000000': {
    +      'other': '0 milyar'
    +    },
    +    '10000000000': {
    +      'other': '00 milyar'
    +    },
    +    '100000000000': {
    +      'other': '000 milyar'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilyon'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilyon'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilyon'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tr_TR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tr_TR =
    +    goog.i18n.CompactNumberFormatSymbols_tr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale uk.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uk = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u0438\u0441'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u0438\u0441'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u0438\u0441'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u0438\u0441\u044F\u0447\u0456'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u0438\u0441\u044F\u0447\u0456'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u0438\u0441\u044F\u0447\u0456'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0456\u043B\u044C\u0439\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0456\u043B\u044C\u0439\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0456\u043B\u044C\u0439\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0456\u043B\u044C\u044F\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0456\u043B\u044C\u044F\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0456\u043B\u044C\u044F\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u044C\u0439\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u044C\u0439\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u044C\u0439\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uk_UA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uk_UA =
    +    goog.i18n.CompactNumberFormatSymbols_uk;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ur.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ur = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u06C1\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u06C1\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0644\u0627\u06A9\u06BE'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0644\u0627\u06A9\u06BE'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u06A9\u0631\u0648\u0691'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u06A9\u0631\u0648\u0691'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0627\u0631\u0628'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0627\u0631\u0628'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u06A9\u06BE\u0631\u0628'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u06A9\u06BE\u0631\u0628'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u06C1\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00 \u06C1\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '0 \u0644\u0627\u06A9\u06BE'
    +    },
    +    '1000000': {
    +      'other': '00 \u0644\u0627\u06A9\u06BE'
    +    },
    +    '10000000': {
    +      'other': '0 \u06A9\u0631\u0648\u0691'
    +    },
    +    '100000000': {
    +      'other': '00 \u06A9\u0631\u0648\u0691'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0627\u0631\u0628'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0627\u0631\u0628'
    +    },
    +    '100000000000': {
    +      'other': '0 \u06A9\u06BE\u0631\u0628'
    +    },
    +    '1000000000000': {
    +      'other': '00 \u06A9\u06BE\u0631\u0628'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ur_PK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ur_PK =
    +    goog.i18n.CompactNumberFormatSymbols_ur;
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0ming'
    +    },
    +    '10000': {
    +      'other': '00ming'
    +    },
    +    '100000': {
    +      'other': '000ming'
    +    },
    +    '1000000': {
    +      'other': '0mln'
    +    },
    +    '10000000': {
    +      'other': '00mln'
    +    },
    +    '100000000': {
    +      'other': '000mln'
    +    },
    +    '1000000000': {
    +      'other': '0mlrd'
    +    },
    +    '10000000000': {
    +      'other': '00mlrd'
    +    },
    +    '100000000000': {
    +      'other': '000mlrd'
    +    },
    +    '1000000000000': {
    +      'other': '0trln'
    +    },
    +    '10000000000000': {
    +      'other': '00trln'
    +    },
    +    '100000000000000': {
    +      'other': '000trln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ming'
    +    },
    +    '10000': {
    +      'other': '00 ming'
    +    },
    +    '100000': {
    +      'other': '000 ming'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 milliard'
    +    },
    +    '10000000000': {
    +      'other': '00 milliard'
    +    },
    +    '100000000000': {
    +      'other': '000 milliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Latn_UZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Latn_UZ =
    +    goog.i18n.CompactNumberFormatSymbols_uz;
    +
    +
    +/**
    + * Compact number formatting symbols for locale vi.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vi = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0N'
    +    },
    +    '10000': {
    +      'other': '00\u00A0N'
    +    },
    +    '100000': {
    +      'other': '000\u00A0N'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Tr'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Tr'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Tr'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0T'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0T'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0T'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0NT'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0NT'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0NT'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ngh\u00ECn'
    +    },
    +    '10000': {
    +      'other': '00 ngh\u00ECn'
    +    },
    +    '100000': {
    +      'other': '000 ngh\u00ECn'
    +    },
    +    '1000000': {
    +      'other': '0 tri\u1EC7u'
    +    },
    +    '10000000': {
    +      'other': '00 tri\u1EC7u'
    +    },
    +    '100000000': {
    +      'other': '000 tri\u1EC7u'
    +    },
    +    '1000000000': {
    +      'other': '0 t\u1EF7'
    +    },
    +    '10000000000': {
    +      'other': '00 t\u1EF7'
    +    },
    +    '100000000000': {
    +      'other': '000 t\u1EF7'
    +    },
    +    '1000000000000': {
    +      'other': '0 ngh\u00ECn t\u1EF7'
    +    },
    +    '10000000000000': {
    +      'other': '00 ngh\u00ECn t\u1EF7'
    +    },
    +    '100000000000000': {
    +      'other': '000 ngh\u00ECn t\u1EF7'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vi_VN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vi_VN =
    +    goog.i18n.CompactNumberFormatSymbols_vi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_CN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_CN =
    +    goog.i18n.CompactNumberFormatSymbols_zh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_HK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_HK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u842C'
    +    },
    +    '100000': {
    +      'other': '00\u842C'
    +    },
    +    '1000000': {
    +      'other': '000\u842C'
    +    },
    +    '10000000': {
    +      'other': '0000\u842C'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hans_CN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hans_CN =
    +    goog.i18n.CompactNumberFormatSymbols_zh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_TW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_TW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u5343'
    +    },
    +    '10000': {
    +      'other': '0\u842C'
    +    },
    +    '100000': {
    +      'other': '00\u842C'
    +    },
    +    '1000000': {
    +      'other': '000\u842C'
    +    },
    +    '10000000': {
    +      'other': '0000\u842C'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 inkulungwane'
    +    },
    +    '10000': {
    +      'other': '00 inkulungwane'
    +    },
    +    '100000': {
    +      'other': '000 inkulungwane'
    +    },
    +    '1000000': {
    +      'other': '0 isigidi'
    +    },
    +    '10000000': {
    +      'other': '00 isigidi'
    +    },
    +    '100000000': {
    +      'other': '000 isigidi'
    +    },
    +    '1000000000': {
    +      'other': '0 isigidi sezigidi'
    +    },
    +    '10000000000': {
    +      'other': '00 isigidi sezigidi'
    +    },
    +    '100000000000': {
    +      'other': '000 isigidi sezigidi'
    +    },
    +    '1000000000000': {
    +      'other': '0 isigidintathu'
    +    },
    +    '10000000000000': {
    +      'other': '00 isigidintathu'
    +    },
    +    '100000000000000': {
    +      'other': '000 isigidintathu'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zu_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zu_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_zu;
    +
    +
    +/**
    + * Selected compact number formatting symbols by locale.
    + */
    +goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_af;
    +}
    +
    +if (goog.LOCALE == 'af_ZA' || goog.LOCALE == 'af-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_af;
    +}
    +
    +if (goog.LOCALE == 'am') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_am;
    +}
    +
    +if (goog.LOCALE == 'am_ET' || goog.LOCALE == 'am-ET') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_am;
    +}
    +
    +if (goog.LOCALE == 'ar') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar;
    +}
    +
    +if (goog.LOCALE == 'ar_001' || goog.LOCALE == 'ar-001') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar;
    +}
    +
    +if (goog.LOCALE == 'az') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_az;
    +}
    +
    +if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_az;
    +}
    +
    +if (goog.LOCALE == 'bg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bg;
    +}
    +
    +if (goog.LOCALE == 'bg_BG' || goog.LOCALE == 'bg-BG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bg;
    +}
    +
    +if (goog.LOCALE == 'bn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bn;
    +}
    +
    +if (goog.LOCALE == 'bn_BD' || goog.LOCALE == 'bn-BD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bn;
    +}
    +
    +if (goog.LOCALE == 'br') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_br;
    +}
    +
    +if (goog.LOCALE == 'br_FR' || goog.LOCALE == 'br-FR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_br;
    +}
    +
    +if (goog.LOCALE == 'ca') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_AD' || goog.LOCALE == 'ca-AD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_ES' || goog.LOCALE == 'ca-ES') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_ES_VALENCIA' || goog.LOCALE == 'ca-ES-VALENCIA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_FR' || goog.LOCALE == 'ca-FR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_IT' || goog.LOCALE == 'ca-IT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'chr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_chr;
    +}
    +
    +if (goog.LOCALE == 'chr_US' || goog.LOCALE == 'chr-US') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_chr;
    +}
    +
    +if (goog.LOCALE == 'cs') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cs;
    +}
    +
    +if (goog.LOCALE == 'cs_CZ' || goog.LOCALE == 'cs-CZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cs;
    +}
    +
    +if (goog.LOCALE == 'cy') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cy;
    +}
    +
    +if (goog.LOCALE == 'cy_GB' || goog.LOCALE == 'cy-GB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cy;
    +}
    +
    +if (goog.LOCALE == 'da') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'da_DK' || goog.LOCALE == 'da-DK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'da_GL' || goog.LOCALE == 'da-GL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'de') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de_AT;
    +}
    +
    +if (goog.LOCALE == 'de_BE' || goog.LOCALE == 'de-BE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de_CH;
    +}
    +
    +if (goog.LOCALE == 'de_DE' || goog.LOCALE == 'de-DE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_LU' || goog.LOCALE == 'de-LU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'el') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_el;
    +}
    +
    +if (goog.LOCALE == 'el_GR' || goog.LOCALE == 'el-GR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_el;
    +}
    +
    +if (goog.LOCALE == 'en') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_001' || goog.LOCALE == 'en-001') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_AS' || goog.LOCALE == 'en-AS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_AU;
    +}
    +
    +if (goog.LOCALE == 'en_DG' || goog.LOCALE == 'en-DG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_FM' || goog.LOCALE == 'en-FM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GB;
    +}
    +
    +if (goog.LOCALE == 'en_GU' || goog.LOCALE == 'en-GU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_IE;
    +}
    +
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_IN;
    +}
    +
    +if (goog.LOCALE == 'en_IO' || goog.LOCALE == 'en-IO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_MH' || goog.LOCALE == 'en-MH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_MP' || goog.LOCALE == 'en-MP') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_PR' || goog.LOCALE == 'en-PR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_PW' || goog.LOCALE == 'en-PW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SG;
    +}
    +
    +if (goog.LOCALE == 'en_TC' || goog.LOCALE == 'en-TC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_UM' || goog.LOCALE == 'en-UM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_VG' || goog.LOCALE == 'en-VG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_VI' || goog.LOCALE == 'en-VI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_ZA;
    +}
    +
    +if (goog.LOCALE == 'en_ZW' || goog.LOCALE == 'en-ZW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'es') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_419;
    +}
    +
    +if (goog.LOCALE == 'es_EA' || goog.LOCALE == 'es-EA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_IC' || goog.LOCALE == 'es-IC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'et') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_et;
    +}
    +
    +if (goog.LOCALE == 'et_EE' || goog.LOCALE == 'et-EE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_et;
    +}
    +
    +if (goog.LOCALE == 'eu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_eu;
    +}
    +
    +if (goog.LOCALE == 'eu_ES' || goog.LOCALE == 'eu-ES') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_eu;
    +}
    +
    +if (goog.LOCALE == 'fa') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fa;
    +}
    +
    +if (goog.LOCALE == 'fa_IR' || goog.LOCALE == 'fa-IR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fa;
    +}
    +
    +if (goog.LOCALE == 'fi') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fi;
    +}
    +
    +if (goog.LOCALE == 'fi_FI' || goog.LOCALE == 'fi-FI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fi;
    +}
    +
    +if (goog.LOCALE == 'fil') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fil;
    +}
    +
    +if (goog.LOCALE == 'fil_PH' || goog.LOCALE == 'fil-PH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fil;
    +}
    +
    +if (goog.LOCALE == 'fr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_BL' || goog.LOCALE == 'fr-BL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CA;
    +}
    +
    +if (goog.LOCALE == 'fr_FR' || goog.LOCALE == 'fr-FR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_GF' || goog.LOCALE == 'fr-GF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_GP' || goog.LOCALE == 'fr-GP') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MC' || goog.LOCALE == 'fr-MC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MF' || goog.LOCALE == 'fr-MF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MQ' || goog.LOCALE == 'fr-MQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_PM' || goog.LOCALE == 'fr-PM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_RE' || goog.LOCALE == 'fr-RE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'ga') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ga;
    +}
    +
    +if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ga;
    +}
    +
    +if (goog.LOCALE == 'gl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gl;
    +}
    +
    +if (goog.LOCALE == 'gl_ES' || goog.LOCALE == 'gl-ES') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gl;
    +}
    +
    +if (goog.LOCALE == 'gsw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gsw_CH' || goog.LOCALE == 'gsw-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gsw_LI' || goog.LOCALE == 'gsw-LI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gu;
    +}
    +
    +if (goog.LOCALE == 'gu_IN' || goog.LOCALE == 'gu-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gu;
    +}
    +
    +if (goog.LOCALE == 'haw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_haw;
    +}
    +
    +if (goog.LOCALE == 'haw_US' || goog.LOCALE == 'haw-US') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_haw;
    +}
    +
    +if (goog.LOCALE == 'he') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_he;
    +}
    +
    +if (goog.LOCALE == 'he_IL' || goog.LOCALE == 'he-IL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_he;
    +}
    +
    +if (goog.LOCALE == 'hi') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hi;
    +}
    +
    +if (goog.LOCALE == 'hi_IN' || goog.LOCALE == 'hi-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hi;
    +}
    +
    +if (goog.LOCALE == 'hr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hr;
    +}
    +
    +if (goog.LOCALE == 'hr_HR' || goog.LOCALE == 'hr-HR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hr;
    +}
    +
    +if (goog.LOCALE == 'hu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hu;
    +}
    +
    +if (goog.LOCALE == 'hu_HU' || goog.LOCALE == 'hu-HU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hu;
    +}
    +
    +if (goog.LOCALE == 'hy') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hy;
    +}
    +
    +if (goog.LOCALE == 'hy_AM' || goog.LOCALE == 'hy-AM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hy;
    +}
    +
    +if (goog.LOCALE == 'id') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_id;
    +}
    +
    +if (goog.LOCALE == 'id_ID' || goog.LOCALE == 'id-ID') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_id;
    +}
    +
    +if (goog.LOCALE == 'in') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_in;
    +}
    +
    +if (goog.LOCALE == 'is') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_is;
    +}
    +
    +if (goog.LOCALE == 'is_IS' || goog.LOCALE == 'is-IS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_is;
    +}
    +
    +if (goog.LOCALE == 'it') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'it_IT' || goog.LOCALE == 'it-IT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'it_SM' || goog.LOCALE == 'it-SM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'iw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_iw;
    +}
    +
    +if (goog.LOCALE == 'ja') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ja;
    +}
    +
    +if (goog.LOCALE == 'ja_JP' || goog.LOCALE == 'ja-JP') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ja;
    +}
    +
    +if (goog.LOCALE == 'ka') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ka;
    +}
    +
    +if (goog.LOCALE == 'ka_GE' || goog.LOCALE == 'ka-GE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ka;
    +}
    +
    +if (goog.LOCALE == 'kk') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kk;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl_KZ' || goog.LOCALE == 'kk-Cyrl-KZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kk;
    +}
    +
    +if (goog.LOCALE == 'km') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_km;
    +}
    +
    +if (goog.LOCALE == 'km_KH' || goog.LOCALE == 'km-KH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_km;
    +}
    +
    +if (goog.LOCALE == 'kn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kn;
    +}
    +
    +if (goog.LOCALE == 'kn_IN' || goog.LOCALE == 'kn-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kn;
    +}
    +
    +if (goog.LOCALE == 'ko') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ko;
    +}
    +
    +if (goog.LOCALE == 'ko_KR' || goog.LOCALE == 'ko-KR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ko;
    +}
    +
    +if (goog.LOCALE == 'ky') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ky;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl_KG' || goog.LOCALE == 'ky-Cyrl-KG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ky;
    +}
    +
    +if (goog.LOCALE == 'ln') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ln;
    +}
    +
    +if (goog.LOCALE == 'ln_CD' || goog.LOCALE == 'ln-CD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ln;
    +}
    +
    +if (goog.LOCALE == 'lo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lo;
    +}
    +
    +if (goog.LOCALE == 'lo_LA' || goog.LOCALE == 'lo-LA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lo;
    +}
    +
    +if (goog.LOCALE == 'lt') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lt;
    +}
    +
    +if (goog.LOCALE == 'lt_LT' || goog.LOCALE == 'lt-LT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lt;
    +}
    +
    +if (goog.LOCALE == 'lv') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lv;
    +}
    +
    +if (goog.LOCALE == 'lv_LV' || goog.LOCALE == 'lv-LV') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lv;
    +}
    +
    +if (goog.LOCALE == 'mk') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mk;
    +}
    +
    +if (goog.LOCALE == 'mk_MK' || goog.LOCALE == 'mk-MK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mk;
    +}
    +
    +if (goog.LOCALE == 'ml') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ml;
    +}
    +
    +if (goog.LOCALE == 'ml_IN' || goog.LOCALE == 'ml-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ml;
    +}
    +
    +if (goog.LOCALE == 'mn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mn;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl_MN' || goog.LOCALE == 'mn-Cyrl-MN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mn;
    +}
    +
    +if (goog.LOCALE == 'mr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mr;
    +}
    +
    +if (goog.LOCALE == 'mr_IN' || goog.LOCALE == 'mr-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mr;
    +}
    +
    +if (goog.LOCALE == 'ms') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ms;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_MY' || goog.LOCALE == 'ms-Latn-MY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ms;
    +}
    +
    +if (goog.LOCALE == 'mt') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mt;
    +}
    +
    +if (goog.LOCALE == 'mt_MT' || goog.LOCALE == 'mt-MT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mt;
    +}
    +
    +if (goog.LOCALE == 'my') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_my;
    +}
    +
    +if (goog.LOCALE == 'my_MM' || goog.LOCALE == 'my-MM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_my;
    +}
    +
    +if (goog.LOCALE == 'nb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'nb_NO' || goog.LOCALE == 'nb-NO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'nb_SJ' || goog.LOCALE == 'nb-SJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'ne') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ne;
    +}
    +
    +if (goog.LOCALE == 'ne_NP' || goog.LOCALE == 'ne-NP') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ne;
    +}
    +
    +if (goog.LOCALE == 'nl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl;
    +}
    +
    +if (goog.LOCALE == 'nl_NL' || goog.LOCALE == 'nl-NL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl;
    +}
    +
    +if (goog.LOCALE == 'no') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_no;
    +}
    +
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_no;
    +}
    +
    +if (goog.LOCALE == 'or') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_or;
    +}
    +
    +if (goog.LOCALE == 'or_IN' || goog.LOCALE == 'or-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_or;
    +}
    +
    +if (goog.LOCALE == 'pa') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pa;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru_IN' || goog.LOCALE == 'pa-Guru-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pa;
    +}
    +
    +if (goog.LOCALE == 'pl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pl;
    +}
    +
    +if (goog.LOCALE == 'pl_PL' || goog.LOCALE == 'pl-PL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pl;
    +}
    +
    +if (goog.LOCALE == 'pt') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_PT;
    +}
    +
    +if (goog.LOCALE == 'ro') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ro;
    +}
    +
    +if (goog.LOCALE == 'ro_RO' || goog.LOCALE == 'ro-RO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ro;
    +}
    +
    +if (goog.LOCALE == 'ru') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru;
    +}
    +
    +if (goog.LOCALE == 'ru_RU' || goog.LOCALE == 'ru-RU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru;
    +}
    +
    +if (goog.LOCALE == 'si') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_si;
    +}
    +
    +if (goog.LOCALE == 'si_LK' || goog.LOCALE == 'si-LK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_si;
    +}
    +
    +if (goog.LOCALE == 'sk') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sk;
    +}
    +
    +if (goog.LOCALE == 'sk_SK' || goog.LOCALE == 'sk-SK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sk;
    +}
    +
    +if (goog.LOCALE == 'sl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sl;
    +}
    +
    +if (goog.LOCALE == 'sl_SI' || goog.LOCALE == 'sl-SI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sl;
    +}
    +
    +if (goog.LOCALE == 'sq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sq;
    +}
    +
    +if (goog.LOCALE == 'sq_AL' || goog.LOCALE == 'sq-AL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sq;
    +}
    +
    +if (goog.LOCALE == 'sr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_RS' || goog.LOCALE == 'sr-Cyrl-RS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr;
    +}
    +
    +if (goog.LOCALE == 'sv') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sv;
    +}
    +
    +if (goog.LOCALE == 'sv_SE' || goog.LOCALE == 'sv-SE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sv;
    +}
    +
    +if (goog.LOCALE == 'sw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sw;
    +}
    +
    +if (goog.LOCALE == 'sw_TZ' || goog.LOCALE == 'sw-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sw;
    +}
    +
    +if (goog.LOCALE == 'ta') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ta;
    +}
    +
    +if (goog.LOCALE == 'ta_IN' || goog.LOCALE == 'ta-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ta;
    +}
    +
    +if (goog.LOCALE == 'te') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_te;
    +}
    +
    +if (goog.LOCALE == 'te_IN' || goog.LOCALE == 'te-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_te;
    +}
    +
    +if (goog.LOCALE == 'th') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_th;
    +}
    +
    +if (goog.LOCALE == 'th_TH' || goog.LOCALE == 'th-TH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_th;
    +}
    +
    +if (goog.LOCALE == 'tl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tl;
    +}
    +
    +if (goog.LOCALE == 'tr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tr;
    +}
    +
    +if (goog.LOCALE == 'tr_TR' || goog.LOCALE == 'tr-TR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tr;
    +}
    +
    +if (goog.LOCALE == 'uk') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uk;
    +}
    +
    +if (goog.LOCALE == 'uk_UA' || goog.LOCALE == 'uk-UA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uk;
    +}
    +
    +if (goog.LOCALE == 'ur') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ur;
    +}
    +
    +if (goog.LOCALE == 'ur_PK' || goog.LOCALE == 'ur-PK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ur;
    +}
    +
    +if (goog.LOCALE == 'uz') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn_UZ' || goog.LOCALE == 'uz-Latn-UZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz;
    +}
    +
    +if (goog.LOCALE == 'vi') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vi;
    +}
    +
    +if (goog.LOCALE == 'vi_VN' || goog.LOCALE == 'vi-VN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vi;
    +}
    +
    +if (goog.LOCALE == 'zh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_CN' || goog.LOCALE == 'zh-Hans-CN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_TW;
    +}
    +
    +if (goog.LOCALE == 'zu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zu;
    +}
    +
    +if (goog.LOCALE == 'zu_ZA' || goog.LOCALE == 'zu-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zu;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols_ext.js b/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols_ext.js
    new file mode 100644
    index 0000000000..422fbc141b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/compactnumberformatsymbols_ext.js
    @@ -0,0 +1,30072 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    +// implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Compact number formatting symbols.
    + *
    + * This file is autogenerated by script:
    + * http://go/generate_number_constants.py
    + * using the --for_closure flag.
    + * File generated from CLDR ver. 26
    + *
    + * This file coveres those locales that are not covered in
    + * "compactnumberformatsymbols.js".
    + *
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could fix CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.i18n.CompactNumberFormatSymbolsExt');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_aa');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_aa_DJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_aa_ER');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_aa_ET');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_af_NA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_agq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_agq_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ak');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ak_GH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_AE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_BH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_DJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_DZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_EG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_EH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_ER');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_IL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_IQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_JO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_KM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_KW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_LB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_LY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_MR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_OM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_PS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_QA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_SA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_SD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_SO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_SS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_SY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_TD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_TN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ar_YE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_as');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_as_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_asa');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_asa_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ast');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ast_ES');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_az_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_az_Cyrl_AZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_az_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bas');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bas_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_be');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_be_BY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bem');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bem_ZM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bez');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bez_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bm');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bm_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bm_Latn_ML');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bn_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bo_CN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bo_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_brx');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_brx_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bs');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bs_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bs_Cyrl_BA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bs_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_bs_Latn_BA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cgg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_cgg_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_Arab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_IQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_IR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ckb_Latn_IQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dav');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dav_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_de_LI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dje');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dje_NE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dsb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dsb_DE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dua');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dua_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dyo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dyo_SN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dz');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_dz_BT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ebu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ebu_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ee');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ee_GH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ee_TG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_el_CY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_150');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_AG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_AI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_BZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_CA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_CC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_CK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_CX');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_DM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_ER');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_FJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_FK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_GY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_HK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_IM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_JE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_JM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_KI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_KN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_KY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_LC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_LR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_LS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_MY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_NZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_PN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_RW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SX');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_SZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TV');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_VC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_VU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_WS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_en_ZM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_eo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_eo_001');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_AR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_BO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_CL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_CO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_CR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_CU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_DO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_EC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_GQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_GT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_HN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_MX');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_NI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_PA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_PE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_PH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_PR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_PY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_SV');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_US');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_UY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_es_VE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ewo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ewo_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fa_AF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ff');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ff_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ff_GN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ff_MR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ff_SN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fo_FO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_BE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_BF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_BI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_BJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_DJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_DZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_GA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_GN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_GQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_HT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_KM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_LU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_ML');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_MU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_NC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_NE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_PF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_RW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_SC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_SN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_SY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_TD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_TG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_TN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_VU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fr_WF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fur');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fur_IT');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fy');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_fy_NL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gd');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gd_GB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gsw_FR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_guz');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_guz_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gv');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_gv_IM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ha');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ha_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ha_Latn_GH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ha_Latn_NE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ha_Latn_NG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hr_BA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hsb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_hsb_DE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ia');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ia_FR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ig');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ig_NG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ii');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ii_CN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_it_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_jgo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_jgo_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_jmc');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_jmc_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kab_DZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kam');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kam_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kde');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kde_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kea');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kea_CV');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_khq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_khq_ML');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ki');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ki_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kk_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kkj');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kkj_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kl_GL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kln');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kln_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ko_KP');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kok');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kok_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ks');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ks_Arab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ks_Arab_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksb_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksf');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksf_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ksh_DE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_kw_GB');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ky_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lag');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lag_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lb');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lb_LU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lg_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lkt');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lkt_US');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ln_AO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ln_CF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ln_CG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_lu_CD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_luo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_luo_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_luy');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_luy_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mas');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mas_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mas_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mer');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mer_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mfe');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mfe_MU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mg_MG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mgh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mgh_MZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mgo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mgo_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mn_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ms_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ms_Latn_BN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ms_Latn_SG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mua');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_mua_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_naq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_naq_NA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nd');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nd_ZW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ne_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_AW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_BE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_BQ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_CW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_SR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nl_SX');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nmg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nmg_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nn_NO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nnh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nnh_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nr');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nr_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nso');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nso_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nus');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nus_SD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nyn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_nyn_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_om');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_om_ET');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_om_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_os');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_os_GE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_os_RU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pa_Arab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pa_Arab_PK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pa_Guru');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ps');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ps_AF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_AO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_CV');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_GW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_MO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_MZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_ST');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_pt_TL');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_qu');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_qu_BO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_qu_EC');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_qu_PE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rm');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rm_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rn_BI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ro_MD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rof');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rof_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_BY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_KG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_KZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_MD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ru_UA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rw');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rw_RW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rwk');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_rwk_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sah');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sah_RU');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_saq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_saq_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sbp');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sbp_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_se');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_se_FI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_se_NO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_se_SE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_seh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_seh_MZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ses');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ses_ML');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sg');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sg_CF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_shi');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_shi_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_shi_Latn_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_shi_Tfng');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_shi_Tfng_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_smn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_smn_FI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sn_ZW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_so');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_so_DJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_so_ET');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_so_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_so_SO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sq_MK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sq_XK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_BA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_ME');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_XK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Latn_BA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Latn_ME');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Latn_RS');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sr_Latn_XK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ss');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ss_SZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ss_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ssy');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ssy_ER');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sv_AX');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sv_FI');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sw_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_sw_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_swc');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_swc_CD');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ta_LK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ta_MY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ta_SG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_teo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_teo_KE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_teo_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ti');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ti_ER');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ti_ET');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tn_BW');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tn_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_to');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_to_TO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tr_CY');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ts');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ts_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_twq');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_twq_NE');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tzm');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tzm_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_tzm_Latn_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ug');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ug_Arab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ug_Arab_CN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ur_IN');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Arab');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Arab_AF');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Cyrl');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Cyrl_UZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_uz_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vai');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vai_Latn');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vai_Latn_LR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vai_Vaii');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vai_Vaii_LR');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ve');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_ve_ZA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vo_001');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vun');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_vun_TZ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_wae');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_wae_CH');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_xog');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_xog_UG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yav');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yav_CM');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yi');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yi_001');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yo');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yo_BJ');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_yo_NG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zgh');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zgh_MA');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hans');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hans_HK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hans_MO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hans_SG');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hant');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hant_HK');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hant_MO');
    +goog.provide('goog.i18n.CompactNumberFormatSymbols_zh_Hant_TW');
    +
    +
    +/**
    + * Compact number formatting symbols for locale aa.
    + */
    +goog.i18n.CompactNumberFormatSymbols_aa = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale aa_DJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_aa_DJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale aa_ER.
    + */
    +goog.i18n.CompactNumberFormatSymbols_aa_ER = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale aa_ET.
    + */
    +goog.i18n.CompactNumberFormatSymbols_aa_ET =
    +    goog.i18n.CompactNumberFormatSymbols_aa;
    +
    +
    +/**
    + * Compact number formatting symbols for locale af_NA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_af_NA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '0'
    +    },
    +    '100000': {
    +      'other': '0'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0m'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0m'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0m'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mjd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mjd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mjd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duisend'
    +    },
    +    '10000': {
    +      'other': '00 duisend'
    +    },
    +    '100000': {
    +      'other': '000 duisend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale agq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_agq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale agq_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_agq_CM =
    +    goog.i18n.CompactNumberFormatSymbols_agq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ak.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ak = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ak_GH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ak_GH =
    +    goog.i18n.CompactNumberFormatSymbols_ak;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_AE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_AE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_BH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_BH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_DJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_DJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_DZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_DZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_EG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_EG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_EH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_EH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_ER.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_ER = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_IL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_IL = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_IQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_IQ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_JO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_JO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_KM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_KM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_KW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_KW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_LB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_LB = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_LY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_LY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_MA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_MR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_MR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_OM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_OM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_PS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_PS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_QA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_QA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_SA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_SA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_SD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_SD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_SO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_SO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_SS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_SS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_SY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_SY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_TD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_TD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_TN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_TN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ar_YE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ar_YE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u0645\u0644\u064A\u0648'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u0628\u0644\u064A\u0648'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u062A\u0631\u0644\u064A\u0648'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0623\u0644\u0641'
    +    },
    +    '10000': {
    +      'other': '00 \u0623\u0644\u0641'
    +    },
    +    '100000': {
    +      'other': '000 \u0623\u0644\u0641'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0628\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0631\u064A\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale as.
    + */
    +goog.i18n.CompactNumberFormatSymbols_as = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale as_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_as_IN =
    +    goog.i18n.CompactNumberFormatSymbols_as;
    +
    +
    +/**
    + * Compact number formatting symbols for locale asa.
    + */
    +goog.i18n.CompactNumberFormatSymbols_asa = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale asa_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_asa_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_asa;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ast.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ast = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ast_ES.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ast_ES =
    +    goog.i18n.CompactNumberFormatSymbols_ast;
    +
    +
    +/**
    + * Compact number formatting symbols for locale az_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_az_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale az_Cyrl_AZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_az_Cyrl_AZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale az_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_az_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bas.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bas = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bas_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bas_CM =
    +    goog.i18n.CompactNumberFormatSymbols_bas;
    +
    +
    +/**
    + * Compact number formatting symbols for locale be.
    + */
    +goog.i18n.CompactNumberFormatSymbols_be = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale be_BY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_be_BY =
    +    goog.i18n.CompactNumberFormatSymbols_be;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bem.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bem = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bem_ZM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bem_ZM =
    +    goog.i18n.CompactNumberFormatSymbols_bem;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bez.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bez = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bez_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bez_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_bez;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bm.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bm = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bm_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bm_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bm_Latn_ML.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bm_Latn_ML =
    +    goog.i18n.CompactNumberFormatSymbols_bm;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bn_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bn_IN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '10000': {
    +      'other': '00 \u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '100000': {
    +      'other': '000 \u09B9\u09BE\u099C\u09BE\u09B0'
    +    },
    +    '1000000': {
    +      'other': '0 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000': {
    +      'other': '00 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000': {
    +      'other': '000 \u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '1000000000': {
    +      'other': '0 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000000': {
    +      'other': '00 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000000': {
    +      'other': '000 \u09AC\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u099F\u09CD\u09B0\u09BF\u09B2\u09BF\u09AF\u09BC\u09A8'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bo_CN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bo_CN =
    +    goog.i18n.CompactNumberFormatSymbols_bo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bo_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bo_IN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale brx.
    + */
    +goog.i18n.CompactNumberFormatSymbols_brx = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale brx_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_brx_IN =
    +    goog.i18n.CompactNumberFormatSymbols_brx;
    +
    +
    +/**
    + * Compact number formatting symbols for locale bs.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bs = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlr.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlr.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlr.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 biliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 biliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 biliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bs_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bs_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u0459'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u0459'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0431\u0438\u043B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0431\u0438\u043B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0431\u0438\u043B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u0459'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u0459'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0431\u0438\u043B'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0431\u0438\u043B'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0431\u0438\u043B'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bs_Cyrl_BA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bs_Cyrl_BA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u0459'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u0459'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0431\u0438\u043B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0431\u0438\u043B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0431\u0438\u043B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u0459'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u0459'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0431\u0438\u043B'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0431\u0438\u043B'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0431\u0438\u043B'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bs_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bs_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlr.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlr.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlr.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 biliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 biliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 biliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale bs_Latn_BA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_bs_Latn_BA =
    +    goog.i18n.CompactNumberFormatSymbols_bs;
    +
    +
    +/**
    + * Compact number formatting symbols for locale cgg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cgg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale cgg_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_cgg_UG =
    +    goog.i18n.CompactNumberFormatSymbols_cgg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_Arab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_Arab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_Arab_IQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IQ =
    +    goog.i18n.CompactNumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_Arab_IR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_IQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_IQ =
    +    goog.i18n.CompactNumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_IR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_IR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ckb_Latn_IQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ckb_Latn_IQ =
    +    goog.i18n.CompactNumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale dav.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dav = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dav_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dav_KE =
    +    goog.i18n.CompactNumberFormatSymbols_dav;
    +
    +
    +/**
    + * Compact number formatting symbols for locale de_LI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_de_LI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0Tsd.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0Tsd.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0Tsd.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Tausend'
    +    },
    +    '10000': {
    +      'other': '00 Tausend'
    +    },
    +    '100000': {
    +      'other': '000 Tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dje.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dje = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dje_NE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dje_NE =
    +    goog.i18n.CompactNumberFormatSymbols_dje;
    +
    +
    +/**
    + * Compact number formatting symbols for locale dsb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dsb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tys.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tys.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tys.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mio.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mio.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mio.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tysac'
    +    },
    +    '10000': {
    +      'other': '00 tysac'
    +    },
    +    '100000': {
    +      'other': '000 tysac'
    +    },
    +    '1000000': {
    +      'other': '0 milionow'
    +    },
    +    '10000000': {
    +      'other': '00 milionow'
    +    },
    +    '100000000': {
    +      'other': '000 milionow'
    +    },
    +    '1000000000': {
    +      'other': '0 miliardow'
    +    },
    +    '10000000000': {
    +      'other': '00 miliardow'
    +    },
    +    '100000000000': {
    +      'other': '000 miliardow'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilionow'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilionow'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilionow'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dsb_DE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dsb_DE =
    +    goog.i18n.CompactNumberFormatSymbols_dsb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale dua.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dua = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dua_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dua_CM =
    +    goog.i18n.CompactNumberFormatSymbols_dua;
    +
    +
    +/**
    + * Compact number formatting symbols for locale dyo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dyo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dyo_SN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dyo_SN =
    +    goog.i18n.CompactNumberFormatSymbols_dyo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale dz.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dz = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '\u0F66\u0F9F\u0F7C\u0F44\u0F0B\u0F55\u0FB2\u0F42 0'
    +    },
    +    '10000': {
    +      'other': '\u0F41\u0FB2\u0F72\u0F0B\u0F55\u0FB2\u0F42 0'
    +    },
    +    '100000': {
    +      'other': '\u0F60\u0F56\u0F74\u0F58\u0F0B\u0F55\u0FB2\u0F42 0'
    +    },
    +    '1000000': {
    +      'other': '\u0F66\u0F0B\u0F61\u0F0B 0'
    +    },
    +    '10000000': {
    +      'other': '\u0F56\u0FB1\u0F7A\u0F0B\u0F56\u0F0B 0'
    +    },
    +    '100000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B 0'
    +    },
    +    '1000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B 00'
    +    },
    +    '10000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F56\u0F62\u0F92\u0FB1\u0F0B 0'
    +    },
    +    '100000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F9F\u0F7C\u0F44 0'
    +    },
    +    '1000000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F41\u0FB2\u0F72\u0F0B 0'
    +    },
    +    '10000000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F60\u0F56\u0F74\u0F58\u0F0B 0'
    +    },
    +    '100000000000000': {
    +      'other': '\u0F51\u0F74\u0F44\u0F0B\u0F55\u0FB1\u0F74\u0F62\u0F0B\u0F66\u0F0B\u0F61\u0F0B 0'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale dz_BT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_dz_BT =
    +    goog.i18n.CompactNumberFormatSymbols_dz;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ebu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ebu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ebu_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ebu_KE =
    +    goog.i18n.CompactNumberFormatSymbols_ebu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ee.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ee = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0000M'
    +    },
    +    '10000000000': {
    +      'other': '00000M'
    +    },
    +    '100000000000': {
    +      'other': '000000M'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000000': {
    +      'other': '000B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': 'akpe 0'
    +    },
    +    '10000': {
    +      'other': 'akpe 00'
    +    },
    +    '100000': {
    +      'other': 'akpe 000'
    +    },
    +    '1000000': {
    +      'other': 'mili\u0254n 0'
    +    },
    +    '10000000': {
    +      'other': 'mili\u0254n 00'
    +    },
    +    '100000000': {
    +      'other': 'mili\u0254n 000'
    +    },
    +    '1000000000': {
    +      'other': 'mili\u0254n 0000'
    +    },
    +    '10000000000': {
    +      'other': 'mili\u0254n 00000'
    +    },
    +    '100000000000': {
    +      'other': 'mili\u0254n 000000'
    +    },
    +    '1000000000000': {
    +      'other': 'bili\u0254n 0'
    +    },
    +    '10000000000000': {
    +      'other': 'bili\u0254n 00'
    +    },
    +    '100000000000000': {
    +      'other': 'bili\u0254n 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ee_GH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ee_GH =
    +    goog.i18n.CompactNumberFormatSymbols_ee;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ee_TG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ee_TG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0000M'
    +    },
    +    '10000000000': {
    +      'other': '00000M'
    +    },
    +    '100000000000': {
    +      'other': '000000M'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000000': {
    +      'other': '000B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': 'akpe 0'
    +    },
    +    '10000': {
    +      'other': 'akpe 00'
    +    },
    +    '100000': {
    +      'other': 'akpe 000'
    +    },
    +    '1000000': {
    +      'other': 'mili\u0254n 0'
    +    },
    +    '10000000': {
    +      'other': 'mili\u0254n 00'
    +    },
    +    '100000000': {
    +      'other': 'mili\u0254n 000'
    +    },
    +    '1000000000': {
    +      'other': 'mili\u0254n 0000'
    +    },
    +    '10000000000': {
    +      'other': 'mili\u0254n 00000'
    +    },
    +    '100000000000': {
    +      'other': 'mili\u0254n 000000'
    +    },
    +    '1000000000000': {
    +      'other': 'bili\u0254n 0'
    +    },
    +    '10000000000000': {
    +      'other': 'bili\u0254n 00'
    +    },
    +    '100000000000000': {
    +      'other': 'bili\u0254n 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale el_CY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_el_CY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u03C7\u03B9\u03BB.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u03B5\u03BA.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u03B5\u03BA.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u03B5\u03BA.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u03B4\u03B9\u03C3.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u03C4\u03C1\u03B9\u03C3.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '10000': {
    +      'other': '00 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '100000': {
    +      'other': '000 \u03C7\u03B9\u03BB\u03B9\u03AC\u03B4\u03B5\u03C2'
    +    },
    +    '1000000': {
    +      'other': '0 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000': {
    +      'other': '00 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000': {
    +      'other': '000 \u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '1000000000': {
    +      'other': '0 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000000': {
    +      'other': '00 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000000': {
    +      'other': '000 \u03B4\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u03C4\u03C1\u03B9\u03C3\u03B5\u03BA\u03B1\u03C4\u03BF\u03BC\u03BC\u03CD\u03C1\u03B9\u03B1'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_150.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_150 = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_AG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_AG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_AI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_AI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BB = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_BZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_BZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_CA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_CA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_CC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_CC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_CK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_CK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_CM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_CX.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_CX = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_DM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_DM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_ER.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_ER = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_FJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_FJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_FK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_FK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_GY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_GY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_HK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_HK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_IM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_IM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_JE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_JE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_JM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_JM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_KE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_KI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_KI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_KN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_KN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_KY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_KY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_LC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_LC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_LR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_LR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_LS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_LS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_MY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_MY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_NZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_NZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_PN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_PN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_RW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_RW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SB = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SL = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SX.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SX = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_SZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_SZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TV.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TV = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_TZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_UG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_VC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_VC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_VU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_VU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_WS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_WS = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale en_ZM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_en_ZM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 thousand'
    +    },
    +    '10000': {
    +      'other': '00 thousand'
    +    },
    +    '100000': {
    +      'other': '000 thousand'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 billion'
    +    },
    +    '10000000000': {
    +      'other': '00 billion'
    +    },
    +    '100000000000': {
    +      'other': '000 billion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale eo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_eo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale eo_001.
    + */
    +goog.i18n.CompactNumberFormatSymbols_eo_001 =
    +    goog.i18n.CompactNumberFormatSymbols_eo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_AR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_AR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_BO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_BO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_CL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_CL = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_CO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_CO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_CR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_CR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_CU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_CU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_DO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_DO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_EC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_EC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_GQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_GQ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0000M'
    +    },
    +    '10000000000': {
    +      'other': '00MRD'
    +    },
    +    '100000000000': {
    +      'other': '000MRD'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000000': {
    +      'other': '000B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_GT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_GT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_HN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_HN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_MX.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_MX = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0k'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_NI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_NI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_PA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_PA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_PE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_PE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_PH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_PH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0000M'
    +    },
    +    '10000000000': {
    +      'other': '00MRD'
    +    },
    +    '100000000000': {
    +      'other': '000MRD'
    +    },
    +    '1000000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000000': {
    +      'other': '000B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_PR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_PR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_PY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_PY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_SV.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_SV = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_US.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_US = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_UY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_UY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale es_VE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_es_VE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0k\u00A0M'
    +    },
    +    '10000000000': {
    +      'other': '00k\u00A0M'
    +    },
    +    '100000000000': {
    +      'other': '000k\u00A0M'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0B'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0B'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 millones'
    +    },
    +    '10000000': {
    +      'other': '00 millones'
    +    },
    +    '100000000': {
    +      'other': '000 millones'
    +    },
    +    '1000000000': {
    +      'other': '0 mil millones'
    +    },
    +    '10000000000': {
    +      'other': '00 mil millones'
    +    },
    +    '100000000000': {
    +      'other': '000 mil millones'
    +    },
    +    '1000000000000': {
    +      'other': '0 billones'
    +    },
    +    '10000000000000': {
    +      'other': '00 billones'
    +    },
    +    '100000000000000': {
    +      'other': '000 billones'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ewo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ewo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ewo_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ewo_CM =
    +    goog.i18n.CompactNumberFormatSymbols_ewo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fa_AF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fa_AF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0647\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00 \u0647\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '000 \u0647\u0632\u0627\u0631'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u06CC\u0644\u06CC\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0647\u0632\u0627\u0631 \u0645\u06CC\u0644\u06CC\u0627\u0631\u062F'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ff.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ff = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ff_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ff_CM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ff_GN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ff_GN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ff_MR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ff_MR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ff_SN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ff_SN =
    +    goog.i18n.CompactNumberFormatSymbols_ff;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0td'
    +    },
    +    '10000': {
    +      'other': '00\u00A0td'
    +    },
    +    '100000': {
    +      'other': '000\u00A0td'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusind'
    +    },
    +    '10000': {
    +      'other': '00 tusind'
    +    },
    +    '100000': {
    +      'other': '000 tusind'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fo_FO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fo_FO =
    +    goog.i18n.CompactNumberFormatSymbols_fo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_BE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_BE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_BF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_BF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_BI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_BI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_BJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_BJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_CM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_DJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_DJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_DZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_DZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_GA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_GA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_GN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_GN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_GQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_GQ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_HT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_HT = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_KM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_KM = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_LU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_LU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_ML.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_ML = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_MU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_MU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_NC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_NC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_NE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_NE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_PF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_PF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_RW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_RW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_SC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_SC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_SN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_SN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_SY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_SY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_TD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_TD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_TG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_TG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_TN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_TN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_VU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_VU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fr_WF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fr_WF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0k'
    +    },
    +    '10000': {
    +      'other': '00\u00A0k'
    +    },
    +    '100000': {
    +      'other': '000\u00A0k'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mille'
    +    },
    +    '10000': {
    +      'other': '00 mille'
    +    },
    +    '100000': {
    +      'other': '000 mille'
    +    },
    +    '1000000': {
    +      'other': '0 millions'
    +    },
    +    '10000000': {
    +      'other': '00 millions'
    +    },
    +    '100000000': {
    +      'other': '000 millions'
    +    },
    +    '1000000000': {
    +      'other': '0 milliards'
    +    },
    +    '10000000000': {
    +      'other': '00 milliards'
    +    },
    +    '100000000000': {
    +      'other': '000 milliards'
    +    },
    +    '1000000000000': {
    +      'other': '0 billions'
    +    },
    +    '10000000000000': {
    +      'other': '00 billions'
    +    },
    +    '100000000000000': {
    +      'other': '000 billions'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fur.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fur = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fur_IT.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fur_IT =
    +    goog.i18n.CompactNumberFormatSymbols_fur;
    +
    +
    +/**
    + * Compact number formatting symbols for locale fy.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fy = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 t\u00FBzen'
    +    },
    +    '10000': {
    +      'other': '00 t\u00FBzen'
    +    },
    +    '100000': {
    +      'other': '000 t\u00FBzen'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale fy_NL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_fy_NL =
    +    goog.i18n.CompactNumberFormatSymbols_fy;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gd.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gd = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 m\u00ECle'
    +    },
    +    '10000': {
    +      'other': '00 m\u00ECle'
    +    },
    +    '100000': {
    +      'other': '000 m\u00ECle'
    +    },
    +    '1000000': {
    +      'other': '0 millean'
    +    },
    +    '10000000': {
    +      'other': '00 millean'
    +    },
    +    '100000000': {
    +      'other': '000 millean'
    +    },
    +    '1000000000': {
    +      'other': '0 billean'
    +    },
    +    '10000000000': {
    +      'other': '00 billean'
    +    },
    +    '100000000000': {
    +      'other': '000 billean'
    +    },
    +    '1000000000000': {
    +      'other': '0 trillean'
    +    },
    +    '10000000000000': {
    +      'other': '00 trillean'
    +    },
    +    '100000000000000': {
    +      'other': '000 trillean'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale gd_GB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gd_GB =
    +    goog.i18n.CompactNumberFormatSymbols_gd;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gsw_FR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gsw_FR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tsd'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tsd'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tsd'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tausend'
    +    },
    +    '10000': {
    +      'other': '00 tausend'
    +    },
    +    '100000': {
    +      'other': '000 tausend'
    +    },
    +    '1000000': {
    +      'other': '0 Millionen'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billionen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale guz.
    + */
    +goog.i18n.CompactNumberFormatSymbols_guz = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale guz_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_guz_KE =
    +    goog.i18n.CompactNumberFormatSymbols_guz;
    +
    +
    +/**
    + * Compact number formatting symbols for locale gv.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gv = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale gv_IM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_gv_IM =
    +    goog.i18n.CompactNumberFormatSymbols_gv;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ha.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ha = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ha_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ha_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ha_Latn_GH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ha_Latn_GH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ha_Latn_NE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ha_Latn_NE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ha_Latn_NG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ha_Latn_NG =
    +    goog.i18n.CompactNumberFormatSymbols_ha;
    +
    +
    +/**
    + * Compact number formatting symbols for locale hr_BA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hr_BA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tis.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tis.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tis.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlr.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlr.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlr.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tisu\u0107a'
    +    },
    +    '10000': {
    +      'other': '00 tisu\u0107a'
    +    },
    +    '100000': {
    +      'other': '000 tisu\u0107a'
    +    },
    +    '1000000': {
    +      'other': '0 milijuna'
    +    },
    +    '10000000': {
    +      'other': '00 milijuna'
    +    },
    +    '100000000': {
    +      'other': '000 milijuna'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilijuna'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilijuna'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilijuna'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hsb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hsb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tys.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tys.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tys.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mio.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mio.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mio.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tysac'
    +    },
    +    '10000': {
    +      'other': '00 tysac'
    +    },
    +    '100000': {
    +      'other': '000 tysac'
    +    },
    +    '1000000': {
    +      'other': '0 milionow'
    +    },
    +    '10000000': {
    +      'other': '00 milionow'
    +    },
    +    '100000000': {
    +      'other': '000 milionow'
    +    },
    +    '1000000000': {
    +      'other': '0 miliardow'
    +    },
    +    '10000000000': {
    +      'other': '00 miliardow'
    +    },
    +    '100000000000': {
    +      'other': '000 miliardow'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilionow'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilionow'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilionow'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale hsb_DE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_hsb_DE =
    +    goog.i18n.CompactNumberFormatSymbols_hsb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ia.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ia = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ia_FR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ia_FR =
    +    goog.i18n.CompactNumberFormatSymbols_ia;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ig.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ig = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ig_NG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ig_NG =
    +    goog.i18n.CompactNumberFormatSymbols_ig;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ii.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ii = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ii_CN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ii_CN =
    +    goog.i18n.CompactNumberFormatSymbols_ii;
    +
    +
    +/**
    + * Compact number formatting symbols for locale it_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_it_CH = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '0'
    +    },
    +    '100000': {
    +      'other': '0'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 migliaia'
    +    },
    +    '10000': {
    +      'other': '00 migliaia'
    +    },
    +    '100000': {
    +      'other': '000 migliaia'
    +    },
    +    '1000000': {
    +      'other': '0 milioni'
    +    },
    +    '10000000': {
    +      'other': '00 milioni'
    +    },
    +    '100000000': {
    +      'other': '000 milioni'
    +    },
    +    '1000000000': {
    +      'other': '0 miliardi'
    +    },
    +    '10000000000': {
    +      'other': '00 miliardi'
    +    },
    +    '100000000000': {
    +      'other': '000 miliardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 migliaia di miliardi'
    +    },
    +    '10000000000000': {
    +      'other': '00 migliaia di miliardi'
    +    },
    +    '100000000000000': {
    +      'other': '000 migliaia di miliardi'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale jgo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_jgo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale jgo_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_jgo_CM =
    +    goog.i18n.CompactNumberFormatSymbols_jgo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale jmc.
    + */
    +goog.i18n.CompactNumberFormatSymbols_jmc = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale jmc_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_jmc_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_jmc;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kab_DZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kab_DZ =
    +    goog.i18n.CompactNumberFormatSymbols_kab;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kam.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kam = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kam_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kam_KE =
    +    goog.i18n.CompactNumberFormatSymbols_kam;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kde.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kde = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kde_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kde_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_kde;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kea.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kea = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00E3u'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00E3u'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00E3u'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00E3u'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00E3u'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00E3u'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilh\u00E3u'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilh\u00E3u'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilh\u00E3u'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kea_CV.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kea_CV =
    +    goog.i18n.CompactNumberFormatSymbols_kea;
    +
    +
    +/**
    + * Compact number formatting symbols for locale khq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_khq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale khq_ML.
    + */
    +goog.i18n.CompactNumberFormatSymbols_khq_ML =
    +    goog.i18n.CompactNumberFormatSymbols_khq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ki.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ki = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ki_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ki_KE =
    +    goog.i18n.CompactNumberFormatSymbols_ki;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kk_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kk_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u043C\u044B\u04A3'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u043C\u044B\u04A3'
    +    },
    +    '100000': {
    +      'other': '000\u041C'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u0411'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kkj.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kkj = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kkj_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kkj_CM =
    +    goog.i18n.CompactNumberFormatSymbols_kkj;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0td'
    +    },
    +    '10000': {
    +      'other': '00\u00A0td'
    +    },
    +    '100000': {
    +      'other': '000\u00A0td'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusind'
    +    },
    +    '10000': {
    +      'other': '00 tusind'
    +    },
    +    '100000': {
    +      'other': '000 tusind'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kl_GL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kl_GL =
    +    goog.i18n.CompactNumberFormatSymbols_kl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kln.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kln = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kln_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kln_KE =
    +    goog.i18n.CompactNumberFormatSymbols_kln;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ko_KP.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ko_KP = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '0\uB9CC'
    +    },
    +    '100000': {
    +      'other': '00\uB9CC'
    +    },
    +    '1000000': {
    +      'other': '000\uB9CC'
    +    },
    +    '10000000': {
    +      'other': '0000\uB9CC'
    +    },
    +    '100000000': {
    +      'other': '0\uC5B5'
    +    },
    +    '1000000000': {
    +      'other': '00\uC5B5'
    +    },
    +    '10000000000': {
    +      'other': '000\uC5B5'
    +    },
    +    '100000000000': {
    +      'other': '0000\uC5B5'
    +    },
    +    '1000000000000': {
    +      'other': '0\uC870'
    +    },
    +    '10000000000000': {
    +      'other': '00\uC870'
    +    },
    +    '100000000000000': {
    +      'other': '000\uC870'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0000'
    +    },
    +    '10000': {
    +      'other': '0\uB9CC'
    +    },
    +    '100000': {
    +      'other': '00\uB9CC'
    +    },
    +    '1000000': {
    +      'other': '000\uB9CC'
    +    },
    +    '10000000': {
    +      'other': '0000\uB9CC'
    +    },
    +    '100000000': {
    +      'other': '0\uC5B5'
    +    },
    +    '1000000000': {
    +      'other': '00\uC5B5'
    +    },
    +    '10000000000': {
    +      'other': '000\uC5B5'
    +    },
    +    '100000000000': {
    +      'other': '0000\uC5B5'
    +    },
    +    '1000000000000': {
    +      'other': '0\uC870'
    +    },
    +    '10000000000000': {
    +      'other': '00\uC870'
    +    },
    +    '100000000000000': {
    +      'other': '000\uC870'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kok.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kok = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kok_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kok_IN =
    +    goog.i18n.CompactNumberFormatSymbols_kok;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ks.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ks = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ks_Arab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ks_Arab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ks_Arab_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ks_Arab_IN =
    +    goog.i18n.CompactNumberFormatSymbols_ks;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksb_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksb_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_ksb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksf.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksf = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksf_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksf_CM =
    +    goog.i18n.CompactNumberFormatSymbols_ksf;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tsd'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tsd'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tsd'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Dousend'
    +    },
    +    '10000': {
    +      'other': '00 Dousend'
    +    },
    +    '100000': {
    +      'other': '000 Dousend'
    +    },
    +    '1000000': {
    +      'other': '0 Milljuhne'
    +    },
    +    '10000000': {
    +      'other': '00 Millionen'
    +    },
    +    '100000000': {
    +      'other': '000 Millionen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milljarde'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billjuhn'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billionen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billionen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ksh_DE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ksh_DE =
    +    goog.i18n.CompactNumberFormatSymbols_ksh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale kw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale kw_GB.
    + */
    +goog.i18n.CompactNumberFormatSymbols_kw_GB =
    +    goog.i18n.CompactNumberFormatSymbols_kw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ky_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ky_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u043C\u0438\u04CA'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u043C\u0438\u04CA'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u043C\u0438\u04CA'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u0438\u04CA'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u0438\u04CA'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u0438\u04CA'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lag.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lag = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lag_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lag_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_lag;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lb.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lb = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0Dsd.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0Dsd.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0Dsd.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mio.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mio.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mio.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bio.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bio.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bio.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 Dausend'
    +    },
    +    '10000': {
    +      'other': '00 Dausend'
    +    },
    +    '100000': {
    +      'other': '000 Dausend'
    +    },
    +    '1000000': {
    +      'other': '0 Milliounen'
    +    },
    +    '10000000': {
    +      'other': '00 Milliounen'
    +    },
    +    '100000000': {
    +      'other': '000 Milliounen'
    +    },
    +    '1000000000': {
    +      'other': '0 Milliarden'
    +    },
    +    '10000000000': {
    +      'other': '00 Milliarden'
    +    },
    +    '100000000000': {
    +      'other': '000 Milliarden'
    +    },
    +    '1000000000000': {
    +      'other': '0 Billiounen'
    +    },
    +    '10000000000000': {
    +      'other': '00 Billiounen'
    +    },
    +    '100000000000000': {
    +      'other': '000 Billiounen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lb_LU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lb_LU =
    +    goog.i18n.CompactNumberFormatSymbols_lb;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lg_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lg_UG =
    +    goog.i18n.CompactNumberFormatSymbols_lg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale lkt.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lkt = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lkt_US.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lkt_US =
    +    goog.i18n.CompactNumberFormatSymbols_lkt;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ln_AO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ln_AO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ln_CF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ln_CF = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ln_CG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ln_CG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale lu_CD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_lu_CD =
    +    goog.i18n.CompactNumberFormatSymbols_lu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale luo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_luo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale luo_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_luo_KE =
    +    goog.i18n.CompactNumberFormatSymbols_luo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale luy.
    + */
    +goog.i18n.CompactNumberFormatSymbols_luy = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale luy_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_luy_KE =
    +    goog.i18n.CompactNumberFormatSymbols_luy;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mas.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mas = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mas_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mas_KE =
    +    goog.i18n.CompactNumberFormatSymbols_mas;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mas_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mas_TZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mer.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mer = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mer_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mer_KE =
    +    goog.i18n.CompactNumberFormatSymbols_mer;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mfe.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mfe = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mfe_MU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mfe_MU =
    +    goog.i18n.CompactNumberFormatSymbols_mfe;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mg_MG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mg_MG =
    +    goog.i18n.CompactNumberFormatSymbols_mg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mgh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mgh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mgh_MZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mgh_MZ =
    +    goog.i18n.CompactNumberFormatSymbols_mgh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mgo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mgo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mgo_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mgo_CM =
    +    goog.i18n.CompactNumberFormatSymbols_mgo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale mn_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mn_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u041C'
    +    },
    +    '10000': {
    +      'other': '00\u041C'
    +    },
    +    '100000': {
    +      'other': '000\u041C'
    +    },
    +    '1000000': {
    +      'other': '0\u0421'
    +    },
    +    '10000000': {
    +      'other': '00\u0421'
    +    },
    +    '100000000': {
    +      'other': '000\u0421'
    +    },
    +    '1000000000': {
    +      'other': '0\u0422'
    +    },
    +    '10000000000': {
    +      'other': '00\u0422'
    +    },
    +    '100000000000': {
    +      'other': '000\u0422'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0418\u041D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0418\u041D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0418\u041D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u044F\u043D\u0433\u0430'
    +    },
    +    '1000000': {
    +      'other': '0 \u0441\u0430\u044F'
    +    },
    +    '10000000': {
    +      'other': '00 \u0441\u0430\u044F'
    +    },
    +    '100000000': {
    +      'other': '000 \u0441\u0430\u044F'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0442\u044D\u0440\u0431\u0443\u043C'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0438\u0445 \u043D\u0430\u044F\u0434'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ms_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ms_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0J'
    +    },
    +    '10000000': {
    +      'other': '00J'
    +    },
    +    '100000000': {
    +      'other': '000J'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000': {
    +      'other': '000 bilion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ms_Latn_BN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ms_Latn_BN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0J'
    +    },
    +    '10000000': {
    +      'other': '00J'
    +    },
    +    '100000000': {
    +      'other': '000J'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000': {
    +      'other': '000 bilion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ms_Latn_SG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ms_Latn_SG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0J'
    +    },
    +    '10000000': {
    +      'other': '00J'
    +    },
    +    '100000000': {
    +      'other': '000J'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ribu'
    +    },
    +    '10000': {
    +      'other': '00 ribu'
    +    },
    +    '100000': {
    +      'other': '000 ribu'
    +    },
    +    '1000000': {
    +      'other': '0 juta'
    +    },
    +    '10000000': {
    +      'other': '00 juta'
    +    },
    +    '100000000': {
    +      'other': '000 juta'
    +    },
    +    '1000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000': {
    +      'other': '000 bilion'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mua.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mua = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale mua_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_mua_CM =
    +    goog.i18n.CompactNumberFormatSymbols_mua;
    +
    +
    +/**
    + * Compact number formatting symbols for locale naq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_naq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale naq_NA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_naq_NA =
    +    goog.i18n.CompactNumberFormatSymbols_naq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nd.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nd = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nd_ZW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nd_ZW =
    +    goog.i18n.CompactNumberFormatSymbols_nd;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ne_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ne_IN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0939\u091C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0939\u091C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0932\u093E\u0916'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0915\u0930\u094B\u0921'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0915\u0930\u094B\u0921'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0905\u0930\u092C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0905\u0930\u092C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0916\u0930\u092C'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0916\u0930\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0936\u0902\u0916'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u0936\u0902\u0916'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0939\u091C\u093E\u0930'
    +    },
    +    '10000': {
    +      'other': '00 \u0939\u091C\u093E\u0930'
    +    },
    +    '100000': {
    +      'other': '0 \u0932\u093E\u0916'
    +    },
    +    '1000000': {
    +      'other': '0 \u0915\u0930\u094B\u0921'
    +    },
    +    '10000000': {
    +      'other': '00 \u0915\u0930\u094B\u0921'
    +    },
    +    '100000000': {
    +      'other': '000 \u0915\u0930\u094B\u0921'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0905\u0930\u094D\u092C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0905\u0930\u094D\u092C'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0905\u0930\u092C'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0916\u0930\u094D\u092C'
    +    },
    +    '10000000000000': {
    +      'other': '0 \u0936\u0902\u0916'
    +    },
    +    '100000000000000': {
    +      'other': '00 \u0936\u0902\u0916'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_AW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_AW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_BE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_BE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_BQ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_BQ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_CW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_CW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_SR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_SR = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nl_SX.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nl_SX = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mln.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mln.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mln.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bln.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bln.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bln.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duizend'
    +    },
    +    '10000': {
    +      'other': '00 duizend'
    +    },
    +    '100000': {
    +      'other': '000 duizend'
    +    },
    +    '1000000': {
    +      'other': '0 miljoen'
    +    },
    +    '10000000': {
    +      'other': '00 miljoen'
    +    },
    +    '100000000': {
    +      'other': '000 miljoen'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoen'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoen'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoen'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nmg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nmg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nmg_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nmg_CM =
    +    goog.i18n.CompactNumberFormatSymbols_nmg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tn'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tn'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tn'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 millioner'
    +    },
    +    '10000000': {
    +      'other': '00 millioner'
    +    },
    +    '100000000': {
    +      'other': '000 millioner'
    +    },
    +    '1000000000': {
    +      'other': '0 milliarder'
    +    },
    +    '10000000000': {
    +      'other': '00 milliarder'
    +    },
    +    '100000000000': {
    +      'other': '000 milliarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 billioner'
    +    },
    +    '10000000000000': {
    +      'other': '00 billioner'
    +    },
    +    '100000000000000': {
    +      'other': '000 billioner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nn_NO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nn_NO =
    +    goog.i18n.CompactNumberFormatSymbols_nn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nnh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nnh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nnh_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nnh_CM =
    +    goog.i18n.CompactNumberFormatSymbols_nnh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nr.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nr = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nr_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nr_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_nr;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nso.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nso = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nso_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nso_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_nso;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nus.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nus = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nus_SD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nus_SD =
    +    goog.i18n.CompactNumberFormatSymbols_nus;
    +
    +
    +/**
    + * Compact number formatting symbols for locale nyn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nyn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale nyn_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_nyn_UG =
    +    goog.i18n.CompactNumberFormatSymbols_nyn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale om.
    + */
    +goog.i18n.CompactNumberFormatSymbols_om = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale om_ET.
    + */
    +goog.i18n.CompactNumberFormatSymbols_om_ET =
    +    goog.i18n.CompactNumberFormatSymbols_om;
    +
    +
    +/**
    + * Compact number formatting symbols for locale om_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_om_KE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale os.
    + */
    +goog.i18n.CompactNumberFormatSymbols_os = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale os_GE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_os_GE =
    +    goog.i18n.CompactNumberFormatSymbols_os;
    +
    +
    +/**
    + * Compact number formatting symbols for locale os_RU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_os_RU = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pa_Arab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pa_Arab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pa_Arab_PK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pa_Arab_PK =
    +    goog.i18n.CompactNumberFormatSymbols_pa_Arab;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pa_Guru.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pa_Guru = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0A32\u0A71\u0A16'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0A32\u0A71\u0A16'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0A05\u0A30\u0A2C'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0A05\u0A30\u0A2C'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u0A16\u0A30\u0A2C'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u0A16\u0A30\u0A2C'
    +    },
    +    '10000000000000': {
    +      'other': '0\u00A0\u0A28\u0A40\u0A32'
    +    },
    +    '100000000000000': {
    +      'other': '00\u00A0\u0A28\u0A40\u0A32'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '10000': {
    +      'other': '00 \u0A39\u0A1C\u0A3C\u0A3E\u0A30'
    +    },
    +    '100000': {
    +      'other': '0 \u0A32\u0A71\u0A16'
    +    },
    +    '1000000': {
    +      'other': '00 \u0A32\u0A71\u0A16'
    +    },
    +    '10000000': {
    +      'other': '0 \u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '100000000': {
    +      'other': '00 \u0A15\u0A30\u0A4B\u0A5C'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0A05\u0A30\u0A2C'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0A05\u0A30\u0A2C'
    +    },
    +    '100000000000': {
    +      'other': '0 \u0A16\u0A30\u0A2C'
    +    },
    +    '1000000000000': {
    +      'other': '00 \u0A16\u0A30\u0A2C'
    +    },
    +    '10000000000000': {
    +      'other': '0 \u0A28\u0A40\u0A32'
    +    },
    +    '100000000000000': {
    +      'other': '00 \u0A28\u0A40\u0A32'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ps.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ps = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ps_AF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ps_AF =
    +    goog.i18n.CompactNumberFormatSymbols_ps;
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_AO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_AO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_CV.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_CV = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_GW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_GW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_MO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_MO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_MZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_MZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_ST.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_ST = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale pt_TL.
    + */
    +goog.i18n.CompactNumberFormatSymbols_pt_TL = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mil'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mil'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mil'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0M'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0M'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0M'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0MM'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0MM'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0MM'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bi'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bi'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bi'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mil'
    +    },
    +    '10000': {
    +      'other': '00 mil'
    +    },
    +    '100000': {
    +      'other': '000 mil'
    +    },
    +    '1000000': {
    +      'other': '0 milh\u00F5es'
    +    },
    +    '10000000': {
    +      'other': '00 milh\u00F5es'
    +    },
    +    '100000000': {
    +      'other': '000 milh\u00F5es'
    +    },
    +    '1000000000': {
    +      'other': '0 mil milh\u00F5es'
    +    },
    +    '10000000000': {
    +      'other': '00 mil milh\u00F5es'
    +    },
    +    '100000000000': {
    +      'other': '000 mil milh\u00F5es'
    +    },
    +    '1000000000000': {
    +      'other': '0 bili\u00F5es'
    +    },
    +    '10000000000000': {
    +      'other': '00 bili\u00F5es'
    +    },
    +    '100000000000000': {
    +      'other': '000 bili\u00F5es'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale qu.
    + */
    +goog.i18n.CompactNumberFormatSymbols_qu = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale qu_BO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_qu_BO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale qu_EC.
    + */
    +goog.i18n.CompactNumberFormatSymbols_qu_EC = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale qu_PE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_qu_PE =
    +    goog.i18n.CompactNumberFormatSymbols_qu;
    +
    +
    +/**
    + * Compact number formatting symbols for locale rm.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rm = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rm_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rm_CH =
    +    goog.i18n.CompactNumberFormatSymbols_rm;
    +
    +
    +/**
    + * Compact number formatting symbols for locale rn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rn_BI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rn_BI =
    +    goog.i18n.CompactNumberFormatSymbols_rn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ro_MD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ro_MD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0K'
    +    },
    +    '10000': {
    +      'other': '00\u00A0K'
    +    },
    +    '100000': {
    +      'other': '000\u00A0K'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mld.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mld.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mld.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0tril.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0tril.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0tril.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 de mii'
    +    },
    +    '10000': {
    +      'other': '00 de mii'
    +    },
    +    '100000': {
    +      'other': '000 de mii'
    +    },
    +    '1000000': {
    +      'other': '0 de milioane'
    +    },
    +    '10000000': {
    +      'other': '00 de milioane'
    +    },
    +    '100000000': {
    +      'other': '000 de milioane'
    +    },
    +    '1000000000': {
    +      'other': '0 de miliarde'
    +    },
    +    '10000000000': {
    +      'other': '00 de miliarde'
    +    },
    +    '100000000000': {
    +      'other': '000 de miliarde'
    +    },
    +    '1000000000000': {
    +      'other': '0 de trilioane'
    +    },
    +    '10000000000000': {
    +      'other': '00 de trilioane'
    +    },
    +    '100000000000000': {
    +      'other': '000 de trilioane'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rof.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rof = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rof_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rof_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_rof;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_BY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_BY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_KG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_KG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_KZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_KZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_MD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_MD = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ru_UA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ru_UA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0442\u044B\u0441.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0442\u044B\u0441.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0442\u044B\u0441.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '10000': {
    +      'other': '00 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '100000': {
    +      'other': '000 \u0442\u044B\u0441\u044F\u0447\u0438'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434\u0430'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rw.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rw = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rw_RW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rw_RW =
    +    goog.i18n.CompactNumberFormatSymbols_rw;
    +
    +
    +/**
    + * Compact number formatting symbols for locale rwk.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rwk = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale rwk_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_rwk_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_rwk;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sah.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sah = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sah_RU.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sah_RU =
    +    goog.i18n.CompactNumberFormatSymbols_sah;
    +
    +
    +/**
    + * Compact number formatting symbols for locale saq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_saq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale saq_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_saq_KE =
    +    goog.i18n.CompactNumberFormatSymbols_saq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sbp.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sbp = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sbp_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sbp_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_sbp;
    +
    +
    +/**
    + * Compact number formatting symbols for locale se.
    + */
    +goog.i18n.CompactNumberFormatSymbols_se = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0dt'
    +    },
    +    '10000': {
    +      'other': '00\u00A0dt'
    +    },
    +    '100000': {
    +      'other': '000\u00A0dt'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duh\u00E1hat'
    +    },
    +    '10000': {
    +      'other': '00 duh\u00E1hat'
    +    },
    +    '100000': {
    +      'other': '000 duh\u00E1hat'
    +    },
    +    '1000000': {
    +      'other': '0 miljonat'
    +    },
    +    '10000000': {
    +      'other': '00 miljonat'
    +    },
    +    '100000000': {
    +      'other': '000 miljonat'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardit'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardit'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardit'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljonat'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljonat'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljonat'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale se_FI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_se_FI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0dt'
    +    },
    +    '10000': {
    +      'other': '00\u00A0dt'
    +    },
    +    '100000': {
    +      'other': '000\u00A0dt'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duh\u00E1hat'
    +    },
    +    '10000': {
    +      'other': '00 duh\u00E1hat'
    +    },
    +    '100000': {
    +      'other': '000 duh\u00E1hat'
    +    },
    +    '1000000': {
    +      'other': '0 miljonat'
    +    },
    +    '10000000': {
    +      'other': '00 miljonat'
    +    },
    +    '100000000': {
    +      'other': '000 miljonat'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardit'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardit'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardit'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljonat'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljonat'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljonat'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale se_NO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_se_NO =
    +    goog.i18n.CompactNumberFormatSymbols_se;
    +
    +
    +/**
    + * Compact number formatting symbols for locale se_SE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_se_SE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0dt'
    +    },
    +    '10000': {
    +      'other': '00\u00A0dt'
    +    },
    +    '100000': {
    +      'other': '000\u00A0dt'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 duh\u00E1hat'
    +    },
    +    '10000': {
    +      'other': '00 duh\u00E1hat'
    +    },
    +    '100000': {
    +      'other': '000 duh\u00E1hat'
    +    },
    +    '1000000': {
    +      'other': '0 miljonat'
    +    },
    +    '10000000': {
    +      'other': '00 miljonat'
    +    },
    +    '100000000': {
    +      'other': '000 miljonat'
    +    },
    +    '1000000000': {
    +      'other': '0 miljardit'
    +    },
    +    '10000000000': {
    +      'other': '00 miljardit'
    +    },
    +    '100000000000': {
    +      'other': '000 miljardit'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljonat'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljonat'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljonat'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale seh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_seh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale seh_MZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_seh_MZ =
    +    goog.i18n.CompactNumberFormatSymbols_seh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ses.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ses = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ses_ML.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ses_ML =
    +    goog.i18n.CompactNumberFormatSymbols_ses;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sg.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sg = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sg_CF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sg_CF =
    +    goog.i18n.CompactNumberFormatSymbols_sg;
    +
    +
    +/**
    + * Compact number formatting symbols for locale shi.
    + */
    +goog.i18n.CompactNumberFormatSymbols_shi = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale shi_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_shi_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale shi_Latn_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_shi_Latn_MA =
    +    goog.i18n.CompactNumberFormatSymbols_shi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale shi_Tfng.
    + */
    +goog.i18n.CompactNumberFormatSymbols_shi_Tfng = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale shi_Tfng_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_shi_Tfng_MA =
    +    goog.i18n.CompactNumberFormatSymbols_shi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale smn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_smn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tuhh\u00E1\u00E1t'
    +    },
    +    '10000': {
    +      'other': '00 tuhh\u00E1\u00E1t'
    +    },
    +    '100000': {
    +      'other': '000 tuhh\u00E1\u00E1t'
    +    },
    +    '1000000': {
    +      'other': '0 miljovn'
    +    },
    +    '10000000': {
    +      'other': '00 miljovn'
    +    },
    +    '100000000': {
    +      'other': '000 miljovn'
    +    },
    +    '1000000000': {
    +      'other': '0 miljard'
    +    },
    +    '10000000000': {
    +      'other': '00 miljard'
    +    },
    +    '100000000000': {
    +      'other': '000 miljard'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljovn'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljovn'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljovn'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale smn_FI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_smn_FI =
    +    goog.i18n.CompactNumberFormatSymbols_smn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sn_ZW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sn_ZW =
    +    goog.i18n.CompactNumberFormatSymbols_sn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale so.
    + */
    +goog.i18n.CompactNumberFormatSymbols_so = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale so_DJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_so_DJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale so_ET.
    + */
    +goog.i18n.CompactNumberFormatSymbols_so_ET = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale so_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_so_KE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale so_SO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_so_SO =
    +    goog.i18n.CompactNumberFormatSymbols_so;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sq_MK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sq_MK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00 mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000 mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0 milion'
    +    },
    +    '10000000': {
    +      'other': '00 milion'
    +    },
    +    '100000000': {
    +      'other': '000 milion'
    +    },
    +    '1000000000': {
    +      'other': '0 miliard'
    +    },
    +    '10000000000': {
    +      'other': '00 miliard'
    +    },
    +    '100000000000': {
    +      'other': '000 miliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sq_XK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sq_XK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00\u00A0mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000\u00A0mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mln'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mln'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mln'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mld'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mld'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mld'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Bln'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Bln'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Bln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 mij\u00EB'
    +    },
    +    '10000': {
    +      'other': '00 mij\u00EB'
    +    },
    +    '100000': {
    +      'other': '000 mij\u00EB'
    +    },
    +    '1000000': {
    +      'other': '0 milion'
    +    },
    +    '10000000': {
    +      'other': '00 milion'
    +    },
    +    '100000000': {
    +      'other': '000 milion'
    +    },
    +    '1000000000': {
    +      'other': '0 miliard'
    +    },
    +    '10000000000': {
    +      'other': '00 miliard'
    +    },
    +    '100000000000': {
    +      'other': '000 miliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 bilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 bilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 bilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0445\u0438\u0459.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u0459.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u0459.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0431\u0438\u043B.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0431\u0438\u043B.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0431\u0438\u043B.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Cyrl_BA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_BA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u0445\u0438\u0459.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u0445\u0438\u0459.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0\u0445\u0438\u0459.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0\u043C\u0438\u043B.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0\u043C\u0438\u043B.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0\u043C\u0438\u043B.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0\u043C\u043B\u0440\u0434.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0\u0431\u0438\u043B.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0431\u0438\u043B.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0431\u0438\u043B.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '10000': {
    +      'other': '00 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '100000': {
    +      'other': '000 \u0445\u0438\u0459\u0430\u0434\u0430'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u0438\u0458\u0430\u0440\u0434\u0438'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D\u0430'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Cyrl_ME.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_ME =
    +    goog.i18n.CompactNumberFormatSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Cyrl_XK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_XK =
    +    goog.i18n.CompactNumberFormatSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Latn_BA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Latn_BA = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Latn_ME.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Latn_ME = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Latn_RS.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Latn_RS =
    +    goog.i18n.CompactNumberFormatSymbols_sr_Latn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sr_Latn_XK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sr_Latn_XK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0hilj.'
    +    },
    +    '10000': {
    +      'other': '00\u00A0hilj.'
    +    },
    +    '100000': {
    +      'other': '000\u00A0hilj.'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mil.'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mil.'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mil.'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0mlrd.'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0mlrd.'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0mlrd.'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bil.'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bil.'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bil.'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 hiljada'
    +    },
    +    '10000': {
    +      'other': '00 hiljada'
    +    },
    +    '100000': {
    +      'other': '000 hiljada'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 milijardi'
    +    },
    +    '10000000000': {
    +      'other': '00 milijardi'
    +    },
    +    '100000000000': {
    +      'other': '000 milijardi'
    +    },
    +    '1000000000000': {
    +      'other': '0 triliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 triliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 triliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ss.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ss = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ss_SZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ss_SZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ss_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ss_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_ss;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ssy.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ssy = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ssy_ER.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ssy_ER =
    +    goog.i18n.CompactNumberFormatSymbols_ssy;
    +
    +
    +/**
    + * Compact number formatting symbols for locale sv_AX.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sv_AX = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tn'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tn'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tn'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 miljoner'
    +    },
    +    '10000000': {
    +      'other': '00 miljoner'
    +    },
    +    '100000000': {
    +      'other': '000 miljoner'
    +    },
    +    '1000000000': {
    +      'other': '0 miljarder'
    +    },
    +    '10000000000': {
    +      'other': '00 miljarder'
    +    },
    +    '100000000000': {
    +      'other': '000 miljarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoner'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoner'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sv_FI.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sv_FI = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0tn'
    +    },
    +    '10000': {
    +      'other': '00\u00A0tn'
    +    },
    +    '100000': {
    +      'other': '000\u00A0tn'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0md'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0md'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0md'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0bn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0bn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0bn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 tusen'
    +    },
    +    '10000': {
    +      'other': '00 tusen'
    +    },
    +    '100000': {
    +      'other': '000 tusen'
    +    },
    +    '1000000': {
    +      'other': '0 miljoner'
    +    },
    +    '10000000': {
    +      'other': '00 miljoner'
    +    },
    +    '100000000': {
    +      'other': '000 miljoner'
    +    },
    +    '1000000000': {
    +      'other': '0 miljarder'
    +    },
    +    '10000000000': {
    +      'other': '00 miljarder'
    +    },
    +    '100000000000': {
    +      'other': '000 miljarder'
    +    },
    +    '1000000000000': {
    +      'other': '0 biljoner'
    +    },
    +    '10000000000000': {
    +      'other': '00 biljoner'
    +    },
    +    '100000000000000': {
    +      'other': '000 biljoner'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sw_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sw_KE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': 'elfu\u00A00'
    +    },
    +    '10000': {
    +      'other': 'elfu\u00A000'
    +    },
    +    '100000': {
    +      'other': 'elfu\u00A0000'
    +    },
    +    '1000000': {
    +      'other': 'M0'
    +    },
    +    '10000000': {
    +      'other': 'M00'
    +    },
    +    '100000000': {
    +      'other': 'M000'
    +    },
    +    '1000000000': {
    +      'other': 'B0'
    +    },
    +    '10000000000': {
    +      'other': 'B00'
    +    },
    +    '100000000000': {
    +      'other': 'B000'
    +    },
    +    '1000000000000': {
    +      'other': 'T0'
    +    },
    +    '10000000000000': {
    +      'other': 'T00'
    +    },
    +    '100000000000000': {
    +      'other': 'T000'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': 'Elfu 0'
    +    },
    +    '10000': {
    +      'other': 'Elfu 00'
    +    },
    +    '100000': {
    +      'other': 'Elfu 000'
    +    },
    +    '1000000': {
    +      'other': 'Milioni 0'
    +    },
    +    '10000000': {
    +      'other': 'Milioni 00'
    +    },
    +    '100000000': {
    +      'other': 'Milioni 000'
    +    },
    +    '1000000000': {
    +      'other': 'Bilioni 0'
    +    },
    +    '10000000000': {
    +      'other': 'Bilioni 00'
    +    },
    +    '100000000000': {
    +      'other': 'Bilioni 000'
    +    },
    +    '1000000000000': {
    +      'other': 'Trilioni 0'
    +    },
    +    '10000000000000': {
    +      'other': 'Trilioni 00'
    +    },
    +    '100000000000000': {
    +      'other': 'Trilioni 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale sw_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_sw_UG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': 'elfu\u00A00'
    +    },
    +    '10000': {
    +      'other': 'elfu\u00A000'
    +    },
    +    '100000': {
    +      'other': 'elfu\u00A0000'
    +    },
    +    '1000000': {
    +      'other': 'M0'
    +    },
    +    '10000000': {
    +      'other': 'M00'
    +    },
    +    '100000000': {
    +      'other': 'M000'
    +    },
    +    '1000000000': {
    +      'other': 'B0'
    +    },
    +    '10000000000': {
    +      'other': 'B00'
    +    },
    +    '100000000000': {
    +      'other': 'B000'
    +    },
    +    '1000000000000': {
    +      'other': 'T0'
    +    },
    +    '10000000000000': {
    +      'other': 'T00'
    +    },
    +    '100000000000000': {
    +      'other': 'T000'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': 'Elfu 0'
    +    },
    +    '10000': {
    +      'other': 'Elfu 00'
    +    },
    +    '100000': {
    +      'other': 'Elfu 000'
    +    },
    +    '1000000': {
    +      'other': 'Milioni 0'
    +    },
    +    '10000000': {
    +      'other': 'Milioni 00'
    +    },
    +    '100000000': {
    +      'other': 'Milioni 000'
    +    },
    +    '1000000000': {
    +      'other': 'Bilioni 0'
    +    },
    +    '10000000000': {
    +      'other': 'Bilioni 00'
    +    },
    +    '100000000000': {
    +      'other': 'Bilioni 000'
    +    },
    +    '1000000000000': {
    +      'other': 'Trilioni 0'
    +    },
    +    '10000000000000': {
    +      'other': 'Trilioni 00'
    +    },
    +    '100000000000000': {
    +      'other': 'Trilioni 000'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale swc.
    + */
    +goog.i18n.CompactNumberFormatSymbols_swc = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale swc_CD.
    + */
    +goog.i18n.CompactNumberFormatSymbols_swc_CD =
    +    goog.i18n.CompactNumberFormatSymbols_swc;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ta_LK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ta_LK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0B86'
    +    },
    +    '10000': {
    +      'other': '00\u0B86'
    +    },
    +    '100000': {
    +      'other': '000\u0B86'
    +    },
    +    '1000000': {
    +      'other': '0\u0BAE\u0BBF'
    +    },
    +    '10000000': {
    +      'other': '00\u0BAE\u0BBF'
    +    },
    +    '100000000': {
    +      'other': '000\u0BAE\u0BBF'
    +    },
    +    '1000000000': {
    +      'other': '0\u0BAA\u0BBF'
    +    },
    +    '10000000000': {
    +      'other': '00\u0BAA\u0BBF'
    +    },
    +    '100000000000': {
    +      'other': '000\u0BAA\u0BBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0B9F\u0BBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0B9F\u0BBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0B9F\u0BBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '10000': {
    +      'other': '00 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '100000': {
    +      'other': '000 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000': {
    +      'other': '00 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000': {
    +      'other': '000 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ta_MY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ta_MY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0B86'
    +    },
    +    '10000': {
    +      'other': '00\u0B86'
    +    },
    +    '100000': {
    +      'other': '000\u0B86'
    +    },
    +    '1000000': {
    +      'other': '0\u0BAE\u0BBF'
    +    },
    +    '10000000': {
    +      'other': '00\u0BAE\u0BBF'
    +    },
    +    '100000000': {
    +      'other': '000\u0BAE\u0BBF'
    +    },
    +    '1000000000': {
    +      'other': '0\u0BAA\u0BBF'
    +    },
    +    '10000000000': {
    +      'other': '00\u0BAA\u0BBF'
    +    },
    +    '100000000000': {
    +      'other': '000\u0BAA\u0BBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0B9F\u0BBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0B9F\u0BBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0B9F\u0BBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '10000': {
    +      'other': '00 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '100000': {
    +      'other': '000 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000': {
    +      'other': '00 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000': {
    +      'other': '000 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ta_SG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ta_SG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0B86'
    +    },
    +    '10000': {
    +      'other': '00\u0B86'
    +    },
    +    '100000': {
    +      'other': '000\u0B86'
    +    },
    +    '1000000': {
    +      'other': '0\u0BAE\u0BBF'
    +    },
    +    '10000000': {
    +      'other': '00\u0BAE\u0BBF'
    +    },
    +    '100000000': {
    +      'other': '000\u0BAE\u0BBF'
    +    },
    +    '1000000000': {
    +      'other': '0\u0BAA\u0BBF'
    +    },
    +    '10000000000': {
    +      'other': '00\u0BAA\u0BBF'
    +    },
    +    '100000000000': {
    +      'other': '000\u0BAA\u0BBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0B9F\u0BBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0B9F\u0BBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0B9F\u0BBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '10000': {
    +      'other': '00 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '100000': {
    +      'other': '000 \u0B86\u0BAF\u0BBF\u0BB0\u0BAE\u0BCD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000': {
    +      'other': '00 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000': {
    +      'other': '000 \u0BAE\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0BAA\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0B9F\u0BBF\u0BB0\u0BBF\u0BB2\u0BCD\u0BB2\u0BBF\u0BAF\u0BA9\u0BCD'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale teo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_teo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale teo_KE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_teo_KE = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale teo_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_teo_UG =
    +    goog.i18n.CompactNumberFormatSymbols_teo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ti.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ti = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ti_ER.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ti_ER = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ti_ET.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ti_ET =
    +    goog.i18n.CompactNumberFormatSymbols_ti;
    +
    +
    +/**
    + * Compact number formatting symbols for locale tn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tn_BW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tn_BW = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tn_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tn_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_tn;
    +
    +
    +/**
    + * Compact number formatting symbols for locale to.
    + */
    +goog.i18n.CompactNumberFormatSymbols_to = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0k'
    +    },
    +    '10000': {
    +      'other': '00k'
    +    },
    +    '100000': {
    +      'other': '000k'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0Ki'
    +    },
    +    '10000000000': {
    +      'other': '00Ki'
    +    },
    +    '100000000000': {
    +      'other': '000Ki'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 afe'
    +    },
    +    '10000': {
    +      'other': '0 mano'
    +    },
    +    '100000': {
    +      'other': '0 kilu'
    +    },
    +    '1000000': {
    +      'other': '0 miliona'
    +    },
    +    '10000000': {
    +      'other': '00 miliona'
    +    },
    +    '100000000': {
    +      'other': '000 miliona'
    +    },
    +    '1000000000': {
    +      'other': '0 piliona'
    +    },
    +    '10000000000': {
    +      'other': '00 piliona'
    +    },
    +    '100000000000': {
    +      'other': '000 piliona'
    +    },
    +    '1000000000000': {
    +      'other': '0 tiliona'
    +    },
    +    '10000000000000': {
    +      'other': '00 tiliona'
    +    },
    +    '100000000000000': {
    +      'other': '000 tiliona'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale to_TO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_to_TO =
    +    goog.i18n.CompactNumberFormatSymbols_to;
    +
    +
    +/**
    + * Compact number formatting symbols for locale tr_CY.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tr_CY = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0'
    +    },
    +    '10000': {
    +      'other': '00\u00A0B'
    +    },
    +    '100000': {
    +      'other': '000\u00A0B'
    +    },
    +    '1000000': {
    +      'other': '0\u00A0Mn'
    +    },
    +    '10000000': {
    +      'other': '00\u00A0Mn'
    +    },
    +    '100000000': {
    +      'other': '000\u00A0Mn'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0Mr'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0Mr'
    +    },
    +    '100000000000': {
    +      'other': '000\u00A0Mr'
    +    },
    +    '1000000000000': {
    +      'other': '0\u00A0Tn'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0Tn'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0Tn'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 bin'
    +    },
    +    '10000': {
    +      'other': '00 bin'
    +    },
    +    '100000': {
    +      'other': '000 bin'
    +    },
    +    '1000000': {
    +      'other': '0 milyon'
    +    },
    +    '10000000': {
    +      'other': '00 milyon'
    +    },
    +    '100000000': {
    +      'other': '000 milyon'
    +    },
    +    '1000000000': {
    +      'other': '0 milyar'
    +    },
    +    '10000000000': {
    +      'other': '00 milyar'
    +    },
    +    '100000000000': {
    +      'other': '000 milyar'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilyon'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilyon'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilyon'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ts.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ts = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ts_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ts_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_ts;
    +
    +
    +/**
    + * Compact number formatting symbols for locale twq.
    + */
    +goog.i18n.CompactNumberFormatSymbols_twq = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale twq_NE.
    + */
    +goog.i18n.CompactNumberFormatSymbols_twq_NE =
    +    goog.i18n.CompactNumberFormatSymbols_twq;
    +
    +
    +/**
    + * Compact number formatting symbols for locale tzm.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tzm = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tzm_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tzm_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale tzm_Latn_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_tzm_Latn_MA =
    +    goog.i18n.CompactNumberFormatSymbols_tzm;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ug.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ug = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0645\u0649\u06AD'
    +    },
    +    '10000': {
    +      'other': '00\u0645\u0649\u06AD'
    +    },
    +    '100000': {
    +      'other': '000\u0645\u0649\u06AD'
    +    },
    +    '1000000': {
    +      'other': '0\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0645\u0649\u06AD'
    +    },
    +    '10000': {
    +      'other': '00 \u0645\u0649\u06AD'
    +    },
    +    '100000': {
    +      'other': '000 \u0645\u0649\u06AD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ug_Arab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ug_Arab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u0645\u0649\u06AD'
    +    },
    +    '10000': {
    +      'other': '00\u0645\u0649\u06AD'
    +    },
    +    '100000': {
    +      'other': '000\u0645\u0649\u06AD'
    +    },
    +    '1000000': {
    +      'other': '0\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000\u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000\u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u0645\u0649\u06AD'
    +    },
    +    '10000': {
    +      'other': '00 \u0645\u0649\u06AD'
    +    },
    +    '100000': {
    +      'other': '000 \u0645\u0649\u06AD'
    +    },
    +    '1000000': {
    +      'other': '0 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000': {
    +      'other': '00 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000': {
    +      'other': '000 \u0645\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '100000000000': {
    +      'other': '000 \u0645\u0649\u0644\u064A\u0627\u0631\u062F'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u062A\u0649\u0631\u0649\u0644\u064A\u0648\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ug_Arab_CN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ug_Arab_CN =
    +    goog.i18n.CompactNumberFormatSymbols_ug;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ur_IN.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ur_IN = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u00A0\u06C1\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00\u00A0\u06C1\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '0\u00A0\u0644\u0627\u06A9\u06BE'
    +    },
    +    '1000000': {
    +      'other': '00\u00A0\u0644\u0627\u06A9\u06BE'
    +    },
    +    '10000000': {
    +      'other': '0\u00A0\u06A9\u0631\u0648\u0691'
    +    },
    +    '100000000': {
    +      'other': '00\u00A0\u06A9\u0631\u0648\u0691'
    +    },
    +    '1000000000': {
    +      'other': '0\u00A0\u0627\u0631\u0628'
    +    },
    +    '10000000000': {
    +      'other': '00\u00A0\u0627\u0631\u0628'
    +    },
    +    '100000000000': {
    +      'other': '0\u00A0\u06A9\u06BE\u0631\u0628'
    +    },
    +    '1000000000000': {
    +      'other': '00\u00A0\u06A9\u06BE\u0631\u0628'
    +    },
    +    '10000000000000': {
    +      'other': '00\u00A0\u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000\u00A0\u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u06C1\u0632\u0627\u0631'
    +    },
    +    '10000': {
    +      'other': '00 \u06C1\u0632\u0627\u0631'
    +    },
    +    '100000': {
    +      'other': '0 \u0644\u0627\u06A9\u06BE'
    +    },
    +    '1000000': {
    +      'other': '00 \u0644\u0627\u06A9\u06BE'
    +    },
    +    '10000000': {
    +      'other': '0 \u06A9\u0631\u0648\u0691'
    +    },
    +    '100000000': {
    +      'other': '00 \u06A9\u0631\u0648\u0691'
    +    },
    +    '1000000000': {
    +      'other': '0 \u0627\u0631\u0628'
    +    },
    +    '10000000000': {
    +      'other': '00 \u0627\u0631\u0628'
    +    },
    +    '100000000000': {
    +      'other': '0 \u06A9\u06BE\u0631\u0628'
    +    },
    +    '1000000000000': {
    +      'other': '00 \u06A9\u06BE\u0631\u0628'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0679\u0631\u06CC\u0644\u06CC\u0646'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Arab.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Arab = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Arab_AF.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Arab_AF =
    +    goog.i18n.CompactNumberFormatSymbols_uz_Arab;
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Cyrl.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Cyrl = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u043C\u0438\u043D\u0433'
    +    },
    +    '10000': {
    +      'other': '00\u043C\u0438\u043D\u0433'
    +    },
    +    '100000': {
    +      'other': '000\u043C\u0438\u043D\u0433'
    +    },
    +    '1000000': {
    +      'other': '0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u0438\u043D\u0433'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u0438\u043D\u0433'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u0438\u043D\u0433'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Cyrl_UZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Cyrl_UZ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u043C\u0438\u043D\u0433'
    +    },
    +    '10000': {
    +      'other': '00\u043C\u0438\u043D\u0433'
    +    },
    +    '100000': {
    +      'other': '000\u043C\u0438\u043D\u0433'
    +    },
    +    '1000000': {
    +      'other': '0\u043C\u043B\u043D'
    +    },
    +    '10000000': {
    +      'other': '00\u043C\u043B\u043D'
    +    },
    +    '100000000': {
    +      'other': '000\u043C\u043B\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0\u043C\u043B\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00\u043C\u043B\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000\u043C\u043B\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0\u0442\u0440\u043B\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00\u0442\u0440\u043B\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000\u0442\u0440\u043B\u043D'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u043C\u0438\u043D\u0433'
    +    },
    +    '10000': {
    +      'other': '00 \u043C\u0438\u043D\u0433'
    +    },
    +    '100000': {
    +      'other': '000 \u043C\u0438\u043D\u0433'
    +    },
    +    '1000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u043E\u043D'
    +    },
    +    '1000000000': {
    +      'other': '0 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '10000000000': {
    +      'other': '00 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '100000000000': {
    +      'other': '000 \u043C\u0438\u043B\u043B\u0438\u0430\u0440\u0434'
    +    },
    +    '1000000000000': {
    +      'other': '0 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    },
    +    '10000000000000': {
    +      'other': '00 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    },
    +    '100000000000000': {
    +      'other': '000 \u0442\u0440\u0438\u043B\u0438\u043E\u043D'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale uz_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_uz_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0ming'
    +    },
    +    '10000': {
    +      'other': '00ming'
    +    },
    +    '100000': {
    +      'other': '000ming'
    +    },
    +    '1000000': {
    +      'other': '0mln'
    +    },
    +    '10000000': {
    +      'other': '00mln'
    +    },
    +    '100000000': {
    +      'other': '000mln'
    +    },
    +    '1000000000': {
    +      'other': '0mlrd'
    +    },
    +    '10000000000': {
    +      'other': '00mlrd'
    +    },
    +    '100000000000': {
    +      'other': '000mlrd'
    +    },
    +    '1000000000000': {
    +      'other': '0trln'
    +    },
    +    '10000000000000': {
    +      'other': '00trln'
    +    },
    +    '100000000000000': {
    +      'other': '000trln'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 ming'
    +    },
    +    '10000': {
    +      'other': '00 ming'
    +    },
    +    '100000': {
    +      'other': '000 ming'
    +    },
    +    '1000000': {
    +      'other': '0 million'
    +    },
    +    '10000000': {
    +      'other': '00 million'
    +    },
    +    '100000000': {
    +      'other': '000 million'
    +    },
    +    '1000000000': {
    +      'other': '0 milliard'
    +    },
    +    '10000000000': {
    +      'other': '00 milliard'
    +    },
    +    '100000000000': {
    +      'other': '000 milliard'
    +    },
    +    '1000000000000': {
    +      'other': '0 trilion'
    +    },
    +    '10000000000000': {
    +      'other': '00 trilion'
    +    },
    +    '100000000000000': {
    +      'other': '000 trilion'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vai.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vai = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vai_Latn.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vai_Latn = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vai_Latn_LR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vai_Latn_LR =
    +    goog.i18n.CompactNumberFormatSymbols_vai;
    +
    +
    +/**
    + * Compact number formatting symbols for locale vai_Vaii.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vai_Vaii = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vai_Vaii_LR.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vai_Vaii_LR =
    +    goog.i18n.CompactNumberFormatSymbols_vai;
    +
    +
    +/**
    + * Compact number formatting symbols for locale ve.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ve = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale ve_ZA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_ve_ZA =
    +    goog.i18n.CompactNumberFormatSymbols_ve;
    +
    +
    +/**
    + * Compact number formatting symbols for locale vo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vo_001.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vo_001 =
    +    goog.i18n.CompactNumberFormatSymbols_vo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale vun.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vun = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale vun_TZ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_vun_TZ =
    +    goog.i18n.CompactNumberFormatSymbols_vun;
    +
    +
    +/**
    + * Compact number formatting symbols for locale wae.
    + */
    +goog.i18n.CompactNumberFormatSymbols_wae = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale wae_CH.
    + */
    +goog.i18n.CompactNumberFormatSymbols_wae_CH =
    +    goog.i18n.CompactNumberFormatSymbols_wae;
    +
    +
    +/**
    + * Compact number formatting symbols for locale xog.
    + */
    +goog.i18n.CompactNumberFormatSymbols_xog = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale xog_UG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_xog_UG =
    +    goog.i18n.CompactNumberFormatSymbols_xog;
    +
    +
    +/**
    + * Compact number formatting symbols for locale yav.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yav = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale yav_CM.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yav_CM =
    +    goog.i18n.CompactNumberFormatSymbols_yav;
    +
    +
    +/**
    + * Compact number formatting symbols for locale yi.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yi = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale yi_001.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yi_001 =
    +    goog.i18n.CompactNumberFormatSymbols_yi;
    +
    +
    +/**
    + * Compact number formatting symbols for locale yo.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yo = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale yo_BJ.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yo_BJ = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale yo_NG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_yo_NG =
    +    goog.i18n.CompactNumberFormatSymbols_yo;
    +
    +
    +/**
    + * Compact number formatting symbols for locale zgh.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zgh = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0G'
    +    },
    +    '10000000000': {
    +      'other': '00G'
    +    },
    +    '100000000000': {
    +      'other': '000G'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zgh_MA.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zgh_MA =
    +    goog.i18n.CompactNumberFormatSymbols_zgh;
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hans.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hans = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hans_HK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hans_HK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u4E07\u4EBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u4E07\u4EBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u4E07\u4EBF'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u4E07\u4EBF'
    +    },
    +    '10000000000000': {
    +      'other': '00\u4E07\u4EBF'
    +    },
    +    '100000000000000': {
    +      'other': '000\u4E07\u4EBF'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hans_MO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hans_MO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hans_SG.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hans_SG = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u4E07'
    +    },
    +    '100000': {
    +      'other': '00\u4E07'
    +    },
    +    '1000000': {
    +      'other': '000\u4E07'
    +    },
    +    '10000000': {
    +      'other': '0000\u4E07'
    +    },
    +    '100000000': {
    +      'other': '0\u4EBF'
    +    },
    +    '1000000000': {
    +      'other': '00\u4EBF'
    +    },
    +    '10000000000': {
    +      'other': '000\u4EBF'
    +    },
    +    '100000000000': {
    +      'other': '0000\u4EBF'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hant.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hant = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0 \u5343'
    +    },
    +    '10000': {
    +      'other': '0\u842C'
    +    },
    +    '100000': {
    +      'other': '00\u842C'
    +    },
    +    '1000000': {
    +      'other': '000\u842C'
    +    },
    +    '10000000': {
    +      'other': '0000\u842C'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hant_HK.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hant_HK = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u842C'
    +    },
    +    '100000': {
    +      'other': '00\u842C'
    +    },
    +    '1000000': {
    +      'other': '000\u842C'
    +    },
    +    '10000000': {
    +      'other': '0000\u842C'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hant_MO.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hant_MO = {
    +  COMPACT_DECIMAL_SHORT_PATTERN: {
    +    '1000': {
    +      'other': '0K'
    +    },
    +    '10000': {
    +      'other': '00K'
    +    },
    +    '100000': {
    +      'other': '000K'
    +    },
    +    '1000000': {
    +      'other': '0M'
    +    },
    +    '10000000': {
    +      'other': '00M'
    +    },
    +    '100000000': {
    +      'other': '000M'
    +    },
    +    '1000000000': {
    +      'other': '0B'
    +    },
    +    '10000000000': {
    +      'other': '00B'
    +    },
    +    '100000000000': {
    +      'other': '000B'
    +    },
    +    '1000000000000': {
    +      'other': '0T'
    +    },
    +    '10000000000000': {
    +      'other': '00T'
    +    },
    +    '100000000000000': {
    +      'other': '000T'
    +    }
    +  },
    +  COMPACT_DECIMAL_LONG_PATTERN: {
    +    '1000': {
    +      'other': '0\u5343'
    +    },
    +    '10000': {
    +      'other': '0\u842C'
    +    },
    +    '100000': {
    +      'other': '00\u842C'
    +    },
    +    '1000000': {
    +      'other': '000\u842C'
    +    },
    +    '10000000': {
    +      'other': '0000\u842C'
    +    },
    +    '100000000': {
    +      'other': '0\u5104'
    +    },
    +    '1000000000': {
    +      'other': '00\u5104'
    +    },
    +    '10000000000': {
    +      'other': '000\u5104'
    +    },
    +    '100000000000': {
    +      'other': '0000\u5104'
    +    },
    +    '1000000000000': {
    +      'other': '0\u5146'
    +    },
    +    '10000000000000': {
    +      'other': '00\u5146'
    +    },
    +    '100000000000000': {
    +      'other': '000\u5146'
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compact number formatting symbols for locale zh_Hant_TW.
    + */
    +goog.i18n.CompactNumberFormatSymbols_zh_Hant_TW =
    +    goog.i18n.CompactNumberFormatSymbols_zh_Hant;
    +
    +
    +/**
    + * Selected compact number formatting symbols by locale.
    + */
    +goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en;
    +
    +if (goog.LOCALE == 'aa') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'aa_DJ' || goog.LOCALE == 'aa-DJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_aa_DJ;
    +}
    +
    +if (goog.LOCALE == 'aa_ER' || goog.LOCALE == 'aa-ER') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_aa_ER;
    +}
    +
    +if (goog.LOCALE == 'aa_ET' || goog.LOCALE == 'aa-ET') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'af_NA' || goog.LOCALE == 'af-NA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_af_NA;
    +}
    +
    +if (goog.LOCALE == 'agq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'agq_CM' || goog.LOCALE == 'agq-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'ak') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ak_GH' || goog.LOCALE == 'ak-GH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ar_AE' || goog.LOCALE == 'ar-AE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_AE;
    +}
    +
    +if (goog.LOCALE == 'ar_BH' || goog.LOCALE == 'ar-BH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_BH;
    +}
    +
    +if (goog.LOCALE == 'ar_DJ' || goog.LOCALE == 'ar-DJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_DJ;
    +}
    +
    +if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_DZ;
    +}
    +
    +if (goog.LOCALE == 'ar_EG' || goog.LOCALE == 'ar-EG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_EG;
    +}
    +
    +if (goog.LOCALE == 'ar_EH' || goog.LOCALE == 'ar-EH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_EH;
    +}
    +
    +if (goog.LOCALE == 'ar_ER' || goog.LOCALE == 'ar-ER') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_ER;
    +}
    +
    +if (goog.LOCALE == 'ar_IL' || goog.LOCALE == 'ar-IL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_IL;
    +}
    +
    +if (goog.LOCALE == 'ar_IQ' || goog.LOCALE == 'ar-IQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_IQ;
    +}
    +
    +if (goog.LOCALE == 'ar_JO' || goog.LOCALE == 'ar-JO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_JO;
    +}
    +
    +if (goog.LOCALE == 'ar_KM' || goog.LOCALE == 'ar-KM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_KM;
    +}
    +
    +if (goog.LOCALE == 'ar_KW' || goog.LOCALE == 'ar-KW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_KW;
    +}
    +
    +if (goog.LOCALE == 'ar_LB' || goog.LOCALE == 'ar-LB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_LB;
    +}
    +
    +if (goog.LOCALE == 'ar_LY' || goog.LOCALE == 'ar-LY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_LY;
    +}
    +
    +if (goog.LOCALE == 'ar_MA' || goog.LOCALE == 'ar-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_MA;
    +}
    +
    +if (goog.LOCALE == 'ar_MR' || goog.LOCALE == 'ar-MR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_MR;
    +}
    +
    +if (goog.LOCALE == 'ar_OM' || goog.LOCALE == 'ar-OM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_OM;
    +}
    +
    +if (goog.LOCALE == 'ar_PS' || goog.LOCALE == 'ar-PS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_PS;
    +}
    +
    +if (goog.LOCALE == 'ar_QA' || goog.LOCALE == 'ar-QA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_QA;
    +}
    +
    +if (goog.LOCALE == 'ar_SA' || goog.LOCALE == 'ar-SA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_SA;
    +}
    +
    +if (goog.LOCALE == 'ar_SD' || goog.LOCALE == 'ar-SD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_SD;
    +}
    +
    +if (goog.LOCALE == 'ar_SO' || goog.LOCALE == 'ar-SO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_SO;
    +}
    +
    +if (goog.LOCALE == 'ar_SS' || goog.LOCALE == 'ar-SS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_SS;
    +}
    +
    +if (goog.LOCALE == 'ar_SY' || goog.LOCALE == 'ar-SY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_SY;
    +}
    +
    +if (goog.LOCALE == 'ar_TD' || goog.LOCALE == 'ar-TD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_TD;
    +}
    +
    +if (goog.LOCALE == 'ar_TN' || goog.LOCALE == 'ar-TN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_TN;
    +}
    +
    +if (goog.LOCALE == 'ar_YE' || goog.LOCALE == 'ar-YE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ar_YE;
    +}
    +
    +if (goog.LOCALE == 'as') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'as_IN' || goog.LOCALE == 'as-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'asa') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'asa_TZ' || goog.LOCALE == 'asa-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'ast') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'ast_ES' || goog.LOCALE == 'ast-ES') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl' || goog.LOCALE == 'az-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_az_Cyrl_AZ;
    +}
    +
    +if (goog.LOCALE == 'az_Latn' || goog.LOCALE == 'az-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'bas') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'bas_CM' || goog.LOCALE == 'bas-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'be') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'be_BY' || goog.LOCALE == 'be-BY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'bem') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bem_ZM' || goog.LOCALE == 'bem-ZM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bez') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bez_TZ' || goog.LOCALE == 'bez-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bm') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn' || goog.LOCALE == 'bm-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bm_Latn;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn_ML' || goog.LOCALE == 'bm-Latn-ML') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bn_IN' || goog.LOCALE == 'bn-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bn_IN;
    +}
    +
    +if (goog.LOCALE == 'bo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_CN' || goog.LOCALE == 'bo-CN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_IN' || goog.LOCALE == 'bo-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bo_IN;
    +}
    +
    +if (goog.LOCALE == 'brx') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'brx_IN' || goog.LOCALE == 'brx-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'bs') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl' || goog.LOCALE == 'bs-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl_BA' || goog.LOCALE == 'bs-Cyrl-BA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bs_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn' || goog.LOCALE == 'bs-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bs_Latn;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn_BA' || goog.LOCALE == 'bs-Latn-BA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'cgg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'cgg_UG' || goog.LOCALE == 'cgg-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'ckb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab' || goog.LOCALE == 'ckb-Arab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb_Arab;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IQ' || goog.LOCALE == 'ckb-Arab-IQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IR' || goog.LOCALE == 'ckb-Arab-IR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb_Arab_IR;
    +}
    +
    +if (goog.LOCALE == 'ckb_IQ' || goog.LOCALE == 'ckb-IQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_IR' || goog.LOCALE == 'ckb-IR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb_IR;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn' || goog.LOCALE == 'ckb-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb_Latn;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn_IQ' || goog.LOCALE == 'ckb-Latn-IQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'dav') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'dav_KE' || goog.LOCALE == 'dav-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'de_LI' || goog.LOCALE == 'de-LI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_de_LI;
    +}
    +
    +if (goog.LOCALE == 'dje') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dje_NE' || goog.LOCALE == 'dje-NE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dsb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dsb_DE' || goog.LOCALE == 'dsb-DE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dua') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dua_CM' || goog.LOCALE == 'dua-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dyo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dyo_SN' || goog.LOCALE == 'dyo-SN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dz') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'dz_BT' || goog.LOCALE == 'dz-BT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'ebu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ebu_KE' || goog.LOCALE == 'ebu-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ee') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_GH' || goog.LOCALE == 'ee-GH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_TG' || goog.LOCALE == 'ee-TG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ee_TG;
    +}
    +
    +if (goog.LOCALE == 'el_CY' || goog.LOCALE == 'el-CY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_el_CY;
    +}
    +
    +if (goog.LOCALE == 'en_150' || goog.LOCALE == 'en-150') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_150;
    +}
    +
    +if (goog.LOCALE == 'en_AG' || goog.LOCALE == 'en-AG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_AG;
    +}
    +
    +if (goog.LOCALE == 'en_AI' || goog.LOCALE == 'en-AI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_AI;
    +}
    +
    +if (goog.LOCALE == 'en_BB' || goog.LOCALE == 'en-BB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BB;
    +}
    +
    +if (goog.LOCALE == 'en_BE' || goog.LOCALE == 'en-BE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BE;
    +}
    +
    +if (goog.LOCALE == 'en_BM' || goog.LOCALE == 'en-BM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BM;
    +}
    +
    +if (goog.LOCALE == 'en_BS' || goog.LOCALE == 'en-BS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BS;
    +}
    +
    +if (goog.LOCALE == 'en_BW' || goog.LOCALE == 'en-BW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BW;
    +}
    +
    +if (goog.LOCALE == 'en_BZ' || goog.LOCALE == 'en-BZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_BZ;
    +}
    +
    +if (goog.LOCALE == 'en_CA' || goog.LOCALE == 'en-CA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_CA;
    +}
    +
    +if (goog.LOCALE == 'en_CC' || goog.LOCALE == 'en-CC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_CC;
    +}
    +
    +if (goog.LOCALE == 'en_CK' || goog.LOCALE == 'en-CK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_CK;
    +}
    +
    +if (goog.LOCALE == 'en_CM' || goog.LOCALE == 'en-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_CM;
    +}
    +
    +if (goog.LOCALE == 'en_CX' || goog.LOCALE == 'en-CX') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_CX;
    +}
    +
    +if (goog.LOCALE == 'en_DM' || goog.LOCALE == 'en-DM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_DM;
    +}
    +
    +if (goog.LOCALE == 'en_ER' || goog.LOCALE == 'en-ER') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_ER;
    +}
    +
    +if (goog.LOCALE == 'en_FJ' || goog.LOCALE == 'en-FJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_FJ;
    +}
    +
    +if (goog.LOCALE == 'en_FK' || goog.LOCALE == 'en-FK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_FK;
    +}
    +
    +if (goog.LOCALE == 'en_GD' || goog.LOCALE == 'en-GD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GD;
    +}
    +
    +if (goog.LOCALE == 'en_GG' || goog.LOCALE == 'en-GG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GG;
    +}
    +
    +if (goog.LOCALE == 'en_GH' || goog.LOCALE == 'en-GH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GH;
    +}
    +
    +if (goog.LOCALE == 'en_GI' || goog.LOCALE == 'en-GI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GI;
    +}
    +
    +if (goog.LOCALE == 'en_GM' || goog.LOCALE == 'en-GM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GM;
    +}
    +
    +if (goog.LOCALE == 'en_GY' || goog.LOCALE == 'en-GY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_GY;
    +}
    +
    +if (goog.LOCALE == 'en_HK' || goog.LOCALE == 'en-HK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_HK;
    +}
    +
    +if (goog.LOCALE == 'en_IM' || goog.LOCALE == 'en-IM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_IM;
    +}
    +
    +if (goog.LOCALE == 'en_JE' || goog.LOCALE == 'en-JE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_JE;
    +}
    +
    +if (goog.LOCALE == 'en_JM' || goog.LOCALE == 'en-JM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_JM;
    +}
    +
    +if (goog.LOCALE == 'en_KE' || goog.LOCALE == 'en-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_KE;
    +}
    +
    +if (goog.LOCALE == 'en_KI' || goog.LOCALE == 'en-KI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_KI;
    +}
    +
    +if (goog.LOCALE == 'en_KN' || goog.LOCALE == 'en-KN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_KN;
    +}
    +
    +if (goog.LOCALE == 'en_KY' || goog.LOCALE == 'en-KY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_KY;
    +}
    +
    +if (goog.LOCALE == 'en_LC' || goog.LOCALE == 'en-LC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_LC;
    +}
    +
    +if (goog.LOCALE == 'en_LR' || goog.LOCALE == 'en-LR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_LR;
    +}
    +
    +if (goog.LOCALE == 'en_LS' || goog.LOCALE == 'en-LS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_LS;
    +}
    +
    +if (goog.LOCALE == 'en_MG' || goog.LOCALE == 'en-MG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MG;
    +}
    +
    +if (goog.LOCALE == 'en_MO' || goog.LOCALE == 'en-MO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MO;
    +}
    +
    +if (goog.LOCALE == 'en_MS' || goog.LOCALE == 'en-MS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MS;
    +}
    +
    +if (goog.LOCALE == 'en_MT' || goog.LOCALE == 'en-MT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MT;
    +}
    +
    +if (goog.LOCALE == 'en_MU' || goog.LOCALE == 'en-MU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MU;
    +}
    +
    +if (goog.LOCALE == 'en_MW' || goog.LOCALE == 'en-MW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MW;
    +}
    +
    +if (goog.LOCALE == 'en_MY' || goog.LOCALE == 'en-MY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_MY;
    +}
    +
    +if (goog.LOCALE == 'en_NA' || goog.LOCALE == 'en-NA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NA;
    +}
    +
    +if (goog.LOCALE == 'en_NF' || goog.LOCALE == 'en-NF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NF;
    +}
    +
    +if (goog.LOCALE == 'en_NG' || goog.LOCALE == 'en-NG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NG;
    +}
    +
    +if (goog.LOCALE == 'en_NR' || goog.LOCALE == 'en-NR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NR;
    +}
    +
    +if (goog.LOCALE == 'en_NU' || goog.LOCALE == 'en-NU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NU;
    +}
    +
    +if (goog.LOCALE == 'en_NZ' || goog.LOCALE == 'en-NZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_NZ;
    +}
    +
    +if (goog.LOCALE == 'en_PG' || goog.LOCALE == 'en-PG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_PG;
    +}
    +
    +if (goog.LOCALE == 'en_PH' || goog.LOCALE == 'en-PH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_PH;
    +}
    +
    +if (goog.LOCALE == 'en_PK' || goog.LOCALE == 'en-PK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_PK;
    +}
    +
    +if (goog.LOCALE == 'en_PN' || goog.LOCALE == 'en-PN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_PN;
    +}
    +
    +if (goog.LOCALE == 'en_RW' || goog.LOCALE == 'en-RW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_RW;
    +}
    +
    +if (goog.LOCALE == 'en_SB' || goog.LOCALE == 'en-SB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SB;
    +}
    +
    +if (goog.LOCALE == 'en_SC' || goog.LOCALE == 'en-SC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SC;
    +}
    +
    +if (goog.LOCALE == 'en_SD' || goog.LOCALE == 'en-SD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SD;
    +}
    +
    +if (goog.LOCALE == 'en_SH' || goog.LOCALE == 'en-SH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SH;
    +}
    +
    +if (goog.LOCALE == 'en_SL' || goog.LOCALE == 'en-SL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SL;
    +}
    +
    +if (goog.LOCALE == 'en_SS' || goog.LOCALE == 'en-SS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SS;
    +}
    +
    +if (goog.LOCALE == 'en_SX' || goog.LOCALE == 'en-SX') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SX;
    +}
    +
    +if (goog.LOCALE == 'en_SZ' || goog.LOCALE == 'en-SZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_SZ;
    +}
    +
    +if (goog.LOCALE == 'en_TK' || goog.LOCALE == 'en-TK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_TK;
    +}
    +
    +if (goog.LOCALE == 'en_TO' || goog.LOCALE == 'en-TO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_TO;
    +}
    +
    +if (goog.LOCALE == 'en_TT' || goog.LOCALE == 'en-TT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_TT;
    +}
    +
    +if (goog.LOCALE == 'en_TV' || goog.LOCALE == 'en-TV') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_TV;
    +}
    +
    +if (goog.LOCALE == 'en_TZ' || goog.LOCALE == 'en-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_TZ;
    +}
    +
    +if (goog.LOCALE == 'en_UG' || goog.LOCALE == 'en-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_UG;
    +}
    +
    +if (goog.LOCALE == 'en_VC' || goog.LOCALE == 'en-VC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_VC;
    +}
    +
    +if (goog.LOCALE == 'en_VU' || goog.LOCALE == 'en-VU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_VU;
    +}
    +
    +if (goog.LOCALE == 'en_WS' || goog.LOCALE == 'en-WS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_WS;
    +}
    +
    +if (goog.LOCALE == 'en_ZM' || goog.LOCALE == 'en-ZM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_en_ZM;
    +}
    +
    +if (goog.LOCALE == 'eo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'eo_001' || goog.LOCALE == 'eo-001') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'es_AR' || goog.LOCALE == 'es-AR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_AR;
    +}
    +
    +if (goog.LOCALE == 'es_BO' || goog.LOCALE == 'es-BO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_BO;
    +}
    +
    +if (goog.LOCALE == 'es_CL' || goog.LOCALE == 'es-CL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_CL;
    +}
    +
    +if (goog.LOCALE == 'es_CO' || goog.LOCALE == 'es-CO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_CO;
    +}
    +
    +if (goog.LOCALE == 'es_CR' || goog.LOCALE == 'es-CR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_CR;
    +}
    +
    +if (goog.LOCALE == 'es_CU' || goog.LOCALE == 'es-CU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_CU;
    +}
    +
    +if (goog.LOCALE == 'es_DO' || goog.LOCALE == 'es-DO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_DO;
    +}
    +
    +if (goog.LOCALE == 'es_EC' || goog.LOCALE == 'es-EC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_EC;
    +}
    +
    +if (goog.LOCALE == 'es_GQ' || goog.LOCALE == 'es-GQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_GQ;
    +}
    +
    +if (goog.LOCALE == 'es_GT' || goog.LOCALE == 'es-GT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_GT;
    +}
    +
    +if (goog.LOCALE == 'es_HN' || goog.LOCALE == 'es-HN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_HN;
    +}
    +
    +if (goog.LOCALE == 'es_MX' || goog.LOCALE == 'es-MX') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_MX;
    +}
    +
    +if (goog.LOCALE == 'es_NI' || goog.LOCALE == 'es-NI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_NI;
    +}
    +
    +if (goog.LOCALE == 'es_PA' || goog.LOCALE == 'es-PA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_PA;
    +}
    +
    +if (goog.LOCALE == 'es_PE' || goog.LOCALE == 'es-PE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_PE;
    +}
    +
    +if (goog.LOCALE == 'es_PH' || goog.LOCALE == 'es-PH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_PH;
    +}
    +
    +if (goog.LOCALE == 'es_PR' || goog.LOCALE == 'es-PR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_PR;
    +}
    +
    +if (goog.LOCALE == 'es_PY' || goog.LOCALE == 'es-PY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_PY;
    +}
    +
    +if (goog.LOCALE == 'es_SV' || goog.LOCALE == 'es-SV') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_SV;
    +}
    +
    +if (goog.LOCALE == 'es_US' || goog.LOCALE == 'es-US') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_US;
    +}
    +
    +if (goog.LOCALE == 'es_UY' || goog.LOCALE == 'es-UY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_UY;
    +}
    +
    +if (goog.LOCALE == 'es_VE' || goog.LOCALE == 'es-VE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_es_VE;
    +}
    +
    +if (goog.LOCALE == 'ewo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'ewo_CM' || goog.LOCALE == 'ewo-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'fa_AF' || goog.LOCALE == 'fa-AF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fa_AF;
    +}
    +
    +if (goog.LOCALE == 'ff') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_CM' || goog.LOCALE == 'ff-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ff_CM;
    +}
    +
    +if (goog.LOCALE == 'ff_GN' || goog.LOCALE == 'ff-GN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ff_GN;
    +}
    +
    +if (goog.LOCALE == 'ff_MR' || goog.LOCALE == 'ff-MR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ff_MR;
    +}
    +
    +if (goog.LOCALE == 'ff_SN' || goog.LOCALE == 'ff-SN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'fo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fo_FO' || goog.LOCALE == 'fo-FO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fr_BE' || goog.LOCALE == 'fr-BE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_BE;
    +}
    +
    +if (goog.LOCALE == 'fr_BF' || goog.LOCALE == 'fr-BF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_BF;
    +}
    +
    +if (goog.LOCALE == 'fr_BI' || goog.LOCALE == 'fr-BI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_BI;
    +}
    +
    +if (goog.LOCALE == 'fr_BJ' || goog.LOCALE == 'fr-BJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_BJ;
    +}
    +
    +if (goog.LOCALE == 'fr_CD' || goog.LOCALE == 'fr-CD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CD;
    +}
    +
    +if (goog.LOCALE == 'fr_CF' || goog.LOCALE == 'fr-CF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CF;
    +}
    +
    +if (goog.LOCALE == 'fr_CG' || goog.LOCALE == 'fr-CG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CG;
    +}
    +
    +if (goog.LOCALE == 'fr_CH' || goog.LOCALE == 'fr-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CH;
    +}
    +
    +if (goog.LOCALE == 'fr_CI' || goog.LOCALE == 'fr-CI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CI;
    +}
    +
    +if (goog.LOCALE == 'fr_CM' || goog.LOCALE == 'fr-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_CM;
    +}
    +
    +if (goog.LOCALE == 'fr_DJ' || goog.LOCALE == 'fr-DJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_DJ;
    +}
    +
    +if (goog.LOCALE == 'fr_DZ' || goog.LOCALE == 'fr-DZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_DZ;
    +}
    +
    +if (goog.LOCALE == 'fr_GA' || goog.LOCALE == 'fr-GA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_GA;
    +}
    +
    +if (goog.LOCALE == 'fr_GN' || goog.LOCALE == 'fr-GN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_GN;
    +}
    +
    +if (goog.LOCALE == 'fr_GQ' || goog.LOCALE == 'fr-GQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_GQ;
    +}
    +
    +if (goog.LOCALE == 'fr_HT' || goog.LOCALE == 'fr-HT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_HT;
    +}
    +
    +if (goog.LOCALE == 'fr_KM' || goog.LOCALE == 'fr-KM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_KM;
    +}
    +
    +if (goog.LOCALE == 'fr_LU' || goog.LOCALE == 'fr-LU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_LU;
    +}
    +
    +if (goog.LOCALE == 'fr_MA' || goog.LOCALE == 'fr-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_MA;
    +}
    +
    +if (goog.LOCALE == 'fr_MG' || goog.LOCALE == 'fr-MG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_MG;
    +}
    +
    +if (goog.LOCALE == 'fr_ML' || goog.LOCALE == 'fr-ML') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_ML;
    +}
    +
    +if (goog.LOCALE == 'fr_MR' || goog.LOCALE == 'fr-MR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_MR;
    +}
    +
    +if (goog.LOCALE == 'fr_MU' || goog.LOCALE == 'fr-MU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_MU;
    +}
    +
    +if (goog.LOCALE == 'fr_NC' || goog.LOCALE == 'fr-NC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_NC;
    +}
    +
    +if (goog.LOCALE == 'fr_NE' || goog.LOCALE == 'fr-NE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_NE;
    +}
    +
    +if (goog.LOCALE == 'fr_PF' || goog.LOCALE == 'fr-PF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_PF;
    +}
    +
    +if (goog.LOCALE == 'fr_RW' || goog.LOCALE == 'fr-RW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_RW;
    +}
    +
    +if (goog.LOCALE == 'fr_SC' || goog.LOCALE == 'fr-SC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_SC;
    +}
    +
    +if (goog.LOCALE == 'fr_SN' || goog.LOCALE == 'fr-SN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_SN;
    +}
    +
    +if (goog.LOCALE == 'fr_SY' || goog.LOCALE == 'fr-SY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_SY;
    +}
    +
    +if (goog.LOCALE == 'fr_TD' || goog.LOCALE == 'fr-TD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_TD;
    +}
    +
    +if (goog.LOCALE == 'fr_TG' || goog.LOCALE == 'fr-TG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_TG;
    +}
    +
    +if (goog.LOCALE == 'fr_TN' || goog.LOCALE == 'fr-TN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_TN;
    +}
    +
    +if (goog.LOCALE == 'fr_VU' || goog.LOCALE == 'fr-VU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_VU;
    +}
    +
    +if (goog.LOCALE == 'fr_WF' || goog.LOCALE == 'fr-WF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fr_WF;
    +}
    +
    +if (goog.LOCALE == 'fur') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fur_IT' || goog.LOCALE == 'fur-IT') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fy') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'gd') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gd_GB' || goog.LOCALE == 'gd-GB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gsw_FR' || goog.LOCALE == 'gsw-FR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gsw_FR;
    +}
    +
    +if (goog.LOCALE == 'guz') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'guz_KE' || goog.LOCALE == 'guz-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'gv') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'gv_IM' || goog.LOCALE == 'gv-IM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'ha') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn' || goog.LOCALE == 'ha-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ha_Latn;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_GH' || goog.LOCALE == 'ha-Latn-GH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ha_Latn_GH;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NE' || goog.LOCALE == 'ha-Latn-NE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ha_Latn_NE;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NG' || goog.LOCALE == 'ha-Latn-NG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'hr_BA' || goog.LOCALE == 'hr-BA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hr_BA;
    +}
    +
    +if (goog.LOCALE == 'hsb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'hsb_DE' || goog.LOCALE == 'hsb-DE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'ia') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'ia_FR' || goog.LOCALE == 'ia-FR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'ig') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ig_NG' || goog.LOCALE == 'ig-NG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ii') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'ii_CN' || goog.LOCALE == 'ii-CN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'it_CH' || goog.LOCALE == 'it-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_it_CH;
    +}
    +
    +if (goog.LOCALE == 'jgo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jgo_CM' || goog.LOCALE == 'jgo-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jmc') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'jmc_TZ' || goog.LOCALE == 'jmc-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'kab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kab_DZ' || goog.LOCALE == 'kab-DZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kam') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kam_KE' || goog.LOCALE == 'kam-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kde') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kde_TZ' || goog.LOCALE == 'kde-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kea') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'kea_CV' || goog.LOCALE == 'kea-CV') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'khq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'khq_ML' || goog.LOCALE == 'khq-ML') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'ki') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'ki_KE' || goog.LOCALE == 'ki-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl' || goog.LOCALE == 'kk-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kkj') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kkj_CM' || goog.LOCALE == 'kkj-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kl_GL' || goog.LOCALE == 'kl-GL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kln') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'kln_KE' || goog.LOCALE == 'kln-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'ko_KP' || goog.LOCALE == 'ko-KP') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ko_KP;
    +}
    +
    +if (goog.LOCALE == 'kok') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'kok_IN' || goog.LOCALE == 'kok-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'ks') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab' || goog.LOCALE == 'ks-Arab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ks_Arab;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab_IN' || goog.LOCALE == 'ks-Arab-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ksb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksb_TZ' || goog.LOCALE == 'ksb-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksf') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksf_CM' || goog.LOCALE == 'ksf-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'ksh_DE' || goog.LOCALE == 'ksh-DE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'kw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'kw_GB' || goog.LOCALE == 'kw-GB') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl' || goog.LOCALE == 'ky-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'lag') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lag_TZ' || goog.LOCALE == 'lag-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lb') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lb_LU' || goog.LOCALE == 'lb-LU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lg_UG' || goog.LOCALE == 'lg-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lkt') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'lkt_US' || goog.LOCALE == 'lkt-US') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'ln_AO' || goog.LOCALE == 'ln-AO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ln_AO;
    +}
    +
    +if (goog.LOCALE == 'ln_CF' || goog.LOCALE == 'ln-CF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ln_CF;
    +}
    +
    +if (goog.LOCALE == 'ln_CG' || goog.LOCALE == 'ln-CG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ln_CG;
    +}
    +
    +if (goog.LOCALE == 'lu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'lu_CD' || goog.LOCALE == 'lu-CD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'luo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luo_KE' || goog.LOCALE == 'luo-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luy') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'luy_KE' || goog.LOCALE == 'luy-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'mas') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_KE' || goog.LOCALE == 'mas-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_TZ' || goog.LOCALE == 'mas-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mas_TZ;
    +}
    +
    +if (goog.LOCALE == 'mer') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mer_KE' || goog.LOCALE == 'mer-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mfe') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mfe_MU' || goog.LOCALE == 'mfe-MU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mg_MG' || goog.LOCALE == 'mg-MG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mgh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgh_MZ' || goog.LOCALE == 'mgh-MZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mgo_CM' || goog.LOCALE == 'mgo-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl' || goog.LOCALE == 'mn-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn' || goog.LOCALE == 'ms-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_BN' || goog.LOCALE == 'ms-Latn-BN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ms_Latn_BN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_SG' || goog.LOCALE == 'ms-Latn-SG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ms_Latn_SG;
    +}
    +
    +if (goog.LOCALE == 'mua') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'mua_CM' || goog.LOCALE == 'mua-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'naq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'naq_NA' || goog.LOCALE == 'naq-NA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'nd') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'nd_ZW' || goog.LOCALE == 'nd-ZW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'ne_IN' || goog.LOCALE == 'ne-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ne_IN;
    +}
    +
    +if (goog.LOCALE == 'nl_AW' || goog.LOCALE == 'nl-AW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_AW;
    +}
    +
    +if (goog.LOCALE == 'nl_BE' || goog.LOCALE == 'nl-BE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_BE;
    +}
    +
    +if (goog.LOCALE == 'nl_BQ' || goog.LOCALE == 'nl-BQ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_BQ;
    +}
    +
    +if (goog.LOCALE == 'nl_CW' || goog.LOCALE == 'nl-CW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_CW;
    +}
    +
    +if (goog.LOCALE == 'nl_SR' || goog.LOCALE == 'nl-SR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_SR;
    +}
    +
    +if (goog.LOCALE == 'nl_SX' || goog.LOCALE == 'nl-SX') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nl_SX;
    +}
    +
    +if (goog.LOCALE == 'nmg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nmg_CM' || goog.LOCALE == 'nmg-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nn_NO' || goog.LOCALE == 'nn-NO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nnh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nnh_CM' || goog.LOCALE == 'nnh-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nr') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nr_ZA' || goog.LOCALE == 'nr-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nso') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nso_ZA' || goog.LOCALE == 'nso-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nus') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nus_SD' || goog.LOCALE == 'nus-SD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nyn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'nyn_UG' || goog.LOCALE == 'nyn-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'om') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_ET' || goog.LOCALE == 'om-ET') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_KE' || goog.LOCALE == 'om-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_om_KE;
    +}
    +
    +if (goog.LOCALE == 'os') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_GE' || goog.LOCALE == 'os-GE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_RU' || goog.LOCALE == 'os-RU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_os_RU;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab' || goog.LOCALE == 'pa-Arab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab_PK' || goog.LOCALE == 'pa-Arab-PK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru' || goog.LOCALE == 'pa-Guru') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'ps') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'ps_AF' || goog.LOCALE == 'ps-AF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'pt_AO' || goog.LOCALE == 'pt-AO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_AO;
    +}
    +
    +if (goog.LOCALE == 'pt_CV' || goog.LOCALE == 'pt-CV') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_CV;
    +}
    +
    +if (goog.LOCALE == 'pt_GW' || goog.LOCALE == 'pt-GW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_GW;
    +}
    +
    +if (goog.LOCALE == 'pt_MO' || goog.LOCALE == 'pt-MO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_MO;
    +}
    +
    +if (goog.LOCALE == 'pt_MZ' || goog.LOCALE == 'pt-MZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_MZ;
    +}
    +
    +if (goog.LOCALE == 'pt_ST' || goog.LOCALE == 'pt-ST') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_ST;
    +}
    +
    +if (goog.LOCALE == 'pt_TL' || goog.LOCALE == 'pt-TL') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_pt_TL;
    +}
    +
    +if (goog.LOCALE == 'qu') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_BO' || goog.LOCALE == 'qu-BO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_qu_BO;
    +}
    +
    +if (goog.LOCALE == 'qu_EC' || goog.LOCALE == 'qu-EC') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_qu_EC;
    +}
    +
    +if (goog.LOCALE == 'qu_PE' || goog.LOCALE == 'qu-PE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'rm') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rm_CH' || goog.LOCALE == 'rm-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'rn_BI' || goog.LOCALE == 'rn-BI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'ro_MD' || goog.LOCALE == 'ro-MD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ro_MD;
    +}
    +
    +if (goog.LOCALE == 'rof') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'rof_TZ' || goog.LOCALE == 'rof-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'ru_BY' || goog.LOCALE == 'ru-BY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru_BY;
    +}
    +
    +if (goog.LOCALE == 'ru_KG' || goog.LOCALE == 'ru-KG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru_KG;
    +}
    +
    +if (goog.LOCALE == 'ru_KZ' || goog.LOCALE == 'ru-KZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru_KZ;
    +}
    +
    +if (goog.LOCALE == 'ru_MD' || goog.LOCALE == 'ru-MD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru_MD;
    +}
    +
    +if (goog.LOCALE == 'ru_UA' || goog.LOCALE == 'ru-UA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ru_UA;
    +}
    +
    +if (goog.LOCALE == 'rw') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rw_RW' || goog.LOCALE == 'rw-RW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rwk') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'rwk_TZ' || goog.LOCALE == 'rwk-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'sah') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'sah_RU' || goog.LOCALE == 'sah-RU') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'saq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'saq_KE' || goog.LOCALE == 'saq-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'sbp') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'sbp_TZ' || goog.LOCALE == 'sbp-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'se') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_FI' || goog.LOCALE == 'se-FI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_se_FI;
    +}
    +
    +if (goog.LOCALE == 'se_NO' || goog.LOCALE == 'se-NO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_SE' || goog.LOCALE == 'se-SE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_se_SE;
    +}
    +
    +if (goog.LOCALE == 'seh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'seh_MZ' || goog.LOCALE == 'seh-MZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'ses') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'ses_ML' || goog.LOCALE == 'ses-ML') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'sg') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'sg_CF' || goog.LOCALE == 'sg-CF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'shi') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn' || goog.LOCALE == 'shi-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn_MA' || goog.LOCALE == 'shi-Latn-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng' || goog.LOCALE == 'shi-Tfng') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_shi_Tfng;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng_MA' || goog.LOCALE == 'shi-Tfng-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'smn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'smn_FI' || goog.LOCALE == 'smn-FI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'sn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'sn_ZW' || goog.LOCALE == 'sn-ZW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'so') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_DJ' || goog.LOCALE == 'so-DJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_so_DJ;
    +}
    +
    +if (goog.LOCALE == 'so_ET' || goog.LOCALE == 'so-ET') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_so_ET;
    +}
    +
    +if (goog.LOCALE == 'so_KE' || goog.LOCALE == 'so-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_so_KE;
    +}
    +
    +if (goog.LOCALE == 'so_SO' || goog.LOCALE == 'so-SO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'sq_MK' || goog.LOCALE == 'sq-MK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sq_MK;
    +}
    +
    +if (goog.LOCALE == 'sq_XK' || goog.LOCALE == 'sq-XK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sq_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl' || goog.LOCALE == 'sr-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_BA' || goog.LOCALE == 'sr-Cyrl-BA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_ME' || goog.LOCALE == 'sr-Cyrl-ME') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_XK' || goog.LOCALE == 'sr-Cyrl-XK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn' || goog.LOCALE == 'sr-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_BA' || goog.LOCALE == 'sr-Latn-BA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_ME' || goog.LOCALE == 'sr-Latn-ME') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Latn_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_RS' || goog.LOCALE == 'sr-Latn-RS') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_XK' || goog.LOCALE == 'sr-Latn-XK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sr_Latn_XK;
    +}
    +
    +if (goog.LOCALE == 'ss') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ss_SZ' || goog.LOCALE == 'ss-SZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ss_SZ;
    +}
    +
    +if (goog.LOCALE == 'ss_ZA' || goog.LOCALE == 'ss-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ssy') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'ssy_ER' || goog.LOCALE == 'ssy-ER') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'sv_AX' || goog.LOCALE == 'sv-AX') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sv_AX;
    +}
    +
    +if (goog.LOCALE == 'sv_FI' || goog.LOCALE == 'sv-FI') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sv_FI;
    +}
    +
    +if (goog.LOCALE == 'sw_KE' || goog.LOCALE == 'sw-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sw_KE;
    +}
    +
    +if (goog.LOCALE == 'sw_UG' || goog.LOCALE == 'sw-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_sw_UG;
    +}
    +
    +if (goog.LOCALE == 'swc') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'swc_CD' || goog.LOCALE == 'swc-CD') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'ta_LK' || goog.LOCALE == 'ta-LK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ta_LK;
    +}
    +
    +if (goog.LOCALE == 'ta_MY' || goog.LOCALE == 'ta-MY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ta_MY;
    +}
    +
    +if (goog.LOCALE == 'ta_SG' || goog.LOCALE == 'ta-SG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ta_SG;
    +}
    +
    +if (goog.LOCALE == 'teo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_KE' || goog.LOCALE == 'teo-KE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_teo_KE;
    +}
    +
    +if (goog.LOCALE == 'teo_UG' || goog.LOCALE == 'teo-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'ti') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'ti_ER' || goog.LOCALE == 'ti-ER') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ti_ER;
    +}
    +
    +if (goog.LOCALE == 'ti_ET' || goog.LOCALE == 'ti-ET') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'tn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'tn_BW' || goog.LOCALE == 'tn-BW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tn_BW;
    +}
    +
    +if (goog.LOCALE == 'tn_ZA' || goog.LOCALE == 'tn-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'to') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'to_TO' || goog.LOCALE == 'to-TO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'tr_CY' || goog.LOCALE == 'tr-CY') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tr_CY;
    +}
    +
    +if (goog.LOCALE == 'ts') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'ts_ZA' || goog.LOCALE == 'ts-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'twq') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'twq_NE' || goog.LOCALE == 'twq-NE') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'tzm') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn' || goog.LOCALE == 'tzm-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tzm_Latn;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn_MA' || goog.LOCALE == 'tzm-Latn-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'ug') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab' || goog.LOCALE == 'ug-Arab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ug_Arab;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab_CN' || goog.LOCALE == 'ug-Arab-CN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ur_IN' || goog.LOCALE == 'ur-IN') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ur_IN;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab' || goog.LOCALE == 'uz-Arab') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab_AF' || goog.LOCALE == 'uz-Arab-AF') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl' || goog.LOCALE == 'uz-Cyrl') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl_UZ' || goog.LOCALE == 'uz-Cyrl-UZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz_Cyrl_UZ;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn' || goog.LOCALE == 'uz-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn' || goog.LOCALE == 'vai-Latn') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn_LR' || goog.LOCALE == 'vai-Latn-LR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii' || goog.LOCALE == 'vai-Vaii') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vai_Vaii;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii_LR' || goog.LOCALE == 'vai-Vaii-LR') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 've') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 've_ZA' || goog.LOCALE == 've-ZA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 'vo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vo_001' || goog.LOCALE == 'vo-001') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vun') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'vun_TZ' || goog.LOCALE == 'vun-TZ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'wae') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'wae_CH' || goog.LOCALE == 'wae-CH') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'xog') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'xog_UG' || goog.LOCALE == 'xog-UG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'yav') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yav_CM' || goog.LOCALE == 'yav-CM') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yi') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yi_001' || goog.LOCALE == 'yi-001') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yo') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'yo_BJ' || goog.LOCALE == 'yo-BJ') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yo_BJ;
    +}
    +
    +if (goog.LOCALE == 'yo_NG' || goog.LOCALE == 'yo-NG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'zgh') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zgh_MA' || goog.LOCALE == 'zgh-MA') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans' || goog.LOCALE == 'zh-Hans') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_HK' || goog.LOCALE == 'zh-Hans-HK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hans_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_MO' || goog.LOCALE == 'zh-Hans-MO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hans_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_SG' || goog.LOCALE == 'zh-Hans-SG') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hans_SG;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant' || goog.LOCALE == 'zh-Hant') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_HK' || goog.LOCALE == 'zh-Hant-HK') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hant_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_MO' || goog.LOCALE == 'zh-Hant-MO') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hant_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_TW' || goog.LOCALE == 'zh-Hant-TW') {
    +  goog.i18n.CompactNumberFormatSymbols = goog.i18n.CompactNumberFormatSymbols_zh_Hant;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/currency.js b/mapbox-gl/closure-library/closure/goog/i18n/currency.js
    new file mode 100644
    index 0000000000..ef207ee00d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/currency.js
    @@ -0,0 +1,434 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A utility to get better currency format pattern.
    + *
    + * This module implements a new currency format representation model. It
    + * provides 3 currency representation forms: global, portable and local. Local
    + * format is the most popular format people use to represent currency in its
    + * circulating country without worrying about how it should be distinguished
    + * from other currencies.  Global format is a formal representation in context
    + * of multiple currencies in same page, it is ISO 4217 currency code. Portable
    + * format is a compromise between global and local. It looks similar to how
    + * people would like to see how their currency is being represented in other
    + * media. While at the same time, it should be distinguishable to world's
    + * popular currencies (like USD, EUR) and currencies somewhat relevant in the
    + * area (like CNY in HK, though native currency is HKD). There is no guarantee
    + * of uniqueness.
    + *
    + */
    +
    +
    +goog.provide('goog.i18n.currency');
    +goog.provide('goog.i18n.currency.CurrencyInfo');
    +goog.provide('goog.i18n.currency.CurrencyInfoTier2');
    +
    +
    +/**
    + * The mask of precision field.
    + * @private
    + */
    +goog.i18n.currency.PRECISION_MASK_ = 0x07;
    +
    +
    +/**
    + * Whether the currency sign should be positioned after the number.
    + * @private
    + */
    +goog.i18n.currency.POSITION_FLAG_ = 0x10;
    +
    +
    +/**
    + * Whether a space should be inserted between the number and currency sign.
    + * @private
    + */
    +goog.i18n.currency.SPACE_FLAG_ = 0x20;
    +
    +
    +/**
    + * Whether tier2 was enabled already by calling addTier2Support().
    + * @private
    + */
    +goog.i18n.currency.tier2Enabled_ = false;
    +
    +
    +/**
    + * This function will add tier2 currency support. Be default, only tier1
    + * (most popular currencies) are supported. If an application really needs
    + * to support some of the rarely used currencies, it should call this function
    + * before any other functions in this namespace.
    + */
    +goog.i18n.currency.addTier2Support = function() {
    +  // Protection from executing this these again and again.
    +  if (!goog.i18n.currency.tier2Enabled_) {
    +    for (var key in goog.i18n.currency.CurrencyInfoTier2) {
    +      goog.i18n.currency.CurrencyInfo[key] =
    +          goog.i18n.currency.CurrencyInfoTier2[key];
    +    }
    +    goog.i18n.currency.tier2Enabled_ = true;
    +  }
    +};
    +
    +
    +/**
    + * Global currency pattern always uses ISO-4217 currency code as prefix. Local
    + * currency sign is added if it is different from currency code. Each currency
    + * is unique in this form. The negative side is that ISO code looks weird in
    + * some countries as people normally do not use it. Local currency sign
    + * alleviates the problem, but also makes it a little verbose.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Global currency pattern string for given currency.
    + */
    +goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
    +  var info = goog.i18n.currency.CurrencyInfo[currencyCode];
    +  var patternNum = info[0];
    +  if (currencyCode == info[1]) {
    +    return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
    +  }
    +  return currencyCode + ' ' +
    +      goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
    +};
    +
    +
    +/**
    + * Return global currency sign string for those applications
    + * that want to handle currency sign themselves.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Global currency sign for given currency.
    + */
    +goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
    +  var info = goog.i18n.currency.CurrencyInfo[currencyCode];
    +  return (currencyCode == info[1]) ? currencyCode :
    +      currencyCode + ' ' + info[1];
    +};
    +
    +
    +/**
    + * Local currency pattern is the most frequently used pattern in currency's
    + * native region. It does not care about how it is distinguished from other
    + * currencies.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Local currency pattern string for given currency.
    + */
    +goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
    +  var info = goog.i18n.currency.CurrencyInfo[currencyCode];
    +  return goog.i18n.currency.getCurrencyPattern_(info[0], info[1]);
    +};
    +
    +
    +/**
    + * Returns local currency sign string for those applications that need to
    + * handle currency sign separately.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Local currency sign for given currency.
    + */
    +goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
    +  return goog.i18n.currency.CurrencyInfo[currencyCode][1];
    +};
    +
    +
    +/**
    + * Portable currency pattern is a compromise between local and global. It is
    + * not a mere blend or mid-way between the two. Currency sign is chosen so that
    + * it looks familiar to native users. It also has enough information to
    + * distinguish itself from other popular currencies in its native region.
    + * In this pattern, currency sign symbols that has availability problem in
    + * popular fonts are also avoided.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Portable currency pattern string for given currency.
    + */
    +goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
    +  var info = goog.i18n.currency.CurrencyInfo[currencyCode];
    +  return goog.i18n.currency.getCurrencyPattern_(info[0], info[2]);
    +};
    +
    +
    +/**
    + * Return portable currency sign string for those applications that need to
    + * handle currency sign themselves.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {string} Portable currency sign for given currency.
    + */
    +goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
    +  return goog.i18n.currency.CurrencyInfo[currencyCode][2];
    +};
    +
    +
    +/**
    + * This function returns the default currency sign position. Some applications
    + * may want to handle currency sign and currency amount separately. This
    + * function can be used in such situations to correctly position the currency
    + * sign relative to the amount.
    + *
    + * To match the behavior of ICU, position is not determined by display locale.
    + *
    + * @param {string} currencyCode ISO-4217 3-letter currency code.
    + * @return {boolean} true if currency should be positioned before amount field.
    + */
    +goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
    +  return (goog.i18n.currency.CurrencyInfo[currencyCode][0] &
    +          goog.i18n.currency.POSITION_FLAG_) == 0;
    +};
    +
    +
    +/**
    + * This function constructs the currency pattern. Currency sign is provided. The
    + * pattern information is encoded in patternNum.
    + *
    + * @param {number} patternNum Encoded pattern number that has
    + *     currency pattern information.
    + * @param {string} sign The currency sign that will be used in pattern.
    + * @return {string} currency pattern string.
    + * @private
    + */
    +goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
    +  var strParts = ['#,##0'];
    +  var precision = patternNum & goog.i18n.currency.PRECISION_MASK_;
    +  if (precision > 0) {
    +    strParts.push('.');
    +    for (var i = 0; i < precision; i++) {
    +      strParts.push('0');
    +    }
    +  }
    +  if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
    +    strParts.unshift((patternNum & goog.i18n.currency.SPACE_FLAG_) ?
    +                     "' " : "'");
    +    strParts.unshift(sign);
    +    strParts.unshift("'");
    +  } else {
    +    strParts.push((patternNum & goog.i18n.currency.SPACE_FLAG_) ? " '" : "'",
    +                  sign, "'");
    +  }
    +  return strParts.join('');
    +};
    +
    +
    +/**
    + * Modify currency pattern string by adjusting precision for given currency.
    + * Standard currency pattern will have 2 digit after decimal point.
    + * Examples:
    + *   $#,##0.00 ->  $#,##0    (precision == 0)
    + *   $#,##0.00 ->  $#,##0.0  (precision == 1)
    + *   $#,##0.00 ->  $#,##0.000  (precision == 3)
    + *
    + * @param {string} pattern currency pattern string.
    + * @param {string} currencyCode 3-letter currency code.
    + * @return {string} modified currency pattern string.
    + */
    +goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
    +  var strParts = ['0'];
    +  var info = goog.i18n.currency.CurrencyInfo[currencyCode];
    +  var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
    +  if (precision > 0) {
    +    strParts.push('.');
    +    for (var i = 0; i < precision; i++) {
    +      strParts.push('0');
    +    }
    +  }
    +  return pattern.replace(/0.00/g, strParts.join(''));
    +};
    +
    +
    +/**
    + * Tier 1 currency information.
    + *
    + * The first number in the array is a combination of the precision mask and
    + * other flags. The precision mask indicates how many decimal places to show for
    + * the currency. Valid values are [0..7]. The position flag indicates whether
    + * the currency sign should be positioned after the number. Valid values are 0
    + * (before the number) or 16 (after the number). The space flag indicates
    + * whether a space should be inserted between the currency sign and number.
    + * Valid values are 0 (no space) and 32 (space).
    + *
    + * The number in the array is calculated by adding together the mask and flag
    + * values. For example:
    + *
    + * 0: no precision (0), currency sign first (0), no space (0)
    + * 2: two decimals precision (2), currency sign first (0), no space (0)
    + * 18: two decimals precision (2), currency sign last (16), no space (0)
    + * 50: two decimals precision (2), currency sign last (16), space (32)
    + *
    + * @const {!Object<!Array<?>>}
    + */
    +goog.i18n.currency.CurrencyInfo = {
    +  'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
    +  'ALL': [0, 'Lek', 'Lek'],
    +  'AUD': [2, '$', 'AU$'],
    +  'BDT': [2, '\u09F3', 'Tk'],
    +  'BGN': [2, 'lev', 'lev'],
    +  'BRL': [2, 'R$', 'R$'],
    +  'CAD': [2, '$', 'C$'],
    +  'CDF': [2, 'FrCD', 'CDF'],
    +  'CHF': [2, 'CHF', 'CHF'],
    +  'CLP': [0, '$', 'CL$'],
    +  'CNY': [2, '¥', 'RMB¥'],
    +  'COP': [0, '$', 'COL$'],
    +  'CRC': [0, '\u20a1', 'CR\u20a1'],
    +  'CZK': [50, 'K\u010d', 'K\u010d'],
    +  'DKK': [18, 'kr', 'kr'],
    +  'DOP': [2, '$', 'RD$'],
    +  'EGP': [2, '£', 'LE'],
    +  'ETB': [2, 'Birr', 'Birr'],
    +  'EUR': [2, '€', '€'],
    +  'GBP': [2, '£', 'GB£'],
    +  'HKD': [2, '$', 'HK$'],
    +  'HRK': [2, 'kn', 'kn'],
    +  'HUF': [0, 'Ft', 'Ft'],
    +  'IDR': [0, 'Rp', 'Rp'],
    +  'ILS': [2, '\u20AA', 'IL\u20AA'],
    +  'INR': [2, '\u20B9', 'Rs'],
    +  'IRR': [0, 'Rial', 'IRR'],
    +  'ISK': [0, 'kr', 'kr'],
    +  'JMD': [2, '$', 'JA$'],
    +  'JPY': [0, '¥', 'JP¥'],
    +  'KRW': [0, '\u20A9', 'KR₩'],
    +  'LKR': [2, 'Rs', 'SLRs'],
    +  'LTL': [2, 'Lt', 'Lt'],
    +  'LVL': [2, 'Ls', 'Ls'],
    +  'MNT': [0, '\u20AE', 'MN₮'],
    +  'MXN': [2, '$', 'Mex$'],
    +  'MYR': [2, 'RM', 'RM'],
    +  'NOK': [50, 'kr', 'NOkr'],
    +  'PAB': [2, 'B/.', 'B/.'],
    +  'PEN': [2, 'S/.', 'S/.'],
    +  'PHP': [2, '\u20B1', 'Php'],
    +  'PKR': [0, 'Rs', 'PKRs.'],
    +  'PLN': [50, 'z\u0142', 'z\u0142'],
    +  'RON': [2, 'RON', 'RON'],
    +  'RSD': [0, 'din', 'RSD'],
    +  'RUB': [50, 'руб.', 'руб.'],
    +  'SAR': [2, 'Rial', 'Rial'],
    +  'SEK': [2, 'kr', 'kr'],
    +  'SGD': [2, '$', 'S$'],
    +  'THB': [2, '\u0e3f', 'THB'],
    +  'TRY': [2, 'TL', 'YTL'],
    +  'TWD': [2, 'NT$', 'NT$'],
    +  'TZS': [0, 'TSh', 'TSh'],
    +  'UAH': [2, '\u20B4', 'UAH'],
    +  'USD': [2, '$', 'US$'],
    +  'UYU': [2, '$', '$U'],
    +  'VND': [0, '\u20AB', 'VN\u20AB'],
    +  'YER': [0, 'Rial', 'Rial'],
    +  'ZAR': [2, 'R', 'ZAR']
    +};
    +
    +
    +/**
    + * Tier 2 currency information.
    + * @const {!Object<!Array<?>>}
    + */
    +goog.i18n.currency.CurrencyInfoTier2 = {
    +  'AFN': [48, 'Af.', 'AFN'],
    +  'AMD': [0, 'Dram', 'dram'],
    +  'AOA': [2, 'Kz', 'Kz'],
    +  'ARS': [2, '$', 'AR$'],
    +  'AWG': [2, 'Afl.', 'Afl.'],
    +  'AZN': [2, 'man.', 'man.'],
    +  'BAM': [2, 'KM', 'KM'],
    +  'BBD': [2, '$', 'Bds$'],
    +  'BHD': [3, 'din', 'din'],
    +  'BIF': [0, 'FBu', 'FBu'],
    +  'BMD': [2, '$', 'BD$'],
    +  'BND': [2, '$', 'B$'],
    +  'BOB': [2, 'Bs', 'Bs'],
    +  'BSD': [2, '$', 'BS$'],
    +  'BTN': [2, 'Nu.', 'Nu.'],
    +  'BWP': [2, 'P', 'pula'],
    +  'BYR': [0, 'BYR', 'BYR'],
    +  'BZD': [2, '$', 'BZ$'],
    +  'CUC': [1, '$', 'CUC$'],
    +  'CUP': [2, '$', 'CU$'],
    +  'CVE': [2, 'CVE', 'Esc'],
    +  'DJF': [0, 'Fdj', 'Fdj'],
    +  'DZD': [2, 'din', 'din'],
    +  'ERN': [2, 'Nfk', 'Nfk'],
    +  'FJD': [2, '$', 'FJ$'],
    +  'FKP': [2, '£', 'FK£'],
    +  'GEL': [2, 'GEL', 'GEL'],
    +  'GHS': [2, 'GHS', 'GHS'],
    +  'GIP': [2, '£', 'GI£'],
    +  'GMD': [2, 'GMD', 'GMD'],
    +  'GNF': [0, 'FG', 'FG'],
    +  'GTQ': [2, 'Q', 'GTQ'],
    +  'GYD': [0, '$', 'GY$'],
    +  'HNL': [2, 'L', 'HNL'],
    +  'HTG': [2, 'HTG', 'HTG'],
    +  'IQD': [0, 'din', 'IQD'],
    +  'JOD': [3, 'din', 'JOD'],
    +  'KES': [2, 'Ksh', 'Ksh'],
    +  'KGS': [2, 'KGS', 'KGS'],
    +  'KHR': [2, 'Riel', 'KHR'],
    +  'KMF': [0, 'CF', 'KMF'],
    +  'KPW': [0, '\u20A9KP', 'KPW'],
    +  'KWD': [3, 'din', 'KWD'],
    +  'KYD': [2, '$', 'KY$'],
    +  'KZT': [2, '\u20B8', 'KZT'],
    +  'LAK': [0, '\u20AD', '\u20AD'],
    +  'LBP': [0, 'L£', 'LBP'],
    +  'LRD': [2, '$', 'L$'],
    +  'LSL': [2, 'LSL', 'LSL'],
    +  'LYD': [3, 'din', 'LD'],
    +  'MAD': [2, 'dh', 'MAD'],
    +  'MDL': [2, 'MDL', 'MDL'],
    +  'MGA': [0, 'Ar', 'MGA'],
    +  'MKD': [2, 'din', 'MKD'],
    +  'MMK': [0, 'K', 'MMK'],
    +  'MOP': [2, 'MOP', 'MOP$'],
    +  'MRO': [0, 'MRO', 'MRO'],
    +  'MUR': [0, 'MURs', 'MURs'],
    +  'MWK': [2, 'MWK', 'MWK'],
    +  'MZN': [2, 'MTn', 'MTn'],
    +  'NAD': [2, '$', 'N$'],
    +  'NGN': [2, '\u20A6', 'NG\u20A6'],
    +  'NIO': [2, 'C$', 'C$'],
    +  'NPR': [2, 'Rs', 'NPRs'],
    +  'NZD': [2, '$', 'NZ$'],
    +  'OMR': [3, 'Rial', 'OMR'],
    +  'PGK': [2, 'PGK', 'PGK'],
    +  'PYG': [0, 'Gs', 'PYG'],
    +  'QAR': [2, 'Rial', 'QR'],
    +  'RWF': [0, 'RF', 'RF'],
    +  'SBD': [2, '$', 'SI$'],
    +  'SCR': [2, 'SCR', 'SCR'],
    +  'SDG': [2, 'SDG', 'SDG'],
    +  'SHP': [2, '£', 'SH£'],
    +  'SLL': [0, 'SLL', 'SLL'],
    +  'SOS': [0, 'SOS', 'SOS'],
    +  'SRD': [2, '$', 'SR$'],
    +  'STD': [0, 'Db', 'Db'],
    +  'SYP': [0, '£', 'SY£'],
    +  'SZL': [2, 'SZL', 'SZL'],
    +  'TJS': [2, 'Som', 'TJS'],
    +  'TND': [3, 'din', 'DT'],
    +  'TOP': [2, 'T$', 'T$'],
    +  'TTD': [2, '$', 'TT$'],
    +  'UGX': [0, 'UGX', 'UGX'],
    +  'UZS': [0, 'so\u02bcm', 'UZS'],
    +  'VEF': [2, 'Bs', 'Bs'],
    +  'VUV': [0, 'VUV', 'VUV'],
    +  'WST': [2, 'WST', 'WST'],
    +  'XAF': [0, 'FCFA', 'FCFA'],
    +  'XCD': [2, '$', 'EC$'],
    +  'XOF': [0, 'CFA', 'CFA'],
    +  'XPF': [0, 'FCFP', 'FCFP'],
    +  'ZMK': [0, 'ZMK', 'ZMK']
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/currency_test.html b/mapbox-gl/closure-library/closure/goog/i18n/currency_test.html
    new file mode 100644
    index 0000000000..d6f1b1d4a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/currency_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.currency
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.currencyTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/currency_test.js b/mapbox-gl/closure-library/closure/goog/i18n/currency_test.js
    new file mode 100644
    index 0000000000..6d61642b3c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/currency_test.js
    @@ -0,0 +1,267 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.i18n.currencyTest');
    +goog.setTestOnly('goog.i18n.currencyTest');
    +
    +goog.require('goog.i18n.NumberFormat');
    +goog.require('goog.i18n.currency');
    +goog.require('goog.i18n.currency.CurrencyInfo');
    +goog.require('goog.object');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  stubs.replace(goog.i18n.currency, 'CurrencyInfo',
    +      goog.object.clone(goog.i18n.currency.CurrencyInfo));
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testAddTier2Support() {
    +  assertFalse('LRD' in goog.i18n.currency.CurrencyInfo);
    +  assertThrows(function() {
    +    goog.i18n.currency.getLocalCurrencyPattern('LRD');
    +  });
    +
    +  goog.i18n.currency.addTier2Support();
    +  assertTrue('LRD' in goog.i18n.currency.CurrencyInfo);
    +  assertEquals("'$'#,##0.00",
    +      goog.i18n.currency.getLocalCurrencyPattern('LRD'));
    +}
    +
    +function testCurrencyPattern() {
    +  assertEquals("'$'#,##0.00",
    +      goog.i18n.currency.getLocalCurrencyPattern('USD'));
    +  assertEquals("'US$'#,##0.00",
    +      goog.i18n.currency.getPortableCurrencyPattern('USD'));
    +  assertEquals("USD '$'#,##0.00",
    +      goog.i18n.currency.getGlobalCurrencyPattern('USD'));
    +
    +  assertEquals("'¥'#,##0",
    +      goog.i18n.currency.getLocalCurrencyPattern('JPY'));
    +  assertEquals("'JP¥'#,##0",
    +      goog.i18n.currency.getPortableCurrencyPattern('JPY'));
    +  assertEquals("JPY '¥'#,##0",
    +      goog.i18n.currency.getGlobalCurrencyPattern('JPY'));
    +
    +  assertEquals("'€'#,##0.00",
    +      goog.i18n.currency.getLocalCurrencyPattern('EUR'));
    +  assertEquals("'€'#,##0.00",
    +      goog.i18n.currency.getPortableCurrencyPattern('EUR'));
    +  assertEquals("EUR '€'#,##0.00",
    +      goog.i18n.currency.getGlobalCurrencyPattern('EUR'));
    +
    +  assertEquals("'¥'#,##0.00",
    +      goog.i18n.currency.getLocalCurrencyPattern('CNY'));
    +  assertEquals("'RMB¥'#,##0.00",
    +      goog.i18n.currency.getPortableCurrencyPattern('CNY'));
    +  assertEquals("CNY '¥'#,##0.00",
    +      goog.i18n.currency.getGlobalCurrencyPattern('CNY'));
    +
    +  assertEquals("'Rial'#,##0",
    +      goog.i18n.currency.getLocalCurrencyPattern('YER'));
    +  assertEquals("'Rial'#,##0",
    +      goog.i18n.currency.getPortableCurrencyPattern('YER'));
    +  assertEquals("YER 'Rial'#,##0",
    +      goog.i18n.currency.getGlobalCurrencyPattern('YER'));
    +
    +  assertEquals("'CHF'#,##0.00",
    +      goog.i18n.currency.getLocalCurrencyPattern('CHF'));
    +  assertEquals("'CHF'#,##0.00",
    +      goog.i18n.currency.getPortableCurrencyPattern('CHF'));
    +  assertEquals("'CHF'#,##0.00",
    +      goog.i18n.currency.getGlobalCurrencyPattern('CHF'));
    +}
    +
    +function testCurrencyFormatCHF() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('CHF'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('CHF123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('CHF'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('CHF123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('CHF'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('CHF123,456.79', str);
    +}
    +
    +function testCurrencyFormatYER() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('YER'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('Rial123,457', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('YER'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('Rial123,457', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('YER'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('YER Rial123,457', str);
    +}
    +
    +function testCurrencyFormatCNY() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('CNY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('¥123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('CNY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('RMB¥123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('CNY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('CNY ¥123,456.79', str);
    +}
    +
    +function testCurrencyFormatCZK() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('CZK'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('123,456.79 Kč', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('CZK'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('123,456.79 Kč', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('CZK'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('CZK 123,456.79 Kč', str);
    +}
    +
    +function testCurrencyFormatEUR() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('EUR'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('€123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('EUR'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('€123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('EUR'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('EUR €123,456.79', str);
    +}
    +
    +function testCurrencyFormatJPY() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('JPY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('¥123,457', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('JPY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('JP¥123,457', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('JPY'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('JPY ¥123,457', str);
    +}
    +
    +function testCurrencyFormatPLN() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('PLN'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('123,456.79 zł', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('PLN'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('123,456.79 zł', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('PLN'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('PLN 123,456.79 zł', str);
    +}
    +
    +function testCurrencyFormatUSD() {
    +  var formatter;
    +  var str;
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getLocalCurrencyPattern('USD'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('$123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getPortableCurrencyPattern('USD'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('US$123,456.79', str);
    +
    +  formatter = new goog.i18n.NumberFormat(
    +      goog.i18n.currency.getGlobalCurrencyPattern('USD'));
    +  str = formatter.format(123456.7899);
    +  assertEquals('USD $123,456.79', str);
    +}
    +
    +
    +function testIsPrefixSignPosition() {
    +  assertTrue(goog.i18n.currency.isPrefixSignPosition('USD'));
    +  assertTrue(goog.i18n.currency.isPrefixSignPosition('EUR'));
    +}
    +
    +function testGetCurrencySign() {
    +  assertEquals('USD $', goog.i18n.currency.getGlobalCurrencySign('USD'));
    +  assertEquals('$', goog.i18n.currency.getLocalCurrencySign('USD'));
    +  assertEquals('US$', goog.i18n.currency.getPortableCurrencySign('USD'));
    +
    +  assertEquals('YER Rial', goog.i18n.currency.getGlobalCurrencySign('YER'));
    +  assertEquals('Rial', goog.i18n.currency.getLocalCurrencySign('YER'));
    +  assertEquals('Rial', goog.i18n.currency.getPortableCurrencySign('YER'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/currencycodemap.js b/mapbox-gl/closure-library/closure/goog/i18n/currencycodemap.js
    new file mode 100644
    index 0000000000..eb23e00ed7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/currencycodemap.js
    @@ -0,0 +1,208 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Currency code map.
    + */
    +
    +
    +/**
    + * Namespace for locale number format functions
    + */
    +goog.provide('goog.i18n.currencyCodeMap');
    +goog.provide('goog.i18n.currencyCodeMapTier2');
    +
    +
    +/**
    + * The mapping of currency symbol through intl currency code.
    + * The source of information is mostly from wikipedia and CLDR. Since there is
    + * no authoritive source, items are judged by personal perception.
    +
    + * If an application need currency support that available in tier2, it
    + * should extend currencyCodeMap to include tier2 data by doing this:
    + *     goog.object.extend(goog.i18n.currencyCodeMap,
    + *                        goog.i18n.currencyCodeMapTier2);
    + *
    + * @const {!Object<string, string>}
    + */
    +goog.i18n.currencyCodeMap = {
    +  'AED': '\u062F\u002e\u0625',
    +  'ARS': '$',
    +  'AUD': '$',
    +  'BDT': '\u09F3',
    +  'BRL': 'R$',
    +  'CAD': '$',
    +  'CHF': 'Fr.',
    +  'CLP': '$',
    +  'CNY': '\u00a5',
    +  'COP': '$',
    +  'CRC': '\u20a1',
    +  'CUP': '$',
    +  'CZK': 'K\u010d',
    +  'DKK': 'kr',
    +  'DOP': '$',
    +  'EGP': '\u00a3',
    +  'EUR': '\u20ac',
    +  'GBP': '\u00a3',
    +  'HKD': '$',
    +  'HRK': 'kn',
    +  'HUF': 'Ft',
    +  'IDR': 'Rp',
    +  'ILS': '\u20AA',
    +  'INR': 'Rs',
    +  'IQD': '\u0639\u062F',
    +  'ISK': 'kr',
    +  'JMD': '$',
    +  'JPY': '\u00a5',
    +  'KRW': '\u20A9',
    +  'KWD': '\u062F\u002e\u0643',
    +  'LKR': 'Rs',
    +  'LVL': 'Ls',
    +  'MNT': '\u20AE',
    +  'MXN': '$',
    +  'MYR': 'RM',
    +  'NOK': 'kr',
    +  'NZD': '$',
    +  'PAB': 'B/.',
    +  'PEN': 'S/.',
    +  'PHP': 'P',
    +  'PKR': 'Rs.',
    +  'PLN': 'z\u0142',
    +  'RON': 'L',
    +  'RUB': '\u0440\u0443\u0431',
    +  'SAR': '\u0633\u002E\u0631',
    +  'SEK': 'kr',
    +  'SGD': '$',
    +  'SKK': 'Sk',
    +  'SYP': 'SYP',
    +  'THB': '\u0e3f',
    +  'TRY': 'TL',
    +  'TWD': 'NT$',
    +  'USD': '$',
    +  'UYU': '$',
    +  'VEF': 'Bs.F',
    +  'VND': '\u20AB',
    +  'XAF': 'FCFA',
    +  'XCD': '$',
    +  'YER': 'YER',
    +  'ZAR': 'R'
    +};
    +
    +
    +/**
    + * This group of currency data is unlikely to be used. In case they are,
    + * program need to merge it into goog.locale.CurrencyCodeMap.
    + *
    + * @const {!Object<string, string>}
    + */
    +goog.i18n.currencyCodeMapTier2 = {
    +  'AFN': '\u060b',
    +  'ALL': 'Lek',
    +  'AMD': '\u0564\u0580\u002e',
    +  'ANG': '\u0083',
    +  'AOA': 'Kz',
    +  'AWG': '\u0192',
    +  'AZN': 'm',
    +  'BAM': '\u041a\u041c',
    +  'BBD': '$',
    +  'BGN': '\u043b\u0432',
    +  'BHD': '\u0628\u002e\u062f\u002e',
    +  'BIF': 'FBu',
    +  'BMD': '$',
    +  'BND': '$',
    +  'BOB': 'B$',
    +  'BSD': '$',
    +  'BTN': 'Nu.',
    +  'BWP': 'P',
    +  'BYR': 'Br',
    +  'BZD': '$',
    +  'CDF': 'F',
    +  'CVE': '$',
    +  'DJF': 'Fdj',
    +  'DZD': '\u062f\u062C',
    +  'EEK': 'EEK',
    +  'ERN': 'Nfk',
    +  'ETB': 'Br',
    +  'FJD': '$',
    +  'FKP': '\u00a3',
    +  'GEL': 'GEL',
    +  'GHS': '\u20B5',
    +  'GIP': '\u00a3',
    +  'GMD': 'D',
    +  'GNF': 'FG',
    +  'GTQ': 'Q',
    +  'GYD': '$',
    +  'HNL': 'L',
    +  'HTG': 'G',
    +  'IRR': '\ufdfc',
    +  'JOD': 'JOD',
    +  'KES': 'KSh',
    +  'KGS': 'som',
    +  'KHR': '\u17DB',
    +  'KMF': 'KMF',
    +  'KPW': '\u20A9',
    +  'KYD': '$',
    +  'KZT': 'KZT',
    +  'LAK': '\u20AD',
    +  'LBP': '\u0644\u002e\u0644',
    +  'LRD': '$',
    +  'LSL': 'L',
    +  'LTL': 'Lt',
    +  'LYD': '\u0644\u002e\u062F',
    +  'MAD': '\u0645\u002E\u062F\u002E',
    +  'MDL': 'MDL',
    +  'MGA': 'MGA',
    +  'MKD': 'MKD',
    +  'MMK': 'K',
    +  'MOP': 'MOP$',
    +  'MRO': 'UM',
    +  'MUR': 'Rs',
    +  'MVR': 'Rf',
    +  'MWK': 'MK',
    +  'MZN': 'MTn',
    +  'NAD': '$',
    +  'NGN': '\u20A6',
    +  'NIO': 'C$',
    +  'NPR': 'Rs',
    +  'OMR': '\u0639\u002E\u062F\u002E',
    +  'PGK': 'K',
    +  'PYG': '\u20b2',
    +  'QAR': '\u0642\u002E\u0631',
    +  'RSD': '\u0420\u0421\u0414',
    +  'RWF': 'RF',
    +  'SBD': '$',
    +  'SCR': 'SR',
    +  'SDG': 'SDG',
    +  'SHP': '\u00a3',
    +  'SLL': 'Le',
    +  'SOS': 'So. Sh.',
    +  'SRD': '$',
    +  'STD': 'Db',
    +  'SZL': 'L',
    +  'TJS': 'TJS',
    +  'TMM': 'm',
    +  'TND': '\u062F\u002e\u062A ',
    +  'TOP': 'T$',
    +  'TTD': '$',
    +  'TZS': 'TZS',
    +  'UAH': 'UAH',
    +  'UGX': 'USh',
    +  'UZS': 'UZS',
    +  'VUV': 'Vt',
    +  'WST': 'WS$',
    +  'XOF': 'CFA',
    +  'XPF': 'F',
    +  'ZMK': 'ZK',
    +  'ZWD': '$'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat.js
    new file mode 100644
    index 0000000000..078c804eb3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat.js
    @@ -0,0 +1,758 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for dealing with date/time formatting.
    + */
    +
    +
    +/**
    + * Namespace for i18n date/time formatting functions
    + */
    +goog.provide('goog.i18n.DateTimeFormat');
    +goog.provide('goog.i18n.DateTimeFormat.Format');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.date');
    +goog.require('goog.i18n.DateTimeSymbols');
    +goog.require('goog.i18n.TimeZone');
    +goog.require('goog.string');
    +
    +
    +/**
    + * Datetime formatting functions following the pattern specification as defined
    + * in JDK, ICU and CLDR, with minor modification for typical usage in JS.
    + * Pattern specification: (Refer to JDK/ICU/CLDR)
    + * <pre>
    + * Symbol Meaning Presentation        Example
    + * ------   -------                 ------------        -------
    + * G        era designator          (Text)              AD
    + * y#       year                    (Number)            1996
    + * Y*       year (week of year)     (Number)            1997
    + * u*       extended year           (Number)            4601
    + * M        month in year           (Text & Number)     July & 07
    + * d        day in month            (Number)            10
    + * h        hour in am/pm (1~12)    (Number)            12
    + * H        hour in day (0~23)      (Number)            0
    + * m        minute in hour          (Number)            30
    + * s        second in minute        (Number)            55
    + * S        fractional second       (Number)            978
    + * E        day of week             (Text)              Tuesday
    + * e*       day of week (local 1~7) (Number)            2
    + * D*       day in year             (Number)            189
    + * F*       day of week in month    (Number)            2 (2nd Wed in July)
    + * w        week in year            (Number)            27
    + * W*       week in month           (Number)            2
    + * a        am/pm marker            (Text)              PM
    + * k        hour in day (1~24)      (Number)            24
    + * K        hour in am/pm (0~11)    (Number)            0
    + * z        time zone               (Text)              Pacific Standard Time
    + * Z        time zone (RFC 822)     (Number)            -0800
    + * v        time zone (generic)     (Text)              Pacific Time
    + * g*       Julian day              (Number)            2451334
    + * A*       milliseconds in day     (Number)            69540000
    + * '        escape for text         (Delimiter)         'Date='
    + * ''       single quote            (Literal)           'o''clock'
    + *
    + * Item marked with '*' are not supported yet.
    + * Item marked with '#' works different than java
    + *
    + * The count of pattern letters determine the format.
    + * (Text): 4 or more, use full form, <4, use short or abbreviated form if it
    + * exists. (e.g., "EEEE" produces "Monday", "EEE" produces "Mon")
    + *
    + * (Number): the minimum number of digits. Shorter numbers are zero-padded to
    + * this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
    + * specially; that is, if the count of 'y' is 2, the Year will be truncated to
    + * 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
    + * fields, fractional seconds are padded on the right with zero.
    + *
    + * (Text & Number): 3 or over, use text, otherwise use number. (e.g., "M"
    + * produces "1", "MM" produces "01", "MMM" produces "Jan", and "MMMM" produces
    + * "January".)
    + *
    + * Any characters in the pattern that are not in the ranges of ['a'..'z'] and
    + * ['A'..'Z'] will be treated as quoted text. For instance, characters like ':',
    + * '.', ' ', '#' and '@' will appear in the resulting time text even they are
    + * not embraced within single quotes.
    + * </pre>
    + */
    +
    +
    +
    +/**
    + * Construct a DateTimeFormat object based on current locale.
    + * @constructor
    + * @param {string|number} pattern pattern specification or pattern type.
    + * @param {!Object=} opt_dateTimeSymbols Optional symbols to use use for this
    + *     instance rather than the global symbols.
    + * @final
    + */
    +goog.i18n.DateTimeFormat = function(pattern, opt_dateTimeSymbols) {
    +  goog.asserts.assert(goog.isDef(pattern), 'Pattern must be defined');
    +  goog.asserts.assert(
    +      goog.isDef(opt_dateTimeSymbols) || goog.isDef(goog.i18n.DateTimeSymbols),
    +      'goog.i18n.DateTimeSymbols or explicit symbols must be defined');
    +
    +  this.patternParts_ = [];
    +
    +  /**
    +   * Data structure that with all the locale info needed for date formatting.
    +   * (day/month names, most common patterns, rules for week-end, etc.)
    +   * @type {!Object}
    +   * @private
    +   */
    +  this.dateTimeSymbols_ = opt_dateTimeSymbols || goog.i18n.DateTimeSymbols;
    +  if (typeof pattern == 'number') {
    +    this.applyStandardPattern_(pattern);
    +  } else {
    +    this.applyPattern_(pattern);
    +  }
    +};
    +
    +
    +/**
    + * Enum to identify predefined Date/Time format pattern.
    + * @enum {number}
    + */
    +goog.i18n.DateTimeFormat.Format = {
    +  FULL_DATE: 0,
    +  LONG_DATE: 1,
    +  MEDIUM_DATE: 2,
    +  SHORT_DATE: 3,
    +  FULL_TIME: 4,
    +  LONG_TIME: 5,
    +  MEDIUM_TIME: 6,
    +  SHORT_TIME: 7,
    +  FULL_DATETIME: 8,
    +  LONG_DATETIME: 9,
    +  MEDIUM_DATETIME: 10,
    +  SHORT_DATETIME: 11
    +};
    +
    +
    +/**
    + * regular expression pattern for parsing pattern string
    + * @type {Array<RegExp>}
    + * @private
    + */
    +goog.i18n.DateTimeFormat.TOKENS_ = [
    +  //quote string
    +  /^\'(?:[^\']|\'\')*\'/,
    +  // pattern chars
    +  /^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|m+|s+|v+|w+|z+|Z+)/,
    +  // and all the other chars
    +  /^[^\'GyMkSEahKHcLQdmsvwzZ]+/  // and all the other chars
    +];
    +
    +
    +/**
    + * These are token types, corresponding to above token definitions.
    + * @enum {number}
    + * @private
    + */
    +goog.i18n.DateTimeFormat.PartTypes_ = {
    +  QUOTED_STRING: 0,
    +  FIELD: 1,
    +  LITERAL: 2
    +};
    +
    +
    +/**
    + * Apply specified pattern to this formatter object.
    + * @param {string} pattern String specifying how the date should be formatted.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.applyPattern_ = function(pattern) {
    +  // lex the pattern, once for all uses
    +  while (pattern) {
    +    for (var i = 0; i < goog.i18n.DateTimeFormat.TOKENS_.length; ++i) {
    +      var m = pattern.match(goog.i18n.DateTimeFormat.TOKENS_[i]);
    +      if (m) {
    +        var part = m[0];
    +        pattern = pattern.substring(part.length);
    +        if (i == goog.i18n.DateTimeFormat.PartTypes_.QUOTED_STRING) {
    +          if (part == "''") {
    +            part = "'";  // '' -> '
    +          } else {
    +            part = part.substring(1, part.length - 1); // strip quotes
    +            part = part.replace(/\'\'/, "'");
    +          }
    +        }
    +        this.patternParts_.push({ text: part, type: i });
    +        break;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Format the given date object according to preset pattern and current lcoale.
    + * @param {goog.date.DateLike} date The Date object that is being formatted.
    + * @param {goog.i18n.TimeZone=} opt_timeZone optional, if specified, time
    + *    related fields will be formatted based on its setting. When this field
    + *    is not specified, "undefined" will be pass around and those function
    + *    that really need time zone service will create a default one.
    + * @return {string} Formatted string for the given date.
    + *    Throws an error if the date is null or if one tries to format a date-only
    + *    object (for instance goog.date.Date) using a pattern with time fields.
    + */
    +goog.i18n.DateTimeFormat.prototype.format = function(date, opt_timeZone) {
    +  if (!date)
    +    throw Error('The date to format must be non-null.');
    +
    +  // We don't want to write code to calculate each date field because we
    +  // want to maximize performance and minimize code size.
    +  // JavaScript only provide API to render local time.
    +  // Suppose target date is: 16:00 GMT-0400
    +  // OS local time is:       12:00 GMT-0800
    +  // We want to create a Local Date Object : 16:00 GMT-0800, and fix the
    +  // time zone display ourselves.
    +  // Thing get a little bit tricky when daylight time transition happens. For
    +  // example, suppose OS timeZone is America/Los_Angeles, it is impossible to
    +  // represent "2006/4/2 02:30" even for those timeZone that has no transition
    +  // at this time. Because 2:00 to 3:00 on that day does not exising in
    +  // America/Los_Angeles time zone. To avoid calculating date field through
    +  // our own code, we uses 3 Date object instead, one for "Year, month, day",
    +  // one for time within that day, and one for timeZone object since it need
    +  // the real time to figure out actual time zone offset.
    +  var diff = opt_timeZone ?
    +      (date.getTimezoneOffset() - opt_timeZone.getOffset(date)) * 60000 : 0;
    +  var dateForDate = diff ? new Date(date.getTime() + diff) : date;
    +  var dateForTime = dateForDate;
    +  // in daylight time switch on/off hour, diff adjustment could alter time
    +  // because of timeZone offset change, move 1 day forward or backward.
    +  if (opt_timeZone &&
    +      dateForDate.getTimezoneOffset() != date.getTimezoneOffset()) {
    +    diff += diff > 0 ? -goog.date.MS_PER_DAY : goog.date.MS_PER_DAY;
    +    dateForTime = new Date(date.getTime() + diff);
    +  }
    +
    +  var out = [];
    +  for (var i = 0; i < this.patternParts_.length; ++i) {
    +    var text = this.patternParts_[i].text;
    +    if (goog.i18n.DateTimeFormat.PartTypes_.FIELD ==
    +        this.patternParts_[i].type) {
    +      out.push(this.formatField_(text, date, dateForDate, dateForTime,
    +                                 opt_timeZone));
    +    } else {
    +      out.push(text);
    +    }
    +  }
    +  return out.join('');
    +};
    +
    +
    +/**
    + * Apply a predefined pattern as identified by formatType, which is stored in
    + * locale specific repository.
    + * @param {number} formatType A number that identified the predefined pattern.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.applyStandardPattern_ =
    +    function(formatType) {
    +  var pattern;
    +  if (formatType < 4) {
    +    pattern = this.dateTimeSymbols_.DATEFORMATS[formatType];
    +  } else if (formatType < 8) {
    +    pattern = this.dateTimeSymbols_.TIMEFORMATS[formatType - 4];
    +  } else if (formatType < 12) {
    +    pattern = this.dateTimeSymbols_.DATETIMEFORMATS[formatType - 8];
    +    pattern = pattern.replace('{1}',
    +        this.dateTimeSymbols_.DATEFORMATS[formatType - 8]);
    +    pattern = pattern.replace('{0}',
    +        this.dateTimeSymbols_.TIMEFORMATS[formatType - 8]);
    +  } else {
    +    this.applyStandardPattern_(goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +    return;
    +  }
    +  this.applyPattern_(pattern);
    +};
    +
    +
    +/**
    + * Localizes a string potentially containing numbers, replacing ASCII digits
    + * with native digits if specified so by the locale. Leaves other characters.
    + * @param {string} input the string to be localized, using ASCII digits.
    + * @return {string} localized string, potentially using native digits.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.localizeNumbers_ = function(input) {
    +  return goog.i18n.DateTimeFormat.localizeNumbers(input, this.dateTimeSymbols_);
    +};
    +
    +
    +/**
    + * Localizes a string potentially containing numbers, replacing ASCII digits
    + * with native digits if specified so by the locale. Leaves other characters.
    + * @param {number|string} input the string to be localized, using ASCII digits.
    + * @param {!Object=} opt_dateTimeSymbols Optional symbols to use use rather than
    + *     the global symbols.
    + * @return {string} localized string, potentially using native digits.
    + */
    +goog.i18n.DateTimeFormat.localizeNumbers =
    +    function(input, opt_dateTimeSymbols) {
    +  input = String(input);
    +  var dateTimeSymbols = opt_dateTimeSymbols || goog.i18n.DateTimeSymbols;
    +  if (dateTimeSymbols.ZERODIGIT === undefined) {
    +    return input;
    +  }
    +
    +  var parts = [];
    +  for (var i = 0; i < input.length; i++) {
    +    var c = input.charCodeAt(i);
    +    parts.push((0x30 <= c && c <= 0x39) ? // '0' <= c <= '9'
    +        String.fromCharCode(dateTimeSymbols.ZERODIGIT + c - 0x30) :
    +        input.charAt(i));
    +  }
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Formats Era field according to pattern specified.
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatEra_ = function(count, date) {
    +  var value = date.getFullYear() > 0 ? 1 : 0;
    +  return count >= 4 ? this.dateTimeSymbols_.ERANAMES[value] :
    +                      this.dateTimeSymbols_.ERAS[value];
    +};
    +
    +
    +/**
    + * Formats Year field according to pattern specified
    + *   Javascript Date object seems incapable handling 1BC and
    + *   year before. It can show you year 0 which does not exists.
    + *   following we just keep consistent with javascript's
    + *   toString method. But keep in mind those things should be
    + *   unsupported.
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatYear_ = function(count, date) {
    +  var value = date.getFullYear();
    +  if (value < 0) {
    +    value = -value;
    +  }
    +  if (count == 2) {
    +    // See comment about special casing 'yy' at the start of the file, this
    +    // matches ICU and CLDR behaviour. See also:
    +    // http://icu-project.org/apiref/icu4j/com/ibm/icu/text/SimpleDateFormat.html
    +    // http://www.unicode.org/reports/tr35/tr35-dates.html
    +    value = value % 100;
    +  }
    +  return this.localizeNumbers_(goog.string.padNumber(value, count));
    +};
    +
    +
    +/**
    + * Formats Month field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatMonth_ = function(count, date) {
    +  var value = date.getMonth();
    +  switch (count) {
    +    case 5: return this.dateTimeSymbols_.NARROWMONTHS[value];
    +    case 4: return this.dateTimeSymbols_.MONTHS[value];
    +    case 3: return this.dateTimeSymbols_.SHORTMONTHS[value];
    +    default:
    +      return this.localizeNumbers_(goog.string.padNumber(value + 1, count));
    +  }
    +};
    +
    +
    +/**
    + * Validates is the goog.date.DateLike object to format has a time.
    + * DateLike means Date|goog.date.Date, and goog.date.DateTime inherits
    + * from goog.date.Date. But goog.date.Date does not have time related
    + * members (getHours, getMinutes, getSeconds).
    + * Formatting can be done, if there are no time placeholders in the pattern.
    + *
    + * @param {!goog.date.DateLike} date the object to validate.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.validateDateHasTime_ = function(date) {
    +  if (date.getHours && date.getSeconds && date.getMinutes)
    +    return;
    +  // if (date instanceof Date || date instanceof goog.date.DateTime)
    +  throw Error('The date to format has no time (probably a goog.date.Date). ' +
    +      'Use Date or goog.date.DateTime, or use a pattern without time fields.');
    +};
    +
    +
    +/**
    + * Formats (1..24) Hours field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats. This controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.format24Hours_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(
    +      goog.string.padNumber(date.getHours() || 24, count));
    +};
    +
    +
    +/**
    + * Formats Fractional seconds field according to pattern
    + * specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + *
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatFractionalSeconds_ =
    +    function(count, date) {
    +  // Fractional seconds left-justify, append 0 for precision beyond 3
    +  var value = date.getTime() % 1000 / 1000;
    +  return this.localizeNumbers_(
    +      value.toFixed(Math.min(3, count)).substr(2) +
    +      (count > 3 ? goog.string.padNumber(0, count - 3) : ''));
    +};
    +
    +
    +/**
    + * Formats Day of week field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatDayOfWeek_ =
    +    function(count, date) {
    +  var value = date.getDay();
    +  return count >= 4 ? this.dateTimeSymbols_.WEEKDAYS[value] :
    +                      this.dateTimeSymbols_.SHORTWEEKDAYS[value];
    +};
    +
    +
    +/**
    + * Formats Am/Pm field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatAmPm_ = function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  var hours = date.getHours();
    +  return this.dateTimeSymbols_.AMPMS[hours >= 12 && hours < 24 ? 1 : 0];
    +};
    +
    +
    +/**
    + * Formats (1..12) Hours field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.format1To12Hours_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(
    +      goog.string.padNumber(date.getHours() % 12 || 12, count));
    +};
    +
    +
    +/**
    + * Formats (0..11) Hours field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.format0To11Hours_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(
    +      goog.string.padNumber(date.getHours() % 12, count));
    +};
    +
    +
    +/**
    + * Formats (0..23) Hours field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.format0To23Hours_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(goog.string.padNumber(date.getHours(), count));
    +};
    +
    +
    +/**
    + * Formats Standalone weekday field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatStandaloneDay_ =
    +    function(count, date) {
    +  var value = date.getDay();
    +  switch (count) {
    +    case 5:
    +      return this.dateTimeSymbols_.STANDALONENARROWWEEKDAYS[value];
    +    case 4:
    +      return this.dateTimeSymbols_.STANDALONEWEEKDAYS[value];
    +    case 3:
    +      return this.dateTimeSymbols_.STANDALONESHORTWEEKDAYS[value];
    +    default:
    +      return this.localizeNumbers_(goog.string.padNumber(value, 1));
    +  }
    +};
    +
    +
    +/**
    + * Formats Standalone Month field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatStandaloneMonth_ =
    +    function(count, date) {
    +  var value = date.getMonth();
    +  switch (count) {
    +    case 5:
    +      return this.dateTimeSymbols_.STANDALONENARROWMONTHS[value];
    +    case 4:
    +      return this.dateTimeSymbols_.STANDALONEMONTHS[value];
    +    case 3:
    +      return this.dateTimeSymbols_.STANDALONESHORTMONTHS[value];
    +    default:
    +      return this.localizeNumbers_(goog.string.padNumber(value + 1, count));
    +  }
    +};
    +
    +
    +/**
    + * Formats Quarter field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatQuarter_ =
    +    function(count, date) {
    +  var value = Math.floor(date.getMonth() / 3);
    +  return count < 4 ? this.dateTimeSymbols_.SHORTQUARTERS[value] :
    +                     this.dateTimeSymbols_.QUARTERS[value];
    +};
    +
    +
    +/**
    + * Formats Date field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatDate_ = function(count, date) {
    +  return this.localizeNumbers_(goog.string.padNumber(date.getDate(), count));
    +};
    +
    +
    +/**
    + * Formats Minutes field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatMinutes_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(goog.string.padNumber(date.getMinutes(), count));
    +};
    +
    +
    +/**
    + * Formats Seconds field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatSeconds_ =
    +    function(count, date) {
    +  goog.i18n.DateTimeFormat.validateDateHasTime_(date);
    +  return this.localizeNumbers_(goog.string.padNumber(date.getSeconds(), count));
    +};
    +
    +
    +/**
    + * Formats the week of year field according to pattern specified
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatWeekOfYear_ = function(count, date) {
    +
    +
    +  var weekNum = goog.date.getWeekNumber(
    +      date.getFullYear(), date.getMonth(), date.getDate(),
    +      this.dateTimeSymbols_.FIRSTWEEKCUTOFFDAY,
    +      this.dateTimeSymbols_.FIRSTDAYOFWEEK);
    +
    +  return this.localizeNumbers_(goog.string.padNumber(weekNum, count));
    +};
    +
    +
    +/**
    + * Formats TimeZone field following RFC
    + *
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date It holds the date object to be formatted.
    + * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
    + * @return {string} Formatted string that represent this field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatTimeZoneRFC_ =
    +    function(count, date, opt_timeZone) {
    +  opt_timeZone = opt_timeZone ||
    +      goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
    +
    +  // RFC 822 formats should be kept in ASCII, but localized GMT formats may need
    +  // to use native digits.
    +  return count < 4 ? opt_timeZone.getRFCTimeZoneString(date) :
    +                     this.localizeNumbers_(opt_timeZone.getGMTString(date));
    +};
    +
    +
    +/**
    + * Generate GMT timeZone string for given date
    + * @param {number} count Number of time pattern char repeats, it controls
    + *     how a field should be formatted.
    + * @param {!goog.date.DateLike} date Whose value being evaluated.
    + * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
    + * @return {string} GMT timeZone string.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatTimeZone_ =
    +    function(count, date, opt_timeZone) {
    +  opt_timeZone = opt_timeZone ||
    +      goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
    +  return count < 4 ? opt_timeZone.getShortName(date) :
    +             opt_timeZone.getLongName(date);
    +};
    +
    +
    +/**
    + * Generate GMT timeZone string for given date
    + * @param {!goog.date.DateLike} date Whose value being evaluated.
    + * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
    + * @return {string} GMT timeZone string.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatTimeZoneId_ =
    +    function(date, opt_timeZone) {
    +  opt_timeZone = opt_timeZone ||
    +      goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
    +  return opt_timeZone.getTimeZoneId();
    +};
    +
    +
    +/**
    + * Formatting one date field.
    + * @param {string} patternStr The pattern string for the field being formatted.
    + * @param {!goog.date.DateLike} date represents the real date to be formatted.
    + * @param {!goog.date.DateLike} dateForDate used to resolve date fields
    + *     for formatting.
    + * @param {!goog.date.DateLike} dateForTime used to resolve time fields
    + *     for formatting.
    + * @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
    + * @return {string} string representation for the given field.
    + * @private
    + */
    +goog.i18n.DateTimeFormat.prototype.formatField_ =
    +    function(patternStr, date, dateForDate, dateForTime, opt_timeZone) {
    +  var count = patternStr.length;
    +  switch (patternStr.charAt(0)) {
    +    case 'G': return this.formatEra_(count, dateForDate);
    +    case 'y': return this.formatYear_(count, dateForDate);
    +    case 'M': return this.formatMonth_(count, dateForDate);
    +    case 'k': return this.format24Hours_(count, dateForTime);
    +    case 'S': return this.formatFractionalSeconds_(count, dateForTime);
    +    case 'E': return this.formatDayOfWeek_(count, dateForDate);
    +    case 'a': return this.formatAmPm_(count, dateForTime);
    +    case 'h': return this.format1To12Hours_(count, dateForTime);
    +    case 'K': return this.format0To11Hours_(count, dateForTime);
    +    case 'H': return this.format0To23Hours_(count, dateForTime);
    +    case 'c': return this.formatStandaloneDay_(count, dateForDate);
    +    case 'L': return this.formatStandaloneMonth_(count, dateForDate);
    +    case 'Q': return this.formatQuarter_(count, dateForDate);
    +    case 'd': return this.formatDate_(count, dateForDate);
    +    case 'm': return this.formatMinutes_(count, dateForTime);
    +    case 's': return this.formatSeconds_(count, dateForTime);
    +    case 'v': return this.formatTimeZoneId_(date, opt_timeZone);
    +    case 'w': return this.formatWeekOfYear_(count, dateForTime);
    +    case 'z': return this.formatTimeZone_(count, date, opt_timeZone);
    +    case 'Z': return this.formatTimeZoneRFC_(count, date, opt_timeZone);
    +    default: return '';
    +  }
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.html b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.html
    new file mode 100644
    index 0000000000..1d25722e2a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.DateTimeFormat
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.DateTimeFormatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.js
    new file mode 100644
    index 0000000000..0cb70c642c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeformat_test.js
    @@ -0,0 +1,768 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.DateTimeFormatTest');
    +goog.setTestOnly('goog.i18n.DateTimeFormatTest');
    +
    +goog.require('goog.date.Date');
    +goog.require('goog.date.DateTime');
    +goog.require('goog.i18n.DateTimeFormat');
    +goog.require('goog.i18n.DateTimePatterns');
    +goog.require('goog.i18n.DateTimePatterns_de');
    +goog.require('goog.i18n.DateTimePatterns_en');
    +goog.require('goog.i18n.DateTimePatterns_fa');
    +goog.require('goog.i18n.DateTimePatterns_fr');
    +goog.require('goog.i18n.DateTimePatterns_ja');
    +goog.require('goog.i18n.DateTimePatterns_sv');
    +goog.require('goog.i18n.DateTimeSymbols');
    +goog.require('goog.i18n.DateTimeSymbols_ar_AE');
    +goog.require('goog.i18n.DateTimeSymbols_ar_SA');
    +goog.require('goog.i18n.DateTimeSymbols_bn_BD');
    +goog.require('goog.i18n.DateTimeSymbols_de');
    +goog.require('goog.i18n.DateTimeSymbols_en');
    +goog.require('goog.i18n.DateTimeSymbols_en_GB');
    +goog.require('goog.i18n.DateTimeSymbols_en_IE');
    +goog.require('goog.i18n.DateTimeSymbols_en_IN');
    +goog.require('goog.i18n.DateTimeSymbols_en_US');
    +goog.require('goog.i18n.DateTimeSymbols_fa');
    +goog.require('goog.i18n.DateTimeSymbols_fr');
    +goog.require('goog.i18n.DateTimeSymbols_fr_DJ');
    +goog.require('goog.i18n.DateTimeSymbols_he_IL');
    +goog.require('goog.i18n.DateTimeSymbols_ja');
    +goog.require('goog.i18n.DateTimeSymbols_ro_RO');
    +goog.require('goog.i18n.DateTimeSymbols_sv');
    +goog.require('goog.i18n.TimeZone');
    +goog.require('goog.testing.jsunit');
    +
    +// Initial values
    +goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en;
    +goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +
    +function tearDown() {
    +  // We always revert to a known state
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +}
    +
    +// Helpers to make tests work regardless of the timeZone we're in.
    +function timezoneString(date) {
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
    +  return timeZone.getShortName(date);
    +}
    +
    +function timezoneId(date) {
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(
    +      date.getTimezoneOffset());
    +  return timeZone.getTimeZoneId(date);
    +}
    +
    +function timezoneStringRFC(date) {
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
    +  return timeZone.getRFCTimeZoneString(date);
    +}
    +
    +// Where could such data be found
    +// In js_i18n_data in http://go/i18n_dir, we have a bunch of files with names
    +// like TimeZoneConstant__<locale>.js
    +// We strongly discourage you to use them directly as those data can make
    +// your client code bloated. You should try to provide this data from server
    +// in a selective manner. In typical scenario, user's time zone is retrieved
    +// and only data for that time zone should be provided.
    +var americaLosAngelesData = {
    +  'transitions': [
    +    2770, 60, 7137, 0, 11506, 60, 16041, 0, 20410, 60, 24777, 0, 29146, 60,
    +    33513, 0, 35194, 60, 42249, 0, 45106, 60, 50985, 0, 55354, 60, 59889, 0,
    +    64090, 60, 68625, 0, 72994, 60, 77361, 0, 81730, 60, 86097, 0, 90466, 60,
    +    94833, 0, 99202, 60, 103569, 0, 107938, 60, 112473, 0, 116674, 60, 121209,
    +    0, 125578, 60, 129945, 0, 134314, 60, 138681, 0, 143050, 60, 147417, 0,
    +    151282, 60, 156153, 0, 160018, 60, 165057, 0, 168754, 60, 173793, 0,
    +    177490, 60, 182529, 0, 186394, 60, 191265, 0, 195130, 60, 200001, 0,
    +    203866, 60, 208905, 0, 212602, 60, 217641, 0, 221338, 60, 226377, 0,
    +    230242, 60, 235113, 0, 238978, 60, 243849, 0, 247714, 60, 252585, 0,
    +    256450, 60, 261489, 0, 265186, 60, 270225, 0, 273922, 60, 278961, 0,
    +    282826, 60, 287697, 0, 291562, 60, 296433, 0, 300298, 60, 305337, 0,
    +    309034, 60, 314073, 0, 317770, 60, 322809, 0, 326002, 60, 331713, 0,
    +    334738, 60, 340449, 0, 343474, 60, 349185, 0, 352378, 60, 358089, 0,
    +    361114, 60, 366825, 0, 369850, 60, 375561, 0, 378586, 60, 384297, 0,
    +    387322, 60, 393033, 0, 396058, 60, 401769, 0, 404962, 60, 410673, 0,
    +    413698, 60, 419409, 0, 422434, 60, 428145, 0, 431170, 60, 436881, 0,
    +    439906, 60, 445617, 0, 448810, 60, 454521, 0, 457546, 60, 463257, 0,
    +    466282, 60, 471993, 0, 475018, 60, 480729, 0, 483754, 60, 489465, 0,
    +    492490, 60, 498201, 0, 501394, 60, 507105, 0, 510130, 60, 515841, 0,
    +    518866, 60, 524577, 0, 527602, 60, 533313, 0, 536338, 60, 542049, 0,
    +    545242, 60, 550953, 0, 553978, 60, 559689, 0, 562714, 60, 568425, 0,
    +    571450, 60, 577161, 0, 580186, 60, 585897, 0, 588922, 60, 594633, 0
    +  ],
    +  'names': ['PST', 'Pacific Standard Time', 'PDT', 'Pacific Daylight Time'],
    +  'id': 'America/Los_Angeles',
    +  'std_offset': -480
    +};
    +
    +var europeBerlinData = {
    +  'transitions': [
    +    89953, 60, 94153, 0, 98521, 60, 102889, 0, 107257, 60, 111625, 0,
    +    115993, 60, 120361, 0, 124729, 60, 129265, 0, 133633, 60, 138001, 0,
    +    142369, 60, 146737, 0, 151105, 60, 155473, 0, 159841, 60, 164209, 0,
    +    168577, 60, 172945, 0, 177313, 60, 181849, 0, 186217, 60, 190585, 0,
    +    194953, 60, 199321, 0, 203689, 60, 208057, 0, 212425, 60, 216793, 0,
    +    221161, 60, 225529, 0, 230065, 60, 235105, 0, 238801, 60, 243841, 0,
    +    247537, 60, 252577, 0, 256273, 60, 261481, 0, 265009, 60, 270217, 0,
    +    273745, 60, 278953, 0, 282649, 60, 287689, 0, 291385, 60, 296425, 0,
    +    300121, 60, 305329, 0, 308857, 60, 314065, 0, 317593, 60, 322801, 0,
    +    326329, 60, 331537, 0, 335233, 60, 340273, 0, 343969, 60, 349009, 0,
    +    352705, 60, 357913, 0, 361441, 60, 366649, 0, 370177, 60, 375385, 0,
    +    379081, 60, 384121, 0, 387817, 60, 392857, 0, 396553, 60, 401593, 0,
    +    405289, 60, 410497, 0, 414025, 60, 419233, 0, 422761, 60, 427969, 0,
    +    431665, 60, 436705, 0, 440401, 60, 445441, 0, 449137, 60, 454345, 0,
    +    457873, 60, 463081, 0, 466609, 60, 471817, 0, 475513, 60, 480553, 0,
    +    484249, 60, 489289, 0, 492985, 60, 498025, 0, 501721, 60, 506929, 0,
    +    510457, 60, 515665, 0, 519193, 60, 524401, 0, 528097, 60, 533137, 0,
    +    536833, 60, 541873, 0, 545569, 60, 550777, 0, 554305, 60, 559513, 0,
    +    563041, 60, 568249, 0, 571777, 60, 576985, 0, 580681, 60, 585721, 0,
    +    589417, 60, 594457, 0],
    +  'names': ['MEZ', 'Mitteleurop\u00e4ische Zeit',
    +            'MESZ', 'Mitteleurop\u00e4ische Sommerzeit'],
    +  'id': 'Europe/Berlin',
    +  'std_offset': 60
    +};
    +
    +var date;
    +
    +function testHHmmss() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('HH:mm:ss');
    +  assertEquals('13:10:10', fmt.format(date));
    +}
    +
    +function testhhmmssa() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('h:mm:ss a');
    +  assertEquals('1:10:10 nachm.', fmt.format(date));
    +}
    +
    +function testEEEMMMddyy() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('EEE, MMM d, yy');
    +  assertEquals('Do., Juli 27, 06', fmt.format(date));
    +}
    +
    +function testEEEEMMMddyy() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('EEEE,MMMM dd, yyyy');
    +  assertEquals('Donnerstag,Juli 27, 2006', fmt.format(date));
    +}
    +
    +function testyyyyMMddG() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10, 250));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(420,
    +      goog.i18n.DateTimeSymbols_de);
    +  var fmt = new goog.i18n.DateTimeFormat('yyyy.MM.dd G \'at\' HH:mm:ss vvvv');
    +  assertEquals('2006.07.27 n. Chr. at 06:10:10 Etc/GMT+7',
    +               fmt.format(date, timeZone));
    +}
    +
    +function testyyyyyMMMMM() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('yyyyy.MMMMM.dd GGG hh:mm aaa');
    +  assertEquals('02006.J.27 n. Chr. 01:10 nachm.', fmt.format(date));
    +
    +  date = new Date(972, 11, 25, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('yyyyy.MMMMM.dd');
    +  assertEquals('00972.D.25', fmt.format(date));
    +}
    +
    +function testQQQQyy() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 0, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('QQQQ yy');
    +  assertEquals('1. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 1, 27, 13, 10, 10, 250);
    +  assertEquals('1. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 2, 27, 13, 10, 10, 250);
    +  assertEquals('1. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 3, 27, 13, 10, 10, 250);
    +  assertEquals('2. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 4, 27, 13, 10, 10, 250);
    +  assertEquals('2. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 5, 27, 13, 10, 10, 250);
    +  assertEquals('2. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  assertEquals('3. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 7, 27, 13, 10, 10, 250);
    +  assertEquals('3. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 8, 27, 13, 10, 10, 250);
    +  assertEquals('3. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 9, 27, 13, 10, 10, 250);
    +  assertEquals('4. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 10, 27, 13, 10, 10, 250);
    +  assertEquals('4. Quartal 06', fmt.format(date));
    +  date = new Date(2006, 11, 27, 13, 10, 10, 250);
    +  assertEquals('4. Quartal 06', fmt.format(date));
    +}
    +
    +function testQQyyyy() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 0, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('QQ yyyy');
    +  assertEquals('Q1 2006', fmt.format(date));
    +  date = new Date(2006, 1, 27, 13, 10, 10, 250);
    +  assertEquals('Q1 2006', fmt.format(date));
    +  date = new Date(2006, 2, 27, 13, 10, 10, 250);
    +  assertEquals('Q1 2006', fmt.format(date));
    +  date = new Date(2006, 3, 27, 13, 10, 10, 250);
    +  assertEquals('Q2 2006', fmt.format(date));
    +  date = new Date(2006, 4, 27, 13, 10, 10, 250);
    +  assertEquals('Q2 2006', fmt.format(date));
    +  date = new Date(2006, 5, 27, 13, 10, 10, 250);
    +  assertEquals('Q2 2006', fmt.format(date));
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  assertEquals('Q3 2006', fmt.format(date));
    +  date = new Date(2006, 7, 27, 13, 10, 10, 250);
    +  assertEquals('Q3 2006', fmt.format(date));
    +  date = new Date(2006, 8, 27, 13, 10, 10, 250);
    +  assertEquals('Q3 2006', fmt.format(date));
    +  date = new Date(2006, 9, 27, 13, 10, 10, 250);
    +  assertEquals('Q4 2006', fmt.format(date));
    +  date = new Date(2006, 10, 27, 13, 10, 10, 250);
    +  assertEquals('Q4 2006', fmt.format(date));
    +  date = new Date(2006, 11, 27, 13, 10, 10, 250);
    +  assertEquals('Q4 2006', fmt.format(date));
    +}
    +
    +function testMMddyyyyHHmmsszzz() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzz');
    +  assertEquals('07/27/2006 13:10:10 ' + timezoneString(date),
    +      fmt.format(date));
    +}
    +
    +function testMMddyyyyHHmmssZ() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('07/27/2006 13:10:10 ' + timezoneStringRFC(date),
    +      fmt.format(date));
    +}
    +
    +function testPatternMonthDayMedium() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.MONTH_DAY_MEDIUM);
    +  assertEquals('27. Juli', fmt.format(date));
    +}
    +
    +function testPatternDayOfWeekMonthDayMedium() {
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.WEEKDAY_MONTH_DAY_MEDIUM);
    +  assertEquals('Thu, Jul 27', fmt.format(date));
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.WEEKDAY_MONTH_DAY_MEDIUM);
    +  assertEquals('Do., 27. Juli', fmt.format(date));
    +}
    +
    +function testPatternDayOfWeekMonthDayYearMedium() {
    +  date = new Date(2012, 5, 28, 13, 10, 10, 250);
    +
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.WEEKDAY_MONTH_DAY_YEAR_MEDIUM);
    +  assertEquals('Thu, Jun 28, 2012', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.MONTH_DAY_YEAR_MEDIUM);
    +  assertEquals('Jun 28, 2012', fmt.format(date));
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sv;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sv;
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.WEEKDAY_MONTH_DAY_YEAR_MEDIUM);
    +  assertEquals('tors 28 juni 2012', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.MONTH_DAY_YEAR_MEDIUM);
    +  assertEquals('28 juni 2012', fmt.format(date));
    +}
    +
    +function testQuote() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var fmt = new goog.i18n.DateTimeFormat('HH \'o\'\'clock\'');
    +  assertEquals('13 o\'clock', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat('HH \'oclock\'');
    +  assertEquals('13 oclock', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat('HH \'\'');
    +  assertEquals('13 \'', fmt.format(date));
    +}
    +
    +function testFractionalSeconds() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 6, 27, 13, 10, 10, 256);
    +  var fmt = new goog.i18n.DateTimeFormat('s:S');
    +  assertEquals('10:3', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('s:SS');
    +  assertEquals('10:26', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('s:SSS');
    +  assertEquals('10:256', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('s:SSSS');
    +  assertEquals('10:2560', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('s:SSSSS');
    +  assertEquals('10:25600', fmt.format(date));
    +}
    +
    +function testPredefinedFormatter() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  date = new Date(2006, 7, 4, 13, 49, 24, 000);
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +  assertEquals('Freitag, 4. August 2006', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.LONG_DATE);
    +  assertEquals('4. August 2006', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATE);
    +  assertEquals('04.08.2006', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.SHORT_DATE);
    +  assertEquals('04.08.06', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.FULL_TIME);
    +  assertEquals('13:49:24 ' + timezoneString(date), fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.LONG_TIME);
    +  assertEquals('13:49:24 ' + timezoneString(date), fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_TIME);
    +  assertEquals('13:49:24', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.SHORT_TIME);
    +  assertEquals('13:49', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATETIME);
    +  assertEquals('Freitag, 4. August 2006 um 13:49:24 ' + timezoneString(date),
    +      fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.LONG_DATETIME);
    +  assertEquals('4. August 2006 um 13:49:24 ' + timezoneString(date),
    +      fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +  assertEquals('04.08.2006, 13:49:24', fmt.format(date));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.SHORT_DATETIME);
    +  assertEquals('04.08.06, 13:49', fmt.format(date));
    +}
    +
    +function testMMddyyyyHHmmssZSimpleTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(480);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('07/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZ');
    +  assertEquals('07/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZ');
    +  assertEquals('07/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZZ');
    +  assertEquals('07/27/2006 05:10:10 GMT-08:00', fmt.format(date, timeZone));
    +}
    +
    +
    +function testMMddyyyyHHmmssZCommonTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('07/27/2006 06:10:10 -0700', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZ');
    +  assertEquals('07/27/2006 06:10:10 -0700', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZ');
    +  assertEquals('07/27/2006 06:10:10 -0700', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZZ');
    +  assertEquals('07/27/2006 06:10:10 GMT-07:00', fmt.format(date, timeZone));
    +  date = new Date(Date.UTC(2006, 1, 27, 13, 10, 10));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('02/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZ');
    +  assertEquals('02/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZ');
    +  assertEquals('02/27/2006 05:10:10 -0800', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss ZZZZ');
    +  assertEquals('02/27/2006 05:10:10 GMT-08:00', fmt.format(date, timeZone));
    +}
    +
    +function testMMddyyyyHHmmsszSimpleTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(420);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('07/27/2006 06:10:10 UTC-7', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zz');
    +  assertEquals('07/27/2006 06:10:10 UTC-7', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzz');
    +  assertEquals('07/27/2006 06:10:10 UTC-7', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzzz');
    +  assertEquals('07/27/2006 06:10:10 UTC-7', fmt.format(date, timeZone));
    +}
    +
    +
    +function testMMddyyyyHHmmsszCommonTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('07/27/2006 06:10:10 PDT', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zz');
    +  assertEquals('07/27/2006 06:10:10 PDT', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzz');
    +  assertEquals('07/27/2006 06:10:10 PDT', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzzz');
    +  assertEquals('07/27/2006 06:10:10 Pacific Daylight Time',
    +               fmt.format(date, timeZone));
    +  date = new Date(Date.UTC(2006, 1, 27, 13, 10, 10));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('02/27/2006 05:10:10 PST', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zz');
    +  assertEquals('02/27/2006 05:10:10 PST', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzz');
    +  assertEquals('02/27/2006 05:10:10 PST', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzzz');
    +  assertEquals('02/27/2006 05:10:10 Pacific Standard Time',
    +      fmt.format(date, timeZone));
    +
    +  timeZone = goog.i18n.TimeZone.createTimeZone(europeBerlinData);
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('02/27/2006 14:10:10 MEZ', fmt.format(date, timeZone));
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss zzzz');
    +  assertEquals('02/27/2006 14:10:10 Mitteleurop\u00e4ische Zeit',
    +      fmt.format(date, timeZone));
    +}
    +
    +function testMMddyyyyHHmmssvCommonTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss v');
    +  assertEquals('07/27/2006 06:10:10 America/Los_Angeles',
    +               fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vv');
    +  assertEquals('07/27/2006 06:10:10 America/Los_Angeles',
    +               fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vvv');
    +  assertEquals('07/27/2006 06:10:10 America/Los_Angeles',
    +               fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vvvv');
    +  assertEquals('07/27/2006 06:10:10 America/Los_Angeles',
    +      fmt.format(date, timeZone));
    +}
    +
    +function testMMddyyyyHHmmssvSimpleTimeZone() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 13, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(420);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss v');
    +  assertEquals('07/27/2006 06:10:10 Etc/GMT+7', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vv');
    +  assertEquals('07/27/2006 06:10:10 Etc/GMT+7', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vvv');
    +  assertEquals('07/27/2006 06:10:10 Etc/GMT+7', fmt.format(date, timeZone));
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss vvvv');
    +  assertEquals('07/27/2006 06:10:10 Etc/GMT+7', fmt.format(date, timeZone));
    +}
    +
    +
    +function test_yyyyMMddG() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var date = new Date(Date.UTC(2006, 6, 27, 20, 10, 10));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(420);
    +  var fmt = new goog.i18n.DateTimeFormat('yyyy.MM.dd G \'at\' HH:mm:ss vvvv');
    +  assertEquals('2006.07.27 n. Chr. at 13:10:10 Etc/GMT+7',
    +               fmt.format(date, timeZone));
    +
    +  timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  fmt = new goog.i18n.DateTimeFormat('yyyy.MM.dd G \'at\' HH:mm:ss vvvv');
    +  assertEquals('2006.07.27 n. Chr. at 13:10:10 America/Los_Angeles',
    +      fmt.format(date, timeZone));
    +}
    +
    +function test_daylightTimeTransition() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +
    +  // US PST transition to PDT on 2006/4/2/ 2:00am, jump to 2006/4/2 3:00am,
    +  // That's UTC time 2006/4/2 10:00am
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var date = new Date(Date.UTC(2006, 4 - 1, 2, 9, 59, 0));
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('04/02/2006 01:59:00 PST', fmt.format(date, timeZone));
    +  date = new Date(Date.UTC(2006, 4 - 1, 2, 10, 01, 0));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('04/02/2006 03:01:00 PDT', fmt.format(date, timeZone));
    +  date = new Date(Date.UTC(2006, 4 - 1, 2, 10, 00, 0));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss z');
    +  assertEquals('04/02/2006 03:00:00 PDT', fmt.format(date, timeZone));
    +}
    +
    +function test_timeDisplayOnDaylighTimeTransition() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +
    +  // US PST transition to PDT on 2006/4/2/ 2:00am, jump to 2006/4/2 3:00am,
    +  var date = new Date(Date.UTC(2006, 4 - 1, 2, 2, 30, 0));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(0);
    +  var fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('04/02/2006 02:30:00 +0000', fmt.format(date, timeZone));
    +
    +  // US PDT transition to PST on 2006/10/29/ 2:00am, jump back to PDT
    +  // 2006/4/2 1:00am,
    +  date = new Date(Date.UTC(2006, 10 - 1, 29, 1, 30, 0));
    +  fmt = new goog.i18n.DateTimeFormat('MM/dd/yyyy HH:mm:ss Z');
    +  assertEquals('10/29/2006 01:30:00 +0000', fmt.format(date, timeZone));
    +}
    +
    +function test_nativeDigits() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fa;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa;
    +
    +  date = new Date(2006, 6, 27, 13, 10, 10, 250);
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(420);
    +  var fmt = new goog.i18n.DateTimeFormat('y/MM/dd H:mm:ss٫SS');
    +  assertEquals('۲۰۰۶/۰۷/۲۷ ۱۳:۱۰:۱۰٫۲۵', fmt.format(date));
    +
    +  // Make sure standardized timezone formats don't use native digits
    +  fmt = new goog.i18n.DateTimeFormat('Z');
    +  assertEquals('-0700', fmt.format(date, timeZone));
    +}
    +
    +// Making sure that the date-time combination is not a simple concatenation
    +function test_dateTimeConcatenation() {
    +  var date = new Date(Date.UTC(2006, 4 - 1, 2, 2, 30, 0));
    +  var timeZone = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATETIME);
    +  // {1} 'at' {0}
    +  assertEquals('Saturday, April 1, 2006 at 6:30:00 PM Pacific Standard Time',
    +               fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.LONG_DATETIME);
    +  assertEquals('April 1, 2006 at 6:30:00 PM PST', fmt.format(date, timeZone));
    +  // {1}, {0}
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +  assertEquals('Apr 1, 2006, 6:30:00 PM', fmt.format(date, timeZone));
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.SHORT_DATETIME);
    +  assertEquals('4/1/06, 6:30 PM', fmt.format(date, timeZone));
    +}
    +
    +function testNotUsingGlobalSymbols() {
    +  date = new Date(2013, 10, 15);
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
    +  var fmtFr = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  var fmtDe = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +
    +  // The two formatters should return different results (French & German)
    +  assertEquals('vendredi 15 novembre 2013', fmtFr.format(date));
    +  assertEquals('Freitag, 15. November 2013', fmtDe.format(date));
    +}
    +
    +function testConstructorSymbols() {
    +  date = new Date(2013, 10, 15);
    +
    +  var fmtFr = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE,
    +      goog.i18n.DateTimeSymbols_fr);
    +
    +  var fmtDe = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE,
    +      goog.i18n.DateTimeSymbols_de);
    +
    +  // The two formatters should return different results (French & German)
    +  assertEquals('vendredi 15 novembre 2013', fmtFr.format(date));
    +  assertEquals('Freitag, 15. November 2013', fmtDe.format(date));
    +}
    +
    +function testSupportForWeekInYear() {
    +  var date = new Date(2013, 1, 25);
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
    +  var fmt = new goog.i18n.DateTimeFormat('\'week\' w');
    +  assertEquals('week 9', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('\'week\' ww');
    +  assertEquals('week 09', fmt.format(date));
    +
    +  // Make sure it uses native digits when needed
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fa;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa;
    +  var fmt = new goog.i18n.DateTimeFormat('\'week\' w');
    +  assertEquals('week ۹', fmt.format(date));
    +  var fmt = new goog.i18n.DateTimeFormat('\'week\' ww');
    +  assertEquals('week ۰۹', fmt.format(date));
    +}
    +
    +function testSupportForYearAndEra() {
    +  var date = new Date(2013, 1, 25);
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.YEAR_FULL_WITH_ERA);
    +
    +  assertEquals('2013 AD', fmt.format(date));
    +
    +  date.setFullYear(213);
    +  assertEquals('213 AD', fmt.format(date));
    +
    +  date.setFullYear(11);
    +  assertEquals('11 AD', fmt.format(date));
    +
    +  date.setFullYear(-213);
    +  assertEquals('213 BC', fmt.format(date));
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.YEAR_FULL_WITH_ERA);
    +
    +  date.setFullYear(2013);
    +  assertEquals('2013 n. Chr.', fmt.format(date));
    +
    +  date.setFullYear(213);
    +  assertEquals('213 n. Chr.', fmt.format(date));
    +
    +  date.setFullYear(11);
    +  assertEquals('11 n. Chr.', fmt.format(date));
    +
    +  date.setFullYear(-213);
    +  assertEquals('213 v. Chr.', fmt.format(date));
    +
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ja;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ja;
    +  fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimePatterns.YEAR_FULL_WITH_ERA);
    +
    +  date.setFullYear(2013);
    +  assertEquals('西暦2013年', fmt.format(date));
    +
    +  date.setFullYear(213);
    +  assertEquals('西暦213年', fmt.format(date));
    +
    +  date.setFullYear(11);
    +  assertEquals('西暦11年', fmt.format(date));
    +
    +  date.setFullYear(-213);
    +  assertEquals('紀元前213年', fmt.format(date));
    +}
    +
    +// Creates a string by concatenating the week number for 7 successive days
    +function weekInYearFor7Days() {
    +  var date = new Date(2013, 0, 1); // January
    +  var fmt = new goog.i18n.DateTimeFormat('w');
    +  var result = '';
    +  for (var i = 1; i <= 7; ++i) {
    +    date.setDate(i);
    +    result += fmt.format(date);
    +  }
    +  return result;
    +}
    +
    +// Expected results from ICU4J v51. One entry will change in v52.
    +// These cover all combinations of FIRSTDAYOFWEEK / FIRSTWEEKCUTOFFDAY in use.
    +function testWeekInYearI18n() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bn_BD;
    +  assertEquals('bn_BD', '১১১২২২২', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IE;
    +  assertEquals('en_IE', '1111122', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_DJ;
    +  assertEquals('fr_DJ', '1111222', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_he_IL;
    +  assertEquals('he_IL', '1111122', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SA;
    +  assertEquals('ar_SA', '١١١١١٢٢', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_AE;
    +  assertEquals('ar_AE', '١١١١٢٢٢', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IN;
    +  assertEquals('en_IN', '1111122', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GB;
    +  assertEquals('en_GB', '1111112', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_US;
    +  assertEquals('en_US', '1111122', weekInYearFor7Days());
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ro_RO;
    +  assertEquals('ro_RO', '1111112', weekInYearFor7Days());
    +}
    +
    +// Regression for b/11567443 (no method 'getHours' when formatting a
    +// goog.date.Date)
    +function test_variousDateTypes() {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr;
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
    +
    +  var fmt = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +
    +  var date = new Date(2006, 6, 27, 13, 10, 42, 250);
    +  assertEquals('27 juil. 2006 13:10:42', fmt.format(date));
    +
    +  var gdatetime = new goog.date.DateTime(2006, 6, 27, 13, 10, 42, 250);
    +  assertEquals('27 juil. 2006 13:10:42', fmt.format(gdatetime));
    +
    +  var gdate = new goog.date.Date(2006, 6, 27);
    +  var fmtDate =
    +      new goog.i18n.DateTimeFormat(goog.i18n.DateTimeFormat.Format.MEDIUM_DATE);
    +  assertEquals('27 juil. 2006', fmtDate.format(gdatetime));
    +  try {
    +    fmt.format(gdate);
    +    fail('Should have thrown exception.');
    +  } catch (e) {}
    +}
    +
    +function testExceptionWhenFormattingNull() {
    +  var fmt = new goog.i18n.DateTimeFormat('M/d/y');
    +  try {
    +    fmt.format(null);
    +    fail('Should have thrown exception.');
    +  } catch (e) {}
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse.js
    new file mode 100644
    index 0000000000..c799470d0b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse.js
    @@ -0,0 +1,1150 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Date/Time parsing library with locale support.
    + */
    +
    +
    +/**
    + * Namespace for locale date/time parsing functions
    + */
    +goog.provide('goog.i18n.DateTimeParse');
    +
    +goog.require('goog.date');
    +goog.require('goog.i18n.DateTimeFormat');
    +goog.require('goog.i18n.DateTimeSymbols');
    +
    +
    +/**
    + * DateTimeParse is for parsing date in a locale-sensitive manner. It allows
    + * user to use any customized patterns to parse date-time string under certain
    + * locale. Things varies across locales like month name, weekname, field
    + * order, etc.
    + *
    + * This module is the counter-part of DateTimeFormat. They use the same
    + * date/time pattern specification, which is borrowed from ICU/JDK.
    + *
    + * This implementation could parse partial date/time.
    + *
    + * Time Format Syntax: To specify the time format use a time pattern string.
    + * In this pattern, following letters are reserved as pattern letters, which
    + * are defined as the following:
    + *
    + * <pre>
    + * Symbol   Meaning                 Presentation        Example
    + * ------   -------                 ------------        -------
    + * G        era designator          (Text)              AD
    + * y#       year                    (Number)            1996
    + * M        month in year           (Text & Number)     July & 07
    + * d        day in month            (Number)            10
    + * h        hour in am/pm (1~12)    (Number)            12
    + * H        hour in day (0~23)      (Number)            0
    + * m        minute in hour          (Number)            30
    + * s        second in minute        (Number)            55
    + * S        fractional second       (Number)            978
    + * E        day of week             (Text)              Tuesday
    + * D        day in year             (Number)            189
    + * a        am/pm marker            (Text)              PM
    + * k        hour in day (1~24)      (Number)            24
    + * K        hour in am/pm (0~11)    (Number)            0
    + * z        time zone               (Text)              Pacific Standard Time
    + * Z        time zone (RFC 822)     (Number)            -0800
    + * v        time zone (generic)     (Text)              Pacific Time
    + * '        escape for text         (Delimiter)         'Date='
    + * ''       single quote            (Literal)           'o''clock'
    + * </pre>
    + *
    + * The count of pattern letters determine the format. <p>
    + * (Text): 4 or more pattern letters--use full form,
    + *         less than 4--use short or abbreviated form if one exists.
    + *         In parsing, we will always try long format, then short. <p>
    + * (Number): the minimum number of digits. <p>
    + * (Text & Number): 3 or over, use text, otherwise use number. <p>
    + * Any characters that not in the pattern will be treated as quoted text. For
    + * instance, characters like ':', '.', ' ', '#' and '@' will appear in the
    + * resulting time text even they are not embraced within single quotes. In our
    + * current pattern usage, we didn't use up all letters. But those unused
    + * letters are strongly discouraged to be used as quoted text without quote.
    + * That's because we may use other letter for pattern in future. <p>
    + *
    + * Examples Using the US Locale:
    + *
    + * Format Pattern                         Result
    + * --------------                         -------
    + * "yyyy.MM.dd G 'at' HH:mm:ss vvvv" ->>  1996.07.10 AD at 15:08:56 Pacific Time
    + * "EEE, MMM d, ''yy"                ->>  Wed, July 10, '96
    + * "h:mm a"                          ->>  12:08 PM
    + * "hh 'o''clock' a, zzzz"           ->>  12 o'clock PM, Pacific Daylight Time
    + * "K:mm a, vvv"                     ->>  0:00 PM, PT
    + * "yyyyy.MMMMM.dd GGG hh:mm aaa"    ->>  01996.July.10 AD 12:08 PM
    + *
    + * <p> When parsing a date string using the abbreviated year pattern ("yy"),
    + * DateTimeParse must interpret the abbreviated year relative to some
    + * century. It does this by adjusting dates to be within 80 years before and 20
    + * years after the time the parse function is called. For example, using a
    + * pattern of "MM/dd/yy" and a DateTimeParse instance created on Jan 1, 1997,
    + * the string "01/11/12" would be interpreted as Jan 11, 2012 while the string
    + * "05/04/64" would be interpreted as May 4, 1964. During parsing, only
    + * strings consisting of exactly two digits, as defined by {@link
    + * java.lang.Character#isDigit(char)}, will be parsed into the default
    + * century. Any other numeric string, such as a one digit string, a three or
    + * more digit string will be interpreted as its face value.
    + *
    + * <p> If the year pattern does not have exactly two 'y' characters, the year is
    + * interpreted literally, regardless of the number of digits. So using the
    + * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
    + *
    + * <p> When numeric fields abut one another directly, with no intervening
    + * delimiter characters, they constitute a run of abutting numeric fields. Such
    + * runs are parsed specially. For example, the format "HHmmss" parses the input
    + * text "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and
    + * fails to parse "1234". In other words, the leftmost field of the run is
    + * flexible, while the others keep a fixed width. If the parse fails anywhere in
    + * the run, then the leftmost field is shortened by one character, and the
    + * entire run is parsed again. This is repeated until either the parse succeeds
    + * or the leftmost field is one character in length. If the parse still fails at
    + * that point, the parse of the run fails.
    + *
    + * <p> Now timezone parsing only support GMT:hhmm, GMT:+hhmm, GMT:-hhmm
    + */
    +
    +
    +
    +/**
    + * Construct a DateTimeParse based on current locale.
    + * @param {string|number} pattern pattern specification or pattern type.
    + * @constructor
    + * @final
    + */
    +goog.i18n.DateTimeParse = function(pattern) {
    +  this.patternParts_ = [];
    +  if (typeof pattern == 'number') {
    +    this.applyStandardPattern_(pattern);
    +  } else {
    +    this.applyPattern_(pattern);
    +  }
    +};
    +
    +
    +/**
    + * Number of years prior to now that the century used to
    + * disambiguate two digit years will begin
    + *
    + * @type {number}
    + */
    +goog.i18n.DateTimeParse.ambiguousYearCenturyStart = 80;
    +
    +
    +/**
    + * Apply a pattern to this Parser. The pattern string will be parsed and saved
    + * in "compiled" form.
    + * Note: this method is somewhat similar to the pattern parsing method in
    + *       datetimeformat. If you see something wrong here, you might want
    + *       to check the other.
    + * @param {string} pattern It describes the format of date string that need to
    + *     be parsed.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.applyPattern_ = function(pattern) {
    +  var inQuote = false;
    +  var buf = '';
    +
    +  for (var i = 0; i < pattern.length; i++) {
    +    var ch = pattern.charAt(i);
    +
    +    // handle space, add literal part (if exist), and add space part
    +    if (ch == ' ') {
    +      if (buf.length > 0) {
    +        this.patternParts_.push({text: buf, count: 0, abutStart: false});
    +        buf = '';
    +      }
    +      this.patternParts_.push({text: ' ', count: 0, abutStart: false});
    +      while (i < pattern.length - 1 && pattern.charAt(i + 1) == ' ') {
    +        i++;
    +      }
    +    } else if (inQuote) {
    +      // inside quote, except '', just copy or exit
    +      if (ch == '\'') {
    +        if (i + 1 < pattern.length && pattern.charAt(i + 1) == '\'') {
    +          // quote appeared twice continuously, interpret as one quote.
    +          buf += '\'';
    +          i++;
    +        } else {
    +          // exit quote
    +          inQuote = false;
    +        }
    +      } else {
    +        // literal
    +        buf += ch;
    +      }
    +    } else if (goog.i18n.DateTimeParse.PATTERN_CHARS_.indexOf(ch) >= 0) {
    +      // outside quote, it is a pattern char
    +      if (buf.length > 0) {
    +        this.patternParts_.push({text: buf, count: 0, abutStart: false});
    +        buf = '';
    +      }
    +      var count = this.getNextCharCount_(pattern, i);
    +      this.patternParts_.push({text: ch, count: count, abutStart: false});
    +      i += count - 1;
    +    } else if (ch == '\'') {
    +      // Two consecutive quotes is a quote literal, inside or outside of quotes.
    +      if (i + 1 < pattern.length && pattern.charAt(i + 1) == '\'') {
    +        buf += '\'';
    +        i++;
    +      } else {
    +        inQuote = true;
    +      }
    +    } else {
    +      buf += ch;
    +    }
    +  }
    +
    +  if (buf.length > 0) {
    +    this.patternParts_.push({text: buf, count: 0, abutStart: false});
    +  }
    +
    +  this.markAbutStart_();
    +};
    +
    +
    +/**
    + * Apply a predefined pattern to this Parser.
    + * @param {number} formatType A constant used to identified the predefined
    + *     pattern string stored in locale repository.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.applyStandardPattern_ = function(formatType) {
    +  var pattern;
    +  // formatType constants are in consecutive numbers. So it can be used to
    +  // index array in following way.
    +
    +  // if type is out of range, default to medium date/time format.
    +  if (formatType > goog.i18n.DateTimeFormat.Format.SHORT_DATETIME) {
    +    formatType = goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME;
    +  }
    +
    +  if (formatType < 4) {
    +    pattern = goog.i18n.DateTimeSymbols.DATEFORMATS[formatType];
    +  } else if (formatType < 8) {
    +    pattern = goog.i18n.DateTimeSymbols.TIMEFORMATS[formatType - 4];
    +  } else {
    +    pattern = goog.i18n.DateTimeSymbols.DATETIMEFORMATS[formatType - 8];
    +    pattern = pattern.replace('{1}',
    +        goog.i18n.DateTimeSymbols.DATEFORMATS[formatType - 8]);
    +    pattern = pattern.replace('{0}',
    +        goog.i18n.DateTimeSymbols.TIMEFORMATS[formatType - 8]);
    +  }
    +  this.applyPattern_(pattern);
    +};
    +
    +
    +/**
    + * Parse the given string and fill info into date object. This version does
    + * not validate the input.
    + * @param {string} text The string being parsed.
    + * @param {goog.date.DateLike} date The Date object to hold the parsed date.
    + * @param {number=} opt_start The position from where parse should begin.
    + * @return {number} How many characters parser advanced.
    + */
    +goog.i18n.DateTimeParse.prototype.parse = function(text, date, opt_start) {
    +  var start = opt_start || 0;
    +  return this.internalParse_(text, date, start, false/*validation*/);
    +};
    +
    +
    +/**
    + * Parse the given string and fill info into date object. This version will
    + * validate the input and make sure it is a validate date/time.
    + * @param {string} text The string being parsed.
    + * @param {goog.date.DateLike} date The Date object to hold the parsed date.
    + * @param {number=} opt_start The position from where parse should begin.
    + * @return {number} How many characters parser advanced.
    + */
    +goog.i18n.DateTimeParse.prototype.strictParse =
    +    function(text, date, opt_start) {
    +  var start = opt_start || 0;
    +  return this.internalParse_(text, date, start, true/*validation*/);
    +};
    +
    +
    +/**
    + * Parse the given string and fill info into date object.
    + * @param {string} text The string being parsed.
    + * @param {goog.date.DateLike} date The Date object to hold the parsed date.
    + * @param {number} start The position from where parse should begin.
    + * @param {boolean} validation If true, input string need to be a valid
    + *     date/time string.
    + * @return {number} How many characters parser advanced.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.internalParse_ =
    +    function(text, date, start, validation) {
    +  var cal = new goog.i18n.DateTimeParse.MyDate_();
    +  var parsePos = [start];
    +
    +  // For parsing abutting numeric fields. 'abutPat' is the
    +  // offset into 'pattern' of the first of 2 or more abutting
    +  // numeric fields. 'abutStart' is the offset into 'text'
    +  // where parsing the fields begins. 'abutPass' starts off as 0
    +  // and increments each time we try to parse the fields.
    +  var abutPat = -1; // If >=0, we are in a run of abutting numeric fields
    +  var abutStart = 0;
    +  var abutPass = 0;
    +
    +  for (var i = 0; i < this.patternParts_.length; i++) {
    +    if (this.patternParts_[i].count > 0) {
    +      if (abutPat < 0 && this.patternParts_[i].abutStart) {
    +        abutPat = i;
    +        abutStart = start;
    +        abutPass = 0;
    +      }
    +
    +      // Handle fields within a run of abutting numeric fields. Take
    +      // the pattern "HHmmss" as an example. We will try to parse
    +      // 2/2/2 characters of the input text, then if that fails,
    +      // 1/2/2. We only adjust the width of the leftmost field; the
    +      // others remain fixed. This allows "123456" => 12:34:56, but
    +      // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
    +      // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
    +      if (abutPat >= 0) {
    +        // If we are at the start of a run of abutting fields, then
    +        // shorten this field in each pass. If we can't shorten
    +        // this field any more, then the parse of this set of
    +        // abutting numeric fields has failed.
    +        var count = this.patternParts_[i].count;
    +        if (i == abutPat) {
    +          count -= abutPass;
    +          abutPass++;
    +          if (count == 0) {
    +            // tried all possible width, fail now
    +            return 0;
    +          }
    +        }
    +
    +        if (!this.subParse_(text, parsePos, this.patternParts_[i], count,
    +                            cal)) {
    +          // If the parse fails anywhere in the run, back up to the
    +          // start of the run and retry.
    +          i = abutPat - 1;
    +          parsePos[0] = abutStart;
    +          continue;
    +        }
    +      }
    +
    +      // Handle non-numeric fields and non-abutting numeric fields.
    +      else {
    +        abutPat = -1;
    +        if (!this.subParse_(text, parsePos, this.patternParts_[i], 0, cal)) {
    +          return 0;
    +        }
    +      }
    +    } else {
    +      // Handle literal pattern characters. These are any
    +      // quoted characters and non-alphabetic unquoted
    +      // characters.
    +      abutPat = -1;
    +      // A run of white space in the pattern matches a run
    +      // of white space in the input text.
    +      if (this.patternParts_[i].text.charAt(0) == ' ') {
    +        // Advance over run in input text
    +        var s = parsePos[0];
    +        this.skipSpace_(text, parsePos);
    +
    +        // Must see at least one white space char in input
    +        if (parsePos[0] > s) {
    +          continue;
    +        }
    +      } else if (text.indexOf(this.patternParts_[i].text, parsePos[0]) ==
    +                 parsePos[0]) {
    +        parsePos[0] += this.patternParts_[i].text.length;
    +        continue;
    +      }
    +      // We fall through to this point if the match fails
    +      return 0;
    +    }
    +  }
    +
    +  // return progress
    +  return cal.calcDate_(date, validation) ? parsePos[0] - start : 0;
    +};
    +
    +
    +/**
    + * Calculate character repeat count in pattern.
    + *
    + * @param {string} pattern It describes the format of date string that need to
    + *     be parsed.
    + * @param {number} start The position of pattern character.
    + *
    + * @return {number} Repeat count.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.getNextCharCount_ =
    +    function(pattern, start) {
    +  var ch = pattern.charAt(start);
    +  var next = start + 1;
    +  while (next < pattern.length && pattern.charAt(next) == ch) {
    +    next++;
    +  }
    +  return next - start;
    +};
    +
    +
    +/**
    + * All acceptable pattern characters.
    + * @private
    + */
    +goog.i18n.DateTimeParse.PATTERN_CHARS_ = 'GyMdkHmsSEDahKzZvQL';
    +
    +
    +/**
    + * Pattern characters that specify numerical field.
    + * @private
    + */
    +goog.i18n.DateTimeParse.NUMERIC_FORMAT_CHARS_ = 'MydhHmsSDkK';
    +
    +
    +/**
    + * Check if the pattern part is a numeric field.
    + *
    + * @param {Object} part pattern part to be examined.
    + *
    + * @return {boolean} true if the pattern part is numeric field.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.isNumericField_ = function(part) {
    +  if (part.count <= 0) {
    +    return false;
    +  }
    +  var i = goog.i18n.DateTimeParse.NUMERIC_FORMAT_CHARS_.indexOf(
    +      part.text.charAt(0));
    +  return i > 0 || i == 0 && part.count < 3;
    +};
    +
    +
    +/**
    + * Identify the start of an abutting numeric fields' run. Taking pattern
    + * "HHmmss" as an example. It will try to parse 2/2/2 characters of the input
    + * text, then if that fails, 1/2/2. We only adjust the width of the leftmost
    + * field; the others remain fixed. This allows "123456" => 12:34:56, but
    + * "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we try 4/2/2,
    + * 3/2/2, 2/2/2, and finally 1/2/2. The first field of connected numeric
    + * fields will be marked as abutStart, its width can be reduced to accommodate
    + * others.
    + *
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.markAbutStart_ = function() {
    +  // abut parts are continuous numeric parts. abutStart is the switch
    +  // point from non-abut to abut
    +  var abut = false;
    +
    +  for (var i = 0; i < this.patternParts_.length; i++) {
    +    if (this.isNumericField_(this.patternParts_[i])) {
    +      // if next part is not following abut sequence, and isNumericField_
    +      if (!abut && i + 1 < this.patternParts_.length &&
    +          this.isNumericField_(this.patternParts_[i + 1])) {
    +        abut = true;
    +        this.patternParts_[i].abutStart = true;
    +      }
    +    } else {
    +      abut = false;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Skip space in the string.
    + *
    + * @param {string} text input string.
    + * @param {Array<number>} pos where skip start, and return back where the skip
    + *     stops.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.skipSpace_ = function(text, pos) {
    +  var m = text.substring(pos[0]).match(/^\s+/);
    +  if (m) {
    +    pos[0] += m[0].length;
    +  }
    +};
    +
    +
    +/**
    + * Protected method that converts one field of the input string into a
    + * numeric field value.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {Object} part the pattern part for this field.
    + * @param {number} digitCount when > 0, numeric parsing must obey the count.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object that holds parsed value.
    + *
    + * @return {boolean} True if it parses successfully.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParse_ =
    +    function(text, pos, part, digitCount, cal) {
    +  this.skipSpace_(text, pos);
    +
    +  var start = pos[0];
    +  var ch = part.text.charAt(0);
    +
    +  // parse integer value if it is a numeric field
    +  var value = -1;
    +  if (this.isNumericField_(part)) {
    +    if (digitCount > 0) {
    +      if ((start + digitCount) > text.length) {
    +        return false;
    +      }
    +      value = this.parseInt_(
    +          text.substring(0, start + digitCount), pos);
    +    } else {
    +      value = this.parseInt_(text, pos);
    +    }
    +  }
    +
    +  switch (ch) {
    +    case 'G': // ERA
    +      value = this.matchString_(text, pos, goog.i18n.DateTimeSymbols.ERAS);
    +      if (value >= 0) {
    +        cal.era = value;
    +      }
    +      return true;
    +    case 'M': // MONTH
    +    case 'L': // STANDALONEMONTH
    +      return this.subParseMonth_(text, pos, cal, value);
    +    case 'E':
    +      return this.subParseDayOfWeek_(text, pos, cal);
    +    case 'a': // AM_PM
    +      value = this.matchString_(text, pos, goog.i18n.DateTimeSymbols.AMPMS);
    +      if (value >= 0) {
    +        cal.ampm = value;
    +      }
    +      return true;
    +    case 'y': // YEAR
    +      return this.subParseYear_(text, pos, start, value, part, cal);
    +    case 'Q': // QUARTER
    +      return this.subParseQuarter_(text, pos, cal, value);
    +    case 'd': // DATE
    +      if (value >= 0) {
    +        cal.day = value;
    +      }
    +      return true;
    +    case 'S': // FRACTIONAL_SECOND
    +      return this.subParseFractionalSeconds_(value, pos, start, cal);
    +    case 'h': // HOUR (1..12)
    +      if (value == 12) {
    +        value = 0;
    +      }
    +    case 'K': // HOUR (0..11)
    +    case 'H': // HOUR_OF_DAY (0..23)
    +    case 'k': // HOUR_OF_DAY (1..24)
    +      if (value >= 0) {
    +        cal.hours = value;
    +      }
    +      return true;
    +    case 'm': // MINUTE
    +      if (value >= 0) {
    +        cal.minutes = value;
    +      }
    +      return true;
    +    case 's': // SECOND
    +      if (value >= 0) {
    +        cal.seconds = value;
    +      }
    +      return true;
    +
    +    case 'z': // ZONE_OFFSET
    +    case 'Z': // TIMEZONE_RFC
    +    case 'v': // TIMEZONE_GENERIC
    +      return this.subparseTimeZoneInGMT_(text, pos, cal);
    +    default:
    +      return false;
    +  }
    +};
    +
    +
    +/**
    + * Parse year field. Year field is special because
    + * 1) two digit year need to be resolved.
    + * 2) we allow year to take a sign.
    + * 3) year field participate in abut processing.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {number} start where this field start.
    + * @param {number} value integer value of year.
    + * @param {Object} part the pattern part for this field.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + *
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParseYear_ =
    +    function(text, pos, start, value, part, cal) {
    +  var ch;
    +  if (value < 0) {
    +    //possible sign
    +    ch = text.charAt(pos[0]);
    +    if (ch != '+' && ch != '-') {
    +      return false;
    +    }
    +    pos[0]++;
    +    value = this.parseInt_(text, pos);
    +    if (value < 0) {
    +      return false;
    +    }
    +    if (ch == '-') {
    +      value = -value;
    +    }
    +  }
    +
    +  // only if 2 digit was actually parsed, and pattern say it has 2 digit.
    +  if (!ch && pos[0] - start == 2 && part.count == 2) {
    +    cal.setTwoDigitYear_(value);
    +  } else {
    +    cal.year = value;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Parse Month field.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + * @param {number} value numeric value if this field is expressed using
    + *      numeric pattern, or -1 if not.
    + *
    + * @return {boolean} True if parsing successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParseMonth_ =
    +    function(text, pos, cal, value) {
    +  // when month is symbols, i.e., MMM, MMMM, LLL or LLLL, value will be -1
    +  if (value < 0) {
    +    // Want to be able to parse both short and long forms.
    +    // Try count == 4 first
    +    var months = goog.i18n.DateTimeSymbols.MONTHS.concat(
    +        goog.i18n.DateTimeSymbols.STANDALONEMONTHS).concat(
    +        goog.i18n.DateTimeSymbols.SHORTMONTHS).concat(
    +        goog.i18n.DateTimeSymbols.STANDALONESHORTMONTHS);
    +    value = this.matchString_(text, pos, months);
    +    if (value < 0) {
    +      return false;
    +    }
    +    // The months variable is multiple of 12, so we have to get the actual
    +    // month index by modulo 12.
    +    cal.month = (value % 12);
    +    return true;
    +  } else {
    +    cal.month = value - 1;
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * Parse Quarter field.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + * @param {number} value numeric value if this field is expressed using
    + *      numeric pattern, or -1 if not.
    + *
    + * @return {boolean} True if parsing successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParseQuarter_ =
    +    function(text, pos, cal, value) {
    +  // value should be -1, since this is a non-numeric field.
    +  if (value < 0) {
    +    // Want to be able to parse both short and long forms.
    +    // Try count == 4 first:
    +    value = this.matchString_(text, pos, goog.i18n.DateTimeSymbols.QUARTERS);
    +    if (value < 0) { // count == 4 failed, now try count == 3
    +      value = this.matchString_(text, pos,
    +                                goog.i18n.DateTimeSymbols.SHORTQUARTERS);
    +    }
    +    if (value < 0) {
    +      return false;
    +    }
    +    cal.month = value * 3;  // First month of quarter.
    +    cal.day = 1;
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Parse Day of week field.
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + *
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParseDayOfWeek_ =
    +    function(text, pos, cal) {
    +  // Handle both short and long forms.
    +  // Try count == 4 (DDDD) first:
    +  var value = this.matchString_(text, pos, goog.i18n.DateTimeSymbols.WEEKDAYS);
    +  if (value < 0) {
    +    value = this.matchString_(text, pos,
    +                              goog.i18n.DateTimeSymbols.SHORTWEEKDAYS);
    +  }
    +  if (value < 0) {
    +    return false;
    +  }
    +  cal.dayOfWeek = value;
    +  return true;
    +};
    +
    +
    +/**
    + * Parse fractional seconds field.
    + *
    + * @param {number} value parsed numeric value.
    + * @param {Array<number>} pos current parse position.
    + * @param {number} start where this field start.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + *
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subParseFractionalSeconds_ =
    +    function(value, pos, start, cal) {
    +  // Fractional seconds left-justify
    +  var len = pos[0] - start;
    +  cal.milliseconds = len < 3 ? value * Math.pow(10, 3 - len) :
    +                               Math.round(value / Math.pow(10, len - 3));
    +  return true;
    +};
    +
    +
    +/**
    + * Parse GMT type timezone.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + *
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.subparseTimeZoneInGMT_ =
    +    function(text, pos, cal) {
    +  // First try to parse generic forms such as GMT-07:00. Do this first
    +  // in case localized DateFormatZoneData contains the string "GMT"
    +  // for a zone; in that case, we don't want to match the first three
    +  // characters of GMT+/-HH:MM etc.
    +
    +  // For time zones that have no known names, look for strings
    +  // of the form:
    +  //    GMT[+-]hours:minutes or
    +  //    GMT[+-]hhmm or
    +  //    GMT.
    +  if (text.indexOf('GMT', pos[0]) == pos[0]) {
    +    pos[0] += 3;  // 3 is the length of GMT
    +    return this.parseTimeZoneOffset_(text, pos, cal);
    +  }
    +
    +  // TODO(user): check for named time zones by looking through the locale
    +  // data from the DateFormatZoneData strings. Should parse both short and long
    +  // forms.
    +  // subParseZoneString(text, start, cal);
    +
    +  // As a last resort, look for numeric timezones of the form
    +  // [+-]hhmm as specified by RFC 822.  This code is actually
    +  // a little more permissive than RFC 822.  It will try to do
    +  // its best with numbers that aren't strictly 4 digits long.
    +  return this.parseTimeZoneOffset_(text, pos, cal);
    +};
    +
    +
    +/**
    + * Parse time zone offset.
    + *
    + * @param {string} text the time text to be parsed.
    + * @param {Array<number>} pos Parse position.
    + * @param {goog.i18n.DateTimeParse.MyDate_} cal object to hold parsed value.
    + *
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.parseTimeZoneOffset_ =
    +    function(text, pos, cal) {
    +  if (pos[0] >= text.length) {
    +    cal.tzOffset = 0;
    +    return true;
    +  }
    +
    +  var sign = 1;
    +  switch (text.charAt(pos[0])) {
    +    case '-': sign = -1;  // fall through
    +    case '+': pos[0]++;
    +  }
    +
    +  // Look for hours:minutes or hhmm.
    +  var st = pos[0];
    +  var value = this.parseInt_(text, pos);
    +  if (value == 0 && pos[0] == st) {
    +    return false;
    +  }
    +
    +  var offset;
    +  if (pos[0] < text.length && text.charAt(pos[0]) == ':') {
    +    // This is the hours:minutes case
    +    offset = value * 60;
    +    pos[0]++;
    +    st = pos[0];
    +    value = this.parseInt_(text, pos);
    +    if (value == 0 && pos[0] == st) {
    +      return false;
    +    }
    +    offset += value;
    +  } else {
    +    // This is the hhmm case.
    +    offset = value;
    +    // Assume "-23".."+23" refers to hours.
    +    if (offset < 24 && (pos[0] - st) <= 2) {
    +      offset *= 60;
    +    } else {
    +      // todo: this looks questionable, should have more error checking
    +      offset = offset % 100 + offset / 100 * 60;
    +    }
    +  }
    +
    +  offset *= sign;
    +  cal.tzOffset = -offset;
    +  return true;
    +};
    +
    +
    +/**
    + * Parse a integer string and return integer value.
    + *
    + * @param {string} text string being parsed.
    + * @param {Array<number>} pos parse position.
    + *
    + * @return {number} Converted integer value.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.parseInt_ = function(text, pos) {
    +  // Delocalizes the string containing native digits specified by the locale,
    +  // replaces the native digits with ASCII digits. Leaves other characters.
    +  // This is the reverse operation of localizeNumbers_ in datetimeformat.js.
    +  if (goog.i18n.DateTimeSymbols.ZERODIGIT) {
    +    var parts = [];
    +    for (var i = pos[0]; i < text.length; i++) {
    +      var c = text.charCodeAt(i) - goog.i18n.DateTimeSymbols.ZERODIGIT;
    +      parts.push((0 <= c && c <= 9) ?
    +          String.fromCharCode(c + 0x30) :
    +          text.charAt(i));
    +    }
    +    text = parts.join('');
    +  } else {
    +    text = text.substring(pos[0]);
    +  }
    +
    +  var m = text.match(/^\d+/);
    +  if (!m) {
    +    return -1;
    +  }
    +  pos[0] += m[0].length;
    +  return parseInt(m[0], 10);
    +};
    +
    +
    +/**
    + * Attempt to match the text at a given position against an array of strings.
    + * Since multiple strings in the array may match (for example, if the array
    + * contains "a", "ab", and "abc", all will match the input string "abcd") the
    + * longest match is returned.
    + *
    + * @param {string} text The string to match to.
    + * @param {Array<number>} pos parsing position.
    + * @param {Array<string>} data The string array of matching patterns.
    + *
    + * @return {number} the new start position if matching succeeded; a negative
    + *     number indicating matching failure.
    + * @private
    + */
    +goog.i18n.DateTimeParse.prototype.matchString_ = function(text, pos, data) {
    +  // There may be multiple strings in the data[] array which begin with
    +  // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
    +  // We keep track of the longest match, and return that. Note that this
    +  // unfortunately requires us to test all array elements.
    +  var bestMatchLength = 0;
    +  var bestMatch = -1;
    +  var lower_text = text.substring(pos[0]).toLowerCase();
    +  for (var i = 0; i < data.length; i++) {
    +    var len = data[i].length;
    +    // Always compare if we have no match yet; otherwise only compare
    +    // against potentially better matches (longer strings).
    +    if (len > bestMatchLength &&
    +        lower_text.indexOf(data[i].toLowerCase()) == 0) {
    +      bestMatch = i;
    +      bestMatchLength = len;
    +    }
    +  }
    +  if (bestMatch >= 0) {
    +    pos[0] += bestMatchLength;
    +  }
    +  return bestMatch;
    +};
    +
    +
    +
    +/**
    + * This class hold the intermediate parsing result. After all fields are
    + * consumed, final result will be resolved from this class.
    + * @constructor
    + * @private
    + */
    +goog.i18n.DateTimeParse.MyDate_ = function() {};
    +
    +
    +/**
    + * The date's era.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.era;
    +
    +
    +/**
    + * The date's year.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.year;
    +
    +
    +/**
    + * The date's month.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.month;
    +
    +
    +/**
    + * The date's day of month.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.day;
    +
    +
    +/**
    + * The date's hour.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.hours;
    +
    +
    +/**
    + * The date's before/afternoon denominator.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.ampm;
    +
    +
    +/**
    + * The date's minutes.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.minutes;
    +
    +
    +/**
    + * The date's seconds.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.seconds;
    +
    +
    +/**
    + * The date's milliseconds.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.milliseconds;
    +
    +
    +/**
    + * The date's timezone offset.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.tzOffset;
    +
    +
    +/**
    + * The date's day of week. Sunday is 0, Saturday is 6.
    + * @type {?number}
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.dayOfWeek;
    +
    +
    +/**
    + * 2 digit year special handling. Assuming for example that the
    + * defaultCenturyStart is 6/18/1903. This means that two-digit years will be
    + * forced into the range 6/18/1903 to 6/17/2003. As a result, years 00, 01, and
    + * 02 correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
    + * to 1904, 1905, etc. If the year is 03, then it is 2003 if the
    + * other fields specify a date before 6/18, or 1903 if they specify a
    + * date afterwards. As a result, 03 is an ambiguous year. All other
    + * two-digit years are unambiguous.
    + *
    + * @param {number} year 2 digit year value before adjustment.
    + * @return {number} disambiguated year.
    + * @private
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.setTwoDigitYear_ = function(year) {
    +  var now = new Date();
    +  var defaultCenturyStartYear =
    +      now.getFullYear() - goog.i18n.DateTimeParse.ambiguousYearCenturyStart;
    +  var ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
    +  this.ambiguousYear = (year == ambiguousTwoDigitYear);
    +  year += Math.floor(defaultCenturyStartYear / 100) * 100 +
    +      (year < ambiguousTwoDigitYear ? 100 : 0);
    +  return this.year = year;
    +};
    +
    +
    +/**
    + * Based on the fields set, fill a Date object. For those fields that not
    + * set, use the passed in date object's value.
    + *
    + * @param {goog.date.DateLike} date Date object to be filled.
    + * @param {boolean} validation If true, input string will be checked to make
    + *     sure it is valid.
    + *
    + * @return {boolean} false if fields specify a invalid date.
    + * @private
    + */
    +goog.i18n.DateTimeParse.MyDate_.prototype.calcDate_ =
    +    function(date, validation) {
    +  // year 0 is 1 BC, and so on.
    +  if (this.era != undefined && this.year != undefined &&
    +      this.era == 0 && this.year > 0) {
    +    this.year = -(this.year - 1);
    +  }
    +
    +  if (this.year != undefined) {
    +    date.setFullYear(this.year);
    +  }
    +
    +  // The setMonth and setDate logic is a little tricky. We need to make sure
    +  // day of month is smaller enough so that it won't cause a month switch when
    +  // setting month. For example, if data in date is Nov 30, when month is set
    +  // to Feb, because there is no Feb 30, JS adjust it to Mar 2. So Feb 12 will
    +  // become  Mar 12.
    +  var orgDate = date.getDate();
    +
    +  // Every month has a 1st day, this can actually be anything less than 29.
    +  date.setDate(1);
    +
    +  if (this.month != undefined) {
    +    date.setMonth(this.month);
    +  }
    +
    +  if (this.day != undefined) {
    +    date.setDate(this.day);
    +  } else {
    +    var maxDate =
    +        goog.date.getNumberOfDaysInMonth(date.getFullYear(), date.getMonth());
    +    date.setDate(orgDate > maxDate ? maxDate : orgDate);
    +  }
    +
    +  if (goog.isFunction(date.setHours)) {
    +    if (this.hours == undefined) {
    +      this.hours = date.getHours();
    +    }
    +    // adjust ampm
    +    if (this.ampm != undefined && this.ampm > 0 && this.hours < 12) {
    +      this.hours += 12;
    +    }
    +    date.setHours(this.hours);
    +  }
    +
    +  if (goog.isFunction(date.setMinutes) && this.minutes != undefined) {
    +    date.setMinutes(this.minutes);
    +  }
    +
    +  if (goog.isFunction(date.setSeconds) && this.seconds != undefined) {
    +    date.setSeconds(this.seconds);
    +  }
    +
    +  if (goog.isFunction(date.setMilliseconds) &&
    +      this.milliseconds != undefined) {
    +    date.setMilliseconds(this.milliseconds);
    +  }
    +
    +  // If validation is needed, verify that the uncalculated date fields
    +  // match the calculated date fields.  We do this before we set the
    +  // timezone offset, which will skew all of the dates.
    +  //
    +  // Don't need to check the day of week as it is guaranteed to be
    +  // correct or return false below.
    +  if (validation &&
    +      (this.year != undefined && this.year != date.getFullYear() ||
    +       this.month != undefined && this.month != date.getMonth() ||
    +       this.day != undefined && this.day != date.getDate() ||
    +       this.hours >= 24 || this.minutes >= 60 || this.seconds >= 60 ||
    +       this.milliseconds >= 1000)) {
    +    return false;
    +  }
    +
    +  // adjust time zone
    +  if (this.tzOffset != undefined) {
    +    var offset = date.getTimezoneOffset();
    +    date.setTime(date.getTime() + (this.tzOffset - offset) * 60 * 1000);
    +  }
    +
    +  // resolve ambiguous year if needed
    +  if (this.ambiguousYear) { // the two-digit year == the default start year
    +    var defaultCenturyStart = new Date();
    +    defaultCenturyStart.setFullYear(
    +        defaultCenturyStart.getFullYear() -
    +        goog.i18n.DateTimeParse.ambiguousYearCenturyStart);
    +    if (date.getTime() < defaultCenturyStart.getTime()) {
    +      date.setFullYear(defaultCenturyStart.getFullYear() + 100);
    +    }
    +  }
    +
    +  // dayOfWeek, validation only
    +  if (this.dayOfWeek != undefined) {
    +    if (this.day == undefined) {
    +      // adjust to the nearest day of the week
    +      var adjustment = (7 + this.dayOfWeek - date.getDay()) % 7;
    +      if (adjustment > 3) {
    +        adjustment -= 7;
    +      }
    +      var orgMonth = date.getMonth();
    +      date.setDate(date.getDate() + adjustment);
    +
    +      // don't let it switch month
    +      if (date.getMonth() != orgMonth) {
    +        date.setDate(date.getDate() + (adjustment > 0 ? -7 : 7));
    +      }
    +    } else if (this.dayOfWeek != date.getDay()) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.html b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.html
    new file mode 100644
    index 0000000000..56032b0e68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  All Rights Reserved.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.DateTimeParse
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.DateTimeParseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.js
    new file mode 100644
    index 0000000000..9ac288fb49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimeparse_test.js
    @@ -0,0 +1,627 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.DateTimeParseTest');
    +goog.setTestOnly('goog.i18n.DateTimeParseTest');
    +
    +goog.require('goog.date.Date');
    +goog.require('goog.i18n.DateTimeFormat');
    +goog.require('goog.i18n.DateTimeParse');
    +goog.require('goog.i18n.DateTimeSymbols');
    +goog.require('goog.i18n.DateTimeSymbols_en');
    +goog.require('goog.i18n.DateTimeSymbols_fa');
    +goog.require('goog.i18n.DateTimeSymbols_fr');
    +goog.require('goog.i18n.DateTimeSymbols_pl');
    +goog.require('goog.i18n.DateTimeSymbols_zh');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +
    +var expectedFailures;
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function tearDown() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +  expectedFailures.handleTearDown();
    +}
    +
    +// Helper equivalent of assertEquals for dates, with day of month optional
    +function assertDateEquals(expectYear, expectMonth, expectDate, date) {
    +  assertEquals(expectYear, date.getFullYear());
    +  assertEquals(expectMonth, date.getMonth());
    +  if (expectDate)
    +    assertEquals(expectDate, date.getDate());
    +}
    +
    +// Helper equivalent of assertEquals for times, with seconds and milliseconds
    +function assertTimeEquals(expectHour, expectMin, expectSec, expectMilli, date) {
    +  assertEquals(expectHour, date.getHours());
    +  assertEquals(expectMin, date.getMinutes());
    +  if (expectSec)
    +    assertEquals(expectSec, date.getSeconds());
    +  if (expectMilli)
    +    assertEquals(expectMilli, date.getTime() % 1000);
    +}
    +
    +// Helper function, doing parse and assert on dates
    +function assertParsedDateEquals(expectYear, expectMonth, expectDate,
    +    parser, stringToParse, date) {
    +  assertTrue(parser.parse(stringToParse, date) > 0);
    +  assertDateEquals(expectYear, expectMonth, expectDate, date);
    +}
    +
    +// Helper function, doing parse and assert on times
    +function assertParsedTimeEquals(expectHour, expectMin, expectSec, expectMilli,
    +    parser, stringToParse, date) {
    +  assertTrue(parser.parse(stringToParse, date) > 0);
    +  assertTimeEquals(expectHour, expectMin, expectSec, expectMilli, date);
    +}
    +
    +function testNegativeYear() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('MM/dd, yyyy');
    +  assertParsedDateEquals(1999, 11 - 1, 22, parser, '11/22, 1999', date);
    +  assertParsedDateEquals(-1999, 11 - 1, 22, parser, '11/22, -1999', date);
    +}
    +
    +function testEra() {
    +  // Bug 2350397
    +  if (goog.userAgent.WEBKIT) {
    +    // Bug 2350397 Test seems to be very flaky on Chrome. Disabling it
    +    return;
    +  }
    +
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('MM/dd, yyyyG');
    +  assertParsedDateEquals(-1998, 11 - 1, 22, parser, '11/22, 1999BC', date);
    +  assertParsedDateEquals(0, 11 - 1, 22, parser, '11/22, 1BC', date);
    +  assertParsedDateEquals(1999, 11 - 1, 22, parser, '11/22, 1999AD', date);
    +}
    +
    +function testFractionalSeconds() {
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('hh:mm:ss.SSS');
    +
    +  assertParsedTimeEquals(11, 12, 13, 956, parser, '11:12:13.956', date);
    +  assertParsedTimeEquals(11, 12, 13, 950, parser, '11:12:13.95', date);
    +  assertParsedTimeEquals(11, 12, 13, 900, parser, '11:12:13.9', date);
    +}
    +
    +function testAmbiguousYear() {
    +  // assume this year is 2006, year 27 to 99 will be interpret as 1927 to 1999
    +  // year 00 to 25 will be 2000 to 2025. Year 26 can be either 1926 or 2026
    +  // depend on the time being parsed and the time when this program runs.
    +  // For example, if the program is run at 2006/03/03 12:12:12, the following
    +  // code should work.
    +  // assertTrue(parser.parse('01/01/26 00:00:00:001', date) > 0);
    +  // assertTrue(date.getFullYear() == 2026 - 1900);
    +  // assertTrue(parser.parse('12/30/26 23:59:59:999', date) > 0);
    +  // assertTrue(date.getFullYear() == 1926 - 1900);
    +
    +  // Since this test can run in any time, some logic needed here.
    +
    +  var futureDate = new Date();
    +  futureDate.setFullYear(futureDate.getFullYear() +
    +      100 - goog.i18n.DateTimeParse.ambiguousYearCenturyStart);
    +  var ambiguousYear = futureDate.getFullYear() % 100;
    +
    +  var parser = new goog.i18n.DateTimeParse('MM/dd/yy HH:mm:ss:SSS');
    +  var date = new Date();
    +
    +  var str = '01/01/' + ambiguousYear + ' 00:00:00:001';
    +  assertTrue(parser.parse(str, date) > 0);
    +  assertEquals(futureDate.getFullYear(), date.getFullYear());
    +
    +  str = '12/31/' + ambiguousYear + ' 23:59:59:999';
    +  assertTrue(parser.parse(str, date) > 0);
    +  assertEquals(futureDate.getFullYear(), date.getFullYear() + 100);
    +
    +  // Test the ability to move the disambiguation century
    +  goog.i18n.DateTimeParse.ambiguousYearCenturyStart = 60;
    +
    +  futureDate = new Date();
    +  futureDate.setFullYear(futureDate.getFullYear() +
    +      100 - goog.i18n.DateTimeParse.ambiguousYearCenturyStart);
    +  ambiguousYear = futureDate.getFullYear() % 100;
    +
    +  var str = '01/01/' + ambiguousYear + ' 00:00:00:001';
    +  assertTrue(parser.parse(str, date) > 0);
    +  assertEquals(futureDate.getFullYear(), date.getFullYear());
    +
    +
    +  str = '12/31/' + ambiguousYear + ' 23:59:59:999';
    +  assertTrue(parser.parse(str, date) > 0);
    +  assertEquals(futureDate.getFullYear(), date.getFullYear() + 100);
    +
    +  // Reset parameter for other test cases
    +  goog.i18n.DateTimeParse.ambiguousYearCenturyStart = 80;
    +}
    +
    +function testLeapYear() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('MMdd, yyyy');
    +
    +  assertParsedDateEquals(2001, 3 - 1, 1, parser, '0229, 2001', date);
    +  assertParsedDateEquals(2000, 2 - 1, 29, parser, '0229, 2000', date);
    +}
    +
    +function testAbutField() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('hhmm');
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser, '1122', date);
    +  assertParsedTimeEquals(1, 22, undefined, undefined, parser, '122', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('hhmmss');
    +  assertParsedTimeEquals(11, 22, 33, undefined, parser2, '112233', date);
    +  assertParsedTimeEquals(1, 22, 33, undefined, parser2, '12233', date);
    +
    +  var parser3 = new goog.i18n.DateTimeParse('yyyyMMdd');
    +  assertParsedDateEquals(1999, 12 - 1, 2, parser3, '19991202', date);
    +  assertParsedDateEquals(999, 12 - 1, 2, parser3, '9991202', date);
    +  assertParsedDateEquals(99, 12 - 1, 2, parser3, '991202', date);
    +  assertParsedDateEquals(9, 12 - 1, 2, parser3, '91202', date);
    +}
    +
    +function testYearParsing() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('yyMMdd');
    +  assertParsedDateEquals(1999, 12 - 1, 2, parser, '991202', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('yyyyMMdd');
    +  assertParsedDateEquals(2005, 12 - 1, 2, parser2, '20051202', date);
    +}
    +
    +function testGoogDateParsing() {
    +  var date = new goog.date.Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('yyMMdd');
    +  assertParsedDateEquals(1999, 12 - 1, 2, parser, '991202', date);
    +}
    +
    +function testHourParsing_hh() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('hhmm');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '0022', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser, '1122', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '1222', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser, '2322', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '2422', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('hhmma');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '0022am', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser2, '1122am', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '1222am', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322am', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '0022pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '1122pm', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322pm', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422pm', date);
    +}
    +
    +function testHourParsing_KK() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('KKmm');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '0022', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser, '1122', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser, '1222', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser, '2322', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '2422', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('KKmma');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '0022am', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser2, '1122am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222am', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322am', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '0022pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '1122pm', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322pm', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422pm', date);
    +}
    +
    +function testHourParsing_kk() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('kkmm');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '0022', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser, '1122', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser, '1222', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser, '2322', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '2422', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('kkmma');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '0022am', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser2, '1122am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222am', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322am', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '0022pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '1122pm', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322pm', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422pm', date);
    +}
    +
    +function testHourParsing_HH() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('HHmm');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '0022', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser, '1122', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser, '1222', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser, '2322', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser, '2422', date);
    +
    +  var parser2 = new goog.i18n.DateTimeParse('HHmma');
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '0022am', date);
    +  assertParsedTimeEquals(11, 22, undefined, undefined, parser2, '1122am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222am', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322am', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422am', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '0022pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '1122pm', date);
    +  assertParsedTimeEquals(12, 22, undefined, undefined, parser2, '1222pm', date);
    +  assertParsedTimeEquals(23, 22, undefined, undefined, parser2, '2322pm', date);
    +  assertParsedTimeEquals(0, 22, undefined, undefined, parser2, '2422pm', date);
    +}
    +
    +function testEnglishDate() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('yyyy MMM dd hh:mm');
    +
    +  // Fails in Safari4/Chrome Winxp because of infrastructure issues, temporarily
    +  // disabled. See b/4274778.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    +  try {
    +    assertParsedDateEquals(2006, 7 - 1, 10, parser, '2006 Jul 10 15:44', date);
    +    assertTimeEquals(15, 44, undefined, undefined, date);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testChineseDate() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh;
    +
    +  // Javascript month start from 0, July is 7 - 1
    +  var date = new Date(2006, 7 - 1, 24, 12, 12, 12, 0);
    +  var formatter = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +  var dateStr = formatter.format(date);
    +  var parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +
    +  assertParsedDateEquals(2006, 7 - 1, 24, parser, dateStr, date);
    +
    +  parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.LONG_DATE);
    +  assertParsedDateEquals(
    +      2006, 7 - 1, 24, parser, '2006\u5E747\u670824\u65E5', date);
    +
    +  parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.FULL_TIME);
    +  assertTrue(parser.parse('GMT-07:00\u4E0B\u534803:26:28', date) > 0);
    +
    +  // Fails in Safari4/Chrome Winxp because of infrastructure issues, temporarily
    +  // disabled. See b/4274778.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    +  try {
    +    assertEquals(
    +        22, (24 + date.getHours() + date.getTimezoneOffset() / 60) % 24);
    +    assertEquals(26, date.getMinutes());
    +    assertEquals(28, date.getSeconds());
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +
    +}
    +
    +// For languages with goog.i18n.DateTimeSymbols.ZERODIGIT defined, the int
    +// digits are localized by the locale in datetimeformat.js. This test case is
    +// for parsing dates with such native digits.
    +function testDatesWithNativeDigits() {
    +  // Language Arabic is one example with
    +  // goog.i18n.DateTimeSymbols.ZERODIGIT defined.
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa;
    +
    +  // Javascript month starts from 0, July is 7 - 1
    +  var date = new Date(2006, 7 - 1, 24, 12, 12, 12, 0);
    +  var formatter = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +  dateStr = formatter.format(date);
    +  var parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE);
    +
    +  assertParsedDateEquals(2006, 7 - 1, 24, parser, dateStr, date);
    +
    +  date = new Date(2006, 7 - 1, 24);
    +  formatter = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.SHORT_DATE);
    +  dateStr = formatter.format(date);
    +  parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.SHORT_DATE);
    +
    +  assertParsedDateEquals(2006, 7 - 1, 24, parser, dateStr, date);
    +
    +  date = new Date();
    +
    +  parser = new goog.i18n.DateTimeParse('y/MM/dd H:mm:ss٫SS');
    +  assertParsedDateEquals(2006, 6, 27, parser, '۲۰۰۶/۰۷/۲۷ ۱۳:۱۰:۱۰٫۲۵', date);
    +}
    +
    +function testTimeZone() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('MM/dd/yyyy, hh:mm:ss zzz');
    +  assertTrue(parser.parse('07/21/2003, 11:22:33 GMT-0700', date) > 0);
    +  var hourGmtMinus07 = date.getHours();
    +
    +  assertTrue(parser.parse('07/21/2003, 11:22:33 GMT-0600', date) > 0);
    +  var hourGmtMinus06 = date.getHours();
    +  assertEquals(1, (hourGmtMinus07 + 24 - hourGmtMinus06) % 24);
    +
    +  assertTrue(parser.parse('07/21/2003, 11:22:33 GMT-0800', date) > 0);
    +  var hourGmtMinus08 = date.getHours();
    +  assertEquals(1, (hourGmtMinus08 + 24 - hourGmtMinus07) % 24);
    +
    +  assertTrue(parser.parse('07/21/2003, 23:22:33 GMT-0800', date) > 0);
    +  assertEquals((date.getHours() + 24 - hourGmtMinus07) % 24, 13);
    +
    +  assertTrue(parser.parse('07/21/2003, 11:22:33 GMT+0800', date) > 0);
    +  var hourGmt08 = date.getHours();
    +  assertEquals(16, (hourGmtMinus08 + 24 - hourGmt08) % 24);
    +
    +  assertTrue(parser.parse('07/21/2003, 11:22:33 GMT0800', date) > 0);
    +  assertEquals(hourGmt08, date.getHours());
    +}
    +
    +function testWeekDay() {
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('EEEE, MM/dd/yyyy');
    +
    +  assertTrue(parser.parse('Wednesday, 08/16/2006', date) > 0);
    +  assertDateEquals(2006, 8 - 1, 16, date);
    +  assertTrue(parser.parse('Tuesday, 08/16/2006', date) == 0);
    +  assertTrue(parser.parse('Thursday, 08/16/2006', date) == 0);
    +  assertTrue(parser.parse('Wed, 08/16/2006', date) > 0);
    +  assertTrue(parser.parse('Wasdfed, 08/16/2006', date) == 0);
    +
    +  date.setDate(25);
    +  parser = new goog.i18n.DateTimeParse('EEEE, MM/yyyy');
    +  assertTrue(parser.parse('Wed, 09/2006', date) > 0);
    +  assertEquals(27, date.getDate());
    +
    +  date.setDate(30);
    +  assertTrue(parser.parse('Wed, 09/2006', date) > 0);
    +  assertEquals(27, date.getDate());
    +  date.setDate(30);
    +  assertTrue(parser.parse('Mon, 09/2006', date) > 0);
    +  assertEquals(25, date.getDate());
    +}
    +
    +function testStrictParse() {
    +  var date = new Date();
    +
    +  var parser = new goog.i18n.DateTimeParse('yyyy/MM/dd');
    +  assertTrue(parser.strictParse('2000/13/10', date) == 0);
    +  assertTrue(parser.strictParse('2000/13/40', date) == 0);
    +  assertTrue(parser.strictParse('2000/11/10', date) > 0);
    +  assertDateEquals(2000, 11 - 1, 10, date);
    +
    +  parser = new goog.i18n.DateTimeParse('yy/MM/dd');
    +  assertTrue(parser.strictParse('00/11/10', date) > 0);
    +  assertTrue(parser.strictParse('99/11/10', date) > 0);
    +  assertTrue(parser.strictParse('00/13/10', date) == 0);
    +  assertTrue(parser.strictParse('00/11/32', date) == 0);
    +  assertTrue(parser.strictParse('1900/11/2', date) > 0);
    +
    +  parser = new goog.i18n.DateTimeParse('hh:mm');
    +  assertTrue(parser.strictParse('15:44', date) > 0);
    +  assertTrue(parser.strictParse('25:44', date) == 0);
    +  assertTrue(parser.strictParse('15:64', date) == 0);
    +
    +  // leap year
    +  parser = new goog.i18n.DateTimeParse('yy/MM/dd');
    +  assertTrue(parser.strictParse('00/02/29', date) > 0);
    +  assertTrue(parser.strictParse('01/02/29', date) == 0);
    +}
    +
    +function testPartialParses() {
    +  var date = new Date(0);
    +  var parser = new goog.i18n.DateTimeParse('h:mma');
    +  assertTrue(parser.parse('5:', date) > 0);
    +  assertEquals(5, date.getHours());
    +  assertEquals(0, date.getMinutes());
    +
    +  date = new Date(0);
    +  assertTrue(parser.parse('5:44pm', date) > 0);
    +  assertEquals(17, date.getHours());
    +  assertEquals(44, date.getMinutes());
    +
    +  date = new Date(0);
    +  assertTrue(parser.parse('5:44ym', date) > 0);
    +  assertEquals(5, date.getHours());
    +  assertEquals(44, date.getMinutes());
    +
    +  parser = new goog.i18n.DateTimeParse('mm:ss');
    +  date = new Date(0);
    +  assertTrue(parser.parse('15:', date) > 0);
    +  assertEquals(15, date.getMinutes());
    +  assertEquals(0, date.getSeconds());
    +}
    +
    +function testEnglishQuarter() {
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('QQQQ yyyy');
    +
    +  // Fails in Safari4/Chrome Winxp because of infrastructure issues, temporarily
    +  // disabled. See b/4274778.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    +  try {
    +    assertParsedDateEquals(2009, 0, 1, parser, '1st quarter 2009', date);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testEnglishShortQuarter() {
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('yyyyQQ');
    +
    +  // Fails in Safari4/Chrome Winxp because of infrastructure issues, temporarily
    +  // disabled. See b/4274778.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    +  try {
    +    assertParsedDateEquals(2006, 4 - 1, 1, parser, '2006Q2', date);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testFrenchShortQuarter() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
    +
    +  var date = new Date();
    +  var parser = new goog.i18n.DateTimeParse('yyyyQQ');
    +  assertParsedDateEquals(2009, 7 - 1, 1, parser, '2009T3', date);
    +}
    +
    +function testDateTime() {
    +  var dateOrg = new Date(2006, 7 - 1, 24, 17, 21, 42, 0);
    +
    +  var formatter = new goog.i18n.DateTimeFormat(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +  dateStr = formatter.format(dateOrg);
    +
    +  var parser = new goog.i18n.DateTimeParse(
    +      goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
    +  var dateParsed = new Date();
    +
    +  assertParsedDateEquals(dateOrg.getFullYear(), dateOrg.getMonth(),
    +      dateOrg.getDate(), parser, dateStr, dateParsed);
    +  assertTimeEquals(dateOrg.getHours(), dateOrg.getMinutes(),
    +      dateOrg.getSeconds(), undefined, dateParsed);
    +}
    +
    +
    +/** @bug 10075434 */
    +function testParseDateWithOverflow() {
    +
    +  // We force the initial day of month to 30 so that it will always cause an
    +  // overflow in February, no matter if it is a leap year or not.
    +  var dateOrg = new Date(2006, 7 - 1, 30, 17, 21, 42, 0);
    +  var dateParsed;  // this will receive the result of the parsing
    +
    +  var parserMonthYear = new goog.i18n.DateTimeParse('MMMM yyyy');
    +
    +  // The API can be a bit confusing, as this date is both input and output.
    +  // Benefit: fields that don't come from parsing are preserved.
    +  // In the typical use case, dateParsed = new Date()
    +  // and when you parse "February 3" the year is implied as "this year"
    +  // This works as intended.
    +  // But because of this we will initialize dateParsed from dateOrg
    +  // before every test (because the previous test changes it).
    +
    +  dateParsed = new Date(dateOrg.getTime());
    +  // if preserved February 30 overflows, so we get the closest February day, 28
    +  assertParsedDateEquals(
    +      2013, 2 - 1, 28, parserMonthYear, 'February 2013', dateParsed);
    +
    +  // Same as above, but the last February date is 29 (leap year)
    +  dateParsed = new Date(dateOrg.getTime());
    +  assertParsedDateEquals(
    +      2012, 2 - 1, 29, parserMonthYear, 'February 2012', dateParsed);
    +
    +  // Same as above, but no overflow (Match has 31 days, the parsed 30 is OK)
    +  dateParsed = new Date(dateOrg.getTime());
    +  assertParsedDateEquals(
    +      2013, 3 - 1, 30, parserMonthYear, 'March 2013', dateParsed);
    +
    +  // The pattern does not expect the day of month, so 12 is interpreted
    +  // as year, 12. May be weird, but this is the original behavior.
    +  // The overflow for leap year applies, same as above.
    +  dateParsed = new Date(dateOrg.getTime());
    +  assertParsedDateEquals(
    +      12, 2 - 1, 29, parserMonthYear, 'February 12, 2013', dateParsed);
    +
    +  // We make sure that the fix did not break parsing with day of month present
    +  var parserMonthDayYear = new goog.i18n.DateTimeParse('MMMM d, yyyy');
    +
    +  dateParsed = new Date(dateOrg.getTime());
    +  assertParsedDateEquals(
    +      2012, 2 - 1, 12, parserMonthDayYear, 'February 12, 2012', dateParsed);
    +
    +  // The current behavior when parsing 'February 31, 2012' is to
    +  // return 'March 2, 2012'
    +  // Expected or not, we make sure the fix does not break this.
    +  assertParsedDateEquals(
    +      2012, 3 - 1, 2, parserMonthDayYear, 'February 31, 2012', dateParsed);
    +}
    +
    +
    +/** @bug 9901750 */
    +function testStandaloneMonthPattern() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pl;
    +  var date1 = new goog.date.Date(2006, 7 - 1);
    +  var date2 = new goog.date.Date();
    +  var formatter =
    +      new goog.i18n.DateTimeFormat('LLLL yyyy');
    +  var parser =
    +      new goog.i18n.DateTimeParse('LLLL yyyy');
    +  var dateStr = formatter.format(date1);
    +  assertParsedDateEquals(
    +      date1.getFullYear(), date1.getMonth(), undefined, parser, dateStr, date2);
    +
    +  // Sanity tests to make sure MMM... (and LLL...) formats still work for
    +  // different locales.
    +  var symbols = [
    +    goog.i18n.DateTimeSymbols_en,
    +    goog.i18n.DateTimeSymbols_pl
    +  ];
    +
    +  for (var i = 0; i < symbols.length; i++) {
    +    goog.i18n.DateTimeSymbols = symbols[i];
    +    var tests = {
    +      'MMMM yyyy': goog.i18n.DateTimeSymbols.MONTHS,
    +      'LLLL yyyy': goog.i18n.DateTimeSymbols.STANDALONEMONTHS,
    +      'MMM yyyy': goog.i18n.DateTimeSymbols.SHORTMONTHS,
    +      'LLL yyyy': goog.i18n.DateTimeSymbols.STANDALONESHORTMONTHS
    +    };
    +
    +    for (var format in tests) {
    +      var parser = new goog.i18n.DateTimeParse(format);
    +      var months = tests[format];
    +      for (var m = 0; m < months.length; m++) {
    +        var dateStr = months[m] + ' 2006';
    +        var date = new goog.date.Date();
    +        assertParsedDateEquals(2006, m, undefined, parser, dateStr, date);
    +      }
    +    }
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimepatterns.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimepatterns.js
    new file mode 100644
    index 0000000000..8e301859a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimepatterns.js
    @@ -0,0 +1,2520 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Extended date/time patterns.
    + *
    + * This file is autogenerated by script.  See
    + * http://go/generate_datetime_pattern.cc
    + *
    + * This file is generated using ICU's implementation of
    + * DateTimePatternGenerator. The whole set has two files:
    + * datetimepatterns.js and datetimepatternsext.js. The former covers
    + * frequently used locales, the latter covers the rest. There won't be any
    + * difference in compiled code, but some developing environments have
    + * difficulty in dealing large js files. So we do the separation.
    +
    + * Only locales that can be enumerated in ICU are supported. For the rest
    + * of the locales, it will fallback to 'en'.
    + * The code is designed to work with Closure compiler using
    + * ADVANCED_OPTIMIZATIONS. We will continue to add popular date/time
    + * patterns over time. There is no intention cover all possible
    + * usages. If simple pattern works fine, it won't be covered here either.
    + * For example, pattern 'MMM' will work well to get short month name for
    + * almost all locales thus won't be included here.
    + */
    +
    +/* File generated from CLDR ver. 26.0 */
    +
    +goog.provide('goog.i18n.DateTimePatterns');
    +
    +goog.provide('goog.i18n.DateTimePatterns_af');
    +goog.provide('goog.i18n.DateTimePatterns_am');
    +goog.provide('goog.i18n.DateTimePatterns_ar');
    +goog.provide('goog.i18n.DateTimePatterns_az');
    +goog.provide('goog.i18n.DateTimePatterns_bg');
    +goog.provide('goog.i18n.DateTimePatterns_bn');
    +goog.provide('goog.i18n.DateTimePatterns_br');
    +goog.provide('goog.i18n.DateTimePatterns_ca');
    +goog.provide('goog.i18n.DateTimePatterns_chr');
    +goog.provide('goog.i18n.DateTimePatterns_cs');
    +goog.provide('goog.i18n.DateTimePatterns_cy');
    +goog.provide('goog.i18n.DateTimePatterns_da');
    +goog.provide('goog.i18n.DateTimePatterns_de');
    +goog.provide('goog.i18n.DateTimePatterns_de_AT');
    +goog.provide('goog.i18n.DateTimePatterns_de_CH');
    +goog.provide('goog.i18n.DateTimePatterns_el');
    +goog.provide('goog.i18n.DateTimePatterns_en');
    +goog.provide('goog.i18n.DateTimePatterns_en_AU');
    +goog.provide('goog.i18n.DateTimePatterns_en_GB');
    +goog.provide('goog.i18n.DateTimePatterns_en_IE');
    +goog.provide('goog.i18n.DateTimePatterns_en_IN');
    +goog.provide('goog.i18n.DateTimePatterns_en_SG');
    +goog.provide('goog.i18n.DateTimePatterns_en_US');
    +goog.provide('goog.i18n.DateTimePatterns_en_ZA');
    +goog.provide('goog.i18n.DateTimePatterns_es');
    +goog.provide('goog.i18n.DateTimePatterns_es_419');
    +goog.provide('goog.i18n.DateTimePatterns_es_ES');
    +goog.provide('goog.i18n.DateTimePatterns_et');
    +goog.provide('goog.i18n.DateTimePatterns_eu');
    +goog.provide('goog.i18n.DateTimePatterns_fa');
    +goog.provide('goog.i18n.DateTimePatterns_fi');
    +goog.provide('goog.i18n.DateTimePatterns_fil');
    +goog.provide('goog.i18n.DateTimePatterns_fr');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CA');
    +goog.provide('goog.i18n.DateTimePatterns_ga');
    +goog.provide('goog.i18n.DateTimePatterns_gl');
    +goog.provide('goog.i18n.DateTimePatterns_gsw');
    +goog.provide('goog.i18n.DateTimePatterns_gu');
    +goog.provide('goog.i18n.DateTimePatterns_haw');
    +goog.provide('goog.i18n.DateTimePatterns_he');
    +goog.provide('goog.i18n.DateTimePatterns_hi');
    +goog.provide('goog.i18n.DateTimePatterns_hr');
    +goog.provide('goog.i18n.DateTimePatterns_hu');
    +goog.provide('goog.i18n.DateTimePatterns_hy');
    +goog.provide('goog.i18n.DateTimePatterns_id');
    +goog.provide('goog.i18n.DateTimePatterns_in');
    +goog.provide('goog.i18n.DateTimePatterns_is');
    +goog.provide('goog.i18n.DateTimePatterns_it');
    +goog.provide('goog.i18n.DateTimePatterns_iw');
    +goog.provide('goog.i18n.DateTimePatterns_ja');
    +goog.provide('goog.i18n.DateTimePatterns_ka');
    +goog.provide('goog.i18n.DateTimePatterns_kk');
    +goog.provide('goog.i18n.DateTimePatterns_km');
    +goog.provide('goog.i18n.DateTimePatterns_kn');
    +goog.provide('goog.i18n.DateTimePatterns_ko');
    +goog.provide('goog.i18n.DateTimePatterns_ky');
    +goog.provide('goog.i18n.DateTimePatterns_ln');
    +goog.provide('goog.i18n.DateTimePatterns_lo');
    +goog.provide('goog.i18n.DateTimePatterns_lt');
    +goog.provide('goog.i18n.DateTimePatterns_lv');
    +goog.provide('goog.i18n.DateTimePatterns_mk');
    +goog.provide('goog.i18n.DateTimePatterns_ml');
    +goog.provide('goog.i18n.DateTimePatterns_mn');
    +goog.provide('goog.i18n.DateTimePatterns_mo');
    +goog.provide('goog.i18n.DateTimePatterns_mr');
    +goog.provide('goog.i18n.DateTimePatterns_ms');
    +goog.provide('goog.i18n.DateTimePatterns_mt');
    +goog.provide('goog.i18n.DateTimePatterns_my');
    +goog.provide('goog.i18n.DateTimePatterns_nb');
    +goog.provide('goog.i18n.DateTimePatterns_ne');
    +goog.provide('goog.i18n.DateTimePatterns_nl');
    +goog.provide('goog.i18n.DateTimePatterns_no');
    +goog.provide('goog.i18n.DateTimePatterns_no_NO');
    +goog.provide('goog.i18n.DateTimePatterns_or');
    +goog.provide('goog.i18n.DateTimePatterns_pa');
    +goog.provide('goog.i18n.DateTimePatterns_pl');
    +goog.provide('goog.i18n.DateTimePatterns_pt');
    +goog.provide('goog.i18n.DateTimePatterns_pt_BR');
    +goog.provide('goog.i18n.DateTimePatterns_pt_PT');
    +goog.provide('goog.i18n.DateTimePatterns_ro');
    +goog.provide('goog.i18n.DateTimePatterns_ru');
    +goog.provide('goog.i18n.DateTimePatterns_sh');
    +goog.provide('goog.i18n.DateTimePatterns_si');
    +goog.provide('goog.i18n.DateTimePatterns_sk');
    +goog.provide('goog.i18n.DateTimePatterns_sl');
    +goog.provide('goog.i18n.DateTimePatterns_sq');
    +goog.provide('goog.i18n.DateTimePatterns_sr');
    +goog.provide('goog.i18n.DateTimePatterns_sv');
    +goog.provide('goog.i18n.DateTimePatterns_sw');
    +goog.provide('goog.i18n.DateTimePatterns_ta');
    +goog.provide('goog.i18n.DateTimePatterns_te');
    +goog.provide('goog.i18n.DateTimePatterns_th');
    +goog.provide('goog.i18n.DateTimePatterns_tl');
    +goog.provide('goog.i18n.DateTimePatterns_tr');
    +goog.provide('goog.i18n.DateTimePatterns_uk');
    +goog.provide('goog.i18n.DateTimePatterns_ur');
    +goog.provide('goog.i18n.DateTimePatterns_uz');
    +goog.provide('goog.i18n.DateTimePatterns_vi');
    +goog.provide('goog.i18n.DateTimePatterns_zh');
    +goog.provide('goog.i18n.DateTimePatterns_zh_CN');
    +goog.provide('goog.i18n.DateTimePatterns_zh_HK');
    +goog.provide('goog.i18n.DateTimePatterns_zh_TW');
    +goog.provide('goog.i18n.DateTimePatterns_zu');
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale af.
    + */
    +goog.i18n.DateTimePatterns_af = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale am.
    + */
    +goog.i18n.DateTimePatterns_am = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE፣ MMM d y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar.
    + */
    +goog.i18n.DateTimePatterns_ar = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale az.
    + */
    +goog.i18n.DateTimePatterns_az = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bg.
    + */
    +goog.i18n.DateTimePatterns_bg = {
    +  YEAR_FULL: 'y \'г\'.',
    +  YEAR_FULL_WITH_ERA: 'y \'г\'. G',
    +  YEAR_MONTH_ABBR: 'MM.y \'г\'.',
    +  YEAR_MONTH_FULL: 'MMMM y \'г\'.',
    +  MONTH_DAY_ABBR: 'd.MM',
    +  MONTH_DAY_FULL: 'd MMMM',
    +  MONTH_DAY_SHORT: 'd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd.MM.y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d.MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d.MM.y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bn.
    + */
    +goog.i18n.DateTimePatterns_bn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale br.
    + */
    +goog.i18n.DateTimePatterns_br = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ca.
    + */
    +goog.i18n.DateTimePatterns_ca = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL \'de\' y',
    +  YEAR_MONTH_FULL: 'LLLL \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale chr.
    + */
    +goog.i18n.DateTimePatterns_chr = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cs.
    + */
    +goog.i18n.DateTimePatterns_cs = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. M.',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. M. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. M.',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. M. y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cy.
    + */
    +goog.i18n.DateTimePatterns_cy = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale da.
    + */
    +goog.i18n.DateTimePatterns_da = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de.
    + */
    +goog.i18n.DateTimePatterns_de = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_AT.
    + */
    +goog.i18n.DateTimePatterns_de_AT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_CH.
    + */
    +goog.i18n.DateTimePatterns_de_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale el.
    + */
    +goog.i18n.DateTimePatterns_el = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en.
    + */
    +goog.i18n.DateTimePatterns_en = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_AU.
    + */
    +goog.i18n.DateTimePatterns_en_AU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GB.
    + */
    +goog.i18n.DateTimePatterns_en_GB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_IE.
    + */
    +goog.i18n.DateTimePatterns_en_IE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_IN.
    + */
    +goog.i18n.DateTimePatterns_en_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SG.
    + */
    +goog.i18n.DateTimePatterns_en_SG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_US.
    + */
    +goog.i18n.DateTimePatterns_en_US = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_ZA.
    + */
    +goog.i18n.DateTimePatterns_en_ZA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'dd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'MM/dd',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es.
    + */
    +goog.i18n.DateTimePatterns_es = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_419.
    + */
    +goog.i18n.DateTimePatterns_es_419 = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_ES.
    + */
    +goog.i18n.DateTimePatterns_es_ES = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale et.
    + */
    +goog.i18n.DateTimePatterns_et = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale eu.
    + */
    +goog.i18n.DateTimePatterns_eu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y(\'e\')\'ko\' MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fa.
    + */
    +goog.i18n.DateTimePatterns_fa = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd LLL',
    +  MONTH_DAY_FULL: 'dd LLLL',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd LLLL',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d LLL',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fi.
    + */
    +goog.i18n.DateTimePatterns_fi = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fil.
    + */
    +goog.i18n.DateTimePatterns_fil = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr.
    + */
    +goog.i18n.DateTimePatterns_fr = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CA.
    + */
    +goog.i18n.DateTimePatterns_fr_CA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ga.
    + */
    +goog.i18n.DateTimePatterns_ga = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gl.
    + */
    +goog.i18n.DateTimePatterns_gl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gsw.
    + */
    +goog.i18n.DateTimePatterns_gsw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gu.
    + */
    +goog.i18n.DateTimePatterns_gu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale haw.
    + */
    +goog.i18n.DateTimePatterns_haw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale he.
    + */
    +goog.i18n.DateTimePatterns_he = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd בMMM',
    +  MONTH_DAY_FULL: 'dd בMMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd בMMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd בMMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d בMMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d בMMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hi.
    + */
    +goog.i18n.DateTimePatterns_hi = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hr.
    + */
    +goog.i18n.DateTimePatterns_hr = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'LLL y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hu.
    + */
    +goog.i18n.DateTimePatterns_hu = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'G y.',
    +  YEAR_MONTH_ABBR: 'y. MMM',
    +  YEAR_MONTH_FULL: 'y. MMMM',
    +  MONTH_DAY_ABBR: 'MMM d.',
    +  MONTH_DAY_FULL: 'MMMM dd.',
    +  MONTH_DAY_SHORT: 'M. d.',
    +  MONTH_DAY_MEDIUM: 'MMMM d.',
    +  MONTH_DAY_YEAR_MEDIUM: 'y. MMM d.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d., EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y. MMM d., EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hy.
    + */
    +goog.i18n.DateTimePatterns_hy = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G yթ.',
    +  YEAR_MONTH_ABBR: 'yթ. LLL',
    +  YEAR_MONTH_FULL: 'yթ. LLLL',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, yթ.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'yթ. MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale id.
    + */
    +goog.i18n.DateTimePatterns_id = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale in.
    + */
    +goog.i18n.DateTimePatterns_in = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale is.
    + */
    +goog.i18n.DateTimePatterns_is = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale it.
    + */
    +goog.i18n.DateTimePatterns_it = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale iw.
    + */
    +goog.i18n.DateTimePatterns_iw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd בMMM',
    +  MONTH_DAY_FULL: 'dd בMMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd בMMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd בMMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d בMMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d בMMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ja.
    + */
    +goog.i18n.DateTimePatterns_ja = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日(EEE)',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日(EEE)',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ka.
    + */
    +goog.i18n.DateTimePatterns_ka = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM, y',
    +  YEAR_MONTH_FULL: 'MMMM, y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kk.
    + */
    +goog.i18n.DateTimePatterns_kk = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale km.
    + */
    +goog.i18n.DateTimePatterns_km = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y នៃ G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kn.
    + */
    +goog.i18n.DateTimePatterns_kn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d,y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ko.
    + */
    +goog.i18n.DateTimePatterns_ko = {
    +  YEAR_FULL: 'y년',
    +  YEAR_FULL_WITH_ERA: 'G y년',
    +  YEAR_MONTH_ABBR: 'y년 MMM',
    +  YEAR_MONTH_FULL: 'y년 MMMM',
    +  MONTH_DAY_ABBR: 'MMM d일',
    +  MONTH_DAY_FULL: 'MMMM dd일',
    +  MONTH_DAY_SHORT: 'M. d.',
    +  MONTH_DAY_MEDIUM: 'MMMM d일',
    +  MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d일 (EEE)',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일 (EEE)',
    +  DAY_ABBR: 'd일'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ky.
    + */
    +goog.i18n.DateTimePatterns_ky = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y-\'ж\'.',
    +  YEAR_MONTH_ABBR: 'y-\'ж\'. MMM',
    +  YEAR_MONTH_FULL: 'y-\'ж\'. MMMM',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd-MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd-MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ln.
    + */
    +goog.i18n.DateTimePatterns_ln = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lo.
    + */
    +goog.i18n.DateTimePatterns_lo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lt.
    + */
    +goog.i18n.DateTimePatterns_lt = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'y-MM',
    +  YEAR_MONTH_FULL: 'y LLLL',
    +  MONTH_DAY_ABBR: 'MM-dd',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y-MM-dd',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MM-dd, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y-MM-dd, EEE',
    +  DAY_ABBR: 'dd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lv.
    + */
    +goog.i18n.DateTimePatterns_lv = {
    +  YEAR_FULL: 'y. \'g\'.',
    +  YEAR_FULL_WITH_ERA: 'G y. \'g\'.',
    +  YEAR_MONTH_ABBR: 'y. \'g\'. MMM',
    +  YEAR_MONTH_FULL: 'y. \'g\'. MMMM',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y. \'g\'. d. MMM',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y. \'g\'. d. MMM',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mk.
    + */
    +goog.i18n.DateTimePatterns_mk = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y \'г\'.',
    +  YEAR_MONTH_FULL: 'MMMM y \'г\'.',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ml.
    + */
    +goog.i18n.DateTimePatterns_ml = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mn.
    + */
    +goog.i18n.DateTimePatterns_mn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mo.
    + */
    +goog.i18n.DateTimePatterns_mo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mr.
    + */
    +goog.i18n.DateTimePatterns_mr = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ms.
    + */
    +goog.i18n.DateTimePatterns_ms = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mt.
    + */
    +goog.i18n.DateTimePatterns_mt = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale my.
    + */
    +goog.i18n.DateTimePatterns_my = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nb.
    + */
    +goog.i18n.DateTimePatterns_nb = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ne.
    + */
    +goog.i18n.DateTimePatterns_ne = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl.
    + */
    +goog.i18n.DateTimePatterns_nl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale no.
    + */
    +goog.i18n.DateTimePatterns_no = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale no_NO.
    + */
    +goog.i18n.DateTimePatterns_no_NO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale or.
    + */
    +goog.i18n.DateTimePatterns_or = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pa.
    + */
    +goog.i18n.DateTimePatterns_pa = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pl.
    + */
    +goog.i18n.DateTimePatterns_pl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt.
    + */
    +goog.i18n.DateTimePatterns_pt = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_BR.
    + */
    +goog.i18n.DateTimePatterns_pt_BR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_PT.
    + */
    +goog.i18n.DateTimePatterns_pt_PT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ro.
    + */
    +goog.i18n.DateTimePatterns_ro = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru.
    + */
    +goog.i18n.DateTimePatterns_ru = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sh.
    + */
    +goog.i18n.DateTimePatterns_sh = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale si.
    + */
    +goog.i18n.DateTimePatterns_si = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sk.
    + */
    +goog.i18n.DateTimePatterns_sk = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. M',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. M. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. M.',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. M. y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sl.
    + */
    +goog.i18n.DateTimePatterns_sl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sq.
    + */
    +goog.i18n.DateTimePatterns_sq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr.
    + */
    +goog.i18n.DateTimePatterns_sr = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sv.
    + */
    +goog.i18n.DateTimePatterns_sv = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sw.
    + */
    +goog.i18n.DateTimePatterns_sw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ta.
    + */
    +goog.i18n.DateTimePatterns_ta = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale te.
    + */
    +goog.i18n.DateTimePatterns_te = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd, MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale th.
    + */
    +goog.i18n.DateTimePatterns_th = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tl.
    + */
    +goog.i18n.DateTimePatterns_tl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tr.
    + */
    +goog.i18n.DateTimePatterns_tr = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMMM EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uk.
    + */
    +goog.i18n.DateTimePatterns_uk = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ur.
    + */
    +goog.i18n.DateTimePatterns_ur = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz.
    + */
    +goog.i18n.DateTimePatterns_uz = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vi.
    + */
    +goog.i18n.DateTimePatterns_vi = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM \'năm\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh.
    + */
    +goog.i18n.DateTimePatterns_zh = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_CN.
    + */
    +goog.i18n.DateTimePatterns_zh_CN = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_HK.
    + */
    +goog.i18n.DateTimePatterns_zh_HK = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_TW.
    + */
    +goog.i18n.DateTimePatterns_zh_TW = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日 EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日 EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zu.
    + */
    +goog.i18n.DateTimePatterns_zu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Select date/time pattern by locale.
    + */
    +goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en;
    +
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_af;
    +}
    +
    +if (goog.LOCALE == 'am') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_am;
    +}
    +
    +if (goog.LOCALE == 'ar') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar;
    +}
    +
    +if (goog.LOCALE == 'az') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_az;
    +}
    +
    +if (goog.LOCALE == 'bg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bg;
    +}
    +
    +if (goog.LOCALE == 'bn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bn;
    +}
    +
    +if (goog.LOCALE == 'br') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_br;
    +}
    +
    +if (goog.LOCALE == 'ca') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ca;
    +}
    +
    +if (goog.LOCALE == 'chr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_chr;
    +}
    +
    +if (goog.LOCALE == 'cs') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cs;
    +}
    +
    +if (goog.LOCALE == 'cy') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cy;
    +}
    +
    +if (goog.LOCALE == 'da') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_da;
    +}
    +
    +if (goog.LOCALE == 'de') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de;
    +}
    +
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_AT;
    +}
    +
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_CH;
    +}
    +
    +if (goog.LOCALE == 'el') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_el;
    +}
    +
    +if (goog.LOCALE == 'en') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en;
    +}
    +
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_AU;
    +}
    +
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GB;
    +}
    +
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_IE;
    +}
    +
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_IN;
    +}
    +
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SG;
    +}
    +
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_US;
    +}
    +
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_ZA;
    +}
    +
    +if (goog.LOCALE == 'es') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es;
    +}
    +
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_419;
    +}
    +
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_ES;
    +}
    +
    +if (goog.LOCALE == 'et') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_et;
    +}
    +
    +if (goog.LOCALE == 'eu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_eu;
    +}
    +
    +if (goog.LOCALE == 'fa') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fa;
    +}
    +
    +if (goog.LOCALE == 'fi') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fi;
    +}
    +
    +if (goog.LOCALE == 'fil') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fil;
    +}
    +
    +if (goog.LOCALE == 'fr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CA;
    +}
    +
    +if (goog.LOCALE == 'ga') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ga;
    +}
    +
    +if (goog.LOCALE == 'gl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gl;
    +}
    +
    +if (goog.LOCALE == 'gsw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gsw;
    +}
    +
    +if (goog.LOCALE == 'gu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gu;
    +}
    +
    +if (goog.LOCALE == 'haw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_haw;
    +}
    +
    +if (goog.LOCALE == 'he') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_he;
    +}
    +
    +if (goog.LOCALE == 'hi') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hi;
    +}
    +
    +if (goog.LOCALE == 'hr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hr;
    +}
    +
    +if (goog.LOCALE == 'hu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hu;
    +}
    +
    +if (goog.LOCALE == 'hy') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hy;
    +}
    +
    +if (goog.LOCALE == 'id') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_id;
    +}
    +
    +if (goog.LOCALE == 'in') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_in;
    +}
    +
    +if (goog.LOCALE == 'is') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_is;
    +}
    +
    +if (goog.LOCALE == 'it') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_it;
    +}
    +
    +if (goog.LOCALE == 'iw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_iw;
    +}
    +
    +if (goog.LOCALE == 'ja') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ja;
    +}
    +
    +if (goog.LOCALE == 'ka') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ka;
    +}
    +
    +if (goog.LOCALE == 'kk') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kk;
    +}
    +
    +if (goog.LOCALE == 'km') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_km;
    +}
    +
    +if (goog.LOCALE == 'kn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kn;
    +}
    +
    +if (goog.LOCALE == 'ko') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ko;
    +}
    +
    +if (goog.LOCALE == 'ky') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ky;
    +}
    +
    +if (goog.LOCALE == 'ln') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ln;
    +}
    +
    +if (goog.LOCALE == 'lo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lo;
    +}
    +
    +if (goog.LOCALE == 'lt') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lt;
    +}
    +
    +if (goog.LOCALE == 'lv') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lv;
    +}
    +
    +if (goog.LOCALE == 'mk') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mk;
    +}
    +
    +if (goog.LOCALE == 'ml') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ml;
    +}
    +
    +if (goog.LOCALE == 'mn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mn;
    +}
    +
    +if (goog.LOCALE == 'mo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mo;
    +}
    +
    +if (goog.LOCALE == 'mr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mr;
    +}
    +
    +if (goog.LOCALE == 'ms') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ms;
    +}
    +
    +if (goog.LOCALE == 'mt') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mt;
    +}
    +
    +if (goog.LOCALE == 'my') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_my;
    +}
    +
    +if (goog.LOCALE == 'nb') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nb;
    +}
    +
    +if (goog.LOCALE == 'ne') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ne;
    +}
    +
    +if (goog.LOCALE == 'nl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl;
    +}
    +
    +if (goog.LOCALE == 'no') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_no;
    +}
    +
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_no_NO;
    +}
    +
    +if (goog.LOCALE == 'or') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_or;
    +}
    +
    +if (goog.LOCALE == 'pa') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pa;
    +}
    +
    +if (goog.LOCALE == 'pl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pl;
    +}
    +
    +if (goog.LOCALE == 'pt') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_BR;
    +}
    +
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_PT;
    +}
    +
    +if (goog.LOCALE == 'ro') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ro;
    +}
    +
    +if (goog.LOCALE == 'ru') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru;
    +}
    +
    +if (goog.LOCALE == 'sh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sh;
    +}
    +
    +if (goog.LOCALE == 'si') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_si;
    +}
    +
    +if (goog.LOCALE == 'sk') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sk;
    +}
    +
    +if (goog.LOCALE == 'sl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sl;
    +}
    +
    +if (goog.LOCALE == 'sq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sq;
    +}
    +
    +if (goog.LOCALE == 'sr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr;
    +}
    +
    +if (goog.LOCALE == 'sv') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sv;
    +}
    +
    +if (goog.LOCALE == 'sw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sw;
    +}
    +
    +if (goog.LOCALE == 'ta') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ta;
    +}
    +
    +if (goog.LOCALE == 'te') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_te;
    +}
    +
    +if (goog.LOCALE == 'th') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_th;
    +}
    +
    +if (goog.LOCALE == 'tl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tl;
    +}
    +
    +if (goog.LOCALE == 'tr') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tr;
    +}
    +
    +if (goog.LOCALE == 'uk') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uk;
    +}
    +
    +if (goog.LOCALE == 'ur') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ur;
    +}
    +
    +if (goog.LOCALE == 'uz') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz;
    +}
    +
    +if (goog.LOCALE == 'vi') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vi;
    +}
    +
    +if (goog.LOCALE == 'zh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_CN;
    +}
    +
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_TW;
    +}
    +
    +if (goog.LOCALE == 'zu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zu;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimepatternsext.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimepatternsext.js
    new file mode 100644
    index 0000000000..3661d00582
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimepatternsext.js
    @@ -0,0 +1,14232 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Extended date/time patterns.
    + *
    + * This file is autogenerated by script.  See
    + * http://go/generate_datetime_pattern.cc
    + *
    + * This file is generated using ICU's implementation of
    + * DateTimePatternGenerator. The whole set has two files:
    + * datetimepatterns.js and datetimepatternsext.js. The former covers
    + * frequently used locales, the latter covers the rest. There won't be any
    + * difference in compiled code, but some developing environments have
    + * difficulty in dealing large js files. So we do the separation.
    +
    + * Only locales that can be enumerated in ICU are supported. For the rest
    + * of the locales, it will fallback to 'en'.
    + * The code is designed to work with Closure compiler using
    + * ADVANCED_OPTIMIZATIONS. We will continue to add popular date/time
    + * patterns over time. There is no intention cover all possible
    + * usages. If simple pattern works fine, it won't be covered here either.
    + * For example, pattern 'MMM' will work well to get short month name for
    + * almost all locales thus won't be included here.
    + */
    +
    +/* File generated from CLDR ver. 26.0 */
    +
    +goog.provide('goog.i18n.DateTimePatternsExt');
    +
    +goog.provide('goog.i18n.DateTimePatterns_af_NA');
    +goog.provide('goog.i18n.DateTimePatterns_af_ZA');
    +goog.provide('goog.i18n.DateTimePatterns_agq');
    +goog.provide('goog.i18n.DateTimePatterns_agq_CM');
    +goog.provide('goog.i18n.DateTimePatterns_ak');
    +goog.provide('goog.i18n.DateTimePatterns_ak_GH');
    +goog.provide('goog.i18n.DateTimePatterns_am_ET');
    +goog.provide('goog.i18n.DateTimePatterns_ar_001');
    +goog.provide('goog.i18n.DateTimePatterns_ar_AE');
    +goog.provide('goog.i18n.DateTimePatterns_ar_BH');
    +goog.provide('goog.i18n.DateTimePatterns_ar_DJ');
    +goog.provide('goog.i18n.DateTimePatterns_ar_DZ');
    +goog.provide('goog.i18n.DateTimePatterns_ar_EG');
    +goog.provide('goog.i18n.DateTimePatterns_ar_EH');
    +goog.provide('goog.i18n.DateTimePatterns_ar_ER');
    +goog.provide('goog.i18n.DateTimePatterns_ar_IL');
    +goog.provide('goog.i18n.DateTimePatterns_ar_IQ');
    +goog.provide('goog.i18n.DateTimePatterns_ar_JO');
    +goog.provide('goog.i18n.DateTimePatterns_ar_KM');
    +goog.provide('goog.i18n.DateTimePatterns_ar_KW');
    +goog.provide('goog.i18n.DateTimePatterns_ar_LB');
    +goog.provide('goog.i18n.DateTimePatterns_ar_LY');
    +goog.provide('goog.i18n.DateTimePatterns_ar_MA');
    +goog.provide('goog.i18n.DateTimePatterns_ar_MR');
    +goog.provide('goog.i18n.DateTimePatterns_ar_OM');
    +goog.provide('goog.i18n.DateTimePatterns_ar_PS');
    +goog.provide('goog.i18n.DateTimePatterns_ar_QA');
    +goog.provide('goog.i18n.DateTimePatterns_ar_SA');
    +goog.provide('goog.i18n.DateTimePatterns_ar_SD');
    +goog.provide('goog.i18n.DateTimePatterns_ar_SO');
    +goog.provide('goog.i18n.DateTimePatterns_ar_SS');
    +goog.provide('goog.i18n.DateTimePatterns_ar_SY');
    +goog.provide('goog.i18n.DateTimePatterns_ar_TD');
    +goog.provide('goog.i18n.DateTimePatterns_ar_TN');
    +goog.provide('goog.i18n.DateTimePatterns_ar_YE');
    +goog.provide('goog.i18n.DateTimePatterns_as');
    +goog.provide('goog.i18n.DateTimePatterns_as_IN');
    +goog.provide('goog.i18n.DateTimePatterns_asa');
    +goog.provide('goog.i18n.DateTimePatterns_asa_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_az_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_az_Cyrl_AZ');
    +goog.provide('goog.i18n.DateTimePatterns_az_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_az_Latn_AZ');
    +goog.provide('goog.i18n.DateTimePatterns_bas');
    +goog.provide('goog.i18n.DateTimePatterns_bas_CM');
    +goog.provide('goog.i18n.DateTimePatterns_be');
    +goog.provide('goog.i18n.DateTimePatterns_be_BY');
    +goog.provide('goog.i18n.DateTimePatterns_bem');
    +goog.provide('goog.i18n.DateTimePatterns_bem_ZM');
    +goog.provide('goog.i18n.DateTimePatterns_bez');
    +goog.provide('goog.i18n.DateTimePatterns_bez_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_bg_BG');
    +goog.provide('goog.i18n.DateTimePatterns_bm');
    +goog.provide('goog.i18n.DateTimePatterns_bm_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_bm_Latn_ML');
    +goog.provide('goog.i18n.DateTimePatterns_bn_BD');
    +goog.provide('goog.i18n.DateTimePatterns_bn_IN');
    +goog.provide('goog.i18n.DateTimePatterns_bo');
    +goog.provide('goog.i18n.DateTimePatterns_bo_CN');
    +goog.provide('goog.i18n.DateTimePatterns_bo_IN');
    +goog.provide('goog.i18n.DateTimePatterns_br_FR');
    +goog.provide('goog.i18n.DateTimePatterns_brx');
    +goog.provide('goog.i18n.DateTimePatterns_brx_IN');
    +goog.provide('goog.i18n.DateTimePatterns_bs');
    +goog.provide('goog.i18n.DateTimePatterns_bs_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_bs_Cyrl_BA');
    +goog.provide('goog.i18n.DateTimePatterns_bs_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_bs_Latn_BA');
    +goog.provide('goog.i18n.DateTimePatterns_ca_AD');
    +goog.provide('goog.i18n.DateTimePatterns_ca_ES');
    +goog.provide('goog.i18n.DateTimePatterns_ca_FR');
    +goog.provide('goog.i18n.DateTimePatterns_ca_IT');
    +goog.provide('goog.i18n.DateTimePatterns_cgg');
    +goog.provide('goog.i18n.DateTimePatterns_cgg_UG');
    +goog.provide('goog.i18n.DateTimePatterns_chr_US');
    +goog.provide('goog.i18n.DateTimePatterns_cs_CZ');
    +goog.provide('goog.i18n.DateTimePatterns_cy_GB');
    +goog.provide('goog.i18n.DateTimePatterns_da_DK');
    +goog.provide('goog.i18n.DateTimePatterns_da_GL');
    +goog.provide('goog.i18n.DateTimePatterns_dav');
    +goog.provide('goog.i18n.DateTimePatterns_dav_KE');
    +goog.provide('goog.i18n.DateTimePatterns_de_BE');
    +goog.provide('goog.i18n.DateTimePatterns_de_DE');
    +goog.provide('goog.i18n.DateTimePatterns_de_LI');
    +goog.provide('goog.i18n.DateTimePatterns_de_LU');
    +goog.provide('goog.i18n.DateTimePatterns_dje');
    +goog.provide('goog.i18n.DateTimePatterns_dje_NE');
    +goog.provide('goog.i18n.DateTimePatterns_dsb');
    +goog.provide('goog.i18n.DateTimePatterns_dsb_DE');
    +goog.provide('goog.i18n.DateTimePatterns_dua');
    +goog.provide('goog.i18n.DateTimePatterns_dua_CM');
    +goog.provide('goog.i18n.DateTimePatterns_dyo');
    +goog.provide('goog.i18n.DateTimePatterns_dyo_SN');
    +goog.provide('goog.i18n.DateTimePatterns_dz');
    +goog.provide('goog.i18n.DateTimePatterns_dz_BT');
    +goog.provide('goog.i18n.DateTimePatterns_ebu');
    +goog.provide('goog.i18n.DateTimePatterns_ebu_KE');
    +goog.provide('goog.i18n.DateTimePatterns_ee');
    +goog.provide('goog.i18n.DateTimePatterns_ee_GH');
    +goog.provide('goog.i18n.DateTimePatterns_ee_TG');
    +goog.provide('goog.i18n.DateTimePatterns_el_CY');
    +goog.provide('goog.i18n.DateTimePatterns_el_GR');
    +goog.provide('goog.i18n.DateTimePatterns_en_001');
    +goog.provide('goog.i18n.DateTimePatterns_en_150');
    +goog.provide('goog.i18n.DateTimePatterns_en_AG');
    +goog.provide('goog.i18n.DateTimePatterns_en_AI');
    +goog.provide('goog.i18n.DateTimePatterns_en_AS');
    +goog.provide('goog.i18n.DateTimePatterns_en_BB');
    +goog.provide('goog.i18n.DateTimePatterns_en_BE');
    +goog.provide('goog.i18n.DateTimePatterns_en_BM');
    +goog.provide('goog.i18n.DateTimePatterns_en_BS');
    +goog.provide('goog.i18n.DateTimePatterns_en_BW');
    +goog.provide('goog.i18n.DateTimePatterns_en_BZ');
    +goog.provide('goog.i18n.DateTimePatterns_en_CA');
    +goog.provide('goog.i18n.DateTimePatterns_en_CC');
    +goog.provide('goog.i18n.DateTimePatterns_en_CK');
    +goog.provide('goog.i18n.DateTimePatterns_en_CM');
    +goog.provide('goog.i18n.DateTimePatterns_en_CX');
    +goog.provide('goog.i18n.DateTimePatterns_en_DG');
    +goog.provide('goog.i18n.DateTimePatterns_en_DM');
    +goog.provide('goog.i18n.DateTimePatterns_en_ER');
    +goog.provide('goog.i18n.DateTimePatterns_en_FJ');
    +goog.provide('goog.i18n.DateTimePatterns_en_FK');
    +goog.provide('goog.i18n.DateTimePatterns_en_FM');
    +goog.provide('goog.i18n.DateTimePatterns_en_GD');
    +goog.provide('goog.i18n.DateTimePatterns_en_GG');
    +goog.provide('goog.i18n.DateTimePatterns_en_GH');
    +goog.provide('goog.i18n.DateTimePatterns_en_GI');
    +goog.provide('goog.i18n.DateTimePatterns_en_GM');
    +goog.provide('goog.i18n.DateTimePatterns_en_GU');
    +goog.provide('goog.i18n.DateTimePatterns_en_GY');
    +goog.provide('goog.i18n.DateTimePatterns_en_HK');
    +goog.provide('goog.i18n.DateTimePatterns_en_IM');
    +goog.provide('goog.i18n.DateTimePatterns_en_IO');
    +goog.provide('goog.i18n.DateTimePatterns_en_JE');
    +goog.provide('goog.i18n.DateTimePatterns_en_JM');
    +goog.provide('goog.i18n.DateTimePatterns_en_KE');
    +goog.provide('goog.i18n.DateTimePatterns_en_KI');
    +goog.provide('goog.i18n.DateTimePatterns_en_KN');
    +goog.provide('goog.i18n.DateTimePatterns_en_KY');
    +goog.provide('goog.i18n.DateTimePatterns_en_LC');
    +goog.provide('goog.i18n.DateTimePatterns_en_LR');
    +goog.provide('goog.i18n.DateTimePatterns_en_LS');
    +goog.provide('goog.i18n.DateTimePatterns_en_MG');
    +goog.provide('goog.i18n.DateTimePatterns_en_MH');
    +goog.provide('goog.i18n.DateTimePatterns_en_MO');
    +goog.provide('goog.i18n.DateTimePatterns_en_MP');
    +goog.provide('goog.i18n.DateTimePatterns_en_MS');
    +goog.provide('goog.i18n.DateTimePatterns_en_MT');
    +goog.provide('goog.i18n.DateTimePatterns_en_MU');
    +goog.provide('goog.i18n.DateTimePatterns_en_MW');
    +goog.provide('goog.i18n.DateTimePatterns_en_MY');
    +goog.provide('goog.i18n.DateTimePatterns_en_NA');
    +goog.provide('goog.i18n.DateTimePatterns_en_NF');
    +goog.provide('goog.i18n.DateTimePatterns_en_NG');
    +goog.provide('goog.i18n.DateTimePatterns_en_NR');
    +goog.provide('goog.i18n.DateTimePatterns_en_NU');
    +goog.provide('goog.i18n.DateTimePatterns_en_NZ');
    +goog.provide('goog.i18n.DateTimePatterns_en_PG');
    +goog.provide('goog.i18n.DateTimePatterns_en_PH');
    +goog.provide('goog.i18n.DateTimePatterns_en_PK');
    +goog.provide('goog.i18n.DateTimePatterns_en_PN');
    +goog.provide('goog.i18n.DateTimePatterns_en_PR');
    +goog.provide('goog.i18n.DateTimePatterns_en_PW');
    +goog.provide('goog.i18n.DateTimePatterns_en_RW');
    +goog.provide('goog.i18n.DateTimePatterns_en_SB');
    +goog.provide('goog.i18n.DateTimePatterns_en_SC');
    +goog.provide('goog.i18n.DateTimePatterns_en_SD');
    +goog.provide('goog.i18n.DateTimePatterns_en_SH');
    +goog.provide('goog.i18n.DateTimePatterns_en_SL');
    +goog.provide('goog.i18n.DateTimePatterns_en_SS');
    +goog.provide('goog.i18n.DateTimePatterns_en_SX');
    +goog.provide('goog.i18n.DateTimePatterns_en_SZ');
    +goog.provide('goog.i18n.DateTimePatterns_en_TC');
    +goog.provide('goog.i18n.DateTimePatterns_en_TK');
    +goog.provide('goog.i18n.DateTimePatterns_en_TO');
    +goog.provide('goog.i18n.DateTimePatterns_en_TT');
    +goog.provide('goog.i18n.DateTimePatterns_en_TV');
    +goog.provide('goog.i18n.DateTimePatterns_en_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_en_UG');
    +goog.provide('goog.i18n.DateTimePatterns_en_UM');
    +goog.provide('goog.i18n.DateTimePatterns_en_US_POSIX');
    +goog.provide('goog.i18n.DateTimePatterns_en_VC');
    +goog.provide('goog.i18n.DateTimePatterns_en_VG');
    +goog.provide('goog.i18n.DateTimePatterns_en_VI');
    +goog.provide('goog.i18n.DateTimePatterns_en_VU');
    +goog.provide('goog.i18n.DateTimePatterns_en_WS');
    +goog.provide('goog.i18n.DateTimePatterns_en_ZM');
    +goog.provide('goog.i18n.DateTimePatterns_en_ZW');
    +goog.provide('goog.i18n.DateTimePatterns_eo');
    +goog.provide('goog.i18n.DateTimePatterns_es_AR');
    +goog.provide('goog.i18n.DateTimePatterns_es_BO');
    +goog.provide('goog.i18n.DateTimePatterns_es_CL');
    +goog.provide('goog.i18n.DateTimePatterns_es_CO');
    +goog.provide('goog.i18n.DateTimePatterns_es_CR');
    +goog.provide('goog.i18n.DateTimePatterns_es_CU');
    +goog.provide('goog.i18n.DateTimePatterns_es_DO');
    +goog.provide('goog.i18n.DateTimePatterns_es_EA');
    +goog.provide('goog.i18n.DateTimePatterns_es_EC');
    +goog.provide('goog.i18n.DateTimePatterns_es_GQ');
    +goog.provide('goog.i18n.DateTimePatterns_es_GT');
    +goog.provide('goog.i18n.DateTimePatterns_es_HN');
    +goog.provide('goog.i18n.DateTimePatterns_es_IC');
    +goog.provide('goog.i18n.DateTimePatterns_es_MX');
    +goog.provide('goog.i18n.DateTimePatterns_es_NI');
    +goog.provide('goog.i18n.DateTimePatterns_es_PA');
    +goog.provide('goog.i18n.DateTimePatterns_es_PE');
    +goog.provide('goog.i18n.DateTimePatterns_es_PH');
    +goog.provide('goog.i18n.DateTimePatterns_es_PR');
    +goog.provide('goog.i18n.DateTimePatterns_es_PY');
    +goog.provide('goog.i18n.DateTimePatterns_es_SV');
    +goog.provide('goog.i18n.DateTimePatterns_es_US');
    +goog.provide('goog.i18n.DateTimePatterns_es_UY');
    +goog.provide('goog.i18n.DateTimePatterns_es_VE');
    +goog.provide('goog.i18n.DateTimePatterns_et_EE');
    +goog.provide('goog.i18n.DateTimePatterns_eu_ES');
    +goog.provide('goog.i18n.DateTimePatterns_ewo');
    +goog.provide('goog.i18n.DateTimePatterns_ewo_CM');
    +goog.provide('goog.i18n.DateTimePatterns_fa_AF');
    +goog.provide('goog.i18n.DateTimePatterns_fa_IR');
    +goog.provide('goog.i18n.DateTimePatterns_ff');
    +goog.provide('goog.i18n.DateTimePatterns_ff_CM');
    +goog.provide('goog.i18n.DateTimePatterns_ff_GN');
    +goog.provide('goog.i18n.DateTimePatterns_ff_MR');
    +goog.provide('goog.i18n.DateTimePatterns_ff_SN');
    +goog.provide('goog.i18n.DateTimePatterns_fi_FI');
    +goog.provide('goog.i18n.DateTimePatterns_fil_PH');
    +goog.provide('goog.i18n.DateTimePatterns_fo');
    +goog.provide('goog.i18n.DateTimePatterns_fo_FO');
    +goog.provide('goog.i18n.DateTimePatterns_fr_BE');
    +goog.provide('goog.i18n.DateTimePatterns_fr_BF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_BI');
    +goog.provide('goog.i18n.DateTimePatterns_fr_BJ');
    +goog.provide('goog.i18n.DateTimePatterns_fr_BL');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CD');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CG');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CH');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CI');
    +goog.provide('goog.i18n.DateTimePatterns_fr_CM');
    +goog.provide('goog.i18n.DateTimePatterns_fr_DJ');
    +goog.provide('goog.i18n.DateTimePatterns_fr_DZ');
    +goog.provide('goog.i18n.DateTimePatterns_fr_FR');
    +goog.provide('goog.i18n.DateTimePatterns_fr_GA');
    +goog.provide('goog.i18n.DateTimePatterns_fr_GF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_GN');
    +goog.provide('goog.i18n.DateTimePatterns_fr_GP');
    +goog.provide('goog.i18n.DateTimePatterns_fr_GQ');
    +goog.provide('goog.i18n.DateTimePatterns_fr_HT');
    +goog.provide('goog.i18n.DateTimePatterns_fr_KM');
    +goog.provide('goog.i18n.DateTimePatterns_fr_LU');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MA');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MC');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MG');
    +goog.provide('goog.i18n.DateTimePatterns_fr_ML');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MQ');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MR');
    +goog.provide('goog.i18n.DateTimePatterns_fr_MU');
    +goog.provide('goog.i18n.DateTimePatterns_fr_NC');
    +goog.provide('goog.i18n.DateTimePatterns_fr_NE');
    +goog.provide('goog.i18n.DateTimePatterns_fr_PF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_PM');
    +goog.provide('goog.i18n.DateTimePatterns_fr_RE');
    +goog.provide('goog.i18n.DateTimePatterns_fr_RW');
    +goog.provide('goog.i18n.DateTimePatterns_fr_SC');
    +goog.provide('goog.i18n.DateTimePatterns_fr_SN');
    +goog.provide('goog.i18n.DateTimePatterns_fr_SY');
    +goog.provide('goog.i18n.DateTimePatterns_fr_TD');
    +goog.provide('goog.i18n.DateTimePatterns_fr_TG');
    +goog.provide('goog.i18n.DateTimePatterns_fr_TN');
    +goog.provide('goog.i18n.DateTimePatterns_fr_VU');
    +goog.provide('goog.i18n.DateTimePatterns_fr_WF');
    +goog.provide('goog.i18n.DateTimePatterns_fr_YT');
    +goog.provide('goog.i18n.DateTimePatterns_fur');
    +goog.provide('goog.i18n.DateTimePatterns_fur_IT');
    +goog.provide('goog.i18n.DateTimePatterns_fy');
    +goog.provide('goog.i18n.DateTimePatterns_fy_NL');
    +goog.provide('goog.i18n.DateTimePatterns_ga_IE');
    +goog.provide('goog.i18n.DateTimePatterns_gd');
    +goog.provide('goog.i18n.DateTimePatterns_gd_GB');
    +goog.provide('goog.i18n.DateTimePatterns_gl_ES');
    +goog.provide('goog.i18n.DateTimePatterns_gsw_CH');
    +goog.provide('goog.i18n.DateTimePatterns_gsw_FR');
    +goog.provide('goog.i18n.DateTimePatterns_gsw_LI');
    +goog.provide('goog.i18n.DateTimePatterns_gu_IN');
    +goog.provide('goog.i18n.DateTimePatterns_guz');
    +goog.provide('goog.i18n.DateTimePatterns_guz_KE');
    +goog.provide('goog.i18n.DateTimePatterns_gv');
    +goog.provide('goog.i18n.DateTimePatterns_gv_IM');
    +goog.provide('goog.i18n.DateTimePatterns_ha');
    +goog.provide('goog.i18n.DateTimePatterns_ha_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_ha_Latn_GH');
    +goog.provide('goog.i18n.DateTimePatterns_ha_Latn_NE');
    +goog.provide('goog.i18n.DateTimePatterns_ha_Latn_NG');
    +goog.provide('goog.i18n.DateTimePatterns_haw_US');
    +goog.provide('goog.i18n.DateTimePatterns_he_IL');
    +goog.provide('goog.i18n.DateTimePatterns_hi_IN');
    +goog.provide('goog.i18n.DateTimePatterns_hr_BA');
    +goog.provide('goog.i18n.DateTimePatterns_hr_HR');
    +goog.provide('goog.i18n.DateTimePatterns_hsb');
    +goog.provide('goog.i18n.DateTimePatterns_hsb_DE');
    +goog.provide('goog.i18n.DateTimePatterns_hu_HU');
    +goog.provide('goog.i18n.DateTimePatterns_hy_AM');
    +goog.provide('goog.i18n.DateTimePatterns_id_ID');
    +goog.provide('goog.i18n.DateTimePatterns_ig');
    +goog.provide('goog.i18n.DateTimePatterns_ig_NG');
    +goog.provide('goog.i18n.DateTimePatterns_ii');
    +goog.provide('goog.i18n.DateTimePatterns_ii_CN');
    +goog.provide('goog.i18n.DateTimePatterns_is_IS');
    +goog.provide('goog.i18n.DateTimePatterns_it_CH');
    +goog.provide('goog.i18n.DateTimePatterns_it_IT');
    +goog.provide('goog.i18n.DateTimePatterns_it_SM');
    +goog.provide('goog.i18n.DateTimePatterns_ja_JP');
    +goog.provide('goog.i18n.DateTimePatterns_jgo');
    +goog.provide('goog.i18n.DateTimePatterns_jgo_CM');
    +goog.provide('goog.i18n.DateTimePatterns_jmc');
    +goog.provide('goog.i18n.DateTimePatterns_jmc_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_ka_GE');
    +goog.provide('goog.i18n.DateTimePatterns_kab');
    +goog.provide('goog.i18n.DateTimePatterns_kab_DZ');
    +goog.provide('goog.i18n.DateTimePatterns_kam');
    +goog.provide('goog.i18n.DateTimePatterns_kam_KE');
    +goog.provide('goog.i18n.DateTimePatterns_kde');
    +goog.provide('goog.i18n.DateTimePatterns_kde_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_kea');
    +goog.provide('goog.i18n.DateTimePatterns_kea_CV');
    +goog.provide('goog.i18n.DateTimePatterns_khq');
    +goog.provide('goog.i18n.DateTimePatterns_khq_ML');
    +goog.provide('goog.i18n.DateTimePatterns_ki');
    +goog.provide('goog.i18n.DateTimePatterns_ki_KE');
    +goog.provide('goog.i18n.DateTimePatterns_kk_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_kk_Cyrl_KZ');
    +goog.provide('goog.i18n.DateTimePatterns_kkj');
    +goog.provide('goog.i18n.DateTimePatterns_kkj_CM');
    +goog.provide('goog.i18n.DateTimePatterns_kl');
    +goog.provide('goog.i18n.DateTimePatterns_kl_GL');
    +goog.provide('goog.i18n.DateTimePatterns_kln');
    +goog.provide('goog.i18n.DateTimePatterns_kln_KE');
    +goog.provide('goog.i18n.DateTimePatterns_km_KH');
    +goog.provide('goog.i18n.DateTimePatterns_kn_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ko_KP');
    +goog.provide('goog.i18n.DateTimePatterns_ko_KR');
    +goog.provide('goog.i18n.DateTimePatterns_kok');
    +goog.provide('goog.i18n.DateTimePatterns_kok_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ks');
    +goog.provide('goog.i18n.DateTimePatterns_ks_Arab');
    +goog.provide('goog.i18n.DateTimePatterns_ks_Arab_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ksb');
    +goog.provide('goog.i18n.DateTimePatterns_ksb_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_ksf');
    +goog.provide('goog.i18n.DateTimePatterns_ksf_CM');
    +goog.provide('goog.i18n.DateTimePatterns_ksh');
    +goog.provide('goog.i18n.DateTimePatterns_ksh_DE');
    +goog.provide('goog.i18n.DateTimePatterns_kw');
    +goog.provide('goog.i18n.DateTimePatterns_kw_GB');
    +goog.provide('goog.i18n.DateTimePatterns_ky_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_ky_Cyrl_KG');
    +goog.provide('goog.i18n.DateTimePatterns_lag');
    +goog.provide('goog.i18n.DateTimePatterns_lag_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_lb');
    +goog.provide('goog.i18n.DateTimePatterns_lb_LU');
    +goog.provide('goog.i18n.DateTimePatterns_lg');
    +goog.provide('goog.i18n.DateTimePatterns_lg_UG');
    +goog.provide('goog.i18n.DateTimePatterns_lkt');
    +goog.provide('goog.i18n.DateTimePatterns_lkt_US');
    +goog.provide('goog.i18n.DateTimePatterns_ln_AO');
    +goog.provide('goog.i18n.DateTimePatterns_ln_CD');
    +goog.provide('goog.i18n.DateTimePatterns_ln_CF');
    +goog.provide('goog.i18n.DateTimePatterns_ln_CG');
    +goog.provide('goog.i18n.DateTimePatterns_lo_LA');
    +goog.provide('goog.i18n.DateTimePatterns_lt_LT');
    +goog.provide('goog.i18n.DateTimePatterns_lu');
    +goog.provide('goog.i18n.DateTimePatterns_lu_CD');
    +goog.provide('goog.i18n.DateTimePatterns_luo');
    +goog.provide('goog.i18n.DateTimePatterns_luo_KE');
    +goog.provide('goog.i18n.DateTimePatterns_luy');
    +goog.provide('goog.i18n.DateTimePatterns_luy_KE');
    +goog.provide('goog.i18n.DateTimePatterns_lv_LV');
    +goog.provide('goog.i18n.DateTimePatterns_mas');
    +goog.provide('goog.i18n.DateTimePatterns_mas_KE');
    +goog.provide('goog.i18n.DateTimePatterns_mas_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_mer');
    +goog.provide('goog.i18n.DateTimePatterns_mer_KE');
    +goog.provide('goog.i18n.DateTimePatterns_mfe');
    +goog.provide('goog.i18n.DateTimePatterns_mfe_MU');
    +goog.provide('goog.i18n.DateTimePatterns_mg');
    +goog.provide('goog.i18n.DateTimePatterns_mg_MG');
    +goog.provide('goog.i18n.DateTimePatterns_mgh');
    +goog.provide('goog.i18n.DateTimePatterns_mgh_MZ');
    +goog.provide('goog.i18n.DateTimePatterns_mgo');
    +goog.provide('goog.i18n.DateTimePatterns_mgo_CM');
    +goog.provide('goog.i18n.DateTimePatterns_mk_MK');
    +goog.provide('goog.i18n.DateTimePatterns_ml_IN');
    +goog.provide('goog.i18n.DateTimePatterns_mn_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_mn_Cyrl_MN');
    +goog.provide('goog.i18n.DateTimePatterns_mr_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ms_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_ms_Latn_BN');
    +goog.provide('goog.i18n.DateTimePatterns_ms_Latn_MY');
    +goog.provide('goog.i18n.DateTimePatterns_ms_Latn_SG');
    +goog.provide('goog.i18n.DateTimePatterns_mt_MT');
    +goog.provide('goog.i18n.DateTimePatterns_mua');
    +goog.provide('goog.i18n.DateTimePatterns_mua_CM');
    +goog.provide('goog.i18n.DateTimePatterns_my_MM');
    +goog.provide('goog.i18n.DateTimePatterns_naq');
    +goog.provide('goog.i18n.DateTimePatterns_naq_NA');
    +goog.provide('goog.i18n.DateTimePatterns_nb_NO');
    +goog.provide('goog.i18n.DateTimePatterns_nb_SJ');
    +goog.provide('goog.i18n.DateTimePatterns_nd');
    +goog.provide('goog.i18n.DateTimePatterns_nd_ZW');
    +goog.provide('goog.i18n.DateTimePatterns_ne_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ne_NP');
    +goog.provide('goog.i18n.DateTimePatterns_nl_AW');
    +goog.provide('goog.i18n.DateTimePatterns_nl_BE');
    +goog.provide('goog.i18n.DateTimePatterns_nl_BQ');
    +goog.provide('goog.i18n.DateTimePatterns_nl_CW');
    +goog.provide('goog.i18n.DateTimePatterns_nl_NL');
    +goog.provide('goog.i18n.DateTimePatterns_nl_SR');
    +goog.provide('goog.i18n.DateTimePatterns_nl_SX');
    +goog.provide('goog.i18n.DateTimePatterns_nmg');
    +goog.provide('goog.i18n.DateTimePatterns_nmg_CM');
    +goog.provide('goog.i18n.DateTimePatterns_nn');
    +goog.provide('goog.i18n.DateTimePatterns_nn_NO');
    +goog.provide('goog.i18n.DateTimePatterns_nnh');
    +goog.provide('goog.i18n.DateTimePatterns_nnh_CM');
    +goog.provide('goog.i18n.DateTimePatterns_nus');
    +goog.provide('goog.i18n.DateTimePatterns_nus_SD');
    +goog.provide('goog.i18n.DateTimePatterns_nyn');
    +goog.provide('goog.i18n.DateTimePatterns_nyn_UG');
    +goog.provide('goog.i18n.DateTimePatterns_om');
    +goog.provide('goog.i18n.DateTimePatterns_om_ET');
    +goog.provide('goog.i18n.DateTimePatterns_om_KE');
    +goog.provide('goog.i18n.DateTimePatterns_or_IN');
    +goog.provide('goog.i18n.DateTimePatterns_os');
    +goog.provide('goog.i18n.DateTimePatterns_os_GE');
    +goog.provide('goog.i18n.DateTimePatterns_os_RU');
    +goog.provide('goog.i18n.DateTimePatterns_pa_Arab');
    +goog.provide('goog.i18n.DateTimePatterns_pa_Arab_PK');
    +goog.provide('goog.i18n.DateTimePatterns_pa_Guru');
    +goog.provide('goog.i18n.DateTimePatterns_pa_Guru_IN');
    +goog.provide('goog.i18n.DateTimePatterns_pl_PL');
    +goog.provide('goog.i18n.DateTimePatterns_ps');
    +goog.provide('goog.i18n.DateTimePatterns_ps_AF');
    +goog.provide('goog.i18n.DateTimePatterns_pt_AO');
    +goog.provide('goog.i18n.DateTimePatterns_pt_CV');
    +goog.provide('goog.i18n.DateTimePatterns_pt_GW');
    +goog.provide('goog.i18n.DateTimePatterns_pt_MO');
    +goog.provide('goog.i18n.DateTimePatterns_pt_MZ');
    +goog.provide('goog.i18n.DateTimePatterns_pt_ST');
    +goog.provide('goog.i18n.DateTimePatterns_pt_TL');
    +goog.provide('goog.i18n.DateTimePatterns_qu');
    +goog.provide('goog.i18n.DateTimePatterns_qu_BO');
    +goog.provide('goog.i18n.DateTimePatterns_qu_EC');
    +goog.provide('goog.i18n.DateTimePatterns_qu_PE');
    +goog.provide('goog.i18n.DateTimePatterns_rm');
    +goog.provide('goog.i18n.DateTimePatterns_rm_CH');
    +goog.provide('goog.i18n.DateTimePatterns_rn');
    +goog.provide('goog.i18n.DateTimePatterns_rn_BI');
    +goog.provide('goog.i18n.DateTimePatterns_ro_MD');
    +goog.provide('goog.i18n.DateTimePatterns_ro_RO');
    +goog.provide('goog.i18n.DateTimePatterns_rof');
    +goog.provide('goog.i18n.DateTimePatterns_rof_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_ru_BY');
    +goog.provide('goog.i18n.DateTimePatterns_ru_KG');
    +goog.provide('goog.i18n.DateTimePatterns_ru_KZ');
    +goog.provide('goog.i18n.DateTimePatterns_ru_MD');
    +goog.provide('goog.i18n.DateTimePatterns_ru_RU');
    +goog.provide('goog.i18n.DateTimePatterns_ru_UA');
    +goog.provide('goog.i18n.DateTimePatterns_rw');
    +goog.provide('goog.i18n.DateTimePatterns_rw_RW');
    +goog.provide('goog.i18n.DateTimePatterns_rwk');
    +goog.provide('goog.i18n.DateTimePatterns_rwk_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_sah');
    +goog.provide('goog.i18n.DateTimePatterns_sah_RU');
    +goog.provide('goog.i18n.DateTimePatterns_saq');
    +goog.provide('goog.i18n.DateTimePatterns_saq_KE');
    +goog.provide('goog.i18n.DateTimePatterns_sbp');
    +goog.provide('goog.i18n.DateTimePatterns_sbp_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_se');
    +goog.provide('goog.i18n.DateTimePatterns_se_FI');
    +goog.provide('goog.i18n.DateTimePatterns_se_NO');
    +goog.provide('goog.i18n.DateTimePatterns_se_SE');
    +goog.provide('goog.i18n.DateTimePatterns_seh');
    +goog.provide('goog.i18n.DateTimePatterns_seh_MZ');
    +goog.provide('goog.i18n.DateTimePatterns_ses');
    +goog.provide('goog.i18n.DateTimePatterns_ses_ML');
    +goog.provide('goog.i18n.DateTimePatterns_sg');
    +goog.provide('goog.i18n.DateTimePatterns_sg_CF');
    +goog.provide('goog.i18n.DateTimePatterns_shi');
    +goog.provide('goog.i18n.DateTimePatterns_shi_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_shi_Latn_MA');
    +goog.provide('goog.i18n.DateTimePatterns_shi_Tfng');
    +goog.provide('goog.i18n.DateTimePatterns_shi_Tfng_MA');
    +goog.provide('goog.i18n.DateTimePatterns_si_LK');
    +goog.provide('goog.i18n.DateTimePatterns_sk_SK');
    +goog.provide('goog.i18n.DateTimePatterns_sl_SI');
    +goog.provide('goog.i18n.DateTimePatterns_smn');
    +goog.provide('goog.i18n.DateTimePatterns_smn_FI');
    +goog.provide('goog.i18n.DateTimePatterns_sn');
    +goog.provide('goog.i18n.DateTimePatterns_sn_ZW');
    +goog.provide('goog.i18n.DateTimePatterns_so');
    +goog.provide('goog.i18n.DateTimePatterns_so_DJ');
    +goog.provide('goog.i18n.DateTimePatterns_so_ET');
    +goog.provide('goog.i18n.DateTimePatterns_so_KE');
    +goog.provide('goog.i18n.DateTimePatterns_so_SO');
    +goog.provide('goog.i18n.DateTimePatterns_sq_AL');
    +goog.provide('goog.i18n.DateTimePatterns_sq_MK');
    +goog.provide('goog.i18n.DateTimePatterns_sq_XK');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Cyrl_BA');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Cyrl_ME');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Cyrl_RS');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Cyrl_XK');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Latn_BA');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Latn_ME');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Latn_RS');
    +goog.provide('goog.i18n.DateTimePatterns_sr_Latn_XK');
    +goog.provide('goog.i18n.DateTimePatterns_sv_AX');
    +goog.provide('goog.i18n.DateTimePatterns_sv_FI');
    +goog.provide('goog.i18n.DateTimePatterns_sv_SE');
    +goog.provide('goog.i18n.DateTimePatterns_sw_KE');
    +goog.provide('goog.i18n.DateTimePatterns_sw_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_sw_UG');
    +goog.provide('goog.i18n.DateTimePatterns_swc');
    +goog.provide('goog.i18n.DateTimePatterns_swc_CD');
    +goog.provide('goog.i18n.DateTimePatterns_ta_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ta_LK');
    +goog.provide('goog.i18n.DateTimePatterns_ta_MY');
    +goog.provide('goog.i18n.DateTimePatterns_ta_SG');
    +goog.provide('goog.i18n.DateTimePatterns_te_IN');
    +goog.provide('goog.i18n.DateTimePatterns_teo');
    +goog.provide('goog.i18n.DateTimePatterns_teo_KE');
    +goog.provide('goog.i18n.DateTimePatterns_teo_UG');
    +goog.provide('goog.i18n.DateTimePatterns_th_TH');
    +goog.provide('goog.i18n.DateTimePatterns_ti');
    +goog.provide('goog.i18n.DateTimePatterns_ti_ER');
    +goog.provide('goog.i18n.DateTimePatterns_ti_ET');
    +goog.provide('goog.i18n.DateTimePatterns_to');
    +goog.provide('goog.i18n.DateTimePatterns_to_TO');
    +goog.provide('goog.i18n.DateTimePatterns_tr_CY');
    +goog.provide('goog.i18n.DateTimePatterns_tr_TR');
    +goog.provide('goog.i18n.DateTimePatterns_twq');
    +goog.provide('goog.i18n.DateTimePatterns_twq_NE');
    +goog.provide('goog.i18n.DateTimePatterns_tzm');
    +goog.provide('goog.i18n.DateTimePatterns_tzm_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_tzm_Latn_MA');
    +goog.provide('goog.i18n.DateTimePatterns_ug');
    +goog.provide('goog.i18n.DateTimePatterns_ug_Arab');
    +goog.provide('goog.i18n.DateTimePatterns_ug_Arab_CN');
    +goog.provide('goog.i18n.DateTimePatterns_uk_UA');
    +goog.provide('goog.i18n.DateTimePatterns_ur_IN');
    +goog.provide('goog.i18n.DateTimePatterns_ur_PK');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Arab');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Arab_AF');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Cyrl');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Cyrl_UZ');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_uz_Latn_UZ');
    +goog.provide('goog.i18n.DateTimePatterns_vai');
    +goog.provide('goog.i18n.DateTimePatterns_vai_Latn');
    +goog.provide('goog.i18n.DateTimePatterns_vai_Latn_LR');
    +goog.provide('goog.i18n.DateTimePatterns_vai_Vaii');
    +goog.provide('goog.i18n.DateTimePatterns_vai_Vaii_LR');
    +goog.provide('goog.i18n.DateTimePatterns_vi_VN');
    +goog.provide('goog.i18n.DateTimePatterns_vun');
    +goog.provide('goog.i18n.DateTimePatterns_vun_TZ');
    +goog.provide('goog.i18n.DateTimePatterns_wae');
    +goog.provide('goog.i18n.DateTimePatterns_wae_CH');
    +goog.provide('goog.i18n.DateTimePatterns_xog');
    +goog.provide('goog.i18n.DateTimePatterns_xog_UG');
    +goog.provide('goog.i18n.DateTimePatterns_yav');
    +goog.provide('goog.i18n.DateTimePatterns_yav_CM');
    +goog.provide('goog.i18n.DateTimePatterns_yi');
    +goog.provide('goog.i18n.DateTimePatterns_yi_001');
    +goog.provide('goog.i18n.DateTimePatterns_yo');
    +goog.provide('goog.i18n.DateTimePatterns_yo_BJ');
    +goog.provide('goog.i18n.DateTimePatterns_yo_NG');
    +goog.provide('goog.i18n.DateTimePatterns_zgh');
    +goog.provide('goog.i18n.DateTimePatterns_zgh_MA');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hans');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hans_CN');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hans_HK');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hans_MO');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hans_SG');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hant');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hant_HK');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hant_MO');
    +goog.provide('goog.i18n.DateTimePatterns_zh_Hant_TW');
    +goog.provide('goog.i18n.DateTimePatterns_zu_ZA');
    +
    +goog.require('goog.i18n.DateTimePatterns');
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale af_NA.
    + */
    +goog.i18n.DateTimePatterns_af_NA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale af_ZA.
    + */
    +goog.i18n.DateTimePatterns_af_ZA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale agq.
    + */
    +goog.i18n.DateTimePatterns_agq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale agq_CM.
    + */
    +goog.i18n.DateTimePatterns_agq_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ak.
    + */
    +goog.i18n.DateTimePatterns_ak = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ak_GH.
    + */
    +goog.i18n.DateTimePatterns_ak_GH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale am_ET.
    + */
    +goog.i18n.DateTimePatterns_am_ET = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE፣ MMM d y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_001.
    + */
    +goog.i18n.DateTimePatterns_ar_001 = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_AE.
    + */
    +goog.i18n.DateTimePatterns_ar_AE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_BH.
    + */
    +goog.i18n.DateTimePatterns_ar_BH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_DJ.
    + */
    +goog.i18n.DateTimePatterns_ar_DJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_DZ.
    + */
    +goog.i18n.DateTimePatterns_ar_DZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_EG.
    + */
    +goog.i18n.DateTimePatterns_ar_EG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_EH.
    + */
    +goog.i18n.DateTimePatterns_ar_EH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_ER.
    + */
    +goog.i18n.DateTimePatterns_ar_ER = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_IL.
    + */
    +goog.i18n.DateTimePatterns_ar_IL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_IQ.
    + */
    +goog.i18n.DateTimePatterns_ar_IQ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_JO.
    + */
    +goog.i18n.DateTimePatterns_ar_JO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_KM.
    + */
    +goog.i18n.DateTimePatterns_ar_KM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_KW.
    + */
    +goog.i18n.DateTimePatterns_ar_KW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_LB.
    + */
    +goog.i18n.DateTimePatterns_ar_LB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_LY.
    + */
    +goog.i18n.DateTimePatterns_ar_LY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_MA.
    + */
    +goog.i18n.DateTimePatterns_ar_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_MR.
    + */
    +goog.i18n.DateTimePatterns_ar_MR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_OM.
    + */
    +goog.i18n.DateTimePatterns_ar_OM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_PS.
    + */
    +goog.i18n.DateTimePatterns_ar_PS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_QA.
    + */
    +goog.i18n.DateTimePatterns_ar_QA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_SA.
    + */
    +goog.i18n.DateTimePatterns_ar_SA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_SD.
    + */
    +goog.i18n.DateTimePatterns_ar_SD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_SO.
    + */
    +goog.i18n.DateTimePatterns_ar_SO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_SS.
    + */
    +goog.i18n.DateTimePatterns_ar_SS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_SY.
    + */
    +goog.i18n.DateTimePatterns_ar_SY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_TD.
    + */
    +goog.i18n.DateTimePatterns_ar_TD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_TN.
    + */
    +goog.i18n.DateTimePatterns_ar_TN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ar_YE.
    + */
    +goog.i18n.DateTimePatterns_ar_YE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/‏M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale as.
    + */
    +goog.i18n.DateTimePatterns_as = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale as_IN.
    + */
    +goog.i18n.DateTimePatterns_as_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale asa.
    + */
    +goog.i18n.DateTimePatterns_asa = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale asa_TZ.
    + */
    +goog.i18n.DateTimePatterns_asa_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale az_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_az_Cyrl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM, y',
    +  YEAR_MONTH_FULL: 'MMMM, y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d, MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale az_Cyrl_AZ.
    + */
    +goog.i18n.DateTimePatterns_az_Cyrl_AZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM, y',
    +  YEAR_MONTH_FULL: 'MMMM, y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d, MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale az_Latn.
    + */
    +goog.i18n.DateTimePatterns_az_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale az_Latn_AZ.
    + */
    +goog.i18n.DateTimePatterns_az_Latn_AZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bas.
    + */
    +goog.i18n.DateTimePatterns_bas = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bas_CM.
    + */
    +goog.i18n.DateTimePatterns_bas_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale be.
    + */
    +goog.i18n.DateTimePatterns_be = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale be_BY.
    + */
    +goog.i18n.DateTimePatterns_be_BY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bem.
    + */
    +goog.i18n.DateTimePatterns_bem = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bem_ZM.
    + */
    +goog.i18n.DateTimePatterns_bem_ZM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bez.
    + */
    +goog.i18n.DateTimePatterns_bez = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bez_TZ.
    + */
    +goog.i18n.DateTimePatterns_bez_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bg_BG.
    + */
    +goog.i18n.DateTimePatterns_bg_BG = {
    +  YEAR_FULL: 'y \'г\'.',
    +  YEAR_FULL_WITH_ERA: 'y \'г\'. G',
    +  YEAR_MONTH_ABBR: 'MM.y \'г\'.',
    +  YEAR_MONTH_FULL: 'MMMM y \'г\'.',
    +  MONTH_DAY_ABBR: 'd.MM',
    +  MONTH_DAY_FULL: 'd MMMM',
    +  MONTH_DAY_SHORT: 'd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd.MM.y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d.MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d.MM.y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bm.
    + */
    +goog.i18n.DateTimePatterns_bm = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bm_Latn.
    + */
    +goog.i18n.DateTimePatterns_bm_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bm_Latn_ML.
    + */
    +goog.i18n.DateTimePatterns_bm_Latn_ML = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bn_BD.
    + */
    +goog.i18n.DateTimePatterns_bn_BD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bn_IN.
    + */
    +goog.i18n.DateTimePatterns_bn_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bo.
    + */
    +goog.i18n.DateTimePatterns_bo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y ལོ་འི་MMMཙེས་d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bo_CN.
    + */
    +goog.i18n.DateTimePatterns_bo_CN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y ལོ་འི་MMMཙེས་d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bo_IN.
    + */
    +goog.i18n.DateTimePatterns_bo_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y ལོ་འི་MMMཙེས་d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale br_FR.
    + */
    +goog.i18n.DateTimePatterns_br_FR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale brx.
    + */
    +goog.i18n.DateTimePatterns_brx = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale brx_IN.
    + */
    +goog.i18n.DateTimePatterns_brx_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bs.
    + */
    +goog.i18n.DateTimePatterns_bs = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'dd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bs_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_bs_Cyrl = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'dd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'dd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bs_Cyrl_BA.
    + */
    +goog.i18n.DateTimePatterns_bs_Cyrl_BA = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'dd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'dd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bs_Latn.
    + */
    +goog.i18n.DateTimePatterns_bs_Latn = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'dd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale bs_Latn_BA.
    + */
    +goog.i18n.DateTimePatterns_bs_Latn_BA = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'dd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ca_AD.
    + */
    +goog.i18n.DateTimePatterns_ca_AD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL \'de\' y',
    +  YEAR_MONTH_FULL: 'LLLL \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ca_ES.
    + */
    +goog.i18n.DateTimePatterns_ca_ES = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL \'de\' y',
    +  YEAR_MONTH_FULL: 'LLLL \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ca_FR.
    + */
    +goog.i18n.DateTimePatterns_ca_FR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL \'de\' y',
    +  YEAR_MONTH_FULL: 'LLLL \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ca_IT.
    + */
    +goog.i18n.DateTimePatterns_ca_IT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL \'de\' y',
    +  YEAR_MONTH_FULL: 'LLLL \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cgg.
    + */
    +goog.i18n.DateTimePatterns_cgg = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cgg_UG.
    + */
    +goog.i18n.DateTimePatterns_cgg_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale chr_US.
    + */
    +goog.i18n.DateTimePatterns_chr_US = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cs_CZ.
    + */
    +goog.i18n.DateTimePatterns_cs_CZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. M.',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. M. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. M.',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. M. y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale cy_GB.
    + */
    +goog.i18n.DateTimePatterns_cy_GB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale da_DK.
    + */
    +goog.i18n.DateTimePatterns_da_DK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale da_GL.
    + */
    +goog.i18n.DateTimePatterns_da_GL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dav.
    + */
    +goog.i18n.DateTimePatterns_dav = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dav_KE.
    + */
    +goog.i18n.DateTimePatterns_dav_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_BE.
    + */
    +goog.i18n.DateTimePatterns_de_BE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_DE.
    + */
    +goog.i18n.DateTimePatterns_de_DE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_LI.
    + */
    +goog.i18n.DateTimePatterns_de_LI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale de_LU.
    + */
    +goog.i18n.DateTimePatterns_de_LU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dje.
    + */
    +goog.i18n.DateTimePatterns_dje = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dje_NE.
    + */
    +goog.i18n.DateTimePatterns_dje_NE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dsb.
    + */
    +goog.i18n.DateTimePatterns_dsb = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dsb_DE.
    + */
    +goog.i18n.DateTimePatterns_dsb_DE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dua.
    + */
    +goog.i18n.DateTimePatterns_dua = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dua_CM.
    + */
    +goog.i18n.DateTimePatterns_dua_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dyo.
    + */
    +goog.i18n.DateTimePatterns_dyo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dyo_SN.
    + */
    +goog.i18n.DateTimePatterns_dyo_SN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dz.
    + */
    +goog.i18n.DateTimePatterns_dz = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y སྤྱི་ཟླ་MMM',
    +  YEAR_MONTH_FULL: 'y སྤྱི་ཟླ་MMMM',
    +  MONTH_DAY_ABBR: 'སྤྱི་LLL ཚེ་d',
    +  MONTH_DAY_FULL: 'སྤྱི་LLLL ཚེ་dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'སྤྱི་LLLL ཚེ་d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, སྤྱི་LLL ཚེ་d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'གཟའ་EEE, ལོy ཟླ་MMM ཚེ་d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale dz_BT.
    + */
    +goog.i18n.DateTimePatterns_dz_BT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y སྤྱི་ཟླ་MMM',
    +  YEAR_MONTH_FULL: 'y སྤྱི་ཟླ་MMMM',
    +  MONTH_DAY_ABBR: 'སྤྱི་LLL ཚེ་d',
    +  MONTH_DAY_FULL: 'སྤྱི་LLLL ཚེ་dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'སྤྱི་LLLL ཚེ་d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, སྤྱི་LLL ཚེ་d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'གཟའ་EEE, ལོy ཟླ་MMM ཚེ་d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ebu.
    + */
    +goog.i18n.DateTimePatterns_ebu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ebu_KE.
    + */
    +goog.i18n.DateTimePatterns_ebu_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ee.
    + */
    +goog.i18n.DateTimePatterns_ee = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d \'lia\'',
    +  MONTH_DAY_FULL: 'MMMM dd \'lia\'',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d \'lia\'',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d \'lia\', y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d \'lia\'',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ee_GH.
    + */
    +goog.i18n.DateTimePatterns_ee_GH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d \'lia\'',
    +  MONTH_DAY_FULL: 'MMMM dd \'lia\'',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d \'lia\'',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d \'lia\', y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d \'lia\'',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ee_TG.
    + */
    +goog.i18n.DateTimePatterns_ee_TG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d \'lia\'',
    +  MONTH_DAY_FULL: 'MMMM dd \'lia\'',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d \'lia\'',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d \'lia\', y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d \'lia\'',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale el_CY.
    + */
    +goog.i18n.DateTimePatterns_el_CY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale el_GR.
    + */
    +goog.i18n.DateTimePatterns_el_GR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_001.
    + */
    +goog.i18n.DateTimePatterns_en_001 = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_150.
    + */
    +goog.i18n.DateTimePatterns_en_150 = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_AG.
    + */
    +goog.i18n.DateTimePatterns_en_AG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_AI.
    + */
    +goog.i18n.DateTimePatterns_en_AI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_AS.
    + */
    +goog.i18n.DateTimePatterns_en_AS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BB.
    + */
    +goog.i18n.DateTimePatterns_en_BB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BE.
    + */
    +goog.i18n.DateTimePatterns_en_BE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BM.
    + */
    +goog.i18n.DateTimePatterns_en_BM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BS.
    + */
    +goog.i18n.DateTimePatterns_en_BS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BW.
    + */
    +goog.i18n.DateTimePatterns_en_BW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'dd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_BZ.
    + */
    +goog.i18n.DateTimePatterns_en_BZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'dd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_CA.
    + */
    +goog.i18n.DateTimePatterns_en_CA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_CC.
    + */
    +goog.i18n.DateTimePatterns_en_CC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_CK.
    + */
    +goog.i18n.DateTimePatterns_en_CK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_CM.
    + */
    +goog.i18n.DateTimePatterns_en_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_CX.
    + */
    +goog.i18n.DateTimePatterns_en_CX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_DG.
    + */
    +goog.i18n.DateTimePatterns_en_DG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_DM.
    + */
    +goog.i18n.DateTimePatterns_en_DM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_ER.
    + */
    +goog.i18n.DateTimePatterns_en_ER = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_FJ.
    + */
    +goog.i18n.DateTimePatterns_en_FJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_FK.
    + */
    +goog.i18n.DateTimePatterns_en_FK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_FM.
    + */
    +goog.i18n.DateTimePatterns_en_FM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GD.
    + */
    +goog.i18n.DateTimePatterns_en_GD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GG.
    + */
    +goog.i18n.DateTimePatterns_en_GG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GH.
    + */
    +goog.i18n.DateTimePatterns_en_GH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GI.
    + */
    +goog.i18n.DateTimePatterns_en_GI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GM.
    + */
    +goog.i18n.DateTimePatterns_en_GM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GU.
    + */
    +goog.i18n.DateTimePatterns_en_GU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_GY.
    + */
    +goog.i18n.DateTimePatterns_en_GY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_HK.
    + */
    +goog.i18n.DateTimePatterns_en_HK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_IM.
    + */
    +goog.i18n.DateTimePatterns_en_IM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_IO.
    + */
    +goog.i18n.DateTimePatterns_en_IO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_JE.
    + */
    +goog.i18n.DateTimePatterns_en_JE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_JM.
    + */
    +goog.i18n.DateTimePatterns_en_JM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_KE.
    + */
    +goog.i18n.DateTimePatterns_en_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_KI.
    + */
    +goog.i18n.DateTimePatterns_en_KI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_KN.
    + */
    +goog.i18n.DateTimePatterns_en_KN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_KY.
    + */
    +goog.i18n.DateTimePatterns_en_KY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_LC.
    + */
    +goog.i18n.DateTimePatterns_en_LC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_LR.
    + */
    +goog.i18n.DateTimePatterns_en_LR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_LS.
    + */
    +goog.i18n.DateTimePatterns_en_LS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MG.
    + */
    +goog.i18n.DateTimePatterns_en_MG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MH.
    + */
    +goog.i18n.DateTimePatterns_en_MH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MO.
    + */
    +goog.i18n.DateTimePatterns_en_MO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MP.
    + */
    +goog.i18n.DateTimePatterns_en_MP = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MS.
    + */
    +goog.i18n.DateTimePatterns_en_MS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MT.
    + */
    +goog.i18n.DateTimePatterns_en_MT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'dd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MU.
    + */
    +goog.i18n.DateTimePatterns_en_MU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MW.
    + */
    +goog.i18n.DateTimePatterns_en_MW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_MY.
    + */
    +goog.i18n.DateTimePatterns_en_MY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NA.
    + */
    +goog.i18n.DateTimePatterns_en_NA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NF.
    + */
    +goog.i18n.DateTimePatterns_en_NF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NG.
    + */
    +goog.i18n.DateTimePatterns_en_NG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NR.
    + */
    +goog.i18n.DateTimePatterns_en_NR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NU.
    + */
    +goog.i18n.DateTimePatterns_en_NU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_NZ.
    + */
    +goog.i18n.DateTimePatterns_en_NZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PG.
    + */
    +goog.i18n.DateTimePatterns_en_PG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PH.
    + */
    +goog.i18n.DateTimePatterns_en_PH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PK.
    + */
    +goog.i18n.DateTimePatterns_en_PK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PN.
    + */
    +goog.i18n.DateTimePatterns_en_PN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PR.
    + */
    +goog.i18n.DateTimePatterns_en_PR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_PW.
    + */
    +goog.i18n.DateTimePatterns_en_PW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_RW.
    + */
    +goog.i18n.DateTimePatterns_en_RW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SB.
    + */
    +goog.i18n.DateTimePatterns_en_SB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SC.
    + */
    +goog.i18n.DateTimePatterns_en_SC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SD.
    + */
    +goog.i18n.DateTimePatterns_en_SD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SH.
    + */
    +goog.i18n.DateTimePatterns_en_SH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SL.
    + */
    +goog.i18n.DateTimePatterns_en_SL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SS.
    + */
    +goog.i18n.DateTimePatterns_en_SS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SX.
    + */
    +goog.i18n.DateTimePatterns_en_SX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_SZ.
    + */
    +goog.i18n.DateTimePatterns_en_SZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TC.
    + */
    +goog.i18n.DateTimePatterns_en_TC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TK.
    + */
    +goog.i18n.DateTimePatterns_en_TK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TO.
    + */
    +goog.i18n.DateTimePatterns_en_TO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TT.
    + */
    +goog.i18n.DateTimePatterns_en_TT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TV.
    + */
    +goog.i18n.DateTimePatterns_en_TV = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_TZ.
    + */
    +goog.i18n.DateTimePatterns_en_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_UG.
    + */
    +goog.i18n.DateTimePatterns_en_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_UM.
    + */
    +goog.i18n.DateTimePatterns_en_UM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_US_POSIX.
    + */
    +goog.i18n.DateTimePatterns_en_US_POSIX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_VC.
    + */
    +goog.i18n.DateTimePatterns_en_VC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_VG.
    + */
    +goog.i18n.DateTimePatterns_en_VG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_VI.
    + */
    +goog.i18n.DateTimePatterns_en_VI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_VU.
    + */
    +goog.i18n.DateTimePatterns_en_VU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_WS.
    + */
    +goog.i18n.DateTimePatterns_en_WS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_ZM.
    + */
    +goog.i18n.DateTimePatterns_en_ZM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale en_ZW.
    + */
    +goog.i18n.DateTimePatterns_en_ZW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'dd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale eo.
    + */
    +goog.i18n.DateTimePatterns_eo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_AR.
    + */
    +goog.i18n.DateTimePatterns_es_AR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_BO.
    + */
    +goog.i18n.DateTimePatterns_es_BO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_CL.
    + */
    +goog.i18n.DateTimePatterns_es_CL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_CO.
    + */
    +goog.i18n.DateTimePatterns_es_CO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_CR.
    + */
    +goog.i18n.DateTimePatterns_es_CR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_CU.
    + */
    +goog.i18n.DateTimePatterns_es_CU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_DO.
    + */
    +goog.i18n.DateTimePatterns_es_DO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_EA.
    + */
    +goog.i18n.DateTimePatterns_es_EA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_EC.
    + */
    +goog.i18n.DateTimePatterns_es_EC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_GQ.
    + */
    +goog.i18n.DateTimePatterns_es_GQ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_GT.
    + */
    +goog.i18n.DateTimePatterns_es_GT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_HN.
    + */
    +goog.i18n.DateTimePatterns_es_HN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_IC.
    + */
    +goog.i18n.DateTimePatterns_es_IC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_MX.
    + */
    +goog.i18n.DateTimePatterns_es_MX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_NI.
    + */
    +goog.i18n.DateTimePatterns_es_NI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_PA.
    + */
    +goog.i18n.DateTimePatterns_es_PA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'MM/dd',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_PE.
    + */
    +goog.i18n.DateTimePatterns_es_PE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_PH.
    + */
    +goog.i18n.DateTimePatterns_es_PH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_PR.
    + */
    +goog.i18n.DateTimePatterns_es_PR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'MM/dd',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_PY.
    + */
    +goog.i18n.DateTimePatterns_es_PY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_SV.
    + */
    +goog.i18n.DateTimePatterns_es_SV = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_US.
    + */
    +goog.i18n.DateTimePatterns_es_US = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_UY.
    + */
    +goog.i18n.DateTimePatterns_es_UY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale es_VE.
    + */
    +goog.i18n.DateTimePatterns_es_VE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd \'de\' MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d \'de\' MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale et_EE.
    + */
    +goog.i18n.DateTimePatterns_et_EE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale eu_ES.
    + */
    +goog.i18n.DateTimePatterns_eu_ES = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y(\'e\')\'ko\' MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ewo.
    + */
    +goog.i18n.DateTimePatterns_ewo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ewo_CM.
    + */
    +goog.i18n.DateTimePatterns_ewo_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fa_AF.
    + */
    +goog.i18n.DateTimePatterns_fa_AF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd LLL',
    +  MONTH_DAY_FULL: 'dd LLLL',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd LLLL',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d LLL',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fa_IR.
    + */
    +goog.i18n.DateTimePatterns_fa_IR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd LLL',
    +  MONTH_DAY_FULL: 'dd LLLL',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd LLLL',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d LLL',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ff.
    + */
    +goog.i18n.DateTimePatterns_ff = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ff_CM.
    + */
    +goog.i18n.DateTimePatterns_ff_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ff_GN.
    + */
    +goog.i18n.DateTimePatterns_ff_GN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ff_MR.
    + */
    +goog.i18n.DateTimePatterns_ff_MR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ff_SN.
    + */
    +goog.i18n.DateTimePatterns_ff_SN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fi_FI.
    + */
    +goog.i18n.DateTimePatterns_fi_FI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fil_PH.
    + */
    +goog.i18n.DateTimePatterns_fil_PH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fo.
    + */
    +goog.i18n.DateTimePatterns_fo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fo_FO.
    + */
    +goog.i18n.DateTimePatterns_fo_FO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_BE.
    + */
    +goog.i18n.DateTimePatterns_fr_BE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_BF.
    + */
    +goog.i18n.DateTimePatterns_fr_BF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_BI.
    + */
    +goog.i18n.DateTimePatterns_fr_BI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_BJ.
    + */
    +goog.i18n.DateTimePatterns_fr_BJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_BL.
    + */
    +goog.i18n.DateTimePatterns_fr_BL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CD.
    + */
    +goog.i18n.DateTimePatterns_fr_CD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CF.
    + */
    +goog.i18n.DateTimePatterns_fr_CF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CG.
    + */
    +goog.i18n.DateTimePatterns_fr_CG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CH.
    + */
    +goog.i18n.DateTimePatterns_fr_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CI.
    + */
    +goog.i18n.DateTimePatterns_fr_CI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_CM.
    + */
    +goog.i18n.DateTimePatterns_fr_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_DJ.
    + */
    +goog.i18n.DateTimePatterns_fr_DJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_DZ.
    + */
    +goog.i18n.DateTimePatterns_fr_DZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_FR.
    + */
    +goog.i18n.DateTimePatterns_fr_FR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_GA.
    + */
    +goog.i18n.DateTimePatterns_fr_GA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_GF.
    + */
    +goog.i18n.DateTimePatterns_fr_GF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_GN.
    + */
    +goog.i18n.DateTimePatterns_fr_GN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_GP.
    + */
    +goog.i18n.DateTimePatterns_fr_GP = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_GQ.
    + */
    +goog.i18n.DateTimePatterns_fr_GQ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_HT.
    + */
    +goog.i18n.DateTimePatterns_fr_HT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_KM.
    + */
    +goog.i18n.DateTimePatterns_fr_KM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_LU.
    + */
    +goog.i18n.DateTimePatterns_fr_LU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MA.
    + */
    +goog.i18n.DateTimePatterns_fr_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MC.
    + */
    +goog.i18n.DateTimePatterns_fr_MC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MF.
    + */
    +goog.i18n.DateTimePatterns_fr_MF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MG.
    + */
    +goog.i18n.DateTimePatterns_fr_MG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_ML.
    + */
    +goog.i18n.DateTimePatterns_fr_ML = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MQ.
    + */
    +goog.i18n.DateTimePatterns_fr_MQ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MR.
    + */
    +goog.i18n.DateTimePatterns_fr_MR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_MU.
    + */
    +goog.i18n.DateTimePatterns_fr_MU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_NC.
    + */
    +goog.i18n.DateTimePatterns_fr_NC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_NE.
    + */
    +goog.i18n.DateTimePatterns_fr_NE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_PF.
    + */
    +goog.i18n.DateTimePatterns_fr_PF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_PM.
    + */
    +goog.i18n.DateTimePatterns_fr_PM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_RE.
    + */
    +goog.i18n.DateTimePatterns_fr_RE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_RW.
    + */
    +goog.i18n.DateTimePatterns_fr_RW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_SC.
    + */
    +goog.i18n.DateTimePatterns_fr_SC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_SN.
    + */
    +goog.i18n.DateTimePatterns_fr_SN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_SY.
    + */
    +goog.i18n.DateTimePatterns_fr_SY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_TD.
    + */
    +goog.i18n.DateTimePatterns_fr_TD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_TG.
    + */
    +goog.i18n.DateTimePatterns_fr_TG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_TN.
    + */
    +goog.i18n.DateTimePatterns_fr_TN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_VU.
    + */
    +goog.i18n.DateTimePatterns_fr_VU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_WF.
    + */
    +goog.i18n.DateTimePatterns_fr_WF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fr_YT.
    + */
    +goog.i18n.DateTimePatterns_fr_YT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fur.
    + */
    +goog.i18n.DateTimePatterns_fur = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'LLLL \'dal\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd \'di\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'di\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fur_IT.
    + */
    +goog.i18n.DateTimePatterns_fur_IT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'LLLL \'dal\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd \'di\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'di\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fy.
    + */
    +goog.i18n.DateTimePatterns_fy = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale fy_NL.
    + */
    +goog.i18n.DateTimePatterns_fy_NL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ga_IE.
    + */
    +goog.i18n.DateTimePatterns_ga_IE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gd.
    + */
    +goog.i18n.DateTimePatterns_gd = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd\'mh\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd\'mh\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gd_GB.
    + */
    +goog.i18n.DateTimePatterns_gd_GB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd\'mh\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd\'mh\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gl_ES.
    + */
    +goog.i18n.DateTimePatterns_gl_ES = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gsw_CH.
    + */
    +goog.i18n.DateTimePatterns_gsw_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gsw_FR.
    + */
    +goog.i18n.DateTimePatterns_gsw_FR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gsw_LI.
    + */
    +goog.i18n.DateTimePatterns_gsw_LI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gu_IN.
    + */
    +goog.i18n.DateTimePatterns_gu_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale guz.
    + */
    +goog.i18n.DateTimePatterns_guz = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale guz_KE.
    + */
    +goog.i18n.DateTimePatterns_guz_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gv.
    + */
    +goog.i18n.DateTimePatterns_gv = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale gv_IM.
    + */
    +goog.i18n.DateTimePatterns_gv_IM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ha.
    + */
    +goog.i18n.DateTimePatterns_ha = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ha_Latn.
    + */
    +goog.i18n.DateTimePatterns_ha_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ha_Latn_GH.
    + */
    +goog.i18n.DateTimePatterns_ha_Latn_GH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ha_Latn_NE.
    + */
    +goog.i18n.DateTimePatterns_ha_Latn_NE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ha_Latn_NG.
    + */
    +goog.i18n.DateTimePatterns_ha_Latn_NG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale haw_US.
    + */
    +goog.i18n.DateTimePatterns_haw_US = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale he_IL.
    + */
    +goog.i18n.DateTimePatterns_he_IL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd בMMM',
    +  MONTH_DAY_FULL: 'dd בMMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd בMMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd בMMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d בMMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d בMMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hi_IN.
    + */
    +goog.i18n.DateTimePatterns_hi_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hr_BA.
    + */
    +goog.i18n.DateTimePatterns_hr_BA = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'LLL y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hr_HR.
    + */
    +goog.i18n.DateTimePatterns_hr_HR = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'LLL y.',
    +  YEAR_MONTH_FULL: 'LLLL y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hsb.
    + */
    +goog.i18n.DateTimePatterns_hsb = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hsb_DE.
    + */
    +goog.i18n.DateTimePatterns_hsb_DE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hu_HU.
    + */
    +goog.i18n.DateTimePatterns_hu_HU = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'G y.',
    +  YEAR_MONTH_ABBR: 'y. MMM',
    +  YEAR_MONTH_FULL: 'y. MMMM',
    +  MONTH_DAY_ABBR: 'MMM d.',
    +  MONTH_DAY_FULL: 'MMMM dd.',
    +  MONTH_DAY_SHORT: 'M. d.',
    +  MONTH_DAY_MEDIUM: 'MMMM d.',
    +  MONTH_DAY_YEAR_MEDIUM: 'y. MMM d.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d., EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y. MMM d., EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale hy_AM.
    + */
    +goog.i18n.DateTimePatterns_hy_AM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G yթ.',
    +  YEAR_MONTH_ABBR: 'yթ. LLL',
    +  YEAR_MONTH_FULL: 'yթ. LLLL',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, yթ.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'yթ. MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale id_ID.
    + */
    +goog.i18n.DateTimePatterns_id_ID = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ig.
    + */
    +goog.i18n.DateTimePatterns_ig = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ig_NG.
    + */
    +goog.i18n.DateTimePatterns_ig_NG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ii.
    + */
    +goog.i18n.DateTimePatterns_ii = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ii_CN.
    + */
    +goog.i18n.DateTimePatterns_ii_CN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale is_IS.
    + */
    +goog.i18n.DateTimePatterns_is_IS = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale it_CH.
    + */
    +goog.i18n.DateTimePatterns_it_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale it_IT.
    + */
    +goog.i18n.DateTimePatterns_it_IT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale it_SM.
    + */
    +goog.i18n.DateTimePatterns_it_SM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ja_JP.
    + */
    +goog.i18n.DateTimePatterns_ja_JP = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日(EEE)',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日(EEE)',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale jgo.
    + */
    +goog.i18n.DateTimePatterns_jgo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale jgo_CM.
    + */
    +goog.i18n.DateTimePatterns_jgo_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale jmc.
    + */
    +goog.i18n.DateTimePatterns_jmc = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale jmc_TZ.
    + */
    +goog.i18n.DateTimePatterns_jmc_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ka_GE.
    + */
    +goog.i18n.DateTimePatterns_ka_GE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM, y',
    +  YEAR_MONTH_FULL: 'MMMM, y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kab.
    + */
    +goog.i18n.DateTimePatterns_kab = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kab_DZ.
    + */
    +goog.i18n.DateTimePatterns_kab_DZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kam.
    + */
    +goog.i18n.DateTimePatterns_kam = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kam_KE.
    + */
    +goog.i18n.DateTimePatterns_kam_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kde.
    + */
    +goog.i18n.DateTimePatterns_kde = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kde_TZ.
    + */
    +goog.i18n.DateTimePatterns_kde_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kea.
    + */
    +goog.i18n.DateTimePatterns_kea = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'di\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'di\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd \'di\' MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd \'di\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kea_CV.
    + */
    +goog.i18n.DateTimePatterns_kea_CV = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM \'di\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'di\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd \'di\' MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd \'di\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale khq.
    + */
    +goog.i18n.DateTimePatterns_khq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale khq_ML.
    + */
    +goog.i18n.DateTimePatterns_khq_ML = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ki.
    + */
    +goog.i18n.DateTimePatterns_ki = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ki_KE.
    + */
    +goog.i18n.DateTimePatterns_ki_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kk_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_kk_Cyrl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kk_Cyrl_KZ.
    + */
    +goog.i18n.DateTimePatterns_kk_Cyrl_KZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kkj.
    + */
    +goog.i18n.DateTimePatterns_kkj = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kkj_CM.
    + */
    +goog.i18n.DateTimePatterns_kkj_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kl.
    + */
    +goog.i18n.DateTimePatterns_kl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kl_GL.
    + */
    +goog.i18n.DateTimePatterns_kl_GL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kln.
    + */
    +goog.i18n.DateTimePatterns_kln = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kln_KE.
    + */
    +goog.i18n.DateTimePatterns_kln_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale km_KH.
    + */
    +goog.i18n.DateTimePatterns_km_KH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y នៃ G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kn_IN.
    + */
    +goog.i18n.DateTimePatterns_kn_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d,y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ko_KP.
    + */
    +goog.i18n.DateTimePatterns_ko_KP = {
    +  YEAR_FULL: 'y년',
    +  YEAR_FULL_WITH_ERA: 'G y년',
    +  YEAR_MONTH_ABBR: 'y년 MMM',
    +  YEAR_MONTH_FULL: 'y년 MMMM',
    +  MONTH_DAY_ABBR: 'MMM d일',
    +  MONTH_DAY_FULL: 'MMMM dd일',
    +  MONTH_DAY_SHORT: 'M. d.',
    +  MONTH_DAY_MEDIUM: 'MMMM d일',
    +  MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d일 (EEE)',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일 (EEE)',
    +  DAY_ABBR: 'd일'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ko_KR.
    + */
    +goog.i18n.DateTimePatterns_ko_KR = {
    +  YEAR_FULL: 'y년',
    +  YEAR_FULL_WITH_ERA: 'G y년',
    +  YEAR_MONTH_ABBR: 'y년 MMM',
    +  YEAR_MONTH_FULL: 'y년 MMMM',
    +  MONTH_DAY_ABBR: 'MMM d일',
    +  MONTH_DAY_FULL: 'MMMM dd일',
    +  MONTH_DAY_SHORT: 'M. d.',
    +  MONTH_DAY_MEDIUM: 'MMMM d일',
    +  MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d일 (EEE)',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y년 MMM d일 (EEE)',
    +  DAY_ABBR: 'd일'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kok.
    + */
    +goog.i18n.DateTimePatterns_kok = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kok_IN.
    + */
    +goog.i18n.DateTimePatterns_kok_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ks.
    + */
    +goog.i18n.DateTimePatterns_ks = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'Gy',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ks_Arab.
    + */
    +goog.i18n.DateTimePatterns_ks_Arab = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'Gy',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ks_Arab_IN.
    + */
    +goog.i18n.DateTimePatterns_ks_Arab_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'Gy',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksb.
    + */
    +goog.i18n.DateTimePatterns_ksb = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksb_TZ.
    + */
    +goog.i18n.DateTimePatterns_ksb_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksf.
    + */
    +goog.i18n.DateTimePatterns_ksf = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksf_CM.
    + */
    +goog.i18n.DateTimePatterns_ksf_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksh.
    + */
    +goog.i18n.DateTimePatterns_ksh = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM. y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ksh_DE.
    + */
    +goog.i18n.DateTimePatterns_ksh_DE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM. y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kw.
    + */
    +goog.i18n.DateTimePatterns_kw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale kw_GB.
    + */
    +goog.i18n.DateTimePatterns_kw_GB = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ky_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_ky_Cyrl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y-\'ж\'.',
    +  YEAR_MONTH_ABBR: 'y-\'ж\'. MMM',
    +  YEAR_MONTH_FULL: 'y-\'ж\'. MMMM',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd-MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd-MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ky_Cyrl_KG.
    + */
    +goog.i18n.DateTimePatterns_ky_Cyrl_KG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y-\'ж\'.',
    +  YEAR_MONTH_ABBR: 'y-\'ж\'. MMM',
    +  YEAR_MONTH_FULL: 'y-\'ж\'. MMMM',
    +  MONTH_DAY_ABBR: 'd-MMM',
    +  MONTH_DAY_FULL: 'dd-MMMM',
    +  MONTH_DAY_SHORT: 'dd-MM',
    +  MONTH_DAY_MEDIUM: 'd-MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd-MMM, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y-\'ж\'. d-MMM, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lag.
    + */
    +goog.i18n.DateTimePatterns_lag = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lag_TZ.
    + */
    +goog.i18n.DateTimePatterns_lag_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lb.
    + */
    +goog.i18n.DateTimePatterns_lb = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lb_LU.
    + */
    +goog.i18n.DateTimePatterns_lb_LU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lg.
    + */
    +goog.i18n.DateTimePatterns_lg = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lg_UG.
    + */
    +goog.i18n.DateTimePatterns_lg_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lkt.
    + */
    +goog.i18n.DateTimePatterns_lkt = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lkt_US.
    + */
    +goog.i18n.DateTimePatterns_lkt_US = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ln_AO.
    + */
    +goog.i18n.DateTimePatterns_ln_AO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ln_CD.
    + */
    +goog.i18n.DateTimePatterns_ln_CD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ln_CF.
    + */
    +goog.i18n.DateTimePatterns_ln_CF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ln_CG.
    + */
    +goog.i18n.DateTimePatterns_ln_CG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lo_LA.
    + */
    +goog.i18n.DateTimePatterns_lo_LA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lt_LT.
    + */
    +goog.i18n.DateTimePatterns_lt_LT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'y-MM',
    +  YEAR_MONTH_FULL: 'y LLLL',
    +  MONTH_DAY_ABBR: 'MM-dd',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y-MM-dd',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MM-dd, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y-MM-dd, EEE',
    +  DAY_ABBR: 'dd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lu.
    + */
    +goog.i18n.DateTimePatterns_lu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lu_CD.
    + */
    +goog.i18n.DateTimePatterns_lu_CD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale luo.
    + */
    +goog.i18n.DateTimePatterns_luo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale luo_KE.
    + */
    +goog.i18n.DateTimePatterns_luo_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale luy.
    + */
    +goog.i18n.DateTimePatterns_luy = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale luy_KE.
    + */
    +goog.i18n.DateTimePatterns_luy_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale lv_LV.
    + */
    +goog.i18n.DateTimePatterns_lv_LV = {
    +  YEAR_FULL: 'y. \'g\'.',
    +  YEAR_FULL_WITH_ERA: 'G y. \'g\'.',
    +  YEAR_MONTH_ABBR: 'y. \'g\'. MMM',
    +  YEAR_MONTH_FULL: 'y. \'g\'. MMMM',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y. \'g\'. d. MMM',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y. \'g\'. d. MMM',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mas.
    + */
    +goog.i18n.DateTimePatterns_mas = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mas_KE.
    + */
    +goog.i18n.DateTimePatterns_mas_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mas_TZ.
    + */
    +goog.i18n.DateTimePatterns_mas_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mer.
    + */
    +goog.i18n.DateTimePatterns_mer = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mer_KE.
    + */
    +goog.i18n.DateTimePatterns_mer_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mfe.
    + */
    +goog.i18n.DateTimePatterns_mfe = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mfe_MU.
    + */
    +goog.i18n.DateTimePatterns_mfe_MU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mg.
    + */
    +goog.i18n.DateTimePatterns_mg = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mg_MG.
    + */
    +goog.i18n.DateTimePatterns_mg_MG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mgh.
    + */
    +goog.i18n.DateTimePatterns_mgh = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mgh_MZ.
    + */
    +goog.i18n.DateTimePatterns_mgh_MZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mgo.
    + */
    +goog.i18n.DateTimePatterns_mgo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mgo_CM.
    + */
    +goog.i18n.DateTimePatterns_mgo_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mk_MK.
    + */
    +goog.i18n.DateTimePatterns_mk_MK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y \'г\'.',
    +  YEAR_MONTH_FULL: 'MMMM y \'г\'.',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ml_IN.
    + */
    +goog.i18n.DateTimePatterns_ml_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mn_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_mn_Cyrl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mn_Cyrl_MN.
    + */
    +goog.i18n.DateTimePatterns_mn_Cyrl_MN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mr_IN.
    + */
    +goog.i18n.DateTimePatterns_mr_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ms_Latn.
    + */
    +goog.i18n.DateTimePatterns_ms_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ms_Latn_BN.
    + */
    +goog.i18n.DateTimePatterns_ms_Latn_BN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ms_Latn_MY.
    + */
    +goog.i18n.DateTimePatterns_ms_Latn_MY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ms_Latn_SG.
    + */
    +goog.i18n.DateTimePatterns_ms_Latn_SG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mt_MT.
    + */
    +goog.i18n.DateTimePatterns_mt_MT = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mua.
    + */
    +goog.i18n.DateTimePatterns_mua = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale mua_CM.
    + */
    +goog.i18n.DateTimePatterns_mua_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale my_MM.
    + */
    +goog.i18n.DateTimePatterns_my_MM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, y MMM d',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale naq.
    + */
    +goog.i18n.DateTimePatterns_naq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale naq_NA.
    + */
    +goog.i18n.DateTimePatterns_naq_NA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nb_NO.
    + */
    +goog.i18n.DateTimePatterns_nb_NO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nb_SJ.
    + */
    +goog.i18n.DateTimePatterns_nb_SJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nd.
    + */
    +goog.i18n.DateTimePatterns_nd = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nd_ZW.
    + */
    +goog.i18n.DateTimePatterns_nd_ZW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ne_IN.
    + */
    +goog.i18n.DateTimePatterns_ne_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ne_NP.
    + */
    +goog.i18n.DateTimePatterns_ne_NP = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_AW.
    + */
    +goog.i18n.DateTimePatterns_nl_AW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_BE.
    + */
    +goog.i18n.DateTimePatterns_nl_BE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_BQ.
    + */
    +goog.i18n.DateTimePatterns_nl_BQ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_CW.
    + */
    +goog.i18n.DateTimePatterns_nl_CW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_NL.
    + */
    +goog.i18n.DateTimePatterns_nl_NL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_SR.
    + */
    +goog.i18n.DateTimePatterns_nl_SR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nl_SX.
    + */
    +goog.i18n.DateTimePatterns_nl_SX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nmg.
    + */
    +goog.i18n.DateTimePatterns_nmg = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nmg_CM.
    + */
    +goog.i18n.DateTimePatterns_nmg_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nn.
    + */
    +goog.i18n.DateTimePatterns_nn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nn_NO.
    + */
    +goog.i18n.DateTimePatterns_nn_NO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. MMM y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nnh.
    + */
    +goog.i18n.DateTimePatterns_nnh = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: '\'lyɛ\'̌ʼ d \'na\' MMMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE , \'lyɛ\'̌ʼ d \'na\' MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nnh_CM.
    + */
    +goog.i18n.DateTimePatterns_nnh_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: '\'lyɛ\'̌ʼ d \'na\' MMMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE , \'lyɛ\'̌ʼ d \'na\' MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nus.
    + */
    +goog.i18n.DateTimePatterns_nus = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nus_SD.
    + */
    +goog.i18n.DateTimePatterns_nus_SD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nyn.
    + */
    +goog.i18n.DateTimePatterns_nyn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale nyn_UG.
    + */
    +goog.i18n.DateTimePatterns_nyn_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale om.
    + */
    +goog.i18n.DateTimePatterns_om = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale om_ET.
    + */
    +goog.i18n.DateTimePatterns_om_ET = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale om_KE.
    + */
    +goog.i18n.DateTimePatterns_om_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale or_IN.
    + */
    +goog.i18n.DateTimePatterns_or_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale os.
    + */
    +goog.i18n.DateTimePatterns_os = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y \'аз\'',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale os_GE.
    + */
    +goog.i18n.DateTimePatterns_os_GE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y \'аз\'',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale os_RU.
    + */
    +goog.i18n.DateTimePatterns_os_RU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y \'аз\'',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pa_Arab.
    + */
    +goog.i18n.DateTimePatterns_pa_Arab = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pa_Arab_PK.
    + */
    +goog.i18n.DateTimePatterns_pa_Arab_PK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pa_Guru.
    + */
    +goog.i18n.DateTimePatterns_pa_Guru = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pa_Guru_IN.
    + */
    +goog.i18n.DateTimePatterns_pa_Guru_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pl_PL.
    + */
    +goog.i18n.DateTimePatterns_pl_PL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ps.
    + */
    +goog.i18n.DateTimePatterns_ps = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ps_AF.
    + */
    +goog.i18n.DateTimePatterns_ps_AF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_AO.
    + */
    +goog.i18n.DateTimePatterns_pt_AO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_CV.
    + */
    +goog.i18n.DateTimePatterns_pt_CV = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_GW.
    + */
    +goog.i18n.DateTimePatterns_pt_GW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_MO.
    + */
    +goog.i18n.DateTimePatterns_pt_MO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_MZ.
    + */
    +goog.i18n.DateTimePatterns_pt_MZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_ST.
    + */
    +goog.i18n.DateTimePatterns_pt_ST = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale pt_TL.
    + */
    +goog.i18n.DateTimePatterns_pt_TL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MM/y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd/MM',
    +  MONTH_DAY_FULL: 'dd \'de\' MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd \'de\' MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd/MM/y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d/MM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d/MM/y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale qu.
    + */
    +goog.i18n.DateTimePatterns_qu = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale qu_BO.
    + */
    +goog.i18n.DateTimePatterns_qu_BO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale qu_EC.
    + */
    +goog.i18n.DateTimePatterns_qu_EC = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale qu_PE.
    + */
    +goog.i18n.DateTimePatterns_qu_PE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rm.
    + */
    +goog.i18n.DateTimePatterns_rm = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rm_CH.
    + */
    +goog.i18n.DateTimePatterns_rm_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd.M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rn.
    + */
    +goog.i18n.DateTimePatterns_rn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rn_BI.
    + */
    +goog.i18n.DateTimePatterns_rn_BI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ro_MD.
    + */
    +goog.i18n.DateTimePatterns_ro_MD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ro_RO.
    + */
    +goog.i18n.DateTimePatterns_ro_RO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rof.
    + */
    +goog.i18n.DateTimePatterns_rof = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rof_TZ.
    + */
    +goog.i18n.DateTimePatterns_rof_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_BY.
    + */
    +goog.i18n.DateTimePatterns_ru_BY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_KG.
    + */
    +goog.i18n.DateTimePatterns_ru_KG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_KZ.
    + */
    +goog.i18n.DateTimePatterns_ru_KZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_MD.
    + */
    +goog.i18n.DateTimePatterns_ru_MD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_RU.
    + */
    +goog.i18n.DateTimePatterns_ru_RU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y \'г\'.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ru_UA.
    + */
    +goog.i18n.DateTimePatterns_ru_UA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'ccc, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y \'г\'.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rw.
    + */
    +goog.i18n.DateTimePatterns_rw = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rw_RW.
    + */
    +goog.i18n.DateTimePatterns_rw_RW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rwk.
    + */
    +goog.i18n.DateTimePatterns_rwk = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale rwk_TZ.
    + */
    +goog.i18n.DateTimePatterns_rwk_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sah.
    + */
    +goog.i18n.DateTimePatterns_sah = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y, MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sah_RU.
    + */
    +goog.i18n.DateTimePatterns_sah_RU = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y, MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale saq.
    + */
    +goog.i18n.DateTimePatterns_saq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale saq_KE.
    + */
    +goog.i18n.DateTimePatterns_saq_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sbp.
    + */
    +goog.i18n.DateTimePatterns_sbp = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sbp_TZ.
    + */
    +goog.i18n.DateTimePatterns_sbp_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale se.
    + */
    +goog.i18n.DateTimePatterns_se = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale se_FI.
    + */
    +goog.i18n.DateTimePatterns_se_FI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale se_NO.
    + */
    +goog.i18n.DateTimePatterns_se_NO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale se_SE.
    + */
    +goog.i18n.DateTimePatterns_se_SE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale seh.
    + */
    +goog.i18n.DateTimePatterns_seh = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale seh_MZ.
    + */
    +goog.i18n.DateTimePatterns_seh_MZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM \'de\' y',
    +  YEAR_MONTH_FULL: 'MMMM \'de\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd \'de\' MMM \'de\' y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d \'de\' MMM \'de\' y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ses.
    + */
    +goog.i18n.DateTimePatterns_ses = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ses_ML.
    + */
    +goog.i18n.DateTimePatterns_ses_ML = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sg.
    + */
    +goog.i18n.DateTimePatterns_sg = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sg_CF.
    + */
    +goog.i18n.DateTimePatterns_sg_CF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale shi.
    + */
    +goog.i18n.DateTimePatterns_shi = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale shi_Latn.
    + */
    +goog.i18n.DateTimePatterns_shi_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale shi_Latn_MA.
    + */
    +goog.i18n.DateTimePatterns_shi_Latn_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale shi_Tfng.
    + */
    +goog.i18n.DateTimePatterns_shi_Tfng = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale shi_Tfng_MA.
    + */
    +goog.i18n.DateTimePatterns_shi_Tfng_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale si_LK.
    + */
    +goog.i18n.DateTimePatterns_si_LK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sk_SK.
    + */
    +goog.i18n.DateTimePatterns_sk_SK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd. M',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. M. y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. M.',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d. M. y',
    +  DAY_ABBR: 'd.'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sl_SI.
    + */
    +goog.i18n.DateTimePatterns_sl_SI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. M.',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale smn.
    + */
    +goog.i18n.DateTimePatterns_smn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale smn_FI.
    + */
    +goog.i18n.DateTimePatterns_smn_FI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sn.
    + */
    +goog.i18n.DateTimePatterns_sn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sn_ZW.
    + */
    +goog.i18n.DateTimePatterns_sn_ZW = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale so.
    + */
    +goog.i18n.DateTimePatterns_so = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale so_DJ.
    + */
    +goog.i18n.DateTimePatterns_so_DJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale so_ET.
    + */
    +goog.i18n.DateTimePatterns_so_ET = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale so_KE.
    + */
    +goog.i18n.DateTimePatterns_so_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale so_SO.
    + */
    +goog.i18n.DateTimePatterns_so_SO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sq_AL.
    + */
    +goog.i18n.DateTimePatterns_sq_AL = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sq_MK.
    + */
    +goog.i18n.DateTimePatterns_sq_MK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sq_XK.
    + */
    +goog.i18n.DateTimePatterns_sq_XK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd.M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_sr_Cyrl = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Cyrl_BA.
    + */
    +goog.i18n.DateTimePatterns_sr_Cyrl_BA = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Cyrl_ME.
    + */
    +goog.i18n.DateTimePatterns_sr_Cyrl_ME = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Cyrl_RS.
    + */
    +goog.i18n.DateTimePatterns_sr_Cyrl_RS = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Cyrl_XK.
    + */
    +goog.i18n.DateTimePatterns_sr_Cyrl_XK = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Latn.
    + */
    +goog.i18n.DateTimePatterns_sr_Latn = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Latn_BA.
    + */
    +goog.i18n.DateTimePatterns_sr_Latn_BA = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Latn_ME.
    + */
    +goog.i18n.DateTimePatterns_sr_Latn_ME = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Latn_RS.
    + */
    +goog.i18n.DateTimePatterns_sr_Latn_RS = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sr_Latn_XK.
    + */
    +goog.i18n.DateTimePatterns_sr_Latn_XK = {
    +  YEAR_FULL: 'y.',
    +  YEAR_FULL_WITH_ERA: 'y. G',
    +  YEAR_MONTH_ABBR: 'MMM y.',
    +  YEAR_MONTH_FULL: 'MMMM y.',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y.',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y.',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sv_AX.
    + */
    +goog.i18n.DateTimePatterns_sv_AX = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sv_FI.
    + */
    +goog.i18n.DateTimePatterns_sv_FI = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sv_SE.
    + */
    +goog.i18n.DateTimePatterns_sv_SE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sw_KE.
    + */
    +goog.i18n.DateTimePatterns_sw_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sw_TZ.
    + */
    +goog.i18n.DateTimePatterns_sw_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale sw_UG.
    + */
    +goog.i18n.DateTimePatterns_sw_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale swc.
    + */
    +goog.i18n.DateTimePatterns_swc = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale swc_CD.
    + */
    +goog.i18n.DateTimePatterns_swc_CD = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ta_IN.
    + */
    +goog.i18n.DateTimePatterns_ta_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ta_LK.
    + */
    +goog.i18n.DateTimePatterns_ta_LK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ta_MY.
    + */
    +goog.i18n.DateTimePatterns_ta_MY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ta_SG.
    + */
    +goog.i18n.DateTimePatterns_ta_SG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale te_IN.
    + */
    +goog.i18n.DateTimePatterns_te_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd, MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d, MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale teo.
    + */
    +goog.i18n.DateTimePatterns_teo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale teo_KE.
    + */
    +goog.i18n.DateTimePatterns_teo_KE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale teo_UG.
    + */
    +goog.i18n.DateTimePatterns_teo_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale th_TH.
    + */
    +goog.i18n.DateTimePatterns_th_TH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ti.
    + */
    +goog.i18n.DateTimePatterns_ti = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ti_ER.
    + */
    +goog.i18n.DateTimePatterns_ti_ER = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ti_ET.
    + */
    +goog.i18n.DateTimePatterns_ti_ET = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd-MMM-y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale to.
    + */
    +goog.i18n.DateTimePatterns_to = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale to_TO.
    + */
    +goog.i18n.DateTimePatterns_to_TO = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tr_CY.
    + */
    +goog.i18n.DateTimePatterns_tr_CY = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMMM EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tr_TR.
    + */
    +goog.i18n.DateTimePatterns_tr_TR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd/MM',
    +  MONTH_DAY_MEDIUM: 'dd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'd MMMM EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'd MMM y EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale twq.
    + */
    +goog.i18n.DateTimePatterns_twq = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale twq_NE.
    + */
    +goog.i18n.DateTimePatterns_twq_NE = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tzm.
    + */
    +goog.i18n.DateTimePatterns_tzm = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tzm_Latn.
    + */
    +goog.i18n.DateTimePatterns_tzm_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale tzm_Latn_MA.
    + */
    +goog.i18n.DateTimePatterns_tzm_Latn_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ug.
    + */
    +goog.i18n.DateTimePatterns_ug = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، MMM d، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ug_Arab.
    + */
    +goog.i18n.DateTimePatterns_ug_Arab = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، MMM d، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ug_Arab_CN.
    + */
    +goog.i18n.DateTimePatterns_ug_Arab_CN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، MMM d، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uk_UA.
    + */
    +goog.i18n.DateTimePatterns_uk_UA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'LLL y',
    +  YEAR_MONTH_FULL: 'LLLL y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd.MM',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ur_IN.
    + */
    +goog.i18n.DateTimePatterns_ur_IN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale ur_PK.
    + */
    +goog.i18n.DateTimePatterns_ur_PK = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM، y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE، d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE، d MMM، y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Arab.
    + */
    +goog.i18n.DateTimePatterns_uz_Arab = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Arab_AF.
    + */
    +goog.i18n.DateTimePatterns_uz_Arab_AF = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Cyrl.
    + */
    +goog.i18n.DateTimePatterns_uz_Cyrl = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Cyrl_UZ.
    + */
    +goog.i18n.DateTimePatterns_uz_Cyrl_UZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Latn.
    + */
    +goog.i18n.DateTimePatterns_uz_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale uz_Latn_UZ.
    + */
    +goog.i18n.DateTimePatterns_uz_Latn_UZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'y MMM',
    +  YEAR_MONTH_FULL: 'y MMMM',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'y MMM d',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y MMM d, EEE',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vai.
    + */
    +goog.i18n.DateTimePatterns_vai = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vai_Latn.
    + */
    +goog.i18n.DateTimePatterns_vai_Latn = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vai_Latn_LR.
    + */
    +goog.i18n.DateTimePatterns_vai_Latn_LR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vai_Vaii.
    + */
    +goog.i18n.DateTimePatterns_vai_Vaii = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vai_Vaii_LR.
    + */
    +goog.i18n.DateTimePatterns_vai_Vaii_LR = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vi_VN.
    + */
    +goog.i18n.DateTimePatterns_vi_VN = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM \'năm\' y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'dd-M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'dd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, dd MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dd MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vun.
    + */
    +goog.i18n.DateTimePatterns_vun = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale vun_TZ.
    + */
    +goog.i18n.DateTimePatterns_vun_TZ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale wae.
    + */
    +goog.i18n.DateTimePatterns_wae = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. MMM',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale wae_CH.
    + */
    +goog.i18n.DateTimePatterns_wae_CH = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd. MMM',
    +  MONTH_DAY_FULL: 'dd. MMMM',
    +  MONTH_DAY_SHORT: 'd. MMM',
    +  MONTH_DAY_MEDIUM: 'd. MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd. MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, d. MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, d. MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale xog.
    + */
    +goog.i18n.DateTimePatterns_xog = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale xog_UG.
    + */
    +goog.i18n.DateTimePatterns_xog_UG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yav.
    + */
    +goog.i18n.DateTimePatterns_yav = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yav_CM.
    + */
    +goog.i18n.DateTimePatterns_yav_CM = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yi.
    + */
    +goog.i18n.DateTimePatterns_yi = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'dטן MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dטן MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yi_001.
    + */
    +goog.i18n.DateTimePatterns_yi_001 = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'MM-dd',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'dטן MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'MMM d, EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, dטן MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yo.
    + */
    +goog.i18n.DateTimePatterns_yo = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yo_BJ.
    + */
    +goog.i18n.DateTimePatterns_yo_BJ = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale yo_NG.
    + */
    +goog.i18n.DateTimePatterns_yo_NG = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zgh.
    + */
    +goog.i18n.DateTimePatterns_zgh = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zgh_MA.
    + */
    +goog.i18n.DateTimePatterns_zgh_MA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'G y',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'd MMM',
    +  MONTH_DAY_FULL: 'dd MMMM',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'd MMMM',
    +  MONTH_DAY_YEAR_MEDIUM: 'd MMM, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE d MMM',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE d MMM y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hans.
    + */
    +goog.i18n.DateTimePatterns_zh_Hans = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hans_CN.
    + */
    +goog.i18n.DateTimePatterns_zh_Hans_CN = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hans_HK.
    + */
    +goog.i18n.DateTimePatterns_zh_Hans_HK = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月d日',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hans_MO.
    + */
    +goog.i18n.DateTimePatterns_zh_Hans_MO = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月d日',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hans_SG.
    + */
    +goog.i18n.DateTimePatterns_zh_Hans_SG = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月d日',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hant.
    + */
    +goog.i18n.DateTimePatterns_zh_Hant = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日 EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日 EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hant_HK.
    + */
    +goog.i18n.DateTimePatterns_zh_Hant_HK = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'd/M',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hant_MO.
    + */
    +goog.i18n.DateTimePatterns_zh_Hant_MO = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M-d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zh_Hant_TW.
    + */
    +goog.i18n.DateTimePatterns_zh_Hant_TW = {
    +  YEAR_FULL: 'y年',
    +  YEAR_FULL_WITH_ERA: 'Gy年',
    +  YEAR_MONTH_ABBR: 'y年M月',
    +  YEAR_MONTH_FULL: 'y年M月',
    +  MONTH_DAY_ABBR: 'M月d日',
    +  MONTH_DAY_FULL: 'M月dd日',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'M月d日',
    +  MONTH_DAY_YEAR_MEDIUM: 'y年M月d日',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'M月d日 EEE',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'y年M月d日 EEE',
    +  DAY_ABBR: 'd日'
    +};
    +
    +
    +/**
    + * Extended set of localized date/time patterns for locale zu_ZA.
    + */
    +goog.i18n.DateTimePatterns_zu_ZA = {
    +  YEAR_FULL: 'y',
    +  YEAR_FULL_WITH_ERA: 'y G',
    +  YEAR_MONTH_ABBR: 'MMM y',
    +  YEAR_MONTH_FULL: 'MMMM y',
    +  MONTH_DAY_ABBR: 'MMM d',
    +  MONTH_DAY_FULL: 'MMMM dd',
    +  MONTH_DAY_SHORT: 'M/d',
    +  MONTH_DAY_MEDIUM: 'MMMM d',
    +  MONTH_DAY_YEAR_MEDIUM: 'MMM d, y',
    +  WEEKDAY_MONTH_DAY_MEDIUM: 'EEE, MMM d',
    +  WEEKDAY_MONTH_DAY_YEAR_MEDIUM: 'EEE, MMM d, y',
    +  DAY_ABBR: 'd'
    +};
    +
    +
    +/**
    + * Select date/time pattern by locale.
    + */
    +if (goog.LOCALE == 'af_NA' || goog.LOCALE == 'af-NA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_af_NA;
    +}
    +
    +if (goog.LOCALE == 'af_ZA' || goog.LOCALE == 'af-ZA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_af_ZA;
    +}
    +
    +if (goog.LOCALE == 'agq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_agq;
    +}
    +
    +if (goog.LOCALE == 'agq_CM' || goog.LOCALE == 'agq-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_agq_CM;
    +}
    +
    +if (goog.LOCALE == 'ak') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ak;
    +}
    +
    +if (goog.LOCALE == 'ak_GH' || goog.LOCALE == 'ak-GH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ak_GH;
    +}
    +
    +if (goog.LOCALE == 'am_ET' || goog.LOCALE == 'am-ET') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_am_ET;
    +}
    +
    +if (goog.LOCALE == 'ar_001' || goog.LOCALE == 'ar-001') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_001;
    +}
    +
    +if (goog.LOCALE == 'ar_AE' || goog.LOCALE == 'ar-AE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_AE;
    +}
    +
    +if (goog.LOCALE == 'ar_BH' || goog.LOCALE == 'ar-BH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_BH;
    +}
    +
    +if (goog.LOCALE == 'ar_DJ' || goog.LOCALE == 'ar-DJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_DJ;
    +}
    +
    +if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_DZ;
    +}
    +
    +if (goog.LOCALE == 'ar_EG' || goog.LOCALE == 'ar-EG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_EG;
    +}
    +
    +if (goog.LOCALE == 'ar_EH' || goog.LOCALE == 'ar-EH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_EH;
    +}
    +
    +if (goog.LOCALE == 'ar_ER' || goog.LOCALE == 'ar-ER') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_ER;
    +}
    +
    +if (goog.LOCALE == 'ar_IL' || goog.LOCALE == 'ar-IL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_IL;
    +}
    +
    +if (goog.LOCALE == 'ar_IQ' || goog.LOCALE == 'ar-IQ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_IQ;
    +}
    +
    +if (goog.LOCALE == 'ar_JO' || goog.LOCALE == 'ar-JO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_JO;
    +}
    +
    +if (goog.LOCALE == 'ar_KM' || goog.LOCALE == 'ar-KM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_KM;
    +}
    +
    +if (goog.LOCALE == 'ar_KW' || goog.LOCALE == 'ar-KW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_KW;
    +}
    +
    +if (goog.LOCALE == 'ar_LB' || goog.LOCALE == 'ar-LB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_LB;
    +}
    +
    +if (goog.LOCALE == 'ar_LY' || goog.LOCALE == 'ar-LY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_LY;
    +}
    +
    +if (goog.LOCALE == 'ar_MA' || goog.LOCALE == 'ar-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_MA;
    +}
    +
    +if (goog.LOCALE == 'ar_MR' || goog.LOCALE == 'ar-MR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_MR;
    +}
    +
    +if (goog.LOCALE == 'ar_OM' || goog.LOCALE == 'ar-OM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_OM;
    +}
    +
    +if (goog.LOCALE == 'ar_PS' || goog.LOCALE == 'ar-PS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_PS;
    +}
    +
    +if (goog.LOCALE == 'ar_QA' || goog.LOCALE == 'ar-QA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_QA;
    +}
    +
    +if (goog.LOCALE == 'ar_SA' || goog.LOCALE == 'ar-SA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_SA;
    +}
    +
    +if (goog.LOCALE == 'ar_SD' || goog.LOCALE == 'ar-SD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_SD;
    +}
    +
    +if (goog.LOCALE == 'ar_SO' || goog.LOCALE == 'ar-SO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_SO;
    +}
    +
    +if (goog.LOCALE == 'ar_SS' || goog.LOCALE == 'ar-SS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_SS;
    +}
    +
    +if (goog.LOCALE == 'ar_SY' || goog.LOCALE == 'ar-SY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_SY;
    +}
    +
    +if (goog.LOCALE == 'ar_TD' || goog.LOCALE == 'ar-TD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_TD;
    +}
    +
    +if (goog.LOCALE == 'ar_TN' || goog.LOCALE == 'ar-TN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_TN;
    +}
    +
    +if (goog.LOCALE == 'ar_YE' || goog.LOCALE == 'ar-YE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ar_YE;
    +}
    +
    +if (goog.LOCALE == 'as') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_as;
    +}
    +
    +if (goog.LOCALE == 'as_IN' || goog.LOCALE == 'as-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_as_IN;
    +}
    +
    +if (goog.LOCALE == 'asa') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_asa;
    +}
    +
    +if (goog.LOCALE == 'asa_TZ' || goog.LOCALE == 'asa-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_asa_TZ;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl' || goog.LOCALE == 'az-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_az_Cyrl_AZ;
    +}
    +
    +if (goog.LOCALE == 'az_Latn' || goog.LOCALE == 'az-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_az_Latn_AZ;
    +}
    +
    +if (goog.LOCALE == 'bas') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bas;
    +}
    +
    +if (goog.LOCALE == 'bas_CM' || goog.LOCALE == 'bas-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bas_CM;
    +}
    +
    +if (goog.LOCALE == 'be') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_be;
    +}
    +
    +if (goog.LOCALE == 'be_BY' || goog.LOCALE == 'be-BY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_be_BY;
    +}
    +
    +if (goog.LOCALE == 'bem') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bem;
    +}
    +
    +if (goog.LOCALE == 'bem_ZM' || goog.LOCALE == 'bem-ZM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bem_ZM;
    +}
    +
    +if (goog.LOCALE == 'bez') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bez;
    +}
    +
    +if (goog.LOCALE == 'bez_TZ' || goog.LOCALE == 'bez-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bez_TZ;
    +}
    +
    +if (goog.LOCALE == 'bg_BG' || goog.LOCALE == 'bg-BG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bg_BG;
    +}
    +
    +if (goog.LOCALE == 'bm') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn' || goog.LOCALE == 'bm-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bm_Latn;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn_ML' || goog.LOCALE == 'bm-Latn-ML') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bm_Latn_ML;
    +}
    +
    +if (goog.LOCALE == 'bn_BD' || goog.LOCALE == 'bn-BD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bn_BD;
    +}
    +
    +if (goog.LOCALE == 'bn_IN' || goog.LOCALE == 'bn-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bn_IN;
    +}
    +
    +if (goog.LOCALE == 'bo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_CN' || goog.LOCALE == 'bo-CN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bo_CN;
    +}
    +
    +if (goog.LOCALE == 'bo_IN' || goog.LOCALE == 'bo-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bo_IN;
    +}
    +
    +if (goog.LOCALE == 'br_FR' || goog.LOCALE == 'br-FR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_br_FR;
    +}
    +
    +if (goog.LOCALE == 'brx') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_brx;
    +}
    +
    +if (goog.LOCALE == 'brx_IN' || goog.LOCALE == 'brx-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_brx_IN;
    +}
    +
    +if (goog.LOCALE == 'bs') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl' || goog.LOCALE == 'bs-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl_BA' || goog.LOCALE == 'bs-Cyrl-BA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bs_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn' || goog.LOCALE == 'bs-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bs_Latn;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn_BA' || goog.LOCALE == 'bs-Latn-BA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_bs_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'ca_AD' || goog.LOCALE == 'ca-AD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ca_AD;
    +}
    +
    +if (goog.LOCALE == 'ca_ES' || goog.LOCALE == 'ca-ES') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ca_ES;
    +}
    +
    +if (goog.LOCALE == 'ca_FR' || goog.LOCALE == 'ca-FR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ca_FR;
    +}
    +
    +if (goog.LOCALE == 'ca_IT' || goog.LOCALE == 'ca-IT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ca_IT;
    +}
    +
    +if (goog.LOCALE == 'cgg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cgg;
    +}
    +
    +if (goog.LOCALE == 'cgg_UG' || goog.LOCALE == 'cgg-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cgg_UG;
    +}
    +
    +if (goog.LOCALE == 'chr_US' || goog.LOCALE == 'chr-US') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_chr_US;
    +}
    +
    +if (goog.LOCALE == 'cs_CZ' || goog.LOCALE == 'cs-CZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cs_CZ;
    +}
    +
    +if (goog.LOCALE == 'cy_GB' || goog.LOCALE == 'cy-GB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_cy_GB;
    +}
    +
    +if (goog.LOCALE == 'da_DK' || goog.LOCALE == 'da-DK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_da_DK;
    +}
    +
    +if (goog.LOCALE == 'da_GL' || goog.LOCALE == 'da-GL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_da_GL;
    +}
    +
    +if (goog.LOCALE == 'dav') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dav;
    +}
    +
    +if (goog.LOCALE == 'dav_KE' || goog.LOCALE == 'dav-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dav_KE;
    +}
    +
    +if (goog.LOCALE == 'de_BE' || goog.LOCALE == 'de-BE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_BE;
    +}
    +
    +if (goog.LOCALE == 'de_DE' || goog.LOCALE == 'de-DE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_DE;
    +}
    +
    +if (goog.LOCALE == 'de_LI' || goog.LOCALE == 'de-LI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_LI;
    +}
    +
    +if (goog.LOCALE == 'de_LU' || goog.LOCALE == 'de-LU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_de_LU;
    +}
    +
    +if (goog.LOCALE == 'dje') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dje;
    +}
    +
    +if (goog.LOCALE == 'dje_NE' || goog.LOCALE == 'dje-NE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dje_NE;
    +}
    +
    +if (goog.LOCALE == 'dsb') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dsb;
    +}
    +
    +if (goog.LOCALE == 'dsb_DE' || goog.LOCALE == 'dsb-DE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dsb_DE;
    +}
    +
    +if (goog.LOCALE == 'dua') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dua;
    +}
    +
    +if (goog.LOCALE == 'dua_CM' || goog.LOCALE == 'dua-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dua_CM;
    +}
    +
    +if (goog.LOCALE == 'dyo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dyo;
    +}
    +
    +if (goog.LOCALE == 'dyo_SN' || goog.LOCALE == 'dyo-SN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dyo_SN;
    +}
    +
    +if (goog.LOCALE == 'dz') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dz;
    +}
    +
    +if (goog.LOCALE == 'dz_BT' || goog.LOCALE == 'dz-BT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_dz_BT;
    +}
    +
    +if (goog.LOCALE == 'ebu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ebu;
    +}
    +
    +if (goog.LOCALE == 'ebu_KE' || goog.LOCALE == 'ebu-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ebu_KE;
    +}
    +
    +if (goog.LOCALE == 'ee') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_GH' || goog.LOCALE == 'ee-GH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ee_GH;
    +}
    +
    +if (goog.LOCALE == 'ee_TG' || goog.LOCALE == 'ee-TG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ee_TG;
    +}
    +
    +if (goog.LOCALE == 'el_CY' || goog.LOCALE == 'el-CY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_el_CY;
    +}
    +
    +if (goog.LOCALE == 'el_GR' || goog.LOCALE == 'el-GR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_el_GR;
    +}
    +
    +if (goog.LOCALE == 'en_001' || goog.LOCALE == 'en-001') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_001;
    +}
    +
    +if (goog.LOCALE == 'en_150' || goog.LOCALE == 'en-150') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_150;
    +}
    +
    +if (goog.LOCALE == 'en_AG' || goog.LOCALE == 'en-AG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_AG;
    +}
    +
    +if (goog.LOCALE == 'en_AI' || goog.LOCALE == 'en-AI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_AI;
    +}
    +
    +if (goog.LOCALE == 'en_AS' || goog.LOCALE == 'en-AS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_AS;
    +}
    +
    +if (goog.LOCALE == 'en_BB' || goog.LOCALE == 'en-BB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BB;
    +}
    +
    +if (goog.LOCALE == 'en_BE' || goog.LOCALE == 'en-BE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BE;
    +}
    +
    +if (goog.LOCALE == 'en_BM' || goog.LOCALE == 'en-BM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BM;
    +}
    +
    +if (goog.LOCALE == 'en_BS' || goog.LOCALE == 'en-BS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BS;
    +}
    +
    +if (goog.LOCALE == 'en_BW' || goog.LOCALE == 'en-BW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BW;
    +}
    +
    +if (goog.LOCALE == 'en_BZ' || goog.LOCALE == 'en-BZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_BZ;
    +}
    +
    +if (goog.LOCALE == 'en_CA' || goog.LOCALE == 'en-CA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_CA;
    +}
    +
    +if (goog.LOCALE == 'en_CC' || goog.LOCALE == 'en-CC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_CC;
    +}
    +
    +if (goog.LOCALE == 'en_CK' || goog.LOCALE == 'en-CK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_CK;
    +}
    +
    +if (goog.LOCALE == 'en_CM' || goog.LOCALE == 'en-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_CM;
    +}
    +
    +if (goog.LOCALE == 'en_CX' || goog.LOCALE == 'en-CX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_CX;
    +}
    +
    +if (goog.LOCALE == 'en_DG' || goog.LOCALE == 'en-DG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_DG;
    +}
    +
    +if (goog.LOCALE == 'en_DM' || goog.LOCALE == 'en-DM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_DM;
    +}
    +
    +if (goog.LOCALE == 'en_ER' || goog.LOCALE == 'en-ER') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_ER;
    +}
    +
    +if (goog.LOCALE == 'en_FJ' || goog.LOCALE == 'en-FJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_FJ;
    +}
    +
    +if (goog.LOCALE == 'en_FK' || goog.LOCALE == 'en-FK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_FK;
    +}
    +
    +if (goog.LOCALE == 'en_FM' || goog.LOCALE == 'en-FM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_FM;
    +}
    +
    +if (goog.LOCALE == 'en_GD' || goog.LOCALE == 'en-GD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GD;
    +}
    +
    +if (goog.LOCALE == 'en_GG' || goog.LOCALE == 'en-GG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GG;
    +}
    +
    +if (goog.LOCALE == 'en_GH' || goog.LOCALE == 'en-GH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GH;
    +}
    +
    +if (goog.LOCALE == 'en_GI' || goog.LOCALE == 'en-GI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GI;
    +}
    +
    +if (goog.LOCALE == 'en_GM' || goog.LOCALE == 'en-GM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GM;
    +}
    +
    +if (goog.LOCALE == 'en_GU' || goog.LOCALE == 'en-GU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GU;
    +}
    +
    +if (goog.LOCALE == 'en_GY' || goog.LOCALE == 'en-GY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_GY;
    +}
    +
    +if (goog.LOCALE == 'en_HK' || goog.LOCALE == 'en-HK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_HK;
    +}
    +
    +if (goog.LOCALE == 'en_IM' || goog.LOCALE == 'en-IM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_IM;
    +}
    +
    +if (goog.LOCALE == 'en_IO' || goog.LOCALE == 'en-IO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_IO;
    +}
    +
    +if (goog.LOCALE == 'en_JE' || goog.LOCALE == 'en-JE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_JE;
    +}
    +
    +if (goog.LOCALE == 'en_JM' || goog.LOCALE == 'en-JM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_JM;
    +}
    +
    +if (goog.LOCALE == 'en_KE' || goog.LOCALE == 'en-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_KE;
    +}
    +
    +if (goog.LOCALE == 'en_KI' || goog.LOCALE == 'en-KI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_KI;
    +}
    +
    +if (goog.LOCALE == 'en_KN' || goog.LOCALE == 'en-KN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_KN;
    +}
    +
    +if (goog.LOCALE == 'en_KY' || goog.LOCALE == 'en-KY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_KY;
    +}
    +
    +if (goog.LOCALE == 'en_LC' || goog.LOCALE == 'en-LC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_LC;
    +}
    +
    +if (goog.LOCALE == 'en_LR' || goog.LOCALE == 'en-LR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_LR;
    +}
    +
    +if (goog.LOCALE == 'en_LS' || goog.LOCALE == 'en-LS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_LS;
    +}
    +
    +if (goog.LOCALE == 'en_MG' || goog.LOCALE == 'en-MG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MG;
    +}
    +
    +if (goog.LOCALE == 'en_MH' || goog.LOCALE == 'en-MH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MH;
    +}
    +
    +if (goog.LOCALE == 'en_MO' || goog.LOCALE == 'en-MO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MO;
    +}
    +
    +if (goog.LOCALE == 'en_MP' || goog.LOCALE == 'en-MP') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MP;
    +}
    +
    +if (goog.LOCALE == 'en_MS' || goog.LOCALE == 'en-MS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MS;
    +}
    +
    +if (goog.LOCALE == 'en_MT' || goog.LOCALE == 'en-MT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MT;
    +}
    +
    +if (goog.LOCALE == 'en_MU' || goog.LOCALE == 'en-MU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MU;
    +}
    +
    +if (goog.LOCALE == 'en_MW' || goog.LOCALE == 'en-MW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MW;
    +}
    +
    +if (goog.LOCALE == 'en_MY' || goog.LOCALE == 'en-MY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_MY;
    +}
    +
    +if (goog.LOCALE == 'en_NA' || goog.LOCALE == 'en-NA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NA;
    +}
    +
    +if (goog.LOCALE == 'en_NF' || goog.LOCALE == 'en-NF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NF;
    +}
    +
    +if (goog.LOCALE == 'en_NG' || goog.LOCALE == 'en-NG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NG;
    +}
    +
    +if (goog.LOCALE == 'en_NR' || goog.LOCALE == 'en-NR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NR;
    +}
    +
    +if (goog.LOCALE == 'en_NU' || goog.LOCALE == 'en-NU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NU;
    +}
    +
    +if (goog.LOCALE == 'en_NZ' || goog.LOCALE == 'en-NZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_NZ;
    +}
    +
    +if (goog.LOCALE == 'en_PG' || goog.LOCALE == 'en-PG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PG;
    +}
    +
    +if (goog.LOCALE == 'en_PH' || goog.LOCALE == 'en-PH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PH;
    +}
    +
    +if (goog.LOCALE == 'en_PK' || goog.LOCALE == 'en-PK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PK;
    +}
    +
    +if (goog.LOCALE == 'en_PN' || goog.LOCALE == 'en-PN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PN;
    +}
    +
    +if (goog.LOCALE == 'en_PR' || goog.LOCALE == 'en-PR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PR;
    +}
    +
    +if (goog.LOCALE == 'en_PW' || goog.LOCALE == 'en-PW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_PW;
    +}
    +
    +if (goog.LOCALE == 'en_RW' || goog.LOCALE == 'en-RW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_RW;
    +}
    +
    +if (goog.LOCALE == 'en_SB' || goog.LOCALE == 'en-SB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SB;
    +}
    +
    +if (goog.LOCALE == 'en_SC' || goog.LOCALE == 'en-SC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SC;
    +}
    +
    +if (goog.LOCALE == 'en_SD' || goog.LOCALE == 'en-SD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SD;
    +}
    +
    +if (goog.LOCALE == 'en_SH' || goog.LOCALE == 'en-SH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SH;
    +}
    +
    +if (goog.LOCALE == 'en_SL' || goog.LOCALE == 'en-SL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SL;
    +}
    +
    +if (goog.LOCALE == 'en_SS' || goog.LOCALE == 'en-SS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SS;
    +}
    +
    +if (goog.LOCALE == 'en_SX' || goog.LOCALE == 'en-SX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SX;
    +}
    +
    +if (goog.LOCALE == 'en_SZ' || goog.LOCALE == 'en-SZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_SZ;
    +}
    +
    +if (goog.LOCALE == 'en_TC' || goog.LOCALE == 'en-TC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TC;
    +}
    +
    +if (goog.LOCALE == 'en_TK' || goog.LOCALE == 'en-TK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TK;
    +}
    +
    +if (goog.LOCALE == 'en_TO' || goog.LOCALE == 'en-TO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TO;
    +}
    +
    +if (goog.LOCALE == 'en_TT' || goog.LOCALE == 'en-TT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TT;
    +}
    +
    +if (goog.LOCALE == 'en_TV' || goog.LOCALE == 'en-TV') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TV;
    +}
    +
    +if (goog.LOCALE == 'en_TZ' || goog.LOCALE == 'en-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_TZ;
    +}
    +
    +if (goog.LOCALE == 'en_UG' || goog.LOCALE == 'en-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_UG;
    +}
    +
    +if (goog.LOCALE == 'en_UM' || goog.LOCALE == 'en-UM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_UM;
    +}
    +
    +if (goog.LOCALE == 'en_US_POSIX' || goog.LOCALE == 'en-US-POSIX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_US_POSIX;
    +}
    +
    +if (goog.LOCALE == 'en_VC' || goog.LOCALE == 'en-VC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_VC;
    +}
    +
    +if (goog.LOCALE == 'en_VG' || goog.LOCALE == 'en-VG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_VG;
    +}
    +
    +if (goog.LOCALE == 'en_VI' || goog.LOCALE == 'en-VI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_VI;
    +}
    +
    +if (goog.LOCALE == 'en_VU' || goog.LOCALE == 'en-VU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_VU;
    +}
    +
    +if (goog.LOCALE == 'en_WS' || goog.LOCALE == 'en-WS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_WS;
    +}
    +
    +if (goog.LOCALE == 'en_ZM' || goog.LOCALE == 'en-ZM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_ZM;
    +}
    +
    +if (goog.LOCALE == 'en_ZW' || goog.LOCALE == 'en-ZW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_en_ZW;
    +}
    +
    +if (goog.LOCALE == 'eo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_eo;
    +}
    +
    +if (goog.LOCALE == 'es_AR' || goog.LOCALE == 'es-AR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_AR;
    +}
    +
    +if (goog.LOCALE == 'es_BO' || goog.LOCALE == 'es-BO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_BO;
    +}
    +
    +if (goog.LOCALE == 'es_CL' || goog.LOCALE == 'es-CL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_CL;
    +}
    +
    +if (goog.LOCALE == 'es_CO' || goog.LOCALE == 'es-CO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_CO;
    +}
    +
    +if (goog.LOCALE == 'es_CR' || goog.LOCALE == 'es-CR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_CR;
    +}
    +
    +if (goog.LOCALE == 'es_CU' || goog.LOCALE == 'es-CU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_CU;
    +}
    +
    +if (goog.LOCALE == 'es_DO' || goog.LOCALE == 'es-DO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_DO;
    +}
    +
    +if (goog.LOCALE == 'es_EA' || goog.LOCALE == 'es-EA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_EA;
    +}
    +
    +if (goog.LOCALE == 'es_EC' || goog.LOCALE == 'es-EC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_EC;
    +}
    +
    +if (goog.LOCALE == 'es_GQ' || goog.LOCALE == 'es-GQ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_GQ;
    +}
    +
    +if (goog.LOCALE == 'es_GT' || goog.LOCALE == 'es-GT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_GT;
    +}
    +
    +if (goog.LOCALE == 'es_HN' || goog.LOCALE == 'es-HN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_HN;
    +}
    +
    +if (goog.LOCALE == 'es_IC' || goog.LOCALE == 'es-IC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_IC;
    +}
    +
    +if (goog.LOCALE == 'es_MX' || goog.LOCALE == 'es-MX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_MX;
    +}
    +
    +if (goog.LOCALE == 'es_NI' || goog.LOCALE == 'es-NI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_NI;
    +}
    +
    +if (goog.LOCALE == 'es_PA' || goog.LOCALE == 'es-PA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_PA;
    +}
    +
    +if (goog.LOCALE == 'es_PE' || goog.LOCALE == 'es-PE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_PE;
    +}
    +
    +if (goog.LOCALE == 'es_PH' || goog.LOCALE == 'es-PH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_PH;
    +}
    +
    +if (goog.LOCALE == 'es_PR' || goog.LOCALE == 'es-PR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_PR;
    +}
    +
    +if (goog.LOCALE == 'es_PY' || goog.LOCALE == 'es-PY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_PY;
    +}
    +
    +if (goog.LOCALE == 'es_SV' || goog.LOCALE == 'es-SV') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_SV;
    +}
    +
    +if (goog.LOCALE == 'es_US' || goog.LOCALE == 'es-US') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_US;
    +}
    +
    +if (goog.LOCALE == 'es_UY' || goog.LOCALE == 'es-UY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_UY;
    +}
    +
    +if (goog.LOCALE == 'es_VE' || goog.LOCALE == 'es-VE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_es_VE;
    +}
    +
    +if (goog.LOCALE == 'et_EE' || goog.LOCALE == 'et-EE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_et_EE;
    +}
    +
    +if (goog.LOCALE == 'eu_ES' || goog.LOCALE == 'eu-ES') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_eu_ES;
    +}
    +
    +if (goog.LOCALE == 'ewo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ewo;
    +}
    +
    +if (goog.LOCALE == 'ewo_CM' || goog.LOCALE == 'ewo-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ewo_CM;
    +}
    +
    +if (goog.LOCALE == 'fa_AF' || goog.LOCALE == 'fa-AF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fa_AF;
    +}
    +
    +if (goog.LOCALE == 'fa_IR' || goog.LOCALE == 'fa-IR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fa_IR;
    +}
    +
    +if (goog.LOCALE == 'ff') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_CM' || goog.LOCALE == 'ff-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ff_CM;
    +}
    +
    +if (goog.LOCALE == 'ff_GN' || goog.LOCALE == 'ff-GN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ff_GN;
    +}
    +
    +if (goog.LOCALE == 'ff_MR' || goog.LOCALE == 'ff-MR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ff_MR;
    +}
    +
    +if (goog.LOCALE == 'ff_SN' || goog.LOCALE == 'ff-SN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ff_SN;
    +}
    +
    +if (goog.LOCALE == 'fi_FI' || goog.LOCALE == 'fi-FI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fi_FI;
    +}
    +
    +if (goog.LOCALE == 'fil_PH' || goog.LOCALE == 'fil-PH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fil_PH;
    +}
    +
    +if (goog.LOCALE == 'fo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fo;
    +}
    +
    +if (goog.LOCALE == 'fo_FO' || goog.LOCALE == 'fo-FO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fo_FO;
    +}
    +
    +if (goog.LOCALE == 'fr_BE' || goog.LOCALE == 'fr-BE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_BE;
    +}
    +
    +if (goog.LOCALE == 'fr_BF' || goog.LOCALE == 'fr-BF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_BF;
    +}
    +
    +if (goog.LOCALE == 'fr_BI' || goog.LOCALE == 'fr-BI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_BI;
    +}
    +
    +if (goog.LOCALE == 'fr_BJ' || goog.LOCALE == 'fr-BJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_BJ;
    +}
    +
    +if (goog.LOCALE == 'fr_BL' || goog.LOCALE == 'fr-BL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_BL;
    +}
    +
    +if (goog.LOCALE == 'fr_CD' || goog.LOCALE == 'fr-CD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CD;
    +}
    +
    +if (goog.LOCALE == 'fr_CF' || goog.LOCALE == 'fr-CF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CF;
    +}
    +
    +if (goog.LOCALE == 'fr_CG' || goog.LOCALE == 'fr-CG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CG;
    +}
    +
    +if (goog.LOCALE == 'fr_CH' || goog.LOCALE == 'fr-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CH;
    +}
    +
    +if (goog.LOCALE == 'fr_CI' || goog.LOCALE == 'fr-CI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CI;
    +}
    +
    +if (goog.LOCALE == 'fr_CM' || goog.LOCALE == 'fr-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_CM;
    +}
    +
    +if (goog.LOCALE == 'fr_DJ' || goog.LOCALE == 'fr-DJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_DJ;
    +}
    +
    +if (goog.LOCALE == 'fr_DZ' || goog.LOCALE == 'fr-DZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_DZ;
    +}
    +
    +if (goog.LOCALE == 'fr_FR' || goog.LOCALE == 'fr-FR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_FR;
    +}
    +
    +if (goog.LOCALE == 'fr_GA' || goog.LOCALE == 'fr-GA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_GA;
    +}
    +
    +if (goog.LOCALE == 'fr_GF' || goog.LOCALE == 'fr-GF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_GF;
    +}
    +
    +if (goog.LOCALE == 'fr_GN' || goog.LOCALE == 'fr-GN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_GN;
    +}
    +
    +if (goog.LOCALE == 'fr_GP' || goog.LOCALE == 'fr-GP') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_GP;
    +}
    +
    +if (goog.LOCALE == 'fr_GQ' || goog.LOCALE == 'fr-GQ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_GQ;
    +}
    +
    +if (goog.LOCALE == 'fr_HT' || goog.LOCALE == 'fr-HT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_HT;
    +}
    +
    +if (goog.LOCALE == 'fr_KM' || goog.LOCALE == 'fr-KM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_KM;
    +}
    +
    +if (goog.LOCALE == 'fr_LU' || goog.LOCALE == 'fr-LU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_LU;
    +}
    +
    +if (goog.LOCALE == 'fr_MA' || goog.LOCALE == 'fr-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MA;
    +}
    +
    +if (goog.LOCALE == 'fr_MC' || goog.LOCALE == 'fr-MC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MC;
    +}
    +
    +if (goog.LOCALE == 'fr_MF' || goog.LOCALE == 'fr-MF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MF;
    +}
    +
    +if (goog.LOCALE == 'fr_MG' || goog.LOCALE == 'fr-MG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MG;
    +}
    +
    +if (goog.LOCALE == 'fr_ML' || goog.LOCALE == 'fr-ML') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_ML;
    +}
    +
    +if (goog.LOCALE == 'fr_MQ' || goog.LOCALE == 'fr-MQ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MQ;
    +}
    +
    +if (goog.LOCALE == 'fr_MR' || goog.LOCALE == 'fr-MR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MR;
    +}
    +
    +if (goog.LOCALE == 'fr_MU' || goog.LOCALE == 'fr-MU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_MU;
    +}
    +
    +if (goog.LOCALE == 'fr_NC' || goog.LOCALE == 'fr-NC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_NC;
    +}
    +
    +if (goog.LOCALE == 'fr_NE' || goog.LOCALE == 'fr-NE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_NE;
    +}
    +
    +if (goog.LOCALE == 'fr_PF' || goog.LOCALE == 'fr-PF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_PF;
    +}
    +
    +if (goog.LOCALE == 'fr_PM' || goog.LOCALE == 'fr-PM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_PM;
    +}
    +
    +if (goog.LOCALE == 'fr_RE' || goog.LOCALE == 'fr-RE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_RE;
    +}
    +
    +if (goog.LOCALE == 'fr_RW' || goog.LOCALE == 'fr-RW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_RW;
    +}
    +
    +if (goog.LOCALE == 'fr_SC' || goog.LOCALE == 'fr-SC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_SC;
    +}
    +
    +if (goog.LOCALE == 'fr_SN' || goog.LOCALE == 'fr-SN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_SN;
    +}
    +
    +if (goog.LOCALE == 'fr_SY' || goog.LOCALE == 'fr-SY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_SY;
    +}
    +
    +if (goog.LOCALE == 'fr_TD' || goog.LOCALE == 'fr-TD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_TD;
    +}
    +
    +if (goog.LOCALE == 'fr_TG' || goog.LOCALE == 'fr-TG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_TG;
    +}
    +
    +if (goog.LOCALE == 'fr_TN' || goog.LOCALE == 'fr-TN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_TN;
    +}
    +
    +if (goog.LOCALE == 'fr_VU' || goog.LOCALE == 'fr-VU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_VU;
    +}
    +
    +if (goog.LOCALE == 'fr_WF' || goog.LOCALE == 'fr-WF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_WF;
    +}
    +
    +if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fr_YT;
    +}
    +
    +if (goog.LOCALE == 'fur') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fur;
    +}
    +
    +if (goog.LOCALE == 'fur_IT' || goog.LOCALE == 'fur-IT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fur_IT;
    +}
    +
    +if (goog.LOCALE == 'fy') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fy;
    +}
    +
    +if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_fy_NL;
    +}
    +
    +if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ga_IE;
    +}
    +
    +if (goog.LOCALE == 'gd') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gd;
    +}
    +
    +if (goog.LOCALE == 'gd_GB' || goog.LOCALE == 'gd-GB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gd_GB;
    +}
    +
    +if (goog.LOCALE == 'gl_ES' || goog.LOCALE == 'gl-ES') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gl_ES;
    +}
    +
    +if (goog.LOCALE == 'gsw_CH' || goog.LOCALE == 'gsw-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gsw_CH;
    +}
    +
    +if (goog.LOCALE == 'gsw_FR' || goog.LOCALE == 'gsw-FR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gsw_FR;
    +}
    +
    +if (goog.LOCALE == 'gsw_LI' || goog.LOCALE == 'gsw-LI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gsw_LI;
    +}
    +
    +if (goog.LOCALE == 'gu_IN' || goog.LOCALE == 'gu-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gu_IN;
    +}
    +
    +if (goog.LOCALE == 'guz') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_guz;
    +}
    +
    +if (goog.LOCALE == 'guz_KE' || goog.LOCALE == 'guz-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_guz_KE;
    +}
    +
    +if (goog.LOCALE == 'gv') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gv;
    +}
    +
    +if (goog.LOCALE == 'gv_IM' || goog.LOCALE == 'gv-IM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_gv_IM;
    +}
    +
    +if (goog.LOCALE == 'ha') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn' || goog.LOCALE == 'ha-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ha_Latn;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_GH' || goog.LOCALE == 'ha-Latn-GH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ha_Latn_GH;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NE' || goog.LOCALE == 'ha-Latn-NE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ha_Latn_NE;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NG' || goog.LOCALE == 'ha-Latn-NG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ha_Latn_NG;
    +}
    +
    +if (goog.LOCALE == 'haw_US' || goog.LOCALE == 'haw-US') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_haw_US;
    +}
    +
    +if (goog.LOCALE == 'he_IL' || goog.LOCALE == 'he-IL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_he_IL;
    +}
    +
    +if (goog.LOCALE == 'hi_IN' || goog.LOCALE == 'hi-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hi_IN;
    +}
    +
    +if (goog.LOCALE == 'hr_BA' || goog.LOCALE == 'hr-BA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hr_BA;
    +}
    +
    +if (goog.LOCALE == 'hr_HR' || goog.LOCALE == 'hr-HR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hr_HR;
    +}
    +
    +if (goog.LOCALE == 'hsb') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hsb;
    +}
    +
    +if (goog.LOCALE == 'hsb_DE' || goog.LOCALE == 'hsb-DE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hsb_DE;
    +}
    +
    +if (goog.LOCALE == 'hu_HU' || goog.LOCALE == 'hu-HU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hu_HU;
    +}
    +
    +if (goog.LOCALE == 'hy_AM' || goog.LOCALE == 'hy-AM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_hy_AM;
    +}
    +
    +if (goog.LOCALE == 'id_ID' || goog.LOCALE == 'id-ID') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_id_ID;
    +}
    +
    +if (goog.LOCALE == 'ig') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ig;
    +}
    +
    +if (goog.LOCALE == 'ig_NG' || goog.LOCALE == 'ig-NG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ig_NG;
    +}
    +
    +if (goog.LOCALE == 'ii') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ii;
    +}
    +
    +if (goog.LOCALE == 'ii_CN' || goog.LOCALE == 'ii-CN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ii_CN;
    +}
    +
    +if (goog.LOCALE == 'is_IS' || goog.LOCALE == 'is-IS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_is_IS;
    +}
    +
    +if (goog.LOCALE == 'it_CH' || goog.LOCALE == 'it-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_it_CH;
    +}
    +
    +if (goog.LOCALE == 'it_IT' || goog.LOCALE == 'it-IT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_it_IT;
    +}
    +
    +if (goog.LOCALE == 'it_SM' || goog.LOCALE == 'it-SM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_it_SM;
    +}
    +
    +if (goog.LOCALE == 'ja_JP' || goog.LOCALE == 'ja-JP') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ja_JP;
    +}
    +
    +if (goog.LOCALE == 'jgo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_jgo;
    +}
    +
    +if (goog.LOCALE == 'jgo_CM' || goog.LOCALE == 'jgo-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_jgo_CM;
    +}
    +
    +if (goog.LOCALE == 'jmc') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_jmc;
    +}
    +
    +if (goog.LOCALE == 'jmc_TZ' || goog.LOCALE == 'jmc-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_jmc_TZ;
    +}
    +
    +if (goog.LOCALE == 'ka_GE' || goog.LOCALE == 'ka-GE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ka_GE;
    +}
    +
    +if (goog.LOCALE == 'kab') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kab;
    +}
    +
    +if (goog.LOCALE == 'kab_DZ' || goog.LOCALE == 'kab-DZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kab_DZ;
    +}
    +
    +if (goog.LOCALE == 'kam') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kam;
    +}
    +
    +if (goog.LOCALE == 'kam_KE' || goog.LOCALE == 'kam-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kam_KE;
    +}
    +
    +if (goog.LOCALE == 'kde') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kde;
    +}
    +
    +if (goog.LOCALE == 'kde_TZ' || goog.LOCALE == 'kde-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kde_TZ;
    +}
    +
    +if (goog.LOCALE == 'kea') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kea;
    +}
    +
    +if (goog.LOCALE == 'kea_CV' || goog.LOCALE == 'kea-CV') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kea_CV;
    +}
    +
    +if (goog.LOCALE == 'khq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_khq;
    +}
    +
    +if (goog.LOCALE == 'khq_ML' || goog.LOCALE == 'khq-ML') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_khq_ML;
    +}
    +
    +if (goog.LOCALE == 'ki') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ki;
    +}
    +
    +if (goog.LOCALE == 'ki_KE' || goog.LOCALE == 'ki-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ki_KE;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl' || goog.LOCALE == 'kk-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl_KZ' || goog.LOCALE == 'kk-Cyrl-KZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kk_Cyrl_KZ;
    +}
    +
    +if (goog.LOCALE == 'kkj') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kkj;
    +}
    +
    +if (goog.LOCALE == 'kkj_CM' || goog.LOCALE == 'kkj-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kkj_CM;
    +}
    +
    +if (goog.LOCALE == 'kl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kl;
    +}
    +
    +if (goog.LOCALE == 'kl_GL' || goog.LOCALE == 'kl-GL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kl_GL;
    +}
    +
    +if (goog.LOCALE == 'kln') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kln;
    +}
    +
    +if (goog.LOCALE == 'kln_KE' || goog.LOCALE == 'kln-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kln_KE;
    +}
    +
    +if (goog.LOCALE == 'km_KH' || goog.LOCALE == 'km-KH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_km_KH;
    +}
    +
    +if (goog.LOCALE == 'kn_IN' || goog.LOCALE == 'kn-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kn_IN;
    +}
    +
    +if (goog.LOCALE == 'ko_KP' || goog.LOCALE == 'ko-KP') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ko_KP;
    +}
    +
    +if (goog.LOCALE == 'ko_KR' || goog.LOCALE == 'ko-KR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ko_KR;
    +}
    +
    +if (goog.LOCALE == 'kok') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kok;
    +}
    +
    +if (goog.LOCALE == 'kok_IN' || goog.LOCALE == 'kok-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kok_IN;
    +}
    +
    +if (goog.LOCALE == 'ks') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab' || goog.LOCALE == 'ks-Arab') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ks_Arab;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab_IN' || goog.LOCALE == 'ks-Arab-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ks_Arab_IN;
    +}
    +
    +if (goog.LOCALE == 'ksb') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksb_TZ' || goog.LOCALE == 'ksb-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksb_TZ;
    +}
    +
    +if (goog.LOCALE == 'ksf') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksf_CM' || goog.LOCALE == 'ksf-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksf_CM;
    +}
    +
    +if (goog.LOCALE == 'ksh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksh;
    +}
    +
    +if (goog.LOCALE == 'ksh_DE' || goog.LOCALE == 'ksh-DE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ksh_DE;
    +}
    +
    +if (goog.LOCALE == 'kw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kw;
    +}
    +
    +if (goog.LOCALE == 'kw_GB' || goog.LOCALE == 'kw-GB') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_kw_GB;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl' || goog.LOCALE == 'ky-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl_KG' || goog.LOCALE == 'ky-Cyrl-KG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ky_Cyrl_KG;
    +}
    +
    +if (goog.LOCALE == 'lag') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lag;
    +}
    +
    +if (goog.LOCALE == 'lag_TZ' || goog.LOCALE == 'lag-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lag_TZ;
    +}
    +
    +if (goog.LOCALE == 'lb') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lb;
    +}
    +
    +if (goog.LOCALE == 'lb_LU' || goog.LOCALE == 'lb-LU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lb_LU;
    +}
    +
    +if (goog.LOCALE == 'lg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lg;
    +}
    +
    +if (goog.LOCALE == 'lg_UG' || goog.LOCALE == 'lg-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lg_UG;
    +}
    +
    +if (goog.LOCALE == 'lkt') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lkt;
    +}
    +
    +if (goog.LOCALE == 'lkt_US' || goog.LOCALE == 'lkt-US') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lkt_US;
    +}
    +
    +if (goog.LOCALE == 'ln_AO' || goog.LOCALE == 'ln-AO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ln_AO;
    +}
    +
    +if (goog.LOCALE == 'ln_CD' || goog.LOCALE == 'ln-CD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ln_CD;
    +}
    +
    +if (goog.LOCALE == 'ln_CF' || goog.LOCALE == 'ln-CF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ln_CF;
    +}
    +
    +if (goog.LOCALE == 'ln_CG' || goog.LOCALE == 'ln-CG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ln_CG;
    +}
    +
    +if (goog.LOCALE == 'lo_LA' || goog.LOCALE == 'lo-LA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lo_LA;
    +}
    +
    +if (goog.LOCALE == 'lt_LT' || goog.LOCALE == 'lt-LT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lt_LT;
    +}
    +
    +if (goog.LOCALE == 'lu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lu;
    +}
    +
    +if (goog.LOCALE == 'lu_CD' || goog.LOCALE == 'lu-CD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lu_CD;
    +}
    +
    +if (goog.LOCALE == 'luo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_luo;
    +}
    +
    +if (goog.LOCALE == 'luo_KE' || goog.LOCALE == 'luo-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_luo_KE;
    +}
    +
    +if (goog.LOCALE == 'luy') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_luy;
    +}
    +
    +if (goog.LOCALE == 'luy_KE' || goog.LOCALE == 'luy-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_luy_KE;
    +}
    +
    +if (goog.LOCALE == 'lv_LV' || goog.LOCALE == 'lv-LV') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_lv_LV;
    +}
    +
    +if (goog.LOCALE == 'mas') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_KE' || goog.LOCALE == 'mas-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mas_KE;
    +}
    +
    +if (goog.LOCALE == 'mas_TZ' || goog.LOCALE == 'mas-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mas_TZ;
    +}
    +
    +if (goog.LOCALE == 'mer') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mer;
    +}
    +
    +if (goog.LOCALE == 'mer_KE' || goog.LOCALE == 'mer-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mer_KE;
    +}
    +
    +if (goog.LOCALE == 'mfe') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mfe;
    +}
    +
    +if (goog.LOCALE == 'mfe_MU' || goog.LOCALE == 'mfe-MU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mfe_MU;
    +}
    +
    +if (goog.LOCALE == 'mg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mg;
    +}
    +
    +if (goog.LOCALE == 'mg_MG' || goog.LOCALE == 'mg-MG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mg_MG;
    +}
    +
    +if (goog.LOCALE == 'mgh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgh_MZ' || goog.LOCALE == 'mgh-MZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mgh_MZ;
    +}
    +
    +if (goog.LOCALE == 'mgo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mgo;
    +}
    +
    +if (goog.LOCALE == 'mgo_CM' || goog.LOCALE == 'mgo-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mgo_CM;
    +}
    +
    +if (goog.LOCALE == 'mk_MK' || goog.LOCALE == 'mk-MK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mk_MK;
    +}
    +
    +if (goog.LOCALE == 'ml_IN' || goog.LOCALE == 'ml-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ml_IN;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl' || goog.LOCALE == 'mn-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl_MN' || goog.LOCALE == 'mn-Cyrl-MN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mn_Cyrl_MN;
    +}
    +
    +if (goog.LOCALE == 'mr_IN' || goog.LOCALE == 'mr-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mr_IN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn' || goog.LOCALE == 'ms-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_BN' || goog.LOCALE == 'ms-Latn-BN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ms_Latn_BN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_MY' || goog.LOCALE == 'ms-Latn-MY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ms_Latn_MY;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_SG' || goog.LOCALE == 'ms-Latn-SG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ms_Latn_SG;
    +}
    +
    +if (goog.LOCALE == 'mt_MT' || goog.LOCALE == 'mt-MT') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mt_MT;
    +}
    +
    +if (goog.LOCALE == 'mua') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mua;
    +}
    +
    +if (goog.LOCALE == 'mua_CM' || goog.LOCALE == 'mua-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_mua_CM;
    +}
    +
    +if (goog.LOCALE == 'my_MM' || goog.LOCALE == 'my-MM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_my_MM;
    +}
    +
    +if (goog.LOCALE == 'naq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_naq;
    +}
    +
    +if (goog.LOCALE == 'naq_NA' || goog.LOCALE == 'naq-NA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_naq_NA;
    +}
    +
    +if (goog.LOCALE == 'nb_NO' || goog.LOCALE == 'nb-NO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nb_NO;
    +}
    +
    +if (goog.LOCALE == 'nb_SJ' || goog.LOCALE == 'nb-SJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nb_SJ;
    +}
    +
    +if (goog.LOCALE == 'nd') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nd;
    +}
    +
    +if (goog.LOCALE == 'nd_ZW' || goog.LOCALE == 'nd-ZW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nd_ZW;
    +}
    +
    +if (goog.LOCALE == 'ne_IN' || goog.LOCALE == 'ne-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ne_IN;
    +}
    +
    +if (goog.LOCALE == 'ne_NP' || goog.LOCALE == 'ne-NP') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ne_NP;
    +}
    +
    +if (goog.LOCALE == 'nl_AW' || goog.LOCALE == 'nl-AW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_AW;
    +}
    +
    +if (goog.LOCALE == 'nl_BE' || goog.LOCALE == 'nl-BE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_BE;
    +}
    +
    +if (goog.LOCALE == 'nl_BQ' || goog.LOCALE == 'nl-BQ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_BQ;
    +}
    +
    +if (goog.LOCALE == 'nl_CW' || goog.LOCALE == 'nl-CW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_CW;
    +}
    +
    +if (goog.LOCALE == 'nl_NL' || goog.LOCALE == 'nl-NL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_NL;
    +}
    +
    +if (goog.LOCALE == 'nl_SR' || goog.LOCALE == 'nl-SR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_SR;
    +}
    +
    +if (goog.LOCALE == 'nl_SX' || goog.LOCALE == 'nl-SX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nl_SX;
    +}
    +
    +if (goog.LOCALE == 'nmg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nmg;
    +}
    +
    +if (goog.LOCALE == 'nmg_CM' || goog.LOCALE == 'nmg-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nmg_CM;
    +}
    +
    +if (goog.LOCALE == 'nn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nn;
    +}
    +
    +if (goog.LOCALE == 'nn_NO' || goog.LOCALE == 'nn-NO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nn_NO;
    +}
    +
    +if (goog.LOCALE == 'nnh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nnh;
    +}
    +
    +if (goog.LOCALE == 'nnh_CM' || goog.LOCALE == 'nnh-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nnh_CM;
    +}
    +
    +if (goog.LOCALE == 'nus') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nus;
    +}
    +
    +if (goog.LOCALE == 'nus_SD' || goog.LOCALE == 'nus-SD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nus_SD;
    +}
    +
    +if (goog.LOCALE == 'nyn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nyn;
    +}
    +
    +if (goog.LOCALE == 'nyn_UG' || goog.LOCALE == 'nyn-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_nyn_UG;
    +}
    +
    +if (goog.LOCALE == 'om') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_om;
    +}
    +
    +if (goog.LOCALE == 'om_ET' || goog.LOCALE == 'om-ET') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_om_ET;
    +}
    +
    +if (goog.LOCALE == 'om_KE' || goog.LOCALE == 'om-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_om_KE;
    +}
    +
    +if (goog.LOCALE == 'or_IN' || goog.LOCALE == 'or-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_or_IN;
    +}
    +
    +if (goog.LOCALE == 'os') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_os;
    +}
    +
    +if (goog.LOCALE == 'os_GE' || goog.LOCALE == 'os-GE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_os_GE;
    +}
    +
    +if (goog.LOCALE == 'os_RU' || goog.LOCALE == 'os-RU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_os_RU;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab' || goog.LOCALE == 'pa-Arab') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab_PK' || goog.LOCALE == 'pa-Arab-PK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pa_Arab_PK;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru' || goog.LOCALE == 'pa-Guru') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru_IN' || goog.LOCALE == 'pa-Guru-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pa_Guru_IN;
    +}
    +
    +if (goog.LOCALE == 'pl_PL' || goog.LOCALE == 'pl-PL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pl_PL;
    +}
    +
    +if (goog.LOCALE == 'ps') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ps;
    +}
    +
    +if (goog.LOCALE == 'ps_AF' || goog.LOCALE == 'ps-AF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ps_AF;
    +}
    +
    +if (goog.LOCALE == 'pt_AO' || goog.LOCALE == 'pt-AO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_AO;
    +}
    +
    +if (goog.LOCALE == 'pt_CV' || goog.LOCALE == 'pt-CV') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_CV;
    +}
    +
    +if (goog.LOCALE == 'pt_GW' || goog.LOCALE == 'pt-GW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_GW;
    +}
    +
    +if (goog.LOCALE == 'pt_MO' || goog.LOCALE == 'pt-MO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_MO;
    +}
    +
    +if (goog.LOCALE == 'pt_MZ' || goog.LOCALE == 'pt-MZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_MZ;
    +}
    +
    +if (goog.LOCALE == 'pt_ST' || goog.LOCALE == 'pt-ST') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_ST;
    +}
    +
    +if (goog.LOCALE == 'pt_TL' || goog.LOCALE == 'pt-TL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_pt_TL;
    +}
    +
    +if (goog.LOCALE == 'qu') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_BO' || goog.LOCALE == 'qu-BO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_qu_BO;
    +}
    +
    +if (goog.LOCALE == 'qu_EC' || goog.LOCALE == 'qu-EC') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_qu_EC;
    +}
    +
    +if (goog.LOCALE == 'qu_PE' || goog.LOCALE == 'qu-PE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_qu_PE;
    +}
    +
    +if (goog.LOCALE == 'rm') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rm;
    +}
    +
    +if (goog.LOCALE == 'rm_CH' || goog.LOCALE == 'rm-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rm_CH;
    +}
    +
    +if (goog.LOCALE == 'rn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rn;
    +}
    +
    +if (goog.LOCALE == 'rn_BI' || goog.LOCALE == 'rn-BI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rn_BI;
    +}
    +
    +if (goog.LOCALE == 'ro_MD' || goog.LOCALE == 'ro-MD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ro_MD;
    +}
    +
    +if (goog.LOCALE == 'ro_RO' || goog.LOCALE == 'ro-RO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ro_RO;
    +}
    +
    +if (goog.LOCALE == 'rof') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rof;
    +}
    +
    +if (goog.LOCALE == 'rof_TZ' || goog.LOCALE == 'rof-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rof_TZ;
    +}
    +
    +if (goog.LOCALE == 'ru_BY' || goog.LOCALE == 'ru-BY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_BY;
    +}
    +
    +if (goog.LOCALE == 'ru_KG' || goog.LOCALE == 'ru-KG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_KG;
    +}
    +
    +if (goog.LOCALE == 'ru_KZ' || goog.LOCALE == 'ru-KZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_KZ;
    +}
    +
    +if (goog.LOCALE == 'ru_MD' || goog.LOCALE == 'ru-MD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_MD;
    +}
    +
    +if (goog.LOCALE == 'ru_RU' || goog.LOCALE == 'ru-RU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_RU;
    +}
    +
    +if (goog.LOCALE == 'ru_UA' || goog.LOCALE == 'ru-UA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ru_UA;
    +}
    +
    +if (goog.LOCALE == 'rw') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rw;
    +}
    +
    +if (goog.LOCALE == 'rw_RW' || goog.LOCALE == 'rw-RW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rw_RW;
    +}
    +
    +if (goog.LOCALE == 'rwk') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rwk;
    +}
    +
    +if (goog.LOCALE == 'rwk_TZ' || goog.LOCALE == 'rwk-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_rwk_TZ;
    +}
    +
    +if (goog.LOCALE == 'sah') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sah;
    +}
    +
    +if (goog.LOCALE == 'sah_RU' || goog.LOCALE == 'sah-RU') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sah_RU;
    +}
    +
    +if (goog.LOCALE == 'saq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_saq;
    +}
    +
    +if (goog.LOCALE == 'saq_KE' || goog.LOCALE == 'saq-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_saq_KE;
    +}
    +
    +if (goog.LOCALE == 'sbp') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sbp;
    +}
    +
    +if (goog.LOCALE == 'sbp_TZ' || goog.LOCALE == 'sbp-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sbp_TZ;
    +}
    +
    +if (goog.LOCALE == 'se') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_se;
    +}
    +
    +if (goog.LOCALE == 'se_FI' || goog.LOCALE == 'se-FI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_se_FI;
    +}
    +
    +if (goog.LOCALE == 'se_NO' || goog.LOCALE == 'se-NO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_se_NO;
    +}
    +
    +if (goog.LOCALE == 'se_SE' || goog.LOCALE == 'se-SE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_se_SE;
    +}
    +
    +if (goog.LOCALE == 'seh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_seh;
    +}
    +
    +if (goog.LOCALE == 'seh_MZ' || goog.LOCALE == 'seh-MZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_seh_MZ;
    +}
    +
    +if (goog.LOCALE == 'ses') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ses;
    +}
    +
    +if (goog.LOCALE == 'ses_ML' || goog.LOCALE == 'ses-ML') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ses_ML;
    +}
    +
    +if (goog.LOCALE == 'sg') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sg;
    +}
    +
    +if (goog.LOCALE == 'sg_CF' || goog.LOCALE == 'sg-CF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sg_CF;
    +}
    +
    +if (goog.LOCALE == 'shi') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn' || goog.LOCALE == 'shi-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn_MA' || goog.LOCALE == 'shi-Latn-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_shi_Latn_MA;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng' || goog.LOCALE == 'shi-Tfng') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_shi_Tfng;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng_MA' || goog.LOCALE == 'shi-Tfng-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_shi_Tfng_MA;
    +}
    +
    +if (goog.LOCALE == 'si_LK' || goog.LOCALE == 'si-LK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_si_LK;
    +}
    +
    +if (goog.LOCALE == 'sk_SK' || goog.LOCALE == 'sk-SK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sk_SK;
    +}
    +
    +if (goog.LOCALE == 'sl_SI' || goog.LOCALE == 'sl-SI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sl_SI;
    +}
    +
    +if (goog.LOCALE == 'smn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_smn;
    +}
    +
    +if (goog.LOCALE == 'smn_FI' || goog.LOCALE == 'smn-FI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_smn_FI;
    +}
    +
    +if (goog.LOCALE == 'sn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sn;
    +}
    +
    +if (goog.LOCALE == 'sn_ZW' || goog.LOCALE == 'sn-ZW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sn_ZW;
    +}
    +
    +if (goog.LOCALE == 'so') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_so;
    +}
    +
    +if (goog.LOCALE == 'so_DJ' || goog.LOCALE == 'so-DJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_so_DJ;
    +}
    +
    +if (goog.LOCALE == 'so_ET' || goog.LOCALE == 'so-ET') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_so_ET;
    +}
    +
    +if (goog.LOCALE == 'so_KE' || goog.LOCALE == 'so-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_so_KE;
    +}
    +
    +if (goog.LOCALE == 'so_SO' || goog.LOCALE == 'so-SO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_so_SO;
    +}
    +
    +if (goog.LOCALE == 'sq_AL' || goog.LOCALE == 'sq-AL') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sq_AL;
    +}
    +
    +if (goog.LOCALE == 'sq_MK' || goog.LOCALE == 'sq-MK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sq_MK;
    +}
    +
    +if (goog.LOCALE == 'sq_XK' || goog.LOCALE == 'sq-XK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sq_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl' || goog.LOCALE == 'sr-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_BA' || goog.LOCALE == 'sr-Cyrl-BA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_ME' || goog.LOCALE == 'sr-Cyrl-ME') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Cyrl_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_RS' || goog.LOCALE == 'sr-Cyrl-RS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Cyrl_RS;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_XK' || goog.LOCALE == 'sr-Cyrl-XK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Cyrl_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn' || goog.LOCALE == 'sr-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_BA' || goog.LOCALE == 'sr-Latn-BA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_ME' || goog.LOCALE == 'sr-Latn-ME') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Latn_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_RS' || goog.LOCALE == 'sr-Latn-RS') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Latn_RS;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_XK' || goog.LOCALE == 'sr-Latn-XK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sr_Latn_XK;
    +}
    +
    +if (goog.LOCALE == 'sv_AX' || goog.LOCALE == 'sv-AX') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sv_AX;
    +}
    +
    +if (goog.LOCALE == 'sv_FI' || goog.LOCALE == 'sv-FI') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sv_FI;
    +}
    +
    +if (goog.LOCALE == 'sv_SE' || goog.LOCALE == 'sv-SE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sv_SE;
    +}
    +
    +if (goog.LOCALE == 'sw_KE' || goog.LOCALE == 'sw-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sw_KE;
    +}
    +
    +if (goog.LOCALE == 'sw_TZ' || goog.LOCALE == 'sw-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sw_TZ;
    +}
    +
    +if (goog.LOCALE == 'sw_UG' || goog.LOCALE == 'sw-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_sw_UG;
    +}
    +
    +if (goog.LOCALE == 'swc') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_swc;
    +}
    +
    +if (goog.LOCALE == 'swc_CD' || goog.LOCALE == 'swc-CD') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_swc_CD;
    +}
    +
    +if (goog.LOCALE == 'ta_IN' || goog.LOCALE == 'ta-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ta_IN;
    +}
    +
    +if (goog.LOCALE == 'ta_LK' || goog.LOCALE == 'ta-LK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ta_LK;
    +}
    +
    +if (goog.LOCALE == 'ta_MY' || goog.LOCALE == 'ta-MY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ta_MY;
    +}
    +
    +if (goog.LOCALE == 'ta_SG' || goog.LOCALE == 'ta-SG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ta_SG;
    +}
    +
    +if (goog.LOCALE == 'te_IN' || goog.LOCALE == 'te-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_te_IN;
    +}
    +
    +if (goog.LOCALE == 'teo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_KE' || goog.LOCALE == 'teo-KE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_teo_KE;
    +}
    +
    +if (goog.LOCALE == 'teo_UG' || goog.LOCALE == 'teo-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_teo_UG;
    +}
    +
    +if (goog.LOCALE == 'th_TH' || goog.LOCALE == 'th-TH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_th_TH;
    +}
    +
    +if (goog.LOCALE == 'ti') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ti;
    +}
    +
    +if (goog.LOCALE == 'ti_ER' || goog.LOCALE == 'ti-ER') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ti_ER;
    +}
    +
    +if (goog.LOCALE == 'ti_ET' || goog.LOCALE == 'ti-ET') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ti_ET;
    +}
    +
    +if (goog.LOCALE == 'to') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_to;
    +}
    +
    +if (goog.LOCALE == 'to_TO' || goog.LOCALE == 'to-TO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_to_TO;
    +}
    +
    +if (goog.LOCALE == 'tr_CY' || goog.LOCALE == 'tr-CY') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tr_CY;
    +}
    +
    +if (goog.LOCALE == 'tr_TR' || goog.LOCALE == 'tr-TR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tr_TR;
    +}
    +
    +if (goog.LOCALE == 'twq') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_twq;
    +}
    +
    +if (goog.LOCALE == 'twq_NE' || goog.LOCALE == 'twq-NE') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_twq_NE;
    +}
    +
    +if (goog.LOCALE == 'tzm') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn' || goog.LOCALE == 'tzm-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tzm_Latn;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn_MA' || goog.LOCALE == 'tzm-Latn-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_tzm_Latn_MA;
    +}
    +
    +if (goog.LOCALE == 'ug') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab' || goog.LOCALE == 'ug-Arab') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ug_Arab;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab_CN' || goog.LOCALE == 'ug-Arab-CN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ug_Arab_CN;
    +}
    +
    +if (goog.LOCALE == 'uk_UA' || goog.LOCALE == 'uk-UA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uk_UA;
    +}
    +
    +if (goog.LOCALE == 'ur_IN' || goog.LOCALE == 'ur-IN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ur_IN;
    +}
    +
    +if (goog.LOCALE == 'ur_PK' || goog.LOCALE == 'ur-PK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_ur_PK;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab' || goog.LOCALE == 'uz-Arab') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab_AF' || goog.LOCALE == 'uz-Arab-AF') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Arab_AF;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl' || goog.LOCALE == 'uz-Cyrl') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl_UZ' || goog.LOCALE == 'uz-Cyrl-UZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Cyrl_UZ;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn' || goog.LOCALE == 'uz-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn_UZ' || goog.LOCALE == 'uz-Latn-UZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_uz_Latn_UZ;
    +}
    +
    +if (goog.LOCALE == 'vai') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn' || goog.LOCALE == 'vai-Latn') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn_LR' || goog.LOCALE == 'vai-Latn-LR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vai_Latn_LR;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii' || goog.LOCALE == 'vai-Vaii') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vai_Vaii;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii_LR' || goog.LOCALE == 'vai-Vaii-LR') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vai_Vaii_LR;
    +}
    +
    +if (goog.LOCALE == 'vi_VN' || goog.LOCALE == 'vi-VN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vi_VN;
    +}
    +
    +if (goog.LOCALE == 'vun') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vun;
    +}
    +
    +if (goog.LOCALE == 'vun_TZ' || goog.LOCALE == 'vun-TZ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_vun_TZ;
    +}
    +
    +if (goog.LOCALE == 'wae') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_wae;
    +}
    +
    +if (goog.LOCALE == 'wae_CH' || goog.LOCALE == 'wae-CH') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_wae_CH;
    +}
    +
    +if (goog.LOCALE == 'xog') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_xog;
    +}
    +
    +if (goog.LOCALE == 'xog_UG' || goog.LOCALE == 'xog-UG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_xog_UG;
    +}
    +
    +if (goog.LOCALE == 'yav') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yav;
    +}
    +
    +if (goog.LOCALE == 'yav_CM' || goog.LOCALE == 'yav-CM') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yav_CM;
    +}
    +
    +if (goog.LOCALE == 'yi') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yi;
    +}
    +
    +if (goog.LOCALE == 'yi_001' || goog.LOCALE == 'yi-001') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yi_001;
    +}
    +
    +if (goog.LOCALE == 'yo') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yo;
    +}
    +
    +if (goog.LOCALE == 'yo_BJ' || goog.LOCALE == 'yo-BJ') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yo_BJ;
    +}
    +
    +if (goog.LOCALE == 'yo_NG' || goog.LOCALE == 'yo-NG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_yo_NG;
    +}
    +
    +if (goog.LOCALE == 'zgh') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zgh;
    +}
    +
    +if (goog.LOCALE == 'zgh_MA' || goog.LOCALE == 'zgh-MA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zgh_MA;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans' || goog.LOCALE == 'zh-Hans') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_CN' || goog.LOCALE == 'zh-Hans-CN') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hans_CN;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_HK' || goog.LOCALE == 'zh-Hans-HK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hans_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_MO' || goog.LOCALE == 'zh-Hans-MO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hans_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_SG' || goog.LOCALE == 'zh-Hans-SG') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hans_SG;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant' || goog.LOCALE == 'zh-Hant') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_HK' || goog.LOCALE == 'zh-Hant-HK') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hant_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_MO' || goog.LOCALE == 'zh-Hant-MO') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hant_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_TW' || goog.LOCALE == 'zh-Hant-TW') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zh_Hant_TW;
    +}
    +
    +if (goog.LOCALE == 'zu_ZA' || goog.LOCALE == 'zu-ZA') {
    +  goog.i18n.DateTimePatterns = goog.i18n.DateTimePatterns_zu_ZA;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbols.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbols.js
    new file mode 100644
    index 0000000000..ad299351e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbols.js
    @@ -0,0 +1,4530 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Date/time formatting symbols for all locales.
    + *
    + * This file is autogenerated by script.  See
    + * http://go/generate_datetime_constants.py using --for_closure
    + * File generated from CLDR ver. 26
    + *
    + * To reduce the file size (which may cause issues in some JS
    + * developing environments), this file will only contain locales
    + * that are usually supported by google products. It is a super
    + * set of 40 languages. Rest of the data can be found in another file
    + * named "datetimesymbolsext.js", which will be generated at the same
    + * time as this file.
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could correct CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.i18n.DateTimeSymbols');
    +goog.provide('goog.i18n.DateTimeSymbols_af');
    +goog.provide('goog.i18n.DateTimeSymbols_am');
    +goog.provide('goog.i18n.DateTimeSymbols_ar');
    +goog.provide('goog.i18n.DateTimeSymbols_az');
    +goog.provide('goog.i18n.DateTimeSymbols_bg');
    +goog.provide('goog.i18n.DateTimeSymbols_bn');
    +goog.provide('goog.i18n.DateTimeSymbols_br');
    +goog.provide('goog.i18n.DateTimeSymbols_ca');
    +goog.provide('goog.i18n.DateTimeSymbols_chr');
    +goog.provide('goog.i18n.DateTimeSymbols_cs');
    +goog.provide('goog.i18n.DateTimeSymbols_cy');
    +goog.provide('goog.i18n.DateTimeSymbols_da');
    +goog.provide('goog.i18n.DateTimeSymbols_de');
    +goog.provide('goog.i18n.DateTimeSymbols_de_AT');
    +goog.provide('goog.i18n.DateTimeSymbols_de_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_el');
    +goog.provide('goog.i18n.DateTimeSymbols_en');
    +goog.provide('goog.i18n.DateTimeSymbols_en_AU');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GB');
    +goog.provide('goog.i18n.DateTimeSymbols_en_IE');
    +goog.provide('goog.i18n.DateTimeSymbols_en_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_en_ISO');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_US');
    +goog.provide('goog.i18n.DateTimeSymbols_en_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_es');
    +goog.provide('goog.i18n.DateTimeSymbols_es_419');
    +goog.provide('goog.i18n.DateTimeSymbols_es_ES');
    +goog.provide('goog.i18n.DateTimeSymbols_et');
    +goog.provide('goog.i18n.DateTimeSymbols_eu');
    +goog.provide('goog.i18n.DateTimeSymbols_fa');
    +goog.provide('goog.i18n.DateTimeSymbols_fi');
    +goog.provide('goog.i18n.DateTimeSymbols_fil');
    +goog.provide('goog.i18n.DateTimeSymbols_fr');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CA');
    +goog.provide('goog.i18n.DateTimeSymbols_ga');
    +goog.provide('goog.i18n.DateTimeSymbols_gl');
    +goog.provide('goog.i18n.DateTimeSymbols_gsw');
    +goog.provide('goog.i18n.DateTimeSymbols_gu');
    +goog.provide('goog.i18n.DateTimeSymbols_haw');
    +goog.provide('goog.i18n.DateTimeSymbols_he');
    +goog.provide('goog.i18n.DateTimeSymbols_hi');
    +goog.provide('goog.i18n.DateTimeSymbols_hr');
    +goog.provide('goog.i18n.DateTimeSymbols_hu');
    +goog.provide('goog.i18n.DateTimeSymbols_hy');
    +goog.provide('goog.i18n.DateTimeSymbols_id');
    +goog.provide('goog.i18n.DateTimeSymbols_in');
    +goog.provide('goog.i18n.DateTimeSymbols_is');
    +goog.provide('goog.i18n.DateTimeSymbols_it');
    +goog.provide('goog.i18n.DateTimeSymbols_iw');
    +goog.provide('goog.i18n.DateTimeSymbols_ja');
    +goog.provide('goog.i18n.DateTimeSymbols_ka');
    +goog.provide('goog.i18n.DateTimeSymbols_kk');
    +goog.provide('goog.i18n.DateTimeSymbols_km');
    +goog.provide('goog.i18n.DateTimeSymbols_kn');
    +goog.provide('goog.i18n.DateTimeSymbols_ko');
    +goog.provide('goog.i18n.DateTimeSymbols_ky');
    +goog.provide('goog.i18n.DateTimeSymbols_ln');
    +goog.provide('goog.i18n.DateTimeSymbols_lo');
    +goog.provide('goog.i18n.DateTimeSymbols_lt');
    +goog.provide('goog.i18n.DateTimeSymbols_lv');
    +goog.provide('goog.i18n.DateTimeSymbols_mk');
    +goog.provide('goog.i18n.DateTimeSymbols_ml');
    +goog.provide('goog.i18n.DateTimeSymbols_mn');
    +goog.provide('goog.i18n.DateTimeSymbols_mr');
    +goog.provide('goog.i18n.DateTimeSymbols_ms');
    +goog.provide('goog.i18n.DateTimeSymbols_mt');
    +goog.provide('goog.i18n.DateTimeSymbols_my');
    +goog.provide('goog.i18n.DateTimeSymbols_nb');
    +goog.provide('goog.i18n.DateTimeSymbols_ne');
    +goog.provide('goog.i18n.DateTimeSymbols_nl');
    +goog.provide('goog.i18n.DateTimeSymbols_no');
    +goog.provide('goog.i18n.DateTimeSymbols_no_NO');
    +goog.provide('goog.i18n.DateTimeSymbols_or');
    +goog.provide('goog.i18n.DateTimeSymbols_pa');
    +goog.provide('goog.i18n.DateTimeSymbols_pl');
    +goog.provide('goog.i18n.DateTimeSymbols_pt');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_BR');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_PT');
    +goog.provide('goog.i18n.DateTimeSymbols_ro');
    +goog.provide('goog.i18n.DateTimeSymbols_ru');
    +goog.provide('goog.i18n.DateTimeSymbols_si');
    +goog.provide('goog.i18n.DateTimeSymbols_sk');
    +goog.provide('goog.i18n.DateTimeSymbols_sl');
    +goog.provide('goog.i18n.DateTimeSymbols_sq');
    +goog.provide('goog.i18n.DateTimeSymbols_sr');
    +goog.provide('goog.i18n.DateTimeSymbols_sv');
    +goog.provide('goog.i18n.DateTimeSymbols_sw');
    +goog.provide('goog.i18n.DateTimeSymbols_ta');
    +goog.provide('goog.i18n.DateTimeSymbols_te');
    +goog.provide('goog.i18n.DateTimeSymbols_th');
    +goog.provide('goog.i18n.DateTimeSymbols_tl');
    +goog.provide('goog.i18n.DateTimeSymbols_tr');
    +goog.provide('goog.i18n.DateTimeSymbols_uk');
    +goog.provide('goog.i18n.DateTimeSymbols_ur');
    +goog.provide('goog.i18n.DateTimeSymbols_uz');
    +goog.provide('goog.i18n.DateTimeSymbols_vi');
    +goog.provide('goog.i18n.DateTimeSymbols_zh');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_CN');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_HK');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_TW');
    +goog.provide('goog.i18n.DateTimeSymbols_zu');
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_ISO.
    + */
    +goog.i18n.DateTimeSymbols_en_ISO = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yyyy-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss v', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}',
    +    '{1}, {0}', '{1}, {0}'],
    +  AVAILABLEFORMATS: {'Md': 'M/d', 'MMMMd': 'MMMM d', 'MMMd': 'MMM d'},
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale af.
    + */
    +goog.i18n.DateTimeSymbols_af = {
    +  ERAS: ['v.C.', 'n.C.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie',
    +    'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie',
    +    'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug',
    +    'Sep', 'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag',
    +    'Saterdag'],
    +  STANDALONEWEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrydag', 'Saterdag'],
    +  SHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1ste kwartaal', '2de kwartaal', '3de kwartaal', '4de kwartaal'],
    +  AMPMS: ['vm.', 'nm.'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'dd MMMM y', 'dd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale am.
    + */
    +goog.i18n.DateTimeSymbols_am = {
    +  ERAS: ['ዓ/ዓ', 'ዓ/ም'],
    +  ERANAMES: ['ዓመተ ዓለም', 'ዓመተ ምሕረት'],
    +  NARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ', 'ኦ', 'ሴ',
    +    'ኦ', 'ኖ', 'ዲ'],
    +  STANDALONENARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ',
    +    'ኦ', 'ሴ', 'ኦ', 'ኖ', 'ዲ'],
    +  MONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕሪል',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር',
    +    'ኦክቶበር', 'ኖቬምበር', 'ዲሴምበር'],
    +  STANDALONEMONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች',
    +    'ኤፕሪል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት',
    +    'ሴፕቴምበር', 'ኦክቶበር', 'ኖቬምበር',
    +    'ዲሴምበር'],
    +  SHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕሪ', 'ሜይ',
    +    'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክቶ', 'ኖቬም',
    +    'ዲሴም'],
    +  STANDALONESHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕሪ',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክቶ',
    +    'ኖቬም', 'ዲሴም'],
    +  WEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ',
    +    'ዓርብ', 'ቅዳሜ'],
    +  STANDALONEWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ',
    +    'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
    +  SHORTWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ',
    +    'ዓርብ', 'ቅዳሜ'],
    +  STANDALONESHORTWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰ', 'ረቡዕ',
    +    'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
    +  NARROWWEEKDAYS: ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
    +  STANDALONENARROWWEEKDAYS: ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
    +  SHORTQUARTERS: ['ሩብ1', 'ሩብ2', 'ሩብ3', 'ሩብ4'],
    +  QUARTERS: ['1ኛው ሩብ', 'ሁለተኛው ሩብ', '3ኛው ሩብ',
    +    '4ኛው ሩብ'],
    +  AMPMS: ['ጥዋት', 'ከሰዓት'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar.
    + */
    +goog.i18n.DateTimeSymbols_ar = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale az.
    + */
    +goog.i18n.DateTimeSymbols_az = {
    +  ERAS: ['e.ə.', 'b.e.'],
    +  ERANAMES: ['eramızdan əvvəl', 'bizim eramızın'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust',
    +    'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
    +  STANDALONEMONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun',
    +    'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'],
    +  SHORTMONTHS: ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen',
    +    'okt', 'noy', 'dek'],
    +  STANDALONESHORTMONTHS: ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl',
    +    'avq', 'sen', 'okt', 'noy', 'dek'],
    +  WEEKDAYS: ['bazar', 'bazar ertəsi', 'çərşənbə axşamı',
    +    'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
    +  STANDALONEWEEKDAYS: ['bazar', 'bazar ertəsi', 'çərşənbə axşamı',
    +    'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
    +  SHORTWEEKDAYS: ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
    +  STANDALONESHORTWEEKDAYS: ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
    +  NARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  STANDALONENARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  SHORTQUARTERS: ['1-ci kv.', '2-ci kv.', '3-cü kv.', '4-cü kv.'],
    +  QUARTERS: ['1-ci kvartal', '2-ci kvartal', '3-cü kvartal', '4-cü kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['d MMMM y, EEEE', 'd MMMM y', 'd MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bg.
    + */
    +goog.i18n.DateTimeSymbols_bg = {
    +  ERAS: ['пр.Хр.', 'сл.Хр.'],
    +  ERANAMES: ['преди Христа', 'след Христа'],
    +  NARROWMONTHS: ['я', 'ф', 'м', 'а', 'м', 'ю', 'ю', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['я', 'ф', 'м', 'а', 'м', 'ю', 'ю', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['януари', 'февруари', 'март', 'април',
    +    'май', 'юни', 'юли', 'август', 'септември',
    +    'октомври', 'ноември', 'декември'],
    +  STANDALONEMONTHS: ['януари', 'февруари', 'март',
    +    'април', 'май', 'юни', 'юли', 'август',
    +    'септември', 'октомври', 'ноември',
    +    'декември'],
    +  SHORTMONTHS: ['ян.', 'февр.', 'март', 'апр.', 'май', 'юни',
    +    'юли', 'авг.', 'септ.', 'окт.', 'ноем.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['ян.', 'февр.', 'март', 'апр.', 'май',
    +    'юни', 'юли', 'авг.', 'септ.', 'окт.', 'ноем.',
    +    'дек.'],
    +  WEEKDAYS: ['неделя', 'понеделник', 'вторник',
    +    'сряда', 'четвъртък', 'петък', 'събота'],
    +  STANDALONEWEEKDAYS: ['неделя', 'понеделник', 'вторник',
    +    'сряда', 'четвъртък', 'петък', 'събота'],
    +  SHORTWEEKDAYS: ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['нд', 'пн', 'вт', 'ср', 'чт', 'пт',
    +    'сб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['1. трим.', '2. трим.', '3. трим.',
    +    '4. трим.'],
    +  QUARTERS: ['1. тримесечие', '2. тримесечие',
    +    '3. тримесечие', '4. тримесечие'],
    +  AMPMS: ['пр.об.', 'сл.об.'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd.MM.y \'г\'.',
    +    'd.MM.yy \'г\'.'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bn.
    + */
    +goog.i18n.DateTimeSymbols_bn = {
    +  ZERODIGIT: 0x09E6,
    +  ERAS: ['খ্রিস্টপূর্ব', 'খৃষ্টাব্দ'],
    +  ERANAMES: ['খ্রিস্টপূর্ব',
    +    'খৃষ্টাব্দ'],
    +  NARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে', 'জুন',
    +    'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  STANDALONENARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে',
    +    'জুন', 'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  MONTHS: ['জানুয়ারী', 'ফেব্রুয়ারী',
    +    'মার্চ', 'এপ্রিল', 'মে', 'জুন',
    +    'জুলাই', 'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONEMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  SHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONESHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  WEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহস্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  STANDALONEWEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহষ্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  SHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল', 'বুধ',
    +    'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  STANDALONESHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল',
    +    'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  NARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ', 'শু', 'শ'],
    +  STANDALONENARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ',
    +    'শু', 'শ'],
    +  SHORTQUARTERS: [
    +    'প্র. ত্রৈ. এক. চতুর্থাংশ',
    +    'দ্বি.ত্রৈ.এক. চতুর্থাংশ',
    +    'তৃ.ত্রৈ.এক.চতুর্থাংশ',
    +    'চ.ত্রৈ.এক চতুর্থাংশ'],
    +  QUARTERS: [
    +    'প্রথম ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'দ্বিতীয় ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'তৃতীয় ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'চতুর্থ ত্রৈমাসিকের এক চতুর্থাংশ'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 4,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale br.
    + */
    +goog.i18n.DateTimeSymbols_br = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
    +    '11', '12'],
    +  STANDALONENARROWMONTHS: ['01', '02', '03', '04', '05', '06', '07', '08', '09',
    +    '10', '11', '12'],
    +  MONTHS: ['Genver', 'Cʼhwevrer', 'Meurzh', 'Ebrel', 'Mae', 'Mezheven',
    +    'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'],
    +  STANDALONEMONTHS: ['Genver', 'Cʼhwevrer', 'Meurzh', 'Ebrel', 'Mae',
    +    'Mezheven', 'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'],
    +  SHORTMONTHS: ['Gen', 'Cʼhwe', 'Meur', 'Ebr', 'Mae', 'Mezh', 'Goue', 'Eost',
    +    'Gwen', 'Here', 'Du', 'Ker'],
    +  STANDALONESHORTMONTHS: ['Gen', 'Cʼhwe', 'Meur', 'Ebr', 'Mae', 'Mezh', 'Goue',
    +    'Eost', 'Gwen', 'Here', 'Du', 'Ker'],
    +  WEEKDAYS: ['Sul', 'Lun', 'Meurzh', 'Mercʼher', 'Yaou', 'Gwener', 'Sadorn'],
    +  STANDALONEWEEKDAYS: ['Sul', 'Lun', 'Meurzh', 'Mercʼher', 'Yaou', 'Gwener',
    +    'Sadorn'],
    +  SHORTWEEKDAYS: ['Sul', 'Lun', 'Meu.', 'Mer.', 'Yaou', 'Gwe.', 'Sad.'],
    +  STANDALONESHORTWEEKDAYS: ['Sul', 'Lun', 'Meu.', 'Mer.', 'Yaou', 'Gwe.',
    +    'Sad.'],
    +  NARROWWEEKDAYS: ['Su', 'L', 'Mz', 'Mc', 'Y', 'G', 'Sa'],
    +  STANDALONENARROWWEEKDAYS: ['Su', 'L', 'Mz', 'Mc', 'Y', 'G', 'Sa'],
    +  SHORTQUARTERS: ['1añ trim.', '2l trim.', '3e trim.', '4e trim.'],
    +  QUARTERS: ['1añ trimiziad', '2l trimiziad', '3e trimiziad', '4e trimiziad'],
    +  AMPMS: ['A.M.', 'G.M.'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca.
    + */
    +goog.i18n.DateTimeSymbols_ca = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['abans de Crist', 'després de Crist'],
    +  NARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG', 'ST', 'OC',
    +    'NV', 'DS'],
    +  STANDALONENARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG',
    +    'ST', 'OC', 'NV', 'DS'],
    +  MONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol',
    +    'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  STANDALONEMONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny',
    +    'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  SHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.',
    +    'set.', 'oct.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny',
    +    'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
    +  WEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  STANDALONEWEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  SHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  STANDALONESHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  NARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  STANDALONENARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1r trimestre', '2n trimestre', '3r trimestre', '4t trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} \'a les\' {0}', '{1}, {0}', '{1} , {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale chr.
    + */
    +goog.i18n.DateTimeSymbols_chr = {
    +  ERAS: ['ᎤᏓᎷᎸ', 'ᎤᎶᏐᏅ'],
    +  ERANAMES: ['Ꮟ ᏥᏌ ᎾᏕᎲᏍᎬᎾ',
    +    'ᎠᎩᏃᎮᎵᏓᏍᏗᏱ ᎠᏕᏘᏱᏍᎬ ᏱᎰᏩ ᏧᏓᏂᎸᎢᏍᏗ'],
    +  NARROWMONTHS: ['Ꭴ', 'Ꭷ', 'Ꭰ', 'Ꭷ', 'Ꭰ', 'Ꮥ', 'Ꭻ', 'Ꭶ', 'Ꮪ',
    +    'Ꮪ', 'Ꮕ', 'Ꭵ'],
    +  STANDALONENARROWMONTHS: ['Ꭴ', 'Ꭷ', 'Ꭰ', 'Ꭷ', 'Ꭰ', 'Ꮥ', 'Ꭻ',
    +    'Ꭶ', 'Ꮪ', 'Ꮪ', 'Ꮕ', 'Ꭵ'],
    +  MONTHS: ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ',
    +    'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ',
    +    'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'],
    +  STANDALONEMONTHS: ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ',
    +    'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ',
    +    'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'],
    +  SHORTMONTHS: ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ', 'ᏕᎭ',
    +    'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'],
    +  STANDALONESHORTMONTHS: ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ',
    +    'ᏕᎭ', 'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'],
    +  WEEKDAYS: ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ',
    +    'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ',
    +    'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'],
    +  STANDALONEWEEKDAYS: ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ',
    +    'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ',
    +    'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'],
    +  SHORTWEEKDAYS: ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ',
    +    'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
    +  STANDALONESHORTWEEKDAYS: ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ',
    +    'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
    +  NARROWWEEKDAYS: ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
    +  STANDALONENARROWWEEKDAYS: ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale cs.
    + */
    +goog.i18n.DateTimeSymbols_cs = {
    +  ERAS: ['př. n. l.', 'n. l.'],
    +  ERANAMES: ['př. n. l.', 'n. l.'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ledna', 'února', 'března', 'dubna', 'května', 'června',
    +    'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'],
    +  STANDALONEMONTHS: ['leden', 'únor', 'březen', 'duben', 'květen', 'červen',
    +    'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'],
    +  SHORTMONTHS: ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc', 'srp',
    +    'zář', 'říj', 'lis', 'pro'],
    +  STANDALONESHORTMONTHS: ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc',
    +    'srp', 'zář', 'říj', 'lis', 'pro'],
    +  WEEKDAYS: ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek',
    +    'pátek', 'sobota'],
    +  SHORTWEEKDAYS: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
    +  NARROWWEEKDAYS: ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. čtvrtletí', '2. čtvrtletí', '3. čtvrtletí',
    +    '4. čtvrtletí'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. M. y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale cy.
    + */
    +goog.i18n.DateTimeSymbols_cy = {
    +  ERAS: ['CC', 'OC'],
    +  ERANAMES: ['Cyn Crist', 'Oed Crist'],
    +  NARROWMONTHS: ['I', 'Ch', 'M', 'E', 'M', 'M', 'G', 'A', 'M', 'H', 'T', 'Rh'],
    +  STANDALONENARROWMONTHS: ['I', 'Ch', 'M', 'E', 'M', 'M', 'G', 'A', 'M', 'H',
    +    'T', 'Rh'],
    +  MONTHS: ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin',
    +    'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'],
    +  STANDALONEMONTHS: ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin',
    +    'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'],
    +  SHORTMONTHS: ['Ion', 'Chwef', 'Mawrth', 'Ebrill', 'Mai', 'Meh', 'Gorff',
    +    'Awst', 'Medi', 'Hyd', 'Tach', 'Rhag'],
    +  STANDALONESHORTMONTHS: ['Ion', 'Chw', 'Maw', 'Ebr', 'Mai', 'Meh', 'Gor',
    +    'Awst', 'Medi', 'Hyd', 'Tach', 'Rhag'],
    +  WEEKDAYS: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau',
    +    'Dydd Gwener', 'Dydd Sadwrn'],
    +  STANDALONEWEEKDAYS: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher',
    +    'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'],
    +  SHORTWEEKDAYS: ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwen', 'Sad'],
    +  STANDALONESHORTWEEKDAYS: ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'],
    +  NARROWWEEKDAYS: ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
    +  SHORTQUARTERS: ['Ch1', 'Ch2', 'Ch3', 'Ch4'],
    +  QUARTERS: ['Chwarter 1af', '2il chwarter', '3ydd chwarter', '4ydd chwarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'am\' {0}', '{1} \'am\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale da.
    + */
    +goog.i18n.DateTimeSymbols_da = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['1. kvt.', '2. kvt.', '3. kvt.', '4. kvt.'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE \'den\' d. MMMM y', 'd. MMMM y', 'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} \'kl.\' {0}', '{1} \'kl.\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de.
    + */
    +goog.i18n.DateTimeSymbols_de = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_AT.
    + */
    +goog.i18n.DateTimeSymbols_de_AT = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Jänner', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Jänner', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jän.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jän', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y', 'dd. MMMM y', 'dd. MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_CH.
    + */
    +goog.i18n.DateTimeSymbols_de_CH = goog.i18n.DateTimeSymbols_de;
    +
    +
    +/**
    + * Date/time formatting symbols for locale el.
    + */
    +goog.i18n.DateTimeSymbols_el = {
    +  ERAS: ['π.Χ.', 'μ.Χ.'],
    +  ERANAMES: ['προ Χριστού', 'μετά Χριστόν'],
    +  NARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ', 'Ο',
    +    'Ν', 'Δ'],
    +  STANDALONENARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ',
    +    'Ο', 'Ν', 'Δ'],
    +  MONTHS: ['Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου',
    +    'Απριλίου', 'Μαΐου', 'Ιουνίου', 'Ιουλίου',
    +    'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου',
    +    'Νοεμβρίου', 'Δεκεμβρίου'],
    +  STANDALONEMONTHS: ['Ιανουάριος', 'Φεβρουάριος',
    +    'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος',
    +    'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος',
    +    'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
    +  SHORTMONTHS: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαΐ', 'Ιουν',
    +    'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
    +  STANDALONESHORTMONTHS: ['Ιαν', 'Φεβ', 'Μάρ', 'Απρ', 'Μάι',
    +    'Ιούν', 'Ιούλ', 'Αύγ', 'Σεπ', 'Οκτ', 'Νοέ', 'Δεκ'],
    +  WEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη',
    +    'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  STANDALONEWEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη',
    +    'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  SHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ', 'Παρ',
    +    'Σάβ'],
    +  STANDALONESHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ',
    +    'Παρ', 'Σάβ'],
    +  NARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  STANDALONENARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  SHORTQUARTERS: ['Τ1', 'Τ2', 'Τ3', 'Τ4'],
    +  QUARTERS: ['1ο τρίμηνο', '2ο τρίμηνο', '3ο τρίμηνο',
    +    '4ο τρίμηνο'],
    +  AMPMS: ['π.μ.', 'μ.μ.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} - {0}', '{1} - {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en.
    + */
    +goog.i18n.DateTimeSymbols_en = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_AU.
    + */
    +goog.i18n.DateTimeSymbols_en_AU = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GB.
    + */
    +goog.i18n.DateTimeSymbols_en_GB = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_IE.
    + */
    +goog.i18n.DateTimeSymbols_en_IE = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 2
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_IN.
    + */
    +goog.i18n.DateTimeSymbols_en_IN = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SG.
    + */
    +goog.i18n.DateTimeSymbols_en_SG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_US.
    + */
    +goog.i18n.DateTimeSymbols_en_US = goog.i18n.DateTimeSymbols_en;
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_ZA.
    + */
    +goog.i18n.DateTimeSymbols_en_ZA = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'dd MMM y', 'y/MM/dd'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es.
    + */
    +goog.i18n.DateTimeSymbols_es = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'anno Dómini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'sept.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Sept.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_419.
    + */
    +goog.i18n.DateTimeSymbols_es_419 = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_ES.
    + */
    +goog.i18n.DateTimeSymbols_es_ES = goog.i18n.DateTimeSymbols_es;
    +
    +
    +/**
    + * Date/time formatting symbols for locale et.
    + */
    +goog.i18n.DateTimeSymbols_et = {
    +  ERAS: ['e.m.a.', 'm.a.j.'],
    +  ERANAMES: ['enne meie aega', 'meie aja järgi'],
    +  NARROWMONTHS: ['J', 'V', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'V', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli',
    +    'august', 'september', 'oktoober', 'november', 'detsember'],
    +  STANDALONEMONTHS: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni',
    +    'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'],
    +  SHORTMONTHS: ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli',
    +    'aug', 'sept', 'okt', 'nov', 'dets'],
    +  STANDALONESHORTMONTHS: ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni',
    +    'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'],
    +  WEEKDAYS: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev',
    +    'neljapäev', 'reede', 'laupäev'],
    +  STANDALONEWEEKDAYS: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev',
    +    'neljapäev', 'reede', 'laupäev'],
    +  SHORTWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  STANDALONESHORTWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  NARROWWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm.ss zzzz', 'H:mm.ss z', 'H:mm.ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale eu.
    + */
    +goog.i18n.DateTimeSymbols_eu = {
    +  ERAS: ['K.a.', 'K.o.'],
    +  ERANAMES: ['K.a.', 'K.o.'],
    +  NARROWMONTHS: ['U', 'O', 'M', 'A', 'M', 'E', 'U', 'A', 'I', 'U', 'A', 'A'],
    +  STANDALONENARROWMONTHS: ['U', 'O', 'M', 'A', 'M', 'E', 'U', 'A', 'I', 'U',
    +    'A', 'A'],
    +  MONTHS: ['urtarrilak', 'otsailak', 'martxoak', 'apirilak', 'maiatzak',
    +    'ekainak', 'uztailak', 'abuztuak', 'irailak', 'urriak', 'azaroak',
    +    'abenduak'],
    +  STANDALONEMONTHS: ['Urtarrila', 'Otsaila', 'Martxoa', 'Apirila', 'Maiatza',
    +    'Ekaina', 'Uztaila', 'Abuztua', 'Iraila', 'Urria', 'Azaroa', 'Abendua'],
    +  SHORTMONTHS: ['urt.', 'ots.', 'mar.', 'api.', 'mai.', 'eka.', 'uzt.', 'abu.',
    +    'ira.', 'urr.', 'aza.', 'abe.'],
    +  STANDALONESHORTMONTHS: ['Urt.', 'Ots.', 'Mar.', 'Api.', 'Mai.', 'Eka.',
    +    'Uzt.', 'Abu.', 'Ira.', 'Urr.', 'Aza.', 'Abe.'],
    +  WEEKDAYS: ['igandea', 'astelehena', 'asteartea', 'asteazkena', 'osteguna',
    +    'ostirala', 'larunbata'],
    +  STANDALONEWEEKDAYS: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena',
    +    'Osteguna', 'Ostirala', 'Larunbata'],
    +  SHORTWEEKDAYS: ['ig.', 'al.', 'ar.', 'az.', 'og.', 'or.', 'lr.'],
    +  STANDALONESHORTWEEKDAYS: ['Ig.', 'Al.', 'Ar.', 'Az.', 'Og.', 'Or.', 'Lr.'],
    +  NARROWWEEKDAYS: ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
    +  SHORTQUARTERS: ['1Hh', '2Hh', '3Hh', '4Hh'],
    +  QUARTERS: ['1. hiruhilekoa', '2. hiruhilekoa', '3. hiruhilekoa',
    +    '4. hiruhilekoa'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y(\'e\')\'ko\' MMMM d, EEEE', 'y(\'e\')\'ko\' MMMM d',
    +    'y MMM d', 'y/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss (zzzz)', 'HH:mm:ss (z)', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fa.
    + */
    +goog.i18n.DateTimeSymbols_fa = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['ق.م.', 'م.'],
    +  ERANAMES: ['قبل از میلاد', 'میلادی'],
    +  NARROWMONTHS: ['ژ', 'ف', 'م', 'آ', 'م', 'ژ', 'ژ', 'ا', 'س', 'ا',
    +    'ن', 'د'],
    +  STANDALONENARROWMONTHS: ['ژ', 'ف', 'م', 'آ', 'م', 'ژ', 'ژ', 'ا', 'س',
    +    'ا', 'ن', 'د'],
    +  MONTHS: ['ژانویهٔ', 'فوریهٔ', 'مارس', 'آوریل', 'مهٔ',
    +    'ژوئن', 'ژوئیهٔ', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسامبر'],
    +  STANDALONEMONTHS: ['ژانویه', 'فوریه', 'مارس', 'آوریل',
    +    'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسامبر'],
    +  SHORTMONTHS: ['ژانویهٔ', 'فوریهٔ', 'مارس', 'آوریل',
    +    'مهٔ', 'ژوئن', 'ژوئیهٔ', 'اوت', 'سپتامبر',
    +    'اکتبر', 'نوامبر', 'دسامبر'],
    +  STANDALONESHORTMONTHS: ['ژانویه', 'فوریه', 'مارس',
    +    'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر',
    +    'اکتبر', 'نوامبر', 'دسامبر'],
    +  WEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONEWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  SHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONESHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  NARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  STANDALONENARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  SHORTQUARTERS: ['س‌م۱', 'س‌م۲', 'س‌م۳', 'س‌م۴'],
    +  QUARTERS: ['سه‌ماههٔ اول', 'سه‌ماههٔ دوم',
    +    'سه‌ماههٔ سوم', 'سه‌ماههٔ چهارم'],
    +  AMPMS: ['قبل‌ازظهر', 'بعدازظهر'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y/M/d'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}، ساعت {0}', '{1}، ساعت {0}', '{1}،‏ {0}',
    +    '{1}،‏ {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 4],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fi.
    + */
    +goog.i18n.DateTimeSymbols_fi = {
    +  ERAS: ['eKr.', 'jKr.'],
    +  ERANAMES: ['ennen Kristuksen syntymää', 'jälkeen Kristuksen syntymän'],
    +  NARROWMONTHS: ['T', 'H', 'M', 'H', 'T', 'K', 'H', 'E', 'S', 'L', 'M', 'J'],
    +  STANDALONENARROWMONTHS: ['T', 'H', 'M', 'H', 'T', 'K', 'H', 'E', 'S', 'L',
    +    'M', 'J'],
    +  MONTHS: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta',
    +    'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta',
    +    'lokakuuta', 'marraskuuta', 'joulukuuta'],
    +  STANDALONEMONTHS: ['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu',
    +    'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu',
    +    'marraskuu', 'joulukuu'],
    +  SHORTMONTHS: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta',
    +    'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta',
    +    'lokakuuta', 'marraskuuta', 'joulukuuta'],
    +  STANDALONESHORTMONTHS: ['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä',
    +    'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'],
    +  WEEKDAYS: ['sunnuntaina', 'maanantaina', 'tiistaina', 'keskiviikkona',
    +    'torstaina', 'perjantaina', 'lauantaina'],
    +  STANDALONEWEEKDAYS: ['sunnuntai', 'maanantai', 'tiistai', 'keskiviikko',
    +    'torstai', 'perjantai', 'lauantai'],
    +  SHORTWEEKDAYS: ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
    +  STANDALONESHORTWEEKDAYS: ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
    +  SHORTQUARTERS: ['1. nelj.', '2. nelj.', '3. nelj.', '4. nelj.'],
    +  QUARTERS: ['1. neljännes', '2. neljännes', '3. neljännes',
    +    '4. neljännes'],
    +  AMPMS: ['ap.', 'ip.'],
    +  DATEFORMATS: ['cccc d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.y'],
    +  TIMEFORMATS: ['H.mm.ss zzzz', 'H.mm.ss z', 'H.mm.ss', 'H.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fil.
    + */
    +goog.i18n.DateTimeSymbols_fil = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo',
    +    'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  STANDALONEMONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo',
    +    'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  SHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set',
    +    'Okt', 'Nob', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul',
    +    'Ago', 'Set', 'Okt', 'Nob', 'Dis'],
    +  WEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes', 'Biyernes',
    +    'Sabado'],
    +  STANDALONEWEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes',
    +    'Biyernes', 'Sabado'],
    +  SHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  NARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ika-1 quarter', 'ika-2 quarter', 'ika-3 quarter',
    +    'ika-4 na quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'nang\' {0}', '{1} \'nang\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr.
    + */
    +goog.i18n.DateTimeSymbols_fr = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CA.
    + */
    +goog.i18n.DateTimeSymbols_fr_CA = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
    +    'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',
    +    'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',
    +    'vendredi', 'samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',
    +    'sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'y-MM-dd', 'yy-MM-dd'],
    +  TIMEFORMATS: ['HH \'h\' mm \'min\' ss \'s\' zzzz', 'HH:mm:ss z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'à\' {0}', '{1} \'à\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ga.
    + */
    +goog.i18n.DateTimeSymbols_ga = {
    +  ERAS: ['RC', 'AD'],
    +  ERANAMES: ['Roimh Chríost', 'Anno Domini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D',
    +    'S', 'N'],
    +  MONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Meitheamh',
    +    'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair', 'Samhain',
    +    'Nollaig'],
    +  STANDALONEMONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine',
    +    'Meitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair',
    +    'Samhain', 'Nollaig'],
    +  SHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith', 'Iúil',
    +    'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
    +  STANDALONESHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith',
    +    'Iúil', 'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
    +  WEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin',
    +    'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
    +  STANDALONEWEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt',
    +    'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
    +  SHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine', 'Sath'],
    +  STANDALONESHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine',
    +    'Sath'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['1ú ráithe', '2ú ráithe', '3ú ráithe', '4ú ráithe'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 2
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gl.
    + */
    +goog.i18n.DateTimeSymbols_gl = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'despois de Cristo'],
    +  NARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'decembro'],
    +  STANDALONEMONTHS: ['Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño',
    +    'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro'],
    +  SHORTMONTHS: ['xan', 'feb', 'mar', 'abr', 'mai', 'xuñ', 'xul', 'ago', 'set',
    +    'out', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xuñ', 'Xul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dec'],
    +  WEEKDAYS: ['domingo', 'luns', 'martes', 'mércores', 'xoves', 'venres',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Luns', 'Martes', 'Mércores', 'Xoves',
    +    'Venres', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mér', 'xov', 'ven', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['Dom', 'Lun', 'Mar', 'Mér', 'Xov', 'Ven', 'Sáb'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1o trimestre', '2o trimestre', '3o trimestre', '4o trimestre'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'd MMM, y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gsw.
    + */
    +goog.i18n.DateTimeSymbols_gsw = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Okt', 'Nov', 'Dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch', 'Dunschtig',
    +    'Friitig', 'Samschtig'],
    +  STANDALONEWEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch',
    +    'Dunschtig', 'Friitig', 'Samschtig'],
    +  SHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nam.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gu.
    + */
    +goog.i18n.DateTimeSymbols_gu = {
    +  ERAS: ['ઈસુના જન્મ પહેલા', 'ઇસવીસન'],
    +  ERANAMES: ['ઈસવીસન પૂર્વે', 'ઇસવીસન'],
    +  NARROWMONTHS: ['જા', 'ફે', 'મા', 'એ', 'મે', 'જૂ',
    +    'જુ', 'ઑ', 'સ', 'ઑ', 'ન', 'ડિ'],
    +  STANDALONENARROWMONTHS: ['જા', 'ફે', 'મા', 'એ', 'મે',
    +    'જૂ', 'જુ', 'ઑ', 'સ', 'ઑ', 'ન', 'ડિ'],
    +  MONTHS: ['જાન્યુઆરી', 'ફેબ્રુઆરી',
    +    'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન',
    +    'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટેમ્બર',
    +    'ઑક્ટોબર', 'નવેમ્બર',
    +    'ડિસેમ્બર'],
    +  STANDALONEMONTHS: ['જાન્યુઆરી',
    +    'ફેબ્રુઆરી', 'માર્ચ', 'એપ્રિલ',
    +    'મે', 'જૂન', 'જુલાઈ', 'ઑગસ્ટ',
    +    'સપ્ટેમ્બર', 'ઑક્ટોબર',
    +    'નવેમ્બર', 'ડિસેમ્બર'],
    +  SHORTMONTHS: ['જાન્યુ', 'ફેબ્રુ', 'માર્ચ',
    +    'એપ્રિલ', 'મે', 'જૂન', 'જુલાઈ', 'ઑગ',
    +    'સપ્ટે', 'ઑક્ટો', 'નવે', 'ડિસે'],
    +  STANDALONESHORTMONTHS: ['જાન્યુ', 'ફેબ્રુ',
    +    'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન',
    +    'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટે', 'ઑક્ટો',
    +    'નવે', 'ડિસે'],
    +  WEEKDAYS: ['રવિવાર', 'સોમવાર',
    +    'મંગળવાર', 'બુધવાર', 'ગુરુવાર',
    +    'શુક્રવાર', 'શનિવાર'],
    +  STANDALONEWEEKDAYS: ['રવિવાર', 'સોમવાર',
    +    'મંગળવાર', 'બુધવાર', 'ગુરુવાર',
    +    'શુક્રવાર', 'શનિવાર'],
    +  SHORTWEEKDAYS: ['રવિ', 'સોમ', 'મંગળ', 'બુધ',
    +    'ગુરુ', 'શુક્ર', 'શનિ'],
    +  STANDALONESHORTWEEKDAYS: ['રવિ', 'સોમ', 'મંગળ',
    +    'બુધ', 'ગુરુ', 'શુક્ર', 'શનિ'],
    +  NARROWWEEKDAYS: ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ',
    +    'શ'],
    +  STANDALONENARROWWEEKDAYS: ['ર', 'સો', 'મં', 'બુ', 'ગુ',
    +    'શુ', 'શ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['પહેલો ત્રિમાસ',
    +    'બીજો ત્રિમાસ',
    +    'ત્રીજો ત્રિમાસ',
    +    'ચોથો ત્રિમાસ'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['hh:mm:ss a zzzz', 'hh:mm:ss a z', 'hh:mm:ss a', 'hh:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale haw.
    + */
    +goog.i18n.DateTimeSymbols_haw = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei', 'Iune',
    +    'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa', 'Kekemapa'],
    +  STANDALONEMONTHS: ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei',
    +    'Iune', 'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa',
    +    'Kekemapa'],
    +  SHORTMONTHS: ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.', 'Iul.', 'ʻAu.',
    +    'Kep.', 'ʻOk.', 'Now.', 'Kek.'],
    +  STANDALONESHORTMONTHS: ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.',
    +    'Iul.', 'ʻAu.', 'Kep.', 'ʻOk.', 'Now.', 'Kek.'],
    +  WEEKDAYS: ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu', 'Poʻahā',
    +    'Poʻalima', 'Poʻaono'],
    +  STANDALONEWEEKDAYS: ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu',
    +    'Poʻahā', 'Poʻalima', 'Poʻaono'],
    +  SHORTWEEKDAYS: ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
    +  STANDALONESHORTWEEKDAYS: ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale he.
    + */
    +goog.i18n.DateTimeSymbols_he = {
    +  ERAS: ['לפנה״ס', 'לספירה'],
    +  ERANAMES: ['לפני הספירה', 'לספירה'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי',
    +    'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר',
    +    'נובמבר', 'דצמבר'],
    +  STANDALONEMONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל',
    +    'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר',
    +    'אוקטובר', 'נובמבר', 'דצמבר'],
    +  SHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי',
    +    'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳',
    +    'דצמ׳'],
    +  STANDALONESHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳',
    +    'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳',
    +    'נוב׳', 'דצמ׳'],
    +  WEEKDAYS: ['יום ראשון', 'יום שני', 'יום שלישי',
    +    'יום רביעי', 'יום חמישי', 'יום שישי',
    +    'יום שבת'],
    +  STANDALONEWEEKDAYS: ['יום ראשון', 'יום שני',
    +    'יום שלישי', 'יום רביעי', 'יום חמישי',
    +    'יום שישי', 'יום שבת'],
    +  SHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳',
    +    'יום ה׳', 'יום ו׳', 'שבת'],
    +  STANDALONESHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳',
    +    'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'],
    +  NARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
    +  STANDALONENARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳',
    +    'ש׳'],
    +  SHORTQUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3',
    +    'רבעון 4'],
    +  QUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
    +  AMPMS: ['לפנה״צ', 'אחה״צ'],
    +  DATEFORMATS: ['EEEE, d בMMMM y', 'd בMMMM y', 'd בMMM y', 'd.M.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} בשעה {0}', '{1} בשעה {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hi.
    + */
    +goog.i18n.DateTimeSymbols_hi = {
    +  ERAS: ['ईसा-पूर्व', 'ईस्वी'],
    +  ERANAMES: ['ईसा-पूर्व', 'ईसवी सन'],
    +  NARROWMONTHS: ['ज', 'फ़', 'मा', 'अ', 'म', 'जू', 'जु',
    +    'अ', 'सि', 'अ', 'न', 'दि'],
    +  STANDALONENARROWMONTHS: ['ज', 'फ़', 'मा', 'अ', 'म', 'जू',
    +    'जु', 'अ', 'सि', 'अ', 'न', 'दि'],
    +  MONTHS: ['जनवरी', 'फ़रवरी', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुलाई',
    +    'अगस्त', 'सितंबर', 'अक्तूबर',
    +    'नवंबर', 'दिसंबर'],
    +  STANDALONEMONTHS: ['जनवरी', 'फ़रवरी', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुलाई',
    +    'अगस्त', 'सितंबर', 'अक्तूबर',
    +    'नवंबर', 'दिसंबर'],
    +  SHORTMONTHS: ['जन॰', 'फ़र॰', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुल॰', 'अग॰',
    +    'सित॰', 'अक्तू॰', 'नव॰', 'दिस॰'],
    +  STANDALONESHORTMONTHS: ['जन॰', 'फ़र॰', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुल॰', 'अग॰',
    +    'सित॰', 'अक्तू॰', 'नव॰', 'दिस॰'],
    +  WEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगलवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगलवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  SHORTWEEKDAYS: ['रवि', 'सोम', 'मंगल', 'बुध',
    +    'गुरु', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['रवि', 'सोम', 'मंगल',
    +    'बुध', 'गुरु', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु', 'शु',
    +    'श'],
    +  STANDALONENARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['ति1', 'ति2', 'ति3', 'ति4'],
    +  QUARTERS: ['पहली तिमाही',
    +    'दूसरी तिमाही', 'तीसरी तिमाही',
    +    'चौथी तिमाही'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'dd/MM/y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} को {0}', '{1} को {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hr.
    + */
    +goog.i18n.DateTimeSymbols_hr = {
    +  ERAS: ['pr. Kr.', 'p. Kr.'],
    +  ERANAMES: ['Prije Krista', 'Poslije Krista'],
    +  NARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.',
    +    '11.', '12.'],
    +  STANDALONENARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.',
    +    '10.', '11.', '12.'],
    +  MONTHS: ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja',
    +    'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'],
    +  STANDALONEMONTHS: ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj',
    +    'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'],
    +  SHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj',
    +    'lis', 'stu', 'pro'],
    +  STANDALONESHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp',
    +    'kol', 'ruj', 'lis', 'stu', 'pro'],
    +  WEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak',
    +    'petak', 'subota'],
    +  STANDALONEWEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda',
    +    'četvrtak', 'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['1kv', '2kv', '3kv', '4kv'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y.', 'd. MMMM y.', 'd. MMM y.', 'dd.MM.y.'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'u\' {0}', '{1} \'u\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hu.
    + */
    +goog.i18n.DateTimeSymbols_hu = {
    +  ERAS: ['i. e.', 'i. sz.'],
    +  ERANAMES: ['időszámításunk előtt', 'időszámításunk szerint'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'Á', 'M', 'J', 'J', 'A', 'Sz', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'Á', 'M', 'J', 'J', 'A', 'Sz', 'O',
    +    'N', 'D'],
    +  MONTHS: ['január', 'február', 'március', 'április', 'május', 'június',
    +    'július', 'augusztus', 'szeptember', 'október', 'november', 'december'],
    +  STANDALONEMONTHS: ['január', 'február', 'március', 'április', 'május',
    +    'június', 'július', 'augusztus', 'szeptember', 'október', 'november',
    +    'december'],
    +  SHORTMONTHS: ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.', 'júl.',
    +    'aug.', 'szept.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.',
    +    'júl.', 'aug.', 'szept.', 'okt.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök',
    +    'péntek', 'szombat'],
    +  STANDALONEWEEKDAYS: ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök',
    +    'péntek', 'szombat'],
    +  SHORTWEEKDAYS: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
    +  STANDALONESHORTWEEKDAYS: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
    +  NARROWWEEKDAYS: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
    +  STANDALONENARROWWEEKDAYS: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
    +  SHORTQUARTERS: ['N1', 'N2', 'N3', 'N4'],
    +  QUARTERS: ['I. negyedév', 'II. negyedév', 'III. negyedév',
    +    'IV. negyedév'],
    +  AMPMS: ['de.', 'du.'],
    +  DATEFORMATS: ['y. MMMM d., EEEE', 'y. MMMM d.', 'y. MMM d.', 'y. MM. dd.'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hy.
    + */
    +goog.i18n.DateTimeSymbols_hy = {
    +  ERAS: ['մ.թ.ա.', 'մ.թ.'],
    +  ERANAMES: ['մ.թ.ա.', 'մ.թ.'],
    +  NARROWMONTHS: ['Հ', 'Փ', 'Մ', 'Ա', 'Մ', 'Հ', 'Հ', 'Օ', 'Ս', 'Հ',
    +    'Ն', 'Դ'],
    +  STANDALONENARROWMONTHS: ['Հ', 'Փ', 'Մ', 'Ա', 'Մ', 'Հ', 'Հ', 'Օ', 'Ս',
    +    'Հ', 'Ն', 'Դ'],
    +  MONTHS: ['հունվարի', 'փետրվարի', 'մարտի', 'ապրիլի',
    +    'մայիսի', 'հունիսի', 'հուլիսի', 'օգոստոսի',
    +    'սեպտեմբերի', 'հոկտեմբերի', 'նոյեմբերի',
    +    'դեկտեմբերի'],
    +  STANDALONEMONTHS: ['հունվար', 'փետրվար', 'մարտ',
    +    'ապրիլ', 'մայիս', 'հունիս', 'հուլիս',
    +    'օգոստոս', 'սեպտեմբեր', 'հոկտեմբեր',
    +    'նոյեմբեր', 'դեկտեմբեր'],
    +  SHORTMONTHS: ['հնվ', 'փտվ', 'մրտ', 'ապր', 'մյս', 'հնս',
    +    'հլս', 'օգս', 'սեպ', 'հոկ', 'նոյ', 'դեկ'],
    +  STANDALONESHORTMONTHS: ['հնվ', 'փտվ', 'մրտ', 'ապր', 'մյս',
    +    'հնս', 'հլս', 'օգս', 'սեպ', 'հոկ', 'նոյ', 'դեկ'],
    +  WEEKDAYS: ['կիրակի', 'երկուշաբթի', 'երեքշաբթի',
    +    'չորեքշաբթի', 'հինգշաբթի', 'ուրբաթ', 'շաբաթ'],
    +  STANDALONEWEEKDAYS: ['կիրակի', 'երկուշաբթի',
    +    'երեքշաբթի', 'չորեքշաբթի', 'հինգշաբթի',
    +    'ուրբաթ', 'շաբաթ'],
    +  SHORTWEEKDAYS: ['կիր', 'երկ', 'երք', 'չրք', 'հնգ', 'ուր',
    +    'շբթ'],
    +  STANDALONESHORTWEEKDAYS: ['կիր', 'երկ', 'երք', 'չրք', 'հնգ',
    +    'ուր', 'շբթ'],
    +  NARROWWEEKDAYS: ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
    +  STANDALONENARROWWEEKDAYS: ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
    +  SHORTQUARTERS: ['1-ին եռմս.', '2-րդ եռմս.', '3-րդ եռմս.',
    +    '4-րդ եռմս.'],
    +  QUARTERS: ['1-ին եռամսյակ', '2-րդ եռամսյակ',
    +    '3-րդ եռամսյակ', '4-րդ եռամսյակ'],
    +  AMPMS: ['կեսօրից առաջ', 'կեսօրից հետո'],
    +  DATEFORMATS: ['yթ. MMMM d, EEEE', 'dd MMMM, yթ.', 'dd MMM, yթ.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss, zzzz', 'H:mm:ss, z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale id.
    + */
    +goog.i18n.DateTimeSymbols_id = {
    +  ERAS: ['SM', 'M'],
    +  ERANAMES: ['Sebelum Masehi', 'M'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli',
    +    'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
    +    'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Agt', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kuartal ke-1', 'Kuartal ke-2', 'Kuartal ke-3', 'Kuartal ke-4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale in.
    + */
    +goog.i18n.DateTimeSymbols_in = {
    +  ERAS: ['SM', 'M'],
    +  ERANAMES: ['Sebelum Masehi', 'M'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli',
    +    'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
    +    'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Agt', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kuartal ke-1', 'Kuartal ke-2', 'Kuartal ke-3', 'Kuartal ke-4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale is.
    + */
    +goog.i18n.DateTimeSymbols_is = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['fyrir Krist', 'eftir Krist'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'Á', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'Á', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní', 'júlí',
    +    'ágúst', 'september', 'október', 'nóvember', 'desember'],
    +  STANDALONEMONTHS: ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní',
    +    'júlí', 'ágúst', 'september', 'október', 'nóvember', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maí', 'jún.', 'júl.',
    +    'ágú.', 'sep.', 'okt.', 'nóv.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maí', 'jún.',
    +    'júl.', 'ágú.', 'sep.', 'okt.', 'nóv.', 'des.'],
    +  WEEKDAYS: ['sunnudagur', 'mánudagur', 'þriðjudagur', 'miðvikudagur',
    +    'fimmtudagur', 'föstudagur', 'laugardagur'],
    +  STANDALONEWEEKDAYS: ['sunnudagur', 'mánudagur', 'þriðjudagur',
    +    'miðvikudagur', 'fimmtudagur', 'föstudagur', 'laugardagur'],
    +  SHORTWEEKDAYS: ['sun.', 'mán.', 'þri.', 'mið.', 'fim.', 'fös.', 'lau.'],
    +  STANDALONESHORTWEEKDAYS: ['sun.', 'mán.', 'þri.', 'mið.', 'fim.', 'fös.',
    +    'lau.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
    +  SHORTQUARTERS: ['F1', 'F2', 'F3', 'F4'],
    +  QUARTERS: ['1. fjórðungur', '2. fjórðungur', '3. fjórðungur',
    +    '4. fjórðungur'],
    +  AMPMS: ['f.h.', 'e.h.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'd.M.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'kl.\' {0}', '{1} \'kl.\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale it.
    + */
    +goog.i18n.DateTimeSymbols_it = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['a.C.', 'd.C.'],
    +  NARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
    +    'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
    +  STANDALONEMONTHS: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio',
    +    'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre',
    +    'Dicembre'],
    +  SHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set',
    +    'ott', 'nov', 'dic'],
    +  STANDALONESHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug',
    +    'ago', 'set', 'ott', 'nov', 'dic'],
    +  WEEKDAYS: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì',
    +    'venerdì', 'sabato'],
    +  STANDALONEWEEKDAYS: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì',
    +    'Giovedì', 'Venerdì', 'Sabato'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestre', '2º trimestre', '3º trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale iw.
    + */
    +goog.i18n.DateTimeSymbols_iw = {
    +  ERAS: ['לפנה״ס', 'לספירה'],
    +  ERANAMES: ['לפני הספירה', 'לספירה'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי',
    +    'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר',
    +    'נובמבר', 'דצמבר'],
    +  STANDALONEMONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל',
    +    'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר',
    +    'אוקטובר', 'נובמבר', 'דצמבר'],
    +  SHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי',
    +    'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳',
    +    'דצמ׳'],
    +  STANDALONESHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳',
    +    'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳',
    +    'נוב׳', 'דצמ׳'],
    +  WEEKDAYS: ['יום ראשון', 'יום שני', 'יום שלישי',
    +    'יום רביעי', 'יום חמישי', 'יום שישי',
    +    'יום שבת'],
    +  STANDALONEWEEKDAYS: ['יום ראשון', 'יום שני',
    +    'יום שלישי', 'יום רביעי', 'יום חמישי',
    +    'יום שישי', 'יום שבת'],
    +  SHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳',
    +    'יום ה׳', 'יום ו׳', 'שבת'],
    +  STANDALONESHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳',
    +    'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'],
    +  NARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
    +  STANDALONENARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳',
    +    'ש׳'],
    +  SHORTQUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3',
    +    'רבעון 4'],
    +  QUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
    +  AMPMS: ['לפנה״צ', 'אחה״צ'],
    +  DATEFORMATS: ['EEEE, d בMMMM y', 'd בMMMM y', 'd בMMM y', 'd.M.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} בשעה {0}', '{1} בשעה {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ja.
    + */
    +goog.i18n.DateTimeSymbols_ja = {
    +  ERAS: ['紀元前', '西暦'],
    +  ERANAMES: ['紀元前', '西暦'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日',
    +    '金曜日', '土曜日'],
    +  STANDALONEWEEKDAYS: ['日曜日', '月曜日', '火曜日', '水曜日',
    +    '木曜日', '金曜日', '土曜日'],
    +  SHORTWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  STANDALONESHORTWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  NARROWWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  STANDALONENARROWWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['第1四半期', '第2四半期', '第3四半期',
    +    '第4四半期'],
    +  AMPMS: ['午前', '午後'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y/MM/dd', 'y/MM/dd'],
    +  TIMEFORMATS: ['H時mm分ss秒 zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ka.
    + */
    +goog.i18n.DateTimeSymbols_ka = {
    +  ERAS: ['ძვ. წ.', 'ახ. წ.'],
    +  ERANAMES: ['ძველი წელთაღრიცხვით',
    +    'ახალი წელთაღრიცხვით'],
    +  NARROWMONTHS: ['ი', 'თ', 'მ', 'ა', 'მ', 'ი', 'ი', 'ა', 'ს',
    +    'ო', 'ნ', 'დ'],
    +  STANDALONENARROWMONTHS: ['ი', 'თ', 'მ', 'ა', 'მ', 'ი', 'ი',
    +    'ა', 'ს', 'ო', 'ნ', 'დ'],
    +  MONTHS: ['იანვარი', 'თებერვალი',
    +    'მარტი', 'აპრილი', 'მაისი',
    +    'ივნისი', 'ივლისი', 'აგვისტო',
    +    'სექტემბერი', 'ოქტომბერი',
    +    'ნოემბერი', 'დეკემბერი'],
    +  STANDALONEMONTHS: ['იანვარი', 'თებერვალი',
    +    'მარტი', 'აპრილი', 'მაისი',
    +    'ივნისი', 'ივლისი', 'აგვისტო',
    +    'სექტემბერი', 'ოქტომბერი',
    +    'ნოემბერი', 'დეკემბერი'],
    +  SHORTMONTHS: ['იან', 'თებ', 'მარ', 'აპრ', 'მაი',
    +    'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ',
    +    'ნოე', 'დეკ'],
    +  STANDALONESHORTMONTHS: ['იან', 'თებ', 'მარ', 'აპრ',
    +    'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ',
    +    'ოქტ', 'ნოე', 'დეკ'],
    +  WEEKDAYS: ['კვირა', 'ორშაბათი',
    +    'სამშაბათი', 'ოთხშაბათი',
    +    'ხუთშაბათი', 'პარასკევი',
    +    'შაბათი'],
    +  STANDALONEWEEKDAYS: ['კვირა', 'ორშაბათი',
    +    'სამშაბათი', 'ოთხშაბათი',
    +    'ხუთშაბათი', 'პარასკევი',
    +    'შაბათი'],
    +  SHORTWEEKDAYS: ['კვი', 'ორშ', 'სამ', 'ოთხ',
    +    'ხუთ', 'პარ', 'შაბ'],
    +  STANDALONESHORTWEEKDAYS: ['კვი', 'ორშ', 'სამ', 'ოთხ',
    +    'ხუთ', 'პარ', 'შაბ'],
    +  NARROWWEEKDAYS: ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
    +  STANDALONENARROWWEEKDAYS: ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
    +  SHORTQUARTERS: ['I კვ.', 'II კვ.', 'III კვ.', 'IV კვ.'],
    +  QUARTERS: ['I კვარტალი', 'II კვარტალი',
    +    'III კვარტალი', 'IV კვარტალი'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM, y', 'd MMMM, y', 'd MMM, y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1}, {0}', '{1} {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kk.
    + */
    +goog.i18n.DateTimeSymbols_kk = {
    +  ERAS: ['б.з.д.', 'б.з.'],
    +  ERANAMES: ['Біздің заманымызға дейін',
    +    'Біздің заманымыз'],
    +  NARROWMONTHS: ['Қ', 'А', 'Н', 'С', 'М', 'М', 'Ш', 'Т', 'Қ', 'Қ',
    +    'Қ', 'Ж'],
    +  STANDALONENARROWMONTHS: ['Қ', 'А', 'Н', 'С', 'М', 'М', 'Ш', 'Т', 'Қ',
    +    'Қ', 'Қ', 'Ж'],
    +  MONTHS: ['қаңтар', 'ақпан', 'наурыз', 'сәуір',
    +    'мамыр', 'маусым', 'шілде', 'тамыз',
    +    'қыркүйек', 'қазан', 'қараша', 'желтоқсан'],
    +  STANDALONEMONTHS: ['қаңтар', 'ақпан', 'наурыз', 'сәуір',
    +    'мамыр', 'маусым', 'шілде', 'тамыз',
    +    'қыркүйек', 'қазан', 'қараша', 'желтоқсан'],
    +  SHORTMONTHS: ['қаң.', 'ақп.', 'нау.', 'сәу.', 'мам.',
    +    'мау.', 'шіл.', 'там.', 'қыр.', 'қаз.', 'қар.',
    +    'желт.'],
    +  STANDALONESHORTMONTHS: ['қаң.', 'ақп.', 'нау.', 'сәу.', 'мам.',
    +    'мау.', 'шіл.', 'там.', 'қыр.', 'қаз.', 'қар.',
    +    'желт.'],
    +  WEEKDAYS: ['жексенбі', 'дүйсенбі', 'сейсенбі',
    +    'сәрсенбі', 'бейсенбі', 'жұма', 'сенбі'],
    +  STANDALONEWEEKDAYS: ['жексенбі', 'дүйсенбі',
    +    'сейсенбі', 'сәрсенбі', 'бейсенбі', 'жұма',
    +    'сенбі'],
    +  SHORTWEEKDAYS: ['жек', 'дүй', 'сей', 'сәр', 'бей', 'жұма',
    +    'сен'],
    +  STANDALONESHORTWEEKDAYS: ['жек', 'дүй', 'сей', 'сәр', 'бей',
    +    'жұма', 'сб.'],
    +  NARROWWEEKDAYS: ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
    +  SHORTQUARTERS: ['Т1', 'Т2', 'Т3', 'Т4'],
    +  QUARTERS: ['1-інші тоқсан', '2-інші тоқсан',
    +    '3-інші тоқсан', '4-інші тоқсан'],
    +  AMPMS: ['таңертеңгі', 'түстен кейінгі'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'y, dd-MMM', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale km.
    + */
    +goog.i18n.DateTimeSymbols_km = {
    +  ERAS: ['មុន គ.ស.', 'គ.ស.'],
    +  ERANAMES: ['មុន​គ្រិស្តសករាជ',
    +    'គ្រិស្តសករាជ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា',
    +    'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា',
    +    'កញ្ញា', 'តុលា', 'វិច្ឆិកា',
    +    'ធ្នូ'],
    +  STANDALONEMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  SHORTMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  STANDALONESHORTMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  WEEKDAYS: ['អាទិត្យ', 'ចន្ទ', 'អង្គារ',
    +    'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ',
    +    'សៅរ៍'],
    +  STANDALONEWEEKDAYS: ['អាទិត្យ', 'ចន្ទ',
    +    'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍',
    +    'សុក្រ', 'សៅរ៍'],
    +  SHORTWEEKDAYS: ['អាទិត្យ', 'ចន្ទ', 'អង្គារ',
    +    'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ',
    +    'សៅរ៍'],
    +  STANDALONESHORTWEEKDAYS: ['អាទិត្យ', 'ចន្ទ',
    +    'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍',
    +    'សុក្រ', 'សៅរ៍'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  SHORTQUARTERS: ['ត្រីមាសទី ១',
    +    'ត្រីមាសទី ២', 'ត្រីមាសទី ៣',
    +    'ត្រីមាសទី ៤'],
    +  QUARTERS: ['ត្រីមាសទី ១',
    +    'ត្រីមាសទី ២', 'ត្រីមាសទី ៣',
    +    'ត្រីមាសទី ៤'],
    +  AMPMS: ['ព្រឹក', 'ល្ងាច'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} នៅ {0}', '{1} នៅ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kn.
    + */
    +goog.i18n.DateTimeSymbols_kn = {
    +  ERAS: ['ಕ್ರಿ.ಪೂ', 'ಕ್ರಿ.ಶ'],
    +  ERANAMES: ['ಕ್ರಿಸ್ತ ಪೂರ್ವ',
    +    'ಕ್ರಿಸ್ತ ಶಕ'],
    +  NARROWMONTHS: ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ', 'ಜು',
    +    'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'],
    +  STANDALONENARROWMONTHS: ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ',
    +    'ಜು', 'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'],
    +  MONTHS: ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ',
    +    'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್',
    +    'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್',
    +    'ಡಿಸೆಂಬರ್'],
    +  STANDALONEMONTHS: ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ',
    +    'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್',
    +    'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್',
    +    'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್',
    +    'ಡಿಸೆಂಬರ್'],
    +  SHORTMONTHS: ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿ', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗ',
    +    'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ',
    +    'ಡಿಸೆಂ'],
    +  STANDALONESHORTMONTHS: ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿ', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗ',
    +    'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ',
    +    'ಡಿಸೆಂ'],
    +  WEEKDAYS: ['ಭಾನುವಾರ', 'ಸೋಮವಾರ',
    +    'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ',
    +    'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'],
    +  STANDALONEWEEKDAYS: ['ಭಾನುವಾರ', 'ಸೋಮವಾರ',
    +    'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ',
    +    'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'],
    +  SHORTWEEKDAYS: ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ', 'ಬುಧ',
    +    'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'],
    +  STANDALONESHORTWEEKDAYS: ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ',
    +    'ಬುಧ', 'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'],
    +  NARROWWEEKDAYS: ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು',
    +    'ಶ'],
    +  STANDALONENARROWWEEKDAYS: ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು',
    +    'ಶು', 'ಶ'],
    +  SHORTQUARTERS: ['ತ್ರೈ 1', 'ತ್ರೈ 2', 'ತ್ರೈ 3',
    +    'ತ್ರೈ 4'],
    +  QUARTERS: ['1ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '2ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '3ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '4ನೇ ತ್ರೈಮಾಸಿಕ'],
    +  AMPMS: ['ಪೂರ್ವಾಹ್ನ', 'ಅಪರಾಹ್ನ'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['hh:mm:ss a zzzz', 'hh:mm:ss a z', 'hh:mm:ss a', 'hh:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ko.
    + */
    +goog.i18n.DateTimeSymbols_ko = {
    +  ERAS: ['기원전', '서기'],
    +  ERANAMES: ['기원전', '서기'],
    +  NARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONENARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  MONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONEMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월',
    +    '8월', '9월', '10월', '11월', '12월'],
    +  SHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONESHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  WEEKDAYS: ['일요일', '월요일', '화요일', '수요일', '목요일',
    +    '금요일', '토요일'],
    +  STANDALONEWEEKDAYS: ['일요일', '월요일', '화요일', '수요일',
    +    '목요일', '금요일', '토요일'],
    +  SHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONESHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  NARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONENARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  SHORTQUARTERS: ['1분기', '2분기', '3분기', '4분기'],
    +  QUARTERS: ['제 1/4분기', '제 2/4분기', '제 3/4분기',
    +    '제 4/4분기'],
    +  AMPMS: ['오전', '오후'],
    +  DATEFORMATS: ['y년 M월 d일 EEEE', 'y년 M월 d일', 'y. M. d.',
    +    'yy. M. d.'],
    +  TIMEFORMATS: ['a h시 m분 s초 zzzz', 'a h시 m분 s초 z', 'a h:mm:ss',
    +    'a h:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ky.
    + */
    +goog.i18n.DateTimeSymbols_ky = {
    +  ERAS: ['б.з.ч.', 'б.з.'],
    +  ERANAMES: ['биздин заманга чейин',
    +    'биздин заман'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['январь', 'февраль', 'март', 'апрель',
    +    'май', 'июнь', 'июль', 'август', 'сентябрь',
    +    'октябрь', 'ноябрь', 'декабрь'],
    +  STANDALONEMONTHS: ['Январь', 'Февраль', 'Март',
    +    'Апрель', 'Май', 'Июнь', 'Июль', 'Август',
    +    'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
    +  SHORTMONTHS: ['янв.', 'фев.', 'мар.', 'апр.', 'май', 'июн.',
    +    'июл.', 'авг.', 'сен.', 'окт.', 'ноя.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['Янв', 'Фев', 'Мар', 'Апр', 'Май',
    +    'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
    +  WEEKDAYS: ['жекшемби', 'дүйшөмбү', 'шейшемби',
    +    'шаршемби', 'бейшемби', 'жума', 'ишемби'],
    +  STANDALONEWEEKDAYS: ['жекшемби', 'дүйшөмбү',
    +    'шейшемби', 'шаршемби', 'бейшемби', 'жума',
    +    'ишемби'],
    +  SHORTWEEKDAYS: ['жек.', 'дүй.', 'шейш.', 'шарш.', 'бейш.',
    +    'жума', 'ишм.'],
    +  STANDALONESHORTWEEKDAYS: ['жек.', 'дүй.', 'шейш.', 'шарш.',
    +    'бейш.', 'жума', 'ишм.'],
    +  NARROWWEEKDAYS: ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
    +  STANDALONENARROWWEEKDAYS: ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
    +  SHORTQUARTERS: ['1-чей.', '2-чей.', '3-чей.', '4-чей.'],
    +  QUARTERS: ['1-чейрек', '2-чейрек', '3-чейрек',
    +    '4-чейрек'],
    +  AMPMS: ['таңкы', 'түштөн кийин'],
    +  DATEFORMATS: ['EEEE, d-MMMM, y-\'ж\'.', 'y MMMM d', 'y MMM d', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ln.
    + */
    +goog.i18n.DateTimeSymbols_ln = {
    +  ERAS: ['libóso ya', 'nsima ya Y'],
    +  ERANAMES: ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
    +  NARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ',
    +    'n', 'd'],
    +  MONTHS: ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto',
    +    'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá',
    +    'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa',
    +    'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  STANDALONEMONTHS: ['sánzá ya yambo', 'sánzá ya míbalé',
    +    'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno',
    +    'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe',
    +    'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  SHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb',
    +    'ɔtb', 'nvb', 'dsb'],
    +  STANDALONESHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul',
    +    'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
    +  WEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  STANDALONEWEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  SHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  STANDALONESHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  NARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  SHORTQUARTERS: ['SM1', 'SM2', 'SM3', 'SM4'],
    +  QUARTERS: ['sánzá mísáto ya yambo', 'sánzá mísáto ya míbalé',
    +    'sánzá mísáto ya mísáto', 'sánzá mísáto ya mínei'],
    +  AMPMS: ['ntɔ́ngɔ́', 'mpókwa'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lo.
    + */
    +goog.i18n.DateTimeSymbols_lo = {
    +  ERAS: ['ກ່ອນ ຄ.ສ.', 'ຄ.ສ.'],
    +  ERANAMES: ['ກ່ອນຄຣິດສັກກະລາດ',
    +    'ຄຣິດສັກກະລາດ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ',
    +    'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ',
    +    'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ',
    +    'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
    +  STANDALONEMONTHS: ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ',
    +    'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ',
    +    'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ',
    +    'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
    +  SHORTMONTHS: ['ມ.ກ.', 'ກ.ພ.', 'ມ.ນ.', 'ມ.ສ.', 'ພ.ພ.',
    +    'ມິ.ຖ.', 'ກ.ລ.', 'ສ.ຫ.', 'ກ.ຍ.', 'ຕ.ລ.', 'ພ.ຈ.',
    +    'ທ.ວ.'],
    +  STANDALONESHORTMONTHS: ['ມ.ກ.', 'ກ.ພ.', 'ມ.ນ.', 'ມ.ສ.',
    +    'ພ.ພ.', 'ມິ.ຖ.', 'ກ.ລ.', 'ສ.ຫ.', 'ກ.ຍ.', 'ຕ.ລ.',
    +    'ພ.ຈ.', 'ທ.ວ.'],
    +  WEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  STANDALONEWEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  SHORTWEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  STANDALONESHORTWEEKDAYS: ['ອາທິດ', 'ຈັນ',
    +    'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ',
    +    'ເສົາ'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['ອ', 'ຈ', 'ອ', 'ພ', 'ພຫ', 'ສຸ',
    +    'ສ'],
    +  SHORTQUARTERS: ['ຕມ1', 'ຕມ2', 'ຕມ3', 'ຕມ4'],
    +  QUARTERS: ['ໄຕຣມາດ 1', 'ໄຕຣມາດ 2',
    +    'ໄຕຣມາດ 3', 'ໄຕຣມາດ 4'],
    +  AMPMS: ['ກ່ອນທ່ຽງ', 'ຫຼັງທ່ຽງ'],
    +  DATEFORMATS: ['EEEE ທີ d MMMM G y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['H ໂມງ m ນາທີ ss ວິນາທີ zzzz',
    +    'H ໂມງ m ນາທີ ss ວິນາທີ z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lt.
    + */
    +goog.i18n.DateTimeSymbols_lt = {
    +  ERAS: ['pr. Kr.', 'po Kr.'],
    +  ERANAMES: ['prieš Kristų', 'po Kristaus'],
    +  NARROWMONTHS: ['S', 'V', 'K', 'B', 'G', 'B', 'L', 'R', 'R', 'S', 'L', 'G'],
    +  STANDALONENARROWMONTHS: ['S', 'V', 'K', 'B', 'G', 'B', 'L', 'R', 'R', 'S',
    +    'L', 'G'],
    +  MONTHS: ['sausio', 'vasario', 'kovo', 'balandžio', 'gegužės', 'birželio',
    +    'liepos', 'rugpjūčio', 'rugsėjo', 'spalio', 'lapkričio', 'gruodžio'],
    +  STANDALONEMONTHS: ['sausis', 'vasaris', 'kovas', 'balandis', 'gegužė',
    +    'birželis', 'liepa', 'rugpjūtis', 'rugsėjis', 'spalis', 'lapkritis',
    +    'gruodis'],
    +  SHORTMONTHS: ['saus.', 'vas.', 'kov.', 'bal.', 'geg.', 'birž.', 'liep.',
    +    'rugp.', 'rugs.', 'spal.', 'lapkr.', 'gruod.'],
    +  STANDALONESHORTMONTHS: ['saus.', 'vas.', 'kov.', 'bal.', 'geg.', 'birž.',
    +    'liep.', 'rugp.', 'rugs.', 'spal.', 'lapkr.', 'gruod.'],
    +  WEEKDAYS: ['sekmadienis', 'pirmadienis', 'antradienis', 'trečiadienis',
    +    'ketvirtadienis', 'penktadienis', 'šeštadienis'],
    +  STANDALONEWEEKDAYS: ['sekmadienis', 'pirmadienis', 'antradienis',
    +    'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis'],
    +  SHORTWEEKDAYS: ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
    +  STANDALONESHORTWEEKDAYS: ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
    +  NARROWWEEKDAYS: ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
    +  SHORTQUARTERS: ['I k.', 'II k.', 'III k.', 'IV k.'],
    +  QUARTERS: ['I ketvirtis', 'II ketvirtis', 'III ketvirtis', 'IV ketvirtis'],
    +  AMPMS: ['priešpiet', 'popiet'],
    +  DATEFORMATS: ['y \'m\'. MMMM d \'d\'., EEEE', 'y \'m\'. MMMM d \'d\'.',
    +    'y-MM-dd', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lv.
    + */
    +goog.i18n.DateTimeSymbols_lv = {
    +  ERAS: ['p.m.ē.', 'm.ē.'],
    +  ERANAMES: ['pirms mūsu ēras', 'mūsu ērā'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvāris', 'februāris', 'marts', 'aprīlis', 'maijs', 'jūnijs',
    +    'jūlijs', 'augusts', 'septembris', 'oktobris', 'novembris', 'decembris'],
    +  STANDALONEMONTHS: ['Janvāris', 'Februāris', 'Marts', 'Aprīlis', 'Maijs',
    +    'Jūnijs', 'Jūlijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris',
    +    'Decembris'],
    +  SHORTMONTHS: ['janv.', 'febr.', 'marts', 'apr.', 'maijs', 'jūn.', 'jūl.',
    +    'aug.', 'sept.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Febr.', 'Marts', 'Apr.', 'Maijs', 'Jūn.',
    +    'Jūl.', 'Aug.', 'Sept.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['svētdiena', 'pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena',
    +    'piektdiena', 'sestdiena'],
    +  STANDALONEWEEKDAYS: ['Svētdiena', 'Pirmdiena', 'Otrdiena', 'Trešdiena',
    +    'Ceturtdiena', 'Piektdiena', 'Sestdiena'],
    +  SHORTWEEKDAYS: ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
    +  STANDALONESHORTWEEKDAYS: ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
    +  NARROWWEEKDAYS: ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
    +  SHORTQUARTERS: ['1. cet.', '2. cet.', '3. cet.', '4. cet.'],
    +  QUARTERS: ['1. ceturksnis', '2. ceturksnis', '3. ceturksnis',
    +    '4. ceturksnis'],
    +  AMPMS: ['priekšpusdienā', 'pēcpusdienā'],
    +  DATEFORMATS: ['EEEE, y. \'gada\' d. MMMM', 'y. \'gada\' d. MMMM',
    +    'y. \'gada\' d. MMM', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mk.
    + */
    +goog.i18n.DateTimeSymbols_mk = {
    +  ERAS: ['пр.н.е.', 'н.е.'],
    +  ERANAMES: ['пред нашата ера', 'од нашата ера'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануари', 'февруари', 'март', 'април',
    +    'мај', 'јуни', 'јули', 'август', 'септември',
    +    'октомври', 'ноември', 'декември'],
    +  STANDALONEMONTHS: ['јануари', 'февруари', 'март',
    +    'април', 'мај', 'јуни', 'јули', 'август',
    +    'септември', 'октомври', 'ноември',
    +    'декември'],
    +  SHORTMONTHS: ['јан.', 'фев.', 'мар.', 'апр.', 'мај', 'јун.',
    +    'јул.', 'авг.', 'септ.', 'окт.', 'ноем.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['јан.', 'фев.', 'мар.', 'апр.', 'мај',
    +    'јун.', 'јул.', 'авг.', 'септ.', 'окт.', 'ноем.',
    +    'дек.'],
    +  WEEKDAYS: ['недела', 'понеделник', 'вторник',
    +    'среда', 'четврток', 'петок', 'сабота'],
    +  STANDALONEWEEKDAYS: ['недела', 'понеделник', 'вторник',
    +    'среда', 'четврток', 'петок', 'сабота'],
    +  SHORTWEEKDAYS: ['нед.', 'пон.', 'вт.', 'сре.', 'чет.',
    +    'пет.', 'саб.'],
    +  STANDALONESHORTWEEKDAYS: ['нед.', 'пон.', 'вт.', 'сре.', 'чет.',
    +    'пет.', 'саб.'],
    +  NARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['јан-мар', 'апр-јун', 'јул-сеп',
    +    'окт-дек'],
    +  QUARTERS: ['прво тромесечје', 'второ тромесечје',
    +    'трето тромесечје', 'четврто тромесечје'],
    +  AMPMS: ['претпладне', 'попладне'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'dd MMMM y', 'dd.M.y', 'dd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ml.
    + */
    +goog.i18n.DateTimeSymbols_ml = {
    +  ERAS: ['ക്രി.മു.', 'എഡി'],
    +  ERANAMES: ['ക്രിസ്‌തുവിന് മുമ്പ്',
    +    'ആന്നോ ഡൊമിനി'],
    +  NARROWMONTHS: ['ജ', 'ഫ', 'മാ', 'ഏ', 'മെ', 'ജൂ', 'ജൂ',
    +    'ഓ', 'സ', 'ഒ', 'ന', 'ഡി'],
    +  STANDALONENARROWMONTHS: ['ജ', 'ഫ', 'മാ', 'ഏ', 'മേ', 'ജൂ',
    +    'ജൂ', 'ഓ', 'സ', 'ഒ', 'ന', 'ഡി'],
    +  MONTHS: ['ജനുവരി', 'ഫെബ്രുവരി',
    +    'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ',
    +    'ജൂലൈ', 'ആഗസ്റ്റ്',
    +    'സെപ്റ്റംബർ', 'ഒക്‌ടോബർ',
    +    'നവംബർ', 'ഡിസംബർ'],
    +  STANDALONEMONTHS: ['ജനുവരി', 'ഫെബ്രുവരി',
    +    'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ',
    +    'ജൂലൈ', 'ആഗസ്റ്റ്',
    +    'സെപ്റ്റംബർ', 'ഒക്‌ടോബർ',
    +    'നവംബർ', 'ഡിസംബർ'],
    +  SHORTMONTHS: ['ജനു', 'ഫെബ്രു', 'മാർ',
    +    'ഏപ്രി', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗ',
    +    'സെപ്റ്റം', 'ഒക്ടോ', 'നവം', 'ഡിസം'],
    +  STANDALONESHORTMONTHS: ['ജനു', 'ഫെബ്രു', 'മാർ',
    +    'ഏപ്രി', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗ',
    +    'സെപ്റ്റം', 'ഒക്ടോ', 'നവം', 'ഡിസം'],
    +  WEEKDAYS: ['ഞായറാഴ്‌ച', 'തിങ്കളാഴ്‌ച',
    +    'ചൊവ്വാഴ്ച', 'ബുധനാഴ്‌ച',
    +    'വ്യാഴാഴ്‌ച', 'വെള്ളിയാഴ്‌ച',
    +    'ശനിയാഴ്‌ച'],
    +  STANDALONEWEEKDAYS: ['ഞായറാഴ്‌ച',
    +    'തിങ്കളാഴ്‌ച', 'ചൊവ്വാഴ്‌ച',
    +    'ബുധനാഴ്‌ച', 'വ്യാഴാഴ്‌ച',
    +    'വെള്ളിയാഴ്‌ച', 'ശനിയാഴ്‌ച'],
    +  SHORTWEEKDAYS: ['ഞായർ', 'തിങ്കൾ', 'ചൊവ്വ',
    +    'ബുധൻ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
    +  STANDALONESHORTWEEKDAYS: ['ഞായർ', 'തിങ്കൾ',
    +    'ചൊവ്വ', 'ബുധൻ', 'വ്യാഴം',
    +    'വെള്ളി', 'ശനി'],
    +  NARROWWEEKDAYS: ['ഞ', 'തി', 'ച', 'ബു', 'വ', 'വെ', 'ശ'],
    +  STANDALONENARROWWEEKDAYS: ['ഞ', 'തി', 'ച', 'ബു', 'വ', 'വെ',
    +    'ശ'],
    +  SHORTQUARTERS: ['ഒന്നാം പാദം',
    +    'രണ്ടാം പാദം', 'മൂന്നാം പാദം',
    +    'നാലാം പാദം'],
    +  QUARTERS: ['ഒന്നാം പാദം',
    +    'രണ്ടാം പാദം', 'മൂന്നാം പാദം',
    +    'നാലാം പാദം'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y, MMMM d, EEEE', 'y, MMMM d', 'y, MMM d', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mn.
    + */
    +goog.i18n.DateTimeSymbols_mn = {
    +  ERAS: ['МЭӨ', 'МЭ'],
    +  ERANAMES: ['манай эриний өмнөх', 'манай эриний'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Нэгдүгээр сар', 'Хоёрдугаар сар',
    +    'Гуравдугаар сар', 'Дөрөвдүгээр сар',
    +    'Тавдугаар сар', 'Зургадугаар сар',
    +    'Долдугаар сар', 'Наймдугаар сар',
    +    'Есдүгээр сар', 'Аравдугаар сар',
    +    'Арван нэгдүгээр сар',
    +    'Арван хоёрдугаар сар'],
    +  STANDALONEMONTHS: ['Нэгдүгээр сар', 'Хоёрдугаар сар',
    +    'Гуравдугаар сар', 'Дөрөвдүгээр сар',
    +    'Тавдугаар сар', 'Зургадугаар сар',
    +    'Долдугаар сар', 'Наймдугаар сар',
    +    'Есдүгээр сар', 'Аравдугаар сар',
    +    'Арван нэгдүгээр сар',
    +    'Арван хоёрдугаар сар'],
    +  SHORTMONTHS: ['1-р сар', '2-р сар', '3-р сар', '4-р сар',
    +    '5-р сар', '6-р сар', '7-р сар', '8-р сар', '9-р сар',
    +    '10-р сар', '11-р сар', '12-р сар'],
    +  STANDALONESHORTMONTHS: ['1-р сар', '2-р сар', '3-р сар',
    +    '4-р сар', '5-р сар', '6-р сар', '7-р сар', '8-р сар',
    +    '9-р сар', '10-р сар', '11-р сар', '12-р сар'],
    +  WEEKDAYS: ['ням', 'даваа', 'мягмар', 'лхагва',
    +    'пүрэв', 'баасан', 'бямба'],
    +  STANDALONEWEEKDAYS: ['ням', 'даваа', 'мягмар', 'лхагва',
    +    'пүрэв', 'баасан', 'бямба'],
    +  SHORTWEEKDAYS: ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
    +  STANDALONESHORTWEEKDAYS: ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба',
    +    'Бя'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  SHORTQUARTERS: ['У1', 'У2', 'У3', 'У4'],
    +  QUARTERS: ['1-р улирал', '2-р улирал', '3-р улирал',
    +    '4-р улирал'],
    +  AMPMS: ['ҮӨ', 'ҮХ'],
    +  DATEFORMATS: ['EEEE, y \'оны\' MM \'сарын\' d',
    +    'y \'оны\' MM \'сарын\' d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mr.
    + */
    +goog.i18n.DateTimeSymbols_mr = {
    +  ZERODIGIT: 0x0966,
    +  ERAS: ['इ. स. पू.', 'इ. स.'],
    +  ERANAMES: ['ईसवीसनपूर्व', 'ईसवीसन'],
    +  NARROWMONTHS: ['जा', 'फे', 'मा', 'ए', 'मे', 'जू',
    +    'जु', 'ऑ', 'स', 'ऑ', 'नो', 'डि'],
    +  STANDALONENARROWMONTHS: ['जा', 'फे', 'मा', 'ए', 'मे',
    +    'जू', 'जु', 'ऑ', 'स', 'ऑ', 'नो', 'डि'],
    +  MONTHS: ['जानेवारी', 'फेब्रुवारी',
    +    'मार्च', 'एप्रिल', 'मे', 'जून',
    +    'जुलै', 'ऑगस्ट', 'सप्टेंबर',
    +    'ऑक्टोबर', 'नोव्हेंबर',
    +    'डिसेंबर'],
    +  STANDALONEMONTHS: ['जानेवारी',
    +    'फेब्रुवारी', 'मार्च', 'एप्रिल',
    +    'मे', 'जून', 'जुलै', 'ऑगस्ट',
    +    'सप्टेंबर', 'ऑक्टोबर',
    +    'नोव्हेंबर', 'डिसेंबर'],
    +  SHORTMONTHS: ['जाने', 'फेब्रु', 'मार्च',
    +    'एप्रि', 'मे', 'जून', 'जुलै', 'ऑग',
    +    'सप्टें', 'ऑक्टो', 'नोव्हें',
    +    'डिसें'],
    +  STANDALONESHORTMONTHS: ['जाने', 'फेब्रु',
    +    'मार्च', 'एप्रि', 'मे', 'जून', 'जुलै',
    +    'ऑग', 'सप्टें', 'ऑक्टो', 'नोव्हें',
    +    'डिसें'],
    +  WEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगळवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगळवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  SHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ', 'बुध',
    +    'गुरु', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ',
    +    'बुध', 'गुरु', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु', 'शु',
    +    'श'],
    +  STANDALONENARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['ति१', 'ति२', 'ति३', 'ति४'],
    +  QUARTERS: ['प्रथम तिमाही',
    +    'द्वितीय तिमाही',
    +    'तृतीय तिमाही',
    +    'चतुर्थ तिमाही'],
    +  AMPMS: ['म.पू.', 'म.उ.'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'रोजी\' {0}', '{1} \'रोजी\' {0}',
    +    '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ms.
    + */
    +goog.i18n.DateTimeSymbols_ms = {
    +  ERAS: ['S.M.', 'TM'],
    +  ERANAMES: ['S.M.', 'TM'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos',
    +    'September', 'Oktober', 'November', 'Disember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun',
    +    'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogo', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ogo', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['S1', 'S2', 'S3', 'S4'],
    +  QUARTERS: ['Suku pertama', 'Suku Ke-2', 'Suku Ke-3', 'Suku Ke-4'],
    +  AMPMS: ['PG', 'PTG'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mt.
    + */
    +goog.i18n.DateTimeSymbols_mt = {
    +  ERAS: ['QK', 'WK'],
    +  ERANAMES: ['Qabel Kristu', 'Wara Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'Ġ', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Jn', 'Fr', 'Mz', 'Ap', 'Mj', 'Ġn', 'Lj', 'Aw',
    +    'St', 'Ob', 'Nv', 'Dċ'],
    +  MONTHS: ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju', 'Lulju',
    +    'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'],
    +  STANDALONEMONTHS: ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju',
    +    'Lulju', 'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'],
    +  SHORTMONTHS: ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul', 'Aww', 'Set',
    +    'Ott', 'Nov', 'Diċ'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul',
    +    'Aww', 'Set', 'Ott', 'Nov', 'Diċ'],
    +  WEEKDAYS: ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa', 'Il-Ħamis',
    +    'Il-Ġimgħa', 'Is-Sibt'],
    +  STANDALONEWEEKDAYS: ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa',
    +    'Il-Ħamis', 'Il-Ġimgħa', 'Is-Sibt'],
    +  SHORTWEEKDAYS: ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
    +  STANDALONESHORTWEEKDAYS: ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
    +  NARROWWEEKDAYS: ['Ħ', 'T', 'T', 'E', 'Ħ', 'Ġ', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['Ħd', 'Tn', 'Tl', 'Er', 'Ħm', 'Ġm', 'Sb'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1el kwart', '2ni kwart', '3et kwart', '4ba’ kwart'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d \'ta\'’ MMMM y', 'd \'ta\'’ MMMM y', 'dd MMM y',
    +    'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale my.
    + */
    +goog.i18n.DateTimeSymbols_my = {
    +  ZERODIGIT: 0x1040,
    +  ERAS: ['ဘီစီ', 'အေဒီ'],
    +  ERANAMES: ['ခရစ်တော် မပေါ်မီကာလ',
    +    'ခရစ်တော် ပေါ်ထွန်းပြီးကာလ'],
    +  NARROWMONTHS: ['ဇ', 'ဖ', 'မ', 'ဧ', 'မ', 'ဇ', 'ဇ', 'ဩ', 'စ',
    +    'အ', 'န', 'ဒ'],
    +  STANDALONENARROWMONTHS: ['ဇ', 'ဖ', 'မ', 'ဧ', 'မ', 'ဇ', 'ဇ',
    +    'ဩ', 'စ', 'အ', 'န', 'ဒ'],
    +  MONTHS: ['ဇန်နဝါရီ', 'ဖေဖော်ဝါရီ',
    +    'မတ်', 'ဧပြီ', 'မေ', 'ဇွန်',
    +    'ဇူလိုင်', 'ဩဂုတ်', 'စက်တင်ဘာ',
    +    'အောက်တိုဘာ', 'နိုဝင်ဘာ',
    +    'ဒီဇင်ဘာ'],
    +  STANDALONEMONTHS: ['ဇန်နဝါရီ',
    +    'ဖေဖော်ဝါရီ', 'မတ်', 'ဧပြီ', 'မေ',
    +    'ဇွန်', 'ဇူလိုင်', 'ဩဂုတ်',
    +    'စက်တင်ဘာ', 'အောက်တိုဘာ',
    +    'နိုဝင်ဘာ', 'ဒီဇင်ဘာ'],
    +  SHORTMONTHS: ['ဇန်', 'ဖေ', 'မတ်', 'ဧပြီ', 'မေ',
    +    'ဇွန်', 'ဇူ', 'ဩ', 'စက်', 'အောက်',
    +    'နို', 'ဒီ'],
    +  STANDALONESHORTMONTHS: ['ဇန်', 'ဖေ', 'မတ်', 'ဧပြီ',
    +    'မေ', 'ဇွန်', 'ဇူ', 'ဩ', 'စက်', 'အောက်',
    +    'နို', 'ဒီ'],
    +  WEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  STANDALONEWEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  SHORTWEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  STANDALONESHORTWEEKDAYS: ['တနင်္ဂနွေ',
    +    'တနင်္လာ', 'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  NARROWWEEKDAYS: ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
    +  STANDALONENARROWWEEKDAYS: ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
    +  SHORTQUARTERS: ['ပထမ သုံးလပတ်',
    +    'ဒုတိယ သုံးလပတ်',
    +    'တတိယ သုံးလပတ်',
    +    'စတုတ္ထ သုံးလပတ်'],
    +  QUARTERS: ['ပထမ သုံးလပတ်',
    +    'ဒုတိယ သုံးလပတ်',
    +    'တတိယ သုံးလပတ်',
    +    'စတုတ္ထ သုံးလပတ်'],
    +  AMPMS: ['နံနက်', 'ညနေ'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}မှာ {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nb.
    + */
    +goog.i18n.DateTimeSymbols_nb = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'mai', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['sø.', 'ma.', 'ti.', 'on.', 'to.', 'fr.', 'lø.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} \'kl.\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ne.
    + */
    +goog.i18n.DateTimeSymbols_ne = {
    +  ZERODIGIT: 0x0966,
    +  ERAS: ['ईसा पूर्व', 'सन्'],
    +  ERANAMES: ['ईसा पूर्व', 'सन्'],
    +  NARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७', '८', '९',
    +    '१०', '११', '१२'],
    +  STANDALONENARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७',
    +    '८', '९', '१०', '११', '१२'],
    +  MONTHS: ['जनवरी', 'फेब्रुअरी', 'मार्च',
    +    'अप्रिल', 'मे', 'जुन', 'जुलाई',
    +    'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  STANDALONEMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  SHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  STANDALONESHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  WEEKDAYS: ['आइतबार', 'सोमबार',
    +    'मङ्गलबार', 'बुधबार', 'बिहीबार',
    +    'शुक्रबार', 'शनिबार'],
    +  STANDALONEWEEKDAYS: ['आइतबार', 'सोमबार',
    +    'मङ्गलबार', 'बुधबार', 'बिहीबार',
    +    'शुक्रबार', 'शनिबार'],
    +  SHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल', 'बुध',
    +    'बिही', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल',
    +    'बुध', 'बिही', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि', 'शु', 'श'],
    +  STANDALONENARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['पहिलो सत्र',
    +    'दोस्रो सत्र', 'तेस्रो सत्र',
    +    'चौथो सत्र'],
    +  QUARTERS: ['पहिलो सत्र', 'दोस्रो सत्र',
    +    'तेस्रो सत्र', 'चौथो सत्र'],
    +  AMPMS: ['पूर्व मध्यान्ह',
    +    'उत्तर मध्यान्ह'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl.
    + */
    +goog.i18n.DateTimeSymbols_nl = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale no.
    + */
    +goog.i18n.DateTimeSymbols_no = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'mai', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['sø.', 'ma.', 'ti.', 'on.', 'to.', 'fr.', 'lø.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} \'kl.\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale no_NO.
    + */
    +goog.i18n.DateTimeSymbols_no_NO = goog.i18n.DateTimeSymbols_no;
    +
    +
    +/**
    + * Date/time formatting symbols for locale or.
    + */
    +goog.i18n.DateTimeSymbols_or = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['ଜା', 'ଫେ', 'ମା', 'ଅ', 'ମଇ', 'ଜୁ',
    +    'ଜୁ', 'ଅ', 'ସେ', 'ଅ', 'ନ', 'ଡି'],
    +  STANDALONENARROWMONTHS: ['ଜା', 'ଫେ', 'ମା', 'ଅ', 'ମଇ',
    +    'ଜୁ', 'ଜୁ', 'ଅ', 'ସେ', 'ଅ', 'ନ', 'ଡି'],
    +  MONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  STANDALONEMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  SHORTMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  STANDALONESHORTMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  WEEKDAYS: ['ରବିବାର', 'ସୋମବାର',
    +    'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର',
    +    'ଶୁକ୍ରବାର', 'ଶନିବାର'],
    +  STANDALONEWEEKDAYS: ['ରବିବାର', 'ସୋମବାର',
    +    'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର',
    +    'ଶୁକ୍ରବାର', 'ଶନିବାର'],
    +  SHORTWEEKDAYS: ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ',
    +    'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
    +  STANDALONESHORTWEEKDAYS: ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ',
    +    'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
    +  NARROWWEEKDAYS: ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ', 'ଶୁ', 'ଶ'],
    +  STANDALONENARROWWEEKDAYS: ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ',
    +    'ଶୁ', 'ଶ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pa.
    + */
    +goog.i18n.DateTimeSymbols_pa = {
    +  ERAS: ['ਈ. ਪੂ.', 'ਸੰਨ'],
    +  ERANAMES: ['ਈਸਵੀ ਪੂਰਵ', 'ਈਸਵੀ ਸੰਨ'],
    +  NARROWMONTHS: ['ਜ', 'ਫ਼', 'ਮਾ', 'ਅ', 'ਮ', 'ਜੂ', 'ਜੁ',
    +    'ਅ', 'ਸ', 'ਅ', 'ਨ', 'ਦ'],
    +  STANDALONENARROWMONTHS: ['ਜ', 'ਫ਼', 'ਮਾ', 'ਅ', 'ਮ', 'ਜੂ',
    +    'ਜੁ', 'ਅ', 'ਸ', 'ਅ', 'ਨ', 'ਦ'],
    +  MONTHS: ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ',
    +    'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ',
    +    'ਦਸੰਬਰ'],
    +  STANDALONEMONTHS: ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ',
    +    'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ',
    +    'ਦਸੰਬਰ'],
    +  SHORTMONTHS: ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈ',
    +    'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ', 'ਸਤੰ',
    +    'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'],
    +  STANDALONESHORTMONTHS: ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ',
    +    'ਸਤੰ', 'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'],
    +  WEEKDAYS: ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ',
    +    'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ',
    +    'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'],
    +  STANDALONEWEEKDAYS: ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ',
    +    'ਮੰਗਲਵਾਰ', 'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ',
    +    'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'],
    +  SHORTWEEKDAYS: ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁੱਧ',
    +    'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'],
    +  STANDALONESHORTWEEKDAYS: ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ',
    +    'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'],
    +  NARROWWEEKDAYS: ['ਐ', 'ਸੋ', 'ਮੰ', 'ਬੁੱ', 'ਵੀ',
    +    'ਸ਼ੁੱ', 'ਸ਼'],
    +  STANDALONENARROWWEEKDAYS: ['ਐ', 'ਸੋ', 'ਮੰ', 'ਬੁੱ', 'ਵੀ',
    +    'ਸ਼ੁੱ', 'ਸ਼'],
    +  SHORTQUARTERS: ['ਤਿਮਾਹੀ1', 'ਤਿਮਾਹੀ2',
    +    'ਤਿਮਾਹੀ3', 'ਤਿਮਾਹੀ4'],
    +  QUARTERS: ['ਪਹਿਲੀ ਤਿਮਾਹੀ',
    +    'ਦੂਜੀ ਤਿਮਾਹੀ', 'ਤੀਜੀ ਤਿਮਾਹੀ',
    +    'ਚੌਥੀ ਤਿਮਾਹੀ'],
    +  AMPMS: ['ਪੂ.ਦੁ.', 'ਬਾ.ਦੁ.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pl.
    + */
    +goog.i18n.DateTimeSymbols_pl = {
    +  ERAS: ['p.n.e.', 'n.e.'],
    +  ERANAMES: ['p.n.e.', 'n.e.'],
    +  NARROWMONTHS: ['s', 'l', 'm', 'k', 'm', 'c', 'l', 's', 'w', 'p', 'l', 'g'],
    +  STANDALONENARROWMONTHS: ['s', 'l', 'm', 'k', 'm', 'c', 'l', 's', 'w', 'p',
    +    'l', 'g'],
    +  MONTHS: ['stycznia', 'lutego', 'marca', 'kwietnia', 'maja', 'czerwca',
    +    'lipca', 'sierpnia', 'września', 'października', 'listopada', 'grudnia'],
    +  STANDALONEMONTHS: ['styczeń', 'luty', 'marzec', 'kwiecień', 'maj',
    +    'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik', 'listopad',
    +    'grudzień'],
    +  SHORTMONTHS: ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz',
    +    'paź', 'lis', 'gru'],
    +  STANDALONESHORTMONTHS: ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip',
    +    'sie', 'wrz', 'paź', 'lis', 'gru'],
    +  WEEKDAYS: ['niedziela', 'poniedziałek', 'wtorek', 'środa', 'czwartek',
    +    'piątek', 'sobota'],
    +  STANDALONEWEEKDAYS: ['niedziela', 'poniedziałek', 'wtorek', 'środa',
    +    'czwartek', 'piątek', 'sobota'],
    +  SHORTWEEKDAYS: ['niedz.', 'pon.', 'wt.', 'śr.', 'czw.', 'pt.', 'sob.'],
    +  STANDALONESHORTWEEKDAYS: ['niedz.', 'pon.', 'wt.', 'śr.', 'czw.', 'pt.',
    +    'sob.'],
    +  NARROWWEEKDAYS: ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['I kwartał', 'II kwartał', 'III kwartał', 'IV kwartał'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt.
    + */
    +goog.i18n.DateTimeSymbols_pt = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['Antes de Cristo', 'Ano do Senhor'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho',
    +    'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul',
    +    'ago', 'set', 'out', 'nov', 'dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestre', '2º trimestre', '3º trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_BR.
    + */
    +goog.i18n.DateTimeSymbols_pt_BR = goog.i18n.DateTimeSymbols_pt;
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_PT.
    + */
    +goog.i18n.DateTimeSymbols_pt_PT = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ro.
    + */
    +goog.i18n.DateTimeSymbols_ro = {
    +  ERAS: ['î.Hr.', 'd.Hr.'],
    +  ERANAMES: ['înainte de Hristos', 'după Hristos'],
    +  NARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie',
    +    'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'],
    +  STANDALONEMONTHS: ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai',
    +    'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie',
    +    'Decembrie'],
    +  SHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.', 'aug.',
    +    'sept.', 'oct.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.',
    +    'aug.', 'sept.', 'oct.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['duminică', 'luni', 'marți', 'miercuri', 'joi', 'vineri',
    +    'sâmbătă'],
    +  STANDALONEWEEKDAYS: ['Duminică', 'Luni', 'Marți', 'Miercuri', 'Joi',
    +    'Vineri', 'Sâmbătă'],
    +  SHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  STANDALONESHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['trim. I', 'trim. II', 'trim. III', 'trim. IV'],
    +  QUARTERS: ['trimestrul I', 'trimestrul al II-lea', 'trimestrul al III-lea',
    +    'trimestrul al IV-lea'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru.
    + */
    +goog.i18n.DateTimeSymbols_ru = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale si.
    + */
    +goog.i18n.DateTimeSymbols_si = {
    +  ERAS: ['ක්‍රි.පූ.', 'ක්‍රි.ව.'],
    +  ERANAMES: ['ක්‍රිස්තු පූර්‍ව',
    +    'ක්‍රිස්තු වර්‍ෂ'],
    +  NARROWMONTHS: ['ජ', 'පෙ', 'මා', 'අ', 'මැ', 'ජූ', 'ජූ',
    +    'අ', 'සැ', 'ඔ', 'නෙ', 'දෙ'],
    +  STANDALONENARROWMONTHS: ['ජ', 'පෙ', 'මා', 'අ', 'මැ', 'ජූ',
    +    'ජූ', 'අ', 'සැ', 'ඔ', 'නෙ', 'දෙ'],
    +  MONTHS: ['ජනවාරි', 'පෙබරවාරි',
    +    'මාර්තු', 'අප්‍රේල්', 'මැයි',
    +    'ජූනි', 'ජූලි', 'අගෝස්තු',
    +    'සැප්තැම්බර්', 'ඔක්තෝබර්',
    +    'නොවැම්බර්', 'දෙසැම්බර්'],
    +  STANDALONEMONTHS: ['ජනවාරි', 'පෙබරවාරි',
    +    'මාර්තු', 'අප්‍රේල්', 'මැයි',
    +    'ජූනි', 'ජූලි', 'අගෝස්තු',
    +    'සැප්තැම්බර්', 'ඔක්තෝබර්',
    +    'නොවැම්බර්', 'දෙසැම්බර්'],
    +  SHORTMONTHS: ['ජන', 'පෙබ', 'මාර්තු',
    +    'අප්‍රේල්', 'මැයි', 'ජූනි', 'ජූලි',
    +    'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
    +  STANDALONESHORTMONTHS: ['ජන', 'පෙබ', 'මාර්',
    +    'අප්‍රේල්', 'මැයි', 'ජූනි', 'ජූලි',
    +    'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
    +  WEEKDAYS: ['ඉරිදා', 'සඳුදා',
    +    'අඟහරුවාදා', 'බදාදා',
    +    'බ්‍රහස්පතින්දා', 'සිකුරාදා',
    +    'සෙනසුරාදා'],
    +  STANDALONEWEEKDAYS: ['ඉරිදා', 'සඳුදා',
    +    'අඟහරුවාදා', 'බදාදා',
    +    'බ්‍රහස්පතින්දා', 'සිකුරාදා',
    +    'සෙනසුරාදා'],
    +  SHORTWEEKDAYS: ['ඉරිදා', 'සඳුදා', 'අඟහ',
    +    'බදාදා', 'බ්‍රහස්', 'සිකු', 'සෙන'],
    +  STANDALONESHORTWEEKDAYS: ['ඉරිදා', 'සඳුදා', 'අඟහ',
    +    'බදාදා', 'බ්‍රහස්', 'සිකු', 'සෙන'],
    +  NARROWWEEKDAYS: ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර', 'සි',
    +    'සෙ'],
    +  STANDALONENARROWWEEKDAYS: ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර',
    +    'සි', 'සෙ'],
    +  SHORTQUARTERS: ['කාර්:1', 'කාර්:2', 'කාර්:3',
    +    'කාර්:4'],
    +  QUARTERS: ['1 වන කාර්තුව', '2 වන කාර්තුව',
    +    '3 වන කාර්තුව', '4 වන කාර්තුව'],
    +  AMPMS: ['පෙ.ව.', 'ප.ව.'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['a h.mm.ss zzzz', 'a h.mm.ss z', 'a h.mm.ss', 'a h.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sk.
    + */
    +goog.i18n.DateTimeSymbols_sk = {
    +  ERAS: ['pred Kr.', 'po Kr.'],
    +  ERANAMES: ['pred Kristom', 'po Kristovi'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januára', 'februára', 'marca', 'apríla', 'mája', 'júna',
    +    'júla', 'augusta', 'septembra', 'októbra', 'novembra', 'decembra'],
    +  STANDALONEMONTHS: ['január', 'február', 'marec', 'apríl', 'máj', 'jún',
    +    'júl', 'august', 'september', 'október', 'november', 'december'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug',
    +    'sep', 'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok', 'piatok',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok',
    +    'piatok', 'sobota'],
    +  SHORTWEEKDAYS: ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. štvrťrok', '2. štvrťrok', '3. štvrťrok',
    +    '4. štvrťrok'],
    +  AMPMS: ['dopoludnia', 'odpoludnia'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. M. y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sl.
    + */
    +goog.i18n.DateTimeSymbols_sl = {
    +  ERAS: ['pr. n. št.', 'po Kr.'],
    +  ERANAMES: ['pred našim štetjem', 'naše štetje'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij',
    +    'avgust', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'marec', 'april', 'maj', 'junij',
    +    'julij', 'avgust', 'september', 'oktober', 'november', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'avg.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'avg', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek', 'petek',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek',
    +    'petek', 'sobota'],
    +  SHORTWEEKDAYS: ['ned.', 'pon.', 'tor.', 'sre.', 'čet.', 'pet.', 'sob.'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'tor', 'sre', 'čet', 'pet', 'sob'],
    +  NARROWWEEKDAYS: ['n', 'p', 't', 's', 'č', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 't', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. četrtletje', '2. četrtletje', '3. četrtletje',
    +    '4. četrtletje'],
    +  AMPMS: ['dop.', 'pop.'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y', 'dd. MMMM y', 'd. MMM y', 'd. MM. yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sq.
    + */
    +goog.i18n.DateTimeSymbols_sq = {
    +  ERAS: ['p.e.r.', 'e.r.'],
    +  ERANAMES: ['para erës së re', 'erës së re'],
    +  NARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T',
    +    'N', 'D'],
    +  MONTHS: ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik',
    +    'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'],
    +  STANDALONEMONTHS: ['Janar', 'Shkurt', 'Mars', 'Prill', 'Maj', 'Qershor',
    +    'Korrik', 'Gusht', 'Shtator', 'Tetor', 'Nëntor', 'Dhjetor'],
    +  SHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor', 'Gsh', 'Sht',
    +    'Tet', 'Nën', 'Dhj'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor',
    +    'Gsh', 'Sht', 'Tet', 'Nën', 'Dhj'],
    +  WEEKDAYS: ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte',
    +    'e premte', 'e shtunë'],
    +  STANDALONEWEEKDAYS: ['E diel', 'E hënë', 'E martë', 'E mërkurë',
    +    'E enjte', 'E premte', 'E shtunë'],
    +  SHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  STANDALONESHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  NARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  SHORTQUARTERS: ['tremujori I', 'tremujori II', 'tremujori III',
    +    'tremujori IV'],
    +  QUARTERS: ['tremujori i parë', 'tremujori i dytë', 'tremujori i tretë',
    +    'tremujori i katërt'],
    +  AMPMS: ['paradite', 'pasdite'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'në\' {0}', '{1} \'në\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr.
    + */
    +goog.i18n.DateTimeSymbols_sr = {
    +  ERAS: ['п. н. е.', 'н. е.'],
    +  ERANAMES: ['Пре нове ере', 'Нове ере'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануар', 'фебруар', 'март', 'април', 'мај',
    +    'јун', 'јул', 'август', 'септембар', 'октобар',
    +    'новембар', 'децембар'],
    +  STANDALONEMONTHS: ['јануар', 'фебруар', 'март', 'април',
    +    'мај', 'јун', 'јул', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  SHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај', 'јун',
    +    'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  STANDALONESHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај',
    +    'јун', 'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  WEEKDAYS: ['недеља', 'понедељак', 'уторак', 'среда',
    +    'четвртак', 'петак', 'субота'],
    +  STANDALONEWEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'среда', 'четвртак', 'петак', 'субота'],
    +  SHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сре', 'чет', 'пет',
    +    'суб'],
    +  STANDALONESHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сре', 'чет',
    +    'пет', 'суб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['К1', 'К2', 'К3', 'К4'],
    +  QUARTERS: ['Прво тромесечје', 'Друго тромесечје',
    +    'Треће тромесечје', 'Четврто тромесечје'],
    +  AMPMS: ['пре подне', 'по подне'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'dd.MM.y.', 'd.M.yy.'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sv.
    + */
    +goog.i18n.DateTimeSymbols_sv = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['före Kristus', 'efter Kristus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli',
    +    'augusti', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni',
    +    'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mars', 'apr.', 'maj', 'juni', 'juli', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mars', 'Apr.', 'Maj', 'Juni', 'Juli',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag',
    +    'lördag'],
    +  STANDALONEWEEKDAYS: ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag',
    +    'Fredag', 'Lördag'],
    +  SHORTWEEKDAYS: ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
    +  STANDALONESHORTWEEKDAYS: ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1:a kvartalet', '2:a kvartalet', '3:e kvartalet',
    +    '4:e kvartalet'],
    +  AMPMS: ['fm', 'em'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['\'kl\'. HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sw.
    + */
    +goog.i18n.DateTimeSymbols_sw = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kabla ya Kristo', 'Baada ya Kristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONESHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  QUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ta.
    + */
    +goog.i18n.DateTimeSymbols_ta = {
    +  ERAS: ['கி.மு.', 'கி.பி.'],
    +  ERANAMES: ['கிறிஸ்துவுக்கு முன்',
    +    'அனோ டோமினி'],
    +  NARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ', 'ஜூ',
    +    'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  STANDALONENARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ',
    +    'ஜூ', 'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  MONTHS: ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்',
    +    'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை',
    +    'ஆகஸ்ட்', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  STANDALONEMONTHS: ['ஜனவரி', 'பிப்ரவரி',
    +    'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்',
    +    'ஜூலை', 'ஆகஸ்டு', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  SHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.',
    +    'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.',
    +    'அக்.', 'நவ.', 'டிச.'],
    +  STANDALONESHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.',
    +    'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.',
    +    'செப்.', 'அக்.', 'நவ.', 'டிச.'],
    +  WEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  STANDALONEWEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  SHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONESHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  NARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONENARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  SHORTQUARTERS: ['காலாண்டு1', 'காலாண்டு2',
    +    'காலாண்டு3', 'காலாண்டு4'],
    +  QUARTERS: ['முதல் காலாண்டு',
    +    'இரண்டாம் காலாண்டு',
    +    'மூன்றாம் காலாண்டு',
    +    'நான்காம் காலாண்டு'],
    +  AMPMS: ['முற்பகல்', 'பிற்பகல்'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} ’அன்று’ {0}',
    +    '{1} ’அன்று’ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale te.
    + */
    +goog.i18n.DateTimeSymbols_te = {
    +  ERAS: ['క్రీపూ', 'క్రీశ'],
    +  ERANAMES: ['క్రీస్తు పూర్వం',
    +    'క్రీస్తు శకం'],
    +  NARROWMONTHS: ['జ', 'ఫి', 'మా', 'ఏ', 'మే', 'జూ', 'జు',
    +    'ఆ', 'సె', 'అ', 'న', 'డి'],
    +  STANDALONENARROWMONTHS: ['జ', 'ఫి', 'మా', 'ఏ', 'మే', 'జూ',
    +    'జు', 'ఆ', 'సె', 'అ', 'న', 'డి'],
    +  MONTHS: ['జనవరి', 'ఫిబ్రవరి', 'మార్చి',
    +    'ఏప్రిల్', 'మే', 'జూన్', 'జులై',
    +    'ఆగస్టు', 'సెప్టెంబర్',
    +    'అక్టోబర్', 'నవంబర్',
    +    'డిసెంబర్'],
    +  STANDALONEMONTHS: ['జనవరి', 'ఫిబ్రవరి',
    +    'మార్చి', 'ఏప్రిల్', 'మే', 'జూన్',
    +    'జులై', 'ఆగస్టు', 'సెప్టెంబర్',
    +    'అక్టోబర్', 'నవంబర్',
    +    'డిసెంబర్'],
    +  SHORTMONTHS: ['జన', 'ఫిబ్ర', 'మార్చి',
    +    'ఏప్రి', 'మే', 'జూన్', 'జులై', 'ఆగ',
    +    'సెప్టెం', 'అక్టో', 'నవం', 'డిసెం'],
    +  STANDALONESHORTMONTHS: ['జన', 'ఫిబ్ర', 'మార్చి',
    +    'ఏప్రి', 'మే', 'జూన్', 'జులై',
    +    'ఆగస్టు', 'సెప్టెం', 'అక్టో',
    +    'నవం', 'డిసెం'],
    +  WEEKDAYS: ['ఆదివారం', 'సోమవారం',
    +    'మంగళవారం', 'బుధవారం',
    +    'గురువారం', 'శుక్రవారం',
    +    'శనివారం'],
    +  STANDALONEWEEKDAYS: ['ఆదివారం', 'సోమవారం',
    +    'మంగళవారం', 'బుధవారం',
    +    'గురువారం', 'శుక్రవారం',
    +    'శనివారం'],
    +  SHORTWEEKDAYS: ['ఆది', 'సోమ', 'మంగళ', 'బుధ',
    +    'గురు', 'శుక్ర', 'శని'],
    +  STANDALONESHORTWEEKDAYS: ['ఆది', 'సోమ', 'మంగళ',
    +    'బుధ', 'గురు', 'శుక్ర', 'శని'],
    +  NARROWWEEKDAYS: ['ఆ', 'సో', 'మ', 'బు', 'గు', 'శు', 'శ'],
    +  STANDALONENARROWWEEKDAYS: ['ఆ', 'సో', 'మ', 'బు', 'గు',
    +    'శు', 'శ'],
    +  SHORTQUARTERS: ['త్రై1', 'త్రై2', 'త్రై3',
    +    'త్రై4'],
    +  QUARTERS: ['1వ త్రైమాసం', '2వ త్రైమాసం',
    +    '3వ త్రైమాసం', '4వ త్రైమాసం'],
    +  AMPMS: ['[AM]', '[PM]'],
    +  DATEFORMATS: ['d, MMMM y, EEEE', 'd MMMM, y', 'd MMM, y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale th.
    + */
    +goog.i18n.DateTimeSymbols_th = {
    +  ERAS: ['ปีก่อน ค.ศ.', 'ค.ศ.'],
    +  ERANAMES: ['ปีก่อนคริสต์ศักราช',
    +    'คริสต์ศักราช'],
    +  NARROWMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  STANDALONENARROWMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  MONTHS: ['มกราคม', 'กุมภาพันธ์',
    +    'มีนาคม', 'เมษายน', 'พฤษภาคม',
    +    'มิถุนายน', 'กรกฎาคม',
    +    'สิงหาคม', 'กันยายน', 'ตุลาคม',
    +    'พฤศจิกายน', 'ธันวาคม'],
    +  STANDALONEMONTHS: ['มกราคม', 'กุมภาพันธ์',
    +    'มีนาคม', 'เมษายน', 'พฤษภาคม',
    +    'มิถุนายน', 'กรกฎาคม',
    +    'สิงหาคม', 'กันยายน', 'ตุลาคม',
    +    'พฤศจิกายน', 'ธันวาคม'],
    +  SHORTMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  STANDALONESHORTMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  WEEKDAYS: ['วันอาทิตย์', 'วันจันทร์',
    +    'วันอังคาร', 'วันพุธ',
    +    'วันพฤหัสบดี', 'วันศุกร์',
    +    'วันเสาร์'],
    +  STANDALONEWEEKDAYS: ['วันอาทิตย์',
    +    'วันจันทร์', 'วันอังคาร',
    +    'วันพุธ', 'วันพฤหัสบดี',
    +    'วันศุกร์', 'วันเสาร์'],
    +  SHORTWEEKDAYS: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
    +  STANDALONESHORTWEEKDAYS: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.',
    +    'ศ.', 'ส.'],
    +  NARROWWEEKDAYS: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
    +  STANDALONENARROWWEEKDAYS: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ',
    +    'ส'],
    +  SHORTQUARTERS: ['ไตรมาส 1', 'ไตรมาส 2',
    +    'ไตรมาส 3', 'ไตรมาส 4'],
    +  QUARTERS: ['ไตรมาส 1', 'ไตรมาส 2',
    +    'ไตรมาส 3', 'ไตรมาส 4'],
    +  AMPMS: ['ก่อนเที่ยง', 'หลังเที่ยง'],
    +  DATEFORMATS: ['EEEEที่ d MMMM G y', 'd MMMM G y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: [
    +    'H นาฬิกา mm นาที ss วินาที zzzz',
    +    'H นาฬิกา mm นาที ss วินาที z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale tl.
    + */
    +goog.i18n.DateTimeSymbols_tl = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo',
    +    'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  STANDALONEMONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo',
    +    'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  SHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set',
    +    'Okt', 'Nob', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul',
    +    'Ago', 'Set', 'Okt', 'Nob', 'Dis'],
    +  WEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes', 'Biyernes',
    +    'Sabado'],
    +  STANDALONEWEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes',
    +    'Biyernes', 'Sabado'],
    +  SHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  NARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ika-1 quarter', 'ika-2 quarter', 'ika-3 quarter',
    +    'ika-4 na quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'nang\' {0}', '{1} \'nang\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale tr.
    + */
    +goog.i18n.DateTimeSymbols_tr = {
    +  ERAS: ['MÖ', 'MS'],
    +  ERANAMES: ['Milattan Önce', 'Milattan Sonra'],
    +  NARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E', 'K', 'A'],
    +  STANDALONENARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E',
    +    'K', 'A'],
    +  MONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz',
    +    'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  STANDALONEMONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
    +    'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  SHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl',
    +    'Eki', 'Kas', 'Ara'],
    +  STANDALONESHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem',
    +    'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
    +  WEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma',
    +    'Cumartesi'],
    +  STANDALONEWEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe',
    +    'Cuma', 'Cumartesi'],
    +  SHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  STANDALONESHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  NARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  SHORTQUARTERS: ['Ç1', 'Ç2', 'Ç3', 'Ç4'],
    +  QUARTERS: ['1. çeyrek', '2. çeyrek', '3. çeyrek', '4. çeyrek'],
    +  AMPMS: ['ÖÖ', 'ÖS'],
    +  DATEFORMATS: ['d MMMM y EEEE', 'd MMMM y', 'd MMM y', 'd MM y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uk.
    + */
    +goog.i18n.DateTimeSymbols_uk = {
    +  ERAS: ['до н.е.', 'н.е.'],
    +  ERANAMES: ['до нашої ери', 'нашої ери'],
    +  NARROWMONTHS: ['С', 'Л', 'Б', 'К', 'Т', 'Ч', 'Л', 'С', 'В', 'Ж',
    +    'Л', 'Г'],
    +  STANDALONENARROWMONTHS: ['С', 'Л', 'Б', 'К', 'Т', 'Ч', 'Л', 'С', 'В',
    +    'Ж', 'Л', 'Г'],
    +  MONTHS: ['січня', 'лютого', 'березня', 'квітня',
    +    'травня', 'червня', 'липня', 'серпня',
    +    'вересня', 'жовтня', 'листопада', 'грудня'],
    +  STANDALONEMONTHS: ['Січень', 'Лютий', 'Березень',
    +    'Квітень', 'Травень', 'Червень', 'Липень',
    +    'Серпень', 'Вересень', 'Жовтень', 'Листопад',
    +    'Грудень'],
    +  SHORTMONTHS: ['січ.', 'лют.', 'бер.', 'квіт.', 'трав.',
    +    'черв.', 'лип.', 'серп.', 'вер.', 'жовт.', 'лист.',
    +    'груд.'],
    +  STANDALONESHORTMONTHS: ['Січ', 'Лют', 'Бер', 'Кві', 'Тра',
    +    'Чер', 'Лип', 'Сер', 'Вер', 'Жов', 'Лис', 'Гру'],
    +  WEEKDAYS: ['неділя', 'понеділок', 'вівторок',
    +    'середа', 'четвер', 'пʼятниця', 'субота'],
    +  STANDALONEWEEKDAYS: ['Неділя', 'Понеділок', 'Вівторок',
    +    'Середа', 'Четвер', 'Пʼятниця', 'Субота'],
    +  SHORTWEEKDAYS: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
    +  STANDALONESHORTWEEKDAYS: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['I кв.', 'II кв.', 'III кв.', 'IV кв.'],
    +  QUARTERS: ['I квартал', 'II квартал', 'III квартал',
    +    'IV квартал'],
    +  AMPMS: ['дп', 'пп'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'р\'.', 'd MMMM y \'р\'.', 'd MMM y \'р\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ur.
    + */
    +goog.i18n.DateTimeSymbols_ur = {
    +  ERAS: ['ق م', 'عیسوی سن'],
    +  ERANAMES: ['قبل مسیح', 'عیسوی سن'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ', 'جمعرات',
    +    'جمعہ', 'ہفتہ'],
    +  STANDALONEWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  SHORTWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  STANDALONESHORTWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['پہلی سہ ماہی', 'دوسری سہ ماہی',
    +    'تیسری سہ ماہی', 'چوتهی سہ ماہی'],
    +  QUARTERS: ['پہلی سہ ماہی', 'دوسری سہ ماہی',
    +    'تیسری سہ ماہی', 'چوتهی سہ ماہی'],
    +  AMPMS: ['قبل دوپہر', 'بعد دوپہر'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'd MMM، y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz.
    + */
    +goog.i18n.DateTimeSymbols_uz = {
    +  ERAS: ['M.A.', 'E'],
    +  ERANAMES: ['M.A.', 'E'],
    +  NARROWMONTHS: ['Y', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Y', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust',
    +    'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
    +  STANDALONEMONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul',
    +    'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
    +  SHORTMONTHS: ['Yanv', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul', 'Avg',
    +    'Sen', 'Okt', 'Noya', 'Dek'],
    +  STANDALONESHORTMONTHS: ['Yanv', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul',
    +    'Avg', 'Sen', 'Okt', 'Noya', 'Dek'],
    +  WEEKDAYS: ['yakshanba', 'dushanba', 'seshanba', 'chorshanba', 'payshanba',
    +    'juma', 'shanba'],
    +  STANDALONEWEEKDAYS: ['yakshanba', 'dushanba', 'seshanba', 'chorshanba',
    +    'payshanba', 'juma', 'shanba'],
    +  SHORTWEEKDAYS: ['Yaksh', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum', 'Shan'],
    +  STANDALONESHORTWEEKDAYS: ['Yaksh', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum',
    +    'Shan'],
    +  NARROWWEEKDAYS: ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
    +  SHORTQUARTERS: ['1-ch', '2-ch', '3-ch', '4-ch'],
    +  QUARTERS: ['1-chorak', '2-chorak', '3-chorak', '4-chorak'],
    +  AMPMS: ['TO', 'TK'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vi.
    + */
    +goog.i18n.DateTimeSymbols_vi = {
    +  ERAS: ['tr. CN', 'sau CN'],
    +  ERANAMES: ['tr. CN', 'sau CN'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['tháng 1', 'tháng 2', 'tháng 3', 'tháng 4', 'tháng 5',
    +    'tháng 6', 'tháng 7', 'tháng 8', 'tháng 9', 'tháng 10', 'tháng 11',
    +    'tháng 12'],
    +  STANDALONEMONTHS: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5',
    +    'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11',
    +    'Tháng 12'],
    +  SHORTMONTHS: ['thg 1', 'thg 2', 'thg 3', 'thg 4', 'thg 5', 'thg 6', 'thg 7',
    +    'thg 8', 'thg 9', 'thg 10', 'thg 11', 'thg 12'],
    +  STANDALONESHORTMONTHS: ['Thg 1', 'Thg 2', 'Thg 3', 'Thg 4', 'Thg 5', 'Thg 6',
    +    'Thg 7', 'Thg 8', 'Thg 9', 'Thg 10', 'Thg 11', 'Thg 12'],
    +  WEEKDAYS: ['Chủ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm',
    +    'Thứ Sáu', 'Thứ Bảy'],
    +  STANDALONEWEEKDAYS: ['Chủ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư',
    +    'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'],
    +  SHORTWEEKDAYS: ['CN', 'Th 2', 'Th 3', 'Th 4', 'Th 5', 'Th 6', 'Th 7'],
    +  STANDALONESHORTWEEKDAYS: ['CN', 'Th 2', 'Th 3', 'Th 4', 'Th 5', 'Th 6',
    +    'Th 7'],
    +  NARROWWEEKDAYS: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
    +  STANDALONENARROWWEEKDAYS: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Quý 1', 'Quý 2', 'Quý 3', 'Quý 4'],
    +  AMPMS: ['SA', 'CH'],
    +  DATEFORMATS: ['EEEE, \'ngày\' dd MMMM \'năm\' y',
    +    '\'Ngày\' dd \'tháng\' MM \'năm\' y', 'dd-MM-y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{0} {1}', '{0} {1}', '{0} {1}', '{0} {1}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh.
    + */
    +goog.i18n.DateTimeSymbols_zh = {
    +  ERAS: ['公元前', '公元'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['一月', '二月', '三月', '四月', '五月', '六月', '七月',
    +    '八月', '九月', '十月', '十一月', '十二月'],
    +  STANDALONEMONTHS: ['一月', '二月', '三月', '四月', '五月', '六月',
    +    '七月', '八月', '九月', '十月', '十一月', '十二月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四', '周五',
    +    '周六'],
    +  STANDALONESHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四',
    +    '周五', '周六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季度', '2季度', '3季度', '4季度'],
    +  QUARTERS: ['第一季度', '第二季度', '第三季度', '第四季度'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'yy/M/d'],
    +  TIMEFORMATS: ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_CN.
    + */
    +goog.i18n.DateTimeSymbols_zh_CN = goog.i18n.DateTimeSymbols_zh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_HK.
    + */
    +goog.i18n.DateTimeSymbols_zh_HK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四', '週五',
    +    '週六'],
    +  STANDALONESHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四',
    +    '週五', '週六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['第1季', '第2季', '第3季', '第4季'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'd/M/yy'],
    +  TIMEFORMATS: ['ah:mm:ss [zzzz]', 'ah:mm:ss [z]', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_TW.
    + */
    +goog.i18n.DateTimeSymbols_zh_TW = {
    +  ERAS: ['西元前', '西元'],
    +  ERANAMES: ['西元前', '西元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四', '週五',
    +    '週六'],
    +  STANDALONESHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四',
    +    '週五', '週六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季', '2季', '3季', '4季'],
    +  QUARTERS: ['第1季', '第2季', '第3季', '第4季'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日 EEEE', 'y年M月d日', 'y年M月d日', 'y/M/d'],
    +  TIMEFORMATS: ['ah:mm:ss [zzzz]', 'ah:mm:ss [z]', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zu.
    + */
    +goog.i18n.DateTimeSymbols_zu = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januwari', 'Februwari', 'Mashi', 'Apreli', 'Meyi', 'Juni', 'Julayi',
    +    'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'],
    +  STANDALONEMONTHS: ['Januwari', 'Februwari', 'Mashi', 'Apreli', 'Meyi', 'Juni',
    +    'Julayi', 'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mas', 'Apr', 'Mey', 'Jun', 'Jul', 'Aga', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mas', 'Apr', 'Mey', 'Jun', 'Jul',
    +    'Aga', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Sonto', 'Msombuluko', 'Lwesibili', 'Lwesithathu', 'Lwesine',
    +    'Lwesihlanu', 'Mgqibelo'],
    +  STANDALONEWEEKDAYS: ['Sonto', 'Msombuluko', 'Lwesibili', 'Lwesithathu',
    +    'Lwesine', 'Lwesihlanu', 'Mgqibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'T', 'S', 'H', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'B', 'T', 'S', 'H', 'M'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ikota engu-1', 'ikota engu-2', 'ikota engu-3', 'ikota engu-4'],
    +  AMPMS: ['Ekuseni', 'Ntambama'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Selected date/time formatting symbols by locale.
    + * "switch" statement won't work here. JsCompiler cannot handle it yet.
    + */
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_af;
    +} else if (goog.LOCALE == 'am') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_am;
    +} else if (goog.LOCALE == 'ar') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar;
    +} else if (goog.LOCALE == 'az') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_az;
    +} else if (goog.LOCALE == 'bg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bg;
    +} else if (goog.LOCALE == 'bn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bn;
    +} else if (goog.LOCALE == 'br') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_br;
    +} else if (goog.LOCALE == 'ca') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca;
    +} else if (goog.LOCALE == 'chr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_chr;
    +} else if (goog.LOCALE == 'cs') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cs;
    +} else if (goog.LOCALE == 'cy') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cy;
    +} else if (goog.LOCALE == 'da') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_da;
    +} else if (goog.LOCALE == 'de') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +} else if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de_AT;
    +} else if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de;
    +} else if (goog.LOCALE == 'el') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_el;
    +} else if (goog.LOCALE == 'en') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +} else if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_AU;
    +} else if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GB;
    +} else if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IE;
    +} else if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IN;
    +} else if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SG;
    +} else if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +} else if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_ZA;
    +} else if (goog.LOCALE == 'es') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es;
    +} else if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_419;
    +} else if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es;
    +} else if (goog.LOCALE == 'et') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_et;
    +} else if (goog.LOCALE == 'eu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_eu;
    +} else if (goog.LOCALE == 'fa') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa;
    +} else if (goog.LOCALE == 'fi') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fi;
    +} else if (goog.LOCALE == 'fil') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fil;
    +} else if (goog.LOCALE == 'fr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
    +} else if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CA;
    +} else if (goog.LOCALE == 'ga') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
    +} else if (goog.LOCALE == 'gl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gl;
    +} else if (goog.LOCALE == 'gsw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gsw;
    +} else if (goog.LOCALE == 'gu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gu;
    +} else if (goog.LOCALE == 'haw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_haw;
    +} else if (goog.LOCALE == 'he') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_he;
    +} else if (goog.LOCALE == 'hi') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hi;
    +} else if (goog.LOCALE == 'hr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hr;
    +} else if (goog.LOCALE == 'hu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hu;
    +} else if (goog.LOCALE == 'hy') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hy;
    +} else if (goog.LOCALE == 'id') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_id;
    +} else if (goog.LOCALE == 'in') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_in;
    +} else if (goog.LOCALE == 'is') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_is;
    +} else if (goog.LOCALE == 'it') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_it;
    +} else if (goog.LOCALE == 'iw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_iw;
    +} else if (goog.LOCALE == 'ja') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ja;
    +} else if (goog.LOCALE == 'ka') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ka;
    +} else if (goog.LOCALE == 'kk') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kk;
    +} else if (goog.LOCALE == 'km') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_km;
    +} else if (goog.LOCALE == 'kn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kn;
    +} else if (goog.LOCALE == 'ko') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ko;
    +} else if (goog.LOCALE == 'ky') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ky;
    +} else if (goog.LOCALE == 'ln') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ln;
    +} else if (goog.LOCALE == 'lo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lo;
    +} else if (goog.LOCALE == 'lt') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lt;
    +} else if (goog.LOCALE == 'lv') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lv;
    +} else if (goog.LOCALE == 'mk') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mk;
    +} else if (goog.LOCALE == 'ml') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ml;
    +} else if (goog.LOCALE == 'mn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mn;
    +} else if (goog.LOCALE == 'mr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mr;
    +} else if (goog.LOCALE == 'ms') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ms;
    +} else if (goog.LOCALE == 'mt') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mt;
    +} else if (goog.LOCALE == 'my') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_my;
    +} else if (goog.LOCALE == 'nb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nb;
    +} else if (goog.LOCALE == 'ne') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ne;
    +} else if (goog.LOCALE == 'nl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl;
    +} else if (goog.LOCALE == 'no') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_no;
    +} else if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_no;
    +} else if (goog.LOCALE == 'or') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_or;
    +} else if (goog.LOCALE == 'pa') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pa;
    +} else if (goog.LOCALE == 'pl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pl;
    +} else if (goog.LOCALE == 'pt') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt;
    +} else if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt;
    +} else if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_PT;
    +} else if (goog.LOCALE == 'ro') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ro;
    +} else if (goog.LOCALE == 'ru') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru;
    +} else if (goog.LOCALE == 'si') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_si;
    +} else if (goog.LOCALE == 'sk') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sk;
    +} else if (goog.LOCALE == 'sl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sl;
    +} else if (goog.LOCALE == 'sq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sq;
    +} else if (goog.LOCALE == 'sr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr;
    +} else if (goog.LOCALE == 'sv') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sv;
    +} else if (goog.LOCALE == 'sw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sw;
    +} else if (goog.LOCALE == 'ta') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ta;
    +} else if (goog.LOCALE == 'te') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_te;
    +} else if (goog.LOCALE == 'th') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_th;
    +} else if (goog.LOCALE == 'tl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tl;
    +} else if (goog.LOCALE == 'tr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tr;
    +} else if (goog.LOCALE == 'uk') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uk;
    +} else if (goog.LOCALE == 'ur') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ur;
    +} else if (goog.LOCALE == 'uz') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz;
    +} else if (goog.LOCALE == 'vi') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vi;
    +} else if (goog.LOCALE == 'zh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh;
    +} else if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh;
    +} else if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_HK;
    +} else if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_TW;
    +} else if (goog.LOCALE == 'zu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zu;
    +} else {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbolsext.js b/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbolsext.js
    new file mode 100644
    index 0000000000..e6dda8f034
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/datetimesymbolsext.js
    @@ -0,0 +1,22754 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Date/time formatting symbols for all locales.
    + *
    + * This file is autogenerated by scripts
    + *   i18n/tools/generate_datetime_constants.py --for_closure
    + * File generated from CLDR ver. 26
    + *
    + * This file contains symbols for locales that are not covered by
    + * datetimesymbols.js.
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could correct CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes lands to CLDR.
    + */
    +
    +goog.provide('goog.i18n.DateTimeSymbolsExt');
    +goog.provide('goog.i18n.DateTimeSymbols_aa');
    +goog.provide('goog.i18n.DateTimeSymbols_aa_DJ');
    +goog.provide('goog.i18n.DateTimeSymbols_aa_ER');
    +goog.provide('goog.i18n.DateTimeSymbols_aa_ET');
    +goog.provide('goog.i18n.DateTimeSymbols_af_NA');
    +goog.provide('goog.i18n.DateTimeSymbols_af_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_agq');
    +goog.provide('goog.i18n.DateTimeSymbols_agq_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_ak');
    +goog.provide('goog.i18n.DateTimeSymbols_ak_GH');
    +goog.provide('goog.i18n.DateTimeSymbols_am_ET');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_001');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_AE');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_BH');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_DJ');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_DZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_EG');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_EH');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_ER');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_IL');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_IQ');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_JO');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_KM');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_KW');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_LB');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_LY');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_MR');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_OM');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_PS');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_QA');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_SA');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_SD');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_SO');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_SS');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_SY');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_TD');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_TN');
    +goog.provide('goog.i18n.DateTimeSymbols_ar_YE');
    +goog.provide('goog.i18n.DateTimeSymbols_as');
    +goog.provide('goog.i18n.DateTimeSymbols_as_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_asa');
    +goog.provide('goog.i18n.DateTimeSymbols_asa_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ast');
    +goog.provide('goog.i18n.DateTimeSymbols_ast_ES');
    +goog.provide('goog.i18n.DateTimeSymbols_az_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_az_Cyrl_AZ');
    +goog.provide('goog.i18n.DateTimeSymbols_az_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_az_Latn_AZ');
    +goog.provide('goog.i18n.DateTimeSymbols_bas');
    +goog.provide('goog.i18n.DateTimeSymbols_bas_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_be');
    +goog.provide('goog.i18n.DateTimeSymbols_be_BY');
    +goog.provide('goog.i18n.DateTimeSymbols_bem');
    +goog.provide('goog.i18n.DateTimeSymbols_bem_ZM');
    +goog.provide('goog.i18n.DateTimeSymbols_bez');
    +goog.provide('goog.i18n.DateTimeSymbols_bez_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_bg_BG');
    +goog.provide('goog.i18n.DateTimeSymbols_bm');
    +goog.provide('goog.i18n.DateTimeSymbols_bm_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_bm_Latn_ML');
    +goog.provide('goog.i18n.DateTimeSymbols_bn_BD');
    +goog.provide('goog.i18n.DateTimeSymbols_bn_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_bo');
    +goog.provide('goog.i18n.DateTimeSymbols_bo_CN');
    +goog.provide('goog.i18n.DateTimeSymbols_bo_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_br_FR');
    +goog.provide('goog.i18n.DateTimeSymbols_brx');
    +goog.provide('goog.i18n.DateTimeSymbols_brx_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_bs');
    +goog.provide('goog.i18n.DateTimeSymbols_bs_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_bs_Cyrl_BA');
    +goog.provide('goog.i18n.DateTimeSymbols_bs_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_bs_Latn_BA');
    +goog.provide('goog.i18n.DateTimeSymbols_ca_AD');
    +goog.provide('goog.i18n.DateTimeSymbols_ca_ES');
    +goog.provide('goog.i18n.DateTimeSymbols_ca_ES_VALENCIA');
    +goog.provide('goog.i18n.DateTimeSymbols_ca_FR');
    +goog.provide('goog.i18n.DateTimeSymbols_ca_IT');
    +goog.provide('goog.i18n.DateTimeSymbols_cgg');
    +goog.provide('goog.i18n.DateTimeSymbols_cgg_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_chr_US');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_Arab');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_Arab_IQ');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_Arab_IR');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_IQ');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_IR');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_ckb_Latn_IQ');
    +goog.provide('goog.i18n.DateTimeSymbols_cs_CZ');
    +goog.provide('goog.i18n.DateTimeSymbols_cy_GB');
    +goog.provide('goog.i18n.DateTimeSymbols_da_DK');
    +goog.provide('goog.i18n.DateTimeSymbols_da_GL');
    +goog.provide('goog.i18n.DateTimeSymbols_dav');
    +goog.provide('goog.i18n.DateTimeSymbols_dav_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_de_BE');
    +goog.provide('goog.i18n.DateTimeSymbols_de_DE');
    +goog.provide('goog.i18n.DateTimeSymbols_de_LI');
    +goog.provide('goog.i18n.DateTimeSymbols_de_LU');
    +goog.provide('goog.i18n.DateTimeSymbols_dje');
    +goog.provide('goog.i18n.DateTimeSymbols_dje_NE');
    +goog.provide('goog.i18n.DateTimeSymbols_dsb');
    +goog.provide('goog.i18n.DateTimeSymbols_dsb_DE');
    +goog.provide('goog.i18n.DateTimeSymbols_dua');
    +goog.provide('goog.i18n.DateTimeSymbols_dua_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_dyo');
    +goog.provide('goog.i18n.DateTimeSymbols_dyo_SN');
    +goog.provide('goog.i18n.DateTimeSymbols_dz');
    +goog.provide('goog.i18n.DateTimeSymbols_dz_BT');
    +goog.provide('goog.i18n.DateTimeSymbols_ebu');
    +goog.provide('goog.i18n.DateTimeSymbols_ebu_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_ee');
    +goog.provide('goog.i18n.DateTimeSymbols_ee_GH');
    +goog.provide('goog.i18n.DateTimeSymbols_ee_TG');
    +goog.provide('goog.i18n.DateTimeSymbols_el_CY');
    +goog.provide('goog.i18n.DateTimeSymbols_el_GR');
    +goog.provide('goog.i18n.DateTimeSymbols_en_001');
    +goog.provide('goog.i18n.DateTimeSymbols_en_150');
    +goog.provide('goog.i18n.DateTimeSymbols_en_AG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_AI');
    +goog.provide('goog.i18n.DateTimeSymbols_en_AS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BB');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BE');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BW');
    +goog.provide('goog.i18n.DateTimeSymbols_en_BZ');
    +goog.provide('goog.i18n.DateTimeSymbols_en_CA');
    +goog.provide('goog.i18n.DateTimeSymbols_en_CC');
    +goog.provide('goog.i18n.DateTimeSymbols_en_CK');
    +goog.provide('goog.i18n.DateTimeSymbols_en_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_CX');
    +goog.provide('goog.i18n.DateTimeSymbols_en_DG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_DM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_ER');
    +goog.provide('goog.i18n.DateTimeSymbols_en_FJ');
    +goog.provide('goog.i18n.DateTimeSymbols_en_FK');
    +goog.provide('goog.i18n.DateTimeSymbols_en_FM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GD');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GH');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GI');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GU');
    +goog.provide('goog.i18n.DateTimeSymbols_en_GY');
    +goog.provide('goog.i18n.DateTimeSymbols_en_HK');
    +goog.provide('goog.i18n.DateTimeSymbols_en_IM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_IO');
    +goog.provide('goog.i18n.DateTimeSymbols_en_JE');
    +goog.provide('goog.i18n.DateTimeSymbols_en_JM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_en_KI');
    +goog.provide('goog.i18n.DateTimeSymbols_en_KN');
    +goog.provide('goog.i18n.DateTimeSymbols_en_KY');
    +goog.provide('goog.i18n.DateTimeSymbols_en_LC');
    +goog.provide('goog.i18n.DateTimeSymbols_en_LR');
    +goog.provide('goog.i18n.DateTimeSymbols_en_LS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MH');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MO');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MP');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MT');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MU');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MW');
    +goog.provide('goog.i18n.DateTimeSymbols_en_MY');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NA');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NF');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NR');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NU');
    +goog.provide('goog.i18n.DateTimeSymbols_en_NZ');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PH');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PK');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PN');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PR');
    +goog.provide('goog.i18n.DateTimeSymbols_en_PW');
    +goog.provide('goog.i18n.DateTimeSymbols_en_RW');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SB');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SC');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SD');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SH');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SL');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SX');
    +goog.provide('goog.i18n.DateTimeSymbols_en_SZ');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TC');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TK');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TO');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TT');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TV');
    +goog.provide('goog.i18n.DateTimeSymbols_en_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_en_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_UM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_VC');
    +goog.provide('goog.i18n.DateTimeSymbols_en_VG');
    +goog.provide('goog.i18n.DateTimeSymbols_en_VI');
    +goog.provide('goog.i18n.DateTimeSymbols_en_VU');
    +goog.provide('goog.i18n.DateTimeSymbols_en_WS');
    +goog.provide('goog.i18n.DateTimeSymbols_en_ZM');
    +goog.provide('goog.i18n.DateTimeSymbols_en_ZW');
    +goog.provide('goog.i18n.DateTimeSymbols_eo');
    +goog.provide('goog.i18n.DateTimeSymbols_eo_001');
    +goog.provide('goog.i18n.DateTimeSymbols_es_AR');
    +goog.provide('goog.i18n.DateTimeSymbols_es_BO');
    +goog.provide('goog.i18n.DateTimeSymbols_es_CL');
    +goog.provide('goog.i18n.DateTimeSymbols_es_CO');
    +goog.provide('goog.i18n.DateTimeSymbols_es_CR');
    +goog.provide('goog.i18n.DateTimeSymbols_es_CU');
    +goog.provide('goog.i18n.DateTimeSymbols_es_DO');
    +goog.provide('goog.i18n.DateTimeSymbols_es_EA');
    +goog.provide('goog.i18n.DateTimeSymbols_es_EC');
    +goog.provide('goog.i18n.DateTimeSymbols_es_GQ');
    +goog.provide('goog.i18n.DateTimeSymbols_es_GT');
    +goog.provide('goog.i18n.DateTimeSymbols_es_HN');
    +goog.provide('goog.i18n.DateTimeSymbols_es_IC');
    +goog.provide('goog.i18n.DateTimeSymbols_es_MX');
    +goog.provide('goog.i18n.DateTimeSymbols_es_NI');
    +goog.provide('goog.i18n.DateTimeSymbols_es_PA');
    +goog.provide('goog.i18n.DateTimeSymbols_es_PE');
    +goog.provide('goog.i18n.DateTimeSymbols_es_PH');
    +goog.provide('goog.i18n.DateTimeSymbols_es_PR');
    +goog.provide('goog.i18n.DateTimeSymbols_es_PY');
    +goog.provide('goog.i18n.DateTimeSymbols_es_SV');
    +goog.provide('goog.i18n.DateTimeSymbols_es_US');
    +goog.provide('goog.i18n.DateTimeSymbols_es_UY');
    +goog.provide('goog.i18n.DateTimeSymbols_es_VE');
    +goog.provide('goog.i18n.DateTimeSymbols_et_EE');
    +goog.provide('goog.i18n.DateTimeSymbols_eu_ES');
    +goog.provide('goog.i18n.DateTimeSymbols_ewo');
    +goog.provide('goog.i18n.DateTimeSymbols_ewo_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_fa_AF');
    +goog.provide('goog.i18n.DateTimeSymbols_fa_IR');
    +goog.provide('goog.i18n.DateTimeSymbols_ff');
    +goog.provide('goog.i18n.DateTimeSymbols_ff_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_ff_GN');
    +goog.provide('goog.i18n.DateTimeSymbols_ff_MR');
    +goog.provide('goog.i18n.DateTimeSymbols_ff_SN');
    +goog.provide('goog.i18n.DateTimeSymbols_fi_FI');
    +goog.provide('goog.i18n.DateTimeSymbols_fil_PH');
    +goog.provide('goog.i18n.DateTimeSymbols_fo');
    +goog.provide('goog.i18n.DateTimeSymbols_fo_FO');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_BE');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_BF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_BI');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_BJ');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_BL');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CD');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CG');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CI');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_DJ');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_DZ');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_FR');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_GA');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_GF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_GN');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_GP');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_GQ');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_HT');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_KM');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_LU');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MC');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MG');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_ML');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MQ');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MR');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_MU');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_NC');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_NE');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_PF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_PM');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_RE');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_RW');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_SC');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_SN');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_SY');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_TD');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_TG');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_TN');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_VU');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_WF');
    +goog.provide('goog.i18n.DateTimeSymbols_fr_YT');
    +goog.provide('goog.i18n.DateTimeSymbols_fur');
    +goog.provide('goog.i18n.DateTimeSymbols_fur_IT');
    +goog.provide('goog.i18n.DateTimeSymbols_fy');
    +goog.provide('goog.i18n.DateTimeSymbols_fy_NL');
    +goog.provide('goog.i18n.DateTimeSymbols_ga_IE');
    +goog.provide('goog.i18n.DateTimeSymbols_gd');
    +goog.provide('goog.i18n.DateTimeSymbols_gd_GB');
    +goog.provide('goog.i18n.DateTimeSymbols_gl_ES');
    +goog.provide('goog.i18n.DateTimeSymbols_gsw_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_gsw_FR');
    +goog.provide('goog.i18n.DateTimeSymbols_gsw_LI');
    +goog.provide('goog.i18n.DateTimeSymbols_gu_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_guz');
    +goog.provide('goog.i18n.DateTimeSymbols_guz_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_gv');
    +goog.provide('goog.i18n.DateTimeSymbols_gv_IM');
    +goog.provide('goog.i18n.DateTimeSymbols_ha');
    +goog.provide('goog.i18n.DateTimeSymbols_ha_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_ha_Latn_GH');
    +goog.provide('goog.i18n.DateTimeSymbols_ha_Latn_NE');
    +goog.provide('goog.i18n.DateTimeSymbols_ha_Latn_NG');
    +goog.provide('goog.i18n.DateTimeSymbols_haw_US');
    +goog.provide('goog.i18n.DateTimeSymbols_he_IL');
    +goog.provide('goog.i18n.DateTimeSymbols_hi_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_hr_BA');
    +goog.provide('goog.i18n.DateTimeSymbols_hr_HR');
    +goog.provide('goog.i18n.DateTimeSymbols_hsb');
    +goog.provide('goog.i18n.DateTimeSymbols_hsb_DE');
    +goog.provide('goog.i18n.DateTimeSymbols_hu_HU');
    +goog.provide('goog.i18n.DateTimeSymbols_hy_AM');
    +goog.provide('goog.i18n.DateTimeSymbols_ia');
    +goog.provide('goog.i18n.DateTimeSymbols_ia_FR');
    +goog.provide('goog.i18n.DateTimeSymbols_id_ID');
    +goog.provide('goog.i18n.DateTimeSymbols_ig');
    +goog.provide('goog.i18n.DateTimeSymbols_ig_NG');
    +goog.provide('goog.i18n.DateTimeSymbols_ii');
    +goog.provide('goog.i18n.DateTimeSymbols_ii_CN');
    +goog.provide('goog.i18n.DateTimeSymbols_is_IS');
    +goog.provide('goog.i18n.DateTimeSymbols_it_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_it_IT');
    +goog.provide('goog.i18n.DateTimeSymbols_it_SM');
    +goog.provide('goog.i18n.DateTimeSymbols_ja_JP');
    +goog.provide('goog.i18n.DateTimeSymbols_jgo');
    +goog.provide('goog.i18n.DateTimeSymbols_jgo_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_jmc');
    +goog.provide('goog.i18n.DateTimeSymbols_jmc_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ka_GE');
    +goog.provide('goog.i18n.DateTimeSymbols_kab');
    +goog.provide('goog.i18n.DateTimeSymbols_kab_DZ');
    +goog.provide('goog.i18n.DateTimeSymbols_kam');
    +goog.provide('goog.i18n.DateTimeSymbols_kam_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_kde');
    +goog.provide('goog.i18n.DateTimeSymbols_kde_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_kea');
    +goog.provide('goog.i18n.DateTimeSymbols_kea_CV');
    +goog.provide('goog.i18n.DateTimeSymbols_khq');
    +goog.provide('goog.i18n.DateTimeSymbols_khq_ML');
    +goog.provide('goog.i18n.DateTimeSymbols_ki');
    +goog.provide('goog.i18n.DateTimeSymbols_ki_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_kk_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_kk_Cyrl_KZ');
    +goog.provide('goog.i18n.DateTimeSymbols_kkj');
    +goog.provide('goog.i18n.DateTimeSymbols_kkj_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_kl');
    +goog.provide('goog.i18n.DateTimeSymbols_kl_GL');
    +goog.provide('goog.i18n.DateTimeSymbols_kln');
    +goog.provide('goog.i18n.DateTimeSymbols_kln_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_km_KH');
    +goog.provide('goog.i18n.DateTimeSymbols_kn_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ko_KP');
    +goog.provide('goog.i18n.DateTimeSymbols_ko_KR');
    +goog.provide('goog.i18n.DateTimeSymbols_kok');
    +goog.provide('goog.i18n.DateTimeSymbols_kok_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ks');
    +goog.provide('goog.i18n.DateTimeSymbols_ks_Arab');
    +goog.provide('goog.i18n.DateTimeSymbols_ks_Arab_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ksb');
    +goog.provide('goog.i18n.DateTimeSymbols_ksb_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ksf');
    +goog.provide('goog.i18n.DateTimeSymbols_ksf_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_ksh');
    +goog.provide('goog.i18n.DateTimeSymbols_ksh_DE');
    +goog.provide('goog.i18n.DateTimeSymbols_kw');
    +goog.provide('goog.i18n.DateTimeSymbols_kw_GB');
    +goog.provide('goog.i18n.DateTimeSymbols_ky_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_ky_Cyrl_KG');
    +goog.provide('goog.i18n.DateTimeSymbols_lag');
    +goog.provide('goog.i18n.DateTimeSymbols_lag_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_lb');
    +goog.provide('goog.i18n.DateTimeSymbols_lb_LU');
    +goog.provide('goog.i18n.DateTimeSymbols_lg');
    +goog.provide('goog.i18n.DateTimeSymbols_lg_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_lkt');
    +goog.provide('goog.i18n.DateTimeSymbols_lkt_US');
    +goog.provide('goog.i18n.DateTimeSymbols_ln_AO');
    +goog.provide('goog.i18n.DateTimeSymbols_ln_CD');
    +goog.provide('goog.i18n.DateTimeSymbols_ln_CF');
    +goog.provide('goog.i18n.DateTimeSymbols_ln_CG');
    +goog.provide('goog.i18n.DateTimeSymbols_lo_LA');
    +goog.provide('goog.i18n.DateTimeSymbols_lt_LT');
    +goog.provide('goog.i18n.DateTimeSymbols_lu');
    +goog.provide('goog.i18n.DateTimeSymbols_lu_CD');
    +goog.provide('goog.i18n.DateTimeSymbols_luo');
    +goog.provide('goog.i18n.DateTimeSymbols_luo_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_luy');
    +goog.provide('goog.i18n.DateTimeSymbols_luy_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_lv_LV');
    +goog.provide('goog.i18n.DateTimeSymbols_mas');
    +goog.provide('goog.i18n.DateTimeSymbols_mas_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_mas_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_mer');
    +goog.provide('goog.i18n.DateTimeSymbols_mer_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_mfe');
    +goog.provide('goog.i18n.DateTimeSymbols_mfe_MU');
    +goog.provide('goog.i18n.DateTimeSymbols_mg');
    +goog.provide('goog.i18n.DateTimeSymbols_mg_MG');
    +goog.provide('goog.i18n.DateTimeSymbols_mgh');
    +goog.provide('goog.i18n.DateTimeSymbols_mgh_MZ');
    +goog.provide('goog.i18n.DateTimeSymbols_mgo');
    +goog.provide('goog.i18n.DateTimeSymbols_mgo_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_mk_MK');
    +goog.provide('goog.i18n.DateTimeSymbols_ml_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_mn_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_mn_Cyrl_MN');
    +goog.provide('goog.i18n.DateTimeSymbols_mr_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ms_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_ms_Latn_BN');
    +goog.provide('goog.i18n.DateTimeSymbols_ms_Latn_MY');
    +goog.provide('goog.i18n.DateTimeSymbols_ms_Latn_SG');
    +goog.provide('goog.i18n.DateTimeSymbols_mt_MT');
    +goog.provide('goog.i18n.DateTimeSymbols_mua');
    +goog.provide('goog.i18n.DateTimeSymbols_mua_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_my_MM');
    +goog.provide('goog.i18n.DateTimeSymbols_naq');
    +goog.provide('goog.i18n.DateTimeSymbols_naq_NA');
    +goog.provide('goog.i18n.DateTimeSymbols_nb_NO');
    +goog.provide('goog.i18n.DateTimeSymbols_nb_SJ');
    +goog.provide('goog.i18n.DateTimeSymbols_nd');
    +goog.provide('goog.i18n.DateTimeSymbols_nd_ZW');
    +goog.provide('goog.i18n.DateTimeSymbols_ne_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ne_NP');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_AW');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_BE');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_BQ');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_CW');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_NL');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_SR');
    +goog.provide('goog.i18n.DateTimeSymbols_nl_SX');
    +goog.provide('goog.i18n.DateTimeSymbols_nmg');
    +goog.provide('goog.i18n.DateTimeSymbols_nmg_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_nn');
    +goog.provide('goog.i18n.DateTimeSymbols_nn_NO');
    +goog.provide('goog.i18n.DateTimeSymbols_nnh');
    +goog.provide('goog.i18n.DateTimeSymbols_nnh_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_nr');
    +goog.provide('goog.i18n.DateTimeSymbols_nr_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_nso');
    +goog.provide('goog.i18n.DateTimeSymbols_nso_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_nus');
    +goog.provide('goog.i18n.DateTimeSymbols_nus_SD');
    +goog.provide('goog.i18n.DateTimeSymbols_nyn');
    +goog.provide('goog.i18n.DateTimeSymbols_nyn_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_om');
    +goog.provide('goog.i18n.DateTimeSymbols_om_ET');
    +goog.provide('goog.i18n.DateTimeSymbols_om_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_or_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_os');
    +goog.provide('goog.i18n.DateTimeSymbols_os_GE');
    +goog.provide('goog.i18n.DateTimeSymbols_os_RU');
    +goog.provide('goog.i18n.DateTimeSymbols_pa_Arab');
    +goog.provide('goog.i18n.DateTimeSymbols_pa_Arab_PK');
    +goog.provide('goog.i18n.DateTimeSymbols_pa_Guru');
    +goog.provide('goog.i18n.DateTimeSymbols_pa_Guru_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_pl_PL');
    +goog.provide('goog.i18n.DateTimeSymbols_ps');
    +goog.provide('goog.i18n.DateTimeSymbols_ps_AF');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_AO');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_CV');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_GW');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_MO');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_MZ');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_ST');
    +goog.provide('goog.i18n.DateTimeSymbols_pt_TL');
    +goog.provide('goog.i18n.DateTimeSymbols_qu');
    +goog.provide('goog.i18n.DateTimeSymbols_qu_BO');
    +goog.provide('goog.i18n.DateTimeSymbols_qu_EC');
    +goog.provide('goog.i18n.DateTimeSymbols_qu_PE');
    +goog.provide('goog.i18n.DateTimeSymbols_rm');
    +goog.provide('goog.i18n.DateTimeSymbols_rm_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_rn');
    +goog.provide('goog.i18n.DateTimeSymbols_rn_BI');
    +goog.provide('goog.i18n.DateTimeSymbols_ro_MD');
    +goog.provide('goog.i18n.DateTimeSymbols_ro_RO');
    +goog.provide('goog.i18n.DateTimeSymbols_rof');
    +goog.provide('goog.i18n.DateTimeSymbols_rof_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_BY');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_KG');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_KZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_MD');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_RU');
    +goog.provide('goog.i18n.DateTimeSymbols_ru_UA');
    +goog.provide('goog.i18n.DateTimeSymbols_rw');
    +goog.provide('goog.i18n.DateTimeSymbols_rw_RW');
    +goog.provide('goog.i18n.DateTimeSymbols_rwk');
    +goog.provide('goog.i18n.DateTimeSymbols_rwk_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_sah');
    +goog.provide('goog.i18n.DateTimeSymbols_sah_RU');
    +goog.provide('goog.i18n.DateTimeSymbols_saq');
    +goog.provide('goog.i18n.DateTimeSymbols_saq_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_sbp');
    +goog.provide('goog.i18n.DateTimeSymbols_sbp_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_se');
    +goog.provide('goog.i18n.DateTimeSymbols_se_FI');
    +goog.provide('goog.i18n.DateTimeSymbols_se_NO');
    +goog.provide('goog.i18n.DateTimeSymbols_se_SE');
    +goog.provide('goog.i18n.DateTimeSymbols_seh');
    +goog.provide('goog.i18n.DateTimeSymbols_seh_MZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ses');
    +goog.provide('goog.i18n.DateTimeSymbols_ses_ML');
    +goog.provide('goog.i18n.DateTimeSymbols_sg');
    +goog.provide('goog.i18n.DateTimeSymbols_sg_CF');
    +goog.provide('goog.i18n.DateTimeSymbols_shi');
    +goog.provide('goog.i18n.DateTimeSymbols_shi_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_shi_Latn_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_shi_Tfng');
    +goog.provide('goog.i18n.DateTimeSymbols_shi_Tfng_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_si_LK');
    +goog.provide('goog.i18n.DateTimeSymbols_sk_SK');
    +goog.provide('goog.i18n.DateTimeSymbols_sl_SI');
    +goog.provide('goog.i18n.DateTimeSymbols_smn');
    +goog.provide('goog.i18n.DateTimeSymbols_smn_FI');
    +goog.provide('goog.i18n.DateTimeSymbols_sn');
    +goog.provide('goog.i18n.DateTimeSymbols_sn_ZW');
    +goog.provide('goog.i18n.DateTimeSymbols_so');
    +goog.provide('goog.i18n.DateTimeSymbols_so_DJ');
    +goog.provide('goog.i18n.DateTimeSymbols_so_ET');
    +goog.provide('goog.i18n.DateTimeSymbols_so_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_so_SO');
    +goog.provide('goog.i18n.DateTimeSymbols_sq_AL');
    +goog.provide('goog.i18n.DateTimeSymbols_sq_MK');
    +goog.provide('goog.i18n.DateTimeSymbols_sq_XK');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Cyrl_BA');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Cyrl_ME');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Cyrl_RS');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Cyrl_XK');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Latn_BA');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Latn_ME');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Latn_RS');
    +goog.provide('goog.i18n.DateTimeSymbols_sr_Latn_XK');
    +goog.provide('goog.i18n.DateTimeSymbols_ss');
    +goog.provide('goog.i18n.DateTimeSymbols_ss_SZ');
    +goog.provide('goog.i18n.DateTimeSymbols_ss_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_ssy');
    +goog.provide('goog.i18n.DateTimeSymbols_ssy_ER');
    +goog.provide('goog.i18n.DateTimeSymbols_sv_AX');
    +goog.provide('goog.i18n.DateTimeSymbols_sv_FI');
    +goog.provide('goog.i18n.DateTimeSymbols_sv_SE');
    +goog.provide('goog.i18n.DateTimeSymbols_sw_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_sw_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_sw_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_swc');
    +goog.provide('goog.i18n.DateTimeSymbols_swc_CD');
    +goog.provide('goog.i18n.DateTimeSymbols_ta_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ta_LK');
    +goog.provide('goog.i18n.DateTimeSymbols_ta_MY');
    +goog.provide('goog.i18n.DateTimeSymbols_ta_SG');
    +goog.provide('goog.i18n.DateTimeSymbols_te_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_teo');
    +goog.provide('goog.i18n.DateTimeSymbols_teo_KE');
    +goog.provide('goog.i18n.DateTimeSymbols_teo_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_th_TH');
    +goog.provide('goog.i18n.DateTimeSymbols_ti');
    +goog.provide('goog.i18n.DateTimeSymbols_ti_ER');
    +goog.provide('goog.i18n.DateTimeSymbols_ti_ET');
    +goog.provide('goog.i18n.DateTimeSymbols_tn');
    +goog.provide('goog.i18n.DateTimeSymbols_tn_BW');
    +goog.provide('goog.i18n.DateTimeSymbols_tn_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_to');
    +goog.provide('goog.i18n.DateTimeSymbols_to_TO');
    +goog.provide('goog.i18n.DateTimeSymbols_tr_CY');
    +goog.provide('goog.i18n.DateTimeSymbols_tr_TR');
    +goog.provide('goog.i18n.DateTimeSymbols_ts');
    +goog.provide('goog.i18n.DateTimeSymbols_ts_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_twq');
    +goog.provide('goog.i18n.DateTimeSymbols_twq_NE');
    +goog.provide('goog.i18n.DateTimeSymbols_tzm');
    +goog.provide('goog.i18n.DateTimeSymbols_tzm_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_tzm_Latn_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_ug');
    +goog.provide('goog.i18n.DateTimeSymbols_ug_Arab');
    +goog.provide('goog.i18n.DateTimeSymbols_ug_Arab_CN');
    +goog.provide('goog.i18n.DateTimeSymbols_uk_UA');
    +goog.provide('goog.i18n.DateTimeSymbols_ur_IN');
    +goog.provide('goog.i18n.DateTimeSymbols_ur_PK');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Arab');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Arab_AF');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Cyrl');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Cyrl_UZ');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_uz_Latn_UZ');
    +goog.provide('goog.i18n.DateTimeSymbols_vai');
    +goog.provide('goog.i18n.DateTimeSymbols_vai_Latn');
    +goog.provide('goog.i18n.DateTimeSymbols_vai_Latn_LR');
    +goog.provide('goog.i18n.DateTimeSymbols_vai_Vaii');
    +goog.provide('goog.i18n.DateTimeSymbols_vai_Vaii_LR');
    +goog.provide('goog.i18n.DateTimeSymbols_ve');
    +goog.provide('goog.i18n.DateTimeSymbols_ve_ZA');
    +goog.provide('goog.i18n.DateTimeSymbols_vi_VN');
    +goog.provide('goog.i18n.DateTimeSymbols_vo');
    +goog.provide('goog.i18n.DateTimeSymbols_vo_001');
    +goog.provide('goog.i18n.DateTimeSymbols_vun');
    +goog.provide('goog.i18n.DateTimeSymbols_vun_TZ');
    +goog.provide('goog.i18n.DateTimeSymbols_wae');
    +goog.provide('goog.i18n.DateTimeSymbols_wae_CH');
    +goog.provide('goog.i18n.DateTimeSymbols_xog');
    +goog.provide('goog.i18n.DateTimeSymbols_xog_UG');
    +goog.provide('goog.i18n.DateTimeSymbols_yav');
    +goog.provide('goog.i18n.DateTimeSymbols_yav_CM');
    +goog.provide('goog.i18n.DateTimeSymbols_yi');
    +goog.provide('goog.i18n.DateTimeSymbols_yi_001');
    +goog.provide('goog.i18n.DateTimeSymbols_yo');
    +goog.provide('goog.i18n.DateTimeSymbols_yo_BJ');
    +goog.provide('goog.i18n.DateTimeSymbols_yo_NG');
    +goog.provide('goog.i18n.DateTimeSymbols_zgh');
    +goog.provide('goog.i18n.DateTimeSymbols_zgh_MA');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hans');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hans_CN');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hans_HK');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hans_MO');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hans_SG');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hant');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hant_HK');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hant_MO');
    +goog.provide('goog.i18n.DateTimeSymbols_zh_Hant_TW');
    +goog.provide('goog.i18n.DateTimeSymbols_zu_ZA');
    +
    +goog.require('goog.i18n.DateTimeSymbols');
    +
    +/**
    + * Date/time formatting symbols for locale aa.
    + */
    +goog.i18n.DateTimeSymbols_aa = {
    +  ERAS: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  ERANAMES: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  NARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D', 'X', 'K'],
    +  STANDALONENARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D',
    +    'X', 'K'],
    +  MONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis', 'Caxah Alsa',
    +    'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli',
    +    'Kaxxa Garablu'],
    +  STANDALONEMONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis',
    +    'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli',
    +    'Ximoli', 'Kaxxa Garablu'],
    +  SHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way',
    +    'Dit', 'Xim', 'Kax'],
    +  STANDALONESHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad',
    +    'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
    +  WEEKDAYS: ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata',
    +    'Sabti'],
    +  STANDALONEWEEKDAYS: ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi',
    +    'Gumqata', 'Sabti'],
    +  SHORTWEEKDAYS: ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'E', 'T', 'A', 'K', 'G', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'E', 'T', 'A', 'K', 'G', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['saaku', 'carra'],
    +  DATEFORMATS: ['EEEE, MMMM dd, y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale aa_DJ.
    + */
    +goog.i18n.DateTimeSymbols_aa_DJ = {
    +  ERAS: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  ERANAMES: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  NARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D', 'X', 'K'],
    +  STANDALONENARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D',
    +    'X', 'K'],
    +  MONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis', 'Caxah Alsa',
    +    'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli', 'Ximoli',
    +    'Kaxxa Garablu'],
    +  STANDALONEMONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis',
    +    'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Leqeeni', 'Waysu', 'Diteli',
    +    'Ximoli', 'Kaxxa Garablu'],
    +  SHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way',
    +    'Dit', 'Xim', 'Kax'],
    +  STANDALONESHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad',
    +    'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
    +  WEEKDAYS: ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi', 'Gumqata',
    +    'Sabti'],
    +  STANDALONEWEEKDAYS: ['Acaada', 'Etleeni', 'Talaata', 'Arbaqa', 'Kamiisi',
    +    'Gumqata', 'Sabti'],
    +  SHORTWEEKDAYS: ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Aca', 'Etl', 'Tal', 'Arb', 'Kam', 'Gum', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'E', 'T', 'A', 'K', 'G', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'E', 'T', 'A', 'K', 'G', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['saaku', 'carra'],
    +  DATEFORMATS: ['EEEE, MMMM dd, y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale aa_ER.
    + */
    +goog.i18n.DateTimeSymbols_aa_ER = goog.i18n.DateTimeSymbols_aa;
    +
    +
    +/**
    + * Date/time formatting symbols for locale aa_ET.
    + */
    +goog.i18n.DateTimeSymbols_aa_ET = goog.i18n.DateTimeSymbols_aa;
    +
    +
    +/**
    + * Date/time formatting symbols for locale af_NA.
    + */
    +goog.i18n.DateTimeSymbols_af_NA = {
    +  ERAS: ['v.C.', 'n.C.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie',
    +    'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie',
    +    'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug',
    +    'Sep', 'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag',
    +    'Saterdag'],
    +  STANDALONEWEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrydag', 'Saterdag'],
    +  SHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1ste kwartaal', '2de kwartaal', '3de kwartaal', '4de kwartaal'],
    +  AMPMS: ['vm.', 'nm.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale af_ZA.
    + */
    +goog.i18n.DateTimeSymbols_af_ZA = {
    +  ERAS: ['v.C.', 'n.C.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie',
    +    'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie',
    +    'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug',
    +    'Sep', 'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag',
    +    'Saterdag'],
    +  STANDALONEWEEKDAYS: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrydag', 'Saterdag'],
    +  SHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1ste kwartaal', '2de kwartaal', '3de kwartaal', '4de kwartaal'],
    +  AMPMS: ['vm.', 'nm.'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'dd MMMM y', 'dd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale agq.
    + */
    +goog.i18n.DateTimeSymbols_agq = {
    +  ERAS: ['SK', 'BK'],
    +  ERANAMES: ['Sěe Kɨ̀lesto', 'Bǎa Kɨ̀lesto'],
    +  NARROWMONTHS: ['n', 'k', 't', 't', 's', 'z', 'k', 'f', 'd', 'l', 'c', 'f'],
    +  STANDALONENARROWMONTHS: ['n', 'k', 't', 't', 's', 'z', 'k', 'f', 'd', 'l',
    +    'c', 'f'],
    +  MONTHS: ['ndzɔ̀ŋɔ̀nùm', 'ndzɔ̀ŋɔ̀kƗ̀zùʔ',
    +    'ndzɔ̀ŋɔ̀tƗ̀dʉ̀ghà', 'ndzɔ̀ŋɔ̀tǎafʉ̄ghā',
    +    'ndzɔ̀ŋèsèe', 'ndzɔ̀ŋɔ̀nzùghò', 'ndzɔ̀ŋɔ̀dùmlo',
    +    'ndzɔ̀ŋɔ̀kwîfɔ̀e', 'ndzɔ̀ŋɔ̀tƗ̀fʉ̀ghàdzughù',
    +    'ndzɔ̀ŋɔ̀ghǔuwelɔ̀m', 'ndzɔ̀ŋɔ̀chwaʔàkaa wo',
    +    'ndzɔ̀ŋèfwòo'],
    +  STANDALONEMONTHS: ['ndzɔ̀ŋɔ̀nùm', 'ndzɔ̀ŋɔ̀kƗ̀zùʔ',
    +    'ndzɔ̀ŋɔ̀tƗ̀dʉ̀ghà', 'ndzɔ̀ŋɔ̀tǎafʉ̄ghā',
    +    'ndzɔ̀ŋèsèe', 'ndzɔ̀ŋɔ̀nzùghò', 'ndzɔ̀ŋɔ̀dùmlo',
    +    'ndzɔ̀ŋɔ̀kwîfɔ̀e', 'ndzɔ̀ŋɔ̀tƗ̀fʉ̀ghàdzughù',
    +    'ndzɔ̀ŋɔ̀ghǔuwelɔ̀m', 'ndzɔ̀ŋɔ̀chwaʔàkaa wo',
    +    'ndzɔ̀ŋèfwòo'],
    +  SHORTMONTHS: ['nùm', 'kɨz', 'tɨd', 'taa', 'see', 'nzu', 'dum', 'fɔe',
    +    'dzu', 'lɔm', 'kaa', 'fwo'],
    +  STANDALONESHORTMONTHS: ['nùm', 'kɨz', 'tɨd', 'taa', 'see', 'nzu', 'dum',
    +    'fɔe', 'dzu', 'lɔm', 'kaa', 'fwo'],
    +  WEEKDAYS: ['tsuʔntsɨ', 'tsuʔukpà', 'tsuʔughɔe', 'tsuʔutɔ̀mlò',
    +    'tsuʔumè', 'tsuʔughɨ̂m', 'tsuʔndzɨkɔʔɔ'],
    +  STANDALONEWEEKDAYS: ['tsuʔntsɨ', 'tsuʔukpà', 'tsuʔughɔe',
    +    'tsuʔutɔ̀mlò', 'tsuʔumè', 'tsuʔughɨ̂m', 'tsuʔndzɨkɔʔɔ'],
    +  SHORTWEEKDAYS: ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'],
    +  STANDALONESHORTWEEKDAYS: ['nts', 'kpa', 'ghɔ', 'tɔm', 'ume', 'ghɨ', 'dzk'],
    +  NARROWWEEKDAYS: ['n', 'k', 'g', 't', 'u', 'g', 'd'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'k', 'g', 't', 'u', 'g', 'd'],
    +  SHORTQUARTERS: ['kɨbâ kɨ 1', 'ugbâ u 2', 'ugbâ u 3', 'ugbâ u 4'],
    +  QUARTERS: ['kɨbâ kɨ 1', 'ugbâ u 2', 'ugbâ u 3', 'ugbâ u 4'],
    +  AMPMS: ['a.g', 'a.k'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale agq_CM.
    + */
    +goog.i18n.DateTimeSymbols_agq_CM = goog.i18n.DateTimeSymbols_agq;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ak.
    + */
    +goog.i18n.DateTimeSymbols_ak = {
    +  ERAS: ['AK', 'KE'],
    +  ERANAMES: ['Ansa Kristo', 'Kristo Ekyiri'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Sanda-Ɔpɛpɔn', 'Kwakwar-Ɔgyefuo', 'Ebɔw-Ɔbenem',
    +    'Ebɔbira-Oforisuo', 'Esusow Aketseaba-Kɔtɔnimba', 'Obirade-Ayɛwohomumu',
    +    'Ayɛwoho-Kitawonsa', 'Difuu-Ɔsandaa', 'Fankwa-Ɛbɔ', 'Ɔbɛsɛ-Ahinime',
    +    'Ɔberɛfɛw-Obubuo', 'Mumu-Ɔpɛnimba'],
    +  STANDALONEMONTHS: ['Sanda-Ɔpɛpɔn', 'Kwakwar-Ɔgyefuo', 'Ebɔw-Ɔbenem',
    +    'Ebɔbira-Oforisuo', 'Esusow Aketseaba-Kɔtɔnimba', 'Obirade-Ayɛwohomumu',
    +    'Ayɛwoho-Kitawonsa', 'Difuu-Ɔsandaa', 'Fankwa-Ɛbɔ', 'Ɔbɛsɛ-Ahinime',
    +    'Ɔberɛfɛw-Obubuo', 'Mumu-Ɔpɛnimba'],
    +  SHORTMONTHS: ['S-Ɔ', 'K-Ɔ', 'E-Ɔ', 'E-O', 'E-K', 'O-A', 'A-K', 'D-Ɔ',
    +    'F-Ɛ', 'Ɔ-A', 'Ɔ-O', 'M-Ɔ'],
    +  STANDALONESHORTMONTHS: ['S-Ɔ', 'K-Ɔ', 'E-Ɔ', 'E-O', 'E-K', 'O-A', 'A-K',
    +    'D-Ɔ', 'F-Ɛ', 'Ɔ-A', 'Ɔ-O', 'M-Ɔ'],
    +  WEEKDAYS: ['Kwesida', 'Dwowda', 'Benada', 'Wukuda', 'Yawda', 'Fida',
    +    'Memeneda'],
    +  STANDALONEWEEKDAYS: ['Kwesida', 'Dwowda', 'Benada', 'Wukuda', 'Yawda', 'Fida',
    +    'Memeneda'],
    +  SHORTWEEKDAYS: ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'],
    +  STANDALONESHORTWEEKDAYS: ['Kwe', 'Dwo', 'Ben', 'Wuk', 'Yaw', 'Fia', 'Mem'],
    +  NARROWWEEKDAYS: ['K', 'D', 'B', 'W', 'Y', 'F', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'D', 'B', 'W', 'Y', 'F', 'M'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AN', 'EW'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ak_GH.
    + */
    +goog.i18n.DateTimeSymbols_ak_GH = goog.i18n.DateTimeSymbols_ak;
    +
    +
    +/**
    + * Date/time formatting symbols for locale am_ET.
    + */
    +goog.i18n.DateTimeSymbols_am_ET = {
    +  ERAS: ['ዓ/ዓ', 'ዓ/ም'],
    +  ERANAMES: ['ዓመተ ዓለም', 'ዓመተ ምሕረት'],
    +  NARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ', 'ኦ', 'ሴ',
    +    'ኦ', 'ኖ', 'ዲ'],
    +  STANDALONENARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ',
    +    'ኦ', 'ሴ', 'ኦ', 'ኖ', 'ዲ'],
    +  MONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕሪል',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር',
    +    'ኦክቶበር', 'ኖቬምበር', 'ዲሴምበር'],
    +  STANDALONEMONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች',
    +    'ኤፕሪል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት',
    +    'ሴፕቴምበር', 'ኦክቶበር', 'ኖቬምበር',
    +    'ዲሴምበር'],
    +  SHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕሪ', 'ሜይ',
    +    'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክቶ', 'ኖቬም',
    +    'ዲሴም'],
    +  STANDALONESHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕሪ',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክቶ',
    +    'ኖቬም', 'ዲሴም'],
    +  WEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ',
    +    'ዓርብ', 'ቅዳሜ'],
    +  STANDALONEWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ',
    +    'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
    +  SHORTWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ',
    +    'ዓርብ', 'ቅዳሜ'],
    +  STANDALONESHORTWEEKDAYS: ['እሑድ', 'ሰኞ', 'ማክሰ', 'ረቡዕ',
    +    'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
    +  NARROWWEEKDAYS: ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
    +  STANDALONENARROWWEEKDAYS: ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
    +  SHORTQUARTERS: ['ሩብ1', 'ሩብ2', 'ሩብ3', 'ሩብ4'],
    +  QUARTERS: ['1ኛው ሩብ', 'ሁለተኛው ሩብ', '3ኛው ሩብ',
    +    '4ኛው ሩብ'],
    +  AMPMS: ['ጥዋት', 'ከሰዓት'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_001.
    + */
    +goog.i18n.DateTimeSymbols_ar_001 = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_AE.
    + */
    +goog.i18n.DateTimeSymbols_ar_AE = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_BH.
    + */
    +goog.i18n.DateTimeSymbols_ar_BH = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_DJ.
    + */
    +goog.i18n.DateTimeSymbols_ar_DJ = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_DZ.
    + */
    +goog.i18n.DateTimeSymbols_ar_DZ = {
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ج', 'ف', 'م', 'أ', 'م', 'ج', 'ج', 'أ', 'س', 'أ',
    +    'ن', 'د'],
    +  STANDALONENARROWMONTHS: ['ج', 'ف', 'م', 'أ', 'م', 'ج', 'ج', 'أ', 'س',
    +    'أ', 'ن', 'د'],
    +  MONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل', 'ماي',
    +    'جوان', 'جويلية', 'أوت', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل',
    +    'ماي', 'جوان', 'جويلية', 'أوت', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل', 'ماي',
    +    'جوان', 'جويلية', 'أوت', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل',
    +    'ماي', 'جوان', 'جويلية', 'أوت', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'y/MM/dd', 'y/M/d'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_EG.
    + */
    +goog.i18n.DateTimeSymbols_ar_EG = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_EH.
    + */
    +goog.i18n.DateTimeSymbols_ar_EH = {
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_ER.
    + */
    +goog.i18n.DateTimeSymbols_ar_ER = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_IL.
    + */
    +goog.i18n.DateTimeSymbols_ar_IL = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_IQ.
    + */
    +goog.i18n.DateTimeSymbols_ar_IQ = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ', 'ت',
    +    'ت', 'ك'],
    +  STANDALONENARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ',
    +    'ت', 'ت', 'ك'],
    +  MONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONEMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  SHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرین الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONESHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_JO.
    + */
    +goog.i18n.DateTimeSymbols_ar_JO = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ', 'ت',
    +    'ت', 'ك'],
    +  STANDALONENARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ',
    +    'ت', 'ت', 'ك'],
    +  MONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONEMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  SHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONESHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_KM.
    + */
    +goog.i18n.DateTimeSymbols_ar_KM = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_KW.
    + */
    +goog.i18n.DateTimeSymbols_ar_KW = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_LB.
    + */
    +goog.i18n.DateTimeSymbols_ar_LB = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ', 'ت',
    +    'ت', 'ك'],
    +  STANDALONENARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ',
    +    'ت', 'ت', 'ك'],
    +  MONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONEMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  SHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONESHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'MMM d, y', 'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_LY.
    + */
    +goog.i18n.DateTimeSymbols_ar_LY = {
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_MA.
    + */
    +goog.i18n.DateTimeSymbols_ar_MA = {
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'م', 'ن', 'ل', 'غ', 'ش', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'م', 'ن', 'ل', 'غ', 'ش',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'ماي',
    +    'يونيو', 'يوليوز', 'غشت', 'شتنبر', 'أكتوبر',
    +    'نونبر', 'دجنبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'ماي', 'يونيو', 'يوليوز', 'غشت', 'شتنبر',
    +    'أكتوبر', 'نونبر', 'دجنبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'ماي', 'يونيو', 'يوليوز', 'غشت', 'شتنبر',
    +    'أكتوبر', 'نونبر', 'دجنبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'ماي', 'يونيو', 'يوليوز', 'غشت',
    +    'شتنبر', 'أكتوبر', 'نونبر', 'دجنبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'y/MM/dd', 'y/M/d'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_MR.
    + */
    +goog.i18n.DateTimeSymbols_ar_MR = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'إ', 'و', 'ن', 'ل', 'غ', 'ش', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'إ', 'و', 'ن', 'ل', 'غ', 'ش',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'إبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغشت', 'شتمبر', 'أكتوبر',
    +    'نوفمبر', 'دجمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'إبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغشت', 'شتمبر',
    +    'أكتوبر', 'نوفمبر', 'دجمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'إبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغشت', 'شتمبر',
    +    'أكتوبر', 'نوفمبر', 'دجمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'إبريل', 'مايو', 'يونيو', 'يوليو', 'أغشت',
    +    'شتمبر', 'أكتوبر', 'نوفمبر', 'دجمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_OM.
    + */
    +goog.i18n.DateTimeSymbols_ar_OM = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_PS.
    + */
    +goog.i18n.DateTimeSymbols_ar_PS = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ', 'ت',
    +    'ت', 'ك'],
    +  STANDALONENARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ',
    +    'ت', 'ت', 'ك'],
    +  MONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONEMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  SHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONESHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_QA.
    + */
    +goog.i18n.DateTimeSymbols_ar_QA = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_SA.
    + */
    +goog.i18n.DateTimeSymbols_ar_SA = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_SD.
    + */
    +goog.i18n.DateTimeSymbols_ar_SD = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_SO.
    + */
    +goog.i18n.DateTimeSymbols_ar_SO = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_SS.
    + */
    +goog.i18n.DateTimeSymbols_ar_SS = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_SY.
    + */
    +goog.i18n.DateTimeSymbols_ar_SY = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ', 'ت',
    +    'ت', 'ك'],
    +  STANDALONENARROWMONTHS: ['ك', 'ش', 'آ', 'ن', 'أ', 'ح', 'ت', 'آ', 'أ',
    +    'ت', 'ت', 'ك'],
    +  MONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONEMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  SHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار', 'نيسان',
    +    'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  STANDALONESHORTMONTHS: ['كانون الثاني', 'شباط', 'آذار',
    +    'نيسان', 'أيار', 'حزيران', 'تموز', 'آب', 'أيلول',
    +    'تشرين الأول', 'تشرين الثاني',
    +    'كانون الأول'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_TD.
    + */
    +goog.i18n.DateTimeSymbols_ar_TD = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_TN.
    + */
    +goog.i18n.DateTimeSymbols_ar_TN = {
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ج', 'ف', 'م', 'أ', 'م', 'ج', 'ج', 'أ', 'س', 'أ',
    +    'ن', 'د'],
    +  STANDALONENARROWMONTHS: ['ج', 'ف', 'م', 'أ', 'م', 'ج', 'ج', 'أ', 'س',
    +    'أ', 'ن', 'د'],
    +  MONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل', 'ماي',
    +    'جوان', 'جويلية', 'أوت', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل',
    +    'ماي', 'جوان', 'جويلية', 'أوت', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل', 'ماي',
    +    'جوان', 'جويلية', 'أوت', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['جانفي', 'فيفري', 'مارس', 'أفريل',
    +    'ماي', 'جوان', 'جويلية', 'أوت', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'y/MM/dd', 'y/M/d'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ar_YE.
    + */
    +goog.i18n.DateTimeSymbols_ar_YE = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['ق.م', 'م'],
    +  ERANAMES: ['قبل الميلاد', 'ميلادي'],
    +  NARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س', 'ك',
    +    'ب', 'د'],
    +  STANDALONENARROWMONTHS: ['ي', 'ف', 'م', 'أ', 'و', 'ن', 'ل', 'غ', 'س',
    +    'ك', 'ب', 'د'],
    +  MONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو',
    +    'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر',
    +    'نوفمبر', 'ديسمبر'],
    +  STANDALONEMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  SHORTMONTHS: ['يناير', 'فبراير', 'مارس', 'أبريل',
    +    'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر',
    +    'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  STANDALONESHORTMONTHS: ['يناير', 'فبراير', 'مارس',
    +    'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس',
    +    'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'],
    +  WEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONEWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  SHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  STANDALONESHORTWEEKDAYS: ['الأحد', 'الاثنين', 'الثلاثاء',
    +    'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
    +  NARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  STANDALONENARROWWEEKDAYS: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
    +  SHORTQUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  QUARTERS: ['الربع الأول', 'الربع الثاني',
    +    'الربع الثالث', 'الربع الرابع'],
    +  AMPMS: ['ص', 'م'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'dd‏/MM‏/y',
    +    'd‏/M‏/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale as.
    + */
    +goog.i18n.DateTimeSymbols_as = {
    +  ZERODIGIT: 0x09E6,
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['জানুৱাৰী', 'ফেব্ৰুৱাৰী',
    +    'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন',
    +    'জুলাই', 'আগষ্ট', 'ছেপ্তেম্বৰ',
    +    'অক্টোবৰ', 'নৱেম্বৰ',
    +    'ডিচেম্বৰ'],
    +  STANDALONEMONTHS: ['জানুৱাৰী',
    +    'ফেব্ৰুৱাৰী', 'মাৰ্চ', 'এপ্ৰিল',
    +    'মে', 'জুন', 'জুলাই', 'আগষ্ট',
    +    'ছেপ্তেম্বৰ', 'অক্টোবৰ',
    +    'নৱেম্বৰ', 'ডিচেম্বৰ'],
    +  SHORTMONTHS: ['জানু', 'ফেব্ৰু', 'মাৰ্চ',
    +    'এপ্ৰিল', 'মে', 'জুন', 'জুলাই', 'আগ',
    +    'সেপ্ট', 'অক্টো', 'নভে', 'ডিসে'],
    +  STANDALONESHORTMONTHS: ['জানু', 'ফেব্ৰু',
    +    'মাৰ্চ', 'এপ্ৰিল', 'মে', 'জুন',
    +    'জুলাই', 'আগ', 'সেপ্ট', 'অক্টো',
    +    'নভে', 'ডিসে'],
    +  WEEKDAYS: ['দেওবাৰ', 'সোমবাৰ',
    +    'মঙ্গলবাৰ', 'বুধবাৰ',
    +    'বৃহষ্পতিবাৰ', 'শুক্ৰবাৰ',
    +    'শনিবাৰ'],
    +  STANDALONEWEEKDAYS: ['দেওবাৰ', 'সোমবাৰ',
    +    'মঙ্গলবাৰ', 'বুধবাৰ',
    +    'বৃহষ্পতিবাৰ', 'শুক্ৰবাৰ',
    +    'শনিবাৰ'],
    +  SHORTWEEKDAYS: ['ৰবি', 'সোম', 'মঙ্গল', 'বুধ',
    +    'বৃহষ্পতি', 'শুক্ৰ', 'শনি'],
    +  STANDALONESHORTWEEKDAYS: ['ৰবি', 'সোম', 'মঙ্গল',
    +    'বুধ', 'বৃহষ্পতি', 'শুক্ৰ', 'শনি'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['প্ৰথম প্ৰহৰ',
    +    'দ্বিতীয় প্ৰহৰ',
    +    'তৃতীয় প্ৰহৰ', 'চতুৰ্থ প্ৰহৰ'],
    +  QUARTERS: ['প্ৰথম প্ৰহৰ',
    +    'দ্বিতীয় প্ৰহৰ',
    +    'তৃতীয় প্ৰহৰ', 'চতুৰ্থ প্ৰহৰ'],
    +  AMPMS: ['পূৰ্বাহ্ণ', 'অপৰাহ্ণ'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'dd-MM-y', 'd-M-y'],
    +  TIMEFORMATS: ['h.mm.ss a zzzz', 'h.mm.ss a z', 'h.mm.ss a', 'h.mm. a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale as_IN.
    + */
    +goog.i18n.DateTimeSymbols_as_IN = goog.i18n.DateTimeSymbols_as;
    +
    +
    +/**
    + * Date/time formatting symbols for locale asa.
    + */
    +goog.i18n.DateTimeSymbols_asa = {
    +  ERAS: ['KM', 'BM'],
    +  ERANAMES: ['Kabla yakwe Yethu', 'Baada yakwe Yethu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Ijm', 'Jmo'],
    +  NARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['icheheavo', 'ichamthi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale asa_TZ.
    + */
    +goog.i18n.DateTimeSymbols_asa_TZ = goog.i18n.DateTimeSymbols_asa;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ast.
    + */
    +goog.i18n.DateTimeSymbols_ast = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['a.C.', 'd.C.'],
    +  NARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O', 'P', 'A'],
    +  STANDALONENARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O',
    +    'P', 'A'],
    +  MONTHS: ['de xineru', 'de febreru', 'de marzu', 'd’abril', 'de mayu',
    +    'de xunu', 'de xunetu', 'd’agostu', 'de setiembre', 'd’ochobre',
    +    'de payares', 'd’avientu'],
    +  STANDALONEMONTHS: ['xineru', 'febreru', 'marzu', 'abril', 'mayu', 'xunu',
    +    'xunetu', 'agostu', 'setiembre', 'ochobre', 'payares', 'avientu'],
    +  SHORTMONTHS: ['xin', 'feb', 'mar', 'abr', 'may', 'xun', 'xnt', 'ago', 'set',
    +    'och', 'pay', 'avi'],
    +  STANDALONESHORTMONTHS: ['Xin', 'Feb', 'Mar', 'Abr', 'May', 'Xun', 'Xnt',
    +    'Ago', 'Set', 'Och', 'Pay', 'Avi'],
    +  WEEKDAYS: ['domingu', 'llunes', 'martes', 'miércoles', 'xueves', 'vienres',
    +    'sábadu'],
    +  STANDALONEWEEKDAYS: ['domingu', 'llunes', 'martes', 'miércoles', 'xueves',
    +    'vienres', 'sábadu'],
    +  SHORTWEEKDAYS: ['dom', 'llu', 'mar', 'mie', 'xue', 'vie', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'llu', 'mar', 'mie', 'xue', 'vie', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1er trimestre', '2u trimestre', '3er trimestre', '4u trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ast_ES.
    + */
    +goog.i18n.DateTimeSymbols_ast_ES = goog.i18n.DateTimeSymbols_ast;
    +
    +
    +/**
    + * Date/time formatting symbols for locale az_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_az_Cyrl = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['јанвар', 'феврал', 'март', 'апрел', 'май',
    +    'ијун', 'ијул', 'август', 'сентјабр',
    +    'октјабр', 'нојабр', 'декабр'],
    +  STANDALONEMONTHS: ['јанвар', 'феврал', 'март', 'апрел',
    +    'май', 'ијун', 'ијул', 'август', 'сентјабр',
    +    'октјабр', 'нојабр', 'декабр'],
    +  SHORTMONTHS: ['јанвар', 'феврал', 'март', 'апрел',
    +    'май', 'ијун', 'ијул', 'август', 'сентјабр',
    +    'октјабр', 'нојабр', 'декабр'],
    +  STANDALONESHORTMONTHS: ['јанвар', 'феврал', 'март',
    +    'апрел', 'май', 'ијун', 'ијул', 'август',
    +    'сентјабр', 'октјабр', 'нојабр', 'декабр'],
    +  WEEKDAYS: ['базар', 'базар ертәси',
    +    'чәршәнбә ахшамы', 'чәршәнбә',
    +    'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'],
    +  STANDALONEWEEKDAYS: ['базар', 'базар ертәси',
    +    'чәршәнбә ахшамы', 'чәршәнбә',
    +    'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'],
    +  SHORTWEEKDAYS: ['базар', 'базар ертәси',
    +    'чәршәнбә ахшамы', 'чәршәнбә',
    +    'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'],
    +  STANDALONESHORTWEEKDAYS: ['базар', 'базар ертәси',
    +    'чәршәнбә ахшамы', 'чәршәнбә',
    +    'ҹүмә ахшамы', 'ҹүмә', 'шәнбә'],
    +  NARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  STANDALONENARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d, MMMM, y', 'd MMMM, y', 'd MMM, y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale az_Cyrl_AZ.
    + */
    +goog.i18n.DateTimeSymbols_az_Cyrl_AZ = goog.i18n.DateTimeSymbols_az_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale az_Latn.
    + */
    +goog.i18n.DateTimeSymbols_az_Latn = {
    +  ERAS: ['e.ə.', 'b.e.'],
    +  ERANAMES: ['eramızdan əvvəl', 'bizim eramızın'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun', 'iyul', 'avqust',
    +    'sentyabr', 'oktyabr', 'noyabr', 'dekabr'],
    +  STANDALONEMONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'İyun',
    +    'İyul', 'Avqust', 'Sentyabr', 'Oktyabr', 'Noyabr', 'Dekabr'],
    +  SHORTMONTHS: ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl', 'avq', 'sen',
    +    'okt', 'noy', 'dek'],
    +  STANDALONESHORTMONTHS: ['yan', 'fev', 'mar', 'apr', 'may', 'iyn', 'iyl',
    +    'avq', 'sen', 'okt', 'noy', 'dek'],
    +  WEEKDAYS: ['bazar', 'bazar ertəsi', 'çərşənbə axşamı',
    +    'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
    +  STANDALONEWEEKDAYS: ['bazar', 'bazar ertəsi', 'çərşənbə axşamı',
    +    'çərşənbə', 'cümə axşamı', 'cümə', 'şənbə'],
    +  SHORTWEEKDAYS: ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
    +  STANDALONESHORTWEEKDAYS: ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C.', 'Ş.'],
    +  NARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  STANDALONENARROWWEEKDAYS: ['7', '1', '2', '3', '4', '5', '6'],
    +  SHORTQUARTERS: ['1-ci kv.', '2-ci kv.', '3-cü kv.', '4-cü kv.'],
    +  QUARTERS: ['1-ci kvartal', '2-ci kvartal', '3-cü kvartal', '4-cü kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['d MMMM y, EEEE', 'd MMMM y', 'd MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale az_Latn_AZ.
    + */
    +goog.i18n.DateTimeSymbols_az_Latn_AZ = goog.i18n.DateTimeSymbols_az_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bas.
    + */
    +goog.i18n.DateTimeSymbols_bas = {
    +  ERAS: ['b.Y.K', 'm.Y.K'],
    +  ERANAMES: ['bisū bi Yesù Krǐstò', 'i mbūs Yesù Krǐstò'],
    +  NARROWMONTHS: ['k', 'm', 'm', 'm', 'm', 'h', 'n', 'h', 'd', 'b', 'm', 'l'],
    +  STANDALONENARROWMONTHS: ['k', 'm', 'm', 'm', 'm', 'h', 'n', 'h', 'd', 'b',
    +    'm', 'l'],
    +  MONTHS: ['Kɔndɔŋ', 'Màcɛ̂l', 'Màtùmb', 'Màtop', 'M̀puyɛ',
    +    'Hìlòndɛ̀', 'Njèbà', 'Hìkaŋ', 'Dìpɔ̀s', 'Bìòôm', 'Màyɛsèp',
    +    'Lìbuy li ńyèe'],
    +  STANDALONEMONTHS: ['Kɔndɔŋ', 'Màcɛ̂l', 'Màtùmb', 'Màtop', 'M̀puyɛ',
    +    'Hìlòndɛ̀', 'Njèbà', 'Hìkaŋ', 'Dìpɔ̀s', 'Bìòôm', 'Màyɛsèp',
    +    'Lìbuy li ńyèe'],
    +  SHORTMONTHS: ['kɔn', 'mac', 'mat', 'mto', 'mpu', 'hil', 'nje', 'hik', 'dip',
    +    'bio', 'may', 'liɓ'],
    +  STANDALONESHORTMONTHS: ['kɔn', 'mac', 'mat', 'mto', 'mpu', 'hil', 'nje',
    +    'hik', 'dip', 'bio', 'may', 'liɓ'],
    +  WEEKDAYS: ['ŋgwà nɔ̂y', 'ŋgwà njaŋgumba', 'ŋgwà ûm', 'ŋgwà ŋgê',
    +    'ŋgwà mbɔk', 'ŋgwà kɔɔ', 'ŋgwà jôn'],
    +  STANDALONEWEEKDAYS: ['ŋgwà nɔ̂y', 'ŋgwà njaŋgumba', 'ŋgwà ûm',
    +    'ŋgwà ŋgê', 'ŋgwà mbɔk', 'ŋgwà kɔɔ', 'ŋgwà jôn'],
    +  SHORTWEEKDAYS: ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ', 'jon'],
    +  STANDALONESHORTWEEKDAYS: ['nɔy', 'nja', 'uum', 'ŋge', 'mbɔ', 'kɔɔ',
    +    'jon'],
    +  NARROWWEEKDAYS: ['n', 'n', 'u', 'ŋ', 'm', 'k', 'j'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'n', 'u', 'ŋ', 'm', 'k', 'j'],
    +  SHORTQUARTERS: ['K1s3', 'K2s3', 'K3s3', 'K4s3'],
    +  QUARTERS: ['Kèk bisu i soŋ iaâ', 'Kèk i ńyonos biɓaà i soŋ iaâ',
    +    'Kèk i ńyonos biaâ i soŋ iaâ', 'Kèk i ńyonos binâ i soŋ iaâ'],
    +  AMPMS: ['I bikɛ̂glà', 'I ɓugajɔp'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bas_CM.
    + */
    +goog.i18n.DateTimeSymbols_bas_CM = goog.i18n.DateTimeSymbols_bas;
    +
    +
    +/**
    + * Date/time formatting symbols for locale be.
    + */
    +goog.i18n.DateTimeSymbols_be = {
    +  ERAS: ['да н.э.', 'н.э.'],
    +  ERANAMES: ['да н.э.', 'н.э.'],
    +  NARROWMONTHS: ['с', 'л', 'с', 'к', 'м', 'ч', 'л', 'ж', 'в', 'к',
    +    'л', 'с'],
    +  STANDALONENARROWMONTHS: ['с', 'л', 'с', 'к', 'м', 'ч', 'л', 'ж', 'в',
    +    'к', 'л', 'с'],
    +  MONTHS: ['студзеня', 'лютага', 'сакавіка',
    +    'красавіка', 'мая', 'чэрвеня', 'ліпеня',
    +    'жніўня', 'верасня', 'кастрычніка',
    +    'лістапада', 'снежня'],
    +  STANDALONEMONTHS: ['студзень', 'люты', 'сакавік',
    +    'красавік', 'май', 'чэрвень', 'ліпень',
    +    'жнівень', 'верасень', 'кастрычнік',
    +    'лістапад', 'снежань'],
    +  SHORTMONTHS: ['сту', 'лют', 'сак', 'кра', 'мая', 'чэр',
    +    'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'],
    +  STANDALONESHORTMONTHS: ['сту', 'лют', 'сак', 'кра', 'май',
    +    'чэр', 'ліп', 'жні', 'вер', 'кас', 'ліс', 'сне'],
    +  WEEKDAYS: ['нядзеля', 'панядзелак', 'аўторак',
    +    'серада', 'чацвер', 'пятніца', 'субота'],
    +  STANDALONEWEEKDAYS: ['нядзеля', 'панядзелак',
    +    'аўторак', 'серада', 'чацвер', 'пятніца',
    +    'субота'],
    +  SHORTWEEKDAYS: ['нд', 'пн', 'аў', 'ср', 'чц', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['нд', 'пн', 'аў', 'ср', 'чц', 'пт',
    +    'сб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'а', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'а', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['1-шы кв.', '2-гі кв.', '3-ці кв.',
    +    '4-ты кв.'],
    +  QUARTERS: ['1-шы квартал', '2-гі квартал',
    +    '3-ці квартал', '4-ты квартал'],
    +  AMPMS: ['да палудня', 'пасля палудня'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd.M.y', 'd.M.yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale be_BY.
    + */
    +goog.i18n.DateTimeSymbols_be_BY = goog.i18n.DateTimeSymbols_be;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bem.
    + */
    +goog.i18n.DateTimeSymbols_bem = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Yesu', 'After Yesu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'E', 'M', 'J', 'J', 'O', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'E', 'M', 'J', 'J', 'O', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Epreo', 'Mei', 'Juni', 'Julai',
    +    'Ogasti', 'Septemba', 'Oktoba', 'Novemba', 'Disemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Epreo', 'Mei', 'Juni',
    +    'Julai', 'Ogasti', 'Septemba', 'Oktoba', 'Novemba', 'Disemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Epr', 'Mei', 'Jun', 'Jul', 'Oga', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Epr', 'Mei', 'Jun', 'Jul',
    +    'Oga', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Pa Mulungu', 'Palichimo', 'Palichibuli', 'Palichitatu',
    +    'Palichine', 'Palichisano', 'Pachibelushi'],
    +  STANDALONEWEEKDAYS: ['Pa Mulungu', 'Palichimo', 'Palichibuli', 'Palichitatu',
    +    'Palichine', 'Palichisano', 'Pachibelushi'],
    +  SHORTWEEKDAYS: ['Pa Mulungu', 'Palichimo', 'Palichibuli', 'Palichitatu',
    +    'Palichine', 'Palichisano', 'Pachibelushi'],
    +  STANDALONESHORTWEEKDAYS: ['Pa Mulungu', 'Palichimo', 'Palichibuli',
    +    'Palichitatu', 'Palichine', 'Palichisano', 'Pachibelushi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['uluchelo', 'akasuba'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bem_ZM.
    + */
    +goog.i18n.DateTimeSymbols_bem_ZM = goog.i18n.DateTimeSymbols_bem;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bez.
    + */
    +goog.i18n.DateTimeSymbols_bez = {
    +  ERAS: ['KM', 'BM'],
    +  ERANAMES: ['Kabla ya Mtwaa', 'Baada ya Mtwaa'],
    +  NARROWMONTHS: ['H', 'V', 'D', 'T', 'H', 'S', 'S', 'N', 'T', 'K', 'K', 'K'],
    +  STANDALONENARROWMONTHS: ['H', 'V', 'D', 'T', 'H', 'S', 'S', 'N', 'T', 'K',
    +    'K', 'K'],
    +  MONTHS: ['pa mwedzi gwa hutala', 'pa mwedzi gwa wuvili',
    +    'pa mwedzi gwa wudatu', 'pa mwedzi gwa wutai', 'pa mwedzi gwa wuhanu',
    +    'pa mwedzi gwa sita', 'pa mwedzi gwa saba', 'pa mwedzi gwa nane',
    +    'pa mwedzi gwa tisa', 'pa mwedzi gwa kumi', 'pa mwedzi gwa kumi na moja',
    +    'pa mwedzi gwa kumi na mbili'],
    +  STANDALONEMONTHS: ['pa mwedzi gwa hutala', 'pa mwedzi gwa wuvili',
    +    'pa mwedzi gwa wudatu', 'pa mwedzi gwa wutai', 'pa mwedzi gwa wuhanu',
    +    'pa mwedzi gwa sita', 'pa mwedzi gwa saba', 'pa mwedzi gwa nane',
    +    'pa mwedzi gwa tisa', 'pa mwedzi gwa kumi', 'pa mwedzi gwa kumi na moja',
    +    'pa mwedzi gwa kumi na mbili'],
    +  SHORTMONTHS: ['Hut', 'Vil', 'Dat', 'Tai', 'Han', 'Sit', 'Sab', 'Nan', 'Tis',
    +    'Kum', 'Kmj', 'Kmb'],
    +  STANDALONESHORTMONTHS: ['Hut', 'Vil', 'Dat', 'Tai', 'Han', 'Sit', 'Sab',
    +    'Nan', 'Tis', 'Kum', 'Kmj', 'Kmb'],
    +  WEEKDAYS: ['pa mulungu', 'pa shahuviluha', 'pa hivili', 'pa hidatu',
    +    'pa hitayi', 'pa hihanu', 'pa shahulembela'],
    +  STANDALONEWEEKDAYS: ['pa mulungu', 'pa shahuviluha', 'pa hivili', 'pa hidatu',
    +    'pa hitayi', 'pa hihanu', 'pa shahulembela'],
    +  SHORTWEEKDAYS: ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'],
    +  STANDALONESHORTWEEKDAYS: ['Mul', 'Vil', 'Hiv', 'Hid', 'Hit', 'Hih', 'Lem'],
    +  NARROWWEEKDAYS: ['M', 'J', 'H', 'H', 'H', 'W', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['M', 'J', 'H', 'H', 'H', 'W', 'J'],
    +  SHORTQUARTERS: ['L1', 'L2', 'L3', 'L4'],
    +  QUARTERS: ['Lobo 1', 'Lobo 2', 'Lobo 3', 'Lobo 4'],
    +  AMPMS: ['pamilau', 'pamunyi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bez_TZ.
    + */
    +goog.i18n.DateTimeSymbols_bez_TZ = goog.i18n.DateTimeSymbols_bez;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bg_BG.
    + */
    +goog.i18n.DateTimeSymbols_bg_BG = {
    +  ERAS: ['пр.Хр.', 'сл.Хр.'],
    +  ERANAMES: ['преди Христа', 'след Христа'],
    +  NARROWMONTHS: ['я', 'ф', 'м', 'а', 'м', 'ю', 'ю', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['я', 'ф', 'м', 'а', 'м', 'ю', 'ю', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['януари', 'февруари', 'март', 'април',
    +    'май', 'юни', 'юли', 'август', 'септември',
    +    'октомври', 'ноември', 'декември'],
    +  STANDALONEMONTHS: ['януари', 'февруари', 'март',
    +    'април', 'май', 'юни', 'юли', 'август',
    +    'септември', 'октомври', 'ноември',
    +    'декември'],
    +  SHORTMONTHS: ['ян.', 'февр.', 'март', 'апр.', 'май', 'юни',
    +    'юли', 'авг.', 'септ.', 'окт.', 'ноем.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['ян.', 'февр.', 'март', 'апр.', 'май',
    +    'юни', 'юли', 'авг.', 'септ.', 'окт.', 'ноем.',
    +    'дек.'],
    +  WEEKDAYS: ['неделя', 'понеделник', 'вторник',
    +    'сряда', 'четвъртък', 'петък', 'събота'],
    +  STANDALONEWEEKDAYS: ['неделя', 'понеделник', 'вторник',
    +    'сряда', 'четвъртък', 'петък', 'събота'],
    +  SHORTWEEKDAYS: ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['нд', 'пн', 'вт', 'ср', 'чт', 'пт',
    +    'сб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['1. трим.', '2. трим.', '3. трим.',
    +    '4. трим.'],
    +  QUARTERS: ['1. тримесечие', '2. тримесечие',
    +    '3. тримесечие', '4. тримесечие'],
    +  AMPMS: ['пр.об.', 'сл.об.'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd.MM.y \'г\'.',
    +    'd.MM.yy \'г\'.'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bm.
    + */
    +goog.i18n.DateTimeSymbols_bm = {
    +  ERAS: ['J.-C. ɲɛ', 'ni J.-C.'],
    +  ERANAMES: ['jezu krisiti ɲɛ', 'jezu krisiti minkɛ'],
    +  NARROWMONTHS: ['Z', 'F', 'M', 'A', 'M', 'Z', 'Z', 'U', 'S', 'Ɔ', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Z', 'F', 'M', 'A', 'M', 'Z', 'Z', 'U', 'S', 'Ɔ',
    +    'N', 'D'],
    +  MONTHS: ['zanwuye', 'feburuye', 'marisi', 'awirili', 'mɛ', 'zuwɛn',
    +    'zuluye', 'uti', 'sɛtanburu', 'ɔkutɔburu', 'nowanburu', 'desanburu'],
    +  STANDALONEMONTHS: ['zanwuye', 'feburuye', 'marisi', 'awirili', 'mɛ',
    +    'zuwɛn', 'zuluye', 'uti', 'sɛtanburu', 'ɔkutɔburu', 'nowanburu',
    +    'desanburu'],
    +  SHORTMONTHS: ['zan', 'feb', 'mar', 'awi', 'mɛ', 'zuw', 'zul', 'uti', 'sɛt',
    +    'ɔku', 'now', 'des'],
    +  STANDALONESHORTMONTHS: ['zan', 'feb', 'mar', 'awi', 'mɛ', 'zuw', 'zul',
    +    'uti', 'sɛt', 'ɔku', 'now', 'des'],
    +  WEEKDAYS: ['kari', 'ntɛnɛ', 'tarata', 'araba', 'alamisa', 'juma', 'sibiri'],
    +  STANDALONEWEEKDAYS: ['kari', 'ntɛnɛ', 'tarata', 'araba', 'alamisa', 'juma',
    +    'sibiri'],
    +  SHORTWEEKDAYS: ['kar', 'ntɛ', 'tar', 'ara', 'ala', 'jum', 'sib'],
    +  STANDALONESHORTWEEKDAYS: ['kar', 'ntɛ', 'tar', 'ara', 'ala', 'jum', 'sib'],
    +  NARROWWEEKDAYS: ['K', 'N', 'T', 'A', 'A', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'N', 'T', 'A', 'A', 'J', 'S'],
    +  SHORTQUARTERS: ['KS1', 'KS2', 'KS3', 'KS4'],
    +  QUARTERS: ['kalo saba fɔlɔ', 'kalo saba filanan', 'kalo saba sabanan',
    +    'kalo saba naaninan'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bm_Latn.
    + */
    +goog.i18n.DateTimeSymbols_bm_Latn = goog.i18n.DateTimeSymbols_bm;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bm_Latn_ML.
    + */
    +goog.i18n.DateTimeSymbols_bm_Latn_ML = goog.i18n.DateTimeSymbols_bm;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bn_BD.
    + */
    +goog.i18n.DateTimeSymbols_bn_BD = {
    +  ZERODIGIT: 0x09E6,
    +  ERAS: ['খ্রিস্টপূর্ব', 'খৃষ্টাব্দ'],
    +  ERANAMES: ['খ্রিস্টপূর্ব',
    +    'খৃষ্টাব্দ'],
    +  NARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে', 'জুন',
    +    'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  STANDALONENARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে',
    +    'জুন', 'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  MONTHS: ['জানুয়ারী', 'ফেব্রুয়ারী',
    +    'মার্চ', 'এপ্রিল', 'মে', 'জুন',
    +    'জুলাই', 'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONEMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  SHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONESHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  WEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহস্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  STANDALONEWEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহষ্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  SHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল', 'বুধ',
    +    'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  STANDALONESHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল',
    +    'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  NARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ', 'শু', 'শ'],
    +  STANDALONENARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ',
    +    'শু', 'শ'],
    +  SHORTQUARTERS: [
    +    'প্র. ত্রৈ. এক. চতুর্থাংশ',
    +    'দ্বি.ত্রৈ.এক. চতুর্থাংশ',
    +    'তৃ.ত্রৈ.এক.চতুর্থাংশ',
    +    'চ.ত্রৈ.এক চতুর্থাংশ'],
    +  QUARTERS: [
    +    'প্রথম ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'দ্বিতীয় ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'তৃতীয় ত্রৈমাসিকের এক চতুর্থাংশ',
    +    'চতুর্থ ত্রৈমাসিকের এক চতুর্থাংশ'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 4,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bn_IN.
    + */
    +goog.i18n.DateTimeSymbols_bn_IN = {
    +  ZERODIGIT: 0x09E6,
    +  ERAS: ['খ্রিস্টপূর্ব', 'খৃষ্টাব্দ'],
    +  ERANAMES: ['খ্রিস্টপূর্ব',
    +    'খৃষ্টাব্দ'],
    +  NARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে', 'জুন',
    +    'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  STANDALONENARROWMONTHS: ['জা', 'ফে', 'মা', 'এ', 'মে',
    +    'জুন', 'জু', 'আ', 'সে', 'অ', 'ন', 'ডি'],
    +  MONTHS: ['জানুয়ারী', 'ফেব্রুয়ারী',
    +    'মার্চ', 'এপ্রিল', 'মে', 'জুন',
    +    'জুলাই', 'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONEMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  SHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  STANDALONESHORTMONTHS: ['জানুয়ারী',
    +    'ফেব্রুয়ারী', 'মার্চ',
    +    'এপ্রিল', 'মে', 'জুন', 'জুলাই',
    +    'আগস্ট', 'সেপ্টেম্বর',
    +    'অক্টোবর', 'নভেম্বর',
    +    'ডিসেম্বর'],
    +  WEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহস্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  STANDALONEWEEKDAYS: ['রবিবার', 'সোমবার',
    +    'মঙ্গলবার', 'বুধবার',
    +    'বৃহষ্পতিবার', 'শুক্রবার',
    +    'শনিবার'],
    +  SHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল', 'বুধ',
    +    'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  STANDALONESHORTWEEKDAYS: ['রবি', 'সোম', 'মঙ্গল',
    +    'বুধ', 'বৃহস্পতি', 'শুক্র', 'শনি'],
    +  NARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ', 'শু', 'শ'],
    +  STANDALONENARROWWEEKDAYS: ['র', 'সো', 'ম', 'বু', 'বৃ',
    +    'শু', 'শ'],
    +  SHORTQUARTERS: ['ত্রৈমাসিক', 'ষাণ্মাসিক',
    +    'তৃ.ত্রৈ.এক.চতুর্থাংশ',
    +    'বার্ষিক'],
    +  QUARTERS: ['ত্রৈমাসিক', 'ষাণ্মাসিক',
    +    'তৃতীয় চতুর্থাংশ', 'বার্ষিক'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bo.
    + */
    +goog.i18n.DateTimeSymbols_bo = {
    +  ERAS: ['སྤྱི་ལོ་སྔོན།', 'སྤྱི་ལོ།'],
    +  ERANAMES: ['སྤྱི་ལོ་སྔོན།',
    +    'སྤྱི་ལོ།'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ཟླ་བ་དང་པོ་',
    +    'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་སུམ་པ་',
    +    'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་',
    +    'ཟླ་བ་དྲུག་པ་',
    +    'ཟླ་བ་བདུན་པ་',
    +    'ཟླ་བ་བརྒྱད་པ་',
    +    'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་',
    +    'ཟླ་བ་བཅུ་གཅིག་པ་',
    +    'ཟླ་བ་བཅུ་གཉིས་པ་'],
    +  STANDALONEMONTHS: ['ཟླ་བ་དང་པོ་',
    +    'ཟླ་བ་གཉིས་པ་', 'ཟླ་བ་སུམ་པ་',
    +    'ཟླ་བ་བཞི་པ་', 'ཟླ་བ་ལྔ་པ་',
    +    'ཟླ་བ་དྲུག་པ་',
    +    'ཟླ་བ་བདུན་པ་',
    +    'ཟླ་བ་བརྒྱད་པ་',
    +    'ཟླ་བ་དགུ་པ་', 'ཟླ་བ་བཅུ་པ་',
    +    'ཟླ་བ་བཅུ་གཅིག་པ་',
    +    'ཟླ་བ་བཅུ་གཉིས་པ་'],
    +  SHORTMONTHS: ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣', 'ཟླ་༤',
    +    'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧', 'ཟླ་༨',
    +    'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡', 'ཟླ་༡༢'],
    +  STANDALONESHORTMONTHS: ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣',
    +    'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧',
    +    'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡',
    +    'ཟླ་༡༢'],
    +  WEEKDAYS: ['གཟའ་ཉི་མ་', 'གཟའ་ཟླ་བ་',
    +    'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་',
    +    'གཟའ་ཕུར་བུ་', 'གཟའ་པ་སངས་',
    +    'གཟའ་སྤེན་པ་'],
    +  STANDALONEWEEKDAYS: ['གཟའ་ཉི་མ་',
    +    'གཟའ་ཟླ་བ་', 'གཟའ་མིག་དམར་',
    +    'གཟའ་ལྷག་པ་', 'གཟའ་ཕུར་བུ་',
    +    'གཟའ་པ་སངས་', 'གཟའ་སྤེན་པ་'],
    +  SHORTWEEKDAYS: ['ཉི་མ་', 'ཟླ་བ་',
    +    'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་',
    +    'པ་སངས་', 'སྤེན་པ་'],
    +  STANDALONESHORTWEEKDAYS: ['ཉི་མ་', 'ཟླ་བ་',
    +    'མིག་དམར་', 'ལྷག་པ་', 'ཕུར་བུ་',
    +    'པ་སངས་', 'སྤེན་པ་'],
    +  NARROWWEEKDAYS: ['ཉི', 'ཟླ', 'མི', 'ལྷ', 'ཕུ', 'པ',
    +    'སྤེ'],
    +  STANDALONENARROWWEEKDAYS: ['ཉི', 'ཟླ', 'མི', 'ལྷ', 'ཕུ',
    +    'པ', 'སྤེ'],
    +  SHORTQUARTERS: ['དུས་ཚིགས་དང་པོ།',
    +    'དུས་ཚིགས་གཉིས་པ།',
    +    '་དུས་ཚིགས་གསུམ་པ།',
    +    'དུས་ཚིགས་བཞི་པ།'],
    +  QUARTERS: ['དུས་ཚིགས་དང་པོ།',
    +    'དུས་ཚིགས་གཉིས་པ།',
    +    '་དུས་ཚིགས་གསུམ་པ།',
    +    'དུས་ཚིགས་བཞི་པ།'],
    +  AMPMS: ['སྔ་དྲོ་', 'ཕྱི་དྲོ་'],
    +  DATEFORMATS: ['y MMMM d, EEEE',
    +    'སྤྱི་ལོ་y MMMMའི་ཙེས་dད',
    +    'y ལོ་འི་MMMཙེས་d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bo_CN.
    + */
    +goog.i18n.DateTimeSymbols_bo_CN = goog.i18n.DateTimeSymbols_bo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bo_IN.
    + */
    +goog.i18n.DateTimeSymbols_bo_IN = goog.i18n.DateTimeSymbols_bo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale br_FR.
    + */
    +goog.i18n.DateTimeSymbols_br_FR = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
    +    '11', '12'],
    +  STANDALONENARROWMONTHS: ['01', '02', '03', '04', '05', '06', '07', '08', '09',
    +    '10', '11', '12'],
    +  MONTHS: ['Genver', 'Cʼhwevrer', 'Meurzh', 'Ebrel', 'Mae', 'Mezheven',
    +    'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'],
    +  STANDALONEMONTHS: ['Genver', 'Cʼhwevrer', 'Meurzh', 'Ebrel', 'Mae',
    +    'Mezheven', 'Gouere', 'Eost', 'Gwengolo', 'Here', 'Du', 'Kerzu'],
    +  SHORTMONTHS: ['Gen', 'Cʼhwe', 'Meur', 'Ebr', 'Mae', 'Mezh', 'Goue', 'Eost',
    +    'Gwen', 'Here', 'Du', 'Ker'],
    +  STANDALONESHORTMONTHS: ['Gen', 'Cʼhwe', 'Meur', 'Ebr', 'Mae', 'Mezh', 'Goue',
    +    'Eost', 'Gwen', 'Here', 'Du', 'Ker'],
    +  WEEKDAYS: ['Sul', 'Lun', 'Meurzh', 'Mercʼher', 'Yaou', 'Gwener', 'Sadorn'],
    +  STANDALONEWEEKDAYS: ['Sul', 'Lun', 'Meurzh', 'Mercʼher', 'Yaou', 'Gwener',
    +    'Sadorn'],
    +  SHORTWEEKDAYS: ['Sul', 'Lun', 'Meu.', 'Mer.', 'Yaou', 'Gwe.', 'Sad.'],
    +  STANDALONESHORTWEEKDAYS: ['Sul', 'Lun', 'Meu.', 'Mer.', 'Yaou', 'Gwe.',
    +    'Sad.'],
    +  NARROWWEEKDAYS: ['Su', 'L', 'Mz', 'Mc', 'Y', 'G', 'Sa'],
    +  STANDALONENARROWWEEKDAYS: ['Su', 'L', 'Mz', 'Mc', 'Y', 'G', 'Sa'],
    +  SHORTQUARTERS: ['1añ trim.', '2l trim.', '3e trim.', '4e trim.'],
    +  QUARTERS: ['1añ trimiziad', '2l trimiziad', '3e trimiziad', '4e trimiziad'],
    +  AMPMS: ['A.M.', 'G.M.'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale brx.
    + */
    +goog.i18n.DateTimeSymbols_brx = {
    +  ERAS: ['ईसा.पूर्व', 'सन'],
    +  ERANAMES: ['ईसा.पूर्व', 'सन'],
    +  NARROWMONTHS: ['ज', 'फे', 'मा', 'ए', 'मे', 'जु', 'जु',
    +    'आ', 'से', 'अ', 'न', 'दि'],
    +  STANDALONENARROWMONTHS: ['ज', 'फे', 'मा', 'ए', 'मे', 'जु',
    +    'जु', 'आ', 'से', 'अ', 'न', 'दि'],
    +  MONTHS: ['जानुवारी', 'फेब्रुवारी',
    +    'मार्स', 'एफ्रिल', 'मे', 'जुन',
    +    'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र',
    +    'अखथबर', 'नबेज्ब़र',
    +    'दिसेज्ब़र'],
    +  STANDALONEMONTHS: ['जानुवारी',
    +    'फेब्रुवारी', 'मार्स', 'एफ्रिल',
    +    'मे', 'जुन', 'जुलाइ', 'आगस्थ',
    +    'सेबथेज्ब़र', 'अखथबर',
    +    'नबेज्ब़र', 'दिसेज्ब़र'],
    +  SHORTMONTHS: ['जानुवारी', 'फेब्रुवारी',
    +    'मार्स', 'एफ्रिल', 'मे', 'जुन',
    +    'जुलाइ', 'आगस्थ', 'सेबथेज्ब़र',
    +    'अखथबर', 'नबेज्ब़र',
    +    'दिसेज्ब़र'],
    +  STANDALONESHORTMONTHS: ['जानुवारी',
    +    'फेब्रुवारी', 'मार्स', 'एफ्रिल',
    +    'मे', 'जुन', 'जुलाइ', 'आगस्थ',
    +    'सेबथेज्ब़र', 'अखथबर',
    +    'नबेज्ब़र', 'दिसेज्ब़र'],
    +  WEEKDAYS: ['रबिबार', 'समबार', 'मंगलबार',
    +    'बुदबार', 'बिसथिबार',
    +    'सुखुरबार', 'सुनिबार'],
    +  STANDALONEWEEKDAYS: ['रबिबार', 'समबार',
    +    'मंगलबार', 'बुदबार', 'बिसथिबार',
    +    'सुखुरबार', 'सुनिबार'],
    +  SHORTWEEKDAYS: ['रबि', 'सम', 'मंगल', 'बुद',
    +    'बिसथि', 'सुखुर', 'सुनि'],
    +  STANDALONESHORTWEEKDAYS: ['रबि', 'सम', 'मंगल', 'बुद',
    +    'बिसथि', 'सुखुर', 'सुनि'],
    +  NARROWWEEKDAYS: ['र', 'स', 'मं', 'बु', 'बि', 'सु',
    +    'सु'],
    +  STANDALONENARROWWEEKDAYS: ['र', 'स', 'मं', 'बु', 'बि',
    +    'सु', 'सु'],
    +  SHORTQUARTERS: [
    +    'सिथासे/खोन्दोसे/बाहागोसे',
    +    'खावसे/खोन्दोनै/बाहागोनै',
    +    'खावथाम/खोन्दोथाम/बाहागोथाम',
    +    'खावब्रै/खोन्दोब्रै/फुरा/आबुं'],
    +  QUARTERS: [
    +    'सिथासे/खोन्दोसे/बाहागोसे',
    +    'खावसे/खोन्दोनै/बाहागोनै',
    +    'खावथाम/खोन्दोथाम/बाहागोथाम',
    +    'खावब्रै/खोन्दोब्रै/फुरा/आबुं'],
    +  AMPMS: ['फुं', 'बेलासे'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale brx_IN.
    + */
    +goog.i18n.DateTimeSymbols_brx_IN = goog.i18n.DateTimeSymbols_brx;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bs.
    + */
    +goog.i18n.DateTimeSymbols_bs = {
    +  ERAS: ['p. n. e.', 'n. e.'],
    +  ERANAMES: ['Prije nove ere', 'Nove ere'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'juni', 'juli',
    +    'august', 'septembar', 'oktobar', 'novembar', 'decembar'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'juni',
    +    'juli', 'august', 'septembar', 'oktobar', 'novembar', 'decembar'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep',
    +    'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak',
    +    'petak', 'subota'],
    +  STANDALONEWEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda',
    +    'četvrtak', 'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Prvi kvartal', 'Drugi kvartal', 'Treći kvartal',
    +    'Četvrti kvartal'],
    +  AMPMS: ['prije podne', 'popodne'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'dd. MMM. y.', 'dd.MM.yy.'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'u\' {0}', '{1} \'u\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bs_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_bs_Cyrl = {
    +  ERAS: ['п. н. е.', 'н. е.'],
    +  ERANAMES: ['Пре нове ере', 'Нове ере'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануар', 'фебруар', 'март', 'април', 'мај',
    +    'јуни', 'јули', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  STANDALONEMONTHS: ['јануар', 'фебруар', 'март', 'април',
    +    'мај', 'јуни', 'јули', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  SHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај', 'јун',
    +    'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  STANDALONESHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај',
    +    'јун', 'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  WEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'сриједа', 'четвртак', 'петак', 'субота'],
    +  STANDALONEWEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'сриједа', 'четвртак', 'петак', 'субота'],
    +  SHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сри', 'чет', 'пет',
    +    'суб'],
    +  STANDALONESHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сри', 'чет',
    +    'пет', 'суб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['К1', 'К2', 'К3', 'К4'],
    +  QUARTERS: ['Прво тромесечје', 'Друго тромесечје',
    +    'Треће тромесечје', 'Четврто тромесечје'],
    +  AMPMS: ['пре подне', 'поподне'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'dd.MM.y.', 'd.M.yy.'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale bs_Cyrl_BA.
    + */
    +goog.i18n.DateTimeSymbols_bs_Cyrl_BA = goog.i18n.DateTimeSymbols_bs_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bs_Latn.
    + */
    +goog.i18n.DateTimeSymbols_bs_Latn = goog.i18n.DateTimeSymbols_bs;
    +
    +
    +/**
    + * Date/time formatting symbols for locale bs_Latn_BA.
    + */
    +goog.i18n.DateTimeSymbols_bs_Latn_BA = goog.i18n.DateTimeSymbols_bs;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca_AD.
    + */
    +goog.i18n.DateTimeSymbols_ca_AD = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['abans de Crist', 'després de Crist'],
    +  NARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG', 'ST', 'OC',
    +    'NV', 'DS'],
    +  STANDALONENARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG',
    +    'ST', 'OC', 'NV', 'DS'],
    +  MONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol',
    +    'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  STANDALONEMONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny',
    +    'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  SHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.',
    +    'set.', 'oct.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny',
    +    'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
    +  WEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  STANDALONEWEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  SHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  STANDALONESHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  NARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  STANDALONENARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1r trimestre', '2n trimestre', '3r trimestre', '4t trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} \'a les\' {0}', '{1}, {0}', '{1} , {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca_ES.
    + */
    +goog.i18n.DateTimeSymbols_ca_ES = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['abans de Crist', 'després de Crist'],
    +  NARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG', 'ST', 'OC',
    +    'NV', 'DS'],
    +  STANDALONENARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG',
    +    'ST', 'OC', 'NV', 'DS'],
    +  MONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol',
    +    'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  STANDALONEMONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny',
    +    'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  SHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.',
    +    'set.', 'oct.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny',
    +    'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
    +  WEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  STANDALONEWEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  SHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  STANDALONESHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  NARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  STANDALONENARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1r trimestre', '2n trimestre', '3r trimestre', '4t trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} \'a les\' {0}', '{1}, {0}', '{1} , {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca_ES_VALENCIA.
    + */
    +goog.i18n.DateTimeSymbols_ca_ES_VALENCIA = goog.i18n.DateTimeSymbols_ca_ES;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca_FR.
    + */
    +goog.i18n.DateTimeSymbols_ca_FR = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['abans de Crist', 'després de Crist'],
    +  NARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG', 'ST', 'OC',
    +    'NV', 'DS'],
    +  STANDALONENARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG',
    +    'ST', 'OC', 'NV', 'DS'],
    +  MONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol',
    +    'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  STANDALONEMONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny',
    +    'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  SHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.',
    +    'set.', 'oct.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny',
    +    'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
    +  WEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  STANDALONEWEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  SHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  STANDALONESHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  NARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  STANDALONENARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1r trimestre', '2n trimestre', '3r trimestre', '4t trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} \'a les\' {0}', '{1}, {0}', '{1} , {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ca_IT.
    + */
    +goog.i18n.DateTimeSymbols_ca_IT = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['abans de Crist', 'després de Crist'],
    +  NARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG', 'ST', 'OC',
    +    'NV', 'DS'],
    +  STANDALONENARROWMONTHS: ['GN', 'FB', 'MÇ', 'AB', 'MG', 'JN', 'JL', 'AG',
    +    'ST', 'OC', 'NV', 'DS'],
    +  MONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny', 'juliol',
    +    'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  STANDALONEMONTHS: ['gener', 'febrer', 'març', 'abril', 'maig', 'juny',
    +    'juliol', 'agost', 'setembre', 'octubre', 'novembre', 'desembre'],
    +  SHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.',
    +    'set.', 'oct.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny',
    +    'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'],
    +  WEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  STANDALONEWEEKDAYS: ['diumenge', 'dilluns', 'dimarts', 'dimecres', 'dijous',
    +    'divendres', 'dissabte'],
    +  SHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  STANDALONESHORTWEEKDAYS: ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
    +  NARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  STANDALONENARROWWEEKDAYS: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
    +  SHORTQUARTERS: ['1T', '2T', '3T', '4T'],
    +  QUARTERS: ['1r trimestre', '2n trimestre', '3r trimestre', '4t trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d MMMM \'de\' y', 'd MMMM \'de\' y', 'd MMM y',
    +    'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} \'a les\' {0}', '{1}, {0}', '{1} , {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale cgg.
    + */
    +goog.i18n.DateTimeSymbols_cgg = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kurisito Atakaijire', 'Kurisito Yaijire'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana',
    +    'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda',
    +    'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
    +  STANDALONEMONTHS: ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana',
    +    'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda',
    +    'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
    +  SHORTMONTHS: ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW',
    +    'KKM', 'KNK', 'KNB'],
    +  STANDALONESHORTMONTHS: ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS',
    +    'KMN', 'KMW', 'KKM', 'KNK', 'KNB'],
    +  WEEKDAYS: ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana',
    +    'Orwakataano', 'Orwamukaaga'],
    +  STANDALONEWEEKDAYS: ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu',
    +    'Orwakana', 'Orwakataano', 'Orwamukaaga'],
    +  SHORTWEEKDAYS: ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
    +  STANDALONESHORTWEEKDAYS: ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
    +  NARROWWEEKDAYS: ['S', 'K', 'R', 'S', 'N', 'T', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'K', 'R', 'S', 'N', 'T', 'M'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['KWOTA 1', 'KWOTA 2', 'KWOTA 3', 'KWOTA 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale cgg_UG.
    + */
    +goog.i18n.DateTimeSymbols_cgg_UG = goog.i18n.DateTimeSymbols_cgg;
    +
    +
    +/**
    + * Date/time formatting symbols for locale chr_US.
    + */
    +goog.i18n.DateTimeSymbols_chr_US = {
    +  ERAS: ['ᎤᏓᎷᎸ', 'ᎤᎶᏐᏅ'],
    +  ERANAMES: ['Ꮟ ᏥᏌ ᎾᏕᎲᏍᎬᎾ',
    +    'ᎠᎩᏃᎮᎵᏓᏍᏗᏱ ᎠᏕᏘᏱᏍᎬ ᏱᎰᏩ ᏧᏓᏂᎸᎢᏍᏗ'],
    +  NARROWMONTHS: ['Ꭴ', 'Ꭷ', 'Ꭰ', 'Ꭷ', 'Ꭰ', 'Ꮥ', 'Ꭻ', 'Ꭶ', 'Ꮪ',
    +    'Ꮪ', 'Ꮕ', 'Ꭵ'],
    +  STANDALONENARROWMONTHS: ['Ꭴ', 'Ꭷ', 'Ꭰ', 'Ꭷ', 'Ꭰ', 'Ꮥ', 'Ꭻ',
    +    'Ꭶ', 'Ꮪ', 'Ꮪ', 'Ꮕ', 'Ꭵ'],
    +  MONTHS: ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ',
    +    'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ',
    +    'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'],
    +  STANDALONEMONTHS: ['ᎤᏃᎸᏔᏅ', 'ᎧᎦᎵ', 'ᎠᏅᏱ', 'ᎧᏬᏂ',
    +    'ᎠᏂᏍᎬᏘ', 'ᏕᎭᎷᏱ', 'ᎫᏰᏉᏂ', 'ᎦᎶᏂ',
    +    'ᏚᎵᏍᏗ', 'ᏚᏂᏅᏗ', 'ᏅᏓᏕᏆ', 'ᎥᏍᎩᏱ'],
    +  SHORTMONTHS: ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ', 'ᏕᎭ',
    +    'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'],
    +  STANDALONESHORTMONTHS: ['ᎤᏃ', 'ᎧᎦ', 'ᎠᏅ', 'ᎧᏬ', 'ᎠᏂ',
    +    'ᏕᎭ', 'ᎫᏰ', 'ᎦᎶ', 'ᏚᎵ', 'ᏚᏂ', 'ᏅᏓ', 'ᎥᏍ'],
    +  WEEKDAYS: ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ',
    +    'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ',
    +    'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'],
    +  STANDALONEWEEKDAYS: ['ᎤᎾᏙᏓᏆᏍᎬ', 'ᎤᎾᏙᏓᏉᏅᎯ',
    +    'ᏔᎵᏁᎢᎦ', 'ᏦᎢᏁᎢᎦ', 'ᏅᎩᏁᎢᎦ',
    +    'ᏧᎾᎩᎶᏍᏗ', 'ᎤᎾᏙᏓᏈᏕᎾ'],
    +  SHORTWEEKDAYS: ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ',
    +    'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
    +  STANDALONESHORTWEEKDAYS: ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ',
    +    'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
    +  NARROWWEEKDAYS: ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
    +  STANDALONENARROWWEEKDAYS: ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb.
    + */
    +goog.i18n.DateTimeSymbols_ckb = {
    +  ZERODIGIT: 0x0660,
    +  ERAS: ['پێش زاییین', 'ز'],
    +  ERANAMES: ['پێش زایین', 'زایینی'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', 'D'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', 'D'],
    +  MONTHS: ['کانوونی دووەم', 'شوبات', 'ئازار',
    +    'نیسان', 'ئایار', 'حوزەیران', 'تەمووز', 'ئاب',
    +    'ئەیلوول', 'تشرینی یەکەم', 'تشرینی دووەم',
    +    'کانونی یەکەم'],
    +  STANDALONEMONTHS: ['کانوونی دووەم', 'شوبات', 'ئازار',
    +    'نیسان', 'ئایار', 'حوزەیران', 'تەمووز', 'ئاب',
    +    'ئەیلوول', 'تشرینی یەکەم', 'تشرینی دووەم',
    +    'کانونی یەکەم'],
    +  SHORTMONTHS: ['کانوونی دووەم', 'شوبات', 'ئازار',
    +    'نیسان', 'ئایار', 'حوزەیران', 'تەمووز', 'ئاب',
    +    'ئەیلوول', 'تشرینی یەکەم', 'تشرینی دووەم',
    +    'کانونی یەکەم'],
    +  STANDALONESHORTMONTHS: ['کانوونی دووەم', 'شوبات',
    +    'ئازار', 'نیسان', 'ئایار', 'حوزەیران',
    +    'تەمووز', 'ئاب', 'ئەیلوول', 'تشرینی یەکەم',
    +    'تشرینی دووەم', 'کانونی یەکەم'],
    +  WEEKDAYS: ['یەکشەممە', 'دووشەممە', 'سێشەممە',
    +    'چوارشەممە', 'پێنجشەممە', 'ھەینی', 'شەممە'],
    +  STANDALONEWEEKDAYS: ['یەکشەممە', 'دووشەممە', 'سێشەممە',
    +    'چوارشەممە', 'پێنجشەممە', 'ھەینی', 'شەممە'],
    +  SHORTWEEKDAYS: ['یەکشەممە', 'دووشەممە', 'سێشەممە',
    +    'چوارشەممە', 'پێنجشەممە', 'ھەینی', 'شەممە'],
    +  STANDALONESHORTWEEKDAYS: ['یەکشەممە', 'دووشەممە',
    +    'سێشەممە', 'چوارشەممە', 'پێنجشەممە', 'ھەینی',
    +    'شەممە'],
    +  NARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ھ', 'ش'],
    +  STANDALONENARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ھ', 'ش'],
    +  SHORTQUARTERS: ['چارەکی یەکەم', 'چارەکی دووەم',
    +    'چارەکی سێەم', 'چارەکی چوارەم'],
    +  QUARTERS: ['چارەکی یەکەم', 'چارەکی دووەم',
    +    'چارەکی سێەم', 'چارەکی چوارەم'],
    +  AMPMS: ['ب.ن', 'د.ن'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'dی MMMMی y', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_Arab.
    + */
    +goog.i18n.DateTimeSymbols_ckb_Arab = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_Arab_IQ.
    + */
    +goog.i18n.DateTimeSymbols_ckb_Arab_IQ = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_Arab_IR.
    + */
    +goog.i18n.DateTimeSymbols_ckb_Arab_IR = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_IQ.
    + */
    +goog.i18n.DateTimeSymbols_ckb_IQ = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_IR.
    + */
    +goog.i18n.DateTimeSymbols_ckb_IR = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_Latn.
    + */
    +goog.i18n.DateTimeSymbols_ckb_Latn = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ckb_Latn_IQ.
    + */
    +goog.i18n.DateTimeSymbols_ckb_Latn_IQ = goog.i18n.DateTimeSymbols_ckb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale cs_CZ.
    + */
    +goog.i18n.DateTimeSymbols_cs_CZ = {
    +  ERAS: ['př. n. l.', 'n. l.'],
    +  ERANAMES: ['př. n. l.', 'n. l.'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ledna', 'února', 'března', 'dubna', 'května', 'června',
    +    'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'],
    +  STANDALONEMONTHS: ['leden', 'únor', 'březen', 'duben', 'květen', 'červen',
    +    'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'],
    +  SHORTMONTHS: ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc', 'srp',
    +    'zář', 'říj', 'lis', 'pro'],
    +  STANDALONESHORTMONTHS: ['led', 'úno', 'bře', 'dub', 'kvě', 'čvn', 'čvc',
    +    'srp', 'zář', 'říj', 'lis', 'pro'],
    +  WEEKDAYS: ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['neděle', 'pondělí', 'úterý', 'středa', 'čtvrtek',
    +    'pátek', 'sobota'],
    +  SHORTWEEKDAYS: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
    +  NARROWWEEKDAYS: ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. čtvrtletí', '2. čtvrtletí', '3. čtvrtletí',
    +    '4. čtvrtletí'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. M. y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale cy_GB.
    + */
    +goog.i18n.DateTimeSymbols_cy_GB = {
    +  ERAS: ['CC', 'OC'],
    +  ERANAMES: ['Cyn Crist', 'Oed Crist'],
    +  NARROWMONTHS: ['I', 'Ch', 'M', 'E', 'M', 'M', 'G', 'A', 'M', 'H', 'T', 'Rh'],
    +  STANDALONENARROWMONTHS: ['I', 'Ch', 'M', 'E', 'M', 'M', 'G', 'A', 'M', 'H',
    +    'T', 'Rh'],
    +  MONTHS: ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin',
    +    'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'],
    +  STANDALONEMONTHS: ['Ionawr', 'Chwefror', 'Mawrth', 'Ebrill', 'Mai', 'Mehefin',
    +    'Gorffennaf', 'Awst', 'Medi', 'Hydref', 'Tachwedd', 'Rhagfyr'],
    +  SHORTMONTHS: ['Ion', 'Chwef', 'Mawrth', 'Ebrill', 'Mai', 'Meh', 'Gorff',
    +    'Awst', 'Medi', 'Hyd', 'Tach', 'Rhag'],
    +  STANDALONESHORTMONTHS: ['Ion', 'Chw', 'Maw', 'Ebr', 'Mai', 'Meh', 'Gor',
    +    'Awst', 'Medi', 'Hyd', 'Tach', 'Rhag'],
    +  WEEKDAYS: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher', 'Dydd Iau',
    +    'Dydd Gwener', 'Dydd Sadwrn'],
    +  STANDALONEWEEKDAYS: ['Dydd Sul', 'Dydd Llun', 'Dydd Mawrth', 'Dydd Mercher',
    +    'Dydd Iau', 'Dydd Gwener', 'Dydd Sadwrn'],
    +  SHORTWEEKDAYS: ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwen', 'Sad'],
    +  STANDALONESHORTWEEKDAYS: ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwe', 'Sad'],
    +  NARROWWEEKDAYS: ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
    +  SHORTQUARTERS: ['Ch1', 'Ch2', 'Ch3', 'Ch4'],
    +  QUARTERS: ['Chwarter 1af', '2il chwarter', '3ydd chwarter', '4ydd chwarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'am\' {0}', '{1} \'am\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale da_DK.
    + */
    +goog.i18n.DateTimeSymbols_da_DK = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['1. kvt.', '2. kvt.', '3. kvt.', '4. kvt.'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE \'den\' d. MMMM y', 'd. MMMM y', 'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} \'kl.\' {0}', '{1} \'kl.\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale da_GL.
    + */
    +goog.i18n.DateTimeSymbols_da_GL = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'marts', 'april', 'maj', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['1. kvt.', '2. kvt.', '3. kvt.', '4. kvt.'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE \'den\' d. MMMM y', 'd. MMMM y', 'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} \'kl.\' {0}', '{1} \'kl.\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dav.
    + */
    +goog.i18n.DateTimeSymbols_dav = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Kristo', 'Baada ya Kristo'],
    +  NARROWMONTHS: ['I', 'K', 'K', 'K', 'K', 'K', 'M', 'W', 'I', 'I', 'I', 'I'],
    +  STANDALONENARROWMONTHS: ['I', 'K', 'K', 'K', 'K', 'K', 'M', 'W', 'I', 'I',
    +    'I', 'I'],
    +  MONTHS: ['Mori ghwa imbiri', 'Mori ghwa kawi', 'Mori ghwa kadadu',
    +    'Mori ghwa kana', 'Mori ghwa kasanu', 'Mori ghwa karandadu',
    +    'Mori ghwa mfungade', 'Mori ghwa wunyanya', 'Mori ghwa ikenda',
    +    'Mori ghwa ikumi', 'Mori ghwa ikumi na imweri', 'Mori ghwa ikumi na iwi'],
    +  STANDALONEMONTHS: ['Mori ghwa imbiri', 'Mori ghwa kawi', 'Mori ghwa kadadu',
    +    'Mori ghwa kana', 'Mori ghwa kasanu', 'Mori ghwa karandadu',
    +    'Mori ghwa mfungade', 'Mori ghwa wunyanya', 'Mori ghwa ikenda',
    +    'Mori ghwa ikumi', 'Mori ghwa ikumi na imweri', 'Mori ghwa ikumi na iwi'],
    +  SHORTMONTHS: ['Imb', 'Kaw', 'Kad', 'Kan', 'Kas', 'Kar', 'Mfu', 'Wun', 'Ike',
    +    'Iku', 'Imw', 'Iwi'],
    +  STANDALONESHORTMONTHS: ['Imb', 'Kaw', 'Kad', 'Kan', 'Kas', 'Kar', 'Mfu',
    +    'Wun', 'Ike', 'Iku', 'Imw', 'Iwi'],
    +  WEEKDAYS: ['Ituku ja jumwa', 'Kuramuka jimweri', 'Kuramuka kawi',
    +    'Kuramuka kadadu', 'Kuramuka kana', 'Kuramuka kasanu', 'Kifula nguwo'],
    +  STANDALONEWEEKDAYS: ['Ituku ja jumwa', 'Kuramuka jimweri', 'Kuramuka kawi',
    +    'Kuramuka kadadu', 'Kuramuka kana', 'Kuramuka kasanu', 'Kifula nguwo'],
    +  SHORTWEEKDAYS: ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'],
    +  STANDALONESHORTWEEKDAYS: ['Jum', 'Jim', 'Kaw', 'Kad', 'Kan', 'Kas', 'Ngu'],
    +  NARROWWEEKDAYS: ['J', 'J', 'K', 'K', 'K', 'K', 'N'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'J', 'K', 'K', 'K', 'K', 'N'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kimu cha imbiri', 'Kimu cha kawi', 'Kimu cha kadadu',
    +    'Kimu cha kana'],
    +  AMPMS: ['Luma lwa K', 'luma lwa p'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dav_KE.
    + */
    +goog.i18n.DateTimeSymbols_dav_KE = goog.i18n.DateTimeSymbols_dav;
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_BE.
    + */
    +goog.i18n.DateTimeSymbols_de_BE = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_DE.
    + */
    +goog.i18n.DateTimeSymbols_de_DE = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_LI.
    + */
    +goog.i18n.DateTimeSymbols_de_LI = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale de_LU.
    + */
    +goog.i18n.DateTimeSymbols_de_LU = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'März', 'Apr.', 'Mai', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
    +    'Freitag', 'Samstag'],
    +  STANDALONEWEEKDAYS: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch',
    +    'Donnerstag', 'Freitag', 'Samstag'],
    +  SHORTWEEKDAYS: ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nachm.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'um\' {0}', '{1} \'um\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dje.
    + */
    +goog.i18n.DateTimeSymbols_dje = {
    +  ERAS: ['IJ', 'IZ'],
    +  ERANAMES: ['Isaa jine', 'Isaa zamanoo'],
    +  NARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ',
    +    'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
    +  STANDALONEMONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me',
    +    'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur',
    +    'Deesanbur'],
    +  SHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek',
    +    'Okt', 'Noo', 'Dee'],
    +  STANDALONESHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy',
    +    'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
    +  WEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamisi', 'Alzuma',
    +    'Asibti'],
    +  STANDALONEWEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamisi',
    +    'Alzuma', 'Asibti'],
    +  SHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  STANDALONESHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  NARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'M', 'Z', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'M', 'Z', 'S'],
    +  SHORTQUARTERS: ['A1', 'A2', 'A3', 'A4'],
    +  QUARTERS: ['Arrubu 1', 'Arrubu 2', 'Arrubu 3', 'Arrubu 4'],
    +  AMPMS: ['Subbaahi', 'Zaarikay b'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dje_NE.
    + */
    +goog.i18n.DateTimeSymbols_dje_NE = goog.i18n.DateTimeSymbols_dje;
    +
    +
    +/**
    + * Date/time formatting symbols for locale dsb.
    + */
    +goog.i18n.DateTimeSymbols_dsb = {
    +  ERAS: ['pś.Chr.n.', 'pó Chr.n.'],
    +  ERANAMES: ['pśed Kristusowym naroźenim', 'pó Kristusowem naroźenju'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januara', 'februara', 'měrca', 'apryla', 'maja', 'junija',
    +    'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'měrc', 'apryl', 'maj', 'junij',
    +    'julij', 'awgust', 'september', 'oktober', 'nowember', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'měr.', 'apr.', 'maj.', 'jun.', 'jul.', 'awg.',
    +    'sep.', 'okt.', 'now.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'měr', 'apr', 'maj', 'jun', 'jul',
    +    'awg', 'sep', 'okt', 'now', 'dec'],
    +  WEEKDAYS: ['njeźela', 'pónjeźele', 'wałtora', 'srjoda', 'stwórtk',
    +    'pětk', 'sobota'],
    +  STANDALONEWEEKDAYS: ['njeźela', 'pónjeźele', 'wałtora', 'srjoda',
    +    'stwórtk', 'pětk', 'sobota'],
    +  SHORTWEEKDAYS: ['nje', 'pón', 'wał', 'srj', 'stw', 'pět', 'sob'],
    +  STANDALONESHORTWEEKDAYS: ['nje', 'pón', 'wał', 'srj', 'stw', 'pět', 'sob'],
    +  NARROWWEEKDAYS: ['n', 'p', 'w', 's', 's', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'w', 's', 's', 'p', 's'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. kwartal', '2. kwartal', '3. kwartal', '4. kwartal'],
    +  AMPMS: ['dopołdnja', 'wótpołdnja'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dsb_DE.
    + */
    +goog.i18n.DateTimeSymbols_dsb_DE = goog.i18n.DateTimeSymbols_dsb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale dua.
    + */
    +goog.i18n.DateTimeSymbols_dua = {
    +  ERAS: ['ɓ.Ys', 'mb.Ys'],
    +  ERANAMES: ['ɓoso ɓwá yáɓe lá', 'mbúsa kwédi a Yés'],
    +  NARROWMONTHS: ['d', 'ŋ', 's', 'd', 'e', 'e', 'm', 'd', 'n', 'm', 't', 'e'],
    +  STANDALONENARROWMONTHS: ['d', 'ŋ', 's', 'd', 'e', 'e', 'm', 'd', 'n', 'm',
    +    't', 'e'],
    +  MONTHS: ['dimɔ́di', 'ŋgɔndɛ', 'sɔŋɛ', 'diɓáɓá', 'emiasele',
    +    'esɔpɛsɔpɛ', 'madiɓɛ́díɓɛ́', 'diŋgindi', 'nyɛtɛki',
    +    'mayésɛ́', 'tiníní', 'eláŋgɛ́'],
    +  STANDALONEMONTHS: ['dimɔ́di', 'ŋgɔndɛ', 'sɔŋɛ', 'diɓáɓá',
    +    'emiasele', 'esɔpɛsɔpɛ', 'madiɓɛ́díɓɛ́', 'diŋgindi',
    +    'nyɛtɛki', 'mayésɛ́', 'tiníní', 'eláŋgɛ́'],
    +  SHORTMONTHS: ['di', 'ŋgɔn', 'sɔŋ', 'diɓ', 'emi', 'esɔ', 'mad', 'diŋ',
    +    'nyɛt', 'may', 'tin', 'elá'],
    +  STANDALONESHORTMONTHS: ['di', 'ŋgɔn', 'sɔŋ', 'diɓ', 'emi', 'esɔ', 'mad',
    +    'diŋ', 'nyɛt', 'may', 'tin', 'elá'],
    +  WEEKDAYS: ['éti', 'mɔ́sú', 'kwasú', 'mukɔ́sú', 'ŋgisú',
    +    'ɗónɛsú', 'esaɓasú'],
    +  STANDALONEWEEKDAYS: ['éti', 'mɔ́sú', 'kwasú', 'mukɔ́sú', 'ŋgisú',
    +    'ɗónɛsú', 'esaɓasú'],
    +  SHORTWEEKDAYS: ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón', 'esa'],
    +  STANDALONESHORTWEEKDAYS: ['ét', 'mɔ́s', 'kwa', 'muk', 'ŋgi', 'ɗón',
    +    'esa'],
    +  NARROWWEEKDAYS: ['e', 'm', 'k', 'm', 'ŋ', 'ɗ', 'e'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'm', 'k', 'm', 'ŋ', 'ɗ', 'e'],
    +  SHORTQUARTERS: ['ndu1', 'ndu2', 'ndu3', 'ndu4'],
    +  QUARTERS: ['ndúmbū nyá ɓosó', 'ndúmbū ní lóndɛ́ íɓaá',
    +    'ndúmbū ní lóndɛ́ ílálo', 'ndúmbū ní lóndɛ́ ínɛ́y'],
    +  AMPMS: ['idiɓa', 'ebyámu'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dua_CM.
    + */
    +goog.i18n.DateTimeSymbols_dua_CM = goog.i18n.DateTimeSymbols_dua;
    +
    +
    +/**
    + * Date/time formatting symbols for locale dyo.
    + */
    +goog.i18n.DateTimeSymbols_dyo = {
    +  ERAS: ['ArY', 'AtY'],
    +  ERANAMES: ['Ariŋuu Yeesu', 'Atooŋe Yeesu'],
    +  NARROWMONTHS: ['S', 'F', 'M', 'A', 'M', 'S', 'S', 'U', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['S', 'F', 'M', 'A', 'M', 'S', 'S', 'U', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Sanvie', 'Fébirie', 'Mars', 'Aburil', 'Mee', 'Sueŋ', 'Súuyee',
    +    'Ut', 'Settembar', 'Oktobar', 'Novembar', 'Disambar'],
    +  STANDALONEMONTHS: ['Sanvie', 'Fébirie', 'Mars', 'Aburil', 'Mee', 'Sueŋ',
    +    'Súuyee', 'Ut', 'Settembar', 'Oktobar', 'Novembar', 'Disambar'],
    +  SHORTMONTHS: ['Sa', 'Fe', 'Ma', 'Ab', 'Me', 'Su', 'Sú', 'Ut', 'Se', 'Ok',
    +    'No', 'De'],
    +  STANDALONESHORTMONTHS: ['Sa', 'Fe', 'Ma', 'Ab', 'Me', 'Su', 'Sú', 'Ut', 'Se',
    +    'Ok', 'No', 'De'],
    +  WEEKDAYS: ['Dimas', 'Teneŋ', 'Talata', 'Alarbay', 'Aramisay', 'Arjuma',
    +    'Sibiti'],
    +  STANDALONEWEEKDAYS: ['Dimas', 'Teneŋ', 'Talata', 'Alarbay', 'Aramisay',
    +    'Arjuma', 'Sibiti'],
    +  SHORTWEEKDAYS: ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'],
    +  STANDALONESHORTWEEKDAYS: ['Dim', 'Ten', 'Tal', 'Ala', 'Ara', 'Arj', 'Sib'],
    +  NARROWWEEKDAYS: ['D', 'T', 'T', 'A', 'A', 'A', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'T', 'T', 'A', 'A', 'A', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dyo_SN.
    + */
    +goog.i18n.DateTimeSymbols_dyo_SN = goog.i18n.DateTimeSymbols_dyo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale dz.
    + */
    +goog.i18n.DateTimeSymbols_dz = {
    +  ZERODIGIT: 0x0F20,
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['༡', '༢', '༣', '4', '༥', '༦', '༧', '༨', '9',
    +    '༡༠', '༡༡', '༡༢'],
    +  STANDALONENARROWMONTHS: ['༡', '༢', '༣', '༤', '༥', '༦', '༧',
    +    '༨', '༩', '༡༠', '༡༡', '༡༢'],
    +  MONTHS: ['ཟླ་དངཔ་', 'ཟླ་གཉིས་པ་',
    +    'ཟླ་གསུམ་པ་', 'ཟླ་བཞི་པ་',
    +    'ཟླ་ལྔ་པ་', 'ཟླ་དྲུག་པ',
    +    'ཟླ་བདུན་པ་', 'ཟླ་བརྒྱད་པ་',
    +    'ཟླ་དགུ་པ་', 'ཟླ་བཅུ་པ་',
    +    'ཟླ་བཅུ་གཅིག་པ་',
    +    'ཟླ་བཅུ་གཉིས་པ་'],
    +  STANDALONEMONTHS: ['སྤྱི་ཟླ་དངཔ་',
    +    'སྤྱི་ཟླ་གཉིས་པ་',
    +    'སྤྱི་ཟླ་གསུམ་པ་',
    +    'སྤྱི་ཟླ་བཞི་པ',
    +    'སྤྱི་ཟླ་ལྔ་པ་',
    +    'སྤྱི་ཟླ་དྲུག་པ',
    +    'སྤྱི་ཟླ་བདུན་པ་',
    +    'སྤྱི་ཟླ་བརྒྱད་པ་',
    +    'སྤྱི་ཟླ་དགུ་པ་',
    +    'སྤྱི་ཟླ་བཅུ་པ་',
    +    'སྤྱི་ཟླ་བཅུ་གཅིག་པ་',
    +    'སྤྱི་ཟླ་བཅུ་གཉིས་པ་'],
    +  SHORTMONTHS: ['༡', '༢', '༣', '༤', '༥', '༦', '༧', '༨', '༩',
    +    '༡༠', '༡༡', '12'],
    +  STANDALONESHORTMONTHS: ['ཟླ་༡', 'ཟླ་༢', 'ཟླ་༣',
    +    'ཟླ་༤', 'ཟླ་༥', 'ཟླ་༦', 'ཟླ་༧',
    +    'ཟླ་༨', 'ཟླ་༩', 'ཟླ་༡༠', 'ཟླ་༡༡',
    +    'ཟླ་༡༢'],
    +  WEEKDAYS: ['གཟའ་ཟླ་བ་',
    +    'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་',
    +    'གཟའ་ཕུར་བུ་', 'གཟའ་པ་སངས་',
    +    'གཟའ་སྤེན་པ་', 'གཟའ་ཉི་མ་'],
    +  STANDALONEWEEKDAYS: ['གཟའ་ཟླ་བ་',
    +    'གཟའ་མིག་དམར་', 'གཟའ་ལྷག་པ་',
    +    'གཟའ་ཕུར་བུ་', 'གཟའ་པ་སངས་',
    +    'གཟའ་སྤེན་པ་', 'གཟའ་ཉི་མ་'],
    +  SHORTWEEKDAYS: ['ཟླ་', 'མིར་', 'ལྷག་', 'ཕུར་',
    +    'སངས་', 'སྤེན་', 'ཉི་'],
    +  STANDALONESHORTWEEKDAYS: ['ཟླ་', 'མིར་', 'ལྷག་',
    +    'ཕུར་', 'སངས་', 'སྤེན་', 'ཉི་'],
    +  NARROWWEEKDAYS: ['ཟླ', 'མིར', 'ལྷག', 'ཕུར', 'སངྶ',
    +    'སྤེན', 'ཉི'],
    +  STANDALONENARROWWEEKDAYS: ['ཟླ', 'མིར', 'ལྷག', 'ཕུར',
    +    'སངྶ', 'སྤེན', 'ཉི'],
    +  SHORTQUARTERS: ['བཞི་དཔྱ་༡', 'བཞི་དཔྱ་༢',
    +    'བཞི་དཔྱ་༣', 'བཞི་དཔྱ་༤'],
    +  QUARTERS: ['བཞི་དཔྱ་དང་པ་',
    +    'བཞི་དཔྱ་གཉིས་པ་',
    +    'བཞི་དཔྱ་གསུམ་པ་',
    +    'བཞི་དཔྱ་བཞི་པ་'],
    +  AMPMS: ['སྔ་ཆ་', 'ཕྱི་ཆ་'],
    +  DATEFORMATS: ['EEEE, སྤྱི་ལོ་y MMMM ཚེས་dd',
    +    'སྤྱི་ལོ་y MMMM ཚེས་ dd',
    +    'སྤྱི་ལོ་y ཟླ་MMM ཚེས་dd', 'y-MM-dd'],
    +  TIMEFORMATS: ['ཆུ་ཚོད་ h སྐར་མ་ mm:ss a zzzz',
    +    'ཆུ་ཚོད་ h སྐར་མ་ mm:ss a z',
    +    'ཆུ་ཚོད་h:mm:ss a',
    +    'ཆུ་ཚོད་ h སྐར་མ་ mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale dz_BT.
    + */
    +goog.i18n.DateTimeSymbols_dz_BT = goog.i18n.DateTimeSymbols_dz;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ebu.
    + */
    +goog.i18n.DateTimeSymbols_ebu = {
    +  ERAS: ['MK', 'TK'],
    +  ERANAMES: ['Mbere ya Kristo', 'Thutha wa Kristo'],
    +  NARROWMONTHS: ['M', 'K', 'K', 'K', 'G', 'G', 'M', 'K', 'K', 'I', 'I', 'I'],
    +  STANDALONENARROWMONTHS: ['M', 'K', 'K', 'K', 'G', 'G', 'M', 'K', 'K', 'I',
    +    'I', 'I'],
    +  MONTHS: ['Mweri wa mbere', 'Mweri wa kaĩri', 'Mweri wa kathatũ',
    +    'Mweri wa kana', 'Mweri wa gatano', 'Mweri wa gatantatũ',
    +    'Mweri wa mũgwanja', 'Mweri wa kanana', 'Mweri wa kenda',
    +    'Mweri wa ikũmi', 'Mweri wa ikũmi na ũmwe',
    +    'Mweri wa ikũmi na Kaĩrĩ'],
    +  STANDALONEMONTHS: ['Mweri wa mbere', 'Mweri wa kaĩri', 'Mweri wa kathatũ',
    +    'Mweri wa kana', 'Mweri wa gatano', 'Mweri wa gatantatũ',
    +    'Mweri wa mũgwanja', 'Mweri wa kanana', 'Mweri wa kenda',
    +    'Mweri wa ikũmi', 'Mweri wa ikũmi na ũmwe',
    +    'Mweri wa ikũmi na Kaĩrĩ'],
    +  SHORTMONTHS: ['Mbe', 'Kai', 'Kat', 'Kan', 'Gat', 'Gan', 'Mug', 'Knn', 'Ken',
    +    'Iku', 'Imw', 'Igi'],
    +  STANDALONESHORTMONTHS: ['Mbe', 'Kai', 'Kat', 'Kan', 'Gat', 'Gan', 'Mug',
    +    'Knn', 'Ken', 'Iku', 'Imw', 'Igi'],
    +  WEEKDAYS: ['Kiumia', 'Njumatatu', 'Njumaine', 'Njumatano', 'Aramithi',
    +    'Njumaa', 'NJumamothii'],
    +  STANDALONEWEEKDAYS: ['Kiumia', 'Njumatatu', 'Njumaine', 'Njumatano',
    +    'Aramithi', 'Njumaa', 'NJumamothii'],
    +  SHORTWEEKDAYS: ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'],
    +  STANDALONESHORTWEEKDAYS: ['Kma', 'Tat', 'Ine', 'Tan', 'Arm', 'Maa', 'NMM'],
    +  NARROWWEEKDAYS: ['K', 'N', 'N', 'N', 'A', 'M', 'N'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'N', 'N', 'N', 'A', 'M', 'N'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kuota ya mbere', 'Kuota ya Kaĩrĩ', 'Kuota ya kathatu',
    +    'Kuota ya kana'],
    +  AMPMS: ['KI', 'UT'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ebu_KE.
    + */
    +goog.i18n.DateTimeSymbols_ebu_KE = goog.i18n.DateTimeSymbols_ebu;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ee.
    + */
    +goog.i18n.DateTimeSymbols_ee = {
    +  ERAS: ['hY', 'Yŋ'],
    +  ERANAMES: ['Hafi Yesu Va Do ŋgɔ', 'Yesu Ŋɔli'],
    +  NARROWMONTHS: ['d', 'd', 't', 'a', 'd', 'm', 's', 'd', 'a', 'k', 'a', 'd'],
    +  STANDALONENARROWMONTHS: ['d', 'd', 't', 'a', 'd', 'm', 's', 'd', 'a', 'k',
    +    'a', 'd'],
    +  MONTHS: ['dzove', 'dzodze', 'tedoxe', 'afɔfĩe', 'dama', 'masa', 'siamlɔm',
    +    'deasiamime', 'anyɔnyɔ', 'kele', 'adeɛmekpɔxe', 'dzome'],
    +  STANDALONEMONTHS: ['dzove', 'dzodze', 'tedoxe', 'afɔfĩe', 'dama', 'masa',
    +    'siamlɔm', 'deasiamime', 'anyɔnyɔ', 'kele', 'adeɛmekpɔxe', 'dzome'],
    +  SHORTMONTHS: ['dzv', 'dzd', 'ted', 'afɔ', 'dam', 'mas', 'sia', 'dea', 'any',
    +    'kel', 'ade', 'dzm'],
    +  STANDALONESHORTMONTHS: ['dzv', 'dzd', 'ted', 'afɔ', 'dam', 'mas', 'sia',
    +    'dea', 'any', 'kel', 'ade', 'dzm'],
    +  WEEKDAYS: ['kɔsiɖa', 'dzoɖa', 'blaɖa', 'kuɖa', 'yawoɖa', 'fiɖa',
    +    'memleɖa'],
    +  STANDALONEWEEKDAYS: ['kɔsiɖa', 'dzoɖa', 'blaɖa', 'kuɖa', 'yawoɖa',
    +    'fiɖa', 'memleɖa'],
    +  SHORTWEEKDAYS: ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'],
    +  STANDALONESHORTWEEKDAYS: ['kɔs', 'dzo', 'bla', 'kuɖ', 'yaw', 'fiɖ', 'mem'],
    +  NARROWWEEKDAYS: ['k', 'd', 'b', 'k', 'y', 'f', 'm'],
    +  STANDALONENARROWWEEKDAYS: ['k', 'd', 'b', 'k', 'y', 'f', 'm'],
    +  SHORTQUARTERS: ['k1', 'k2', 'k3', 'k4'],
    +  QUARTERS: ['kɔta gbãtɔ', 'kɔta evelia', 'kɔta etɔ̃lia',
    +    'kɔta enelia'],
    +  AMPMS: ['ŋdi', 'ɣetrɔ'],
    +  DATEFORMATS: ['EEEE, MMMM d \'lia\' y', 'MMMM d \'lia\' y',
    +    'MMM d \'lia\', y', 'M/d/yy'],
    +  TIMEFORMATS: ['a h:mm:ss zzzz', 'a \'ga\' h:mm:ss z', 'a \'ga\' h:mm:ss',
    +    'a \'ga\' h:mm'],
    +  DATETIMEFORMATS: ['{0} {1}', '{0} {1}', '{0} {1}', '{0} {1}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ee_GH.
    + */
    +goog.i18n.DateTimeSymbols_ee_GH = goog.i18n.DateTimeSymbols_ee;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ee_TG.
    + */
    +goog.i18n.DateTimeSymbols_ee_TG = goog.i18n.DateTimeSymbols_ee;
    +
    +
    +/**
    + * Date/time formatting symbols for locale el_CY.
    + */
    +goog.i18n.DateTimeSymbols_el_CY = {
    +  ERAS: ['π.Χ.', 'μ.Χ.'],
    +  ERANAMES: ['προ Χριστού', 'μετά Χριστόν'],
    +  NARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ', 'Ο',
    +    'Ν', 'Δ'],
    +  STANDALONENARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ',
    +    'Ο', 'Ν', 'Δ'],
    +  MONTHS: ['Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου',
    +    'Απριλίου', 'Μαΐου', 'Ιουνίου', 'Ιουλίου',
    +    'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου',
    +    'Νοεμβρίου', 'Δεκεμβρίου'],
    +  STANDALONEMONTHS: ['Ιανουάριος', 'Φεβρουάριος',
    +    'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος',
    +    'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος',
    +    'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
    +  SHORTMONTHS: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαΐ', 'Ιουν',
    +    'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
    +  STANDALONESHORTMONTHS: ['Ιαν', 'Φεβ', 'Μάρ', 'Απρ', 'Μάι',
    +    'Ιούν', 'Ιούλ', 'Αύγ', 'Σεπ', 'Οκτ', 'Νοέ', 'Δεκ'],
    +  WEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη',
    +    'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  STANDALONEWEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη',
    +    'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  SHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ', 'Παρ',
    +    'Σάβ'],
    +  STANDALONESHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ',
    +    'Παρ', 'Σάβ'],
    +  NARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  STANDALONENARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  SHORTQUARTERS: ['Τ1', 'Τ2', 'Τ3', 'Τ4'],
    +  QUARTERS: ['1ο τρίμηνο', '2ο τρίμηνο', '3ο τρίμηνο',
    +    '4ο τρίμηνο'],
    +  AMPMS: ['π.μ.', 'μ.μ.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} - {0}', '{1} - {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale el_GR.
    + */
    +goog.i18n.DateTimeSymbols_el_GR = {
    +  ERAS: ['π.Χ.', 'μ.Χ.'],
    +  ERANAMES: ['προ Χριστού', 'μετά Χριστόν'],
    +  NARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ', 'Ο',
    +    'Ν', 'Δ'],
    +  STANDALONENARROWMONTHS: ['Ι', 'Φ', 'Μ', 'Α', 'Μ', 'Ι', 'Ι', 'Α', 'Σ',
    +    'Ο', 'Ν', 'Δ'],
    +  MONTHS: ['Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου',
    +    'Απριλίου', 'Μαΐου', 'Ιουνίου', 'Ιουλίου',
    +    'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου',
    +    'Νοεμβρίου', 'Δεκεμβρίου'],
    +  STANDALONEMONTHS: ['Ιανουάριος', 'Φεβρουάριος',
    +    'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος',
    +    'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος',
    +    'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
    +  SHORTMONTHS: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαΐ', 'Ιουν',
    +    'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
    +  STANDALONESHORTMONTHS: ['Ιαν', 'Φεβ', 'Μάρ', 'Απρ', 'Μάι',
    +    'Ιούν', 'Ιούλ', 'Αύγ', 'Σεπ', 'Οκτ', 'Νοέ', 'Δεκ'],
    +  WEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη',
    +    'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  STANDALONEWEEKDAYS: ['Κυριακή', 'Δευτέρα', 'Τρίτη',
    +    'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'],
    +  SHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ', 'Παρ',
    +    'Σάβ'],
    +  STANDALONESHORTWEEKDAYS: ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ',
    +    'Παρ', 'Σάβ'],
    +  NARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  STANDALONENARROWWEEKDAYS: ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
    +  SHORTQUARTERS: ['Τ1', 'Τ2', 'Τ3', 'Τ4'],
    +  QUARTERS: ['1ο τρίμηνο', '2ο τρίμηνο', '3ο τρίμηνο',
    +    '4ο τρίμηνο'],
    +  AMPMS: ['π.μ.', 'μ.μ.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} - {0}', '{1} - {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_001.
    + */
    +goog.i18n.DateTimeSymbols_en_001 = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_150.
    + */
    +goog.i18n.DateTimeSymbols_en_150 = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH \'h\' mm \'min\' ss \'s\' zzzz', 'HH:mm:ss z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_AG.
    + */
    +goog.i18n.DateTimeSymbols_en_AG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_AI.
    + */
    +goog.i18n.DateTimeSymbols_en_AI = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_AS.
    + */
    +goog.i18n.DateTimeSymbols_en_AS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BB.
    + */
    +goog.i18n.DateTimeSymbols_en_BB = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BE.
    + */
    +goog.i18n.DateTimeSymbols_en_BE = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH \'h\' mm \'min\' ss \'s\' zzzz', 'HH:mm:ss z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BM.
    + */
    +goog.i18n.DateTimeSymbols_en_BM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BS.
    + */
    +goog.i18n.DateTimeSymbols_en_BS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BW.
    + */
    +goog.i18n.DateTimeSymbols_en_BW = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_BZ.
    + */
    +goog.i18n.DateTimeSymbols_en_BZ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_CA.
    + */
    +goog.i18n.DateTimeSymbols_en_CA = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'y-MM-dd'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_CC.
    + */
    +goog.i18n.DateTimeSymbols_en_CC = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_CK.
    + */
    +goog.i18n.DateTimeSymbols_en_CK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_CM.
    + */
    +goog.i18n.DateTimeSymbols_en_CM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_CX.
    + */
    +goog.i18n.DateTimeSymbols_en_CX = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_DG.
    + */
    +goog.i18n.DateTimeSymbols_en_DG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_DM.
    + */
    +goog.i18n.DateTimeSymbols_en_DM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_ER.
    + */
    +goog.i18n.DateTimeSymbols_en_ER = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_FJ.
    + */
    +goog.i18n.DateTimeSymbols_en_FJ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_FK.
    + */
    +goog.i18n.DateTimeSymbols_en_FK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_FM.
    + */
    +goog.i18n.DateTimeSymbols_en_FM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GD.
    + */
    +goog.i18n.DateTimeSymbols_en_GD = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GG.
    + */
    +goog.i18n.DateTimeSymbols_en_GG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GH.
    + */
    +goog.i18n.DateTimeSymbols_en_GH = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GI.
    + */
    +goog.i18n.DateTimeSymbols_en_GI = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GM.
    + */
    +goog.i18n.DateTimeSymbols_en_GM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GU.
    + */
    +goog.i18n.DateTimeSymbols_en_GU = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_GY.
    + */
    +goog.i18n.DateTimeSymbols_en_GY = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_HK.
    + */
    +goog.i18n.DateTimeSymbols_en_HK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_IM.
    + */
    +goog.i18n.DateTimeSymbols_en_IM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_IO.
    + */
    +goog.i18n.DateTimeSymbols_en_IO = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_JE.
    + */
    +goog.i18n.DateTimeSymbols_en_JE = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_JM.
    + */
    +goog.i18n.DateTimeSymbols_en_JM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_KE.
    + */
    +goog.i18n.DateTimeSymbols_en_KE = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_KI.
    + */
    +goog.i18n.DateTimeSymbols_en_KI = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_KN.
    + */
    +goog.i18n.DateTimeSymbols_en_KN = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_KY.
    + */
    +goog.i18n.DateTimeSymbols_en_KY = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_LC.
    + */
    +goog.i18n.DateTimeSymbols_en_LC = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_LR.
    + */
    +goog.i18n.DateTimeSymbols_en_LR = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_LS.
    + */
    +goog.i18n.DateTimeSymbols_en_LS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MG.
    + */
    +goog.i18n.DateTimeSymbols_en_MG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MH.
    + */
    +goog.i18n.DateTimeSymbols_en_MH = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MO.
    + */
    +goog.i18n.DateTimeSymbols_en_MO = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MP.
    + */
    +goog.i18n.DateTimeSymbols_en_MP = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MS.
    + */
    +goog.i18n.DateTimeSymbols_en_MS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MT.
    + */
    +goog.i18n.DateTimeSymbols_en_MT = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'dd MMMM y', 'dd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MU.
    + */
    +goog.i18n.DateTimeSymbols_en_MU = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MW.
    + */
    +goog.i18n.DateTimeSymbols_en_MW = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_MY.
    + */
    +goog.i18n.DateTimeSymbols_en_MY = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NA.
    + */
    +goog.i18n.DateTimeSymbols_en_NA = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NF.
    + */
    +goog.i18n.DateTimeSymbols_en_NF = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NG.
    + */
    +goog.i18n.DateTimeSymbols_en_NG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NR.
    + */
    +goog.i18n.DateTimeSymbols_en_NR = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NU.
    + */
    +goog.i18n.DateTimeSymbols_en_NU = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_NZ.
    + */
    +goog.i18n.DateTimeSymbols_en_NZ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd/MM/y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PG.
    + */
    +goog.i18n.DateTimeSymbols_en_PG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PH.
    + */
    +goog.i18n.DateTimeSymbols_en_PH = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PK.
    + */
    +goog.i18n.DateTimeSymbols_en_PK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd-MMM-y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PN.
    + */
    +goog.i18n.DateTimeSymbols_en_PN = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PR.
    + */
    +goog.i18n.DateTimeSymbols_en_PR = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_PW.
    + */
    +goog.i18n.DateTimeSymbols_en_PW = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_RW.
    + */
    +goog.i18n.DateTimeSymbols_en_RW = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SB.
    + */
    +goog.i18n.DateTimeSymbols_en_SB = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SC.
    + */
    +goog.i18n.DateTimeSymbols_en_SC = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SD.
    + */
    +goog.i18n.DateTimeSymbols_en_SD = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SH.
    + */
    +goog.i18n.DateTimeSymbols_en_SH = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SL.
    + */
    +goog.i18n.DateTimeSymbols_en_SL = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SS.
    + */
    +goog.i18n.DateTimeSymbols_en_SS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SX.
    + */
    +goog.i18n.DateTimeSymbols_en_SX = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_SZ.
    + */
    +goog.i18n.DateTimeSymbols_en_SZ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TC.
    + */
    +goog.i18n.DateTimeSymbols_en_TC = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TK.
    + */
    +goog.i18n.DateTimeSymbols_en_TK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TO.
    + */
    +goog.i18n.DateTimeSymbols_en_TO = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TT.
    + */
    +goog.i18n.DateTimeSymbols_en_TT = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TV.
    + */
    +goog.i18n.DateTimeSymbols_en_TV = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_TZ.
    + */
    +goog.i18n.DateTimeSymbols_en_TZ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_UG.
    + */
    +goog.i18n.DateTimeSymbols_en_UG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_UM.
    + */
    +goog.i18n.DateTimeSymbols_en_UM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_VC.
    + */
    +goog.i18n.DateTimeSymbols_en_VC = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_VG.
    + */
    +goog.i18n.DateTimeSymbols_en_VG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_VI.
    + */
    +goog.i18n.DateTimeSymbols_en_VI = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_VU.
    + */
    +goog.i18n.DateTimeSymbols_en_VU = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_WS.
    + */
    +goog.i18n.DateTimeSymbols_en_WS = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_ZM.
    + */
    +goog.i18n.DateTimeSymbols_en_ZM = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale en_ZW.
    + */
    +goog.i18n.DateTimeSymbols_en_ZW = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Before Christ', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
    +    'August', 'September', 'October', 'November', 'December'],
    +  STANDALONEMONTHS: ['January', 'February', 'March', 'April', 'May', 'June',
    +    'July', 'August', 'September', 'October', 'November', 'December'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
    +    'Saturday'],
    +  STANDALONEWEEKDAYS: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
    +    'Friday', 'Saturday'],
    +  SHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1st quarter', '2nd quarter', '3rd quarter', '4th quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'dd MMM,y', 'd/M/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'at\' {0}', '{1} \'at\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale eo.
    + */
    +goog.i18n.DateTimeSymbols_eo = {
    +  ERAS: ['aK', 'pK'],
    +  ERANAMES: ['aK', 'pK'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januaro', 'februaro', 'marto', 'aprilo', 'majo', 'junio', 'julio',
    +    'aŭgusto', 'septembro', 'oktobro', 'novembro', 'decembro'],
    +  STANDALONEMONTHS: ['januaro', 'februaro', 'marto', 'aprilo', 'majo', 'junio',
    +    'julio', 'aŭgusto', 'septembro', 'oktobro', 'novembro', 'decembro'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aŭg', 'sep',
    +    'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aŭg', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['dimanĉo', 'lundo', 'mardo', 'merkredo', 'ĵaŭdo', 'vendredo',
    +    'sabato'],
    +  STANDALONEWEEKDAYS: ['dimanĉo', 'lundo', 'mardo', 'merkredo', 'ĵaŭdo',
    +    'vendredo', 'sabato'],
    +  SHORTWEEKDAYS: ['di', 'lu', 'ma', 'me', 'ĵa', 've', 'sa'],
    +  STANDALONESHORTWEEKDAYS: ['di', 'lu', 'ma', 'me', 'ĵa', 've', 'sa'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'Ĵ', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'Ĵ', 'V', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1-a kvaronjaro', '2-a kvaronjaro', '3-a kvaronjaro',
    +    '4-a kvaronjaro'],
    +  AMPMS: ['atm', 'ptm'],
    +  DATEFORMATS: ['EEEE, d-\'a\' \'de\' MMMM y', 'y-MMMM-dd', 'y-MMM-dd',
    +    'yy-MM-dd'],
    +  TIMEFORMATS: ['H-\'a\' \'horo\' \'kaj\' m:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale eo_001.
    + */
    +goog.i18n.DateTimeSymbols_eo_001 = goog.i18n.DateTimeSymbols_eo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_AR.
    + */
    +goog.i18n.DateTimeSymbols_es_AR = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['HH\'h\'\'\'mm:ss zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_BO.
    + */
    +goog.i18n.DateTimeSymbols_es_BO = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_CL.
    + */
    +goog.i18n.DateTimeSymbols_es_CL = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd-MM-y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_CO.
    + */
    +goog.i18n.DateTimeSymbols_es_CO = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd/MM/y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a (zzzz)', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_CR.
    + */
    +goog.i18n.DateTimeSymbols_es_CR = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_CU.
    + */
    +goog.i18n.DateTimeSymbols_es_CU = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_DO.
    + */
    +goog.i18n.DateTimeSymbols_es_DO = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_EA.
    + */
    +goog.i18n.DateTimeSymbols_es_EA = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'anno Dómini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'sept.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Sept.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_EC.
    + */
    +goog.i18n.DateTimeSymbols_es_EC = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_GQ.
    + */
    +goog.i18n.DateTimeSymbols_es_GQ = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'anno Dómini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'sept.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Sept.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_GT.
    + */
    +goog.i18n.DateTimeSymbols_es_GT = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd/MM/y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_HN.
    + */
    +goog.i18n.DateTimeSymbols_es_HN = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE dd \'de\' MMMM \'de\' y', 'dd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_IC.
    + */
    +goog.i18n.DateTimeSymbols_es_IC = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'anno Dómini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'sept.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Sept.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_MX.
    + */
    +goog.i18n.DateTimeSymbols_es_MX = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'Anno Domini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
    +    'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  SHORTMONTHS: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep',
    +    'oct', 'nov', 'dic'],
    +  STANDALONESHORTMONTHS: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul',
    +    'ago', 'sep', 'oct', 'nov', 'dic'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves',
    +    'viernes', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  SHORTQUARTERS: ['1er. trim.', '2º. trim.', '3er. trim.', '4º trim.'],
    +  QUARTERS: ['1er. trimestre', '2º. trimestre', '3er. trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_NI.
    + */
    +goog.i18n.DateTimeSymbols_es_NI = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_PA.
    + */
    +goog.i18n.DateTimeSymbols_es_PA = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'MM/dd/y', 'MM/dd/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_PE.
    + */
    +goog.i18n.DateTimeSymbols_es_PE = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/MM/yy'],
    +  TIMEFORMATS: ['HH\'H\'mm\'\'ss\'\' zzzz', 'h:mm:ss a z', 'h:mm:ss a',
    +    'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_PH.
    + */
    +goog.i18n.DateTimeSymbols_es_PH = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'anno Dómini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'sept.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Sept.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_PR.
    + */
    +goog.i18n.DateTimeSymbols_es_PR = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'MM/dd/y', 'MM/dd/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_PY.
    + */
    +goog.i18n.DateTimeSymbols_es_PY = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_SV.
    + */
    +goog.i18n.DateTimeSymbols_es_SV = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_US.
    + */
    +goog.i18n.DateTimeSymbols_es_US = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_UY.
    + */
    +goog.i18n.DateTimeSymbols_es_UY = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale es_VE.
    + */
    +goog.i18n.DateTimeSymbols_es_VE = {
    +  ERAS: ['a. C.', 'd. C.'],
    +  ERANAMES: ['antes de Cristo', 'después de Cristo'],
    +  NARROWMONTHS: ['e', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio',
    +    'agosto', 'setiembre', 'octubre', 'noviembre', 'diciembre'],
    +  STANDALONEMONTHS: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
    +    'Julio', 'Agosto', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre'],
    +  SHORTMONTHS: ['ene.', 'feb.', 'mar.', 'abr.', 'may.', 'jun.', 'jul.', 'ago.',
    +    'set.', 'oct.', 'nov.', 'dic.'],
    +  STANDALONESHORTMONTHS: ['Ene.', 'Feb.', 'Mar.', 'Abr.', 'May.', 'Jun.',
    +    'Jul.', 'Ago.', 'Set.', 'Oct.', 'Nov.', 'Dic.'],
    +  WEEKDAYS: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom.', 'lun.', 'mar.', 'mié.', 'jue.', 'vie.', 'sáb.'],
    +  STANDALONESHORTWEEKDAYS: ['Dom.', 'Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.',
    +    'Sáb.'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'j', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['1.er trim.', '2.º trim.', '3.er trim.', '4.º trim.'],
    +  QUARTERS: ['1.er trimestre', '2.º trimestre', '3.er trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['a. m.', 'p. m.'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale et_EE.
    + */
    +goog.i18n.DateTimeSymbols_et_EE = {
    +  ERAS: ['e.m.a.', 'm.a.j.'],
    +  ERANAMES: ['enne meie aega', 'meie aja järgi'],
    +  NARROWMONTHS: ['J', 'V', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'V', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli',
    +    'august', 'september', 'oktoober', 'november', 'detsember'],
    +  STANDALONEMONTHS: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni',
    +    'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'],
    +  SHORTMONTHS: ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli',
    +    'aug', 'sept', 'okt', 'nov', 'dets'],
    +  STANDALONESHORTMONTHS: ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni',
    +    'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'],
    +  WEEKDAYS: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev',
    +    'neljapäev', 'reede', 'laupäev'],
    +  STANDALONEWEEKDAYS: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev',
    +    'neljapäev', 'reede', 'laupäev'],
    +  SHORTWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  STANDALONESHORTWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  NARROWWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm.ss zzzz', 'H:mm.ss z', 'H:mm.ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale eu_ES.
    + */
    +goog.i18n.DateTimeSymbols_eu_ES = {
    +  ERAS: ['K.a.', 'K.o.'],
    +  ERANAMES: ['K.a.', 'K.o.'],
    +  NARROWMONTHS: ['U', 'O', 'M', 'A', 'M', 'E', 'U', 'A', 'I', 'U', 'A', 'A'],
    +  STANDALONENARROWMONTHS: ['U', 'O', 'M', 'A', 'M', 'E', 'U', 'A', 'I', 'U',
    +    'A', 'A'],
    +  MONTHS: ['urtarrilak', 'otsailak', 'martxoak', 'apirilak', 'maiatzak',
    +    'ekainak', 'uztailak', 'abuztuak', 'irailak', 'urriak', 'azaroak',
    +    'abenduak'],
    +  STANDALONEMONTHS: ['Urtarrila', 'Otsaila', 'Martxoa', 'Apirila', 'Maiatza',
    +    'Ekaina', 'Uztaila', 'Abuztua', 'Iraila', 'Urria', 'Azaroa', 'Abendua'],
    +  SHORTMONTHS: ['urt.', 'ots.', 'mar.', 'api.', 'mai.', 'eka.', 'uzt.', 'abu.',
    +    'ira.', 'urr.', 'aza.', 'abe.'],
    +  STANDALONESHORTMONTHS: ['Urt.', 'Ots.', 'Mar.', 'Api.', 'Mai.', 'Eka.',
    +    'Uzt.', 'Abu.', 'Ira.', 'Urr.', 'Aza.', 'Abe.'],
    +  WEEKDAYS: ['igandea', 'astelehena', 'asteartea', 'asteazkena', 'osteguna',
    +    'ostirala', 'larunbata'],
    +  STANDALONEWEEKDAYS: ['Igandea', 'Astelehena', 'Asteartea', 'Asteazkena',
    +    'Osteguna', 'Ostirala', 'Larunbata'],
    +  SHORTWEEKDAYS: ['ig.', 'al.', 'ar.', 'az.', 'og.', 'or.', 'lr.'],
    +  STANDALONESHORTWEEKDAYS: ['Ig.', 'Al.', 'Ar.', 'Az.', 'Og.', 'Or.', 'Lr.'],
    +  NARROWWEEKDAYS: ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
    +  SHORTQUARTERS: ['1Hh', '2Hh', '3Hh', '4Hh'],
    +  QUARTERS: ['1. hiruhilekoa', '2. hiruhilekoa', '3. hiruhilekoa',
    +    '4. hiruhilekoa'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y(\'e\')\'ko\' MMMM d, EEEE', 'y(\'e\')\'ko\' MMMM d',
    +    'y MMM d', 'y/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss (zzzz)', 'HH:mm:ss (z)', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ewo.
    + */
    +goog.i18n.DateTimeSymbols_ewo = {
    +  ERAS: ['oyk', 'ayk'],
    +  ERANAMES: ['osúsúa Yésus kiri', 'ámvus Yésus Kirís'],
    +  NARROWMONTHS: ['o', 'b', 'l', 'n', 't', 's', 'z', 'm', 'e', 'a', 'd', 'b'],
    +  STANDALONENARROWMONTHS: ['o', 'b', 'l', 'n', 't', 's', 'z', 'm', 'e', 'a',
    +    'd', 'b'],
    +  MONTHS: ['ngɔn osú', 'ngɔn bɛ̌', 'ngɔn lála', 'ngɔn nyina',
    +    'ngɔn tána', 'ngɔn saməna', 'ngɔn zamgbála', 'ngɔn mwom',
    +    'ngɔn ebulú', 'ngɔn awóm', 'ngɔn awóm ai dziá',
    +    'ngɔn awóm ai bɛ̌'],
    +  STANDALONEMONTHS: ['ngɔn osú', 'ngɔn bɛ̌', 'ngɔn lála', 'ngɔn nyina',
    +    'ngɔn tána', 'ngɔn saməna', 'ngɔn zamgbála', 'ngɔn mwom',
    +    'ngɔn ebulú', 'ngɔn awóm', 'ngɔn awóm ai dziá',
    +    'ngɔn awóm ai bɛ̌'],
    +  SHORTMONTHS: ['ngo', 'ngb', 'ngl', 'ngn', 'ngt', 'ngs', 'ngz', 'ngm', 'nge',
    +    'nga', 'ngad', 'ngab'],
    +  STANDALONESHORTMONTHS: ['ngo', 'ngb', 'ngl', 'ngn', 'ngt', 'ngs', 'ngz',
    +    'ngm', 'nge', 'nga', 'ngad', 'ngab'],
    +  WEEKDAYS: ['sɔ́ndɔ', 'mɔ́ndi', 'sɔ́ndɔ məlú mə́bɛ̌',
    +    'sɔ́ndɔ məlú mə́lɛ́', 'sɔ́ndɔ məlú mə́nyi', 'fúladé',
    +    'séradé'],
    +  STANDALONEWEEKDAYS: ['sɔ́ndɔ', 'mɔ́ndi', 'sɔ́ndɔ məlú mə́bɛ̌',
    +    'sɔ́ndɔ məlú mə́lɛ́', 'sɔ́ndɔ məlú mə́nyi', 'fúladé',
    +    'séradé'],
    +  SHORTWEEKDAYS: ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl', 'sér'],
    +  STANDALONESHORTWEEKDAYS: ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'fúl',
    +    'sér'],
    +  NARROWWEEKDAYS: ['s', 'm', 's', 's', 's', 'f', 's'],
    +  STANDALONENARROWWEEKDAYS: ['s', 'm', 's', 's', 's', 'f', 's'],
    +  SHORTQUARTERS: ['nno', 'nnb', 'nnl', 'nnny'],
    +  QUARTERS: ['nsámbá ngɔn asú', 'nsámbá ngɔn bɛ̌',
    +    'nsámbá ngɔn lála', 'nsámbá ngɔn nyina'],
    +  AMPMS: ['kíkíríg', 'ngəgógəle'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ewo_CM.
    + */
    +goog.i18n.DateTimeSymbols_ewo_CM = goog.i18n.DateTimeSymbols_ewo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale fa_AF.
    + */
    +goog.i18n.DateTimeSymbols_fa_AF = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['ق.م.', 'م.'],
    +  ERANAMES: ['قبل از میلاد', 'میلادی'],
    +  NARROWMONTHS: ['ژ', 'ف', 'م', 'آ', 'م', 'ژ', 'ژ', 'ا', 'س', 'ا',
    +    'ن', 'د'],
    +  STANDALONENARROWMONTHS: ['ج', 'ف', 'م', 'ا', 'م', 'ج', 'ج', 'ا', 'س',
    +    'ا', 'ن', 'د'],
    +  MONTHS: ['جنوری', 'فبروری', 'مارچ', 'اپریل', 'می',
    +    'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['ژانویه', 'فوریه', 'مارس', 'آوریل',
    +    'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسامبر'],
    +  SHORTMONTHS: ['جنو', 'فوریهٔ', 'مارس', 'آوریل', 'مـی',
    +    'ژوئن', 'جول', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسم'],
    +  STANDALONESHORTMONTHS: ['ژانویه', 'فوریه', 'مارس',
    +    'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر',
    +    'اکتبر', 'نوامبر', 'دسامبر'],
    +  WEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONEWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  SHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONESHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  NARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  STANDALONENARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  SHORTQUARTERS: ['س‌م۱', 'س‌م۲', 'س‌م۳', 'س‌م۴'],
    +  QUARTERS: ['سه‌ماههٔ اول', 'سه‌ماههٔ دوم',
    +    'سه‌ماههٔ سوم', 'سه‌ماههٔ چهارم'],
    +  AMPMS: ['قبل‌ازظهر', 'بعدازظهر'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y/M/d'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}، ساعت {0}', '{1}، ساعت {0}', '{1}،‏ {0}',
    +    '{1}،‏ {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [3, 4],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fa_IR.
    + */
    +goog.i18n.DateTimeSymbols_fa_IR = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['ق.م.', 'م.'],
    +  ERANAMES: ['قبل از میلاد', 'میلادی'],
    +  NARROWMONTHS: ['ژ', 'ف', 'م', 'آ', 'م', 'ژ', 'ژ', 'ا', 'س', 'ا',
    +    'ن', 'د'],
    +  STANDALONENARROWMONTHS: ['ژ', 'ف', 'م', 'آ', 'م', 'ژ', 'ژ', 'ا', 'س',
    +    'ا', 'ن', 'د'],
    +  MONTHS: ['ژانویهٔ', 'فوریهٔ', 'مارس', 'آوریل', 'مهٔ',
    +    'ژوئن', 'ژوئیهٔ', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسامبر'],
    +  STANDALONEMONTHS: ['ژانویه', 'فوریه', 'مارس', 'آوریل',
    +    'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر', 'اکتبر',
    +    'نوامبر', 'دسامبر'],
    +  SHORTMONTHS: ['ژانویهٔ', 'فوریهٔ', 'مارس', 'آوریل',
    +    'مهٔ', 'ژوئن', 'ژوئیهٔ', 'اوت', 'سپتامبر',
    +    'اکتبر', 'نوامبر', 'دسامبر'],
    +  STANDALONESHORTMONTHS: ['ژانویه', 'فوریه', 'مارس',
    +    'آوریل', 'مه', 'ژوئن', 'ژوئیه', 'اوت', 'سپتامبر',
    +    'اکتبر', 'نوامبر', 'دسامبر'],
    +  WEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONEWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  SHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONESHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  NARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  STANDALONENARROWWEEKDAYS: ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  SHORTQUARTERS: ['س‌م۱', 'س‌م۲', 'س‌م۳', 'س‌م۴'],
    +  QUARTERS: ['سه‌ماههٔ اول', 'سه‌ماههٔ دوم',
    +    'سه‌ماههٔ سوم', 'سه‌ماههٔ چهارم'],
    +  AMPMS: ['قبل‌ازظهر', 'بعدازظهر'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y/M/d'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}، ساعت {0}', '{1}، ساعت {0}', '{1}،‏ {0}',
    +    '{1}،‏ {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 4],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ff.
    + */
    +goog.i18n.DateTimeSymbols_ff = {
    +  ERAS: ['H-I', 'C-I'],
    +  ERANAMES: ['Hade Iisa', 'Caggal Iisa'],
    +  NARROWMONTHS: ['s', 'c', 'm', 's', 'd', 'k', 'm', 'j', 's', 'y', 'j', 'b'],
    +  STANDALONENARROWMONTHS: ['s', 'c', 'm', 's', 'd', 'k', 'm', 'j', 's', 'y',
    +    'j', 'b'],
    +  MONTHS: ['siilo', 'colte', 'mbooy', 'seeɗto', 'duujal', 'korse', 'morso',
    +    'juko', 'siilto', 'yarkomaa', 'jolal', 'bowte'],
    +  STANDALONEMONTHS: ['siilo', 'colte', 'mbooy', 'seeɗto', 'duujal', 'korse',
    +    'morso', 'juko', 'siilto', 'yarkomaa', 'jolal', 'bowte'],
    +  SHORTMONTHS: ['sii', 'col', 'mbo', 'see', 'duu', 'kor', 'mor', 'juk', 'slt',
    +    'yar', 'jol', 'bow'],
    +  STANDALONESHORTMONTHS: ['sii', 'col', 'mbo', 'see', 'duu', 'kor', 'mor',
    +    'juk', 'slt', 'yar', 'jol', 'bow'],
    +  WEEKDAYS: ['dewo', 'aaɓnde', 'mawbaare', 'njeslaare', 'naasaande', 'mawnde',
    +    'hoore-biir'],
    +  STANDALONEWEEKDAYS: ['dewo', 'aaɓnde', 'mawbaare', 'njeslaare', 'naasaande',
    +    'mawnde', 'hoore-biir'],
    +  SHORTWEEKDAYS: ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'],
    +  STANDALONESHORTWEEKDAYS: ['dew', 'aaɓ', 'maw', 'nje', 'naa', 'mwd', 'hbi'],
    +  NARROWWEEKDAYS: ['d', 'a', 'm', 'n', 'n', 'm', 'h'],
    +  STANDALONENARROWWEEKDAYS: ['d', 'a', 'm', 'n', 'n', 'm', 'h'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['Termes 1', 'Termes 2', 'Termes 3', 'Termes 4'],
    +  AMPMS: ['subaka', 'kikiiɗe'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ff_CM.
    + */
    +goog.i18n.DateTimeSymbols_ff_CM = goog.i18n.DateTimeSymbols_ff;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ff_GN.
    + */
    +goog.i18n.DateTimeSymbols_ff_GN = goog.i18n.DateTimeSymbols_ff;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ff_MR.
    + */
    +goog.i18n.DateTimeSymbols_ff_MR = goog.i18n.DateTimeSymbols_ff;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ff_SN.
    + */
    +goog.i18n.DateTimeSymbols_ff_SN = goog.i18n.DateTimeSymbols_ff;
    +
    +
    +/**
    + * Date/time formatting symbols for locale fi_FI.
    + */
    +goog.i18n.DateTimeSymbols_fi_FI = {
    +  ERAS: ['eKr.', 'jKr.'],
    +  ERANAMES: ['ennen Kristuksen syntymää', 'jälkeen Kristuksen syntymän'],
    +  NARROWMONTHS: ['T', 'H', 'M', 'H', 'T', 'K', 'H', 'E', 'S', 'L', 'M', 'J'],
    +  STANDALONENARROWMONTHS: ['T', 'H', 'M', 'H', 'T', 'K', 'H', 'E', 'S', 'L',
    +    'M', 'J'],
    +  MONTHS: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta',
    +    'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta',
    +    'lokakuuta', 'marraskuuta', 'joulukuuta'],
    +  STANDALONEMONTHS: ['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu',
    +    'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu',
    +    'marraskuu', 'joulukuu'],
    +  SHORTMONTHS: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta',
    +    'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta',
    +    'lokakuuta', 'marraskuuta', 'joulukuuta'],
    +  STANDALONESHORTMONTHS: ['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä',
    +    'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'],
    +  WEEKDAYS: ['sunnuntaina', 'maanantaina', 'tiistaina', 'keskiviikkona',
    +    'torstaina', 'perjantaina', 'lauantaina'],
    +  STANDALONEWEEKDAYS: ['sunnuntai', 'maanantai', 'tiistai', 'keskiviikko',
    +    'torstai', 'perjantai', 'lauantai'],
    +  SHORTWEEKDAYS: ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
    +  STANDALONESHORTWEEKDAYS: ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
    +  SHORTQUARTERS: ['1. nelj.', '2. nelj.', '3. nelj.', '4. nelj.'],
    +  QUARTERS: ['1. neljännes', '2. neljännes', '3. neljännes',
    +    '4. neljännes'],
    +  AMPMS: ['ap.', 'ip.'],
    +  DATEFORMATS: ['cccc d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.y'],
    +  TIMEFORMATS: ['H.mm.ss zzzz', 'H.mm.ss z', 'H.mm.ss', 'H.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fil_PH.
    + */
    +goog.i18n.DateTimeSymbols_fil_PH = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['E', 'P', 'M', 'A', 'M', 'H', 'H', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo', 'Hulyo',
    +    'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  STANDALONEMONTHS: ['Enero', 'Pebrero', 'Marso', 'Abril', 'Mayo', 'Hunyo',
    +    'Hulyo', 'Agosto', 'Setyembre', 'Oktubre', 'Nobyembre', 'Disyembre'],
    +  SHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul', 'Ago', 'Set',
    +    'Okt', 'Nob', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Ene', 'Peb', 'Mar', 'Abr', 'May', 'Hun', 'Hul',
    +    'Ago', 'Set', 'Okt', 'Nob', 'Dis'],
    +  WEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes', 'Biyernes',
    +    'Sabado'],
    +  STANDALONEWEEKDAYS: ['Linggo', 'Lunes', 'Martes', 'Miyerkules', 'Huwebes',
    +    'Biyernes', 'Sabado'],
    +  SHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
    +  NARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ika-1 quarter', 'ika-2 quarter', 'ika-3 quarter',
    +    'ika-4 na quarter'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'nang\' {0}', '{1} \'nang\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fo.
    + */
    +goog.i18n.DateTimeSymbols_fo = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['fyrir Krist', 'eftir Krist'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'apríl', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'apríl', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep',
    +    'okt', 'nov', 'des'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['sunnudagur', 'mánadagur', 'týsdagur', 'mikudagur', 'hósdagur',
    +    'fríggjadagur', 'leygardagur'],
    +  STANDALONEWEEKDAYS: ['sunnudagur', 'mánadagur', 'týsdagur', 'mikudagur',
    +    'hósdagur', 'fríggjadagur', 'leygardagur'],
    +  SHORTWEEKDAYS: ['sun', 'mán', 'týs', 'mik', 'hós', 'frí', 'ley'],
    +  STANDALONESHORTWEEKDAYS: ['sun', 'mán', 'týs', 'mik', 'hós', 'frí',
    +    'ley'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'M', 'H', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'M', 'H', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['um fyrrapartur', 'um seinnapartur'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'd. MMM y', 'dd-MM-y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fo_FO.
    + */
    +goog.i18n.DateTimeSymbols_fo_FO = goog.i18n.DateTimeSymbols_fo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_BE.
    + */
    +goog.i18n.DateTimeSymbols_fr_BE = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/yy'],
    +  TIMEFORMATS: ['H \'h\' mm \'min\' ss \'s\' zzzz', 'HH:mm:ss z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_BF.
    + */
    +goog.i18n.DateTimeSymbols_fr_BF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_BI.
    + */
    +goog.i18n.DateTimeSymbols_fr_BI = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_BJ.
    + */
    +goog.i18n.DateTimeSymbols_fr_BJ = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_BL.
    + */
    +goog.i18n.DateTimeSymbols_fr_BL = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CD.
    + */
    +goog.i18n.DateTimeSymbols_fr_CD = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CF.
    + */
    +goog.i18n.DateTimeSymbols_fr_CF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CG.
    + */
    +goog.i18n.DateTimeSymbols_fr_CG = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CH.
    + */
    +goog.i18n.DateTimeSymbols_fr_CH = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH.mm:ss \'h\' zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CI.
    + */
    +goog.i18n.DateTimeSymbols_fr_CI = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_CM.
    + */
    +goog.i18n.DateTimeSymbols_fr_CM = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_DJ.
    + */
    +goog.i18n.DateTimeSymbols_fr_DJ = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_DZ.
    + */
    +goog.i18n.DateTimeSymbols_fr_DZ = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_FR.
    + */
    +goog.i18n.DateTimeSymbols_fr_FR = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_GA.
    + */
    +goog.i18n.DateTimeSymbols_fr_GA = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_GF.
    + */
    +goog.i18n.DateTimeSymbols_fr_GF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_GN.
    + */
    +goog.i18n.DateTimeSymbols_fr_GN = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_GP.
    + */
    +goog.i18n.DateTimeSymbols_fr_GP = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_GQ.
    + */
    +goog.i18n.DateTimeSymbols_fr_GQ = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_HT.
    + */
    +goog.i18n.DateTimeSymbols_fr_HT = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_KM.
    + */
    +goog.i18n.DateTimeSymbols_fr_KM = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_LU.
    + */
    +goog.i18n.DateTimeSymbols_fr_LU = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MA.
    + */
    +goog.i18n.DateTimeSymbols_fr_MA = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MC.
    + */
    +goog.i18n.DateTimeSymbols_fr_MC = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MF.
    + */
    +goog.i18n.DateTimeSymbols_fr_MF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MG.
    + */
    +goog.i18n.DateTimeSymbols_fr_MG = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_ML.
    + */
    +goog.i18n.DateTimeSymbols_fr_ML = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MQ.
    + */
    +goog.i18n.DateTimeSymbols_fr_MQ = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MR.
    + */
    +goog.i18n.DateTimeSymbols_fr_MR = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_MU.
    + */
    +goog.i18n.DateTimeSymbols_fr_MU = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_NC.
    + */
    +goog.i18n.DateTimeSymbols_fr_NC = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_NE.
    + */
    +goog.i18n.DateTimeSymbols_fr_NE = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_PF.
    + */
    +goog.i18n.DateTimeSymbols_fr_PF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_PM.
    + */
    +goog.i18n.DateTimeSymbols_fr_PM = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_RE.
    + */
    +goog.i18n.DateTimeSymbols_fr_RE = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_RW.
    + */
    +goog.i18n.DateTimeSymbols_fr_RW = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_SC.
    + */
    +goog.i18n.DateTimeSymbols_fr_SC = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_SN.
    + */
    +goog.i18n.DateTimeSymbols_fr_SN = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_SY.
    + */
    +goog.i18n.DateTimeSymbols_fr_SY = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_TD.
    + */
    +goog.i18n.DateTimeSymbols_fr_TD = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_TG.
    + */
    +goog.i18n.DateTimeSymbols_fr_TG = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_TN.
    + */
    +goog.i18n.DateTimeSymbols_fr_TN = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_VU.
    + */
    +goog.i18n.DateTimeSymbols_fr_VU = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_WF.
    + */
    +goog.i18n.DateTimeSymbols_fr_WF = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fr_YT.
    + */
    +goog.i18n.DateTimeSymbols_fr_YT = {
    +  ERAS: ['av. J.-C.', 'ap. J.-C.'],
    +  ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
    +    'août', 'septembre', 'octobre', 'novembre', 'décembre'],
    +  STANDALONEMONTHS: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    +    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    +  SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',
    +    'août', 'sept.', 'oct.', 'nov.', 'déc.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Févr.', 'Mars', 'Avr.', 'Mai', 'Juin',
    +    'Juil.', 'Août', 'Sept.', 'Oct.', 'Nov.', 'Déc.'],
    +  WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
    +    'samedi'],
    +  STANDALONEWEEKDAYS: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi',
    +    'Vendredi', 'Samedi'],
    +  SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Dim.', 'Lun.', 'Mar.', 'Mer.', 'Jeu.', 'Ven.',
    +    'Sam.'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fur.
    + */
    +goog.i18n.DateTimeSymbols_fur = {
    +  ERAS: ['pdC', 'ddC'],
    +  ERANAMES: ['pdC', 'ddC'],
    +  NARROWMONTHS: ['Z', 'F', 'M', 'A', 'M', 'J', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Z', 'F', 'M', 'A', 'M', 'J', 'L', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Zenâr', 'Fevrâr', 'Març', 'Avrîl', 'Mai', 'Jugn', 'Lui',
    +    'Avost', 'Setembar', 'Otubar', 'Novembar', 'Dicembar'],
    +  STANDALONEMONTHS: ['Zenâr', 'Fevrâr', 'Març', 'Avrîl', 'Mai', 'Jugn',
    +    'Lui', 'Avost', 'Setembar', 'Otubar', 'Novembar', 'Dicembar'],
    +  SHORTMONTHS: ['Zen', 'Fev', 'Mar', 'Avr', 'Mai', 'Jug', 'Lui', 'Avo', 'Set',
    +    'Otu', 'Nov', 'Dic'],
    +  STANDALONESHORTMONTHS: ['Zen', 'Fev', 'Mar', 'Avr', 'Mai', 'Jug', 'Lui',
    +    'Avo', 'Set', 'Otu', 'Nov', 'Dic'],
    +  WEEKDAYS: ['domenie', 'lunis', 'martars', 'miercus', 'joibe', 'vinars',
    +    'sabide'],
    +  STANDALONEWEEKDAYS: ['domenie', 'lunis', 'martars', 'miercus', 'joibe',
    +    'vinars', 'sabide'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mie', 'joi', 'vin', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['Prin trimestri', 'Secont trimestri', 'Tierç trimestri',
    +    'Cuart trimestri'],
    +  AMPMS: ['a.', 'p.'],
    +  DATEFORMATS: ['EEEE d \'di\' MMMM \'dal\' y', 'd \'di\' MMMM \'dal\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fur_IT.
    + */
    +goog.i18n.DateTimeSymbols_fur_IT = goog.i18n.DateTimeSymbols_fur;
    +
    +
    +/**
    + * Date/time formatting symbols for locale fy.
    + */
    +goog.i18n.DateTimeSymbols_fy = {
    +  ERAS: ['f.Kr.', 'n.Kr.'],
    +  ERANAMES: ['Foar Kristus', 'nei Kristus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie', 'juny',
    +    'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'],
    +  STANDALONEMONTHS: ['jannewaris', 'febrewaris', 'maart', 'april', 'maaie',
    +    'juny', 'july', 'augustus', 'septimber', 'oktober', 'novimber', 'desimber'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mai', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mrt', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['snein', 'moandei', 'tiisdei', 'woansdei', 'tongersdei', 'freed',
    +    'sneon'],
    +  STANDALONEWEEKDAYS: ['snein', 'moandei', 'tiisdei', 'woansdei', 'tongersdei',
    +    'freed', 'sneon'],
    +  SHORTWEEKDAYS: ['si', 'mo', 'ti', 'wo', 'to', 'fr', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['si', 'mo', 'ti', 'wo', 'to', 'fr', 'so'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale fy_NL.
    + */
    +goog.i18n.DateTimeSymbols_fy_NL = goog.i18n.DateTimeSymbols_fy;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ga_IE.
    + */
    +goog.i18n.DateTimeSymbols_ga_IE = {
    +  ERAS: ['RC', 'AD'],
    +  ERANAMES: ['Roimh Chríost', 'Anno Domini'],
    +  NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
    +  STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D',
    +    'S', 'N'],
    +  MONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Meitheamh',
    +    'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair', 'Samhain',
    +    'Nollaig'],
    +  STANDALONEMONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine',
    +    'Meitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair',
    +    'Samhain', 'Nollaig'],
    +  SHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith', 'Iúil',
    +    'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
    +  STANDALONESHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith',
    +    'Iúil', 'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
    +  WEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin',
    +    'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
    +  STANDALONEWEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt',
    +    'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
    +  SHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine', 'Sath'],
    +  STANDALONESHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine',
    +    'Sath'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['1ú ráithe', '2ú ráithe', '3ú ráithe', '4ú ráithe'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 2
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gd.
    + */
    +goog.i18n.DateTimeSymbols_gd = {
    +  ERAS: ['RC', 'AD'],
    +  ERANAMES: ['Ro Chrìosta', 'An dèidh Chrìosta'],
    +  NARROWMONTHS: ['F', 'G', 'M', 'G', 'C', 'Ò', 'I', 'L', 'S', 'D', 'S', 'D'],
    +  STANDALONENARROWMONTHS: ['F', 'G', 'M', 'G', 'C', 'Ò', 'I', 'L', 'S', 'D',
    +    'S', 'D'],
    +  MONTHS: ['dhen Fhaoilleach', 'dhen Ghearran', 'dhen Mhàrt', 'dhen Ghiblean',
    +    'dhen Chèitean', 'dhen Ògmhios', 'dhen Iuchar', 'dhen Lùnastal',
    +    'dhen t-Sultain', 'dhen Dàmhair', 'dhen t-Samhain', 'dhen Dùbhlachd'],
    +  STANDALONEMONTHS: ['Am Faoilleach', 'An Gearran', 'Am Màrt', 'An Giblean',
    +    'An Cèitean', 'An t-Ògmhios', 'An t-Iuchar', 'An Lùnastal',
    +    'An t-Sultain', 'An Dàmhair', 'An t-Samhain', 'An Dùbhlachd'],
    +  SHORTMONTHS: ['Faoi', 'Gearr', 'Màrt', 'Gibl', 'Cèit', 'Ògmh', 'Iuch',
    +    'Lùna', 'Sult', 'Dàmh', 'Samh', 'Dùbh'],
    +  STANDALONESHORTMONTHS: ['Faoi', 'Gearr', 'Màrt', 'Gibl', 'Cèit', 'Ògmh',
    +    'Iuch', 'Lùna', 'Sult', 'Dàmh', 'Samh', 'Dùbh'],
    +  WEEKDAYS: ['DiDòmhnaich', 'DiLuain', 'DiMàirt', 'DiCiadain', 'DiarDaoin',
    +    'DihAoine', 'DiSathairne'],
    +  STANDALONEWEEKDAYS: ['DiDòmhnaich', 'DiLuain', 'DiMàirt', 'DiCiadain',
    +    'DiarDaoin', 'DihAoine', 'DiSathairne'],
    +  SHORTWEEKDAYS: ['DiD', 'DiL', 'DiM', 'DiC', 'Dia', 'Dih', 'DiS'],
    +  STANDALONESHORTWEEKDAYS: ['DiD', 'DiL', 'DiM', 'DiC', 'Dia', 'Dih', 'DiS'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'A', 'H', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'A', 'H', 'S'],
    +  SHORTQUARTERS: ['C1', 'C2', 'C3', 'C4'],
    +  QUARTERS: ['1d chairteal', '2na cairteal', '3s cairteal', '4mh cairteal'],
    +  AMPMS: ['m', 'f'],
    +  DATEFORMATS: ['EEEE, d\'mh\' MMMM y', 'd\'mh\' MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gd_GB.
    + */
    +goog.i18n.DateTimeSymbols_gd_GB = goog.i18n.DateTimeSymbols_gd;
    +
    +
    +/**
    + * Date/time formatting symbols for locale gl_ES.
    + */
    +goog.i18n.DateTimeSymbols_gl_ES = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'despois de Cristo'],
    +  NARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['X', 'F', 'M', 'A', 'M', 'X', 'X', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['xaneiro', 'febreiro', 'marzo', 'abril', 'maio', 'xuño', 'xullo',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'decembro'],
    +  STANDALONEMONTHS: ['Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño',
    +    'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro'],
    +  SHORTMONTHS: ['xan', 'feb', 'mar', 'abr', 'mai', 'xuñ', 'xul', 'ago', 'set',
    +    'out', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xuñ', 'Xul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dec'],
    +  WEEKDAYS: ['domingo', 'luns', 'martes', 'mércores', 'xoves', 'venres',
    +    'sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Luns', 'Martes', 'Mércores', 'Xoves',
    +    'Venres', 'Sábado'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mér', 'xov', 'ven', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['Dom', 'Lun', 'Mar', 'Mér', 'Xov', 'Ven', 'Sáb'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1o trimestre', '2o trimestre', '3o trimestre', '4o trimestre'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'd MMM, y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gsw_CH.
    + */
    +goog.i18n.DateTimeSymbols_gsw_CH = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Okt', 'Nov', 'Dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch', 'Dunschtig',
    +    'Friitig', 'Samschtig'],
    +  STANDALONEWEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch',
    +    'Dunschtig', 'Friitig', 'Samschtig'],
    +  SHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nam.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gsw_FR.
    + */
    +goog.i18n.DateTimeSymbols_gsw_FR = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Okt', 'Nov', 'Dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch', 'Dunschtig',
    +    'Friitig', 'Samschtig'],
    +  STANDALONEWEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch',
    +    'Dunschtig', 'Friitig', 'Samschtig'],
    +  SHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nam.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gsw_LI.
    + */
    +goog.i18n.DateTimeSymbols_gsw_LI = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
    +    'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
    +    'Juli', 'Auguscht', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Okt', 'Nov', 'Dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch', 'Dunschtig',
    +    'Friitig', 'Samschtig'],
    +  STANDALONEWEEKDAYS: ['Sunntig', 'Määntig', 'Ziischtig', 'Mittwuch',
    +    'Dunschtig', 'Friitig', 'Samschtig'],
    +  SHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['vorm.', 'nam.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'dd.MM.y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gu_IN.
    + */
    +goog.i18n.DateTimeSymbols_gu_IN = {
    +  ERAS: ['ઈસુના જન્મ પહેલા', 'ઇસવીસન'],
    +  ERANAMES: ['ઈસવીસન પૂર્વે', 'ઇસવીસન'],
    +  NARROWMONTHS: ['જા', 'ફે', 'મા', 'એ', 'મે', 'જૂ',
    +    'જુ', 'ઑ', 'સ', 'ઑ', 'ન', 'ડિ'],
    +  STANDALONENARROWMONTHS: ['જા', 'ફે', 'મા', 'એ', 'મે',
    +    'જૂ', 'જુ', 'ઑ', 'સ', 'ઑ', 'ન', 'ડિ'],
    +  MONTHS: ['જાન્યુઆરી', 'ફેબ્રુઆરી',
    +    'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન',
    +    'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટેમ્બર',
    +    'ઑક્ટોબર', 'નવેમ્બર',
    +    'ડિસેમ્બર'],
    +  STANDALONEMONTHS: ['જાન્યુઆરી',
    +    'ફેબ્રુઆરી', 'માર્ચ', 'એપ્રિલ',
    +    'મે', 'જૂન', 'જુલાઈ', 'ઑગસ્ટ',
    +    'સપ્ટેમ્બર', 'ઑક્ટોબર',
    +    'નવેમ્બર', 'ડિસેમ્બર'],
    +  SHORTMONTHS: ['જાન્યુ', 'ફેબ્રુ', 'માર્ચ',
    +    'એપ્રિલ', 'મે', 'જૂન', 'જુલાઈ', 'ઑગ',
    +    'સપ્ટે', 'ઑક્ટો', 'નવે', 'ડિસે'],
    +  STANDALONESHORTMONTHS: ['જાન્યુ', 'ફેબ્રુ',
    +    'માર્ચ', 'એપ્રિલ', 'મે', 'જૂન',
    +    'જુલાઈ', 'ઑગસ્ટ', 'સપ્ટે', 'ઑક્ટો',
    +    'નવે', 'ડિસે'],
    +  WEEKDAYS: ['રવિવાર', 'સોમવાર',
    +    'મંગળવાર', 'બુધવાર', 'ગુરુવાર',
    +    'શુક્રવાર', 'શનિવાર'],
    +  STANDALONEWEEKDAYS: ['રવિવાર', 'સોમવાર',
    +    'મંગળવાર', 'બુધવાર', 'ગુરુવાર',
    +    'શુક્રવાર', 'શનિવાર'],
    +  SHORTWEEKDAYS: ['રવિ', 'સોમ', 'મંગળ', 'બુધ',
    +    'ગુરુ', 'શુક્ર', 'શનિ'],
    +  STANDALONESHORTWEEKDAYS: ['રવિ', 'સોમ', 'મંગળ',
    +    'બુધ', 'ગુરુ', 'શુક્ર', 'શનિ'],
    +  NARROWWEEKDAYS: ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ',
    +    'શ'],
    +  STANDALONENARROWWEEKDAYS: ['ર', 'સો', 'મં', 'બુ', 'ગુ',
    +    'શુ', 'શ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['પહેલો ત્રિમાસ',
    +    'બીજો ત્રિમાસ',
    +    'ત્રીજો ત્રિમાસ',
    +    'ચોથો ત્રિમાસ'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['hh:mm:ss a zzzz', 'hh:mm:ss a z', 'hh:mm:ss a', 'hh:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale guz.
    + */
    +goog.i18n.DateTimeSymbols_guz = {
    +  ERAS: ['YA', 'YK'],
    +  ERANAMES: ['Yeso ataiborwa', 'Yeso kaiboirwe'],
    +  NARROWMONTHS: ['C', 'F', 'M', 'A', 'M', 'J', 'C', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['C', 'F', 'M', 'A', 'M', 'J', 'C', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Chanuari', 'Feburari', 'Machi', 'Apiriri', 'Mei', 'Juni', 'Chulai',
    +    'Agosti', 'Septemba', 'Okitoba', 'Nobemba', 'Disemba'],
    +  STANDALONEMONTHS: ['Chanuari', 'Feburari', 'Machi', 'Apiriri', 'Mei', 'Juni',
    +    'Chulai', 'Agosti', 'Septemba', 'Okitoba', 'Nobemba', 'Disemba'],
    +  SHORTMONTHS: ['Can', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Cul', 'Agt', 'Sep',
    +    'Okt', 'Nob', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Can', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Cul',
    +    'Agt', 'Sep', 'Okt', 'Nob', 'Dis'],
    +  WEEKDAYS: ['Chumapiri', 'Chumatato', 'Chumaine', 'Chumatano', 'Aramisi',
    +    'Ichuma', 'Esabato'],
    +  STANDALONEWEEKDAYS: ['Chumapiri', 'Chumatato', 'Chumaine', 'Chumatano',
    +    'Aramisi', 'Ichuma', 'Esabato'],
    +  SHORTWEEKDAYS: ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'],
    +  STANDALONESHORTWEEKDAYS: ['Cpr', 'Ctt', 'Cmn', 'Cmt', 'Ars', 'Icm', 'Est'],
    +  NARROWWEEKDAYS: ['C', 'C', 'C', 'C', 'A', 'I', 'E'],
    +  STANDALONENARROWWEEKDAYS: ['C', 'C', 'C', 'C', 'A', 'I', 'E'],
    +  SHORTQUARTERS: ['E1', 'E2', 'E3', 'E4'],
    +  QUARTERS: ['Erobo entang’ani', 'Erobo yakabere', 'Erobo yagatato',
    +    'Erobo yakane'],
    +  AMPMS: ['Ma/Mo', 'Mambia/Mog'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale guz_KE.
    + */
    +goog.i18n.DateTimeSymbols_guz_KE = goog.i18n.DateTimeSymbols_guz;
    +
    +
    +/**
    + * Date/time formatting symbols for locale gv.
    + */
    +goog.i18n.DateTimeSymbols_gv = {
    +  ERAS: ['RC', 'AD'],
    +  ERANAMES: ['RC', 'AD'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Jerrey-geuree', 'Toshiaght-arree', 'Mayrnt', 'Averil', 'Boaldyn',
    +    'Mean-souree', 'Jerrey-souree', 'Luanistyn', 'Mean-fouyir', 'Jerrey-fouyir',
    +    'Mee Houney', 'Mee ny Nollick'],
    +  STANDALONEMONTHS: ['Jerrey-geuree', 'Toshiaght-arree', 'Mayrnt', 'Averil',
    +    'Boaldyn', 'Mean-souree', 'Jerrey-souree', 'Luanistyn', 'Mean-fouyir',
    +    'Jerrey-fouyir', 'Mee Houney', 'Mee ny Nollick'],
    +  SHORTMONTHS: ['J-guer', 'T-arree', 'Mayrnt', 'Avrril', 'Boaldyn', 'M-souree',
    +    'J-souree', 'Luanistyn', 'M-fouyir', 'J-fouyir', 'M.Houney', 'M.Nollick'],
    +  STANDALONESHORTMONTHS: ['J-guer', 'T-arree', 'Mayrnt', 'Avrril', 'Boaldyn',
    +    'M-souree', 'J-souree', 'Luanistyn', 'M-fouyir', 'J-fouyir', 'M.Houney',
    +    'M.Nollick'],
    +  WEEKDAYS: ['Jedoonee', 'Jelhein', 'Jemayrt', 'Jercean', 'Jerdein', 'Jeheiney',
    +    'Jesarn'],
    +  STANDALONEWEEKDAYS: ['Jedoonee', 'Jelhein', 'Jemayrt', 'Jercean', 'Jerdein',
    +    'Jeheiney', 'Jesarn'],
    +  SHORTWEEKDAYS: ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'],
    +  STANDALONESHORTWEEKDAYS: ['Jed', 'Jel', 'Jem', 'Jerc', 'Jerd', 'Jeh', 'Jes'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'MMM dd, y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale gv_IM.
    + */
    +goog.i18n.DateTimeSymbols_gv_IM = goog.i18n.DateTimeSymbols_gv;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ha.
    + */
    +goog.i18n.DateTimeSymbols_ha = {
    +  ERAS: ['KHAI', 'BHAI'],
    +  ERANAMES: ['Kafin haihuwar annab', 'Bayan haihuwar annab'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'Y', 'Y', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'Y', 'Y', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janairu', 'Faburairu', 'Maris', 'Afirilu', 'Mayu', 'Yuni', 'Yuli',
    +    'Agusta', 'Satumba', 'Oktoba', 'Nuwamba', 'Disamba'],
    +  STANDALONEMONTHS: ['Janairu', 'Faburairu', 'Maris', 'Afirilu', 'Mayu', 'Yuni',
    +    'Yuli', 'Agusta', 'Satumba', 'Oktoba', 'Nuwamba', 'Disamba'],
    +  SHORTMONTHS: ['Jan', 'Fab', 'Mar', 'Afi', 'May', 'Yun', 'Yul', 'Agu', 'Sat',
    +    'Okt', 'Nuw', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fab', 'Mar', 'Afi', 'May', 'Yun', 'Yul',
    +    'Agu', 'Sat', 'Okt', 'Nuw', 'Dis'],
    +  WEEKDAYS: ['Lahadi', 'Litinin', 'Talata', 'Laraba', 'Alhamis', 'Jummaʼa',
    +    'Asabar'],
    +  STANDALONEWEEKDAYS: ['Lahadi', 'Litinin', 'Talata', 'Laraba', 'Alhamis',
    +    'Jummaʼa', 'Asabar'],
    +  SHORTWEEKDAYS: ['Lh', 'Li', 'Ta', 'Lr', 'Al', 'Ju', 'As'],
    +  STANDALONESHORTWEEKDAYS: ['Lh', 'Li', 'Ta', 'Lr', 'Al', 'Ju', 'As'],
    +  NARROWWEEKDAYS: ['L', 'L', 'T', 'L', 'A', 'J', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['L', 'L', 'T', 'L', 'A', 'J', 'A'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kwata na ɗaya', 'Kwata na biyu', 'Kwata na uku',
    +    'Kwata na huɗu'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ha_Latn.
    + */
    +goog.i18n.DateTimeSymbols_ha_Latn = goog.i18n.DateTimeSymbols_ha;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ha_Latn_GH.
    + */
    +goog.i18n.DateTimeSymbols_ha_Latn_GH = goog.i18n.DateTimeSymbols_ha;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ha_Latn_NE.
    + */
    +goog.i18n.DateTimeSymbols_ha_Latn_NE = goog.i18n.DateTimeSymbols_ha;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ha_Latn_NG.
    + */
    +goog.i18n.DateTimeSymbols_ha_Latn_NG = goog.i18n.DateTimeSymbols_ha;
    +
    +
    +/**
    + * Date/time formatting symbols for locale haw_US.
    + */
    +goog.i18n.DateTimeSymbols_haw_US = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei', 'Iune',
    +    'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa', 'Kekemapa'],
    +  STANDALONEMONTHS: ['Ianuali', 'Pepeluali', 'Malaki', 'ʻApelila', 'Mei',
    +    'Iune', 'Iulai', 'ʻAukake', 'Kepakemapa', 'ʻOkakopa', 'Nowemapa',
    +    'Kekemapa'],
    +  SHORTMONTHS: ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.', 'Iul.', 'ʻAu.',
    +    'Kep.', 'ʻOk.', 'Now.', 'Kek.'],
    +  STANDALONESHORTMONTHS: ['Ian.', 'Pep.', 'Mal.', 'ʻAp.', 'Mei', 'Iun.',
    +    'Iul.', 'ʻAu.', 'Kep.', 'ʻOk.', 'Now.', 'Kek.'],
    +  WEEKDAYS: ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu', 'Poʻahā',
    +    'Poʻalima', 'Poʻaono'],
    +  STANDALONEWEEKDAYS: ['Lāpule', 'Poʻakahi', 'Poʻalua', 'Poʻakolu',
    +    'Poʻahā', 'Poʻalima', 'Poʻaono'],
    +  SHORTWEEKDAYS: ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
    +  STANDALONESHORTWEEKDAYS: ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale he_IL.
    + */
    +goog.i18n.DateTimeSymbols_he_IL = {
    +  ERAS: ['לפנה״ס', 'לספירה'],
    +  ERANAMES: ['לפני הספירה', 'לספירה'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי',
    +    'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר',
    +    'נובמבר', 'דצמבר'],
    +  STANDALONEMONTHS: ['ינואר', 'פברואר', 'מרץ', 'אפריל',
    +    'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר',
    +    'אוקטובר', 'נובמבר', 'דצמבר'],
    +  SHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳', 'מאי',
    +    'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳', 'נוב׳',
    +    'דצמ׳'],
    +  STANDALONESHORTMONTHS: ['ינו׳', 'פבר׳', 'מרץ', 'אפר׳',
    +    'מאי', 'יוני', 'יולי', 'אוג׳', 'ספט׳', 'אוק׳',
    +    'נוב׳', 'דצמ׳'],
    +  WEEKDAYS: ['יום ראשון', 'יום שני', 'יום שלישי',
    +    'יום רביעי', 'יום חמישי', 'יום שישי',
    +    'יום שבת'],
    +  STANDALONEWEEKDAYS: ['יום ראשון', 'יום שני',
    +    'יום שלישי', 'יום רביעי', 'יום חמישי',
    +    'יום שישי', 'יום שבת'],
    +  SHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳', 'יום ד׳',
    +    'יום ה׳', 'יום ו׳', 'שבת'],
    +  STANDALONESHORTWEEKDAYS: ['יום א׳', 'יום ב׳', 'יום ג׳',
    +    'יום ד׳', 'יום ה׳', 'יום ו׳', 'שבת'],
    +  NARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
    +  STANDALONENARROWWEEKDAYS: ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳',
    +    'ש׳'],
    +  SHORTQUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3',
    +    'רבעון 4'],
    +  QUARTERS: ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
    +  AMPMS: ['לפנה״צ', 'אחה״צ'],
    +  DATEFORMATS: ['EEEE, d בMMMM y', 'd בMMMM y', 'd בMMM y', 'd.M.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} בשעה {0}', '{1} בשעה {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hi_IN.
    + */
    +goog.i18n.DateTimeSymbols_hi_IN = {
    +  ERAS: ['ईसा-पूर्व', 'ईस्वी'],
    +  ERANAMES: ['ईसा-पूर्व', 'ईसवी सन'],
    +  NARROWMONTHS: ['ज', 'फ़', 'मा', 'अ', 'म', 'जू', 'जु',
    +    'अ', 'सि', 'अ', 'न', 'दि'],
    +  STANDALONENARROWMONTHS: ['ज', 'फ़', 'मा', 'अ', 'म', 'जू',
    +    'जु', 'अ', 'सि', 'अ', 'न', 'दि'],
    +  MONTHS: ['जनवरी', 'फ़रवरी', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुलाई',
    +    'अगस्त', 'सितंबर', 'अक्तूबर',
    +    'नवंबर', 'दिसंबर'],
    +  STANDALONEMONTHS: ['जनवरी', 'फ़रवरी', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुलाई',
    +    'अगस्त', 'सितंबर', 'अक्तूबर',
    +    'नवंबर', 'दिसंबर'],
    +  SHORTMONTHS: ['जन॰', 'फ़र॰', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुल॰', 'अग॰',
    +    'सित॰', 'अक्तू॰', 'नव॰', 'दिस॰'],
    +  STANDALONESHORTMONTHS: ['जन॰', 'फ़र॰', 'मार्च',
    +    'अप्रैल', 'मई', 'जून', 'जुल॰', 'अग॰',
    +    'सित॰', 'अक्तू॰', 'नव॰', 'दिस॰'],
    +  WEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगलवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगलवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  SHORTWEEKDAYS: ['रवि', 'सोम', 'मंगल', 'बुध',
    +    'गुरु', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['रवि', 'सोम', 'मंगल',
    +    'बुध', 'गुरु', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु', 'शु',
    +    'श'],
    +  STANDALONENARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['ति1', 'ति2', 'ति3', 'ति4'],
    +  QUARTERS: ['पहली तिमाही',
    +    'दूसरी तिमाही', 'तीसरी तिमाही',
    +    'चौथी तिमाही'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'dd/MM/y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} को {0}', '{1} को {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hr_BA.
    + */
    +goog.i18n.DateTimeSymbols_hr_BA = {
    +  ERAS: ['pr. Kr.', 'p. Kr.'],
    +  ERANAMES: ['Prije Krista', 'Poslije Krista'],
    +  NARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.',
    +    '11.', '12.'],
    +  STANDALONENARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.',
    +    '10.', '11.', '12.'],
    +  MONTHS: ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja',
    +    'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'],
    +  STANDALONEMONTHS: ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj',
    +    'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'],
    +  SHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj',
    +    'lis', 'stu', 'pro'],
    +  STANDALONESHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp',
    +    'kol', 'ruj', 'lis', 'stu', 'pro'],
    +  WEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak',
    +    'petak', 'subota'],
    +  STANDALONEWEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda',
    +    'četvrtak', 'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['1kv', '2kv', '3kv', '4kv'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y.', 'd. MMMM y.', 'd. MMM y.', 'dd.MM.y.'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'u\' {0}', '{1} \'u\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hr_HR.
    + */
    +goog.i18n.DateTimeSymbols_hr_HR = {
    +  ERAS: ['pr. Kr.', 'p. Kr.'],
    +  ERANAMES: ['Prije Krista', 'Poslije Krista'],
    +  NARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.',
    +    '11.', '12.'],
    +  STANDALONENARROWMONTHS: ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.',
    +    '10.', '11.', '12.'],
    +  MONTHS: ['siječnja', 'veljače', 'ožujka', 'travnja', 'svibnja', 'lipnja',
    +    'srpnja', 'kolovoza', 'rujna', 'listopada', 'studenoga', 'prosinca'],
    +  STANDALONEMONTHS: ['siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj',
    +    'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'],
    +  SHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj',
    +    'lis', 'stu', 'pro'],
    +  STANDALONESHORTMONTHS: ['sij', 'velj', 'ožu', 'tra', 'svi', 'lip', 'srp',
    +    'kol', 'ruj', 'lis', 'stu', 'pro'],
    +  WEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda', 'četvrtak',
    +    'petak', 'subota'],
    +  STANDALONEWEEKDAYS: ['nedjelja', 'ponedjeljak', 'utorak', 'srijeda',
    +    'četvrtak', 'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Č', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['1kv', '2kv', '3kv', '4kv'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y.', 'd. MMMM y.', 'd. MMM y.', 'dd.MM.y.'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'u\' {0}', '{1} \'u\' {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hsb.
    + */
    +goog.i18n.DateTimeSymbols_hsb = {
    +  ERAS: ['př.Chr.n.', 'po Chr.n.'],
    +  ERANAMES: ['před Chrystowym narodźenjom', 'po Chrystowym narodźenju'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januara', 'februara', 'měrca', 'apryla', 'meje', 'junija',
    +    'julija', 'awgusta', 'septembra', 'oktobra', 'nowembra', 'decembra'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'měrc', 'apryl', 'meja', 'junij',
    +    'julij', 'awgust', 'september', 'oktober', 'nowember', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'měr.', 'apr.', 'mej.', 'jun.', 'jul.', 'awg.',
    +    'sep.', 'okt.', 'now.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'měr', 'apr', 'mej', 'jun', 'jul',
    +    'awg', 'sep', 'okt', 'now', 'dec'],
    +  WEEKDAYS: ['njedźela', 'póndźela', 'wutora', 'srjeda', 'štwórtk',
    +    'pjatk', 'sobota'],
    +  STANDALONEWEEKDAYS: ['njedźela', 'póndźela', 'wutora', 'srjeda',
    +    'štwórtk', 'pjatk', 'sobota'],
    +  SHORTWEEKDAYS: ['nje', 'pón', 'wut', 'srj', 'štw', 'pja', 'sob'],
    +  STANDALONESHORTWEEKDAYS: ['nje', 'pón', 'wut', 'srj', 'štw', 'pja', 'sob'],
    +  NARROWWEEKDAYS: ['n', 'p', 'w', 's', 'š', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'w', 's', 'š', 'p', 's'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. kwartal', '2. kwartal', '3. kwartal', '4. kwartal'],
    +  AMPMS: ['dopołdnja', 'popołdnju'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm \'hodź\'.'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hsb_DE.
    + */
    +goog.i18n.DateTimeSymbols_hsb_DE = goog.i18n.DateTimeSymbols_hsb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale hu_HU.
    + */
    +goog.i18n.DateTimeSymbols_hu_HU = {
    +  ERAS: ['i. e.', 'i. sz.'],
    +  ERANAMES: ['időszámításunk előtt', 'időszámításunk szerint'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'Á', 'M', 'J', 'J', 'A', 'Sz', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'Á', 'M', 'J', 'J', 'A', 'Sz', 'O',
    +    'N', 'D'],
    +  MONTHS: ['január', 'február', 'március', 'április', 'május', 'június',
    +    'július', 'augusztus', 'szeptember', 'október', 'november', 'december'],
    +  STANDALONEMONTHS: ['január', 'február', 'március', 'április', 'május',
    +    'június', 'július', 'augusztus', 'szeptember', 'október', 'november',
    +    'december'],
    +  SHORTMONTHS: ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.', 'júl.',
    +    'aug.', 'szept.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.',
    +    'júl.', 'aug.', 'szept.', 'okt.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök',
    +    'péntek', 'szombat'],
    +  STANDALONEWEEKDAYS: ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök',
    +    'péntek', 'szombat'],
    +  SHORTWEEKDAYS: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
    +  STANDALONESHORTWEEKDAYS: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
    +  NARROWWEEKDAYS: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
    +  STANDALONENARROWWEEKDAYS: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
    +  SHORTQUARTERS: ['N1', 'N2', 'N3', 'N4'],
    +  QUARTERS: ['I. negyedév', 'II. negyedév', 'III. negyedév',
    +    'IV. negyedév'],
    +  AMPMS: ['de.', 'du.'],
    +  DATEFORMATS: ['y. MMMM d., EEEE', 'y. MMMM d.', 'y. MMM d.', 'y. MM. dd.'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale hy_AM.
    + */
    +goog.i18n.DateTimeSymbols_hy_AM = {
    +  ERAS: ['մ.թ.ա.', 'մ.թ.'],
    +  ERANAMES: ['մ.թ.ա.', 'մ.թ.'],
    +  NARROWMONTHS: ['Հ', 'Փ', 'Մ', 'Ա', 'Մ', 'Հ', 'Հ', 'Օ', 'Ս', 'Հ',
    +    'Ն', 'Դ'],
    +  STANDALONENARROWMONTHS: ['Հ', 'Փ', 'Մ', 'Ա', 'Մ', 'Հ', 'Հ', 'Օ', 'Ս',
    +    'Հ', 'Ն', 'Դ'],
    +  MONTHS: ['հունվարի', 'փետրվարի', 'մարտի', 'ապրիլի',
    +    'մայիսի', 'հունիսի', 'հուլիսի', 'օգոստոսի',
    +    'սեպտեմբերի', 'հոկտեմբերի', 'նոյեմբերի',
    +    'դեկտեմբերի'],
    +  STANDALONEMONTHS: ['հունվար', 'փետրվար', 'մարտ',
    +    'ապրիլ', 'մայիս', 'հունիս', 'հուլիս',
    +    'օգոստոս', 'սեպտեմբեր', 'հոկտեմբեր',
    +    'նոյեմբեր', 'դեկտեմբեր'],
    +  SHORTMONTHS: ['հնվ', 'փտվ', 'մրտ', 'ապր', 'մյս', 'հնս',
    +    'հլս', 'օգս', 'սեպ', 'հոկ', 'նոյ', 'դեկ'],
    +  STANDALONESHORTMONTHS: ['հնվ', 'փտվ', 'մրտ', 'ապր', 'մյս',
    +    'հնս', 'հլս', 'օգս', 'սեպ', 'հոկ', 'նոյ', 'դեկ'],
    +  WEEKDAYS: ['կիրակի', 'երկուշաբթի', 'երեքշաբթի',
    +    'չորեքշաբթի', 'հինգշաբթի', 'ուրբաթ', 'շաբաթ'],
    +  STANDALONEWEEKDAYS: ['կիրակի', 'երկուշաբթի',
    +    'երեքշաբթի', 'չորեքշաբթի', 'հինգշաբթի',
    +    'ուրբաթ', 'շաբաթ'],
    +  SHORTWEEKDAYS: ['կիր', 'երկ', 'երք', 'չրք', 'հնգ', 'ուր',
    +    'շբթ'],
    +  STANDALONESHORTWEEKDAYS: ['կիր', 'երկ', 'երք', 'չրք', 'հնգ',
    +    'ուր', 'շբթ'],
    +  NARROWWEEKDAYS: ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
    +  STANDALONENARROWWEEKDAYS: ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
    +  SHORTQUARTERS: ['1-ին եռմս.', '2-րդ եռմս.', '3-րդ եռմս.',
    +    '4-րդ եռմս.'],
    +  QUARTERS: ['1-ին եռամսյակ', '2-րդ եռամսյակ',
    +    '3-րդ եռամսյակ', '4-րդ եռամսյակ'],
    +  AMPMS: ['կեսօրից առաջ', 'կեսօրից հետո'],
    +  DATEFORMATS: ['yթ. MMMM d, EEEE', 'dd MMMM, yթ.', 'dd MMM, yթ.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss, zzzz', 'H:mm:ss, z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ia.
    + */
    +goog.i18n.DateTimeSymbols_ia = {
    +  ERAS: ['a.Chr.', 'p.Chr.'],
    +  ERANAMES: ['ante Christo', 'post Christo'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['januario', 'februario', 'martio', 'april', 'maio', 'junio', 'julio',
    +    'augusto', 'septembre', 'octobre', 'novembre', 'decembre'],
    +  STANDALONEMONTHS: ['januario', 'februario', 'martio', 'april', 'maio',
    +    'junio', 'julio', 'augusto', 'septembre', 'octobre', 'novembre',
    +    'decembre'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep',
    +    'oct', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'oct', 'nov', 'dec'],
    +  WEEKDAYS: ['dominica', 'lunedi', 'martedi', 'mercuridi', 'jovedi', 'venerdi',
    +    'sabbato'],
    +  STANDALONEWEEKDAYS: ['dominica', 'lunedi', 'martedi', 'mercuridi', 'jovedi',
    +    'venerdi', 'sabbato'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'jov', 'ven', 'sab'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1-me trimestre', '2-nde trimestre', '3-tie trimestre',
    +    '4-te trimestre'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ia_FR.
    + */
    +goog.i18n.DateTimeSymbols_ia_FR = goog.i18n.DateTimeSymbols_ia;
    +
    +
    +/**
    + * Date/time formatting symbols for locale id_ID.
    + */
    +goog.i18n.DateTimeSymbols_id_ID = {
    +  ERAS: ['SM', 'M'],
    +  ERANAMES: ['Sebelum Masehi', 'M'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli',
    +    'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
    +    'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agt', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Agt', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kuartal ke-1', 'Kuartal ke-2', 'Kuartal ke-3', 'Kuartal ke-4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ig.
    + */
    +goog.i18n.DateTimeSymbols_ig = {
    +  ERAS: ['T.K.', 'A.K.'],
    +  ERANAMES: ['Tupu Kristi', 'Afọ Kristi'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Jenụwarị', 'Febrụwarị', 'Maachị', 'Eprel', 'Mee', 'Juun',
    +    'Julaị', 'Ọgọọst', 'Septemba', 'Ọktoba', 'Novemba', 'Disemba'],
    +  STANDALONEMONTHS: ['Jenụwarị', 'Febrụwarị', 'Maachị', 'Eprel',
    +    'Mee', 'Juun', 'Julaị', 'Ọgọọst', 'Septemba', 'Ọktoba', 'Novemba',
    +    'Disemba'],
    +  SHORTMONTHS: ['Jen', 'Feb', 'Maa', 'Epr', 'Mee', 'Juu', 'Jul', 'Ọgọ',
    +    'Sep', 'Ọkt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jen', 'Feb', 'Maa', 'Epr', 'Mee', 'Juu', 'Jul',
    +    'Ọgọ', 'Sep', 'Ọkt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Mbọsị Ụka', 'Mọnde', 'Tiuzdee', 'Wenezdee', 'Tọọzdee',
    +    'Fraịdee', 'Satọdee'],
    +  STANDALONEWEEKDAYS: ['Mbọsị Ụka', 'Mọnde', 'Tiuzdee', 'Wenezdee',
    +    'Tọọzdee', 'Fraịdee', 'Satọdee'],
    +  SHORTWEEKDAYS: ['Ụka', 'Mọn', 'Tiu', 'Wen', 'Tọọ', 'Fraị', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Ụka', 'Mọn', 'Tiu', 'Wen', 'Tọọ', 'Fraị',
    +    'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Ọ1', 'Ọ2', 'Ọ3', 'Ọ4'],
    +  QUARTERS: ['Ọkara 1', 'Ọkara 2', 'Ọkara 3', 'Ọkara 4'],
    +  AMPMS: ['A.M.', 'P.M.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ig_NG.
    + */
    +goog.i18n.DateTimeSymbols_ig_NG = goog.i18n.DateTimeSymbols_ig;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ii.
    + */
    +goog.i18n.DateTimeSymbols_ii = {
    +  ERAS: ['ꃅꋊꂿ', 'ꃅꋊꊂ'],
    +  ERANAMES: ['ꃅꋊꂿ', 'ꃅꋊꊂ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ꋍꆪ', 'ꑍꆪ', 'ꌕꆪ', 'ꇖꆪ', 'ꉬꆪ', 'ꃘꆪ', 'ꏃꆪ',
    +    'ꉆꆪ', 'ꈬꆪ', 'ꊰꆪ', 'ꊰꊪꆪ', 'ꊰꑋꆪ'],
    +  STANDALONEMONTHS: ['ꋍꆪ', 'ꑍꆪ', 'ꌕꆪ', 'ꇖꆪ', 'ꉬꆪ', 'ꃘꆪ',
    +    'ꏃꆪ', 'ꉆꆪ', 'ꈬꆪ', 'ꊰꆪ', 'ꊰꊪꆪ', 'ꊰꑋꆪ'],
    +  SHORTMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONESHORTMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  WEEKDAYS: ['ꑭꆏꑍ', 'ꆏꊂꋍ', 'ꆏꊂꑍ', 'ꆏꊂꌕ', 'ꆏꊂꇖ',
    +    'ꆏꊂꉬ', 'ꆏꊂꃘ'],
    +  STANDALONEWEEKDAYS: ['ꑭꆏꑍ', 'ꆏꊂꋍ', 'ꆏꊂꑍ', 'ꆏꊂꌕ',
    +    'ꆏꊂꇖ', 'ꆏꊂꉬ', 'ꆏꊂꃘ'],
    +  SHORTWEEKDAYS: ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ', 'ꆏꉬ',
    +    'ꆏꃘ'],
    +  STANDALONESHORTWEEKDAYS: ['ꑭꆏ', 'ꆏꋍ', 'ꆏꑍ', 'ꆏꌕ', 'ꆏꇖ',
    +    'ꆏꉬ', 'ꆏꃘ'],
    +  NARROWWEEKDAYS: ['ꆏ', 'ꋍ', 'ꑍ', 'ꌕ', 'ꇖ', 'ꉬ', 'ꃘ'],
    +  STANDALONENARROWWEEKDAYS: ['ꆏ', 'ꋍ', 'ꑍ', 'ꌕ', 'ꇖ', 'ꉬ', 'ꃘ'],
    +  SHORTQUARTERS: ['ꃅꑌ', 'ꃅꎸ', 'ꃅꍵ', 'ꃅꋆ'],
    +  QUARTERS: ['ꃅꑌ', 'ꃅꎸ', 'ꃅꍵ', 'ꃅꋆ'],
    +  AMPMS: ['ꎸꄑ', 'ꁯꋒ'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ii_CN.
    + */
    +goog.i18n.DateTimeSymbols_ii_CN = goog.i18n.DateTimeSymbols_ii;
    +
    +
    +/**
    + * Date/time formatting symbols for locale is_IS.
    + */
    +goog.i18n.DateTimeSymbols_is_IS = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['fyrir Krist', 'eftir Krist'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'Á', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'Á', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní', 'júlí',
    +    'ágúst', 'september', 'október', 'nóvember', 'desember'],
    +  STANDALONEMONTHS: ['janúar', 'febrúar', 'mars', 'apríl', 'maí', 'júní',
    +    'júlí', 'ágúst', 'september', 'október', 'nóvember', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maí', 'jún.', 'júl.',
    +    'ágú.', 'sep.', 'okt.', 'nóv.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maí', 'jún.',
    +    'júl.', 'ágú.', 'sep.', 'okt.', 'nóv.', 'des.'],
    +  WEEKDAYS: ['sunnudagur', 'mánudagur', 'þriðjudagur', 'miðvikudagur',
    +    'fimmtudagur', 'föstudagur', 'laugardagur'],
    +  STANDALONEWEEKDAYS: ['sunnudagur', 'mánudagur', 'þriðjudagur',
    +    'miðvikudagur', 'fimmtudagur', 'föstudagur', 'laugardagur'],
    +  SHORTWEEKDAYS: ['sun.', 'mán.', 'þri.', 'mið.', 'fim.', 'fös.', 'lau.'],
    +  STANDALONESHORTWEEKDAYS: ['sun.', 'mán.', 'þri.', 'mið.', 'fim.', 'fös.',
    +    'lau.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
    +  SHORTQUARTERS: ['F1', 'F2', 'F3', 'F4'],
    +  QUARTERS: ['1. fjórðungur', '2. fjórðungur', '3. fjórðungur',
    +    '4. fjórðungur'],
    +  AMPMS: ['f.h.', 'e.h.'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'd.M.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'kl.\' {0}', '{1} \'kl.\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale it_CH.
    + */
    +goog.i18n.DateTimeSymbols_it_CH = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['a.C.', 'd.C.'],
    +  NARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
    +    'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
    +  STANDALONEMONTHS: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio',
    +    'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre',
    +    'Dicembre'],
    +  SHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set',
    +    'ott', 'nov', 'dic'],
    +  STANDALONESHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug',
    +    'ago', 'set', 'ott', 'nov', 'dic'],
    +  WEEKDAYS: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì',
    +    'venerdì', 'sabato'],
    +  STANDALONEWEEKDAYS: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì',
    +    'Giovedì', 'Venerdì', 'Sabato'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestre', '2º trimestre', '3º trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd-MMM-y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH.mm:ss \'h\' zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale it_IT.
    + */
    +goog.i18n.DateTimeSymbols_it_IT = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['a.C.', 'd.C.'],
    +  NARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
    +    'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
    +  STANDALONEMONTHS: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio',
    +    'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre',
    +    'Dicembre'],
    +  SHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set',
    +    'ott', 'nov', 'dic'],
    +  STANDALONESHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug',
    +    'ago', 'set', 'ott', 'nov', 'dic'],
    +  WEEKDAYS: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì',
    +    'venerdì', 'sabato'],
    +  STANDALONEWEEKDAYS: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì',
    +    'Giovedì', 'Venerdì', 'Sabato'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestre', '2º trimestre', '3º trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale it_SM.
    + */
    +goog.i18n.DateTimeSymbols_it_SM = {
    +  ERAS: ['aC', 'dC'],
    +  ERANAMES: ['a.C.', 'd.C.'],
    +  NARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['G', 'F', 'M', 'A', 'M', 'G', 'L', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno',
    +    'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
    +  STANDALONEMONTHS: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio',
    +    'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre',
    +    'Dicembre'],
    +  SHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set',
    +    'ott', 'nov', 'dic'],
    +  STANDALONESHORTMONTHS: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug',
    +    'ago', 'set', 'ott', 'nov', 'dic'],
    +  WEEKDAYS: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì',
    +    'venerdì', 'sabato'],
    +  STANDALONEWEEKDAYS: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì',
    +    'Giovedì', 'Venerdì', 'Sabato'],
    +  SHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestre', '2º trimestre', '3º trimestre',
    +    '4º trimestre'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd MMM y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ja_JP.
    + */
    +goog.i18n.DateTimeSymbols_ja_JP = {
    +  ERAS: ['紀元前', '西暦'],
    +  ERANAMES: ['紀元前', '西暦'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日',
    +    '金曜日', '土曜日'],
    +  STANDALONEWEEKDAYS: ['日曜日', '月曜日', '火曜日', '水曜日',
    +    '木曜日', '金曜日', '土曜日'],
    +  SHORTWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  STANDALONESHORTWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  NARROWWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  STANDALONENARROWWEEKDAYS: ['日', '月', '火', '水', '木', '金', '土'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['第1四半期', '第2四半期', '第3四半期',
    +    '第4四半期'],
    +  AMPMS: ['午前', '午後'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y/MM/dd', 'y/MM/dd'],
    +  TIMEFORMATS: ['H時mm分ss秒 zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale jgo.
    + */
    +goog.i18n.DateTimeSymbols_jgo = {
    +  ERAS: ['tsɛttsɛt mɛŋguꞌ mi ɛ́ lɛɛnɛ Kɛlísɛtɔ gɔ ńɔ́',
    +    'tsɛttsɛt mɛŋguꞌ mi ɛ́ fúnɛ Kɛlísɛtɔ tɔ́ mɔ́'],
    +  ERANAMES: ['tsɛttsɛt mɛŋguꞌ mi ɛ́ lɛɛnɛ Kɛlísɛtɔ gɔ ńɔ́',
    +    'tsɛttsɛt mɛŋguꞌ mi ɛ́ fúnɛ Kɛlísɛtɔ tɔ́ mɔ́'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Nduŋmbi Saŋ', 'Pɛsaŋ Pɛ́pá', 'Pɛsaŋ Pɛ́tát',
    +    'Pɛsaŋ Pɛ́nɛ́kwa', 'Pɛsaŋ Pataa', 'Pɛsaŋ Pɛ́nɛ́ntúkú',
    +    'Pɛsaŋ Saambá', 'Pɛsaŋ Pɛ́nɛ́fɔm', 'Pɛsaŋ Pɛ́nɛ́pfúꞋú',
    +    'Pɛsaŋ Nɛgɛ́m', 'Pɛsaŋ Ntsɔ̌pmɔ́', 'Pɛsaŋ Ntsɔ̌ppá'],
    +  STANDALONEMONTHS: ['Nduŋmbi Saŋ', 'Pɛsaŋ Pɛ́pá', 'Pɛsaŋ Pɛ́tát',
    +    'Pɛsaŋ Pɛ́nɛ́kwa', 'Pɛsaŋ Pataa', 'Pɛsaŋ Pɛ́nɛ́ntúkú',
    +    'Pɛsaŋ Saambá', 'Pɛsaŋ Pɛ́nɛ́fɔm', 'Pɛsaŋ Pɛ́nɛ́pfúꞋú',
    +    'Pɛsaŋ Nɛgɛ́m', 'Pɛsaŋ Ntsɔ̌pmɔ́', 'Pɛsaŋ Ntsɔ̌ppá'],
    +  SHORTMONTHS: ['Nduŋmbi Saŋ', 'Pɛsaŋ Pɛ́pá', 'Pɛsaŋ Pɛ́tát',
    +    'Pɛsaŋ Pɛ́nɛ́kwa', 'Pɛsaŋ Pataa', 'Pɛsaŋ Pɛ́nɛ́ntúkú',
    +    'Pɛsaŋ Saambá', 'Pɛsaŋ Pɛ́nɛ́fɔm', 'Pɛsaŋ Pɛ́nɛ́pfúꞋú',
    +    'Pɛsaŋ Nɛgɛ́m', 'Pɛsaŋ Ntsɔ̌pmɔ́', 'Pɛsaŋ Ntsɔ̌ppá'],
    +  STANDALONESHORTMONTHS: ['Nduŋmbi Saŋ', 'Pɛsaŋ Pɛ́pá',
    +    'Pɛsaŋ Pɛ́tát', 'Pɛsaŋ Pɛ́nɛ́kwa', 'Pɛsaŋ Pataa',
    +    'Pɛsaŋ Pɛ́nɛ́ntúkú', 'Pɛsaŋ Saambá', 'Pɛsaŋ Pɛ́nɛ́fɔm',
    +    'Pɛsaŋ Pɛ́nɛ́pfúꞋú', 'Pɛsaŋ Nɛgɛ́m', 'Pɛsaŋ Ntsɔ̌pmɔ́',
    +    'Pɛsaŋ Ntsɔ̌ppá'],
    +  WEEKDAYS: ['Sɔ́ndi', 'Mɔ́ndi', 'Ápta Mɔ́ndi', 'Wɛ́nɛsɛdɛ',
    +    'Tɔ́sɛdɛ', 'Fɛlâyɛdɛ', 'Sásidɛ'],
    +  STANDALONEWEEKDAYS: ['Sɔ́ndi', 'Mɔ́ndi', 'Ápta Mɔ́ndi',
    +    'Wɛ́nɛsɛdɛ', 'Tɔ́sɛdɛ', 'Fɛlâyɛdɛ', 'Sásidɛ'],
    +  SHORTWEEKDAYS: ['Sɔ́ndi', 'Mɔ́ndi', 'Ápta Mɔ́ndi', 'Wɛ́nɛsɛdɛ',
    +    'Tɔ́sɛdɛ', 'Fɛlâyɛdɛ', 'Sásidɛ'],
    +  STANDALONESHORTWEEKDAYS: ['Sɔ́ndi', 'Mɔ́ndi', 'Ápta Mɔ́ndi',
    +    'Wɛ́nɛsɛdɛ', 'Tɔ́sɛdɛ', 'Fɛlâyɛdɛ', 'Sásidɛ'],
    +  NARROWWEEKDAYS: ['Sɔ́', 'Mɔ́', 'ÁM', 'Wɛ́', 'Tɔ́', 'Fɛ', 'Sá'],
    +  STANDALONENARROWWEEKDAYS: ['Sɔ́', 'Mɔ́', 'ÁM', 'Wɛ́', 'Tɔ́', 'Fɛ',
    +    'Sá'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['mbaꞌmbaꞌ', 'ŋka mbɔ́t nji'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale jgo_CM.
    + */
    +goog.i18n.DateTimeSymbols_jgo_CM = goog.i18n.DateTimeSymbols_jgo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale jmc.
    + */
    +goog.i18n.DateTimeSymbols_jmc = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Kristu', 'Baada ya Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai',
    +    'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi',
    +    'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['utuko', 'kyiukonyi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale jmc_TZ.
    + */
    +goog.i18n.DateTimeSymbols_jmc_TZ = goog.i18n.DateTimeSymbols_jmc;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ka_GE.
    + */
    +goog.i18n.DateTimeSymbols_ka_GE = {
    +  ERAS: ['ძვ. წ.', 'ახ. წ.'],
    +  ERANAMES: ['ძველი წელთაღრიცხვით',
    +    'ახალი წელთაღრიცხვით'],
    +  NARROWMONTHS: ['ი', 'თ', 'მ', 'ა', 'მ', 'ი', 'ი', 'ა', 'ს',
    +    'ო', 'ნ', 'დ'],
    +  STANDALONENARROWMONTHS: ['ი', 'თ', 'მ', 'ა', 'მ', 'ი', 'ი',
    +    'ა', 'ს', 'ო', 'ნ', 'დ'],
    +  MONTHS: ['იანვარი', 'თებერვალი',
    +    'მარტი', 'აპრილი', 'მაისი',
    +    'ივნისი', 'ივლისი', 'აგვისტო',
    +    'სექტემბერი', 'ოქტომბერი',
    +    'ნოემბერი', 'დეკემბერი'],
    +  STANDALONEMONTHS: ['იანვარი', 'თებერვალი',
    +    'მარტი', 'აპრილი', 'მაისი',
    +    'ივნისი', 'ივლისი', 'აგვისტო',
    +    'სექტემბერი', 'ოქტომბერი',
    +    'ნოემბერი', 'დეკემბერი'],
    +  SHORTMONTHS: ['იან', 'თებ', 'მარ', 'აპრ', 'მაი',
    +    'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ',
    +    'ნოე', 'დეკ'],
    +  STANDALONESHORTMONTHS: ['იან', 'თებ', 'მარ', 'აპრ',
    +    'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ',
    +    'ოქტ', 'ნოე', 'დეკ'],
    +  WEEKDAYS: ['კვირა', 'ორშაბათი',
    +    'სამშაბათი', 'ოთხშაბათი',
    +    'ხუთშაბათი', 'პარასკევი',
    +    'შაბათი'],
    +  STANDALONEWEEKDAYS: ['კვირა', 'ორშაბათი',
    +    'სამშაბათი', 'ოთხშაბათი',
    +    'ხუთშაბათი', 'პარასკევი',
    +    'შაბათი'],
    +  SHORTWEEKDAYS: ['კვი', 'ორშ', 'სამ', 'ოთხ',
    +    'ხუთ', 'პარ', 'შაბ'],
    +  STANDALONESHORTWEEKDAYS: ['კვი', 'ორშ', 'სამ', 'ოთხ',
    +    'ხუთ', 'პარ', 'შაბ'],
    +  NARROWWEEKDAYS: ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
    +  STANDALONENARROWWEEKDAYS: ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
    +  SHORTQUARTERS: ['I კვ.', 'II კვ.', 'III კვ.', 'IV კვ.'],
    +  QUARTERS: ['I კვარტალი', 'II კვარტალი',
    +    'III კვარტალი', 'IV კვარტალი'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM, y', 'd MMMM, y', 'd MMM, y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1}, {0}', '{1} {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kab.
    + */
    +goog.i18n.DateTimeSymbols_kab = {
    +  ERAS: ['snd. T.Ɛ', 'sld. T.Ɛ'],
    +  ERANAMES: ['send talalit n Ɛisa', 'seld talalit n Ɛisa'],
    +  NARROWMONTHS: ['Y', 'F', 'M', 'Y', 'M', 'Y', 'Y', 'Ɣ', 'C', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Y', 'F', 'M', 'Y', 'M', 'Y', 'Y', 'Ɣ', 'C', 'T',
    +    'N', 'D'],
    +  MONTHS: ['Yennayer', 'Fuṛar', 'Meɣres', 'Yebrir', 'Mayyu', 'Yunyu',
    +    'Yulyu', 'Ɣuct', 'Ctembeṛ', 'Tubeṛ', 'Nunembeṛ', 'Duǧembeṛ'],
    +  STANDALONEMONTHS: ['Yennayer', 'Fuṛar', 'Meɣres', 'Yebrir', 'Mayyu',
    +    'Yunyu', 'Yulyu', 'Ɣuct', 'Ctembeṛ', 'Tubeṛ', 'Nunembeṛ',
    +    'Duǧembeṛ'],
    +  SHORTMONTHS: ['Yen', 'Fur', 'Meɣ', 'Yeb', 'May', 'Yun', 'Yul', 'Ɣuc', 'Cte',
    +    'Tub', 'Nun', 'Duǧ'],
    +  STANDALONESHORTMONTHS: ['Yen', 'Fur', 'Meɣ', 'Yeb', 'May', 'Yun', 'Yul',
    +    'Ɣuc', 'Cte', 'Tub', 'Nun', 'Duǧ'],
    +  WEEKDAYS: ['Yanass', 'Sanass', 'Kraḍass', 'Kuẓass', 'Samass', 'Sḍisass',
    +    'Sayass'],
    +  STANDALONEWEEKDAYS: ['Yanass', 'Sanass', 'Kraḍass', 'Kuẓass', 'Samass',
    +    'Sḍisass', 'Sayass'],
    +  SHORTWEEKDAYS: ['Yan', 'San', 'Kraḍ', 'Kuẓ', 'Sam', 'Sḍis', 'Say'],
    +  STANDALONESHORTWEEKDAYS: ['Yan', 'San', 'Kraḍ', 'Kuẓ', 'Sam', 'Sḍis',
    +    'Say'],
    +  NARROWWEEKDAYS: ['Y', 'S', 'K', 'K', 'S', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['Y', 'S', 'K', 'K', 'S', 'S', 'S'],
    +  SHORTQUARTERS: ['Kḍg1', 'Kḍg2', 'Kḍg3', 'Kḍg4'],
    +  QUARTERS: ['akraḍaggur amenzu', 'akraḍaggur wis-sin',
    +    'akraḍaggur wis-kraḍ', 'akraḍaggur wis-kuẓ'],
    +  AMPMS: ['n tufat', 'n tmeddit'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kab_DZ.
    + */
    +goog.i18n.DateTimeSymbols_kab_DZ = goog.i18n.DateTimeSymbols_kab;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kam.
    + */
    +goog.i18n.DateTimeSymbols_kam = {
    +  ERAS: ['MY', 'IY'],
    +  ERANAMES: ['Mbee wa Yesũ', 'Ĩtina wa Yesũ'],
    +  NARROWMONTHS: ['M', 'K', 'K', 'K', 'K', 'T', 'M', 'N', 'K', 'Ĩ', 'Ĩ', 'Ĩ'],
    +  STANDALONENARROWMONTHS: ['M', 'K', 'K', 'K', 'K', 'T', 'M', 'N', 'K', 'Ĩ',
    +    'Ĩ', 'Ĩ'],
    +  MONTHS: ['Mwai wa mbee', 'Mwai wa kelĩ', 'Mwai wa katatũ', 'Mwai wa kana',
    +    'Mwai wa katano', 'Mwai wa thanthatũ', 'Mwai wa muonza', 'Mwai wa nyaanya',
    +    'Mwai wa kenda', 'Mwai wa ĩkumi', 'Mwai wa ĩkumi na ĩmwe',
    +    'Mwai wa ĩkumi na ilĩ'],
    +  STANDALONEMONTHS: ['Mwai wa mbee', 'Mwai wa kelĩ', 'Mwai wa katatũ',
    +    'Mwai wa kana', 'Mwai wa katano', 'Mwai wa thanthatũ', 'Mwai wa muonza',
    +    'Mwai wa nyaanya', 'Mwai wa kenda', 'Mwai wa ĩkumi',
    +    'Mwai wa ĩkumi na ĩmwe', 'Mwai wa ĩkumi na ilĩ'],
    +  SHORTMONTHS: ['Mbe', 'Kel', 'Ktũ', 'Kan', 'Ktn', 'Tha', 'Moo', 'Nya', 'Knd',
    +    'Ĩku', 'Ĩkm', 'Ĩkl'],
    +  STANDALONESHORTMONTHS: ['Mbe', 'Kel', 'Ktũ', 'Kan', 'Ktn', 'Tha', 'Moo',
    +    'Nya', 'Knd', 'Ĩku', 'Ĩkm', 'Ĩkl'],
    +  WEEKDAYS: ['Wa kyumwa', 'Wa kwambĩlĩlya', 'Wa kelĩ', 'Wa katatũ',
    +    'Wa kana', 'Wa katano', 'Wa thanthatũ'],
    +  STANDALONEWEEKDAYS: ['Wa kyumwa', 'Wa kwambĩlĩlya', 'Wa kelĩ',
    +    'Wa katatũ', 'Wa kana', 'Wa katano', 'Wa thanthatũ'],
    +  SHORTWEEKDAYS: ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'],
    +  STANDALONESHORTWEEKDAYS: ['Wky', 'Wkw', 'Wkl', 'Wtũ', 'Wkn', 'Wtn', 'Wth'],
    +  NARROWWEEKDAYS: ['Y', 'W', 'E', 'A', 'A', 'A', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['Y', 'W', 'E', 'A', 'A', 'A', 'A'],
    +  SHORTQUARTERS: ['L1', 'L2', 'L3', 'L4'],
    +  QUARTERS: ['Lovo ya mbee', 'Lovo ya kelĩ', 'Lovo ya katatũ',
    +    'Lovo ya kana'],
    +  AMPMS: ['Ĩyakwakya', 'Ĩyawĩoo'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kam_KE.
    + */
    +goog.i18n.DateTimeSymbols_kam_KE = goog.i18n.DateTimeSymbols_kam;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kde.
    + */
    +goog.i18n.DateTimeSymbols_kde = {
    +  ERAS: ['AY', 'NY'],
    +  ERANAMES: ['Akanapawa Yesu', 'Nankuida Yesu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Mwedi Ntandi', 'Mwedi wa Pili', 'Mwedi wa Tatu', 'Mwedi wa Nchechi',
    +    'Mwedi wa Nnyano', 'Mwedi wa Nnyano na Umo', 'Mwedi wa Nnyano na Mivili',
    +    'Mwedi wa Nnyano na Mitatu', 'Mwedi wa Nnyano na Nchechi',
    +    'Mwedi wa Nnyano na Nnyano', 'Mwedi wa Nnyano na Nnyano na U',
    +    'Mwedi wa Nnyano na Nnyano na M'],
    +  STANDALONEMONTHS: ['Mwedi Ntandi', 'Mwedi wa Pili', 'Mwedi wa Tatu',
    +    'Mwedi wa Nchechi', 'Mwedi wa Nnyano', 'Mwedi wa Nnyano na Umo',
    +    'Mwedi wa Nnyano na Mivili', 'Mwedi wa Nnyano na Mitatu',
    +    'Mwedi wa Nnyano na Nchechi', 'Mwedi wa Nnyano na Nnyano',
    +    'Mwedi wa Nnyano na Nnyano na U', 'Mwedi wa Nnyano na Nnyano na M'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Liduva lyapili', 'Liduva lyatatu', 'Liduva lyanchechi',
    +    'Liduva lyannyano', 'Liduva lyannyano na linji',
    +    'Liduva lyannyano na mavili', 'Liduva litandi'],
    +  STANDALONEWEEKDAYS: ['Liduva lyapili', 'Liduva lyatatu', 'Liduva lyanchechi',
    +    'Liduva lyannyano', 'Liduva lyannyano na linji',
    +    'Liduva lyannyano na mavili', 'Liduva litandi'],
    +  SHORTWEEKDAYS: ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'],
    +  STANDALONESHORTWEEKDAYS: ['Ll2', 'Ll3', 'Ll4', 'Ll5', 'Ll6', 'Ll7', 'Ll1'],
    +  NARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  STANDALONENARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  SHORTQUARTERS: ['L1', 'L2', 'L3', 'L4'],
    +  QUARTERS: ['Lobo 1', 'Lobo 2', 'Lobo 3', 'Lobo 4'],
    +  AMPMS: ['Muhi', 'Chilo'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kde_TZ.
    + */
    +goog.i18n.DateTimeSymbols_kde_TZ = goog.i18n.DateTimeSymbols_kde;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kea.
    + */
    +goog.i18n.DateTimeSymbols_kea = {
    +  ERAS: ['AK', 'DK'],
    +  ERANAMES: ['Antis di Kristu', 'Dispos di Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janeru', 'Febreru', 'Marsu', 'Abril', 'Maiu', 'Junhu', 'Julhu',
    +    'Agostu', 'Setenbru', 'Otubru', 'Nuvenbru', 'Dizenbru'],
    +  STANDALONEMONTHS: ['Janeru', 'Febreru', 'Marsu', 'Abril', 'Maiu', 'Junhu',
    +    'Julhu', 'Agostu', 'Setenbru', 'Otubru', 'Nuvenbru', 'Dizenbru'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set',
    +    'Otu', 'Nuv', 'Diz'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Otu', 'Nuv', 'Diz'],
    +  WEEKDAYS: ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera',
    +    'kinta-fera', 'sesta-fera', 'sabadu'],
    +  STANDALONEWEEKDAYS: ['dumingu', 'sigunda-fera', 'tersa-fera', 'kuarta-fera',
    +    'kinta-fera', 'sesta-fera', 'sabadu'],
    +  SHORTWEEKDAYS: ['dum', 'sig', 'ter', 'kua', 'kin', 'ses', 'sab'],
    +  STANDALONESHORTWEEKDAYS: ['dum', 'sig', 'ter', 'kua', 'kin', 'ses', 'sab'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'K', 'K', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['d', 's', 't', 'k', 'k', 's', 's'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1º trimestri', '2º trimestri', '3º trimestri',
    +    '4º trimestri'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d \'di\' MMMM \'di\' y', 'd \'di\' MMMM \'di\' y',
    +    'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kea_CV.
    + */
    +goog.i18n.DateTimeSymbols_kea_CV = goog.i18n.DateTimeSymbols_kea;
    +
    +
    +/**
    + * Date/time formatting symbols for locale khq.
    + */
    +goog.i18n.DateTimeSymbols_khq = {
    +  ERAS: ['IJ', 'IZ'],
    +  ERANAMES: ['Isaa jine', 'Isaa jamanoo'],
    +  NARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ',
    +    'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
    +  STANDALONEMONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me',
    +    'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur',
    +    'Deesanbur'],
    +  SHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek',
    +    'Okt', 'Noo', 'Dee'],
    +  STANDALONESHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy',
    +    'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
    +  WEEKDAYS: ['Alhadi', 'Atini', 'Atalata', 'Alarba', 'Alhamiisa', 'Aljuma',
    +    'Assabdu'],
    +  STANDALONEWEEKDAYS: ['Alhadi', 'Atini', 'Atalata', 'Alarba', 'Alhamiisa',
    +    'Aljuma', 'Assabdu'],
    +  SHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'],
    +  STANDALONESHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alj', 'Ass'],
    +  NARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  SHORTQUARTERS: ['A1', 'A2', 'A3', 'A4'],
    +  QUARTERS: ['Arrubu 1', 'Arrubu 2', 'Arrubu 3', 'Arrubu 4'],
    +  AMPMS: ['Adduha', 'Aluula'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale khq_ML.
    + */
    +goog.i18n.DateTimeSymbols_khq_ML = goog.i18n.DateTimeSymbols_khq;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ki.
    + */
    +goog.i18n.DateTimeSymbols_ki = {
    +  ERAS: ['MK', 'TK'],
    +  ERANAMES: ['Mbere ya Kristo', 'Thutha wa Kristo'],
    +  NARROWMONTHS: ['J', 'K', 'G', 'K', 'G', 'G', 'M', 'K', 'K', 'I', 'I', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'K', 'G', 'K', 'G', 'G', 'M', 'K', 'K', 'I',
    +    'I', 'D'],
    +  MONTHS: ['Njenuarĩ', 'Mwere wa kerĩ', 'Mwere wa gatatũ', 'Mwere wa kana',
    +    'Mwere wa gatano', 'Mwere wa gatandatũ', 'Mwere wa mũgwanja',
    +    'Mwere wa kanana', 'Mwere wa kenda', 'Mwere wa ikũmi',
    +    'Mwere wa ikũmi na ũmwe', 'Ndithemba'],
    +  STANDALONEMONTHS: ['Njenuarĩ', 'Mwere wa kerĩ', 'Mwere wa gatatũ',
    +    'Mwere wa kana', 'Mwere wa gatano', 'Mwere wa gatandatũ',
    +    'Mwere wa mũgwanja', 'Mwere wa kanana', 'Mwere wa kenda',
    +    'Mwere wa ikũmi', 'Mwere wa ikũmi na ũmwe', 'Ndithemba'],
    +  SHORTMONTHS: ['JEN', 'WKR', 'WGT', 'WKN', 'WTN', 'WTD', 'WMJ', 'WNN', 'WKD',
    +    'WIK', 'WMW', 'DIT'],
    +  STANDALONESHORTMONTHS: ['JEN', 'WKR', 'WGT', 'WKN', 'WTN', 'WTD', 'WMJ',
    +    'WNN', 'WKD', 'WIK', 'WMW', 'DIT'],
    +  WEEKDAYS: ['Kiumia', 'Njumatatũ', 'Njumaine', 'Njumatana', 'Aramithi',
    +    'Njumaa', 'Njumamothi'],
    +  STANDALONEWEEKDAYS: ['Kiumia', 'Njumatatũ', 'Njumaine', 'Njumatana',
    +    'Aramithi', 'Njumaa', 'Njumamothi'],
    +  SHORTWEEKDAYS: ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'],
    +  STANDALONESHORTWEEKDAYS: ['KMA', 'NTT', 'NMN', 'NMT', 'ART', 'NMA', 'NMM'],
    +  NARROWWEEKDAYS: ['K', 'N', 'N', 'N', 'A', 'N', 'N'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'N', 'N', 'N', 'A', 'N', 'N'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo ya mbere', 'Robo ya kerĩ', 'Robo ya gatatũ',
    +    'Robo ya kana'],
    +  AMPMS: ['Kiroko', 'Hwaĩ-inĩ'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ki_KE.
    + */
    +goog.i18n.DateTimeSymbols_ki_KE = goog.i18n.DateTimeSymbols_ki;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kk_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_kk_Cyrl = {
    +  ERAS: ['б.з.д.', 'б.з.'],
    +  ERANAMES: ['Біздің заманымызға дейін',
    +    'Біздің заманымыз'],
    +  NARROWMONTHS: ['Қ', 'А', 'Н', 'С', 'М', 'М', 'Ш', 'Т', 'Қ', 'Қ',
    +    'Қ', 'Ж'],
    +  STANDALONENARROWMONTHS: ['Қ', 'А', 'Н', 'С', 'М', 'М', 'Ш', 'Т', 'Қ',
    +    'Қ', 'Қ', 'Ж'],
    +  MONTHS: ['қаңтар', 'ақпан', 'наурыз', 'сәуір',
    +    'мамыр', 'маусым', 'шілде', 'тамыз',
    +    'қыркүйек', 'қазан', 'қараша', 'желтоқсан'],
    +  STANDALONEMONTHS: ['қаңтар', 'ақпан', 'наурыз', 'сәуір',
    +    'мамыр', 'маусым', 'шілде', 'тамыз',
    +    'қыркүйек', 'қазан', 'қараша', 'желтоқсан'],
    +  SHORTMONTHS: ['қаң.', 'ақп.', 'нау.', 'сәу.', 'мам.',
    +    'мау.', 'шіл.', 'там.', 'қыр.', 'қаз.', 'қар.',
    +    'желт.'],
    +  STANDALONESHORTMONTHS: ['қаң.', 'ақп.', 'нау.', 'сәу.', 'мам.',
    +    'мау.', 'шіл.', 'там.', 'қыр.', 'қаз.', 'қар.',
    +    'желт.'],
    +  WEEKDAYS: ['жексенбі', 'дүйсенбі', 'сейсенбі',
    +    'сәрсенбі', 'бейсенбі', 'жұма', 'сенбі'],
    +  STANDALONEWEEKDAYS: ['жексенбі', 'дүйсенбі',
    +    'сейсенбі', 'сәрсенбі', 'бейсенбі', 'жұма',
    +    'сенбі'],
    +  SHORTWEEKDAYS: ['жек', 'дүй', 'сей', 'сәр', 'бей', 'жұма',
    +    'сен'],
    +  STANDALONESHORTWEEKDAYS: ['жек', 'дүй', 'сей', 'сәр', 'бей',
    +    'жұма', 'сб.'],
    +  NARROWWEEKDAYS: ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
    +  SHORTQUARTERS: ['Т1', 'Т2', 'Т3', 'Т4'],
    +  QUARTERS: ['1-інші тоқсан', '2-інші тоқсан',
    +    '3-інші тоқсан', '4-інші тоқсан'],
    +  AMPMS: ['таңертеңгі', 'түстен кейінгі'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'y, dd-MMM', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kk_Cyrl_KZ.
    + */
    +goog.i18n.DateTimeSymbols_kk_Cyrl_KZ = goog.i18n.DateTimeSymbols_kk_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kkj.
    + */
    +goog.i18n.DateTimeSymbols_kkj = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['pamba', 'wanja', 'mbiyɔ mɛndoŋgɔ', 'Nyɔlɔmbɔŋgɔ',
    +    'Mɔnɔ ŋgbanja', 'Nyaŋgwɛ ŋgbanja', 'kuŋgwɛ', 'fɛ', 'njapi',
    +    'nyukul', '11', 'ɓulɓusɛ'],
    +  STANDALONEMONTHS: ['pamba', 'wanja', 'mbiyɔ mɛndoŋgɔ', 'Nyɔlɔmbɔŋgɔ',
    +    'Mɔnɔ ŋgbanja', 'Nyaŋgwɛ ŋgbanja', 'kuŋgwɛ', 'fɛ', 'njapi',
    +    'nyukul', '11', 'ɓulɓusɛ'],
    +  SHORTMONTHS: ['pamba', 'wanja', 'mbiyɔ mɛndoŋgɔ', 'Nyɔlɔmbɔŋgɔ',
    +    'Mɔnɔ ŋgbanja', 'Nyaŋgwɛ ŋgbanja', 'kuŋgwɛ', 'fɛ', 'njapi',
    +    'nyukul', '11', 'ɓulɓusɛ'],
    +  STANDALONESHORTMONTHS: ['pamba', 'wanja', 'mbiyɔ mɛndoŋgɔ',
    +    'Nyɔlɔmbɔŋgɔ', 'Mɔnɔ ŋgbanja', 'Nyaŋgwɛ ŋgbanja', 'kuŋgwɛ',
    +    'fɛ', 'njapi', 'nyukul', '11', 'ɓulɓusɛ'],
    +  WEEKDAYS: ['sɔndi', 'lundi', 'mardi', 'mɛrkɛrɛdi', 'yedi', 'vaŋdɛrɛdi',
    +    'mɔnɔ sɔndi'],
    +  STANDALONEWEEKDAYS: ['sɔndi', 'lundi', 'mardi', 'mɛrkɛrɛdi', 'yedi',
    +    'vaŋdɛrɛdi', 'mɔnɔ sɔndi'],
    +  SHORTWEEKDAYS: ['sɔndi', 'lundi', 'mardi', 'mɛrkɛrɛdi', 'yedi',
    +    'vaŋdɛrɛdi', 'mɔnɔ sɔndi'],
    +  STANDALONESHORTWEEKDAYS: ['sɔndi', 'lundi', 'mardi', 'mɛrkɛrɛdi', 'yedi',
    +    'vaŋdɛrɛdi', 'mɔnɔ sɔndi'],
    +  NARROWWEEKDAYS: ['so', 'lu', 'ma', 'mɛ', 'ye', 'va', 'ms'],
    +  STANDALONENARROWWEEKDAYS: ['so', 'lu', 'ma', 'mɛ', 'ye', 'va', 'ms'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kkj_CM.
    + */
    +goog.i18n.DateTimeSymbols_kkj_CM = goog.i18n.DateTimeSymbols_kkj;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kl.
    + */
    +goog.i18n.DateTimeSymbols_kl = {
    +  ERAS: ['Kr.in.si.', 'Kr.in.king.'],
    +  ERANAMES: ['Kristusip inunngornerata siornagut',
    +    'Kristusip inunngornerata kingornagut'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'martsi', 'aprili', 'maji', 'juni', 'juli',
    +    'augustusi', 'septemberi', 'oktoberi', 'novemberi', 'decemberi'],
    +  STANDALONEMONTHS: ['januari', 'februari', 'martsi', 'aprili', 'maji', 'juni',
    +    'juli', 'augustusi', 'septemberi', 'oktoberi', 'novemberi', 'decemberi'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep',
    +    'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['sabaat', 'ataasinngorneq', 'marlunngorneq', 'pingasunngorneq',
    +    'sisamanngorneq', 'tallimanngorneq', 'arfininngorneq'],
    +  STANDALONEWEEKDAYS: ['sabaat', 'ataasinngorneq', 'marlunngorneq',
    +    'pingasunngorneq', 'sisamanngorneq', 'tallimanngorneq', 'arfininngorneq'],
    +  SHORTWEEKDAYS: ['sab', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'],
    +  STANDALONESHORTWEEKDAYS: ['sab', 'ata', 'mar', 'pin', 'sis', 'tal', 'arf'],
    +  NARROWWEEKDAYS: ['S', 'A', 'M', 'P', 'S', 'T', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'A', 'M', 'P', 'S', 'T', 'A'],
    +  SHORTQUARTERS: ['S1', 'S2', 'S3', 'S4'],
    +  QUARTERS: ['ukiup sisamararterutaa 1', 'ukiup sisamararterutaa 2',
    +    'ukiup sisamararterutaa 3', 'ukiup sisamararterutaa 4'],
    +  AMPMS: ['ulloqeqqata-tungaa', 'ulloqeqqata-kingorna'],
    +  DATEFORMATS: ['EEEE dd MMMM y', 'dd MMMM y', 'MMM dd, y', 'y-MM-dd'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kl_GL.
    + */
    +goog.i18n.DateTimeSymbols_kl_GL = goog.i18n.DateTimeSymbols_kl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kln.
    + */
    +goog.i18n.DateTimeSymbols_kln = {
    +  ERAS: ['AM', 'KO'],
    +  ERANAMES: ['Amait kesich Jesu', 'Kokakesich Jesu'],
    +  NARROWMONTHS: ['M', 'N', 'K', 'I', 'N', 'W', 'R', 'K', 'B', 'E', 'K', 'K'],
    +  STANDALONENARROWMONTHS: ['M', 'N', 'K', 'I', 'N', 'W', 'R', 'K', 'B', 'E',
    +    'K', 'K'],
    +  MONTHS: ['Mulgul', 'Ng’atyato', 'Kiptamo', 'Iwat kut', 'Ng’eiyet', 'Waki',
    +    'Roptui', 'Kipkogaga', 'Buret', 'Epeso', 'Kipsunde netai',
    +    'Kipsunde nebo aeng'],
    +  STANDALONEMONTHS: ['Mulgul', 'Ng’atyato', 'Kiptamo', 'Iwat kut',
    +    'Ng’eiyet', 'Waki', 'Roptui', 'Kipkogaga', 'Buret', 'Epeso',
    +    'Kipsunde netai', 'Kipsunde nebo aeng'],
    +  SHORTMONTHS: ['Mul', 'Nga', 'Kip', 'Iwa', 'Nge', 'Wak', 'Rop', 'Kog', 'Bur',
    +    'Epe', 'Tai', 'Aen'],
    +  STANDALONESHORTMONTHS: ['Mul', 'Nga', 'Kip', 'Iwa', 'Nge', 'Wak', 'Rop',
    +    'Kog', 'Bur', 'Epe', 'Tai', 'Aen'],
    +  WEEKDAYS: ['Betutab tisap', 'Betut netai', 'Betutab aeng’', 'Betutab somok',
    +    'Betutab ang’wan', 'Betutab mut', 'Betutab lo'],
    +  STANDALONEWEEKDAYS: ['Betutab tisap', 'Betut netai', 'Betutab aeng’',
    +    'Betutab somok', 'Betutab ang’wan', 'Betutab mut', 'Betutab lo'],
    +  SHORTWEEKDAYS: ['Tis', 'Tai', 'Aen', 'Som', 'Ang', 'Mut', 'Loh'],
    +  STANDALONESHORTWEEKDAYS: ['Tis', 'Tai', 'Aen', 'Som', 'Ang', 'Mut', 'Loh'],
    +  NARROWWEEKDAYS: ['T', 'T', 'A', 'S', 'A', 'M', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['T', 'T', 'A', 'S', 'A', 'M', 'L'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo netai', 'Robo nebo aeng’', 'Robo nebo somok',
    +    'Robo nebo ang’wan'],
    +  AMPMS: ['Beet', 'Kemo'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kln_KE.
    + */
    +goog.i18n.DateTimeSymbols_kln_KE = goog.i18n.DateTimeSymbols_kln;
    +
    +
    +/**
    + * Date/time formatting symbols for locale km_KH.
    + */
    +goog.i18n.DateTimeSymbols_km_KH = {
    +  ERAS: ['មុន គ.ស.', 'គ.ស.'],
    +  ERANAMES: ['មុន​គ្រិស្តសករាជ',
    +    'គ្រិស្តសករាជ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['មករា', 'កុម្ភៈ', 'មីនា', 'មេសា',
    +    'ឧសភា', 'មិថុនា', 'កក្កដា', 'សីហា',
    +    'កញ្ញា', 'តុលា', 'វិច្ឆិកា',
    +    'ធ្នូ'],
    +  STANDALONEMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  SHORTMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  STANDALONESHORTMONTHS: ['មករា', 'កុម្ភៈ', 'មីនា',
    +    'មេសា', 'ឧសភា', 'មិថុនា', 'កក្កដា',
    +    'សីហា', 'កញ្ញា', 'តុលា',
    +    'វិច្ឆិកា', 'ធ្នូ'],
    +  WEEKDAYS: ['អាទិត្យ', 'ចន្ទ', 'អង្គារ',
    +    'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ',
    +    'សៅរ៍'],
    +  STANDALONEWEEKDAYS: ['អាទិត្យ', 'ចន្ទ',
    +    'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍',
    +    'សុក្រ', 'សៅរ៍'],
    +  SHORTWEEKDAYS: ['អាទិត្យ', 'ចន្ទ', 'អង្គារ',
    +    'ពុធ', 'ព្រហស្បតិ៍', 'សុក្រ',
    +    'សៅរ៍'],
    +  STANDALONESHORTWEEKDAYS: ['អាទិត្យ', 'ចន្ទ',
    +    'អង្គារ', 'ពុធ', 'ព្រហស្បតិ៍',
    +    'សុក្រ', 'សៅរ៍'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  SHORTQUARTERS: ['ត្រីមាសទី ១',
    +    'ត្រីមាសទី ២', 'ត្រីមាសទី ៣',
    +    'ត្រីមាសទី ៤'],
    +  QUARTERS: ['ត្រីមាសទី ១',
    +    'ត្រីមាសទី ២', 'ត្រីមាសទី ៣',
    +    'ត្រីមាសទី ៤'],
    +  AMPMS: ['ព្រឹក', 'ល្ងាច'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} នៅ {0}', '{1} នៅ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kn_IN.
    + */
    +goog.i18n.DateTimeSymbols_kn_IN = {
    +  ERAS: ['ಕ್ರಿ.ಪೂ', 'ಕ್ರಿ.ಶ'],
    +  ERANAMES: ['ಕ್ರಿಸ್ತ ಪೂರ್ವ',
    +    'ಕ್ರಿಸ್ತ ಶಕ'],
    +  NARROWMONTHS: ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ', 'ಜು',
    +    'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'],
    +  STANDALONENARROWMONTHS: ['ಜ', 'ಫೆ', 'ಮಾ', 'ಏ', 'ಮೇ', 'ಜೂ',
    +    'ಜು', 'ಆ', 'ಸೆ', 'ಅ', 'ನ', 'ಡಿ'],
    +  MONTHS: ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ',
    +    'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್',
    +    'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್',
    +    'ಡಿಸೆಂಬರ್'],
    +  STANDALONEMONTHS: ['ಜನವರಿ', 'ಫೆಬ್ರವರಿ',
    +    'ಮಾರ್ಚ್', 'ಏಪ್ರಿಲ್', 'ಮೇ', 'ಜೂನ್',
    +    'ಜುಲೈ', 'ಆಗಸ್ಟ್', 'ಸೆಪ್ಟೆಂಬರ್',
    +    'ಅಕ್ಟೋಬರ್', 'ನವೆಂಬರ್',
    +    'ಡಿಸೆಂಬರ್'],
    +  SHORTMONTHS: ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿ', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗ',
    +    'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ',
    +    'ಡಿಸೆಂ'],
    +  STANDALONESHORTMONTHS: ['ಜನ', 'ಫೆಬ್ರ', 'ಮಾರ್ಚ್',
    +    'ಏಪ್ರಿ', 'ಮೇ', 'ಜೂನ್', 'ಜುಲೈ', 'ಆಗ',
    +    'ಸೆಪ್ಟೆಂ', 'ಅಕ್ಟೋ', 'ನವೆಂ',
    +    'ಡಿಸೆಂ'],
    +  WEEKDAYS: ['ಭಾನುವಾರ', 'ಸೋಮವಾರ',
    +    'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ',
    +    'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'],
    +  STANDALONEWEEKDAYS: ['ಭಾನುವಾರ', 'ಸೋಮವಾರ',
    +    'ಮಂಗಳವಾರ', 'ಬುಧವಾರ', 'ಗುರುವಾರ',
    +    'ಶುಕ್ರವಾರ', 'ಶನಿವಾರ'],
    +  SHORTWEEKDAYS: ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ', 'ಬುಧ',
    +    'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'],
    +  STANDALONESHORTWEEKDAYS: ['ಭಾನು', 'ಸೋಮ', 'ಮಂಗಳ',
    +    'ಬುಧ', 'ಗುರು', 'ಶುಕ್ರ', 'ಶನಿ'],
    +  NARROWWEEKDAYS: ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು',
    +    'ಶ'],
    +  STANDALONENARROWWEEKDAYS: ['ಭಾ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು',
    +    'ಶು', 'ಶ'],
    +  SHORTQUARTERS: ['ತ್ರೈ 1', 'ತ್ರೈ 2', 'ತ್ರೈ 3',
    +    'ತ್ರೈ 4'],
    +  QUARTERS: ['1ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '2ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '3ನೇ ತ್ರೈಮಾಸಿಕ',
    +    '4ನೇ ತ್ರೈಮಾಸಿಕ'],
    +  AMPMS: ['ಪೂರ್ವಾಹ್ನ', 'ಅಪರಾಹ್ನ'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['hh:mm:ss a zzzz', 'hh:mm:ss a z', 'hh:mm:ss a', 'hh:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ko_KP.
    + */
    +goog.i18n.DateTimeSymbols_ko_KP = {
    +  ERAS: ['기원전', '서기'],
    +  ERANAMES: ['기원전', '서기'],
    +  NARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONENARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  MONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONEMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월',
    +    '8월', '9월', '10월', '11월', '12월'],
    +  SHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONESHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  WEEKDAYS: ['일요일', '월요일', '화요일', '수요일', '목요일',
    +    '금요일', '토요일'],
    +  STANDALONEWEEKDAYS: ['일요일', '월요일', '화요일', '수요일',
    +    '목요일', '금요일', '토요일'],
    +  SHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONESHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  NARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONENARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  SHORTQUARTERS: ['1분기', '2분기', '3분기', '4분기'],
    +  QUARTERS: ['제 1/4분기', '제 2/4분기', '제 3/4분기',
    +    '제 4/4분기'],
    +  AMPMS: ['오전', '오후'],
    +  DATEFORMATS: ['y년 M월 d일 EEEE', 'y년 M월 d일', 'y. M. d.',
    +    'yy. M. d.'],
    +  TIMEFORMATS: ['a h시 m분 s초 zzzz', 'a h시 m분 s초 z', 'a h:mm:ss',
    +    'a h:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ko_KR.
    + */
    +goog.i18n.DateTimeSymbols_ko_KR = {
    +  ERAS: ['기원전', '서기'],
    +  ERANAMES: ['기원전', '서기'],
    +  NARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONENARROWMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  MONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONEMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월',
    +    '8월', '9월', '10월', '11월', '12월'],
    +  SHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월',
    +    '9월', '10월', '11월', '12월'],
    +  STANDALONESHORTMONTHS: ['1월', '2월', '3월', '4월', '5월', '6월',
    +    '7월', '8월', '9월', '10월', '11월', '12월'],
    +  WEEKDAYS: ['일요일', '월요일', '화요일', '수요일', '목요일',
    +    '금요일', '토요일'],
    +  STANDALONEWEEKDAYS: ['일요일', '월요일', '화요일', '수요일',
    +    '목요일', '금요일', '토요일'],
    +  SHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONESHORTWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  NARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  STANDALONENARROWWEEKDAYS: ['일', '월', '화', '수', '목', '금', '토'],
    +  SHORTQUARTERS: ['1분기', '2분기', '3분기', '4분기'],
    +  QUARTERS: ['제 1/4분기', '제 2/4분기', '제 3/4분기',
    +    '제 4/4분기'],
    +  AMPMS: ['오전', '오후'],
    +  DATEFORMATS: ['y년 M월 d일 EEEE', 'y년 M월 d일', 'y. M. d.',
    +    'yy. M. d.'],
    +  TIMEFORMATS: ['a h시 m분 s초 zzzz', 'a h시 m분 s초 z', 'a h:mm:ss',
    +    'a h:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kok.
    + */
    +goog.i18n.DateTimeSymbols_kok = {
    +  ERAS: ['क्रिस्तपूर्व',
    +    'क्रिस्तशखा'],
    +  ERANAMES: ['क्रिस्तपूर्व',
    +    'क्रिस्तशखा'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['जानेवारी', 'फेब्रुवारी',
    +    'मार्च', 'एप्रिल', 'मे', 'जून',
    +    'जुलै', 'ओगस्ट', 'सेप्टेंबर',
    +    'ओक्टोबर', 'नोव्हेंबर',
    +    'डिसेंबर'],
    +  STANDALONEMONTHS: ['जानेवारी',
    +    'फेब्रुवारी', 'मार्च', 'एप्रिल',
    +    'मे', 'जून', 'जुलै', 'ओगस्ट',
    +    'सेप्टेंबर', 'ओक्टोबर',
    +    'नोव्हेंबर', 'डिसेंबर'],
    +  SHORTMONTHS: ['जानेवारी', 'फेब्रुवारी',
    +    'मार्च', 'एप्रिल', 'मे', 'जून',
    +    'जुलै', 'ओगस्ट', 'सेप्टेंबर',
    +    'ओक्टोबर', 'नोव्हेंबर',
    +    'डिसेंबर'],
    +  STANDALONESHORTMONTHS: ['जानेवारी',
    +    'फेब्रुवारी', 'मार्च', 'एप्रिल',
    +    'मे', 'जून', 'जुलै', 'ओगस्ट',
    +    'सेप्टेंबर', 'ओक्टोबर',
    +    'नोव्हेंबर', 'डिसेंबर'],
    +  WEEKDAYS: ['आदित्यवार', 'सोमवार',
    +    'मंगळार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['आदित्यवार', 'सोमवार',
    +    'मंगळार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  SHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ', 'बुध',
    +    'गुरु', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ',
    +    'बुध', 'गुरु', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['म.पू.', 'म.नं.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'dd-MM-y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kok_IN.
    + */
    +goog.i18n.DateTimeSymbols_kok_IN = goog.i18n.DateTimeSymbols_kok;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ks.
    + */
    +goog.i18n.DateTimeSymbols_ks = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['بی سی', 'اے ڈی'],
    +  ERANAMES: ['قبٕل مسیٖح', 'عیٖسوی سنہٕ'],
    +  NARROWMONTHS: ['ج', 'ف', 'م', 'ا', 'م', 'ج', 'ج', 'ا', 'س', 'س',
    +    'ا', 'ن'],
    +  STANDALONENARROWMONTHS: ['ج', 'ف', 'م', 'ا', 'م', 'ج', 'ج', 'ا', 'س',
    +    'س', 'ا', 'ن'],
    +  MONTHS: ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل', 'میٔ',
    +    'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر', 'اکتوٗبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل',
    +    'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر',
    +    'اکتوٗبر', 'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنؤری', 'فرؤری', 'مارٕچ', 'اپریل',
    +    'میٔ', 'جوٗن', 'جوٗلایی', 'اگست', 'ستمبر',
    +    'اکتوٗبر', 'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنؤری', 'فرؤری', 'مارٕچ',
    +    'اپریل', 'میٔ', 'جوٗن', 'جوٗلایی', 'اگست',
    +    'ستمبر', 'اکتوٗبر', 'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['اَتھوار', 'ژٔنٛدرٕروار', 'بوٚموار',
    +    'بودوار', 'برٛٮ۪سوار', 'جُمہ', 'بٹوار'],
    +  STANDALONEWEEKDAYS: ['اَتھوار', 'ژٔنٛدرٕروار',
    +    'بوٚموار', 'بودوار', 'برٛٮ۪سوار', 'جُمہ',
    +    'بٹوار'],
    +  SHORTWEEKDAYS: ['آتھوار', 'ژٔنٛدٕروار', 'بوٚموار',
    +    'بودوار', 'برٛٮ۪سوار', 'جُمہ', 'بٹوار'],
    +  STANDALONESHORTWEEKDAYS: ['آتھوار', 'ژٔنٛدٕروار',
    +    'بوٚموار', 'بودوار', 'برٛٮ۪سوار', 'جُمہ',
    +    'بٹوار'],
    +  NARROWWEEKDAYS: ['ا', 'ژ', 'ب', 'ب', 'ب', 'ج', 'ب'],
    +  STANDALONENARROWWEEKDAYS: ['ا', 'ژ', 'ب', 'ب', 'ب', 'ج', 'ب'],
    +  SHORTQUARTERS: ['ژۄباگ', 'دوٚیِم ژۄباگ',
    +    'ترٛیِم ژۄباگ', 'ژوٗرِم ژۄباگ'],
    +  QUARTERS: ['گۄڑنیُک ژۄباگ', 'دوٚیِم ژۄباگ',
    +    'ترٛیِم ژۄباگ', 'ژوٗرِم ژۄباگ'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ks_Arab.
    + */
    +goog.i18n.DateTimeSymbols_ks_Arab = goog.i18n.DateTimeSymbols_ks;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ks_Arab_IN.
    + */
    +goog.i18n.DateTimeSymbols_ks_Arab_IN = goog.i18n.DateTimeSymbols_ks;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksb.
    + */
    +goog.i18n.DateTimeSymbols_ksb = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Klisto', 'Baada ya Klisto'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januali', 'Febluali', 'Machi', 'Aplili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januali', 'Febluali', 'Machi', 'Aplili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumaapii', 'Jumaatatu', 'Jumaane', 'Jumaatano', 'Alhamisi',
    +    'Ijumaa', 'Jumaamosi'],
    +  STANDALONEWEEKDAYS: ['Jumaapii', 'Jumaatatu', 'Jumaane', 'Jumaatano',
    +    'Alhamisi', 'Ijumaa', 'Jumaamosi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jmn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['2', '3', '4', '5', 'A', 'I', '1'],
    +  STANDALONENARROWWEEKDAYS: ['2', '3', '4', '5', 'A', 'I', '1'],
    +  SHORTQUARTERS: ['L1', 'L2', 'L3', 'L4'],
    +  QUARTERS: ['Lobo ya bosi', 'Lobo ya mbii', 'Lobo ya nnd’atu',
    +    'Lobo ya nne'],
    +  AMPMS: ['makeo', 'nyiaghuo'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksb_TZ.
    + */
    +goog.i18n.DateTimeSymbols_ksb_TZ = goog.i18n.DateTimeSymbols_ksb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksf.
    + */
    +goog.i18n.DateTimeSymbols_ksf = {
    +  ERAS: ['d.Y.', 'k.Y.'],
    +  ERANAMES: ['di Yɛ́sus aká yálɛ', 'cámɛɛn kǝ kǝbɔpka Y'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ŋwíí a ntɔ́ntɔ', 'ŋwíí akǝ bɛ́ɛ', 'ŋwíí akǝ ráá',
    +    'ŋwíí akǝ nin', 'ŋwíí akǝ táan', 'ŋwíí akǝ táafɔk',
    +    'ŋwíí akǝ táabɛɛ', 'ŋwíí akǝ táaraa', 'ŋwíí akǝ táanin',
    +    'ŋwíí akǝ ntɛk', 'ŋwíí akǝ ntɛk di bɔ́k',
    +    'ŋwíí akǝ ntɛk di bɛ́ɛ'],
    +  STANDALONEMONTHS: ['ŋwíí a ntɔ́ntɔ', 'ŋwíí akǝ bɛ́ɛ',
    +    'ŋwíí akǝ ráá', 'ŋwíí akǝ nin', 'ŋwíí akǝ táan',
    +    'ŋwíí akǝ táafɔk', 'ŋwíí akǝ táabɛɛ', 'ŋwíí akǝ táaraa',
    +    'ŋwíí akǝ táanin', 'ŋwíí akǝ ntɛk',
    +    'ŋwíí akǝ ntɛk di bɔ́k', 'ŋwíí akǝ ntɛk di bɛ́ɛ'],
    +  SHORTMONTHS: ['ŋ1', 'ŋ2', 'ŋ3', 'ŋ4', 'ŋ5', 'ŋ6', 'ŋ7', 'ŋ8', 'ŋ9',
    +    'ŋ10', 'ŋ11', 'ŋ12'],
    +  STANDALONESHORTMONTHS: ['ŋ1', 'ŋ2', 'ŋ3', 'ŋ4', 'ŋ5', 'ŋ6', 'ŋ7',
    +    'ŋ8', 'ŋ9', 'ŋ10', 'ŋ11', 'ŋ12'],
    +  WEEKDAYS: ['sɔ́ndǝ', 'lǝndí', 'maadí', 'mɛkrɛdí', 'jǝǝdí',
    +    'júmbá', 'samdí'],
    +  STANDALONEWEEKDAYS: ['sɔ́ndǝ', 'lǝndí', 'maadí', 'mɛkrɛdí',
    +    'jǝǝdí', 'júmbá', 'samdí'],
    +  SHORTWEEKDAYS: ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm', 'sam'],
    +  STANDALONESHORTWEEKDAYS: ['sɔ́n', 'lǝn', 'maa', 'mɛk', 'jǝǝ', 'júm',
    +    'sam'],
    +  NARROWWEEKDAYS: ['s', 'l', 'm', 'm', 'j', 'j', 's'],
    +  STANDALONENARROWWEEKDAYS: ['s', 'l', 'm', 'm', 'j', 'j', 's'],
    +  SHORTQUARTERS: ['i1', 'i2', 'i3', 'i4'],
    +  QUARTERS: ['id́ɛ́n kǝbǝk kǝ ntɔ́ntɔ́',
    +    'idɛ́n kǝbǝk kǝ kǝbɛ́ɛ', 'idɛ́n kǝbǝk kǝ kǝráá',
    +    'idɛ́n kǝbǝk kǝ kǝnin'],
    +  AMPMS: ['sárúwá', 'cɛɛ́nko'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksf_CM.
    + */
    +goog.i18n.DateTimeSymbols_ksf_CM = goog.i18n.DateTimeSymbols_ksf;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksh.
    + */
    +goog.i18n.DateTimeSymbols_ksh = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['vür Chrestus', 'noh Chrestus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Jannewa', 'Fäbrowa', 'Määz', 'Aprell', 'Mäi', 'Juuni', 'Juuli',
    +    'Oujoß', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  STANDALONEMONTHS: ['Jannewa', 'Fäbrowa', 'Määz', 'Aprell', 'Mäi', 'Juuni',
    +    'Juuli', 'Oujoß', 'Septämber', 'Oktoober', 'Novämber', 'Dezämber'],
    +  SHORTMONTHS: ['Jan', 'Fäb', 'Mäz', 'Apr', 'Mäi', 'Jun', 'Jul', 'Ouj',
    +    'Säp', 'Okt', 'Nov', 'Dez'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Fäb.', 'Mäz.', 'Apr.', 'Mäi', 'Jun.',
    +    'Jul.', 'Ouj.', 'Säp.', 'Okt.', 'Nov.', 'Dez.'],
    +  WEEKDAYS: ['Sunndaach', 'Moondaach', 'Dinnsdaach', 'Metwoch', 'Dunnersdaach',
    +    'Friidaach', 'Samsdaach'],
    +  STANDALONEWEEKDAYS: ['Sunndaach', 'Moondaach', 'Dinnsdaach', 'Metwoch',
    +    'Dunnersdaach', 'Friidaach', 'Samsdaach'],
    +  SHORTWEEKDAYS: ['Su.', 'Mo.', 'Di.', 'Me.', 'Du.', 'Fr.', 'Sa.'],
    +  STANDALONESHORTWEEKDAYS: ['Su.', 'Mo.', 'Di.', 'Me.', 'Du.', 'Fr.', 'Sa.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['1.Q.', '2.Q.', '3.Q.', '4.Q.'],
    +  QUARTERS: ['1. Quattaal', '2. Quattaal', '3. Quattaal', '4. Quattaal'],
    +  AMPMS: ['Uhr vörmiddaachs', 'Uhr nommendaachs'],
    +  DATEFORMATS: ['EEEE, \'dä\' d. MMMM y', 'd. MMMM y', 'd. MMM. y', 'd. M. y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ksh_DE.
    + */
    +goog.i18n.DateTimeSymbols_ksh_DE = goog.i18n.DateTimeSymbols_ksh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale kw.
    + */
    +goog.i18n.DateTimeSymbols_kw = {
    +  ERAS: ['RC', 'AD'],
    +  ERANAMES: ['RC', 'AD'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Mys Genver', 'Mys Whevrel', 'Mys Merth', 'Mys Ebrel', 'Mys Me',
    +    'Mys Efan', 'Mys Gortheren', 'Mye Est', 'Mys Gwyngala', 'Mys Hedra',
    +    'Mys Du', 'Mys Kevardhu'],
    +  STANDALONEMONTHS: ['Mys Genver', 'Mys Whevrel', 'Mys Merth', 'Mys Ebrel',
    +    'Mys Me', 'Mys Efan', 'Mys Gortheren', 'Mye Est', 'Mys Gwyngala',
    +    'Mys Hedra', 'Mys Du', 'Mys Kevardhu'],
    +  SHORTMONTHS: ['Gen', 'Whe', 'Mer', 'Ebr', 'Me', 'Efn', 'Gor', 'Est', 'Gwn',
    +    'Hed', 'Du', 'Kev'],
    +  STANDALONESHORTMONTHS: ['Gen', 'Whe', 'Mer', 'Ebr', 'Me', 'Efn', 'Gor', 'Est',
    +    'Gwn', 'Hed', 'Du', 'Kev'],
    +  WEEKDAYS: ['De Sul', 'De Lun', 'De Merth', 'De Merher', 'De Yow', 'De Gwener',
    +    'De Sadorn'],
    +  STANDALONEWEEKDAYS: ['De Sul', 'De Lun', 'De Merth', 'De Merher', 'De Yow',
    +    'De Gwener', 'De Sadorn'],
    +  SHORTWEEKDAYS: ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'],
    +  STANDALONESHORTWEEKDAYS: ['Sul', 'Lun', 'Mth', 'Mhr', 'Yow', 'Gwe', 'Sad'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale kw_GB.
    + */
    +goog.i18n.DateTimeSymbols_kw_GB = goog.i18n.DateTimeSymbols_kw;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ky_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_ky_Cyrl = {
    +  ERAS: ['б.з.ч.', 'б.з.'],
    +  ERANAMES: ['биздин заманга чейин',
    +    'биздин заман'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['январь', 'февраль', 'март', 'апрель',
    +    'май', 'июнь', 'июль', 'август', 'сентябрь',
    +    'октябрь', 'ноябрь', 'декабрь'],
    +  STANDALONEMONTHS: ['Январь', 'Февраль', 'Март',
    +    'Апрель', 'Май', 'Июнь', 'Июль', 'Август',
    +    'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
    +  SHORTMONTHS: ['янв.', 'фев.', 'мар.', 'апр.', 'май', 'июн.',
    +    'июл.', 'авг.', 'сен.', 'окт.', 'ноя.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['Янв', 'Фев', 'Мар', 'Апр', 'Май',
    +    'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
    +  WEEKDAYS: ['жекшемби', 'дүйшөмбү', 'шейшемби',
    +    'шаршемби', 'бейшемби', 'жума', 'ишемби'],
    +  STANDALONEWEEKDAYS: ['жекшемби', 'дүйшөмбү',
    +    'шейшемби', 'шаршемби', 'бейшемби', 'жума',
    +    'ишемби'],
    +  SHORTWEEKDAYS: ['жек.', 'дүй.', 'шейш.', 'шарш.', 'бейш.',
    +    'жума', 'ишм.'],
    +  STANDALONESHORTWEEKDAYS: ['жек.', 'дүй.', 'шейш.', 'шарш.',
    +    'бейш.', 'жума', 'ишм.'],
    +  NARROWWEEKDAYS: ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
    +  STANDALONENARROWWEEKDAYS: ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
    +  SHORTQUARTERS: ['1-чей.', '2-чей.', '3-чей.', '4-чей.'],
    +  QUARTERS: ['1-чейрек', '2-чейрек', '3-чейрек',
    +    '4-чейрек'],
    +  AMPMS: ['таңкы', 'түштөн кийин'],
    +  DATEFORMATS: ['EEEE, d-MMMM, y-\'ж\'.', 'y MMMM d', 'y MMM d', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ky_Cyrl_KG.
    + */
    +goog.i18n.DateTimeSymbols_ky_Cyrl_KG = goog.i18n.DateTimeSymbols_ky_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale lag.
    + */
    +goog.i18n.DateTimeSymbols_lag = {
    +  ERAS: ['KSA', 'KA'],
    +  ERANAMES: ['Kɨrɨsitʉ sɨ anavyaal', 'Kɨrɨsitʉ akavyaalwe'],
    +  NARROWMONTHS: ['F', 'N', 'K', 'I', 'I', 'I', 'M', 'V', 'S', 'I', 'S', 'S'],
    +  STANDALONENARROWMONTHS: ['F', 'N', 'K', 'I', 'I', 'I', 'M', 'V', 'S', 'I',
    +    'S', 'S'],
    +  MONTHS: ['Kʉfúngatɨ', 'Kʉnaanɨ', 'Kʉkeenda', 'Kwiikumi',
    +    'Kwiinyambála', 'Kwiidwaata', 'Kʉmʉʉnchɨ', 'Kʉvɨɨrɨ', 'Kʉsaatʉ',
    +    'Kwiinyi', 'Kʉsaano', 'Kʉsasatʉ'],
    +  STANDALONEMONTHS: ['Kʉfúngatɨ', 'Kʉnaanɨ', 'Kʉkeenda', 'Kwiikumi',
    +    'Kwiinyambála', 'Kwiidwaata', 'Kʉmʉʉnchɨ', 'Kʉvɨɨrɨ', 'Kʉsaatʉ',
    +    'Kwiinyi', 'Kʉsaano', 'Kʉsasatʉ'],
    +  SHORTMONTHS: ['Fúngatɨ', 'Naanɨ', 'Keenda', 'Ikúmi', 'Inyambala',
    +    'Idwaata', 'Mʉʉnchɨ', 'Vɨɨrɨ', 'Saatʉ', 'Inyi', 'Saano', 'Sasatʉ'],
    +  STANDALONESHORTMONTHS: ['Fúngatɨ', 'Naanɨ', 'Keenda', 'Ikúmi',
    +    'Inyambala', 'Idwaata', 'Mʉʉnchɨ', 'Vɨɨrɨ', 'Saatʉ', 'Inyi', 'Saano',
    +    'Sasatʉ'],
    +  WEEKDAYS: ['Jumapíiri', 'Jumatátu', 'Jumaíne', 'Jumatáano', 'Alamíisi',
    +    'Ijumáa', 'Jumamóosi'],
    +  STANDALONEWEEKDAYS: ['Jumapíiri', 'Jumatátu', 'Jumaíne', 'Jumatáano',
    +    'Alamíisi', 'Ijumáa', 'Jumamóosi'],
    +  SHORTWEEKDAYS: ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm', 'Móosi'],
    +  STANDALONESHORTWEEKDAYS: ['Píili', 'Táatu', 'Íne', 'Táano', 'Alh', 'Ijm',
    +    'Móosi'],
    +  NARROWWEEKDAYS: ['P', 'T', 'E', 'O', 'A', 'I', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'T', 'E', 'O', 'A', 'I', 'M'],
    +  SHORTQUARTERS: ['Ncho 1', 'Ncho 2', 'Ncho 3', 'Ncho 4'],
    +  QUARTERS: ['Ncholo ya 1', 'Ncholo ya 2', 'Ncholo ya 3', 'Ncholo ya 4'],
    +  AMPMS: ['TOO', 'MUU'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lag_TZ.
    + */
    +goog.i18n.DateTimeSymbols_lag_TZ = goog.i18n.DateTimeSymbols_lag;
    +
    +
    +/**
    + * Date/time formatting symbols for locale lb.
    + */
    +goog.i18n.DateTimeSymbols_lb = {
    +  ERAS: ['v. Chr.', 'n. Chr.'],
    +  ERANAMES: ['v. Chr.', 'n. Chr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni', 'Juli',
    +    'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  STANDALONEMONTHS: ['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni',
    +    'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
    +  SHORTMONTHS: ['Jan.', 'Feb.', 'Mäe.', 'Abr.', 'Mee', 'Juni', 'Juli', 'Aug.',
    +    'Sep.', 'Okt.', 'Nov.', 'Dez.'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
    +  WEEKDAYS: ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch', 'Donneschdeg',
    +    'Freideg', 'Samschdeg'],
    +  STANDALONEWEEKDAYS: ['Sonndeg', 'Méindeg', 'Dënschdeg', 'Mëttwoch',
    +    'Donneschdeg', 'Freideg', 'Samschdeg'],
    +  SHORTWEEKDAYS: ['Son.', 'Méi.', 'Dën.', 'Mët.', 'Don.', 'Fre.', 'Sam.'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Méi', 'Dën', 'Mët', 'Don', 'Fre', 'Sam'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
    +  AMPMS: ['moies', 'nomëttes'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lb_LU.
    + */
    +goog.i18n.DateTimeSymbols_lb_LU = goog.i18n.DateTimeSymbols_lb;
    +
    +
    +/**
    + * Date/time formatting symbols for locale lg.
    + */
    +goog.i18n.DateTimeSymbols_lg = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kulisito nga tannaza', 'Bukya Kulisito Azaal'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni',
    +    'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi',
    +    'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba',
    +    'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb',
    +    'Oki', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul',
    +    'Agu', 'Seb', 'Oki', 'Nov', 'Des'],
    +  WEEKDAYS: ['Sabbiiti', 'Balaza', 'Lwakubiri', 'Lwakusatu', 'Lwakuna',
    +    'Lwakutaano', 'Lwamukaaga'],
    +  STANDALONEWEEKDAYS: ['Sabbiiti', 'Balaza', 'Lwakubiri', 'Lwakusatu',
    +    'Lwakuna', 'Lwakutaano', 'Lwamukaaga'],
    +  SHORTWEEKDAYS: ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'],
    +  STANDALONESHORTWEEKDAYS: ['Sab', 'Bal', 'Lw2', 'Lw3', 'Lw4', 'Lw5', 'Lw6'],
    +  NARROWWEEKDAYS: ['S', 'B', 'L', 'L', 'L', 'L', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'B', 'L', 'L', 'L', 'L', 'L'],
    +  SHORTQUARTERS: ['Kya1', 'Kya2', 'Kya3', 'Kya4'],
    +  QUARTERS: ['Kyakuna 1', 'Kyakuna 2', 'Kyakuna 3', 'Kyakuna 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lg_UG.
    + */
    +goog.i18n.DateTimeSymbols_lg_UG = goog.i18n.DateTimeSymbols_lg;
    +
    +
    +/**
    + * Date/time formatting symbols for locale lkt.
    + */
    +goog.i18n.DateTimeSymbols_lkt = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Wiótheȟika Wí', 'Thiyóȟeyuŋka Wí', 'Ištáwičhayazaŋ Wí',
    +    'Pȟežítȟo Wí', 'Čhaŋwápetȟo Wí', 'Wípazukȟa-wašté Wí',
    +    'Čhaŋpȟásapa Wí', 'Wasútȟuŋ Wí', 'Čhaŋwápeǧi Wí',
    +    'Čhaŋwápe-kasná Wí', 'Waníyetu Wí', 'Tȟahékapšuŋ Wí'],
    +  STANDALONEMONTHS: ['Wiótheȟika Wí', 'Thiyóȟeyuŋka Wí',
    +    'Ištáwičhayazaŋ Wí', 'Pȟežítȟo Wí', 'Čhaŋwápetȟo Wí',
    +    'Wípazukȟa-wašté Wí', 'Čhaŋpȟásapa Wí', 'Wasútȟuŋ Wí',
    +    'Čhaŋwápeǧi Wí', 'Čhaŋwápe-kasná Wí', 'Waníyetu Wí',
    +    'Tȟahékapšuŋ Wí'],
    +  SHORTMONTHS: ['Wiótheȟika Wí', 'Thiyóȟeyuŋka Wí',
    +    'Ištáwičhayazaŋ Wí', 'Pȟežítȟo Wí', 'Čhaŋwápetȟo Wí',
    +    'Wípazukȟa-wašté Wí', 'Čhaŋpȟásapa Wí', 'Wasútȟuŋ Wí',
    +    'Čhaŋwápeǧi Wí', 'Čhaŋwápe-kasná Wí', 'Waníyetu Wí',
    +    'Tȟahékapšuŋ Wí'],
    +  STANDALONESHORTMONTHS: ['Wiótheȟika Wí', 'Thiyóȟeyuŋka Wí',
    +    'Ištáwičhayazaŋ Wí', 'Pȟežítȟo Wí', 'Čhaŋwápetȟo Wí',
    +    'Wípazukȟa-wašté Wí', 'Čhaŋpȟásapa Wí', 'Wasútȟuŋ Wí',
    +    'Čhaŋwápeǧi Wí', 'Čhaŋwápe-kasná Wí', 'Waníyetu Wí',
    +    'Tȟahékapšuŋ Wí'],
    +  WEEKDAYS: ['Aŋpétuwakȟaŋ', 'Aŋpétuwaŋži', 'Aŋpétunuŋpa',
    +    'Aŋpétuyamni', 'Aŋpétutopa', 'Aŋpétuzaptaŋ', 'Owáŋgyužažapi'],
    +  STANDALONEWEEKDAYS: ['Aŋpétuwakȟaŋ', 'Aŋpétuwaŋži', 'Aŋpétunuŋpa',
    +    'Aŋpétuyamni', 'Aŋpétutopa', 'Aŋpétuzaptaŋ', 'Owáŋgyužažapi'],
    +  SHORTWEEKDAYS: ['Aŋpétuwakȟaŋ', 'Aŋpétuwaŋži', 'Aŋpétunuŋpa',
    +    'Aŋpétuyamni', 'Aŋpétutopa', 'Aŋpétuzaptaŋ', 'Owáŋgyužažapi'],
    +  STANDALONESHORTWEEKDAYS: ['Aŋpétuwakȟaŋ', 'Aŋpétuwaŋži',
    +    'Aŋpétunuŋpa', 'Aŋpétuyamni', 'Aŋpétutopa', 'Aŋpétuzaptaŋ',
    +    'Owáŋgyužažapi'],
    +  NARROWWEEKDAYS: ['A', 'W', 'N', 'Y', 'T', 'Z', 'O'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lkt_US.
    + */
    +goog.i18n.DateTimeSymbols_lkt_US = goog.i18n.DateTimeSymbols_lkt;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ln_AO.
    + */
    +goog.i18n.DateTimeSymbols_ln_AO = {
    +  ERAS: ['libóso ya', 'nsima ya Y'],
    +  ERANAMES: ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
    +  NARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ',
    +    'n', 'd'],
    +  MONTHS: ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto',
    +    'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá',
    +    'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa',
    +    'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  STANDALONEMONTHS: ['sánzá ya yambo', 'sánzá ya míbalé',
    +    'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno',
    +    'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe',
    +    'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  SHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb',
    +    'ɔtb', 'nvb', 'dsb'],
    +  STANDALONESHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul',
    +    'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
    +  WEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  STANDALONEWEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  SHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  STANDALONESHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  NARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  SHORTQUARTERS: ['SM1', 'SM2', 'SM3', 'SM4'],
    +  QUARTERS: ['sánzá mísáto ya yambo', 'sánzá mísáto ya míbalé',
    +    'sánzá mísáto ya mísáto', 'sánzá mísáto ya mínei'],
    +  AMPMS: ['ntɔ́ngɔ́', 'mpókwa'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ln_CD.
    + */
    +goog.i18n.DateTimeSymbols_ln_CD = {
    +  ERAS: ['libóso ya', 'nsima ya Y'],
    +  ERANAMES: ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
    +  NARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ',
    +    'n', 'd'],
    +  MONTHS: ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto',
    +    'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá',
    +    'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa',
    +    'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  STANDALONEMONTHS: ['sánzá ya yambo', 'sánzá ya míbalé',
    +    'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno',
    +    'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe',
    +    'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  SHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb',
    +    'ɔtb', 'nvb', 'dsb'],
    +  STANDALONESHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul',
    +    'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
    +  WEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  STANDALONEWEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  SHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  STANDALONESHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  NARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  SHORTQUARTERS: ['SM1', 'SM2', 'SM3', 'SM4'],
    +  QUARTERS: ['sánzá mísáto ya yambo', 'sánzá mísáto ya míbalé',
    +    'sánzá mísáto ya mísáto', 'sánzá mísáto ya mínei'],
    +  AMPMS: ['ntɔ́ngɔ́', 'mpókwa'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ln_CF.
    + */
    +goog.i18n.DateTimeSymbols_ln_CF = {
    +  ERAS: ['libóso ya', 'nsima ya Y'],
    +  ERANAMES: ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
    +  NARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ',
    +    'n', 'd'],
    +  MONTHS: ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto',
    +    'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá',
    +    'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa',
    +    'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  STANDALONEMONTHS: ['sánzá ya yambo', 'sánzá ya míbalé',
    +    'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno',
    +    'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe',
    +    'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  SHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb',
    +    'ɔtb', 'nvb', 'dsb'],
    +  STANDALONESHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul',
    +    'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
    +  WEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  STANDALONEWEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  SHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  STANDALONESHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  NARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  SHORTQUARTERS: ['SM1', 'SM2', 'SM3', 'SM4'],
    +  QUARTERS: ['sánzá mísáto ya yambo', 'sánzá mísáto ya míbalé',
    +    'sánzá mísáto ya mísáto', 'sánzá mísáto ya mínei'],
    +  AMPMS: ['ntɔ́ngɔ́', 'mpókwa'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ln_CG.
    + */
    +goog.i18n.DateTimeSymbols_ln_CG = {
    +  ERAS: ['libóso ya', 'nsima ya Y'],
    +  ERANAMES: ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
    +  NARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['y', 'f', 'm', 'a', 'm', 'y', 'y', 'a', 's', 'ɔ',
    +    'n', 'd'],
    +  MONTHS: ['sánzá ya yambo', 'sánzá ya míbalé', 'sánzá ya mísáto',
    +    'sánzá ya mínei', 'sánzá ya mítáno', 'sánzá ya motóbá',
    +    'sánzá ya nsambo', 'sánzá ya mwambe', 'sánzá ya libwa',
    +    'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  STANDALONEMONTHS: ['sánzá ya yambo', 'sánzá ya míbalé',
    +    'sánzá ya mísáto', 'sánzá ya mínei', 'sánzá ya mítáno',
    +    'sánzá ya motóbá', 'sánzá ya nsambo', 'sánzá ya mwambe',
    +    'sánzá ya libwa', 'sánzá ya zómi', 'sánzá ya zómi na mɔ̌kɔ́',
    +    'sánzá ya zómi na míbalé'],
    +  SHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul', 'agt', 'stb',
    +    'ɔtb', 'nvb', 'dsb'],
    +  STANDALONESHORTMONTHS: ['yan', 'fbl', 'msi', 'apl', 'mai', 'yun', 'yul',
    +    'agt', 'stb', 'ɔtb', 'nvb', 'dsb'],
    +  WEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  STANDALONEWEEKDAYS: ['eyenga', 'mokɔlɔ mwa yambo', 'mokɔlɔ mwa míbalé',
    +    'mokɔlɔ mwa mísáto', 'mokɔlɔ ya mínéi', 'mokɔlɔ ya mítáno',
    +    'mpɔ́sɔ'],
    +  SHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  STANDALONESHORTWEEKDAYS: ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
    +  NARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  STANDALONENARROWWEEKDAYS: ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
    +  SHORTQUARTERS: ['SM1', 'SM2', 'SM3', 'SM4'],
    +  QUARTERS: ['sánzá mísáto ya yambo', 'sánzá mísáto ya míbalé',
    +    'sánzá mísáto ya mísáto', 'sánzá mísáto ya mínei'],
    +  AMPMS: ['ntɔ́ngɔ́', 'mpókwa'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lo_LA.
    + */
    +goog.i18n.DateTimeSymbols_lo_LA = {
    +  ERAS: ['ກ່ອນ ຄ.ສ.', 'ຄ.ສ.'],
    +  ERANAMES: ['ກ່ອນຄຣິດສັກກະລາດ',
    +    'ຄຣິດສັກກະລາດ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ',
    +    'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ',
    +    'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ',
    +    'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
    +  STANDALONEMONTHS: ['ມັງກອນ', 'ກຸມພາ', 'ມີນາ',
    +    'ເມສາ', 'ພຶດສະພາ', 'ມິຖຸນາ',
    +    'ກໍລະກົດ', 'ສິງຫາ', 'ກັນຍາ',
    +    'ຕຸລາ', 'ພະຈິກ', 'ທັນວາ'],
    +  SHORTMONTHS: ['ມ.ກ.', 'ກ.ພ.', 'ມ.ນ.', 'ມ.ສ.', 'ພ.ພ.',
    +    'ມິ.ຖ.', 'ກ.ລ.', 'ສ.ຫ.', 'ກ.ຍ.', 'ຕ.ລ.', 'ພ.ຈ.',
    +    'ທ.ວ.'],
    +  STANDALONESHORTMONTHS: ['ມ.ກ.', 'ກ.ພ.', 'ມ.ນ.', 'ມ.ສ.',
    +    'ພ.ພ.', 'ມິ.ຖ.', 'ກ.ລ.', 'ສ.ຫ.', 'ກ.ຍ.', 'ຕ.ລ.',
    +    'ພ.ຈ.', 'ທ.ວ.'],
    +  WEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  STANDALONEWEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  SHORTWEEKDAYS: ['ວັນອາທິດ', 'ວັນຈັນ',
    +    'ວັນອັງຄານ', 'ວັນພຸດ',
    +    'ວັນພະຫັດ', 'ວັນສຸກ', 'ວັນເສົາ'],
    +  STANDALONESHORTWEEKDAYS: ['ອາທິດ', 'ຈັນ',
    +    'ອັງຄານ', 'ພຸດ', 'ພະຫັດ', 'ສຸກ',
    +    'ເສົາ'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['ອ', 'ຈ', 'ອ', 'ພ', 'ພຫ', 'ສຸ',
    +    'ສ'],
    +  SHORTQUARTERS: ['ຕມ1', 'ຕມ2', 'ຕມ3', 'ຕມ4'],
    +  QUARTERS: ['ໄຕຣມາດ 1', 'ໄຕຣມາດ 2',
    +    'ໄຕຣມາດ 3', 'ໄຕຣມາດ 4'],
    +  AMPMS: ['ກ່ອນທ່ຽງ', 'ຫຼັງທ່ຽງ'],
    +  DATEFORMATS: ['EEEE ທີ d MMMM G y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['H ໂມງ m ນາທີ ss ວິນາທີ zzzz',
    +    'H ໂມງ m ນາທີ ss ວິນາທີ z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lt_LT.
    + */
    +goog.i18n.DateTimeSymbols_lt_LT = {
    +  ERAS: ['pr. Kr.', 'po Kr.'],
    +  ERANAMES: ['prieš Kristų', 'po Kristaus'],
    +  NARROWMONTHS: ['S', 'V', 'K', 'B', 'G', 'B', 'L', 'R', 'R', 'S', 'L', 'G'],
    +  STANDALONENARROWMONTHS: ['S', 'V', 'K', 'B', 'G', 'B', 'L', 'R', 'R', 'S',
    +    'L', 'G'],
    +  MONTHS: ['sausio', 'vasario', 'kovo', 'balandžio', 'gegužės', 'birželio',
    +    'liepos', 'rugpjūčio', 'rugsėjo', 'spalio', 'lapkričio', 'gruodžio'],
    +  STANDALONEMONTHS: ['sausis', 'vasaris', 'kovas', 'balandis', 'gegužė',
    +    'birželis', 'liepa', 'rugpjūtis', 'rugsėjis', 'spalis', 'lapkritis',
    +    'gruodis'],
    +  SHORTMONTHS: ['saus.', 'vas.', 'kov.', 'bal.', 'geg.', 'birž.', 'liep.',
    +    'rugp.', 'rugs.', 'spal.', 'lapkr.', 'gruod.'],
    +  STANDALONESHORTMONTHS: ['saus.', 'vas.', 'kov.', 'bal.', 'geg.', 'birž.',
    +    'liep.', 'rugp.', 'rugs.', 'spal.', 'lapkr.', 'gruod.'],
    +  WEEKDAYS: ['sekmadienis', 'pirmadienis', 'antradienis', 'trečiadienis',
    +    'ketvirtadienis', 'penktadienis', 'šeštadienis'],
    +  STANDALONEWEEKDAYS: ['sekmadienis', 'pirmadienis', 'antradienis',
    +    'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis'],
    +  SHORTWEEKDAYS: ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
    +  STANDALONESHORTWEEKDAYS: ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
    +  NARROWWEEKDAYS: ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
    +  SHORTQUARTERS: ['I k.', 'II k.', 'III k.', 'IV k.'],
    +  QUARTERS: ['I ketvirtis', 'II ketvirtis', 'III ketvirtis', 'IV ketvirtis'],
    +  AMPMS: ['priešpiet', 'popiet'],
    +  DATEFORMATS: ['y \'m\'. MMMM d \'d\'., EEEE', 'y \'m\'. MMMM d \'d\'.',
    +    'y-MM-dd', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lu.
    + */
    +goog.i18n.DateTimeSymbols_lu = {
    +  ERAS: ['kmp. Y.K.', 'kny. Y. K.'],
    +  ERANAMES: ['Kumpala kwa Yezu Kli', 'Kunyima kwa Yezu Kli'],
    +  NARROWMONTHS: ['C', 'L', 'L', 'M', 'L', 'L', 'K', 'L', 'L', 'L', 'K', 'C'],
    +  STANDALONENARROWMONTHS: ['C', 'L', 'L', 'M', 'L', 'L', 'K', 'L', 'L', 'L',
    +    'K', 'C'],
    +  MONTHS: ['Ciongo', 'Lùishi', 'Lusòlo', 'Mùuyà', 'Lumùngùlù', 'Lufuimi',
    +    'Kabàlàshìpù', 'Lùshìkà', 'Lutongolo', 'Lungùdi', 'Kaswèkèsè',
    +    'Ciswà'],
    +  STANDALONEMONTHS: ['Ciongo', 'Lùishi', 'Lusòlo', 'Mùuyà', 'Lumùngùlù',
    +    'Lufuimi', 'Kabàlàshìpù', 'Lùshìkà', 'Lutongolo', 'Lungùdi',
    +    'Kaswèkèsè', 'Ciswà'],
    +  SHORTMONTHS: ['Cio', 'Lui', 'Lus', 'Muu', 'Lum', 'Luf', 'Kab', 'Lush', 'Lut',
    +    'Lun', 'Kas', 'Cis'],
    +  STANDALONESHORTMONTHS: ['Cio', 'Lui', 'Lus', 'Muu', 'Lum', 'Luf', 'Kab',
    +    'Lush', 'Lut', 'Lun', 'Kas', 'Cis'],
    +  WEEKDAYS: ['Lumingu', 'Nkodya', 'Ndàayà', 'Ndangù', 'Njòwa', 'Ngòvya',
    +    'Lubingu'],
    +  STANDALONEWEEKDAYS: ['Lumingu', 'Nkodya', 'Ndàayà', 'Ndangù', 'Njòwa',
    +    'Ngòvya', 'Lubingu'],
    +  SHORTWEEKDAYS: ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'],
    +  STANDALONESHORTWEEKDAYS: ['Lum', 'Nko', 'Ndy', 'Ndg', 'Njw', 'Ngv', 'Lub'],
    +  NARROWWEEKDAYS: ['L', 'N', 'N', 'N', 'N', 'N', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['L', 'N', 'N', 'N', 'N', 'N', 'L'],
    +  SHORTQUARTERS: ['M1', 'M2', 'M3', 'M4'],
    +  QUARTERS: ['Mueji 1', 'Mueji 2', 'Mueji 3', 'Mueji 4'],
    +  AMPMS: ['Dinda', 'Dilolo'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale lu_CD.
    + */
    +goog.i18n.DateTimeSymbols_lu_CD = goog.i18n.DateTimeSymbols_lu;
    +
    +
    +/**
    + * Date/time formatting symbols for locale luo.
    + */
    +goog.i18n.DateTimeSymbols_luo = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kapok Kristo obiro', 'Ka Kristo osebiro'],
    +  NARROWMONTHS: ['C', 'R', 'D', 'N', 'B', 'U', 'B', 'B', 'C', 'P', 'C', 'P'],
    +  STANDALONENARROWMONTHS: ['C', 'R', 'D', 'N', 'B', 'U', 'B', 'B', 'C', 'P',
    +    'C', 'P'],
    +  MONTHS: ['Dwe mar Achiel', 'Dwe mar Ariyo', 'Dwe mar Adek',
    +    'Dwe mar Ang’wen', 'Dwe mar Abich', 'Dwe mar Auchiel', 'Dwe mar Abiriyo',
    +    'Dwe mar Aboro', 'Dwe mar Ochiko', 'Dwe mar Apar', 'Dwe mar gi achiel',
    +    'Dwe mar Apar gi ariyo'],
    +  STANDALONEMONTHS: ['Dwe mar Achiel', 'Dwe mar Ariyo', 'Dwe mar Adek',
    +    'Dwe mar Ang’wen', 'Dwe mar Abich', 'Dwe mar Auchiel', 'Dwe mar Abiriyo',
    +    'Dwe mar Aboro', 'Dwe mar Ochiko', 'Dwe mar Apar', 'Dwe mar gi achiel',
    +    'Dwe mar Apar gi ariyo'],
    +  SHORTMONTHS: ['DAC', 'DAR', 'DAD', 'DAN', 'DAH', 'DAU', 'DAO', 'DAB', 'DOC',
    +    'DAP', 'DGI', 'DAG'],
    +  STANDALONESHORTMONTHS: ['DAC', 'DAR', 'DAD', 'DAN', 'DAH', 'DAU', 'DAO',
    +    'DAB', 'DOC', 'DAP', 'DGI', 'DAG'],
    +  WEEKDAYS: ['Jumapil', 'Wuok Tich', 'Tich Ariyo', 'Tich Adek',
    +    'Tich Ang’wen', 'Tich Abich', 'Ngeso'],
    +  STANDALONEWEEKDAYS: ['Jumapil', 'Wuok Tich', 'Tich Ariyo', 'Tich Adek',
    +    'Tich Ang’wen', 'Tich Abich', 'Ngeso'],
    +  SHORTWEEKDAYS: ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'],
    +  STANDALONESHORTWEEKDAYS: ['JMP', 'WUT', 'TAR', 'TAD', 'TAN', 'TAB', 'NGS'],
    +  NARROWWEEKDAYS: ['J', 'W', 'T', 'T', 'T', 'T', 'N'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'W', 'T', 'T', 'T', 'T', 'N'],
    +  SHORTQUARTERS: ['NMN1', 'NMN2', 'NMN3', 'NMN4'],
    +  QUARTERS: ['nus mar nus 1', 'nus mar nus 2', 'nus mar nus 3',
    +    'nus mar nus 4'],
    +  AMPMS: ['OD', 'OT'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale luo_KE.
    + */
    +goog.i18n.DateTimeSymbols_luo_KE = goog.i18n.DateTimeSymbols_luo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale luy.
    + */
    +goog.i18n.DateTimeSymbols_luy = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Imberi ya Kuuza Kwa', 'Muhiga Kuvita Kuuza'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapiri', 'Jumatatu', 'Jumanne', 'Jumatano', 'Murwa wa Kanne',
    +    'Murwa wa Katano', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapiri', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Murwa wa Kanne', 'Murwa wa Katano', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'],
    +  STANDALONESHORTWEEKDAYS: ['J2', 'J3', 'J4', 'J5', 'Al', 'Ij', 'J1'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Robo ya Kala', 'Robo ya Kaviri', 'Robo ya Kavaga',
    +    'Robo ya Kanne'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale luy_KE.
    + */
    +goog.i18n.DateTimeSymbols_luy_KE = goog.i18n.DateTimeSymbols_luy;
    +
    +
    +/**
    + * Date/time formatting symbols for locale lv_LV.
    + */
    +goog.i18n.DateTimeSymbols_lv_LV = {
    +  ERAS: ['p.m.ē.', 'm.ē.'],
    +  ERANAMES: ['pirms mūsu ēras', 'mūsu ērā'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janvāris', 'februāris', 'marts', 'aprīlis', 'maijs', 'jūnijs',
    +    'jūlijs', 'augusts', 'septembris', 'oktobris', 'novembris', 'decembris'],
    +  STANDALONEMONTHS: ['Janvāris', 'Februāris', 'Marts', 'Aprīlis', 'Maijs',
    +    'Jūnijs', 'Jūlijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris',
    +    'Decembris'],
    +  SHORTMONTHS: ['janv.', 'febr.', 'marts', 'apr.', 'maijs', 'jūn.', 'jūl.',
    +    'aug.', 'sept.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Janv.', 'Febr.', 'Marts', 'Apr.', 'Maijs', 'Jūn.',
    +    'Jūl.', 'Aug.', 'Sept.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['svētdiena', 'pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena',
    +    'piektdiena', 'sestdiena'],
    +  STANDALONEWEEKDAYS: ['Svētdiena', 'Pirmdiena', 'Otrdiena', 'Trešdiena',
    +    'Ceturtdiena', 'Piektdiena', 'Sestdiena'],
    +  SHORTWEEKDAYS: ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
    +  STANDALONESHORTWEEKDAYS: ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
    +  NARROWWEEKDAYS: ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
    +  SHORTQUARTERS: ['1. cet.', '2. cet.', '3. cet.', '4. cet.'],
    +  QUARTERS: ['1. ceturksnis', '2. ceturksnis', '3. ceturksnis',
    +    '4. ceturksnis'],
    +  AMPMS: ['priekšpusdienā', 'pēcpusdienā'],
    +  DATEFORMATS: ['EEEE, y. \'gada\' d. MMMM', 'y. \'gada\' d. MMMM',
    +    'y. \'gada\' d. MMM', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mas.
    + */
    +goog.i18n.DateTimeSymbols_mas = {
    +  ERAS: ['MY', 'EY'],
    +  ERANAMES: ['Meínō Yɛ́sʉ', 'Eínō Yɛ́sʉ'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Oladalʉ́', 'Arát', 'Ɔɛnɨ́ɔɨŋɔk',
    +    'Olodoyíóríê inkókúâ', 'Oloilépūnyīē inkókúâ', 'Kújúɔrɔk',
    +    'Mórusásin', 'Ɔlɔ́ɨ́bɔ́rárɛ', 'Kúshîn', 'Olgísan',
    +    'Pʉshʉ́ka', 'Ntʉ́ŋʉ́s'],
    +  STANDALONEMONTHS: ['Oladalʉ́', 'Arát', 'Ɔɛnɨ́ɔɨŋɔk',
    +    'Olodoyíóríê inkókúâ', 'Oloilépūnyīē inkókúâ', 'Kújúɔrɔk',
    +    'Mórusásin', 'Ɔlɔ́ɨ́bɔ́rárɛ', 'Kúshîn', 'Olgísan',
    +    'Pʉshʉ́ka', 'Ntʉ́ŋʉ́s'],
    +  SHORTMONTHS: ['Dal', 'Ará', 'Ɔɛn', 'Doy', 'Lép', 'Rok', 'Sás', 'Bɔ́r',
    +    'Kús', 'Gís', 'Shʉ́', 'Ntʉ́'],
    +  STANDALONESHORTMONTHS: ['Dal', 'Ará', 'Ɔɛn', 'Doy', 'Lép', 'Rok', 'Sás',
    +    'Bɔ́r', 'Kús', 'Gís', 'Shʉ́', 'Ntʉ́'],
    +  WEEKDAYS: ['Jumapílí', 'Jumatátu', 'Jumane', 'Jumatánɔ', 'Alaámisi',
    +    'Jumáa', 'Jumamósi'],
    +  STANDALONEWEEKDAYS: ['Jumapílí', 'Jumatátu', 'Jumane', 'Jumatánɔ',
    +    'Alaámisi', 'Jumáa', 'Jumamósi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  STANDALONENARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  SHORTQUARTERS: ['E1', 'E2', 'E3', 'E4'],
    +  QUARTERS: ['Erobo 1', 'Erobo 2', 'Erobo 3', 'Erobo 4'],
    +  AMPMS: ['Ɛnkakɛnyá', 'Ɛndámâ'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mas_KE.
    + */
    +goog.i18n.DateTimeSymbols_mas_KE = goog.i18n.DateTimeSymbols_mas;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mas_TZ.
    + */
    +goog.i18n.DateTimeSymbols_mas_TZ = goog.i18n.DateTimeSymbols_mas;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mer.
    + */
    +goog.i18n.DateTimeSymbols_mer = {
    +  ERAS: ['MK', 'NK'],
    +  ERANAMES: ['Mbere ya Kristũ', 'Nyuma ya Kristũ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'Ĩ', 'M', 'N', 'N', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'Ĩ', 'M', 'N', 'N', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januarĩ', 'Feburuarĩ', 'Machi', 'Ĩpurũ', 'Mĩĩ', 'Njuni',
    +    'Njuraĩ', 'Agasti', 'Septemba', 'Oktũba', 'Novemba', 'Dicemba'],
    +  STANDALONEMONTHS: ['Januarĩ', 'Feburuarĩ', 'Machi', 'Ĩpurũ', 'Mĩĩ',
    +    'Njuni', 'Njuraĩ', 'Agasti', 'Septemba', 'Oktũba', 'Novemba', 'Dicemba'],
    +  SHORTMONTHS: ['JAN', 'FEB', 'MAC', 'ĨPU', 'MĨĨ', 'NJU', 'NJR', 'AGA',
    +    'SPT', 'OKT', 'NOV', 'DEC'],
    +  STANDALONESHORTMONTHS: ['JAN', 'FEB', 'MAC', 'ĨPU', 'MĨĨ', 'NJU', 'NJR',
    +    'AGA', 'SPT', 'OKT', 'NOV', 'DEC'],
    +  WEEKDAYS: ['Kiumia', 'Muramuko', 'Wairi', 'Wethatu', 'Wena', 'Wetano',
    +    'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Kiumia', 'Muramuko', 'Wairi', 'Wethatu', 'Wena',
    +    'Wetano', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'],
    +  STANDALONESHORTWEEKDAYS: ['KIU', 'MRA', 'WAI', 'WET', 'WEN', 'WTN', 'JUM'],
    +  NARROWWEEKDAYS: ['K', 'M', 'W', 'W', 'W', 'W', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'M', 'W', 'W', 'W', 'W', 'J'],
    +  SHORTQUARTERS: ['Ĩmwe kĩrĩ inya', 'Ijĩrĩ kĩrĩ inya',
    +    'Ithatũ kĩrĩ inya', 'Inya kĩrĩ inya'],
    +  QUARTERS: ['Ĩmwe kĩrĩ inya', 'Ijĩrĩ kĩrĩ inya', 'Ithatũ kĩrĩ inya',
    +    'Inya kĩrĩ inya'],
    +  AMPMS: ['RŨ', 'ŨG'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mer_KE.
    + */
    +goog.i18n.DateTimeSymbols_mer_KE = goog.i18n.DateTimeSymbols_mer;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mfe.
    + */
    +goog.i18n.DateTimeSymbols_mfe = {
    +  ERAS: ['av. Z-K', 'ap. Z-K'],
    +  ERANAMES: ['avan Zezi-Krist', 'apre Zezi-Krist'],
    +  NARROWMONTHS: ['z', 'f', 'm', 'a', 'm', 'z', 'z', 'o', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['z', 'f', 'm', 'a', 'm', 'z', 'z', 'o', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['zanvie', 'fevriye', 'mars', 'avril', 'me', 'zin', 'zilye', 'out',
    +    'septam', 'oktob', 'novam', 'desam'],
    +  STANDALONEMONTHS: ['zanvie', 'fevriye', 'mars', 'avril', 'me', 'zin', 'zilye',
    +    'out', 'septam', 'oktob', 'novam', 'desam'],
    +  SHORTMONTHS: ['zan', 'fev', 'mar', 'avr', 'me', 'zin', 'zil', 'out', 'sep',
    +    'okt', 'nov', 'des'],
    +  STANDALONESHORTMONTHS: ['zan', 'fev', 'mar', 'avr', 'me', 'zin', 'zil', 'out',
    +    'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['dimans', 'lindi', 'mardi', 'merkredi', 'zedi', 'vandredi',
    +    'samdi'],
    +  STANDALONEWEEKDAYS: ['dimans', 'lindi', 'mardi', 'merkredi', 'zedi',
    +    'vandredi', 'samdi'],
    +  SHORTWEEKDAYS: ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'],
    +  STANDALONESHORTWEEKDAYS: ['dim', 'lin', 'mar', 'mer', 'ze', 'van', 'sam'],
    +  NARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'z', 'v', 's'],
    +  STANDALONENARROWWEEKDAYS: ['d', 'l', 'm', 'm', 'z', 'v', 's'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1e trimes', '2em trimes', '3em trimes', '4em trimes'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mfe_MU.
    + */
    +goog.i18n.DateTimeSymbols_mfe_MU = goog.i18n.DateTimeSymbols_mfe;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mg.
    + */
    +goog.i18n.DateTimeSymbols_mg = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Alohan’i JK', 'Aorian’i JK'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janoary', 'Febroary', 'Martsa', 'Aprily', 'Mey', 'Jona', 'Jolay',
    +    'Aogositra', 'Septambra', 'Oktobra', 'Novambra', 'Desambra'],
    +  STANDALONEMONTHS: ['Janoary', 'Febroary', 'Martsa', 'Aprily', 'Mey', 'Jona',
    +    'Jolay', 'Aogositra', 'Septambra', 'Oktobra', 'Novambra', 'Desambra'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mey', 'Jon', 'Jol', 'Aog', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'Mey', 'Jon', 'Jol',
    +    'Aog', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Alahady', 'Alatsinainy', 'Talata', 'Alarobia', 'Alakamisy',
    +    'Zoma', 'Asabotsy'],
    +  STANDALONEWEEKDAYS: ['Alahady', 'Alatsinainy', 'Talata', 'Alarobia',
    +    'Alakamisy', 'Zoma', 'Asabotsy'],
    +  SHORTWEEKDAYS: ['Alah', 'Alats', 'Tal', 'Alar', 'Alak', 'Zom', 'Asab'],
    +  STANDALONESHORTWEEKDAYS: ['Alah', 'Alats', 'Tal', 'Alar', 'Alak', 'Zom',
    +    'Asab'],
    +  NARROWWEEKDAYS: ['A', 'A', 'T', 'A', 'A', 'Z', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'A', 'T', 'A', 'A', 'Z', 'A'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['Telovolana voalohany', 'Telovolana faharoa',
    +    'Telovolana fahatelo', 'Telovolana fahefatra'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mg_MG.
    + */
    +goog.i18n.DateTimeSymbols_mg_MG = goog.i18n.DateTimeSymbols_mg;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mgh.
    + */
    +goog.i18n.DateTimeSymbols_mgh = {
    +  ERAS: ['HY', 'YY'],
    +  ERANAMES: ['Hinapiya yesu', 'Yopia yesu'],
    +  NARROWMONTHS: ['K', 'U', 'R', 'C', 'T', 'M', 'S', 'N', 'T', 'K', 'M', 'Y'],
    +  STANDALONENARROWMONTHS: ['K', 'U', 'R', 'C', 'T', 'M', 'S', 'N', 'T', 'K',
    +    'M', 'Y'],
    +  MONTHS: ['Mweri wo kwanza', 'Mweri wo unayeli', 'Mweri wo uneraru',
    +    'Mweri wo unecheshe', 'Mweri wo unethanu', 'Mweri wo thanu na mocha',
    +    'Mweri wo saba', 'Mweri wo nane', 'Mweri wo tisa', 'Mweri wo kumi',
    +    'Mweri wo kumi na moja', 'Mweri wo kumi na yel’li'],
    +  STANDALONEMONTHS: ['Mweri wo kwanza', 'Mweri wo unayeli', 'Mweri wo uneraru',
    +    'Mweri wo unecheshe', 'Mweri wo unethanu', 'Mweri wo thanu na mocha',
    +    'Mweri wo saba', 'Mweri wo nane', 'Mweri wo tisa', 'Mweri wo kumi',
    +    'Mweri wo kumi na moja', 'Mweri wo kumi na yel’li'],
    +  SHORTMONTHS: ['Kwa', 'Una', 'Rar', 'Che', 'Tha', 'Moc', 'Sab', 'Nan', 'Tis',
    +    'Kum', 'Moj', 'Yel'],
    +  STANDALONESHORTMONTHS: ['Kwa', 'Una', 'Rar', 'Che', 'Tha', 'Moc', 'Sab',
    +    'Nan', 'Tis', 'Kum', 'Moj', 'Yel'],
    +  WEEKDAYS: ['Sabato', 'Jumatatu', 'Jumanne', 'Jumatano', 'Arahamisi', 'Ijumaa',
    +    'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Sabato', 'Jumatatu', 'Jumanne', 'Jumatano', 'Arahamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Sab', 'Jtt', 'Jnn', 'Jtn', 'Ara', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['S', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['wichishu', 'mchochil’l'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mgh_MZ.
    + */
    +goog.i18n.DateTimeSymbols_mgh_MZ = goog.i18n.DateTimeSymbols_mgh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mgo.
    + */
    +goog.i18n.DateTimeSymbols_mgo = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['M1', 'A2', 'M3', 'N4', 'F5', 'I6', 'A7', 'I8', 'K9', '10',
    +    '11', '12'],
    +  STANDALONENARROWMONTHS: ['M1', 'A2', 'M3', 'N4', 'F5', 'I6', 'A7', 'I8', 'K9',
    +    '10', '11', '12'],
    +  MONTHS: ['iməg mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi',
    +    'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ',
    +    'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò',
    +    'iməg krizmed'],
    +  STANDALONEMONTHS: ['iməg mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi',
    +    'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ',
    +    'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò',
    +    'iməg krizmed'],
    +  SHORTMONTHS: ['mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi',
    +    'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ',
    +    'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò',
    +    'iməg krizmed'],
    +  STANDALONESHORTMONTHS: ['mbegtug', 'imeg àbùbì', 'imeg mbəŋchubi',
    +    'iməg ngwə̀t', 'iməg fog', 'iməg ichiibɔd', 'iməg àdùmbə̀ŋ',
    +    'iməg ichika', 'iməg kud', 'iməg tèsiʼe', 'iməg zò',
    +    'iməg krizmed'],
    +  WEEKDAYS: ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6',
    +    'Aneg 7'],
    +  STANDALONEWEEKDAYS: ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5',
    +    'Aneg 6', 'Aneg 7'],
    +  SHORTWEEKDAYS: ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5', 'Aneg 6',
    +    'Aneg 7'],
    +  STANDALONESHORTWEEKDAYS: ['Aneg 1', 'Aneg 2', 'Aneg 3', 'Aneg 4', 'Aneg 5',
    +    'Aneg 6', 'Aneg 7'],
    +  NARROWWEEKDAYS: ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7'],
    +  STANDALONENARROWWEEKDAYS: ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mgo_CM.
    + */
    +goog.i18n.DateTimeSymbols_mgo_CM = goog.i18n.DateTimeSymbols_mgo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mk_MK.
    + */
    +goog.i18n.DateTimeSymbols_mk_MK = {
    +  ERAS: ['пр.н.е.', 'н.е.'],
    +  ERANAMES: ['пред нашата ера', 'од нашата ера'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануари', 'февруари', 'март', 'април',
    +    'мај', 'јуни', 'јули', 'август', 'септември',
    +    'октомври', 'ноември', 'декември'],
    +  STANDALONEMONTHS: ['јануари', 'февруари', 'март',
    +    'април', 'мај', 'јуни', 'јули', 'август',
    +    'септември', 'октомври', 'ноември',
    +    'декември'],
    +  SHORTMONTHS: ['јан.', 'фев.', 'мар.', 'апр.', 'мај', 'јун.',
    +    'јул.', 'авг.', 'септ.', 'окт.', 'ноем.', 'дек.'],
    +  STANDALONESHORTMONTHS: ['јан.', 'фев.', 'мар.', 'апр.', 'мај',
    +    'јун.', 'јул.', 'авг.', 'септ.', 'окт.', 'ноем.',
    +    'дек.'],
    +  WEEKDAYS: ['недела', 'понеделник', 'вторник',
    +    'среда', 'четврток', 'петок', 'сабота'],
    +  STANDALONEWEEKDAYS: ['недела', 'понеделник', 'вторник',
    +    'среда', 'четврток', 'петок', 'сабота'],
    +  SHORTWEEKDAYS: ['нед.', 'пон.', 'вт.', 'сре.', 'чет.',
    +    'пет.', 'саб.'],
    +  STANDALONESHORTWEEKDAYS: ['нед.', 'пон.', 'вт.', 'сре.', 'чет.',
    +    'пет.', 'саб.'],
    +  NARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['јан-мар', 'апр-јун', 'јул-сеп',
    +    'окт-дек'],
    +  QUARTERS: ['прво тромесечје', 'второ тромесечје',
    +    'трето тромесечје', 'четврто тромесечје'],
    +  AMPMS: ['претпладне', 'попладне'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'dd MMMM y', 'dd.M.y', 'dd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ml_IN.
    + */
    +goog.i18n.DateTimeSymbols_ml_IN = {
    +  ERAS: ['ക്രി.മു.', 'എഡി'],
    +  ERANAMES: ['ക്രിസ്‌തുവിന് മുമ്പ്',
    +    'ആന്നോ ഡൊമിനി'],
    +  NARROWMONTHS: ['ജ', 'ഫ', 'മാ', 'ഏ', 'മെ', 'ജൂ', 'ജൂ',
    +    'ഓ', 'സ', 'ഒ', 'ന', 'ഡി'],
    +  STANDALONENARROWMONTHS: ['ജ', 'ഫ', 'മാ', 'ഏ', 'മേ', 'ജൂ',
    +    'ജൂ', 'ഓ', 'സ', 'ഒ', 'ന', 'ഡി'],
    +  MONTHS: ['ജനുവരി', 'ഫെബ്രുവരി',
    +    'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ',
    +    'ജൂലൈ', 'ആഗസ്റ്റ്',
    +    'സെപ്റ്റംബർ', 'ഒക്‌ടോബർ',
    +    'നവംബർ', 'ഡിസംബർ'],
    +  STANDALONEMONTHS: ['ജനുവരി', 'ഫെബ്രുവരി',
    +    'മാർച്ച്', 'ഏപ്രിൽ', 'മേയ്', 'ജൂൺ',
    +    'ജൂലൈ', 'ആഗസ്റ്റ്',
    +    'സെപ്റ്റംബർ', 'ഒക്‌ടോബർ',
    +    'നവംബർ', 'ഡിസംബർ'],
    +  SHORTMONTHS: ['ജനു', 'ഫെബ്രു', 'മാർ',
    +    'ഏപ്രി', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗ',
    +    'സെപ്റ്റം', 'ഒക്ടോ', 'നവം', 'ഡിസം'],
    +  STANDALONESHORTMONTHS: ['ജനു', 'ഫെബ്രു', 'മാർ',
    +    'ഏപ്രി', 'മേയ്', 'ജൂൺ', 'ജൂലൈ', 'ഓഗ',
    +    'സെപ്റ്റം', 'ഒക്ടോ', 'നവം', 'ഡിസം'],
    +  WEEKDAYS: ['ഞായറാഴ്‌ച', 'തിങ്കളാഴ്‌ച',
    +    'ചൊവ്വാഴ്ച', 'ബുധനാഴ്‌ച',
    +    'വ്യാഴാഴ്‌ച', 'വെള്ളിയാഴ്‌ച',
    +    'ശനിയാഴ്‌ച'],
    +  STANDALONEWEEKDAYS: ['ഞായറാഴ്‌ച',
    +    'തിങ്കളാഴ്‌ച', 'ചൊവ്വാഴ്‌ച',
    +    'ബുധനാഴ്‌ച', 'വ്യാഴാഴ്‌ച',
    +    'വെള്ളിയാഴ്‌ച', 'ശനിയാഴ്‌ച'],
    +  SHORTWEEKDAYS: ['ഞായർ', 'തിങ്കൾ', 'ചൊവ്വ',
    +    'ബുധൻ', 'വ്യാഴം', 'വെള്ളി', 'ശനി'],
    +  STANDALONESHORTWEEKDAYS: ['ഞായർ', 'തിങ്കൾ',
    +    'ചൊവ്വ', 'ബുധൻ', 'വ്യാഴം',
    +    'വെള്ളി', 'ശനി'],
    +  NARROWWEEKDAYS: ['ഞ', 'തി', 'ച', 'ബു', 'വ', 'വെ', 'ശ'],
    +  STANDALONENARROWWEEKDAYS: ['ഞ', 'തി', 'ച', 'ബു', 'വ', 'വെ',
    +    'ശ'],
    +  SHORTQUARTERS: ['ഒന്നാം പാദം',
    +    'രണ്ടാം പാദം', 'മൂന്നാം പാദം',
    +    'നാലാം പാദം'],
    +  QUARTERS: ['ഒന്നാം പാദം',
    +    'രണ്ടാം പാദം', 'മൂന്നാം പാദം',
    +    'നാലാം പാദം'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y, MMMM d, EEEE', 'y, MMMM d', 'y, MMM d', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mn_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_mn_Cyrl = {
    +  ERAS: ['МЭӨ', 'МЭ'],
    +  ERANAMES: ['манай эриний өмнөх', 'манай эриний'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Нэгдүгээр сар', 'Хоёрдугаар сар',
    +    'Гуравдугаар сар', 'Дөрөвдүгээр сар',
    +    'Тавдугаар сар', 'Зургадугаар сар',
    +    'Долдугаар сар', 'Наймдугаар сар',
    +    'Есдүгээр сар', 'Аравдугаар сар',
    +    'Арван нэгдүгээр сар',
    +    'Арван хоёрдугаар сар'],
    +  STANDALONEMONTHS: ['Нэгдүгээр сар', 'Хоёрдугаар сар',
    +    'Гуравдугаар сар', 'Дөрөвдүгээр сар',
    +    'Тавдугаар сар', 'Зургадугаар сар',
    +    'Долдугаар сар', 'Наймдугаар сар',
    +    'Есдүгээр сар', 'Аравдугаар сар',
    +    'Арван нэгдүгээр сар',
    +    'Арван хоёрдугаар сар'],
    +  SHORTMONTHS: ['1-р сар', '2-р сар', '3-р сар', '4-р сар',
    +    '5-р сар', '6-р сар', '7-р сар', '8-р сар', '9-р сар',
    +    '10-р сар', '11-р сар', '12-р сар'],
    +  STANDALONESHORTMONTHS: ['1-р сар', '2-р сар', '3-р сар',
    +    '4-р сар', '5-р сар', '6-р сар', '7-р сар', '8-р сар',
    +    '9-р сар', '10-р сар', '11-р сар', '12-р сар'],
    +  WEEKDAYS: ['ням', 'даваа', 'мягмар', 'лхагва',
    +    'пүрэв', 'баасан', 'бямба'],
    +  STANDALONEWEEKDAYS: ['ням', 'даваа', 'мягмар', 'лхагва',
    +    'пүрэв', 'баасан', 'бямба'],
    +  SHORTWEEKDAYS: ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
    +  STANDALONESHORTWEEKDAYS: ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба',
    +    'Бя'],
    +  NARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  STANDALONENARROWWEEKDAYS: ['1', '2', '3', '4', '5', '6', '7'],
    +  SHORTQUARTERS: ['У1', 'У2', 'У3', 'У4'],
    +  QUARTERS: ['1-р улирал', '2-р улирал', '3-р улирал',
    +    '4-р улирал'],
    +  AMPMS: ['ҮӨ', 'ҮХ'],
    +  DATEFORMATS: ['EEEE, y \'оны\' MM \'сарын\' d',
    +    'y \'оны\' MM \'сарын\' d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mn_Cyrl_MN.
    + */
    +goog.i18n.DateTimeSymbols_mn_Cyrl_MN = goog.i18n.DateTimeSymbols_mn_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mr_IN.
    + */
    +goog.i18n.DateTimeSymbols_mr_IN = {
    +  ZERODIGIT: 0x0966,
    +  ERAS: ['इ. स. पू.', 'इ. स.'],
    +  ERANAMES: ['ईसवीसनपूर्व', 'ईसवीसन'],
    +  NARROWMONTHS: ['जा', 'फे', 'मा', 'ए', 'मे', 'जू',
    +    'जु', 'ऑ', 'स', 'ऑ', 'नो', 'डि'],
    +  STANDALONENARROWMONTHS: ['जा', 'फे', 'मा', 'ए', 'मे',
    +    'जू', 'जु', 'ऑ', 'स', 'ऑ', 'नो', 'डि'],
    +  MONTHS: ['जानेवारी', 'फेब्रुवारी',
    +    'मार्च', 'एप्रिल', 'मे', 'जून',
    +    'जुलै', 'ऑगस्ट', 'सप्टेंबर',
    +    'ऑक्टोबर', 'नोव्हेंबर',
    +    'डिसेंबर'],
    +  STANDALONEMONTHS: ['जानेवारी',
    +    'फेब्रुवारी', 'मार्च', 'एप्रिल',
    +    'मे', 'जून', 'जुलै', 'ऑगस्ट',
    +    'सप्टेंबर', 'ऑक्टोबर',
    +    'नोव्हेंबर', 'डिसेंबर'],
    +  SHORTMONTHS: ['जाने', 'फेब्रु', 'मार्च',
    +    'एप्रि', 'मे', 'जून', 'जुलै', 'ऑग',
    +    'सप्टें', 'ऑक्टो', 'नोव्हें',
    +    'डिसें'],
    +  STANDALONESHORTMONTHS: ['जाने', 'फेब्रु',
    +    'मार्च', 'एप्रि', 'मे', 'जून', 'जुलै',
    +    'ऑग', 'सप्टें', 'ऑक्टो', 'नोव्हें',
    +    'डिसें'],
    +  WEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगळवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['रविवार', 'सोमवार',
    +    'मंगळवार', 'बुधवार', 'गुरुवार',
    +    'शुक्रवार', 'शनिवार'],
    +  SHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ', 'बुध',
    +    'गुरु', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['रवि', 'सोम', 'मंगळ',
    +    'बुध', 'गुरु', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु', 'शु',
    +    'श'],
    +  STANDALONENARROWWEEKDAYS: ['र', 'सो', 'मं', 'बु', 'गु',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['ति१', 'ति२', 'ति३', 'ति४'],
    +  QUARTERS: ['प्रथम तिमाही',
    +    'द्वितीय तिमाही',
    +    'तृतीय तिमाही',
    +    'चतुर्थ तिमाही'],
    +  AMPMS: ['म.पू.', 'म.उ.'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} \'रोजी\' {0}', '{1} \'रोजी\' {0}',
    +    '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ms_Latn.
    + */
    +goog.i18n.DateTimeSymbols_ms_Latn = {
    +  ERAS: ['S.M.', 'TM'],
    +  ERANAMES: ['S.M.', 'TM'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos',
    +    'September', 'Oktober', 'November', 'Disember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun',
    +    'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogo', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ogo', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['S1', 'S2', 'S3', 'S4'],
    +  QUARTERS: ['Suku pertama', 'Suku Ke-2', 'Suku Ke-3', 'Suku Ke-4'],
    +  AMPMS: ['PG', 'PTG'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ms_Latn_BN.
    + */
    +goog.i18n.DateTimeSymbols_ms_Latn_BN = {
    +  ERAS: ['S.M.', 'TM'],
    +  ERANAMES: ['S.M.', 'TM'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'O', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun', 'Julai', 'Ogos',
    +    'September', 'Oktober', 'November', 'Disember'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mac', 'April', 'Mei', 'Jun',
    +    'Julai', 'Ogos', 'September', 'Oktober', 'November', 'Disember'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogo', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ogo', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'],
    +  STANDALONEWEEKDAYS: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat',
    +    'Sabtu'],
    +  SHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['S1', 'S2', 'S3', 'S4'],
    +  QUARTERS: ['Suku pertama', 'Suku Ke-2', 'Suku Ke-3', 'Suku Ke-4'],
    +  AMPMS: ['PG', 'PTG'],
    +  DATEFORMATS: ['dd MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ms_Latn_MY.
    + */
    +goog.i18n.DateTimeSymbols_ms_Latn_MY = goog.i18n.DateTimeSymbols_ms_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ms_Latn_SG.
    + */
    +goog.i18n.DateTimeSymbols_ms_Latn_SG = goog.i18n.DateTimeSymbols_ms_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale mt_MT.
    + */
    +goog.i18n.DateTimeSymbols_mt_MT = {
    +  ERAS: ['QK', 'WK'],
    +  ERANAMES: ['Qabel Kristu', 'Wara Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'Ġ', 'L', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Jn', 'Fr', 'Mz', 'Ap', 'Mj', 'Ġn', 'Lj', 'Aw',
    +    'St', 'Ob', 'Nv', 'Dċ'],
    +  MONTHS: ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju', 'Lulju',
    +    'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'],
    +  STANDALONEMONTHS: ['Jannar', 'Frar', 'Marzu', 'April', 'Mejju', 'Ġunju',
    +    'Lulju', 'Awwissu', 'Settembru', 'Ottubru', 'Novembru', 'Diċembru'],
    +  SHORTMONTHS: ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul', 'Aww', 'Set',
    +    'Ott', 'Nov', 'Diċ'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fra', 'Mar', 'Apr', 'Mej', 'Ġun', 'Lul',
    +    'Aww', 'Set', 'Ott', 'Nov', 'Diċ'],
    +  WEEKDAYS: ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa', 'Il-Ħamis',
    +    'Il-Ġimgħa', 'Is-Sibt'],
    +  STANDALONEWEEKDAYS: ['Il-Ħadd', 'It-Tnejn', 'It-Tlieta', 'L-Erbgħa',
    +    'Il-Ħamis', 'Il-Ġimgħa', 'Is-Sibt'],
    +  SHORTWEEKDAYS: ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
    +  STANDALONESHORTWEEKDAYS: ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
    +  NARROWWEEKDAYS: ['Ħ', 'T', 'T', 'E', 'Ħ', 'Ġ', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['Ħd', 'Tn', 'Tl', 'Er', 'Ħm', 'Ġm', 'Sb'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1el kwart', '2ni kwart', '3et kwart', '4ba’ kwart'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d \'ta\'’ MMMM y', 'd \'ta\'’ MMMM y', 'dd MMM y',
    +    'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mua.
    + */
    +goog.i18n.DateTimeSymbols_mua = {
    +  ERAS: ['KK', 'PK'],
    +  ERANAMES: ['KǝPel Kristu', 'Pel Kristu'],
    +  NARROWMONTHS: ['O', 'A', 'I', 'F', 'D', 'B', 'L', 'M', 'E', 'U', 'W', 'Y'],
    +  STANDALONENARROWMONTHS: ['O', 'A', 'I', 'F', 'D', 'B', 'L', 'M', 'E', 'U',
    +    'W', 'Y'],
    +  MONTHS: ['Fĩi Loo', 'Cokcwaklaŋne', 'Cokcwaklii', 'Fĩi Marfoo',
    +    'Madǝǝuutǝbijaŋ', 'Mamǝŋgwãafahbii', 'Mamǝŋgwãalii', 'Madǝmbii',
    +    'Fĩi Dǝɓlii', 'Fĩi Mundaŋ', 'Fĩi Gwahlle', 'Fĩi Yuru'],
    +  STANDALONEMONTHS: ['Fĩi Loo', 'Cokcwaklaŋne', 'Cokcwaklii', 'Fĩi Marfoo',
    +    'Madǝǝuutǝbijaŋ', 'Mamǝŋgwãafahbii', 'Mamǝŋgwãalii', 'Madǝmbii',
    +    'Fĩi Dǝɓlii', 'Fĩi Mundaŋ', 'Fĩi Gwahlle', 'Fĩi Yuru'],
    +  SHORTMONTHS: ['FLO', 'CLA', 'CKI', 'FMF', 'MAD', 'MBI', 'MLI', 'MAM', 'FDE',
    +    'FMU', 'FGW', 'FYU'],
    +  STANDALONESHORTMONTHS: ['FLO', 'CLA', 'CKI', 'FMF', 'MAD', 'MBI', 'MLI',
    +    'MAM', 'FDE', 'FMU', 'FGW', 'FYU'],
    +  WEEKDAYS: ['Com’yakke', 'Comlaaɗii', 'Comzyiiɗii', 'Comkolle',
    +    'Comkaldǝɓlii', 'Comgaisuu', 'Comzyeɓsuu'],
    +  STANDALONEWEEKDAYS: ['Com’yakke', 'Comlaaɗii', 'Comzyiiɗii', 'Comkolle',
    +    'Comkaldǝɓlii', 'Comgaisuu', 'Comzyeɓsuu'],
    +  SHORTWEEKDAYS: ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'],
    +  STANDALONESHORTWEEKDAYS: ['Cya', 'Cla', 'Czi', 'Cko', 'Cka', 'Cga', 'Cze'],
    +  NARROWWEEKDAYS: ['Y', 'L', 'Z', 'O', 'A', 'G', 'E'],
    +  STANDALONENARROWWEEKDAYS: ['Y', 'L', 'Z', 'O', 'A', 'G', 'E'],
    +  SHORTQUARTERS: ['F1', 'F2', 'F3', 'F4'],
    +  QUARTERS: ['Tai fĩi sai ma tǝn kee zah', 'Tai fĩi sai zah lǝn gwa ma kee',
    +    'Tai fĩi sai zah lǝn sai ma kee', 'Tai fĩi sai ma coo kee zah ‘na'],
    +  AMPMS: ['comme', 'lilli'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale mua_CM.
    + */
    +goog.i18n.DateTimeSymbols_mua_CM = goog.i18n.DateTimeSymbols_mua;
    +
    +
    +/**
    + * Date/time formatting symbols for locale my_MM.
    + */
    +goog.i18n.DateTimeSymbols_my_MM = {
    +  ZERODIGIT: 0x1040,
    +  ERAS: ['ဘီစီ', 'အေဒီ'],
    +  ERANAMES: ['ခရစ်တော် မပေါ်မီကာလ',
    +    'ခရစ်တော် ပေါ်ထွန်းပြီးကာလ'],
    +  NARROWMONTHS: ['ဇ', 'ဖ', 'မ', 'ဧ', 'မ', 'ဇ', 'ဇ', 'ဩ', 'စ',
    +    'အ', 'န', 'ဒ'],
    +  STANDALONENARROWMONTHS: ['ဇ', 'ဖ', 'မ', 'ဧ', 'မ', 'ဇ', 'ဇ',
    +    'ဩ', 'စ', 'အ', 'န', 'ဒ'],
    +  MONTHS: ['ဇန်နဝါရီ', 'ဖေဖော်ဝါရီ',
    +    'မတ်', 'ဧပြီ', 'မေ', 'ဇွန်',
    +    'ဇူလိုင်', 'ဩဂုတ်', 'စက်တင်ဘာ',
    +    'အောက်တိုဘာ', 'နိုဝင်ဘာ',
    +    'ဒီဇင်ဘာ'],
    +  STANDALONEMONTHS: ['ဇန်နဝါရီ',
    +    'ဖေဖော်ဝါရီ', 'မတ်', 'ဧပြီ', 'မေ',
    +    'ဇွန်', 'ဇူလိုင်', 'ဩဂုတ်',
    +    'စက်တင်ဘာ', 'အောက်တိုဘာ',
    +    'နိုဝင်ဘာ', 'ဒီဇင်ဘာ'],
    +  SHORTMONTHS: ['ဇန်', 'ဖေ', 'မတ်', 'ဧပြီ', 'မေ',
    +    'ဇွန်', 'ဇူ', 'ဩ', 'စက်', 'အောက်',
    +    'နို', 'ဒီ'],
    +  STANDALONESHORTMONTHS: ['ဇန်', 'ဖေ', 'မတ်', 'ဧပြီ',
    +    'မေ', 'ဇွန်', 'ဇူ', 'ဩ', 'စက်', 'အောက်',
    +    'နို', 'ဒီ'],
    +  WEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  STANDALONEWEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  SHORTWEEKDAYS: ['တနင်္ဂနွေ', 'တနင်္လာ',
    +    'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  STANDALONESHORTWEEKDAYS: ['တနင်္ဂနွေ',
    +    'တနင်္လာ', 'အင်္ဂါ', 'ဗုဒ္ဓဟူး',
    +    'ကြာသပတေး', 'သောကြာ', 'စနေ'],
    +  NARROWWEEKDAYS: ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
    +  STANDALONENARROWWEEKDAYS: ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
    +  SHORTQUARTERS: ['ပထမ သုံးလပတ်',
    +    'ဒုတိယ သုံးလပတ်',
    +    'တတိယ သုံးလပတ်',
    +    'စတုတ္ထ သုံးလပတ်'],
    +  QUARTERS: ['ပထမ သုံးလပတ်',
    +    'ဒုတိယ သုံးလပတ်',
    +    'တတိယ သုံးလပတ်',
    +    'စတုတ္ထ သုံးလပတ်'],
    +  AMPMS: ['နံနက်', 'ညနေ'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}မှာ {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale naq.
    + */
    +goog.i18n.DateTimeSymbols_naq = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Xristub aiǃâ', 'Xristub khaoǃgâ'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['ǃKhanni', 'ǃKhanǀgôab', 'ǀKhuuǁkhâb', 'ǃHôaǂkhaib',
    +    'ǃKhaitsâb', 'Gamaǀaeb', 'ǂKhoesaob', 'Aoǁkhuumûǁkhâb',
    +    'Taraǀkhuumûǁkhâb', 'ǂNûǁnâiseb', 'ǀHooǂgaeb', 'Hôasoreǁkhâb'],
    +  STANDALONEMONTHS: ['ǃKhanni', 'ǃKhanǀgôab', 'ǀKhuuǁkhâb',
    +    'ǃHôaǂkhaib', 'ǃKhaitsâb', 'Gamaǀaeb', 'ǂKhoesaob',
    +    'Aoǁkhuumûǁkhâb', 'Taraǀkhuumûǁkhâb', 'ǂNûǁnâiseb',
    +    'ǀHooǂgaeb', 'Hôasoreǁkhâb'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
    +    'Oct', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
    +    'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Sontaxtsees', 'Mantaxtsees', 'Denstaxtsees', 'Wunstaxtsees',
    +    'Dondertaxtsees', 'Fraitaxtsees', 'Satertaxtsees'],
    +  STANDALONEWEEKDAYS: ['Sontaxtsees', 'Mantaxtsees', 'Denstaxtsees',
    +    'Wunstaxtsees', 'Dondertaxtsees', 'Fraitaxtsees', 'Satertaxtsees'],
    +  SHORTWEEKDAYS: ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Ma', 'De', 'Wu', 'Do', 'Fr', 'Sat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'E', 'W', 'D', 'F', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'E', 'W', 'D', 'F', 'A'],
    +  SHORTQUARTERS: ['KW1', 'KW2', 'KW3', 'KW4'],
    +  QUARTERS: ['1ro kwartals', '2ǁî kwartals', '3ǁî kwartals',
    +    '4ǁî kwartals'],
    +  AMPMS: ['ǁgoagas', 'ǃuias'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale naq_NA.
    + */
    +goog.i18n.DateTimeSymbols_naq_NA = goog.i18n.DateTimeSymbols_naq;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nb_NO.
    + */
    +goog.i18n.DateTimeSymbols_nb_NO = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'mai', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['sø.', 'ma.', 'ti.', 'on.', 'to.', 'fr.', 'lø.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} \'kl.\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nb_SJ.
    + */
    +goog.i18n.DateTimeSymbols_nb_SJ = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'mai', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag',
    +    'lørdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'mandag', 'tirsdag', 'onsdag', 'torsdag',
    +    'fredag', 'lørdag'],
    +  SHORTWEEKDAYS: ['søn.', 'man.', 'tir.', 'ons.', 'tor.', 'fre.', 'lør.'],
    +  STANDALONESHORTWEEKDAYS: ['sø.', 'ma.', 'ti.', 'on.', 'to.', 'fr.', 'lø.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} \'kl.\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nd.
    + */
    +goog.i18n.DateTimeSymbols_nd = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['UKristo angakabuyi', 'Ukristo ebuyile'],
    +  NARROWMONTHS: ['Z', 'N', 'M', 'M', 'N', 'N', 'N', 'N', 'M', 'M', 'L', 'M'],
    +  STANDALONENARROWMONTHS: ['Z', 'N', 'M', 'M', 'N', 'N', 'N', 'N', 'M', 'M',
    +    'L', 'M'],
    +  MONTHS: ['Zibandlela', 'Nhlolanja', 'Mbimbitho', 'Mabasa', 'Nkwenkwezi',
    +    'Nhlangula', 'Ntulikazi', 'Ncwabakazi', 'Mpandula', 'Mfumfu', 'Lwezi',
    +    'Mpalakazi'],
    +  STANDALONEMONTHS: ['Zibandlela', 'Nhlolanja', 'Mbimbitho', 'Mabasa',
    +    'Nkwenkwezi', 'Nhlangula', 'Ntulikazi', 'Ncwabakazi', 'Mpandula', 'Mfumfu',
    +    'Lwezi', 'Mpalakazi'],
    +  SHORTMONTHS: ['Zib', 'Nhlo', 'Mbi', 'Mab', 'Nkw', 'Nhla', 'Ntu', 'Ncw',
    +    'Mpan', 'Mfu', 'Lwe', 'Mpal'],
    +  STANDALONESHORTMONTHS: ['Zib', 'Nhlo', 'Mbi', 'Mab', 'Nkw', 'Nhla', 'Ntu',
    +    'Ncw', 'Mpan', 'Mfu', 'Lwe', 'Mpal'],
    +  WEEKDAYS: ['Sonto', 'Mvulo', 'Sibili', 'Sithathu', 'Sine', 'Sihlanu',
    +    'Mgqibelo'],
    +  STANDALONEWEEKDAYS: ['Sonto', 'Mvulo', 'Sibili', 'Sithathu', 'Sine',
    +    'Sihlanu', 'Mgqibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mvu', 'Sib', 'Sit', 'Sin', 'Sih', 'Mgq'],
    +  NARROWWEEKDAYS: ['S', 'M', 'S', 'S', 'S', 'S', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'S', 'S', 'S', 'S', 'M'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kota 1', 'Kota 2', 'Kota 3', 'Kota 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nd_ZW.
    + */
    +goog.i18n.DateTimeSymbols_nd_ZW = goog.i18n.DateTimeSymbols_nd;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ne_IN.
    + */
    +goog.i18n.DateTimeSymbols_ne_IN = {
    +  ZERODIGIT: 0x0966,
    +  ERAS: ['ईसा पूर्व', 'सन्'],
    +  ERANAMES: ['ईसा पूर्व', 'सन्'],
    +  NARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७', '८', '९',
    +    '१०', '११', '१२'],
    +  STANDALONENARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७',
    +    '८', '९', '१०', '११', '१२'],
    +  MONTHS: ['जनवरी', 'फरवरी', 'मार्च',
    +    'अप्रेल', 'मई', 'जुन', 'जुलाई',
    +    'अगस्त', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'दिसम्बर'],
    +  STANDALONEMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  SHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  STANDALONESHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  WEEKDAYS: ['आइतवार', 'सोमवार',
    +    'मङ्गलवार', 'बुधवार', 'बिहीवार',
    +    'शुक्रवार', 'शनिवार'],
    +  STANDALONEWEEKDAYS: ['आइतबार', 'सोमबार',
    +    'मङ्गलबार', 'बुधबार', 'बिहीबार',
    +    'शुक्रबार', 'शनिबार'],
    +  SHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल', 'बुध',
    +    'बिही', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल',
    +    'बुध', 'बिही', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि', 'शु', 'श'],
    +  STANDALONENARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['पहिलो सत्र',
    +    'दोस्रो सत्र', 'तेस्रो सत्र',
    +    'चौथो सत्र'],
    +  QUARTERS: ['पहिलो सत्र', 'दोस्रो सत्र',
    +    'तेस्रो सत्र', 'चौथो सत्र'],
    +  AMPMS: ['पूर्वाह्न', 'अपराह्न'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ne_NP.
    + */
    +goog.i18n.DateTimeSymbols_ne_NP = {
    +  ZERODIGIT: 0x0966,
    +  ERAS: ['ईसा पूर्व', 'सन्'],
    +  ERANAMES: ['ईसा पूर्व', 'सन्'],
    +  NARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७', '८', '९',
    +    '१०', '११', '१२'],
    +  STANDALONENARROWMONTHS: ['१', '२', '३', '४', '५', '६', '७',
    +    '८', '९', '१०', '११', '१२'],
    +  MONTHS: ['जनवरी', 'फेब्रुअरी', 'मार्च',
    +    'अप्रिल', 'मे', 'जुन', 'जुलाई',
    +    'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  STANDALONEMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  SHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  STANDALONESHORTMONTHS: ['जनवरी', 'फेब्रुअरी',
    +    'मार्च', 'अप्रिल', 'मे', 'जुन',
    +    'जुलाई', 'अगस्ट', 'सेप्टेम्बर',
    +    'अक्टोबर', 'नोभेम्बर',
    +    'डिसेम्बर'],
    +  WEEKDAYS: ['आइतबार', 'सोमबार',
    +    'मङ्गलबार', 'बुधबार', 'बिहीबार',
    +    'शुक्रबार', 'शनिबार'],
    +  STANDALONEWEEKDAYS: ['आइतबार', 'सोमबार',
    +    'मङ्गलबार', 'बुधबार', 'बिहीबार',
    +    'शुक्रबार', 'शनिबार'],
    +  SHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल', 'बुध',
    +    'बिही', 'शुक्र', 'शनि'],
    +  STANDALONESHORTWEEKDAYS: ['आइत', 'सोम', 'मङ्गल',
    +    'बुध', 'बिही', 'शुक्र', 'शनि'],
    +  NARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि', 'शु', 'श'],
    +  STANDALONENARROWWEEKDAYS: ['आ', 'सो', 'म', 'बु', 'बि',
    +    'शु', 'श'],
    +  SHORTQUARTERS: ['पहिलो सत्र',
    +    'दोस्रो सत्र', 'तेस्रो सत्र',
    +    'चौथो सत्र'],
    +  QUARTERS: ['पहिलो सत्र', 'दोस्रो सत्र',
    +    'तेस्रो सत्र', 'चौथो सत्र'],
    +  AMPMS: ['पूर्व मध्यान्ह',
    +    'उत्तर मध्यान्ह'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_AW.
    + */
    +goog.i18n.DateTimeSymbols_nl_AW = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_BE.
    + */
    +goog.i18n.DateTimeSymbols_nl_BE = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd-MMM-y', 'd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_BQ.
    + */
    +goog.i18n.DateTimeSymbols_nl_BQ = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_CW.
    + */
    +goog.i18n.DateTimeSymbols_nl_CW = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_NL.
    + */
    +goog.i18n.DateTimeSymbols_nl_NL = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_SR.
    + */
    +goog.i18n.DateTimeSymbols_nl_SR = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nl_SX.
    + */
    +goog.i18n.DateTimeSymbols_nl_SX = {
    +  ERAS: ['v.Chr.', 'n.Chr.'],
    +  ERANAMES: ['voor Christus', 'na Christus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
    +    'augustus', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni',
    +    'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mrt.', 'apr.', 'mei', 'jun.', 'jul.', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mrt.', 'Apr.', 'Mei', 'Jun.', 'Jul.',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag',
    +    'zaterdag'],
    +  STANDALONEWEEKDAYS: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag',
    +    'Vrijdag', 'Zaterdag'],
    +  SHORTWEEKDAYS: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
    +  STANDALONESHORTWEEKDAYS: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
    +  NARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1e kwartaal', '2e kwartaal', '3e kwartaal', '4e kwartaal'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nmg.
    + */
    +goog.i18n.DateTimeSymbols_nmg = {
    +  ERAS: ['BL', 'PB'],
    +  ERANAMES: ['Bó Lahlɛ̄', 'Pfiɛ Burī'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ngwɛn matáhra', 'ngwɛn ńmba', 'ngwɛn ńlal', 'ngwɛn ńna',
    +    'ngwɛn ńtan', 'ngwɛn ńtuó', 'ngwɛn hɛmbuɛrí', 'ngwɛn lɔmbi',
    +    'ngwɛn rɛbvuâ', 'ngwɛn wum', 'ngwɛn wum navǔr', 'krísimin'],
    +  STANDALONEMONTHS: ['ngwɛn matáhra', 'ngwɛn ńmba', 'ngwɛn ńlal',
    +    'ngwɛn ńna', 'ngwɛn ńtan', 'ngwɛn ńtuó', 'ngwɛn hɛmbuɛrí',
    +    'ngwɛn lɔmbi', 'ngwɛn rɛbvuâ', 'ngwɛn wum', 'ngwɛn wum navǔr',
    +    'krísimin'],
    +  SHORTMONTHS: ['ng1', 'ng2', 'ng3', 'ng4', 'ng5', 'ng6', 'ng7', 'ng8', 'ng9',
    +    'ng10', 'ng11', 'kris'],
    +  STANDALONESHORTMONTHS: ['ng1', 'ng2', 'ng3', 'ng4', 'ng5', 'ng6', 'ng7',
    +    'ng8', 'ng9', 'ng10', 'ng11', 'kris'],
    +  WEEKDAYS: ['sɔ́ndɔ', 'mɔ́ndɔ', 'sɔ́ndɔ mafú mába',
    +    'sɔ́ndɔ mafú málal', 'sɔ́ndɔ mafú mána', 'mabágá má sukul',
    +    'sásadi'],
    +  STANDALONEWEEKDAYS: ['sɔ́ndɔ', 'mɔ́ndɔ', 'sɔ́ndɔ mafú mába',
    +    'sɔ́ndɔ mafú málal', 'sɔ́ndɔ mafú mána', 'mabágá má sukul',
    +    'sásadi'],
    +  SHORTWEEKDAYS: ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs', 'sas'],
    +  STANDALONESHORTWEEKDAYS: ['sɔ́n', 'mɔ́n', 'smb', 'sml', 'smn', 'mbs',
    +    'sas'],
    +  NARROWWEEKDAYS: ['s', 'm', 's', 's', 's', 'm', 's'],
    +  STANDALONENARROWWEEKDAYS: ['s', 'm', 's', 's', 's', 'm', 's'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['Tindɛ nvúr', 'Tindɛ ńmba', 'Tindɛ ńlal', 'Tindɛ ńna'],
    +  AMPMS: ['maná', 'kugú'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nmg_CM.
    + */
    +goog.i18n.DateTimeSymbols_nmg_CM = goog.i18n.DateTimeSymbols_nmg;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nn.
    + */
    +goog.i18n.DateTimeSymbols_nn = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['f.Kr.', 'e.Kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli',
    +    'august', 'september', 'oktober', 'november', 'desember'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mars', 'april', 'mai', 'juni',
    +    'juli', 'august', 'september', 'oktober', 'november', 'desember'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mars', 'apr.', 'mai', 'juni', 'juli', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'des.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul',
    +    'aug', 'sep', 'okt', 'nov', 'des'],
    +  WEEKDAYS: ['søndag', 'måndag', 'tysdag', 'onsdag', 'torsdag', 'fredag',
    +    'laurdag'],
    +  STANDALONEWEEKDAYS: ['søndag', 'måndag', 'tysdag', 'onsdag', 'torsdag',
    +    'fredag', 'laurdag'],
    +  SHORTWEEKDAYS: ['sø.', 'må.', 'ty.', 'on.', 'to.', 'fr.', 'la.'],
    +  STANDALONESHORTWEEKDAYS: ['søn', 'mån', 'tys', 'ons', 'tor', 'fre', 'lau'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
    +  AMPMS: ['formiddag', 'ettermiddag'],
    +  DATEFORMATS: ['EEEE d. MMMM y', 'd. MMMM y', 'd. MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['\'kl\'. HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} \'kl.\' {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nn_NO.
    + */
    +goog.i18n.DateTimeSymbols_nn_NO = goog.i18n.DateTimeSymbols_nn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nnh.
    + */
    +goog.i18n.DateTimeSymbols_nnh = {
    +  ERAS: ['m.z.Y.', 'm.g.n.Y.'],
    +  ERANAMES: ['mé zyé Yěsô', 'mé gÿo ńzyé Yěsô'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ', 'saŋ lepyè shúm',
    +    'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ',
    +    'saŋ tyɛ̀b tyɛ̀b mbʉ̀', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ',
    +    'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'],
    +  STANDALONEMONTHS: ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ',
    +    'saŋ lepyè shúm', 'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ',
    +    'saŋ tyɛ̀b tyɛ̀b mbʉ̀', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ',
    +    'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'],
    +  SHORTMONTHS: ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ',
    +    'saŋ lepyè shúm', 'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ',
    +    'saŋ tyɛ̀b tyɛ̀b mbʉ̀', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ',
    +    'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'],
    +  STANDALONESHORTMONTHS: ['saŋ tsetsɛ̀ɛ lùm', 'saŋ kàg ngwóŋ',
    +    'saŋ lepyè shúm', 'saŋ cÿó', 'saŋ tsɛ̀ɛ cÿó', 'saŋ njÿoláʼ',
    +    'saŋ tyɛ̀b tyɛ̀b mbʉ̀', 'saŋ mbʉ̀ŋ', 'saŋ ngwɔ̀ʼ mbÿɛ',
    +    'saŋ tàŋa tsetsáʼ', 'saŋ mejwoŋó', 'saŋ lùm'],
    +  WEEKDAYS: ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ',
    +    'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ',
    +    'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ',
    +    'màga lyɛ̌ʼ'],
    +  STANDALONEWEEKDAYS: ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ',
    +    'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ',
    +    'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ',
    +    'màga lyɛ̌ʼ'],
    +  SHORTWEEKDAYS: ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ',
    +    'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ',
    +    'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ',
    +    'màga lyɛ̌ʼ'],
    +  STANDALONESHORTWEEKDAYS: ['lyɛʼɛ́ sẅíŋtè', 'mvfò lyɛ̌ʼ',
    +    'mbɔ́ɔntè mvfò lyɛ̌ʼ', 'tsètsɛ̀ɛ lyɛ̌ʼ',
    +    'mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ', 'mvfò màga lyɛ̌ʼ',
    +    'màga lyɛ̌ʼ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['mbaʼámbaʼ', 'ncwònzém'],
    +  DATEFORMATS: ['EEEE , \'lyɛ\'̌ʼ d \'na\' MMMM, y',
    +    '\'lyɛ\'̌ʼ d \'na\' MMMM, y', 'd MMM, y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1},{0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nnh_CM.
    + */
    +goog.i18n.DateTimeSymbols_nnh_CM = goog.i18n.DateTimeSymbols_nnh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nr.
    + */
    +goog.i18n.DateTimeSymbols_nr = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Janabari', 'uFeberbari', 'uMatjhi', 'u-Apreli', 'Meyi', 'Juni',
    +    'Julayi', 'Arhostosi', 'Septemba', 'Oktoba', 'Usinyikhaba', 'Disemba'],
    +  STANDALONEMONTHS: ['Janabari', 'uFeberbari', 'uMatjhi', 'u-Apreli', 'Meyi',
    +    'Juni', 'Julayi', 'Arhostosi', 'Septemba', 'Oktoba', 'Usinyikhaba',
    +    'Disemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mat', 'Apr', 'Mey', 'Jun', 'Jul', 'Arh', 'Sep',
    +    'Okt', 'Usi', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mat', 'Apr', 'Mey', 'Jun', 'Jul',
    +    'Arh', 'Sep', 'Okt', 'Usi', 'Dis'],
    +  WEEKDAYS: ['uSonto', 'uMvulo', 'uLesibili', 'Lesithathu', 'uLesine',
    +    'ngoLesihlanu', 'umGqibelo'],
    +  STANDALONEWEEKDAYS: ['uSonto', 'uMvulo', 'uLesibili', 'Lesithathu', 'uLesine',
    +    'ngoLesihlanu', 'umGqibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mvu', 'Bil', 'Tha', 'Ne', 'Hla', 'Gqi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nr_ZA.
    + */
    +goog.i18n.DateTimeSymbols_nr_ZA = goog.i18n.DateTimeSymbols_nr;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nso.
    + */
    +goog.i18n.DateTimeSymbols_nso = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Janaware', 'Feberware', 'Matšhe', 'Aporele', 'Mei', 'June',
    +    'Julae', 'Agostose', 'Setemere', 'Oktobore', 'Nofemere', 'Disemere'],
    +  STANDALONEMONTHS: ['Janaware', 'Feberware', 'Matšhe', 'Aporele', 'Mei',
    +    'June', 'Julae', 'Agostose', 'Setemere', 'Oktobore', 'Nofemere',
    +    'Disemere'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mat', 'Apo', 'Mei', 'Jun', 'Jul', 'Ago', 'Set',
    +    'Okt', 'Nof', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mat', 'Apo', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Okt', 'Nof', 'Dis'],
    +  WEEKDAYS: ['Sontaga', 'Mosupalogo', 'Labobedi', 'Laboraro', 'Labone',
    +    'Labohlano', 'Mokibelo'],
    +  STANDALONEWEEKDAYS: ['Sontaga', 'Mosupalogo', 'Labobedi', 'Laboraro',
    +    'Labone', 'Labohlano', 'Mokibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mos', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mos', 'Bed', 'Rar', 'Ne', 'Hla', 'Mok'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nso_ZA.
    + */
    +goog.i18n.DateTimeSymbols_nso_ZA = goog.i18n.DateTimeSymbols_nso;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nus.
    + */
    +goog.i18n.DateTimeSymbols_nus = {
    +  ERAS: ['AY', 'ƐY'],
    +  ERANAMES: ['A ka̱n Yecu ni dap', 'Ɛ ca Yecu dap'],
    +  NARROWMONTHS: ['T', 'P', 'D', 'G', 'D', 'K', 'P', 'T', 'T', 'L', 'K', 'T'],
    +  STANDALONENARROWMONTHS: ['T', 'P', 'D', 'G', 'D', 'K', 'P', 'T', 'T', 'L',
    +    'K', 'T'],
    +  MONTHS: ['Tiop thar pɛt', 'Pɛt', 'Duɔ̱ɔ̱ŋ', 'Guak', 'Duät',
    +    'Kornyoot', 'Pay yie̱tni', 'Tho̱o̱r', 'Tɛɛr', 'Laath', 'Kur',
    +    'Tio̱p in di̱i̱t'],
    +  STANDALONEMONTHS: ['Tiop thar pɛt', 'Pɛt', 'Duɔ̱ɔ̱ŋ', 'Guak', 'Duät',
    +    'Kornyoot', 'Pay yie̱tni', 'Tho̱o̱r', 'Tɛɛr', 'Laath', 'Kur',
    +    'Tio̱p in di̱i̱t'],
    +  SHORTMONTHS: ['Tiop', 'Pɛt', 'Duɔ̱ɔ̱', 'Guak', 'Duä', 'Kor', 'Pay',
    +    'Thoo', 'Tɛɛ', 'Laa', 'Kur', 'Tid'],
    +  STANDALONESHORTMONTHS: ['Tiop', 'Pɛt', 'Duɔ̱ɔ̱', 'Guak', 'Duä', 'Kor',
    +    'Pay', 'Thoo', 'Tɛɛ', 'Laa', 'Kur', 'Tid'],
    +  WEEKDAYS: ['Cäŋ kuɔth', 'Jiec la̱t', 'Rɛw lätni', 'Diɔ̱k lätni',
    +    'Ŋuaan lätni', 'Dhieec lätni', 'Bäkɛl lätni'],
    +  STANDALONEWEEKDAYS: ['Cäŋ kuɔth', 'Jiec la̱t', 'Rɛw lätni',
    +    'Diɔ̱k lätni', 'Ŋuaan lätni', 'Dhieec lätni', 'Bäkɛl lätni'],
    +  SHORTWEEKDAYS: ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan', 'Dhieec',
    +    'Bäkɛl'],
    +  STANDALONESHORTWEEKDAYS: ['Cäŋ', 'Jiec', 'Rɛw', 'Diɔ̱k', 'Ŋuaan',
    +    'Dhieec', 'Bäkɛl'],
    +  NARROWWEEKDAYS: ['C', 'J', 'R', 'D', 'Ŋ', 'D', 'B'],
    +  STANDALONENARROWWEEKDAYS: ['C', 'J', 'R', 'D', 'Ŋ', 'D', 'B'],
    +  SHORTQUARTERS: ['P1', 'P2', 'P3', 'P4'],
    +  QUARTERS: ['Päth diɔk tin nhiam', 'Päth diɔk tin guurɛ',
    +    'Päth diɔk tin wä kɔɔriɛn', 'Päth diɔk tin jiɔakdiɛn'],
    +  AMPMS: ['RW', 'TŊ'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/y'],
    +  TIMEFORMATS: ['zzzz h:mm:ss a', 'z h:mm:ss a', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nus_SD.
    + */
    +goog.i18n.DateTimeSymbols_nus_SD = goog.i18n.DateTimeSymbols_nus;
    +
    +
    +/**
    + * Date/time formatting symbols for locale nyn.
    + */
    +goog.i18n.DateTimeSymbols_nyn = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kurisito Atakaijire', 'Kurisito Yaijire'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana',
    +    'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda',
    +    'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
    +  STANDALONEMONTHS: ['Okwokubanza', 'Okwakabiri', 'Okwakashatu', 'Okwakana',
    +    'Okwakataana', 'Okwamukaaga', 'Okwamushanju', 'Okwamunaana', 'Okwamwenda',
    +    'Okwaikumi', 'Okwaikumi na kumwe', 'Okwaikumi na ibiri'],
    +  SHORTMONTHS: ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS', 'KMN', 'KMW',
    +    'KKM', 'KNK', 'KNB'],
    +  STANDALONESHORTMONTHS: ['KBZ', 'KBR', 'KST', 'KKN', 'KTN', 'KMK', 'KMS',
    +    'KMN', 'KMW', 'KKM', 'KNK', 'KNB'],
    +  WEEKDAYS: ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu', 'Orwakana',
    +    'Orwakataano', 'Orwamukaaga'],
    +  STANDALONEWEEKDAYS: ['Sande', 'Orwokubanza', 'Orwakabiri', 'Orwakashatu',
    +    'Orwakana', 'Orwakataano', 'Orwamukaaga'],
    +  SHORTWEEKDAYS: ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
    +  STANDALONESHORTWEEKDAYS: ['SAN', 'ORK', 'OKB', 'OKS', 'OKN', 'OKT', 'OMK'],
    +  NARROWWEEKDAYS: ['S', 'K', 'R', 'S', 'N', 'T', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'K', 'R', 'S', 'N', 'T', 'M'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['KWOTA 1', 'KWOTA 2', 'KWOTA 3', 'KWOTA 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale nyn_UG.
    + */
    +goog.i18n.DateTimeSymbols_nyn_UG = goog.i18n.DateTimeSymbols_nyn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale om.
    + */
    +goog.i18n.DateTimeSymbols_om = {
    +  ERAS: ['KD', 'KB'],
    +  ERANAMES: ['KD', 'KB'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Amajjii', 'Guraandhala', 'Bitooteessa', 'Elba', 'Caamsa',
    +    'Waxabajjii', 'Adooleessa', 'Hagayya', 'Fuulbana', 'Onkololeessa',
    +    'Sadaasa', 'Muddee'],
    +  STANDALONEMONTHS: ['Amajjii', 'Guraandhala', 'Bitooteessa', 'Elba', 'Caamsa',
    +    'Waxabajjii', 'Adooleessa', 'Hagayya', 'Fuulbana', 'Onkololeessa',
    +    'Sadaasa', 'Muddee'],
    +  SHORTMONTHS: ['Ama', 'Gur', 'Bit', 'Elb', 'Cam', 'Wax', 'Ado', 'Hag', 'Ful',
    +    'Onk', 'Sad', 'Mud'],
    +  STANDALONESHORTMONTHS: ['Ama', 'Gur', 'Bit', 'Elb', 'Cam', 'Wax', 'Ado',
    +    'Hag', 'Ful', 'Onk', 'Sad', 'Mud'],
    +  WEEKDAYS: ['Dilbata', 'Wiixata', 'Qibxata', 'Roobii', 'Kamiisa', 'Jimaata',
    +    'Sanbata'],
    +  STANDALONEWEEKDAYS: ['Dilbata', 'Wiixata', 'Qibxata', 'Roobii', 'Kamiisa',
    +    'Jimaata', 'Sanbata'],
    +  SHORTWEEKDAYS: ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'],
    +  STANDALONESHORTWEEKDAYS: ['Dil', 'Wix', 'Qib', 'Rob', 'Kam', 'Jim', 'San'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['WD', 'WB'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale om_ET.
    + */
    +goog.i18n.DateTimeSymbols_om_ET = goog.i18n.DateTimeSymbols_om;
    +
    +
    +/**
    + * Date/time formatting symbols for locale om_KE.
    + */
    +goog.i18n.DateTimeSymbols_om_KE = goog.i18n.DateTimeSymbols_om;
    +
    +
    +/**
    + * Date/time formatting symbols for locale or_IN.
    + */
    +goog.i18n.DateTimeSymbols_or_IN = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['ଜା', 'ଫେ', 'ମା', 'ଅ', 'ମଇ', 'ଜୁ',
    +    'ଜୁ', 'ଅ', 'ସେ', 'ଅ', 'ନ', 'ଡି'],
    +  STANDALONENARROWMONTHS: ['ଜା', 'ଫେ', 'ମା', 'ଅ', 'ମଇ',
    +    'ଜୁ', 'ଜୁ', 'ଅ', 'ସେ', 'ଅ', 'ନ', 'ଡି'],
    +  MONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  STANDALONEMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  SHORTMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  STANDALONESHORTMONTHS: ['ଜାନୁଆରୀ', 'ଫେବୃଆରୀ',
    +    'ମାର୍ଚ୍ଚ', 'ଅପ୍ରେଲ', 'ମଇ', 'ଜୁନ',
    +    'ଜୁଲାଇ', 'ଅଗଷ୍ଟ', 'ସେପ୍ଟେମ୍ବର',
    +    'ଅକ୍ଟୋବର', 'ନଭେମ୍ବର',
    +    'ଡିସେମ୍ବର'],
    +  WEEKDAYS: ['ରବିବାର', 'ସୋମବାର',
    +    'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର',
    +    'ଶୁକ୍ରବାର', 'ଶନିବାର'],
    +  STANDALONEWEEKDAYS: ['ରବିବାର', 'ସୋମବାର',
    +    'ମଙ୍ଗଳବାର', 'ବୁଧବାର', 'ଗୁରୁବାର',
    +    'ଶୁକ୍ରବାର', 'ଶନିବାର'],
    +  SHORTWEEKDAYS: ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ', 'ବୁଧ',
    +    'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
    +  STANDALONESHORTWEEKDAYS: ['ରବି', 'ସୋମ', 'ମଙ୍ଗଳ',
    +    'ବୁଧ', 'ଗୁରୁ', 'ଶୁକ୍ର', 'ଶନି'],
    +  NARROWWEEKDAYS: ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ', 'ଶୁ', 'ଶ'],
    +  STANDALONENARROWWEEKDAYS: ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ',
    +    'ଶୁ', 'ଶ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['am', 'pm'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale os.
    + */
    +goog.i18n.DateTimeSymbols_os = {
    +  ERAS: ['н.д.а.', 'н.д.'],
    +  ERANAMES: ['н.д.а.', 'н.д.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['январы', 'февралы', 'мартъийы', 'апрелы',
    +    'майы', 'июны', 'июлы', 'августы', 'сентябры',
    +    'октябры', 'ноябры', 'декабры'],
    +  STANDALONEMONTHS: ['Январь', 'Февраль', 'Мартъи',
    +    'Апрель', 'Май', 'Июнь', 'Июль', 'Август',
    +    'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
    +  SHORTMONTHS: ['янв.', 'фев.', 'мар.', 'апр.', 'мая',
    +    'июны', 'июлы', 'авг.', 'сен.', 'окт.', 'ноя.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['Янв.', 'Февр.', 'Март', 'Апр.',
    +    'Май', 'Июнь', 'Июль', 'Авг.', 'Сент.', 'Окт.',
    +    'Нояб.', 'Дек.'],
    +  WEEKDAYS: ['хуыцаубон', 'къуырисӕр', 'дыццӕг',
    +    'ӕртыццӕг', 'цыппӕрӕм', 'майрӕмбон', 'сабат'],
    +  STANDALONEWEEKDAYS: ['Хуыцаубон', 'Къуырисӕр',
    +    'Дыццӕг', 'Ӕртыццӕг', 'Цыппӕрӕм',
    +    'Майрӕмбон', 'Сабат'],
    +  SHORTWEEKDAYS: ['хцб', 'крс', 'дцг', 'ӕрт', 'цпр', 'мрб',
    +    'сбт'],
    +  STANDALONESHORTWEEKDAYS: ['Хцб', 'Крс', 'Дцг', 'Ӕрт', 'Цпр',
    +    'Мрб', 'Сбт'],
    +  NARROWWEEKDAYS: ['Х', 'К', 'Д', 'Ӕ', 'Ц', 'М', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Х', 'К', 'Д', 'Ӕ', 'Ц', 'М', 'С'],
    +  SHORTQUARTERS: ['1-аг кв.', '2-аг кв.', '3-аг кв.',
    +    '4-ӕм кв.'],
    +  QUARTERS: ['1-аг квартал', '2-аг квартал',
    +    '3-аг квартал', '4-ӕм квартал'],
    +  AMPMS: ['ӕмбисбоны размӕ', 'ӕмбисбоны фӕстӕ'],
    +  DATEFORMATS: ['EEEE, d MMMM, y \'аз\'', 'd MMMM, y \'аз\'',
    +    'dd MMM y \'аз\'', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale os_GE.
    + */
    +goog.i18n.DateTimeSymbols_os_GE = goog.i18n.DateTimeSymbols_os;
    +
    +
    +/**
    + * Date/time formatting symbols for locale os_RU.
    + */
    +goog.i18n.DateTimeSymbols_os_RU = goog.i18n.DateTimeSymbols_os;
    +
    +
    +/**
    + * Date/time formatting symbols for locale pa_Arab.
    + */
    +goog.i18n.DateTimeSymbols_pa_Arab = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['ايساپورو', 'سں'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئ',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئ', 'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بُدھ', 'جمعرات',
    +    'جمعہ', 'ہفتہ'],
    +  STANDALONEWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بُدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  SHORTWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بُدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  STANDALONESHORTWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بُدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['چوتھاي پہلاں', 'چوتھاي دوجا',
    +    'چوتھاي تيجا', 'چوتھاي چوتھا'],
    +  QUARTERS: ['چوتھاي پہلاں', 'چوتھاي دوجا',
    +    'چوتھاي تيجا', 'چوتھاي چوتھا'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pa_Arab_PK.
    + */
    +goog.i18n.DateTimeSymbols_pa_Arab_PK = goog.i18n.DateTimeSymbols_pa_Arab;
    +
    +
    +/**
    + * Date/time formatting symbols for locale pa_Guru.
    + */
    +goog.i18n.DateTimeSymbols_pa_Guru = {
    +  ERAS: ['ਈ. ਪੂ.', 'ਸੰਨ'],
    +  ERANAMES: ['ਈਸਵੀ ਪੂਰਵ', 'ਈਸਵੀ ਸੰਨ'],
    +  NARROWMONTHS: ['ਜ', 'ਫ਼', 'ਮਾ', 'ਅ', 'ਮ', 'ਜੂ', 'ਜੁ',
    +    'ਅ', 'ਸ', 'ਅ', 'ਨ', 'ਦ'],
    +  STANDALONENARROWMONTHS: ['ਜ', 'ਫ਼', 'ਮਾ', 'ਅ', 'ਮ', 'ਜੂ',
    +    'ਜੁ', 'ਅ', 'ਸ', 'ਅ', 'ਨ', 'ਦ'],
    +  MONTHS: ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ',
    +    'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ',
    +    'ਦਸੰਬਰ'],
    +  STANDALONEMONTHS: ['ਜਨਵਰੀ', 'ਫ਼ਰਵਰੀ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈਲ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾਈ',
    +    'ਅਗਸਤ', 'ਸਤੰਬਰ', 'ਅਕਤੂਬਰ', 'ਨਵੰਬਰ',
    +    'ਦਸੰਬਰ'],
    +  SHORTMONTHS: ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ', 'ਅਪ੍ਰੈ',
    +    'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ', 'ਸਤੰ',
    +    'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'],
    +  STANDALONESHORTMONTHS: ['ਜਨ', 'ਫ਼ਰ', 'ਮਾਰਚ',
    +    'ਅਪ੍ਰੈ', 'ਮਈ', 'ਜੂਨ', 'ਜੁਲਾ', 'ਅਗ',
    +    'ਸਤੰ', 'ਅਕਤੂ', 'ਨਵੰ', 'ਦਸੰ'],
    +  WEEKDAYS: ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ', 'ਮੰਗਲਵਾਰ',
    +    'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ',
    +    'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'],
    +  STANDALONEWEEKDAYS: ['ਐਤਵਾਰ', 'ਸੋਮਵਾਰ',
    +    'ਮੰਗਲਵਾਰ', 'ਬੁੱਧਵਾਰ', 'ਵੀਰਵਾਰ',
    +    'ਸ਼ੁੱਕਰਵਾਰ', 'ਸ਼ਨਿੱਚਰਵਾਰ'],
    +  SHORTWEEKDAYS: ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ', 'ਬੁੱਧ',
    +    'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'],
    +  STANDALONESHORTWEEKDAYS: ['ਐਤ', 'ਸੋਮ', 'ਮੰਗਲ',
    +    'ਬੁੱਧ', 'ਵੀਰ', 'ਸ਼ੁੱਕਰ', 'ਸ਼ਨਿੱਚਰ'],
    +  NARROWWEEKDAYS: ['ਐ', 'ਸੋ', 'ਮੰ', 'ਬੁੱ', 'ਵੀ',
    +    'ਸ਼ੁੱ', 'ਸ਼'],
    +  STANDALONENARROWWEEKDAYS: ['ਐ', 'ਸੋ', 'ਮੰ', 'ਬੁੱ', 'ਵੀ',
    +    'ਸ਼ੁੱ', 'ਸ਼'],
    +  SHORTQUARTERS: ['ਤਿਮਾਹੀ1', 'ਤਿਮਾਹੀ2',
    +    'ਤਿਮਾਹੀ3', 'ਤਿਮਾਹੀ4'],
    +  QUARTERS: ['ਪਹਿਲੀ ਤਿਮਾਹੀ',
    +    'ਦੂਜੀ ਤਿਮਾਹੀ', 'ਤੀਜੀ ਤਿਮਾਹੀ',
    +    'ਚੌਥੀ ਤਿਮਾਹੀ'],
    +  AMPMS: ['ਪੂ.ਦੁ.', 'ਬਾ.ਦੁ.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pa_Guru_IN.
    + */
    +goog.i18n.DateTimeSymbols_pa_Guru_IN = goog.i18n.DateTimeSymbols_pa_Guru;
    +
    +
    +/**
    + * Date/time formatting symbols for locale pl_PL.
    + */
    +goog.i18n.DateTimeSymbols_pl_PL = {
    +  ERAS: ['p.n.e.', 'n.e.'],
    +  ERANAMES: ['p.n.e.', 'n.e.'],
    +  NARROWMONTHS: ['s', 'l', 'm', 'k', 'm', 'c', 'l', 's', 'w', 'p', 'l', 'g'],
    +  STANDALONENARROWMONTHS: ['s', 'l', 'm', 'k', 'm', 'c', 'l', 's', 'w', 'p',
    +    'l', 'g'],
    +  MONTHS: ['stycznia', 'lutego', 'marca', 'kwietnia', 'maja', 'czerwca',
    +    'lipca', 'sierpnia', 'września', 'października', 'listopada', 'grudnia'],
    +  STANDALONEMONTHS: ['styczeń', 'luty', 'marzec', 'kwiecień', 'maj',
    +    'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik', 'listopad',
    +    'grudzień'],
    +  SHORTMONTHS: ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz',
    +    'paź', 'lis', 'gru'],
    +  STANDALONESHORTMONTHS: ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip',
    +    'sie', 'wrz', 'paź', 'lis', 'gru'],
    +  WEEKDAYS: ['niedziela', 'poniedziałek', 'wtorek', 'środa', 'czwartek',
    +    'piątek', 'sobota'],
    +  STANDALONEWEEKDAYS: ['niedziela', 'poniedziałek', 'wtorek', 'środa',
    +    'czwartek', 'piątek', 'sobota'],
    +  SHORTWEEKDAYS: ['niedz.', 'pon.', 'wt.', 'śr.', 'czw.', 'pt.', 'sob.'],
    +  STANDALONESHORTWEEKDAYS: ['niedz.', 'pon.', 'wt.', 'śr.', 'czw.', 'pt.',
    +    'sob.'],
    +  NARROWWEEKDAYS: ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['I kwartał', 'II kwartał', 'III kwartał', 'IV kwartał'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ps.
    + */
    +goog.i18n.DateTimeSymbols_ps = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['ق.م.', 'م.'],
    +  ERANAMES: ['ق.م.', 'م.'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'می',
    +    'جون', 'جولای', 'اګست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوري', 'فبروري', 'مارچ', 'اپریل',
    +    'می', 'جون', 'جولای', 'اګست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنوري', 'فبروري', 'مارچ', 'اپریل', 'می',
    +    'جون', 'جولای', 'اګست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنوري', 'فبروري', 'مارچ',
    +    'اپریل', 'می', 'جون', 'جولای', 'اګست', 'سپتمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONEWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  SHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONESHORTWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['غ.م.', 'غ.و.'],
    +  DATEFORMATS: ['EEEE د y د MMMM d', 'د y د MMMM d', 'd MMM y', 'y/M/d'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [3, 4],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ps_AF.
    + */
    +goog.i18n.DateTimeSymbols_ps_AF = goog.i18n.DateTimeSymbols_ps;
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_AO.
    + */
    +goog.i18n.DateTimeSymbols_pt_AO = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_CV.
    + */
    +goog.i18n.DateTimeSymbols_pt_CV = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_GW.
    + */
    +goog.i18n.DateTimeSymbols_pt_GW = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_MO.
    + */
    +goog.i18n.DateTimeSymbols_pt_MO = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_MZ.
    + */
    +goog.i18n.DateTimeSymbols_pt_MZ = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_ST.
    + */
    +goog.i18n.DateTimeSymbols_pt_ST = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale pt_TL.
    + */
    +goog.i18n.DateTimeSymbols_pt_TL = {
    +  ERAS: ['a.C.', 'd.C.'],
    +  ERANAMES: ['antes de Cristo', 'depois de Cristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
    +    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
    +  SHORTMONTHS: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set',
    +    'out', 'nov', 'dez'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Ago', 'Set', 'Out', 'Nov', 'Dez'],
    +  WEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
    +    'quinta-feira', 'sexta-feira', 'sábado'],
    +  STANDALONEWEEKDAYS: ['domingo', 'segunda-feira', 'terça-feira',
    +    'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
    +  SHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  STANDALONESHORTWEEKDAYS: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
    +  NARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
    +  SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],
    +  QUARTERS: ['1.º trimestre', '2.º trimestre', '3.º trimestre',
    +    '4.º trimestre'],
    +  AMPMS: ['da manhã', 'da tarde'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'dd/MM/y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'às\' {0}', '{1} \'às\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale qu.
    + */
    +goog.i18n.DateTimeSymbols_qu = {
    +  ERAS: ['BCE', 'd.C.'],
    +  ERANAMES: ['BCE', 'd.C.'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Qulla puquy', 'Hatun puquy', 'Pauqar waray', 'Ayriwa', 'Aymuray',
    +    'Inti raymi', 'Anta Sitwa', 'Qhapaq Sitwa', 'Uma raymi', 'Kantaray',
    +    'Ayamarqʼa', 'Kapaq Raymi'],
    +  STANDALONEMONTHS: ['Qulla puquy', 'Hatun puquy', 'Pauqar waray', 'Ayriwa',
    +    'Aymuray', 'Inti raymi', 'Anta Sitwa', 'Qhapaq Sitwa', 'Uma raymi',
    +    'Kantaray', 'Ayamarqʼa', 'Kapaq Raymi'],
    +  SHORTMONTHS: ['Qul', 'Hat', 'Pau', 'Ayr', 'Aym', 'Int', 'Ant', 'Qha', 'Uma',
    +    'Kan', 'Aya', 'Kap'],
    +  STANDALONESHORTMONTHS: ['Qul', 'Hat', 'Pau', 'Ayr', 'Aym', 'Int', 'Ant',
    +    'Qha', 'Uma', 'Kan', 'Aya', 'Kap'],
    +  WEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes',
    +    'Sábado'],
    +  STANDALONEWEEKDAYS: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves',
    +    'Viernes', 'Sábado'],
    +  SHORTWEEKDAYS: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sab'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'y MMMM d', 'y MMM d', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'hh:mm:ss a', 'hh:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale qu_BO.
    + */
    +goog.i18n.DateTimeSymbols_qu_BO = goog.i18n.DateTimeSymbols_qu;
    +
    +
    +/**
    + * Date/time formatting symbols for locale qu_EC.
    + */
    +goog.i18n.DateTimeSymbols_qu_EC = goog.i18n.DateTimeSymbols_qu;
    +
    +
    +/**
    + * Date/time formatting symbols for locale qu_PE.
    + */
    +goog.i18n.DateTimeSymbols_qu_PE = goog.i18n.DateTimeSymbols_qu;
    +
    +
    +/**
    + * Date/time formatting symbols for locale rm.
    + */
    +goog.i18n.DateTimeSymbols_rm = {
    +  ERAS: ['av. Cr.', 's. Cr.'],
    +  ERANAMES: ['avant Cristus', 'suenter Cristus'],
    +  NARROWMONTHS: ['S', 'F', 'M', 'A', 'M', 'Z', 'F', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['S', 'F', 'M', 'A', 'M', 'Z', 'F', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['schaner', 'favrer', 'mars', 'avrigl', 'matg', 'zercladur',
    +    'fanadur', 'avust', 'settember', 'october', 'november', 'december'],
    +  STANDALONEMONTHS: ['schaner', 'favrer', 'mars', 'avrigl', 'matg', 'zercladur',
    +    'fanadur', 'avust', 'settember', 'october', 'november', 'december'],
    +  SHORTMONTHS: ['schan.', 'favr.', 'mars', 'avr.', 'matg', 'zercl.', 'fan.',
    +    'avust', 'sett.', 'oct.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['schan.', 'favr.', 'mars', 'avr.', 'matg', 'zercl.',
    +    'fan.', 'avust', 'sett.', 'oct.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['dumengia', 'glindesdi', 'mardi', 'mesemna', 'gievgia', 'venderdi',
    +    'sonda'],
    +  STANDALONEWEEKDAYS: ['dumengia', 'glindesdi', 'mardi', 'mesemna', 'gievgia',
    +    'venderdi', 'sonda'],
    +  SHORTWEEKDAYS: ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['du', 'gli', 'ma', 'me', 'gie', 've', 'so'],
    +  NARROWWEEKDAYS: ['D', 'G', 'M', 'M', 'G', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'G', 'M', 'M', 'G', 'V', 'S'],
    +  SHORTQUARTERS: ['1. quartal', '2. quartal', '3. quartal', '4. quartal'],
    +  QUARTERS: ['1. quartal', '2. quartal', '3. quartal', '4. quartal'],
    +  AMPMS: ['am', 'sm'],
    +  DATEFORMATS: ['EEEE, \'ils\' d \'da\' MMMM y', 'd \'da\' MMMM y', 'dd-MM-y',
    +    'dd-MM-yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rm_CH.
    + */
    +goog.i18n.DateTimeSymbols_rm_CH = goog.i18n.DateTimeSymbols_rm;
    +
    +
    +/**
    + * Date/time formatting symbols for locale rn.
    + */
    +goog.i18n.DateTimeSymbols_rn = {
    +  ERAS: ['Mb.Y.', 'Ny.Y'],
    +  ERANAMES: ['Mbere ya Yezu', 'Nyuma ya Yezu'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Nzero', 'Ruhuhuma', 'Ntwarante', 'Ndamukiza', 'Rusama', 'Ruheshi',
    +    'Mukakaro', 'Nyandagaro', 'Nyakanga', 'Gitugutu', 'Munyonyo', 'Kigarama'],
    +  STANDALONEMONTHS: ['Nzero', 'Ruhuhuma', 'Ntwarante', 'Ndamukiza', 'Rusama',
    +    'Ruheshi', 'Mukakaro', 'Nyandagaro', 'Nyakanga', 'Gitugutu', 'Munyonyo',
    +    'Kigarama'],
    +  SHORTMONTHS: ['Mut.', 'Gas.', 'Wer.', 'Mat.', 'Gic.', 'Kam.', 'Nya.', 'Kan.',
    +    'Nze.', 'Ukw.', 'Ugu.', 'Uku.'],
    +  STANDALONESHORTMONTHS: ['Mut.', 'Gas.', 'Wer.', 'Mat.', 'Gic.', 'Kam.',
    +    'Nya.', 'Kan.', 'Nze.', 'Ukw.', 'Ugu.', 'Uku.'],
    +  WEEKDAYS: ['Ku w’indwi', 'Ku wa mbere', 'Ku wa kabiri', 'Ku wa gatatu',
    +    'Ku wa kane', 'Ku wa gatanu', 'Ku wa gatandatu'],
    +  STANDALONEWEEKDAYS: ['Ku w’indwi', 'Ku wa mbere', 'Ku wa kabiri',
    +    'Ku wa gatatu', 'Ku wa kane', 'Ku wa gatanu', 'Ku wa gatandatu'],
    +  SHORTWEEKDAYS: ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'],
    +  STANDALONESHORTWEEKDAYS: ['cu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.',
    +    'gnd.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['I1', 'I2', 'I3', 'I4'],
    +  QUARTERS: ['Igice ca mbere c’umwaka', 'Igice ca kabiri c’umwaka',
    +    'Igice ca gatatu c’umwaka', 'Igice ca kane c’umwaka'],
    +  AMPMS: ['Z.MU.', 'Z.MW.'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rn_BI.
    + */
    +goog.i18n.DateTimeSymbols_rn_BI = goog.i18n.DateTimeSymbols_rn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ro_MD.
    + */
    +goog.i18n.DateTimeSymbols_ro_MD = {
    +  ERAS: ['î.Hr.', 'd.Hr.'],
    +  ERANAMES: ['înainte de Hristos', 'după Hristos'],
    +  NARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie',
    +    'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'],
    +  STANDALONEMONTHS: ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai',
    +    'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie',
    +    'Decembrie'],
    +  SHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.', 'aug.',
    +    'sept.', 'oct.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.',
    +    'aug.', 'sept.', 'oct.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['duminică', 'luni', 'marți', 'miercuri', 'joi', 'vineri',
    +    'sâmbătă'],
    +  STANDALONEWEEKDAYS: ['Duminică', 'Luni', 'Marți', 'Miercuri', 'Joi',
    +    'Vineri', 'Sâmbătă'],
    +  SHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  STANDALONESHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['trim. I', 'trim. II', 'trim. III', 'trim. IV'],
    +  QUARTERS: ['trimestrul I', 'trimestrul al II-lea', 'trimestrul al III-lea',
    +    'trimestrul al IV-lea'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ro_RO.
    + */
    +goog.i18n.DateTimeSymbols_ro_RO = {
    +  ERAS: ['î.Hr.', 'd.Hr.'],
    +  ERANAMES: ['înainte de Hristos', 'după Hristos'],
    +  NARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['I', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie',
    +    'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'],
    +  STANDALONEMONTHS: ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai',
    +    'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie',
    +    'Decembrie'],
    +  SHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.', 'aug.',
    +    'sept.', 'oct.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['ian.', 'feb.', 'mar.', 'apr.', 'mai', 'iun.', 'iul.',
    +    'aug.', 'sept.', 'oct.', 'nov.', 'dec.'],
    +  WEEKDAYS: ['duminică', 'luni', 'marți', 'miercuri', 'joi', 'vineri',
    +    'sâmbătă'],
    +  STANDALONEWEEKDAYS: ['Duminică', 'Luni', 'Marți', 'Miercuri', 'Joi',
    +    'Vineri', 'Sâmbătă'],
    +  SHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  STANDALONESHORTWEEKDAYS: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
    +  NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
    +  SHORTQUARTERS: ['trim. I', 'trim. II', 'trim. III', 'trim. IV'],
    +  QUARTERS: ['trimestrul I', 'trimestrul al II-lea', 'trimestrul al III-lea',
    +    'trimestrul al IV-lea'],
    +  AMPMS: ['a.m.', 'p.m.'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rof.
    + */
    +goog.i18n.DateTimeSymbols_rof = {
    +  ERAS: ['KM', 'BM'],
    +  ERANAMES: ['Kabla ya Mayesu', 'Baada ya Mayesu'],
    +  NARROWMONTHS: ['K', 'K', 'K', 'K', 'T', 'S', 'S', 'N', 'T', 'I', 'I', 'I'],
    +  STANDALONENARROWMONTHS: ['K', 'K', 'K', 'K', 'T', 'S', 'S', 'N', 'T', 'I',
    +    'I', 'I'],
    +  MONTHS: ['Mweri wa kwanza', 'Mweri wa kaili', 'Mweri wa katatu',
    +    'Mweri wa kaana', 'Mweri wa tanu', 'Mweri wa sita', 'Mweri wa saba',
    +    'Mweri wa nane', 'Mweri wa tisa', 'Mweri wa ikumi',
    +    'Mweri wa ikumi na moja', 'Mweri wa ikumi na mbili'],
    +  STANDALONEMONTHS: ['Mweri wa kwanza', 'Mweri wa kaili', 'Mweri wa katatu',
    +    'Mweri wa kaana', 'Mweri wa tanu', 'Mweri wa sita', 'Mweri wa saba',
    +    'Mweri wa nane', 'Mweri wa tisa', 'Mweri wa ikumi',
    +    'Mweri wa ikumi na moja', 'Mweri wa ikumi na mbili'],
    +  SHORTMONTHS: ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10',
    +    'M11', 'M12'],
    +  STANDALONESHORTMONTHS: ['M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9',
    +    'M10', 'M11', 'M12'],
    +  WEEKDAYS: ['Ijumapili', 'Ijumatatu', 'Ijumanne', 'Ijumatano', 'Alhamisi',
    +    'Ijumaa', 'Ijumamosi'],
    +  STANDALONEWEEKDAYS: ['Ijumapili', 'Ijumatatu', 'Ijumanne', 'Ijumatano',
    +    'Alhamisi', 'Ijumaa', 'Ijumamosi'],
    +  SHORTWEEKDAYS: ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'],
    +  STANDALONESHORTWEEKDAYS: ['Ijp', 'Ijt', 'Ijn', 'Ijtn', 'Alh', 'Iju', 'Ijm'],
    +  NARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  STANDALONENARROWWEEKDAYS: ['2', '3', '4', '5', '6', '7', '1'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo ya kwanza', 'Robo ya kaili', 'Robo ya katatu',
    +    'Robo ya kaana'],
    +  AMPMS: ['kang’ama', 'kingoto'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rof_TZ.
    + */
    +goog.i18n.DateTimeSymbols_rof_TZ = goog.i18n.DateTimeSymbols_rof;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_BY.
    + */
    +goog.i18n.DateTimeSymbols_ru_BY = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_KG.
    + */
    +goog.i18n.DateTimeSymbols_ru_KG = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_KZ.
    + */
    +goog.i18n.DateTimeSymbols_ru_KZ = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_MD.
    + */
    +goog.i18n.DateTimeSymbols_ru_MD = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_RU.
    + */
    +goog.i18n.DateTimeSymbols_ru_RU = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y \'г\'.', 'd MMM y \'г\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ru_UA.
    + */
    +goog.i18n.DateTimeSymbols_ru_UA = {
    +  ERAS: ['до н. э.', 'н. э.'],
    +  ERANAMES: ['до н. э.', 'н. э.'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['января', 'февраля', 'марта', 'апреля',
    +    'мая', 'июня', 'июля', 'августа', 'сентября',
    +    'октября', 'ноября', 'декабря'],
    +  STANDALONEMONTHS: ['январь', 'февраль', 'март',
    +    'апрель', 'май', 'июнь', 'июль', 'август',
    +    'сентябрь', 'октябрь', 'ноябрь', 'декабрь'],
    +  SHORTMONTHS: ['янв.', 'февр.', 'марта', 'апр.', 'мая',
    +    'июня', 'июля', 'авг.', 'сент.', 'окт.', 'нояб.',
    +    'дек.'],
    +  STANDALONESHORTMONTHS: ['янв.', 'февр.', 'март', 'апр.',
    +    'май', 'июнь', 'июль', 'авг.', 'сент.', 'окт.',
    +    'нояб.', 'дек.'],
    +  WEEKDAYS: ['воскресенье', 'понедельник',
    +    'вторник', 'среда', 'четверг', 'пятница',
    +    'суббота'],
    +  STANDALONEWEEKDAYS: ['Воскресенье', 'Понедельник',
    +    'Вторник', 'Среда', 'Четверг', 'Пятница',
    +    'Суббота'],
    +  SHORTWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONESHORTWEEKDAYS: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
    +  STANDALONENARROWWEEKDAYS: ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
    +  QUARTERS: ['1-й квартал', '2-й квартал',
    +    '3-й квартал', '4-й квартал'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'г\'.', 'd MMMM y', 'd MMM y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rw.
    + */
    +goog.i18n.DateTimeSymbols_rw = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Mutarama', 'Gashyantare', 'Werurwe', 'Mata', 'Gicuransi', 'Kamena',
    +    'Nyakanga', 'Kanama', 'Nzeli', 'Ukwakira', 'Ugushyingo', 'Ukuboza'],
    +  STANDALONEMONTHS: ['Mutarama', 'Gashyantare', 'Werurwe', 'Mata', 'Gicuransi',
    +    'Kamena', 'Nyakanga', 'Kanama', 'Nzeli', 'Ukwakira', 'Ugushyingo',
    +    'Ukuboza'],
    +  SHORTMONTHS: ['mut.', 'gas.', 'wer.', 'mat.', 'gic.', 'kam.', 'nya.', 'kan.',
    +    'nze.', 'ukw.', 'ugu.', 'uku.'],
    +  STANDALONESHORTMONTHS: ['mut.', 'gas.', 'wer.', 'mat.', 'gic.', 'kam.',
    +    'nya.', 'kan.', 'nze.', 'ukw.', 'ugu.', 'uku.'],
    +  WEEKDAYS: ['Ku cyumweru', 'Kuwa mbere', 'Kuwa kabiri', 'Kuwa gatatu',
    +    'Kuwa kane', 'Kuwa gatanu', 'Kuwa gatandatu'],
    +  STANDALONEWEEKDAYS: ['Ku cyumweru', 'Kuwa mbere', 'Kuwa kabiri',
    +    'Kuwa gatatu', 'Kuwa kane', 'Kuwa gatanu', 'Kuwa gatandatu'],
    +  SHORTWEEKDAYS: ['cyu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.', 'gnd.'],
    +  STANDALONESHORTWEEKDAYS: ['cyu.', 'mbe.', 'kab.', 'gtu.', 'kan.', 'gnu.',
    +    'gnd.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['I1', 'I2', 'I3', 'I4'],
    +  QUARTERS: ['igihembwe cya mbere', 'igihembwe cya kabiri',
    +    'igihembwe cya gatatu', 'igihembwe cya kane'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rw_RW.
    + */
    +goog.i18n.DateTimeSymbols_rw_RW = goog.i18n.DateTimeSymbols_rw;
    +
    +
    +/**
    + * Date/time formatting symbols for locale rwk.
    + */
    +goog.i18n.DateTimeSymbols_rwk = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Kristu', 'Baada ya Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai',
    +    'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi',
    +    'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['utuko', 'kyiukonyi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale rwk_TZ.
    + */
    +goog.i18n.DateTimeSymbols_rwk_TZ = goog.i18n.DateTimeSymbols_rwk;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sah.
    + */
    +goog.i18n.DateTimeSymbols_sah = {
    +  ERAS: ['б. э. и.', 'б. э'],
    +  ERANAMES: ['б. э. и.', 'б. э'],
    +  NARROWMONTHS: ['Т', 'О', 'К', 'М', 'Ы', 'Б', 'О', 'А', 'Б', 'А',
    +    'С', 'А'],
    +  STANDALONENARROWMONTHS: ['Т', 'О', 'К', 'М', 'Ы', 'Б', 'О', 'А', 'Б',
    +    'А', 'С', 'А'],
    +  MONTHS: ['Тохсунньу', 'Олунньу', 'Кулун тутар',
    +    'Муус устар', 'Ыам ыйын', 'Бэс ыйын',
    +    'От ыйын', 'Атырдьых ыйын', 'Балаҕан ыйын',
    +    'Алтынньы', 'Сэтинньи', 'Ахсынньы'],
    +  STANDALONEMONTHS: ['Тохсунньу', 'Олунньу',
    +    'Кулун тутар', 'Муус устар', 'Ыам ыйын',
    +    'Бэс ыйын', 'От ыйын', 'Атырдьых ыйын',
    +    'Балаҕан ыйын', 'Алтынньы', 'Сэтинньи',
    +    'Ахсынньы'],
    +  SHORTMONTHS: ['Тохс', 'Олун', 'Клн_ттр', 'Мус_уст',
    +    'Ыам_йн', 'Бэс_йн', 'От_йн', 'Атрдь_йн',
    +    'Блҕн_йн', 'Алт', 'Сэт', 'Ахс'],
    +  STANDALONESHORTMONTHS: ['Тохс', 'Олун', 'Клн_ттр',
    +    'Мус_уст', 'Ыам_йн', 'Бэс_йн', 'От_йн',
    +    'Атрдь_йн', 'Блҕн_йн', 'Алт', 'Сэт', 'Ахс'],
    +  WEEKDAYS: ['Баскыһыанньа', 'Бэнидиэлинньик',
    +    'Оптуорунньук', 'Сэрэдэ', 'Чэппиэр',
    +    'Бээтиҥсэ', 'Субуота'],
    +  STANDALONEWEEKDAYS: ['Баскыһыанньа',
    +    'Бэнидиэлинньик', 'Оптуорунньук', 'Сэрэдэ',
    +    'Чэппиэр', 'Бээтиҥсэ', 'Субуота'],
    +  SHORTWEEKDAYS: ['Бс', 'Бн', 'Оп', 'Сэ', 'Чп', 'Бэ', 'Сб'],
    +  STANDALONESHORTWEEKDAYS: ['Бс', 'Бн', 'Оп', 'Сэ', 'Чп', 'Бэ',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['Б', 'Б', 'О', 'С', 'Ч', 'Б', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Б', 'Б', 'О', 'С', 'Ч', 'Б', 'С'],
    +  SHORTQUARTERS: ['1-кы кб', '2-с кб', '3-с кб', '4-с кб'],
    +  QUARTERS: ['1-кы кыбаартал', '2-с кыбаартал',
    +    '3-с кыбаартал', '4-с кыбаартал'],
    +  AMPMS: ['ЭИ', 'ЭК'],
    +  DATEFORMATS: ['y \'сыл\' MMMM d \'күнэ\', EEEE', 'y, MMMM d',
    +    'y, MMM d', 'yy/M/d'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sah_RU.
    + */
    +goog.i18n.DateTimeSymbols_sah_RU = goog.i18n.DateTimeSymbols_sah;
    +
    +
    +/**
    + * Date/time formatting symbols for locale saq.
    + */
    +goog.i18n.DateTimeSymbols_saq = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Christo', 'Baada ya Christo'],
    +  NARROWMONTHS: ['O', 'W', 'O', 'O', 'I', 'I', 'S', 'I', 'S', 'T', 'T', 'T'],
    +  STANDALONENARROWMONTHS: ['O', 'W', 'O', 'O', 'I', 'I', 'S', 'I', 'S', 'T',
    +    'T', 'T'],
    +  MONTHS: ['Lapa le obo', 'Lapa le waare', 'Lapa le okuni', 'Lapa le ong’wan',
    +    'Lapa le imet', 'Lapa le ile', 'Lapa le sapa', 'Lapa le isiet',
    +    'Lapa le saal', 'Lapa le tomon', 'Lapa le tomon obo',
    +    'Lapa le tomon waare'],
    +  STANDALONEMONTHS: ['Lapa le obo', 'Lapa le waare', 'Lapa le okuni',
    +    'Lapa le ong’wan', 'Lapa le imet', 'Lapa le ile', 'Lapa le sapa',
    +    'Lapa le isiet', 'Lapa le saal', 'Lapa le tomon', 'Lapa le tomon obo',
    +    'Lapa le tomon waare'],
    +  SHORTMONTHS: ['Obo', 'Waa', 'Oku', 'Ong', 'Ime', 'Ile', 'Sap', 'Isi', 'Saa',
    +    'Tom', 'Tob', 'Tow'],
    +  STANDALONESHORTMONTHS: ['Obo', 'Waa', 'Oku', 'Ong', 'Ime', 'Ile', 'Sap',
    +    'Isi', 'Saa', 'Tom', 'Tob', 'Tow'],
    +  WEEKDAYS: ['Mderot ee are', 'Mderot ee kuni', 'Mderot ee ong’wan',
    +    'Mderot ee inet', 'Mderot ee ile', 'Mderot ee sapa', 'Mderot ee kwe'],
    +  STANDALONEWEEKDAYS: ['Mderot ee are', 'Mderot ee kuni', 'Mderot ee ong’wan',
    +    'Mderot ee inet', 'Mderot ee ile', 'Mderot ee sapa', 'Mderot ee kwe'],
    +  SHORTWEEKDAYS: ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'],
    +  STANDALONESHORTWEEKDAYS: ['Are', 'Kun', 'Ong', 'Ine', 'Ile', 'Sap', 'Kwe'],
    +  NARROWWEEKDAYS: ['A', 'K', 'O', 'I', 'I', 'S', 'K'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'K', 'O', 'I', 'I', 'S', 'K'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['Tesiran', 'Teipa'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale saq_KE.
    + */
    +goog.i18n.DateTimeSymbols_saq_KE = goog.i18n.DateTimeSymbols_saq;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sbp.
    + */
    +goog.i18n.DateTimeSymbols_sbp = {
    +  ERAS: ['AK', 'PK'],
    +  ERANAMES: ['Ashanali uKilisito', 'Pamwandi ya Kilisto'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Mupalangulwa', 'Mwitope', 'Mushende', 'Munyi', 'Mushende Magali',
    +    'Mujimbi', 'Mushipepo', 'Mupuguto', 'Munyense', 'Mokhu', 'Musongandembwe',
    +    'Muhaano'],
    +  STANDALONEMONTHS: ['Mupalangulwa', 'Mwitope', 'Mushende', 'Munyi',
    +    'Mushende Magali', 'Mujimbi', 'Mushipepo', 'Mupuguto', 'Munyense', 'Mokhu',
    +    'Musongandembwe', 'Muhaano'],
    +  SHORTMONTHS: ['Mup', 'Mwi', 'Msh', 'Mun', 'Mag', 'Muj', 'Msp', 'Mpg', 'Mye',
    +    'Mok', 'Mus', 'Muh'],
    +  STANDALONESHORTMONTHS: ['Mup', 'Mwi', 'Msh', 'Mun', 'Mag', 'Muj', 'Msp',
    +    'Mpg', 'Mye', 'Mok', 'Mus', 'Muh'],
    +  WEEKDAYS: ['Mulungu', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alahamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Mulungu', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alahamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Mul', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['M', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['M', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['L1', 'L2', 'L3', 'L4'],
    +  QUARTERS: ['Lobo 1', 'Lobo 2', 'Lobo 3', 'Lobo 4'],
    +  AMPMS: ['Lwamilawu', 'Pashamihe'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sbp_TZ.
    + */
    +goog.i18n.DateTimeSymbols_sbp_TZ = goog.i18n.DateTimeSymbols_sbp;
    +
    +
    +/**
    + * Date/time formatting symbols for locale se.
    + */
    +goog.i18n.DateTimeSymbols_se = {
    +  ERAS: ['o.Kr.', 'm.Kr.'],
    +  ERANAMES: ['ovdal Kristtusa', 'maŋŋel Kristtusa'],
    +  NARROWMONTHS: ['O', 'G', 'N', 'C', 'M', 'G', 'S', 'B', 'Č', 'G', 'S', 'J'],
    +  STANDALONENARROWMONTHS: ['O', 'G', 'N', 'C', 'M', 'G', 'S', 'B', 'Č', 'G',
    +    'S', 'J'],
    +  MONTHS: ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu',
    +    'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu',
    +    'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'],
    +  STANDALONEMONTHS: ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu',
    +    'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu',
    +    'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu',
    +    'juovlamánnu'],
    +  SHORTMONTHS: ['ođđj', 'guov', 'njuk', 'cuo', 'mies', 'geas', 'suoi', 'borg',
    +    'čakč', 'golg', 'skáb', 'juov'],
    +  STANDALONESHORTMONTHS: ['ođđj', 'guov', 'njuk', 'cuo', 'mies', 'geas',
    +    'suoi', 'borg', 'čakč', 'golg', 'skáb', 'juov'],
    +  WEEKDAYS: ['sotnabeaivi', 'vuossárga', 'maŋŋebárga', 'gaskavahkku',
    +    'duorasdat', 'bearjadat', 'lávvardat'],
    +  STANDALONEWEEKDAYS: ['sotnabeaivi', 'vuossárga', 'maŋŋebárga',
    +    'gaskavahkku', 'duorasdat', 'bearjadat', 'lávvardat'],
    +  SHORTWEEKDAYS: ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear', 'láv'],
    +  STANDALONESHORTWEEKDAYS: ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear',
    +    'láv'],
    +  NARROWWEEKDAYS: ['S', 'V', 'M', 'G', 'D', 'B', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'V', 'M', 'G', 'D', 'B', 'L'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['iđitbeaivet', 'eahketbeaivet'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale se_FI.
    + */
    +goog.i18n.DateTimeSymbols_se_FI = {
    +  ERAS: ['o.Kr.', 'm.Kr.'],
    +  ERANAMES: ['ovdal Kristtusa', 'maŋŋel Kristtusa'],
    +  NARROWMONTHS: ['O', 'G', 'N', 'C', 'M', 'G', 'S', 'B', 'Č', 'G', 'S', 'J'],
    +  STANDALONENARROWMONTHS: ['O', 'G', 'N', 'C', 'M', 'G', 'S', 'B', 'Č', 'G',
    +    'S', 'J'],
    +  MONTHS: ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu', 'cuoŋománnu',
    +    'miessemánnu', 'geassemánnu', 'suoidnemánnu', 'borgemánnu',
    +    'čakčamánnu', 'golggotmánnu', 'skábmamánnu', 'juovlamánnu'],
    +  STANDALONEMONTHS: ['ođđajagemánnu', 'guovvamánnu', 'njukčamánnu',
    +    'cuoŋománnu', 'miessemánnu', 'geassemánnu', 'suoidnemánnu',
    +    'borgemánnu', 'čakčamánnu', 'golggotmánnu', 'skábmamánnu',
    +    'juovlamánnu'],
    +  SHORTMONTHS: ['ođđajage', 'guovva', 'njukča', 'cuoŋo', 'miesse', 'geasse',
    +    'suoidne', 'borge', 'čakča', 'golggot', 'skábma', 'juovla'],
    +  STANDALONESHORTMONTHS: ['ođđajage', 'guovva', 'njukča', 'cuoŋo', 'miesse',
    +    'geasse', 'suoidne', 'borge', 'čakča', 'golggot', 'skábma', 'juovla'],
    +  WEEKDAYS: ['aejlege', 'måanta', 'däjsta', 'gaskevahkoe', 'dåarsta',
    +    'bearjadahke', 'laavadahke'],
    +  STANDALONEWEEKDAYS: ['aejlege', 'måanta', 'däjsta', 'gaskevahkoe',
    +    'dåarsta', 'bearjadahke', 'laavadahke'],
    +  SHORTWEEKDAYS: ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear', 'láv'],
    +  STANDALONESHORTWEEKDAYS: ['sotn', 'vuos', 'maŋ', 'gask', 'duor', 'bear',
    +    'láv'],
    +  NARROWWEEKDAYS: ['S', 'M', 'D', 'G', 'D', 'B', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'D', 'G', 'D', 'B', 'L'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['iđitbeaivet', 'eahketbeaivet'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale se_NO.
    + */
    +goog.i18n.DateTimeSymbols_se_NO = goog.i18n.DateTimeSymbols_se;
    +
    +
    +/**
    + * Date/time formatting symbols for locale se_SE.
    + */
    +goog.i18n.DateTimeSymbols_se_SE = goog.i18n.DateTimeSymbols_se;
    +
    +
    +/**
    + * Date/time formatting symbols for locale seh.
    + */
    +goog.i18n.DateTimeSymbols_seh = {
    +  ERAS: ['AC', 'AD'],
    +  ERANAMES: ['Antes de Cristo', 'Anno Domini'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janeiro', 'Fevreiro', 'Marco', 'Abril', 'Maio', 'Junho', 'Julho',
    +    'Augusto', 'Setembro', 'Otubro', 'Novembro', 'Decembro'],
    +  STANDALONEMONTHS: ['Janeiro', 'Fevreiro', 'Marco', 'Abril', 'Maio', 'Junho',
    +    'Julho', 'Augusto', 'Setembro', 'Otubro', 'Novembro', 'Decembro'],
    +  SHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Aug', 'Set',
    +    'Otu', 'Nov', 'Dec'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul',
    +    'Aug', 'Set', 'Otu', 'Nov', 'Dec'],
    +  WEEKDAYS: ['Dimingu', 'Chiposi', 'Chipiri', 'Chitatu', 'Chinai', 'Chishanu',
    +    'Sabudu'],
    +  STANDALONEWEEKDAYS: ['Dimingu', 'Chiposi', 'Chipiri', 'Chitatu', 'Chinai',
    +    'Chishanu', 'Sabudu'],
    +  SHORTWEEKDAYS: ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Dim', 'Pos', 'Pir', 'Tat', 'Nai', 'Sha', 'Sab'],
    +  NARROWWEEKDAYS: ['D', 'P', 'C', 'T', 'N', 'S', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'P', 'C', 'T', 'N', 'S', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d \'de\' MMMM \'de\' y', 'd \'de\' MMMM \'de\' y',
    +    'd \'de\' MMM \'de\' y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale seh_MZ.
    + */
    +goog.i18n.DateTimeSymbols_seh_MZ = goog.i18n.DateTimeSymbols_seh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ses.
    + */
    +goog.i18n.DateTimeSymbols_ses = {
    +  ERAS: ['IJ', 'IZ'],
    +  ERANAMES: ['Isaa jine', 'Isaa zamanoo'],
    +  NARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ',
    +    'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
    +  STANDALONEMONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me',
    +    'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur',
    +    'Deesanbur'],
    +  SHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek',
    +    'Okt', 'Noo', 'Dee'],
    +  STANDALONESHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy',
    +    'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
    +  WEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa', 'Alzuma',
    +    'Asibti'],
    +  STANDALONEWEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa',
    +    'Alzuma', 'Asibti'],
    +  SHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  STANDALONESHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  NARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  SHORTQUARTERS: ['A1', 'A2', 'A3', 'A4'],
    +  QUARTERS: ['Arrubu 1', 'Arrubu 2', 'Arrubu 3', 'Arrubu 4'],
    +  AMPMS: ['Adduha', 'Aluula'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ses_ML.
    + */
    +goog.i18n.DateTimeSymbols_ses_ML = goog.i18n.DateTimeSymbols_ses;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sg.
    + */
    +goog.i18n.DateTimeSymbols_sg = {
    +  ERAS: ['KnK', 'NpK'],
    +  ERANAMES: ['Kôzo na Krîstu', 'Na pekô tî Krîstu'],
    +  NARROWMONTHS: ['N', 'F', 'M', 'N', 'B', 'F', 'L', 'K', 'M', 'N', 'N', 'K'],
    +  STANDALONENARROWMONTHS: ['N', 'F', 'M', 'N', 'B', 'F', 'L', 'K', 'M', 'N',
    +    'N', 'K'],
    +  MONTHS: ['Nyenye', 'Fulundïgi', 'Mbängü', 'Ngubùe', 'Bêläwü', 'Föndo',
    +    'Lengua', 'Kükürü', 'Mvuka', 'Ngberere', 'Nabändüru', 'Kakauka'],
    +  STANDALONEMONTHS: ['Nyenye', 'Fulundïgi', 'Mbängü', 'Ngubùe', 'Bêläwü',
    +    'Föndo', 'Lengua', 'Kükürü', 'Mvuka', 'Ngberere', 'Nabändüru',
    +    'Kakauka'],
    +  SHORTMONTHS: ['Nye', 'Ful', 'Mbä', 'Ngu', 'Bêl', 'Fön', 'Len', 'Kük',
    +    'Mvu', 'Ngb', 'Nab', 'Kak'],
    +  STANDALONESHORTMONTHS: ['Nye', 'Ful', 'Mbä', 'Ngu', 'Bêl', 'Fön', 'Len',
    +    'Kük', 'Mvu', 'Ngb', 'Nab', 'Kak'],
    +  WEEKDAYS: ['Bikua-ôko', 'Bïkua-ûse', 'Bïkua-ptâ', 'Bïkua-usïö',
    +    'Bïkua-okü', 'Lâpôsö', 'Lâyenga'],
    +  STANDALONEWEEKDAYS: ['Bikua-ôko', 'Bïkua-ûse', 'Bïkua-ptâ',
    +    'Bïkua-usïö', 'Bïkua-okü', 'Lâpôsö', 'Lâyenga'],
    +  SHORTWEEKDAYS: ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'],
    +  STANDALONESHORTWEEKDAYS: ['Bk1', 'Bk2', 'Bk3', 'Bk4', 'Bk5', 'Lâp', 'Lây'],
    +  NARROWWEEKDAYS: ['K', 'S', 'T', 'S', 'K', 'P', 'Y'],
    +  STANDALONENARROWWEEKDAYS: ['K', 'S', 'T', 'S', 'K', 'P', 'Y'],
    +  SHORTQUARTERS: ['F4-1', 'F4-2', 'F4-3', 'F4-4'],
    +  QUARTERS: ['Fângbisïö ôko', 'Fângbisïö ûse', 'Fângbisïö otâ',
    +    'Fângbisïö usïö'],
    +  AMPMS: ['ND', 'LK'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sg_CF.
    + */
    +goog.i18n.DateTimeSymbols_sg_CF = goog.i18n.DateTimeSymbols_sg;
    +
    +
    +/**
    + * Date/time formatting symbols for locale shi.
    + */
    +goog.i18n.DateTimeSymbols_shi = {
    +  ERAS: ['ⴷⴰⵄ', 'ⴷⴼⵄ'],
    +  ERANAMES: ['ⴷⴰⵜ ⵏ ⵄⵉⵙⴰ', 'ⴷⴼⴼⵉⵔ ⵏ ⵄⵉⵙⴰ'],
    +  NARROWMONTHS: ['ⵉ', 'ⴱ', 'ⵎ', 'ⵉ', 'ⵎ', 'ⵢ', 'ⵢ', 'ⵖ', 'ⵛ',
    +    'ⴽ', 'ⵏ', 'ⴷ'],
    +  STANDALONENARROWMONTHS: ['ⵉ', 'ⴱ', 'ⵎ', 'ⵉ', 'ⵎ', 'ⵢ', 'ⵢ',
    +    'ⵖ', 'ⵛ', 'ⴽ', 'ⵏ', 'ⴷ'],
    +  MONTHS: ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ',
    +    'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ',
    +    'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ',
    +    'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
    +  STANDALONEMONTHS: ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ',
    +    'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ',
    +    'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ',
    +    'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
    +  SHORTMONTHS: ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ',
    +    'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵜⵓ',
    +    'ⵏⵓⵡ', 'ⴷⵓⵊ'],
    +  STANDALONESHORTMONTHS: ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ',
    +    'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ',
    +    'ⴽⵜⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'],
    +  WEEKDAYS: ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ',
    +    'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⵙⵉⵎⵡⴰⵙ',
    +    'ⴰⵙⵉⴹⵢⴰⵙ'],
    +  STANDALONEWEEKDAYS: ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ',
    +    'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ',
    +    'ⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
    +  SHORTWEEKDAYS: ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ',
    +    'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
    +  STANDALONESHORTWEEKDAYS: ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ',
    +    'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['ⴰⴽ 1', 'ⴰⴽ 2', 'ⴰⴽ 3', 'ⴰⴽ 4'],
    +  QUARTERS: ['ⴰⴽⵕⴰⴹⵢⵓⵔ 1', 'ⴰⴽⵕⴰⴹⵢⵓⵔ 2',
    +    'ⴰⴽⵕⴰⴹⵢⵓⵔ 3', 'ⴰⴽⵕⴰⴹⵢⵓⵔ 4'],
    +  AMPMS: ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale shi_Latn.
    + */
    +goog.i18n.DateTimeSymbols_shi_Latn = {
    +  ERAS: ['daɛ', 'dfɛ'],
    +  ERANAMES: ['dat n ɛisa', 'dffir n ɛisa'],
    +  NARROWMONTHS: ['i', 'b', 'm', 'i', 'm', 'y', 'y', 'ɣ', 'c', 'k', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['i', 'b', 'm', 'i', 'm', 'y', 'y', 'ɣ', 'c', 'k',
    +    'n', 'd'],
    +  MONTHS: ['innayr', 'bṛayṛ', 'maṛṣ', 'ibrir', 'mayyu', 'yunyu',
    +    'yulyuz', 'ɣuct', 'cutanbir', 'ktubr', 'nuwanbir', 'dujanbir'],
    +  STANDALONEMONTHS: ['innayr', 'bṛayṛ', 'maṛṣ', 'ibrir', 'mayyu',
    +    'yunyu', 'yulyuz', 'ɣuct', 'cutanbir', 'ktubr', 'nuwanbir', 'dujanbir'],
    +  SHORTMONTHS: ['inn', 'bṛa', 'maṛ', 'ibr', 'may', 'yun', 'yul', 'ɣuc',
    +    'cut', 'ktu', 'nuw', 'duj'],
    +  STANDALONESHORTMONTHS: ['inn', 'bṛa', 'maṛ', 'ibr', 'may', 'yun', 'yul',
    +    'ɣuc', 'cut', 'ktu', 'nuw', 'duj'],
    +  WEEKDAYS: ['asamas', 'aynas', 'asinas', 'akṛas', 'akwas', 'asimwas',
    +    'asiḍyas'],
    +  STANDALONEWEEKDAYS: ['asamas', 'aynas', 'asinas', 'akṛas', 'akwas',
    +    'asimwas', 'asiḍyas'],
    +  SHORTWEEKDAYS: ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim', 'asiḍ'],
    +  STANDALONESHORTWEEKDAYS: ['asa', 'ayn', 'asi', 'akṛ', 'akw', 'asim',
    +    'asiḍ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['ak 1', 'ak 2', 'ak 3', 'ak 4'],
    +  QUARTERS: ['akṛaḍyur 1', 'akṛaḍyur 2', 'akṛaḍyur 3',
    +    'akṛaḍyur 4'],
    +  AMPMS: ['tifawt', 'tadggʷat'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale shi_Latn_MA.
    + */
    +goog.i18n.DateTimeSymbols_shi_Latn_MA = goog.i18n.DateTimeSymbols_shi_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale shi_Tfng.
    + */
    +goog.i18n.DateTimeSymbols_shi_Tfng = goog.i18n.DateTimeSymbols_shi;
    +
    +
    +/**
    + * Date/time formatting symbols for locale shi_Tfng_MA.
    + */
    +goog.i18n.DateTimeSymbols_shi_Tfng_MA = goog.i18n.DateTimeSymbols_shi;
    +
    +
    +/**
    + * Date/time formatting symbols for locale si_LK.
    + */
    +goog.i18n.DateTimeSymbols_si_LK = {
    +  ERAS: ['ක්‍රි.පූ.', 'ක්‍රි.ව.'],
    +  ERANAMES: ['ක්‍රිස්තු පූර්‍ව',
    +    'ක්‍රිස්තු වර්‍ෂ'],
    +  NARROWMONTHS: ['ජ', 'පෙ', 'මා', 'අ', 'මැ', 'ජූ', 'ජූ',
    +    'අ', 'සැ', 'ඔ', 'නෙ', 'දෙ'],
    +  STANDALONENARROWMONTHS: ['ජ', 'පෙ', 'මා', 'අ', 'මැ', 'ජූ',
    +    'ජූ', 'අ', 'සැ', 'ඔ', 'නෙ', 'දෙ'],
    +  MONTHS: ['ජනවාරි', 'පෙබරවාරි',
    +    'මාර්තු', 'අප්‍රේල්', 'මැයි',
    +    'ජූනි', 'ජූලි', 'අගෝස්තු',
    +    'සැප්තැම්බර්', 'ඔක්තෝබර්',
    +    'නොවැම්බර්', 'දෙසැම්බර්'],
    +  STANDALONEMONTHS: ['ජනවාරි', 'පෙබරවාරි',
    +    'මාර්තු', 'අප්‍රේල්', 'මැයි',
    +    'ජූනි', 'ජූලි', 'අගෝස්තු',
    +    'සැප්තැම්බර්', 'ඔක්තෝබර්',
    +    'නොවැම්බර්', 'දෙසැම්බර්'],
    +  SHORTMONTHS: ['ජන', 'පෙබ', 'මාර්තු',
    +    'අප්‍රේල්', 'මැයි', 'ජූනි', 'ජූලි',
    +    'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
    +  STANDALONESHORTMONTHS: ['ජන', 'පෙබ', 'මාර්',
    +    'අප්‍රේල්', 'මැයි', 'ජූනි', 'ජූලි',
    +    'අගෝ', 'සැප්', 'ඔක්', 'නොවැ', 'දෙසැ'],
    +  WEEKDAYS: ['ඉරිදා', 'සඳුදා',
    +    'අඟහරුවාදා', 'බදාදා',
    +    'බ්‍රහස්පතින්දා', 'සිකුරාදා',
    +    'සෙනසුරාදා'],
    +  STANDALONEWEEKDAYS: ['ඉරිදා', 'සඳුදා',
    +    'අඟහරුවාදා', 'බදාදා',
    +    'බ්‍රහස්පතින්දා', 'සිකුරාදා',
    +    'සෙනසුරාදා'],
    +  SHORTWEEKDAYS: ['ඉරිදා', 'සඳුදා', 'අඟහ',
    +    'බදාදා', 'බ්‍රහස්', 'සිකු', 'සෙන'],
    +  STANDALONESHORTWEEKDAYS: ['ඉරිදා', 'සඳුදා', 'අඟහ',
    +    'බදාදා', 'බ්‍රහස්', 'සිකු', 'සෙන'],
    +  NARROWWEEKDAYS: ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර', 'සි',
    +    'සෙ'],
    +  STANDALONENARROWWEEKDAYS: ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර',
    +    'සි', 'සෙ'],
    +  SHORTQUARTERS: ['කාර්:1', 'කාර්:2', 'කාර්:3',
    +    'කාර්:4'],
    +  QUARTERS: ['1 වන කාර්තුව', '2 වන කාර්තුව',
    +    '3 වන කාර්තුව', '4 වන කාර්තුව'],
    +  AMPMS: ['පෙ.ව.', 'ප.ව.'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['a h.mm.ss zzzz', 'a h.mm.ss z', 'a h.mm.ss', 'a h.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sk_SK.
    + */
    +goog.i18n.DateTimeSymbols_sk_SK = {
    +  ERAS: ['pred Kr.', 'po Kr.'],
    +  ERANAMES: ['pred Kristom', 'po Kristovi'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januára', 'februára', 'marca', 'apríla', 'mája', 'júna',
    +    'júla', 'augusta', 'septembra', 'októbra', 'novembra', 'decembra'],
    +  STANDALONEMONTHS: ['január', 'február', 'marec', 'apríl', 'máj', 'jún',
    +    'júl', 'august', 'september', 'október', 'november', 'december'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug',
    +    'sep', 'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl',
    +    'aug', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok', 'piatok',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['nedeľa', 'pondelok', 'utorok', 'streda', 'štvrtok',
    +    'piatok', 'sobota'],
    +  SHORTWEEKDAYS: ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
    +  STANDALONESHORTWEEKDAYS: ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
    +  NARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. štvrťrok', '2. štvrťrok', '3. štvrťrok',
    +    '4. štvrťrok'],
    +  AMPMS: ['dopoludnia', 'odpoludnia'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. M. y', 'dd.MM.yy'],
    +  TIMEFORMATS: ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sl_SI.
    + */
    +goog.i18n.DateTimeSymbols_sl_SI = {
    +  ERAS: ['pr. n. št.', 'po Kr.'],
    +  ERANAMES: ['pred našim štetjem', 'naše štetje'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij',
    +    'avgust', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'marec', 'april', 'maj', 'junij',
    +    'julij', 'avgust', 'september', 'oktober', 'november', 'december'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mar.', 'apr.', 'maj', 'jun.', 'jul.', 'avg.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'avg', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek', 'petek',
    +    'sobota'],
    +  STANDALONEWEEKDAYS: ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek',
    +    'petek', 'sobota'],
    +  SHORTWEEKDAYS: ['ned.', 'pon.', 'tor.', 'sre.', 'čet.', 'pet.', 'sob.'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'tor', 'sre', 'čet', 'pet', 'sob'],
    +  NARROWWEEKDAYS: ['n', 'p', 't', 's', 'č', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 't', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. četrtletje', '2. četrtletje', '3. četrtletje',
    +    '4. četrtletje'],
    +  AMPMS: ['dop.', 'pop.'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y', 'dd. MMMM y', 'd. MMM y', 'd. MM. yy'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale smn.
    + */
    +goog.i18n.DateTimeSymbols_smn = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09', 'M10',
    +    'M11', 'M12'],
    +  STANDALONEMONTHS: ['uđđâivemáánu', 'kuovâmáánu', 'njuhčâmáánu',
    +    'cuáŋuimáánu', 'vyesimáánu', 'kesimáánu', 'syeinimáánu',
    +    'porgemáánu', 'čohčâmáánu', 'roovvâdmáánu', 'skammâmáánu',
    +    'juovlâmáánu'],
    +  SHORTMONTHS: ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07', 'M08', 'M09',
    +    'M10', 'M11', 'M12'],
    +  STANDALONESHORTMONTHS: ['M01', 'M02', 'M03', 'M04', 'M05', 'M06', 'M07',
    +    'M08', 'M09', 'M10', 'M11', 'M12'],
    +  WEEKDAYS: ['pasepeeivi', 'vuossaargâ', 'majebaargâ', 'koskoho',
    +    'tuorâstuv', 'vástuppeeivi', 'lávurduv'],
    +  STANDALONEWEEKDAYS: ['pasepeivi', 'vuossargâ', 'majebargâ', 'koskokko',
    +    'tuorâstâh', 'vástuppeivi', 'lávurdâh'],
    +  SHORTWEEKDAYS: ['pa', 'vu', 'ma', 'ko', 'tu', 'vá', 'lá'],
    +  STANDALONESHORTWEEKDAYS: ['pa', 'vu', 'ma', 'ko', 'tu', 'vá', 'lá'],
    +  NARROWWEEKDAYS: ['P', 'V', 'M', 'K', 'T', 'V', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['1. niälj.', '2. niälj.', '3. niälj.', '4. niälj.'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale smn_FI.
    + */
    +goog.i18n.DateTimeSymbols_smn_FI = goog.i18n.DateTimeSymbols_smn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sn.
    + */
    +goog.i18n.DateTimeSymbols_sn = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kristo asati auya', 'Kristo ashaya'],
    +  NARROWMONTHS: ['N', 'K', 'K', 'K', 'C', 'C', 'C', 'N', 'G', 'G', 'M', 'Z'],
    +  STANDALONENARROWMONTHS: ['N', 'K', 'K', 'K', 'C', 'C', 'C', 'N', 'G', 'G',
    +    'M', 'Z'],
    +  MONTHS: ['Ndira', 'Kukadzi', 'Kurume', 'Kubvumbi', 'Chivabvu', 'Chikumi',
    +    'Chikunguru', 'Nyamavhuvhu', 'Gunyana', 'Gumiguru', 'Mbudzi', 'Zvita'],
    +  STANDALONEMONTHS: ['Ndira', 'Kukadzi', 'Kurume', 'Kubvumbi', 'Chivabvu',
    +    'Chikumi', 'Chikunguru', 'Nyamavhuvhu', 'Gunyana', 'Gumiguru', 'Mbudzi',
    +    'Zvita'],
    +  SHORTMONTHS: ['Ndi', 'Kuk', 'Kur', 'Kub', 'Chv', 'Chk', 'Chg', 'Nya', 'Gun',
    +    'Gum', 'Mb', 'Zvi'],
    +  STANDALONESHORTMONTHS: ['Ndi', 'Kuk', 'Kur', 'Kub', 'Chv', 'Chk', 'Chg',
    +    'Nya', 'Gun', 'Gum', 'Mb', 'Zvi'],
    +  WEEKDAYS: ['Svondo', 'Muvhuro', 'Chipiri', 'Chitatu', 'China', 'Chishanu',
    +    'Mugovera'],
    +  STANDALONEWEEKDAYS: ['Svondo', 'Muvhuro', 'Chipiri', 'Chitatu', 'China',
    +    'Chishanu', 'Mugovera'],
    +  SHORTWEEKDAYS: ['Svo', 'Muv', 'Chip', 'Chit', 'Chin', 'Chis', 'Mug'],
    +  STANDALONESHORTWEEKDAYS: ['Svo', 'Muv', 'Chip', 'Chit', 'Chin', 'Chis',
    +    'Mug'],
    +  NARROWWEEKDAYS: ['S', 'M', 'C', 'C', 'C', 'C', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'C', 'C', 'C', 'C', 'M'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kota 1', 'Kota 2', 'Kota 3', 'Kota 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sn_ZW.
    + */
    +goog.i18n.DateTimeSymbols_sn_ZW = goog.i18n.DateTimeSymbols_sn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale so.
    + */
    +goog.i18n.DateTimeSymbols_so = {
    +  ERAS: ['CK', 'CD'],
    +  ERANAMES: ['Ciise ka hor (CS)', 'Ciise ka dib (CS)'],
    +  NARROWMONTHS: ['K', 'L', 'S', 'A', 'S', 'L', 'T', 'S', 'S', 'T', 'K', 'L'],
    +  STANDALONENARROWMONTHS: ['K', 'L', 'S', 'A', 'S', 'L', 'T', 'S', 'S', 'T',
    +    'K', 'L'],
    +  MONTHS: ['Bisha Koobaad', 'Bisha Labaad', 'Bisha Saddexaad', 'Bisha Afraad',
    +    'Bisha Shanaad', 'Bisha Lixaad', 'Bisha Todobaad', 'Bisha Sideedaad',
    +    'Bisha Sagaalaad', 'Bisha Tobnaad', 'Bisha Kow iyo Tobnaad',
    +    'Bisha Laba iyo Tobnaad'],
    +  STANDALONEMONTHS: ['Bisha Koobaad', 'Bisha Labaad', 'Bisha Saddexaad',
    +    'Bisha Afraad', 'Bisha Shanaad', 'Bisha Lixaad', 'Bisha Todobaad',
    +    'Bisha Sideedaad', 'Bisha Sagaalaad', 'Bisha Tobnaad',
    +    'Bisha Kow iyo Tobnaad', 'Bisha Laba iyo Tobnaad'],
    +  SHORTMONTHS: ['Kob', 'Lab', 'Sad', 'Afr', 'Sha', 'Lix', 'Tod', 'Sid', 'Sag',
    +    'Tob', 'KIT', 'LIT'],
    +  STANDALONESHORTMONTHS: ['Kob', 'Lab', 'Sad', 'Afr', 'Sha', 'Lix', 'Tod',
    +    'Sid', 'Sag', 'Tob', 'KIT', 'LIT'],
    +  WEEKDAYS: ['Axad', 'Isniin', 'Talaado', 'Arbaco', 'Khamiis', 'Jimco',
    +    'Sabti'],
    +  STANDALONEWEEKDAYS: ['Axad', 'Isniin', 'Talaado', 'Arbaco', 'Khamiis',
    +    'Jimco', 'Sabti'],
    +  SHORTWEEKDAYS: ['Axd', 'Isn', 'Tal', 'Arb', 'Kha', 'Jim', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Axd', 'Isn', 'Tal', 'Arb', 'Kha', 'Jim', 'Sab'],
    +  NARROWWEEKDAYS: ['A', 'I', 'T', 'A', 'K', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'I', 'T', 'A', 'K', 'J', 'S'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Rubaca 1aad', 'Rubaca 2aad', 'Rubaca 3aad', 'Rubaca 4aad'],
    +  AMPMS: ['sn.', 'gn.'],
    +  DATEFORMATS: ['EEEE, MMMM dd, y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale so_DJ.
    + */
    +goog.i18n.DateTimeSymbols_so_DJ = goog.i18n.DateTimeSymbols_so;
    +
    +
    +/**
    + * Date/time formatting symbols for locale so_ET.
    + */
    +goog.i18n.DateTimeSymbols_so_ET = goog.i18n.DateTimeSymbols_so;
    +
    +
    +/**
    + * Date/time formatting symbols for locale so_KE.
    + */
    +goog.i18n.DateTimeSymbols_so_KE = goog.i18n.DateTimeSymbols_so;
    +
    +
    +/**
    + * Date/time formatting symbols for locale so_SO.
    + */
    +goog.i18n.DateTimeSymbols_so_SO = goog.i18n.DateTimeSymbols_so;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sq_AL.
    + */
    +goog.i18n.DateTimeSymbols_sq_AL = {
    +  ERAS: ['p.e.r.', 'e.r.'],
    +  ERANAMES: ['para erës së re', 'erës së re'],
    +  NARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T',
    +    'N', 'D'],
    +  MONTHS: ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik',
    +    'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'],
    +  STANDALONEMONTHS: ['Janar', 'Shkurt', 'Mars', 'Prill', 'Maj', 'Qershor',
    +    'Korrik', 'Gusht', 'Shtator', 'Tetor', 'Nëntor', 'Dhjetor'],
    +  SHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor', 'Gsh', 'Sht',
    +    'Tet', 'Nën', 'Dhj'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor',
    +    'Gsh', 'Sht', 'Tet', 'Nën', 'Dhj'],
    +  WEEKDAYS: ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte',
    +    'e premte', 'e shtunë'],
    +  STANDALONEWEEKDAYS: ['E diel', 'E hënë', 'E martë', 'E mërkurë',
    +    'E enjte', 'E premte', 'E shtunë'],
    +  SHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  STANDALONESHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  NARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  SHORTQUARTERS: ['tremujori I', 'tremujori II', 'tremujori III',
    +    'tremujori IV'],
    +  QUARTERS: ['tremujori i parë', 'tremujori i dytë', 'tremujori i tretë',
    +    'tremujori i katërt'],
    +  AMPMS: ['paradite', 'pasdite'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'në\' {0}', '{1} \'në\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sq_MK.
    + */
    +goog.i18n.DateTimeSymbols_sq_MK = {
    +  ERAS: ['p.e.r.', 'e.r.'],
    +  ERANAMES: ['para erës së re', 'erës së re'],
    +  NARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T',
    +    'N', 'D'],
    +  MONTHS: ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik',
    +    'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'],
    +  STANDALONEMONTHS: ['Janar', 'Shkurt', 'Mars', 'Prill', 'Maj', 'Qershor',
    +    'Korrik', 'Gusht', 'Shtator', 'Tetor', 'Nëntor', 'Dhjetor'],
    +  SHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor', 'Gsh', 'Sht',
    +    'Tet', 'Nën', 'Dhj'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor',
    +    'Gsh', 'Sht', 'Tet', 'Nën', 'Dhj'],
    +  WEEKDAYS: ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte',
    +    'e premte', 'e shtunë'],
    +  STANDALONEWEEKDAYS: ['E diel', 'E hënë', 'E martë', 'E mërkurë',
    +    'E enjte', 'E premte', 'E shtunë'],
    +  SHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  STANDALONESHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  NARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  SHORTQUARTERS: ['tremujori I', 'tremujori II', 'tremujori III',
    +    'tremujori IV'],
    +  QUARTERS: ['tremujori i parë', 'tremujori i dytë', 'tremujori i tretë',
    +    'tremujori i katërt'],
    +  AMPMS: ['paradite', 'pasdite'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'në\' {0}', '{1} \'në\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sq_XK.
    + */
    +goog.i18n.DateTimeSymbols_sq_XK = {
    +  ERAS: ['p.e.r.', 'e.r.'],
    +  ERANAMES: ['para erës së re', 'erës së re'],
    +  NARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'S', 'M', 'P', 'M', 'Q', 'K', 'G', 'S', 'T',
    +    'N', 'D'],
    +  MONTHS: ['janar', 'shkurt', 'mars', 'prill', 'maj', 'qershor', 'korrik',
    +    'gusht', 'shtator', 'tetor', 'nëntor', 'dhjetor'],
    +  STANDALONEMONTHS: ['Janar', 'Shkurt', 'Mars', 'Prill', 'Maj', 'Qershor',
    +    'Korrik', 'Gusht', 'Shtator', 'Tetor', 'Nëntor', 'Dhjetor'],
    +  SHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor', 'Gsh', 'Sht',
    +    'Tet', 'Nën', 'Dhj'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Shk', 'Mar', 'Pri', 'Maj', 'Qer', 'Kor',
    +    'Gsh', 'Sht', 'Tet', 'Nën', 'Dhj'],
    +  WEEKDAYS: ['e diel', 'e hënë', 'e martë', 'e mërkurë', 'e enjte',
    +    'e premte', 'e shtunë'],
    +  STANDALONEWEEKDAYS: ['E diel', 'E hënë', 'E martë', 'E mërkurë',
    +    'E enjte', 'E premte', 'E shtunë'],
    +  SHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  STANDALONESHORTWEEKDAYS: ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
    +  NARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
    +  SHORTQUARTERS: ['tremujori I', 'tremujori II', 'tremujori III',
    +    'tremujori IV'],
    +  QUARTERS: ['tremujori i parë', 'tremujori i dytë', 'tremujori i tretë',
    +    'tremujori i katërt'],
    +  AMPMS: ['paradite', 'pasdite'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd.M.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} \'në\' {0}', '{1} \'në\' {0}', '{1}, {0}',
    +    '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_sr_Cyrl = {
    +  ERAS: ['п. н. е.', 'н. е.'],
    +  ERANAMES: ['Пре нове ере', 'Нове ере'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануар', 'фебруар', 'март', 'април', 'мај',
    +    'јун', 'јул', 'август', 'септембар', 'октобар',
    +    'новембар', 'децембар'],
    +  STANDALONEMONTHS: ['јануар', 'фебруар', 'март', 'април',
    +    'мај', 'јун', 'јул', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  SHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај', 'јун',
    +    'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  STANDALONESHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај',
    +    'јун', 'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  WEEKDAYS: ['недеља', 'понедељак', 'уторак', 'среда',
    +    'четвртак', 'петак', 'субота'],
    +  STANDALONEWEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'среда', 'четвртак', 'петак', 'субота'],
    +  SHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сре', 'чет', 'пет',
    +    'суб'],
    +  STANDALONESHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сре', 'чет',
    +    'пет', 'суб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['К1', 'К2', 'К3', 'К4'],
    +  QUARTERS: ['Прво тромесечје', 'Друго тромесечје',
    +    'Треће тромесечје', 'Четврто тромесечје'],
    +  AMPMS: ['пре подне', 'по подне'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'dd.MM.y.', 'd.M.yy.'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Cyrl_BA.
    + */
    +goog.i18n.DateTimeSymbols_sr_Cyrl_BA = {
    +  ERAS: ['п. н. е.', 'н. е.'],
    +  ERANAMES: ['Пре нове ере', 'Нове ере'],
    +  NARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с', 'о',
    +    'н', 'д'],
    +  STANDALONENARROWMONTHS: ['ј', 'ф', 'м', 'а', 'м', 'ј', 'ј', 'а', 'с',
    +    'о', 'н', 'д'],
    +  MONTHS: ['јануар', 'фебруар', 'март', 'април', 'мај',
    +    'јуни', 'јули', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  STANDALONEMONTHS: ['јануар', 'фебруар', 'март', 'април',
    +    'мај', 'јун', 'јул', 'август', 'септембар',
    +    'октобар', 'новембар', 'децембар'],
    +  SHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај', 'јун',
    +    'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  STANDALONESHORTMONTHS: ['јан', 'феб', 'мар', 'апр', 'мај',
    +    'јун', 'јул', 'авг', 'сеп', 'окт', 'нов', 'дец'],
    +  WEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'сриједа', 'четвртак', 'петак', 'субота'],
    +  STANDALONEWEEKDAYS: ['недеља', 'понедељак', 'уторак',
    +    'среда', 'четвртак', 'петак', 'субота'],
    +  SHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сри', 'чет', 'пет',
    +    'суб'],
    +  STANDALONESHORTWEEKDAYS: ['нед', 'пон', 'уто', 'сре', 'чет',
    +    'пет', 'суб'],
    +  NARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  STANDALONENARROWWEEKDAYS: ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
    +  SHORTQUARTERS: ['К1', 'К2', 'К3', 'К4'],
    +  QUARTERS: ['Прво тромесечје', 'Друго тромесечје',
    +    'Треће тромесечје', 'Четврто тромесечје'],
    +  AMPMS: ['пре подне', 'по подне'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'y-MM-dd', 'yy-MM-dd'],
    +  TIMEFORMATS: [
    +    'HH \'часова\', mm \'минута\', ss \'секунди\' zzzz',
    +    'HH.mm.ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Cyrl_ME.
    + */
    +goog.i18n.DateTimeSymbols_sr_Cyrl_ME = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Cyrl_RS.
    + */
    +goog.i18n.DateTimeSymbols_sr_Cyrl_RS = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Cyrl_XK.
    + */
    +goog.i18n.DateTimeSymbols_sr_Cyrl_XK = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Latn.
    + */
    +goog.i18n.DateTimeSymbols_sr_Latn = {
    +  ERAS: ['p. n. e.', 'n. e.'],
    +  ERANAMES: ['Pre nove ere', 'Nove ere'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul', 'avgust',
    +    'septembar', 'oktobar', 'novembar', 'decembar'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul',
    +    'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sep',
    +    'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'avg', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak', 'petak',
    +    'subota'],
    +  STANDALONEWEEKDAYS: ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak',
    +    'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sre', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sre', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Prvo tromesečje', 'Drugo tromesečje', 'Treće tromesečje',
    +    'Četvrto tromesečje'],
    +  AMPMS: ['pre podne', 'po podne'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'dd.MM.y.', 'd.M.yy.'],
    +  TIMEFORMATS: ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Latn_BA.
    + */
    +goog.i18n.DateTimeSymbols_sr_Latn_BA = {
    +  ERAS: ['p. n. e.', 'n. e.'],
    +  ERANAMES: ['Pre nove ere', 'Nove ere'],
    +  NARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o', 'n', 'd'],
    +  STANDALONENARROWMONTHS: ['j', 'f', 'm', 'a', 'm', 'j', 'j', 'a', 's', 'o',
    +    'n', 'd'],
    +  MONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'juni', 'juli',
    +    'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'],
    +  STANDALONEMONTHS: ['januar', 'februar', 'mart', 'april', 'maj', 'jun', 'jul',
    +    'avgust', 'septembar', 'oktobar', 'novembar', 'decembar'],
    +  SHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sep',
    +    'okt', 'nov', 'dec'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul',
    +    'avg', 'sep', 'okt', 'nov', 'dec'],
    +  WEEKDAYS: ['nedelja', 'ponedeljak', 'utorak', 'srijeda', 'četvrtak', 'petak',
    +    'subota'],
    +  STANDALONEWEEKDAYS: ['nedelja', 'ponedeljak', 'utorak', 'sreda', 'četvrtak',
    +    'petak', 'subota'],
    +  SHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
    +  STANDALONESHORTWEEKDAYS: ['ned', 'pon', 'uto', 'sre', 'čet', 'pet', 'sub'],
    +  NARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  STANDALONENARROWWEEKDAYS: ['n', 'p', 'u', 's', 'č', 'p', 's'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Prvo tromesečje', 'Drugo tromesečje', 'Treće tromesečje',
    +    'Četvrto tromesečje'],
    +  AMPMS: ['pre podne', 'po podne'],
    +  DATEFORMATS: ['EEEE, dd. MMMM y.', 'dd. MMMM y.', 'y-MM-dd', 'yy-MM-dd'],
    +  TIMEFORMATS: ['HH \'časova\', mm \'minuta\', ss \'sekundi\' zzzz',
    +    'HH.mm.ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Latn_ME.
    + */
    +goog.i18n.DateTimeSymbols_sr_Latn_ME = goog.i18n.DateTimeSymbols_sr_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Latn_RS.
    + */
    +goog.i18n.DateTimeSymbols_sr_Latn_RS = goog.i18n.DateTimeSymbols_sr_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sr_Latn_XK.
    + */
    +goog.i18n.DateTimeSymbols_sr_Latn_XK = goog.i18n.DateTimeSymbols_sr_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ss.
    + */
    +goog.i18n.DateTimeSymbols_ss = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Bhimbidvwane', 'iNdlovana', 'iNdlovu-lenkhulu', 'Mabasa',
    +    'iNkhwekhweti', 'iNhlaba', 'Kholwane', 'iNgci', 'iNyoni', 'iMphala',
    +    'Lweti', 'iNgongoni'],
    +  STANDALONEMONTHS: ['Bhimbidvwane', 'iNdlovana', 'iNdlovu-lenkhulu', 'Mabasa',
    +    'iNkhwekhweti', 'iNhlaba', 'Kholwane', 'iNgci', 'iNyoni', 'iMphala',
    +    'Lweti', 'iNgongoni'],
    +  SHORTMONTHS: ['Bhi', 'Van', 'Vol', 'Mab', 'Nkh', 'Nhl', 'Kho', 'Ngc', 'Nyo',
    +    'Mph', 'Lwe', 'Ngo'],
    +  STANDALONESHORTMONTHS: ['Bhi', 'Van', 'Vol', 'Mab', 'Nkh', 'Nhl', 'Kho',
    +    'Ngc', 'Nyo', 'Mph', 'Lwe', 'Ngo'],
    +  WEEKDAYS: ['Lisontfo', 'uMsombuluko', 'Lesibili', 'Lesitsatfu', 'Lesine',
    +    'Lesihlanu', 'uMgcibelo'],
    +  STANDALONEWEEKDAYS: ['Lisontfo', 'uMsombuluko', 'Lesibili', 'Lesitsatfu',
    +    'Lesine', 'Lesihlanu', 'uMgcibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tsa', 'Ne', 'Hla', 'Mgc'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tsa', 'Ne', 'Hla', 'Mgc'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ss_SZ.
    + */
    +goog.i18n.DateTimeSymbols_ss_SZ = goog.i18n.DateTimeSymbols_ss;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ss_ZA.
    + */
    +goog.i18n.DateTimeSymbols_ss_ZA = goog.i18n.DateTimeSymbols_ss;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ssy.
    + */
    +goog.i18n.DateTimeSymbols_ssy = {
    +  ERAS: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  ERANAMES: ['Yaasuusuk Duma', 'Yaasuusuk Wadir'],
    +  NARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D', 'X', 'K'],
    +  STANDALONENARROWMONTHS: ['Q', 'N', 'C', 'A', 'C', 'Q', 'Q', 'L', 'W', 'D',
    +    'X', 'K'],
    +  MONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis', 'Caxah Alsa',
    +    'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli', 'Ximoli',
    +    'Kaxxa Garablu'],
    +  STANDALONEMONTHS: ['Qunxa Garablu', 'Kudo', 'Ciggilta Kudo', 'Agda Baxis',
    +    'Caxah Alsa', 'Qasa Dirri', 'Qado Dirri', 'Liiqen', 'Waysu', 'Diteli',
    +    'Ximoli', 'Kaxxa Garablu'],
    +  SHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad', 'Leq', 'Way',
    +    'Dit', 'Xim', 'Kax'],
    +  STANDALONESHORTMONTHS: ['Qun', 'Nah', 'Cig', 'Agd', 'Cax', 'Qas', 'Qad',
    +    'Leq', 'Way', 'Dit', 'Xim', 'Kax'],
    +  WEEKDAYS: ['Naba Sambat', 'Sani', 'Salus', 'Rabuq', 'Camus', 'Jumqata',
    +    'Qunxa Sambat'],
    +  STANDALONEWEEKDAYS: ['Naba Sambat', 'Sani', 'Salus', 'Rabuq', 'Camus',
    +    'Jumqata', 'Qunxa Sambat'],
    +  SHORTWEEKDAYS: ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'],
    +  STANDALONESHORTWEEKDAYS: ['Nab', 'San', 'Sal', 'Rab', 'Cam', 'Jum', 'Qun'],
    +  NARROWWEEKDAYS: ['N', 'S', 'S', 'R', 'C', 'J', 'Q'],
    +  STANDALONENARROWWEEKDAYS: ['N', 'S', 'S', 'R', 'C', 'J', 'Q'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['saaku', 'carra'],
    +  DATEFORMATS: ['EEEE, MMMM dd, y', 'dd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ssy_ER.
    + */
    +goog.i18n.DateTimeSymbols_ssy_ER = goog.i18n.DateTimeSymbols_ssy;
    +
    +
    +/**
    + * Date/time formatting symbols for locale sv_AX.
    + */
    +goog.i18n.DateTimeSymbols_sv_AX = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['före Kristus', 'efter Kristus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli',
    +    'augusti', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni',
    +    'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mars', 'apr.', 'maj', 'juni', 'juli', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mars', 'Apr.', 'Maj', 'Juni', 'Juli',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag',
    +    'lördag'],
    +  STANDALONEWEEKDAYS: ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag',
    +    'Fredag', 'Lördag'],
    +  SHORTWEEKDAYS: ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
    +  STANDALONESHORTWEEKDAYS: ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1:a kvartalet', '2:a kvartalet', '3:e kvartalet',
    +    '4:e kvartalet'],
    +  AMPMS: ['fm', 'em'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['\'kl\'. HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sv_FI.
    + */
    +goog.i18n.DateTimeSymbols_sv_FI = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['före Kristus', 'efter Kristus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli',
    +    'augusti', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni',
    +    'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mars', 'apr.', 'maj', 'juni', 'juli', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mars', 'Apr.', 'Maj', 'Juni', 'Juli',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag',
    +    'lördag'],
    +  STANDALONEWEEKDAYS: ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag',
    +    'Fredag', 'Lördag'],
    +  SHORTWEEKDAYS: ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
    +  STANDALONESHORTWEEKDAYS: ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1:a kvartalet', '2:a kvartalet', '3:e kvartalet',
    +    '4:e kvartalet'],
    +  AMPMS: ['fm', 'em'],
    +  DATEFORMATS: ['EEEE\'en\' \'den\' d:\'e\' MMMM y', 'd MMMM y', 'd MMM y',
    +    'dd-MM-y'],
    +  TIMEFORMATS: ['\'kl\'. HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sv_SE.
    + */
    +goog.i18n.DateTimeSymbols_sv_SE = {
    +  ERAS: ['f.Kr.', 'e.Kr.'],
    +  ERANAMES: ['före Kristus', 'efter Kristus'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli',
    +    'augusti', 'september', 'oktober', 'november', 'december'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni',
    +    'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
    +  SHORTMONTHS: ['jan.', 'feb.', 'mars', 'apr.', 'maj', 'juni', 'juli', 'aug.',
    +    'sep.', 'okt.', 'nov.', 'dec.'],
    +  STANDALONESHORTMONTHS: ['Jan.', 'Feb.', 'Mars', 'Apr.', 'Maj', 'Juni', 'Juli',
    +    'Aug.', 'Sep.', 'Okt.', 'Nov.', 'Dec.'],
    +  WEEKDAYS: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag',
    +    'lördag'],
    +  STANDALONEWEEKDAYS: ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag',
    +    'Fredag', 'Lördag'],
    +  SHORTWEEKDAYS: ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
    +  STANDALONESHORTWEEKDAYS: ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['1:a kvartalet', '2:a kvartalet', '3:e kvartalet',
    +    '4:e kvartalet'],
    +  AMPMS: ['fm', 'em'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['\'kl\'. HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 3
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sw_KE.
    + */
    +goog.i18n.DateTimeSymbols_sw_KE = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kabla ya Kristo', 'Baada ya Kristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONESHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  QUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sw_TZ.
    + */
    +goog.i18n.DateTimeSymbols_sw_TZ = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kabla ya Kristo', 'Baada ya Kristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONESHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  QUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale sw_UG.
    + */
    +goog.i18n.DateTimeSymbols_sw_UG = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['Kabla ya Kristo', 'Baada ya Kristo'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni', 'Julai',
    +    'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprili', 'Mei', 'Juni',
    +    'Julai', 'Agosti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONESHORTWEEKDAYS: ['Jumapili', 'Jumatatu', 'Jumanne', 'Jumatano',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  QUARTERS: ['Robo ya 1', 'Robo ya 2', 'Robo ya 3', 'Robo ya 4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale swc.
    + */
    +goog.i18n.DateTimeSymbols_swc = {
    +  ERAS: ['mbele ya Y', 'kisha ya Y'],
    +  ERANAMES: ['mbele ya Yezu Kristo', 'kisha ya Yezu Kristo'],
    +  NARROWMONTHS: ['k', 'p', 't', 'i', 't', 's', 's', 'm', 't', 'k', 'm', 'm'],
    +  STANDALONENARROWMONTHS: ['k', 'p', 't', 'i', 't', 's', 's', 'm', 't', 'k',
    +    'm', 'm'],
    +  MONTHS: ['mwezi ya kwanja', 'mwezi ya pili', 'mwezi ya tatu', 'mwezi ya ine',
    +    'mwezi ya tanu', 'mwezi ya sita', 'mwezi ya saba', 'mwezi ya munane',
    +    'mwezi ya tisa', 'mwezi ya kumi', 'mwezi ya kumi na moya',
    +    'mwezi ya kumi ya mbili'],
    +  STANDALONEMONTHS: ['mwezi ya kwanja', 'mwezi ya pili', 'mwezi ya tatu',
    +    'mwezi ya ine', 'mwezi ya tanu', 'mwezi ya sita', 'mwezi ya saba',
    +    'mwezi ya munane', 'mwezi ya tisa', 'mwezi ya kumi',
    +    'mwezi ya kumi na moya', 'mwezi ya kumi ya mbili'],
    +  SHORTMONTHS: ['mkw', 'mpi', 'mtu', 'min', 'mtn', 'mst', 'msb', 'mun', 'mts',
    +    'mku', 'mkm', 'mkb'],
    +  STANDALONESHORTMONTHS: ['mkw', 'mpi', 'mtu', 'min', 'mtn', 'mst', 'msb',
    +    'mun', 'mts', 'mku', 'mkm', 'mkb'],
    +  WEEKDAYS: ['siku ya yenga', 'siku ya kwanza', 'siku ya pili', 'siku ya tatu',
    +    'siku ya ine', 'siku ya tanu', 'siku ya sita'],
    +  STANDALONEWEEKDAYS: ['siku ya yenga', 'siku ya kwanza', 'siku ya pili',
    +    'siku ya tatu', 'siku ya ine', 'siku ya tanu', 'siku ya sita'],
    +  SHORTWEEKDAYS: ['yen', 'kwa', 'pil', 'tat', 'ine', 'tan', 'sit'],
    +  STANDALONESHORTWEEKDAYS: ['yen', 'kwa', 'pil', 'tat', 'ine', 'tan', 'sit'],
    +  NARROWWEEKDAYS: ['y', 'k', 'p', 't', 'i', 't', 's'],
    +  STANDALONENARROWWEEKDAYS: ['y', 'k', 'p', 't', 'i', 't', 's'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['ya asubuyi', 'ya muchana'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale swc_CD.
    + */
    +goog.i18n.DateTimeSymbols_swc_CD = goog.i18n.DateTimeSymbols_swc;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ta_IN.
    + */
    +goog.i18n.DateTimeSymbols_ta_IN = {
    +  ERAS: ['கி.மு.', 'கி.பி.'],
    +  ERANAMES: ['கிறிஸ்துவுக்கு முன்',
    +    'அனோ டோமினி'],
    +  NARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ', 'ஜூ',
    +    'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  STANDALONENARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ',
    +    'ஜூ', 'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  MONTHS: ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்',
    +    'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை',
    +    'ஆகஸ்ட்', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  STANDALONEMONTHS: ['ஜனவரி', 'பிப்ரவரி',
    +    'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்',
    +    'ஜூலை', 'ஆகஸ்டு', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  SHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.',
    +    'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.',
    +    'அக்.', 'நவ.', 'டிச.'],
    +  STANDALONESHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.',
    +    'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.',
    +    'செப்.', 'அக்.', 'நவ.', 'டிச.'],
    +  WEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  STANDALONEWEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  SHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONESHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  NARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONENARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  SHORTQUARTERS: ['காலாண்டு1', 'காலாண்டு2',
    +    'காலாண்டு3', 'காலாண்டு4'],
    +  QUARTERS: ['முதல் காலாண்டு',
    +    'இரண்டாம் காலாண்டு',
    +    'மூன்றாம் காலாண்டு',
    +    'நான்காம் காலாண்டு'],
    +  AMPMS: ['முற்பகல்', 'பிற்பகல்'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} ’அன்று’ {0}',
    +    '{1} ’அன்று’ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ta_LK.
    + */
    +goog.i18n.DateTimeSymbols_ta_LK = {
    +  ERAS: ['கி.மு.', 'கி.பி.'],
    +  ERANAMES: ['கிறிஸ்துவுக்கு முன்',
    +    'அனோ டோமினி'],
    +  NARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ', 'ஜூ',
    +    'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  STANDALONENARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ',
    +    'ஜூ', 'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  MONTHS: ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்',
    +    'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை',
    +    'ஆகஸ்ட்', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  STANDALONEMONTHS: ['ஜனவரி', 'பிப்ரவரி',
    +    'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்',
    +    'ஜூலை', 'ஆகஸ்டு', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  SHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.',
    +    'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.',
    +    'அக்.', 'நவ.', 'டிச.'],
    +  STANDALONESHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.',
    +    'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.',
    +    'செப்.', 'அக்.', 'நவ.', 'டிச.'],
    +  WEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  STANDALONEWEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  SHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONESHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  NARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONENARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  SHORTQUARTERS: ['காலாண்டு1', 'காலாண்டு2',
    +    'காலாண்டு3', 'காலாண்டு4'],
    +  QUARTERS: ['முதல் காலாண்டு',
    +    'இரண்டாம் காலாண்டு',
    +    'மூன்றாம் காலாண்டு',
    +    'நான்காம் காலாண்டு'],
    +  AMPMS: ['முற்பகல்', 'பிற்பகல்'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} ’அன்று’ {0}',
    +    '{1} ’அன்று’ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ta_MY.
    + */
    +goog.i18n.DateTimeSymbols_ta_MY = {
    +  ERAS: ['கி.மு.', 'கி.பி.'],
    +  ERANAMES: ['கிறிஸ்துவுக்கு முன்',
    +    'அனோ டோமினி'],
    +  NARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ', 'ஜூ',
    +    'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  STANDALONENARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ',
    +    'ஜூ', 'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  MONTHS: ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்',
    +    'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை',
    +    'ஆகஸ்ட்', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  STANDALONEMONTHS: ['ஜனவரி', 'பிப்ரவரி',
    +    'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்',
    +    'ஜூலை', 'ஆகஸ்டு', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  SHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.',
    +    'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.',
    +    'அக்.', 'நவ.', 'டிச.'],
    +  STANDALONESHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.',
    +    'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.',
    +    'செப்.', 'அக்.', 'நவ.', 'டிச.'],
    +  WEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  STANDALONEWEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  SHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONESHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  NARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONENARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  SHORTQUARTERS: ['காலாண்டு1', 'காலாண்டு2',
    +    'காலாண்டு3', 'காலாண்டு4'],
    +  QUARTERS: ['முதல் காலாண்டு',
    +    'இரண்டாம் காலாண்டு',
    +    'மூன்றாம் காலாண்டு',
    +    'நான்காம் காலாண்டு'],
    +  AMPMS: ['முற்பகல்', 'பிற்பகல்'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} ’அன்று’ {0}',
    +    '{1} ’அன்று’ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ta_SG.
    + */
    +goog.i18n.DateTimeSymbols_ta_SG = {
    +  ERAS: ['கி.மு.', 'கி.பி.'],
    +  ERANAMES: ['கிறிஸ்துவுக்கு முன்',
    +    'அனோ டோமினி'],
    +  NARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ', 'ஜூ',
    +    'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  STANDALONENARROWMONTHS: ['ஜ', 'பி', 'மா', 'ஏ', 'மே', 'ஜூ',
    +    'ஜூ', 'ஆ', 'செ', 'அ', 'ந', 'டி'],
    +  MONTHS: ['ஜனவரி', 'பிப்ரவரி', 'மார்ச்',
    +    'ஏப்ரல்', 'மே', 'ஜூன்', 'ஜூலை',
    +    'ஆகஸ்ட்', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  STANDALONEMONTHS: ['ஜனவரி', 'பிப்ரவரி',
    +    'மார்ச்', 'ஏப்ரல்', 'மே', 'ஜூன்',
    +    'ஜூலை', 'ஆகஸ்டு', 'செப்டம்பர்',
    +    'அக்டோபர்', 'நவம்பர்',
    +    'டிசம்பர்'],
    +  SHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.', 'ஏப்.',
    +    'மே', 'ஜூன்', 'ஜூலை', 'ஆக.', 'செப்.',
    +    'அக்.', 'நவ.', 'டிச.'],
    +  STANDALONESHORTMONTHS: ['ஜன.', 'பிப்.', 'மார்.',
    +    'ஏப்.', 'மே', 'ஜூன்', 'ஜூலை', 'ஆக.',
    +    'செப்.', 'அக்.', 'நவ.', 'டிச.'],
    +  WEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  STANDALONEWEEKDAYS: ['ஞாயிறு', 'திங்கள்',
    +    'செவ்வாய்', 'புதன்', 'வியாழன்',
    +    'வெள்ளி', 'சனி'],
    +  SHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONESHORTWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  NARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ',
    +    'ச'],
    +  STANDALONENARROWWEEKDAYS: ['ஞா', 'தி', 'செ', 'பு', 'வி',
    +    'வெ', 'ச'],
    +  SHORTQUARTERS: ['காலாண்டு1', 'காலாண்டு2',
    +    'காலாண்டு3', 'காலாண்டு4'],
    +  QUARTERS: ['முதல் காலாண்டு',
    +    'இரண்டாம் காலாண்டு',
    +    'மூன்றாம் காலாண்டு',
    +    'நான்காம் காலாண்டு'],
    +  AMPMS: ['முற்பகல்', 'பிற்பகல்'],
    +  DATEFORMATS: ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} ’அன்று’ {0}',
    +    '{1} ’அன்று’ {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale te_IN.
    + */
    +goog.i18n.DateTimeSymbols_te_IN = {
    +  ERAS: ['క్రీపూ', 'క్రీశ'],
    +  ERANAMES: ['క్రీస్తు పూర్వం',
    +    'క్రీస్తు శకం'],
    +  NARROWMONTHS: ['జ', 'ఫి', 'మా', 'ఏ', 'మే', 'జూ', 'జు',
    +    'ఆ', 'సె', 'అ', 'న', 'డి'],
    +  STANDALONENARROWMONTHS: ['జ', 'ఫి', 'మా', 'ఏ', 'మే', 'జూ',
    +    'జు', 'ఆ', 'సె', 'అ', 'న', 'డి'],
    +  MONTHS: ['జనవరి', 'ఫిబ్రవరి', 'మార్చి',
    +    'ఏప్రిల్', 'మే', 'జూన్', 'జులై',
    +    'ఆగస్టు', 'సెప్టెంబర్',
    +    'అక్టోబర్', 'నవంబర్',
    +    'డిసెంబర్'],
    +  STANDALONEMONTHS: ['జనవరి', 'ఫిబ్రవరి',
    +    'మార్చి', 'ఏప్రిల్', 'మే', 'జూన్',
    +    'జులై', 'ఆగస్టు', 'సెప్టెంబర్',
    +    'అక్టోబర్', 'నవంబర్',
    +    'డిసెంబర్'],
    +  SHORTMONTHS: ['జన', 'ఫిబ్ర', 'మార్చి',
    +    'ఏప్రి', 'మే', 'జూన్', 'జులై', 'ఆగ',
    +    'సెప్టెం', 'అక్టో', 'నవం', 'డిసెం'],
    +  STANDALONESHORTMONTHS: ['జన', 'ఫిబ్ర', 'మార్చి',
    +    'ఏప్రి', 'మే', 'జూన్', 'జులై',
    +    'ఆగస్టు', 'సెప్టెం', 'అక్టో',
    +    'నవం', 'డిసెం'],
    +  WEEKDAYS: ['ఆదివారం', 'సోమవారం',
    +    'మంగళవారం', 'బుధవారం',
    +    'గురువారం', 'శుక్రవారం',
    +    'శనివారం'],
    +  STANDALONEWEEKDAYS: ['ఆదివారం', 'సోమవారం',
    +    'మంగళవారం', 'బుధవారం',
    +    'గురువారం', 'శుక్రవారం',
    +    'శనివారం'],
    +  SHORTWEEKDAYS: ['ఆది', 'సోమ', 'మంగళ', 'బుధ',
    +    'గురు', 'శుక్ర', 'శని'],
    +  STANDALONESHORTWEEKDAYS: ['ఆది', 'సోమ', 'మంగళ',
    +    'బుధ', 'గురు', 'శుక్ర', 'శని'],
    +  NARROWWEEKDAYS: ['ఆ', 'సో', 'మ', 'బు', 'గు', 'శు', 'శ'],
    +  STANDALONENARROWWEEKDAYS: ['ఆ', 'సో', 'మ', 'బు', 'గు',
    +    'శు', 'శ'],
    +  SHORTQUARTERS: ['త్రై1', 'త్రై2', 'త్రై3',
    +    'త్రై4'],
    +  QUARTERS: ['1వ త్రైమాసం', '2వ త్రైమాసం',
    +    '3వ త్రైమాసం', '4వ త్రైమాసం'],
    +  AMPMS: ['[AM]', '[PM]'],
    +  DATEFORMATS: ['d, MMMM y, EEEE', 'd MMMM, y', 'd MMM, y', 'dd-MM-yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale teo.
    + */
    +goog.i18n.DateTimeSymbols_teo = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Christo', 'Baada ya Christo'],
    +  NARROWMONTHS: ['R', 'M', 'K', 'D', 'M', 'M', 'J', 'P', 'S', 'T', 'L', 'P'],
    +  STANDALONENARROWMONTHS: ['R', 'M', 'K', 'D', 'M', 'M', 'J', 'P', 'S', 'T',
    +    'L', 'P'],
    +  MONTHS: ['Orara', 'Omuk', 'Okwamg’', 'Odung’el', 'Omaruk',
    +    'Omodok’king’ol', 'Ojola', 'Opedel', 'Osokosokoma', 'Otibar', 'Olabor',
    +    'Opoo'],
    +  STANDALONEMONTHS: ['Orara', 'Omuk', 'Okwamg’', 'Odung’el', 'Omaruk',
    +    'Omodok’king’ol', 'Ojola', 'Opedel', 'Osokosokoma', 'Otibar', 'Olabor',
    +    'Opoo'],
    +  SHORTMONTHS: ['Rar', 'Muk', 'Kwa', 'Dun', 'Mar', 'Mod', 'Jol', 'Ped', 'Sok',
    +    'Tib', 'Lab', 'Poo'],
    +  STANDALONESHORTMONTHS: ['Rar', 'Muk', 'Kwa', 'Dun', 'Mar', 'Mod', 'Jol',
    +    'Ped', 'Sok', 'Tib', 'Lab', 'Poo'],
    +  WEEKDAYS: ['Nakaejuma', 'Nakaebarasa', 'Nakaare', 'Nakauni', 'Nakaung’on',
    +    'Nakakany', 'Nakasabiti'],
    +  STANDALONEWEEKDAYS: ['Nakaejuma', 'Nakaebarasa', 'Nakaare', 'Nakauni',
    +    'Nakaung’on', 'Nakakany', 'Nakasabiti'],
    +  SHORTWEEKDAYS: ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'],
    +  STANDALONESHORTWEEKDAYS: ['Jum', 'Bar', 'Aar', 'Uni', 'Ung', 'Kan', 'Sab'],
    +  NARROWWEEKDAYS: ['J', 'B', 'A', 'U', 'U', 'K', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'B', 'A', 'U', 'U', 'K', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Akwota abe', 'Akwota Aane', 'Akwota auni', 'Akwota Aung’on'],
    +  AMPMS: ['Taparachu', 'Ebongi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale teo_KE.
    + */
    +goog.i18n.DateTimeSymbols_teo_KE = goog.i18n.DateTimeSymbols_teo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale teo_UG.
    + */
    +goog.i18n.DateTimeSymbols_teo_UG = goog.i18n.DateTimeSymbols_teo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale th_TH.
    + */
    +goog.i18n.DateTimeSymbols_th_TH = {
    +  ERAS: ['ปีก่อน ค.ศ.', 'ค.ศ.'],
    +  ERANAMES: ['ปีก่อนคริสต์ศักราช',
    +    'คริสต์ศักราช'],
    +  NARROWMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  STANDALONENARROWMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  MONTHS: ['มกราคม', 'กุมภาพันธ์',
    +    'มีนาคม', 'เมษายน', 'พฤษภาคม',
    +    'มิถุนายน', 'กรกฎาคม',
    +    'สิงหาคม', 'กันยายน', 'ตุลาคม',
    +    'พฤศจิกายน', 'ธันวาคม'],
    +  STANDALONEMONTHS: ['มกราคม', 'กุมภาพันธ์',
    +    'มีนาคม', 'เมษายน', 'พฤษภาคม',
    +    'มิถุนายน', 'กรกฎาคม',
    +    'สิงหาคม', 'กันยายน', 'ตุลาคม',
    +    'พฤศจิกายน', 'ธันวาคม'],
    +  SHORTMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  STANDALONESHORTMONTHS: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.',
    +    'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.',
    +    'พ.ย.', 'ธ.ค.'],
    +  WEEKDAYS: ['วันอาทิตย์', 'วันจันทร์',
    +    'วันอังคาร', 'วันพุธ',
    +    'วันพฤหัสบดี', 'วันศุกร์',
    +    'วันเสาร์'],
    +  STANDALONEWEEKDAYS: ['วันอาทิตย์',
    +    'วันจันทร์', 'วันอังคาร',
    +    'วันพุธ', 'วันพฤหัสบดี',
    +    'วันศุกร์', 'วันเสาร์'],
    +  SHORTWEEKDAYS: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
    +  STANDALONESHORTWEEKDAYS: ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.',
    +    'ศ.', 'ส.'],
    +  NARROWWEEKDAYS: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
    +  STANDALONENARROWWEEKDAYS: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ',
    +    'ส'],
    +  SHORTQUARTERS: ['ไตรมาส 1', 'ไตรมาส 2',
    +    'ไตรมาส 3', 'ไตรมาส 4'],
    +  QUARTERS: ['ไตรมาส 1', 'ไตรมาส 2',
    +    'ไตรมาส 3', 'ไตรมาส 4'],
    +  AMPMS: ['ก่อนเที่ยง', 'หลังเที่ยง'],
    +  DATEFORMATS: ['EEEEที่ d MMMM G y', 'd MMMM G y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: [
    +    'H นาฬิกา mm นาที ss วินาที zzzz',
    +    'H นาฬิกา mm นาที ss วินาที z', 'HH:mm:ss',
    +    'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ti.
    + */
    +goog.i18n.DateTimeSymbols_ti = {
    +  ERAS: ['ዓ/ዓ', 'ዓ/ም'],
    +  ERANAMES: ['ዓ/ዓ', 'ዓ/ም'],
    +  NARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ', 'ኦ', 'ሴ',
    +    'ኦ', 'ኖ', 'ዲ'],
    +  STANDALONENARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ',
    +    'ኦ', 'ሴ', 'ኦ', 'ኖ', 'ዲ'],
    +  MONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች', 'ኤፕረል',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት', 'ሴፕቴምበር',
    +    'ኦክተውበር', 'ኖቬምበር', 'ዲሴምበር'],
    +  STANDALONEMONTHS: ['ጃንዩወሪ', 'ፌብሩወሪ', 'ማርች',
    +    'ኤፕረል', 'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስት',
    +    'ሴፕቴምበር', 'ኦክተውበር', 'ኖቬምበር',
    +    'ዲሴምበር'],
    +  SHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ', 'ሜይ',
    +    'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ', 'ኖቬም',
    +    'ዲሴም'],
    +  STANDALONESHORTMONTHS: ['ጃንዩ', 'ፌብሩ', 'ማርች', 'ኤፕረ',
    +    'ሜይ', 'ጁን', 'ጁላይ', 'ኦገስ', 'ሴፕቴ', 'ኦክተ',
    +    'ኖቬም', 'ዲሴም'],
    +  WEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሠሉስ', 'ረቡዕ', 'ኃሙስ',
    +    'ዓርቢ', 'ቀዳም'],
    +  STANDALONEWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሠሉስ', 'ረቡዕ',
    +    'ኃሙስ', 'ዓርቢ', 'ቀዳም'],
    +  SHORTWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሠሉስ', 'ረቡዕ',
    +    'ኃሙስ', 'ዓርቢ', 'ቀዳም'],
    +  STANDALONESHORTWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሠሉስ',
    +    'ረቡዕ', 'ኃሙስ', 'ዓርቢ', 'ቀዳም'],
    +  NARROWWEEKDAYS: ['ሰ', 'ሰ', 'ሠ', 'ረ', 'ኃ', 'ዓ', 'ቀ'],
    +  STANDALONENARROWWEEKDAYS: ['ሰ', 'ሰ', 'ሠ', 'ረ', 'ኃ', 'ዓ', 'ቀ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'],
    +  DATEFORMATS: ['EEEE፣ dd MMMM መዓልቲ y G', 'dd MMMM y', 'dd-MMM-y',
    +    'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ti_ER.
    + */
    +goog.i18n.DateTimeSymbols_ti_ER = {
    +  ERAS: ['ዓ/ዓ', 'ዓ/ም'],
    +  ERANAMES: ['ዓ/ዓ', 'ዓ/ም'],
    +  NARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ', 'ኦ', 'ሴ',
    +    'ኦ', 'ኖ', 'ዲ'],
    +  STANDALONENARROWMONTHS: ['ጃ', 'ፌ', 'ማ', 'ኤ', 'ሜ', 'ጁ', 'ጁ',
    +    'ኦ', 'ሴ', 'ኦ', 'ኖ', 'ዲ'],
    +  MONTHS: ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ',
    +    'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም',
    +    'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'],
    +  STANDALONEMONTHS: ['ጥሪ', 'ለካቲት', 'መጋቢት', 'ሚያዝያ',
    +    'ግንቦት', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከረም',
    +    'ጥቅምቲ', 'ሕዳር', 'ታሕሳስ'],
    +  SHORTMONTHS: ['ጥሪ', 'ለካቲ', 'መጋቢ', 'ሚያዝ', 'ግንቦ',
    +    'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም', 'ሕዳር',
    +    'ታሕሳ'],
    +  STANDALONESHORTMONTHS: ['ጥሪ', 'ለካቲ', 'መጋቢ', 'ሚያዝ',
    +    'ግንቦ', 'ሰነ', 'ሓምለ', 'ነሓሰ', 'መስከ', 'ጥቅም',
    +    'ሕዳር', 'ታሕሳ'],
    +  WEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ', 'ሓሙስ',
    +    'ዓርቢ', 'ቀዳም'],
    +  STANDALONEWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ',
    +    'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
    +  SHORTWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሰሉስ', 'ረቡዕ',
    +    'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
    +  STANDALONESHORTWEEKDAYS: ['ሰንበት', 'ሰኑይ', 'ሰሉስ',
    +    'ረቡዕ', 'ሓሙስ', 'ዓርቢ', 'ቀዳም'],
    +  NARROWWEEKDAYS: ['ሰ', 'ሰ', 'ሠ', 'ረ', 'ኃ', 'ዓ', 'ቀ'],
    +  STANDALONENARROWWEEKDAYS: ['ሰ', 'ሰ', 'ሠ', 'ረ', 'ኃ', 'ዓ', 'ቀ'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['ንጉሆ ሰዓተ', 'ድሕር ሰዓት'],
    +  DATEFORMATS: ['EEEE፡ dd MMMM መዓልቲ y G', 'dd MMMM y', 'dd-MMM-y',
    +    'dd/MM/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ti_ET.
    + */
    +goog.i18n.DateTimeSymbols_ti_ET = goog.i18n.DateTimeSymbols_ti;
    +
    +
    +/**
    + * Date/time formatting symbols for locale tn.
    + */
    +goog.i18n.DateTimeSymbols_tn = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Ferikgong', 'Tlhakole', 'Mopitlo', 'Moranang', 'Motsheganang',
    +    'Seetebosigo', 'Phukwi', 'Phatwe', 'Lwetse', 'Diphalane', 'Ngwanatsele',
    +    'Sedimonthole'],
    +  STANDALONEMONTHS: ['Ferikgong', 'Tlhakole', 'Mopitlo', 'Moranang',
    +    'Motsheganang', 'Seetebosigo', 'Phukwi', 'Phatwe', 'Lwetse', 'Diphalane',
    +    'Ngwanatsele', 'Sedimonthole'],
    +  SHORTMONTHS: ['Fer', 'Tlh', 'Mop', 'Mor', 'Mot', 'See', 'Phu', 'Pha', 'Lwe',
    +    'Dip', 'Ngw', 'Sed'],
    +  STANDALONESHORTMONTHS: ['Fer', 'Tlh', 'Mop', 'Mor', 'Mot', 'See', 'Phu',
    +    'Pha', 'Lwe', 'Dip', 'Ngw', 'Sed'],
    +  WEEKDAYS: ['Tshipi', 'Mosopulogo', 'Labobedi', 'Laboraro', 'Labone',
    +    'Labotlhano', 'Matlhatso'],
    +  STANDALONEWEEKDAYS: ['Tshipi', 'Mosopulogo', 'Labobedi', 'Laboraro', 'Labone',
    +    'Labotlhano', 'Matlhatso'],
    +  SHORTWEEKDAYS: ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tla', 'Mat'],
    +  STANDALONESHORTWEEKDAYS: ['Tsh', 'Mos', 'Bed', 'Rar', 'Ne', 'Tla', 'Mat'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale tn_BW.
    + */
    +goog.i18n.DateTimeSymbols_tn_BW = goog.i18n.DateTimeSymbols_tn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale tn_ZA.
    + */
    +goog.i18n.DateTimeSymbols_tn_ZA = goog.i18n.DateTimeSymbols_tn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale to.
    + */
    +goog.i18n.DateTimeSymbols_to = {
    +  ERAS: ['KM', 'TS'],
    +  ERANAMES: ['ki muʻa', 'taʻu ʻo Sīsū'],
    +  NARROWMONTHS: ['S', 'F', 'M', 'E', 'M', 'S', 'S', 'A', 'S', 'O', 'N', 'T'],
    +  STANDALONENARROWMONTHS: ['S', 'F', 'M', 'E', 'M', 'S', 'S', 'A', 'S', 'O',
    +    'N', 'T'],
    +  MONTHS: ['Sānuali', 'Fēpueli', 'Maʻasi', 'ʻEpeleli', 'Mē', 'Sune',
    +    'Siulai', 'ʻAokosi', 'Sepitema', 'ʻOkatopa', 'Nōvema', 'Tīsema'],
    +  STANDALONEMONTHS: ['Sānuali', 'Fēpueli', 'Maʻasi', 'ʻEpeleli', 'Mē',
    +    'Sune', 'Siulai', 'ʻAokosi', 'Sepitema', 'ʻOkatopa', 'Nōvema',
    +    'Tīsema'],
    +  SHORTMONTHS: ['Sān', 'Fēp', 'Maʻa', 'ʻEpe', 'Mē', 'Sun', 'Siu', 'ʻAok',
    +    'Sep', 'ʻOka', 'Nōv', 'Tīs'],
    +  STANDALONESHORTMONTHS: ['Sān', 'Fēp', 'Maʻa', 'ʻEpe', 'Mē', 'Sun', 'Siu',
    +    'ʻAok', 'Sep', 'ʻOka', 'Nōv', 'Tīs'],
    +  WEEKDAYS: ['Sāpate', 'Mōnite', 'Tūsite', 'Pulelulu', 'Tuʻapulelulu',
    +    'Falaite', 'Tokonaki'],
    +  STANDALONEWEEKDAYS: ['Sāpate', 'Mōnite', 'Tūsite', 'Pulelulu',
    +    'Tuʻapulelulu', 'Falaite', 'Tokonaki'],
    +  SHORTWEEKDAYS: ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal', 'Tok'],
    +  STANDALONESHORTWEEKDAYS: ['Sāp', 'Mōn', 'Tūs', 'Pul', 'Tuʻa', 'Fal',
    +    'Tok'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'P', 'T', 'F', 'T'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'P', 'T', 'F', 'T'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['kuata ʻuluaki', 'kuata ua', 'kuata tolu', 'kuata fā'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale to_TO.
    + */
    +goog.i18n.DateTimeSymbols_to_TO = goog.i18n.DateTimeSymbols_to;
    +
    +
    +/**
    + * Date/time formatting symbols for locale tr_CY.
    + */
    +goog.i18n.DateTimeSymbols_tr_CY = {
    +  ERAS: ['MÖ', 'MS'],
    +  ERANAMES: ['Milattan Önce', 'Milattan Sonra'],
    +  NARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E', 'K', 'A'],
    +  STANDALONENARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E',
    +    'K', 'A'],
    +  MONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz',
    +    'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  STANDALONEMONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
    +    'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  SHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl',
    +    'Eki', 'Kas', 'Ara'],
    +  STANDALONESHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem',
    +    'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
    +  WEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma',
    +    'Cumartesi'],
    +  STANDALONEWEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe',
    +    'Cuma', 'Cumartesi'],
    +  SHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  STANDALONESHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  NARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  SHORTQUARTERS: ['Ç1', 'Ç2', 'Ç3', 'Ç4'],
    +  QUARTERS: ['1. çeyrek', '2. çeyrek', '3. çeyrek', '4. çeyrek'],
    +  AMPMS: ['ÖÖ', 'ÖS'],
    +  DATEFORMATS: ['d MMMM y EEEE', 'd MMMM y', 'd MMM y', 'd MM y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale tr_TR.
    + */
    +goog.i18n.DateTimeSymbols_tr_TR = {
    +  ERAS: ['MÖ', 'MS'],
    +  ERANAMES: ['Milattan Önce', 'Milattan Sonra'],
    +  NARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E', 'K', 'A'],
    +  STANDALONENARROWMONTHS: ['O', 'Ş', 'M', 'N', 'M', 'H', 'T', 'A', 'E', 'E',
    +    'K', 'A'],
    +  MONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz',
    +    'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  STANDALONEMONTHS: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
    +    'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
    +  SHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl',
    +    'Eki', 'Kas', 'Ara'],
    +  STANDALONESHORTMONTHS: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem',
    +    'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
    +  WEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma',
    +    'Cumartesi'],
    +  STANDALONEWEEKDAYS: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe',
    +    'Cuma', 'Cumartesi'],
    +  SHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  STANDALONESHORTWEEKDAYS: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
    +  NARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  STANDALONENARROWWEEKDAYS: ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
    +  SHORTQUARTERS: ['Ç1', 'Ç2', 'Ç3', 'Ç4'],
    +  QUARTERS: ['1. çeyrek', '2. çeyrek', '3. çeyrek', '4. çeyrek'],
    +  AMPMS: ['ÖÖ', 'ÖS'],
    +  DATEFORMATS: ['d MMMM y EEEE', 'd MMMM y', 'd MMM y', 'd MM y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ts.
    + */
    +goog.i18n.DateTimeSymbols_ts = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Sunguti', 'Nyenyenyani', 'Nyenyankulu', 'Dzivamisoko', 'Mudyaxihi',
    +    'Khotavuxika', 'Mawuwani', 'Mhawuri', 'Ndzhati', 'Nhlangula', 'Hukuri',
    +    'N’wendzamhala'],
    +  STANDALONEMONTHS: ['Sunguti', 'Nyenyenyani', 'Nyenyankulu', 'Dzivamisoko',
    +    'Mudyaxihi', 'Khotavuxika', 'Mawuwani', 'Mhawuri', 'Ndzhati', 'Nhlangula',
    +    'Hukuri', 'N’wendzamhala'],
    +  SHORTMONTHS: ['Sun', 'Yan', 'Kul', 'Dzi', 'Mud', 'Kho', 'Maw', 'Mha', 'Ndz',
    +    'Nhl', 'Huk', 'N’w'],
    +  STANDALONESHORTMONTHS: ['Sun', 'Yan', 'Kul', 'Dzi', 'Mud', 'Kho', 'Maw',
    +    'Mha', 'Ndz', 'Nhl', 'Huk', 'N’w'],
    +  WEEKDAYS: ['Sonto', 'Musumbhunuku', 'Ravumbirhi', 'Ravunharhu', 'Ravumune',
    +    'Ravuntlhanu', 'Mugqivela'],
    +  STANDALONEWEEKDAYS: ['Sonto', 'Musumbhunuku', 'Ravumbirhi', 'Ravunharhu',
    +    'Ravumune', 'Ravuntlhanu', 'Mugqivela'],
    +  SHORTWEEKDAYS: ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mus', 'Bir', 'Har', 'Ne', 'Tlh', 'Mug'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kotara yo sungula', 'Kotara ya vumbirhi', 'Kotara ya vunharhu',
    +    'Kotara ya vumune'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ts_ZA.
    + */
    +goog.i18n.DateTimeSymbols_ts_ZA = goog.i18n.DateTimeSymbols_ts;
    +
    +
    +/**
    + * Date/time formatting symbols for locale twq.
    + */
    +goog.i18n.DateTimeSymbols_twq = {
    +  ERAS: ['IJ', 'IZ'],
    +  ERANAMES: ['Isaa jine', 'Isaa zamanoo'],
    +  NARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Ž', 'F', 'M', 'A', 'M', 'Ž', 'Ž', 'U', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me', 'Žuweŋ',
    +    'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur', 'Deesanbur'],
    +  STANDALONEMONTHS: ['Žanwiye', 'Feewiriye', 'Marsi', 'Awiril', 'Me',
    +    'Žuweŋ', 'Žuyye', 'Ut', 'Sektanbur', 'Oktoobur', 'Noowanbur',
    +    'Deesanbur'],
    +  SHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy', 'Ut', 'Sek',
    +    'Okt', 'Noo', 'Dee'],
    +  STANDALONESHORTMONTHS: ['Žan', 'Fee', 'Mar', 'Awi', 'Me', 'Žuw', 'Žuy',
    +    'Ut', 'Sek', 'Okt', 'Noo', 'Dee'],
    +  WEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa', 'Alzuma',
    +    'Asibti'],
    +  STANDALONEWEEKDAYS: ['Alhadi', 'Atinni', 'Atalaata', 'Alarba', 'Alhamiisa',
    +    'Alzuma', 'Asibti'],
    +  SHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  STANDALONESHORTWEEKDAYS: ['Alh', 'Ati', 'Ata', 'Ala', 'Alm', 'Alz', 'Asi'],
    +  NARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['H', 'T', 'T', 'L', 'L', 'L', 'S'],
    +  SHORTQUARTERS: ['A1', 'A2', 'A3', 'A4'],
    +  QUARTERS: ['Arrubu 1', 'Arrubu 2', 'Arrubu 3', 'Arrubu 4'],
    +  AMPMS: ['Subbaahi', 'Zaarikay b'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale twq_NE.
    + */
    +goog.i18n.DateTimeSymbols_twq_NE = goog.i18n.DateTimeSymbols_twq;
    +
    +
    +/**
    + * Date/time formatting symbols for locale tzm.
    + */
    +goog.i18n.DateTimeSymbols_tzm = {
    +  ERAS: ['ZƐ', 'ḌƐ'],
    +  ERANAMES: ['Zdat Ɛisa (TAƔ)', 'Ḍeffir Ɛisa (TAƔ)'],
    +  NARROWMONTHS: ['Y', 'Y', 'M', 'I', 'M', 'Y', 'Y', 'Ɣ', 'C', 'K', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Y', 'Y', 'M', 'I', 'M', 'Y', 'Y', 'Ɣ', 'C', 'K',
    +    'N', 'D'],
    +  MONTHS: ['Yennayer', 'Yebrayer', 'Mars', 'Ibrir', 'Mayyu', 'Yunyu', 'Yulyuz',
    +    'Ɣuct', 'Cutanbir', 'Kṭuber', 'Nwanbir', 'Dujanbir'],
    +  STANDALONEMONTHS: ['Yennayer', 'Yebrayer', 'Mars', 'Ibrir', 'Mayyu', 'Yunyu',
    +    'Yulyuz', 'Ɣuct', 'Cutanbir', 'Kṭuber', 'Nwanbir', 'Dujanbir'],
    +  SHORTMONTHS: ['Yen', 'Yeb', 'Mar', 'Ibr', 'May', 'Yun', 'Yul', 'Ɣuc', 'Cut',
    +    'Kṭu', 'Nwa', 'Duj'],
    +  STANDALONESHORTMONTHS: ['Yen', 'Yeb', 'Mar', 'Ibr', 'May', 'Yun', 'Yul',
    +    'Ɣuc', 'Cut', 'Kṭu', 'Nwa', 'Duj'],
    +  WEEKDAYS: ['Asamas', 'Aynas', 'Asinas', 'Akras', 'Akwas', 'Asimwas',
    +    'Asiḍyas'],
    +  STANDALONEWEEKDAYS: ['Asamas', 'Aynas', 'Asinas', 'Akras', 'Akwas', 'Asimwas',
    +    'Asiḍyas'],
    +  SHORTWEEKDAYS: ['Asa', 'Ayn', 'Asn', 'Akr', 'Akw', 'Asm', 'Asḍ'],
    +  STANDALONESHORTWEEKDAYS: ['Asa', 'Ayn', 'Asn', 'Akr', 'Akw', 'Asm', 'Asḍ'],
    +  NARROWWEEKDAYS: ['A', 'A', 'A', 'A', 'A', 'A', 'A'],
    +  STANDALONENARROWWEEKDAYS: ['A', 'A', 'A', 'A', 'A', 'A', 'A'],
    +  SHORTQUARTERS: ['IA1', 'IA2', 'IA3', 'IA4'],
    +  QUARTERS: ['Imir adamsan 1', 'Imir adamsan 2', 'Imir adamsan 3',
    +    'Imir adamsan 4'],
    +  AMPMS: ['Zdat azal', 'Ḍeffir aza'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [4, 5],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale tzm_Latn.
    + */
    +goog.i18n.DateTimeSymbols_tzm_Latn = goog.i18n.DateTimeSymbols_tzm;
    +
    +
    +/**
    + * Date/time formatting symbols for locale tzm_Latn_MA.
    + */
    +goog.i18n.DateTimeSymbols_tzm_Latn_MA = goog.i18n.DateTimeSymbols_tzm;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ug.
    + */
    +goog.i18n.DateTimeSymbols_ug = {
    +  ERAS: ['مىلادىيەدىن بۇرۇن', 'مىلادىيە'],
    +  ERANAMES: ['مىلادىيەدىن بۇرۇن', 'مىلادىيە'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل', 'ماي',
    +    'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر',
    +    'ئۆكتەبىر', 'بويابىر', 'دېكابىر'],
    +  STANDALONEMONTHS: ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل',
    +    'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر',
    +    'ئۆكتەبىر', 'بويابىر', 'دېكابىر'],
    +  SHORTMONTHS: ['يانۋار', 'فېۋرال', 'مارت', 'ئاپرېل',
    +    'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست', 'سېنتەبىر',
    +    'ئۆكتەبىر', 'نويابىر', 'دېكابىر'],
    +  STANDALONESHORTMONTHS: ['يانۋار', 'فېۋرال', 'مارت',
    +    'ئاپرېل', 'ماي', 'ئىيۇن', 'ئىيۇل', 'ئاۋغۇست',
    +    'سېنتەبىر', 'ئۆكتەبىر', 'نويابىر', 'دېكابىر'],
    +  WEEKDAYS: ['يەكشەنبە', 'دۈشەنبە', 'سەيشەنبە',
    +    'چارشەنبە', 'پەيشەنبە', 'جۈمە', 'شەنبە'],
    +  STANDALONEWEEKDAYS: ['يەكشەنبە', 'دۈشەنبە', 'سەيشەنبە',
    +    'چارشەنبە', 'پەيشەنبە', 'جۈمە', 'شەنبە'],
    +  SHORTWEEKDAYS: ['يە', 'دۈ', 'سە', 'چا', 'پە', 'چۈ', 'شە'],
    +  STANDALONESHORTWEEKDAYS: ['يە', 'دۈ', 'سە', 'چا', 'پە', 'چۈ',
    +    'شە'],
    +  NARROWWEEKDAYS: ['ي', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  STANDALONENARROWWEEKDAYS: ['ي', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
    +  SHORTQUARTERS: ['بىرىنچى پەسىل', 'ئىككىنچى پەسىل',
    +    'ئۈچىنچى پەسىل', 'تۆتىنچى پەسىل'],
    +  QUARTERS: ['بىرىنچى پەسىل', 'ئىككىنچى پەسىل',
    +    'ئۈچىنچى پەسىل', 'تۆتىنچى پەسىل'],
    +  AMPMS: ['چۈشتىن بۇرۇن', 'چۈشتىن كېيىن'],
    +  DATEFORMATS: ['EEEE، MMMM d، y', 'MMMM d، y', 'MMM d، y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}، {0}', '{1}، {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ug_Arab.
    + */
    +goog.i18n.DateTimeSymbols_ug_Arab = goog.i18n.DateTimeSymbols_ug;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ug_Arab_CN.
    + */
    +goog.i18n.DateTimeSymbols_ug_Arab_CN = goog.i18n.DateTimeSymbols_ug;
    +
    +
    +/**
    + * Date/time formatting symbols for locale uk_UA.
    + */
    +goog.i18n.DateTimeSymbols_uk_UA = {
    +  ERAS: ['до н.е.', 'н.е.'],
    +  ERANAMES: ['до нашої ери', 'нашої ери'],
    +  NARROWMONTHS: ['С', 'Л', 'Б', 'К', 'Т', 'Ч', 'Л', 'С', 'В', 'Ж',
    +    'Л', 'Г'],
    +  STANDALONENARROWMONTHS: ['С', 'Л', 'Б', 'К', 'Т', 'Ч', 'Л', 'С', 'В',
    +    'Ж', 'Л', 'Г'],
    +  MONTHS: ['січня', 'лютого', 'березня', 'квітня',
    +    'травня', 'червня', 'липня', 'серпня',
    +    'вересня', 'жовтня', 'листопада', 'грудня'],
    +  STANDALONEMONTHS: ['Січень', 'Лютий', 'Березень',
    +    'Квітень', 'Травень', 'Червень', 'Липень',
    +    'Серпень', 'Вересень', 'Жовтень', 'Листопад',
    +    'Грудень'],
    +  SHORTMONTHS: ['січ.', 'лют.', 'бер.', 'квіт.', 'трав.',
    +    'черв.', 'лип.', 'серп.', 'вер.', 'жовт.', 'лист.',
    +    'груд.'],
    +  STANDALONESHORTMONTHS: ['Січ', 'Лют', 'Бер', 'Кві', 'Тра',
    +    'Чер', 'Лип', 'Сер', 'Вер', 'Жов', 'Лис', 'Гру'],
    +  WEEKDAYS: ['неділя', 'понеділок', 'вівторок',
    +    'середа', 'четвер', 'пʼятниця', 'субота'],
    +  STANDALONEWEEKDAYS: ['Неділя', 'Понеділок', 'Вівторок',
    +    'Середа', 'Четвер', 'Пʼятниця', 'Субота'],
    +  SHORTWEEKDAYS: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
    +  STANDALONESHORTWEEKDAYS: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт',
    +    'Сб'],
    +  NARROWWEEKDAYS: ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  STANDALONENARROWWEEKDAYS: ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
    +  SHORTQUARTERS: ['I кв.', 'II кв.', 'III кв.', 'IV кв.'],
    +  QUARTERS: ['I квартал', 'II квартал', 'III квартал',
    +    'IV квартал'],
    +  AMPMS: ['дп', 'пп'],
    +  DATEFORMATS: ['EEEE, d MMMM y \'р\'.', 'd MMMM y \'р\'.', 'd MMM y \'р\'.',
    +    'dd.MM.yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ur_IN.
    + */
    +goog.i18n.DateTimeSymbols_ur_IN = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['قبل مسیح', 'عیسوی'],
    +  ERANAMES: ['قبل مسیح', 'عیسوی'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات',
    +    'جمعہ', 'ہفتہ'],
    +  STANDALONEWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  SHORTWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات',
    +    'جمعہ', 'ہفتہ'],
    +  STANDALONESHORTWEEKDAYS: ['اتوار', 'پیر', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['پہلی سہ ماہی', 'دوسری سہ ماہی',
    +    'تیسری سہ ماہی', 'چوتهی سہ ماہی'],
    +  AMPMS: ['قبل دوپہر', 'بعد دوپہر'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'd MMM، y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [6, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ur_PK.
    + */
    +goog.i18n.DateTimeSymbols_ur_PK = {
    +  ERAS: ['ق م', 'عیسوی سن'],
    +  ERANAMES: ['قبل مسیح', 'عیسوی سن'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی',
    +    'جون', 'جولائی', 'اگست', 'ستمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONESHORTMONTHS: ['جنوری', 'فروری', 'مارچ', 'اپریل',
    +    'مئی', 'جون', 'جولائی', 'اگست', 'ستمبر',
    +    'اکتوبر', 'نومبر', 'دسمبر'],
    +  WEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ', 'جمعرات',
    +    'جمعہ', 'ہفتہ'],
    +  STANDALONEWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  SHORTWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  STANDALONESHORTWEEKDAYS: ['اتوار', 'سوموار', 'منگل', 'بدھ',
    +    'جمعرات', 'جمعہ', 'ہفتہ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['پہلی سہ ماہی', 'دوسری سہ ماہی',
    +    'تیسری سہ ماہی', 'چوتهی سہ ماہی'],
    +  QUARTERS: ['پہلی سہ ماہی', 'دوسری سہ ماہی',
    +    'تیسری سہ ماہی', 'چوتهی سہ ماہی'],
    +  AMPMS: ['قبل دوپہر', 'بعد دوپہر'],
    +  DATEFORMATS: ['EEEE، d MMMM، y', 'd MMMM، y', 'd MMM، y', 'd/M/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Arab.
    + */
    +goog.i18n.DateTimeSymbols_uz_Arab = {
    +  ZERODIGIT: 0x06F0,
    +  ERAS: ['ق.م.', 'م.'],
    +  ERANAMES: ['ق.م.', 'م.'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['جنوری', 'فبروری', 'مارچ', 'اپریل', 'می',
    +    'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  STANDALONEMONTHS: ['جنوری', 'فبروری', 'مارچ', 'اپریل',
    +    'می', 'جون', 'جولای', 'اگست', 'سپتمبر', 'اکتوبر',
    +    'نومبر', 'دسمبر'],
    +  SHORTMONTHS: ['جنو', 'فبر', 'مار', 'اپر', 'مـی', 'جون',
    +    'جول', 'اگس', 'سپت', 'اکت', 'نوم', 'دسم'],
    +  STANDALONESHORTMONTHS: ['جنو', 'فبر', 'مار', 'اپر', 'مـی',
    +    'جون', 'جول', 'اگس', 'سپت', 'اکت', 'نوم', 'دسم'],
    +  WEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  STANDALONEWEEKDAYS: ['یکشنبه', 'دوشنبه', 'سه‌شنبه',
    +    'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
    +  SHORTWEEKDAYS: ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'],
    +  STANDALONESHORTWEEKDAYS: ['ی.', 'د.', 'س.', 'چ.', 'پ.', 'ج.', 'ش.'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y نچی ییل d نچی MMMM EEEE کونی',
    +    'd نچی MMMM y', 'd MMM y', 'y/M/d'],
    +  TIMEFORMATS: ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 5,
    +  WEEKENDRANGE: [3, 4],
    +  FIRSTWEEKCUTOFFDAY: 4
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Arab_AF.
    + */
    +goog.i18n.DateTimeSymbols_uz_Arab_AF = goog.i18n.DateTimeSymbols_uz_Arab;
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Cyrl.
    + */
    +goog.i18n.DateTimeSymbols_uz_Cyrl = {
    +  ERAS: ['М.А.', 'Э'],
    +  ERANAMES: ['М.А.', 'Э'],
    +  NARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С', 'О',
    +    'Н', 'Д'],
    +  STANDALONENARROWMONTHS: ['Я', 'Ф', 'М', 'А', 'М', 'И', 'И', 'А', 'С',
    +    'О', 'Н', 'Д'],
    +  MONTHS: ['Январ', 'Феврал', 'Март', 'Апрел', 'Май',
    +    'Июн', 'Июл', 'Август', 'Сентябр', 'Октябр',
    +    'Ноябр', 'Декабр'],
    +  STANDALONEMONTHS: ['Январ', 'Феврал', 'Март', 'Апрел',
    +    'Май', 'Июн', 'Июл', 'Август', 'Сентябр',
    +    'Октябр', 'Ноябр', 'Декабр'],
    +  SHORTMONTHS: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн',
    +    'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
    +  STANDALONESHORTMONTHS: ['Янв', 'Фев', 'Мар', 'Апр', 'Май',
    +    'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
    +  WEEKDAYS: ['якшанба', 'душанба', 'сешанба',
    +    'чоршанба', 'пайшанба', 'жума', 'шанба'],
    +  STANDALONEWEEKDAYS: ['якшанба', 'душанба', 'сешанба',
    +    'чоршанба', 'пайшанба', 'жума', 'шанба'],
    +  SHORTWEEKDAYS: ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай', 'Жум',
    +    'Шан'],
    +  STANDALONESHORTWEEKDAYS: ['Якш', 'Душ', 'Сеш', 'Чор', 'Пай',
    +    'Жум', 'Шан'],
    +  NARROWWEEKDAYS: ['Я', 'Д', 'С', 'Ч', 'П', 'Ж', 'Ш'],
    +  STANDALONENARROWWEEKDAYS: ['Я', 'Д', 'С', 'Ч', 'П', 'Ж', 'Ш'],
    +  SHORTQUARTERS: ['1-ч', '2-ч', '3-ч', '4-ч'],
    +  QUARTERS: ['1-чорак', '2-чорак', '3-чорак', '4-чорак'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Cyrl_UZ.
    + */
    +goog.i18n.DateTimeSymbols_uz_Cyrl_UZ = goog.i18n.DateTimeSymbols_uz_Cyrl;
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Latn.
    + */
    +goog.i18n.DateTimeSymbols_uz_Latn = {
    +  ERAS: ['M.A.', 'E'],
    +  ERANAMES: ['M.A.', 'E'],
    +  NARROWMONTHS: ['Y', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['Y', 'F', 'M', 'A', 'M', 'I', 'I', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul', 'Avgust',
    +    'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
    +  STANDALONEMONTHS: ['Yanvar', 'Fevral', 'Mart', 'Aprel', 'May', 'Iyun', 'Iyul',
    +    'Avgust', 'Sentabr', 'Oktabr', 'Noyabr', 'Dekabr'],
    +  SHORTMONTHS: ['Yanv', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul', 'Avg',
    +    'Sen', 'Okt', 'Noya', 'Dek'],
    +  STANDALONESHORTMONTHS: ['Yanv', 'Fev', 'Mar', 'Apr', 'May', 'Iyun', 'Iyul',
    +    'Avg', 'Sen', 'Okt', 'Noya', 'Dek'],
    +  WEEKDAYS: ['yakshanba', 'dushanba', 'seshanba', 'chorshanba', 'payshanba',
    +    'juma', 'shanba'],
    +  STANDALONEWEEKDAYS: ['yakshanba', 'dushanba', 'seshanba', 'chorshanba',
    +    'payshanba', 'juma', 'shanba'],
    +  SHORTWEEKDAYS: ['Yaksh', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum', 'Shan'],
    +  STANDALONESHORTWEEKDAYS: ['Yaksh', 'Dush', 'Sesh', 'Chor', 'Pay', 'Jum',
    +    'Shan'],
    +  NARROWWEEKDAYS: ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
    +  SHORTQUARTERS: ['1-ch', '2-ch', '3-ch', '4-ch'],
    +  QUARTERS: ['1-chorak', '2-chorak', '3-chorak', '4-chorak'],
    +  AMPMS: ['TO', 'TK'],
    +  DATEFORMATS: ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale uz_Latn_UZ.
    + */
    +goog.i18n.DateTimeSymbols_uz_Latn_UZ = goog.i18n.DateTimeSymbols_uz_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale vai.
    + */
    +goog.i18n.DateTimeSymbols_vai = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['ꖨꕪꖃ ꔞꕮ', 'ꕒꕡꖝꖕ', 'ꕾꖺ', 'ꖢꖕ', 'ꖑꕱ',
    +    '6', '7', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋꕔꕿ ꕸꖃꗏ',
    +    'ꖨꕪꕱ ꗏꕮ'],
    +  STANDALONEMONTHS: ['ꖨꕪꖃ ꔞꕮ', 'ꕒꕡꖝꖕ', 'ꕾꖺ', 'ꖢꖕ',
    +    'ꖑꕱ', '6', '7', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋꕔꕿ ꕸꖃꗏ',
    +    'ꖨꕪꕱ ꗏꕮ'],
    +  SHORTMONTHS: ['ꖨꕪꖃ ꔞꕮ', 'ꕒꕡꖝꖕ', 'ꕾꖺ', 'ꖢꖕ',
    +    'ꖑꕱ', '6', '7', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ', 'ꔞꘋꕔꕿ ꕸꖃꗏ',
    +    'ꖨꕪꕱ ꗏꕮ'],
    +  STANDALONESHORTMONTHS: ['ꖨꕪꖃ ꔞꕮ', 'ꕒꕡꖝꖕ', 'ꕾꖺ',
    +    'ꖢꖕ', 'ꖑꕱ', '6', '7', 'ꗛꔕ', 'ꕢꕌ', 'ꕭꖃ',
    +    'ꔞꘋꕔꕿ ꕸꖃꗏ', 'ꖨꕪꕱ ꗏꕮ'],
    +  WEEKDAYS: ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ', 'ꕉꔤꕆꕢ',
    +    'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
    +  STANDALONEWEEKDAYS: ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ',
    +    'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
    +  SHORTWEEKDAYS: ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ',
    +    'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
    +  STANDALONESHORTWEEKDAYS: ['ꕞꕌꔵ', 'ꗳꗡꘉ', 'ꕚꕞꕚ', 'ꕉꕞꕒ',
    +    'ꕉꔤꕆꕢ', 'ꕉꔤꕀꕮ', 'ꔻꔬꔳ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vai_Latn.
    + */
    +goog.i18n.DateTimeSymbols_vai_Latn = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7',
    +    'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
    +  STANDALONEMONTHS: ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6',
    +    '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
    +  SHORTMONTHS: ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo', '6', '7',
    +    'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
    +  STANDALONESHORTMONTHS: ['luukao kemã', 'ɓandaɓu', 'vɔɔ', 'fulu', 'goo',
    +    '6', '7', 'kɔnde', 'saah', 'galo', 'kenpkato ɓololɔ', 'luukao lɔma'],
    +  WEEKDAYS: ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima',
    +    'siɓiti'],
    +  STANDALONEWEEKDAYS: ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa',
    +    'aijima', 'siɓiti'],
    +  SHORTWEEKDAYS: ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa', 'aijima',
    +    'siɓiti'],
    +  STANDALONESHORTWEEKDAYS: ['lahadi', 'tɛɛnɛɛ', 'talata', 'alaba', 'aimisa',
    +    'aijima', 'siɓiti'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vai_Latn_LR.
    + */
    +goog.i18n.DateTimeSymbols_vai_Latn_LR = goog.i18n.DateTimeSymbols_vai_Latn;
    +
    +
    +/**
    + * Date/time formatting symbols for locale vai_Vaii.
    + */
    +goog.i18n.DateTimeSymbols_vai_Vaii = goog.i18n.DateTimeSymbols_vai;
    +
    +
    +/**
    + * Date/time formatting symbols for locale vai_Vaii_LR.
    + */
    +goog.i18n.DateTimeSymbols_vai_Vaii_LR = goog.i18n.DateTimeSymbols_vai;
    +
    +
    +/**
    + * Date/time formatting symbols for locale ve.
    + */
    +goog.i18n.DateTimeSymbols_ve = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Phando', 'Luhuhi', 'Ṱhafamuhwe', 'Lambamai', 'Shundunthule',
    +    'Fulwi', 'Fulwana', 'Ṱhangule', 'Khubvumedzi', 'Tshimedzi', 'Ḽara',
    +    'Nyendavhusiku'],
    +  STANDALONEMONTHS: ['Phando', 'Luhuhi', 'Ṱhafamuhwe', 'Lambamai',
    +    'Shundunthule', 'Fulwi', 'Fulwana', 'Ṱhangule', 'Khubvumedzi',
    +    'Tshimedzi', 'Ḽara', 'Nyendavhusiku'],
    +  SHORTMONTHS: ['Pha', 'Luh', 'Ṱhf', 'Lam', 'Shu', 'Lwi', 'Lwa', 'Ṱha',
    +    'Khu', 'Tsh', 'Ḽar', 'Nye'],
    +  STANDALONESHORTMONTHS: ['Pha', 'Luh', 'Ṱhf', 'Lam', 'Shu', 'Lwi', 'Lwa',
    +    'Ṱha', 'Khu', 'Tsh', 'Ḽar', 'Nye'],
    +  WEEKDAYS: ['Swondaha', 'Musumbuluwo', 'Ḽavhuvhili', 'Ḽavhuraru',
    +    'Ḽavhuṋa', 'Ḽavhuṱanu', 'Mugivhela'],
    +  STANDALONEWEEKDAYS: ['Swondaha', 'Musumbuluwo', 'Ḽavhuvhili', 'Ḽavhuraru',
    +    'Ḽavhuṋa', 'Ḽavhuṱanu', 'Mugivhela'],
    +  SHORTWEEKDAYS: ['Swo', 'Mus', 'Vhi', 'Rar', 'Ṋa', 'Ṱan', 'Mug'],
    +  STANDALONESHORTWEEKDAYS: ['Swo', 'Mus', 'Vhi', 'Rar', 'Ṋa', 'Ṱan', 'Mug'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kotara ya u thoma', 'Kotara ya vhuvhili', 'Kotara ya vhuraru',
    +    'Kotara ya vhuṋa'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale ve_ZA.
    + */
    +goog.i18n.DateTimeSymbols_ve_ZA = goog.i18n.DateTimeSymbols_ve;
    +
    +
    +/**
    + * Date/time formatting symbols for locale vi_VN.
    + */
    +goog.i18n.DateTimeSymbols_vi_VN = {
    +  ERAS: ['tr. CN', 'sau CN'],
    +  ERANAMES: ['tr. CN', 'sau CN'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['tháng 1', 'tháng 2', 'tháng 3', 'tháng 4', 'tháng 5',
    +    'tháng 6', 'tháng 7', 'tháng 8', 'tháng 9', 'tháng 10', 'tháng 11',
    +    'tháng 12'],
    +  STANDALONEMONTHS: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5',
    +    'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11',
    +    'Tháng 12'],
    +  SHORTMONTHS: ['thg 1', 'thg 2', 'thg 3', 'thg 4', 'thg 5', 'thg 6', 'thg 7',
    +    'thg 8', 'thg 9', 'thg 10', 'thg 11', 'thg 12'],
    +  STANDALONESHORTMONTHS: ['Thg 1', 'Thg 2', 'Thg 3', 'Thg 4', 'Thg 5', 'Thg 6',
    +    'Thg 7', 'Thg 8', 'Thg 9', 'Thg 10', 'Thg 11', 'Thg 12'],
    +  WEEKDAYS: ['Chủ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm',
    +    'Thứ Sáu', 'Thứ Bảy'],
    +  STANDALONEWEEKDAYS: ['Chủ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư',
    +    'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'],
    +  SHORTWEEKDAYS: ['CN', 'Th 2', 'Th 3', 'Th 4', 'Th 5', 'Th 6', 'Th 7'],
    +  STANDALONESHORTWEEKDAYS: ['CN', 'Th 2', 'Th 3', 'Th 4', 'Th 5', 'Th 6',
    +    'Th 7'],
    +  NARROWWEEKDAYS: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
    +  STANDALONENARROWWEEKDAYS: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Quý 1', 'Quý 2', 'Quý 3', 'Quý 4'],
    +  AMPMS: ['SA', 'CH'],
    +  DATEFORMATS: ['EEEE, \'ngày\' dd MMMM \'năm\' y',
    +    '\'Ngày\' dd \'tháng\' MM \'năm\' y', 'dd-MM-y', 'dd/MM/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{0} {1}', '{0} {1}', '{0} {1}', '{0} {1}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vo.
    + */
    +goog.i18n.DateTimeSymbols_vo = {
    +  ERAS: ['b. t. kr.', 'p. t. kr.'],
    +  ERANAMES: ['b. t. kr.', 'p. t. kr.'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'P', 'M', 'Y', 'Y', 'G', 'S', 'T', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'P', 'M', 'Y', 'Y', 'G', 'S', 'T',
    +    'N', 'D'],
    +  MONTHS: ['janul', 'febul', 'mäzil', 'prilul', 'mayul', 'yunul', 'yulul',
    +    'gustul', 'setul', 'tobul', 'novul', 'dekul'],
    +  STANDALONEMONTHS: ['janul', 'febul', 'mäzil', 'prilul', 'mayul', 'yunul',
    +    'yulul', 'gustul', 'setul', 'tobul', 'novul', 'dekul'],
    +  SHORTMONTHS: ['jan', 'feb', 'mäz', 'prl', 'may', 'yun', 'yul', 'gst', 'set',
    +    'ton', 'nov', 'dek'],
    +  STANDALONESHORTMONTHS: ['jan', 'feb', 'mäz', 'prl', 'may', 'yun', 'yul',
    +    'gst', 'set', 'tob', 'nov', 'Dek'],
    +  WEEKDAYS: ['sudel', 'mudel', 'tudel', 'vedel', 'dödel', 'fridel', 'zädel'],
    +  STANDALONEWEEKDAYS: ['sudel', 'mudel', 'tudel', 'vedel', 'dödel', 'fridel',
    +    'zädel'],
    +  SHORTWEEKDAYS: ['su.', 'mu.', 'tu.', 've.', 'dö.', 'fr.', 'zä.'],
    +  STANDALONESHORTWEEKDAYS: ['Su', 'Mu', 'Tu', 'Ve', 'Dö', 'Fr', 'Zä'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'V', 'D', 'F', 'Z'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'V', 'D', 'F', 'Z'],
    +  SHORTQUARTERS: ['Yf1', 'Yf2', 'Yf3', 'Yf4'],
    +  QUARTERS: ['1id yelafoldil', '2id yelafoldil', '3id yelafoldil',
    +    '4id yelafoldil'],
    +  AMPMS: ['posz.', 'büz.'],
    +  DATEFORMATS: ['y MMMMa \'d\'. d\'id\'', 'y MMMM d', 'y MMM. d', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vo_001.
    + */
    +goog.i18n.DateTimeSymbols_vo_001 = goog.i18n.DateTimeSymbols_vo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale vun.
    + */
    +goog.i18n.DateTimeSymbols_vun = {
    +  ERAS: ['KK', 'BK'],
    +  ERANAMES: ['Kabla ya Kristu', 'Baada ya Kristu'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi', 'Julyai',
    +    'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Januari', 'Februari', 'Machi', 'Aprilyi', 'Mei', 'Junyi',
    +    'Julyai', 'Agusti', 'Septemba', 'Oktoba', 'Novemba', 'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ago', 'Sep',
    +    'Okt', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul',
    +    'Ago', 'Sep', 'Okt', 'Nov', 'Des'],
    +  WEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu', 'Alhamisi',
    +    'Ijumaa', 'Jumamosi'],
    +  STANDALONEWEEKDAYS: ['Jumapilyi', 'Jumatatuu', 'Jumanne', 'Jumatanu',
    +    'Alhamisi', 'Ijumaa', 'Jumamosi'],
    +  SHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  STANDALONESHORTWEEKDAYS: ['Jpi', 'Jtt', 'Jnn', 'Jtn', 'Alh', 'Iju', 'Jmo'],
    +  NARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  STANDALONENARROWWEEKDAYS: ['J', 'J', 'J', 'J', 'A', 'I', 'J'],
    +  SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
    +  QUARTERS: ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
    +  AMPMS: ['utuko', 'kyiukonyi'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale vun_TZ.
    + */
    +goog.i18n.DateTimeSymbols_vun_TZ = goog.i18n.DateTimeSymbols_vun;
    +
    +
    +/**
    + * Date/time formatting symbols for locale wae.
    + */
    +goog.i18n.DateTimeSymbols_wae = {
    +  ERAS: ['v. Chr.', 'n. Chr'],
    +  ERANAMES: ['v. Chr.', 'n. Chr'],
    +  NARROWMONTHS: ['J', 'H', 'M', 'A', 'M', 'B', 'H', 'Ö', 'H', 'W', 'W', 'C'],
    +  STANDALONENARROWMONTHS: ['J', 'H', 'M', 'A', 'M', 'B', 'H', 'Ö', 'H', 'W',
    +    'W', 'C'],
    +  MONTHS: ['Jenner', 'Hornig', 'Märze', 'Abrille', 'Meije', 'Bráčet',
    +    'Heiwet', 'Öigšte', 'Herbštmánet', 'Wímánet', 'Wintermánet',
    +    'Chrištmánet'],
    +  STANDALONEMONTHS: ['Jenner', 'Hornig', 'Märze', 'Abrille', 'Meije',
    +    'Bráčet', 'Heiwet', 'Öigšte', 'Herbštmánet', 'Wímánet',
    +    'Wintermánet', 'Chrištmánet'],
    +  SHORTMONTHS: ['Jen', 'Hor', 'Mär', 'Abr', 'Mei', 'Brá', 'Hei', 'Öig',
    +    'Her', 'Wím', 'Win', 'Chr'],
    +  STANDALONESHORTMONTHS: ['Jen', 'Hor', 'Mär', 'Abr', 'Mei', 'Brá', 'Hei',
    +    'Öig', 'Her', 'Wím', 'Win', 'Chr'],
    +  WEEKDAYS: ['Sunntag', 'Mäntag', 'Zištag', 'Mittwuč', 'Fróntag', 'Fritag',
    +    'Samštag'],
    +  STANDALONEWEEKDAYS: ['Sunntag', 'Mäntag', 'Zištag', 'Mittwuč', 'Fróntag',
    +    'Fritag', 'Samštag'],
    +  SHORTWEEKDAYS: ['Sun', 'Män', 'Ziš', 'Mit', 'Fró', 'Fri', 'Sam'],
    +  STANDALONESHORTWEEKDAYS: ['Sun', 'Män', 'Ziš', 'Mit', 'Fró', 'Fri', 'Sam'],
    +  NARROWWEEKDAYS: ['S', 'M', 'Z', 'M', 'F', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'Z', 'M', 'F', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['1. quartal', '2. quartal', '3. quartal', '4. quartal'],
    +  AMPMS: ['AM', 'PM'],
    +  DATEFORMATS: ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'y-MM-dd'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale wae_CH.
    + */
    +goog.i18n.DateTimeSymbols_wae_CH = goog.i18n.DateTimeSymbols_wae;
    +
    +
    +/**
    + * Date/time formatting symbols for locale xog.
    + */
    +goog.i18n.DateTimeSymbols_xog = {
    +  ERAS: ['AZ', 'AF'],
    +  ERANAMES: ['Kulisto nga azilawo', 'Kulisto nga affile'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi', 'Juuni',
    +    'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba', 'Desemba'],
    +  STANDALONEMONTHS: ['Janwaliyo', 'Febwaliyo', 'Marisi', 'Apuli', 'Maayi',
    +    'Juuni', 'Julaayi', 'Agusito', 'Sebuttemba', 'Okitobba', 'Novemba',
    +    'Desemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul', 'Agu', 'Seb',
    +    'Oki', 'Nov', 'Des'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mar', 'Apu', 'Maa', 'Juu', 'Jul',
    +    'Agu', 'Seb', 'Oki', 'Nov', 'Des'],
    +  WEEKDAYS: ['Sabiiti', 'Balaza', 'Owokubili', 'Owokusatu', 'Olokuna',
    +    'Olokutaanu', 'Olomukaaga'],
    +  STANDALONEWEEKDAYS: ['Sabiiti', 'Balaza', 'Owokubili', 'Owokusatu', 'Olokuna',
    +    'Olokutaanu', 'Olomukaaga'],
    +  SHORTWEEKDAYS: ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta', 'Muka'],
    +  STANDALONESHORTWEEKDAYS: ['Sabi', 'Bala', 'Kubi', 'Kusa', 'Kuna', 'Kuta',
    +    'Muka'],
    +  NARROWWEEKDAYS: ['S', 'B', 'B', 'S', 'K', 'K', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'B', 'B', 'S', 'K', 'K', 'M'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Ebisera ebyomwaka ebisoka', 'Ebisera ebyomwaka ebyokubiri',
    +    'Ebisera ebyomwaka ebyokusatu', 'Ebisera ebyomwaka ebyokuna'],
    +  AMPMS: ['Munkyo', 'Eigulo'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale xog_UG.
    + */
    +goog.i18n.DateTimeSymbols_xog_UG = goog.i18n.DateTimeSymbols_xog;
    +
    +
    +/**
    + * Date/time formatting symbols for locale yav.
    + */
    +goog.i18n.DateTimeSymbols_yav = {
    +  ERAS: ['k.Y.', '+J.C.'],
    +  ERANAMES: ['katikupíen Yésuse', 'ékélémkúnupíén n'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['pikítíkítie, oólí ú kutúan', 'siɛyɛ́, oóli ú kándíɛ',
    +    'ɔnsúmbɔl, oóli ú kátátúɛ', 'mesiŋ, oóli ú kénie',
    +    'ensil, oóli ú kátánuɛ', 'ɔsɔn', 'efute', 'pisuyú',
    +    'imɛŋ i puɔs', 'imɛŋ i putúk,oóli ú kátíɛ', 'makandikɛ',
    +    'pilɔndɔ́'],
    +  STANDALONEMONTHS: ['pikítíkítie, oólí ú kutúan',
    +    'siɛyɛ́, oóli ú kándíɛ', 'ɔnsúmbɔl, oóli ú kátátúɛ',
    +    'mesiŋ, oóli ú kénie', 'ensil, oóli ú kátánuɛ', 'ɔsɔn', 'efute',
    +    'pisuyú', 'imɛŋ i puɔs', 'imɛŋ i putúk,oóli ú kátíɛ',
    +    'makandikɛ', 'pilɔndɔ́'],
    +  SHORTMONTHS: ['o.1', 'o.2', 'o.3', 'o.4', 'o.5', 'o.6', 'o.7', 'o.8', 'o.9',
    +    'o.10', 'o.11', 'o.12'],
    +  STANDALONESHORTMONTHS: ['o.1', 'o.2', 'o.3', 'o.4', 'o.5', 'o.6', 'o.7',
    +    'o.8', 'o.9', 'o.10', 'o.11', 'o.12'],
    +  WEEKDAYS: ['sɔ́ndiɛ', 'móndie', 'muányáŋmóndie', 'metúkpíápɛ',
    +    'kúpélimetúkpiapɛ', 'feléte', 'séselé'],
    +  STANDALONEWEEKDAYS: ['sɔ́ndiɛ', 'móndie', 'muányáŋmóndie',
    +    'metúkpíápɛ', 'kúpélimetúkpiapɛ', 'feléte', 'séselé'],
    +  SHORTWEEKDAYS: ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'],
    +  STANDALONESHORTWEEKDAYS: ['sd', 'md', 'mw', 'et', 'kl', 'fl', 'ss'],
    +  NARROWWEEKDAYS: ['s', 'm', 'm', 'e', 'k', 'f', 's'],
    +  STANDALONENARROWWEEKDAYS: ['s', 'm', 'm', 'e', 'k', 'f', 's'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ndátúɛ 1', 'ndátúɛ 2', 'ndátúɛ 3', 'ndátúɛ 4'],
    +  AMPMS: ['kiɛmɛ́ɛm', 'kisɛ́ndɛ'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale yav_CM.
    + */
    +goog.i18n.DateTimeSymbols_yav_CM = goog.i18n.DateTimeSymbols_yav;
    +
    +
    +/**
    + * Date/time formatting symbols for locale yi.
    + */
    +goog.i18n.DateTimeSymbols_yi = {
    +  ERAS: ['BCE', 'CE'],
    +  ERANAMES: ['BCE', 'CE'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['יאַנואַר', 'פֿעברואַר', 'מערץ',
    +    'אַפּריל', 'מיי', 'יוני', 'יולי', 'אויגוסט',
    +    'סעפּטעמבער', 'אקטאבער', 'נאוועמבער',
    +    'דעצעמבער'],
    +  STANDALONEMONTHS: ['יאַנואַר', 'פֿעברואַר', 'מערץ',
    +    'אַפּריל', 'מיי', 'יוני', 'יולי', 'אויגוסט',
    +    'סעפּטעמבער', 'אקטאבער', 'נאוועמבער',
    +    'דעצעמבער'],
    +  SHORTMONTHS: ['יאַנואַר', 'פֿעברואַר', 'מערץ',
    +    'אַפּריל', 'מיי', 'יוני', 'יולי', 'אויגוסט',
    +    'סעפּטעמבער', 'אקטאבער', 'נאוועמבער',
    +    'דעצעמבער'],
    +  STANDALONESHORTMONTHS: ['יאַנ', 'פֿעב', 'מערץ', 'אַפּר',
    +    'מיי', 'יוני', 'יולי', 'אויגוסט', 'סעפּ', 'אקט',
    +    'נאוו', 'דעצ'],
    +  WEEKDAYS: ['זונטיק', 'מאָנטיק', 'דינסטיק',
    +    'מיטוואך', 'דאנערשטיק', 'פֿרײַטיק', 'שבת'],
    +  STANDALONEWEEKDAYS: ['זונטיק', 'מאָנטיק', 'דינסטיק',
    +    'מיטוואך', 'דאנערשטיק', 'פֿרײַטיק', 'שבת'],
    +  SHORTWEEKDAYS: ['זונטיק', 'מאָנטיק', 'דינסטיק',
    +    'מיטוואך', 'דאנערשטיק', 'פֿרײַטיק', 'שבת'],
    +  STANDALONESHORTWEEKDAYS: ['זונטיק', 'מאָנטיק', 'דינסטיק',
    +    'מיטוואך', 'דאנערשטיק', 'פֿרײַטיק', 'שבת'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  AMPMS: ['פארמיטאג', 'נאכמיטאג'],
    +  DATEFORMATS: ['EEEE, dטן MMMM y', 'dטן MMMM y', 'dטן MMM y',
    +    'dd/MM/yy'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale yi_001.
    + */
    +goog.i18n.DateTimeSymbols_yi_001 = goog.i18n.DateTimeSymbols_yi;
    +
    +
    +/**
    + * Date/time formatting symbols for locale yo.
    + */
    +goog.i18n.DateTimeSymbols_yo = {
    +  ERAS: ['SK', 'LK'],
    +  ERANAMES: ['Saju Kristi', 'Lehin Kristi'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Oṣù Ṣẹ́rẹ́', 'Oṣù Èrèlè', 'Oṣù Ẹrẹ̀nà',
    +    'Oṣù Ìgbé', 'Oṣù Ẹ̀bibi', 'Oṣù Òkúdu', 'Oṣù Agẹmọ',
    +    'Oṣù Ògún', 'Oṣù Owewe', 'Oṣù Ọ̀wàrà', 'Oṣù Bélú',
    +    'Oṣù Ọ̀pẹ̀'],
    +  STANDALONEMONTHS: ['Oṣù Ṣẹ́rẹ́', 'Oṣù Èrèlè',
    +    'Oṣù Ẹrẹ̀nà', 'Oṣù Ìgbé', 'Oṣù Ẹ̀bibi',
    +    'Oṣù Òkúdu', 'Oṣù Agẹmọ', 'Oṣù Ògún', 'Oṣù Owewe',
    +    'Oṣù Ọ̀wàrà', 'Oṣù Bélú', 'Oṣù Ọ̀pẹ̀'],
    +  SHORTMONTHS: ['Ṣẹ́rẹ́', 'Èrèlè', 'Ẹrẹ̀nà', 'Ìgbé',
    +    'Ẹ̀bibi', 'Òkúdu', 'Agẹmọ', 'Ògún', 'Owewe', 'Ọ̀wàrà',
    +    'Bélú', 'Ọ̀pẹ̀'],
    +  STANDALONESHORTMONTHS: ['Ṣẹ́rẹ́', 'Èrèlè', 'Ẹrẹ̀nà',
    +    'Ìgbé', 'Ẹ̀bibi', 'Òkúdu', 'Agẹmọ', 'Ògún', 'Owewe',
    +    'Ọ̀wàrà', 'Bélú', 'Ọ̀pẹ̀'],
    +  WEEKDAYS: ['Ọjọ́ Àìkú', 'Ọjọ́ Ajé', 'Ọjọ́ Ìsẹ́gun',
    +    'Ọjọ́rú', 'Ọjọ́bọ', 'Ọjọ́ Ẹtì',
    +    'Ọjọ́ Àbámẹ́ta'],
    +  STANDALONEWEEKDAYS: ['Ọjọ́ Àìkú', 'Ọjọ́ Ajé',
    +    'Ọjọ́ Ìsẹ́gun', 'Ọjọ́rú', 'Ọjọ́bọ',
    +    'Ọjọ́ Ẹtì', 'Ọjọ́ Àbámẹ́ta'],
    +  SHORTWEEKDAYS: ['Àìkú', 'Ajé', 'Ìsẹ́gun', 'Ọjọ́rú',
    +    'Ọjọ́bọ', 'Ẹtì', 'Àbámẹ́ta'],
    +  STANDALONESHORTWEEKDAYS: ['Àìkú', 'Ajé', 'Ìsẹ́gun', 'Ọjọ́rú',
    +    'Ọjọ́bọ', 'Ẹtì', 'Àbámẹ́ta'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kọ́tà Kínní', 'Kọ́tà Kejì', 'Kọ́à Keta',
    +    'Kọ́tà Kẹrin'],
    +  AMPMS: ['Àárọ̀', 'Ọ̀sán'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale yo_BJ.
    + */
    +goog.i18n.DateTimeSymbols_yo_BJ = {
    +  ERAS: ['SK', 'LK'],
    +  ERANAMES: ['Saju Kristi', 'Lehin Kristi'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['Oshù Shɛ́rɛ́', 'Oshù Èrèlè', 'Oshù Ɛrɛ̀nà',
    +    'Oshù Ìgbé', 'Oshù Ɛ̀bibi', 'Oshù Òkúdu', 'Oshù Agɛmɔ',
    +    'Oshù Ògún', 'Oshù Owewe', 'Oshù Ɔ̀wàrà', 'Oshù Bélú',
    +    'Oshù Ɔ̀pɛ̀'],
    +  STANDALONEMONTHS: ['Oshù Shɛ́rɛ́', 'Oshù Èrèlè', 'Oshù Ɛrɛ̀nà',
    +    'Oshù Ìgbé', 'Oshù Ɛ̀bibi', 'Oshù Òkúdu', 'Oshù Agɛmɔ',
    +    'Oshù Ògún', 'Oshù Owewe', 'Oshù Ɔ̀wàrà', 'Oshù Bélú',
    +    'Oshù Ɔ̀pɛ̀'],
    +  SHORTMONTHS: ['Shɛ́rɛ́', 'Èrèlè', 'Ɛrɛ̀nà', 'Ìgbé', 'Ɛ̀bibi',
    +    'Òkúdu', 'Agɛmɔ', 'Ògún', 'Owewe', 'Ɔ̀wàrà', 'Bélú',
    +    'Ɔ̀pɛ̀'],
    +  STANDALONESHORTMONTHS: ['Shɛ́rɛ́', 'Èrèlè', 'Ɛrɛ̀nà', 'Ìgbé',
    +    'Ɛ̀bibi', 'Òkúdu', 'Agɛmɔ', 'Ògún', 'Owewe', 'Ɔ̀wàrà', 'Bélú',
    +    'Ɔ̀pɛ̀'],
    +  WEEKDAYS: ['Ɔjɔ́ Àìkú', 'Ɔjɔ́ Ajé', 'Ɔjɔ́ Ìsɛ́gun',
    +    'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɔjɔ́ Ɛtì', 'Ɔjɔ́ Àbámɛ́ta'],
    +  STANDALONEWEEKDAYS: ['Ɔjɔ́ Àìkú', 'Ɔjɔ́ Ajé', 'Ɔjɔ́ Ìsɛ́gun',
    +    'Ɔjɔ́rú', 'Ɔjɔ́bɔ', 'Ɔjɔ́ Ɛtì', 'Ɔjɔ́ Àbámɛ́ta'],
    +  SHORTWEEKDAYS: ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú', 'Ɔjɔ́bɔ',
    +    'Ɛtì', 'Àbámɛ́ta'],
    +  STANDALONESHORTWEEKDAYS: ['Àìkú', 'Ajé', 'Ìsɛ́gun', 'Ɔjɔ́rú',
    +    'Ɔjɔ́bɔ', 'Ɛtì', 'Àbámɛ́ta'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['K1', 'K2', 'K3', 'K4'],
    +  QUARTERS: ['Kɔ́tà Kínní', 'Kɔ́tà Kejì', 'Kɔ́à Keta',
    +    'Kɔ́tà Kɛrin'],
    +  AMPMS: ['Àárɔ̀', 'Ɔ̀sán'],
    +  DATEFORMATS: ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale yo_NG.
    + */
    +goog.i18n.DateTimeSymbols_yo_NG = goog.i18n.DateTimeSymbols_yo;
    +
    +
    +/**
    + * Date/time formatting symbols for locale zgh.
    + */
    +goog.i18n.DateTimeSymbols_zgh = {
    +  ERAS: ['ⴷⴰⵄ', 'ⴷⴼⵄ'],
    +  ERANAMES: ['ⴷⴰⵜ ⵏ ⵄⵉⵙⴰ', 'ⴷⴼⴼⵉⵔ ⵏ ⵄⵉⵙⴰ'],
    +  NARROWMONTHS: ['ⵉ', 'ⴱ', 'ⵎ', 'ⵉ', 'ⵎ', 'ⵢ', 'ⵢ', 'ⵖ', 'ⵛ',
    +    'ⴽ', 'ⵏ', 'ⴷ'],
    +  STANDALONENARROWMONTHS: ['ⵉ', 'ⴱ', 'ⵎ', 'ⵉ', 'ⵎ', 'ⵢ', 'ⵢ',
    +    'ⵖ', 'ⵛ', 'ⴽ', 'ⵏ', 'ⴷ'],
    +  MONTHS: ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ',
    +    'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ',
    +    'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ',
    +    'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
    +  STANDALONEMONTHS: ['ⵉⵏⵏⴰⵢⵔ', 'ⴱⵕⴰⵢⵕ', 'ⵎⴰⵕⵚ',
    +    'ⵉⴱⵔⵉⵔ', 'ⵎⴰⵢⵢⵓ', 'ⵢⵓⵏⵢⵓ',
    +    'ⵢⵓⵍⵢⵓⵣ', 'ⵖⵓⵛⵜ', 'ⵛⵓⵜⴰⵏⴱⵉⵔ',
    +    'ⴽⵜⵓⴱⵔ', 'ⵏⵓⵡⴰⵏⴱⵉⵔ', 'ⴷⵓⵊⴰⵏⴱⵉⵔ'],
    +  SHORTMONTHS: ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ', 'ⵎⴰⵢ',
    +    'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ', 'ⴽⵜⵓ',
    +    'ⵏⵓⵡ', 'ⴷⵓⵊ'],
    +  STANDALONESHORTMONTHS: ['ⵉⵏⵏ', 'ⴱⵕⴰ', 'ⵎⴰⵕ', 'ⵉⴱⵔ',
    +    'ⵎⴰⵢ', 'ⵢⵓⵏ', 'ⵢⵓⵍ', 'ⵖⵓⵛ', 'ⵛⵓⵜ',
    +    'ⴽⵜⵓ', 'ⵏⵓⵡ', 'ⴷⵓⵊ'],
    +  WEEKDAYS: ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ', 'ⴰⵙⵉⵏⴰⵙ',
    +    'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ', 'ⴰⵙⵉⵎⵡⴰⵙ',
    +    'ⴰⵙⵉⴹⵢⴰⵙ'],
    +  STANDALONEWEEKDAYS: ['ⴰⵙⴰⵎⴰⵙ', 'ⴰⵢⵏⴰⵙ',
    +    'ⴰⵙⵉⵏⴰⵙ', 'ⴰⴽⵕⴰⵙ', 'ⴰⴽⵡⴰⵙ',
    +    'ⴰⵙⵉⵎⵡⴰⵙ', 'ⴰⵙⵉⴹⵢⴰⵙ'],
    +  SHORTWEEKDAYS: ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ',
    +    'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
    +  STANDALONESHORTWEEKDAYS: ['ⴰⵙⴰ', 'ⴰⵢⵏ', 'ⴰⵙⵉ', 'ⴰⴽⵕ',
    +    'ⴰⴽⵡ', 'ⴰⵙⵉⵎ', 'ⴰⵙⵉⴹ'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    +  SHORTQUARTERS: ['ⴰⴽ 1', 'ⴰⴽ 2', 'ⴰⴽ 3', 'ⴰⴽ 4'],
    +  QUARTERS: ['ⴰⴽⵕⴰⴹⵢⵓⵔ 1', 'ⴰⴽⵕⴰⴹⵢⵓⵔ 2',
    +    'ⴰⴽⵕⴰⴹⵢⵓⵔ 3', 'ⴰⴽⵕⴰⴹⵢⵓⵔ 4'],
    +  AMPMS: ['ⵜⵉⴼⴰⵡⵜ', 'ⵜⴰⴷⴳⴳⵯⴰⵜ'],
    +  DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM, y', 'd/M/y'],
    +  TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 0,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 6
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zgh_MA.
    + */
    +goog.i18n.DateTimeSymbols_zgh_MA = goog.i18n.DateTimeSymbols_zgh;
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hans.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hans = {
    +  ERAS: ['公元前', '公元'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['一月', '二月', '三月', '四月', '五月', '六月', '七月',
    +    '八月', '九月', '十月', '十一月', '十二月'],
    +  STANDALONEMONTHS: ['一月', '二月', '三月', '四月', '五月', '六月',
    +    '七月', '八月', '九月', '十月', '十一月', '十二月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四', '周五',
    +    '周六'],
    +  STANDALONESHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四',
    +    '周五', '周六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季度', '2季度', '3季度', '4季度'],
    +  QUARTERS: ['第一季度', '第二季度', '第三季度', '第四季度'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'yy/M/d'],
    +  TIMEFORMATS: ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hans_CN.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hans_CN = goog.i18n.DateTimeSymbols_zh_Hans;
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hans_HK.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hans_HK = {
    +  ERAS: ['公元前', '公元'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['一月', '二月', '三月', '四月', '五月', '六月', '七月',
    +    '八月', '九月', '十月', '十一月', '十二月'],
    +  STANDALONEMONTHS: ['一月', '二月', '三月', '四月', '五月', '六月',
    +    '七月', '八月', '九月', '十月', '十一月', '十二月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四', '周五',
    +    '周六'],
    +  STANDALONESHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四',
    +    '周五', '周六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季度', '2季度', '3季度', '4季度'],
    +  QUARTERS: ['第一季度', '第二季度', '第三季度', '第四季度'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'd/M/yy'],
    +  TIMEFORMATS: ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1}{0}', '{1}{0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hans_MO.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hans_MO = {
    +  ERAS: ['公元前', '公元'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['一月', '二月', '三月', '四月', '五月', '六月', '七月',
    +    '八月', '九月', '十月', '十一月', '十二月'],
    +  STANDALONEMONTHS: ['一月', '二月', '三月', '四月', '五月', '六月',
    +    '七月', '八月', '九月', '十月', '十一月', '十二月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四', '周五',
    +    '周六'],
    +  STANDALONESHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四',
    +    '周五', '周六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季度', '2季度', '3季度', '4季度'],
    +  QUARTERS: ['第一季度', '第二季度', '第三季度', '第四季度'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'd/M/yy'],
    +  TIMEFORMATS: ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1}{0}', '{1}{0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hans_SG.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hans_SG = {
    +  ERAS: ['公元前', '公元'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['一月', '二月', '三月', '四月', '五月', '六月', '七月',
    +    '八月', '九月', '十月', '十一月', '十二月'],
    +  STANDALONEMONTHS: ['一月', '二月', '三月', '四月', '五月', '六月',
    +    '七月', '八月', '九月', '十月', '十一月', '十二月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四', '周五',
    +    '周六'],
    +  STANDALONESHORTWEEKDAYS: ['周日', '周一', '周二', '周三', '周四',
    +    '周五', '周六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季度', '2季度', '3季度', '4季度'],
    +  QUARTERS: ['第一季度', '第二季度', '第三季度', '第四季度'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'dd/MM/yy'],
    +  TIMEFORMATS: ['zzzzah:mm:ss', 'ahh:mm:ssz', 'ah:mm:ss', 'ahh:mm'],
    +  DATETIMEFORMATS: ['{1}{0}', '{1}{0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hant.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hant = {
    +  ERAS: ['西元前', '西元'],
    +  ERANAMES: ['西元前', '西元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四', '週五',
    +    '週六'],
    +  STANDALONESHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四',
    +    '週五', '週六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['1季', '2季', '3季', '4季'],
    +  QUARTERS: ['第1季', '第2季', '第3季', '第4季'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日 EEEE', 'y年M月d日', 'y年M月d日', 'y/M/d'],
    +  TIMEFORMATS: ['ah:mm:ss [zzzz]', 'ah:mm:ss [z]', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hant_HK.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hant_HK = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四', '週五',
    +    '週六'],
    +  STANDALONESHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四',
    +    '週五', '週六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['第1季', '第2季', '第3季', '第4季'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'd/M/yy'],
    +  TIMEFORMATS: ['ah:mm:ss [zzzz]', 'ah:mm:ss [z]', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hant_MO.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hant_MO = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['公元前', '公元'],
    +  NARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
    +  STANDALONENARROWMONTHS: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
    +    '11', '12'],
    +  MONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONEMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月',
    +    '8月', '9月', '10月', '11月', '12月'],
    +  SHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月',
    +    '9月', '10月', '11月', '12月'],
    +  STANDALONESHORTMONTHS: ['1月', '2月', '3月', '4月', '5月', '6月',
    +    '7月', '8月', '9月', '10月', '11月', '12月'],
    +  WEEKDAYS: ['星期日', '星期一', '星期二', '星期三', '星期四',
    +    '星期五', '星期六'],
    +  STANDALONEWEEKDAYS: ['星期日', '星期一', '星期二', '星期三',
    +    '星期四', '星期五', '星期六'],
    +  SHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四', '週五',
    +    '週六'],
    +  STANDALONESHORTWEEKDAYS: ['週日', '週一', '週二', '週三', '週四',
    +    '週五', '週六'],
    +  NARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  STANDALONENARROWWEEKDAYS: ['日', '一', '二', '三', '四', '五', '六'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['第1季', '第2季', '第3季', '第4季'],
    +  AMPMS: ['上午', '下午'],
    +  DATEFORMATS: ['y年MM月dd日EEEE', 'y年MM月dd日', 'y年M月d日',
    +    'yy年M月d日'],
    +  TIMEFORMATS: ['ah:mm:ss [zzzz]', 'ah:mm:ss [z]', 'ah:mm:ss', 'ah:mm'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Date/time formatting symbols for locale zh_Hant_TW.
    + */
    +goog.i18n.DateTimeSymbols_zh_Hant_TW = goog.i18n.DateTimeSymbols_zh_Hant;
    +
    +
    +/**
    + * Date/time formatting symbols for locale zu_ZA.
    + */
    +goog.i18n.DateTimeSymbols_zu_ZA = {
    +  ERAS: ['BC', 'AD'],
    +  ERANAMES: ['BC', 'AD'],
    +  NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],
    +  STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',
    +    'N', 'D'],
    +  MONTHS: ['Januwari', 'Februwari', 'Mashi', 'Apreli', 'Meyi', 'Juni', 'Julayi',
    +    'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'],
    +  STANDALONEMONTHS: ['Januwari', 'Februwari', 'Mashi', 'Apreli', 'Meyi', 'Juni',
    +    'Julayi', 'Agasti', 'Septhemba', 'Okthoba', 'Novemba', 'Disemba'],
    +  SHORTMONTHS: ['Jan', 'Feb', 'Mas', 'Apr', 'Mey', 'Jun', 'Jul', 'Aga', 'Sep',
    +    'Okt', 'Nov', 'Dis'],
    +  STANDALONESHORTMONTHS: ['Jan', 'Feb', 'Mas', 'Apr', 'Mey', 'Jun', 'Jul',
    +    'Aga', 'Sep', 'Okt', 'Nov', 'Dis'],
    +  WEEKDAYS: ['Sonto', 'Msombuluko', 'Lwesibili', 'Lwesithathu', 'Lwesine',
    +    'Lwesihlanu', 'Mgqibelo'],
    +  STANDALONEWEEKDAYS: ['Sonto', 'Msombuluko', 'Lwesibili', 'Lwesithathu',
    +    'Lwesine', 'Lwesihlanu', 'Mgqibelo'],
    +  SHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
    +  STANDALONESHORTWEEKDAYS: ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
    +  NARROWWEEKDAYS: ['S', 'M', 'T', 'T', 'S', 'H', 'M'],
    +  STANDALONENARROWWEEKDAYS: ['S', 'M', 'B', 'T', 'S', 'H', 'M'],
    +  SHORTQUARTERS: ['Q1', 'Q2', 'Q3', 'Q4'],
    +  QUARTERS: ['ikota engu-1', 'ikota engu-2', 'ikota engu-3', 'ikota engu-4'],
    +  AMPMS: ['Ekuseni', 'Ntambama'],
    +  DATEFORMATS: ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
    +  TIMEFORMATS: ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
    +  DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
    +  FIRSTDAYOFWEEK: 6,
    +  WEEKENDRANGE: [5, 6],
    +  FIRSTWEEKCUTOFFDAY: 5
    +};
    +
    +
    +/**
    + * Selected date/time formatting symbols by locale.
    + */
    +if (goog.LOCALE == 'aa') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'aa_DJ' || goog.LOCALE == 'aa-DJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_aa_DJ;
    +}
    +
    +if (goog.LOCALE == 'aa_ER' || goog.LOCALE == 'aa-ER') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'aa_ET' || goog.LOCALE == 'aa-ET') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'af_NA' || goog.LOCALE == 'af-NA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_af_NA;
    +}
    +
    +if (goog.LOCALE == 'af_ZA' || goog.LOCALE == 'af-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_af_ZA;
    +}
    +
    +if (goog.LOCALE == 'agq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'agq_CM' || goog.LOCALE == 'agq-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'ak') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ak_GH' || goog.LOCALE == 'ak-GH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'am_ET' || goog.LOCALE == 'am-ET') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_am_ET;
    +}
    +
    +if (goog.LOCALE == 'ar_001' || goog.LOCALE == 'ar-001') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_001;
    +}
    +
    +if (goog.LOCALE == 'ar_AE' || goog.LOCALE == 'ar-AE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_AE;
    +}
    +
    +if (goog.LOCALE == 'ar_BH' || goog.LOCALE == 'ar-BH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_BH;
    +}
    +
    +if (goog.LOCALE == 'ar_DJ' || goog.LOCALE == 'ar-DJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_DJ;
    +}
    +
    +if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_DZ;
    +}
    +
    +if (goog.LOCALE == 'ar_EG' || goog.LOCALE == 'ar-EG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_EG;
    +}
    +
    +if (goog.LOCALE == 'ar_EH' || goog.LOCALE == 'ar-EH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_EH;
    +}
    +
    +if (goog.LOCALE == 'ar_ER' || goog.LOCALE == 'ar-ER') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_ER;
    +}
    +
    +if (goog.LOCALE == 'ar_IL' || goog.LOCALE == 'ar-IL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_IL;
    +}
    +
    +if (goog.LOCALE == 'ar_IQ' || goog.LOCALE == 'ar-IQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_IQ;
    +}
    +
    +if (goog.LOCALE == 'ar_JO' || goog.LOCALE == 'ar-JO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_JO;
    +}
    +
    +if (goog.LOCALE == 'ar_KM' || goog.LOCALE == 'ar-KM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_KM;
    +}
    +
    +if (goog.LOCALE == 'ar_KW' || goog.LOCALE == 'ar-KW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_KW;
    +}
    +
    +if (goog.LOCALE == 'ar_LB' || goog.LOCALE == 'ar-LB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_LB;
    +}
    +
    +if (goog.LOCALE == 'ar_LY' || goog.LOCALE == 'ar-LY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_LY;
    +}
    +
    +if (goog.LOCALE == 'ar_MA' || goog.LOCALE == 'ar-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_MA;
    +}
    +
    +if (goog.LOCALE == 'ar_MR' || goog.LOCALE == 'ar-MR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_MR;
    +}
    +
    +if (goog.LOCALE == 'ar_OM' || goog.LOCALE == 'ar-OM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_OM;
    +}
    +
    +if (goog.LOCALE == 'ar_PS' || goog.LOCALE == 'ar-PS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_PS;
    +}
    +
    +if (goog.LOCALE == 'ar_QA' || goog.LOCALE == 'ar-QA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_QA;
    +}
    +
    +if (goog.LOCALE == 'ar_SA' || goog.LOCALE == 'ar-SA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SA;
    +}
    +
    +if (goog.LOCALE == 'ar_SD' || goog.LOCALE == 'ar-SD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SD;
    +}
    +
    +if (goog.LOCALE == 'ar_SO' || goog.LOCALE == 'ar-SO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SO;
    +}
    +
    +if (goog.LOCALE == 'ar_SS' || goog.LOCALE == 'ar-SS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SS;
    +}
    +
    +if (goog.LOCALE == 'ar_SY' || goog.LOCALE == 'ar-SY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_SY;
    +}
    +
    +if (goog.LOCALE == 'ar_TD' || goog.LOCALE == 'ar-TD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_TD;
    +}
    +
    +if (goog.LOCALE == 'ar_TN' || goog.LOCALE == 'ar-TN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_TN;
    +}
    +
    +if (goog.LOCALE == 'ar_YE' || goog.LOCALE == 'ar-YE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ar_YE;
    +}
    +
    +if (goog.LOCALE == 'as') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'as_IN' || goog.LOCALE == 'as-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'asa') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'asa_TZ' || goog.LOCALE == 'asa-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'ast') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'ast_ES' || goog.LOCALE == 'ast-ES') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl' || goog.LOCALE == 'az-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Latn' || goog.LOCALE == 'az-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'bas') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'bas_CM' || goog.LOCALE == 'bas-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'be') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'be_BY' || goog.LOCALE == 'be-BY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'bem') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bem_ZM' || goog.LOCALE == 'bem-ZM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bez') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bez_TZ' || goog.LOCALE == 'bez-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bg_BG' || goog.LOCALE == 'bg-BG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bg_BG;
    +}
    +
    +if (goog.LOCALE == 'bm') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn' || goog.LOCALE == 'bm-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn_ML' || goog.LOCALE == 'bm-Latn-ML') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bn_BD' || goog.LOCALE == 'bn-BD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bn_BD;
    +}
    +
    +if (goog.LOCALE == 'bn_IN' || goog.LOCALE == 'bn-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bn_IN;
    +}
    +
    +if (goog.LOCALE == 'bo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_CN' || goog.LOCALE == 'bo-CN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_IN' || goog.LOCALE == 'bo-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'br_FR' || goog.LOCALE == 'br-FR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_br_FR;
    +}
    +
    +if (goog.LOCALE == 'brx') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'brx_IN' || goog.LOCALE == 'brx-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'bs') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl' || goog.LOCALE == 'bs-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl_BA' || goog.LOCALE == 'bs-Cyrl-BA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn' || goog.LOCALE == 'bs-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn_BA' || goog.LOCALE == 'bs-Latn-BA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'ca_AD' || goog.LOCALE == 'ca-AD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca_AD;
    +}
    +
    +if (goog.LOCALE == 'ca_ES' || goog.LOCALE == 'ca-ES') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca_ES;
    +}
    +
    +if (goog.LOCALE == 'ca_ES_VALENCIA' || goog.LOCALE == 'ca-ES-VALENCIA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca_ES;
    +}
    +
    +if (goog.LOCALE == 'ca_FR' || goog.LOCALE == 'ca-FR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca_FR;
    +}
    +
    +if (goog.LOCALE == 'ca_IT' || goog.LOCALE == 'ca-IT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ca_IT;
    +}
    +
    +if (goog.LOCALE == 'cgg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'cgg_UG' || goog.LOCALE == 'cgg-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'chr_US' || goog.LOCALE == 'chr-US') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_chr_US;
    +}
    +
    +if (goog.LOCALE == 'ckb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab' || goog.LOCALE == 'ckb-Arab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IQ' || goog.LOCALE == 'ckb-Arab-IQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IR' || goog.LOCALE == 'ckb-Arab-IR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_IQ' || goog.LOCALE == 'ckb-IQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_IR' || goog.LOCALE == 'ckb-IR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn' || goog.LOCALE == 'ckb-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn_IQ' || goog.LOCALE == 'ckb-Latn-IQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'cs_CZ' || goog.LOCALE == 'cs-CZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cs_CZ;
    +}
    +
    +if (goog.LOCALE == 'cy_GB' || goog.LOCALE == 'cy-GB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_cy_GB;
    +}
    +
    +if (goog.LOCALE == 'da_DK' || goog.LOCALE == 'da-DK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_da_DK;
    +}
    +
    +if (goog.LOCALE == 'da_GL' || goog.LOCALE == 'da-GL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_da_GL;
    +}
    +
    +if (goog.LOCALE == 'dav') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'dav_KE' || goog.LOCALE == 'dav-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'de_BE' || goog.LOCALE == 'de-BE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de_BE;
    +}
    +
    +if (goog.LOCALE == 'de_DE' || goog.LOCALE == 'de-DE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de_DE;
    +}
    +
    +if (goog.LOCALE == 'de_LI' || goog.LOCALE == 'de-LI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de_LI;
    +}
    +
    +if (goog.LOCALE == 'de_LU' || goog.LOCALE == 'de-LU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_de_LU;
    +}
    +
    +if (goog.LOCALE == 'dje') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dje_NE' || goog.LOCALE == 'dje-NE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dsb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dsb_DE' || goog.LOCALE == 'dsb-DE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dua') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dua_CM' || goog.LOCALE == 'dua-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dyo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dyo_SN' || goog.LOCALE == 'dyo-SN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dz') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'dz_BT' || goog.LOCALE == 'dz-BT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'ebu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ebu_KE' || goog.LOCALE == 'ebu-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ee') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_GH' || goog.LOCALE == 'ee-GH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_TG' || goog.LOCALE == 'ee-TG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'el_CY' || goog.LOCALE == 'el-CY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_el_CY;
    +}
    +
    +if (goog.LOCALE == 'el_GR' || goog.LOCALE == 'el-GR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_el_GR;
    +}
    +
    +if (goog.LOCALE == 'en_001' || goog.LOCALE == 'en-001') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_001;
    +}
    +
    +if (goog.LOCALE == 'en_150' || goog.LOCALE == 'en-150') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_150;
    +}
    +
    +if (goog.LOCALE == 'en_AG' || goog.LOCALE == 'en-AG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_AG;
    +}
    +
    +if (goog.LOCALE == 'en_AI' || goog.LOCALE == 'en-AI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_AI;
    +}
    +
    +if (goog.LOCALE == 'en_AS' || goog.LOCALE == 'en-AS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_AS;
    +}
    +
    +if (goog.LOCALE == 'en_BB' || goog.LOCALE == 'en-BB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BB;
    +}
    +
    +if (goog.LOCALE == 'en_BE' || goog.LOCALE == 'en-BE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BE;
    +}
    +
    +if (goog.LOCALE == 'en_BM' || goog.LOCALE == 'en-BM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BM;
    +}
    +
    +if (goog.LOCALE == 'en_BS' || goog.LOCALE == 'en-BS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BS;
    +}
    +
    +if (goog.LOCALE == 'en_BW' || goog.LOCALE == 'en-BW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BW;
    +}
    +
    +if (goog.LOCALE == 'en_BZ' || goog.LOCALE == 'en-BZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_BZ;
    +}
    +
    +if (goog.LOCALE == 'en_CA' || goog.LOCALE == 'en-CA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_CA;
    +}
    +
    +if (goog.LOCALE == 'en_CC' || goog.LOCALE == 'en-CC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_CC;
    +}
    +
    +if (goog.LOCALE == 'en_CK' || goog.LOCALE == 'en-CK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_CK;
    +}
    +
    +if (goog.LOCALE == 'en_CM' || goog.LOCALE == 'en-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_CM;
    +}
    +
    +if (goog.LOCALE == 'en_CX' || goog.LOCALE == 'en-CX') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_CX;
    +}
    +
    +if (goog.LOCALE == 'en_DG' || goog.LOCALE == 'en-DG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_DG;
    +}
    +
    +if (goog.LOCALE == 'en_DM' || goog.LOCALE == 'en-DM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_DM;
    +}
    +
    +if (goog.LOCALE == 'en_ER' || goog.LOCALE == 'en-ER') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_ER;
    +}
    +
    +if (goog.LOCALE == 'en_FJ' || goog.LOCALE == 'en-FJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_FJ;
    +}
    +
    +if (goog.LOCALE == 'en_FK' || goog.LOCALE == 'en-FK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_FK;
    +}
    +
    +if (goog.LOCALE == 'en_FM' || goog.LOCALE == 'en-FM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_FM;
    +}
    +
    +if (goog.LOCALE == 'en_GD' || goog.LOCALE == 'en-GD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GD;
    +}
    +
    +if (goog.LOCALE == 'en_GG' || goog.LOCALE == 'en-GG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GG;
    +}
    +
    +if (goog.LOCALE == 'en_GH' || goog.LOCALE == 'en-GH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GH;
    +}
    +
    +if (goog.LOCALE == 'en_GI' || goog.LOCALE == 'en-GI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GI;
    +}
    +
    +if (goog.LOCALE == 'en_GM' || goog.LOCALE == 'en-GM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GM;
    +}
    +
    +if (goog.LOCALE == 'en_GU' || goog.LOCALE == 'en-GU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GU;
    +}
    +
    +if (goog.LOCALE == 'en_GY' || goog.LOCALE == 'en-GY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_GY;
    +}
    +
    +if (goog.LOCALE == 'en_HK' || goog.LOCALE == 'en-HK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_HK;
    +}
    +
    +if (goog.LOCALE == 'en_IM' || goog.LOCALE == 'en-IM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IM;
    +}
    +
    +if (goog.LOCALE == 'en_IO' || goog.LOCALE == 'en-IO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_IO;
    +}
    +
    +if (goog.LOCALE == 'en_JE' || goog.LOCALE == 'en-JE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_JE;
    +}
    +
    +if (goog.LOCALE == 'en_JM' || goog.LOCALE == 'en-JM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_JM;
    +}
    +
    +if (goog.LOCALE == 'en_KE' || goog.LOCALE == 'en-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_KE;
    +}
    +
    +if (goog.LOCALE == 'en_KI' || goog.LOCALE == 'en-KI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_KI;
    +}
    +
    +if (goog.LOCALE == 'en_KN' || goog.LOCALE == 'en-KN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_KN;
    +}
    +
    +if (goog.LOCALE == 'en_KY' || goog.LOCALE == 'en-KY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_KY;
    +}
    +
    +if (goog.LOCALE == 'en_LC' || goog.LOCALE == 'en-LC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_LC;
    +}
    +
    +if (goog.LOCALE == 'en_LR' || goog.LOCALE == 'en-LR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_LR;
    +}
    +
    +if (goog.LOCALE == 'en_LS' || goog.LOCALE == 'en-LS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_LS;
    +}
    +
    +if (goog.LOCALE == 'en_MG' || goog.LOCALE == 'en-MG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MG;
    +}
    +
    +if (goog.LOCALE == 'en_MH' || goog.LOCALE == 'en-MH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MH;
    +}
    +
    +if (goog.LOCALE == 'en_MO' || goog.LOCALE == 'en-MO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MO;
    +}
    +
    +if (goog.LOCALE == 'en_MP' || goog.LOCALE == 'en-MP') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MP;
    +}
    +
    +if (goog.LOCALE == 'en_MS' || goog.LOCALE == 'en-MS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MS;
    +}
    +
    +if (goog.LOCALE == 'en_MT' || goog.LOCALE == 'en-MT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MT;
    +}
    +
    +if (goog.LOCALE == 'en_MU' || goog.LOCALE == 'en-MU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MU;
    +}
    +
    +if (goog.LOCALE == 'en_MW' || goog.LOCALE == 'en-MW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MW;
    +}
    +
    +if (goog.LOCALE == 'en_MY' || goog.LOCALE == 'en-MY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_MY;
    +}
    +
    +if (goog.LOCALE == 'en_NA' || goog.LOCALE == 'en-NA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NA;
    +}
    +
    +if (goog.LOCALE == 'en_NF' || goog.LOCALE == 'en-NF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NF;
    +}
    +
    +if (goog.LOCALE == 'en_NG' || goog.LOCALE == 'en-NG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NG;
    +}
    +
    +if (goog.LOCALE == 'en_NR' || goog.LOCALE == 'en-NR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NR;
    +}
    +
    +if (goog.LOCALE == 'en_NU' || goog.LOCALE == 'en-NU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NU;
    +}
    +
    +if (goog.LOCALE == 'en_NZ' || goog.LOCALE == 'en-NZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_NZ;
    +}
    +
    +if (goog.LOCALE == 'en_PG' || goog.LOCALE == 'en-PG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PG;
    +}
    +
    +if (goog.LOCALE == 'en_PH' || goog.LOCALE == 'en-PH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PH;
    +}
    +
    +if (goog.LOCALE == 'en_PK' || goog.LOCALE == 'en-PK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PK;
    +}
    +
    +if (goog.LOCALE == 'en_PN' || goog.LOCALE == 'en-PN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PN;
    +}
    +
    +if (goog.LOCALE == 'en_PR' || goog.LOCALE == 'en-PR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PR;
    +}
    +
    +if (goog.LOCALE == 'en_PW' || goog.LOCALE == 'en-PW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_PW;
    +}
    +
    +if (goog.LOCALE == 'en_RW' || goog.LOCALE == 'en-RW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_RW;
    +}
    +
    +if (goog.LOCALE == 'en_SB' || goog.LOCALE == 'en-SB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SB;
    +}
    +
    +if (goog.LOCALE == 'en_SC' || goog.LOCALE == 'en-SC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SC;
    +}
    +
    +if (goog.LOCALE == 'en_SD' || goog.LOCALE == 'en-SD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SD;
    +}
    +
    +if (goog.LOCALE == 'en_SH' || goog.LOCALE == 'en-SH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SH;
    +}
    +
    +if (goog.LOCALE == 'en_SL' || goog.LOCALE == 'en-SL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SL;
    +}
    +
    +if (goog.LOCALE == 'en_SS' || goog.LOCALE == 'en-SS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SS;
    +}
    +
    +if (goog.LOCALE == 'en_SX' || goog.LOCALE == 'en-SX') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SX;
    +}
    +
    +if (goog.LOCALE == 'en_SZ' || goog.LOCALE == 'en-SZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_SZ;
    +}
    +
    +if (goog.LOCALE == 'en_TC' || goog.LOCALE == 'en-TC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TC;
    +}
    +
    +if (goog.LOCALE == 'en_TK' || goog.LOCALE == 'en-TK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TK;
    +}
    +
    +if (goog.LOCALE == 'en_TO' || goog.LOCALE == 'en-TO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TO;
    +}
    +
    +if (goog.LOCALE == 'en_TT' || goog.LOCALE == 'en-TT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TT;
    +}
    +
    +if (goog.LOCALE == 'en_TV' || goog.LOCALE == 'en-TV') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TV;
    +}
    +
    +if (goog.LOCALE == 'en_TZ' || goog.LOCALE == 'en-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_TZ;
    +}
    +
    +if (goog.LOCALE == 'en_UG' || goog.LOCALE == 'en-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_UG;
    +}
    +
    +if (goog.LOCALE == 'en_UM' || goog.LOCALE == 'en-UM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_UM;
    +}
    +
    +if (goog.LOCALE == 'en_VC' || goog.LOCALE == 'en-VC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_VC;
    +}
    +
    +if (goog.LOCALE == 'en_VG' || goog.LOCALE == 'en-VG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_VG;
    +}
    +
    +if (goog.LOCALE == 'en_VI' || goog.LOCALE == 'en-VI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_VI;
    +}
    +
    +if (goog.LOCALE == 'en_VU' || goog.LOCALE == 'en-VU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_VU;
    +}
    +
    +if (goog.LOCALE == 'en_WS' || goog.LOCALE == 'en-WS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_WS;
    +}
    +
    +if (goog.LOCALE == 'en_ZM' || goog.LOCALE == 'en-ZM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_ZM;
    +}
    +
    +if (goog.LOCALE == 'en_ZW' || goog.LOCALE == 'en-ZW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_ZW;
    +}
    +
    +if (goog.LOCALE == 'eo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'eo_001' || goog.LOCALE == 'eo-001') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'es_AR' || goog.LOCALE == 'es-AR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_AR;
    +}
    +
    +if (goog.LOCALE == 'es_BO' || goog.LOCALE == 'es-BO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_BO;
    +}
    +
    +if (goog.LOCALE == 'es_CL' || goog.LOCALE == 'es-CL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_CL;
    +}
    +
    +if (goog.LOCALE == 'es_CO' || goog.LOCALE == 'es-CO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_CO;
    +}
    +
    +if (goog.LOCALE == 'es_CR' || goog.LOCALE == 'es-CR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_CR;
    +}
    +
    +if (goog.LOCALE == 'es_CU' || goog.LOCALE == 'es-CU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_CU;
    +}
    +
    +if (goog.LOCALE == 'es_DO' || goog.LOCALE == 'es-DO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_DO;
    +}
    +
    +if (goog.LOCALE == 'es_EA' || goog.LOCALE == 'es-EA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_EA;
    +}
    +
    +if (goog.LOCALE == 'es_EC' || goog.LOCALE == 'es-EC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_EC;
    +}
    +
    +if (goog.LOCALE == 'es_GQ' || goog.LOCALE == 'es-GQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_GQ;
    +}
    +
    +if (goog.LOCALE == 'es_GT' || goog.LOCALE == 'es-GT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_GT;
    +}
    +
    +if (goog.LOCALE == 'es_HN' || goog.LOCALE == 'es-HN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_HN;
    +}
    +
    +if (goog.LOCALE == 'es_IC' || goog.LOCALE == 'es-IC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_IC;
    +}
    +
    +if (goog.LOCALE == 'es_MX' || goog.LOCALE == 'es-MX') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_MX;
    +}
    +
    +if (goog.LOCALE == 'es_NI' || goog.LOCALE == 'es-NI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_NI;
    +}
    +
    +if (goog.LOCALE == 'es_PA' || goog.LOCALE == 'es-PA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_PA;
    +}
    +
    +if (goog.LOCALE == 'es_PE' || goog.LOCALE == 'es-PE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_PE;
    +}
    +
    +if (goog.LOCALE == 'es_PH' || goog.LOCALE == 'es-PH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_PH;
    +}
    +
    +if (goog.LOCALE == 'es_PR' || goog.LOCALE == 'es-PR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_PR;
    +}
    +
    +if (goog.LOCALE == 'es_PY' || goog.LOCALE == 'es-PY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_PY;
    +}
    +
    +if (goog.LOCALE == 'es_SV' || goog.LOCALE == 'es-SV') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_SV;
    +}
    +
    +if (goog.LOCALE == 'es_US' || goog.LOCALE == 'es-US') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_US;
    +}
    +
    +if (goog.LOCALE == 'es_UY' || goog.LOCALE == 'es-UY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_UY;
    +}
    +
    +if (goog.LOCALE == 'es_VE' || goog.LOCALE == 'es-VE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_es_VE;
    +}
    +
    +if (goog.LOCALE == 'et_EE' || goog.LOCALE == 'et-EE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_et_EE;
    +}
    +
    +if (goog.LOCALE == 'eu_ES' || goog.LOCALE == 'eu-ES') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_eu_ES;
    +}
    +
    +if (goog.LOCALE == 'ewo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'ewo_CM' || goog.LOCALE == 'ewo-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'fa_AF' || goog.LOCALE == 'fa-AF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa_AF;
    +}
    +
    +if (goog.LOCALE == 'fa_IR' || goog.LOCALE == 'fa-IR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fa_IR;
    +}
    +
    +if (goog.LOCALE == 'ff') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_CM' || goog.LOCALE == 'ff-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_GN' || goog.LOCALE == 'ff-GN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_MR' || goog.LOCALE == 'ff-MR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_SN' || goog.LOCALE == 'ff-SN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'fi_FI' || goog.LOCALE == 'fi-FI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fi_FI;
    +}
    +
    +if (goog.LOCALE == 'fil_PH' || goog.LOCALE == 'fil-PH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fil_PH;
    +}
    +
    +if (goog.LOCALE == 'fo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fo_FO' || goog.LOCALE == 'fo-FO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fr_BE' || goog.LOCALE == 'fr-BE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_BE;
    +}
    +
    +if (goog.LOCALE == 'fr_BF' || goog.LOCALE == 'fr-BF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_BF;
    +}
    +
    +if (goog.LOCALE == 'fr_BI' || goog.LOCALE == 'fr-BI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_BI;
    +}
    +
    +if (goog.LOCALE == 'fr_BJ' || goog.LOCALE == 'fr-BJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_BJ;
    +}
    +
    +if (goog.LOCALE == 'fr_BL' || goog.LOCALE == 'fr-BL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_BL;
    +}
    +
    +if (goog.LOCALE == 'fr_CD' || goog.LOCALE == 'fr-CD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CD;
    +}
    +
    +if (goog.LOCALE == 'fr_CF' || goog.LOCALE == 'fr-CF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CF;
    +}
    +
    +if (goog.LOCALE == 'fr_CG' || goog.LOCALE == 'fr-CG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CG;
    +}
    +
    +if (goog.LOCALE == 'fr_CH' || goog.LOCALE == 'fr-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CH;
    +}
    +
    +if (goog.LOCALE == 'fr_CI' || goog.LOCALE == 'fr-CI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CI;
    +}
    +
    +if (goog.LOCALE == 'fr_CM' || goog.LOCALE == 'fr-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CM;
    +}
    +
    +if (goog.LOCALE == 'fr_DJ' || goog.LOCALE == 'fr-DJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_DJ;
    +}
    +
    +if (goog.LOCALE == 'fr_DZ' || goog.LOCALE == 'fr-DZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_DZ;
    +}
    +
    +if (goog.LOCALE == 'fr_FR' || goog.LOCALE == 'fr-FR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_FR;
    +}
    +
    +if (goog.LOCALE == 'fr_GA' || goog.LOCALE == 'fr-GA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_GA;
    +}
    +
    +if (goog.LOCALE == 'fr_GF' || goog.LOCALE == 'fr-GF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_GF;
    +}
    +
    +if (goog.LOCALE == 'fr_GN' || goog.LOCALE == 'fr-GN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_GN;
    +}
    +
    +if (goog.LOCALE == 'fr_GP' || goog.LOCALE == 'fr-GP') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_GP;
    +}
    +
    +if (goog.LOCALE == 'fr_GQ' || goog.LOCALE == 'fr-GQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_GQ;
    +}
    +
    +if (goog.LOCALE == 'fr_HT' || goog.LOCALE == 'fr-HT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_HT;
    +}
    +
    +if (goog.LOCALE == 'fr_KM' || goog.LOCALE == 'fr-KM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_KM;
    +}
    +
    +if (goog.LOCALE == 'fr_LU' || goog.LOCALE == 'fr-LU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_LU;
    +}
    +
    +if (goog.LOCALE == 'fr_MA' || goog.LOCALE == 'fr-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MA;
    +}
    +
    +if (goog.LOCALE == 'fr_MC' || goog.LOCALE == 'fr-MC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MC;
    +}
    +
    +if (goog.LOCALE == 'fr_MF' || goog.LOCALE == 'fr-MF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MF;
    +}
    +
    +if (goog.LOCALE == 'fr_MG' || goog.LOCALE == 'fr-MG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MG;
    +}
    +
    +if (goog.LOCALE == 'fr_ML' || goog.LOCALE == 'fr-ML') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_ML;
    +}
    +
    +if (goog.LOCALE == 'fr_MQ' || goog.LOCALE == 'fr-MQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MQ;
    +}
    +
    +if (goog.LOCALE == 'fr_MR' || goog.LOCALE == 'fr-MR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MR;
    +}
    +
    +if (goog.LOCALE == 'fr_MU' || goog.LOCALE == 'fr-MU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_MU;
    +}
    +
    +if (goog.LOCALE == 'fr_NC' || goog.LOCALE == 'fr-NC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_NC;
    +}
    +
    +if (goog.LOCALE == 'fr_NE' || goog.LOCALE == 'fr-NE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_NE;
    +}
    +
    +if (goog.LOCALE == 'fr_PF' || goog.LOCALE == 'fr-PF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_PF;
    +}
    +
    +if (goog.LOCALE == 'fr_PM' || goog.LOCALE == 'fr-PM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_PM;
    +}
    +
    +if (goog.LOCALE == 'fr_RE' || goog.LOCALE == 'fr-RE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_RE;
    +}
    +
    +if (goog.LOCALE == 'fr_RW' || goog.LOCALE == 'fr-RW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_RW;
    +}
    +
    +if (goog.LOCALE == 'fr_SC' || goog.LOCALE == 'fr-SC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_SC;
    +}
    +
    +if (goog.LOCALE == 'fr_SN' || goog.LOCALE == 'fr-SN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_SN;
    +}
    +
    +if (goog.LOCALE == 'fr_SY' || goog.LOCALE == 'fr-SY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_SY;
    +}
    +
    +if (goog.LOCALE == 'fr_TD' || goog.LOCALE == 'fr-TD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_TD;
    +}
    +
    +if (goog.LOCALE == 'fr_TG' || goog.LOCALE == 'fr-TG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_TG;
    +}
    +
    +if (goog.LOCALE == 'fr_TN' || goog.LOCALE == 'fr-TN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_TN;
    +}
    +
    +if (goog.LOCALE == 'fr_VU' || goog.LOCALE == 'fr-VU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_VU;
    +}
    +
    +if (goog.LOCALE == 'fr_WF' || goog.LOCALE == 'fr-WF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_WF;
    +}
    +
    +if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_YT;
    +}
    +
    +if (goog.LOCALE == 'fur') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fur_IT' || goog.LOCALE == 'fur-IT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fy') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga_IE;
    +}
    +
    +if (goog.LOCALE == 'gd') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gd_GB' || goog.LOCALE == 'gd-GB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gl_ES' || goog.LOCALE == 'gl-ES') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gl_ES;
    +}
    +
    +if (goog.LOCALE == 'gsw_CH' || goog.LOCALE == 'gsw-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gsw_CH;
    +}
    +
    +if (goog.LOCALE == 'gsw_FR' || goog.LOCALE == 'gsw-FR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gsw_FR;
    +}
    +
    +if (goog.LOCALE == 'gsw_LI' || goog.LOCALE == 'gsw-LI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gsw_LI;
    +}
    +
    +if (goog.LOCALE == 'gu_IN' || goog.LOCALE == 'gu-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gu_IN;
    +}
    +
    +if (goog.LOCALE == 'guz') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'guz_KE' || goog.LOCALE == 'guz-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'gv') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'gv_IM' || goog.LOCALE == 'gv-IM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'ha') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn' || goog.LOCALE == 'ha-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_GH' || goog.LOCALE == 'ha-Latn-GH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NE' || goog.LOCALE == 'ha-Latn-NE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NG' || goog.LOCALE == 'ha-Latn-NG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'haw_US' || goog.LOCALE == 'haw-US') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_haw_US;
    +}
    +
    +if (goog.LOCALE == 'he_IL' || goog.LOCALE == 'he-IL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_he_IL;
    +}
    +
    +if (goog.LOCALE == 'hi_IN' || goog.LOCALE == 'hi-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hi_IN;
    +}
    +
    +if (goog.LOCALE == 'hr_BA' || goog.LOCALE == 'hr-BA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hr_BA;
    +}
    +
    +if (goog.LOCALE == 'hr_HR' || goog.LOCALE == 'hr-HR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hr_HR;
    +}
    +
    +if (goog.LOCALE == 'hsb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'hsb_DE' || goog.LOCALE == 'hsb-DE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'hu_HU' || goog.LOCALE == 'hu-HU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hu_HU;
    +}
    +
    +if (goog.LOCALE == 'hy_AM' || goog.LOCALE == 'hy-AM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_hy_AM;
    +}
    +
    +if (goog.LOCALE == 'ia') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'ia_FR' || goog.LOCALE == 'ia-FR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'id_ID' || goog.LOCALE == 'id-ID') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_id_ID;
    +}
    +
    +if (goog.LOCALE == 'ig') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ig_NG' || goog.LOCALE == 'ig-NG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ii') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'ii_CN' || goog.LOCALE == 'ii-CN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'is_IS' || goog.LOCALE == 'is-IS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_is_IS;
    +}
    +
    +if (goog.LOCALE == 'it_CH' || goog.LOCALE == 'it-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_it_CH;
    +}
    +
    +if (goog.LOCALE == 'it_IT' || goog.LOCALE == 'it-IT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_it_IT;
    +}
    +
    +if (goog.LOCALE == 'it_SM' || goog.LOCALE == 'it-SM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_it_SM;
    +}
    +
    +if (goog.LOCALE == 'ja_JP' || goog.LOCALE == 'ja-JP') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ja_JP;
    +}
    +
    +if (goog.LOCALE == 'jgo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jgo_CM' || goog.LOCALE == 'jgo-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jmc') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'jmc_TZ' || goog.LOCALE == 'jmc-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'ka_GE' || goog.LOCALE == 'ka-GE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ka_GE;
    +}
    +
    +if (goog.LOCALE == 'kab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kab_DZ' || goog.LOCALE == 'kab-DZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kam') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kam_KE' || goog.LOCALE == 'kam-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kde') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kde_TZ' || goog.LOCALE == 'kde-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kea') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'kea_CV' || goog.LOCALE == 'kea-CV') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'khq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'khq_ML' || goog.LOCALE == 'khq-ML') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'ki') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'ki_KE' || goog.LOCALE == 'ki-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl' || goog.LOCALE == 'kk-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl_KZ' || goog.LOCALE == 'kk-Cyrl-KZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kkj') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kkj_CM' || goog.LOCALE == 'kkj-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kl_GL' || goog.LOCALE == 'kl-GL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kln') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'kln_KE' || goog.LOCALE == 'kln-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'km_KH' || goog.LOCALE == 'km-KH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_km_KH;
    +}
    +
    +if (goog.LOCALE == 'kn_IN' || goog.LOCALE == 'kn-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kn_IN;
    +}
    +
    +if (goog.LOCALE == 'ko_KP' || goog.LOCALE == 'ko-KP') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ko_KP;
    +}
    +
    +if (goog.LOCALE == 'ko_KR' || goog.LOCALE == 'ko-KR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ko_KR;
    +}
    +
    +if (goog.LOCALE == 'kok') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'kok_IN' || goog.LOCALE == 'kok-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'ks') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab' || goog.LOCALE == 'ks-Arab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab_IN' || goog.LOCALE == 'ks-Arab-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ksb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksb_TZ' || goog.LOCALE == 'ksb-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksf') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksf_CM' || goog.LOCALE == 'ksf-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'ksh_DE' || goog.LOCALE == 'ksh-DE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'kw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'kw_GB' || goog.LOCALE == 'kw-GB') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl' || goog.LOCALE == 'ky-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl_KG' || goog.LOCALE == 'ky-Cyrl-KG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'lag') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lag_TZ' || goog.LOCALE == 'lag-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lb') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lb_LU' || goog.LOCALE == 'lb-LU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lg_UG' || goog.LOCALE == 'lg-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lkt') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'lkt_US' || goog.LOCALE == 'lkt-US') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'ln_AO' || goog.LOCALE == 'ln-AO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ln_AO;
    +}
    +
    +if (goog.LOCALE == 'ln_CD' || goog.LOCALE == 'ln-CD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ln_CD;
    +}
    +
    +if (goog.LOCALE == 'ln_CF' || goog.LOCALE == 'ln-CF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ln_CF;
    +}
    +
    +if (goog.LOCALE == 'ln_CG' || goog.LOCALE == 'ln-CG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ln_CG;
    +}
    +
    +if (goog.LOCALE == 'lo_LA' || goog.LOCALE == 'lo-LA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lo_LA;
    +}
    +
    +if (goog.LOCALE == 'lt_LT' || goog.LOCALE == 'lt-LT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lt_LT;
    +}
    +
    +if (goog.LOCALE == 'lu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'lu_CD' || goog.LOCALE == 'lu-CD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'luo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luo_KE' || goog.LOCALE == 'luo-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luy') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'luy_KE' || goog.LOCALE == 'luy-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'lv_LV' || goog.LOCALE == 'lv-LV') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_lv_LV;
    +}
    +
    +if (goog.LOCALE == 'mas') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_KE' || goog.LOCALE == 'mas-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_TZ' || goog.LOCALE == 'mas-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mer') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mer_KE' || goog.LOCALE == 'mer-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mfe') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mfe_MU' || goog.LOCALE == 'mfe-MU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mg_MG' || goog.LOCALE == 'mg-MG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mgh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgh_MZ' || goog.LOCALE == 'mgh-MZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mgo_CM' || goog.LOCALE == 'mgo-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mk_MK' || goog.LOCALE == 'mk-MK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mk_MK;
    +}
    +
    +if (goog.LOCALE == 'ml_IN' || goog.LOCALE == 'ml-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ml_IN;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl' || goog.LOCALE == 'mn-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl_MN' || goog.LOCALE == 'mn-Cyrl-MN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'mr_IN' || goog.LOCALE == 'mr-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mr_IN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn' || goog.LOCALE == 'ms-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_BN' || goog.LOCALE == 'ms-Latn-BN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ms_Latn_BN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_MY' || goog.LOCALE == 'ms-Latn-MY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_SG' || goog.LOCALE == 'ms-Latn-SG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'mt_MT' || goog.LOCALE == 'mt-MT') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mt_MT;
    +}
    +
    +if (goog.LOCALE == 'mua') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'mua_CM' || goog.LOCALE == 'mua-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'my_MM' || goog.LOCALE == 'my-MM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_my_MM;
    +}
    +
    +if (goog.LOCALE == 'naq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'naq_NA' || goog.LOCALE == 'naq-NA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'nb_NO' || goog.LOCALE == 'nb-NO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nb_NO;
    +}
    +
    +if (goog.LOCALE == 'nb_SJ' || goog.LOCALE == 'nb-SJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nb_SJ;
    +}
    +
    +if (goog.LOCALE == 'nd') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'nd_ZW' || goog.LOCALE == 'nd-ZW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'ne_IN' || goog.LOCALE == 'ne-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ne_IN;
    +}
    +
    +if (goog.LOCALE == 'ne_NP' || goog.LOCALE == 'ne-NP') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ne_NP;
    +}
    +
    +if (goog.LOCALE == 'nl_AW' || goog.LOCALE == 'nl-AW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_AW;
    +}
    +
    +if (goog.LOCALE == 'nl_BE' || goog.LOCALE == 'nl-BE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_BE;
    +}
    +
    +if (goog.LOCALE == 'nl_BQ' || goog.LOCALE == 'nl-BQ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_BQ;
    +}
    +
    +if (goog.LOCALE == 'nl_CW' || goog.LOCALE == 'nl-CW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_CW;
    +}
    +
    +if (goog.LOCALE == 'nl_NL' || goog.LOCALE == 'nl-NL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_NL;
    +}
    +
    +if (goog.LOCALE == 'nl_SR' || goog.LOCALE == 'nl-SR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_SR;
    +}
    +
    +if (goog.LOCALE == 'nl_SX' || goog.LOCALE == 'nl-SX') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nl_SX;
    +}
    +
    +if (goog.LOCALE == 'nmg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nmg_CM' || goog.LOCALE == 'nmg-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nn_NO' || goog.LOCALE == 'nn-NO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nnh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nnh_CM' || goog.LOCALE == 'nnh-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nr') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nr_ZA' || goog.LOCALE == 'nr-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nso') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nso_ZA' || goog.LOCALE == 'nso-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nus') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nus_SD' || goog.LOCALE == 'nus-SD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nyn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'nyn_UG' || goog.LOCALE == 'nyn-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'om') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_ET' || goog.LOCALE == 'om-ET') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_KE' || goog.LOCALE == 'om-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'or_IN' || goog.LOCALE == 'or-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_or_IN;
    +}
    +
    +if (goog.LOCALE == 'os') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_GE' || goog.LOCALE == 'os-GE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_RU' || goog.LOCALE == 'os-RU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab' || goog.LOCALE == 'pa-Arab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab_PK' || goog.LOCALE == 'pa-Arab-PK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru' || goog.LOCALE == 'pa-Guru') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru_IN' || goog.LOCALE == 'pa-Guru-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'pl_PL' || goog.LOCALE == 'pl-PL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pl_PL;
    +}
    +
    +if (goog.LOCALE == 'ps') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'ps_AF' || goog.LOCALE == 'ps-AF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'pt_AO' || goog.LOCALE == 'pt-AO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_AO;
    +}
    +
    +if (goog.LOCALE == 'pt_CV' || goog.LOCALE == 'pt-CV') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_CV;
    +}
    +
    +if (goog.LOCALE == 'pt_GW' || goog.LOCALE == 'pt-GW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_GW;
    +}
    +
    +if (goog.LOCALE == 'pt_MO' || goog.LOCALE == 'pt-MO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_MO;
    +}
    +
    +if (goog.LOCALE == 'pt_MZ' || goog.LOCALE == 'pt-MZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_MZ;
    +}
    +
    +if (goog.LOCALE == 'pt_ST' || goog.LOCALE == 'pt-ST') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_ST;
    +}
    +
    +if (goog.LOCALE == 'pt_TL' || goog.LOCALE == 'pt-TL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_pt_TL;
    +}
    +
    +if (goog.LOCALE == 'qu') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_BO' || goog.LOCALE == 'qu-BO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_EC' || goog.LOCALE == 'qu-EC') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_PE' || goog.LOCALE == 'qu-PE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'rm') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rm_CH' || goog.LOCALE == 'rm-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'rn_BI' || goog.LOCALE == 'rn-BI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'ro_MD' || goog.LOCALE == 'ro-MD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ro_MD;
    +}
    +
    +if (goog.LOCALE == 'ro_RO' || goog.LOCALE == 'ro-RO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ro_RO;
    +}
    +
    +if (goog.LOCALE == 'rof') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'rof_TZ' || goog.LOCALE == 'rof-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'ru_BY' || goog.LOCALE == 'ru-BY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_BY;
    +}
    +
    +if (goog.LOCALE == 'ru_KG' || goog.LOCALE == 'ru-KG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_KG;
    +}
    +
    +if (goog.LOCALE == 'ru_KZ' || goog.LOCALE == 'ru-KZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_KZ;
    +}
    +
    +if (goog.LOCALE == 'ru_MD' || goog.LOCALE == 'ru-MD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_MD;
    +}
    +
    +if (goog.LOCALE == 'ru_RU' || goog.LOCALE == 'ru-RU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_RU;
    +}
    +
    +if (goog.LOCALE == 'ru_UA' || goog.LOCALE == 'ru-UA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ru_UA;
    +}
    +
    +if (goog.LOCALE == 'rw') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rw_RW' || goog.LOCALE == 'rw-RW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rwk') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'rwk_TZ' || goog.LOCALE == 'rwk-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'sah') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'sah_RU' || goog.LOCALE == 'sah-RU') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'saq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'saq_KE' || goog.LOCALE == 'saq-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'sbp') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'sbp_TZ' || goog.LOCALE == 'sbp-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'se') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_FI' || goog.LOCALE == 'se-FI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_se_FI;
    +}
    +
    +if (goog.LOCALE == 'se_NO' || goog.LOCALE == 'se-NO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_SE' || goog.LOCALE == 'se-SE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'seh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'seh_MZ' || goog.LOCALE == 'seh-MZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'ses') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'ses_ML' || goog.LOCALE == 'ses-ML') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'sg') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'sg_CF' || goog.LOCALE == 'sg-CF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'shi') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn' || goog.LOCALE == 'shi-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn_MA' || goog.LOCALE == 'shi-Latn-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng' || goog.LOCALE == 'shi-Tfng') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng_MA' || goog.LOCALE == 'shi-Tfng-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'si_LK' || goog.LOCALE == 'si-LK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_si_LK;
    +}
    +
    +if (goog.LOCALE == 'sk_SK' || goog.LOCALE == 'sk-SK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sk_SK;
    +}
    +
    +if (goog.LOCALE == 'sl_SI' || goog.LOCALE == 'sl-SI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sl_SI;
    +}
    +
    +if (goog.LOCALE == 'smn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'smn_FI' || goog.LOCALE == 'smn-FI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'sn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'sn_ZW' || goog.LOCALE == 'sn-ZW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'so') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_DJ' || goog.LOCALE == 'so-DJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_ET' || goog.LOCALE == 'so-ET') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_KE' || goog.LOCALE == 'so-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_SO' || goog.LOCALE == 'so-SO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'sq_AL' || goog.LOCALE == 'sq-AL') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sq_AL;
    +}
    +
    +if (goog.LOCALE == 'sq_MK' || goog.LOCALE == 'sq-MK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sq_MK;
    +}
    +
    +if (goog.LOCALE == 'sq_XK' || goog.LOCALE == 'sq-XK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sq_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl' || goog.LOCALE == 'sr-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_BA' || goog.LOCALE == 'sr-Cyrl-BA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_ME' || goog.LOCALE == 'sr-Cyrl-ME') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_RS' || goog.LOCALE == 'sr-Cyrl-RS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_XK' || goog.LOCALE == 'sr-Cyrl-XK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn' || goog.LOCALE == 'sr-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_BA' || goog.LOCALE == 'sr-Latn-BA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_ME' || goog.LOCALE == 'sr-Latn-ME') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_RS' || goog.LOCALE == 'sr-Latn-RS') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_XK' || goog.LOCALE == 'sr-Latn-XK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'ss') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ss_SZ' || goog.LOCALE == 'ss-SZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ss_ZA' || goog.LOCALE == 'ss-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ssy') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'ssy_ER' || goog.LOCALE == 'ssy-ER') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'sv_AX' || goog.LOCALE == 'sv-AX') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sv_AX;
    +}
    +
    +if (goog.LOCALE == 'sv_FI' || goog.LOCALE == 'sv-FI') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sv_FI;
    +}
    +
    +if (goog.LOCALE == 'sv_SE' || goog.LOCALE == 'sv-SE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sv_SE;
    +}
    +
    +if (goog.LOCALE == 'sw_KE' || goog.LOCALE == 'sw-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sw_KE;
    +}
    +
    +if (goog.LOCALE == 'sw_TZ' || goog.LOCALE == 'sw-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sw_TZ;
    +}
    +
    +if (goog.LOCALE == 'sw_UG' || goog.LOCALE == 'sw-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_sw_UG;
    +}
    +
    +if (goog.LOCALE == 'swc') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'swc_CD' || goog.LOCALE == 'swc-CD') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'ta_IN' || goog.LOCALE == 'ta-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ta_IN;
    +}
    +
    +if (goog.LOCALE == 'ta_LK' || goog.LOCALE == 'ta-LK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ta_LK;
    +}
    +
    +if (goog.LOCALE == 'ta_MY' || goog.LOCALE == 'ta-MY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ta_MY;
    +}
    +
    +if (goog.LOCALE == 'ta_SG' || goog.LOCALE == 'ta-SG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ta_SG;
    +}
    +
    +if (goog.LOCALE == 'te_IN' || goog.LOCALE == 'te-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_te_IN;
    +}
    +
    +if (goog.LOCALE == 'teo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_KE' || goog.LOCALE == 'teo-KE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_UG' || goog.LOCALE == 'teo-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'th_TH' || goog.LOCALE == 'th-TH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_th_TH;
    +}
    +
    +if (goog.LOCALE == 'ti') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'ti_ER' || goog.LOCALE == 'ti-ER') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ti_ER;
    +}
    +
    +if (goog.LOCALE == 'ti_ET' || goog.LOCALE == 'ti-ET') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'tn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'tn_BW' || goog.LOCALE == 'tn-BW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'tn_ZA' || goog.LOCALE == 'tn-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'to') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'to_TO' || goog.LOCALE == 'to-TO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'tr_CY' || goog.LOCALE == 'tr-CY') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tr_CY;
    +}
    +
    +if (goog.LOCALE == 'tr_TR' || goog.LOCALE == 'tr-TR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tr_TR;
    +}
    +
    +if (goog.LOCALE == 'ts') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'ts_ZA' || goog.LOCALE == 'ts-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'twq') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'twq_NE' || goog.LOCALE == 'twq-NE') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'tzm') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn' || goog.LOCALE == 'tzm-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn_MA' || goog.LOCALE == 'tzm-Latn-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'ug') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab' || goog.LOCALE == 'ug-Arab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab_CN' || goog.LOCALE == 'ug-Arab-CN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'uk_UA' || goog.LOCALE == 'uk-UA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uk_UA;
    +}
    +
    +if (goog.LOCALE == 'ur_IN' || goog.LOCALE == 'ur-IN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ur_IN;
    +}
    +
    +if (goog.LOCALE == 'ur_PK' || goog.LOCALE == 'ur-PK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ur_PK;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab' || goog.LOCALE == 'uz-Arab') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab_AF' || goog.LOCALE == 'uz-Arab-AF') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl' || goog.LOCALE == 'uz-Cyrl') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl_UZ' || goog.LOCALE == 'uz-Cyrl-UZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn' || goog.LOCALE == 'uz-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn_UZ' || goog.LOCALE == 'uz-Latn-UZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn' || goog.LOCALE == 'vai-Latn') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn_LR' || goog.LOCALE == 'vai-Latn-LR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii' || goog.LOCALE == 'vai-Vaii') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii_LR' || goog.LOCALE == 'vai-Vaii-LR') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 've') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 've_ZA' || goog.LOCALE == 've-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 'vi_VN' || goog.LOCALE == 'vi-VN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vi_VN;
    +}
    +
    +if (goog.LOCALE == 'vo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vo_001' || goog.LOCALE == 'vo-001') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vun') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'vun_TZ' || goog.LOCALE == 'vun-TZ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'wae') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'wae_CH' || goog.LOCALE == 'wae-CH') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'xog') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'xog_UG' || goog.LOCALE == 'xog-UG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'yav') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yav_CM' || goog.LOCALE == 'yav-CM') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yi') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yi_001' || goog.LOCALE == 'yi-001') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yo') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'yo_BJ' || goog.LOCALE == 'yo-BJ') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yo_BJ;
    +}
    +
    +if (goog.LOCALE == 'yo_NG' || goog.LOCALE == 'yo-NG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'zgh') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zgh_MA' || goog.LOCALE == 'zgh-MA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans' || goog.LOCALE == 'zh-Hans') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_CN' || goog.LOCALE == 'zh-Hans-CN') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_HK' || goog.LOCALE == 'zh-Hans-HK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hans_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_MO' || goog.LOCALE == 'zh-Hans-MO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hans_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_SG' || goog.LOCALE == 'zh-Hans-SG') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hans_SG;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant' || goog.LOCALE == 'zh-Hant') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_HK' || goog.LOCALE == 'zh-Hant-HK') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hant_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_MO' || goog.LOCALE == 'zh-Hant-MO') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hant_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_TW' || goog.LOCALE == 'zh-Hant-TW') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zu_ZA' || goog.LOCALE == 'zu-ZA') {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zu_ZA;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak.js b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak.js
    new file mode 100644
    index 0000000000..c5eb9ef456
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak.js
    @@ -0,0 +1,214 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detect Grapheme Cluster Break in a pair of codepoints. Follows
    + * Unicode 5.1 UAX#29. Tailoring for Virama × Indic Consonants is used.
    + *
    + */
    +
    +goog.provide('goog.i18n.GraphemeBreak');
    +goog.require('goog.structs.InversionMap');
    +
    +
    +/**
    + * Enum for all Grapheme Cluster Break properties.
    + * These enums directly corresponds to Grapheme_Cluster_Break property values
    + * mentioned in http://unicode.org/reports/tr29 table 2. VIRAMA and
    + * INDIC_CONSONANT are for the Virama × Base tailoring mentioned in the notes.
    + *
    + * CR and LF are moved to the bottom of the list because they occur only once
    + * and so good candidates to take 2 decimal digit values.
    + * @enum {number}
    + * @protected
    + */
    +goog.i18n.GraphemeBreak.property = {
    +  ANY: 0,
    +  CONTROL: 1,
    +  EXTEND: 2,
    +  PREPEND: 3,
    +  SPACING_MARK: 4,
    +  INDIC_CONSONANT: 5,
    +  VIRAMA: 6,
    +  L: 7,
    +  V: 8,
    +  T: 9,
    +  LV: 10,
    +  LVT: 11,
    +  CR: 12,
    +  LF: 13,
    +  REGIONAL_INDICATOR: 14
    +};
    +
    +
    +/**
    + * Grapheme Cluster Break property values for all codepoints as inversion map.
    + * Constructed lazily.
    + *
    + * @type {goog.structs.InversionMap}
    + * @private
    + */
    +goog.i18n.GraphemeBreak.inversions_ = null;
    +
    +
    +/**
    + * There are two kinds of grapheme clusters: 1) Legacy 2)Extended. This method
    + * is to check for legacy rules.
    + *
    + * @param {number} prop_a The property enum value of the first character.
    + * @param {number} prop_b The property enum value of the second character.
    + * @return {boolean} True if a & b do not form a cluster; False otherwise.
    + * @private
    + */
    +goog.i18n.GraphemeBreak.applyLegacyBreakRules_ = function(prop_a, prop_b) {
    +
    +  var prop = goog.i18n.GraphemeBreak.property;
    +
    +  if (prop_a == prop.CR && prop_b == prop.LF) {
    +    return false;
    +  }
    +  if (prop_a == prop.CONTROL || prop_a == prop.CR || prop_a == prop.LF) {
    +    return true;
    +  }
    +  if (prop_b == prop.CONTROL || prop_b == prop.CR || prop_b == prop.LF) {
    +    return true;
    +  }
    +  if ((prop_a == prop.L) &&
    +      (prop_b == prop.L || prop_b == prop.V ||
    +      prop_b == prop.LV || prop_b == prop.LVT)) {
    +    return false;
    +  }
    +  if ((prop_a == prop.LV || prop_a == prop.V) &&
    +      (prop_b == prop.V || prop_b == prop.T)) {
    +    return false;
    +  }
    +  if ((prop_a == prop.LVT || prop_a == prop.T) && (prop_b == prop.T)) {
    +    return false;
    +  }
    +  if (prop_b == prop.EXTEND || prop_b == prop.VIRAMA) {
    +    return false;
    +  }
    +  if (prop_a == prop.VIRAMA && prop_b == prop.INDIC_CONSONANT) {
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Method to return property enum value of the codepoint. If it is Hangul LV or
    + * LVT, then it is computed; for the rest it is picked from the inversion map.
    + * @param {number} acode The code point value of the character.
    + * @return {number} Property enum value of codepoint.
    + * @private
    + */
    +goog.i18n.GraphemeBreak.getBreakProp_ = function(acode) {
    +  if (0xAC00 <= acode && acode <= 0xD7A3) {
    +    var prop = goog.i18n.GraphemeBreak.property;
    +    if (acode % 0x1C == 0x10) {
    +      return prop.LV;
    +    }
    +    return prop.LVT;
    +  } else {
    +    if (!goog.i18n.GraphemeBreak.inversions_) {
    +      goog.i18n.GraphemeBreak.inversions_ = new goog.structs.InversionMap(
    +          [0, 10, 1, 2, 1, 18, 95, 33, 13, 1, 594, 112, 275, 7, 263, 45, 1, 1,
    +           1, 2, 1, 2, 1, 1, 56, 5, 11, 11, 48, 21, 16, 1, 101, 7, 1, 1, 6, 2,
    +           2, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 9, 34, 4, 1, 9, 1, 3, 1,
    +           5, 43, 3, 136, 31, 1, 17, 37, 1, 1, 1, 1, 3, 8, 4, 1, 2, 1, 7, 8, 2,
    +           2, 21, 8, 1, 2, 17, 39, 1, 1, 1, 2, 6, 6, 1, 9, 5, 4, 2, 2, 12, 2,
    +           15, 2, 1, 17, 39, 2, 3, 12, 4, 8, 6, 17, 2, 3, 14, 1, 17, 39, 1, 1,
    +           3, 8, 4, 1, 20, 2, 29, 1, 2, 17, 39, 1, 1, 2, 1, 6, 6, 9, 6, 4, 2,
    +           2, 13, 1, 16, 1, 18, 41, 1, 1, 1, 12, 1, 9, 1, 41, 3, 17, 37, 4, 3,
    +           5, 7, 8, 3, 2, 8, 2, 30, 2, 17, 39, 1, 1, 1, 1, 2, 1, 3, 1, 5, 1, 8,
    +           9, 1, 3, 2, 30, 2, 17, 38, 3, 1, 2, 5, 7, 1, 9, 1, 10, 2, 30, 2, 22,
    +           48, 5, 1, 2, 6, 7, 19, 2, 13, 46, 2, 1, 1, 1, 6, 1, 12, 8, 50, 46,
    +           2, 1, 1, 1, 9, 11, 6, 14, 2, 58, 2, 27, 1, 1, 1, 1, 1, 4, 2, 49, 14,
    +           1, 4, 1, 1, 2, 5, 48, 9, 1, 57, 33, 12, 4, 1, 6, 1, 2, 2, 2, 1, 16,
    +           2, 4, 2, 2, 4, 3, 1, 3, 2, 7, 3, 4, 13, 1, 1, 1, 2, 6, 1, 1, 14, 1,
    +           98, 96, 72, 88, 349, 3, 931, 15, 2, 1, 14, 15, 2, 1, 14, 15, 2, 15,
    +           15, 14, 35, 17, 2, 1, 7, 8, 1, 2, 9, 1, 1, 9, 1, 45, 3, 155, 1, 87,
    +           31, 3, 4, 2, 9, 1, 6, 3, 20, 19, 29, 44, 9, 3, 2, 1, 69, 23, 2, 3,
    +           4, 45, 6, 2, 1, 1, 1, 8, 1, 1, 1, 2, 8, 6, 13, 128, 4, 1, 14, 33, 1,
    +           1, 5, 1, 1, 5, 1, 1, 1, 7, 31, 9, 12, 2, 1, 7, 23, 1, 4, 2, 2, 2, 2,
    +           2, 11, 3, 2, 36, 2, 1, 1, 2, 3, 1, 1, 3, 2, 12, 36, 8, 8, 2, 2, 21,
    +           3, 128, 3, 1, 13, 1, 7, 4, 1, 4, 2, 1, 203, 64, 523, 1, 2, 2, 24, 7,
    +           49, 16, 96, 33, 3070, 3, 141, 1, 96, 32, 554, 6, 105, 2, 30164, 4,
    +           1, 10, 33, 1, 80, 2, 272, 1, 3, 1, 4, 1, 23, 2, 2, 1, 24, 30, 4, 4,
    +           3, 8, 1, 1, 13, 2, 16, 34, 16, 1, 27, 18, 24, 24, 4, 8, 2, 23, 11,
    +           1, 1, 12, 32, 3, 1, 5, 3, 3, 36, 1, 2, 4, 2, 1, 3, 1, 69, 35, 6, 2,
    +           2, 2, 2, 12, 1, 8, 1, 1, 18, 16, 1, 3, 6, 1, 5, 48, 1, 1, 3, 2, 2,
    +           5, 2, 1, 1, 32, 9, 1, 2, 2, 5, 1, 1, 201, 14, 2, 1, 1, 9, 8, 2, 1,
    +           2, 1, 2, 1, 1, 1, 18, 11184, 27, 49, 1028, 1024, 6942, 1, 737, 16,
    +           16, 7, 216, 1, 158, 2, 89, 3, 513, 1, 2051, 15, 40, 7, 1, 1472, 1,
    +           1, 1, 53, 14, 1, 57, 2, 1, 45, 3, 4, 2, 1, 1, 2, 1, 66, 3, 36, 5, 1,
    +           6, 2, 75, 2, 1, 48, 3, 9, 1, 1, 1258, 1, 1, 1, 2, 6, 1, 1, 22681,
    +           62, 4, 25042, 1, 1, 3, 3, 1, 5, 8, 8, 2, 7, 30, 4, 148, 3, 8097, 26,
    +           790017, 255],
    +          [1, 13, 1, 12, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
    +           2, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0,
    +           2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 4, 0, 5, 2, 4, 2,
    +           0, 4, 2, 4, 6, 4, 0, 2, 5, 0, 2, 0, 5, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4,
    +           6, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0, 5, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2,
    +           4, 0, 5, 2, 0, 4, 2, 4, 6, 0, 2, 0, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4, 6,
    +           2, 5, 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 2, 4, 6, 0, 2, 0, 4, 0, 5, 0, 2,
    +           4, 2, 6, 2, 5, 0, 2, 0, 4, 0, 5, 2, 0, 4, 2, 4, 2, 4, 2, 4, 2, 6, 2,
    +           5, 0, 2, 0, 4, 0, 5, 0, 2, 4, 2, 4, 6, 0, 2, 0, 2, 0, 4, 0, 5, 6, 2,
    +           4, 2, 4, 2, 4, 0, 5, 0, 2, 0, 4, 2, 6, 0, 2, 0, 5, 0, 2, 0, 4, 2, 0,
    +           2, 0, 5, 0, 2, 0, 2, 0, 2, 0, 2, 0, 4, 5, 2, 4, 2, 6, 0, 2, 0, 2, 0,
    +           2, 0, 5, 0, 2, 4, 2, 0, 6, 4, 2, 5, 0, 5, 0, 4, 2, 5, 2, 5, 0, 5, 0,
    +           5, 2, 5, 2, 0, 4, 2, 0, 2, 5, 0, 2, 0, 7, 8, 9, 0, 2, 0, 5, 2, 6, 0,
    +           5, 2, 6, 0, 5, 2, 0, 5, 2, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 2, 0, 2, 0,
    +           2, 0, 2, 0, 5, 2, 4, 2, 4, 2, 4, 2, 0, 5, 0, 5, 0, 4, 0, 4, 0, 5, 2,
    +           4, 0, 5, 0, 5, 4, 2, 4, 2, 6, 0, 2, 0, 2, 4, 2, 0, 2, 4, 0, 5, 2, 4,
    +           2, 4, 2, 4, 2, 4, 6, 5, 0, 2, 0, 2, 4, 0, 5, 4, 2, 4, 2, 6, 4, 5, 0,
    +           5, 0, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 0, 5, 4, 2, 4, 2, 0, 5, 0, 2, 0,
    +           2, 4, 2, 0, 2, 0, 4, 2, 0, 2, 0, 1, 2, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0,
    +           6, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 6, 5, 2, 5, 4,
    +           2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 0, 4, 0, 5, 4, 6, 0, 2, 0, 5, 0, 2, 0,
    +           5, 2, 4, 6, 0, 7, 2, 4, 0, 5, 0, 5, 2, 4, 2, 4, 2, 4, 6, 0, 5, 2, 4,
    +           2, 4, 2, 0, 2, 0, 2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 2, 0, 2, 0, 2, 0, 2,
    +           0, 2, 0, 5, 4, 2, 4, 0, 4, 6, 0, 5, 0, 5, 0, 5, 0, 4, 2, 4, 2, 4, 0,
    +           4, 6, 0, 11, 8, 9, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2,
    +           0, 2, 0, 2, 6, 0, 4, 2, 4, 0, 2, 6, 0, 2, 4, 0, 4, 2, 4, 6, 2, 0, 1,
    +           0, 2, 0, 2, 4, 2, 6, 0, 2, 4, 0, 4, 2, 4, 6, 0, 2, 4, 2, 4, 2, 6, 2,
    +           0, 4, 2, 0, 2, 4, 2, 0, 4, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 14, 0, 1,
    +           2],
    +          true);
    +    }
    +    return /** @type {number} */ (
    +        goog.i18n.GraphemeBreak.inversions_.at(acode));
    +  }
    +};
    +
    +
    +/**
    + * There are two kinds of grapheme clusters: 1) Legacy 2)Extended. This method
    + * is to check for both using a boolean flag to switch between them.
    + * @param {number} a The code point value of the first character.
    + * @param {number} b The code point value of the second character.
    + * @param {boolean=} opt_extended If true, indicates extended grapheme cluster;
    + *     If false, indicates legacy cluster.
    + * @return {boolean} True if a & b do not form a cluster; False otherwise.
    + */
    +goog.i18n.GraphemeBreak.hasGraphemeBreak = function(a, b, opt_extended) {
    +
    +  var prop_a = goog.i18n.GraphemeBreak.getBreakProp_(a);
    +  var prop_b = goog.i18n.GraphemeBreak.getBreakProp_(b);
    +  var prop = goog.i18n.GraphemeBreak.property;
    +
    +  return goog.i18n.GraphemeBreak.applyLegacyBreakRules_(prop_a, prop_b) &&
    +      !(opt_extended &&
    +          (prop_a == prop.PREPEND || prop_b == prop.SPACING_MARK));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.html b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.html
    new file mode 100644
    index 0000000000..c40c1f53c6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.GraphemeBreak
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.i18n.GraphemeBreakTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.js b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.js
    new file mode 100644
    index 0000000000..2cf92307c9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/graphemebreak_test.js
    @@ -0,0 +1,78 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.GraphemeBreakTest');
    +goog.setTestOnly('goog.i18n.GraphemeBreakTest');
    +
    +goog.require('goog.i18n.GraphemeBreak');
    +goog.require('goog.testing.jsunit');
    +
    +function testBreakNormalAscii() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak('a'.charCodeAt(0),
    +      'b'.charCodeAt(0), true));
    +}
    +
    +function testBreakAsciiWithExtendedChar() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak('a'.charCodeAt(0),
    +      0x0300, true));
    +}
    +
    +function testBreakSurrogates() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0xDA00, 0xDC00, true));
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0xDBFF, 0xDFFF, true));
    +}
    +
    +function testBreakHangLxL() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x1100, 0x1100, true));
    +}
    +
    +function testBreakHangL_T() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x1100, 0x11A8));
    +}
    +
    +function testBreakHangLVxV() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0xAC00, 0x1160, true));
    +}
    +
    +function testBreakHangLV_L() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0xAC00, 0x1100, true));
    +}
    +
    +function testBreakHangLVTxT() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0xAC01, 0x11A8, true));
    +}
    +
    +function testBreakThaiPrependLegacy() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x0E40, 0x0E01));
    +}
    +
    +function testBreakThaiPrependExtended() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x0E40, 0x0E01, true));
    +}
    +
    +function testBreakDevaSpacingMarkLegacy() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x0915, 0x093E));
    +}
    +
    +function testBreakDevaSpacingMarkExtended() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x0915, 0x093E, true));
    +}
    +
    +function testBreakDevaViramaSpace() {
    +  assertTrue(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x094D, 0x0020));
    +}
    +
    +function testBreakDevaViramaConsonant() {
    +  assertFalse(goog.i18n.GraphemeBreak.hasGraphemeBreak(0x094D, 0x0915));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/messageformat.js b/mapbox-gl/closure-library/closure/goog/i18n/messageformat.js
    new file mode 100644
    index 0000000000..8f88370ba0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/messageformat.js
    @@ -0,0 +1,780 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Message/plural format library with locale support.
    + *
    + * Message format grammar:
    + *
    + * messageFormatPattern := string ( "{" messageFormatElement "}" string )*
    + * messageFormatElement := argumentIndex [ "," elementFormat ]
    + * elementFormat := "plural" "," pluralStyle
    + *                  | "selectordinal" "," ordinalStyle
    + *                  | "select" "," selectStyle
    + * pluralStyle :=  pluralFormatPattern
    + * ordinalStyle :=  selectFormatPattern
    + * selectStyle :=  selectFormatPattern
    + * pluralFormatPattern := [ "offset" ":" offsetIndex ] pluralForms*
    + * selectFormatPattern := pluralForms*
    + * pluralForms := stringKey "{" ( "{" messageFormatElement "}"|string )* "}"
    + *
    + * This is a subset of the ICU MessageFormatSyntax:
    + *   http://userguide.icu-project.org/formatparse/messages
    + * See also http://go/plurals and http://go/ordinals for internal details.
    + *
    + *
    + * Message example:
    + *
    + * I see {NUM_PEOPLE, plural, offset:1
    + *         =0 {no one at all}
    + *         =1 {{WHO}}
    + *         one {{WHO} and one other person}
    + *         other {{WHO} and # other people}}
    + * in {PLACE}.
    + *
    + * Calling format({'NUM_PEOPLE': 2, 'WHO': 'Mark', 'PLACE': 'Athens'}) would
    + * produce "I see Mark and one other person in Athens." as output.
    + *
    + * OR:
    + *
    + * {NUM_FLOOR, selectordinal,
    + *   one {Take the elevator to the #st floor.}
    + *   two {Take the elevator to the #nd floor.}
    + *   few {Take the elevator to the #rd floor.}
    + *   other {Take the elevator to the #th floor.}}
    + *
    + * Calling format({'NUM_FLOOR': 22}) would produce
    + * "Take the elevator to the 22nd floor".
    + *
    + * See messageformat_test.html for more examples.
    + */
    +
    +goog.provide('goog.i18n.MessageFormat');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.i18n.NumberFormat');
    +goog.require('goog.i18n.ordinalRules');
    +goog.require('goog.i18n.pluralRules');
    +
    +
    +
    +/**
    + * Constructor of MessageFormat.
    + * @param {string} pattern The pattern we parse and apply positional parameters
    + *     to.
    + * @constructor
    + * @final
    + */
    +goog.i18n.MessageFormat = function(pattern) {
    +  /**
    +   * All encountered literals during parse stage. Indices tell us the order of
    +   * replacement.
    +   * @type {!Array<string>}
    +   * @private
    +   */
    +  this.literals_ = [];
    +
    +  /**
    +   * Input pattern gets parsed into objects for faster formatting.
    +   * @type {!Array<!Object>}
    +   * @private
    +   */
    +  this.parsedPattern_ = [];
    +
    +  /**
    +   * Locale aware number formatter.
    +   * @type {goog.i18n.NumberFormat}
    +   * @private
    +   */
    +  this.numberFormatter_ = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.DECIMAL);
    +
    +  this.parsePattern_(pattern);
    +};
    +
    +
    +/**
    + * Literal strings, including '', are replaced with \uFDDF_x_ for
    + * parsing purposes, and recovered during format phase.
    + * \uFDDF is a Unicode nonprinting character, not expected to be found in the
    + * typical message.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ = '\uFDDF_';
    +
    +
    +/**
    + * Marks a string and block during parsing.
    + * @enum {number}
    + * @private
    + */
    +goog.i18n.MessageFormat.Element_ = {
    +  STRING: 0,
    +  BLOCK: 1
    +};
    +
    +
    +/**
    + * Block type.
    + * @enum {number}
    + * @private
    + */
    +goog.i18n.MessageFormat.BlockType_ = {
    +  PLURAL: 0,
    +  ORDINAL: 1,
    +  SELECT: 2,
    +  SIMPLE: 3,
    +  STRING: 4,
    +  UNKNOWN: 5
    +};
    +
    +
    +/**
    + * Mandatory option in both select and plural form.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.MessageFormat.OTHER_ = 'other';
    +
    +
    +/**
    + * Regular expression for looking for string literals.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.MessageFormat.REGEX_LITERAL_ = new RegExp("'([{}#].*?)'", 'g');
    +
    +
    +/**
    + * Regular expression for looking for '' in the message.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_ = new RegExp("''", 'g');
    +
    +
    +/**
    + * Formats a message, treating '#' with special meaning representing
    + * the number (plural_variable - offset).
    + * @param {!Object} namedParameters Parameters that either
    + *     influence the formatting or are used as actual data.
    + *     I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
    + *     object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
    + *     1st parameter could mean 5 people, which could influence plural format,
    + *     and 2nd parameter is just a data to be printed out in proper position.
    + * @return {string} Formatted message.
    + */
    +goog.i18n.MessageFormat.prototype.format = function(namedParameters) {
    +  return this.format_(namedParameters, false);
    +};
    +
    +
    +/**
    + * Formats a message, treating '#' as literary character.
    + * @param {!Object} namedParameters Parameters that either
    + *     influence the formatting or are used as actual data.
    + *     I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
    + *     object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
    + *     1st parameter could mean 5 people, which could influence plural format,
    + *     and 2nd parameter is just a data to be printed out in proper position.
    + * @return {string} Formatted message.
    + */
    +goog.i18n.MessageFormat.prototype.formatIgnoringPound =
    +    function(namedParameters) {
    +  return this.format_(namedParameters, true);
    +};
    +
    +
    +/**
    + * Formats a message.
    + * @param {!Object} namedParameters Parameters that either
    + *     influence the formatting or are used as actual data.
    + *     I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
    + *     object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
    + *     1st parameter could mean 5 people, which could influence plural format,
    + *     and 2nd parameter is just a data to be printed out in proper position.
    + * @param {boolean} ignorePound If true, treat '#' in plural messages as a
    + *     literary character, else treat it as an ICU syntax character, resolving
    + *     to the number (plural_variable - offset).
    + * @return {string} Formatted message.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.format_ =
    +    function(namedParameters, ignorePound) {
    +  if (this.parsedPattern_.length == 0) {
    +    return '';
    +  }
    +
    +  var result = [];
    +  this.formatBlock_(this.parsedPattern_, namedParameters, ignorePound, result);
    +  var message = result.join('');
    +
    +  if (!ignorePound) {
    +    goog.asserts.assert(message.search('#') == -1, 'Not all # were replaced.');
    +  }
    +
    +  while (this.literals_.length > 0) {
    +    message = message.replace(this.buildPlaceholder_(this.literals_),
    +                              this.literals_.pop());
    +  }
    +
    +  return message;
    +};
    +
    +
    +/**
    + * Parses generic block and returns a formatted string.
    + * @param {!Array<!Object>} parsedPattern Holds parsed tree.
    + * @param {!Object} namedParameters Parameters that either influence
    + *     the formatting or are used as actual data.
    + * @param {boolean} ignorePound If true, treat '#' in plural messages as a
    + *     literary character, else treat it as an ICU syntax character, resolving
    + *     to the number (plural_variable - offset).
    + * @param {!Array<!string>} result Each formatting stage appends its product
    + *     to the result.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.formatBlock_ = function(
    +    parsedPattern, namedParameters, ignorePound, result) {
    +  for (var i = 0; i < parsedPattern.length; i++) {
    +    switch (parsedPattern[i].type) {
    +      case goog.i18n.MessageFormat.BlockType_.STRING:
    +        result.push(parsedPattern[i].value);
    +        break;
    +      case goog.i18n.MessageFormat.BlockType_.SIMPLE:
    +        var pattern = parsedPattern[i].value;
    +        this.formatSimplePlaceholder_(pattern, namedParameters, result);
    +        break;
    +      case goog.i18n.MessageFormat.BlockType_.SELECT:
    +        var pattern = parsedPattern[i].value;
    +        this.formatSelectBlock_(pattern, namedParameters, ignorePound, result);
    +        break;
    +      case goog.i18n.MessageFormat.BlockType_.PLURAL:
    +        var pattern = parsedPattern[i].value;
    +        this.formatPluralOrdinalBlock_(pattern,
    +                                       namedParameters,
    +                                       goog.i18n.pluralRules.select,
    +                                       ignorePound,
    +                                       result);
    +        break;
    +      case goog.i18n.MessageFormat.BlockType_.ORDINAL:
    +        var pattern = parsedPattern[i].value;
    +        this.formatPluralOrdinalBlock_(pattern,
    +                                       namedParameters,
    +                                       goog.i18n.ordinalRules.select,
    +                                       ignorePound,
    +                                       result);
    +        break;
    +      default:
    +        goog.asserts.fail('Unrecognized block type: ' + parsedPattern[i].type);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Formats simple placeholder.
    + * @param {!Object} parsedPattern JSON object containing placeholder info.
    + * @param {!Object} namedParameters Parameters that are used as actual data.
    + * @param {!Array<!string>} result Each formatting stage appends its product
    + *     to the result.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.formatSimplePlaceholder_ = function(
    +    parsedPattern, namedParameters, result) {
    +  var value = namedParameters[parsedPattern];
    +  if (!goog.isDef(value)) {
    +    result.push('Undefined parameter - ' + parsedPattern);
    +    return;
    +  }
    +
    +  // Don't push the value yet, it may contain any of # { } in it which
    +  // will break formatter. Insert a placeholder and replace at the end.
    +  this.literals_.push(value);
    +  result.push(this.buildPlaceholder_(this.literals_));
    +};
    +
    +
    +/**
    + * Formats select block. Only one option is selected.
    + * @param {!Object} parsedPattern JSON object containing select block info.
    + * @param {!Object} namedParameters Parameters that either influence
    + *     the formatting or are used as actual data.
    + * @param {boolean} ignorePound If true, treat '#' in plural messages as a
    + *     literary character, else treat it as an ICU syntax character, resolving
    + *     to the number (plural_variable - offset).
    + * @param {!Array<!string>} result Each formatting stage appends its product
    + *     to the result.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.formatSelectBlock_ = function(
    +    parsedPattern, namedParameters, ignorePound, result) {
    +  var argumentIndex = parsedPattern.argumentIndex;
    +  if (!goog.isDef(namedParameters[argumentIndex])) {
    +    result.push('Undefined parameter - ' + argumentIndex);
    +    return;
    +  }
    +
    +  var option = parsedPattern[namedParameters[argumentIndex]];
    +  if (!goog.isDef(option)) {
    +    option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
    +    goog.asserts.assertArray(
    +        option, 'Invalid option or missing other option for select block.');
    +  }
    +
    +  this.formatBlock_(option, namedParameters, ignorePound, result);
    +};
    +
    +
    +/**
    + * Formats plural or selectordinal block. Only one option is selected and all #
    + * are replaced.
    + * @param {!Object} parsedPattern JSON object containing plural block info.
    + * @param {!Object} namedParameters Parameters that either influence
    + *     the formatting or are used as actual data.
    + * @param {!function(number, number=):string} pluralSelector  A select function
    + *     from goog.i18n.pluralRules or goog.i18n.ordinalRules which determines
    + *     which plural/ordinal form to use based on the input number's cardinality.
    + * @param {boolean} ignorePound If true, treat '#' in plural messages as a
    + *     literary character, else treat it as an ICU syntax character, resolving
    + *     to the number (plural_variable - offset).
    + * @param {!Array<!string>} result Each formatting stage appends its product
    + *     to the result.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.formatPluralOrdinalBlock_ = function(
    +    parsedPattern, namedParameters, pluralSelector, ignorePound, result) {
    +  var argumentIndex = parsedPattern.argumentIndex;
    +  var argumentOffset = parsedPattern.argumentOffset;
    +  var pluralValue = +namedParameters[argumentIndex];
    +  if (isNaN(pluralValue)) {
    +    // TODO(user): Distinguish between undefined and invalid parameters.
    +    result.push('Undefined or invalid parameter - ' + argumentIndex);
    +    return;
    +  }
    +  var diff = pluralValue - argumentOffset;
    +
    +  // Check if there is an exact match.
    +  var option = parsedPattern[namedParameters[argumentIndex]];
    +  if (!goog.isDef(option)) {
    +    goog.asserts.assert(diff >= 0, 'Argument index smaller than offset.');
    +    var item;
    +    if (this.numberFormatter_.getMinimumFractionDigits) { // number formatter?
    +      // If we know the number of fractional digits we can make better decisions
    +      // We can decide (for instance) between "1 dollar" and "1.00 dollars".
    +      item = pluralSelector(diff,
    +          this.numberFormatter_.getMinimumFractionDigits());
    +    } else {
    +      item = pluralSelector(diff);
    +    }
    +    goog.asserts.assertString(item, 'Invalid plural key.');
    +
    +    option = parsedPattern[item];
    +
    +    // If option is not provided fall back to "other".
    +    if (!goog.isDef(option)) {
    +      option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
    +    }
    +
    +    goog.asserts.assertArray(
    +        option, 'Invalid option or missing other option for plural block.');
    +  }
    +
    +  var pluralResult = [];
    +  this.formatBlock_(option, namedParameters, ignorePound, pluralResult);
    +  var plural = pluralResult.join('');
    +  goog.asserts.assertString(plural, 'Empty block in plural.');
    +  if (ignorePound) {
    +    result.push(plural);
    +  } else {
    +    var localeAwareDiff = this.numberFormatter_.format(diff);
    +    result.push(plural.replace(/#/g, localeAwareDiff));
    +  }
    +};
    +
    +
    +/**
    + * Parses input pattern into an array, for faster reformatting with
    + * different input parameters.
    + * Parsing is locale independent.
    + * @param {string} pattern MessageFormat pattern to parse.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parsePattern_ = function(pattern) {
    +  if (pattern) {
    +    pattern = this.insertPlaceholders_(pattern);
    +
    +    this.parsedPattern_ = this.parseBlock_(pattern);
    +  }
    +};
    +
    +
    +/**
    + * Replaces string literals with literal placeholders.
    + * Literals are string of the form '}...', '{...' and '#...' where ... is
    + * set of characters not containing '
    + * Builds a dictionary so we can recover literals during format phase.
    + * @param {string} pattern Pattern to clean up.
    + * @return {string} Pattern with literals replaced with placeholders.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.insertPlaceholders_ = function(pattern) {
    +  var literals = this.literals_;
    +  var buildPlaceholder = goog.bind(this.buildPlaceholder_, this);
    +
    +  // First replace '' with single quote placeholder since they can be found
    +  // inside other literals.
    +  pattern = pattern.replace(
    +      goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_,
    +      function() {
    +        literals.push("'");
    +        return buildPlaceholder(literals);
    +      });
    +
    +  pattern = pattern.replace(
    +      goog.i18n.MessageFormat.REGEX_LITERAL_,
    +      function(match, text) {
    +        literals.push(text);
    +        return buildPlaceholder(literals);
    +      });
    +
    +  return pattern;
    +};
    +
    +
    +/**
    + * Breaks pattern into strings and top level {...} blocks.
    + * @param {string} pattern (sub)Pattern to be broken.
    + * @return {!Array<Object>} Each item is {type, value}.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.extractParts_ = function(pattern) {
    +  var prevPos = 0;
    +  var inBlock = false;
    +  var braceStack = [];
    +  var results = [];
    +
    +  var braces = /[{}]/g;
    +  braces.lastIndex = 0;  // lastIndex doesn't get set to 0 so we have to.
    +  var match;
    +
    +  while (match = braces.exec(pattern)) {
    +    var pos = match.index;
    +    if (match[0] == '}') {
    +      var brace = braceStack.pop();
    +      goog.asserts.assert(goog.isDef(brace) && brace == '{',
    +                          'No matching { for }.');
    +
    +      if (braceStack.length == 0) {
    +        // End of the block.
    +        var part = {};
    +        part.type = goog.i18n.MessageFormat.Element_.BLOCK;
    +        part.value = pattern.substring(prevPos, pos);
    +        results.push(part);
    +        prevPos = pos + 1;
    +        inBlock = false;
    +      }
    +    } else {
    +      if (braceStack.length == 0) {
    +        inBlock = true;
    +        var substring = pattern.substring(prevPos, pos);
    +        if (substring != '') {
    +          results.push({
    +            type: goog.i18n.MessageFormat.Element_.STRING,
    +            value: substring
    +          });
    +        }
    +        prevPos = pos + 1;
    +      }
    +      braceStack.push('{');
    +    }
    +  }
    +
    +  // Take care of the final string, and check if the braceStack is empty.
    +  goog.asserts.assert(braceStack.length == 0,
    +                      'There are mismatched { or } in the pattern.');
    +
    +  var substring = pattern.substring(prevPos);
    +  if (substring != '') {
    +    results.push({
    +      type: goog.i18n.MessageFormat.Element_.STRING,
    +      value: substring
    +    });
    +  }
    +
    +  return results;
    +};
    +
    +
    +/**
    + * A regular expression to parse the plural block, extracting the argument
    + * index and offset (if any).
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.MessageFormat.PLURAL_BLOCK_RE_ =
    +    /^\s*(\w+)\s*,\s*plural\s*,(?:\s*offset:(\d+))?/;
    +
    +
    +/**
    + * A regular expression to parse the ordinal block, extracting the argument
    + * index.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_ = /^\s*(\w+)\s*,\s*selectordinal\s*,/;
    +
    +
    +/**
    + * A regular expression to parse the select block, extracting the argument
    + * index.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.MessageFormat.SELECT_BLOCK_RE_ = /^\s*(\w+)\s*,\s*select\s*,/;
    +
    +
    +/**
    + * Detects which type of a block is the pattern.
    + * @param {string} pattern Content of the block.
    + * @return {goog.i18n.MessageFormat.BlockType_} One of the block types.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parseBlockType_ = function(pattern) {
    +  if (goog.i18n.MessageFormat.PLURAL_BLOCK_RE_.test(pattern)) {
    +    return goog.i18n.MessageFormat.BlockType_.PLURAL;
    +  }
    +
    +  if (goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_.test(pattern)) {
    +    return goog.i18n.MessageFormat.BlockType_.ORDINAL;
    +  }
    +
    +  if (goog.i18n.MessageFormat.SELECT_BLOCK_RE_.test(pattern)) {
    +    return goog.i18n.MessageFormat.BlockType_.SELECT;
    +  }
    +
    +  if (/^\s*\w+\s*/.test(pattern)) {
    +    return goog.i18n.MessageFormat.BlockType_.SIMPLE;
    +  }
    +
    +  return goog.i18n.MessageFormat.BlockType_.UNKNOWN;
    +};
    +
    +
    +/**
    + * Parses generic block.
    + * @param {string} pattern Content of the block to parse.
    + * @return {!Array<!Object>} Subblocks marked as strings, select...
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parseBlock_ = function(pattern) {
    +  var result = [];
    +  var parts = this.extractParts_(pattern);
    +  for (var i = 0; i < parts.length; i++) {
    +    var block = {};
    +    if (goog.i18n.MessageFormat.Element_.STRING == parts[i].type) {
    +      block.type = goog.i18n.MessageFormat.BlockType_.STRING;
    +      block.value = parts[i].value;
    +    } else if (goog.i18n.MessageFormat.Element_.BLOCK == parts[i].type) {
    +      var blockType = this.parseBlockType_(parts[i].value);
    +
    +      switch (blockType) {
    +        case goog.i18n.MessageFormat.BlockType_.SELECT:
    +          block.type = goog.i18n.MessageFormat.BlockType_.SELECT;
    +          block.value = this.parseSelectBlock_(parts[i].value);
    +          break;
    +        case goog.i18n.MessageFormat.BlockType_.PLURAL:
    +          block.type = goog.i18n.MessageFormat.BlockType_.PLURAL;
    +          block.value = this.parsePluralBlock_(parts[i].value);
    +          break;
    +        case goog.i18n.MessageFormat.BlockType_.ORDINAL:
    +          block.type = goog.i18n.MessageFormat.BlockType_.ORDINAL;
    +          block.value = this.parseOrdinalBlock_(parts[i].value);
    +          break;
    +        case goog.i18n.MessageFormat.BlockType_.SIMPLE:
    +          block.type = goog.i18n.MessageFormat.BlockType_.SIMPLE;
    +          block.value = parts[i].value;
    +          break;
    +        default:
    +          goog.asserts.fail(
    +              'Unknown block type for pattern: ' + parts[i].value);
    +      }
    +    } else {
    +      goog.asserts.fail('Unknown part of the pattern.');
    +    }
    +    result.push(block);
    +  }
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Parses a select type of a block and produces JSON object for it.
    + * @param {string} pattern Subpattern that needs to be parsed as select pattern.
    + * @return {!Object} Object with select block info.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parseSelectBlock_ = function(pattern) {
    +  var argumentIndex = '';
    +  var replaceRegex = goog.i18n.MessageFormat.SELECT_BLOCK_RE_;
    +  pattern = pattern.replace(replaceRegex, function(string, name) {
    +    argumentIndex = name;
    +    return '';
    +  });
    +  var result = {};
    +  result.argumentIndex = argumentIndex;
    +
    +  var parts = this.extractParts_(pattern);
    +  // Looking for (key block)+ sequence. One of the keys has to be "other".
    +  var pos = 0;
    +  while (pos < parts.length) {
    +    var key = parts[pos].value;
    +    goog.asserts.assertString(key, 'Missing select key element.');
    +
    +    pos++;
    +    goog.asserts.assert(pos < parts.length,
    +                        'Missing or invalid select value element.');
    +
    +    if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
    +      var value = this.parseBlock_(parts[pos].value);
    +    } else {
    +      goog.asserts.fail('Expected block type.');
    +    }
    +    result[key.replace(/\s/g, '')] = value;
    +    pos++;
    +  }
    +
    +  goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
    +                           'Missing other key in select statement.');
    +  return result;
    +};
    +
    +
    +/**
    + * Parses a plural type of a block and produces JSON object for it.
    + * @param {string} pattern Subpattern that needs to be parsed as plural pattern.
    + * @return {!Object} Object with select block info.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parsePluralBlock_ = function(pattern) {
    +  var argumentIndex = '';
    +  var argumentOffset = 0;
    +  var replaceRegex = goog.i18n.MessageFormat.PLURAL_BLOCK_RE_;
    +  pattern = pattern.replace(replaceRegex, function(string, name, offset) {
    +    argumentIndex = name;
    +    if (offset) {
    +      argumentOffset = parseInt(offset, 10);
    +    }
    +    return '';
    +  });
    +
    +  var result = {};
    +  result.argumentIndex = argumentIndex;
    +  result.argumentOffset = argumentOffset;
    +
    +  var parts = this.extractParts_(pattern);
    +  // Looking for (key block)+ sequence.
    +  var pos = 0;
    +  while (pos < parts.length) {
    +    var key = parts[pos].value;
    +    goog.asserts.assertString(key, 'Missing plural key element.');
    +
    +    pos++;
    +    goog.asserts.assert(pos < parts.length,
    +                        'Missing or invalid plural value element.');
    +
    +    if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
    +      var value = this.parseBlock_(parts[pos].value);
    +    } else {
    +      goog.asserts.fail('Expected block type.');
    +    }
    +    result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
    +    pos++;
    +  }
    +
    +  goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
    +                           'Missing other key in plural statement.');
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Parses an ordinal type of a block and produces JSON object for it.
    + * For example the input string:
    + *  '{FOO, selectordinal, one {Message A}other {Message B}}'
    + * Should result in the output object:
    + * {
    + *   argumentIndex: 'FOO',
    + *   argumentOffest: 0,
    + *   one: [ { type: 4, value: 'Message A' } ],
    + *   other: [ { type: 4, value: 'Message B' } ]
    + * }
    + * @param {string} pattern Subpattern that needs to be parsed as plural pattern.
    + * @return {!Object} Object with select block info.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.parseOrdinalBlock_ = function(pattern) {
    +  var argumentIndex = '';
    +  var replaceRegex = goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_;
    +  pattern = pattern.replace(replaceRegex, function(string, name) {
    +    argumentIndex = name;
    +    return '';
    +  });
    +
    +  var result = {};
    +  result.argumentIndex = argumentIndex;
    +  result.argumentOffset = 0;
    +
    +  var parts = this.extractParts_(pattern);
    +  // Looking for (key block)+ sequence.
    +  var pos = 0;
    +  while (pos < parts.length) {
    +    var key = parts[pos].value;
    +    goog.asserts.assertString(key, 'Missing ordinal key element.');
    +
    +    pos++;
    +    goog.asserts.assert(pos < parts.length,
    +                        'Missing or invalid ordinal value element.');
    +
    +    if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
    +      var value = this.parseBlock_(parts[pos].value);
    +    } else {
    +      goog.asserts.fail('Expected block type.');
    +    }
    +    result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
    +    pos++;
    +  }
    +
    +  goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
    +                           'Missing other key in selectordinal statement.');
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Builds a placeholder from the last index of the array.
    + * @param {!Array<string>} literals All literals encountered during parse.
    + * @return {string} \uFDDF_ + last index + _.
    + * @private
    + */
    +goog.i18n.MessageFormat.prototype.buildPlaceholder_ = function(literals) {
    +  goog.asserts.assert(literals.length > 0, 'Literal array is empty.');
    +
    +  var index = (literals.length - 1).toString(10);
    +  return goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ + index + '_';
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.html b/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.html
    new file mode 100644
    index 0000000000..3d85939080
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.MessageFormat
    +  </title>
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.MessageFormatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.js b/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.js
    new file mode 100644
    index 0000000000..2dadb47a2e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/messageformat_test.js
    @@ -0,0 +1,464 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.MessageFormatTest');
    +goog.setTestOnly('goog.i18n.MessageFormatTest');
    +
    +goog.require('goog.i18n.MessageFormat');
    +goog.require('goog.i18n.NumberFormatSymbols_hr');
    +goog.require('goog.i18n.pluralRules');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +// Testing stubs that autoreset after each test run.
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testEmptyPattern() {
    +  var fmt = new goog.i18n.MessageFormat('');
    +  assertEquals('', fmt.format({}));
    +}
    +
    +function testMissingLeftCurlyBrace() {
    +  var err = assertThrows(function() {
    +    new goog.i18n.MessageFormat('\'\'{}}');
    +  });
    +  assertEquals('Assertion failed: No matching { for }.', err.message);
    +}
    +
    +function testTooManyLeftCurlyBraces() {
    +  var err = assertThrows(function() {
    +    new goog.i18n.MessageFormat('{} {');
    +  });
    +  assertEquals('Assertion failed: There are mismatched { or } in the pattern.',
    +      err.message);
    +}
    +
    +function testSimpleReplacement() {
    +  var fmt = new goog.i18n.MessageFormat('New York in {SEASON} is nice.');
    +  assertEquals('New York in the Summer is nice.',
    +               fmt.format({'SEASON': 'the Summer'}));
    +}
    +
    +function testSimpleSelect() {
    +  var fmt = new goog.i18n.MessageFormat('{GENDER, select,' +
    +      'male {His} ' +
    +      'female {Her} ' +
    +      'other {Its}}' +
    +      ' bicycle is {GENDER, select, male {blue} female {red} other {green}}.');
    +
    +  assertEquals('His bicycle is blue.', fmt.format({'GENDER': 'male'}));
    +  assertEquals('Her bicycle is red.', fmt.format({'GENDER': 'female'}));
    +  assertEquals('Its bicycle is green.', fmt.format({'GENDER': 'other'}));
    +  assertEquals('Its bicycle is green.', fmt.format({'GENDER': 'whatever'}));
    +}
    +
    +function testSimplePlural() {
    +  var fmt = new goog.i18n.MessageFormat('I see {NUM_PEOPLE, plural, offset:1 ' +
    +      '=0 {no one at all in {PLACE}.} ' +
    +      '=1 {{PERSON} in {PLACE}.} ' +
    +      'one {{PERSON} and one other person in {PLACE}.} ' +
    +      'other {{PERSON} and # other people in {PLACE}.}}');
    +
    +  assertEquals('I see no one at all in Belgrade.',
    +               fmt.format({'NUM_PEOPLE': 0,
    +        'PLACE': 'Belgrade'}));
    +  assertEquals('I see Markus in Berlin.',
    +               fmt.format({'NUM_PEOPLE': 1,
    +        'PERSON': 'Markus',
    +        'PLACE': 'Berlin'}));
    +  assertEquals('I see Mark and one other person in Athens.',
    +               fmt.format({'NUM_PEOPLE': 2,
    +        'PERSON': 'Mark',
    +        'PLACE': 'Athens'}));
    +  assertEquals('I see Cibu and 99 other people in the cubes.',
    +               fmt.format({'NUM_PEOPLE': 100,
    +        'PERSON': 'Cibu',
    +        'PLACE': 'the cubes'}));
    +}
    +
    +function testSimplePluralNoOffset() {
    +  var fmt = new goog.i18n.MessageFormat('I see {NUM_PEOPLE, plural, ' +
    +      '=0 {no one at all} ' +
    +      '=1 {{PERSON}} ' +
    +      'one {{PERSON} and one other person} ' +
    +      'other {{PERSON} and # other people}} in {PLACE}.');
    +
    +  assertEquals('I see no one at all in Belgrade.',
    +               fmt.format({'NUM_PEOPLE': 0,
    +        'PLACE': 'Belgrade'}));
    +  assertEquals('I see Markus in Berlin.',
    +               fmt.format({'NUM_PEOPLE': 1,
    +        'PERSON': 'Markus',
    +        'PLACE': 'Berlin'}));
    +  assertEquals('I see Mark and 2 other people in Athens.',
    +               fmt.format({'NUM_PEOPLE': 2,
    +        'PERSON': 'Mark',
    +        'PLACE': 'Athens'}));
    +  assertEquals('I see Cibu and 100 other people in the cubes.',
    +               fmt.format({'NUM_PEOPLE': 100,
    +        'PERSON': 'Cibu',
    +        'PLACE': 'the cubes'}));
    +}
    +
    +function testSelectNestedInPlural() {
    +  var fmt = new goog.i18n.MessageFormat('{CIRCLES, plural, ' +
    +      'one {{GENDER, select, ' +
    +      '  female {{WHO} added you to her circle} ' +
    +      '  other  {{WHO} added you to his circle}}} ' +
    +      'other {{GENDER, select, ' +
    +      '  female {{WHO} added you to her # circles} ' +
    +      '  other  {{WHO} added you to his # circles}}}}');
    +
    +  assertEquals('Jelena added you to her circle',
    +               fmt.format({'GENDER': 'female',
    +        'WHO': 'Jelena',
    +        'CIRCLES': 1}));
    +  assertEquals('Milan added you to his 1,234 circles',
    +               fmt.format({'GENDER': 'male',
    +        'WHO': 'Milan',
    +        'CIRCLES': 1234}));
    +}
    +
    +function testPluralNestedInSelect() {
    +  // Added offset just for testing purposes. It doesn't make sense
    +  // to have it otherwise.
    +  var fmt = new goog.i18n.MessageFormat('{GENDER, select, ' +
    +      'female {{NUM_GROUPS, plural, ' +
    +      '  one {{WHO} added you to her group} ' +
    +      '  other {{WHO} added you to her # groups}}} ' +
    +      'other {{NUM_GROUPS, plural, offset:1' +
    +      '  one {{WHO} added you to his group} ' +
    +      '  other {{WHO} added you to his # groups}}}}');
    +
    +  assertEquals('Jelena added you to her group',
    +               fmt.format({'GENDER': 'female',
    +        'WHO': 'Jelena',
    +        'NUM_GROUPS': 1}));
    +  assertEquals('Milan added you to his 1,233 groups',
    +               fmt.format({'GENDER': 'male',
    +        'WHO': 'Milan',
    +        'NUM_GROUPS': 1234}));
    +}
    +
    +function testLiteralOpenCurlyBrace() {
    +  var fmt = new goog.i18n.MessageFormat("Anna's house" +
    +      " has '{0} and # in the roof' and {NUM_COWS} cows.");
    +  assertEquals("Anna's house has {0} and # in the roof and 5 cows.",
    +               fmt.format({'NUM_COWS': '5'}));
    +}
    +
    +function testLiteralClosedCurlyBrace() {
    +  var fmt = new goog.i18n.MessageFormat("Anna's house" +
    +      " has '{'0'} and # in the roof' and {NUM_COWS} cows.");
    +  assertEquals("Anna's house has {0} and # in the roof and 5 cows.",
    +               fmt.format({'NUM_COWS': '5'}));
    +}
    +
    +function testLiteralPoundSign() {
    +  var fmt = new goog.i18n.MessageFormat("Anna's house" +
    +      " has '{0}' and '# in the roof' and {NUM_COWS} cows.");
    +  assertEquals("Anna's house has {0} and # in the roof and 5 cows.",
    +               fmt.format({'NUM_COWS': '5'}));
    +}
    +
    +function testNoLiteralsForSingleQuotes() {
    +  var fmt = new goog.i18n.MessageFormat("Anna's house" +
    +      " 'has {NUM_COWS} cows'.");
    +  assertEquals("Anna's house 'has 5 cows'.", fmt.format({'NUM_COWS': '5'}));
    +}
    +
    +function testConsecutiveSingleQuotesAreReplacedWithOneSingleQuote() {
    +  var fmt = new goog.i18n.MessageFormat("Anna''s house a'{''''b'");
    +  assertEquals("Anna's house a{''b", fmt.format({}));
    +}
    +
    +function testConsecutiveSingleQuotesBeforeSpecialCharDontCreateLiteral() {
    +  var fmt = new goog.i18n.MessageFormat("a''{NUM_COWS}'b");
    +  assertEquals("a'5'b", fmt.format({'NUM_COWS': '5'}));
    +}
    +
    +function testSerbianSimpleSelect() {
    +  stubs.set(goog.i18n.pluralRules, 'select', goog.i18n.pluralRules.beSelect_);
    +
    +  var fmt = new goog.i18n.MessageFormat('{GENDER, select, ' +
    +      'female {Njen} other {Njegov}} bicikl je ' +
    +      '{GENDER, select, female {crven} other {plav}}.');
    +
    +  assertEquals('Njegov bicikl je plav.', fmt.format({'GENDER': 'male'}));
    +  assertEquals('Njen bicikl je crven.', fmt.format({'GENDER': 'female'}));
    +}
    +
    +function testSerbianSimplePlural() {
    +  stubs.set(goog.i18n.pluralRules, 'select', goog.i18n.pluralRules.beSelect_);
    +
    +  var fmt = new goog.i18n.MessageFormat('Ja {NUM_PEOPLE, plural, offset:1 ' +
    +      '=0 {ne vidim nikoga} ' +
    +      '=1 {vidim {PERSON}} ' +
    +      'one {vidim {PERSON} i jos # osobu} ' +
    +      'few {vidim {PERSON} i jos # osobe} ' +
    +      'many {vidim {PERSON} i jos # osoba} ' +
    +      'other {{PERSON} i jos # osoba}} ' +
    +      'u {PLACE}.');
    +
    +  assertEquals('Ja ne vidim nikoga u Beogradu.',
    +               fmt.format({'NUM_PEOPLE': 0,
    +        'PLACE': 'Beogradu'}));
    +  assertEquals('Ja vidim Markusa u Berlinu.',
    +               fmt.format({'NUM_PEOPLE': 1,
    +        'PERSON': 'Markusa',
    +        'PLACE': 'Berlinu'}));
    +  assertEquals('Ja vidim Marka i jos 1 osobu u Atini.',
    +               fmt.format({'NUM_PEOPLE': 2,
    +        'PERSON': 'Marka',
    +        'PLACE': 'Atini'}));
    +  assertEquals('Ja vidim Petra i jos 3 osobe u muzeju.',
    +               fmt.format({'NUM_PEOPLE': 4,
    +        'PERSON': 'Petra',
    +        'PLACE': 'muzeju'}));
    +  assertEquals('Ja vidim Cibua i jos 99 osoba u bazenu.',
    +               fmt.format({'NUM_PEOPLE': 100,
    +        'PERSON': 'Cibua',
    +        'PLACE': 'bazenu'}));
    +}
    +
    +function testSerbianSimplePluralNoOffset() {
    +  stubs.set(goog.i18n.pluralRules, 'select', goog.i18n.pluralRules.beSelect_);
    +
    +  var fmt = new goog.i18n.MessageFormat('Ja {NUM_PEOPLE, plural, ' +
    +      '=0 {ne vidim nikoga} ' +
    +      '=1 {vidim {PERSON}} ' +
    +      'one {vidim {PERSON} i jos # osobu} ' +
    +      'few {vidim {PERSON} i jos # osobe} ' +
    +      'many {vidim {PERSON} i jos # osoba} ' +
    +      'other {{PERSON} i jos # osoba}} ' +
    +      'u {PLACE}.');
    +
    +  assertEquals('Ja ne vidim nikoga u Beogradu.',
    +               fmt.format({'NUM_PEOPLE': 0,
    +        'PLACE': 'Beogradu'}));
    +  assertEquals('Ja vidim Markusa u Berlinu.',
    +               fmt.format({'NUM_PEOPLE': 1,
    +        'PERSON': 'Markusa',
    +        'PLACE': 'Berlinu'}));
    +  assertEquals('Ja vidim Marka i jos 21 osobu u Atini.',
    +               fmt.format({'NUM_PEOPLE': 21,
    +        'PERSON': 'Marka',
    +        'PLACE': 'Atini'}));
    +  assertEquals('Ja vidim Petra i jos 3 osobe u muzeju.',
    +               fmt.format({'NUM_PEOPLE': 3,
    +        'PERSON': 'Petra',
    +        'PLACE': 'muzeju'}));
    +  assertEquals('Ja vidim Cibua i jos 100 osoba u bazenu.',
    +               fmt.format({'NUM_PEOPLE': 100,
    +        'PERSON': 'Cibua',
    +        'PLACE': 'bazenu'}));
    +}
    +
    +function testSerbianSelectNestedInPlural() {
    +  stubs.set(goog.i18n.pluralRules, 'select', goog.i18n.pluralRules.beSelect_);
    +  stubs.set(goog.i18n, 'NumberFormatSymbols', goog.i18n.NumberFormatSymbols_hr);
    +
    +  var fmt = new goog.i18n.MessageFormat('{CIRCLES, plural, ' +
    +      'one {{GENDER, select, ' +
    +      '  female {{WHO} vas je dodala u njen # kruzok} ' +
    +      '  other  {{WHO} vas je dodao u njegov # kruzok}}} ' +
    +      'few {{GENDER, select, ' +
    +      '  female {{WHO} vas je dodala u njena # kruzoka} ' +
    +      '  other  {{WHO} vas je dodao u njegova # kruzoka}}} ' +
    +      'many {{GENDER, select, ' +
    +      '  female {{WHO} vas je dodala u njenih # kruzoka} ' +
    +      '  other  {{WHO} vas je dodao u njegovih # kruzoka}}} ' +
    +      'other {{GENDER, select, ' +
    +      '  female {{WHO} vas je dodala u njenih # kruzoka} ' +
    +      '  other  {{WHO} vas je dodao u njegovih # kruzoka}}}}');
    +
    +  assertEquals('Jelena vas je dodala u njen 21 kruzok',
    +               fmt.format({'GENDER': 'female',
    +        'WHO': 'Jelena',
    +        'CIRCLES': 21}));
    +  assertEquals('Jelena vas je dodala u njena 3 kruzoka',
    +               fmt.format({'GENDER': 'female',
    +        'WHO': 'Jelena',
    +        'CIRCLES': 3}));
    +  assertEquals('Jelena vas je dodala u njenih 5 kruzoka',
    +               fmt.format({'GENDER': 'female',
    +        'WHO': 'Jelena',
    +        'CIRCLES': 5}));
    +  assertEquals('Milan vas je dodao u njegovih 1.235 kruzoka',
    +               fmt.format({'GENDER': 'male',
    +        'WHO': 'Milan',
    +        'CIRCLES': 1235}));
    +}
    +
    +function testFallbackToOtherOptionInPlurals() {
    +  // Use Arabic plural rules since they have all six cases.
    +  // Only locale and numbers matter, the actual language of the message
    +  // does not.
    +  stubs.set(goog.i18n.pluralRules, 'select', goog.i18n.pluralRules.arSelect_);
    +
    +  var fmt = new goog.i18n.MessageFormat('{NUM_MINUTES, plural, ' +
    +      'other {# minutes}}');
    +
    +  // These numbers exercise all cases for the arabic plural rules.
    +  assertEquals('0 minutes', fmt.format({'NUM_MINUTES': 0}));
    +  assertEquals('1 minutes', fmt.format({'NUM_MINUTES': 1}));
    +  assertEquals('2 minutes', fmt.format({'NUM_MINUTES': 2}));
    +  assertEquals('3 minutes', fmt.format({'NUM_MINUTES': 3}));
    +  assertEquals('11 minutes', fmt.format({'NUM_MINUTES': 11}));
    +  assertEquals('1.5 minutes', fmt.format({'NUM_MINUTES': 1.5}));
    +}
    +
    +function testPoundShowsNumberMinusOffsetInAllCases() {
    +  var fmt = new goog.i18n.MessageFormat('{SOME_NUM, plural, offset:1 ' +
    +      '=0 {#} =1 {#} =2 {#}one {#} other {#}}');
    +
    +  assertEquals('-1', fmt.format({'SOME_NUM': '0'}));
    +  assertEquals('0', fmt.format({'SOME_NUM': '1'}));
    +  assertEquals('1', fmt.format({'SOME_NUM': '2'}));
    +  assertEquals('20', fmt.format({'SOME_NUM': '21'}));
    +}
    +
    +function testSpecialCharactersInParamaterDontChangeFormat() {
    +  var fmt = new goog.i18n.MessageFormat('{SOME_NUM, plural,' +
    +      'other {# {GROUP}}}');
    +
    +  // Test pound sign.
    +  assertEquals('10 group#1',
    +               fmt.format({'SOME_NUM': '10', 'GROUP': 'group#1'}));
    +  // Test other special characters in parameters, like { and }.
    +  assertEquals('10 } {', fmt.format({'SOME_NUM': '10', 'GROUP': '} {'}));
    +}
    +
    +function testMissingOrInvalidPluralParameter() {
    +  var fmt = new goog.i18n.MessageFormat('{SOME_NUM, plural,' +
    +      'other {result}}');
    +
    +  // Key name doesn't match A != SOME_NUM.
    +  assertEquals('Undefined or invalid parameter - SOME_NUM',
    +               fmt.format({A: '10'}));
    +
    +  // Value is not a number.
    +  assertEquals('Undefined or invalid parameter - SOME_NUM',
    +               fmt.format({'SOME_NUM': 'Value'}));
    +}
    +
    +function testMissingSelectParameter() {
    +  var fmt = new goog.i18n.MessageFormat('{GENDER, select,' +
    +      'other {result}}');
    +
    +  // Key name doesn't match A != GENDER.
    +  assertEquals('Undefined parameter - GENDER',
    +               fmt.format({A: 'female'}));
    +}
    +
    +function testMissingSimplePlaceholder() {
    +  var fmt = new goog.i18n.MessageFormat('{result}');
    +
    +  // Key name doesn't match A != result.
    +  assertEquals('Undefined parameter - result',
    +               fmt.format({A: 'none'}));
    +}
    +
    +function testPluralWithIgnorePound() {
    +  var fmt = new goog.i18n.MessageFormat('{SOME_NUM, plural,' +
    +      'other {# {GROUP}}}');
    +
    +  // Test pound sign.
    +  assertEquals('# group#1',
    +               fmt.formatIgnoringPound({'SOME_NUM': '10', 'GROUP': 'group#1'}));
    +  // Test other special characters in parameters, like { and }.
    +  assertEquals('# } {',
    +      fmt.formatIgnoringPound({'SOME_NUM': '10', 'GROUP': '} {'}));
    +}
    +
    +function testSimplePluralWithIgnorePound() {
    +  var fmt = new goog.i18n.MessageFormat(
    +      'I see {NUM_PEOPLE, plural, offset:1 ' +
    +      '=0 {no one at all in {PLACE}.} ' +
    +      '=1 {{PERSON} in {PLACE}.} ' +
    +      'one {{PERSON} and one other person in {PLACE}.} ' +
    +      'other {{PERSON} and # other people in {PLACE}.}}');
    +
    +  assertEquals('I see Cibu and # other people in the cubes.',
    +               fmt.formatIgnoringPound({'NUM_PEOPLE': 100,
    +        'PERSON': 'Cibu',
    +        'PLACE': 'the cubes'}));
    +}
    +
    +function testSimpleOrdinal() {
    +  var fmt = new goog.i18n.MessageFormat('{NUM_FLOOR, selectordinal, ' +
    +      'one {Take the elevator to the #st floor.}' +
    +      'two {Take the elevator to the #nd floor.}' +
    +      'few {Take the elevator to the #rd floor.}' +
    +      'other {Take the elevator to the #th floor.}}');
    +
    +  assertEquals('Take the elevator to the 1st floor.',
    +               fmt.format({'NUM_FLOOR': 1}));
    +  assertEquals('Take the elevator to the 2nd floor.',
    +               fmt.format({'NUM_FLOOR': 2}));
    +  assertEquals('Take the elevator to the 3rd floor.',
    +               fmt.format({'NUM_FLOOR': 3}));
    +  assertEquals('Take the elevator to the 4th floor.',
    +               fmt.format({'NUM_FLOOR': 4}));
    +  assertEquals('Take the elevator to the 23rd floor.',
    +               fmt.format({'NUM_FLOOR': 23}));
    +  // Esoteric example.
    +  assertEquals('Take the elevator to the 0th floor.',
    +               fmt.format({'NUM_FLOOR': 0}));
    +}
    +
    +function testOrdinalWithNegativeValue() {
    +  var fmt = new goog.i18n.MessageFormat('{NUM_FLOOR, selectordinal, ' +
    +      'one {Take the elevator to the #st floor.}' +
    +      'two {Take the elevator to the #nd floor.}' +
    +      'few {Take the elevator to the #rd floor.}' +
    +      'other {Take the elevator to the #th floor.}}');
    +
    +  try {
    +    fmt.format({'NUM_FLOOR': -2});
    +  } catch (e) {
    +    assertEquals('Assertion failed: Argument index smaller than offset.',
    +                 e.message);
    +    return;
    +  }
    +  fail('Expected an error to be thrown');
    +}
    +
    +function testSimpleOrdinalWithIgnorePound() {
    +  var fmt = new goog.i18n.MessageFormat('{NUM_FLOOR, selectordinal, ' +
    +      'one {Take the elevator to the #st floor.}' +
    +      'two {Take the elevator to the #nd floor.}' +
    +      'few {Take the elevator to the #rd floor.}' +
    +      'other {Take the elevator to the #th floor.}}');
    +
    +  assertEquals('Take the elevator to the #th floor.',
    +               fmt.formatIgnoringPound({'NUM_FLOOR': 100}));
    +}
    +
    +function testMissingOrInvalidOrdinalParameter() {
    +  var fmt = new goog.i18n.MessageFormat('{SOME_NUM, selectordinal,' +
    +      'other {result}}');
    +
    +  // Key name doesn't match A != SOME_NUM.
    +  assertEquals('Undefined or invalid parameter - SOME_NUM',
    +               fmt.format({A: '10'}));
    +
    +  // Value is not a number.
    +  assertEquals('Undefined or invalid parameter - SOME_NUM',
    +               fmt.format({'SOME_NUM': 'Value'}));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/mime.js b/mapbox-gl/closure-library/closure/goog/i18n/mime.js
    new file mode 100644
    index 0000000000..852d3b219a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/mime.js
    @@ -0,0 +1,111 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for encoding strings according to MIME
    + * standards, especially RFC 1522.
    + */
    +goog.provide('goog.i18n.mime');
    +goog.provide('goog.i18n.mime.encode');
    +
    +goog.require('goog.array');
    +
    +
    +/**
    + * Regular expression for matching those characters that are outside the
    + * range that can be used in the quoted-printable encoding of RFC 1522:
    + * anything outside the 7-bit ASCII encoding, plus ?, =, _ or space.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.mime.NONASCII_ = /[^!-<>@-^`-~]/g;
    +
    +
    +/**
    + * Like goog.i18n.NONASCII_ but also omits double-quotes.
    + * @type {RegExp}
    + * @private
    + */
    +goog.i18n.mime.NONASCII_NOQUOTE_ = /[^!#-<>@-^`-~]/g;
    +
    +
    +/**
    + * Encodes a string for inclusion in a MIME header. The string is encoded
    + * in UTF-8 according to RFC 1522, using quoted-printable form.
    + * @param {string} str The string to encode.
    + * @param {boolean=} opt_noquote Whether double-quote characters should also
    + *     be escaped (should be true if the result will be placed inside a
    + *     quoted string for a parameter value in a MIME header).
    + * @return {string} The encoded string.
    + */
    +goog.i18n.mime.encode = function(str, opt_noquote) {
    +  var nonascii = opt_noquote ?
    +      goog.i18n.mime.NONASCII_NOQUOTE_ : goog.i18n.mime.NONASCII_;
    +
    +  if (str.search(nonascii) >= 0) {
    +    str = '=?UTF-8?Q?' + str.replace(nonascii,
    +        /**
    +         * @param {string} c The matched char.
    +         * @return {string} The quoted-printable form of utf-8 encoding.
    +         */
    +        function(c) {
    +          var i = c.charCodeAt(0);
    +          if (i == 32) {
    +            // Special case for space, which can be encoded as _ not =20
    +            return '_';
    +          }
    +          var a = goog.array.concat('', goog.i18n.mime.getHexCharArray(c));
    +          return a.join('=');
    +        }) + '?=';
    +  }
    +  return str;
    +};
    +
    +
    +/**
    + * Get an array of UTF-8 hex codes for a given character.
    + * @param {string} c The matched character.
    + * @return {!Array<string>} A hex array representing the character.
    + */
    +goog.i18n.mime.getHexCharArray = function(c) {
    +  var i = c.charCodeAt(0);
    +  var a = [];
    +  // First convert the UCS-2 character into its UTF-8 bytes
    +  if (i < 128) {
    +    a.push(i);
    +  } else if (i <= 0x7ff) {
    +    a.push(
    +        0xc0 + ((i >> 6) & 0x3f),
    +        0x80 + (i & 0x3f));
    +  } else if (i <= 0xffff) {
    +    a.push(
    +        0xe0 + ((i >> 12) & 0x3f),
    +        0x80 + ((i >> 6) & 0x3f),
    +        0x80 + (i & 0x3f));
    +  } else {
    +    // (This is defensive programming, since ecmascript isn't supposed
    +    // to handle code points that take more than 16 bits.)
    +    a.push(
    +        0xf0 + ((i >> 18) & 0x3f),
    +        0x80 + ((i >> 12) & 0x3f),
    +        0x80 + ((i >> 6) & 0x3f),
    +        0x80 + (i & 0x3f));
    +  }
    +  // Now convert those bytes into hex strings (don't do anything with
    +  // a[0] as that's got the empty string that lets us use join())
    +  for (i = a.length - 1; i >= 0; --i) {
    +    a[i] = a[i].toString(16);
    +  }
    +  return a;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/mime_test.html b/mapbox-gl/closure-library/closure/goog/i18n/mime_test.html
    new file mode 100644
    index 0000000000..6bbf95a1f3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/mime_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.mime
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.i18n.mime.encodeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/mime_test.js b/mapbox-gl/closure-library/closure/goog/i18n/mime_test.js
    new file mode 100644
    index 0000000000..94f569156a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/mime_test.js
    @@ -0,0 +1,43 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.mime.encodeTest');
    +goog.setTestOnly('goog.i18n.mime.encodeTest');
    +
    +goog.require('goog.i18n.mime.encode');
    +goog.require('goog.testing.jsunit');
    +
    +function testEncodeAllAscii() {
    +  // A string holding all the characters that should be encoded unchanged.
    +  // Double-quote is doubled to avoid annoying syntax highlighting in emacs,
    +  // which doesn't recognize the double-quote as being in a string constant.
    +  var identity = '!""#$%&\'()*+,-./0123456789:;<>@ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    +      '[\\]^`abcdefghijklmnopqrstuvwxyz{|}~';
    +  assertEquals(identity, goog.i18n.mime.encode(identity));
    +}
    +
    +function testEncodeSpecials() {
    +  assertEquals('=?UTF-8?Q?=3f=5f=3d_?=', goog.i18n.mime.encode('?_= '));
    +  assertEquals('=?UTF-8?Q?=3f=5f=3d_=22=22?=',
    +      goog.i18n.mime.encode('?_= ""', true));
    +}
    +
    +function testEncodeUnicode() {
    +  // Two-byte UTF-8, plus a special
    +  assertEquals('=?UTF-8?Q?=c2=82=de=a0_dude?=',
    +      goog.i18n.mime.encode('\u0082\u07a0 dude'));
    +  // Three-byte UTF-8, plus a special
    +  assertEquals('=?UTF-8?Q?=e0=a0=80=ef=bf=bf=3d?=',
    +      goog.i18n.mime.encode('\u0800\uffff='));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/numberformat.js b/mapbox-gl/closure-library/closure/goog/i18n/numberformat.js
    new file mode 100644
    index 0000000000..edc9eca3ad
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/numberformat.js
    @@ -0,0 +1,1249 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Number format/parse library with locale support.
    + */
    +
    +
    +/**
    + * Namespace for locale number format functions
    + */
    +goog.provide('goog.i18n.NumberFormat');
    +goog.provide('goog.i18n.NumberFormat.CurrencyStyle');
    +goog.provide('goog.i18n.NumberFormat.Format');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.i18n.CompactNumberFormatSymbols');
    +goog.require('goog.i18n.NumberFormatSymbols');
    +goog.require('goog.i18n.currency');
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Constructor of NumberFormat.
    + * @param {number|string} pattern The number that indicates a predefined
    + *     number format pattern.
    + * @param {string=} opt_currency Optional international currency
    + *     code. This determines the currency code/symbol used in format/parse. If
    + *     not given, the currency code for current locale will be used.
    + * @param {number=} opt_currencyStyle currency style, value defined in
    + *        goog.i18n.NumberFormat.CurrencyStyle.
    + * @constructor
    + */
    +goog.i18n.NumberFormat = function(pattern, opt_currency, opt_currencyStyle) {
    +  this.intlCurrencyCode_ = opt_currency ||
    +      goog.i18n.NumberFormatSymbols.DEF_CURRENCY_CODE;
    +
    +  this.currencyStyle_ = opt_currencyStyle ||
    +      goog.i18n.NumberFormat.CurrencyStyle.LOCAL;
    +
    +  this.maximumIntegerDigits_ = 40;
    +  this.minimumIntegerDigits_ = 1;
    +  this.significantDigits_ = 0; // invariant, <= maximumFractionDigits
    +  this.maximumFractionDigits_ = 3; // invariant, >= minFractionDigits
    +  this.minimumFractionDigits_ = 0;
    +  this.minExponentDigits_ = 0;
    +  this.useSignForPositiveExponent_ = false;
    +
    +  /**
    +   * Whether to show trailing zeros in the fraction when significantDigits_ is
    +   * positive.
    +   * @private {boolean}
    +   */
    +  this.showTrailingZeros_ = false;
    +
    +  this.positivePrefix_ = '';
    +  this.positiveSuffix_ = '';
    +  this.negativePrefix_ = '-';
    +  this.negativeSuffix_ = '';
    +
    +  // The multiplier for use in percent, per mille, etc.
    +  this.multiplier_ = 1;
    +  this.groupingSize_ = 3;
    +  this.decimalSeparatorAlwaysShown_ = false;
    +  this.useExponentialNotation_ = false;
    +  this.compactStyle_ = goog.i18n.NumberFormat.CompactStyle.NONE;
    +
    +  /**
    +   * The number to base the formatting on when using compact styles, or null
    +   * if formatting should not be based on another number.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.baseFormattingNumber_ = null;
    +
    +  /** @private {string} */
    +  this.pattern_;
    +
    +  if (typeof pattern == 'number') {
    +    this.applyStandardPattern_(pattern);
    +  } else {
    +    this.applyPattern_(pattern);
    +  }
    +};
    +
    +
    +/**
    + * Standard number formatting patterns.
    + * @enum {number}
    + */
    +goog.i18n.NumberFormat.Format = {
    +  DECIMAL: 1,
    +  SCIENTIFIC: 2,
    +  PERCENT: 3,
    +  CURRENCY: 4,
    +  COMPACT_SHORT: 5,
    +  COMPACT_LONG: 6
    +};
    +
    +
    +/**
    + * Currency styles.
    + * @enum {number}
    + */
    +goog.i18n.NumberFormat.CurrencyStyle = {
    +  LOCAL: 0,     // currency style as it is used in its circulating country.
    +  PORTABLE: 1,  // currency style that differentiate it from other popular ones.
    +  GLOBAL: 2     // currency style that is unique among all currencies.
    +};
    +
    +
    +/**
    + * Compacting styles.
    + * @enum {number}
    + */
    +goog.i18n.NumberFormat.CompactStyle = {
    +  NONE: 0,  // Don't compact.
    +  SHORT: 1, // Short compact form, such as 1.2B.
    +  LONG: 2  // Long compact form, such as 1.2 billion.
    +};
    +
    +
    +/**
    + * If the usage of Ascii digits should be enforced.
    + * @type {boolean}
    + * @private
    + */
    +goog.i18n.NumberFormat.enforceAsciiDigits_ = false;
    +
    +
    +/**
    + * Set if the usage of Ascii digits in formatting should be enforced.
    + * @param {boolean} doEnforce Boolean value about if Ascii digits should be
    + *     enforced.
    + */
    +goog.i18n.NumberFormat.setEnforceAsciiDigits = function(doEnforce) {
    +  goog.i18n.NumberFormat.enforceAsciiDigits_ = doEnforce;
    +};
    +
    +
    +/**
    + * Return if Ascii digits is enforced.
    + * @return {boolean} If Ascii digits is enforced.
    + */
    +goog.i18n.NumberFormat.isEnforceAsciiDigits = function() {
    +  return goog.i18n.NumberFormat.enforceAsciiDigits_;
    +};
    +
    +
    +/**
    + * Sets minimum number of fraction digits.
    + * @param {number} min the minimum.
    + * @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.
    + */
    +goog.i18n.NumberFormat.prototype.setMinimumFractionDigits = function(min) {
    +  if (this.significantDigits_ > 0 && min > 0) {
    +    throw Error(
    +        'Can\'t combine significant digits and minimum fraction digits');
    +  }
    +  this.minimumFractionDigits_ = min;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets maximum number of fraction digits.
    + * @param {number} max the maximum.
    + * @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.
    + */
    +goog.i18n.NumberFormat.prototype.setMaximumFractionDigits = function(max) {
    +  this.maximumFractionDigits_ = max;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets number of significant digits to show. Only fractions will be rounded.
    + * @param {number} number The number of significant digits to include.
    + * @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.
    + */
    +goog.i18n.NumberFormat.prototype.setSignificantDigits = function(number) {
    +  if (this.minimumFractionDigits_ > 0 && number >= 0) {
    +    throw Error(
    +        'Can\'t combine significant digits and minimum fraction digits');
    +  }
    +  this.significantDigits_ = number;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets number of significant digits to show. Only fractions will be rounded.
    + * @return {number} The number of significant digits to include.
    + */
    +goog.i18n.NumberFormat.prototype.getSignificantDigits = function() {
    +  return this.significantDigits_;
    +};
    +
    +
    +/**
    + * Sets whether trailing fraction zeros should be shown when significantDigits_
    + * is positive. If this is true and significantDigits_ is 2, 1 will be formatted
    + * as '1.0'.
    + * @param {boolean} showTrailingZeros Whether trailing zeros should be shown.
    + * @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.
    + */
    +goog.i18n.NumberFormat.prototype.setShowTrailingZeros =
    +    function(showTrailingZeros) {
    +  this.showTrailingZeros_ = showTrailingZeros;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets a number to base the formatting on when compact style formatting is
    + * used. If this is null, the formatting should be based only on the number to
    + * be formatting.
    + *
    + * This base formatting number can be used to format the target number as
    + * another number would be formatted. For example, 100,000 is normally formatted
    + * as "100K" in the COMPACT_SHORT format. To instead format it as '0.1M', the
    + * base number could be set to 1,000,000 in order to force all numbers to be
    + * formatted in millions. Similarly, 1,000,000,000 would normally be formatted
    + * as '1B' and setting the base formatting number to 1,000,000, would cause it
    + * to be formatted instead as '1,000M'.
    + *
    + * @param {?number} baseFormattingNumber The number to base formatting on, or
    + * null if formatting should not be based on another number.
    + * @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.
    + */
    +goog.i18n.NumberFormat.prototype.setBaseFormatting =
    +    function(baseFormattingNumber) {
    +  goog.asserts.assert(goog.isNull(baseFormattingNumber) ||
    +      isFinite(baseFormattingNumber));
    +  this.baseFormattingNumber_ = baseFormattingNumber;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the number on which compact formatting is currently based, or null if
    + * no such number is set. See setBaseFormatting() for more information.
    + * @return {?number}
    + */
    +goog.i18n.NumberFormat.prototype.getBaseFormatting = function() {
    +  return this.baseFormattingNumber_;
    +};
    +
    +
    +/**
    + * Apply provided pattern, result are stored in member variables.
    + *
    + * @param {string} pattern String pattern being applied.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.applyPattern_ = function(pattern) {
    +  this.pattern_ = pattern.replace(/ /g, '\u00a0');
    +  var pos = [0];
    +
    +  this.positivePrefix_ = this.parseAffix_(pattern, pos);
    +  var trunkStart = pos[0];
    +  this.parseTrunk_(pattern, pos);
    +  var trunkLen = pos[0] - trunkStart;
    +  this.positiveSuffix_ = this.parseAffix_(pattern, pos);
    +  if (pos[0] < pattern.length &&
    +      pattern.charAt(pos[0]) == goog.i18n.NumberFormat.PATTERN_SEPARATOR_) {
    +    pos[0]++;
    +    this.negativePrefix_ = this.parseAffix_(pattern, pos);
    +    // we assume this part is identical to positive part.
    +    // user must make sure the pattern is correctly constructed.
    +    pos[0] += trunkLen;
    +    this.negativeSuffix_ = this.parseAffix_(pattern, pos);
    +  } else {
    +    // if no negative affix specified, they share the same positive affix
    +    this.negativePrefix_ = this.positivePrefix_ + this.negativePrefix_;
    +    this.negativeSuffix_ += this.positiveSuffix_;
    +  }
    +};
    +
    +
    +/**
    + * Apply a predefined pattern to NumberFormat object.
    + * @param {number} patternType The number that indicates a predefined number
    + *     format pattern.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.applyStandardPattern_ = function(patternType) {
    +  switch (patternType) {
    +    case goog.i18n.NumberFormat.Format.DECIMAL:
    +      this.applyPattern_(goog.i18n.NumberFormatSymbols.DECIMAL_PATTERN);
    +      break;
    +    case goog.i18n.NumberFormat.Format.SCIENTIFIC:
    +      this.applyPattern_(goog.i18n.NumberFormatSymbols.SCIENTIFIC_PATTERN);
    +      break;
    +    case goog.i18n.NumberFormat.Format.PERCENT:
    +      this.applyPattern_(goog.i18n.NumberFormatSymbols.PERCENT_PATTERN);
    +      break;
    +    case goog.i18n.NumberFormat.Format.CURRENCY:
    +      this.applyPattern_(goog.i18n.currency.adjustPrecision(
    +          goog.i18n.NumberFormatSymbols.CURRENCY_PATTERN,
    +          this.intlCurrencyCode_));
    +      break;
    +    case goog.i18n.NumberFormat.Format.COMPACT_SHORT:
    +      this.applyCompactStyle_(goog.i18n.NumberFormat.CompactStyle.SHORT);
    +      break;
    +    case goog.i18n.NumberFormat.Format.COMPACT_LONG:
    +      this.applyCompactStyle_(goog.i18n.NumberFormat.CompactStyle.LONG);
    +      break;
    +    default:
    +      throw Error('Unsupported pattern type.');
    +  }
    +};
    +
    +
    +/**
    + * Apply a predefined pattern for shorthand formats.
    + * @param {goog.i18n.NumberFormat.CompactStyle} style the compact style to
    + *     set defaults for.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.applyCompactStyle_ = function(style) {
    +  this.compactStyle_ = style;
    +  this.applyPattern_(goog.i18n.NumberFormatSymbols.DECIMAL_PATTERN);
    +  this.setMinimumFractionDigits(0);
    +  this.setMaximumFractionDigits(2);
    +  this.setSignificantDigits(2);
    +};
    +
    +
    +/**
    + * Parses text string to produce a Number.
    + *
    + * This method attempts to parse text starting from position "opt_pos" if it
    + * is given. Otherwise the parse will start from the beginning of the text.
    + * When opt_pos presents, opt_pos will be updated to the character next to where
    + * parsing stops after the call. If an error occurs, opt_pos won't be updated.
    + *
    + * @param {string} text The string to be parsed.
    + * @param {Array<number>=} opt_pos Position to pass in and get back.
    + * @return {number} Parsed number. This throws an error if the text cannot be
    + *     parsed.
    + */
    +goog.i18n.NumberFormat.prototype.parse = function(text, opt_pos) {
    +  var pos = opt_pos || [0];
    +
    +  if (this.compactStyle_ != goog.i18n.NumberFormat.CompactStyle.NONE) {
    +    throw Error('Parsing of compact numbers is unimplemented');
    +  }
    +
    +  var start = pos[0];
    +  var ret = NaN;
    +
    +  // we don't want to handle 2 kind of space in parsing, normalize it to nbsp
    +  text = text.replace(/ /g, '\u00a0');
    +
    +  var gotPositive = text.indexOf(this.positivePrefix_, pos[0]) == pos[0];
    +  var gotNegative = text.indexOf(this.negativePrefix_, pos[0]) == pos[0];
    +
    +  // check for the longest match
    +  if (gotPositive && gotNegative) {
    +    if (this.positivePrefix_.length > this.negativePrefix_.length) {
    +      gotNegative = false;
    +    } else if (this.positivePrefix_.length < this.negativePrefix_.length) {
    +      gotPositive = false;
    +    }
    +  }
    +
    +  if (gotPositive) {
    +    pos[0] += this.positivePrefix_.length;
    +  } else if (gotNegative) {
    +    pos[0] += this.negativePrefix_.length;
    +  }
    +
    +  // process digits or Inf, find decimal position
    +  if (text.indexOf(goog.i18n.NumberFormatSymbols.INFINITY, pos[0]) == pos[0]) {
    +    pos[0] += goog.i18n.NumberFormatSymbols.INFINITY.length;
    +    ret = Infinity;
    +  } else {
    +    ret = this.parseNumber_(text, pos);
    +  }
    +
    +  // check for suffix
    +  if (gotPositive) {
    +    if (!(text.indexOf(this.positiveSuffix_, pos[0]) == pos[0])) {
    +      return NaN;
    +    }
    +    pos[0] += this.positiveSuffix_.length;
    +  } else if (gotNegative) {
    +    if (!(text.indexOf(this.negativeSuffix_, pos[0]) == pos[0])) {
    +      return NaN;
    +    }
    +    pos[0] += this.negativeSuffix_.length;
    +  }
    +
    +  return gotNegative ? -ret : ret;
    +};
    +
    +
    +/**
    + * This function will parse a "localized" text into a Number. It needs to
    + * handle locale specific decimal, grouping, exponent and digits.
    + *
    + * @param {string} text The text that need to be parsed.
    + * @param {Array<number>} pos  In/out parsing position. In case of failure,
    + *    pos value won't be changed.
    + * @return {number} Number value, or NaN if nothing can be parsed.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.parseNumber_ = function(text, pos) {
    +  var sawDecimal = false;
    +  var sawExponent = false;
    +  var sawDigit = false;
    +  var scale = 1;
    +  var decimal = goog.i18n.NumberFormatSymbols.DECIMAL_SEP;
    +  var grouping = goog.i18n.NumberFormatSymbols.GROUP_SEP;
    +  var exponentChar = goog.i18n.NumberFormatSymbols.EXP_SYMBOL;
    +
    +  if (this.compactStyle_ != goog.i18n.NumberFormat.CompactStyle.NONE) {
    +    throw Error('Parsing of compact style numbers is not implemented');
    +  }
    +
    +  var normalizedText = '';
    +  for (; pos[0] < text.length; pos[0]++) {
    +    var ch = text.charAt(pos[0]);
    +    var digit = this.getDigit_(ch);
    +    if (digit >= 0 && digit <= 9) {
    +      normalizedText += digit;
    +      sawDigit = true;
    +    } else if (ch == decimal.charAt(0)) {
    +      if (sawDecimal || sawExponent) {
    +        break;
    +      }
    +      normalizedText += '.';
    +      sawDecimal = true;
    +    } else if (ch == grouping.charAt(0) &&
    +               ('\u00a0' != grouping.charAt(0) ||
    +                pos[0] + 1 < text.length &&
    +                this.getDigit_(text.charAt(pos[0] + 1)) >= 0)) {
    +      // Got a grouping character here. When grouping character is nbsp, need
    +      // to make sure the character following it is a digit.
    +      if (sawDecimal || sawExponent) {
    +        break;
    +      }
    +      continue;
    +    } else if (ch == exponentChar.charAt(0)) {
    +      if (sawExponent) {
    +        break;
    +      }
    +      normalizedText += 'E';
    +      sawExponent = true;
    +    } else if (ch == '+' || ch == '-') {
    +      normalizedText += ch;
    +    } else if (ch == goog.i18n.NumberFormatSymbols.PERCENT.charAt(0)) {
    +      if (scale != 1) {
    +        break;
    +      }
    +      scale = 100;
    +      if (sawDigit) {
    +        pos[0]++; // eat this character if parse end here
    +        break;
    +      }
    +    } else if (ch == goog.i18n.NumberFormatSymbols.PERMILL.charAt(0)) {
    +      if (scale != 1) {
    +        break;
    +      }
    +      scale = 1000;
    +      if (sawDigit) {
    +        pos[0]++; // eat this character if parse end here
    +        break;
    +      }
    +    } else {
    +      break;
    +    }
    +  }
    +  return parseFloat(normalizedText) / scale;
    +};
    +
    +
    +/**
    + * Formats a Number to produce a string.
    + *
    + * @param {number} number The Number to be formatted.
    + * @return {string} The formatted number string.
    + */
    +goog.i18n.NumberFormat.prototype.format = function(number) {
    +  if (isNaN(number)) {
    +    return goog.i18n.NumberFormatSymbols.NAN;
    +  }
    +
    +  var parts = [];
    +  var baseFormattingNumber = goog.isNull(this.baseFormattingNumber_) ?
    +      number :
    +      this.baseFormattingNumber_;
    +  var unit = this.getUnitAfterRounding_(baseFormattingNumber, number);
    +  number /= Math.pow(10, unit.divisorBase);
    +
    +  parts.push(unit.prefix);
    +
    +  // in icu code, it is commented that certain computation need to keep the
    +  // negative sign for 0.
    +  var isNegative = number < 0.0 || number == 0.0 && 1 / number < 0.0;
    +
    +  parts.push(isNegative ? this.negativePrefix_ : this.positivePrefix_);
    +
    +  if (!isFinite(number)) {
    +    parts.push(goog.i18n.NumberFormatSymbols.INFINITY);
    +  } else {
    +    // convert number to non-negative value
    +    number *= isNegative ? -1 : 1;
    +
    +    number *= this.multiplier_;
    +    this.useExponentialNotation_ ?
    +        this.subformatExponential_(number, parts) :
    +        this.subformatFixed_(number, this.minimumIntegerDigits_, parts);
    +  }
    +
    +  parts.push(isNegative ? this.negativeSuffix_ : this.positiveSuffix_);
    +  parts.push(unit.suffix);
    +
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Round a number into an integer and fractional part
    + * based on the rounding rules for this NumberFormat.
    + * @param {number} number The number to round.
    + * @return {{intValue: number, fracValue: number}} The integer and fractional
    + *     part after rounding.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.roundNumber_ = function(number) {
    +  var power = Math.pow(10, this.maximumFractionDigits_);
    +  var shiftedNumber = this.significantDigits_ <= 0 ?
    +      Math.round(number * power) :
    +      Math.round(this.roundToSignificantDigits_(
    +          number * power,
    +          this.significantDigits_,
    +          this.maximumFractionDigits_));
    +
    +  var intValue, fracValue;
    +  if (isFinite(shiftedNumber)) {
    +    intValue = Math.floor(shiftedNumber / power);
    +    fracValue = Math.floor(shiftedNumber - intValue * power);
    +  } else {
    +    intValue = number;
    +    fracValue = 0;
    +  }
    +  return {intValue: intValue, fracValue: fracValue};
    +};
    +
    +
    +/**
    + * Formats a Number in fraction format.
    + *
    + * @param {number} number
    + * @param {number} minIntDigits Minimum integer digits.
    + * @param {Array<string>} parts
    + *     This array holds the pieces of formatted string.
    + *     This function will add its formatted pieces to the array.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.subformatFixed_ =
    +    function(number, minIntDigits, parts) {
    +  if (this.minimumFractionDigits_ > this.maximumFractionDigits_) {
    +    throw Error('Min value must be less than max value');
    +  }
    +
    +  var rounded = this.roundNumber_(number);
    +  var power = Math.pow(10, this.maximumFractionDigits_);
    +  var intValue = rounded.intValue;
    +  var fracValue = rounded.fracValue;
    +
    +  var numIntDigits = (intValue == 0) ? 0 : this.intLog10_(intValue) + 1;
    +  var fractionPresent = this.minimumFractionDigits_ > 0 || fracValue > 0 ||
    +      (this.showTrailingZeros_ && numIntDigits < this.significantDigits_);
    +  var minimumFractionDigits = this.minimumFractionDigits_;
    +  if (fractionPresent) {
    +    if (this.showTrailingZeros_ && this.significantDigits_ > 0) {
    +      minimumFractionDigits = this.significantDigits_ - numIntDigits;
    +    } else {
    +      minimumFractionDigits = this.minimumFractionDigits_;
    +    }
    +  }
    +
    +  var intPart = '';
    +  var translatableInt = intValue;
    +  while (translatableInt > 1E20) {
    +    // here it goes beyond double precision, add '0' make it look better
    +    intPart = '0' + intPart;
    +    translatableInt = Math.round(translatableInt / 10);
    +  }
    +  intPart = translatableInt + intPart;
    +
    +  var decimal = goog.i18n.NumberFormatSymbols.DECIMAL_SEP;
    +  var grouping = goog.i18n.NumberFormatSymbols.GROUP_SEP;
    +  var zeroCode = goog.i18n.NumberFormat.enforceAsciiDigits_ ?
    +                 48  /* ascii '0' */ :
    +                 goog.i18n.NumberFormatSymbols.ZERO_DIGIT.charCodeAt(0);
    +  var digitLen = intPart.length;
    +
    +  if (intValue > 0 || minIntDigits > 0) {
    +    for (var i = digitLen; i < minIntDigits; i++) {
    +      parts.push(String.fromCharCode(zeroCode));
    +    }
    +
    +    for (var i = 0; i < digitLen; i++) {
    +      parts.push(String.fromCharCode(zeroCode + intPart.charAt(i) * 1));
    +
    +      if (digitLen - i > 1 && this.groupingSize_ > 0 &&
    +          ((digitLen - i) % this.groupingSize_ == 1)) {
    +        parts.push(grouping);
    +      }
    +    }
    +  } else if (!fractionPresent) {
    +    // If there is no fraction present, and we haven't printed any
    +    // integer digits, then print a zero.
    +    parts.push(String.fromCharCode(zeroCode));
    +  }
    +
    +  // Output the decimal separator if we always do so.
    +  if (this.decimalSeparatorAlwaysShown_ || fractionPresent) {
    +    parts.push(decimal);
    +  }
    +
    +  var fracPart = '' + (fracValue + power);
    +  var fracLen = fracPart.length;
    +  while (fracPart.charAt(fracLen - 1) == '0' &&
    +      fracLen > minimumFractionDigits + 1) {
    +    fracLen--;
    +  }
    +
    +  for (var i = 1; i < fracLen; i++) {
    +    parts.push(String.fromCharCode(zeroCode + fracPart.charAt(i) * 1));
    +  }
    +};
    +
    +
    +/**
    + * Formats exponent part of a Number.
    + *
    + * @param {number} exponent Exponential value.
    + * @param {Array<string>} parts The array that holds the pieces of formatted
    + *     string. This function will append more formatted pieces to the array.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.addExponentPart_ = function(exponent, parts) {
    +  parts.push(goog.i18n.NumberFormatSymbols.EXP_SYMBOL);
    +
    +  if (exponent < 0) {
    +    exponent = -exponent;
    +    parts.push(goog.i18n.NumberFormatSymbols.MINUS_SIGN);
    +  } else if (this.useSignForPositiveExponent_) {
    +    parts.push(goog.i18n.NumberFormatSymbols.PLUS_SIGN);
    +  }
    +
    +  var exponentDigits = '' + exponent;
    +  var zeroChar = goog.i18n.NumberFormat.enforceAsciiDigits_ ? '0' :
    +                 goog.i18n.NumberFormatSymbols.ZERO_DIGIT;
    +  for (var i = exponentDigits.length; i < this.minExponentDigits_; i++) {
    +    parts.push(zeroChar);
    +  }
    +  parts.push(exponentDigits);
    +};
    +
    +
    +/**
    + * Formats Number in exponential format.
    + *
    + * @param {number} number Value need to be formated.
    + * @param {Array<string>} parts The array that holds the pieces of formatted
    + *     string. This function will append more formatted pieces to the array.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.subformatExponential_ =
    +    function(number, parts) {
    +  if (number == 0.0) {
    +    this.subformatFixed_(number, this.minimumIntegerDigits_, parts);
    +    this.addExponentPart_(0, parts);
    +    return;
    +  }
    +
    +  var exponent = goog.math.safeFloor(Math.log(number) / Math.log(10));
    +  number /= Math.pow(10, exponent);
    +
    +  var minIntDigits = this.minimumIntegerDigits_;
    +  if (this.maximumIntegerDigits_ > 1 &&
    +      this.maximumIntegerDigits_ > this.minimumIntegerDigits_) {
    +    // A repeating range is defined; adjust to it as follows.
    +    // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
    +    // -3,-4,-5=>-6, etc. This takes into account that the
    +    // exponent we have here is off by one from what we expect;
    +    // it is for the format 0.MMMMMx10^n.
    +    while ((exponent % this.maximumIntegerDigits_) != 0) {
    +      number *= 10;
    +      exponent--;
    +    }
    +    minIntDigits = 1;
    +  } else {
    +    // No repeating range is defined; use minimum integer digits.
    +    if (this.minimumIntegerDigits_ < 1) {
    +      exponent++;
    +      number /= 10;
    +    } else {
    +      exponent -= this.minimumIntegerDigits_ - 1;
    +      number *= Math.pow(10, this.minimumIntegerDigits_ - 1);
    +    }
    +  }
    +  this.subformatFixed_(number, minIntDigits, parts);
    +  this.addExponentPart_(exponent, parts);
    +};
    +
    +
    +/**
    + * Returns the digit value of current character. The character could be either
    + * '0' to '9', or a locale specific digit.
    + *
    + * @param {string} ch Character that represents a digit.
    + * @return {number} The digit value, or -1 on error.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.getDigit_ = function(ch) {
    +  var code = ch.charCodeAt(0);
    +  // between '0' to '9'
    +  if (48 <= code && code < 58) {
    +    return code - 48;
    +  } else {
    +    var zeroCode = goog.i18n.NumberFormatSymbols.ZERO_DIGIT.charCodeAt(0);
    +    return zeroCode <= code && code < zeroCode + 10 ? code - zeroCode : -1;
    +  }
    +};
    +
    +
    +// ----------------------------------------------------------------------
    +// CONSTANTS
    +// ----------------------------------------------------------------------
    +// Constants for characters used in programmatic (unlocalized) patterns.
    +/**
    + * A zero digit character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_ = '0';
    +
    +
    +/**
    + * A grouping separator character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_ = ',';
    +
    +
    +/**
    + * A decimal separator character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_ = '.';
    +
    +
    +/**
    + * A per mille character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_PER_MILLE_ = '\u2030';
    +
    +
    +/**
    + * A percent character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_PERCENT_ = '%';
    +
    +
    +/**
    + * A digit character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_DIGIT_ = '#';
    +
    +
    +/**
    + * A separator character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_SEPARATOR_ = ';';
    +
    +
    +/**
    + * An exponent character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_EXPONENT_ = 'E';
    +
    +
    +/**
    + * An plus character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_PLUS_ = '+';
    +
    +
    +/**
    + * A minus character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_MINUS_ = '-';
    +
    +
    +/**
    + * A quote character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_ = '\u00A4';
    +
    +
    +/**
    + * A quote character.
    + * @type {string}
    + * @private
    + */
    +goog.i18n.NumberFormat.QUOTE_ = '\'';
    +
    +
    +/**
    + * Parses affix part of pattern.
    + *
    + * @param {string} pattern Pattern string that need to be parsed.
    + * @param {Array<number>} pos One element position array to set and receive
    + *     parsing position.
    + *
    + * @return {string} Affix received from parsing.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.parseAffix_ = function(pattern, pos) {
    +  var affix = '';
    +  var inQuote = false;
    +  var len = pattern.length;
    +
    +  for (; pos[0] < len; pos[0]++) {
    +    var ch = pattern.charAt(pos[0]);
    +    if (ch == goog.i18n.NumberFormat.QUOTE_) {
    +      if (pos[0] + 1 < len &&
    +          pattern.charAt(pos[0] + 1) == goog.i18n.NumberFormat.QUOTE_) {
    +        pos[0]++;
    +        affix += '\''; // 'don''t'
    +      } else {
    +        inQuote = !inQuote;
    +      }
    +      continue;
    +    }
    +
    +    if (inQuote) {
    +      affix += ch;
    +    } else {
    +      switch (ch) {
    +        case goog.i18n.NumberFormat.PATTERN_DIGIT_:
    +        case goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_:
    +        case goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_:
    +        case goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_:
    +        case goog.i18n.NumberFormat.PATTERN_SEPARATOR_:
    +          return affix;
    +        case goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_:
    +          if ((pos[0] + 1) < len &&
    +              pattern.charAt(pos[0] + 1) ==
    +              goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_) {
    +            pos[0]++;
    +            affix += this.intlCurrencyCode_;
    +          } else {
    +            switch (this.currencyStyle_) {
    +              case goog.i18n.NumberFormat.CurrencyStyle.LOCAL:
    +                affix += goog.i18n.currency.getLocalCurrencySign(
    +                    this.intlCurrencyCode_);
    +                break;
    +              case goog.i18n.NumberFormat.CurrencyStyle.GLOBAL:
    +                affix += goog.i18n.currency.getGlobalCurrencySign(
    +                    this.intlCurrencyCode_);
    +                break;
    +              case goog.i18n.NumberFormat.CurrencyStyle.PORTABLE:
    +                affix += goog.i18n.currency.getPortableCurrencySign(
    +                    this.intlCurrencyCode_);
    +                break;
    +              default:
    +                break;
    +            }
    +          }
    +          break;
    +        case goog.i18n.NumberFormat.PATTERN_PERCENT_:
    +          if (this.multiplier_ != 1) {
    +            throw Error('Too many percent/permill');
    +          }
    +          this.multiplier_ = 100;
    +          affix += goog.i18n.NumberFormatSymbols.PERCENT;
    +          break;
    +        case goog.i18n.NumberFormat.PATTERN_PER_MILLE_:
    +          if (this.multiplier_ != 1) {
    +            throw Error('Too many percent/permill');
    +          }
    +          this.multiplier_ = 1000;
    +          affix += goog.i18n.NumberFormatSymbols.PERMILL;
    +          break;
    +        default:
    +          affix += ch;
    +      }
    +    }
    +  }
    +
    +  return affix;
    +};
    +
    +
    +/**
    + * Parses the trunk part of a pattern.
    + *
    + * @param {string} pattern Pattern string that need to be parsed.
    + * @param {Array<number>} pos One element position array to set and receive
    + *     parsing position.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.parseTrunk_ = function(pattern, pos) {
    +  var decimalPos = -1;
    +  var digitLeftCount = 0;
    +  var zeroDigitCount = 0;
    +  var digitRightCount = 0;
    +  var groupingCount = -1;
    +
    +  var len = pattern.length;
    +  for (var loop = true; pos[0] < len && loop; pos[0]++) {
    +    var ch = pattern.charAt(pos[0]);
    +    switch (ch) {
    +      case goog.i18n.NumberFormat.PATTERN_DIGIT_:
    +        if (zeroDigitCount > 0) {
    +          digitRightCount++;
    +        } else {
    +          digitLeftCount++;
    +        }
    +        if (groupingCount >= 0 && decimalPos < 0) {
    +          groupingCount++;
    +        }
    +        break;
    +      case goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_:
    +        if (digitRightCount > 0) {
    +          throw Error('Unexpected "0" in pattern "' + pattern + '"');
    +        }
    +        zeroDigitCount++;
    +        if (groupingCount >= 0 && decimalPos < 0) {
    +          groupingCount++;
    +        }
    +        break;
    +      case goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_:
    +        groupingCount = 0;
    +        break;
    +      case goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_:
    +        if (decimalPos >= 0) {
    +          throw Error('Multiple decimal separators in pattern "' +
    +                      pattern + '"');
    +        }
    +        decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
    +        break;
    +      case goog.i18n.NumberFormat.PATTERN_EXPONENT_:
    +        if (this.useExponentialNotation_) {
    +          throw Error('Multiple exponential symbols in pattern "' +
    +                      pattern + '"');
    +        }
    +        this.useExponentialNotation_ = true;
    +        this.minExponentDigits_ = 0;
    +
    +        // exponent pattern can have a optional '+'.
    +        if ((pos[0] + 1) < len && pattern.charAt(pos[0] + 1) ==
    +            goog.i18n.NumberFormat.PATTERN_PLUS_) {
    +          pos[0]++;
    +          this.useSignForPositiveExponent_ = true;
    +        }
    +
    +        // Use lookahead to parse out the exponential part
    +        // of the pattern, then jump into phase 2.
    +        while ((pos[0] + 1) < len && pattern.charAt(pos[0] + 1) ==
    +               goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_) {
    +          pos[0]++;
    +          this.minExponentDigits_++;
    +        }
    +
    +        if ((digitLeftCount + zeroDigitCount) < 1 ||
    +            this.minExponentDigits_ < 1) {
    +          throw Error('Malformed exponential pattern "' + pattern + '"');
    +        }
    +        loop = false;
    +        break;
    +      default:
    +        pos[0]--;
    +        loop = false;
    +        break;
    +    }
    +  }
    +
    +  if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {
    +    // Handle '###.###' and '###.' and '.###'
    +    var n = decimalPos;
    +    if (n == 0) { // Handle '.###'
    +      n++;
    +    }
    +    digitRightCount = digitLeftCount - n;
    +    digitLeftCount = n - 1;
    +    zeroDigitCount = 1;
    +  }
    +
    +  // Do syntax checking on the digits.
    +  if (decimalPos < 0 && digitRightCount > 0 ||
    +      decimalPos >= 0 && (decimalPos < digitLeftCount ||
    +                          decimalPos > digitLeftCount + zeroDigitCount) ||
    +      groupingCount == 0) {
    +    throw Error('Malformed pattern "' + pattern + '"');
    +  }
    +  var totalDigits = digitLeftCount + zeroDigitCount + digitRightCount;
    +
    +  this.maximumFractionDigits_ = decimalPos >= 0 ? totalDigits - decimalPos : 0;
    +  if (decimalPos >= 0) {
    +    this.minimumFractionDigits_ = digitLeftCount + zeroDigitCount - decimalPos;
    +    if (this.minimumFractionDigits_ < 0) {
    +      this.minimumFractionDigits_ = 0;
    +    }
    +  }
    +
    +  // The effectiveDecimalPos is the position the decimal is at or would be at
    +  // if there is no decimal. Note that if decimalPos<0, then digitTotalCount ==
    +  // digitLeftCount + zeroDigitCount.
    +  var effectiveDecimalPos = decimalPos >= 0 ? decimalPos : totalDigits;
    +  this.minimumIntegerDigits_ = effectiveDecimalPos - digitLeftCount;
    +  if (this.useExponentialNotation_) {
    +    this.maximumIntegerDigits_ = digitLeftCount + this.minimumIntegerDigits_;
    +
    +    // in exponential display, we need to at least show something.
    +    if (this.maximumFractionDigits_ == 0 && this.minimumIntegerDigits_ == 0) {
    +      this.minimumIntegerDigits_ = 1;
    +    }
    +  }
    +
    +  this.groupingSize_ = Math.max(0, groupingCount);
    +  this.decimalSeparatorAlwaysShown_ = decimalPos == 0 ||
    +                                      decimalPos == totalDigits;
    +};
    +
    +
    +/**
    + * Alias for the compact format 'unit' object.
    + * @typedef {{
    + *     prefix: string,
    + *     suffix: string,
    + *     divisorBase: number
    + * }}
    + */
    +goog.i18n.NumberFormat.CompactNumberUnit;
    +
    +
    +/**
    + * The empty unit, corresponding to a base of 0.
    + * @private {!goog.i18n.NumberFormat.CompactNumberUnit}
    + */
    +goog.i18n.NumberFormat.NULL_UNIT_ = { prefix: '', suffix: '', divisorBase: 0 };
    +
    +
    +/**
    + * Get compact unit for a certain number of digits
    + *
    + * @param {number} base The number of digits to get the unit for.
    + * @param {string} plurality The plurality of the number.
    + * @return {!goog.i18n.NumberFormat.CompactNumberUnit} The compact unit.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.getUnitFor_ = function(base, plurality) {
    +  var table = this.compactStyle_ == goog.i18n.NumberFormat.CompactStyle.SHORT ?
    +      goog.i18n.CompactNumberFormatSymbols.COMPACT_DECIMAL_SHORT_PATTERN :
    +      goog.i18n.CompactNumberFormatSymbols.COMPACT_DECIMAL_LONG_PATTERN;
    +
    +  if (base < 3) {
    +    return goog.i18n.NumberFormat.NULL_UNIT_;
    +  } else {
    +    base = Math.min(14, base);
    +    var patterns = table[Math.pow(10, base)];
    +    if (!patterns) {
    +      return goog.i18n.NumberFormat.NULL_UNIT_;
    +    }
    +
    +    var pattern = patterns[plurality];
    +    if (!pattern || pattern == '0') {
    +      return goog.i18n.NumberFormat.NULL_UNIT_;
    +    }
    +
    +    var parts = /([^0]*)(0+)(.*)/.exec(pattern);
    +    if (!parts) {
    +      return goog.i18n.NumberFormat.NULL_UNIT_;
    +    }
    +
    +    return {
    +      prefix: parts[1],
    +      suffix: parts[3],
    +      divisorBase: base - (parts[2].length - 1)
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Get the compact unit divisor, accounting for rounding of the quantity.
    + *
    + * @param {number} formattingNumber The number to base the formatting on. The
    + *     unit will be calculated from this number.
    + * @param {number} pluralityNumber The number to use for calculating the
    + *     plurality.
    + * @return {!goog.i18n.NumberFormat.CompactNumberUnit} The unit after rounding.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.getUnitAfterRounding_ =
    +    function(formattingNumber, pluralityNumber) {
    +  if (this.compactStyle_ == goog.i18n.NumberFormat.CompactStyle.NONE) {
    +    return goog.i18n.NumberFormat.NULL_UNIT_;
    +  }
    +
    +  formattingNumber = Math.abs(formattingNumber);
    +  pluralityNumber = Math.abs(pluralityNumber);
    +
    +  var initialPlurality = this.pluralForm_(formattingNumber);
    +  // Compute the exponent from the formattingNumber, to compute the unit.
    +  var base = formattingNumber <= 1 ? 0 : this.intLog10_(formattingNumber);
    +  var initialDivisor = this.getUnitFor_(base, initialPlurality).divisorBase;
    +  // Round both numbers based on the unit used.
    +  var pluralityAttempt = pluralityNumber / Math.pow(10, initialDivisor);
    +  var pluralityRounded = this.roundNumber_(pluralityAttempt);
    +  var formattingAttempt = formattingNumber / Math.pow(10, initialDivisor);
    +  var formattingRounded = this.roundNumber_(formattingAttempt);
    +  // Compute the plurality of the pluralityNumber when formatted using the name
    +  // units as the formattingNumber.
    +  var finalPlurality =
    +      this.pluralForm_(pluralityRounded.intValue + pluralityRounded.fracValue);
    +  // Get the final unit, using the rounded formatting number to get the correct
    +  // unit, and the plurality computed from the pluralityNumber.
    +  return this.getUnitFor_(
    +      initialDivisor + this.intLog10_(formattingRounded.intValue),
    +      finalPlurality);
    +};
    +
    +
    +/**
    + * Get the integer base 10 logarithm of a number.
    + *
    + * @param {number} number The number to log.
    + * @return {number} The lowest integer n such that 10^n >= number.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.intLog10_ = function(number) {
    +  // Turns out Math.log(1000000)/Math.LN10 is strictly less than 6.
    +  var i = 0;
    +  while ((number /= 10) >= 1) i++;
    +  return i;
    +};
    +
    +
    +/**
    + * Round to a certain number of significant digits.
    + *
    + * @param {number} number The number to round.
    + * @param {number} significantDigits The number of significant digits
    + *     to round to.
    + * @param {number} scale Treat number as fixed point times 10^scale.
    + * @return {number} The rounded number.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.roundToSignificantDigits_ =
    +    function(number, significantDigits, scale) {
    +  if (!number)
    +    return number;
    +
    +  var digits = this.intLog10_(number);
    +  var magnitude = significantDigits - digits - 1;
    +
    +  // Only round fraction, not (potentially shifted) integers.
    +  if (magnitude < -scale) {
    +    var point = Math.pow(10, scale);
    +    return Math.round(number / point) * point;
    +  }
    +
    +  var power = Math.pow(10, magnitude);
    +  var shifted = Math.round(number * power);
    +  return shifted / power;
    +};
    +
    +
    +/**
    + * Get the plural form of a number.
    + * @param {number} quantity The quantity to find plurality of.
    + * @return {string} One of 'zero', 'one', 'two', 'few', 'many', 'other'.
    + * @private
    + */
    +goog.i18n.NumberFormat.prototype.pluralForm_ = function(quantity) {
    +  /* TODO: Implement */
    +  return 'other';
    +};
    +
    +
    +/**
    + * Checks if the currency symbol comes before the value ($12) or after (12$)
    + * Handy for applications that need to have separate UI fields for the currency
    + * value and symbol, especially for input: Price: [USD] [123.45]
    + * The currency symbol might be a combo box, or a label.
    + *
    + * @return {boolean} true if currency is before value.
    + */
    +goog.i18n.NumberFormat.prototype.isCurrencyCodeBeforeValue = function() {
    +  var posCurrSymbol = this.pattern_.indexOf('\u00A4'); // '¤' Currency sign
    +  var posPound = this.pattern_.indexOf('#');
    +  var posZero = this.pattern_.indexOf('0');
    +
    +  // posCurrValue is the first '#' or '0' found.
    +  // If none of them is found (not possible, but still),
    +  // the result is true (postCurrSymbol < MAX_VALUE)
    +  // That is OK, matches the en_US and ROOT locales.
    +  var posCurrValue = Number.MAX_VALUE;
    +  if (posPound >= 0 && posPound < posCurrValue) {
    +    posCurrValue = posPound;
    +  }
    +  if (posZero >= 0 && posZero < posCurrValue) {
    +    posCurrValue = posZero;
    +  }
    +
    +  // No need to test, it is guaranteed that both these symbols exist.
    +  // If not, we have bigger problems than this.
    +  return posCurrSymbol < posCurrValue;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.html b/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.html
    new file mode 100644
    index 0000000000..0bfbcedfb9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.NumberFormat
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.NumberFormatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.js b/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.js
    new file mode 100644
    index 0000000000..8e368747fa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/numberformat_test.js
    @@ -0,0 +1,1050 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.NumberFormatTest');
    +goog.setTestOnly('goog.i18n.NumberFormatTest');
    +
    +goog.require('goog.i18n.CompactNumberFormatSymbols');
    +goog.require('goog.i18n.CompactNumberFormatSymbols_de');
    +goog.require('goog.i18n.CompactNumberFormatSymbols_en');
    +goog.require('goog.i18n.CompactNumberFormatSymbols_fr');
    +goog.require('goog.i18n.NumberFormat');
    +goog.require('goog.i18n.NumberFormatSymbols');
    +goog.require('goog.i18n.NumberFormatSymbols_de');
    +goog.require('goog.i18n.NumberFormatSymbols_en');
    +goog.require('goog.i18n.NumberFormatSymbols_fr');
    +goog.require('goog.i18n.NumberFormatSymbols_pl');
    +goog.require('goog.i18n.NumberFormatSymbols_ro');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +var expectedFailures;
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  // Always switch back to English on startup.
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +  goog.i18n.CompactNumberFormatSymbols =
    +      goog.i18n.CompactNumberFormatSymbols_en;
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  stubs.reset();
    +}
    +
    +function veryBigNumberCompare(str1, str2) {
    +  return str1.length == str2.length &&
    +         str1.substring(0, 8) == str2.substring(0, 8);
    +}
    +
    +function testVeryBigNumber() {
    +  var str;
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  str = fmt.format(1785599999999999888888888888888);
    +  // when comparing big number, various platform have small different in
    +  // precision. We have to tolerate that using veryBigNumberCompare.
    +  var expected = '$1,785,599,999,999,999,400,000,000,000,000.00';
    +  assertTrue(veryBigNumberCompare(
    +      '$1,785,599,999,999,999,400,000,000,000,000.00', str));
    +  str = fmt.format(1.7856E30);
    +  assertTrue(veryBigNumberCompare(
    +      '$1,785,599,999,999,999,400,000,000,000,000.00', str));
    +  str = fmt.format(1.3456E20);
    +  assertTrue(veryBigNumberCompare('$134,560,000,000,000,000,000.00', str));
    +
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  str = fmt.format(1.3456E20);
    +  assertTrue(veryBigNumberCompare('134,559,999,999,999,980,000', str));
    +
    +
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.PERCENT);
    +  str = fmt.format(1.3456E20);
    +  assertTrue(veryBigNumberCompare('13,456,000,000,000,000,000,000%', str));
    +
    +  fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.SCIENTIFIC);
    +  str = fmt.format(1.3456E20);
    +  assertEquals('1E20', str);
    +
    +  fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.DECIMAL);
    +  str = fmt.format(-1.234567890123456e306);
    +  assertEquals(1 + 1 + 306 + 306 / 3, str.length);
    +  assertEquals('-1,234,567,890,123,45', str.substr(0, 21));
    +
    +  str = fmt.format(Infinity);
    +  assertEquals('\u221e', str);
    +  str = fmt.format(-Infinity);
    +  assertEquals('-\u221e', str);
    +}
    +
    +function testStandardFormat() {
    +  var str;
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.CURRENCY);
    +  str = fmt.format(1234.579);
    +  assertEquals('$1,234.58', str);
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  str = fmt.format(1234.579);
    +  assertEquals('1,234.579', str);
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.PERCENT);
    +  str = fmt.format(1234.579);
    +  assertEquals('123,458%', str);
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.SCIENTIFIC);
    +  str = fmt.format(1234.579);
    +  assertEquals('1E3', str);
    +  // Math.log(1000000)/Math.LN10 is strictly less than 6. Make sure it gets
    +  // formatted correctly.
    +  str = fmt.format(1000000);
    +  assertEquals('1E6', str);
    +}
    +
    +function testNegativePercentage() {
    +  var str;
    +  var fmt = new goog.i18n.NumberFormat('#,##0.00%');
    +  str = fmt.format(-1234.56);
    +  assertEquals('-123,456.00%', str);
    +
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.PERCENT);
    +  str = fmt.format(-1234.579);
    +  assertEquals('-123,458%', str);
    +}
    +
    +function testCustomPercentage() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.PERCENT);
    +  fmt.setMaximumFractionDigits(1);
    +  fmt.setMinimumFractionDigits(1);
    +  var str = fmt.format(0.1291);
    +  assertEquals('12.9%', str);
    +  fmt.setMaximumFractionDigits(2);
    +  fmt.setMinimumFractionDigits(1);
    +  str = fmt.format(0.129);
    +  assertEquals('12.9%', str);
    +  fmt.setMaximumFractionDigits(2);
    +  fmt.setMinimumFractionDigits(1);
    +  str = fmt.format(0.12);
    +  assertEquals('12.0%', str);
    +  fmt.setMaximumFractionDigits(2);
    +  fmt.setMinimumFractionDigits(1);
    +  str = fmt.format(0.12911);
    +  assertEquals('12.91%', str);
    +}
    +
    +function testBasicParse() {
    +  var value;
    +
    +  var fmt = new goog.i18n.NumberFormat('0.0000');
    +  value = fmt.parse('123.4579');
    +  assertEquals(123.4579, value);
    +
    +  value = fmt.parse('+123.4579');
    +  assertEquals(123.4579, value);
    +
    +  value = fmt.parse('-123.4579');
    +  assertEquals(-123.4579, value);
    +}
    +
    +function testPrefixParse() {
    +  var value;
    +
    +  var fmt = new goog.i18n.NumberFormat('0.0;(0.0)');
    +  value = fmt.parse('123.4579');
    +  assertEquals(123.4579, value);
    +
    +  value = fmt.parse('(123.4579)');
    +  assertEquals(-123.4579, value);
    +}
    +
    +function testPrecentParse() {
    +  var value;
    +
    +  var fmt = new goog.i18n.NumberFormat('0.0;(0.0)');
    +  value = fmt.parse('123.4579%');
    +  assertEquals((123.4579 / 100), value);
    +
    +  value = fmt.parse('(%123.4579)');
    +  assertEquals((-123.4579 / 100), value);
    +
    +  value = fmt.parse('123.4579\u2030');
    +  assertEquals((123.4579 / 1000), value);
    +
    +  value = fmt.parse('(\u2030123.4579)');
    +  assertEquals((-123.4579 / 1000), value);
    +}
    +
    +function testPercentAndPerMillAdvance() {
    +  var value;
    +  var pos = [0];
    +  var fmt = new goog.i18n.NumberFormat('0');
    +  value = fmt.parse('120%', pos);
    +  assertEquals(1.2, value);
    +  assertEquals(4, pos[0]);
    +  pos[0] = 0;
    +  value = fmt.parse('120\u2030', pos);
    +  assertEquals(0.12, value);
    +  assertEquals(4, pos[0]);
    +}
    +
    +function testInfinityParse() {
    +  var value;
    +  var fmt = new goog.i18n.NumberFormat('0.0;(0.0)');
    +
    +  // gwt need to add those symbols first
    +  value = fmt.parse('\u221e');
    +  assertEquals(Number.POSITIVE_INFINITY, value);
    +
    +  value = fmt.parse('(\u221e)');
    +  assertEquals(Number.NEGATIVE_INFINITY, value);
    +}
    +function testExponentParse() {
    +  var value;
    +  var fmt;
    +
    +  fmt = new goog.i18n.NumberFormat('#E0');
    +  value = fmt.parse('1.234E3');
    +  assertEquals(1.234E+3, value);
    +
    +  fmt = new goog.i18n.NumberFormat('0.###E0');
    +  value = fmt.parse('1.234E3');
    +  assertEquals(1.234E+3, value);
    +
    +  fmt = new goog.i18n.NumberFormat('#E0');
    +  value = fmt.parse('1.2345E4');
    +  assertEquals(12345.0, value);
    +
    +  value = fmt.parse('1.2345E4');
    +  assertEquals(12345.0, value);
    +
    +  value = fmt.parse('1.2345E+4');
    +  assertEquals(12345.0, value);
    +}
    +
    +function testGroupingParse() {
    +  var value;
    +
    +  var fmt = new goog.i18n.NumberFormat('#,###');
    +  value = fmt.parse('1,234,567,890');
    +  assertEquals(1234567890, value);
    +  value = fmt.parse('12,3456,7890');
    +  assertEquals(1234567890, value);
    +
    +  fmt = new goog.i18n.NumberFormat('#');
    +  value = fmt.parse('1234567890');
    +  assertEquals(1234567890, value);
    +}
    +
    +function testBasicFormat() {
    +  var fmt = new goog.i18n.NumberFormat('0.0000');
    +  var str = fmt.format(123.45789179565757);
    +  assertEquals('123.4579', str);
    +}
    +
    +function testGrouping() {
    +  var str;
    +
    +  var fmt = new goog.i18n.NumberFormat('#,###');
    +  str = fmt.format(1234567890);
    +  assertEquals('1,234,567,890', str);
    +  fmt = new goog.i18n.NumberFormat('#,####');
    +  str = fmt.format(1234567890);
    +  assertEquals('12,3456,7890', str);
    +
    +  fmt = new goog.i18n.NumberFormat('#');
    +  str = fmt.format(1234567890);
    +  assertEquals('1234567890', str);
    +}
    +
    +function testPerMill() {
    +  var str;
    +
    +  var fmt = new goog.i18n.NumberFormat('###.###\u2030');
    +  str = fmt.format(0.4857);
    +  assertEquals('485.7\u2030', str);
    +}
    +
    +function testCurrency() {
    +  var str;
    +
    +  var fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00');
    +  str = fmt.format(1234.56);
    +  assertEquals('$1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('-$1,234.56', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'USD',
    +      goog.i18n.NumberFormat.CurrencyStyle.LOCAL);
    +  str = fmt.format(1234.56);
    +  assertEquals('$1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('-$1,234.56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'USD',
    +      goog.i18n.NumberFormat.CurrencyStyle.PORTABLE);
    +  str = fmt.format(1234.56);
    +  assertEquals('US$1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('-US$1,234.56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'USD',
    +      goog.i18n.NumberFormat.CurrencyStyle.GLOBAL);
    +  str = fmt.format(1234.56);
    +  assertEquals('USD $1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('-USD $1,234.56', str);
    +
    +
    +  fmt = new goog.i18n.NumberFormat(
    +      '\u00a4\u00a4 #,##0.00;-\u00a4\u00a4 #,##0.00');
    +  str = fmt.format(1234.56);
    +  assertEquals('USD 1,234.56', str);
    +  fmt = new goog.i18n.NumberFormat(
    +      '\u00a4\u00a4 #,##0.00;\u00a4\u00a4 -#,##0.00');
    +  str = fmt.format(-1234.56);
    +  assertEquals('USD -1,234.56', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'BRL');
    +  str = fmt.format(1234.56);
    +  assertEquals('R$1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('-R$1,234.56', str);
    +
    +  fmt = new goog.i18n.NumberFormat(
    +      '\u00a4\u00a4 #,##0.00;(\u00a4\u00a4 #,##0.00)', 'BRL');
    +  str = fmt.format(1234.56);
    +  assertEquals('BRL 1,234.56', str);
    +  str = fmt.format(-1234.56);
    +  assertEquals('(BRL 1,234.56)', str);
    +}
    +
    +function testQuotes() {
    +  var str;
    +
    +  var fmt = new goog.i18n.NumberFormat('a\'fo\'\'o\'b#');
    +  str = fmt.format(123);
    +  assertEquals('afo\'ob123', str);
    +
    +  fmt = new goog.i18n.NumberFormat('a\'\'b#');
    +  str = fmt.format(123);
    +  assertEquals('a\'b123', str);
    +
    +  fmt = new goog.i18n.NumberFormat('a\'fo\'\'o\'b#');
    +  str = fmt.format(-123);
    +  assertEquals('afo\'ob-123', str);
    +
    +  fmt = new goog.i18n.NumberFormat('a\'\'b#');
    +  str = fmt.format(-123);
    +  assertEquals('a\'b-123', str);
    +}
    +
    +function testZeros() {
    +  var str;
    +  var fmt;
    +
    +  fmt = new goog.i18n.NumberFormat('#.#');
    +  str = fmt.format(0);
    +  assertEquals('0', str);
    +  fmt = new goog.i18n.NumberFormat('#.');
    +  str = fmt.format(0);
    +  assertEquals('0.', str);
    +  fmt = new goog.i18n.NumberFormat('.#');
    +  str = fmt.format(0);
    +  assertEquals('.0', str);
    +  fmt = new goog.i18n.NumberFormat('#');
    +  str = fmt.format(0);
    +  assertEquals('0', str);
    +
    +  fmt = new goog.i18n.NumberFormat('#0.#');
    +  str = fmt.format(0);
    +  assertEquals('0', str);
    +  fmt = new goog.i18n.NumberFormat('#0.');
    +  str = fmt.format(0);
    +  assertEquals('0.', str);
    +  fmt = new goog.i18n.NumberFormat('#.0');
    +  str = fmt.format(0);
    +  assertEquals('.0', str);
    +  fmt = new goog.i18n.NumberFormat('#');
    +  str = fmt.format(0);
    +  assertEquals('0', str);
    +  fmt = new goog.i18n.NumberFormat('000');
    +  str = fmt.format(0);
    +  assertEquals('000', str);
    +}
    +
    +function testExponential() {
    +  var str;
    +  var fmt;
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(0.01234);
    +  assertEquals('1.234E-2', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(0.01234);
    +  assertEquals('12.340E-03', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(0.01234);
    +  assertEquals('12.34E-003', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(0.01234);
    +  assertEquals('1.234E-2', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(123456789);
    +  assertEquals('1.2346E8', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(123456789);
    +  assertEquals('12.346E07', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(123456789);
    +  assertEquals('123.456789E006', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(123456789);
    +  assertEquals('1.235E8', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(1.23e300);
    +  assertEquals('1.23E300', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(1.23e300);
    +  assertEquals('12.300E299', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(1.23e300);
    +  assertEquals('1.23E300', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(1.23e300);
    +  assertEquals('1.23E300', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(-3.141592653e-271);
    +  assertEquals('-3.1416E-271', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(-3.141592653e-271);
    +  assertEquals('-31.416E-272', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(-3.141592653e-271);
    +  assertEquals('-314.159265E-273', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(-3.141592653e-271);
    +  assertEquals('[3.142E-271]', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(0);
    +  assertEquals('0E0', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(0);
    +  assertEquals('00.000E00', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(0);
    +  assertEquals('0E000', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(0);
    +  assertEquals('0E0', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(-1);
    +  assertEquals('-1E0', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(-1);
    +  assertEquals('-10.000E-01', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(-1);
    +  assertEquals('-1E000', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(-1);
    +  assertEquals('[1E0]', str);
    +
    +  fmt = new goog.i18n.NumberFormat('0.####E0');
    +  str = fmt.format(1);
    +  assertEquals('1E0', str);
    +  fmt = new goog.i18n.NumberFormat('00.000E00');
    +  str = fmt.format(1);
    +  assertEquals('10.000E-01', str);
    +  fmt = new goog.i18n.NumberFormat('##0.######E000');
    +  str = fmt.format(1);
    +  assertEquals('1E000', str);
    +  fmt = new goog.i18n.NumberFormat('0.###E0;[0.###E0]');
    +  str = fmt.format(1);
    +  assertEquals('1E0', str);
    +
    +  fmt = new goog.i18n.NumberFormat('#E0');
    +  str = fmt.format(12345.0);
    +  assertEquals('1E4', str);
    +  fmt = new goog.i18n.NumberFormat('0E0');
    +  str = fmt.format(12345.0);
    +  assertEquals('1E4', str);
    +  fmt = new goog.i18n.NumberFormat('##0.###E0');
    +  str = fmt.format(12345.0);
    +  assertEquals('12.345E3', str);
    +  fmt = new goog.i18n.NumberFormat('##0.###E0');
    +  str = fmt.format(12345.00001);
    +  assertEquals('12.345E3', str);
    +  fmt = new goog.i18n.NumberFormat('##0.###E0');
    +  str = fmt.format(12345);
    +  assertEquals('12.345E3', str);
    +
    +  fmt = new goog.i18n.NumberFormat('##0.####E0');
    +  str = fmt.format(789.12345e-9);
    +  // Firefox 3.6.3 Linux is known to fail here with a rounding error.
    +  // fmt.format will return '789.1234E-9'.
    +  expectedFailures.expectFailureFor(isFirefox363Linux());
    +  try {
    +    assertEquals('789.1235E-9', str);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  fmt = new goog.i18n.NumberFormat('##0.####E0');
    +  str = fmt.format(780.e-9);
    +  assertEquals('780E-9', str);
    +  fmt = new goog.i18n.NumberFormat('.###E0');
    +  str = fmt.format(45678.0);
    +  assertEquals('.457E5', str);
    +  fmt = new goog.i18n.NumberFormat('.###E0');
    +  str = fmt.format(0);
    +  assertEquals('.0E0', str);
    +
    +  fmt = new goog.i18n.NumberFormat('#E0');
    +  str = fmt.format(45678000);
    +  assertEquals('5E7', str);
    +  fmt = new goog.i18n.NumberFormat('##E0');
    +  str = fmt.format(45678000);
    +  assertEquals('46E6', str);
    +  fmt = new goog.i18n.NumberFormat('####E0');
    +  str = fmt.format(45678000);
    +  assertEquals('4568E4', str);
    +  fmt = new goog.i18n.NumberFormat('0E0');
    +  str = fmt.format(45678000);
    +  assertEquals('5E7', str);
    +  fmt = new goog.i18n.NumberFormat('00E0');
    +  str = fmt.format(45678000);
    +  assertEquals('46E6', str);
    +  fmt = new goog.i18n.NumberFormat('000E0');
    +  str = fmt.format(45678000);
    +  assertEquals('457E5', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(0.0000123);
    +  assertEquals('12E-6', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(0.000123);
    +  assertEquals('123E-6', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(0.00123);
    +  assertEquals('1E-3', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(0.0123);
    +  assertEquals('12E-3', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(0.123);
    +  assertEquals('123E-3', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(1.23);
    +  assertEquals('1E0', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(12.3);
    +  assertEquals('12E0', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(123.0);
    +  assertEquals('123E0', str);
    +  fmt = new goog.i18n.NumberFormat('###E0');
    +  str = fmt.format(1230.0);
    +  assertEquals('1E3', str);
    +}
    +
    +function testPlusSignInExponentPart() {
    +  var fmt;
    +  fmt = new goog.i18n.NumberFormat('0E+0');
    +  str = fmt.format(45678000);
    +  assertEquals('5E+7', str);
    +}
    +
    +function testGroupingParse2() {
    +  var value;
    +  var fmt;
    +
    +  fmt = new goog.i18n.NumberFormat('#,###');
    +  value = fmt.parse('1,234,567,890');
    +  assertEquals(1234567890, value);
    +  fmt = new goog.i18n.NumberFormat('#,###');
    +  value = fmt.parse('12,3456,7890');
    +  assertEquals(1234567890, value);
    +
    +  fmt = new goog.i18n.NumberFormat('#');
    +  value = fmt.parse('1234567890');
    +  assertEquals(1234567890, value);
    +}
    +
    +function testApis() {
    +  var fmt;
    +  var str;
    +
    +  fmt = new goog.i18n.NumberFormat('#,###');
    +  str = fmt.format(1234567890);
    +  assertEquals('1,234,567,890', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00');
    +  str = fmt.format(1234.56);
    +  assertEquals('$1,234.56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;(\u00a4#,##0.00)');
    +  str = fmt.format(-1234.56);
    +  assertEquals('($1,234.56)', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'SEK');
    +  str = fmt.format(1234.56);
    +  assertEquals('kr1,234.56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;(\u00a4#,##0.00)', 'SEK');
    +  str = fmt.format(-1234.56);
    +  assertEquals('(kr1,234.56)', str);
    +}
    +
    +function testLocaleSwitch() {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +
    +  // When this test is performed in test cluster, 2 out of 60 machines have
    +  // problem getting the symbol. It is likely to be caused by size of uncompiled
    +  // symbol file. There will not be an issue after it is compiled.
    +  if (goog.i18n.NumberFormatSymbols.DECIMAL_SEP ==
    +      goog.i18n.NumberFormatSymbols_en.DECIMAL_SEP) {
    +    // fails to load French symbols, skip the test.
    +    return;
    +  }
    +
    +  var fmt = new goog.i18n.NumberFormat('#,###');
    +  var str = fmt.format(1234567890);
    +  assertEquals('1\u00a0234\u00a0567\u00a0890', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00');
    +  str = fmt.format(1234.56);
    +  assertEquals('\u20AC1\u00a0234,56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;(\u00a4#,##0.00)');
    +  str = fmt.format(-1234.56);
    +  assertEquals('(\u20AC1\u00a0234,56)', str);
    +
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;-\u00a4#,##0.00', 'SEK');
    +  str = fmt.format(1234.56);
    +  assertEquals('kr1\u00a0234,56', str);
    +  fmt = new goog.i18n.NumberFormat('\u00a4#,##0.00;(\u00a4#,##0.00)', 'SEK');
    +  str = fmt.format(-1234.56);
    +  assertEquals('(kr1\u00a0234,56)', str);
    +}
    +
    +function testFrenchParse() {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +
    +  // When this test is performed in test cluster, 2 out of 60 machines have
    +  // problem getting the symbol. It is likely to be caused by size of uncompiled
    +  // symbol file. There will not be an issue after it is compiled.
    +  if (goog.i18n.NumberFormatSymbols.DECIMAL_SEP ==
    +      goog.i18n.NumberFormatSymbols_en.DECIMAL_SEP) {
    +    // fails to load French symbols, skip the test.
    +    return;
    +  }
    +
    +  var fmt = new goog.i18n.NumberFormat('0.0000');
    +  var value = fmt.parse('0,30');
    +  assertEquals(0.30, value);
    +
    +  fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  value = fmt.parse('0,30\u00A0\u20AC');
    +  assertEquals(0.30, value);
    +  fmt = new goog.i18n.NumberFormat('#,##0.00');
    +  value = fmt.parse('123 456,99');
    +  assertEquals(123456.99, value);
    +
    +  fmt = new goog.i18n.NumberFormat('#,##0.00');
    +  value = fmt.parse('123\u00a0456,99');
    +  assertEquals(123456.99, value);
    +
    +  fmt = new goog.i18n.NumberFormat('#,##0.00');
    +  value = fmt.parse('8 123\u00a0456,99');
    +  assertEquals(8123456.99, value);
    +}
    +
    +
    +function testFailParseShouldThrow() {
    +  var fmt = new goog.i18n.NumberFormat('0.0000');
    +  var value = fmt.parse('x');
    +  assertNaN(value);
    +
    +  fmt = new goog.i18n.NumberFormat('0.000x');
    +  value = fmt.parse('3y');
    +  assertNaN(value);
    +
    +  fmt = new goog.i18n.NumberFormat('x0.000');
    +  value = fmt.parse('y3');
    +  assertNaN(value);
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on Linux Firefox 3.6.3.
    + */
    +function isFirefox363Linux() {
    +  return goog.userAgent.product.FIREFOX && goog.userAgent.LINUX &&
    +      goog.userAgent.product.isVersion('3.6.3') &&
    +      !goog.userAgent.product.isVersion('3.6.4');
    +}
    +
    +
    +function testEnforceAscii() {
    +  try {
    +    goog.i18n.NumberFormatSymbols.ZERO_DIGIT = 'A';
    +    var fmt = new goog.i18n.NumberFormat('0.0000');
    +    var str = fmt.format(123.45789179565757);
    +    assertEquals('BCD.EFHJ', str);
    +    goog.i18n.NumberFormat.setEnforceAsciiDigits(true);
    +    str = fmt.format(123.45789179565757);
    +    assertEquals('123.4579', str);
    +    goog.i18n.NumberFormat.setEnforceAsciiDigits(false);
    +    str = fmt.format(123.45789179565757);
    +    assertEquals('BCD.EFHJ', str);
    +  } finally {
    +    goog.i18n.NumberFormatSymbols.ZERO_DIGIT = '0';
    +  }
    +}
    +
    +function testFractionDigits() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setMinimumFractionDigits(4);
    +  fmt.setMaximumFractionDigits(6);
    +  assertEquals('0.1230', fmt.format(0.123));
    +  assertEquals('0.123456', fmt.format(0.123456));
    +  assertEquals('0.123457', fmt.format(0.12345678));
    +}
    +
    +function testFractionDigitsSetOutOfOrder() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  // First, setup basic min/max
    +  fmt.setMinimumFractionDigits(2);
    +  fmt.setMaximumFractionDigits(2);
    +  // Now change to a lower min & max, but change the max value first so that it
    +  // is temporarily less than the current "min" value.  This makes sure that we
    +  // don't throw an error.
    +  fmt.setMaximumFractionDigits(1);
    +  fmt.setMinimumFractionDigits(1);
    +  assertEquals('2.3', fmt.format(2.34));
    +}
    +
    +function testFractionDigitsInvalid() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setMinimumFractionDigits(2);
    +  fmt.setMaximumFractionDigits(1);
    +  try {
    +    fmt.format(0.123);
    +    fail('Should have thrown exception.');
    +  } catch (e) {}
    +}
    +
    +function testSignificantDigitsEqualToMax() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setMinimumFractionDigits(0);
    +  fmt.setMaximumFractionDigits(2);
    +
    +  fmt.setSignificantDigits(2);
    +  assertEquals('123', fmt.format(123.4));
    +  assertEquals('12', fmt.format(12.34));
    +  assertEquals('1.2', fmt.format(1.234));
    +  assertEquals('0.12', fmt.format(0.1234));
    +  assertEquals('0.13', fmt.format(0.1284));
    +}
    +
    +function testSignificantDigitsLessThanMax() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setMinimumFractionDigits(0);
    +  fmt.setMaximumFractionDigits(4);
    +  fmt.setSignificantDigits(1);
    +
    +  assertEquals('123', fmt.format(123.4));
    +  assertEquals('12', fmt.format(12.34));
    +  assertEquals('1', fmt.format(1.234));
    +  assertEquals('0.1', fmt.format(0.1234));
    +  assertEquals('0.2', fmt.format(0.1834));
    +}
    +
    +function testSignificantDigitsMoreThanMax() {
    +  // Max fractional digits should be absolute
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setMinimumFractionDigits(0);
    +  fmt.setMaximumFractionDigits(2);
    +  fmt.setSignificantDigits(3);
    +
    +  assertEquals('123', fmt.format(123.4));
    +  assertEquals('12.3', fmt.format(12.34));
    +  assertEquals('1.23', fmt.format(1.234));
    +  assertEquals('0.12', fmt.format(0.1234));
    +  assertEquals('0.13', fmt.format(0.1284));
    +}
    +
    +function testSimpleCompactFrench() {
    +  // Switch to French.
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +  goog.i18n.CompactNumberFormatSymbols =
    +      goog.i18n.CompactNumberFormatSymbols_fr;
    +
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(123400000);
    +  assertEquals('123\u00A0M', str);
    +}
    +
    +function testSimpleCompactGerman() {
    +  // Switch to German.
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de;
    +  goog.i18n.CompactNumberFormatSymbols =
    +      goog.i18n.CompactNumberFormatSymbols_de;
    +
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  // The german short compact decimal has a simple '0' for 1000's, which is
    +  // supposed to be interpreted as 'leave the number as-is'.
    +  // (The number itself will still be formatted with the '.', but no rounding)
    +  var str = fmt.format(1234);
    +  assertEquals('1,2\u00A0Tsd.', str);
    +}
    +
    +function testSimpleCompact1() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(1234);
    +  assertEquals('1.2K', str);
    +}
    +
    +function testSimpleCompact2() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(12345);
    +  assertEquals('12K', str);
    +}
    +
    +function testRoundingCompact() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(999999);
    +  assertEquals('1M', str); //as opposed to 1000k
    +}
    +
    +function testRoundingCompactNegative() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(-999999);
    +  assertEquals('-1M', str);
    +}
    +
    +function testCompactSmall() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  var str = fmt.format(0.1234);
    +  assertEquals('0.12', str);
    +}
    +
    +function testCompactLong() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_LONG);
    +
    +  var str = fmt.format(12345);
    +  assertEquals('12 thousand', str);
    +}
    +
    +function testCompactWithoutSignificant() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +  fmt.setSignificantDigits(0);
    +  fmt.setMinimumFractionDigits(2);
    +  fmt.setMaximumFractionDigits(2);
    +
    +  assertEquals('1.23K', fmt.format(1234));
    +  assertEquals('1.00K', fmt.format(1000));
    +  assertEquals('123.46K', fmt.format(123456.7));
    +  assertEquals('999.99K', fmt.format(999994));
    +  assertEquals('1.00M', fmt.format(999995));
    +}
    +
    +function testCompactWithoutSignificant2() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +  fmt.setSignificantDigits(0);
    +  fmt.setMinimumFractionDigits(0);
    +  fmt.setMaximumFractionDigits(2);
    +
    +  assertEquals('1.23K', fmt.format(1234));
    +  assertEquals('1K', fmt.format(1000));
    +  assertEquals('123.46K', fmt.format(123456.7));
    +  assertEquals('999.99K', fmt.format(999994));
    +  assertEquals('1M', fmt.format(999995));
    +}
    +
    +function testShowTrailingZerosWithSignificantDigits() {
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.DECIMAL);
    +  fmt.setSignificantDigits(2);
    +  fmt.setShowTrailingZeros(true);
    +
    +  assertEquals('2.0', fmt.format(2));
    +  assertEquals('2,000', fmt.format(2000));
    +  assertEquals('0.20', fmt.format(0.2));
    +  assertEquals('0.02', fmt.format(0.02));
    +  assertEquals('0.002', fmt.format(0.002));
    +  assertEquals('0.00', fmt.format(0));
    +
    +  fmt.setShowTrailingZeros(false);
    +  assertEquals('2', fmt.format(2));
    +  assertEquals('0.2', fmt.format(0.2));
    +}
    +
    +
    +function testShowTrailingZerosWithSignificantDigitsCompactShort() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +  fmt.setSignificantDigits(2);
    +  fmt.setShowTrailingZeros(true);
    +
    +  assertEquals('2.0', fmt.format(2));
    +  assertEquals('2.0K', fmt.format(2000));
    +  assertEquals('20', fmt.format(20));
    +}
    +
    +function testCurrencyCodeOrder() {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +  goog.i18n.CompactNumberFormatSymbols =
    +      goog.i18n.CompactNumberFormatSymbols_fr;
    +  var fmt = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +  goog.i18n.CompactNumberFormatSymbols =
    +      goog.i18n.CompactNumberFormatSymbols_en;
    +  var fmt1 = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  assertTrue(fmt1.isCurrencyCodeBeforeValue());
    +
    +  // Check that we really have different formatters with different patterns
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +
    +  // Using custom patterns instead of standard locale ones
    +
    +  fmt = new goog.i18n.NumberFormat('\u00A4 #0');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('\u00A4 0 and #');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('#0 \u00A4');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('0 and # \u00A4');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('\u00A4 0');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('0 \u00A4');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('\u00A4 #');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('# \u00A4');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +
    +  // Edge cases, should never happen (like #0 separated by currency symbol,
    +  // or missing currency symbol, or missing both # and 0, or missing all)
    +  // We still make sure we get reasonable results (as much as possible)
    +
    +  fmt = new goog.i18n.NumberFormat('0 \u00A4 #');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('# \u00A4 0');
    +  assertFalse(fmt.isCurrencyCodeBeforeValue());
    +
    +  fmt = new goog.i18n.NumberFormat('\u00A4');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +
    +  fmt = new goog.i18n.NumberFormat('0');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +
    +  fmt = new goog.i18n.NumberFormat('#');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +
    +  fmt = new goog.i18n.NumberFormat('#0');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +
    +  fmt = new goog.i18n.NumberFormat('0 and #');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +
    +  fmt = new goog.i18n.NumberFormat('nothing');
    +  assertTrue(fmt.isCurrencyCodeBeforeValue());  // currency first, en_US style
    +}
    +
    +function testCompactWithBaseFormattingNumber() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +
    +  fmt.setBaseFormatting(1000);
    +  assertEquals('0.8K', fmt.format(800, 1000));
    +
    +  fmt.setBaseFormatting(null);
    +  assertEquals('800', fmt.format(800, 1000));
    +  fmt.setBaseFormatting(1000);
    +  assertEquals('1,200K', fmt.format(1200000, 1000));
    +  assertEquals('0.01K', fmt.format(10, 1000));
    +  fmt.setSignificantDigits(0);
    +  fmt.setMinimumFractionDigits(2);
    +  assertEquals('0.00K', fmt.format(1, 1000));
    +}
    +
    +function testCompactWithBaseFormattingFrench() {
    +  // Switch to French.
    +  stubs.set(goog.i18n, 'NumberFormatSymbols', goog.i18n.NumberFormatSymbols_fr);
    +  stubs.set(goog.i18n, 'CompactNumberFormatSymbols',
    +            goog.i18n.CompactNumberFormatSymbols_fr);
    +
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +  assertEquals('123\u00A0M', fmt.format(123400000));
    +  fmt.setBaseFormatting(1000);
    +  assertEquals('123\u00A0400\u00A0k', fmt.format(123400000));
    +
    +}
    +
    +function testGetBaseFormattingNumber() {
    +  var fmt = new goog.i18n.NumberFormat(
    +      goog.i18n.NumberFormat.Format.COMPACT_SHORT);
    +  assertEquals(null, fmt.getBaseFormatting());
    +  fmt.setBaseFormatting(10000);
    +  assertEquals(10000, fmt.getBaseFormatting());
    +}
    +
    +// Moved Polish, Romanian, other currencies to tier 2, check that it works now
    +function testPolish() {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pl;
    +  var fmPl = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ro;
    +  var fmRo = new goog.i18n.NumberFormat(goog.i18n.NumberFormat.Format.CURRENCY);
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +
    +  assertEquals('100.00\u00A0z\u0142', fmPl.format(100)); // 100.00 zł
    +  assertEquals('100.00\u00A0RON', fmRo.format(100));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbols.js b/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbols.js
    new file mode 100644
    index 0000000000..3f4296497e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbols.js
    @@ -0,0 +1,4139 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Number formatting symbols.
    + *
    + * This file is autogenerated by script:
    + * http://go/generate_number_constants.py
    + * using the --for_closure flag.
    + * File generated from CLDR ver. 26
    + *
    + * To reduce the file size (which may cause issues in some JS
    + * developing environments), this file will only contain locales
    + * that are frequently used by web applications. This is defined as
    + * closure_tier1_locales and will change (most likely addition)
    + * over time.  Rest of the data can be found in another file named
    + * "numberformatsymbolsext.js", which will be generated at the
    + * same time together with this file.
    + *
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could fix CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.i18n.NumberFormatSymbols');
    +goog.provide('goog.i18n.NumberFormatSymbols_af');
    +goog.provide('goog.i18n.NumberFormatSymbols_af_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_am');
    +goog.provide('goog.i18n.NumberFormatSymbols_am_ET');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_001');
    +goog.provide('goog.i18n.NumberFormatSymbols_az');
    +goog.provide('goog.i18n.NumberFormatSymbols_az_Latn_AZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_bg');
    +goog.provide('goog.i18n.NumberFormatSymbols_bg_BG');
    +goog.provide('goog.i18n.NumberFormatSymbols_bn');
    +goog.provide('goog.i18n.NumberFormatSymbols_bn_BD');
    +goog.provide('goog.i18n.NumberFormatSymbols_br');
    +goog.provide('goog.i18n.NumberFormatSymbols_br_FR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca_AD');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca_ES');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca_ES_VALENCIA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca_FR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ca_IT');
    +goog.provide('goog.i18n.NumberFormatSymbols_chr');
    +goog.provide('goog.i18n.NumberFormatSymbols_chr_US');
    +goog.provide('goog.i18n.NumberFormatSymbols_cs');
    +goog.provide('goog.i18n.NumberFormatSymbols_cs_CZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_cy');
    +goog.provide('goog.i18n.NumberFormatSymbols_cy_GB');
    +goog.provide('goog.i18n.NumberFormatSymbols_da');
    +goog.provide('goog.i18n.NumberFormatSymbols_da_DK');
    +goog.provide('goog.i18n.NumberFormatSymbols_da_GL');
    +goog.provide('goog.i18n.NumberFormatSymbols_de');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_AT');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_BE');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_DE');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_LU');
    +goog.provide('goog.i18n.NumberFormatSymbols_el');
    +goog.provide('goog.i18n.NumberFormatSymbols_el_GR');
    +goog.provide('goog.i18n.NumberFormatSymbols_en');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_001');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_AS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_AU');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_DG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_FM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GB');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GU');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_IE');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_IO');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MH');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MP');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PR');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PW');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TC');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_UM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_US');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_VG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_VI');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_ZW');
    +goog.provide('goog.i18n.NumberFormatSymbols_es');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_419');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_EA');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_ES');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_IC');
    +goog.provide('goog.i18n.NumberFormatSymbols_et');
    +goog.provide('goog.i18n.NumberFormatSymbols_et_EE');
    +goog.provide('goog.i18n.NumberFormatSymbols_eu');
    +goog.provide('goog.i18n.NumberFormatSymbols_eu_ES');
    +goog.provide('goog.i18n.NumberFormatSymbols_fa');
    +goog.provide('goog.i18n.NumberFormatSymbols_fa_IR');
    +goog.provide('goog.i18n.NumberFormatSymbols_fi');
    +goog.provide('goog.i18n.NumberFormatSymbols_fi_FI');
    +goog.provide('goog.i18n.NumberFormatSymbols_fil');
    +goog.provide('goog.i18n.NumberFormatSymbols_fil_PH');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_BL');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CA');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_FR');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_GF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_GP');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MC');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_PM');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_RE');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_YT');
    +goog.provide('goog.i18n.NumberFormatSymbols_ga');
    +goog.provide('goog.i18n.NumberFormatSymbols_ga_IE');
    +goog.provide('goog.i18n.NumberFormatSymbols_gl');
    +goog.provide('goog.i18n.NumberFormatSymbols_gl_ES');
    +goog.provide('goog.i18n.NumberFormatSymbols_gsw');
    +goog.provide('goog.i18n.NumberFormatSymbols_gsw_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_gsw_LI');
    +goog.provide('goog.i18n.NumberFormatSymbols_gu');
    +goog.provide('goog.i18n.NumberFormatSymbols_gu_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_haw');
    +goog.provide('goog.i18n.NumberFormatSymbols_haw_US');
    +goog.provide('goog.i18n.NumberFormatSymbols_he');
    +goog.provide('goog.i18n.NumberFormatSymbols_he_IL');
    +goog.provide('goog.i18n.NumberFormatSymbols_hi');
    +goog.provide('goog.i18n.NumberFormatSymbols_hi_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_hr');
    +goog.provide('goog.i18n.NumberFormatSymbols_hr_HR');
    +goog.provide('goog.i18n.NumberFormatSymbols_hu');
    +goog.provide('goog.i18n.NumberFormatSymbols_hu_HU');
    +goog.provide('goog.i18n.NumberFormatSymbols_hy');
    +goog.provide('goog.i18n.NumberFormatSymbols_hy_AM');
    +goog.provide('goog.i18n.NumberFormatSymbols_id');
    +goog.provide('goog.i18n.NumberFormatSymbols_id_ID');
    +goog.provide('goog.i18n.NumberFormatSymbols_in');
    +goog.provide('goog.i18n.NumberFormatSymbols_is');
    +goog.provide('goog.i18n.NumberFormatSymbols_is_IS');
    +goog.provide('goog.i18n.NumberFormatSymbols_it');
    +goog.provide('goog.i18n.NumberFormatSymbols_it_IT');
    +goog.provide('goog.i18n.NumberFormatSymbols_it_SM');
    +goog.provide('goog.i18n.NumberFormatSymbols_iw');
    +goog.provide('goog.i18n.NumberFormatSymbols_ja');
    +goog.provide('goog.i18n.NumberFormatSymbols_ja_JP');
    +goog.provide('goog.i18n.NumberFormatSymbols_ka');
    +goog.provide('goog.i18n.NumberFormatSymbols_ka_GE');
    +goog.provide('goog.i18n.NumberFormatSymbols_kk');
    +goog.provide('goog.i18n.NumberFormatSymbols_kk_Cyrl_KZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_km');
    +goog.provide('goog.i18n.NumberFormatSymbols_km_KH');
    +goog.provide('goog.i18n.NumberFormatSymbols_kn');
    +goog.provide('goog.i18n.NumberFormatSymbols_kn_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ko');
    +goog.provide('goog.i18n.NumberFormatSymbols_ko_KR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ky');
    +goog.provide('goog.i18n.NumberFormatSymbols_ky_Cyrl_KG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ln');
    +goog.provide('goog.i18n.NumberFormatSymbols_ln_CD');
    +goog.provide('goog.i18n.NumberFormatSymbols_lo');
    +goog.provide('goog.i18n.NumberFormatSymbols_lo_LA');
    +goog.provide('goog.i18n.NumberFormatSymbols_lt');
    +goog.provide('goog.i18n.NumberFormatSymbols_lt_LT');
    +goog.provide('goog.i18n.NumberFormatSymbols_lv');
    +goog.provide('goog.i18n.NumberFormatSymbols_lv_LV');
    +goog.provide('goog.i18n.NumberFormatSymbols_mk');
    +goog.provide('goog.i18n.NumberFormatSymbols_mk_MK');
    +goog.provide('goog.i18n.NumberFormatSymbols_ml');
    +goog.provide('goog.i18n.NumberFormatSymbols_ml_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_mn');
    +goog.provide('goog.i18n.NumberFormatSymbols_mn_Cyrl_MN');
    +goog.provide('goog.i18n.NumberFormatSymbols_mr');
    +goog.provide('goog.i18n.NumberFormatSymbols_mr_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ms');
    +goog.provide('goog.i18n.NumberFormatSymbols_ms_Latn_MY');
    +goog.provide('goog.i18n.NumberFormatSymbols_mt');
    +goog.provide('goog.i18n.NumberFormatSymbols_mt_MT');
    +goog.provide('goog.i18n.NumberFormatSymbols_my');
    +goog.provide('goog.i18n.NumberFormatSymbols_my_MM');
    +goog.provide('goog.i18n.NumberFormatSymbols_nb');
    +goog.provide('goog.i18n.NumberFormatSymbols_nb_NO');
    +goog.provide('goog.i18n.NumberFormatSymbols_nb_SJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ne');
    +goog.provide('goog.i18n.NumberFormatSymbols_ne_NP');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_NL');
    +goog.provide('goog.i18n.NumberFormatSymbols_no');
    +goog.provide('goog.i18n.NumberFormatSymbols_no_NO');
    +goog.provide('goog.i18n.NumberFormatSymbols_or');
    +goog.provide('goog.i18n.NumberFormatSymbols_or_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_pa');
    +goog.provide('goog.i18n.NumberFormatSymbols_pa_Guru_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_pl');
    +goog.provide('goog.i18n.NumberFormatSymbols_pl_PL');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_BR');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_PT');
    +goog.provide('goog.i18n.NumberFormatSymbols_ro');
    +goog.provide('goog.i18n.NumberFormatSymbols_ro_RO');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_RU');
    +goog.provide('goog.i18n.NumberFormatSymbols_si');
    +goog.provide('goog.i18n.NumberFormatSymbols_si_LK');
    +goog.provide('goog.i18n.NumberFormatSymbols_sk');
    +goog.provide('goog.i18n.NumberFormatSymbols_sk_SK');
    +goog.provide('goog.i18n.NumberFormatSymbols_sl');
    +goog.provide('goog.i18n.NumberFormatSymbols_sl_SI');
    +goog.provide('goog.i18n.NumberFormatSymbols_sq');
    +goog.provide('goog.i18n.NumberFormatSymbols_sq_AL');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Cyrl_RS');
    +goog.provide('goog.i18n.NumberFormatSymbols_sv');
    +goog.provide('goog.i18n.NumberFormatSymbols_sv_SE');
    +goog.provide('goog.i18n.NumberFormatSymbols_sw');
    +goog.provide('goog.i18n.NumberFormatSymbols_sw_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ta');
    +goog.provide('goog.i18n.NumberFormatSymbols_ta_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_te');
    +goog.provide('goog.i18n.NumberFormatSymbols_te_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_th');
    +goog.provide('goog.i18n.NumberFormatSymbols_th_TH');
    +goog.provide('goog.i18n.NumberFormatSymbols_tl');
    +goog.provide('goog.i18n.NumberFormatSymbols_tr');
    +goog.provide('goog.i18n.NumberFormatSymbols_tr_TR');
    +goog.provide('goog.i18n.NumberFormatSymbols_uk');
    +goog.provide('goog.i18n.NumberFormatSymbols_uk_UA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ur');
    +goog.provide('goog.i18n.NumberFormatSymbols_ur_PK');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Latn_UZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_vi');
    +goog.provide('goog.i18n.NumberFormatSymbols_vi_VN');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_CN');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_HK');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hans_CN');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_TW');
    +goog.provide('goog.i18n.NumberFormatSymbols_zu');
    +goog.provide('goog.i18n.NumberFormatSymbols_zu_ZA');
    +
    +
    +/**
    + * Number formatting symbols for locale af.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_af = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale af_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_af_ZA = goog.i18n.NumberFormatSymbols_af;
    +
    +
    +/**
    + * Number formatting symbols for locale am.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_am = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ETB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale am_ET.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_am_ET = goog.i18n.NumberFormatSymbols_am;
    +
    +
    +/**
    + * Number formatting symbols for locale ar.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EGP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_001.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_001 = goog.i18n.NumberFormatSymbols_ar;
    +
    +
    +/**
    + * Number formatting symbols for locale az.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_az = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'AZN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale az_Latn_AZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_az_Latn_AZ = goog.i18n.NumberFormatSymbols_az;
    +
    +
    +/**
    + * Number formatting symbols for locale bg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bg = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BGN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bg_BG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bg_BG = goog.i18n.NumberFormatSymbols_bg;
    +
    +
    +/**
    + * Number formatting symbols for locale bn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u09E6',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u09B8\u0982\u0996\u09CD\u09AF\u09BE\u00A0\u09A8\u09BE',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '#,##,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'BDT'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bn_BD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bn_BD = goog.i18n.NumberFormatSymbols_bn;
    +
    +
    +/**
    + * Number formatting symbols for locale br.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_br = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale br_FR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_br_FR = goog.i18n.NumberFormatSymbols_br;
    +
    +
    +/**
    + * Number formatting symbols for locale ca.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ca_AD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca_AD = goog.i18n.NumberFormatSymbols_ca;
    +
    +
    +/**
    + * Number formatting symbols for locale ca_ES.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca_ES = goog.i18n.NumberFormatSymbols_ca;
    +
    +
    +/**
    + * Number formatting symbols for locale ca_ES_VALENCIA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca_ES_VALENCIA = goog.i18n.NumberFormatSymbols_ca;
    +
    +
    +/**
    + * Number formatting symbols for locale ca_FR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca_FR = goog.i18n.NumberFormatSymbols_ca;
    +
    +
    +/**
    + * Number formatting symbols for locale ca_IT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ca_IT = goog.i18n.NumberFormatSymbols_ca;
    +
    +
    +/**
    + * Number formatting symbols for locale chr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_chr = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale chr_US.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_chr_US = goog.i18n.NumberFormatSymbols_chr;
    +
    +
    +/**
    + * Number formatting symbols for locale cs.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cs = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CZK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale cs_CZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cs_CZ = goog.i18n.NumberFormatSymbols_cs;
    +
    +
    +/**
    + * Number formatting symbols for locale cy.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cy = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale cy_GB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cy_GB = goog.i18n.NumberFormatSymbols_cy;
    +
    +
    +/**
    + * Number formatting symbols for locale da.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_da = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'DKK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale da_DK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_da_DK = goog.i18n.NumberFormatSymbols_da;
    +
    +
    +/**
    + * Number formatting symbols for locale da_GL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_da_GL = goog.i18n.NumberFormatSymbols_da;
    +
    +
    +/**
    + * Number formatting symbols for locale de.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale de_AT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_AT = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale de_BE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_BE = goog.i18n.NumberFormatSymbols_de;
    +
    +
    +/**
    + * Number formatting symbols for locale de_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_CH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\'',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale de_DE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_DE = goog.i18n.NumberFormatSymbols_de;
    +
    +
    +/**
    + * Number formatting symbols for locale de_LU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_LU = goog.i18n.NumberFormatSymbols_de;
    +
    +
    +/**
    + * Number formatting symbols for locale el.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_el = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'e',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale el_GR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_el_GR = goog.i18n.NumberFormatSymbols_el;
    +
    +
    +/**
    + * Number formatting symbols for locale en.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_001.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_001 = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_AS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_AS = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_AU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_AU = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_DG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_DG = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_FM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_FM = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_GB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GB = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GU = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_IE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_IE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_IN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_IO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_IO = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_MH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MH = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_MP.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MP = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_PR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PR = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_PW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PW = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_SG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SGD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TC = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_UM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_UM = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_US.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_US = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_VG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_VG = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_VI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_VI = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale en_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_ZA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_ZW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_ZW = goog.i18n.NumberFormatSymbols_en;
    +
    +
    +/**
    + * Number formatting symbols for locale es.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_419.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_419 = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MXN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_EA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_EA = goog.i18n.NumberFormatSymbols_es;
    +
    +
    +/**
    + * Number formatting symbols for locale es_ES.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_ES = goog.i18n.NumberFormatSymbols_es;
    +
    +
    +/**
    + * Number formatting symbols for locale es_IC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_IC = goog.i18n.NumberFormatSymbols_es;
    +
    +
    +/**
    + * Number formatting symbols for locale et.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_et = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale et_EE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_et_EE = goog.i18n.NumberFormatSymbols_et;
    +
    +
    +/**
    + * Number formatting symbols for locale eu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_eu = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '%\u00A0#,##0',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale eu_ES.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_eu_ES = goog.i18n.NumberFormatSymbols_eu;
    +
    +
    +/**
    + * Number formatting symbols for locale fa.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fa = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E\u2212',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0646\u0627\u0639\u062F\u062F',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u200E\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'IRR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fa_IR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fa_IR = goog.i18n.NumberFormatSymbols_fa;
    +
    +
    +/**
    + * Number formatting symbols for locale fi.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fi = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'ep\u00E4luku',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fi_FI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fi_FI = goog.i18n.NumberFormatSymbols_fi;
    +
    +
    +/**
    + * Number formatting symbols for locale fil.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fil = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PHP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fil_PH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fil_PH = goog.i18n.NumberFormatSymbols_fil;
    +
    +
    +/**
    + * Number formatting symbols for locale fr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_BL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_BL = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_FR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_FR = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_GF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_GF = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_GP.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_GP = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MC = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MF = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MQ = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_PM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_PM = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_RE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_RE = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_YT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_YT = goog.i18n.NumberFormatSymbols_fr;
    +
    +
    +/**
    + * Number formatting symbols for locale ga.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ga = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ga_IE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ga_IE = goog.i18n.NumberFormatSymbols_ga;
    +
    +
    +/**
    + * Number formatting symbols for locale gl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale gl_ES.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gl_ES = goog.i18n.NumberFormatSymbols_gl;
    +
    +
    +/**
    + * Number formatting symbols for locale gsw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gsw = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u2019',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale gsw_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gsw_CH = goog.i18n.NumberFormatSymbols_gsw;
    +
    +
    +/**
    + * Number formatting symbols for locale gsw_LI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gsw_LI = goog.i18n.NumberFormatSymbols_gsw;
    +
    +
    +/**
    + * Number formatting symbols for locale gu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gu = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '[#E0]',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale gu_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gu_IN = goog.i18n.NumberFormatSymbols_gu;
    +
    +
    +/**
    + * Number formatting symbols for locale haw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_haw = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale haw_US.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_haw_US = goog.i18n.NumberFormatSymbols_haw;
    +
    +
    +/**
    + * Number formatting symbols for locale he.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_he = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'ILS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale he_IL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_he_IL = goog.i18n.NumberFormatSymbols_he;
    +
    +
    +/**
    + * Number formatting symbols for locale hi.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hi = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '[#E0]',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hi_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hi_IN = goog.i18n.NumberFormatSymbols_hi;
    +
    +
    +/**
    + * Number formatting symbols for locale hr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hr = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'HRK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hr_HR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hr_HR = goog.i18n.NumberFormatSymbols_hr;
    +
    +
    +/**
    + * Number formatting symbols for locale hu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hu = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'HUF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hu_HU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hu_HU = goog.i18n.NumberFormatSymbols_hu;
    +
    +
    +/**
    + * Number formatting symbols for locale hy.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hy = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#0%',
    +  CURRENCY_PATTERN: '#0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'AMD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hy_AM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hy_AM = goog.i18n.NumberFormatSymbols_hy;
    +
    +
    +/**
    + * Number formatting symbols for locale id.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_id = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'IDR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale id_ID.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_id_ID = goog.i18n.NumberFormatSymbols_id;
    +
    +
    +/**
    + * Number formatting symbols for locale in.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_in = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'IDR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale is.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_is = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'ISK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale is_IS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_is_IS = goog.i18n.NumberFormatSymbols_is;
    +
    +
    +/**
    + * Number formatting symbols for locale it.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_it = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale it_IT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_it_IT = goog.i18n.NumberFormatSymbols_it;
    +
    +
    +/**
    + * Number formatting symbols for locale it_SM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_it_SM = goog.i18n.NumberFormatSymbols_it;
    +
    +
    +/**
    + * Number formatting symbols for locale iw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_iw = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'ILS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ja.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ja = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'JPY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ja_JP.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ja_JP = goog.i18n.NumberFormatSymbols_ja;
    +
    +
    +/**
    + * Number formatting symbols for locale ka.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ka = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN:
    +      '\u10D0\u10E0\u00A0\u10D0\u10E0\u10D8\u10E1\u00A0\u10E0\u10D8\u10EA\u10EE\u10D5\u10D8',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'GEL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ka_GE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ka_GE = goog.i18n.NumberFormatSymbols_ka;
    +
    +
    +/**
    + * Number formatting symbols for locale kk.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kk = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'KZT'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kk_Cyrl_KZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kk_Cyrl_KZ = goog.i18n.NumberFormatSymbols_kk;
    +
    +
    +/**
    + * Number formatting symbols for locale km.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_km = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KHR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale km_KH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_km_KH = goog.i18n.NumberFormatSymbols_km;
    +
    +
    +/**
    + * Number formatting symbols for locale kn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kn_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kn_IN = goog.i18n.NumberFormatSymbols_kn;
    +
    +
    +/**
    + * Number formatting symbols for locale ko.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ko = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KRW'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ko_KR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ko_KR = goog.i18n.NumberFormatSymbols_ko;
    +
    +
    +/**
    + * Number formatting symbols for locale ky.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ky = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u0441\u0430\u043D\u00A0\u044D\u043C\u0435\u0441',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'KGS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ky_Cyrl_KG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ky_Cyrl_KG = goog.i18n.NumberFormatSymbols_ky;
    +
    +
    +/**
    + * Number formatting symbols for locale ln.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ln = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CDF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ln_CD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ln_CD = goog.i18n.NumberFormatSymbols_ln;
    +
    +
    +/**
    + * Number formatting symbols for locale lo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN:
    +      '\u0E9A\u0ECD\u0EC8\u200B\u0EC1\u0EA1\u0EC8\u0E99\u200B\u0EC2\u0E95\u200B\u0EC0\u0EA5\u0E81',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'LAK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lo_LA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lo_LA = goog.i18n.NumberFormatSymbols_lo;
    +
    +
    +/**
    + * Number formatting symbols for locale lt.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lt = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lt_LT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lt_LT = goog.i18n.NumberFormatSymbols_lt;
    +
    +
    +/**
    + * Number formatting symbols for locale lv.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lv = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'nav\u00A0skaitlis',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lv_LV.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lv_LV = goog.i18n.NumberFormatSymbols_lv;
    +
    +
    +/**
    + * Number formatting symbols for locale mk.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mk = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mk_MK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mk_MK = goog.i18n.NumberFormatSymbols_mk;
    +
    +
    +/**
    + * Number formatting symbols for locale ml.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ml = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ml_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ml_IN = goog.i18n.NumberFormatSymbols_ml;
    +
    +
    +/**
    + * Number formatting symbols for locale mn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MNT'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mn_Cyrl_MN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mn_Cyrl_MN = goog.i18n.NumberFormatSymbols_mn;
    +
    +
    +/**
    + * Number formatting symbols for locale mr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mr = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u0966',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '[#E0]',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mr_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mr_IN = goog.i18n.NumberFormatSymbols_mr;
    +
    +
    +/**
    + * Number formatting symbols for locale ms.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ms = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MYR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ms_Latn_MY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ms_Latn_MY = goog.i18n.NumberFormatSymbols_ms;
    +
    +
    +/**
    + * Number formatting symbols for locale mt.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mt = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mt_MT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mt_MT = goog.i18n.NumberFormatSymbols_mt;
    +
    +
    +/**
    + * Number formatting symbols for locale my.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_my = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u1040',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN:
    +      '\u1002\u100F\u1014\u103A\u1038\u1019\u101F\u102F\u1010\u103A\u101E\u1031\u102C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MMK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale my_MM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_my_MM = goog.i18n.NumberFormatSymbols_my;
    +
    +
    +/**
    + * Number formatting symbols for locale nb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nb = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'NOK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nb_NO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nb_NO = goog.i18n.NumberFormatSymbols_nb;
    +
    +
    +/**
    + * Number formatting symbols for locale nb_SJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nb_SJ = goog.i18n.NumberFormatSymbols_nb;
    +
    +
    +/**
    + * Number formatting symbols for locale ne.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ne = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u0966',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'NPR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ne_NP.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ne_NP = goog.i18n.NumberFormatSymbols_ne;
    +
    +
    +/**
    + * Number formatting symbols for locale nl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_NL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_NL = goog.i18n.NumberFormatSymbols_nl;
    +
    +
    +/**
    + * Number formatting symbols for locale no.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_no = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'NOK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale no_NO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_no_NO = goog.i18n.NumberFormatSymbols_no;
    +
    +
    +/**
    + * Number formatting symbols for locale or.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_or = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale or_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_or_IN = goog.i18n.NumberFormatSymbols_or;
    +
    +
    +/**
    + * Number formatting symbols for locale pa.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pa = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '[#E0]',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pa_Guru_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pa_Guru_IN = goog.i18n.NumberFormatSymbols_pa;
    +
    +
    +/**
    + * Number formatting symbols for locale pl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'PLN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pl_PL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pl_PL = goog.i18n.NumberFormatSymbols_pl;
    +
    +
    +/**
    + * Number formatting symbols for locale pt.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BRL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_BR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_BR = goog.i18n.NumberFormatSymbols_pt;
    +
    +
    +/**
    + * Number formatting symbols for locale pt_PT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_PT = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ro.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ro = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'RON'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ro_RO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ro_RO = goog.i18n.NumberFormatSymbols_ro;
    +
    +
    +/**
    + * Number formatting symbols for locale ru.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'RUB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ru_RU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_RU = goog.i18n.NumberFormatSymbols_ru;
    +
    +
    +/**
    + * Number formatting symbols for locale si.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_si = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'LKR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale si_LK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_si_LK = goog.i18n.NumberFormatSymbols_si;
    +
    +
    +/**
    + * Number formatting symbols for locale sk.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sk = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sk_SK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sk_SK = goog.i18n.NumberFormatSymbols_sk;
    +
    +
    +/**
    + * Number formatting symbols for locale sl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'e',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sl_SI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sl_SI = goog.i18n.NumberFormatSymbols_sl;
    +
    +
    +/**
    + * Number formatting symbols for locale sq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sq = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'ALL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sq_AL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sq_AL = goog.i18n.NumberFormatSymbols_sq;
    +
    +
    +/**
    + * Number formatting symbols for locale sr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'RSD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Cyrl_RS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Cyrl_RS = goog.i18n.NumberFormatSymbols_sr;
    +
    +
    +/**
    + * Number formatting symbols for locale sv.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sv = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'SEK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sv_SE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sv_SE = goog.i18n.NumberFormatSymbols_sv;
    +
    +
    +/**
    + * Number formatting symbols for locale sw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sw = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sw_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sw_TZ = goog.i18n.NumberFormatSymbols_sw;
    +
    +
    +/**
    + * Number formatting symbols for locale ta.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ta = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ta_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ta_IN = goog.i18n.NumberFormatSymbols_ta;
    +
    +
    +/**
    + * Number formatting symbols for locale te.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_te = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale te_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_te_IN = goog.i18n.NumberFormatSymbols_te;
    +
    +
    +/**
    + * Number formatting symbols for locale th.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_th = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'THB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale th_TH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_th_TH = goog.i18n.NumberFormatSymbols_th;
    +
    +
    +/**
    + * Number formatting symbols for locale tl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tl = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PHP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tr = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '%#,##0',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'TRY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tr_TR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tr_TR = goog.i18n.NumberFormatSymbols_tr;
    +
    +
    +/**
    + * Number formatting symbols for locale uk.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uk = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: '\u0415',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u041D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'UAH'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uk_UA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uk_UA = goog.i18n.NumberFormatSymbols_uk;
    +
    +
    +/**
    + * Number formatting symbols for locale ur.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ur = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'PKR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ur_PK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ur_PK = goog.i18n.NumberFormatSymbols_ur;
    +
    +
    +/**
    + * Number formatting symbols for locale uz.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'UZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Latn_UZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Latn_UZ = goog.i18n.NumberFormatSymbols_uz;
    +
    +
    +/**
    + * Number formatting symbols for locale vi.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vi = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'VND'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vi_VN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vi_VN = goog.i18n.NumberFormatSymbols_vi;
    +
    +
    +/**
    + * Number formatting symbols for locale zh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'CNY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_CN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_CN = goog.i18n.NumberFormatSymbols_zh;
    +
    +
    +/**
    + * Number formatting symbols for locale zh_HK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_HK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u975E\u6578\u503C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'HKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hans_CN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hans_CN = goog.i18n.NumberFormatSymbols_zh;
    +
    +
    +/**
    + * Number formatting symbols for locale zh_TW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_TW = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u975E\u6578\u503C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TWD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zu = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'I-NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zu_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zu_ZA = goog.i18n.NumberFormatSymbols_zu;
    +
    +
    +/**
    + * Selected number formatting symbols by locale.
    + */
    +goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_af;
    +}
    +
    +if (goog.LOCALE == 'af_ZA' || goog.LOCALE == 'af-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_af;
    +}
    +
    +if (goog.LOCALE == 'am') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_am;
    +}
    +
    +if (goog.LOCALE == 'am_ET' || goog.LOCALE == 'am-ET') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_am;
    +}
    +
    +if (goog.LOCALE == 'ar') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar;
    +}
    +
    +if (goog.LOCALE == 'ar_001' || goog.LOCALE == 'ar-001') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar;
    +}
    +
    +if (goog.LOCALE == 'az') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az;
    +}
    +
    +if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az;
    +}
    +
    +if (goog.LOCALE == 'bg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bg;
    +}
    +
    +if (goog.LOCALE == 'bg_BG' || goog.LOCALE == 'bg-BG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bg;
    +}
    +
    +if (goog.LOCALE == 'bn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bn;
    +}
    +
    +if (goog.LOCALE == 'bn_BD' || goog.LOCALE == 'bn-BD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bn;
    +}
    +
    +if (goog.LOCALE == 'br') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_br;
    +}
    +
    +if (goog.LOCALE == 'br_FR' || goog.LOCALE == 'br-FR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_br;
    +}
    +
    +if (goog.LOCALE == 'ca') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_AD' || goog.LOCALE == 'ca-AD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_ES' || goog.LOCALE == 'ca-ES') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_ES_VALENCIA' || goog.LOCALE == 'ca-ES-VALENCIA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_FR' || goog.LOCALE == 'ca-FR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'ca_IT' || goog.LOCALE == 'ca-IT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'chr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_chr;
    +}
    +
    +if (goog.LOCALE == 'chr_US' || goog.LOCALE == 'chr-US') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_chr;
    +}
    +
    +if (goog.LOCALE == 'cs') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cs;
    +}
    +
    +if (goog.LOCALE == 'cs_CZ' || goog.LOCALE == 'cs-CZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cs;
    +}
    +
    +if (goog.LOCALE == 'cy') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cy;
    +}
    +
    +if (goog.LOCALE == 'cy_GB' || goog.LOCALE == 'cy-GB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cy;
    +}
    +
    +if (goog.LOCALE == 'da') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'da_DK' || goog.LOCALE == 'da-DK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'da_GL' || goog.LOCALE == 'da-GL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'de') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de_AT;
    +}
    +
    +if (goog.LOCALE == 'de_BE' || goog.LOCALE == 'de-BE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de_CH;
    +}
    +
    +if (goog.LOCALE == 'de_DE' || goog.LOCALE == 'de-DE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_LU' || goog.LOCALE == 'de-LU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'el') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_el;
    +}
    +
    +if (goog.LOCALE == 'el_GR' || goog.LOCALE == 'el-GR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_el;
    +}
    +
    +if (goog.LOCALE == 'en') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_001' || goog.LOCALE == 'en-001') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_AS' || goog.LOCALE == 'en-AS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_AU;
    +}
    +
    +if (goog.LOCALE == 'en_DG' || goog.LOCALE == 'en-DG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_FM' || goog.LOCALE == 'en-FM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GB;
    +}
    +
    +if (goog.LOCALE == 'en_GU' || goog.LOCALE == 'en-GU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_IE;
    +}
    +
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_IN;
    +}
    +
    +if (goog.LOCALE == 'en_IO' || goog.LOCALE == 'en-IO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_MH' || goog.LOCALE == 'en-MH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_MP' || goog.LOCALE == 'en-MP') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_PR' || goog.LOCALE == 'en-PR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_PW' || goog.LOCALE == 'en-PW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SG;
    +}
    +
    +if (goog.LOCALE == 'en_TC' || goog.LOCALE == 'en-TC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_UM' || goog.LOCALE == 'en-UM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_VG' || goog.LOCALE == 'en-VG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_VI' || goog.LOCALE == 'en-VI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_ZA;
    +}
    +
    +if (goog.LOCALE == 'en_ZW' || goog.LOCALE == 'en-ZW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'es') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_419;
    +}
    +
    +if (goog.LOCALE == 'es_EA' || goog.LOCALE == 'es-EA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_IC' || goog.LOCALE == 'es-IC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'et') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_et;
    +}
    +
    +if (goog.LOCALE == 'et_EE' || goog.LOCALE == 'et-EE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_et;
    +}
    +
    +if (goog.LOCALE == 'eu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_eu;
    +}
    +
    +if (goog.LOCALE == 'eu_ES' || goog.LOCALE == 'eu-ES') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_eu;
    +}
    +
    +if (goog.LOCALE == 'fa') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fa;
    +}
    +
    +if (goog.LOCALE == 'fa_IR' || goog.LOCALE == 'fa-IR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fa;
    +}
    +
    +if (goog.LOCALE == 'fi') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fi;
    +}
    +
    +if (goog.LOCALE == 'fi_FI' || goog.LOCALE == 'fi-FI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fi;
    +}
    +
    +if (goog.LOCALE == 'fil') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fil;
    +}
    +
    +if (goog.LOCALE == 'fil_PH' || goog.LOCALE == 'fil-PH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fil;
    +}
    +
    +if (goog.LOCALE == 'fr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_BL' || goog.LOCALE == 'fr-BL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CA;
    +}
    +
    +if (goog.LOCALE == 'fr_FR' || goog.LOCALE == 'fr-FR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_GF' || goog.LOCALE == 'fr-GF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_GP' || goog.LOCALE == 'fr-GP') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MC' || goog.LOCALE == 'fr-MC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MF' || goog.LOCALE == 'fr-MF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_MQ' || goog.LOCALE == 'fr-MQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_PM' || goog.LOCALE == 'fr-PM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_RE' || goog.LOCALE == 'fr-RE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'ga') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ga;
    +}
    +
    +if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ga;
    +}
    +
    +if (goog.LOCALE == 'gl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gl;
    +}
    +
    +if (goog.LOCALE == 'gl_ES' || goog.LOCALE == 'gl-ES') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gl;
    +}
    +
    +if (goog.LOCALE == 'gsw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gsw_CH' || goog.LOCALE == 'gsw-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gsw_LI' || goog.LOCALE == 'gsw-LI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gu;
    +}
    +
    +if (goog.LOCALE == 'gu_IN' || goog.LOCALE == 'gu-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gu;
    +}
    +
    +if (goog.LOCALE == 'haw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_haw;
    +}
    +
    +if (goog.LOCALE == 'haw_US' || goog.LOCALE == 'haw-US') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_haw;
    +}
    +
    +if (goog.LOCALE == 'he') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_he;
    +}
    +
    +if (goog.LOCALE == 'he_IL' || goog.LOCALE == 'he-IL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_he;
    +}
    +
    +if (goog.LOCALE == 'hi') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hi;
    +}
    +
    +if (goog.LOCALE == 'hi_IN' || goog.LOCALE == 'hi-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hi;
    +}
    +
    +if (goog.LOCALE == 'hr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hr;
    +}
    +
    +if (goog.LOCALE == 'hr_HR' || goog.LOCALE == 'hr-HR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hr;
    +}
    +
    +if (goog.LOCALE == 'hu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hu;
    +}
    +
    +if (goog.LOCALE == 'hu_HU' || goog.LOCALE == 'hu-HU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hu;
    +}
    +
    +if (goog.LOCALE == 'hy') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hy;
    +}
    +
    +if (goog.LOCALE == 'hy_AM' || goog.LOCALE == 'hy-AM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hy;
    +}
    +
    +if (goog.LOCALE == 'id') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_id;
    +}
    +
    +if (goog.LOCALE == 'id_ID' || goog.LOCALE == 'id-ID') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_id;
    +}
    +
    +if (goog.LOCALE == 'in') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_in;
    +}
    +
    +if (goog.LOCALE == 'is') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_is;
    +}
    +
    +if (goog.LOCALE == 'is_IS' || goog.LOCALE == 'is-IS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_is;
    +}
    +
    +if (goog.LOCALE == 'it') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'it_IT' || goog.LOCALE == 'it-IT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'it_SM' || goog.LOCALE == 'it-SM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'iw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_iw;
    +}
    +
    +if (goog.LOCALE == 'ja') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ja;
    +}
    +
    +if (goog.LOCALE == 'ja_JP' || goog.LOCALE == 'ja-JP') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ja;
    +}
    +
    +if (goog.LOCALE == 'ka') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ka;
    +}
    +
    +if (goog.LOCALE == 'ka_GE' || goog.LOCALE == 'ka-GE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ka;
    +}
    +
    +if (goog.LOCALE == 'kk') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kk;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl_KZ' || goog.LOCALE == 'kk-Cyrl-KZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kk;
    +}
    +
    +if (goog.LOCALE == 'km') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_km;
    +}
    +
    +if (goog.LOCALE == 'km_KH' || goog.LOCALE == 'km-KH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_km;
    +}
    +
    +if (goog.LOCALE == 'kn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kn;
    +}
    +
    +if (goog.LOCALE == 'kn_IN' || goog.LOCALE == 'kn-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kn;
    +}
    +
    +if (goog.LOCALE == 'ko') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ko;
    +}
    +
    +if (goog.LOCALE == 'ko_KR' || goog.LOCALE == 'ko-KR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ko;
    +}
    +
    +if (goog.LOCALE == 'ky') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ky;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl_KG' || goog.LOCALE == 'ky-Cyrl-KG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ky;
    +}
    +
    +if (goog.LOCALE == 'ln') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ln;
    +}
    +
    +if (goog.LOCALE == 'ln_CD' || goog.LOCALE == 'ln-CD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ln;
    +}
    +
    +if (goog.LOCALE == 'lo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lo;
    +}
    +
    +if (goog.LOCALE == 'lo_LA' || goog.LOCALE == 'lo-LA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lo;
    +}
    +
    +if (goog.LOCALE == 'lt') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lt;
    +}
    +
    +if (goog.LOCALE == 'lt_LT' || goog.LOCALE == 'lt-LT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lt;
    +}
    +
    +if (goog.LOCALE == 'lv') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lv;
    +}
    +
    +if (goog.LOCALE == 'lv_LV' || goog.LOCALE == 'lv-LV') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lv;
    +}
    +
    +if (goog.LOCALE == 'mk') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mk;
    +}
    +
    +if (goog.LOCALE == 'mk_MK' || goog.LOCALE == 'mk-MK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mk;
    +}
    +
    +if (goog.LOCALE == 'ml') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ml;
    +}
    +
    +if (goog.LOCALE == 'ml_IN' || goog.LOCALE == 'ml-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ml;
    +}
    +
    +if (goog.LOCALE == 'mn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mn;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl_MN' || goog.LOCALE == 'mn-Cyrl-MN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mn;
    +}
    +
    +if (goog.LOCALE == 'mr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mr;
    +}
    +
    +if (goog.LOCALE == 'mr_IN' || goog.LOCALE == 'mr-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mr;
    +}
    +
    +if (goog.LOCALE == 'ms') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ms;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_MY' || goog.LOCALE == 'ms-Latn-MY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ms;
    +}
    +
    +if (goog.LOCALE == 'mt') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mt;
    +}
    +
    +if (goog.LOCALE == 'mt_MT' || goog.LOCALE == 'mt-MT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mt;
    +}
    +
    +if (goog.LOCALE == 'my') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_my;
    +}
    +
    +if (goog.LOCALE == 'my_MM' || goog.LOCALE == 'my-MM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_my;
    +}
    +
    +if (goog.LOCALE == 'nb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'nb_NO' || goog.LOCALE == 'nb-NO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'nb_SJ' || goog.LOCALE == 'nb-SJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'ne') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ne;
    +}
    +
    +if (goog.LOCALE == 'ne_NP' || goog.LOCALE == 'ne-NP') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ne;
    +}
    +
    +if (goog.LOCALE == 'nl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl;
    +}
    +
    +if (goog.LOCALE == 'nl_NL' || goog.LOCALE == 'nl-NL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl;
    +}
    +
    +if (goog.LOCALE == 'no') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_no;
    +}
    +
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_no;
    +}
    +
    +if (goog.LOCALE == 'or') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_or;
    +}
    +
    +if (goog.LOCALE == 'or_IN' || goog.LOCALE == 'or-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_or;
    +}
    +
    +if (goog.LOCALE == 'pa') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pa;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru_IN' || goog.LOCALE == 'pa-Guru-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pa;
    +}
    +
    +if (goog.LOCALE == 'pl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pl;
    +}
    +
    +if (goog.LOCALE == 'pl_PL' || goog.LOCALE == 'pl-PL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pl;
    +}
    +
    +if (goog.LOCALE == 'pt') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_PT;
    +}
    +
    +if (goog.LOCALE == 'ro') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ro;
    +}
    +
    +if (goog.LOCALE == 'ro_RO' || goog.LOCALE == 'ro-RO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ro;
    +}
    +
    +if (goog.LOCALE == 'ru') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru;
    +}
    +
    +if (goog.LOCALE == 'ru_RU' || goog.LOCALE == 'ru-RU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru;
    +}
    +
    +if (goog.LOCALE == 'si') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_si;
    +}
    +
    +if (goog.LOCALE == 'si_LK' || goog.LOCALE == 'si-LK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_si;
    +}
    +
    +if (goog.LOCALE == 'sk') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sk;
    +}
    +
    +if (goog.LOCALE == 'sk_SK' || goog.LOCALE == 'sk-SK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sk;
    +}
    +
    +if (goog.LOCALE == 'sl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sl;
    +}
    +
    +if (goog.LOCALE == 'sl_SI' || goog.LOCALE == 'sl-SI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sl;
    +}
    +
    +if (goog.LOCALE == 'sq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sq;
    +}
    +
    +if (goog.LOCALE == 'sq_AL' || goog.LOCALE == 'sq-AL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sq;
    +}
    +
    +if (goog.LOCALE == 'sr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_RS' || goog.LOCALE == 'sr-Cyrl-RS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr;
    +}
    +
    +if (goog.LOCALE == 'sv') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sv;
    +}
    +
    +if (goog.LOCALE == 'sv_SE' || goog.LOCALE == 'sv-SE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sv;
    +}
    +
    +if (goog.LOCALE == 'sw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sw;
    +}
    +
    +if (goog.LOCALE == 'sw_TZ' || goog.LOCALE == 'sw-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sw;
    +}
    +
    +if (goog.LOCALE == 'ta') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ta;
    +}
    +
    +if (goog.LOCALE == 'ta_IN' || goog.LOCALE == 'ta-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ta;
    +}
    +
    +if (goog.LOCALE == 'te') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_te;
    +}
    +
    +if (goog.LOCALE == 'te_IN' || goog.LOCALE == 'te-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_te;
    +}
    +
    +if (goog.LOCALE == 'th') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_th;
    +}
    +
    +if (goog.LOCALE == 'th_TH' || goog.LOCALE == 'th-TH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_th;
    +}
    +
    +if (goog.LOCALE == 'tl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tl;
    +}
    +
    +if (goog.LOCALE == 'tr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tr;
    +}
    +
    +if (goog.LOCALE == 'tr_TR' || goog.LOCALE == 'tr-TR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tr;
    +}
    +
    +if (goog.LOCALE == 'uk') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uk;
    +}
    +
    +if (goog.LOCALE == 'uk_UA' || goog.LOCALE == 'uk-UA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uk;
    +}
    +
    +if (goog.LOCALE == 'ur') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ur;
    +}
    +
    +if (goog.LOCALE == 'ur_PK' || goog.LOCALE == 'ur-PK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ur;
    +}
    +
    +if (goog.LOCALE == 'uz') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn_UZ' || goog.LOCALE == 'uz-Latn-UZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz;
    +}
    +
    +if (goog.LOCALE == 'vi') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vi;
    +}
    +
    +if (goog.LOCALE == 'vi_VN' || goog.LOCALE == 'vi-VN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vi;
    +}
    +
    +if (goog.LOCALE == 'zh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_CN' || goog.LOCALE == 'zh-Hans-CN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_TW;
    +}
    +
    +if (goog.LOCALE == 'zu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zu;
    +}
    +
    +if (goog.LOCALE == 'zu_ZA' || goog.LOCALE == 'zu-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zu;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbolsext.js b/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbolsext.js
    new file mode 100644
    index 0000000000..e9c011196c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/numberformatsymbolsext.js
    @@ -0,0 +1,12374 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Number formatting symbols.
    + *
    + * This file is autogenerated by script:
    + * http://go/generate_number_constants.py
    + * using the --for_closure flag.
    + * File generated from CLDR ver. 26
    + *
    + * This file coveres those locales that are not covered in
    + * "numberformatsymbols.js".
    + *
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could fix CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.i18n.NumberFormatSymbolsExt');
    +goog.provide('goog.i18n.NumberFormatSymbols_aa');
    +goog.provide('goog.i18n.NumberFormatSymbols_aa_DJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_aa_ER');
    +goog.provide('goog.i18n.NumberFormatSymbols_aa_ET');
    +goog.provide('goog.i18n.NumberFormatSymbols_af_NA');
    +goog.provide('goog.i18n.NumberFormatSymbols_agq');
    +goog.provide('goog.i18n.NumberFormatSymbols_agq_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ak');
    +goog.provide('goog.i18n.NumberFormatSymbols_ak_GH');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_AE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_BH');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_DJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_DZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_EG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_EH');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_ER');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_IL');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_IQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_JO');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_KM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_KW');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_LB');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_LY');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_MR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_OM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_PS');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_QA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_SA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_SD');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_SO');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_SS');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_SY');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_TD');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_TN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ar_YE');
    +goog.provide('goog.i18n.NumberFormatSymbols_as');
    +goog.provide('goog.i18n.NumberFormatSymbols_as_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_asa');
    +goog.provide('goog.i18n.NumberFormatSymbols_asa_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ast');
    +goog.provide('goog.i18n.NumberFormatSymbols_ast_ES');
    +goog.provide('goog.i18n.NumberFormatSymbols_az_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_az_Cyrl_AZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_az_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_bas');
    +goog.provide('goog.i18n.NumberFormatSymbols_bas_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_be');
    +goog.provide('goog.i18n.NumberFormatSymbols_be_BY');
    +goog.provide('goog.i18n.NumberFormatSymbols_bem');
    +goog.provide('goog.i18n.NumberFormatSymbols_bem_ZM');
    +goog.provide('goog.i18n.NumberFormatSymbols_bez');
    +goog.provide('goog.i18n.NumberFormatSymbols_bez_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_bm');
    +goog.provide('goog.i18n.NumberFormatSymbols_bm_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_bm_Latn_ML');
    +goog.provide('goog.i18n.NumberFormatSymbols_bn_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_bo');
    +goog.provide('goog.i18n.NumberFormatSymbols_bo_CN');
    +goog.provide('goog.i18n.NumberFormatSymbols_bo_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_brx');
    +goog.provide('goog.i18n.NumberFormatSymbols_brx_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_bs');
    +goog.provide('goog.i18n.NumberFormatSymbols_bs_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_bs_Cyrl_BA');
    +goog.provide('goog.i18n.NumberFormatSymbols_bs_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_bs_Latn_BA');
    +goog.provide('goog.i18n.NumberFormatSymbols_cgg');
    +goog.provide('goog.i18n.NumberFormatSymbols_cgg_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_Arab');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_Arab_IQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_Arab_IR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_IQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_IR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_ckb_Latn_IQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_dav');
    +goog.provide('goog.i18n.NumberFormatSymbols_dav_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_de_LI');
    +goog.provide('goog.i18n.NumberFormatSymbols_dje');
    +goog.provide('goog.i18n.NumberFormatSymbols_dje_NE');
    +goog.provide('goog.i18n.NumberFormatSymbols_dsb');
    +goog.provide('goog.i18n.NumberFormatSymbols_dsb_DE');
    +goog.provide('goog.i18n.NumberFormatSymbols_dua');
    +goog.provide('goog.i18n.NumberFormatSymbols_dua_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_dyo');
    +goog.provide('goog.i18n.NumberFormatSymbols_dyo_SN');
    +goog.provide('goog.i18n.NumberFormatSymbols_dz');
    +goog.provide('goog.i18n.NumberFormatSymbols_dz_BT');
    +goog.provide('goog.i18n.NumberFormatSymbols_ebu');
    +goog.provide('goog.i18n.NumberFormatSymbols_ebu_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ee');
    +goog.provide('goog.i18n.NumberFormatSymbols_ee_GH');
    +goog.provide('goog.i18n.NumberFormatSymbols_ee_TG');
    +goog.provide('goog.i18n.NumberFormatSymbols_el_CY');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_150');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_AG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_AI');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BB');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BE');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BW');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_BZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_CA');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_CC');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_CK');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_CX');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_DM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_ER');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_FJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_FK');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GD');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GH');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GI');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_GY');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_HK');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_IM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_JE');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_JM');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_KI');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_KN');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_KY');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_LC');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_LR');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_LS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MO');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MT');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MU');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MW');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_MY');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NA');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NF');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NR');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NU');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_NZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PH');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PK');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_PN');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_RW');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SB');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SC');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SD');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SH');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SL');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SX');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_SZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TK');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TO');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TT');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TV');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_VC');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_VU');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_WS');
    +goog.provide('goog.i18n.NumberFormatSymbols_en_ZM');
    +goog.provide('goog.i18n.NumberFormatSymbols_eo');
    +goog.provide('goog.i18n.NumberFormatSymbols_eo_001');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_AR');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_BO');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_CL');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_CO');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_CR');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_CU');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_DO');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_EC');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_GQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_GT');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_HN');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_MX');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_NI');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_PA');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_PE');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_PH');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_PR');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_PY');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_SV');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_US');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_UY');
    +goog.provide('goog.i18n.NumberFormatSymbols_es_VE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ewo');
    +goog.provide('goog.i18n.NumberFormatSymbols_ewo_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_fa_AF');
    +goog.provide('goog.i18n.NumberFormatSymbols_ff');
    +goog.provide('goog.i18n.NumberFormatSymbols_ff_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ff_GN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ff_MR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ff_SN');
    +goog.provide('goog.i18n.NumberFormatSymbols_fo');
    +goog.provide('goog.i18n.NumberFormatSymbols_fo_FO');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_BE');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_BF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_BI');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_BJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CD');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CG');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CI');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_DJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_DZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_GA');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_GN');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_GQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_HT');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_KM');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_LU');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MG');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_ML');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MR');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_MU');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_NC');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_NE');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_PF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_RW');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_SC');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_SN');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_SY');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_TD');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_TG');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_TN');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_VU');
    +goog.provide('goog.i18n.NumberFormatSymbols_fr_WF');
    +goog.provide('goog.i18n.NumberFormatSymbols_fur');
    +goog.provide('goog.i18n.NumberFormatSymbols_fur_IT');
    +goog.provide('goog.i18n.NumberFormatSymbols_fy');
    +goog.provide('goog.i18n.NumberFormatSymbols_fy_NL');
    +goog.provide('goog.i18n.NumberFormatSymbols_gd');
    +goog.provide('goog.i18n.NumberFormatSymbols_gd_GB');
    +goog.provide('goog.i18n.NumberFormatSymbols_gsw_FR');
    +goog.provide('goog.i18n.NumberFormatSymbols_guz');
    +goog.provide('goog.i18n.NumberFormatSymbols_guz_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_gv');
    +goog.provide('goog.i18n.NumberFormatSymbols_gv_IM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ha');
    +goog.provide('goog.i18n.NumberFormatSymbols_ha_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_ha_Latn_GH');
    +goog.provide('goog.i18n.NumberFormatSymbols_ha_Latn_NE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ha_Latn_NG');
    +goog.provide('goog.i18n.NumberFormatSymbols_hr_BA');
    +goog.provide('goog.i18n.NumberFormatSymbols_hsb');
    +goog.provide('goog.i18n.NumberFormatSymbols_hsb_DE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ia');
    +goog.provide('goog.i18n.NumberFormatSymbols_ia_FR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ig');
    +goog.provide('goog.i18n.NumberFormatSymbols_ig_NG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ii');
    +goog.provide('goog.i18n.NumberFormatSymbols_ii_CN');
    +goog.provide('goog.i18n.NumberFormatSymbols_it_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_jgo');
    +goog.provide('goog.i18n.NumberFormatSymbols_jgo_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_jmc');
    +goog.provide('goog.i18n.NumberFormatSymbols_jmc_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_kab');
    +goog.provide('goog.i18n.NumberFormatSymbols_kab_DZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_kam');
    +goog.provide('goog.i18n.NumberFormatSymbols_kam_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_kde');
    +goog.provide('goog.i18n.NumberFormatSymbols_kde_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_kea');
    +goog.provide('goog.i18n.NumberFormatSymbols_kea_CV');
    +goog.provide('goog.i18n.NumberFormatSymbols_khq');
    +goog.provide('goog.i18n.NumberFormatSymbols_khq_ML');
    +goog.provide('goog.i18n.NumberFormatSymbols_ki');
    +goog.provide('goog.i18n.NumberFormatSymbols_ki_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_kk_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_kkj');
    +goog.provide('goog.i18n.NumberFormatSymbols_kkj_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_kl');
    +goog.provide('goog.i18n.NumberFormatSymbols_kl_GL');
    +goog.provide('goog.i18n.NumberFormatSymbols_kln');
    +goog.provide('goog.i18n.NumberFormatSymbols_kln_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_ko_KP');
    +goog.provide('goog.i18n.NumberFormatSymbols_kok');
    +goog.provide('goog.i18n.NumberFormatSymbols_kok_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ks');
    +goog.provide('goog.i18n.NumberFormatSymbols_ks_Arab');
    +goog.provide('goog.i18n.NumberFormatSymbols_ks_Arab_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksb');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksb_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksf');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksf_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksh');
    +goog.provide('goog.i18n.NumberFormatSymbols_ksh_DE');
    +goog.provide('goog.i18n.NumberFormatSymbols_kw');
    +goog.provide('goog.i18n.NumberFormatSymbols_kw_GB');
    +goog.provide('goog.i18n.NumberFormatSymbols_ky_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_lag');
    +goog.provide('goog.i18n.NumberFormatSymbols_lag_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_lb');
    +goog.provide('goog.i18n.NumberFormatSymbols_lb_LU');
    +goog.provide('goog.i18n.NumberFormatSymbols_lg');
    +goog.provide('goog.i18n.NumberFormatSymbols_lg_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_lkt');
    +goog.provide('goog.i18n.NumberFormatSymbols_lkt_US');
    +goog.provide('goog.i18n.NumberFormatSymbols_ln_AO');
    +goog.provide('goog.i18n.NumberFormatSymbols_ln_CF');
    +goog.provide('goog.i18n.NumberFormatSymbols_ln_CG');
    +goog.provide('goog.i18n.NumberFormatSymbols_lu');
    +goog.provide('goog.i18n.NumberFormatSymbols_lu_CD');
    +goog.provide('goog.i18n.NumberFormatSymbols_luo');
    +goog.provide('goog.i18n.NumberFormatSymbols_luo_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_luy');
    +goog.provide('goog.i18n.NumberFormatSymbols_luy_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_mas');
    +goog.provide('goog.i18n.NumberFormatSymbols_mas_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_mas_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_mer');
    +goog.provide('goog.i18n.NumberFormatSymbols_mer_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_mfe');
    +goog.provide('goog.i18n.NumberFormatSymbols_mfe_MU');
    +goog.provide('goog.i18n.NumberFormatSymbols_mg');
    +goog.provide('goog.i18n.NumberFormatSymbols_mg_MG');
    +goog.provide('goog.i18n.NumberFormatSymbols_mgh');
    +goog.provide('goog.i18n.NumberFormatSymbols_mgh_MZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_mgo');
    +goog.provide('goog.i18n.NumberFormatSymbols_mgo_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_mn_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_ms_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_ms_Latn_BN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ms_Latn_SG');
    +goog.provide('goog.i18n.NumberFormatSymbols_mua');
    +goog.provide('goog.i18n.NumberFormatSymbols_mua_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_naq');
    +goog.provide('goog.i18n.NumberFormatSymbols_naq_NA');
    +goog.provide('goog.i18n.NumberFormatSymbols_nd');
    +goog.provide('goog.i18n.NumberFormatSymbols_nd_ZW');
    +goog.provide('goog.i18n.NumberFormatSymbols_ne_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_AW');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_BE');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_BQ');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_CW');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_SR');
    +goog.provide('goog.i18n.NumberFormatSymbols_nl_SX');
    +goog.provide('goog.i18n.NumberFormatSymbols_nmg');
    +goog.provide('goog.i18n.NumberFormatSymbols_nmg_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_nn');
    +goog.provide('goog.i18n.NumberFormatSymbols_nn_NO');
    +goog.provide('goog.i18n.NumberFormatSymbols_nnh');
    +goog.provide('goog.i18n.NumberFormatSymbols_nnh_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_nr');
    +goog.provide('goog.i18n.NumberFormatSymbols_nr_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_nso');
    +goog.provide('goog.i18n.NumberFormatSymbols_nso_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_nus');
    +goog.provide('goog.i18n.NumberFormatSymbols_nus_SD');
    +goog.provide('goog.i18n.NumberFormatSymbols_nyn');
    +goog.provide('goog.i18n.NumberFormatSymbols_nyn_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_om');
    +goog.provide('goog.i18n.NumberFormatSymbols_om_ET');
    +goog.provide('goog.i18n.NumberFormatSymbols_om_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_os');
    +goog.provide('goog.i18n.NumberFormatSymbols_os_GE');
    +goog.provide('goog.i18n.NumberFormatSymbols_os_RU');
    +goog.provide('goog.i18n.NumberFormatSymbols_pa_Arab');
    +goog.provide('goog.i18n.NumberFormatSymbols_pa_Arab_PK');
    +goog.provide('goog.i18n.NumberFormatSymbols_pa_Guru');
    +goog.provide('goog.i18n.NumberFormatSymbols_ps');
    +goog.provide('goog.i18n.NumberFormatSymbols_ps_AF');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_AO');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_CV');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_GW');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_MO');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_MZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_ST');
    +goog.provide('goog.i18n.NumberFormatSymbols_pt_TL');
    +goog.provide('goog.i18n.NumberFormatSymbols_qu');
    +goog.provide('goog.i18n.NumberFormatSymbols_qu_BO');
    +goog.provide('goog.i18n.NumberFormatSymbols_qu_EC');
    +goog.provide('goog.i18n.NumberFormatSymbols_qu_PE');
    +goog.provide('goog.i18n.NumberFormatSymbols_rm');
    +goog.provide('goog.i18n.NumberFormatSymbols_rm_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_rn');
    +goog.provide('goog.i18n.NumberFormatSymbols_rn_BI');
    +goog.provide('goog.i18n.NumberFormatSymbols_ro_MD');
    +goog.provide('goog.i18n.NumberFormatSymbols_rof');
    +goog.provide('goog.i18n.NumberFormatSymbols_rof_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_BY');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_KG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_KZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_MD');
    +goog.provide('goog.i18n.NumberFormatSymbols_ru_UA');
    +goog.provide('goog.i18n.NumberFormatSymbols_rw');
    +goog.provide('goog.i18n.NumberFormatSymbols_rw_RW');
    +goog.provide('goog.i18n.NumberFormatSymbols_rwk');
    +goog.provide('goog.i18n.NumberFormatSymbols_rwk_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_sah');
    +goog.provide('goog.i18n.NumberFormatSymbols_sah_RU');
    +goog.provide('goog.i18n.NumberFormatSymbols_saq');
    +goog.provide('goog.i18n.NumberFormatSymbols_saq_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_sbp');
    +goog.provide('goog.i18n.NumberFormatSymbols_sbp_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_se');
    +goog.provide('goog.i18n.NumberFormatSymbols_se_FI');
    +goog.provide('goog.i18n.NumberFormatSymbols_se_NO');
    +goog.provide('goog.i18n.NumberFormatSymbols_se_SE');
    +goog.provide('goog.i18n.NumberFormatSymbols_seh');
    +goog.provide('goog.i18n.NumberFormatSymbols_seh_MZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ses');
    +goog.provide('goog.i18n.NumberFormatSymbols_ses_ML');
    +goog.provide('goog.i18n.NumberFormatSymbols_sg');
    +goog.provide('goog.i18n.NumberFormatSymbols_sg_CF');
    +goog.provide('goog.i18n.NumberFormatSymbols_shi');
    +goog.provide('goog.i18n.NumberFormatSymbols_shi_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_shi_Latn_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_shi_Tfng');
    +goog.provide('goog.i18n.NumberFormatSymbols_shi_Tfng_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_smn');
    +goog.provide('goog.i18n.NumberFormatSymbols_smn_FI');
    +goog.provide('goog.i18n.NumberFormatSymbols_sn');
    +goog.provide('goog.i18n.NumberFormatSymbols_sn_ZW');
    +goog.provide('goog.i18n.NumberFormatSymbols_so');
    +goog.provide('goog.i18n.NumberFormatSymbols_so_DJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_so_ET');
    +goog.provide('goog.i18n.NumberFormatSymbols_so_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_so_SO');
    +goog.provide('goog.i18n.NumberFormatSymbols_sq_MK');
    +goog.provide('goog.i18n.NumberFormatSymbols_sq_XK');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Cyrl_BA');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Cyrl_ME');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Cyrl_XK');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Latn_BA');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Latn_ME');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Latn_RS');
    +goog.provide('goog.i18n.NumberFormatSymbols_sr_Latn_XK');
    +goog.provide('goog.i18n.NumberFormatSymbols_ss');
    +goog.provide('goog.i18n.NumberFormatSymbols_ss_SZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_ss_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ssy');
    +goog.provide('goog.i18n.NumberFormatSymbols_ssy_ER');
    +goog.provide('goog.i18n.NumberFormatSymbols_sv_AX');
    +goog.provide('goog.i18n.NumberFormatSymbols_sv_FI');
    +goog.provide('goog.i18n.NumberFormatSymbols_sw_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_sw_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_swc');
    +goog.provide('goog.i18n.NumberFormatSymbols_swc_CD');
    +goog.provide('goog.i18n.NumberFormatSymbols_ta_LK');
    +goog.provide('goog.i18n.NumberFormatSymbols_ta_MY');
    +goog.provide('goog.i18n.NumberFormatSymbols_ta_SG');
    +goog.provide('goog.i18n.NumberFormatSymbols_teo');
    +goog.provide('goog.i18n.NumberFormatSymbols_teo_KE');
    +goog.provide('goog.i18n.NumberFormatSymbols_teo_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_ti');
    +goog.provide('goog.i18n.NumberFormatSymbols_ti_ER');
    +goog.provide('goog.i18n.NumberFormatSymbols_ti_ET');
    +goog.provide('goog.i18n.NumberFormatSymbols_tn');
    +goog.provide('goog.i18n.NumberFormatSymbols_tn_BW');
    +goog.provide('goog.i18n.NumberFormatSymbols_tn_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_to');
    +goog.provide('goog.i18n.NumberFormatSymbols_to_TO');
    +goog.provide('goog.i18n.NumberFormatSymbols_tr_CY');
    +goog.provide('goog.i18n.NumberFormatSymbols_ts');
    +goog.provide('goog.i18n.NumberFormatSymbols_ts_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_twq');
    +goog.provide('goog.i18n.NumberFormatSymbols_twq_NE');
    +goog.provide('goog.i18n.NumberFormatSymbols_tzm');
    +goog.provide('goog.i18n.NumberFormatSymbols_tzm_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_tzm_Latn_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_ug');
    +goog.provide('goog.i18n.NumberFormatSymbols_ug_Arab');
    +goog.provide('goog.i18n.NumberFormatSymbols_ug_Arab_CN');
    +goog.provide('goog.i18n.NumberFormatSymbols_ur_IN');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Arab');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Arab_AF');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Cyrl');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Cyrl_UZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_uz_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_vai');
    +goog.provide('goog.i18n.NumberFormatSymbols_vai_Latn');
    +goog.provide('goog.i18n.NumberFormatSymbols_vai_Latn_LR');
    +goog.provide('goog.i18n.NumberFormatSymbols_vai_Vaii');
    +goog.provide('goog.i18n.NumberFormatSymbols_vai_Vaii_LR');
    +goog.provide('goog.i18n.NumberFormatSymbols_ve');
    +goog.provide('goog.i18n.NumberFormatSymbols_ve_ZA');
    +goog.provide('goog.i18n.NumberFormatSymbols_vo');
    +goog.provide('goog.i18n.NumberFormatSymbols_vo_001');
    +goog.provide('goog.i18n.NumberFormatSymbols_vun');
    +goog.provide('goog.i18n.NumberFormatSymbols_vun_TZ');
    +goog.provide('goog.i18n.NumberFormatSymbols_wae');
    +goog.provide('goog.i18n.NumberFormatSymbols_wae_CH');
    +goog.provide('goog.i18n.NumberFormatSymbols_xog');
    +goog.provide('goog.i18n.NumberFormatSymbols_xog_UG');
    +goog.provide('goog.i18n.NumberFormatSymbols_yav');
    +goog.provide('goog.i18n.NumberFormatSymbols_yav_CM');
    +goog.provide('goog.i18n.NumberFormatSymbols_yi');
    +goog.provide('goog.i18n.NumberFormatSymbols_yi_001');
    +goog.provide('goog.i18n.NumberFormatSymbols_yo');
    +goog.provide('goog.i18n.NumberFormatSymbols_yo_BJ');
    +goog.provide('goog.i18n.NumberFormatSymbols_yo_NG');
    +goog.provide('goog.i18n.NumberFormatSymbols_zgh');
    +goog.provide('goog.i18n.NumberFormatSymbols_zgh_MA');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hans');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hans_HK');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hans_MO');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hans_SG');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hant');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hant_HK');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hant_MO');
    +goog.provide('goog.i18n.NumberFormatSymbols_zh_Hant_TW');
    +
    +goog.require('goog.i18n.NumberFormatSymbols');
    +
    +
    +/**
    + * Number formatting symbols for locale aa.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_aa = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ETB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale aa_DJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_aa_DJ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'DJF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale aa_ER.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_aa_ER = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ERN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale aa_ET.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_aa_ET = goog.i18n.NumberFormatSymbols_aa;
    +
    +
    +/**
    + * Number formatting symbols for locale af_NA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_af_NA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'NAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale agq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_agq = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale agq_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_agq_CM = goog.i18n.NumberFormatSymbols_agq;
    +
    +
    +/**
    + * Number formatting symbols for locale ak.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ak = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GHS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ak_GH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ak_GH = goog.i18n.NumberFormatSymbols_ak;
    +
    +
    +/**
    + * Number formatting symbols for locale ar_AE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_AE = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'AED'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_BH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_BH = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'BHD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_DJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_DJ = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'DJF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_DZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_DZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'DZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_EG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_EG = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EGP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_EH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_EH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_ER.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_ER = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'ERN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_IL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_IL = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'ILS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_IQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_IQ = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'IQD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_JO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_JO = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'JOD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_KM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_KM = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'KMF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_KW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_KW = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'KWD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_LB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_LB = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'LBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_LY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_LY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'LYD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_MA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_MR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_MR = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MRO'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_OM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_OM = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'OMR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_PS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_PS = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'ILS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_QA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_QA = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'QAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_SA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_SA = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'SAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_SD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_SD = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'SDG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_SO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_SO = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'SOS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_SS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_SS = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'SSP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_SY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_SY = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'SYP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_TD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_TD = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_TN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_TN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '\u200E+',
    +  MINUS_SIGN: '\u200E-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'TND'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ar_YE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ar_YE = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#0.00',
    +  DEF_CURRENCY_CODE: 'YER'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale as.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_as = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u09E6',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale as_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_as_IN = goog.i18n.NumberFormatSymbols_as;
    +
    +
    +/**
    + * Number formatting symbols for locale asa.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_asa = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale asa_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_asa_TZ = goog.i18n.NumberFormatSymbols_asa;
    +
    +
    +/**
    + * Number formatting symbols for locale ast.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ast = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ast_ES.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ast_ES = goog.i18n.NumberFormatSymbols_ast;
    +
    +
    +/**
    + * Number formatting symbols for locale az_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_az_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale az_Cyrl_AZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_az_Cyrl_AZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'AZN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale az_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_az_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bas.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bas = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bas_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bas_CM = goog.i18n.NumberFormatSymbols_bas;
    +
    +
    +/**
    + * Number formatting symbols for locale be.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_be = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BYR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale be_BY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_be_BY = goog.i18n.NumberFormatSymbols_be;
    +
    +
    +/**
    + * Number formatting symbols for locale bem.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bem = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZMW'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bem_ZM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bem_ZM = goog.i18n.NumberFormatSymbols_bem;
    +
    +
    +/**
    + * Number formatting symbols for locale bez.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bez = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bez_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bez_TZ = goog.i18n.NumberFormatSymbols_bez;
    +
    +
    +/**
    + * Number formatting symbols for locale bm.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bm = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bm_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bm_Latn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bm_Latn_ML.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bm_Latn_ML = goog.i18n.NumberFormatSymbols_bm;
    +
    +
    +/**
    + * Number formatting symbols for locale bn_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bn_IN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u09E6',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u09B8\u0982\u0996\u09CD\u09AF\u09BE\u00A0\u09A8\u09BE',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '#,##,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'CNY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bo_CN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bo_CN = goog.i18n.NumberFormatSymbols_bo;
    +
    +
    +/**
    + * Number formatting symbols for locale bo_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bo_IN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale brx.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_brx = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale brx_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_brx_IN = goog.i18n.NumberFormatSymbols_brx;
    +
    +
    +/**
    + * Number formatting symbols for locale bs.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bs = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BAM'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bs_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bs_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bs_Cyrl_BA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bs_Cyrl_BA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BAM'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bs_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bs_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale bs_Latn_BA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_bs_Latn_BA = goog.i18n.NumberFormatSymbols_bs;
    +
    +
    +/**
    + * Number formatting symbols for locale cgg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cgg = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale cgg_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_cgg_UG = goog.i18n.NumberFormatSymbols_cgg;
    +
    +
    +/**
    + * Number formatting symbols for locale ckb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'IQD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_Arab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_Arab = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_Arab_IQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_Arab_IQ = goog.i18n.NumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_Arab_IR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_Arab_IR = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'IRR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_IQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_IQ = goog.i18n.NumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_IR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_IR = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'IRR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_Latn = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u0660',
    +  PLUS_SIGN: '\u200F+',
    +  MINUS_SIGN: '\u200F-',
    +  EXP_SYMBOL: '\u0627\u0633',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ckb_Latn_IQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ckb_Latn_IQ = goog.i18n.NumberFormatSymbols_ckb;
    +
    +
    +/**
    + * Number formatting symbols for locale dav.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dav = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dav_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dav_KE = goog.i18n.NumberFormatSymbols_dav;
    +
    +
    +/**
    + * Number formatting symbols for locale de_LI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_de_LI = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\'',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dje.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dje = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dje_NE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dje_NE = goog.i18n.NumberFormatSymbols_dje;
    +
    +
    +/**
    + * Number formatting symbols for locale dsb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dsb = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dsb_DE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dsb_DE = goog.i18n.NumberFormatSymbols_dsb;
    +
    +
    +/**
    + * Number formatting symbols for locale dua.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dua = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dua_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dua_CM = goog.i18n.NumberFormatSymbols_dua;
    +
    +
    +/**
    + * Number formatting symbols for locale dyo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dyo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dyo_SN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dyo_SN = goog.i18n.NumberFormatSymbols_dyo;
    +
    +
    +/**
    + * Number formatting symbols for locale dz.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dz = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u0F20',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u0F42\u0FB2\u0F44\u0F66\u0F0B\u0F58\u0F7A\u0F51',
    +  NAN: '\u0F68\u0F44\u0F0B\u0F58\u0F51',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'BTN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale dz_BT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_dz_BT = goog.i18n.NumberFormatSymbols_dz;
    +
    +
    +/**
    + * Number formatting symbols for locale ebu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ebu = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ebu_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ebu_KE = goog.i18n.NumberFormatSymbols_ebu;
    +
    +
    +/**
    + * Number formatting symbols for locale ee.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ee = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'mnn',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GHS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ee_GH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ee_GH = goog.i18n.NumberFormatSymbols_ee;
    +
    +
    +/**
    + * Number formatting symbols for locale ee_TG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ee_TG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'mnn',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale el_CY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_el_CY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'e',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_150.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_150 = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_AG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_AG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_AI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_AI = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BB = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BBD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BMD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BS = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BSD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BW = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BWP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_BZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_BZ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_CA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_CA = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'CAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_CC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_CC = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_CK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_CK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_CM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_CX.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_CX = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_DM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_DM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_ER.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_ER = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ERN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_FJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_FJ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'FJD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_FK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_FK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'FKP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GD = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GHS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GI = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GIP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GMD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_GY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_GY = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GYD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_HK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_HK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'HKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_IM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_IM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_JE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_JE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_JM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_JM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'JMD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_KE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_KI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_KI = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_KN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_KN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_KY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_KY = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KYD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_LC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_LC = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_LR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_LR = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'LRD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_LS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_LS = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MGA'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MO = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MS = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MT = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MU = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MW = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MWK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_MY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_MY = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MYR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NA = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NF = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NGN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NR = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NU = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_NZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_NZ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_PG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PGK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_PH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PHP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_PK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'PKR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_PN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_PN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_RW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_RW = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'RWF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SB = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SBD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SC = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SCR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SD = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SDG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SHP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SL = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SLL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SS = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SSP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SX.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SX = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ANG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_SZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_SZ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SZL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TO = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TT = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TTD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TV.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TV = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AUD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_TZ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_UG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_VC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_VC = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XCD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_VU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_VU = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'VUV'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_WS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_WS = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'WST'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale en_ZM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_en_ZM = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZMW'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale eo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_eo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale eo_001.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_eo_001 = goog.i18n.NumberFormatSymbols_eo;
    +
    +
    +/**
    + * Number formatting symbols for locale es_AR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_AR = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ARS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_BO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_BO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BOB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_CL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_CL = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'CLP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_CO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_CO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'COP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_CR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_CR = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'CRC'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_CU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_CU = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'CUP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_DO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_DO = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'DOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_EC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_EC = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_GQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_GQ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_GT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_GT = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GTQ'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_HN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_HN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'HNL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_MX.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_MX = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MXN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_NI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_NI = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NIO'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_PA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_PA = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PAB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_PE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_PE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'PEN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_PH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_PH = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'PHP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_PR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_PR = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_PY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_PY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0-#,##0.00',
    +  DEF_CURRENCY_CODE: 'PYG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_SV.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_SV = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_US.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_US = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_UY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_UY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'UYU'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale es_VE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_es_VE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'VEF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ewo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ewo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ewo_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ewo_CM = goog.i18n.NumberFormatSymbols_ewo;
    +
    +
    +/**
    + * Number formatting symbols for locale fa_AF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fa_AF = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E\u2212',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: '\u0646\u0627\u0639\u062F\u062F',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '\'\u202A\'#,##0%\'\u202C\'',
    +  CURRENCY_PATTERN: '\u200E\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'AFN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ff.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ff = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ff_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ff_CM = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ff_GN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ff_GN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'GNF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ff_MR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ff_MR = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MRO'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ff_SN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ff_SN = goog.i18n.NumberFormatSymbols_ff;
    +
    +
    +/**
    + * Number formatting symbols for locale fo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'DKK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fo_FO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fo_FO = goog.i18n.NumberFormatSymbols_fo;
    +
    +
    +/**
    + * Number formatting symbols for locale fr_BE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_BE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_BF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_BF = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_BI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_BI = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BIF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_BJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_BJ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CD = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CDF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CF = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CG = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CI = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_CM = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_DJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_DJ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'DJF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_DZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_DZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'DZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_GA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_GA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_GN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_GN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'GNF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_GQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_GQ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_HT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_HT = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'HTG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_KM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_KM = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'KMF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_LU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_LU = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MG = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MGA'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_ML.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_ML = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MR = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MRO'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_MU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_MU = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_NC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_NC = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XPF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_NE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_NE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_PF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_PF = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XPF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_RW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_RW = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'RWF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_SC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_SC = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'SCR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_SN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_SN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_SY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_SY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'SYP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_TD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_TD = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_TG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_TG = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_TN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_TN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'TND'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_VU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_VU = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'VUV'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fr_WF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fr_WF = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XPF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fur.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fur = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fur_IT.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fur_IT = goog.i18n.NumberFormatSymbols_fur;
    +
    +
    +/**
    + * Number formatting symbols for locale fy.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fy = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale fy_NL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_fy_NL = goog.i18n.NumberFormatSymbols_fy;
    +
    +
    +/**
    + * Number formatting symbols for locale gd.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gd = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale gd_GB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gd_GB = goog.i18n.NumberFormatSymbols_gd;
    +
    +
    +/**
    + * Number formatting symbols for locale gsw_FR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gsw_FR = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u2019',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale guz.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_guz = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale guz_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_guz_KE = goog.i18n.NumberFormatSymbols_guz;
    +
    +
    +/**
    + * Number formatting symbols for locale gv.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gv = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale gv_IM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_gv_IM = goog.i18n.NumberFormatSymbols_gv;
    +
    +
    +/**
    + * Number formatting symbols for locale ha.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ha = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'NGN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ha_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ha_Latn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ha_Latn_GH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ha_Latn_GH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'GHS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ha_Latn_NE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ha_Latn_NE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ha_Latn_NG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ha_Latn_NG = goog.i18n.NumberFormatSymbols_ha;
    +
    +
    +/**
    + * Number formatting symbols for locale hr_BA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hr_BA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BAM'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hsb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hsb = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale hsb_DE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_hsb_DE = goog.i18n.NumberFormatSymbols_hsb;
    +
    +
    +/**
    + * Number formatting symbols for locale ia.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ia = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ia_FR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ia_FR = goog.i18n.NumberFormatSymbols_ia;
    +
    +
    +/**
    + * Number formatting symbols for locale ig.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ig = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NGN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ig_NG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ig_NG = goog.i18n.NumberFormatSymbols_ig;
    +
    +
    +/**
    + * Number formatting symbols for locale ii.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ii = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'CNY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ii_CN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ii_CN = goog.i18n.NumberFormatSymbols_ii;
    +
    +
    +/**
    + * Number formatting symbols for locale it_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_it_CH = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\'',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale jgo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_jgo = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale jgo_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_jgo_CM = goog.i18n.NumberFormatSymbols_jgo;
    +
    +
    +/**
    + * Number formatting symbols for locale jmc.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_jmc = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale jmc_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_jmc_TZ = goog.i18n.NumberFormatSymbols_jmc;
    +
    +
    +/**
    + * Number formatting symbols for locale kab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kab = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'DZD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kab_DZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kab_DZ = goog.i18n.NumberFormatSymbols_kab;
    +
    +
    +/**
    + * Number formatting symbols for locale kam.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kam = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kam_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kam_KE = goog.i18n.NumberFormatSymbols_kam;
    +
    +
    +/**
    + * Number formatting symbols for locale kde.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kde = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kde_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kde_TZ = goog.i18n.NumberFormatSymbols_kde;
    +
    +
    +/**
    + * Number formatting symbols for locale kea.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kea = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CVE'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kea_CV.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kea_CV = goog.i18n.NumberFormatSymbols_kea;
    +
    +
    +/**
    + * Number formatting symbols for locale khq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_khq = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale khq_ML.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_khq_ML = goog.i18n.NumberFormatSymbols_khq;
    +
    +
    +/**
    + * Number formatting symbols for locale ki.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ki = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ki_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ki_KE = goog.i18n.NumberFormatSymbols_ki;
    +
    +
    +/**
    + * Number formatting symbols for locale kk_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kk_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kkj.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kkj = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kkj_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kkj_CM = goog.i18n.NumberFormatSymbols_kkj;
    +
    +
    +/**
    + * Number formatting symbols for locale kl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'DKK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kl_GL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kl_GL = goog.i18n.NumberFormatSymbols_kl;
    +
    +
    +/**
    + * Number formatting symbols for locale kln.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kln = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kln_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kln_KE = goog.i18n.NumberFormatSymbols_kln;
    +
    +
    +/**
    + * Number formatting symbols for locale ko_KP.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ko_KP = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KPW'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kok.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kok = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kok_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kok_IN = goog.i18n.NumberFormatSymbols_kok;
    +
    +
    +/**
    + * Number formatting symbols for locale ks.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ks = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ks_Arab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ks_Arab = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ks_Arab_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ks_Arab_IN = goog.i18n.NumberFormatSymbols_ks;
    +
    +
    +/**
    + * Number formatting symbols for locale ksb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksb = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ksb_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksb_TZ = goog.i18n.NumberFormatSymbols_ksb;
    +
    +
    +/**
    + * Number formatting symbols for locale ksf.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksf = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ksf_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksf_CM = goog.i18n.NumberFormatSymbols_ksf;
    +
    +
    +/**
    + * Number formatting symbols for locale ksh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksh = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ksh_DE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ksh_DE = goog.i18n.NumberFormatSymbols_ksh;
    +
    +
    +/**
    + * Number formatting symbols for locale kw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kw = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'GBP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale kw_GB.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_kw_GB = goog.i18n.NumberFormatSymbols_kw;
    +
    +
    +/**
    + * Number formatting symbols for locale ky_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ky_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u0441\u0430\u043D\u00A0\u044D\u043C\u0435\u0441',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lag.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lag = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lag_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lag_TZ = goog.i18n.NumberFormatSymbols_lag;
    +
    +
    +/**
    + * Number formatting symbols for locale lb.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lb = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lb_LU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lb_LU = goog.i18n.NumberFormatSymbols_lb;
    +
    +
    +/**
    + * Number formatting symbols for locale lg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lg = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lg_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lg_UG = goog.i18n.NumberFormatSymbols_lg;
    +
    +
    +/**
    + * Number formatting symbols for locale lkt.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lkt = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lkt_US.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lkt_US = goog.i18n.NumberFormatSymbols_lkt;
    +
    +
    +/**
    + * Number formatting symbols for locale ln_AO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ln_AO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'AOA'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ln_CF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ln_CF = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ln_CG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ln_CG = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lu = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'CDF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale lu_CD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_lu_CD = goog.i18n.NumberFormatSymbols_lu;
    +
    +
    +/**
    + * Number formatting symbols for locale luo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_luo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale luo_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_luo_KE = goog.i18n.NumberFormatSymbols_luo;
    +
    +
    +/**
    + * Number formatting symbols for locale luy.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_luy = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale luy_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_luy_KE = goog.i18n.NumberFormatSymbols_luy;
    +
    +
    +/**
    + * Number formatting symbols for locale mas.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mas = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mas_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mas_KE = goog.i18n.NumberFormatSymbols_mas;
    +
    +
    +/**
    + * Number formatting symbols for locale mas_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mas_TZ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mer.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mer = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mer_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mer_KE = goog.i18n.NumberFormatSymbols_mer;
    +
    +
    +/**
    + * Number formatting symbols for locale mfe.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mfe = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mfe_MU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mfe_MU = goog.i18n.NumberFormatSymbols_mfe;
    +
    +
    +/**
    + * Number formatting symbols for locale mg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mg = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MGA'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mg_MG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mg_MG = goog.i18n.NumberFormatSymbols_mg;
    +
    +
    +/**
    + * Number formatting symbols for locale mgh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mgh = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MZN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mgh_MZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mgh_MZ = goog.i18n.NumberFormatSymbols_mgh;
    +
    +
    +/**
    + * Number formatting symbols for locale mgo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mgo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mgo_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mgo_CM = goog.i18n.NumberFormatSymbols_mgo;
    +
    +
    +/**
    + * Number formatting symbols for locale mn_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mn_Cyrl = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ms_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ms_Latn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ms_Latn_BN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ms_Latn_BN = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'BND'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ms_Latn_SG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ms_Latn_SG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SGD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mua.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mua = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale mua_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_mua_CM = goog.i18n.NumberFormatSymbols_mua;
    +
    +
    +/**
    + * Number formatting symbols for locale naq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_naq = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale naq_NA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_naq_NA = goog.i18n.NumberFormatSymbols_naq;
    +
    +
    +/**
    + * Number formatting symbols for locale nd.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nd = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nd_ZW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nd_ZW = goog.i18n.NumberFormatSymbols_nd;
    +
    +
    +/**
    + * Number formatting symbols for locale ne_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ne_IN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u0966',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_AW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_AW = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'AWG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_BE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_BE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_BQ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_BQ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_CW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_CW = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'ANG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_SR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_SR = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'SRD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nl_SX.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nl_SX = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
    +  DEF_CURRENCY_CODE: 'ANG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nmg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nmg = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nmg_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nmg_CM = goog.i18n.NumberFormatSymbols_nmg;
    +
    +
    +/**
    + * Number formatting symbols for locale nn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'NOK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nn_NO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nn_NO = goog.i18n.NumberFormatSymbols_nn;
    +
    +
    +/**
    + * Number formatting symbols for locale nnh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nnh = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nnh_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nnh_CM = goog.i18n.NumberFormatSymbols_nnh;
    +
    +
    +/**
    + * Number formatting symbols for locale nr.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nr = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nr_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nr_ZA = goog.i18n.NumberFormatSymbols_nr;
    +
    +
    +/**
    + * Number formatting symbols for locale nso.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nso = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nso_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nso_ZA = goog.i18n.NumberFormatSymbols_nso;
    +
    +
    +/**
    + * Number formatting symbols for locale nus.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nus = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SDG'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nus_SD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nus_SD = goog.i18n.NumberFormatSymbols_nus;
    +
    +
    +/**
    + * Number formatting symbols for locale nyn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nyn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale nyn_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_nyn_UG = goog.i18n.NumberFormatSymbols_nyn;
    +
    +
    +/**
    + * Number formatting symbols for locale om.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_om = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ETB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale om_ET.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_om_ET = goog.i18n.NumberFormatSymbols_om;
    +
    +
    +/**
    + * Number formatting symbols for locale om_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_om_KE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale os.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_os = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u041D\u041D',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'GEL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale os_GE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_os_GE = goog.i18n.NumberFormatSymbols_os;
    +
    +
    +/**
    + * Number formatting symbols for locale os_RU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_os_RU = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u041D\u041D',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'RUB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pa_Arab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pa_Arab = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'PKR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pa_Arab_PK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pa_Arab_PK =
    +    goog.i18n.NumberFormatSymbols_pa_Arab;
    +
    +
    +/**
    + * Number formatting symbols for locale pa_Guru.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pa_Guru = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '[#E0]',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ps.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ps = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'AFN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ps_AF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ps_AF = goog.i18n.NumberFormatSymbols_ps;
    +
    +
    +/**
    + * Number formatting symbols for locale pt_AO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_AO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'AOA'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_CV.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_CV = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CVE'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_GW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_GW = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_MO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_MO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_MZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_MZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MZN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_ST.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_ST = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'STD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale pt_TL.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_pt_TL = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale qu.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_qu = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'PEN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale qu_BO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_qu_BO = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'BOB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale qu_EC.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_qu_EC = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale qu_PE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_qu_PE = goog.i18n.NumberFormatSymbols_qu;
    +
    +
    +/**
    + * Number formatting symbols for locale rm.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rm = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u2019',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rm_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rm_CH = goog.i18n.NumberFormatSymbols_rm;
    +
    +
    +/**
    + * Number formatting symbols for locale rn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'BIF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rn_BI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rn_BI = goog.i18n.NumberFormatSymbols_rn;
    +
    +
    +/**
    + * Number formatting symbols for locale ro_MD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ro_MD = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MDL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rof.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rof = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rof_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rof_TZ = goog.i18n.NumberFormatSymbols_rof;
    +
    +
    +/**
    + * Number formatting symbols for locale ru_BY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_BY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BYR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ru_KG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_KG = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'KGS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ru_KZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_KZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'KZT'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ru_MD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_MD = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MDL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ru_UA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ru_UA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'UAH'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rw.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rw = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'RWF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rw_RW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rw_RW = goog.i18n.NumberFormatSymbols_rw;
    +
    +
    +/**
    + * Number formatting symbols for locale rwk.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rwk = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale rwk_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_rwk_TZ = goog.i18n.NumberFormatSymbols_rwk;
    +
    +
    +/**
    + * Number formatting symbols for locale sah.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sah = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'RUB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sah_RU.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sah_RU = goog.i18n.NumberFormatSymbols_sah;
    +
    +
    +/**
    + * Number formatting symbols for locale saq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_saq = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale saq_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_saq_KE = goog.i18n.NumberFormatSymbols_saq;
    +
    +
    +/**
    + * Number formatting symbols for locale sbp.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sbp = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sbp_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sbp_TZ = goog.i18n.NumberFormatSymbols_sbp;
    +
    +
    +/**
    + * Number formatting symbols for locale se.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_se = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'NOK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale se_FI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_se_FI = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale se_NO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_se_NO = goog.i18n.NumberFormatSymbols_se;
    +
    +
    +/**
    + * Number formatting symbols for locale se_SE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_se_SE = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'SEK'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale seh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_seh = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'MZN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale seh_MZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_seh_MZ = goog.i18n.NumberFormatSymbols_seh;
    +
    +
    +/**
    + * Number formatting symbols for locale ses.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ses = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ses_ML.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ses_ML = goog.i18n.NumberFormatSymbols_ses;
    +
    +
    +/**
    + * Number formatting symbols for locale sg.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sg = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sg_CF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sg_CF = goog.i18n.NumberFormatSymbols_sg;
    +
    +
    +/**
    + * Number formatting symbols for locale shi.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_shi = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale shi_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_shi_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale shi_Latn_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_shi_Latn_MA = goog.i18n.NumberFormatSymbols_shi;
    +
    +
    +/**
    + * Number formatting symbols for locale shi_Tfng.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_shi_Tfng = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale shi_Tfng_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_shi_Tfng_MA = goog.i18n.NumberFormatSymbols_shi;
    +
    +
    +/**
    + * Number formatting symbols for locale smn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_smn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'epiloho',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale smn_FI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_smn_FI = goog.i18n.NumberFormatSymbols_smn;
    +
    +
    +/**
    + * Number formatting symbols for locale sn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sn_ZW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sn_ZW = goog.i18n.NumberFormatSymbols_sn;
    +
    +
    +/**
    + * Number formatting symbols for locale so.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_so = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SOS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale so_DJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_so_DJ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'DJF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale so_ET.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_so_ET = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ETB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale so_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_so_KE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale so_SO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_so_SO = goog.i18n.NumberFormatSymbols_so;
    +
    +
    +/**
    + * Number formatting symbols for locale sq_MK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sq_MK = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sq_XK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sq_XK = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Cyrl_BA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Cyrl_BA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BAM'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Cyrl_ME.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Cyrl_ME =
    +    goog.i18n.NumberFormatSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Cyrl_XK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Cyrl_XK =
    +    goog.i18n.NumberFormatSymbols_sr_Cyrl;
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'RSD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Latn_BA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Latn_BA = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'BAM'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Latn_ME.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Latn_ME = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Latn_RS.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Latn_RS =
    +    goog.i18n.NumberFormatSymbols_sr_Latn;
    +
    +
    +/**
    + * Number formatting symbols for locale sr_Latn_XK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sr_Latn_XK = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ss.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ss = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ss_SZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ss_SZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SZL'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ss_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ss_ZA = goog.i18n.NumberFormatSymbols_ss;
    +
    +
    +/**
    + * Number formatting symbols for locale ssy.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ssy = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ERN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ssy_ER.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ssy_ER = goog.i18n.NumberFormatSymbols_ssy;
    +
    +
    +/**
    + * Number formatting symbols for locale sv_AX.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sv_AX = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sv_FI.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sv_FI = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '\u2212',
    +  EXP_SYMBOL: '\u00D710^',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u00A4\u00A4\u00A4',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sw_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sw_KE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale sw_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_sw_UG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale swc.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_swc = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'CDF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale swc_CD.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_swc_CD = goog.i18n.NumberFormatSymbols_swc;
    +
    +
    +/**
    + * Number formatting symbols for locale ta_LK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ta_LK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'LKR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ta_MY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ta_MY = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'MYR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ta_SG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ta_SG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'SGD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale teo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_teo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale teo_KE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_teo_KE = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'KES'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale teo_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_teo_UG = goog.i18n.NumberFormatSymbols_teo;
    +
    +
    +/**
    + * Number formatting symbols for locale ti.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ti = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ETB'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ti_ER.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ti_ER = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ERN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ti_ET.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ti_ET = goog.i18n.NumberFormatSymbols_ti;
    +
    +
    +/**
    + * Number formatting symbols for locale tn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tn_BW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tn_BW = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'BWP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tn_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tn_ZA = goog.i18n.NumberFormatSymbols_tn;
    +
    +
    +/**
    + * Number formatting symbols for locale to.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_to = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'TF',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'TOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale to_TO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_to_TO = goog.i18n.NumberFormatSymbols_to;
    +
    +
    +/**
    + * Number formatting symbols for locale tr_CY.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tr_CY = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '.',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '%#,##0',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ts.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ts = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ts_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ts_ZA = goog.i18n.NumberFormatSymbols_ts;
    +
    +
    +/**
    + * Number formatting symbols for locale twq.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_twq = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale twq_NE.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_twq_NE = goog.i18n.NumberFormatSymbols_twq;
    +
    +
    +/**
    + * Number formatting symbols for locale tzm.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tzm = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tzm_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tzm_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale tzm_Latn_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_tzm_Latn_MA = goog.i18n.NumberFormatSymbols_tzm;
    +
    +
    +/**
    + * Number formatting symbols for locale ug.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ug = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'CNY'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ug_Arab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ug_Arab = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ug_Arab_CN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ug_Arab_CN = goog.i18n.NumberFormatSymbols_ug;
    +
    +
    +/**
    + * Number formatting symbols for locale ur_IN.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ur_IN = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u0642',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u06CC\u06C1\u00A0\u0639\u062F\u062F\u00A0\u0646\u06C1\u06CC\u06BA',
    +  DECIMAL_PATTERN: '#,##,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
    +  DEF_CURRENCY_CODE: 'INR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Arab.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Arab = {
    +  DECIMAL_SEP: '\u066B',
    +  GROUP_SEP: '\u066C',
    +  PERCENT: '\u066A',
    +  ZERO_DIGIT: '\u06F0',
    +  PLUS_SIGN: '\u200E+\u200E',
    +  MINUS_SIGN: '\u200E-\u200E',
    +  EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
    +  PERMILL: '\u0609',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'AFN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Arab_AF.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Arab_AF =
    +    goog.i18n.NumberFormatSymbols_uz_Arab;
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Cyrl.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Cyrl = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Cyrl_UZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Cyrl_UZ = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'UZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale uz_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_uz_Latn = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vai.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vai = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'LRD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vai_Latn.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vai_Latn = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vai_Latn_LR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vai_Latn_LR = goog.i18n.NumberFormatSymbols_vai;
    +
    +
    +/**
    + * Number formatting symbols for locale vai_Vaii.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vai_Vaii = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vai_Vaii_LR.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vai_Vaii_LR = goog.i18n.NumberFormatSymbols_vai;
    +
    +
    +/**
    + * Number formatting symbols for locale ve.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ve = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'ZAR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale ve_ZA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_ve_ZA = goog.i18n.NumberFormatSymbols_ve;
    +
    +
    +/**
    + * Number formatting symbols for locale vo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vo_001.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vo_001 = goog.i18n.NumberFormatSymbols_vo;
    +
    +
    +/**
    + * Number formatting symbols for locale vun.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vun = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TZS'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale vun_TZ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_vun_TZ = goog.i18n.NumberFormatSymbols_vun;
    +
    +
    +/**
    + * Number formatting symbols for locale wae.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_wae = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u2019',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'CHF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale wae_CH.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_wae_CH = goog.i18n.NumberFormatSymbols_wae;
    +
    +
    +/**
    + * Number formatting symbols for locale xog.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_xog = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'UGX'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale xog_UG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_xog_UG = goog.i18n.NumberFormatSymbols_xog;
    +
    +
    +/**
    + * Number formatting symbols for locale yav.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yav = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
    +  DEF_CURRENCY_CODE: 'XAF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale yav_CM.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yav_CM = goog.i18n.NumberFormatSymbols_yav;
    +
    +
    +/**
    + * Number formatting symbols for locale yi.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yi = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'USD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale yi_001.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yi_001 = goog.i18n.NumberFormatSymbols_yi;
    +
    +
    +/**
    + * Number formatting symbols for locale yo.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yo = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'NGN'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale yo_BJ.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yo_BJ = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'XOF'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale yo_NG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_yo_NG = goog.i18n.NumberFormatSymbols_yo;
    +
    +
    +/**
    + * Number formatting symbols for locale zgh.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zgh = {
    +  DECIMAL_SEP: ',',
    +  GROUP_SEP: '\u00A0',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0\u00A0%',
    +  CURRENCY_PATTERN: '#,##0.00\u00A4',
    +  DEF_CURRENCY_CODE: 'MAD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zgh_MA.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zgh_MA = goog.i18n.NumberFormatSymbols_zgh;
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hans.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hans = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
    +  DEF_CURRENCY_CODE: 'EUR'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hans_HK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hans_HK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'HKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hans_MO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hans_MO = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hans_SG.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hans_SG = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: 'NaN',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'SGD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hant.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hant = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u975E\u6578\u503C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'TWD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hant_HK.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hant_HK = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u975E\u6578\u503C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'HKD'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hant_MO.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hant_MO = {
    +  DECIMAL_SEP: '.',
    +  GROUP_SEP: ',',
    +  PERCENT: '%',
    +  ZERO_DIGIT: '0',
    +  PLUS_SIGN: '+',
    +  MINUS_SIGN: '-',
    +  EXP_SYMBOL: 'E',
    +  PERMILL: '\u2030',
    +  INFINITY: '\u221E',
    +  NAN: '\u975E\u6578\u503C',
    +  DECIMAL_PATTERN: '#,##0.###',
    +  SCIENTIFIC_PATTERN: '#E0',
    +  PERCENT_PATTERN: '#,##0%',
    +  CURRENCY_PATTERN: '\u00A4#,##0.00',
    +  DEF_CURRENCY_CODE: 'MOP'
    +};
    +
    +
    +/**
    + * Number formatting symbols for locale zh_Hant_TW.
    + * @enum {string}
    + */
    +goog.i18n.NumberFormatSymbols_zh_Hant_TW =
    +    goog.i18n.NumberFormatSymbols_zh_Hant;
    +
    +
    +/**
    + * Selected number formatting symbols by locale.
    + */
    +
    +if (goog.LOCALE == 'aa') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'aa_DJ' || goog.LOCALE == 'aa-DJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_aa_DJ;
    +}
    +
    +if (goog.LOCALE == 'aa_ER' || goog.LOCALE == 'aa-ER') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_aa_ER;
    +}
    +
    +if (goog.LOCALE == 'aa_ET' || goog.LOCALE == 'aa-ET') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_aa;
    +}
    +
    +if (goog.LOCALE == 'af_NA' || goog.LOCALE == 'af-NA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_af_NA;
    +}
    +
    +if (goog.LOCALE == 'agq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'agq_CM' || goog.LOCALE == 'agq-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'ak') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ak_GH' || goog.LOCALE == 'ak-GH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ar_AE' || goog.LOCALE == 'ar-AE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_AE;
    +}
    +
    +if (goog.LOCALE == 'ar_BH' || goog.LOCALE == 'ar-BH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_BH;
    +}
    +
    +if (goog.LOCALE == 'ar_DJ' || goog.LOCALE == 'ar-DJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_DJ;
    +}
    +
    +if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_DZ;
    +}
    +
    +if (goog.LOCALE == 'ar_EG' || goog.LOCALE == 'ar-EG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_EG;
    +}
    +
    +if (goog.LOCALE == 'ar_EH' || goog.LOCALE == 'ar-EH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_EH;
    +}
    +
    +if (goog.LOCALE == 'ar_ER' || goog.LOCALE == 'ar-ER') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_ER;
    +}
    +
    +if (goog.LOCALE == 'ar_IL' || goog.LOCALE == 'ar-IL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_IL;
    +}
    +
    +if (goog.LOCALE == 'ar_IQ' || goog.LOCALE == 'ar-IQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_IQ;
    +}
    +
    +if (goog.LOCALE == 'ar_JO' || goog.LOCALE == 'ar-JO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_JO;
    +}
    +
    +if (goog.LOCALE == 'ar_KM' || goog.LOCALE == 'ar-KM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_KM;
    +}
    +
    +if (goog.LOCALE == 'ar_KW' || goog.LOCALE == 'ar-KW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_KW;
    +}
    +
    +if (goog.LOCALE == 'ar_LB' || goog.LOCALE == 'ar-LB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_LB;
    +}
    +
    +if (goog.LOCALE == 'ar_LY' || goog.LOCALE == 'ar-LY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_LY;
    +}
    +
    +if (goog.LOCALE == 'ar_MA' || goog.LOCALE == 'ar-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_MA;
    +}
    +
    +if (goog.LOCALE == 'ar_MR' || goog.LOCALE == 'ar-MR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_MR;
    +}
    +
    +if (goog.LOCALE == 'ar_OM' || goog.LOCALE == 'ar-OM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_OM;
    +}
    +
    +if (goog.LOCALE == 'ar_PS' || goog.LOCALE == 'ar-PS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_PS;
    +}
    +
    +if (goog.LOCALE == 'ar_QA' || goog.LOCALE == 'ar-QA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_QA;
    +}
    +
    +if (goog.LOCALE == 'ar_SA' || goog.LOCALE == 'ar-SA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_SA;
    +}
    +
    +if (goog.LOCALE == 'ar_SD' || goog.LOCALE == 'ar-SD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_SD;
    +}
    +
    +if (goog.LOCALE == 'ar_SO' || goog.LOCALE == 'ar-SO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_SO;
    +}
    +
    +if (goog.LOCALE == 'ar_SS' || goog.LOCALE == 'ar-SS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_SS;
    +}
    +
    +if (goog.LOCALE == 'ar_SY' || goog.LOCALE == 'ar-SY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_SY;
    +}
    +
    +if (goog.LOCALE == 'ar_TD' || goog.LOCALE == 'ar-TD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_TD;
    +}
    +
    +if (goog.LOCALE == 'ar_TN' || goog.LOCALE == 'ar-TN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_TN;
    +}
    +
    +if (goog.LOCALE == 'ar_YE' || goog.LOCALE == 'ar-YE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ar_YE;
    +}
    +
    +if (goog.LOCALE == 'as') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'as_IN' || goog.LOCALE == 'as-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'asa') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'asa_TZ' || goog.LOCALE == 'asa-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'ast') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'ast_ES' || goog.LOCALE == 'ast-ES') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ast;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl' || goog.LOCALE == 'az-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az_Cyrl_AZ;
    +}
    +
    +if (goog.LOCALE == 'az_Latn' || goog.LOCALE == 'az-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'bas') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'bas_CM' || goog.LOCALE == 'bas-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'be') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'be_BY' || goog.LOCALE == 'be-BY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'bem') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bem_ZM' || goog.LOCALE == 'bem-ZM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bez') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bez_TZ' || goog.LOCALE == 'bez-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bm') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn' || goog.LOCALE == 'bm-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bm_Latn;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn_ML' || goog.LOCALE == 'bm-Latn-ML') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bn_IN' || goog.LOCALE == 'bn-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bn_IN;
    +}
    +
    +if (goog.LOCALE == 'bo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_CN' || goog.LOCALE == 'bo-CN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_IN' || goog.LOCALE == 'bo-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bo_IN;
    +}
    +
    +if (goog.LOCALE == 'brx') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'brx_IN' || goog.LOCALE == 'brx-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'bs') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl' || goog.LOCALE == 'bs-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl_BA' || goog.LOCALE == 'bs-Cyrl-BA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bs_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn' || goog.LOCALE == 'bs-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bs_Latn;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn_BA' || goog.LOCALE == 'bs-Latn-BA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'cgg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'cgg_UG' || goog.LOCALE == 'cgg-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'ckb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab' || goog.LOCALE == 'ckb-Arab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb_Arab;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IQ' || goog.LOCALE == 'ckb-Arab-IQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_Arab_IR' || goog.LOCALE == 'ckb-Arab-IR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb_Arab_IR;
    +}
    +
    +if (goog.LOCALE == 'ckb_IQ' || goog.LOCALE == 'ckb-IQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'ckb_IR' || goog.LOCALE == 'ckb-IR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb_IR;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn' || goog.LOCALE == 'ckb-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb_Latn;
    +}
    +
    +if (goog.LOCALE == 'ckb_Latn_IQ' || goog.LOCALE == 'ckb-Latn-IQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ckb;
    +}
    +
    +if (goog.LOCALE == 'dav') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'dav_KE' || goog.LOCALE == 'dav-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'de_LI' || goog.LOCALE == 'de-LI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_de_LI;
    +}
    +
    +if (goog.LOCALE == 'dje') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dje_NE' || goog.LOCALE == 'dje-NE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dsb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dsb_DE' || goog.LOCALE == 'dsb-DE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dua') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dua_CM' || goog.LOCALE == 'dua-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dyo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dyo_SN' || goog.LOCALE == 'dyo-SN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dz') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'dz_BT' || goog.LOCALE == 'dz-BT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'ebu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ebu_KE' || goog.LOCALE == 'ebu-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ee') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_GH' || goog.LOCALE == 'ee-GH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_TG' || goog.LOCALE == 'ee-TG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ee_TG;
    +}
    +
    +if (goog.LOCALE == 'el_CY' || goog.LOCALE == 'el-CY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_el_CY;
    +}
    +
    +if (goog.LOCALE == 'en_150' || goog.LOCALE == 'en-150') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_150;
    +}
    +
    +if (goog.LOCALE == 'en_AG' || goog.LOCALE == 'en-AG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_AG;
    +}
    +
    +if (goog.LOCALE == 'en_AI' || goog.LOCALE == 'en-AI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_AI;
    +}
    +
    +if (goog.LOCALE == 'en_BB' || goog.LOCALE == 'en-BB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BB;
    +}
    +
    +if (goog.LOCALE == 'en_BE' || goog.LOCALE == 'en-BE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BE;
    +}
    +
    +if (goog.LOCALE == 'en_BM' || goog.LOCALE == 'en-BM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BM;
    +}
    +
    +if (goog.LOCALE == 'en_BS' || goog.LOCALE == 'en-BS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BS;
    +}
    +
    +if (goog.LOCALE == 'en_BW' || goog.LOCALE == 'en-BW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BW;
    +}
    +
    +if (goog.LOCALE == 'en_BZ' || goog.LOCALE == 'en-BZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_BZ;
    +}
    +
    +if (goog.LOCALE == 'en_CA' || goog.LOCALE == 'en-CA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_CA;
    +}
    +
    +if (goog.LOCALE == 'en_CC' || goog.LOCALE == 'en-CC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_CC;
    +}
    +
    +if (goog.LOCALE == 'en_CK' || goog.LOCALE == 'en-CK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_CK;
    +}
    +
    +if (goog.LOCALE == 'en_CM' || goog.LOCALE == 'en-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_CM;
    +}
    +
    +if (goog.LOCALE == 'en_CX' || goog.LOCALE == 'en-CX') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_CX;
    +}
    +
    +if (goog.LOCALE == 'en_DM' || goog.LOCALE == 'en-DM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_DM;
    +}
    +
    +if (goog.LOCALE == 'en_ER' || goog.LOCALE == 'en-ER') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_ER;
    +}
    +
    +if (goog.LOCALE == 'en_FJ' || goog.LOCALE == 'en-FJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_FJ;
    +}
    +
    +if (goog.LOCALE == 'en_FK' || goog.LOCALE == 'en-FK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_FK;
    +}
    +
    +if (goog.LOCALE == 'en_GD' || goog.LOCALE == 'en-GD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GD;
    +}
    +
    +if (goog.LOCALE == 'en_GG' || goog.LOCALE == 'en-GG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GG;
    +}
    +
    +if (goog.LOCALE == 'en_GH' || goog.LOCALE == 'en-GH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GH;
    +}
    +
    +if (goog.LOCALE == 'en_GI' || goog.LOCALE == 'en-GI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GI;
    +}
    +
    +if (goog.LOCALE == 'en_GM' || goog.LOCALE == 'en-GM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GM;
    +}
    +
    +if (goog.LOCALE == 'en_GY' || goog.LOCALE == 'en-GY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_GY;
    +}
    +
    +if (goog.LOCALE == 'en_HK' || goog.LOCALE == 'en-HK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_HK;
    +}
    +
    +if (goog.LOCALE == 'en_IM' || goog.LOCALE == 'en-IM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_IM;
    +}
    +
    +if (goog.LOCALE == 'en_JE' || goog.LOCALE == 'en-JE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_JE;
    +}
    +
    +if (goog.LOCALE == 'en_JM' || goog.LOCALE == 'en-JM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_JM;
    +}
    +
    +if (goog.LOCALE == 'en_KE' || goog.LOCALE == 'en-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_KE;
    +}
    +
    +if (goog.LOCALE == 'en_KI' || goog.LOCALE == 'en-KI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_KI;
    +}
    +
    +if (goog.LOCALE == 'en_KN' || goog.LOCALE == 'en-KN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_KN;
    +}
    +
    +if (goog.LOCALE == 'en_KY' || goog.LOCALE == 'en-KY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_KY;
    +}
    +
    +if (goog.LOCALE == 'en_LC' || goog.LOCALE == 'en-LC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_LC;
    +}
    +
    +if (goog.LOCALE == 'en_LR' || goog.LOCALE == 'en-LR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_LR;
    +}
    +
    +if (goog.LOCALE == 'en_LS' || goog.LOCALE == 'en-LS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_LS;
    +}
    +
    +if (goog.LOCALE == 'en_MG' || goog.LOCALE == 'en-MG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MG;
    +}
    +
    +if (goog.LOCALE == 'en_MO' || goog.LOCALE == 'en-MO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MO;
    +}
    +
    +if (goog.LOCALE == 'en_MS' || goog.LOCALE == 'en-MS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MS;
    +}
    +
    +if (goog.LOCALE == 'en_MT' || goog.LOCALE == 'en-MT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MT;
    +}
    +
    +if (goog.LOCALE == 'en_MU' || goog.LOCALE == 'en-MU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MU;
    +}
    +
    +if (goog.LOCALE == 'en_MW' || goog.LOCALE == 'en-MW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MW;
    +}
    +
    +if (goog.LOCALE == 'en_MY' || goog.LOCALE == 'en-MY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_MY;
    +}
    +
    +if (goog.LOCALE == 'en_NA' || goog.LOCALE == 'en-NA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NA;
    +}
    +
    +if (goog.LOCALE == 'en_NF' || goog.LOCALE == 'en-NF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NF;
    +}
    +
    +if (goog.LOCALE == 'en_NG' || goog.LOCALE == 'en-NG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NG;
    +}
    +
    +if (goog.LOCALE == 'en_NR' || goog.LOCALE == 'en-NR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NR;
    +}
    +
    +if (goog.LOCALE == 'en_NU' || goog.LOCALE == 'en-NU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NU;
    +}
    +
    +if (goog.LOCALE == 'en_NZ' || goog.LOCALE == 'en-NZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_NZ;
    +}
    +
    +if (goog.LOCALE == 'en_PG' || goog.LOCALE == 'en-PG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_PG;
    +}
    +
    +if (goog.LOCALE == 'en_PH' || goog.LOCALE == 'en-PH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_PH;
    +}
    +
    +if (goog.LOCALE == 'en_PK' || goog.LOCALE == 'en-PK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_PK;
    +}
    +
    +if (goog.LOCALE == 'en_PN' || goog.LOCALE == 'en-PN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_PN;
    +}
    +
    +if (goog.LOCALE == 'en_RW' || goog.LOCALE == 'en-RW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_RW;
    +}
    +
    +if (goog.LOCALE == 'en_SB' || goog.LOCALE == 'en-SB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SB;
    +}
    +
    +if (goog.LOCALE == 'en_SC' || goog.LOCALE == 'en-SC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SC;
    +}
    +
    +if (goog.LOCALE == 'en_SD' || goog.LOCALE == 'en-SD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SD;
    +}
    +
    +if (goog.LOCALE == 'en_SH' || goog.LOCALE == 'en-SH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SH;
    +}
    +
    +if (goog.LOCALE == 'en_SL' || goog.LOCALE == 'en-SL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SL;
    +}
    +
    +if (goog.LOCALE == 'en_SS' || goog.LOCALE == 'en-SS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SS;
    +}
    +
    +if (goog.LOCALE == 'en_SX' || goog.LOCALE == 'en-SX') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SX;
    +}
    +
    +if (goog.LOCALE == 'en_SZ' || goog.LOCALE == 'en-SZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_SZ;
    +}
    +
    +if (goog.LOCALE == 'en_TK' || goog.LOCALE == 'en-TK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_TK;
    +}
    +
    +if (goog.LOCALE == 'en_TO' || goog.LOCALE == 'en-TO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_TO;
    +}
    +
    +if (goog.LOCALE == 'en_TT' || goog.LOCALE == 'en-TT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_TT;
    +}
    +
    +if (goog.LOCALE == 'en_TV' || goog.LOCALE == 'en-TV') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_TV;
    +}
    +
    +if (goog.LOCALE == 'en_TZ' || goog.LOCALE == 'en-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_TZ;
    +}
    +
    +if (goog.LOCALE == 'en_UG' || goog.LOCALE == 'en-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_UG;
    +}
    +
    +if (goog.LOCALE == 'en_VC' || goog.LOCALE == 'en-VC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_VC;
    +}
    +
    +if (goog.LOCALE == 'en_VU' || goog.LOCALE == 'en-VU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_VU;
    +}
    +
    +if (goog.LOCALE == 'en_WS' || goog.LOCALE == 'en-WS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_WS;
    +}
    +
    +if (goog.LOCALE == 'en_ZM' || goog.LOCALE == 'en-ZM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_en_ZM;
    +}
    +
    +if (goog.LOCALE == 'eo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'eo_001' || goog.LOCALE == 'eo-001') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'es_AR' || goog.LOCALE == 'es-AR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_AR;
    +}
    +
    +if (goog.LOCALE == 'es_BO' || goog.LOCALE == 'es-BO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_BO;
    +}
    +
    +if (goog.LOCALE == 'es_CL' || goog.LOCALE == 'es-CL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_CL;
    +}
    +
    +if (goog.LOCALE == 'es_CO' || goog.LOCALE == 'es-CO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_CO;
    +}
    +
    +if (goog.LOCALE == 'es_CR' || goog.LOCALE == 'es-CR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_CR;
    +}
    +
    +if (goog.LOCALE == 'es_CU' || goog.LOCALE == 'es-CU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_CU;
    +}
    +
    +if (goog.LOCALE == 'es_DO' || goog.LOCALE == 'es-DO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_DO;
    +}
    +
    +if (goog.LOCALE == 'es_EC' || goog.LOCALE == 'es-EC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_EC;
    +}
    +
    +if (goog.LOCALE == 'es_GQ' || goog.LOCALE == 'es-GQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_GQ;
    +}
    +
    +if (goog.LOCALE == 'es_GT' || goog.LOCALE == 'es-GT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_GT;
    +}
    +
    +if (goog.LOCALE == 'es_HN' || goog.LOCALE == 'es-HN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_HN;
    +}
    +
    +if (goog.LOCALE == 'es_MX' || goog.LOCALE == 'es-MX') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_MX;
    +}
    +
    +if (goog.LOCALE == 'es_NI' || goog.LOCALE == 'es-NI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_NI;
    +}
    +
    +if (goog.LOCALE == 'es_PA' || goog.LOCALE == 'es-PA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_PA;
    +}
    +
    +if (goog.LOCALE == 'es_PE' || goog.LOCALE == 'es-PE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_PE;
    +}
    +
    +if (goog.LOCALE == 'es_PH' || goog.LOCALE == 'es-PH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_PH;
    +}
    +
    +if (goog.LOCALE == 'es_PR' || goog.LOCALE == 'es-PR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_PR;
    +}
    +
    +if (goog.LOCALE == 'es_PY' || goog.LOCALE == 'es-PY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_PY;
    +}
    +
    +if (goog.LOCALE == 'es_SV' || goog.LOCALE == 'es-SV') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_SV;
    +}
    +
    +if (goog.LOCALE == 'es_US' || goog.LOCALE == 'es-US') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_US;
    +}
    +
    +if (goog.LOCALE == 'es_UY' || goog.LOCALE == 'es-UY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_UY;
    +}
    +
    +if (goog.LOCALE == 'es_VE' || goog.LOCALE == 'es-VE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_es_VE;
    +}
    +
    +if (goog.LOCALE == 'ewo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'ewo_CM' || goog.LOCALE == 'ewo-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'fa_AF' || goog.LOCALE == 'fa-AF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fa_AF;
    +}
    +
    +if (goog.LOCALE == 'ff') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_CM' || goog.LOCALE == 'ff-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ff_CM;
    +}
    +
    +if (goog.LOCALE == 'ff_GN' || goog.LOCALE == 'ff-GN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ff_GN;
    +}
    +
    +if (goog.LOCALE == 'ff_MR' || goog.LOCALE == 'ff-MR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ff_MR;
    +}
    +
    +if (goog.LOCALE == 'ff_SN' || goog.LOCALE == 'ff-SN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'fo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fo_FO' || goog.LOCALE == 'fo-FO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fr_BE' || goog.LOCALE == 'fr-BE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_BE;
    +}
    +
    +if (goog.LOCALE == 'fr_BF' || goog.LOCALE == 'fr-BF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_BF;
    +}
    +
    +if (goog.LOCALE == 'fr_BI' || goog.LOCALE == 'fr-BI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_BI;
    +}
    +
    +if (goog.LOCALE == 'fr_BJ' || goog.LOCALE == 'fr-BJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_BJ;
    +}
    +
    +if (goog.LOCALE == 'fr_CD' || goog.LOCALE == 'fr-CD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CD;
    +}
    +
    +if (goog.LOCALE == 'fr_CF' || goog.LOCALE == 'fr-CF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CF;
    +}
    +
    +if (goog.LOCALE == 'fr_CG' || goog.LOCALE == 'fr-CG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CG;
    +}
    +
    +if (goog.LOCALE == 'fr_CH' || goog.LOCALE == 'fr-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CH;
    +}
    +
    +if (goog.LOCALE == 'fr_CI' || goog.LOCALE == 'fr-CI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CI;
    +}
    +
    +if (goog.LOCALE == 'fr_CM' || goog.LOCALE == 'fr-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_CM;
    +}
    +
    +if (goog.LOCALE == 'fr_DJ' || goog.LOCALE == 'fr-DJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_DJ;
    +}
    +
    +if (goog.LOCALE == 'fr_DZ' || goog.LOCALE == 'fr-DZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_DZ;
    +}
    +
    +if (goog.LOCALE == 'fr_GA' || goog.LOCALE == 'fr-GA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_GA;
    +}
    +
    +if (goog.LOCALE == 'fr_GN' || goog.LOCALE == 'fr-GN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_GN;
    +}
    +
    +if (goog.LOCALE == 'fr_GQ' || goog.LOCALE == 'fr-GQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_GQ;
    +}
    +
    +if (goog.LOCALE == 'fr_HT' || goog.LOCALE == 'fr-HT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_HT;
    +}
    +
    +if (goog.LOCALE == 'fr_KM' || goog.LOCALE == 'fr-KM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_KM;
    +}
    +
    +if (goog.LOCALE == 'fr_LU' || goog.LOCALE == 'fr-LU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_LU;
    +}
    +
    +if (goog.LOCALE == 'fr_MA' || goog.LOCALE == 'fr-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_MA;
    +}
    +
    +if (goog.LOCALE == 'fr_MG' || goog.LOCALE == 'fr-MG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_MG;
    +}
    +
    +if (goog.LOCALE == 'fr_ML' || goog.LOCALE == 'fr-ML') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_ML;
    +}
    +
    +if (goog.LOCALE == 'fr_MR' || goog.LOCALE == 'fr-MR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_MR;
    +}
    +
    +if (goog.LOCALE == 'fr_MU' || goog.LOCALE == 'fr-MU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_MU;
    +}
    +
    +if (goog.LOCALE == 'fr_NC' || goog.LOCALE == 'fr-NC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_NC;
    +}
    +
    +if (goog.LOCALE == 'fr_NE' || goog.LOCALE == 'fr-NE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_NE;
    +}
    +
    +if (goog.LOCALE == 'fr_PF' || goog.LOCALE == 'fr-PF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_PF;
    +}
    +
    +if (goog.LOCALE == 'fr_RW' || goog.LOCALE == 'fr-RW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_RW;
    +}
    +
    +if (goog.LOCALE == 'fr_SC' || goog.LOCALE == 'fr-SC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_SC;
    +}
    +
    +if (goog.LOCALE == 'fr_SN' || goog.LOCALE == 'fr-SN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_SN;
    +}
    +
    +if (goog.LOCALE == 'fr_SY' || goog.LOCALE == 'fr-SY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_SY;
    +}
    +
    +if (goog.LOCALE == 'fr_TD' || goog.LOCALE == 'fr-TD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_TD;
    +}
    +
    +if (goog.LOCALE == 'fr_TG' || goog.LOCALE == 'fr-TG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_TG;
    +}
    +
    +if (goog.LOCALE == 'fr_TN' || goog.LOCALE == 'fr-TN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_TN;
    +}
    +
    +if (goog.LOCALE == 'fr_VU' || goog.LOCALE == 'fr-VU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_VU;
    +}
    +
    +if (goog.LOCALE == 'fr_WF' || goog.LOCALE == 'fr-WF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr_WF;
    +}
    +
    +if (goog.LOCALE == 'fur') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fur_IT' || goog.LOCALE == 'fur-IT') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fy') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'gd') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gd_GB' || goog.LOCALE == 'gd-GB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gsw_FR' || goog.LOCALE == 'gsw-FR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gsw_FR;
    +}
    +
    +if (goog.LOCALE == 'guz') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'guz_KE' || goog.LOCALE == 'guz-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'gv') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'gv_IM' || goog.LOCALE == 'gv-IM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'ha') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn' || goog.LOCALE == 'ha-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ha_Latn;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_GH' || goog.LOCALE == 'ha-Latn-GH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ha_Latn_GH;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NE' || goog.LOCALE == 'ha-Latn-NE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ha_Latn_NE;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NG' || goog.LOCALE == 'ha-Latn-NG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'hr_BA' || goog.LOCALE == 'hr-BA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hr_BA;
    +}
    +
    +if (goog.LOCALE == 'hsb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'hsb_DE' || goog.LOCALE == 'hsb-DE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'ia') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'ia_FR' || goog.LOCALE == 'ia-FR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ia;
    +}
    +
    +if (goog.LOCALE == 'ig') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ig_NG' || goog.LOCALE == 'ig-NG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ii') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'ii_CN' || goog.LOCALE == 'ii-CN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'it_CH' || goog.LOCALE == 'it-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_it_CH;
    +}
    +
    +if (goog.LOCALE == 'jgo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jgo_CM' || goog.LOCALE == 'jgo-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jmc') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'jmc_TZ' || goog.LOCALE == 'jmc-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'kab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kab_DZ' || goog.LOCALE == 'kab-DZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kam') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kam_KE' || goog.LOCALE == 'kam-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kde') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kde_TZ' || goog.LOCALE == 'kde-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kea') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'kea_CV' || goog.LOCALE == 'kea-CV') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'khq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'khq_ML' || goog.LOCALE == 'khq-ML') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'ki') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'ki_KE' || goog.LOCALE == 'ki-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl' || goog.LOCALE == 'kk-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kkj') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kkj_CM' || goog.LOCALE == 'kkj-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kl_GL' || goog.LOCALE == 'kl-GL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kln') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'kln_KE' || goog.LOCALE == 'kln-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'ko_KP' || goog.LOCALE == 'ko-KP') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ko_KP;
    +}
    +
    +if (goog.LOCALE == 'kok') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'kok_IN' || goog.LOCALE == 'kok-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'ks') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab' || goog.LOCALE == 'ks-Arab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ks_Arab;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab_IN' || goog.LOCALE == 'ks-Arab-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ksb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksb_TZ' || goog.LOCALE == 'ksb-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksf') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksf_CM' || goog.LOCALE == 'ksf-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'ksh_DE' || goog.LOCALE == 'ksh-DE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'kw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'kw_GB' || goog.LOCALE == 'kw-GB') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl' || goog.LOCALE == 'ky-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'lag') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lag_TZ' || goog.LOCALE == 'lag-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lb') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lb_LU' || goog.LOCALE == 'lb-LU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lg_UG' || goog.LOCALE == 'lg-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lkt') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'lkt_US' || goog.LOCALE == 'lkt-US') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'ln_AO' || goog.LOCALE == 'ln-AO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ln_AO;
    +}
    +
    +if (goog.LOCALE == 'ln_CF' || goog.LOCALE == 'ln-CF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ln_CF;
    +}
    +
    +if (goog.LOCALE == 'ln_CG' || goog.LOCALE == 'ln-CG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ln_CG;
    +}
    +
    +if (goog.LOCALE == 'lu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'lu_CD' || goog.LOCALE == 'lu-CD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'luo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luo_KE' || goog.LOCALE == 'luo-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luy') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'luy_KE' || goog.LOCALE == 'luy-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'mas') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_KE' || goog.LOCALE == 'mas-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_TZ' || goog.LOCALE == 'mas-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mas_TZ;
    +}
    +
    +if (goog.LOCALE == 'mer') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mer_KE' || goog.LOCALE == 'mer-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mfe') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mfe_MU' || goog.LOCALE == 'mfe-MU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mg_MG' || goog.LOCALE == 'mg-MG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mgh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgh_MZ' || goog.LOCALE == 'mgh-MZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mgo_CM' || goog.LOCALE == 'mgo-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl' || goog.LOCALE == 'mn-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn' || goog.LOCALE == 'ms-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_BN' || goog.LOCALE == 'ms-Latn-BN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ms_Latn_BN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_SG' || goog.LOCALE == 'ms-Latn-SG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ms_Latn_SG;
    +}
    +
    +if (goog.LOCALE == 'mua') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'mua_CM' || goog.LOCALE == 'mua-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'naq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'naq_NA' || goog.LOCALE == 'naq-NA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'nd') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'nd_ZW' || goog.LOCALE == 'nd-ZW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'ne_IN' || goog.LOCALE == 'ne-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ne_IN;
    +}
    +
    +if (goog.LOCALE == 'nl_AW' || goog.LOCALE == 'nl-AW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_AW;
    +}
    +
    +if (goog.LOCALE == 'nl_BE' || goog.LOCALE == 'nl-BE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_BE;
    +}
    +
    +if (goog.LOCALE == 'nl_BQ' || goog.LOCALE == 'nl-BQ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_BQ;
    +}
    +
    +if (goog.LOCALE == 'nl_CW' || goog.LOCALE == 'nl-CW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_CW;
    +}
    +
    +if (goog.LOCALE == 'nl_SR' || goog.LOCALE == 'nl-SR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_SR;
    +}
    +
    +if (goog.LOCALE == 'nl_SX' || goog.LOCALE == 'nl-SX') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nl_SX;
    +}
    +
    +if (goog.LOCALE == 'nmg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nmg_CM' || goog.LOCALE == 'nmg-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nn_NO' || goog.LOCALE == 'nn-NO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nnh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nnh_CM' || goog.LOCALE == 'nnh-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nr') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nr_ZA' || goog.LOCALE == 'nr-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nr;
    +}
    +
    +if (goog.LOCALE == 'nso') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nso_ZA' || goog.LOCALE == 'nso-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nso;
    +}
    +
    +if (goog.LOCALE == 'nus') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nus_SD' || goog.LOCALE == 'nus-SD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nyn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'nyn_UG' || goog.LOCALE == 'nyn-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'om') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_ET' || goog.LOCALE == 'om-ET') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_KE' || goog.LOCALE == 'om-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_om_KE;
    +}
    +
    +if (goog.LOCALE == 'os') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_GE' || goog.LOCALE == 'os-GE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_RU' || goog.LOCALE == 'os-RU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_os_RU;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab' || goog.LOCALE == 'pa-Arab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab_PK' || goog.LOCALE == 'pa-Arab-PK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru' || goog.LOCALE == 'pa-Guru') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'ps') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'ps_AF' || goog.LOCALE == 'ps-AF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'pt_AO' || goog.LOCALE == 'pt-AO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_AO;
    +}
    +
    +if (goog.LOCALE == 'pt_CV' || goog.LOCALE == 'pt-CV') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_CV;
    +}
    +
    +if (goog.LOCALE == 'pt_GW' || goog.LOCALE == 'pt-GW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_GW;
    +}
    +
    +if (goog.LOCALE == 'pt_MO' || goog.LOCALE == 'pt-MO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_MO;
    +}
    +
    +if (goog.LOCALE == 'pt_MZ' || goog.LOCALE == 'pt-MZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_MZ;
    +}
    +
    +if (goog.LOCALE == 'pt_ST' || goog.LOCALE == 'pt-ST') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_ST;
    +}
    +
    +if (goog.LOCALE == 'pt_TL' || goog.LOCALE == 'pt-TL') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_pt_TL;
    +}
    +
    +if (goog.LOCALE == 'qu') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_BO' || goog.LOCALE == 'qu-BO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_qu_BO;
    +}
    +
    +if (goog.LOCALE == 'qu_EC' || goog.LOCALE == 'qu-EC') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_qu_EC;
    +}
    +
    +if (goog.LOCALE == 'qu_PE' || goog.LOCALE == 'qu-PE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'rm') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rm_CH' || goog.LOCALE == 'rm-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'rn_BI' || goog.LOCALE == 'rn-BI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'ro_MD' || goog.LOCALE == 'ro-MD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ro_MD;
    +}
    +
    +if (goog.LOCALE == 'rof') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'rof_TZ' || goog.LOCALE == 'rof-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'ru_BY' || goog.LOCALE == 'ru-BY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru_BY;
    +}
    +
    +if (goog.LOCALE == 'ru_KG' || goog.LOCALE == 'ru-KG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru_KG;
    +}
    +
    +if (goog.LOCALE == 'ru_KZ' || goog.LOCALE == 'ru-KZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru_KZ;
    +}
    +
    +if (goog.LOCALE == 'ru_MD' || goog.LOCALE == 'ru-MD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru_MD;
    +}
    +
    +if (goog.LOCALE == 'ru_UA' || goog.LOCALE == 'ru-UA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ru_UA;
    +}
    +
    +if (goog.LOCALE == 'rw') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rw_RW' || goog.LOCALE == 'rw-RW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rwk') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'rwk_TZ' || goog.LOCALE == 'rwk-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'sah') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'sah_RU' || goog.LOCALE == 'sah-RU') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'saq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'saq_KE' || goog.LOCALE == 'saq-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'sbp') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'sbp_TZ' || goog.LOCALE == 'sbp-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'se') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_FI' || goog.LOCALE == 'se-FI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_se_FI;
    +}
    +
    +if (goog.LOCALE == 'se_NO' || goog.LOCALE == 'se-NO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_SE' || goog.LOCALE == 'se-SE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_se_SE;
    +}
    +
    +if (goog.LOCALE == 'seh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'seh_MZ' || goog.LOCALE == 'seh-MZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'ses') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'ses_ML' || goog.LOCALE == 'ses-ML') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'sg') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'sg_CF' || goog.LOCALE == 'sg-CF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'shi') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn' || goog.LOCALE == 'shi-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn_MA' || goog.LOCALE == 'shi-Latn-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng' || goog.LOCALE == 'shi-Tfng') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_shi_Tfng;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng_MA' || goog.LOCALE == 'shi-Tfng-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'smn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'smn_FI' || goog.LOCALE == 'smn-FI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'sn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'sn_ZW' || goog.LOCALE == 'sn-ZW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'so') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_DJ' || goog.LOCALE == 'so-DJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_so_DJ;
    +}
    +
    +if (goog.LOCALE == 'so_ET' || goog.LOCALE == 'so-ET') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_so_ET;
    +}
    +
    +if (goog.LOCALE == 'so_KE' || goog.LOCALE == 'so-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_so_KE;
    +}
    +
    +if (goog.LOCALE == 'so_SO' || goog.LOCALE == 'so-SO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'sq_MK' || goog.LOCALE == 'sq-MK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sq_MK;
    +}
    +
    +if (goog.LOCALE == 'sq_XK' || goog.LOCALE == 'sq-XK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sq_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl' || goog.LOCALE == 'sr-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_BA' || goog.LOCALE == 'sr-Cyrl-BA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_ME' || goog.LOCALE == 'sr-Cyrl-ME') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_XK' || goog.LOCALE == 'sr-Cyrl-XK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn' || goog.LOCALE == 'sr-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_BA' || goog.LOCALE == 'sr-Latn-BA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_ME' || goog.LOCALE == 'sr-Latn-ME') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Latn_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_RS' || goog.LOCALE == 'sr-Latn-RS') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_XK' || goog.LOCALE == 'sr-Latn-XK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sr_Latn_XK;
    +}
    +
    +if (goog.LOCALE == 'ss') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ss_SZ' || goog.LOCALE == 'ss-SZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ss_SZ;
    +}
    +
    +if (goog.LOCALE == 'ss_ZA' || goog.LOCALE == 'ss-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ss;
    +}
    +
    +if (goog.LOCALE == 'ssy') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'ssy_ER' || goog.LOCALE == 'ssy-ER') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ssy;
    +}
    +
    +if (goog.LOCALE == 'sv_AX' || goog.LOCALE == 'sv-AX') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sv_AX;
    +}
    +
    +if (goog.LOCALE == 'sv_FI' || goog.LOCALE == 'sv-FI') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sv_FI;
    +}
    +
    +if (goog.LOCALE == 'sw_KE' || goog.LOCALE == 'sw-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sw_KE;
    +}
    +
    +if (goog.LOCALE == 'sw_UG' || goog.LOCALE == 'sw-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_sw_UG;
    +}
    +
    +if (goog.LOCALE == 'swc') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'swc_CD' || goog.LOCALE == 'swc-CD') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'ta_LK' || goog.LOCALE == 'ta-LK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ta_LK;
    +}
    +
    +if (goog.LOCALE == 'ta_MY' || goog.LOCALE == 'ta-MY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ta_MY;
    +}
    +
    +if (goog.LOCALE == 'ta_SG' || goog.LOCALE == 'ta-SG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ta_SG;
    +}
    +
    +if (goog.LOCALE == 'teo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_KE' || goog.LOCALE == 'teo-KE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_teo_KE;
    +}
    +
    +if (goog.LOCALE == 'teo_UG' || goog.LOCALE == 'teo-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'ti') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'ti_ER' || goog.LOCALE == 'ti-ER') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ti_ER;
    +}
    +
    +if (goog.LOCALE == 'ti_ET' || goog.LOCALE == 'ti-ET') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'tn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'tn_BW' || goog.LOCALE == 'tn-BW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tn_BW;
    +}
    +
    +if (goog.LOCALE == 'tn_ZA' || goog.LOCALE == 'tn-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tn;
    +}
    +
    +if (goog.LOCALE == 'to') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'to_TO' || goog.LOCALE == 'to-TO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'tr_CY' || goog.LOCALE == 'tr-CY') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tr_CY;
    +}
    +
    +if (goog.LOCALE == 'ts') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'ts_ZA' || goog.LOCALE == 'ts-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ts;
    +}
    +
    +if (goog.LOCALE == 'twq') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'twq_NE' || goog.LOCALE == 'twq-NE') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'tzm') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn' || goog.LOCALE == 'tzm-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tzm_Latn;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn_MA' || goog.LOCALE == 'tzm-Latn-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'ug') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab' || goog.LOCALE == 'ug-Arab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ug_Arab;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab_CN' || goog.LOCALE == 'ug-Arab-CN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ur_IN' || goog.LOCALE == 'ur-IN') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ur_IN;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab' || goog.LOCALE == 'uz-Arab') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab_AF' || goog.LOCALE == 'uz-Arab-AF') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl' || goog.LOCALE == 'uz-Cyrl') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl_UZ' || goog.LOCALE == 'uz-Cyrl-UZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz_Cyrl_UZ;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn' || goog.LOCALE == 'uz-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn' || goog.LOCALE == 'vai-Latn') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn_LR' || goog.LOCALE == 'vai-Latn-LR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii' || goog.LOCALE == 'vai-Vaii') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vai_Vaii;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii_LR' || goog.LOCALE == 'vai-Vaii-LR') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 've') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 've_ZA' || goog.LOCALE == 've-ZA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ve;
    +}
    +
    +if (goog.LOCALE == 'vo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vo_001' || goog.LOCALE == 'vo-001') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vo;
    +}
    +
    +if (goog.LOCALE == 'vun') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'vun_TZ' || goog.LOCALE == 'vun-TZ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'wae') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'wae_CH' || goog.LOCALE == 'wae-CH') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'xog') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'xog_UG' || goog.LOCALE == 'xog-UG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'yav') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yav_CM' || goog.LOCALE == 'yav-CM') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yi') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yi_001' || goog.LOCALE == 'yi-001') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yo') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'yo_BJ' || goog.LOCALE == 'yo-BJ') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yo_BJ;
    +}
    +
    +if (goog.LOCALE == 'yo_NG' || goog.LOCALE == 'yo-NG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'zgh') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zgh_MA' || goog.LOCALE == 'zgh-MA') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans' || goog.LOCALE == 'zh-Hans') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_HK' || goog.LOCALE == 'zh-Hans-HK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hans_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_MO' || goog.LOCALE == 'zh-Hans-MO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hans_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_SG' || goog.LOCALE == 'zh-Hans-SG') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hans_SG;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant' || goog.LOCALE == 'zh-Hant') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_HK' || goog.LOCALE == 'zh-Hant-HK') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hant_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_MO' || goog.LOCALE == 'zh-Hant-MO') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hant_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_TW' || goog.LOCALE == 'zh-Hant-TW') {
    +  goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_zh_Hant;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/ordinalrules.js b/mapbox-gl/closure-library/closure/goog/i18n/ordinalrules.js
    new file mode 100644
    index 0000000000..c9dc82ed2b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/ordinalrules.js
    @@ -0,0 +1,748 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Ordinal rules.
    + *
    + * This file is autogenerated by script:
    + *   http://go/generate_pluralrules.py
    + * File generated from CLDR ver. 26
    + *
    + * Before check in, this file could have been manually edited. This is to
    + * incorporate changes before we could fix CLDR. All manual modification must be
    + * documented in this section, and should be removed after those changes land to
    + * CLDR.
    + */
    +
    +goog.provide('goog.i18n.ordinalRules');
    +/**
    + * Ordinal pattern keyword
    + * @enum {string}
    + */
    +goog.i18n.ordinalRules.Keyword = {
    +  ZERO: 'zero',
    +  ONE: 'one',
    +  TWO: 'two',
    +  FEW: 'few',
    +  MANY: 'many',
    +  OTHER: 'other'
    +};
    +
    +
    +/**
    + * Default Ordinal select rule.
    + * @param {number} n The count of items.
    + * @param {number=} opt_precision optional, precision.
    + * @return {goog.i18n.ordinalRules.Keyword} Default value.
    + * @private
    + */
    +goog.i18n.ordinalRules.defaultSelect_ = function(n, opt_precision) {
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Returns the fractional part of a number (3.1416 => 1416)
    + * @param {number} n The count of items.
    + * @return {number} The fractional part.
    + * @private
    + */
    +goog.i18n.ordinalRules.decimals_ = function(n) {
    +  var str = n + '';
    +  var result = str.indexOf('.');
    +  return (result == -1) ? 0 : str.length - result - 1;
    +};
    +
    +/**
    + * Calculates v and f as per CLDR plural rules.
    + * The short names for parameters / return match the CLDR syntax and UTS #35
    + *     (http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax)
    + * @param {number} n The count of items.
    + * @param {number=} opt_precision optional, precision.
    + * @return {!Object} The v and f.
    + * @private
    + */
    +goog.i18n.ordinalRules.get_vf_ = function(n, opt_precision) {
    +  var DEFAULT_DIGITS = 3;
    +
    +  if (undefined === opt_precision) {
    +    var v = Math.min(goog.i18n.ordinalRules.decimals_(n), DEFAULT_DIGITS);
    +  } else {
    +    var v = opt_precision;
    +  }
    +
    +  var base = Math.pow(10, v);
    +  var f = ((n * base) | 0) % base;
    +
    +  return {v: v, f: f};
    +};
    +
    +/**
    + * Calculates w and t as per CLDR plural rules.
    + * The short names for parameters / return match the CLDR syntax and UTS #35
    + *     (http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax)
    + * @param {number} v Calculated previously.
    + * @param {number} f Calculated previously.
    + * @return {!Object} The w and t.
    + * @private
    + */
    +goog.i18n.ordinalRules.get_wt_ = function(v, f) {
    +  if (f === 0) {
    +    return {w: 0, t: 0};
    +  }
    +
    +  while ((f % 10) === 0) {
    +    f /= 10;
    +    v--;
    +  }
    +
    +  return {w: v, t: f};
    +};
    +
    +/**
    + * Ordinal select rules for en locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.enSelect_ = function(n, opt_precision) {
    +  if (n % 10 == 1 && n % 100 != 11) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n % 10 == 2 && n % 100 != 12) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n % 10 == 3 && n % 100 != 13) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for sv locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.svSelect_ = function(n, opt_precision) {
    +  if ((n % 10 == 1 || n % 10 == 2) && n % 100 != 11 && n % 100 != 12) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for hu locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.huSelect_ = function(n, opt_precision) {
    +  if (n == 1 || n == 5) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for kk locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.kkSelect_ = function(n, opt_precision) {
    +  if (n % 10 == 6 || n % 10 == 9 || n % 10 == 0 && n != 0) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for mr locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.mrSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n == 2 || n == 3) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n == 4) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for sq locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.sqSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n % 10 == 4 && n % 100 != 14) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for bn locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.bnSelect_ = function(n, opt_precision) {
    +  if (n == 1 || n == 5 || n == 7 || n == 8 || n == 9 || n == 10) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n == 2 || n == 3) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n == 4) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  if (n == 6) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for gu locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.guSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n == 2 || n == 3) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n == 4) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  if (n == 6) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for ka locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.kaSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (i == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (i == 0 || (i % 100 >= 2 && i % 100 <= 20 || i % 100 == 40 || i % 100 == 60 || i % 100 == 80)) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for fr locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.frSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for ne locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.neSelect_ = function(n, opt_precision) {
    +  if (n >= 1 && n <= 4) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for cy locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.cySelect_ = function(n, opt_precision) {
    +  if (n == 0 || n == 7 || n == 8 || n == 9) {
    +    return goog.i18n.ordinalRules.Keyword.ZERO;
    +  }
    +  if (n == 1) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n == 3 || n == 4) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  if (n == 5 || n == 6) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for uk locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.ukSelect_ = function(n, opt_precision) {
    +  if (n % 10 == 3 && n % 100 != 13) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for az locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.azSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if ((i % 10 == 1 || i % 10 == 2 || i % 10 == 5 || i % 10 == 7 || i % 10 == 8) || (i % 100 == 20 || i % 100 == 50 || i % 100 == 70 || i % 100 == 80)) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if ((i % 10 == 3 || i % 10 == 4) || (i % 1000 == 100 || i % 1000 == 200 || i % 1000 == 300 || i % 1000 == 400 || i % 1000 == 500 || i % 1000 == 600 || i % 1000 == 700 || i % 1000 == 800 || i % 1000 == 900)) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  if (i == 0 || i % 10 == 6 || (i % 100 == 40 || i % 100 == 60 || i % 100 == 90)) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for ca locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.caSelect_ = function(n, opt_precision) {
    +  if (n == 1 || n == 3) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if (n == 4) {
    +    return goog.i18n.ordinalRules.Keyword.FEW;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for it locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.itSelect_ = function(n, opt_precision) {
    +  if (n == 11 || n == 8 || n == 80 || n == 800) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Ordinal select rules for mk locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
    + * @private
    + */
    +goog.i18n.ordinalRules.mkSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (i % 10 == 1 && i % 100 != 11) {
    +    return goog.i18n.ordinalRules.Keyword.ONE;
    +  }
    +  if (i % 10 == 2 && i % 100 != 12) {
    +    return goog.i18n.ordinalRules.Keyword.TWO;
    +  }
    +  if ((i % 10 == 7 || i % 10 == 8) && i % 100 != 17 && i % 100 != 18) {
    +    return goog.i18n.ordinalRules.Keyword.MANY;
    +  }
    +  return goog.i18n.ordinalRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Selected Ordinal rules by locale.
    + */
    +goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'am') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ar') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'az') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.azSelect_;
    +}
    +if (goog.LOCALE == 'bg') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'bn') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.bnSelect_;
    +}
    +if (goog.LOCALE == 'br') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ca') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.caSelect_;
    +}
    +if (goog.LOCALE == 'chr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'cs') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'cy') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.cySelect_;
    +}
    +if (goog.LOCALE == 'da') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'de') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'el') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'en') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
    +}
    +if (goog.LOCALE == 'es') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'et') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'eu') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'fa') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'fi') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'fil') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'fr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'ga') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'gl') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'gsw') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'gu') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.guSelect_;
    +}
    +if (goog.LOCALE == 'haw') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'he') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'hi') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.guSelect_;
    +}
    +if (goog.LOCALE == 'hr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'hu') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.huSelect_;
    +}
    +if (goog.LOCALE == 'hy') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'id') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'in') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'is') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'it') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.itSelect_;
    +}
    +if (goog.LOCALE == 'iw') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ja') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ka') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.kaSelect_;
    +}
    +if (goog.LOCALE == 'kk') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.kkSelect_;
    +}
    +if (goog.LOCALE == 'km') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'kn') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ko') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ky') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ln') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'lo') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'lt') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'lv') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'mk') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.mkSelect_;
    +}
    +if (goog.LOCALE == 'ml') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'mn') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'mo') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'mr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.mrSelect_;
    +}
    +if (goog.LOCALE == 'ms') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'mt') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'my') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'nb') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ne') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.neSelect_;
    +}
    +if (goog.LOCALE == 'nl') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'no') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'or') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'pa') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'pl') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'pt') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ro') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'ru') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'sh') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'si') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'sk') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'sl') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'sq') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.sqSelect_;
    +}
    +if (goog.LOCALE == 'sr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'sv') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.svSelect_;
    +}
    +if (goog.LOCALE == 'sw') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ta') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'te') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'th') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'tl') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'tr') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'uk') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.ukSelect_;
    +}
    +if (goog.LOCALE == 'ur') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'uz') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'vi') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
    +}
    +if (goog.LOCALE == 'zh') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zu') {
    +  goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/pluralrules.js b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules.js
    new file mode 100644
    index 0000000000..78f3c7fb1a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules.js
    @@ -0,0 +1,1120 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Plural rules.
    + *
    + * This file is autogenerated by script:
    + *   http://go/generate_pluralrules.py
    + * File generated from CLDR ver. 26
    + *
    + * Before check in, this file could have been manually edited. This is to
    + * incorporate changes before we could fix CLDR. All manual modification must be
    + * documented in this section, and should be removed after those changes land to
    + * CLDR.
    + */
    +
    +goog.provide('goog.i18n.pluralRules');
    +/**
    + * Plural pattern keyword
    + * @enum {string}
    + */
    +goog.i18n.pluralRules.Keyword = {
    +  ZERO: 'zero',
    +  ONE: 'one',
    +  TWO: 'two',
    +  FEW: 'few',
    +  MANY: 'many',
    +  OTHER: 'other'
    +};
    +
    +
    +/**
    + * Default Plural select rule.
    + * @param {number} n The count of items.
    + * @param {number=} opt_precision optional, precision.
    + * @return {goog.i18n.pluralRules.Keyword} Default value.
    + * @private
    + */
    +goog.i18n.pluralRules.defaultSelect_ = function(n, opt_precision) {
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Returns the fractional part of a number (3.1416 => 1416)
    + * @param {number} n The count of items.
    + * @return {number} The fractional part.
    + * @private
    + */
    +goog.i18n.pluralRules.decimals_ = function(n) {
    +  var str = n + '';
    +  var result = str.indexOf('.');
    +  return (result == -1) ? 0 : str.length - result - 1;
    +};
    +
    +/**
    + * Calculates v and f as per CLDR plural rules.
    + * The short names for parameters / return match the CLDR syntax and UTS #35
    + *     (http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax)
    + * @param {number} n The count of items.
    + * @param {number=} opt_precision optional, precision.
    + * @return {!Object} The v and f.
    + * @private
    + */
    +goog.i18n.pluralRules.get_vf_ = function(n, opt_precision) {
    +  var DEFAULT_DIGITS = 3;
    +
    +  if (undefined === opt_precision) {
    +    var v = Math.min(goog.i18n.pluralRules.decimals_(n), DEFAULT_DIGITS);
    +  } else {
    +    var v = opt_precision;
    +  }
    +
    +  var base = Math.pow(10, v);
    +  var f = ((n * base) | 0) % base;
    +
    +  return {v: v, f: f};
    +};
    +
    +/**
    + * Calculates w and t as per CLDR plural rules.
    + * The short names for parameters / return match the CLDR syntax and UTS #35
    + *     (http://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax)
    + * @param {number} v Calculated previously.
    + * @param {number} f Calculated previously.
    + * @return {!Object} The w and t.
    + * @private
    + */
    +goog.i18n.pluralRules.get_wt_ = function(v, f) {
    +  if (f === 0) {
    +    return {w: 0, t: 0};
    +  }
    +
    +  while ((f % 10) === 0) {
    +    f /= 10;
    +    v--;
    +  }
    +
    +  return {w: v, t: f};
    +};
    +
    +/**
    + * Plural select rules for ga locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.gaSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (n >= 3 && n <= 6) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n >= 7 && n <= 10) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for ro locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.roSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (i == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v != 0 || n == 0 || n != 1 && n % 100 >= 1 && n % 100 <= 19) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for fil locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.filSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && (i == 1 || i == 2 || i == 3) || vf.v == 0 && i % 10 != 4 && i % 10 != 6 && i % 10 != 9 || vf.v != 0 && vf.f % 10 != 4 && vf.f % 10 != 6 && vf.f % 10 != 9) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for fr locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.frSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (i == 0 || i == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for en locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.enSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (i == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for mt locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.mtSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 0 || n % 100 >= 2 && n % 100 <= 10) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n % 100 >= 11 && n % 100 <= 19) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for da locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.daSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  var wt = goog.i18n.pluralRules.get_wt_(vf.v, vf.f);
    +  if (n == 1 || wt.t != 0 && (i == 0 || i == 1)) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for gv locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.gvSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 10 == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 10 == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (vf.v == 0 && (i % 100 == 0 || i % 100 == 20 || i % 100 == 40 || i % 100 == 60 || i % 100 == 80)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (vf.v != 0) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for cy locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.cySelect_ = function(n, opt_precision) {
    +  if (n == 0) {
    +    return goog.i18n.pluralRules.Keyword.ZERO;
    +  }
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (n == 3) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n == 6) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for br locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.brSelect_ = function(n, opt_precision) {
    +  if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if ((n % 10 >= 3 && n % 10 <= 4 || n % 10 == 9) && (n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n != 0 && n % 1000000 == 0) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for es locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.esSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for si locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.siSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if ((n == 0 || n == 1) || i == 0 && vf.f == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for sl locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.slSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 100 == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 100 == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (vf.v == 0 && i % 100 >= 3 && i % 100 <= 4 || vf.v != 0) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for tzm locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.tzmSelect_ = function(n, opt_precision) {
    +  if (n >= 0 && n <= 1 || n >= 11 && n <= 99) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for sr locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.srSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 10 == 1 && i % 100 != 11 || vf.f % 10 == 1 && vf.f % 100 != 11) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 10 >= 2 && i % 10 <= 4 && (i % 100 < 12 || i % 100 > 14) || vf.f % 10 >= 2 && vf.f % 10 <= 4 && (vf.f % 100 < 12 || vf.f % 100 > 14)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for mk locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.mkSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 10 == 1 || vf.f % 10 == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for hi locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.hiSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (i == 0 || n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for pt locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
    +  if (n >= 0 && n <= 2 && n != 2) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for ar locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.arSelect_ = function(n, opt_precision) {
    +  if (n == 0) {
    +    return goog.i18n.pluralRules.Keyword.ZERO;
    +  }
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (n % 100 >= 3 && n % 100 <= 10) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n % 100 >= 11 && n % 100 <= 99) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for iu locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.iuSelect_ = function(n, opt_precision) {
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for cs locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.csSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (i == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (i >= 2 && i <= 4 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (vf.v != 0) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for pt_PT locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.pt_PTSelect_ = function(n, opt_precision) {
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (n == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for be locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.beSelect_ = function(n, opt_precision) {
    +  if (n % 10 == 1 && n % 100 != 11) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (n % 10 == 0 || n % 10 >= 5 && n % 10 <= 9 || n % 100 >= 11 && n % 100 <= 14) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for ak locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.akSelect_ = function(n, opt_precision) {
    +  if (n >= 0 && n <= 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for pl locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.plSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (i == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 10 >= 2 && i % 10 <= 4 && (i % 100 < 12 || i % 100 > 14)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (vf.v == 0 && i != 1 && i % 10 >= 0 && i % 10 <= 1 || vf.v == 0 && i % 10 >= 5 && i % 10 <= 9 || vf.v == 0 && i % 100 >= 12 && i % 100 <= 14) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for ru locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.ruSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 10 == 1 && i % 100 != 11) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 10 >= 2 && i % 10 <= 4 && (i % 100 < 12 || i % 100 > 14)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (vf.v == 0 && i % 10 == 0 || vf.v == 0 && i % 10 >= 5 && i % 10 <= 9 || vf.v == 0 && i % 100 >= 11 && i % 100 <= 14) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for lag locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.lagSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (n == 0) {
    +    return goog.i18n.pluralRules.Keyword.ZERO;
    +  }
    +  if ((i == 0 || i == 1) && n != 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for shi locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.shiSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  if (i == 0 || n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n >= 2 && n <= 10) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for he locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.heSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (i == 1 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (i == 2 && vf.v == 0) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (vf.v == 0 && (n < 0 || n > 10) && n % 10 == 0) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for is locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.isSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  var wt = goog.i18n.pluralRules.get_wt_(vf.v, vf.f);
    +  if (wt.t == 0 && i % 10 == 1 && i % 100 != 11 || wt.t != 0) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for lt locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.ltSelect_ = function(n, opt_precision) {
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  if (vf.f != 0) {
    +    return goog.i18n.pluralRules.Keyword.MANY;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for gd locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.gdSelect_ = function(n, opt_precision) {
    +  if (n == 1 || n == 11) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (n == 2 || n == 12) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (n >= 3 && n <= 10 || n >= 13 && n <= 19) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for dsb locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.dsbSelect_ = function(n, opt_precision) {
    +  var i = n | 0;
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (vf.v == 0 && i % 100 == 1 || vf.f % 100 == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  if (vf.v == 0 && i % 100 == 2 || vf.f % 100 == 2) {
    +    return goog.i18n.pluralRules.Keyword.TWO;
    +  }
    +  if (vf.v == 0 && i % 100 >= 3 && i % 100 <= 4 || vf.f % 100 >= 3 && vf.f % 100 <= 4) {
    +    return goog.i18n.pluralRules.Keyword.FEW;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for lv locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.lvSelect_ = function(n, opt_precision) {
    +  var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
    +  if (n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19 || vf.v == 2 && vf.f % 100 >= 11 && vf.f % 100 <= 19) {
    +    return goog.i18n.pluralRules.Keyword.ZERO;
    +  }
    +  if (n % 10 == 1 && n % 100 != 11 || vf.v == 2 && vf.f % 10 == 1 && vf.f % 100 != 11 || vf.v != 2 && vf.f % 10 == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Plural select rules for ksh locale
    + *
    + * @param {number} n  The count of items.
    + * @param {number=} opt_precision Precision for number formatting, if not default.
    + * @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
    + * @private
    + */
    +goog.i18n.pluralRules.kshSelect_ = function(n, opt_precision) {
    +  if (n == 0) {
    +    return goog.i18n.pluralRules.Keyword.ZERO;
    +  }
    +  if (n == 1) {
    +    return goog.i18n.pluralRules.Keyword.ONE;
    +  }
    +  return goog.i18n.pluralRules.Keyword.OTHER;
    +};
    +
    +/**
    + * Selected Plural rules by locale.
    + */
    +goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +if (goog.LOCALE == 'af') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'am') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'ar') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
    +}
    +if (goog.LOCALE == 'az') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'bg') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'bn') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'br') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.brSelect_;
    +}
    +if (goog.LOCALE == 'ca') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'chr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'cs') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.csSelect_;
    +}
    +if (goog.LOCALE == 'cy') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.cySelect_;
    +}
    +if (goog.LOCALE == 'da') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.daSelect_;
    +}
    +if (goog.LOCALE == 'de') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'el') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'en') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'es') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'et') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'eu') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'fa') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'fi') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'fil') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
    +}
    +if (goog.LOCALE == 'fr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
    +}
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
    +}
    +if (goog.LOCALE == 'ga') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.gaSelect_;
    +}
    +if (goog.LOCALE == 'gl') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'gsw') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'gu') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'haw') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'he') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.heSelect_;
    +}
    +if (goog.LOCALE == 'hi') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'hr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.srSelect_;
    +}
    +if (goog.LOCALE == 'hu') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'hy') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
    +}
    +if (goog.LOCALE == 'id') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'in') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'is') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.isSelect_;
    +}
    +if (goog.LOCALE == 'it') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'iw') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.heSelect_;
    +}
    +if (goog.LOCALE == 'ja') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ka') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'kk') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'km') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'kn') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'ko') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'ky') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'ln') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.akSelect_;
    +}
    +if (goog.LOCALE == 'lo') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'lt') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.ltSelect_;
    +}
    +if (goog.LOCALE == 'lv') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.lvSelect_;
    +}
    +if (goog.LOCALE == 'mk') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.mkSelect_;
    +}
    +if (goog.LOCALE == 'ml') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'mn') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'mo') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
    +}
    +if (goog.LOCALE == 'mr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    +if (goog.LOCALE == 'ms') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'mt') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.mtSelect_;
    +}
    +if (goog.LOCALE == 'my') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'nb') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'ne') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'nl') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'no') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'or') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'pa') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.akSelect_;
    +}
    +if (goog.LOCALE == 'pl') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.plSelect_;
    +}
    +if (goog.LOCALE == 'pt') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
    +}
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.ptSelect_;
    +}
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.pt_PTSelect_;
    +}
    +if (goog.LOCALE == 'ro') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
    +}
    +if (goog.LOCALE == 'ru') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.ruSelect_;
    +}
    +if (goog.LOCALE == 'sh') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.srSelect_;
    +}
    +if (goog.LOCALE == 'si') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.siSelect_;
    +}
    +if (goog.LOCALE == 'sk') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.csSelect_;
    +}
    +if (goog.LOCALE == 'sl') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.slSelect_;
    +}
    +if (goog.LOCALE == 'sq') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'sr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.srSelect_;
    +}
    +if (goog.LOCALE == 'sv') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'sw') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'ta') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'te') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'th') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'tl') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
    +}
    +if (goog.LOCALE == 'tr') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'uk') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.ruSelect_;
    +}
    +if (goog.LOCALE == 'ur') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
    +}
    +if (goog.LOCALE == 'uz') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.esSelect_;
    +}
    +if (goog.LOCALE == 'vi') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
    +}
    +if (goog.LOCALE == 'zu') {
    +  goog.i18n.pluralRules.select = goog.i18n.pluralRules.hiSelect_;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.html b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.html
    new file mode 100644
    index 0000000000..0f546de80a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.pluralRules
    +  </title>
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.pluralRulesTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.js b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.js
    new file mode 100644
    index 0000000000..75d5eafe68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/pluralrules_test.js
    @@ -0,0 +1,102 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.pluralRulesTest');
    +goog.setTestOnly('goog.i18n.pluralRulesTest');
    +
    +goog.require('goog.i18n.pluralRules');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/** @suppress {missingRequire} */
    +var Keyword = goog.i18n.pluralRules.Keyword;
    +
    +function testSimpleSelectEn() {
    +  var funcSelect = goog.i18n.pluralRules.enSelect_;
    +
    +  assertEquals(Keyword.OTHER, funcSelect(0)); // 0 dollars
    +  assertEquals(Keyword.ONE, funcSelect(1)); // 1 dollar
    +  assertEquals(Keyword.OTHER, funcSelect(2)); // 2 dollars
    +
    +  assertEquals(Keyword.OTHER, funcSelect(0, 2)); // 0.00 dollars
    +  assertEquals(Keyword.OTHER, funcSelect(1, 2)); // 1.00 dollars
    +  assertEquals(Keyword.OTHER, funcSelect(2, 2)); // 2.00 dollars
    +}
    +
    +function testSimpleSelectRo() {
    +  var funcSelect = goog.i18n.pluralRules.roSelect_;
    +
    +  assertEquals(Keyword.FEW, funcSelect(0)); // 0 dolari
    +  assertEquals(Keyword.ONE, funcSelect(1)); // 1 dolar
    +  assertEquals(Keyword.FEW, funcSelect(2)); // 2 dolari
    +  assertEquals(Keyword.FEW, funcSelect(12)); // 12 dolari
    +  assertEquals(Keyword.OTHER, funcSelect(23)); // 23 de dolari
    +  assertEquals(Keyword.FEW, funcSelect(1212)); // 1212 dolari
    +  assertEquals(Keyword.OTHER, funcSelect(1223)); // 1223 de dolari
    +
    +  assertEquals(Keyword.FEW, funcSelect(0, 2)); // 0.00 dolari
    +  assertEquals(Keyword.FEW, funcSelect(1, 2)); // 1.00 dolari
    +  assertEquals(Keyword.FEW, funcSelect(2, 2)); // 2.00 dolari
    +  assertEquals(Keyword.FEW, funcSelect(12, 2)); // 12.00 dolari
    +  assertEquals(Keyword.FEW, funcSelect(23, 2)); // 23.00  dolari
    +  assertEquals(Keyword.FEW, funcSelect(1212, 2)); // 1212.00  dolari
    +  assertEquals(Keyword.FEW, funcSelect(1223, 2)); // 1223.00 dolari
    +}
    +
    +function testSimpleSelectSr() {
    +  var funcSelect = goog.i18n.pluralRules.srSelect_; // Serbian
    +
    +  assertEquals(Keyword.ONE, funcSelect(1));
    +  assertEquals(Keyword.ONE, funcSelect(31));
    +  assertEquals(Keyword.ONE, funcSelect(0.1));
    +  assertEquals(Keyword.ONE, funcSelect(1.1));
    +  assertEquals(Keyword.ONE, funcSelect(2.1));
    +
    +  assertEquals(Keyword.FEW, funcSelect(3));
    +  assertEquals(Keyword.FEW, funcSelect(33));
    +  assertEquals(Keyword.FEW, funcSelect(0.2));
    +  assertEquals(Keyword.FEW, funcSelect(0.3));
    +  assertEquals(Keyword.FEW, funcSelect(0.4));
    +  assertEquals(Keyword.FEW, funcSelect(2.2));
    +
    +  assertEquals(Keyword.OTHER, funcSelect(2.11));
    +  assertEquals(Keyword.OTHER, funcSelect(2.12));
    +  assertEquals(Keyword.OTHER, funcSelect(2.13));
    +  assertEquals(Keyword.OTHER, funcSelect(2.14));
    +  assertEquals(Keyword.OTHER, funcSelect(2.15));
    +
    +  assertEquals(Keyword.OTHER, funcSelect(0));
    +  assertEquals(Keyword.OTHER, funcSelect(5));
    +  assertEquals(Keyword.OTHER, funcSelect(10));
    +  assertEquals(Keyword.OTHER, funcSelect(35));
    +  assertEquals(Keyword.OTHER, funcSelect(37));
    +  assertEquals(Keyword.OTHER, funcSelect(40));
    +  assertEquals(Keyword.OTHER, funcSelect(0.0, 1));
    +  assertEquals(Keyword.OTHER, funcSelect(0.5));
    +  assertEquals(Keyword.OTHER, funcSelect(0.6));
    +
    +  assertEquals(Keyword.FEW, funcSelect(2));
    +  assertEquals(Keyword.ONE, funcSelect(2.1));
    +  assertEquals(Keyword.FEW, funcSelect(2.2));
    +  assertEquals(Keyword.FEW, funcSelect(2.3));
    +  assertEquals(Keyword.FEW, funcSelect(2.4));
    +  assertEquals(Keyword.OTHER, funcSelect(2.5));
    +
    +  assertEquals(Keyword.OTHER, funcSelect(20));
    +  assertEquals(Keyword.ONE, funcSelect(21));
    +  assertEquals(Keyword.FEW, funcSelect(22));
    +  assertEquals(Keyword.FEW, funcSelect(23));
    +  assertEquals(Keyword.FEW, funcSelect(24));
    +  assertEquals(Keyword.OTHER, funcSelect(25));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/timezone.js b/mapbox-gl/closure-library/closure/goog/i18n/timezone.js
    new file mode 100644
    index 0000000000..8123beda54
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/timezone.js
    @@ -0,0 +1,341 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions to provide timezone information for use with
    + * date/time format.
    + */
    +
    +goog.provide('goog.i18n.TimeZone');
    +
    +goog.require('goog.array');
    +goog.require('goog.date.DateLike');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * TimeZone class implemented a time zone resolution and name information
    + * source for client applications. The time zone object is initiated from
    + * a time zone information object. Application can initiate a time zone
    + * statically, or it may choose to initiate from a data obtained from server.
    + * Each time zone information array is small, but the whole set of data
    + * is too much for client application to download. If end user is allowed to
    + * change time zone setting, dynamic retrieval should be the method to use.
    + * In case only time zone offset is known, there is a decent fallback
    + * that only use the time zone offset to create a TimeZone object.
    + * A whole set of time zone information array was available under
    + * http://go/js_locale_data. It is generated based on CLDR and
    + * Olson time zone data base (through pytz), and will be updated timely.
    + *
    + * @constructor
    + * @final
    + */
    +goog.i18n.TimeZone = function() {
    +  /**
    +   * The standard time zone id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.timeZoneId_;
    +
    +
    +  /**
    +   * The standard, non-daylight time zone offset, in minutes WEST of UTC.
    +   * @type {number}
    +   * @private
    +   */
    +  this.standardOffset_;
    +
    +
    +  /**
    +   * An array of strings that can have 2 or 4 elements.  The first two elements
    +   * are the long and short names for standard time in this time zone, and the
    +   * last two elements (if present) are the long and short names for daylight
    +   * time in this time zone.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.tzNames_;
    +
    +
    +  /**
    +   * This array specifies the Daylight Saving Time transitions for this time
    +   * zone.  This is a flat array of numbers which are interpreted in pairs:
    +   * [time1, adjustment1, time2, adjustment2, ...] where each time is a DST
    +   * transition point given as a number of hours since 00:00 UTC, January 1,
    +   * 1970, and each adjustment is the adjustment to apply for times after the
    +   * DST transition, given as minutes EAST of UTC.
    +   * @type {Array<number>}
    +   * @private
    +   */
    +  this.transitions_;
    +};
    +
    +
    +/**
    + * The number of milliseconds in an hour.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_ = 3600 * 1000;
    +
    +
    +/**
    + * Indices into the array of time zone names.
    + * @enum {number}
    + */
    +goog.i18n.TimeZone.NameType = {
    +  STD_SHORT_NAME: 0,
    +  STD_LONG_NAME: 1,
    +  DLT_SHORT_NAME: 2,
    +  DLT_LONG_NAME: 3
    +};
    +
    +
    +/**
    + * This factory method creates a time zone instance.  It takes either an object
    + * containing complete time zone information, or a single number representing a
    + * constant time zone offset.  If the latter form is used, DST functionality is
    + * not available.
    + *
    + * @param {number|Object} timeZoneData If this parameter is a number, it should
    + *     indicate minutes WEST of UTC to be used as a constant time zone offset.
    + *     Otherwise, it should be an object with these four fields:
    + *     <ul>
    + *     <li>id: A string ID for the time zone.
    + *     <li>std_offset: The standard time zone offset in minutes EAST of UTC.
    + *     <li>names: An array of four names (standard short name, standard long
    + *           name, daylight short name, daylight long, name)
    + *     <li>transitions: An array of numbers which are interpreted in pairs:
    + *           [time1, adjustment1, time2, adjustment2, ...] where each time is
    + *           a DST transition point given as a number of hours since 00:00 UTC,
    + *           January 1, 1970, and each adjustment is the adjustment to apply
    + *           for times after the DST transition, given as minutes EAST of UTC.
    + *     </ul>
    + * @return {!goog.i18n.TimeZone} A goog.i18n.TimeZone object for the given
    + *     time zone data.
    + */
    +goog.i18n.TimeZone.createTimeZone = function(timeZoneData) {
    +  if (typeof timeZoneData == 'number') {
    +    return goog.i18n.TimeZone.createSimpleTimeZone_(timeZoneData);
    +  }
    +  var tz = new goog.i18n.TimeZone();
    +  tz.timeZoneId_ = timeZoneData['id'];
    +  tz.standardOffset_ = -timeZoneData['std_offset'];
    +  tz.tzNames_ = timeZoneData['names'];
    +  tz.transitions_ = timeZoneData['transitions'];
    +  return tz;
    +};
    +
    +
    +/**
    + * This factory method creates a time zone object with a constant offset.
    + * @param {number} timeZoneOffsetInMinutes Offset in minutes WEST of UTC.
    + * @return {!goog.i18n.TimeZone} A time zone object with the given constant
    + *     offset.  Note that the time zone ID of this object will use the POSIX
    + *     convention, which has a reversed sign ("Etc/GMT+8" means UTC-8 or PST).
    + * @private
    + */
    +goog.i18n.TimeZone.createSimpleTimeZone_ = function(timeZoneOffsetInMinutes) {
    +  var tz = new goog.i18n.TimeZone();
    +  tz.standardOffset_ = timeZoneOffsetInMinutes;
    +  tz.timeZoneId_ =
    +      goog.i18n.TimeZone.composePosixTimeZoneID_(timeZoneOffsetInMinutes);
    +  var str = goog.i18n.TimeZone.composeUTCString_(timeZoneOffsetInMinutes);
    +  tz.tzNames_ = [str, str];
    +  tz.transitions_ = [];
    +  return tz;
    +};
    +
    +
    +/**
    + * Generate a GMT-relative string for a constant time zone offset.
    + * @param {number} offset The time zone offset in minutes WEST of UTC.
    + * @return {string} The GMT string for this offset, which will indicate
    + *     hours EAST of UTC.
    + * @private
    + */
    +goog.i18n.TimeZone.composeGMTString_ = function(offset) {
    +  var parts = ['GMT'];
    +  parts.push(offset <= 0 ? '+' : '-');
    +  offset = Math.abs(offset);
    +  parts.push(goog.string.padNumber(Math.floor(offset / 60) % 100, 2),
    +             ':', goog.string.padNumber(offset % 60, 2));
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Generate a POSIX time zone ID for a constant time zone offset.
    + * @param {number} offset The time zone offset in minutes WEST of UTC.
    + * @return {string} The POSIX time zone ID for this offset, which will indicate
    + *     hours WEST of UTC.
    + * @private
    + */
    +goog.i18n.TimeZone.composePosixTimeZoneID_ = function(offset) {
    +  if (offset == 0) {
    +    return 'Etc/GMT';
    +  }
    +  var parts = ['Etc/GMT', offset < 0 ? '-' : '+'];
    +  offset = Math.abs(offset);
    +  parts.push(Math.floor(offset / 60) % 100);
    +  offset = offset % 60;
    +  if (offset != 0) {
    +    parts.push(':', goog.string.padNumber(offset, 2));
    +  }
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Generate a UTC-relative string for a constant time zone offset.
    + * @param {number} offset The time zone offset in minutes WEST of UTC.
    + * @return {string} The UTC string for this offset, which will indicate
    + *     hours EAST of UTC.
    + * @private
    + */
    +goog.i18n.TimeZone.composeUTCString_ = function(offset) {
    +  if (offset == 0) {
    +    return 'UTC';
    +  }
    +  var parts = ['UTC', offset < 0 ? '+' : '-'];
    +  offset = Math.abs(offset);
    +  parts.push(Math.floor(offset / 60) % 100);
    +  offset = offset % 60;
    +  if (offset != 0) {
    +    parts.push(':', offset);
    +  }
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Convert the contents of time zone object to a timeZoneData object, suitable
    + * for passing to goog.i18n.TimeZone.createTimeZone.
    + * @return {!Object} A timeZoneData object (see the documentation for
    + *     goog.i18n.TimeZone.createTimeZone).
    + */
    +goog.i18n.TimeZone.prototype.getTimeZoneData = function() {
    +  return {
    +    'id': this.timeZoneId_,
    +    'std_offset': -this.standardOffset_,  // note createTimeZone flips the sign
    +    'names': goog.array.clone(this.tzNames_),  // avoid aliasing the array
    +    'transitions': goog.array.clone(this.transitions_)  // avoid aliasing
    +  };
    +};
    +
    +
    +/**
    + * Return the DST adjustment to the time zone offset for a given time.
    + * While Daylight Saving Time is in effect, this number is positive.
    + * Otherwise, it is zero.
    + * @param {goog.date.DateLike} date The time to check.
    + * @return {number} The DST adjustment in minutes EAST of UTC.
    + */
    +goog.i18n.TimeZone.prototype.getDaylightAdjustment = function(date) {
    +  var timeInMs = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
    +                          date.getUTCDate(), date.getUTCHours(),
    +                          date.getUTCMinutes());
    +  var timeInHours = timeInMs / goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_;
    +  var index = 0;
    +  while (index < this.transitions_.length &&
    +         timeInHours >= this.transitions_[index]) {
    +    index += 2;
    +  }
    +  return (index == 0) ? 0 : this.transitions_[index - 1];
    +};
    +
    +
    +/**
    + * Return the GMT representation of this time zone object.
    + * @param {goog.date.DateLike} date The date for which time to retrieve
    + *     GMT string.
    + * @return {string} GMT representation string.
    + */
    +goog.i18n.TimeZone.prototype.getGMTString = function(date) {
    +  return goog.i18n.TimeZone.composeGMTString_(this.getOffset(date));
    +};
    +
    +
    +/**
    + * Get the long time zone name for a given date/time.
    + * @param {goog.date.DateLike} date The time for which to retrieve
    + *     the long time zone name.
    + * @return {string} The long time zone name.
    + */
    +goog.i18n.TimeZone.prototype.getLongName = function(date) {
    +  return this.tzNames_[this.isDaylightTime(date) ?
    +      goog.i18n.TimeZone.NameType.DLT_LONG_NAME :
    +      goog.i18n.TimeZone.NameType.STD_LONG_NAME];
    +};
    +
    +
    +/**
    + * Get the time zone offset in minutes WEST of UTC for a given date/time.
    + * @param {goog.date.DateLike} date The time for which to retrieve
    + *     the time zone offset.
    + * @return {number} The time zone offset in minutes WEST of UTC.
    + */
    +goog.i18n.TimeZone.prototype.getOffset = function(date) {
    +  return this.standardOffset_ - this.getDaylightAdjustment(date);
    +};
    +
    +
    +/**
    + * Get the RFC representation of the time zone for a given date/time.
    + * @param {goog.date.DateLike} date The time for which to retrieve the
    + *     RFC time zone string.
    + * @return {string} The RFC time zone string.
    + */
    +goog.i18n.TimeZone.prototype.getRFCTimeZoneString = function(date) {
    +  var offset = -this.getOffset(date);
    +  var parts = [offset < 0 ? '-' : '+'];
    +  offset = Math.abs(offset);
    +  parts.push(goog.string.padNumber(Math.floor(offset / 60) % 100, 2),
    +             goog.string.padNumber(offset % 60, 2));
    +  return parts.join('');
    +};
    +
    +
    +/**
    + * Get the short time zone name for given date/time.
    + * @param {goog.date.DateLike} date The time for which to retrieve
    + *     the short time zone name.
    + * @return {string} The short time zone name.
    + */
    +goog.i18n.TimeZone.prototype.getShortName = function(date) {
    +  return this.tzNames_[this.isDaylightTime(date) ?
    +      goog.i18n.TimeZone.NameType.DLT_SHORT_NAME :
    +      goog.i18n.TimeZone.NameType.STD_SHORT_NAME];
    +};
    +
    +
    +/**
    + * Return the time zone ID for this time zone.
    + * @return {string} The time zone ID.
    + */
    +goog.i18n.TimeZone.prototype.getTimeZoneId = function() {
    +  return this.timeZoneId_;
    +};
    +
    +
    +/**
    + * Check if Daylight Saving Time is in effect at a given time in this time zone.
    + * @param {goog.date.DateLike} date The time to check.
    + * @return {boolean} True if Daylight Saving Time is in effect.
    + */
    +goog.i18n.TimeZone.prototype.isDaylightTime = function(date) {
    +  return this.getDaylightAdjustment(date) > 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.html b/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.html
    new file mode 100644
    index 0000000000..94e6132c83
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.TimeZone
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.TimeZoneTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.js b/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.js
    new file mode 100644
    index 0000000000..3acef6cf34
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/timezone_test.js
    @@ -0,0 +1,160 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.TimeZoneTest');
    +goog.setTestOnly('goog.i18n.TimeZoneTest');
    +
    +goog.require('goog.i18n.TimeZone');
    +goog.require('goog.testing.jsunit');
    +
    +// Where could such data be found
    +// In js_i18n_data in http://go/i18n_dir, we have a bunch of files with names
    +// like TimeZoneConstant__<locale>.js
    +// We strongly discourage you to use them directly as those data can make
    +// your client code bloated. You should try to provide this data from server
    +// in a selective manner. In typical scenario, user's time zone is retrieved
    +// and only data for that time zone should be provided.
    +// This piece of data is in JSON format. It requires double quote.
    +var americaLosAngelesData = {
    +  'transitions': [
    +    2770, 60, 7137, 0, 11506, 60, 16041, 0, 20410, 60, 24777, 0, 29146, 60,
    +    33513, 0, 35194, 60, 42249, 0, 45106, 60, 50985, 0, 55354, 60, 59889, 0,
    +    64090, 60, 68625, 0, 72994, 60, 77361, 0, 81730, 60, 86097, 0, 90466, 60,
    +    94833, 0, 99202, 60, 103569, 0, 107938, 60, 112473, 0, 116674, 60, 121209,
    +    0, 125578, 60, 129945, 0, 134314, 60, 138681, 0, 143050, 60, 147417, 0,
    +    151282, 60, 156153, 0, 160018, 60, 165057, 0, 168754, 60, 173793, 0,
    +    177490, 60, 182529, 0, 186394, 60, 191265, 0, 195130, 60, 200001, 0,
    +    203866, 60, 208905, 0, 212602, 60, 217641, 0, 221338, 60, 226377, 0,
    +    230242, 60, 235113, 0, 238978, 60, 243849, 0, 247714, 60, 252585, 0,
    +    256450, 60, 261489, 0, 265186, 60, 270225, 0, 273922, 60, 278961, 0,
    +    282826, 60, 287697, 0, 291562, 60, 296433, 0, 300298, 60, 305337, 0,
    +    309034, 60, 314073, 0, 317770, 60, 322809, 0, 326002, 60, 331713, 0,
    +    334738, 60, 340449, 0, 343474, 60, 349185, 0, 352378, 60, 358089, 0,
    +    361114, 60, 366825, 0, 369850, 60, 375561, 0, 378586, 60, 384297, 0,
    +    387322, 60, 393033, 0, 396058, 60, 401769, 0, 404962, 60, 410673, 0,
    +    413698, 60, 419409, 0, 422434, 60, 428145, 0, 431170, 60, 436881, 0,
    +    439906, 60, 445617, 0, 448810, 60, 454521, 0, 457546, 60, 463257, 0,
    +    466282, 60, 471993, 0, 475018, 60, 480729, 0, 483754, 60, 489465, 0,
    +    492490, 60, 498201, 0, 501394, 60, 507105, 0, 510130, 60, 515841, 0,
    +    518866, 60, 524577, 0, 527602, 60, 533313, 0, 536338, 60, 542049, 0,
    +    545242, 60, 550953, 0, 553978, 60, 559689, 0, 562714, 60, 568425, 0,
    +    571450, 60, 577161, 0, 580186, 60, 585897, 0, 588922, 60, 594633, 0
    +  ],
    +  'names': ['PST', 'Pacific Standard Time', 'PDT', 'Pacific Daylight Time'],
    +  'id': 'America/Los_Angeles',
    +  'std_offset': -480
    +};
    +
    +function testIsDaylightTime() {
    +  var usPacific = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var dt = new Date(2007, 7 - 1, 1);
    +  assertTrue(usPacific.isDaylightTime(dt));
    +  // 2007/03/11 2:00am has daylight change. We set time through UTC so that
    +  // this test won't be affected by browser's local time handling.
    +  dt = new Date(2007, 3 - 1, 11);
    +  // The date above is created with a timezone of the computer it is run on.
    +  // Therefore the UTC day has to be set explicitly to be sure that the date
    +  // is correct for timezones east of Greenwich  where 2007/3/11 0:00 is still
    +  // 2007/03/10 in UTC.
    +  dt.setUTCDate(11);
    +  dt.setUTCHours(2 + 8);
    +  dt.setUTCMinutes(1);
    +  assertTrue(usPacific.isDaylightTime(dt));
    +  dt.setUTCHours(1 + 8);
    +  dt.setUTCMinutes(59);
    +  assertTrue(!usPacific.isDaylightTime(dt));
    +
    +  dt = new Date(2007, 11 - 1, 4);
    +  // Set the UTC day explicitly to make it work in timezones east of
    +  // Greenwich.
    +  dt.setUTCDate(4);
    +  dt.setUTCHours(2 + 7);
    +  dt.setUTCMinutes(1);
    +  assertTrue(!usPacific.isDaylightTime(dt));
    +
    +  // there seems to be a browser bug. local time 1:59am should still be PDT.
    +  dt.setUTCHours(0 + 7);
    +  dt.setUTCMinutes(59);
    +  assertTrue(usPacific.isDaylightTime(dt));
    +}
    +
    +function testGetters() {
    +  var date = new Date();
    +  var usPacific = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  if (usPacific.isDaylightTime(date)) {
    +    assertEquals(420, usPacific.getOffset(date));
    +  } else {
    +    assertEquals(480, usPacific.getOffset(date));
    +  }
    +  assertEquals('America/Los_Angeles', usPacific.getTimeZoneId());
    +  assertObjectEquals(americaLosAngelesData, usPacific.getTimeZoneData());
    +}
    +
    +function testNames() {
    +  var usPacific = goog.i18n.TimeZone.createTimeZone(americaLosAngelesData);
    +  var dt = new Date(2007, 7 - 1, 1);
    +  assertTrue(usPacific.isDaylightTime(dt));
    +  assertEquals('PDT', usPacific.getShortName(dt));
    +  assertEquals('Pacific Daylight Time', usPacific.getLongName(dt));
    +
    +  dt = new Date(2007, 12 - 1, 1);
    +  assertTrue(!usPacific.isDaylightTime(dt));
    +  assertEquals('PST', usPacific.getShortName(dt));
    +  assertEquals('Pacific Standard Time', usPacific.getLongName(dt));
    +}
    +
    +function testSimpleTimeZonePositive() {
    +  var date = new Date();
    +  var simpleTimeZone = goog.i18n.TimeZone.createTimeZone(480);
    +  assertEquals(480, simpleTimeZone.getOffset(date));
    +  assertEquals('GMT-08:00', simpleTimeZone.getGMTString(date));
    +  assertEquals('Etc/GMT+8', simpleTimeZone.getTimeZoneId());
    +  assertEquals('UTC-8', simpleTimeZone.getLongName(date));
    +  assertEquals('UTC-8', simpleTimeZone.getShortName(date));
    +  assertEquals('-0800', simpleTimeZone.getRFCTimeZoneString(date));
    +  assertEquals(false, simpleTimeZone.isDaylightTime(date));
    +
    +  simpleTimeZone = goog.i18n.TimeZone.createTimeZone(630);
    +  assertEquals(630, simpleTimeZone.getOffset(date));
    +  assertEquals('GMT-10:30', simpleTimeZone.getGMTString(date));
    +  assertEquals('Etc/GMT+10:30', simpleTimeZone.getTimeZoneId());
    +  assertEquals('UTC-10:30', simpleTimeZone.getLongName(date));
    +  assertEquals('UTC-10:30', simpleTimeZone.getShortName(date));
    +  assertEquals('-1030', simpleTimeZone.getRFCTimeZoneString(date));
    +  assertEquals(false, simpleTimeZone.isDaylightTime(date));
    +}
    +
    +function testSimpleTimeZoneNegative() {
    +  var date = new Date();
    +  var simpleTimeZone = goog.i18n.TimeZone.createTimeZone(-480);
    +  assertEquals(-480, simpleTimeZone.getOffset(date));
    +  assertEquals('GMT+08:00', simpleTimeZone.getGMTString(date));
    +  assertEquals('Etc/GMT-8', simpleTimeZone.getTimeZoneId());
    +  assertEquals('UTC+8', simpleTimeZone.getLongName(date));
    +  assertEquals('UTC+8', simpleTimeZone.getShortName(date));
    +  assertEquals('+0800', simpleTimeZone.getRFCTimeZoneString(date));
    +  assertEquals(false, simpleTimeZone.isDaylightTime(date));
    +}
    +
    +function testSimpleTimeZoneZero() {
    +  var date = new Date();
    +  var simpleTimeZone = goog.i18n.TimeZone.createTimeZone(0);
    +  assertEquals(0, simpleTimeZone.getOffset(date));
    +  assertEquals('GMT+00:00', simpleTimeZone.getGMTString(date));
    +  assertEquals('Etc/GMT', simpleTimeZone.getTimeZoneId());
    +  assertEquals('UTC', simpleTimeZone.getLongName(date));
    +  assertEquals('UTC', simpleTimeZone.getShortName(date));
    +  assertEquals('+0000', simpleTimeZone.getRFCTimeZoneString(date));
    +  assertEquals(false, simpleTimeZone.isDaylightTime(date));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar.js
    new file mode 100644
    index 0000000000..b4568ab312
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar.js
    @@ -0,0 +1,1368 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Collection of utility functions for Unicode character.
    + *
    + */
    +
    +goog.provide('goog.i18n.uChar');
    +
    +
    +/**
    + * Map used for looking up the char data.  Will be created lazily.
    + * @type {Object}
    + * @private
    + */
    +goog.i18n.uChar.charData_ = null;
    +
    +
    +// Constants for handling Unicode supplementary characters (surrogate pairs).
    +
    +
    +/**
    + * The minimum value for Supplementary code points.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ = 0x10000;
    +
    +
    +/**
    + * The highest Unicode code point value (scalar value) according to the Unicode
    + * Standard.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.CODE_POINT_MAX_VALUE_ = 0x10FFFF;
    +
    +
    +/**
    + * Lead surrogate minimum value.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ = 0xD800;
    +
    +
    +/**
    + * Lead surrogate maximum value.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.LEAD_SURROGATE_MAX_VALUE_ = 0xDBFF;
    +
    +
    +/**
    + * Trail surrogate minimum value.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ = 0xDC00;
    +
    +
    +/**
    + * Trail surrogate maximum value.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.TRAIL_SURROGATE_MAX_VALUE_ = 0xDFFF;
    +
    +
    +/**
    + * The number of least significant bits of a supplementary code point that in
    + * UTF-16 become the least significant bits of the trail surrogate. The rest of
    + * the in-use bits of the supplementary code point become the least significant
    + * bits of the lead surrogate.
    + * @type {number}
    + * @private
    + */
    +goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_ = 10;
    +
    +
    +/**
    + * Gets the U+ notation string of a Unicode character. Ex: 'U+0041' for 'A'.
    + * @param {string} ch The given character.
    + * @return {string} The U+ notation of the given character.
    + */
    +goog.i18n.uChar.toHexString = function(ch) {
    +  var chCode = goog.i18n.uChar.toCharCode(ch);
    +  var chCodeStr = 'U+' + goog.i18n.uChar.padString_(
    +      chCode.toString(16).toUpperCase(), 4, '0');
    +
    +  return chCodeStr;
    +};
    +
    +
    +/**
    + * Gets a string padded with given character to get given size.
    + * @param {string} str The given string to be padded.
    + * @param {number} length The target size of the string.
    + * @param {string} ch The character to be padded with.
    + * @return {string} The padded string.
    + * @private
    + */
    +goog.i18n.uChar.padString_ = function(str, length, ch) {
    +  while (str.length < length) {
    +    str = ch + str;
    +  }
    +  return str;
    +};
    +
    +
    +/**
    + * Gets Unicode value of the given character.
    + * @param {string} ch The given character, which in the case of a supplementary
    + * character is actually a surrogate pair. The remainder of the string is
    + * ignored.
    + * @return {number} The Unicode value of the character.
    + */
    +goog.i18n.uChar.toCharCode = function(ch) {
    +  return goog.i18n.uChar.getCodePointAround(ch, 0);
    +};
    +
    +
    +/**
    + * Gets a character from the given Unicode value. If the given code point is not
    + * a valid Unicode code point, null is returned.
    + * @param {number} code The Unicode value of the character.
    + * @return {?string} The character corresponding to the given Unicode value.
    + */
    +goog.i18n.uChar.fromCharCode = function(code) {
    +  if (!goog.isDefAndNotNull(code) ||
    +      !(code >= 0 && code <= goog.i18n.uChar.CODE_POINT_MAX_VALUE_)) {
    +    return null;
    +  }
    +  if (goog.i18n.uChar.isSupplementaryCodePoint(code)) {
    +    // First, we split the code point into the trail surrogate part (the
    +    // TRAIL_SURROGATE_BIT_COUNT_ least significant bits) and the lead surrogate
    +    // part (the rest of the bits, shifted down; note that for now this includes
    +    // the supplementary offset, also shifted down, to be subtracted off below).
    +    var leadBits = code >> goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_;
    +    var trailBits = code &
    +        // A bit-mask to get the TRAIL_SURROGATE_BIT_COUNT_ (i.e. 10) least
    +        // significant bits. 1 << 10 = 0x0400. 0x0400 - 1 = 0x03FF.
    +        ((1 << goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_) - 1);
    +
    +    // Now we calculate the code point of each surrogate by adding each offset
    +    // to the corresponding base code point.
    +    var leadCodePoint = leadBits + (goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ -
    +        // Subtract off the supplementary offset, which had been shifted down
    +        // with the rest of leadBits. We do this here instead of before the
    +        // shift in order to save a separate subtraction step.
    +        (goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ >>
    +        goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_));
    +    var trailCodePoint = trailBits + goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_;
    +
    +    // Convert the code points into a 2-character long string.
    +    return String.fromCharCode(leadCodePoint) +
    +           String.fromCharCode(trailCodePoint);
    +  }
    +  return String.fromCharCode(code);
    +};
    +
    +
    +/**
    + * Returns the Unicode code point at the specified index.
    + *
    + * If the char value specified at the given index is in the leading-surrogate
    + * range, and the following index is less than the length of {@code string}, and
    + * the char value at the following index is in the trailing-surrogate range,
    + * then the supplementary code point corresponding to this surrogate pair is
    + * returned.
    + *
    + * If the char value specified at the given index is in the trailing-surrogate
    + * range, and the preceding index is not before the start of {@code string}, and
    + * the char value at the preceding index is in the leading-surrogate range, then
    + * the negated supplementary code point corresponding to this surrogate pair is
    + * returned.
    + *
    + * The negation allows the caller to differentiate between the case where the
    + * given index is at the leading surrogate and the one where it is at the
    + * trailing surrogate, and thus deduce where the next character starts and
    + * preceding character ends.
    + *
    + * Otherwise, the char value at the given index is returned. Thus, a leading
    + * surrogate is returned when it is not followed by a trailing surrogate, and a
    + * trailing surrogate is returned when it is not preceded by a leading
    + * surrogate.
    + *
    + * @param {!string} string The string.
    + * @param {number} index The index from which the code point is to be retrieved.
    + * @return {number} The code point at the given index. If the given index is
    + * that of the start (i.e. lead surrogate) of a surrogate pair, returns the code
    + * point encoded by the pair. If the given index is that of the end (i.e. trail
    + * surrogate) of a surrogate pair, returns the negated code pointed encoded by
    + * the pair.
    + */
    +goog.i18n.uChar.getCodePointAround = function(string, index) {
    +  var charCode = string.charCodeAt(index);
    +  if (goog.i18n.uChar.isLeadSurrogateCodePoint(charCode) &&
    +      index + 1 < string.length) {
    +    var trail = string.charCodeAt(index + 1);
    +    if (goog.i18n.uChar.isTrailSurrogateCodePoint(trail)) {
    +      // Part of a surrogate pair.
    +      return /** @type {number} */ (goog.i18n.uChar.
    +          buildSupplementaryCodePoint(charCode, trail));
    +    }
    +  } else if (goog.i18n.uChar.isTrailSurrogateCodePoint(charCode) &&
    +      index > 0) {
    +    var lead = string.charCodeAt(index - 1);
    +    if (goog.i18n.uChar.isLeadSurrogateCodePoint(lead)) {
    +      // Part of a surrogate pair.
    +      return /** @type {number} */ (-goog.i18n.uChar.
    +          buildSupplementaryCodePoint(lead, charCode));
    +    }
    +  }
    +  return charCode;
    +};
    +
    +
    +/**
    + * Determines the length of the string needed to represent the specified
    + * Unicode code point.
    + * @param {number} codePoint
    + * @return {number} 2 if codePoint is a supplementary character, 1 otherwise.
    + */
    +goog.i18n.uChar.charCount = function(codePoint) {
    +  return goog.i18n.uChar.isSupplementaryCodePoint(codePoint) ? 2 : 1;
    +};
    +
    +
    +/**
    + * Determines whether the specified Unicode code point is in the supplementary
    + * Unicode characters range.
    + * @param {number} codePoint
    + * @return {boolean} Whether then given code point is a supplementary character.
    + */
    +goog.i18n.uChar.isSupplementaryCodePoint = function(codePoint) {
    +  return codePoint >= goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ &&
    +         codePoint <= goog.i18n.uChar.CODE_POINT_MAX_VALUE_;
    +};
    +
    +
    +/**
    + * Gets whether the given code point is a leading surrogate character.
    + * @param {number} codePoint
    + * @return {boolean} Whether the given code point is a leading surrogate
    + * character.
    + */
    +goog.i18n.uChar.isLeadSurrogateCodePoint = function(codePoint) {
    +  return codePoint >= goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ &&
    +         codePoint <= goog.i18n.uChar.LEAD_SURROGATE_MAX_VALUE_;
    +};
    +
    +
    +/**
    + * Gets whether the given code point is a trailing surrogate character.
    + * @param {number} codePoint
    + * @return {boolean} Whether the given code point is a trailing surrogate
    + * character.
    + */
    +goog.i18n.uChar.isTrailSurrogateCodePoint = function(codePoint) {
    +  return codePoint >= goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ &&
    +         codePoint <= goog.i18n.uChar.TRAIL_SURROGATE_MAX_VALUE_;
    +};
    +
    +
    +/**
    + * Composes a supplementary Unicode code point from the given UTF-16 surrogate
    + * pair. If leadSurrogate isn't a leading surrogate code point or trailSurrogate
    + * isn't a trailing surrogate code point, null is returned.
    + * @param {number} lead The leading surrogate code point.
    + * @param {number} trail The trailing surrogate code point.
    + * @return {?number} The supplementary Unicode code point obtained by decoding
    + * the given UTF-16 surrogate pair.
    + */
    +goog.i18n.uChar.buildSupplementaryCodePoint = function(lead, trail) {
    +  if (goog.i18n.uChar.isLeadSurrogateCodePoint(lead) &&
    +      goog.i18n.uChar.isTrailSurrogateCodePoint(trail)) {
    +    var shiftedLeadOffset = (lead <<
    +        goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_) -
    +        (goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ <<
    +        goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_);
    +    var trailOffset = trail - goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ +
    +        goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_;
    +    return shiftedLeadOffset + trailOffset;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Gets the name of a character, if available, returns null otherwise.
    + * @param {string} ch The character.
    + * @return {?string} The name of the character.
    + */
    +goog.i18n.uChar.toName = function(ch) {
    +  if (!goog.i18n.uChar.charData_) {
    +    goog.i18n.uChar.createCharData();
    +  }
    +
    +  var names = goog.i18n.uChar.charData_;
    +  var chCode = goog.i18n.uChar.toCharCode(ch);
    +  var chCodeStr = chCode + '';
    +
    +  if (ch in names) {
    +    return names[ch];
    +  } else if (chCodeStr in names) {
    +    return names[chCode];
    +  } else if (0xFE00 <= chCode && chCode <= 0xFE0F ||
    +      0xE0100 <= chCode && chCode <= 0xE01EF) {
    +    var seqnum;
    +    if (0xFE00 <= chCode && chCode <= 0xFE0F) {
    +      // Variation selectors from 1 to 16.
    +      seqnum = chCode - 0xFDFF;
    +    } else {
    +      // Variation selectors from 17 to 256.
    +      seqnum = chCode - 0xE00EF;
    +    }
    +
    +    /** @desc Variation selector with the sequence number. */
    +    var MSG_VARIATION_SELECTOR_SEQNUM =
    +        goog.getMsg('Variation Selector - {$seqnum}', {'seqnum': seqnum});
    +    return MSG_VARIATION_SELECTOR_SEQNUM;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Following lines are programatically created.
    + * Details: https://sites/cibu/character-picker.
    + **/
    +
    +
    +/**
    + * Sets up the character map, lazily.  Some characters are indexed by their
    + * decimal value.
    + * @protected
    + */
    +goog.i18n.uChar.createCharData = function() {
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARABIC_SIGN_SANAH = goog.getMsg('Arabic Sign Sanah');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_CANADIAN_SYLLABICS_HYPHEN =
    +      goog.getMsg('Canadian Syllabics Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARABIC_SIGN_SAFHA = goog.getMsg('Arabic Sign Safha');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARABIC_FOOTNOTE_MARKER = goog.getMsg('Arabic Footnote Marker');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_FOUR_PER_EM_SPACE = goog.getMsg('Four-per-em Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_THREE_PER_EM_SPACE = goog.getMsg('Three-per-em Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_FIGURE_SPACE = goog.getMsg('Figure Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MONGOLIAN_SOFT_HYPHEN = goog.getMsg('Mongolian Soft Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_THIN_SPACE = goog.getMsg('Thin Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SOFT_HYPHEN = goog.getMsg('Soft Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ZERO_WIDTH_SPACE = goog.getMsg('Zero Width Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARMENIAN_HYPHEN = goog.getMsg('Armenian Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ZERO_WIDTH_JOINER = goog.getMsg('Zero Width Joiner');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EM_SPACE = goog.getMsg('Em Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SYRIAC_ABBREVIATION_MARK = goog.getMsg('Syriac Abbreviation Mark');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MONGOLIAN_VOWEL_SEPARATOR =
    +      goog.getMsg('Mongolian Vowel Separator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_NON_BREAKING_HYPHEN = goog.getMsg('Non-breaking Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HYPHEN = goog.getMsg('Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EM_QUAD = goog.getMsg('Em Quad');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EN_SPACE = goog.getMsg('En Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HORIZONTAL_BAR = goog.getMsg('Horizontal Bar');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EM_DASH = goog.getMsg('Em Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_DOUBLE_OBLIQUE_HYPHEN = goog.getMsg('Double Oblique Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_END_PHRASE =
    +      goog.getMsg('Musical Symbol End Phrase');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MEDIUM_MATHEMATICAL_SPACE =
    +      goog.getMsg('Medium Mathematical Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_WAVE_DASH = goog.getMsg('Wave Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SPACE = goog.getMsg('Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HYPHEN_WITH_DIAERESIS = goog.getMsg('Hyphen With Diaeresis');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EN_QUAD = goog.getMsg('En Quad');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_RIGHT_TO_LEFT_EMBEDDING = goog.getMsg('Right-to-left Embedding');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SIX_PER_EM_SPACE = goog.getMsg('Six-per-em Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HYPHEN_MINUS = goog.getMsg('Hyphen-minus');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_POP_DIRECTIONAL_FORMATTING =
    +      goog.getMsg('Pop Directional Formatting');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_NARROW_NO_BREAK_SPACE = goog.getMsg('Narrow No-break Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_RIGHT_TO_LEFT_OVERRIDE = goog.getMsg('Right-to-left Override');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_PRESENTATION_FORM_FOR_VERTICAL_EM_DASH =
    +      goog.getMsg('Presentation Form For Vertical Em Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_WAVY_DASH = goog.getMsg('Wavy Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_PRESENTATION_FORM_FOR_VERTICAL_EN_DASH =
    +      goog.getMsg('Presentation Form For Vertical En Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_KHMER_VOWEL_INHERENT_AA = goog.getMsg('Khmer Vowel Inherent Aa');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_KHMER_VOWEL_INHERENT_AQ = goog.getMsg('Khmer Vowel Inherent Aq');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_PUNCTUATION_SPACE = goog.getMsg('Punctuation Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HALFWIDTH_HANGUL_FILLER = goog.getMsg('Halfwidth Hangul Filler');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_KAITHI_NUMBER_SIGN = goog.getMsg('Kaithi Number Sign');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_LEFT_TO_RIGHT_EMBEDDING = goog.getMsg('Left-to-right Embedding');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HEBREW_PUNCTUATION_MAQAF = goog.getMsg('Hebrew Punctuation Maqaf');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_IDEOGRAPHIC_SPACE = goog.getMsg('Ideographic Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HAIR_SPACE = goog.getMsg('Hair Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_NO_BREAK_SPACE = goog.getMsg('No-break Space');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_FULLWIDTH_HYPHEN_MINUS = goog.getMsg('Fullwidth Hyphen-minus');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_PARAGRAPH_SEPARATOR = goog.getMsg('Paragraph Separator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_LEFT_TO_RIGHT_OVERRIDE = goog.getMsg('Left-to-right Override');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SMALL_HYPHEN_MINUS = goog.getMsg('Small Hyphen-minus');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_COMBINING_GRAPHEME_JOINER =
    +      goog.getMsg('Combining Grapheme Joiner');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ZERO_WIDTH_NON_JOINER = goog.getMsg('Zero Width Non-joiner');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_BEGIN_PHRASE =
    +      goog.getMsg('Musical Symbol Begin Phrase');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARABIC_NUMBER_SIGN = goog.getMsg('Arabic Number Sign');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_RIGHT_TO_LEFT_MARK = goog.getMsg('Right-to-left Mark');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_OGHAM_SPACE_MARK = goog.getMsg('Ogham Space Mark');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_SMALL_EM_DASH = goog.getMsg('Small Em Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_LEFT_TO_RIGHT_MARK = goog.getMsg('Left-to-right Mark');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ARABIC_END_OF_AYAH = goog.getMsg('Arabic End Of Ayah');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HANGUL_CHOSEONG_FILLER = goog.getMsg('Hangul Choseong Filler');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HANGUL_FILLER = goog.getMsg('Hangul Filler');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_FUNCTION_APPLICATION = goog.getMsg('Function Application');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_HANGUL_JUNGSEONG_FILLER = goog.getMsg('Hangul Jungseong Filler');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INVISIBLE_SEPARATOR = goog.getMsg('Invisible Separator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INVISIBLE_TIMES = goog.getMsg('Invisible Times');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INVISIBLE_PLUS = goog.getMsg('Invisible Plus');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_WORD_JOINER = goog.getMsg('Word Joiner');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_LINE_SEPARATOR = goog.getMsg('Line Separator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_KATAKANA_HIRAGANA_DOUBLE_HYPHEN =
    +      goog.getMsg('Katakana-hiragana Double Hyphen');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_EN_DASH = goog.getMsg('En Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_BEGIN_BEAM =
    +      goog.getMsg('Musical Symbol Begin Beam');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_FIGURE_DASH = goog.getMsg('Figure Dash');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_BEGIN_TIE = goog.getMsg('Musical Symbol Begin Tie');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_END_BEAM = goog.getMsg('Musical Symbol End Beam');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_BEGIN_SLUR =
    +      goog.getMsg('Musical Symbol Begin Slur');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_END_TIE = goog.getMsg('Musical Symbol End Tie');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INTERLINEAR_ANNOTATION_ANCHOR =
    +      goog.getMsg('Interlinear Annotation Anchor');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_MUSICAL_SYMBOL_END_SLUR = goog.getMsg('Musical Symbol End Slur');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INTERLINEAR_ANNOTATION_TERMINATOR =
    +      goog.getMsg('Interlinear Annotation Terminator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_INTERLINEAR_ANNOTATION_SEPARATOR =
    +      goog.getMsg('Interlinear Annotation Separator');
    +
    +
    +  /**
    +   * @desc Name for a symbol, character or a letter. Used in a pop-up balloon,
    +   *   shown to a document editing user trying to insert a special character.
    +   *   The balloon help would appear while the user hovers over the character
    +   *   displayed. Newlines are not allowed; translation should be a noun and
    +   *   as consise as possible. More details:
    +   *   docs/fileview?id=0B8NbxddKsFtwYjExMGJjNzgtYjkzOS00NjdiLTlmOGQtOGVhZDkyZDU5YjM4.
    +   */
    +  var MSG_CP_ZERO_WIDTH_NO_BREAK_SPACE =
    +      goog.getMsg('Zero Width No-break Space');
    +
    +  goog.i18n.uChar.charData_ = {
    +    '\u0601': MSG_CP_ARABIC_SIGN_SANAH,
    +    '\u1400': MSG_CP_CANADIAN_SYLLABICS_HYPHEN,
    +    '\u0603': MSG_CP_ARABIC_SIGN_SAFHA,
    +    '\u0602': MSG_CP_ARABIC_FOOTNOTE_MARKER,
    +    '\u2005': MSG_CP_FOUR_PER_EM_SPACE,
    +    '\u2004': MSG_CP_THREE_PER_EM_SPACE,
    +    '\u2007': MSG_CP_FIGURE_SPACE,
    +    '\u1806': MSG_CP_MONGOLIAN_SOFT_HYPHEN,
    +    '\u2009': MSG_CP_THIN_SPACE,
    +    '\u00AD': MSG_CP_SOFT_HYPHEN,
    +    '\u200B': MSG_CP_ZERO_WIDTH_SPACE,
    +    '\u058A': MSG_CP_ARMENIAN_HYPHEN,
    +    '\u200D': MSG_CP_ZERO_WIDTH_JOINER,
    +    '\u2003': MSG_CP_EM_SPACE,
    +    '\u070F': MSG_CP_SYRIAC_ABBREVIATION_MARK,
    +    '\u180E': MSG_CP_MONGOLIAN_VOWEL_SEPARATOR,
    +    '\u2011': MSG_CP_NON_BREAKING_HYPHEN,
    +    '\u2010': MSG_CP_HYPHEN,
    +    '\u2001': MSG_CP_EM_QUAD,
    +    '\u2002': MSG_CP_EN_SPACE,
    +    '\u2015': MSG_CP_HORIZONTAL_BAR,
    +    '\u2014': MSG_CP_EM_DASH,
    +    '\u2E17': MSG_CP_DOUBLE_OBLIQUE_HYPHEN,
    +    '\u1D17A': MSG_CP_MUSICAL_SYMBOL_END_PHRASE,
    +    '\u205F': MSG_CP_MEDIUM_MATHEMATICAL_SPACE,
    +    '\u301C': MSG_CP_WAVE_DASH,
    +    ' ': MSG_CP_SPACE,
    +    '\u2E1A': MSG_CP_HYPHEN_WITH_DIAERESIS,
    +    '\u2000': MSG_CP_EN_QUAD,
    +    '\u202B': MSG_CP_RIGHT_TO_LEFT_EMBEDDING,
    +    '\u2006': MSG_CP_SIX_PER_EM_SPACE,
    +    '-': MSG_CP_HYPHEN_MINUS,
    +    '\u202C': MSG_CP_POP_DIRECTIONAL_FORMATTING,
    +    '\u202F': MSG_CP_NARROW_NO_BREAK_SPACE,
    +    '\u202E': MSG_CP_RIGHT_TO_LEFT_OVERRIDE,
    +    '\uFE31': MSG_CP_PRESENTATION_FORM_FOR_VERTICAL_EM_DASH,
    +    '\u3030': MSG_CP_WAVY_DASH,
    +    '\uFE32': MSG_CP_PRESENTATION_FORM_FOR_VERTICAL_EN_DASH,
    +    '\u17B5': MSG_CP_KHMER_VOWEL_INHERENT_AA,
    +    '\u17B4': MSG_CP_KHMER_VOWEL_INHERENT_AQ,
    +    '\u2008': MSG_CP_PUNCTUATION_SPACE,
    +    '\uFFA0': MSG_CP_HALFWIDTH_HANGUL_FILLER,
    +    '\u110BD': MSG_CP_KAITHI_NUMBER_SIGN,
    +    '\u202A': MSG_CP_LEFT_TO_RIGHT_EMBEDDING,
    +    '\u05BE': MSG_CP_HEBREW_PUNCTUATION_MAQAF,
    +    '\u3000': MSG_CP_IDEOGRAPHIC_SPACE,
    +    '\u200A': MSG_CP_HAIR_SPACE,
    +    '\u00A0': MSG_CP_NO_BREAK_SPACE,
    +    '\uFF0D': MSG_CP_FULLWIDTH_HYPHEN_MINUS,
    +    '8233': MSG_CP_PARAGRAPH_SEPARATOR,
    +    '\u202D': MSG_CP_LEFT_TO_RIGHT_OVERRIDE,
    +    '\uFE63': MSG_CP_SMALL_HYPHEN_MINUS,
    +    '\u034F': MSG_CP_COMBINING_GRAPHEME_JOINER,
    +    '\u200C': MSG_CP_ZERO_WIDTH_NON_JOINER,
    +    '\u1D179': MSG_CP_MUSICAL_SYMBOL_BEGIN_PHRASE,
    +    '\u0600': MSG_CP_ARABIC_NUMBER_SIGN,
    +    '\u200F': MSG_CP_RIGHT_TO_LEFT_MARK,
    +    '\u1680': MSG_CP_OGHAM_SPACE_MARK,
    +    '\uFE58': MSG_CP_SMALL_EM_DASH,
    +    '\u200E': MSG_CP_LEFT_TO_RIGHT_MARK,
    +    '\u06DD': MSG_CP_ARABIC_END_OF_AYAH,
    +    '\u115F': MSG_CP_HANGUL_CHOSEONG_FILLER,
    +    '\u3164': MSG_CP_HANGUL_FILLER,
    +    '\u2061': MSG_CP_FUNCTION_APPLICATION,
    +    '\u1160': MSG_CP_HANGUL_JUNGSEONG_FILLER,
    +    '\u2063': MSG_CP_INVISIBLE_SEPARATOR,
    +    '\u2062': MSG_CP_INVISIBLE_TIMES,
    +    '\u2064': MSG_CP_INVISIBLE_PLUS,
    +    '\u2060': MSG_CP_WORD_JOINER,
    +    '8232': MSG_CP_LINE_SEPARATOR,
    +    '\u30A0': MSG_CP_KATAKANA_HIRAGANA_DOUBLE_HYPHEN,
    +    '\u2013': MSG_CP_EN_DASH,
    +    '\u1D173': MSG_CP_MUSICAL_SYMBOL_BEGIN_BEAM,
    +    '\u2012': MSG_CP_FIGURE_DASH,
    +    '\u1D175': MSG_CP_MUSICAL_SYMBOL_BEGIN_TIE,
    +    '\u1D174': MSG_CP_MUSICAL_SYMBOL_END_BEAM,
    +    '\u1D177': MSG_CP_MUSICAL_SYMBOL_BEGIN_SLUR,
    +    '\u1D176': MSG_CP_MUSICAL_SYMBOL_END_TIE,
    +    '\uFFF9': MSG_CP_INTERLINEAR_ANNOTATION_ANCHOR,
    +    '\u1D178': MSG_CP_MUSICAL_SYMBOL_END_SLUR,
    +    '\uFFFB': MSG_CP_INTERLINEAR_ANNOTATION_TERMINATOR,
    +    '\uFFFA': MSG_CP_INTERLINEAR_ANNOTATION_SEPARATOR,
    +    '\uFEFF': MSG_CP_ZERO_WIDTH_NO_BREAK_SPACE
    +  };
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher.js
    new file mode 100644
    index 0000000000..5e1380f942
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher.js
    @@ -0,0 +1,74 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Object which fetches Unicode codepoint names that are locally
    + * stored in a bundled database. Currently, only invisible characters are
    + * covered by this database. See the goog.i18n.uChar.RemoteNameFetcher class for
    + * a remote database option.
    + */
    +
    +goog.provide('goog.i18n.uChar.LocalNameFetcher');
    +
    +goog.require('goog.i18n.uChar');
    +goog.require('goog.i18n.uChar.NameFetcher');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Builds the NameFetcherLocal object. This is a simple object which retrieves
    + * character names from a local bundled database. This database only covers
    + * invisible characters. See the goog.i18n.uChar class for more details.
    + *
    + * @constructor
    + * @implements {goog.i18n.uChar.NameFetcher}
    + * @final
    + */
    +goog.i18n.uChar.LocalNameFetcher = function() {
    +};
    +
    +
    +/**
    + * A reference to the LocalNameFetcher logger.
    + *
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.i18n.uChar.LocalNameFetcher.logger_ =
    +    goog.log.getLogger('goog.i18n.uChar.LocalNameFetcher');
    +
    +
    +/** @override */
    +goog.i18n.uChar.LocalNameFetcher.prototype.prefetch = function(character) {
    +};
    +
    +
    +/** @override */
    +goog.i18n.uChar.LocalNameFetcher.prototype.getName = function(character,
    +    callback) {
    +  var localName = goog.i18n.uChar.toName(character);
    +  if (!localName) {
    +    goog.i18n.uChar.LocalNameFetcher.logger_.
    +        warning('No local name defined for character ' + character);
    +  }
    +  callback(localName);
    +};
    +
    +
    +/** @override */
    +goog.i18n.uChar.LocalNameFetcher.prototype.isNameAvailable = function(
    +    character) {
    +  return !!goog.i18n.uChar.toName(character);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.html b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.html
    new file mode 100644
    index 0000000000..e6c0237d3a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.uChar.LocalNameFetcher
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.uChar.LocalNameFetcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.js
    new file mode 100644
    index 0000000000..4f99c3f942
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/localnamefetcher_test.js
    @@ -0,0 +1,59 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.i18n.uChar.LocalNameFetcherTest');
    +goog.setTestOnly('goog.i18n.uChar.LocalNameFetcherTest');
    +
    +goog.require('goog.i18n.uChar.LocalNameFetcher');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var nameFetcher = null;
    +
    +function setUp() {
    +  nameFetcher = new goog.i18n.uChar.LocalNameFetcher();
    +}
    +
    +function testGetName_exists() {
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertEquals('Space', name);
    +  });
    +  nameFetcher.getName(' ', callback);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testGetName_variationSelector() {
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertEquals('Variation Selector - 1', name);
    +  });
    +  nameFetcher.getName('\ufe00', callback);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testGetName_missing() {
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertNull(name);
    +  });
    +  nameFetcher.getName('P', callback);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testIsNameAvailable_withAvailableName() {
    +  assertTrue(nameFetcher.isNameAvailable(' '));
    +}
    +
    +function testIsNameAvailable_withoutAvailableName() {
    +  assertFalse(nameFetcher.isNameAvailable('a'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/namefetcher.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar/namefetcher.js
    new file mode 100644
    index 0000000000..9cf9f2145f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/namefetcher.js
    @@ -0,0 +1,70 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.i18n.CharNameFetcher interface. This
    + * interface is used to retrieve individual character names.
    + */
    +
    +goog.provide('goog.i18n.uChar.NameFetcher');
    +
    +
    +
    +/**
    + * NameFetcher interface. Implementations of this interface are used to retrieve
    + * Unicode character names.
    + *
    + * @interface
    + */
    +goog.i18n.uChar.NameFetcher = function() {
    +};
    +
    +
    +/**
    + * Retrieves the names of a given set of characters and stores them in a cache
    + * for fast retrieval. Offline implementations can simply provide an empty
    + * implementation.
    + *
    + * @param {string} characters The list of characters in base 88 to fetch. These
    + *     lists are stored by category and subcategory in the
    + *     goog.i18n.charpickerdata class.
    + */
    +goog.i18n.uChar.NameFetcher.prototype.prefetch = function(characters) {
    +};
    +
    +
    +/**
    + * Retrieves the name of a particular character.
    + *
    + * @param {string} character The character to retrieve.
    + * @param {function(?string)} callback The callback function called when the
    + *     name retrieval is complete, contains a single string parameter with the
    + *     codepoint name, this parameter will be null if the character name is not
    + *     defined.
    + */
    +goog.i18n.uChar.NameFetcher.prototype.getName = function(character, callback) {
    +};
    +
    +
    +/**
    + * Tests whether the name of a given character is available to be retrieved by
    + * the getName() function.
    + *
    + * @param {string} character The character to test.
    + * @return {boolean} True if the fetcher can retrieve or has a name available
    + *     for the given character.
    + */
    +goog.i18n.uChar.NameFetcher.prototype.isNameAvailable = function(character) {
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher.js
    new file mode 100644
    index 0000000000..bde93932ae
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher.js
    @@ -0,0 +1,282 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Object which fetches Unicode codepoint names from a remote data
    + * source. This data source should accept two parameters:
    + * <ol>
    + * <li>c - the list of codepoints in hexadecimal format
    + * <li>p - the name property
    + * </ol>
    + * and return a JSON object representation of the result.
    + * For example, calling this data source with the following URL:
    + * http://datasource?c=50,ff,102bd&p=name
    + * Should return a JSON object which looks like this:
    + * <pre>
    + * {"50":{"name":"LATIN CAPITAL LETTER P"},
    + * "ff":{"name":"LATIN SMALL LETTER Y WITH DIAERESIS"},
    + * "102bd":{"name":"CARIAN LETTER K2"}}
    + * </pre>.
    + */
    +
    +goog.provide('goog.i18n.uChar.RemoteNameFetcher');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Uri');
    +goog.require('goog.i18n.uChar');
    +goog.require('goog.i18n.uChar.NameFetcher');
    +goog.require('goog.log');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.structs.Map');
    +
    +
    +
    +/**
    + * Builds the RemoteNameFetcher object. This object retrieves codepoint names
    + * from a remote data source.
    + *
    + * @param {string} dataSourceUri URI to the data source.
    + * @constructor
    + * @implements {goog.i18n.uChar.NameFetcher}
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.i18n.uChar.RemoteNameFetcher = function(dataSourceUri) {
    +  goog.i18n.uChar.RemoteNameFetcher.base(this, 'constructor');
    +
    +  /**
    +   * XHRIo object for prefetch() asynchronous calls.
    +   *
    +   * @type {!goog.net.XhrIo}
    +   * @private
    +   */
    +  this.prefetchXhrIo_ = new goog.net.XhrIo();
    +
    +  /**
    +   * XHRIo object for getName() asynchronous calls.
    +   *
    +   * @type {!goog.net.XhrIo}
    +   * @private
    +   */
    +  this.getNameXhrIo_ = new goog.net.XhrIo();
    +
    +  /**
    +   * URI to the data.
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.dataSourceUri_ = dataSourceUri;
    +
    +  /**
    +   * A cache of all the collected names from the server.
    +   *
    +   * @type {!goog.structs.Map}
    +   * @private
    +   */
    +  this.charNames_ = new goog.structs.Map();
    +};
    +goog.inherits(goog.i18n.uChar.RemoteNameFetcher, goog.Disposable);
    +
    +
    +/**
    + * Key to the listener on XHR for prefetch(). Used to clear previous listeners.
    + *
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchLastListenerKey_;
    +
    +
    +/**
    + * Key to the listener on XHR for getName(). Used to clear previous listeners.
    + *
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.getNameLastListenerKey_;
    +
    +
    +/**
    + * A reference to the RemoteNameFetcher logger.
    + *
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.logger_ =
    +    goog.log.getLogger('goog.i18n.uChar.RemoteNameFetcher');
    +
    +
    +
    +
    +/** @override */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.disposeInternal = function() {
    +  goog.i18n.uChar.RemoteNameFetcher.base(this, 'disposeInternal');
    +  this.prefetchXhrIo_.dispose();
    +  this.getNameXhrIo_.dispose();
    +};
    +
    +
    +/** @override */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.prefetch = function(characters) {
    +  // Abort the current request if there is one
    +  if (this.prefetchXhrIo_.isActive()) {
    +    goog.i18n.uChar.RemoteNameFetcher.logger_.
    +        info('Aborted previous prefetch() call for new incoming request');
    +    this.prefetchXhrIo_.abort();
    +  }
    +  if (this.prefetchLastListenerKey_) {
    +    goog.events.unlistenByKey(this.prefetchLastListenerKey_);
    +  }
    +
    +  // Set up new listener
    +  var preFetchCallback = goog.bind(this.prefetchCallback_, this);
    +  this.prefetchLastListenerKey_ = goog.events.listenOnce(this.prefetchXhrIo_,
    +      goog.net.EventType.COMPLETE, preFetchCallback);
    +
    +  this.fetch_(goog.i18n.uChar.RemoteNameFetcher.RequestType_.BASE_88,
    +      characters, this.prefetchXhrIo_);
    +};
    +
    +
    +/**
    + * Callback on completion of the prefetch operation.
    + *
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchCallback_ = function() {
    +  this.processResponse_(this.prefetchXhrIo_);
    +};
    +
    +
    +/** @override */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.getName = function(character,
    +    callback) {
    +  var codepoint = goog.i18n.uChar.toCharCode(character).toString(16);
    +
    +  if (this.charNames_.containsKey(codepoint)) {
    +    var name = /** @type {string} */ (this.charNames_.get(codepoint));
    +    callback(name);
    +    return;
    +  }
    +
    +  // Abort the current request if there is one
    +  if (this.getNameXhrIo_.isActive()) {
    +    goog.i18n.uChar.RemoteNameFetcher.logger_.
    +        info('Aborted previous getName() call for new incoming request');
    +    this.getNameXhrIo_.abort();
    +  }
    +  if (this.getNameLastListenerKey_) {
    +    goog.events.unlistenByKey(this.getNameLastListenerKey_);
    +  }
    +
    +  // Set up new listener
    +  var getNameCallback = goog.bind(this.getNameCallback_, this, codepoint,
    +      callback);
    +  this.getNameLastListenerKey_ = goog.events.listenOnce(this.getNameXhrIo_,
    +      goog.net.EventType.COMPLETE, getNameCallback);
    +
    +  this.fetch_(goog.i18n.uChar.RemoteNameFetcher.RequestType_.CODEPOINT,
    +      codepoint, this.getNameXhrIo_);
    +};
    +
    +
    +/**
    + * Callback on completion of the getName operation.
    + *
    + * @param {string} codepoint The codepoint in hexadecimal format.
    + * @param {function(?string)} callback The callback function called when the
    + *     name retrieval is complete, contains a single string parameter with the
    + *     codepoint name, this parameter will be null if the character name is not
    + *     defined.
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.getNameCallback_ = function(
    +    codepoint, callback) {
    +  this.processResponse_(this.getNameXhrIo_);
    +  var name = /** @type {?string} */ (this.charNames_.get(codepoint, null));
    +  callback(name);
    +};
    +
    +
    +/**
    + * Process the response received from the server and store results in the cache.
    + *
    + * @param {!goog.net.XhrIo} xhrIo The XhrIo object used to make the request.
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.processResponse_ = function(xhrIo) {
    +  if (!xhrIo.isSuccess()) {
    +    goog.log.error(goog.i18n.uChar.RemoteNameFetcher.logger_,
    +        'Problem with data source: ' + xhrIo.getLastError());
    +    return;
    +  }
    +  var result = xhrIo.getResponseJson();
    +  for (var codepoint in result) {
    +    if (result[codepoint].hasOwnProperty('name')) {
    +      this.charNames_.set(codepoint, result[codepoint]['name']);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Enum for the different request types.
    + *
    + * @enum {string}
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.RequestType_ = {
    +
    +  /**
    +   * Request type that uses a base 88 string containing a set of codepoints to
    +   * be fetched from the server (see goog.i18n.charpickerdata for more
    +   * information on b88).
    +   */
    +  BASE_88: 'b88',
    +
    +  /**
    +   * Request type that uses a a string of comma separated codepoint values.
    +   */
    +  CODEPOINT: 'c'
    +};
    +
    +
    +/**
    + * Fetches a set of codepoint names from the data source.
    + *
    + * @param {!goog.i18n.uChar.RemoteNameFetcher.RequestType_} requestType The
    + *     request type of the operation. This parameter specifies how the server is
    + *     called to fetch a particular set of codepoints.
    + * @param {string} requestInput The input to the request, this is the value that
    + *     is passed onto the server to complete the request.
    + * @param {!goog.net.XhrIo} xhrIo The XHRIo object to execute the server call.
    + * @private
    + */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.fetch_ = function(requestType,
    +    requestInput, xhrIo) {
    +  var url = new goog.Uri(this.dataSourceUri_);
    +  url.setParameterValue(requestType, requestInput);
    +  url.setParameterValue('p', 'name');
    +  goog.log.info(goog.i18n.uChar.RemoteNameFetcher.logger_, 'Request: ' +
    +      url.toString());
    +  xhrIo.send(url);
    +};
    +
    +
    +/** @override */
    +goog.i18n.uChar.RemoteNameFetcher.prototype.isNameAvailable = function(
    +    character) {
    +  return true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.html b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.html
    new file mode 100644
    index 0000000000..dd6aa9a209
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.uChar.RemoteNameFetcher
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.uChar.RemoteNameFetcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.js
    new file mode 100644
    index 0000000000..45d11836a0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar/remotenamefetcher_test.js
    @@ -0,0 +1,108 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.i18n.uChar.RemoteNameFetcherTest');
    +goog.setTestOnly('goog.i18n.uChar.RemoteNameFetcherTest');
    +
    +goog.require('goog.i18n.uChar.RemoteNameFetcher');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIo');
    +goog.require('goog.testing.recordFunction');
    +
    +var nameFetcher = null;
    +
    +function setUp() {
    +  goog.net.XhrIo = goog.testing.net.XhrIo;
    +  nameFetcher = new goog.i18n.uChar.RemoteNameFetcher('http://www.example.com');
    +}
    +
    +function tearDown() {
    +  nameFetcher.dispose();
    +}
    +
    +function testGetName_remote() {
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertEquals('Latin Capital Letter P', name);
    +    assertTrue(nameFetcher.charNames_.containsKey('50'));
    +  });
    +  nameFetcher.getName('P', callback);
    +  var responseJsonText = '{"50":{"name":"Latin Capital Letter P"}}';
    +  nameFetcher.getNameXhrIo_.simulateResponse(200, responseJsonText);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testGetName_existing() {
    +  nameFetcher.charNames_.set('1049d', 'OSYMANYA LETTER OO');
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertEquals('OSYMANYA LETTER OO', name);
    +  });
    +  nameFetcher.getName('\uD801\uDC9D', callback);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testGetName_fail() {
    +  var callback = goog.testing.recordFunction(function(name) {
    +    assertNull(name);
    +  });
    +  nameFetcher.getName('\uD801\uDC9D', callback);
    +  assertEquals('http://www.example.com?c=1049d&p=name',
    +      nameFetcher.getNameXhrIo_.getLastUri().toString());
    +  nameFetcher.getNameXhrIo_.simulateResponse(400);
    +  assertEquals(1, callback.getCallCount());
    +}
    +
    +function testGetName_abort() {
    +  var callback1 = goog.testing.recordFunction(function(name) {
    +    assertNull(name);
    +  });
    +  nameFetcher.getName('I', callback1);
    +  var callback2 = goog.testing.recordFunction(function(name) {
    +    assertEquals(name, 'LATIN SMALL LETTER Y');
    +  });
    +  nameFetcher.getName('ÿ', callback2);
    +  assertEquals('http://www.example.com?c=ff&p=name',
    +      nameFetcher.getNameXhrIo_.getLastUri().toString());
    +  var responseJsonText = '{"ff":{"name":"LATIN SMALL LETTER Y"}}';
    +  nameFetcher.getNameXhrIo_.simulateResponse(200, responseJsonText);
    +  assertEquals(1, callback1.getCallCount());
    +  assertEquals(1, callback2.getCallCount());
    +}
    +
    +function testPrefetch() {
    +  nameFetcher.prefetch('ÿI\uD801\uDC9D');
    +  assertEquals('http://www.example.com?b88=%C3%BFI%F0%90%92%9D&p=name',
    +      nameFetcher.prefetchXhrIo_.getLastUri().toString());
    +
    +  var responseJsonText = '{"ff":{"name":"LATIN SMALL LETTER Y"},"49":{' +
    +      '"name":"LATIN CAPITAL LETTER I"}, "1049d":{"name":"OSMYANA OO"}}';
    +  nameFetcher.prefetchXhrIo_.simulateResponse(200, responseJsonText);
    +
    +  assertEquals(3, nameFetcher.charNames_.getCount());
    +  assertEquals('LATIN SMALL LETTER Y', nameFetcher.charNames_.get('ff'));
    +  assertEquals('LATIN CAPITAL LETTER I', nameFetcher.charNames_.get('49'));
    +  assertEquals('OSMYANA OO', nameFetcher.charNames_.get('1049d'));
    +}
    +
    +function testPrefetch_abort() {
    +  nameFetcher.prefetch('I\uD801\uDC9D');
    +  nameFetcher.prefetch('ÿ');
    +  assertEquals('http://www.example.com?b88=%C3%BF&p=name',
    +      nameFetcher.prefetchXhrIo_.getLastUri().toString());
    +}
    +
    +function testIsNameAvailable() {
    +  assertTrue(nameFetcher.isNameAvailable('a'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.html b/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.html
    new file mode 100644
    index 0000000000..8d490c796f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.i18n.uChar
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.i18n.uCharTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.js b/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.js
    new file mode 100644
    index 0000000000..3852be3a0f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/i18n/uchar_test.js
    @@ -0,0 +1,125 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.i18n.uCharTest');
    +goog.setTestOnly('goog.i18n.uCharTest');
    +
    +goog.require('goog.i18n.uChar');
    +goog.require('goog.testing.jsunit');
    +
    +function testToHexString() {
    +  var result = goog.i18n.uChar.toHexString('\uD869\uDED6');
    +  assertEquals('U+2A6D6', result);
    +}
    +
    +function testPadString() {
    +  var result = goog.i18n.uChar.padString_('abc', 4, '0');
    +  assertEquals('0abc', result);
    +}
    +
    +function testToCharCode() {
    +  var result = goog.i18n.uChar.toCharCode('\uD869\uDED6');
    +  assertEquals(0x2A6D6, result);
    +}
    +
    +function testcodePointAt() {
    +  // Basic cases.
    +  assertEquals(0x006C, goog.i18n.uChar.getCodePointAround('Hello!', 2));
    +  assertEquals(0x2708 /* Airplane symbol (non-ASCII) */,
    +      goog.i18n.uChar.getCodePointAround('Hello\u2708', 5));
    +
    +  // Supplementary characters.
    +  assertEquals(0x2A6D6, goog.i18n.uChar.getCodePointAround('\uD869\uDED6', 0));
    +  assertEquals(-0x2A6D6, goog.i18n.uChar.getCodePointAround('\uD869\uDED6', 1));
    +  assertEquals(0xD869, goog.i18n.uChar.getCodePointAround('\uD869' + 'w', 0));
    +  assertEquals(0xDED6, goog.i18n.uChar.getCodePointAround('\uD869' + 'w' +
    +      '\uDED6', 2));
    +}
    +
    +function testBuildSupplementaryCodePoint() {
    +  var result = goog.i18n.uChar.buildSupplementaryCodePoint(0xD869, 0xDED6);
    +  assertEquals(0x2A6D6, result);
    +  assertNull(goog.i18n.uChar.buildSupplementaryCodePoint(0xDED6, 0xD869));
    +  assertNull(goog.i18n.uChar.buildSupplementaryCodePoint(0xD869, 0xAC00));
    +}
    +
    +function testCharCount() {
    +  assertEquals(2, goog.i18n.uChar.charCount(0x2A6D6));
    +  assertEquals(1, goog.i18n.uChar.charCount(0xAC00));
    +}
    +
    +function testIsSupplementaryCodePoint() {
    +  assertTrue(goog.i18n.uChar.isSupplementaryCodePoint(0x2A6D6));
    +  assertFalse(goog.i18n.uChar.isSupplementaryCodePoint(0xAC00));
    +}
    +
    +function testIsLeadSurrogateCodepoint() {
    +  assertTrue(goog.i18n.uChar.isLeadSurrogateCodePoint(0xD869));
    +  assertFalse(goog.i18n.uChar.isLeadSurrogateCodePoint(0xDED6));
    +  assertFalse(goog.i18n.uChar.isLeadSurrogateCodePoint(0xAC00));
    +}
    +
    +function testIsTrailSurrogateCodePoint() {
    +  assertTrue(goog.i18n.uChar.isTrailSurrogateCodePoint(0xDED6));
    +  assertFalse(goog.i18n.uChar.isTrailSurrogateCodePoint(0xD869));
    +  assertFalse(goog.i18n.uChar.isTrailSurrogateCodePoint(0xAC00));
    +}
    +
    +function testFromCharCode() {
    +  var result = goog.i18n.uChar.fromCharCode(0x2A6D6);
    +  assertEquals('\uD869\uDED6', result);
    +}
    +
    +function testFromCharCode_invalidValues() {
    +  var result = goog.i18n.uChar.fromCharCode(-1);
    +  assertEquals(null, result);
    +
    +  result = goog.i18n.uChar.fromCharCode(
    +      goog.i18n.uChar.CODE_POINT_MAX_VALUE_ + 1);
    +  assertEquals(null, result);
    +
    +  result = goog.i18n.uChar.fromCharCode(null);
    +  assertEquals(null, result);
    +
    +  result = goog.i18n.uChar.fromCharCode(undefined);
    +  assertEquals(null, result);
    +
    +  result = goog.i18n.uChar.fromCharCode(NaN);
    +  assertEquals(null, result);
    +}
    +
    +function testToName() {
    +  var result = goog.i18n.uChar.toName(' ');
    +  assertEquals('Space', result);
    +}
    +
    +function testToNameForNumberKey() {
    +  var result = goog.i18n.uChar.toName('\u2028');
    +  assertEquals('Line Separator', result);
    +}
    +
    +function testToNameForVariationSelector() {
    +  var result = goog.i18n.uChar.toName('\ufe00');
    +  assertEquals('Variation Selector - 1', result);
    +}
    +
    +function testToNameForVariationSelectorSupp() {
    +  var result = goog.i18n.uChar.toName('\uDB40\uDD00');
    +  assertEquals('Variation Selector - 17', result);
    +}
    +
    +function testToNameForNull() {
    +  var result = goog.i18n.uChar.toName('a');
    +  assertNull(result);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/images/blank.gif b/mapbox-gl/closure-library/closure/goog/images/blank.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..75b945d2553848b8b6f41fe5e24599c0687b8472
    GIT binary patch
    literal 49
    zcmZ?wbhEHbWMp7unE0RJ|Ns9C3=9Vj8~~DvKUo+V7?>DzfNY>Fh|Ltj$Y2csQN9XW
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/bubble_close.jpg b/mapbox-gl/closure-library/closure/goog/images/bubble_close.jpg
    new file mode 100644
    index 0000000000000000000000000000000000000000..486ef47e5f822012991add1a68f2c31680c9f0fe
    GIT binary patch
    literal 586
    zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5
    z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!NAMF$IK|mz$D1XEXer(2tzsp
    z12ZGgkqE%T%Ekq@3?T$$F)}d=Ffg(*0#(D6voJBUvavG?35#)v1C3;2WM)O^0a~KS
    zWEd#axNzgai~nyi@Bpo05@Z%+uxGfQeK_r=pLb8jwLRTxn=dTgyec5<Xwl4|XVXuY
    ztW7aKIImZ!q%nD2(@EX!(iVD!GX;NNl|3Gx%D-^kd7VuPLfkJ^)b9H*XxT+p{d4|#
    zRe00)lhXQUj~}_#u)=np`Lv8{^JmQ7S9P*^_LL_w@q2~km=7w2D(`>w;_&Y~%qd3G
    zHwu2sR9CuhV0Lmzy6e4(ore~iYaL&iG4Z9^ju{+%z7rYcp4qE=us!)8lN;pI;3@R>
    z`1hm7!^ADtotb*mKk=K2>O8+itJ=zz1{I|hiT!7An6t&qZSj)HbEoINE&Foco%Lr=
    pjYr3yr)HsF@9h=28M;;Ia<A?>zL))`3w}!5Ogs4V`X2xPHv!znqQU?G
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/bubble_left.gif b/mapbox-gl/closure-library/closure/goog/images/bubble_left.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..0e890bfcaa8970ac5d9feec80f8bd84b2ec36130
    GIT binary patch
    literal 85
    zcmZ?wbhEHbWMR-@Sj51vdiCn>-@iY6`0)S#|4*Jg0g?=0p!k!8k)44Rs0au^Y8jZN
    iBv=;9JUVK&aF59CGuziM5G#AEK2IfU*_&Nz4Aub6Jt30-
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/bubble_right.gif b/mapbox-gl/closure-library/closure/goog/images/bubble_right.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..5b42ea098e061cc0eed9542c1bc3aef3272d9d11
    GIT binary patch
    literal 86
    zcmZ?wbhEHbWMR-@Sj51vdiCljPo6w{`0zg%Fu;J~PZmaY29P2k08`A;76MDwENbLX
    aoojZz_vhaz^`<zFzO<-y8r4ml4AuZTc^!-Z
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/button-bg.gif b/mapbox-gl/closure-library/closure/goog/images/button-bg.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..cd2d728b23298ca00ee1aa64107aa3a637fab32f
    GIT binary patch
    literal 454
    zcmV;%0XhChNk%w1VFv*80o6YM`uqF%`1#)6-tFz}>gww9@$$XAz0S_g%F4>WzrXbK
    z^w-zd#KgqG!NK6*;L*|1<mBYLySvoX)c^ng=;-MF{{Gt8+2iEn@9*!($H(vQ@W;o;
    z;NRf<{QcY8+vMZq_V@PL+1lsm==S#a=H=z)=H}tz;rI6U_xJYx{r=qC+verw+uYpW
    z;Naii-{Ruo+1c6L+uPdO+2!Tt<>uzd$j95<+vDTp=H}(_@9*K^;o{-p;^E@`{rvs>
    z{r~^}A^8LW002J#EC2ui00#i{0RRT$z=?1;NmVLFWT)3#`Z|E96cDId10WP=v^^=l
    zU`1g}E?_V@4Mw9GxZeYUd`_U)MHD<~hAXXRG#7#&5H}wtI*B|lI~*+>E)E<V0+lY7
    zKbe}FoSmMZprN9pq@|{(sHv)}tgWuEu(7hUw6(UkxVgH!yuH4^z`?@9#Kp$P$jQpf
    z%+1cv(9zP<)YaD4*xB0K+}+;a;Njxq<mKk)!V&80?Cb{b@CFI<^a&03_zevE{0td2
    w8#ZJRh@hZB1P2l-Jn*2Q!v_)~N|dmmqQwOkGRg=6BZLPJEMlneU?T_sJHI0$`2YX_
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/check-outline.gif b/mapbox-gl/closure-library/closure/goog/images/check-outline.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..392ffcc4f4bd99b2bf2ee05c8da8579868d17af0
    GIT binary patch
    literal 69
    zcmZ?wbhEHb<YM4rn8?KN|Nnmm1}LfclZBCifr&u}C<2t_0<y)X^k)icF*-O0tP4x;
    QD|Vgj5c^_^6C;B)0CU(9K>z>%
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/check-sprite.gif b/mapbox-gl/closure-library/closure/goog/images/check-sprite.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..44a927c49b3a10af304b80856be2f6caf2edf7cc
    GIT binary patch
    literal 75
    zcmZ?wbhEHb6kuRySjfl#1T$ek@h1x-7XuT64oDOv&%h)zg@3sa_s)ohM+eiI>ZU$l
    TW;*SAD@WxFuB{1fj11NQ@sAdB
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/check.gif b/mapbox-gl/closure-library/closure/goog/images/check.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..1ccde2667ddce5a21f1d5555d25ec6d32672ea42
    GIT binary patch
    literal 53
    zcmZ?wbhEHbWM^P!XkcUjg8%;+6o0ZXaxpM6=z#bj83rbv9{%(LC;lGF?p6wAV6X-N
    D799(j
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/close_box.gif b/mapbox-gl/closure-library/closure/goog/images/close_box.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..d829321aaf1ae9b67f147c7783dbb20bf5c210d5
    GIT binary patch
    literal 65
    zcmZ?wbhEHb<YwSzXkcJCbLPzd|Nj+#vM{nUFf!;c00Bsbfk~pJU+nanU2+`lr#dz?
    Si(QXv6)rdRZDL|&um%8e^Ak@1
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/color-swatch-tick.gif b/mapbox-gl/closure-library/closure/goog/images/color-swatch-tick.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..392ffcc4f4bd99b2bf2ee05c8da8579868d17af0
    GIT binary patch
    literal 69
    zcmZ?wbhEHb<YM4rn8?KN|Nnmm1}LfclZBCifr&u}C<2t_0<y)X^k)icF*-O0tP4x;
    QD|Vgj5c^_^6C;B)0CU(9K>z>%
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dialog_close_box.gif b/mapbox-gl/closure-library/closure/goog/images/dialog_close_box.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..d9b1a658cbb004ef217ef6915d6f2e8e9ad5a094
    GIT binary patch
    literal 86
    zcmZ?wbhEHb<Y(Y#n8?6TK6Cf~|Nkpy?q*<MQ2fclz|QcWK?leN%JMTXFsZlntvvX)
    qc3#RvJH|qX$m_C&Q_Q*^f1Mlqt1#{BA(hOPlPBL>a7dYz!5RSDjUZP5
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dimension-highlighted.png b/mapbox-gl/closure-library/closure/goog/images/dimension-highlighted.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..e03cc5a5ac9153f50b5d5e27df5767f087047b21
    GIT binary patch
    literal 171
    zcmeAS@N?(olHy`uVBq!ia0vp^LLkh<3?#J|q#XfLoB=)|t_%zvcVGQJeDlAz#@2m6
    zLB^6GzhDN3XE)M7oFs2|7lsa2Sr3r%0*}aI1_o|n5N2eUHAey{$X?><>&kwEQI=7g
    z*;z2;3s6Yf)5S4_<9c!e1M}L@)nSGPE85!H0{dHLbh0pby%RRtroKBLsEonW)z4*}
    HQ$iB}Q?4tq
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dimension-unhighlighted.png b/mapbox-gl/closure-library/closure/goog/images/dimension-unhighlighted.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..7fdc0c0bb8a7f0fca9a4d3449fd4328855c9f8b7
    GIT binary patch
    literal 171
    zcmeAS@N?(olHy`uVBq!ia0vp^LLkh<3?#J|q#XfLoB=)|t_%zvKYsjp`SK;#6t2ZU
    zLB^6GzhDN3XE)M7oFs2|7lsa2Sr3r%0*}aI1_o|n5N2eUHAey{$X?><>&kwEQI=6l
    za(Tv_^FSeKPZ!4!j_b(@49sgoSBDuItY~X%3+!*1(aFN#^-kDmoBHm2pfUzeS3j3^
    HP6<r_VdX4O
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dropdn.gif b/mapbox-gl/closure-library/closure/goog/images/dropdn.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..9aeef817d0fcf5192da4aace3f6019f35e86e3ce
    GIT binary patch
    literal 51
    ycmZ?wbhEHb<YZuFn8*MEsi~>|{`~_obU-|iI0F+8PoF^2&O;ZP3xmqI8LR;yZVVCt
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dropdn_disabled.gif b/mapbox-gl/closure-library/closure/goog/images/dropdn_disabled.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..8db1c2ff4ca7ecf8e039583cf790178ec0fa6f5f
    GIT binary patch
    literal 51
    zcmZ?wbhEHb<YZuFn8?6TUthm*<HrC0|1&T!=m0STNSuL*ho?^<Y3HE}&4oc_+zi$L
    Db|emV
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/dropdown.gif b/mapbox-gl/closure-library/closure/goog/images/dropdown.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..edcd0b9a6213ffa62a5f50f81955a26b9e136b1c
    GIT binary patch
    literal 78
    zcmZ?wbhEHb6k=dySi}GVsi~>;_4S)KZ~p)PKUe@n=zs{28U|)*i48o4N?wK?S&ae~
    WtM~OSE1dG9^;y_5G3!~J4AuY<J{A%H
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/gears_bluedot.gif b/mapbox-gl/closure-library/closure/goog/images/gears_bluedot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..091af46b728a54b63967b51a00288ad7ebf506a0
    GIT binary patch
    literal 236
    zcmZ?wbhEHblwgoxIKse?z4FPV1Fz3L`c}C1>5|j$8n?Z;{p!coD<5-KJ*nROyl>yD
    zxyRmKefE9L`47)N{@Qcn^ZmC!ryhDeW$&~9|Nk=(0Th3-FtRfUGw6WSg6w2q&3vHJ
    zH(5Z{flH;^ao^&`RVAx-Y`Ae{&#foj^8_3!-nJc@-(l#$r?{x8c!h@V#I^&aJ2LXB
    zFRice(No_0tm&Fh$_B=D20N_`7aZ-o*jQ7+y5CEmpCi21QdpqKmNB5+keicZ5;G@D
    Juf8IKH2?&EVRrxk
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/gears_online.gif b/mapbox-gl/closure-library/closure/goog/images/gears_online.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..1a40b086d358e9b55682e68fdf72397426f8518b
    GIT binary patch
    literal 137
    zcmZ?wbhEHb<YwSz*v!BXcuX_snAXwPJ5Rmc=Y3S^>+jFO$8<uE>wo|A^~0~XC*ST3
    zI;Q>q|NqeAdJJHo_>%=p>wrj*84N6D6P6av%GEO5GVSfXCXKkmbLI<8QUA=y-fh+`
    jxV_@;Op9CrBXM4Z8#C>=T@O~gRPgKSRz2Loz+epks5dt>
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/gears_paused.gif b/mapbox-gl/closure-library/closure/goog/images/gears_paused.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..135da131bbf8ee46e3873ba55aae63bf080bd26f
    GIT binary patch
    literal 93
    zcmZ?wbhEHb<YwSzSj52a`Sa&LfByXa`}fP2FaQ7lH!%2r{rYtV1_s5SEI^WhK?g*D
    u)G{zDc^F=^ZQ*dAov<#zp?iI=dRP)?--1TFo8Ns_Dr(FyW^rL+um%81aU=Nv
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/gears_syncing.gif b/mapbox-gl/closure-library/closure/goog/images/gears_syncing.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..18c3e94d0ad723410a6d1714b2e5316355d67e7c
    GIT binary patch
    literal 761
    zcmZ?wbhEHb<YwSzxN6N1cuaHUlh(NpOU`}RdGqtBg3Bg5Ud}rDdS}ovty6FJef|B}
    z`>4{>@3&HqYqi}9&cAGQ{=@EP-|sAZRM~MmZ2QX@g_q4H-c7mv<#hTf?X54ScixW3
    zxuie;QF-1a!-H>@S6y?iz3!HBRBOwNsfXUIT=leV>b=avZ&z1ecbR-IHTjU{nx|bo
    zccLq<*%x22oN_O{^=44U8SM|h-hTh{HTalL=yCl|zu)}-_y6SEy`jhT{{R0UbW9sa
    zG9UrP|J;7AA;Hd$0j@@R2F#2=DaD^GtPBj+3_2iXAa63T1s#Za)21z!zjKby@hKTI
    ze2(^JZn(N&7qdh9jsU4$x7@bRc$p)bap17Y?F~(heQA9KCA|WQ2KSrPcuh@Nd^m!A
    z8f$#ay#ov+0*sto+)OwPEgccoFflMPurqK1t<iFryFp2lBXrvm2lbVreoOlFj0Cxu
    z%@%(+yn%~dN%p8&(v_CJ#siOX*%{Hba{#rgIm|84Fu9Slb=J88%@=*=xdPP{t(bNl
    za`xZ|$q{F4p73-6n^>X%LMtN!69Wg(ZlKl!cl}hiu(r&a^<m@ZHzihy9Sco-TKwl8
    zTPDI5WbMPN^u~$xMl70EPOw(Z19#6Utg&pdp6k+aE4M}JNuNW4WR6Hm+k~Ei5+~UT
    z_Cs8*m)zWTaWkP>4AHF(^m58())ws!#~&*=TefxtJeT0eo&55^D&yW|Ew{YeS31s{
    zeI-QwM9DIwAOkrZqF>7)cJ-p>D>A#D9G&KJ)67#^WhHmTqARZyBp<5CZ*vezS!`ov
    z#<X>!V^A}~dPs<af(#hq>r`~4a`a+>A?{exJ4?Yx(~4u8)5l=r-j->N9KqJLIa?RK
    LJ1D@$$Y2cslNTm4
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.gif b/mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..8eb9aa4a23633be79279bc3b0788965d22ff6c82
    GIT binary patch
    literal 11851
    zcmWkvby(947o96c3@`|l5(Pm4K{_2R($YN|B&0h=4Wt#6R+N^MZbvtc?ht7ZCP>Qm
    z``+)|=l*-1bMJZ1y~-*|V&ax>Kyjcm5C{f?k*KfJAxXg2O&a1&;QA)*`c*CICQwh;
    zjJV3r&nN5?DS<kGD)ZJyV0ak#uQxY0lZc2~pwrIIE{qxgVyS_W`mg1rj8zgbZ|*lx
    za)X2^CuULv5{Wl`NELBF!uuFfb?`Ka==U=r6Q~Ubl$4ZA0J;cbBI$Qj8Oa+kL|WY-
    zh6Vv7{{W!g9&pOA2kMa|OM^wA0ey9<PckVY4i67U0uGd#SAv3qzyW%H8~_-!hkzYx
    zAhMREZQ)G<_Rat^uP9xjJdufnF$7eyr%0WoMkO^;b%O#hTS}7JzOn=C0LisH;P0Ot
    zAU-}`_6CV$@{%}H9YcC9|An*<ECFJ-i4$l4h;s8l&J4gVM~XW40|=z9UDAoHFENO8
    z#6(gUKO@GI=FKz!cG5gBmk5Xy197b+Ij<QkX_ke^#Kd&MKw<z6V<mw9K#UP5PV52W
    zOijSj_#TkZK^!M}Gx1%KicT7O0Y#!&!8xEyI+oQSHa0e5iBADTY1S^lhA3YP2m(yJ
    z!$1aLHw-K~oB<Kbz$wXS@f9#L4E(?5Ew6yP&A@O5AgOlopYo&9J0xBapoP@aY=;9#
    z`3Ez=Qg1ma6G*9B0%nQoIUxY)VCIQ05O@Z(@Boj1IAZX*GD()WphP<1!;!LphpP$S
    zfTJ^F>;vEpsqeuSaZ-*5bR+{juX*!`Kzzyk{Jbb|bWXbT@$n(4wf~hMriKt_x=5s@
    zX;O1TZ7Q%b0~lP8NFRMv|1S~<-UVt%Q$Q@IogkpEug^zMs;>ZmgoQfb=&Jh3Q7u3^
    zzf1+<rhys2uAWpo2MpVNUHs3X;n$ym4w9#=5RvqI^S}A+fQ)0n(jtL4YzM>wESUx*
    zk}(Gtz;KnJPnzi^J@|wM_(*@dfQPp@0PAd^@(g%Kln@yuGVo{vEUy7NVj3r@4u}ts
    zYrm>v0ZxFMM7`sK1X523=|6++T|lpqfdCK>=s)~F6CeZtf*`!M5~E}z=x?wa#-K4>
    z*Z#4OX8EmRBI9moIVglx+z(4)6SU2g^?;L6pY9774xkcM#o<<W_-*>b)M>=L{qUb7
    zXjreIA|&$UY8B%slpjCP%4Czx7Ij~{mHwgx6;X<maX9IiQf3bWmyF$3_2Dt}H6E|7
    zsuKAe73F(JBEB(cO^Tde&~DP98zGU*s%ZB}=O|yAnrEXn==MbNH8SDXo9lDO_FC6Q
    zu`dNOy&QFlH1b~ZqTY;8(HJ#&sQ=YN&&@!{Sxgt`I~K4WI~v&r*YnI1wy#JqQ@aP2
    zDO`G-=FXta;eKi<g_VJcBHPajq7Tb)e?4`_bX7$G{_+#~83h>}oc7<mGsyRv;Vr4{
    zQ=i4?X6{o`LMKA`KIS^x%iDeT(#AcWh@UZeiD?@wd7~WbZ@*mhpVnpitRg(`XZYUN
    z-051a^0blhus*B2hioeAYk#xIPV->@TjOX!Ci`@c#~ib!)_C<THdYA)$kK;btv~0b
    zTHCcL-@88BKVPv?f1?zx(sHzQel=E@9L)Vfmm!Y#VCs__2B~ykMYOt>(OT+hV@d=V
    z%AFdi#O(O<E;&z$rCvdXPv)!bFv9{y`4~$M9>Z#_FCShniJ)9>>_n%<@l%LqWA4AV
    zVM}z(?@0F)lI6^ZcyGoDdaLYmmZ1}H;Q0PiNd`b>3$o}qLh|VGxeb6@x(m7F>fGO0
    zgwSp1QO6Sp7!BQFg&Dsxb7kyPl1_9DUPWfQ+u6e|*E1fb=)C1z{!~4B2M4waia03W
    zZI7M~;>q@8mxnBVw#rApaB~*Fo51}GGr}2tzgAM_)X+w3O6PZkoR!ZjZEt;ARq&Ee
    z>4V06qaI8DjW1;S&6GCZq8P8w?z+ly3=VZZP3W{eH1!T1FcJ4}>o<rGwUq<aM2Tm?
    zAs?%E42#Fqi{$H2m+SmF*FA^I8_Mk#i&8cMdW=CF%2k-@mE`>j<1NPSr$bare~#K(
    z=vpe%2S}2$?uU!T<I*Uy-|y5uv+sv>DjNWHMs~&QbVC#L7E~V_fAwDeG<c(sT=(mN
    zGU4QlWm0g~5bd|>wQE9qCe+`pjbpjP1Y2uLq`d&=2Bw-?aThO&Csa0F82RkU?r+->
    zML(m@nlvDLZms7{iB4ugP(ne0;_T7Ku4<vvoHl<&p*-A^ZLz#O!LY~-K2b=?f;6RC
    z4PIAAef*D^=J>Ne+JSGs)$;rCG%-}zUvqDKIVDj5`($jOuNo<tsEoSyg~OKK?IWyz
    z^J(C3dCz}@a0ae$#Aq<J7wHIrd(juxSOE3@IibCGWS3&HpYZxG!Ne~6C-Muwe8>$|
    z6w#}%<%$j!x@M$IkU_^Knk8WRJeApjagW3e;X{A=n7Fy!6I{%5S~Z4N7K+)h8a0Qc
    zz{0q@kHp+)Cc`Rqp4?tR$a}|aFS7ec<dXf%zK5X?h!Fgf_eo~AD4*lP>d|G_Jr1w)
    zu+=#$cL?;$FKBVAC*!#P5L=cIEm|bdQN#1b-_C2Ts2VN}fr$BqRmrv9Yr6UCEca3)
    z_zdkaN4MAL-wIgnzbsZdYkJMa>u1f>B-|wt(uWmWw;)W9$ydy>Ystl<_?Sf1EZJYj
    z9L1#FyP46i$XETgj1lyhjk|+_1$158rtj82E9hhJKC|uYI=%FqqL$Ai2P>fsZf33C
    zfb4H9Wwjc1i?iVj<C=+=_$BVtbs(;|kv%kE@+Y;+m?~?3gzy{2Lb-$t%4>sNF{ASN
    zLLYvr)hK#q(xk3Fs5GK>{t&^;+GfLf1+H>)txhTCDiWXIAJiUQE7+9WxV@r6<2Ro7
    z-o@q~ow>u0#f^hoLbdl%+;3HxMRJjw7kM1uVtgUR1oM%b@^zX4wOCH=JNRtQ$Lu>W
    z1zmhz<T;mQ@od^#>gRM>L1d36UnQkx9<$_laPLj}$31PYxh3Ml!P+|3n{$wQ`|Qe5
    zW^eX~*m);i4DZOD)BYhi?Ic;K5SNpO?A)NfPmFOgnI6XrZAY{>oiBb@sFYj9nT4&4
    zZHkjlZ%o~b7Lo0!80D--VW0`r-74IWw>4WbqG+8@rTOcI{nt7sXUCrh#+;PMq@(1G
    z6$(DRiIM}LgI#@>xEpW8iftQ8qxR))y&X{i>3<uH#V7idnh{@#cfN?3eCM5S>sGEZ
    z^;KW<6*x6Brj*P6GiCbbuhgf!Q4oUhMAm_K<tQ2}>KE`sd>c|+%jW!yCTrGQ%L*w`
    z3wy%aG8r)lAzNtMmlqC9ln|?=zE!ZzEaPNe{_aCw3>!4Cui5y1cLUW|t}a$9Nt1>`
    z*_+Miosb-K%n{MIp<&xa`h~krfS+|^cX^RJq-8Nk$*&WuSoSzS(2|+`b2HPZtK4JC
    zHB*dVcQlVXyY{|OS*8m>_hsX&!DBA#+Y^nwBAj?uwMPqcYYp|y2(M6k+4cJ~bro&4
    z9w{_xi?G1^^q3s^gH5;C;P#(`*aXO}8-YLhDj_9Mv)z}1PMa^&Zj))fkCvj)wi2$H
    zt9j7~ai|-N3Xb_wy7Sv1XF*bbdVaQ7T*E2&@aL~UCi#9Mv)5-^<6eOW%>%ET|Ar@I
    zEgEVWv)1BH3y^zpU9R(V^yUz(@FF~X{!7<<&$XDDU^~4}5mOZ!-|P=_p<*jxloRW6
    z53ax6V_eASEz?uv^l1I{EO(8POgW14R>hku7I{3;yOW3X&w__dVxX-cnk~E4VC|9L
    zpeLe9hATK0VbQcu0T4tuIuk4()LM3J)kkvZj<|X+m`-Kd*1g%otV!UE@8m{#wi~(c
    znf#br`Mu$dBTM|{%ec6Tu<+|TrnLD0H(t9)Z@hQpzHK5V`RlH4b#2K=e6q>ETWZCB
    z2U^NKc4F^#J$d3=jFj-qS}<Gs%BL{8rxjyMXdU69muPRxeXY$DJo8&xp)w)aI{ft!
    z<%wd?jRS!bAt_V*4PUwGZ)ikZU@(;hg93N{??12~pZD#Hgl?*7#JM^JOlVc{4?QzG
    z@EBrh`Ow(1<Zh18X9(YtIcpM1rF9acY$qJ^w;GWDJd`J7GJlO}{{?`&BcFu*L_QPg
    z&Pn!<UFlr2#x(-lUCY}^cSYAd(Q=oniNB|7mKd@QqPzm{xjqQGoY+eAU-C{m(>wck
    zh}-hq;Sc&-Ptm9BWsdgpR6-cASx4vk;U5M&)LB~)$Pi~R9Z~Q3X#YiJchj@rPX-j$
    zx$fa?2xd{2t>_SkG@n|0c>8JyfeiL-i{f<y+}$AXgPZG7a^O`oq;xa%J6X^WKc>sz
    zb#EVqX>ufaI>yrnCNPbO%=L?=brVwxp7eE_e&|1E@4wI&oS8<0MugnsLfq#<ETgf@
    z82GYVz;;oHV?#hWSp<R1b+sP;HyY}+>fwTkczR|Tk?R^70|`y{1sEc|bD_~<L2xdo
    z=joBAfR>jz9n^dyl4?8XD}U6DZ7+Jm;7or1IWn8HEod`7%pwMMfq@zF!wuQOt+m2A
    z{II=IaNFpRojxc}qt6m&#J3qrdTcbGa-fS_;6H|#?&mSz?PI+8G2LWd$}z9MX!*tD
    z2BrGJM$a(Uxq|f_!YnXhjEzv8BFZ#hZ*v6Ja@)HE5nxm7ZvWi0x}VAckNrv({p38P
    znKAHjTC_-Ubf8~g2v-d0Jo*DZ5Nt%IoE#thJm|zOGBG{!3qBe9@Qo}EgM6Mq6Z6KQ
    zfMT*C;m%INvL96@!HtFik@q~-GA6dfFR^?()-fG>AV#K0>#^xZv5Ai3oedDdyIcXZ
    z5l4M-{xML#qNL9AXt|4|DB5H_LZBWfC?+OZsh^^BHAZ18IR^ALhX3s?$G2nlQFodW
    za?W8a{#57^N_HcvMP-PE;k#l)KzaH*J1)clSKJeEvQ4cxA)^$rl9U#QXle3P7cJOR
    zQ3U38dbk+k+FYQDQ8IiW74Cqc+({;X{@#H0tpO_G)|-UeP486+Fs6(5X$%Mr)CUa|
    z<bo};F#Q9U3bvx3DjWkpJWo`lrL1Sn6829KBZpnFMYQ@w%QQu|I+DAv(ddEFBW|Zh
    zXr(F}kwvQ{k0(>dHl<Tu#9$v%$nxjtiDyJ>Wu&Jkn3QDX_))2XKHMvz%mqsPzr6Xt
    z^(K*{IMZz>bLU}N_3dcUjFg6^l%}1O(}Apx?X>QuPfA9xfeh+_V(4&2a{NUq)lM?)
    zZRlI^3<G~eDCRArW0+>uTUD*ktmGfr#Xo%Z{~+oXYsgQ%#RYZp$CeFbDiYo)@~3!-
    zXSLIQs=poB5(8~>`_#IVcG>hP%m|_Ep9(Wd9v06n%}v(irY3uoGcNv~VlWqZnR{FO
    zvjqs6S(34Gk%PXCxbxzJ-2ge-55g%<ZM|9$&IT=Q%H)iNKYWxX=olxA4;MjE{@I~y
    zxInb@$DQqXC{#s@#O8%Ll9P;J%36pWHz-IV_eC;NsUHG=kx%WIKLIr5PG>-9&dI1>
    z1Zy-W{9JvXxs${A=tB>t;0t#q^I!oRMF9JqLTlr~hZ1SunhI<A$zA8ah~Q}4FA+`R
    zdEWDI-^`)_2?WUq5gGu+-N}v2M8*w3Oi}rL&BYT<WiKu<nya}fm#~`@1sO)4=_o$-
    zp$mH0KH8~3gmMcma0RB91^M%_4yZy!bm0?6>it2eFgmVczEFg=)a_AG8>&?BRcT8`
    zX|N-CL@o$JUIh%622+&bGN}=zP=2lAlU_tAx~%sy9x+%(K|yUmfZn<&OT8r1x`d@Y
    zf=o9f88gen@FhK~<$1R&eqMh5wOYXb2)bqbWjFKd@5_pH03V*hU&;HZGzni*7Yh|w
    zO;K<wYTEq-p{xRzf`WI8;Bi&_xNPi;vQiwv6bDgwMEwO*hiodN8_G?-gnlJJL)q%b
    zFY_~RQ%_&!u&Y*c1XeFU$}|pud<lT8Q`A_q)$9a(;Wn;0ctN?&R{3p^CW)=_Tq17<
    z{Ye1>RWN}Czo}KGtb#dJg=bb#KCYu4g7khZHo6NP7tdM6*CoG5KtHa(c?s3JL+ub(
    zuUb_<7eLj6uXp`det{$ZO!0;HF_isLjosIZ{lSXE=0ee|n&U^M;gqo2{~mZiqij~A
    z{6f+5y+(@M+K|E8l)YLi)v}n_CWF$tKzx%dp@|*{go(R-yQA7XvHOjs1<Dpk#ZF1?
    z99aGBaf`>}ue_4*#yB{COG`jY!zQ{$n6|R>Zlm-+ME_9SU}<A)OQQm98*r!Xxk;5;
    zT-6StR$H|!9@EtKwb*z8X-Y}W!qc9d3DHC~k5fQVwA837=-0;(_Q%!wU&{&q%Bxjd
    zJQk{dVOknh3+1Z5x0yhQl&vYWIPiW=e;`uuPOSic?T9K=Zx#3BZkryiTiv9MU#m&y
    zU)6k8#}|Ct7hK26zc#iY1bYzTzEme?P;+r<`)(lB0VO&AV#({$f^1ZmsMdGUpzr)y
    z4MKZe!uY;*+HMiH?udn&{uV@ZAa1y{TOHW%*4Y1~jDo_Qs5DG^wD+nQoa@$-2aFbw
    z<2yYp`yJwhI$P6b&Y%(p%4(j)_An56<wD5-ZJ%clM98^I&aLY_aOmMK_-dgK_peV*
    z4IHV8Qw+kXWOu6$_a`v*ynEbn%e0ujtSw_07Km$;#dWMwQvbR;Xe(7;7}#5Um-;EF
    znzwb3<1V5Q)b~`Xug0{mA+FD^xhwFhYn||2{4VYa4S|^Ux49v^%lg&ghpDLsl9-Bj
    zFf=z!d-#))6eEbV_<;-_>ZKO4DJJM+!oY0Vh%FU#>1w2iX>@@w<~lrRgRT}L3}zF`
    zt4+Ha0{i$We>7iFO7HizXV;(s{z`Q6kIvyA0&d72=iw1byw+7e+zEd(2v3oNWC$9d
    z$r)H08enP#uTT!M^K{-H{<cgty1GBM03tUvqbj57<0O`U<7rq#4f&Zu30IWd5)?<K
    z5JB~cZ!f?iRftaKehFl)!s>+fez(qQ_sc7=GHUW&JYIPOpT38u<sAvcb!ZX?K1)sA
    zCPJBVrb=3;oK30jn^A4;m)~cb78*wG50CSjL7PpX{4Ug-Y(E6eCPb1cL_ot2k|%uH
    zCIqyG<^O@z7iY#6W`dCe7$P`KVm7i3AD;_OUqGm~k*O0W>3F9YU3v?yMt+t}nX1Ft
    ze-HNH=XRy~a?Pd}0j(ji)-Jx0iagwqt2%Wk6<lb9292Zn)`lQRFG!g!s9b>?MiwY?
    z7lO@T-<fB3&{Ul{_~$OOkJzZw7ZDR>i&KlUiO9Lama*c;5F_NcDKp}}^!V1|+<f_{
    zC=O{WP0i=l=g`*0w+h|_O$ci(3$M~_Gk2$GO`!41B?QDFa^-4x;@D+DK5IoPaix(6
    zK3`oq2SIxI7a^{TQkcbl<d_N1*isdk&TK5HeB_QxCpT_vf;eV^B%e<BnQ1n@A^~0;
    zA!j-G$-4(WGg&KincvJ^Bdt!8XuEfrf0YyGg;-X6a(+Fbg`5)m&xu>@yz3y1b+F7T
    zgX!uTP_<6pJ{t(y&^}nyWa~6wre^2=$um4<>Ow&uGCGjFba!;gHe}OxAMB93$#1so
    zy1e#;v{sIU3u=J<LVnenEhwN@PNiXuANMY#VafzB>R?6Dd_8p-0U3q#mao$y$KsZd
    zn$#OCrpV9P;J4-*D7TI4A2&=~m(t7Uvdzb{**15bH(3)m9W>^jj7)F1fZa%auDD%+
    zgZT%by(-sVVj3$aW_ztUhmGcYs6-l&%r?IkrE)o(DtQ0R$L+x!!tlZNNDgF`c1Iip
    zz5WUO&~%5LcC@+*dD~*i2LC5BhpJKvY(;&rh}vX10575rI9%7hyX^|+9*}Tr?!fYa
    z0O{aCGR2>EMAayK26x<uS_wSZQ%K%ZSea>GJ|wa1r)DFe7K`zCgi0czzkG4Hoj_-X
    zWXT49=J_)f(rFO#hrJ5<wCa!P=tjB=MK<-($|!jN?$kE%=$`qST-6aTK*>#gypCJ@
    zy-f4O{8)_j@Oboi216-szMyRJSK)9kEaz_oHCTD%uLkSMi*_=E#);<g{wn5VOy<uN
    z=_FO=G>w|v9=E})33s458elsT!k=>fKyoA>aX&k2-Uok2ujM5laMB+8KO+~iI2NTj
    zj{_kIyywqYA@^`w^@-<AA%D+t=L+VO*M9t+%LVu3tfyF9d|26koO^)>K0=IFPCn9{
    z-uaHqTiIYvIxPepv420UL~T|QPTAUzYFCaLDi1i5&jNn@3!#C3|8(qJg#dMI-l=dB
    zaO?34BlN^^Bbg;kV>G`A#Im6$!{3(E-xrS8<$u+kr+wc&;WT}5LVY9F^|7fw#DG^h
    zHiH|Jk4Tfl#^fy6Ttq^5sBV8LjOUb>kmG(Roo(*^sDt+_r7K@#Bkpo<rdV58DWg~*
    zKKq$y$|6Rb60Y`D_rc;jbPX3@M})vW*07)Ovn%E%_nlIhoOOL7t6ni<2II!Z)Fp{p
    z(`!1M>RH@wg|uP*MTk%Kk#g+De;u9{yvW-9c^aEF`PI<OiD<i43XtC<UA^`EMN>Xs
    z=e+O^Q_t0iGIgfvMSM=aSBV`i;tvgb(ku`}XNf5;c$=4*^hPz)v}{~p=+)Z}?fPsP
    zQ@iWgwgZ<;nek&|1)7rTBbmE<<))4^#R)vsqc2Ju8Wv;YkZaTuh)PC+BVQ*rKc!g0
    zny0$>!#>3^;#;?dTuGL+v17?yt>LV)!XX92q@uIcW9Q<FO+gs<_`da{@5P0KgRt`U
    z%n^}=MYd$ol@N$VqZ9OLMMq?QAm1RDzyCex8&o4f4k`RHZ-`o2*Ez}WRO<o94N1dm
    zBd?~*IHiYXoemX^tRBcK-EpaX4V6Bf{03#X8vgoy&f)I6=U^ZyvXIvy*D^vIHkVr}
    z+bV~y80Y4wzQM{P+fNG>7Fj!}Qo=FSyyatjeEjXtAjGy%WTm^TO>9LnPhav-;SuDY
    z)`WbWflv3K=IqsdT@?G+?!)&s!4Yt=#?^pZ436@+f<cByJ*Q0+^2ISa@sH+;Zyd<E
    zJownHAzY%S*z6cpaC69HV#Lt)eT_aI8yl{KdN-~|BOMsLF>|BvMtvj+`<WMa?ae+}
    zyiJ<Am$uSpvzNvZa_OlI&fn^uLTQD1p7JA|cB+TE)~?mWd7O#Wsohvmym?DR$B%B*
    z$gxgO2K8!qsx}nDCtDXrxxAZZ*?y;&^`?hlEvqPt-D9g&ATwB3*3n&s+WLj?W~Nh+
    z#HP^x+uPSgCsRg*XC{@JwbPdVNPu9e>GRMHbtA>%hG~go;a6K!@5bs@q_5HYtv(Co
    z4lC8BEgVU(qzZ0k9eA%^kfeLV#cS+qW$zv9pk_GHiMA1WSMr?--vvJ%o~0yCphe5Z
    zOwrh%>Y&lSV!^I;D_hY1>CwqTD(>VejqsUFiRn+CIwR*!ADLIckqvK>p{H_ryYXJR
    z><r`nn4$<#C#6YVoMqa~z}Lq|60@KnvJh#%oaNG!R-8kGOA$H}o-nyryl`MSW>U_#
    zRM7O8P*r@$_fTL&k@DsI0?Qe{9aWP5<DlZZGjqd5^}vUoM6+7K>x<L64U1uGuKp7X
    zhyApzRNIw)*C2O}s6^ts?+yDyoafi`lTj>BtF*JSK1?_E3CAZ?c&&od?hMvT(4jkn
    zG`-9i@)=v^>cV~Y_fW!Xc5+%cd8m0L8O0CUD1i$6Ep6J5gue~Xa_RWoLC)q(tTRw{
    z;SS8p?LKO{S#r2J&C6s8_5v<koGkr229{DbA2oxx7pdi{>4r-;M;Ll^Ix6V&_EC>f
    zmMx7Pd2#6O1*n@y?^C5~3ID(flqSW|R8@cI7{yqS3}k(M!j*{%oz^VA4oxzsh@`l2
    z(A{wI5=ymFPppF7b@z@E%J{OkYd6v;CP6AH8o*N)o!Wf5Z~X&&nY1kCw>AP($0T2Z
    z*L=^0K0PvOdPoJL`XDcOjrZ3qG!#FE^znkQ{F#jo*}tA1Z#4-k(;prsuTCaYe1PCv
    z<^3;%ZzgmhgV|gvFCKw?YIGDhzcngqh6u*9zVCrLEA|FujNih<$US{oGOB-Rsd=pn
    zSGlYrFHZgICh5GJ_cs?T__WI-M=KYedv6jA*U4?<vP1CEf+>VfawGrF%H4iMr6lq9
    z?e&B{g~}#9Zu7}l1Dh32$(^y+*jnGgVmcLd{};MntKak5ZapSb((xCmB=5?-&+PwI
    z_2Y|c#VBGC{~ViEINy6?4Mk_2w69vUh`eur9$gyUBs$7ZZ6e5c2`Ak0qNmdJmMil~
    zvC$8<m$hF{e6>LeuLP^c>0imyR7}aMS#TTNvC8%TQMJc(IG4ovx32QpMjkxIc(Q}q
    zr=$leYjwR;*P5r6egv9E$Mg#3DChA-W7wHNq-t)~+b55(4KnKDdhzz-=h^~c$5)!o
    z#tpk21$+sPYO0_ivH9sjzaA&^f~tv?Q_G|-x(YOToUZk%T$2X#l3b4Jl>G~z>OZ+<
    zHy`dc|F)`WsfFxu%KlS78YM3u)O!3%yVQ8PdAg2}z{{gYh58o){;uBt_;v8mgv|L#
    zWfvW%Wt_ihAxk=#B7{dzRGWN9gZ?IM51{a_CUXwy+|bF;vrg9`X-l0&N0ToNVVQn4
    zIO*eGH{XvD!CfuS7BWKW6_5Gd_SRZD8GE08kYionU%5+Z(8z!XeSA`7FJqB=Tb<~c
    z%Z;0jdRa%WkItui;1qTL@!XjD^g&9Bmf|5+w3UV9Q0)DmpIjKfA#*r1T`}0|EYrW;
    z(fl}Wsnud-+Hf@VcVy0C_LdB*0loqLR4?it6+5f(*n}5*UP|jVHKDlyv*(?PDTFEV
    zDjJq@oRAbJ+n{<DL-$8>r!<|?1l`!V?n7zMpss{4d#SFTPv;6Z$IDliHSUjk7S_P6
    zT<oRy{C==G^p-=^w~hBN@$DkjRY1y>gSC@y?2vvpH2wEJwHj*vUV%@YY?&=zlHbB-
    z>KYNbKgLg!{tds<KYsY}v#V(3{1PEVSj#rY{=>{A_*TE~uyworS94T<mR9W-fqR57
    znwD9Za(#8%f^)R0$^1<%xTEOO4wu&An%QZ6!wRKqQrg1e4$DPz-~!b56Vo!PN2%=F
    zJyYk&mg%R;E#bsAa!WRet@us<9|>(s{LkIThw8s{zM(dc)|AZ8GQaH3qeY_ryCt8M
    zS|@v`T%W}+?6ND14yf`)IeIZ~czigU73M#!P9yF+eDWWO60pgpG1_uxJzu<+6Zw0&
    zjZUS1z2;2jx1-Y?g1V@!kd_m0K8Xd>ex~*0db&#Ha^7*C@LH;AAlTc>DQEA;VVBUC
    zXTjppOPAK)QFFp<@joKkZ~r(s+ja^&Za8e++A;{Po(ffIYp^;Eu9RN=!w+;Ee?0q6
    zBTOh5_1j3hd$i-zzE#>fm?g7Q+OC0vkE-JK^TcJ2+k<MF%oZZaYr#hmk{_L-A5cbE
    zQAQxQQPVolY)tBH^Q7DI+SKJGY;h3V7_0*>f<98Vi!#aq0b42U&>}#Xq-54jWH#fZ
    zytCkpcTwO6o#Lvv`%-dno))tf%*}q$hmoCjvvp&4r740W8&#{Rx+9T`ax~6QX>WDR
    z-0X7n{C-2B`IfFMeNYF$sDSOvlD%r_xGN<?#V=DTDEiDn27L}iV+3TVI`5^n4|c(K
    zT4Ww{!!5a5)MId5Hr?E1ofNHdYFg1e-H}woIO=`W6KL0iRfRKEaFnWqAhcB|`x!%g
    z2V<@5o%k-MMOld;_%p(HO+S#=FZpLsIEo3kQxhTc9Az%mslF|Bprc?Zc8!at*N&@~
    zI~zCN(&<wL=B-sw{o6^i2s+v8*2u<a-s-uP{r#nBmri8I?Ra_dMOnkh9!*@A(PEd3
    zbGXU61Zp3m5~FOM4VS;_sgA%P6nd@KmC4KGvUSKFBc9to=zmex?bF!pSk}wG+Kmm8
    zZky=6nfl%JN(o?embY=}kSdndE0fiq?2zb|ms;$ShGLDgu_mpGri(o?RS;;cG8)=T
    ziI%kD>CG%td6bXx2KC#otEfu#M^Hi3)WFy>C8zkv=qm-6cxZkclrI0d&Q%zFcfW%}
    zmlOzUQ!JZM)<K5rAmdW@O6{rQ8SqgUxR)9^D5?72A_AOMB%h-Y&rxWFZt{GUtg-=H
    zQ>8Gf{;(jWNU7(M3jMja)M%aiu~LHtUf&C{F@?I==i9xkejUa6FG{l2?!?2(CkM4w
    zU;H{A{6#rr#H4EF(3euy%EE~9ml}HStok5Q)fqD&zX)xqRcT#><}9ju<3@b6q8*^a
    zKC?I=_b=}AgW*oK!Jh2;0%x_N>}UPX9mU=KTU0O`+~9V6*U02BGZgFfVBj5(%D9kv
    zZ8o+h73!Sa_hB&{>NPYSshVyhIoCR*j={`#k6bK{EKQCqQ>m@o8t$-pz7`*;86R0d
    zr6uL2CRGKO!VDK*U4!MQZ7X1<@XrMiu!QwNvSO`g>55c2J!Bv(8Qaji{V)?D^*>ZU
    z9B0EF4OJU-$DmZ$2CtFxNDMQV((RnFn;7l$e9fgY&8z(pe!M2>mWIaFk9;b8Arl^~
    zj)x_{w}M6;_#t1j@k7~o@^buMtrqfq5A}uyE!~*oxrPd@CO`9!Kc?D}m?6O^3~*95
    zb~9P~c9bSl6ozR@o8@FgVo4Lbui2#*4l)~NcNu1liG(U@aS*TBaOv)(YV1z-E0)Vz
    z;fEi$jZ<%EQE!aWBDJ5KyyO?w=I78<F;qpc={WQ2I6I69sZWU5PUtRO)6t6-EBGP)
    zdqfr0uSyt`?9p-Rp8zJO*qQN=lSo-JJWN<uA$XK5Sc^OgujQso8LW$})8Z}HMYF-5
    z2#>HJbOhc<@}dWhYatEJ+JeG*I<^zc4ih3sgG(y-lA6Iw2QnX+no*9P2@+ze9?nBM
    zZniN6LF!5oBGJfcD~{=hQFt45ePs?TMIAmeO5bXAoW*VSI<vOG5*X05RcTlZcjeG?
    zey)=st><xK=utN&=B?+A!0Nk1dzsA`=@}UpO!4=Fu|abI>R_kzIq8v^=LEfw69Y@$
    zNqI%M;Q5@CA3kg(f(+ju!8;$RXY6Qetm-|hR;TX_(~+DFkNZ7c8U+%B8R!saD{QCU
    z!A!i_VBcH}B5e$Oqh|OW=F+_9{L77;R;SoulNP^ca+vk4-fQGW;fEd==bbF%>lsqp
    z8d|x{M<*DU<QPg4bT!+IwMXXrHs&i%W`$g)s+bqNrA=yG$c${~a&1gZ>kI-5j53j0
    zjdfT^9oC`_oAcYK^}WVtj`=X*i0^vFl(w^E=vi70)2_N{po>nY2ew>Fx7>%s_P<|b
    z=2}LBOg514mu#?M+Zk0h*l5ABYVH(|-yFVtW>OujDYo>Suslf&Yc4lYc35Hs&HojS
    zI1!GJa##uDfFpzr^Lxw|Ik5lQR#;}uqTfTKb7lb;`e)lb8gIOoF!sxAYQxri%f);v
    zVR>iCbl2PH!^pB~@eB`ciXgo-akBj91g*)x!pd)9;|4tywmeNRyNJSHO3%{wT3l_I
    zk$#5(Bhz2&jIY&0z*rD7pE*=%nc2>KBiNjb(`q*Xy;p!v8!@NqMI$xLG%;%-Bj96i
    zC@sC^jo!5zA`o8Jm9wZ7sm7HrE|&j-Es0AO0FYyOSN~UB+fSwgeXzc{AQH=rfh3h%
    zdmz_fu4wj;%R7o|&+3<X8Y4MI!J0U8CmfnO(Tba8<x%0<W1q+yAJ$Jc)^5RnsfPSi
    z<6IP64!^DOOSs;W$;Vn$e;HEoOS0mZ6w4a=94_-=*#N$dYG0P^U6!|7duYD?2yRL1
    zv;O>pjm7dB?<hpMKLUo)vQD&6OSDj*wut7y!u)3MrT>H#|Kw!^-(>-Rt&4a~Yc4iw
    z36`<-KyI;_Z|I9`$zV4Om8@hdHhe_Zx%k!<mp9B$Hv-^x0*rQohL}^~O)Gsn{%ISB
    z5WDCoJI7`4t9Cml{cY!p`PYqx(VS?&75htEX2T5&*(AdCIioyfw!HMWWD7TZDmHu*
    zw|LIOxIfwl)Nj4#v{Rn74?W$orMA8@!~BcdWNCDCFvomd+KFu6{8Yc`I%4Pg!Qo~7
    zcC7w#y#8*&=C<lNeDCC!A-zp9y`wze`n%-~GkS=*-Ol@I`(PgnYoEw-VLKc6b|%Yq
    z*tBEzrel<k)2I3fr_D{5LZ>$*xEO9Pw%5sZ*+HL#{SJ373fV0pZF?8KdOI3k{&B6x
    z2P)vO-{<4phu!ZxwKe323}Io`SeTXDj#bF!kBS|9$R-S9XNYzhmvL|+?Mx-^O|yi5
    zUbe&4N3<6HmT=veh3_x2I0HrX=ABAwW>w~^N>&0m^R)`w|EPOEuJso>5BMBB@7*8b
    zvr9YJuMV;MLGLm??Kr9Q`<@|YM&D)1?)SZO>}Ss1*$S8W>E9>x&dY_^XY>cEv<GJB
    zEmhhGSrOMY&V%&|D*?CtZdd2c)7Q!v*mj~#U!v=8KBv73M*)xvEZXf*X4|O{I|DzY
    z4E^26`D(6x@9*;7iQQpQ$f2s=;q|n`o6r9I)Bm%ku}`n*3Sj>*rn%pKNVsDG0<Ub{
    zwQz&6y1@Dz$4?K~09VR~N9+xRBPEZ;QvwZ{J8l_veaqzr&B4v2*Q@&O|6JXP8qUOz
    zfA~KA0Y4$YFi`?Cu7Dwz-MGv#9!ofcAKhd1xF79#^uZIy63!6~c|hj;>fA|s#`8w>
    z(HZ;zRpjwc!(DF116cNWV&DZ}VFCzG<sZ&zqx-@|Ucc#&*piObJ{^DOd@Tk7wUWFv
    zzaNVi!C&H!UxN-_bGhp_n4_M#OECULogJz=c&%Zh6fFKK&3G$sd7GVigCBaTRC>YA
    zTt&WnsV5yDaULGQPwZCqY_~l1Ry_66j&7tKq56Ct^_}umc_{at@U5J<9eO`!J@$Bb
    ztn%rv@OLlkN;ex(4_oCU4dAcVr!$8lxJ(h&tkE}k<t%9C%<SA3L*^<$>lc>x8p9A_
    z_WV3b`5bHD2VkP3qLa>JR-)ci`g!*G2{0TF_nvzc!3`UIUm0QoqoeL2{8K6k$p#mx
    zqW*fK7yf5n=>{i(pDr@C&XURe44<5b|L_gfJkJUB!-k%J*7S=Ly^K}9d~@iR`0Q+$
    z<o7o8@-1ulyYFXj?JokAFUmA8N<}a3rJq%vd1caEyv~jIcz70icoFvFOqK2YjML9K
    z`d<#<pQ{`YQxsV6{k-7tyok&{+2Z0m*%eOtBAE<S^&|*{2&y%>%C^7CKJyJN`q!U!
    zHL&6@k?YrT7}y>fFhX-RdUo~w*%kiD)u=&Gf9O9mF=B&e;GjKm`pJ1iG;At~I4An=
    zM+0%;yDu<CL&QBiuRaWZwH-W3Lt0fPj?<j~d_r0e4P3V;tsMqSoCj|`30W-)Ss@GA
    z?hDzQA?<FFem@CZdUmxL9r81jv@Z(m{qX%G8hZQ?IPneLe;s<x8oC)8x)M#=YXGkL
    zfP=~q!jr2@vKTY3Z3L48VHFidLH^X#koI18%#DXH%N+i&%Yp8Qx?XL)y$?zP<YIXo
    zk8D)laT!!Q?;LRqr$2orldtshms*OL=h6Po@s<i`Qf-`^9Gy0pp_~GC+5KDc9OR|N
    z{ytWyNH<HX!Q;gLmhD)jqHV10{0Yx|u8yX@S>Q>5Zqn<C@$!EsHnSzx*1bz6XNPl*
    y&ca+bg!ab2TH<2EDCos~95qYcFz9S@PpvNGynSeJFq~#RS(q)IB6FJ-B>6uvn{uE4
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.png b/mapbox-gl/closure-library/closure/goog/images/hsv-sprite-sm.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..470f8e0c967715af12ac91e8b43e1034283a9b6b
    GIT binary patch
    literal 19537
    zcmcG!WpEu$lO=p5T`@EB6<W+}F*7qWGs|LTu$akWW@ZM9nVFd^$zDJ2%)B$ZvGIMs
    zwj#Q#J0nkK_NnUX>a2=Xl$St+!-E3=0EkkOqRM~k$iGK44AkGV+GWSU-wNm=ETszb
    zxA?-CM*O{pb(GX{0RZ69{yD(rx4wP+6%x3LX}YR7n7euyIhz55OdX8PNW`t2%{&~O
    zZCpr1R9MrO#UcLcVg92h;%sK*YUN;0qH1Ml2H<35=452zaxM=2^Vb>bKMI<1vOFdZ
    zb__=U*kSOra|G~x^>j2cu`zQcF*dWXvgaowAt50nu`=Z+(_oill5-R>v$T@*b~aP-
    zmRB|LwlU#0CHpD>$LGoO7i?$dYDD5`XKU}m<H=9<FPl7num2G<l9BvN#npzN>_39m
    zlv5-Tad0*xVPjyXH(_FBBVprUVBuup=H#FwQ898Qkv6g?Vc{fUVPg7wV&P$C=3!(0
    z8_d57nE>41n$OwPoJUzy{6BsEz2hgdbai#)VPy31@L=#@WpHq|U}Waz=4NDKVPs*U
    z|Eodo;$`n@<VkPuLjE5{{>zT2nTv_DmE*tY`p2%3v4fi{KN;CSj{aT#^Wlsf|Lw@$
    z<v+>$OCzJFks~8B0~6!FE&m1b{T1^3t05emRUI5`1^zE>#`h2Me^dHzum6oV6E$)*
    z6Zp#q7d;avJu{~&GZzmV3l9s6_W!{DhtmH6mvb<+GWYtwz}Z=N*qQzl{y&ud6VAu@
    z4-x-^n18YU&oliWa+3ca!c0y6&6lH_v+ci{#ngn+%+}1#%-+@IFK5jEJ7=aQJmwD0
    zc1ErOR(3`fW{i&Z7JQ8VE&TtC??0mc+b#ZfOU8fq%l``f-%a`d<l?`1|9@isKaBpr
    zbP`!5ZUX?20Hj2PR6Vn=y8RnL<FTOQxU!ebPLLZ*#<lFP-+A&aEK)w8w8N>%zE3a@
    zl7?Sy9*EpFBdpA$-ui0h>gFGU03>8!(0lASBk&t`-wnsT|Jv9N`W-L80iO8J6-4|F
    z-R=wUTUOgQ53pW{<r|m}%DO+Th`%Sn{qx3lDDPhaKR5am>hID%`g)mfS7bkE0>6^o
    z?U?Mv1AeFU8Mqw$LE2~ZIZ%N8AdUFSa=WAS@}~*?&kEc}6T1IQpMvM;@DA7q;n)tu
    z8`SO}z#U{?FZe5A!XL=poBj`tfSc7<2a@f?4*8GTr}qyH(02dVKS(PE9JizJuOhp5
    zKOldDt@;CNdVLK)zas1UOYI`Vet>EE$Mp34>AHLMkNl9?|78OHrw7$v`s0{tpVM;?
    z8~o4v_N(xIE9e{aZtvlU1M8g*nt|lUN=OYvufpz&*WQcP&Q9>_*V<T4G=tkf=-=Rn
    z#9$x1N&dn$y<|21BKuS*e*%Yy1%LCh{0TVp2fU$2{1w)c{6@Hqm;CS#s6qc~a`hE`
    zhnoc*IVr#aK1fs2mINZAqYf5$F5pb+RL}zi1b`BN38M!GuW5i5fgNPB$9MQdZ;9nz
    zTC>yEbdN+gKj=Cb(;S6QGjPM4(K+B^`&h>z4`hVg^3(6R8t2C+M3(#&jf^%SfklR=
    znGSgrE&mKZq*XojKOz<MsAH0jjoQqwYB_UH#!r|xpBp<OFHrp;K%unii|x#d0`JjT
    zMm()-88ZYfJ`9;2OwD=Obrle2tg-Mjw~~htz7H}ja8vO7cMoSE0uw|cHWpR@P=ydI
    zKoS@!X?!-G59#GLh<g5^q6h#*Dh2~OD9g1-S~|iLz1_*qRkmhn?O2x9&R+rQqU`wS
    zt`9&I0cd#H7Dq^kKt%J;(kO!~*iTAwRIT1<7}LU#(O{M(sKli;p#*O)G}srN-2{CQ
    zSFhTZbbw=8lR}(?b6|8IoFF*b9RTaGumVKK<m~`E+R$YrzFWZ0q8-5()o<B;Ej~J+
    zI=m7%77GbKMk{pBs`iFUL5|R~tk7_58Os^8PgOiYjHe?+mJtDY2#%vfNCmbn3#=fo
    zZwN63Llv#!d$e5SE({4?+X?QN@ixeB(%q6EK|?^?Ra{d!k0li&+CnOM0MD>3kp8TG
    zaAW)=1oQPZNCqTODxCzwM+iDAkhV!{25tS;sb~%xu*|^QqlHNoPZ_b*Cb9h{T^FI~
    z<buZ@IJU@nQ92JtlDO4fDKSz`L&sKP_H)JJXN;p4UCH9gJbn4Qq#>!35;%%ITB>5z
    z`48Qt#CmB*a6FF{Qk3%9^9Qt;>~Aww?#d5P7jJvQP?JLqP5OSV%MnE8Xsy4qvma<;
    zO^o`Hrw{>rILQ3H;-Xf`C7E;y*kfv;pgM<FVZv=~iagl`(mi{iI48JSZiS5P;K5H?
    z4J!!n#6apNcQ#SChp@ZQ?H4)%<?3oDuFO%wx^{{>G_=$VFQ8+!@=X&rnB(|I6{M-+
    zF(oQk6@?ITKqr$_NQeD}!|H9RtavI4Z2X!mX;lDwovxr><+J-%H7Oea#K&1+WQDO4
    z&J4YSg+|JH(K8Qa%@kZj?*n&<hO^N{ZO2T$NJ!t9JVyynkAp>UkTTC^rmuXZ*y1B)
    zLYKYExLW}Mk8j3-7eUEHrc6@ZZ@kQ(!NHGL&mM4Lr<{q+3?9Yn7)$N)0OPJ=x(Mmq
    zDn)v}ecErJ9o7Z~90Vuz+NP5uD%oO{aVcSQ$eJ99p;d&RfRQi`sQhDQW1=m&>9xa0
    zW@m27GoNELJ0jTBk5U0>mB_=3ZoN@I4<4$XJ?ROMK!!B*YXKJWt`d5|^ctDNRD31>
    z{rl7D>O)6b6(^uG5B(~%4V_}OV&L2&|If0^{cBDhv%Ugi6ICr$=>qGFhZ*PFE|X@%
    zFAufdB9arQO`l~SsE299&&M5z8;3q0fCFF~)Lcn4MY0S5X}c7mE0fO=sN05x@}&<`
    zl9B_07d}fD7DyB!TcuZYK@Z^o+-~%}4IPq**CeUa#W1x8ZlidSO44;fctW5W_K-Ro
    z9l%FHdqF&7C%|VoFbQbmvG%E%jm$17y(yJ4_6BI|AyJ|z9c|hKI~iT|K_#|V@?!-+
    z9^#|&qRKKUA6t;C@7qyqqj-{|+L$<j(Sg2462(Z7xbF(F(-_7ViTj|u{~gL~bzyV`
    z^~bsrx)SR9N1^+Wakuy_XLA(~hD!Q>LuUAk-AALY!KWW`U`!yzAI|I~{sb%M6cZ~K
    z`SVN}<D4@bycy3Tkr+xRJio&ywNs>&5SSnMWyI$Eg+rNk!CCi(Kc)4+0~qP&Oqc#J
    z5^+LZ^E|t3{Hcp}7`CM~A7DIdVJOQ6X3~nVhP9TSh?zx(*CBNf<Ia;6mEv>J;m|ti
    zNRC_k019A^oR+(MexWA_l>kb&3tl&@Z0$VHRWDyz>*=-8ew^224Q|V=ouW9>(;0eB
    z5JBTt($>5{sk_x&RcC_J*_+9r_|6Q*j6Ol`LWGj14<#7}gj#^(aotl&lJmEi^t)?K
    zsA5nfqClE%B#)!f**|+-i-z4wp=@^AsF=i#uHroio5QNZAG8fnG1aD}tWT(Dt}=a&
    z3MbX8j-#dCS4`Y+wpA$8len`Zq~XmY9C}h8jbq#dZsI9b3K=(Ii$W}Se07Vz8)+Mw
    ze1T1FD?HfIG}(kS%Q>98<3+cZVK@r{3KPjZcHhgYd*vE68fKF@ggFPd!_<iCF5O2<
    z;L0UYKu>}1MulO8%uIHg0otnzYOIWbW7Ns>`ZASm6)wMgx?`*yO27Gdaxz*cY0|E#
    zwrGoXq*f$$P`4*y<rjCQpRX$U7a+wye+G)cD4XKL9M>oz1mS9)$K)*#s?Xd|u&NUv
    z^y}KmdwQ~aS;i>uuqP=DOk*^UDz^{1`Du|!RWW&FyPxj<ZcBi+*uPL}D{T&MG8)6(
    z;<RY!sw%2<u1v9>2j8YqmbF9lOVjb~=y<!lKX;hFzNxEbd;n?gOD$V-lD9`o6;nTj
    zo$JuPuzkf*LyACcR_QQOt_BY0*t$3yz43nFUBDUM@l@z)Tb>4y&Lc&60ZZIsj%hES
    zJL>bAP>b2Iwyf1JsOjqf=-*P{2~f?*_}a)&>QQBagi<za<czLCcBuecIsQjC8>TcB
    z^RWejjdcKv%AD&54a|t>Q~NKL<l$W+;m4$epZ1{v7TPeE#&Vq+KbR6wP+WRRY(Hd@
    zJqxd)biKM-w%N5w>!-9%&4i{8#zg9W*%#{T56B8gE8>|p)ZbeRId{&w9XNM3*~3lX
    z3mFczO~<@pO!qfg*TL??fQytK8tN#>CI$1Ss~H~KXM;;?y^AdJ(}2%Fr1PZV2uWi6
    zJ`+MkOBsn`go|9VZ${B6!lk3IBio!~p{bq;=Y7LV*9HNhl-NEEu|Ma_3<&v(H8*qJ
    z@7-%oxs^^HynMxNh~Q`M_~M`%$JVo88>XbRx3YHXm{75wskJi7o}G11Ft$$W?^R<K
    zRLWD==V8vAz6c5MsNYD<o_HoGmOfYYnulz;o^)XwV50fL?$QS^g7HP@YF+1)z1h%L
    ziQQfiwsUZBupGe}?s@BK%20;lV_P>lgyP%s9dmB=MM6h@VMK8t@o=J%;sfxddy*%5
    z?Gg5Zc@~h|LSXLz_ypx%?Yh9;;)Zyw8BR#6(D{F@+7|0Q!cW1E$9{r^@WWlt;ndd?
    zMXqLkpwD04wMKZ-@ik1{VFHK*`82Z?!ASWy?nv$;ycIxo9_<DAqI_)VW<ppaG8mfj
    zx@}3T?a@?5eJ<+wqC?PV*b(~*5kunY87iM+trd*hfUe+IWg?xP3aOYcbo(tRJQY&}
    zGzimi0<jad@%HjDUU#I`CQE4+&=YslHgHMIE9?`IwL1><`(LOO?g<=NoTy56TG;f)
    z%(m(FG=c8Y@e>dgyzw+J4b4id<#l0+S^*cC#^&K>8EcLRfg*9bF44VYQ`{xZGI1jW
    z(_V(`d{)aU4<pgS^EK!av3md^wZKe5aoq*nh9bMHjt83Cavx?wTTBa&)>*4xdB5zB
    zh%+&m6r7DnH^gdqdBj6lsZ2<04n`l=<pfvVzpm>sdNgk-re?wO&SerRuzOSq>W7Nh
    za!e&7NMe#arA(QR)}`Q1F{sdy+%o|Ov|JbZO2NswMqmtsm%$z+{Li-q+h={%lBf7M
    zkg4=a^v-WNx|Vr#{Nuf_)^@;Ks03(?t96YuTs^-FPP5h0-IsSo&!LMNItBA0>niOw
    zuRCt8&jN@4?7b16NDm}hErSEw!~GLH4$5FvRY=&~Wd(ovxHSpLq?fOzP|oAMSTK*-
    zC+S&_=_O?x!FU4N0g8`uT;?H`oU3=Ba4MHy5wQ~cYS`&;nh|7^%GXTO$BriZa)6M5
    zOP-(o^u<&}+9iS&UP)lJHI8l(KOV&)(M7cBQHZUo8<={ao_sG#JM%S{$dO68)#{(H
    z5a*G9WE^!-ai(HY=#AXlCk<g0Wg4%sbl_I1(Se5g+XUcI#FKv}db-pIg92o6VUeuP
    z+yz35AjyiLf8w@7wGR&d>={aytQv0f)+Y-_#~VdWxBfuPDj%VxpGIlt$Sk(K=yo;*
    z*U5$}fRg1MCgEJxhhYFRR_VUhaRyoDg8ClHKW|2o0fgVp@%g1YPjp6A&UV(O>8>U)
    zSrbJ1#V&q=P&L1xYQ!ZR7L+|i9;3O#8VQwB0UBrbl9(h6H3v?G%na>UIH#NJU$#h(
    zoM58gAKB&5icr&6Sz<^ZzfS|LQqWEvt5m3w47Kt4f$djEp(^kxHtAdKPo8OMSKi8G
    zsE&N=bKT&laz$C>^*`Z$xpqYcY{mMiVYc*{^4q*fegoE%V<-%iPBJntlCl={e`ze-
    zCADfq6@a;s?UF*(mGJDZ(YkPtE)rx}$Hxs&hN$Mrn<t=0`4Y!-j}mi0^!Fw5<vNWh
    zr7ouNY~Oj*U0Fu7MGg^Ob$T$dPe1&E{DW+GbZiq6(To2MME|MxrX$GOn~O>jqJnpT
    zItxv2>GdIx*BD1{BhaG)yM4U_k=}+O>!0p&X>Rk$PDvRrc$(bF&A!oPInfJNRfkI$
    zn=8S8%jY<A(nSX{AnMzw0K`EDHuskWu}1jn^l1yv2NnxhfUgMmfV~Sj+SwM|<y87U
    z>7t+I4sY_4-Np@l7c}2hh{&zggyqBW6j5K403X69T73o7zlDu?gcwUY@&`OI?n`}x
    z*o7Zsl<W%J2e!B>K|<Q37#E=oLzC^4?5_pW8?^*_>JBv*!38fRd+XpG#%eh+i|1Yp
    zY{=6Ax5?^izz>kUcw`8Yt6w{;zfpsB+rK!Qk~={<?O9|LAnwv465wZaGNB3p^xY6?
    z5a|_V$Qo1oQbx7xz(2%I?%7Er4{4JJg2nhn4n+M_wqubM8lGM__@D3<4s)t5V_5tR
    zSiZ~}rE*ZkHE0Mn$wCGk^L%XL<DVzGrrPB4%vq$T&%eJNtbv6hqs&!RvJKtoCr5gq
    zyXbcEyRgY;{35Uu&FCc8AKFWl;{|L{&ANSkpY^`g@`-bp6TaW_kM|Zj?bm4<6`feP
    zs|l|lQ~qRN`Ys`+a|Nrp)>%&|mTzLFg4W~mfgz5YAP402RJQi)2NLvWLWWIeH`~Lu
    zMmC{hv`*>fQ5fiGtmUe!ExFATAPyJ*dWo_)ng>J!Ucq>BhwK^GGrtJ;tTRd073NH^
    z(sGML<O$2Rf0;3@gH<Xn-7;_?v*<%*FR+-s0X`67zbCbG3)1Wt%q3U~;$KjTs*%Qq
    zAbou2OpZko4fX`T2`ax=K9>1RaPfo25;;`y;*<M)v8{s$oJy|C__+Z2_pfCiVbl#C
    z?oy0UGt4<gYY%)JPG>b&Hu@B{i;&vrmg_V<N$rsr@`3?2Y)TH~(DxmU0G?bP%vptU
    zv|Tdp+yt>B7p&#R^%g`SN~N&xkCvhl0+KsS2h*mQr+I0G^X5xwxX`19C}vh#8Xw$_
    zgQ`DQJ1N%FhvrNB`8_zRZK~F=04om)Jl=#4L18~}UhwKHn|CK@URxsfjc&^EL{bHa
    zB5ON)4Hw9uu4|Ki`*S`DiN`o_La|Ix3yf#I&?^aDWL=D*S|_u3UjS*gG_3?<<d-ze
    zJqnhUIW#_^#QqJVHChdIe;t$Kmw?jZ)}Uz0<&e!))cb>YY6Wn}Dq&6VbWgM<DV)H1
    znXZvA3-zB&)QHU})VZK}u)q`~%`41EAM2Inn%dA&8-pMDSevkl6+RpT5y~EP;Att=
    z?g-<K<n>G;4LEC0DBs{_tOeXT-997%NB6(pxb+OLAO!90<!xam=-opyM9{t9AdsG)
    z46R5DGJ6^CKsG=S0aSjNaZ5Qa@u#gLVMF*b50Z2ybra6*^7@bLF=u{u2@sewlK?lc
    zi+tfD1=5E8nT~OZ4ShJ&L~q6-NSTWq-Ozpy28n|7Ck{)!994#{i$s*-#cmN)%|m-|
    z6xOwn#C}R<@c1GRKH@J#wzK`!1ai6syl=n(jtM_!n6Uz*lXv*-Y2ONB+&3hvFnm;#
    zUzO{xtjbpx8k-05x30BKa|;f<buj5Oa`-=J23q-WPdnYVKFqJVzcRwdO4SX4t(cE+
    zI={H!ukK?+bZR*3crLv-r!tKvdl7jcRLcwSr%A?~kna*o_<42}!$WR#Tn}#aYIJ3N
    z9tC=$_=ZHpNXV&5WnDgj34rT{?WlQP;`I|3U_NV01JE)8;Ul|=SMQJxp<V<Q>F&nv
    z?+I(#0994&KFC;;X(U9QKP9aap&87`NFt#B4pWE;@a0@d_sOYc=UaW44ZCLe3@F6S
    z(xDDfVc}3_w5H^V8bLmh?@AJkLGY`{mT@D|m*wb@qtbi{nzBPaarp&oSnghwhZW5O
    z;fn^mP83ee^+BG!6VNili<;OPpV;oY*5%9vp%O$vOo37LUeI@7yFV&em4Cfqx<EYg
    z$*Gydi#m&+)MbRv&URvG<VT;FfS$^n6=#XMf~f5F`-nM3Lm7ig)U55PVpx3+{oOsG
    z{L(iabUBeiPCVG0S+m-|FVQxJTynUb8jjX!{;XO*JWhjDr;37mdI5W}<S^n&i*j09
    z9AHM%>CfT<?$6j)YK0=8bcHjXdyFvhejeWtBiEtvY7*Le)Getz5MsrI#`w9kPx<ln
    z*5X-}JAp|0d&0VQan#?xBu1%A1g?PBz&65@8YJuf;)_jCh}~tdBlR5kLIg9e$6V7S
    zqGh&8Hd@s{xN&~^G!5rH1OGHblC)CQY1GnAia}QA1r)_zmKvR=Gz7`zV`uXo1lDVG
    z#84q{9Z1RxDSmwh8NzD}@AQB`tby2qmt<i)7y!2!ZE^xsitCW`%T(n~CYcEMb480+
    z?hG}~+}e14;wHsMcjM(<)jC<BHHzs06-D4G9Y$`l7SUY-LlZ6;NH<qD#FK>G3Ip=%
    zHep~HtdK(f2`x+2{imoGo0d73qZ^Ye|Gwr9x*#K&2B)ZapyeHPFec~avp6s6n&}#>
    z?hndea`x$bykSM!2bkEJ{bEO}Uz$}saYM~r=$9Q0E*X3g8}<aMl6(p)GCxsDI}3F+
    zwOOZ@rOdUWt>|}(`Q-Kx-*_pgIhV9@VMR-HJdsuNT6?kZe9Z3+wM8qhzgWYDvP&s5
    zcWg#Uir`)YZX?JRs<@NTX#>bhhqk9oqS7+<ZDX`z7@zejsX7K1AU<{_!~w27jYyWT
    zP{;2!!er=VTa+61z|rDNc0}+olozA*Hj3AZ>nlYtC(<fZ6W3h^rX3;pJV=Cw6)U6L
    zBnx=$47@C4lN1h$a1^e3vCio5d`-(%V{uwgf9e=%Hi{L;Ho|nIZkH3~?-ekyz%vHp
    z0dytp`sV2Ko4#U|PC-pN9_5+e?ZA)muC{OJA9|T*G0GSIjL^b185Xdq$JQ{OeoS6%
    zmb!ul9#4G8CT2D2=-!MQXQjlO%5>E~*0I&?=S(`TQK4BIW>A^>#dED-VI>aH`SX6{
    zXB+R`*mfoOafpuV<cQR>vm1zodurF2YN?n^hv<|YM3T2j9#ol_g<#ToHjpfV&+87y
    zrti&kn)7)k_IPLN@&DoWkMw`1r6;x|8RuZ;zjJ#%y-LdVh4LE2wb)L0z;x+s1Z(0K
    zWg|xO^O!ELs{OVzJ6=)z@V!i~SE(NrgFJ`!;x6Nw>mXDF$<pQA#3VE1)SP!VM@TgM
    zIpZZ(y@I-rMj}*fJMV{L&b_1df%?~zxvzQCaGm}x6}*gDP`?Eff?g!5JH4<K8kCDl
    zY<Hii7gVYa#3zPLmdA!^Z?cl(04v+C5y+RC-@PLbHfrg7@5Z#rA0!rD;ns9OL@-@a
    zsyi?!N{RPxNDcSNK=eTPDMmp7fO@v*@&q9h@v0{YPiC)lg7frb0f<P$_~x$1aQzeP
    zQm#sm-nU$Z9K1ci7vxT<{u=>Kait+|2)}`)6vcBV>JhPIMU$d1D>5(fRl{6F7#w9}
    z)V%sgs+eg%bjU#HckyZ}-~n;B{yI!U<&j3K0f|!GtL8OmsN{k%Gwb<5XP|3l<^0ro
    zyb5V&{&;X^J6(Qb66w!KfuE^@kEwiD#7|AP3W){?<YaJfTNf2+7ugl=WxY%>ju%Ir
    zwNg07@)OC?bh8srNXMz6xRElEf;4FKja-8jIX>9HTv@IFfeqD0&>+J_y<k=5^-nKS
    zI~-B_tWlp&PN&YDH>pxpg%yJV5FHUXNLjM7&!$=0d!&Bud7xz%2D^>CPc7-jtbnF*
    zj_^>4Plk13S2dK%UKBA8TNr200<%g2N^Vvrhe&hCUO(C1s+rDbx}8Y2WaH7NG0$y}
    zmSBjisq2G<ZFVVuW8$eAXC<Cj0Sza>`<5IN2?dj?VJ5&nR0gP>pGn2MO)Xr49q?lr
    z!L8eDC%uzJv7mwD(YyHImyB>wzOrs87it+LRNm1ZP~^C@3HW`#JTL)J(Va3*xjW7~
    z_+`fcxHd3wyj;e?P=1k#OKX+^kF7qOe3(SlV9z&x*33qnaO$yLQ`yg_lun2}n0qHz
    z7jRah2)4W{C8w}RS1T)uT(c*&u0umO7h?|_`_aIzy*8kvEv=1^5^pFE10lYLfkEoN
    zPU$}YVLwTC`}DLD5+XyWKte=O5=W&4zpECi$AUPeg<-;psQjsi+SzdNlNQ=An0}^s
    z7(_{%3sa>!a&WM2t;x$5DdrG8!yGi*qpV$CJ89SYoQ0<2$SkbRBvP(&_FR3^oIl2!
    zk}yP$LX#W9b)Q(L_om3qLwJH`UR0O6VuG%12Q@qPBPcbBirJyT$1qD3UV8AA=WFlp
    z%mlhX)$m)!5-($zaPP-jaB;e-p8`Z5MDbp0+5~G5#UnBsrilE2IBYO$459U*cy|OT
    z5C{W_L9@)1vl8;}Y>hyn58T-Ks_{Z5y)RdI`Z~^t4!adfzATp3mLv=P&bmjxpX!_G
    zFpP**yd^yggfQ30&Gr%3BaXocGn@th&a<rB+*=gy&e4l^X~?6d7X;#<%D1f)#Ib9I
    ziR3diu4&k_8h>m~nDDUd6ZIA<Lg(+H5FE$inRN-tUT5N(;=vvv*EX29s^=O~THS6j
    z?^{_>STOOe@rmt(Dd|-vN~KuHLUL9AO0RTLR+`j~svzuovWx@jVpuJ4EY4gxPg$E-
    zjz|$Zb>mFhW~-4JyS&6F!qpu(|8eto(M{62Fq|GEwk^KQR^gkJK~W>|-Wg$zjJ{=0
    zEF=?`7#mjLM0pE}zV5bFGuMrkLwQK$NNCLosrSyVl<b24O@~FD%pt{CiM`_QoJhe8
    z!d8b?{9gtSpqlP1L}(9it9VgHgYuT#wf&6Iif~g+!PCRe(Lux^WFgD@%kUM0pP(D$
    zx|OZPUflw>YUNg$3L5um+9+gFw#n1x!MncgzUFRU0r>P|^(rdVs>t?+jrUS+EZU$a
    z!lBeHzd_!ybv_C<J%eOZ`0sa5@BtHBnW~T|`|v}UrQv_X@4+7+zFRVUIkBmwOnFw-
    z9gCZH{vziO(fz@qsYN}E=U{|#n`EO)n){`JikRT@CO=qIYg=N`A+5&&#KFk`#qupR
    ztR*95cw|=cBjnzh0eCf}A(zh-y6G&4p^?;{5Jg23c9C`qng^^Egr|hu)P7qeb)v$)
    z$K04WSij3&#(HQsW-oEtEcomctd89(tb(@=)6(vc<sc{0h3*9qWbSFk7ru8qfx*TI
    z@V+KOMJ`*Lf%BM3;0@!oU^wjS0(NBoe&{z&o2`}`$;Fo7M?-U-E(2jHpL5NfQ`Xyk
    z*_Tqftp<(E*Ec5Z4{wO#S-JJSok@$ACFPFCKgxFXv2C(=Z@3|%<n2=ZW?`wnSN>RC
    zW@`bP!c&C;&H6>O^Y8N(@wyzFPZaW=KAs}MLzoIw6FPtS(}h)K0|3o=>2jhY7ykGP
    z{-k5$7py(DxcJMb{deRD+0osQVKN9uhnWKnX{ppR$x>0~d$Zm<1&V1`Xxkmh!Bd&8
    z7n~yI2ljAsoyo^^^i)(Ko?ReOVO*nQT~iCeSxA2k@M;aCMX{dl-7$XHiN7j!iL0Q@
    z>4Bmd>PKB<H{n6P^omv{l@2x1tf23BAeOdg1i{aWz}-Hgl3IF_O8?Y;Te2Tz-c{CN
    z_ajKNl%tiTBk!>JUfGV^DSm;wvBX00t!R{GQ&(xkjXlRgh2ovcxsyO0NpGv+=**!m
    zX!2sY2AB3{Buv68p8|;DZkf<lo-;^IeTD)SkmJTkF?%SlZ^vx4(ihD}2^DtVx%*`@
    z_?ZLiKE48o6I<r0_RR$RYAvkq#4UsPKtrw7d`ja+i?Bg-^Lw7QLi0ch%qAwOrBQ))
    zRHEzKwyxXHIx-6zHN*S>aR%K0%CSscn-()qG-S&~ez}g%Ag$d!DRm50Gd8jR+6!Ra
    zfIo#IZIBMz5bN4<f+gH*!Y+OmGj}z2Th}{E3ee8!nTu{ZCnHc*<%5>p++mN@)I4`-
    zANw{@KcNsbuL6V9s0OxBx;5O^(FF#HWmk2abbg|fNzMol+f*^@A{Wk)2;)&f=TY9B
    z{>{RQNZ6!yxGPs~C<OVj4-PQ|YjjVDB~zAHOC#Bu5q3P1)7~fsyoOK#4T$k)J@N@J
    zo&ZhGDv3|GZG-}ejfJ|2EC+P}iKXzet#^a9j0i4L9o4F`h6wy;&>m-0s?#Ox`iCc3
    zUC2O`cPnmE`v71bz`jGSmHbxj<ZK=yek2i`r`Nbb7!*)-fkf5Uc!uB7%t4_Tn<iH-
    zHv-e0r!2$g0V$j{#ceDrd(uk=8s2NxM}MbL<RF@fxtWc(KxAG$z%p^ph9=ne1gx`$
    z9aggQJ>-mLu<9S`lWkv9ti8lAfz6a^=6k-pEzLsOXR)=sIDZ?NlQ=+>--JPCglfBq
    zb{Q3Rv(0|r2LT+iGgL^&q2m4Rah~5l-)s?HuBvf+A8ryIkS{TJ@s(hO*~W`M)>g>-
    zp;4pz$iK{9tdC(DqO2Ui(`sK?e~&Fo%*lEsN$?Di2PQwgke2^O2C7$+lyjk**S2ec
    ze$!#x=fcAyU-}Lr?c4?S`H)IFfijR$kUdLH7s#p2G=W>r<W8i|yzYh0#h&ar!TlO$
    z6(bCk?L2KHYwAq;)PBceR*4eEPL;sEjbKkhlM<JcqG>lu>sk`DT&TL9Pm)BRgVudJ
    zTHiY=RsHYIeeJz~4;k3fW1q4Z{^rp28Ulu^>8p8BpgWJjI3p9MIZ|tkrJ8B^g9J-7
    zW{T1XY5f5*5qRbU=K;rrFCCrlqn|<%3I{B>3AKZ0Mx%qo_q1*?*nr%BQaSQ)P7!Ch
    z@5NezY*bFWknxN9R{O)243#rmnrfOpipl5U2E#P0iKitPgf?WcjZQXykjz%+87c-X
    ze-9Qx>>hH(iJdXcs0T8_NLjxpjgW8uC=GNL(@ay@#Z5GUCBvd*lHwD)pJ^Nysej85
    zZ^G}|^+GFD{Rpq|kw%`!pX+Pd*Hh@I@JbjVxwoIsDF{O+!X*-=rvR^_<%aLhDZACh
    zRCH>@M}=>vAdr_U@cKT$$T}$pq0&Y^hXb7FYg(2$GHX}?!l}ck0Htjl9Y+@LxK0G0
    zgQ;+_-^54@ro<I4xn1k8s?b^>jl}QnjZ5)qf9eADs4E@T)`8~6$j*@`1ZSc9<)~k)
    zN==B#WSBUjQyhJ(a3%J)4ei-(vwtqQaRtxCSpF&4?tbrdZ!k`M5$?lhU8bvFNF^eW
    zJn)n6h{XCixVwNC258L`n+2zI-{gR!8zn8``W}p4KN5Q3J9Rg;Od%m1r6B@|6CY)>
    zM9F%VkJs)}A_Xc)>0C875)1>+sV+ZiLqe=@a?XP+q2*09BJ!hnK$;?rkiE#FFqhGf
    zy9^XTt-i1gFCQVq<a@#8;B8CSW9@_WR>%0gIyJbc`c6l)qgzE&l;;!zTj!G86aYvr
    zZ5%Nb>n`DMXZF|&w17OrKbN`ESs+@wG(l%VZrFfZ-Q=z6%RD-A<Ls`X1Rt;DPExiz
    z=Bo&3G0{KPdX`u1q8#tKsVZ8h!`L!9!!@miuK}_XTfd8WG+IP_F<hJEsf@%X!1Fo~
    za<ouKrr62bIv=56m0U9mo3t$`wJdIc{(c72Io_=A(P(Wl`O0K#4(*wySl297JIF2T
    z;C%|)8Bxu+b~}w#l%qf?x(@c@GMyaag@&9m5>T6gPuE*a{e~fT%}2pc7&ifDN$$5{
    z-yWXN22YU40j^>-+^j3T9tIbr?+*Qxx~^Jno_LWVCa#;Q$6jblBpN)GIc$DvJ;#N1
    zgI`&<Oz42;(KsDg`h48l%~%JNu)qA9Ly)L;<Ik0qojnrF6uGetl*Nh%k6GhBKf>9t
    zb;;z{Fo|)BH#wriX?1a89sRo;yKDr^AJ!_NU)+5}2@0|oKmN)eBXud?H}RW~ZkWSt
    zRzlAJjji@Kbp9&}jd=?r*#dJGE5}IR?g^{+)muq7Y2u8=k5i*(V_;2X<~-Nu%Liv)
    z6D#a%_RQ=F`Xp?v6be`>S{e9WM(`Q-j-%EVZ_uuKKuH7i>fGuMKhfKWG)o0+#ou{U
    zHm?A7fW%_vQpB+W^^|ipVohsQI3hp;Y(tldR4`pN9!OMnDI7SYfI85CkL+0w-iM$I
    z+}7e%(S`9fBsIz{e*P`?Tl~4@HSuR|U$EuzgC`n(^B)<qXPv9B$vPE!L1>ZP99TOQ
    zaMsbcP=Ux!BVRALQQ2L7*X;~volP|{df=p~j#Kj+%Etff!i4PRh(Kn`BHJ^e-3AW7
    zXFq5d29~E_QD*l(qk#+r1@zIA<a~PBkH<*k0Gk86*7r)<`Plq~61jLo((fC5Qt?n2
    zFKA?;$Jzsnyo$f4hHJf&?lqb2(3cHMe&{HHLq)qWJa$S}dsw3gtluw+7N9S0qKwsa
    zJ53c3a3QcTC%{%{_0zB}maq)3q34?%Z&C%<a7F$6z`Dfi>_xH%jbh+O<96;Xxp9iX
    zN?0M4AU&?*&nw-l%@O*{4C4u%>hlKB9dKEo&Fn>o&Qz*EqyZV3XjeZ5Zb2nR=0h;<
    z6~WL$6$OiqTM<3RxkJtcGqFX%_?Io?_0Q%-TQg^PVOHxazF?a(UoYyXGd|h3Pt#@z
    z<i9h{*|$owzAr6yKlybQQN<l5>_c4vXW08;eS~Y_r1<cRFr{k&QDk8ViV_`_{S+l=
    z0f8?+paQ=gJ#iK>!w_6sea2=l+2^v;?^+`?u-$vzf=o<&&UYx}oNz~hIQ`qU>qU*i
    zV7a^tg3Uk<QTVCLjwxYaU5)bl6pKWbr*2$IUKj26b}QW-_sBpvQ8ZX=KsEt)z|0to
    zYlw^zR$0du+Gajfc4(jcH|xIj&%8(U>po%_sU@f*vTD&0sQrQrxmn<YV!d^t=y&~b
    zFP#FJl6wrY&LplsUQlGW1xh8^U{KeZ;Ua0Ubjt{{vM=EU{x;B@#S&48y^y0`GV-}V
    zw4J2SPR=IqX7%*OVsLGNg6@k-oapbXD}0etnhqA=tQBa0EIHF&Y;M?AsD4Wn$^3<E
    zmE|VEPzX(%SwZ@1(NgK{(^y%N)uvt&kSmG{d|{OYH3-=ePr+tXiQwDaoRvDVo)qN6
    zY90FRHYa-ca#&FaQp*vkh^nu2ABQKJC8F|IwX0nu;k?cmEHoPo8w+EyQ<6Bzfys3(
    zLLQ68R9R2bA`yGz?*Vk|dIz~G!Nqd%0S#d>Z|H#2>4eQ{BTkK+4JiX;wbkPipvAs~
    z_@m)*vrB3h8Aym?8Q*2_?>sAM&2_D9!25uSo*S~_sTUto)lWXNM5<LSeP|QjaY)Oy
    zI`QrrnLGzuj%u0!vRG?=yW)yIr|(#5vTs(f2=wK_$*(f4C^(T8wQgnEb60>mw$fOc
    z(ki0i^5+m*CeF6#_fjS}?G5n*RO#Iuatcx=Z-z?GIFqA=4>>8ntv_ltNd^GjJwTC)
    z%4)564EtGncq)b~eE&_?8$_!KCFIM=irI+y-=xC6)G7|QseMd<b~-ZAp}=4?7K%;M
    zERsoJ17##7M1B24V6PKtOV{pVgzIw9vQuko^PAnJVF8$92)*D<#`5|D0I@a>5f*+z
    z3wNWe2>4lu9SYjnp$)&l|Dq2vl_g~V-I5p&AP&?;a(Kj3AtP3*r$DA%K(4pZF~eNe
    zf;``VVEyh-dbi@*Re>Erwas~3z}&m<2Uz)*{}8{6I1YJ>I2M|NoDi?IT;|3}*uC_C
    zi9hD;jp_ie@1c?#C4O8a{_^WW_!?eE6yId<z3;l~_SKVwwZ1miGX>Z0r_Tp)yC}w_
    zo+}{dBDUh7cfwOFP7t$*)Q*;wQ&cc^e;uOjnP}asKpj<$L#DQ~ybhKI;90n<r|_%q
    zLJRWnAaBYjx~n;6!u+@441X;01E{-Y<jXDFw`r`WgUSJ0x3=%3S<@zuem=Sec_zyZ
    z(D{I~8J5@_<fYe+@b^y#*Y4)C{kxuHDN_iItelRoPVtQyijj-!8LX@CZ0<$uw%@`h
    z;%Rs<Ti#do;t^8ueVY#2X=nW{%n3d>9~^onl4bi)HyRfCp7P7c7v+)&x#X9|bu0_7
    z3crxXfCuYh>UXN@T2FDIfo<67Bkia_*c}b{ju5Q{k-g^}@$B3u6M!2dkGb$+B_ueX
    zC?Qj2fL$eNK-_ds4mp%;s8R<qi#slPZBkvGk3X-A2MG4T!o(W=d{QCK5V2Yy>+jnt
    zf}l12=6;{?j=-~F{YO%AjH03&NZ!n4<9^3>&&VbY)7l0GX(-S5WBGl;r=W#hg#}-`
    zzK_^^@vUraq2pS$&+X5-(gA3godpBNuCbUqOxt^$g&z&!0kq(F{d(yEk>XL5eoz+C
    zwKORg&^5xkZ=8`;=rZBM%_dcn^Ewg8H*;2fhEd?Ix1~A}HZ8+tJE(=z5#LSa;zHJ#
    zO~x*GMd*c<Ufa<(V|s~^6GIj@m4{H*%xxS#p%xzw6m-s*x3hCbs(8zuVmWrSrCen&
    zhSgEe4&_Ck30KXt$W|lEGSk`8;Zxnuin1jZyZOU0Qmm!IJOms0ZLQ3FtrP7Xi&3e_
    zce3aX4Fp83JlD{L4X1h&WeO8Bq@i8=S{M@J?6cuAm^d9_ZJ@w)?L9{W%*|1-O4(Do
    z^qfTeCY4+j?noH;Bsfx&?*-NtQyB_@Fr{AX_Q}SZE15;<PK9)ew{R4-e0n=z6Rie)
    zNMhabb(&_VKa(NROj=<ZkLqXplJX=E@}8xspgSmglHM<(aNzq>MqsqQw-z8rqc9o%
    zdrA;oR5=@36ghWDaWf3E5FnrL>=uh^3$7hmxnZ4{=mToQkp8V0=R4){1jS>FFVSvy
    zaGd@&w+un#!BG{B_0vb7RE(B3SrxagBWIwHbs(=toM>@pKwTE^CY+M!Vt5+*m^vd0
    zw#Zv26{(zChs!WHSCgdAdA5hn0ktHU`eTWdJiSK1>Ld+HIHeOH%266U_g9Qs(28cr
    zWodMcPtKP{+FxJA{tzFhq3R(Qc1?_6TJf30Ue7|;V$7lYQrY9`2Cak?hYE&oJok(j
    z;CIsU!zWj<%jXWQAF*+hlbkwRVGv}CPfgmG!i<&H*x}6&UrB8o`J!P&3ujSwPX_JN
    zW^d<|7bxN{@}s}%-8@d+KyuY(<qFL*$iTx$jQSmIHPP@*fHjuj>(uwU5Zh6XRGtal
    zKVm<J=(ga}K}~o{Nq!^${wcVZAW=RUhI3QNL5mRKfMjSD#sb@;cY4af+Inl6>i4Of
    z1HTw_>ir6StKmswam4LcN4emxe!Ymy3P50L=AjD6bz4J*9n|^6#6OE$A5i6BWzVYz
    z8&1A8+3P@i9aDaxX<?;RxG-4}oqnEbdVTC3+WeM(j#18cH>AB+?NGN5kvK?@7QCNP
    zi5ic=+L5>l>uc#EeT-$kAxKR$B-a*=U%vT(&4Ssp6%4ECi5}0=rA8mx&8PZzYeHZQ
    zZX{0&Wbt8hiF>X^{P7~v%V_S)(#_4AOqfZ;f$kPM?u*Z^l=y_&v+m7j&xu7|-Yj8D
    zM+)Bl_RGr4i}Y83#NDrla&Af2_G3SNV=&G4<>o#8W6ocfpb3_Tmy-gyKi_y>KCWvg
    z$zoj;340{u?)h~I0fkJFFU?n;8twhv<f;2w{Ox4y+g4Qa1!LAY_(5d$s~x{L&H4eY
    zD}Q*PmNmuhC?mC9ztcR*+a%55JbB#4&t@n#yk$pzjc-zVC~S;Lw<gV;@(sm+@vtmJ
    zZp!$ZsYdJe3gF6@MqEYmW-K`M${~ONY;z~kTIayF=A9>5@lp7=XT`t$YDCq4pe}|!
    z__7@<Xl}T#y(01$&O&T8<T_4)jccytb~obxcQp;Rp)FA4ogyt{tVk2T7L4m59n<PY
    zmQr454)K1<zl=|Y(W56x916>qE85u@k8R1Qd1-?(A+b@OO0%)yJ#&#>3%QU5B*2)H
    zf9{;;NQ}TNVs|b8p)_gX7kAXo1w=Dc!oJPrLXX}6{cuiUi)XpWHRuD~QTwd3!=*C`
    zSI%&SH2OZz!I?n3OBQ;JDO0IvmkOq`C9(dl+n3JPjEno8?*ga!H<{<wzbE#N!|eOZ
    zC$5Wi8%UrqieRZt&V*k#@D5Jp&94l@%eCRsn*0#M2ku082MQ_u?1l;_7yKn5g{$LY
    z4zG6A#DIJ&d5b^sHsazW@I6VTOS1Ia!&FgmJ9-x_s6;^~`Va=LfV9rr>j!0LNdp~t
    zYHom<c<AR3I44)axt{l-XhYh#yQ^-krshFw=!TGzNn;fk>%_s2fa)#>82>zaRgso7
    zzhw(()_5gX_do=RCM&5>;$qEPV_kZ{zDyqyt|e5eVw-4DrMX@;D&KQORJnc`Y!99#
    z*l4O}I34o-%!)M;#R<^^H6v_f@-Oi^Dwz!nRxNkSL>^*^g3j*}&CTs#VY12TTJNqu
    zT~F)pi8PH*KSHh_or7(}<UdO@-HETaxspx3bqx)q?oj*R_r>9N6XcRK?mw56Q(47L
    zUuWgoB*6-sCR$F6S0qKjAizc>h|d0!&YA#eS<k)^g;RUyMm>jZK3h2Nh!h2MRd=50
    zQp6hQ4}y%Ecy$ARL&ooX_0P@Y{V@&{_J7BzIkbr%#f6^j0_D&~Ujj}S4%+$O@rmNn
    z{3{w-doAqteOq{kCb1L3C5+Ln3W#ldW}sfY(n3c7?`K5Y-N=As*5^}&&F-06zDb7n
    zk0+ORu)9Z%oo|mf^*h_=fq&Qc$%I{5ZA=gGdGDhpc`%sJcJ9<Mbp=H;C~KK%+_ZKl
    z<`7CZiK$iEjZG#O%BM!zmDSaITH=Mr*vC_=mbf4JkQ9LmeeYMR>yz`ao8yMXxV!5z
    z*tn}JI_!LbE?Ocbr$|+K>^1T7PoKVYy2NJsHt{`IqDq?aXqTMUpK@^a=rDmZqDO{C
    z*GTv7y;|5l?`vk&^d()5O?X5a8APV79)T+CeJwQK%66!8hciDYTv*S*v*g@Fn)26H
    zY7<R!cInxroi+YET{Hi_Y8&EEIC0|Z)id)ET{GY3E2E;XBKv){mF=JB|8T}%F>CIF
    zYcVFHqtAYA8Adad&C88q#cz5?d^ML-QzKWz9&Pjl+d225THy{OCZfH+DWgU2vI_w%
    ztYg5z#kI64)2AlxwJ^6!!B!$Cob?z%gzQ52Ezof=xpT>Uh!kRm#n2=6R1sjQ=crvm
    zZVtu`w1d5RZO60NtP@MMvc4(DUrE$b059ADw9y~a{u+6Z`X$@q?pWu7IA9HL(r5Rw
    z^1uKHYPRJ_4^K16CbP)hQ*H?esID^Fl%Uoigo87^*7rX^a!4I^ixMm`Y=CWmpYj^7
    z+?In61eSo=W)bELzMf%`F&Z5<<+6bz&C!Dy=VcdI=7u=GcO>!@`i?AyNiqN3;r^EV
    zON*YC>coMumkU_joVrsnhr7Y+T1-aNl;}o9Mk(I8^yfwSZJu)2zUFHY`SD5Y>6($v
    zToV-Urr`s%{)25*k|e$DLa<`#=b_~M7t$47XzCy~q?-pwQmLbW-UrZoABDZ?*eaB_
    z#B44_3rJH_Q(N?mbF@>)Lq7g37ZQ`&6?^Pbc7QKPk)Z`E`!$=2yJ--xg@&;`gFMJX
    z;kyiqJlh-4Z~KW0?DA}9VI$_&;q7NEVV<!m5#r$=UU)Jln;;Yy+(Up&vfH;7tMWt%
    z2S$+BheMd|rT*5haWpuoOcxF0XvXXq*xxTmnxNt=^q3M~2y=$6{H_Y~^_Qndob=h6
    zwp;W{qHSGLPd?6y*lrTX8n=&@YW8^Mar1Ekid^d}!Pn1dkwMkBIpLJj&15qMGE==F
    zY^&;6mJ7rqlyr9XACyv?vsyTcb{5;kVx2-ba~T0#Jeu_c^BoO=1AbPeS%MBxpgjk;
    zKU^=F3665+<kszE;y#;e>M1ZH<<J}YJc(A_jhuo>0YAsKb`!h|lUUqBU&Bqm#3*CJ
    zs;?R;JuexRk2vvN5ITs$5B6RD=*3bmU4C8NQVov@3JU%{4gP>a%lN)|_8O0P&Be2Q
    zVGr{F`q2WzT+J1O!w*&BL{dBaw&;zwnngj-_cW5Yi!K{<veVU<y!yg0J3=Y`r|mFH
    zM?3IXOwv!l!s7<;yb%uXx&5-{06(&<Hlror_A8mwB@1%obN#`Oa1cDA`@7rS$+LA_
    z4(B^q=Y|hg%AT^lSqVR)j}VtY-fRvybHG+-OnuuonFJAVd4<rtrqQkB{&Dv9^{>BP
    zc16x=u$op71Hu8ufciK(h{-Rj0rH!*e3V6z))?Muy2Z;?V<9OM3vj5-TBQ)g(8;>`
    zE<u@hn;>KKHC$!*ciS$Lp!#OXl~BRq23bFq%xM}1v<zfdB=@F<%Jhubd1<^Z_s|?m
    z3eLbh3MH^Z6HZA)-CYh$19XI^AU;Zr%6JxNXf1iP@+<!w6oh%>&iPpWD%EdE0n>%m
    zmxi^X$N03;CE&%hBliW{?R20W{<y29hCo^tB%wBeH!V$IsbhFMcOzWW50?*nvYGp=
    z6$f%@@@=qbJEYmMR)ywqDuT%cKS3*$oiS0P#>zs<8ij8s7lGAU&Q0-~uvUf(wMV$+
    zv)NA)toZgQ4pBxm#E`WdR2_&7FDUAI57d=g`w!W<53bjcH{vdwKc6W?n?JE6D<9Nu
    zZI`k_Mbasnp;a^~Tu$Vk2{Ca;NCY&tMW|ugPH2vX57Xh%>P5IU%I7F^i~AkFVt(@E
    zo_Io3aW$!Rb}I!>lfp%>MWeK<B&i&el38oaVm+G_s9`B6(@;HMVpj2Zb=PeK7@ia;
    zEX!XG9a6&rDEBIo5gODOjhaRXIvh5Qkj3T40Qwk7aa5?6j=EAS!ZKLeTw6qH*0R)r
    z^5(Q})2BgBY+FWagg7u>jNwtC1AphJKqne^qoX+l{Y?2@*oiZ^oojrdaSR1##Or!N
    zs$wlday(<^f)NQwoTV#>#3LLy-{XY_xLKGJzi*EbDGt9yGutu;e3k*tf*4o#%NLVG
    z4g6V`KiE11yp3kMg-ffU(VEep1UA&jyF9Scyez)~MjIqvUZ*L_A{v#-O@uAKM-|kn
    zY|Hygc%kJYtJ8fll@YYQYEjEiJ6Wf``&k$0x25Fw8S)Cy1u9t0qAZV77<OFrG;tR&
    zo0-Mh8YN1&Tq90Zd9hgh;7|}wcGl;kDA5J2hHs-sp^n=>uqLw`V)5xza<-cn3^4u#
    zEm4OC@!a(wSj#zU33Ay@4(hQP{M?;$R}5|8%>Ido)}f`;&+09(HtwXs=?7rCjXir@
    zjN<2z@ZqExeR0V7%AQcv)De4Rx5&n;I%4~KX!`O4G$QFGr>NXn2ggcqzLoL`<Wm|j
    z(mR|2aB>W~m=pEc$spTT%sr7ZQ7+67j?=5Ub%~cs1*gG%*Pmi~OXBb7;`jk_`{kcP
    z!vy=dNMrsR#uL0&B^07e{TWH^7%m1z`d6@OjjAdeulew{m9o7siJJcBhn-NnShW%t
    z2|pLw_o>lk(aSpWBb7;U6P_~c=pULPb_Gm>JWAHnEHb={Ji9#DXsOmkXq(cVq;=ZJ
    z#8-BEuTjwLHnYhMqm_M_g%;z|lq&UkKmL5Hfavv*74ZR$fcT-Fop?90vxy=nPdroK
    zs;8CBoS%Z6MQ|CXKPHO$7d%YTZ?=--IybmvZszGyY3Sr_nrKQ`M)_pkoAMuS4NbEQ
    zZ$=5-n^@y$A~_5&uQv9Gojg5GgM8;X^L+jHrH|L)g&THYsPi*s#5!U!1AWsg(h#cx
    zBJ5Kx^i0zlug2C%D<Nt;ZN?G79h586W<J$yTm*Ob=pgpnIMMutcEK!u!`k9$X_)x+
    z_m)Y>%~oz3dw%t<ubGZFmI93>B9kD?E*^^{TYY`P6G;!M)c`p|?^}%apJq2pfFBNY
    z0!^h%V&y8`y|LYXW}ry=n!+lA?!Lv$@^$3^6Wawpw3arazBnCJ_ULOb+<X@P93>iJ
    zv_y-va%lPhIT6~%;`TezI-1e#%Xur%J6lHO_Ie3Wx({g=?~4{YoAJ^JX{GO;sF%%B
    zo*XYt-DRkkbJU{t#P6gPrmLdIv9^2JC61Q4j^g+pRJLS*WrwG7Pb|o#BT|*djgYV;
    zbJDZ7jj?Ba&~jEYGgB_iG8&>)!RZ&QO5Kr$b}i!4y?4dZhZwk@h<0Wb<IcsIxv@WT
    zo^)b^SM`jVGcHP5+c(_8Kz#9&&$BbO7<>9(0c#1A_HoW4&pn4wG2vBvOE%sL-9q7?
    z+v~6St%@CK5UDl-*m2}Fhg9!ytgAPN4)H6QPl1@kU=<RhmSwX1zI0V*aGneEo5_tk
    zPeKsqDoVVnNrDee^0C&^^m2;9B|T+s&>DpqJf>&Epgbmh2hi3avsaTnQXw$6y}aU3
    zwHv4i{%-6uh$z$)PeMQrn7cLNsa;OZQ!lMaKG(C(CCUYDPxI34N>t<E=wp%_x!Io0
    z*#lk>PFrhh9F4Qt@e{osnVHH)M2s*Bz1gKb>M^nY=d}};<<s9=5x;pnI#4#s<@KgW
    z*~<>=8MUMUqftCf?w8ur5SjLz`T4Vh^kyYyQOD$LCRPTnG!-4f9VHrD=Wd~Ur=aNb
    zAd*8ca@24Ok%c3DR;E>_LPbjnal%;yRQCY#sx~Q1!lMU<9|0sRnwp?Z%7Ongy{Ig~
    zYbFLwU>30kID4g!I~UU6>1gpp$DYjbiVOUxpEFUL4hX1fRZ=mVv&&`s>2aU&Y&*wi
    zfiQQHx4-BLX@fR_UZh7`xu1)d{EmA0@U{n7!d);<A=sasvoJK%42YYsV50`iRhTSn
    zHMa&p+HFNILc{JV!A*4bK<iYpO%J$-<xP{^PC-#^z$H^|4qu>RjrNKF00(tRL_t&+
    zEyjmAdD0|R7Pp;#I}vHqK|eSi*<tp|aHO@kkf2nvtsZHU`OJ3la<lY1X*xfxr=N~b
    zv>gGhO|2Q=y002R_+L|HG4awkI{y5Apv(1%V4wlu@fwWKr#dks5w*qf#C)(8aXH2y
    z;OKL-{f{_|Knsg>8QFpW#MA@!2dZ*KNC&X@+|YQ0XfDdaequs+4hMbi2CkZPJ38Py
    z$K)rG-YXTm8toSdwP<J#Lz90Q#N=cJCbqQ*H6ESyeAo;*60LGSmr#txSqNTjVYQu7
    z3(8r)&pjO6-v8OT0jJjc+1!bo3~5gZ##J~eA+vH?yMs$PJKry|7dIyI$UkEG{mDDJ
    zmt8Fp*@FF9{X4GrEJ4@ZnCq7a$=er!BXz3tZ@6x6L?vn&4C0weB(P<65?j}|IF=Hg
    z;Cs80O5|4Vt?KuamWY&lTmQ_V006UopAx>^b*qXp-={Xuv(abcSk(fkK#{89#-qxW
    z_6FDjf*|-adh!eqB_P~^6~IrIUhRRSIT*b+I?O}fjXPV<TjDl)VLKG%=1$l3RB5Mv
    zKJ)X}y?S7d%nyA!2J<h)BooZM8m#6~$@whs#NkeWQ({lP@-xsh+T#SX6J>@w(+k}1
    z5ee6EC5O5Adk*7-3QiceJD*2s6|hG9rQc-U%aNl6ZWvDA`SiEyJvt+f>$i8$H@~Pq
    zf0KUt-tT`YCNVIzWE+qURunmAjMD3HhW*2>nuA*Wc{JUOX6n1ONR_sLWMo4~+5Eyv
    zB`7>{vwAy|ZV|ocz#I6ik>=daU7iL?P!?j=w*c)$I9++jnE_DoACUvbU5~>QoU`V3
    zpqyHwZa7LPyn@4AjrZmGun~}ZTc5`%IiCsH?RXWqt`QCvD7x?La9|XDc9=kq_Rt!a
    z;9(QXP@gXF&9<_9_)}0UL`}z~U2s(F2R-`22<DSU_(X7UzU2NJKdG93LY;bT3!%O6
    z%H(F*gc>+&BE785pW|j-S7$&NvokCLC0J&ar_6qsCFc|n-W`Kav}^w0I$kn2tT@c#
    zpX~lB1AL5$nbZqJWrRNSl6Z<1^S4dU-K3m<Pjzi`V|CjG>lneH3CMt#6nw6ZPfhau
    zV;Qja)*lJ)n7PO%%e1TpnP$yZZpYFd+T#=q?imobK<n20)>u5W#|VmX+}8pU_v^Q6
    zpU6DEfBhwMvmS8G(lulW%64MB(Cnl$x&WpAwM5fzuMXRHZE@srt+P4T=)5BWvuE)I
    zR+j{Ho?!>v*$lp4!6ys<^cRO-uiooD>-?F<D5~GCI<#<`t~s@bA5GlEb+&K&`nt`G
    zHKQYl$KQX_Ag4!(<AKk|uc1lWtRNJtV7{c>+YE>FD@x5Aa(gi6drsiWzBa-E^z=IH
    zHPmk#dz)e9G)0Jr@E*U;E&nu8^{F|zVUo`<%P~?b$SBK=m4<Q96@i|LVr{z9oJjL5
    zYK}2Cc}tk*WWIMzGDW_JPfGstIKxvwJ~N#@^$kt(+4Wz-lQ`0>*;edZAbXFb!_(<P
    zc-q;Y&68lF)oE*gXc?cc=bt#`@O0{AsedTYO77{eakNITn=5<Lb1)vorv?dhg5N2{
    z;F!|{e(l|$|DN?%BlM5HvJ6i<q5g`;3+Coa<>&ZXF3T$B`z*ftOtbUx_1T6b_Xn1a
    z$=b2T$4{TA>GShPYY&$SHyM3SX7kmtPhrZZzW|TLW*rv-l%QmSAM>&{l{0UtbvO5V
    zp2y?yJPUsRcEc2XZ5;6Uyw#77pDlTr>w7;wxqdrdae;p@B7ew7{0JKU=<(lw{&Nh-
    zFTMWF-up&bo;|UXvpLK9%=3KjnSW2|epvdy^ZocGWZK_;{NNo=GGvuJC%!La@e4Zn
    zZ#mkWwq_ck0Bmk-iB16;O!EEbf4_eGLH&OyCO;H{UqMJRzJE{h``2F`DW5ekaOV{M
    zmgm2|?)Y2JH-J7Kd450N|HK5}u){ZwzqfurVpM-9BtPgsydXutg1LF^_)L|*_W1ww
    zk@Ve;;-M)<Hcq(u{o{v1@}si-;QFsn|MjCA9__b%<H#Rkf*&1s!FUcp`cWh1kIMI1
    zD8er|p4Gjl3VbXcjOyvS`}mQ$`8%zNmNfp&58w|h1%6RH$+uqe_e$!MP&{K2{y<88
    zgh}#;=IHyEpIz{0lVY#`9>{al;RizcW61aY0{*Cfeti52nB<2x@fQ^5v*UZ=i~mT6
    zd7S?K)GjCbI2qR;9)BmM`1_6h-)qmmzTvAX<)P1cOJVe%a%BF!_T!(&uYR0NOMW)u
    zNj`Vnf0m&9<ExwhpB@&H6j}6D=VxY~f};C=Z9(z-f5k`s^Z2z6G3B2Nhz=ft0iZ$K
    zuQl>N)nNZT{&9{UBHPanmTCe1enI(>fd2FN2RQyFApw3BLHVbE{7-WH%|arq0{rdR
    zgmRDnX>0z$kH1Msegy&X@lQedpWsjw|93kob^YW^fM3tM`KQJC2S0B7$2WMA7Ldck
    z`A_3X{z<0&;~giNMt&09@JXlrXLaHcZ}!jQAL;lXMfpj(gC_h>00000NkvXXu0mjf
    D5}s30
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsv-sprite.gif b/mapbox-gl/closure-library/closure/goog/images/hsv-sprite.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..c6a62bae567c211708c7f6e8050dcbbb70f279be
    GIT binary patch
    literal 33309
    zcmV)PK()U|Nk%w1VG{uh0rvm^2nY!O4-yd*{{QO~5&ska58@8t{|o=W{<ajf6aTXR
    z6r`l2>+${m5}FjF|NqR)!r%Yq=H?RrjmH17g%JOAbaelT3;(sCsjmP3rq-C=|L*^|
    zg#Qi{cK@CKw5tD={}gfmjgWWV{r_*^6#rsQPEP->hPQzKjhX-c@QscCf&O3@T^avw
    z|Gu>Uae#gP|9^J>W|?9B5m(*+|5x5;r2qf5V76fY?Eh%Y%*_51PJ{nwIyyRN|BDe&
    z|LM#B%m3+MbN{ygPXF)n9R5Q8N}*5xzl2u*|6p`x|7QQzs<%V_$p72cs{cx;|2*OU
    z|G7i+qW=GePyagNrvK~s|4RKr7ef>OOaGCG|4aVs+(Z8xeg5kI)A#ms|Nmc8(fI#h
    zQvd%mnL~H~*!~qIB_-Pbmh1lbi;Ii@j=cWbhS!GwGylKc|G@wBvY!9m{z7v9|Nqz<
    z{W7foLH=ZjI{*LI9rG6d|KH#h|McG8|Bc1}{~IzLG5_*b|GBxj|F^|o|3g*(^8Z9j
    z|Nk?&#sB}O_qF2x|A;X{PyUJjGIamm-v9D5|C!AH%=6y=*8gyw|JL;XiHyDf%>S7!
    z{v7^kz5mo<)nWht``wNIrT@-{LjObmw!QxrzyHhc|EKG=|1<w)Gt%Gx|C(~w|4zlO
    z|L*_OW;6esegAy-{w;R@g#N?-jpC#C|7XGfI{)>V|IEk#!o>fRUteGJI{*Lk#u)$K
    zj<o;(j;#JA<^R^g*24d_EdJWs+O(Aa_VWMpwg3N0!T!R8jQ*S^<Nt^x{@X(T|IGjY
    zxGYWo+06g{85tS>lywro|NpeH|Jp-~-v0Ri`2Q_*rvLjb|6~91|C4_I-T&JE@@M`k
    z5-t<|x~~8K%i{hqzM21Ksp0=}|J4%yEoc9wUyT3%Rrmja;+tCk@&6M4K{vYpmG%Fe
    z{|~li|1<CZ;urs<K{EeY_Wd3H!ixSOO2z;GE=~1`|JMIBegFM1P5+Ll|NoAl{)irp
    z|Bx{M-v9sqA^8LW2LS&7EC2ui022WV0RRa900RgVsI6c@D+pI8Txem1g@+IkSU^A#
    zfgJ<`><rjA0G-E=WYCcrsi34u1sqUvSjj;~OBpb?$lOw?CV@%>a_ZbEuBSL6KuHiG
    zL6n39qY{!zc*pd>y8}=U+>&}Lk5#J%@)+1^z*$#kU>%4Z+rdGHvmJb>UF)NdTLC@<
    z$el}fn}BV2ujswPqAv>ye<=tSoS?Ab1ac6|k#nc9;{taING{;106S0YGB;q?pfiXD
    zAVP=M0YZmq9eh#;;4^UGkT|eo%bp#(wYC5UwIQ^!g15qk5F<{kc+rk=jvGIM97#q&
    zbCW4qN{1O!hRjMdZSLGoz~@hKB)}K{C3;lpyQNK|PQAtc>Q$~>!G?7<)~s2yKF+dj
    z8#gZ9|8n7#mtHLJ)fZrc4VC}}hZVNigN{WO*<_NjV_9aJb><mpqLp?UYB~fEB5SSD
    zW+H8^;bxE(x+S!bL%}^HQAHG8l+leHb>vY<%r*BMbkVKk5_Q#KR})S+ahH=g-F+9-
    zct#<W6jMz-rQQK~ROMb)@pbhTef5=vmVRli)klAF^#NdjcMZ6gUk3)pAYq3k_8?=7
    zJvQNFmtmHnhM;j+nun*Q#u|yOS##o|aiq9ni@m+*5R5WTG+d1sVU(PX$}#8MNhJlz
    z(va27BohH!D7j>lJpuLPc;!w1iJp~Et=FD=Sam6%17MPMADR1|>6V&n&L!YBZ}N2o
    zf(a7FV4aFB7NLX`TIh+Ne+Js&poOBw$A~F1x~Od`w&)^5lnO_qMVe~Fk#Zu5MADC;
    zsx%#uTUeK!O-XX+9TDISH57SLnpb61SW<<nR$9d;U$4H7nO2!<4XY-xYue?efqpTo
    z;Inn!IW4u-UW*}TfPS{^hu$8#T5GR~iy}egvbdXXlcp=trI-$2(Yu^3=P7idO4rg$
    z)R}r@zuPhCU3fx48KtaL(kc~ITH*?ySGz(i=9u~IrxsiSU<{yuS0Jlw$8sjPV1stz
    zIWmP6Qg$+++KNWnhu?nx`*Mjg&rI`*yG8n2x|XiX?ndqgy`#K7o)n$ELK1163`X*+
    zs!ssB8a2U6$?DYTwPLMx!@ABF=C8oAb!OSHpWUX~1j6a@oP~iLx3qaS1{vL#os4qI
    zD-&v{YKR69xZt-vnh?&v#h5sa#WjwO<B*dCs;ELDeff3!D(N)m0DlfEQs<pVx_azo
    zy=Ch3dCmI7u8$p;+5a7zEZfaG3wN}=^WC26hONzLNM(ZB8{bg#w}D7Q8o+p91R*HF
    z3R2KEgex4nfTONURj+Z_<I%jfN4f4LO<&<-8c#SEFet^!JQ5h4)z*`x_o41fVX2RO
    zBo;O^wdF0v(%8oT;MBH{H3)zK6kxS1bD?bIO>YY{R09Jr1PN9Vf>n&*1c7nIfiy3J
    zhC5tvcBVL=9S1q^g50Nwh7!t=Drw+bn!i}p6Q~WWC@)OP0b)2q(=BXkU*eSxzlI-*
    z@#l5;s|(r)q$~s#@ooR(-L!tUMB(90WgSwWY5Jy`)-YfQXQ<*Qr2)z+4q=p9#9(fE
    z13e2^L3%SPqeiR;FA+vgawYU$ky0qWCUp*Kctnbn)Yr$U)No5T^itO%Hb1c;l5F{#
    zUE1EnKeyq|FuRjmgy=?@COT12dLvZJM3kaZhEf`z45d2F2}&=j$YvVcV2ol}5ue#}
    zQ)%oGz1qnCy`!B_X-AslzdYx{g2j-J2(y*@-UB~Du8xO>6xOhcH9OiB$aejMB(x;C
    z%>h=%WSSWs0$0|p5P@qTp&Vs8OS#gOdeI=}g5IP!m^hwQ4>=)J<39W8&wtV+JDVe)
    zzzQ0=^vMumd63F6v69S(L5wUCqn{6<i9fsuV44=KX0win#JLTSiIz+mIJvdJmm%tu
    z?EGX)St^aeWso<%X$az`N4-n|EuTFCRlSVny?g-_N&ae89a{*xf>CLGS8LxNZ^$*(
    ziA7AT>tV(CQ?^73ahi<Osu8h?7{%NniC_IFNS%nu(TLM;Cl$zASDMnazVskBlN3$I
    zs5s;Q%<`@v(J3yuHzcI_^*RA<8moW;9`WEYDd<Db`V_j*rsD9agv3v<?g!1fNHeo>
    zy4~AA)>)BFGPD=+lL0rhEwX}0iZEk<7Sy`lQ?^&V>~*VdASKh;&WLC1I%AITI>Ox=
    z?Tz32spgu-LZRF-N(Mb_F>i&;tu1qq%{1g8FJ@i+?S*51DdIIbd#5B)QZk|y@9@YQ
    z&L|>pAdEok86p5m7OT}AFJ{1NW7^8!@MDbly(uj_l|8-ka~}7CBj$dJs=x}id{RTM
    z!4!M|c_fy>Oa<h_bhyK)PM2(!U1aT+mEFxAF)^P7Ey+M!nY$g(GbH8P@n9AK2C#Vl
    z#iwL(1VTAyIpd~gF_qLzA97=QV#>BS<!^sWG7pphc~pJXmyuGXLOOcxj^#0|a-ZU4
    z<}S>+4~`hvC?>2Q#*R&Su_(uG=Vb>)>$BgbTbV_C$+2pZlP*(jorln7T1%tW5x6z2
    zWqjwi>KQmUzVSr(QCp1McE3XN)J{tX;NR{GusRuE`3MWTSXGTym2TfcxwI9o;Sj<Q
    zm8?W5Y%FPJwPP85mhJ$E>TRm}TA0Dic%N9}LamuZxt24n;aq1o+d2Zh&Q0N<#}9`H
    zyU#hc$g!`-W8Lo7(4)c^3Nxod7MAb8)n3Z1oZ{q4`%!DfaIj;c8d(XC`_tzC#%_kS
    zyW#Fa$iv_@nJ4B=V$-tBTdAdWzat=LH}~Am<)U#I8&Yr!1pB_!vn>b(tv!0p1IUMN
    z$viCQY&rI8ph1E3)Rz1dO3ULGUsvqu;5YIrgROoR`}DDBhv5Sy$QX3`@~OQ#nGmy@
    zyx`R2c~2YwpUb(;JpVh-R}Ank!WOQQDmXuaZFB?>yROGsI*;?Z&q8wm>Lx5z9HBiv
    zCk^#{MV-|<I^JZgHMe0QS7zBETS&=H7>IXC`P#A@7`P|E7#cP8%Tuk3ZiW~`tu7wR
    zj)y3o>)h`?_czboOpv$AR3X9cGqE54XJez0>1!mk0xS=800;QlI;n*JcYg9{&>tmz
    z3hp@AwY}J_6r-`b0yhtRunHf(1@@P{FcC?Odj{cN_ac)-y;lGO2ygJl86!4UqtPwI
    z_cDPOcseI|4;TT42TzFiB8nG%j;A;ZcWlV^IP<bk+Xr!nCIeHac{3qLOyW!A_j!d?
    zR8`Y)Ul(>z#xNn5dd~2EWoLH!QGaTY4;3>}7_)ZzXFGBNcStCAyVp?!*o1c105f-H
    zrDa}w7i$xEg;-d4Fm*FBm4QYleH-{^fVL5Y&=G{79QUAX*|&X9cX=yuf-unz-<LTz
    z(H%E&WUoR3)v$F*(Fn6552Gg!g1CM)m<Pq66*u@Wa}Yld6>j$bCxo%5Wo_3N#_$(#
    z=YO}iQQkEzx)2$?=Lx#-QNbeyC?f`*F%8925B4TSe4q_k_=-G-8?88B1Gf;0w*rf&
    zfz&64I6{WCM0pAj3ffm_PuG1MAct_2g8afH;^%oTNFLP?I-&=Nf;fZhcNK?-hz1aH
    zIVc~C_)yK{gZ%MB6s9J}0uL%jiIzwKN%(&W&<ifdQH{}wchGzGNErtxZ^0)T^>A7(
    zlNz@Hi?6sE&KE|wh>N*6eZ{79j57db_!0LIFKMVfB_WK&*oIRFhbTyKci0_7Kme|i
    zb$VzN(U=s~fPPd`je~fN?s0?L*o}%vWoPj#VUrJKk`EUDLtz2}4{C#s7ZoR$h=kk)
    zj}Fpd?KY2|=!BtoE#9yhrl=v)z#*tO8q=Tys#t{%D3Dk<i+o^#23c?j8GX~23`rMx
    z8NqPqaE3e5Bcae7!e|oQXN<@who-_y;vjzFSAHWok`hpoqBnYhXpLfrD=ztdI{1xq
    zz)T&m3TIJ&J_v;4_7-;02RoULD~E)**MCH5C#NQ6x?qo>Xn>eOiW*{yARq>zF_oop
    zm8xhc+HfdZiG>vyTLhPoix-9nK$iJ6hGpoG@Dh<D(Uza`mWsfJ#dwTyh=Ra{BzK92
    z0<(vDm`4)O2q_7e)kug{0Si{4orjo+ItUhsITmC8Fh7g=gO6B_JK2*&REg|~nVDD^
    z@R&^kSdaAxnxM!I-XL$L_>>J0m7-CVAkdG6QV+)ykg+*|ulbtrlnw|X3Jc+&8JLg=
    zfCv<!n;(QDYI%l)5E2tf2EzH4#Auwy2xQ1819bUUc<G!%0iDqpjU?cQ(>aZSiJhmn
    zjW=kR&JdoyvI-8snDv(y^#_EInRb+k7d)vKKG_$jppKXccfEjn@ED)H5P+Omp9C0+
    zp_reZ(3I@JpQee5Qz@XT*qW}{iU&G@vS@+4kq!?^bcm3P8%UuRnhZ*39O$4C4+)XX
    zk%kIz4;86>icpN*H+6K`4oZ_9&6$^#;40PsFcd7>mrCJ>??9bQL5+hLcK)Cx+G&V}
    zc%wP!0Pm-xkJY1#S*k#2o_&Cm7BiVfdId=eC+*3O#*mrs2%qoRVR%vtNcow(S^(aF
    zk6VhLow1*v5tUPkietJ-1X`vM;0w+AtUTwe(HeNps-Ue{I1DN&h!B>GmkbJ-n-!{b
    zgn*~Md4>@w5)=8Me|nL}DTi~3Ld#hbciA~1*{DR(qA&WQeOR3;DG!<29{xa>or<Hy
    zu!uS80EsD{X5ph}VUt4odfRX@5t}yX=&EqCq_H}yv)T(35T$l9r5vTJcc80R>Z`uG
    zrA*lwP5G3@3IbqC8p%qgt|_fF3#~Q(E3FA?1zl+c*}4J_`m+`w3bx4)-b$86`$4u<
    zhDr;O=#Ykds;&x9oKI(*?SP!fxCMr~d3TAI^-2VL__ZWCokk(4Ou+_#=?{bWuK~NA
    zoLUTS>$WgS4>?L73Cp8p!I<Ugm=Bwi5!<Th=#%iEq=116vO24oXbe$$t04OzB0I9X
    z`k5x1pOV|94X~g6*`F?(nlNjX|JaIr@T|@nx}ZC{&dQYt!JupUpa*H2$zTEA3a)h8
    zfyU9B8k&|K;k3c&mcdwPQrm`A`@1G;qIJ16T)U`@>a}{<qL3P+lS(CKTa9ST9+}Fv
    z*!Z^H_ypidw_)N0jcKYKu(zuJx~i1PlYpzFf{O|oyRnE{t5Qm(joXR5stdpB4NIAy
    zq-eRO2&OKp1IWs`1iGxR`MJIjx&%zRpqsPWfVyz?v#a~2-KvnuV6^N44dS}A<|>h7
    z@U+1QsDk=*!RxMd`E2uQyvGZ+$}6_b`wq{m!q=&`d9bP6`L=Ld44}G*bC8&cIi7dh
    zy^Q&=`GAw*+qZpi1w;I|7s~?enZB7BrM(cJcTx*^qN|-)3-ODkC>yNzTe+vX#V<Pz
    zG5e3NS-=C_tOH!eW*h-s*@~#krdGhf{2;VK>%ebXv=R&rvTFz9TCTPmk+|EI!uZEg
    zYn;UhyoH*ABD|<1+^ApwTMf$F2+`<=l&Zok?63YRy|)0vR?(d@><rjDw>T^o<7v06
    z(8GG`147ET<GT$;9Jnm7q!^37>HDPb=&@6}xN@MYjVr~x3W}18vSP5Zm3z5h3a0%y
    zrvF=@W=z0iyvzigpy`mnJln=TJI&jgiw<10xjCoeipLIFyXKm~9vaSoEVWTP$W|M?
    zSSu5pa3n-f6YcCOoZz*{%gBu!qm;@962Jz6sLx=RsR0|XFf7Axi@kE|$p{O|q&fzs
    zx(Ys=s%`-fZZWasYq3k($`pVKgd59<3#GIy2gk4&P}~@}OrP;fzrE}Ylq&|7I}I%D
    zvc&Aa$D9tSk<2vzUDLksn$OIjY`W9aEDCVU4{}_zL`#-XK&Qq*!Q8C0wadrg?8h4{
    zjKvwKPp1eb*banhsD{c3%IMCEDh}~1&-Bd5ed);d{0>-9wv&vu{(K7n&9<8Se%RZ|
    zHhjYf&7%rE%A;(`LHfge0MWyu%Bmc=f^EKp3(Fe&q=?(ZiY*6=3&o6q%Trv^zM7?5
    z+LYdazs5Su{A<j7u&e-S)1W=To~zS5y8>+bvv0h3aZI$VZ3l=D!3qZjNsGsXV8MLc
    zv`zigyBi8qZO-lBu2?-2>&(@Q$_eDB$cx;fkbIK&EXn#z$!o2h(*42${eA^~!v~wN
    zq1*v?J<1OME2KdD*L_{YgYC)~ozY7Cq>4SuwamD;EYc%e#q#T`z5L6SyPwk_2E**K
    z_29+z@ZT}L8ler^I1SAVDhkq!+M@8La}3mUoSSw41$R8IN^QG@(8nI?r++*Tyxqa`
    zVAa5#&gz^U?TpAo5YNaR)?>ZmjX<f5@Vxr0)@;qzFI?j>{I=L@-8qWGb1<F`FrMAb
    z&_Zs?;r-B*Ilimh$`*~jhK<<jt=R4D(b6)~R9v5*Y0{Nl*$r^n_np}+{pD6^%sL>`
    zd?3>UZqsSZvpQ|sKKrvm3)FD_+Cv?|5gg%1J-f8+&3x>%p`hWRu;Cm&4}q!(AkG1S
    zV4}hQt<^L!-0vLED4ybs?8uPphipKdX3GZp9OM4#5C3fA#Q+PRZoP6Vw{v~aKF*_~
    zyqMli<UWuH4(-?Ei;jsv(Mrzb>7Ce#-QK<6*ibyux}d95P}1_7<-dHt_^sc=+}{8W
    z;AQ^VXHL^PtLCMB+HU^F3_izlPUm!L)JRR?d4Ay=df`p&p?@BXyv^H#{&XPz+lhW1
    z>%0Z+?A2Wx*45AmFIw-@3CWkv!u}BBoSvz$Fx@xqwsGwYq<+H*>jXXe<EqZk-^<WH
    z{KK$5-tbWI70&{JYu?5H%XM(rxxUzq4bqMa*}Q7f@+-MquG072pI`pv#mwx>YUa=X
    zp3G^i(+5uND}V?;&EQ1)+S+c^P>>O`TiX?W4;PLOrI5jXUhd~E=z<=o>kh(My#?>i
    z)hB-SkRItT>W5f>9xsmR@(|-}!0Bx557Hg*dGG|FF2izLum+Ft4xsR3Fb9ko<oO`v
    zsT$sX9oT`5@fh98=bgSCzp+rh*dP7Ty*~2rs|&#{zbEevkbl{it@8Qp<pJu&{{7$0
    zUgiUw1D#*Sp8xruU*-vH^QP_QK7H+o@Y<~Z^LB0r6zuIppO!~|54P{e_wdI}kM2<a
    z;UCWK?OqA%eB#9o6m;OVDqipS-r{CGsh6JVW54N~{=(87>c#K`1fPS8c<^!m-~B+|
    z@ZJsa5$^*KUH*M-@$i5O8Sly)Zw!OK(N7NYQjYi|AKz45@+VL1E34A_jqEM2>;O?u
    zAfI}C_@v|GD~B(I3>z|B2$3PeblTEgT#-)HMo}w7A^M1jAEIO=`6WY%(hd|mcA&Ym
    zLntO8nuO31x`*@LJu;#0DF_ub=)6$#6dhGGubt9%GTOD<5@<`iN}RS-twf|M5h9$3
    zR0A8fTBEUJ8=ZZ}CM_0uY}m;2$Hw1Wf3R-B%7YhHvAxCo(ktfd$sEFk=QtRH81a}6
    zV^%%Rw<_P{$$j$mfroh;JkBgQqXG@u7^>06jMXvLt4=T0t>r9{(@W0(F*&tr*Sb@y
    zmag5pbWPFa&0BEc-ozL;-aw3)2GgdAaqe8;^nrsYE{ur1q4w<B4qmh(DqTj7A4QEs
    zl%6EXl*ynVVEOW;OlUQ0=G;kUr%$0niz@mrDMglgsz9i$;smP%z3PexI=&JMEV9m$
    z0If#YXr!&S+@gyvy<pMHuVS9?YmQ>*aR$Z09B^We#9n+1GRY*PED_9Z<Sa+eP+=i7
    z(nvckHPlpVtryr}qs_KsaMLX}-hTUSxZ;XCZaFKVgD$#yrrC13bn2lfA?~&#Q#*vL
    zxCkSS$_q(7^-y9DC1?;>sipbss|lT*blORQdx8Qg6abAHbU>#642)_E1hHz+D^9?A
    zG^`2DI;)YiE_5r?xH<&!L%#eXal~RwoFhfUSX7KL#$t@D)yne8EY=)93yp<3ehd;d
    zA&Yd4wj^!SZ8zM2gK~-~sWk4&EVI<I%YniiGg^gm!0tQnE@E>dklZY(Ta@5SrcRjZ
    zo9RB9c0!lG{)!S*P^1hsupOzSLU0Qr>~*xO2qhegjtVVQD^m{T;xsQ1^Ac556H%n+
    zR3{K~5k?tZoeZAKWX%y3cr^C0G}MM%Z8ctx>BZODZevndD7`V3IN_>H*2?Fi<FaPz
    zz&uDyYNy3+XKH({=vs}&D-R^}-i*^d`0A8zfcomo6DL0Z_xn>Q0Iz<RKt$WIie5(X
    zbx_|3C#>`=EYvb0t${b4%dUe#{FFqwQ&iEJ77u&)MO9g)k+REd#Q3v~eMIHfTz4gs
    zHIirRrIufV4YpX|SUy(S<z{Bq=4rls7NMU%2R-y0tR0%XqT58eBq&f~DZX)K!c%H=
    zsa`iItg{}_K&iM^bStnO-Ir`iEo~%if#o9D!%q3yOYRc|Beh|}#+ai6Cssi$#)&Dj
    zm@~{~<(P28>mWbm#ET@^WXG9&vKHW`nB3SYVODl#nr-$H=X6Bp|8t7a9GZAe=ce>{
    z3lXbx&$wU`JK60iT~)K1yF$^9qs$`<@q!Aydbgneu6TtGkP6EO$yPQDy~7B{V_Vza
    z2Dj#o=m~=v)e{_WDi>9640~HeB4*?_Sbgt%85>;3ZuK$qwPp#ddCf6=1v$v^=6&#!
    z()?m3GiJd-I&iqzmplhLo_(&1q!Z7YII=AQcI$wwTZ!xBLkOth2|o|4UAvHy1*1?9
    zgPhPH2RQ+}NLAy5Wf@_mFmycRdB`6I>s0ez_(JFnMtXC2Sc|L&Bge!j5q`5#4`;Qp
    zAckg$L?n{Pj#x4z&Bk#k6V`6v7bW>sZi<+T4i@nTCjIr#mT5|p0NF%FZk^FRXoLbZ
    z*7!90;Ad)of?&JkI7d6`j%!?7&>kyMjXhHT6D)reAsxtqp|cDUQ)^=bEZSC=<-y_!
    zEu;q{rALn$LJ~1fu-+<E<(PboAs?6Yo2-5qK2H{|eB~q35{z-gBc;!jOJbrE;RnT5
    zBBx4Mnwb@yBg;Whha&s4CI3hlzybF1j3ilI>&7*oHkxa8wCfMn+(pL$UC=<BfTp6N
    zsjo<3b5dm!<To{yJaHxxMCWwjBI#zT6*(bqi;?GJ=qVX|!U}Le<R@r8c^dPrhM<Zo
    z*=&}`MECt>p_G$i{jf9*D@t?_T&gIT262##@(-6B-K7-DDA!+xG??-cCL0-O%-N|c
    zr4l^K1zTF6-N96kd-R|m73;@FXj7a2B)rhs1m+KAaB78cd!Y*fLyv}ysxXlpgA??Y
    zR2}MYGMDV$COPS&A^MYip)^u#mykXs{^}UH(Tx+`$I#*k16j&~Llw!orL?9sO>Gt3
    zTd^g^ZG}Xx>wyADf$0+YxJ0k!nwn3NX}fl10Vx#B-BSYb6g<kbDtxr&2i4oJN@3w0
    zScvRo3pq}o{?wd8eFDP_Q<%_B(lP(dAyf1DRCt7eR;+a`K+Sj6l9g<U2!-4fcDqnx
    zwemPcnBsDROI8kJ5sSuMD@Kv)zg)sqTj_bO6jXOcy9P5E!X#!%X;OtZ_I10<^sXWV
    ztGn?cb_;YkFJjZnSjV1)4%|HdTVyxH24x@*7M1O3BG0+V{N}e9PH5O8LF=N$RMIht
    zDD5Q^AuCvIED?m0>OaeO)vKnjaka%Q6DeF_Z}_&(4lRO}f-8pLmX%8$u17_!bCJ*e
    z;b(@X7NT>2=(p;!qtAuz0Ym!Y7YmcRdac5YWk+K;3PlLreb<0mI^Jg*R;GxhDIL~Z
    z<Z8gzY=b=833p1_x{%YSR3=e6=io@g7?nj4L+Thxn?o^Itjq;oZ9hM$VAZsFwhmU}
    z`s6%e-riX_7+y}EHO%J@dsxu4B=n*WJ?=!47?<Vwg0@7FVidDZb?%7|Uaw1tOw*fB
    zoW4gXf^v^f2So@+2}O?o>F83I_BarT-D3wI^(a+0yw#7zsbnQ9>&ngr%D28Ru6NB@
    zQ{By|lf2%4|69*{7MnB2=2#e5tL8c^q2(=??Xzq9IIc=~&ep!RakRbdvhumCf&MVK
    z7wrp07k%7@ZnQ?yt!{Q7kR&RGLey6Wjd%~!-u2E2zV-bleG4TEpw{tV3ofsDAqe3M
    zSFd^-yLR}(mkno|_^hF9>rPEMv+0}x-R7WW7Qs`rH@u-d6CrFcm)xm2GB7GsF4e*%
    z__Z)6C_!l(Z3qX;=3k|=Z!x_2Z1bF~Wrf2af}V~+ms{N8Ms(__brI``Vi2w`j}$)A
    z^yo&N#Zjj^jAMNNb$aKd6zKYWfBGGYe+M?O%^Ytalz;HEFPsx;rvny<+=#ZnTH<ao
    zf<Llfab?ukzEn;pVD>(KNQ%~|tN1(Mzidy!AAZc7Nxb42e}sR!yyZ*4fBq-0)yhYB
    z&cZMj-?B4o)3ze$GoSlBD;fkrtEh`mJwtoI(?hyJd%f2ag>;iW*+T@mlB?Q-I;jh$
    zyV5;Ovp1ZeF&Ya#ebcw$`??kkyLQNe3(A7zQ$E9LzUO<shWj8aC^A=rySKwWw`el%
    zi!v$8xEBgP&Kf_DGpf)+KlKx~C<L=9gopP-Hqcl+R5$|1BOjOZ!pNJg*{UsRi!f?~
    z9{{|w&GUx;JYz#Xb3-_Q!)Xw`dI-HhvyMT4KnX-WJ^TaG(}>qghoxIO4&1;(ti7m%
    zx7N`;V#>5e>^-eRrcmg*esjTq3%fulI2yD;grkETgucdtFKueOAB;ODqqU2xFI;<}
    zjmxqngd~uYr@*_RFcUM>8neV(Je4~F7NEcS>%#lXzc8FRG7Oe-aGwbSz$rjO&09k;
    zXhQ?^IiG_=I%GgU+`~V}!)(k$)&oRA+z1UU#151+Nt-$mTtqf<#74Xa6wEh%`;$-m
    zv=$T;@1jQ-gu&%IIOaRMPSinF<3Z`GJ}hvrSPMcXGes!tK8(Y)B;+i5V8SN6p7c|{
    z!J9(=Fss6gM1?bxKhW4hmUFpYI0qvM#$yn}G3>u%yg6m8GXY$~W`ss)gu|GGL(yA6
    zJKP^^T(oRNy==V0K->sH1PLi%H*o|(smnbW(<^95$0|5M;A_WzgU5giIC|7E8I%{Y
    zt3iF_L{E&qhYLvRGXm;kgA*c4%Bn17=)Qymk$Ct%B&@Q%YsD5Zzlgk{kc&botjLQr
    zFqJ#AFlen_tb=p-#bCrglAO6Q^aI^6##$(cDQL_opn)?~NnsF%BG|lVgaZSV$(i)F
    z1+)$b1kIcbO`Pn*I~0#}!@zZ$Jwo)rqRc&WR7$0!w@h2dru;n>ltkjwO?iyLs<g-d
    z8C*WD(aJsI$9_~b>Jvz@gs&4KNF!*n?K{g(5d`~+hlR|qhIB=vauM@`!dWD|yd1ML
    z>r46D!d?VSF9gPsEXgHVlEq|9lw7D~)I80@%slI}&7?`sTtLzU&Hl{Eob<yl@JZBE
    zO+u6cDCocs1i_<3N~L7V*)&1h%(vWJL8)xP7^FuT#K+~k!Q#}xe@xD{d%NZwOM{F~
    zRIEF5s?J4f$SlLM@)Ipcs!J%O$chY4y_7$d^GhwPgYyhb`~ybAT+j7MOvri6$BazM
    zT*=C;Ov`M;%!EVD3_Z^DOwz-~EA>w-z0w>Yy$Za*FU7zO6iP!>u>*BY*c7J!Dsa$7
    zWXe)NLEK!ysLTQ(aKYZh#CzOOJlaRDjJ`faPUI9wAT$E99743DwdtHrSYS&<1-~l8
    zPKT6Bhzzan^iCTEvy8-49le$EEFZ!A$a5e}Ast49T1;bf%=lDBWmMA2Ttha@OwE+i
    zn9NW8)Y4krQY^j0*CWs`kb*Bj1YQ->12t0wT~OFm(_?*5raZyls{%>PH>i|T44ujt
    zyhq^7N<Ae`KJ~}w15xCByFndGa7D{;eX??eg+z^qVp!29n1SnT(Y-UOc;!xsl)~?%
    z!bAvzzSL131x)($!jL3PVVt=lP0Ui=t;mE-Ri#WOy-Zh)Rs0M+TD?{OTb<Yi3`AYM
    zz+PRqU=2rNEy}wx)-`3*Dsa<$`#m90K_P%gI;GQT#n2hFRu0uie&o}C<W_GLNah4r
    z=VY?8tSoe0OGSl+c5PQj<t!|VR7s^*z^l=F-ODi0(emV1^6}J=1l3SA%wa58#bnG=
    zO<2lQScY{{Cxz07ol^b8LyA?~KUhEk{Q}g)K%wM7p(NH}J<~-*){(8vd%yvc<+nJE
    zN0z<Ns>H+%<<PDC&}|jS#m(8`!Gh-`)Iu#+pk>r2SXYKTT1b^w?i>Shh{bxfRD3Nk
    zs8u{pr9b-f)RqI-!Ys^zJ%%DR#@O}RBn?~2EP^I=SOYZMnMB+FwN+csbX(Ny)eIa{
    zx;0a~4Z%f(vAms4l6_N2Oj+VvSw*1Jm&MS+&C`SHL~ZR;5M^8}FoIYkOXqaXaZO7Q
    z0b0v_p?1Ywhtyo9?cDBc+KSA`eTCXO@XON;%$HjNt5wgeWzSmJTG?$(+NA*iT-e)H
    z;IY-+&7{(Z-A~`O)wUf3xXoBzm0MvIR;jarV%5E46<K8^*>$|lQb5^HD}^}~+!(B0
    z8H8EG#a2#8f`*&n?)~1J&DlZK;XyUav{VK}6<W5e&gvvu%thMGom7bo0>H~xe62{R
    zCEfeY-yWr6!35Rx)IUs6A}xO1B^ltZEnvv>h67$$u_aso%Y;%S*vu~oy)W=g3AWXX
    zwP0Rdx4D&Dk44_xyMzx8VP)-Gk}ZWm4rJmpVU<-`6}H|O#9n;FR+_EhKDFWR4c|%L
    zS!F0+v=oHy17aaoU-o6!7Ij~FB?n23U#1ndAeh1?ep)ETU(?-RP=)3HT>>udVgY_n
    z0{&XaEaL>mT{MPOI8b9aVB@oWV>s4Q3Wi%=#o*%IV7u*L+`FqiPSZYKRuVShKsMw<
    zKIAz~<QGl?M$ShMRkb7lQErvwK^5OYHD9zmS45poP9|cbZP6o+*HNC-QeNUHY+})c
    z;vDtMFc3`C-QWJjzpGt>8^~HN_Jb~tUB)bj#tdWsVIX5(He+7~W;hIHVUAcgPFrKX
    z(h9ENx4mHE&ERH^x@Qh18xSUGj!hAEY3CJWYzACI=H^6RWN_Zz8lGVu<W_TD+(`!C
    z9VTD%&16IsVn&VUBA(~>oy+(o2lZRxL_lRHc48=I<uEv1fxhBcKIqv(=vl6<g-%RA
    zh~0-iRT_xQ1D<GJX5d#v0x`(wU=}@&?r4u@Tadm$;az6q-C!uN0ZCKo5R~Ri0O2ag
    z2ABThn8s#F%x1xD;g`k4Zw>^>4g@-=?B}a&I@oE=zG23t<Q=x;aV_c}Zs#Dj+#;R<
    zdPwce-CQRa<){9;d>(_S4g+CpV#SMMtk%N+tbS#JHs~Ah>Md3xu`bw$Htw@l>oMNt
    z1s+>Ch=B$U=3)MVdT`?}Sb!_NZfwl%KiKZ<uI{%5@8K<Jp|pX%?qHSfU}zZZX+G?i
    zZc{)O<P)Z8ZSH18o<Ye@0zB%(%+~4d_357uYN5VlOx9$hMrxu}>ZL|v^Ly&2o^5@$
    zZ7|DMt3Cp#Jp$f7f^&dnt_JR7_-cjjVm}~jhjz>YPHTx??uveH=)P!;W@C`X?iknZ
    z8Ru@c{cgAw@9`$q^7d<~yM$=|U}bIZ_YP!0M%im_Z2Gp|!c79KoNWEJ>~W61%^rsv
    z25O-W?R6G}Ef)kK_VUx7fz)2@S72@bqZ(z`7Oe&cyd_5Esh)7Fw(#A?>I=8xSO!(#
    z?r<#zYfK<+hc0W!L~FH1@iJy^Uq%80RO4V)<GF6z8mDnbhji|~@$la34CZk&J#Q%Z
    z@jYhgY>3Sv_hXojY5AV-n*Iix&I0^az9{E!&F<bF@aZbYf+`pAp%(2Q-f~6#@^}dI
    zPhRaZ7v(gMUp9Ymsjg~#g>$T)bFDrC-|lMs!vttQ1}zqA0UmMVE(aROcE}X;U9M=d
    zJ#<BP@itD|>(1_TcXV^Naqxay@!sq5mI5Bnbid9$mF9FcJ#1(I^)|(UQs~?GMsh>`
    zh84EnB)IR%R=#hJ^8V&QSYO=#9suyq25qALVO;0(c)<7>;B|HlbJcFhS3uetsAn{H
    z>Kj_(+OF+mhvEun`5wLS3~%-fzgj$xb}hE~*Ij5Yuy$-O@jyrGLAM0k#oZM@^deAW
    zqi=B}sNgn6cXfCAbf5HguXOT0P<ik1O%En%_H<7-Y!Su*n1*kFPjYTP^%&d%3^jO!
    zfAUqAVOO8woyG!)7f4yZa?l>|iof_SA7Ua7b1?^HkPr6PR`Ul(`7xMqWQTK>fB6g#
    zObv%+;O=UKMrbakc55eVY~OaDr_6~C`XaD|u_bz>PxNuG?x%nH8drCAm--wpX?d^u
    ztJic+7bdPhY*7FDzU_Dav2W~w|AtUF^|FsagHLrPmzVtR?-_=5h))7QoqO;d2Xvlz
    zqUQ2<#`uii_`c_?8Nf1!1bmT4^9FBlVsCSmr|@Ne`E!tYJI8Ym-}7k~ah$(=Y}fWc
    zmuNB$da?a{%M5p;7yXN7V>bo}B!m7eAZV~4!h-}Cd>QoR;X^53q)@C#(S{U^P;A)P
    z=<yO1XqTX68(HO2m26d3vaC|73XW1TX9_7L<mSyRLcZzD;&X@3Bv}IO2$aNVAUa8n
    z(n->kP7gX*ruLYMW!zOPY{nVs+EtkmWn#q&QYPqZJVEi+vYp}9Exj49=+Ub|&&ew%
    zdYzEt>tqZ$z+-~{3?7^)NTM)_!!QnW#IfTek|m$3T=~Yi8%s1}TGAQQF-(3y`zc+z
    z9O`m3P}6ALT9_~z!YskIR76;j+%a+&LDCxz65zrG3jC7rI6~xqATpn5u_DIk9y!_o
    zdFi@Hk|Iy0><-gq%$PG>*evx8=T0G6c&=9jG>cH7NRK{cngq-J{8X(P;fkfdS6{U-
    z)*@#as1{rC%%I>~GsHy~J$c<#gA)|;MGl4-0u}^eVQ_dDV~aKR*kqI)VVPx{X?DX6
    zpMgf&XtkW?M{266=Gtqq-BufKyA=@w95MJ79Ff6&QQSZaD7lbAN-n9SL(EAurA4G%
    zBn1?tY}8TzN7iLG5=tnkq|!<_z;uszGnI#4dOGpM(|hs3Hy=_@IptJTRbioLR#*WT
    zAXsCWWfocpCb-~R56-nHUKQ%q7o>ra!{J~b1`%RojFDKPi795bVvC-=_@Zg1)wtS?
    zugNAGZMF#$!>hXe7-Wz|!tuqew;GomlTIFlP;(PG=bRK)W|UD!T5@zrbx>RfW;P^s
    zM`oGdl_DM_G6_N6P3N`A9-KcBMQ0M6Bys1R`0Y0pR$2YYRafDRCGVgHrj?+A48r9g
    zqj@<>VWb)c#$ktuU79J0kxh2ai72wDB4;g*I$Eja(3sk4IKE~HtFzq(tB<<@2_zCE
    z8;L~!LrD1gB+D!>*BlJOK4)cg8Ev_db=X}JZJ5)3XKhT`mcp$~=$*Hodf~#CMNvf^
    zb!SrNHl^QvRq5#`SMVCqLRd!7t4utEs%7X}h&ISCU31w*A;1@AI4QwncnG41BP!hK
    zW#=sMsb?+T5E`kXReWlys?tE)Y_NeW>&UeNS0t_vCFkVil`{m9L=(Y4(dN({OC6V8
    zR;P2c*_iG#&}qs<9=9%R8_IcauJ_Z@@y!SIQ$6Tsb)Q)I$8}d+izTnT2a?^E*$#>j
    zA@hy0?bq9q63k&3a%T$JWD28b*~1TGA~E0;3x08pHsZ)};;j~8n{6@d=9`ecApyDn
    zuDiBOIpqsAHzlzZ(Tt_CID#{0foW$X5!0BeWv1}RL}=x4UANNn4cP$&C`oYI)1Edp
    zs5C`gyz|{v{uI2feQgn9yBEIn<(9d~B|?qL%ku&j1h`eoLpHSGW3X2uhB@q0EMk$1
    z0%t1n*{E=>L7)2AC$b;?Xh`#W;^QKRzbR7jawvO9%%spciWo~vq=*i6tYZU_V5cM}
    zNuWv=Xr^nG!hzh{Rs<(VL8j3$KIdYJQP8!!J@L_ZM(6>6fG0d<(5oy3LQ4vhw?gKr
    ztuFrxnDi=Du)1ApQwwWY-hQZ}Aclrwrtw1g%%`#094>w9W1{=sCnS^2???~-QCSsZ
    zdAV2ek8@o79I`-HozWGrbYnVQ8ewuRp=FC(khqEKVz(1H($RwO$zUG!sIID2#dm-N
    zq}G5J4zG#Dgoi{Sz7#2!7QUj9Yy(*IW;i|SUFu*uG!Z7b=R+V$3={Sg4QRY@nowrU
    zH5@Bt5>Y8K_c`&2Pjsa$W9iC?AVCTZtw=L&X#+1(XG_d_7Dh_~%<kZzqcCA$8*OsT
    zW5O|Z>zU&Yc3{VIv6K!wm>Pbnk_4-Pa2z)6k08N9ui~AhK;SG}zTWZ*az+S-uV5QF
    zY5>V~Udnnn48sfVcA|J<=7&J+8{qKiNg9C?H6vin2+jx4Rw?mEsuYs{K@WPxgjVs2
    zD08SSBU-GvW=WU3#OTiQni61UV}TzXW=MxNG&m-6CoVYY4&YS53$j#?c*;)?tX55%
    zQZ|rV^IBNsWkNytG=)H&fe3^umvZK%oaa=kQs1V|>DlmLLD<v_@YX}C`t6?fq?kVa
    z(A9G6HmqXhm?g-%zJZ#xH>*r5%238uD!Ni4G22|{B1$uw+4Z7$)hGfJsKzQlB4{B!
    z=3re{*a;%Gf=xTuGo^Me-OV(nG`&h?xxzKBZIgK2w3lb&Ap(63m7Ju-OAXHHwhXP7
    zwXP-6YbSG!cxGmvKeX*Val6mn-pIFy8|!S!n#$v{Qmyt|u9MvV`h_e;cdpZ&DCjQA
    z%V%knuN?hoN3$j4ny{{<%OtO2nWj>eHnzv@rRIAno3+c%mym?ytXUS3-=Kzeg`-_m
    z4USr&04G(oa5FGtUh9|yA0f6poUK*AC*}xiwX0@kO*wq~PvB<wRZ4W9SxYA8v?3J5
    zl%pjUovTHOeldV4w(fOWY`Q`BwWA&lkBkK?-fexu1&u!GHxirDNuRgJQ_E9i_tXR5
    z+4Mg)oiAp$=}l!6sMKu12V3@=yrGt}sHF|?Ijc<ID--O7ujTR%Um(w_KDeH?)n{(?
    z$y=|cSv6`tVsMik+<|VG!v=LFEbXkMg>G)dd$tm`Gt%e(6yNo_J0o;o(`aZm%D9-}
    zwXqy=Y{4D-c)jk9EW9Ht<Z-k>zL%}9k;B2+eG3BBDQt3RSv^|392JI0O)Z8B%uXC8
    z7^;%dH6~Nh;4s$~!k#?yu)j)!uyXvu!Oie+EzxXeBUic7o_0e~2HicIm=x5l?urAQ
    zS3;)*2jcGNciYH1@g~#J>5jCHEy&n*>n^fT$@Hc-9pCx3fWAid=Dt&?inXX3;7|VV
    zQDKc*Stp#St=)2$OLPs(NZi5ngs?sJiHUMxLf91U_z@4M?BIr+*~dk0ozdVVy(@|D
    z3-CJ{l56dTt~}c){vymXI&*N}+|W3HM7ec7G@i%*F5Qw&=>>V*?wQUs-btVLO`E<o
    zWwb!EM=o{0*W&N}>enqzF6Y$?uJx^3+3S_OwSuQQ_H#&_*Az$lXn?KTVT1Ck9k)bS
    zk)L5@$3WyGmor+Qtunyxz50D;h{~<Ka_PEU+bCYvvhW&c#t+)@G3IVDi@9^dHu~q6
    z$GqknTlD$I8}#(1chgBP>Tp24v+=-(p;VoM1YGPz+3ZnS?OmCNTuM|O!E42qmyz9N
    zqylaE+OM_UXz<qXfl~3M*~V=gaLrw^ja(|tne^EY^<AF|E<pA@7j#8bGho}{>5_^m
    zo}dw$&6(fhMV<>BlMDPHqxqa1y&t>9A4}2yU;QbakZldqd5!56NI0Yg7o=W1Ox@JY
    zfGW72>#-VXnO4@p8rMw<tu0(s#a7rI!Gi4`@0p#LSzy~?pcil;2d0@9Y#hgN%m{?w
    z2#TEZZP=VepF!ze3jUq->7B|U0d#F&M43Rbd>=;)!?Fy6_}w75<si;IUK>FkB&;70
    z3Zdmi+N2F#k0Bwvje`<WR=sfzD|pR-goC_r!6KO2|G|R4O<fg&7A~@y2y~(BX<Zm5
    z+!*3s89v;J>|WVn#tHmTGLG3jdBGdTp>D}xv5f#VejqjKA@ez39}-mb!9Xj;3?YW2
    zAQB?q3F46u9^t)Uv25G6g;BTJAmds8qM>z{CMuc_Ql2MH5TzC25x$$}eV&k|V$;=E
    zE1HnLDVdT{;p*wv0D=}5@}dFG9@nYU3#^leIAGX$nXauNW>6q)iP;IDoihqw9L}Nf
    z*<teKq1??~^N}F*(b@EgBRR^XAv%-`YEd(ApUcf4bwDDZU4j}c9{K6u4r(Ig?c5Kx
    zpC?KhD8k<;`eXeC<fk#=K)NES#Uf`Z85jH?S5_fIia=<EB`<nmL{eF5y+FcMWDrCZ
    z!C>SAcG)@T+So~)N3uvVvd9|TQ(mSW9L6DU`K38{!B?3BHRhpViXb1l<Z*c;BDT`?
    zjpIy0CK`C-O~ODkY)%ZWV>{aa03=4Dw~-$X&SOzR-q37fCyLk4>Ek|%Vn6;PK%yf4
    zHQhj#9{+ihR;C`RP2GNFVOZwkE<)rNg5g<;p;~f?TS8z)R)#U2!wF=@T`r>=j$~i@
    zC3SL~b>^W6HeX_rpf?JMH;xz*jH7r)=HJyMA||2~T&BzI<W7Q)Pny7WNaC}o0ceJs
    z`O%|M`k-khrK2rnQ-&gsu@@=6W<bg&D-w?@W+g(#!&Y*o6=ESSisjWsWN-!;T4D%V
    z&QNi-<yt1^MwT4~-es?0!d{YS+C?L<fs#G&mhn~NHDV`Xw&e2>6k{UdkqDxAj;DB1
    z=7_zZWol+K%%BN8Vs*&>Cs5AZx5Z<j&0~IU;(l&Rf2QVt^5eU8UJ}lmK+0w~&}MDM
    zVmx>$s^z9v?iW}_=qvnY7nY@2avfT#rCK^5a>iB~uHiM@00lCqUEV-QhNOu;W83}Z
    zU;5<;2xeg#=3%m=Hge};K2dp=XO5DiAf6`-OzNbjXHH@pd)mN|a^_tbX;7AgBn(=p
    zmfwAjrY8O%s-mit?tlucrb-Fu4*a7jp5pywY3b4CRhF7oa;2E60$A#oSneX4%3g({
    zKvG>OIaH*Z8s`f@AOxPn*v(~Tc%+D80-v71XxNiIJYz}9;h+vGO1>y!Zs&%<0F8R*
    z{D31GtgE9o>bm0pC>nrgrB3Q)#^8Ex+opD=B+BQf0wpElpd>h6llCCNrfQT<kP1W^
    zD5^l_v0KquX(^s!y~XBix}qz_BE~W(uZrnzhGnqw;v}dkl^JUdv}v+(NExo>1Ij5H
    zt|498sh;L(NZ!C+=H*CsE6&E@w-#z)BHy^S<UWj?xi)5F!T`G-t-BtrAW|l!S|$vT
    z!qcuNiVbPL7AZ#cD}81|)@H+~$|HW}r@^Xfe<tiVBrK~^n(oBv1r?nN{39_?*4x_Z
    zmd@?PV(cPp<yMAiZu%;)f)-ewDQQgtY3T#3p#T_?tjW45RIR16qG7bsCGW*-GA`q_
    z+LO(;UCvhjC!rQ9@*V0P#=t%}U(m*Yxsoor#!PrR>btHk>lz}{QYzGvg1z3WrV^=7
    zO5!G5M^Mt|){biV1#FC&D#7lj*ftv3n(f)D6vMviOvNg^5yNXvY?f{*m)fnDE`r{A
    zr4^Ft#}4Z&glw@Ym9Zx4q$sP(HfxtTE3`r`1%{|yTI=`Jtcl)i<_hNK@~qFssK||O
    zDh(~^60OlHZPF&~WS*xMIc-kP?iZQB0uyQO^6Sp<F1Uqis2UpZe(kB6CL}bPs!os#
    z951D{Y7#K)+QM7gMi%sr!`o68D^TpMT5OjxC>H?W4F4ZKOkFepYu^g%_Xe&3hHQWt
    zu2Q}KK*=Vn`6?@%rf(Y3Wh%6<<W4U9_NmRrA?D`nx8Cnz!e|;KTj<KD`-Fh|n5!5U
    zZR?it>i(UiHf;jm>rPTFr(VLo9%)eeYu5U11z&K$g6&ZfZ<KnX*|zHPzG|!vvfC<M
    z+)^y{!f=<uBHrq)75b{Lf~6$?=5Gq_Q30_p8ZP;^>G?9Q5<4!m)@c*NEEMml6t~?J
    z*RTEZ>@}K0aKY#vj_&A|t{77$BAPKSuj}hRt<$pcM7i;hLgFS=NAB)!1e;C@n9itH
    zuxMiN@P@6UsX#QBZSu0}2(NAP##=um-3qJl{jq|Ua%q>wVk6t|)ZMTuWML%x?JGq8
    zLpg~+l=X0cVX`I@u@N8f$~Nl@T!UOPv7NeaDZ_7G4zw4n0sUGr7WZr(Zm}gWv?X*g
    z=x!Joi}C+@V=m)z8q=#~Ug~->aP2~39801xYpv<ju`+XQM+K}TVDKIntl0kG@kSaT
    zTQea$Zx5WXDXy?4e3}ctu*H(|-C{5HI&u|?KqOBx4_h+$)&LL_ZVebN5hrf5o-YKZ
    z;p6gNot{95?x`sc^!!$F=3X(+x-wbMUFh<mEJrj&+j0P3bn7;)M$4`O4=E|QF-Q*s
    z?#i(QpETF@H4Xsp<E`|-{vaPqv+<TKHMeR?EpHDv?+K%@A;Ya~wg5Pb^El)G!`<dB
    zK7?sH>*i4l>n`p=ISDRlYn>27brDasYNxN9UW02lar<tyo=$E^dUalIt39Z)&I%@B
    zQll&9p(|U0D>HO1h+G=TDF040=}u~Rx^?TOalIz6y)y6u2eS<fGwv3%9Fuf1_w^m8
    zG$aW2!1A%!N^`=V_t~~;W3PAHwk`Db0QBl~y|r*<vjW}LZBS!2KFsiDZ*~<zGE%#<
    zu!?pM>%%42fK#J(RJZ97>oY%xGVj5*oznJflPJxSXhCDRLDz3!jzDjJxQ93NLI)Q^
    zhi=fOwQ@7}q&oLoL-$5=G}SKfUB_`T2W1M-abJ6PUjeURi#H_9f|El3^9M)s2dltL
    zGpu^Y>SIH;H_JC=k3)ScvfaYse($$Aqw{A^vfzfcJOE2Tw7=s5QwQ-=TWHCmB|ob+
    z`tCD7UjuCOo>tFxg`dC)40LT{LT;}Cie_$ygZK!f!8J0pLw9k|S^^lyK#DK-EwlLQ
    z!gX}db#-ehNW<|k)cAIHw~niHHiUOzYa);bIeBxic_a3bk1!$MbPsHEe0%da?DS4k
    zcGH1#IM_FSQ#tnX_hxr?IvaI>pDAhgaL6__m_xO(GC1SvbF;ekYmag&v^kv5_MX}{
    zK^HWKdqH3lCbft7o{zXC6!$}mTrNcPpiguyw>6?qrW#9k3{tlVP;D>^|8sT&r3BMw
    zGAA<yqqIu@b(2D#IuP~;pz6L4`FWSEkpptu5^{UPH+<)Gs>`M(usS%yI;?X+ZE}Ic
    z)A|jgGp=X(-@5ZFh_>K*xvxJpIRLvp-?M`oyPCsxvZp+pb9G+EIf<@;L2o$D=5|R=
    zyNBOApAUCK@B9{<`2I$8(855_H|@B;tD<i-8#8daJ343L^(AbAcB{d=XM?<Z_g`PV
    zVB>%pXu6Zm0;=Y_3w%JMiM`mLHz12Tdz1QmzwO)ZG%>Wm!Z$o+zdHZHdQeY%mG`%;
    z-#Su*<s|Ilu9vo#6RzPZID->=v7`CPH?dZ)yyG|f%WF6)cPq`u|KYSt`_1<@pKoz+
    z_x!eZ`-!{HKA3AWnC^-fJ-JirWrjdbhJec{eY&rE)3>|SOFh-!zPw9<?qhwXHz|<2
    zbgGK|2Y*997&*Z2KtHf*+LyY)69e0;@O;BPWj8$CF9IuI?8H-it#>xA_x;~*{AeF|
    zuOoPvqreLotNH8mu&24PqrA#5JDjgUoVz^CzdW?ZVdYo8%~yL_o5L3SIY3y-WociG
    zG%<3;C|EE=3>Xh##6W|l!bFM|D^AQv@nHxU%|12@L5idaQYMFy8fL=AFqfBxZQ686
    zP0eg-QryfbDH~6pI3xiD3P}<qGe#k~fHcd63#RIterT$~|HTIt-&ADHx}%2*t}43r
    z>p`rCrm^FQwMeUJ9IF<r+_DNMcadB!i|%sKi;oLFzkXE(4lF{I2*V<xy%I4g@t#+c
    zCOxh(sfNB9C~Ca8Ob*417vwyL_I!;r=~Smrq)L5)b?XyrUSnFbM)n#nwr$(<vRn6V
    zb6$R%6Fx$CD@(@BSxTc^AVGl$6~<`T5XQcU4>xAVxE&2dkIh7qYF6^3N)sklx>PSy
    z4NaOg)%47k!l$I4Me#$@e{^Y5rjVe5DynZjAW$o>w(1J31$*d+EVhV2s|8IaeCsW^
    z<dQ2KE?B_}FTMKu3m?G>D=eDB4x2=TR~&OJGJPns|I9KJFzf6x(L%fC1szR04K>wV
    zdyO^PW-D^G+mQ64H{Em#&K%;NOk)J&mXoqd=k_6}pj@VtE+UT}vPdKDE*i6=heR50
    zB$H4&DLwUKnrRdGYRYM+oq7t&zM=BtufJLRYpN<2s4CDZLJvGp!3G;dD?(x*WTh<%
    zG0d>8x;Ff<uMkBfu|&g8Tuf9Kae0g~Tq?V)MpZe(amO7?^HH@~gA@`rB5_5MN#2wM
    z&R5}p9l=Urg=KD_f%ZvgpM|8G4k8#X%BUh~$IJ)@Fhc+-TT((`^Q808bE&16E}8Sa
    z_;6B#C;GBk$38vvBuZYS`g6)&sH7V7KtvG)|BI`Bz#{9>2sNQllekzRW-hqqS|pZ5
    zGzC$_zetS6#KS^WF*3(+f$UVwW~||w&QJi&)zW&T706m|H4-=6dfl~2dw_kiX5p?(
    z<H}sJ>{%g(yo?B>G0QBI+KRmUXrwipVG~^R!gXnrVbqv&lbh<iNs^uJgLmGe00q=u
    zrkKiCszUkIYEc!$5{sC(31*8zVpyS+VYo2mFfWNY1uQU&Eyma|#}*^?6;vm&Y!Vt(
    z)r@2uc})4oT8FgN<u&ZdE#@Ybbn@mXiPhQI=92wcIxnsBD1>RFHM(_*umvx?lA1ox
    zhV$TzQ>Jt2T({kKfd`)6qrk5BsX+~;{|ewlv+61=6$E}%6G$gac;QOJd6*Rtx!<eP
    zid8vLd=d5_e=!z4K0HPlX~Zm*7djIy@>peEEy!D6E3!6Sm9&RRCe1-JdqAmX!?Kdj
    zmNSqLq@i@nvd*%q1CiFTj&&2fmeOX|t=fGHYPplioam%35pFF{zynHtibA|Ujm-rD
    zG8>}O);zVbZ7lsj3xuj?uu5fUdp+#lrYJU|zm;fw(MU`*TI3@2RR(d4BiZBP_bQat
    zZ)Nw(pUZB8In4QQb20l|8lq&5D&;H<$Wj)wQs)qlOpuKf?2hWfv!-rw5O-129SG?(
    zA9c-#C+-TKedMLWuq}m!E;N)?|Cl$!wmArHkQzt9-f}R6!G%LU<U%aE#}|r$uZZM}
    zhKly!F;T9sGERJ){9cd>`km$%v}z4(EO)ug$;^NM3*arkC`Mr|Cj-XHg&7@qx_yMe
    zBhqo>F)1>w3S2N-OXHT)>S)KQeK3S6z=@r<_D4WU;wULZ3ZT?M$m2;xD(X-{@|Xv(
    z=Mlq(Z+qn2>b65l;tiAA%Vgh#7(RV=vV4$P3}haIs*a&RA2X^E6kmmcSY3mPl*8ZU
    zjx@_!_78wG`y9@8DMke5VmZlLma;-HOlPgJ0?JJ0NlO|nI5zW{C+XJH-Xt~l;L(~j
    zO##=m`7RX-vOiE6C-Mw*|IPye3}9q&&{*C!JxCr*k~`$k!y>`GH%RO+t`MRIMkGG1
    zo@ftU)tD$dRs&q<gP`q$-zhs&8Wi}bm8$s#%VH_ZEmE|W0>ou;fCa__imr?a<QYPi
    z)hyLrtE4G4>|qb9x@MX{j!&zj2dP=j5z;58I50z>zPS{#{gj-9vI-)J%C-eLh>?zZ
    zTRfX8Prcc5s(hQ6D^!6Gz=aPnNc3bsM+q5yjI}ZbHS6O>W}3CCvX!l5&0AslP`OI9
    zu64a-E6f31=!%iLI-`>4e#yXJE+nK6RNY~diCB~p^ID7T)=M1=S@uZwrjk$>WphfD
    zZXSgaW&r0bm_msF|8@2QpZ&)20Nf7&yDh1ZlA9z`OUYPl50g>l9u-1lRlfyB1NziJ
    zZOgZZPj=NslMrY@L2OF+jTlx6Whe?#lM3U$^_7)_=q$I`-2d5Qx;2h&boFXo1nTUU
    zT5@1Wn-#&r7B;*?Hfec9vNXmz_OVjCBYdoxE^EFEzS_JjQp^ChaBjAR_^QgGJ{#KQ
    zK~KPNQ)<CbyTj}$*kK@QEfA|37}x^Nh;>GyZ71PX4|^CFV_h6s+xJ`e1veGLWu;q>
    z>l)>{jJY!QUn}e?-5cw*x;y6ab{)7ZVv?7bL>_XHi(CQ4a_hY33BgJpYl5jgnWoo#
    zZ<VcVS>n+b{}PnIFMwvT-!E51sJ$MrfXUp*12YvaO<nNzJa9?9>7}()5Rp|?F=tg&
    z*v@t4Fm658XR*SFMt}}<LP=xjUtFWODn9P5egRP}#@Nxdgmfz;4R7dDddJGaZjXbh
    zX_2nZ$o=*=F-cu&^rm+`ajF`7Z`xj1+vl>}ylj?@G7I~L4ZmI9b$^BW0Wq`f*TOCs
    zvEx~(1=lkYi<oM&`{HbTKpVCc4qvs6s9~>$5;7g@Gq(#GXc~=d&?75!p~;Qtx3W0h
    zaW#_v*zM?d(>S`)g?ElM%HuNr64UzLbf^8D^}=3#B>Px0lhJblIDHV0b-HH4DZ6lP
    zt{qSi|F^GZE1v7BsH4AMR)IN%O4N=sGO5IFss}1{$?ZA#&0K&mKBy7r@U7g4FOT`1
    zuTAr8;{pX^^>(0xYvQ${SjC+m`U#Mm?sUI6>Fs8^yyXqXR-|F|I*WQ=Ia+m1UmXTo
    zxBaF`z2t$LK<rVw0ND?2cAKI-rwm{FtRs$J<>y)*bf0_1&#Cvl_x<A_2Rw#G9*#G#
    zqQT5Ie9GfIB5A`M@{ynXSS?T1%ahFKn;&<%KOg!vjGpw3mhsY2k9yXxzV()lJ=HTU
    zq^{2PwC?H{YxgbyT*Ph|#BQ1*3(8zA;j-zE8t&n;EXx4J*6!;m=#K8{PUFJt*UHSy
    z|30p64(9r<Z{%1Z62c*>FbU-hud4i_<y;{e<_zX!j>Gb-=K3uCat`x$PN9Ztt$qOo
    zeqrc_ZvK=oe@vlBqF^@cPw8lk#`5p!_|MW{4*)Z5->{AV`)$(>Q1=p00mDvees88A
    zaQNs0zWfMyE{hTz&M53_;+C)ZT8Ny~p&u|V1P6?rNU-`?0R`bA`?e30UXTk;jti)v
    zFA{I%9I?V~ko>v|iS*3o&=17aPv_dN&|1j|&Fu(55A>F>39iUUp3wB_>fNf4|E`er
    zuyE?M@W(dI3;)dkz0TCmV+<K^JvKoJWI_qx0~pm%;UvM2+VJ5dLE<j3)-tdT|C{dv
    zJg~pQ%qm2X@2XD|JOCR55iS64<QD7=BBA>d5yBd=5v>ZgTtNhCaN3MW10atQb&$_)
    z&hpAi=Y;U*Jn;xs><C$mHAoRQnxIGmvPh6F3Tnw9{l^g8jS5$B|5%Ts{0|mAYW8N4
    z7LAPS#4s0gaTkB@3?DGc(2f}CqYaDk_$&|u=}`Ha?-`@9zw|KrI<CMzE*npf8@X=~
    zS8(tS@#OH~4e-I)&e0KVaN5|h+H&yP&d&$yv3*8H2s^R3_%Res;RODXDqmwDp>PdO
    z5f!I!74@(6`me7fl4k<2A}w<3h$#$jk@qxm7dOEpeX$pzObJ5rrrhN&|LwzFknsZf
    zat`Z|`C9U`Ku{0+kddx28?$j6Z!#7ZED~5@`@k^~eUcmzuQG#D17HgrqQM<Gb2E=p
    z&*aeu*DnZ*FcmOB#ZZhNqjD;h5TZ^|EByx{6LL23uL>Qq|N1ZXV391Xj^EC*q%hL!
    zLSPINQ0x})4B@hBQa~8bE(O+MBuBC&^KviwQsS&rC07y}rO_n^v&;<Bz-Ce#0k0by
    z^D!Uu1;;T4oy{B>54PN~1~)T4_do;Y6DcQew?gdln)2r`Am~){KVgG4n{W*VG9X`5
    zHthi+5faisfyNA>3NxS;!&2%1kT)rE$Y`OkGPI;Nv;sCXExpd`|A><<InoT(ATCcd
    zMH7xL6Ye?j5-%@n9p2F4F7P_BlO?y4J4Fy~J|H}^F&4ygF&WbkT`)bJZ9Oe><)RHU
    zjR-zFvp(%JG?jAwIFSgA@IOJ(6hv<z2NWx{a*LWEHWPF<rw|n2jUgkH|1!W8Uy<r!
    zahOEPLot*?=TuIKDMWd%TZ)tSjPou1^n-j+Md`9R?Q$evv=~p4FYAjEG!Va5@)>hf
    z8VU0Q-lj(n6C1@d7RYnt91|R)>J1<aRMoRG6AvAu%?9IB15}khSrsX1t_MZa9*vMj
    zim+Dy^8}_+OO>!oXX6Qd6*dhLAr;ad6fz3l%}iS%O<6BO|Jig+H!2HdPXPB}Lo<|G
    z>C{?9%>;(&0`yc*+42I6lL1e(7k{xPgpoxH)j8MUF4q-N^-@Or(!LmV78><YwR1Zs
    z)ermO%xDrZJzzWoaY$n!9Ajas3ijltiV7f%3al#esNo!I&^>EVRX0;r>r(^rY!3>d
    z=Jo&s`V0d=3<J8Y2-~MtuhdpmlPX1zKv|Y$e^oYvl}sD-L8%ZFnl4$*fmxl^S$E7$
    zEfh{y2U|N-PKlNl;8a`l6kNZxPsL6Nh9NG2QBZG64cwqmU36X9^<B*XUN3M_=k-SK
    zHAnN67%Y`i5fdB#HDEV&V9zrWK2=mjl^T%L6-w0=|Im?1S9Mh{)>ScJ&pZ}CMHU4D
    zmsSb)2v7D1xdHU1vI)|y2@BL^F(+mvmsp7v(jN3dlNH_~lv!7AXM1)x9Vi#HHKc_0
    zTItkIL-Ybfl=t?50r`{x{nTlF5g2K=rqK0jvleT)Nk+L=Q5Th79W^@xlRL>asXicK
    z{Pkazm-{?GVA(b@BjF7aHer9VVXb%N&=FPh)?&e-t26)uX0C5PcC0}52MKppu~apu
    z5?8G<AgQutpRfsn6<8@(A?>#n9u!$KH*?cqO*>a-c=j2l?isdAb+<KW<+NH8c!4u?
    zbh#B=hqE}rl>s|acFVPP=@MP5)<xA7YuR;c|Ff)jy>?#p3otuSY$=sei`N7C!C%of
    zZJW1whcq1D;9yT~VUKh@>lS-aHDb3{Z|U>ea3PdD78m}OtO9p%O%`Pl7xaEXSDVmr
    zSvDXI)E>e#X6=`98`N0yw-u0ebNP2od3K`+c)NZ!1Qa-p890IW;em&fb-`7FVfXhe
    zSS~R*BsaK&J=lXmI9?g`k2TO<hnILOwO~FVd6idFotH7)HhQJ^k#YD)?Y3_3)`z$E
    zVke;xx=NEfmWVyfW6M_``m<!wH+{8qanp?(rg(}I6j+1RAhTE@8`Mm@I8DL0e{tDa
    zJAsUIfq*^AfG>a+sx^&=Ie|0ufFGDo|M^txp21Hqz=CHtMOie1Ie3q)xsQ9-3`Uq<
    z)xm_rw!g?Wg*Bl^%@%E6IEEFOkq5Ss9~o3ZRbhXU8YmeZ?Y4)vw|iTalR4RAC!vTz
    zR%DYHeT@(Ueu1D<Hdj-54WyWgztmS>xqcyfX1#cS!8ilN7?;yvmvu&fdztE<VOb2g
    zm_?eHJ+zI7vyIz0j#V05-*R@J){akfId7_JQy`D`xER+JgtNJ80lAwW^@MY@cze{q
    z4jGXH5uF9Lk)zj<rS~TnR-WbAGV9rH@%D%J0G~5fV<7{4K^brbw{T1OWVzu8b9D_=
    zS(O($3Jg>$4f2X%ISOFe9%dPT|0fz0ZrP$QnrAh-m-nHJKH7m)SC~Cim`7T$1$$dl
    zTA5YanOmA%X*Yvqnnm3JcddDkLpWZw`H#DK7A8P=BelQAIkXS?oRt@;lbW5|nF`!l
    zZsm669@cJQ+Z8ZVRb@MGullNSfj&98ll!)-0s5<rP#<j7pvRi5pZEmMnn0<Tq176$
    z**aLWI123da_d@ilXY`*S!XnQqX(F0KH7i>+pvdOyayYYBUp7;8eEm&nHg|)qZzW5
    z;5lhpzAGDRb-FJnK%32A0_c?z_`CTgKsy7xzdQi6)j_lov#3iuos)W<BU5@+TeV$5
    z5p~#l>DiL+SyeGWw>fOX|3SH*jaZaLIk*QJWibG`k6TL_H;R>8xz(Dj*B}_+8m<%6
    zeqq@Va2$U@!A#B6qOZGu^E#usyQ4dLAO1R+2|K(Ad%Ou7y-_;7<9NLrTM6D9zGb><
    zH@Lp}7+yEPkN>!{^{cb9^Rp3roJ)JbO*<Uj_Q4_fovGmspgOkYe70p995ler?|eQr
    zK$E|>&$$}JeOqvaTX0RYpbdSjPx*-#*KwEIK*LmiXFSrSd%7#v5O%z-H#gI>Tcb6a
    zqd%IjQF_Tmebkry$-y<HS9-mlnT~sr${#zj*MTmvT#v6gr@K7MHlV)tJInZ+QPn}r
    ze_g<(vB1e0k<VO_|7BR6Q5(YFSz#F#@f=ppWqaCZ8_!i0pEY)qy9#^@0hGOc#6{N7
    zuavlrd&QfO8=4>>O@WG=`_XA!(&5?=CSAwr-L5kiuPqvv0YSUdK!6GO-*e#t0)AP<
    zd%VN@;GZ1Tp<KP6xz%6Y%4c26Et|_PFu!-5QTZF#JNp9$oWMbQv`72Q6FJ#uc+HvG
    zomG1gSzDf^J+`a;+VkAb`Fx+hw_}Ys#4!M%f7{T>{fVC#1I${X8J*pU9^T=7y6c_C
    zGd<s#9+x}4-#eOsftl2)e#xDD;aA;hv)<JoUJ0sJ)@ynlvRv#dyUQt_*U>)Xa}C(l
    z!2{@S0>YWt{|kKX75rb3eMn_tVAuKNBiz{)7OGiZ+A-4=?7YtJJm#+&1EN8b3t@=C
    zmm6~4+jYLg3A*xs0nruR6rxz&)7`}(U9A%|(&b&cCq2h4UFq!_uYVj6)^y)9K!E!l
    z>H%Khk-X}u-qf*P)z{m-Tm3|-+zqI<riFj9*OmC|8-#D&4BCL#{oB9kRm=li%!&Pw
    zJsunD9?c^m+11?S+gxGcoC;R{wO@PM?Y8g_-_FrrV>w)tGr9e99_K|_=Xw6neO^n^
    zdh?f?K-=Bu<$d&V9LE6ylp#<AL1k!VFoVK{3e#jr11L==iD{ly%=K*Fvs@SU(fSB7
    zq{xsR|1X#<c`~Jv3raRAY56kd%M>r&&~&rT=DM9Zd-B|{bAy?6W`vqCGvO#wN=VgB
    zm^DG2RCOLGefZ!(9oDTLJYek#HkJpmkvv3-6)TcjwQt(CrSgUv6)M!;xT1y^udBU#
    z!MXAaF0cl0gEbV=pkdEp#f&i&!i8ZHHw=_3qu}-n!!Lc9HDktXD)foaO-wa8jXFw`
    zHLC43i2^%z6hpLO)3#R-A;Am@89K}`@nOV?7B?E_$Z@3dk^4+GPYHdcbe1w>(p2I`
    zjT@UfapLs34rq9yLXEaLnlve;3G1Oso$vvERIgjPegz9w*4S9G(xzSO)?07PMIc>y
    z|50-nUwi@f*I<MNwufQhFjh!olN3T3WR&SsnLe3m=GiZwnKGIw+^k_5YOAI8+H11K
    zX2@)`%{Wj&yY<FUL!D$0(Qp-AbX-Tv4f)Xp96|TYNh`5Lopdj$A(I=}-Nao^Dfsjq
    zPvH$!lv3xVx71T&MkQZ-_IZV$2NUEsmIwW%HNjeuyfNp10~RP(o(Cql;9r6T254Xv
    zf-@+EifNePhLwTn88@I|#>+3ErMTj0s<jxKi?+!~<BT>M1Zs{sLewL1#cgy!M$ZsA
    z<dGVsYEqI*I@x4(O58+c3f}d!t54#U*X4O%A{8c7WSTi2RaBwLY?`r5fEIsf|EU!V
    z6Lkt$AYBF?h@gUheG90AgY9w19upQ)VIdbfw#TCGekdXapFKLMY4)m!DW)&Z#%ZUY
    zhAOH;JMJjaM4g<9k*XJjR02p1fAlad4;z^ztQO14<V&^Mn!+wNVb|-fS^m1tdFxC-
    z)UaT($1<5td9@~0UwyS^SNe4&R<yF5#Y9?fF!5F-+0Lcso_&HFsGx%#RtPv45PB$v
    zkGV@33Y2{~*@%*cwgxw)t%0wl`N9^=rk#3Pqm8@;EHH2nbz(5W&t$Z4#1cD<H^i@A
    z9P6wZ+gcNLw@O*1$h?j>9<V~4XNJm1wd_=xYsQBrRyC_>bDRC`%%7as|K6Ex(RuON
    zXVQThW|(5BOMM|O7*hSNydpYUuh;jgc&Tc-?;GRUw0+7OjtuSScEJ)ASCMfVMf`BX
    z%|m>5l5@jau}Ra(v~l2s=d_)!-fd?%P>KgjY#S4tzux(gt0N1tXinbMeK)@;?SE?l
    z-4+uBf-cv#M!Rx|y#x?2rXx(#n6^5pQD$mX%S_fB1sW68D{L&<T`)8ijNbV!ZE6!9
    zLjaM1X8;Exa6?!pV0Aa=LGO7fRG9QqwUTFC&q;t2oUWz>zQb*&C*n&S`GmDH^$F2^
    zREb$uz)~~)h~-T?i$oKx^)qaR&VQrp766fEI@BRBUFmAq>tH7@|9L@>cHEdCrC8LW
    zvSE;48T6e%fCsP~4eV{eG1$S9rz#buaE~f%nDkyaJtbhQhA|<W;I;!Y?s0F2IO!o!
    z+OVa?H4Z6XLf`p@*ghj#t}B<51uHi9$+R?47IcaP{-|Kep;7UFhoFXE2AH&_5h@x@
    zdm6eFBQ->Uadz{n7irFjJF%553S}$i27mXjwXtnSAT-{=;x@Or^^uR%EWz}mS+Nt!
    zFk{&(<RL+pLq>kjkz0b~c{V_a4J4(Kj}zi0*CZdzZ4z^8%Gu^bc|W%RZHn6B)`0@Z
    z7g#1uLWI&{y4a;aFgi+%5cDN!DA<GxN;IOxB%2{J*hU(?|1p`${Gi)*Btp1V<Y05-
    z$TOVRN0g?}d4HsyAXy^Amvo|rQu-$NxbwZ6=0toO=-~u<`qN2Hz<lp4YJARzxgx4b
    z6Kr~xv^J-fCYn;81Y$z}meJ3=EaNVC31BO|>L+j<0#LGK+F`hHOJ1z&i<Noc7|ZC;
    z(ok@s#jL0?GfJC`{;MDg0R;#Z0zweZ@fmP~CLT+GScxSTu_$dUHj|`HB^*qUG^FVv
    z33t=X>a?ffDUT%8sZL?Fv#1l0#mnNktTpKeTF_GBDAi)E*!~PIcp{pCmcgx7Qsb)t
    z6l-9{>RUk(%B;9Vmtz)4naPCltr9dNL?a4aiQ4s||J21O+V<KF-T(qef&J)k3QN*x
    zF1E2MWvs-m8OXU&Hl{KKoFO$U&YXtF4Lt3seJ2UT^T^_SqrIe>;EB1Ksn$(Pl$I0U
    z@WeXpQ-1^k!d170TWW086%z*QSTF3`LmY6phT-Bt+4?|+!Zog8<He=u8jKVZ)4JBp
    zD?tp=4LD|3Av69f2tx{pxZ!bmj5TkMeT-OaUO=0Xjp<A+Yg5dIQ?ooBk2#A1#L)Ja
    zwEqn-Co$(#*LH4a)|#sR_F3Dka*L~9-R%`IJl3)fcev=1T0+0*Rx-vFm@1+xMN@oH
    z7FQP(8T?d?cawn`1C~c_3n_TRE6pD(deM&^|1wKvDnld3WEbANtdijiz75dVzMOt?
    z8$R8*^Hq|x{zWQ)F{htYKh=q*td=R~1T+RScrIAQON1rd)m-xy0vSHUg*S{Yr?r`j
    z$8}8BL`*v~nuZsrVfJY*7~K?0RK<AKGi|z2qfZnhyWI6Fj{=+HaDx|{i!OJMA1gz+
    zQFf+BRsyAELTNX|8=RQVtfuR`0dwX%-;K*Jsi|ygRM+#KJL^x+Vw=hZ(}lrUr9!Q3
    ztry&u*{i;;_^*Wv>|rYqL&s+Bh$qzqUMw3m&_;1YrCn`|viOa`uu+Ty?bn!tn@7w9
    zM2^QzQgiG4u_qKHyCqwlcMJWpAfq>v|F^?w&i3@2=fQ^gMorXz0~o-muKJcE;RsjD
    zHr8{Ib^qjo+gopSx72_D*&zVH3tL>nX=bxo+k9M(7vilScW$#u{_K;ZyqGD^^F{g0
    z=M1#`&!Ecgb_45In*+z@ow#{n#jEpjqg%2+Kk`h6?r!YF`_lDBI(;{tbbhxX-~NvJ
    zzyAx*0b9M`POO&Jz1;P#gWcf}uf4=6T=ulD-PdAed$2X`=D07^QFAY2-9;Yq<R_om
    zcHX<nQU3RqBSRp6e!2B!{_StueEW}o=1C*Z=w}Kt-9M)^=HoqYTNJ(Xp1*hKN5Ai(
    zg4%rQ9IDh!4Np~<mOoH+#DN_V|3zHKU_ekH4(4!l5n&Q1ab;&_5U_i`=WB(aVF#pf
    zjv;r%2SJ4}DTPoP%QkY!cR{>>V$Mf`8H5afhi#ysZJ_XHfktC42tp6AeT(OKB}7=@
    zw`kr+Sd^y(fB<ynmw7-aB_5*$>c@U=U<;WBdhfSyqbGm%XL_VoWu@gNZn8Q3hjj?H
    zbyV<WR6u}UNDT&<fVRhgW@mN}SaA{vfxriMb60%DX9^l<A|huBB4>xo2VEi<a={>i
    zCFp#HFnt+>Z7`;S1QBD_2ZM&EecR`Fgmo}DsA$gwUXry7&p>IH7liI6Z$+qyMF>6~
    zQ*WBKX-l|tB{P5Y2ZfhJ|5W>RNvxQE9^eSFAdCFxL_6aM2*(5h$c4Mua1sV#w&x09
    z06^3*3cH1dYuIKJSb=URMspV$a#)9<!EDorhaos^*XV~C^a+4y2--GygoubRm;o{f
    zZroRcHwcO6Hg1*3j+E$bKxlrO=rN!uZ%5dEp|^CS_i3Q@Q>n;`|5#c`m1=I%TC^yO
    zP83_Q_A^~LfM6(wxE6L2Mq$CYTgz~a4!DNO$cAXek#NX?8c2tCNR8C^0}&;HeVC1S
    zc7omrV}!VZhxmfo)eYsyi0Al-CBOrWm6M3IlQ_wfB_N5ESBdVp3+4xpnmCX3m|01P
    zgxisXw$M|b$AnF||Bv<;TK3n9=L3*c*alW;kW3&83292XI9m+Kg>vCwX&I4WM`4Gs
    zRcGjyy|;S~D1mj!hHmJF9T|s{!VBCWlGDfw%vO>Y^aCkrm@yU%DR_u1$byL&XpA_H
    zHF;=0Ntu<|lRN2%l4XhPc8Ny$j`4_o+u<=xNt)o3gqn7m__%ab>4Z;*mHP#dR4ALX
    zD2oMWi&{9A3%QH>Lx5=shFG<h61i1d1qQiik!UD^bg5wwPzcO<mmOI~8aQ`-X@_+P
    zn9Ik9*?E%Lhzu!qg58i}-WX%rwwN;Mg4y)|<H#d3S(A)cSUU-yKN+7sd1&m|37x3~
    z4*-uvS&#h5|CFO?nocR8P-$NT+G$itdY|T+_6M5**`Tl~i>+0gV2O3sf(^MCYhT8j
    zym^2Q2AmKnoWbapaXF$DhmmxNqRsi7=mMQ`$O{-yoq=hcf!PE<AebhJa)s%gDT!@?
    zIG&7|U7v8Ck2#Zu*Pb=l36wdX@d=-gXP@qNc}4k|NSTyLS(;bclu(I;_y~Uqs-T}H
    zf3mQO4oaq}CZV?JL;x6;TlZxI$OOIVg}>==VYimSNMYcx40vjBCwd4Dn4-?<oEwQQ
    zdszt78I3d=nA2F2+qs=O8Vruwoxu>P-8p^V84TKnsVsPi=E<ox$DZv8UOvgBqAH(2
    zxeJAL|Ej54l=oSbM%k)v@BsaprL+2!S-JsQN}vP^fBNX9Un-_UwO@`<rg1`ru{evd
    z7zu5Vi?Tokw}_!YgAH70Rk=t7jNpa9iLDYAkzinvTQ#B<>43-Srzpytff}g8hE{G!
    zsCHPWc9^K|%7@#TosXJgg^3JNFsa@Nh?m-pDhQtCsj1x{lYn4&>^YO<m}3v{o}`+S
    z4a=}N2{);FpRCHNt!lCJSf#d5s~bzJO?j(5WuT|fguM!;pZ2R_3Zb=`vML*vvfv2*
    z7nc5qp}P5|T<E6D;G5aH3~T9@5>}!k>Zg4=uH{;;Dr&UD7KhTw3rc&a?+UMhshv7n
    z|Cl-&sgi27l-iA#3JS=ujom<=o7$M&ke&leu#6~!3R_qY>##hqwmI1e5AX~MtEv@y
    zu@)<Dwy+CNnYS8?gipD#8hfksm8%;dvaCt6ZGf_fTUsf*xK($Bwg{FBNtV=#p<bvz
    z)(Qm6zyv!>dpoPG)WEIX>a*Pnv_cC3r;EDHS+vcWu7%oWNgIbU+OAF;a!_l#HcGEj
    zORtoAwfp)BiK(@pfNjF7shS#S*@XvX%bqg0yp0F9ozSpsi<8kiy`stka4WZOtEzR|
    zs(72Xqq(<P>bK%+tAh)Fw!o`j>Z>M;xQJVujmxqs3zqXszt5VBZVIPBK!7+q|EF~N
    zxncLMKMMvzYr3dgv<G~b(iyw6>$FbGhc+sLxI4AGd$kpOg53$8!ppA$8?c&6utci7
    z+o!NHII+`9y(e6~Jn*&?yQ<vl!t;o?eXFrEEW<NA!ygN{A}g}`xWnqZz9<{N@yma<
    z7>jS<tYev>ya@zvTC<Fhxz!qmo-4(;6}kaDzz%rD2CT&@dak3;W=re3t{a$zI-?GJ
    zyV*Ix_PV3I%e$YjlE16L|H{Dv8>9~~lb;a6XA1~t3pWqIw$b~?fh@g$oW1%<w=ev?
    zcpJltOv5(Jw}9)&A6vMDtB*d+zU?cE?i<AN>#{MstoCca_)86M>R_9T|Fd+uvtd`9
    zq)WPB00CL7#aoQPU`)&Cvb491%MF~UHd>gxkjA-d!4?e1zw5P$>9w1R%tczHd(6yw
    zJi^dx!hoE@gS^7KAh-JY0Qt$i-mA!o?6-~#&X9boA`6u}Ot|^h!#?Z?n4HO*%*pfX
    ztf1Vcq+GN5tCkX$xu~qIZn?7~I>1>BuCjc(h2X`33dUbd#$&9@?~1#DIkgeIyJ}3S
    z$S~1X3&)o#yxr)v0eid|kh}t$$IZOQZ!5?pZPM5Kwt!&GE6vg_+|4f?&M{5Hr{D(U
    zJbF6Z2B+|8I{l^V+`gD>teb4C@La#4jG_ALg-CtM4wt#wipr_H{|pe&%BLIEvAo3v
    zy|iA8(6=ncxva~)?90A9wLf655-q6}O{v5DuUbpTZSB^}EVg^>%p-i#b<MUTe9bNG
    z(uuscGVRy4K+}PJ&gY!YJl)fuw$7a#)GQl|vB(53E6<^v)b^aYZ+gl*o6k_av%%P{
    zQ4OxH{K{6Hx}yLAys(^w%A5zC%d^YCyByYFjSOV{sIvVClKKN^P0`)y#u)9!Ui%5c
    zOT2V^yaX$@MasMnfZTEoy>@-lhK$_UT$K7*x7_T_ef`(qjKhSBzBrxJ-L1Ypz0NP&
    z292%Qo!qiSeT(t@tV>MU?5)2z`^1_p-^)<NI~&^f4cb;+|Il8{-(k$vxZK(dE!JX9
    z)?+=j-Fe#+o!e>6wZHwZ$j}WC{?-C(#}r=Ndwj5GOV`Y8($DST&~33=(7i<o)7K5o
    zG!57_jlO`L)0Ax3hV66;8s6ev-i__p>HXL_F3;_4&zgJL`8=mi?bL3`t)U&&uAIfB
    zjk-!++Nis<rA^TO&A<Wf+GPFO1wPx4O3`Pn;0w;%za7U8F0jJe))h{$cI@GDjoi!4
    z;exE@&&|@(z0KZi;^3^}ISkl@?c&}Y<89z{;XU3jOWuPn=sK?6_lxM1y}6dXx$!;U
    z^DWhpj^v@O<duH9yZ{0H4dqbo#jCy04n5!nF6spy|J$W*>I=@bsSf6=F6PJl))kKC
    zcKqgV>&I;_$R57ZATH<Bt;kw1!`5BrfGzB&K<qfZ!#vH?eg5a;9q5QF=r8NC&#vR^
    zo#^h(<ENa@nXTDU?b-D0%14g6`JL&f%jBeu>8734|6SU=FbaX$?yi01qb};PecK7%
    z<*2UetPb3+KH+qH=CS_awtnll4&8EIlz8yHBF@Ob{@0nHv8V9xd9K3~@7>7m=NAv?
    z&kpT2F6}sO)JsgtJf68DkJ<Aby7X<|M?UFbQ10VS+ArVa{oU!Pt<bux2cXW&@?P&`
    zZR+-p@2Q^eV1CSGPUdcn=EaTNVjJ)RPsrGe|J=DA;zg<Od5Z@c>+20a!wx^g5I^jI
    zUF_)G-8v29%)ac;9_<?M@u1A!Of2&7jqQ&P)dTG9L@w#4>&h^X_XHjDQQqH)`tI+}
    z3pbDRp|10!?(?U<;BKt%4o>v^Ugk*u+)Dr4OyBec5A_JY3kdJMzP<|zZwps{^;s|a
    z5zqAyZ~DZ(-C`g1-R*R(kMT2(_8LF!iO%*S|K8bt+49Y;yAKAv|Lw0#+AJUZF8}g)
    z-{j|h?&}WtfFJnrUikHX>W7c-iof{#UiAH*^Z-BkJWvLI-2H6({oD`!*Zbkftp)mt
    z`BLBcRFAh>|M@c<`nKTy4)6Y^KkUTb{{}3c39GNut{>x$01(*PI3ifkU_mAc6Jk?{
    z4G5EmKqOA22xOT>jLR-+)Ci{IGLH~If*}b;<fu_5QLY>XK@S9!g;4T>34!L#LcDa2
    z(#cDYPhLNQ2K`ag=uu=wlOki<)G4x0OuKxVI`z}lZlAJl)!NN#w+CRoJ_t+JELk39
    zc%)scb}iesYtQa_fQRlbyLfkL*}KaY-@k#i*a~bblVQS#4<kl=%9i6(ktIiNLzyzR
    zQ*8up*6gVBz=MvMh!)LoAw-A~Q&-GrQKM^)96io1Su!P<lrV9_WZ9Bs&6}fg^5n^j
    z=g*%)lj}K(wCK_wOP@aNf~-1K|Lj+_bN$+Vtk|+(&AKHYw>;akWbxM3YnSi)zkUY~
    zE}Yo@{l$kNM}8c6gXGFGyG%0zv2nz}f)G5235HBlC?ba<nurmJF2cwo*kb#rHj*Id
    z5TuksYzepCR<da(n1VBI#TAzm>Jg%vGwPA&qKj_2>8h*hDyy{8&PVUM3$LuR;5x57
    z^2oyDf%V$!>%9l!n@=#presVp`!aFtF)k;Ytg-+z!;CY|4m2}Cf<|Mop@k#_BEkvd
    z1j)4wHyo+9Wp2Z5Pu)uFEk&4QQn9$>bYc!D8D&h$My77e(K_p>+ObC;u?q6LA;lvL
    zNwzpePrW7WdVnwYp4{uN|H1H}GB7M#ZPgZc9@ElGTE)EV6l`+cY%_usG;>X09b!--
    zIDw#0A_^~J<RUO!Th>kvqrELe5=HcLCF6)&kvQal`UytnnEOH8A84fUhb|sTsybxU
    zl?qZFt?OdmNxP}48$7(iqpN)J{i?3AmPAV}P6;-T2PK(Y(#cZagHP2d2YYp5#I)3J
    z)><PQ(^fJM?CeYf)eO18ha57QqGOXa_CkxE9SJ0uqqU7BYEi28TH>_T*;}5G8@DKO
    z&9zZobfZ&O-FVrZS6+It>tf$epx#$ss=>;4$g(=s+Tb#{W)D=m8s00_z#yhl;)>I5
    z^%RRG>o4Pt1;RB~|IG}vdmscGTvKm2i%ppcm74|QSs*-ud0CmIrJ2N=Z@zY0$h-YE
    zT;+f+cSal2ZCAR@bzGX>rn|70Dtk-Um*1+dUef9ZxZb)`gF|I6Y}&`h%NA83o^Q%b
    z&{oV<#n=`e<BT=Nb?&;8x7$p+_ul(W=%G(}<z*;b_WJ5IVuYkS6R)ji#&PZ~Tk?<l
    zX_Rq&rkuHRg%*0LqdRAsY0yI-J?f~l%C}$st!BM0f(1~3uuBWT!Zs?rxCak>$;1L3
    zhN}7W&M=vv-~=aFK?)8}cpIb@W{fu`U4@W&%-dUoPIfXzM96w8Tb8l1XA#4BPjQRe
    z(@MaghZ5m%|0dYlAr5__2lf4IeV20|y5bi<`qfW~^uoiunux!t?Q4Jj+MfZ#(lxa(
    zBQ0IqA{M)7K-z^(3%MI31hsI+GyX+_T5#0`Dfl}Lau9AG1Yrnub)a08a9Afa7DHC}
    zM`K}cdxDf<wDeGtLmo0EIJ_ZBbT|qfK9XBMM9vT?S1$Guv0O)NS9Kn-L{QF4QdDW8
    z6rV;#R8CQL0_0*V2UrH#;USE88ROW<xH~kmk&V53V=sS)!8jK06z=%o+~&3`JlaiH
    z4AK?!Q0R~pT8|OaRF)vKSCZ`wPG$_KO(JjjO-AA{k}DY}Bqs^PO8x+S;i3`y+-I&w
    z9F3Eo|C|>+L)pZqi4qUUkYfI%m`W?+!IiA^q8CG@g}TU+mJ4)XE*)x^2v+QOZp@&?
    z+9t=z5c7D)tD_zB_)%s$6B{Xf6NN^~LX=XIklG|kHf4B7M1r%C;xs2s$LW)rrW1%l
    zY$qm(=)QIxO>>_lYPv$XM0}c3imI$)Q?d9|s74i*vK*Bx8M;7Q;?AMF<E1Wr`9=%A
    zQG>IZl}05)Om|==t;Y0dTRmFRlb$r1)1+%4={ikq4iXG!7|Hg|KoV{OmLxEhX(Ayx
    z)0#q3oam$^I>W_IJv<_^k%i|b<r!3?8r7ckv}YHFz(gnJvy@R=D!%q-)lhZgsa<4k
    z|5QVzP^<2Rp=WI9R#64fujaO*aV%?OG>TT=g7BDb4dI(iHd5qHQ?AmqD_!$CxMloR
    zuX+VTV5=+GZx$AuIbCc`8|&DehF7xVwX9|1d0x$?H@%##Us6F^%BL+AwN$jKRPFmz
    z)XI`nW8|;ywtB{{DrT3ut*BVr<_;|Z7p-c=Ra(=ktKkwiS1C;Ha;14!3=ht)+WhNY
    z`>IXr3f7w=R_qOdsMy7#Q?Zmx-()9?$??Kd#+WT?jq91&&(15yIfid3^D9f!<`-Z0
    zCGwB;JKO&{w0B+|FoAnHR<<d4n8RE!F|py`D@)jp6prRd=W5sHVi>O*#%qWF|N2*M
    zW|N!T^d_4Ndt&g$S<dK`>|`Yi<MZZO&+A=pQ9Vn~9qTx>J$@Q#ja*+M+t<irJaUrJ
    zxMW^BnZO99bS8*t=_#Yt9a7HnT66W_El;?@%B8E9#e8Z;NO!LqUUivQ9oRLOgaIjG
    zz#(g$NFbVc&bzMjcq#j9JqNp}z%Z&cimhXL<=C^5dfI23ID{rL+Q)|$XriT>Uq<)W
    z(E#T58=EX?OTUrQQO-1#H+}9aWBI|L_9&N=J6BVqTFk0G^Iccn;WS?t){L;Vtw+J;
    zS>Jls;O#XKeH~-U3R~gF{-m+d``JKyR^s`Db{DiDT52QO+ED#*wjEg8|6D@atKiNp
    zf+<L8lq*=%o2GK7v;1=8in_~lCAGX!UGKfp^~`(Sd8~bYLq^m(;6R*pt_MzVgWFl*
    z30L@@gDvsT#&Zat_++5r3vF3WU6%1R^2S}eXe4htz!dzplTBXfackP#=Wh9?UH)=V
    z&-~r*wz<x8zVm7BT<@<Ay1uWghco>9h5-LJ(T`5&q#qpEO@F$>8xM7fS6$G~_UyB{
    zo^gRPqYRqgeCIi@dC*Ta>>xkm5FV||A@DNwZ(KdN&mL~HpIi2`m%YoeOyN<}z3%cp
    zL*7%J>bu|j)oA|tngOo|!VA3UbFQ<Ul^$cpM}OhQhB)N$@CZCO|NFBS*8;S$p8c+O
    z-t$=eeAhI8<k^OOtETsA>q-7qlb;;+o3=FV(d~A1BR8#G-v3D5G0p#DgaEub-}}9K
    z(>vfhGX_92G^?&yI|Jm~s|Z9s<SV?Pqd>&VK!ke)29Q2s>jGm7K@c245u`rIgE;Jy
    zIPjY~?*qTC3q2SdKhLu&{(`pdLnHRPLDg$N)uSNyE4f}myZN&}BE&!2%e`m{KvNsQ
    zQ|rA1e8K|^zB5=r2b@9&yg~-3wZD1<FyKO=L$O;EI0}@kT06K7jI0hkFJMr^c(R5(
    zbHhil24sLkJ*&P)s6L3xzRIh<Xp1kb`#y$>K|$QS&~ril|LQ&m%0WeBBh$--)oZ;U
    zoHW>*MEWa2+Do@|bFd^tt~0=fPE0jV6hH#}Jp;VE1U$eqQ^37iGgj0$Zt_Cp6FLeE
    z!=x+2F+4*KjHfh=r#0loHvGjnY`8aU!x0<;sXIYr%)X0j!9UD87bHZ`V}@vy#%SC;
    ztMWb^yg}33#_n@OM(jaHWW8RJhBqQd3Npt`Fh>NFhLp2J`xC(b+rM}e#d(~^0yIE;
    z?7cJCN1jWyeJsU&)W-#6#a0A{EF{Q-JU&@m$XX;r$~wa|Ov7Ge!(fcVbfH6xB*qZD
    zLym+vkMzhrWJYI1EiIr%lRQa6ghpm8J!ND+ZcM#L{|rZ%^ucgE$8sFUn_S2Ft3P*q
    z$4wJO{}V-_{6BrnN2C15fXv5$T*?Iux_#uPDGa)UoXUjE!dbjZTI|3w)Jk2<#YgDH
    zH4MvNbVITX#*KtSV-!IY1j!-zNY84^JzPP#gtn8cOS@!-6-30B)W&X{NtqPKa3n``
    zv`KVS$8-!gcHFeYbce;1y)!sBc>KwDtjD2DwRm*GqSQyE)XdG~OrA?c25`!Oq{4%A
    zK!l7%)RaYFKt3|$Ks5A9uKY@06ieAG%d<2~i?jweOv^h2!H@jN>+`<0%s!DcJv_Ke
    z<dnvh+{>4I$-kV*)H6rvJj|~mN1a?p>{Lwq|9ea#ghzV(&c__e%sfiYJW79D%BKv-
    z(VRl6Y|pB+%Bq}2*W^n2oK4vT%i6?E+cZlM>`4DK#<q0J0EJ88oJ(a)PUPH1;bcze
    zbi}rN&cGy02^Gx4q)^60%u`^;OgIBeYfQ#;hsO+05S`5L98t?$%JT$K^juLEZP5i(
    z&-5Hk8Ffz@h0poS#rot*gS^kLT*LkRQ4rM4IJ8aP1keG!!vPi0;apPiL(sb<&hArC
    zZDdZFY*2EX$?2p}EtOF0w9dr*(oN&e5e?HZrN?;;&k*%T18h+?ZBrPH(J7468l}-#
    z<iIS%Q9H#`23W&Ay-%_HQL`jV+x*cX|F8xlZA&CgQbjFLCLIDNwMzw+(zfi&2MtgM
    zh0y4%P)*fT!Q4{8ywDE~RWJ?Fb0byn98pr;M->fGRdrKWWy<u7(JW-o8J$zA%+rI!
    z)vxS8U<gaG{8RlLQr!$v{nX90EL3ErK15yAw_H-;yunAU%P5Uf1$9tMy;hjS(sayJ
    zPvul^{nT{SPB8sYc1+bV712~Z(N|qpb{$AKwN-evQ>~;=U+qzQ?NwkP%U-pHeAP`s
    z6;^)@)FL(1wj|g^Jy=GK){`7iY8_B(ok@ee)Hn#t)SK8ikOm5!SaQ_ZP9;}zd`x!y
    z)R6s9kR?-dWz|(}RaRwJmPOAO|8-ZGrB!&H*QtC2_N-Tf?A3eKS$^f&d<EEm4O(OM
    z*JSlgL%l<TMc8MZRAvBxrlm%wmBy%jTIFn5hrL>dm4@itTCLsKOwicF1Y3?x$4(7b
    zktJ8O9a(7rS(I(tm2FwLWzz)&hB!R~gS6Wj&DlBK(LLqWJ;hg@^;y8pSHk62e?45p
    zP28a!+GK6qq*dB&lv=2rT*-af0FBaz4N$GERL!l}2gTU0C0nl*TeAIHE$!Hl1zn6q
    z+c-eml5N$LMbA}T+kK?nGq7ED#a$MqTf3E0X_!jA?OTI9$l)bk;qBAF9b6wxULQ5w
    zA?;ViMchJ_UPG<k#tqQN|2^2Kb=vKf-0lSc$Q@9v%>--M*6}^xArQ>;4cpR{hOt#$
    z_%+)LUES1W%-aRgat+y3eOp%5T^6NX0A|wwMo+rs-2(32Jw;%>9p2#WTjAZ+oK;@H
    z?b*TYS;K|i=p|YWu3qcyVC)Uw$mQOr6=4##T&%s+h>h6vP2cg2gZ1rN(~aMY#aJ2^
    z-O`=m)UDsvRa>^T-?jB&AHLnS9bzFaVj>>mBfecFPU0b^TOvN-17?8Vz1s!O!UnG5
    z!Tnna-q*r4+zkHW#7$NWMpiNg;kGPcG#=rmJ=pTqTnFvi6;@x2U15%`VT`Tg(4}EL
    z-eb{a%+$SKj0I%;|FvHr{$VCQVkS1?C0^u2K7%;uT`2BdNS;Evo!h@X-UZ%bP3~eZ
    z24hcNTn`TAf+geZJ>wBhV*ub@r)|s4U13)4TsV&97v5MIuHlP~;rNB$J>FwKF61F5
    zWFlr{VLs$xPUN+HWJFeEX^7+k28Jo_RZKqKOWtHIp5SWsVhsLd>OEY?CFKuB*i>#>
    zRUT(?=H6D0V|13~uGLzOW#M&p=UtBHU9Mw!)?+`W-(;p?BMxMK-s2*EWMn>OM;2m%
    z7HDVw=iXiBWrpMeUgl=b;s~bZYMyA_EZl6?W^L|f5BBC!R%LN6XOK>1@P%VoPUkqL
    z;UU0bJ%(q{|DE9&#${XHWzn_gdah@JMq(r;=$j^D93JR^KITAHX^eGdg;r>1KIoox
    z=!dpjiI(6Ce(HXWYJZh#jOOMI=3qm0;jBhhICf!a5NWUuXR#LPHBM)BHtTgx>s@B)
    znr>^i-sPL#=YE#woxbUxj_14H>3$|^g=T;_AnIN<YNd8)rf%wrK5T66<f_(Yt)AYL
    zM%KnQ)XjC_LJe!nzU+~vV-{xXJ5Jw=6>9f=YtbHU(#Gl1Zfl*sW4$hFU;t?M1?<0$
    z11Uag!9HrH9_(I??ba^bI56D5wuaaSZs87Y;vR0{KJMPu&E!6A;7)04kOqH+ZpeOY
    z%J%B9{~l+sE@$v{VeWqHIri%AreUxC?#@1K^FD9%E^XJ|<ImRPzrN$vcI4fDZ`wZU
    z`rhsN&TYc|?R@=h=*I8g?ri}_Zsjg;<|gpyre5iOZU%pB>K+2jj__3OZVC_W&`#;l
    ze(v@b>J1O=4*zfq$LsX2=?`~g3kPu&_iz<=?bdc*zgFnae(|DSX5GHn{C@4f&Trz*
    zZ{r?t0dMUd4|3xU^4_Ly=$3BoK5_=<Zs3;i2xsp-)^H35@jI^a6~FQn$MO*;aV?+W
    z6#w!SpYJN~@D_jX&(?7o*KswkaUhRvA187+_i;HVasgNJI1upOKJq-zb8B#N%uaH4
    z|8`#t@9c|}aw@;`E>HCKc3(u#avXki@vdht$8s>QbS*D)6ZdjQws8*^^E=n^Pp|P%
    zzjNTm^HLvfQ@`_6PxVuO^F2>#?%s0^*K^<wZ9l(k4{!8b&vjK-bw&U64Y%}4|LjH&
    z_FXr2VFz{;?{!}v^#O<hcmCK8b7pt;UT<wxr}kFI^HpE<Yrl4FCv{cdc2Z|_0l#(2
    zj`nC5_g+VLb6<CMZ+BvUcX5yRW-s=4uXlI9cYM!xeP?%SXZC)-cW!6*QpfgQANYV@
    zcXjvnJ16(B-gkHBb%#Iqh@W_huXu~Uc#O|@jsI$f7io?Ec#scykso=IFL{dZc#k)E
    zmHJ<KmT!5NS9y*{`G1)Cu%7vU005iMd7a;Rp6_{_?|GmPdZ8bBqAz-*KYFB3dZk}_
    zrf+(k2LNLKYMTFfr{DQhu6nG`dad7juJ3xU4|)Iq2Az)soCg4@-+HsB`mbMmwr_j4
    ze|x2edY<R_i&cB4hx)UJd%fR#zVCajH+!BZ`>_89vd4PBFMPjGe8pdU#<zL7-+8*9
    z`Mh8H$DjPhzkJNk{J)omz)xk)NBq10e7o0t(l33}2m3gf`w6%FrpNoyKYiGbebRq?
    xn=kyZfBn{Hec9iA-dB96_w}E@`mPuL-Y<UR=lhzc`L)OW<8OZFclv+;06Wbp%m)Af
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsv-sprite.png b/mapbox-gl/closure-library/closure/goog/images/hsv-sprite.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..1dc6870e901f636915ce7e3b302ab2b4102f2cf1
    GIT binary patch
    literal 58142
    zcmcF}bCf5+vgWU?X<O5_ZB5(8v~Am*Hl}UczqW1Lc2C>Z&b{}&``+%^v-{V}bE>i`
    zPJ9uOU&g7b%!+UYIdOPc99RGV052&aq67edp#DAJpdtTCUTF>G{&wI_KO|M4|2A)E
    z<FLQ?F!mBZod5thlz$u`bDO^Je}#C?q8iT1cBam52972GA!9p36GAZyM-w+YM=K{n
    zVP%%oB{8tSdg%Yq6LvH)aJI0sB~-DnF#&KeFmW(2ayk|UfBkia{0{{USs894I~#g~
    zf7qdSx3LHC3b@-F7+IM(6B?SBS=jOs6A}^<6IvMa5v#MwGRoQuo0wZjcsiOWd&;R8
    zd0H89850Ze!}7Xw{{`EaI2#bU+gRH=al7*o|I;S--|K&f8Hfr0sp4$KNBpm#HDnbC
    zh3y<o2wCZw=!_UySP5C#>6tm0xj5Kq36%|;38f5d37I(vnHd@Xo|w6rn7CP){s!~U
    zM$8ZUx94><Hsw|l5&I9HfA9E+&7Ga?xfvMT+}!BhSm^B>%@~-txVRV?nHiXw>Hcca
    zIeFMR8@SWiI+6U#$bZ@qF>x|-w6OmtUH`CaU})#!%tuW84@duQ|1mfN`+qyKb@~tT
    z{?f?cZeY*AM9;|ZZ_9syynlt<|C|UrM-@9eYySVsobmnx`M)Xsm)HNon}`@VoACeT
    zgOiStgN}(qg^81!m6@BFS@XZ)|E2VQz-8@>ElfTBFK{+4Zbq*Efd4n8|A6x{{DX-9
    zM$A81|HqjA4>{rghcIKKfAeMU;%NQPX)!ipFtIkVF|l=a`pX&9|IV4Q5x1$Gqm6+x
    zzlDu~nF)ivtr;)Fe+mDe@%=06ziII|EgAlum;V|1zo+tl<l?`0{~s~`FQfmHPTDJm
    zp#cCwfTW0!ihE{5=X(ADdkifOh)Jv0<L8$=AB#<UJSU<cfUrp0<o9xNCp8OM{2s)&
    zJ+WB3z8BDETz@~1?40-L9*E~JhimkwYa@QBYvT8Ayl0T>P>?Up9=ot^Ka)>Co@c}D
    zHyaRvx(>|RPhb${-N(S|WPl&nbAJ!Pudnf50)%(MEnu(!;2mKH5ikz_)gLSX^1kbl
    z({si64l*q7_Ab|-qwzc+ur2g1w}(iW&9LJa<!-bqz<($9BCvTQ;M{@#%$M^ev?l=g
    z1izjS_?o$4IlC7?c$e&7X8?e2>^%Av?Ar%CbNKrOJ!4|{0zN@s`~V$0ErMSRNM9lQ
    z96f&i9W~E>01fBzuaWmJh+{i{+#WwL-+*V3<8GlZ4!keW8?bA*nmB^5z~i^@?H$JF
    zX@Z_<YkSwq!>5P8fniVAgFQp_Y`?XBgvQBbJ$e=1Y=d-wd?NY_;D3gK`7w_@<KMg;
    z2Yx(3e4Shi?BTx$T?;_JpY-G)yb<)jvdn#s1Bro{c-?9H+mX*8dr(1zpNBEqg71(6
    zd~R4_euqy4_V2hoc7*hRFLe^(olvmuMW-DI$klquN4>6VVLq8XsIBKohWF%xH{4jX
    zdkTfb8^~+am@mLHBAy><aF@`#9{>Z-&;J>J#}Dv@A0q&uNBV+q0UPmpM(o)Z`a<3L
    zJK+8s3V)8UuLB-msAmd)i0Utf?K`tBlKE>yB!t(DXQ-6+WjB(9rSm))lt_P6p%w=#
    zhA1h2TJz~I1xmr9j~RrojEg%xid8Fp&NC@c=PgTPkK<Y@aVoJuah>N>C6lR=H@G8~
    zMmy;%g1OIVtLa951gL#7Rpjr}<tBJOq65a)m3j}8YBv`jq<6sinMBs1;`+)el#LK4
    zo9QHqu@T1|LKvuFr(7z3f?;h7kzzRkaiH$0g4u+re0tk_-^Ej<-<F=Y9M3#&%Hp)B
    z+Sh@dE>IL141}de4JF7E7J_sK;+Q9VzqB$e8q?qHNV1ReROvxh75jw_`56Rz<F<>x
    zQ!F0G`H3_Hj_Fr{GT%5o60VE**4>LP*sKc?5-L$-yZx@bn?`XflHkI0c=K8?jaPPB
    z-MGJ*)r#F>VMZenHV5VX?`?HC$Z6Iwv;FKbMLTTH5`)>!<7b_pO}9EL#Wn$0@GY$8
    z#(@sW5DULw;h`+_V}xmcd?(aTTgs2iXLx{{>|OkjZfyRX@mpPv8!`$geFGu3SK(({
    zuwhqen7dz(73|s0wQx9O*H~v|2knZNQj3DS+d8x-aAG*gJ&G4>i7OoG+F|TfwD6e$
    zj1pQ*f{^?{!#;HCR(;9QW|(8QC;S#k1G8=@*mR7EQHLr4u%iVP1H&rVbqyc~Z%hS*
    z0BE|Na9ZHYiNfGw+&X#-yme#*%Nq;0y<C3c>eC`&jwI<jMKeP)7sLAeh$2hbVVr)X
    z(ATlo0PPbsT-0DSh{xOrhl6Y(IucBg(}tl1r1|qmvcdRQQ!u?WmcL3Un_%ctXgu$f
    z-ojUGAze3}dfk6dq8R2G>O@Y|V8o#eaRkWC_yu~%@*2YI)Fa~407MH+b?gGt{UMJ0
    z%jm%v7s#2HNC^b{IysD@Zxu^mShMu{QLT~zVfIHj!lAgk(yT={cYlB;ec5;f7^iiA
    zSl%$Qd-zV&BcX9=k65n6mfEe*dp>E$XFE&l0h(7xCJu5Xe%_sgA_cvzo-WJgD@ppj
    znNJnH!Qk$yqWI|(r@UY<w$uQCMi;}~{xQ~QQkFOT5^Y%u07t!`YYLn(2K~pu9+-BR
    zFyZKQFdQZZz6OGk%!7as7Cmh>I0{rs5Qd<B>mx1(J!!>xOyt0N<JfQxI)~$+u}hDI
    zgP)d#0=Odh-dGw9X1Z4z&GOo<=<83YMt4Saq$sX{$QTnqi~cId-~}lb?NDJZ#R3W-
    zDB))|R%~v$neqK7al-zh6VOwQZ89<@-;wFaluB$S&>;js%9RB}DiSmF+zE+<E(iqW
    zN6US1Dk=HXm-(*tv2*IhI7;K+Peu%v2^ktth&Y`iX1C^Y<m>@}Q-O?%p(8{-c9{Fk
    znV5`~uAFrRXsyrsy7nAAt!(>+1vakOK$=^gXqLVs8_1~!12*eMb$SryDR<C^5)kJY
    z{WMHj>H>xJpmpk1RI$y@j}Ku<MNlB6kS!se@{HsmdnGkBzzZ^%0JeU*ZetdRC(YUb
    z9$7Ib?$yYw14ii?utOMu@^gk*YR9Mq$PAgMfu;%0t2KGrT>sPtjOw|C2t!q-f<eP6
    zBZilbgNSR!h|)0pfrq*b1{3FNPo6&enwc4{j$6uKew;Rl!YkJ4nwtige`z=p>GmxB
    z%^bwjUJ;!O%$xu*MVOAGQVR2%0MUb|T&fa#knK{{58%<TenluS&eQppkHNZFi-~HQ
    zeN>F1;0?TS(*~9s=TmvuC^w4WXn;VWO88?KX@EC8mhtlh)lw__8-ZUtDIDx@nH87O
    zlyxVwhtmwEraC(0ytM}ie_QK?6pUayUN>6%#i_>!LUm0GwLLJGQX1s>T|JT`xs_c9
    zT<Ur49&b<VbxR}M6ZZUN<TZ0+1U_~KZzQ;PHyU{Za|Q2%C<Rq=U$k;@6=Z-hj5r^E
    zPY{Q;hu-VhQ#Euo0tN{l`pw6I0?b^+I$3=D6WfE2$9SIY0goYew#CD-nubV}L<!DN
    z)!IJ{bt>sKo@Dc}4+Nv&z^f>G&J?!^WEQlD>iUBl)#;~$Wj>oHj5e!7k>ds~n;*(l
    zFaEduYzrTxv5`%S?u+!yF&HID>+I>y98}DStK33WwoBcrC~x<>gI`T=L7d!Q*`}JV
    z_mvpKRh6C2>z6rKK3%N9eb|{w$L~XZxso{bRvAzzZY{@I80l92PYvE|B@RG6f$q4C
    ztT(Ks9Qo+&GYmf~K%!Go_2I-Zm^VUOs!N5`Z!!K!blf}t4Z3pqyPae)KL=oR`LH(k
    zF>doQ1@i~+OE)*z^mQym9ZCeJ9zqvfS4NGUmjAS~LNgekM{t%dpC5ysEl8P&*8__n
    zx*=C%6~y7zb5pL$@o9)}&nP{=uJlP;JL<$_zMt9au-k__FpjwcH!f9K^`MMjrRp8v
    zeg~Md+=rV(L@Kim3H>g1`?dqQx6bpWnFp7$$MGDRM69Uc|7{@n5V%7ZwkN#7;178t
    zF&mqdZ~km#j>h}8^=Gl*6Y|<sAA6(o0W$2e0peMy^(Ier=6o$mzX&zpPs$`XPEZVe
    z4=w*@_}Oo)`6R+48RW>;uC$=!g!OsmBe(<5=o+%PwiB?lugO2HbB%<rRboQ`>7nN&
    zV;g9iq+TJCY7VIVY(8%Yh(6k$`F6j?TOgXwMA~bjHxI)vqvYX|4>!3)x5bHCeeZss
    zfG$8a7On#)iqX&>1_OHwZV%i5+#G%AX|G^&F0|jVwjQ*2{#EOFgHX!HYn)fuxC1f1
    zr(~GnZ?hjGJZ^xnH?CVBhww!K_3myz*?zoJF9*mVtu*eWpYsauC|;ZJd4?Cf7npO}
    zN`NJ&kDRr;vgnfEu>O;XhZW4$BZrXu2#UNhNuK*r-t@U2eO4WMJ<U!;_4iAWyxnd~
    zt7lJX$LjrGCoe;aBQiQySTHtE7pc_>qM#nz7kJ|d1t(MX`&Et!`&=zyTItVB<1qw&
    z{fL8IXIHA7ir~VP21mlRhE-;%(a!S`VS+;$1QAQWI(O+Hvrdi69`8G#+|)a=Pabp_
    z!s~v9%vj)DsupvShx0(q$_6w1_Dar=8EXT)52&C*7D{y2oY81(kFBNCM0?2()ttMu
    zl^G}H(`VzG8p&5om3lD2thj*c+SQFFUiy-V|85={bznLrgRpwYdgVM3I!Ig^Thc<y
    z&7ETlCE**x;p0Is6^5xWD+3y&*BxsPH9RfxNU1CLN0mw6q;;1_Mo?KK-MA8rlx5mT
    zs`-@3SZKuV<Jlsb90t5A)Cuf%Zou;;ahp-=ldW0%Qc7Lze{5NF#m;@HFVncU6ynYM
    z*U?l-W{t;ZXxFW+v527zBt=j~|9B1mX4a&p9vwN)XSu8=VrB^qE|?xjwe^jju-zq*
    z1q3QpA5{T0#uhUz#8TSTjZ8xO8Kzel4lP|y?ZSc}f*8yER_xNs+stPh9XPaf&^-0)
    z0a&0Vd8KNe9&IJc<EbgAP~4AJh|z~AqP-xc5LdV8AdXJQ2?2Q;s$Ibuh3zN-_9H5G
    zWi5ez9!+APe@5rv1E&=PgT#k#exPZ$VTp9DyKFR#8}&gQGzE7Iv1lj>T)WtW4pB{i
    z!2~>o0WMNxt4>nXz{s1K0#`w^4Ciq3UE#iir|yqA?vso(o-d`}^87pIa-^BO7JHXk
    z!YW`fFKe!b4T}p2{&>oYRl19IY+Ws{Q+W9HIg-X1Iwx~Z#k7khB@HyAFnMm8r=Pi~
    zFY6_<%#O(Sh?O0D1Uz=-PuI0z+&I7TF_W@Jue_BcUfmO^2Zm4?{D++rCletHfs>5q
    zY2G|6p}%8h9XOc%u9P1vUPbNrt)b9)(Y*f%=>vz!tIgD4&9W{l+G03!<cil8DVDJo
    zR_V}ULL*uedrzRm{IyD0cINODU(!zv7pW4qMYBsZ7^sWfqU%nSEV59g$#0$FC!Fbs
    zGWHK3M_j;;hd^>c$)1-#q0E>Fs;>UQCH1&kLpc8VPhY{3kC?y|t6Pl+cJiK<rCo2}
    z?{IY*-)L>l=U=5pFq|jNO8Ulg=ntxByp>=d5SsFAY=*BIAJs{Db7#YzEl{i`g+H*8
    zi6edm!X3el^9?56)0K|esq%XSbSdF1_)Vs1_*U<kaW8H(-<W^;?8rFFinDLpRhxC&
    z8mer~AIue&hVv)p4P2)#{+i}Uc!`<}xQMjK@a4Etcl`<j3#g=!#MOz}=EUHZ5?S*H
    zf903$ue`+x8@jvf^6Vll#_C1i0?O{udLpS8gGM+g3yd?6D!``GFy-+zVT5VQB=|<P
    zQ*&f3vDaYvgGYG`9a-N3-xVNp^{qYxx?Qs*leX7bwsb{h9^9SiA;VecgV->hl9x)n
    zyj^C{6q@0+dAq)(%pCoebbOxkV)cKFG+9j9Xlz^4lysxs>(^Iw3ibx{RKHrG+O3%2
    z(OL47DY2nFu2Ynr1DuM57R`-3es6=I{k&>*WU(<d8I(HiEd1KJAKEjzRX%Ccdl8ni
    zcFU3il2joII*@gRII86W0zz66$Wa{u6O?{_G3n-LTt^>=Sa9M`5L}#0R1DyZMH5K+
    zRuOHwXdfy)pkd0kSeA^jlq|vbZ@l_XUID{S>F^rl(WF>59ACSQT28lZF9BGMXge}`
    z83_P=ry%t}=6A4_#))<5MER3V#wsem6Pzxh-&gJ({N2Lba7kwSZxrLOc89As-b<Hd
    zT_5WmT(K?PewQAP%@=`FFW(StMykGnZC+yJ$+2*}r>#6%42<WyeH|57AQ^PbVL)4(
    z*aL)UWO;haz2tjtPS)0Zs5Kg)izS{4)yyMD%S7f<8jIF@^^@un^;BA%Dfl#!fU&pU
    zPpd*K43Jy)1qQy$+g7XSTUSFXqwZBRv7OMV0n4z!MP@+fJknJUTT_B+T#yUd+(!pb
    zB{X%}Q5JV%Y=01h4RB59vs#rGt!20T2dysAu$WuGD5mx&0$F~izDCXAD4o=Otoj+v
    zJ!dt#sHXk&;x0!AGCvjWUI|1g`<O}nT412JG|*YiC9$SOKdglmt~&r)AYLPRz8U-k
    zG|?w_Hf4%L?JBlkI&<`?hi@`*0);dQh@rlWo>`z3FrX<>5jr#%IZo+`k7|c(BRgZy
    z<ZUzY?GUEqg9CG?ReF#3N2W}2_v3R<?Z!==zIem$d)T^u8p*^*)C^(_BL11`(m9$(
    z+{GCa+=sUZ-~GT{<~_&X<;7>P-*UT!uqGVQIv?Og{N-i}c;(poZgh035zyS?%UjHm
    zwk4(EVseU<zxl;+j9?9WzkG5G5WCUcdxn}GAr5>t6YyO=LVmD`oq5rNrBhxudG$Ha
    za-d2QH1>i44g{F_KDu_mPuZOKGBU?5@Q;s``BXLwss{(XTO1v!WZw+W$x_y{x2=%(
    zW=b&i_a&+e$RGH-x4mf3cKO(<5wEHgJt^lPkBR(GRhwWX;U;x~Cx*2SstMKJK)Y}}
    zonEc)ix0D^C$xHX6LC*QU9C!5c5sw(gO7C-t9Huz-au#7CY8CDcl|evt2-dI?+{Mg
    zsExc28_oQaCCEyS@A!5F4-jgn*3hG($-PykKRlh?8MC9j5?;jf1vpjehOU7}1Utl%
    zjLhOIc_l=zFIoJ(M}y(3Qav1n{(S)V7NT|zct26AiGLrX-H2}jGKU6VL^n*DxmCtQ
    zA{B5d``EbN9DLFS43@Z&xd$fZlmm?~TH9R^`tg1y;o_ycO*^g-l!}rGBjw)vvz-vz
    zP!<Fnup`m9re*mUII`^Nmf^P<JJPF7Hz3=u_o%zzWB{b&vtLovHcjwpnICPw6LW&&
    zmiIZ`BAA6JS(h^icPY1yG1$lY4nFX5Wh$8$Kqv(V&V00w<bPLy@)Srs6@$<4jCTMo
    zqCi9t=s#ZRF|g_KU9g4G)@L;7V|oo%BpYy?NX82^1p72dDQ5uhiHGK}jeP@xA{ni~
    zr>4BH>`Qq!Q}Pyu++>A7Z<9uk6M4K4(%+$I;cE|6ld%aW20Aaa5nw<_KiFIQSmsw!
    z?=9F(Q4`1??^#OWjR#AvzChc_%po<18}W064kv&+A~6WAq_H(jM>oC?Fk`j!ucZDX
    zj7BbiTTh-UCL*E66{>50f0EED!#GmHnzf{tO4&-AJc~PL&N4PZbcj-I;kL5jXD;kK
    z@_0z<;^Z--LZHqdj3be8-7Ec(e4U;J`#hEx_jZlxtxA0J>f<*a1*g(V;l<I1^Y4fd
    zUYbS7d_Fv>8mTp@`4UzO`-c%GN_Tac<E*E{Q{rY<-)x`7emZ)K-?U|CE?NFWi8bFl
    zeA9C23V<5~5v2cdb2)1B3&Ip4(fzeta58Gtt~DLw7}S9t1kE@QsWFKB)F(KZyu03b
    z1%`UQv6{S1jT!_N=X1be;7KS<vX=!^%cD1DE3?yqk$>G6k5JT00x{uXf_1m|Gy$u5
    z)K=$Y27~F<$w9TV-y*uP)czdC${u8bJ4Qm*Jd8BtxB_oQgc+<Qwo_Xh)V_kJ(kHkh
    zu|P-7-^MAZ4j%jumo}5`kbA&Dyh}0G?d+Z79x5Zc&2Z`xC=;@V&l|di_0F%o5V;u;
    zlci5F#?^y{`;aQRiD{K+6N=CuO*s7(6g)5-9v$Vq8}n<hO-=WdKd5}3PPG@1d|ara
    zT}n=GA@lc~Pg~g`+0Etv5+mbJeZj#Hkt-I|Sd!whg={ZCrW=M1B3n|C0b4D5@|?Xi
    zQUUPj2Uf>nuiMWZ!PJi_A$@pBs!#o6WBQSED*4nh7QzN1>zL&vR^%f+hNag14c2k|
    zE9=}cB7)-MQEd6TlWk$|*=-ooJ(+JH&7Rr$cE?Y2-69p~72`x?7Pb0F%e%tff7qFQ
    z7TCDyX9;KkN||jmHdE++DS_G>VcxRIL*50zSt^@!E`XW;T&6T=k``~G9eBbkd*+h9
    zH7G~|i)7#FH}?Y>q%|*wSnB3MU)`aPZ@vvW^2Li>5P0avjqf~ts@+_Od~eiIDzaDL
    zzkK?!6S@_~e&SX<>0onGUFy|1w+jdX6bc(L0*V*Ks}5ZC=(bzKU6=1b77mA%+)Ec^
    zpS8{qcWfXkQm!OZz0-*C9c9>B*NXVmIX5zwB7(5<>vap3(6Wk@*8vGImN~t@nvb2X
    zqWwFzAZ(|1@qGbX#4C6YbQAfhrr-*61l(1#DG02Q;>50w1RqSAq)@#+cJNjKD<BZW
    z+qPIoPcRs(EiQ#8yxWlEN9+yrPRuti_*PEf&xuoFo<)sd3jD{4I+xRJEF6*348V5a
    z@T2o30>9Gmeb>;_+uM~|W<er}npE?yu+Pxd-D>uwuO!f3K4XVH8~%CwE=-qVIo;za
    ztM9H;I7jtFquU1XmgWZ*73$GjAQWdxk0!32qy;hk&J%{wBa8HX!tFT(k4)`5fd|uA
    z8^^8p)g)HBg)5xmXDUB=+n3v3-Zjyr`z^j7!y4Rt0h{$Eh*x1Ry?CgDx46vI_|D;H
    zrn$`twEeftq8z~oXLzMTCtc)iLk-x2e3@SqVmwo!Xjj>ll&L%%AcN@PTca-7Wrwl!
    zQB`*>`u33zZoMGXOC9i%%<m$2;K`wtyj#~|a_I}nvDj(8wAQe4+NV33W%+pO*HrRZ
    za6GWMaSpb#AHvNTJow0GV70f{#E%ds8Ws-iNZ?l%dXhls!X;vF2VQpqw-*nQE9ucT
    zZ7p;P8b29?ZIP$&x1Pi6oL{T<vfXud-+H`Mvz4ME==%F;?tVhrCg;Gsgm?_l<Vjr9
    zF_Dkesh>g?$~^3l4@9qIphv3hJINwVxLF8PR@InEc|=8}(BdmEaD8~aL*e-mi)3LP
    z?|lDxk6|FG?nn=~4%x?dkSXLY%|0F>KtDP~c#0b$+)hwuo#=1EnAVj9TkHPdTMlVv
    z!RQaERQ^?_Rmn@Tiyv1)ai)>ij%|c~yuHhwo!+zWmPmJZen-})s9)fLIhl0*I+wDq
    zyESEyYGl=Y>*&2Ly{cX-e<R7i3A`O1aGyXFM0#;{R!Vf5&J-gQS@8Tyy}XBmu^%@B
    zZG=)2hnqS`^g1Naj`h|F@o?{6!aOX%Xr{2W@^s@!x!sT~pWRz)!JvxZ7y`Bs0Oh))
    zzeWe6{w5Z~eqPjTmPgWi`)7!SJ8kOFy)GK4qgfeXfwE;GdXH5_SX80E`nN+d+s<0z
    z&)6B*+AJB^7sIBBsrD={5+CtI9@z?sY_pt8%3zxV4@~)m0AI9Dw(NNlPxMK+p{C7T
    z1-!SZc1M@Yo}c+(T4wPWqV9)gRiEsaL|4+?MRGZg-m<V|104^5Q-Kq{qs%;hC$?oD
    z>~%BUqi3B~`#yl56)$3uZ*u)g#H8hTu5c1nWQ1BSy<L$~J%zWdzQ!;^+C_k1l&B-h
    zpd@%nhBNA5aW4oLp@oOi0FOt#k_T&GVm8$qtB`57jxriejeR0Y6Ng&e(J0^d@Cvov
    zpkGaw9z*Un-Gz{(IT^6Oe}2eUr#uoS$^%MJaS@>XNW7zclw(BYbphKT#2F!at`Q0o
    zL%t#676in2+ih(JKw2|&Wc$=y57b)n(UL(^d(gX=U^u_x`pjq2nBxNot*cX4DubX@
    z<nyOL^iKWxdr8jzq@CkXvDG3sIyY-ZgiCm<IY%4%_P7=AQw+B$&+%zGJ}}V&1L(^G
    zZUEM+>u*lCCwzFY3zg%BkNFJR2ahp2q~)s`g0n2!a4_U<bzD`o@e5e$t1%8OnC08;
    znZDbOvhB8B0kFu1L1xDqb_GcKExbtBk<nUFn15_yOj_PJoS%#_Ck%4VO@b{14LaSa
    z9UvP1K8a^nd3%o5dyUf%%l9;Y(srq&Hy8qlcdWKyq{iz_Mf45oD&oqfB(0`5@^#J}
    zMjSaHtQ_cHxdf{>>bR|EEnc&vHuh-8`cRIKJ0g*OwCUI~>b8<?+H>7}&m0ftG?xBE
    z11CPBfLTpbOIi+S4Pt?oh`$O0<Z~qmAq|f)PY+@TDjm*JH$JG=hL2#8j~yzl3RXLB
    z!tcRGTy2nBdBuEN|1~|@ef0_-y?}7XB!YpF$TbS@;IC5qLpO5>JYa<-!7~9o#?@LG
    zKV&`~alWP~JBW`=h(WwD>49cP*!`^h?g+Hb@;2mQ&PqiUdm1vF#XK<uQC^jsE0Hp6
    zJrW!m7b+95udt*Y%Vtu(0V46TRUF~x+X-2S`D%os#6W;kWpijQN6v-HZf2M$fm(Q}
    zu~?e%tfX7mb%K$<Vfu~2PSGRgRs@F)5)7P2mS=P}!wFeHapT+V44vH+snqfvW1*Bd
    z%3j!_|6nd++;8;*Dn%05tPpFaaY7D(_!GC9a2yKz13O~xAF(KX4epLetGsmyB0N=!
    z!<qvFvCOzhNn8$VGTP1=9|k`YR`i!V$ldY>E99;dgvWBFXK{wx-x>x_7Mh&MlWeP(
    z=oywF$z(fV5jN)mF3-v2OaT13;8g_uFjelF3RCAp2D|m2`%J4zOBAIGJs18;Pu&rr
    zj(|jeQp^K8aMUOe)H6u<wk?vC-_Hb*<tYb;JH+0x&j)x%r!ejwi((?Rg?Q2WR1@BQ
    zU%joYJETnaKNH$AzAW9sq7}O%g?Af<%3*b4)CPFgS6<@}x>#%N7strtVpj7`Ga7`y
    zwHbn1>#?*b#u2*J<PDuk=!t-aTePP{$FO7(%YRE(Q~EUwEbb-`_c-MT6jCMxWeFam
    zpMeQ42yF_t&sW@M8i^fH#a$@!V9X*Y$2O?#7?fTiztEmp1GeT_FfzjWtr0QBai7?m
    zyA@qsv!Jme)f}9y5o`-CCnf1z!R<!Mt^~tE3hJes2boqWkP>-|p5BtX_woccPTY^(
    z?Sdws0!Qhx!&l~rMW|rK+oqHc6rYX{R$TF(XoipH?~uzruZCD&;4quH{p#Yu30L~x
    zv~obNdtdKIE$6^1&`s{wnX+F$EE2=LvP<fw*1pE-oQ`%CESo9UQ~BJwF12+!4mPlH
    zPxd~RUF{l*Fn9J>FYnp!rd)s+`BjxH^J{a!xb698#`)wxc6^)S@elRoCPFRxKqPZK
    zHCo3v{5=VQ_3Ca^M3e3p6${^GJ;+4YL}0j%$E7v#!2={q6Ab;YxE%jL{u)px&CjMD
    zf6Y8nmN9iAa-%Q|k<33O?{R8n#7)Ce>^yQ49wd0(2J<Rs_;#Qf0;ZjJ)a)&E40FDi
    z(wrJ5Q0&p}m4fL9b?+=L1O+-GC(>=Ci^BZhiQS>D!H}m#=z~@s|Fah07^@r51?U#k
    zCCW6x>{7XmJ!f@OOUA4qHVl?+gL^$O>uuyb&~9dii+375H=*8sLS{?#uXky0cJLnp
    zt)@{Mx8LzELD*KOB)*WBUfs0%t<d3zZgw=9XZ~A5yispA;9DbbdV~t4%W*<Pmuigg
    z?dW!6Cw2cF^g2kSRb3I6Eunm3%n}M<8+2Bc^o5f}w%>l3%?8>*&D^PhYr)&$$=!_0
    zIpZK9vQ0Bj^BA80r_fP=Z{Up9DEvO^4`&R`helT#OBp2oGX5l#5MSOd>aimYm>aOW
    z+k+1{qURv^d_CuJ<@6a_d$HT;I!^U0(Xc5KqSsx4UsPV%E4neXXLlg5U+C>JDcXQB
    zSSum0wqt7?&)?-c&4ON_mz3w&^@{(L{j;)8&R6yGB*k!p#)}tG-hrQ=p(5r*>`iG0
    z)+D_?eXB<)Vr!k7>o@S#=!hZMZSihiyK>tv?e2L|FFi!|-K(0rTrgcgUXk2@za{oj
    zxAOR+>v_h1hnD?nXWe|gg{*{S`>XiGno@>9H$v%jX-Yq-<Q89hslsz((Cd!{T12kZ
    zB70#y@8R#Sb@c6d&wbER-UJOU1_u+(MMh|8Ub(R?(6ooh_wqPr$~M0JGVNG`a8oZi
    zgfgGiSHx)RZ<0|#dnv4&4d~EP8K{yOCP9CSfL!-@mOoCE5IUi7C4M}y`iLz4I%1X<
    z`LV6unSXG}*eQ&UiFW<ovFOSNt*f7xJ8wv^ZxT6)9Eo~zt9e{o_RW+x@t`opne?Y^
    zM)VDnfcU4{r7wZmEv%K~qc)1dd3<x_FPZ+oo6p;ke+pH+>+V7W*>aX|Y1I^9)rd-o
    zbnNQIA+T_$M!We8q&Kjhn32=9jdj1LzZ?Cgj|<4|R8K53X7d<S(n8%gk4()B3<2cP
    zjFV{6rnaTgr<q(R;(FrjU`!jY<(BeStiGSU`yL6Hmqf5w`;wyj3MPccKcFhyXEWW%
    z8ueZCl)X!iBA@=a&R#+oB{^EJMgI|8M=uu%-@Z95IOmrU$PQ9-i@JM9WG@y^tZ4R`
    zXg86I3?T7&`neN=SRvKd9P=on@Gb1InvCmR0Y{<f_+_)6s7R1bA$Dccr17UmNnBVP
    z&}<u;oUAFTx55XL-$$lSZ{U*21*a)JsSm3Vx0&g#py8!mVxc1XGKm*9U?e?QD8{i7
    zWnms11!N+cEPF$FfS$<}<_M!!4}{Pt`P&<E!`@G!Fu@Q(2vH?IO=S||SeIiRk2>Qj
    z`c<gE>1g>NrrkZppoGGUT3WXw8(C#_k<};dLzAAhhlp&HM`@I~6usbB)YvDgwY7v7
    zThjnOt6+MfnG&UuEwwQSNGBqnt?M?ip^CeGH}a%}3nM2iW0?IUErOFoQNHqcI@Ugu
    zc=!pAP?#A<4{go;4XVi;*beZLab^uACUl6t*|DF(B1^DZeOIvjUS3(4tXAwpkSdZ$
    z<$#HBoP^52h59`+oSiV7<mP}Dx~I>=hW{zho$8&p72JW^DSjPG)@2C+G{FYF8JWCJ
    zIbWbP<0Z@GH*DZSVagS>+bf*<q7L3jj-W5cJC`8YPA?>ffvQ>7TS=ocYACa6V;uhU
    z0F8jyXV<4CZz4qm5&AiFs@bT&KPWETzUXkiJeL=CTxhpfIX*bGE*@eyFA^-DZGWUD
    z&g3Ms5h$$f)kQZx+UXlOIx$Jfs(C0y$;{C~%<?(zwa|saoAUJ;BM3h^<6NyRK%OW{
    zn-WJw`)^f9$ts?>0~UPHo7*S4Kq83XXxT)a?XM?9pt=)&<`r$mQ*^(CT7#w1i<ZGj
    z(_5zLP{e^xJA(10R~*np=@5Tf3iX=pm?_6CGy;{m#|>J)RUedv)~K6rY-1{`3`2aU
    zpj2lS1J<<|tD)H(DzXXH;9wi%kBrxJ_j21>=q~B7vc|;2N@D~&xH`_G#uDtbZ6^uw
    zT8ez$K)_8GD1F!xxyC~$R&~3sXyh0qIhG$N@TKx}cfB5kpyOB&Y^-ZO68dcnE>5E4
    z#U>kk@>Os{7v}E|y_+~SBUZ=)yyLorwrK?0q7`Y3pYj5!mkP7JBVW<_hP6~@w9_>K
    zBQTVsjs9;;P<O9M5QdiupY16lyFd*t(^uf-yaV2lOYw!S{BFfrz`Z4Y?6o(%=%a*7
    zadS6g*H^~-lXIH@p_Nq&sNUw?fSU)>ba-g`v-~n!fWZ;hndTG1ET)fNvpH`Uea<E(
    z<Drh~65n>d|MiOpNcNPln#WOW-B-iLL|}xK0M2zwEJ62I9l_YhZx%n_kLqsB>K!cN
    zN=RjHNi5X%;HF2kdpf9dP~bwE_H20cJKPRLSGCHK*djx{?E-TpO{e(0kiMiva3)&B
    zDIDmD$)2qR#3&y=R`n|rUQ!VRd=Prh%tW!Xh->FeVNnO<API6LFGY>Fp1=9$IEY*S
    zbmC5Oabp|mg%xwwaoh!I<r_~cZYaoMw%=lBgjqhH0IUZ5z{DfOZS*8?u+iOxZz8-A
    za8XUPqFaW+6QOpI7iIEBd|li>N$ktnVc6ba2xhOPRa0AjB>F7hqt|mbjTF$3GB7ZL
    zy`yf8biZNe)Y*qvDEUCK;R`q)dCe{?6HN!2w<ne(_h%3%ghj@;!Qo16kse>`;p8hb
    zljP|+t=go&_3bEcIvw*3u#K1{^-jj_UiDt%&%Veqim>|85|SWCq}XfwADgb@NjqF3
    z%OC#8eCo9PWN$>dhl^2Pa7ltZ)2gPVGj)<3vbb}t;|hacwZ@3!0LFdI7=#^WbnKv+
    zJW^k5`xGZ|7ERw5d559r^L7%u`Q}pW1@l!!ALH%evWAPp9E)ysT9ROlQP}QQ0xz#B
    zM#_-qOh{y_?mty@i=W>34qskxFMNu*O@?UH|4^%QW0G=!%7(pn#RaI%2@8l7Ge2JY
    z7CnN(5cy|;t>BVZY||5;IH_`FOqnJs^W&uxFLk?Ah3*#7ssiNlvD#4{AG7z9pZMSM
    z-aFr2z@Z2cj@~#E?hYe<kuep!5SN8zx-)v|IFiJe@e78=Xp#;2q(zW4udH=rQd%$w
    z=Rox=phcq_`#rw2dXv01@6riC{fy@|83I>I%VWUz$&1+!bI6ycdZrva0i?yLy@t|d
    zOGcPE=|OdP?VJ}V2UNTjeT@X$9`eNnR?;=|n^lLQPX!d^-m~^MW!F65o}$99fAd?h
    z7VI{P*9;P-nIIWwBzOZ!KA33$eVeCjAt}mx7o^T|kx6mS)}v>LM46-AXpD<rrIDx1
    z?!3n-dJ2oF^s&l*5+wk*3MA6{Iq&T7Qiwa=3w4DaJYC_$jFl9LS>HCjP*D_dr>NJf
    zE$UZfWp5ztwC%jN=9xIqVVB>qmq8f}lR?l1@Y)6{^`?Njs<Wx_CmLKW88PGQ5&D{z
    zXHi(rGh<`qIF6Rwqik7fK)$4E;W_v(=}e5$yDAgGh{!m_KNWYKSc5ktnf?)t7mRWu
    z(d)w8XqtCQ1S&2Kc!6mZwkp}wi{_SHtBAZox8rx=)FOGw2SWKHF^cO=!~24<MkL>R
    zQ|ji)2PX3L>{{isvKctN!ZXf0EvGaBcWwo!)<!*O2CaqQ3l#D6?PvQvAsnpY9FAaW
    zt_>|Xxg}$4;L)DXLx~&cuT#escZYUAPGCs6wzi?jx&MC2U{Y6$IFe7%1pGKMdZ^6Y
    z3>!tSJkTxe$=q2<K$<>!Y4-vA(3I!p=@V52WzmO%KU?f-iA`-(%yHH9+EemeWE#MD
    zAKCt)cmNaA6RB*N)F4h+3(g_?yrt(%)Dj2BEiL|?a`V-THzE6aTEF1dDyjF{e-J<n
    zGCo71pO_$eF4))0<sOLZ)u-YyLpt58NQiT#nMo4+!m&n>8P4U-qyqj^0{Dy@4w(w4
    z04u={81tqlROIHPu=<)liBjwAqi<dV6f#6>&oq2V;)S|?<9Nf;?hm|=w>h!AMlvIU
    zje2t1ZYCHr|1-g`Be@1x?=GBN+tckrmidv8)T1A=)&lkW?RmAQS7D+TM}NZ+KKhV5
    zyn><yp&7*tRV2NO$gK;T1EB!n7zO5@I*6Ay5=KO%eouDjmEZU@-JW=A<+-pLeZMmN
    zI}zra{xtyqrMWOaIa^NW0*<Fs9UMs)FC6~%3u)T;ef?L`j(_&GU?l&%h&CXxJ_Ssn
    zTqg9#vk2E|_kg|wITL1~x_%qtKn6WEI7x+af9N1oNiV&?7n(CHXkj+udM7CAM;-|6
    zkoX?N_s`4eMSAkdV8^Cyh^$kN<BF%Jc>22UR-E~wZRohTy}NFGV=6tn7gof!`Q)#F
    zKm@PhAYsLB%kPuPX1-^2>@Dd2J`}0f-<V=^XL$1Nd7<#7V>98Crbt8)5#d4)vlH2h
    z{kP>V_MfY1B=xP15BQO`OXwjle*N;c(i7%TR(5lwQA&kn9f$yWn`)Jgx&|ea;MRhn
    zTgJX|<;_H=pO?@6ni_T|7hm&TbI>h%SC$tLbd=3(?Ke+1oqUpML{buIW$r#bL|^3d
    zeac6nsh}86Xbah;06iq4nYbeD&VO>Q9oG15SuTTY7Qx^vL&ZgE8Ya6~%W!qvX2kwV
    zOeZ?$Mc5in<QbvD^@rS&-4)VEREN4v-;r*Ka2E$PoIEv4DTwKw8`XYga+MehRw8%H
    z5`Tg)`l=<w!l>u7qnYh!ceQFDvl?|3J3ppc=k5bedg^7Zd*QKZ*js9PcDCDd+TtV@
    zy6*;CaS3hIH`$~46D#KSH@Do>Nvo|_wv$;+OZnFY8$6=VnEZA(OX6mVyc*H%R;awp
    zNr~h1F-lbz^;NCyAm6B+vHh@^3a_y{f|kJ3SPq046tDzx`?R|WQX=U()pz4*wZ<c7
    zGh2&d!t2~Sr;=Cd0pAbk(%C!Sr*{xBsbP!IPK*ID5oDswyUcCkgwE_X**O{C3adS)
    z@W`5mR)CN(k^1Dr_|Pa%x!}Dzid?hGH%EVOiAeYXS&UM_%OcgHKsYF%P)C9@y3dMF
    z*b<N1>i%5Ywd)@Dj5<5O;m<*4dXUC;dbR+ObQNyw@}8`{1hEH^P?_2uaRQ`04E~VG
    z5)zDooyMvt#tg!)J3ataZ`uTT|Mh~nSR92f5-G6wd;tEl+0mJGfqNo)rTGa{ISrMy
    zDww9~eyvufa?dr|qVFCf!91$I#&R;!jKS>G#rfLv%(gHch)Ra1YWDFS_u`lPdv^w#
    z3t;d)yJHzmDl{IOE#0I0)x%$W3*oRzL}$Q#?aJOSNZbA${5;x$(}A71EK~PFO{-7_
    z{b@FtP#Pd_6w0H|u&^|nF!>QOE)@p@*t*>0m#uf|&rz2*;yE_W-ErlgQdjlSF!&sM
    zcxp+wd-G)&I}M*JwBMY&bitx=Np{%h^iFisB6{uYsRm@7obv9|%0EptOiZ(UB&biQ
    zm9)mLQod>514xOFc<kHPx(}-nX{PiWglg6zuR?hfNLI4{?w{Q0w|}Ma7<k&dy(WM~
    z(1;Lv3}8Iv(<C+fcovZadQRTo;d=(S?l?)iHTzESW<qS<$_t>_M7FxjeP(PwJb{`>
    zLeu|RkBZ37Hl<0irz@&bn>$x!h!IV2Ao~-Q#&%DaAxK+!+>bW$T<<Ag;#H8+J$v)u
    zbgFfEBln|`8h=`uH<9>d$30cbWSMjeW?Wy`gM4d;Iv2r5o4eK|B@sjNgBOESR*=AE
    z?a~LNvMSSN>SCc+g*M@Ss*<>~iqC;T{T&37za?P@)ajj<4c<eKm#lVeOq|NuMtNom
    zrYdNrov)OwVsPR8&X@g4Jm=8sJ6Ze!03O9r?)GN0fW1C!h+H38kl>;(H{tyFPx}Ei
    z-{Uc5a<s|kl9lrtD<I*G^7VV+@&k5GdOfxa;=XRu2Rg{d<e9379r3sE`qKO~Tbe|D
    z^zd?<ui6$^bUFu<`!-_8;Zh0yv3<q^%W9$I7q@e642MYb<6lqgm`|*udDo?1I!--O
    z%f<U1hYfc%fK;uPn3B31vw*zAjJ3Xm@(uUMn0LIV)2@0EX!z$c_o{HRAHM`kHCG0-
    zPCPLrk-f_Ers?98LEwfV(QZkQIqA`|S+VqY^HtY$A4oO>L$`cI{Q_-je=3bMi8UV}
    z#~B|B7vXQcvc)IhH48)_#QR$FA3svX-^S@l=v_!L09Qt&w59a$%GJyG#OJ@!-eINk
    za~ahsH;jX30g$e|>YH$W-Ha;AaaGFxT^cuT>N!99=<hM#1=_c3R@tdfT_NvqSteI1
    zpE0Md-?QP&nyk(@ML^scO4rLsU(G^&ca6}6r@y*XLAhk+o3<#aNJ$6oXAuSwjyBLZ
    z+Yv_c*8V=sS6|WQYadzzvV4Ivv8_T0`D4-}+M3kDto_CT9jzMKE%p47J14>eqRQvM
    z-F@PA?qRv+35E`%+nY;!c!PJVVRew1^g$H&!%GuwdjOV}J2nyFK#8z?C(0NnIz>b~
    z0R+K06<ZC0dM#ie40)jR%lZ?;|Ij_#KS4*)$30LH8?BDy-Cd4#c`umuyO$LuEH)~3
    zwwLD*D8mwYXl5OU=HYr6STOAfC+r+sD84cyn&eTAAjw2_?ZdoOa=}s+oNtyn!Vjdv
    z%|DU?G=EyE3=c$rHG&mo$S$sB&&Q}!W)yRoqpCKsy+2l7JvF@HU6HEPau4OE(*3S!
    zw=<hl;;s*O^q(Lxyn%(9l-bObd`&VZ`TpiCAY1;<=4IeNNJxrTy!lG9rVyd|K;BNf
    zxdYuCCy4$%V_sD|Mm>guUU!maNk)nutjZt#IPZ_8@}YEbAI}LnXU>s|%F%&kDy#ai
    zroG->vk`;+yX;KOxje$32N-6a8bJS*m_C~r$sAb^(jZ@^l!K{&h@>tl|KM-Sb3m|%
    zym8wJ&2QGj-Sp$>KgtM>JZj$rPa)pldI~vz@7ke&_aWH<7KAhw0aYTSbPIQi-QLxk
    z9nCm78za|39x7uInA^6SSBoFmv7quNN1Xufp8?K5t_+>+d=~Q`k@J71$3h)AD7+@Q
    z3&B(`p}#kJKt?KuY;ncBEWs45Qgen#BpI5XY1#V+grxnsP6xqKpP~&Ilgfh58XUZD
    zuiv)T@G8HGg$x0I2?wMK&N(aO){*NY^_FR#&>t<l2;!(_d(v?Ba|awHH@^`pj5p>5
    ze|Tr>p^`o!lx=~GS;%CaRnVs1KF9Q)-kikvyB@J|C~1#w@h_izYc^s`J*oqd&=T%O
    zl(CMasp}L^!YQ{UeT^$6GiZmtel_ncJ9$>0?x*VcaY9wMwtlShl(+WYmJ=4hMW8&;
    zZkX|kReAIMgCc>oxnt!vb1AJp+pRn9?cqS{_8w-VO5koQ!KAcH{*haJ`dy-54lbO1
    z-$Ns4pUgF5_f2lQ*Wc#pG5B70dA+USkb+c7emjcg>gBsn_KjzDrRbgJp^z`Z6+H>j
    z=e)E4eX&_^ZR3g2CyQ;YKeeCF@^%Fn#{u3ni!(1G^_pRpw*R`+6e(K0DQ0YM$$sHe
    zZ)0pr^-2aP7_ChNH7+A9*tubcGDymd<Fk}>EW)|2zF3n~R`^u%r0NIp4GUDPy>{zW
    z2tXG)XMjM#bC`f|>l?$cs5AzLT#^HL{5ZyLE35^F`HM3t?)r^BTbh;%Oq1W?7}@}u
    zOQM&LrnizqZUVx%mf{`bHkHHl%M?e)yyZQl@uB0MD)J6yU&=20EyNDKSMU-<S2!;#
    zmzp1<qU`Xl+)O>GjM4LIA|?WdSog+A(JeDm_8l}PR<%IAk3|yPcH;F#?0A=mcRlCy
    z5$JVY*s6i6iMMY4H1bqBsTpIdNUOa_s^p%gVzp!<I#@OiEy)ieOF@XF@98$d<&{r|
    z`R(x(tj*@Q$obWSSixJdi>(L_A=My)sTl<~$<73sNRfd7>WO9u&k!pS;nv7G!;8DV
    zAl|}%-&fb&;N;tm!KX>f`oD{>Mi$p9_Zv;d`C&W4DHGr%;bdxJ+yr8iDQ5khll_`}
    zB3*8s@jpqmwByXakR7<27OfbR4;x<^&px%`ind!R;|9H_6H?Xr=`w7Mjum_tQIB05
    zIpklS^Y{hrad=Yt(kGVLnI1OoI*0d0^L}mPXq2*y{TcPGIEx0;@2nU7L-N~YZN=^A
    zp?#q7?V94{RSmId-CH_)jUBe*B@Qq6q)TLR^_1Cw28f@d<=}>ci~G?P%3`q>liLor
    zYiiyRBzT|YQZkaRq>madh0RmJ?TmEi3&o+-#}8Y;lh5&HV#o_BlmtO2+fT9UvN{(E
    zm<vDOqU*$FdS^)NQpTMFCFSshkfq;V78Z!h9E5<28NUz{X>50+-U@8&IgwWIAmlF!
    zCv$yuEVMX0FAGZ3X5hjQn<ZDXKA_PF>l(zK#8iG#pt;M8wHzMPz36J`*@8|%O{1}}
    zv34@uQs>)e`AQ&eTC$?D68E8+A{q`I(P@T*TO!v*a-oGF#UT6OWC^KFKMs@qTd9im
    ziZ<=TNzaG4c!krFNi%vuyuP5;O=}LHj-NSATjmD(Tc{wpOm2MNN##JRdYS3R&Wlzj
    zBD}k^aWHx8mTs*iG-Z<sSJKUxv6AOu@rBL)HbZrSMCkeVPh90sHHu_cxb<_~r_@|~
    zp<%fOI&$7L_&P|41onOk(q1x&UoJ;DV~WB9ezhZHNcv~ri-{NWm~o&rLwWs2<@=Pa
    zC;|&MM`~%zC>};wE9p%of<0=~-q6xQS2?DzBi5?Nkd}bVXT<6ro{-XW>#NxxFj%s7
    zr@}tRu6Ff*&{CgoR#DyBhik1;p?hjRbk+0X*q#xB6J_8Ee?Kjg#<13!SDtDlpYXiq
    zPuFATNB!}baR$L%#ZZ(_63+K#fYvH8;d;$t6*3%RDVyfudM`L5+YF(kg7y{)b>iB-
    zaU7TH)t2pd)?TC<Zq6^)?v1<lC{j1Vzxl<TE0V>#t9F3Yq3ycW;Gz)|K#>crKj0g&
    zJd<9Tsff@V{YCB2T+<+_H2*M{VN`d>!@&7j>0K4Fn5+H_qMLi1R>OT@wV>bu&VG29
    zF46!pMlN>lPh6A6=N1@E58>fT==@FKfxEb*V!1s|w?c9IkMOi&E6T<gaD*>wPD$R>
    zztig|B(C?axdqa;TT#$3bbbwnR9KJrI@c~<G$VA-!+6>Bsd|q#BOz^H{$61qaEw!`
    zptvz}VlNiuWF^9iG1m<NGvFcBKIb}>Onn<ee#ryUN5yD(glA@{X`cALN}u>jO}4LL
    zi8G-FZJcz&&}xu~WyE1i99dT8fcs9Eel~-acj_oD6NYQ0>zIA1f$(i4Yo9Wr#kGVj
    z`_?9c$?%;*ixvx-sYsNJ>)x(EErAvQ;jUf^A|ywn#^G?FVc@BQUx5;Vd_Ebr?{hZd
    z29=0mrLuG%xJbP*f%x_;cL1sxLuEHZ+XHA-oQ6s7PeE{#rw>Nn2TLy4p=nhthryLl
    zK^@xoTUAv)?UfE5%GeU+$sf>Tm4^)sY=Ki*)2+rR1h>t$wf$x7zayp^j55Jr@Y>S(
    z(~4|m2;sxWo|;8sjyxaD&Ec%&!p@PAsCv5LuITICv++=rm#G45CnVTSHa!BB8x%3g
    zr@hae%uFd_eKU&Y((>@YQq(HJn`W}|Oj$qHZ_hk`A<xfz26`{`J62;?l-GR4yr!{G
    zAZ(f;5<06AT^Tz>-mqPn*D=`-7xgIb#1WDy<D7$}dT9GV5fE8&+dv^7uiF?11O^R&
    z^Uq=pRK~DHt#b$KS4@aNlvGR_fw9e}OX#+QetU<6J%or&&La^=*d@W-qpSRJ5K2(o
    zStC%nGu8ogGoHmejE(q5ixisAq0PcS37>3(@D$5x6~3SWfkC3_s)S6fq44IW;EmqR
    zoD0}rjhMt6V`tN~E2Vf7ueR8qHzvYa-+pS~33{#PwI=LuzJ+ugTl#D?YD|BR<*Il*
    zLHX%Z$Gp)5@OGHY>h`kGJ6=@ObgbB(zuF>K1y)|E`gHaWy}VQ?(r3*YeZ(|$Q(M`G
    zWe}%}{QMEf__x^YB9|X8;j+>wX|v^Krk}dKd;4^h!S~MlQe4yaDNTy(E5FM#)RqJD
    zm~6@ghfG^mgPoZM;m3=MkNq4i{k>WeolWNzFW(P*Uf>;V7Jca7mDMfMe!UX1?G*a%
    zsg#aL6^=y#yD^fbs`dFvc>1uLu7ww5VQoskhXZxeabO4Mj!y(!&zw3$1d!8gaDLku
    z?}PKL7<(<ortbiJf=`&Y2Ald2|GWe<1@$nDCJj~BK~C?dK*26WU}U%&xAF0B6qb0O
    z(;~dO(++0Yfr$SFXh4_0jn(6oua+Brh)v5afZUtA(~4CJU9sdu^~WlHCF(T$*PY1i
    zBB?89IZnnnN~tbxA{G;I7P?=5My%Cg377SHRc8sW48BZY@h2`y2ga>jCx8pM-Z2K{
    ze*%dQ+0d4IqYMXl4Gos}d5Xm^dx&Vp0Qt)uW-*?nD^9Wyt?0}g0&p1+S8`&Rp^olM
    zzmEE>^HQ@>_IuLD8(^FZ)gpOY69K6h(7q_ude-8<i8LU5{eT?gDoUdG-~h8<tuuf5
    zvJDjn1pA3(g8hdxbVhODClA<h&%dGiPd)`_UHXMjT*l5xOg=y`)Zba(JI#LYaYci{
    zJV}{nf9`eXc($!RV{z5@JLm9xeR?w6wAbTn%BuSL{eDO)zLhaoZ7Gyba{DXoi0cHb
    z(MLzWu|V%D8u3mud_nZ-=1@G4Qu!ZM+<vDElOKLDk<gn{dCZJw&Vk&xpL1@0ZrfO3
    z9y<J(fBde$S4p~pVCg&nl=Y<=)GY3%pv^LnD+WopS@&1@sc>)}$h<#CXPlf(E@e<$
    zi#49B-a_Zrlk8WP<x>~a9iBT&AQhL5_r%Ji`-)CqjT-%^ez}Ts3Ef1btyryKDXbOG
    z#JKOp5zi*QivAN}=#zd%x{YVGZ%6FIy(ez(3eEtvUg-H`0zVf303ZNKL_t)xZsY^?
    zd~h&KP6FEjTvJSmBb&uxBxg!(^vil?rk>nWhr;US6MP~m8^8u&_3yDbh|`ju1At=5
    zYCM+%uQ3m5gq6=S=ARmlS*GYCDtjvZx5!h;xiklk-O@6$on!Bij!U{YaZP)9t^XP{
    zgrS7(A^Vo=#X7<w2C-Pt4XI_5i@mYsB;yEoZkzdsl?AZ_m<n)P8^2on)2lY4d@7Vq
    z>l8YUNlgA2ys6mD@`j#g(=b)X8N%Gxx^d;+c^AFg$I~7r@iB5uvt*w8Oc=wvl(Rin
    zss}xtw^Qddfky$7uIk_Fr2%<S)`dNG$q6X+-sTmuRqEHKDa+ljp6yzBzx++0u}khE
    zLBbHqL2XvmtF+>q2Z6i!20m@skvs;>b8=ew>J@)u;10^VGr*$F!#Snp$KpW`4M5Sc
    z^BCL$1G(fkEHpVy=eF#?>RFKskO0+TyS9=@pu~+s@v62@KpH5|n<#?<i__Y}qZQk;
    z8UzBPiU0I^MG#$i9U?1><V$VLPo)ttF>fixYxJhWD{u6HIwM}VK(OMb+*whmv5!3E
    zowKMJ$L6S}2%^c;A^sY~CQw9rokv_@jw)8k1uL_#rlV)NcX1|Gz#yLyrhdbDK?iS1
    zGoK?nYSXKV2K@;vDjvz`q6>FG>FL$bx^X^e@kIT5i6cPhGir?TT{`O1Jv~^5(^?R9
    zK8NS=7eHA|a*Pb(gm$O3eJ-)?ZO;Kjur@*v+<%-|n+}G9nrPr|YSwzwow|H1NvezQ
    z4ssgC#4AYK7bL0UQ<P$IvjarXbkM9`Ja<`GC&$7aFo3r9Owa*T{)cD$W-yWeNS`^d
    zu$XLpq9-)@lLJ{?8AQjh_%k+tB^%ocqiA%i`6Nzsq_pM@@;$pYAdA=_q4LXF3dQE`
    zyamU<y{u@`^gX%`uhQGKmiQ>EgDXXf2ALqN1d(!j8eEkCt`@-%nqrc+=;WAZ?eM6<
    z9V{dQ$xG%b&Ad*|rrg>MsfSy|DQU7tddG7401rTB9(-OZ<`MHH7bPjW0pgwIOCf(T
    zoz~22g0Q0c0cl(A^47tbR6mXBNx$El90_o69(`(GaNAxa4-CLzk6$4zZqU#GMqoeI
    zVgQ7++;Ehy{RuGRvp5TJB*%RLgahp%YsWh$5N%Ft-wY<Za!2N_j!)9(>coU<g^<|z
    zoH$I&4?K5zs<LY<ALxB8yPo74-!bxheZ}OQ6JKTSzAHd;F1jbej8tCvW95!=dV+^V
    zBaRcD@7r}85`to48_WYV7O$edi0C$5>3c*EkDCIT=%Ht@m?i9fBo&K%d&ls@%05K(
    zbz3<)7<d3z1BpDrTfd<QZDBPq#7^uw8nfsj%agcZ2sWO8T<bq_CX%!<>vRIg6zIm}
    zR`WdTto=8xvLyDzv(RNk-5toNWy+7OyvdnFu%zThtVg)Tnwr3~<LAhX`*9UuZQMwg
    zwcV#Ul+lMveaRhI1UBCYCgwzN9_oN&*RW(VHS$S29IXyAl&oH?AF!8c<GV=rQ<)DJ
    zAKU(Gf0EB-%xS#$tk&t~GwdP7scrdOf5nDXA5H~-gUW7xKI>HX{MTu^#$VKF!|(OQ
    zv;Cf_4mDnDh-H0$A6nmB;y>2u7ti{+7OFkp&c#1{b{*%t7VyDRlAl^PM|pdF1hBJ=
    zH8RYFwd;MW<N$Y)A1polJN>CF+?MC3xyJAMd+{<ThDpqgaJUIw$^u{6m^$|)D8aRo
    zD3(daGYRk;tC+p&b)jF$lVg&lcJ`50pksL+_o<$U0QvwNZ>)@=5j=xybD?&fZek3T
    z3V>Gm834McSo0+E)6}Yz2T&9FNq9!6e>YT)0owt|5DL}#Uu-$hDxvd-GZKoNFyu#~
    zvK-}3FU2f*gbLIdGZHO|omZS$?|$MNit<Lkz{T|4vBQF?mDy{tP^2_~fC2BH=w;h$
    zBvUYqxHvA%m15rlSVH$9ZZ-XQ26?6%QHVn;)}af)i}F$xxMkG)8vfQ#w4cc-max*(
    z2{YhwPo*~MlXl+&6j@VDLRf%8iXNr~PD2D@p>~w`Rt@DFdE!#+UmEp<R0bTnj{FQi
    znt_^A7XtZUvF8^Jju*n)=!Ky&_yW5B-X&ROQ#{pmLNJJDpN%uygJRV`ykk@&@}8WF
    zfMqbTg5kduv2DIn6So1UIf1@2nEg?&z3$;d(&g?tn=a!NW2#)=H8k@bPfF=B$<BL|
    zvze>yu^c@{TD#wRJP)S;ktkrE6$xjreDCor^2kH>+;lfr$l(bpUlpPjr`uu=U<M*T
    zxuDI@q&mZIWt<$#yw60cb1Ww3a&~L^sBl&NK63JiSS^7EzaD?Dvin_6p1Yg-K>kf}
    zwae?Q-}U#x(by2Nv=I-$=9=Eys|yi@qXcByUA0$bQdOjl#vRd5nM+VGnxl=n^z1=d
    zJ66Try<fJLR-DfAc1_FFROoH3*YV-KYg>0ko4R&!f}ulk^aEskf=y2LKJ$iy`#>vd
    zON8i|bb=0mat2!%dD%)ir6-yo)wk7Z`X+kBLFCwpbt*Fb6o?dMT>#R$1iin&F!L9|
    z=SIRfSJ=}O+U*$C-6R0%#+q|LacA3ea`bWoO8o|*BloJGpcrMEn31lX*OEh-x|AG4
    z7B~>%|5)Ct`Cg7jWnYOdP6g>4M{sJ11Sm?U_YOcA_4QGH9i_4L?mxd?sC_rx*-^hR
    z?QZVoWG<*dF-}V#6BHOoLA#hC{tPIx<(1Dp@=_iBe35%cYVbuY&8ix<cqT<&Vn&Ao
    zaqu7k1cnTgoLs^luf)>fPJ4c(s1u8;Py_~)Z%LQlb&2ocI$wl?KI%e)E5%}m516e&
    zDPxsJb=A2#lZqr_sSit0JnP2X#FwEYq$q8m0eo0`1UgceubSQQK=<kLS0U;=3R~dY
    z)7jB`RTcnnDq!PTfkRrICYT5$CM=I4t9|>pY2wTBs3SdEWZBBH4mK@IQ0yo+==f4*
    zzdCyxNc3|rsu~ubiyMddqMC91F3EAw(6%6S>$js+X0HqyIKvQRS>FjJ*&C)o9h`&M
    zwA!qcu}DgLpJXZPkA>bLg6asi6HwR|73~oL*-MyCKyn?9rTaurgFTiB5Zp+`GeJ+e
    z=3eG^<=yXT-%j>4#8bTCh|~LGvRq(oh9<N_*fGgk4s07Y0)r*TLbW+#fJ$NMNWTrV
    zE@Tpi>b?U?r_;es>i{DF#pUQ7bJsA4+5ud^q;4kw*9x89Gd&Noye4=p&D8LC9;1N)
    z(ghgmNKd+RQ~;!uZr2J`o>XBQXTscP<D(xqoYq`ZSRmz1t{Sb)l-#}T2|%uXRP0v)
    ze2Q&8!Opvk;Pm7r%NEfE9{ZN-rga;ZnaM@WyeII0HFlMEP>0^DI)u;F%>SKW656or
    zja)Ukf^z_hK&s*S{@ye;#31)LtuL37I&`miLF)-s0CSILGDVKfgB1I-x_%MQMBgXJ
    z(f<nM0Q0q*Faa=6^>d_|H7BOTBylz}1y6_n1>j0xT$wgJU+1c$A#c#wzt7t2wV%g|
    z>1iK>(L`C|MgrzMJEvHG0wa`Fzqh!S0bu#iaslxLC}@(6=MGsW8)S5FyaR9#It#jH
    zaVC@2Z-3DDpi|2KWK8BtTfQj|-m;W)Sv$0e6PtZk!JUfUyMbv@u6)QgF**madC$>K
    zYpWHos?1cPZgemjJ;0^&vIbT_cPeSCoUxc}9M1m}&ICY!%3issi)4LuqVyUuDO`x3
    z9e7h%ktqrpw4rKurw3Zpdg3N*e3#e6RFL-cJzcdXXfzphmkM5Fs1mo6HP1G71a=W*
    z`0wrMnh+EMayJt1J|eLpa)QPbKUy=igUtEa7K)jsp|gIo38+P>)<P-3-K9w~=@&W^
    zH;w|uF}@+r^of1P<Eo3716Uy_+WU3rV<uUA0+sQe?Y@s5s{5Xk$-B(*Dd|2DMaCGU
    zSX5rx0v9i_U+H$yNo{h#S14=OXh==4tf<b=sWHUp_#F&3J!P#9N4X#N_G~~^b*wbs
    z1}1II8Kd*C_BW_xrFSiLN4J+AK;KU#8qm&AD1Laa&i(rUJjj*nZgJTs_5O^1q0grO
    zhs|?(hRM@Ly0doi#%p|^DaNmd4&J#YY%Tm)Uz|3iy^PtWvZRc6&ogkIdVV%@w;b)0
    zxy$wK`9>nME)V(Ry61l?YhU#CdTpOJ(rur9eBMD2)&VH$-_06%xAe@bEjzad8{%yi
    zpXW~b^0%M&K9-T6cIz;H*WYWs9DEsgG0w%gB0i}G%I(jTVI|v&SaX6%BbQ<exhhI3
    z5IzFtNb^95Lf6V(egp3x1T7S~E>SC6ZwME4#X#{?b%s?wR(Qu?HAg-}u&;lz?-QOL
    zsS&q=Abw}8stc0sdnGDA0y0-6mdtIbKe&`eJ>p|Ag5}<v$oFFE9=NdZGy2H?E8fZr
    z@C#%HpDy71QLS3lvI!iy-zR%X;%D2Gg5;uHZPrWLB4BS+Y#2{Z@A&f)9fGsyg^)Rv
    zum$^hWgl&%UAuzKWDQG_$k^X*fd6p(_)qlvCHAOX13LIn#Hd}kt{6XKh@N!fm82)X
    z6KCt=-oEf#O&(U6i@-Zy_pg*ZE(5NR+KN9l5U+)dfrC2XsObY`PG11t764^VaHSpe
    zB2o@aPV<6JZVL18AKBcTZ$5WZ#TX)~^Ug!5O}SUhRsPTDM<>9adCt@Y@*M+!A32f#
    zAH_4-xuRYUHe}tlg=QaA`14~OjipEhj_&|IuDSPieTDve&gXPIxq>Oyefj3RUyfXI
    zzu!~#mOyT+x4vJG^*_oGtE`oCau&19FY5Zpl{`1;i|Y$tj3;htBz}rl0*qc}3tr^D
    zK%V81pT}Lw)U$q1b-In!#wz*tK43}@ygfU~#_ixbzG5R8z`?IO^YQo1uVl)3ldCz~
    z2lQ0^nrq>A{k>xAcvfnv3<hq%uIi9227?AcUnL?*3?guILeu7^TI%i{+sdI`LHneD
    z9Xj~&JKweGV4mOA9w(OjZgj~{MRGAijs_6BgX3WHa=6O-tktJ=Ces&0R-J3?ATA;v
    zHEu4cTYYNmJ7529eF>{f{rj}Hr+hv<SKUw-WX#()=vVrk!~@IkDxox7O%4Ea^Q6wo
    zp<>8-f$Q8D*c{I+WPnqWLMT*akVp517-VR!CEy6)(8Wr@FK!W*+%tTfcpLq6rJ8Qz
    z=Fscr<kSH4A?ZjZ+(etwXLx_Avxa4dD$vnjA&z+5pQK#YnbMcAh^d^O6O1<9Op7_+
    za=mu6X0xyb1_N>#@z*y6Ez&Go^5+!?5T!|M0!?!(zN2Hpd_}Ambpri+IFCoyT{&r<
    zSU+F~lB_=FaTs#jmnpgfB3Y4Z?}FCRkc66QMYv(n$@Ff8l`=B7i@Z(>-1Sm(=dV@k
    zcEGp=KN|maYgw(HX|TO(_EwDd#f&x&jFUxW{vX4iyZ_>VFDW2#8YIR&_Ga`Ndo<^f
    z3i*h|8vZB46orm&qOug+Vbs6M8(^X^0SbJS`kaky0rUpHEFaOP-a*VhiDjFZgW4Ek
    z)0I_UTva#V9otUS<Ly<54Mb{pF|a97Cr$JCJiq&qH|^=YFi)Twfc2eV0y6gD7^@>q
    zd}#JoIG6>AdODsNQnPPzRI^Z8QGzF-*e8)cI#O;Uwbk6;#a4!F_rL-I1wxZMnQL}G
    z(|xhr?FqXVXR*&F-K?(d@T>$V?=AByos?oVRb+D3KAixeEB!pKn%G%|B38wCwK5qu
    zK=$-#PDK38Jyyx|x?#8Z03!mUU9;EX_r;LPW0H9F8xt_f35|-#o*?3@Ce=J|Q9zng
    zDMK>=CNEPBWxb1Io<v6ZnYeSz7q52CY1V!}15A+mu?UOS@k~SylqM+lm?6^#Sakb0
    z@L-WdO}{nyL~8)r`Z^on8|~&_O>FFwoLh_fEt4zx71-2!`J7k3zY|QRexv|1+ZPr7
    zf-?bfV63PpR`N6-=U($#-Kp<W<}I?oZXg9hX{1h%y$qQPEt843rqDXKVd+G^YrIbb
    zdWu5bvvrbR*3XJFVJcahGr2H#t%Qyx&YB7b`&KHSp&#4+B%cuxj=rOE9`Q^PFo-?u
    zqyC-Zkb3IL{H*7TEw;cDyvj>dW93k*6i9p0?p*m7{V+B2NJlu|=%CcenHmdB<T#rZ
    ztf_V5NF?I)Yap(x9O1O*XO_Wm-w)Cy;ND=7+zc#~^Ny_lq_GVM)zB4rm%M(1)>xZS
    z*}yS(6yGK}YZ~+%`2ZTN@kiINdI89Vnos$^6HFFStM3F9X~=?wV=Vy@C0+3uG*RPw
    zbsSj}$kWG!X(os<%K9INcneS}BK~nruH@6bUbhBcdWbt&u_Ip#9~^^NPHSX>L=xc*
    ztb#L<Tf0OTB40DCREopaal&@-qkCfql~3_ZX4uCDi#w>CpYxiIY)q>}i9;0NdHG(%
    z{0`rfV)K=sC{f6WicDV(eIw(gOwJ@-_D9wBMLJ>Ax#>tM-B#E6jN?<flJwAq=3?5y
    zZ1fC!-_BIVy24v>%n!7vGmsa%L#FTdHxb2~(`fNy=RR&?%pAOtE=Rbo2}qMW`F{W=
    zR+{S^g-t^(@ndLa0}QMY7Q=4@*xb19S4_Ef&p)1infJ?Pe0bM+XTK+|s)s<j8?6?0
    zPcMB50Q#pk@I#$uz1ksv)@=$bpId@k8pB>j-`~@m<xQ)~&0|OXkPE>@#krjuGWR^Y
    zl_vF2V;<GHIrNWlO(vIfmUXw#Jb%Z?e}?RwpFTL3y<C~Y?=<5D3}-!exBI;@`aa{E
    zJXQSZH)dnv^E8J&l~qi@k!R1#@A`Ye2){gagb66Ng3Zj1@U+f2EuLK{eMpHRH5eIT
    zEGLxlC+3<<#m1-zP}jCuuz@-Ew)pgVodcc|))!1Z6e&_ki5h#1WJ8HFl*1>1cvI}F
    zkMh*-gw>|41%H@0k(MyL0ECyQp|=SpXI|;1Zm0hiu2dp%{8g~&^vemWPXOIT0iIR;
    zvc`zh7hiCM_z>ESReGU#Q*%|4XP9Cs6~SM=4n+D)q=I<pj6e*v_3r3NCN~nwwQNF)
    z5Jqt2^9!&tpD~rb0fsJp+Lhev1#pd+ugU$9Qknx&V}}AZCbnI+aWP=hVCqP&6@3!6
    zwJ6_GcUSr`k!SriT))%y@XEe2AoYloCj{L09ppL^gQgx#*Lz<8-%0mA#LqktQPfBW
    zEn<{-y6w}mGM*#D5Qar}+GQ2xyfk|M+ZFtF1!sahP&*OKVYMo=9N5N+eY?I`iOY_r
    zq&srnY~Z&TC3buQ(iVJP{h#tb)$=+3y_Gyf9~>;4ee9d5jC*|Uz4}RMYDZa5E2{GT
    z>bm{{t(^Wbv;2LycuOA-?v?o!lX>&`u)g0UF$<Zx4*}YY>o@aHT77oaKkMQnPn~@;
    zw~DBpPi=fCxBNn8dyn&ZwlwEFc$jl1jrq|}=Vzt);yJ#FD*?dQHhG8kylwu?(nUqC
    z?v&(I+WHd;T4B6Q?G#&;+=u*CYNg(_UTBq}>Lpov1~$_72Aewvuv>9v-d4DlUjLwp
    z7G=V>!oc*AY2T03P9*oBeZm(UZ}yt7Ztot-<7CBd^K+MLOQ#|0VvL2v&?w_#T26SH
    zp3y3#>I;>DBwAGe$%_{w&AFc{?uE`I{3_c^R*b6l>%7NkMd*R}E?r9LGh}r!^nvLb
    z;wAl)L>4Jx=O=&+mrxYmtZux6=sa}5f~$=qIeKiBcOip6WgphGVEU&dDf#Tw91j+E
    z%j|`$%hesMdA9R0J0%v_rM{%%k!Y`wcU`A~Ugd^l-uX*sA~yV+t`Ku1(irmateosL
    zxV<AJ9trJ;o9_0(g3o@uP~L@Q5*F2@yja1NCm3YA)W&M8_vgb^Cgs~QErJ@L_4s@m
    zaaoRQ9Bgifp}Zjfld#`}>)D%8+S^Cju70^Pl0A}{M!idImzBs}k@9PxGc{`hY6TU5
    z08LDe1e9vs(NEPMT?YY&PIuKR9srv2*vGLJ8Y9RNZ77euVKQ6{BK<|N>sICKK&~2Q
    z_ShG|6urdrvUzQeYX!^Wdnj0$GrSeH9L0H6y35q!5cQv-4@9og7OBd>sABc2SGhv}
    z@Kwq=g!~*U!>oOFP!}G=vqzsV4zfEf0x7lC#)tA4GwrDD{PIt);}9+YSa4I^$e2Ji
    z*aLCn3i}-kEQEtSQ5=(1)4mc%o1OcpqCJ|LI-w79B_*Yc>rh%R?j`grKwEJz>NKTY
    zJ%1yPs_^oDy>jpcB9`&fRu*$XO6lZCyhGk-uZ|`^>+M&5SG#jEi!-6bo3;S?pyU{j
    zaU6ywKH9QyI<{3Wihf5g7o;Q*mgAXsoo%EkoUBQbfXlGjkf9UdVwrYB$I~3jbV4Io
    z;VPopF>@PCu+P%LiJ0XGxh6I9d(`L2u}mOS+zFejDf9!-x(10jEU>LbQ=O4>zQzN=
    ztqC3$l51ZpIkNrqz(%T#-5i`lQ3HI8byF6KrhVk~>I1g2H9^RS7%Qs6H(f6&S22{J
    z%?-%dFV18og~`zk0IXIpDQF`uG68WpAUXsAk~ah@8h(xVROx-bI6Dvtc+Jp3R+<hS
    zup3h&cRV-OCN)bJazIm_4qUu4cyz3)666Rko?}tUH%}FBuJ|;xen)S%0Vq3XG6@7c
    zKxMQ!%&tuXdYZMYG}V%wct(BD?)2EX-)Y)?mtr*ELqH}|5<6gwGM9IZlSL2lBPYEU
    z>Cc=AdQHlT?VQOJN5)c-AlG046#!PB3RE~cKxTgRxfVifqGff|_TJ`X#JL7AXQs5M
    z?7k*G-Yrm>4UfX7_qBjv0)n^@_zpG|sqf%SHV){!!K8Y~<>L`nn_RMvcUiAMHTVLG
    z803zK8rPx?x9sukV{qv)TH-RWP!4lr(oHw`eTtmrW!-NSA+Bivjcb6sIH9{+T3D=n
    z)>Fip1hu#e8sYwLHR;uklZ)6U_5hViGr44z{bK8Z*%RU}o*dR>fhjQ1F!lTbAWmnr
    zU#lP9Ag^scG&9v;BXDaoEVt(;X9Da16QJU?HwP0m>id}QD!0Hu`I0Niph+dx6$Ny>
    z9m=T;SFEYs{AS|kcOUJ5kR=;m=rb}E>EumyBF1N4ZdyKDuLGtSQ^3X+eFoTgzL((r
    z9sa#wVw+w3=0w&Ne`@5X=<u-z=0L`H+inDzmYS`sj+MHnn<Qfob(o?L@R$3^?XGfp
    zXxpiWsJj}mBh9DUoaMxdz5~ITrrhI?73Zn^{`|WAf5p;sUi*$+)kTx->_imjaMm@}
    zsWnJz0Ld3dy0+B#p7sNYo_XVo^6vHBeWYY$ILnwnpe<<k_cFk!tu@}AF?~(Wf<FPu
    z*@pb?zwjg3EL*qF`uAJ@Ud4f2K>GrC&ID^-)YKtFC}<<yE~dn@8UzybG1}>~0F$GE
    zrSAvP$Gk~4jXIVH1<x2Jiu@H*wIiWTpo#7$<nJP#M)w|oeA>i06=vPj%(v?p%%PA?
    z&f=ZTHCV-9HI?Wif?0AjDf!2}GQnGmG<9xHuV7I=ic9I0bq6Eg>J5~h0AAm_zg8%U
    zGmZ&XT2mYuSXm;j<6;K^Z}Q=0`6*om#hpbu5?Hy3Pl2K3{R)PnRxvr^{2<RoJw=aH
    zj%LUmTvUwWcH6J>jy$<6aRyRaqPe9yQ;4kxeZ>-p7vj4y^)Its`Oc6|;`!?x^uCPv
    zsqzqFZy&uIf92AyPC@P(_oQq_;7?}^NQgHU8)qp)gT$q=bT~`DR?l=&VyO<oFya#C
    zy}Zo7fZxA&P3MjH;P9E4=$O43NPBV;GuhBiD8C=+KQ13%r}@=N`|*0L>nE<NKcC(<
    z13%3&&eQqoKR)v_LEuAYlC0_J=b`8G^Nf-5<Zp7m^EnNVA->miYnSR{0x1+%612?i
    zJrxm`ow??gInGtKpLctiBDH)>;u^jtXPYwJ^h2q-9h^t!>A&?bSo1mHBh7v`jzo}8
    zz9h3w!JV{Mue@#7X#XcalJnHTCr{m*8sX+KTfg~Tf3Y{ma?LZX5L3huBqLYMj(`QK
    z3$NB~r0xYw#6%-{tHW7JsZWzSdjNk$Av*&~w*)*VyjPkjA<yR=)*zO`AoWP4Gx5x^
    z>r9?@C2!N&Eqy;)M<S)`(}buJGEM0)M$KDqv9D4TcY_|~lC!rSYQT!2T+Kz&dpEJg
    zDjSZvskg}b*Z>z#Q<p**h^dl3{{mx&?lD_~)4A(VH1RgwoOB!$cpycql&cLW5Lgat
    z$>~R;Jd4T;RFG-WOFr+g4y~(+x!|{Pq#+fhijYPe@f9{QeAamkBSV#v{AINx6td#x
    zeb7wJF_Gr*Q3=+wX6CnCuRfSOdL|;%Hwzk-3y}m|yFv-p@}iv9PSFR})n)XujzL7d
    zr$wAny3Tke)#6oy$l{K?f7fdi@+b!Iw3f{0#0tZ2`5B1F(%O@*=wSfgtwP#}XK*ZD
    zFlTZNc}zxHay<JXa$ANwi27FGq`?i!gQCO-w(_kGgddY)bBf~*765ifH|$WWVFg+7
    zN|?RVJe8w@ARLojrr}GI3!3C@;n8rayqW}4FF}X4Q)r_h5?nxRVFYSIa{`H1xwXz^
    z-KS+o{v^zvTfE9Ct)c-SfJCjn>VBq4Q&i1Irmq&fs4hgBxuGwCL@1mcB}<(RAeaYh
    zO(}s>%t1S?S<=G<n^<H-s7?E|@bta!1Cud&N!jUFVh0ESk<M1?QaC7b)~B`hxvZgm
    zr6s&tgT)!PN+AyBMNNTES7|6Pf$seN_`JMp0~W&ZD);832~RqZNEM)P6<`EsM&CIT
    zW<<46(1z&f1CAsvO+{)?i?}Y4u*-q0i9!S;y$vt{qy<88LAswc>J`s(;<~5PlWOf5
    zr={brgl|Xmp}Ch+SGWb_UYIP`2YCPvEtxd{03ZNKL_t&{k(}6NLvFALI`lZ8Yh|5n
    z=S=3gzuI!p%S3#JMQSVzsaMl+7A+$6JxwX;LWCNdt&AdH&-F1O*Me4LsF0qu*fV(p
    zmRWc!tZILHuf`TO*4&!TYLNaZ-?`~a)Dzp7=WjU^?GyX^z+_jTCXYI_!qfEItpAvh
    zHvLGDT*o*c=Ku>&SNd25@_m~-eQ#3I+CLf09vI~F3vz4k4(fq=ejaIv3;#cJZ@TO_
    zk1GKtDEFOt|A(EWyDZEPAb|u$pQ`H5H)0fZ%C<yt1(VnaUUuW{QL`a%<tliAhm=Q7
    zZ|QsTB|)GUE^n5T(8bsv&6&_ePHV&a>b<IdG**suTC%pbduIZN22xTet*K}tGFif%
    z$+mm|FluO<dx^%~Sa-Y2mx43Nh2~}$xoYdEf=b&od#RYSdJK@<Q*#)r=1OxYXoHD>
    zY4u_$cqy(nkVV+WEGNNIo%2r5sra=!wg=RaFAI6Zsr9_lX=6>#H4+H~!+XXiC`^&o
    zL<iCfP`TVQ&B=v#u#Q3sU$|Cn1gg5F*pnWrZpsrZ5qnY_JWROAh9{5=y@gmU(5O1r
    zuOZqz42kppN3--^u72|Qyr;k0@*+Bc8f2c=7jE@qr;yLQmef~|&ERLa$m#uOtrm1z
    zOgPkftu62QR$R4-@Q{Xz-}oV0{q3$p$;qw7<uK6^UA6XD^;=+y96Y|f$b<)(1F}w$
    z^B_p@Ue71=iGO}fV~HqK?&_w%T`4-t5fV;?uXDj0&mFXMT7rTqNvRIH%k(QgRh1Eg
    zCUsv|8laS`0m?m!W!Ts%uFBi##(~)|ejhO>tWtjCZ}p^)vu|RY4`PU1kKK^{1J6my
    zXZm9Uiw%r^cRe-pe{QXLT-)wn_$dNNj(tr?KpSM}9oME*`KZnA$fn<)fS_qUJ>T=%
    z!RT~Niwi)@f>r(RTJb|U{o!2&&|x{rTaR(|@$5ONt-dpTW&Z#KY-#s0RqXWb*|UCa
    zZEb6f`qX@iZ2~~RO@OQR72REz-M{1jbdN?!haR6pfCl9D1W*p?|JUM~__cziyh8rN
    zx^agwA8R@6Wg&Uwr(Fy8x-$qcmz9-TDlZPJYI<E1WPwFzbdgjnxt-L(+&rBcK&kI)
    zq`4Ovz_zL@W7{mh=f0|3Sv_QMI^VzHy|RFkHUY8Yr-g4zE5_Uz#*d;;HqR)g^e&?%
    zEHiVb{sFKA>)>|6!{LR67fLanBA{1L>Xq_*Ze#G!cX&<&lbQpb+yu*wdU(q2i50MW
    zg=Co;$I>Mzh@d%u2ta*Bxo=6cuPe#%-phkjM0IAK%>FwC+lrME{j#jpS)L?TW^+pp
    zo?Lv2H){bM%Oodv)gGztXyMchz3)y&{>nHrOGKTjN9kEom$s^z+2f>Z-n=sPFHq#}
    zJ!T77|IWAG=zV85oDIwlGX|ed-Ani|-PNh>*^~=)zVGQz67i0yAimW3^}OQz7uRC_
    zNC|4g?nQfY5|bOj;k%)a$Czn`jr`b*zWI~cbY4GkA)3d=5oN$=lc#&0hS(cyX2b8}
    z!GQ<|M}j?b6Hn2ll(5@pwP0JBD!tTa9i9U?auUNzAouq^5K5?wJ`G82V>CQKMLlhI
    zzm));ySM=5I<&ktzX}H=>bUvN<XnfSvawmELKx{=e7NnDf283v2P%jW-``)FwA>tu
    z2GA;D!1nZ*71+|Bp4WVq{OQ9o#3Fo;Gs)q|)sbS#BKvi9g$-zTdxH}!wxdBK0I$4L
    zz0)i0esEVs6{~CY%8XpnoIpV2&AQfmT-`qd%Wl=^YW8M4iZhH&GV-^pHg8#$XfFB)
    zH>|8@<g;)|ohJOG*2Qeow`83>VP^*poeQQxC!c8wTu%9KKXmi>9?G%nD0-5-uRXqG
    zF^yn+va*)NgYV)g9;D917VDGsFVJYwNP#3Sma&UQTf5Dsp2p(c>aUvQ_v3xDCI@vw
    zEihWqrV6fvdoG?UIj<GKi|i8x6a_K`#nP9kh37F?+W_+Y!(ud;&M*DF+^*EMS{5Fg
    z>d3B{T~qUC7u;ymNQQnKE|NQi6PPrpKm|`K%{8ze(q*Pw$|gcnc)|Ak8CKCMHP1$Q
    z&L~<0IJH;h-{rKsK7BP}0<UtbX8@eVRSqrC5TIxV?r@gZSgk2~-5^&Yl$u-wHTC<h
    zP~<xf1DqS`EI`kxytmTg?@nzC;;<#oU3DKQ3jsv~_KsyBBC>ZiQFGA!?oEW2>^p--
    zue=}JxmA<d{P+t~G5es*xstAvTh%T8QsnmSzV#<SP>L3{#cMjP(FnobiRvbuteGdR
    za^P-xzu{Mknx1ij98lj69p5?e+rZs|F~_~%FA$OU2`D9$3EdRe++^^T>qSKE9=XE2
    zYX~_V@AMAkgluQ5=1jWs@m+V4+LSEPX>FNBASS|%8OkwA+gPj-hm(Y}<eZ&?KA5EI
    z3V8vZ)l3MmST!x2^c%!Zk65=N`~_fTw6*3n(!fnlBJ0)?Y@2~YS94`+49XFCJ#P?E
    zR=Fp1pKDxWAkPif`iT5ppaMc<c1gYK4U|a+F%lt%+{>eZtec@sx66>`@R)oR8>Ik%
    zcwyCVoRAmdoaaC_@7=sI^VdS28X+FC?3xwrUc|GE93Sd8PRa=+)D^MUZ;#+jt!K+;
    zRtL6JosDhJzRqq7imRb$GkP6_H6}FKH=$c2<#yVffa7<0ulh_=-?9H`J*QJ!)T@a2
    zE7z-o6tV2w)VAm&a5dNfz0HO|q1{b1F3(1KyE5vSCFjv$9S71_D`?pf^E<f)-s*db
    z*iR4GRJp3ogCN6cPw+7A0-l@+6?JA;h2;fW0eb-x^`)NS_!WOq<7>7<-eHR-e{MyT
    z1RaLpQ4iW87@MID0CF$=N#&$ja}I7z+~g?Ob{AkUgzp92)Xf@<m?oU;0$k{p;u|2;
    zhQ+MNku$0L8<1$S`rUbO3<;)?ba9FKm;eX|19(5CrPXV(_5nyA-tv&WEmY{0(5!%3
    zfu|M$=Xc_c&F8E%7u0dog*@N6lFEOIZ^Nmrd*X{=0vVwd13sM9@C%>8!Ww7{*~`(}
    zHSImE4rYb)1xzG=oj4L#r5)a6Y1Q_-@YF**LERhiW5@eRH2zMPa2qQ$!6rZ?-0dgS
    z5sWM&tx=B?AWRF7J;q9PJk4p$TB(aX`)ahjbUSR+DMsvtSe5ozC^q_%lSP6Xv1R4-
    z#0dS}W=oohGh?FfG)`=ZF^1Y%d<lTLF-;e$^QRK#hsNwGa@$Df2o}<&PSfu6v2(q3
    zLy_)IkcjrUl*N3?Y0YI`lj~3v=YmJ4KO#py^XxmF@XZ}br&I@3VY6k5!bL1o+#aCQ
    z%=_2CWX3Z&<vm8PUPDa^PSyg5M8d;W3f?{ceOGZ!Ol);KHevhxZ!WRKaGag1zQ%1u
    zD>pG?jF1p79q?A;%6ec81j3tf!1=ky8aX+p={0;Xw&(jUV{J!V;tTQN(%sA|a%%Y+
    zGVxKK0K>fh1O)8ACEY3Q>HdiZpJfYJaed-qG8DP}ysbMw&?UwGa>-QyNWF>l`F_^a
    zrXYRJztPw|e@vw;xW;YssngiE{$ADa1z?57tV~nO5hh+HzhLwOc>oM6+KMb-VM(N$
    zn1IPc<Z|PplnakS%T<f{JIom@>3?whjXUk|ZjLvu+VJG516f(l0`SXTS$1$i)K~z(
    z<%a*;{*@e)Cz8`oA-INrmu{iOo{o`%%+<@>Y97JIYY_m&2wp)%mVO21|3~H<0NSfu
    zsB7a4lz7evqI@2p%l3sZ6`L^)Zojhs|A!-y{q2#ac&9uu#sxr~a6$cxj$yC%6jMrv
    zX-O`QI^oV2a9`0sep@+AhcV7@y^#^avR~cGG1L*hPgnX(EJU8KJSo5c{3~fG`rK=h
    zza1r9nl(MJUDWgZpFmpVS`Zy$xpHh*YBiktwYI`I(A>9si&@#u5U~@5oToScD|&GK
    zzkuDb>ze)~)j`b2x8pr!bFMM`dEVy<3U_erW*KAI9ayhML;Ob-e_y#&SyGjJ+PL`~
    z57c{JM0-Lwi%||H50yGp=TCe~o_+*2oAT(Nb6KCSpVF_7_tfvB&KrVoF!DJUnkL}M
    zrSx-sU(LblGjj4XsdQV7_d5LUB52T6o|%>ia<V^zL(Y2l&5Q5Wg2}qih4*879&PrG
    zCiL@Aw;vk6GsO@87OCr<?cdv>uJS%Ji_f(+^yE(epmA29Dl+^?p6Q<XtJ(P5?({Do
    z!H}fe7pWvoziy}y1l5=bAl+W6jG;-*b!Iw1#HB&?AIQ~=4)L%i=^)rCU9CC#ok}z{
    z9ZP$t+(QR~j@5Bt8M%(&MAW?4C0wZ)>goAl{Q=M+V%5<X{ghkBKV3!+E*CcHQo7gr
    zIme}}QThJwqQR6tgTF}{=*2XYu_^Pj3o-GdtT+%l&Z_MSTwuOW{}Tce-(`a>>4&Ch
    z9#b)ZL^!7UGoa227M%z2rMSm2F5QRRoalDfMm+kED(A@-4Fzh_J-^ra1*Ir${T$4O
    z0=6>O&b-kI*d1i5F1y8s<x5`M`<PAtM_2O5H2ub4b;Npkwk&zrIC`=thJeKi9~9{O
    zI>D-w@*cs04rl}`-CgJ^wB);*$LNb`p_vzzv=W5c2_=p?XbW_!=HW~$tq}nnjB?V$
    z7KFj$FH&ubaB=7PKvzx%%am7@VnwiQ$ItOh+I+kb6l+vz<9S(ofU5P_No+I@^wc26
    zk3+-`%1&6X3wM^qA-{4ML%}K&N8T+g-K;@+R7RzdfYJn%aLa!xTIXK_P6NJJj!<TR
    z@m|$)(c05uj%C#}x&Q$po;fNYHQgy{F(&Qj%Zs&)3RSEUerH9A^0u{b<60}9-F26F
    z-I9m*aDU@^#eNwSSeU&569+0ovt)(<6z=Y{1*|v9WfAY3tSCTe`@7}sh>{V0CW!QG
    zj&LGG+ySgb4D;T!G!Qga-Iu8r&y1$y$SW)=O$=qp0mUgBc@S7oQ<hQerCt@aUG~`6
    z4}K!mnpVDjif2+2oWSGlp))5&gVZ?pW#r#{9BIr9sVkxGD7M_2x5X!7#I{$f`hkdY
    zBJ?H*T|yt1(JBF_9XUayUF=amDjY#_!BM0YX|n&upYipaG0t&Q8yP@vQ{;ZDJF7E7
    zt6RUzIaqCASwJrc?&$E0`GRs4{LRijJa09@$yrTWykg%?&5dLJ-uY`eHZuIj*Q-sy
    z(feK}@&UjKrK|=wE>S0SAik<|VHa+D7XaDLnShgwYc9cK6}NGIjx7&%-2@Bzmz+SS
    z*JZ5}RjF~1R&dop(L7Jbd}Rp3;5&H8)+)x8z;mUzV0fo!b7i}{IFl<=u2gC+;T#$W
    z{v@8s$%EF5I(oWTXCiQkD$9&ecvpSc^-Dr5US<(?@ol|!c=%XwWo|Afy4XBoeT42e
    z5V0e>$=^bVBe!r2g$Sgr1On4nTTem|QUgjuZ-O1gRhJo*WwU?u4-WY9cFfDwvF0nf
    zt^{fZnnH3WF8hwkSxuZRtM6*!H;53e%(iE>Ld$d3`oX#2a8Kd%dtkC+D=?u|)N>lI
    z1X&?f5~W!4REAFXcrQ>|vIl{aDW5Tas+Wt;Wn1}-lh@=sW<EN}di^>!-ExBOc`VoV
    zaPn;?;>>sHdraY|x>2e6EIsQ-&f!t}<}^OF&|noqWPh#`N#D`BjIxX(1co>h0p7EY
    zUL=rKAJxe8u@$xV#!U9=q;N;lDSe5gwf!C*`)K-8#zxZ(Whg&;VbS(c%n7kvo-O^9
    zGR$)Cjr5TypNBFuHPt5brVDN~<NPU6$u!+o;XR+dLHgVd&-eVLlyCg?yeW278n;)b
    zdT+l1EV_M^w&QAZr)(U=lXut}`GreC2~wxoh8Wvxq%hMBc(kEWVLJgzLE)1%>MFo#
    zFq$>951&6j|LJ{g%ctuTxEQ%m05MYcc&3wC%h-!b#$@K3_dB1NzAxXN^&}gU|8VU<
    z5<r0Q{>(GtnD`;T`@{EM2cH1Paysn~6MocV|9muPe&UDc_M7Kj!dL!Yc%kQ9Z1u!#
    zcZysKxNhKDA^ffzGQ$Ae@Yl+N%>k?z%-*Q*eK4%`;xY^>2P;~*JMMqI!i)ZbNK=XG
    zHL_t%x-EpBx2HF)1#j>cg=~ugIeBd=E<?m1YajX(WWP_{31fs%&p~aKfl_lrCZ*bi
    z3j4<%=FVtDUCpTPNJ{iqmb?&gOkTKi9jDk)Zs${n7_Z+EGxx?DW-pkh4hs(8jaRYe
    zUj7l$&8aVX$KDse5lXvUG^3AJiYwW#oHQ5R=?(VQMWMwStkmP&oXjCIa7=HH_g?=I
    zon%`2)(VC=-cPQ6MLe2_PZ#<u#(PD!Cpe+D(wX(~q(v3peaWhF>|aS2S8`zN>hM2H
    z|0_|zJ{|NjoTE;WWpH1BQCv@-qTEmm`*(`<H-KM&y<z?frMw;ccqk^-p!EJK*c5|c
    zz*_J!I9%z&;%K8rk>*#f)M~8JD=KEplL-!7$%pWX_4ucUhZ6)x85{cUt8o-??u)T%
    zeptnSf2}F<jBp2<s#n}7`%2E>s^49w5$thzmQtM9y<bnK`SpTjRfj*l_SVx`ZTw~p
    z#{ixU&Fw9$G;~@V_vc=pbSs#mj@9?U8SQjYVH)M{aW2pE?{}A`tGzqWPbS0=i8KU3
    z<kQ@d#xeo!aZXOD5k9eM4{k)uJUqwNS4{h?{a5~0g&gFPv$J8UGpRXXtqXZnR2|Pa
    z607q`Q|qMYT^y0$dmNL;Uw0ZE#q39rQt{-1&2=mP@4VIV>=Qt>%Y5eI4LrlatV$8F
    zX^2y^wnL|)I=o6VL4_0A_%5i~nIJVppiRe6njx;*GD&Fhp~q-}!SWFqr`pT<EQc+x
    z{50-Kr7hMAZ4WT5TZ2TTiTy4UG@g_9F~4+fQIWj5EF&<iO$Q?RDag5y4}n2-CYp!y
    zvqCyr)p{3`$FZMg+q~>&8uz3CMc#D)bk%4@x#agnFnRI#&4%CwtUD`7_xV-DhVKMp
    zIS9o8pvm}#9#(F}rbgq_*JIa5a1^=8Z@+|{)0Mdfs&=`N#plL8rku}MfxdHruK4YW
    z7HKYA_?0Z?mG+!pV1T?7A2M*#1;^Vg^m;Aa0Jz|J7+1hp10Jn09e{ywfZFa3SZ*q#
    z)kg?CYuev!<V{fpN#nH%wes|GM&l&bxo)`A#G9kFP)CY#L!Tp#5QTLF`j}kdSyOK2
    z(&<-PM(Tle`L)_-+Gtxltq~A8xx;LnO61XtkL>GSQAO65(z2l)`3&i_hA^|Y(;7o3
    zYM$ODINL(S&<)0mv7}WxssU)f61FVMvZeW%R2U5m)_p{Qn<H0Facqk|Zx7h7TrblQ
    z1Lo<`+P?qJ<1K51--(*6#oE9kWYu_oh5m(7dhQc&yHnpG0OpZ*4~<;Ly*WUUIrnrc
    zSny=Ok0<=~XHQ6`&@>4~pdQ6bt5GIYp!6sUx;uA;IXS^$gbOIZq&a=nWOv)*nXvXs
    z_4f2rX(RgXMwexlK0*jko?DiC*ZvodBWhnsg(9FYN$NZ9T&d}0QwwJPT{*^gnnz=Q
    zP>)#oRV~18FB;<db>E#|0f>2TC<K~20^^vh0^tXMY=1!K+ei|9`eGkAW3-TB@>9mm
    zNrq;C-*8&CJUEPs-)a_gn}=`(XVc?b0g_gG#EuTd_FGvuj_1}&KRLyjse0ghU{abU
    zAlacS03|t&IN>i>d2=u*H2qWDVv@!DyNfdcS;G7tgnr>(x&9^}T@wmGXkmA!wQg$z
    zLto7vAHc%14biP=KNLE&d9SWOnLY#l)Z67s;GhcEi;IZfXy-0<i=urf#0z@8vL%~{
    zHh`_kov!F8j${KUPJTQAWyCY_Vmz9|sQY;&Mfo+bmkE~LdMY?0kMzfZY;Mbzm8*s2
    z7PMsV8FY$u=()l1RshlO%9+S?F`Lhz&rn?SexG8Nm-rENCuhQrqF6!>k-^28X3w|z
    zB5eydgKC444oV!!@ih@pJJSbfj1J=FK(a%^ZUxs)P?P+onDdpZoz@Hh-dU+8bn+Dh
    z1SCt}mxwXGk}$h|>U9~04grLd@|I^61EARR^R<Ft61WU%PCO_hM$OsToTdUwS#bhM
    ztln08R0txhWh_3cRshz`Mt@FSPaPjx_30YW!<C}T-V20Guix$PMnK{IRIZ{bN>!PL
    zj5XseQh3(E442&^#-Q59NfJ7c=qv6YDw=h2Cg_Pr5y?`#GAl95?;Xr!?r9`82=-X?
    z&O<57xd!`42A$U?HhJgoxXU640jvVU{LaX9#-l%Bz)cIzUG)swBFlAi`VSGMM3EvR
    zBQmq*&rTw@;qtp4$8R^w@IRTC7f<z-Yn#mi5<z9hmx9Ipo9rh*x!E?p^SMD3wueg<
    zlTp>ZGN;Nt6|h?FlNg_Ql*w_+U+YtfP@06j-z}wTerhfFo1dR7I-mShW_`5PzZs}a
    z6M9kxpYs2hPUu*c6et`rG8sU+H~Eg^_ff={Q)0>46{%Xrr)P(T_;|O+b+-<U{B6Eh
    zR-0Xs3XFNB%1|xqVWiX!J5^#-roTV)dAx7m`g{3u*p-KuTn4`!c4YvUwc=cmG{9Tl
    zi|5Giu@=St0HAhtW}Pp4p@Vd;9K05W-TH{^*g)7Gh6alR!E-fFU45JtlYRE9SRW;d
    zSTACgS?La)@-}w$t0l<CRszb!?-G?fLA-bKM4ek-!W<o_<3q#uBNenBp(EipEDvS!
    z<TsYTvL6yalanz@|F?m$PS#4@QaR)>j)xE{#iF|2A%2x&@|`dg(ZujxaU!f+#MJQa
    z&UG`U3c9xpr8D8Zds`5zitezBVz9$6*<<Yqtnb(n6N_p<ZE;F)T(1>p^O%=6u6dG_
    zqsD=K?bbT38wk;zqeC$cD8prM>}1=-$J!M9n|moP3yfnTbXQDmkA+pI$Y&~yS`<u{
    zP<%*Y5LVJ)ze7bWQSztfh8G-f*?7q9DizrCfJpz{l_xbWY+IY=C8pg@uQGos6{}kF
    z`SZ%*+Y$Pc*rVp*hpCw9e(IA`T_|dtQ|-PT<ITdY_1GixysVELrN0U05fvx1F6(yA
    zQ|hU!rA+#t>on8Nqa^rUR!XF$rknI2L0u5zdan5UqPUD@W;tT!ecq2h;BHS=Y2&BA
    zNYI-Q&zy^za54H|Z^<CD4<x?#xf`CW+#U_B3>Z?UEkD~U2Aus=w!HWETTc@|zxDUB
    z(R-<IrL1ojOMrkxtgMNI3!x{m(8R}no;)ig6>mPK&B9~j-j7aih7(c#oK6+7F@Zat
    ziOI8#y{|2NA+^%Fvk0QO`EMmBwQ>vjZBkyYY7G2zrSh4FeT7AE)$F~vFY05fWTi{4
    z1GzF&DT~x^&|0H@F(VXd3g>%i%NVyqz{ruO7GmqC-Ylm&yf~2$wOCT06P7&cOsvRe
    z^t_yH%o3%k@tr_x$tjppF;{-twKKksBLHZ2=qQ}|v0m{#H5Qg_zsM-aZfw>s-%BPD
    z9W|PMBysYk`(5p+f$%pfyn$4xRK-+un|GLsY;WsSqIKL+?xGy`W!1JCXzx#B#h)bc
    zUtet+F5a%g5VS@vE0KO2VLk@pjGRfK<*7j+%6qMV%J&TjaB^81!&*{XsyT^jh0H<r
    zrU)|u1~Cm&bkTIvq65%s5=?&&6?Xz_0BLoT?>sqiL)H1tdqFR&2#yqY^b;YWSX*+M
    zvCjP1!3rqA0FMHg>?!1sR@ZFLdO9|EjQRk11VlR-u6(T&w5XwZOfda^S@v%N0!OS?
    zRS5-Z>2hDYjCtRB!VfT^Y52}wweH>K^fW!ju3@tlOWZt<PeD)E%5&yZz^LS}787Ln
    z%%T$;c@lX&E75_ZZB$In!vqjog*tDc8$L%`GdI()jytg%U%Fn=8PrbYIl$`@BB30g
    z#inVORB>kzifFq9XHzH|D>r$MilypH=H;#s-8qp&y5enV<0nhZm5ut21SqaDH5rv7
    z;o8}gqW9nQvy5nxfqop9^R(7Tx4(BG_6?a)gjy&cnQTK?hbZVYXF?(@SwHh;3zaj`
    zWxJ#g<yg=wnDT3C8YTrrba^yDaj2K4+m9C**fOk}a{eAywF_E_B=?GaDg=$<z6G4Z
    zi*?~nKNCY(9M-bnn8+BrOS}`A+(zYV=y#TR?Vl;-tRDac1mM(rsYOTrD0VOefg|d5
    zt?oz7*O$Y{rs17BLXZ3zXtx|JWs(a>sLaL1N4X06GH?;fX)(;dlY5ch1$Jbsz6d52
    zoX8Bo1^SA_Jx1-23ef&%0HB;nkW)k}%kYimpF)u%6hODVLz$~0f<@=IC<RP2@w{55
    zq^a%z7N-3)pF!ST?rHLkXFQq<0Bt~$zwizcJTR{F2v|r3Y_4$OyRn33dUbI8{@fb|
    zMIwk?Y4TIYGwlbk`-Qs!htQe}I+=!Hq|s2iFOL-(2r(r#M)srxCn8YdZDtxG*PB)8
    zm@AgCV)!y&!Gc6j3*?Z6xWI$-S-mKAP^UF@Y_q%jW~AT$$f|gPVzE{}CUTV_`6)vT
    zP}Iqc3EgGZg@8fMBfJv~ten+)p+Er!jP484IpjFVnQWY`f)u=Jo*m0NeHlz5L}T^_
    zxei)ejBHk-huzU}r7b2qyBd`Z2u0)0VMMRrb<T}V=#L7ZDU(;0H5^v73v}(>!2)Qe
    z_r&oO9U<%|^v$5&gek9>ILY~h)gbgHoZl=708}*L)&U>f6*8A52G&}8<B~~VGPABa
    zuwLjyj8Q<LxKZ@%vnwg$slu=N&iUzxb8%LS`~ooTp3l0X0YGP;Njrm_gxn0}3TTja
    zXZi)ysk7u*9No`0$GCMYK<!fB+eez^st#{)Rsh>mw^42NISsYw)<v+*+N;(t<#y=A
    zt=4fs8oxT@BWDV$1uOieTvFF6001BWNkl<Z{6Qa8Eb|?J^<6MIYd`JN0pRE4&Xq>w
    z&C*UE-uc-_<nv{J-R`#d#Jj8RYhF*sGI?JV#42k)-vjpZf4U6r$)PY5&la9(AD=>t
    zwb^fLrR^wy=SZE>?EPtDLQs6{F(MbhG+e}&)-C5ynhB31rb9lgHUyt4S84v|_uJp3
    zEjMHzezN5B_2hu98xkqxz1`2MnE=ga>l9;H0iEQBIiSDQPvsCC>7$>@UNL=dmS^Ak
    zd-*F(Z5QCNnIa^aFzUl!$;w^;t_8ac<#%oi%l?Dj@R#hEBX>$IT<eW;!?zS6C}iIY
    z_bX1+<KCP0>ZD%b7pN!=7wwfk35?J2LWy71g_KRxbXy!mz<Bw@0QhBV=t}7SQDcdA
    zwZDb?u{^6s4a|->9`cu4Eh?eEalr4~zmi+X{EZN!;@q6D_*{s1CJjm#LIeU=pDc(j
    z>??`q&ikJC+2+?yJVqT)PV^fUW+7h#?ofmyEeXLvNjOhKj_D15)1{zCtrq4&maLCg
    z3akquM*TZ=Pw4|>02tTw@*6y>*)xXh3)bzv*}o|MlTwQjNEB7wi@uUvsWikZxl-;Y
    zs%ZV_*tHiNmxmwBWu&3#JMJ@b82n;1d(RwV&)LF47$qEWe$JiG?uqurIxf8Z-=Uo|
    z2Y3d!cPL0kEL@gbQSw}#@Wfr{K;bbDI6OhsU-mOW%JvvXUQo#<BG7SLe`dkYPh7@d
    zRQGQ<mAVe*Wevx;=ehsZ9lSx%O(wsqPe2}?X%Bucr+N&|gWQYH^n0uj0OGr{y`%X{
    z-am15=6hNkIGGN#Ik=N;bse~+`5)3W$1u5*V;x7DtsX~r+^I9`a^_+k@8=r{e9whk
    z^RWi7jl&qF+u3Jks}L9-AZnb~=6`iO@o&qnIHl77$a{}E{FhwF>F;F(j70L*4r`$3
    zIay;RA;@q9;(gl2xxp^3o~+G&aiuc~{rdFT6WJMCw<nO$d`At*)SJY`6~StzFo;oO
    zcWW;CF3WAx@&%qCSR)v9aJJTRKMQJQg<UX@*tg3JE&-DalVGU4>^K)~)mf5ubrtJ9
    zzz)6G|2GddCmP?00MW6zt)kM|wrLP4+L8gRcdgY8nMl9$-6+!^0o+aBuf;6Bs}6;q
    zmicr)^3w}EOV{wwNM*U1Pi;ycjYM&MFJtuj8+{6ZxG=D8)PLNmbE~~NzWIK1kCw;m
    zsiFDXS5lw?v!OO;jzJt~X~l6?IW`9LjNbpn_1c3WS($nb9ps{)gYNP`vTV03L8%ZT
    zI4$m@1p;uA<zK`PhC1MXBzK}ZDKSTVxX$wrA|v)xiThXELyMTa0BBxorpbHssbEa{
    zIG`)UoF!vINAU{_zj!YxlulGAkVr))nu0mqTa8;SFL*0}5d~U@3<xeqkHlyqh4hY2
    zwZ7l<C#$aK(XBbDR43sGE10bab66{}rD*6;QBF4JB&-F2p$RzY5k4l-n7FZA&ZTeX
    zEx{TA4WvU|n|J}Qc9j*#`&|N8<;VUenPq?U*wE}8eY?^(r1s4jU@r7)clvtt0u^Ft
    z)7h}HXl6wimL}uq(^Uf{^EQVqOv7w&ybxm*4J{z4a)k80aJ?cJ61j|Q_{xf{SV1es
    zYmPv$gUOz)4Bl!;5IV4&Chsq{T-z`?pCIJbg6)TEXsi`TG@eN<7fDH6mo4-=%T}D}
    zg51wx<UTsIK$NvPy%}o|gc0UttDUlR_PTJTWV%v@CFc@A+Gb#rRwNjJ@Y2sphqCv@
    z0IR+?0k1z@US#h#*Z>s(%K}K<9pxSBT@UqB88$d_9tPl!UDuVy0(_Q5yx02%)(U`b
    zJ&QMIV}Pv;af#<2M`D<Uy@6}|5m2%iU%lM14=PWtSIX@aJtRrd?TUHlXmvm(`hedJ
    z3{rE?JV$r}mUFW|1kMNw0!E(a;&lD81#1hqrTKT8zX&GO78`1!_rX}=^O0#{vCWmX
    z?0Tr)3>1{D_p6WhbQs`_OxCMPUld-8x2q0=^@`1@)`6=PK>D3pQ;HZErHfpMvdEGK
    z;!cm%frGq#F49307p?@Il|CA1mOeQXZkF>Dm%<%CTf%X#TJP;_&V<ieOle;Z0t^8M
    zrp|Biz5<q^`=9+-{917gQ`C#L7_c0EGgwkD1)hE<maeh)x?y*MpM{bY`J5tyT3%w_
    zBm(A4wK8S4!gGKE>nFWeX~@yY^y^8_@0r6c8z$hm#TJU&IX!x{Uobe`?Gqq_iAWPO
    zj}@hZn%}5n0D<8AgtnV=XfR-lAArF=j=^cbXeiA8sbA?usewfje&KqVt+LFpqwX3w
    zM3)7GLs$5AmZ7(7zQ4pnBPUkKiYY8))tkLdpOjC&DRLckCWx||K&-fiKgkaOthkoO
    z)VsKoNEcqPlf`EKNwb+3^HmYXfIQ)^f9rJa@9k99#4rJ7rtKbhg3%i9Y;CW5JleDu
    zh4OC^S-|a`^(M!lo;WC)SRDo~^*hwLWP}!nfQJ=bQ%stRXa64RfLS$Z`2?F9;}2w@
    zdCMA5Yg2HUy5J-zo8u*wY9`tQY~IgaCzm0OAzx(rtmqd#<*>a=b8yWFYX?68mEtb7
    z182>?1|~soB683HO4%!yi2;NNL~Bppcvfq$;-~s9(LnzuO8sw|&=X^ApQrrRxc4#o
    z{DS~{YF2tNAdZv{sgYQUoT|IdN7oDfo~6uE_5SaA$VgyY`bUft5eX|^ikaNI$2%Gj
    zn(NTh3d20H<=4;G4lLXAs-Dv1$TQM)*{vhl(;efP$h^AeAdL5Ksn5L)z~1a(l`<1+
    z&E3;XMHk_?XQgL;_3nolL*re!#xCU1?y7@*>+i)#HE!*165B~E6ksn4DqId+mS@~}
    z#xc3$H*JfS9ei0f`i8An^-R@21!sfDk(1SMt7zd~uKZWkH(2A8;Su7}J4K5a@D8_l
    zAy~s-0C{(qMG#}aYq#wGlVE?Rc*a#ObPZnR-JsZDppX&Pj$g%}FrJ1&gdPQ|%WjJx
    zadtd?OL~0i-@^V|VF42NRfC~6Tauedaf)KxWfysZthXbyL8&NA58bj9q-rF5g{;Nd
    zth%Fz+U#nMtgAIF4i1P1Y3N?ukmg(i{@ps8sJ5kOa)++26tH)RH8iQ4(N389m&zl#
    zDS+-xK#uoHo`J$fdimsE><p*w<p%ISX|-HzcV@=;Dd5WA!v-GpTHeViKF4NC%0)3M
    z_xiu0?anaeG-z0i)AH<N(b@P5@Hax9EOmd@C9NCm8T(cGG{Y-dlz-uW$<O4c+h?R}
    zE6fMq6ZK6wpk*N=f1c9(aQ~S0$39tyylngRe7}Kwc2gLX51bAi6{OrP#fN(s@l3Rw
    zs*9cLJK~~{TiEj6qqf<jMf>I3=d9=coa;%{vwuJQG4lHbiXzHw*drbD8+DiaJIC_*
    zU6*&}%Kv!p*fHI)$^`-KtY(&?k+6dFhAjnnawt{4)SH_jr(NRQH=_g8PsQY!T$Ag1
    zwErJ*Bl`EEr6qJH-6`xiw~j8l*CJF6s`wm_^ZUX><*f=<Pb400=&g-t9RTYLpjOq)
    zPSlc5KN8gT`btxrQcIi>d4Bq+&^E%idCY}N4OvT|_en6GiB*|%b03v$JgO@z^U#X`
    z$oQlA-Kn?Pd`Z}Bk%y@uHmOBf$)z{CqHTI5m!dNK6uD)?Vs=G|D+MZ+`U8)X0?E$g
    z${Sn@xer4reG`xQPAFi5Rf?i%vBC6OC<~5IHzzvNORN%2r@9irYbyvf8p~Bz*PKgJ
    zQZtM(@o%x(2q)t}+mU173J4eiqErXc;rXoh??B1#vrff3zlTm`iaB4oUPv@F`V5Oj
    z=SBq6#ah9t5@6tnxF??cUjoX?ezLG`bTv2SDk-neVK_8Npfek;ldT6=cMV(Bea12|
    zIakL905u&okgTk)iU&M9XX?!<R<YckjG<|uGBwaStUZ?XBf+x#2{^n5pf_^aRMJLU
    zn9p#t1FQwa38gmkw#0e?LjV=P*|Wf<?;tEQ?o>?Ya9V#4^s;b*lh`C}8TAT89hp;4
    z$La?{5xii+&Gao8#9>i^WTc4}tErf6F93AZ@uilYcuXIX7sZTp$#+z^18%G6@>2mz
    z@bD0<V%-%K%uZw8ZT1y(1~#is&j*-*O94L{#n&@O7ce4*^eX+8>jfUPg%0M^;B|o;
    zX+pNnDQk@Dk<m8{K&9=$Yszrc`8(P0ib-k!aA%%+$@_s&J-8+Dj`cYSNXdSU{3_PT
    z*!oG@Udf^_(Slex40(bK&+F~z^o(B{X_p(`8cB_PT41mQ3|e_iFi^9vVfvNufAu9{
    z3wJ9Jd$8Usxw$!%;&F`r5MC>qzhgod$CHGyjHXU(rrxyX*zDdrCtOD#O8@{prL$G(
    zHQHbjWm*Cca(8co2eUhX2>-I-md;$&3;_^!qJjm?Bz_4;i8O1Ymwf}#@L5We@49on
    z>+uvt3bNx0L~U<@wLiCxSfR3Tt^56`+`(y$HLrSo6HEZ8YdfO~MxdkQHj>06Tme!!
    z%P46Zf?6hD<W{m1a$u_;aiwEVmQNxf0T?xB6Jj2hb0F9+JGT;7gUqV3f`#pfVau(E
    zzk(W<C=_B4W1TpB01sn!IK_law{W#aJp)F!QuYi6Cij>o6oL)Yml78`R!R3mk`7lZ
    zo9{BO5=XKVseq;L^5P`2767R?8LYF}(3@z8f|{UwH`$@(9XHqtK#=q3181V(Ve;2F
    zG{B_5TGDdiufXRup%eI&+sQT)NW#rn(_={R^%gz$_#W?7gpKI95@A`%*sCIA*k<~1
    zOhs}cCgWOw0z)aGPawsG`s-qjI<3*!tU7uD_|>dwP7uz~J2ef^=QXi=;?|X5#M9qC
    zUg~pV*JR%+@0Ov5zk_B&VsIR~j2s0M<U78nK((V|*SE~f$Y^FOS71X0gCZdlp^YLB
    zCv>Mffx%UIbmgDFyN7qZ3^0jpwjRSZ-z!zi@v(Kl(lQ1hbGK#iJHk48PJjT?=Jr;u
    zh#_JMC>yG!Dr(hdA)rkDh$~s+ZODl<Ch_wR+cP}w+2i?bxigu4W(dz*sV~wsVl}ac
    zMbWZPKv1Gn+MW9gKs3$Fdpp5R%Ao1@MAGaK={Sm}r7tU5Cx}*0Y_hG*gkQK;uZ`=R
    zZAD4@WVyLUI=upTto>3R@jkY=DQ@HW&%vhmOT8!*1niM!Z%df{TEW}{&;g|*Pf7D(
    z3<lB4fXa~8{UzV`cTV|zvaub9ZOf}$)b}Dk@;|+IhINk1>-lbdIaR{;XXfpfVmBcB
    zak!;E^5e?{5*x_=wAjd;Kz~Ah+cx~2eiNt-!)cCd!p<3UU()`**5mPj*X|!;Htzkk
    zwMNEaSb(ZS_PX%BXDz?=_lo6j0V@?mxi<$Z5%x;G#<G4^;tL3i(vS^OMNB{r<WdB+
    z7Z^gza=vnvbTh2`77h693{|3*g=K68v~o&&d%8cDsNxCMzFA)Y>WFS>OYBcUxkl5b
    zQvy{k%KKiMr^=KBdS`Y2CremZ{(_aGO?=+$6s+VC`nho<W&z=yS{mM?WuiJ8v3ZZ*
    z9dEz#_}i5XKp*ePSgOIiY>s>u&qD-g?ysD%EZR<&hai#8V<kuPzGHVX<rG7&OLk}8
    zAUK3rdIh;YNH3fPRWubc1;UrZY%DQN&LNUrNuR6o6CuHx8ztz#TLfzI=eT4kzKX_S
    z7u+K}DGag(d+KiTyHS{qdWSSh+Q~l;l|$%v2r!YUJ3X&blYtxHX;2=Z-n`d$u3?W=
    zmbfFzAIe*zf+}L4c}x!D59<4)-1EEZ#KHVoe(U%ybFh&z{y#vw&s<8?G~OM{$C2wg
    z`Ad+H$#2m=ki`GD*U5KB{Y0w%hwiJ1%QeO3{q~-XM78+ZGmlU8IddTP@%f%->)lTE
    z|L|;xRZKAC-Qho??cO)G7WbyR@pV5A-yad#=KK3~+u@mYMLzl3=)GO+)uiA0do{m^
    z5J~6-YO#525S1A<MVV%y&L<w}uJml(?q?p<;+v#@vW`4??044`n|g4cT7ux5d1yJn
    z&4C8)Goe0&XXr%d>sh)0@<hR_b{pSDK(W(~jI3M30>D94k&Gam&r&v4-;sXrG@<go
    z>+iGZJ>O%Q@%xe$H%GUikS0*%LhJIrh889FS$)cWV;Na?Z{6^sA>$;WK>&Tvs(*xn
    z$Hc}+gY7vo99@I7Do26Xglc$^CX6`aEl&7sH~ZSMU&71w7PGK+bX5ZFz}6Kk-Cl7R
    zR{kor(NF_aXa7wfmFa`z$<6V;)j;&@!<VjC={($%9qkb*0+qq;Mo^--FoHm9FSF81
    zN0)i?eX+9SfOb;xpjDF>6@}6eIi@?GO*3h{%DPQ!&#E_~J}ZTxU@@i6G%pt55(;|w
    zI(X4a1*+<2iod94sCQy(<Db`25o>BHWIL^81I+A9%GKXvlPWqpF0f>VxsI^43#bDG
    z$e@;EodM=Xf3%wY+Bzz2)%0A^?#=4#9LimVGXw)ANd*}8)K;F+t)G4*pdvLoB~14e
    zW5e9wrcP^}XJPjydY6K@I=L5BuLH}Ok%e<!y=z@@f`Tk6?T5lN@?PdweklDgrp_(t
    z>hHT^P6$*L!!;+Bg_9a;kSCyc+QY1P?kjXG>80p(j0N`@Bw%!4)GTL^<*b<w5|<tR
    z>}i1Mpw?i)UuG$5ZXpUH+XeGm%+(PfptrRpp}XVCbCAP3H1@o_J@&d>Bmg)3N`>QH
    zM8O*O9wX{M;vAg=an#IHiG6#P*L2n13GDoiaxprz)jM1Tvs-7BS1<Q`g6;I?$@Gb7
    z>eN>AswN^na3)CZ$HGm4;F4|GITOL6ai0~6^x`8@Tu#zK`Jo7RN)7~#%}e@;URJ}E
    zvP|H#=H$!9#g8-{^Ts{(fvz2c0tJrII7mzXSUIr4hSOYemnd&cLw|^`f(ds*m0Nv^
    zAPT>ngmgy+Ku^G^PbmC~5R%H=u6&j6m$3}UNn2u6vnuGFW=U1am3(qq>ml8_j#XKM
    zfP&3sKEYx!u(YR_h=n?{f*!%kv5ii9vp#VxtUNmiSr?sZPtHWuN&BSN>l3Ja;#e98
    zBtNcNg4=ao6xvU@=+UO{a!kt3FF0<m2}7NPEx_LydSD^WcFUoxBV(*N_I9JsNw{%4
    zq^AZ7ay|kk^$dL^22cxD0ZERSdd=PdE{TQ~hiq9WY#|yiEk`D^IZCXrXFzm9sRhs#
    zhV^NCHK{icV7jrO2)NW@D`3L$&3w42vcx6MF%BL{=1bSB8v+#9U_h8pZ<*xDSr~O5
    z4Bwb*5$)^jh}`wt4Hs|9!G#FI4lUr{v@r>!CRYQ=NX5x7#VlXBIo8uW+zxGgha3?1
    ze^AfD9m;gSuhaA#2~JfCnlq8#q=OGY@ewR^LZ9vntGk`-eCQJlT_9I+{?B_gOc3Lm
    zN8HzPmq%BDp2oEfy``=;L90Q-sw~AsE^O5!8+dfRw>+y~n!F>e^wvUyj6SbPn9Ej@
    z=MC;GW{(ApH@Cw^sy>946ZYWUyQWp_(Lvb9e;|6N9kvb5PZnhzQldK(mA7`yH?CJ#
    zzIommkv>J0GBF-5;zj1;<2Tdv`|-R21Wu8r(i-qb3fR$`5N3T#r(PN?_nEH61#bnU
    zPu)iUroJfhw>_^KtyxF?ecI8h*Pr#kmd6JmKHnGCq~v|{?~Ri&(|2+;7DkeyPkY8v
    zoRRl^8zCXTS>MicmOsd8d<W1+WRLseVO<k*8!v`%+`Jqo83(O0oWaTZL@(Kau9d(a
    zYpK3mn^Jp7;wmm?R;u{?F-DgCzRdvzlz#L@Fj)>?iLT`5*4+Z)6{qm6mHutZy2H$3
    z)v0^5j<ANSmylZPvx8DKejD>7n6b^-NkFlBjIfP-vYc^S5$^01KR{mzhnjL-bRU+S
    z$h+#R)JIDM5YAdxmPf9%Ti9_Uf@`@TVo|laA%VxFBm2kMPo?K+9}}c9cuW9MP{V;+
    z&20xU<{G3DoMh9#0q+2V`$iz-jb`0b@mQ-fa%{*3<V<Howd%8U9f-4Tto%DbAEGPz
    z%iZv=H}w-akv#IXd~Sw{WDy`m4is3g7@ubE8D25CX9sYtb~<z-o$zsbW>E2uChcCx
    z4Smu0(!JQ7n9=XQvwh1Eiex@1;{iW2wu#+{0Mr><!~nB9{ZMYe9Dd7ti@Y%JA>!Tm
    zJLtxemJD!zM^W^=<)G)rLt#n31|}<<n%YmK-Fe$TvK*20%F31d_Q3<T@`U6&Z*E!s
    z*$R6s=Et=5O!M&b7%n%>$Bc9m+qUnFPadCPMV^hP=BtRbR_{^EXZ!!A5B}tOl)?G`
    zA-(c*r;A`^`xg2=|G{}M?P+CymK2=5ZNHUE(PygI-^-J<55Ex&DXQgsXjhI6Iq=+e
    zg@NQq=de-_p#H+>u{Gp}KKSw3hdgZWsM+B9^{u~GRZu&kHrWZVSb9?$QP)CYW*93&
    zbP(mie76p`t>qOZ<POmUXqQJ9F(k@sjR@CCmjTSQBQm}0WESxS>2u<_Igcmx^{(5l
    z5BL7WYSgOU*3HEC+J{mjq`RQ0BWVz&OaIi+bFAm@pYF5y>YLK}K83wUuGTDaDO<e*
    zGDMmfzY8c&-y3b!p~a;<n*rIoa>KAfNAS+_b7DpGb>RpNc@iDMWJ<llo#V`-FMtX(
    zO-`(i1%(fKH0_OwXEMEHhX_=CEzF?N--WVVMoF{S$3yzy7)2ZJ=17VLv+d=9ido<&
    z--Ij@j9i{^evJ{e9?uOJM;=y{*)#Y@*URi8Uxu=;0mVs}Ex6rSc7exg!pYVr%RU?w
    z_`O@iX)4H54W~#$P-q41N$+vGcI(YF7nf5HL1qUV*>2a{&ln(JDp>E#dqoFG@S!H!
    zFsV`t&nE&D!*^y4hHY1(Mpo>r9Vp`7&1cMm=?`7^;7Y*I12)u0X03Rn(wUq1Y|%8{
    z09qD_obchEZ)ufDMRiSZX$~i_EI!>_S&(GnTlRTp#U4(OVDH%8VjpS1wi$Cl(sg%V
    z?Ql-8D)+|h@18DFrUfn9&~JFRk(>KS=dwe%V)DqMVQCF2>yJFI`DmR-%^_v4vW`U`
    zBmEb_qyV7wC1`Wjq6OxPt@$g7Y8fo|^I%=I3V1eN4`jMIsm9qlz~DHlqkCO1Vj$9v
    zN#mdsQ@_Jqz-#2wc}%cjn%MOl<<rL`%)u+46GV8>c>19j;t(o}R4eQ9W_hXu*{wOA
    zoQWyeDBuXe$Y7m-;!mtOKUsvxRaIc^%$^`)$t6|*C2yTX?cFh51aeej-U#2>JRgDk
    zB4{XQLXJWfo>=F+I24L2!30OR2t!3)1${}_83B$0Hq?Q5lRnp??wRIv2w*_R;<E%g
    z_XfUTzKO{C$?+t@5QQu?`2~lPHW$GHvw^hYoVJ65-h-$Yp3g`4p)Xvo;z+V{H~`2=
    zV%Bj?YGMvV6{jyFGSYV?6=}fOk{<G@T60#VpPUOtnUqhfTLd8L)I12elB9GhegGS9
    z-sYyD!}u!oCO7>xXL9GNRY!OTM+3Jy3;2Fa<muHrSnDk<oM#FjfP!w;AAv~o+{@4_
    zLo1lL{^jH=Uz*#K)t3|a9Qk{J(Z-rxXl{lR(v32F^!`Pl2vlZ_$r1&WGvQR@^YSw(
    zS|G6H{k!!hgA0L(sn%&~8-G(pq)f1iG!34xaBpY4yy=l^C=rLbQKU&=Zi1yEG&@Cs
    zB+K!8axVn}lpMV8Kk{$^RdQKEFUCpzrRx=Wr=^BHT8g2bU_3wPMri1>HlX>~>-?K(
    zBK52^_b^5Wa7E|j->-A~PEKM^zZK1e9m$Ap@*Wm;unFti0s|e5L$(q|ei5t^RB1l8
    z;!doCx{tet@+Fm(=10!KHqK-Nif3D&AhM?(zg<B7ZsYcy56vPjT(rJ{#ARWi_-bYU
    zCGhlkA-Ra!Yn2|SP3(mXviMq8SjDfc3!Y~Uqul1Zy$3&%>Q9bG&VTPS5dgqf?y~_o
    zqz0#mRg@y3WxGu*h^iY&kQI&~`r%!bn&YJHFL01&3KIR&GY=_ij(+9wH89!DDLDS#
    zEFU3XDyMKgpL6Lr*aH}_m%@cS07hQZOnUy?n_@*sEnYm+;PLHn{U)9YiRP+!>w0M!
    z*Zgh$`}dr~=c~AcKg{D#^Jc3VqSw|0>#_a}v1FUnB?15iuV$TZ{l{{)-`4jdpK$gn
    zQHMszZ9(a}V7u>+v|3KpzkWAY%qybEcUu5N?AQm$CqnJ$$G%x-5c#xNXnD`f4teN;
    z|8xHKt-qIFFHn?;`RO4$TIfrzR~DJ`JO(_{8Wp$F*cztWfGd1Iz|Jmk^jeIZx)-CF
    zjhu&Pveoq+u}b(Siq6{Oz%-!<e3Ou!Ips>}j&}l{gI1fgTvfhapOuF-SHlS!el6;X
    z&u21to<JhBZ1#dGtPJ@(?1$N$tJ5%#{QlYrwN}L(wlSF<wRbfAeB3R)SCwl^NM_&o
    zXaxsuY>JJxpgHN(KX5g5-7#4GWw3Wxd)(-60`buR?%Pul>KO^tvo|uID>wr_>peIL
    zTvzu8`-V|r%n&YB$!%YV1^n{vx1!sk9Z3FY1!=wuPklzQLG&v!L@X28Ctv{9uO2Ko
    zroVv8J=SN19}1DUd|2qalJ}DXT7I?e1^t;`I6WX=xYj2-e~ElFpqrs0rRi@Vbt*RV
    z7=9&(75=ZHsQtIs&t=eO#DEHf^C_2RZ?BRF<K1Wf#lh!)GDaI?c+Bf-WBo^YDR&)T
    zT)y|(u_Vz2UCnE)k#grsUU}JeZ%?z`*FUZ6o~H5<^XpSQ>h@J6z`RZ#-2J{6&bL)?
    zuPogto^6@!qX=|Jt6a@r^wH_}$zMtC*|+{)fPg`ER(}WK7NwWp*hF8{nNWFIPmu0|
    zS$p{_QgwHk001BWNkl<Z9e<ot+{aMpr2(tYml4pa-aI9D*3n8%^~^UpjbSF{#PtM&
    zh)cC#Y1T|jXXQDM(*PDO1KwQt5jGx;^QwoIQCD!yjv4wCxVZ6e_obIRe*ueH6h8J|
    z_seC?v&8*u0~f_CR-Gv=pEL3cbtYOpTY$ZTmNJYhnVL$c3fR8aO=<lGz-a43f`BSP
    zlbbcw$q5N1BW=<SO~ZI4wXMKWRwsUcQ4G?!w(`EQf$oTa>0V{bSSIL>Ws+=^`+N+b
    z=282Q9P+5Vh0o+b@mc>wXYift#jt*E2-9yOt|37NKsmAHvye8dmLFPg_$~~=x-J^h
    zGTuqGUwB4_wDaNkjs~#9L_t~zG!XQDRu05&pLq_j$#>f{oQ`Y0aOVuV5F!#+r1m&z
    zCpU^nfEVb*^a|i;uviTUr3?CfJd<fIWPf*md{o}%wf1{1?<YG>FfjI;6Y3F4MQ4BX
    z@UX|!gu4SqU_^>ivPf~?n*kEDBq#7a7qbEl0tn>KNDB!d($dx^ZSP5b*q%kBIv<#T
    zpNM+ZPspP@bZn}p1%=@v-dGhDVu)0tfK3~Z&WB(+XhhOhg*y)~1|We-k$7bA&##x6
    zwb_mhF-kLrhH`QyF>utP(JEqjY<x9AWXZ>|70kDgdOoiSw{LT^lQl;-$&=%WfsEAm
    zOvmVIW=Z5(lK7!4EZn($`-0)^mTu$eM*=S^Pt_wiDMn#yDOe`GbpUO)aXQ?i)7IeQ
    zd0%c;NWWtq827r|c`aHZs{g6e00^2Lw$N%aIslZ@+q9UhcLLHN;JM?z>XYCdWML0R
    zxZrr%Sci%;DNxDpVsfuuuzsiJSAWT{17ZG6OcT|;D40QRMV;jupkmqF6tb@Ej55nV
    zWp;+JH(6TAlQta~f%4$)dmFG|ql3EtzXT@5YMLlO^%H=4^Ryyz0T-RxRvBHKoJ)BF
    zwcAoHN6{6S+whnc#cGC*2D1|?fq)ODPl=j+%cb6ca`U8biXk1;qHMDJkTwpbu($cx
    z(eC6oOLU;l0OA2$ATTOtausLtyLcwD*7m)>;7l~PT{+9V=t&vYL(e2UP>W7&F%IOY
    z$$8Mcx=QibT^*qv!xx3D#YH-53@O5{HN??6OTLPpuoat`z)?LUiqUFc_d^4TkA7-U
    zvi6x`8}n^e^>*NeHa%zaGbRe_ns=&08bl(mcQ@X*QBtl!X<s=?j@bho4828Lfu-A^
    zY2Ek2|Jn6wD+|#)iOE=b?y}9{V&_zpoANixx+eu>5Z&T@#u}UJojICAS-_TNP{rYD
    z^pAH*P%0u-9R@d}zgL+m-^unze{=f)ciDs&u-ETCRj&jpqXr>&5^G(z)kC?j&w%g&
    zSOysE@96KJLl^|-ER$XOr#O!Y5tUADX`|wEPUw5mJ+J}AeK&`eX@+q}=>CxpNkExe
    znQsLIHQ}B;M-b`sCUcu(XH|{^P~XOQKe)G*FW#OWyik!Z%Wbwz8p9eX2&i-0*6<#n
    z?#KM806hHvE1l5Dws7dJ?>nvS7eS@b0gp*O(sBn2pc6PA_la$L$dY*-faG}Z)4P9i
    zCE#pgQH<9tXL$rRs=LX_Ou>uOl`?kMSB!yRa6i421^y@TO#a5t<XH##?&+8|K$g^b
    zu6JK9MB3s?Et2GD=p}o+x~u{UP8_K(C%Xa(`@eu>BiE7EyR2Fi*)B(J+?#ng!OA}O
    zU)<PQQ<-d$r*@LA$xr0!%)PiAO};P7A1b7x5-TNn;D3WN!7@0H`Yc~Cu2hU7^{Ru*
    z!x-<SGif*JM9!sP%t^*pxZJ16(;MtUv?>Zj={2k*;uLOTzl>){#k+_xCNu>M0J!~5
    zAc$u-7G9|jaR>fiyL+QDD3r7q^Y6?m=x~W~%EV74O1m7ejNBF*tgqTJqN@{I5%!{g
    zp~bt^G~hr8R4<G}!;(#}HPKgpFZTP5qMq_&VY!BgqsVP*ah8pf6K7}8bsUD;?AN(g
    ziAu7ZluGR7=fP@OtoqqJ+_;5HVcF8B=4mNHD`Bl8h|yh;`*$dI6|g54C6X3yZ!zUx
    z-tVsx9k>}UtvA%IS@cVfUQ-=R^d;3vK7j&=xdj5kg4usi0m#gUB_~AYe}iY>v|h0n
    z@UD%?eRYZiz;k+#XH;PUjCb-)F!6GS4^AR3j;3y#7Zx--qT~ZrYQl>P3As3hXQt!o
    zVBc%Ut}}UZH7D-HM2h0dx~E-OewP^6oJ!m<;{oPj4JmfnI-je_4J<?#5e=(&_7nK?
    zJ{s=RTYe{QC2C1)PLJ>CIja?{=4kVE@j~d6nzP7wMYY$umCos-!L$nzYCx@W$HiOf
    zAPIJnrp?Aa(<paR>1LT)>fVpDF8Rf~{9T0V2}~&@CcB0PD9YQ)X*Y&>Z`M%8_7C32
    zDXnc4`NGxlOh{H5sh*uxqED_omOKLeG-@NDG)Sy9(GU}#mC@21a4JVrx>o8Y$^0ZM
    z(ch%}PJkzf@b@-pA7F%v4LblsiP9ZBV`#bODwXExG;_OvN*JLhIJ4|ieV%;u`ThxT
    z^j@2W<?>0OIs9Z;dLekoSizmHdx|WKv<B%PT7y)-!n!GEPfnu>Hpp=o=-|*~Sn1!f
    zP3rp2cqqmO2*{P6%CyeZ4rw$HMc9p`xwb#ZJIEcv_FTJI=>#@UHjFOx{+QH<@zc4!
    z#pL<;zGI~8+Rn5at$z<p)co6GR@^hY0lP|W<j9S>iql%TlBBR)L6a+HO1?+x6%}T;
    zmw=PX$xkvOW~as2(c0iOv8h;N2tX2_OBqoE168N*1g+(cw;UbH)%FbstUXAAvtDVc
    z>)_H8D5Q;Y#<Gs&&(@stJ+J>rgf<BKK7p5Q{hh&(*q!W|pyOMPs}S>XwL57d$%Br~
    z<fiRSYmimV>Bq9Ne!WB3ghsl4+JnqRfxXFAUFm)FRB;Xb1VEIdd4M~o-J-sZUN4dO
    zMvdD7dP5^`7)ohhavb|O+ZR?CBBQj*r4Kl^+z{Inv&BIfQRX{WySP_=EKKWCpRT}H
    zu9t#A?7N{F%^OI#)U4=0Sgz&CX_V6%=xS_Wyh`qMY11%2^PcWqsCE&!LiRhEJL$AG
    zfnhM<+k50OXW9#)mth=PFExI~EZLxp0g`%Bg?~+`2Drzz-)f!Fe(VLG{{dM1F6|H8
    z%6PJ^;%pHtVz^KrS2_$&t^;0Rkp}Gm%k<KtQ=1uDtk1Or%Y%~{QPWI3Yi0ADc?MtV
    ziZ@uKJ}b+pc_VG4eq<R_qw54Az2WuDd<aCL<?`jV{*kFx9~a2)3I|l>vJ<RT0m}02
    zi;QyMh3IDowI>a*odbb8-pO`fSj3sZ7p_;VHau2SIFD4qW{Bi=N@H6~Vu<ISDo%n?
    zgG@A_oSxWc2c1i?4ggNpoM|<X`|Z>XA)|cC)q!W-Xj)o$y1t=!jRUqi8@1eAWh#y*
    zp7cOqO>oKlduL0LJldCZJEQmj7Jj(*M_{81AD<mT2g90E=}QK=Lm|zvVibX#02P6Z
    zdtAiP6F^EDjt-<`wYMeGT6@7N?eqPzT5nEb(!Ba^{ag0~&FA9biaXno)+XHL@w>8~
    zL#MG&CgE^jwc$zIOn~M3C<piyeKgQG(z9<1V5HnfE0j5=J&qM=O~E3o*0G#7`cdxX
    z+t!@n?~&X^69Z-A@H3#!BUrlzgW^ooX3x$oQ9{hH44fm1+X=Rt$Lx>S+P?2x3UKbP
    z<X${imNW;?*sX?+&Z1tg8mSF<BVwRjX1O;ftR!8^iBpPu9nVB-`v+k0xWD~YQRehc
    zK3~b1XslaupN#GwiwU`jqyftX259br5j?p7CKnK%#^R%5J%_6O+XAZJWm*+?H6VKD
    z@N2Qq_DZ{pJ1Gy9(2*2SS*2BKGS)}ZkSozWJn?eoZ@>`ejXICsS6-DXV3N8I0+{1J
    zoYqD%d*nIZ@4zT{y!;QYkGqDYup|I&#4}3!{=Unz`={#a?qT2a{8Jy2XWHgZH2*a}
    znf9=Qxh-4l?N4=Cxoeg)XwUzw^kp5-K=NnTpS+(DCHl{M3;y0BRZ5PUKgOSF@5lYF
    zNB>?0BAsB3N!Kr#{LQ)@%Fwi?X+8l-es1wu?!!B%_VwaE=6HRl7r4A88+lGKRQA4j
    zRCK?0s@tEwUs3RxChuA4BJEp$FRTn5cO^@<ymk@0IqW6k&q{8@td*@PSQZh}fx`{3
    zSIEo~v&0nM;dJPSGw(&0W~<eU0eqQl85wjxPa8Rh7-X)z#v%`xdvO5nm(h2e?J&Uc
    z2G{KPwrWofw*#=;=oU)dT+KdtxW?0qeuKFmA;ct^>ZZDuOe1~-_vY3XdH21SIO&ab
    zC}r}uUm;)L!m@*$h2a*nO;6fPyA1DNQRdAdRbv_e_Z8KlUP`gmf$Kj4V59utkl&0<
    zpXFQXae|CY^d}qxRl;^T!I1Qbv6e4q&&K#M!pE}UR(lRI|32V$EgR?VbXWD(f)-a8
    zheY%#?jh;`2zfAECBvMau?dHcqxd7f4s*2-3bGcObz7ZbB2&Ekw$oaBNx2d%JH{TX
    z_{U2@Vqc>2Vw?zG5x?}XihppG4EguZ+g|%)PKdpiDV42Ii$2q^Z0bX1u3a~+<LCN*
    z=IZqK@7k+ehjJX`Kz?5jp+CJxabo>UZ4}M7#lD|=lSGAh;yWo)KmJzt#iv#L6Gz>j
    z(Fi5SJq>^cLF7UufH!t@%e!<LaN(-Ro!nB^-X+KJJ+$v5y`Itum%;hvS;t)>_)w00
    z>o1l?J*vV)-@&E8XySF$LFba5NLNe8er$#;5p}diHRhz_h@Oc-{hi+A2?l48WF4M0
    z9~0pQ$XO&YxEEJInDDW_Q30m$Pk6@gzMGj7dJgF0EZ8iYZn+be53~Mo?{9P_0AwG|
    z^Wws$p%!a9h+1)M4!LgRdpKda(ih6#WQg}+H_ipQP5hSa*}0Xf17ehR|C_Sp3_p~%
    zQjw$5o+?f(lTar}i<YJvVvYl_8$DNjjxDUbT9um-ZedfPqC0W%ec?8%FHO`T^<=zX
    z<#+u*AlKhtX&Oc?A)Y4NAjl06s@PJo=g$hii+O;TbvjE+2OMy*4rJcLggk8URPr^_
    z>qKt#D63a0wXQ2T$X0{hU=dUtW7fb{PHX9!&KM@N9EH-2z{H`cbN%XbmF0$QQESdz
    zy^aoOpTMG8`*Yg!-k-ilMt6_P%EQP{AVatOTBY^G$}s^WU7cOeq8G3>7bC7oSZHUt
    z0tl<5^=Fv(OrP6)UilD|ye2xqQH_b@YN~ViuJm3}O8S?m_XEw~iY2{s*u^KLMW*W2
    zmL<CrbSH44?gR?~&4Frv^8Po$q(*_J;GE2L;16N{&J~%Hog?HZHNN<0z}l@o*s$nT
    zv#Wg$=n>1#_6C#ykDs==?R~LxIBjXDsA`28MD+{H-@y*L<FYUbFN>osLWM&sPvl9m
    zj?#LE1ULHK+o#rXPUM**64av3Pf&Qi&sP6A*qp8$+M4*~$dzL<L?r<k0{&%bvA7##
    zI3t&A?i;5ycu(M)97C={V~1sul-<1wTlUXBl_r=x!3JnWXp1aS&+^bEIa=x}EOj?A
    z%i+0mIIP0iV~h~DlHv@pm&fil-HyeTkhJ?&sYq*8UUy!%#v2GGO>CNAcQ4)6%y-bs
    zH^C%VNs1@S5k4js=D#W`eNkkz8Z52`gfbJSEND0SP%Z(Rhmd_T>(UQICnG@a;~80v
    zpHHntn7mgyx?N+Z@bV>rj;JrmSSA_0<QCN(r*N<PoQmxq8JtUarF6dJO!}U}<^Kj}
    z^1FN%Jh_2TUJ`7>3pBVhFR-`vbOq`l_mv=H1R1k*XhVXEOjcQga*k$Q`uR4eWc(*^
    zobElI7w)Av=BcFBjuU2>^(m*dr1O>iMGgl9w3U5f1R)f7Odz3hRk(ysH9G_1YohTz
    za>1FpkIbw0CiD|!6T6qU_rD1ytaPm?^k&V;;qT@kIMFkbSnV_hQvKakD-*FHCUF80
    z&)<PthJ}<Jt{b2{<i2COnO?u^z29*+Ek?noJ3UqM1PS)il@QY$jupHRNC-N5%?Z6e
    z+|S?kZ0lL)q9FTBck*LVg6tY{rKGnF5KdQYkJU;J^RRz8(}~pqR$kNYUhrC6eo~g3
    z*Pf@nt8AMa5B*5BLBOMNY)I#t=lm4<Ot4dPZ!ojcb4gO+<!#_z_1fv(61<3MQu)Dk
    z-U-mRaADRBm->}iSA9c-YiXQ_G1uj_^3nG(-^a77{_AT!Mx^SdO9mUY5xEO`WOe}H
    z_1PDFQ_lAF&F`Z|2ggZO>0sE;?D>4nNz5|1BAu)e!A?hd?!*c5>iV{&(+X(%*-7cU
    z*Z%|rf7+t|?){;Bh0RrZOY>v@JZ{Esn&Q`>C;nM|KA%1R_*~Qe?%qx^^S&-p_ajaJ
    zZm<E+dyYy(vTDmCk1CM)-;V;Uj(_=3+5OtzFV6pY|F7Q1;0OQF-zz{O<0*u3Jqo^(
    zP%e%v5N8jYbVT(G9@4q5)STzJE6F~a<r=>%fcbjm%6>NolNmdJb$hRLEX62T0G68m
    zkWVj6cj*K%pw2v|kHR}GHFvH)acW<mU)OtYyv3=O>(Kwd3p5x`fSgO`F#$+9hijPR
    z30{3qIi64;CxBN#>gpBgHK6x)#Pe24J1Bs$&UM5jYiIeux7|c^iI@6s5d^x8RN$sA
    z!Mj@V_U$1`@I<@Hx$C)LeTL5MB*8r6rY8qAR}7zd!?5hG#X$GK@eNYQl?+*szIXf+
    zYrzzj5kh#+I^ZbP6b|KVqN460LPyfrGz)SxZ1wd{E`gj!^0cXlYD$YaS{y~Di*T8W
    zmO*PxdoL7d)>iQN`+jgF10L2HAT>(T$gFJZKJI;P73fgT3{rgKc{RFstkx%TmaP8Q
    z7YUNr0qh>!Lh~4%L)5<%hFM=Lq6J3CgZkd4;Iig8@3%$is59~T`!rFHEk3+k-+$m<
    zs0+uv^^iSX()<0XGtpheu+0Ca^VNwflQBQgP8aJnLwD0K#T?IymvR-Hgr?cn_e$$`
    z-^;Rg1+8uad5lYRQ63?ibum&}o&as3cZ?;8`T6*Lu^%u*kGi)L02j{vUia#}_Xt<_
    zO!G}Jk(E!Cu$F6lFIX*L0(D+a`=}(*=zbeeM`D?0nuHh4igIr8Q2{#|J?{zk5Wl&B
    zuk|f1$}5s$h^72&T@7o?<1N%rq{S|mw3ep@POyQrV%OF*fvoKwJNSq8)~Ykp?DWuO
    z%$Ci*>#HSaZpMqoL>qz=1p!Ca2KG+rM3`48aicCgIdLJu!qL0!Z`8&*9&kVQ@N@&?
    zd&MJxh(T5pmq2qA{~`lWXTknsbTk;_x~jFNML?CrZ%>HKX!aG`Ou;JayN^qlg~cn{
    zI^FIo((gq(UGFK441LT7Hcb1_nRE(#>3Yrm(+wzYvhPCV$3B)Kzt=WV<rkB4$)HKi
    zr8HRF%&?dBdZveCsaDt6`PHrIFQ6U8%Uc~ZLllVO)Z(ZK`i+z_P&kV|xV%}`Vm_@!
    z8p6npR#<lU$Tg~_Iw4(zi%2Gm(A~p6*f|S4-{siteBo)5<$BzUfH9s2O`BOimmwo(
    zK)R;iVp)UQiepaT&yvhwo-^{M7#5n#T6pNnUbKc1bqYLkfurAup>27!YnVN@AeNlG
    zn^qkzL00_{jZ>WN-NFblzxp|cCrKaG-n+ij5od-4=qSN0p3Jc;umMS2L4?4Bd9NxZ
    zAdH!2mhnw6DaC4Ln|g~1A5e8@JqgbT4)Wl;>Z}Gh&Bf_F8~PUdpX4sp$~&o2fy7tH
    z%XNypbT(StU;w1@qI{Ts)mxPTlP34m`+5L)9LLbW@@~LLKOEX_ZUo{l(1Lf>3f-U0
    zu|>H{IFr@w()Q`VZ>!D@7L^WLy2q0ZNJ>n|deSGs@0tvC)P-^accz8h7m%?e?pW;s
    z{6bBt^6QXQzunJc7rB|k-#O9m<di|uw6fIb;;bgyfC&+Eq-#xqNyHgW?aM-=aS1y1
    zv9J(u)7`+5hso)^zL|#0a^szczk5u$V5_ePpOPaJFd;Ba$gu5uB^*>R(|r?6>_W#~
    zu>7Zn_h=}mE@j9N6|$2yv#*oi+d~$Jo1k;&vES$spWzUy0)@(_CJZJUs=S;RC$;3`
    z5lt@Y2P~t%NrTNoI<NsCQ}5}65+IWG&bmCm#dEE@awbF0or4@D*3u{MdV|WHO&=g4
    zthQ`)pXJmo9<pVrFi!>hEqOt}F;{qUg^O`WWPuHkGVq8)1BM?;aVD1aysZ`-E0<a^
    zxdIbJwguTLPvJZW9Guj`v9u6$$L3XZ{Y%ti3IYTYXlfa0aF?kQVpDQ2QEUT=0)qQ{
    zj#-K#D#9=#T_b1wwXX_ssnhp7q4=m}K<3i_2ABW{_QBhxO_4`TT2#E!+R`j8J4h?;
    zDe3@R0Yc;vy^QjSJ$88diM+Mpb(PK}!6CR4VSNopok%NDHCh?~22H8}NtXU5A;hQ-
    zo-4u)FXy)Ct*OL~Zw+F`6a5Bf@@ZFbd~275Esd<IJJ<I3{lhh&&lQ$61`V_H0umU(
    zB7S!7Bz+VM9=9%7?QH=qcMP^Q6igYa({*o4RcbKN{aZK_OU`6~u<Ba)If!lmQ|LgN
    z!zqBU*30Cws%peiJJvZ<sUP99Scq=);(cS7fNI}O|L!$)-*?0IO}Z?wso&X$BZkSK
    ziLZf)oUI@eq3WQ7Kd;Be-cP%W<(LNU%B<fr;qPl}PsLha&+>=A$|ys9P4K9D4B%?6
    z06=R%)UPEKg|i`}q@ON3wCB{mr=H`#AOFwgaW;Fd(gq{fsP#1*+iVN{%({-f4<`Dx
    zV!xpMP`L26??(T1**f7recJeBp>>~J&f1`lWS|RV9)Y6mV}51XD`Ny6*W@T>A8t2}
    zX+A6AA2<K({m<pvxBg!KFLtb_m4z0X<Or6*jbR<L9*)*=yrd^nkxMD3spJ*;l5`!w
    zog&6s)o?vIFjh$m_So80{$^e4(*%`lJ_34ex48iFyWuLyho`ZG6rp+<8}Gy?W5_i}
    zMz?#OY<9y#2f<y}!;Bek-(@-ueM%^`8}ycjh!^pN?W=7U-Mb@g^c>GkF*J*w+}}Gp
    z2Nu;65z2v%gG$lgdh!?y_8*KgYbaK9<WN-B5qMUrvFnXeBwDWY0RU(#P<9<f9O3e}
    zP3$h>3j3$W&fpPaR)o-KU)=9NQWDxL^J;&RR@Q2d7ucj2=$_*Jx+0DU`<UE=ztMZu
    zf!O(YOU#pOwCf^oL1o?&gNiM!<c<!HZe0jLE#D~(da5_-g@1m%K*G|1NURLY&0dO$
    zot7RpkW;L7ayfgnOEF-GFXitnjFDc#JBCX?^I&2XPaF(*bkd(h*uee%yq-_mngdnN
    zK1k@92lv56F4vQT`{CMG6N2a;z59WC*%!?naNLp`PaxnYfX&UQBeqjQa1|G1v!IGb
    zxyV;~UK*fb_nokXjt`rlGR_=6^w(6b@|o<)6(FXl)9J1A$TqQ^6ssz&_orSJwo%)}
    z%?w;DA0o*!AF~!yMuU7$^So$?_%Yp*G)mN8xH6uJ`aSF$^|aST7L$Qwq|o4Gs@6e8
    zbLreZg)|EOMF7R2^N}mqcy^VtiCS*m8Ck)klN4j6C6y?z(xB8jkT+YGkP;K7j=X@@
    ziyVlKPp>TTeDysSQA!D1CV)=3QyPI)xi8$sACQXnbX7+-wU$X!NI{JR#lu;egAv2-
    zH_9oFfCWRPvE)Kx>{fZr#yS)9z{zevp;75Br2Oim@<q94owJb|nO=1k4HV)3birzc
    zpiWHtr}QIe&k8DQ%W19HniwfdbnX-SZrK4W6JPGx7??wv<V^M*#<HAt{xyE4d3+H}
    zmdID)Z(_w>b9GG@>~ubh^g&Tv11pMs$)uOID%jgJ0fK{BS6XsZ7@;Wj(=|L$Ir?LI
    zj4~83qJ~$APOH{S6ETLs74KYF6~3_@(i46><Y~Hkw_rE90TWq4Teq8k``*uKa44r%
    zLj~Oj_tLbk-|56f59A98z6-E7dx(>rw3D1mt|6XeQfpCi>^x5g<c6w!2?C2s-k8j4
    zR3xp{Ux7&jLMUU?VndO;-Vp^Y?;2_$^jyr^0)(!EbUCR3CZL<;r`lRN*=2zdKM;!j
    z1pVTitn%|hTvP>i-!E2ZT8a*vWSK_r!$*RDe!aq~LBuwJ=$I-MK7c@?B8=<_DBqM3
    ztE81!q5nXONwNG$nM!30Y>{acseuwZJoyDA;n2qf!`FlnN9ZBjRvYB|<Ujg~X!9|D
    z;y!bU(g*}aRyjsA9L9ZuQRPX_MC9Pl@l3LR$GvA7)(e%(1}aKuz$8fYs_VG|*Z%0V
    zv{-R)!8P#oJLg2Cv4T!+w61yg6s61xML@l-MFoSQu%bbw?s3eLiF2BAM%G}sQfFaF
    zWAqbgvulbzjxvR_Z&XyGMW?!#<+(n`AkSXYQoBcs7T5uBACKT@Pb-&qrq$P+2bQ;{
    znHV70>2FW%g3yrJXwzgP8%$dC|2Ag=pxo#Ilp`BB`Lm-djerG}addXWDvqT{(+qbG
    zVtLU5)64e3HPAfpnKNlKsIou(8LLp6fFI96fl0$ICZlTY>?ZDch!%P&Z^@a2M=vL5
    zwh!A`!wLkhU?BG&+{5YjbNoM<m$%m+`IV-LlO3dWD=;pFd#>7HIu8I)u(0AhtvC*0
    zTuO>9(d*U<%H9}lhfdUwoJo>OeF|qH{n2367N*#U*j#Ln^&NgCb({w{??G;b;ldmc
    z?^mEef*lOmpJSbnPQZOHJ}VO2X%QgDITnTa`JfS#UNgz->tK?THk7Ig1lbV^Hsnzi
    zEYg-<>k#=rkz;#994U(3Eo!)G+{XQ2?=X@>Go6O^F{w|AI|Q+wUIh}!dKoAor9Et`
    zzW@Lp5=lfsRLCTbZ6x0gkVInBi1x{aSfsou0zf8?uje3RmN9=vpZIUmw9EcEoo`<T
    z_OkyBu)D1sp_J_?Yr)ASXM#JAA<KJ~Wpdcwn^gg!nB*L*101<-+&wXhJ05I$1BL4D
    zZGXnLl_&!=w=2M7Gk<PJy!3urON=>4bEcGG-6kR3zz)Y4YVjv}=q_^Py1i0J5rCc3
    z<g-@pT7w=$<kGxG<hJjD$s)lX?b5sUZm?Z)dygq%1|T>Zb-GfSvo1B#AM*aQ>tyIZ
    z+&jECIU{)@m5gCjC=!MBZ{w+~?fX=A|IW`ON}DUP{{EZmlYh!OJdZWOANP-Wnm>ll
    zQrDu2%i0qP8S3rbAh8Wd?C0<Nt#$45Y9`3&CO=*9PM^G*`|i&Y-%sQ_j1ICm<F4{c
    z0j$@lp9lLDzv&M%{9q0o>~DQdww(Xz-%ECif!gI^Z~$5hjr2pwwDH<X?uDb<ONHS=
    z9Wyytt47g#0TB`st&Ee-Ha&|#-VF40{Ccdy@qdNE!2eW_!5uHmLHIZGN@3XYU9$53
    zskk95BlI=YE&LLmXL$rzUJ0ZTgHsMH(-p}8;El`wCF8npcfaBIS3Rr#&S&t2w_h6|
    za&pG_RM<%Nr;HCW1-&-it=cZit`Og_aemT?ed-|s9AKpzaG6-v!^6(R>lf0xk^H8k
    zmQ|<S8XFKTD-m*ePsmFq9`C;LU>pORv~YXtn*L9zPn98Udl}1r<{b*=_Jew(Lp^F6
    zyXMziNX_7DbJQsz8UQXVdqZ`9S<}>?+@LRRl%T9p>I{urJ(Ftfg3s-rf4e4qyhf}n
    znuA^Pr{$pz!m1}1>fG3D;G<e*Xhp83A%Q<DG|_bJ4}V-GpYQ+0Gfbl2IfTO82gmYr
    zT^4Mx=H~kMTuuJ8zJHUZ>-$Wr?YR#3^2OFR*}T_jJ&l1gppqr$u~N>_h5E!1>F?Te
    zbK-^v_hQH2<=Rl(kJleL8?hl2ZFBu1mt=#^`&s?i>m`nc!9m>3NhI*_-l=_m!e#sk
    z=V4}BWsmuP<TU>F??q*(YM9&1lq*DPmr4m+RvPYvCAU%n<CtaKB|NUx7MmSF9%+BL
    zXvO(&FSXXFCZ=g6EjQ)kfbko~{lx*4jE54myNNXiKa}51lu!4H6ZFq%es|ekkC7L3
    z85rhIrYR$O(G9tX_6>>jj^*u#!ep)Zr4^3#r;lk@L`|IrPNWI?s%&NbN1q5+_(-Qf
    zk)PC++;;bhyQptNTgjZ<dn^t&cW9-7f>y2Qir@%Ll`z`>FrhO=xq0squO$0<G-#g_
    zTRNeYzayF-t`}vn0*j?uu9eT|0}#)4_xjp-450j*iNflHMjw?z?v(x=$VfSfXQ?<X
    zfXX)K7{nNQ5pB~;v$O!jk*$?s!FfHwr_Hz%P}FIN79Xp85+GP!ewOdbm{h7OQKZHB
    zKI?Uu40>IMyj0Qq_W3(%G-nTogOj8t3)@d%NpzV`XYnvBbymQ!v@Ft)_r^HBALdy{
    zh52NZ{Bk3X=jv}#90O1S*1iBM$nd@H=h6J}y@dvnW7K&G9dKBE&nr7C{=er#?h+Pc
    z9&YhD04)M<6;>c=wkRvjd&X=kjoP#JbcZhZN7u_ap)(SB8S8~>aL!=r`-?+?%(hE<
    zeDg(F^g-Z#%8tzrh6%R;rQPWNf=j^!id~^LW3sX}AzSv&Ux3bM@h+7|UUGWGJq=J<
    z#+Gmbtqxi6;7rs+oE*=I3z6|{8n#24=`Q`p9F1JNhiXhFu_EOR3y~mnA&!oqR_JcP
    zdS{xPPU^T;d!4eQ*WK?HcXG$9HJ!cp1Oo#3Z#k3tc0Zp<pWM0N6!;0#5P+JrV$;xB
    z3l{qk{m>-N2|aw{8M*epHy4&UHpBVs0S>Ii1}?`mvq$QC#Vq*F^`bmj5!6a-q4;Bk
    zvYvxD!xLDFLE`<I095DfWCH>xR&gt46I3iYo&g`_Ez7#J5>z>kE`{q$WiXzdJVHrq
    z1OW!0j*Vt<@!sv8bZ%t|1~86oC~n2-zfl#lCpV+@(d%@g@lVe?&33;yl;<Tw)PPc)
    zK#r&Pgt1iCQYR72Lx4nt?y%xwriX1hy@?-6&tCNdky?e5OHT=6SLUs|J$8MT_SaMA
    zFrSypyv|SS2~DaR&T=F_W1AsjU$c8{xgn6qJrzF&uuvM<I15o|`(sS4M9pVUNTXrO
    z;d~EF+K(hh3674M#*5L{tQXo8ZK$nR@7MO4Wj%Y34kw^STC>gL^{;d$IEyV>_j&DU
    zg>&%sX)PrjQ@MwwCdC_1P>~oy8If(4WCH$FW-DhBA!I%S#`|!uf7@@t=%;JyOu$Xe
    zHK}@^lV>~<t27iv7~2)_^h1%fKu?OCOZ1dQPCk$;sa4w1dvaK@xxT8&_t6Gk&gb4Y
    z&>&Qo*bU0-Ro0{CYSG7>3e#Pji2;53A9v`o;;E5Sy~e)#G3Vb%Bkh(3PrU~d{Qp2_
    zGEFeM;F<RVFhqAzJm$g2!ZdAMT;CN%-S$a-@C!NBXFO(y|Gawt^lTn^{QI&#m%X-6
    z1gG{>UCjQd?tjz1Pw761J3lnJE?Q#ZN@A>4Sn@QMGeu4HIITS8p1wm7X6E~BhjRZr
    znS3IfPh)rzCDn6%&u?0L_-qhTegZ?3FGRebX+PBQ)0{lsJEi$uIuslGkN;k=Z`z{K
    zH6H}!R)-{L?wRZv_qcQj0)G%#l5L74b^h#~;S8=-T78W`)eJAU;Tab#5aoj_Xr0EK
    zV^NO_OVW~Ch-YE0>tjC|Pgac=dc|pfDPrX~QA5(QGr5z%X&*UR%)+u|I9B?>@a_Hk
    zr668StL3QtAmSoxE%`qHztoEee}!J`!p8)?Fz1kP#auv(x>?lC5Q`owGdva_cXS8?
    zv(9%V4P>>ioBD%pqMHrBlF+?$`<d@xewmJ4ToboH9-@$lv1V5Kqk7en1645dnNQJs
    za&h7ODwSHwt4f8c(*HTuwVz96_G_)O5REjWz7)AxkM;_Nm<hgdz3SUIx+3Ld17<wf
    zB+{i~3DA=R<6g%-0c!K!wsIXWYWDx)+O0G{i9aWEa;|recg9|l<JBP{Yuq>SLreJE
    z%XUfw&V=<3uSK)2>%DE&b{RfvPI`GL@3l`gUP)Eiw@=AiEpMjsBhDmqT!`^G&CyQ-
    z=TX_;)9J@&sVhd!gC!jz<@KGvqvvO34mEvnwofp5h&zW|{=14juUDCD0h#a@d_;xk
    z5~W4AvNbAgaVAcxy@NAOHot=F%4WdQnP^)Sl(=zYJt>FPqPd{1CqU41vt`PD-!b(b
    zq}jG*HPO-Ha~by<MSjaV)IgsIs1}@48`(F%AIT$-eH6`pAiNLP+dP9p>x7ml>uGr~
    zj-7MES{(Y=pna$3uLMytpmz9W$8vj@-x$iwhF}gxf?G97Cu+A_VS5*Wn5$kps0<y*
    zY5*lC!|ZE!HM-t(LR%w^H6c}b$|_M2n;iG>hI@RGbBpo3>;pt;mc`<oXyY*f|Isy1
    z^fLp4OSk`o+$TmCr1Ogp$F{QM;QwpyYPTFmb)X=<@BhAc&zSoIB$b3fNjvV&?po)}
    zbZjtSK8lhMm_+^QJB<5pIh2~akLd)+MR8ao+AkPK!bbeFeM$Tw##PM`R&fZ+Mnd;>
    z;ylbsPUU6qX!8czRRq8yFwo!MoX?(?2NfCIwSWPYOL1^L>5gGm3EBV~o*&I7Kz=jW
    z*g5SSE~Ka6Q<GGBfCC1k?r>@BLhdg<CS3|0;K~3aEdsTwQ$g>Ws;Kg}fVh}t6a-hY
    zJ7yM1)AmQ6kkKV#4<?c7f?=P<OvDz#D$HnQVJqxUGM0;rsWkxzqH)cDf-^@c;^@|G
    z4SmS=)e-%Wz{Cv%#O#d1OrZ=Qv;e_{BQifXRaOd~oTh#jcvk2^)>*BNM*359Y50(V
    z6oG{|&;|zQpxqzQjAH6y-<iC|?(^Wm-R%S86nS{>2Fyg*!lA4I2Tp}!3UFjI)8Uq7
    z*3!WRl|y_7_bQ<FiuWl|%uE_QJK|z3qJ2~VV%)$Uh^6B61UL%A-Z)JkhKy+{d)@-6
    z(;9dR?qp&XD6nc~GKMl&cW;jWMlh-1PLD>`0<55pL5<cYfot_vWvv65NrR1P?C{2h
    z)5(jL4h1y>GJ8QMG=J^wBU4;H@vNwUl?bbu8OJbb4r3v=34bkEPi9j_H{9Bwq``{M
    z0wm^+5ryS4*<8HsV9d;|6&UdL7=b$4yd8xeW1=XH>tZ~?pS|Gi_f*CqNKNW-!UfZp
    z8%47;szLZGWF{7e#&w?-=2alJ52<?tx3Sr{NWe@u%m7DO-tT|$Wj&xQ1luU-{<)g_
    zlVE}~ANnj4QK#d8qQV%IIh4UV8Y49Eib#KsXLAw{S8$d0?*X%)$|!?FeeEb~v1*jJ
    zr#ORcQ@!|rd&~@Ezm2n@h`$ICwQe;ST}D@lwC?cldnEIA-1d~F>e{2gu3?zX8z~sq
    zq2{tlQkekv2^zrEh#9E?c4-eVVF6f}uEy;wbCG4Ak()fHXKF<BQ+MsdgSb#l)3ydV
    zom6gjACp$~jm?X!fFvXw!348lCaHL4GEZJ_1QU}m^Ifui9?x}^m+MTz>>4Hu1n7*$
    zJ0$rtnzH<B&Wue&IylD1ffLzG+9oW@qVF%67xV&G{lEj42$zQgX7D1z$YNyS*&D0n
    z3U4-`=fk0Fe4S4n*?MSqa4W>;|H`qB;-Am6J|7z(W|VHD7;#MZgTGQFV3-CFV5}LU
    zi*y3gFt1s_XiB<hD&DZ9?I)%Z2s?@$Cb)vb6VJW@p6K@zT5cjlD)EonOe(w6_bSkj
    z%`vlpb?Sm~iTd|&AhlsC-~-G0fO`lC^!b{Rc>qJ{dxQ80$AeX-k*DhJnvEU@Tx>w6
    z4CZW_+G+<d@GT+g>dXVKxdZ%>8Pzs*m3(0dxD+usa^goR#K#%-I9h7Udph{`n1L$f
    z&QUSaRu^0j;6qXw&HZIRm;sby0MjtI>cF&qF6jM9gh~KmtH>2^+2XxSjrZDcHMB<5
    zdfn(2!6qQ38G=i_*+pz&-+Q>$M>XZvGfVL8^i(-~_Tg@@t;WXLGfg|$dLNHMZG10B
    z#TsendA5*U;t3`v7?iEq_R~1s-`6E@O1_veV5gM9j1@IlhKXzWB+1m$U!(u2b&hYq
    zbn`Bv_B=1%-^AQ^egOU2b_RrtnFR&;jsI7(Xe{qjb<Qpgo2T0#r7fB>z51!~iIjQx
    zZlK26Li{;VWctW-)R%>63HY8Eqcglas4vqbj(StE@ogMEa=<VTS}+g9xTLCMH565y
    z91!M2k$LR%e?r~|Celd5x1`%ZfKEc~b{5gdI;{<x#&96OeL3<$FH>GMa~Y8{VqXK0
    zeBkb%rJ=qsL{=hm4izu;;*xOhqNu|c)ehyfH9k}tnF#(R;QPNX1_aEU&{#(mRj0ET
    z>I0N*1q3z^GkJ~y*eo!#2?R#%7XTl5T<ui+42J1D%)M=OPp@a!1H2ZnY-$(bJu%#S
    zwj#lY`%4y$zbnyX5&-We&ilZGpwsg)0EH?Yl{dZhQNo|;Bph}IA*(p82^bk>GoJHH
    z&+NR#A6>$~Jz#oc{4JfBu(RPcteg~$BXvpyRs%R@K(0tswhk6!!=DU+0#Ej17x~*d
    zL<%Z2uFP8$9wzG!5q66gW~ODZ=xV$JyI9tMU{I)^{uh7&ZuH~xO2h{RO3w9EIiK<T
    zQN|MgVNp!xxJsnwnP_|TIJWMbf+a8NmxeKY$*;jiKPP7C*Mn_37EvVuMiT9i#@|u_
    zQJQ))w}|+3d`-L)OnBGtP;Lu7I&knHDU9Sei?S;t|7M#<mA~RgB0Af!=MS(c%~O5U
    zDUo1)pQTvvi{o%U@@lOas!I$ktg{#ElcFv0kU`m(nHi+PMgeKQOI}_Yq_~q7HoCg%
    z$7b#ec(ET!9X1bkcr*5U-WA+vPQQ(wRtGDU$%M<*3v1s90S6hP`ztmV?avWw;Q(CG
    zgVM|U8bBd3pt=&x163QRGQO;;&H%c4_6gShPG-X1uRYyXnPv_LGa2;EUpf2!oI1_0
    ziu2ITU!Y@}GzZf6yJ%BkyUat?1N};NEN%+y>V~br$+tudtg}1)pf4s_HU_dAeFuv&
    zY6ZcPznxrN86qHI5c9SO+aW?`@=9l-z4TI#@{VH#grN`#h0ueftshG7I(;M>)-j5}
    zj&MY3Yw#}YvT_vU&w#-#sQ1P|J?6SFq(f+`wmZg=$r4xx7YC;qX6ci+Yo?MnB5Ppf
    zzV6Hf@D}!ObO{%x{|C?d;>~g12qrRncjp~u74ofNIgrI})I-l#0S)Obx>2uxHj}8I
    ze6MNSH%0c~Z7Tc5N^K_Q^P$aDT^Y~gQZSc9eT@dgaL#HB77DfeHNPV1B9Yb9@u|Ee
    zbC<nyGS-YbsE_5`!iFJ|Tf~+nZXE$rRuc{jMzSk-OjPYCre&pii-Q2aC&$FmYyFLF
    z4-81c2~y`aggAq_n0+-vyxPP4!gw?645dUwEn*~kZ|b=oL$cYxR@9WpRJu#SL}c~}
    zk>9=`1-b+5z39$rY+Gq&28yJV3bi@ElUnun5_!2US9BKl4WK}J0xld!>Ld4CDYo)3
    z1b#oS#$6$}Q7>wP8onp8DIWZ6Pa!=D08Ip=BJpo?gWj&}XgZvY1rf-ec7WqpYB2aC
    zXEj~rIn44b9)&ROOzFiExAWw@8L^A$a>G1cmp6imz)2%H@3iRmJ<6BPjdH>`5dceX
    zahY>@r||ev9@VinQZ?-{I>$7{42Ixu2bn(v>-bP<s5BhdX$;ZP2k<yi?{Cof^PvU#
    zkusPjbN6^HC`g}Wt6Aw8+)L_aUa+)2CAy)~`vVYBt5oqI9d!cITTxdqg92F|1oDH2
    z?aUE&n>{Z@y@LDdZLz)(!=TztApI+d{Y;Q1J2E;6>MG{rs5akG9I;V>HefzC!oME?
    zFw(a0KD7kC8t}#qzV|%(0GGd4kE%8ZSYl{SxMPt?8krZah9X;mmzYZmw~#)efqw>~
    z-nacyv`OOvX5tWghv0hL;*;g@Xuf&w=`@4qVn337Jiio76f+yP8!F(y@K2dh^iJ>b
    zHA#1krfOo$5T44rkU9IOj(SjM={{j7_+Y(Tl|sk?J`1meKW%N=Hg~tC^7=4QM;pW;
    zJg;OVS&OXXGT?MT$@(j@IQ;2e_#){>=q(_<5lnnWV{d_M$#>zX0{IFL3tgj%#00&8
    z)ls<Az!l88t~fKOSQhi3J_uNa>6543>=tbzq)|Ms6Z{kgKW9I*H8YC^8(+QBd@o@p
    z2K@rzfkCA8WG235S;<(bm3t8g6iDj<;3NQo`)sheWhXdZ&-NwT&2YrqWnhkMulw`h
    zSj&YA!NN1kQsOFKiotr$41G<Oj3G!MxL2e5$TVA4;ETY7J4VGSgwn$OzOq4M^~aUY
    z^ax11kpY*k7C1IBrSm*;@7M?yMh(WQ0&pim2euwd-`{*UhvUHD;Z+_yJW2D8oYr7}
    zYN#uh;M&H?YNzn%nbMdOu=V0f3j@&HE2MULQs*xDJnH}mSuHuVb629p7W(*3%uI^;
    z<-vqC>M0FjAj+h$e~Vp*eWrNQfQMo0(NfGxm8diPm+}NM3G2_+NvsK6!Av+a29n|i
    zb<l#%G+010QOv}wj7H+KcO{H>>C{}djTEX*y@{Ae5awqnm~~+>172WuXe&grw9K6t
    zKbJwI{wAXW?B619sq!N0oz9r$7hNUjE^%qoJM<C3p-*EhSQq+%nE)i@W^;2^NYi@u
    zMUJE|{o?ibYu>?wi36a4`jghwIIg-W7^_u*d2l2u+YMk$_)omfFM&S}_m}3suQC&(
    zqefBPgfwHw8^Ht*UnzFR9i=sJ&P7+d$@~qN2~?nk`G`Y~Kc8^^`nxIkM9gE3Yr*F+
    z4*wBcGc;YxW5I$3sFc!uTv<($<<Dtg#_sf({>TtZOSY|}xp^P{3N}UcCanvTDcC$a
    zpqe;M1|AX7!A_Bbv6$nnk4E$0@r9ay+9&Kp5-km*J!C#*L%$YyklJ@}k`X7p0#HyG
    zJTo;M5zqt40cyJM2mX0X6fV&I8?oCTbz1X~?R8A55-7zoYS4Ob1E&`i_bj`6Ms}dW
    z!XMQ7L(R^drHqs4Ei<vFu-Uu{&44y?nKS;03V1@ijRNmYhi;2g4S<c*F^`ubz#-2C
    z_7sk*dC$1w@5NwJ=5Tx_=zjhl9z!5f$&^uW1D>61hLISsiFOCC7z!<}-G!M;;u`un
    zW@7Smm|u%Q=pH5A+GNvnAd23vVSF!>+r57Vf4;C|crw<`_7>~6>drG6*Qhu1$`G@=
    z9xqaJVu1aQWNSi897S6e{|xGh)8J_4#QKK)0@8$M@$;iR@OYN@>Zvd9)E$zJmm0H!
    zG>Zd^&Q3-$Q{3qKm4*aH!oOcsYnMqb?k$s_G5EB69i!$}OTi5mPT3%8z}BqRM76D(
    zEMzDl;ukwWQM&u`zP6itrjV_jLzn`YVa>n)7r9G9Vn+l{P+ouDNVHK5OEahS6~tp{
    zWJ`_r`DYXkzCmS?K7)XJ0S`W<xDJT}sF3-dR5U)2EpXbRDz`!pwnMI1QP922c_l%8
    zBM5vg)F>|YUr}K}c3|BW&#Wa-b?e`YxD4dpalt1Uy#V$Je~`i=qc_Oc3Pe~|j3l&+
    zgx7xWfsgwqPVdJgo!hseCDBPgM4MR(4)T8O*vVie-p*&7%zSv6K}qj>Hex5U@H91>
    zmF@k{vXxc%J~)+^w4859Ixf<2lfL0Q)g1JBm<Ro=Z+hJ_0hO)$;UY~)XD)%2vn=r(
    zl#sKWL=m1>(|$$D%m$*xpk<st+)gfhT+8lasYnkRkX3UN*^^r-&PaHY9Z1CcO#azq
    zlNBH-|HVFSM3*uXcfA|v+j}bo)~8Po$BP|T&UZTPgOk=cJ9A~7IRnEi{D~4I;fWZ=
    zVFx<VY|EG#1SHkw`Y3Wgj+fO=bXAryf)viSZyJ8G{!jLw&tfM21LEEYOM8`OHd4*c
    zufjU-O?1MOa(1xSl$-3Chqvi`n)pH9@99mylsYjFeBRYn-(ORw{%+!#0=TldvYNd)
    zi#T4+4y7Y${u<c9-kJYtu*jL)JJp;ZL+ngqY}+%B9pI$)mF>M(=B`<uIgx&!{<UCm
    znwgxoHAO50@7axbD`pa@D3fb@5bqWF7Bl%)PVjunY|b*1B>bPG=4@AEh~Ohy9A&bi
    z^mvPKbZYa@5I?yEmy>Ev_IzR?%~H>7E}2e#HE+pz6#zis^yf`p-u58_d}ULi_`Y5%
    zSgoF!1iNgi=I(<_<|uN~rjZ+9uU2Dzjv0TM4}(Xxi)m@t#Yt*&`u<FyQ%~}PD?2F|
    zB;mOlbvB2elknR>(`a=5IaIf+LFygr{CGyaxpSwdv}|7Xum2rM7M|69W`O+{$Qzl-
    z7f}q~cIH@?r0&C~vt)cOkDZ^zdC8V;&oYPHz?w}>e<k6WoMmqN_><pB5>8}6;IJ0n
    zNgn=rKVKz&Aem&$Vec12l)46-wD7j+0F2l2&nqQ`!}pwpLD3Nex7_lAoVM>>Y!f)|
    zXY%1Tvv(BXO>!+QAfh({-?t=JL-umsfSYD;F8zxqgR7OWtDVYHxx$_OmF(Y5EMJ#5
    zf=Or~RY`YPr=2-lI=i;EGBC)Y?wkoW9gH8&471{8_RAEEPlA0W>11&d?a%h#^L`|!
    z<(*)XH0J57vf+AhWu@l!YqS4dUPjk1EuK|#E`9nP&3pA={x^Y1>U5QqBwae&8<>83
    zUe1>8a^`ie{?5u-H7u2+g7W&?hi6WGXUEdB$Bk@aoC;3mya(&Y$P)(GlQx52rt!a>
    z|J9Py<a6ntpG>aZ&XCIG%<SR{zFvOL0Q+wrjJNFNv`rubn^UPjQ-CD+w^ySsRukV*
    zg6}32RgH63{RrOmDdo83mjARM^Zc^+XDlhD_<{S|1Nrxf;Vrk^vXmAg2?@e97w@+Q
    z^l#^X%PqHnwASQb|5`!c;kno*Q_8@Zh6&Km59k%%-Ezw<A0XdygNOj;V%Y-TEw|kA
    z`|@p{sviWD8%S=s<sTs529wrWD|jOCbyCK*_FKof<(3bYZ-L1V0tw3vD7W15;Zi9X
    zs1b1dFpxmDx2AK;EuSjJe`NJ-UWg~BGhSP)t$acmZn@<aa_0s6ups^?IjpgKN;z)1
    e<v%S5CjSSOl0Cu}V8i+V0000<MNUMnLSTaAc_^#^
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.gif b/mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..fd0584ea0146fb3d590eb4c7cb3234810b0cb27a
    GIT binary patch
    literal 12571
    zcmWlf<wMgC6Tl~IFk%B01tmm9x(vFebfeNa=>|bM9NjRbd(vIfC7lB%-Q6W1DX{PJ
    zd+raoz8Ckola-ed6#DcXlmNN_{a^6#yANRan;Up#WhKS!ec<jU<@ya!`=AkeQ;om>
    zXBY40<_3_bP-OtrKrI#v09fqa-X0Y`rU$6<g9AV~94M?UD#fd)s2t!Mlb7+g*)uV>
    zP*Shk+xxC%x8E~>3Vc}v5EXw2B>GI^Z{1g-QUEHvQMC^chL3#x`Zb^pGro80g0KDR
    z4U_`5i;IhR4^N=h0<cL2jvV!X2U_z$9rET}6R($l8wgmEsoq!v5y{EP`=bCr{eBSG
    zF>=Abc<}--_!6Xk`vKo$4wyFpvXZxSc$79EpZ53m2463O#bU2+KGsPA)5Z9G{JQA&
    z4Ir}G3(RH%r^o5Q+k1Gt-iO<%s!+U~WENfl{|yj)bUSuoZf<_*4ZL-`^~Rsj;iX0|
    zZbg6=ERbxV0su470Dl1x(G2W1%>Z=xcVDK0@zV^q*SCV{z>L(b@fkIK`L=}`_!BM+
    z;7@LDLxpa~_JC2k(LErl{dNklq2s=xqN2j*pVjpMxtD-C{_k6rTl}Bd(9qD}+gE_L
    zm<xUx0Dvc*fa$FyK*t6Ajd_ia1<bL)yyXqBz6hLS0W4l;9>4;#pKbu!MgW@(h$ubA
    zbK$!i&D#Kc&S6g}J_^Wn1tw}1fOKD=0pQ?q1H3MPCQjhi=Ta8`mKvD8os&rhuv52k
    z|CJVCS&pg%!kK`2d@j@0?fAP}pdH<d?*cd-xUz47$U*_&<mBQOuhe=fd>iL`JJpHD
    zFHGVa>r#N&>NsF&3edX3<1<|Ge|Fgcv;Ugq?AF<57x;I}41_bAzXrG;;%mzQAZpG9
    z-%|se+*I+LR0B)EEMQ)Xubu(0=0)>>uCA`b58&;a+hoA}7hv7e((>-zJEN#uD<BVR
    z4uk^?DOz~E?h96cdUA3SxNgeL&Amz1#836$nKF?8H^4%rj-LfC@@|Fshi|Dl-vht!
    zvHQRfg99K6OaPId?^<ta7=T-izyIa-=@s}6F$e&0{(lnwKNBD*03w7&QpW_n4S0C(
    znRY00B;yX{)5+8V#M_VvdRxotZ)GNaF^oc=-@PS*gp;tch-x?N`lTll8#5rg`uvnB
    z1U=lB+t)^(+(87q>wH%&9YHF~!K9YLc!yr_%Qj6?N<Si?7$$BB>39(O+>hXx^Rc2U
    zC+er=Z~v&RTrM&)hx@w7?W0dp;b+3y*rfL+-<ft0zEu5%IsLJ$T2=aIqbf==#B}4$
    z$*~qAnQtKo+VF16(Ol(p;V6X(M4pnCgz3V-#BVJ>Gn4#uG4yhK-RHYlJ4<Tg)UY#l
    z<MrZ~^viAj{gH3dK5VS^5YvsM?YBvurHlF)KUAo89wk+hlm%WWpM4=5*OmC<0tVZn
    zrp+w!>1j~X?Ey}1e-Fh#?%gf?r}1D}d8j5z&XhMK$(f>LmGMi=Z__cCX|p<dwKSJt
    zDt8Q@bg$)ycvl9^ozjb0ucPD_OFA|lpY?pd@V!Ae;168hqkQREX#9h@`j!KD<ylq&
    z1vM^-DG2nZ4agq*=Tm)jMDS&VG9^sUhPm^8+epwQ0EH%6_>joi*{|fGeO;u-pGbFn
    zs{U=n5Tu^u&sI5(G#OQvHBfm#ipo)Y^mxNG#F2^7EL0;sSl7Cw((T#(z9WLdoNwWe
    zn3GJama~$b4>r*$b~g{xq(P+abWX$%WxmS~v28M%M3VqIW2Vx&t~%xfoF3zDKZIOZ
    zvoyIHQ#`eZjvnwdOQmpDHKVL{5^Jo&)$A>OAAc(V3{0a@{M)P+{w3Vo>?OQIZ|J`X
    zZp1E>(l~9-U>=;B6#V$8VwU0&c~8$4bnj^N&#yi?P-xN!5duwpQHg>_lI2=y(b||d
    zl?u_i>~Y0}TnTcQ{O(>ym{xnDV;Uba7wVKrlj!<!lJs#n#GR#>PlWF53^Hir6&UZ_
    z7rqwZZ8waTS9{CNjrD(>&L7zpN%-dZx38OVDcR9!R2co3$4%i|CR>_r^c1@cJ7fKR
    zMRX3$fAQ=;vov=1;%#nrIqT*nro<}RZ6DZCj>?SzW_DvgstVh>e6A+$I??>p-Rhyt
    zpgrkB8U@doH$HnoHp|ngwAgY9w^`MS#&R_-yd_Jod%I0%_jbuwV*M24M@;QPyg4p@
    zwo>TtrCmJ2C!-iRt|9QMUp7U_V?e%=Q&A$wy0sze&zmEaq0C;c#$nefdR35}$G3kS
    z+l&TL`I(0+Z@T<t?FhT6^t|0CNX`ZC7*hxm=g?OR%8)7l^n`hc8(;T7iSA|6sa|%f
    z(l*?Ln}LMeve^F!4xMU_#n%ZvV|ZE5a9$-)L6!mc_AY)nmhxHLHs08=#wPPx^t&Jx
    z)AN@S&l2%;7P0!F<WA@4d2`GPEK76^#i58I0Ft*vi61ZfZF=;cilfL<+m#6(MLwf~
    zod0}kIda$TZO8?}((m}-lZ49iPX)f&sQY<nAAS^Z+Gslof6qt|nZFddPl$w=5F_<O
    zfA^6)G6wWc5+rsSY(bvX=F7^f@ZqFeoguVKiS?Lpth0NIS5TU*r_Ek-AGuf?8hs>`
    z?JzMNbJamp|6%;OwT03e=}cd?DV<C-xiw{p>-*l@m(QqUD_4%3rhD(e9a*_3nVzok
    zNl8WbhRQ`QpDzBEDqHS&e+)Gs&m-x!>On#^v8MdlI}lISlGw}}Q&vfxMDxfh*bsDr
    zMDY{cbrQ~Cb)$H1c3R;T(^?QuT=pZ)EP$d1_v&kP#Rk`(ZIt)+%O}m_!{!7t{!f@I
    z(6>ki_N?`Yg0>F7DD~BQpMNVc;yU@FEAr&>!-i}L2vSk?jADX2<f@vTkJi3~3dytf
    z@FzK?vjv6-Et9CKfP!VuzpQ}Qp<(e!KVCE)vET^?e(CKVW6lu;VxA;&48|)VaD&AY
    zwYObT#&7QTs~Z!}V;JP^JN%zN*pXM(9d*Y&XVBfxVd=K=6I1VeT4JfIJfTDCT6O=f
    zNKnXQVD%%oup_99i9H~?kyQ7P07EbYVW6-kEj&M=SOl$Wl>dtivFFPQEn%hiz7sK;
    zAp?sxH767#1<QMm^I6rH^DCw(a{{g(%eWq}C>4L!((H>1By@xr%~`?QcH{Ep)gYh9
    zVk0r{o@G*?y2N9OC`1Gk3a>kJO<~sI0}5vp=cpb~%kNGL(QJbB32O-qqDxmQH!CmK
    zAIvu(HA(R4e)MI5uyMr_Ud6ezx;u~hWU#-XgX)f3%O1ljj+`ieERX-p=CfNkl&|AC
    zeGxL7{oa=muX{BG6w?+G<=4}<$7=~Hd_jclv=fEK4}ap^g9R%7;Y3aYiff>D((*R5
    zhNkae3SE%bI$o18qmuM;Do)=CmGlV4vSU4<Mhr;Ok4>vCamG~kM%TLfQtmnx*6Q3W
    z>MzZ`JOv-2S&P0%imvXd_yKpBUD@3@Y{Cm?J89QmI7kld&78@+=@ntHv-<RO=?TYY
    zlCu|1zVtuVWnMSraQIZ|=kG_d{nc!5HLD1a+z97-o<)T!<xPr>SP?4B>a<jGelOh|
    zzI(VjaVP4hsl~#A@Z44RdU~aR>F*g4&Oh*iBvv|*n69u|d$k1rsYu_z@1GepsZp9I
    zl$r=V>+-hJQ;h60+}#Z}y?>+0{F!-k7&GpG*}*LMU(r}ZFwE_;p$PsmmG;_NMCmEU
    zLHxLco-tT=t|6u&qtauHgA93*{gC*{xuwtBS(DC=UTaVldZF#TK9$Zd6*bu&fik#F
    z;`r>0?@4Wgfff5-R|;idnvf%<(us(yZY)FGt#Rdt{woJ}cz>HmQ7(tU>!i0&{<-s&
    z{P9`6@BFqZuP^8q*=GyvcdZ`p)`0QrJGScUG&u4Q>CV@8J~Q&P!z61jnfbrjMT@NL
    zKL##I3!;Y5*(P%f=dP@&Xs4xGQTS&|Njin!L`*2%&7%Ux=igITQe*FI9c^BHDdadc
    zpiF-#o$12;Z^6Ke_jdTslgrmRhK}lpr_<UNmy;W`pBV$vSBK77XGVtqP3n0aFpRi9
    zlgbE@QWvI3JS-icjdXL0Z7TPby-1{eI-OE?eRPo)Q1n1`0Yi4X_;Hz-tG+EXTh)*}
    zjwg_f0Dt;u>C@kIVt#<C%#bXY_Ig_V=<`&f^5^gv`d_J*EjLCK*H$5-cjr{37|xAg
    z^I8FiO}(V<A#OZcq`t(~a?55>X!{}@iq_TI8RK0z2@%_JMohVX1o?b(`(ljwl85n0
    zOtew6F#O;~tcLX0baUKb@oaUpZDBNOq5P~^VLNF4*|^U0q}Q`T_A3XY*QJ)%b??`M
    zqpw~V*d0L>WZJ4d#6Ak){bmal65>1{>xafc%yG`t+uqTMe$u@Z-@E;8nTZm#ph3%D
    z2xR?VY<pyCk=bsOaZLNGqkSvXJgpr)59&xB%2K3K!b^HXEU4kD6#*42@Yy^&37WuR
    z-atUh?K2|^4A2lmXuRbrAcnP&BZN1+kHi^6dN+yOU^xt%Pa%;Xq}La8;}}lo?(>8-
    zIA|GGx(%~jhAnCNvs`+tY56)#`%_#vT2lrnVu*~kh};BSPme-+`a(D;Lq%vp#S6av
    ztNW^qCRRTBTA~(37VM@93M<0|63K-v)}xAWQ1~=Th}thxJLrL6xUL{XuaDw)FNHoR
    zMxT*LA4xhb8*I8Az8OT6s^!lNh1k$|l2eE5Op%G7M;P_`+8_BA_lB%2qg5#30ZEY+
    zQ;`UoKuI|_7p<tz=qP%5dxd%kQ7AMn6smj~2&eIL#z8)^8faSjQD1sLx{NLm^!V!@
    zQvz&<uXo3cpopqjee1S^Ipxqhy)JFqA?>V@UG*UpSLnN8kpTsf92F#&TJatjum=u4
    zTJWs|2d3KzEyF~Wp}?leC_O}2R0TwtHF~`-?5=hYm1U3tW4H|Emy-&zK(+A0&~SZR
    z;%TUlF)F5%CgNs0R+`ae3q`sGLfdNl3bi@nK_NVr=(c*WQDPkVOdNbB?mb(47$RN=
    z^Hqm3@OQyCLpD@&Qq)|1lvx8LR_?p?OyJsf0!PDl4ND>(okZ%S#2&Pt03!BeJ5ew!
    z=3gk$jeC-tBOK^UiXut=u7hsTPEOh((&2SY@kn1qr&A@TuOi|v1yZz=<MjY}w~4-Q
    z^fTXzg%V74NXtADD%nzD^<kU!-whZO#Vx=RZi((fkQO0`KpO?{D8|<#*6&f0=_Pp(
    zGR+N{R0TrAXOm(`lGT?fK3k$&Et5NvexxoFouVOAg~{`eLZALi{*g?)>=8eKgXigF
    z5EP`SR-~B8CmbdxRIx>$=%k+MWY${|x#%Ri^{2ij!Q|7Vfjwi0Q!pcH;9x8N{wqxV
    zWzskKAF)<H5_EDOHKHf3ekec6>hDX(Btuw=;@OL`3_Vg*+elS0DZH~OhgTW4@~NlE
    z*-mT}qD2%Z=tRW7RLLU36G6BD8yK=1-eZ*}?U*E8ku*s1qZO2UA4u_qG=N)>xsP<y
    zQ_&DGB|I^uFzIpDBi^hZk5aV5;!PAX%)|4xud+&JQep+Ol?93Ug^6WR*}@8N(cSES
    z2q<DU_16wbegUCRKV*%Os9hKO4visED6mv3=x;~@g%R2&5!QfmTM<7J)X+p$xrzvI
    zM-pVbAtXfslsu50K3h1WQ-~}AhvDD~jCqd>VfkTsR=Zh+?AgUd2_h8<>{iidkMe7*
    z!7_{#ZsCbNm}1~6(|fnrH{1uMP|_1#5>%81-z}*@k^F8f5L%`fw<dY`xNy3tbiulC
    z`EgZ}0%$em&N@3BnF8G|0&}LuD<krhF)+@QJo>PrG5`dB`446{3zfx?ip@bcX^Mry
    zQ)O@vF?NbN1!zwNS<g|bfFoh^07dI=aYs>wUl>tuQO<xTT%Qag2trell?pjlj_H=R
    zU@E^2RMPAc&FZEz(pEXzRIzZ>AwA1Do5~I_S+6O78nTfbJucq~0}Dogg^PdcIhN}=
    zf)QlZ@4Uc53UC=jF`l%-Exed;t{TEo^MR~njj{yA4%QYa=+`X>nXNI>gWHCZm^2m8
    z*pQSpRVpF~6Xe0O3QaC#b@d!|FH-Ngq(a;x>Rw~A4%vTN{;MlH%JM~l`xO(GDvCrL
    z>y-=YD<1nr>t(LvAm8`O0o?|Lhz8X`ie{jQ5L!&ALDp!@*r<D5k+)Y<qXswjs>CBn
    zjw5Qng~K!yz%#S8<6%voi<_JVn_Ru>Jc{dH4wAm&AURIS>)j*m7pY&uHCuR;U(Eij
    z+^vtEBb>A$LW?wjB8nIGTAFuBi1+f-Ilw6P#(v>O{lOZey^@0BMw5ujV@1egT9PU2
    zUsVymYRU4zZE&%hPI23+RrH<tqhDLQO`IZa2NiXlgQUI@Z6w$>a?bid#rhDkmT<jx
    zXB?rPZHs1o``loQ@?MJ?N6qSBLk3wRCbc6+uW`$<QIN8hm;6_m4GF(3S}?72W}tRH
    z1tNaaE4JS$snjJcTINy=mJ@Aj2j;qnZ@_$%<-UyFf)PI{ZrZfcx@FbcsplayN^mq;
    z!^u%O!+eVXt~x`l=NV^fx(J-vwxgFFY}ec&c+|rkN#d;3TW-@iO<7o_NLYP6sDl9S
    z^J2U9JH?NF&8zh_D}v>k%iI;)W*L9Z9)Z=;@_diL-{#u_$>4gJpCXRnu+;ts^AK5F
    z|8#NttYSlgSA%NAKw$}CI!8%qBoQXHHCJR%5IwSi929aKJlSjHrz1H}?G^qHaOq>A
    z>DYl1+?E2^&5PVagz(-$+4rNa=o|1LYDg6X7ZB+mx^5e}9!j!pr<w21plgufg~i#B
    zhdVZ4al;vuW5VcxVt^A|qBJ6CP{6a?bG6@b?LElM`^&Xt(1Ww-6$slAiR&uCISu04
    zZ<_A5bai^QL2vrd*tS`6c)|@#<fwU-yf}HD@P$tcEByed7^<KM&F8I9^%{7oJa(kt
    z!JIZ$QPO%DNw`7TTO`tpM-#fx;aX6zY22XH@*vScuYwp>$*zj@0H=Te@6N)nDSs=`
    z4L#r@iAnoC=s4s{*}r@=^JHj3pKEj#+i!BvpUFuo!$|5DKAAXI&2upMx^&<LcEH(S
    zO5~QTs$@i>wDA*fZ6T7xm!3qDYg&<ax>9et4GR}<n;%}7mrA>XY=a>w7h3nG6&%SG
    zD4Xwz5&i~BV2K8Ve3+T($cfC-30}2+10QfsOZNun<n(p_7<$w~8ES%=tbo<1ag43b
    z!T5aUq}8T=ITqv~20a$$ic(3bY3IA>N#NYm0a36ZyJ@0@`G>>vID;-E?}9WIhAjcV
    z!ho+U%6i)t7VTy<(r0K6hzHx+GNZt}=tahs#T=h@F$7$|ca*zg_M~n0Laf5(w)#cI
    zQsF%8v)vr1bxayFw}mDkTAcfpPFRSWVg}&|xNrf=gt}$(QjCN;%Y;aV&4*<;+ToRX
    zRNqta)dSG)7vjHJqi5DyR;TA@GGT<<VrzR`!^SOJ1$JPOveA-KD3=%!;UIYr@45p(
    zKPGdyw16BdB_FIGs>#8Pxf{T*=SPH&=J1sB%*)FZ3@f^=8?wqx#)i9QDx0*2xToB!
    zHmzL?x0|bCizy2;z94uF9R8Af&ER(E*#hC8^tFuUHEz(hrPy{)#iV5PwnHi5!t&DE
    z5kW2ea>We+k0ZP>iZIr2M=fLL!y$#n;!Y;_^pC^A;8Jid_hx<u)(k(*C_WSGLvH80
    zx@<t0)Iwf0yjtzM*ig3ERJLUtwN`LIAi_<`UJA`Zk_Wa<<~tHr8W4mZk_;{Gr(Y8|
    zD<3Gfu5a<~u%QUqkUO<>L`g?;q{BNI_?<WCjaLXlS)cJOhRq$7g+lo1GXQ`1!f<to
    zesQ&QCbeazE_&}Jd^Fn!%&s>4i{a?2;ZYU_dLc$+W4HYRvmefIeBWV?2SxO-d@eTQ
    zU;|B1OLy`#bEmLvXE}Ofm0{Pw_Z;a9{Zw`?j#%Iw0qrhsJmV&`G5lN3O|lHzs%rgv
    zWw<3Fu_lI?^@8tnBj96>ms#jbP9IVd2XdYv@{Hvp2d?8`mE#1X10K|w^6+s2o~ZVQ
    zD4F|22uYAGe!iNqq1$>QwM=Ne^v_CSfp!>FF#>y~cKsaw=ZO1v?a_i2=3?;Z`d7>!
    zvCKVQ41s$qq2I5qAC&vSF_&RH+a{E^`HrO6qfyl-R}@c<d2p~qK=oh(PX@^#pj|pn
    z|8w?<lIW8IK_TMbH{6a53ihX*>_^5)4G&@2SAcdHRz-0^3tz4IN)D?0GhO)zY+*T;
    zAfpG9=cD5e*tST1BC*WwAMovNPo0B^`u_Cq=cW|j*WZ0}{^m5R@IH%*?eXiL>B5ga
    zNeidy9Un8(7ra}y&yDBFp7Iz{c^vQ2v3(Hyki$xR2FojSa0(=eWsfH*V}2&B8vsk?
    zwta(5iT`$vd-}v?rpye)%uUnaG{!!|;>h^+F7$5Rx46xxU7<{@{vrX#i#-mV!oh*_
    zlF}4T%<om4UzX-7CiV2-n)^$n!e~XsuJ&D@S*m4A#p3^UzX<|97H;GPdj#->OqPAi
    z@qvj<?n572h4ne6{ufh&Q*wU%H@83JcgJgHzEe$qf^`J4EjHB)X0JIs5o1W^oN*e!
    zrs%asUOlR|Ocypuj}8*H{5@G=66?txB#>1^dcUM<)|#WNjlia)>I++$U`?EJM8hVh
    z5(iI9bYU5f-aTD8-b3hX+h-DocVu7EAXv*qV?@74=ES~xAC`Nun!OW;lNk%kzgGVU
    zE0~P*k7A<(rP;L!By=6HgPUG`f)VxmAC=t2WaGFR(`>jUch0N2I|B(~*Y6%OJl&5B
    z8!muKQ}zVBg;1NMDM4aCkea=?F!bM{y84pi@FwHmHuJA>K$~{-7ZsXHt?fmjFmF7s
    zlG;(-Qpczn>@gz~e2|l3X0(GVJK~V0MfiqNq+LqtzH;caXr(eu*T<*B)EWh2uO7d)
    zF;h)49=NN<KzzpZ>&9QIT5g7b3RgSt6!E6LEJvU4BUC8fFW5&M!pBY4vcS^FBP80B
    zpx#e~oiJlK4hZC%16x#nLOPOb#W-nvlA%7)m;Yw9e67?<A9AoWEqU!_H<Q2U7$<M}
    zGpm|Iv64jbqis#RpP%HLP4~5^WM_MmM>2hiV5cGJM+=X!-Z>Ly^JcL_Ui13j7Ec_z
    zg_cAl@8(Wv3dXK!`8hnU4HS>&ep7SbP&=g$ps_oB&kR(KvOlm=Ld&G^RhPNK*o9i0
    zz%e4sb9$Tb{)lxSrH4O+onKVGh|<t=eY9z29PKtKKu^oO<$XZ@Le<wxLn;olMw^lh
    z3w}{eVdD43+6tgDcb6ahoZYxvTyFS$pv>it57<Cv*xhzaB6`pSCQB1)r)Tc2clc$;
    z_iDq{B*Wt+<@w><pEfHh@5h-&-=8yctkVW=cVF|)9%~y5J^hDal30;2{!lM&9a7l(
    zj>D>Cxbd$w8<Ef0-3~mz4;hDPwCSXdgNRa`S@}~~<Bn@$V3MlTF~j28>B1iZfo4tC
    zRmbB;U<?&Y!KT<BaBsNbha%bV`@9>TT?J_y84rSi!`#*Tnw^ig8aHcjzn8tAt7ZP1
    z?nU|3m)r5AW$y3GioE&O7VIVxfHkibp(GD*s3(<fMJ47_LLDBz<uE)(yM@Cz9X({7
    z8BVk6s&_ST{;CZbM49p7a6`WjOM|+X;>5xRX^v0Wl$J;h`kvDC;Cx?qJ;9!aG|`_|
    zg!t^~QGV6xdIqNm>!Fn-5NPYV$64<2KF#>l_kH_)$^a_ru4US19zpY8{*ZpUWTffr
    ztFiu88Ob7*-IMTqKr-zQ&2#r3(DnSVH(kp1F({Kk`=m~~&Ec<2HslO`tZm}8Sz2F3
    z4H;FR%5`(u6RJt&P!;#Z$=rJ$@vWNfg;1X~g+IU(`TR6xO2xfZtqjM-CG~<`qfkMh
    z<HYys=gOngt&rE#qtT3NcNorS>YoL7SU{AFm582V-`ck&VFOnG(iQaW-p%$$cQL-n
    zwd%JvcZyA8T48C}p$oSz;o7qqyprPe=dDq`UyB2C=sZ%qCz_SRU%eu>xjno9TZ`^o
    zHiN#yg{$?oCpxw5(3Ciea*)S{Dc!KVqAyUA5gsd$$z7+-Itu1c`~Ks}O&4?Bc|)lG
    zsO^IXW_sceRcOB7&w1{%@qF{shuAFj^lDoRdcECFHS*`+AIl~@4k=x02hNn5$R5U<
    zoe^=m*O<yS#535xK6BxZ-xUnLe|+>fR2}^8G4LMtgjn3o59d?Gy_hvPLxX$xBuzP+
    z+Hww}_VB~ET27eB0~?(o;oj;+r&3!N4t)#ZF{4#=xM=AfZ$!gXD+7#hm~5X4=Bb{|
    z{h(6G%=%+0=|stA#N+Srwk&KMzp<K$+zfyHGzEGzaGvqiPGbagy`H0As%q0MKz=qp
    z(6Q;(ng`YC90p$2K~0-3<-MYLjM3+!<OrVnPBC^?c++?;dt#*)vcx2NcYaHAkH9AB
    z5X#N_n@dBmiP}uj_5IAkXVQRbB)v_jMw4#!u<b)peFx|~`^V(gi@eeb0+r7q*tblo
    z<;_Fo4tHo<Rvy;YcQGE(mVBG%1sgYHh620(HAD+a?_E3YZOR03r7e`0yLCJRm0AWJ
    zPoVRjHvbhY)m9XyxHopfZ~$%(bIV^nuT84uud$1CYa9dbp7r48WuBbOhu{fZdx^SH
    zud`RMtdx>%U`=lS)W^^Q_GCg_Pz84y=gM6<kAc~iiolpVx_Y7K*}B_R50Q^nDd!v+
    zECkOAheXMM^yyr7^Uo1JkBo%=#P#qkwxwSc8Qq@O{bGTZyeG7?WPHv~swL=}br-wM
    zBysWkyN_e_3m<X=ZBIq}h6+9Y6f+I`tEQ|;aMIR$>sPVM<Bt(GToymfDcd}|?d>6H
    zYu^7M)w-Vgo}U@lI2&=|$)!&EH)sID0FmOjCe0KS{Ql&?)=D2mNR6t`k;ahSMLivO
    zn6DUDIRrm&@*31T3}i;o8*+tu&9ey95y@fv%+!|29}E6{Wd8K8O9*D+z`QbIO%dif
    za<%BX*v9YA{l)Na!xA3;1w#xAe|KNMPzw~~NBGi(iQ{Gl{#7vW>EXJyiRgfl8(Htn
    z%0qjbnTNU{w*D4xr`njg`2ydoNBZK<n&sD<l?g5!_MhhF&eMi37K06gTBAg%=?PoK
    zMPJ6*TdL8dEXT;!`Xw5isfu2&BrXYeRbr#coflW<k+R#J44#}3%WJhqyt6fwhWv5f
    z3}qHg0~&_Jx{YZBdp#{f$9Q0oy{qYg2D*~2VCApZmhQIX(gi;+=B88N*Hu6?5o^xU
    z7b)5HzNt*%pF(O^#S-%?dWmngx+U<qm=>;$-dC1EcTRrBc%PL!h|H}-+ry>}4;wP2
    zp(o`)@}lu%8@2e#Um>sJ%C_gCS#zMbYORLO;<*vxy(Yqaiu^Wxt^38ogI?8t21P?@
    zf5AB=hS`5{Ue|Luhn$lMOa-*W#<qB1K=&|Wvp>b=iyLm{B!DrIIfB;o$F0Avg*xU?
    z?-@l6$9U8z1BoJopU1WCL?HZs26I?;kmfY1_5}|N2Fn(Az~&ptw-LvL?YOlPGD;$@
    zG4LmxzlwItYfhnQJz^kk4CO2-K-2tdE*o^4-JYBUGCGk+BM{G^6}P4VOPqGT<wd*|
    zZL^~Z@>%2C5_!j@-qHIr*d)TAIS0W?9(Yjv4&E0`*4L#pkFdwRQw2-g_f_DYqsZ@b
    z@#;$`^LC>3#Q5b1`Dp~S(?a;tLikGpJPHV@MFWH}AeywGx$O3vRLPc3Ng)tIe7{pd
    zz2_|r#GFZ#@jvX{g?iS7vMlJ?j%eNSYL<(XVj%~Urgb5bIzE?laIAM>$2z`%yUFW$
    zT^T{qgJu7j)7uVI!N|TD_n%B9*2(GqJce>{Zx9H4OUl@1RMH@n7Nl7(X$)=?Q<6dG
    z_n5!wB8-#)-m-Q)tCnHH_ONQdll}6}2Gr|-6?f|DQXcPuVNqX{<W;dKHzpLBv%ET0
    zuu78rYbnV~js`yckaSx~*pWP}AOJHj?awqI)EuN!A4Dmq0L(SD6ca3y5m=)K3eyH8
    zY<r67g5DR%MKt%>V0)OD1l8QTD#7xO<o$(%!PV-6)uR2jXxUG4{qd252~4O&Y*`}{
    z>TGOKL#lJqgit%MJAJ%Aqe~h?K48eHkRzp#tFB<&3|XF&G|5r0RtuKM2`)D6DH&HP
    zOOtu4-L-u`kO@qr1|st3P_E(}bf^xt#UPy26_u%iZMVU^;Le6><>tWt#`!_d>i$-2
    zFVJR6*sf2U$*DA3{EM1X!C!qqq&Y~W1T5sPWKu$q5i!u;jNKc@4vi}epDK-DWuWNR
    zQPCke&cPEZc`?dfTe*=Ma>=@8)J*kI16H}|)bA|2-;+~v>1NP}YPkKTUl=s7#yOCa
    zgUU4>&I`ouq^azsVc*wd_o)I8)(0$)l%9bhM@&Pkl&Us@Lnk?Hicm16*-(x8pvp2z
    zrB5m)aFCobB+mBz!v{@`<it;w>lfA)-THd})9(URS-FwVBp+auR?4y+*3(u1ioyAT
    zO4izHMI}mRZ7Re=!v}G=!)morEDi=CqTo_JB?pV4-`!JIq>55*vHktp>^(q}`<?(A
    z;+FIKx<uBESCVdm!0mF7{^0!+dKE?p&Tze3n01tZOD*rqz?QcHbNaw;;D-S^uo(?R
    zmL4LGQ-b0Ypvwvo)oMpzRV3~M`sNP08LS5B=P^*`ogl1<M7@kxUZe_n?J|)-9^%OQ
    zOB)uFvaU?$LM$@zUi3gUbAF6bObPK|{2AYe=XOC`C4rJHkV1W&bkGM2pYf8o@sb?%
    zh-&q+W}NNDxZT1i*O|H+?7hv=hmXpG>KYoa(uXuPrd)C-bfPpC_rXG?AN9=$MK&hV
    zy&)Ml8lB{$dX$>R^y<$`{Y~RlAn^*)xk>|(lY?oK?+Y|TTGWxyAwYRU9d@7zuL)Kg
    znvR4`+13Z%6`S}t6s#`&-rZm-)@}-yJ)sf`N{E`$FCFu2`N;1MNihiV?$*idA{K{e
    ze&w3X^uhLe4@j2!7myQV`)CfR6Uk~2i6gZ`;?)&u#tR>Sp*VFU3S<unegX<soE}wc
    z39PuCt~i@Ub4`6zo~X8)i8au2`wx)Y%p?p=G{ny!FBK&(71M^ayxk$NZ9Q09h%j^>
    zQvyan)G?(5Kj>%k$^EnVbaORkWnIQ!g{oV*Xou9y7M0Ex59!)(=-OP4t3u`?VH#q6
    z)3&^N)&IeL>EBSKp394FS0CAgC=}F@crI5@q+2f~cLvZpT1Z=%S%xWiv(9(41TpcA
    zGAV0zbIoSu24}+v%t8(O40Oel=H=pb#i!^0>&ud%+2IZKvfFtaOwZ=hzv9-PM_N^k
    zcMhq&FdH@ZfRgwscj>BB(G^O}V)@Z6a&9htPQOO4$&9eMbY|&n!KIBD+!NAffNbYl
    zR8U)ls!Vpf%xC9fw+&{=?RBk}2?sU;D5BNhE)y1}>lWV{jzEHqK?Wx;3{Lr$V$6)j
    zqfj4RmS*W^W=ogoCXHAe`{{#?<{?P_3ccnR%Zq%-)(yRO*u0G+ay4jK7=?r~On2Mq
    zZwD<}Of2u-BL9Oq1@xcp6Pc83j2lCjN^kvu*TdSsAI$xo2v9^Wq0?7vp}|jjRvuKW
    zUA3&;*nNEDLcmgB06>-z%&T_{i6wcFOVX<lO;cv`RpMx4whUxX&Fc1l7jf=@JiA$n
    z$@RP3!SA<CdHGkU9-19XuRW;;e|)G{X=gI#vnJqbB!)2&N!ZXiG+`(k)D1RrgAy4`
    znmy$YM7l$e%%+m!rqcZ8-pF-@`e51M^&Y!L`GiHz;br)usr-R4w0@I!Soak-(FgIh
    z*D4!)t|kKZW`e;R+8G-<J%hSEgR|*t2Ei6?mm7B&kdmzHlD<Jw{F^q2O$Ek9Gv9Ta
    zB;%ga<?N_U&Q??Fvdvd*o54h8uYy6qtIb(^yt$*yhPwHhhO60m6zC#-O<2W3#BfWz
    zXV7hO1Buu%;tz~B+|lASHD)$v>)Vo-Gxe>tGCQ|Od$`SBAI!MDW5XJdog0`9+kI8P
    zp3QHat+`FXO_X=OZft2;z#UwexBCQR8G-lvJF&~NyvxJ7C6KX#L~Xdit#md`zT1yQ
    zA6mwl?;#nD6I`v9(!mW0HcScr(lX|zt-)pww_DG5ex2|9fcyX8-({5B?bO`0w%^T*
    z-tI}TE-Kp%uiY+f4J^Z3i&5HC9&T4M_&?aT9U-!FaU^g-ek7o@b8+9Q)wHU!-<izY
    z)57db8!k81?zEHzv}kU&Zdw7E+yNPRfi@U$M_%B6q|%J_a7AS|cXGXZ)3i5u-#){-
    zAMamsxVw99UCwPW)U#hfbTEwHf2v0O+kVUu>cC=YQ>S8;py`m9v3DMPpoQMK%s6OX
    z-21m_b&Wr0Cpv8B-(O?4Ut>N5sqQNq?r-rsZkKKEXgcnd9S)i=VSDzGGz5Rn?ML7a
    zE|>{235Qcnt26$<2K>>5y~9lF-o2^4f3<&F@O!uLLq?>3JHz2}^yk%0LWP3Q;@T*R
    z;m_j8Lqb`{VndWePe6$wYGBh^blci=*;)({9M!WAoeChybR0=IA*XQA)v$wK9K~k@
    zQf3C;?G54SC1%4|0c@<tHfRE>@;^&Nhx~xuqmaLXF~^U20wAn}bQabOTgL=n4;d*=
    zo@k?ZpiXbMcAv>Qm1#POUHUUy7`+HN!X})^Wgd~o+#!#d_=`6))wWXJa^em-xJ&(4
    zKGfkg%h?-MXTCo>Z(^L66aH%EgGG9eMV5}~Mve;?{~Bwf?1x<tTQ2V|Ttw@^OcGy2
    z)xI!0d@-T6kqmW`FF(;PJ*8=LijyI#%WzehI^_nAYyl@V3g`O*e_sl?@O^a_uyE0e
    z`7117Es}rs%5qnK%DHIRMSsghg5?w;aEerQk}~>IZtqlS?_`$fXr6gQ!|1O2;*zG|
    z9El)Owzv$=1Ouki=b?%2?}3vKb?&bLyU0ZU<;6=)3xCb6pjS{2!_YGbuZQT!SzPZ~
    zyucOX76egufuKI;N%DA6cNJuG%B<>cN<)B1a?Av-0;bjiv@Wd$0)s6sxkE0)EIeQR
    za{n;muJPxxFh4Ll(b*7s_>I~_9C{Ud;i=#IIic4@BF5vb><J|4s(Ol$sm{a1$i42u
    z0~2%oRPd5s{L<R!vf1r2Xz4uH!ZV!3<^9)xMI-JVe=cM417ozjA5nNHw0V{op|r}K
    zUGo3)@HnCO221h9P|iz~($y5{C0g&1?r<U>deV@2edFR!gGSL5n6=1yhHiPy0A67%
    z&h6AUz=v|rDAk)N3j#Lnn=WeKF2KFd?M5rcyIjk6XvA9-?NKf3l|p@<!s7ay<-Bgn
    z3xUIDEcqg~*BmW<NTxqcvwWS+zYQ}A<gxG%&kS60^L*_FMD<?ELl2jp;IEBtdL{6X
    z<siJO?|0%*2m$du&d@Dl20xNx@AgjIkIrC<Z!2%jk5{D;kNMy;+w0kZ=qKq&PLI#`
    zUd6>~=IQTz?^j^sNbbvF>&}Zy6|#EuLu!mgjae+@-+q6@z49D6YR-+CHmmjmQfk2*
    z>j%#!OQ`Bnq-u|gd(ih}!Vf%0SD8fFsT)j1`ni;nVv&n!Irl9_n#3JQxn?hTc$j5l
    z0U~DXDcjou@O{G|IlgC7VHA~4-O%%5?W(7Wno<#(2Xi%c7Q-);FHVP>1TOA95I+5*
    z)=>o$DdnOp8Ve$$wK!NvwXrBq><m}mlCtey*P~wfG}nBkR~wNho5(QhGQTfc>HOy~
    z{d)hX#%66rL%i_M=`Q^Tr9;Ph8I~T8pf_bIS6LelKeqDiZ@*ebbn$7q3UGB$8!fR-
    zr@PCGz0mWxn<DU3YO{3fzmFFUFcd`R+4n>EsRF_%^dfiS$t$dUZ{`<#18ces_#W$c
    zBwtTG#?6mJlO|A1fo3JB<>eL*eOPnxDTJQj!!w?~qYtZLY<UF<;n&I+tMOc~?qmZf
    zz2WIxHKZ#WW3pa~&SsK7Nx^dlrMRs;564=ajPQ{OkNk8Gc!Pzryl0YWtZFb#LHI3S
    zqFKVk3r{nbPaT1IiD(5+=3J}q{ff!<_A{H$HkI89(%r+0SPEU3J*-1iNQ+dER1f~K
    zWmOJ@Zzoh{2pN>des8cTYv~E!iEkqyvrY(kNLHK?bWe{JRNw?f{kI-qRLE0?J-KQl
    zts~587tE83OO`!3iYtF6L|FdZDWWB8O0A;ptUor~DQ-TS_2OyW-M!{c{)7Kp-U{Fe
    zQEd=f?}~QNk;p<v#G4!5Caq_pFS{S;dB5zTCPZ<!fi1ipe^Pc1I(9L|>^oleM|d~C
    z8sz%gT-hE$*<9JnImUSsFPO?%IVg6${|ZYy*j$O_SCxG;z_OS2Wq2Qd^Je@ZF}?Fo
    zcIxz(*jECjUj`JfY5Ar<QIu5SGyt!faZ{U?*K<mBw>8tBuSH$jCG`w`_PBX1@GY?n
    z-U{GMKPtajq`BWJFctQfTyRaB%=a8?S?=w;q^Gy&x|U2keB2hK8vSN9k)>2<=K(SK
    z(HKSx@4A%RblAB4BlSl3AbIeR|FC8ce{rDr2k$lAQjQlq>TaUDK6*y`>*lzG<52YP
    s@2`<Jf9pHB#Ln-N8Tp(qJa2B<UX0-3y-bSX5kK1*@%0_Ka|a~yKgEvh$N&HU
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.png b/mapbox-gl/closure-library/closure/goog/images/hsva-sprite-sm.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..04d01f5a1a96b678690c7b2554612d388e963f7a
    GIT binary patch
    literal 19921
    zcmcG#bx<8&vo^YMch}&sfejmXw~f0K+}$k+!QI{6-JJw?4=%wWxCIYz^Lx)Z?>XnI
    zTlfC;t*V(d)74LR&(o`BYV}$Zp`s*%hD?kM007WrA(Co;`^3LTDkA*fGuO#n`ri)L
    zO<Yz3@o)Q#XdeFe9?2P^>jnUzVE=PK&2Res`70!Km(p=pcd~T%GI6y4h?zT?T2M&a
    zxLSBQx!So=NT_q9FG<7v)x-NoPr}v0#NEcpkwU}9!2-a;%ErSA=5;L&dH?GS>mLOj
    zMFoB{CkGajf9$Y$J2(RbMZBF&%<L@ODNHS_Y#fECC@3hXC~VAysI<8h!HUij7S=Wp
    zUsnrtUnLDQUpq5Cb1D&GWI=EKzhDOocM}S42YW|1es3YFf7#^!d;O1?m5SnDD(-ed
    zRR0mQj-m>Mgp;cU1t$v|$PCQENx{j@!p_6a$HUD`p>E<%A!p)9!Ola$4hH`{vGcRB
    z@pH2M4d!2sN*MWXFX(D+$*(3U{hvPn-U(4zySqE{v$A@5d9iqLusFF|v9j^;@v(y0
    zS=re^e>FgEK924t-XKRe>i;nEUv?xd+{|2Uoc~4FKXy$_ojlxysHpyN^zZhc4`<^1
    zZ%2-9|4H6o8d<$foLSjez^wna{1+(rSIGabL^!!>I62u1|G(r+@E_#=Qu=SN|Bbhh
    zG;y~O{>uk12+RXw<I!N_<>zGQXJ^;@ANc=J`aj@`PUbe2KK~2O&h`(Si-(_+>p$WD
    zL+L-^f~@}#@jr<97wi8#)Bho-`2Qiy-0a_cIeWO;|0^x#W~>(W77iAU?rwiMWBcEn
    znVa!jI=MQSxC`4jm{?h`Iy+hkvi`U5|1-Y-i2Coa_&Y3F|2;1MEA)S-^8d=kfAju-
    z#r%I5{lDp?U6L>#0H6TKN{VTCXP<vDNurT)+=OZLP4$w|Ll3K||9<@b4m_@sK-Zs7
    zka6{5+`;h15WSncm~_cP&M}Q@$iApNsjODmhq>~XT>YlIf&lZ)Yx^bUk(J~+sP8+1
    zv%dG+ixue>Xd40M4VU-3NWf*lT@Tb2-~HG1dJ*OojDSaV3P0yF5kBGPVb<&DuiPVb
    zSdT#e4_J>pMvqv%%20oZN!~}$zMsY9K|eN;_;HSWr<{3jhk5_RCw$R6v-22EpM5W6
    zyftu!K;Z~@qeuM!_=DB^Ao_>QSfqD1&R@u6@6yCu{HlHD`{CtY9HL;K-y8W6E6L+E
    z?AsZypI*HP%!|zSp97-PcXHmZNZT_|e~5HMe95*%oJikiFu%i2e}H*hMtcWdZOROI
    zweJK$y?=4~3?9gvyb|*N9u*@ZwXJOVXdL>Ewe7MU2Jj2V5s~!!>N@bw&*NzM=x;^(
    z2p9eV>JL-DpLn&=&XEZ96;|kXiYu%1S0<{r==$$~Ypl?((%agEe(+~9uy1hNE(7${
    zuT48B`jXpuZ^wBjFCu>iL>7)Zwx&?)pWz2~@kp-(>(KzH;2@&70e~9-onzv5rwGaK
    z0mEy=1Fz1`cmI|7#;676wc0ZE9lCz%liT+<-~8#0ji@8<axVC$KPZL-vvHn3i((Nj
    zb6)(>Rxa+l9|;6`PB`q1^7@$Tf~I7qJ_Q#%e_J~zrMH6FIPtm2tMfbc^r{o2Z_nHO
    zBBGM^)$)F5@7Zf#F~{rxam)W<%9sg(BO8O%pKo_w6lpXgFaT~_5-rVv!rL#0M3k4A
    zl$;(P-`_t(qR|1dok$QhN;|Zq9zn~~8W8&D)DjU1%O4z+b>MPE7~<Mj-}mC3T<`5S
    zehbnkp+&`|LrDGPnFTlbiM->!FO-6B<mb*x{pgnYZIejfRxkp+c<&~b7}SqJwEF;r
    zR-#EXt|b#h>kVEgMIoD4ec`MS%-z#AYWu!as0k?Kgcm60dbU-pc$EeRpeRfZ<{u`?
    zl6;lf=pb~rD^*|P`q{XuJ^zv6z+mf=`s2(qir<TJ#_rX%xIT6~o`;xpA1PP-5a9Yz
    zO`wcKnR_?foPqo6(6OZU)etc~|E@mm=;M=)nRsY()vZnMjT^sM$qB(307m*JYE?pB
    z^~Mj2Izl|8@G`+E+i>rPZ-UFe$<PJA7t&p{&QvsYh+suPL+kqs^n3CwXd&Z%nvJeh
    z3gVk(ObCn_+MV3rz(9(@D3I?PlZm`|1PBIh1-nGBfF+U<t20e-TxfJ6w95B{p7a1*
    z^g-ZuZ@DW$4r5AZ;*h~b+SI@%S7x&V;}^LdS7*mu3G2W-AOTQtt`RfgHF)0eidJ!m
    zOh1YL@#C*OE<9l39bfeWLV`SW$nsgtRz2Av!g@Rhld8?N4Z)<+8hCY%BtXLj#s@iP
    zR|zdPqcE1eE|$HrpOkx*LU{JrUVx4?7tF`cBt@yup>ksAkGL0Nu!LADw$=(<z@?E-
    z@#J-`)u5$YgxfBz)JH+&sciKZsK=)df@%FB?0R(N_~rq8%J+@L<y;F>i*SbGqt7EA
    zHCx^C^`u0jkFZ|>>ZFbq-_F_|oWe#d6+3gQhU74sIjDb!5eSjvU%~P4EtMxM-|d$d
    zDh!tCr|kk_F=^`7*UhKuNan&3)%tw&z^JcJU}S7&^^f4y_r#5HqIdN<SN6ab)0DXq
    zI=78>T6Z}0u52Bs>7)|GNIzFI3Kd*Nk;JY+wfe|JpAzlT+gc)hwSbKk;V%Zux@f@7
    zOrp`h0F4_@w(wr4vPlCW?w)bHyk3j#7SYt-mq<<n82{j}ARowxX<mh<j9)9c<;=F0
    zoUao<!os9p42gCu$dTvusuZqh{l*azp2khQ(PUv_D}jRdC`8ZYfW3ZYoE7+;1C2A}
    zLlonzvs$mj%u(Y@Y5XW~hX&cl*KB^w9#P`)a2v;}XPvRR=R`OOikFz3dkk5X@37@&
    z+f0Kb*ReMVh6lhS_dX7vL9!IUIlMa8ijQ^;Fx@fmXAJ|%=on|2$*jp)0b*gu16tft
    zfA+g@Yy%UEbPxrXClN^H6~zWa_u%7j9799s0X&4H_&nas_Mb&>BzcU#`mc%)qBSn#
    zACWs-W?ds4uZfgp#$eQQoKv9m3axi6hdxELkdcIj=e#zkrS4i@kvXHJBWP}h*7Z8T
    zIXZ7lZrhoyLNkN95CJ<HIsQ%~CTD7*xf)7@JZHCq+{Sysd$7BbgAw%{As628XBo(T
    zZFfM7y0ayBed7<9*G&DwqvD_AW6U?5XIVz;v^cscr5-lz7uplCeDBtG+Hf75LmFIq
    z{AH$lCCN5iL>r|O+yll|9Fvb<QV&wCxnfUAiwMd-wG-VlZ6u+{9x9|UHZc_D*V@51
    zp>^C?vO9Zzg<m5Di?*7#bD7HIAIRFyn9CJ*Q22dAh57hN=*1D1U8&;gE!qol{-v9{
    zDVUO@*Xx=RJHI)u+|j<zzU)<noe~}9Af+VB2OY8GnK`9I(NKR4Rmn`i0ViWY>(<81
    z;^^vIYzj2R>&!(U?L)Iz%Gj68TJBg@zzn2oxWAr3u)euYjcXp-)5is1d;r$%Fs-Pa
    zk5?)=6&7rU>@+#FY<W^-SI(lfdI>2`%aGjGDWW$)c9;Sjh>iJiq>SO=P(bF{t#M+-
    zS3|Lo0J%7JAFShN`zPqnR!6I*R!uPD`E=+yH{#(_86D&GAkPs1(W;N%73r#oA_8-c
    zOps2_oL^S_5u<_~w)37B6^LN-8V(svh^@};*rn!o=SRlTaNOVX(YXGgpc?-?KK==5
    z5-HBm7AW-I)U*n-Z9m`pLs5Im&DD>nVHsu5j84`0jb{a>azYVXXF-3?uhz)TsVmBu
    zkqzvx$M9V?9~Vf6>to{ML*@h~`kcx&e+@*DK!xVRBFPgn&-WUDb$Ia+&eW|{eq4)G
    zuqPF<>r|_K<nC@E98IdE!ZJxrW#{LjPeLBulUK6~J#ZxvkF|Xwqc?Sy<G--6!b8T=
    z%!z&c#yfT_6uT2WrPU};CYdL`nB5G;gkjeC<Z^j&>y&QrZrQRmLEST9H$WBFtkt1x
    zB0!{X<{iXBZIAP_tWu&1&403q^ac$p38;V#TTzQ6zg4h~=;m*6^ea`esZ>2Pn$-O(
    zin@trVa(XUvj0W_ak%irLX#RLgj3faqF(fJnQNHxDC!pW63U7kNBPl@GPb=W9vQZt
    z2)=SpT)ZWTQJJy~qyeQ_C{d6AWNw_haf`JGrd1b)Jr@f6oePR_u!^I!9vri5<m2XZ
    zK17`LURdd~h^C=Zt!P6G&t6D<uysdbcH!^NAlY;L-CfMcXf1web;^TX${t;F_M+Ke
    z+HkygL%RfCDCyLavybwv;Jj9h&cGXlZ0e#!@hvVHx0J=;I-uy92Z20S6~TeuglSm&
    zT;^eBTfCGgK7==q47wnzuhwGV79ZlN44WsE#E*ZJAmkVSg0|9H=l*?+Zv4W!sLPSb
    zDO7xRu+XTMyF=My81<X&*XS7rScxg;=sg@2DN*DeB{k>@J^p!&+)t)FI_hMp52aS<
    zYNbK8SySocI`<27jlqZ9Azx61FL5Hj+fc2hNHE1RR+7Na%VG$Q+r4b}P6c3~lZNUf
    z)Pe?Rj`VaUjN9l3*H%`-^TUEdZh0H<7t<WFAqy)j5rn+YJY0wY1E^Nn;0@!}Bs=5*
    zsO|dRDO4=_+$%C;`gwa1=QXrmC^Jm2J@`Q!Xk|#_RsQ(_S5%FT(Hssvvr@3bnN{xr
    zaMf}DI>rO`Ge^GsY5U<d`(uN%WaZlrC{q6qsoA>3mb|Sy;DH^V4;aRm?tMI{eqdJy
    zld>ry2#kpHF6uyRj$k4muGt2-U4+`XKmJc5Wj)sF;WqCYD35>#pbY*DQRXC%fHb-_
    zhGjkPq|R7?rl!juH_!Y!G}DSSGq?-WjJXdfejLX&kzoHEiNFs{dmtb_9^Fsi)MrYI
    z_$t=Y5RtCbX{?Vt%WjNtou2zKmoMF>NxWrTA}~IZ5z%)^<7*_a&~JbqyU${&(JAXs
    zu2{AKVo6~_8<zJ<Y`es-i0wB~F~+tzVs<vvw$G!J$o_dk#tWw<2nF0kXqF)mJw(_t
    z*E_op>JrggCDx=b{nR-+;e`k{(c^55u?1s~sf8^*XH=3}p5X+V3AMf*+o>$Vk`!Pj
    zDqfmzq%Nf&Y4B*tE70tS=a35=sz}Q<I#eBQ;Ji^wB)Vo6xZ?H1QPR$^bcoi@{iCn<
    zJ7SB`&vL?l3)j`SlG?`;elC<MV2}{~)$*hrBsU<y9dm8NO3RnQ>Ca!}LoZ`VSPg+@
    z?I|552{K##Hn=AFU?tKf|DFjx7TI`j62_Lym&OTNb@U50%wo5V{}{0S2Rowb5{UXo
    zA%N$Q8;?l$n;eaL9m^_`6`phy<S3*^6%EkTX22&CS_nvv;}mTE`>pu0f9dwV)kG%C
    zNt~BEc~2#4gT5kt#02F$Lt#ZbUQXaM6rg0I6D4CCq@R-*)XETx_5fsD<v|HO!sG`v
    zV_0;5179#Is4<08LU8UwzS51DV#22mO05iIZ)dKh8|tT<Bz2&(r|c}otTJKK3>ddw
    zhM>kkN~33grX6#L!r~=zU03<#LTiZQ7$(_Ussw8ELQMSPDL_gX?CX)#A^K%5dMKLc
    zm>jT-ZRH4T7J;LsVVwSR<F;+OfUNAKfE-dPSmV})`@r?U^$0wanVZJ#A!^OvdHsA=
    zRd4H}7Bb_O)^L<n18Xo<TVq2?$*l)yh|{pGT$p2Ic5r{9@rw!lKX_>rT&p!Jf^ZjN
    z*20pYFJmqBaJ~&vW~kX8|4jO|yAqSJEs@VzigCM^>@UG$t*$Bn8fM^*Y1;IcL&*;0
    zh(4CYhhr-KuHKU(9ziOMBx-0lV}9UCGBjM!JxU4fF34C+i7p$cs9t@OVjXR$<0#x;
    z3YERQ7J?W?(kgJ9P%TbCPYtD%B8{1k`~w<x&5Slb|8?<6P8M4_uZ=^$fB{N*fc(Is
    zr7t;&4Q?=8l$LX1gkude*Ckd6JE0laFeSNz>2kKPa`W>93oTHPspx2%;Qh>$y60*`
    ziRK%kY;Auy@1=KfrqrTX8jas4A|#$!N5-xN=kFQ8Y{Qq~-5m~k1m`%V&KJ)@{@94;
    zOcWnB7vC(anh3|<lh!j4S9wnEod8ug@p>GC-sl_3wLUpv$n#+^?KKJjepDTj!zbs%
    z&~}h9$QLmd>=z4|4rzya0k5pB{ATy8-e)(xLym{s-#U-rKwAN@V)M4~jP#&L58El(
    z?HB+9Xb9Z7CM%vnBRJW75MB)9*8stJFMx3G84NTJAzzZB!i1#vw5NS>$SrCdeI-tn
    zED0uw8L!enhY(4SX26;vO{cE8VM_IZ$8IYON^F+EQU>~<7=~j)P|{HSbo3D)z1Z2L
    z;2L7KBky$tqZ14-&e#`f)3TUQtKMnBP>T5<m^>1La=Bw#cSu)cN9QlF$2mlaHk>@q
    zM3j0x<KSSTqN(f<)NG}<GIvGm5Eus_2)%su<o6Z6=87qTHI7s-YTvm<F}zj&f=<3w
    zp(SY%qQiG7BM)*f<i}EE_gA<`8Fnk{;rNxZXoQ&;ub*1rE)588!np@IkR<8V^B~Z(
    zlBrPjt-1n5bb3?e@ATwGhhA-_W?qeaikj%0rb$t+TsuDfrVW9aIIyh@itq##HC)qb
    z&MwI?EZ7#RFQc*OmGzdi-obhO;N@VOIed^)NBX@03bWs8Br^IeR>SK48h>-DNQ>ND
    zxH30_jf>i>W{X2)9e-|;tlld9CSm`kh#8@lbWGTtL&tKVWQ4%jK=3QdrKeS{+PYjQ
    zP3XaM6i244!!SjS;M%djM()Fa#yYro2cdTwNWHNvHG5OQ7sR-lW2m=4N}*Vse})tS
    zQgdOB)%<;Zu5(CpHJ?-kR<M9#SXB^A#M=|xd~cMN6gDMI1B?CKsjrDH_P1b^)hTVZ
    zhl6Y~F?5ju!I?}cNzLrIrkotg$QYVgVIQL%ikxMQLxy`;=gLV7!5YM62ZPn4-#_>F
    zQJ3T^lFPoIZ3@mGEv#<jQDP%RO6l`)1=fBQo@{TJn=TJUiG{nB7PC(6P8fxsY%&94
    zR{+((KRPh7ck}A&F-NIK(zU0kZH7}^eA<+usZ1DuQ!&j&WpesUk|JzC>#KgLY_!NI
    zJEsb6XRg3rXGSxzB9537G^SI!hWxm|6N>+p6Q1lNF<@3j`_bmM2Q!%vS}IsuT;C1y
    z{03QU6%3JqHp`OL<3nK&sBue~<Zc!4iA`v+7bOllMzFGC_<&U6r&lhCP~A#P#jZ*&
    zU99g+6PG-gz1@KY41vQ7O4?4&#~d2J1(I6Fp~L4k?KB4Z46|?3@X8!eaCz;cC~hP@
    z8HOdeHye7`aV4jG-52eA>Gb=Ji8ut99g3R<iK{`6yK4~FC<YMGl%R2w;&#8iFf3|S
    zdr&2el}{&tT9!zMNpT$)X2e-(zj<)ecnE`4n8|**cM<(4inmFK)J2d;rmU!9MtrR<
    zd>OttwHF6bWUeL<=45&*!x}f(EbFLqTaxZ;97(lB<S!T}C0E0?u{XZB(bj?taMnjV
    z@MQ4%JnUiP&~Skz8-)qt%!+g3)L+Wkh{WAp<bJ=38R~zbnyN&UA&#4rU}Q%LmOYN-
    z5)S(D+s`c3f)G5p&Lk^K(<GcbW@jZ_ccHtQ#!vt9xSNAYHD>N!q|4li{O8M8d>h>{
    zCn*LsXF2QR4yc2Y7VZ>hb^Dy~fp1B}?8w%R(fc<#Et@crcd|rTKhO^t<2m5tijVTX
    z7#EdYg#4<*EPk*bd2{Z?2qIG|JR04mR<)_KTG^?mZKIk18Pj763NX1j6QNIH?(kx*
    zy!76ex!*mI$2;;;>0%-cL)TL+FOjEWlY>bGYKkO_VCb3B2~iQy?LQ+7ii0tLuiuC2
    z`&i6Kni<r*l?ij*@X|Ni<zTZn`jYcRv!T|bHV9w*i0q+W0A{v~-6k7Yyjt6$#=5<E
    zg~4<2qhUGZX{+OF0Ae|qX)@jcXFcPNon!Mcl0$Q(AGc2gt$Ak^TFW*c$T%xVGm7qD
    z2RQuWo+c4@|G;E83@zTMddYJa5Q%=`PU2yVSiTM*#<zx56a0$tf&|ky|IVlZ^#zNC
    z5zIWiYtYI@oMz}aUTZ!4hhxN|OZ=L*P-@UZjeZw`S&~UoQuqxf<w7=+x<v>fGr8$o
    zn?#_g8spco#ICjBp)AW41<qm2OeoV9P2aS2uEo^vNkT0WUT-(|EOlt9Qf9&K`r;JY
    zMUNCnGvbfm+Ou{;FV>UEI#vR@kfg~avQ?B?|9~-Scm3yr^fO2lP28VXEu-?figs&@
    zIL~Mq+SimzY8|YmTgNh{xg9H6l6%aD=q*lU>7GX-y30uVrwx}aC-L)rJrO>y>%hjh
    zYBOL60(9TuHL#F1<q|-IwtL}uA8+DI>zzrN>h+=1pY|`Y{-#?7!Gi26XJ4-*g5m+`
    zi$R9Zerhzd>O2dK8T(>mf_p8uhx57$yMn4jMnPz>Nj^^F{2xO}hOB-`YW2V(!ZmDH
    z|8&L_o-<*^m|0@K;_b6S!@)d@c<l(B20}C)(e|=3{E8bgD-d8*KKjmWH+9t1D%8Jh
    zM>$s(M=wMOx^LXm#xmSFd)QM1>8nI3DS?S^^UDluD~)h0F8VSyO=e{#`K<T3Z*O+>
    z6dKYSlL7^HBkTq8I7$>3balO=y(jyc4SZRe;!BvH0rC&H(jR-`$gyu(p_T@3a`pQz
    z^BhcU(^f{g6LG;1lS#g|sYyt2!ZrY1SzHYfcgco^kWcnzKGxAta@H#WjrNn3QHw-=
    zFcmCxkcchi&ReBc|AG9k%fB>v`8cR)52|)HfDEYULUf!dvNz%x{$wIqMT(Xr%rZ4)
    z`!s5CD(nU+Kr>Rpo`=QvmZJs+vn^(7q}Z3L=}V|+aF!VyP%2=bO=TA;XeTTc`GVuU
    zlNASC^!;-@caneCB`$T_d_R6e`G`{T_(#q!iveye)Q~7^myQ?dLL(!$jzEy6f69LB
    z`C)edE6ZwXmy-p7Gn8pT-2HKTr>F}D5dEIHCvo9`IJdQkkvBpz2QLekI?nD;e6r0p
    z%3VB*%1jCBrX3|^5VsV_|Lkb-UGm%`RHm4}ccU5-3ppOe7Ph_K{)}sLjEg=1G2Sz%
    zHegy~!B(=ZI)kDzc9n^w8N@R|aMF+|i#)o!Jf%Nwi&Ja??JdjYI2>+`GVLHewN*A8
    zd#?-W${qUQ7}0RSK>)YEh?%520Z}&znNTN+P;z1TiiQ2kte1XaI*Uu!x^!V<er%&c
    z$)`Wlwg%LjCRWNnaQG<Bz5Kd`-=Y>%<E~u80-qtf7R~G&?b9ENkYGi`2BC=sMkCbN
    zaYWNg#~lrHx0X>aw+JQ3==JHQDf^LE942mVB6hanM*wujlyuVA`Z{=WUy0VWQ!|<l
    zg|&)%+$3woUQ{XZO4C?4NS!wrta;=U_!=;;VVkPs_VJyw>mLDwP&$XJlC{x6Ljf_=
    zC{{5mYVoNMt2O8BfS&cLU~vkB`Zkku(uO44sZA`DaO!&M`dyr8B~g|tlnqSXhB5!L
    zsYws3<6JmEs6&7<;-9%f1q~R{@38BxH3p@P_Pt85ej%$ehthn^j{Y$o4xZvWiK580
    z^&9o`)FTM6O5|VjZL3yGgj#BiX1&Bu+te7ixx(;J{KmWz-dY1_h6rqIt^EY}jewNB
    zLu7E)A_q?Mye(=DF|rK7<ucn_7Su9Ujl-X}hltJ!3`WYcX#mvFPB)DmY>GN3Honbw
    z_WKFd^#x>@I71Y1-UNkb4G@klM`L;-hjz+VjqAIo2TAg_%D9D^PJuTt$yj1p08!wQ
    z3Gv3NTLHw)2Ma~NUrL|^yVm{ybsqaN|D;L`m-u&MsfOb!Fvn}KF9Js?>i~yDi(@i%
    zFkIVl9m3*>m6pJmPqFpfu3x{<a6#fxgJZR3ky@OeswS|PXc7FW^ECtW@$*3fse=Z_
    z8-u!(;BE90>H#=qSXEqGF!3ypCcBbB(pk|^h({4ODktT4{Gy?4&lc|+xtQ-mXDjoO
    z47sdU!A&?wh=-@lR=$}nwaE^q58kdukeY3iIP+MDcC5X=-l6RyEb)x{)5Lac2oBS2
    zg6Z6V9s#+Huh~!<>`89g-kAp6*P@%sYN}T#d9!D8;)nxSl+Z}_PGt`boO++)n|^G&
    zNIvDh!tWi#o3x@4o<Ztv0J>>Z2T_Rwhip|q20;$Lt8QU%+Q=3)<Z?CEyb^fsSKQ3>
    z!3NYj{W)KX%X}_sdcQExwv=4U@elDY&S(WlmxrvWL>)dGuU38ID{t#Kt`oWJZilSb
    z*mDej;xDZcSzSb_t|4s4W{T3)f%&CUw$alR@Z4z$#na9eMVvX|U3|4QYS^eu<<;V(
    z@>*`?cjN6yWd<DTbiy>jMDLQ1Nk)UkBnfgO=XZN?IK@%}dJ{(Yw@Fhr0w_{i#BQmr
    z@=t9l`oUxiuj8ok^O(L;`CDU@CZnr}g^4URq%;^R&dJ`5@o*hlNQyodbAs`~5ZD8|
    z5dsHXA5rw6>{gKE8efM&$9e930xxw<XH81$eq3r>@88;VfvPg`nr7#Nl2uu~=~%5P
    zBT9TI=1Y}?rsUGjg*H(ZE9uubiod0{q?16^<sZ(R$Eq-8=;hPX%|LPNS~I0P@qJ>V
    zunr<P`9I1p#doQIJ#0q+OAxnf-h}Y(a}6Vuwc@vq-(M+F4axItm4xADQpl{0D@2w8
    z^fYc$Hf9<OP~9lHA}eDA6ANG|jw-}F01L<#3br7F%25V$F55i4WoaagRb8mc_Ad=_
    zYf9{?pWgmlKOoM3YNZbH(h5}1h3$wr1OH5Zs9|($x0C7l6l^F5q{~02QwxAMj~Rm_
    zn9-nxt+`Y;G)6X_W+jiK%+1?Lz-51$cd=mEZ_ckz%98!AY0jvbJ)HlHc30<3Bh=IB
    za3vce9-#M}@?}#On#^l5%KA=$_wSktTf!|kxCC*OCr)Ja^(((|OD*Vq$hFptzL~W@
    z|1z2`iIpAPS*-`<XY91S!PtdmqcCE5(b)I5>S`l%h5`*Tm%hhIOqL*Y^bXWDuMepp
    zC3-X%jokPEFn2fy3j?x0N4mAd8Jkm|1rKpF@>-cIUg+J)<60Wd-AhhL)hRPjQ6F*E
    zS92`pL)9lWi<VNe#v#MnXhviIt<oJ}%xtmvZ6VsP^G;#CML<|n6ixwon2+JVe=4;{
    z^itHw-|%qf&>hlBF~i;_Btm_TU*^`;qrgm|vr9i+In+ANN@D8o6tN>ESq3f~&t!a3
    z*zZ)SZ|<SmZY2_i-17R&ebed~o~0rCg964ZO`|;SCZ_ZkADWR{;38@jAJO1R!Iw72
    zR-^YvuvCpUbtN2Ph10hL)SdU$Bc00;sA6&Xm<056Wkp`MGTmWmR2-)mYLcjItnGDk
    z_qj6=?gy)Iz*|?GJUZ(ku#)7jvnMUlxk~47#27D;%4-Rj+d-LXHYR47YMPJAd~00x
    z_hJTtP-#`;D3f~qEA<#~vM!lSuuzl|!5TT|4gmuyJB%`KTEx<!yyZ)cbO<(aqN~!!
    zV?OJ0Cg82fxkA9TO`^;V`(VnUe)DjlD%}f)-%i9@mRXl6vM%?ZohOLaso5aOUFEmz
    zme|ab@gH;0P~Xt2BEOxADU$22@!Rt|ty>@LwadfR!{k%}2k8%p+nQ7MhsIbY)W#x=
    zRRcK$$!}x_e1T9Lk|b-{%yQ&rKG&Szo|IjQrQ9u6V41oodnR`pq(0e&-k*>%<W4N2
    zYdNs3gop%&X3Kx;<0hbID}3FJ5X1_o`<~yKMzPpfjNs!?uh|{nfs;~b>#&@auphcz
    zI{bU?zxD!1(pH_GLwwP+L4g`-BI`t=bt9SK)CIpNwc-vU@5*&Ef|4k)3+4h(>?cOG
    zWfG=I!#R7}Xf|<DQXBldO1{nhYHikJ2Yg7u+IANY{Fw}z`6WU-LgTE#^o`!jJP6s^
    z>jVD>HAJmnsLiW=t7u&LkYOr@s2E#v*(7owC9E`zxVI=7z1-AX27sII2Nbg38;DX@
    z^687Qn%wF=MEQbTB=@~5)|_R=_6y=;f{3)Wv>32(s<q*gt?Q3`$50<Fs)tL!ch65+
    z2^|noN=){3+q$4JJCdsQ);~{l&_h5mXzeXVZeA56;GrhI$^gq{^+g7v<@TTIiob){
    zyDW8Kf1=9BP(&Ze!7zB=yiH0$SRuaj)lIgeq~YTe#1U}YQAKF8Dr06zhy@IN(eq49
    zTs0p>ZvIrNOWR0vj{GHUB&p~Z?spn$ZE`Ykwf-W3#8$NnY`H~XdsZNINxl4mDpz5Q
    zbr_qlj8AdpL<c9+JN2oQ`0{i$oT_-}=$>5qKu@NFmVlA_0KK8wx|*^On|q2uZ4bwj
    zk@@nds+#q|-$j9MwlqIPEqNsxK4?<dWbJa8D^$=pwn&4*8Vr;FAS=^ork5{De*zJf
    z*W@h^lUK0Q*4^=G9gQ=A+!;lPiJA{w)FDueewn6KsN>OWa{1N6PSL7DJno98RArSh
    z#=@p?cj985sHkML{m#44=}w%L_sckP@sIWU*~m19pF>FZZm2()+i#(t9n@!0qTsSQ
    zk9q;KWqQ$rSYcvE`8VvSQwKJj$v?#FCRtS=cvDH*4>Umqz?7P5CVYJw7W)Gio5Ix0
    zt~^$_)C}VIqfqq=h>wqc)^5@@?H#%YtTA+jU?HF);I6Kfs-iQpWo9*C@$S3;C)Vsc
    zPUlc4w7w+(1c*NQ`}e5TSP(MH6XZ#A@<UH>>jO5>2LfX7e3#{BN)Y$EE|?Kg?xb(V
    zCNV$lDet$iFWyr()~Y9Bw2A=k<&jzU+=eSnpE~DqxQfT+HzTa!&s2Jf5vsVS>J>|J
    zF}fHiga#77lq7&He_n<793S70h*q4o)?XPM#iX<r5`Z@}i_=P;rQIg(^?HO+W+ydY
    zZki0~p`^*41<k7XY9@ky6j{D6FoHmcn))4={e}Z7zjpIKeeEDUZx#hfR2tCIxH=d-
    zf1fwtR*jrdVbpoGluM$#X%B~6a1p*W>u%kyY8e-v|Fkp?<y_7<7D}a)^9)=pK}X&F
    z6H--tM60<>20}I#a{)=LHO>guf<fUThQscMPRiqT-c;7`^L8j`gaT>zs<)D7ij{Sr
    zM_JzTq3hjR6e{P@9zs@dDN=e&NSV(orX9gVKiKiD=_?F9skrAjbc~dKQfO+sNX1Hi
    zu<I(zb2<C58`DS4C3Gb$<~!p_sHp*Gmtx7(V#n-yTrp$Pfog>&q2Ev2lZ}>EnLkG;
    zieM+kpZ}0i;Vp^D2WS`>&$))|tfm$#9)7|B^650GFE-Q@6(6|d8{W{H&wkkJd9bJl
    zTBBP1vKY-E%TJsOqGtRs)S0Y^bnUFF0I~3Q@I<MD`|XQ#ETO|MurtPoHVqj?6S@r_
    zyR&PN?#PlUO-TQcQ8txU9D?2UYrGIu%#7uV<<}Iv4Iot-aF<zAn#H$OJLhB;<{nyT
    z=`u}5`;`b4tG`CdGyW%uN8G$+NX9{i3xN3IMKI&R_e=0%5)>m<tKfNo1KWb${R~z3
    zNF(a?mwpIkLuHJHIIMfbes){N7or;#{TBXD?fzQy`vj@b_D(97CT`*MYbA>2dJG4W
    zh<CFd4lRyTWF}TF5Tv5r;2X+f?G|%^JJudyc;P!k9}8_cXImAC446uoc&X-GKH&)(
    z<Z;wdnlGTaX%ih4;>!^F{**p)<Pu>!b94GT^EO+K1xuuF36}DkNqFn^QTf##4opUL
    z=QHegHTYb`+d2hmHEtxf!kj`j@@%e&H$_RbwgMLmc&hNrUxuhVylE6m8)ZL~t<^8Y
    zqsGBX;8!MT)Uq_LRYr$SJ1toM4%5&r{>&xHD5?jCPnaUDG6jjS(CU@8#MtLbs^%lo
    zOv5t}#M*yy6<J)--Q_Smq*@<qobKg*Le{Db+gW>a=s3n@VL83<aL`&!x+C>EsWx?@
    zfWFS9Z8jO>fU4OYb^}@o%6Z5PEt%gM+&0!kLDD~Z(;$M$X(1tL@ct+?I3BzVr4@*D
    zo_8BQ9!W7YJV<KHZa?hf*SfcE9t**hwupd30#ziPCmjuSFF`E1Cg^rc)t_6<hMgm?
    ze9qnJ-5i`V3qG>X0`DzsAMqf~aW5~o%^({QH618<@EH7#nn)y0spjKdjSPtS1P<bA
    zhaEfpwtLU#!Ts>TrHL7OD&OpofarH^f(`(EhU?^Fh?U5p+|6O7)55O}hW7_y92Zhj
    z_8tnK0ET%Ar_6Sd!1-kijNUI89bP4vT;{EC_x|hX5P$Dt{X)afGOu}m5=PLFU+E?v
    zj_%{RZ(c2+w%R^_)w8d}LQX=sU(@lNkTEuDH*EhDpY=X?+HN&GNcnZ1#g<=O_(a9L
    zFO}$8(ho7)MY;&sZsIN7popu2a>zspDLq+(^`ENzo-J;(mFrH8k2%r>dn~lxpqTr0
    z`ePH_7QkRWi2auWXGKVuSyY6i8fM;6f~^*12avvDX#^TM2yk4MbDt<9r;M8Om6|4-
    znRVvQ=EJpDWy@x+e$j;_=MlOM(k$C(=EiP%=tsm#4wX%d;<j~Ebb&=b4V5m<%HiS~
    zAv;mLcZ4};;<`REsj4rlp^dcHqaF@9{5uh%%wv@;4uzLuzczm+3u_Q#s+WgeI|$!L
    zjN-oL8Ooq{?-e`q6JHE{=Zs5+EONw^vWMiOIeF3vwDIf73{n@U{!{>t^rPhJpD1Sb
    zkBir3uCp|;5!9S|=;_&0LzS0*Kg;X%8QEz35>QCGlIgdlmz%ZK7F=)}A6lGNWYSv=
    zg&80A@b~>>6k7bj0<ZKT0f|?(cC7qDH!%oWqgL&`KD3_5HnTsI@mIb>*WuH0k#-5A
    zUhZ(KeW4U=Lmzd^bX_xM3+L!vbbhJWL}In2Pie#kMtk%rt}Q+7d<KDr*hMeD`jAfX
    z$$IGC0&-&QX)@lgwR2W}z^FM7b_TjA+yz;=T$`u`g!W_5=T><vmi^-7{P1l=C0o@G
    zAJ?Q|9`8tunnK($v?)Hr_<;*+Pyr;)(~hY7IE}r&K#cx2MX@B3m?vc{>Jbhrdb^lW
    z%0A+0d)RtrYS$d(T~`}qi-pxpmm6yugUT#U9~zZGH$Q`(?t{@az|urQZ;6cYqF=mx
    zquUikBjc_T6y8x950Roi`4-N?vVxCCj~pUAI3^Wp&8{i=Yb%MbgSJx2ca=aKn}uUn
    zEYMtTBMde(b38*BxjeWrz8wk@LjuE;UJ+~DpN@(K^Fj1ivD8Z9939i`N(PW>KRTM)
    zXGacxcQmTPoA|L~jX?~XT^5IsKM7}lmSMX!sd-<xVz%BNpOndBLK}I`bUB5LJAtA2
    z(0ap7FTx1g-*+c3^yk5@I-ru_!GA%`0%(x)bzv8YZ9|je_;2BRO+xFmv6o73wN|p2
    z-FRXe>kX9)fAg2GY|ME>9HQpnfgFkK1}^x<oqIX|xJClhR~C_YnvNZ=LaF@mc}v6m
    z6ul<jF29-^80-0>j@^y@voXDEsTK)<PVL&1A$_-OZl*!(fSk%xHyFmfrogap-xwF2
    zB+iuy&6$6cSp3p$-xbw%Fgbc_v>@kO-+)Hjpg4$i32>UCJ#8sLT8vWZV)2`{I2FD0
    zd*#)%l<AuhARimA^{V97HK)A_qVJTxR~4s`^mRybNX%qX31;NhN+ckth3JtlDVvwt
    z(ubs^b){6Y>PrX085cMEKOFyD`vRrZH!kRb^y4$(@xWYQ=Nt?iFxOGn?8P#}X<y?)
    zPtj#Pe841(I#tc3rE~)`%*t`zq*}*vg_o&~4h>nf-y{Er;NNl*+JRA(wdA%9%*<fy
    zfwBY51&ro9B2O@=*HM)DjMv!nH?~EsT@`bGB&!3(&55ibi}go+U*qf@nZm(m56b%p
    z#m4#15|_?fF1y~sm{==Ep&u$5F@6Y~kSga!^6IjP4)#k6?|rCX5YqdVcumQuUc!x(
    z$RR{E!5nHK;F@Bng|yV!s!%}*5f#g#H#{batC2QcQghJm3z>~PC-^@9a&eg9T|6GV
    zAg8<QPuSM^*)tOFx8pZZU*u86t!LNknpJ{)C_u|GN@GB?PES~@Z}_fm+3i+pWY2Gi
    zT6L>d&Be!WJF{%jhBKgYMQfA`&#ov=<4c%k_d2>N{+g$dOgk+c_}Qg4cX4YvT=b*Y
    zTa3&NBxk|0WQaYi0Q4Q3G^Q=)x50U}sa`Y0UYNmwZWMe3b8`-%7ub+t^HPPEuP8km
    zoZ=sRXjKvIm;6MP$lbmmLo7}+qr}FCl%Srl;*E~i9~cokDz@^eY^*`|B;AIt(Pu;J
    zVuTAbOFNZkO&lIZqgj(1@8pSBdN*AzM%CN-P9wlTcHXJ*^wu0UE#%7o9ITJ}srU+(
    z0CK<Haxhm6m*J!;SwTXi@#E9P-Rtl1=sJK!9|%WO?1S&U?L@f`xi;T};Mc`7^@68t
    z6NR@lWb<zNG*$gdKIAy>;tK;LnS~yxqUB-1PcxIVn({6Fyz6^S78PrW7jkv>6l$r>
    zK0AbQU%Kte1)DZJJ@B@Cjp@(((KMZae8%5=+@yPOfxHn1cfe<-OZRt?vgyM&3A|HH
    z@>Rhxu4{RRX!$$c5|2_1-jLuMvcWkur=a^hD7jV9ajwvmPub8!8D&LKVJDpjN4a&n
    z_I6{Q<=Q9~F0}^Nb8=Ge4$aTFyBSFw)Iu!@n~h%hR?0u==1hO6|G*Y$@6#4Ga+O^Y
    zoM-C)ff`A>t-4ICh8e^1)u)(Hb2a+4isELPL{UstOBypH(+c}o6nWbuycvD15WX5_
    zM}Qz{_7|#%yuUF~I_K%0`Vf$JZ6H6uQKDh7n4j7zBa#guglH077hs?exo%uWfF$#+
    zvM_!^wS_K7zBvq#Qevt_lwr%`f=85U?D5qtyVPRgSYw5#oXQ3`2By=rh#)?sL=$7=
    zY@}ocJWiwjy3=1oX+UVmdc_C`Cxz8v+2Uqbl2R(Is#1t1M3<p$6><$I=A}9CM0ROB
    z8Lm$-m$3N(qfMc<Ni)EYriMlV)<iDa<OB~f5hYH33bjR{Q3DYwxqX``h_Wm(E>Gq0
    z50D;G)7jD=9fHM&RM1c<HY-pI>)!u1MM+5RmF%hrx9}U5+hJZGt?ryYU3D|6bPhEQ
    zEJ!annd-?Mi2v!j%18MkmnoKqaCvAbwr(TSk^{SKVweRRlJ)%lY-)lA@l;?%$9+~H
    zmu28u)JMUpJkM4Py1oA9GYux*>N8t64}Ct6(@XPYRlrVD3+i4rfcZQ1^{g)Eu==pi
    z*Rw)-SrPt@WX%=?bfc;S(Mm4qJoVc#wFs<1HO!M_b)0?4m8ETI)51BvVBO!Ta1bgm
    z-`F!TT=&wzK$vIY$IhJC_tATDHAd+8ihxi3!=!#aV!RPo0v(QFD1=6Vl`x8Ce~7@}
    zsK8=XH|)6{HU46BZE407GHKGc=!CA3Y;Pr0Y*2JC^lS^}aS*I%juvt7e}de1JLf_P
    z%2cLORGsO{ysQxEtdB#C?af%(_h&|}Zni&3q1tsAZiXJrTv>+-ld(z>>jwv-{+2dI
    zxSAvo*SBAbCviT+sX*~^m3gX7#KAIXRKY>lX-}}SHDpa|$;(v2z^0+y(tlB`TvVW=
    zeRnFEJN+qfb;-RWNv)|4_;sop#$#0!J&Kxzs~lwLZw%HczlnHY{&rM$Av|%|xlOhB
    z$bJe3C~f)v8?Ps;hSQ1?Gn^MrVDYfFA?Arx4o5W>8}(xj$QQair{JCkuW9k(vw!3>
    zPo{-FV?D@h^VxvpXqznX@Ajvunk{{!r}bjM$yEI0j21YWu}E%BL*Ja$Mnv7Mq>@4c
    z!@5vS_HRw@F4WQcMgiBjOflZE9&?N(r-YO=S_~6NuesT0FtwruEQYn{J1|wrB+GCV
    z7YOfLCyo2u>aGQSCr<2YuB+5C8lmT>;nHqx0n_CQSzXxJI}s6<4?Yt>zNYoNziDEg
    z-p!cQF}g7^BtbKiIkKq>`i;rdzBsw}L)EjNEXR&U!x{>xt?r2(Vr_}QLa2nSSDF}a
    zap*y6n!({;C@070q@LqBgV7)q#iQL*+^E6HM*)lOUWS0cWfawkh8SmvaFjat_{H<M
    zd=!O<Y33)Z0K4!6*Ic1PW^U~3nUHP=uHg%_5PQifwCqRN-%4%MAlAZkuEa>I%7qP?
    z3T_d#@5}0+5*!)Q?<x1kY&bicm1;q#ccl>IK&%?rBTjrdMP_fjJ<fx*blU{Po#d}Q
    z`M9k@mbsg+IUL@HOnqxXI6p|m@qgq)<xgA&=v^Ky#8sXcy%zJ!gbmCj#F-n*g)g=1
    zq7c0k(as?U6v1L-31ar}mh<Ss{E(I`IB`zmiQIO&@F~S?3-&^q?_zonI>s`L;24UA
    zYPGH4#-T)v!^V+KmzYhGw+uuh8?a+=EV_-E%?UabzDXI?<Ieb;c<6zX__LuSLRw<6
    zz+Rxso8{BJsnV-ne@<RX?-sHSZT3Kl?$`mO6LVRFrU^tznFm|to@m;Mrcg5XrDnx}
    zGV0Ju*wh)9-8w62u3&?v2dIYAb7B)^6)7wu_~X+*(}RNkvkNV;n;UH@UUbg!S7GvM
    z=|Yoc4U*Yl8Yxu<Efl!yw3tR<j|zhvI1zdj&*>@a>`e&u+_PAJ(Z(l{8Ow+DV_Db7
    zpppf=O7NY`Gm>P!1D78jNpWY840b|w##Cyef(J4dC@b<FC#B+zW|c#@%w2B?53PVo
    zS36+NPBuaT+*NH?r6+5lEHo=!sRT1_R{k@jVEH473Uq<u3d_gWgPM+FI!e1m2x3Du
    z>_WK2*gM~vwdB-J=e=F7emuvn)pr~^Art@Bo%Gkx&lNF?QNF~oT;PHdF|Jh_QlX=@
    zTU*ES%FWF9wP7<m?E7{GJGCK}alY^gm}R#I9lb@(amNn?XL-WIcI$2Wp1sibCeHMC
    zPJ9EqmMKuy^4@5p11o+L!z{f*i}_2=w47z}KoaSlIGBpgG$(ss=YVpeHmZ*|N6jZ3
    z&cvxzTM8~b6iDE(p9EGRFce^<em78-Bu&Ux!wd<4FQJEq!Dx9~Codd>tD$DP&1~nh
    zl4kB@_a>uvm<J&<DbL<>YkrjRs8@_i4y$~Y3JM8lE3MMxo}u{H=>xKeSl($3xA2oo
    z&hk|#{Y1ayV0sMNgwN@=r|i{3dR0yNFX1SZTTo(-3%b5Z=F{8by8G9Jd6#W2ubHVg
    zzY8&~9mNwuQpD0x;AVrqJWW*{UHN#5xyo#;vXj{M^vpO)UPe8y<E=ms6Z9T`5?|g<
    zz(t>bs>v?-oPg?e=x%1fn$353*S!?OaqZrzCZdh7amx1HSD<*f)h0y}S*tMdR>~b!
    zlQ*QCSWMt>da-&ri7`iVQFgzQm{C!3|6>J)H$e(u$^~X?uxI?DV}(D#R>c#`7LZKo
    zO)qI}{m>N~8~J|Sq+1Y;{l|@6z0cLpzT+)23Cekb_R5j1n`c##hJA8z9GeOv6#5p{
    z92K3Lt>1as#aCee)v)BJ;!!L!Eqx39!`3rh-0w9x6$S+h_q518Va_;8u^+DHr{vPA
    zqXZI?<mqSCf0)UEb!3|fE%Sv(hoRw8*AV~5l0Ulmnc&m!Ls#gOjK6<;<eT_%I;GNQ
    zpZ<HTGF6?}#rh2KZ0J5@A<f|P7wH<BV}Zg(=-^>W8d6+;Z@N_tS7YeDp^|RQ`RH`g
    z#4~#RFQnkaN#X7j1WL8597=6+8w>$i7%0ZCGCek3jW9GIYzBEw)8oDSH5C}071cSX
    z&She{O5O<h+?lmxT7x(KI8K$)0cN0r=jxV%XWJEd0yGHD24z=LlRRL)N4s-p2tzZ$
    zllo@TEJ9NA`S8NK5iASW+ozG1&oRHLFPJbdHA*?=219jT{+!DxO4cT`22nUfmj9A#
    zLyM3XnjInzH~s|l)ykFfmsOx;O}m$pQ2(TT)6+=J&5WW>nQ?B>E32_Kcd0U-zIsB5
    z39UNo`>T1z@HHwxW7RJ?KcxSINk!&H#n@<>3rJt9f7A}Br(w55M+H~=LIWQChEu1p
    zYb0Gz&}~T~>~{Es*;C<U6BbH^48IWd?ux0hliYxDiTTey3Ck*`W}-CmBGq5xw$x?N
    zzgjVPdiZEqg9O**1gIBqBy%SU0Y2=C8l_Xo7^kVgMk3)cF*MKoo2I(^xU}A8JLd;&
    zXoo&7cfyQ3g6&}J-$SkIn1Z04pOOesXzRkk5~pjZy9snByhIMDAvT$1k}R_%>R-l?
    z-%sHL2Ufm0L%fHi`(K^NgCUW>%SKW$mYHmFTo~?t4Ih9uw8t*Ezmx_#IDsUWx8OC>
    z<&SKjEU54v^iUv3uid@{hA1;tHQ=h{A{cRj<|1HnBg4Ac(wx$rqWB_q5AIl+*p?yH
    zL&X7%?;ywB6yFs(i~KHM<0IPWGPR4r<gDFt=hw%QxaA&}+^Zms^}cyOl+W5E{z1|;
    zFyEv-5=&A_MSD;~J(7v%Zgg9G7lZ8j%WJU>JD{Up@e61+TN9>#ffZnxW*sFo;7*%|
    zxN~PwFxwf7V*JHeJ8i_!bW&!JBuUG?Jgo`QGTd4ce&L&CFKCm8JM_|1HNm)?u~(DP
    z+cPmYj>%Ckwoet9E1a1kn{S;1wi*2p3wNZ0=|Q-ngEGiotl80W45*SD@w!SG!D&yu
    z2r@^X(<pl0zc{e0(3OT_nD7~OG|MYILxtLAEkbH#MPv9a^uHWFf^m(12PO|1F>rE#
    zjU-A=CqLZ@QY4gPGRL>>W)N{5F@68xGGgfoiwvr?#)n<$nyt`rNYu=Mqa2a2UL27b
    zXqu@>;C<ABqExSu&Q0#!7&+ZI{}jJGtV8l>3zCSPDK*J>LAqRuw}(V=u)wvI+NzIK
    zRNZ(fayR0*?WaE)UfcLuseTV8`YbR2JFF{DejLKM=TuJ1g1Z%hPS|JF@h3M)>zHYx
    z5Fce7T`moZv%jqv>}pie3Lythte*L1#HeWBK`kJ_S3RT#6D@s=p+TUa;y{p%?Nf0g
    z<i?z_V-&?%;?IHo`=i6S%($n9p_&jd?Ik(BYUXBa)`o6_s1N#M4Dj6CPNfKbb?5gu
    ze0ESpMPC4H2VKYI?}gS1G!P+yNhW9SK{tLW;tiyXm+3VS<mn6&mXOi}(dLa+8LMU8
    zxqI7yM>Z2_ET9Lu4u!PhalcDS&eYBdEc+Xrp)EvW`gh0wBEXGHo^d|v_;@cgYB#_?
    zA8#OBRfM<~wF#%GdQDF#pH7QMpiR&5f#jS1$Bb<fGC4%z%saAUN+5>6okC_sYbopn
    zoE6oqWcjJj*^r(fA^31ERl<r77|3we++#Xdw9z=>pnwVxsJc!Sol}=rj-~xfwU_CO
    z65vgP(@+5y4f{ZYo#*qxDppk^Z-xgS5Xn92W7EgjHxPsc6kq|h`DVzsW(UY4C3w86
    zTaF9N2SO@q<lKnZLjlXy8J*-A`9r5!ngO?(1A74T>Q5-r&K5Oz1b&)EdoM@#$~5Y&
    z#xX&vigP6%otwL9oVb;7G+Cf&rO!+cIlu-oc-6<^q!ZVlk*$iAlEO8a3l-zf%pP3r
    zT{noKDiQIdvM#jV9YV43#jTKbI{2`t-pI@ny_Zz%E%y0dwC2;90Gt6^|G>t(HAH|h
    zV}xs=j3t{bQ45isF15B#zV?#R9UQ7y!CLz~0c*%{qL59qyf_X+Jlsfe$7sJDoUAia
    z`vZj*R0PCosTEd6!P80}KTA1z%;UXd2B@+sGin#s5|BgS1fqcQ?Zf$~>K&?lrDXP!
    zvj~~TXFp9L(R{QemNu(zo~gXfMoP&93-L^AxzG&!nCIZU#QGuxcxqJDNI=Ta(v!OG
    zS0C8)TGcS(r%!(GVAHQ#VwP{o>NYNF#PVIorHEWKuiwc~#V~dLyO-Qd^zrj6h?$7<
    zfVwYiIns8N$?SIg;n-vC&v_BzSQeDRF3)15;SIb$FD`e1(sXd<N&793`t>f=#@0v@
    zeg3#D6UwnS2Gh@VtIpsB9tK^L-lW@v`U-^<L8EW7LGh=k=P!gmlzGMCv}Yvj$1^Rw
    zMwPK05M0kVm~fM#luA?o-)1#%!?cu>Fb!mHvOXFc^tk^|0PPAA^$Ngj;fX@Dh<vR-
    zv!9+wn^XlAGdxEMnTO$N%;6YbT)+^qVECDjx0#ou&gb)L=~~_Q1JiVCqCdz+qPS;?
    zKfI9KT7-Z`0<q7kyk?hKFwtlr&dlb!Y@K@~G|D_9!aH@jQJ5;@abSzx&BALAcW<wu
    zQ;tg_#bnMCaJr0*@{}&#sK=Py8az1+c+ocv1ToNRw5Pf-%Dm}klubmHd_lXm)AVG$
    zR`7y8ouky_{1~szurfi)$O4luN^6k=W@#^M**dhRcGZZys=r1C<!0+q`8WEz7SXIa
    zM=I7ikQ@@Tw2#cOLi_kxD!-?0Z8LkI&l!Se0j;iGv^k_aBsluH7>|rcX6{iLzU(8w
    zYVAbE{PG^k-A)@SJvJc7>W|IPwv!ne-l-mSztE%h1fIn@iZS;qk7O{+)y{Y%BDoz5
    z+*+<xtEEV7CsjIy3~f&vM%XY{k#?}ZjqseT^46keJZOM!ElnD*o~ue@49HAWb9UFA
    zpqRA*dg<tiQWL@QrWv@^bLPH4y5HqxpFEw#w6q{#BsU<omB%(mNkt+|mVuCqbFtsV
    zjjBIj5v;0aXo00QDR+K6N3VgI$EKYacYiNU{HpB;+BWcX_x0kxa6x0<Czs=(k&%2Y
    z5C8xO^+`lQR7oimYM4AI5|V9PeqODa=|x&%t6RNo3#@RQp|F6{4odOa+;>Scox;S=
    zZ6OWeiH7_VGzm~>FV#4AIhz&~^nj|5@Yx-RGy5cMq`dA>%r(sJiLTNxrW2#OkPABl
    zc1nFlk-$k#0;&qE?aCFEF*kZVP`OLJJ;rM@Kk^n9$QRkrxaP{hWRPoBW(gB$AJl87
    zs#orXM%+?d{?2Ih0&vn$M`+fj>%HuxKb1FcetZ~jxC+k{l>N#+2~08i0J)p7dcy;D
    zrj6**(3_Ja<g%j{f@0SrL0vRQsj%6s)EsO#n;r@#sJu<~fi0R1il@*}M~V7SU3Z9-
    zZBgIxx1AZ;1N4CH2z7)iwQ=n|C`{S4t7dFhf|Q;n-Va@E3ax~6!rk@?vKqj(=o$IX
    z^RlDp*D(C)bH!2%$NWAl;`NH~pp4dW8}!5o4s>|HPw;-Ny0`RGK5eB3$sTbZ3a12$
    zm}pkQ0(d_M<iq5{CGqc>E}|34J~9+nrwK7_R4C?#+IKPbM#>2uB}l+T6ZSuaDeDf+
    zn%ImvF`%1krgR+WT<s(!XI{|#CY7g*$gJ_7g44*g;C7bA=PLtCm}Vcm?z!P||JU}l
    zK`k8<#Vyf!A_`M@&a~SCRAE!I4z6hmh;AhBwAJybf22^zo!575JA1;Tg#WeYuRQhH
    zf_iq@fbSQicX@5NeV{?dfmi(>J^#&+#iZIMQiK`-Y^u1x=J8DqiY037soZE5zf9Mj
    zeejV>u+p{9f8v+`z#@Mi6wqDIpPgax)K7e$g)lo%unr&-jq(XMj%-)R%drw7xWPW5
    z0-}JD{*0Lb4tzX$YfUc3%y?g~G$DFz5(r*zk^8A)7htxf1cS$et?lR2o=<qO4I3!p
    znV-gl|0`{z_iV)@Rmv?i3+?VR4L)mb37C>UdEwU#y+)yFfS2tBTp4xXdJc)q%1aum
    z>1#IGEt$B|ltbLl_H1Ax$I0JBT-y<iCKsEM-|g;iK6hh}kK>nn_cz_BK7W>;`_k`!
    z4;x9wv}TBm=(cZ3Fv@1Cb#BeF=BBP_D@vamRT~wI--Sl`L}~nbl-WxgByG18CL6W0
    z^{OrPlWsKZAH3F)y>G`(4+<$v3*PI;fbx#?>I$cm1|ai?JlcWKa}2dJvnAvhs%%Z3
    zFj`3a2}3ouSE=}A3(>W&=QgG5H9@>=kMY`4t*sUyOGEDZMxkLd@v~A$3N@T-$lQjG
    zPQzik{})|v$#G0!lAsl(8KNJMxkIK|?6Rm{>8MwP3-cz|-{?v?M4e7oYsU<Hw`{8S
    zNd1$OYjwt<G>4CI)MqoR-0N0x&7c^CPwaiSSr?OZO(C+YGxG{c#~a7-Fx6&OCGcQ!
    z{rScaZ*|sXT@0D<KI<Zlr1VOim72S%ulaYX2B|hddnw0(1TYc34lT!d9r`CCeR*3l
    zvg@@}0lmq3nHAy4Wow8~1cU8XZumf<DLl4&(o~3YI87D6CCRC)th#<9*K0^*Q@7P)
    zPjAA%eEeamZ5_NEtpYx+*@3NMqRyzas!jo^YPj55EzCjPSJWAGwsUXvF;m?m8sPI0
    zX<#o3abH8^!+NDpUT$|5$K78H9g-tl?|{{3lg-5E+xZMN0Yqm_6w;*)7m@B)oxVIS
    zl_rE$MtpM}cStlnWsD2nZx2AEcX@54!r;tx5ucQ<ebDXDnl}@lU!K`)%QUvFj<o;*
    zm3jerL;2hCDa2%3cbAi>IVBc+7s*}l@m1Azv5{W0p~j5QM7Bs5o=By{J_9OrX!+8(
    z;*5%iVt2G^i_DsUR9XzxcGuD&M1|P(Kb<^<=ruL1U7tau*N#7$M^eUPvD(;|1s*jc
    zhX+>;RZ4<Bi$_AQ;xJhs1Ff__uRrak;lbt3R5f72A6@fDY=tax@u|BiW+pV4NaUy@
    z?cIvDtERer>9wK$&gW+*)Q?_hp)alz{HpCis_jG82YM;rsios-)t=Y1h#fEYmWgz|
    zU}9C|8`rdbd_^V9&(DQIY85VWd|z<u#kQxw#ob?^E2mI>7Ld{#d+wW+TJBgh*R@@r
    zb8g3RpZ#$CcCm5lSd$O_dasX<uPqnr<9i>k9KUQ2inYDP$Gpkcyk*Y5b@?w}|2~K4
    zuRZ?R5&leD?mf)OY0b9Y^I%`P=GV0D&DMYA{qbl*^0#kqwBt_B1pmF`%Yx{Cg&_Qv
    ztyFg*Tni2$)h4Ysg{*C)FJJ%5&u4Eu|C^%prl9=$2$Jnz|MJBriS_0C*m~bU-=~u3
    z`}xMt<E@T*&6vDwUxxPA(p)az_sjcV1nLdbWyP1<|5o?CEoWbDK&Rh+=Kg>9@Y5T2
    zxIFI{s*!xH#J_KQO>MmI^1u6x{386@K%7FYX-vOyZGS@$es9(Gjw`IUPI%9Dl+XH^
    zE#9<I-rBChb3gmv>gaf@eQ(V8x3+uLqOgs&WrMk%w1u{}RNGf-E-pR(%@;6#Q*%8y
    zAL(-q`C5s;6P$Y+$=;Bpw``=hRo|Bn-_s8m#<jkhjpMj`hBpNHZ4>m%4SZ{S-rm06
    zMtX?X_H{nl>nQXeaTCu~-|zZmdWVzQesg;hP`(!N-`c*`cKB^~dGXjWbcbC^vHdPv
    z{Nq3VV|{rKHUFsn_<f)2Km9gcns^O4%q-$+i2Po{^dBF#zHOh~BuGKR?S+6uL`-4g
    zd5;z*-T#4?{I>mjZnPU;7b2jH%6K(v@iD>_^P2DBEPki`-=z2}w%_FXSwa->_3G%0
    zLi}y}+u43IhwuPl`X)sGiMBUG!=Kw|I|094y-^k;rZD|GtFzzG>~Gs^+iyao2MCci
    zqh0jPWBVK5xB}*jgBd(N`6S?RUfVbA_Mc$8@I#;BkxGcH15>{nkMzxJ``g^6jb!#p
    za!kC8ivC_5Zumw@{>^U39SVI>NL~@DM{nPR>2GxV&0gE@wSDv2{x-LV<f44rzHQ$G
    z>D%^@&He*YIMtwUUfbW!_6?D~Z4cSL3DURiA=@{v?c4T{?HeL}+a9ug6Qpn3L$+^T
    hTkrqowSC3*{{hm?b1vZNOAr76002ovPDHLkV1lGB>XQHf
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsva-sprite.gif b/mapbox-gl/closure-library/closure/goog/images/hsva-sprite.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..5605741b2b964f2e03f4657891dec33b10656cba
    GIT binary patch
    literal 36428
    zcmV)1K+V5LNk%w1VP^pg0rvm^000304-yd*{`db63;(|n{}cZY;tt{e{<ajf6aVb*
    z|FZuScXxOG5}FjF|I*UZ>gwwM%*?{y{}TU=v9Ym*5dZ&(692WJsjmO-|GWRD)|lS^
    zPEJmQ{|*#(|Em9${}geO|GEE-kpJ+Nci#Q~Z{QUFV*jp&w}AhRng5Oci2h&~yZ^Rb
    z8UHggGqnG4fPMaM|Gxi!cK>FXVg3>A|8G~_|NmFsXQcoCwP3bj|JMJz{u54_nVEzC
    zXlVb75m5i>%m2&&>0opJw*O8X{zCssp-=z6gjWCmV032xW+f#h{q<7v{`~*es<%V_
    z)~f&i>*PwP|2*OU|G7i+qW=GePyagNrvK~xLKj05|4aXoi2qAwW@bbG8-4!j|I_#O
    zbN~NeQ_=YUU{e48Gnqqo|JeQ&+W(gSj=cWbhX3@k*M|T7_&PKH%>F`hy#L$S8~rk@
    z|3Us_h&uoO*BzDr+w&Ix|KH#h|McG8|Bc1}{~I#@^7|bz|MFJ<|F^~O|Jz^xL)QM=
    zRsZt;L`%8F|A>f)|EKrC!NIlS|Nn?FLQnpQ|1$sj<{23o|NsBq|MD~cne*QN*8gzo
    z|CZj~-i*Ef%>S7!{v7^kz5mo<)nWgQ|E2%VheH2D|F*sV7Qg??@BgRkw*NE#W;4>?
    z|NokD*Z)q%uK#8;|D1jQeE0q>cK>hx+yC^=|Bd3K_y1?X|2qHmng7hh|C97O{}}(@
    zj<o;(j;#JA<^R^gy#LDn_afH9|Ftasw3Pqzwg3N0!T!R8jQ*S^<Nt^x{@X(T|IGjY
    zxGe7f>`njK%>V!Ylywro|NpeH|Jp-~-v0kBbf*9NE&pSae*gdTaQ-S1E))K`uK)J(
    z|5{pF|I6b3F}|7qW~t%-LPA0k{w-(!rC*Hy|5f+@f#S^n%%!EJn_B<>^TQAj5dT3p
    zy8o5`|C#>}w(bATX8$v#|G5|cq(L(OS@!)M{=$m>Axg#n|1M4SiT~FBG=2a5FiroC
    zsQ>?tp#T5>A^8LW2LS&7EC2ui0A~RU0RRa900RgVc&%W;EC^R9Txem1g@+IkSU^A#
    zK^=>A>I~30prgk#1I~zCK(ge54ktUP>_CGhjh9|xddWnyK&ApXbLtGoGhC6MCW@FC
    zYGOjs2}mc%TS`D4fu{s+MZJ}$YSjaI4ro2#jH|P+4#JA{;J|}f4?xnY1>&cz03ZV5
    z%AJc%0JghY@@i?pw?zfN6#@rNK)7&%IEUlJxx=`zfjb5w8wh2<T_|^$8@Ox08AJmR
    zph5Eh!J{+}KBxom891;Hq}Q-x%Z3f0w(Z-vZ(G{ETQ=|Bv430g{hK&%<G^7HXMHWe
    z!D|S!tdOp-A;gFiD_+cqU?WG5Aw!D)JhOm&Nt7$ozl2F6W~P}nb;>W$(`Psn_lXWQ
    znpB=rrcg<B%Yar{wbfQ%f#r-?WtDXn5N55#7F=-6Wno-*;gweld-3&`V1gx3fnkIx
    zW@2NIK_*#b?o@_ZW}J2QS!kr0b{Y=>;J6xVv-LPzZb0TH9CE`2SDcZ@A(`B3Ln>(r
    zYt9*z1#}55<j{2!Wml0!7-{s~N8*uZo=GaD#F9(vc|pT_G~IO5d^m+epM5~-SJY8R
    zEya{m0TPf0RRv;&AXi>3$RJr~MTnMKeoz>f9~WY1mxg-v<so1r4rUl)CN8#^V=78!
    z*<~=s_}Pr4)o2=OtL?~|Yd*&R8sx0CHL2W@w<ekEl91TSB(A^m2Bnl#PG_Y<S6(O4
    zmR)AF5qRN^C#Fd!nW<7sX|^Ym7X@$&Cw+7B^i!Ss@t5a+d_E;0fdyVQXo3zHI+lZq
    zO4w*ykVYz3hS*fPmlhv_IM|4rCZ-sQDX!RJD5REpDvhbGdKw=(LK*ADw9e{lufTGw
    zYp}d-Eb?#6MM-6q3?-Z8vf6R^?3ZAUM=eR{rH3Ya?zL&txAhfqpHM>Whi89#LgnXF
    z?Piq~f(gnyZ=z@|nijs0<{N2Uly=xxz#$U6X{QJ$tT4kbZf2@y5ZgF$tE#aU#l=2m
    z9OTF!dmM7dbl)v3K_*ZC869=VsvHrs+jZwGv>-`a-gz|JjO}{b<{aOAa@L1mP<GyV
    zuAb>eH5H)k9@rJUP9vHqgc7FpmH<{?n4uQ^{<}4%Ac{yLr=EtrB8!uiZR*3Jjb<ao
    z6t~SI+&u!IPR7gMM&$EOLceQ~)5}(}l+hVmo!~5QnbG&pLK~*cWLE1q<n65qpPM|3
    z>l08xYo3%+6EtPNx}S%>^t%ckN^iYuX%y=Q=(<V8ZbO#hTBZo&HQXKSQ;d1c?krO_
    zvz;w%s$x}YTonN1C9gKkQyvI!6GG{MjCv<Dq1{lpLhJF3SP21Kb+EKD?=>rYF_YPt
    zXlAW5K~7EXSs$GLD%X>r{48_k@{fP+7oY)wE>QeiQ0e{`wE(IPF0Hc=zx*{jfeq|-
    z27_P(L&dv>X(mQ!LzVD`cN*ihrU`w3qa5Q%$2m3u5+W2~^Y)m><*6`lECi$qnS;IT
    zrOY}Hk%*QkvK?o6NqmVL$;@h|9y9?_ayuJh5sz4|q0!HNofFWXpt!$6frUX0f*R@=
    z#TK{d3u{@k6z#f3qA-eaj0&raiyQ{L;IWZaZVZ4UnxKt38q<z()FTN;s7wI-5pU3}
    zn;>H|kmeZVWZK&v4ZFvsiwwYUkR0FQcxb+rJT6T^Y}1=|5-y%xE`CCrU;XMA%KRlw
    zbcKo+DoywQAgN7AfLWx=>o)YS56#kcV0<9LB$&$z{tj(4T4QO5mm1c<Q5(vjV@5ZM
    z(LQXn56cUwGDS+#XG$}gdP^w_3HhAq$Pigr;wB@(*&XqXPm<-c+0EF)rkbpio$eD#
    ze&T7aPu5c^qYP9j2}Qc6Q89G@U7Z#U2)jykv33hAj01CrqTNkqm%Kz(@Km+796_n0
    z9aV-l)|$~`suiQ!(4#t*_XxQvPo&eVUi9{=*XdO=N`5n>HkDPwnSRfa;nZPHdw9-E
    zO75p|YUjA%Nz|kefSyKM*AopCMSc2DmHxaJzVv0l19B*VUPNfY6xvlXQig1qv0xh0
    zCNUiURcs(_<*05w>Q;}2l%&74t2*+ULcSKauP3bLHUD-jm=cy*7y&2DIwGxU<<xOE
    zyJSz>CocHesi^dGig=Bdho~g=Dobse)5Zd|DzZ|lS-gt?s%qM+3apm4)Eyah>Cm%@
    zHDYXRo2wwuQ37Ywz}kQSZxtNZx!%>mb#*X=0YKaq8rQ<eUEvE0DIMxGgnNbMQg;kD
    z-Dr`IT9h<rwzj1+$cD>j_u-`GhNdSbdd`XWEam9>7ZybQZ<SM<5bFX8%c>I8zO>Zp
    zVG?tPTkiL@u;nF0MMKu&-BuudP%s3Vpk*!>IKgp*fP}|9<_8ZKrGZ4VNmZ)cgD}_s
    zN{D<e;Trie@I-PXJ4BDQuw}`}sgrUq_Sxmig_NTxalH6*;%3up%JuT|LH5F;ROv$1
    zBVS5^w)5JO!M4yRyNFb0lo7-Ld?R48X3JgHa+VX!<yDKB%yW(PT_0TGTC3Sf8-`A?
    zUK-~_F0z~8o9>;h%Q&5$Gkxh~pL}}uGk6-slQljnyOb)rrtb5p{`_avD7{6!Xw5IP
    z%&L+*bxVdu2Fjqeps2_C%MG>$)mc^sfgvF8cpLcDv6gkd^R2u!r}?A+SE&n4b~2O&
    zE~X*A;Uf_z&Pkrc9_sm_n%KlGZ|!80f<|IdIXB*V+<4mP#b?t{nacJ?dUZtq9w-6<
    zcIhP75;0A7ve?c|8Q-C?GqfF^22Y(}dG~zYJ|}P>2)ys#7CqnoPWpv6=A6D2{8+vA
    zwGA8j$X?j_rsE`uJsw`kA8L!@nY?&+fkqVm(D*<0^SEXg&1h>s?Q~MRS1So9DZH3g
    zYYKFEi44}{PVcVf>3-;cB}!4Y2}IuWhCt#IkN5;1{qaa&`mHlu;atBN>f8LaH!%`E
    zt5X;2J1nO;v+L&;1Jyq7>|~+y#5t&_UD5h`mnp9eS|E#d+xwzweYcicf;BLVeedeu
    z8Frb=gu0jPUe(Tbq<F+@S@Bs`e1a8^Z^$2A>6D-RabMGP7{>6BAcijgot@=*J1cgv
    z9f0_?ye^ZTLKfpPE<2tMEnbhS-JYQ<Mzwj&-XCZC_H?IvmIiK^CT^Q{BIV|3WD^R<
    zw{BFXHWPDY)Hi_?Sb^1Nefx%e+lOHr$aDnvHys9N;kQVKg;<_vb;D*JJ7f<Kr+zG9
    z59s4qf3{9NGcK4DPc#;71W*t4a0@+%W3i_dJLYyJcRE%?HFU=>2lQ<o0wTtsDKE!&
    z`Xw3Y7I>m2RuWZe?iK(FV1ZspeHi$7wia;zCUCfBO@)+A&V_J<(<}>DexOG!TbF)x
    z=tDS@Y?(wvbV7Ej*B|I25B9f%pTl-qp?^%pf2l+-s1sVP<Vtq`A%L=TX@K#B31AGW
    zwRZ^Uce?OZ==LIlhe2wyd;;->q_}U7pbn>aiusm`XGVq^$bqo993OajhlCy6M0H-G
    z5pY;tcxDn?H-{~FY{>RlIgttV@jl)qe}zVW^|ymQ2zx<T3`2NIpQTDb&<d>N7LHhC
    z-X=?VXNe<XiI@mBx3q}{V2;E`e4C+e|0Nm{qd{y#ieG4osHlpoSdU|9hS^7kXsB!1
    z!)xK!h9x)vy<l~_h-aa<0Kiy>GysEl*nT=ACv)O6Ht1~f!+LsRe?9nvAg46?XNb-q
    zjfhx;5rU0r!4Fe2T2`Zk-Izd_@`NxMg_+n|=U9_f*Z>Uwcym)mYFcP`^5})A7?h8Y
    zimXVKsF;ssn3VoTa0LgDy~Z+HQi65R3%Y2KA<+wpLu?Akf_3PSF98q3I1})(Gjd{#
    zdk9a|aFG)r3FuM}fXIUz34{ew3|aA!O(O?VRE=<Vgev)nD#wI;VGMrpgyC3;tHo&u
    zqYIF+iJ_niuSG#+U^ZpY8Pb43_@I2i#0S@42|rnZK`E3&$%^$Dnn?MIfaDyS>5t!c
    zNQ;0a$)I@(SCCeTmDRO4q@Z;Qxs}1#0bq$v5a}jm8C1zwcGMtGoa2^o`HXUz2XtAN
    z2cUl;nIL%iSx;q>6ViwS1S$M74+9vOgINJFDUJ#M@CygXT8yEXcd&`-IT`9`e4iK^
    z_)ukQQ&F8+fuR|ipn0FHXqws=iyfmH92SCbRtgZY2+Q)CEmMNBNstDaf(xJzC2^a-
    z$dxeYl@IBDHc>f6Pymxfc4;{Q#fcQuz>II1oX|Lz&-t9ud6y(-7K(@#{IDqeKsDQz
    zA@dMB+<97;LYOeAY2;ZV<Q9DAxtKQTm<-bmnlYI%A`LTAnW7;LJaCyk380F{nGj&5
    zS4w?Y%9;AvrC$07{OO94S2wB22Z1G^3_+k4(3%f{pmTOuR4JPcN}Gyxn<W8^yV;vy
    zxh+C;6X9Tt##oU<k(MU#p&|N*fLNl+*^yQM@sTPjlG7;%PUQiuU>4YDk|=q5Zc!J0
    zV58khfG}x^;P|7Q!gn&K3+maJ>3A8D86%Sk0%ZUiPD&b5dYKw@4Xcr*R?3;XTBTVE
    z0bd%dLm8&}*nu6>k07XqYO1DddY}pVJ#osQR;dqRLJHF&p}NU}5T~aPiAjJeoIpdU
    z#py|skdcX+oE(`4vrrZ5N|$yil3x+2VqpeUM5!m4joQdMHj1ONB$)mhs)k7z<hfPl
    zn4amGs*ULm-C%r^siY0Sq@e+&AP}Fcf)BUK2fV7Q8k@1aDy+dutV%hiyn?H^8d&Rq
    z5T&pX1iAuj+N>6Upvmy884;&)IsnxFdItJ19@e@5c8aaq%A4D|9(&4SZStq!8WiFR
    zqDH}%Yx#%B$%BhZh{f=c(kPAcIxnr@0F{c3Xn~C=nW;$FsdsUsd6A=hp$gs^s!#X}
    zmPnoh`wQflm<M}~j_H^S8wyD34i5XOmFc7udzrS%DkRIXyh^wl>#<)7vWiPhCHr*i
    zpt23I2oAxf25O)bkqo#vl~cK(4Jr~jOCAu)v$)v^+8T^7xTo;IV(?+Kftm^9LJdNZ
    zw1>);@=&fz@w7eY2GF>!%*mWo8<GxSwF@GtSWBtKi<0;12Qx}FoI18zV78X>ui$A6
    zqB@=eJD%j`DYh`AsM@^-&<*VWiMI;N84c?hOzNbS3Ahtwv4V@Sg?pui8^65KrArX3
    z_4^2=DY79uJteD|*>keTBDrfizzAxfF?+dEDW?T_23Sd_XRxz&`it2bwA{+4>IA41
    zIa#nfs32+rlHkEeVY}ywu8PLHR^hHw8=b{~m(C!m#2Xg$YP^-|0Wtcu+SaL6gAINF
    zJKXuamV&m@OTE<l3l-qDoC3G3wY_(+y>$D%-;1|L${9$iq~{9)eLEWJ8@QU8tHSXc
    z@ax5vpr7?S#$r5_OOU_&Sgb2VvRPoJSpdK*kg{@Y0R&39FZ;3xaKN}&kegeO1j)dk
    zo3lJi9=Ayf6I#0Pz?(Gx(3@V6y2<vZ82q|Lkd~BO6vg?$O?#Xrn!7l*uI=i(Q(LuP
    zaSts#ykU{GWHG~+dW|+rqdM%o+?k{EaJGM;3e!uyhG`6Li@iu}B1*i(+>5GCys*Tq
    zw++y+5BsoIEUQ=?r4z%xnhC~TJk4cH#`j6aXl$%vIz45I5GN}gkE;+VOUDD;ny-1z
    z2tWmS45u@zzzyuVA_2joy8w-#o4k3?V42SrtWJMg1g<N|lw6zw9l}acu9ASeoD75}
    z49cQB1@UUiV<82Wdb~6Y1ev<L%-g9qD$8d}%c>y6L#)fz`?hiW%Zk~(x&Xf2V5AEh
    znaDhueQU)$kiO3UY_ZUctJ9p*zj4ivFvj<5#y#EB+3cU&d_rt|4d2{hb3D$iY0j0K
    z$56e;eJq=2&;~dQ&ks7!hD^_;%Yuvi)s9TGG||uh4A6yIs7kw*CQ!SY441ll3vQsU
    zfbz+qoWjKrym%=GF6;m?thEzesW!|HGm6o#yu&*j%Rk%#{|eHg>bAcyu$^KHpR&D*
    zX$vS_w@@s;E`7|f+Syku4Ov`xIIYt<9n@yL1Z4cvs9nE9-P*rlrkeT9kNdbO8^<im
    zvQ7QeF`EpSi@EIV&IL)WIXm20UC8u2!S;~I`P?31?S2?s*03AU11$-cyq4zb)(L$J
    zp3JTcox%?PUCMjS*IHW^tpM1U8rZI^(b$04v)lq`3&bA{(ri1PMhv&T90%L`%aq-{
    zjY-T<Tn1Bo%*ouheH)*E%Ln)X&CyKSIqlP^4b-c>+Gf1CX}rI$L9*+B)Nd>uavaBW
    zoZD+!xxAgnz5PAVpt)@zrwoj<SP7vIEV{Q@&s#^jEU4UI&D=5Z32oxfu3OfhFv*i#
    zsMIjsO)J7~u*q-z*1SvCb?poeP0HZy0K`kLm1+jZ+Y0Etyz|f&I{dur?Zas6-W8w<
    zwk+Se+_v={2gjfolFb<TZLlc4(*E5I##{!-EDd;W#RXo|r(v<U>b?x##Sxy`Jssge
    zy#y5huFYy3+h%&QCtC_x(BY*Zxr>n72(Sp~tj@j-ty5i)R&CY8jkCoqy7f%s_Rt6o
    z2@j{6x~cmKzuDtH^W#81<g^Z=)LrEAumwvY2}fQJZs6;1Js?Z2uIk$44*lJEt=BL-
    z-d0}T<!#YkE7*Rp<@mDR>pjcZ-qE%k-$Kl`y8PJS9tXWF*^80il^y5bYqxldq}}k+
    z=S$!-4Y)SV2kU?un;GbWKIn(8?+>2nM6Jdp>*#O10<;~+ln&y_P`Pz5;#6=EC;mMt
    z?&-3r54{lT5DKBjZODdv>e{N@`ONAvq2n~c<3;cZ?-Sk9ZR=|}qPE-XNB--<9@pCc
    z&E2zr6?H9*P_EZ4Eal5y<&;|9HT<=MUG39;*xCNxWM1Zs&9>l9-}ddx`0eIPeAy}e
    zz5Wg0$K0^ePzD5U#rOc)@~%N`YzYsr8)aYiWN-FqpZ0^E+HGIx{yx-<zTvZN+j4yH
    zA)fG;&f6uP5hqTw?wqp~|G+q#@%FIsU9IXL-^d{kA0+R(KaTRWF3@aA4Qwe1w#(}!
    z>IS;2<ix(+H}B9m-@+~|2lOi5%?|Wf9_`ADFI)b~^T6KjE#@E1?cUDx;QsV#elTxt
    z*$0cNa=z~C-T>{M_3w`7d!FD3&c5}|2WWrxY7hNsU-oV9;BSxU`WyEhL+?ocE%1;o
    z$E7goxE<n{j_D)L+jY?JevjgEO7ZTm4>?N;)++yn%z}nYx{aXt9Uusc4Dzl%69Dlk
    zQ>G#WKNUGhjam_f4JA>NG(l2Cd0XVkbAz#-n~iR6&HDH;&#aNfW)(wO%-Ow_&g8h1
    z;~=IP2M$`<Y~`WVPJTf6^<?CSpPzYth+>;JPYWY0^IE8S`qY>zV^ypEGWH9r)?abD
    z{<0M(PFP!a%IYe6Mb}zg-P(rg)-6<-Ze`q+Dbt{gw7t?mNRx&S1U|xr`K;r^CyS*A
    zj2$<I{Mhjl%9Sl&wrokWW=NeoZ|3Y75@<`JM}sy^IyGw1q!k~|vMzT2mQpKf-(F;4
    z(QXUL79m)aOgI$;$?6=3R;O2;HoclR@1-yL8GY1AogtNdL3Vqj>^1ss&mO$-@XULG
    z=jD?ZFPQ{-DH!fx5&8=oDwJg6VMIw4<w>eXl8iWVgQHpM0f{69OG>GjDV%wbB{?RH
    z$pI;Ba=;9oc=CXsR(=ApD56G8s>BwYf=U&ts4|8QtG41wE3d>FORTbPG>fgac8m+I
    zy7bBmuLb}k5-@zE;lVI`5<`r!$ex7kNzA6iOtscn^UTUDvHXn7(>7aev%{`gN49lJ
    zq3z7t7Ab|d-EtGow>axCj*8=$Q?3{1^o%Y#>GY{CJ3+VWF1$wnz$4E*cxXs3z4hK3
    zs33$UweLQN^owYV{<a`Mo_gdtVxx|3@n{wW6>KmimWp}l9tthYP>!55-04GGg&Jxo
    z5s{jQ*ArEIN<|i{a`CDdxr*bMWNXCH7G3UGOD!JbqLwba^5RRHA&ESau)qjAjL8S0
    z9JgF2ogCK^%v_*y33gr3vP&?poOjE5<qgf=eDSp;i^I&6t&}ruE2R_*+I%y(-Qt|n
    z&N`A??zuhx^b<RMNV#}B?>7Dp(ecVt6g~CY69^%L=$nI~`zC-0B8fJ==m?Dn^vI1_
    zY#!-UlN?0pK~`Iu5Y`K6qP13ALHr4bqLwDA#CcRfF~zC>T6{69tYVx|S!J8e(OI_K
    z>aj<<fW#}?BD?*Thjo?=w_I}3b-Ucl*majmcH{kW-@HqsS8u%k22HVbt|=t8bqFq)
    z;M{nVGdPCpa5%Z-+F)+a=cua~V~qtZ^w2{;HjmNt1VPU|k_mE;zJx4odB2JJ!w5i{
    z3#>W8Q}aOSB$Vz^X$lH|&oFp1G&G}D50TE5L|&arN>~-AX3@o8=s32kC$eVtMrXa&
    zMXk8ns<y5lg#@fGBfmAQ5OL#Xzx{RVo|`gwy#)N;y~FgIe$Wsrv&<FFR8zs&l(2;<
    zjA6-u1_C6f9B5chPts`vpd^SfL2<`Bjkq9q8j&dfMV;qS?g8O^4w5p~wT?e90~JPQ
    zXS0vo?j*XK0!rji43<bJXi|^^(K2MT<9!H3L1CKHn3p19O^s^P%L?_la=q(qEm_*@
    zTKBpKHfqJiTGuL@`2w>=CGn3B?E|CR-UYwPjFE1X!5`oBrAB_G(T#m$BP7PuKLFmQ
    zU<f0i<9tH};m`?k$We~Op7SR{>SF;HBZbB=*s%+8u!CM8-3Lo{5K1cK6ZhE<L|(V4
    z0fCBkHM_+!T9~t)@r-9PY!&c^mLU%m?PxyCRS<>fycW=5MN})+^s3htt&N2&w%Fbs
    zq@|WCR55&5ESuT5)h)t!aUEfV=DW^VMt4pBa|v%;BR1OzM{Poae`PY<9R=7XJQ`4s
    z$v^-fAGXe3G_VctbPmNNcrip)usay^+@Tz49@0f}WJQ1vB@t4>{mkKI6Um6o3e=GT
    zg>sapLRAb;NyC+>GFHReAy1ALB3(7DL{I~15vk%L=}}LKF*?RCdihK3p+$<_C?@!<
    zC_ZE=lZ)tUX4{|%#%hwWjAN`!H|v)_`wcaYg99f3|0m7?60mV`vQt#o*+6%~GlCJE
    zCp{~OPg3M_gCIK(Jk}FQ5CU{kDZ8YlT(`-J1jr3r_+)_+WHVEd@+2|D>q;c#3>?N}
    zmBVYL@n8ueKLF9A&3l^jRAEb8CQ&Q@CrxRxz_K-9Ru-62yn^?NSw&=K(WYoyTTT(P
    zQ!q*ss4hTlYT+kT{S~#fMAh5Bk}5E9k~5D6gsM~}*G>lx603lsU<I>-Pp`sbgB>KD
    zSk;rdfKDo{nAD^uUnesGG4zu+t1CoJ#j_Z)#CNQ6)$hz82M<W$CdK37VCTV)q#X8X
    ziFF=h=`dfD>hf4AUFk7y<WgG@Q?tgjR$Z`Y)AJPuA8X?uY1^0D(^gZpbz`k-B@Ej*
    z;_aJdqHS%<SwP1vf{%5Y>cgC4)jna>Pg>n-S21W@@Nl7n=kcdNWQ>pzTCyQ?Fd<vv
    zijgf?*Jdot?hCowUGT!>RXHU8FJRHjh>eD*Y3>cF^IEjn7S(r&GJ3)>v}Re$qQ$>4
    z1+ahzY`!-b=?w-x@PWVhfJBt5&EtAAo84?i2A`I}MvX9q^K1#()-izNysex*OogaQ
    zj&g0N>O0@5kKdwLxWsJ)7rtZM7mGE<sry7FpNr$xN%wX2*n&diDp$H{SI82*M})vj
    zp$p-#yhLjSdUwJIqy>e&P=>FtY&qX5>r#nN(8@8I_(aNH_Oh9s;%37<W@Vn4i_Kh8
    znc_TcH&^=sbu%q$vF+gbO}N6Z{kFH$NJk9+*|rI5+@O;|=mbK;(9L1Oh~wF4;MTLN
    z!&UL2Tzp(b2|}!5m2sy3GeM0^6I>yj#<3*k@DNQ+q{nWMM5soM-4>d<81!%uk>wzv
    z@XBFmQYde%TUk7Qn1LU(M%vO;{$9oIn!fba<r9nnY$oED*j(OTW_1oz&eD|GWd@A1
    z3t?aculdc?hO?Z<MdvzmJGFS`X4JDOb$*e%j&~g3hIhlq;CvVw2$bBr6|HDSXEnHj
    zBCd)hy+Idu@Dl)6PrwJ7R)lAAlM?}T#8r6RADh}#Q@Cr!AymVT`}MEILodlcgmU%*
    z@uMt{?_x<>U+U33*eXrdmT~^&Vk&#4HQmcz$_(^Ed~4|HE0fZXF6Y(D8QXQ1rqiK5
    zb=q&U_ItZ<hM8Lb3M)9(3d0!=agsv?Lo;XWSH<dY&yIE#tDVwqhevYXKI2Shx(GPN
    z{=pT#{Y~~5u5X=qsZX`tDU{zqyxTY`S#2RRNP%dGZv~T?{1aNQSBQv>yvyh7>t9$t
    z%PQ3}$~p$KFt#h`ugv1PxDbY8!lE}IGieYyd>FbklQ!0~z&2aGHtQ+ai@iGow><+v
    zII_B{1GI#pf&$_ruS1}R5xe8NstE$FN4qyJ@EjIHF7x<=NRqot;z4T2zJy!2*VzJ#
    z5D)>0t7RZHzk8wbW5V*Qq2Hm9_Iow>E1vmdyvFk=GH9Nblf0MXKg&}o0K7RaGeDi&
    zIc1{@BM`IyYjHpbbi)X2Ce*vYIZOl$WHS!LLk0`M+nYVx>kK-gtv|B@+v>fo^RRVW
    zH$o#mu!9CfL$nbiyBD0N<-<39(>F@%H-D=>FPH=OsJ<S=zU>RbIj9IpfGbmkINCV^
    z+QBaIgDAjbLf+vWDR7mJYqjxuzr>3_`J**PP&xK`8b(ko6?wUr^S}NJ!<4dx03<*%
    zG((<Sz;0MTYcT>g#0v);J!vR}Gdr^h>>oP>#|yN>JghTt&_k-bA9U=)bo2~z(><)i
    zqe48vWca-lL?EzZ!FF=NMWeUnBZ3($F1GWxf1AFztG>CLgCE>JYS5G+^bfd#$Rq4K
    zzk@~pCZr+X@twkxf>z6+_lq_8+r^Xn3HwV5EsV0oLdjzEHOmt={0hKj+`I!kLj|N3
    zHN=Z*yv8!f#y8+b3G_(>t3x&m$~wfrp~OHJFh_HQy`-$OJ@i9%d`b&*$2$@O0V+fS
    zIzc|Nf)un9ck@a|JU&KCJ_$mF?9hjQ%nl+r6jGqUNVA8wYdcL8gioZTg1e-I#7l+@
    z!iICmBQQenD-a~au4SOeS!lxYlY=R^hbQc-jeIrpLOjGPxs&TfU<5{%BZK%VMml%`
    z&g4vFe1iWAz+#I@Gc2ZPj0>B@$($TLog6(0?8ZX)Mxvz6+BC`sKrN-@K&DJBr-aJ?
    z-&{v`Jd>(~$KB|?-pk5FL_}!l%0_gzeSATzD$9#0OF`iRfQ&S@gNL|`zJ7X?Pb9cc
    z$V(jC0*2hahHR^d49xYM1;Q*$i%c~e(5v*DkX8dZ_Zyz!`A9Qhg#M(=#w!t%%uHfT
    zNtOIenxi?F)H0YPLz(P6XzT_xl+c@GO$Z#lH`qoA96F#B%A)kp4y{9^%*~|~(R1X(
    z4^&4KZO3(x%~B`?7rmoFY?DI#J>>K-MNCBI^h)Mv#C_BUCYUEwus5_M1+}!qNW(;b
    z6NDw5OS-JSIk?M&RJes(IKD*1Q(Q$i2o)rB1Hp7pQ>#cPRLoTiuZ?U!^TN;n{H#Kg
    z<Ino@MPDq*#2QdrLIPu)uh7KP&{WXO(=VKJ(99Bs)0D>4tP7kp0&H|m3oWxkIK2&B
    z2it^H+bl;B)y)w#$92S16|FkiWKkF`1>zh;8eIe%O-@DB(XVt)7c9F75>iqq%d#ZW
    z>y*{(+)fwc0wrD2>2p#j#mipROH%|^ix@({%+fCHQtW!qCQP-(tVNHjPcubRHRaD~
    z?aykR2RNMvlT?KPRRtvILObou(Cj}w&9ay5Q_Ctt(+t!>mCy(+gKDfsMCHjv<;F&3
    z(MZMD5G_aEWU!>nRB=mDZ)4FjdC_$sPBCDUP&H0^q(?<`Sjh3p=j>7cCJ0MF0aD<4
    z)gskc45C4{%uYdwOI+2}Pvq5v^uakO&mlxvhYPicTt&FbQo?itLQq1(JWPyS%=B{=
    z9J<f;i#7cm9$xH4H_g_k8PLo`f)=P#JN3*wCD+kpP|G5R{*uW+y@GXJzzK!dcg4o8
    z<w<<NPz@c}e8tyE1&6cUS962_O$AQ2HL5aURDn6zf*Hh7wcD-qup1>b1R}nOeNKuU
    zJ0NWXRFF=6m`=3(M_KJE8f4t<+)gH)%O)k+OzYLk4OUZZIPm+vRYbzhZO>$lMa5*+
    zduYs!biZhoR%)%*H@((iR1t3V*3In9UqFX(#nYCA+H<X}bUnlW1r!F~jZk+zRIYW+
    zdF9D%Y*g7K+el4YwCz`<1c!gsvw)>A*_6$>{nUiLTf5E5?q#>Wl~_jvT(Y}Z!!=x2
    z)mXHYRmP1pTaCW%%vH&KQt|B7EkN0oZAi@3TrK5a&n3(+odtUMJDXL^0^Yc0<=HcB
    z1^k4}X;oco^+herOkpI0UF%l*LWeu$OsB=&(bTU$0H&$61#-9o625{P0Mx6MMqw}l
    z3EkR5Jyhd8-q_U8LRel%Em7!2Tcu3OvyEQ21!90*qk%2hs;tUT<-P6A%B^%*9R=T1
    z9bdt{SRl<<SWREW<pRca-&<u;OQTC(rC*b^UxmwGh;-Ti{x#PBg(#Xe%m6;jWo6d%
    z<5?VPzdBA`MrhO3ZCzoc;BD>J3#NmmZQ2dy;BsA1{Sx63rdl(^+N{mm7k-0yEneeA
    zUPe{k8?H?scHY#|<VyXAPX^^s7UfYU<x)1~Q$FQ^6$69))K*s5<D>%QtVf3>z9)Xp
    zcN*Ua0#YWx7$L1rD^^P)Sl=ve-^YdAT;0_$#><q|6qId9h+M^J=G-n_1~mp#!=wjg
    zjbqZ?Su^#x;Yrh=&0`2|-OM!SZKcydrmxOKhtMSCmNaDj`d}_IVG~B--(6uAX5kjz
    z+DPWf7{<^Uwq!=d<V@CNO4Z>VMoLs>=!SOahi1|L7kyD9cID$dP7kY5TCQck6<=KT
    zXjGu$^F;+<F49I2W{)LikTqt=&B4j7-21iPA$;Z`JOY=E=4w7x_he%McH_{6<67KE
    zkAqe_zT<JOR#o6*Kd#_E7UT?`-FD7Xc+TArrs@*bxj@Bgd}iSYh1W=qVSt|3O19)j
    zO#})cO0%v*v@YwkPJ~WoO1EaphnDNPo@?u!=vEd3B(~_SWa3zEVq12^Mg&~I{ZSz`
    z+*U;eBKSwePTVZ!0%3OS$K~SqZRsa{X_&@bnTF<76bCKk+-m;ZFWu>jV?rAc-O=4R
    z(>2{X4rgjL>Nh0=3O?sL734Y%*V=X3EQ{y=V*p`W0AUfH+RQ@X6TaHt{aqv2YJTQg
    zu$E!5CTO&V?z5KeI_zYGuI{9y>+II<Qnut(76XdTYu>Yh@xEKX)@XOLWhS85_10x7
    z9^8?3RU&{@#b(QsCT7RJ)yZbk$`;T5uH2Y*=KQVc&A#c*t!Bcc*_{sHSukzWE?{P*
    z#W^18*3M5nUR~JERx&VWZ{=`xrUR$0U8s)g4qnjTrfL$l=i)Bz<KAa}Zef3BZsf&K
    zOOEcfW^1#KZnvgXxL)Y(_VMjzRPX*&BM#NQ_Upc;<x@q~Hehci|Ixxe>{tEA#3luA
    zsPAEJUywa!WS-ypO~{zmY?*$mmpy|2GVjtOXisZqW15A<Y!=he<!04hZTuXEpLOjE
    z?@tNV@NMmI+WzpS#cguk?LXk{6CYu#Chn{@Zhcm67=~dP7U+RqYaE|+wyty^hF%Uh
    zXtdSzv;FZ;r|a%ESR&7Biyl?JCNw19=!j(kXkhPGA6zPi@5DaK#Wv|+#_#;j?=5d>
    zC{^ZVhH1?n@G&>@BUlDwKk)xm@L3>)DR6Vs{^`YxaMfmQGZ=@W#&bSCXAP%=3iflS
    z=FAX}YTY(h-&W+QMe#&GVMRyoc6aedhxC9RXd9;Vdara!-|@5U^d0tLO$YUVcjzE5
    z1S2N(Qy=eGMs-5Vfxq_KR$uS`SBG*YkZ<^wZ^l;a$IfENc5E%@b;(r%y!`lJ4|AEW
    z=`xq*&;DNoPw)lL=4@{8XrFd!KV3B)g8YnaM!5Mr*Kl*LU~gCFI?dpACgiB*1T8Ci
    z-kxW4KXejC;T!0N8(4HkcX6-w=Oi#*F$icepz(XZcO1v@gVyx0hhC)YbPfRcvrlEM
    zC-_e-^1V)YRoBr?@JfegawcEz!j|>K)_aQ==DyE$kmX|iu52(4`TG@lVQ*QJH~C~&
    zcA6F7$9MKAfOg4;^O%={aTscAS5pfgf@-~aJ>U6m@AE%z=h7c{VLN)HH+Q8^_j_LV
    zsF!+v_UGBR`gz5ANv8n+uP^J|-~HateYXyKxaI(VNBiOzWq~L7ye{?eX8t30*oFUI
    zcawWpcXjo)d%G{}ksj%ak8j4tc#Yrq$yS2>j@*(3c>o{z#II@3)>6h_hLtA-$9H3w
    zfAGpDUDd{XJHB=^sQGEldC&(4Rb&K`p+g289VB!RB7B1Gi9?7_bR2tf>=>tue~xV=
    zXUikGkXOiExp76<4Ph!(8eu8S2uzr7jJ!$m=BCXzh2DrMRH!F~L==V+Ejm=_QE*C?
    zasbES=~AXnIsB_?^(xk^TDNlT+V$s8F=EA7AzR7{+O$@vtX)eb2bH+cazKN&sV);}
    zOxiH%yO)VRCa3`aqbgjO6cJQJ7Bl8TgbU=zkOgs3BIxqvPlD!r`lMveoX=Z8kKSVX
    z^bymkkFajN?8vg%%LvV$MTp)k-1MgGwlT%GjeEj{OSvcI_;EAI$C+Vn=J^majD|2`
    zM5g+6GTOHrRER_&!zT+PPP9nzNk)wvZT$f$lH?7ODO0X=8B-?ynmBKY(b=<*QAi1B
    z)KF776%|uWHMNvi4LbNBgg*5oMky4MMV4A@wG~$mi*N`H6(HVaR~vfybs}Ij1ZG%a
    zi7~dAV~;&1S!9?AV%cSLZ1#zcpouoe4|AM0<Z4^A<{E6V(YB;*gyfbRZ@}FXTyVr0
    zhum@FNWq-{6wp0qMs(ImcO4@H4Wu0ss(hCac;h|v5JWndcTsv9{qe^~A?Z0%N%M6>
    z-%2grC({fx(UenvJn8fkfsGP{)Kifn7%5Z?LU<{rmrgjASY@fDRt_7!_10V=jyPgo
    zC<4~kU@R*3>WeVWNF!yG;drBFoNX4Gk3#x7Bx*(;>6&b_Ez_iJx<&b$l*3txWtY)1
    z=UkX(kTE89)@_Fbn%;Sb&YN$>IVXDR+1Xy6Bf%#hO7*?;?tO*QG=qNn^#>MEj_OMR
    zr3)_AZ&aBIJn&ZdidEr-pQgpuTWi4;*Crv_rI(4SvMQKhEw=b#jE>P5nG#%FcB>M(
    z_Q>o17NRKutZGIIi)67%5;C)HHAiU!mBPvLY?eJY_p_JOQD>dD+o7o@x8EVbkhl-Y
    zDJMo1WyHz3dAc`JeDk$ipP={6l*FO+^5>sV`|1mDRA?(jFxzZz%BiOcTM;V5qrw&O
    zU8hoP@roH6=AvSYq0;eV-h3=FjyU$X>t~+<DI~Dsm?rtKti4w3k~8OKGd(!}Hl?%0
    zLAzy_(}H<=nABxgw9!eg$q*6|IV}+<5KYa|dUv`P(hcOur?tEA##;uxM%ZLie@PUg
    zJfmhes&;~sTG}@Cmxcv5h8Ut1>f8>GN@|F7-NnTC7GFGAVj8>Hn2d)D;zO;8Z?>8L
    z<3Fa%vdgPg9`lm3%}g^rI@dX7<xFRy!vS)%q@|xx%UTX3T6WMxhNQ9SJLrhr(%|&8
    z+TAWAcREHG*wdap@h)q?(}aCsA}`~;fnNGKo}wn?yhcrmQZ;-^^>X;2w~Zxk3JVon
    zyayM$&}CxfGheIx_P4-sY(|EIj1MLQzgy{OM~&lEkVf-^(?~9VFSA^dWRtnu+-5fc
    zBwzuDBMWFLa7(5;-L>KfL8D1fX%<`vxZWhE@=$~#>G7ZlN2n*?^(j6o0Tc?2pow_R
    z>uWEJAy{TepaV_Ch70PK4l~&mPIW3_KWxhoM};>d?yX`c8jKRv7b|bT=!sANtc(;#
    zhM68+@iSS(ALYcsnlHx8fB)ND00jsfoC(JU#WBtrrGP-vO>0`wdFFH=$hrwm(2h&X
    zVAGy<3^{f1gLe|7o_d!xl+Xu-!doOvz-Bxa!Vq~pY2G;`Ss(>XWgC*ZWTZ5y&#ds!
    zlX3&1-QIFHD&);B<TKx1DA6ict&cIR^w<-l2$_&sMt+~69~Oahzb$ToaxT;5kuHZh
    zU(%zD!R#E)h-rhJ-2(^6#8Mj>h&nJuGlJWqrZuq%E*+rbY3TXs)c)YXZ-TRTzBAz=
    zRbtK=yhMfRWFfs^C<f&zPYm%SN<6QcRSIa80tae}Jvm^{uY$v#_S`4`TC2jz9xCh<
    zivXg-hB(lIZsMQ_r9>Mjp^qA{=moE0r9%q`8E-TqiWIHPjc`@V&}b2Irr~HV^%v3@
    z$i@}_``<~0nbI__(KrZP<~Twt+U5))I%FvAbhI-~*D39SdCcQPo~8p1-tJDE5Ft5=
    zdQ_yMq$R>gq*IMhD6k!`xIC#UfVRrjp(uB`Tvd>Q!a7#yhBd8dy<R7Mm=?HF;S>UW
    z3*PdUH@m6|uPWjzDio^9z;fZCuaxNf5?fJPhNf{F-2!AM*U|lj(O8%@X)tYo(wyzA
    zz*1NvXqoBEXQFlps-+`q?dV!?IYbAtr7cE+N>ttMR%<Ci;Ub0q!Blw_ie4r*ZW5@<
    zsI9Vixh)nejG_A!vqA-j*45`)<EFh+Y;SIG@oo<8M#S;fmAojT1QYOT81)jtuZwxX
    zVpwUoh(gh@h?B35YI)z4;kToa1u1J>ktA36_rIAH@Mb}0;ADPQ!J++GX-z9M2;)@3
    zI_;@!xjEaZQH^Tf%<Uk>X~GYG*d-%&>WN#JVycRTCoO)oi@8eUuvT&#G=|EJam+~>
    z_!BKWhHEH;Oi=M{g2)IJOkUHw<c3C>2vQy_7pzRtiYoSsL0Z|$^4n1(=@QH_upF6{
    zL{b22Hq9*EnM>QOK+%Ra&T>xeH0#`2Z1S|idgg>5gX-u1KL@(Q^C|RiMJ!@Ow@}eF
    zIbMIp!qszRmEG%ZH@o90R!eWAlJlN-B{Qu_?2$zVXz}-UyBFkvgO{MCwo6@$Yz>Ry
    z70HERG9`!*@xU6+)l-IIWoE5}`QB*bj|<WkL%!=Hh(niOq%3U6Oy<iHJAgPF@I6Ry
    zj{?6`*=`)|IGpWZYtdP0)x7ggWs9CYXIQ94*WDm_*I`W*8pMixdWnTQ1LCIm+)0?K
    zSmLeqt!sVVmcDntz8+Q^>pO>>^py6}^69kzncz7nwZRjfmxVKd?tXE2l9#NQi8tBR
    ze)q=3qbO_nmdxXgQv%4lyhX{CoYzPA_2XGX*<}R(+p;uk`G9e@vz5;Lz@E3cXorw<
    z2A{U{rRSEkEvQqTTDbMIMGq&m{bvpj`UXr7_abvI+}#t`c#B>%*00X>;d7ndU#FDu
    z$4-^9n;q+7;j|S1?rBeV7l;Gj#M^T~zEaEGu69SV!yj%KC-=S8fNwAQ1rN)LZoKe^
    zPg&wc&iKeee)5zTw&gL8=FQR^*)3J3GA-Mi<s6ulmbIwPb~spsUDIo^o`uC0ZOPs@
    zg+bE!j<*pSi2ar#mDmh);BZl0)m>dx5g!S%Ti6-j30fNSHJ?}{0owiB^nse&WgqvI
    z+CeGYi6B{DC=_}rl#@*ylu4PzrC+SAU;DBDUwz#g{3#yec^Sza31$77%FTx5l~iWA
    zT;{D5=gETqeHH>STeFRxvz4BM)zP#8oddQWw#6PB_8ABvUD63!d@!Bu;oj3}LezoH
    z)J@&*0iW=dAn~Q(@kNU9tzh*ypB1>E3#LUg#GrSb!oXc$+#%dS(cQv*-wrZ?U#Xgt
    zL0R9u8sG(<tQ}$dC1Gf65#ohh$$i<cLEaHe;mQ$$Wm(}#q1k}R!WPaPg2}>XjROt{
    zpcuwk9BGFFk^v0-oa!|oo~hx`WmumbU3*YqNR$BYlz<5_9f&#IB6%PO`d%N3;8gVh
    zAP%1(D&M@Rpdq%+)nQ#C!eAp>0gv(jSoK9B4c4IC)s-dYV3Oq>C!&I1L0J&~-T4`g
    z4;WTKwqM89SL6Hu$c5bEd0ELlVJp7kE5?T9RpAv16A7dgE^;9k9vCkw6At|1FEX3x
    z=^PmnqwByxGQL(%Eu)34VH+A<7~F|8lEY0}<2Blz?PX)`MV$vG!5)4i){P@MA|DDO
    zB|4@f!LXwkaGJjXTqAzkr_p0Q+G8cwU0*C*4P+uJ@RdUOpg;QKCtBQ;`2ax{WX4Sq
    zDI(s+F(iJeB1HCD<hA1cxq`|8P)5=N3EZOQ>EcK7q8Hv==mleGncfg2O=2odHQZSm
    zx?UTyAx$D(P9Bv`@?<t@ARc!ApwyjMRP|v|o`X2D+h~eryp`rT5aPUbf!IX~Yc3^I
    z3d}lQ9rQ^hJl<vwKq4ew-&Jzo!fnDOYGq$^CHUo?SpH+*1!O3mrQm7Y5h~<H<(ewa
    zUqnu16yD{`Se7g7C7Nxd=B*SL&KwSe<VcdFNgAeum0@B|&|)G1OHNoGJ?3o9=S)7M
    zG~TDu=_DOuqfgGjHgaYM3Z;O8rtp=ffu1I5iY9Bmrt&dp7tGl5#iqctU|Bq6Swy8H
    z=B7YJVyV5IJqBlRb|rBNMp&wv4<@Ipy})t;UO~2~4=BNlw&fB2Kq@li4^SsvibE@2
    zCoF2`N$o>0_2vF)VR(A~<bjo^Nc!S=>RfuNWP3^&48-S4Lgt=DW=+=QAJimHP9r%`
    z;|=KN9R8Lj<Q^VwCN~ZxAEsFFjV7C#rkl2@r7b9g(i?-$n1l|D^ZA>F`dd8Wrfza*
    zZ<d-h0H;=lC=-k*C6wqt`XfLpXDFg&tQjPX%II6BVqDtjDrV}AKHf6KKwkRjUIvhN
    zZe&M-CrFN`NCsd@h9OEeDH1HEF@8ssV$&*2sT$H}WF8%UZmE}AqpxD7Pi|mm2Ix?J
    z<JE~HvYIBcCTp^8!-BSEoH}c3-syxY;+{s|^qqpX`j}N>pFIL+!db&5a^<0xEBK|?
    zar%`v1fhzqC|bJz=%i9A{7I)O{=gC3Xs=mkr^2Ev$|5~{!AY@#sCLe%f~QBWkzkgm
    zlCCOxHmL$8CQCAA9@Q#L-fCpVUQKcWIdFoOR_3qHVNZ^!$oePAp6P%Rs8#jAvMMXH
    zs;q)a>x0r*7tHL;-W#=M3OM?k3u0)W=BCeftB2O$pcbm35@!vRXriXqKQ^kAJ*qdj
    z=t0Klj8>|RVk*97sxHNV);^w?;pL~&!*&J`kn*Ld;*584p}}(Dc$Vi#D(RB8Dto>v
    zV_s7=fJ?2)C$84#m1gOdh5<GHZG4n~CRihXifoylDY2fcAFiy+Hm=G#F3rwt@=0!;
    zZo|!D3f4jYCARw8RBo%!?q>D{t#5+sxN4<{5-0d2?F%Golf8iJrs%r5>(jO<rBZFx
    zVk*|^sE%^2r>Y!Y_G{SE!#<dlkQOhI9_-q(ZQCxX7@n4c9i!Z`<a<u+POXCL#pK0u
    zEXN8JPVy}UQeX-ED&Y#Q;fkr5BCg`9X@Nd2`#!E{wy#;)Q{*O}v(oJ4UJ7kK<x@sw
    zBQ`@kel9(FtLTz$-Hj_(o^FyQZK6gTB{XW2#qM&xYt+7|)K2Z~GNd?M?JDl5)<)s)
    z;w9Jy5Fw1MzzVDlu!7l|ZATs~FD7i;GVDph?J-7d#5QK#+HJ+sXO&837;JACd@uNR
    zX_tQg>6a32$c`z=o^Rr&Z#cHE6F>3f&hI%!?)^dtZ6-lfVrVlo;t9ax&j#5r1lcAG
    z!?zN!xL)Gv8f{prZUZ}TyUH%SR&e{#=mp=X2A5n6>~06ULhn+Z*AfE24zKVAYzmV=
    zM;bEny0CeIVGKK4^v0@tF3k;7EKi|8>shi)vSANza`*ml-~Mg*5;6H2?#OPY5}&LL
    zEE=ahF)On#y@^%iGH9IIZxuVpRPF4}@-IC8>7VkZw+b!kifaKMsv0M7SpH+gy{_v*
    za0JJ3yw0&(UT_9$s*ZNB6jIh7YbPL!Z7>OJUv4BguP_TIFWb6p&gC2ov}*K5GCD;6
    zDLUNkJ6|t-qJWiB%_ajePJ*(phVS4OuHlxf$rkJ4wsI>+G0hru7u2$YC?ZtyZ|7<&
    z01sRk|8l9RK`;w5M%REw6Y~Nqa5B5@GOOqtKP^c+vjx{^)#k45S~DNFwAX^|b_Vhw
    zdo$UBvkf5fIJa;)mm~}{TRIQHF~*tPLackXGdk35ty=FTw;mg?VN+`|$Ks?XPqjYp
    zb3c>v;iB(QrZN_*>H8A26C<=N*YC_Kbc3XFLq}gTaBe(8H0Xx07?bgb66zVBaT;qh
    zB^+%>uP$G=?gOvr?9wg;lOi2U^Bw20HEXR)do2h9>>$%a3D@*ZPqr-Jv`3Qvv%>Q9
    zIsbHN4Yl+}vO7;QI-o#LS+Wjb@(yFH4^K5dm%|YAv*3<$DQ7j}rt;LK@>3=OShI30
    zi*+HIbyqNS7F)quYpYv>E*QVv36z>#Uo>!Hv^CT~bZfL;ceD>Gb4W9DNt?7=M)U1X
    zbM9L6D=Kz2<K-7@Gmw%nWZ$$+8*<tvuOj0>Plr}diymhSHBf`LQNOd@p7!;w;Zt+M
    zQ{QuzdU8JGbC-rPY-4pPBkmsFwkkLAZntl58()RPCU9p3TJNt~%fLf70|1{ua*r`E
    zUvym`Fm+e=UbivRj`X8K?O;<aN@MV0S2IiZE@N+}E2Q^K*EC;Fwjo>pw%RK4!irW;
    z|MXA4>Swd04FEtuzrXW$HRyo#j<$taGCk9?fJ5~?1F<KUfZv8MY{xcMm#h+ZpbTvF
    zqD{CH(<}pwbr-C;<kDM)a|IU50G-!4ac^;3|EY-Qz(l{DTwAn>*R^z~_|dBP0)sT7
    zyEuymHjKU~jlQK17<P>#_8xmGj>q(lkMJM^DJ!r734A(x<6@8}GG^mI3i#rDk3%{m
    zd1u3b^h)nh$NJqWwQ0|mm7{=AY3~nT>8?vPmvea$d-+v|`IwjaZ9{lhAA-s%`?5bS
    zv-iNWxB2`osG7fdLdUsQ<Tjm0pND(6al3U6>^WSM@t+H7pxgCyQ@3?D|1i5Jx=2T`
    zH>d#APO77OcS>t;9^1HLTY8S0_rGpBWC#2=gY&2#>?M@?FP^%5tGcR})(itR#4<S&
    zAT@tmul1TXm1EmeH+55I`L2KbuX}lHgE@nftUuH?grl`?GrO`sJIgowA-K7lN2|2A
    zd9-4?R-kpAcl#;Gz_<IXh>N?Q*PumP^ogH1p%?nPyL)!Ou?oztycgsTIC{OSK-Cfh
    zLlVO(YCWYVcE5jtE1WmLenEP3x~F@3dsntcUV=yVLwTlv!YcfcH#|@qBT&Ek#Mf;#
    zBy~JfxqoxQt;b%+bL_`|d_H%%$b-3TAFe-a=E={1v6p#QLuJc1|2wq9{N*dVwAQc8
    zN9#7oJkDpu<nMgXpMw8>J0uGIM3b>6RP=K{_t6h}iZ6Y;CwlC@z|(Ixys7}C7xukx
    zFdplBOJ91}hyB0vc&D#73V(WKBeG>5yduN>IGj4eL;R}Wy?eg;QS-gU`}b)BzQzl_
    zt`mOY?|R55KFJgN3_QLEN<QRIzHVo}{8v8xKkLk{xtl*L{$G3OTZPVZJ3us((Tw0J
    zgQs$wKzJfYj$t`GZ5p;|lbTJ|ELzjJkz);yA1T$KG?L^CN-s=$L-|6bH<c<}s_-#W
    zg_4;{e)z!ogQp6fKZyMhM@(E%aWRl4Md~VsE2mGt(EAc~|6f&l`{=PEp_N3}uChpg
    zl{Hq@B^<JFn8kr3#oBQ&aKLyAV+dWlNa`Yi0izm{YOBz#60BpD4ittDqu`{N6B|yA
    zIfaoNCi3LTCBk4Xv3awm5}q}MP7_*_KTR`E&rmHwwTv0AWh99$<4}*<w{7RfeY;DS
    zZN3?F3C`fR@7}}zC0D+jd2{E_p+}D{TSj&31e+Z^n2`HJiQgkuyr_m_M{6HFh9r4%
    zq{)>iTe5WN4W<v7GiT<+=_BXQpg@Hhs;C%_QmQGYo^t97SEBmDs(n~tC9AEvvcapc
    z!YYfbwAShnhjH3^OGLZsstX3a`0}e7bgHR_u)+#U|7@`t9fQm<$}Gz<v&=Z-Op|g#
    z`;4^EQ1eGMBVTLnHQAW7?Y7-?i?X=ih6}E@;;NKR%PqO=ayIKu8At~05ITjSGaZ5`
    zhfN}~$j$P~tKmHK)GLX-_g3=egP2_M!5jPXyQ!xBe&R_Spp1%v7^Dhp%FzX(a%CS$
    zsj@0U2`}_YmT1Nz;Vcc;Qj4t*;hKv?x=`GUFBn(^Y_P!+Gi*l19J`SX$tu%v#~ynG
    zZ8S}OEiE-9S4$E$>YhyYkY$<Gt+!_xsM0v#qV1x~Yq7<aI_t#5PCJFVbLhK?+^h&A
    zINLdIPLM=e@6MCniw`FH=%a}yB?jfMC!T^L|B9$ij%qY11P^48!KtLG3M&Zdp^&Q!
    z#kyoHv(QqjpS9Y8ORkOgDseBp`f9P&z+6NjM#WG-*~Z9h)m1Xhc;pPonTHhiw9<~8
    z!ARJaoQ>HhoecWtL!^E7xM-_=Hru6{evaGf1UjXlgBUV1+=(V?^CELELKi)d+-(mf
    zc;jUWruy!??+2TB;?F1l4iyLBp$tswi-HwAxYDXF)#~A|zS?7A4MiPwRE_5{F)vkB
    zY*8>6Peu%}mDzACAD1oDtQ?s^1M){;L(Ao~G*HV~=Om1+E(s@x2D;g0kxm-gYOU<8
    z>DzDLwp;A7Gt;1i7<$MPhq4w=jf}{H|5KxnDrvXf_1wdAPqSZ`7wtg%bNeS14)yma
    zfa`WNL4zlC&_RW<>O0e}1Sdgo4g0AWhl>x7D=v;bR<#;dMSdK{!~obB9aPRnVYt<;
    zbXAVbY_0?h+#Hb7@R?uz$}~no(#}$ctk!LCXtAr^(U4{|+>J0>UxL~|yk#|To!}wE
    zaG^xBcDzlDU?a)1M!L?^4)j!_P9v$so$!Pb6;SVNfVvNVwD$v_^y^S_TU3EMkrX>P
    z$_{y(N`v-MDONzpZ|e(7`y?g{T*!|@ZOPdE#<9PSF^(_$`-@-#V=~F5Y%wUP6=d?!
    z7z1K13<_-EW*8Vj35td^rqLN{|5!5v*0@Fl){zYdAql(K8BI8oR2m5}xuvHGB!vc1
    z$eDuo!WXs)dFMjcx|-L+AHFMyD;eV1>Lrt$)Wj!A<Q}5@MIc2XB?JSb#rO<1l`NvK
    zZ(Mm8r`R_*vzP@JYq?koKol2f;!jo8Y?0$$CC8K@h7Gi`V*wH9G6jzFj}QD@2_~os
    z(FyC2tTECeVN;z*Lehhjtmh>&nNO#2i+2T4hzif-LWm5*6QryNd2DD68s3lzU-JkW
    z!bS;pjO_y}sc7}gMlYFc6p8)u$=iY=Afg;)QAr5_Q^e<rDlRCC!c^E6Bea#jL5x!p
    zvng7dNx%BRAO)&2mHw{z|Hj9ejHq-pYON}FK*@wN440b>IUk7527)evgsk8UNSBLt
    zrtUSgndb)WY023|OP^(J&L&YvrcVa6g;-;gn-FTs5ikLVHPE3)cqoz@K-7oqsl`er
    zDn!A;lB2TJ-V&84Qn~GtmyLDBNmXh^VV;j~>YFJt2?wfXn&p}HgJw^EYRxtZbrnTT
    z*=Yy(&7^{JGERMJANyENsg4GMpsOIxwE9j(?v`6$1*_5SIaUUkmAJ`4s|W}p0#BOh
    z1kD`=Lp<TaVW?rc2Q}q&Z@9eX;q`gz`C;^e_{!?hr--yXtP%m_J%B|q2JG!+45s+l
    zNL|sTFTE6n*0%=||6C$0H{C48M)ldf;6ROb*^3*m>A%vJcAF_<Lu!$#RHimnGERl6
    zY$KRX+X~BqhkPfuUgO)=L93D>1TNBwOWf)-l9SC)?s5T2yisNiuII8L4sR%@8f<s3
    zB7rDGP4d?l{57x_eI<Fx3n(R?mx;v4<)Yk+l=wn6vJ1N6Fo|i|Okq}Vm#`^^avETs
    zqOqDlO|W2y%HY#Fm{v&D<E<{|S_{Lr9Ca=MEPjN}&y9x9!qRGPJ<Q0idR4^CIt~bF
    zhv?y^xUAM;v5Td)+_gSe#x5-AL9I(38qPIcIy_G$n2^f7j)bq@{i{S@P~^c@?`#}h
    zGJ2ON4(@g0|6lEm!F%5eU#8rRzVt;O7h|fGXn1AV?vsV!x-79~hS`i6>uH$}yee!C
    zRn4dErX9OUz{>40oN=x&ogcV4SO5tYeAe@5uA0@YHgX06kteel0B?E2Zdt-n^dSyi
    zNun{j(K2Y{K#-dq5qy%l&1Io<v*w8!tAU1^&Te+?%Df&^qSTZyHA+;C(IcyX<Mvtd
    zN0rzwf8{dDy`%+vn=-`(t#q=#26nJB#oztz_sd?M-_29tYyp=U!P8!_Y1f?X#w2yy
    zaHcSYFE`FU)|u&>%ka6?8TAVKqr>j>cUS41b)wCC>(L@wzKIq>EAd;O{$8;_kgMFa
    zGWWRx|BY^R-<oNMYXpaP-SI<5O=^qpeaIL`NkuElagLLBqqPLNlYc<#e?i&Sy)?P5
    zb-i+5yS#7F5H<;lJ#(9Sxh%}QL|dL|MxKWDz(J)p(5;PaZ66TMM)&sRV6pUYle-*!
    zPD7t_aa#+1cxV6K`tRed^>`1T_*NQD*zs*|vggy$06&Ftn?Z1cv+&>qB`7J67uSW>
    zU0vHvB@Zc1>LB-B$bhfjp7zvn!F!zJA;;xV7R7kS-&(%9X1T8^TUl0E-txqD-^`J%
    z^U`9oLXR!rqNkose@08eP>;>*NcED6+mMdJ6eu6c!2+AE&R}7-(8=~h#~*Ubf>>jb
    z|NLRag6{<BZTPaT_=ZE@hR@y>ZTU#(-vSQOq_4Sv2jN^u4GJpz5Q^b6V*7GThjgfU
    zQi-o1>WBUc4f-yyUI6`AZSaUe@RaDI+Rw@SMe*9Jm!hbbrsxYJ>;A4R%PxigFpquy
    z&&x;v7dVd%aUiEcuPs)Ar+99(eD3GgjLnE{51T5^^5GBru#YS-9~vkFpDx4r%rtUO
    zSVVy8X5a)AiIL<<1u3!XERhmh5NWm!24xVF%+AF?@%e7hxvJ0l6i(re#}gE4?r5mf
    zBJS>Zh=+b`?=Ehl%rEf9OYqds3YSRa)~mg`@YaNJihk*rs>lv{4a*<|*!b`A|AtM!
    zpt17=(98PaV){Y!a*hrYu;)mt0Ut2i_K*$o;ecN6^)4_2YtQLw4_NqYG;-kssm>9p
    z&gvRT1S;_!FVPZVunSVK1v`<1%r5PoZy*h>;Mxx13JMi{5F<8Cp|XnzVNoQ&527$(
    z@7Mv=%I^s=>H}0D{W@+63r_{6(8-3P6V@w=I${1yjw!0>m!<+4@z1`>aR1J5*rI{I
    zaMH_ka@ja5=bDWUMQat#OfY}}4;>I3`A{6;%nt!^5XT7})1dY`P!lwO9n}dQ<57`H
    zumooyOo$KpyzVQD&jmLzcIfRNM<^g6P3;EGAe{>#6VeBdX9OIs6^Ah5|C$F4b}amo
    z(Bl5;FH=n;0k0zoPZzDw{jjhkyU_6-j~HFS7*#46^N$(#k0xzWzxZnplC3OoVT@9s
    z8VfKR5pY#fAi=t^+ENcGiEbR5@(-EPDbcac5V7`d4_HKJ5#><?6{#M>QY?!P`AW>|
    z;Ei^E4=r)09zjvL22LR^px~$vE>F=1>9XPMveT@g60Y;^W^opk5HSDh76VW4qEHt@
    zP6ZUtB#D96won)w)BYm!3t+Oo_8}IyXagW58q5$s-RCCv;1V=bCocgP`k^PUQ8Y)B
    z4zDQ%9;YZv3mEe78zC?$S+fsa(<w1f1DP%=5%KnJ?;T5H5gSow|3qL!WuOG@u{Sjl
    zMZ=DEg0Jh$a_fxqcIr_%MRDMk)8GzLx)M?$vyUzvvO4W5JB?5;f2=Ptf&7R}FawVx
    z$I~!HQY5i3Jpm>JQu5Y9zzaj57*z@xxiluxCl+Q>Kk*Ykp%F6ylmOw7Eqrn`d#V~r
    z^Dh)sD2uWvQ^rBBE!!fra#*uM`ScIZaohk^Hq-I;XtNzXG#=-X>Ppn?Sg=tYHAM|A
    zgnlMQn}$S3^c4Kz1Z;Hra1=Uuv`2r`A=_b8?W!G&aMWax5-<S`y7Mn5$`%2SBW2Y|
    z%M&O_(j;+}3$HW=;voi3&JKX}G2!zirveMkhd%W$8uo$z|Hd>LppjGlv-3JrKtZ!W
    z)l?fx?@d=B1x#iY=+r?|?;FFBHN~+{nR4990bK*N+zPc&-SHiBZ}+s)Qaz|%?RAn`
    zRFX82Stylh{OwYeQ&TyW6m|6adQdunusVmZ2zN{`E1^hf@h^!CR-bT)(9cN?GfHvQ
    zBo$>>clG3G;R}oLCHDa)m2o~Jgg%+oCdt%UHS<5cj12>HKtFR$eNr3U6i)r;TkACF
    z#B~qJ)m;A&7MyZj2X#<6ltbYaQRgui0`f%fHEKKPMTe8D`ZZf-G+>ug1fGjvPawJs
    zHah2$;TUcN%=U&ltrAT2I`a~u9+u)*)h{J#Vq^6K|7LYhKvEY+5=zrkN*VKSzaRvF
    z6~2&>7D%>OzZ62i)bci9|C+UB|Fb`L^7Af%W^2}FNwY=b)LW}zPQA5Gvke%+QBU<$
    zXe;yrVPRcU_h^&WHg69E<24=|aYRWpYHfFF_qA$c@M^n+cAsxkZ<JFJ^7?pCF3I+6
    zN1)SO(NuTrVfPARYoR++trpmUVh3|pF?J-eFjrIZZ%;~aeYGWrl}k%@AF!Y*=#v$|
    zG(sBpaUu6*B{ybg78gFVK-bhwc}jD)bz3{PK~ca#!BupB7HId>bcyzW(-lxtS3?;%
    zY5gG$axV>-)*onhL~U1Uf5wAwcXxSrODb5o|1xz`h4%tH6=65+gp(I-?Wz)3xOs`R
    zNPmbOqSsZQkR9yy1&9HAJ5uoWHcFi=SFbdPvouQyH(0wA8PC^2ra@uWS6O2LKOMJO
    z_rPUe7C<YPek<30@3%DHR6)7bTfrEB|JRO0S9C|WfK7L36F7AjI9=1A+z2&ka{+=S
    zICf>Xf-m@9H#lEcl!H51I(&CT1$JObxcV+&M~l~l%Qjt7cvP{Ig;|&q9u^HKnN`uC
    zB4?O-Q_Y5-FjjY%BlVVVrIdSx_~eK<WQ!PB$G3dX7k#;Caids@skn+`wln*ITCY)y
    z5ztNdHwA#9f9G_J?X-+Xmvqs%PY*aB|JZn$%WZ+-SZV1PUSoH5@i>p`b&ogrk9(JP
    zHC2R9-~tSegolBI-!3kDbZpzfY#TXU)b^1fxsu^lRcCl^skeGRz=rL%BXzik`F3Ne
    z)O&~64nnpVkN69W)u5ReeN#3X7T0~J7=Hb;mTkFOZPrb>n3wk#7<?Ik0hkSl8QW4J
    zsj9%3(^#2Jnwi=7ja52nI~0Q7ae}ECYOPt1Gni@*L7Tbcg9ljzz&V^*NSr<4kbCe`
    z>9QTl)@;*xREZFhBYBc5xfWP8lj}Lv>h^ATnB%nfhfVUIyElkIHUt6}Sj8GXyL1|q
    z*p=zCaU0rwU6zXF*OqZPmn+(f|F^X$efe{NL5x2d3i6Z<M!KYjR+&+{nVb3cp1Gx^
    zd73Ghc1x_W#g0WaxTbUWrg2(JxLJ@hb&z{{oORR_oS>kb;GCyZofRsb(YBpi_?_d~
    zlKpaqtJj8M+itBIt3`Q~x4NrKS#ZHRWRDSv;nRHgVTqBIKEX5=NZ^TCVFU0}xgUC#
    zC3ji{lv*!$u6ub-tvj#RjIX;13X+Pulj;xs+BK`dH4(uz4|uQ%`;F(ArRR8Qr@5LP
    z8?qZ4ER&`9f~K-9yMj+3gnPQbIs1@>+MK0xwAWdwP1RK2*{Ppes_WUF@maP%`AM^y
    z*8CZ_uap*aTcE{Sm3{ks|Lq{S(Rx|Iv~iXDaUU9f;}^OqT9+-_P3`)+#W)pEK)Yw$
    zj=Y<6|GK2l8@&sArP=$X?Rc>*xRQumT8z9mg=R%x2fqRNviG}d`}?N}D!@P6v&WXe
    z)j6q`x(E}zk|`OgF#!`?yTN5UpUr%Svs$ZfTZk__OMjKOH+)!)^?c8lxaSjPH{gk*
    zLAm*S&rh6|19V!eRhM(l#T66-yp>z?deI$J#yz^2|2n+$VZ6!v56e552m6hC+{fG7
    zrC(Zh7dsP+eAKH&$(O}Rko?q{JUX15zncr3Nm!hPn#y@_1Uz9nk2-mi*T4~+snI~e
    zzdWj4`?c@6%psg3|F!o6)O@S|Ikz!fpy6Ds=ll!I`h0~OWs6&__k7!#djs~s+W|e$
    zZy7YFn~Uih1Av*)8NHhvJ*3&dySY2kDg6&AozjVR6qFeeP+9|e{IC%l$eEV0jeH+P
    zJ>GPK5~Cz6R~<Sqd%vg8A7K5zE$sr-pvo)$;?+6hk$Tq;oT<CKl2;X~Gx@dg`N8!W
    z%_TgCE1b=T_}N2Na5tR8jn%A|xX!U%eeqMzH=xhI9mRK2#mQZl>smoITHO~N1yZ4m
    zz1!Vy+^@?O3g~^(Ej_SDA$0{C7WloHHNeMzT$&M;np=?I#XdL^9{F~o>lXewMs%^k
    zIa8xN)`L3k|3TZzG5&b7oZ}t2*ID@2z5J3dS<Hvs!HwO_r%>5deq-01%`u!77(d!&
    z-k=fM=80QnpEwry{PKGq+#^?_<C@TYIp>OgqZu9Pk)D{DzH&x7^}~C-DZRY&p}ec$
    zfU6$sotfWPdh4ONy_YuN?eWlzZ|r-c2XezURIoO3Lj}{`IW{#@-JYkv`T2u-oGae5
    zr_0Le(g`#^%MHAp_5NXjUE~2jdP#opm0ebSSV}*DtK)&?8=vtte8Y{^4yJvH=^w;n
    zp|~0M@^{|b`QJbHzz_i9(j{DbFyO+4p`StwDN^(U*03SOiNGjUtcU^%Fg6>Zu<`hj
    zO%;(M|MQuwk`HA*L?%bAgq05#tWo7;!qQMq1FSR}((v>NG)=CdL;V@`CjzO6E)2Rb
    zb=nlF)2B=i^|*Q{D}zG}uwvcH;GfvBWXqa8i#DxVwMI|0z2NV}1-WvEUAW-s!Z0U$
    zJ$Z7X#)+p9fJdkqff#YS5$zf`uGFrQ<jIxLShkD_4W`YtFx9HXF7yY{A4;44Kn!*2
    zPMuJP==3@s>=3j=(6UX7mYv*odi%D6<x4C()4oibwl5a)Sg}~eiuOF#hUwJzY_Ps3
    z`<_b$2VOGR4_7~iDacDaUopkSirG*AOO<hBs*oW)j!cOkrT&#KUm}vJ(@g@Vu~Sb!
    z{{fYYP(&SdR8mt#1>u8Ib>USHTXAKTRXt>d;aVPk2x5pyi2&CHB+50{U3%%|1YUdz
    z23TN+B{m~tj5+2QWs;$><7Jv{=GkYUg~rZkr<Il(YO1yNS|{-kLz`^1-IiOGyZzQ%
    zaKP<@1#;8Gk{p=KIk!P{(NH(t9^YiQ0S4J|hrvJyiI<Q<5vhmXdV0c#0(?+p#0H=s
    zjRb{$C85MpqW;ZfQ-C`CRG?1=rtyq|&lCk0QYKC~m4s9|#o<;JUdZ89J%osAs;O!k
    zmp>`KY8PI2?bX*`GCDz6jf&ZrV`Pz0#^Ys~4a*rO><lSdkw+>?3^A>-MvQDy|Ar9H
    zl(<=`+izM1hedG5?Xz5S%t1#knrLFD-5vyecMy2}l-H1X6XB_6dljW39~<*|#BWF_
    zL6IMSin>$<EdPzdsDL^>+S8;6URqQx3_93p#THMcRaT~AmGQ=^ehe~NZjtD!TqNFw
    z;xNzTl^2Y*78clIiS3HxV>(t=nXo?s`5BQ#DtlULLa)Z!2eCOBrES;prdzhYb!$f~
    z#$f^Omtu-*rn%Q`eTgC2d0o(Uan4!ic-2HSubvm->*q$<)K_0hhw6u@!1=g@@WBEq
    z9BIQof%0%Jm--`drWag{c&8Y0wef}=YIs%1l1t92Tydd1>s>AP<uYKN|09NLVvE%b
    zS?R#$tTWHXBKz~QNeW$b2+<-<Ep4<^Tcwp)VgVd*T^jc-b73-9CUoYmx%D1kgKZGI
    z2#p6XylG<q4E5{v*{9q3!fhW(RnTqsz%L=}sKS052)Iv`3Qp7og&UQ4{EtJ<ajB+G
    zj=!p|lKiTQn*Z-2&fUr|i3uIAaAdk);mk*x+1cuR);iZIDRxe(V6|!&kK8d$TUq)-
    zw><c*<ABL(Ws=Tww00fjap!9Sag#w3LZ|4ZO+6OL#=ZK)hPS<KUmvML_r8}8fB_K^
    zL%iGF492KYEUZ8pI3G~bCpdoy<rxfuOZ*Zyzs6<pe);R-hm2yP|GT)Ve;0U$U0U>)
    zG&<pPxC&k98gnBA{;C~3LLCEvWGpm5FoMf^meEinE!tHpgG=KLmU{QW-yO$l%xR<v
    zrz4$nt?3)MxDMFD7N-|Nk0Im%#zSamJsaNZJt)xONBGq*AAV0HK&*nIT)C3}=nY}!
    zyU9<`7nJp}FBc_(-xV*W#m5CRD_$(7T1@1sF_y@G|7%g_2G~Z#tU+`&dyF+LQ<)50
    zW(N&q$B)>-0?@EPG%PSpJWAt7CbdRd)EeaN-l@CZz#&V%>((^7RJ9R~sgcIh+Bc?y
    z0ZVEppf6-sLJq2)cU8|K7$_wv@uiXZ><g7XWF;Gd@x4|i{|Zrv=%~R)fiUtdtYJ<}
    z-})d1F~j{5m>b)osUBy{n3^S-Vq~WOJOPT9sd0^gA&gH)FioJsv6_uR109WuO`ZA3
    zo9r+ak;VzMBl$4|9|&YT21zY=@`e|?LnLvC(<Meal6a5&=RY@qybXA*O>A|cL7&IT
    zPhwyd)T=8nY{<6v_-Uf2v=6`vmC?YuGEs^8Xh>%=(wmg@Z|WOLO5e9qiw#p_E#;6*
    zFDn+B*3_mgy1<J(3nQLJ(~XK*Lt{+am<&J$4Qp7fYAvHp%do&Q?AQTl&=5`{8L6D5
    zIp+tQ^xF^6!bw-fY6dfSJ3GX~H(aGB)b3fFMS@Fs|BWn@b)|FG=^#&eU(1f04DrJ7
    zZc;te10{P#8PWM<0}38V1;14J*H#MFm4p4LeU)gwCi)E!jIGqA{>w{Vx~dD?m`Z`2
    za$o`*m_j``NBuPB;0D7KvofWrGHrSnXL|N8p6h8efhx_RR@11G(PlR9h+ECf_E=CI
    z=LdM}+u#P*s)H=<?UuV--86TeyzB8az#3NNjCHKmJ*!#Oq*m)NuT2FZ+juz1N%AiA
    zuA@u^26CvciN1Hs^PMkY?;FyQHjyWbr4*&ucd1?;c+CoKbAb=s;H#V?iw<`1geh#y
    zUSc@Io;`sxIlRU|dl<w)on|{wi`sUmmX1e-|E*=Xc^MXLM$(~a@ovpI<KLdMxL3_8
    zr|rOtZ0#74-757xt0va!GUuPzMJrm>q$UhR!)t~hh?}y3fm|ab*X3czLhVXDyy`U-
    z5^XPgPpQi92{Eulq;E$(iU^P*R*CvmY#IR36aH$TQf3BlntAzV1&4dw;-+()=U3-b
    z;n|p)-Lr~d1lrJ!)~C}ntx$24fyb<Y#Huwhq`5g;Ozfbkm}s$Xd0SOX1NW-L<us0!
    zo10Pdj?_Pf?jo7P$P&ha)vh+{s|_JguXSzYx<2TWd;RN$3L9TLEH)~8SSY{>mWa)M
    zcC@1nQv6a|zdZj5e*p(L|FX}0R@}y$|Bo(ia-+M>bGBcd*PZ7JU-;dg-LSkpw?@LW
    z16TDPooP{<Z#wS#sIiSL8X!Ge+)ldEmBw_!*J(6|U-ibz4YhLR`LueLx=UfbxFf%@
    z@r{4O<NpM?B|~12TE_|3xo&(4Y)HL+K%UBYC3c_O>t6P1zM?)1Vwa!&y^h{Z+Bw(2
    z&h<-Xz-2oPK|eEqiH>xm6FBy=-$tnp!Sp#h4)^D9Fw~1V#;G$?!_Ew}@tv@B!ql74
    zr#*CP56xO0y;hstM7xUDUJHZAgB`xz^xHQr_Y89N;iKlU-tXCW5vDrufH$lAmAoce
    zb2m=l4evp7{g7O%obvD?_6{|F|7@EVb6w_aHrG)(S9+(%b3FkH8t`+hM=79yW<zIm
    z6<C3@M|<0_A&%2~y~i;d!gRmqMMTF;&p>rm7ijSo7;Ush)8qup_jSz&c979;m0)&f
    zcXpWpaC$Ub<78>&1Y-#oesXtrTxEoK*JFFfcZ+m!V%2_whgO3ph2P*cR0wPGr+AIG
    zYfq+s`j-t~_#QUo3&W;)edR*~sBFwOSO$222xxk5xPU!}Z9wN=LPtgJV}W_7hZdM2
    z8q$Hf2Vos3f?dQ*aAATbSVmQcg2i`y#^-`C=zKBwbv76o{pM)Zrx`oggW$x8K*)VU
    zD1_h_ezd>~83YG)hj%=t|Ab4(gd#Ln?I(Ed_iBo8g|yZ+7~nOHr;A+}Y#3leV3-Qo
    zKy1VY1(*kb1Biw*w}u9YfFK2jai{?e_<*1gbalu}X|{*iXn}udXBonc8gmGPm_P1=
    zh~;<}CP;jZm~}0v25V4n$R~-BsA!7jS~iFVv4x2_m|{NIiQx2Npy+)=h-0L9J6Nz>
    z^TcDSsD!QPcdO=*Qz(TXM}=zj4H#gBwFX{UxQi&4Yq<7{Y(s20RE&KEfbf-U7*&95
    zI0`uDh6>1Y&)|U9#(LFADbJ8*&!CMt`HdUMdmYG=;)p-~V2+7cMxDio@#csvh<scZ
    zk1?2U`DTOrW{=c||APXTVgt8{KKNpvSWW>cknz9*5Ep7kXa@+Xcczwp>i3GV2$6-C
    zWFSXtxY#wgP=C9)i`DasUpS1xSd4vHlI&%YUbc)j*Nmp;l1-#-(m0c>XN_e9ZaJBa
    z+{lv+_J<jwZa^6_hj@rY$!F=PjxVTuFX)ay^>yxOT2DETmZ(juse?E;Z~!N1{Kx`8
    z*p;DJRRU>*I#!musgQ7hmTBpVv7iXdF`Q4>mJ|t>Z<&#~rjfdcm%jLgl_!kDc#MB}
    zk}?+zG{=T*SeS?DfYHbkfCC8B$e1=MIF|yMk@<&{DVZ69nUX_An`wfeP?XQ$nV<QB
    zObL%od77y?|CRT6kC-T+wFOSN<%6DhPAmX^w<&j|D1LQEio98tSm2ur37lWTNX5yP
    zQ>a!Q3YW?wf4FFuy6BP7sh879jG6bBF^5suNnhEyoh#X$&$xh!*?{B;3N~4jf^z}s
    zxrgUK0lh~7mf2uI3Z#(po|#!j@_7ODiGud&1W<Z>YY=EuI-2fyXjp2RPN`@apqj3E
    zb^s@)1v;AtT28!BRb6>i462~<zzZQzilBCcX6c&>xpxfNkcu!3fLELlX$XU72(wm^
    zR7h(Xxoacpg<z<cdufc-8FMW9Js2ga+xcwG=#1WpZQr?=IZB5%DU{p50Y(?9p$e*^
    zTB@bm|58C}q#L*)MtTlO3NlM7tM*x+_t}V6Dw?9XrCExlsOhCPxS9aE1Jx&Rn%IN0
    z2~M>6k828+SJkGa=%8{6YST)m7RrivI-HHvrw~bz6A71b39g6=ozNMbj7p4-3I)NC
    zuInnPk%|b}38RF`l5R+&Ja?np7L9Z$fi_u2q&ljmTB`nPbo67SLE1P#3Z$_5F*eDs
    z4cnxiMXS5;nN*seR%)80*$(NDrHa<8s=1mwKz-F$eJrp8v&9Q#dY~_+pxoE2&dO<}
    z=%(>-iu1$^aKKe?;F}Xlq38!)>BkJpaU8>`r$QTxiV%^2s#YME4Tj(~zHp0m*?1*t
    z|Dq_mmwtJS>WZ~h(5{mjuN@VymRfqIm#N=5htarg)Tpoj^$h>Ywrm@)8n`hCYp`CS
    zs;{cBsiK(;YoB`?rTAH;6>G6uD!6O#l!fc1_ehl@>zY0In#qc?+Xt3u+Onlcxo$eG
    zyl}G<ijWlAC2+vAVZ{ussHf8~w8HrfMti!0hq_cqs7VW=Oe><X%Xm_IqKs;_)rqz2
    zs<m4CuDuJdl{&9E#|>h8de~OBpzw2Oo1+Z7wr)GBZ9A%sQ@0dwu&R2uc+0$dYm<E|
    zrA`0}x2lwaE1JAYz8OHcU5dCLE3(7NzG!f=B}=l%TBh8mplw>eS5>*Ec!V_z|7r+X
    zr%K2S-4eQsG{8R_3kFQIMytT7s|bJUz~B11$`iXCIj$yZuIGA;zHo-Q8w?#RslWTX
    zUJFu)zzy9wyiIh%#_NDLI-ZPKlMKrNF)YI}45~Ct!_up&)Vnctd%dWVy_y-N_W8Zu
    z>$erFtAfj`TUxkX`mq5TrZ^bC25O)vi?WhCxiMRc`ir>+iL+(-zdP%>_H>~@JG7)L
    zoTj_Lf6BT{OQ=knk;^&3v@48Mo1*B-uDaX7?+U`cI||v^j3w-h8gRmhNw)Z^ywsSz
    z&#TBbtjHAL$UrK+K<daI)59Q=y$?&oQaZ$IB(ZCh4&m#LM?AQuX}(@+|Gpq=%1%72
    zVY<r08o#qS#g5yo_sas6Te(<(xu-~>IQzd98pc=<!1px3(=foJTgJmF0Sv4N3hcmy
    zT9IyCsIhAZ9!aj{TD8RJ3+$E6?4_>k>cQ#S!CJe=@yZQ2Cknz#qku39#>>vg>&|G)
    zyfZA%^E|_jY|l7+&s<c=9>TCqN}ob3#6--=>WHg>JIbWYv4;Dx833{k-O8>krc^w?
    zU`fjgn!j#}x&2$S{>!-?9l(4y%maMDq)XC5yUZue%z(PaZS2M(3SQJawZUkvP~Z#h
    zwax2##~lpL9t^KN&BxIe3<y}lg1pW}t-^pXo(_oAhx`o7OV3YT|IhZU!=@tDQ+=8H
    ztQOh(y-nS>n;dAJybDw+u>~E<2TjU^y9PTzxT|@%3>~s)U=FT)b|hO{5?!VgUB4%b
    z#WIW08|}q7%elQg4W1j!#7w}-p{Ei+(h?w?3XIYXY_t!|v`O32%ek~uOUKlyqBq^m
    zSj*Gl?7@00+Cfd)8W7ZgtVE+Qyy|Sc>&(teJ)ZFF)KCr8RgKS+tj}1T7TFubMEt!H
    z``ZKU1Y_+4pIp{vEx4t;)~4*%4$Z#n8^7=izY;ym7G2Afi@7%|vt7)~oGSzZ4Bo;_
    zz{e4~!x_4a{n#jdx{%Gr&<wlpjf-ChozpzWj9Sw)jo<j4|IHk{)1m#@@Y=_tfD9`M
    z$U|-5My=FJjnoUy)HzDqwJp`RZPmKH7ERsM{_MTqtKq{f++fY&TPn(<Y`)8FxQNS5
    zZq3Rf8{KwYzb&rUm@Bi{Z3pJi#T(7hzWLqZP0U?Vz@%H=)1cVMoZjrc%!bgwl3m#^
    zP2cprw8Ll!#c0zut<CG|3mp94SDwen@UE&IZRf1o&UnJZyV@EM0Sg|DOD)??{oqe6
    z;kQlUYO%a%o8i9A;Sy`-FDS9bUECo)xar`I%FW!S+|bSa+z?%|hd!IrO~o&c-HmRu
    zHqOg{{oOb|*y3&2mww(ruIWR5(ne0??hVcHz2tgn{|Nq9)2UF?sjlBtuC7&H<yijT
    zh%n&US<WOp3SM5q1}?}BIOYrP>&k24YfjH{R-_RgVUp~~Z=M#1OpV^V3&9=ScYf!e
    zOzo!W=g3{k*&gVF{@f|fzAJ9m6n(PUP446#>3_Y;STO0+AOynPcRfzbu`mY0ISr;j
    zx}5&$ZhYkKE$Z_<ouy7}r=HCNzv`{-*#{5n*(nOLjtJ*`+P1FiyN+#T{@Sn&?4ZB_
    z;E?efAHy2o@iMIOaaQb!@W{k2)yj?*7LLff@C?sR#1Y^GFfZ-Hy#{%ntJjY0XdUQ+
    zPUs9>=--a$uWZ-je%<A6vqvB4>COz4-tO%V|ICIRx>0|?^6umKE&)ZJ@1V}Kmp#q&
    zoeEE0>H?4It4{Dikgl$t@I1}%4o||ij_az8!n@85760oO@9}rf@p$j?$KC@ZZ_nu_
    z$s~{RXThl&uHnxP+!8DEbzbv(o(^G+^L{S*Yasc6PU1W7&^}-2BpdWBZr$ac-AEtl
    zNiPIAuI}o-<53Uw@vaCz8|1CO-syeyMo#-$-}PP3^<O`XzK{*7?h6C|`%qx^>KX)T
    zZ~P@34CVagxeoWME#|&X=Dr@=XuI(mZ~fJe{UPsxKuYo?&vZRN0e(ODX3^9d-twTp
    z@`+#Pc5df2@BZ#T`R;h_lb->8zVq8o|L8xD=s~aOC`<H44-mZI2!vD6U_l{R;3Q;-
    zg_%QU(;`lk7>k*rG1DYiRMgR<M?;DvC<<vvf=NRuRjSNpsAaZPhGJ&(MKhJJo4%m<
    z(%CbZub)AI1|wRuC^DkR5OE_aYE&?8qnt*is)3tTtPwP51;h=|SFm3}jRggcgIREB
    z(Vk_CmO`Sp6wdXSdoC{Ay667w<=fZqU%-I{4<1Z*?5=0KK<#p}*hz$4j3YbQm5%b7
    zU6|LbNym9}XV0BQXIPVgv}x3)cc@;y!}aUeEOyAI&4PtF+bnqL=FQ9ZZ-E348dL}e
    zixA^N9xit%(K$1V92H&cNWJ4o|B)m|qCB~hWy+T>W!}Vq)BJg!P=e?T0wrkCAV-bP
    zC&~>GvZqm}j=Cxe>s7A;)F7;|3@a-wwa!9t!L&HIORjSwv<oi0BqZ!Y3^UY_EI=At
    z43x(xn{33)BGarg%|r`r#dOliptICs%%HUyVY6{I9BrG;w%vNu4LIKh8t$Ou5OQdt
    z;~Z*kB8o18E~Du_!fqrgwL|Gj@4!nbrkH9nFQ=W-^QAqY$_y$#qR<QmsWp>Ya}=eR
    za%xWgr~>dmtG@DzK(WYb@J|F46f`Xg-;yxGLK8VuLq;2I^g~c0_%JfZAdBo$5l_TS
    zMNUQY^hHo*oKdw=Z*)zz{~TxIMO7Yq+YQLzhD4~KhZ3rFnn)zVVhm=OoMsG;pu>(j
    z35XSGI|)we4$GBV5|2wm%!?L1o~r%H%=gfY51A_1^!C1BL_uUurl{htPHq6~YQR0c
    ziU6!g$x@I(dKsj!LJ1QobWun9^%pR*6cc#hOAC%Hor5dOti={X^OUpETy#;?QfsWy
    z)E&%Gb%)(peHB(Ag_9KyW{@mdh-pY(?l~r>lPF5+#<=d-W0h6*%4JoGNv3Fd{<2!2
    zdAe38ZG%CR%{Gxzdfaf~6qlQ+?BvfXt<>!*tal~#vtF(de2c<EC)8I_3;#9y-$<X4
    zl;Fh-Hh9y8TZ|jx|21BryTunv3pImxWZdDM*Di()Rl;$5wN+SS9T_>~NTyX~hm?zm
    zSC(Opa=LGltvOlFO}g16XM5gqbe_5lx+kGiU(aaPS>HD4HNh1Hs-{J;&-Oa&wCXB6
    zuHN(A-$4N+g2cKuKJ2meMSkqFn|D;wwHI64?Sc`m%n5`MhWKIbBi>t78e7A7RgG7@
    z?fj2{GbcF34{H2zlTlWVWr=!qIU^^h8;O}JIrpDC0A^`)U7FVEg7%a30A+RYVIb_3
    zmNan5Nlr_#lbzNTAm4%IUGW-=@w&FWuPsks&Ld%lCMCVcP|sl2%U)#|_CgrOFjOw;
    z8{bURx7Gme|8KC-m}^$`!`a|wed!zG`U+x>#VzD<C1c_=l(VZ|G3iP9yGXG{A~XMG
    z@n$(Qg-U#pk}vj*C(+8u>4r9@Gr6u!4uqX3lp>1R*^YMUs-Ol$5dp7=B7<2Q-tgk(
    zL9U5Vd56;1TS|Dy!K@7s0<)9~6_&8<MKXqylvt>0c*)fOPKP?wSPwHM8;$9PV;uX@
    z`idwdBo<^4vr;1C9A_(%L8Mppo2C71i4l=tQHxmw;7P*xOE8M@iv|SRdg6n|1~%}4
    z%Un|&X9qQ?K&^u43J?bQc+Ecol3s!&TfUfwNO1ZkUJ-!IA}Qs_M@o{C>#XD}Zum|c
    zYI29K{~3g9dRV?t8tyi~F(ogIh(wXi3OOZ1k`N3UktaqminN>#6(yj{Vr?#2o;yhw
    zmlZ}Yl5vd0tk&vy!axPG4jHdg8Zt`SrZ|SncH@E?x^}05-lc1geau><(&7RH;q<0C
    z&8b`<lsrZG^c=|xXHoa^u3aD%GL57j3l*kLb~4PJ>6oewt=czvX7x4q9HkJwy3Zo|
    z6Anm3;wo3!I9D3faZJqBLTjl|TSio7bKNB`E&9bSW^|asET936Ii@i&FbLZs>6%We
    zG-sBpX>??4V*`Z8tC4ef&?}2h!C_g<@)W2-{o2@sYSf}eYHj3PDmtCYFc(G@hVNKI
    z|KHq+)p-6*tL6Y)Ju`M&b96GdU<IpK$vReptg@908K^;>s2s^j&aF<e<rpIRP>E{M
    z5T=lTFRbJXU+Q(E+YQ=Z|2k5^#%B_13c_JciWE1h)QwPsqcmMA({w?Ww3D4|q;z`U
    z%kDJ4I^C>Y#O7K5f;ODcBW+S6!&JAm5UMYHMT;8j;1<1usu^%+Jo$!IO={J)oy?eS
    zZ`04*`t!HHHLE}kI#A*sG@)>1aYG}@Kj%JF#&xx57i}!My$(~yehqA32OD0aA*H+}
    zJuhOB0Y^D<0~tzoEFJMk$F9iNUE~z)eyxmCEL*w1J_V|ODIj2QQaP#QG_b+&|B$d<
    zydupAdvJr>OjS6?`Gpjw^E+T`=e`YMMjFl-;BwpH+y+|2BF-%w22oa3Dsd2uJMlqD
    zrW}>2Op3H*2CrnsKNu$~#?<8;r>o1kFIIQDIPPm-yW3-9{@7>?G^t^UTx9hkxm@i{
    zm+csf#|Y?=8@QG%Y3+MuU}xC{V|xHzC?J<a6uXc<1v4T=OWFdLIl*bJjGEsZ=W9b)
    z!tUhug)f}p9cq%cuBqpr&E4Ta7rLw@Hgvlged1Rlmx(N<H>NQ?<4q@v)11ccjYTc$
    zF7DXWryi`2e|+$fCS2h~K61m6{BT-Jv&p;Ob+4gKS}Y4Y<NL*OvOVk5|IQX0<jp=L
    zv<+NZX{$NS5n%J0tqo^4Q#Ix>PltqUj%Pc68_%-cfu3p0;X?;H(0~?tpxa$y<Se?;
    zOJ|0PoBniqPls2<8iR8)?c5maTkBfKu66O6<Ldgt6g(MwM?Y%tgZ~)33wJfcP3lI7
    zx7T*IcI=bwUU7>@zy*81EZ_H@aljv2@X33C;3uSPl9%w}CztktRc>t;;sNH9cXQ=m
    z@rrlE+|D(}xt)0)h;aY>9U40{pow1eLLd6*NmuK;nJ#arvzS+yo_g(NoOSBvUIM!B
    z_oxFNt*{TACwe%0s?%OxwGVm7Zhw38Audf@)BV=*<u%?h-gm%b{~!B%7yPk3F7oad
    zyV!{*A(;z|^2nSyGLKKQnkj#Inadphmv3<9?Vocze_OVr7k8g^`+w9krPM>TMKd%Z
    zIIE>YE_qWf+3OWct2!9Nx*4M`=h8ju!mdxdu^X$FvMW1ZAim;5wc|s$<uj=gJTJJ5
    zySb}1iEAvotG<i-I~klo8U%;#qX6+swj2CD@^hZ^V?6acGc^-JYO^*YoIm<YLiwXV
    z`{O+P>paiHGtv9Gatpu!9E1|XLIXU&6iYzZgS`f{v<Q^I3VbLF+`Tirz)$0eH55Az
    z1RCNqz7ZU(gj>E7bh{K(LFk(}ii5!zq(MNO!5z%O8_dBT|7@O%Bds936e5(oB1}R^
    zj6_MSKg`2CD3qr_uru1?zbOnlD-5(%GKWJuz*UNbMN>L2Jh7+yLV6R!s;fGx!#Y!_
    zK<d&1G(<xi8@t_GLs5If;rl?qlEV=MFXUssB6A~T00U7_MrD+>6<opT>qG0qyFjc)
    z?hC)~E5u}bfI~bRlRGoWz=Lqy1#wJ=Gc!jcY`;ld$IHvSO1wmP)Wmlf1Wv3cd$hu!
    z(?WebKz{5;FI>eaVn7G1v`Tx$2sFbnm_<`i$c1dkg~UbPtFC|>yAA}#I0VKZ(1ST#
    zyJEaUh696SJThjCyFF|&T$9E>v_?SOMjh-vmMlEM|LaDxNyIa&$@HT($%}wjkUTtK
    zN1v?3cf3C+q(?g=O8%oqD(pE?1T;@PG^fNuv+_q&6oN=lG34SEfmEmmEXY@^I)zNg
    zgrq=+97~DRMO}15iX=OX)JR{L1hh-LNua}GG{zzW$&p+}XLLqBJV{=|IF$s%2MEl-
    z)W*PEwl9-Oe^EpO6Gw7v%$iiQC}_Vm>&c&-M0XU*c+9hT<ivTrN6*YHrxZm)!$Ki2
    z2U0XBrJKsCG^nbqwB)L{2kc6(oK1!d%dvFG-Rr%xOiMQeLAH#;IZVE}^vIC3%ViwN
    zW|X^#t2>lT$-k7r!W_)-6HJ&qHpEO!#T>`R{~W?}(8*nhf}OlP%9Omygv3Co#7umL
    z^h{4YGfJbREqlbmpIgd&6h)|nN`5>=e_TzfM6O6^x-n3$|0KxRl+A>!G*e)PuS7%J
    zyv>FbyR?i@;+s%lbW4rY$c+54xQxpn0E0;wDUd{l4>c*fEY4&E0~AG0ZeT`kz=L$b
    z16y;^7IiXl+%<6|r{|<W>a5OV6U>%`i|g!{8wE$36vtQa&YmPkbTmhFl*IE~$0yCa
    z^IT6mqtc?>OnOAhES=BL6h-^APtwFs{bWrtZMxP}jz|blW*AUc<VpjbMX;<*2OUcY
    zMN2!SP(1y>4BgWp*ihvA&<-uH5B<;*{|!+TJ<$}!OE7>@QD9UUeNjlY(HG@QJXnIi
    zv(&xAR2R@xOx@AJ!$InV3+oKhMu`A$G*!oROrG2YN7%_Gg+%a-#8$n$S*5=zeaB2>
    zPdrP{EG^0`J+yhGPcQY-`xMjsM9tMqRx}j?HC@vvV$%XOP_T?shFr)7ol`qa%RHTg
    z3tbNk)kqHQQ{?kd5nVV!MN~vhR4`c4Wn58rebE?=)EcePUD&lvy;n}XRDJcn9;ME>
    z=-0p`)kYCVf=$)O+|Hg%Sn+&?Sd~@!tJPM$)hSg^ilx|G-As(_RV?hx(CpY^{n%qw
    z*8coYs&v)?byjIjS=yx5Yn@Yy|9sirBtBEf$O+BRZskxv_0xnK)N-}ULrqbjRabUx
    z)JBCG7nRYY#UmFrRa~poOnq8RjoN#4fq&gbALUo-6j(-K+M4WIJm}h;OjvXz&&zAr
    zT76iVa|D@_*jKpNi?vvb)!1Lf!k&A|kG;>+90XJ3&#LTOGyPkVjaHSNO~S3#G0=nC
    zMBIghP?$9Z#Qi`$sM&6vTyf3W4&_;%P1m5^+!VzF6!l!9eN-4l+8H%f8)aI2O<hY}
    z-PC;$Pc4+J)!GapSX1>{o$SfTR9J;&Sl*@8hkb=vZPnlf-nMPqiFMm5RnLnZgiQQZ
    zEQLxiy<0J@%0=7Tk)_JM|BZzI+}^57)4`QiIMsv01xv&|+=WzJ$30(%6uSwHT*$Rq
    z`0Z10t=vAvToDDyN#I=lMbYD2M$aAHMV-;372w)6UA<d^)lFd4#n(jH(FPVw*`3|m
    z6~`#}+VA|@Gppe6q|8|5;NK-);#FJWy*!DX(m=>nw+#g5J>hv&;pc_Qy0zQ;G({|=
    zVe8Fa?A>0I)nV>^MemhO1*kgl72@#~-||IV#YNvFR$umo-1ZHEnZ3}s1lOG1&`B6n
    z`^{ez)#Cm2+(qSIJV1ugE!xo);1)Gtaa3Rg-c$r`V>hN+*bUXYh}{S#i~`1Ba<pLF
    z)m`4j-QFEx5C&mD|Bl$=RbE48VMK1;6J}nG<yh&R;ZxLGy*<Fby<Q&X;WhQ)@9jz>
    zE@JaV;`9aOCH}xre&Wcr*(sLa%FS639aM77Us(3g{pH;MZPZ0=)T15XuRUXeU1MKv
    zV>S+E1P)9~cmPZInzt}!AEl5wz7T@-+6zutKK5e|F53{kW<fqrM>t_ec!zC1VQ>a#
    z<y~ZQW@HuK*hl`@y`AJ676N6^-W=9mdCufCt>-bYXL}CfG0<mD_GBdf<WP28B#zwn
    zCFoN=Xl_ktJ-FiftzRr|Wm%SGF4kf$27@T*+|SKu0M=y;Zcz-zU^jkaU=HaeC}w3&
    z9ttRCmA;o||0ayDo!~r{<_+Fs-UZKCjnxqzWZ>22;@xKBHQsOzYI0U#qGn`{b>v50
    z=e?C@cxGyOzGtY$=ctD2s?%qF4&ov1WUVgcfevLSHfVyT*^E?ZD=zCRK5L0i>sY4f
    zh|cIQ{^I{#)UU<mTo&M7CTYA5X=N_vLeiJLR<;ppX~Fnt!v5N@-AVAI=9=c+oX+WM
    zj#!@t=WiBj%3fh}X5_g|YDrdVd5&kN*4}%rXQ`IzApT)~_Gj}wTqBNLfgWgr7HB7?
    z*@cc=Zk6AJE^CK|=(SdBFzACV?rjvkXp2S$;g0M7Eo|ie+Pq$F1XgKd1}f*a1fmk`
    zz?k6V|32&r&TgAt?8biV5Y}dI)@I5cZ*aEkqQ>lVK4(eZ?9J9*c4lhNX6n#ph9(H@
    zsV?oR-e)4lYW+3^tX}Qb25SKyaBiLL+P>|xPVfZh?cHYUSO#v3)@Zn{<>dZo=3Z`N
    zwge4_?hFU)M4;}#xbBZeY=*VzC@^uyb_C&7@epQl7Y~HUhVgMG@A59^^TzB!P=*?2
    zUQ^U@&i-s5cj~DI?KG|LBCqO73u3DtUj^{*S)2sNZSn!1a)T~#vcBzwR&XtsWo?jU
    z-$v{F_3hq1&Im7a&%Nj~$6&dx@C(mtpz3hxP9BxE1P}*|3de)&rs)$uafWpS@bvQ)
    z{|DX`cX2`A>1{rAZl>|duJOyp!t`eErPkY}4suGT^f9P!C5P(L7T-+g^uuj(1)y>&
    zw{qGx@GQ@AEl>3;_Hqwh^)N@#F9+`Y74D0!^)CV14JGnM^E+4WHYaK5*6<CNb6>CX
    zzbNcH*KR!Db7Z&a6c2P2ckw}oacH0H@m2<*rgn2aZxyy-_V#f}m-I^i_8`A>sK#{t
    z?)3lu?=gUKP#1NuCUpeYa#UA!1#k6v|MD;wg;$@1Gr#p)S93OBf=Af(Htuy_CwMv+
    z_P#*wVn6m|SN13X^oMu$K^OF9SMg}ScxgXjYOnS{uy!3U>W{}lZvSy_2X~V{a?mDs
    z1^-CXOgHyW|8)OmcL8^IQ@`SPmv^+bcX<bHG52j-|9Ol4+Mvht3J-XIN8o@@dZa)4
    zfH(L!9~)mkc!b{z3^w+LZ+Jg{_!9^8itqYEZ+1b4af?TEMBn(JHv1Ju`;gah9nW#M
    zf9F7mgm;$vldpU2t#7<9_m(gBa@PZ_UiZkA@+mLynn(4U$9cq8@Qa>=C_o0EpLL7Y
    zcgBzK6F>9pUULgKdd+X+%|CjlH~6XNi?6L<qObg`NA|2&ajhS8ulIVfe|@sQcxs=0
    zjjw%k{`g2x;kIXcz4dY5m*=^!^d`7_anF0>FMe^4gq2rv=I{Hz_xqR!{7@%o!vFVs
    z16P2A#{R_La^LQLp6~5d_x>oDb;h6kS~vfoANo88X{B#}rhosae|mk1|LG3>(m!^F
    zzxvc){fLivucvr`c;(1Zpn(Gm5=<CKS)hgt87_<{@!_C`k={iLG)P&bjUF>bio`MG
    z$dD#aqHHoIq)L`6Q7RatvPnz@k!s=`qX4HK1*R0}88b-_QJ_7E7S%IUDbq=l6byap
    z)G1V_+MM>8Iw|W`WL&jQ?c)_pA25@mfQfRJES|Dx-$ua`H?CYfb@4n}l6SA(zI^vu
    z3Jk$gqQZp-3pSkSALGW3A485Tc`{|nbL&RQta<ZhM^QY3{%mkG>C&SS7XK`$%+czJ
    zt|Mktwo#*OWwk$Y+$eJQ$lfNKu#5y;cu1MQjSJTq(kAnpId|%;d35LWq}8DwmD-ak
    z*s52zVhvALE7!8-$)Z2+_UzisY2RW_iy4pJM|<tpzkh!);s=HO2WH7&|1DOTfd?Xp
    zAZDB~m|18K4kVgs6CPv`YOE3TkZKXxwvdJt*|rf!90ej$Z6NJN5=tix*HRPA0Qcf>
    z#Lbi(aLhqRlXE-q2o#S$`FI^sQB^0Ek=}vzU3q4G)ns_;jfYkrYONQ=lxJ!7o?MsB
    zWuKQ{f(fR8V<zHXnPUoAW?>1k*=Cy!!ugq;pcy0~g?Ey++CZ&kxc?Aq9DWF>ha!43
    zVv2_PW@w8v+DM$EHP+Z1jypyd9d$ek8RVr;DFv0MMt*lys852Im3iQ8mEL%#TA7}e
    zYxU9AdzqcJpj=_%8s?aTndu*YX{za_u)|6gSFCZ$Ij6FCGHZ~Y2(1?FhPHi3ZHFD2
    zNFt&jjkv9Ei^gaiq`}2_(~ZI@pwdn|_SkNZ(Pi2bcAwG<s#Ky*a#eY$np&#Arbfwc
    ztFe~l2$lr5wdJgt<=U`){y{7te`@;5AF&r-oY|HQdX_AN5jNYbp3!1x8-^ZwNG;0P
    zmS|hHyqO5IZ@)2GZjFpGny$GrP3o?@^7<Hbc0o&3FOv9<+W)W8|MI)4(^F<Ou$59K
    zd@O^vI-D!TiSWlOfn8%9wws$xnOWHmIyfbrbA}wUo+Yyut=tb;Ter5{;_Wg=O(+AL
    z%T3r^vv7b9PUB2EBTm!dI5O^YyFl+$w5OBb3l-A&E}c2knfK9ozgAw^`P2v#{2rE3
    zWSuL-WkS$;>l8oWx+P-Mj-c6^oh_%@%c|Wro+CSZGRbxmZ}-d8#@zSbBKaM-&CVMx
    zc;X6Z!*jZhW8dymlt=FARn+I3@8z0zzImypvMPGio=<(2d2QXkS?aiM?Rx7FSKNB-
    z`&-7~?*DtH4<NJCogjfH17awF!<!oei|4>@>8)=u<Nup*BuKam#t3j3oQaMUCzB4Q
    z%Tm{CT=t0Oxb01%2~R;9zOtgP^OetYTZtk1XlNc9z6xrigWuGq_6T+SFo-}Lq7Z}l
    zfFrWben>o`{rKlZ$n;Q$0o2{v1n3AWjs}Y%YeMi82sgRK?SaO#*75k(JPY<MgN555
    z1_!4`#li7<iW^}G>$pAdtx$a8(}t+D;le)n(T_5W;S3AuIrPmibn08A4$J1q%n&h%
    zk_6%;A7MWT95IuZEYlI6=t;)l?sfpYBArNCN-SCtfsRn6DhFafE@B9lb1NAb#aKMP
    zDbF^zG^6s~h(TX!Fqj$~T)O6HOgZj^CUwl-_y786xjyo7D}Iz_4G)>fYg*HWP^+Q)
    zMi)ig5iy)X9O4i!i9fC>5n`Y8WFw~-z$<=6fbtxO6;bKR(IBvutGuTpxCl!wk_@18
    z!=){$waZ>2l$XL>C_@|C&|)6*m^{+PGAU|N>Vc1s)Z=3x#Ro_}0y3n6JS0g=YSNQ#
    zb99Z2-w)N{QgXhur7|^VCS#Y*POj65Q+wy=NLf#xni8mb$|pYgxle=q^NaoL=W1{X
    z3E~kHs<}*PFW2}>H)7SHfP*Mkx2jBKQWUJ%qbNo%%2AH)F_6&&sWcOb(zn7DrJ(a>
    z*<9Mvm)aGtc%>=C`1*jHI#GaDf~VT%IseainlhCJ!6H$K%FkC0B&j4bU@heq)v4-|
    zstVPtRy*riIL=WUUp4Da$m%oFp4FnMU9Ct3Inubo)})|I=}M=h*W2P&9j?;`CTrR{
    z-u4!!fUVtN3rotuBG#~py{7@2yHCeD*SXJK%~O}^)Lw=$j8Q!(chmUU?}E3h8kFN#
    zuZP;wZgis@Rqtv+8c31GHoo#@A0zva+xxn-rZ)Ypew#Sl+Q~Dy$pxx%``}ywHkP>x
    ze(pZG2w4Z)=Ba#pYGx0b*<5yavt|sYcfku@(Z*46qDAI;%gWyNqWHa?8|iCbJKq;~
    z?uwqfB7X0yUu`G@0=(64j(PiE0RPXHz(SrUB#699=|-2qNp^69@99t0JQWgwkZ{}p
    z%1h1m5^&z#GKW3vWzm8d%uNt;y25&1jixxotyQt-Tnyv5%2=L@lyQx7+h1)sLdX5>
    z0|c7zW7-wCz(Ovvk_(;WLl@egkbUKoqpUm%Us=Ox%<_i4Z0Uu97;s@8Gno0v>5bCF
    ztZC-xN7cIGQ@=UYCY5oV?OW&D-nhrg&@Z2x0D(V??U1aVv4IV|N?#W_y1-WQp`+Ve
    z2OGPeuVl2cmEq_p1FoM8eRP*AeQ8UJdCb}tGq=YK>Q0aPz2b(iie0Pf__mtY0>mo-
    zUfOP#zFQsdzOjxW(1%!0LjM`K4x3M7%xeQ8Se^hM_;Z_E>?0Q(;gDD~lb@|<XZtha
    zla}zLwF>cTYY-9))wnP@uJKKGdfS{9dAQvxQb+_6<$zQ<Y>RyEx2C%f?~XUk;cate
    z_z~w1utX+tu8cl3p{D!Bn814-Y@-MJ*GXUakqzE-r!#%wQJ1>Hoegb9Ydq`C=D4z(
    z0PP7!TZ7W}dd30Aak85|m}g&m+R2`Ew4>bZnkG5jIm+oyqnwX<UpahTe)M2#W8s1S
    zyU}}X_@f`bb1pYL<4L+}dAmFE@P5S1`+#$MXOic&<}J^KJ9J?w-gAWZHL#7Y^wU#4
    z>I#Q?(-H3V)vprStN(;}l&|jWi+jAG5-vOM*}n0%=br6Z5B!*i|M9qYo7;IuIrH@%
    zc)k}o@P2H<M-fl>!uvh++Ry&>O}h9)LSp7Nuer_D;d7uLvGX{#{JnSn*Ut}Y>A+sP
    z!541eU`xIJ`QJbP`(J<8Yn|6w-RpT>?Y-TyfnC-e-~kq(+%25jDWC%~AOnh4@MRkD
    zd7bn%-v(-62Xdf+X(09)-Sa)4^@$+inPB3fpa?=<ySZQs!eG1MnP}Y`&+#1fQJ*r{
    z;QPU!n>1JH{ovo>U(?Os>P=ni<)0Bk-NFrE1jZf$P9Ow2U;{276-r?QVxbkn-4sS)
    z+%aDU8lMMxApZyo9`l_b_MzYks-YU5Aqu+S8^&M^Y8(0m#?AqqBLtrN%}x)x2@s;*
    z`~hO>_21J8qW<|G|50HTKH$|oVIxAI0xqH=Mq&X@V*Xv>B_1I0DWE2DqVa(t7-}H%
    z*<TvoAJW|){*7WPmLmSOVJj9H0Kg$E!lJy*qAaR|9$Eqp0-X)=oW=niANt@S3ga*m
    zV=?j{Ax@n!N?jr@qa`-uGcqF<O5-9@A|>|UCsv{;o}xDP-~A09|E;3bo#Hl*qd2l+
    zDxzXJqGKu2;v2eSJJMo+1P1l_qC8qckx?Howuv(GV?X+1B<`OzUgJL!WI^WNH})Sw
    zN}WQMVgEz=<2LpqL>grH$znxX<VD&e(BWf%?A+VwBb!WQNQ&f0l4MDm<Vm7rF}|ZM
    zvScmVW3YLhJ$jviePn{9<W1saPU_@N^5igH<WFLxJf>nsN?lDNNKYbVQYz(AGG+e(
    z<x|4q{=H=W$)i!SiA6RB0DO{GT4h&y<yV5`j{(5`aV0E1ky(aiTB_w*vgLt{<zs~3
    zKa3?;*3MkE<z3=sUV7zQLIy=H99@RZSjMGZ5@ul<=6~@eTt?+T3?|0BWn&&@WJ=~_
    z4ohM>hF>xUWC{ykY9?iR=4XN?V_0TmU}k4-CL}N>V20*uvSwk9W@bjEX;$W6wq|YG
    wCMjNyW?b6k{_N&$`sQ!$C0UB5YSIo`0_SleCm&*^Xl`Y4I_Gl^S3m#&J0yVB5&!@I
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/hsva-sprite.png b/mapbox-gl/closure-library/closure/goog/images/hsva-sprite.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..9e5ccce6ad22a96e9ec472ebe8c9915c5c5619ec
    GIT binary patch
    literal 60591
    zcmcG#Wl$YKw>G+Q-?(p_Ai>==5Znn4!QD6R7TkgbcXxLW?h@P`g1f`zmG3?0e0A&I
    zU#F|aRxRtN*Ua>+UK6G$FM)zcfCvBpP^2WqlmP$;vyTl49`=I*6D?8xaexMkN~yws
    zG%tAL(2r{bdr3_&0Dy$?j}2mG&FAHV2yznFa8j`|b#gUuGy#Yh+ZmdWezkBkakX=_
    z0+W7GVN0I>3iZK<{SUq`jwS|97IwCzsungT04^pLE+%Gf$NV4fAI8xC!Jr{0%WGt3
    z!)WjiJ&bNP_5gk%H+uskD-$PDLlZL#TLE%XQc`kK3u6Itbq+aZIr}dr<`$A3jwUJ|
    z@~TE2Rz^I=<U)do{BFD-!Zs#O2BdB_*0x|?Hv#g0>g4@6{|A|gob;b8PF4cs|8iPG
    zPLcGBoudgUJ0lB&5i=V*DLW@4D;Fyd7biWbih&cUw1F)tD;Fs%GxNuem6wHum!0Lq
    znSVBNLBx+EzoW4!ud>+Jf7|@H5+FBsa<b=TVsdqLWprg@v~x6LV&UQ8VPa-wVr6Cc
    z;9vl|+d3JzG1!7B{-xyq=n*pk8#!9o{}Zl%=ru63b9NFSC;x|`|FnO1&cOaZhHSzA
    z2JZt#CN~3nCKg6!rvGUE5aj<L^8Rxm>>O3??5qX<U&f68ACmur>A$T0m%NFXfs={g
    z2OiuE%v=mCT&gVGyzH#JtgKr9P5xg@|ChL&ow0?f`~MJUWl?3};bs27mF?f+|BdP2
    z;`~hi0OG#^^H0?O(Wn25PWt}?n6c4+@UnMywEpL?7#lH}Sew|G*gAnfFk|_jm>C=K
    zn%X(q7&r-9*ch0ZFxlIh@iYAw@&D7me|h~879U~B^q;u=Kd%30DF0VZ{tNg274`p8
    z`oC#JM=tRh03ZcOiHWGXrJr=z1W<BSQkuiYe7^Mrhqb<!JYigGDVv}@LooxK?j=j+
    zTKIU%@)NMp&t9m^p&`(A<Lw0jK5!REn~%fi(7^8Zl^zNH-%XFi-GLYHj+=ztcCatC
    ze&?ZG-&tRUKwi~Z-ru{M#?D2`-}g3q=(997h#vZT>|lK;UU1>McrK#c1aUsJ5&CUF
    z&~v>+APGUdoIR#@+cCcY2h;d`^0ugPe0VPiLpLN}fPK#}FQcI!gxeI(D_`(n*P$El
    zg?8tAMqd@c2ht6^sm{AzEC1mHHHv}|0L?yd<vl$5H^epx;0<CMm);BN1>&j;f`5}+
    z_?-#u-EW6)b3>R|<HZhulks<h_0_lMk;oqE0ks<yZDaEV?(l;H7i|L|1Ry-RMB*Q%
    z?)+_V(PY=H&B@I-fA#DE-sBp1J^{P|FPePpx>&bMW_=>K-w6GvyL}-ZjxIj>IXtw(
    z)py<m^PC0swtumahx{i0a1Q;^omqo-*77X$dt9OLc4B)ku0bK3OO!q?SgF_T=yTFe
    zsLL@T*zI30Zz$2efCu0<;G&D26}pq>qGKHjm3b&ruD0u;m+O`}%+77QjpQLS@0myv
    z@d*LTgdd{4=aSCv`__X1?G3`VFO-J;3vipuuM6^x5Yz>wL;4114;MRmLD#(@{El#e
    zu<5(`fUt?My+FUi<tGF|Z~uBue_)pM)8T;+9`04@1+Hi9_*v-vadPyTM94=c^EJio
    z{pCRDHD=q(DJ6>`^Ji#_6_=5iA-X-eA1Sd-PYL``<lPNd3Z&{PuG7{#RfkUS<!iV3
    z#akpG;}?OZV2jNNEiOfmimn@>iu**9C&YEea#R03uETp&b&(cRNT8DBx1b`|Io9Y-
    za!4KiV|DMkZ(ej+#&6D`)ZBqZr?`f10W0>EryVn5#3`sC%B@W|RCbt)y=1!Rp+83l
    z(`BMWe<PPpq*T<knqM++Cl4a-9n8|7+n+jy{t7*&Z3&GZ-`qE$#)!)?LYn$jr0eAJ
    zV=)K9rB9|?XM|CaYywaLld|!c6$5g+`UJ~A*W<o4i)b}@RVq}ah{%!Or|ZBON+Xls
    zj<w<ZP;%X6%i1Fdtwiz%B#oVQ`)8e1M}kTHIyNvB8jvKE3E4~!ASUOlWw1lXmt!O0
    z{#X)p#X#dua!Z@-q+BW4;d2{Q*!O{7qQ|t4fY!_!5s)a{>?g*Iwkf!ZLVvkH`Tjh>
    znqp7CQWKqsui>44nAqXe@0sS-pT4}Ce3t;lz%*sT(fn3A`lHyiq&O`-YexYK;Tu3@
    z`eQ}krq~eL>r-Mm9`1hK?=8W7y%~}5PYjk(W8!xW?Yo9~&4T8B#<BJLe&|l&kyjnx
    zOJA@B72IMQ{TU22bP%v`pgTK+s$<2(A>9nonF+eHZJVIjD=u*S4B;;hz*YV$$v>bS
    zx-TO!_^;IUA=`^f!d#v82mwrJa6^fk-jV$9tl1%gWGitI&dFxaU<mHbYUstlzR$~N
    zVe-WG)Ihw=;O>Np3~r2K2v*!`o?Ec!^et7ls!mo6ccNK^FG8mv_Zz*zH-s9U@+aaB
    ziGtzFXoqv`Fb!scVw6e1MR&Xo2x%y^0EX>(ss=Kf!*c$1o}4T@m`A^K6rkS_7Rj*=
    z#|%Cn*SPlnV*%+}v~U59N-9j9-eNkt_4NTm&f<mt$ttCqe=wvWyylSY87C8IEB`yg
    zi1kIrc{`z|$wF!PtL}cDnUWcU+vhI{{X7|+z0^pk=KTddEve5~&MQARNBgGd^yMJ=
    zEjNiECD8Nyz3$2(0UAlajxKRx;EqN2QZ6Z&03Bx%-We^V_V&!FK7!q;+z6ys;H_?d
    zak!-4cvFv<L0jG1()up;JmwJPN$n*DTf(+cYYwq)t<6&pb=yfX03o<^oJlTuc#a;+
    z-WFvlvmg7F)MgEhPUyb#A`HZ1LiP0A{(;dZfM)$AuKp<MW=7bdDew_F0Jf-upM?XL
    zwu2&10Vjozwd20j?AvVnA<bu8BikfHC@t#~Qek;{FG#kZc`QCv5GM_r6<U&tNMc>C
    z9^s#p^)2U6D?8!h+aW`B$yxkgH~oIzY(35%%C$|TGjeT4Nc&CKz>IB2PTo)eTV3lr
    z;!{ohR-44XZ>ju{u#Z7cHbl~xKpaKT3_QW7b~{M6F5`R`iJ)B`!rw-S{4oycT0pP|
    z_boz8esjIx&waRhmyH*%<xj~+^3QMuVQx-gNaPhI<-6(QnArlZ@Y>#Or(@!QpAc)I
    zH}bY6Jo|Zwpxn~@w~~!N<qAho8g?8#Bn~FK;iTY@j;Yl>ovwPWsejdfgsfLlWJG8u
    zC?Q9YL5Cr?Vd$<g4tWsQ;h|)|!aSf4`g5?G-mxrvtJE<`nWWGEw*l%N`b55nga2vn
    z%S$H0>XNPvwD%|Of$hx;Pzw2vhe6QA9KdGHQgy?)1oFF1k83<G;iu=teqyY^Y~H)=
    zG1=KwAISQ1j<?W@oxzyVx~@{R=b`pDyQq6WtbQbAs!?DkhCw%CXi7J7{bB$I*#a;l
    zz8O!40%r3oz|JT*Mqpo--DLcBVTN~GvPN83ga1H}(;ma~WD|w7AMaoWDp(YxB>+L2
    zKu;ZSf%H8W<<B!b!$h@g!?2@LZ<3rmy=_YtihK!~iGsxZ!EO>0AZ9-nHIiruuO|ay
    zY?{PTj+`Zr%GcS2-kLd-JeO!Yme8wb!gC|B1XzcSX-}wsf3TeiXHT~>7L)YKHsj$k
    zL{(-tMn5&*?0#0RCR+bBA1|_W5nw`{PPaE+<fiQp<s~t(AidT<&S?#m3-euNQ)d5V
    z>GyBB6&8feYPs0qrC>yB6vi$7p9n>6pXqwdQ=I#3DlLPD=%|9TzQ6S|V}~uEN!NP`
    zg;N5P`7&cQ0-;{|*FemuNFjWGGt-~$ykt4S`LZ4{VA!3M2rc%k$D_5(?Mj@wOuVZd
    znCrZ&_Bu$G8#ms}6~hTEJGYtW0hVpJ2NDF+@Qqu2|J%;y`{w|XjSaiav<!Q*)9OTL
    zX(n$YdPk&svd^W>oe*v}8Q+i962H2hl`zTpH+?0DzH0u}@$GJ=@`2UA=}AASqk?-5
    z_50?F)NiQzDgKb*o`HTq&=MpnB<lV=2R*MC4NIcu1(ys|RySrg(M{@4HT$XU0V`1}
    zrZc}CB^`qon5=F6{lXb#AN7bZYX3%-_#TJNZp^;lxSL2a%{t(mhKp@h+)(OMR`uLu
    zILhyzlphgLc{tmt+MDMd?#8;*n0pICk>6Yj;3?_(k^B7jVm7_Q0_^UgvPJm4*o_Mg
    zX;1t)5Hr2)ZvHCyLb~x*BcH81s*AZULweC}JfOv!x>$c@{G<}x9cSciJT(oG++W1!
    z@Iq`vAU&qh%+ND~k~39@ot2ry)F}+>w;TswaRim}HhkC;_YG<)ZcCGEG-`!nco|KN
    z1{N-gI}=1Wda^}9gJw!*b$|99Vpm$}2agJIM!~f(2>Ifn{(U&?syvkkCsLuAX$rR+
    z+lT_FV;IpxFV+L=3gzbKCcHd>#zAn1gS9o8evD)=^oA{H<d$wjsy>2w(0BzZv@y^B
    zQcJ&lv2>Kd9X764LLM9cTR;=M3T<=hx^#<DbQwZ6!p9|EyU>!B%xW>LG^%sfi?12R
    znEAvIjbUR{G2Cfi@lLexR2mgOTWQw0(mTQbf;CC&fMmyouVf7ljaNDQ%rWUhI{$q*
    zAI?hfO2xe8vc)0ZN=--jYFpRZdAV<`aqH}LAW}=c{rJy$k}L5nmT$ONI*nDN4@tGQ
    zbZZTl)6N{H&yF=0qicl9aIH=Fd1y^jS~J^(`K|gx5Sj7Bmy>SjLsG56TF7KaEnv6{
    z=3uZ!3zN&#wkGS?0T%nXJx&DjMMs9WYeYb($J|%mWfxUl820|@w!ifUf?J_QX2BE;
    zUE9MDn<y$a*lPF~=qFd}nQkT-N$Ihknug+|i<gBn3gLNfuB>&}^~-o1=b!9<m3Sl?
    zgJ+!}H#!E1;uiU$p($x!O3(icN}-Y$)t_0moI{&)>X&X%;-rWcSV$>v+yYzTrDnT^
    z37JoKN+aW(7nqGka=i=BEifErq@mXLlg2tD8+X+lX%(c3f9`-}Ts0kpU8b~{J2>dp
    zUvnl>5JiZ{S5C6AHzXd{9scNeDL!ms29P8zyjNTj*O-l_n5U9jlcPzpCK+-O)tFmF
    zv#<$+N@&o(QB(5+i|U_@p(l2LZMyboMUq;6jFK`AU-xLR(__3QK%RlF!mc14o+0*=
    zy4Cec=rt%iwADs{$c6D-X5&@`0=&OLzTklC{Jk!^xwV}4Xu9S*&aa5{XApys!<D)_
    zC!>yzmS>10Y0FC@DZ^gYPWX&gcUswm&(JtTNKF7R89>H~hPh0d<bde3W3otla81wx
    z(MjRcfvLF69W+C^+(lcxVltrxZ@I%>&f^qHIb+$<0C_(_A-^yZAWB|Y%M&&>@L(*>
    zDa>^MdelA1zU}0zJv1l0kCaJ2$$nppZ~ngZK6|m5;)r6ioHwivox^hag=!HVUTSgN
    zmODm-s<uFFx$$WT4jz)|WQ7E}5PSHQ40A8Vgo;pgGo!BR=6s@>Uul2BYcfF{(+(jb
    z7;p+OC0xVBnc^j%NWh=OQ?p-JHVrx9aco_H{zmw;vEOYZY|6WzjEwzl5PLJXX+3EZ
    zkr8Au8G+e!SX*8Vh>1h^VjLF`#hPd}v0S~N$Bs1@#*f5L5Q3po#}N4~psc4#_Q=|g
    z>1Ux6)TRtpK%9VVDjgf|94%^2vEVG52uL4A3vtDFTGMfAZY-AYS7h@&ZHvaE1_-R@
    ze*8@qkO6p`0eUd@m7V~;PyVJe;cSAaA9=@&KcRaeK|saD(^O)j(r<g{14)nwtAQ-W
    zgP0dlr-MKF6|Fh>w@JB9=b#y2(?l|%bx{{1aMwQRrpxp%#5i9gAEr=^V#@ps3IZ5}
    z)vT(}|7@&)DO7umhdZPz5PGWOF&mxaz;b!Ad(v&@_|>+Rj=kT5C16+Sg^F7vC5Wkk
    zV2sG^9j-VKCJJGmB)iXd2Q)CdTUNE@BN{p*aTs!d|1=vFNLE900^%V0gt6p@|0F1P
    zr18j69dp0ted{AdZvd^H4z$KFoBpDWGhQPcn0*olb(_<I0XVXt%?IL!MmwnQ?UN;-
    zc)@i;8D-Y+oWIcV9YRbkYQ19%YA>2haOi#VB8*q^PyTzpU#^|PrSJfA{V2Lc9gqgu
    zIi7=}u<<Ur@8!La?^zp2)f2Qk9u~WD0b!yo_?nSD*x0&^gw<|&S^T!#hkwE^&tNIY
    zYj`^|Q=kyL4)F|v`<7=D;rZmvVPnO&i>5v#{Rwr4GIOY1(HkK?!)f7lh=e!6F;VKi
    zb@fUWYvH5}HAk2f$-lb!fC|$Qa5v=Z8w1Ihq{fxWG^i2U#xzu;5__TUr`xN~;1|HJ
    zE;6kE^T*G{5#<=A5<^!uXVsaw`njX~L2v^0PyAPm)uxbt%<I;)D1P??TN*R<bQA{X
    zx}h8rJPqnp7>lIP%^s>m98hir#Zes7cQJF{#D)a^0zIpQI|%M;BgcIj?$E%7I@cLK
    z?7Z)fnZI1OO(z}lt<zuL%W#e~S9re<Ixmah;;Dts`pr6aGmcvo<hY+TqHNB7`6>|Q
    z6esT@CyLtH-2~8OZ`zx7;#Ppao1)?p6n2BWEURvK&b=^-BV$T-mG9zgQ$p_5BeENM
    z|8{_+KjJ!25(#-u+gj}Crg8`DmSdLc>D&Lcm)B1q<nz+Z|IADFu&^VLMC7o%jF(v(
    zjZKgS2f=os;ZUl8qkkv+MG-D~ORq>Xb-fhBeYK^ei^|+FV=_S`1jN>u?)}%^K8piY
    zGzYJ6rf(bFQ5X=%K|%`jrPAlu*QnlW8Ib&wt>POK8`yTJt!1lF{Txl*OXaa6In$d5
    zf-|XG7KPN51{<n5C)k_!p!uZWb_T-p?F(1d+k8#Ujv2-SX*If8^xWWlH|DumjI*-W
    zDs9o?Z-`!8dH}5bsJEQb@Nnq)fo`OvC#H>L-i+6{Zi<sap!miH?7mig6A5~tX?obp
    zgNojZ4%rJxLmZ$O0>)4(EfaF~lZxsH^W{p%T;M8yIncV<zTbK5gZORGw?+I-Zq@=%
    z*f)S#gJ|B;odKPc_@)@sonY&sQpFF5b-zm5f)?G~o4ItU#+y>D?i1itFCo719*oyF
    ztEuARGYxl%vemV(9n&Tt;u|^rgqX#WF9oj3H&dYzs{0Z@-aK~)6rS^sWgSdA{~mHD
    z?!VveU08>k<$9~=tl*sJ`&T_tvRWBbU`MytQF&v<Ao_Ay>X*?bAkR^9_40JWS(WB!
    zsbe={L%3+w>I9FwQPbpGd!BI6@N?J+NpPipI`RCL0sV{mMch~CJ)|q1*;9AB%hKaQ
    z&0<*z>O8Iee2O>9XB$M>!f%(K&l7l#UoEt2-7<W?UDj3IM$M5p3pE|9R;9e3p**X+
    zy`KKg3#&nRa>>3{YH4>!nb|9~{led!ceO0svZP|NYJ&Z+4icp$O`KMLiCaz9>5h|i
    zq-X#3z?X}}EVq{#%TSx>)%tTN+*1jkUMiBFAKE=fY!}er?!kmsh~;5qrnA1Qg{L{$
    zPcxiV_m;>if6Fi(+&D3(U9^SwH~`E=-7iCYB93rsMHW9QK<p#8rIGO84Mqo<q#PX;
    z^R*%G%9j{V&u8u%!tKBe?M)T`Wltwh+_fG_D{3|lRq(@r!A${+ldV6Vh_OZreTpuo
    z-4ebVm1U|<^jf|Z<hf9;4&X?2Tq`|j;}&~I*3Zo43+tS#rsc$+)I!#b7{f*LnYjQA
    z_+gm55Q{Ojq?ME9?{bIBImKTw-3KO3K!!ibfRn{N<j&$+Og;Q7ta5S1cf=yOFyTiO
    zCQUR<ciKp!{Hz<U1)RNjsr^sX8Bi96^5~iTn9HoVezJz5=%39v-K63V!iE;65P3Qx
    z(iCM_@%$<Aetcu9*7lGMd5u&5MVs+t`<Av1E5SF3d$^2uP4nG5fR_f-8>8DcxicIh
    z!;nW?JE$+eUl!5`J`}t-f(wSEFVjt#Sf;FCU=FTA`a)}qRL^q7v0HP9*LC6iQ;kA@
    zV><f<@6x##y2U(5F>KD2#n;g@)|7?7NFjv{3BPEjaeQ^cR@4fZcq`%T*wOYBI6$oK
    zAg1s!_b>TkKQU0xMWiDFEcD{RotQH~AJ2_9V=(i~w7P>dDcR!trL=X1CS4;3X$qm4
    zv{8DAQcerRRwfr;iYEjjMPN^zSkR$xuyw0OM}^k~a~}irSVc0f{LpU&MMp9=G+Db5
    zX0D56A&R(ZA=o{5&?P_{gMIR1$z1ue1*lUp^EVz`X1g~<5W+rS<>-vtF>Z(6$%EbX
    z?%CEEG8?m-gY8m}K{Bj)^6{<Nf*t~9E1?k;9zseMdDy?g;gY8G&_jBRj5y7Ih~^vr
    z+IBS4RB5z(G{uoW>4<?CNfwOSg$;+FaycXO4xDKugumAl8EGM%=}iA=I`=<^y`!;x
    zMc&RH0|)`ryX4`0tlI~v4n}4J#1kG<N(le}e8gUIATdfN<*F08B6OcXWyRt^5=g7K
    z>Rhj{bmJVZzlKT~#!jC}zI}aENf|uRk&VU+<G$J58_>T-HG;>(<T-<zObjK7%1>s?
    zBPP1mxS9U_5iIXRl*%_t!#py45ctLPj&Cr?Ce+R<YS@A#HYJ(9rf4iNHZYw>Phv(a
    zdDPt|ab?ogt|o5`_Ke`lpp?tiHs1Q_9!>r59C>Uz9i_(W$UD51x5Mt}lQiv#Hjwtz
    zG6LYxFW3jO_H{qSJStk|PY5<rv`buNGmrX<CZe{(Xsxpz!F5#U*P?`KPp4(4ftYm&
    zT)&N{z&CFDH$)Ob;#(V#sDBMLC^;Q0|3}_WdjdZv;~w&rcYf}CwE0i>b;Gx&LUT-I
    zkh6mR67Jyi#eU@NnE+dRz$o;hfl8XY^sHbB7c|-Db4iJp%NM^rmUFgZ$X0{`apJbe
    zbb|wHvr$*urkITfjfauw)_L51Thb*hU#%gntpk&1>(E(8vR;wslJ3$N#Zjj#NB9En
    z6SB`LES7T9OSU*%^S^`uW_s%i8~9pyW%Tx*sJ~c&pW<(aw+v4V*{4baf7skSu8Xs<
    zH^7jgXF@8xvS{T)3ANcF&j++Yv5R%CF^`>Ndeu1`V-?MM`Cn6`>~q#nVy3$ZnOc>O
    zK7P)OdI-0o_Vda3>fF+4wY^Hu`^MaEW`(?14c($;R6Szo@X7O0oi0j+k>dRdJK!u@
    z#(UuWx=mo6Lz2-mDlMYljVozZAZq`8^_z*-uvgU{&|A3Q=(Vv%F>}@q;1$Tm&q1?2
    z_QN$}CWFpRzKcn^L4Qo##v0h;d~_m+eoDdcRd3;I=4b~VpGo=}B&BacThOJKmdMdP
    zkM3({7~8s6Z&#PM+Z1s!^;_>eYm&)~en?(bg~vdKZAi~?vyCHWQgCrmn7&1kR)V$|
    z$z{64!#z<FfEPn1=#xFkA0Tb9r>#KG4KnOd6)UOz38sHRn!#u!D&A^<kdD1ytISF;
    zCF4l~5p+^WxkJK*Sb(B*+HkwOC$nBFef^*KekpOmtqm?#&iom)@T_~v+~*Km22;nN
    zDkHQDY{1aPQT0sjzAzxP=v7Q>kG!Ds2$sza?7D<6aV~Szq*Ix;KmQ8N*%>ByPwT?{
    z62(j?krQ)52z8;8Scv7k%&EWi^N5~FIWdFnG06SFs!;NRwnlu{BqpIky5*qfG+eup
    z`k3)c)FcaTBE8I8kM)m^xP+9UXU<P~F}y|IExv&})S~t1JD@f~oQ{4DYdRh)fLre+
    zvd{zuw%Q~(KL!!(9P%7$6nlD2L9lPYhj~sc1$Bk`@Q*L_F_MWAv=;3jE0h(xw&{7J
    z>oea9ZF1RT%tF8<d$g1?lg{sI$)=8?L9}@A?z_pSMX^Xf39YLW`(uaj=)KL;r5;BL
    zZu@X(pe%kk>~GG6JasxWtZo-*TrWT2PV&81POh;>uFKT)je1qO&Psv^sa!07H^g!y
    zEOy11>o78J^UCmWj96>lVud-y(7x*of!#jk{zco4vr20vgS-Y)X!mWtzQqYO67-MX
    ze$Lw)|BX?%FX{dlth0}zIaqegH*K`6VtI)7b(u`1^2z4v0R43)xeL$d<-x)I&RO)L
    zYLKwdDKMwbsa1$S_!Lgc&(Pm_T*aaHQZ1at)JGhB;?Erk9|XtkK_9}|HnHNpeMW5D
    z*~Bo1CQoiO`DEmX6%hn~gjrK6rDG1s`H@2#Gy`@}_u|B0yfT~V@*d=fLrtX^jCBfF
    zOTULsAcC*q68W)x?5uXS2vl#aT}Nb6$@NrKnNVtmqP5L=>!l5ZbA!A(1)*FN4P56N
    zPk7;IPpjGBI~DVru#kPNx<)Fnjo8LOOKH!`k(*a#gtnoNi$9DKJL!wFC0|kXqkyAi
    zka41jl7CjRU=K)07ko~j+i85urcvuYp()VkOC{J8z&TnyE><&uifLTGY^SI`-B$2h
    zw{;OhPVed+3~v6S$QC>Pc$SR-jVFX|rtNKQAbtziZRU4X*z=1>lm>C<sZiU3mL2`I
    z_m#>YlIAfNZu3@*FG7_nC|?|wT4?zk74)F#npE7^BEzkJtZ7VNpzjA|*e1|Ax>`-+
    zJAP{a-Q-atS*<d>jl6nLSlhwaHx;a{)>t!g?nUh#M#oxH#*}k8`BRY~(O~^dl{&d3
    z{pf)FxHh@t5DBg&ar!1O%oe6SDqnAicW-8e3`;G6U}t1ydewP)e5`0xn-TB05myqE
    zjOyY9bZqntslO0LKCpd?vjgLiR>czi>t1<rv&007&n+oY9E!bcvqbezZ^L6CamF~N
    zo3H=%0kBQTEStx8764SCu3r?nY8;=%L!l%31NSpuQY1mIFKHe$TVQw@0c?R;6Lpw>
    z;=zgHHJ!G-m@ITuDbYx`(^M>P)~=>flZ5+47-=9aK>(!A46GB>*Rb}~gLE(hAuRnY
    zA2~1Pg+3raV_GqRumw*T1!Qn%@k%W!LK+{#G^Z)oP!$I^Uz6*Q+Z~$Ax&}7SFXP&5
    zE+mcqzU!t=KGWNyTud1?MgIBYM<29Y*q88c_uE0##agz<;~0a-`XkVn4)FaUv>D>#
    zJ;B^l$D6|=PjjLH+~dU)X-HR}nUSY|Efkls>Rn}Q>Uhp2ptyhI<vAP*)fRzVv<XS_
    zJ5`)9wr4l_ya91RQ!|Xq?K^XUM=#+(os3zxM5y#Wu*6=Jk<OL|=9CUXpF;m-hZw7{
    z9nNcVuvktg3Wio%-u0#%rxlWG$hFWhS&I7v1wSY>tQoPvU`v>DmiLi0X*u@B%lsiO
    z>gZ_80bz)Nf{q#^sgnC+$cf<10*vffXO!tu)t3JX-S?OVpJtpdrXL=}L&czyO=}nA
    z4F72qcS^D)TzUjLm$QrxH1t+oF2%ii#2)X7!$vwNxkqO;RbU>KcXFlLM5MgLa}dte
    zBDFx<4eJh}sXM<s3mBUXM5BRhp-_E>^GU+{G>c{9^28g(ZN=ZM;@}<Ngt7{px_uGw
    zoucl>Wu^|ihMCA>KY2@U_!da@b3o2o>On~F54X%Zo=zp|P;&a|?irv;B_s!HETtG9
    zVl83Y^O1U@6@s67Ar4;>N~Kq2SMy}c-9R$-wo}zlnD`l`+Q*bodp*4k3IaQNyS#o@
    z-=+A8>J)i^--J<vsIcxOb!N3Eji5^9CDkcb!?fv2kb7^<$V(@kcv~!gQ#IQtv(`wM
    zkr!MI-nzJQ=GS%0&CH3hqr2~;r<h^0&f*5i>IKw`5kp0y+zQxI4f7KE9q{vn%H{W1
    zqvA>s-g7o}c)Hvv>-9kYi4N@ey;$tY=Y&Cg(3mK>;g1FrHNm(BfOC^?;+OoWIQmyD
    zfIh=x*vLJD_hVqNi$dO*l5ODqV8HbPLQhPT>*WD`ug(<1YKWe-gGW}oP?p~&p>L5r
    z4qN(fZsNBQ<Sili4u3f#j;=b?u$|RLzN`r@VG=l@>KwXihoXf98lY{fdK!5|r4iIk
    z1Ip{U_j7CCQsXx9_j!j>7bE}W8Pgh+%;#r4V)&`LFtev{$bEy?ab<s^$z$D<Vwa2_
    z8du2t#XGGF>bz`8sHu3<Z%kZ<kB6j{wWnr!m4_hxH~B+d*-B#!D|50gs4<nSvHoUJ
    zTN}<#_v8HXRNjIo4?9QC6%UB-MWT8ae`&PM_^OTTWqM<%lRGmUNF!wthK?AH4Y@`)
    zaAa*96ysWk)i}Eu#1Nn_IA<hu`Vlt<=$huDVFocBrtmn4etSNz^t<R)jPTQF*p4Dv
    zG+7~OIK(~U5u5`6J-PG>DVWan5j5`LgM5#n0gdWoKquJ?56RLtTsPb%+z>@KN83e?
    zR|us<b}h92R43nxCbDuDQsr{aZ&tc^_+~bD%O`~qiAbJ_rB-irD)hpTLG)D}Jha~`
    zMJIGvMt8HycSn0W6eHhJCWb{u2W2*vJGO(U{s?m7mjNZBQ-<ioafdp2_=yDfxP<^-
    z6iWNA2|K0vpX+KzAOKA@badGQU(Hfz*u5vvHkG*StW19PEtVBsyR|``hG^b&uy%S0
    zjYhbAPP>L?m`N>25)F2>mq`7wZNzg>by2hR3CVtsa{!uwG5puP+buD?&LgVS)(9WO
    zz0QN1i}s<{9j~{&p^HkTt}<dY_;y*ge4VKbRZvy$(KEB&lc`<W_YU(_PZ5VVH1`VI
    z*2bFAIL?gpHP!U<-}TWikL~w-nQNzXtY`c+Zoj%Rt~kyT1v+Bl>)XA*s}Z)!k|HIF
    zDi!N)$DP*aCpaxM%1>{5zOu~zf^7WzXvsKl=QHy$6O>fprHkOev4TEaE|F@e?e%;4
    ztdVtAdS3<fqD*U=y1%K(oYdd>nGW;##w8aeJ>zj8LowU>>43Ca^>xN#qIREmhc@%e
    z1j9m(ZdoHb&q2}6r7y|rUHybTgka2CR-Fq9SiNGHmYCn<m-Emb>P*?!IFaMa8)|Q1
    zmod>vXu=Y*r7NQ-Yh<a&z!xsAwfaOjEG~bk14c+O%5%Ztu(_Z(@vPkeYki>7Ape&l
    zsvAc0aIwOR>=Aq-PH{@Z(9`~BHP0JTH~%U||G7IVMC}uzH155LDwBb<VzZ~fi448f
    ztN03sahd4&)P)<+k@nI9Oi&gFe=h+fe8G3wp$kh3o?_Ml8Ha)DSCm{CaS)$3&C8W{
    zP0aF>)K(F;pvkE7P8dKVA&XKC5S6M2nq<ZBcp3)Tng3M~+zf4Zn@ihCkro1%RX8<U
    z^6F1HD+VkBZYi!Q3h~pTJZR05I6{`Esskc~4&+SB1?ik#c5Zy+eN!V6GYsmC7+rH9
    zDRRqOk$X)L!NR6pl&!q#MGD673-wxzv#BCiaMxEPdJ!GX!nuI1o)(i;b^Wt0IfG;*
    z=>qEBeT@jBIIOVNzg5<Ca8Urf%eG0#5N{2+UB|=dt8-+{b+?5$kjoUx)XMYA*;1_X
    zA@-2;*t~lK{<=JcLs|2>H;DWF;d1Bd<Tz;ad(1rzOMf!*9#>CM6kEVVeTUIVkj?Ep
    zLf|3O>V<5~%=|fd?0)b~`K~(}O;rlM(?tPyqDNXceRrp|Kea1KXIfN7U`zj+KOj~x
    zEyNqX@293nt#Fz!&yu=;)=Y)er{gHIg5mW3S7VZ!j&;?trr{@vBMy#+RrKQaO@Suu
    z`-RNIq$f2z=6)iv$RD;4TlIR93?f<-&E}&kT2Peq3<BmfY@J#MDlE9t+_3hjHSi42
    z+VfbSM0wv?6zmZfsk_UVrtii+$;e}<oV(v2>=H@dYN7xsSCi<8ta&4$Xs7jD8T_EQ
    zr$P08OB@4%P(sh<nL4+oaJtuu?w=a!S+{?8Khk+wyY(@kZTk>SQ25jHm}PRC@ff2I
    zmTT7{bZ?`Lc%AnLNJ8ul_lL)4W?LA!gg&?^W~`_>{5DW_N(#+vIe*N!jdU#CP~jO3
    z-C2Q)E^Xu9%@X$EdI6CV*;@s!G?aq%d|)m;(jF*`z7a0HrFYC2<)2@ey^3s%3oB1t
    z`*%uF4k+VBS&m~dns**w1hbUm5oH=```6=2$dk)YTCm(*>gg)MsXb`|n3^1)?ckQ4
    z1VvJZ(&Vh`f5*q$T(@-!%{BdiwhUR$3dlSH=Ru&mE1Ck_$r@$l3G;QS@}M-V^5Njt
    z@_e5rgpA+>00KNmDGNU?I(v%{WA?*&2CJ<ZnLzfo0@$geHZZ9x88zv1a{a&Tw?R*u
    z`Bo{JGtsV}q`b4(GSAb5`%4Hn`SKk-PVg5LosWJNd65i)%RtE!kLoKxtVo5L!S!4s
    zuN5FP#Vhdepn2iOv!cu!FGSA`2l%P$aQn(bs*L-jh8~!(<{`1VsB0F){qWqIrvp!_
    zmA1JtY1mxpXu&W;$Zhmu!IFGlO5E8&=iq-2GWS~-Xh1U%x)c_>g5Eg=ZkvwhtVtqp
    z7<-;lR1TOUf91nAI(3w`&R?c^s~~cUkweYKfos@DN*A(n3+njm?o}uXT=ebpB)ZlU
    z<Cf(8Aymr@f581BlaQn8I}o0;^Bwnx=2PR|Kyd%m4njO8_j5JpRBl2WGgevMiOP$+
    z%5d2f+V#O~R?C_4nH>BELFMsKrNy>qvgwh&S-u3s(VD0inufDsRm%Z*%qNMVJ@Qrx
    z_6110>M=SB9ru1!h+*}|zq#`qrmqYYCw3jB;pQ|6zmAyft|ajgdIaAU@bHxlW{N9n
    z<0PB!bu4DPy{r3iHW)5?)BJY@vkJk_-<7A>%c*%u`+I&wn?G3(po({z0HVb1elZk@
    z5h4g7nwYb!<rE4F1^#~xSFnPrLRVb_gtd6uw6%Eh6<{Otwg7-CN(K#Pjmw&jmW0T)
    zXgKztAZe>uAyb<$YT?m4g3x^-kUdv>L7!>Z;5XM?n~Vbk8)H6%<2U)U6$4=+IHA9q
    zkKm+9pe`wrun%a6nzPMvq3_%y8Pf{lDq5sk;Z)NNT60h3^WsQgcRY|Q;@N9!Z>1IH
    zx8I5rHE*dq+8gWxl-mpP&k!$>WKIIu2XtUO|EmcoruvJmvUlJLm!CJdnc5;eY?or0
    z@zeL!5}BV;4?pCWs!$4y;=qfOs7g|-qd8gjOC~pG03(vU%uJ%`@@CcPO!5%>FNBSw
    z6ZTMmFRN<NFuQUh0fvJRZoDL;HGd{pU!+I?>QlHT*fZPgPY{C?e_N^euChX^J{DsM
    znZ8m_)pXk|>+os{W*#7}2|5ny$qzQWd+1rToWB+x8dlA`t(ozMDcqz`<9Rd1P@L~`
    z^9oFLyXePQ?Y#0!!TN6xZGj|ho1%)NZ=QJ+^?kQ}CUj;rb1U^J<ZXUW>dev{oKayF
    zxC=d~WtPaNT#z33>HBGTLhy?T#(&uuB6TG#l8%+#+vHZxFlkI#BMNEdl2sFElN8G;
    zyIL|{r^u=$o5!kotEjdO{#7dmVNVtAfaqg)?!J1pWW{&r%jAt`Q7qqY<2~^49&ZM1
    z%*$b8XxGc~U>jk6r^c2!3!dd8A<gvc(kBo0av9p!0=}H<eEFe5{KOC8BBE!{{oJ;2
    zZj!drg`%*uG@{)1^Ng%c4&l*3#CqOtwVlDC&zMt$_6pWKra5lI_AQ-g&))ZQ@*k)J
    zLEZ??oA%wo;m`^%D&<&?{({LAkao*=6Pr<i@p(#&AKj?Mg45OG$C>*#>4+j7cGWg8
    zwv_n$E}fJ1VM<S11uTbYvWhN4*ptge&h3*-A;0?JXCf=DC~BhAQx$_e!ic18K}4E9
    zX8c(Zf`Aj3sg(D_?i#jK&~*uFZ8}rUzyWFuX>bCarFJ_{pj0eo4Qoue{4`uSe;7=0
    z_z%8KuNAfH?b2(@QY1-Wb|~o&QgmBE{8ek7M|IuF*xApuWqnH71c)6?kqy@;HsKP%
    z;u1s<MG21UmLdaA>|Esu1!Y{+ZkC?3xVfq6nCvUV)BOB^dh7v3#Qh*P`N6N6jLj`w
    zyF=ge=t(^xoprQ3)TuWp!Q$ZR)*8Ob9u(~@eXI_VJy`pODe><LAreqQ5lJ^bG>|z8
    z0SS-0!}FRBhycuqf$i-3N+zoVW|McF`gm>{aRU*qJf%1yrv0eh)`m7)zO5vx?uCfG
    z{X<G3Rg2AjD?alIAD97^C9W~O4j}=5p26pjC!2*^5Rnz+r`hie_|l@}vd75gU_N2e
    zDA3AqL*;H|NyJ+BmI~>5MZHvX6NYJ#a}c#bi)jfCoIad^m2G|Vu5eGW0*PqLPHq|<
    zYtZPJ<c4UhBqpc~mwIx<;=bFuMFU=VMQF%>GYRz&l0sA1^|<OSoly9^NBa>MH^@O0
    z4QdhQ&lh0CPX*H&C^k5=;}(r-h~le2VK$jOoB|pvhiKu#jcQ~2iT%wUv>H>Y@t?Mo
    z1QB_FR=-CW;mLbQ)Bah{+85V!)L*Jq)z=lAfRaD6KQSJe&?V?&oH{xXDqTc8G8&C7
    zm~js15F@YJ&R2jQ>`sNavD7wxHXHnjQEwk#pS<}ydC*!&cGfW6IE<o5q4?qC8c>be
    zS8hG>`^Gli`Njdm?t}RXE4#;n__)C2tM7%hb2^HkxSu=tM8C$3&4E5owy<L9OO!EB
    zQ+u6CpE&FXuvPR%CH<OD%2XUAC%+JQtJ!NQAlY|tn-bbz^=4f^G*MdyFc1j)<2cL4
    zw?f{CeF6Ga@(W;VX}Xl)I?7=lfELMH=x0}1rFo0#iZn6^IR6N88@RkZ7Rex!`q_Cc
    zWnyz5qjr$?JxHeerF9*9Hbh8)bLdZMtJ}L!d3$IDYKg5t^kHsvAOZj1XDNxrev2#f
    znya=D2Y~sz+CfaX3ZFUtREEi$d3T%9UpPUi&<FplGxp9vdpW)&=5K?Ri?x|#_Q;Bd
    zm!bcH5p6s`YiY0Gn)lNdMbbe&*1CA5WjS4$a833M@6vmub~*$xw!j6i_B5b(SWd>D
    z8f|`Fn^D}RO{*+s?XNy!4U~}O%#Ek0`Zqbu_PXJf3ZVm)RiBl6XleX{%uda1#L&W!
    z*v6f4&KR-&Vx@R56OXyk<N3c2YY|TNKA9l*5zCMa)`VaXZk<cZ4-)r$LL+ac!xVB9
    z2+u}~KpG<E5xRcWLE!rNZJ+gxxK@CqZRD;?NU>D@hzOTb-IhC$?yqvwJEgh}N^2#{
    z7HsAk56oXpwkVWDYQ+peugUtQ40=vpOYx6YRK}armH5_!Iv%iTvnQ<Zua@~ON=k>w
    zQ7v3*P*w%o{P?xf3B!%fXaC?vPfu<6-i{VI_i*$H(^|43ThION+F9RFs^1Mn<<u)-
    zotTme6K|CTnAzX58ajlS@Za@{+f=80e)XF`5en{}<K(Prdav4sGP-X<Vw&;o1GEi(
    zagQvRjPXk=X&_nUXC)!QM3cpy-fxFbJEMg$@J6dAaesSn;Hb7$YPlI+NBER9gWiuY
    z;+pJnCYC#saoQ}Q`OoS(BO-;1>S}etUY@A5-_aNSNY{(GM}opFrE-@NnEDv6U*RKn
    zCV};@KV_=}xH(g_1@CLZ?zf%P{L3jA6`f<6k*KS|Yu}2nbLj(RlCvZ7d?bd~r_zIe
    zD<ihML7fsGyky#cBrn9+FA+AS<h*4dB<jMUSMD@x<-I0#yCJqA;;m^q=BG(gMkxjW
    zZH@}@=QL;#yQ>oIJ{!-_0J@bkz`FjMTm!8UtRbMDAd4??pc_Pl(oo}i&hZD7MjUf)
    zfl@zHsziJl`mK${DAmh7Lfjl$oh3CE`Eg6O#qG%?X#%?9GbdAK&foH^kADZa#E1<|
    zO-xLQsfl7}g9xN2A$0_*d~DCel*1IMr0aj+DLkW`Da0a0b9HB$mtmMv#El%y**1ue
    z2r$Ij9OcmUm*WsDLhVT(x1&6N1OUHu4J^PG#SfI}Hwu9f?9`KERR*~EtV<rk$M}fF
    zY5#t`m1NZ%r0ecxQdY<zA-gy1Dw}ev>j8lm9$|rhyW4EF;=84f;j9YDgWvNP7aWw)
    za=%J)M%dU)Ds#s(nl-5F<%7eQdX7@vq|~9}+22^mlv{Qvc3Sqs%(1cj>06s6%gIwZ
    zk0XoQeHvGn^~vX`%&fnv#;HbKu`Y|-LlU!u&_+C}X)U*uas2JLY`lM&5f#|Urkt~N
    z7HthfI!L4?NS~UAeD7|Q>G@K>!t3x=`+j6wsZmfBp$K$Xnf~yc#-@Y=?LOsl3-q}%
    zMQgc?(21<_ZID&1Lx*pRb3WG4SkpsSqo0Sdxt(b@(^hrb-h7#(#(&Hp6;5Xiq%ix;
    z1D?t1r-vmR0bN!~FnU4G^C!gVDhGgWuaoaFupi84?@lsW=0<uUf~MGLX|<{7kC8Z}
    z=0TTZ-~Gy(okW?S*wh6UGD9?M`oH7{OmqG)Fm#I@?EmF<4PZzb5juChZt4C;USt)T
    zDu=r}Atl(E#XUyZu^7$bFNU);OY3`AjwawLK>WtBJGx>IHAM~davT)j2)vnyc;_^`
    zRs%(vCEmmRJ9~CdgT^9P-)t&<H*QOp!0N0bwvq{Z{cHIw-MFF#?k}tYAcq-R*s$P-
    zI9e-J)Yx9II-ma!VSUKrCU@Q6@^Ot4uT=+sH7=L`*aio&prUlO^edU<3pg<_!VcGl
    zkm)DlBE|OsFm-_4n&eP&*>&XAewOw+SF`*rW&0bv0>znYds8NB{;-)PX!H~Oqj5xa
    zIB7q64`9{lLG|HuGC+u@fDmH$ntl`yY8ZnuIO#XixD%)1<{c-us9nmz)0o`Ae3G>D
    z7Vq$Uul$7(&C`(zIqo}hV86OhFWtgGJ9}FEBT_Qw78F12vWdSNp!U8_bH#>l`rO+K
    z60}n`2gGxU%dJC&`ez1di!}mj%cXvKJ-SZ@LSr;iV9=?Kx)9eH4uNWK`Ey0>LQc@w
    z?3L5nc&2l>7D0KITt4NhNvW$^NlB;XdfQv(Px)IDMC!9V++aye9~z*CZd<YI;HI37
    zhIze>ghG0<EF2KkmiH_+!?HB048c|l4!)EcdIkj&?60hlj#_u<`aplw@&x_2&`Ot4
    z{{kmwcUdQdp;m}eG*$ue(X|nBqcd5bg+XATV*iwr4H2Ph0?PV`nT_5(qaQf-6}jl)
    z)$F<nCkQmj=4Ym^N|M>5+xTPK=k+GjymA?-M8L~P5sTFiJyVcJG}ziK&Dgko**jxh
    zE|y4NH~sd(iElcdx6zB|<32D0q5@zlS<bGGkb_urdKzA^uuNFTc8d8G(-p?!AXJTc
    z5e*d_lWIR3M8fHe<m9FyOcBPoyDv-<4(L650_U<TJF3S0204-y%qGV9({oecvv)Hz
    zU3Di%BG=Nv%N(OD=rLrz<*!h@)N(C7a&<e_h4!}+^#=IibZIL)eC`c<VnT;2)5b*k
    z_WTYPixnKnDJ)i!Hlf=own8bNC0VhnrMP-hxTq^WvfJbNJo}856$}Y-j3$I!Ua<DN
    zp1CjI4yL%w&xKKt+wA<`@uEV1`N?6qdaBK^K9f0Kkzdl<77qYpGbY6f(^^((<ud@0
    zd7ZZ<P)r|{d46`mhyR@IVVUutWEl0<=!CJpTC$6Tsz}>5uG49w_;^ONDR!r56LU^A
    zNsFF#G)XL&-Forv_UpDk(&GR%-48Xv>uv2%`k+K0ts9uS+zC$slOL%+7LI&oXSH}-
    z;33l_mQR9TCPPN05iy!fRH|mCkbSlL+i%$$XG4f?3Gzf?j{@10iS639lM^n&dC;7z
    zBvgP7J9VxhBzRWD|0We6+b1u*9|2{YxR%KNS?E^nM8_%pFtzcSTn!>cmy?1|StU2f
    zO!5+x4fHPtdpE%GeGi28^lGIbRS+L%z+Gr>n%whQXq_wt|8gxlOEr#U{CK3(^K*8M
    ztTbE1YNtbF-}*WiZ6ZUv={x8?sOop8ZG21-Ily0xd*fCz#EI@l+S5L5Q2`lU#uo7W
    zR6|a)t4{N%V+^%Z1!n6+ixf^yFDqR}7wpYePKp$M)H#P_G7KEJC~Rrq!Tg!6`H6kM
    zK>DrCooKCyad7;np&mA$FpKEZg&1lBx0=8TUcU-A-QLrqWDM*A<T)9YVxnyi(N$s(
    zO8JI1fa-Lz-(&#kKw|dJUDeIyZ`s$VN!ocly3@>c`L285Gqe5M<<8*V(#NVm7xDRY
    zv=t`X?=Tz7nVM%0K|oVtI+zA=97xI(2(vKpEuK62f&t<SHyJgZU3WEdb)Zrq=ppap
    zS1kqsdX6=uu{3Jsee0_bG@S#HZIo28Xo^=;w2$IK(_=!cjZ3~z>K&}b+`S|hG6rb~
    zTyB<EqC=s2IR{(qXF(BvqX;U)w5~WXVs!-XUv5_|r~J)1<yDoU7OKj<PtDMqUZx0>
    z^@#3YeRmMJFN+B)aeI^Q5UwCL@jC_QAq4Sh>f`us(Z<~vNh>Z4b<6dU`OaB??KIX@
    z8I3CAC(lp5Ak(q_zGPeZrN(7INYvQROU{U1g72^!y=Smhw|Fx2OoZ<;ZJsUeSP90Z
    zz}#+Di{Mt<wZhQFUMy<hMMva?`Wv@U^ZBo3CDDyDpS%vniQ53=4i3>)%L*1(#%)-Z
    zRs#gH@vZ*I;jCRGG<{8;r<l*aABkT@@(|A3urARVTsQUZIS;fqT%*WG;Cv4H96PuO
    zL_vZT=rVxr$JW7pnAK2V<YAx>h)-Uvi>eP?Y2NDkNN(;QKQh)Qlc|yFt)D(ZVcagN
    zRS78UE$myypSPC%*gnT0Pc^Me=c|#=m{>9XeM&0Tb*}A960PEWK65GV?Y9}kCC}F)
    z9VS9>c(Y!&ig`VdgSAeeK_hr&e$oEtR&_F#jxJNow1|D);kRAn!g&kdA|d0I^Oy&X
    z-jTUpgN?C9;}FxLufQ*J%f?pOH%oQTr`6iFnMHfA8~pdTyBu#T(#k@uP$P{2x?)%c
    zEzFeiPl)Xl%CvC)({pVMwV6$IUgB6RlTsTKF|4e%{tZ`<v4u_tVXN*kln|1I{NVBk
    zqj(kC`(G4|WDNYfHS)ib=a;(Uy|{Qbpm6vgn@02L&mFdV2YlYGN3|?S1zV}VyT$E<
    z&W`lfE#`5{dE@RBwnSq`M@c7@+aWn2;lGr#QFWIP(RnsJZv$3X6d@_pXO`{r2EFuh
    z;ndD^u>$)Qq}{py(kY4QvPRteZLHR}!GIf(r-74}{*6|~a^9_a4cbyIK7#*3e0n7D
    z`xAB1lP=UR@F!QJA<l+x3a0h2<G=~8{ra#0&Ke1%)d}*dS=krFEM|UrEYwWjh}0Ye
    zXux`<Si!GfcBYW64sF1&iy?Cn0t2<ZWa(}p<O8X$VHDIqCgs9%6FD|L!KOR-n8v6e
    z1LfexhS2yrSWVA9D#KR3afI}2!g<_5#d-YR_XhFL{-3p7_^>^Jd(+*^yyFlzKAA(7
    zr#k4<gGn_q9tYM5v>C0*WH5*dVDmJWYvy=9QkY+jtC>jh&njW9{dX@@u5+`0>6|Jw
    zUy1XBFZoNWvo5TJGqX^FR@i`5PGW%S)5%uE4rQ3{<#hGSBwrK1XX7~-qU+7o(5v(u
    zMzo@MhBJyy1{PZ7kgjYGtMnz8nSNCG6`2S=Ww47FK7lJI>3F<;C}0s6Y{`%#c56Vm
    zA*WC!ClR_c7qoORH<WEv1k!WJ#Qzp!JeSCPx=OepyAKI8r{>E(!uD*U9IzPGmzvE4
    zFm%tmsI*tReJzOea#Jo7k8XnzPqRmO115qt6HSQiU&@_e!vxvGMs#UVr(&2zoalr`
    z^l0OL&xsM~%)(@I)${<TAkUY;W*2JQ@O=m(L6LWI{VWQQ9*I%>bGWxrf;XT<3fzcV
    zE7!>{R3&E2-I-BA24b6!+BytP8#MZjvt$Wu%&6{l-N~I&7|ZGM=?*S7F6X5g3gBh%
    zOBivuz9M{O3-jUB5G0w|>u=6gzdKoV=y{`FB4HMAWoL!Jmm>Tdr6?kbb7Ae-bOu2|
    zw0{@cNw~YC7vp1MIp&W10)q(YqeI7JB(8ZZF<_aMH%lp+)96`jA{3}Bdu-~uHyN?U
    zVoxJ_6&|6IS8KB!FzxaeMG0S9P;knFHFCu%c!fLVVxTJk=9`+8A~0JOhbw)k)WG7$
    z&K`~&&d;am770!AN0opzL*zXU1%z-J>;3fK-z8byO#WU%k3;JGjE+dp=J9eL$%>CK
    zec%q>qj?xv!<Y*~aVkTAva;nl_<J3kv8fM-t8-3W?ZL2`M-%!i-haC1D<VC-4wfBM
    zL}^|v{(qP{$M#B~W!<i*lXTp1$F^<Twr$(C?R0G0wrwXXw$-=yKKI<`{)PFWX3d&o
    zyj3Bewr-}3_hQkjsnJ?5<cF92=-km8K^*zf?Y#BO#&L-_JKfeeS9`lYP)I}71Zj$d
    zvQ>9WjacDz5~`HnP+Miin&?C;Qoug{x@2ofqTYN^OHh=;zVbw2Kh5W`m2eTi%L+AW
    zoQ2jm{5RU{VJWLQ929!NIFhGKs$nV7QVAgTxg&EHe}gQk?dD%7jD3*pHdmF*@`vmc
    z&ED)N*T2lptrRj~kwO6AtdUy>pdsyTO&_cuuXU;SM;qPfi1T*a#nSwl1m=3>I@FEE
    z<voyoWLzDfGQBH;O6HH|9)*kCr$t(&qt&9sW7j`DiJ*`3wbOQk;!@<}cD3TDrl4&B
    zT1lG~-_1;E8@KvntFyHL&4n04E<tR5f}-~uJF4}*zv?D+kIK72Uq}a5L!RBAHzxQb
    z_fU;w2M1-}HC?L7Wf1xT<JIR!tqnZ0jGhLPc5HSp^2ATWzRf%dtAiM&qlQtt`VrOI
    zsv8BL?!nu)hd5K2@h!Uxx5y7Mj~JY(5)2ZBRCoc5Lyrza6+C!EuGRW`TkM`QgX8U8
    zx~C(qUJu`!(zdN!rjYwq+$j{A{{LkGzGY2L9c~!AhS+e#kkpV<V}|avG)L~~;B}U1
    z&lFQ=Y*SW8UJ3_FgCFcCqPbEB<evXo)d&a-=J1Z%wTB%62jUvG4lEW#7GqKdEOUco
    zkp2h39!1r-_%U7Z?C4}V7Vu+{7__>CBNFdPdyHsXK~8t^-Al3zR6#LD{2#8Sjuj`l
    zw6^Q8y$i8dCFtn5Z{b$CfC{gBTn)a$eYk!TF)Ew}rWovplI!FPF5~9AF?NSleS5Yz
    zYR%CeA?b!KLxaa1pYTFTh{BdV;ipkbU(R#pkJTJ;H-z#|6CCO&hvY=HE!sdZ5`3MI
    zx<GLAAx$5+MM=kTrOP*7Iw6v}*U+E^0{h8T=siHJS+u}9pU^>|wfST_Ap)d3EFAy)
    zelCpAoZuQ09x=RI`mlTIHRHHolc90Mkj@W5Dz~2*eNjQN97=haY?jGisgHNyudweJ
    zTtgPCmm3mG!MC2P-)hVQ?OIy9_-21yq7t4h>e;<P_Q~MlY?f<KSEaP)Z=x6zyo$0e
    zL6y<ICQxHex$wW6>&CMybCZ;Q7bNn<OWY)``<$UIA^Z{Ky)oNVb%-+Zm`+*#*mO_I
    zx?2aPOMX4aF<5?EzT|iG`uh5KjoNQ7JFQ5}3Ng+*>gL^1Ai+A2wmR+94vuRY`QMo~
    zs7ZER%oyE#%+R4#SS;egMm{bU2l6JYshS9l=&kZ5Tbti7TZ3DZz3bBz4zYtkH7$>n
    z+W;9dWaCXk*k*s=LCyS+TY(@8^<D0{ef;z{%{?v=1ug5CN5NR9pHvetJe@p1|G@9a
    zOuTO@(`7<7-H%DM*rSrGHp8VsGgf=^`$okM4>2N(BE#b?*`#nndKM9pf0LYyos4Jo
    zYIB*KIl5BsA4Vh8g5_{g!&iq=b`P}P^06?NBWQ2>MqWEh^6jBu*h$%it8ZP+ZA(VY
    zrgF^w_-fIbK|g=nt1EvZ76v~@s_+X`cqD;mcau#%EUYpkB`m%%Ums;|<_hu<K0cy&
    z_xQq%s&<cT2PkrdW6U1+8BGQKHYjfFN-_iZ04d(XCvsEfNY2GQp-c|cMN3GA#a(f|
    zAXzKn=xTgtYo8#4Lg|wOd5rb;kwYPyrza#7v}VU+#0|!7yH!PRV%d4EnZAz92M4iT
    z_Ki6Pkm^8eGDpYq9H(~}*PnBc^nWKAQ2u7>B_JCmFXSMVnyGNvmF#wUr`#OfB=wR=
    za#<R3Y@~+J(KcY=V6cA52O8`rugl@54Sp5Aw|ZJo6C|t#Y`Z;R=2vc=U-%&4<GTz!
    z=ce@7aZ-4hnO`P;$jq?>H%QLRDJui|0zCksS$6t6saT1<`Rq4|MDzlDUkC`;Vj6a^
    zw>C~4!Q$%*UUNcVp3YV9S&y+0KHyyjE#nrrs^jogxv1>`=vN=Op;Tn(WcU3dDCJGE
    zzBrX6^m&M%-m@#%+|7nwYC-(^%y(4YHMjOe6WJ8t=?t14cKU}o2j-59cxSPF`LbbZ
    z8~GGSYA9{|b)TXSu3VduDOUtD_x=Yh-RPlZ;{ZG;`DYq>R&_KOq!yIWjic1s6EIa%
    zXPTLCcgOod?xH`H;7}zLx&0o*FEq%)u~!$E-&tB6V7vFD+#RQVsQFkmp5+_tPEG8B
    zUtZxxB4a^T(G68ZeA^GfyX?Wg@Dpy3!juhPAp~=6ewFs%39i>iJXw1o;L|D*G!;GJ
    znJttAIqV5D+p|nBzHjGrcvIh1MBPB@GDk=Jjc*|qXH3(k9NDsglNVhSuoF4xo(!~f
    zT-pM`_~$-CANb$<-NpVJfo2Q}Jx}_GeMx52Q!`^Drjv$swJlI|8r;!j?;o`^qhf@&
    zi3t9}dz2xa?lk4-%;7h!7qBi~-G^LiaRvHteA~2$;V;de{((AQ7`}KNtIT;4i~ezy
    zUIsnk5^iAsrAZ$t0Yf0+uNx(aeMRdW$%rzuN0ks()pT<(Wq;CjfzeFxBv{Gr5nwjt
    zM!kwTUf;)nW=NF+y!FV5>O!dn+)aHJ<MXD6$xp%9tG8YQETUH5s*!=(U(9>4zCNrR
    zrh7w}7y02spt{5eumH0-y=v_?ej7R{@b|h1enu+VYAS4(mJisq!$x*JhpAE^s5Yb~
    znoTJqS)E;&d{5sSQ<y3gDG&w5>cfmS!x6XH_JP1=6FEFR73cPmxTIfC>zn_hc<p%V
    zK~N={`6kT54=y}wjpN`zs`ayq)N|-w5-;22Xwe@N4F)p2wYp13l=_btptzr#-~Fy^
    z9h<FYs&JD>XvR_!#O-m8=XD0#mCN+{n*WKTSaQyLbs)*a%dvG%aITMfTXizbB}h^o
    z0%Kqi??O20l;9!By$^BI6_j)0E72*{7_L#)3_lk%8zj{-*DU9lZ!jfo2>XuKxL?5y
    z@?I6Gcr<nQD0}eD*Ku-%Xv;7fbZGs~$#~=esu^It<p^x%DP=#M50mm4$0>u@`Bw<>
    zd@QeTaH&5zE;h<eP5^+8W+VBd;SM1JlfFck6;*__ho(Cn`CH1_tUfKxcO$QpRS#+P
    zEeaKH<A;7%<U{IeUmd?~nlj#|fI7H^&i0*l*{7+$?6Rw`Ih-~Z`<}q<?<wW5-?c15
    z^k3e1n!B<3y;X~NT^BE!uP@FYHDA|_7wnUo{E<l47fRk^V|KN1XjK`bzoAJB7yf~X
    zj?}5dQcD9)*$O4Q$00DCPHd~M68|1&`~8FXcryVk2K;yprx3h}><hp~S)?|0oHs{V
    zznOY=1lmd5<!ypI-ST=lF0+*%SouaCSu7Azx8lDYx$76g>~EWB&7>OOHipoYsR0Lq
    zz>q39jcKL}?5s9YgjyqE-(R_G_IzGvn8^B&?n30RCw>k?I@(hSJ+}+UP0{`)qg7ql
    zF5X&^g7?lWiC&IQ6q03Eo!1nP*JQi|Mq=+Ev?W(<HZY+ZC&Qf2GNb?yuv|OymIW(f
    zB}5e8ejTfHlxZ}6oCc$-l7(1r=~-qYguBs5moLC=zU-Yv{g_s_)vH#|rt6Fu12`3L
    z4m}wBi*iE&uZ0>OZUXZ#Arwqpd?uU<o12nc@>svyb&6c@`7P$Rm?%&G7K_7J<O9=X
    z+bAWo$@m;5;Q6zl|Bhe1laF3FSdafzBFGpyi?C=QLj*9d`m0tbLwI_RZUSRDyvb}v
    zlb?sR+7S7BDTcC98pHt1-KpZs*!o(m`?(_w&I#B#*~JytxK?ogq%*)X*`}133ccyg
    z%5gIem%B#7^_BaUbC?rV0})ylVyj9FZ)e<I>Xsl%1~n@iE-LU)lpPy`+`Oijt^jy2
    z++#|keW$92Y4}vaHH-D+H6#4%K}~ArbFDfI2h=@ey>|-G_}i9Ukk`xht?S{Y;YNnV
    z<}~@4bx=N2oAh69&V5gqo1h@kh>(93s@WLTXc_N<8zf_(`ksZs%wh=6kYhVntUx;!
    zKDH@nXPX=Wa<v7IER9Bl>fY41v&d7@Q$=%f*eBbzsk*a3Mu+P_T;v#8arG=V25rBN
    zegBoH0=k-=IzfPF5pxIx#4<K(p&>;$QtlC4ZCADOEURo^pAy_mtJH9M78G%tkcQt@
    zqiUjdm}C*%zsQIrvvk*Ve##t(PqbpTpybd}wmtZqi`~5i(CHO8f{wc;JB3FA(HXiJ
    zBp(-$({PR@fU!QTpq+f&kV_q~pDO}*Hja@~#ndw$CO5$3qskq_`{O=6CGbUk<aU4j
    zlXu<oKTVnMM%=?Fwp=sgq(kXU^A{IJFd=FbF=$UxM1P>b-j2QCT2KshBOJ-5XG&*!
    zceAiqe`i~DSUzx|Xv#D|>9ZU)7mp8Le`nGl%#D?1qlj%ROt_SRm~3vfyXhQwVK3K2
    zs1t_eQY$()ch0U;COgVID61jw#c5C^M84Odq&3<~mx`Xft3-DGu<Z*Y3FFs3-&8Tj
    zq<hS!^|BC*6kG$%%MDBGY8M5eEES~RL0v0R83#8DxYj-qO<nHs*#a+Wpq>CD5fZLH
    zHt~6@q~;kg${D_)wgRzl*$g-Znkq6=^JYN6JA_z{+;tL*1ckOhv}p19c-eI`n(tk*
    zGC#ck?^I80hv3x>=E{1PS>lITrHuxMorK<0an7s-?FBqcyA8Vqx>f>Ux+Oh64l;h)
    z(4*p(#~VC~J%|&6s*}zPzwP)Q7#&)7QUi1=-p(7p5mlredf?s1*DCSxKempGhvXa$
    zhQ<+JZ!^K5=CG`o_6W;HF^_G+5}iy08Y^wP`Cq%wxGwPZ<M2D|QyO^|gDK#i{5)O0
    z0^d8pU-ayBAbGF?AYF*V#f4QnH<1aH1JrTOB-~;-zjGF<ngvk_RR%^ln7)i(Pa$K4
    z$Z5q<PQ8*UMXi{+FLs{6oz?ixulZ@xDN63z-Q9smTu3>D#!*M$Bb`#cB`#R0=t+|~
    z7PI{DD{Cqn0yvavE>Ky2GrkZ1OVVM?JmWY{2-fUbgFNcO@o>@SoWj<Jaiv~M7cQ4y
    z?R;o}DT6#O=RKe>Sd(4W^;X`Jd7CkZvz|g5HyhFGb&jL^w}nzD@>OyTaTK!KWz0;2
    zoxt=Zz)VlHIWXEgiew7H4Sc+4WOYAZ&9?8Z+}R9266lUWnD@qPdi506h@Lt|RpP=3
    z_B3h7`+ztO*d2BYrE0@%us$<f3z!8UQ_px9jvgbt<2p;O^C8$*wV7b~IAz9jQZGO%
    zHz}q2sHH)mbi08qg4Qk&Y!01b2Qv|g;R$L~BPyGeDB2YU&7Xp-<fO9HX|m5q4o;X+
    z&tO6R7e#>0Xk4au$bwEx0ZsBsl}L{w0>KyiVq(uIKnSAk+={aUqtR?XbhYtLTfSCa
    zW5Ldi)0IZNwpb^oY)d7GY`d%SXsNeLUjdJMVE5|ERqwdBWUJV;NgkZs0^g^wH6MS6
    z-vG|G@TWU?QdI0-La!Gjr5;T7&l1dKxC1x<n(QTHg3Ch>@x1LI5AC$NkpKS01@qvs
    z?=L0LYWY2r8+l&lg`T#Mv%4Osvrev&6^x?=A_dM-D*HC1rb^vA%Y<zl`_#x0R?Wp_
    z>Z;0WZ31dykq{B=w;#EiH*)WN_Opz@zAKD|-#~$Kzc)0E<Uio-Ht^xJ1>CoGXgDf*
    z@=`eY@of38{9ewrd*dL$IWrxa%hm)O{YLo&f)>BmywzP`kr}Y=W}2gZ#ohUgVD#E@
    zx&zPan{Q2g99odpAJT0or6Lqsk|Vry!T{l3+R;E{Y%aas9Xn#i3&$kh0hTWvT`uJM
    zRjZFCB)gBtMWBm9K=a_*yIUh`ho<AyTq;%?`sU9D4d*SliO$}y&r1{BH;Xj*L$a@r
    z&9G5V_E-8_e0zZJ(3dNrcWWL6ShBTz5+8`z?*bI1_)6P#umRC!?DB7f`B9_|<UMHa
    zUZf^@>JDKlfPeBI&kz4+<?av_bNl?*`ey7i*m|9>abH>Y+%=fqZty#-pbM>d>n4uk
    z1|yOrE5mVWn&aBAgEBF1da1g{YNRt<>2(@(w4H*u&)x!MTz@?L2gWtc@E(;))n;o@
    zhxdyy-Nd$X8&QA2FDV^<rwDv~xnw)**`(n+APFVZ3_2FsKfn#<+0__NgyZKurx~eL
    z8gWEd)GFMqU94TNXfK}O?qqU9&iW`$7|bZrp&6_Ka*jLk+4%MP`g?RKoi`o5;c+_@
    zcVnBL;(|SfeQ<Kgv)qq^`gq>H1RkC<FcaT405=U_H+xT#t29WwUG6#<9<bXG3~~En
    zZ9Bu3w<B)|o3P|@e79Wk_;fJwm*#yQ`j+M$4|2>57SMAn{H@Lf{4Y;erTThTH~hOa
    zNi1u#{*~PJz}iXrvhsCAp!Wc}*7+XSt)a9lZnR$h@@<`BC(8RmKQz|shGA3W7u&Vs
    zZrtX3-u>K)diJ(^uS3Vp(o?EckKF>-K91HNm#v>L8oDppiu@0Vq%F)gC*PwBhcw3<
    z?cPpKoe@i;V;?Gn^aW7eot*KJt0C0h)@cI(!|u)0<}P}tslyIO>5dhhyQlB0%0tqB
    zoOL2?o)dhzgo{f;uE;)Cye2|RpA>OnLj=$?Ari-D#j3Rwc`c24D%cy8K%r4yUo(v?
    z*pHs77WbCNtUvf%7UQUxj^coQpP4)70(ki&Cp5PuMxaF$b!Ci^dl{8H28%6ikCxBM
    zm|Ms9_PwcU?)%FOo7g(GmtzQl?KBs1UOt}13pO2fFICtAA~Od-+DuWpmUsy#ST}0f
    zIe7{DZOYi+CsC06)wnPG8qQ`9|56gL8W=F6`qS*nRKLA5Xwnm9%$HMfy8eoF&U<W2
    z0x<T-A!%|?NGR{IlXqTa&@NzdYJR&MYr~yaq`iVpVrPn^g!5Cze%E-DX65zYG+o<P
    zu$;y!;6fi7<3-Bur%(~5*7yy4Tj^Vd6WDbo&uxi${#txT!awcqboa2%G5#O{-??=e
    z%vFEpywpo6GD!l%+Ham_IhC9QMN~R#CHwTCC~0lbkm#^;d4s61i#O577_(E`&oi~r
    zavQ!6-R%9a*OsPonF+q7!PIWp9dBy02}xxbc~ILC*3VOjv+q$e%`g#KaUc=<!qHXT
    zDDEU$NdVjJE#&Kgo<-opO;y)%g!5eQ4bDDQOO9hf&f`_I1+cNNa1v*(<W***5#$Kg
    zAz%Z`FrJ{Ir>(-ahiMDHbm$`qg+-qB!l|!xE|Yn3_VBqaXR-KQhnWNYX$126uYzIW
    z;t$4!)<Z2e-AJEDfe|qr^B^Q*C_343x*Prx{sZn7?9C_kjxU`-lV&CEx5CP3Y{aI;
    zo6qm#IGa>@>E>BO<C=%E)!2BOlrTyPk3A@*4cgNhHJK6yAGo<VJxR@n1#&L7fr$r^
    ztPzs=*+DcnF;s(|9h!BLJnX0E!y;E{=b9zAC#&CN^{n~Hi=j9Tz^U~$0=37X{9~If
    zEB~y5zpO@(k5&>~;#~YQU3TAsypkGzD<ZhT$rad#SL-UfX4&}wG9&&asI9f|oh^cp
    zOw8jwgntP6>iWS(Z<-+7zn=MlVa31u&WW{$b#<Z#LgG4iv{FE8;yCRGzN12Wvv~gU
    z&t2QNO}{_ZoJ-OT1nlf*2M{|~YG7uYW^NYSjUDio-zb@?FT!>TU?NJwB00PgjwzdC
    zMw4EkyvwvQdx(pyN~e&UIIc_^L@7?Yw2?UBxh;k))N%m97B7Uy%JPH-F9K8@b89;+
    zKYp!KTM?dfQ67)yIf2sG>p2zsmA(q`xR}+HzC_WK_yY~)OHY|q;hQDVH)W~Qv(F0h
    zq#xWP#n|&sr3{^b#?<yJ{R36{0?Q#rsp5rE0j>}(bt1f6rnu3E2Lc`7TUg#no1q~M
    zYY9(z%q=w3mZy<TQl*4JE9x`tYy8|iD?0p1EO4H&U4Z=+{z5DDZUFJ&VCUoio5B4j
    z-$ZCEJD}ilWCEjct)X5$xeY?|+XBM-t{W<Wn*YAZ!88*De432M`u=nZ%z?@USu1Ru
    zrPp)CdwDG^cL+<PUa13()m}Q1Ggy%H^rWcA-_L%AQ<O#lB4HSo3S~QK;l=&(AoMy0
    zr|6%$pS!Bf@~SKF{>d@Ooj%aSNqz^}?C-w*{X;kO%E{&;C6|0Y49VremE|$w5uJwv
    z$W6#w(ZZC_4$HxY=`_4~&bjO0ExD3SrcPijrG<{gGx*%ekP=jL(ZzlOsoBJ!%6JJ`
    z<?Y7{!sa86Zk|eQHPSC(%>ZdPG>KG058~@M`<KrDLG5+zmVDZ|0pCpTcR$DYiQ1m!
    zG)SKU0O9EE3%wvBm_FlJXXoB!;$;x-wFmsZy9?Scy4%^tU>1!WWF>sKr0tItRz*(h
    zA2Ru|icVW(akG)t)Ki;L3z5B@8R#Q7FXThtCbK9i;pvd4v1dA2NEMu-ic>ZZ+J&%L
    zHyY5#G~XPy6Whtke%B~2hg#iyG*NgYN#J}Bi}j-~(CgWRA<qjne;9wD&S!_;Gu9%O
    zzvEvFUr4t?qZe5yWzEq8Q*7SC%<>gW+y5X^82e$RGnC}hHp4B9kgJRD0~UAJ{NqVd
    zm+~h99$@02$#-x`WJTU!tJXZMKnWSGv#f!ed@yprBSaFMU8H=`5)2+eLjpe9fY&|P
    zP|`-t`<PsloH|Fn#w`|pWLxk)2)^t*=7x>p^$^uga?H!_K!TuqY;VUW%hkYO1rgd%
    zkYcXpGM!a)2w6BJHUVw;uycwwkECUd-aSkSMr6ky?Uokdjmwe&$FVH|SeA2N#8_!t
    zj%4KZU=&P@{*)Vpz6tKo^5Cfu$@g#~H#NvUmTTClZD<M-TKr~9a&R#6tGbxUA4xxu
    zqQ~kB<OW<x?^~CAl6?h^H2k_hd6dw<$9fp?pXfgnFM7Ploi*OlJ?Y~-KR{olJ6;HE
    z@3;Ptuq+6`;7#X_4xMRW&L$W@^$NS)C;oclX7>CKco?JrRu)7C)qt`(#u>kmJ<8HG
    zNRhgoP!P*)nmg=}{ZPM1hHr6Ub^S>DrNeWxsHZiaR0c)^Hs-CnalB*00`#)GmX_q^
    zI(FgNACdeJ+kZuNy0OmVdDsn$N_)LK(9B3Ha-5KYm|paj`Nfn(!L<Tff4)8C#^u6h
    zJXm{<5-nDWWN+9(D$jMZ;Xe6wV+LO0GErqV%rP63#o<=M!1uKH)_BnMfD3d1K79VW
    z!HvKM|KQkVDg9COy)`G4lI^JG5;P#k6)wqLiU0#WzAQU39+yB@gKqjM^^{2M$T_k4
    z^5cdotV7FUs)^F-^hypTfmF+6(@KBCy!FaK(k-bo1+N1vmMqImBdsBfk*W13de`Q0
    zAPI6Ri)wVpkZi(ee^WGjL|D;LmSQ>_^3SdUm0v>oLmVGM5oQEVu})_HlC-cW-ZtH1
    z=YOjGxzlMl{lzp^F(B`oYZab$^pc1w#lX%NFsA|Q)F~s3U#w3BZcu;0xce1LekG_&
    z8t<dN7-)f)Q*_j*<ixGsNoPuDtP^{!Ki#NCNHg62G)k?9x8suY)9Cm@p7T;t%m$f{
    z-1{HwyM|~87eNneA4RlAA8^A|10W<o5O{ERvu-ET8Cj9s(`lJJa8^v7Z)ia_qspW>
    zxclycHVJeyAYu_1CA9zW{oP#;UyxjsW1}O88~^3BW;UddNJi9J8rP-s<#;5sJvPl$
    zLEWs=VCS|Cu8T}&)CiEl&IMqej-QG9*R15cm%kTY;N#2c*g9m?<CD;Ob}-R!ZEHIZ
    zNtoS?2&7)x5O%Eu?6WeT@xO7+hRJ(g!qi3S9{8*`SwkvVW?=0K?T-piss77I2dbRS
    zC`H*U`E^jDwo7i*P=XakF4yY5S+c-sy=4oH&DJl>Y?wj1oCt7#Tx~QDp2@<Dey$@!
    z?|jT8f3;T=>+LgzLR!cKfcrzRZF8adD<_%RgCh;>L_qXtmSVYMJ6VzTBF3^VdKHvD
    z3I;WYQ-Vw{MjE*fV_A+J)CkGKHKqIz%_(V;zsbWt*vcKs6O$zkXgK~NrE28()sYhB
    z(J7x+3l~69`AF1TxeXnAkWJwrs^$L5ABus%@nXlHn+gUDqFjy|2%4XSm~XFJ68Lq)
    zxf!l>AsZk|s}EZV%V<2IO|8@xpTGvmgOGwq{n9Xh$v^PJ?6QoF-J}tl09~B1SIp-c
    zJ`$=a%FEPgyEzkJu2C)gyK1;H9nnS9&^!jxkd2^CXH4f<v)u?W#XMp0)pqkw|2i5=
    z->5p9$sh9#G0j?D{PS$@zz&4^l(L`y639Zsl6^2F+fP(YtSM(aj-!|RQ2})abw%{i
    zUXsw^Fp%@KLJ3|1`L-W5n9M5DwP0idgy``*eF813SLaS_yirScJKC~d)N#~cf%2U8
    zw_}sqK^IATp|=MS=*6)2${eI(l0uWCDh+V{I~+ic{XS&zIM%QX%~^`>X-(B^%W+;z
    zF`v27*7_UQQi`oc6c-J6kc{+%H4yis_AVc8I?r&dU&6z}Jtx`oR^F=C!0ggj1M2Q}
    zbLLk;O#kIlP>Wdjc#WcW?Pv&A1x1%7Ra>Wx4UJ*Jw21qL$nBMNv31b1_1iJ;Tv^mQ
    zS%1_t*xmk4+XW7X8LY7XzQnl?f+${O{3anr-f99_!jzLF`mmBp?3SYH%nvbc-Cyn-
    zRt3He0_)`=^?!s<8?AC3Wj{=I=$WRsy@UMw<9s<-svVUcARIR1VOe(yq`eLdVYlUt
    zX-Sv+w>|-E0%QTz>^f588CnZrDi`s^%MBoch6YjdyJUn4a4;nHF`Tm>QRlTQz|!%j
    zUn?pE!DPqV?<6|_rw**B+uKS%(B22{wcHy<JB7CW#Cbr^FuTD2nN1ewS3eTBBwH_A
    z#zSYEE5CS73>`CGM5OcmA5Nr0rl72~1cRMX6s;w}el!=@+h^*uy=K_Ipukz|_h5W~
    z)l-+X-5Y*1|DI9EU(96l<QQ&mk0@44#sGhcB(!YlXAz44RkT<$r(KSV4@&p@tgXD9
    z@fF*Lq<_;fiOc*A>psr=I~vHlbGWJ(NEz_4?OG>_E{GSmfxP-7c1U>EhUUK*_)S2H
    zXcZlxBbs51gQJo$D-Ai_an#2{Bo~$s>AaKXco4`+z;B3Meut|jEf0Mny7o#`{u`t`
    zp$IMu$DgU9-h6(pp_E3H>{1Hkd)@U$HNHH+>I!fC|4&3kh&zZ!$hT48aob+;k{jH!
    zRqQhG{UtTjNeK0e+A2QZm^Rei7?WRW(@0RPTjchDTFMwy4sWI-gJ=lUM``>h$KRie
    ziod&aVLUO3#tLLfT2P6$@o_H)Jv5M>W7mvzRo^8n55Oi&l(R~C%^~vJq`{nK!2XR9
    zGYOU~;|@Sn>8UHwb5wQvzVp>A=#b%?UB93?kbs^eS#-?4mUgfD5}#!vHtt!zEi^<F
    zkP=4ftWYTn8Tf}iS<djpKb&*xe^~%S^w-3KQ00~4NeApDoHQTlt!3I-9e;K;iut*%
    zuFJOZl}Vn!;#a@5j}@0u2kddcapwBy*o$9Zw{vO87YI0F{!vn?)r|MW*G3hmg;oK%
    zZjb;X;4WMsz=WcA#qpR+=r_^}-t6fOknz*h6N&*H;o(>>L%vZlUKg6jt7TZg%yu51
    zU`Bmke5&nE<_^ls0T$z0ZmB!TD86!Xt<3G`O;?q@A8a<?zS&@5j|1cJitFcTdOff7
    z9`Ry#=*rK%hger%ucT|J0k9kONw<t!T-{s-ceoaxYl05ehk1*lLDq3I-$KlfZ(OnP
    zIDG3d>RPSQgpbc6(a;hX0>e$No5QBHKlk+^INl&dKu$Xc(GnfbJ?~VkE@Ad2j6&t(
    zqwPmWwjtd1dW`6|D&M7@UuM%YF+G6dUk=gf2gmX3scJ7HnTAQ+iIYTd%jKq85J|<-
    zTFwE~yL)3@&C5h;oE7k^ya}GoNBmYcbw3)si8ddl*?jFnChT5=TVeOLj7Z3|jOndr
    zram*#jB~-rYq%s~1CWz-luDNQW3Fuk(js}f;FWy87@=NS{h4$q3q-T2`p8C6ZQ^u8
    zOR#&4XL9AFGahnQJLiEu#n<(4cfyU&A9G+nkyL<`57~c(S0{HK=RLUfRi*0oQYF^I
    zt5T~UbTFRbxF-Yeci|f7XHgkIs5s3q3=9o-Z$?8`>SyO<e%SmEKOlJV0g9v{j5n36
    z+>Z*l`U{4KjKx=K`LLdb-lH$q!HtG%3>aSvF|Dx}u3UY=Q}h$+?OR<_>Z~1e0K%Rr
    zhhb11z!Tjm-DMR8=S=<g^4MoE8*H3oXdVxZ?9Mfb8MA9RzJBQ#T7Y^gk`bIiTxSI5
    zShjyxi()bP^=hbLOx|bBceY2R>jg4m4weJBcuy$pqY?k*2BPP#eQ1I9S-az039DEp
    zZ`i<IGWa6HeBfad*d&bzMyQzpgWJs8qvT6b!mi`cY`;x)zgG0JCsX*jD`GsRC@pJ0
    zdLOfPe|E=O6+M;TbZYaWRwjG}$Mv3sN=J}BM4+jaeH}-JZop4v@RR&EUK~1t{@<bd
    z&y4^@bw|4x&F(x8wQ0C>Cask%3o*<L6eSS=la$ZWQ*=@VX^35F3+U~+3&DUqq}XG5
    zLQF2Ih)gy~pXLS!#KJl+<qAL6pQ=lQpF0c|jfJkewGmylK7DU8gF>Q0gd*oGwQ$M-
    z;jJ^tB3aS)HoH41avLEn)5W)PLxdmoznmp1`k^Rl1tr}7Adh~3J^R{xLQI7ch`g_i
    zZDUWw6RG-NDgdylARorFY2>&g;09Bq7DC-}8_Te~;Fyr$1p^oHiy$!}@sD@>K^^J#
    z#}_<!oR*@ekfF90!vor%AC<O!ONCn5&+XgO5=6yu7t%yZE*5wIu1H8K#APatS}QK^
    zJ5WzE<JmS5HBQy=nF45H{?bkDbOyFMQsY$~F^D1>j0*0j0~npPWV>S&)*^a;pn~3K
    zn!WQ~<jUt`hA3Y;UG&=W>wjxmdJC3{k+7D#Ny$XB&h9Fk=7D{T_PrSqyf*GLusu4;
    zNIvCq+D2kw9chEwOFgA0RJlJXdV=Z6EnX$+97b65Y|Cnm$+!rt<KTGw1x%Mch@|id
    zu0Ht=F<`S_4lNRy3H2pXU-e1y0L9NJd@{N?etZTDdPR(v`}f3UaV#E*I1OG{ZkKv)
    zsfIGVo6^{<&VU=bOw}qW31mft4>`WmI}}0#MsNmAR~6cjFRULfH|!^`1)sPv)aF-8
    z$thH;K9%Z4$1@LcK{_oXHK<IZStS$3%Ps3!u_<!TBM4&4CtmFBgrjY)ubqe%X6VJ@
    z#v?pP@Ijli$*U8uXP19LR|$D-l8(tDr9NR)TY%aeOtZU@#u`}=>Fgu#&~lf2Vw7!f
    zH|;sW<dn3@BnSsNT!~ASF-xIsdj8m#sm>VLQ){+pqyLC50Yf?LhMo@b_L9x`xwwSr
    z0N)616~6e1%S<`p-!raqo*jxF!8w^W7NC(71}hq^{j^l-zg3*!|BuL`wjjaT+tKh_
    z&cK{HyNW)53EE`(VrdQ0le4aVn<C0o?j%Te`Y>-vRx3QGypR*HO2|Og3)TRBj0Zz+
    zQI6=B2Z<T`Ek1?fvZSIi=ZS#a((k4=t=qu<cwKMMD?}fXN7ANG=H0t(m1YDRVmLZw
    zH>)#2R@HWjinx;ix!pO@)Od>v7ddb3c>tTS!@`9tU|<&n`73&fZ@@mMpkLEuD~wnO
    zW4+fI#qf<N`9rI=-3zFO74u6u3Y;pfv7-Dl8C%N-m=OTb=dKqq3EupGUU_Gwd7|=H
    z!5^76<J-Mau<zZJRl#KLFPbWh$scKOuN`?<m(;85;@5%R*s1AuSULFFpV=|$<}B*s
    z)Wmvkf5mB|Ttq^qw6c-+cENkamV|V(%Y8%cbN0hmD1t?p3alyhibTQibZMl|MB3&}
    z#sAoeCg!Z?PfC#u3kwJD-e<}F{^-wV$KX%ciy_R3BK1iF1Q-b+-;H|v8YYC}qBf)B
    znh1mvF#ONEg-7e=1Su+8@XDi%KoFES>+z6r!083Nc><&{KYp(RhR1t`gxt7qWH|mz
    z8`WRN{GtXj3}1xp^PMsWV7&;6i}Br5Wa)#`_^*s|C0{Hsmw<zexjG;H{eX=!nS{7i
    zg8FR!1?wFl-Af4w8Atekg86|L5uPrxgmP^T6|qEw1xb_&0AGj&7s_kMYgqj#kOI07
    zcz;US@TA|qP-phDVM5J$X>y{1qHq6Mq~t8)N3l)lW$f{!h5$ETPIu^D)UyyVjUWD}
    zH64<TGOwg;ju%p8WCqKO?{t*d$TwW%+J@K^=nCI`IoXEY6to#g<3O~2X2ZqYAHGUO
    ztwvrG;kT|*UZ?D|akxpL7%;y7934m*1|b{4m{!GNvpVP+#(a_AS1~J!&(Ph|!Melz
    z1y3#Z_Uvt7wVrN4gAKm&R_;VrYa3E$6Nlqb^K&p1vPg~{x|nphd;~e$V&Hm}F@NXe
    ztj=sobhYyOui*5VI>HBCaGBP^L`bA@a~VmwGKWvg1R=I-<uGJN_Nm_Tvc9ei0%4Ps
    zo~t9qw1;YgKd?F?$eMZ&Y-G38QqyA|<}~Z3?L^I)Lkr%Aq&&HSx%QScg{8l-_0(1?
    z6lN*cEO^e^jN=wx+|OoGS@gKLW+oJL&d?33@xXw3y}mj%KGfiUeZ`5AwVSlwN7R*T
    z^z1*jAFMLmv^vnHtwJpCtgT4}PK6cowmzc{4+5Tn0t}KuLx>1VP!PY>lYq(yD!Z63
    z{5;KQ#vYp59mD7-{M^fFO(E8)SyC{nX#V@{)VW@oli4aZv352{kc(aOiKPCfxq9zT
    z&$vS_*vV1q{Xixi)A9i8-1|9-L+&BmWQo?TzkMZ?X*Xo+T!1!*kbRIE3cr&KFGL6a
    zBLb6|E&QTSjx<6NR{duCU3LYU#B~vZRbF}PP4@M&H6q{9w{X@DZE0e1fR0BTHO`yw
    z3)96uHKwC7=?B6`Bbm7;;~Th(5+H?Uin2`_)Kn<0T{S~F?x=}4sU=dNhUXA@hlG18
    zcIzWblm&)N=<=XsFH8SYG$#a`WvPXJw%E-|>y%YD!0Sr7FzqHIcby|&MdO|J;qZ&9
    zvn6tRx{wP-fGL%r<u={7w{&_ig=KWz&p6V;*jg7X5)@VtZ29E<wZ`bRIln9<%Mv>u
    z;7Lysexo}IV7$U`hs0p>r4kof(5HcTJ@Fb+FRUpf`xyzU&NUK|GHtiwxd?OHm_w;S
    zy9zx=Rf`Ul<!}2HVu^E+zqCuNE0%{9gktkJLh#|_2MYbEQD8c-2o>_pq|TL#Z(Vl)
    zFP)^xMx)2OFUD<?i**IRPMm!;87)JE@=Wo3etozO;wj3~u6kDr?*jeZf#p4b6$+`6
    z*Aa%(JoRYYsmcr5kTefhf*Hw?_KxFUoI4>s$FcCptb4mWVI&gb=`aTQN}R8=ta?fi
    zXD~=x#ZPS*hTVt6)8~dd#eRq17lamM3qcIiH?b(62~#SV&qlu}b5g^K{`on!fU%TQ
    zw)*wM#AF68zB{{yHU1;i^q`g+cKmfiddurz+!m1riTXwx7s7Z4w)lLcyI6I&(J;-p
    zgruW>z@aVV<|65<rfWuJzn}|-T`oVa^jEx$#KOU6JekqxGlYOG=mSa+yKLMbz%!5V
    zrdT}aNkx9l0RM7vKDasLMbu;JN4&PKcrVMdwn2x);**=k%UiV4Zv9^iUSKGbr^+1&
    zkPR-*)e|qEAV$7MEXWStSWhq+=FB~yj$l+>;btAGqCWhhnn&Pzn<}?XD3)5MEK^%a
    z)S($sx<2H^sT*Gog9M{=c0Y&1>2U0l{Ec!BE3iHqU6t?+Rw(v03LCEx`yN(`d19tN
    zf>r;Ilv^`>6^QvFD#e_mwQohZ|I|u#e_(W)i5rNf(CPOA^+r{Hj>CXCY4;;0hzF=O
    z$3OmgixJq-1JWp3xtsH2Gd>BKSQnf@bUzpr5@D&kFMHXjp9B!R(vt$QZ&6>Yg*0t>
    zfE8wgUU$zWDPy~3R${f-m+8{EwX5M&oM$`&zE%76pZo4NE_O7wifyccci^r&T+d3M
    zO}P8GIE*My^UtZs2$VTwS(NahT4$^}sHZ3xoHVObf>?eXEFeNG&<XLK>7O)DFCOUT
    z?`RD*Qy#Qu>+$R7@d-n@LeL<skDp~$Sx1(vi%sHm$8Qq4b#@pRvLs%_DhqbnjRWOO
    z(wTdW2F1r?=p!A|NnW9Kr?Dat>cl;GD-Cv&wjnIskl{2+T$Q2EIVSvyP8%H{ns>1@
    zju<O?wTK-}AuqCXAvf)fA*V_3L12>c@E}!+i7r$pTqb=a)6#%^(Mf~j`Ok{9>BO@t
    z)f|(^)gR$62lxNCuQPae`Y|Vz#0RHUEA~Lf{Axl*Or_K>NG8J7r9?-H1NozllBrEb
    z=3!vA_P_`AeVqh=HhBj|iGt|3PP%10g$sDpTA;BS4}~mI_RxFkJk&|2yMD^Y3?&sS
    z8LIQD2czdVIk)U8`(tRT9cy=%@Go)b`NP+L$rpz!cPe5~k~80Z+kpU)1>+vo>A7Z!
    zbM<RNbDzQ!PTrK)_UAZ1N6TcBmQJ`MaK(?-qwz?J_0nF;!l-5CaURx-YEgf6((&!V
    zVCLpGE(x73K=pDj*>c-aA7}gH%3lHD4Roz+XLqC=X410{GM{8rhV!UF0zBK|CkzMA
    zuz~Lp@hu+v*HDIMzHKa7tRvu%LP+;57h<oK=YE@EkNgh8e-Q5P<hP{g%{X%z8CKOZ
    zp6t4H6vu`bbN_<Z%kH7QNz$ETrl1I{q*S-+!`~@qBSA|vw9T;F!s`cc&Yd`k=wq0D
    z3HDOPwy&A@zX&bDE3IPL2hR!L8AWlw(Pvy8H97CQr$s4J%XnBOuMa>B#b!Ez38B=b
    zDFPPcn;5ZV_rh^iYOM>l_QBU*37K#(2`5Byf@}ak6TL`IJEFAG!e9y0Z9H|_iSfUX
    zguRsRzirwZY}6z}az2n%u}a_{&*v=uU_A9t54LCkjsL1G?Y;B)=j<=Xy-%qKNRMrz
    zH`Px!=#LzRKXA&0^BJjFj0{p^tH)*Q!1nRE93CO>(3jO=T0cgaq$#GRjY?S<H?0l6
    z{x(^Iv2~-i(b@HR`O%0}TaBF&#1r%3)(x*WtVdT1*KL$5ANy=)a9_?rjHn)ghEaQS
    z@!$zS%fq^}J&#w@ygP~2>VXG0$nEYEmO8SZ4?TNf<ddSs^-fJnXjp?$=rZc}%J8{B
    ze)mXfrTfW;X7d!=SYx3(OZ`_jb|o*Xpo<~4Nt>g_9I~#~Jj_Kufhc3@rDc~&@$-1;
    zi*?s`wAb(BHhht0O@AQ<T}-E9qWzC{s46fVOlQ+F&aBSCj1w6r|8ha#9Pc!<@xE|W
    zKW3(N7>h7w=QK)HRfUTpu9-a$vhjzPmb$2F*N4L#8#aZ8UE8Jrex%smNs0MxapkC5
    ze<Et4dpUpEUM7l95*hN*%~UH(GIx5fiFy3x@QMCUCH0VWcWuqzh}vUSS6GpK-aXF2
    zsHX*;O|*?77E^1aSoAQ3wR~U`XWfyaj9|?jm{$uf;$||oWT)xjtgao5h7lv7y3by|
    zwGZnm+{SPN9R%#dXc`P;n%eb2uE!>;V|yPY)mI)<1%3lqA==1FDzrGr%Mn|Sb{aJr
    zc}S(4P5w7Oam-I2IzKf{Cc*W7CnJ99aF$la6XxOETU===ff@afbUl_=Cu8=qm2I}s
    zJ7wfjwKdlSW$5O9p$`v0`;D#qC(5J<YgUl^mo~K#?h_gnK%b-M8Mt-sx(P6erL^6m
    zvw?5g@^r6Yvi)XTvY#n3!G_avmPY31n9Y5oWqnJS#=!Cg6~4EgmdR=fqhVDGYQepg
    z^k)^bDbew7|Nh<Q1&TV2xYY6((z5IoW$m6}*J<DmMzS~3&0Ip!-0^8cX30b^2LM`&
    zCy2aRp17GNH<s9tY^kcG%uvh*x>yjdlrcOT?R{RX)=oCZh3xGj#NE0Dfa*1p=^G*%
    zoRXvy06gczMnG4XK)C%^#`?{{6}|@V9o?bfdCY{pT@ef>jKPf#YWrDgWn@4DZ!0c9
    zbsT2PEYU4OMuZV+7M1|RWULGL@`d>ErOr}4=gO9M2?lzn6lxZpROcytKu~GW_SVo>
    z@XQ}y3n%g1EqJHisv}-wnG{|K=ou%~^GIf}oS9E88(C0pLf6sp!-`><dp{;gXLVT5
    zY1qDp;vDfZE1TSlRaT9^V)zaZBlu@rm2fbQhQiaO8*L75{pZu4s2Qp!`rJTx`a24T
    zeiIW<c)n$&_&D_r2fB@rDIS;*Gy>P!WOliHeBxrj`Szy=3RO-D*gA(}Wgi<+vw}U>
    z%p+J8LoX{isV@60W<i}dGnk+>J6Bs*R@!>wXy9Cjp?j9>{%yNFiOD3hy`rHm#g)Jx
    zx?h@oN)cm)pp#u?q4>!Fa!)i2X3zr{Om~`|Kb9RA-9C3MoG{fA@Wl0<eM1dfIwIzq
    zcI+U>SLT-CV97znSM^RJCW&L}FtxPkN&7r^N8)6ShO1(Y+Y@U<$7q_)>ml-uCirRp
    zvydj(WLHw3#i!|b3jB$9{}<g&rOO`}!sAX2zIRASZx5^u;!W~TAz-tQI{QR9+YIw*
    z!KY}st9NY&6~2u;?SsqaHR@r)$JA4Z#^Fh(tQ?-b@9Hfe!wQtf?M|i-ouAp<*QQ=<
    z;%|`$m^O47l}tB!9c<}5>TwK`q)YnJ&Kjnm4NdEji>*aM4GX;%$PP@M>}?L?8MtYQ
    z!O~tndoZF|N|qT6lO#A|CK6A=WrN<m?KX85qqHm;Yp#MB7wPPWYo1H#0A383^^N3`
    zBBrS}f~dF=O5)&gxYD0dUobL~NT#ZzMaWVxEB`Jg!Tz?;L|0TV9DGaE-o7<YK}OkM
    z%6tN&LMbHbCIOzR(U})7P6VnJIKF6ngmKgDOPLh<#~G@zGx(T{x^;&Wux4Pqba2Yn
    zKAn2&9d9*opCG_ixJqXoD-X-=rGKH@P`ZgAsg%Cbe7e=RuFf581IOO8a^OGdE+8Cr
    zYB>2Fq|rAsMOKFT;DoU($i;gWSUD^y8$3-<rgXi1eg0Mgo2hFDbgls4WCLwNrD=xc
    ziP!ODR-7%}LIMp78FyT-uC)SEs$Z>t0(!{S6%iS`XX*9{X8u7{dfs-r28npnbtBDh
    z>xLmTcpt!G{s&~etyaFR8@8^)-G2e`djTVLV8ix8#?AhR;IXI!gD!@i>U{fg5QScI
    zPyXx3E(_4GRy<{#09(~%i?M6=zn;O%&mal8#Df(HzcvU<A!_|RHL*0Uw|JC@fBlsO
    zl(aLZeGw?`DehQ>Alg?pn7xaaWVfxeXDG@E9Pz-oOeyRfMyw?8#`y~hHS*n0!|ac~
    z1u(I}QJ9j4mcs;5m*hE|97K>!;nL6Y0NDM>aGOzH7G`5CW4UY0UTbcV>m!n38PCm&
    z-3c#J@SW$pH}&OmjJ2WzQXFrS_Am%RTh20!pYP-zaWyROt59KD+<DdRs6HL=pb`|b
    zAJ&e^Rduj}e@w#J>V%<wyyUX4J;z<5i$$7iEKdS*V+*hXKS(L!N7c>{HGaH6sMnHK
    zYXflozlet2$^3Wyc?`a>nlsox?d7QzV-~&@^p!3_z*YiRBX9hdov<&LS^S8-slokN
    zj|!|~bWNLec5=sw-(l%uqbfG_wy|-~Y<KGGgK}s_V6Hz_^j+xGPx1y6i$%a}9`wEk
    zO!r-gop{dOfD$@cEmN5MIGUc;Z&W-M20)pzs{7mRy0qL8vO_i}_TRU<47Jf}qIexd
    zb{{^qauo*uw%D${dSt=-+p}a+^}K7TbpyX{<OB{=Zs1PIhzN)1FxRby1-4I3Z>q5{
    zUgRju&NzEDCAHTYdLB-ATRIH4tgd(OZgzROrA=m*dhn~uG|;}V`ndB^eeggYO(~bB
    zn8$2=_M3pFlM9ZM-_r{H5rS`WB_srZ$~gJ%B!<@bc<ZP{dAyf0(NSj*I<;)d+2?oM
    zL~Ro@GF8S8uJ#*B8<^ku-V{l$^DT%gQ%zDWSDba#-^V9-G6T?8@Y3BT$m?#t2yl_f
    z97J=eXf9+XOJ@SRa?zP|Qk|%Db@Ck0Emw^p<eQL!pL=FOL@xedevTyklRPK=0Sg8S
    z3L`rzD3z*zyT-ETby}W^s?U)%#4139M#bve#iXWd!d6Ut1=(!MF^64`#y2}MU{V>>
    ziavEp)<?RLE~4vUCx^_!ta*K>T6a0Q9lM}Jpx|QE3Ffu405MD3)3W@Hb~|#YoybyC
    zwbiM`{M0&dVX*1i&t#R<rD9(3$61VGycZ$ba04R=Xm%qw{4XhB%wM*PiQ!IreK*1V
    zL{BWfL6D%uZGG_RrD>d?mWq_`_O5!zwQq<fG?rRTgG-@+BoK+)E18)euz0&RMKbf?
    zRRXwgHR6jfMJ8KrzuA>YXtkpDGA8{zYq=bA<YHp<Lm28)G#ZuP6EYE8gzU*GWwCKF
    zm!LuzT^KLr8<~~}>f(vb?p>-qMNV4#Fl^F|{tZsDvp1na#6&DIckJv_Lxh3gUnW}5
    z%bazn=G~?sa$&$WB>fuL(Hlh$n!THGu<Whl_qM+rdr#GagG(f39-5!cS3O#^`&L8T
    zgF{UigByLTnH-xFN#RNp8Lr>9X7v;9<|Kwqbgf9yJuLo#G54@Udy|C)M`LB7HPBOV
    z_R)VFer*YGG^HuGDbM&DuELP+?5dXSlB~hM<g$P$eA%PAci4PqXLyMqb)HMSXd1Ca
    zjk`5;1bdwMOupnE_Xb{xtDanOCU;DYd(NT=$xn6;0p8q<vGU4g(Y|>)h9_hVL3}-*
    z2<`(OE+2o!Pah`R1rmx7<>LWWk~SdXTlw-AQ8S~F4RVe{xxM6~<=32wwj=#AL3{~6
    zhwG$%pMUbvf;ko&gz~n;Aln7nfwbDRw_JUg6f+-w<RnDVTa`!3xi{XH<sjgv^0|WZ
    zlM%6Uf#t6sKE$h@i*-VnL*(WRXD5KI-g^N?1Dr_srd*srQ2f_Ww_@ZwZkXp>&7H2X
    zZl_(mT$Re!f(oJy{lXa<8UOZ&H)`4wBRWaVM;(jWG}VV@a>)NBG9GFAX)K}KS)6CF
    zXKnw49jwINU(V2vqUMjQd*xlRU+wTU)!q)WNu`n;PnK?&MM2lJVIbA5SSB25dzLsF
    z7;ujWcJik_^s3swYBz-g<}-cTYtE}oWyiO>X<afK(Syv%RHXThm7n#ZodRlFY&lCM
    z%UL54qSDuqbgm+Py+^b^$f5up5f@e1Orq$b0lO_+BH3pa{O{!cYOP+H6&_4~FWv^!
    zFkmkuW~)zNN6KG>cAqPJZ9onF(bg_yJcKX^aJg+-P)S<j>xZfe+m{UmTj6`G#{(59
    zz6I?CdY|C{>QpC1g@+7oa>6(^PZ>JUwO%Hdl84-)cR^SQ1x%6NxNyU&L(HFCw}2N%
    z%~|4tJG0u<3f{|rWWsihTmw~M4V2A}#BmY%ix&bSdlLPFF@bFHjdz&R_c|#^KO;98
    zwBIpmquu(~uTFisPJz<q2#Kymw`|8fTR8wr`_W688iEm);#tz~-)6^5?`Ub@Wc&Yi
    z8%jRPZ;!DrG5}X439($?bXpHvvx+|+q$i^u{x$HSqn{MMWQprLPu5}xL$!kgC@R7m
    zo}oDBTDxrWQx15bP2`dKqt8f(nKzYP90w#*8ZIl653$8erSH6f<Qb{9P`@2^Ts>~7
    zeg0zQb@Bmm)S*e$KE^pVP{QqkraL`3pC`rc>o+Zw?1f6q$@TcE^eq{s`tgR-x+#*0
    zE08}xRi`+~^Jx{tOU>9IR%+Zz|FxFo0osG(8MVmDE1a?v29MW5jo)*0ZByD>bqQ}f
    z!oCiEe$!-Ka_+B=Yj@HRazS|TTr*pjTIw)$%7ErBw3mcku&sd}Jn~wD^`kJJxv;Kk
    z3|Mmm!|b`Pa}72-7*z~euEfsTP}~2*)HyI$0&U$kwmP=iv5k(6PRF+G6Lf6bwsT_J
    zwrxA<_~qWJTkrc3yK1ks#vC(S9c;%<>7JHr*jJky^S5r8H*c-qR@Cop=lqx(Enlzf
    zEiTe&**ws#A6sI+*!srGCBsS@H)td=$6-NK$$~(_->sZ!O-cX&?#a1FRc3=UNv2Qa
    z@eWbBx_fX-5y#k^A?p%Xj}z#$es}iRaOITCTnSrS6W4BTO%|R(1zlS|Qg|o6PbjjW
    zf~0q_L<F%nC~)v#mx~boGZH#E?_ko(fs~{~EV=#}MxI#I#hM`8#+S=`5=8o?-P(~f
    z@{zpMhuabZ<kcbhyK%PK7QR9{ZW_I%`GhKkF25f?suqa+#Ja&HS`FxKvvcpcMVF9u
    zj!~Tdg3jD#<HMKy*tG6wSHyE|MOYvBkz71hwLSV)!=;V{ZE&>c=f<EOf@yk)AFljA
    z79joMXz|CLQ3(vLZrPIDP=PQy|DmzqChw*waUfyVzNZXdMS|oQ&hJmojwwV%8<bWP
    z3Dk!~v}g<ZVYf<ft%aVjavYn3{&7Dg6Kk2L*K{kbi$eN$3ku01$=pa+Sy5~Q+U)Lc
    z-y^PUTVca~J=w-c7IO{}s{e$*>g|b*XGrU*W7XR7bSbO{U;CiOQU%@7=wbVj@?41s
    zT?(6bT3Z5PWJhYNTFs~LeMYc{ECweC4Q-?T*rwNl!Blp;?~GP-mTNtDEo&q9$*eFU
    z2lY|xP0^2t=)ju3&Cp$9K?rTRD;r-ehA81PiOZ;@7pI8Hnk3o3hvW|F9gG@S$f#V7
    ztmxU*q6wn~S44XLcJ+)nH*g0Y^mECep%v<dUSihQI~HQb141lwxE)TUh4;kOQ~Nq)
    zELL;$vB|}`6RYLxk?EYcmZ4k!rUp8D+Xk?9h|*{tLFTO@poycuzxQFM%zC%{B?_bu
    zy;3z|h)IHATpnUsd(sq_XifN6C9>{m;etw6k`yBBLH7HLrPJP23DI*HLgS`Yj1@o?
    z5zoJx<FP*9C<LazAf9GY^^)TyM}u2vEE|;&^};0x*RKvVIynO{Pu+9GI4^-Tvrr+_
    zTxIAsZDP23Fo3@NYAr}r>73G^yxZa-s5D}%m(53u0T%V!Tv^!!A#5$X=?=_&yG4*A
    zn65?Tqn`R?sb}Ff4TG*)P+V+%ui*}tasBR+_c(pNJMbMzB^b^JRRLAJ;L_q&kYpcZ
    z9H)2l8*#1S(OI)(%B{n}=;bO<XJ6KDT&_~(d~K26KRPC!U#*K(r+H>TzfmP(qfi+w
    z%JuJJs1R=6AHljfN;4J~19sHe$TA)R&@@e=tfmLNEeT6nn{wxGkskaltQ`)a$*CDs
    zdEE<-@r)8uIK=6LXmY`YELF#B3SFw(<XtbF%!tyts7~@vr!Fx4N&LgcV$N&a8&Fyg
    z2QUE*MZXLdQ^$6ww5Oce=eYtWo@h7x)86Pxz~3vOe;>JSO{L2Vvr{aAPa%iovlD3n
    zZN^2^n=uf*P^LvmmSV*qa=}ICVW+bU{(x9zLX&YP#$|YI8=NI6?o%HIy>4>~mK@r5
    zS(HE-4qAP?quNIM;_P~o<O3@WCIG|6#^83nZxN*LD%{II7RE2EL!-6XTMd_;z~nb5
    zMp2@T@q%$hiGy=>otX4$#t;V_KeMYk_rmD_+`)x<A!Q*oP89Am+Vi3-Dym0(Oc5Ph
    zpw){`0<^_{-Z*>cOA{!Ukj`O?c+KDmR8e{`s4vSgcD6&hB%zwh>nu9+K=6RY^osVM
    zIA5`E&<2)i@yyhmn?xnUwr9Cvd(*<|z%|>md}Oe5#xa^@F)zz#>Vyr<)JcR1XQjF-
    zZ(_Lal!j_0lj9m(HP@>!JP|kEoZokm@dD3U*ecnlL>hWOHAZgdu$R~mMJBMI6p9;V
    z#TV7gFI$8_UBR@-TVCsR?5N)h0^YGrintDC5X|Ulx?F5D05`8RFFZ?!lkD?T;^!sz
    zDEi|wXyhZ3lmeDDh!|cPQ;G2661DxLL!g;|MbfR5LWembQbAkEA0!;+wEL#J-!ma<
    zQ}u>vCCFUePVRwrM;AKAgA2t>3>BzRmyyxx?Y-Jv)uxD0frJlJzJ9R^_XHeeQpO<s
    zb2xW8A_ll~MTvM<JMa&r&(U$qEwUdlXHd#l6R8p)N-Xy?a4@Z`xAAshAQ)mZe>S1t
    zB#P$(B3hT$xFAHI<?{T0qB8%`q83u__%2DV|4B19D_2*}dtB1b*J#;7{iDa$$^Hlf
    zu{7M2@-Xs>d~;H<m>-J9ZAeeDCki55tUlYO$zi<I@6Nv^CB{3s5^ZabHVuT|q+dlf
    zr?$ApR)b-@veEKGv2A_aBiCdgwy#G1pX*_uYSKRC_LksTqpX`O$oCfF_@<>3a88fZ
    zvN;2;`ONQG4Zc`+6s%9#&D@QL-95cTKjMQyE$en#8MrrAhEPgdzl120VdUzGgtKfc
    z%LXwQOTs}o+DC4)Vf`<c3V8>vGQH+wuj{BK!e|{5&3D%i7GfF12<P3jY7BmXLrAJ8
    z6E1)=SFk4~p#v+IB^qchcd!Q4!&GHDO2_gfZOR88B(UyT2s-wyuC((Acg*r1)+%an
    zb<@3OiM*g*tFVj0Gka@ve1x)Jd?<xmz`!yKjEndJ{ChlKK3p3XzPSzw5uE$;2Evy~
    z5_mwGD7_n_-9`u!4ao~sD^@pL6`Vzib$mILoMwg}2INn4(ThPvpU@rmy|@QYhq0W~
    zf2L_aake;MQTEbOfxlAu<POj;4iao&^XUsQZ{*?>B_QrJ&vJ2#p)pZcVx%Z2iTSm+
    zWA9;aheIQDZ-zevzORD{&VPxvE!^IGHeU$#O6l(k{d2mK+7$}rvz$4MFlQlH%yzV-
    z8p#`b2XSRXGrFM0-^@QU>cPRQmz49<|L5q1^UEshhKlOWwvjX%B3kqsqzYurg?NDS
    zOLEowEEf+XJHqXsMnc*#P9U;+%A4^AY9<=F6{?F)rSCBWuyN9I9x{g7;RU>Geq`2+
    zh|nIN5U>(bhb*=^s}S111FdlWBuj!&hpB2C&%5(L{VDolTkYitrR)AGXz13D?*PKO
    zvn8RIwF;h6bPhh<xE=nv4gE|>ynNsT%=yCR`r9`h9F!Op+)-nuI-RXc8pF8i?6Vf(
    z`iYhPMccWNvMWwd6SB-c`Oyprh4(!%crJO5{nFjrQs>$_6d5M};7*83d+$0(i*)!a
    zKCfo_EJl`k?QnbzW7g>GhK9HOn0-{lanQs?0m%J_P;>4%w0c*^kOs;0jQ`5tH{zjS
    zGIUP)VN=BU!7B6X@$#rZHpP({t>RjRX2~nZZW?#EB=D}?XBp&fg915d!|wUnj)+hk
    zM80^oy&GVE4$-a817Y)Y;-1}>=FEkj*IR?7Y-}i=SGF~%^JeeCv*%&=`3Q6!%OZ=d
    zQzt>r+T(0CAFaWi5nrncck3C?uex8$2sUp2Ipj;g6ceD6f}@RpgLedSV5(_hk=w|T
    zJfM?NDy-6EO8?Z|DW2LhXtvj*8nA4tpP?0#-mnnk>b<0?oe}rIn-k^sdgyC8e+)i3
    zT%PIhVv#1lOeRP(bjB9K^=mxqG;zLPNphk5z<@97O*F@m;b+c7M#=lXLig$O59Q|;
    zs|Gr_YdB5R`U^jvSscX@$kiw7#}ag?wvJ)(03Y8Qxqc(T`3Y(EsTp~nHv#i(!~@jJ
    zi=#=L&K{px1w0gxY?*g4zGAE2pCbQ=&v!<ql-Oh&UqW=R;2#DaMMT54PT#P0bUZ?>
    z7ETY-*2npR%^2#(QIXL_=-!VS@o(9}h5T%7EAxK%-ITFtvN+0PL9QecX?hA3v`gWV
    zgHQ$#{^o_#@$MY1uN9sa(LyU6!PVHNU0g@PDmJHqyBneI9f!4K@10Ac{)L{mj~^5U
    zKkhj{vyLrNt0jbOK}^PC=AKm1z}Jy;D5`i`*R?tc6lk+4{g%GA|K@BR=z~g?`D$c!
    ziSq0#x#*&F_O7Q?CFuV>$Z+PYA~M=CX?j1{0-@ieg5KcUx<#gQ{U7|PuT?;U>34P}
    zVyFM^B97l`fzeCKcwJJ+7Y^!zU2-brG-EEdb<pSpIJPQ0lwM#RSyfNvXJ*9I!3eos
    zLu?GA7T0F4)2TceudQ0{ZS`d6U}^xid0w>HVOZ1<9&~i#MO7kbyvFdrf~6B>k)%E3
    zZrgPd0-z=W^wZl$p7`VRj@YWZ7q1i?gN~BYO+sVZ<5=XeFeDl=tqzo{zd~=Y4&>g%
    zL3hUk%xW-j+0oMO$i}ydZw`K0uxUWC=sp`}hzOKNZ@=Oc{5*H=;JwDW<5k3!ru>8T
    z(z?oZW}5!C{yS-Qm3CxLHTIlYiJV3V0|dLenw`n&rt4ipvY&ZqpAdwa`kqsB5KpJm
    zz`;VH6gJI5ryPc0SyE+dA958tSxqe*l3KGLJNiM6M@c3DJc|F+thj}s#H!Xn3`ijK
    zz#IjYGfSYb6lRHKmMH25{8H_V8p!~5S$eO_T=wLlH48t)8Fv%7=RIOe+{q`p>&fvY
    zlFmCbCo?^222rbIWP2!m`xjCvvX^0dW*(;jm5-v#Ycy+NK<rw4pSc$wg{o}8baHXP
    zF^_?nJSAp^&&V74kHn4i%~)1x(wwAVVY?p0Sd-7fr=LhT%{>|c)ZJ!q?R3<j8}zbI
    z3dc9*9Bumc>2Ak?XylwKcXXUk7WR8)K=Y?-r-w_^Cd6vG@0+N`@GKW%4}ar9ccQ{G
    z`L+=K55e8<{E~Ij5hU-H1~}*}WPqU%jyY}jd^%H(UOq{t5C*Ub9m!bj<k0#|w0kZp
    z*`C%(yrUgD6Qklo9sr_0Sne6kGb<l8VNSu7vLO4nG@g52B`yf`^r7@GE#K2K2kL1r
    zJE-T-C2ouV8}78VNsX0<K)x)nNRQs?Z!_QX)QGtX8vY^`<|<H0Kt9a{Q0fYUt+fj=
    zNSd(mcugXorI8S?a8u3UGmFS(lcsD{9itK}zr#J6woj6FCYBBBE?Cnjk4YCqV#nF3
    z_JPHJP|H8AY-A~#S}MaKxqK!;Ykzd{A27i1Jv`J83b8HI5fqnLlyKo_1pc;ck}rA7
    z?XufA<OR9J{wog#k<IF@L&h%3icw*?3*jWVEu0ZfjjUM2kvyGvs;W?4;sxRFbn`^>
    z*`wfMtV56OEo1sIDcBzWBAYr5BjMq=Gk1+%BV8(G)5GiMnjheM<v%aLJs0Wzah1$`
    zeR~olbWJ<UXGRuztS~y=%HE@Uk>_GaxrS|q%!;8h5^iq_C{~-Uz6^4kmR4jrDA-7X
    z_MHFEMYJc5wW6`hsU@IxCDubd^6aq3z^@>rgy@*nM;U7zh2W|7LX^rnsM}EJRRoR@
    z5ebW(nlplrE>M8gJ=>r`jV?$TDjX$-PdO0<yfh|-2SuiL8)W&6DMCaHl+n)mYklu2
    zZ4Vn*8@mYTyoMNrfP%vi&e(OkHG4%<9pOh^1CS5vMd0@#g|<!d>z3+GzG~&|lBiNc
    ziE!r#p9?+H>_+z^IWA2!lNFw}IwWC67Qx2mAI_*WW9peK5o|7)4l@bRgjT-YHZ|44
    zuQuZS{+RUr+c3ckw4n)?T+VGIaZkn$_!(-t051LFQWR%RlndaGz3hyi|J@R+dGvyn
    z4NcQ70QxRrNkN~?i&pSu9-HP`Uv!Obbv}un668cP_A6gy>QS)ZV#+9>wbyo5e!M<f
    z?C27?CkN(5$wtsj*ReT^-Kg72u<u3^?gL8zFm+9IMGs-ejz!v#3@P@5)6AjN_TdtW
    zge!dSxJ%TOt-b3@rA4dFjre2}2J`UcLr#vZ*j3^mz|^btIJnGp1^n&(4)?h`o?_JV
    zCnH|Jp~rBXa&)|u)f(1wUQ4)`=}c43;~LV{+b3wD5pg2I8rmSiq`L-^Ww)aBuloJg
    zz2@Y%@(0ls){<T&i}iZVZ1*8umrK-o?RIT}+5-=RIcOp+jpQ>VrT)X(9y%e6%B5<o
    z$8l|u;QR=kCS?qiznO|!<HoA#njA~~I6j^hU$+0}&j-y;Bm9(Uqb54t=b?n?A+*jS
    z+Sv9ZWOX4})_i?#2~kk6-E7<lq6<n+=Q8%u#4$<mNzvb@CqqqQQ3{$u0-;yV_6c_7
    z_j6wF9SDEIsU%Lh6=7v}h2dfSTQI%?3OQoeSQ2RA_fB2`WSc79(417^Ah<}1!4>?F
    zur`i%Y<x6d1!^sK)dMjKkqyOlG2(sEz-)`j7jJ}lHI@L+5^XM4hccU)@N~dCc5b9+
    z;f@Y5>WU^vFOsn<ojrRj9kd3mj^>AMwI{)3rOO#9(-65+xN0zy4YcMp18(&ICcs+d
    zFvtc)aP14K;jJ@S%JbXqBc)I`MAc#7PHdtij;Aua!u2CW%dkp49kn)r9WTlEG>CEm
    z%+X2WaADn)NP<NS*A0n`W2q%T4H@$>!651RbewzPXoVMJ##5Vvsb-=k92^PXLbCd8
    z553@~6VaBWi~=uwm7#%~?p^|9=Z(tu9@p2^+Q@01X{^sVcojjP9wl8lVX}e#SUyb~
    zWWldIn{lBrfP{^4qg5$=(&B^P6db+)^LBD0-B=g8Uwm)lSpO+Q14~3yp%k&!(6_LM
    zZ%;H#r7_y126iw`J{HUi)*9mai8-5cC)!a#n$^eT-hmAr_}efGgHPNvsgkXD)6$V;
    z9W-M}JO(<|*F~<74xMNkU8g52^}T=M1<i+p56oE1|7g?I;fg@R(ktevL~s8=z#8PI
    zEpjlSMK%IoKoB4<sbv=;rtOS)8hY<pU+SnMf`;y%W4CmkR&Tn$5xdT?2*D`~eRT|^
    z2d&|BqC{c)=q2QbTkCEaRaUJ$ympGOm7OW7wu;;o(_{KeO*oBupU!vyyZX2JkAiK)
    zzUE2&#!&kc!Iw@vo4^Q1rN6dpsHiD;@{Av?=hZnD;?GPxn@p0b3*ywRybQ4fAjXQx
    z)R8lHqMNRz$Uz)K1mwH#WCx!0c6&nAFsD&zyh~0xk2U9~<GJkT%q5M#x$)p)p|K}y
    z<lNa=k$LdObH(%c<H$zT4>%{?msj8W_TX6V2pV<;CAi_2(O?=1?NJ~1nTj(pP+#D+
    z7)MstPiW~!8T_)+oa^J<eczuRkAW95_O8~(0<IhcdA~dt+YLw8r~q`$U<o|N4WdLO
    zPq{T_A;`of(}pGL#QXI4--rx!%M;};e@nWyQRYhSnZ@-?Z+J4T8~=eKc?R%>ZvN-3
    zh$`t}O2ef#1cjB~^xhp2@DB%CoOnRvVx--N_1t5UICm)>$Jlqo1a64(Z`3(>rqx|S
    z3wf~%iUX*zJo(sA{RG4jQ~$z0Y}9r@I3|}@^zs;)?H}#1%GA-DC}Nux%c$b#c$$m`
    zD^NCsMj8P0E4cts-H=v@MWFD0+*u2J&$Vti<qpdIRN{j0HI^a=imIf&Abh1B#G*%&
    z5ySG&%j92MHk4q9I?q>LqwY8)Ik)^!<@nwlZsYhKj$y0P!RDp?kT3|Dw~?esV8+Ak
    z;L8PbClh*q-4L%GZS;}0Gyn+7_BHDC(1^+|GLu*!X>IfYIS!FYRJj8K3l(umk@TX{
    zETcHz4Vs!|)9FeOvK~FO`JITcvf$`7e^;({9VZ!{6=bH}Dp^I;8KiyU$_p!aJ<Sm`
    zmSAD-*jTvdvk5Y@SZ+D=1Xo#0f4VIfX^{MOKo^UW6Y1U9ip-iGb$?~H|5S(hw039n
    zt$s7j2c!`^6Pxe8+Pg&u$}aL%min8}QMWZ`+ZW5&S3q=Ce77QT8|t4P?2t|ACPVJf
    z{l!zJtirSE{874>RK+v&%>A7&H*Yf5Dndr-f!J2#VSBsw>BksQX(zL6=HD82kjsRx
    z64ktIuwIFJtKVVXD3U!gNZo_Egbbi-R8S<ihMVZSvE6D8I1BBfKl#?-JvFbcRV{(4
    zC>%5v$XgkvPLKCNtb@<FH?eC9{K-h?d@cY=!<(5K+57L2b7h|cP9Dy3WEid)eEPoL
    zZeRdSzDanPr&{2&I6ZNWyC&W>0~k9ES=)B{!UJI3EMO3p-0vVUEuqg$LHZ`=d?q35
    z;%akponTYD_@iSZ2d5Uc@ZpqvZ_RdV7$XZvv-2bF(JX_9zNKZvQu|l5`)xtPd4Ek^
    zzsR<0Yv1GcQXtDGGAq$54{E))Pg2V%>sU*8bqkJusc`hA{pjY+CfsT9qWC3u8#Fwi
    zsjzOEYQ%jD_D2qLXY#uCcWV^GoJA!>@iETUHFXNp1D_q0A8Va;5cXN<72p(L@LrHl
    zeOfGZf~wW-4%xs94MVsps8&QJ40^s8A1C1+!PoXyuX9J=RUXri)H3W~&2-3?4ZMEO
    zCgq>@8r~S_O86Vi1ap7!1$hF0weYSmN+Dm^oXSMjoRIeMpH20WQ<Etzv(LcW#+Hwa
    zy0H>Ekwxi!UT?5o5U>81>&gI?n}^TK!B|vh#&>{9mEX5<&jYV}zz!iq1lvB=qy7F@
    z=PV;++UpVOa<^KeUMINFDUDjDwd!_x(1AQ>)FVI(i$j0&$Iy$X?uyI0%uz>0x{bxz
    zJv--i;zv_P7$&k9CD|M)lk3Yr50ho|ZS~hbjhQB<Y439puP>cjWhH!P(zyEABvL%F
    zh6@iJoIT%rMGiho4-=4$c(EZ_@AS-61af{*n4#W{><_O&-nqN!YB{@389UUkIim~r
    zRX;Puj}sBfL~n@VuPDA>y!itVhPgiwg0Li*jaX?{4POczpf9NHTuXlBUQQJVA4Mgo
    z1D(mPZU31xEl&HNF+4kchf+BzyTAB0e^hcMBO3&+wy`%VnjlC)=wvUWC31zSWC6GS
    zMSHom!x4wVVmk%kdPn8*8c)C_Uf8}FM(3DqSSRspR)Y7@r0n(z7#v`wVcj9oaGtLL
    zb^G9M(b;*bPR1ZASr%{}#1(5<N*Q&cQyzNM$x_b;*nh1#HTIsXBF+{d=iDUkD*4cF
    zIK8a$cR+O@5Q4CWg4O>%l8s%^9ha|lZeX$z=pOBGg2hsb@sU>vbkyKH3YaUJ3IkWJ
    znh3Wjr4F=$7-1@!DNuo@o&Gai4jVbM)}NQPTeC5v+)V<HICq=+YI(BxWYw~X&8AXi
    z$X#UunmVep!s*oQXU1%d=2e%SGOS6ec9YnZm=_6pB>omJtV~b9`9npeM*hADy&#Tb
    zDOf*X_aq)}rLyeOTb$MLj5z?$Q;8<;3>%TKlYEf4TGVi_#4f)nUPg_AQ_7(nO;s3k
    zh~u%+We66?BGpXDwm;&OrhEG9S*|dEFKy-Jx!y49|Hl(x>cq1^Q;AIsoc(dx&~o`7
    zL;P>KYNKP7N-$F9@#J4%B|)wN9`E>-jhXyi0du6E*6O`%ToyNnHR?YqQj4}R92b-q
    z-slF=D(0st9u43hCeH~4XtS@FxR8*jIo~;I<V3S37bIW~hifTtN$_rd{@dCdSIke;
    z3iker1X|myl@kCPDwkSq&P`OOMy)Ip%-%D}79@-8%Y6|UQ(5D)v`Sr2E9>caoiMG5
    zmecY=8F|JA`|61j2^%W2M`CB4Hsb;L$zM!SKa+l0S{?L@v-AoO><{rjD{Iq2K?1GG
    z*oAS5jE{Q2@UDi!+cW$6xE6>$e%XHp<{-x0At;+}&^RgrgcJ*2XnmhDK<vTGp;V(a
    zo5_d{?~Ak{$YGLK3d`8m?&YKBj3;X%6#=0amF_BY#6ku1p!>QA(SrfH{Edn9P0M3p
    z0v!k{=7FsDXa)wmbO9Mbr~aQ~nJT6o0j0re#2?{GoVC&bV-*(U?5&vPH4&lTkn*68
    zS-O?AogkqsI$FMh^C~A`7h#st*~Cca7=R2Ls!|*4%#Sj^lnL#g)~mdyEz;>t3C&36
    zDmVp@kW5ua9(%e7MA4CGzb=WMS$vA23QwUw!eOAZIO6^_m8ei$W;mcG<C(L>7$p{3
    z&345iRLT^DyVyqib>m)rTEP--2)XK2psGI|RRdypW?jVfal-Inm*`8x07>W3<V&Uj
    z6!74RF1}dfZ*Q^S)jj2Y9s{JQV^+yE&RxmnK8cK`IVL_d)5X`vrmN`sGh_r6mQjek
    zrc-wr`)80hT@ZSZBkkwFhE4~W#zojOy6#tc8Bg5`Q-A7d_e?zXqtz3-8Pxit$C87^
    zBk~zy>qmwxBT4jfaSo;{FFOm>PUq+7mfu?Nh--juA$kiT*J-o|?P1&q0iV@Swj?TL
    zR)2Q)CN%*JCv&okOF`3-XkLy$Zh@DdR#>$<X{8N~mIAvU!e^_-B~m;r3LEmXqGiUL
    z5PNpT`}>^x5BCmwY_@LOxQpD~^(0&XDr1MPK`qpr>&_voTi@s4RE1Uhui_5=x$cV%
    zwd)*@h3#Pu4(qp%PTZ9_cjmfHJ*+eByWbDjQ5G4etfQDFc_K;@T1daWNT%PGgy%j?
    zeRwBdK4-Ir+(7<JZdXS@T7qsa>GA>3#A8~!CG?8kW-EdbL-)V}aeEmzM2Q%vbT-L<
    zgquvuF7h}x#F=#383}>5+5I;iO7@W9^mP1clSQx3iz&}rw-o-Pv644eqc*=y{1Uo}
    zORfb39cvOPMQgi+Sxz45LS*&SS95Kap7d_YG5alqDW|vvbYbz)6zL@cXe3koUip4z
    zUR(@{GK3}36;m<8VDL7HsOl{=K&)~WtiGSle?F|CKtW|kV0LhH0D`{C6a-#UKEDx4
    zAI<z=STyf$*u&cM$oGSFDonThSX{kM!gxKln9}iXAfdeePowkm0@0KTq{~W7DWhAz
    zp2C<LS}NaMV}~6ke%=7K<HZ6%^zFehdsu4lYzh=V;!#?>Y!5n-u;bSfC*`58B(#Lm
    zzj&SXH0+IO!FsRpA9xuZ${DNgvfERxq)35s+hb6vS!K}PU`IE~*;?8ax43=V&4LBn
    z>Q5Bl8#%W>H7S9!^FI>k(*AcRCDgTK7r=3}&HWKcnIgo_VKUNRHyfIg6X3BdhOol|
    zIe30uBa9=su`L!s%cA`dDTYMP&3gOg`)Aln=2ziXeF?6PI#GHdcb$V=?x1$^7VAz9
    z1xJb-ih{%RoSp^+3%(Q^O-?71lwFS{MSSJ4;gM&Cod-vx`_H}=DsmZ+tT1xoi~$TN
    zywd}Sy>tdemh*8qVMudTq}bwkM_f;Jxvzwm^h4B(^e1^hcZb`&LOH>es!Tnd;CipX
    z?r<OYiHYK~Dz^X_b?%e1wQ)WvCzRx9(Y?mF88?dxbn8}GKFk9Qr45a_Vi=1DzDBMa
    z&;EhUZ6eiPIM}MQs&lwsm`tX7G|>6n54i`T8eva@RK{G9(KC_xxzM<*kIQYR?$9N;
    z{hY4rOx&$a4ZEnU{eLWgP-o%K74?k2TS$M%<Qs^hR1io!57|Vc!h!D)1J47n{ct$v
    ztlM^FtX_bc)OdV#_j_av)-jW5>*GTTqT}8gRo^p3ebc{eRZ(}xd{r<ZBL!!gn6%_n
    z6z#B2(t`LGMAik=`>T-}lu0N$Gs4LCLHi$>e!w`0L@gIvJEild4*mK<CDwHcKLz)K
    z8z$ZsB#{iCJXfbl%nf-u-nlALFz_fmbF;xob~~SILg5v>L~>r#H0obXoCwzkLvFig
    zG9ruT(o#9OodL})M3!!R&L`ZQ?^Cj#VvwP%Z%}@UzxEk;uVHSGHf?5A{lHwXHY=)K
    zccQGQu9*>sy35+&t%4f?xqq59>;fIo66K?;m6B?y3g@^aK@gqnWq7jjN+45z<AdeP
    z9_eVO?LbP>pLZk}B2t|IU9B1GVz~5I6Bj8%^dFO;J0ZSU)W%+~c1f=rP9iuC`Wg29
    z1f+pE5zuXSQVa>TMNJYGZX+MKF-sN;*lkS5*?gw&-*-(bcl6xk4xFmb`ceajK-$Tq
    z0B0P!4+njM!Qc3jZUwsFx8#yZ@XRLd+?D0~9M_e-Vw6lmBfkm)eS&q5mLyB&Q+GNe
    zD8ST}JEsZEvIry;k?zrr@mckg-rXpAhF_eSv&DittgPHw%#?3bK~g}eqg<dnwr7|A
    zN$&?#b|bY-9>A_8SEM?QT+`3fZ{rmwST;%T#8yW9!A3{h{e_}>Bs)Zl=5YhX2qkwb
    zN;lWnS&=7y1@4ljfy2BC<E|WhvH8f|>^8}Ub{q)upuwGt7UHQuUdMHSP?BS<K+2)C
    zsaZKYKL)a$jXyL&F+|C`a>gDyw(<CB-~9<|hebG;T3;`we+=u=x^_dmGmE1a$rZ`U
    z=~Cik1dYu=4$8Al#jlO=m&GcaLAa23X4Ch)0=U(mBl0Kj64Kq%={mt5@inEI>J*|Z
    z?LMutTw%a!2$2(?n`;W5NxovZE^%wGFYa^$gmDs}2i9KP+ASi|Cd|%!D8)ppb2aZ|
    zvCY5`pfOAW%ZFHaYF%#wmT88E`i=}{H{s~~p(gX!WGawhi;@5M3_g?k?sfFIy<@W6
    zQ*4my(6!!EY(VxVhKnDkw=XmOcvUt+OeBmg6;}+Kd)6i7a7bG<9L%X}czU!N{!c6L
    zvCBuQ-z0wTr_gJJgW{Ls|Jjd8G^kh_)`ld<97EA5G`W+y?tmI+q9D2xyz;1w<txl+
    zC;~?fGa=^rH=a{?3t}p^8jAa(J@|Ln-R~f-XH+$F_uuFD*b*KNDW)Dh<MZ3HFg%+C
    z8UWhtiB(xld5mdQHWMD;TUHu1xgEoV-8z($^+}-99Tu3!O`@lGsXCOJ;T4q+hcE}5
    zx=cJm(y`JJ9Z)gCnj|4hII0fgs*r64Y9Os%v|mw#PK#9QWVZ`%6K)_kNj@w>P}}-M
    zUDwJG#u~P0h$Kn$$AuJ;>}R_gY3Y}Rhm5sJo^Wj+Gwg^S%hn&#``_8H?@f?wQjqtK
    z8g%Ncut16IQ_B`rBhKrf?9|V#+T44{$cAFg)sJmm^a{V#y+A8`?XJy0!G8%y#C&(9
    zOE?b%!|yg9Z_^V`F4UNBlWbiX95Rz+gf^3-b`!@U2z7qO^e=wt)e}6MuOZi~K3aM)
    z&VB9$qIiFXw<%Ai;G52U!YZ!I-j}a+8<{gXs31Tbo_0*;WCym*(3U3Kpbym&YPzn|
    zSLAS|bGziXw;ujO7{9yw=127KBCloyb-d&%&kjst3;kGbiAyyo{gnlUpXZ6-x>f#^
    z$Vh53Tc)g&v7RdwyOV{Cq&NAV5Y>FA)WcQBES8e1jg1{Y+Pa^H78;<5Q<nDo6yU9j
    zyHuIsiv?J;e4X^1o96S=sO`M~#rTJJ*F1bpJvHl)D(>_50=!#Yi(?&Rc;>pgP4omc
    z#3iTtCn<r^=09pkysM%wcju*u%T|D5`76r`YXkR2={3Ua52&emC!?wR1J>!a5$9<A
    z>W*arHb+vfHUV|chj5!<#nb7<9Y5mK5Vua1?y4?uDWXmT=<>NDz7}%;jR~n2cZ5zG
    zsCsvM-A-ySGsALf4hCQ**mhWUx*~5+(|UrCzFqupF{V4`2vvuU`$k=~y-*HTDO?Ic
    zO~>91Cozk~>5NFti}<}i(#V%q|1vI-_HVq>OSf$WSn^&Jkt2L4W;Zho@pMbxe=eWo
    zg1q3L{$d|lHv~tO6{r8=@Af++yK!VlgC;b|UzAjUz<eUiG%zl;^Etel@s0dYx)3+r
    zXD)gUOS`bf*mYRi!5>k7FHa=TDsoERGsAG2Nd8!?$t`C9He3*-yWB+=q}vu|FKPJJ
    zUCx4)5ghACCu)|pefoF<p@r-(x7N2wh%<zmA4T;Nxt&<6uE=Z0%^69hX87{AFMZhe
    z22Zly-%zsbq`G2EgWm%8OC8a9i7Z_XM0&G5+MfKArk&XrS_s!0?XhpjnZ-`*Pq_UK
    zvy(hR7Ds7Rb|LBFB-5homWz^kKR_utuDZ*+Z(?RMd8*BDB-id5Ev=LHyZH2!k1AK|
    zAy-&me-nbMJMrwF2J7pc=TlQ-&6Ey+w9%wgRL#Kv8IaFwGu}}{KGVk|H<dIg-am<%
    z-WHOM?kUMBk(izU!w-j%)#~D_zZS@^(IHhGIW7LI@xW@*>sgApx`uye(!aw@)4|X&
    z%#I?ZV~0bzB){;4aEcB(V;R(c2wIxmtft=dUz%soGM*{`#mNJn49uglQEYvo%RqCJ
    zO;Yt&*Kqk+pxuw_tE5wcsk`hcNeOZniS9+CjyW{*Bq46VZhb32_Mo;GL9~Dmf^SaQ
    zXDXG~)iEg<VK_@ZlZP+bK4{uek4Kq2ah36pvy=U}%1O-xzTi&E&ZG$fA0Rly0vTv1
    z(^5!qfv_X7f<}6hLdo$>d_JD?bN#N2aEqrs#w@pEzn9VSy~du}i%nDmBDng`Oh{Z~
    zWfw&Af+R}F=&<*rQY}17cEw6j|GAjr!Fu(z2Zf|HrlT3V>b%RB|2o14_iN`_0EG5y
    zX+5uqVXBq8v=uiT<-L7?)#@<myRG4#EyPFirDW8;AQ_DJ;#Otzh#><Ec4gfnpb-?B
    zAC%$%6E|~vrC#Lo$22q@tkw&Wz<q}O@?9_RS|eDx<wMfmkEEs{fv2khs0f+2^1tDZ
    zAo9Yxw0N}kP9W4|$z?z4vSDw)QHnz*#uVg(XX!71iWR3etEtNo+*p#wZc0j(nzcT~
    zH|5=h88axm2lymfqrhEQpar{+MGrDA&<3R~nRQF!b+(=*(C2tv+Q)dF7?v7-xvL43
    zy~#&$`P-=QCE~s?H)kv&F9=%d>SGs4zmpp`_Ol3%s-EHUlS72M4TAXh+J^((maxWs
    zluLCU8BD5CLStCEcA7n&qN)8t6h~pNR7b|RrzFlWWl?imR>5S7Lf0&4Vst~gS-m4n
    zv;_09;ER*0l$FjKR%(55lN}}ty=Hn)Q<7QY#tkcOnJBM4q@1a&OM?oYfzF~sqRNSI
    z!Ykp0MARrGIy)6F068iqAGCH#ngT@)aa3F<spdS-Z&iYyeMIa0qCWD4tscO0BUEu_
    zayw7OcAu_RE{vN<R%M2@)vWqkwZdCZjnBFseg$_8G8fDx<=1}OHRD&H8(hp%erg8b
    z@-9Jl1Nc^7pS1h{Bmxo2ot7IoyHUm?-V#m&hVnOY86|E8no`};L6?S${zMM2IlopZ
    z*O}HnlK3iW<>@pIOe$Qy7C32ypcaR<29(ct*j|XMLDzsjK`MSb#&c%3_3WgFq&0e!
    z&q_ES_M9)6eoGOcZ&3+lqe=K8x=+`uq2QQN+rdO@&lzCNh^6ePfm(sCG#<f>5o|-y
    zt{b3j?}IF;(qzXh4vs-9D8NgUhEi>>kHgU*I2GoW5B=2YZTD*<yf2YA@89a!lOkHB
    zH0*qs7pKm-so!;I6^^xS7=&b#(B^k*17)^tmT2BIxkqr*#L%<SoN(WUh26=(CV&0%
    zn!dA7)Kg2aQ1}JE8^ba-RYR@d7lLCkLvj6gFz3EI|JZ9X!0VZf4JQFp;?28@w*}!9
    zQvp4UY`+*|*XG0x;|4X9yc<FR(sb!B=>3w8g!Z)?#;ATP{^^K$@h-$)Po2LG2DEY%
    zz%1ihw^K0XDTu5VEcoRU-bs%*5{xJ&<#?!1ywv*|y>5nO0ZjkDf_$|IUGam-HqdG4
    z3Lfr(vdk`7B3S7N8R>-F^^6qo5LF030SXbwwOz%~-<F~U2YsrjgXpG#-SE#hSzb#!
    zNWO(apdj*sXzKf0VL=jO37dXjMrOQy;6W(#P5&e)G4=srcVS`K&K;NbJ$J@Q_B(Eg
    z!_X!QbYdp~??ITym4wPjfgp#VX!PG@ClU28XV;AzKKbHCr%Lyo!CahU_3~;c{M8*A
    z--^NDyi6jO@<;I?5dTbniBlX_EzC-`h!C`rTJtWgvjsl(Uo#p0OH^Jn?;)R@)TU)&
    zEE-*ss`h{giqAVApQKXXsj%O?0iI}>k8@Saok)~-N9E)>J@5l7V4$wO7dhWL(ut5_
    z@z9aa9~oh?j2=9&O2<P7ZMfW^_St!#LS<NU+xN)+_f+rJ?(S)N(DP9}vw5}*CZn(*
    z>X0)=35dFM+y~+L)3P`~kb8wdog*)kO}C3RQA^>Y=3yOmpX8O|G-qw5`aiAkHX)jX
    zS|v^oH;{Vy<PtG)ZNPe{hwH!^=iuuV=Y#k)+|90nqe^U51Kk?lJ-^st&U?A9=fm+D
    z&R@!ZvmLPd*iUYby(AbYkWme=3>wn2rn$zlX}7#ex?F08(^B5|K0bAf=X@p0M0`uy
    z?kcmJJ4+S?jvLcNEY|J#V%l4pOup0I3=M*2NXq<S8Cf-=1f!93+;oqD<1iG$BWavA
    z5l`Y>CVuUk8<jGrh53*nJ8HlmXS^v}toD#5pIbsVU{|H}kkBCK#BS3p!NKCSrGJiP
    z8ilZ8k%~IGS8q4nURtX&u4}eITXd~dcwhyfy*LD6T@)&@(xfZ1m$=2yojH)ph%7xz
    zz0y7Allao?J6BM=WAXeKg!cWzUkC9dm)u?hI534MqvjH_*RT%#kP$C-DLLP?uS3RJ
    zC6S$755Xp!<<YapA=0hiAj$346zY_zC|Ayp3CMJhU57SbU;j(l|Iw)){Npfr0dYay
    ziYL)$?#V1nqjH4Ku|!gQ$jXyLhC7msd$tI1vq9%cVbp&S$c$Mkw*#5Dak3jHfj_};
    z9g2_LlJq<wrQf=5QB?WI`UV!dl0DUkqVsoS8M0?q53NaH>4JraF6iTT_3-CzZWAc_
    z@uso9mUp|_W^daQIM&pHTeP%oy-PCho2V;t8`<vJYWBiY?HKoCsoGHu|4iCL+*HmI
    zvGQyfu)QkX_N}zcu<;$@o6A{We3&@Kd~sp>5}Lx=s8bi>b>D*&u^~;_2zJRlgr|p}
    zV*C9wg-3sV?w3091m>4y1k2_q;hyHH5^J@L4-a_GP4I5s4z3StOVvK&M4>P(4>?_{
    zDa$U4uv8pWvfpvmteRNG+%0QO*|pypRVIwEt7{3NI9VcWgHdlM|1kSuq$lyg8ehna
    zRfd2!hn+Gw!4;=ukdIra>S=#fj%C_gUu^gChn??r&Yn+i@o-j@2~f25#$fUzFCNqr
    zXz1=|_Y-93AVHOr?KiVXgqRb<(4GF;{vR~7aS;b`<Ls$--hITbR-)KzYW#SX-(4M%
    zb+aD+&YcDrW6m%abuv5SA<O;%#%-$ZaRNYRN|HYP>paD%fW<jno#}{YK!O#KKKF3s
    z(r0$_IWRB{_Fd@Te@}T3cs#&<IMyk-UOr$$l<bO|v@psufAa>+72?_~$ylhL_l>xz
    z&H`pnR@2Y|T<p1xC<0EhN6H1oAV;x7S?}yiR8$YR!X|$WTY*5SCYBrDFcwug#JTJ?
    zEfFM%xmG*-&MT7F$I)7Ik5(AY^`<{paFh5TV_H8BUZFKD;6qlZ3{9Ah(uzG0JT@Lu
    zg7>KT?iAgjstN)_=AW0vi;XzZ?~Yg7kM;%thOgW&E_kElgdj1&sZmoCfe=~uygw&>
    z9n--@1_^&;x_Za-<vWLQes8b(ZIW~W_qTS8GhBI>q^RM#(i)|e59Lsy6KOw)|I3gL
    z&v42=tp`K^2WOm$Cb*Iv5vvC?Rm>8z8bzV=o5Uq9fN@--J*G<kOw`w(v&6ukKNwdV
    zsqUp7TRom~@;a3(RC2Qm!T5d%<RaI9_e^M_d?fQy5-qRg)E_<J?Rd6!eW=b3ZnAp(
    zrEprRM{VWVJ3VCPPPjBBs?DtRGwtT9i-e%puv9k;k8WV6RC(d_FWH7H4*!UuvD2Gf
    zKBzcMa>VjvKs=3Q#=6lOT--9Ah#4TUQYFH_wJ)~oLD|MJ8M<t)Wl<Ai^dKV8Z~9^q
    z#+ki{KXUI1g*J;<a38imPm%{n>D8N7nl3As9^d<_rDTc!Od*~MVW@v^cFDp~y8{J8
    z#j@FQA{ynl);eg3$n-WpJa$6+nmu|9e-t0^mu`XIKlfS1g&xR@;iPqXdLMR8sp<f9
    zdb5pq5Y;skgzslH_Ji}Thz`1RrlWE6ZcUe+hEy?4#>AL3x;`}I3Umocd>3tf>BgZw
    zVw_c2UYX=)-I(KL!F;6Wl9!vhI*)(Bdz3|9YVvpMXla=^ek(2h{>K~$P8&1E?Fie{
    z(Xr6VeL=Dp-3Vwv`}><oEmsKAkh6!7#0foTviJd99^8>@UY9Me(NN9#aMZXWqe3uR
    z6%J3xs#@FKVKSwQ4dk_b#w_!Yspl4g+r&%jP&we}*vsw@d{MHoRt+7<{?#wu+=jo%
    zb&_!ZKnF%y=$mcpBn#baO@GF@>UoCFPVPw2%zM55xEobYJ|Vtm&&*GrHJpuXN0{Ee
    zol217Ip%Mv89_RH<x%6df3K&4h<qM~yBbYszW>OJo|Y3tdtqG{;)HzTPd+e|+5}(y
    z0IE)|_1^;3<?84tkeHjOYLgdNBUH>HFDvb~N{Egytp>!ce=4t~U5d+0@bQ|Dgqoy}
    z7?@V$3awv2<;t<gT~B`+e8ck3HzykIwi_OGWc|(W474X}uKWRR-C#mF=x~&OO6u1c
    z<|D5?t<!arR1}_Yix)VFIYC<9Tp}ohOASszEGoIVAci1r(_y!*k+wzxzXl3xSk4hf
    ziKp8=Q=Y-J3;_KMSVD|ejK2)Y3cU4h3Y)O+bZ9k>M?^a3Ex};-+`EPGx^)B#P?V4O
    z_Cff<5f(3lBNRvL1@JWST4RKBj$luyMnu-k!j5K}w>Pbt+})BqG=8vQHy3yh*e!h#
    zZN(5l{2zfB%$lmHo$AF>@6fY;%IazzHS*QJ%&>^H(q`PD@mae+6-KYkEQTPqqrgvy
    z0p6CY<tKOE%vKPFv>GG^6%QG*jNVkT$)~RlR1`Nm4~P+N%<zoS9(ZDu*tiYd;kn1P
    zZc-;GVjs3@KjJD0nIvB5#ktzmgR_+r;H&GU9<}!|C_e1WtQf8_{dj%7#UZ4Ptqm*x
    z4~ts6h}q2|N~@w+rIr%mD2%A)2zmXIP-pKZdd-PEYps=wro52q0SbM(7YfBU_n3hd
    zmm=P;Z^ZTcKTD!=s%QwU@PrTU1-@b_9)|{A$|gj-e2C>KW64q|+vNJidYR|Q=`7TG
    zx=`N*W@NX_?AbUt?*R!NwYXz((`YEiqm=kVI$d}^gq2;yUlH|WbrH6)iWw-)8epPi
    z6U9sE1m2*heJ?oB`Mf#1+)}$a{dm8S+d^SaOkGkc%tk_$E7p<ZB!^)KYNhOOj2{!E
    zTZ*xxDOM%e-(d>aKKhUjA-;yp%(VWa5n9>)Gh|x&Qbj@P!*mg|Xf4;!$+OsLR?7B=
    z)S~5hbQ|izd?xTq3A|FGYUY~n3-s6xG3ILVPnL}Rfw=H&JS$Ft`C&qneTJ@cpn=U0
    zB5OnW>MT!91Ge!o#;mmOty^@R;r1GiyA|q;WsOp5<$lkdSqp;8utA=1^99VI5OlIq
    z1lb(9?7;e3KPV^R^riAWm!Cs%XeK%7v|4PZ!1V9GL><?{95$d9IHtxK)GZml98@#?
    zzsYv3XDIvV9bEr}2tL#5BW314+48N+l+(+0WKd`>O(V&nW!CIO8O|FLq<Xg(qFqEM
    z19idOn@BY1V;VFUWcOehXgxDYyc||m4WoXk!rZpoc8AwiZY?J*7tQW36u%bw@{ESq
    zO>4bY#4-3PxR2kI79>zdt4Gf`G%2Q(7t83FpMD^ANq}3IEh^bPO-yDXb}S>|xmnsT
    zh^X3wyS6f1dw8D-XY=u-dSX}Oiv+iNh3$KluM{yJKWur8siKn9dC=rlT@L$egs3`~
    z$Q*4fJHNhM>refaHtS8O8ZyPDs;Q0FqF%;=TjS1QPylA++CbRUvnl@aZ}82TI1Tei
    zOX=!1NNo6m*lV{+d1H?${5R+{uShG8X*HpMlV>cbr=e%Ovjb5FWQfd-2HS_7*tkH$
    zT(2`V5pvE7DNnepY_`g^*?o&<FltL~KK`Dqt4WNfNYNPV#r&fLGj>tA?h9k?jG+&n
    zILgapil$I<Q+uw;t`VB~pk(+;Z9u%^GB%&chL*1ZmkJl{gOoQGU6b@~{tQ;9k|VZI
    zwvfvZrv5F#u(9WZkMB0iz^(C0m$|l~_W6P(&@FEAgn}kc6mfLA7>xNq59=fD{V-Kf
    zQJbmG;tI`0vuVfsZ=IJ~l8w4k9HSZzM5L!K_8ga-laKV*#ApF*QHi^-B}4lk^<LZ)
    z_&Pq=rUCf@miU*tpFB=|30bN`1VDp>aC8&{mP8fKEAb3koigVKkBMW4s`74v@0m>y
    zkFY-EIVxG_Fvui@i{$5wi@7DhSG+rY>+Ja=!sgLh_xi~Aw(VNop<9rCOXAlV!4=fK
    z8FTjWXCOv%C1U@G%5fOGkk6D8^#0Z+<w5orul|Sbzv%4yOQ$#BN)5X@i$`_k$4}C_
    z!y6}O7?7%k7N`gjdFKleIMF_2^vJ_i1I{{^2-=%HuqW@VsTf~@gWa`BhMh_ND>+lo
    zl$dOt(78((sq<>-mUE_Um*Wo)3WoJZt;xca>A$Jpc%6i=?Q4RLDqa8=kOCR>FsahO
    zM%5dy1q?>KE?+G~z_c%&qJ<uQ&z>*dBYU!Tw$e6hOE%Ju8J$=QsVVlFAoZw~nFf2V
    z7fDm%DJ>n_3|K&Q_e{x>=Ob<`>09*ajR}LcF<M!|v&pY4pbeS{Jnt%w<l|s-^7sus
    zpjstamG_JK`kAsU01WGBHTYJ+wwS-5!$>m^w&~#S{l8U{gJ8ok!_ImPg&e~CfD)!Q
    z`W$#n!ZY8SQNP5tG(-(9iZ4Efi$H_b)r51CXuIC!IQZa`*R9W-DoCZ;hu8_LCFn|}
    ziF3sx0vtpPk)J*xXw0z=^$WwSy@~Y-)vdo{xA`w8UaW^A_IsV5!n&5cul+RnlQpg1
    zy&p2SdHM1!I4<Fdp}ykf!9ge_kh)q7RQ$LOotYcl1lEutc5KI6q{ETyyZXbg=a!Fc
    zDP!O>RL)_FQC?~g=?WG}et}>w`95~497}8Udvvmk+nP*aVLnWvcAJXp0q;re+ZpP0
    z<Z|CUzDwo+RkW^NbE$i_i^MiQ^KP$=x}nV#b8-BhrX3t_LW-<%O<ii*f^V8#R@+Q}
    zn!!_C!+!qcKTQl@bggC1zNLmOKHh)s1@stqGy*ziuYHQ{FFBWO>EX>XzfWWq=49w#
    z4eIwvcv8d?<Z0MDr<2t#g-$5t-3a7g%;RjzLAFYW<0hw`%_ON(+o2rQc>d1j3wM?&
    zljum7>JE4HgUk~)5flSJ`JnLpy1I><jUK$;;W}%Z;I=l)>b-i2&FZs}^)WR*!*r{a
    z?MU<<g--d@vWX;7qA)uw%RpD@;)#X<rhdA`lQ{`!;9H{G1lpnb$OL3hWlt)>j4w$%
    zwGxb0Q&0O>#v@iy(L~DQ@VtyZAC_Vxb0sI7Ni;WqNhV)a`xK$l&Gh@#<sQvdY1t6Z
    zR&~Y3v>3NejRlcIRJ$MNzOL~U!S|$<yvzJSz#-J`0&OY*17b;*$hDfD-R$h0I>D25
    z!x>?F!=I)<Kh?(hZZpDQG)~)@&3w91kaH+57l{Swy*g5_GIx70ayR8pltq982-A#^
    zv-$yL@FHrL`{<xyWn9(k6F-3KoY3d`Sc~L(6mec}-5!De0o4%C_*7{5U?!_lJCP%k
    z5Z8)IWghZpjj*c7U@DAl+ZgIzv?akXWksqYEVFoiaal1x3T&SoVL^+dp&BW#(m4T3
    zRGF-%cnPY+x#Bgn@3doi>jcHHv;5eaFu**g7?=7y;6h`)g+rglsWl_BiXjC;xVOz%
    z6wg-%kaQ*v_Q`8J)$Q}qrv3Ikd41eq<9V+}h&*1NockhZ_ax{H+wa#~dVz5*p3k)~
    z9V{;Vm1i_ha*cL`G}R-qJ18D&l2sGL)&5LCR8Zk=JSqVHv;hY;)q=F_GC`i1f!m+?
    zDRb_NGQ~q$@(~OYztyXu-)>P%c(}ASc=5z?s5S)H4fbPRJDPzXp~4#rw7wjh`N4mr
    zBhXi|?h9ALn``-t)hdB>D-o*z2K%I^fBLx7usQu>^}C-)!e3#pVq;F^D-YM|sSaf?
    zw3PGH9l#14>lY^V8nHHNg!7ffT-Z)dP$e$jVYhI8Uz)g)@6Xw!Bo`{Lm}u8#hT%dx
    zp@LX{>LiWUW!nG1|L+7g<HXQipZry^_x}PVDB9O20E#%ctT&1VveRv9a?bVL?qXET
    zr~;rZnK$zN(|u`Wpjg9Q5yqk#x|ucuK~mE3IPE3!2P~t$bA$In8l(W=BHmL6IzTz|
    zoq4%_i~E{)WzIuRI0sF<v8b6m>kZOp7JY!SEbdM2LI40D07*naRISA^<$O<FO5GZ>
    z+`!EZ-&4|pOg70txoTPT!z=_aK;MFHCV;3ObTMbke4f-9W*m|AX0r`Y-Uy>rn!=0;
    z!~{uU)GhQl!<iR4|0UXC3<53^xNBPO$x?<+`_@oLB6iL;vIJ&rvlKn3$ikgn-})s<
    zNEn9(GK}BjgyJH72V^XL!1>)nx<osOPBH>gYRXQK*Qia=OImg~?FgQt4S)<KL>wa5
    z)^%Zz1zh!%pW2Zsl*~E7wp8N`pqciaOu0Ap^fVIybi!W__bv6ELujFU>|`t_yo^<%
    zw&tc{eCtWU(gWUP&ab@I#kW?O-Qo!A+gBBr-(Q3=mAFoG>Vum}dI2+x;2S@?cCvre
    zk~=QZQ4rH^gCJ}IdOE67-xQUpvB>M+!kjIc^O)3BR?W}R=?2i#u@M=#Ix{k`?*Zis
    zRd_+HBIp*tQ7gX{`EWncWaz|D@hHC;{ku8J?l*(woBguDyMAXK4qe32CqCf(reSUf
    z5+$om*X&>td~ebrCzen)xhO?{KDrH$Kj+q(1zp3djYLgX%bI{*d6!roqMO5FRUS{$
    z5JeptaWe2reHiX@`E~y8!SpI8{`=$Ujzz~{ZW(B?vGZ*4RKAyDi(R|JXnY}YLcOu&
    zCt<T>?Ul|_cf3fUq|e-Jpv>=;tgPC5lXNy_E&j8Ms6S^%E%0hVLQjd5Lg=$J(CXv6
    z?6&KB<wJlG(&zd@KOk|oOa1KiVpqg?{$^tk`|tRMPk77l@W0q#o>~;tz(~fr3~oHe
    zDniI6v#}o{KFE#5>IdvN_K0*jOdP<OB89cOGuA*Ni?dh`TbpK|%xis`^f{ZiAl%a?
    zL2KiYqb3YGFpWW~a%~&W#3#>q=@kRbymL7c!`shb%obOAc^glkrD_-Q=-6cKNo487
    zDB=p1Z^_WQ%9P3Ro@^ZSk~~)@&#W9I;EKq^fj&o%^R_kdHyG@n81Jm6SW%H<<Lt<a
    z7C<|msEsWnaGTf4r?gFK!;xjn*9<3#KCA?ze!A@p9%mGa&?(9n*OybbS&mjPPS%`b
    zdh<|gytFw|?Bi+JW9xuB&6liZqPRxWCLlaPooJuWS?E3wF3HJC_O1OO?)9cCK&BnJ
    zJ-PyGM>oZpKM2kr3&EcakK%ahU>}Ia9mH-WP^KmakT(;u%Ck)ldZiKu3~@!-*RDH>
    z1$3<#Cge;*IX5qPB8M>lf{5H;^8NY@*E_AP0o^RT49RO6^a~WJ9M=`b7vWad`@7G+
    zVAs3keFtzS`}87)U3&?HjLZ|MDG_iL4~X+5Wwd4}uXRe~#`?oH9p8mMUob&hz0jIG
    z*X89>Uhj$5H<{Q}CtO7*mW8KUUjaQNuJ@;g@&m19qVYV*$A#Tq(=ltY#3+#Oajs!!
    z*0=GN#L-Uu32ztDpR?-S?v9JJ7jpnucNzdR*AAx5tGUjd)NcEfp;q*yfmM6t7>Jl1
    znA#bwwINnh0P1kp9NNJIIqEh?|M}T6<R?p)5FG$S|IOOBtyELAzc=%V`>U_{>J2{y
    z3;`4cu)8=iP04j(zuy>oTGN#sahg^tVA2(aIDxfmR%YJI0$^$6!U+I|iet%=qwQ8%
    z6~??B{ej?VK%slde&zjDN2LpW&OB43VG>tlz74#ge}BMg#^s)Z)=$Zo(VD2-JS-!W
    zqTSI_ma|<QS!Jhqm=hqCYc?9@CQdTv-GiOblTN?d&oqq>IKOFlkTC-o`p#C%X|lgG
    z`AuVzh#w=A9Ia02E{}7E_L5l6rpgpJI4Y#3h6*1jI{7LL3q6je0XO3v3c!)WqeLZ3
    zOe$Jz!HFQAIk4)usy2X5I8WI#O^aKQGuf(%Y(Pu5tIPGZujAn6JuSENEsmzu*rDHP
    zltBy4138&3z@Drjf+zb<GV7d^J-bQG?YTlX%7w_*t9%Kni<^!Cj;b`ozE*z)&JD1!
    zIhYz56tUGavSG-ph9VZ8<e)V?(0Pz5XK#SXB$n^bZB(gbmkC?+kSXTlBwEbgDm||m
    z43&Z1_JA3hnjmWBXfuxBv5yF!@NVG|S_@<pCKMu|r`&xKm{nvUHvOd=J$yBv9R)dX
    z%fJOFIcNd(l6MQERVD$Hn8B0JA_pRMoH2Br>2aABgsqi<e4hNcL!71^cof%}O1zE0
    zTEjH^JQc0tI>EitBy$!y;%k4-S-<1jH4gKIN@W9S9k{?8;AmFp$q>u`UTRWUtuTVs
    zx^bOk4NgECtx$W$yQU~rD%YW>;uYn~R((*>!UV_}*Amner#QK+l=)irG3qI|np%oF
    z9HlzQx>3;xH8!ex`99bC7~QiuLhg=GV+Aa<rxuHMqE*)%2bPznS{=a2Lw|cp6QvKC
    zjWW$;PJ?rc`aa<Nt|6E+fHK<#P=+ZWm}bYVI0C-h9HQ|GRx#=t>Sj1|P?VJ+Fs%?T
    ztakN?dl6b~OzC-l21{y{xyyY}K%&P?<-*k3cty-@5GC~5OeAv-i${W8mJb}@6l1Wi
    zIDqEb;Z}R&6?49;iwj6kf_0Z(N$LrnAa+<yZnXiNtTI#$15g!wtyV^caW)_z)`B8x
    zIW>dg+d|8s!1t0lXIH6DVa`$?4gTIzft4$TjMZ&@hF-I?FM&7;vS}Va;ec2N0tLFP
    zV2FPW>xeXx>}&B^C#Bp?s$3C`Np^l-`iM!bnWXi@;QZ^?ugkTMh2Y;04}xH9@+q()
    zx2^@exh7TcO;x+%NnG!V9Izb(afGt_%C=B-mW`cb?|>X$y~s4Qj&prdyunGK;i^Cv
    znJ)t+A}JyfeLuoMJKX2sRad3Sk}YDMT=GMJ$k%nyV7_Dijymx_#Ay@p`*^;68FLxy
    z&j34Hiy<}%`xKN|X(V&TiG!P@xnw634Y9|jHGtSCBW6*3zUJYxfu>e(V_Dw7t9!||
    z-(%TIbOGGC1RZkeEL&{m$@SERFs2~InNkJoGD+7BEOv~h7Jc@M>^EDk%PTLb4q*3j
    z@?LK?T2CSn5sRiZPFDFjaQ=j!4G#jqMR!&kQ?Fe|H-ep>Gadr)yKiyZXvYSEK5o<g
    zUYMJQmF_p5Yi*91hH72d__tN-;<qyMS9#O#bMw^(cNSo--~YG0tKD*3#kCU2x$8d0
    zy#FK4OquzCKDLBxl5*0O?zUF1uGq#HW7uqjB&u@-_&y+e_R-Fs{hN<5)(6<V-OK&E
    zW=nyggAO3dai*_2Z4Q7%0yc0gFKR#x{DB~UV}zXm==GWcGS1dypf#2s|EhQ_XMeWN
    zFfbsJPXYlQgR{%#&u0>Ix|BiH=d993#f5bQ0Qxdu66VKHE;yS{T=E%mhsZgktH;5h
    zfXG*wN$j8{?YON8yH?;v)_D^h8+*qI@XTWmIl76e%tv|aNE;LOL~B~Y?*2Og)%f4S
    z0Mq|za-!bpPQYe~=---`^g5P%b?W{fi<_*^7|^V`+5ZA3Z+&cd+Nc@(WMj-Nsx_F1
    z=!<UsANU+OJmp56{;_LT-*Sz5qpyCOR72x%d>`8;OY0N94@nC3YU`ct-C?m{sB0+A
    zOrR2VJ~7gZN<nn19jtW}SWP<7{l6sE3o;+-WB}6D&X4?iGLhIS50z2tKDfE%i>r(7
    z_wKL3qqJLhUH$(<dF&bRZg<w_&v}mpPq)MWBH!8y-_c8ci`dIdy(L3&Q<@PG-Kep~
    zdyb3eUA$M-S8+T6sk^MXu!g>pR?9c)F&x$f&L1lG&{qT$ajaMUsP2~S*KX7$NWVZ6
    zP5NPGim{(HjWnR5Sw!2+#E4nV;P&|<M|7L>-eZ8kNR%TZL&1Gb5x0)GJ~NMwo&X8q
    zs;BgGd3Q43x#uu#QQ`Q>a7(EOA{0&JI-CQ&iN)xE`%TX)cF?+DX!_RTJ$-Q(C~~TZ
    zXxbN9BI=rrM5Hf;UU>Q#ZQB@615Fl#RjFq*mkYL^@&HL*nHn~f$U#5bKv%Jyr*oMJ
    zr{la3yp#V0&L1jwU7W)eWH-wZK$g`GTNE@a42`s=Y}yoV;gf3=x**LhG9thfX%FNG
    za32x0#y~js!AUGL%*K|&F|o$48~SRE2R&F~QezZ$&=t5oa?WNd)l~LD;arZQpW7r!
    zlS&!`a%(q^+KlhmJGJh1&<M<(5Qu$X<uuht(=e8Mbn@W~pUKKY2hbzn)^u<}f2yu|
    z<IOEYn?t-=!E~Kn5{=l-5@AIa%++dTRK;MdVT&rPe*oa9caG;~w`>A63Vw^CLUR5u
    z1n0{`@U=2@&eECKZIPtqH@T)ZsN-68=eEdpL}dQ7qZdh_BqCo(1RXQUrcs@QZAg#P
    zg**fNQ9h+Mqfc}L0q=jvd<h&=oUW>lG>e<PMSw;p0JZ2~%SQlLpC^CKb~=C*>Y8!n
    zqWZj+s{j|2Tagzsrmr54M~NaD>E}S!Y@bl-d}t{KT1O|6agl{JD-bt)mbRhJZm~f+
    zo(ui%;DjQJkD~&{b+mU@AQw23cXs^2cSKP4dJeB2e!G%Do8u^RgLa^wwdcH~vc~^g
    zyr@y=V5AFf*zy5f<&_GIz-;$uf6;TS*;pF1SIOy$D!Al><lYq^Wv68`76n$@6%k3+
    z^Km7_(CY>*IN3rswSm?1s(bV4o-Hb>ovgWSKf$7tDk3lN#6A7=ZH8{nBg+w~Gs(DD
    zmG9m3oMYd-0n&~2O%#wCS%}ezIipFPF!G8eLw|8h;3CbjT4et|m+ZkezzM8G1hIy8
    zYv__Y`3@&GS~5}lk~9KO=!jymN~EIO=Jy(V-UbRXqTv?7RR_i;bGBdGd=2VklL6cY
    z6Ckx?fReVt(U8dqfYu}VMW!ue;()5NN<S9|>iBJfGsy(DT8aeLWtvzc_It4C3!Fbx
    z?#y6&3~CE9bq5;@=+Y@-I8he02Y3expsT5sWMq10Wz!=(Cy@^XgOLX~Hg7^Li?C**
    zz=-`R%6}|_aINx~)6T1)s++-%z&k_q+}69`2Ak;|5eci-&Y^W@|AnHMDk+t;(h28n
    zrY3ML?#bqK`k+0v2Yez2Xk)7KJ-Y)C%i`f+d0oiu0NtT(Wn<aV15un<zz*8c5bcnm
    zRKv*HAx6gn$s6^+VKSHMYu4w->*k`3c*hQq+Ny%{i#9*jcP3Et_;f@Uw;h<_9I6`v
    zQ0r_pF<)3<?bA19Q!ULk0WVFA4EzG;4;64ikycNgd{+|{-5rIU2#P90SL4w;(7g)v
    zyL*fc1vK0Vg_B3%Q#xm=)f8CvC^;=K+wMMpY9yj>(0U)17^2ST0@4tZp0Z(_6Mq1R
    zu2vhuvv~$wJpL-q`7tlS{YawDnJf&bf|PcCwR!H$mPS<%x;G$E7dvQ3OXMIU*rc9>
    zBw$9$nvF$v@|YT0u*m;>MpV3mO4sY%PmqJUNeits#MIFAdiU5F#=8ct^^R*AZdD_|
    zX~AVh1NFi=8kF?EkOtlxUO4KFHNgkyoG*E;+*3=OKNY)9+LgX?7(mt2hdhbM9Ddn>
    zv~x@M7PiV&dBSCFz%wJ>1MGY)*fV42D4!KoCS7%{K~w4P!Ck!*j?16Rw>tMI2u}-s
    z#WgPaahAcN6>0R{<i8MLYUGUUO`SfJeOsQu$i3d<qG2d|0u@xZm>e|YvJOmD2cQqY
    z+o*U3WowS~aIXldt}UGI3f|L6FH^nXN9%J{nz=G_H+va4|Lb4>n$BGwg5Q_>h;t~k
    z$?yeQdGipWK3m?bcNpu{T^thYHGQ9`Aet-+(I(+XZ%vpOJL4erIFxdcOl%DcMgtz8
    z>N?@r%tkqG)F7?0Ssft8Afb?>`w^mX0?LhuzC_ooVGdNoYz)fS^Pprs<fODq0lZTa
    zJ8Zo>|GxQD1zyeQlG))7(Wx}#4=R-Sj8P7)Hk~mC2sVPprs6TxmON_HEv5Qkj>GIy
    z)F7Qqr(yXvX#-O1+ot+gf5pxm?3#z}+TUOE4U;oXr&SEtq<<#W2dJ%>+4S#rE6WTC
    z(3yM8$J)CLGQ&9#3e^&M$t|n@r!1>qhr(oO%`8AdnowR<v1vKnYm#8u`jXF+yZvei
    zvfUVS+)q$CeanowV7-KKpUGKvVxXny%Ka0PtuPMRbHR|6gXen^IV{Atz_Nan)D8HA
    zXN7amC8|5>NomSlk==I026jLc5YsAbeJmzponpLZD{@bNA#b#-VVurT-DGkV>`eN?
    zGvc*UGhg0^aHiK}?UJk?pjRL~59g2H5-z?!Tq_s<lJ0u-gzpvXl6TA9Ox%b7K*hm?
    z$mxk4>#ZpchdQdrvi6t>%e3A$yM*9KrgO%&fgrDe7uwttwODEnU@`-)dbYVv&hPIO
    z^~Q<;sBp2xAhF=MvBnA+`8D>?pc+>L?Q|Lb_xIAeS@(=`V8~Pc^t5OC+ZT7)q!j@Y
    zc99m#0@iQWHGmJ%t>YlLiDx7P<!2&`cqA38;aU1dVR6leH2oN=<{&uMclP(jVKAsX
    zAXk(O(m{vZd_YpBoz)zw&0&^rqm0|qus0-o9#VN-7?c@d4~5M?obZ;T$PVOVmNo^c
    z+Q!rYFZm$3k57Za1IA*Y+wa18<q$?W^hXblDrTucWa^F)!EXGvO=7h>hE9cZ&#0Hq
    zCUIF-Y&qXMObBnb*ym3KD-FW3kxlQN*lod5qp6vot#;0;3S5QSGXT@y-{e~l%Z=E;
    zvN0^^X(=|XCyz*{Du6EFt^6$nMbbBeKvz>_Iws%co4VyPE+nAEsX816y?XkKw^@@y
    zhZYua_aablKD3Uu>58cist;GIL_6p)2C?NC+oKX3G$B0(=So$9*DmJ#=gQr6$*QuD
    zUSn)p?U}{AjzcRnz$KDn1!!b8%;vZ<bhQHlhxLPRJU?+)8x$hrQ6|eWutdPs!V!g^
    z2ThO}cQ$c5U)*bMqv(9nq|tw|o6>CtOcnUHoxdPOKe|<nC)CAp=gb<<{0s$4U6;Da
    z0B6b?V9v&7H)pj7L=D1<B2p?7*DtHAdVn=5gYX^FtALuzyz_`^=G@_4NsG0}@CXB3
    zIe{$@OQrc45VH;=vRgQGneeG%d{lBL6!82xl8O1EfK^O*FqFBab94Haf%D}d_*%IS
    z{V{nDRjfs8iY6e5t06`w+?!9e49J{21nN--7G21jybNjcK_Y<mv7l2)KAz$f<g~gY
    z3HyQ{5jNYA-j>*8@k-lxlk)k_DL&&6Hx(%949t-^9d)$o+FB-^b=JKKbL^b~K;7Jp
    zXfSHxPJLs#$-v89>q73>x?_&6$Zg0>VVDXa=Crk9k*M~-UnO&nxGu+In9V8=H>q=T
    zQ2oXskP`uO)@uQXdH4MIi!Yl2dPndZEt>yYt@Z1``I6VlJ?Ns)N0x}j^czNbW3<j}
    zSy*vb20?1^Fz!>fN#+3vuGzU=06TM$G9*^w9dBQv+9d0v(*k05z2XMvl(|~GjHH7$
    zdu1Z(L+dJC(5*nc?(pwxWaSNZ*z>xpXKoBp>40r^^1-c%T1%J6oT+M?AOZSLI%5h0
    zrF{=r0s!z0O-;YEjqP!ERJmnqexXK8AC->t^&%}))8s~PyRq5nVdLDJzj1hxJzy>g
    zaWP=;Fz2OY<~);u3!Fbx+*-rufN@?&1TB%Eu?(i`>lrp-g)H5wW2iIKt4{7EqPXYa
    zLJTl4JpxgsbM6P%sz;3Y5VHYP;Tm3eC=#JLUBO(RWWcKwtLv*bcFjMY>@Xb{2a!$G
    zQv%#(sX-*{akE9krhpEnF3Lu4WNPT4<CQd_`N3Z+63l=CzyfxKi5Svd$RoTa!y+EJ
    zk(t>zlYc)`HN&-yV>dwy4IRjjKfnX+ej0ra5>zU%OXvLC%AK0JZ;vKr@p;rGo4`(z
    zF)b1CJr3xiYSj%z27uiRR0p3_<{&`JBesWbS+%K(M4suoPk)SW&&lz^ptNDb@79}y
    z`s^W}uigzPh`d968RLsxr`dDCWRBSuz%=uXnRNT6k;Y3MdHER3M#@6<k>W@P>3auS
    z4&dun89n_~J8T(v1Q1CU03GO}oi%#L5#gkt@T=I7hFsBDBwb@=Xf^as!%+kD_0$dY
    z{ej@Zjf;9|7|kAl?bbrMJ!Uh$o$vaB_ukS8)pf@-$1|ZwUb2lbQ*RqZ-9=n9oOugV
    zG~p-)d+3zC$M5H9hCgJ=;F1rLyE0nr<OYt|@Pq}2v~m7OMtz7pO>1-dFxVfo4O4SG
    zF4^8WoG(4GV&_8momqPEJo$f*<<;^!)y7#<x^Q=wNmH*lT~;@FdLn1Wxf%H4ArI9=
    zn%N^0cUx+u&0zbC>IZyxP*+SO6ZIq<)5kO(A~Y~#+F`~xW-O|@Ry(0;V-2u#rpSzs
    zpZ|co44ls_{#_n|v)n!E)y>$e2PWdsS_jMR6rE5+CoBf67+0g48B(Ixliq|Jh@S-H
    zH4q-(OQNpOA*T_AeInE}4o&Drmq#5ayEe#nL(xrMktyR}g8BW=O90g{#&*?7nQCLO
    zYZ`#3x>f?6aUgdBXgvaLpFmY=p8?G6cGvUkJ$TLEVQy`E=lpm!)x={Pe7ftE)x}&}
    z&!O1mE&Zi9x_wuw!MPa74$^!`3%n%Dod85MY$sErt!c!FfjYfmFeB|qID$8GLNRa;
    z%v<iurh4j(A@_#A<AUS`S;21u1;WmY$6y)3p2pfJjjk_B57CQ`$ucr%G@%>dtB}HQ
    z=h$ZIMh(pJ(0w}aF!^`PoXT$Y1asQGY`PhZ@W+wWBDgY*-Hr-y#lS}zui0#%q2yXm
    zO`jw8Z!9K}TO1XPGc+11jqZgG%sJH148*20hq7S)_Ph>(kCFsaxjc9+u?<xaz?iED
    zeE8ccpvu!vt~JUzpIj3cIDe|#RSCzU9Avr;AXd;}Wg5ux)YVIWtIoLDU$bL2otLcF
    z3wU$SQ+w3O;e_)0EIJM*bsBw+y;|>KYD)^#s`KP%6YVYgW*|4r@^sJx9WUhhDtX$J
    zit0|!x(Jx6A3yUj(5da9o9K+yfhDXbYt*?;bN*dgT9a9m4bEDwQP}uK2;ew_Nq<de
    z?d`c@)p`LfcA)h3z5$?#!XULL{tQ%QBo)!qs;PNE+Pn9GS>*-JA1Zg7N=hf_O{bOO
    zFk&F8^Hq|x<32ZnSCz)igW-UVF67B<%t{x4#)0a!VK;Am{$3MG6m6&%VuPG)n{B~w
    zcHb6y9kSKMDRtW(;LB1gIQYm0t;sQ_24)=;vMx7vu#!1H(>du_MyaK|5(d;5G?7tU
    z-`F+UL64?0kS(xcDS|q-5V@{4yG)y9Nb!6HVEuxAPJnNl>mrb1n@0I}$23MhfE8<T
    zaM;#a<m3&wQo(hixZtO4J*frZE#lwm5Dr57FYe97o6}t2{GlS|7EEf4udG)$d$n+7
    z>b@=N3G@s!5Vqdufr~mdAqty?33WiXxi$RGI_cxaztlOa>BuNxBrI}2n$BrS+;>12
    zaB@CT@b#hVCbh_xFHT%Ncf4!eGuAFR=R|Bm#vm!?Z>)-SvQwm*dHp&zIH_7-i{Nfi
    z=VajT8;37z)yoM|b4-np#*h6setTf(cAX%1{f3Z6n01JYA(GV`+Af0i=h4C)5mB!g
    zv!9z<ZpP%*=o%|(%490t8^QVV5PYq;oioig$@W{5L`5KBf==U}jZ`<?14F8Wxk2lk
    z+la5)A4}y0xNEVob3Onx<Y!>5X{<ihcGDay53j)Q$JI9P5DZ*YeS=oMC$fD7Q`JY4
    zhwelbt5hWZox%)vt$Paj=wl)%!{g})z|$<M0Mp9(#H~2CV9ti2OXbXzUoG`_o}95N
    z_B$OOFyrUtV$L5bcZcUrwqKrYA4SA>=We!xG7$jv=t0H_H4<GHtz;A1hvs<;+6j$P
    zhE4Eyfc6)G9Vs+IR1$312?G)L8*n?iZ139XCR2Lk@6NEhaZ7nED9B)eEm(5M+`#H)
    zHt>oz%^qMX+oKXuZ`3d_yBneVsj6!Qkil69T+|!42oBX)7-Ckidrb~&H;%TrvcNY~
    z$bG~c>3vN9H8e{bGm+MbZd;4bmExF=A7=l}s}(e10ND0z;e8qxw!-knU41#p+JKgS
    zRFA7NZ~(A{&&Aw^jr8Em0Ii*nO~XsfC52N+?{UF-2KLdmv*}*Q;{xXomE<8vaJbJf
    ze0!+}ziVb22lqXkXK`O`$9$YFo6Zr{%v;y79KZoim(0C-r`OmzFQbwz)j~CcedyUt
    zA(niTdT?jbF#tQ5W4%Wef{|Ce7hVZ}#@dYEJe``#o3EMrsY4pVc+HY!W3zly03QIo
    zYOgrUz@O=b-y~yE^bp`LaQ;xaPajTV16T;nbRAV5xdE4rpj8bMGt3KqY09=-a4A?}
    zD$Wejk!9a#fQqUt2DyvDf2yELB*oJ@Q7aCH_XxXU6t>Pgv+uoEn$Inma~MquQJ^96
    zdJ4p>N7gKsZFa5#RffE70QLsR7$*Ziw?!Jn<5@o+n;9Z#G@H}J@p#-1j)z*f5`6Q_
    zhq<ouT?u2SJ`8Q0cNuMwL2<5C=W!;~R|USn`9mdwvv#zGO%lF7oFA48B-R~K0{acB
    z1`1$3)BuU?PWe2}ERNVR7RDDaeaf$$4jocPDt~_jUrPgEq*fdljNhMKIiW!Ovr^Z{
    z#c@otsvTA|n(~-4i1m_63xF2RG5I<%xwD3T=1l<CtRXfBi$=5)^0GctGiMIJGB~RS
    zZOX$KvNL^b-=-H=n|&RG;bsUOvMa$7#nc!2m)04qq^!SKC-qGA_00Lq;@{;VILlCL
    z039$>H%(FWh#opXF|9-i#Bm(phN|X$2KZ?pp=CP|rk2zh6;m#*Wd;ST5R4Vz1!e~o
    zCNfhz1qBV%ue$JC>4bu_g;drjXSvfJh5Dj<8Ky%t1<MXPM6ii~U`uq_mN5u`nG)L!
    zv68#jb1rfv!&DBB<!^Y044y-eX8-^Pmq|oHRL%*|WfYyXBwf?GS!dAtGw=$@)ZE*l
    zPw*0*@yC|_(){;X<{WfVRnj!T&zSr&aK7ZVau310IWoc_Ff9VmcD=KMPiV|2&|`E<
    zCr!^uZSftQvx9lXjOVlw%x@A*-o}YAE%)+7@Ff9h=HUaVY7sLRn}Du%-^Tpqfmm9K
    zeW&7%$Jd|1`l{aS>jK$>HMlWAg*4rOjEHDcCnsUlVY}I_(+ozrQ1j1tM;w?dQ##Zq
    z&rxx^mz7*Uj)S&2-7r8a>tM7}yWxnEfq4V9Ty6*Xe#{gOa6Ph`eO*o{zpdOSN|AMR
    z)a;^t=Aic2k4>QYRiFRtZ0->`0%?`O?0Bdvkl8WIPTP?=$6Zq5vzX=qLY^`|_*YcG
    z1Cn*Tcy8JR*NnOWyseHEf7pQ7?kn`sn%18^<8(e(gEP<9@t&gj<#QM|)TB`;qv0^z
    zJFU4?62QBPKDWo9Y3Dc{gt57<p^G{Ho^lUIdGW7tU=Sn8GSo}VFAehO9ayH%GP!x@
    zd+_HI5$UGI!&zS){rXZoXE{c_qgsCG>|>;>)Ef1G^&MH+eC&=(xE7#4i+a*LG+Ee@
    zyJNe6JmFs3(y2R;DGSZ&s9fHuIV>99a&fiDlN0z3_AJ><X{+OBJ|r+^{r#jGYd6cC
    zbGzBkW7;JjCihKR;-2YT2SYPmpbKOmSYuSJ*MlS&G$AA52zV&NUG}*3_H++ZY;Bt{
    zSqd<zzyDpilR;)%1@=(h9B;V_oP(vA^ST%5R64~{<9YpAT?gBA-pOA<HN8LvbLP~+
    z5s;X0uhJ54kj-k|rW)%^H(eLoW9>!r3h|nY`bJQ_@2E|**#A_81vwi0`p!N3QmD4t
    ze|FO9z?cm^bC%T$V4KK+Pj?>l0=YZ{Un}=LP*&Mv%%xtrJkGHLa@wytzh5Rx?s<5X
    z7dpKU(O&Edg623*>{(cIUUv#kBl$YdAn)fhTaM(Q-0dVjDx%4wY<lnd@#UpFrPcU`
    zEc?Z>-z?wo!&~UzEB9z~JA>$~ugki*1xO#dXYJ$(`OGDVa&}gD4w@-xo~s0pt7U#f
    zcxJ1WvBz$jFa1vGx*S{dI;+SG!tkh`h{1!d`)8!wX$GX4mxSl92Jck?7Uh4lj|$Px
    zx4=twWM<Ck_y{-;&+RDybN+No^WZc$UPpd8zPxDNXPMOs99DzN>iPpESX^I7V8jtP
    ziDq5!%y1#AKGz4^`8wUIcB&)ZO;O}<_WLfcpRE6r?dM|7f3Dn#6SK>Uf^@9Hm={gv
    z`}eMmdG11|c2drQXIpaTV`iLn;-4lNG{-qVnN80oW{mfzx*G8@+Jqk=T_`{*TPkaa
    zt+A6P%UNjJ7N5T@V{4py{%P=C&n!N>f&RtfPADkYIc@Rhm>C}d_)<T8c<vc)HOnJA
    zZXf6W-nM(1IiJ?mS1cLN#lS>+%(+rYOs>>Lx@Y8D%=ueSg8P@u`z&)_T>p*K8fO;&
    zE)T(3?uXVEL=uA;lT2FZk#6TYI;rG)NbfuZ>yv7Y)%}YVyH)Nho%Kqm`EJ^>=KTPu
    zg%eL+GRV4P1Iz^M6I<S~#z64jyJr$?vP-qr@nv2aDxcH|uNMU0t+u#`@$V}45sW_R
    zd5NG=WK+77X3;wHq_5IRPVx&aH<sYKxV~1KoPENtlgXA_Oedox`*3Q9)*kLXnC!@<
    z-qJp>Q(o~|@o)ZJ$?E#5_E!sne}G)*{h=c9oGXBh%w|ux9bcbfljo=0j_iET%gWU4
    zEVErN_^qhZKa1-tIXkmBPCwZ_FRl|sP~GB+<0Q9ye;)rAcE~GPG23IibfTpx<;fMt
    zJ_W3O?)mFVSq?9+iCufi%ViE3ddVeE$m#W_H|tc-+c|&un!_uKaLGr?dub~X{q(@R
    zRy!Qa`31%01J33D=E3#dN|$##i7e$E?$gga{?!wo=jCNjxI6@3D|adv>|B-+5trbw
    zGBtW{>sx{AdgwT}B)9~(51+ZLinm%%TMlj~2hWu(2j7#U=h^mq-j4aSe0Br;i)E?S
    zINvr_@b~7nv0P4n@8f@5q4#c6tg~v3x8D7V&--rdy1@C8TJCEniep(Z=T+dApKci|
    zQ>XRJ;9UKkm9uJ>r7S9FU4O^dS57)-vFXup%Lge=U0%w055G(PP<dcM@TAW5ho|j7
    z{rq>!dRlxg|MP?Md)?=k^7fg-n=APJ@@IFD-&nYBtKM_=5uann`yTmO=KRxgwt2jL
    zJ?z~N_{~2sH-1?Mtp?%~9)f>?iTSbQrdc($<j(gIBkiY@<C07MYI!7b;3eM^G^Oi1
    zd+uc*{ApmkTEiumT=K_@f^(NZH52Ie-a7)cr28p=)^h&9qc6GSl1qM_jNR>bfSHJh
    znOPXMG1h$ii=<@smt1nmC4ZuP%M7;y>^IBd0CLGCmt69v$+vwZejz|#z<kLim;80|
    zZEzM{G|?|`p^`9^mrmo7OD_3q<XhnU3xQeX0_aOFx#X{sMhoDpfcO^!Gd%XvY+Q25
    zC4Ywm`{f-wXG~|iQN6cVhw=$!xa5*c=yF1Nr(7O_mt1nmx8(xo_sRbNsvR(Myl37o
    P00000NkvXXu0mjf;)}ZL
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_bot.gif b/mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_bot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..e70f1966e1bb5536fb9720d348fd8f0771e2fa81
    GIT binary patch
    literal 431
    zcmV;g0Z{%&Nk%w1VUqwA0J8u9t*x#1_xHuc#opfDwY9a(%*^KI=DoeW@9*!{*4F?3
    z|NsC0000000000000000A^8LW2LKBIEC2ui0FwX}000F4a1DTzy*TU5yZ>M)j^t$k
    z!y*Yq>Ar9*%Yu+DMgb&q@Bcth0#Gohasq&$5^Or3B1Dp9R1E}&17OSTdcOxCBBA}7
    z&*-#z%?2NAs$)VxAhf*)!fag5+V6mYf^BsI2~}|h0|hmWj*pK72TUb=TYdm_Z;+gw
    zo}ZwhqK^dwh;c`X1FNj9u1N!xWS4oFk}L%YuDiUwzQ4f1!Um_P#>dD8l9V3-vjCWy
    zxVpp$$k*7}+S}aS-q=G40nJymTG9o%rUusE?(gvN@CaE39RUM<S+<+%)$#xX3Jf@;
    zg$8_dpzV{!f*QMl5F<*&MsE@ZgmxsIyVY=AyNMuNLqbeIpuo5QIWqcT=<lJ(moVee
    zBd{u=#yT21{)@@8XSX4BGOf(1&eTVrNRt{1kc2>uJVbRFRZ6w03Iw?lFzP4OrPZ%g
    Z6=}^n6|C8+s@9!d`}HW>w;>|{06T`;$Y1~f
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_top.gif b/mapbox-gl/closure-library/closure/goog/images/left_anchor_bubble_top.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..c9ea2b1fcd76b35b26a4884a922673333e96fe18
    GIT binary patch
    literal 332
    zcmV-S0ki%`Nk%w1VUqw70J8u9t*x#9|Nq6s#pdSbwY9a@*4FRu@4daf%*@Q)-QD;1
    z_y7O@000000000000000A^8LW2LKBIEC2ui0FwX`000E}aLFkRN`~sqyZ>M)j$}C+
    z0ze3u>Ar9*&%=PI1aKgOGXKD!5Hv^>5C=j{klXc!(5RF&I0~2uML=O|@_f^<I9f#}
    z35I}rn_%jA<G37VNFbO80AkHow3`J3bAyB)RRRZR1AL5rc{+iGl7w|g1bGEojE!vo
    zkb;w;VTYHOX`84D1&;>>8KSU3l}ULCsJChctDXX`vA;2-w5PbYx{tlDz{?@CrNz*>
    zy8y||g(#lc+S}YZ(cjXK-Q(ot=I7|<0?HH#dc@%I^7Hid_Rtmp2^t0jw)X%73LJQ^
    e004FZ_7Ggiupz@M6b?F^NU`EK0{|pM00288k(R&!
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/menu-arrows.gif b/mapbox-gl/closure-library/closure/goog/images/menu-arrows.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..751d66f7a82d186a5c5c345ad3ae294b21947dec
    GIT binary patch
    literal 113
    zcmZ?wbhEHb<Yv%en8?Hc1pmPR$WZ*r!pOkD1e5>*kURsE)0BSBm8aPr2?uV<609qI
    zX5E@3)Zu73S7Y%ykJnOf7ccv0{cP9akG=Ib{@5M#<6q&JRpJx$B4sycno5?a*WonZ
    IT}%wt06mi_=Kufz
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/minus.png b/mapbox-gl/closure-library/closure/goog/images/minus.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..6c10c0df1adfc25e18ae468cf06617b278485d14
    GIT binary patch
    literal 238
    zcmeAS@N?(olHy`uVBq!ia0vp^!ayv*!VDzYUPT51DfSXiUsv`kT=D`6O3}r+$ALne
    z1s;*b3=DDyL74GyW=JqlkR{#G*O7r?V?XzwL{=bQCBP@d_5c6>TaR6<p0oAbxxL5s
    zu06JQ>GON{?p-_g{{8ds-@gM@0TtX~nKv28Vk`;r3ubV5b|VeQaqx6;45_%4oY26i
    z$EGyH$;-=&NnqKxcXxM-J~%TsVA-=XGjk1|tqMK8z^AvbuS6qw?VdXlr$Wkjc>@m^
    ci0_wXu*~9j3RGPX3p9(t)78&qol`;+0NaIEV*mgE
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_bot.gif b/mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_bot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..c10b36b94db23995023f234877990ddf1fc0ec39
    GIT binary patch
    literal 228
    zcmZ?wbhEHboXo(<u$h5j!-fq%fBrmj<jBK^4>xb#{QUXzGiT21+qduk|NqynUHka)
    zBap#>1QdU=FtRgnGw6WCKxQzoOf2ACFk!F7>$AK6CusCN31v8}#Od~OUxCi)rU_nb
    zR_|-xH*+>T3^U+hXmWaRJjcqp@epIC#tJdcgNa-(s`fwFP~oFD`}~V7R(nNN)^$5g
    zbBMV8{>Pp=|MKXEMQ*0eEv;?s9i3g>J-vPX{cN1|1zN1kGiJ`3J!kH``3n{<TD)ZG
    Z(kW9{u3Eij?Yi|FHg4LyWu+j4H2^nnZ5jXo
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_top.gif b/mapbox-gl/closure-library/closure/goog/images/no_anchor_bubble_top.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..8fc55d12001adb30469d191063b308e9dff63433
    GIT binary patch
    literal 123
    zcmZ?wbhEHboXo(=u!w<S_3G9C|Nq~*b?cKSPtKe<bLY;T_wU~WMHGLsFtRhSGw3h?
    z0Z1(avqMFd+XsVFo~!p-ygqAb=pDfjpD-_D)w-P5`wDc<7b=S=D)W@Q|9@bCfT=)(
    b7|Wr8lQ~wYCj}0CSQEA0k=bD)1A{dH+F&+J
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/offlineicons.png b/mapbox-gl/closure-library/closure/goog/images/offlineicons.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..7cd51c1342bb28e6713132d7a378823ef1823427
    GIT binary patch
    literal 5643
    zcmV+m7WC<fP)<h;3K|Lk000e1NJLTq000XB00Alp1^@s6!z-!*00004XF*Lt007q5
    z)K6G40000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU{ok>JNRCwC#
    zS_ycS)tP?(<?c&zliX~OkPUK^2*I#Z+^Dh(!i-X<fVQ+^Z67T@(}h~>6xV7yeP(P$
    z+d;Ht#$_x>TjkLzqHJYJ01H_NF$sa(?90vFw|~BK?oNVW=b1is+CTiyz4!m$^PO|P
    z_k7>`o&QHr6b0;9b!f$X^ld$Tt)Nex0C?zo1AT7WId}K73?p*j;T2m6@KZW#7?53K
    zpX~RI*`NP8yU(r(D-SNeFA7xSl4E}M=kblwZ{W7~ZiTFuzb>+_8PKY+jtLY65$=93
    z6ankmFe=#1$FF$;S#s7W;M=WlM_MbWkYi(HmRW<hy5GXOqIGfLPmcW<D!q!&L5$8C
    zy}n*#>$7op@!csvNhd**sR6@@f`A4F{@&?L=yCV(9xny*gK$#C`f{1R-r@?mrNe!P
    z;dT1(^SYm<0LiMK&L;#$3iq>uFEg(wAqD=C05s|91W>>WJLl~Fnvl#O-C0jR6ATJb
    z2IoW=lw*Dk10T_pKcKHtl=8Fex5BPR4{rIkLo3$M_lE=sHl3t{EUMrS```_E8K#lW
    z$KIH`dxx;%;PQF&<CoW_*Vu*vLs*b=3#!ajxajG`Z|%F$=xC%46xQIYjKNhV*<_oa
    zJs)W>W7qjz*mC?41bhK3x30i!%N%Ci#&F_9<HRLDtlEO>vaZ8j#dncA)aXGses%sO
    z=p{X7m}em54e?bVhm~+ip{@|VkPpFN2q9+}m6?_Jm&rebR?>p*<E`^=;hfT3A!V=4
    z{)cRJc5X*+2cA3m9NJuM7!(K5G<+6wv*u#s@4t!K=31!J)%B8XefcDJ*ga>Kbr#CA
    z%VDyZkgiRGB#AiMdkn`qkHJ)Af~=MQi-BD|zMi?=uCA;CO(CXbP6JICA3HzBnV~ZP
    zF)r(5=8dOP{7e(E1>He}gJFnji9ix0wbV#VM*JM3n;I~GuVS+%aw;rpOS4wg9>~?@
    zz4>s(!$(r+PCkeP4EP6v)PtvEsAE{D6#8Ry3!6`GzTXpaKR|9O?(%i<#UD~a@Ps@m
    zbW0}}oR~*T@bNpwuP(i(^d1a)hEQZE#th30B1H#x$el%vDqrb%<rJrTaqg0`Fj+d$
    za{?_F&SCetH(+;mVoCN=tjt{rC9Fi~W)qs4J{1Mp0{r{we?zyw8-gt0oq>1YSNyof
    zJQ+b(kkidmfh@=fhZWdI>=@7wK(En5EvR|Ek_9QYHYymn;heJ>&VUmuOIJc9=5!hz
    zzE%EhKFNk5TA~s6HZa%i{jL3X)!AVyre<7=MXE(`i7w2@n1KTq58#cqHy~+P@L(?k
    z@BG-=viMxE+1BW8gf>hbP+WM*{UZdN0jR@jXbsw?Fv2@i=w^$YX3kS6J3_t?8^T#7
    zpgj53l0DnUq&u4bSn}D+dL}{+#Pjo|N2R$^80&h<Fv#Dwo2)nCdg~3?RQFvpdK**e
    zu3vQKdRh}t&B>f&yR-10;0btP@|!SGnTXDCCwhauEDuNM_J{qqsYI1+LCylSI9l-7
    z;YX=~2fsf5EBJ@}u!`11bh8Pu#FDAb!s|`{4MnBknVx5Gj6zDEp~s^9MLZbdbO)6n
    z+T5*hd0i;WE~7~ILSxmyqO)L%r5Y@H__AOwqBK=~pzi=mGs|#y`Q3cxTZ7NyCQ3X_
    zj;6@kFh}X8HBe2Vgr&txP+}}WAQZp??E(z=2Jl+rtEqI;x%kMy5!;`7{)8>nS;$f6
    zprgM7ts|}I^YuezR5dZ&D7q);CLk~v#OapPFpM*hRq-sPf=6P}oz42#(y(P#m&d-;
    z8}`og`}~tA2{apYhW!*$uWq05;`=<h1tAi-%idc$OcCOt-ys@43-I8ebI_v%l@MKH
    zMirUP5F190Cg%sY=HFU&P1<Cz$T>UGG-cPtT~jDIu8<B&50kw}bkmoo3-kq(W?E)J
    zMSmg6rhz~JnMx)CLcr4IXsdd)^VO5g8>^_bZFa_NoOiY3mBv@#^?Py8#0|JU;|8>L
    zv@!<EMYXs}=1H2m9S%CMv-vk*Z02R><EZ~A+B(`$*Iox0kf1vd4q(9Bk92i9ENK?>
    z2l{Yw-~>YI5KNgSNLnco-MQL4d~?!9e79;7G=hfGLKw5N=b${Z96>o0Tl*2Z>8x&a
    zG~o2dr*VgVHPVaIVb+>(sQVDQ9bJ$N5xSY>oxyORWoz?R48wuaaiz%D<)d@hjuZA1
    zTtWp=XpGbSg(Iu)a(msI=t4P*=#Ym;HD5qf?lg%i@zIoIzT(5>*Aas&uPV9;sNf36
    zpQqh5q}_vY?Y@Sz`{L!bn}v`mWQuC{mdk7RmZ)}rsqAh}$|q{~cwGVfK|fU}0`wj|
    zrl-%qcdEVvm7qc(7{K0+z37n6S90yn6f<&9Hk`!sr+$HE*Ew#vk&-xNDcryNe$<{j
    zj*ubLz_r_>c<0<yJRg<$l`v<Rk*-UJBul96KaRTYI+%)0Op^bTYj<ywcGqOo!2DOB
    z-4EAqzTXvyXm|IOw3|SWuO9yeuHD1FVH6pQxUa|5?xlIl%2=S*cGsf0qZw~BUqQPk
    zXbbVP`e)D+?13O%jdnZi=-2c^uQhxM?N)0^yT8T#a+TUWlWX_x)+=bYi~gSWJq`b`
    zAF9u|c2}oOr8@dkYImWb5TDDMhx);K^j%TAX#qX*;TAaT4rB^hqqW<j&WLL_DHo1Y
    zNLg|%STb($Xzf1lZjWfUHKE;^#tc+nPP<DirC2v{9TnOVo(pf~+TD0L?Oss00EM*r
    zDDJSPU@b{;-zRH#PE|I>8ONiow++V~$Nqn7cb+;QKE-co8*b&=EnceK4;}d)9z3`a
    zZYtN76)uD56S#IW+?d*(PujiDu@5cvEjZp@8{KRZE~ef6<WGy%f}kphKGN>){%+*t
    z<iM0+gi{$wM0Xx(_k)uj#OA3Fr)YO+S}6ijaI|(ekaqVD_2BD8-$01O-Ka8LPP>CG
    zThBg=Zf`fQ5_8qLe_`#8gB=csa_rbKw6?Z#LNpo;3JVJ{Yt}57OeP@?X22agcJQug
    z8Ki_iYqW6TLX00jUJyn`MwFLcdWnN%S;m?*YoOQbLD#W=|9;*RYp{CtYKTXUaBbnh
    zP$-1gUV9B5kB4=QB}=I4Eh&ND?*|*A$XYV1YuB!YR;z`}<-*G^zYMqA&5g{PH;)gG
    z;Y8L-6e%a`bhz`*J7KX{`0qF0d=p-;m-oPi#jc5JwHnb$LZwn+`SRtwr>?FpY}>XC
    zPN$Ri#)c@eF){GLg9mvEFdB`x{r20zjIc(pzy3P^WH@p9^y&Nt8`GXWd$<=^x9ire
    z<DYEoY=|>w&W!lEv$IoSKsH8ZB}Nn*Co{Bo@nYoX=L>NLnZJ)7J<5CV`Fz|n#l^Jo
    zoiQVoL3Y&E){e^j)C*r5H8nMfwL!pmk{Ki6^JG44+O!=avq%<FfQ!FY8j*#_I$_rz
    zVmL9{wT<0iCkIk*i|;LsPn51Fk~zD`&PDf@M3qM1Pn%*rL>tYIKZ0YHqIaSv$MXi!
    z;Y5--3)nr+weX8E>+JShTU+Ao8ID=r8(Z*7+^WJqE(wH4bQ7rY5_#nRViSzIZ|5jK
    zCtHhx<9<$IkbtY`_o(P5;DMA<EoSl3fE}gXb#--@rTh2$%=TqBI=)ToV~0q<d6xtJ
    z;gAu}zM7dwD{?c##p&)DkVgT3`fDp?RA69WU{aiJcJrT~&O=wPFbX(#N>}_LtQ@18
    zS$>cj+&15d7k6jIfoqpG@wkj9bM`GeBZz@v4PMxl3N)Jx$jr=4(9OR6_*-^lr1^M%
    zV*nF$GnSa;f29VC=3W>Jn4mk^&n%g+gu^mZa#%4x$LWsxd(2WT7O)~d#gQ@$`fBs|
    zM9TaEH0m(48YOiY(R}gC<7ad>iz$RX2TVA9PK)u`0j#{mfvVE@&MmSLbGSs?9}XMQ
    z)fXaC2Jp<LHu$N&d-?Zze0W@!DAk(UG)p%9G!GvfGvcM+TH$nu_%hva$1przk-%!M
    z1e|V3R7%?IR5+-JHWK;4!4aJHW#ETf$AgVu6cyau*oWEI65VBm{`woIJ5Vv96Kj{$
    zqbT2v%2E$DedPi)st`7;I6yn5)69Ak*X{+Mb60QQnU42sjqp*0kU12hjTo?TtsTX=
    zA>`&(GR1nA(>-O9xABY1`XPw{?AmLf@w0tw2-B)Ouv)DQIGS!s<~Ek;nZmLp&N4rJ
    zHqwOuuj#f<9}s@D(SE;PAAW!&q&P1#fUqLa_^C9~hDX!g-6wC~_M7yw59>@=zp5KD
    zNnHPs3|om0wqhT$ETIJ5zuRXn6RBdUDj9}Ow&SMh7b$8y*!ivjuk10gnkkXYYwLAr
    zyC~zHf9ycMHB25*Vbv{EaMSkcqb3bk*+gX?68LKYi^+v4m1f$5tEmzY_;5mDk?UhT
    zx+fL+Xr~s!+lO;$El8B~)p+a|R{9=BS%EXY5#!Ol^5$XOGG`DMdkpx^J35?b(BjCS
    z3`C9xi$2%Q1vFlnmlgOI7v%+QQ*8kZ4hNA&g1YgGUJMO8z{(Dd*3dL&bhBzP#wwF#
    z`h7MS3_dx3{`~F#d%90IYlSODcU`^C_TC?j*n8B7#~x@$O+_l*(xSyHuBSEe)S;7l
    z+m7F5P<RE<e!+te{^&({nMfold?RMJ+fRvVm2z7kDA@MwH)8^Y)IF;jq0<CujSOP2
    ze-P~#mDG)xo1(aHP@<%G5qg~(k3Sf|^lL_7Fj(-;;dGEUlJ?z_g5vRhqSr??bOvRj
    zFmkf9;SK8X(OG@0#*1jTPODVkFx?G%uNkkum&2v_-aCSLml98Tv6tr!v)LS>n{o#y
    zVqa}8S~@gn?^NNJuLfW-2eEK=2d_m_Hez$8x@{%n1}TLVP!AH)4MUh_3nMcvK$>D2
    zo9>nl8NIy&FsRyD8O);ltifP-<g(FC^=SpG6sY_6lIdm}F^TQ3l4)f*1Mz+R7<6~_
    z$v@cjURv2lO<J(Xv6Xmn$HHN_ePV0*1mDA)ZmMeeMvOc#>5r#$@cNi%*fI^E?LrU+
    zt!T-&x~h76dm|4~y?)Vl_>=(!xqf_QRXZdhh;2J_@b&>cO2#`_Ih@#t(b|C77{V>H
    zE)t6(b#2A1pO-294q{wZH}#U1pqmyG`7?+SmkdQPBReyU$rGJ0r)gm@WyE4I65X9W
    zGM?UU#S{NA4sH+ln2((?qodmlgHAPiBUVYOq``SS`MicK=F;w7h1t~(q#0bKF~)>;
    zt0bl6zSTX*vC6c=%*BOn6;C)txxM6bnHAlMWPa%IiMu4x&o^Q$`><8UuCuXHLT{pt
    z!=ov5Q&oN)ZERLvRdf?j`3q=wGii5DM7t|X*bd_o?S7YQcaUrM#<d?Ow7Yf;?cPV)
    z{o-z>-C>j#N^$MpKT5khWvJNWZenh5(19~P3;tzm0ckh$KoB?2R4}_HuH8ehjXRBX
    zOHOd@t{^ryeU-FZt#Iwuif0qry>Ond`h}gPc)u<PpAyw>DXQH$B58L;Lc1rEcCT4B
    zz_ojKvUXQ~cD4IS(r$w;s@+*v((Zny-IlTssgn8fsvf8ad3o)A-&`iqqN^$yf?n>z
    z{285}tlh1Y<2KxW5&796k_!3C+MRB6VoHVS&(ZE|TAIpLXm_KIv|Ep$--Cs7uR^;Y
    zT<6Eo;1K1$@Ml`PPmp%M8`bW|?{CMom7k#9zb5TgtAgn0(qMm`5t9m?MA9c{_g7an
    zKubB}?xi2m2Fy;{9U86O{lf|Ee(Zs6Os{g1cIS|GXN=bFqWqY4TO!(>L)slO(%QI;
    zc6aug@cO$}{{KDhy(1LS?vl%C_wh{9Zi%#8O#+mDCGGa1yFZ^cks@jL0BXtue|_4$
    zV3=)eS}8cLtljOM5~TyhGVY4ny{m}W%ff`bwole>7t`*W{KV>c6j<%liz%`1X4-8b
    ze>&Y7q?y&oA??mechTxI!enOJo%8~<vscDXw&&o9Z3QXX-QH!OF-w<dw}R@55)X9h
    z5&URdI!s0tTpnp`?UtE#e}S|+BS0VxE_6wMVeJ-@p6Dnp(z1q+?If5XDvMQ^I3XKG
    zy*Bn(DGpW+o;b(fFo|Mn7SK|oX_XTqk89Xd_WiZZ9Hf#Z%$!^t2lfv+QQu-G_o*l*
    ziy*SD*};IpP#CA0I^y8WbR(+D^5OFbNt*o-*%+BsW>K$I!{PMO4(NQmK}MQ^53io#
    zSSvD!-hnV}`Nvb(Yx&OpV4WR(0}4M!ToStJny4YC%aTAMBhdN?<NE3O)JUcw4WqHG
    znO`#*0z{Yqv8ptm6ENs-^M|PHoLpW&dcq!}ia33)ljqOMvT+b~G)`unjeTI)6(8&D
    z>JsuXiI3aIKN&9W=i2&qT5*0pSvDbdu>Tsx3?=({6oVdbfako*l00rP#$f6*EHMbd
    z&<9ydJ*adDfj#X}r$#rkdg$CiglN%ALaG>ORa(?cn3hC$^L`FeNh-`Jo|&NA*^hdA
    zJ@nMew4&)mw=d!W1`LLS_^9Jlf^K6bs`9Jg3;N(8O%xmL4cscTs8j1P>~-M8`4b7c
    z4H<lR4b+3kTE&j31yf<v7~!Q|@tMvu@$1Z(mx@ABBWqHgD_&c8EzG(!47dhxxaCm%
    zI_p)eq%kp%u$!@ijg!rRU1xJ(!?5Mrh;FqG%sLx;a)a3=v-yymJ}3WVxVWF|+H2wR
    zyZQK-CC(U?6U<PupA&SG8(9zR&60t}SdmxBEsmo5(F2cuz0cLRzH7u@(d+8fSq)ZC
    zo;j}}OP{svp?Tj=(7k!z=B<rA4PPeEsQ+L}jr_LkiWeTa@sY1d^3C#n^<8H+B?I?;
    z=>xoY@<p(BhOU51@&w#d&JLa(5Lwp@C?N93fUyQjR8|P1W;l_JQ9<PI{V(lH8A7Zf
    zyT@=Mn@sYJOAmIP^~i8Cn|0Snm+r>xH}H$e*4eeBiF(9bQ(l7rA>Li;!|=qKVK_0%
    zkhP6Y|K<LFLCk#&H_Fe+))+Y1&vCkm!Tb8%ee3Ox1cR(=X7T&;zW;1$GQW2kdySM-
    z*9nyQmA$llcH+6(q|CpeW({I*KAx+MG~Q4XT|fLCz+-s6Zik2s(<0Bz6~%_c3dGrA
    zU#u|TuSi(Wlq6W!g6#@>17?VoYQp^ZD~8bpW%6h2nYog9k1}QhC8pg<L;RwmjG`r}
    zpkdn0-XJN}@dg{F(d4{IhT}$<b}Kuhm2BjAz7~^SIfF@Q8YI1W;o2>d{|<0Ja(`ax
    zIw4*XGhBkf4K?vnt!da-=?^I_lQVRC2%lguEQXb<-H3gvUEANmpFyxJ2mkJkza8-3
    lqx)}*Zbi9jw(&m#3;;O<4K55YX;c6J002ovPDHLkV1n{=GHCz+
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/plus.png b/mapbox-gl/closure-library/closure/goog/images/plus.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..f5dcd2cbc4846c2a610c69b26e5e4bbe13a764d8
    GIT binary patch
    literal 239
    zcmeAS@N?(olHy`uVBq!ia0vp^!ayv*!VDzYUPT51DfSXiUsv`kT=D{nGR>Thx<Db$
    z0*}aI1_rr<Ak26;Gb9)&$dc~p>&U>cv7h@-A}f%u65tc!`v3p`t;eoa&)ItJ+}>k*
    z*B;xu^!dGe_pY4-%D;dA{QLLsKm{9P=B)to8B2ovf*Bm1-ADs+96eneLn>}1Cp0kf
    zu_?`P^78Ux5?J=_-QC@y31{BL1}wX_YF(y5n!CI7l$Dm7jb%@TXy==^8BL9Kb`~)>
    dbM!qM1NTL~V~-}R-T*X=!PC{xWt~$(695<aR-6C;
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/ratingstars.gif b/mapbox-gl/closure-library/closure/goog/images/ratingstars.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..27ba1f6cdbf164894e6a590affdd72102b1e966c
    GIT binary patch
    literal 1139
    zcmV-(1dRJfNk%w1VH5y(0M!5h$jJ2F-T0-Y?a<KmP*B{xz40a{%=q~Ig@xvqm+G~(
    z@S>vZ>+AdI==<mA`uX|()z$XE!1Ckc`LLu*rl##uQO(B2^XlsRJvzIqtM0$CKXq+{
    z+}!x&<obPdea_DHpP%g2#w*puE4sSzczEPHIl!Kt>n<(D_V)dPd3g8t{U9LHJUO_d
    zqwV<k{&8uDQ%;<%t?u#h{PEur|NsBl+4led000000000000000000000000000000
    z00000A^8LV00000EC2ui02Bat000L6K$=j9x<~@Gq;gpsJ}yY6G)a_9Yk+~2_OMO?
    z3W0V>yIgCE0ieJ{MW=$xMZh#)2LhpZ=~Wg$34s7Y0D%c0Aa7U-C_|1z0U%O;0gsS~
    zib((&1rLr71sMP}eN1tV5*bQ%On8<c5J_&6gAWRwGzDx;tV(ebAPQkes!d%>c$=eG
    zqOeUgD5V1hu}TosR6<XgXOBfkXW5qH<U$7y4%|#b=jv1D4p$TwTj%l^81!WES0o!7
    z2$&pjSA#%^5CoX;n_>aOj2ZcsEKGs`gMo`19&Wg300RL<6flH%NV4RN2^gdxFfhR+
    z$q5ENN})ht!$^`LHV~jtW8xD3gC!><OyV<1Lzf2h4OE#i1O}Tk08%Mx5GDx{7$yj%
    zVoym!ojhyM+(-}J5(^0Q8BHs$T{tq1&ZT=>t}czY;vC_9_J~QAEtZym*#oz)k~9ED
    ztP8B91p@{x78|bNiU#4lLrfSNM59n36D`Ius2jk<I1`n|l@4tQK#UPC9Jv?~W7N1L
    zj#1IUEQE5NM<%M>9x=#;0@yT0DCVozu>=#s1RROMn?^DL%K%^x&U^^v5!I4F0AGFY
    zZM=5V>qXC)Zu=wxA^@ITLjVjG@(Zs|LLx<qfckl-1OXz5Fq9JK)er#-58)S}5GSl4
    zVG0kWAmIup5HOAiE(Hbu!#F3j)Zqjfjw1j8FK|eb2rm#o$9V(>b>M9ax^%(^<sm?!
    z2r#5L!wUf3Mj(k-v7psZ<1Cnhi|`SU!j0Ya76Ae{D)9l8<*`R!H|nVu4hJ1Dpu&4+
    zaKL~k0xI#>6djD&o0<wNFoqNzpt%HMyG$|KT}mwQ*aBsasl)&a2qEVgqb;x*A%-lF
    zXA&LIAVXa;D6mWt9MG^prsHtn=oXvc;zTHyDv@atemc@YF?8NVs3i-8x5Q{Q6i~}8
    zp6DV42ZH+9!2-f8&;bXmDzPV_%4i}=tV`6{+cdIXBWs&!etG78Q-CREm+6HQKm!dN
    zK;{+Sid(H%`AHFwZnYFtP(r#)tji~V9(VxY6b-zYL<JgXP{A1ih^s^dB|P-*8TppL
    zkp~ZXz)=bJCXufO9g^g4SV>f30L2|@U_iuFw9Bwb9#nv_5|!-B@k9Pzk*|UUn`A(h
    z@!o~N$Q6uiuM`2_#jnR#mC*0MHPb+F$zh2+^AhqZh^3WJOKk4E-pZ@*6yI9SEjd8|
    F06VQO>H`1(
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_bot.gif b/mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_bot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..4e81e25e3948832956f615a6d23c462486717538
    GIT binary patch
    literal 425
    zcmV;a0apG;Nk%w1VUqwA0J8u9t*x#1_xHuc#opfDwY9a(%*^KI=DoeW@9*!{*4F?3
    z|NsC0000000000000000A^8LW2LKBIEC2ui0FwX}000F4a1DTzy*TU5yZ>M)j^t$k
    z!y*Yn>Ar9*%WS$B1v>A1?}G_I!Jxtk00Kkc$80)9hNEDpS{)96tai)odcWYXc+5Q{
    zDm?=EdllG<2?0U9uG{Z;!Zm9~$Mkpra{>uORW}0#EQ^edj*pO$l9QB;2XlgZ0DUhA
    zmz9nM1BQo11_P+6s;jK6uCK7MvZ)CLikE77Ses`hL2k3C2Bf9M#>dFX%FE2i2*U%l
    zx0t$o14|*E1jNnV-rwNi$<L?K0=S!+ZrB|GISJzN^7Hh?<k5=fm{(W?>m3O6AgD*M
    zpuuJLYOz4aPtZRN?+!|wI5A;9hL;>(!(iabK#Cw%L(a3faE`8ALzcJ$08gaLm)}TU
    zF*(!WDS<F^>Qwd&mBN{GYGP`+v#8N3J@K8xb%2E$0RfImJ(^VExhV;1eVod5XV0v*
    T5{&9f_9!%AWz$kk76AY|vM$Qt
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_top.gif b/mapbox-gl/closure-library/closure/goog/images/right_anchor_bubble_top.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..70bedfbade9c3f2178128310e3c3343502a4ace8
    GIT binary patch
    literal 335
    zcmV-V0kHl@Nk%w1VUqw70J8u9t*x#9|Nq6s#pdSbwY9a@*4FRu@4daf%*@Q*-ro23
    z_y7O@000000000000000A^8LW2LKBIEC2ui0FwX`000F1aLGxly*TU5yZ@h&AO>ij
    zXsWK3C=h^9vUF|Vnlc~<K){H>07onuJvyFGFh~#%h(cm`tXgr&9wDd*C=fA0lzJ_j
    zK|8G=(G9o<hbOc^KHKk;K|#%S``uu1VhJ;NhF5xO1#N$R20sS}0(6I!Mu`A11qO|s
    z3IzahP=uDHGzNQ_2aBDp0S2N3lXa%G9;gDUo36Z|q6nn6!Wy``oV>iSPqLK5&J@L=
    zyU5f5%cF%W2DzEt-rwNi;^XAy=HIKw)$1_l;W8IaB<u9`_V@Vt`mebO6$Stj>>Eh1
    hpuvGovWYp!u%SbT0t8S1pzfi?ix`m&03aa(06Vb)nIr%J
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/toolbar-bg.png b/mapbox-gl/closure-library/closure/goog/images/toolbar-bg.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..cb6791ffdde7298a32a6c755cca5df6504e5f093
    GIT binary patch
    literal 203
    zcmeAS@N?(olHy`uVBq!ia0vp^j6f{G!3HF)&rH7sq}Y<Y-CY>|gW!U_%O?XxI14-?
    ziy0WWg+Q3`(%rg0Ktc8rPhVH|>x>d&Oss}iXIcP-WJ_ElN}Tg^b5rw57@Uhz6H8K4
    z6v{J8G895GQWe}ieFNU7sOA9`>U+94hHzX@?b*o7pulr@(ZBk_wi%6H4v{t%MJcnH
    qwkWPFlv&{QiP!U8&W^%gyI2b+Ti)fdJN*x+mBG{1&t;ucLK6T*8$MkC
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/toolbar-separator.gif b/mapbox-gl/closure-library/closure/goog/images/toolbar-separator.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..a97cad9dd67f792cfa7da1e6e9451bb915f4b65c
    GIT binary patch
    literal 472
    zcmV;}0Vn=PNk%w1VFCaU0LFa)gPgwB?fTf**qpoL($dnj+x4fs)Mkmklda2(r^a!S
    zz24s5L}!%y`}_Cz_Yx8kOLMB*+uPRG*00v`EnAdxjInBsz<Rag+}zxqzSY#!)NO{W
    zdY8nZ!`MY}uK)kcnVFf)%*@o-;7xCvQgfkvy5{)!_<*<Najf2gzv(kun#;?}larH=
    z#_dFFql>=i`T6;Eui$;W==JsWp~&Q1s^3w3v#qVIot>S*!NL6e{9<BaLU5?HwY9gm
    zx5CuvU0q$K$>V9Q;D4C8m$c7>!s}FYpJI-~L2af)ZlzUKRf@0KhP&pp%;lK7)WYNU
    zxZw9DSB>NE`a4^Vs=LmNq{qFzy{N_5LwK)nv*T%LX?2;uyV2x%l)93|>xG`jlef~O
    zq@-SbtZuaBZIQlctl!Sg&d}N9$H&Kqhlhuz#&(pzPIR+KZlsl`$USDFCsLD_mzS=<
    z+-#-Vr^MK+*Yr$ytw3w0cDm<dpx9%l-8^xqFh_L%|Nj600RR90A^sIZa%Ew3Wn>_C
    zX>@2HRA^-&M@dak03rDV0RRAh04x9i000625C8xN9DoZtf(s284H1Yi0*fXBjwTY3
    O4jK*wmNNu1fdD(ICJ!qB
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/toolbar_icons.gif b/mapbox-gl/closure-library/closure/goog/images/toolbar_icons.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..8b62ce87a97584ef048fc426bcecc31854236871
    GIT binary patch
    literal 1062
    zcmV+>1ljvXNk%w1VYvVh0LFa)gPgwB?fTf**qpoL($dnj+x4fs)Mkmklda2(r^a!S
    zz24s5L}!%y`}_Cz_Yx8kOLMB*+uPRG*00v`EnAdxjInBsz<Rag+}zxqzSY#!)NO{W
    zdY8nZ!`MY}uK)kcnVFf)%*@o-;7xCvQgfkvy5{)!_<*<Najf2gzv(kun#;?}larH=
    z#_dFFql>=i`T6;Eui$;W==JsWp~&Q1s^3w3v#qVIot>S*!NL6e{9<BaLU5?HwY9gm
    zx5CuvU0q$K$>V9Q;D4C8m$c7>!s}FYpJI-~L2af)ZlzUKRf@0KhP&pp%;lK7)WYNU
    zxZw9DSB>NE`a4^Vs=LmNq{qFzy{N_5LwK)nv*T%LX?2;uyV2x%l)93|>xG`jlef~O
    zq@-SbtZuaBZIQlctl!Sg&d}N9$H&Kqhlhuz#&(pzPIR+KZlsl`$USDFCsLD_mzS=<
    z+-#-Vr^MK+*Yr$ytw3w0cDm<dpx9%l-8^xqFh_L%|Nj600RR90A^8LW004ggEC2ui
    z0J#7V000O6fPaF6goTEOh>41ejE#<ukdcy%3p$gRn3sK<oSmM1e|(~iqnW0em8YtP
    ze35*cAR!=seJQplxJ)lE7cnt&d7z@Le}2Zt$AiSh$jE}t(8<iv&Ck@;hYJmZ&<{9`
    z*o4l`i6ML;kFR~PAhUgVV;3d!C0S*9LMZyerHsp~02yJ}Cdf=OE5^PpT3~F!p$NuS
    z{8<rE(J)8n_^@k7tA)oRDP+i)0HuzX3pHwhf^?D5H9v@6#=M3o0zqpqnLS{1!_SRG
    z#<=A~WKI-1e<94Z<Jc!afe8hk1d#G$2@M)_GGtg8uxr2wFvJDzVT^^#p@)K6v)HO%
    zB0l~f&M9R&fXjd;XXF?#P(?wID;dD>`}fu@(3jE>Csqt|m}3E9g(6eJD2yMohlQ3S
    zX4K+`K5^S=Y}LmC2Mblc98lxNLWK%LIyf1O6B;36G`&g^;h2vI0Vc%4Bzt+0+RSSa
    z^)`I_^M~geK@jUhgu@X7R1j?OuxCSs4G22WCgn(G+T4cB^A;uI4{+hub|$i%#`w_V
    zh0NVPcL*-%=g=+FhXQMmkw5|fuwjD@B#`jh23%ppoNlV{)}DL83F1RC8~PDPA_5qL
    z#v)qyk;5N3_yL4+{}jcFa{$IwNFO}#00979V8MnR9*odn1x7^J6=OLOS(|x+T!r|D
    z1Q||q%mcH`L=GYX>~PEu0vw_rAw?af%zpq9MoSWRKp+MZZ?xfp3k)zY!UznEw@6DA
    z)+vDlb+Sa~a)t0|NFPy9Kmi;Ss8Fa1Q?v=^c<3#Y8<K@UDqJ*(Xn<#)=+*hjrksQ_
    gp>lcFNx-F{mTIappPq^d0W+xT>Z`D>Izu1;JH~z|wg3PC
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/tree/I.png b/mapbox-gl/closure-library/closure/goog/images/tree/I.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..62a05d5c60dc1b6bb05c00372d05e8d0cb77666b
    GIT binary patch
    literal 232
    zcmeAS@N?(olHy`uVBq!ia0vp^!ayv*!3-o1OKk}NQY`6?zK#qG>ra@ocD)4hB}-f*
    zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%u?6^qxK8h@{{R2Kc+dn7pa@e*
    zkYDhBhNs&NynwtYPZ!6Kid#tuElg}~77QE;DrzzeECS48o(&9#mhmYxY*@+6p^)>6
    hfr(?b6)8do7<Rs3)Qk_1Gy&Se;OXk;vd$@?2>?YiLX`jj
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/tree/cleardot.gif b/mapbox-gl/closure-library/closure/goog/images/tree/cleardot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..35d42e808f0a8017b8d52a06be2f8fec0b466a66
    GIT binary patch
    literal 43
    scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iE{qJ;0LZEa`2YX_
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/tree/tree.gif b/mapbox-gl/closure-library/closure/goog/images/tree/tree.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..0c2393207a703598c02bee82ec3e116ad718b53c
    GIT binary patch
    literal 1568
    zcmd6k{Xf$Q0LQ-==AniN%S)chV&|!nM7P#3QY2la>?Yl%qfX_DblunzLSK=VV-53|
    zYL$l}HOw{-Ef<-GZqmlEX*MxMzIWYUai7=c_5S(uetiN0{k?WZu>m%)2z<+tqv<iR
    z_bw-wUrJ#nQtOk8>LZE<i;^tLFBsP6SiHQ4YNff~%}Hh#Sa1t1y6857Qrp+1n}n?I
    z1<#L*8iGb%#(sE)RWxIzFTx;R1k@7^31h#${S_KZo|7a&;$#p6vmOjH+xzRegVh44
    zjaA8QE$@6@%WZx5n%^Q2v_e7(B&9$yih3>sn$LpN|27|*FIxUw04?W3P!XgqhoJH|
    zz3kzE?%}sRqCr8&J7{1M5|0f@MIxzGDwj<t6jF%-nq7dDO1XORlUl7_`~pGC>Xnt{
    zuV0r}S63kj`cL=&1@yN$-@fpNLx2X{3eTjsk>Tr{V12}rj<mIA*sSz;J3sPzSCd}+
    zU%>5DjNyq-@Gyr?%>eyX6q~t;mx4O{G!DNtDmc|SvH_QPktis1b)SJDb5RGg@p(>>
    z-F4Odm7!%}J~pLJZ);$wu-xwY2SSFSCyIHntacdb?lcri0>;S(M)7>=zRTxy^z}@w
    z<J)&+<k|V9e?r;XaD*M{>zoO_tS4d~XuppZ-1Ky!hq^UZ)Z6q-`kKD?ruF{jy6OI^
    zLk|eAn(JjkW^@m2e@nycDC?q3^s1#%F~%kvIPPyJDaAVY6X=|GtP7d;`uO_<BByg|
    zp#1m^TIBM=$G3NMpjIz}?fm3xds+ZZWV<LIwSQ4*J?6Xg#XuUu3wg3K2i<gc+iM6j
    z8RDeqHk9{id9dHp$yfcBNU3_WW>FN}QfNDnrop0W+N7aM(@l{Qb_S|q3X=iP080?Q
    z98|QPM#9v1w%25pILBvkN_-2Wvqb{p)}N8&;x|`IZU=i#OY%bfw@l=R|8QpF4)|FO
    z9*S@oD94>}`Uyh;=B`r$jkA+V)}1DVvrXbP0jV*%n8PVfd^5edXdl0cLc8=K$&rr#
    z{T^5XvjkuWAAsGV?@XTtOS3%ms~HAna~*4U0nEK?odzs(E4=e-w5m$I8Cxo8r8AR=
    z3L1p0Nwl`R0zNDr&zyMl{1*<8y59{9hnepN^Wm{7KMX=Qrruf08U@Jfthcol97p>Q
    zHGT}L9Zp@Bw230W9_TPmPP5wWT(HKbPUZr)uKI0u55i|oqeammYttUjl;N64#T)Fi
    zJr})?0dU7!w|4Cf^0^M90JM^0dL&NSX>qz%$+fyHSMqFbpjCW_yK$;6RAsHI8{H^Z
    z^|<lS^S#@Iar3Xdq_y(`A9XLIF!p4PQJ=Z~*+WkcFbcQ4333hx`p;n_3j^?1DI6o)
    z?C^!bqZ?lJ8xytSDaJdCVT;43cAV`WK3O?CIZSNvT`)?}B5g+yHl70hzUm4UHtx~3
    zh@uz)@F&$HK6);k4@|pjq3_A&*I0jMZhb@+7h5v1`i=?Lu=;<PGj>X8<c~kEMN#R^
    zdMF|xRHN##pjifMqkWlqc>h|<Ow+P_S;5wI^#Kh{U2DUP9X6(fi5~@Go*nlcrK}kk
    zC4QG-;QJo8;OTWxq2{^_tX$X355aVVd-P!q>>D0qum~2YvGhqes@Nk|ABHtgg>(VB
    z(bb|gtj3gjW{eGDimgRp`+ht_(ot{4uL2b%?Nza8Sr1XiIi8$kDbX;YlGdESw3{^3
    zwJp-=F#CLpokI#d)pkhxEIS1`!b~%xrsx`kxgte#X$GNDd#>a9+DUCO#O*U?ngx8$
    zW{>l>2NF)=aG|MA5z5TKcgNrj{7#p1=M#yBWfnX<*P~FG6%$;B6!N*A&C2ZftTHPp
    zp65NH%t`DmvsUwYyT2%Jkqj9&x`BKwQU#_5GwjT}`2OCiTri8_;278y6rs9J?PYA<
    S(baX}iYkwubqoUlyZ!;0_1eM!
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/tree/tree.png b/mapbox-gl/closure-library/closure/goog/images/tree/tree.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..0dd0ef15ae86477d1cf1fdf7f08e7b0a5a366ac6
    GIT binary patch
    literal 1262
    zcmchV`BPJO6ve+;Fbv2dsO*H&87rtLS`Z14P+>44Vq*db8j!dGS{x$KR`McP5fs!B
    znkur3U{V6I6l`dqK#JffK@58|El@B7!Xgt%^4@D~|A*e0d(O<6x%czS$vJX3#LUFr
    z1OQ-0JrG1Qc*mzTUT09+L=pi2d^0sDke)s^Kh~e<c-oe*sK+3feagT^Wvt7G*@2t1
    z+stduc#DKy>TF<nf#bnut00rWAXB59jTbI7+d_1j6sdQ08&Nt(Q$w3?KJJ|hxOd3+
    zezANZX^WX??TX^$m}>b{r{6n`d{my3vw=rb#;6@19JKiUFjK1*Bi577T;D2mVUB!~
    zP@g%5QGtjfq>cyA)?S=XI3qkOnT)X;d9351&#hz8hi}u<{*9gi?d>&l%l#wae&Ry#
    zrMz7q@<*`hEWA{{lZ&v#xB>!u-f^gDeEx!2$1Yglu?Xv4IBW{<Mde~m+UK~YhKA$E
    z!d8jXE5w^lF6%t&e6oE!b~MuxFFm~rHtJ@)l2E>>qs{I*m7df?O1oG!H_SPIikl0s
    zj0us-RD|^uH<cAD+=7$RQKn5RA6mlBYztYcL5Fmj=yJlog&?#KlPB{r{JnCS+14gK
    z01UVGzd$mheA#y8&`WZ|%5B#12?6ib-iFN0x|zb&_K*7~;gETKPdwpywx|26&+<)8
    zs~Zzsfdu*1Y`k5Go$0dL6rM8~PA&<v5$h`MV*0YHqVhDvffeYD28>&AZM)Y7dq`d}
    z{K-6gSuet{@$vY3Z*>4QwpgEhRMP*z)&<TmSEK;ACxx&`tw+SBQz8nhv2&gck+@D2
    z2xDl3VNyF&q1MH%T6OWWhvu9smn-{FX#S=36IsIP>8=g#gbyEd;nne8PTU_@;g90I
    zM#<OPN{uPkD%|nL6mPviASg<ZTCRT$WhnOEhrZeE-v=J*b}+7UPk0cKf-~L>ebR#T
    zROCxl>CerXLW}PrQ9nFG#s<u{Y3xeZ^@D?h*p4jlNK||^iKyZ)3oX6qU@V)|J{gzB
    zxG`e`^^|D<JP~#9{a>YS8BKRy&mC>VQrti-?ii!?Bb=ZB6MlE1V*`d$Bz64JNMdN1
    z3TTkf_jM4S%fYmI7n-qUxv|3Zi^%fU2<265@-)T!O?%0xCT=%xTxRWXQgzN1c<R6Q
    z)MF`XaD22yT)|^jx11G0sYsP7>I<-q$Lz&Q#c-<RH_8*Syy!>N|MfYLOQK8=j6o4j
    zYNyNt+ldP&iglTi{*qcJ?mM#lo)}duMGX%x5VZ6*dbH1Kd@U$YW8!D`ujkMwaNQyb
    zwxbJI&ml_%4S=0`-51lEtm<V?Pwh#BNgABLU=Vu0;_uK!R+-<a(_Xsu=gxJCHJi6q
    zK#Nys>1t&gLOesb@oNSPrNFEfTW=uY`K`Susj=l?;_3n0fMsmvidJ#E4ybGPgp}I5
    z1Ih1o_8d&v43K5$8b6kgxWN6+_YY-7$ouV<!IXNHm(2v}vmjWakk$IWGvW`X`~<p_
    zEofNEG&g^_C6HA!R{(ZJU~c4=3y)&|E6)F^a)e-M!GGj+OEmBaOAS68B>XBa^B-6H
    BLuLQ~
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/images/ui_controls.jpg b/mapbox-gl/closure-library/closure/goog/images/ui_controls.jpg
    new file mode 100644
    index 0000000000000000000000000000000000000000..4e07611215f4ce4d4ca752221e62a10efbddd793
    GIT binary patch
    literal 21680
    zcmb@u1ymf{(l$H<clW`9ySs(p5Zpbu>)?<OAV?s%ySoJ&oZv3Oo#5^eJmH(<+>>+e
    zy7GPR|Nh;Jz4r9(?p<9~PwnceYM$nw)&LlCQnFG2C@3g^3}gp9tpo5RJ*_PO00jjA
    z0{{R(fc!zWrzL<m02Ues1_l}y(!j#P!oi~;z(Y24WMo7XOmr+POms|4Y&;-7HVzRk
    zCME#|0TBra85tQi{&TA5q*OpsGSZ(nfr4BL4+oEifPhAdgNZ}>Ki-}?0GJ3+M$cNI
    zp`HVtVM0M;LOt~WfB-1KvuDsye^C3Yg@cEIg?a{!02$T806;-Mdj<=O0)v2v3<CxG
    z^K57sSU600ENlvPJTc9f1RPxa=Nu|(#?JneQ!iEBrnz}|U1H;F>kue8xy0jgs@JJ#
    zBuvdgt^t9$HCiTlojug*@4L<*H^+l8@9*<MCWm?kIZlH4a{w~*7gbQ06k^8D*!|C*
    z76B-b!_P3GF#)202WCtROiZZ%YvVONL=iMe3ZT;{{EW9j^w52rl*Wxpg6rYZGg#rG
    zcmcyMl*Fgb=IpgL(FlVbui|iR+Mb~8gJjf3>%;z4u3R{Xbv0T}MY3fn@?OLEi2m(W
    z#!>$phELZxpS{KCCGk!z1ot&UJ{3Gndeo9!m3Ggoe&2^Fz|m_<J}uBB?Mgb8T^;e5
    zEJHVzzsbjnR}NWWXHLRdVxCJYC~Z${M+!mPZID&G7U3k-TjA`45jq)gBA%Y*h5y6{
    zdQTGb|L}bOi&I`34dM;TT+#&x=^Hroun76w&>oSi9*Nzu7#XXeBK|<>A+sNRAW&9H
    zcOfht`&z)zWLnm)wQfy!_8v=vy}?;`focpM59`3`%NprU^or_crjCE+A!pdT@mwIf
    zik5-U6|PIaP6JKGOieO{>(`-nx^MNSpw?r+DZhDY=0~|v?$?U~7V=uC7l>|OfmdT-
    zIg0`&S9|xFcUUWz(!1l@W-Se`ZfTpXUxQ92Qo3J7z5y#Q8d!f-<mqSKcmhP2rhU$^
    zRNX6s@5JsbC@)SlC|vXoTcAobx#_vVZ%rQ@Bj~t<+wQcb{W43CMJRB`c}OB-;^2x}
    z5>wr7tidenn<7XXk>3;?6FsQyeF~TAWH!B{t>+cpb`DPESu$fSw@cmNry#Vr7PNQ_
    zz4Qt-WSs&>>Zw$pRw%ydUTIz#daQS+u~#HD9KpAFXgtx=V+mt3$Sq|^m1Ai0q>hw(
    zQ(+c`fpDD~(}y@%9Z~kE(&AUlWOc>s{~U{h0cl`8F{*LvLw!x?>S*FH$BJF}Hz5ND
    zhI4^qlgawnn{ei2@2?K6_O?OFzHyTR6pDMq#G%oGp0jop<oEp<+LQs|%4<yglA$@D
    zC#RHfeN~6)Jd58da!3rU0+%ny!8{H^$M9kC{!L;A)(X9i<A|6oz2!gl)bCe8c~SFa
    z-x=TXud|w4-bfT(pYEu<wPB?Aze4GMp=y|3(@Lm9Qo0{ZKLmznu6+^SwkqmNwvJ7<
    zKCx>Y<Lh=XE!`rW?Ey-ErXrSKLP^}$=8G<o^VJD+!j+tj3`d$+DLFXIP#-jKc4Z;#
    zCzh9&Pz37qi?~_0V650ImDqRj@^h|J+in&{eNAB{-N7v{>7LKsIkrhT;`!nUrZX#b
    zdIF^M2L71FAVW<aegY_3wOBs^JPf&0J$tTH?913lYBEKun7@sZSirvKm2$;CZ8og@
    z)_?FJO|kO`BX~zRzkCRl*qDe=;_6ygjPFOv`QhNb!|2z=fbd=c&z>YttLM30r+Fw9
    zAKPt^N7~uk?NC9zBE=51slIl7C$Q#=S1bA6#7zxKK~}6ND6=3EeH|{!<aVhp;?K!8
    z7H`7N+ShC)7e}@1JlzCso--}>z_ps4fj}@8Xc88D?`vP{;TDFfsl6~rVU5*6tBY+t
    z_sJOBd;&bUAlW3{N^X?UQ<i2)9tRM@*~hh4#e}R5fs#H`*W^us8OjUIC0PIsqsWg*
    zHP;ttdQtZRU)9&rjwV)|wo4NkkO)fiQyKO%9yjO_;$Dxcm5&onvX5`f`j;Z|e~`kg
    zU%y|nDlBL~LM*Nc@W!u$pW;KO@*j~pTW<_Z7BjlF>l3|qlU!1IX!f-4Gr9-;>Vlit
    z?XQ>*6kq8HBYQ_{xbi+qM^<y^g11Ag?J<@Xc=QBz757n6)BAV;a3#A!8Z#0ry4s7L
    z@2csJrd=z?=sIqgLkUp=o;@dj{`~*XhQ?OU?eqMxwC1))cD~0<YEFf)#7>}D7>{!V
    zuGFDI&86F=KxWX<#|Nr7jgV!z&6e{ZivxLB)TWSox%(B5sC#Fwz<Xnu1Vreth?_L*
    z89$NUu_Eh@B{vjjpV}lV?}qG7JVYUz+tQOr5tXX)L?^#(P`LCb0EXFewF7a(bgEq1
    z3_&T-dNa<NcaN9X&!lf#y<V{V*5L-CovR1V@&q>{+A@z{G!K>>v%ynHwr;rGK-6_0
    zCJ#@P6M!invklB!wn9Q3J3V!^lPK4Key2-GcEf^3(xQIaWOA6Fa&AscS!mfplN2>M
    zKAT@0AgUB_HsXR<RA1BX++`cMqD6i!pt>RK??@g%<oRA%9H<hBYAXKX26itLA@p77
    z&!&zqc39{e`>Nd}N8+X8ie}rY<SVb;>LeCsTK|oyR#0HO1W`G!Pw=Gz#X@^(O~wO5
    zv#d}f)$pK*lV=EOk0Y`T<PL+vlCM0{xT+^ttrtrBKMQ9_c<q?PqcIEhhbx|sp(z5}
    zI=u>`L-PyDi`>U5WA(w&q9eX*=V-*C1Mi8|C0<BK!2GY9S6%yD)#BI%sZ!F!QVu@L
    z0O(mcw7tA3Q+<~H<n<P5J=(mSh;K@w7d=3&EINm8MuBV*V^U-^a)XqG);IY0Q9fc~
    z)RIK=n>JsG<XV_hLhkuY-k4<-ZW2rH;cS)9hz6ZgpR~1}_bb!uJOMfgHiRX6;V#<O
    z#T<jCv^1ww`zF$|xGGGgTNCoP>w^tu_Zd#wO&#-N-N{TBxkc;El#Q9VB{?mNYE<l(
    zS{0~FWT7q(1v#_Y9G@Y~y>;fcTsxvWa0khP%FT4{yG<5J+cd_VHi<@OJPwfUEW_=u
    zbT6XrW0QCx%b+YKjNR)4`p<|fjvnNmVDFpi$m<$msh9&w>YDm_Ce3Wvno-BR-5<|W
    zK8h@N=u!O#_QK}$B<k#zL|$6N#5bh6AJmVnkjV2LnGc8i{vozI44Y3YC&;5*@1^c-
    zhCGf+_o;uVB66%w?E=@koe43(W#S*0;-)pEy=vu2xOXyAD}0^+am7(W*BskdvTL4o
    zr@*zX^_FG)Fl&SlSM~8H3p11Qj|D=Rrn~T=PRn92<cqogpYcAuA^(j!EG7Y$efpIV
    zCq*P3<y_3EPoOT~IsvTF7h_|{&O%Z&a?pG(pY_cj?jhmg(pX>mKI*oJac8fR^1J+_
    zdVZlXj9$W}s}|;uf*<rlcg>8UPXN^?0B({+pSI?uPg(V1HEes$wse-Onm8M1b6^WZ
    zn%8)jc*BL!QYlcC|4t+c%48{U>{!Oy)WLA_j)>#hq8^^ew7KG_Gj4I-)1H2-)brZE
    z80wnuK_f)oKw3j~CB9yHTxrvfn7BJ_BbKDqE0l7%DZ9L&DklIO=B}h92oobSwb7Pu
    zfgq-TV}DZhX6APB)or)9gE^Do24~!lZ~G{GUa?@CkFcRE*jzgcjQa-aJ6U`j{0lxc
    znu^9Q0kmlJm|W6$m_3+mPAJ&}Xt@o^-lx*Ob*ct*@}t7~OqtB?2Q3;Mn8`c*q{y$!
    zB4DQ5?Fem6KMcml)cK@hXz%^QD*Q4H|I)xasT}q(7BsBr*+m9y8mSvE@yEwMC`j4b
    z>?ry)R9x-S>A#U{>->hECC?k&7*}Ewihh_G=rwfb{gbsSPXL^4-xb^*zY@Q&_*{gB
    zKP90Vf@#1=`%@9SwF7<`p{6ToOXV>^dKdo0^MP|~{zxZt{73U3JA4~?je{qEV|Hcv
    zaMN1m>$w5sdNIHyc8jRUcKaxdR5;;`f-Fw$+L62xXEO2zmw#{Mu;+*%Wl}n;gVyK-
    zL;;d+OOS2p4fOBvrh9Bsh|PT|HVw+1$@wDru_igc;ZE*4=YZ9a(t_00zoh73_(l%k
    zw{?DWtZV8(*AdJ4O}$IIL)eDs^ygkVX&5n_IO0ylJq5S0J4u|{UO=i12f^IHb>GOC
    zk3T4t^IOR)N4p3#>k(YEDT!bvD5i>yrHIC;xoo{hcE&=m)D3VDfhY98vi(m}{dajF
    zeVXLmd`tXblJ*4Pwk)lZ$7R#2N|aA99dV6skn^&LW@}OL-{g1!vxT7>o?kH+F*5S8
    zlQ^Dw(Qu-XAE+gTo?N38<h<@D)y9<j9Z>`aLu;>!crb9Qi>3@&T1*nB`pXOtzfWmN
    zRk2+mzd`f@_pxpA_m{`^c6T+}SKip|cvGWl8aBalmOOF{o(#0?%A_aQiBj@X_L%T&
    zz~Bq17mV;-KO($!B=50&>9{`U*?lIa`}|TVh?`X-g7iJRJ?=(7Gl_X*YV3^Q>S+80
    z_la$(dCam2KfW|2oLGm&B-*Ee$Xzk*tj1T>CP;F6oe|MI%?uyH^Fs9m6Gova`d*Wb
    zCIik18CfB-fQA#|b$*!sz&ZPzXPBt+_I(zTSA=|$@-dc`_kOT5Zu|b*^1{-9V1XjF
    zDQV3+Vwf5NZvBbch$bjt$RHshuVri5Cpy}o??hKM;a60*S|a#Ywv$7wU*gs^S0wf|
    z1)e=BH~4jPJ|xbh>Q8j9l#Xb~Z0=wV`28r;$>!m26j{`t;}y^Zrca{B@&$H^S^Re=
    z=Cb&Q*pFWQ*G5Atmq(3$cHhj@{hmJoOp1%0ag&htU+!y#GNy?ssW?Q$$lNaL?jjn>
    zj6i>Wj-ii`-Hv}{{|8}8$oJg)=J<QEXW26wNBChj>}_MmCR;0#s3(!BiS-RuMwBHz
    ztj?n!0pJ(9mm7@@v;YtTnQV@})M19+p{@P%)ZBuo(pZKMBMnkvvtvellr&A%(8Moj
    zg-tl26i&TuCwiNw)bxiR-75WqHV}y1W6ejn-#O|a!z~njCzuhQm|nGEuCki-aGMk^
    zw|WA2e%Tvu>V36?kiiq+VAuNMZo6*qrAa|>cG!pag><%NX=XwO$Lzupt5VnI9XoAS
    zT&y4HPZF@g9^@&xV-)X$N7)nCh>b|(4Fu&~Z1W<5dA74i$?3Vrl@_@IG^g|&O-@zM
    zlkFMNMw>31rOjq9NvXCwaL4ec+%YO**M>)TW?sI|D1R@`6dO`kT}c&3u!=Yw!cjAC
    zSfSF1gAY-W=3k0P57GCPKlGh6)$uVy{Y{R;_j}m3F0z2<*RTfGtTKFpSjPB^b{+$^
    zhZa@Y?A<tyRKc?^cgbj(0(F%>F_IzHfu{&WUI#k-9gF`CpZ|KS&NH?fbrEZj2+h_r
    zl%c6k#Xk1sCbCCmCZc_NQ0bfmu5^H&V~$OC+ulaEMV*ELWB2pI!jFTLQOO{5oi+IM
    zAm=KEW*hHjk0}i?oU>N5+wIpNeW4zbUii_;y@NSxaBn!n0X?d$`{MSig;aOfvEd%#
    zo8(|=5#OwD@z*3cixwZ^BnI*$j?qk0L1^%v<Rz$RMBR~-!Uy=;%u+Oq%qJ*Z&IbV&
    z6)@XL*wF___QZt5#6UY+f-lgz6VU(=46fEM0zIk0_jqLn7#h>wj16LfW*7}@>OXpI
    zBbOBC^9TVP|Iyv}!^4w|aA{Q5v(8X+swm9!&*PO9`sp&IN1O54as`g%66C?Dt^!|G
    zhgj4!>8!NVkk#rt<lk}HJ!EDrn#D2oSNN~?oW5mhI%zVbuV5o(Til$BzyCNH!0|ZC
    zb^k@oL6^IGb`hlJl$sce;W5bNA8|-+trvLg*3SqV3_h6ZawhG{TA@m@Mc0E)XtVEf
    z7hs6dwtA@f<bdl|#f^=w47y|{E!+~8ci))hYU<(6wvZKkCnlkg;}g`Es<~`i!t5;5
    zMS_cBBF%;Qncs|%%<8p<d6l&L!6DN$J`psq?sccQnqc~m@1iyoVcPN^elTHmz1Ywf
    ziIn`R&^Ebgy@F$YZXCK2w~D6N#7BxM5;*oYi#L2*)`mIpAaRPP@T)ARwV7S~hK%ky
    z^3qmQN)%3{2_N}g63$*Ax-Khj3<0IRt#x+lawnlrX}Va%^m_IVi2c(J0`n{_8`L<j
    zWHWaC3fobGZ5nNGdrhJAs62^X>NFLfKa?j$?<fE|OO!|dZB$}Fu<l;jR&L=NGiK29
    zj0vtmVuQp_wlyQr(IaU5%+%LYI<ijg<lQ5V9%irx6z=72Bfy^Tgn$d96TwDUi|+^`
    ztUFpm@Ay;HU|i~aI&7N<4HYL8F5<pF8+EnJh;2`J0_2K!ekZTgLtTu#bhNj}Ue?vu
    zz7}q-`mhN~zCO40Uw%1HUdYtwoTwW-Z1BE{tFy>AT4FIV%2g3G-c;IIZ?eqL$CLJ%
    zYkCrWuk^Ag+DDJvnndnZ>jC}#0fA|80Dbb?@OhwUJv`ee&IFIPnORJu1+f1adMnF;
    zun1B&OZ2FcP2^Y3NQcDL8&%={Srg{*uc`ZkWkI|VcD+f(2<s$bk%gSca~AD}{qM0P
    zR6buGr0p9YCX+o_<kLQwE2^x}dl;}KEHX6?%j_NG-q<wC&UOUIgz=N|bPH?<v?PNE
    zHlxk^cqe0hyRC@OTe#Z`ONvT9$||Vfj8L-etpd?i(f{XfHJHPG+dMbd+%4BLLsEH(
    zPk<r&(<57t<FfdO{}S~y)vv_iSv38Hr=V7|OwH=NRYe&N6)7*Bl}zS<VV4%mtue}Z
    zwJgKQSM_O!uRY4R$9>N2hu!)v6|CQ%2QQGeW{%e7u@rY+Vg!1)G=9;eH_&h{FUt2X
    zqNJLH;}zvGw?<<+`HGLWaDwmmZ9NUi8R8|sst>8~AEsG&f27W>P;nx1SmB6NI!L0z
    zcDc11t9L}mydy&?eA=5NO4NR-WC?Np@|H}n2H-2L;iOjxhQ~BDS`g=pOZBvt4d!&G
    zvfMOf4nEXghP1{C2a@x@)9ZO5q0ir0!Qu?{6MzBGp;lDs?>2JLe3MsA2GaM=x=G@;
    z;3+|r;nIBXNon`bNLk}!Am}ncF`rlu7NjTTC5{1({sI@&#+~Zk{J^H2b9K3?l!C3)
    z3v><`l^VJw;452#4x-sQGe}Szx#syZ90|;pZ-q6n8%N?H^HT=VxUNBq<(0435Z4y|
    zP<(qPUK_#Dw)%KSX9x3Gsq2Gw+9>)c>(x2v#sDy^)G0W1NItL{j?~nrg`8m@x!$z$
    z^^Q>23!x;R$P*|k$*NB)#iu;inkvGJ`H3RD4Vp5Uy!?M&j__->5Evw;GB%5_onMc!
    z4SpSzThf`V>yaWjC<_zvb~2?9es_1)Lc*=$o;>2hurTY)kgi>oH<jPQUd?;QHtPxC
    zV#i*OcY9dg;EtXngrzEawP*%^^(^)|!dJhl>4(EJH)pGhEO8B#HSY^h^5zN5eG+ru
    zt#grPWu*`3ZplcNnaA*s2<kfEWy}^nnyo$pNDMytJ;C)u5Nscma634)hVp7Hp~_L4
    zk(N8Zah-|Nh`*$y`b#QgW@q3_em{jK!3RxVswKDwLyFH!<81bZE1&cDJjJLaiBI%x
    z<sTz9CY-F+7_7b`kJpbllb(Hb@i@d%b!alCw;+vO&;cIbeBg07fQ5;{aZChj`yqnY
    zt%0XiIf(@QU{xN!8|F6fsZA4?$O<U8+<NVGMh*0|9Ln^64;n~zr_bI=zyNbEm%8_f
    zXB$sK-!OWA@y@;Y)tPFhxT-i5%W3$b=ICdt_t{=Q--g{WlMFGbw$8y|rvdZ?sH{+B
    z*$bI$sDLkJ{i7q8OD4BkM_Sz4a~9Vu^{w)lqMHpQHuPuUYJCiK<AnjrSrlZtzzO^$
    zSMhzl*^CMF0!LkRbwh(+o&H9Hio|=6ih+^5Ji;c`WIbe!g0rEk?sJ+Y(HFI;Y6<Le
    zuQgSCV<O9eMhb>!K^38G@)K>JSfubEu&@3=1w9ImH$TM;+!3Q_(x<bJk1B7RRhyWq
    z9ZK|qCKBFtnE+}tw(1L7HX4i<PM>%?A1=x!etPWKKD6)Fb_lLc=*p<q7`d=Hq;``S
    zz1+Hq)jJt<<T=ZNP#hq{d|R{IzV2C6+vS%H?yrmc$`!~`h;FT;c|>cHSB&eBcQQ2b
    zBT0|4l;%cwD_U5boIhZtepCp7F&?T3)77=QA$(DIPcZiq!eC18Ig)>VaDhU&y2g<X
    zz(}mJH(zjo>;M|I1n`c%Zii3*_N>QY@PcdWFb3WS@{-Pf?+YKk?KP~*UN;sLdjuIA
    zk`9b%in-GD3>70DpIQ?#V$cJ|Pe{#gy4sn(K(!hGzyqG4Ynz&>@X#gCO8o`wKSVtz
    zJb|7M5rKpOF_XiEA$ps~PXjaR7{7+$66AHfeid=3?;#<Q0P#!l4;9R*JLf~t*$e-`
    zv5tOc<1<g5UqC*pS=4rz_DJ)wwaVO>l&3OXD!u&;<DjI5K!~bHoz`Ki>KlYQJ>+rs
    zW@}91+tf4#rj1ZT4|+7743o#_&T5|~KJ+#v)PX$n6Wd}@wR+B*MtnT$J5DGp{~$tG
    z#p_qcdIjTN^IP^e$sOAF>F*;B4c`TJ?EDe6`{jPt-2hob99UT^s+Wqq?i~xQJsN~+
    z_1=pSZv_dF1)TB@JEp|UwQHiA(#%<rW<T?2Ay9S9@8RM;CVROq7B~J~CjE=(i<0N$
    z5jRtKYJP%U)f#oqJP(1uH>2@VqV@ciHX*%Dss<H~q{^k)(OiDhRv`4IURiANKt0RP
    z^QQt`Qyw{$%ok}e$3AJtfF0pO^VHc@COpBulIBG^TY29kC1}H)2Smasml;8Xh5a#z
    zmsC-)Imp$32rc~>$zps{mmQZI?tw>B_`^E9<Y18xIS3kf>XXYJb)@)cQmQcHPW;9P
    zeyU+^>k<;b*Q`UIqNAqEeFWzOx{b|wH+Zd3BX7UHZfAD)Kcr$hbZ6@3#fSUy^*8}d
    zR4jpsWqcviidUw!<riT8E%Fb-^UNbw<rm)^HvcW?T;-a>g`42+!)=B!Jvn1gqQk_=
    zCLGIagQjS`EOi2_E%&z<Z!WC%7q3O$uU8HSt}Fi08wy-kl@KG^f`i^uBswY<!TH_8
    zCHjFeon*}>;Rcqkzxb-BWF$*)YA(LXYF5uKP4mg&0vfn^u#v4W$s<}X>jLga%!v2V
    zHpAuF-%g#wabxcp`j9{VSY${mpq~o0azNMEC>8Yyt;w^Kz*74i;;A}eG~XLCokRT#
    zc&IB{RCjeUq&!ybQyE;6<0gUj!mrQ_<UuplOFJhvp?X?^g85T{<wV!qk<8=4dKWsB
    zuXH7NkzIv%!s+c#W35JOH1UPh#wn?-Sw^^fV<J>}ZMRbx93SoqSt@@&rTzu<pA(IB
    zj<?7MGhcqC4X}Ox1+0?+14r}h_&%qg=cE6y*Vja!M&_F0h`-LCvQliEel5=hoKWs2
    zAbm{${{)cpE+c_@OQ-jFJ>~lV)7Bq$T?{aVs^>FI0#j}mJv}=qPD#pXJAV+`ohr70
    zK!rJ;y5a);478TcyWr%@z*rbXvvQq*f687a4C5G0fS$u=Ta$gO1$@`>2C>^90lq80
    zl)HfcaKFwuw3JyXQ{c9uwmsvEWu0v|qP2tG<#<5i7R3M%{!2Rdg0e5;m_3Qrg@r#4
    zOxFcmddHhw=$Ok>lRP#a(LOv(Efp0Nm*xdBEtoem%0s2#9s_y7^x1Zq1qwk;3G^^s
    z?hAMhFuQ35wTA|Emwcc>eQKns4PHouGjsi9XyCU7ctvi`VYZ{%mBz)`gPkq7XaAxp
    zieIc5WfQJf<gmqrLUhIT?c%B<obr56ix!&Jk|2>7?#qEgrxCL%JAN(Uu1<%l(r{B@
    z1t!%&QVE-;5txsW-+Rh}Z)aV83utW(1#vXMY0kX!tb~EC^a&v0#@;XOR6o@-V7<?Z
    zPiGv)6Ab3zsd+tHhuQ%Z4R8Q3LTL(6K2pCWJ3h=ht!3ng;~p}FrQ8B`YBgeY23K=y
    zQZ}uAI?mlzb=YBNHo$Rd<*qveEH5JtN1p0tY@m0#{ILF+P)7ZgP)1O8xPy9kj~(2?
    zfF9&LfbtI}Gsu;)u2UL#<>u71FS%DXOr;cZqZY+l!SqPD5m^RBxtft2!xmIhQtqfZ
    zDe^=AyukzH22M_J&nPJaoF?EyPa#^K@5{1J8mbSgQBW5>q3hn6ghWWf_Z%Ue#wR7t
    zp!}v0FSsFC{H?LsaKW+<iCb<ak(~Fud1Eq(>XO6+2N+rf=pzL$%40SslreSz8hKXo
    zeA^=G6EXq82xhSE?8kR_#Q^9lEur69R7zhLmmlW6%!zY8Vd)N{O66IkdPcC7Wdh|K
    z7Y<+*48hD4r>^WKQk;CH4o0%6S&C5G^snuv&ea^&HsUHzSzin8|AcBy9&rb<z;ZQM
    z{(a-7zYN80?$E>crDwVh^9j={7nOzPuVNqp#xw*UG0f-<MN^><D@RDmiLWczVM}|1
    zoL7OV{bpd@6*X8hms9~M@j5`)3-@Be?L$pr&g*FQA5LW}3_tjd|49ALp6)pRut5nj
    z*lbWWaIVCzUbdz`q$_F~vM4J4C<{!j(@-QDb$`9JhW5h=oue8ZR@+2r=o;lgD(?m<
    z9}9whMToV7*tox0J9Roreg8i}mj0{v1_HM_Z?D+LWkLl!pqo`?=CL#)PJ&{gPoC>2
    zn!`+m^G6I9mVxbF&5MmT+O@-iBt!#?)4*NhHxj}4)3QL2JljVPnVC6Pd%M>y$`&%T
    zp-A!t`U(@*0Bh<-GL(SLl%W~8%iRkthZk!sLB>A`G7=rm&tPuu*0atJny#JFPGfT8
    zSlUV~)2XW-ZWuXn1`RL70DZSGY6~VU;wrR=!en-eS~%qE0*F4?V^v3nt3u;2qw>G|
    z?$IdNCqlnDT8^JY&PMhUf*G;1O0VDAE0aq-QP*e_%NxaUlpUt^8i=$=zr2heyeQ#7
    z9<I+Hn6y)FkG5=N^zNuKrgseQ7K&;|>D*Y}foz09bVcwX;Y~K>4`WFBXw4XsLL#sE
    zmwFcXAlEbotyU1M!Rs^@l3w1AI!3;qbSvX}J?t<Cb)<n~rm!UJGq4@eoS;ZU7vF=9
    z*0<<Mm+F)+-)gcwK(a601!C>L6$xhx4Psk}A*&EBHAC4COf8_iP5t_CBvDCY-+}yR
    zx7l8)Nd*Niu1mRFeRz!TFP2-kyyLNc{5J8199B!x=*bE+P+d{+PCn&V+udJ_i(9$#
    zQ5khg>56UkcPdT}(_`Cp4|L2}uw>EHN0=|5i77+*Nr3>E5=pc;_8H{0^3N($X|9uM
    z!;I_TS0wVt2Q(kn7x3|c$^Dkm!+OE8bahS8@0$Ox4gA0PtfJ2M1dvrTNb@sGcoksr
    zWx)O<J7!{NcS8NMS%*h|1tlfS`78)@NoGUBuy4}bw-`!iUy;gZQ))zCuPD(%x4%j;
    zGYeHYn<%vdJf(d{?q81Y_wVFsy4Q95rbYW8BXkL|vY=5(&MY|NON}P`0{40(3nlv@
    zgEo$=o31G%4{6uj5xt(-r^O18JSdisDlab*UpP1sR4V_1$^vS5>{+!{{^Ase5}z_N
    z=MzA%^9f*2wqk>`F>x!xI*L$U_xnu+R@<8R{4?tJYK132YV9<>tf%qZqD8Q*kg7DL
    zd6+hhuPXa7fBh&3fjn@Bj#j4KH4ApLqGY;HyO=lG<Dh7VB|W^qjkDLFUhO>lUeINe
    zVh4GbMRIdBWutPj<aR2?^QO?V3|#t_i%i<#8_lWeUH@N)whV>$GZ~Icvm)Rp2HKtf
    z`pJ(U<!u?(VpFs!cI8s;3VWO5@B|39yrJVpL+<<jWu=|1m&L-T|7~fzNrS7`Yx96n
    zC2?$d(_osuj$M2Vz<`C~<uM(#%pX~dfKOA>aSjiP;Wx=MnK_Hn-MQZ#-UxyNee~y~
    zDU)}a^7$b<h8Q3KoulO7%#d%X#r&hQn$C#eH?>QQH&|&&V?w_lvFQ2v>8o=sd-X>v
    zk;vt=Hu-Ia7P#0kO1Ff@VGI>pHjzy0DsD$^Xo`yB339@^-DH+=rbr!T(Ai1slKz=%
    zM0iU|5{<He;=MvNC~sV-zR|X+Vi10A0i8`@c$==ANIo1>+}xeB2ls@DLy3wvta0Vl
    zNMu<i%6msja!#02elw)67p$aL<GAiKN_~NQTSk0;{cNLmGUVsvwS^F$l6n5MG%Gp4
    zD_6A3<FujDuYII^8)X6vJVgbBrs(eqUku!txkmY{Zrn7DIa9@M$WmRFq@unhf5d0~
    zR2=;N2>@q;4&M6yp25*2#8Kw<ab49j>zR>h<BWn?yAg9mNs=uiJqxI-UsRn}PooMI
    zp2oisMBaw6<+=4+jHy`>e-|TnS6J-2G&PC6kOt1h$NVs<5bo}gmp1^Yk#?7VFL?J;
    zRWo7vUDE%cOp=#TzlC8v(CRZKq_M;;N`y=%_c_v2enIE^1pW7xyL6+}QWWd}mI;HB
    z!&6?pwjnE`ogQ1;opd%qX1l=U7vt)V!@@(Gg%)FQoFf?CxPPrLL<N?mH?rEAdc!%z
    zMX9B!g{k69*i-5vgp|&<$ub}WN}i_fsAtOBGVP!w*v+<_83-iM9G?I>CAV{iziIyR
    zhNOv?HQnn^h2Eh%Ztl!KtgtsHV`hh~(UnddMn1(=9nnjf@f=;xsf1?pHSTeS0o;Ff
    z*`+uzB42PiiL<q#gPP=FwspX{RC;CLnj8Wr-@;zWSA8THdLg$kZ>K9$*`hI#ZX9Z3
    zT_Pks+v#0ui;{gBr9R-{-xGp?c0wmo_V1XbAxeavbMn_pK=Ic~K!^GnW>b2ESS!5x
    zRW-?&b(@Bn^tZ6BjzmozrZW|ZQJI_%)zwYAaW4i?f8=9P{Wd25sG1N9@q8p>-6@uO
    z|5$E({RDUdgvb@uS07tvk)<;j;jP(zY0}WpVdYITE{&Gww5sIn=x^oX)vk|%7S^n@
    zOkxxBpxORYx^~l#e@gc!!FdhdaV~d}4?%SiVm6tTQP?KU3nqY2J)ct8ZuW91C8{k>
    zet_&Sc^#yLQkG+}xNL1wiae%%22)&--XVdy$e;ted>@gYSNn`q4!z=rMtr1QIvi~u
    zDKp?uM=$?TNp9F&B4%13cV95M?_>I;OEk$5NQu;~x%*a0*3G@R)Pb)GM~o|_k<E}H
    z)eUqm3nyOcGR&SbJQlcr{GUvi`F_^jD*A)fk3;1myr|Cew^r&W7#(w&cat-Qr}$}U
    zCHWjL867ys`uOVV8o5s?h0>+GKnb9U{+dElp-%XkKchL%pRK*~e5XYhSU4nFyqdot
    z{f&6KB^ND??nj<?oH^OFqv}bhfL-2w#DPM*pGup2Gx>f^)B$e^kIHwcZ!CL%Tvo6Q
    z;Dh;H-a<}Fj{BvT@E-NRU;<tt-UxElvj?Y!g^KaYigQzIR9o#8%Yh3QTY`zUF<K|+
    zu&Z_htMco8#<G0oh<xq}t6ASjTAJz#;^hqw0AGb<<cyw?lLbW&X7EfM=|=9jPCOIk
    zbKkM}D2P)6cQlCV<CIoS>hVg2IH!zK0LjyAV99sPHiWSCWc^C&=v$2x+7p12rbb|6
    zcXpJsG{exC@J1}jhODHU{3y68*{mQm0OyGG5W{{BwQ0&g>V87`3D9NwVUZN-382wo
    z_LX*UnTqf2wqu(zQnO}aVq-rS2d}0K6{#BOswMh4VF1!1-S&?OEhR%|I5HG`j4z9+
    zmV%lBd;Jq0DVuwr)BKk`U;@qYA#D7`Lv2muHj;x?NbIyvktva$xQvPeoj%HuGX#?1
    zF8CzY0dOK@QNh4hBSy!AJ~4>-gL|^S815lK@{8v*Kjl=gg5mm0RKMhu<SI7xOH_8b
    z5II4Zu2~(i_66Te0=Cmu2?J^S_qpwP-brp{Jszb#+{Dze{0s~<o?(fJ%opl1Qag2S
    zv5IR@wB#6GCh`^BnVG`6rW$&x#~CD4UbbUT0Jcl0p2ibsFI_|@u7tV^{X-U|5Nx@r
    z1YhW0|9n64S&}~Hht5@{h{o;2T{ImVijvrG-mwDVqvCG*I_&~hcRArAsF#^6g5zw&
    zSD1S3r~#EzmJJhg(|Rqlx_t`JQWFz)BMugp{vf4z-T)}=s!tbUD=WcP$j4r2$Mrp&
    zf{mjHS6Morm7)^*tb*WhD@(tEvV)t0wM*%D9BN5dVf?OqH&e`iF+e4)pt+{ki2I}y
    zx+uWXcA2?+1uS&YJ3@UKiGIDMc|YZx+oHE#)iAcOUr1Ped_W%9iXjF=vi33B3?UCl
    zu=>y1{8!nQfjog{9qs~zo01Ta1k<(WX}PEy9y^t{En9rjwsb`aJy>>N`VshD-O@ik
    z@+}JS*kb^?V1<F?#X5IzH$?!QL5XwYCqIhaFwTx4Y3$B-yZz*?du*tcOWrwUU|DKv
    zSjH9h2LB4|qG=|q0FS`9bc1h^5~t(*+=VOBHVwUEV$jA#ADX!H9%O~KG9Y{UtSUlV
    zKkO0<LCTzW*M7-YY#2!44gqySYG+OBo99GFHG&OX=g>?BK~gCE{W$fFcFwP1FZKG<
    zLG5H(@nd#NO|?{%Tm)B9`DKNXg(>F>Fcs*LG6Nv}sg3}?pu?JQ2+k!BjuYk`3gsh7
    z{b%s*fJRz2*3i#Anq@eb=Rk>09+)wkpKF)*9A<A1D=t4|#|eePV2BOWcbuZZJHva@
    zaisR~2EMpq8TeU+_utdWpB$5P1-YB$4)&3sJG_94uoUjun&r;(NmpEh#~nS{m$Yn<
    zH87x$q%i}a&VrT|#VV{;cd;9xS`!LubPpYP5zf&l!Y!3Y9s=G+?@VxgrySW2-8exd
    zW%L>jOe3kRS$Bch5Oz2XF4Fbfah?gb%S=l-X?zemj>AZ^+&`Q;e!sd|wPg}WTvL|?
    zDqDRr$)?|cU~P^p_x$Q-UeNA{*IRxD+LmtlsFo@ACwf_b+5u*#UF6q!9+03avsVw_
    zn~IaOFAOEJGm*{gBfgVRTc4LboDiQgzX?8-_G$pvM2yYMGR5*n=sfpa-Vy#R2?>eX
    z{Y%Y1UOS|YH?L+XLr;76Ab=3)+@LFKsHk8{e3h=mfz}v&u3(;XlKSqB%BM&D>ipwY
    zrubH0=a`Z*VO%s!IgsD7^rZi5RY9ylp+z3SRJyA($pfC(6W~TPJah*DHHWUtNK>_q
    zMvlVETL(sRZ_yPX82{p!vFl!YcZ=FVkM&2SFnpz=9&Gz@?okvmS<dZ*XD2;KQ4bum
    z+f?I!@>YlB4wFuTz}+^1WOW7i4NR9d6>+2vUQ8oI^pM=A*eb$6c`re$?xDRFqT{VM
    zbABv|&4Z^vc^UM6ZAvWw03Y~n;1aXBW`(W_Z||^}dhz9B-+5f}nhUd&;Nsd(G`=S+
    z{uh-A3Bby|@rf|s*<>O6q-cyuq8HKG(#s|~(LZ8A{y>Cg`SwM?sdGh}KFv=&GXKIO
    z;SG-;WZax`xdSA40q<kgprUL4<%`~$9nRUu)QQitvb0`QJF<d<`~{gy($)=<Vq(a~
    zMWJT15nFMsv5FV6Be5Q_vW(1sq$j0LdE}b)J|->mn1;385tVAC=TrjBxZBI$IiQnv
    zClAv<C!aMwT%kV5IDIdRD#0>cQjlyjtoQ^dg}5wMLI|sf$QE_l!Ckftgl=GUB;+2{
    zA%S<xON^dJ1y&cEv}vImlSs!Qj$Q56_A^&I0jG+~y9J<H7^g+4(;~thkH&W%<SYNV
    zo-kx1ZFGKvSTb^0M^={EtmcDxaiQyag_)xJPDrg6x#OtMO%btc&D4C=Ll>ePU4yrC
    zgFLfv*b|_Y_OC8^d^zLHWaPL*Y<s&wI@17(KZ{S~+N}St#oCKedNtjCa#+da^C_~P
    z+IwenjT7VwMV1C7YEGA;57!=cInLw!L@S>Y#fCW)#e;RyyKT+tXZ0)DH-KVzW9}AK
    zbcmyM_JZ{pKjG=g<yo=I689eofke)K(>uG7xS7U6(7a07GwYQlF3Bx#>Xx!5R2{T{
    zy+T|&2y={wn%Y)>RgqE@3TsLJyt{c89F{z^84V?Vmc_z4uRG!6PF*{d7KFlWL9?*B
    zHkt=r4?2msvrM$24b5HI?^|K2yJq}xmhrn6IQ!kYJioC8V||Bf4g=PJ(7lXtp6`z|
    z$wTA`@z2cvw_llmZCTU#pSjY&xa8}MV<*8-UBjzQheTZ;Tu9N7?)+IKQ`mRvoh*F4
    z>`bd~41^>ZUHlgp`sWR+YrM}m_#OD)aUd@KKk?u%Za}2xPSR>f(W@m0djO8CznbV|
    zQk|M?M(Odt3z)pwjd29_<FW%WFmc53S1ZfgbeO=p&2n<#L8cqAx?vu#i29~RX&_4R
    z*166y>zN06(%)JAPt*JdC0SWLiFfyLO_-U)lNfo23OnW#x$_U7jW@O)581%nzPB+7
    zLyAxr3P;_0aWvaBSi*u=KpLHXc~xd5#l0{OVPhz53*RQ)z#&!60&lA4f%BsZ-)?$@
    z!`ET*?UZqfM)Q<$^WVyb07;AdJ1GUIhT-}a_2Ugwzv)fR9cL`D&QE5awS2cRn`HjD
    zIh{{nvins}L0D&4S0?qwyqg1xj;A>M**7P1Sk3~kVTx3gD`h6CBl}Rcw#s{U>fB^~
    zddd<^WWkk^$}h+I580N9zM4~Qd`kt1nZ16i7;-KuWizGVuP$*I#`E0=MlqE+v#92v
    z7XuiIJ96;wP9Cwx3wU>bf$T3%qOOD;LhOGHIQZq>ATvmRa;^`O5(6n-H<I+=rf0_(
    zqv)Vs%lC>fH;4&M9H>kGLrJcWA9FhR%v_d10P~ttMV@{0qH3d?_gIimy8coz#lO10
    zKHmECw4m+R&4$^%Hz>m1mAMsc6H^-E%)*q}qJ1J3iP2_nz3lwq*?Y2+o-=vh2Tx8l
    zL-Gg+9GEZ8rf>T(mkOGvHjovl&??G;=^{!l@M;)7Ja=$d`Z$OJlOBB8Y4<P%sW-De
    z()zys>$|W&IfVT;UMUy<O?Oy{4oUe+@)9$WC%?YhC(xx19$CA#99!O{6m=Gh@odsG
    zIN?}-fa$}R5O?5(``Ak-cx^ZC93KUO8o7jStzELJ;JGcPqrm9ISmuOS<RmPQB<vKb
    zQ>s{X-SMDvjlvaNSUBoBahTUztCvEtvLlUgDk;wV5c@4R{6A(}RZg1`v*)Bd>dCga
    z0r6g5Nzj=}GyF5d>qg+#Thm@r2UqNPeW|r;cr>R-fePA@z<A}-;fnkHk6ETQ%X?PO
    z1Kz94-0fuurf*VMLDLgPuN><9WmW=I>$SuB<vqhVZUz-}6gTd58x>VMAmKD6KA~g&
    z0A7sxH@{8LUsnaL4*QmdwIvEk0$p!7(7G&8aw@Gh_AW2<;VudJBklUWJy%Yf#qH${
    zgP+jau$7463*idqiU0w5L%6&0cp6F8BG!Gr%%Asi1JzIZ67NH#I;NPwTKJz?Jk0qB
    zAEM<aBwKz<RPcB2pJa>IfHnS+Z5zy3p-5h@`C{=v#{9i*79ohbIFi=vxM|?sh16y%
    zc}DJ8PO-g$pt<9-wlDt_`$c~i>WPD!pttNg7_z8%RO)B4ZIOxespPR15-2uTI+CmX
    zqE4hfWy(fF)wWRZiKyGLmSbJ<Tp98Wp@@Q1XJXC_t=H_=j+5mY>#Li0D?-yCb&aqC
    zPwhM7u#8Dtq2%s73%%u?6kR@)o9#?V*>kCgPwq4=&Bs1<NTvM<on9%&%Y8$AThQ+a
    z5%Uj8jT5<a89LdOgv!C{+_iZ86G#hS-4PHFNLW!ylKyJMKV{etJN1nyYZMVPv#~(2
    z{+R<?TN*o)nks)(#CR!JP9?BM4nf4YP5dk~>vf|+eo?!2UV|!uSt5POfmzC~W=^zC
    zQ{{(14m0a>;A5~?Nrgq2lY&N{;QPcOe-+?P9*^coU)xSzvygkp)*3Z+2e4z@eLI3Q
    z_zaq&By{nR+U#6}oTf=#w%T@;%6(X8*UT+-Dve_eJ2jJy9WIp_Zz)KDnK|s6vpvs5
    zjXW&>3>HVZnsEE>4(QT+SBG^<1mkrvD5o`a)5}~aqvK++i`HJSN7P7Dt3D+7vW30P
    zA_99YM}|I4T%WLn6X>(8Qh8jSYndcy#BrRB29w^o9($~m`w<gXKY^)!O#hCUQTKi!
    zeIT92Zr5V+D5Zi?ch|<hT<UQ`m5CzMid`~{xVOF@B|u9!C;^jT%XEID|7XnvNalvg
    zfcC9IwdnjeziRLKd8XM1T*MskOXDI=d3I|rk0{FFRS;Fii8G@?{^=l?_JOqOnpNVX
    z0a4p{jf6hv{8>VA;>XWWy?6fdDk>2aO{29kx-RE(yAoexTdA+9@s9|a_wmn|vQo9|
    zvERy_CC$4UeKJA#5qtjq34pLsCJaUqwkriMzwenH2f4Dm7soD)PwrtEfn(c_S@m$x
    zKAC!pVb0Q8=W%;b!+L^k=T$r(3T}C$@}0Es@p5<I;|+Z!MK_+_cXM2g#h`js<>U8I
    z<4#ssJ$v~u&~g5z>AbtWTAsCHeD0f;=BTlSxpO>CiyEU`-#7(0@rEr(&CpO$mWfjw
    zj*l5UIa!+RyV)jZ)9k7HYP`L@%tPBVCd4@NB5Fj~>0xZSgquzT$BQf8QcYbG6h&?-
    zgy?*g&p_2z9}9N5rSx>&fEN-j_X)MV!7NMJfpIk0<07^S$CGufXj0IycVmkG-tpzO
    zyMDvW0TlYS(u~c~R#%CiK~m%co5X0jadt&Sf?8Mz2V;VSeV7=_k}lg^L<$GiW?7;+
    z;<w^1BI_ChJM^j+6(xt#4^#H9Lj^1j`c)U-yq4EJKJk%(&EE24vUSL?n;Kfz3#p1%
    zL7<HmnmSBs7iizMvxg4Pya4RLY<Uf>jOL8MGO3rlRaQNwoOn7lsl$)#$}AuzEZz;L
    z%};H{b^1*2rNOAzNXgol3~tzuR;Dz!$Sw)iyZD92SD+Jm(FW_3vXf8IU?moYrI~t}
    z&2aG06CJ=uZuu0=+l+ogo9o@(OdfI6q+82z|Bk;lM4$)68}z{AAAh4?<+D8akj+2F
    zGCH<++mk7Tc06Q-g*2wsXkB^m#e)b*^fW=o?n1g+Q|+yu!{;*`zpo6nI$KdDXiBg;
    zhNPPXBMi$o9%fH~rfEj5GmW6li?$%Yjot5KIk1`PaL19Xn?!TkXFqTv59O^-9PXw?
    z*5;`!mOK~OyJ-&^`uLmLjZ@S%atT7D3FMvtp=ocMAPz(AZAG!Y`p2+5vtz64>iW)D
    zn~Y6uTw%<+tBDZ9JxNq*XS)RD>R64gJ<8OS3xd&CisXl%@G|<Jzj+bRX1{3N#XU8p
    z+Nt1+n*i#?<>ssy#~NBN^^K7u-dmnub&J%oN$=3P5}@gr2DMvNV6~<YT=VD-X3hsv
    zxki}TgF;QY(-pG^yc=K-LtOO&nQ*6PW)JN)N)x5p5iQCSwKz3tdu9V}$S})e)_C8|
    z(lX6tNnpeKA;=LYkhr}$87847;X+BWb}ZvIk8~|$$7$;8B>s_v#WC)^Fztr`#Fhjm
    zB;k*vwu>R}S(rKa?0z`ov6R`t;D#1|N9iThkuhYclXt|b@qW*#HJ+1WL5$I&z|2jR
    z{k$zdTD-fyJ!W@Tw+eb5v2Ob6T-U}J=h*bQuf}DKRCRS|Ab0n-dU+x!!qWlEHaV`-
    zsZ6rMOpoRAn=l3hSlj&aH|*@&3Y3J&!(Ou-ysYfxqzs^U)ow||#@9wnYO>Uf1->j;
    zoEjrBkuxBf^<>KAm>ot1(1a#ZW6DF#k{YF@?`cfN3Y!DShL8j54)Ds(T|6)qVc=Bt
    z6}w4xHyFHRpPT(+uKCq&vF|u29Yoc()p#o6X1V9u_w_^4=h{+Hi|w2V?DQjMDcSMI
    zjI7(B_MZdezXr&@JOMW9$sU6JHZT;X1?A8|Of`7vt!m$gOmb3H9}#JHvQETeaz^iI
    zx}?8H$Uo4OGqY}%rX$;eHB=YZ8s&`)>4_GKd=)I5+wOa6_<_D3YfyzWErOo_RE_dS
    zHbx8F7MnuU7QNH1bq&b}>5FpyD$x6-+eNYb(E2N{Z<ps#$DB6D3bcbsxV4LQloUL{
    z29mvJeRrk}6Y^@vBvgtC<>*Y-RDE-T&%gMpWy-hBDKsi(A{D8C!5|W*I5DC4@yJwb
    z9YqF|=dV7XV`i9cWIDG7;N7DVt<!~iIBO`7`%rO=8*o*DQjpiJSwJ5|sG^7;@6MsD
    z&f$VSCD_;0Ae(V4l@=9nO~~yRr1Ej+U<gR{>>eDWh^rYfJ>H#;-Nz93>UzhHI|ZjA
    zqr$%MbHk!)5}~J>9~roI<~4EKa&I?o2|lXb)w@6DSx?lrv5$|QZf_2&$wX>AATYtK
    zk$tZi4UF{8&*(w9SZVQ2Tz|w2vGWjf&CUTaO1Siya|$nfFkGNoR@7VW%hJ?A9acg6
    zz{ccuIeSxRIVjYk1(q93?{Hu84MeVgv1oDhZrBTmbM6@_{YcK1@J0wUZ+{bBCSQPs
    zP$I*G;y=ZerIxzV9r`ty(5%5z{*|^U=7zTrJ4}M>O<1b?5dH4EI_@0``p~R$f;CU`
    z!*Q44oC!sgyu9F8BBFH^tYZ_~O3oIw+dRDN(H~NI;ck51iBg0UV4Uux&`NNsT%(QY
    zM>gBJk!!xtCDNAE-NdH*2wS&MT8dtsZww}7cgs?#haG|ui6PfBHXRzr(%UsHN4jK7
    z8rh#hU<>+Km}}*vNmLue113#HJ0^cvi(yY3A7r8L85Vxmo@+-!WO3u*4(xL>*FPcm
    z9+Q7&PiJB)2@7jM)4`!8X`}*8|FGg@Z<;ws8M8|RSHE~X4ft5^Yu6nq=};#rt6-Gd
    zr#$;&aKDtnfn5Q+&)$bbhmV<Z<mFYhRT->K7BJrZAW0q=GYE{EQ{Y^xHC-SU&%6*P
    zS|Ek?L^W+vjG(N0$g`Na5j;s?egZtlP2EYjGc&;oz1XZO78_=&UCH!GDiBGri#4@%
    zCrF%g%`a_IJ5&`{dv$wY#1=E^Yw?_yyTaJ(Em(&+&{g!E9xxg_&Q_EduQ~|tK3d}=
    zVZydC>KoooG*OiZUT^b=l4vBU=c;!AtB>l)?X(j$l@<9OgUmTX?2*6K&e94$^oMu5
    z)b0hY`K==>c=!h2g^WTQNezWbsVGsus%^Uj<Gm!<%Nsobr6rbte7ER9T-`whrP<j5
    z$W^{9C@Ir*lPbcIEbm)i^^>BqCt@7kS|E(GHRXJ1k1F2(io#RcZ;5U4;bOUz>W!ct
    zrI|zTm>0X*b^R)h-Xh*B0m_bQO=V5+jIzz~TA1~U0e?(t@Aj^+@1R%AV$Yq<={R|U
    z=ezmO?}NaFslx`Ip9B`fwd^x%*6B(Xn<J%!sy_t>Kf8FWh3{Q{nXq)V@6WiMXHIz%
    zhZINP`!N@kqG%*ovPkW;{B=`TuSLYP+k51UJuf3Z#>KBUA*bpIz>xG-g@fF;lA^kn
    zvY$N*OK!YyA`(3)($PtMXqN}$X4HTy>g|w+IwvoGPkO$yNIQ_1CXR1vwXWB!uqZnb
    zLW~o)9zAdb>|m$L<1&ZZud^`KXmhf`TaHAw4*hN`3~o-OJ*?t-!iWmJh=qL@?yF_;
    z#&I$WAxc!1@bDYE2~yYw_2%5v8D+!W>P=fCA^+wy1I;@AE$%#f+Av<v)$HcZTohy&
    zRdEvVCvj#n=RwTIG40!HJ&JPf3l_}VoPJM$<8s^~t3FwQ5N{Yepjt(3mEKmK2BfF=
    z;#T*SWa=@@$?P*JVCxv^Cob*d6Ni+FHDGNWr|4n6n!E*4H+X4^mCF?^vOY59IL_`e
    zCRysIdFXj{ZQk-hIo;>ZqTx;S_>xRm-Cm??^$#n91KyXAFV*(NA7@2Jk?WN%(67lW
    zo#fMxSqwRam{oo(3=L@%!aY_Rz8#9X9@Er)7HYbz;aao8G%Jsqid?XGbm#u6xb(!+
    zG?K<vc+Ar_iVpynKDdKqsP0E-j7-B6mXPF0%b_x0UYMC$DCd>q3|pirGvG9Wg)r`^
    z{F2GrrMo`9uA&NTuy4CH6In=SZJ>fHG?uDO)XgYm$Q9jI!H;s|w6n8RxFmEH{|y&U
    zuZ&DfRza8<h#{a`@~R?X%LWo4w_|Y!gU?UkIhJxhQb>N-Y~etBNK8f2-rFNqp)`*8
    zA|PFV3bi1&vCm!WNrjn(rD=L4=|2ioAI+|aGMQh%sb03Us|K@^o9do@e+_LO(*)fL
    zYzoNxC|Iye7RruGk~l}*WjWha9#GV{y6Dk1y-`p;Cs_YcT8~vz-(~D=uDb!rI<c?@
    zwIq0wK1*74G1X}>t8|c9X}QBcQ^&4VzY%o4TJWJmxs4~5%S%}r)2ZRd!?RR6&9u+7
    z8lvZrQ0dp#Vr^ZDG!uj(uN#mR-iyLWA0fN^a|Xc^M*^26I$~!utl~CyLR?9Y8M|N%
    zvB1^1^E~my$$FLZl9~8wI1=*TPT>E~a)AT;OMZI2UbA5NDoL*cVVsA{E*0O{zLM4P
    z;cVFwuDiE_YL<LB+qa7~@2#d?(Dt9^IbV2R`T5?qWv-e4oSffy-Qn%trRt}me%H<4
    zm|I%%WkNy}(~shNdHTE)m{)oGovz?xk&oJHxJ`bZj(PAsE@s<<zcwCU5g{v4+)#XM
    zBg3>UlNcX41YEx@X`Qg_@C>!*ym4}?`heBb0$$yc!o43YZ##LE6q!8Vf9%BjlWdz0
    zn_0aO-3@fGC){;Q^Lfm3pEoYPv-8Q_%@@p(9lrEw@g+CQBiFopJ8xLs^tj!svSnFv
    zyz;hcH?h~Lo9<7me6kMWlj#)&fA^_pn~62Qacpi_8Sk0dXWrA1!R4{bSy(A=HJf9(
    zqx^A^^DDR7_DD|RT9IqzeC&tc!JNYzA9Ckr>uxEyWLEcn;%rB0X46eK=WIT0vpM|E
    z#!X>ad(y-<WwY)Je{H8W`HiFBkF?{lOxw$*%-6IrPF~j-z1z!w%G!6ppyaNaZZmsZ
    z728*Z6$qJmps-zg7Z|o-{~6TY&THK3d2)C9g8U$^W~`y`DevYB=__+E%-NYYv+!bp
    zO!Kz){~0npKTX{WF{LQ)_KWB%O}h^i#jXEwXtQwa+b>^)LY#SYn>b??m~3;O9$kB{
    zbkChrck-UKcDv^<TPlClfOT3!h1PseruW%LrtNXfT-3BFyVm)Mfv@t6>sv3Jl%K+Q
    z;>XVq&%zEJU9%9Jm;!?GFY8%lcUMAFnfLm(oYD*0<sfJ5E4aI5&Np3nQj&vv*PrLc
    z>jh!&E=_)_T|c?(yu-aE7!iv*MG|3*eejY$#dX`mqWpM(TfKM4GfudpQnqflck$`e
    z{W@3YE{@{V{U%jaS+n9%w*L->Z4(oJm*ic`(Ph7}t>DOs2&sb;IBZ3tJxcmlZ#i*{
    z=Va!;r<!raM^4UO^>K!y!;LNNy&LAqZA(0{p?23c>s*zRwR5hYIINKD?A!rK8QR+L
    z*z<Zgoj-bMVe!IDyQ9~EK6SdcM3fyKLX#2d?n45=5}fxO11hkkqkrHmIT;dQ#26E3
    z?6c$LIo>C-Thjjl!&+00b=vp3qhBrNeRTA7__<PHX^Mc?O9sJx;o0JrYwwlqOr35t
    z7nsx7ZM&<c)yndSEY_I8#Is<l#WHdKtf{@gY;jv}li~Wz?6yQ!&fH@+PP0V&c1`Av
    zerMns9IR7pwe{o9Ly`A2Sa%ySO8qQ+7Pk)`-{GpRT}|cSJQ5d!k$kC@4Jb3G29#lM
    zgEH)19n%}l63G>XtoJVZ<_KIen>o?hdg*!fokx#M+pe$Ow5`$<bQVrzN!cu~;5mVf
    z?;NLk-Hj^aH;7#(dF0`%*aIh0Iy7rs3yxSgONPehip`3iot4RI{diWn>*~p;8(B<m
    zevDxEx$#2&h&4E$T#n3My2|pjgtqYHvitN+AxH_W@=oD8qhsG5Capen?RSaL)nIRx
    zvukVmGP)|Bo-jGas2;3m^Xf4ql~!GjSfNn1St-Nr2#+!Mf^BZcc{;c=b{#vmap77?
    zhTT|?_|bSK_$T3*ea`XUtx^+LAaOXQ5Q}kW&KhV}$g#5Z3nqE!e7+Q1E}B~E8`GC!
    zaB1VZO5Myt>k|wN{8oparq(+fihm8PIl5r|is~;r+ZU)z+j3?8KXz?J;87-1|MDD(
    zzv}u~7|s$|nshf$q-=Mo>#@hdqPnUkX_3pO-ddaN0NjI>VHp{J;f(9wka)w)H{!eZ
    ziryBua<=IsLu&2OsRB=B)Xlh}D*Eb=<$YXKH09wO-=rg3^ww>>^I&b`q#5&`331CY
    zSS^w1uoNg#o}+WS^i<np|GOflf&O9_KfX43cP+U)&UWEhh2H`hm2g`S_T?|z5pXoz
    zA2<!mvtp&5fXhpUlBJ8y+4^eQK5aS#JQqa!mt@EORo&0@x!cUA-FFO(k8Rn=W3uI<
    zRpt_dBc&1-y!)JdzB7azbl)=jr}>fp49Qpj9erUw<?^PA!;dYSjN?`rFMid2CGt`A
    zN%a6tBc#ne)K5Z+4+8m_nve(TuX=X9n_^M#xy}3NEl-7o9aqdeXLCQ{(d%fOk)V<v
    zeR-L!bo0a`Yvunl2-P>T{LI++cao2LbKW(L-Bb4Mn)zW%sNuPeN#@yX6W;7iXtL;i
    z=)T7&<NlI0e}n29Z46&PS>7h0+hbXhv*#G(el*qbN%dLZ*T3u8jG(P&TGv*mPi%AF
    zyIWmw-b%)w?7ctV=vna{`D(GQ@3oj(lSI-q_jbeg*)O-QN}p}<C+1|u)U^eC2_MT}
    z^E7PO12-RGQ*gi^-i5!X_I+*B`)abIbn-2&l#)j?Kkv?x^|{e=V88w2m}jpiPLTdS
    zseJX;JtEwP<$8B{oxiI7L-?>-?=J|;<){0Wxj)UI5%t>j;bgbFs_CyL|KaxUGg;ZF
    zw?pnfL*DkdyP@I1IV;;F7B8B!^L>dQFY}S-Dhg9xoc<saKSN}zt4Xf(;!Q3xMplhp
    zsB<UOKgQ1OqnYILJqFvtnCH)RiC@$W%zte8U$s32Lg#V5o!7eZ!MvL<qSl%?hE=F<
    zs}ft@RGq#sYs*E}NU%6q$*RjPqUlZ5`3v)dPHlT}?5?N9)D7DW_cv_{WK&(&IVI$g
    S8czeaE+yydVb#n2|0V$S5hStz
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/iter/iter.js b/mapbox-gl/closure-library/closure/goog/iter/iter.js
    new file mode 100644
    index 0000000000..751caff675
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/iter/iter.js
    @@ -0,0 +1,1305 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Python style iteration utilities.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.iter');
    +goog.provide('goog.iter.Iterable');
    +goog.provide('goog.iter.Iterator');
    +goog.provide('goog.iter.StopIteration');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.math');
    +
    +
    +/**
    + * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}
    + */
    +goog.iter.Iterable;
    +
    +
    +/**
    + * Singleton Error object that is used to terminate iterations.
    + * @const {!Error}
    + */
    +goog.iter.StopIteration = ('StopIteration' in goog.global) ?
    +    // For script engines that support legacy iterators.
    +    goog.global['StopIteration'] :
    +    Error('StopIteration');
    +
    +
    +
    +/**
    + * Class/interface for iterators.  An iterator needs to implement a {@code next}
    + * method and it needs to throw a {@code goog.iter.StopIteration} when the
    + * iteration passes beyond the end.  Iterators have no {@code hasNext} method.
    + * It is recommended to always use the helper functions to iterate over the
    + * iterator or in case you are only targeting JavaScript 1.7 for in loops.
    + * @constructor
    + * @template VALUE
    + */
    +goog.iter.Iterator = function() {};
    +
    +
    +/**
    + * Returns the next value of the iteration.  This will throw the object
    + * {@see goog.iter#StopIteration} when the iteration passes the end.
    + * @return {VALUE} Any object or value.
    + */
    +goog.iter.Iterator.prototype.next = function() {
    +  throw goog.iter.StopIteration;
    +};
    +
    +
    +/**
    + * Returns the {@code Iterator} object itself.  This is used to implement
    + * the iterator protocol in JavaScript 1.7
    + * @param {boolean=} opt_keys  Whether to return the keys or values. Default is
    + *     to only return the values.  This is being used by the for-in loop (true)
    + *     and the for-each-in loop (false).  Even though the param gives a hint
    + *     about what the iterator will return there is no guarantee that it will
    + *     return the keys when true is passed.
    + * @return {!goog.iter.Iterator<VALUE>} The object itself.
    + */
    +goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {
    +  return this;
    +};
    +
    +
    +/**
    + * Returns an iterator that knows how to iterate over the values in the object.
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable  If the
    + *     object is an iterator it will be returned as is.  If the object has an
    + *     {@code __iterator__} method that will be called to get the value
    + *     iterator.  If the object is an array-like object we create an iterator
    + *     for that.
    + * @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate
    + *     over the values in {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.toIterator = function(iterable) {
    +  if (iterable instanceof goog.iter.Iterator) {
    +    return iterable;
    +  }
    +  if (typeof iterable.__iterator__ == 'function') {
    +    return iterable.__iterator__(false);
    +  }
    +  if (goog.isArrayLike(iterable)) {
    +    var i = 0;
    +    var newIter = new goog.iter.Iterator;
    +    newIter.next = function() {
    +      while (true) {
    +        if (i >= iterable.length) {
    +          throw goog.iter.StopIteration;
    +        }
    +        // Don't include deleted elements.
    +        if (!(i in iterable)) {
    +          i++;
    +          continue;
    +        }
    +        return iterable[i++];
    +      }
    +    };
    +    return newIter;
    +  }
    +
    +
    +  // TODO(arv): Should we fall back on goog.structs.getValues()?
    +  throw Error('Not implemented');
    +};
    +
    +
    +/**
    + * Calls a function for each element in the iterator with the element of the
    + * iterator passed as argument.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable  The iterator
    + *     to iterate over. If the iterable is an object {@code toIterator} will be
    + *     called on it.
    + * @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f
    + *     The function to call for every element.  This function takes 3 arguments
    + *     (the element, undefined, and the iterator) and the return value is
    + *     irrelevant.  The reason for passing undefined as the second argument is
    + *     so that the same function can be used in {@see goog.array#forEach} as
    + *     well as others.  The third parameter is of type "number" for
    + *     arraylike objects, undefined, otherwise.
    + * @param {THIS=} opt_obj  The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @template THIS, VALUE
    + */
    +goog.iter.forEach = function(iterable, f, opt_obj) {
    +  if (goog.isArrayLike(iterable)) {
    +    /** @preserveTry */
    +    try {
    +      // NOTES: this passes the index number to the second parameter
    +      // of the callback contrary to the documentation above.
    +      goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f,
    +                         opt_obj);
    +    } catch (ex) {
    +      if (ex !== goog.iter.StopIteration) {
    +        throw ex;
    +      }
    +    }
    +  } else {
    +    iterable = goog.iter.toIterator(iterable);
    +    /** @preserveTry */
    +    try {
    +      while (true) {
    +        f.call(opt_obj, iterable.next(), undefined, iterable);
    +      }
    +    } catch (ex) {
    +      if (ex !== goog.iter.StopIteration) {
    +        throw ex;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calls a function for every element in the iterator, and if the function
    + * returns true adds the element to a new iterator.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     to iterate over.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every element. This function takes 3 arguments
    + *     (the element, undefined, and the iterator) and should return a boolean.
    + *     If the return value is true the element will be included in the returned
    + *     iterator.  If it is false the element is not included.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
    + *     that passed the test are present.
    + * @template THIS, VALUE
    + */
    +goog.iter.filter = function(iterable, f, opt_obj) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var newIter = new goog.iter.Iterator;
    +  newIter.next = function() {
    +    while (true) {
    +      var val = iterator.next();
    +      if (f.call(opt_obj, val, undefined, iterator)) {
    +        return val;
    +      }
    +    }
    +  };
    +  return newIter;
    +};
    +
    +
    +/**
    + * Calls a function for every element in the iterator, and if the function
    + * returns false adds the element to a new iterator.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     to iterate over.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every element. This function takes 3 arguments
    + *     (the element, undefined, and the iterator) and should return a boolean.
    + *     If the return value is false the element will be included in the returned
    + *     iterator.  If it is true the element is not included.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
    + *     that did not pass the test are present.
    + * @template THIS, VALUE
    + */
    +goog.iter.filterFalse = function(iterable, f, opt_obj) {
    +  return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);
    +};
    +
    +
    +/**
    + * Creates a new iterator that returns the values in a range.  This function
    + * can take 1, 2 or 3 arguments:
    + * <pre>
    + * range(5) same as range(0, 5, 1)
    + * range(2, 5) same as range(2, 5, 1)
    + * </pre>
    + *
    + * @param {number} startOrStop  The stop value if only one argument is provided.
    + *     The start value if 2 or more arguments are provided.  If only one
    + *     argument is used the start value is 0.
    + * @param {number=} opt_stop  The stop value.  If left out then the first
    + *     argument is used as the stop value.
    + * @param {number=} opt_step  The number to increment with between each call to
    + *     next.  This can be negative.
    + * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
    + *     in the range.
    + */
    +goog.iter.range = function(startOrStop, opt_stop, opt_step) {
    +  var start = 0;
    +  var stop = startOrStop;
    +  var step = opt_step || 1;
    +  if (arguments.length > 1) {
    +    start = startOrStop;
    +    stop = opt_stop;
    +  }
    +  if (step == 0) {
    +    throw Error('Range step argument must not be zero');
    +  }
    +
    +  var newIter = new goog.iter.Iterator;
    +  newIter.next = function() {
    +    if (step > 0 && start >= stop || step < 0 && start <= stop) {
    +      throw goog.iter.StopIteration;
    +    }
    +    var rv = start;
    +    start += step;
    +    return rv;
    +  };
    +  return newIter;
    +};
    +
    +
    +/**
    + * Joins the values in a iterator with a delimiter.
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     to get the values from.
    + * @param {string} deliminator  The text to put between the values.
    + * @return {string} The joined value string.
    + * @template VALUE
    + */
    +goog.iter.join = function(iterable, deliminator) {
    +  return goog.iter.toArray(iterable).join(deliminator);
    +};
    +
    +
    +/**
    + * For every element in the iterator call a function and return a new iterator
    + * with that value.
    + *
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterator to iterate over.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f
    + *     The function to call for every element.  This function takes 3 arguments
    + *     (the element, undefined, and the iterator) and should return a new value.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
    + *     results of applying the function to each element in the original
    + *     iterator.
    + * @template THIS, VALUE, RESULT
    + */
    +goog.iter.map = function(iterable, f, opt_obj) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var newIter = new goog.iter.Iterator;
    +  newIter.next = function() {
    +    var val = iterator.next();
    +    return f.call(opt_obj, val, undefined, iterator);
    +  };
    +  return newIter;
    +};
    +
    +
    +/**
    + * Passes every element of an iterator into a function and accumulates the
    + * result.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     to iterate over.
    + * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for
    + *     every element. This function takes 2 arguments (the function's previous
    + *     result or the initial value, and the value of the current element).
    + *     function(previousValue, currentElement) : newValue.
    + * @param {VALUE} val The initial value to pass into the function on the first
    + *     call.
    + * @param {THIS=} opt_obj  The object to be used as the value of 'this' within
    + *     f.
    + * @return {VALUE} Result of evaluating f repeatedly across the values of
    + *     the iterator.
    + * @template THIS, VALUE
    + */
    +goog.iter.reduce = function(iterable, f, val, opt_obj) {
    +  var rval = val;
    +  goog.iter.forEach(iterable, function(val) {
    +    rval = f.call(opt_obj, rval, val);
    +  });
    +  return rval;
    +};
    +
    +
    +/**
    + * Goes through the values in the iterator. Calls f for each of these, and if
    + * any of them returns true, this returns true (without checking the rest). If
    + * all return false this will return false.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     object.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every value. This function takes 3 arguments
    + *     (the value, undefined, and the iterator) and should return a boolean.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {boolean} true if any value passes the test.
    + * @template THIS, VALUE
    + */
    +goog.iter.some = function(iterable, f, opt_obj) {
    +  iterable = goog.iter.toIterator(iterable);
    +  /** @preserveTry */
    +  try {
    +    while (true) {
    +      if (f.call(opt_obj, iterable.next(), undefined, iterable)) {
    +        return true;
    +      }
    +    }
    +  } catch (ex) {
    +    if (ex !== goog.iter.StopIteration) {
    +      throw ex;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Goes through the values in the iterator. Calls f for each of these and if any
    + * of them returns false this returns false (without checking the rest). If all
    + * return true this will return true.
    + *
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     object.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every value. This function takes 3 arguments
    + *     (the value, undefined, and the iterator) and should return a boolean.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {boolean} true if every value passes the test.
    + * @template THIS, VALUE
    + */
    +goog.iter.every = function(iterable, f, opt_obj) {
    +  iterable = goog.iter.toIterator(iterable);
    +  /** @preserveTry */
    +  try {
    +    while (true) {
    +      if (!f.call(opt_obj, iterable.next(), undefined, iterable)) {
    +        return false;
    +      }
    +    }
    +  } catch (ex) {
    +    if (ex !== goog.iter.StopIteration) {
    +      throw ex;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Takes zero or more iterables and returns one iterator that will iterate over
    + * them in the order chained.
    + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
    + *     number of iterable objects.
    + * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
    + *     iterate over all the given iterables' contents.
    + * @template VALUE
    + */
    +goog.iter.chain = function(var_args) {
    +  return goog.iter.chainFromIterable(arguments);
    +};
    +
    +
    +/**
    + * Takes a single iterable containing zero or more iterables and returns one
    + * iterator that will iterate over each one in the order given.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.chain.from_iterable
    + * @param {goog.iter.Iterable} iterable The iterable of iterables to chain.
    + * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
    + *     iterate over all the contents of the iterables contained within
    + *     {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.chainFromIterable = function(iterable) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var iter = new goog.iter.Iterator();
    +  var current = null;
    +
    +  iter.next = function() {
    +    while (true) {
    +      if (current == null) {
    +        var it = iterator.next();
    +        current = goog.iter.toIterator(it);
    +      }
    +      try {
    +        return current.next();
    +      } catch (ex) {
    +        if (ex !== goog.iter.StopIteration) {
    +          throw ex;
    +        }
    +        current = null;
    +      }
    +    }
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Builds a new iterator that iterates over the original, but skips elements as
    + * long as a supplied function returns true.
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     object.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every value. This function takes 3 arguments
    + *     (the value, undefined, and the iterator) and should return a boolean.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from
    + *     the original iterator as long as {@code f} is true.
    + * @template THIS, VALUE
    + */
    +goog.iter.dropWhile = function(iterable, f, opt_obj) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var newIter = new goog.iter.Iterator;
    +  var dropping = true;
    +  newIter.next = function() {
    +    while (true) {
    +      var val = iterator.next();
    +      if (dropping && f.call(opt_obj, val, undefined, iterator)) {
    +        continue;
    +      } else {
    +        dropping = false;
    +      }
    +      return val;
    +    }
    +  };
    +  return newIter;
    +};
    +
    +
    +/**
    + * Builds a new iterator that iterates over the original, but only as long as a
    + * supplied function returns true.
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     object.
    + * @param {
    + *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
    + *     The function to call for every value. This function takes 3 arguments
    + *     (the value, undefined, and the iterator) and should return a boolean.
    + * @param {THIS=} opt_obj This is used as the 'this' object in f when called.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in
    + *     the original iterator as long as the function is true.
    + * @template THIS, VALUE
    + */
    +goog.iter.takeWhile = function(iterable, f, opt_obj) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var iter = new goog.iter.Iterator();
    +  iter.next = function() {
    +    var val = iterator.next();
    +    if (f.call(opt_obj, val, undefined, iterator)) {
    +      return val;
    +    }
    +    throw goog.iter.StopIteration;
    +  };
    +  return iter;
    +};
    +
    +
    +/**
    + * Converts the iterator to an array
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
    + *     to convert to an array.
    + * @return {!Array<VALUE>} An array of the elements the iterator iterates over.
    + * @template VALUE
    + */
    +goog.iter.toArray = function(iterable) {
    +  // Fast path for array-like.
    +  if (goog.isArrayLike(iterable)) {
    +    return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable));
    +  }
    +  iterable = goog.iter.toIterator(iterable);
    +  var array = [];
    +  goog.iter.forEach(iterable, function(val) {
    +    array.push(val);
    +  });
    +  return array;
    +};
    +
    +
    +/**
    + * Iterates over two iterables and returns true if they contain the same
    + * sequence of elements and have the same length.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first
    + *     iterable object.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second
    + *     iterable object.
    + * @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison
    + *     function.
    + *     Should take two arguments to compare, and return true if the arguments
    + *     are equal. Defaults to {@link goog.array.defaultCompareEquality} which
    + *     compares the elements using the built-in '===' operator.
    + * @return {boolean} true if the iterables contain the same sequence of elements
    + *     and have the same length.
    + * @template VALUE
    + */
    +goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) {
    +  var fillValue = {};
    +  var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);
    +  var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
    +  return goog.iter.every(pairs, function(pair) {
    +    return equalsFn(pair[0], pair[1]);
    +  });
    +};
    +
    +
    +/**
    + * Advances the iterator to the next position, returning the given default value
    + * instead of throwing an exception if the iterator has no more entries.
    + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable
    + *     object.
    + * @param {VALUE} defaultValue The value to return if the iterator is empty.
    + * @return {VALUE} The next item in the iteration, or defaultValue if the
    + *     iterator was empty.
    + * @template VALUE
    + */
    +goog.iter.nextOrValue = function(iterable, defaultValue) {
    +  try {
    +    return goog.iter.toIterator(iterable).next();
    +  } catch (e) {
    +    if (e != goog.iter.StopIteration) {
    +      throw e;
    +    }
    +    return defaultValue;
    +  }
    +};
    +
    +
    +/**
    + * Cartesian product of zero or more sets.  Gives an iterator that gives every
    + * combination of one element chosen from each set.  For example,
    + * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
    + * @see http://docs.python.org/library/itertools.html#itertools.product
    + * @param {...!goog.array.ArrayLike<VALUE>} var_args Zero or more sets, as
    + *     arrays.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each
    + *     n-tuple (as an array).
    + * @template VALUE
    + */
    +goog.iter.product = function(var_args) {
    +  var someArrayEmpty = goog.array.some(arguments, function(arr) {
    +    return !arr.length;
    +  });
    +
    +  // An empty set in a cartesian product gives an empty set.
    +  if (someArrayEmpty || !arguments.length) {
    +    return new goog.iter.Iterator();
    +  }
    +
    +  var iter = new goog.iter.Iterator();
    +  var arrays = arguments;
    +
    +  // The first indices are [0, 0, ...]
    +  var indicies = goog.array.repeat(0, arrays.length);
    +
    +  iter.next = function() {
    +
    +    if (indicies) {
    +      var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {
    +        return arrays[arrayIndex][valueIndex];
    +      });
    +
    +      // Generate the next-largest indices for the next call.
    +      // Increase the rightmost index. If it goes over, increase the next
    +      // rightmost (like carry-over addition).
    +      for (var i = indicies.length - 1; i >= 0; i--) {
    +        // Assertion prevents compiler warning below.
    +        goog.asserts.assert(indicies);
    +        if (indicies[i] < arrays[i].length - 1) {
    +          indicies[i]++;
    +          break;
    +        }
    +
    +        // We're at the last indices (the last element of every array), so
    +        // the iteration is over on the next call.
    +        if (i == 0) {
    +          indicies = null;
    +          break;
    +        }
    +        // Reset the index in this column and loop back to increment the
    +        // next one.
    +        indicies[i] = 0;
    +      }
    +      return retVal;
    +    }
    +
    +    throw goog.iter.StopIteration;
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Create an iterator to cycle over the iterable's elements indefinitely.
    + * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
    + * @see: http://docs.python.org/library/itertools.html#itertools.cycle.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable object.
    + * @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely
    + *     over the values in {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.cycle = function(iterable) {
    +  var baseIterator = goog.iter.toIterator(iterable);
    +
    +  // We maintain a cache to store the iterable elements as we iterate
    +  // over them. The cache is used to return elements once we have
    +  // iterated over the iterable once.
    +  var cache = [];
    +  var cacheIndex = 0;
    +
    +  var iter = new goog.iter.Iterator();
    +
    +  // This flag is set after the iterable is iterated over once
    +  var useCache = false;
    +
    +  iter.next = function() {
    +    var returnElement = null;
    +
    +    // Pull elements off the original iterator if not using cache
    +    if (!useCache) {
    +      try {
    +        // Return the element from the iterable
    +        returnElement = baseIterator.next();
    +        cache.push(returnElement);
    +        return returnElement;
    +      } catch (e) {
    +        // If an exception other than StopIteration is thrown
    +        // or if there are no elements to iterate over (the iterable was empty)
    +        // throw an exception
    +        if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {
    +          throw e;
    +        }
    +        // set useCache to true after we know that a 'StopIteration' exception
    +        // was thrown and the cache is not empty (to handle the 'empty iterable'
    +        // use case)
    +        useCache = true;
    +      }
    +    }
    +
    +    returnElement = cache[cacheIndex];
    +    cacheIndex = (cacheIndex + 1) % cache.length;
    +
    +    return returnElement;
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that counts indefinitely from a starting value.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.count
    + * @param {number=} opt_start The starting value. Default is 0.
    + * @param {number=} opt_step The number to increment with between each call to
    + *     next. Negative and floating point numbers are allowed. Default is 1.
    + * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
    + *     in the series.
    + */
    +goog.iter.count = function(opt_start, opt_step) {
    +  var counter = opt_start || 0;
    +  var step = goog.isDef(opt_step) ? opt_step : 1;
    +  var iter = new goog.iter.Iterator();
    +
    +  iter.next = function() {
    +    var returnValue = counter;
    +    counter += step;
    +    return returnValue;
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns the same object or value repeatedly.
    + * @param {VALUE} value Any object or value to repeat.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
    + *     repeated value.
    + * @template VALUE
    + */
    +goog.iter.repeat = function(value) {
    +  var iter = new goog.iter.Iterator();
    +
    +  iter.next = goog.functions.constant(value);
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns running totals from the numbers in
    + * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields
    + * {@code 1 -> 3 -> 6 -> 10 -> 15}.
    + * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate
    + * @param {!goog.iter.Iterable<number>} iterable The iterable of numbers to
    + *     accumulate.
    + * @return {!goog.iter.Iterator<number>} A new iterator that returns the
    + *     numbers in the series.
    + */
    +goog.iter.accumulate = function(iterable) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var total = 0;
    +  var iter = new goog.iter.Iterator();
    +
    +  iter.next = function() {
    +    total += iterator.next();
    +    return total;
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns arrays containing the ith elements from the
    + * provided iterables. The returned arrays will be the same size as the number
    + * of iterables given in {@code var_args}. Once the shortest iterable is
    + * exhausted, subsequent calls to {@code next()} will throw
    + * {@code goog.iter.StopIteration}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.izip
    + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
    + *     number of iterable objects.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
    + *     arrays of elements from the provided iterables.
    + * @template VALUE
    + */
    +goog.iter.zip = function(var_args) {
    +  var args = arguments;
    +  var iter = new goog.iter.Iterator();
    +
    +  if (args.length > 0) {
    +    var iterators = goog.array.map(args, goog.iter.toIterator);
    +    iter.next = function() {
    +      var arr = goog.array.map(iterators, function(it) {
    +        return it.next();
    +      });
    +      return arr;
    +    };
    +  }
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns arrays containing the ith elements from the
    + * provided iterables. The returned arrays will be the same size as the number
    + * of iterables given in {@code var_args}. Shorter iterables will be extended
    + * with {@code fillValue}. Once the longest iterable is exhausted, subsequent
    + * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest
    + * @param {VALUE} fillValue The object or value used to fill shorter iterables.
    + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
    + *     number of iterable objects.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
    + *     arrays of elements from the provided iterables.
    + * @template VALUE
    + */
    +goog.iter.zipLongest = function(fillValue, var_args) {
    +  var args = goog.array.slice(arguments, 1);
    +  var iter = new goog.iter.Iterator();
    +
    +  if (args.length > 0) {
    +    var iterators = goog.array.map(args, goog.iter.toIterator);
    +
    +    iter.next = function() {
    +      var iteratorsHaveValues = false;  // false when all iterators are empty.
    +      var arr = goog.array.map(iterators, function(it) {
    +        var returnValue;
    +        try {
    +          returnValue = it.next();
    +          // Iterator had a value, so we've not exhausted the iterators.
    +          // Set flag accordingly.
    +          iteratorsHaveValues = true;
    +        } catch (ex) {
    +          if (ex !== goog.iter.StopIteration) {
    +            throw ex;
    +          }
    +          returnValue = fillValue;
    +        }
    +        return returnValue;
    +      });
    +
    +      if (!iteratorsHaveValues) {
    +        throw goog.iter.StopIteration;
    +      }
    +      return arr;
    +    };
    +  }
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that filters {@code iterable} based on a series of
    + * {@code selectors}. On each call to {@code next()}, one item is taken from
    + * both the {@code iterable} and {@code selectors} iterators. If the item from
    + * {@code selectors} evaluates to true, the item from {@code iterable} is given.
    + * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors}
    + * is exhausted, subsequent calls to {@code next()} will throw
    + * {@code goog.iter.StopIteration}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.compress
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to filter.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An
    + *     iterable of items to be evaluated in a boolean context to determine if
    + *     the corresponding element in {@code iterable} should be included in the
    + *     result.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
    + *     filtered values.
    + * @template VALUE
    + */
    +goog.iter.compress = function(iterable, selectors) {
    +  var selectorIterator = goog.iter.toIterator(selectors);
    +
    +  return goog.iter.filter(iterable, function() {
    +    return !!selectorIterator.next();
    +  });
    +};
    +
    +
    +
    +/**
    + * Implements the {@code goog.iter.groupBy} iterator.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to group.
    + * @param {function(...VALUE): KEY=} opt_keyFunc  Optional function for
    + *     determining the key value for each group in the {@code iterable}. Default
    + *     is the identity function.
    + * @constructor
    + * @extends {goog.iter.Iterator<!Array<?>>}
    + * @template KEY, VALUE
    + * @private
    + */
    +goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
    +
    +  /**
    +   * The iterable to group, coerced to an iterator.
    +   * @type {!goog.iter.Iterator}
    +   */
    +  this.iterator = goog.iter.toIterator(iterable);
    +
    +  /**
    +   * A function for determining the key value for each element in the iterable.
    +   * If no function is provided, the identity function is used and returns the
    +   * element unchanged.
    +   * @type {function(...VALUE): KEY}
    +   */
    +  this.keyFunc = opt_keyFunc || goog.functions.identity;
    +
    +  /**
    +   * The target key for determining the start of a group.
    +   * @type {KEY}
    +   */
    +  this.targetKey;
    +
    +  /**
    +   * The current key visited during iteration.
    +   * @type {KEY}
    +   */
    +  this.currentKey;
    +
    +  /**
    +   * The current value being added to the group.
    +   * @type {VALUE}
    +   */
    +  this.currentValue;
    +};
    +goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
    +
    +
    +/** @override */
    +goog.iter.GroupByIterator_.prototype.next = function() {
    +  while (this.currentKey == this.targetKey) {
    +    this.currentValue = this.iterator.next();  // Exits on StopIteration
    +    this.currentKey = this.keyFunc(this.currentValue);
    +  }
    +  this.targetKey = this.currentKey;
    +  return [this.currentKey, this.groupItems_(this.targetKey)];
    +};
    +
    +
    +/**
    + * Performs the grouping of objects using the given key.
    + * @param {KEY} targetKey  The target key object for the group.
    + * @return {!Array<VALUE>} An array of grouped objects.
    + * @private
    + */
    +goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
    +  var arr = [];
    +  while (this.currentKey == targetKey) {
    +    arr.push(this.currentValue);
    +    try {
    +      this.currentValue = this.iterator.next();
    +    } catch (ex) {
    +      if (ex !== goog.iter.StopIteration) {
    +        throw ex;
    +      }
    +      break;
    +    }
    +    this.currentKey = this.keyFunc(this.currentValue);
    +  }
    +  return arr;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns arrays containing elements from the
    + * {@code iterable} grouped by a key value. For iterables with repeated
    + * elements (i.e. sorted according to a particular key function), this function
    + * has a {@code uniq}-like effect. For example, grouping the array:
    + * {@code [A, B, B, C, C, A]} produces
    + * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.groupby
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to group.
    + * @param {function(...VALUE): KEY=} opt_keyFunc  Optional function for
    + *     determining the key value for each group in the {@code iterable}. Default
    + *     is the identity function.
    + * @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns
    + *     arrays of consecutive key and groups.
    + * @template KEY, VALUE
    + */
    +goog.iter.groupBy = function(iterable, opt_keyFunc) {
    +  return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);
    +};
    +
    +
    +/**
    + * Gives an iterator that gives the result of calling the given function
    + * <code>f</code> with the arguments taken from the next element from
    + * <code>iterable</code> (the elements are expected to also be iterables).
    + *
    + * Similar to {@see goog.iter#map} but allows the function to accept multiple
    + * arguments from the iterable.
    + *
    + * @param {!goog.iter.Iterable<!goog.iter.Iterable>} iterable The iterable of
    + *     iterables to iterate over.
    + * @param {function(this:THIS,...*):RESULT} f The function to call for every
    + *     element.  This function takes N+2 arguments, where N represents the
    + *     number of items from the next element of the iterable. The two
    + *     additional arguments passed to the function are undefined and the
    + *     iterator itself. The function should return a new value.
    + * @param {THIS=} opt_obj The object to be used as the value of 'this' within
    + *     {@code f}.
    + * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
    + *     results of applying the function to each element in the original
    + *     iterator.
    + * @template THIS, RESULT
    + */
    +goog.iter.starMap = function(iterable, f, opt_obj) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var iter = new goog.iter.Iterator();
    +
    +  iter.next = function() {
    +    var args = goog.iter.toArray(iterator.next());
    +    return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Returns an array of iterators each of which can iterate over the values in
    + * {@code iterable} without advancing the others.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.tee
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to tee.
    + * @param {number=} opt_num  The number of iterators to create. Default is 2.
    + * @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators.
    + * @template VALUE
    + */
    +goog.iter.tee = function(iterable, opt_num) {
    +  var iterator = goog.iter.toIterator(iterable);
    +  var num = goog.isNumber(opt_num) ? opt_num : 2;
    +  var buffers = goog.array.map(goog.array.range(num), function() {
    +    return [];
    +  });
    +
    +  var addNextIteratorValueToBuffers = function() {
    +    var val = iterator.next();
    +    goog.array.forEach(buffers, function(buffer) {
    +      buffer.push(val);
    +    });
    +  };
    +
    +  var createIterator = function(buffer) {
    +    // Each tee'd iterator has an associated buffer (initially empty). When a
    +    // tee'd iterator's buffer is empty, it calls
    +    // addNextIteratorValueToBuffers(), adding the next value to all tee'd
    +    // iterators' buffers, and then returns that value. This allows each
    +    // iterator to be advanced independently.
    +    var iter = new goog.iter.Iterator();
    +
    +    iter.next = function() {
    +      if (goog.array.isEmpty(buffer)) {
    +        addNextIteratorValueToBuffers();
    +      }
    +      goog.asserts.assert(!goog.array.isEmpty(buffer));
    +      return buffer.shift();
    +    };
    +
    +    return iter;
    +  };
    +
    +  return goog.array.map(buffers, createIterator);
    +};
    +
    +
    +/**
    + * Creates an iterator that returns arrays containing a count and an element
    + * obtained from the given {@code iterable}.
    + * @see http://docs.python.org/2/library/functions.html#enumerate
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to enumerate.
    + * @param {number=} opt_start  Optional starting value. Default is 0.
    + * @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing
    + *     count/item pairs.
    + * @template VALUE
    + */
    +goog.iter.enumerate = function(iterable, opt_start) {
    +  return goog.iter.zip(goog.iter.count(opt_start), iterable);
    +};
    +
    +
    +/**
    + * Creates an iterator that returns the first {@code limitSize} elements from an
    + * iterable. If this number is greater than the number of elements in the
    + * iterable, all the elements are returned.
    + * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to limit.
    + * @param {number} limitSize  The maximum number of elements to return.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator containing
    + *     {@code limitSize} elements.
    + * @template VALUE
    + */
    +goog.iter.limit = function(iterable, limitSize) {
    +  goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);
    +
    +  var iterator = goog.iter.toIterator(iterable);
    +
    +  var iter = new goog.iter.Iterator();
    +  var remaining = limitSize;
    +
    +  iter.next = function() {
    +    if (remaining-- > 0) {
    +      return iterator.next();
    +    }
    +    throw goog.iter.StopIteration;
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that is advanced {@code count} steps ahead. Consumed
    + * values are silently discarded. If {@code count} is greater than the number
    + * of elements in {@code iterable}, an empty iterator is returned. Subsequent
    + * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to consume.
    + * @param {number} count  The number of elements to consume from the iterator.
    + * @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps
    + *     ahead.
    + * @template VALUE
    + */
    +goog.iter.consume = function(iterable, count) {
    +  goog.asserts.assert(goog.math.isInt(count) && count >= 0);
    +
    +  var iterator = goog.iter.toIterator(iterable);
    +
    +  while (count-- > 0) {
    +    goog.iter.nextOrValue(iterator, null);
    +  }
    +
    +  return iterator;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns a range of elements from an iterable.
    + * Similar to {@see goog.array#slice} but does not support negative indexes.
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to slice.
    + * @param {number} start  The index of the first element to return.
    + * @param {number=} opt_end  The index after the last element to return. If
    + *     defined, must be greater than or equal to {@code start}.
    + * @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of
    + *     the original.
    + * @template VALUE
    + */
    +goog.iter.slice = function(iterable, start, opt_end) {
    +  goog.asserts.assert(goog.math.isInt(start) && start >= 0);
    +
    +  var iterator = goog.iter.consume(iterable, start);
    +
    +  if (goog.isNumber(opt_end)) {
    +    goog.asserts.assert(
    +        goog.math.isInt(/** @type {number} */ (opt_end)) && opt_end >= start);
    +    iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
    +  }
    +
    +  return iterator;
    +};
    +
    +
    +/**
    + * Checks an array for duplicate elements.
    + * @param {Array<VALUE>|goog.array.ArrayLike} arr The array to check for
    + *     duplicates.
    + * @return {boolean} True, if the array contains duplicates, false otherwise.
    + * @private
    + * @template VALUE
    + */
    +// TODO(user): Consider moving this into goog.array as a public function.
    +goog.iter.hasDuplicates_ = function(arr) {
    +  var deduped = [];
    +  goog.array.removeDuplicates(arr, deduped);
    +  return arr.length != deduped.length;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns permutations of elements in
    + * {@code iterable}.
    + *
    + * Permutations are obtained by taking the Cartesian product of
    + * {@code opt_length} iterables and filtering out those with repeated
    + * elements. For example, the permutations of {@code [1,2,3]} are
    + * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.permutations
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable from which to generate permutations.
    + * @param {number=} opt_length Length of each permutation. If omitted, defaults
    + *     to the length of {@code iterable}.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the
    + *     permutations of {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.permutations = function(iterable, opt_length) {
    +  var elements = goog.iter.toArray(iterable);
    +  var length = goog.isNumber(opt_length) ? opt_length : elements.length;
    +
    +  var sets = goog.array.repeat(elements, length);
    +  var product = goog.iter.product.apply(undefined, sets);
    +
    +  return goog.iter.filter(product, function(arr) {
    +    return !goog.iter.hasDuplicates_(arr);
    +  });
    +};
    +
    +
    +/**
    + * Creates an iterator that returns combinations of elements from
    + * {@code iterable}.
    + *
    + * Combinations are obtained by taking the {@see goog.iter#permutations} of
    + * {@code iterable} and filtering those whose elements appear in the order they
    + * are encountered in {@code iterable}. For example, the 3-length combinations
    + * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.combinations
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable from which to generate combinations.
    + * @param {number} length The length of each combination.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
    + *     combinations from the {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.combinations = function(iterable, length) {
    +  var elements = goog.iter.toArray(iterable);
    +  var indexes = goog.iter.range(elements.length);
    +  var indexIterator = goog.iter.permutations(indexes, length);
    +  // sortedIndexIterator will now give arrays of with the given length that
    +  // indicate what indexes into "elements" should be returned on each iteration.
    +  var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    +    return goog.array.isSorted(arr);
    +  });
    +
    +  var iter = new goog.iter.Iterator();
    +
    +  function getIndexFromElements(index) {
    +    return elements[index];
    +  }
    +
    +  iter.next = function() {
    +    return goog.array.map(
    +        /** @type {!Array<number>} */
    +        (sortedIndexIterator.next()), getIndexFromElements);
    +  };
    +
    +  return iter;
    +};
    +
    +
    +/**
    + * Creates an iterator that returns combinations of elements from
    + * {@code iterable}, with repeated elements possible.
    + *
    + * Combinations are obtained by taking the Cartesian product of {@code length}
    + * iterables and filtering those whose elements appear in the order they are
    + * encountered in {@code iterable}. For example, the 2-length combinations of
    + * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
    + * @see http://docs.python.org/2/library/itertools.html#itertools.combinations_with_replacement
    + * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition
    + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
    + *     iterable to combine.
    + * @param {number} length The length of each combination.
    + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
    + *     combinations from the {@code iterable}.
    + * @template VALUE
    + */
    +goog.iter.combinationsWithReplacement = function(iterable, length) {
    +  var elements = goog.iter.toArray(iterable);
    +  var indexes = goog.array.range(elements.length);
    +  var sets = goog.array.repeat(indexes, length);
    +  var indexIterator = goog.iter.product.apply(undefined, sets);
    +  // sortedIndexIterator will now give arrays of with the given length that
    +  // indicate what indexes into "elements" should be returned on each iteration.
    +  var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
    +    return goog.array.isSorted(arr);
    +  });
    +
    +  var iter = new goog.iter.Iterator();
    +
    +  function getIndexFromElements(index) {
    +    return elements[index];
    +  }
    +
    +  iter.next = function() {
    +    return goog.array.map(
    +        /** @type {!Array<number>} */
    +        (sortedIndexIterator.next()), getIndexFromElements);
    +  };
    +
    +  return iter;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/iter/iter_test.html b/mapbox-gl/closure-library/closure/goog/iter/iter_test.html
    new file mode 100644
    index 0000000000..e678f77e8e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/iter/iter_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.iter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.iterTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="t1" style="display:none">
    +   <span>0</span>
    +   <span>1</span>
    +   <span>2</span>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/iter/iter_test.js b/mapbox-gl/closure-library/closure/goog/iter/iter_test.js
    new file mode 100644
    index 0000000000..ea29c655cf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/iter/iter_test.js
    @@ -0,0 +1,867 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.iterTest');
    +goog.setTestOnly('goog.iterTest');
    +
    +goog.require('goog.iter');
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.testing.jsunit');
    +
    +function ArrayIterator(array) {
    +  this.array_ = array;
    +  this.current_ = 0;
    +}
    +goog.inherits(ArrayIterator, goog.iter.Iterator);
    +
    +ArrayIterator.prototype.next = function() {
    +  if (this.current_ >= this.array_.length) {
    +    throw goog.iter.StopIteration;
    +  }
    +  return this.array_[this.current_++];
    +};
    +
    +function testForEach() {
    +  var s = '';
    +  var iter = new ArrayIterator(['a', 'b', 'c', 'd']);
    +  goog.iter.forEach(iter, function(val, index, iter2) {
    +    assertEquals(iter, iter2);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    s += val;
    +  });
    +  assertEquals('abcd', s);
    +}
    +
    +function testJoin() {
    +  var iter = new ArrayIterator(['a', 'b', 'c', 'd']);
    +  assertEquals('abcd', goog.iter.join(iter, ''));
    +
    +  iter = new ArrayIterator(['a', 'b', 'c', 'd']);
    +  assertEquals('a,b,c,d', goog.iter.join(iter, ','));
    +
    +  // make sure everything is treated as strings
    +  iter = new ArrayIterator([0, 1, 2, 3]);
    +  assertEquals('0123', goog.iter.join(iter, ''));
    +
    +  iter = new ArrayIterator([0, 1, 2, 3]);
    +  assertEquals('0919293', goog.iter.join(iter, 9));
    +
    +  // Joining an empty iterator should result in an empty string
    +  iter = new ArrayIterator([]);
    +  assertEquals('', goog.iter.join(iter, ','));
    +
    +}
    +
    +function testRange() {
    +  var iter = goog.iter.range(0, 5, 1);
    +  assertEquals('01234', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(0, 5, 2);
    +  assertEquals('024', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(0, 5, 5);
    +  assertEquals('0', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(0, 5, 10);
    +  assertEquals('0', goog.iter.join(iter, ''));
    +
    +  // negative step
    +  var iter = goog.iter.range(5, 0, -1);
    +  assertEquals('54321', goog.iter.join(iter, ''));
    +
    +
    +  iter = goog.iter.range(5, 0, -2);
    +  assertEquals('531', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(5, 0, -5);
    +  assertEquals('5', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(5, 0, -10);
    +  assertEquals('5', goog.iter.join(iter, ''));
    +
    +  // wrong direction should result in empty iterator
    +  iter = goog.iter.range(0, 5, -1);
    +  assertEquals('', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(5, 0, 1);
    +  assertEquals('', goog.iter.join(iter, ''));
    +
    +  // a step of 0 is not allowed
    +  goog.iter.range(0, 5, 0);
    +
    +  // test the opt args
    +  iter = goog.iter.range(0, 5);
    +  assertEquals('01234', goog.iter.join(iter, ''));
    +
    +  iter = goog.iter.range(5);
    +  assertEquals('01234', goog.iter.join(iter, ''));
    +
    +}
    +
    +function testFilter() {
    +  var iter = goog.iter.range(5);
    +  var iter2 = goog.iter.filter(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val > 1;
    +  });
    +
    +  assertEquals('234', goog.iter.join(iter2, ''));
    +
    +  // Chaining filters
    +  iter = goog.iter.range(10);
    +  var sb = [];
    +  var evens = goog.iter.filter(iter, function(v) {
    +    sb.push('a' + v);
    +    return v % 2 == 0;
    +  });
    +  var evens2 = goog.iter.filter(evens, function(v) {
    +    sb.push('b' + v);
    +    return v >= 5;
    +  });
    +
    +  assertEquals('68', goog.iter.join(evens2, ''));
    +  // Note the order here. The next calls are done lazily.
    +  assertEquals('a0b0a1a2b2a3a4b4a5a6b6a7a8b8a9', sb.join(''));
    +
    +}
    +
    +function testFilterFalse() {
    +  var iter = goog.iter.range(5);
    +  var iter2 = goog.iter.filterFalse(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val < 2;
    +  });
    +
    +  assertEquals('234', goog.iter.join(iter2, ''));
    +
    +  // Chaining filters
    +  iter = goog.iter.range(10);
    +  var sb = [];
    +  var odds = goog.iter.filterFalse(iter, function(v) {
    +    sb.push('a' + v);
    +    return v % 2 == 0;
    +  });
    +  var odds2 = goog.iter.filterFalse(odds, function(v) {
    +    sb.push('b' + v);
    +    return v <= 5;
    +  });
    +
    +  assertEquals('79', goog.iter.join(odds2, ''));
    +  // Note the order here. The next calls are done lazily.
    +  assertEquals('a0a1b1a2a3b3a4a5b5a6a7b7a8a9b9', sb.join(''));
    +
    +}
    +
    +function testMap() {
    +  var iter = goog.iter.range(4);
    +  var iter2 = goog.iter.map(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val * val;
    +  });
    +  assertEquals('0149', goog.iter.join(iter2, ''));
    +}
    +
    +function testReduce() {
    +  var iter = goog.iter.range(1, 5);
    +  assertEquals(
    +      10, // 1 + 2 + 3 + 4
    +      goog.iter.reduce(iter, function(val, el) {
    +        return val + el;
    +      }, 0));
    +}
    +
    +function testReduce2() {
    +  var iter = goog.iter.range(1, 5);
    +  assertEquals(
    +      24, // 4!
    +      goog.iter.reduce(iter, function(val, el) {
    +        return val * el;
    +      }, 1));
    +}
    +
    +function testSome() {
    +  var iter = goog.iter.range(5);
    +  var b = goog.iter.some(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val > 1;
    +  });
    +  assertTrue(b);
    +  iter = goog.iter.range(5);
    +  b = goog.iter.some(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val > 100;
    +  });
    +  assertFalse(b);
    +}
    +
    +
    +function testEvery() {
    +  var iter = goog.iter.range(5);
    +  var b = goog.iter.every(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val >= 0;
    +  });
    +  assertTrue(b);
    +  iter = goog.iter.range(5);
    +  b = goog.iter.every(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val > 1;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testChain() {
    +  var iter = goog.iter.range(0, 2);
    +  var iter2 = goog.iter.range(2, 4);
    +  var iter3 = goog.iter.range(4, 6);
    +  var iter4 = goog.iter.chain(iter, iter2, iter3);
    +
    +  assertEquals('012345', goog.iter.join(iter4, ''));
    +
    +  // empty iter
    +  iter = new goog.iter.Iterator;
    +  iter2 = goog.iter.chain(iter);
    +  assertEquals('', goog.iter.join(iter2, ''));
    +
    +  // no args
    +  iter2 = goog.iter.chain();
    +  assertEquals('', goog.iter.join(iter2, ''));
    +
    +  // arrays
    +  var arr = [0, 1];
    +  var arr2 = [2, 3];
    +  var arr3 = [4, 5];
    +  iter = goog.iter.chain(arr, arr2, arr3);
    +  assertEquals('012345', goog.iter.join(iter, ''));
    +}
    +
    +function testChainFromIterable() {
    +  var arg = [0, 1];
    +  var arg2 = [2, 3];
    +  var arg3 = goog.iter.range(4, 6);
    +  var iter = goog.iter.chainFromIterable([arg, arg2, arg3]);
    +  assertEquals('012345', goog.iter.join(iter, ''));
    +}
    +
    +function testChainFromIterable2() {
    +  var arg = goog.iter.zip([0, 3], [1, 4], [2, 5]);
    +  var iter = goog.iter.chainFromIterable(arg);
    +  assertEquals('012345', goog.iter.join(iter, ''));
    +}
    +
    +function testDropWhile() {
    +  var iter = goog.iter.range(10);
    +  var iter2 = goog.iter.dropWhile(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val < 5;
    +  });
    +
    +  assertEquals('56789', goog.iter.join(iter2, ''));
    +}
    +
    +function testDropWhile2() {
    +  var iter = goog.iter.range(10);
    +  var iter2 = goog.iter.dropWhile(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val != 5;
    +  });
    +
    +  assertEquals('56789', goog.iter.join(iter2, ''));
    +}
    +
    +
    +function testTakeWhile() {
    +  var iter = goog.iter.range(10);
    +  var iter2 = goog.iter.takeWhile(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val < 5;
    +  });
    +
    +  assertEquals('01234', goog.iter.join(iter2, ''));
    +
    +  // next() should not have been called on iter after the first failure and
    +  // therefore it should contain some elements.  5 failed so we should have
    +  // the rest
    +  assertEquals('6789', goog.iter.join(iter, ''));
    +}
    +
    +function testTakeWhile2() {
    +  var iter = goog.iter.range(10);
    +  var iter2 = goog.iter.takeWhile(iter, function(val, index, iter3) {
    +    assertEquals(iter, iter3);
    +    assertEquals('index should be undefined', 'undefined', typeof index);
    +    return val != 5;
    +  });
    +
    +  assertEquals('01234', goog.iter.join(iter2, ''));
    +
    +  // next() should not have been called on iter after the first failure and
    +  // therefore it should contain some elements.  5 failed so we should have
    +  // the rest
    +  assertEquals('6789', goog.iter.join(iter, ''));
    +}
    +
    +function testToArray() {
    +  var iter = goog.iter.range(5);
    +  var array = goog.iter.toArray(iter);
    +  assertEquals('01234', array.join(''));
    +
    +  // Empty
    +  iter = new goog.iter.Iterator;
    +  array = goog.iter.toArray(iter);
    +  assertEquals('Empty iterator to array', '', array.join(''));
    +}
    +
    +function testToArray2() {
    +  var iterable = [0, 1, 2, 3, 4];
    +  var array = goog.iter.toArray(iterable);
    +  assertEquals('01234', array.join(''));
    +
    +  // Empty
    +  iterable = [];
    +  array = goog.iter.toArray(iterable);
    +  assertEquals('Empty iterator to array', '', array.join(''));
    +}
    +
    +function testEquals() {
    +  var iter = goog.iter.range(5);
    +  var iter2 = goog.iter.range(5);
    +  assertTrue('Equal iterators', goog.iter.equals(iter, iter2));
    +
    +  iter = goog.iter.range(4);
    +  iter2 = goog.iter.range(5);
    +  assertFalse('Second one is longer', goog.iter.equals(iter, iter2));
    +
    +  iter = goog.iter.range(5);
    +  iter2 = goog.iter.range(4);
    +  assertFalse('First one is longer', goog.iter.equals(iter, iter2));
    +
    +  // 2 empty iterators
    +  iter = new goog.iter.Iterator;
    +  iter2 = new goog.iter.Iterator;
    +  assertTrue('Two empty iterators are equal', goog.iter.equals(iter, iter2));
    +
    +  iter = goog.iter.range(4);
    +  assertFalse('Same iterator', goog.iter.equals(iter, iter));
    +
    +  // equality function
    +  iter = goog.iter.toIterator(['A', 'B', 'C']);
    +  iter2 = goog.iter.toIterator(['a', 'b', 'c']);
    +  var equalsFn = function(a, b) {
    +    return a.toLowerCase() == b.toLowerCase();
    +  };
    +  assertTrue('Case-insensitive equal', goog.iter.equals(iter, iter2, equalsFn));
    +}
    +
    +
    +function testToIterator() {
    +  var iter = new goog.iter.range(5);
    +  var iter2 = goog.iter.toIterator(iter);
    +  assertEquals('toIterator on an iterator should return the same obejct',
    +               iter, iter2);
    +
    +  var iterLikeObject = {
    +    next: function() {
    +      throw goog.iter.StopIteration;
    +    }
    +  };
    +  var obj = {
    +    __iterator__: function(opt_keys) {
    +      assertFalse(
    +          '__iterator__ should always be called with false in toIterator',
    +          opt_keys);
    +      return iterLikeObject;
    +    }
    +  };
    +
    +  assertEquals('Should return the return value of __iterator_(false)',
    +               iterLikeObject, goog.iter.toIterator(obj));
    +
    +  // Array
    +  var array = [0, 1, 2, 3, 4];
    +  iter = goog.iter.toIterator(array);
    +  assertEquals('01234', goog.iter.join(iter, ''));
    +
    +  // Array like
    +  var arrayLike = {'0': 0, '1': 1, '2': 2, length: 3};
    +  iter = goog.iter.toIterator(arrayLike);
    +  assertEquals('012', goog.iter.join(iter, ''));
    +
    +  // DOM
    +  var dom = document.getElementById('t1').childNodes;
    +  iter = goog.iter.toIterator(dom);
    +  iter2 = goog.iter.map(iter, function(el) {
    +    return el.innerHTML;
    +  });
    +  assertEquals('012', goog.iter.join(iter2, ''));
    +}
    +
    +
    +function testNextOrValue() {
    +  var iter = goog.iter.toIterator([1]);
    +
    +  assertEquals('Should return value when iterator is non-empty',
    +      1, goog.iter.nextOrValue(iter, null));
    +  assertNull('Should return given default when iterator is empty',
    +      goog.iter.nextOrValue(iter, null));
    +  assertEquals('Should return given default when iterator is (still) empty',
    +      -1, goog.iter.nextOrValue(iter, -1));
    +}
    +
    +// Return the product of several arrays as an array
    +function productAsArray(var_args) {
    +  var iter = goog.iter.product.apply(null, arguments);
    +  return goog.iter.toArray(iter);
    +}
    +
    +function testProduct() {
    +  assertArrayEquals([[1, 3], [1, 4], [2, 3], [2, 4]],
    +      productAsArray([1, 2], [3, 4]));
    +
    +  assertArrayEquals([
    +    [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6],
    +    [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]
    +  ], productAsArray([1, 2], [3, 4], [5, 6]));
    +
    +  assertArrayEquals([[1]], productAsArray([1]));
    +  assertArrayEquals([], productAsArray([1], []));
    +  assertArrayEquals([], productAsArray());
    +
    +  var expectedResult = [];
    +  var a = [1, 2, 3];
    +  var b = [4, 5, 6];
    +  var c = [7, 8, 9];
    +  for (var i = 0; i < a.length; i++) {
    +    for (var j = 0; j < b.length; j++) {
    +      for (var k = 0; k < c.length; k++) {
    +        expectedResult.push([a[i], b[j], c[k]]);
    +      }
    +    }
    +  }
    +
    +  assertArrayEquals(expectedResult, productAsArray(a, b, c));
    +}
    +
    +function testProductIteration() {
    +  var iter = goog.iter.product([1, 2], [3, 4]);
    +
    +  assertArrayEquals([1, 3], iter.next());
    +  assertArrayEquals([1, 4], iter.next());
    +  assertArrayEquals([2, 3], iter.next());
    +  assertArrayEquals([2, 4], iter.next());
    +
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +
    +  // Ensure the iterator forever throws StopIteration.
    +  for (var i = 0; i < 5; i++) {
    +    var ex = assertThrows(function() { iter.next() });
    +    assertEquals(goog.iter.StopIteration, ex);
    +  }
    +
    +  iter = goog.iter.product();
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +
    +  iter = goog.iter.product([]);
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testCycle() {
    +
    +  var regularArray = [1, 2, 3];
    +  var iter = goog.iter.cycle(regularArray);
    +
    +  // Test 3 cycles to ensure proper cache behavior
    +  var values = [];
    +  for (var i = 0; i < 9; i++) {
    +    values.push(iter.next());
    +  }
    +
    +  assertArrayEquals([1, 2, 3, 1, 2, 3, 1, 2, 3], values);
    +}
    +
    +function testCycleSingleItemIterable() {
    +  var singleItemArray = [1];
    +
    +  var iter = goog.iter.cycle(singleItemArray);
    +  var values = [];
    +
    +  for (var i = 0; i < 5; i++) {
    +    values.push(iter.next());
    +  }
    +
    +  assertArrayEquals([1, 1, 1, 1, 1], values);
    +}
    +
    +function testCycleEmptyIterable() {
    +
    +  var emptyArray = [];
    +
    +  var iter = goog.iter.cycle(emptyArray);
    +  var ex = assertThrows(function() { iter.next(); });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testCountNoArgs() {
    +  var iter = goog.iter.count();
    +  var values = goog.iter.limit(iter, 5);
    +  assertArrayEquals([0, 1, 2, 3, 4], goog.iter.toArray(values));
    +}
    +
    +function testCountStart() {
    +  var iter = goog.iter.count(10);
    +  var values = goog.iter.limit(iter, 5);
    +  assertArrayEquals([10, 11, 12, 13, 14], goog.iter.toArray(values));
    +}
    +
    +function testCountStep() {
    +  var iter = goog.iter.count(10, 2);
    +  var values = goog.iter.limit(iter, 5);
    +  assertArrayEquals([10, 12, 14, 16, 18], goog.iter.toArray(values));
    +}
    +
    +function testCountNegativeStep() {
    +  var iter = goog.iter.count(10, -2);
    +  var values = goog.iter.limit(iter, 5);
    +  assertArrayEquals([10, 8, 6, 4, 2], goog.iter.toArray(values));
    +}
    +
    +function testCountZeroStep() {
    +  var iter = goog.iter.count(42, 0);
    +  assertEquals(42, iter.next());
    +  assertEquals(42, iter.next());
    +  assertEquals(42, iter.next());
    +}
    +
    +function testCountFloat() {
    +  var iter = goog.iter.count(1.5, 0.5);
    +  var values = goog.iter.limit(iter, 5);
    +  assertArrayEquals([1.5, 2.0, 2.5, 3.0, 3.5], goog.iter.toArray(values));
    +}
    +
    +function testRepeat() {
    +  var obj = {foo: 'bar'};
    +  var iter = goog.iter.repeat(obj);
    +  assertEquals(obj, iter.next());
    +  assertEquals(obj, iter.next());
    +  assertEquals(obj, iter.next());
    +}
    +
    +function testAccumulateArray() {
    +  var iter = goog.iter.accumulate([1, 2, 3, 4, 5]);
    +  assertArrayEquals([1, 3, 6, 10, 15], goog.iter.toArray(iter));
    +}
    +
    +function testAccumulateIterator() {
    +  var iter = goog.iter.accumulate(goog.iter.range(1, 6));
    +  assertArrayEquals([1, 3, 6, 10, 15], goog.iter.toArray(iter));
    +}
    +
    +function testAccumulateFloat() {
    +  var iter = goog.iter.accumulate([1.0, 2.5, 0.5, 1.5, 0.5]);
    +  assertArrayEquals([1.0, 3.5, 4.0, 5.5, 6.0], goog.iter.toArray(iter));
    +}
    +
    +function testZipArrays() {
    +  var iter = goog.iter.zip([1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +  assertArrayEquals([1, 4, 7], iter.next());
    +  assertArrayEquals([2, 5, 8], iter.next());
    +  assertArrayEquals([3, 6, 9], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipSingleArg() {
    +  var iter = goog.iter.zip([1, 2, 3]);
    +  assertArrayEquals([1], iter.next());
    +  assertArrayEquals([2], iter.next());
    +  assertArrayEquals([3], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipUnevenArgs() {
    +  var iter = goog.iter.zip([1, 2, 3], [4, 5], [7]);
    +  assertArrayEquals([1, 4, 7], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipNoArgs() {
    +  var iter = goog.iter.zip();
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipIterators() {
    +  var iter = goog.iter.zip(goog.iter.count(), goog.iter.repeat('foo'));
    +  assertArrayEquals([0, 'foo'], iter.next());
    +  assertArrayEquals([1, 'foo'], iter.next());
    +  assertArrayEquals([2, 'foo'], iter.next());
    +  assertArrayEquals([3, 'foo'], iter.next());
    +}
    +
    +function testZipLongestArrays() {
    +  var iter = goog.iter.zipLongest('-', 'ABCD'.split(''), 'xy'.split(''));
    +  assertArrayEquals(['A', 'x'], iter.next());
    +  assertArrayEquals(['B', 'y'], iter.next());
    +  assertArrayEquals(['C', '-'], iter.next());
    +  assertArrayEquals(['D', '-'], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipLongestSingleArg() {
    +  var iter = goog.iter.zipLongest('-', 'ABCD'.split(''));
    +  assertArrayEquals(['A'], iter.next());
    +  assertArrayEquals(['B'], iter.next());
    +  assertArrayEquals(['C'], iter.next());
    +  assertArrayEquals(['D'], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testZipLongestNoArgs() {
    +  var iter = goog.iter.zipLongest();
    +  assertArrayEquals([], goog.iter.toArray(iter));
    +  var iter = goog.iter.zipLongest('fill');
    +  assertArrayEquals([], goog.iter.toArray(iter));
    +}
    +
    +function testZipLongestIterators() {
    +  var iter = goog.iter.zipLongest(null, goog.iter.range(3), goog.iter.range(5));
    +  assertArrayEquals([0, 0], iter.next());
    +  assertArrayEquals([1, 1], iter.next());
    +  assertArrayEquals([2, 2], iter.next());
    +  assertArrayEquals([null, 3], iter.next());
    +  assertArrayEquals([null, 4], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testCompressArray() {
    +  var iter = goog.iter.compress('ABCDEF'.split(''), [1, 0, 1, 0, 1, 1]);
    +  assertEquals('ACEF', goog.iter.join(iter, ''));
    +}
    +
    +function testCompressUnevenArgs() {
    +  var iter = goog.iter.compress('ABCDEF'.split(''), [false, true, true]);
    +  assertEquals('BC', goog.iter.join(iter, ''));
    +}
    +
    +function testCompressIterators() {
    +  var iter = goog.iter.compress(goog.iter.range(10), goog.iter.cycle([0, 1]));
    +  assertArrayEquals([1, 3, 5, 7, 9], goog.iter.toArray(iter));
    +}
    +
    +function testGroupByNoKeyFunc() {
    +  var iter = goog.iter.groupBy('AAABBBBCDD'.split(''));
    +  assertArrayEquals(['A', ['A', 'A', 'A']], iter.next());
    +  assertArrayEquals(['B', ['B', 'B', 'B', 'B']], iter.next());
    +  assertArrayEquals(['C', ['C']], iter.next());
    +  assertArrayEquals(['D', ['D', 'D']], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testGroupByKeyFunc() {
    +  var keyFunc = function(x) { return x.toLowerCase(); };
    +  var iter = goog.iter.groupBy('AaAABBbbBCccddDD'.split(''), keyFunc);
    +  assertArrayEquals(['a', ['A', 'a', 'A', 'A']], iter.next());
    +  assertArrayEquals(['b', ['B', 'B', 'b', 'b', 'B']], iter.next());
    +  assertArrayEquals(['c', ['C', 'c', 'c']], iter.next());
    +  assertArrayEquals(['d', ['d', 'd', 'D', 'D']], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testStarMap() {
    +  var iter = goog.iter.starMap([[2, 5], [3, 2], [10, 3]], Math.pow);
    +  assertEquals(32, iter.next());
    +  assertEquals(9, iter.next());
    +  assertEquals(1000, iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testStarMapExtraArgs() {
    +  var func = function(string, radix, undef, iterator) {
    +    assertEquals('undef should be undefined', 'undefined', typeof undef);
    +    assertTrue(iterator instanceof goog.iter.Iterator);
    +    return parseInt(string, radix);
    +  };
    +  var iter = goog.iter.starMap([['42', 10], ['0xFF', 16], ['101', 2]], func);
    +  assertEquals(42, iter.next());
    +  assertEquals(255, iter.next());
    +  assertEquals(5, iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testTeeArray() {
    +  var iters = goog.iter.tee('ABC'.split(''));
    +  assertEquals(2, iters.length);
    +  var it0 = iters[0], it1 = iters[1];
    +  assertEquals('A', it0.next());
    +  assertEquals('A', it1.next());
    +  assertEquals('B', it0.next());
    +  assertEquals('B', it1.next());
    +  assertEquals('C', it0.next());
    +  assertEquals('C', it1.next());
    +  var ex = assertThrows(function() { it0.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +  ex = assertThrows(function() { it1.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testTeeIterator() {
    +  var iters = goog.iter.tee(goog.iter.count(), 3);
    +  assertEquals(3, iters.length);
    +  var it0 = iters[0], it1 = iters[1], it2 = iters[2];
    +  assertEquals(0, it0.next());
    +  assertEquals(1, it0.next());
    +  assertEquals(0, it1.next());
    +  assertEquals(1, it1.next());
    +  assertEquals(2, it1.next());
    +  assertEquals(2, it0.next());
    +  assertEquals(0, it2.next());
    +  assertEquals(1, it2.next());
    +  assertEquals(2, it2.next());
    +  assertEquals(3, it0.next());
    +  assertEquals(3, it1.next());
    +  assertEquals(3, it2.next());
    +}
    +
    +function testEnumerateNoStart() {
    +  var iter = goog.iter.enumerate('ABC'.split(''));
    +  assertArrayEquals([0, 'A'], iter.next());
    +  assertArrayEquals([1, 'B'], iter.next());
    +  assertArrayEquals([2, 'C'], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testEnumerateStart() {
    +  var iter = goog.iter.enumerate('DEF'.split(''), 3);
    +  assertArrayEquals([3, 'D'], iter.next());
    +  assertArrayEquals([4, 'E'], iter.next());
    +  assertArrayEquals([5, 'F'], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testLimitLess() {
    +  var iter = goog.iter.limit('ABCDEFG'.split(''), 3);
    +  assertEquals('ABC', goog.iter.join(iter, ''));
    +}
    +
    +function testLimitGreater() {
    +  var iter = goog.iter.limit('ABCDEFG'.split(''), 10);
    +  assertEquals('ABCDEFG', goog.iter.join(iter, ''));
    +}
    +
    +function testConsumeLess() {
    +  var iter = goog.iter.consume('ABCDEFG'.split(''), 3);
    +  assertEquals('DEFG', goog.iter.join(iter, ''));
    +}
    +
    +function testConsumeGreater() {
    +  var iter = goog.iter.consume('ABCDEFG'.split(''), 10);
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testSliceStart() {
    +  var iter = goog.iter.slice('ABCDEFG'.split(''), 2);
    +  assertEquals('CDEFG', goog.iter.join(iter, ''));
    +}
    +
    +function testSliceStop() {
    +  var iter = goog.iter.slice('ABCDEFG'.split(''), 2, 4);
    +  assertEquals('CD', goog.iter.join(iter, ''));
    +}
    +
    +function testSliceStartStopEqual() {
    +  var iter = goog.iter.slice('ABCDEFG'.split(''), 1, 1);
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testSliceIterator() {
    +  var iter = goog.iter.slice(goog.iter.count(20), 0, 5);
    +  assertArrayEquals([20, 21, 22, 23, 24], goog.iter.toArray(iter));
    +}
    +
    +function testSliceStartGreater() {
    +  var iter = goog.iter.slice('ABCDEFG'.split(''), 10);
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testPermutationsNoLength() {
    +  var iter = goog.iter.permutations(goog.iter.range(3));
    +  assertArrayEquals([0, 1, 2], iter.next());
    +  assertArrayEquals([0, 2, 1], iter.next());
    +  assertArrayEquals([1, 0, 2], iter.next());
    +  assertArrayEquals([1, 2, 0], iter.next());
    +  assertArrayEquals([2, 0, 1], iter.next());
    +  assertArrayEquals([2, 1, 0], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testPermutationsLength() {
    +  var iter = goog.iter.permutations('ABC'.split(''), 2);
    +  assertArrayEquals(['A', 'B'], iter.next());
    +  assertArrayEquals(['A', 'C'], iter.next());
    +  assertArrayEquals(['B', 'A'], iter.next());
    +  assertArrayEquals(['B', 'C'], iter.next());
    +  assertArrayEquals(['C', 'A'], iter.next());
    +  assertArrayEquals(['C', 'B'], iter.next());
    +}
    +
    +function testCombinations() {
    +  var iter = goog.iter.combinations(goog.iter.range(4), 3);
    +  assertArrayEquals([0, 1, 2], iter.next());
    +  assertArrayEquals([0, 1, 3], iter.next());
    +  assertArrayEquals([0, 2, 3], iter.next());
    +  assertArrayEquals([1, 2, 3], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    +
    +function testCombinationsWithReplacement() {
    +  var iter = goog.iter.combinationsWithReplacement('ABC'.split(''), 2);
    +  assertArrayEquals(['A', 'A'], iter.next());
    +  assertArrayEquals(['A', 'B'], iter.next());
    +  assertArrayEquals(['A', 'C'], iter.next());
    +  assertArrayEquals(['B', 'B'], iter.next());
    +  assertArrayEquals(['B', 'C'], iter.next());
    +  assertArrayEquals(['C', 'C'], iter.next());
    +  var ex = assertThrows(function() { iter.next() });
    +  assertEquals(goog.iter.StopIteration, ex);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/json/evaljsonprocessor.js b/mapbox-gl/closure-library/closure/goog/json/evaljsonprocessor.js
    new file mode 100644
    index 0000000000..0144bf1ade
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/evaljsonprocessor.js
    @@ -0,0 +1,67 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Defines a class for parsing JSON using eval.
    + */
    +
    +goog.provide('goog.json.EvalJsonProcessor');
    +
    +goog.require('goog.json');
    +goog.require('goog.json.Processor');
    +goog.require('goog.json.Serializer');
    +
    +
    +
    +/**
    + * A class that parses and stringifies JSON using eval (as implemented in
    + * goog.json).
    + * Adapts {@code goog.json} to the {@code goog.json.Processor} interface.
    + *
    + * @param {?goog.json.Replacer=} opt_replacer An optional replacer to use during
    + *     serialization.
    + * @param {?boolean=} opt_useUnsafeParsing Whether to use goog.json.unsafeParse
    + *     for parsing. Safe parsing is very slow on large strings. On the other
    + *     hand, unsafe parsing uses eval() without checking whether the string is
    + *     valid, so it should only be used if you trust the source of the string.
    + * @constructor
    + * @implements {goog.json.Processor}
    + * @final
    + */
    +goog.json.EvalJsonProcessor = function(opt_replacer, opt_useUnsafeParsing) {
    +  /**
    +   * @type {goog.json.Serializer}
    +   * @private
    +   */
    +  this.serializer_ = new goog.json.Serializer(opt_replacer);
    +
    +  /**
    +   * @type {function(string): *}
    +   * @private
    +   */
    +  this.parser_ = opt_useUnsafeParsing ? goog.json.unsafeParse : goog.json.parse;
    +};
    +
    +
    +/** @override */
    +goog.json.EvalJsonProcessor.prototype.stringify = function(object) {
    +  return this.serializer_.serialize(object);
    +};
    +
    +
    +/** @override */
    +goog.json.EvalJsonProcessor.prototype.parse = function(s) {
    +  return this.parser_(s);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybrid.js b/mapbox-gl/closure-library/closure/goog/json/hybrid.js
    new file mode 100644
    index 0000000000..ac58bf420a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybrid.js
    @@ -0,0 +1,103 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Utility to attempt native JSON processing, falling back to
    + *     goog.json if not available.
    + *
    + *     This is intended as a drop-in for current users of goog.json who want
    + *     to take advantage of native JSON if present.
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.json.hybrid');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.json');
    +
    +
    +/**
    + * Attempts to serialize the JSON string natively, falling back to
    + * {@code goog.json.serialize} if unsuccessful.
    + * @param {!Object} obj JavaScript object to serialize to JSON.
    + * @return {string} Resulting JSON string.
    + */
    +goog.json.hybrid.stringify = goog.json.USE_NATIVE_JSON ?
    +    goog.global['JSON']['stringify'] :
    +    function(obj) {
    +      if (goog.global.JSON) {
    +        try {
    +          return goog.global.JSON.stringify(obj);
    +        } catch (e) {
    +          // Native serialization failed.  Fall through to retry with
    +          // goog.json.serialize.
    +        }
    +      }
    +
    +      return goog.json.serialize(obj);
    +    };
    +
    +
    +/**
    + * Attempts to parse the JSON string natively, falling back to
    + * the supplied {@code fallbackParser} if unsuccessful.
    + * @param {string} jsonString JSON string to parse.
    + * @param {function(string):Object} fallbackParser Fallback JSON parser used
    + *     if native
    + * @return {!Object} Resulting JSON object.
    + * @private
    + */
    +goog.json.hybrid.parse_ = function(jsonString, fallbackParser) {
    +  if (goog.global.JSON) {
    +    try {
    +      var obj = goog.global.JSON.parse(jsonString);
    +      goog.asserts.assertObject(obj);
    +      return obj;
    +    } catch (e) {
    +      // Native parse failed.  Fall through to retry with goog.json.unsafeParse.
    +    }
    +  }
    +
    +  var obj = fallbackParser(jsonString);
    +  goog.asserts.assert(obj);
    +  return obj;
    +};
    +
    +
    +/**
    + * Attempts to parse the JSON string natively, falling back to
    + * {@code goog.json.parse} if unsuccessful.
    + * @param {string} jsonString JSON string to parse.
    + * @return {!Object} Resulting JSON object.
    + */
    +goog.json.hybrid.parse = goog.json.USE_NATIVE_JSON ?
    +    goog.global['JSON']['parse'] :
    +    function(jsonString) {
    +      return goog.json.hybrid.parse_(jsonString, goog.json.parse);
    +    };
    +
    +
    +/**
    + * Attempts to parse the JSON string natively, falling back to
    + * {@code goog.json.unsafeParse} if unsuccessful.
    + * @param {string} jsonString JSON string to parse.
    + * @return {!Object} Resulting JSON object.
    + */
    +goog.json.hybrid.unsafeParse = goog.json.USE_NATIVE_JSON ?
    +    goog.global['JSON']['parse'] :
    +    function(jsonString) {
    +      return goog.json.hybrid.parse_(jsonString, goog.json.unsafeParse);
    +    };
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybrid_test.html b/mapbox-gl/closure-library/closure/goog/json/hybrid_test.html
    new file mode 100644
    index 0000000000..02ec8b40ff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybrid_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.json.hybridTest</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.json.hybridTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybrid_test.js b/mapbox-gl/closure-library/closure/goog/json/hybrid_test.js
    new file mode 100644
    index 0000000000..bd5ae03b34
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybrid_test.js
    @@ -0,0 +1,157 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.json.hybrid.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.json.hybridTest');
    +
    +goog.require('goog.json');
    +goog.require('goog.json.hybrid');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +goog.setTestOnly('goog.json.hybridTest');
    +
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +
    +var jsonParse;
    +var jsonStringify;
    +var googJsonParse;
    +var googJsonUnsafeParse;
    +var googJsonSerialize;
    +
    +function isIe7() {
    +  return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8');
    +}
    +
    +function setUp() {
    +  googJsonParse = goog.testing.recordFunction(goog.json.parse);
    +  googJsonUnsafeParse = goog.testing.recordFunction(goog.json.unsafeParse);
    +  googJsonSerialize = goog.testing.recordFunction(goog.json.serialize);
    +
    +  propertyReplacer.set(goog.json, 'parse', googJsonParse);
    +  propertyReplacer.set(goog.json, 'unsafeParse', googJsonUnsafeParse);
    +  propertyReplacer.set(goog.json, 'serialize', googJsonSerialize);
    +
    +  jsonParse = goog.testing.recordFunction(
    +      goog.global.JSON && goog.global.JSON.parse);
    +  jsonStringify = goog.testing.recordFunction(
    +      goog.global.JSON && goog.global.JSON.stringify);
    +
    +  if (goog.global.JSON) {
    +    propertyReplacer.set(goog.global.JSON, 'parse', jsonParse);
    +    propertyReplacer.set(goog.global.JSON, 'stringify', jsonStringify);
    +  }
    +}
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +}
    +
    +function parseJson() {
    +  var obj = goog.json.hybrid.parse('{"a": 2}');
    +  assertObjectEquals({'a': 2}, obj);
    +}
    +
    +function unsafeParseJson() {
    +  var obj = goog.json.hybrid.unsafeParse('{"a": 2}');
    +  assertObjectEquals({'a': 2}, obj);
    +}
    +
    +function serializeJson() {
    +  var str = goog.json.hybrid.stringify({b: 2});
    +  assertEquals('{"b":2}', str);
    +}
    +
    +function testUnsafeParseNativeJsonPresent() {
    +  // No native JSON in IE7
    +  if (isIe7()) {
    +    return;
    +  }
    +
    +  unsafeParseJson();
    +  assertEquals(1, jsonParse.getCallCount());
    +  assertEquals(0, googJsonParse.getCallCount());
    +  assertEquals(0, googJsonUnsafeParse.getCallCount());
    +}
    +
    +function testParseNativeJsonPresent() {
    +  // No native JSON in IE7
    +  if (isIe7()) {
    +    return;
    +  }
    +
    +  unsafeParseJson();
    +  assertEquals(1, jsonParse.getCallCount());
    +  assertEquals(0, googJsonParse.getCallCount());
    +  assertEquals(0, googJsonUnsafeParse.getCallCount());
    +}
    +
    +function testStringifyNativeJsonPresent() {
    +  // No native JSON in IE7
    +  if (isIe7()) {
    +    return;
    +  }
    +
    +  serializeJson();
    +
    +  assertEquals(1, jsonStringify.getCallCount());
    +  assertEquals(0, googJsonSerialize.getCallCount());
    +}
    +
    +function testParseNativeJsonAbsent() {
    +  propertyReplacer.set(goog.global, 'JSON', null);
    +
    +  parseJson();
    +
    +  assertEquals(0, jsonParse.getCallCount());
    +  assertEquals(0, jsonStringify.getCallCount());
    +  assertEquals(1, googJsonParse.getCallCount());
    +  assertEquals(0, googJsonUnsafeParse.getCallCount());
    +}
    +
    +function testStringifyNativeJsonAbsent() {
    +  propertyReplacer.set(goog.global, 'JSON', null);
    +
    +  serializeJson();
    +
    +  assertEquals(0, jsonStringify.getCallCount());
    +  assertEquals(1, googJsonSerialize.getCallCount());
    +}
    +
    +function testParseCurrentBrowserParse() {
    +  parseJson();
    +  assertEquals(isIe7() ? 0 : 1, jsonParse.getCallCount());
    +  assertEquals(isIe7() ? 1 : 0, googJsonParse.getCallCount());
    +  assertEquals(0, googJsonUnsafeParse.getCallCount());
    +}
    +
    +function testParseCurrentBrowserUnsafeParse() {
    +  unsafeParseJson();
    +  assertEquals(isIe7() ? 0 : 1, jsonParse.getCallCount());
    +  assertEquals(0, googJsonParse.getCallCount());
    +  assertEquals(isIe7() ? 1 : 0, googJsonUnsafeParse.getCallCount());
    +}
    +
    +function testParseCurrentBrowserStringify() {
    +  serializeJson();
    +  assertEquals(isIe7() ? 0 : 1, jsonStringify.getCallCount());
    +  assertEquals(isIe7() ? 1 : 0, googJsonSerialize.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor.js b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor.js
    new file mode 100644
    index 0000000000..f00168dfe7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor.js
    @@ -0,0 +1,47 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is dihstributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview A class that attempts parse/serialize JSON using native JSON,
    + *     falling back to goog.json if necessary.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.json.HybridJsonProcessor');
    +
    +goog.require('goog.json.Processor');
    +goog.require('goog.json.hybrid');
    +
    +
    +
    +/**
    + * Processor form of goog.json.hybrid, which attempts to parse/serialize
    + * JSON using native JSON methods, falling back to goog.json if not
    + * available.
    + * @constructor
    + * @implements {goog.json.Processor}
    + * @final
    + */
    +goog.json.HybridJsonProcessor = function() {};
    +
    +
    +/** @override */
    +goog.json.HybridJsonProcessor.prototype.stringify =
    +    /** @type {function (*): string} */ (goog.json.hybrid.stringify);
    +
    +
    +/** @override */
    +goog.json.HybridJsonProcessor.prototype.parse =
    +    /** @type {function (*): !Object} */ (goog.json.hybrid.parse);
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.html b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.html
    new file mode 100644
    index 0000000000..fc99c5d6bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.json.HybridJsonProcessor</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.json.HybridJsonProcessorTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.js b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.js
    new file mode 100644
    index 0000000000..024f5b50f4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/hybridjsonprocessor_test.js
    @@ -0,0 +1,33 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.json.hybrid.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.json.HybridJsonProcessorTest');
    +
    +goog.require('goog.json.HybridJsonProcessor');
    +goog.require('goog.json.hybrid');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.json.HybridJsonProcessorTest');
    +
    +
    +function testCorrectFunctions() {
    +  var processor = new goog.json.HybridJsonProcessor();
    +  assertEquals(goog.json.hybrid.stringify, processor.stringify);
    +  assertEquals(goog.json.hybrid.parse, processor.parse);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/json/json.js b/mapbox-gl/closure-library/closure/goog/json/json.js
    new file mode 100644
    index 0000000000..77dccc6f8c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/json.js
    @@ -0,0 +1,369 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview JSON utility functions.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.json');
    +goog.provide('goog.json.Replacer');
    +goog.provide('goog.json.Reviver');
    +goog.provide('goog.json.Serializer');
    +
    +
    +/**
    + * @define {boolean} If true, use the native JSON parsing API.
    + * NOTE(ruilopes): EXPERIMENTAL, handle with care.  Setting this to true might
    + * break your code.  The default {@code goog.json.parse} implementation is able
    + * to handle invalid JSON, such as JSPB.
    + */
    +goog.define('goog.json.USE_NATIVE_JSON', false);
    +
    +
    +/**
    + * Tests if a string is an invalid JSON string. This only ensures that we are
    + * not using any invalid characters
    + * @param {string} s The string to test.
    + * @return {boolean} True if the input is a valid JSON string.
    + */
    +goog.json.isValid = function(s) {
    +  // All empty whitespace is not valid.
    +  if (/^\s*$/.test(s)) {
    +    return false;
    +  }
    +
    +  // This is taken from http://www.json.org/json2.js which is released to the
    +  // public domain.
    +  // Changes: We dissallow \u2028 Line separator and \u2029 Paragraph separator
    +  // inside strings.  We also treat \u2028 and \u2029 as whitespace which they
    +  // are in the RFC but IE and Safari does not match \s to these so we need to
    +  // include them in the reg exps in all places where whitespace is allowed.
    +  // We allowed \x7f inside strings because some tools don't escape it,
    +  // e.g. http://www.json.org/java/org/json/JSONObject.java
    +
    +  // Parsing happens in three stages. In the first stage, we run the text
    +  // against regular expressions that look for non-JSON patterns. We are
    +  // especially concerned with '()' and 'new' because they can cause invocation,
    +  // and '=' because it can cause mutation. But just to be safe, we want to
    +  // reject all unexpected forms.
    +
    +  // We split the first stage into 4 regexp operations in order to work around
    +  // crippling inefficiencies in IE's and Safari's regexp engines. First we
    +  // replace all backslash pairs with '@' (a non-JSON character). Second, we
    +  // replace all simple value tokens with ']' characters. Third, we delete all
    +  // open brackets that follow a colon or comma or that begin the text. Finally,
    +  // we look to see that the remaining characters are only whitespace or ']' or
    +  // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
    +
    +  // Don't make these static since they have the global flag.
    +  var backslashesRe = /\\["\\\/bfnrtu]/g;
    +  var simpleValuesRe =
    +      /"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    +  var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g;
    +  var remainderRe = /^[\],:{}\s\u2028\u2029]*$/;
    +
    +  return remainderRe.test(s.replace(backslashesRe, '@').
    +      replace(simpleValuesRe, ']').
    +      replace(openBracketsRe, ''));
    +};
    +
    +
    +/**
    + * Parses a JSON string and returns the result. This throws an exception if
    + * the string is an invalid JSON string.
    + *
    + * Note that this is very slow on large strings. If you trust the source of
    + * the string then you should use unsafeParse instead.
    + *
    + * @param {*} s The JSON string to parse.
    + * @throws Error if s is invalid JSON.
    + * @return {Object} The object generated from the JSON string, or null.
    + */
    +goog.json.parse = goog.json.USE_NATIVE_JSON ?
    +    /** @type {function(*):Object} */ (goog.global['JSON']['parse']) :
    +    function(s) {
    +      var o = String(s);
    +      if (goog.json.isValid(o)) {
    +        /** @preserveTry */
    +        try {
    +          return /** @type {Object} */ (eval('(' + o + ')'));
    +        } catch (ex) {
    +        }
    +      }
    +      throw Error('Invalid JSON string: ' + o);
    +    };
    +
    +
    +/**
    + * Parses a JSON string and returns the result. This uses eval so it is open
    + * to security issues and it should only be used if you trust the source.
    + *
    + * @param {string} s The JSON string to parse.
    + * @return {Object} The object generated from the JSON string.
    + */
    +goog.json.unsafeParse = goog.json.USE_NATIVE_JSON ?
    +    /** @type {function(string):Object} */ (goog.global['JSON']['parse']) :
    +    function(s) {
    +      return /** @type {Object} */ (eval('(' + s + ')'));
    +    };
    +
    +
    +/**
    + * JSON replacer, as defined in Section 15.12.3 of the ES5 spec.
    + * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
    + *
    + * TODO(nicksantos): Array should also be a valid replacer.
    + *
    + * @typedef {function(this:Object, string, *): *}
    + */
    +goog.json.Replacer;
    +
    +
    +/**
    + * JSON reviver, as defined in Section 15.12.2 of the ES5 spec.
    + * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
    + *
    + * @typedef {function(this:Object, string, *): *}
    + */
    +goog.json.Reviver;
    +
    +
    +/**
    + * Serializes an object or a value to a JSON string.
    + *
    + * @param {*} object The object to serialize.
    + * @param {?goog.json.Replacer=} opt_replacer A replacer function
    + *     called for each (key, value) pair that determines how the value
    + *     should be serialized. By defult, this just returns the value
    + *     and allows default serialization to kick in.
    + * @throws Error if there are loops in the object graph.
    + * @return {string} A JSON string representation of the input.
    + */
    +goog.json.serialize = goog.json.USE_NATIVE_JSON ?
    +    /** @type {function(*, ?goog.json.Replacer=):string} */
    +    (goog.global['JSON']['stringify']) :
    +    function(object, opt_replacer) {
    +      // NOTE(nicksantos): Currently, we never use JSON.stringify.
    +      //
    +      // The last time I evaluated this, JSON.stringify had subtle bugs and
    +      // behavior differences on all browsers, and the performance win was not
    +      // large enough to justify all the issues. This may change in the future
    +      // as browser implementations get better.
    +      //
    +      // assertSerialize in json_test contains if branches for the cases
    +      // that fail.
    +      return new goog.json.Serializer(opt_replacer).serialize(object);
    +    };
    +
    +
    +
    +/**
    + * Class that is used to serialize JSON objects to a string.
    + * @param {?goog.json.Replacer=} opt_replacer Replacer.
    + * @constructor
    + */
    +goog.json.Serializer = function(opt_replacer) {
    +  /**
    +   * @type {goog.json.Replacer|null|undefined}
    +   * @private
    +   */
    +  this.replacer_ = opt_replacer;
    +};
    +
    +
    +/**
    + * Serializes an object or a value to a JSON string.
    + *
    + * @param {*} object The object to serialize.
    + * @throws Error if there are loops in the object graph.
    + * @return {string} A JSON string representation of the input.
    + */
    +goog.json.Serializer.prototype.serialize = function(object) {
    +  var sb = [];
    +  this.serializeInternal(object, sb);
    +  return sb.join('');
    +};
    +
    +
    +/**
    + * Serializes a generic value to a JSON string
    + * @protected
    + * @param {*} object The object to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + * @throws Error if there are loops in the object graph.
    + */
    +goog.json.Serializer.prototype.serializeInternal = function(object, sb) {
    +  switch (typeof object) {
    +    case 'string':
    +      this.serializeString_(/** @type {string} */ (object), sb);
    +      break;
    +    case 'number':
    +      this.serializeNumber_(/** @type {number} */ (object), sb);
    +      break;
    +    case 'boolean':
    +      sb.push(object);
    +      break;
    +    case 'undefined':
    +      sb.push('null');
    +      break;
    +    case 'object':
    +      if (object == null) {
    +        sb.push('null');
    +        break;
    +      }
    +      if (goog.isArray(object)) {
    +        this.serializeArray(/** @type {!Array<?>} */ (object), sb);
    +        break;
    +      }
    +      // should we allow new String, new Number and new Boolean to be treated
    +      // as string, number and boolean? Most implementations do not and the
    +      // need is not very big
    +      this.serializeObject_(/** @type {Object} */ (object), sb);
    +      break;
    +    case 'function':
    +      // Skip functions.
    +      // TODO(user) Should we return something here?
    +      break;
    +    default:
    +      throw Error('Unknown type: ' + typeof object);
    +  }
    +};
    +
    +
    +/**
    + * Character mappings used internally for goog.string.quote
    + * @private
    + * @type {!Object}
    + */
    +goog.json.Serializer.charToJsonCharCache_ = {
    +  '\"': '\\"',
    +  '\\': '\\\\',
    +  '/': '\\/',
    +  '\b': '\\b',
    +  '\f': '\\f',
    +  '\n': '\\n',
    +  '\r': '\\r',
    +  '\t': '\\t',
    +
    +  '\x0B': '\\u000b' // '\v' is not supported in JScript
    +};
    +
    +
    +/**
    + * Regular expression used to match characters that need to be replaced.
    + * The S60 browser has a bug where unicode characters are not matched by
    + * regular expressions. The condition below detects such behaviour and
    + * adjusts the regular expression accordingly.
    + * @private
    + * @type {!RegExp}
    + */
    +goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
    +    /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g;
    +
    +
    +/**
    + * Serializes a string to a JSON string
    + * @private
    + * @param {string} s The string to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + */
    +goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
    +  // The official JSON implementation does not work with international
    +  // characters.
    +  sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
    +    // caching the result improves performance by a factor 2-3
    +    if (c in goog.json.Serializer.charToJsonCharCache_) {
    +      return goog.json.Serializer.charToJsonCharCache_[c];
    +    }
    +
    +    var cc = c.charCodeAt(0);
    +    var rv = '\\u';
    +    if (cc < 16) {
    +      rv += '000';
    +    } else if (cc < 256) {
    +      rv += '00';
    +    } else if (cc < 4096) { // \u1000
    +      rv += '0';
    +    }
    +    return goog.json.Serializer.charToJsonCharCache_[c] = rv + cc.toString(16);
    +  }), '"');
    +};
    +
    +
    +/**
    + * Serializes a number to a JSON string
    + * @private
    + * @param {number} n The number to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + */
    +goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
    +  sb.push(isFinite(n) && !isNaN(n) ? n : 'null');
    +};
    +
    +
    +/**
    + * Serializes an array to a JSON string
    + * @param {Array<string>} arr The array to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + * @protected
    + */
    +goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
    +  var l = arr.length;
    +  sb.push('[');
    +  var sep = '';
    +  for (var i = 0; i < l; i++) {
    +    sb.push(sep);
    +
    +    var value = arr[i];
    +    this.serializeInternal(
    +        this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
    +        sb);
    +
    +    sep = ',';
    +  }
    +  sb.push(']');
    +};
    +
    +
    +/**
    + * Serializes an object to a JSON string
    + * @private
    + * @param {Object} obj The object to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + */
    +goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
    +  sb.push('{');
    +  var sep = '';
    +  for (var key in obj) {
    +    if (Object.prototype.hasOwnProperty.call(obj, key)) {
    +      var value = obj[key];
    +      // Skip functions.
    +      // TODO(ptucker) Should we return something for function properties?
    +      if (typeof value != 'function') {
    +        sb.push(sep);
    +        this.serializeString_(key, sb);
    +        sb.push(':');
    +
    +        this.serializeInternal(
    +            this.replacer_ ? this.replacer_.call(obj, key, value) : value,
    +            sb);
    +
    +        sep = ',';
    +      }
    +    }
    +  }
    +  sb.push('}');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/json/json_perf.html b/mapbox-gl/closure-library/closure/goog/json/json_perf.html
    new file mode 100644
    index 0000000000..a7c2ee1c27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/json_perf.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.json vs JSON</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css"/>
    +  <script src="../base.js"></script>
    +</head>
    +<body>
    +<h1>goog.json and JSON Performance Tests</h1>
    +<p>
    +<strong>User-agent:</strong>
    +<script>document.write(navigator.userAgent);</script>
    +</p>
    +<div id="perfTable"></div>
    +<hr>
    +<script>
    +goog.require('goog.jsonPerf');
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/json/json_perf.js b/mapbox-gl/closure-library/closure/goog/json/json_perf.js
    new file mode 100644
    index 0000000000..ac73225298
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/json_perf.js
    @@ -0,0 +1,112 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview JSON performance tests.
    + */
    +
    +goog.provide('goog.jsonPerf');
    +
    +goog.require('goog.dom');
    +goog.require('goog.json');
    +goog.require('goog.math');
    +goog.require('goog.string');
    +goog.require('goog.testing.PerformanceTable');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.jsonPerf');
    +
    +var table = new goog.testing.PerformanceTable(
    +    goog.dom.getElement('perfTable'));
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testSerialize() {
    +  var obj = populateObject({}, 50, 4);
    +
    +  table.run(function() {
    +    var s = JSON.stringify(obj);
    +  }, 'Stringify using JSON.stringify');
    +
    +  table.run(function() {
    +    var s = goog.json.serialize(obj);
    +  }, 'Stringify using goog.json.serialize');
    +}
    +
    +function testParse() {
    +  var obj = populateObject({}, 50, 4);
    +  var s = JSON.stringify(obj);
    +
    +  table.run(function() {
    +    var o = JSON.parse(s);
    +  }, 'Parse using JSON.parse');
    +
    +  table.run(function() {
    +    var o = goog.json.parse(s);
    +  }, 'Parse using goog.json.parse');
    +
    +  table.run(function() {
    +    var o = goog.json.unsafeParse(s);
    +  }, 'Parse using goog.json.unsafeParse');
    +}
    +
    +
    +/**
    + * @param {!Object} obj The object to add properties to.
    + * @param {number} numProperties The number of properties to add.
    + * @param {number} depth The depth at which to recursively add properties.
    + * @return {!Object} The object given in obj (for convenience).
    + */
    +function populateObject(obj, numProperties, depth) {
    +  if (depth == 0) {
    +    return randomLiteral();
    +  }
    +
    +  // Make an object with a mix of strings, numbers, arrays, objects, booleans
    +  // nulls as children.
    +  for (var i = 0; i < numProperties; i++) {
    +    var bucket = goog.math.randomInt(3);
    +    switch (bucket) {
    +      case 0:
    +        obj[i] = randomLiteral();
    +        break;
    +      case 1:
    +        obj[i] = populateObject({}, numProperties, depth - 1);
    +        break;
    +      case 2:
    +        obj[i] = populateObject([], numProperties, depth - 1);
    +        break;
    +    }
    +  }
    +  return obj;
    +}
    +
    +
    +function randomLiteral() {
    +  var bucket = goog.math.randomInt(3);
    +  switch (bucket) {
    +    case 0:
    +      return goog.string.getRandomString();
    +    case 1:
    +      return Math.random();
    +    case 2:
    +      return Math.random() >= .5;
    +  }
    +  return null;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/json/json_test.html b/mapbox-gl/closure-library/closure/goog/json/json_test.html
    new file mode 100644
    index 0000000000..3d49c9b56d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/json_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.json</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.jsonTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/json/json_test.js b/mapbox-gl/closure-library/closure/goog/json/json_test.js
    new file mode 100644
    index 0000000000..d762a9a74e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/json_test.js
    @@ -0,0 +1,563 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.jsonTest');
    +goog.setTestOnly('goog.jsonTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.json');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function allChars(start, end, opt_allowControlCharacters) {
    +  var sb = [];
    +  for (var i = start; i < end; i++) {
    +    // unicode without the control characters 0x00 - 0x1f
    +    if (opt_allowControlCharacters || i > 0x1f) {
    +      sb.push(String.fromCharCode(i));
    +    }
    +  }
    +  return sb.join('');
    +}
    +
    +// serialization
    +
    +function testStringSerialize() {
    +  assertSerialize('""', '');
    +
    +  // unicode
    +  var str = allChars(0, 10000);
    +  eval(goog.json.serialize(str));
    +
    +  assertSerialize('"true"', 'true');
    +  assertSerialize('"false"', 'false');
    +  assertSerialize('"null"', 'null');
    +  assertSerialize('"0"', '0');
    +}
    +
    +function testNullSerialize() {
    +  assertSerialize('null', null);
    +  assertSerialize('null', undefined);
    +  assertSerialize('null', NaN);
    +
    +  assertSerialize('0', 0);
    +  assertSerialize('""', '');
    +  assertSerialize('false', false);
    +}
    +
    +function testNullPropertySerialize() {
    +  assertSerialize('{"a":null}', {'a': null});
    +  assertSerialize('{"a":null}', {'a': undefined});
    +}
    +
    +function testNumberSerialize() {
    +  assertSerialize('0', 0);
    +  assertSerialize('12345', 12345);
    +  assertSerialize('-12345', -12345);
    +
    +  assertSerialize('0.1', 0.1);
    +  // the leading zero may not be omitted
    +  assertSerialize('0.1', .1);
    +
    +  // no leading +
    +  assertSerialize('1', +1);
    +
    +  // either format is OK
    +  var s = goog.json.serialize(1e50);
    +  assertTrue('1e50',
    +      s == '1e50' || s == '1E50' ||
    +      s == '1e+50' || s == '1E+50');
    +
    +  // either format is OK
    +  s = goog.json.serialize(1e-50);
    +  assertTrue('1e50', s == '1e-50' || s == '1E-50');
    +
    +  // These numbers cannot be represented in JSON
    +  assertSerialize('null', NaN);
    +  assertSerialize('null', Infinity);
    +  assertSerialize('null', -Infinity);
    +}
    +
    +function testBooleanSerialize() {
    +  assertSerialize('true', true);
    +  assertSerialize('"true"', 'true');
    +
    +  assertSerialize('false', false);
    +  assertSerialize('"false"', 'false');
    +}
    +
    +function testArraySerialize() {
    +  assertSerialize('[]', []);
    +  assertSerialize('[1]', [1]);
    +  assertSerialize('[1,2]', [1, 2]);
    +  assertSerialize('[1,2,3]', [1, 2, 3]);
    +  assertSerialize('[[]]', [[]]);
    +
    +  assertNotEquals('{length:0}', goog.json.serialize({length: 0}), '[]');
    +}
    +
    +function testObjectSerialize_emptyObject() {
    +  assertSerialize('{}', {});
    +}
    +
    +function testObjectSerialize_oneItem() {
    +  assertSerialize('{"a":"b"}', {a: 'b'});
    +}
    +
    +function testObjectSerialize_twoItems() {
    +  assertEquals('{"a":"b","c":"d"}',
    +               goog.json.serialize({a: 'b', c: 'd'}),
    +               '{"a":"b","c":"d"}');
    +}
    +
    +function testObjectSerialize_whitespace() {
    +  assertSerialize('{" ":" "}', {' ': ' '});
    +}
    +
    +function testSerializeSkipFunction() {
    +  var object = {
    +    s: 'string value',
    +    b: true,
    +    i: 100,
    +    f: function() { var x = 'x'; }
    +  };
    +  assertSerialize('', object.f);
    +  assertSerialize('{"s":"string value","b":true,"i":100}', object);
    +}
    +
    +function testObjectSerialize_array() {
    +  assertNotEquals('[0,1]', goog.json.serialize([0, 1]), '{"0":"0","1":"1"}');
    +}
    +
    +function testObjectSerialize_recursion() {
    +  if (goog.userAgent.WEBKIT) {
    +    return; // this makes safari 4 crash.
    +  }
    +
    +  var anObject = {};
    +  anObject.thisObject = anObject;
    +  assertThrows('expected recursion exception', function() {
    +    goog.json.serialize(anObject);
    +  });
    +}
    +
    +function testObjectSerializeWithHasOwnProperty() {
    +  var object = {'hasOwnProperty': null};
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {
    +    assertEquals('{}', goog.json.serialize(object));
    +  } else {
    +    assertEquals('{"hasOwnProperty":null}', goog.json.serialize(object));
    +  }
    +}
    +
    +// parsing
    +
    +function testStringParse() {
    +
    +  assertEquals('Empty string', goog.json.parse('""'), '');
    +  assertEquals('whitespace string', goog.json.parse('" "'), ' ');
    +
    +  // unicode without the control characters 0x00 - 0x1f, 0x7f - 0x9f
    +  var str = allChars(0, 1000);
    +  var jsonString = goog.json.serialize(str);
    +  var a = eval(jsonString);
    +  assertEquals('unicode string', goog.json.parse(jsonString), a);
    +
    +  assertEquals('true as a string', goog.json.parse('"true"'), 'true');
    +  assertEquals('false as a string', goog.json.parse('"false"'), 'false');
    +  assertEquals('null as a string', goog.json.parse('"null"'), 'null');
    +  assertEquals('number as a string', goog.json.parse('"0"'), '0');
    +}
    +
    +function testStringUnsafeParse() {
    +
    +  assertEquals('Empty string', goog.json.unsafeParse('""'), '');
    +  assertEquals('whitespace string', goog.json.unsafeParse('" "'), ' ');
    +
    +  // unicode
    +  var str = allChars(0, 1000);
    +  var jsonString = goog.json.serialize(str);
    +  var a = eval(jsonString);
    +  assertEquals('unicode string', goog.json.unsafeParse(jsonString), a);
    +
    +  assertEquals('true as a string', goog.json.unsafeParse('"true"'), 'true');
    +  assertEquals('false as a string', goog.json.unsafeParse('"false"'), 'false');
    +  assertEquals('null as a string', goog.json.unsafeParse('"null"'), 'null');
    +  assertEquals('number as a string', goog.json.unsafeParse('"0"'), '0');
    +}
    +
    +function testNullParse() {
    +  assertEquals('null', goog.json.parse(null), null);
    +  assertEquals('null', goog.json.parse('null'), null);
    +
    +  assertNotEquals('0', goog.json.parse('0'), null);
    +  assertNotEquals('""', goog.json.parse('""'), null);
    +  assertNotEquals('false', goog.json.parse('false'), null);
    +}
    +
    +function testNullUnsafeParse() {
    +  assertEquals('null', goog.json.unsafeParse(null), null);
    +  assertEquals('null', goog.json.unsafeParse('null'), null);
    +
    +  assertNotEquals('0', goog.json.unsafeParse('0'), null);
    +  assertNotEquals('""', goog.json.unsafeParse('""'), null);
    +  assertNotEquals('false', goog.json.unsafeParse('false'), null);
    +}
    +
    +function testNumberParse() {
    +  assertEquals('0', goog.json.parse('0'), 0);
    +  assertEquals('12345', goog.json.parse('12345'), 12345);
    +  assertEquals('-12345', goog.json.parse('-12345'), -12345);
    +
    +  assertEquals('0.1', goog.json.parse('0.1'), 0.1);
    +
    +  // either format is OK
    +  assertEquals(1e50, goog.json.parse('1e50'));
    +  assertEquals(1e50, goog.json.parse('1E50'));
    +  assertEquals(1e50, goog.json.parse('1e+50'));
    +  assertEquals(1e50, goog.json.parse('1E+50'));
    +
    +  // either format is OK
    +  assertEquals(1e-50, goog.json.parse('1e-50'));
    +  assertEquals(1e-50, goog.json.parse('1E-50'));
    +}
    +
    +function testNumberUnsafeParse() {
    +  assertEquals('0', goog.json.unsafeParse('0'), 0);
    +  assertEquals('12345', goog.json.unsafeParse('12345'), 12345);
    +  assertEquals('-12345', goog.json.unsafeParse('-12345'), -12345);
    +
    +  assertEquals('0.1', goog.json.unsafeParse('0.1'), 0.1);
    +
    +  // either format is OK
    +  assertEquals(1e50, goog.json.unsafeParse('1e50'));
    +  assertEquals(1e50, goog.json.unsafeParse('1E50'));
    +  assertEquals(1e50, goog.json.unsafeParse('1e+50'));
    +  assertEquals(1e50, goog.json.unsafeParse('1E+50'));
    +
    +  // either format is OK
    +  assertEquals(1e-50, goog.json.unsafeParse('1e-50'));
    +  assertEquals(1e-50, goog.json.unsafeParse('1E-50'));
    +}
    +
    +function testBooleanParse() {
    +  assertEquals('true', goog.json.parse('true'), true);
    +  assertEquals('false', goog.json.parse('false'), false);
    +
    +  assertNotEquals('0', goog.json.parse('0'), false);
    +  assertNotEquals('"false"', goog.json.parse('"false"'), false);
    +  assertNotEquals('null', goog.json.parse('null'), false);
    +
    +  assertNotEquals('1', goog.json.parse('1'), true);
    +  assertNotEquals('"true"', goog.json.parse('"true"'), true);
    +  assertNotEquals('{}', goog.json.parse('{}'), true);
    +  assertNotEquals('[]', goog.json.parse('[]'), true);
    +}
    +
    +function testBooleanUnsafeParse() {
    +  assertEquals('true', goog.json.unsafeParse('true'), true);
    +  assertEquals('false', goog.json.unsafeParse('false'), false);
    +
    +  assertNotEquals('0', goog.json.unsafeParse('0'), false);
    +  assertNotEquals('"false"', goog.json.unsafeParse('"false"'), false);
    +  assertNotEquals('null', goog.json.unsafeParse('null'), false);
    +
    +  assertNotEquals('1', goog.json.unsafeParse('1'), true);
    +  assertNotEquals('"true"', goog.json.unsafeParse('"true"'), true);
    +  assertNotEquals('{}', goog.json.unsafeParse('{}'), true);
    +  assertNotEquals('[]', goog.json.unsafeParse('[]'), true);
    +}
    +
    +function testArrayParse() {
    +  assertArrayEquals([], goog.json.parse('[]'));
    +  assertArrayEquals([1], goog.json.parse('[1]'));
    +  assertArrayEquals([1, 2], goog.json.parse('[1,2]'));
    +  assertArrayEquals([1, 2, 3], goog.json.parse('[1,2,3]'));
    +  assertArrayEquals([[]], goog.json.parse('[[]]'));
    +
    +  // Note that array-holes are not valid json. However, goog.json.parse
    +  // supports them so that clients can reap the security benefits of
    +  // goog.json.parse even if they are using this non-standard format.
    +  assertArrayEquals([1, /* hole */, 3], goog.json.parse('[1,,3]'));
    +
    +  // make sure we do not get an array for something that looks like an array
    +  assertFalse('{length:0}', 'push' in goog.json.parse('{"length":0}'));
    +}
    +
    +function testArrayUnsafeParse() {
    +  function arrayEquals(a1, a2) {
    +    if (a1.length != a2.length) {
    +      return false;
    +    }
    +    for (var i = 0; i < a1.length; i++) {
    +      if (a1[i] != a2[i]) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  }
    +
    +  assertTrue('[]', arrayEquals(goog.json.unsafeParse('[]'), []));
    +  assertTrue('[1]', arrayEquals(goog.json.unsafeParse('[1]'), [1]));
    +  assertTrue('[1,2]', arrayEquals(goog.json.unsafeParse('[1,2]'), [1, 2]));
    +  assertTrue('[1,2,3]',
    +      arrayEquals(goog.json.unsafeParse('[1,2,3]'), [1, 2, 3]));
    +  assertTrue('[[]]', arrayEquals(goog.json.unsafeParse('[[]]')[0], []));
    +
    +  // make sure we do not get an array for something that looks like an array
    +  assertFalse('{length:0}', 'push' in goog.json.unsafeParse('{"length":0}'));
    +}
    +
    +function testObjectParse() {
    +  function objectEquals(a1, a2) {
    +    for (var key in a1) {
    +      if (a1[key] != a2[key]) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  }
    +
    +  assertTrue('{}', objectEquals(goog.json.parse('{}'), {}));
    +  assertTrue('{"a":"b"}',
    +      objectEquals(goog.json.parse('{"a":"b"}'), {a: 'b'}));
    +  assertTrue('{"a":"b","c":"d"}',
    +             objectEquals(goog.json.parse('{"a":"b","c":"d"}'),
    +             {a: 'b', c: 'd'}));
    +  assertTrue('{" ":" "}',
    +      objectEquals(goog.json.parse('{" ":" "}'), {' ': ' '}));
    +
    +  // make sure we do not get an Object when it is really an array
    +  assertTrue('[0,1]', 'length' in goog.json.parse('[0,1]'));
    +}
    +
    +function testObjectUnsafeParse() {
    +  function objectEquals(a1, a2) {
    +    for (var key in a1) {
    +      if (a1[key] != a2[key]) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  }
    +
    +  assertTrue('{}', objectEquals(goog.json.unsafeParse('{}'), {}));
    +  assertTrue('{"a":"b"}',
    +      objectEquals(goog.json.unsafeParse('{"a":"b"}'), {a: 'b'}));
    +  assertTrue('{"a":"b","c":"d"}',
    +             objectEquals(goog.json.unsafeParse('{"a":"b","c":"d"}'),
    +             {a: 'b', c: 'd'}));
    +  assertTrue('{" ":" "}',
    +      objectEquals(goog.json.unsafeParse('{" ":" "}'), {' ': ' '}));
    +
    +  // make sure we do not get an Object when it is really an array
    +  assertTrue('[0,1]', 'length' in goog.json.unsafeParse('[0,1]'));
    +}
    +
    +
    +function testForValidJson() {
    +  function error_(msg, s) {
    +    assertThrows(msg + ', Should have raised an exception: ' + s,
    +        goog.partial(goog.json.parse, s));
    +  }
    +
    +  error_('Non closed string', '"dasdas');
    +  error_('undefined is not valid json', 'undefined');
    +
    +  // These numbers cannot be represented in JSON
    +  error_('NaN cannot be presented in JSON', 'NaN');
    +  error_('Infinity cannot be presented in JSON', 'Infinity');
    +  error_('-Infinity cannot be presented in JSON', '-Infinity');
    +}
    +
    +function testIsNotValid() {
    +  assertFalse(goog.json.isValid('t'));
    +  assertFalse(goog.json.isValid('r'));
    +  assertFalse(goog.json.isValid('u'));
    +  assertFalse(goog.json.isValid('e'));
    +  assertFalse(goog.json.isValid('f'));
    +  assertFalse(goog.json.isValid('a'));
    +  assertFalse(goog.json.isValid('l'));
    +  assertFalse(goog.json.isValid('s'));
    +  assertFalse(goog.json.isValid('n'));
    +  assertFalse(goog.json.isValid('E'));
    +
    +  assertFalse(goog.json.isValid('+'));
    +  assertFalse(goog.json.isValid('-'));
    +
    +  assertFalse(goog.json.isValid('t++'));
    +  assertFalse(goog.json.isValid('++t'));
    +  assertFalse(goog.json.isValid('t--'));
    +  assertFalse(goog.json.isValid('--t'));
    +  assertFalse(goog.json.isValid('-t'));
    +  assertFalse(goog.json.isValid('+t'));
    +
    +  assertFalse(goog.json.isValid('"\\"')); // "\"
    +  assertFalse(goog.json.isValid('"\\'));  // "\
    +
    +  // multiline string using \ at the end is not valid
    +  assertFalse(goog.json.isValid('"a\\\nb"'));
    +
    +
    +  assertFalse(goog.json.isValid('"\n"'));
    +  assertFalse(goog.json.isValid('"\r"'));
    +  assertFalse(goog.json.isValid('"\r\n"'));
    +  // Disallow the unicode newlines
    +  assertFalse(goog.json.isValid('"\u2028"'));
    +  assertFalse(goog.json.isValid('"\u2029"'));
    +
    +  assertFalse(goog.json.isValid(' '));
    +  assertFalse(goog.json.isValid('\n'));
    +  assertFalse(goog.json.isValid('\r'));
    +  assertFalse(goog.json.isValid('\r\n'));
    +
    +  assertFalse(goog.json.isValid('t.r'));
    +
    +  assertFalse(goog.json.isValid('1e'));
    +  assertFalse(goog.json.isValid('1e-'));
    +  assertFalse(goog.json.isValid('1e+'));
    +
    +  assertFalse(goog.json.isValid('1e-'));
    +
    +  assertFalse(goog.json.isValid('"\\\u200D\\"'));
    +  assertFalse(goog.json.isValid('"\\\0\\"'));
    +  assertFalse(goog.json.isValid('"\\\0"'));
    +  assertFalse(goog.json.isValid('"\\0"'));
    +  assertFalse(goog.json.isValid('"\x0c"'));
    +
    +  assertFalse(goog.json.isValid('"\\\u200D\\", alert(\'foo\') //"\n'));
    +}
    +
    +function testIsValid() {
    +  assertTrue(goog.json.isValid('\n""\n'));
    +  assertTrue(goog.json.isValid('[1\n,2\r,3\u2028\n,4\u2029]'));
    +  assertTrue(goog.json.isValid('"\x7f"'));
    +  assertTrue(goog.json.isValid('"\x09"'));
    +  // Test tab characters in json.
    +  assertTrue(goog.json.isValid('{"\t":"\t"}'));
    +}
    +
    +function testDoNotSerializeProto() {
    +  function F() {};
    +  F.prototype = {
    +    c: 3
    +  };
    +
    +  var obj = new F;
    +  obj.a = 1;
    +  obj.b = 2;
    +
    +  assertEquals('Should not follow the prototype chain',
    +               '{"a":1,"b":2}',
    +               goog.json.serialize(obj));
    +}
    +
    +function testEscape() {
    +  var unescaped = '1a*/]';
    +  assertEquals('Should not escape',
    +               '"' + unescaped + '"',
    +               goog.json.serialize(unescaped));
    +
    +  var escaped = '\n\x7f\u1049';
    +  assertEquals('Should escape',
    +               '',
    +               findCommonChar(escaped, goog.json.serialize(escaped)));
    +  assertEquals('Should eval to the same string after escaping',
    +               escaped,
    +               goog.json.parse(goog.json.serialize(escaped)));
    +}
    +
    +function testReplacer() {
    +  assertSerialize('[null,null,0]', [, , 0]);
    +
    +  assertSerialize('[0,0,{"x":0}]', [, , {x: 0}], function(k, v) {
    +    if (v === undefined && goog.isArray(this)) {
    +      return 0;
    +    }
    +    return v;
    +  });
    +
    +  assertSerialize('[0,1,2,3]', [0, 0, 0, 0], function(k, v) {
    +    var kNum = Number(k);
    +    if (k && !isNaN(kNum)) {
    +      return kNum;
    +    }
    +    return v;
    +  });
    +
    +  var f = function(k, v) {
    +    return typeof v == 'number' ? v + 1 : v;
    +  };
    +  assertSerialize('{"a":1,"b":{"c":2}}', {'a': 0, 'b': {'c': 1}}, f);
    +}
    +
    +function testDateSerialize() {
    +  assertSerialize('{}', new Date(0));
    +}
    +
    +function testToJSONSerialize() {
    +  assertSerialize('{}', {toJSON: goog.functions.constant('serialized')});
    +  assertSerialize('{"toJSON":"normal"}', {toJSON: 'normal'});
    +}
    +
    +
    +/**
    + * Asserts that the given object serializes to the given value.
    + * If the current browser has an implementation of JSON.serialize,
    + * we make sure our version matches that one.
    + */
    +function assertSerialize(expected, obj, opt_replacer) {
    +  assertEquals(expected, goog.json.serialize(obj, opt_replacer));
    +
    +  // I'm pretty sure that the goog.json.serialize behavior is correct by the ES5
    +  // spec, but JSON.stringify(undefined) is undefined on all browsers.
    +  if (obj === undefined) return;
    +
    +  // Browsers don't serialize undefined properties, but goog.json.serialize does
    +  if (goog.isObject(obj) && ('a' in obj) && obj['a'] === undefined) return;
    +
    +  // Replacers are broken on IE and older versions of firefox.
    +  if (opt_replacer && !goog.userAgent.WEBKIT) return;
    +
    +  // goog.json.serialize does not stringify dates the same way.
    +  if (obj instanceof Date) return;
    +
    +  // goog.json.serialize does not stringify functions the same way.
    +  if (obj instanceof Function) return;
    +
    +  // goog.json.serialize doesn't use the toJSON method.
    +  if (goog.isObject(obj) && goog.isFunction(obj.toJSON)) return;
    +
    +  if (typeof JSON != 'undefined') {
    +    assertEquals(
    +        'goog.json.serialize does not match JSON.stringify',
    +        expected,
    +        JSON.stringify(obj, opt_replacer));
    +  }
    +}
    +
    +
    +/**
    + * @param {string} a
    + * @param {string} b
    + * @return {string} any common character between two strings a and b.
    + */
    +function findCommonChar(a, b) {
    +  for (var i = 0; i < b.length; i++) {
    +    if (a.indexOf(b.charAt(i)) >= 0) {
    +      return b.charAt(i);
    +    }
    +  }
    +  return '';
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/json/nativejsonprocessor.js b/mapbox-gl/closure-library/closure/goog/json/nativejsonprocessor.js
    new file mode 100644
    index 0000000000..c6359aa325
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/nativejsonprocessor.js
    @@ -0,0 +1,73 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Defines a class for parsing JSON using the browser's built in
    + * JSON library.
    + */
    +
    +goog.provide('goog.json.NativeJsonProcessor');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.json.Processor');
    +
    +
    +
    +/**
    + * A class that parses and stringifies JSON using the browser's built-in JSON
    + * library, if it is avaliable.
    + *
    + * Note that the native JSON api has subtle differences across browsers, so
    + * use this implementation with care.  See json_test#assertSerialize
    + * for details on the differences from goog.json.
    + *
    + * This implementation is signficantly faster than goog.json, at least on
    + * Chrome.  See json_perf.html for a perf test showing the difference.
    + *
    + * @param {?goog.json.Replacer=} opt_replacer An optional replacer to use during
    + *     serialization.
    + * @param {?goog.json.Reviver=} opt_reviver An optional reviver to use during
    + *     parsing.
    + * @constructor
    + * @implements {goog.json.Processor}
    + * @final
    + */
    +goog.json.NativeJsonProcessor = function(opt_replacer, opt_reviver) {
    +  goog.asserts.assert(goog.isDef(goog.global['JSON']), 'JSON not defined');
    +
    +  /**
    +   * @type {goog.json.Replacer|null|undefined}
    +   * @private
    +   */
    +  this.replacer_ = opt_replacer;
    +
    +  /**
    +   * @type {goog.json.Reviver|null|undefined}
    +   * @private
    +   */
    +  this.reviver_ = opt_reviver;
    +};
    +
    +
    +/** @override */
    +goog.json.NativeJsonProcessor.prototype.stringify = function(object) {
    +  return goog.global['JSON'].stringify(object, this.replacer_);
    +};
    +
    +
    +/** @override */
    +goog.json.NativeJsonProcessor.prototype.parse = function(s) {
    +  return goog.global['JSON'].parse(s, this.reviver_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/json/processor.js b/mapbox-gl/closure-library/closure/goog/json/processor.js
    new file mode 100644
    index 0000000000..5ac3df248d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/processor.js
    @@ -0,0 +1,33 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Defines an interface for JSON parsing and serialization.
    + */
    +
    +goog.provide('goog.json.Processor');
    +
    +goog.require('goog.string.Parser');
    +goog.require('goog.string.Stringifier');
    +
    +
    +
    +/**
    + * An interface for JSON parsing and serialization.
    + * @interface
    + * @extends {goog.string.Parser}
    + * @extends {goog.string.Stringifier}
    + */
    +goog.json.Processor = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/json/processor_test.html b/mapbox-gl/closure-library/closure/goog/json/processor_test.html
    new file mode 100644
    index 0000000000..2e270e39d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/processor_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.json.Processor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.json.processorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/json/processor_test.js b/mapbox-gl/closure-library/closure/goog/json/processor_test.js
    new file mode 100644
    index 0000000000..2e508e09af
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/json/processor_test.js
    @@ -0,0 +1,88 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.json.processorTest');
    +goog.setTestOnly('goog.json.processorTest');
    +
    +goog.require('goog.json.EvalJsonProcessor');
    +goog.require('goog.json.NativeJsonProcessor');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var SUPPORTS_NATIVE_JSON = false;
    +
    +function setUpPage() {
    +  SUPPORTS_NATIVE_JSON = goog.global['JSON'] &&
    +      !(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('5.0'));
    +}
    +
    +var REPLACER = function(k, v) {
    +  return !!k ? v + 'd' : v;
    +};
    +
    +var REVIVER = function(k, v) {
    +  return !!k ? v.substring(0, v.length - 1) : v;
    +};
    +
    +// Just sanity check parsing and stringifying.
    +// Thorough tests are in json_test.html.
    +
    +function testJsParser() {
    +  var json = '{"a":1,"b":{"c":2}}';
    +  runParsingTest(new goog.json.EvalJsonProcessor(), json, json);
    +}
    +
    +function testNativeParser() {
    +  if (!SUPPORTS_NATIVE_JSON) {
    +    return;
    +  }
    +  var json = '{"a":1,"b":{"c":2}}';
    +  runParsingTest(new goog.json.NativeJsonProcessor(), json, json);
    +}
    +
    +function testJsParser_withReplacer() {
    +  runParsingTest(new goog.json.EvalJsonProcessor(REPLACER),
    +      '{"a":"foo","b":"goo"}', '{"a":"food","b":"good"}');
    +}
    +
    +function testNativeParser_withReplacer() {
    +  if (!SUPPORTS_NATIVE_JSON) {
    +    return;
    +  }
    +  runParsingTest(new goog.json.NativeJsonProcessor(REPLACER),
    +      '{"a":"foo","b":"goo"}', '{"a":"food","b":"good"}');
    +}
    +
    +function testNativeParser_withReviver() {
    +  if (!SUPPORTS_NATIVE_JSON) {
    +    return;
    +  }
    +  var json = '{"a":"fod","b":"god"}';
    +  runParsingTest(new goog.json.NativeJsonProcessor(REPLACER, REVIVER),
    +      json, json);
    +}
    +
    +function testUnsafeJsParser() {
    +  var json = '{"a":1,"b":{"c":2}}';
    +  runParsingTest(new goog.json.EvalJsonProcessor(null, true), json, json);
    +}
    +
    +function testUnsafeJsParser_withReplacer() {
    +  runParsingTest(new goog.json.EvalJsonProcessor(REPLACER, true),
    +      '{"a":"foo","b":"goo"}', '{"a":"food","b":"good"}');
    +}
    +
    +function runParsingTest(parser, input, expected) {
    +  assertEquals(expected, parser.stringify(parser.parse(input)));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor.js b/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor.js
    new file mode 100644
    index 0000000000..ef77c999a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor.js
    @@ -0,0 +1,211 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This event monitor wraps the Page Visibility API.
    + * @see http://www.w3.org/TR/page-visibility/
    + */
    +
    +goog.provide('goog.labs.dom.PageVisibilityEvent');
    +goog.provide('goog.labs.dom.PageVisibilityMonitor');
    +goog.provide('goog.labs.dom.PageVisibilityState');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.vendor');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.memoize');
    +
    +goog.scope(function() {
    +var dom = goog.labs.dom;
    +
    +
    +/**
    + * The different visibility states.
    + * @enum {string}
    + */
    +dom.PageVisibilityState = {
    +  HIDDEN: 'hidden',
    +  VISIBLE: 'visible',
    +  PRERENDER: 'prerender',
    +  UNLOADED: 'unloaded'
    +};
    +
    +
    +
    +/**
    + * This event handler allows you to catch page visibility change events.
    + * @param {!goog.dom.DomHelper=} opt_domHelper
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +dom.PageVisibilityMonitor = function(opt_domHelper) {
    +  dom.PageVisibilityMonitor.base(this, 'constructor');
    +
    +  /**
    +   * @private {!goog.dom.DomHelper}
    +   */
    +  this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * @private {?string}
    +   */
    +  this.eventType_ = this.getBrowserEventType_();
    +
    +  // Some browsers do not support visibilityChange and therefore we don't bother
    +  // setting up events.
    +  if (this.eventType_) {
    +    /**
    +     * @private {goog.events.Key}
    +     */
    +    this.eventKey_ = goog.events.listen(this.domHelper_.getDocument(),
    +        this.eventType_, goog.bind(this.handleChange_, this));
    +  }
    +};
    +goog.inherits(dom.PageVisibilityMonitor, goog.events.EventTarget);
    +
    +
    +/**
    + * @return {?string} The visibility change event type, or null if not supported.
    + *     Memoized for performance.
    + * @private
    + */
    +dom.PageVisibilityMonitor.prototype.getBrowserEventType_ =
    +    goog.memoize(function() {
    +  var isSupported = this.isSupported();
    +  var isPrefixed = this.isPrefixed_();
    +
    +  if (isSupported) {
    +    return isPrefixed ? goog.dom.vendor.getPrefixedEventType(
    +        goog.events.EventType.VISIBILITYCHANGE) :
    +        goog.events.EventType.VISIBILITYCHANGE;
    +  } else {
    +    return null;
    +  }
    +});
    +
    +
    +/**
    + * @return {?string} The browser-specific document.hidden property.  Memoized
    + *     for performance.
    + * @private
    + */
    +dom.PageVisibilityMonitor.prototype.getHiddenPropertyName_ = goog.memoize(
    +    function() {
    +      return goog.dom.vendor.getPrefixedPropertyName(
    +          'hidden', this.domHelper_.getDocument());
    +    });
    +
    +
    +/**
    + * @return {boolean} Whether the visibility API is prefixed.
    + * @private
    + */
    +dom.PageVisibilityMonitor.prototype.isPrefixed_ = function() {
    +  return this.getHiddenPropertyName_() != 'hidden';
    +};
    +
    +
    +/**
    + * @return {?string} The browser-specific document.visibilityState property.
    + *     Memoized for performance.
    + * @private
    + */
    +dom.PageVisibilityMonitor.prototype.getVisibilityStatePropertyName_ =
    +    goog.memoize(function() {
    +  return goog.dom.vendor.getPrefixedPropertyName(
    +      'visibilityState', this.domHelper_.getDocument());
    +});
    +
    +
    +/**
    + * @return {boolean} Whether the visibility API is supported.
    + */
    +dom.PageVisibilityMonitor.prototype.isSupported = function() {
    +  return !!this.getHiddenPropertyName_();
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the page is visible.
    + */
    +dom.PageVisibilityMonitor.prototype.isHidden = function() {
    +  return !!this.domHelper_.getDocument()[this.getHiddenPropertyName_()];
    +};
    +
    +
    +/**
    + * @return {?dom.PageVisibilityState} The page visibility state, or null if
    + *     not supported.
    + */
    +dom.PageVisibilityMonitor.prototype.getVisibilityState = function() {
    +  if (!this.isSupported()) {
    +    return null;
    +  }
    +  return this.domHelper_.getDocument()[this.getVisibilityStatePropertyName_()];
    +};
    +
    +
    +/**
    + * Handles the events on the element.
    + * @param {goog.events.BrowserEvent} e The underlying browser event.
    + * @private
    + */
    +dom.PageVisibilityMonitor.prototype.handleChange_ = function(e) {
    +  var state = this.getVisibilityState();
    +  var visibilityEvent = new dom.PageVisibilityEvent(
    +      this.isHidden(), /** @type {dom.PageVisibilityState} */ (state));
    +  this.dispatchEvent(visibilityEvent);
    +};
    +
    +
    +/** @override */
    +dom.PageVisibilityMonitor.prototype.disposeInternal = function() {
    +  goog.events.unlistenByKey(this.eventKey_);
    +  dom.PageVisibilityMonitor.base(this, 'disposeInternal');
    +};
    +
    +
    +
    +/**
    + * A page visibility change event.
    + * @param {boolean} hidden Whether the page is hidden.
    + * @param {goog.labs.dom.PageVisibilityState} visibilityState A more detailed
    + *     visibility state.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +dom.PageVisibilityEvent = function(hidden, visibilityState) {
    +  dom.PageVisibilityEvent.base(
    +      this, 'constructor', goog.events.EventType.VISIBILITYCHANGE);
    +
    +  /**
    +   * Whether the page is hidden.
    +   * @type {boolean}
    +   */
    +  this.hidden = hidden;
    +
    +  /**
    +   * A more detailed visibility state.
    +   * @type {dom.PageVisibilityState}
    +   */
    +  this.visibilityState = visibilityState;
    +};
    +goog.inherits(dom.PageVisibilityEvent, goog.events.Event);
    +
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor_test.js b/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor_test.js
    new file mode 100644
    index 0000000000..f1d21f7971
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/dom/pagevisibilitymonitor_test.js
    @@ -0,0 +1,87 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.dom.PageVisibilityMonitorTest');
    +goog.setTestOnly('goog.labs.dom.PageVisibilityMonitorTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.labs.dom.PageVisibilityMonitor');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +var vh;
    +
    +
    +function tearDown() {
    +  goog.dispose(vh);
    +  vh = null;
    +  stubs.reset();
    +}
    +
    +function testConstructor() {
    +  vh = new goog.labs.dom.PageVisibilityMonitor();
    +}
    +
    +function testNoVisibilitySupport() {
    +  stubs.set(goog.labs.dom.PageVisibilityMonitor.prototype,
    +      'getBrowserEventType_', goog.functions.NULL);
    +
    +  var listener = goog.testing.recordFunction();
    +  vh = new goog.labs.dom.PageVisibilityMonitor();
    +
    +  goog.events.listen(vh, 'visibilitychange', listener);
    +
    +  var e = new goog.testing.events.Event('visibilitychange');
    +  e.target = window.document;
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertEquals(0, listener.getCallCount());
    +}
    +
    +function testListener() {
    +  stubs.set(goog.labs.dom.PageVisibilityMonitor.prototype,
    +      'getBrowserEventType_', goog.functions.constant('visibilitychange'));
    +
    +  var listener = goog.testing.recordFunction();
    +  vh = new goog.labs.dom.PageVisibilityMonitor();
    +
    +  goog.events.listen(vh, 'visibilitychange', listener);
    +
    +  var e = new goog.testing.events.Event('visibilitychange');
    +  e.target = window.document;
    +  goog.testing.events.fireBrowserEvent(e);
    +
    +  assertEquals(1, listener.getCallCount());
    +}
    +
    +function testListenerForWebKit() {
    +  stubs.set(goog.labs.dom.PageVisibilityMonitor.prototype,
    +      'getBrowserEventType_',
    +      goog.functions.constant('webkitvisibilitychange'));
    +
    +  var listener = goog.testing.recordFunction();
    +  vh = new goog.labs.dom.PageVisibilityMonitor();
    +
    +  goog.events.listen(vh, 'visibilitychange', listener);
    +
    +  var e = new goog.testing.events.Event('webkitvisibilitychange');
    +  e.target = window.document;
    +  goog.testing.events.fireBrowserEvent(e);
    +
    +  assertEquals(1, listener.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget.js b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget.js
    new file mode 100644
    index 0000000000..ea421094ee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget.js
    @@ -0,0 +1,305 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An implementation of {@link goog.events.Listenable} that does
    + * not need to be disposed.
    + */
    +
    +goog.provide('goog.labs.events.NonDisposableEventTarget');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.Listenable');
    +goog.require('goog.events.ListenerMap');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * An implementation of {@code goog.events.Listenable} with full W3C
    + * EventTarget-like support (capture/bubble mechanism, stopping event
    + * propagation, preventing default actions).
    + *
    + * You may subclass this class to turn your class into a Listenable.
    + *
    + * Unlike {@link goog.events.EventTarget}, this class does not implement
    + * {@link goog.disposable.IDisposable}. Instances of this class that have had
    + * It is not necessary to call {@link goog.dispose}
    + * or {@link #removeAllListeners} in order for an instance of this class
    + * to be garbage collected.
    + *
    + * Unless propagation is stopped, an event dispatched by an
    + * EventTarget will bubble to the parent returned by
    + * {@code getParentEventTarget}. To set the parent, call
    + * {@code setParentEventTarget}. Subclasses that don't support
    + * changing the parent can override the setter to throw an error.
    + *
    + * Example usage:
    + * <pre>
    + *   var source = new goog.labs.events.NonDisposableEventTarget();
    + *   function handleEvent(e) {
    + *     alert('Type: ' + e.type + '; Target: ' + e.target);
    + *   }
    + *   source.listen('foo', handleEvent);
    + *   source.dispatchEvent('foo'); // will call handleEvent
    + * </pre>
    + *
    + * TODO(chrishenry|johnlenz): Consider a more modern, less viral
    + * (not based on inheritance) replacement of goog.Disposable, which will allow
    + * goog.events.EventTarget to not be disposable.
    + *
    + * @constructor
    + * @implements {goog.events.Listenable}
    + * @final
    + */
    +goog.labs.events.NonDisposableEventTarget = function() {
    +  /**
    +   * Maps of event type to an array of listeners.
    +   * @private {!goog.events.ListenerMap}
    +   */
    +  this.eventTargetListeners_ = new goog.events.ListenerMap(this);
    +};
    +goog.events.Listenable.addImplementation(
    +    goog.labs.events.NonDisposableEventTarget);
    +
    +
    +/**
    + * An artificial cap on the number of ancestors you can have. This is mainly
    + * for loop detection.
    + * @const {number}
    + * @private
    + */
    +goog.labs.events.NonDisposableEventTarget.MAX_ANCESTORS_ = 1000;
    +
    +
    +/**
    + * Parent event target, used during event bubbling.
    + * @private {goog.events.Listenable}
    + */
    +goog.labs.events.NonDisposableEventTarget.prototype.parentEventTarget_ = null;
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.getParentEventTarget =
    +    function() {
    +  return this.parentEventTarget_;
    +};
    +
    +
    +/**
    + * Sets the parent of this event target to use for capture/bubble
    + * mechanism.
    + * @param {goog.events.Listenable} parent Parent listenable (null if none).
    + */
    +goog.labs.events.NonDisposableEventTarget.prototype.setParentEventTarget =
    +    function(parent) {
    +  this.parentEventTarget_ = parent;
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.dispatchEvent = function(
    +    e) {
    +  this.assertInitialized_();
    +  var ancestorsTree, ancestor = this.getParentEventTarget();
    +  if (ancestor) {
    +    ancestorsTree = [];
    +    var ancestorCount = 1;
    +    for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
    +      ancestorsTree.push(ancestor);
    +      goog.asserts.assert(
    +          (++ancestorCount <
    +                  goog.labs.events.NonDisposableEventTarget.MAX_ANCESTORS_),
    +          'infinite loop');
    +    }
    +  }
    +
    +  return goog.labs.events.NonDisposableEventTarget.dispatchEventInternal_(
    +      this, e, ancestorsTree);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.listen = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  this.assertInitialized_();
    +  return this.eventTargetListeners_.add(
    +      String(type), listener, false /* callOnce */, opt_useCapture,
    +      opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.listenOnce = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  return this.eventTargetListeners_.add(
    +      String(type), listener, true /* callOnce */, opt_useCapture,
    +      opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.unlisten = function(
    +    type, listener, opt_useCapture, opt_listenerScope) {
    +  return this.eventTargetListeners_.remove(
    +      String(type), listener, opt_useCapture, opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.unlistenByKey = function(
    +    key) {
    +  return this.eventTargetListeners_.removeByKey(key);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.removeAllListeners =
    +    function(opt_type) {
    +  return this.eventTargetListeners_.removeAll(opt_type);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.fireListeners = function(
    +    type, capture, eventObject) {
    +  // TODO(chrishenry): Original code avoids array creation when there
    +  // is no listener, so we do the same. If this optimization turns
    +  // out to be not required, we can replace this with
    +  // getListeners(type, capture) instead, which is simpler.
    +  var listenerArray = this.eventTargetListeners_.listeners[String(type)];
    +  if (!listenerArray) {
    +    return true;
    +  }
    +  listenerArray = goog.array.clone(listenerArray);
    +
    +  var rv = true;
    +  for (var i = 0; i < listenerArray.length; ++i) {
    +    var listener = listenerArray[i];
    +    // We might not have a listener if the listener was removed.
    +    if (listener && !listener.removed && listener.capture == capture) {
    +      var listenerFn = listener.listener;
    +      var listenerHandler = listener.handler || listener.src;
    +
    +      if (listener.callOnce) {
    +        this.unlistenByKey(listener);
    +      }
    +      rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
    +    }
    +  }
    +
    +  return rv && eventObject.returnValue_ != false;
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.getListeners = function(
    +    type, capture) {
    +  return this.eventTargetListeners_.getListeners(String(type), capture);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.getListener = function(
    +    type, listener, capture, opt_listenerScope) {
    +  return this.eventTargetListeners_.getListener(
    +      String(type), listener, capture, opt_listenerScope);
    +};
    +
    +
    +/** @override */
    +goog.labs.events.NonDisposableEventTarget.prototype.hasListener = function(
    +    opt_type, opt_capture) {
    +  var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
    +  return this.eventTargetListeners_.hasListener(id, opt_capture);
    +};
    +
    +
    +/**
    + * Asserts that the event target instance is initialized properly.
    + * @private
    + */
    +goog.labs.events.NonDisposableEventTarget.prototype.assertInitialized_ =
    +    function() {
    +  goog.asserts.assert(
    +      this.eventTargetListeners_,
    +      'Event target is not initialized. Did you call the superclass ' +
    +      '(goog.labs.events.NonDisposableEventTarget) constructor?');
    +};
    +
    +
    +/**
    + * Dispatches the given event on the ancestorsTree.
    + *
    + * TODO(chrishenry): Look for a way to reuse this logic in
    + * goog.events, if possible.
    + *
    + * @param {!Object} target The target to dispatch on.
    + * @param {goog.events.Event|Object|string} e The event object.
    + * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
    + *     tree of the target, in reverse order from the closest ancestor
    + *     to the root event target. May be null if the target has no ancestor.
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the listeners returns false) this will also return false.
    + * @private
    + */
    +goog.labs.events.NonDisposableEventTarget.dispatchEventInternal_ = function(
    +    target, e, opt_ancestorsTree) {
    +  var type = e.type || /** @type {string} */ (e);
    +
    +  // If accepting a string or object, create a custom event object so that
    +  // preventDefault and stopPropagation work with the event.
    +  if (goog.isString(e)) {
    +    e = new goog.events.Event(e, target);
    +  } else if (!(e instanceof goog.events.Event)) {
    +    var oldEvent = e;
    +    e = new goog.events.Event(type, target);
    +    goog.object.extend(e, oldEvent);
    +  } else {
    +    e.target = e.target || target;
    +  }
    +
    +  var rv = true, currentTarget;
    +
    +  // Executes all capture listeners on the ancestors, if any.
    +  if (opt_ancestorsTree) {
    +    for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
    +         i--) {
    +      currentTarget = e.currentTarget = opt_ancestorsTree[i];
    +      rv = currentTarget.fireListeners(type, true, e) && rv;
    +    }
    +  }
    +
    +  // Executes capture and bubble listeners on the target.
    +  if (!e.propagationStopped_) {
    +    currentTarget = e.currentTarget = target;
    +    rv = currentTarget.fireListeners(type, true, e) && rv;
    +    if (!e.propagationStopped_) {
    +      rv = currentTarget.fireListeners(type, false, e) && rv;
    +    }
    +  }
    +
    +  // Executes all bubble listeners on the ancestors, if any.
    +  if (opt_ancestorsTree) {
    +    for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
    +      currentTarget = e.currentTarget = opt_ancestorsTree[i];
    +      rv = currentTarget.fireListeners(type, false, e) && rv;
    +    }
    +  }
    +
    +  return rv;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.html b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.html
    new file mode 100644
    index 0000000000..63ee72aba9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.events.EventTarget
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.events.NonDisposableEventTargetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.js b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.js
    new file mode 100644
    index 0000000000..dee067b2b4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_test.js
    @@ -0,0 +1,72 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.events.NonDisposableEventTargetTest');
    +goog.setTestOnly('goog.labs.events.NonDisposableEventTargetTest');
    +
    +goog.require('goog.events.Listenable');
    +goog.require('goog.events.eventTargetTester');
    +goog.require('goog.events.eventTargetTester.KeyType');
    +goog.require('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.require('goog.labs.events.NonDisposableEventTarget');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  var newListenableFn = function() {
    +    return new goog.labs.events.NonDisposableEventTarget();
    +  };
    +  var listenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.listen(type, listener, opt_capt, opt_handler);
    +  };
    +  var unlistenFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.unlisten(type, listener, opt_capt, opt_handler);
    +  };
    +  var unlistenByKeyFn = function(src, key) {
    +    return src.unlistenByKey(key);
    +  };
    +  var listenOnceFn = function(src, type, listener, opt_capt, opt_handler) {
    +    return src.listenOnce(type, listener, opt_capt, opt_handler);
    +  };
    +  var dispatchEventFn = function(src, e) {
    +    return src.dispatchEvent(e);
    +  };
    +  var removeAllFn = function(src, opt_type, opt_capture) {
    +    return src.removeAllListeners(opt_type, opt_capture);
    +  };
    +  var getListenersFn = function(src, type, capture) {
    +    return src.getListeners(type, capture);
    +  };
    +  var getListenerFn = function(src, type, listener, capture, opt_handler) {
    +    return src.getListener(type, listener, capture, opt_handler);
    +  };
    +  var hasListenerFn = function(src, opt_type, opt_capture) {
    +    return src.hasListener(opt_type, opt_capture);
    +  };
    +
    +  goog.events.eventTargetTester.setUp(
    +      newListenableFn, listenFn, unlistenFn, unlistenByKeyFn,
    +      listenOnceFn, dispatchEventFn,
    +      removeAllFn, getListenersFn, getListenerFn, hasListenerFn,
    +      goog.events.eventTargetTester.KeyType.NUMBER,
    +      goog.events.eventTargetTester.UnlistenReturnType.BOOLEAN, false);
    +}
    +
    +function tearDown() {
    +  goog.events.eventTargetTester.tearDown();
    +}
    +
    +function testRuntimeTypeIsCorrect() {
    +  var target = new goog.labs.events.NonDisposableEventTarget();
    +  assertTrue(goog.events.Listenable.isImplementedBy(target));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.html b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.html
    new file mode 100644
    index 0000000000..2483d41129
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.events.EventTarget
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.labs.events.NonDisposableEventTargetGoogEventsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.js b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.js
    new file mode 100644
    index 0000000000..a063819776
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/nondisposableeventtarget_via_googevents_test.js
    @@ -0,0 +1,78 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.events.NonDisposableEventTargetGoogEventsTest');
    +goog.setTestOnly('goog.labs.events.NonDisposableEventTargetGoogEventsTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.eventTargetTester');
    +goog.require('goog.events.eventTargetTester.KeyType');
    +goog.require('goog.events.eventTargetTester.UnlistenReturnType');
    +goog.require('goog.labs.events.NonDisposableEventTarget');
    +goog.require('goog.testing');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  var newListenableFn = function() {
    +    return new goog.labs.events.NonDisposableEventTarget();
    +  };
    +  var unlistenByKeyFn = function(src, key) {
    +    return goog.events.unlistenByKey(key);
    +  };
    +  goog.events.eventTargetTester.setUp(
    +      newListenableFn, goog.events.listen, goog.events.unlisten,
    +      unlistenByKeyFn,
    +      goog.events.listenOnce, goog.events.dispatchEvent,
    +      goog.events.removeAll, goog.events.getListeners,
    +      goog.events.getListener, goog.events.hasListener,
    +      goog.events.eventTargetTester.KeyType.NUMBER,
    +      goog.events.eventTargetTester.UnlistenReturnType.BOOLEAN, true);
    +}
    +
    +function tearDown() {
    +  goog.events.eventTargetTester.tearDown();
    +}
    +
    +function testUnlistenProperCleanup() {
    +  goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.events.unlisten(eventTargets[0], EventType.A, listeners[0]);
    +
    +  goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  eventTargets[0].unlisten(EventType.A, listeners[0]);
    +}
    +
    +function testUnlistenByKeyProperCleanup() {
    +  var keyNum = goog.events.listen(eventTargets[0], EventType.A, listeners[0]);
    +  goog.events.unlistenByKey(keyNum);
    +}
    +
    +function testListenOnceProperCleanup() {
    +  goog.events.listenOnce(eventTargets[0], EventType.A, listeners[0]);
    +  eventTargets[0].dispatchEvent(EventType.A);
    +}
    +
    +function testListenWithObject() {
    +  var obj = {};
    +  obj.handleEvent = goog.testing.recordFunction();
    +  goog.events.listen(eventTargets[0], EventType.A, obj);
    +  eventTargets[0].dispatchEvent(EventType.A);
    +  assertEquals(1, obj.handleEvent.getCallCount());
    +}
    +
    +function testListenWithObjectHandleEventReturningFalse() {
    +  var obj = {};
    +  obj.handleEvent = function() { return false; };
    +  goog.events.listen(eventTargets[0], EventType.A, obj);
    +  assertFalse(eventTargets[0].dispatchEvent(EventType.A));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/touch.js b/mapbox-gl/closure-library/closure/goog/labs/events/touch.js
    new file mode 100644
    index 0000000000..f03a0ade43
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/touch.js
    @@ -0,0 +1,82 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities to abstract mouse and touch events.
    + */
    +
    +
    +goog.provide('goog.labs.events.touch');
    +goog.provide('goog.labs.events.touch.TouchData');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events.EventType');
    +goog.require('goog.string');
    +
    +
    +/**
    + * Description the geometry and target of an event.
    + *
    + * @typedef {{
    + *   clientX: number,
    + *   clientY: number,
    + *   screenX: number,
    + *   screenY: number,
    + *   target: EventTarget
    + * }}
    + */
    +goog.labs.events.touch.TouchData;
    +
    +
    +/**
    + * Takes a mouse or touch event and returns the relevent geometry and target
    + * data.
    + * @param {!Event} e A mouse or touch event.
    + * @return {!goog.labs.events.touch.TouchData}
    + */
    +goog.labs.events.touch.getTouchData = function(e) {
    +
    +  var source = e;
    +  goog.asserts.assert(
    +      goog.string.startsWith(e.type, 'touch') ||
    +      goog.string.startsWith(e.type, 'mouse'),
    +      'Event must be mouse or touch event.');
    +
    +  if (goog.string.startsWith(e.type, 'touch')) {
    +    goog.asserts.assert(
    +        goog.array.contains([
    +          goog.events.EventType.TOUCHCANCEL,
    +          goog.events.EventType.TOUCHEND,
    +          goog.events.EventType.TOUCHMOVE,
    +          goog.events.EventType.TOUCHSTART
    +        ], e.type),
    +        'Touch event not of valid type.');
    +
    +    // If the event is end or cancel, take the first changed touch,
    +    // otherwise the first target touch.
    +    source = (e.type == goog.events.EventType.TOUCHEND ||
    +              e.type == goog.events.EventType.TOUCHCANCEL) ?
    +             e.changedTouches[0] : e.targetTouches[0];
    +  }
    +
    +  return {
    +    clientX: source['clientX'],
    +    clientY: source['clientY'],
    +    screenX: source['screenX'],
    +    screenY: source['screenY'],
    +    target: source['target']
    +  };
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.html b/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.html
    new file mode 100644
    index 0000000000..f1ad69dda8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.events.touch</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.labs.events.touchTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.js b/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.js
    new file mode 100644
    index 0000000000..77d1f52a68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/events/touch_test.js
    @@ -0,0 +1,96 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.events.touch.
    + */
    +
    +
    +goog.provide('goog.labs.events.touchTest');
    +
    +goog.require('goog.labs.events.touch');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.events.touchTest');
    +
    +function testMouseEvent() {
    +  var fakeTarget = {};
    +
    +  var fakeMouseMove = {
    +    'clientX': 1,
    +    'clientY': 2,
    +    'screenX': 3,
    +    'screenY': 4,
    +    'target': fakeTarget,
    +    'type': 'mousemove'
    +  };
    +
    +  var data = goog.labs.events.touch.getTouchData(fakeMouseMove);
    +  assertEquals(1, data.clientX);
    +  assertEquals(2, data.clientY);
    +  assertEquals(3, data.screenX);
    +  assertEquals(4, data.screenY);
    +  assertEquals(fakeTarget, data.target);
    +}
    +
    +function testTouchEvent() {
    +  var fakeTarget = {};
    +
    +  var fakeTouch = {
    +    'clientX': 1,
    +    'clientY': 2,
    +    'screenX': 3,
    +    'screenY': 4,
    +    'target': fakeTarget
    +  };
    +
    +  var fakeTouchStart = {
    +    'targetTouches': [fakeTouch],
    +    'target': fakeTarget,
    +    'type': 'touchstart'
    +  };
    +
    +  var data = goog.labs.events.touch.getTouchData(fakeTouchStart);
    +  assertEquals(1, data.clientX);
    +  assertEquals(2, data.clientY);
    +  assertEquals(3, data.screenX);
    +  assertEquals(4, data.screenY);
    +  assertEquals(fakeTarget, data.target);
    +}
    +
    +function testTouchChangeEvent() {
    +  var fakeTarget = {};
    +
    +  var fakeTouch = {
    +    'clientX': 1,
    +    'clientY': 2,
    +    'screenX': 3,
    +    'screenY': 4,
    +    'target': fakeTarget
    +  };
    +
    +  var fakeTouchStart = {
    +    'changedTouches': [fakeTouch],
    +    'target': fakeTarget,
    +    'type': 'touchend'
    +  };
    +
    +  var data = goog.labs.events.touch.getTouchData(fakeTouchStart);
    +  assertEquals(1, data.clientX);
    +  assertEquals(2, data.clientY);
    +  assertEquals(3, data.screenX);
    +  assertEquals(4, data.screenY);
    +  assertEquals(fakeTarget, data.target);
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/format/csv.js b/mapbox-gl/closure-library/closure/goog/labs/format/csv.js
    new file mode 100644
    index 0000000000..36fe25e586
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/format/csv.js
    @@ -0,0 +1,415 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a parser that turns a string of well-formed CSV data
    + * into an array of objects or an array of arrays. All values are returned as
    + * strings; the user has to convert data into numbers or Dates as required.
    + * Empty fields (adjacent commas) are returned as empty strings.
    + *
    + * This parser uses http://tools.ietf.org/html/rfc4180 as the definition of CSV.
    + *
    + * @author nnaze@google.com (Nathan Naze) Ported to Closure
    + */
    +goog.provide('goog.labs.format.csv');
    +goog.provide('goog.labs.format.csv.ParseError');
    +goog.provide('goog.labs.format.csv.Token');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.debug.Error');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.string.newlines');
    +
    +
    +/**
    + * @define {boolean} Enable verbose debugging. This is a flag so it can be
    + * enabled in production if necessary post-compilation.  Otherwise, debug
    + * information will be stripped to minimize final code size.
    + */
    +goog.labs.format.csv.ENABLE_VERBOSE_DEBUGGING = goog.DEBUG;
    +
    +
    +
    +/**
    + * Error thrown when parsing fails.
    + *
    + * @param {string} text The CSV source text being parsed.
    + * @param {number} index The index, in the string, of the position of the
    + *      error.
    + * @param {string=} opt_message A description of the violated parse expectation.
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.labs.format.csv.ParseError = function(text, index, opt_message) {
    +
    +  var message;
    +
    +  /**
    +   * @type {?{line: number, column: number}} The line and column of the parse
    +   *     error.
    +   */
    +  this.position = null;
    +
    +  if (goog.labs.format.csv.ENABLE_VERBOSE_DEBUGGING) {
    +    message = opt_message || '';
    +
    +    var info = goog.labs.format.csv.ParseError.findLineInfo_(text, index);
    +    if (info) {
    +      var lineNumber = info.lineIndex + 1;
    +      var columnNumber = index - info.line.startLineIndex + 1;
    +
    +      this.position = {
    +        line: lineNumber,
    +        column: columnNumber
    +      };
    +
    +      message += goog.string.subs(' at line %s column %s',
    +                                  lineNumber, columnNumber);
    +      message += '\n' + goog.labs.format.csv.ParseError.getLineDebugString_(
    +          info.line.getContent(), columnNumber);
    +    }
    +  }
    +
    +  goog.labs.format.csv.ParseError.base(this, 'constructor', message);
    +};
    +goog.inherits(goog.labs.format.csv.ParseError, goog.debug.Error);
    +
    +
    +/** @inheritDoc */
    +goog.labs.format.csv.ParseError.prototype.name = 'ParseError';
    +
    +
    +/**
    + * Calculate the line and column for an index in a string.
    + * TODO(nnaze): Consider moving to goog.string.newlines.
    + * @param {string} str A string.
    + * @param {number} index An index into the string.
    + * @return {?{line: !goog.string.newlines.Line, lineIndex: number}} The line
    + *     and index of the line.
    + * @private
    + */
    +goog.labs.format.csv.ParseError.findLineInfo_ = function(str, index) {
    +  var lines = goog.string.newlines.getLines(str);
    +  var lineIndex = goog.array.findIndex(lines, function(line) {
    +    return line.startLineIndex <= index && line.endLineIndex > index;
    +  });
    +
    +  if (goog.isNumber(lineIndex)) {
    +    var line = lines[lineIndex];
    +    return {
    +      line: line,
    +      lineIndex: lineIndex
    +    };
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Get a debug string of a line and a pointing caret beneath it.
    + * @param {string} str The string.
    + * @param {number} column The column to point at (1-indexed).
    + * @return {string} The debug line.
    + * @private
    + */
    +goog.labs.format.csv.ParseError.getLineDebugString_ = function(str, column) {
    +  var returnString = str + '\n';
    +  returnString += goog.string.repeat(' ', column - 1) + '^';
    +  return returnString;
    +};
    +
    +
    +/**
    + * A token -- a single-character string or a sentinel.
    + * @typedef {string|!goog.labs.format.csv.Sentinels_}
    + */
    +goog.labs.format.csv.Token;
    +
    +
    +/**
    + * Parses a CSV string to create a two-dimensional array.
    + *
    + * This function does not process header lines, etc -- such transformations can
    + * be made on the resulting array.
    + *
    + * @param {string} text The entire CSV text to be parsed.
    + * @param {boolean=} opt_ignoreErrors Whether to ignore parsing errors and
    + *      instead try to recover and keep going.
    + * @return {!Array<!Array<string>>} The parsed CSV.
    + */
    +goog.labs.format.csv.parse = function(text, opt_ignoreErrors) {
    +
    +  var index = 0;  // current char offset being considered
    +
    +
    +  var EOF = goog.labs.format.csv.Sentinels_.EOF;
    +  var EOR = goog.labs.format.csv.Sentinels_.EOR;
    +  var NEWLINE = goog.labs.format.csv.Sentinels_.NEWLINE;   // \r?\n
    +  var EMPTY = goog.labs.format.csv.Sentinels_.EMPTY;
    +
    +  var pushBackToken = null;   // A single-token pushback.
    +  var sawComma = false; // Special case for terminal comma.
    +
    +  /**
    +   * Push a single token into the push-back variable.
    +   * @param {goog.labs.format.csv.Token} t Single token.
    +   */
    +  function pushBack(t) {
    +    goog.labs.format.csv.assertToken_(t);
    +    goog.asserts.assert(goog.isNull(pushBackToken));
    +    pushBackToken = t;
    +  }
    +
    +  /**
    +   * @return {goog.labs.format.csv.Token} The next token in the stream.
    +   */
    +  function nextToken() {
    +
    +    // Give the push back token if present.
    +    if (pushBackToken != null) {
    +      var c = pushBackToken;
    +      pushBackToken = null;
    +      return c;
    +    }
    +
    +    // We're done. EOF.
    +    if (index >= text.length) {
    +      return EOF;
    +    }
    +
    +    // Give the next charater.
    +    var chr = text.charAt(index++);
    +    goog.labs.format.csv.assertToken_(chr);
    +
    +    // Check if this is a newline.  If so, give the new line sentinel.
    +    var isNewline = false;
    +    if (chr == '\n') {
    +      isNewline = true;
    +    } else if (chr == '\r') {
    +
    +      // This is a '\r\n' newline. Treat as single token, go
    +      // forward two indicies.
    +      if (index < text.length && text.charAt(index) == '\n') {
    +        index++;
    +      }
    +
    +      isNewline = true;
    +    }
    +
    +    if (isNewline) {
    +      return NEWLINE;
    +    }
    +
    +    return chr;
    +  }
    +
    +  /**
    +   * Read a quoted field from input.
    +   * @return {string} The field, as a string.
    +   */
    +  function readQuotedField() {
    +    // We've already consumed the first quote by the time we get here.
    +    var start = index;
    +    var end = null;
    +
    +    for (var token = nextToken(); token != EOF; token = nextToken()) {
    +      if (token == '"') {
    +        end = index - 1;
    +        token = nextToken();
    +
    +        // Two double quotes in a row.  Keep scanning.
    +        if (token == '"') {
    +          end = null;
    +          continue;
    +        }
    +
    +        // End of field.  Break out.
    +        if (token == ',' || token == EOF || token == NEWLINE) {
    +          if (token == NEWLINE) {
    +            pushBack(token);
    +          }
    +          break;
    +        }
    +
    +        if (!opt_ignoreErrors) {
    +          // Ignoring errors here means keep going in current field after
    +          // closing quote. E.g. "ab"c,d splits into abc,d
    +          throw new goog.labs.format.csv.ParseError(
    +              text, index - 1,
    +              'Unexpected character "' + token + '" after quote mark');
    +        } else {
    +          // Fall back to reading the rest of this field as unquoted.
    +          // Note: the rest is guaranteed not start with ", as that case is
    +          // eliminated above.
    +          var prefix = '"' + text.substring(start, index);
    +          var suffix = readField();
    +          if (suffix == EOR) {
    +            pushBack(NEWLINE);
    +            return prefix;
    +          } else {
    +            return prefix + suffix;
    +          }
    +        }
    +      }
    +    }
    +
    +    if (goog.isNull(end)) {
    +      if (!opt_ignoreErrors) {
    +        throw new goog.labs.format.csv.ParseError(
    +            text, text.length - 1,
    +            'Unexpected end of text after open quote');
    +      } else {
    +        end = text.length;
    +      }
    +    }
    +
    +    // Take substring, combine double quotes.
    +    return text.substring(start, end).replace(/""/g, '"');
    +  }
    +
    +  /**
    +   * Read a field from input.
    +   * @return {string|!goog.labs.format.csv.Sentinels_} The field, as a string,
    +   *     or a sentinel (if applicable).
    +   */
    +  function readField() {
    +    var start = index;
    +    var didSeeComma = sawComma;
    +    sawComma = false;
    +    var token = nextToken();
    +    if (token == EMPTY) {
    +      return EOR;
    +    }
    +    if (token == EOF || token == NEWLINE) {
    +      if (didSeeComma) {
    +        pushBack(EMPTY);
    +        return '';
    +      }
    +      return EOR;
    +    }
    +
    +    // This is the beginning of a quoted field.
    +    if (token == '"') {
    +      return readQuotedField();
    +    }
    +
    +    while (true) {
    +
    +      // This is the end of line or file.
    +      if (token == EOF || token == NEWLINE) {
    +        pushBack(token);
    +        break;
    +      }
    +
    +      // This is the end of record.
    +      if (token == ',') {
    +        sawComma = true;
    +        break;
    +      }
    +
    +      if (token == '"' && !opt_ignoreErrors) {
    +        throw new goog.labs.format.csv.ParseError(text, index - 1,
    +                                                  'Unexpected quote mark');
    +      }
    +
    +      token = nextToken();
    +    }
    +
    +
    +    var returnString = (token == EOF) ?
    +        text.substring(start) :  // Return to end of file.
    +        text.substring(start, index - 1);
    +
    +    return returnString.replace(/[\r\n]+/g, ''); // Squash any CRLFs.
    +  }
    +
    +  /**
    +   * Read the next record.
    +   * @return {!Array<string>|!goog.labs.format.csv.Sentinels_} A single record
    +   *     with multiple fields.
    +   */
    +  function readRecord() {
    +    if (index >= text.length) {
    +      return EOF;
    +    }
    +    var record = [];
    +    for (var field = readField(); field != EOR; field = readField()) {
    +      record.push(field);
    +    }
    +    return record;
    +  }
    +
    +  // Read all records and return.
    +  var records = [];
    +  for (var record = readRecord(); record != EOF; record = readRecord()) {
    +    records.push(record);
    +  }
    +  return records;
    +};
    +
    +
    +/**
    + * Sentinel tracking objects.
    + * @enum {!Object}
    + * @private
    + */
    +goog.labs.format.csv.Sentinels_ = {
    +  /** Empty field */
    +  EMPTY: {},
    +
    +  /** End of file */
    +  EOF: {},
    +
    +  /** End of record */
    +  EOR: {},
    +
    +  /** Newline. \r?\n */
    +  NEWLINE: {}
    +};
    +
    +
    +/**
    + * @param {string} str A string.
    + * @return {boolean} Whether the string is a single character.
    + * @private
    + */
    +goog.labs.format.csv.isCharacterString_ = function(str) {
    +  return goog.isString(str) && str.length == 1;
    +};
    +
    +
    +/**
    + * Assert the parameter is a token.
    + * @param {*} o What should be a token.
    + * @throws {goog.asserts.AssertionError} If {@ code} is not a token.
    + * @private
    + */
    +goog.labs.format.csv.assertToken_ = function(o) {
    +  if (goog.isString(o)) {
    +    goog.asserts.assertString(o);
    +    goog.asserts.assert(goog.labs.format.csv.isCharacterString_(o),
    +        'Should be a string of length 1 or a sentinel.');
    +  } else {
    +    goog.asserts.assert(
    +        goog.object.containsValue(goog.labs.format.csv.Sentinels_, o),
    +        'Should be a string of length 1 or a sentinel.');
    +  }
    +};
    +
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.html b/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.html
    new file mode 100644
    index 0000000000..7e342b3697
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +  <head>
    +    <title>csv.js unit tests</title>
    +    <script type="text/javascript"
    +        src="../../base.js">
    +    </script>
    +    <script>
    +      goog.require('goog.labs.format.csvTest');
    +    </script>
    +  </head>
    +  <body>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.js b/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.js
    new file mode 100644
    index 0000000000..79eeaa1588
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/format/csv_test.js
    @@ -0,0 +1,201 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.format.csvTest');
    +
    +goog.require('goog.labs.format.csv');
    +goog.require('goog.labs.format.csv.ParseError');
    +goog.require('goog.object');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.format.csvTest');
    +
    +
    +function testGoldenPath() {
    +  assertObjectEquals(
    +      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
    +      goog.labs.format.csv.parse('a,b,c\nd,e,f\ng,h,i\n'));
    +  assertObjectEquals(
    +      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
    +      goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n'));
    +}
    +
    +function testNoCrlfAtEnd() {
    +  assertObjectEquals(
    +      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
    +      goog.labs.format.csv.parse('a,b,c\nd,e,f\ng,h,i'));
    +}
    +
    +function testQuotes() {
    +  assertObjectEquals(
    +      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
    +      goog.labs.format.csv.parse('a,"b",c\n"d","e","f"\ng,h,"i"'));
    +  assertObjectEquals(
    +      [['a', 'b, as in boy', 'c'], ['d', 'e', 'f']],
    +      goog.labs.format.csv.parse('a,"b, as in boy",c\n"d","e","f"\n'));
    +}
    +
    +function testEmbeddedCrlfs() {
    +  assertObjectEquals(
    +      [['a', 'b\nball', 'c'], ['d\nd', 'e', 'f'], ['g', 'h', 'i']],
    +      goog.labs.format.csv.parse('a,"b\nball",c\n"d\nd","e","f"\ng,h,"i"\n'));
    +}
    +
    +function testEmbeddedQuotes() {
    +  assertObjectEquals(
    +      [['a', '"b"', 'Jonathan "Smokey" Feinberg'], ['d', 'e', 'f']],
    +      goog.labs.format.csv.parse(
    +          'a,"""b""","Jonathan ""Smokey"" Feinberg"\nd,e,f\r\n'));
    +}
    +
    +function testUnclosedQuote() {
    +  var e = assertThrows(function() {
    +    goog.labs.format.csv.parse('a,"b,c\nd,e,f');
    +  });
    +
    +  assertTrue(e instanceof goog.labs.format.csv.ParseError);
    +  assertEquals(2, e.position.line);
    +  assertEquals(5, e.position.column);
    +  assertEquals(
    +      'Unexpected end of text after open quote at line 2 column 5\n' +
    +      'd,e,f\n' +
    +      '    ^',
    +      e.message);
    +}
    +
    +function testQuotesInUnquotedField() {
    +  var e = assertThrows(function() {
    +    goog.labs.format.csv.parse('a,b "and" b,c\nd,e,f');
    +  });
    +
    +  assertTrue(e instanceof goog.labs.format.csv.ParseError);
    +
    +  assertEquals(1, e.position.line);
    +  assertEquals(5, e.position.column);
    +
    +  assertEquals(
    +      'Unexpected quote mark at line 1 column 5\n' +
    +      'a,b "and" b,c\n' +
    +      '    ^',
    +      e.message);
    +}
    +
    +function testGarbageOutsideQuotes() {
    +  var e = assertThrows(function() {
    +    goog.labs.format.csv.parse('a,"b",c\nd,"e"oops,f');
    +  });
    +
    +  assertTrue(e instanceof goog.labs.format.csv.ParseError);
    +  assertEquals(2, e.position.line);
    +  assertEquals(6, e.position.column);
    +  assertEquals(
    +      'Unexpected character "o" after quote mark at line 2 column 6\n' +
    +      'd,"e"oops,f\n' +
    +      '     ^',
    +      e.message);
    +}
    +
    +function testEmptyRecords() {
    +  assertObjectEquals(
    +      [['a', '', 'c'], ['d', 'e', ''], ['', '', '']],
    +      goog.labs.format.csv.parse('a,,c\r\nd,e,\n,,'));
    +}
    +
    +function testIgnoringErrors() {
    +  // The results of these tests are not defined by the RFC. They
    +  // generally strive to be "reasonable" while keeping the code simple.
    +
    +  // Quotes inside field
    +  assertObjectEquals(
    +      [['Hello "World"!', 'b'], ['c', 'd']], goog.labs.format.csv.parse(
    +          'Hello "World"!,b\nc,d', true));
    +
    +  // Missing closing quote
    +  assertObjectEquals(
    +      [['Hello', 'World!']], goog.labs.format.csv.parse(
    +          'Hello,"World!', true));
    +
    +  // Broken use of quotes in quoted field
    +  assertObjectEquals(
    +      [['a', '"Hello"World!"']], goog.labs.format.csv.parse(
    +          'a,"Hello"World!"', true));
    +
    +  // All of the above. A real mess.
    +  assertObjectEquals(
    +      [['This" is', '"very\n\tvery"broken"', ' indeed!']],
    +      goog.labs.format.csv.parse(
    +          'This" is,"very\n\tvery"broken"," indeed!', true));
    +}
    +
    +function testIgnoringErrorsTrailingTabs() {
    +  assertObjectEquals(
    +      [['"a\tb"\t'], ['c,d']], goog.labs.format.csv.parse(
    +          '"a\tb"\t\n"c,d"', true));
    +}
    +
    +function testFindLineInfo() {
    +  var testString = 'abc\ndef\rghi';
    +  var info = goog.labs.format.csv.ParseError.findLineInfo_(testString, 4);
    +
    +  assertEquals(4, info.line.startLineIndex);
    +  assertEquals(7, info.line.endContentIndex);
    +  assertEquals(8, info.line.endLineIndex);
    +
    +  assertEquals(1, info.lineIndex);
    +}
    +
    +function testGetLineDebugString() {
    +  var str = 'abcdefghijklmnop';
    +  var index = str.indexOf('j');
    +  var column = index + 1;
    +  assertEquals(
    +      goog.labs.format.csv.ParseError.getLineDebugString_(str, column),
    +      'abcdefghijklmnop\n' +
    +      '         ^');
    +
    +}
    +
    +function testIsCharacterString() {
    +  assertTrue(goog.labs.format.csv.isCharacterString_('a'));
    +  assertTrue(goog.labs.format.csv.isCharacterString_('\n'));
    +  assertTrue(goog.labs.format.csv.isCharacterString_(' '));
    +
    +  assertFalse(goog.labs.format.csv.isCharacterString_(null));
    +  assertFalse(goog.labs.format.csv.isCharacterString_('  '));
    +  assertFalse(goog.labs.format.csv.isCharacterString_(''));
    +  assertFalse(goog.labs.format.csv.isCharacterString_('aa'));
    +}
    +
    +
    +function testAssertToken() {
    +  goog.labs.format.csv.assertToken_('a');
    +
    +  goog.object.forEach(goog.labs.format.csv.SENTINELS_,
    +      function(value) {
    +        goog.labs.format.csv.assertToken_(value);
    +      });
    +
    +  assertThrows(function() {
    +    goog.labs.format.csv.assertToken_('aa');
    +  });
    +
    +  assertThrows(function() {
    +    goog.labs.format.csv.assertToken_('');
    +  });
    +
    +  assertThrows(function() {
    +    goog.labs.format.csv.assertToken_({});
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/html/attribute_rewriter.js b/mapbox-gl/closure-library/closure/goog/labs/html/attribute_rewriter.js
    new file mode 100644
    index 0000000000..dec1cc2c42
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/html/attribute_rewriter.js
    @@ -0,0 +1,74 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.labs.html.AttributeRewriter');
    +goog.provide('goog.labs.html.AttributeValue');
    +goog.provide('goog.labs.html.attributeRewriterPresubmitWorkaround');
    +
    +
    +/**
    + * The type of an attribute value.
    + * <p>
    + * Many HTML attributes contain structured data like URLs, CSS, or even entire
    + * HTML documents, so the type is a union of several variants.
    + *
    + * @typedef {(string |
    + *            goog.html.SafeHtml | goog.html.SafeStyle | goog.html.SafeUrl)}
    + */
    +goog.labs.html.AttributeValue;
    +
    +
    +/**
    + * A function that takes an attribute value, and returns a safe value.
    + * <p>
    + * Since rewriters can be chained, a rewriter must be able to accept the output
    + * of another rewriter, instead of just a string though a rewriter that coerces
    + * its input to a string before checking its safety will fail safe.
    + * <p>
    + * The meaning of the result is:
    + * <table>
    + *   <tr><td>{@code null}</td>
    + *       <td>Unsafe.  The attribute should not be output.</tr>
    + *   <tr><td>a string</td>
    + *       <td>The plain text (not HTML-entity encoded) of a safe attribute
    + *           value.</td>
    + *   <tr><td>a {@link goog.html.SafeHtml}</td>
    + *       <td>A fragment that is safe to be included as embedded HTML as in
    + *           {@code <iframe srchtml="...">}</td></tr>
    + *   <tr><td>a {@link goog.html.SafeUrl}</td>
    + *       <td>A URL that does not need to be further checked against the URL
    + *           white-list.</td></tr>
    + *   <tr><td>a {@link goog.html.SafeStyle}</td>
    + *       <td>A safe value for a <code>style="..."</code> attribute.</td></tr>
    + * </table>
    + * <p>
    + * Implementations are responsible for making sure that "safe" complies with
    + * the contract established by the safe string types in {@link goog.html}.
    + * </p>
    + *
    + * @typedef {function(goog.labs.html.AttributeValue) :
    + *           goog.labs.html.AttributeValue}
    + */
    +goog.labs.html.AttributeRewriter;
    +
    +
    +/**
    + * g4 presubmit complains about requires of this file because its clients
    + * don't use any symbols from it outside JSCompiler comment annotations.
    + * genjsdeps.sh doesn't generate the right dependency graph unless this
    + * file is required.
    + * Clients can mention this noop.
    + */
    +goog.labs.html.attributeRewriterPresubmitWorkaround = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer.js b/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer.js
    new file mode 100644
    index 0000000000..10155c1fcc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer.js
    @@ -0,0 +1,392 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview
    + * An HTML sanitizer that takes untrusted HTML snippets and produces
    + * safe HTML by filtering/rewriting tags and attributes that contain
    + * high-privilege instructions.
    + */
    +
    +
    +goog.provide('goog.labs.html.Sanitizer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.labs.html.attributeRewriterPresubmitWorkaround');
    +goog.require('goog.labs.html.scrubber');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * A sanitizer that converts untrusted, messy HTML into more regular HTML
    + * that cannot abuse high-authority constructs like the ability to execute
    + * arbitrary JavaScript.
    + * @constructor
    + */
    +goog.labs.html.Sanitizer = function() {
    +  /**
    +   * Maps the lower-case names of allowed elements to attribute white-lists.
    +   * An attribute white-list maps lower-case attribute names to functions
    +   * from values to values or undefined to disallow.
    +   *
    +   * The special element name {@code "*"} contains a white-list of attributes
    +   * allowed on any tag, which is useful for attributes like {@code title} and
    +   * {@code id} which are widely available with element-agnostic meanings.
    +   * It should not be used for attributes like {@code type} whose meaning
    +   * differs based on the element on which it appears:
    +   * e.g. {@code <input type=text>} vs {@code <style type=text/css>}.
    +   *
    +   * @type {!Object<string, !Object<string, goog.labs.html.AttributeRewriter>>}
    +   * @private
    +   */
    +  this.whitelist_ = goog.labs.html.Sanitizer.createBlankObject_();
    +  this.whitelist_['*'] = goog.labs.html.Sanitizer.createBlankObject_();
    +
    +  // To use the sanitizer, we build inputs for the scrubber.
    +  // These inputs are invalidated by changes to the policy, so we (re)build them
    +  // lazily.
    +
    +  /**
    +   * Maps element names to {@code true} so the scrubber does not have to do
    +   * own property checks for every tag filtered.
    +   *
    +   * Built lazily and invalidated when the white-list is modified.
    +   *
    +   * @type {Object<string, boolean>}
    +   * @private
    +   */
    +  this.allowedElementSet_ = null;
    +};
    +
    +
    +// TODO(user): Should the return type be goog.html.SafeHtml?
    +// If we receive a safe HTML string as input, should we simply rebalance
    +// tags?
    +/**
    + * Yields a string of safe HTML that contains all and only the safe
    + * text-nodes and elements in the input.
    + *
    + * <p>
    + * For the purposes of this function, "safe" is defined thus:
    + * <ul>
    + *   <li>Contains only elements explicitly allowed via {@code this.allow*}.
    + *   <li>Contains only attributes explicitly allowed via {@code this.allow*}
    + *       and having had all relevant transformations applied.
    + *   <li>Contains an end tag for all and only non-void open tags.
    + *   <li>Tags nest per XHTML rules.
    + *   <li>Tags do not nest beyond a finite but fairly large level.
    + * </ul>
    + *
    + * @param {!string} unsafeHtml A string of HTML which need not originate with
    + *    a trusted source.
    + * @return {!string} A string of HTML that contains only tags and attributes
    + *    explicitly allowed by this sanitizer, and with end tags for all and only
    + *    non-void elements.
    + */
    +goog.labs.html.Sanitizer.prototype.sanitize = function(unsafeHtml) {
    +  var unsafeHtmlString = '' + unsafeHtml;
    +
    +  /**
    +   * @type {!Object<string, !Object<string, goog.labs.html.AttributeRewriter>>}
    +   */
    +  var whitelist = this.whitelist_;
    +  if (!this.allowedElementSet_) {
    +    this.allowedElementSet_ = goog.object.createSet(
    +        // This can lead to '*' in the allowed element set, but the scrubber
    +        // will not parse "<*" as a tag beginning.
    +        goog.object.getKeys(whitelist));
    +  }
    +
    +  return goog.labs.html.scrubber.scrub(
    +      this.allowedElementSet_, whitelist, unsafeHtmlString);
    +};
    +
    +
    +/**
    + * Adds the element names to the white-list of elements that are allowed
    + * in the safe HTML output.
    + * <p>
    + * Allowing elements does not, by itself, allow any attributes on
    + * those elements.
    + *
    + * @param {...!string} var_args element names that should be allowed in the
    + *     safe HTML output.
    + * @return {!goog.labs.html.Sanitizer} {@code this}.
    + */
    +goog.labs.html.Sanitizer.prototype.allowElements = function(var_args) {
    +  this.allowedElementSet_ = null;  // Invalidate.
    +  var whitelist = this.whitelist_;
    +  for (var i = 0; i < arguments.length; ++i) {
    +    var elementName = arguments[i].toLowerCase();
    +
    +    goog.asserts.assert(
    +        goog.labs.html.Sanitizer.isValidHtmlName_(elementName), elementName);
    +
    +    if (!Object.prototype.hasOwnProperty.call(whitelist, elementName)) {
    +      whitelist[elementName] = goog.labs.html.Sanitizer.createBlankObject_();
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Allows in the sanitized output
    + * <tt>&lt;<i>element</i> <i>attr</i>="..."&gt;</tt>
    + * when <i>element</i> is in {@code elementNames} and
    + * <i>attrNames</i> is in {@code attrNames}.
    + *
    + * If specified, {@code opt_valueXform} is a function that takes the
    + * HTML-entity-decoded attribute value, and can choose to disallow the
    + * attribute by returning {@code null} or substitute a new value
    + * by returning a string with the new value.
    + *
    + * @param {!Array<string>|string} elementNames names (or name) on which the
    + *     attributes are allowed.
    + *
    + *     Element names should be allowed via {@code allowElements(...)} prior
    + *     to white-listing attributes.
    + *
    + *     The special element name {@code "*"} has the same meaning as in CSS
    + *     selectors: it can be used to white-list attributes like {@code title}
    + *     and {@code id} which are widely available with element-agnostic
    + *     meanings.
    + *
    + *     It should not be used for attributes like {@code type} whose meaning
    + *     differs based on the element on which it appears:
    + *     e.g. {@code <input type=text>} vs {@code <style type=text/css>}.
    + *
    + * @param {!Array<string>|string} attrNames names (or name) of the attribute
    + *     that should be allowed.
    + *
    + * @param {goog.labs.html.AttributeRewriter=} opt_rewriteValue A function
    + *     that receives the HTML-entity-decoded attribute value and can return
    + *     {@code null} to disallow the attribute entirely or the value for the
    + *     attribute as a string.
    + *     <p>
    + *     The default is the identity function ({@code function(x){return x}}),
    + *     and the value rewriter is composed with an attribute specific handler:
    + *     <table>
    + *      <tr>
    + *        <th>href, src</th>
    + *        <td>Requires that the value be an absolute URL with a protocol in
    + *            (http, https, mailto) or a protocol relative URL.
    + *      </tr>
    + *     </table>
    + *
    + * @return {!goog.labs.html.Sanitizer} {@code this}.
    + */
    +goog.labs.html.Sanitizer.prototype.allowAttributes =
    +    function(elementNames, attrNames, opt_rewriteValue) {
    +  if (!goog.isArray(elementNames)) {
    +    elementNames = [elementNames];
    +  }
    +  if (!goog.isArray(attrNames)) {
    +    attrNames = [attrNames];
    +  }
    +  goog.asserts.assert(
    +      !opt_rewriteValue || 'function' === typeof opt_rewriteValue,
    +      'opt_rewriteValue should be a function');
    +
    +  var whitelist = this.whitelist_;
    +  for (var ei = 0; ei < elementNames.length; ++ei) {
    +    var elementName = elementNames[ei].toLowerCase();
    +    goog.asserts.assert(
    +        goog.labs.html.Sanitizer.isValidHtmlName_(elementName) ||
    +        '*' === elementName,
    +        elementName);
    +    // If the element has not been white-listed then panic.
    +    // TODO(user): allow allow{Elements,Attributes} to be called in any
    +    // order if someone needs it.
    +    if (!Object.prototype.hasOwnProperty.call(whitelist, elementName)) {
    +      throw new Error(elementName);
    +    }
    +    var attrWhitelist = whitelist[elementName];
    +    for (var ai = 0, an = attrNames.length; ai < an; ++ai) {
    +      var attrName = attrNames[ai].toLowerCase();
    +      goog.asserts.assert(
    +          goog.labs.html.Sanitizer.isValidHtmlName_(attrName), attrName);
    +
    +      // If the value has already been allowed, then chain the rewriters
    +      // so that both white-listers concerns are met.
    +      // We do not use the default rewriter here since it should have
    +      // been introduced by the call that created the initial white-list
    +      // entry.
    +      attrWhitelist[attrName] = goog.labs.html.Sanitizer.chain_(
    +          opt_rewriteValue || goog.labs.html.Sanitizer.valueIdentity_,
    +          Object.prototype.hasOwnProperty.call(attrWhitelist, attrName) ?
    +              attrWhitelist[attrName] :
    +              goog.labs.html.Sanitizer.defaultRewriterForAttr_(attrName));
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * A new object that is as blank as possible.
    + *
    + * Using {@code Object.create} to create an object with
    + * no prototype speeds up whitelist access since there's fewer prototypes
    + * to fall-back to for a common case where an element is not in the
    + * white-list, and reduces the chance of confusing a member of
    + * {@code Object.prototype} with a whitelist entry.
    + *
    + * @return {!Object<string, ?>} a reference to a newly allocated object that
    + *    does not alias any reference that existed prior.
    + * @private
    + */
    +goog.labs.html.Sanitizer.createBlankObject_ = function() {
    +  return (Object.create || Object)(null);
    +};
    +
    +
    +/**
    + * HTML element and attribute names may be almost arbitrary strings, but the
    + * sanitizer is more restrictive as to what can be white-listed.
    + *
    + * Since HTML is case-insensitive, only lower-case identifiers composed of
    + * ASCII letters, digits, and select punctuation are allowed.
    + *
    + * @param {string} name
    + * @return {boolean} true iff name is a valid white-list key.
    + * @private
    + */
    +goog.labs.html.Sanitizer.isValidHtmlName_ = function(name) {
    +  return 'string' === typeof name &&  // Names must be strings.
    +      // Names must be lower-case and ASCII identifier chars only.
    +      /^[a-z][a-z0-9\-:]*$/.test(name);
    +};
    +
    +
    +/**
    + * @param  {goog.labs.html.AttributeValue} x
    + * @return {goog.labs.html.AttributeValue}
    + * @private
    + */
    +goog.labs.html.Sanitizer.valueIdentity_ = function(x) {
    +  return x;
    +};
    +
    +
    +/**
    + * @param  {goog.labs.html.AttributeValue} x
    + * @return {null}
    + * @private
    + */
    +goog.labs.html.Sanitizer.disallow_ = function(x) {
    +  return null;
    +};
    +
    +
    +/**
    + * Chains attribute rewriters.
    + *
    + * @param  {goog.labs.html.AttributeRewriter} f
    + * @param  {goog.labs.html.AttributeRewriter} g
    + * @return {goog.labs.html.AttributeRewriter}
    + *      a function that return g(f(x)) or null if f(x) is null.
    + * @private
    + */
    +goog.labs.html.Sanitizer.chain_ = function(f, g) {
    +  // Sometimes white-listing code ends up allowing things multiple times.
    +  if (f === goog.labs.html.Sanitizer.valueIdentity_) {
    +    return g;
    +  }
    +  if (g === goog.labs.html.Sanitizer.valueIdentity_) {
    +    return f;
    +  }
    +  // If someone tries to white-list a really problematic value, we reject
    +  // it by returning disallow_.  Disallow it quickly.
    +  if (f === goog.labs.html.Sanitizer.disallow_) {
    +    return f;
    +  }
    +  if (g === goog.labs.html.Sanitizer.disallow_) {
    +    return g;
    +  }
    +  return (
    +      /**
    +       * @param {goog.labs.html.AttributeValue} x
    +       * @return {goog.labs.html.AttributeValue}
    +       */
    +      function(x) {
    +        var y = f(x);
    +        return y != null ? g(y) : null;
    +      });
    +};
    +
    +
    +/**
    + * Given an attribute name, returns a value rewriter that enforces some
    + * minimal safety properties.
    + *
    + * <p>
    + * For url atributes, it checks that any protocol is on a safe set that
    + * doesn't allow script execution.
    + * <p>
    + * It also blanket disallows CSS and event handler attributes.
    + *
    + * @param  {string} attrName lower-cased attribute name.
    + * @return {goog.labs.html.AttributeRewriter}
    + * @private
    + */
    +goog.labs.html.Sanitizer.defaultRewriterForAttr_ = function(attrName) {
    +  if ('href' === attrName || 'src' === attrName) {
    +    return goog.labs.html.Sanitizer.checkUrl_;
    +  } else if ('style' === attrName || 'on' === attrName.substr(0, 2)) {
    +    // TODO(user): delegate to a CSS sanitizer if one is available.
    +    return goog.labs.html.Sanitizer.disallow_;
    +  }
    +  return goog.labs.html.Sanitizer.valueIdentity_;
    +};
    +
    +
    +/**
    + * Applied automatically to URL attributes to check that they are safe as per
    + * {@link SafeUrl}.
    + *
    + * @param {goog.labs.html.AttributeValue} attrValue a decoded attribute value.
    + * @return {goog.html.SafeUrl | null} a URL that is equivalent to the
    + *    input or {@code null} if the input is not a safe URL.
    + * @private
    + */
    +goog.labs.html.Sanitizer.checkUrl_ = function(attrValue) {
    +  if (attrValue == null) {
    +    return null;
    +  }
    +  /** @type {!goog.html.SafeUrl} */
    +  var safeUrl;
    +  if (attrValue instanceof goog.html.SafeUrl) {
    +    safeUrl = /** @type {!goog.html.SafeUrl} */ (attrValue);
    +  } else {
    +    if (typeof attrValue === 'string') {
    +      // Whitespace at the ends of URL-valued attributes in HTML is ignored.
    +      attrValue = goog.string.trim(/** @type {string} */ (attrValue));
    +    }
    +    safeUrl = goog.html.SafeUrl.sanitize(
    +        /** @type {!goog.string.TypedString | string} */ (attrValue));
    +  }
    +  if (goog.html.SafeUrl.unwrap(safeUrl) == goog.html.SafeUrl.INNOCUOUS_STRING) {
    +    return null;
    +  } else {
    +    return safeUrl;
    +  }
    +};
    +
    +
    +goog.labs.html.attributeRewriterPresubmitWorkaround();
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer_test.js b/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer_test.js
    new file mode 100644
    index 0000000000..d7846fac9d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/html/sanitizer_test.js
    @@ -0,0 +1,270 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.labs.html.SanitizerTest');
    +
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.labs.html.Sanitizer');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.html.SanitizerTest');
    +
    +
    +var JENNYS_PHONE_NUMBER = goog.html.SafeUrl.fromConstant(
    +    goog.string.Const.from('tel:867-5309'));
    +
    +
    +var sanitizer = new goog.labs.html.Sanitizer()
    +    .allowElements(
    +        'a', 'b', 'i', 'p', 'font', 'hr', 'br', 'span',
    +        'ol', 'ul', 'li',
    +        'table', 'tr', 'td', 'th', 'tbody',
    +        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
    +        'img',
    +        'html', 'head', 'body', 'title'
    +    )
    +    // allow unfiltered title attributes, and
    +    .allowAttributes('*', 'title')
    +    // specific dir values.
    +    .allowAttributes(
    +        '*', 'dir',
    +        function(dir) { return dir === 'ltr' || dir === 'rtl' ? dir : null; })
    +    .allowAttributes(
    +        // Specifically on <a> elements,
    +        'a',
    +        // allow an href but verify and rewrite, and
    +        'href',
    +        function(href) {
    +          if (href === 'tel:867-5309') {
    +            return JENNYS_PHONE_NUMBER;
    +          }
    +          // Missing anchor is an intentional error.
    +          return /https?:\/\/google\.[a-z]{2,3}\/search\?/.test(String(href)) ?
    +              href : null;
    +        })
    +    .allowAttributes(
    +        'a',
    +        // mask the generic title handler for no good reason.
    +        'title',
    +        function(title) { return '<' + title + '>'; });
    +
    +
    +function run(input, golden, desc) {
    +  var actual = sanitizer.sanitize(input);
    +  assertEquals(desc, golden, actual);
    +}
    +
    +
    +function testEmptyString() {
    +  run('', '', 'Empty string');
    +}
    +
    +function testHelloWorld() {
    +  run('Hello, <b>World</b>!', 'Hello, <b>World</b>!',
    +      'Hello World');
    +}
    +
    +function testNoEndTag() {
    +  run('<i>Hello, <b>World!',
    +      '<i>Hello, <b>World!</b></i>',
    +      'Hello World no end tag');
    +}
    +
    +function testUnclosedTags() {
    +  run('<html><head><title>Hello, <<World>>!</TITLE>' +
    +      '</head><body><p>Hello,<Br><<World>>!',
    +      '<html><head><title>Hello, <<World>>!</title>' +
    +      '</head><body><p>Hello,<br>&lt;&gt;!</p></body></html>',
    +      'RCDATA content, different case, unclosed tags');
    +}
    +
    +function testListInList() {
    +  run('<ul><li>foo</li><ul><li>bar</li></ul></ul>',
    +      '<ul><li>foo</li><li><ul><li>bar</li></ul></li></ul>',
    +      'list in list directly');
    +}
    +
    +function testHeaders() {
    +  run('<h1>header</h1>body' +
    +      '<H2>sub-header</h3>sub-body' +
    +      '<h3>sub-sub-</hr>header<hr></hr>sub-sub-body</H4></h2>',
    +      '<h1>header</h1>body' +
    +      '<h2>sub-header</h2>sub-body' +
    +      '<h3>sub-sub-header</h3><hr>sub-sub-body',
    +      'headers');
    +}
    +
    +function testListNesting() {
    +  run('<ul><li><ul><li>foo</li></li><ul><li>bar',
    +      '<ul><li><ul><li>foo</li><li><ul><li>bar</li></ul></li></ul></li></ul>',
    +      'list nesting');
    +}
    +
    +function testTableNesting() {
    +  run('<table><tbody><tr><td>foo</td><table><tbody><tr><th>bar</table></table>',
    +      '<table><tbody><tr><td>foo</td><td>' +
    +      '<table><tbody><tr><th>bar</th></tr></tbody></table>' +
    +      '</td></tr></tbody></table>',
    +      'table nesting');
    +}
    +
    +function testNestingLimit() {
    +  run(goog.string.repeat('<span>', 264) + goog.string.repeat('</span>', 264),
    +      goog.string.repeat('<span>', 256) + goog.string.repeat('</span>', 256),
    +      '264 open spans');
    +}
    +
    +function testTableScopes() {
    +  run('<html><head></head><body><p>Hi</p><p>How are you</p>\n' +
    +      '<p><table><tbody><tr>' +
    +      '<td><b><font><font><p>Cell</b></font></font></p>\n</td>' +
    +      '<td><b><font><font><p>Cell</b></font></font></p>\n</td>' +
    +      '</tr></tbody></table></p>\n' +
    +      '<p>x</p></body></html>',
    +
    +      '<html><head></head><body><p>Hi</p><p>How are you</p>\n' +
    +      '<p><table><tbody><tr>' +
    +      '<td><b><font><font></font></font></b><p>Cell</p>\n</td>' +
    +      // The close </p> tag does not close the whole table. +
    +      '<td><b><font><font></font></font></b><p>Cell</p>\n</td>' +
    +      '</tr></tbody></table></p>\n' +
    +      '<p>x</p></body></html>',
    +
    +      'Table Scopes');
    +}
    +
    +function testConcatSafe() {
    +  run('<<applet>script<applet>>alert(1337)<<!-- -->/script<?...?>>',
    +      '&lt;script&gt;alert(1337)&lt;/script&gt;',
    +      'Concat safe');
    +}
    +
    +function testPrototypeMembersDoNotInfectTables() {
    +  // Constructor is all lower-case so will survive tag name
    +  // normalization.
    +  run('<constructor>Foo</constructor>', 'Foo',
    +      'Object.prototype members');
    +}
    +
    +function testGenericAttributesAllowed() {
    +  run('<span title=howdy></span>', '<span title="howdy"></span>',
    +      'generic attrs allowed');
    +}
    +
    +function testValueWhitelisting() {
    +  run('<span dir=\'ltr\'>LTR</span><span dir=\'evil\'>Evil</span>',
    +      '<span dir="ltr">LTR</span><span>Evil</span>',
    +      'value whitelisted');
    +}
    +
    +function testAttributeNormalization() {
    +  run('<a href="http://google.com/search?q=tests suxor&hl=en">Click</a>',
    +      '<a href="http://google.com/search?q=tests%20suxor&amp;hl=en">Click</a>',
    +      'URL normalized');
    +}
    +
    +function testNaiveAttributeRewriterCaught() {
    +  run('<a href="javascript:http://google.com/&#10;alert(1337)">sneaky</a>',
    +      '<a>sneaky</a>',
    +      'Safety net saves naive attribute rewriters');
    +}
    +
    +function testSafeUrlFromAttributeRewriter() {
    +  run('<a href="tel:867-5309">Jenny</a>', '<a href="tel:867-5309">Jenny</a>',
    +      'Attribute rewriter escapes safety checks via SafeURL');
    +}
    +
    +function testTagSpecificityOfAttributeFiltering() {
    +  run('<img href="http://google.com/search?q=tests+suxor">',
    +      '<img>',
    +      'href blocked on img');
    +}
    +
    +function testTagSpecificAttributeFiltering() {
    +  run('<a href="http://google.evil.com/search?q=tests suxor">Unclicky</a>',
    +      '<a>Unclicky</a>',
    +      'bad href value blocked');
    +}
    +
    +function testNonWhitelistFunctionsNotCalled() {
    +  var called = false;
    +  Object.prototype.dontcallme = function() {
    +    called = true;
    +    return 'dontcallme was called despite being on the prototype';
    +  };
    +  try {
    +    run('<span dontcallme="I\'ll call you">Lorem Ipsum',
    +        '<span>Lorem Ipsum</span>',
    +        'non white-list fn not called');
    +  } finally {
    +    delete Object.prototype.dontcallme;
    +  }
    +  assertFalse('Object.prototype.dontcallme should not have been called',
    +              called);
    +}
    +
    +function testQuotesInAttributeValue() {
    +  run('<span tItlE =\n\'Quoth the raven, "Nevermore"\'>Lorem Ipsum',
    +      '<span title="Quoth the raven, &quot;Nevermore&quot;">Lorem Ipsum</span>',
    +      'quotes in attr value');
    +}
    +
    +function testAttributesNeverMentionedAreDropped() {
    +  run('<b onclick="evil=true">evil</b>', '<b>evil</b>', 'attrs white-listed');
    +}
    +
    +function testAttributesNotOverEscaped() {
    +  run('<I TITLE="Foo &AMP; Bar & Baz">/</I>',
    +      '<i title="Foo &amp; Bar &amp; Baz">/</i>',
    +      'attr value not over-escaped');
    +}
    +
    +function testTagSpecificRulesTakePrecedence() {
    +  run('<a title=zogberts>Link</a>',
    +      '<a title="&lt;zogberts&gt;">Link</a>',
    +      'tag specific rules take precedence');
    +}
    +
    +function testAttributeRejectionLocalized() {
    +  run('<a id=foo href =//evil.org/ title=>Link</a>',
    +      '<a title="&lt;&gt;">Link</a>',
    +      'failure of one attribute does not torpedo others');
    +}
    +
    +function testWeirdHtmlRulesFollowedForAttrValues() {
    +  run('<span title= id=>Lorem Ipsum</span>',
    +      '<span title=\"id=\">Lorem Ipsum</span>',
    +      'same as browser on weird values');
    +}
    +
    +function testAttributesDisallowedOnCloseTags() {
    +  run('<h1 title="open">Header</h1 title="closed">',
    +      '<h1 title="open">Header</h1>',
    +      'attributes on close tags');
    +}
    +
    +function testRoundTrippingOfHtmlSafeAgainstIEBacktickProblems() {
    +  // Introducing a space at the end of an attribute forces IE to quote it when
    +  // turning a DOM into innerHTML which protects against a bunch of problems
    +  // with backticks since IE treats them as attribute value delimiters, allowing
    +  //   foo.innerHTML += ...
    +  // to continue to "work" without introducing an XSS vector.
    +  // Adding a space at the end is innocuous since HTML attributes whose values
    +  // are structured content ignore spaces at the beginning or end.
    +  run('<span title="`backtick">*</span>', '<span title="`backtick ">*</span>',
    +      'not round-trippable on IE');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/html/scrubber.js b/mapbox-gl/closure-library/closure/goog/labs/html/scrubber.js
    new file mode 100644
    index 0000000000..a5fbc822d9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/html/scrubber.js
    @@ -0,0 +1,1027 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview
    + * HTML tag filtering, and balancing.
    + * A more user-friendly API is exposed via {@code goog.labs.html.sanitizer}.
    + * @visibility {//visibility:private}
    + */
    +
    +
    +goog.provide('goog.labs.html.scrubber');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom.tags');
    +goog.require('goog.labs.html.attributeRewriterPresubmitWorkaround');
    +goog.require('goog.string');
    +
    +
    +/**
    + * Replaces tags not on the white-list with empty text nodes, dropping all
    + * attributes, and drops other non-text nodes such as comments.
    + *
    + * @param {!Object<string, boolean>} tagWhitelist a set of lower-case tag names
    + *    following the convention established by {@link goog.object.createSet}.
    + * @param {!Object<string, Object<string, goog.labs.html.AttributeRewriter>>}
    + *        attrWhitelist
    + *    maps lower-case tag names and the special string {@code "*"} to functions
    + *    from decoded attribute values to sanitized values or {@code null} to
    + *    indicate that the attribute is not allowed with that value.
    + *
    + *    For example, if {@code attrWhitelist['a']['href']} is defined then it
    + *    is used to sanitize the value of the link's URL.
    + *
    + *    If {@code attrWhitelist['*']['id']} is defined, and
    + *    {@code attrWhitelist['div']['id']} is not, then the former is used to
    + *    sanitize any {@code id} attribute on a {@code <div>} element.
    + * @param {string} html a string of HTML
    + * @return {string} the input but with potentially dangerous tokens removed.
    + */
    +goog.labs.html.scrubber.scrub = function(tagWhitelist, attrWhitelist, html) {
    +  return goog.labs.html.scrubber.render_(
    +      goog.labs.html.scrubber.balance_(
    +          goog.labs.html.scrubber.filter_(
    +              tagWhitelist,
    +              attrWhitelist,
    +              goog.labs.html.scrubber.lex_(html))));
    +};
    +
    +
    +/**
    + * Balances tags in trusted HTML.
    + * @param {string} html a string of HTML
    + * @return {string} the input but with an end-tag for each non-void start tag
    + *     and only for non-void start tags, and with start and end tags nesting
    + *     properly.
    + */
    +goog.labs.html.scrubber.balance = function(html) {
    +  return goog.labs.html.scrubber.render_(
    +      goog.labs.html.scrubber.balance_(
    +          goog.labs.html.scrubber.lex_(html)));
    +};
    +
    +
    +/** Character code constant for {@code '<'}.  @private */
    +goog.labs.html.scrubber.CC_LT_ = '<'.charCodeAt(0);
    +
    +
    +/** Character code constant for {@code '!'}.  @private */
    +goog.labs.html.scrubber.CC_BANG_ = '!'.charCodeAt(0);
    +
    +
    +/** Character code constant for {@code '/'}.  @private */
    +goog.labs.html.scrubber.CC_SLASH_ = '/'.charCodeAt(0);
    +
    +
    +/** Character code constant for {@code '?'}.  @private */
    +goog.labs.html.scrubber.CC_QMARK_ = '?'.charCodeAt(0);
    +
    +
    +/**
    + * Matches content following a tag name or attribute value, and before the
    + * beginning of the next attribute value.
    + * @private
    + */
    +goog.labs.html.scrubber.ATTR_VALUE_PRECEDER_ = '[^=>]+';
    +
    +
    +/** @private */
    +goog.labs.html.scrubber.UNQUOTED_ATTR_VALUE_ = '(?:[^"\'\\s>][^\\s>]*)';
    +
    +
    +/** @private */
    +goog.labs.html.scrubber.DOUBLE_QUOTED_ATTR_VALUE_ = '(?:"[^"]*"?)';
    +
    +
    +/** @private */
    +goog.labs.html.scrubber.SINGLE_QUOTED_ATTR_VALUE_ = "(?:'[^']*'?)";
    +
    +
    +/**
    + * Matches the equals-sign and any attribute value following it, but does not
    + * capture any {@code >} that would close the tag.
    + * @private
    + */
    +goog.labs.html.scrubber.ATTR_VALUE_ = '=\\s*(?:' +
    +    goog.labs.html.scrubber.UNQUOTED_ATTR_VALUE_ +
    +    '|' + goog.labs.html.scrubber.DOUBLE_QUOTED_ATTR_VALUE_ +
    +    '|' + goog.labs.html.scrubber.SINGLE_QUOTED_ATTR_VALUE_ + ')?';
    +
    +
    +/**
    + * The body of a tag between the end of the name and the closing {@code >}
    + * if any.
    + * @private
    + */
    +goog.labs.html.scrubber.ATTRS_ =
    +    '(?:' + goog.labs.html.scrubber.ATTR_VALUE_PRECEDER_ +
    +    '|' + goog.labs.html.scrubber.ATTR_VALUE_ + ')*';
    +
    +
    +/**
    + * A character that continues a tag name as defined at
    + * http://www.w3.org/html/wg/drafts/html/master/syntax.html#tag-name-state
    + * @private
    + */
    +goog.labs.html.scrubber.TAG_NAME_CHAR_ = '[^\t\f\n />]';
    +
    +
    +/**
    + * Matches when the next character cannot continue a tag name.
    + * @private
    + */
    +goog.labs.html.scrubber.BREAK_ =
    +    '(?!' + goog.labs.html.scrubber.TAG_NAME_CHAR_ + ')';
    +
    +
    +/**
    + * Matches the open tag and body of a special element :
    + * one whose body cannot contain nested elements so uses special parsing rules.
    + * It does not include the end tag.
    + * @private
    + */
    +goog.labs.html.scrubber.SPECIAL_ELEMENT_ = '<(?:' +
    +    // Special tag name.
    +    '(iframe|script|style|textarea|title|xmp)' +
    +    // End of tag name
    +    goog.labs.html.scrubber.BREAK_ +
    +    // Attributes
    +    goog.labs.html.scrubber.ATTRS_ + '>' +
    +    // Element content includes non '<' characters, and
    +    // '<' that don't start a matching end tag.
    +    // This uses a back-reference to the tag name to determine whether
    +    // the tag names match.
    +    // Since matching is case-insensitive, this can only be used in
    +    // a case-insensitive regular expression.
    +    // JavaScript does not treat Turkish dotted I's as equivalent to their
    +    // ASCII equivalents.
    +    '(?:[^<]|<(?!/\\1' + goog.labs.html.scrubber.BREAK_ + '))*' +
    +    ')';
    +
    +
    +/**
    + * Regexp pattern for an HTML tag.
    + * @private
    + */
    +goog.labs.html.scrubber.TAG_ =
    +    '<[/]?[a-z]' + goog.labs.html.scrubber.TAG_NAME_CHAR_ + '*' +
    +    goog.labs.html.scrubber.ATTRS_ + '>?';
    +
    +
    +/**
    + * Regexp pattern for an HTML text node.
    + * @private
    + */
    +goog.labs.html.scrubber.TEXT_NODE_ = '(?:[^<]|<(?![a-z]|[?!/]))+';
    +
    +
    +/**
    + * Matches HTML comments including HTML 5 "bogus comments" of the form
    + * {@code <!...>} or {@code <?...>} or {@code </...>}.
    + * @private
    + */
    +goog.labs.html.scrubber.COMMENT_ =
    +    '<!--(?:[^\\-]|-+(?![\\->]))*(?:-(?:->?)?)?' +
    +    '|<[!?/][^>]*>?';
    +
    +
    +/**
    + * Regexp pattern for an HTML token after a doctype.
    + * Special elements introduces a capturing group for use with a
    + * back-reference.
    + * @private
    + */
    +goog.labs.html.scrubber.HTML_TOKENS_RE_ = new RegExp(
    +    '(?:' + goog.labs.html.scrubber.TEXT_NODE_ +
    +    '|' + goog.labs.html.scrubber.SPECIAL_ELEMENT_ +
    +    '|' + goog.labs.html.scrubber.TAG_ +
    +    '|' + goog.labs.html.scrubber.COMMENT_ + ')',
    +    'ig');
    +
    +
    +/**
    + * An HTML tag which captures the name in group 1,
    + * and any attributes in group 2.
    + * @private
    + */
    +goog.labs.html.scrubber.TAG_RE_ = new RegExp(
    +    '<[/]?([a-z]' + goog.labs.html.scrubber.TAG_NAME_CHAR_ + '*)' +
    +    '(' + goog.labs.html.scrubber.ATTRS_ + ')>?',
    +    'i');
    +
    +
    +/**
    + * A global matcher that separates attributes out of the tag body cruft.
    + * @private
    + */
    +goog.labs.html.scrubber.ATTRS_RE_ = new RegExp(
    +    '[^=\\s]+\\s*(?:' + goog.labs.html.scrubber.ATTR_VALUE_ + ')?', 'ig');
    +
    +
    +/**
    + * Returns an array of HTML tokens including tags, text nodes and comments.
    + * "Special" elements, like {@code <script>..</script>} whose bodies cannot
    + * include nested elements, are returned as single tokens.
    + *
    + * @param {string} html a string of HTML
    + * @return {!Array<string>}
    + * @private
    + */
    +goog.labs.html.scrubber.lex_ = function(html) {
    +  return ('' + html).match(goog.labs.html.scrubber.HTML_TOKENS_RE_) || [];
    +};
    +
    +
    +/**
    + * Replaces tags not on the white-list with empty text nodes, dropping all
    + * attributes, and drops other non-text nodes such as comments.
    + *
    + * @param {!Object<string, boolean>} tagWhitelist a set of lower-case tag names
    + *    following the convention established by {@link goog.object.createSet}.
    + * @param {!Object<string, Object<string, goog.labs.html.AttributeRewriter>>
    + *        } attrWhitelist
    + *    maps lower-case tag names and the special string {@code "*"} to functions
    + *    from decoded attribute values to sanitized values or {@code null} to
    + *    indicate that the attribute is not allowed with that value.
    + *
    + *    For example, if {@code attrWhitelist['a']['href']} is defined then it is
    + *    used to sanitize the value of the link's URL.
    + *
    + *    If {@code attrWhitelist['*']['id']} is defined, and
    + *    {@code attrWhitelist['div']['id']} is not, then the former is used to
    + *    sanitize any {@code id} attribute on a {@code <div>} element.
    + * @param {!Array<string>} htmlTokens an array of HTML tokens as returned by
    + *    {@link goog.labs.html.scrubber.lex_}.
    + * @return {!Array<string>} the input array modified in place to have some
    + *    tokens removed.
    + * @private
    + */
    +goog.labs.html.scrubber.filter_ = function(
    +    tagWhitelist, attrWhitelist, htmlTokens) {
    +  var genericAttrWhitelist = attrWhitelist['*'];
    +  for (var i = 0, n = htmlTokens.length; i < n; ++i) {
    +    var htmlToken = htmlTokens[i];
    +    if (htmlToken.charCodeAt(0) !== goog.labs.html.scrubber.CC_LT_) {
    +      // Definitely not a tag
    +      continue;
    +    }
    +
    +    var tag = htmlToken.match(goog.labs.html.scrubber.TAG_RE_);
    +    if (tag) {
    +      var lowerCaseTagName = tag[1].toLowerCase();
    +      var isCloseTag =
    +          htmlToken.charCodeAt(1) === goog.labs.html.scrubber.CC_SLASH_;
    +      var attrs = '';
    +      if (!isCloseTag && tag[2]) {
    +        var tagSpecificAttrWhitelist =
    +            /** @type {Object<string, goog.labs.html.AttributeRewriter>} */ (
    +            goog.labs.html.scrubber.readOwnProperty_(
    +                attrWhitelist, lowerCaseTagName));
    +        if (genericAttrWhitelist || tagSpecificAttrWhitelist) {
    +          attrs = goog.labs.html.scrubber.filterAttrs_(
    +              tag[2], genericAttrWhitelist, tagSpecificAttrWhitelist);
    +        }
    +      }
    +      var specialContent = htmlToken.substring(tag[0].length);
    +      htmlTokens[i] =
    +          (tagWhitelist[lowerCaseTagName] === true) ?
    +          (
    +              (isCloseTag ? '</' : '<') + lowerCaseTagName + attrs + '>' +
    +              specialContent
    +          ) :
    +          '';
    +    } else if (htmlToken.length > 1) {
    +      switch (htmlToken.charCodeAt(1)) {
    +        case goog.labs.html.scrubber.CC_BANG_:
    +        case goog.labs.html.scrubber.CC_SLASH_:
    +        case goog.labs.html.scrubber.CC_QMARK_:
    +          htmlTokens[i] = '';  // Elide comments.
    +          break;
    +        default:
    +          // Otherwise, token is just a text node that starts with '<'.
    +          // Speed up later passes by normalizing the text node.
    +          htmlTokens[i] = htmlTokens[i].replace(/</g, '&lt;');
    +      }
    +    }
    +  }
    +  return htmlTokens;
    +};
    +
    +
    +/**
    + * Parses attribute names and values out of a tag body and applies the attribute
    + * white-list to produce a tag body containing only safe attributes.
    + *
    + * @param {string} attrsText the text of a tag between the end of the tag name
    + *   and the beginning of the tag end marker, so {@code " foo bar='baz'"} for
    + *   the tag {@code <tag foo bar='baz'/>}.
    + * @param {Object<string, goog.labs.html.AttributeRewriter>}
    + *   genericAttrWhitelist
    + *   a whitelist of attribute transformations for attributes that are allowed
    + *   on any element.
    + * @param {Object<string, goog.labs.html.AttributeRewriter>}
    + *   tagSpecificAttrWhitelist
    + *   a whitelist of attribute transformations for attributes that are allowed
    + *   on the element started by the tag whose body is {@code tagBody}.
    + * @return {string} a tag-body that consists only of safe attributes.
    + * @private
    + */
    +goog.labs.html.scrubber.filterAttrs_ =
    +    function(attrsText, genericAttrWhitelist, tagSpecificAttrWhitelist) {
    +  var attrs = attrsText.match(goog.labs.html.scrubber.ATTRS_RE_);
    +  var nAttrs = attrs ? attrs.length : 0;
    +  var safeAttrs = '';
    +  for (var i = 0; i < nAttrs; ++i) {
    +    var attr = attrs[i];
    +    var eq = attr.indexOf('=');
    +    var name, value;
    +    if (eq >= 0) {
    +      name = goog.string.trim(attr.substr(0, eq));
    +      value = goog.string.stripQuotes(
    +          goog.string.trim(attr.substr(eq + 1)), '"\'');
    +    } else {
    +      name = value = attr;
    +    }
    +    name = name.toLowerCase();
    +    var rewriter = /** @type {?goog.labs.html.AttributeRewriter} */ (
    +        tagSpecificAttrWhitelist &&
    +        goog.labs.html.scrubber.readOwnProperty_(
    +            tagSpecificAttrWhitelist, name) ||
    +        genericAttrWhitelist &&
    +        goog.labs.html.scrubber.readOwnProperty_(genericAttrWhitelist, name));
    +    if (rewriter) {
    +      var safeValue = rewriter(goog.string.unescapeEntities(value));
    +      if (safeValue != null) {
    +        if (safeValue.implementsGoogStringTypedString) {
    +          safeValue = /** @type {goog.string.TypedString} */
    +              (safeValue).getTypedStringValue();
    +        }
    +        safeValue = String(safeValue);
    +        if (safeValue.indexOf('`') >= 0) {
    +          safeValue += ' ';
    +        }
    +        safeAttrs +=
    +            ' ' + name + '="' + goog.string.htmlEscape(safeValue, false) +
    +            '"';
    +      }
    +    }
    +  }
    +  return safeAttrs;
    +};
    +
    +
    +/**
    + * @param {!Object} o the object
    + * @param {!string} k a key into o
    + * @return {*}
    + * @private
    + */
    +goog.labs.html.scrubber.readOwnProperty_ = function(o, k) {
    +  return Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined;
    +};
    +
    +
    +/**
    + * We limit the nesting limit of balanced HTML to a large but manageable number
    + * so that built-in browser limits aren't likely to kick in and undo all our
    + * matching of start and end tags.
    + * <br>
    + * This mitigates the HTML parsing equivalent of stack smashing attacks.
    + * <br>
    + * Otherwise, crafted inputs like
    + * {@code <p><p><p><p>...Ad nauseam..</p></p></p></p>} could exploit
    + * browser bugs, and/or undocumented nesting limit recovery code to misnest
    + * tags.
    + * @private
    + * @const
    + */
    +goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_ = 256;
    +
    +
    +/**
    + * Ensures that there are end-tags for all and only for non-void start tags.
    + * @param {Array<string>} htmlTokens an array of HTML tokens as returned by
    + *    {@link goog.labs.html.scrubber.lex}.
    + * @return {!Array<string>} the input array modified in place to have some
    + *    tokens removed.
    + * @private
    + */
    +goog.labs.html.scrubber.balance_ = function(htmlTokens) {
    +  var openElementStack = [];
    +  for (var i = 0, n = htmlTokens.length; i < n; ++i) {
    +    var htmlToken = htmlTokens[i];
    +    if (htmlToken.charCodeAt(0) !== goog.labs.html.scrubber.CC_LT_) {
    +      // Definitely not a tag
    +      continue;
    +    }
    +    var tag = htmlToken.match(goog.labs.html.scrubber.TAG_RE_);
    +    if (tag) {
    +      var lowerCaseTagName = tag[1].toLowerCase();
    +      var isCloseTag =
    +          htmlToken.charCodeAt(1) === goog.labs.html.scrubber.CC_SLASH_;
    +      // Special case: HTML5 mandates that </br> be treated as <br>.
    +      if (isCloseTag && lowerCaseTagName == 'br') {
    +        isCloseTag = false;
    +        htmlToken = '<br>';
    +      }
    +      var isVoidTag = goog.dom.tags.isVoidTag(lowerCaseTagName);
    +      if (isVoidTag && isCloseTag) {
    +        htmlTokens[i] = '';
    +        continue;
    +      }
    +
    +      var prefix = '';
    +
    +      // Insert implied open tags.
    +      var nOpenElements = openElementStack.length;
    +      if (nOpenElements && !isCloseTag) {
    +        var top = openElementStack[nOpenElements - 1];
    +        var groups = goog.labs.html.scrubber.ELEMENT_GROUPS_[lowerCaseTagName];
    +        if (groups === undefined) {
    +          groups = goog.labs.html.scrubber.Group_.INLINE_;
    +        }
    +        var canContain = goog.labs.html.scrubber.ELEMENT_CONTENTS_[top];
    +        if (!(groups & canContain)) {
    +          var blockContainer = goog.labs.html.scrubber.BLOCK_CONTAINERS_[top];
    +          if ('string' === typeof blockContainer) {
    +            var containerCanContain =
    +                goog.labs.html.scrubber.ELEMENT_CONTENTS_[blockContainer];
    +            if (containerCanContain & groups) {
    +              if (nOpenElements <
    +                  goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
    +                prefix = '<' + blockContainer + '>';
    +              }
    +              openElementStack[nOpenElements] = blockContainer;
    +              ++nOpenElements;
    +            }
    +          }
    +        }
    +      }
    +
    +      // Insert any missing close tags we need.
    +      var newStackLen = goog.labs.html.scrubber.pickElementsToClose_(
    +          lowerCaseTagName, isCloseTag, openElementStack);
    +
    +      var nClosed = nOpenElements - newStackLen;
    +      if (nClosed) {  // ["p", "a", "b"] -> "</b></a></p>"
    +        // First, dump anything past the nesting limit.
    +        if (nOpenElements > goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
    +          nClosed -= nOpenElements -
    +              goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
    +          nOpenElements = goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
    +        }
    +        // Truncate to the new limit, and produce end tags.
    +        var closeTags = openElementStack.splice(newStackLen, nClosed);
    +        if (closeTags.length) {
    +          closeTags.reverse();
    +          prefix += '</' + closeTags.join('></') + '>';
    +        }
    +      }
    +
    +      // We could do resumption here to handle misnested tags like
    +      //    <b><i class="c">Foo</b>Bar</i>
    +      // which is equivalent to
    +      //    <b><i class="c">Foo</i></b><i class="c">Bar</i>
    +      // but that requires storing attributes on the open element stack
    +      // which complicates all the code using it for marginal added value.
    +
    +      if (isCloseTag) {
    +        // If the close tag matched an open tag, then the closed section
    +        // included that tag name.
    +        htmlTokens[i] = prefix;
    +      } else {
    +        if (!isVoidTag) {
    +          openElementStack[openElementStack.length] = lowerCaseTagName;
    +        }
    +        if (openElementStack.length >
    +            goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
    +          htmlToken = '';
    +        }
    +        htmlTokens[i] = prefix + htmlToken;
    +      }
    +    }
    +  }
    +  if (openElementStack.length) {
    +    if (openElementStack.length >
    +        goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_) {
    +      openElementStack.length = goog.labs.html.scrubber.BALANCE_NESTING_LIMIT_;
    +    }
    +    if (openElementStack.length) {
    +      openElementStack.reverse();
    +      htmlTokens[htmlTokens.length] = '</' + openElementStack.join('></') + '>';
    +    }
    +  }
    +  return htmlTokens;
    +};
    +
    +
    +/**
    + * Normalizes HTML tokens and concatenates them into a string.
    + * @param {Array<string>} htmlTokens an array of HTML tokens as returned by
    + *    {@link goog.labs.html.scrubber.lex}.
    + * @return {string} a string of HTML.
    + * @private
    + */
    +goog.labs.html.scrubber.render_ = function(htmlTokens) {
    +  for (var i = 0, n = htmlTokens.length; i < n; ++i) {
    +    var htmlToken = htmlTokens[i];
    +    if (htmlToken.charCodeAt(0) === goog.labs.html.scrubber.CC_LT_ &&
    +        goog.labs.html.scrubber.TAG_RE_.test(htmlToken)) {
    +      // The well-formedness and quotedness of attributes must be ensured by
    +      // earlier passes.  filter does this.
    +    } else {
    +      if (htmlToken.indexOf('<') >= 0) {
    +        htmlToken = htmlToken.replace(/</g, '&lt;');
    +      }
    +      if (htmlToken.indexOf('>') >= 0) {
    +        htmlToken = htmlToken.replace(/>/g, '&gt;');
    +      }
    +      htmlTokens[i] = htmlToken;
    +    }
    +  }
    +  return htmlTokens.join('');
    +};
    +
    +
    +/**
    + * Groups of elements used to specify containment relationships.
    + * @enum {number}
    + * @private
    + */
    +goog.labs.html.scrubber.Group_ = {
    +  BLOCK_: (1 << 0),
    +  INLINE_: (1 << 1),
    +  INLINE_MINUS_A_: (1 << 2),
    +  MIXED_: (1 << 3),
    +  TABLE_CONTENT_: (1 << 4),
    +  HEAD_CONTENT_: (1 << 5),
    +  TOP_CONTENT_: (1 << 6),
    +  AREA_ELEMENT_: (1 << 7),
    +  FORM_ELEMENT_: (1 << 8),
    +  LEGEND_ELEMENT_: (1 << 9),
    +  LI_ELEMENT_: (1 << 10),
    +  DL_PART_: (1 << 11),
    +  P_ELEMENT_: (1 << 12),
    +  OPTIONS_ELEMENT_: (1 << 13),
    +  OPTION_ELEMENT_: (1 << 14),
    +  PARAM_ELEMENT_: (1 << 15),
    +  TABLE_ELEMENT_: (1 << 16),
    +  TR_ELEMENT_: (1 << 17),
    +  TD_ELEMENT_: (1 << 18),
    +  COL_ELEMENT_: (1 << 19),
    +  CHARACTER_DATA_: (1 << 20)
    +};
    +
    +
    +/**
    + * Element scopes limit where close tags can have effects.
    + * For example, a table cannot be implicitly closed by a {@code </p>} even if
    + * the table appears inside a {@code <p>} because the {@code <table>} element
    + * introduces a scope.
    + *
    + * @enum {number}
    + * @private
    + */
    +goog.labs.html.scrubber.Scope_ = {
    +  COMMON_: (1 << 0),
    +  BUTTON_: (1 << 1),
    +  LIST_ITEM_: (1 << 2),
    +  TABLE_: (1 << 3)
    +};
    +
    +
    +/** @const @private */
    +goog.labs.html.scrubber.ALL_SCOPES_ =
    +    goog.labs.html.scrubber.Scope_.COMMON_ |
    +    goog.labs.html.scrubber.Scope_.BUTTON_ |
    +    goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
    +    goog.labs.html.scrubber.Scope_.TABLE_;
    +
    +
    +/**
    + * Picks which open HTML elements to close.
    + *
    + * @param {string}         lowerCaseTagName The name of the tag.
    + * @param {boolean}        isCloseTag       True for a {@code </tagname>} tag.
    + * @param {Array<string>} openElementStack The names of elements that have been
    + *                                          opened and not subsequently closed.
    + * @return {number} the length of openElementStack after closing any tags that
    + *               need to be closed.
    + * @private
    + */
    +goog.labs.html.scrubber.pickElementsToClose_ =
    +    function(lowerCaseTagName, isCloseTag, openElementStack) {
    +  var nOpenElements = openElementStack.length;
    +  if (isCloseTag) {
    +    // Look for a matching close tag inside blocking scopes.
    +    var topMost;
    +    if (/^h[1-6]$/.test(lowerCaseTagName)) {
    +      // </h1> will close any header.
    +      topMost = -1;
    +      for (var i = nOpenElements; --i >= 0;) {
    +        if (/^h[1-6]$/.test(openElementStack[i])) {
    +          topMost = i;
    +        }
    +      }
    +    } else {
    +      topMost = goog.array.lastIndexOf(openElementStack, lowerCaseTagName);
    +    }
    +    if (topMost >= 0) {
    +      var blockers = goog.labs.html.scrubber.ALL_SCOPES_ &
    +          ~(goog.labs.html.scrubber.ELEMENT_SCOPES_[lowerCaseTagName] | 0);
    +      for (var i = nOpenElements; --i > topMost;) {
    +        var blocks =
    +            goog.labs.html.scrubber.ELEMENT_SCOPES_[openElementStack[i]] | 0;
    +        if (blockers & blocks) {
    +          return nOpenElements;
    +        }
    +      }
    +      return topMost;
    +    }
    +    return nOpenElements;
    +  } else {
    +    // Close anything that cannot contain the tag name.
    +    var groups = goog.labs.html.scrubber.ELEMENT_GROUPS_[lowerCaseTagName];
    +    if (groups === undefined) {
    +      groups = goog.labs.html.scrubber.Group_.INLINE_;
    +    }
    +    for (var i = nOpenElements; --i >= 0;) {
    +      var canContain =
    +          goog.labs.html.scrubber.ELEMENT_CONTENTS_[openElementStack[i]];
    +      if (canContain === undefined) {
    +        canContain = goog.labs.html.scrubber.Group_.INLINE_;
    +      }
    +      if (groups & canContain) {
    +        return i + 1;
    +      }
    +    }
    +    return 0;
    +  }
    +};
    +
    +
    +/**
    + * The groups into which the element falls.
    + * The default is an inline element.
    + * @private
    + */
    +goog.labs.html.scrubber.ELEMENT_GROUPS_ = {
    +  'a': goog.labs.html.scrubber.Group_.INLINE_,
    +  'abbr': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'acronym': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'address': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'applet': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'area': goog.labs.html.scrubber.Group_.AREA_ELEMENT_,
    +  'audio': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'b': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'base': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'basefont': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'bdi': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'bdo': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'big': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'blink': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'blockquote': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'body': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
    +  'br': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'button': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'canvas': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'caption': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
    +  'center': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'cite': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'code': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'col': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.COL_ELEMENT_,
    +  'colgroup': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
    +  'dd': goog.labs.html.scrubber.Group_.DL_PART_,
    +  'del': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.MIXED_,
    +  'dfn': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'dir': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'div': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'dl': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'dt': goog.labs.html.scrubber.Group_.DL_PART_,
    +  'em': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'fieldset': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'font': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'form': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.FORM_ELEMENT_,
    +  'h1': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'h2': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'h3': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'h4': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'h5': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'h6': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'head': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
    +  'hr': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'html': 0,
    +  'i': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'iframe': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'img': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'input': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'ins': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'isindex': goog.labs.html.scrubber.Group_.INLINE_,
    +  'kbd': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'label': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'legend': goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_,
    +  'li': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
    +  'link': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'listing': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'map': goog.labs.html.scrubber.Group_.INLINE_,
    +  'meta': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'nobr': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'noframes': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.TOP_CONTENT_,
    +  'noscript': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'object': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
    +      goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'ol': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'optgroup': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
    +  'option': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.OPTION_ELEMENT_,
    +  'p': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.P_ELEMENT_,
    +  'param': goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
    +  'pre': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'q': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  's': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'samp': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'script': (goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
    +      goog.labs.html.scrubber.Group_.MIXED_ |
    +      goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.HEAD_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.TOP_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.AREA_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.LI_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.DL_PART_ |
    +      goog.labs.html.scrubber.Group_.P_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.OPTION_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.PARAM_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TABLE_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TD_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.COL_ELEMENT_),
    +  'select': goog.labs.html.scrubber.Group_.INLINE_,
    +  'small': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'span': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'strike': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'strong': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'style': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'sub': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'sup': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'table': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.TABLE_ELEMENT_,
    +  'tbody': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
    +  'td': goog.labs.html.scrubber.Group_.TD_ELEMENT_,
    +  'textarea': goog.labs.html.scrubber.Group_.INLINE_,
    +  'tfoot': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
    +  'th': goog.labs.html.scrubber.Group_.TD_ELEMENT_,
    +  'thead': goog.labs.html.scrubber.Group_.TABLE_CONTENT_,
    +  'title': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'tr': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.TR_ELEMENT_,
    +  'tt': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'u': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'ul': goog.labs.html.scrubber.Group_.BLOCK_,
    +  'var': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'video': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'wbr': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'xmp': goog.labs.html.scrubber.Group_.BLOCK_
    +};
    +
    +
    +/**
    + * The groups which the element can contain.
    + * Defaults to 0.
    + * @private
    + */
    +goog.labs.html.scrubber.ELEMENT_CONTENTS_ = {
    +  'a': goog.labs.html.scrubber.Group_.INLINE_MINUS_A_,
    +  'abbr': goog.labs.html.scrubber.Group_.INLINE_,
    +  'acronym': goog.labs.html.scrubber.Group_.INLINE_,
    +  'address': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.P_ELEMENT_,
    +  'applet': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
    +  'b': goog.labs.html.scrubber.Group_.INLINE_,
    +  'bdi': goog.labs.html.scrubber.Group_.INLINE_,
    +  'bdo': goog.labs.html.scrubber.Group_.INLINE_,
    +  'big': goog.labs.html.scrubber.Group_.INLINE_,
    +  'blink': goog.labs.html.scrubber.Group_.INLINE_,
    +  'blockquote': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'body': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'button': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'canvas': goog.labs.html.scrubber.Group_.INLINE_,
    +  'caption': goog.labs.html.scrubber.Group_.INLINE_,
    +  'center': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'cite': goog.labs.html.scrubber.Group_.INLINE_,
    +  'code': goog.labs.html.scrubber.Group_.INLINE_,
    +  'colgroup': goog.labs.html.scrubber.Group_.COL_ELEMENT_,
    +  'dd': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'del': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'dfn': goog.labs.html.scrubber.Group_.INLINE_,
    +  'dir': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
    +  'div': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'dl': goog.labs.html.scrubber.Group_.DL_PART_,
    +  'dt': goog.labs.html.scrubber.Group_.INLINE_,
    +  'em': goog.labs.html.scrubber.Group_.INLINE_,
    +  'fieldset': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.LEGEND_ELEMENT_,
    +  'font': goog.labs.html.scrubber.Group_.INLINE_,
    +  'form': (goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.INLINE_MINUS_A_ |
    +      goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TD_ELEMENT_),
    +  'h1': goog.labs.html.scrubber.Group_.INLINE_,
    +  'h2': goog.labs.html.scrubber.Group_.INLINE_,
    +  'h3': goog.labs.html.scrubber.Group_.INLINE_,
    +  'h4': goog.labs.html.scrubber.Group_.INLINE_,
    +  'h5': goog.labs.html.scrubber.Group_.INLINE_,
    +  'h6': goog.labs.html.scrubber.Group_.INLINE_,
    +  'head': goog.labs.html.scrubber.Group_.HEAD_CONTENT_,
    +  'html': goog.labs.html.scrubber.Group_.TOP_CONTENT_,
    +  'i': goog.labs.html.scrubber.Group_.INLINE_,
    +  'iframe': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'ins': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'kbd': goog.labs.html.scrubber.Group_.INLINE_,
    +  'label': goog.labs.html.scrubber.Group_.INLINE_,
    +  'legend': goog.labs.html.scrubber.Group_.INLINE_,
    +  'li': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'listing': goog.labs.html.scrubber.Group_.INLINE_,
    +  'map': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.AREA_ELEMENT_,
    +  'nobr': goog.labs.html.scrubber.Group_.INLINE_,
    +  'noframes': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.TOP_CONTENT_,
    +  'noscript': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'object': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.PARAM_ELEMENT_,
    +  'ol': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
    +  'optgroup': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
    +  'option': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
    +  'p': goog.labs.html.scrubber.Group_.INLINE_ |
    +      goog.labs.html.scrubber.Group_.TABLE_ELEMENT_,
    +  'pre': goog.labs.html.scrubber.Group_.INLINE_,
    +  'q': goog.labs.html.scrubber.Group_.INLINE_,
    +  's': goog.labs.html.scrubber.Group_.INLINE_,
    +  'samp': goog.labs.html.scrubber.Group_.INLINE_,
    +  'script': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
    +  'select': goog.labs.html.scrubber.Group_.OPTIONS_ELEMENT_,
    +  'small': goog.labs.html.scrubber.Group_.INLINE_,
    +  'span': goog.labs.html.scrubber.Group_.INLINE_,
    +  'strike': goog.labs.html.scrubber.Group_.INLINE_,
    +  'strong': goog.labs.html.scrubber.Group_.INLINE_,
    +  'style': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
    +  'sub': goog.labs.html.scrubber.Group_.INLINE_,
    +  'sup': goog.labs.html.scrubber.Group_.INLINE_,
    +  'table': goog.labs.html.scrubber.Group_.TABLE_CONTENT_ |
    +      goog.labs.html.scrubber.Group_.FORM_ELEMENT_,
    +  'tbody': goog.labs.html.scrubber.Group_.TR_ELEMENT_,
    +  'td': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'textarea': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
    +  'tfoot': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TD_ELEMENT_,
    +  'th': goog.labs.html.scrubber.Group_.BLOCK_ |
    +      goog.labs.html.scrubber.Group_.INLINE_,
    +  'thead': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TR_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TD_ELEMENT_,
    +  'title': goog.labs.html.scrubber.Group_.CHARACTER_DATA_,
    +  'tr': goog.labs.html.scrubber.Group_.FORM_ELEMENT_ |
    +      goog.labs.html.scrubber.Group_.TD_ELEMENT_,
    +  'tt': goog.labs.html.scrubber.Group_.INLINE_,
    +  'u': goog.labs.html.scrubber.Group_.INLINE_,
    +  'ul': goog.labs.html.scrubber.Group_.LI_ELEMENT_,
    +  'var': goog.labs.html.scrubber.Group_.INLINE_,
    +  'xmp': goog.labs.html.scrubber.Group_.INLINE_
    +};
    +
    +
    +/**
    + * The scopes in which an element falls.
    + * No property defaults to 0.
    + * @private
    + */
    +goog.labs.html.scrubber.ELEMENT_SCOPES_ = {
    +  'applet': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'button': goog.labs.html.scrubber.Scope_.BUTTON_,
    +  'caption': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'html': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
    +      goog.labs.html.scrubber.Scope_.TABLE_,
    +  'object': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'ol': goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'table': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_ |
    +      goog.labs.html.scrubber.Scope_.TABLE_,
    +  'td': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'th': goog.labs.html.scrubber.Scope_.COMMON_ |
    +      goog.labs.html.scrubber.Scope_.BUTTON_ |
    +      goog.labs.html.scrubber.Scope_.LIST_ITEM_,
    +  'ul': goog.labs.html.scrubber.Scope_.LIST_ITEM_
    +};
    +
    +
    +/**
    + * Per-element, a child that can contain block content.
    + * @private
    + */
    +goog.labs.html.scrubber.BLOCK_CONTAINERS_ = {
    +  'dl': 'dd',
    +  'ol': 'li',
    +  'table': 'tr',
    +  'tr': 'td',
    +  'ul': 'li'
    +};
    +
    +
    +goog.labs.html.attributeRewriterPresubmitWorkaround();
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/html/scrubber_test.js b/mapbox-gl/closure-library/closure/goog/labs/html/scrubber_test.js
    new file mode 100644
    index 0000000000..c268992771
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/html/scrubber_test.js
    @@ -0,0 +1,254 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.html.ScrubberTest');
    +
    +goog.require('goog.labs.html.scrubber');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.html.ScrubberTest');
    +
    +
    +
    +var tagWhitelist = goog.object.createSet(
    +    'a', 'b', 'i', 'p', 'font', 'hr', 'br', 'span',
    +    'ol', 'ul', 'li',
    +    'table', 'tr', 'td', 'th', 'tbody',
    +    'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
    +    'img',
    +    'html', 'head', 'body', 'title');
    +
    +var attrWhitelist = {
    +  // On any element,
    +  '*': {
    +    // allow unfiltered title attributes, and
    +    'title': function(title) { return title; },
    +    // specific dir values.
    +    'dir': function(dir) {
    +      return dir === 'ltr' || dir === 'rtl' ? dir : null;
    +    }
    +  },
    +  // Specifically on <a> elements,
    +  'a': {
    +    // allow an href but verify and rewrite, and
    +    'href': function(href) {
    +      return /^https?:\/\/google\.[a-z]{2,3}\/search\?/.test(href) ?
    +          href.replace(/[^A-Za-z0-9_\-.~:\/?#\[\]@!\$&()*+,;=%]+/,
    +                       encodeURIComponent) :
    +          null;
    +    },
    +    // mask the generic title handler for no good reason.
    +    'title': function(title) { return '<' + title + '>'; }
    +  }
    +};
    +
    +
    +function run(input, golden, desc) {
    +  var actual = goog.labs.html.scrubber.scrub(
    +      tagWhitelist, attrWhitelist, input);
    +  assertEquals(desc, golden, actual);
    +}
    +
    +
    +function testEmptyString() {
    +  run('', '', 'Empty string');
    +}
    +
    +function testHelloWorld() {
    +  run('Hello, <b>World</b>!', 'Hello, <b>World</b>!',
    +      'Hello World');
    +}
    +
    +function testNoEndTag() {
    +  run('<i>Hello, <b>World!',
    +      '<i>Hello, <b>World!</b></i>',
    +      'Hello World no end tag');
    +}
    +
    +function testUnclosedTags() {
    +  run('<html><head><title>Hello, <<World>>!</TITLE>' +
    +      '</head><body><p>Hello,<Br><<World>>!',
    +      '<html><head><title>Hello, <<World>>!</title>' +
    +      '</head><body><p>Hello,<br>&lt;&gt;!</p></body></html>',
    +      'RCDATA content, different case, unclosed tags');
    +}
    +
    +function testListInList() {
    +  run('<ul><li>foo</li><ul><li>bar</li></ul></ul>',
    +      '<ul><li>foo</li><li><ul><li>bar</li></ul></li></ul>',
    +      'list in list directly');
    +}
    +
    +function testHeaders() {
    +  run('<h1>header</h1>body' +
    +      '<H2>sub-header</h3>sub-body' +
    +      '<h3>sub-sub-</hr>header<hr></hr>sub-sub-body</H4></h2>',
    +      '<h1>header</h1>body' +
    +      '<h2>sub-header</h2>sub-body' +
    +      '<h3>sub-sub-header</h3><hr>sub-sub-body',
    +      'headers');
    +}
    +
    +function testListNesting() {
    +  run('<ul><li><ul><li>foo</li></li><ul><li>bar',
    +      '<ul><li><ul><li>foo</li><li><ul><li>bar</li></ul></li></ul></li></ul>',
    +      'list nesting');
    +}
    +
    +function testTableNesting() {
    +  run('<table><tbody><tr><td>foo</td><table><tbody><tr><th>bar</table></table>',
    +      '<table><tbody><tr><td>foo</td><td>' +
    +      '<table><tbody><tr><th>bar</th></tr></tbody></table>' +
    +      '</td></tr></tbody></table>',
    +      'table nesting');
    +}
    +
    +function testNestingLimit() {
    +  run(goog.string.repeat('<span>', 264) + goog.string.repeat('</span>', 264),
    +      goog.string.repeat('<span>', 256) + goog.string.repeat('</span>', 256),
    +      '264 open spans');
    +}
    +
    +function testTableScopes() {
    +  run('<html><head></head><body><p>Hi</p><p>How are you</p>\n' +
    +      '<p><table><tbody><tr>' +
    +      '<td><b><font><font><p>Cell</b></font></font></p>\n</td>' +
    +      '<td><b><font><font><p>Cell</b></font></font></p>\n</td>' +
    +      '</tr></tbody></table></p>\n' +
    +      '<p>x</p></body></html>',
    +
    +      '<html><head></head><body><p>Hi</p><p>How are you</p>\n' +
    +      '<p><table><tbody><tr>' +
    +      '<td><b><font><font></font></font></b><p>Cell</p>\n</td>' +
    +      // The close </p> tag does not close the whole table. +
    +      '<td><b><font><font></font></font></b><p>Cell</p>\n</td>' +
    +      '</tr></tbody></table></p>\n' +
    +      '<p>x</p></body></html>',
    +
    +      'Table Scopes');
    +}
    +
    +function testConcatSafe() {
    +  run('<<applet>script<applet>>alert(1337)<<!-- -->/script<?...?>>',
    +      '&lt;script&gt;alert(1337)&lt;/script&gt;',
    +      'Concat safe');
    +}
    +
    +function testPrototypeMembersDoNotInfectTables() {
    +  // Constructor is all lower-case so will survive tag name
    +  // normalization.
    +  run('<constructor>Foo</constructor>', 'Foo',
    +      'Object.prototype members');
    +}
    +
    +function testGenericAttributesAllowed() {
    +  run('<span title=howdy></span>', '<span title="howdy"></span>',
    +      'generic attrs allowed');
    +}
    +
    +function testValueWhitelisting() {
    +  run('<span dir=\'ltr\'>LTR</span><span dir=\'evil\'>Evil</span>',
    +      '<span dir="ltr">LTR</span><span>Evil</span>',
    +      'value whitelisted');
    +}
    +
    +function testAttributeNormalization() {
    +  run('<a href="http://google.com/search?q=tests suxor&hl=en">Click</a>',
    +      '<a href="http://google.com/search?q=tests%20suxor&amp;hl=en">Click</a>',
    +      'URL normalized');
    +}
    +
    +function testTagSpecificityOfAttributeFiltering() {
    +  run('<img href="http://google.com/search?q=tests+suxor">',
    +      '<img>',
    +      'href blocked on img');
    +}
    +
    +function testTagSpecificAttributeFiltering() {
    +  run('<a href="http://google.evil.com/search?q=tests suxor">Unclicky</a>',
    +      '<a>Unclicky</a>',
    +      'bad href value blocked');
    +}
    +
    +function testNonWhitelistFunctionsNotCalled() {
    +  var called = false;
    +  Object.prototype.dontcallme = function() {
    +    called = true;
    +    return 'dontcallme was called despite being on the prototype';
    +  };
    +  try {
    +    run('<span dontcallme="I\'ll call you">Lorem Ipsum',
    +        '<span>Lorem Ipsum</span>',
    +        'non white-list fn not called');
    +  } finally {
    +    delete Object.prototype.dontcallme;
    +  }
    +  assertFalse('Object.prototype.dontcallme should not have been called',
    +              called);
    +}
    +
    +function testQuotesInAttributeValue() {
    +  run('<span tItlE =\n\'Quoth the raven, "Nevermore"\'>Lorem Ipsum',
    +      '<span title="Quoth the raven, &quot;Nevermore&quot;">Lorem Ipsum</span>',
    +      'quotes in attr value');
    +}
    +
    +function testAttributesNeverMentionedAreDropped() {
    +  run('<b onclick="evil=true">evil</b>', '<b>evil</b>', 'attrs white-listed');
    +}
    +
    +function testAttributesNotOverEscaped() {
    +  run('<I TITLE="Foo &AMP; Bar & Baz">/</I>',
    +      '<i title="Foo &amp; Bar &amp; Baz">/</i>',
    +      'attr value not over-escaped');
    +}
    +
    +function testTagSpecificRulesTakePrecedence() {
    +  run('<a title=zogberts>Link</a>',
    +      '<a title="&lt;zogberts&gt;">Link</a>',
    +      'tag specific rules take precedence');
    +}
    +
    +function testAttributeRejectionLocalized() {
    +  run('<a id=foo href =//evil.org/ title=>Link</a>',
    +      '<a title="&lt;&gt;">Link</a>',
    +      'failure of one attribute does not torpedo others');
    +}
    +
    +function testWeirdHtmlRulesFollowedForAttrValues() {
    +  run('<span title= id=>Lorem Ipsum</span>',
    +      '<span title=\"id=\">Lorem Ipsum</span>',
    +      'same as browser on weird values');
    +}
    +
    +function testAttributesDisallowedOnCloseTags() {
    +  run('<h1 title="open">Header</h1 title="closed">',
    +      '<h1 title="open">Header</h1>',
    +      'attributes on close tags');
    +}
    +
    +function testRoundTrippingOfHtmlSafeAgainstIEBacktickProblems() {
    +  // Introducing a space at the end of an attribute forces IE to quote it when
    +  // turning a DOM into innerHTML which protects against a bunch of problems
    +  // with backticks since IE treats them as attribute value delimiters, allowing
    +  //   foo.innerHTML += ...
    +  // to continue to "work" without introducing an XSS vector.
    +  // Adding a space at the end is innocuous since HTML attributes whose values
    +  // are structured content ignore spaces at the beginning or end.
    +  run('<span title="`backtick">*</span>', '<span title="`backtick ">*</span>',
    +      'not round-trippable on IE');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat.js b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat.js
    new file mode 100644
    index 0000000000..aba373fff0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat.js
    @@ -0,0 +1,261 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview List format and gender decision library with locale support.
    + *
    + * ListFormat takes an array or a var_arg of objects and generates a user
    + * friendly list in a locale-sensitive way (i.e. "red, green, and blue").
    + *
    + * GenderInfo can be used to determine the gender of a list of items,
    + * depending on the gender of all items in the list.
    + *
    + * In English, lists of items don't really have gender, and in fact few things
    + * have gender. But the idea is this:
    + *  - for a list of "male items" (think "John, Steve") you use "they"
    + *  - for "Marry, Ann" (all female) you might have a "feminine" form of "they"
    + *  - and yet another form for mixed lists ("John, Marry") or undetermined
    + *    (when you don't know the gender of the items, or when they are neuter)
    + *
    + * For example in Greek "they" will be translated as "αυτοί" for masculin,
    + * "αυτές" for feminin, and "αυτά" for neutral/undetermined.
    + * (it is in fact more complicated than that, as weak/strong forms and case
    + * also matter, see http://en.wiktionary.org/wiki/Appendix:Greek_pronouns)
    + *
    + */
    +
    +goog.provide('goog.labs.i18n.GenderInfo');
    +goog.provide('goog.labs.i18n.GenderInfo.Gender');
    +goog.provide('goog.labs.i18n.ListFormat');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.labs.i18n.ListFormatSymbols');
    +
    +
    +
    +/**
    + * ListFormat provides a method to format a list/array of objects to a string,
    + * in a user friendly way and in a locale sensitive manner.
    + * If the objects are not strings, toString is called to convert them.
    + * The constructor initializes the object based on the locale data from
    + * the current goog.labs.i18n.ListFormatSymbols.
    + *
    + * Similar to the ICU4J class com.ibm.icu.text.ListFormatter:
    + *   http://icu-project.org/apiref/icu4j/com/ibm/icu/text/ListFormatter.html
    + * @constructor
    + * @final
    + */
    +goog.labs.i18n.ListFormat = function() {
    +  /**
    +   * String for lists of exactly two items, containing {0} for the first,
    +   * and {1} for the second.
    +   * For instance '{0} and {1}' will give 'black and white'.
    +   * @private {string}
    +   *
    +   * Example: for "black and white" the pattern is "{0} and {1}"
    +   * While for a longer list we have "cyan, magenta, yellow, and black"
    +   * Think "{0} start {1} middle {2} middle {3} end {4}"
    +   * The last pattern is "{0}, and {1}." Note the comma before "and".
    +   * So the "Two" pattern can be different than Start/Middle/End ones.
    +   */
    +  this.listTwoPattern_ = goog.labs.i18n.ListFormatSymbols.LIST_TWO;
    +
    +  /**
    +   * String for the start of a list items, containing {0} for the first,
    +   * and {1} for the rest.
    +   * @private {string}
    +   */
    +  this.listStartPattern_ = goog.labs.i18n.ListFormatSymbols.LIST_START;
    +
    +  /**
    +   * String for the start of a list items, containing {0} for the first part
    +   * of the list, and {1} for the rest of the list.
    +   * @private {string}
    +   */
    +  this.listMiddlePattern_ = goog.labs.i18n.ListFormatSymbols.LIST_MIDDLE;
    +
    +  /**
    +   * String for the end of a list items, containing {0} for the first part
    +   * of the list, and {1} for the last item.
    +   *
    +   * This is how start/middle/end come together:
    +   *   start = '{0}, {1}'  middle = '{0}, {1}',  end = '{0}, and {1}'
    +   * will result in the typical English list: 'one, two, three, and four'
    +   * There are languages where the patterns are more complex than
    +   * '{1} someText {1}' and the start pattern is different than the middle one.
    +   *
    +   * @private {string}
    +   */
    +  this.listEndPattern_ = goog.labs.i18n.ListFormatSymbols.LIST_END;
    +};
    +
    +
    +/**
    + * Replaces the {0} and {1} placeholders in a pattern with the first and
    + * the second parameter respectively, and returns the result.
    + * It is a helper function for goog.labs.i18n.ListFormat.format.
    + *
    + * @param {string} pattern used for formatting.
    + * @param {string} first object to add to list.
    + * @param {string} second object to add to list.
    + * @return {string} The formatted list string.
    + * @private
    + */
    +goog.labs.i18n.ListFormat.prototype.patternBasedJoinTwoStrings_ =
    +    function(pattern, first, second) {
    +  return pattern.replace('{0}', first).replace('{1}', second);
    +};
    +
    +
    +/**
    + * Formats an array of strings into a string.
    + * It is a user facing, locale-aware list (i.e. 'red, green, and blue').
    + *
    + * @param {!Array<string|number>} items Items to format.
    + * @return {string} The items formatted into a string, as a list.
    + */
    +goog.labs.i18n.ListFormat.prototype.format = function(items) {
    +  var count = items.length;
    +  switch (count) {
    +    case 0:
    +      return '';
    +    case 1:
    +      return String(items[0]);
    +    case 2:
    +      return this.patternBasedJoinTwoStrings_(this.listTwoPattern_,
    +          String(items[0]), String(items[1]));
    +  }
    +
    +  var result = this.patternBasedJoinTwoStrings_(this.listStartPattern_,
    +      String(items[0]), String(items[1]));
    +
    +  for (var i = 2; i < count - 1; ++i) {
    +    result = this.patternBasedJoinTwoStrings_(this.listMiddlePattern_,
    +        result, String(items[i]));
    +  }
    +
    +  return this.patternBasedJoinTwoStrings_(this.listEndPattern_,
    +      result, String(items[count - 1]));
    +};
    +
    +
    +
    +/**
    + * GenderInfo provides a method to determine the gender of a list/array
    + * of objects when one knows the gender of each item of the list.
    + * It does this in a locale sensitive manner.
    + * The constructor initializes the object based on the locale data from
    + * the current goog.labs.i18n.ListFormatSymbols.
    + *
    + * Similar to the ICU4J class com.icu.util.GenderInfo:
    + *   http://icu-project.org/apiref/icu4j/com/ibm/icu/util/GenderInfo.html
    + * @constructor
    + * @final
    + */
    +goog.labs.i18n.GenderInfo = function() {
    +  /**
    +   * Stores the language-aware mode of determining the gender of a list.
    +   * @private {goog.labs.i18n.GenderInfo.ListGenderStyle_}
    +   */
    +  this.listGenderStyle_ = goog.labs.i18n.ListFormatSymbols.GENDER_STYLE;
    +};
    +
    +
    +/**
    + * Enumeration for the possible ways to generate list genders.
    + * Indicates the category for the locale.
    + * This only affects gender for lists more than one. For lists of 1 item,
    + * the gender of the list always equals the gender of that sole item.
    + * This is for internal use, matching ICU.
    + * @enum {number}
    + * @private
    + */
    +goog.labs.i18n.GenderInfo.ListGenderStyle_ = {
    +  NEUTRAL: 0,
    +  MIXED_NEUTRAL: 1,
    +  MALE_TAINTS: 2
    +};
    +
    +
    +/**
    + * Enumeration for the possible gender values.
    + * Gender: OTHER means either the information is unavailable,
    + * or the person has declined to state MALE or FEMALE.
    + * @enum {number}
    + */
    +goog.labs.i18n.GenderInfo.Gender = {
    +  MALE: 0,
    +  FEMALE: 1,
    +  OTHER: 2
    +};
    +
    +
    +/**
    + * Determines the overal gender of a list based on the gender of all the list
    + * items, in a locale-aware way.
    + * @param {!Array<!goog.labs.i18n.GenderInfo.Gender>} genders An array of
    + *        genders, will give the gender of the list.
    + * @return {goog.labs.i18n.GenderInfo.Gender} Get the gender of the list.
    +*/
    +goog.labs.i18n.GenderInfo.prototype.getListGender = function(genders) {
    +  var Gender = goog.labs.i18n.GenderInfo.Gender;
    +
    +  var count = genders.length;
    +  if (count == 0) {
    +    return Gender.OTHER; // degenerate case
    +  }
    +  if (count == 1) {
    +    return genders[0]; // degenerate case
    +  }
    +
    +  switch (this.listGenderStyle_) {
    +    case goog.labs.i18n.GenderInfo.ListGenderStyle_.NEUTRAL:
    +      return Gender.OTHER;
    +    case goog.labs.i18n.GenderInfo.ListGenderStyle_.MIXED_NEUTRAL:
    +      var hasFemale = false;
    +      var hasMale = false;
    +      for (var i = 0; i < count; ++i) {
    +        switch (genders[i]) {
    +          case Gender.FEMALE:
    +            if (hasMale) {
    +              return Gender.OTHER;
    +            }
    +            hasFemale = true;
    +            break;
    +          case Gender.MALE:
    +            if (hasFemale) {
    +              return Gender.OTHER;
    +            }
    +            hasMale = true;
    +            break;
    +          case Gender.OTHER:
    +            return Gender.OTHER;
    +          default: // Should never happen, but just in case
    +            goog.asserts.assert(false,
    +                'Invalid genders[' + i + '] = ' + genders[i]);
    +            return Gender.OTHER;
    +        }
    +      }
    +      return hasMale ? Gender.MALE : Gender.FEMALE;
    +    case goog.labs.i18n.GenderInfo.ListGenderStyle_.MALE_TAINTS:
    +      for (var i = 0; i < count; ++i) {
    +        if (genders[i] != Gender.FEMALE) {
    +          return Gender.MALE;
    +        }
    +      }
    +      return Gender.FEMALE;
    +    default:
    +      return Gender.OTHER;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.html b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.html
    new file mode 100644
    index 0000000000..c85ea9cf29
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.i18n.ListFormat
    +  </title>
    +  <meta charset="utf-8" />
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.labs.i18n.ListFormatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.js b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.js
    new file mode 100644
    index 0000000000..be52881894
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/i18n/listformat_test.js
    @@ -0,0 +1,324 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.i18n.ListFormatTest');
    +goog.setTestOnly('goog.labs.i18n.ListFormatTest');
    +
    +goog.require('goog.labs.i18n.GenderInfo');
    +goog.require('goog.labs.i18n.ListFormat');
    +goog.require('goog.labs.i18n.ListFormatSymbols');
    +goog.require('goog.labs.i18n.ListFormatSymbols_el');
    +goog.require('goog.labs.i18n.ListFormatSymbols_en');
    +goog.require('goog.labs.i18n.ListFormatSymbols_fr');
    +goog.require('goog.labs.i18n.ListFormatSymbols_ml');
    +goog.require('goog.labs.i18n.ListFormatSymbols_zu');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  // Always switch back to English on startup.
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +}
    +
    +function testListFormatterArrayDirect() {
    +  var fmt = new goog.labs.i18n.ListFormat();
    +  assertEquals(
    +      'One',
    +      fmt.format(['One'])
    +  );
    +  assertEquals(
    +      'One and Two',
    +      fmt.format(['One', 'Two'])
    +  );
    +  assertEquals(
    +      'One, Two, and Three',
    +      fmt.format(['One', 'Two', 'Three'])
    +  );
    +  assertEquals(
    +      'One, Two, Three, Four, Five, and Six',
    +      fmt.format(['One', 'Two', 'Three', 'Four', 'Five', 'Six'])
    +  );
    +}
    +
    +function testListFormatterArrayIndirect() {
    +  var fmt = new goog.labs.i18n.ListFormat();
    +
    +  var items = [];
    +
    +  items.push('One');
    +  assertEquals('One', fmt.format(items));
    +
    +  items.push('Two');
    +  assertEquals('One and Two', fmt.format(items));
    +  items.push('Three');
    +  assertEquals('One, Two, and Three', fmt.format(items));
    +
    +  items.push('Four');
    +  items.push('Five');
    +  items.push('Six');
    +  assertEquals('One, Two, Three, Four, Five, and Six', fmt.format(items));
    +}
    +
    +function testListFormatterFrench() {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr;
    +
    +  var fmt = new goog.labs.i18n.ListFormat();
    +  assertEquals(
    +      'One',
    +      fmt.format(['One'])
    +  );
    +  assertEquals(
    +      'One et Two',
    +      fmt.format(['One', 'Two'])
    +  );
    +  assertEquals(
    +      'One, Two et Three',
    +      fmt.format(['One', 'Two', 'Three'])
    +  );
    +  assertEquals(
    +      'One, Two, Three, Four, Five et Six',
    +      fmt.format(['One', 'Two', 'Three', 'Four', 'Five', 'Six'])
    +  );
    +
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +}
    +
    +// Malayalam and Zulu are the only two locales with pathers
    +// different than '{0} sometext {1}'
    +function testListFormatterSpecialLanguages() {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ml;
    +  var fmt_ml = new goog.labs.i18n.ListFormat();
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zu;
    +  var fmt_zu = new goog.labs.i18n.ListFormat();
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +
    +  // Only the end pattern is special with Malayalam
    +  // Escaped for safety, the string is 'One, Two, Three എന്നിവ'
    +  assertEquals('One, Two, Three \u0D0E\u0D28\u0D4D\u0D28\u0D3F\u0D35',
    +      fmt_ml.format(['One', 'Two', 'Three']));
    +
    +  // Only the two items pattern is special with Zulu
    +  assertEquals('I-One ne-Two', fmt_zu.format(['One', 'Two']));
    +}
    +
    +function testVariousObjectTypes() {
    +  var fmt = new goog.labs.i18n.ListFormat();
    +  var booleanObject = new Boolean(1);
    +  var arrayObject = ['black', 'white'];
    +  // Not sure how "flaky" this is. Firefox and Chrome give the same results,
    +  // but I am not sure if the JavaScript standard specifies exactly what
    +  // Array toString does, for instance.
    +  assertEquals(
    +      'One, black,white, 42, true, and Five',
    +      fmt.format(['One', arrayObject, 42, booleanObject, 'Five'])
    +  );
    +}
    +
    +function testListGendersNeutral() {
    +  var Gender = goog.labs.i18n.GenderInfo.Gender;
    +
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +  var listGen = new goog.labs.i18n.GenderInfo();
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE]));
    +  assertEquals(Gender.FEMALE, listGen.getListGender([Gender.FEMALE]));
    +  assertEquals(Gender.OTHER, listGen.getListGender([Gender.OTHER]));
    +
    +  assertEquals(Gender.OTHER, listGen.getListGender([Gender.MALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.FEMALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.FEMALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.FEMALE, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.MALE, Gender.FEMALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.MALE, Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.FEMALE, Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.FEMALE, Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.OTHER, Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.OTHER, Gender.FEMALE, Gender.MALE]));
    +}
    +
    +function testListGendersMaleTaints() {
    +  var Gender = goog.labs.i18n.GenderInfo.Gender;
    +
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr;
    +  var listGen = new goog.labs.i18n.GenderInfo();
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE]));
    +  assertEquals(Gender.FEMALE, listGen.getListGender([Gender.FEMALE]));
    +  assertEquals(Gender.OTHER, listGen.getListGender([Gender.OTHER]));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.FEMALE, listGen.getListGender([Gender.FEMALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.OTHER, Gender.OTHER]));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE, Gender.OTHER]));
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.FEMALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.FEMALE, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.MALE, Gender.FEMALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.MALE, Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.FEMALE, Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.FEMALE, Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.OTHER, Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE,
    +      listGen.getListGender([Gender.OTHER, Gender.FEMALE, Gender.MALE]));
    +}
    +
    +function testListGendersMixedNeutral() {
    +  var Gender = goog.labs.i18n.GenderInfo.Gender;
    +
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_el;
    +  var listGen = new goog.labs.i18n.GenderInfo();
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE]));
    +  assertEquals(Gender.FEMALE, listGen.getListGender([Gender.FEMALE]));
    +  assertEquals(Gender.OTHER, listGen.getListGender([Gender.OTHER]));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.FEMALE, listGen.getListGender([Gender.FEMALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.FEMALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER, listGen.getListGender([Gender.FEMALE, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.MALE, Gender.FEMALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.MALE, Gender.OTHER, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.FEMALE, Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.FEMALE, Gender.OTHER, Gender.MALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.OTHER, Gender.MALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.OTHER,
    +      listGen.getListGender([Gender.OTHER, Gender.FEMALE, Gender.MALE]));
    +}
    +
    +function testListGendersVariousCallTypes() {
    +  var Gender = goog.labs.i18n.GenderInfo.Gender;
    +
    +  // Using French because with English the results are mostly Gender.OTHER
    +  // so we can detect fewer problems
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr;
    +  var listGen = new goog.labs.i18n.GenderInfo();
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +
    +  // Anynymous Arrays
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE]));
    +  assertEquals(Gender.FEMALE, listGen.getListGender([Gender.FEMALE]));
    +  assertEquals(Gender.OTHER, listGen.getListGender([Gender.OTHER]));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE, Gender.MALE]));
    +  assertEquals(
    +      Gender.FEMALE, listGen.getListGender([Gender.FEMALE, Gender.FEMALE]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.OTHER, Gender.OTHER]));
    +
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.MALE, Gender.FEMALE]));
    +  assertEquals(Gender.MALE, listGen.getListGender([Gender.MALE, Gender.OTHER]));
    +  assertEquals(
    +      Gender.MALE, listGen.getListGender([Gender.FEMALE, Gender.OTHER]));
    +
    +  // Arrays
    +  var arrayM = [Gender.MALE];
    +  var arrayF = [Gender.FEMALE];
    +  var arrayO = [Gender.OTHER];
    +
    +  var arrayMM = [Gender.MALE, Gender.MALE];
    +  var arrayFF = [Gender.FEMALE, Gender.FEMALE];
    +  var arrayOO = [Gender.OTHER, Gender.OTHER];
    +
    +  var arrayMF = [Gender.MALE, Gender.FEMALE];
    +  var arrayMO = [Gender.MALE, Gender.OTHER];
    +  var arrayFO = [Gender.FEMALE, Gender.OTHER];
    +
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayM));
    +  assertEquals(Gender.FEMALE, listGen.getListGender(arrayF));
    +  assertEquals(Gender.OTHER, listGen.getListGender(arrayO));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayMM));
    +  assertEquals(Gender.FEMALE, listGen.getListGender(arrayFF));
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayOO));
    +
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayMF));
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayMO));
    +  assertEquals(Gender.MALE, listGen.getListGender(arrayFO));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbols.js b/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbols.js
    new file mode 100644
    index 0000000000..c62b1d80d7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbols.js
    @@ -0,0 +1,1796 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview List formatting symbols for all locales.
    + *
    + * This file is autogenerated by script.  See
    + * http://go/generate_list_symbols.py using --for_closure
    + * File generated from CLDR ver. 26
    + *
    + * To reduce the file size (which may cause issues in some JS
    + * developing environments), this file will only contain locales
    + * that are usually supported by Google products. It is a super
    + * set of 40 languages. The rest of the data can be found in another file
    + * named "listsymbolsext.js", which will be generated at the same
    + * time as this file.
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could correct CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.labs.i18n.ListFormatSymbols');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_af');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_am');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_az');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_br');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ca');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_chr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cs');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cy');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_da');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_AT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_el');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_AU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_IE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_US');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_ZA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_419');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_ES');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_et');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_eu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fa');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fi');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fil');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ga');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gsw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_haw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_he');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hi');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hy');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_id');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_in');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_is');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_it');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_iw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ja');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ka');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kk');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_km');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ko');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ky');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ln');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lt');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lv');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mk');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ml');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ms');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mt');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_my');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nb');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ne');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_no');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_no_NO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_or');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pa');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_BR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_PT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ro');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_si');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sk');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sv');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ta');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_te');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_th');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tr');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uk');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ur');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vi');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_CN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_HK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_TW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zu');
    +
    +
    +/**
    + * List formatting symbols for locale af.
    + */
    +goog.labs.i18n.ListFormatSymbols_af = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale am.
    + */
    +goog.labs.i18n.ListFormatSymbols_am = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} እና {1}',
    +  LIST_START: '{0}፣ {1}',
    +  LIST_MIDDLE: '{0}፣ {1}',
    +  LIST_END: '{0}, እና {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale az.
    + */
    +goog.labs.i18n.ListFormatSymbols_az = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} və {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} və {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bg.
    + */
    +goog.labs.i18n.ListFormatSymbols_bg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bn.
    + */
    +goog.labs.i18n.ListFormatSymbols_bn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} এবং {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, এবং {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale br.
    + */
    +goog.labs.i18n.ListFormatSymbols_br = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ca.
    + */
    +goog.labs.i18n.ListFormatSymbols_ca = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale chr.
    + */
    +goog.labs.i18n.ListFormatSymbols_chr = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cs.
    + */
    +goog.labs.i18n.ListFormatSymbols_cs = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cy.
    + */
    +goog.labs.i18n.ListFormatSymbols_cy = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale da.
    + */
    +goog.labs.i18n.ListFormatSymbols_da = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de.
    + */
    +goog.labs.i18n.ListFormatSymbols_de = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_AT.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_AT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_CH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale el.
    + */
    +goog.labs.i18n.ListFormatSymbols_el = {
    +  GENDER_STYLE: 1,
    +  LIST_TWO: '{0} και {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} και {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en.
    + */
    +goog.labs.i18n.ListFormatSymbols_en = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_AU.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_AU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GB.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_IE.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_IE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_US.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_US = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_ZA.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_ZA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es.
    + */
    +goog.labs.i18n.ListFormatSymbols_es = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_419.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_419 = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_ES.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_ES = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale et.
    + */
    +goog.labs.i18n.ListFormatSymbols_et = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale eu.
    + */
    +goog.labs.i18n.ListFormatSymbols_eu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} eta {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} eta {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fa.
    + */
    +goog.labs.i18n.ListFormatSymbols_fa = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}،‏ {1}',
    +  LIST_MIDDLE: '{0}،‏ {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fi.
    + */
    +goog.labs.i18n.ListFormatSymbols_fi = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fil.
    + */
    +goog.labs.i18n.ListFormatSymbols_fil = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} at {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, at {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CA.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ga.
    + */
    +goog.labs.i18n.ListFormatSymbols_ga = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} agus {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, agus {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gl.
    + */
    +goog.labs.i18n.ListFormatSymbols_gl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gsw.
    + */
    +goog.labs.i18n.ListFormatSymbols_gsw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gu.
    + */
    +goog.labs.i18n.ListFormatSymbols_gu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} અને {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} અને {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale haw.
    + */
    +goog.labs.i18n.ListFormatSymbols_haw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale he.
    + */
    +goog.labs.i18n.ListFormatSymbols_he = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} ו{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ו{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hi.
    + */
    +goog.labs.i18n.ListFormatSymbols_hi = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} और {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, और {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hr.
    + */
    +goog.labs.i18n.ListFormatSymbols_hr = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hu.
    + */
    +goog.labs.i18n.ListFormatSymbols_hu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} és {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} és {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hy.
    + */
    +goog.labs.i18n.ListFormatSymbols_hy = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} և {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} և {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale id.
    + */
    +goog.labs.i18n.ListFormatSymbols_id = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale in.
    + */
    +goog.labs.i18n.ListFormatSymbols_in = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale is.
    + */
    +goog.labs.i18n.ListFormatSymbols_is = {
    +  GENDER_STYLE: 1,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale it.
    + */
    +goog.labs.i18n.ListFormatSymbols_it = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale iw.
    + */
    +goog.labs.i18n.ListFormatSymbols_iw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ו{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ו{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ja.
    + */
    +goog.labs.i18n.ListFormatSymbols_ja = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}、{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}、{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ka.
    + */
    +goog.labs.i18n.ListFormatSymbols_ka = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} და {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} და {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kk.
    + */
    +goog.labs.i18n.ListFormatSymbols_kk = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} және {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale km.
    + */
    +goog.labs.i18n.ListFormatSymbols_km = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} និង​{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kn.
    + */
    +goog.labs.i18n.ListFormatSymbols_kn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ಮತ್ತು {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, ಮತ್ತು {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ko.
    + */
    +goog.labs.i18n.ListFormatSymbols_ko = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} 및 {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} 및 {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ky.
    + */
    +goog.labs.i18n.ListFormatSymbols_ky = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} жана {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} жана {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ln.
    + */
    +goog.labs.i18n.ListFormatSymbols_ln = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lo.
    + */
    +goog.labs.i18n.ListFormatSymbols_lo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ແລະ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lt.
    + */
    +goog.labs.i18n.ListFormatSymbols_lt = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} ir {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ir {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lv.
    + */
    +goog.labs.i18n.ListFormatSymbols_lv = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} un {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} un {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mk.
    + */
    +goog.labs.i18n.ListFormatSymbols_mk = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ml.
    + */
    +goog.labs.i18n.ListFormatSymbols_ml = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} കൂടാതെ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1} എന്നിവ'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mn.
    + */
    +goog.labs.i18n.ListFormatSymbols_mn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mo.
    + */
    +goog.labs.i18n.ListFormatSymbols_mo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} și {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} și {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mr.
    + */
    +goog.labs.i18n.ListFormatSymbols_mr = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} आणि {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} आणि {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ms.
    + */
    +goog.labs.i18n.ListFormatSymbols_ms = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mt.
    + */
    +goog.labs.i18n.ListFormatSymbols_mt = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} u {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, u {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale my.
    + */
    +goog.labs.i18n.ListFormatSymbols_my = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}နှင့်{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, နှင့်{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nb.
    + */
    +goog.labs.i18n.ListFormatSymbols_nb = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ne.
    + */
    +goog.labs.i18n.ListFormatSymbols_ne = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} र {1}',
    +  LIST_START: '{0} र {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} र {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale no.
    + */
    +goog.labs.i18n.ListFormatSymbols_no = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale no_NO.
    + */
    +goog.labs.i18n.ListFormatSymbols_no_NO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale or.
    + */
    +goog.labs.i18n.ListFormatSymbols_or = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pa.
    + */
    +goog.labs.i18n.ListFormatSymbols_pa = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ਅਤੇ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ਅਤੇ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pl.
    + */
    +goog.labs.i18n.ListFormatSymbols_pl = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_BR.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_BR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_PT.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_PT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ro.
    + */
    +goog.labs.i18n.ListFormatSymbols_ro = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} și {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} și {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sh.
    + */
    +goog.labs.i18n.ListFormatSymbols_sh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale si.
    + */
    +goog.labs.i18n.ListFormatSymbols_si = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} සහ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, සහ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sk.
    + */
    +goog.labs.i18n.ListFormatSymbols_sk = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sl.
    + */
    +goog.labs.i18n.ListFormatSymbols_sl = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} in {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} in {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sq.
    + */
    +goog.labs.i18n.ListFormatSymbols_sq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dhe {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dhe {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sv.
    + */
    +goog.labs.i18n.ListFormatSymbols_sv = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} och {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} och {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sw.
    + */
    +goog.labs.i18n.ListFormatSymbols_sw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} na {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, na {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ta.
    + */
    +goog.labs.i18n.ListFormatSymbols_ta = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} மற்றும் {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} மற்றும் {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale te.
    + */
    +goog.labs.i18n.ListFormatSymbols_te = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} మరియు {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} మరియు {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale th.
    + */
    +goog.labs.i18n.ListFormatSymbols_th = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}และ{1}',
    +  LIST_START: '{0} {1}',
    +  LIST_MIDDLE: '{0} {1}',
    +  LIST_END: '{0} และ{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tl.
    + */
    +goog.labs.i18n.ListFormatSymbols_tl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} at {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, at {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tr.
    + */
    +goog.labs.i18n.ListFormatSymbols_tr = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ve {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ve {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uk.
    + */
    +goog.labs.i18n.ListFormatSymbols_uk = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} і {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} і {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ur.
    + */
    +goog.labs.i18n.ListFormatSymbols_ur = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} اور {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، اور {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} va {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} va {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vi.
    + */
    +goog.labs.i18n.ListFormatSymbols_vi = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} và {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} và {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_CN.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_CN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_HK.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_HK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_TW.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_TW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zu.
    + */
    +goog.labs.i18n.ListFormatSymbols_zu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: 'I-{0} ne-{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, ne-{1}'
    +};
    +
    +
    +/**
    + * Default value, in case nothing else matches
    + */
    +goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +
    +
    +/**
    + * Selecting symbols by locale.
    + */
    +if (goog.LOCALE == 'af') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_af;
    +}
    +
    +if (goog.LOCALE == 'am') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_am;
    +}
    +
    +if (goog.LOCALE == 'ar') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar;
    +}
    +
    +if (goog.LOCALE == 'az') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_az;
    +}
    +
    +if (goog.LOCALE == 'bg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bg;
    +}
    +
    +if (goog.LOCALE == 'bn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bn;
    +}
    +
    +if (goog.LOCALE == 'br') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_br;
    +}
    +
    +if (goog.LOCALE == 'ca') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ca;
    +}
    +
    +if (goog.LOCALE == 'chr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_chr;
    +}
    +
    +if (goog.LOCALE == 'cs') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cs;
    +}
    +
    +if (goog.LOCALE == 'cy') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cy;
    +}
    +
    +if (goog.LOCALE == 'da') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_da;
    +}
    +
    +if (goog.LOCALE == 'de') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de;
    +}
    +
    +if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_AT;
    +}
    +
    +if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_CH;
    +}
    +
    +if (goog.LOCALE == 'el') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_el;
    +}
    +
    +if (goog.LOCALE == 'en') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en;
    +}
    +
    +if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_AU;
    +}
    +
    +if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GB;
    +}
    +
    +if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_IE;
    +}
    +
    +if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_IN;
    +}
    +
    +if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SG;
    +}
    +
    +if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_US;
    +}
    +
    +if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_ZA;
    +}
    +
    +if (goog.LOCALE == 'es') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es;
    +}
    +
    +if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_419;
    +}
    +
    +if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_ES;
    +}
    +
    +if (goog.LOCALE == 'et') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_et;
    +}
    +
    +if (goog.LOCALE == 'eu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_eu;
    +}
    +
    +if (goog.LOCALE == 'fa') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fa;
    +}
    +
    +if (goog.LOCALE == 'fi') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fi;
    +}
    +
    +if (goog.LOCALE == 'fil') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fil;
    +}
    +
    +if (goog.LOCALE == 'fr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr;
    +}
    +
    +if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CA;
    +}
    +
    +if (goog.LOCALE == 'ga') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ga;
    +}
    +
    +if (goog.LOCALE == 'gl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gl;
    +}
    +
    +if (goog.LOCALE == 'gsw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gsw;
    +}
    +
    +if (goog.LOCALE == 'gu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gu;
    +}
    +
    +if (goog.LOCALE == 'haw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_haw;
    +}
    +
    +if (goog.LOCALE == 'he') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_he;
    +}
    +
    +if (goog.LOCALE == 'hi') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hi;
    +}
    +
    +if (goog.LOCALE == 'hr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hr;
    +}
    +
    +if (goog.LOCALE == 'hu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hu;
    +}
    +
    +if (goog.LOCALE == 'hy') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hy;
    +}
    +
    +if (goog.LOCALE == 'id') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_id;
    +}
    +
    +if (goog.LOCALE == 'in') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_in;
    +}
    +
    +if (goog.LOCALE == 'is') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_is;
    +}
    +
    +if (goog.LOCALE == 'it') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_it;
    +}
    +
    +if (goog.LOCALE == 'iw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_iw;
    +}
    +
    +if (goog.LOCALE == 'ja') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ja;
    +}
    +
    +if (goog.LOCALE == 'ka') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ka;
    +}
    +
    +if (goog.LOCALE == 'kk') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kk;
    +}
    +
    +if (goog.LOCALE == 'km') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_km;
    +}
    +
    +if (goog.LOCALE == 'kn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kn;
    +}
    +
    +if (goog.LOCALE == 'ko') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ko;
    +}
    +
    +if (goog.LOCALE == 'ky') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ky;
    +}
    +
    +if (goog.LOCALE == 'ln') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ln;
    +}
    +
    +if (goog.LOCALE == 'lo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lo;
    +}
    +
    +if (goog.LOCALE == 'lt') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lt;
    +}
    +
    +if (goog.LOCALE == 'lv') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lv;
    +}
    +
    +if (goog.LOCALE == 'mk') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mk;
    +}
    +
    +if (goog.LOCALE == 'ml') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ml;
    +}
    +
    +if (goog.LOCALE == 'mn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mn;
    +}
    +
    +if (goog.LOCALE == 'mo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mo;
    +}
    +
    +if (goog.LOCALE == 'mr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mr;
    +}
    +
    +if (goog.LOCALE == 'ms') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ms;
    +}
    +
    +if (goog.LOCALE == 'mt') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mt;
    +}
    +
    +if (goog.LOCALE == 'my') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_my;
    +}
    +
    +if (goog.LOCALE == 'nb') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nb;
    +}
    +
    +if (goog.LOCALE == 'ne') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ne;
    +}
    +
    +if (goog.LOCALE == 'nl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl;
    +}
    +
    +if (goog.LOCALE == 'no') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_no;
    +}
    +
    +if (goog.LOCALE == 'no_NO' || goog.LOCALE == 'no-NO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_no_NO;
    +}
    +
    +if (goog.LOCALE == 'or') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_or;
    +}
    +
    +if (goog.LOCALE == 'pa') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pa;
    +}
    +
    +if (goog.LOCALE == 'pl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pl;
    +}
    +
    +if (goog.LOCALE == 'pt') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt;
    +}
    +
    +if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_BR;
    +}
    +
    +if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_PT;
    +}
    +
    +if (goog.LOCALE == 'ro') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ro;
    +}
    +
    +if (goog.LOCALE == 'ru') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru;
    +}
    +
    +if (goog.LOCALE == 'sh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sh;
    +}
    +
    +if (goog.LOCALE == 'si') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_si;
    +}
    +
    +if (goog.LOCALE == 'sk') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sk;
    +}
    +
    +if (goog.LOCALE == 'sl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sl;
    +}
    +
    +if (goog.LOCALE == 'sq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sq;
    +}
    +
    +if (goog.LOCALE == 'sr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr;
    +}
    +
    +if (goog.LOCALE == 'sv') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sv;
    +}
    +
    +if (goog.LOCALE == 'sw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sw;
    +}
    +
    +if (goog.LOCALE == 'ta') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ta;
    +}
    +
    +if (goog.LOCALE == 'te') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_te;
    +}
    +
    +if (goog.LOCALE == 'th') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_th;
    +}
    +
    +if (goog.LOCALE == 'tl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tl;
    +}
    +
    +if (goog.LOCALE == 'tr') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tr;
    +}
    +
    +if (goog.LOCALE == 'uk') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uk;
    +}
    +
    +if (goog.LOCALE == 'ur') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ur;
    +}
    +
    +if (goog.LOCALE == 'uz') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz;
    +}
    +
    +if (goog.LOCALE == 'vi') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vi;
    +}
    +
    +if (goog.LOCALE == 'zh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh;
    +}
    +
    +if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_CN;
    +}
    +
    +if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_TW;
    +}
    +
    +if (goog.LOCALE == 'zu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zu;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbolsext.js b/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbolsext.js
    new file mode 100644
    index 0000000000..7f76fcd0cf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/i18n/listsymbolsext.js
    @@ -0,0 +1,10088 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview List formatting symbols for all locales.
    + *
    + * This file is autogenerated by script.  See
    + * http://go/generate_list_symbols.py using --for_closure
    + * File generated from CLDR ver. 26
    + *
    + * To reduce the file size (which may cause issues in some JS
    + * developing environments), this file will only contain locales
    + * that are usually supported by Google products. It is a super
    + * set of 40 languages. The rest of the data can be found in another file
    + * named "listsymbolsext.js", which will be generated at the same
    + * time as this file.
    + * Before checkin, this file could have been manually edited. This is
    + * to incorporate changes before we could correct CLDR. All manual
    + * modification must be documented in this section, and should be
    + * removed after those changes land to CLDR.
    + */
    +
    +goog.provide('goog.labs.i18n.ListFormatSymbolsExt');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_af_NA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_af_ZA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_agq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_agq_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ak');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ak_GH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_am_ET');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_001');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_AE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_BH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_DJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_DZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_EG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_EH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_ER');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_IL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_IQ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_JO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_KM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_KW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_LB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_LY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_MR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_OM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_PS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_QA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_SA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_SD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_SO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_SS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_SY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_TD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_TN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ar_YE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_as');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_as_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_asa');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_asa_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_az_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_az_Cyrl_AZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_az_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_az_Latn_AZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bas');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bas_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_be');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_be_BY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bem');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bem_ZM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bez');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bez_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bg_BG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bm');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bm_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bm_Latn_ML');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bn_BD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bn_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bo_CN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bo_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_br_FR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_brx');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_brx_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bs');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bs_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bs_Cyrl_BA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bs_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_bs_Latn_BA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ca_AD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ca_ES');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ca_FR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ca_IT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cgg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cgg_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_chr_US');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cs_CZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_cy_GB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_da_DK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_da_GL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dav');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dav_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_BE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_DE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_LI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_de_LU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dje');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dje_NE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dsb');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dsb_DE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dua');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dua_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dyo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dyo_SN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dz');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_dz_BT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ebu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ebu_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ee');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ee_GH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ee_TG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_el_CY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_el_GR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_001');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_150');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_AG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_AI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_AS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_BZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_CA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_CC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_CK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_CX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_DG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_DM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_ER');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_FJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_FK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_FM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_GY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_HK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_IM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_IO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_JE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_JM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_KI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_KN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_KY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_LC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_LR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_LS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MP');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_MY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_NZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_PW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_RW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_SZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TV');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_UM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_US_POSIX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_VC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_VG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_VI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_VU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_WS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_ZM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_en_ZW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_eo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_AR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_BO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_CL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_CO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_CR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_CU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_DO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_EA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_EC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_GQ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_GT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_HN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_IC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_MX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_NI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_PA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_PE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_PH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_PR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_PY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_SV');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_US');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_UY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_es_VE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_et_EE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_eu_ES');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ewo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ewo_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fa_AF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fa_IR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ff');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ff_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ff_GN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ff_MR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ff_SN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fi_FI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fil_PH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fo_FO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_BE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_BF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_BI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_BJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_BL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_DJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_DZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_FR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_GA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_GF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_GN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_GP');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_GQ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_HT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_KM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_LU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_ML');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MQ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_MU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_NC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_NE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_PF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_PM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_RE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_RW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_SC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_SN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_SY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_TD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_TG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_TN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_VU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_WF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fr_YT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fur');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fur_IT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fy');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_fy_NL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ga_IE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gd');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gd_GB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gl_ES');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gsw_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gsw_FR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gsw_LI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gu_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_guz');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_guz_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gv');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_gv_IM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ha');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ha_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ha_Latn_GH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ha_Latn_NE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ha_Latn_NG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_haw_US');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_he_IL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hi_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hr_BA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hr_HR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hsb');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hsb_DE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hu_HU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_hy_AM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_id_ID');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ig');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ig_NG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ii');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ii_CN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_is_IS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_it_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_it_IT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_it_SM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ja_JP');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_jgo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_jgo_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_jmc');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_jmc_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ka_GE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kab');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kab_DZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kam');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kam_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kde');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kde_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kea');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kea_CV');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_khq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_khq_ML');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ki');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ki_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kk_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kk_Cyrl_KZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kkj');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kkj_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kl_GL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kln');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kln_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_km_KH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kn_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ko_KP');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ko_KR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kok');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kok_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ks');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ks_Arab');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ks_Arab_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksb');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksb_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksf');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksf_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ksh_DE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_kw_GB');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ky_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ky_Cyrl_KG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lag');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lag_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lb');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lb_LU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lg_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lkt');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lkt_US');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ln_AO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ln_CD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ln_CF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ln_CG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lo_LA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lt_LT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lu_CD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_luo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_luo_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_luy');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_luy_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_lv_LV');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mas');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mas_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mas_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mer');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mer_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mfe');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mfe_MU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mg_MG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mgh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mgh_MZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mgo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mgo_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mk_MK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ml_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mn_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mn_Cyrl_MN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mr_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ms_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ms_Latn_BN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ms_Latn_MY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ms_Latn_SG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mt_MT');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mua');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_mua_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_my_MM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_naq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_naq_NA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nb_NO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nb_SJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nd');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nd_ZW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ne_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ne_NP');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_AW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_BE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_BQ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_CW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_NL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_SR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nl_SX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nmg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nmg_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nn_NO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nnh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nnh_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nus');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nus_SD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nyn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_nyn_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_om');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_om_ET');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_om_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_or_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_os');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_os_GE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_os_RU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pa_Arab');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pa_Arab_PK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pa_Guru');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pa_Guru_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pl_PL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ps');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ps_AF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_AO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_CV');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_GW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_MO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_MZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_ST');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_pt_TL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_qu');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_qu_BO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_qu_EC');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_qu_PE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rm');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rm_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rn_BI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ro_MD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ro_RO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rof');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rof_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_BY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_KG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_KZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_MD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_RU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ru_UA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rw');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rw_RW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rwk');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_rwk_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sah');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sah_RU');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_saq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_saq_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sbp');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sbp_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_se');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_se_FI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_se_NO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_se_SE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_seh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_seh_MZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ses');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ses_ML');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sg');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sg_CF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_shi');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_shi_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_shi_Latn_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_shi_Tfng');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_shi_Tfng_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_si_LK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sk_SK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sl_SI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_smn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_smn_FI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sn_ZW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_so');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_so_DJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_so_ET');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_so_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_so_SO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sq_AL');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sq_MK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sq_XK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Cyrl_BA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Cyrl_ME');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Cyrl_RS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Cyrl_XK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Latn_BA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Latn_ME');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Latn_RS');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sr_Latn_XK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sv_AX');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sv_FI');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sv_SE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sw_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sw_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_sw_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_swc');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_swc_CD');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ta_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ta_LK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ta_MY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ta_SG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_te_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_teo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_teo_KE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_teo_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_th_TH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ti');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ti_ER');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ti_ET');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_to');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_to_TO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tr_CY');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tr_TR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_twq');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_twq_NE');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tzm');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tzm_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_tzm_Latn_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ug');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ug_Arab');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ug_Arab_CN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uk_UA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ur_IN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_ur_PK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Arab');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Arab_AF');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Cyrl');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Cyrl_UZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_uz_Latn_UZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vai');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vai_Latn');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vai_Latn_LR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vai_Vaii');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vai_Vaii_LR');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vi_VN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vun');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_vun_TZ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_wae');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_wae_CH');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_xog');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_xog_UG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yav');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yav_CM');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yi');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yi_001');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yo');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yo_BJ');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_yo_NG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zgh');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zgh_MA');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hans');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hans_CN');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hans_HK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hans_MO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hans_SG');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hant');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hant_HK');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hant_MO');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zh_Hant_TW');
    +goog.provide('goog.labs.i18n.ListFormatSymbols_zu_ZA');
    +
    +goog.require('goog.labs.i18n.ListFormatSymbols');
    +
    +
    +/**
    + * List formatting symbols for locale af_NA.
    + */
    +goog.labs.i18n.ListFormatSymbols_af_NA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale af_ZA.
    + */
    +goog.labs.i18n.ListFormatSymbols_af_ZA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale agq.
    + */
    +goog.labs.i18n.ListFormatSymbols_agq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale agq_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_agq_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ak.
    + */
    +goog.labs.i18n.ListFormatSymbols_ak = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ak_GH.
    + */
    +goog.labs.i18n.ListFormatSymbols_ak_GH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale am_ET.
    + */
    +goog.labs.i18n.ListFormatSymbols_am_ET = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} እና {1}',
    +  LIST_START: '{0}፣ {1}',
    +  LIST_MIDDLE: '{0}፣ {1}',
    +  LIST_END: '{0}, እና {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_001.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_001 = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_AE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_AE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_BH.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_BH = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_DJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_DJ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_DZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_DZ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_EG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_EG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و{1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_EH.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_EH = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_ER.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_ER = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_IL.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_IL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_IQ.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_IQ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_JO.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_JO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_KM.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_KM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_KW.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_KW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_LB.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_LB = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_LY.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_LY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_MA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_MR.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_MR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_OM.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_OM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_PS.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_PS = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_QA.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_QA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_SA.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_SA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_SD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_SD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_SO.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_SO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_SS.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_SS = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_SY.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_SY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_TD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_TD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_TN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_TN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ar_YE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ar_YE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale as.
    + */
    +goog.labs.i18n.ListFormatSymbols_as = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale as_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_as_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale asa.
    + */
    +goog.labs.i18n.ListFormatSymbols_asa = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale asa_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_asa_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale az_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_az_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale az_Cyrl_AZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_az_Cyrl_AZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale az_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_az_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} və {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} və {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale az_Latn_AZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_az_Latn_AZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} və {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} və {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bas.
    + */
    +goog.labs.i18n.ListFormatSymbols_bas = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bas_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_bas_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale be.
    + */
    +goog.labs.i18n.ListFormatSymbols_be = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale be_BY.
    + */
    +goog.labs.i18n.ListFormatSymbols_be_BY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bem.
    + */
    +goog.labs.i18n.ListFormatSymbols_bem = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bem_ZM.
    + */
    +goog.labs.i18n.ListFormatSymbols_bem_ZM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bez.
    + */
    +goog.labs.i18n.ListFormatSymbols_bez = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bez_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_bez_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bg_BG.
    + */
    +goog.labs.i18n.ListFormatSymbols_bg_BG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bm.
    + */
    +goog.labs.i18n.ListFormatSymbols_bm = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bm_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_bm_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bm_Latn_ML.
    + */
    +goog.labs.i18n.ListFormatSymbols_bm_Latn_ML = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bn_BD.
    + */
    +goog.labs.i18n.ListFormatSymbols_bn_BD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} এবং {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, এবং {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bn_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_bn_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} এবং {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, এবং {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bo.
    + */
    +goog.labs.i18n.ListFormatSymbols_bo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bo_CN.
    + */
    +goog.labs.i18n.ListFormatSymbols_bo_CN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bo_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_bo_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale br_FR.
    + */
    +goog.labs.i18n.ListFormatSymbols_br_FR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale brx.
    + */
    +goog.labs.i18n.ListFormatSymbols_brx = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale brx_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_brx_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bs.
    + */
    +goog.labs.i18n.ListFormatSymbols_bs = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bs_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_bs_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bs_Cyrl_BA.
    + */
    +goog.labs.i18n.ListFormatSymbols_bs_Cyrl_BA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bs_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_bs_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale bs_Latn_BA.
    + */
    +goog.labs.i18n.ListFormatSymbols_bs_Latn_BA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ca_AD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ca_AD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ca_ES.
    + */
    +goog.labs.i18n.ListFormatSymbols_ca_ES = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ca_FR.
    + */
    +goog.labs.i18n.ListFormatSymbols_ca_FR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ca_IT.
    + */
    +goog.labs.i18n.ListFormatSymbols_ca_IT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cgg.
    + */
    +goog.labs.i18n.ListFormatSymbols_cgg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cgg_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_cgg_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale chr_US.
    + */
    +goog.labs.i18n.ListFormatSymbols_chr_US = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cs_CZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_cs_CZ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale cy_GB.
    + */
    +goog.labs.i18n.ListFormatSymbols_cy_GB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale da_DK.
    + */
    +goog.labs.i18n.ListFormatSymbols_da_DK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale da_GL.
    + */
    +goog.labs.i18n.ListFormatSymbols_da_GL = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dav.
    + */
    +goog.labs.i18n.ListFormatSymbols_dav = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dav_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_dav_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_BE.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_BE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_DE.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_DE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_LI.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_LI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale de_LU.
    + */
    +goog.labs.i18n.ListFormatSymbols_de_LU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dje.
    + */
    +goog.labs.i18n.ListFormatSymbols_dje = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dje_NE.
    + */
    +goog.labs.i18n.ListFormatSymbols_dje_NE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dsb.
    + */
    +goog.labs.i18n.ListFormatSymbols_dsb = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dsb_DE.
    + */
    +goog.labs.i18n.ListFormatSymbols_dsb_DE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dua.
    + */
    +goog.labs.i18n.ListFormatSymbols_dua = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dua_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_dua_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dyo.
    + */
    +goog.labs.i18n.ListFormatSymbols_dyo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dyo_SN.
    + */
    +goog.labs.i18n.ListFormatSymbols_dyo_SN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dz.
    + */
    +goog.labs.i18n.ListFormatSymbols_dz = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} དང་ {1}',
    +  LIST_START: '{0} དང་ {1}',
    +  LIST_MIDDLE: '{0} དང་ {1}',
    +  LIST_END: '{0} དང་ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale dz_BT.
    + */
    +goog.labs.i18n.ListFormatSymbols_dz_BT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} དང་ {1}',
    +  LIST_START: '{0} དང་ {1}',
    +  LIST_MIDDLE: '{0} དང་ {1}',
    +  LIST_END: '{0} དང་ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ebu.
    + */
    +goog.labs.i18n.ListFormatSymbols_ebu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ebu_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ebu_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ee.
    + */
    +goog.labs.i18n.ListFormatSymbols_ee = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} kple {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, kple {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ee_GH.
    + */
    +goog.labs.i18n.ListFormatSymbols_ee_GH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} kple {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, kple {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ee_TG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ee_TG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} kple {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, kple {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale el_CY.
    + */
    +goog.labs.i18n.ListFormatSymbols_el_CY = {
    +  GENDER_STYLE: 1,
    +  LIST_TWO: '{0} και {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} και {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale el_GR.
    + */
    +goog.labs.i18n.ListFormatSymbols_el_GR = {
    +  GENDER_STYLE: 1,
    +  LIST_TWO: '{0} και {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} και {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_001.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_001 = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_150.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_150 = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_AG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_AG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_AI.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_AI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_AS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_AS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BB.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BE.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BW.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_BZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_BZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_CA.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_CA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_CC.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_CC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_CK.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_CK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_CX.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_CX = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_DG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_DG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_DM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_DM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_ER.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_ER = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_FJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_FJ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_FK.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_FK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_FM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_FM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GD.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GH.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GI.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GU.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_GY.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_GY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_HK.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_HK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_IM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_IM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_IO.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_IO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_JE.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_JE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_JM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_JM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_KI.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_KI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_KN.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_KN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_KY.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_KY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_LC.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_LC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_LR.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_LR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_LS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_LS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MH.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MO.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MP.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MP = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MT.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MU.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MW.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_MY.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_MY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NA.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NF.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NR.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NU.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_NZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_NZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PH.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PK.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PN.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PR.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_PW.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_PW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_RW.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_RW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SB.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SC.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SD.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SH.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SL.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SL = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SX.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SX = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_SZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_SZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TC.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TK.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TO.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TT.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TV.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TV = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_UM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_UM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_US_POSIX.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_US_POSIX = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_VC.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_VC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_VG.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_VG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_VI.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_VI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_VU.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_VU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_WS.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_WS = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_ZM.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_ZM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale en_ZW.
    + */
    +goog.labs.i18n.ListFormatSymbols_en_ZW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale eo.
    + */
    +goog.labs.i18n.ListFormatSymbols_eo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_AR.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_AR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_BO.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_BO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_CL.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_CL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_CO.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_CO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_CR.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_CR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_CU.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_CU = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_DO.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_DO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_EA.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_EA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_EC.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_EC = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_GQ.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_GQ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_GT.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_GT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_HN.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_HN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_IC.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_IC = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_MX.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_MX = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_NI.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_NI = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_PA.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_PA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_PE.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_PE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_PH.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_PH = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_PR.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_PR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_PY.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_PY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_SV.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_SV = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_US.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_US = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_UY.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_UY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale es_VE.
    + */
    +goog.labs.i18n.ListFormatSymbols_es_VE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale et_EE.
    + */
    +goog.labs.i18n.ListFormatSymbols_et_EE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale eu_ES.
    + */
    +goog.labs.i18n.ListFormatSymbols_eu_ES = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} eta {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} eta {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ewo.
    + */
    +goog.labs.i18n.ListFormatSymbols_ewo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ewo_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_ewo_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fa_AF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fa_AF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}،‏ {1}',
    +  LIST_MIDDLE: '{0}،‏ {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fa_IR.
    + */
    +goog.labs.i18n.ListFormatSymbols_fa_IR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} و {1}',
    +  LIST_START: '{0}،‏ {1}',
    +  LIST_MIDDLE: '{0}،‏ {1}',
    +  LIST_END: '{0}، و {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ff.
    + */
    +goog.labs.i18n.ListFormatSymbols_ff = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ff_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_ff_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ff_GN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ff_GN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ff_MR.
    + */
    +goog.labs.i18n.ListFormatSymbols_ff_MR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ff_SN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ff_SN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fi_FI.
    + */
    +goog.labs.i18n.ListFormatSymbols_fi_FI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fil_PH.
    + */
    +goog.labs.i18n.ListFormatSymbols_fil_PH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} at {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, at {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fo.
    + */
    +goog.labs.i18n.ListFormatSymbols_fo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fo_FO.
    + */
    +goog.labs.i18n.ListFormatSymbols_fo_FO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_BE.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_BE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_BF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_BF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_BI.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_BI = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_BJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_BJ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_BL.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_BL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CD.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CG.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CH = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CI.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CI = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_CM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_DJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_DJ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_DZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_DZ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_FR.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_FR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_GA.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_GA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_GF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_GF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_GN.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_GN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_GP.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_GP = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_GQ.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_GQ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_HT.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_HT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_KM.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_KM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_LU.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_LU = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MC.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MC = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MG.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_ML.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_ML = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MQ.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MQ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MR.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_MU.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_MU = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_NC.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_NC = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_NE.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_NE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_PF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_PF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_PM.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_PM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_RE.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_RE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_RW.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_RW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_SC.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_SC = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_SN.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_SN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_SY.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_SY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_TD.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_TD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_TG.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_TG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_TN.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_TN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_VU.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_VU = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_WF.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_WF = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fr_YT.
    + */
    +goog.labs.i18n.ListFormatSymbols_fr_YT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} et {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} et {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fur.
    + */
    +goog.labs.i18n.ListFormatSymbols_fur = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fur_IT.
    + */
    +goog.labs.i18n.ListFormatSymbols_fur_IT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fy.
    + */
    +goog.labs.i18n.ListFormatSymbols_fy = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale fy_NL.
    + */
    +goog.labs.i18n.ListFormatSymbols_fy_NL = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ga_IE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ga_IE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} agus {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, agus {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gd.
    + */
    +goog.labs.i18n.ListFormatSymbols_gd = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} agus {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} agus {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gd_GB.
    + */
    +goog.labs.i18n.ListFormatSymbols_gd_GB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} agus {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} agus {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gl_ES.
    + */
    +goog.labs.i18n.ListFormatSymbols_gl_ES = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gsw_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_gsw_CH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gsw_FR.
    + */
    +goog.labs.i18n.ListFormatSymbols_gsw_FR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gsw_LI.
    + */
    +goog.labs.i18n.ListFormatSymbols_gsw_LI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gu_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_gu_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} અને {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} અને {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale guz.
    + */
    +goog.labs.i18n.ListFormatSymbols_guz = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale guz_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_guz_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gv.
    + */
    +goog.labs.i18n.ListFormatSymbols_gv = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale gv_IM.
    + */
    +goog.labs.i18n.ListFormatSymbols_gv_IM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ha.
    + */
    +goog.labs.i18n.ListFormatSymbols_ha = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ha_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_ha_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ha_Latn_GH.
    + */
    +goog.labs.i18n.ListFormatSymbols_ha_Latn_GH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ha_Latn_NE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ha_Latn_NE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ha_Latn_NG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ha_Latn_NG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale haw_US.
    + */
    +goog.labs.i18n.ListFormatSymbols_haw_US = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale he_IL.
    + */
    +goog.labs.i18n.ListFormatSymbols_he_IL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} ו{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ו{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hi_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_hi_IN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} और {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, और {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hr_BA.
    + */
    +goog.labs.i18n.ListFormatSymbols_hr_BA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hr_HR.
    + */
    +goog.labs.i18n.ListFormatSymbols_hr_HR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hsb.
    + */
    +goog.labs.i18n.ListFormatSymbols_hsb = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hsb_DE.
    + */
    +goog.labs.i18n.ListFormatSymbols_hsb_DE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hu_HU.
    + */
    +goog.labs.i18n.ListFormatSymbols_hu_HU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} és {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} és {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale hy_AM.
    + */
    +goog.labs.i18n.ListFormatSymbols_hy_AM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} և {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} և {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale id_ID.
    + */
    +goog.labs.i18n.ListFormatSymbols_id_ID = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ig.
    + */
    +goog.labs.i18n.ListFormatSymbols_ig = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ig_NG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ig_NG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ii.
    + */
    +goog.labs.i18n.ListFormatSymbols_ii = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ii_CN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ii_CN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale is_IS.
    + */
    +goog.labs.i18n.ListFormatSymbols_is_IS = {
    +  GENDER_STYLE: 1,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale it_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_it_CH = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale it_IT.
    + */
    +goog.labs.i18n.ListFormatSymbols_it_IT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale it_SM.
    + */
    +goog.labs.i18n.ListFormatSymbols_it_SM = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ja_JP.
    + */
    +goog.labs.i18n.ListFormatSymbols_ja_JP = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}、{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}、{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale jgo.
    + */
    +goog.labs.i18n.ListFormatSymbols_jgo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} pɔp {1}',
    +  LIST_START: '{0}, ŋ́gɛ {1}',
    +  LIST_MIDDLE: '{0}, ŋ́gɛ {1}',
    +  LIST_END: '{0}, ḿbɛn ŋ́gɛ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale jgo_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_jgo_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} pɔp {1}',
    +  LIST_START: '{0}, ŋ́gɛ {1}',
    +  LIST_MIDDLE: '{0}, ŋ́gɛ {1}',
    +  LIST_END: '{0}, ḿbɛn ŋ́gɛ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale jmc.
    + */
    +goog.labs.i18n.ListFormatSymbols_jmc = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale jmc_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_jmc_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ka_GE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ka_GE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} და {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} და {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kab.
    + */
    +goog.labs.i18n.ListFormatSymbols_kab = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kab_DZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_kab_DZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kam.
    + */
    +goog.labs.i18n.ListFormatSymbols_kam = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kam_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_kam_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kde.
    + */
    +goog.labs.i18n.ListFormatSymbols_kde = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kde_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_kde_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kea.
    + */
    +goog.labs.i18n.ListFormatSymbols_kea = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kea_CV.
    + */
    +goog.labs.i18n.ListFormatSymbols_kea_CV = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} y {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} y {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale khq.
    + */
    +goog.labs.i18n.ListFormatSymbols_khq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale khq_ML.
    + */
    +goog.labs.i18n.ListFormatSymbols_khq_ML = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ki.
    + */
    +goog.labs.i18n.ListFormatSymbols_ki = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ki_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ki_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kk_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_kk_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} және {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kk_Cyrl_KZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_kk_Cyrl_KZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} және {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kkj.
    + */
    +goog.labs.i18n.ListFormatSymbols_kkj = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kkj_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_kkj_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kl.
    + */
    +goog.labs.i18n.ListFormatSymbols_kl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kl_GL.
    + */
    +goog.labs.i18n.ListFormatSymbols_kl_GL = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kln.
    + */
    +goog.labs.i18n.ListFormatSymbols_kln = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kln_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_kln_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale km_KH.
    + */
    +goog.labs.i18n.ListFormatSymbols_km_KH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} និង​{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kn_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_kn_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ಮತ್ತು {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, ಮತ್ತು {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ko_KP.
    + */
    +goog.labs.i18n.ListFormatSymbols_ko_KP = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} 및 {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} 및 {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ko_KR.
    + */
    +goog.labs.i18n.ListFormatSymbols_ko_KR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} 및 {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} 및 {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kok.
    + */
    +goog.labs.i18n.ListFormatSymbols_kok = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kok_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_kok_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ks.
    + */
    +goog.labs.i18n.ListFormatSymbols_ks = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ks_Arab.
    + */
    +goog.labs.i18n.ListFormatSymbols_ks_Arab = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ks_Arab_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ks_Arab_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksb.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksb = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksb_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksb_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksf.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksf = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksf_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksf_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksh.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} un {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} un {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ksh_DE.
    + */
    +goog.labs.i18n.ListFormatSymbols_ksh_DE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} un {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} un {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kw.
    + */
    +goog.labs.i18n.ListFormatSymbols_kw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale kw_GB.
    + */
    +goog.labs.i18n.ListFormatSymbols_kw_GB = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ky_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_ky_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} жана {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} жана {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ky_Cyrl_KG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ky_Cyrl_KG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} жана {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} жана {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lag.
    + */
    +goog.labs.i18n.ListFormatSymbols_lag = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lag_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_lag_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lb.
    + */
    +goog.labs.i18n.ListFormatSymbols_lb = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a(n) {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a(n) {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lb_LU.
    + */
    +goog.labs.i18n.ListFormatSymbols_lb_LU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} a(n) {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a(n) {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lg.
    + */
    +goog.labs.i18n.ListFormatSymbols_lg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lg_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_lg_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lkt.
    + */
    +goog.labs.i18n.ListFormatSymbols_lkt = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lkt_US.
    + */
    +goog.labs.i18n.ListFormatSymbols_lkt_US = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ln_AO.
    + */
    +goog.labs.i18n.ListFormatSymbols_ln_AO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ln_CD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ln_CD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ln_CF.
    + */
    +goog.labs.i18n.ListFormatSymbols_ln_CF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ln_CG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ln_CG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lo_LA.
    + */
    +goog.labs.i18n.ListFormatSymbols_lo_LA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ແລະ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lt_LT.
    + */
    +goog.labs.i18n.ListFormatSymbols_lt_LT = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} ir {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ir {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lu.
    + */
    +goog.labs.i18n.ListFormatSymbols_lu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lu_CD.
    + */
    +goog.labs.i18n.ListFormatSymbols_lu_CD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale luo.
    + */
    +goog.labs.i18n.ListFormatSymbols_luo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale luo_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_luo_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale luy.
    + */
    +goog.labs.i18n.ListFormatSymbols_luy = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale luy_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_luy_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale lv_LV.
    + */
    +goog.labs.i18n.ListFormatSymbols_lv_LV = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} un {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} un {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mas.
    + */
    +goog.labs.i18n.ListFormatSymbols_mas = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mas_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_mas_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mas_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_mas_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mer.
    + */
    +goog.labs.i18n.ListFormatSymbols_mer = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mer_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_mer_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mfe.
    + */
    +goog.labs.i18n.ListFormatSymbols_mfe = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mfe_MU.
    + */
    +goog.labs.i18n.ListFormatSymbols_mfe_MU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mg.
    + */
    +goog.labs.i18n.ListFormatSymbols_mg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mg_MG.
    + */
    +goog.labs.i18n.ListFormatSymbols_mg_MG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mgh.
    + */
    +goog.labs.i18n.ListFormatSymbols_mgh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mgh_MZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_mgh_MZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mgo.
    + */
    +goog.labs.i18n.ListFormatSymbols_mgo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mgo_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_mgo_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mk_MK.
    + */
    +goog.labs.i18n.ListFormatSymbols_mk_MK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ml_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ml_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} കൂടാതെ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1} എന്നിവ'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mn_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_mn_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mn_Cyrl_MN.
    + */
    +goog.labs.i18n.ListFormatSymbols_mn_Cyrl_MN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mr_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_mr_IN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} आणि {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} आणि {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ms_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_ms_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ms_Latn_BN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ms_Latn_BN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ms_Latn_MY.
    + */
    +goog.labs.i18n.ListFormatSymbols_ms_Latn_MY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ms_Latn_SG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ms_Latn_SG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dan {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dan {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mt_MT.
    + */
    +goog.labs.i18n.ListFormatSymbols_mt_MT = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} u {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, u {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mua.
    + */
    +goog.labs.i18n.ListFormatSymbols_mua = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale mua_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_mua_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale my_MM.
    + */
    +goog.labs.i18n.ListFormatSymbols_my_MM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}နှင့်{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, နှင့်{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale naq.
    + */
    +goog.labs.i18n.ListFormatSymbols_naq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale naq_NA.
    + */
    +goog.labs.i18n.ListFormatSymbols_naq_NA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nb_NO.
    + */
    +goog.labs.i18n.ListFormatSymbols_nb_NO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nb_SJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_nb_SJ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nd.
    + */
    +goog.labs.i18n.ListFormatSymbols_nd = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nd_ZW.
    + */
    +goog.labs.i18n.ListFormatSymbols_nd_ZW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ne_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ne_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} र {1}',
    +  LIST_START: '{0} र {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} र {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ne_NP.
    + */
    +goog.labs.i18n.ListFormatSymbols_ne_NP = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} र {1}',
    +  LIST_START: '{0} र {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} र {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_AW.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_AW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_BE.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_BE = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_BQ.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_BQ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_CW.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_CW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_NL.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_NL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_SR.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_SR = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nl_SX.
    + */
    +goog.labs.i18n.ListFormatSymbols_nl_SX = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} en {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} en {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nmg.
    + */
    +goog.labs.i18n.ListFormatSymbols_nmg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nmg_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_nmg_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nn.
    + */
    +goog.labs.i18n.ListFormatSymbols_nn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nn_NO.
    + */
    +goog.labs.i18n.ListFormatSymbols_nn_NO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} og {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} og {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nnh.
    + */
    +goog.labs.i18n.ListFormatSymbols_nnh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nnh_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_nnh_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nus.
    + */
    +goog.labs.i18n.ListFormatSymbols_nus = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nus_SD.
    + */
    +goog.labs.i18n.ListFormatSymbols_nus_SD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nyn.
    + */
    +goog.labs.i18n.ListFormatSymbols_nyn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale nyn_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_nyn_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale om.
    + */
    +goog.labs.i18n.ListFormatSymbols_om = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale om_ET.
    + */
    +goog.labs.i18n.ListFormatSymbols_om_ET = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale om_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_om_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale or_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_or_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale os.
    + */
    +goog.labs.i18n.ListFormatSymbols_os = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ӕмӕ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ӕмӕ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale os_GE.
    + */
    +goog.labs.i18n.ListFormatSymbols_os_GE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ӕмӕ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ӕмӕ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale os_RU.
    + */
    +goog.labs.i18n.ListFormatSymbols_os_RU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ӕмӕ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ӕмӕ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pa_Arab.
    + */
    +goog.labs.i18n.ListFormatSymbols_pa_Arab = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pa_Arab_PK.
    + */
    +goog.labs.i18n.ListFormatSymbols_pa_Arab_PK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pa_Guru.
    + */
    +goog.labs.i18n.ListFormatSymbols_pa_Guru = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ਅਤੇ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ਅਤੇ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pa_Guru_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_pa_Guru_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ਅਤੇ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ਅਤੇ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pl_PL.
    + */
    +goog.labs.i18n.ListFormatSymbols_pl_PL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ps.
    + */
    +goog.labs.i18n.ListFormatSymbols_ps = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ps_AF.
    + */
    +goog.labs.i18n.ListFormatSymbols_ps_AF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_AO.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_AO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_CV.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_CV = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_GW.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_GW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_MO.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_MO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_MZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_MZ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_ST.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_ST = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale pt_TL.
    + */
    +goog.labs.i18n.ListFormatSymbols_pt_TL = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} e {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} e {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale qu.
    + */
    +goog.labs.i18n.ListFormatSymbols_qu = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale qu_BO.
    + */
    +goog.labs.i18n.ListFormatSymbols_qu_BO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale qu_EC.
    + */
    +goog.labs.i18n.ListFormatSymbols_qu_EC = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale qu_PE.
    + */
    +goog.labs.i18n.ListFormatSymbols_qu_PE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rm.
    + */
    +goog.labs.i18n.ListFormatSymbols_rm = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rm_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_rm_CH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rn.
    + */
    +goog.labs.i18n.ListFormatSymbols_rn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rn_BI.
    + */
    +goog.labs.i18n.ListFormatSymbols_rn_BI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ro_MD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ro_MD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} și {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} și {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ro_RO.
    + */
    +goog.labs.i18n.ListFormatSymbols_ro_RO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} și {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} și {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rof.
    + */
    +goog.labs.i18n.ListFormatSymbols_rof = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rof_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_rof_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_BY.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_BY = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_KG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_KG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_KZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_KZ = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_MD.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_MD = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_RU.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_RU = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ru_UA.
    + */
    +goog.labs.i18n.ListFormatSymbols_ru_UA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rw.
    + */
    +goog.labs.i18n.ListFormatSymbols_rw = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rw_RW.
    + */
    +goog.labs.i18n.ListFormatSymbols_rw_RW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rwk.
    + */
    +goog.labs.i18n.ListFormatSymbols_rwk = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale rwk_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_rwk_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sah.
    + */
    +goog.labs.i18n.ListFormatSymbols_sah = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sah_RU.
    + */
    +goog.labs.i18n.ListFormatSymbols_sah_RU = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale saq.
    + */
    +goog.labs.i18n.ListFormatSymbols_saq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale saq_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_saq_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sbp.
    + */
    +goog.labs.i18n.ListFormatSymbols_sbp = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sbp_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_sbp_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale se.
    + */
    +goog.labs.i18n.ListFormatSymbols_se = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale se_FI.
    + */
    +goog.labs.i18n.ListFormatSymbols_se_FI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale se_NO.
    + */
    +goog.labs.i18n.ListFormatSymbols_se_NO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale se_SE.
    + */
    +goog.labs.i18n.ListFormatSymbols_se_SE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ja {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ja {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale seh.
    + */
    +goog.labs.i18n.ListFormatSymbols_seh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale seh_MZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_seh_MZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ses.
    + */
    +goog.labs.i18n.ListFormatSymbols_ses = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ses_ML.
    + */
    +goog.labs.i18n.ListFormatSymbols_ses_ML = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sg.
    + */
    +goog.labs.i18n.ListFormatSymbols_sg = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sg_CF.
    + */
    +goog.labs.i18n.ListFormatSymbols_sg_CF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale shi.
    + */
    +goog.labs.i18n.ListFormatSymbols_shi = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale shi_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_shi_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale shi_Latn_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_shi_Latn_MA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale shi_Tfng.
    + */
    +goog.labs.i18n.ListFormatSymbols_shi_Tfng = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale shi_Tfng_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_shi_Tfng_MA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale si_LK.
    + */
    +goog.labs.i18n.ListFormatSymbols_si_LK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} සහ {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, සහ {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sk_SK.
    + */
    +goog.labs.i18n.ListFormatSymbols_sk_SK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} a {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} a {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sl_SI.
    + */
    +goog.labs.i18n.ListFormatSymbols_sl_SI = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} in {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} in {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale smn.
    + */
    +goog.labs.i18n.ListFormatSymbols_smn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale smn_FI.
    + */
    +goog.labs.i18n.ListFormatSymbols_smn_FI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sn.
    + */
    +goog.labs.i18n.ListFormatSymbols_sn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sn_ZW.
    + */
    +goog.labs.i18n.ListFormatSymbols_sn_ZW = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale so.
    + */
    +goog.labs.i18n.ListFormatSymbols_so = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale so_DJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_so_DJ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale so_ET.
    + */
    +goog.labs.i18n.ListFormatSymbols_so_ET = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale so_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_so_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale so_SO.
    + */
    +goog.labs.i18n.ListFormatSymbols_so_SO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sq_AL.
    + */
    +goog.labs.i18n.ListFormatSymbols_sq_AL = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dhe {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dhe {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sq_MK.
    + */
    +goog.labs.i18n.ListFormatSymbols_sq_MK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dhe {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dhe {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sq_XK.
    + */
    +goog.labs.i18n.ListFormatSymbols_sq_XK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} dhe {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} dhe {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Cyrl = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Cyrl_BA.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Cyrl_BA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Cyrl_ME.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Cyrl_ME = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Cyrl_RS.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Cyrl_RS = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Cyrl_XK.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Cyrl_XK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} и {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} и {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Latn = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Latn_BA.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Latn_BA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Latn_ME.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Latn_ME = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Latn_RS.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Latn_RS = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sr_Latn_XK.
    + */
    +goog.labs.i18n.ListFormatSymbols_sr_Latn_XK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} i {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} i {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sv_AX.
    + */
    +goog.labs.i18n.ListFormatSymbols_sv_AX = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} och {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} och {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sv_FI.
    + */
    +goog.labs.i18n.ListFormatSymbols_sv_FI = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} och {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} och {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sv_SE.
    + */
    +goog.labs.i18n.ListFormatSymbols_sv_SE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} och {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} och {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sw_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_sw_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} na {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, na {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sw_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_sw_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} na {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, na {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale sw_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_sw_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} na {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, na {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale swc.
    + */
    +goog.labs.i18n.ListFormatSymbols_swc = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale swc_CD.
    + */
    +goog.labs.i18n.ListFormatSymbols_swc_CD = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ta_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ta_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} மற்றும் {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} மற்றும் {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ta_LK.
    + */
    +goog.labs.i18n.ListFormatSymbols_ta_LK = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} மற்றும் {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} மற்றும் {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ta_MY.
    + */
    +goog.labs.i18n.ListFormatSymbols_ta_MY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} மற்றும் {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} மற்றும் {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ta_SG.
    + */
    +goog.labs.i18n.ListFormatSymbols_ta_SG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} மற்றும் {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} மற்றும் {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale te_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_te_IN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} మరియు {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} మరియు {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale teo.
    + */
    +goog.labs.i18n.ListFormatSymbols_teo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale teo_KE.
    + */
    +goog.labs.i18n.ListFormatSymbols_teo_KE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale teo_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_teo_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale th_TH.
    + */
    +goog.labs.i18n.ListFormatSymbols_th_TH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}และ{1}',
    +  LIST_START: '{0} {1}',
    +  LIST_MIDDLE: '{0} {1}',
    +  LIST_END: '{0} และ{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ti.
    + */
    +goog.labs.i18n.ListFormatSymbols_ti = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ti_ER.
    + */
    +goog.labs.i18n.ListFormatSymbols_ti_ER = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ti_ET.
    + */
    +goog.labs.i18n.ListFormatSymbols_ti_ET = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale to.
    + */
    +goog.labs.i18n.ListFormatSymbols_to = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} mo {1}',
    +  LIST_START: '{0} mo {1}',
    +  LIST_MIDDLE: '{0} mo {1}',
    +  LIST_END: '{0} mo {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale to_TO.
    + */
    +goog.labs.i18n.ListFormatSymbols_to_TO = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} mo {1}',
    +  LIST_START: '{0} mo {1}',
    +  LIST_MIDDLE: '{0} mo {1}',
    +  LIST_END: '{0} mo {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tr_CY.
    + */
    +goog.labs.i18n.ListFormatSymbols_tr_CY = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ve {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ve {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tr_TR.
    + */
    +goog.labs.i18n.ListFormatSymbols_tr_TR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} ve {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} ve {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale twq.
    + */
    +goog.labs.i18n.ListFormatSymbols_twq = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale twq_NE.
    + */
    +goog.labs.i18n.ListFormatSymbols_twq_NE = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tzm.
    + */
    +goog.labs.i18n.ListFormatSymbols_tzm = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tzm_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_tzm_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale tzm_Latn_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_tzm_Latn_MA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ug.
    + */
    +goog.labs.i18n.ListFormatSymbols_ug = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ug_Arab.
    + */
    +goog.labs.i18n.ListFormatSymbols_ug_Arab = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ug_Arab_CN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ug_Arab_CN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} and {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, and {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uk_UA.
    + */
    +goog.labs.i18n.ListFormatSymbols_uk_UA = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} і {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} і {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ur_IN.
    + */
    +goog.labs.i18n.ListFormatSymbols_ur_IN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} اور {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، اور {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale ur_PK.
    + */
    +goog.labs.i18n.ListFormatSymbols_ur_PK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0} اور {1}',
    +  LIST_START: '{0}، {1}',
    +  LIST_MIDDLE: '{0}، {1}',
    +  LIST_END: '{0}، اور {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Arab.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Arab = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Arab_AF.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Arab_AF = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Cyrl.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Cyrl = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Cyrl_UZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Cyrl_UZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} va {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} va {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale uz_Latn_UZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_uz_Latn_UZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} va {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} va {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vai.
    + */
    +goog.labs.i18n.ListFormatSymbols_vai = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vai_Latn.
    + */
    +goog.labs.i18n.ListFormatSymbols_vai_Latn = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vai_Latn_LR.
    + */
    +goog.labs.i18n.ListFormatSymbols_vai_Latn_LR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vai_Vaii.
    + */
    +goog.labs.i18n.ListFormatSymbols_vai_Vaii = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vai_Vaii_LR.
    + */
    +goog.labs.i18n.ListFormatSymbols_vai_Vaii_LR = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vi_VN.
    + */
    +goog.labs.i18n.ListFormatSymbols_vi_VN = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} và {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} và {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vun.
    + */
    +goog.labs.i18n.ListFormatSymbols_vun = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale vun_TZ.
    + */
    +goog.labs.i18n.ListFormatSymbols_vun_TZ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale wae.
    + */
    +goog.labs.i18n.ListFormatSymbols_wae = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale wae_CH.
    + */
    +goog.labs.i18n.ListFormatSymbols_wae_CH = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0} und {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0} und {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale xog.
    + */
    +goog.labs.i18n.ListFormatSymbols_xog = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale xog_UG.
    + */
    +goog.labs.i18n.ListFormatSymbols_xog_UG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yav.
    + */
    +goog.labs.i18n.ListFormatSymbols_yav = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yav_CM.
    + */
    +goog.labs.i18n.ListFormatSymbols_yav_CM = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yi.
    + */
    +goog.labs.i18n.ListFormatSymbols_yi = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yi_001.
    + */
    +goog.labs.i18n.ListFormatSymbols_yi_001 = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yo.
    + */
    +goog.labs.i18n.ListFormatSymbols_yo = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yo_BJ.
    + */
    +goog.labs.i18n.ListFormatSymbols_yo_BJ = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale yo_NG.
    + */
    +goog.labs.i18n.ListFormatSymbols_yo_NG = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zgh.
    + */
    +goog.labs.i18n.ListFormatSymbols_zgh = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zgh_MA.
    + */
    +goog.labs.i18n.ListFormatSymbols_zgh_MA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: '{0}, {1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, {1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hans.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hans = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hans_CN.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hans_CN = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hans_HK.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hans_HK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hans_MO.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hans_MO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hans_SG.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hans_SG = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hant.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hant = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hant_HK.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hant_HK = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hant_MO.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hant_MO = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zh_Hant_TW.
    + */
    +goog.labs.i18n.ListFormatSymbols_zh_Hant_TW = {
    +  GENDER_STYLE: 2,
    +  LIST_TWO: '{0}和{1}',
    +  LIST_START: '{0}、{1}',
    +  LIST_MIDDLE: '{0}、{1}',
    +  LIST_END: '{0}和{1}'
    +};
    +
    +
    +/**
    + * List formatting symbols for locale zu_ZA.
    + */
    +goog.labs.i18n.ListFormatSymbols_zu_ZA = {
    +  GENDER_STYLE: 0,
    +  LIST_TWO: 'I-{0} ne-{1}',
    +  LIST_START: '{0}, {1}',
    +  LIST_MIDDLE: '{0}, {1}',
    +  LIST_END: '{0}, ne-{1}'
    +};
    +
    +
    +/**
    + * Selecting symbols by locale.
    + */
    +if (goog.LOCALE == 'af_NA' || goog.LOCALE == 'af-NA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_af_NA;
    +}
    +
    +if (goog.LOCALE == 'af_ZA' || goog.LOCALE == 'af-ZA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_af_ZA;
    +}
    +
    +if (goog.LOCALE == 'agq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_agq;
    +}
    +
    +if (goog.LOCALE == 'agq_CM' || goog.LOCALE == 'agq-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_agq_CM;
    +}
    +
    +if (goog.LOCALE == 'ak') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ak;
    +}
    +
    +if (goog.LOCALE == 'ak_GH' || goog.LOCALE == 'ak-GH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ak_GH;
    +}
    +
    +if (goog.LOCALE == 'am_ET' || goog.LOCALE == 'am-ET') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_am_ET;
    +}
    +
    +if (goog.LOCALE == 'ar_001' || goog.LOCALE == 'ar-001') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_001;
    +}
    +
    +if (goog.LOCALE == 'ar_AE' || goog.LOCALE == 'ar-AE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_AE;
    +}
    +
    +if (goog.LOCALE == 'ar_BH' || goog.LOCALE == 'ar-BH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_BH;
    +}
    +
    +if (goog.LOCALE == 'ar_DJ' || goog.LOCALE == 'ar-DJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_DJ;
    +}
    +
    +if (goog.LOCALE == 'ar_DZ' || goog.LOCALE == 'ar-DZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_DZ;
    +}
    +
    +if (goog.LOCALE == 'ar_EG' || goog.LOCALE == 'ar-EG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_EG;
    +}
    +
    +if (goog.LOCALE == 'ar_EH' || goog.LOCALE == 'ar-EH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_EH;
    +}
    +
    +if (goog.LOCALE == 'ar_ER' || goog.LOCALE == 'ar-ER') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_ER;
    +}
    +
    +if (goog.LOCALE == 'ar_IL' || goog.LOCALE == 'ar-IL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_IL;
    +}
    +
    +if (goog.LOCALE == 'ar_IQ' || goog.LOCALE == 'ar-IQ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_IQ;
    +}
    +
    +if (goog.LOCALE == 'ar_JO' || goog.LOCALE == 'ar-JO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_JO;
    +}
    +
    +if (goog.LOCALE == 'ar_KM' || goog.LOCALE == 'ar-KM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_KM;
    +}
    +
    +if (goog.LOCALE == 'ar_KW' || goog.LOCALE == 'ar-KW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_KW;
    +}
    +
    +if (goog.LOCALE == 'ar_LB' || goog.LOCALE == 'ar-LB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_LB;
    +}
    +
    +if (goog.LOCALE == 'ar_LY' || goog.LOCALE == 'ar-LY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_LY;
    +}
    +
    +if (goog.LOCALE == 'ar_MA' || goog.LOCALE == 'ar-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_MA;
    +}
    +
    +if (goog.LOCALE == 'ar_MR' || goog.LOCALE == 'ar-MR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_MR;
    +}
    +
    +if (goog.LOCALE == 'ar_OM' || goog.LOCALE == 'ar-OM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_OM;
    +}
    +
    +if (goog.LOCALE == 'ar_PS' || goog.LOCALE == 'ar-PS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_PS;
    +}
    +
    +if (goog.LOCALE == 'ar_QA' || goog.LOCALE == 'ar-QA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_QA;
    +}
    +
    +if (goog.LOCALE == 'ar_SA' || goog.LOCALE == 'ar-SA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_SA;
    +}
    +
    +if (goog.LOCALE == 'ar_SD' || goog.LOCALE == 'ar-SD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_SD;
    +}
    +
    +if (goog.LOCALE == 'ar_SO' || goog.LOCALE == 'ar-SO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_SO;
    +}
    +
    +if (goog.LOCALE == 'ar_SS' || goog.LOCALE == 'ar-SS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_SS;
    +}
    +
    +if (goog.LOCALE == 'ar_SY' || goog.LOCALE == 'ar-SY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_SY;
    +}
    +
    +if (goog.LOCALE == 'ar_TD' || goog.LOCALE == 'ar-TD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_TD;
    +}
    +
    +if (goog.LOCALE == 'ar_TN' || goog.LOCALE == 'ar-TN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_TN;
    +}
    +
    +if (goog.LOCALE == 'ar_YE' || goog.LOCALE == 'ar-YE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ar_YE;
    +}
    +
    +if (goog.LOCALE == 'as') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_as;
    +}
    +
    +if (goog.LOCALE == 'as_IN' || goog.LOCALE == 'as-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_as_IN;
    +}
    +
    +if (goog.LOCALE == 'asa') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_asa;
    +}
    +
    +if (goog.LOCALE == 'asa_TZ' || goog.LOCALE == 'asa-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_asa_TZ;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl' || goog.LOCALE == 'az-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_az_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_az_Cyrl_AZ;
    +}
    +
    +if (goog.LOCALE == 'az_Latn' || goog.LOCALE == 'az-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_az_Latn;
    +}
    +
    +if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_az_Latn_AZ;
    +}
    +
    +if (goog.LOCALE == 'bas') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bas;
    +}
    +
    +if (goog.LOCALE == 'bas_CM' || goog.LOCALE == 'bas-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bas_CM;
    +}
    +
    +if (goog.LOCALE == 'be') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_be;
    +}
    +
    +if (goog.LOCALE == 'be_BY' || goog.LOCALE == 'be-BY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_be_BY;
    +}
    +
    +if (goog.LOCALE == 'bem') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bem;
    +}
    +
    +if (goog.LOCALE == 'bem_ZM' || goog.LOCALE == 'bem-ZM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bem_ZM;
    +}
    +
    +if (goog.LOCALE == 'bez') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bez;
    +}
    +
    +if (goog.LOCALE == 'bez_TZ' || goog.LOCALE == 'bez-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bez_TZ;
    +}
    +
    +if (goog.LOCALE == 'bg_BG' || goog.LOCALE == 'bg-BG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bg_BG;
    +}
    +
    +if (goog.LOCALE == 'bm') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bm;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn' || goog.LOCALE == 'bm-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bm_Latn;
    +}
    +
    +if (goog.LOCALE == 'bm_Latn_ML' || goog.LOCALE == 'bm-Latn-ML') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bm_Latn_ML;
    +}
    +
    +if (goog.LOCALE == 'bn_BD' || goog.LOCALE == 'bn-BD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bn_BD;
    +}
    +
    +if (goog.LOCALE == 'bn_IN' || goog.LOCALE == 'bn-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bn_IN;
    +}
    +
    +if (goog.LOCALE == 'bo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bo;
    +}
    +
    +if (goog.LOCALE == 'bo_CN' || goog.LOCALE == 'bo-CN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bo_CN;
    +}
    +
    +if (goog.LOCALE == 'bo_IN' || goog.LOCALE == 'bo-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bo_IN;
    +}
    +
    +if (goog.LOCALE == 'br_FR' || goog.LOCALE == 'br-FR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_br_FR;
    +}
    +
    +if (goog.LOCALE == 'brx') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_brx;
    +}
    +
    +if (goog.LOCALE == 'brx_IN' || goog.LOCALE == 'brx-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_brx_IN;
    +}
    +
    +if (goog.LOCALE == 'bs') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bs;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl' || goog.LOCALE == 'bs-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bs_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'bs_Cyrl_BA' || goog.LOCALE == 'bs-Cyrl-BA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bs_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn' || goog.LOCALE == 'bs-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bs_Latn;
    +}
    +
    +if (goog.LOCALE == 'bs_Latn_BA' || goog.LOCALE == 'bs-Latn-BA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_bs_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'ca_AD' || goog.LOCALE == 'ca-AD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ca_AD;
    +}
    +
    +if (goog.LOCALE == 'ca_ES' || goog.LOCALE == 'ca-ES') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ca_ES;
    +}
    +
    +if (goog.LOCALE == 'ca_FR' || goog.LOCALE == 'ca-FR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ca_FR;
    +}
    +
    +if (goog.LOCALE == 'ca_IT' || goog.LOCALE == 'ca-IT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ca_IT;
    +}
    +
    +if (goog.LOCALE == 'cgg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cgg;
    +}
    +
    +if (goog.LOCALE == 'cgg_UG' || goog.LOCALE == 'cgg-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cgg_UG;
    +}
    +
    +if (goog.LOCALE == 'chr_US' || goog.LOCALE == 'chr-US') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_chr_US;
    +}
    +
    +if (goog.LOCALE == 'cs_CZ' || goog.LOCALE == 'cs-CZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cs_CZ;
    +}
    +
    +if (goog.LOCALE == 'cy_GB' || goog.LOCALE == 'cy-GB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_cy_GB;
    +}
    +
    +if (goog.LOCALE == 'da_DK' || goog.LOCALE == 'da-DK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_da_DK;
    +}
    +
    +if (goog.LOCALE == 'da_GL' || goog.LOCALE == 'da-GL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_da_GL;
    +}
    +
    +if (goog.LOCALE == 'dav') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dav;
    +}
    +
    +if (goog.LOCALE == 'dav_KE' || goog.LOCALE == 'dav-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dav_KE;
    +}
    +
    +if (goog.LOCALE == 'de_BE' || goog.LOCALE == 'de-BE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_BE;
    +}
    +
    +if (goog.LOCALE == 'de_DE' || goog.LOCALE == 'de-DE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_DE;
    +}
    +
    +if (goog.LOCALE == 'de_LI' || goog.LOCALE == 'de-LI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_LI;
    +}
    +
    +if (goog.LOCALE == 'de_LU' || goog.LOCALE == 'de-LU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_de_LU;
    +}
    +
    +if (goog.LOCALE == 'dje') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dje;
    +}
    +
    +if (goog.LOCALE == 'dje_NE' || goog.LOCALE == 'dje-NE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dje_NE;
    +}
    +
    +if (goog.LOCALE == 'dsb') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dsb;
    +}
    +
    +if (goog.LOCALE == 'dsb_DE' || goog.LOCALE == 'dsb-DE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dsb_DE;
    +}
    +
    +if (goog.LOCALE == 'dua') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dua;
    +}
    +
    +if (goog.LOCALE == 'dua_CM' || goog.LOCALE == 'dua-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dua_CM;
    +}
    +
    +if (goog.LOCALE == 'dyo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dyo;
    +}
    +
    +if (goog.LOCALE == 'dyo_SN' || goog.LOCALE == 'dyo-SN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dyo_SN;
    +}
    +
    +if (goog.LOCALE == 'dz') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dz;
    +}
    +
    +if (goog.LOCALE == 'dz_BT' || goog.LOCALE == 'dz-BT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_dz_BT;
    +}
    +
    +if (goog.LOCALE == 'ebu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ebu;
    +}
    +
    +if (goog.LOCALE == 'ebu_KE' || goog.LOCALE == 'ebu-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ebu_KE;
    +}
    +
    +if (goog.LOCALE == 'ee') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ee;
    +}
    +
    +if (goog.LOCALE == 'ee_GH' || goog.LOCALE == 'ee-GH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ee_GH;
    +}
    +
    +if (goog.LOCALE == 'ee_TG' || goog.LOCALE == 'ee-TG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ee_TG;
    +}
    +
    +if (goog.LOCALE == 'el_CY' || goog.LOCALE == 'el-CY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_el_CY;
    +}
    +
    +if (goog.LOCALE == 'el_GR' || goog.LOCALE == 'el-GR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_el_GR;
    +}
    +
    +if (goog.LOCALE == 'en_001' || goog.LOCALE == 'en-001') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_001;
    +}
    +
    +if (goog.LOCALE == 'en_150' || goog.LOCALE == 'en-150') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_150;
    +}
    +
    +if (goog.LOCALE == 'en_AG' || goog.LOCALE == 'en-AG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_AG;
    +}
    +
    +if (goog.LOCALE == 'en_AI' || goog.LOCALE == 'en-AI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_AI;
    +}
    +
    +if (goog.LOCALE == 'en_AS' || goog.LOCALE == 'en-AS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_AS;
    +}
    +
    +if (goog.LOCALE == 'en_BB' || goog.LOCALE == 'en-BB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BB;
    +}
    +
    +if (goog.LOCALE == 'en_BE' || goog.LOCALE == 'en-BE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BE;
    +}
    +
    +if (goog.LOCALE == 'en_BM' || goog.LOCALE == 'en-BM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BM;
    +}
    +
    +if (goog.LOCALE == 'en_BS' || goog.LOCALE == 'en-BS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BS;
    +}
    +
    +if (goog.LOCALE == 'en_BW' || goog.LOCALE == 'en-BW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BW;
    +}
    +
    +if (goog.LOCALE == 'en_BZ' || goog.LOCALE == 'en-BZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_BZ;
    +}
    +
    +if (goog.LOCALE == 'en_CA' || goog.LOCALE == 'en-CA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_CA;
    +}
    +
    +if (goog.LOCALE == 'en_CC' || goog.LOCALE == 'en-CC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_CC;
    +}
    +
    +if (goog.LOCALE == 'en_CK' || goog.LOCALE == 'en-CK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_CK;
    +}
    +
    +if (goog.LOCALE == 'en_CM' || goog.LOCALE == 'en-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_CM;
    +}
    +
    +if (goog.LOCALE == 'en_CX' || goog.LOCALE == 'en-CX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_CX;
    +}
    +
    +if (goog.LOCALE == 'en_DG' || goog.LOCALE == 'en-DG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_DG;
    +}
    +
    +if (goog.LOCALE == 'en_DM' || goog.LOCALE == 'en-DM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_DM;
    +}
    +
    +if (goog.LOCALE == 'en_ER' || goog.LOCALE == 'en-ER') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_ER;
    +}
    +
    +if (goog.LOCALE == 'en_FJ' || goog.LOCALE == 'en-FJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_FJ;
    +}
    +
    +if (goog.LOCALE == 'en_FK' || goog.LOCALE == 'en-FK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_FK;
    +}
    +
    +if (goog.LOCALE == 'en_FM' || goog.LOCALE == 'en-FM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_FM;
    +}
    +
    +if (goog.LOCALE == 'en_GD' || goog.LOCALE == 'en-GD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GD;
    +}
    +
    +if (goog.LOCALE == 'en_GG' || goog.LOCALE == 'en-GG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GG;
    +}
    +
    +if (goog.LOCALE == 'en_GH' || goog.LOCALE == 'en-GH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GH;
    +}
    +
    +if (goog.LOCALE == 'en_GI' || goog.LOCALE == 'en-GI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GI;
    +}
    +
    +if (goog.LOCALE == 'en_GM' || goog.LOCALE == 'en-GM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GM;
    +}
    +
    +if (goog.LOCALE == 'en_GU' || goog.LOCALE == 'en-GU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GU;
    +}
    +
    +if (goog.LOCALE == 'en_GY' || goog.LOCALE == 'en-GY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_GY;
    +}
    +
    +if (goog.LOCALE == 'en_HK' || goog.LOCALE == 'en-HK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_HK;
    +}
    +
    +if (goog.LOCALE == 'en_IM' || goog.LOCALE == 'en-IM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_IM;
    +}
    +
    +if (goog.LOCALE == 'en_IO' || goog.LOCALE == 'en-IO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_IO;
    +}
    +
    +if (goog.LOCALE == 'en_JE' || goog.LOCALE == 'en-JE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_JE;
    +}
    +
    +if (goog.LOCALE == 'en_JM' || goog.LOCALE == 'en-JM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_JM;
    +}
    +
    +if (goog.LOCALE == 'en_KE' || goog.LOCALE == 'en-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_KE;
    +}
    +
    +if (goog.LOCALE == 'en_KI' || goog.LOCALE == 'en-KI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_KI;
    +}
    +
    +if (goog.LOCALE == 'en_KN' || goog.LOCALE == 'en-KN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_KN;
    +}
    +
    +if (goog.LOCALE == 'en_KY' || goog.LOCALE == 'en-KY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_KY;
    +}
    +
    +if (goog.LOCALE == 'en_LC' || goog.LOCALE == 'en-LC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_LC;
    +}
    +
    +if (goog.LOCALE == 'en_LR' || goog.LOCALE == 'en-LR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_LR;
    +}
    +
    +if (goog.LOCALE == 'en_LS' || goog.LOCALE == 'en-LS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_LS;
    +}
    +
    +if (goog.LOCALE == 'en_MG' || goog.LOCALE == 'en-MG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MG;
    +}
    +
    +if (goog.LOCALE == 'en_MH' || goog.LOCALE == 'en-MH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MH;
    +}
    +
    +if (goog.LOCALE == 'en_MO' || goog.LOCALE == 'en-MO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MO;
    +}
    +
    +if (goog.LOCALE == 'en_MP' || goog.LOCALE == 'en-MP') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MP;
    +}
    +
    +if (goog.LOCALE == 'en_MS' || goog.LOCALE == 'en-MS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MS;
    +}
    +
    +if (goog.LOCALE == 'en_MT' || goog.LOCALE == 'en-MT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MT;
    +}
    +
    +if (goog.LOCALE == 'en_MU' || goog.LOCALE == 'en-MU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MU;
    +}
    +
    +if (goog.LOCALE == 'en_MW' || goog.LOCALE == 'en-MW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MW;
    +}
    +
    +if (goog.LOCALE == 'en_MY' || goog.LOCALE == 'en-MY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_MY;
    +}
    +
    +if (goog.LOCALE == 'en_NA' || goog.LOCALE == 'en-NA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NA;
    +}
    +
    +if (goog.LOCALE == 'en_NF' || goog.LOCALE == 'en-NF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NF;
    +}
    +
    +if (goog.LOCALE == 'en_NG' || goog.LOCALE == 'en-NG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NG;
    +}
    +
    +if (goog.LOCALE == 'en_NR' || goog.LOCALE == 'en-NR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NR;
    +}
    +
    +if (goog.LOCALE == 'en_NU' || goog.LOCALE == 'en-NU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NU;
    +}
    +
    +if (goog.LOCALE == 'en_NZ' || goog.LOCALE == 'en-NZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_NZ;
    +}
    +
    +if (goog.LOCALE == 'en_PG' || goog.LOCALE == 'en-PG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PG;
    +}
    +
    +if (goog.LOCALE == 'en_PH' || goog.LOCALE == 'en-PH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PH;
    +}
    +
    +if (goog.LOCALE == 'en_PK' || goog.LOCALE == 'en-PK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PK;
    +}
    +
    +if (goog.LOCALE == 'en_PN' || goog.LOCALE == 'en-PN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PN;
    +}
    +
    +if (goog.LOCALE == 'en_PR' || goog.LOCALE == 'en-PR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PR;
    +}
    +
    +if (goog.LOCALE == 'en_PW' || goog.LOCALE == 'en-PW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_PW;
    +}
    +
    +if (goog.LOCALE == 'en_RW' || goog.LOCALE == 'en-RW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_RW;
    +}
    +
    +if (goog.LOCALE == 'en_SB' || goog.LOCALE == 'en-SB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SB;
    +}
    +
    +if (goog.LOCALE == 'en_SC' || goog.LOCALE == 'en-SC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SC;
    +}
    +
    +if (goog.LOCALE == 'en_SD' || goog.LOCALE == 'en-SD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SD;
    +}
    +
    +if (goog.LOCALE == 'en_SH' || goog.LOCALE == 'en-SH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SH;
    +}
    +
    +if (goog.LOCALE == 'en_SL' || goog.LOCALE == 'en-SL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SL;
    +}
    +
    +if (goog.LOCALE == 'en_SS' || goog.LOCALE == 'en-SS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SS;
    +}
    +
    +if (goog.LOCALE == 'en_SX' || goog.LOCALE == 'en-SX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SX;
    +}
    +
    +if (goog.LOCALE == 'en_SZ' || goog.LOCALE == 'en-SZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_SZ;
    +}
    +
    +if (goog.LOCALE == 'en_TC' || goog.LOCALE == 'en-TC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TC;
    +}
    +
    +if (goog.LOCALE == 'en_TK' || goog.LOCALE == 'en-TK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TK;
    +}
    +
    +if (goog.LOCALE == 'en_TO' || goog.LOCALE == 'en-TO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TO;
    +}
    +
    +if (goog.LOCALE == 'en_TT' || goog.LOCALE == 'en-TT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TT;
    +}
    +
    +if (goog.LOCALE == 'en_TV' || goog.LOCALE == 'en-TV') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TV;
    +}
    +
    +if (goog.LOCALE == 'en_TZ' || goog.LOCALE == 'en-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_TZ;
    +}
    +
    +if (goog.LOCALE == 'en_UG' || goog.LOCALE == 'en-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_UG;
    +}
    +
    +if (goog.LOCALE == 'en_UM' || goog.LOCALE == 'en-UM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_UM;
    +}
    +
    +if (goog.LOCALE == 'en_US_POSIX' || goog.LOCALE == 'en-US-POSIX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_US_POSIX;
    +}
    +
    +if (goog.LOCALE == 'en_VC' || goog.LOCALE == 'en-VC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_VC;
    +}
    +
    +if (goog.LOCALE == 'en_VG' || goog.LOCALE == 'en-VG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_VG;
    +}
    +
    +if (goog.LOCALE == 'en_VI' || goog.LOCALE == 'en-VI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_VI;
    +}
    +
    +if (goog.LOCALE == 'en_VU' || goog.LOCALE == 'en-VU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_VU;
    +}
    +
    +if (goog.LOCALE == 'en_WS' || goog.LOCALE == 'en-WS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_WS;
    +}
    +
    +if (goog.LOCALE == 'en_ZM' || goog.LOCALE == 'en-ZM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_ZM;
    +}
    +
    +if (goog.LOCALE == 'en_ZW' || goog.LOCALE == 'en-ZW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_en_ZW;
    +}
    +
    +if (goog.LOCALE == 'eo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_eo;
    +}
    +
    +if (goog.LOCALE == 'es_AR' || goog.LOCALE == 'es-AR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_AR;
    +}
    +
    +if (goog.LOCALE == 'es_BO' || goog.LOCALE == 'es-BO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_BO;
    +}
    +
    +if (goog.LOCALE == 'es_CL' || goog.LOCALE == 'es-CL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_CL;
    +}
    +
    +if (goog.LOCALE == 'es_CO' || goog.LOCALE == 'es-CO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_CO;
    +}
    +
    +if (goog.LOCALE == 'es_CR' || goog.LOCALE == 'es-CR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_CR;
    +}
    +
    +if (goog.LOCALE == 'es_CU' || goog.LOCALE == 'es-CU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_CU;
    +}
    +
    +if (goog.LOCALE == 'es_DO' || goog.LOCALE == 'es-DO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_DO;
    +}
    +
    +if (goog.LOCALE == 'es_EA' || goog.LOCALE == 'es-EA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_EA;
    +}
    +
    +if (goog.LOCALE == 'es_EC' || goog.LOCALE == 'es-EC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_EC;
    +}
    +
    +if (goog.LOCALE == 'es_GQ' || goog.LOCALE == 'es-GQ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_GQ;
    +}
    +
    +if (goog.LOCALE == 'es_GT' || goog.LOCALE == 'es-GT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_GT;
    +}
    +
    +if (goog.LOCALE == 'es_HN' || goog.LOCALE == 'es-HN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_HN;
    +}
    +
    +if (goog.LOCALE == 'es_IC' || goog.LOCALE == 'es-IC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_IC;
    +}
    +
    +if (goog.LOCALE == 'es_MX' || goog.LOCALE == 'es-MX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_MX;
    +}
    +
    +if (goog.LOCALE == 'es_NI' || goog.LOCALE == 'es-NI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_NI;
    +}
    +
    +if (goog.LOCALE == 'es_PA' || goog.LOCALE == 'es-PA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_PA;
    +}
    +
    +if (goog.LOCALE == 'es_PE' || goog.LOCALE == 'es-PE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_PE;
    +}
    +
    +if (goog.LOCALE == 'es_PH' || goog.LOCALE == 'es-PH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_PH;
    +}
    +
    +if (goog.LOCALE == 'es_PR' || goog.LOCALE == 'es-PR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_PR;
    +}
    +
    +if (goog.LOCALE == 'es_PY' || goog.LOCALE == 'es-PY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_PY;
    +}
    +
    +if (goog.LOCALE == 'es_SV' || goog.LOCALE == 'es-SV') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_SV;
    +}
    +
    +if (goog.LOCALE == 'es_US' || goog.LOCALE == 'es-US') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_US;
    +}
    +
    +if (goog.LOCALE == 'es_UY' || goog.LOCALE == 'es-UY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_UY;
    +}
    +
    +if (goog.LOCALE == 'es_VE' || goog.LOCALE == 'es-VE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_es_VE;
    +}
    +
    +if (goog.LOCALE == 'et_EE' || goog.LOCALE == 'et-EE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_et_EE;
    +}
    +
    +if (goog.LOCALE == 'eu_ES' || goog.LOCALE == 'eu-ES') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_eu_ES;
    +}
    +
    +if (goog.LOCALE == 'ewo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ewo;
    +}
    +
    +if (goog.LOCALE == 'ewo_CM' || goog.LOCALE == 'ewo-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ewo_CM;
    +}
    +
    +if (goog.LOCALE == 'fa_AF' || goog.LOCALE == 'fa-AF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fa_AF;
    +}
    +
    +if (goog.LOCALE == 'fa_IR' || goog.LOCALE == 'fa-IR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fa_IR;
    +}
    +
    +if (goog.LOCALE == 'ff') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ff;
    +}
    +
    +if (goog.LOCALE == 'ff_CM' || goog.LOCALE == 'ff-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ff_CM;
    +}
    +
    +if (goog.LOCALE == 'ff_GN' || goog.LOCALE == 'ff-GN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ff_GN;
    +}
    +
    +if (goog.LOCALE == 'ff_MR' || goog.LOCALE == 'ff-MR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ff_MR;
    +}
    +
    +if (goog.LOCALE == 'ff_SN' || goog.LOCALE == 'ff-SN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ff_SN;
    +}
    +
    +if (goog.LOCALE == 'fi_FI' || goog.LOCALE == 'fi-FI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fi_FI;
    +}
    +
    +if (goog.LOCALE == 'fil_PH' || goog.LOCALE == 'fil-PH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fil_PH;
    +}
    +
    +if (goog.LOCALE == 'fo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fo;
    +}
    +
    +if (goog.LOCALE == 'fo_FO' || goog.LOCALE == 'fo-FO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fo_FO;
    +}
    +
    +if (goog.LOCALE == 'fr_BE' || goog.LOCALE == 'fr-BE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_BE;
    +}
    +
    +if (goog.LOCALE == 'fr_BF' || goog.LOCALE == 'fr-BF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_BF;
    +}
    +
    +if (goog.LOCALE == 'fr_BI' || goog.LOCALE == 'fr-BI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_BI;
    +}
    +
    +if (goog.LOCALE == 'fr_BJ' || goog.LOCALE == 'fr-BJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_BJ;
    +}
    +
    +if (goog.LOCALE == 'fr_BL' || goog.LOCALE == 'fr-BL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_BL;
    +}
    +
    +if (goog.LOCALE == 'fr_CD' || goog.LOCALE == 'fr-CD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CD;
    +}
    +
    +if (goog.LOCALE == 'fr_CF' || goog.LOCALE == 'fr-CF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CF;
    +}
    +
    +if (goog.LOCALE == 'fr_CG' || goog.LOCALE == 'fr-CG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CG;
    +}
    +
    +if (goog.LOCALE == 'fr_CH' || goog.LOCALE == 'fr-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CH;
    +}
    +
    +if (goog.LOCALE == 'fr_CI' || goog.LOCALE == 'fr-CI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CI;
    +}
    +
    +if (goog.LOCALE == 'fr_CM' || goog.LOCALE == 'fr-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_CM;
    +}
    +
    +if (goog.LOCALE == 'fr_DJ' || goog.LOCALE == 'fr-DJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_DJ;
    +}
    +
    +if (goog.LOCALE == 'fr_DZ' || goog.LOCALE == 'fr-DZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_DZ;
    +}
    +
    +if (goog.LOCALE == 'fr_FR' || goog.LOCALE == 'fr-FR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_FR;
    +}
    +
    +if (goog.LOCALE == 'fr_GA' || goog.LOCALE == 'fr-GA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_GA;
    +}
    +
    +if (goog.LOCALE == 'fr_GF' || goog.LOCALE == 'fr-GF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_GF;
    +}
    +
    +if (goog.LOCALE == 'fr_GN' || goog.LOCALE == 'fr-GN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_GN;
    +}
    +
    +if (goog.LOCALE == 'fr_GP' || goog.LOCALE == 'fr-GP') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_GP;
    +}
    +
    +if (goog.LOCALE == 'fr_GQ' || goog.LOCALE == 'fr-GQ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_GQ;
    +}
    +
    +if (goog.LOCALE == 'fr_HT' || goog.LOCALE == 'fr-HT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_HT;
    +}
    +
    +if (goog.LOCALE == 'fr_KM' || goog.LOCALE == 'fr-KM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_KM;
    +}
    +
    +if (goog.LOCALE == 'fr_LU' || goog.LOCALE == 'fr-LU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_LU;
    +}
    +
    +if (goog.LOCALE == 'fr_MA' || goog.LOCALE == 'fr-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MA;
    +}
    +
    +if (goog.LOCALE == 'fr_MC' || goog.LOCALE == 'fr-MC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MC;
    +}
    +
    +if (goog.LOCALE == 'fr_MF' || goog.LOCALE == 'fr-MF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MF;
    +}
    +
    +if (goog.LOCALE == 'fr_MG' || goog.LOCALE == 'fr-MG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MG;
    +}
    +
    +if (goog.LOCALE == 'fr_ML' || goog.LOCALE == 'fr-ML') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_ML;
    +}
    +
    +if (goog.LOCALE == 'fr_MQ' || goog.LOCALE == 'fr-MQ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MQ;
    +}
    +
    +if (goog.LOCALE == 'fr_MR' || goog.LOCALE == 'fr-MR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MR;
    +}
    +
    +if (goog.LOCALE == 'fr_MU' || goog.LOCALE == 'fr-MU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_MU;
    +}
    +
    +if (goog.LOCALE == 'fr_NC' || goog.LOCALE == 'fr-NC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_NC;
    +}
    +
    +if (goog.LOCALE == 'fr_NE' || goog.LOCALE == 'fr-NE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_NE;
    +}
    +
    +if (goog.LOCALE == 'fr_PF' || goog.LOCALE == 'fr-PF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_PF;
    +}
    +
    +if (goog.LOCALE == 'fr_PM' || goog.LOCALE == 'fr-PM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_PM;
    +}
    +
    +if (goog.LOCALE == 'fr_RE' || goog.LOCALE == 'fr-RE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_RE;
    +}
    +
    +if (goog.LOCALE == 'fr_RW' || goog.LOCALE == 'fr-RW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_RW;
    +}
    +
    +if (goog.LOCALE == 'fr_SC' || goog.LOCALE == 'fr-SC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_SC;
    +}
    +
    +if (goog.LOCALE == 'fr_SN' || goog.LOCALE == 'fr-SN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_SN;
    +}
    +
    +if (goog.LOCALE == 'fr_SY' || goog.LOCALE == 'fr-SY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_SY;
    +}
    +
    +if (goog.LOCALE == 'fr_TD' || goog.LOCALE == 'fr-TD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_TD;
    +}
    +
    +if (goog.LOCALE == 'fr_TG' || goog.LOCALE == 'fr-TG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_TG;
    +}
    +
    +if (goog.LOCALE == 'fr_TN' || goog.LOCALE == 'fr-TN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_TN;
    +}
    +
    +if (goog.LOCALE == 'fr_VU' || goog.LOCALE == 'fr-VU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_VU;
    +}
    +
    +if (goog.LOCALE == 'fr_WF' || goog.LOCALE == 'fr-WF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_WF;
    +}
    +
    +if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fr_YT;
    +}
    +
    +if (goog.LOCALE == 'fur') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fur;
    +}
    +
    +if (goog.LOCALE == 'fur_IT' || goog.LOCALE == 'fur-IT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fur_IT;
    +}
    +
    +if (goog.LOCALE == 'fy') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fy;
    +}
    +
    +if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_fy_NL;
    +}
    +
    +if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ga_IE;
    +}
    +
    +if (goog.LOCALE == 'gd') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gd;
    +}
    +
    +if (goog.LOCALE == 'gd_GB' || goog.LOCALE == 'gd-GB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gd_GB;
    +}
    +
    +if (goog.LOCALE == 'gl_ES' || goog.LOCALE == 'gl-ES') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gl_ES;
    +}
    +
    +if (goog.LOCALE == 'gsw_CH' || goog.LOCALE == 'gsw-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gsw_CH;
    +}
    +
    +if (goog.LOCALE == 'gsw_FR' || goog.LOCALE == 'gsw-FR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gsw_FR;
    +}
    +
    +if (goog.LOCALE == 'gsw_LI' || goog.LOCALE == 'gsw-LI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gsw_LI;
    +}
    +
    +if (goog.LOCALE == 'gu_IN' || goog.LOCALE == 'gu-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gu_IN;
    +}
    +
    +if (goog.LOCALE == 'guz') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_guz;
    +}
    +
    +if (goog.LOCALE == 'guz_KE' || goog.LOCALE == 'guz-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_guz_KE;
    +}
    +
    +if (goog.LOCALE == 'gv') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gv;
    +}
    +
    +if (goog.LOCALE == 'gv_IM' || goog.LOCALE == 'gv-IM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_gv_IM;
    +}
    +
    +if (goog.LOCALE == 'ha') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ha;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn' || goog.LOCALE == 'ha-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ha_Latn;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_GH' || goog.LOCALE == 'ha-Latn-GH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ha_Latn_GH;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NE' || goog.LOCALE == 'ha-Latn-NE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ha_Latn_NE;
    +}
    +
    +if (goog.LOCALE == 'ha_Latn_NG' || goog.LOCALE == 'ha-Latn-NG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ha_Latn_NG;
    +}
    +
    +if (goog.LOCALE == 'haw_US' || goog.LOCALE == 'haw-US') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_haw_US;
    +}
    +
    +if (goog.LOCALE == 'he_IL' || goog.LOCALE == 'he-IL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_he_IL;
    +}
    +
    +if (goog.LOCALE == 'hi_IN' || goog.LOCALE == 'hi-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hi_IN;
    +}
    +
    +if (goog.LOCALE == 'hr_BA' || goog.LOCALE == 'hr-BA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hr_BA;
    +}
    +
    +if (goog.LOCALE == 'hr_HR' || goog.LOCALE == 'hr-HR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hr_HR;
    +}
    +
    +if (goog.LOCALE == 'hsb') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hsb;
    +}
    +
    +if (goog.LOCALE == 'hsb_DE' || goog.LOCALE == 'hsb-DE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hsb_DE;
    +}
    +
    +if (goog.LOCALE == 'hu_HU' || goog.LOCALE == 'hu-HU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hu_HU;
    +}
    +
    +if (goog.LOCALE == 'hy_AM' || goog.LOCALE == 'hy-AM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_hy_AM;
    +}
    +
    +if (goog.LOCALE == 'id_ID' || goog.LOCALE == 'id-ID') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_id_ID;
    +}
    +
    +if (goog.LOCALE == 'ig') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ig;
    +}
    +
    +if (goog.LOCALE == 'ig_NG' || goog.LOCALE == 'ig-NG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ig_NG;
    +}
    +
    +if (goog.LOCALE == 'ii') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ii;
    +}
    +
    +if (goog.LOCALE == 'ii_CN' || goog.LOCALE == 'ii-CN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ii_CN;
    +}
    +
    +if (goog.LOCALE == 'is_IS' || goog.LOCALE == 'is-IS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_is_IS;
    +}
    +
    +if (goog.LOCALE == 'it_CH' || goog.LOCALE == 'it-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_it_CH;
    +}
    +
    +if (goog.LOCALE == 'it_IT' || goog.LOCALE == 'it-IT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_it_IT;
    +}
    +
    +if (goog.LOCALE == 'it_SM' || goog.LOCALE == 'it-SM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_it_SM;
    +}
    +
    +if (goog.LOCALE == 'ja_JP' || goog.LOCALE == 'ja-JP') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ja_JP;
    +}
    +
    +if (goog.LOCALE == 'jgo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_jgo;
    +}
    +
    +if (goog.LOCALE == 'jgo_CM' || goog.LOCALE == 'jgo-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_jgo_CM;
    +}
    +
    +if (goog.LOCALE == 'jmc') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_jmc;
    +}
    +
    +if (goog.LOCALE == 'jmc_TZ' || goog.LOCALE == 'jmc-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_jmc_TZ;
    +}
    +
    +if (goog.LOCALE == 'ka_GE' || goog.LOCALE == 'ka-GE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ka_GE;
    +}
    +
    +if (goog.LOCALE == 'kab') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kab;
    +}
    +
    +if (goog.LOCALE == 'kab_DZ' || goog.LOCALE == 'kab-DZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kab_DZ;
    +}
    +
    +if (goog.LOCALE == 'kam') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kam;
    +}
    +
    +if (goog.LOCALE == 'kam_KE' || goog.LOCALE == 'kam-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kam_KE;
    +}
    +
    +if (goog.LOCALE == 'kde') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kde;
    +}
    +
    +if (goog.LOCALE == 'kde_TZ' || goog.LOCALE == 'kde-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kde_TZ;
    +}
    +
    +if (goog.LOCALE == 'kea') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kea;
    +}
    +
    +if (goog.LOCALE == 'kea_CV' || goog.LOCALE == 'kea-CV') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kea_CV;
    +}
    +
    +if (goog.LOCALE == 'khq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_khq;
    +}
    +
    +if (goog.LOCALE == 'khq_ML' || goog.LOCALE == 'khq-ML') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_khq_ML;
    +}
    +
    +if (goog.LOCALE == 'ki') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ki;
    +}
    +
    +if (goog.LOCALE == 'ki_KE' || goog.LOCALE == 'ki-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ki_KE;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl' || goog.LOCALE == 'kk-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kk_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'kk_Cyrl_KZ' || goog.LOCALE == 'kk-Cyrl-KZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kk_Cyrl_KZ;
    +}
    +
    +if (goog.LOCALE == 'kkj') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kkj;
    +}
    +
    +if (goog.LOCALE == 'kkj_CM' || goog.LOCALE == 'kkj-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kkj_CM;
    +}
    +
    +if (goog.LOCALE == 'kl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kl;
    +}
    +
    +if (goog.LOCALE == 'kl_GL' || goog.LOCALE == 'kl-GL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kl_GL;
    +}
    +
    +if (goog.LOCALE == 'kln') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kln;
    +}
    +
    +if (goog.LOCALE == 'kln_KE' || goog.LOCALE == 'kln-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kln_KE;
    +}
    +
    +if (goog.LOCALE == 'km_KH' || goog.LOCALE == 'km-KH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_km_KH;
    +}
    +
    +if (goog.LOCALE == 'kn_IN' || goog.LOCALE == 'kn-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kn_IN;
    +}
    +
    +if (goog.LOCALE == 'ko_KP' || goog.LOCALE == 'ko-KP') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ko_KP;
    +}
    +
    +if (goog.LOCALE == 'ko_KR' || goog.LOCALE == 'ko-KR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ko_KR;
    +}
    +
    +if (goog.LOCALE == 'kok') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kok;
    +}
    +
    +if (goog.LOCALE == 'kok_IN' || goog.LOCALE == 'kok-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kok_IN;
    +}
    +
    +if (goog.LOCALE == 'ks') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ks;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab' || goog.LOCALE == 'ks-Arab') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ks_Arab;
    +}
    +
    +if (goog.LOCALE == 'ks_Arab_IN' || goog.LOCALE == 'ks-Arab-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ks_Arab_IN;
    +}
    +
    +if (goog.LOCALE == 'ksb') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksb;
    +}
    +
    +if (goog.LOCALE == 'ksb_TZ' || goog.LOCALE == 'ksb-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksb_TZ;
    +}
    +
    +if (goog.LOCALE == 'ksf') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksf;
    +}
    +
    +if (goog.LOCALE == 'ksf_CM' || goog.LOCALE == 'ksf-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksf_CM;
    +}
    +
    +if (goog.LOCALE == 'ksh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksh;
    +}
    +
    +if (goog.LOCALE == 'ksh_DE' || goog.LOCALE == 'ksh-DE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ksh_DE;
    +}
    +
    +if (goog.LOCALE == 'kw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kw;
    +}
    +
    +if (goog.LOCALE == 'kw_GB' || goog.LOCALE == 'kw-GB') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_kw_GB;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl' || goog.LOCALE == 'ky-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ky_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'ky_Cyrl_KG' || goog.LOCALE == 'ky-Cyrl-KG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ky_Cyrl_KG;
    +}
    +
    +if (goog.LOCALE == 'lag') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lag;
    +}
    +
    +if (goog.LOCALE == 'lag_TZ' || goog.LOCALE == 'lag-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lag_TZ;
    +}
    +
    +if (goog.LOCALE == 'lb') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lb;
    +}
    +
    +if (goog.LOCALE == 'lb_LU' || goog.LOCALE == 'lb-LU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lb_LU;
    +}
    +
    +if (goog.LOCALE == 'lg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lg;
    +}
    +
    +if (goog.LOCALE == 'lg_UG' || goog.LOCALE == 'lg-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lg_UG;
    +}
    +
    +if (goog.LOCALE == 'lkt') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lkt;
    +}
    +
    +if (goog.LOCALE == 'lkt_US' || goog.LOCALE == 'lkt-US') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lkt_US;
    +}
    +
    +if (goog.LOCALE == 'ln_AO' || goog.LOCALE == 'ln-AO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ln_AO;
    +}
    +
    +if (goog.LOCALE == 'ln_CD' || goog.LOCALE == 'ln-CD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ln_CD;
    +}
    +
    +if (goog.LOCALE == 'ln_CF' || goog.LOCALE == 'ln-CF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ln_CF;
    +}
    +
    +if (goog.LOCALE == 'ln_CG' || goog.LOCALE == 'ln-CG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ln_CG;
    +}
    +
    +if (goog.LOCALE == 'lo_LA' || goog.LOCALE == 'lo-LA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lo_LA;
    +}
    +
    +if (goog.LOCALE == 'lt_LT' || goog.LOCALE == 'lt-LT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lt_LT;
    +}
    +
    +if (goog.LOCALE == 'lu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lu;
    +}
    +
    +if (goog.LOCALE == 'lu_CD' || goog.LOCALE == 'lu-CD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lu_CD;
    +}
    +
    +if (goog.LOCALE == 'luo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_luo;
    +}
    +
    +if (goog.LOCALE == 'luo_KE' || goog.LOCALE == 'luo-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_luo_KE;
    +}
    +
    +if (goog.LOCALE == 'luy') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_luy;
    +}
    +
    +if (goog.LOCALE == 'luy_KE' || goog.LOCALE == 'luy-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_luy_KE;
    +}
    +
    +if (goog.LOCALE == 'lv_LV' || goog.LOCALE == 'lv-LV') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_lv_LV;
    +}
    +
    +if (goog.LOCALE == 'mas') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mas;
    +}
    +
    +if (goog.LOCALE == 'mas_KE' || goog.LOCALE == 'mas-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mas_KE;
    +}
    +
    +if (goog.LOCALE == 'mas_TZ' || goog.LOCALE == 'mas-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mas_TZ;
    +}
    +
    +if (goog.LOCALE == 'mer') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mer;
    +}
    +
    +if (goog.LOCALE == 'mer_KE' || goog.LOCALE == 'mer-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mer_KE;
    +}
    +
    +if (goog.LOCALE == 'mfe') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mfe;
    +}
    +
    +if (goog.LOCALE == 'mfe_MU' || goog.LOCALE == 'mfe-MU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mfe_MU;
    +}
    +
    +if (goog.LOCALE == 'mg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mg;
    +}
    +
    +if (goog.LOCALE == 'mg_MG' || goog.LOCALE == 'mg-MG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mg_MG;
    +}
    +
    +if (goog.LOCALE == 'mgh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mgh;
    +}
    +
    +if (goog.LOCALE == 'mgh_MZ' || goog.LOCALE == 'mgh-MZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mgh_MZ;
    +}
    +
    +if (goog.LOCALE == 'mgo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mgo;
    +}
    +
    +if (goog.LOCALE == 'mgo_CM' || goog.LOCALE == 'mgo-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mgo_CM;
    +}
    +
    +if (goog.LOCALE == 'mk_MK' || goog.LOCALE == 'mk-MK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mk_MK;
    +}
    +
    +if (goog.LOCALE == 'ml_IN' || goog.LOCALE == 'ml-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ml_IN;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl' || goog.LOCALE == 'mn-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mn_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'mn_Cyrl_MN' || goog.LOCALE == 'mn-Cyrl-MN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mn_Cyrl_MN;
    +}
    +
    +if (goog.LOCALE == 'mr_IN' || goog.LOCALE == 'mr-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mr_IN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn' || goog.LOCALE == 'ms-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ms_Latn;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_BN' || goog.LOCALE == 'ms-Latn-BN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ms_Latn_BN;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_MY' || goog.LOCALE == 'ms-Latn-MY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ms_Latn_MY;
    +}
    +
    +if (goog.LOCALE == 'ms_Latn_SG' || goog.LOCALE == 'ms-Latn-SG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ms_Latn_SG;
    +}
    +
    +if (goog.LOCALE == 'mt_MT' || goog.LOCALE == 'mt-MT') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mt_MT;
    +}
    +
    +if (goog.LOCALE == 'mua') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mua;
    +}
    +
    +if (goog.LOCALE == 'mua_CM' || goog.LOCALE == 'mua-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_mua_CM;
    +}
    +
    +if (goog.LOCALE == 'my_MM' || goog.LOCALE == 'my-MM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_my_MM;
    +}
    +
    +if (goog.LOCALE == 'naq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_naq;
    +}
    +
    +if (goog.LOCALE == 'naq_NA' || goog.LOCALE == 'naq-NA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_naq_NA;
    +}
    +
    +if (goog.LOCALE == 'nb_NO' || goog.LOCALE == 'nb-NO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nb_NO;
    +}
    +
    +if (goog.LOCALE == 'nb_SJ' || goog.LOCALE == 'nb-SJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nb_SJ;
    +}
    +
    +if (goog.LOCALE == 'nd') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nd;
    +}
    +
    +if (goog.LOCALE == 'nd_ZW' || goog.LOCALE == 'nd-ZW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nd_ZW;
    +}
    +
    +if (goog.LOCALE == 'ne_IN' || goog.LOCALE == 'ne-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ne_IN;
    +}
    +
    +if (goog.LOCALE == 'ne_NP' || goog.LOCALE == 'ne-NP') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ne_NP;
    +}
    +
    +if (goog.LOCALE == 'nl_AW' || goog.LOCALE == 'nl-AW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_AW;
    +}
    +
    +if (goog.LOCALE == 'nl_BE' || goog.LOCALE == 'nl-BE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_BE;
    +}
    +
    +if (goog.LOCALE == 'nl_BQ' || goog.LOCALE == 'nl-BQ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_BQ;
    +}
    +
    +if (goog.LOCALE == 'nl_CW' || goog.LOCALE == 'nl-CW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_CW;
    +}
    +
    +if (goog.LOCALE == 'nl_NL' || goog.LOCALE == 'nl-NL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_NL;
    +}
    +
    +if (goog.LOCALE == 'nl_SR' || goog.LOCALE == 'nl-SR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_SR;
    +}
    +
    +if (goog.LOCALE == 'nl_SX' || goog.LOCALE == 'nl-SX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nl_SX;
    +}
    +
    +if (goog.LOCALE == 'nmg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nmg;
    +}
    +
    +if (goog.LOCALE == 'nmg_CM' || goog.LOCALE == 'nmg-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nmg_CM;
    +}
    +
    +if (goog.LOCALE == 'nn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nn;
    +}
    +
    +if (goog.LOCALE == 'nn_NO' || goog.LOCALE == 'nn-NO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nn_NO;
    +}
    +
    +if (goog.LOCALE == 'nnh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nnh;
    +}
    +
    +if (goog.LOCALE == 'nnh_CM' || goog.LOCALE == 'nnh-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nnh_CM;
    +}
    +
    +if (goog.LOCALE == 'nus') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nus;
    +}
    +
    +if (goog.LOCALE == 'nus_SD' || goog.LOCALE == 'nus-SD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nus_SD;
    +}
    +
    +if (goog.LOCALE == 'nyn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nyn;
    +}
    +
    +if (goog.LOCALE == 'nyn_UG' || goog.LOCALE == 'nyn-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_nyn_UG;
    +}
    +
    +if (goog.LOCALE == 'om') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_om;
    +}
    +
    +if (goog.LOCALE == 'om_ET' || goog.LOCALE == 'om-ET') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_om_ET;
    +}
    +
    +if (goog.LOCALE == 'om_KE' || goog.LOCALE == 'om-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_om_KE;
    +}
    +
    +if (goog.LOCALE == 'or_IN' || goog.LOCALE == 'or-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_or_IN;
    +}
    +
    +if (goog.LOCALE == 'os') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_os;
    +}
    +
    +if (goog.LOCALE == 'os_GE' || goog.LOCALE == 'os-GE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_os_GE;
    +}
    +
    +if (goog.LOCALE == 'os_RU' || goog.LOCALE == 'os-RU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_os_RU;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab' || goog.LOCALE == 'pa-Arab') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pa_Arab;
    +}
    +
    +if (goog.LOCALE == 'pa_Arab_PK' || goog.LOCALE == 'pa-Arab-PK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pa_Arab_PK;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru' || goog.LOCALE == 'pa-Guru') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pa_Guru;
    +}
    +
    +if (goog.LOCALE == 'pa_Guru_IN' || goog.LOCALE == 'pa-Guru-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pa_Guru_IN;
    +}
    +
    +if (goog.LOCALE == 'pl_PL' || goog.LOCALE == 'pl-PL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pl_PL;
    +}
    +
    +if (goog.LOCALE == 'ps') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ps;
    +}
    +
    +if (goog.LOCALE == 'ps_AF' || goog.LOCALE == 'ps-AF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ps_AF;
    +}
    +
    +if (goog.LOCALE == 'pt_AO' || goog.LOCALE == 'pt-AO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_AO;
    +}
    +
    +if (goog.LOCALE == 'pt_CV' || goog.LOCALE == 'pt-CV') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_CV;
    +}
    +
    +if (goog.LOCALE == 'pt_GW' || goog.LOCALE == 'pt-GW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_GW;
    +}
    +
    +if (goog.LOCALE == 'pt_MO' || goog.LOCALE == 'pt-MO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_MO;
    +}
    +
    +if (goog.LOCALE == 'pt_MZ' || goog.LOCALE == 'pt-MZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_MZ;
    +}
    +
    +if (goog.LOCALE == 'pt_ST' || goog.LOCALE == 'pt-ST') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_ST;
    +}
    +
    +if (goog.LOCALE == 'pt_TL' || goog.LOCALE == 'pt-TL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_pt_TL;
    +}
    +
    +if (goog.LOCALE == 'qu') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_qu;
    +}
    +
    +if (goog.LOCALE == 'qu_BO' || goog.LOCALE == 'qu-BO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_qu_BO;
    +}
    +
    +if (goog.LOCALE == 'qu_EC' || goog.LOCALE == 'qu-EC') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_qu_EC;
    +}
    +
    +if (goog.LOCALE == 'qu_PE' || goog.LOCALE == 'qu-PE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_qu_PE;
    +}
    +
    +if (goog.LOCALE == 'rm') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rm;
    +}
    +
    +if (goog.LOCALE == 'rm_CH' || goog.LOCALE == 'rm-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rm_CH;
    +}
    +
    +if (goog.LOCALE == 'rn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rn;
    +}
    +
    +if (goog.LOCALE == 'rn_BI' || goog.LOCALE == 'rn-BI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rn_BI;
    +}
    +
    +if (goog.LOCALE == 'ro_MD' || goog.LOCALE == 'ro-MD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ro_MD;
    +}
    +
    +if (goog.LOCALE == 'ro_RO' || goog.LOCALE == 'ro-RO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ro_RO;
    +}
    +
    +if (goog.LOCALE == 'rof') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rof;
    +}
    +
    +if (goog.LOCALE == 'rof_TZ' || goog.LOCALE == 'rof-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rof_TZ;
    +}
    +
    +if (goog.LOCALE == 'ru_BY' || goog.LOCALE == 'ru-BY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_BY;
    +}
    +
    +if (goog.LOCALE == 'ru_KG' || goog.LOCALE == 'ru-KG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_KG;
    +}
    +
    +if (goog.LOCALE == 'ru_KZ' || goog.LOCALE == 'ru-KZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_KZ;
    +}
    +
    +if (goog.LOCALE == 'ru_MD' || goog.LOCALE == 'ru-MD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_MD;
    +}
    +
    +if (goog.LOCALE == 'ru_RU' || goog.LOCALE == 'ru-RU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_RU;
    +}
    +
    +if (goog.LOCALE == 'ru_UA' || goog.LOCALE == 'ru-UA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ru_UA;
    +}
    +
    +if (goog.LOCALE == 'rw') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rw;
    +}
    +
    +if (goog.LOCALE == 'rw_RW' || goog.LOCALE == 'rw-RW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rw_RW;
    +}
    +
    +if (goog.LOCALE == 'rwk') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rwk;
    +}
    +
    +if (goog.LOCALE == 'rwk_TZ' || goog.LOCALE == 'rwk-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_rwk_TZ;
    +}
    +
    +if (goog.LOCALE == 'sah') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sah;
    +}
    +
    +if (goog.LOCALE == 'sah_RU' || goog.LOCALE == 'sah-RU') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sah_RU;
    +}
    +
    +if (goog.LOCALE == 'saq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_saq;
    +}
    +
    +if (goog.LOCALE == 'saq_KE' || goog.LOCALE == 'saq-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_saq_KE;
    +}
    +
    +if (goog.LOCALE == 'sbp') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sbp;
    +}
    +
    +if (goog.LOCALE == 'sbp_TZ' || goog.LOCALE == 'sbp-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sbp_TZ;
    +}
    +
    +if (goog.LOCALE == 'se') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_se;
    +}
    +
    +if (goog.LOCALE == 'se_FI' || goog.LOCALE == 'se-FI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_se_FI;
    +}
    +
    +if (goog.LOCALE == 'se_NO' || goog.LOCALE == 'se-NO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_se_NO;
    +}
    +
    +if (goog.LOCALE == 'se_SE' || goog.LOCALE == 'se-SE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_se_SE;
    +}
    +
    +if (goog.LOCALE == 'seh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_seh;
    +}
    +
    +if (goog.LOCALE == 'seh_MZ' || goog.LOCALE == 'seh-MZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_seh_MZ;
    +}
    +
    +if (goog.LOCALE == 'ses') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ses;
    +}
    +
    +if (goog.LOCALE == 'ses_ML' || goog.LOCALE == 'ses-ML') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ses_ML;
    +}
    +
    +if (goog.LOCALE == 'sg') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sg;
    +}
    +
    +if (goog.LOCALE == 'sg_CF' || goog.LOCALE == 'sg-CF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sg_CF;
    +}
    +
    +if (goog.LOCALE == 'shi') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_shi;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn' || goog.LOCALE == 'shi-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_shi_Latn;
    +}
    +
    +if (goog.LOCALE == 'shi_Latn_MA' || goog.LOCALE == 'shi-Latn-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_shi_Latn_MA;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng' || goog.LOCALE == 'shi-Tfng') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_shi_Tfng;
    +}
    +
    +if (goog.LOCALE == 'shi_Tfng_MA' || goog.LOCALE == 'shi-Tfng-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_shi_Tfng_MA;
    +}
    +
    +if (goog.LOCALE == 'si_LK' || goog.LOCALE == 'si-LK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_si_LK;
    +}
    +
    +if (goog.LOCALE == 'sk_SK' || goog.LOCALE == 'sk-SK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sk_SK;
    +}
    +
    +if (goog.LOCALE == 'sl_SI' || goog.LOCALE == 'sl-SI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sl_SI;
    +}
    +
    +if (goog.LOCALE == 'smn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_smn;
    +}
    +
    +if (goog.LOCALE == 'smn_FI' || goog.LOCALE == 'smn-FI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_smn_FI;
    +}
    +
    +if (goog.LOCALE == 'sn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sn;
    +}
    +
    +if (goog.LOCALE == 'sn_ZW' || goog.LOCALE == 'sn-ZW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sn_ZW;
    +}
    +
    +if (goog.LOCALE == 'so') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_so;
    +}
    +
    +if (goog.LOCALE == 'so_DJ' || goog.LOCALE == 'so-DJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_so_DJ;
    +}
    +
    +if (goog.LOCALE == 'so_ET' || goog.LOCALE == 'so-ET') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_so_ET;
    +}
    +
    +if (goog.LOCALE == 'so_KE' || goog.LOCALE == 'so-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_so_KE;
    +}
    +
    +if (goog.LOCALE == 'so_SO' || goog.LOCALE == 'so-SO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_so_SO;
    +}
    +
    +if (goog.LOCALE == 'sq_AL' || goog.LOCALE == 'sq-AL') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sq_AL;
    +}
    +
    +if (goog.LOCALE == 'sq_MK' || goog.LOCALE == 'sq-MK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sq_MK;
    +}
    +
    +if (goog.LOCALE == 'sq_XK' || goog.LOCALE == 'sq-XK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sq_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl' || goog.LOCALE == 'sr-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_BA' || goog.LOCALE == 'sr-Cyrl-BA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Cyrl_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_ME' || goog.LOCALE == 'sr-Cyrl-ME') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Cyrl_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_RS' || goog.LOCALE == 'sr-Cyrl-RS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Cyrl_RS;
    +}
    +
    +if (goog.LOCALE == 'sr_Cyrl_XK' || goog.LOCALE == 'sr-Cyrl-XK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Cyrl_XK;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn' || goog.LOCALE == 'sr-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Latn;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_BA' || goog.LOCALE == 'sr-Latn-BA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Latn_BA;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_ME' || goog.LOCALE == 'sr-Latn-ME') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Latn_ME;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_RS' || goog.LOCALE == 'sr-Latn-RS') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Latn_RS;
    +}
    +
    +if (goog.LOCALE == 'sr_Latn_XK' || goog.LOCALE == 'sr-Latn-XK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sr_Latn_XK;
    +}
    +
    +if (goog.LOCALE == 'sv_AX' || goog.LOCALE == 'sv-AX') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sv_AX;
    +}
    +
    +if (goog.LOCALE == 'sv_FI' || goog.LOCALE == 'sv-FI') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sv_FI;
    +}
    +
    +if (goog.LOCALE == 'sv_SE' || goog.LOCALE == 'sv-SE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sv_SE;
    +}
    +
    +if (goog.LOCALE == 'sw_KE' || goog.LOCALE == 'sw-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sw_KE;
    +}
    +
    +if (goog.LOCALE == 'sw_TZ' || goog.LOCALE == 'sw-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sw_TZ;
    +}
    +
    +if (goog.LOCALE == 'sw_UG' || goog.LOCALE == 'sw-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_sw_UG;
    +}
    +
    +if (goog.LOCALE == 'swc') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_swc;
    +}
    +
    +if (goog.LOCALE == 'swc_CD' || goog.LOCALE == 'swc-CD') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_swc_CD;
    +}
    +
    +if (goog.LOCALE == 'ta_IN' || goog.LOCALE == 'ta-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ta_IN;
    +}
    +
    +if (goog.LOCALE == 'ta_LK' || goog.LOCALE == 'ta-LK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ta_LK;
    +}
    +
    +if (goog.LOCALE == 'ta_MY' || goog.LOCALE == 'ta-MY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ta_MY;
    +}
    +
    +if (goog.LOCALE == 'ta_SG' || goog.LOCALE == 'ta-SG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ta_SG;
    +}
    +
    +if (goog.LOCALE == 'te_IN' || goog.LOCALE == 'te-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_te_IN;
    +}
    +
    +if (goog.LOCALE == 'teo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_teo;
    +}
    +
    +if (goog.LOCALE == 'teo_KE' || goog.LOCALE == 'teo-KE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_teo_KE;
    +}
    +
    +if (goog.LOCALE == 'teo_UG' || goog.LOCALE == 'teo-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_teo_UG;
    +}
    +
    +if (goog.LOCALE == 'th_TH' || goog.LOCALE == 'th-TH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_th_TH;
    +}
    +
    +if (goog.LOCALE == 'ti') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ti;
    +}
    +
    +if (goog.LOCALE == 'ti_ER' || goog.LOCALE == 'ti-ER') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ti_ER;
    +}
    +
    +if (goog.LOCALE == 'ti_ET' || goog.LOCALE == 'ti-ET') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ti_ET;
    +}
    +
    +if (goog.LOCALE == 'to') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_to;
    +}
    +
    +if (goog.LOCALE == 'to_TO' || goog.LOCALE == 'to-TO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_to_TO;
    +}
    +
    +if (goog.LOCALE == 'tr_CY' || goog.LOCALE == 'tr-CY') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tr_CY;
    +}
    +
    +if (goog.LOCALE == 'tr_TR' || goog.LOCALE == 'tr-TR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tr_TR;
    +}
    +
    +if (goog.LOCALE == 'twq') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_twq;
    +}
    +
    +if (goog.LOCALE == 'twq_NE' || goog.LOCALE == 'twq-NE') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_twq_NE;
    +}
    +
    +if (goog.LOCALE == 'tzm') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tzm;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn' || goog.LOCALE == 'tzm-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tzm_Latn;
    +}
    +
    +if (goog.LOCALE == 'tzm_Latn_MA' || goog.LOCALE == 'tzm-Latn-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_tzm_Latn_MA;
    +}
    +
    +if (goog.LOCALE == 'ug') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ug;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab' || goog.LOCALE == 'ug-Arab') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ug_Arab;
    +}
    +
    +if (goog.LOCALE == 'ug_Arab_CN' || goog.LOCALE == 'ug-Arab-CN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ug_Arab_CN;
    +}
    +
    +if (goog.LOCALE == 'uk_UA' || goog.LOCALE == 'uk-UA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uk_UA;
    +}
    +
    +if (goog.LOCALE == 'ur_IN' || goog.LOCALE == 'ur-IN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ur_IN;
    +}
    +
    +if (goog.LOCALE == 'ur_PK' || goog.LOCALE == 'ur-PK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_ur_PK;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab' || goog.LOCALE == 'uz-Arab') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Arab;
    +}
    +
    +if (goog.LOCALE == 'uz_Arab_AF' || goog.LOCALE == 'uz-Arab-AF') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Arab_AF;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl' || goog.LOCALE == 'uz-Cyrl') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Cyrl;
    +}
    +
    +if (goog.LOCALE == 'uz_Cyrl_UZ' || goog.LOCALE == 'uz-Cyrl-UZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Cyrl_UZ;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn' || goog.LOCALE == 'uz-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Latn;
    +}
    +
    +if (goog.LOCALE == 'uz_Latn_UZ' || goog.LOCALE == 'uz-Latn-UZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_uz_Latn_UZ;
    +}
    +
    +if (goog.LOCALE == 'vai') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vai;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn' || goog.LOCALE == 'vai-Latn') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vai_Latn;
    +}
    +
    +if (goog.LOCALE == 'vai_Latn_LR' || goog.LOCALE == 'vai-Latn-LR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vai_Latn_LR;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii' || goog.LOCALE == 'vai-Vaii') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vai_Vaii;
    +}
    +
    +if (goog.LOCALE == 'vai_Vaii_LR' || goog.LOCALE == 'vai-Vaii-LR') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vai_Vaii_LR;
    +}
    +
    +if (goog.LOCALE == 'vi_VN' || goog.LOCALE == 'vi-VN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vi_VN;
    +}
    +
    +if (goog.LOCALE == 'vun') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vun;
    +}
    +
    +if (goog.LOCALE == 'vun_TZ' || goog.LOCALE == 'vun-TZ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_vun_TZ;
    +}
    +
    +if (goog.LOCALE == 'wae') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_wae;
    +}
    +
    +if (goog.LOCALE == 'wae_CH' || goog.LOCALE == 'wae-CH') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_wae_CH;
    +}
    +
    +if (goog.LOCALE == 'xog') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_xog;
    +}
    +
    +if (goog.LOCALE == 'xog_UG' || goog.LOCALE == 'xog-UG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_xog_UG;
    +}
    +
    +if (goog.LOCALE == 'yav') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yav;
    +}
    +
    +if (goog.LOCALE == 'yav_CM' || goog.LOCALE == 'yav-CM') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yav_CM;
    +}
    +
    +if (goog.LOCALE == 'yi') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yi;
    +}
    +
    +if (goog.LOCALE == 'yi_001' || goog.LOCALE == 'yi-001') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yi_001;
    +}
    +
    +if (goog.LOCALE == 'yo') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yo;
    +}
    +
    +if (goog.LOCALE == 'yo_BJ' || goog.LOCALE == 'yo-BJ') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yo_BJ;
    +}
    +
    +if (goog.LOCALE == 'yo_NG' || goog.LOCALE == 'yo-NG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_yo_NG;
    +}
    +
    +if (goog.LOCALE == 'zgh') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zgh;
    +}
    +
    +if (goog.LOCALE == 'zgh_MA' || goog.LOCALE == 'zgh-MA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zgh_MA;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans' || goog.LOCALE == 'zh-Hans') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hans;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_CN' || goog.LOCALE == 'zh-Hans-CN') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hans_CN;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_HK' || goog.LOCALE == 'zh-Hans-HK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hans_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_MO' || goog.LOCALE == 'zh-Hans-MO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hans_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hans_SG' || goog.LOCALE == 'zh-Hans-SG') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hans_SG;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant' || goog.LOCALE == 'zh-Hant') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hant;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_HK' || goog.LOCALE == 'zh-Hant-HK') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hant_HK;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_MO' || goog.LOCALE == 'zh-Hant-MO') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hant_MO;
    +}
    +
    +if (goog.LOCALE == 'zh_Hant_TW' || goog.LOCALE == 'zh-Hant-TW') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zh_Hant_TW;
    +}
    +
    +if (goog.LOCALE == 'zu_ZA' || goog.LOCALE == 'zu-ZA') {
    +  goog.labs.i18n.ListFormatSymbols = goog.labs.i18n.ListFormatSymbols_zu_ZA;
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable.js b/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable.js
    new file mode 100644
    index 0000000000..df001d648d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable.js
    @@ -0,0 +1,139 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for working with ES6 iterables.
    + * Note that this file is written ES5-only.
    + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol
    + */
    +
    +goog.module('goog.labs.iterable');
    +
    +
    +/**
    + * Get the iterator for an iterable.
    + * @param {!Iterable<VALUE>} iterable
    + * @return {!Iterator<VALUE>}
    + * @template VALUE
    + */
    +exports.getIterator = function(iterable) {
    +  return iterable[goog.global.Symbol.iterator]();
    +};
    +
    +
    +/**
    + * Call a function with every value of an iterable.
    + *
    + * Warning: this function will never halt if given an iterable that
    + * is never exhausted.
    + *
    + * @param {!function(VALUE): void} f
    + * @param {!Iterable<VALUE>} iterable
    + * @template VALUE
    + */
    +exports.forEach = function(f, iterable) {
    +  var iterator = exports.getIterator(iterable);
    +  while (true) {
    +    var next = iterator.next();
    +    if (next.done) {
    +      return;
    +    }
    +    f(next.value);
    +  }
    +};
    +
    +
    +/**
    + * Maps the values of one iterable to create another iterable.
    + *
    + * When next() is called on the returned iterable, it will call the given
    + * function {@code f} with the next value of the given iterable
    + * {@code iterable} until the given iterable is exhausted.
    + *
    + * @param {!function(this: THIS, VALUE): RESULT} f
    + * @param {!Iterable<VALUE>} iterable
    + * @return {!Iterable<RESULT>} The created iterable that gives the mapped
    + *     values.
    + * @template THIS, VALUE, RESULT
    + */
    +exports.map = function(f, iterable) {
    +  return new FactoryIterable(function() {
    +    var iterator = exports.getIterator(iterable);
    +    return new MapIterator(f, iterator);
    +  });
    +};
    +
    +
    +
    +/**
    + * Helper class for {@code map}.
    + * @param {!function(VALUE): RESULT} f
    + * @param {!Iterator<VALUE>} iterator
    + * @constructor
    + * @implements {Iterator<RESULT>}
    + * @template VALUE, RESULT
    + */
    +var MapIterator = function(f, iterator) {
    +  /** @private */
    +  this.func_ = f;
    +  /** @private */
    +  this.iterator_ = iterator;
    +};
    +
    +
    +/**
    + * @override
    + */
    +MapIterator.prototype.next = function() {
    +  var nextObj = this.iterator_.next();
    +
    +  if (nextObj.done) {
    +    return {done: true, value: undefined};
    +  }
    +
    +  var mappedValue = this.func_(nextObj.value);
    +  return {
    +    done: false,
    +    value: mappedValue
    +  };
    +};
    +
    +
    +
    +/**
    + * Helper class to create an iterable with a given iterator factory.
    + * @param {function():!Iterator<VALUE>} iteratorFactory
    + * @constructor
    + * @implements {Iterable<VALUE>}
    + * @template VALUE
    + */
    +var FactoryIterable = function(iteratorFactory) {
    +  /**
    +   * @private
    +   */
    +  this.iteratorFactory_ = iteratorFactory;
    +};
    +
    +
    +// TODO(nnaze): For now, this section is not run if Symbol is not defined,
    +// since goog.global.Symbol.iterator will not be defined below.
    +// Determine best course of action if "Symbol" is not available.
    +if (goog.global.Symbol) {
    +  /**
    +   * @return {!Iterator<VALUE>}
    +   */
    +  FactoryIterable.prototype[goog.global.Symbol.iterator] = function() {
    +    return this.iteratorFactory_();
    +  };
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable_test.js b/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable_test.js
    new file mode 100644
    index 0000000000..339790d052
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/iterable/iterable_test.js
    @@ -0,0 +1,146 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests for goog.labs.iterable
    + */
    +
    +goog.module('goog.labs.iterableTest');
    +
    +goog.module.declareTestMethods();
    +goog.setTestOnly();
    +
    +goog.require('goog.testing.jsunit');
    +
    +var iterable = goog.require('goog.labs.iterable');
    +var recordFunction = goog.require('goog.testing.recordFunction');
    +
    +
    +/**
    + * Create an iterator starting at "start" and increments up to
    + * (but not including) "stop".
    + */
    +function createRangeIterator(start, stop) {
    +  var value = start;
    +  var next = function() {
    +    if (value < stop) {
    +      return {
    +        value: value++,
    +        done: false
    +      };
    +    }
    +
    +    return {
    +      value: undefined,
    +      done: true
    +    };
    +  };
    +
    +  return {
    +    next: next
    +  };
    +}
    +
    +function createRangeIterable(start, stop) {
    +  var obj = {};
    +
    +  // Refer to goog.global['Symbol'] because otherwise this
    +  // is a parse error in earlier IEs.
    +  obj[goog.global['Symbol'].iterator] = function() {
    +    return createRangeIterator(start, stop);
    +  };
    +  return obj;
    +}
    +
    +function isSymbolDefined() {
    +  return !!goog.global['Symbol'];
    +}
    +
    +exports.testCreateRangeIterable = function() {
    +  // Do not run if Symbol does not exist in this browser.
    +  if (!isSymbolDefined()) {
    +    return;
    +  }
    +
    +  var rangeIterator = createRangeIterator(0, 3);
    +
    +  for (var i = 0; i < 3; i++) {
    +    assertObjectEquals({
    +      value: i,
    +      done: false
    +    }, rangeIterator.next());
    +  }
    +
    +  for (var i = 0; i < 3; i++) {
    +    assertObjectEquals({
    +      value: undefined,
    +      done: true
    +    }, rangeIterator.next());
    +  }
    +};
    +
    +exports.testForEach = function() {
    +  // Do not run if Symbol does not exist in this browser.
    +  if (!isSymbolDefined()) {
    +    return;
    +  }
    +
    +  var range = createRangeIterable(0, 3);
    +
    +  var callback = recordFunction();
    +  iterable.forEach(callback, range, self);
    +
    +  callback.assertCallCount(3);
    +
    +  var calls = callback.getCalls();
    +  for (var i = 0; i < calls.length; i++) {
    +    var call = calls[i];
    +    assertArrayEquals([i], call.getArguments());
    +  }
    +};
    +
    +exports.testMap = function() {
    +  // Do not run if Symbol does not exist in this browser.
    +  if (!isSymbolDefined()) {
    +    return;
    +  }
    +
    +  var range = createRangeIterable(0, 3);
    +
    +  function addTwo(i) {
    +    return i + 2;
    +  }
    +
    +  var newIterable = iterable.map(addTwo, range);
    +  var newIterator = iterable.getIterator(newIterable);
    +
    +  var nextObj = newIterator.next();
    +  assertEquals(2, nextObj.value);
    +  assertFalse(nextObj.done);
    +
    +  nextObj = newIterator.next();
    +  assertEquals(3, nextObj.value);
    +  assertFalse(nextObj.done);
    +
    +  nextObj = newIterator.next();
    +  assertEquals(4, nextObj.value);
    +  assertFalse(nextObj.done);
    +
    +  // Check that the iterator repeatedly signals done.
    +  for (var i = 0; i < 3; i++) {
    +    nextObj = newIterator.next();
    +    assertUndefined(nextObj.value);
    +    assertTrue(nextObj.done);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/mock/mock.js b/mapbox-gl/closure-library/closure/goog/labs/mock/mock.js
    new file mode 100644
    index 0000000000..436734f1f8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/mock/mock.js
    @@ -0,0 +1,861 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a mocking framework in Closure to make unit tests easy
    + * to write and understand. The methods provided here can be used to replace
    + * implementations of existing objects with 'mock' objects to abstract out
    + * external services and dependencies thereby isolating the code under test.
    + * Apart from mocking, methods are also provided to just monitor calls to an
    + * object (spying) and returning specific values for some or all the inputs to
    + * methods (stubbing).
    + *
    + * Design doc : http://go/closuremock
    + *
    + */
    +
    +
    +goog.provide('goog.labs.mock');
    +goog.provide('goog.labs.mock.VerificationError');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.debug');
    +goog.require('goog.debug.Error');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +
    +
    +/**
    + * Mocks a given object or class.
    + *
    + * @param {!Object} objectOrClass An instance or a constructor of a class to be
    + *     mocked.
    + * @return {!Object} The mocked object.
    + */
    +goog.labs.mock.mock = function(objectOrClass) {
    +  // Go over properties of 'objectOrClass' and create a MockManager to
    +  // be used for stubbing out calls to methods.
    +  var mockObjectManager = new goog.labs.mock.MockObjectManager_(objectOrClass);
    +  var mockedObject = mockObjectManager.getMockedItem();
    +  goog.asserts.assertObject(mockedObject);
    +  return /** @type {!Object} */ (mockedObject);
    +};
    +
    +
    +/**
    + * Mocks a given function.
    + *
    + * @param {!Function} func A function to be mocked.
    + * @return {!Function} The mocked function.
    + */
    +goog.labs.mock.mockFunction = function(func) {
    +  var mockFuncManager = new goog.labs.mock.MockFunctionManager_(func);
    +  var mockedFunction = mockFuncManager.getMockedItem();
    +  goog.asserts.assertFunction(mockedFunction);
    +  return /** @type {!Function} */ (mockedFunction);
    +};
    +
    +
    +/**
    + * Spies on a given object.
    + *
    + * @param {!Object} obj The object to be spied on.
    + * @return {!Object} The spy object.
    + */
    +goog.labs.mock.spy = function(obj) {
    +  // Go over properties of 'obj' and create a MockSpyManager_ to
    +  // be used for spying on calls to methods.
    +  var mockSpyManager = new goog.labs.mock.MockSpyManager_(obj);
    +  var spyObject = mockSpyManager.getMockedItem();
    +  goog.asserts.assert(spyObject);
    +  return spyObject;
    +};
    +
    +
    +/**
    + * Returns an object that can be used to verify calls to specific methods of a
    + * given mock.
    + *
    + * @param {!Object} obj The mocked object.
    + * @return {!Object} The verifier.
    + */
    +goog.labs.mock.verify = function(obj) {
    +  return obj.$callVerifier;
    +};
    +
    +
    +/**
    + * Returns a name to identify a function. Named functions return their names,
    + * unnamed functions return a string of the form '#anonymous{ID}' where ID is
    + * a unique identifier for each anonymous function.
    + * @private
    + * @param {!Function} func The function.
    + * @return {string} The function name.
    + */
    +goog.labs.mock.getFunctionName_ = function(func) {
    +  var funcName = goog.debug.getFunctionName(func);
    +  if (funcName == '' || funcName == '[Anonymous]') {
    +    funcName = '#anonymous' + goog.labs.mock.getUid(func);
    +  }
    +  return funcName;
    +};
    +
    +
    +/**
    + * Returns a nicely formatted, readble representation of a method call.
    + * @private
    + * @param {string} methodName The name of the method.
    + * @param {Array<?>=} opt_args The method arguments.
    + * @return {string} The string representation of the method call.
    + */
    +goog.labs.mock.formatMethodCall_ = function(methodName, opt_args) {
    +  opt_args = opt_args || [];
    +  opt_args = goog.array.map(opt_args, function(arg) {
    +    if (goog.isFunction(arg)) {
    +      var funcName = goog.labs.mock.getFunctionName_(arg);
    +      return '<function ' + funcName + '>';
    +    } else {
    +      var isObjectWithClass = goog.isObject(arg) &&
    +          !goog.isFunction(arg) && !goog.isArray(arg) &&
    +          arg.constructor != Object;
    +
    +      if (isObjectWithClass) {
    +        return arg.toString();
    +      }
    +
    +      return goog.labs.mock.formatValue_(arg);
    +    }
    +  });
    +  return methodName + '(' + opt_args.join(', ') + ')';
    +};
    +
    +
    +/**
    + * An array to store objects for unique id generation.
    + * @private
    + * @type {!Array<!Object>}
    + */
    +goog.labs.mock.uid_ = [];
    +
    +
    +/**
    + * A unique Id generator that does not modify the object.
    + * @param {Object!} obj The object whose unique ID we want to generate.
    + * @return {number} an unique id for the object.
    + */
    +goog.labs.mock.getUid = function(obj) {
    +  var index = goog.array.indexOf(goog.labs.mock.uid_, obj);
    +  if (index == -1) {
    +    index = goog.labs.mock.uid_.length;
    +    goog.labs.mock.uid_.push(obj);
    +  }
    +  return index;
    +};
    +
    +
    +/**
    + * This is just another implementation of goog.debug.deepExpose with a more
    + * compact format.
    + * @private
    + * @param {*} obj The object whose string representation will be returned.
    + * @param {boolean=} opt_id Whether to include the id of objects or not.
    + *     Defaults to true.
    + * @return {string} The string representation of the object.
    + */
    +goog.labs.mock.formatValue_ = function(obj, opt_id) {
    +  var id = goog.isDef(opt_id) ? opt_id : true;
    +  var previous = [];
    +  var output = [];
    +
    +  var helper = function(obj) {
    +    var indentMultiline = function(output) {
    +      return output.replace(/\n/g, '\n');
    +    };
    +
    +    /** @preserveTry */
    +    try {
    +      if (!goog.isDef(obj)) {
    +        output.push('undefined');
    +      } else if (goog.isNull(obj)) {
    +        output.push('NULL');
    +      } else if (goog.isString(obj)) {
    +        output.push('"' + indentMultiline(obj) + '"');
    +      } else if (goog.isFunction(obj)) {
    +        var funcName = goog.labs.mock.getFunctionName_(obj);
    +        output.push('<function ' + funcName + '>');
    +      } else if (goog.isObject(obj)) {
    +        if (goog.array.contains(previous, obj)) {
    +          if (id) {
    +            output.push('<recursive/dupe obj_' +
    +                goog.labs.mock.getUid(obj) + '>');
    +          } else {
    +            output.push('<recursive/dupe>');
    +          }
    +        } else {
    +          previous.push(obj);
    +          output.push('{');
    +          var inner_obj = [];
    +          for (var x in obj) {
    +            output.push(' ');
    +            output.push('"' + x + '"' + ':');
    +            helper(obj[x]);
    +          }
    +          if (id) {
    +            output.push(' _id:' + goog.labs.mock.getUid(obj));
    +          }
    +          output.push('}');
    +        }
    +      } else {
    +        output.push(obj);
    +      }
    +    } catch (e) {
    +      output.push('*** ' + e + ' ***');
    +    }
    +  };
    +
    +  helper(obj);
    +  return output.join('').replace(/"closure_uid_\d+"/g, '_id')
    +      .replace(/{ /g, '{');
    +
    +};
    +
    +
    +
    +/**
    + * Error thrown when verification failed.
    + *
    + * @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls
    + *     The recorded calls that didn't match the expectation.
    + * @param {!string} methodName The expected method call.
    + * @param {!Array<?>} args The expected arguments.
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.labs.mock.VerificationError = function(recordedCalls, methodName, args) {
    +  var msg = goog.labs.mock.VerificationError.getVerificationErrorMsg_(
    +      recordedCalls, methodName, args);
    +  goog.labs.mock.VerificationError.base(this, 'constructor', msg);
    +};
    +goog.inherits(goog.labs.mock.VerificationError, goog.debug.Error);
    +
    +
    +/** @override */
    +goog.labs.mock.VerificationError.prototype.name = 'VerificationError';
    +
    +
    +/**
    + * This array contains the name of the functions that are part of the base
    + * Object prototype.
    + * Basically a copy of goog.object.PROTOTYPE_FIELDS_.
    + * @const
    + * @type {!Array<string>}
    + * @private
    + */
    +goog.labs.mock.PROTOTYPE_FIELDS_ = [
    +  'constructor',
    +  'hasOwnProperty',
    +  'isPrototypeOf',
    +  'propertyIsEnumerable',
    +  'toLocaleString',
    +  'toString',
    +  'valueOf'
    +];
    +
    +
    +/**
    + * Constructs a descriptive error message for an expected method call.
    + * @private
    + * @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls
    + *     The recorded calls that didn't match the expectation.
    + * @param {!string} methodName The expected method call.
    + * @param {!Array<?>} args The expected arguments.
    + * @return {string} The error message.
    + */
    +goog.labs.mock.VerificationError.getVerificationErrorMsg_ =
    +    function(recordedCalls, methodName, args) {
    +
    +  recordedCalls = goog.array.filter(recordedCalls, function(binding) {
    +    return binding.getMethodName() == methodName;
    +  });
    +
    +  var expected = goog.labs.mock.formatMethodCall_(methodName, args);
    +
    +  var msg = '\nExpected: ' + expected.toString();
    +  msg += '\nRecorded: ';
    +
    +  if (recordedCalls.length > 0) {
    +    msg += recordedCalls.join(',\n          ');
    +  } else {
    +    msg += 'No recorded calls';
    +  }
    +
    +  return msg;
    +};
    +
    +
    +
    +/**
    + * Base class that provides basic functionality for creating, adding and
    + * finding bindings, offering an executor method that is called when a call to
    + * the stub is made, an array to hold the bindings and the mocked item, among
    + * other things.
    + *
    + * @constructor
    + * @struct
    + * @private
    + */
    +goog.labs.mock.MockManager_ = function() {
    +  /**
    +   * Proxies the methods for the mocked object or class to execute the stubs.
    +   * @type {!Object}
    +   * @protected
    +   */
    +  this.mockedItem = {};
    +
    +  /**
    +   * A reference to the object or function being mocked.
    +   * @type {Object|Function}
    +   * @protected
    +   */
    +  this.mockee = null;
    +
    +  /**
    +   * Holds the stub bindings established so far.
    +   * @protected
    +   */
    +  this.methodBindings = [];
    +
    +  /**
    +   * Holds a reference to the binder used to define stubs.
    +   * @protected
    +   */
    +  this.$stubBinder = null;
    +
    +  /**
    +   * Record method calls with no stub definitions.
    +   * @type {!Array<!goog.labs.mock.MethodBinding_>}
    +   * @private
    +   */
    +  this.callRecords_ = [];
    +};
    +
    +
    +/**
    + * Handles the first step in creating a stub, returning a stub-binder that
    + * is later used to bind a stub for a method.
    + *
    + * @param {string} methodName The name of the method being bound.
    + * @param {...*} var_args The arguments to the method.
    + * @return {!goog.labs.mock.StubBinder_} The stub binder.
    + * @private
    + */
    +goog.labs.mock.MockManager_.prototype.handleMockCall_ =
    +    function(methodName, var_args) {
    +  var args = goog.array.slice(arguments, 1);
    +  return new goog.labs.mock.StubBinder_(this, methodName, args);
    +};
    +
    +
    +/**
    + * Returns the mock object. This should have a stubbed method for each method
    + * on the object being mocked.
    + *
    + * @return {!Object|!Function} The mock object.
    + */
    +goog.labs.mock.MockManager_.prototype.getMockedItem = function() {
    +  return this.mockedItem;
    +};
    +
    +
    +/**
    + * Adds a binding for the method name and arguments to be stubbed.
    + *
    + * @param {?string} methodName The name of the stubbed method.
    + * @param {!Array<?>} args The arguments passed to the method.
    + * @param {!Function} func The stub function.
    + *
    + */
    +goog.labs.mock.MockManager_.prototype.addBinding =
    +    function(methodName, args, func) {
    +  var binding = new goog.labs.mock.MethodBinding_(methodName, args, func);
    +  this.methodBindings.push(binding);
    +};
    +
    +
    +/**
    + * Returns a stub, if defined, for the method name and arguments passed in.
    + * If there are multiple stubs for this method name and arguments, then
    + * the first one is returned and removed from the list.
    + *
    + * @param {string} methodName The name of the stubbed method.
    + * @param {!Array<?>} args The arguments passed to the method.
    + * @return {Function} The stub function or undefined.
    + * @protected
    + */
    +goog.labs.mock.MockManager_.prototype.getNextBinding =
    +    function(methodName, args) {
    +  var first = -1;
    +  var count = 0;
    +  var stub = null;
    +  goog.array.forEach(this.methodBindings, function(binding, i) {
    +    if (binding.matches(methodName, args, false /* isVerification */)) {
    +      count++;
    +      if (goog.isNull(stub)) {
    +        first = i;
    +        stub = binding;
    +      }
    +    }
    +  });
    +  if (count > 1) {
    +    goog.array.removeAt(this.methodBindings, first);
    +  }
    +  return stub && stub.getStub();
    +};
    +
    +
    +/**
    + * Returns a stub, if defined, for the method name and arguments passed in as
    + * parameters.
    + *
    + * @param {string} methodName The name of the stubbed method.
    + * @param {!Array<?>} args The arguments passed to the method.
    + * @return {Function} The stub function or undefined.
    + * @protected
    + */
    +goog.labs.mock.MockManager_.prototype.getExecutor = function(methodName, args) {
    +  return this.getNextBinding(methodName, args);
    +};
    +
    +
    +/**
    + * Looks up the list of stubs defined on the mock object and executes the
    + * function associated with that stub.
    + *
    + * @param {string} methodName The name of the method to execute.
    + * @param {...*} var_args The arguments passed to the method.
    + * @return {*} Value returned by the stub function.
    + * @protected
    + */
    +goog.labs.mock.MockManager_.prototype.executeStub =
    +    function(methodName, var_args) {
    +  var args = goog.array.slice(arguments, 1);
    +
    +  // Record this call
    +  this.recordCall_(methodName, args);
    +
    +  var func = this.getExecutor(methodName, args);
    +  if (func) {
    +    return func.apply(null, args);
    +  }
    +};
    +
    +
    +/**
    + * Records a call to 'methodName' with arguments 'args'.
    + *
    + * @param {string} methodName The name of the called method.
    + * @param {!Array<?>} args The array of arguments.
    + * @private
    + */
    +goog.labs.mock.MockManager_.prototype.recordCall_ =
    +    function(methodName, args) {
    +  var callRecord = new goog.labs.mock.MethodBinding_(methodName, args,
    +      goog.nullFunction);
    +
    +  this.callRecords_.push(callRecord);
    +};
    +
    +
    +/**
    + * Verify invocation of a method with specific arguments.
    + *
    + * @param {string} methodName The name of the method.
    + * @param {...*} var_args The arguments passed.
    + * @protected
    + */
    +goog.labs.mock.MockManager_.prototype.verifyInvocation =
    +    function(methodName, var_args) {
    +  var args = goog.array.slice(arguments, 1);
    +  var binding = goog.array.find(this.callRecords_, function(binding) {
    +    return binding.matches(methodName, args, true /* isVerification */);
    +  });
    +
    +  if (!binding) {
    +    throw new goog.labs.mock.VerificationError(
    +        this.callRecords_, methodName, args);
    +  }
    +};
    +
    +
    +
    +/**
    + * Sets up mock for the given object (or class), stubbing out all the defined
    + * methods. By default, all stubs return {@code undefined}, though stubs can be
    + * later defined using {@code goog.labs.mock.when}.
    + *
    + * @param {!Object|!Function} objOrClass The object or class to set up the mock
    + *     for. A class is a constructor function.
    + *
    + * @constructor
    + * @struct
    + * @extends {goog.labs.mock.MockManager_}
    + * @private
    + */
    +goog.labs.mock.MockObjectManager_ = function(objOrClass) {
    +  goog.labs.mock.MockObjectManager_.base(this, 'constructor');
    +
    +  /**
    +   * Proxies the calls to establish the first step of the stub bindings (object
    +   * and method name)
    +   * @private
    +   */
    +  this.objectStubBinder_ = {};
    +
    +  this.mockee = objOrClass;
    +
    +  /**
    +   * The call verifier is used to verify the calls. It maps property names to
    +   * the method that does call verification.
    +   * @type {!Object<string, function(string, ...)>}
    +   * @private
    +   */
    +  this.objectCallVerifier_ = {};
    +
    +  var obj;
    +  if (goog.isFunction(objOrClass)) {
    +    // Create a temporary subclass with a no-op constructor so that we can
    +    // create an instance and determine what methods it has.
    +    /**
    + * @constructor
    + * @final
    + */
    +    var tempCtor = function() {};
    +    goog.inherits(tempCtor, objOrClass);
    +    obj = new tempCtor();
    +  } else {
    +    obj = objOrClass;
    +  }
    +
    +  // Put the object being mocked in the prototype chain of the mock so that
    +  // it has all the correct properties and instanceof works.
    +  /**
    + * @constructor
    + * @final
    + */
    +  var mockedItemCtor = function() {};
    +  mockedItemCtor.prototype = obj;
    +  this.mockedItem = new mockedItemCtor();
    +
    +  var enumerableProperties = goog.object.getKeys(obj);
    +  // The non enumerable properties are added due to the fact that IE8 does not
    +  // enumerate any of the prototype Object functions even when overriden and
    +  // mocking these is sometimes needed.
    +  for (var i = 0; i < goog.labs.mock.PROTOTYPE_FIELDS_.length; i++) {
    +    var prop = goog.labs.mock.PROTOTYPE_FIELDS_[i];
    +    if (!goog.array.contains(enumerableProperties, prop)) {
    +      enumerableProperties.push(prop);
    +    }
    +  }
    +
    +  // Adds the properties to the mock, creating a proxy stub for each method on
    +  // the instance.
    +  for (var i = 0; i < enumerableProperties.length; i++) {
    +    var prop = enumerableProperties[i];
    +    if (goog.isFunction(obj[prop])) {
    +      this.mockedItem[prop] = goog.bind(this.executeStub, this, prop);
    +      // The stub binder used to create bindings.
    +      this.objectStubBinder_[prop] =
    +          goog.bind(this.handleMockCall_, this, prop);
    +      // The verifier verifies the calls.
    +      this.objectCallVerifier_[prop] =
    +          goog.bind(this.verifyInvocation, this, prop);
    +    }
    +  }
    +  // The alias for stub binder exposed to the world.
    +  this.mockedItem.$stubBinder = this.objectStubBinder_;
    +
    +  // The alias for verifier for the world.
    +  this.mockedItem.$callVerifier = this.objectCallVerifier_;
    +};
    +goog.inherits(goog.labs.mock.MockObjectManager_,
    +              goog.labs.mock.MockManager_);
    +
    +
    +
    +/**
    + * Sets up the spying behavior for the given object.
    + *
    + * @param {!Object} obj The object to be spied on.
    + *
    + * @constructor
    + * @struct
    + * @extends {goog.labs.mock.MockObjectManager_}
    + * @private
    + */
    +goog.labs.mock.MockSpyManager_ = function(obj) {
    +  goog.labs.mock.MockSpyManager_.base(this, 'constructor', obj);
    +};
    +goog.inherits(goog.labs.mock.MockSpyManager_,
    +              goog.labs.mock.MockObjectManager_);
    +
    +
    +/**
    + * Return a stub, if defined, for the method and arguments passed in. If we lack
    + * a stub, instead look for a call record that matches the method and arguments.
    + *
    + * @return {!Function} The stub or the invocation logger, if defined.
    + * @override
    + */
    +goog.labs.mock.MockSpyManager_.prototype.getNextBinding =
    +    function(methodName, args) {
    +  var stub = goog.labs.mock.MockSpyManager_.base(
    +      this, 'getNextBinding', methodName, args);
    +
    +  if (!stub) {
    +    stub = goog.bind(this.mockee[methodName], this.mockee);
    +  }
    +
    +  return stub;
    +};
    +
    +
    +
    +/**
    + * Sets up mock for the given function, stubbing out. By default, all stubs
    + * return {@code undefined}, though stubs can be later defined using
    + * {@code goog.labs.mock.when}.
    + *
    + * @param {!Function} func The function to set up the mock for.
    + *
    + * @constructor
    + * @struct
    + * @extends {goog.labs.mock.MockManager_}
    + * @private
    + */
    +goog.labs.mock.MockFunctionManager_ = function(func) {
    +  goog.labs.mock.MockFunctionManager_.base(this, 'constructor');
    +
    +  this.func_ = func;
    +
    +  /**
    +   * The stub binder used to create bindings.
    +   * Sets the first argument of handleMockCall_ to the function name.
    +   * @type {!Function}
    +   * @private
    +   */
    +  this.functionStubBinder_ = this.useMockedFunctionName_(this.handleMockCall_);
    +
    +  this.mockedItem = this.useMockedFunctionName_(this.executeStub);
    +  this.mockedItem.$stubBinder = this.functionStubBinder_;
    +
    +  /**
    +   * The call verifier is used to verify function invocations.
    +   * Sets the first argument of verifyInvocation to the function name.
    +   * @type {!Function}
    +   */
    +  this.mockedItem.$callVerifier =
    +      this.useMockedFunctionName_(this.verifyInvocation);
    +};
    +goog.inherits(goog.labs.mock.MockFunctionManager_,
    +              goog.labs.mock.MockManager_);
    +
    +
    +/**
    + * Given a method, returns a new function that calls the first one setting
    + * the first argument to the mocked function name.
    + * This is used to dynamically override the stub binders and call verifiers.
    + * @private
    + * @param {Function} nextFunc The function to override.
    + * @return {!Function} The overloaded function.
    + */
    +goog.labs.mock.MockFunctionManager_.prototype.useMockedFunctionName_ =
    +    function(nextFunc) {
    +  return goog.bind(function(var_args) {
    +    var args = goog.array.slice(arguments, 0);
    +    var name =
    +        '#mockFor<' + goog.labs.mock.getFunctionName_(this.func_) + '>';
    +    goog.array.insertAt(args, name, 0);
    +    return nextFunc.apply(this, args);
    +  }, this);
    +};
    +
    +
    +
    +/**
    + * The stub binder is the object that helps define the stubs by binding
    + * method name to the stub method.
    + *
    + * @param {!goog.labs.mock.MockManager_}
    + *   mockManager The mock manager.
    + * @param {?string} name The method name.
    + * @param {!Array<?>} args The other arguments to the method.
    + *
    + * @constructor
    + * @struct
    + * @private
    + */
    +goog.labs.mock.StubBinder_ = function(mockManager, name, args) {
    +  /**
    +   * The mock manager instance.
    +   * @type {!goog.labs.mock.MockManager_}
    +   * @private
    +   */
    +  this.mockManager_ = mockManager;
    +
    +  /**
    +   * Holds the name of the method to be bound.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.name_ = name;
    +
    +  /**
    +   * Holds the arguments for the method.
    +   * @type {!Array<?>}
    +   * @private
    +   */
    +  this.args_ = args;
    +};
    +
    +
    +/**
    + * Defines the stub to be called for the method name and arguments bound
    + * earlier.
    + * TODO(user): Add support for the 'Answer' interface.
    + *
    + * @param {!Function} func The stub.
    + */
    +goog.labs.mock.StubBinder_.prototype.then = function(func) {
    +  this.mockManager_.addBinding(this.name_, this.args_, func);
    +};
    +
    +
    +/**
    + * Defines the stub to return a specific value for a method name and arguments.
    + *
    + * @param {*} value The value to return.
    + */
    +goog.labs.mock.StubBinder_.prototype.thenReturn = function(value) {
    +  this.mockManager_.addBinding(this.name_, this.args_,
    +                               goog.functions.constant(value));
    +};
    +
    +
    +/**
    + * Facilitates (and is the first step in) setting up stubs. Obtains an object
    + * on which, the method to be mocked is called to create a stub. Sample usage:
    + *
    + * var mockObj = goog.labs.mock.mock(objectBeingMocked);
    + * goog.labs.mock.when(mockObj).getFoo(3).thenReturn(4);
    + *
    + * @param {!Object} mockObject The mocked object.
    + * @return {!goog.labs.mock.StubBinder_} The property binder.
    + */
    +goog.labs.mock.when = function(mockObject) {
    +  goog.asserts.assert(mockObject.$stubBinder, 'Stub binder cannot be null!');
    +  return mockObject.$stubBinder;
    +};
    +
    +
    +
    +/**
    + * Represents a binding between a method name, args and a stub.
    + *
    + * @param {?string} methodName The name of the method being stubbed.
    + * @param {!Array<?>} args The arguments passed to the method.
    + * @param {!Function} stub The stub function to be called for the given method.
    + * @constructor
    + * @struct
    + * @private
    + */
    +goog.labs.mock.MethodBinding_ = function(methodName, args, stub) {
    +  /**
    +   * The name of the method being stubbed.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.methodName_ = methodName;
    +
    +  /**
    +   * The arguments for the method being stubbed.
    +   * @type {!Array<?>}
    +   * @private
    +   */
    +  this.args_ = args;
    +
    +  /**
    +   * The stub function.
    +   * @type {!Function}
    +   * @private
    +   */
    +  this.stub_ = stub;
    +};
    +
    +
    +/**
    + * @return {!Function} The stub to be executed.
    + */
    +goog.labs.mock.MethodBinding_.prototype.getStub = function() {
    +  return this.stub_;
    +};
    +
    +
    +/**
    + * @override
    + * @return {string} A readable string representation of the binding
    + *  as a method call.
    + */
    +goog.labs.mock.MethodBinding_.prototype.toString = function() {
    +  return goog.labs.mock.formatMethodCall_(this.methodName_ || '', this.args_);
    +};
    +
    +
    +/**
    + * @return {string} The method name for this binding.
    + */
    +goog.labs.mock.MethodBinding_.prototype.getMethodName = function() {
    +  return this.methodName_ || '';
    +};
    +
    +
    +/**
    + * Determines whether the given args match the stored args_. Used to determine
    + * which stub to invoke for a method.
    + *
    + * @param {string} methodName The name of the method being stubbed.
    + * @param {!Array<?>} args An array of arguments.
    + * @param {boolean} isVerification Whether this is a function verification call
    + *     or not.
    + * @return {boolean} If it matches the stored arguments.
    + */
    +goog.labs.mock.MethodBinding_.prototype.matches = function(
    +    methodName, args, isVerification) {
    +  var specs = isVerification ? args : this.args_;
    +  var calls = isVerification ? this.args_ : args;
    +
    +  //TODO(user): More elaborate argument matching. Think about matching
    +  //    objects.
    +  return this.methodName_ == methodName &&
    +      goog.array.equals(calls, specs, function(arg, spec) {
    +        // Duck-type to see if this is an object that implements the
    +        // goog.labs.testing.Matcher interface.
    +        if (goog.isFunction(spec.matches)) {
    +          return spec.matches(arg);
    +        } else {
    +          return goog.array.defaultCompareEquality(spec, arg);
    +        }
    +      });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.html b/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.html
    new file mode 100644
    index 0000000000..aa39e5ada0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.labs.mock
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.mockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.js b/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.js
    new file mode 100644
    index 0000000000..7d57038d8a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/mock/mock_test.js
    @@ -0,0 +1,517 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.mockTest');
    +goog.setTestOnly('goog.labs.mockTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.mock');
    +goog.require('goog.labs.mock.VerificationError');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.AnythingMatcher');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.GreaterThanMatcher');
    +goog.require('goog.string');
    +goog.require('goog.testing.jsunit');
    +
    +var ParentClass = function() {};
    +ParentClass.prototype.method1 = function() {};
    +ParentClass.prototype.x = 1;
    +ParentClass.prototype.val = 0;
    +ParentClass.prototype.incrementVal = function() { this.val++; };
    +
    +var ChildClass = function() {};
    +goog.inherits(ChildClass, ParentClass);
    +ChildClass.prototype.method2 = function() {};
    +ChildClass.prototype.y = 2;
    +
    +function testParentClass() {
    +  var parentMock = goog.labs.mock.mock(ParentClass);
    +
    +  assertNotUndefined(parentMock.method1);
    +  assertUndefined(parentMock.method1());
    +  assertUndefined(parentMock.method2);
    +  assertNotUndefined(parentMock.x);
    +  assertUndefined(parentMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      parentMock instanceof ParentClass);
    +}
    +
    +function testChildClass() {
    +  var childMock = goog.labs.mock.mock(ChildClass);
    +
    +  assertNotUndefined(childMock.method1);
    +  assertUndefined(childMock.method1());
    +  assertNotUndefined(childMock.method2);
    +  assertUndefined(childMock.method2());
    +  assertNotUndefined(childMock.x);
    +  assertNotUndefined(childMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      childMock instanceof ChildClass);
    +}
    +
    +function testParentClassInstance() {
    +  var parentMock = goog.labs.mock.mock(new ParentClass());
    +
    +  assertNotUndefined(parentMock.method1);
    +  assertUndefined(parentMock.method1());
    +  assertUndefined(parentMock.method2);
    +  assertNotUndefined(parentMock.x);
    +  assertUndefined(parentMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      parentMock instanceof ParentClass);
    +}
    +
    +function testChildClassInstance() {
    +  var childMock = goog.labs.mock.mock(new ChildClass());
    +
    +  assertNotUndefined(childMock.method1);
    +  assertUndefined(childMock.method1());
    +  assertNotUndefined(childMock.method2);
    +  assertUndefined(childMock.method2());
    +  assertNotUndefined(childMock.x);
    +  assertNotUndefined(childMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      childMock instanceof ParentClass);
    +}
    +
    +function testNonEnumerableProperties() {
    +  var mockObject = goog.labs.mock.mock({});
    +  assertNotUndefined(mockObject.toString);
    +  goog.labs.mock.when(mockObject).toString().then(function() {
    +    return 'toString';
    +  });
    +  assertEquals('toString', mockObject.toString());
    +}
    +
    +function testBasicStubbing() {
    +  var obj = {
    +    method1: function(i) {
    +      return 2 * i;
    +    },
    +    method2: function(i, str) {
    +      return str;
    +    },
    +    method3: function(x) {
    +      return x;
    +    }
    +  };
    +
    +  var mockObj = goog.labs.mock.mock(obj);
    +  goog.labs.mock.when(mockObj).method1(2).then(function(i) {return i;});
    +
    +  assertEquals(4, obj.method1(2));
    +  assertEquals(2, mockObj.method1(2));
    +  assertUndefined(mockObj.method1(4));
    +
    +  goog.labs.mock.when(mockObj).method2(1, 'hi').then(function(i) {return 'oh'});
    +  assertEquals('hi', obj.method2(1, 'hi'));
    +  assertEquals('oh', mockObj.method2(1, 'hi'));
    +  assertUndefined(mockObj.method2(3, 'foo'));
    +
    +  goog.labs.mock.when(mockObj).method3(4).thenReturn(10);
    +  assertEquals(4, obj.method3(4));
    +  assertEquals(10, mockObj.method3(4));
    +  goog.labs.mock.verify(mockObj).method3(4);
    +  assertUndefined(mockObj.method3(5));
    +}
    +
    +function testMockFunctions() {
    +  function x(i) { return i; }
    +
    +  var mockedFunc = goog.labs.mock.mockFunction(x);
    +  goog.labs.mock.when(mockedFunc)(100).thenReturn(10);
    +  goog.labs.mock.when(mockedFunc)(50).thenReturn(25);
    +
    +  assertEquals(100, x(100));
    +  assertEquals(10, mockedFunc(100));
    +  assertEquals(25, mockedFunc(50));
    +}
    +
    +function testStubbingConsecutiveCalls() {
    +  var obj = {
    +    method: function(i) {
    +      return i * 42;
    +    }
    +  };
    +
    +  var mockObj = goog.labs.mock.mock(obj);
    +  goog.labs.mock.when(mockObj).method(1).thenReturn(3);
    +  goog.labs.mock.when(mockObj).method(1).thenReturn(4);
    +
    +  assertEquals(42, obj.method(1));
    +  assertEquals(3, mockObj.method(1));
    +  assertEquals(4, mockObj.method(1));
    +  assertEquals(4, mockObj.method(1));
    +
    +  var x = function(i) { return i; };
    +  var mockedFunc = goog.labs.mock.mockFunction(x);
    +  goog.labs.mock.when(mockedFunc)(100).thenReturn(10);
    +  goog.labs.mock.when(mockedFunc)(100).thenReturn(25);
    +
    +  assertEquals(100, x(100));
    +  assertEquals(10, mockedFunc(100));
    +  assertEquals(25, mockedFunc(100));
    +  assertEquals(25, mockedFunc(100));
    +}
    +
    +function testSpying() {
    +  var obj = {
    +    method1: function(i) {
    +      return 2 * i;
    +    },
    +    method2: function(i) {
    +      return 5 * i;
    +    }
    +  };
    +
    +  var spyObj = goog.labs.mock.spy(obj);
    +  goog.labs.mock.when(spyObj).method1(2).thenReturn(5);
    +
    +  assertEquals(2, obj.method1(1));
    +  assertEquals(5, spyObj.method1(2));
    +  goog.labs.mock.verify(spyObj).method1(2);
    +  assertEquals(2, spyObj.method1(1));
    +  goog.labs.mock.verify(spyObj).method1(1);
    +  assertEquals(20, spyObj.method2(4));
    +  goog.labs.mock.verify(spyObj).method2(4);
    +}
    +
    +function testSpyParentClassInstance() {
    +  var parent = new ParentClass();
    +  var parentMock = goog.labs.mock.spy(parent);
    +
    +  assertNotUndefined(parentMock.method1);
    +  assertUndefined(parentMock.method1());
    +  assertUndefined(parentMock.method2);
    +  assertNotUndefined(parentMock.x);
    +  assertUndefined(parentMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      parentMock instanceof ParentClass);
    +  var incrementedOrigVal = parent.val + 1;
    +  parentMock.incrementVal();
    +  assertEquals('Changes in the spied object should reflect in the spy.',
    +      incrementedOrigVal, parentMock.val);
    +}
    +
    +function testSpyChildClassInstance() {
    +  var child = new ChildClass();
    +  var childMock = goog.labs.mock.spy(child);
    +
    +  assertNotUndefined(childMock.method1);
    +  assertUndefined(childMock.method1());
    +  assertNotUndefined(childMock.method2);
    +  assertUndefined(childMock.method2());
    +  assertNotUndefined(childMock.x);
    +  assertNotUndefined(childMock.y);
    +  assertTrue('Mock should be an instance of the mocked class.',
    +      childMock instanceof ParentClass);
    +  var incrementedOrigVal = child.val + 1;
    +  childMock.incrementVal();
    +  assertEquals('Changes in the spied object should reflect in the spy.',
    +      incrementedOrigVal, childMock.val);
    +}
    +
    +function testVerifyForObjects() {
    +  var obj = {
    +    method1: function(i) {
    +      return 2 * i;
    +    },
    +    method2: function(i) {
    +      return 5 * i;
    +    }
    +  };
    +
    +  var mockObj = goog.labs.mock.mock(obj);
    +  goog.labs.mock.when(mockObj).method1(2).thenReturn(5);
    +
    +  assertEquals(5, mockObj.method1(2));
    +  goog.labs.mock.verify(mockObj).method1(2);
    +  var e = assertThrows(goog.bind(goog.labs.mock.verify(mockObj).method1, 2));
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +}
    +
    +function testVerifyForFunctions() {
    +  var func = function(i) {
    +    return i;
    +  };
    +
    +  var mockFunc = goog.labs.mock.mockFunction(func);
    +  goog.labs.mock.when(mockFunc)(2).thenReturn(55);
    +  assertEquals(55, mockFunc(2));
    +  goog.labs.mock.verify(mockFunc)(2);
    +  goog.labs.mock.verify(mockFunc)(lessThan(3));
    +
    +  var e = assertThrows(goog.bind(goog.labs.mock.verify(mockFunc), 3));
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +}
    +
    +
    +/**
    +* When a function invocation verification fails, it should show the failed
    +* expectation call, as well as the recorded calls to the same method.
    +*/
    +function testVerificationErrorMessages() {
    +  var mock = goog.labs.mock.mock({
    +    method: function(i) {
    +      return i;
    +    }
    +  });
    +
    +  // Failure when there are no recorded calls.
    +  var e = assertThrows(function() { goog.labs.mock.verify(mock).method(4); });
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +  var expected = '\nExpected: method(4)\n' +
    +      'Recorded: No recorded calls';
    +  assertEquals(expected, e.message);
    +
    +
    +  // Failure when there are recorded calls with ints and functions
    +  // as arguments.
    +  var callback = function() {};
    +  var callbackId = goog.labs.mock.getUid(callback);
    +
    +  mock.method(1);
    +  mock.method(2);
    +  mock.method(callback);
    +
    +  e = assertThrows(function() { goog.labs.mock.verify(mock).method(3); });
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +
    +  expected = '\nExpected: method(3)\n' +
    +      'Recorded: method(1),\n' +
    +      '          method(2),\n' +
    +      '          method(<function #anonymous' + callbackId + '>)';
    +  assertEquals(expected, e.message);
    +
    +  // With mockFunctions
    +  var mockCallback = goog.labs.mock.mockFunction(callback);
    +  e = assertThrows(function() { goog.labs.mock.verify(mockCallback)(5);});
    +  expected = '\nExpected: #mockFor<#anonymous' + callbackId + '>(5)\n' +
    +      'Recorded: No recorded calls';
    +
    +  mockCallback(8);
    +  goog.labs.mock.verify(mockCallback)(8);
    +  assertEquals(expected, e.message);
    +
    +  // Objects with circular references should not fail.
    +  var obj = {x: 1};
    +  obj.y = obj;
    +
    +  mockCallback(obj);
    +  e = assertThrows(function() { goog.labs.mock.verify(mockCallback)(5);});
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +
    +  // Should respect string representation of different custom classes.
    +  var myClass = function() {};
    +  myClass.prototype.toString = function() { return '<superClass>'; };
    +
    +  var mockFunction = goog.labs.mock.mockFunction(function f() {});
    +  mockFunction(new myClass());
    +
    +  e = assertThrows(function() { goog.labs.mock.verify(mockFunction)(5);});
    +  expected = '\nExpected: #mockFor<f>(5)\n' +
    +      'Recorded: #mockFor<f>(<superClass>)';
    +  assertEquals(expected, e.message);
    +}
    +
    +
    +/**
    +* Asserts that the given string contains a list of others strings
    +* in the given order.
    +*/
    +function assertContainsInOrder(str, var_args) {
    +  var expected = goog.array.splice(arguments, 1);
    +  var indices = goog.array.map(expected, function(val) {
    +    return str.indexOf(val);
    +  });
    +
    +  for (var i = 0; i < expected.length; i++) {
    +    var msg = 'Missing "' + expected[i] + '" from "' + str + '"';
    +    assertTrue(msg, indices[i] != -1);
    +
    +    if (i > 0) {
    +      msg = '"' + expected[i - 1] + '" should come before "' + expected[i] +
    +          '" in "' + str + '"';
    +      assertTrue(msg, indices[i] > indices[i - 1]);
    +    }
    +  }
    +}
    +
    +function testMatchers() {
    +  var obj = {
    +    method1: function(i) {
    +      return 2 * i;
    +    },
    +    method2: function(i) {
    +      return 5 * i;
    +    }
    +  };
    +
    +  var mockObj = goog.labs.mock.mock(obj);
    +
    +  goog.labs.mock.when(mockObj).method1(greaterThan(4)).thenReturn(100);
    +  goog.labs.mock.when(mockObj).method1(lessThan(4)).thenReturn(40);
    +
    +  assertEquals(100, mockObj.method1(5));
    +  assertEquals(100, mockObj.method1(6));
    +  assertEquals(40, mockObj.method1(2));
    +  assertEquals(40, mockObj.method1(1));
    +  assertUndefined(mockObj.method1(4));
    +}
    +
    +function testMatcherVerify() {
    +  var obj = {
    +    method: function(i) {
    +      return 2 * i;
    +    }
    +  };
    +
    +  // Using spy objects.
    +  var spy = goog.labs.mock.spy(obj);
    +
    +  spy.method(6);
    +
    +  goog.labs.mock.verify(spy).method(greaterThan(4));
    +  var e = assertThrows(
    +      goog.bind(goog.labs.mock.verify(spy).method, lessThan(4)));
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +
    +  // Using mocks
    +  var mockObj = goog.labs.mock.mock(obj);
    +
    +  mockObj.method(8);
    +
    +  goog.labs.mock.verify(mockObj).method(greaterThan(7));
    +  var e = assertThrows(
    +      goog.bind(goog.labs.mock.verify(mockObj).method, lessThan(7)));
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +}
    +
    +function testMatcherVerifyCollision() {
    +  var obj = {
    +    method: function(i) {
    +      return 2 * i;
    +    }
    +  };
    +  var mockObj = goog.labs.mock.mock(obj);
    +
    +  goog.labs.mock.when(mockObj).method(5).thenReturn(100);
    +  assertNotEquals(100, mockObj.method(greaterThan(2)));
    +}
    +
    +function testMatcherVerifyCollisionBetweenMatchers() {
    +  var obj = {
    +    method: function(i) {
    +      return 2 * i;
    +    }
    +  };
    +  var mockObj = goog.labs.mock.mock(obj);
    +
    +  goog.labs.mock.when(mockObj).method(anything()).thenReturn(100);
    +
    +  var e = assertThrows(
    +      goog.bind(goog.labs.mock.verify(mockObj).method, anything()));
    +  assertTrue(e instanceof goog.labs.mock.VerificationError);
    +}
    +
    +function testVerifyForUnmockedMethods() {
    +  var Task = function() {};
    +  Task.prototype.run = function() {};
    +
    +  var mockTask = goog.labs.mock.mock(Task);
    +  mockTask.run();
    +
    +  goog.labs.mock.verify(mockTask).run();
    +}
    +
    +function testFormatMethodCall() {
    +  var formatMethodCall = goog.labs.mock.formatMethodCall_;
    +  assertEquals('alert()', formatMethodCall('alert'));
    +  assertEquals('sum(2, 4)', formatMethodCall('sum', [2, 4]));
    +  assertEquals('sum("2", "4")', formatMethodCall('sum', ['2', '4']));
    +  assertEquals('call(<function unicorn>)',
    +      formatMethodCall('call', [function unicorn() {}]));
    +
    +  var arg = {x: 1, y: {hello: 'world'}};
    +  assertEquals('call(' + goog.labs.mock.formatValue_(arg) + ')',
    +      formatMethodCall('call', [arg]));
    +}
    +
    +function testGetFunctionName() {
    +  var f1 = function() {};
    +  var f2 = function() {};
    +  var named = function myName() {};
    +
    +  assert(goog.string.startsWith(
    +      goog.labs.mock.getFunctionName_(f1), '#anonymous'));
    +  assert(goog.string.startsWith(
    +      goog.labs.mock.getFunctionName_(f2), '#anonymous'));
    +  assertNotEquals(
    +      goog.labs.mock.getFunctionName_(f1), goog.labs.mock.getFunctionName_(f2));
    +  assertEquals('myName', goog.labs.mock.getFunctionName_(named));
    +}
    +
    +function testFormatObject() {
    +  var obj, obj2, obj3;
    +
    +  obj = {x: 1};
    +  assertEquals(
    +      '{"x":1 _id:' + goog.labs.mock.getUid(obj) + '}',
    +      goog.labs.mock.formatValue_(obj)
    +  );
    +  assertEquals('{"x":1}', goog.labs.mock.formatValue_(obj, false /* id */));
    +
    +  obj = {x: 'hello'};
    +  assertEquals(
    +      '{"x":"hello" _id:' + goog.labs.mock.getUid(obj) + '}',
    +      goog.labs.mock.formatValue_(obj)
    +  );
    +  assertEquals('{"x":"hello"}',
    +      goog.labs.mock.formatValue_(obj, false /* id */));
    +
    +  obj3 = {};
    +  obj2 = {y: obj3};
    +  obj3.x = obj2;
    +  assertEquals(
    +      '{"x":{"y":<recursive/dupe obj_' + goog.labs.mock.getUid(obj3) + '> ' +
    +      '_id:' + goog.labs.mock.getUid(obj2) + '} ' +
    +      '_id:' + goog.labs.mock.getUid(obj3) + '}',
    +      goog.labs.mock.formatValue_(obj3)
    +  );
    +  assertEquals('{"x":{"y":<recursive/dupe>}}',
    +      goog.labs.mock.formatValue_(obj3, false /* id */)
    +  );
    +
    +
    +  obj = {x: function y() {} };
    +  assertEquals('{"x":<function y> _id:' + goog.labs.mock.getUid(obj) + '}',
    +      goog.labs.mock.formatValue_(obj));
    +  assertEquals('{"x":<function y>}',
    +      goog.labs.mock.formatValue_(obj, false /* id */));
    +
    +}
    +
    +function testGetUid() {
    +  var obj1 = {};
    +  var obj2 = {};
    +  var func1 = function() {};
    +  var func2 = function() {};
    +
    +  assertNotEquals(goog.labs.mock.getUid(obj1), goog.labs.mock.getUid(obj2));
    +  assertNotEquals(goog.labs.mock.getUid(func1), goog.labs.mock.getUid(func2));
    +  assertNotEquals(goog.labs.mock.getUid(obj1), goog.labs.mock.getUid(func2));
    +  assertEquals(goog.labs.mock.getUid(obj1), goog.labs.mock.getUid(obj1));
    +  assertEquals(goog.labs.mock.getUid(func1), goog.labs.mock.getUid(func1));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/image.js b/mapbox-gl/closure-library/closure/goog/labs/net/image.js
    new file mode 100644
    index 0000000000..e9e34fe3e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/image.js
    @@ -0,0 +1,94 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Simple image loader, used for preloading.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.labs.net.image');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.EventType');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Loads a single image.  Useful for preloading images.
    + *
    + * @param {string} uri URI of the image.
    + * @param {(!Image|function(): !Image)=} opt_image If present, instead of
    + *     creating a new Image instance the function will use the passed Image
    + *     instance or the result of calling the Image factory respectively. This
    + *     can be used to control exactly how Image instances are created, for
    + *     example if they should be created in a particular document element, or
    + *     have fields that will trigger CORS image fetches.
    + * @return {!goog.Promise<!Image>} A Promise that will be resolved with the
    + *     given image if the image successfully loads.
    + */
    +goog.labs.net.image.load = function(uri, opt_image) {
    +  return new goog.Promise(function(resolve, reject) {
    +    var image;
    +    if (!goog.isDef(opt_image)) {
    +      image = new Image();
    +    } else if (goog.isFunction(opt_image)) {
    +      image = opt_image();
    +    } else {
    +      image = opt_image;
    +    }
    +
    +    // IE's load event on images can be buggy.  For older browsers, wait for
    +    // readystatechange events and check if readyState is 'complete'.
    +    // See:
    +    // http://msdn.microsoft.com/en-us/library/ie/ms536957(v=vs.85).aspx
    +    // http://msdn.microsoft.com/en-us/library/ie/ms534359(v=vs.85).aspx
    +    //
    +    // Starting with IE11, start using standard 'load' events.
    +    // See:
    +    // http://msdn.microsoft.com/en-us/library/ie/dn467845(v=vs.85).aspx
    +    var loadEvent = (goog.userAgent.IE && goog.userAgent.VERSION < 11) ?
    +        goog.net.EventType.READY_STATE_CHANGE : goog.events.EventType.LOAD;
    +
    +    var handler = new goog.events.EventHandler();
    +    handler.listen(
    +        image,
    +        [loadEvent, goog.net.EventType.ABORT, goog.net.EventType.ERROR],
    +        function(e) {
    +
    +          // We only registered listeners for READY_STATE_CHANGE for IE.
    +          // If readyState is now COMPLETE, the image has loaded.
    +          // See related comment above.
    +          if (e.type == goog.net.EventType.READY_STATE_CHANGE &&
    +              image.readyState != goog.net.EventType.COMPLETE) {
    +            return;
    +          }
    +
    +          // At this point, we know whether the image load was successful
    +          // and no longer care about image events.
    +          goog.dispose(handler);
    +
    +          // Whether the image successfully loaded.
    +          if (e.type == loadEvent) {
    +            resolve(image);
    +          } else {
    +            reject(null);
    +          }
    +        });
    +
    +    // Initiate the image request.
    +    image.src = uri;
    +  });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/image_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/image_test.html
    new file mode 100644
    index 0000000000..32d529af93
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/image_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: nnaze@google.com (Nathan Naze)
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.labs.net.image</title>
    +<script src="../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.imageTest');
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/image_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/image_test.js
    new file mode 100644
    index 0000000000..c973b5bfbd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/image_test.js
    @@ -0,0 +1,87 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.net.Image.
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.labs.net.imageTest');
    +
    +goog.require('goog.labs.net.image');
    +goog.require('goog.string');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +goog.setTestOnly('goog.labs.net.ImageTest');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +function testValidImage() {
    +  var url = 'testdata/cleardot.gif';
    +
    +  asyncTestCase.waitForAsync('image load');
    +
    +  goog.labs.net.image.load(url).then(function(value) {
    +    assertEquals('IMG', value.tagName);
    +    assertTrue(goog.string.endsWith(value.src, url));
    +    asyncTestCase.continueTesting();
    +  });
    +}
    +
    +function testInvalidImage() {
    +
    +  var url = 'testdata/invalid.gif'; // This file does not exist.
    +
    +  asyncTestCase.waitForAsync('image load');
    +
    +  goog.labs.net.image.load(url).then(
    +      fail /* opt_onResolved */,
    +      function() {
    +        asyncTestCase.continueTesting();
    +      });
    +}
    +
    +function testImageFactory() {
    +  var returnedImage = new Image();
    +  var factory = function() {
    +    return returnedImage;
    +  };
    +  var countedFactory = goog.testing.recordFunction(factory);
    +
    +  var url = 'testdata/cleardot.gif';
    +
    +  asyncTestCase.waitForAsync('image load');
    +  goog.labs.net.image.load(url, countedFactory).then(function(value) {
    +    assertEquals(returnedImage, value);
    +    assertEquals(1, countedFactory.getCallCount());
    +    asyncTestCase.continueTesting();
    +  });
    +}
    +
    +function testExistingImage() {
    +  var image = new Image();
    +
    +  var url = 'testdata/cleardot.gif';
    +
    +  asyncTestCase.waitForAsync('image load');
    +  goog.labs.net.image.load(url, image).then(function(value) {
    +    assertEquals(image, value);
    +    asyncTestCase.continueTesting();
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/testdata/cleardot.gif b/mapbox-gl/closure-library/closure/goog/labs/net/testdata/cleardot.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf
    GIT binary patch
    literal 43
    scmZ?wbhEHbWMp7uX!y@?;J^U}1_s5SEQ~;kK?g*DWEhy3To@Uw0n;G|I{*Lx
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data b/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data
    new file mode 100644
    index 0000000000..44afb24def
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data
    @@ -0,0 +1,2 @@
    +while(1);
    +{"stat":"ok","count":12345}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data b/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data
    new file mode 100644
    index 0000000000..b7f2f0e907
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data
    @@ -0,0 +1 @@
    +Just some data.
    \ No newline at end of file
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel.js
    new file mode 100644
    index 0000000000..55341b26a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel.js
    @@ -0,0 +1,302 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The API spec for the WebChannel messaging library.
    + *
    + * Similar to HTML5 WebSocket and Closure BrowserChannel, WebChannel
    + * offers an abstraction for point-to-point socket-like communication between
    + * a browser client and a remote origin.
    + *
    + * WebChannels are created via <code>WebChannel</code>. Multiple WebChannels
    + * may be multiplexed over the same WebChannelTransport, which represents
    + * the underlying physical connectivity over standard wire protocols
    + * such as HTTP and SPDY.
    + *
    + * A WebChannels in turn represents a logical communication channel between
    + * the client and server end point. A WebChannel remains open for
    + * as long as the client or server end-point allows.
    + *
    + * Messages may be delivered in-order or out-of-order, reliably or unreliably
    + * over the same WebChannel. Message delivery guarantees of a WebChannel is
    + * to be specified by the application code; and the choice of the
    + * underlying wire protocols is completely transparent to the API users.
    + *
    + * Client-to-client messaging via WebRTC based transport may also be support
    + * via the same WebChannel API in future.
    + *
    + * Note that we have no immediate plan to move this API out of labs. While
    + * the implementation is production ready, the API is subject to change
    + * (addition):
    + * 1. Completely new W3C APIs for Web messaging may emerge in near future.
    + * 2. New programming models for cloud (on the server-side) may require
    + *    new APIs to be defined.
    + * 3. WebRTC DataChannel alignment
    + * Lastly, we also want to white-list all internal use cases. As a general rule,
    + * we expect most applications to rely on stateless/RPC services.
    + *
    + */
    +
    +goog.provide('goog.net.WebChannel');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +
    +
    +
    +/**
    + * A WebChannel represents a logical bi-directional channel over which the
    + * client communicates with a remote server that holds the other endpoint
    + * of the channel. A WebChannel is always created in the context of a shared
    + * {@link WebChannelTransport} instance. It is up to the underlying client-side
    + * and server-side implementations to decide how or when multiplexing is
    + * to be enabled.
    + *
    + * @interface
    + * @extends {EventTarget}
    + */
    +goog.net.WebChannel = function() {};
    +
    +
    +/**
    + * Configuration spec for newly created WebChannel instances.
    + *
    + * WebChannels are configured in the context of the containing
    + * {@link WebChannelTransport}. The configuration parameters are specified
    + * when a new instance of WebChannel is created via {@link WebChannelTransport}.
    + *
    + * messageHeaders: custom headers to be added to every message sent to the
    + * server.
    + *
    + * messageUrlParams: custom url query parameters to be added to every message
    + * sent to the server.
    + *
    + * clientProtocolHeaderRequired: whether a special header should be added to
    + * each message so that the server can dispatch webchannel messages without
    + * knowing the URL path prefix. Defaults to false.
    + *
    + * concurrentRequestLimit: the maximum number of in-flight HTTP requests allowed
    + * when SPDY is enabled. Currently we only detect SPDY in Chrome.
    + * This parameter defaults to 10. When SPDY is not enabled, this parameter
    + * will have no effect.
    + *
    + * supportsCrossDomainXhr: setting this to true to allow the use of sub-domains
    + * (as configured by the server) to send XHRs with the CORS withCredentials
    + * bit set to true.
    + *
    + * testUrl: the test URL for detecting connectivity during the initial
    + * handshake. This parameter defaults to "/<channel_url>/test".
    + *
    + *
    + * @typedef {{
    + *   messageHeaders: (!Object<string, string>|undefined),
    + *   messageUrlParams: (!Object<string, string>|undefined),
    + *   clientProtocolHeaderRequired: (boolean|undefined),
    + *   concurrentRequestLimit: (number|undefined),
    + *   supportsCrossDomainXhr: (boolean|undefined),
    + *   testUrl: (string|undefined)
    + * }}
    + */
    +goog.net.WebChannel.Options;
    +
    +
    +/**
    + * Types that are allowed as message data.
    + *
    + * @typedef {(ArrayBuffer|Blob|Object<string, string>|Array)}
    + */
    +goog.net.WebChannel.MessageData;
    +
    +
    +/**
    + * Open the WebChannel against the URI specified in the constructor.
    + */
    +goog.net.WebChannel.prototype.open = goog.abstractMethod;
    +
    +
    +/**
    + * Close the WebChannel.
    + */
    +goog.net.WebChannel.prototype.close = goog.abstractMethod;
    +
    +
    +/**
    + * Sends a message to the server that maintains the other end point of
    + * the WebChannel.
    + *
    + * @param {!goog.net.WebChannel.MessageData} message The message to send.
    + */
    +goog.net.WebChannel.prototype.send = goog.abstractMethod;
    +
    +
    +/**
    + * Common events fired by WebChannels.
    + * @enum {string}
    + */
    +goog.net.WebChannel.EventType = {
    +  /** Dispatched when the channel is opened. */
    +  OPEN: goog.events.getUniqueId('open'),
    +
    +  /** Dispatched when the channel is closed. */
    +  CLOSE: goog.events.getUniqueId('close'),
    +
    +  /** Dispatched when the channel is aborted due to errors. */
    +  ERROR: goog.events.getUniqueId('error'),
    +
    +  /** Dispatched when the channel has received a new message. */
    +  MESSAGE: goog.events.getUniqueId('message')
    +};
    +
    +
    +
    +/**
    + * The event interface for the MESSAGE event.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.net.WebChannel.MessageEvent = function() {
    +  goog.net.WebChannel.MessageEvent.base(
    +      this, 'constructor', goog.net.WebChannel.EventType.MESSAGE);
    +};
    +goog.inherits(goog.net.WebChannel.MessageEvent, goog.events.Event);
    +
    +
    +/**
    + * The content of the message received from the server.
    + *
    + * @type {!goog.net.WebChannel.MessageData}
    + */
    +goog.net.WebChannel.MessageEvent.prototype.data;
    +
    +
    +/**
    + * WebChannel level error conditions.
    + * @enum {number}
    + */
    +goog.net.WebChannel.ErrorStatus = {
    +  /** No error has occurred. */
    +  OK: 0,
    +
    +  /** Communication to the server has failed. */
    +  NETWORK_ERROR: 1,
    +
    +  /** The server fails to accept the WebChannel. */
    +  SERVER_ERROR: 2
    +};
    +
    +
    +
    +/**
    + * The event interface for the ERROR event.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.net.WebChannel.ErrorEvent = function() {
    +  goog.net.WebChannel.ErrorEvent.base(
    +      this, 'constructor', goog.net.WebChannel.EventType.ERROR);
    +};
    +goog.inherits(goog.net.WebChannel.ErrorEvent, goog.events.Event);
    +
    +
    +/**
    + * The error status.
    + *
    + * @type {!goog.net.WebChannel.ErrorStatus}
    + */
    +goog.net.WebChannel.ErrorEvent.prototype.status;
    +
    +
    +/**
    + * @return {!goog.net.WebChannel.RuntimeProperties} The runtime properties
    + * of the WebChannel instance.
    + */
    +goog.net.WebChannel.prototype.getRuntimeProperties = goog.abstractMethod;
    +
    +
    +
    +/**
    + * The readonly runtime properties of the WebChannel instance.
    + *
    + * This class is defined for debugging and monitoring purposes, and for
    + * optimization functions that the application may choose to manage by itself.
    + *
    + * @interface
    + */
    +goog.net.WebChannel.RuntimeProperties = function() {};
    +
    +
    +/**
    + * @return {number} The effective limit for the number of concurrent HTTP
    + * requests that are allowed to be made for sending messages from the client
    + * to the server. When SPDY is not enabled, this limit will be one.
    + */
    +goog.net.WebChannel.RuntimeProperties.prototype.getConcurrentRequestLimit =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * For applications that need support multiple channels (e.g. from
    + * different tabs) to the same origin, use this method to decide if SPDY is
    + * enabled and therefore it is safe to open multiple channels.
    + *
    + * If SPDY is disabled, the application may choose to limit the number of active
    + * channels to one or use other means such as sub-domains to work around
    + * the browser connection limit.
    + *
    + * @return {boolean} Whether SPDY is enabled for the origin against which
    + * the channel is created.
    + */
    +goog.net.WebChannel.RuntimeProperties.prototype.isSpdyEnabled =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * This method may be used by the application to stop ack of received messages
    + * as a means of enabling or disabling flow-control on the server-side.
    + *
    + * @param {boolean} enabled If true, enable flow-control behavior on the
    + * server side. Setting it to false will cancel ay previous enabling action.
    + */
    +goog.net.WebChannel.RuntimeProperties.prototype.setServerFlowControl =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * This method may be used by the application to throttle the rate of outgoing
    + * messages, as a means of sender initiated flow-control.
    + *
    + * @return {number} The total number of messages that have not received
    + * ack from the server and therefore remain in the buffer.
    + */
    +goog.net.WebChannel.RuntimeProperties.prototype.getNonAckedMessageCount =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * A special header to indicate to the server what messaging protocol
    + * each HTTP message is speaking.
    + *
    + * @type {string}
    + */
    +goog.net.WebChannel.X_CLIENT_PROTOCOL = 'X-Client-Protocol';
    +
    +
    +/**
    + * The value for x-client-protocol when the messaging protocol is WebChannel.
    + *
    + * @type {string}
    + */
    +goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL = 'webchannel';
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/basetestchannel.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/basetestchannel.js
    new file mode 100644
    index 0000000000..e27d78458e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/basetestchannel.js
    @@ -0,0 +1,519 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base TestChannel implementation.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.BaseTestChannel');
    +
    +goog.require('goog.labs.net.webChannel.Channel');
    +goog.require('goog.labs.net.webChannel.ChannelRequest');
    +goog.require('goog.labs.net.webChannel.requestStats');
    +goog.require('goog.labs.net.webChannel.requestStats.Stat');
    +
    +
    +
    +/**
    + * A TestChannel is used during the first part of channel negotiation
    + * with the server to create the channel. It helps us determine whether we're
    + * behind a buffering proxy.
    + *
    + * @constructor
    + * @struct
    + * @param {!goog.labs.net.webChannel.Channel} channel The channel
    + *     that owns this test channel.
    + * @param {!goog.labs.net.webChannel.WebChannelDebug} channelDebug A
    + *     WebChannelDebug instance to use for logging.
    + * @implements {goog.labs.net.webChannel.Channel}
    + */
    +goog.labs.net.webChannel.BaseTestChannel = function(channel, channelDebug) {
    +  /**
    +   * The channel that owns this test channel
    +   * @private {!goog.labs.net.webChannel.Channel}
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The channel debug to use for logging
    +   * @private {!goog.labs.net.webChannel.WebChannelDebug}
    +   */
    +  this.channelDebug_ = channelDebug;
    +
    +  /**
    +   * Extra HTTP headers to add to all the requests sent to the server.
    +   * @private {Object}
    +   */
    +  this.extraHeaders_ = null;
    +
    +  /**
    +   * The test request.
    +   * @private {goog.labs.net.webChannel.ChannelRequest}
    +   */
    +  this.request_ = null;
    +
    +  /**
    +   * Whether we have received the first result as an intermediate result. This
    +   * helps us determine whether we're behind a buffering proxy.
    +   * @private {boolean}
    +   */
    +  this.receivedIntermediateResult_ = false;
    +
    +  /**
    +   * The time when the test request was started. We use timing in IE as
    +   * a heuristic for whether we're behind a buffering proxy.
    +   * @private {?number}
    +   */
    +  this.startTime_ = null;
    +
    +  /**
    +   * The time for of the first result part. We use timing in IE as a
    +   * heuristic for whether we're behind a buffering proxy.
    +   * @private {?number}
    +   */
    +  this.firstTime_ = null;
    +
    +  /**
    +   * The time for of the last result part. We use timing in IE as a
    +   * heuristic for whether we're behind a buffering proxy.
    +   * @private {?number}
    +   */
    +  this.lastTime_ = null;
    +
    +  /**
    +   * The relative path for test requests.
    +   * @private {?string}
    +   */
    +  this.path_ = null;
    +
    +  /**
    +   * The last status code received.
    +   * @private {number}
    +   */
    +  this.lastStatusCode_ = -1;
    +
    +  /**
    +   * A subdomain prefix for using a subdomain in IE for the backchannel
    +   * requests.
    +   * @private {?string}
    +   */
    +  this.hostPrefix_ = null;
    +
    +  /**
    +   * The effective client protocol as indicated by the initial handshake
    +   * response via the x-client-wire-protocol header.
    +   *
    +   * @private {?string}
    +   */
    +  this.clientProtocol_ = null;
    +};
    +
    +
    +goog.scope(function() {
    +var BaseTestChannel = goog.labs.net.webChannel.BaseTestChannel;
    +var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
    +var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
    +var requestStats = goog.labs.net.webChannel.requestStats;
    +var Channel = goog.labs.net.webChannel.Channel;
    +
    +
    +/**
    + * Enum type for the test channel state machine
    + * @enum {number}
    + * @private
    + */
    +BaseTestChannel.State_ = {
    +  /**
    +   * The state for the TestChannel state machine where we making the
    +   * initial call to get the server configured parameters.
    +   */
    +  INIT: 0,
    +
    +  /**
    +   * The  state for the TestChannel state machine where we're checking to
    +   * se if we're behind a buffering proxy.
    +   */
    +  CONNECTION_TESTING: 1
    +};
    +
    +
    +/**
    + * The state of the state machine for this object.
    + *
    + * @private {?BaseTestChannel.State_}
    + */
    +BaseTestChannel.prototype.state_ = null;
    +
    +
    +/**
    + * Time between chunks in the test connection that indicates that we
    + * are not behind a buffering proxy. This value should be less than or
    + * equals to the time between chunks sent from the server.
    + * @type {number}
    + * @private
    + */
    +BaseTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_ = 500;
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers.
    + */
    +BaseTestChannel.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Starts the test channel. This initiates connections to the server.
    + *
    + * @param {string} path The relative uri for the test connection.
    + */
    +BaseTestChannel.prototype.connect = function(path) {
    +  this.path_ = path;
    +  var sendDataUri = this.channel_.getForwardChannelUri(this.path_);
    +
    +  requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_START);
    +  this.startTime_ = goog.now();
    +
    +  // If the channel already has the result of the handshake, then skip it.
    +  var handshakeResult = this.channel_.getConnectionState().handshakeResult;
    +  if (goog.isDefAndNotNull(handshakeResult)) {
    +    this.hostPrefix_ = this.channel_.correctHostPrefix(handshakeResult[0]);
    +    this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
    +    this.checkBufferingProxy_();
    +    return;
    +  }
    +
    +  // the first request returns server specific parameters
    +  sendDataUri.setParameterValues('MODE', 'init');
    +  this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
    +  this.request_.setExtraHeaders(this.extraHeaders_);
    +  this.request_.xmlHttpGet(sendDataUri, false /* decodeChunks */,
    +      null /* hostPrefix */, true /* opt_noClose */);
    +  this.state_ = BaseTestChannel.State_.INIT;
    +};
    +
    +
    +/**
    + * Begins the second stage of the test channel where we test to see if we're
    + * behind a buffering proxy. The server sends back a multi-chunked response
    + * with the first chunk containing the content '1' and then two seconds later
    + * sending the second chunk containing the content '2'. Depending on how we
    + * receive the content, we can tell if we're behind a buffering proxy.
    + * @private
    + */
    +BaseTestChannel.prototype.checkBufferingProxy_ = function() {
    +  this.channelDebug_.debug('TestConnection: starting stage 2');
    +
    +  // If the test result is already available, skip its execution.
    +  var bufferingProxyResult =
    +      this.channel_.getConnectionState().bufferingProxyResult;
    +  if (goog.isDefAndNotNull(bufferingProxyResult)) {
    +    this.channelDebug_.debug(
    +        'TestConnection: skipping stage 2, precomputed result is ' +
    +        bufferingProxyResult ? 'Buffered' : 'Unbuffered');
    +    requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
    +    if (bufferingProxyResult) { // Buffered/Proxy connection
    +      requestStats.notifyStatEvent(requestStats.Stat.PROXY);
    +      this.channel_.testConnectionFinished(this, false);
    +    } else { // Unbuffered/NoProxy connection
    +      requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
    +      this.channel_.testConnectionFinished(this, true);
    +    }
    +    return; // Skip the test
    +  }
    +  this.request_ = ChannelRequest.createChannelRequest(this, this.channelDebug_);
    +  this.request_.setExtraHeaders(this.extraHeaders_);
    +  var recvDataUri = this.channel_.getBackChannelUri(this.hostPrefix_,
    +      /** @type {string} */ (this.path_));
    +
    +  requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_START);
    +  if (!ChannelRequest.supportsXhrStreaming()) {
    +    recvDataUri.setParameterValues('TYPE', 'html');
    +    this.request_.tridentGet(recvDataUri, Boolean(this.hostPrefix_));
    +  } else {
    +    recvDataUri.setParameterValues('TYPE', 'xmlhttp');
    +    this.request_.xmlHttpGet(recvDataUri, false /** decodeChunks */,
    +        this.hostPrefix_, false /** opt_noClose */);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.createXhrIo = function(hostPrefix) {
    +  return this.channel_.createXhrIo(hostPrefix);
    +};
    +
    +
    +/**
    + * Aborts the test channel.
    + */
    +BaseTestChannel.prototype.abort = function() {
    +  if (this.request_) {
    +    this.request_.cancel();
    +    this.request_ = null;
    +  }
    +  this.lastStatusCode_ = -1;
    +};
    +
    +
    +/**
    + * Returns whether the test channel is closed. The ChannelRequest object expects
    + * this method to be implemented on its handler.
    + *
    + * @return {boolean} Whether the channel is closed.
    + * @override
    + */
    +BaseTestChannel.prototype.isClosed = function() {
    +  return false;
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest for when new data is received
    + *
    + * @param {ChannelRequest} req The request object.
    + * @param {string} responseText The text of the response.
    + * @override
    + */
    +BaseTestChannel.prototype.onRequestData = function(req, responseText) {
    +  this.lastStatusCode_ = req.getLastStatusCode();
    +  if (this.state_ == BaseTestChannel.State_.INIT) {
    +    this.channelDebug_.debug('TestConnection: Got data for stage 1');
    +    if (!responseText) {
    +      this.channelDebug_.debug('TestConnection: Null responseText');
    +      // The server should always send text; something is wrong here
    +      this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
    +      return;
    +    }
    +    /** @preserveTry */
    +    try {
    +      var respArray = this.channel_.getWireCodec().decodeMessage(responseText);
    +    } catch (e) {
    +      this.channelDebug_.dumpException(e);
    +      this.channel_.testConnectionFailure(this, ChannelRequest.Error.BAD_DATA);
    +      return;
    +    }
    +    this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
    +  } else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
    +    if (this.receivedIntermediateResult_) {
    +      requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_TWO);
    +      this.lastTime_ = goog.now();
    +    } else {
    +      // '11111' is used instead of '1' to prevent a small amount of buffering
    +      // by Safari.
    +      if (responseText == '11111') {
    +        requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_DATA_ONE);
    +        this.receivedIntermediateResult_ = true;
    +        this.firstTime_ = goog.now();
    +        if (this.checkForEarlyNonBuffered_()) {
    +          // If early chunk detection is on, and we passed the tests,
    +          // assume HTTP_OK, cancel the test and turn on noproxy mode.
    +          this.lastStatusCode_ = 200;
    +          this.request_.cancel();
    +          this.channelDebug_.debug(
    +              'Test connection succeeded; using streaming connection');
    +          requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
    +          this.channel_.testConnectionFinished(this, true);
    +        }
    +      } else {
    +        requestStats.notifyStatEvent(
    +            requestStats.Stat.TEST_STAGE_TWO_DATA_BOTH);
    +        this.firstTime_ = this.lastTime_ = goog.now();
    +        this.receivedIntermediateResult_ = false;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest that indicates a request has completed.
    + *
    + * @param {!ChannelRequest} req The request object.
    + * @override
    + */
    +BaseTestChannel.prototype.onRequestComplete = function(req) {
    +  this.lastStatusCode_ = this.request_.getLastStatusCode();
    +  if (!this.request_.getSuccess()) {
    +    this.channelDebug_.debug(
    +        'TestConnection: request failed, in state ' + this.state_);
    +    if (this.state_ == BaseTestChannel.State_.INIT) {
    +      requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_ONE_FAILED);
    +    } else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
    +      requestStats.notifyStatEvent(requestStats.Stat.TEST_STAGE_TWO_FAILED);
    +    }
    +    this.channel_.testConnectionFailure(this,
    +        /** @type {ChannelRequest.Error} */
    +        (this.request_.getLastError()));
    +    return;
    +  }
    +
    +  if (this.state_ == BaseTestChannel.State_.INIT) {
    +    this.recordClientProtocol_(req);
    +    this.state_ = BaseTestChannel.State_.CONNECTION_TESTING;
    +
    +    this.channelDebug_.debug(
    +        'TestConnection: request complete for initial check');
    +
    +    this.checkBufferingProxy_();
    +  } else if (this.state_ == BaseTestChannel.State_.CONNECTION_TESTING) {
    +    this.channelDebug_.debug('TestConnection: request complete for stage 2');
    +    var goodConn = false;
    +
    +    if (!ChannelRequest.supportsXhrStreaming()) {
    +      // we always get Trident responses in separate calls to
    +      // onRequestData, so we have to check the time they came
    +      var ms = this.lastTime_ - this.firstTime_;
    +      if (ms < 200) {
    +        // TODO: need to empirically verify that this number is OK
    +        // for slow computers
    +        goodConn = false;
    +      } else {
    +        goodConn = true;
    +      }
    +    } else {
    +      goodConn = this.receivedIntermediateResult_;
    +    }
    +
    +    if (goodConn) {
    +      this.channelDebug_.debug(
    +          'Test connection succeeded; using streaming connection');
    +      requestStats.notifyStatEvent(requestStats.Stat.NOPROXY);
    +      this.channel_.testConnectionFinished(this, true);
    +    } else {
    +      this.channelDebug_.debug(
    +          'Test connection failed; not using streaming');
    +      requestStats.notifyStatEvent(requestStats.Stat.PROXY);
    +      this.channel_.testConnectionFinished(this, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Record the client protocol header from the initial handshake response.
    + *
    + * @param {!ChannelRequest} req The request object.
    + * @private
    + */
    +BaseTestChannel.prototype.recordClientProtocol_ = function(req) {
    +  var xmlHttp = req.getXhr();
    +  if (xmlHttp) {
    +    var protocolHeader = xmlHttp.getResponseHeader('x-client-wire-protocol');
    +    this.clientProtocol_ = protocolHeader ? protocolHeader : null;
    +  }
    +};
    +
    +
    +/**
    + * @return {?string} The client protocol as recorded with the init handshake
    + *     request.
    + */
    +BaseTestChannel.prototype.getClientProtocol = function() {
    +  return this.clientProtocol_;
    +};
    +
    +
    +/**
    + * Returns the last status code received for a request.
    + * @return {number} The last status code received for a request.
    + */
    +BaseTestChannel.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we should be using secondary domains when the
    + *     server instructs us to do so.
    + * @override
    + */
    +BaseTestChannel.prototype.shouldUseSecondaryDomains = function() {
    +  return this.channel_.shouldUseSecondaryDomains();
    +};
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.isActive = function() {
    +  return this.channel_.isActive();
    +};
    +
    +
    +/**
    + * @return {boolean} True if test stage 2 detected a non-buffered
    + *     channel early and early no buffering detection is enabled.
    + * @private
    + */
    +BaseTestChannel.prototype.checkForEarlyNonBuffered_ = function() {
    +  var ms = this.firstTime_ - this.startTime_;
    +
    +  // we always get Trident responses in separate calls to
    +  // onRequestData, so we have to check the time that the first came in
    +  // and verify that the data arrived before the second portion could
    +  // have been sent. For all other browser's we skip the timing test.
    +  return ChannelRequest.supportsXhrStreaming() ||
    +      ms < BaseTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.getForwardChannelUri = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.getBackChannelUri = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.correctHostPrefix = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.createDataUri = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.testConnectionFinished = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.testConnectionFailure = goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +BaseTestChannel.prototype.getConnectionState = goog.abstractMethod;
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channel.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channel.js
    new file mode 100644
    index 0000000000..ab4483ad9a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channel.js
    @@ -0,0 +1,186 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A shared interface for WebChannelBase and BaseTestChannel.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.Channel');
    +
    +
    +
    +/**
    + * Shared interface between Channel and TestChannel to support callbacks
    + * between WebChannelBase and BaseTestChannel and between Channel and
    + * ChannelRequest.
    + *
    + * @interface
    + */
    +goog.labs.net.webChannel.Channel = function() {};
    +
    +
    +goog.scope(function() {
    +var Channel = goog.labs.net.webChannel.Channel;
    +
    +
    +/**
    + * Determines whether to use a secondary domain when the server gives us
    + * a host prefix. This allows us to work around browser per-domain
    + * connection limits.
    + *
    + * Currently, we  use secondary domains when using Trident's ActiveXObject,
    + * because it supports cross-domain requests out of the box.  Note that in IE10
    + * we no longer use ActiveX since it's not supported in Metro mode and IE10
    + * supports XHR streaming.
    + *
    + * If you need to use secondary domains on other browsers and IE10,
    + * you have two choices:
    + *     1) If you only care about browsers that support CORS
    + *        (https://developer.mozilla.org/en-US/docs/HTTP_access_control), you
    + *        can use {@link #setSupportsCrossDomainXhrs} and set the appropriate
    + *        CORS response headers on the server.
    + *     2) Or, override this method in a subclass, and make sure that those
    + *        browsers use some messaging mechanism that works cross-domain (e.g
    + *        iframes and window.postMessage).
    + *
    + * @return {boolean} Whether to use secondary domains.
    + * @see http://code.google.com/p/closure-library/issues/detail?id=339
    + */
    +Channel.prototype.shouldUseSecondaryDomains = goog.abstractMethod;
    +
    +
    +/**
    + * Called when creating an XhrIo object.  Override in a subclass if
    + * you need to customize the behavior, for example to enable the creation of
    + * XHR's capable of calling a secondary domain. Will also allow calling
    + * a secondary domain if withCredentials (CORS) is enabled.
    + * @param {?string} hostPrefix The host prefix, if we need an XhrIo object
    + *     capable of calling a secondary domain.
    + * @return {!goog.net.XhrIo} A new XhrIo object.
    + */
    +Channel.prototype.createXhrIo = goog.abstractMethod;
    +
    +
    +/**
    + * Callback from ChannelRequest that indicates a request has completed.
    + * @param {!goog.labs.net.webChannel.ChannelRequest} request
    + *     The request object.
    + */
    +Channel.prototype.onRequestComplete = goog.abstractMethod;
    +
    +
    +/**
    + * Returns whether the channel is closed
    + * @return {boolean} true if the channel is closed.
    + */
    +Channel.prototype.isClosed = goog.abstractMethod;
    +
    +
    +/**
    + * Callback from ChannelRequest for when new data is received
    + * @param {goog.labs.net.webChannel.ChannelRequest} request
    + *     The request object.
    + * @param {string} responseText The text of the response.
    + */
    +Channel.prototype.onRequestData = goog.abstractMethod;
    +
    +
    +/**
    + * Gets whether this channel is currently active. This is used to determine the
    + * length of time to wait before retrying. This call delegates to the handler.
    + * @return {boolean} Whether the channel is currently active.
    + */
    +Channel.prototype.isActive = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Gets the Uri used for the connection that sends data to the server.
    + * @param {string} path The path on the host.
    + * @return {goog.Uri} The forward channel URI.
    + */
    +Channel.prototype.getForwardChannelUri = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Gets the Uri used for the connection that receives data from the server.
    + * @param {?string} hostPrefix The host prefix.
    + * @param {string} path The path on the host.
    + * @return {goog.Uri} The back channel URI.
    + */
    +Channel.prototype.getBackChannelUri = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Allows the handler to override a host prefix provided by the server.  Will
    + * be called whenever the channel has received such a prefix and is considering
    + * its use.
    + * @param {?string} serverHostPrefix The host prefix provided by the server.
    + * @return {?string} The host prefix the client should use.
    + */
    +Channel.prototype.correctHostPrefix = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Creates a data Uri applying logic for secondary hostprefix, port
    + * overrides, and versioning.
    + * @param {?string} hostPrefix The host prefix.
    + * @param {string} path The path on the host (may be absolute or relative).
    + * @param {number=} opt_overridePort Optional override port.
    + * @return {goog.Uri} The data URI.
    + */
    +Channel.prototype.createDataUri = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Callback from TestChannel for when the channel is finished.
    + * @param {goog.labs.net.webChannel.BaseTestChannel} testChannel
    + *     The TestChannel.
    + * @param {boolean} useChunked  Whether we can chunk responses.
    + */
    +Channel.prototype.testConnectionFinished = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + *
    + * Callback from TestChannel for when the channel has an error.
    + * @param {goog.labs.net.webChannel.BaseTestChannel} testChannel
    + *     The TestChannel.
    + * @param {goog.labs.net.webChannel.ChannelRequest.Error} errorCode
    + *     The error code of the failure.
    + */
    +Channel.prototype.testConnectionFailure = goog.abstractMethod;
    +
    +
    +/**
    + * Not needed for testchannel.
    + * Gets the result of previous connectivity tests.
    + *
    + * @return {!goog.labs.net.webChannel.ConnectionState} The connectivity state.
    + */
    +Channel.prototype.getConnectionState = goog.abstractMethod;
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest.js
    new file mode 100644
    index 0000000000..a142cabcd4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest.js
    @@ -0,0 +1,1268 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the ChannelRequest class. The request
    + * object encapsulates the logic for making a single request, either for the
    + * forward channel, back channel, or test channel, to the server. It contains
    + * the logic for the three types of transports we use:
    + * XMLHTTP, Trident ActiveX (ie only), and Image request. It provides timeout
    + * detection.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.ChannelRequest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.async.Throttle');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.labs.net.webChannel.requestStats');
    +goog.require('goog.labs.net.webChannel.requestStats.ServerReachability');
    +goog.require('goog.labs.net.webChannel.requestStats.Stat');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.uri.utils.StandardQueryParam');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A new ChannelRequest is created for each request to the server.
    + *
    + * @param {goog.labs.net.webChannel.Channel} channel
    + *     The channel that owns this request.
    + * @param {goog.labs.net.webChannel.WebChannelDebug} channelDebug A
    + *     WebChannelDebug to use for logging.
    + * @param {string=} opt_sessionId The session id for the channel.
    + * @param {string|number=} opt_requestId The request id for this request.
    + * @param {number=} opt_retryId The retry id for this request.
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.labs.net.webChannel.ChannelRequest = function(channel, channelDebug,
    +    opt_sessionId, opt_requestId, opt_retryId) {
    +  /**
    +   * The channel object that owns the request.
    +   * @private {goog.labs.net.webChannel.Channel}
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The channel debug to use for logging
    +   * @private {goog.labs.net.webChannel.WebChannelDebug}
    +   */
    +  this.channelDebug_ = channelDebug;
    +
    +  /**
    +   * The Session ID for the channel.
    +   * @private {string|undefined}
    +   */
    +  this.sid_ = opt_sessionId;
    +
    +  /**
    +   * The RID (request ID) for the request.
    +   * @private {string|number|undefined}
    +   */
    +  this.rid_ = opt_requestId;
    +
    +  /**
    +   * The attempt number of the current request.
    +   * @private {number}
    +   */
    +  this.retryId_ = opt_retryId || 1;
    +
    +  /**
    +   * An object to keep track of the channel request event listeners.
    +   * @private {!goog.events.EventHandler<
    +   *     !goog.labs.net.webChannel.ChannelRequest>}
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The timeout in ms before failing the request.
    +   * @private {number}
    +   */
    +  this.timeout_ = goog.labs.net.webChannel.ChannelRequest.TIMEOUT_MS_;
    +
    +  /**
    +   * A timer for polling responseText in browsers that don't fire
    +   * onreadystatechange during incremental loading of responseText.
    +   * @private {goog.Timer}
    +   */
    +  this.pollingTimer_ = new goog.Timer();
    +
    +  this.pollingTimer_.setInterval(
    +      goog.labs.net.webChannel.ChannelRequest.POLLING_INTERVAL_MS_);
    +
    +  /**
    +   * Extra HTTP headers to add to all the requests sent to the server.
    +   * @private {Object}
    +   */
    +  this.extraHeaders_ = null;
    +
    +
    +  /**
    +   * Whether the request was successful. This is only set to true after the
    +   * request successfully completes.
    +   * @private {boolean}
    +   */
    +  this.successful_ = false;
    +
    +
    +  /**
    +   * The TimerID of the timer used to detect if the request has timed-out.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.watchDogTimerId_ = null;
    +
    +  /**
    +   * The time in the future when the request will timeout.
    +   * @private {?number}
    +   */
    +  this.watchDogTimeoutTime_ = null;
    +
    +  /**
    +   * The time the request started.
    +   * @private {?number}
    +   */
    +  this.requestStartTime_ = null;
    +
    +  /**
    +   * The type of request (XMLHTTP, IMG, Trident)
    +   * @private {?number}
    +   */
    +  this.type_ = null;
    +
    +  /**
    +   * The base Uri for the request. The includes all the parameters except the
    +   * one that indicates the retry number.
    +   * @private {goog.Uri}
    +   */
    +  this.baseUri_ = null;
    +
    +  /**
    +   * The request Uri that was actually used for the most recent request attempt.
    +   * @private {goog.Uri}
    +   */
    +  this.requestUri_ = null;
    +
    +  /**
    +   * The post data, if the request is a post.
    +   * @private {?string}
    +   */
    +  this.postData_ = null;
    +
    +  /**
    +   * The XhrLte request if the request is using XMLHTTP
    +   * @private {goog.net.XhrIo}
    +   */
    +  this.xmlHttp_ = null;
    +
    +  /**
    +   * The position of where the next unprocessed chunk starts in the response
    +   * text.
    +   * @private {number}
    +   */
    +  this.xmlHttpChunkStart_ = 0;
    +
    +  /**
    +   * The Trident instance if the request is using Trident.
    +   * @private {ActiveXObject}
    +   */
    +  this.trident_ = null;
    +
    +  /**
    +   * The verb (Get or Post) for the request.
    +   * @private {?string}
    +   */
    +  this.verb_ = null;
    +
    +  /**
    +   * The last error if the request failed.
    +   * @private {?goog.labs.net.webChannel.ChannelRequest.Error}
    +   */
    +  this.lastError_ = null;
    +
    +  /**
    +   * The last status code received.
    +   * @private {number}
    +   */
    +  this.lastStatusCode_ = -1;
    +
    +  /**
    +   * Whether to send the Connection:close header as part of the request.
    +   * @private {boolean}
    +   */
    +  this.sendClose_ = true;
    +
    +  /**
    +   * Whether the request has been cancelled due to a call to cancel.
    +   * @private {boolean}
    +   */
    +  this.cancelled_ = false;
    +
    +  /**
    +   * A throttle time in ms for readystatechange events for the backchannel.
    +   * Useful for throttling when ready state is INTERACTIVE (partial data).
    +   * If set to zero no throttle is used.
    +   *
    +   * See WebChannelBase.prototype.readyStateChangeThrottleMs_
    +   *
    +   * @private {number}
    +   */
    +  this.readyStateChangeThrottleMs_ = 0;
    +
    +  /**
    +   * The throttle for readystatechange events for the current request, or null
    +   * if there is none.
    +   * @private {goog.async.Throttle}
    +   */
    +  this.readyStateChangeThrottle_ = null;
    +
    +
    +  /**
    +   * Whether to the result is expected to be encoded for chunking and thus
    +   * requires decoding.
    +   * @private {boolean}
    +   */
    +  this.decodeChunks_ = false;
    +};
    +
    +
    +goog.scope(function() {
    +var Channel = goog.labs.net.webChannel.Channel;
    +var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
    +var requestStats = goog.labs.net.webChannel.requestStats;
    +var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
    +
    +
    +/**
    + * Default timeout in MS for a request. The server must return data within this
    + * time limit for the request to not timeout.
    + * @private {number}
    + */
    +ChannelRequest.TIMEOUT_MS_ = 45 * 1000;
    +
    +
    +/**
    + * How often to poll (in MS) for changes to responseText in browsers that don't
    + * fire onreadystatechange during incremental loading of responseText.
    + * @private {number}
    + */
    +ChannelRequest.POLLING_INTERVAL_MS_ = 250;
    +
    +
    +/**
    + * Enum for channel requests type
    + * @enum {number}
    + * @private
    + */
    +ChannelRequest.Type_ = {
    +  /**
    +   * XMLHTTP requests.
    +   */
    +  XML_HTTP: 1,
    +
    +  /**
    +   * IMG requests.
    +   */
    +  IMG: 2,
    +
    +  /**
    +   * Requests that use the MSHTML ActiveX control.
    +   */
    +  TRIDENT: 3
    +};
    +
    +
    +/**
    + * Enum type for identifying an error.
    + * @enum {number}
    + */
    +ChannelRequest.Error = {
    +  /**
    +   * Errors due to a non-200 status code.
    +   */
    +  STATUS: 0,
    +
    +  /**
    +   * Errors due to no data being returned.
    +   */
    +  NO_DATA: 1,
    +
    +  /**
    +   * Errors due to a timeout.
    +   */
    +  TIMEOUT: 2,
    +
    +  /**
    +   * Errors due to the server returning an unknown.
    +   */
    +  UNKNOWN_SESSION_ID: 3,
    +
    +  /**
    +   * Errors due to bad data being received.
    +   */
    +  BAD_DATA: 4,
    +
    +  /**
    +   * Errors due to the handler throwing an exception.
    +   */
    +  HANDLER_EXCEPTION: 5,
    +
    +  /**
    +   * The browser declared itself offline during the request.
    +   */
    +  BROWSER_OFFLINE: 6,
    +
    +  /**
    +   * IE is blocking ActiveX streaming.
    +   */
    +  ACTIVE_X_BLOCKED: 7
    +};
    +
    +
    +/**
    + * Returns a useful error string for debugging based on the specified error
    + * code.
    + * @param {?ChannelRequest.Error} errorCode The error code.
    + * @param {number} statusCode The HTTP status code.
    + * @return {string} The error string for the given code combination.
    + */
    +ChannelRequest.errorStringFromCode = function(errorCode, statusCode) {
    +  switch (errorCode) {
    +    case ChannelRequest.Error.STATUS:
    +      return 'Non-200 return code (' + statusCode + ')';
    +    case ChannelRequest.Error.NO_DATA:
    +      return 'XMLHTTP failure (no data)';
    +    case ChannelRequest.Error.TIMEOUT:
    +      return 'HttpConnection timeout';
    +    default:
    +      return 'Unknown error';
    +  }
    +};
    +
    +
    +/**
    + * Sentinel value used to indicate an invalid chunk in a multi-chunk response.
    + * @private {Object}
    + */
    +ChannelRequest.INVALID_CHUNK_ = {};
    +
    +
    +/**
    + * Sentinel value used to indicate an incomplete chunk in a multi-chunk
    + * response.
    + * @private {Object}
    + */
    +ChannelRequest.INCOMPLETE_CHUNK_ = {};
    +
    +
    +/**
    + * Returns whether XHR streaming is supported on this browser.
    + *
    + * If XHR streaming is not supported, we will try to use an ActiveXObject
    + * to create a Forever IFrame.
    + *
    + * @return {boolean} Whether XHR streaming is supported.
    + * @see http://code.google.com/p/closure-library/issues/detail?id=346
    + */
    +ChannelRequest.supportsXhrStreaming = function() {
    +  return !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(10);
    +};
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers.
    + */
    +ChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Sets the timeout for a request
    + *
    + * @param {number} timeout   The timeout in MS for when we fail the request.
    + */
    +ChannelRequest.prototype.setTimeout = function(timeout) {
    +  this.timeout_ = timeout;
    +};
    +
    +
    +/**
    + * Sets the throttle for handling onreadystatechange events for the request.
    + *
    + * @param {number} throttle The throttle in ms.  A value of zero indicates
    + *     no throttle.
    + */
    +ChannelRequest.prototype.setReadyStateChangeThrottle = function(throttle) {
    +  this.readyStateChangeThrottleMs_ = throttle;
    +};
    +
    +
    +/**
    + * Uses XMLHTTP to send an HTTP POST to the server.
    + *
    + * @param {goog.Uri} uri  The uri of the request.
    + * @param {string} postData  The data for the post body.
    + * @param {boolean} decodeChunks  Whether to the result is expected to be
    + *     encoded for chunking and thus requires decoding.
    + */
    +ChannelRequest.prototype.xmlHttpPost = function(uri, postData, decodeChunks) {
    +  this.type_ = ChannelRequest.Type_.XML_HTTP;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.postData_ = postData;
    +  this.decodeChunks_ = decodeChunks;
    +  this.sendXmlHttp_(null /* hostPrefix */);
    +};
    +
    +
    +/**
    + * Uses XMLHTTP to send an HTTP GET to the server.
    + *
    + * @param {goog.Uri} uri  The uri of the request.
    + * @param {boolean} decodeChunks  Whether to the result is expected to be
    + *     encoded for chunking and thus requires decoding.
    + * @param {?string} hostPrefix  The host prefix, if we might be using a
    + *     secondary domain.  Note that it should also be in the URL, adding this
    + *     won't cause it to be added to the URL.
    + * @param {boolean=} opt_noClose   Whether to request that the tcp/ip connection
    + *     should be closed.
    + * @param {boolean=} opt_duplicateRandom   Whether to duplicate the randomness
    + *     parameter which is only required for the initial handshake. This allows
    + *     a server to break compatibility with old version clients.
    + */
    +ChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks,
    +    hostPrefix, opt_noClose, opt_duplicateRandom) {
    +  this.type_ = ChannelRequest.Type_.XML_HTTP;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.postData_ = null;
    +  this.decodeChunks_ = decodeChunks;
    +  if (opt_noClose) {
    +    this.sendClose_ = false;
    +  }
    +
    +  // TODO(user): clean this up once we phase out all BrowserChannel clients,
    +  if (opt_duplicateRandom) {
    +    var randomParam = this.baseUri_.getParameterValue(
    +        goog.uri.utils.StandardQueryParam.RANDOM);
    +    this.baseUri_.setParameterValue(  // baseUri_ reusable for future requests
    +        goog.uri.utils.StandardQueryParam.RANDOM + '1',  // 'zx1'
    +        randomParam);
    +  }
    +
    +  this.sendXmlHttp_(hostPrefix);
    +};
    +
    +
    +/**
    + * Sends a request via XMLHTTP according to the current state of the request
    + * object.
    + *
    + * @param {?string} hostPrefix The host prefix, if we might be using a secondary
    + *     domain.
    + * @private
    + */
    +ChannelRequest.prototype.sendXmlHttp_ = function(hostPrefix) {
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +
    +  // clone the base URI to create the request URI. The request uri has the
    +  // attempt number as a parameter which helps in debugging.
    +  this.requestUri_ = this.baseUri_.clone();
    +  this.requestUri_.setParameterValues('t', this.retryId_);
    +
    +  // send the request either as a POST or GET
    +  this.xmlHttpChunkStart_ = 0;
    +  var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains();
    +  this.xmlHttp_ = this.channel_.createXhrIo(useSecondaryDomains ?
    +      hostPrefix : null);
    +
    +  if (this.readyStateChangeThrottleMs_ > 0) {
    +    this.readyStateChangeThrottle_ = new goog.async.Throttle(
    +        goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_),
    +        this.readyStateChangeThrottleMs_);
    +  }
    +
    +  this.eventHandler_.listen(this.xmlHttp_,
    +      goog.net.EventType.READY_STATE_CHANGE,
    +      this.readyStateChangeHandler_);
    +
    +  var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {};
    +  if (this.postData_) {
    +    this.verb_ = 'POST';
    +    headers['Content-Type'] = 'application/x-www-form-urlencoded';
    +    this.xmlHttp_.send(this.requestUri_, this.verb_, this.postData_, headers);
    +  } else {
    +    this.verb_ = 'GET';
    +
    +    // If the user agent is webkit, we cannot send the close header since it is
    +    // disallowed by the browser.  If we attempt to set the "Connection: close"
    +    // header in WEBKIT browser, it will actually causes an error message.
    +    if (this.sendClose_ && !goog.userAgent.WEBKIT) {
    +      headers['Connection'] = 'close';
    +    }
    +    this.xmlHttp_.send(this.requestUri_, this.verb_, null, headers);
    +  }
    +  requestStats.notifyServerReachabilityEvent(
    +      requestStats.ServerReachability.REQUEST_MADE);
    +  this.channelDebug_.xmlHttpChannelRequest(this.verb_,
    +      this.requestUri_, this.rid_, this.retryId_,
    +      this.postData_);
    +};
    +
    +
    +/**
    + * Handles a readystatechange event.
    + * @param {goog.events.Event} evt The event.
    + * @private
    + */
    +ChannelRequest.prototype.readyStateChangeHandler_ = function(evt) {
    +  var xhr = /** @type {goog.net.XhrIo} */ (evt.target);
    +  var throttle = this.readyStateChangeThrottle_;
    +  if (throttle &&
    +      xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
    +    // Only throttle in the partial data case.
    +    this.channelDebug_.debug('Throttling readystatechange.');
    +    throttle.fire();
    +  } else {
    +    // If we haven't throttled, just handle response directly.
    +    this.xmlHttpHandler_(xhr);
    +  }
    +};
    +
    +
    +/**
    + * XmlHttp handler
    + * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request.
    + * @private
    + */
    +ChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) {
    +  requestStats.onStartExecution();
    +
    +  /** @preserveTry */
    +  try {
    +    if (xmlhttp == this.xmlHttp_) {
    +      this.onXmlHttpReadyStateChanged_();
    +    } else {
    +      this.channelDebug_.warning('Called back with an ' +
    +                                     'unexpected xmlhttp');
    +    }
    +  } catch (ex) {
    +    this.channelDebug_.debug('Failed call to OnXmlHttpReadyStateChanged_');
    +    if (this.xmlHttp_ && this.xmlHttp_.getResponseText()) {
    +      this.channelDebug_.dumpException(ex,
    +          'ResponseText: ' + this.xmlHttp_.getResponseText());
    +    } else {
    +      this.channelDebug_.dumpException(ex, 'No response text');
    +    }
    +  } finally {
    +    requestStats.onEndExecution();
    +  }
    +};
    +
    +
    +/**
    + * Called by the readystate handler for XMLHTTP requests.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.onXmlHttpReadyStateChanged_ = function() {
    +  var readyState = this.xmlHttp_.getReadyState();
    +  var errorCode = this.xmlHttp_.getLastErrorCode();
    +  var statusCode = this.xmlHttp_.getStatus();
    +  if (!ChannelRequest.supportsXhrStreaming()) {
    +    if (readyState < goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      // not yet ready
    +      return;
    +    }
    +  } else {
    +    // we get partial results in browsers that support ready state interactive.
    +    // We also make sure that getResponseText is not null in interactive mode
    +    // before we continue.  However, we don't do it in Opera because it only
    +    // fire readyState == INTERACTIVE once.  We need the following code to poll
    +    if (readyState < goog.net.XmlHttp.ReadyState.INTERACTIVE ||
    +        readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
    +        !goog.userAgent.OPERA && !this.xmlHttp_.getResponseText()) {
    +      // not yet ready
    +      return;
    +    }
    +  }
    +
    +  // Dispatch any appropriate network events.
    +  if (!this.cancelled_ && readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
    +      errorCode != goog.net.ErrorCode.ABORT) {
    +
    +    // Pretty conservative, these are the only known scenarios which we'd
    +    // consider indicative of a truly non-functional network connection.
    +    if (errorCode == goog.net.ErrorCode.TIMEOUT ||
    +        statusCode <= 0) {
    +      requestStats.notifyServerReachabilityEvent(
    +          requestStats.ServerReachability.REQUEST_FAILED);
    +    } else {
    +      requestStats.notifyServerReachabilityEvent(
    +          requestStats.ServerReachability.REQUEST_SUCCEEDED);
    +    }
    +  }
    +
    +  // got some data so cancel the watchdog timer
    +  this.cancelWatchDogTimer_();
    +
    +  var status = this.xmlHttp_.getStatus();
    +  this.lastStatusCode_ = status;
    +  var responseText = this.xmlHttp_.getResponseText();
    +  if (!responseText) {
    +    this.channelDebug_.debug('No response text for uri ' +
    +        this.requestUri_ + ' status ' + status);
    +  }
    +  this.successful_ = (status == 200);
    +
    +  this.channelDebug_.xmlHttpChannelResponseMetaData(
    +      /** @type {string} */ (this.verb_),
    +      this.requestUri_, this.rid_, this.retryId_, readyState,
    +      status);
    +
    +  if (!this.successful_) {
    +    if (status == 400 &&
    +        responseText.indexOf('Unknown SID') > 0) {
    +      // the server error string will include 'Unknown SID' which indicates the
    +      // server doesn't know about the session (maybe it got restarted, maybe
    +      // the user got moved to another server, etc.,). Handlers can special
    +      // case this error
    +      this.lastError_ = ChannelRequest.Error.UNKNOWN_SESSION_ID;
    +      requestStats.notifyStatEvent(
    +          requestStats.Stat.REQUEST_UNKNOWN_SESSION_ID);
    +      this.channelDebug_.warning('XMLHTTP Unknown SID (' + this.rid_ + ')');
    +    } else {
    +      this.lastError_ = ChannelRequest.Error.STATUS;
    +      requestStats.notifyStatEvent(requestStats.Stat.REQUEST_BAD_STATUS);
    +      this.channelDebug_.warning(
    +          'XMLHTTP Bad status ' + status + ' (' + this.rid_ + ')');
    +    }
    +    this.cleanup_();
    +    this.dispatchFailure_();
    +    return;
    +  }
    +
    +  if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +    this.cleanup_();
    +  }
    +
    +  if (this.decodeChunks_) {
    +    this.decodeNextChunks_(readyState, responseText);
    +    if (goog.userAgent.OPERA && this.successful_ &&
    +        readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
    +      this.startPolling_();
    +    }
    +  } else {
    +    this.channelDebug_.xmlHttpChannelResponseText(
    +        this.rid_, responseText, null);
    +    this.safeOnRequestData_(responseText);
    +  }
    +
    +  if (!this.successful_) {
    +    return;
    +  }
    +
    +  if (!this.cancelled_) {
    +    if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      this.channel_.onRequestComplete(this);
    +    } else {
    +      // The default is false, the result from this callback shouldn't carry
    +      // over to the next callback, otherwise the request looks successful if
    +      // the watchdog timer gets called
    +      this.successful_ = false;
    +      this.ensureWatchDogTimer_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Decodes the next set of available chunks in the response.
    + * @param {number} readyState The value of readyState.
    + * @param {string} responseText The value of responseText.
    + * @private
    + */
    +ChannelRequest.prototype.decodeNextChunks_ = function(readyState,
    +    responseText) {
    +  var decodeNextChunksSuccessful = true;
    +  while (!this.cancelled_ &&
    +         this.xmlHttpChunkStart_ < responseText.length) {
    +    var chunkText = this.getNextChunk_(responseText);
    +    if (chunkText == ChannelRequest.INCOMPLETE_CHUNK_) {
    +      if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +        // should have consumed entire response when the request is done
    +        this.lastError_ = ChannelRequest.Error.BAD_DATA;
    +        requestStats.notifyStatEvent(
    +            requestStats.Stat.REQUEST_INCOMPLETE_DATA);
    +        decodeNextChunksSuccessful = false;
    +      }
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, null, '[Incomplete Response]');
    +      break;
    +    } else if (chunkText == ChannelRequest.INVALID_CHUNK_) {
    +      this.lastError_ = ChannelRequest.Error.BAD_DATA;
    +      requestStats.notifyStatEvent(requestStats.Stat.REQUEST_BAD_DATA);
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, responseText, '[Invalid Chunk]');
    +      decodeNextChunksSuccessful = false;
    +      break;
    +    } else {
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, /** @type {string} */ (chunkText), null);
    +      this.safeOnRequestData_(/** @type {string} */ (chunkText));
    +    }
    +  }
    +  if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
    +      responseText.length == 0) {
    +    // also an error if we didn't get any response
    +    this.lastError_ = ChannelRequest.Error.NO_DATA;
    +    requestStats.notifyStatEvent(requestStats.Stat.REQUEST_NO_DATA);
    +    decodeNextChunksSuccessful = false;
    +  }
    +  this.successful_ = this.successful_ && decodeNextChunksSuccessful;
    +  if (!decodeNextChunksSuccessful) {
    +    // malformed response - we make this trigger retry logic
    +    this.channelDebug_.xmlHttpChannelResponseText(
    +        this.rid_, responseText, '[Invalid Chunked Response]');
    +    this.cleanup_();
    +    this.dispatchFailure_();
    +  }
    +};
    +
    +
    +/**
    + * Polls the response for new data.
    + * @private
    + */
    +ChannelRequest.prototype.pollResponse_ = function() {
    +  var readyState = this.xmlHttp_.getReadyState();
    +  var responseText = this.xmlHttp_.getResponseText();
    +  if (this.xmlHttpChunkStart_ < responseText.length) {
    +    this.cancelWatchDogTimer_();
    +    this.decodeNextChunks_(readyState, responseText);
    +    if (this.successful_ &&
    +        readyState != goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      this.ensureWatchDogTimer_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Starts a polling interval for changes to responseText of the
    + * XMLHttpRequest, for browsers that don't fire onreadystatechange
    + * as data comes in incrementally.  This timer is disabled in
    + * cleanup_().
    + * @private
    + */
    +ChannelRequest.prototype.startPolling_ = function() {
    +  this.eventHandler_.listen(this.pollingTimer_, goog.Timer.TICK,
    +      this.pollResponse_);
    +  this.pollingTimer_.start();
    +};
    +
    +
    +/**
    + * Called when the browser declares itself offline at the start of a request or
    + * during its lifetime.  Abandons that request.
    + * @private
    + */
    +ChannelRequest.prototype.cancelRequestAsBrowserIsOffline_ = function() {
    +  if (this.successful_) {
    +    // Should never happen.
    +    this.channelDebug_.severe(
    +        'Received browser offline event even though request completed ' +
    +        'successfully');
    +  }
    +
    +  this.channelDebug_.browserOfflineResponse(this.requestUri_);
    +  this.cleanup_();
    +
    +  // set error and dispatch failure
    +  this.lastError_ = ChannelRequest.Error.BROWSER_OFFLINE;
    +  requestStats.notifyStatEvent(requestStats.Stat.BROWSER_OFFLINE);
    +  this.dispatchFailure_();
    +};
    +
    +
    +/**
    + * Returns the next chunk of a chunk-encoded response. This is not standard
    + * HTTP chunked encoding because browsers don't expose the chunk boundaries to
    + * the application through XMLHTTP. So we have an additional chunk encoding at
    + * the application level that lets us tell where the beginning and end of
    + * individual responses are so that we can only try to eval a complete JS array.
    + *
    + * The encoding is the size of the chunk encoded as a decimal string followed
    + * by a newline followed by the data.
    + *
    + * @param {string} responseText The response text from the XMLHTTP response.
    + * @return {string|Object} The next chunk string or a sentinel object
    + *                         indicating a special condition.
    + * @private
    + */
    +ChannelRequest.prototype.getNextChunk_ = function(responseText) {
    +  var sizeStartIndex = this.xmlHttpChunkStart_;
    +  var sizeEndIndex = responseText.indexOf('\n', sizeStartIndex);
    +  if (sizeEndIndex == -1) {
    +    return ChannelRequest.INCOMPLETE_CHUNK_;
    +  }
    +
    +  var sizeAsString = responseText.substring(sizeStartIndex, sizeEndIndex);
    +  var size = Number(sizeAsString);
    +  if (isNaN(size)) {
    +    return ChannelRequest.INVALID_CHUNK_;
    +  }
    +
    +  var chunkStartIndex = sizeEndIndex + 1;
    +  if (chunkStartIndex + size > responseText.length) {
    +    return ChannelRequest.INCOMPLETE_CHUNK_;
    +  }
    +
    +  var chunkText = responseText.substr(chunkStartIndex, size);
    +  this.xmlHttpChunkStart_ = chunkStartIndex + size;
    +  return chunkText;
    +};
    +
    +
    +/**
    + * Uses the Trident htmlfile ActiveX control to send a GET request in IE. This
    + * is the innovation discovered that lets us get intermediate results in
    + * Internet Explorer.  Thanks to http://go/kev
    + * @param {goog.Uri} uri The uri to request from.
    + * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
    + */
    +ChannelRequest.prototype.tridentGet = function(uri, usingSecondaryDomain) {
    +  this.type_ = ChannelRequest.Type_.TRIDENT;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.tridentGet_(usingSecondaryDomain);
    +};
    +
    +
    +/**
    + * Starts the Trident request.
    + * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
    + * @private
    + */
    +ChannelRequest.prototype.tridentGet_ = function(usingSecondaryDomain) {
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +
    +  var hostname = usingSecondaryDomain ? window.location.hostname : '';
    +  this.requestUri_ = this.baseUri_.clone();
    +  this.requestUri_.setParameterValue('DOMAIN', hostname);
    +  this.requestUri_.setParameterValue('t', this.retryId_);
    +
    +  try {
    +    this.trident_ = new ActiveXObject('htmlfile');
    +  } catch (e) {
    +    this.channelDebug_.severe('ActiveX blocked');
    +    this.cleanup_();
    +
    +    this.lastError_ = ChannelRequest.Error.ACTIVE_X_BLOCKED;
    +    requestStats.notifyStatEvent(requestStats.Stat.ACTIVE_X_BLOCKED);
    +    this.dispatchFailure_();
    +    return;
    +  }
    +
    +  var body = '<html><body>';
    +  if (usingSecondaryDomain) {
    +    body += '<script>document.domain="' + hostname + '"</scr' + 'ipt>';
    +  }
    +  body += '</body></html>';
    +
    +  this.trident_.open();
    +  this.trident_.write(body);
    +  this.trident_.close();
    +
    +  this.trident_.parentWindow['m'] = goog.bind(this.onTridentRpcMessage_, this);
    +  this.trident_.parentWindow['d'] = goog.bind(this.onTridentDone_, this, true);
    +  this.trident_.parentWindow['rpcClose'] =
    +      goog.bind(this.onTridentDone_, this, false);
    +
    +  var div = this.trident_.createElement('div');
    +  this.trident_.parentWindow.document.body.appendChild(div);
    +  div.innerHTML = '<iframe src="' + this.requestUri_ + '"></iframe>';
    +  this.channelDebug_.tridentChannelRequest('GET',
    +      this.requestUri_, this.rid_, this.retryId_);
    +  requestStats.notifyServerReachabilityEvent(
    +      requestStats.ServerReachability.REQUEST_MADE);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when a new message
    + * is received.
    + *
    + * @param {string} msg The data payload.
    + * @private
    + */
    +ChannelRequest.prototype.onTridentRpcMessage_ = function(msg) {
    +  // need to do async b/c this gets called off of the context of the ActiveX
    +  requestStats.setTimeout(
    +      goog.bind(this.onTridentRpcMessageAsync_, this, msg), 0);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when a new message
    + * is received.
    + *
    + * @param {string} msg  The data payload.
    + * @private
    + */
    +ChannelRequest.prototype.onTridentRpcMessageAsync_ = function(msg) {
    +  if (this.cancelled_) {
    +    return;
    +  }
    +  this.channelDebug_.tridentChannelResponseText(this.rid_, msg);
    +  this.cancelWatchDogTimer_();
    +  this.safeOnRequestData_(msg);
    +  this.ensureWatchDogTimer_();
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when the request
    + * is complete
    + *
    + * @param {boolean} successful Whether the request successfully completed.
    + * @private
    + */
    +ChannelRequest.prototype.onTridentDone_ = function(successful) {
    +  // need to do async b/c this gets called off of the context of the ActiveX
    +  requestStats.setTimeout(
    +      goog.bind(this.onTridentDoneAsync_, this, successful), 0);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when the request
    + * is complete
    + *
    + * @param {boolean} successful Whether the request successfully completed.
    + * @private
    + */
    +ChannelRequest.prototype.onTridentDoneAsync_ = function(successful) {
    +  if (this.cancelled_) {
    +    return;
    +  }
    +  this.channelDebug_.tridentChannelResponseDone(
    +      this.rid_, successful);
    +  this.cleanup_();
    +  this.successful_ = successful;
    +  this.channel_.onRequestComplete(this);
    +  requestStats.notifyServerReachabilityEvent(
    +      requestStats.ServerReachability.BACK_CHANNEL_ACTIVITY);
    +};
    +
    +
    +/**
    + * Uses an IMG tag to send an HTTP get to the server. This is only currently
    + * used to terminate the connection, as an IMG tag is the most reliable way to
    + * send something to the server while the page is getting torn down.
    + * @param {goog.Uri} uri The uri to send a request to.
    + */
    +ChannelRequest.prototype.sendUsingImgTag = function(uri) {
    +  this.type_ = ChannelRequest.Type_.IMG;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.imgTagGet_();
    +};
    +
    +
    +/**
    + * Starts the IMG request.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.imgTagGet_ = function() {
    +  var eltImg = new Image();
    +  eltImg.src = this.baseUri_;
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +};
    +
    +
    +/**
    + * Cancels the request no matter what the underlying transport is.
    + */
    +ChannelRequest.prototype.cancel = function() {
    +  this.cancelled_ = true;
    +  this.cleanup_();
    +};
    +
    +
    +/**
    + * Ensures that there is watchdog timeout which is used to ensure that
    + * the connection completes in time.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.ensureWatchDogTimer_ = function() {
    +  this.watchDogTimeoutTime_ = goog.now() + this.timeout_;
    +  this.startWatchDogTimer_(this.timeout_);
    +};
    +
    +
    +/**
    + * Starts the watchdog timer which is used to ensure that the connection
    + * completes in time.
    + * @param {number} time The number of milliseconds to wait.
    + * @private
    + */
    +ChannelRequest.prototype.startWatchDogTimer_ = function(time) {
    +  if (this.watchDogTimerId_ != null) {
    +    // assertion
    +    throw Error('WatchDog timer not null');
    +  }
    +  this.watchDogTimerId_ = requestStats.setTimeout(
    +      goog.bind(this.onWatchDogTimeout_, this), time);
    +};
    +
    +
    +/**
    + * Cancels the watchdog timer if it has been started.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.cancelWatchDogTimer_ = function() {
    +  if (this.watchDogTimerId_) {
    +    goog.global.clearTimeout(this.watchDogTimerId_);
    +    this.watchDogTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Called when the watchdog timer is triggered. It also handles a case where it
    + * is called too early which we suspect may be happening sometimes
    + * (not sure why)
    + *
    + * @private
    + */
    +ChannelRequest.prototype.onWatchDogTimeout_ = function() {
    +  this.watchDogTimerId_ = null;
    +  var now = goog.now();
    +  if (now - this.watchDogTimeoutTime_ >= 0) {
    +    this.handleTimeout_();
    +  } else {
    +    // got called too early for some reason
    +    this.channelDebug_.warning('WatchDog timer called too early');
    +    this.startWatchDogTimer_(this.watchDogTimeoutTime_ - now);
    +  }
    +};
    +
    +
    +/**
    + * Called when the request has actually timed out. Will cleanup and notify the
    + * channel of the failure.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.handleTimeout_ = function() {
    +  if (this.successful_) {
    +    // Should never happen.
    +    this.channelDebug_.severe(
    +        'Received watchdog timeout even though request loaded successfully');
    +  }
    +
    +  this.channelDebug_.timeoutResponse(this.requestUri_);
    +  // IMG requests never notice if they were successful, and always 'time out'.
    +  // This fact says nothing about reachability.
    +  if (this.type_ != ChannelRequest.Type_.IMG) {
    +    requestStats.notifyServerReachabilityEvent(
    +        requestStats.ServerReachability.REQUEST_FAILED);
    +  }
    +  this.cleanup_();
    +
    +  // set error and dispatch failure
    +  this.lastError_ = ChannelRequest.Error.TIMEOUT;
    +  requestStats.notifyStatEvent(requestStats.Stat.REQUEST_TIMEOUT);
    +  this.dispatchFailure_();
    +};
    +
    +
    +/**
    + * Notifies the channel that this request failed.
    + * @private
    + */
    +ChannelRequest.prototype.dispatchFailure_ = function() {
    +  if (this.channel_.isClosed() || this.cancelled_) {
    +    return;
    +  }
    +
    +  this.channel_.onRequestComplete(this);
    +};
    +
    +
    +/**
    + * Cleans up the objects used to make the request. This function is
    + * idempotent.
    + *
    + * @private
    + */
    +ChannelRequest.prototype.cleanup_ = function() {
    +  this.cancelWatchDogTimer_();
    +
    +  goog.dispose(this.readyStateChangeThrottle_);
    +  this.readyStateChangeThrottle_ = null;
    +
    +  // Stop the polling timer, if necessary.
    +  this.pollingTimer_.stop();
    +
    +  // Unhook all event handlers.
    +  this.eventHandler_.removeAll();
    +
    +  if (this.xmlHttp_) {
    +    // clear out this.xmlHttp_ before aborting so we handle getting reentered
    +    // inside abort
    +    var xmlhttp = this.xmlHttp_;
    +    this.xmlHttp_ = null;
    +    xmlhttp.abort();
    +    xmlhttp.dispose();
    +  }
    +
    +  if (this.trident_) {
    +    this.trident_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Indicates whether the request was successful. Only valid after the handler
    + * is called to indicate completion of the request.
    + *
    + * @return {boolean} True if the request succeeded.
    + */
    +ChannelRequest.prototype.getSuccess = function() {
    +  return this.successful_;
    +};
    +
    +
    +/**
    + * If the request was not successful, returns the reason.
    + *
    + * @return {?ChannelRequest.Error}  The last error.
    + */
    +ChannelRequest.prototype.getLastError = function() {
    +  return this.lastError_;
    +};
    +
    +
    +/**
    + * Returns the status code of the last request.
    + * @return {number} The status code of the last request.
    + */
    +ChannelRequest.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * Returns the session id for this channel.
    + *
    + * @return {string|undefined} The session ID.
    + */
    +ChannelRequest.prototype.getSessionId = function() {
    +  return this.sid_;
    +};
    +
    +
    +/**
    + * Returns the request id for this request. Each request has a unique request
    + * id and the request IDs are a sequential increasing count.
    + *
    + * @return {string|number|undefined} The request ID.
    + */
    +ChannelRequest.prototype.getRequestId = function() {
    +  return this.rid_;
    +};
    +
    +
    +/**
    + * Returns the data for a post, if this request is a post.
    + *
    + * @return {?string} The POST data provided by the request initiator.
    + */
    +ChannelRequest.prototype.getPostData = function() {
    +  return this.postData_;
    +};
    +
    +
    +/**
    + * Returns the XhrIo request object.
    + *
    + * @return {?goog.net.XhrIo} Any XhrIo request created for this object.
    + */
    +ChannelRequest.prototype.getXhr = function() {
    +  return this.xmlHttp_;
    +};
    +
    +
    +/**
    + * Returns the time that the request started, if it has started.
    + *
    + * @return {?number} The time the request started, as returned by goog.now().
    + */
    +ChannelRequest.prototype.getRequestStartTime = function() {
    +  return this.requestStartTime_;
    +};
    +
    +
    +/**
    + * Helper to call the callback's onRequestData, which catches any
    + * exception and cleans up the request.
    + * @param {string} data The request data.
    + * @private
    + */
    +ChannelRequest.prototype.safeOnRequestData_ = function(data) {
    +  /** @preserveTry */
    +  try {
    +    this.channel_.onRequestData(this, data);
    +    var stats = requestStats.ServerReachability;
    +    requestStats.notifyServerReachabilityEvent(stats.BACK_CHANNEL_ACTIVITY);
    +  } catch (e) {
    +    // Dump debug info, but keep going without closing the channel.
    +    this.channelDebug_.dumpException(
    +        e, 'Error in httprequest callback');
    +  }
    +};
    +
    +
    +/**
    + * Convenience factory method.
    + *
    + * @param {Channel} channel The channel object that owns this request.
    + * @param {WebChannelDebug} channelDebug A WebChannelDebug to use for logging.
    + * @param {string=} opt_sessionId  The session id for the channel.
    + * @param {string|number=} opt_requestId  The request id for this request.
    + * @param {number=} opt_retryId  The retry id for this request.
    + * @return {!ChannelRequest} The created channel request.
    + */
    +ChannelRequest.createChannelRequest = function(channel, channelDebug,
    +    opt_sessionId, opt_requestId, opt_retryId) {
    +  return new ChannelRequest(channel, channelDebug, opt_sessionId, opt_requestId,
    +      opt_retryId);
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.html
    new file mode 100644
    index 0000000000..bf08e98dec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.net.webChannel.ChannelRequest</title>
    +<script src="../../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.webChannel.channelRequestTest');
    +</script>
    +<div id="debug" style="font-size: small"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.js
    new file mode 100644
    index 0000000000..38f5e8faef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/channelrequest_test.js
    @@ -0,0 +1,310 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.net.webChannel.ChannelRequest.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.channelRequestTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.functions');
    +goog.require('goog.labs.net.webChannel.ChannelRequest');
    +goog.require('goog.labs.net.webChannel.WebChannelDebug');
    +goog.require('goog.labs.net.webChannel.requestStats');
    +goog.require('goog.labs.net.webChannel.requestStats.ServerReachability');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIo');
    +goog.require('goog.testing.recordFunction');
    +
    +goog.setTestOnly('goog.labs.net.webChannel.channelRequestTest');
    +
    +
    +var channelRequest;
    +var mockChannel;
    +var mockClock;
    +var stubs;
    +var xhrIo;
    +var reachabilityEvents;
    +
    +
    +/**
    + * Time to wait for a network request to time out, before aborting.
    + */
    +var WATCHDOG_TIME = 2000;
    +
    +
    +/**
    + * Time to throttle readystatechange events.
    + */
    +var THROTTLE_TIME = 500;
    +
    +
    +/**
    + * A really long time - used to make sure no more timeouts will fire.
    + */
    +var ALL_DAY_MS = 1000 * 60 * 60 * 24;
    +
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +  reachabilityEvents = {};
    +  stubs = new goog.testing.PropertyReplacer();
    +
    +  // Mock out the stat notification code.
    +  var notifyServerReachabilityEvent = function(reachabilityType) {
    +    if (!reachabilityEvents[reachabilityType]) {
    +      reachabilityEvents[reachabilityType] = 0;
    +    }
    +    reachabilityEvents[reachabilityType]++;
    +  };
    +  stubs.set(goog.labs.net.webChannel.requestStats,
    +      'notifyServerReachabilityEvent', notifyServerReachabilityEvent);
    +}
    +
    +
    +function tearDown() {
    +  stubs.reset();
    +  mockClock.uninstall();
    +}
    +
    +
    +
    +/**
    + * Constructs a duck-type WebChannelBase that tracks the completed requests.
    + * @constructor
    + * @struct
    + * @final
    + */
    +function MockWebChannelBase() {
    +  this.isClosed = function() {
    +    return false;
    +  };
    +  this.isActive = function() {
    +    return true;
    +  };
    +  this.shouldUseSecondaryDomains = function() {
    +    return false;
    +  };
    +  this.completedRequests = [];
    +  this.onRequestComplete = function(request) {
    +    this.completedRequests.push(request);
    +  };
    +  this.onRequestData = function(request, data) {};
    +}
    +
    +
    +/**
    + * Creates a real ChannelRequest object, with some modifications for
    + * testability:
    + * <ul>
    + * <li>The channel is a mock channel.
    + * <li>The new watchdogTimeoutCallCount property tracks onWatchDogTimeout_()
    + *     calls.
    + * <li>The timeout is set to WATCHDOG_TIME.
    + * </ul>
    + */
    +function createChannelRequest() {
    +  xhrIo = new goog.testing.net.XhrIo();
    +  xhrIo.abort = xhrIo.abort || function() {
    +    this.active_ = false;
    +  };
    +
    +  // Install mock channel and no-op debug logger.
    +  mockChannel = new MockWebChannelBase();
    +  channelRequest = new goog.labs.net.webChannel.ChannelRequest(
    +      mockChannel,
    +      new goog.labs.net.webChannel.WebChannelDebug());
    +
    +  // Install test XhrIo.
    +  mockChannel.createXhrIo = function() {
    +    return xhrIo;
    +  };
    +
    +  // Install watchdogTimeoutCallCount.
    +  channelRequest.watchdogTimeoutCallCount = 0;
    +  channelRequest.originalOnWatchDogTimeout = channelRequest.onWatchDogTimeout_;
    +  channelRequest.onWatchDogTimeout_ = function() {
    +    channelRequest.watchdogTimeoutCallCount++;
    +    return channelRequest.originalOnWatchDogTimeout();
    +  };
    +
    +  channelRequest.setTimeout(WATCHDOG_TIME);
    +}
    +
    +
    +/**
    + * Run through the lifecycle of a long lived request, checking that the right
    + * network events are reported.
    + */
    +function testNetworkEvents() {
    +  createChannelRequest();
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  checkReachabilityEvents(1, 0, 0, 0);
    +  if (goog.labs.net.webChannel.ChannelRequest.supportsXhrStreaming()) {
    +    xhrIo.simulatePartialResponse('17\nI am a BC Message');
    +    checkReachabilityEvents(1, 0, 0, 1);
    +    xhrIo.simulatePartialResponse('23\nI am another BC Message');
    +    checkReachabilityEvents(1, 0, 0, 2);
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 2);
    +  } else {
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 0);
    +  }
    +}
    +
    +
    +/**
    + * Test throttling of readystatechange events.
    + */
    +function testNetworkEvents_throttleReadyStateChange() {
    +  createChannelRequest();
    +  channelRequest.setReadyStateChangeThrottle(THROTTLE_TIME);
    +
    +  var recordedHandler =
    +      goog.testing.recordFunction(channelRequest.xmlHttpHandler_);
    +  stubs.set(channelRequest, 'xmlHttpHandler_', recordedHandler);
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  assertEquals(1, recordedHandler.getCallCount());
    +
    +  checkReachabilityEvents(1, 0, 0, 0);
    +  if (goog.labs.net.webChannel.ChannelRequest.supportsXhrStreaming()) {
    +    xhrIo.simulatePartialResponse('17\nI am a BC Message');
    +    checkReachabilityEvents(1, 0, 0, 1);
    +    assertEquals(3, recordedHandler.getCallCount());
    +
    +    // Second event should be throttled
    +    xhrIo.simulatePartialResponse('23\nI am another BC Message');
    +    assertEquals(3, recordedHandler.getCallCount());
    +
    +    xhrIo.simulatePartialResponse('27\nI am yet another BC Message');
    +    assertEquals(3, recordedHandler.getCallCount());
    +    mockClock.tick(THROTTLE_TIME);
    +
    +    checkReachabilityEvents(1, 0, 0, 3);
    +    // Only one more call because of throttling.
    +    assertEquals(4, recordedHandler.getCallCount());
    +
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 3);
    +    assertEquals(5, recordedHandler.getCallCount());
    +  } else {
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 0);
    +  }
    +}
    +
    +
    +/**
    + * Make sure that the request "completes" with an error when the timeout
    + * expires.
    + */
    +function testRequestTimeout() {
    +  createChannelRequest();
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  assertEquals(0, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(0, channelRequest.channel_.completedRequests.length);
    +
    +  // Watchdog timeout.
    +  mockClock.tick(WATCHDOG_TIME);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +  assertFalse(channelRequest.getSuccess());
    +
    +  // Make sure no more timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +
    +  checkReachabilityEvents(1, 0, 1, 0);
    +}
    +
    +
    +function testRequestTimeoutWithUnexpectedException() {
    +  createChannelRequest();
    +  channelRequest.channel_.createXhrIo = goog.functions.error('Weird error');
    +
    +  try {
    +    channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null);
    +    fail('Expected error');
    +  } catch (e) {
    +    assertEquals('Weird error', e.message);
    +  }
    +
    +  assertEquals(0, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(0, channelRequest.channel_.completedRequests.length);
    +
    +  // Watchdog timeout.
    +  mockClock.tick(WATCHDOG_TIME);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +  assertFalse(channelRequest.getSuccess());
    +
    +  // Make sure no more timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +
    +  checkReachabilityEvents(0, 0, 1, 0);
    +}
    +
    +
    +function testActiveXBlocked() {
    +  createChannelRequest();
    +  stubs.set(goog.global, 'ActiveXObject',
    +      goog.functions.error('Active X blocked'));
    +
    +  channelRequest.tridentGet(new goog.Uri('some_uri'), false);
    +  assertFalse(channelRequest.getSuccess());
    +  assertEquals(
    +      goog.labs.net.webChannel.ChannelRequest.Error.ACTIVE_X_BLOCKED,
    +      channelRequest.getLastError());
    +
    +  checkReachabilityEvents(0, 0, 0, 0);
    +}
    +
    +
    +function checkReachabilityEvents(reqMade, reqSucceeded, reqFail, backChannel) {
    +  var Reachability =
    +      goog.labs.net.webChannel.requestStats.ServerReachability;
    +  assertEquals(reqMade,
    +      reachabilityEvents[Reachability.REQUEST_MADE] || 0);
    +  assertEquals(reqSucceeded,
    +      reachabilityEvents[Reachability.REQUEST_SUCCEEDED] || 0);
    +  assertEquals(reqFail,
    +      reachabilityEvents[Reachability.REQUEST_FAILED] || 0);
    +  assertEquals(backChannel,
    +      reachabilityEvents[Reachability.BACK_CHANNEL_ACTIVITY] || 0);
    +}
    +
    +
    +function testDuplicatedRandomParams() {
    +  createChannelRequest();
    +  channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null, true,
    +      true /* opt_duplicateRandom */);
    +  var z = xhrIo.getLastUri().getParameterValue('zx');
    +  var z1 = xhrIo.getLastUri().getParameterValue('zx1');
    +  assertTrue(goog.isDefAndNotNull(z));
    +  assertTrue(goog.isDefAndNotNull(z1));
    +  assertEquals(z1, z);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/connectionstate.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/connectionstate.js
    new file mode 100644
    index 0000000000..10a43bdf23
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/connectionstate.js
    @@ -0,0 +1,50 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This class manages the network connectivity state.
    + *
    + * Some of the connectivity state may be exposed to the client code in future,
    + * e.g. the initial handshake state, in order to save one RTT when a channel
    + * has to be reestablished. TODO(user).
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.ConnectionState');
    +
    +
    +
    +/**
    + * The connectivity state of the channel.
    + *
    + * @constructor
    + * @struct
    + */
    +goog.labs.net.webChannel.ConnectionState = function() {
    +  /**
    +   * Handshake result.
    +   * @type {Array<string>}
    +   */
    +  this.handshakeResult = null;
    +
    +  /**
    +   * The result of checking if there is a buffering proxy in the network.
    +   * True means the connection is buffered, False means unbuffered,
    +   * null means that the result is not available.
    +   * @type {?boolean}
    +   */
    +  this.bufferingProxyResult = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool.js
    new file mode 100644
    index 0000000000..3614f4a48a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool.js
    @@ -0,0 +1,278 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A pool of forward channel requests to enable real-time
    + * messaging from the client to server.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.ForwardChannelRequestPool');
    +
    +goog.require('goog.array');
    +goog.require('goog.string');
    +goog.require('goog.structs.Set');
    +
    +goog.scope(function() {
    +// type checking only (no require)
    +var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
    +
    +
    +
    +/**
    + * This class represents the state of all forward channel requests.
    + *
    + * @param {number=} opt_maxPoolSize The maximum pool size.
    + *
    + * @constructor
    + * @final
    + */
    +goog.labs.net.webChannel.ForwardChannelRequestPool = function(opt_maxPoolSize) {
    +  /**
    +   * THe max pool size as configured.
    +   *
    +   * @private {number}
    +   */
    +  this.maxPoolSizeConfigured_ = opt_maxPoolSize ||
    +          goog.labs.net.webChannel.ForwardChannelRequestPool.MAX_POOL_SIZE_;
    +
    +  /**
    +   * The current size limit of the request pool. This limit is meant to be
    +   * read-only after the channel is fully opened.
    +   *
    +   * If SPDY is enabled, set it to the max pool size, which is also
    +   * configurable.
    +   *
    +   * @private {number}
    +   */
    +  this.maxSize_ = ForwardChannelRequestPool.isSpdyEnabled_() ?
    +      this.maxPoolSizeConfigured_ : 1;
    +
    +  /**
    +   * The container for all the pending request objects.
    +   *
    +   * @private {goog.structs.Set<ChannelRequest>}
    +   */
    +  this.requestPool_ = null;
    +
    +  if (this.maxSize_ > 1) {
    +    this.requestPool_ = new goog.structs.Set();
    +  }
    +
    +  /**
    +   * The single request object when the pool size is limited to one.
    +   *
    +   * @private {ChannelRequest}
    +   */
    +  this.request_ = null;
    +};
    +
    +var ForwardChannelRequestPool =
    +    goog.labs.net.webChannel.ForwardChannelRequestPool;
    +
    +
    +/**
    + * The default size limit of the request pool.
    + *
    + * @private {number}
    + */
    +ForwardChannelRequestPool.MAX_POOL_SIZE_ = 10;
    +
    +
    +/**
    + * @return {boolean} True if SPDY is enabled for the current page using
    + *     chrome specific APIs.
    + * @private
    + */
    +ForwardChannelRequestPool.isSpdyEnabled_ = function() {
    +  return !!(window.chrome && window.chrome.loadTimes &&
    +      window.chrome.loadTimes() && window.chrome.loadTimes().wasFetchedViaSpdy);
    +};
    +
    +
    +/**
    + * Once we know the client protocol (from the handshake), check if we need
    + * enable the request pool accordingly. This is more robust than using
    + * browser-internal APIs (specific to Chrome).
    + *
    + * @param {string} clientProtocol The client protocol
    + */
    +ForwardChannelRequestPool.prototype.applyClientProtocol = function(
    +    clientProtocol) {
    +  if (this.requestPool_) {
    +    return;
    +  }
    +
    +  if (goog.string.contains(clientProtocol, 'spdy') ||
    +      goog.string.contains(clientProtocol, 'quic')) {
    +    this.maxSize_ = this.maxPoolSizeConfigured_;
    +    this.requestPool_ = new goog.structs.Set();
    +    if (this.request_) {
    +      this.addRequest(this.request_);
    +      this.request_ = null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} True if the pool is full.
    + */
    +ForwardChannelRequestPool.prototype.isFull = function() {
    +  if (this.request_) {
    +    return true;
    +  }
    +
    +  if (this.requestPool_) {
    +    return this.requestPool_.getCount() >= this.maxSize_;
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * @return {number} The current size limit.
    + */
    +ForwardChannelRequestPool.prototype.getMaxSize = function() {
    +  return this.maxSize_;
    +};
    +
    +
    +/**
    + * @return {number} The number of pending requests in the pool.
    + */
    +ForwardChannelRequestPool.prototype.getRequestCount = function() {
    +  if (this.request_) {
    +    return 1;
    +  }
    +
    +  if (this.requestPool_) {
    +    return this.requestPool_.getCount();
    +  }
    +
    +  return 0;
    +};
    +
    +
    +/**
    + * @param {ChannelRequest} req The channel request.
    + * @return {boolean} True if the request is a included inside the pool.
    + */
    +ForwardChannelRequestPool.prototype.hasRequest = function(req) {
    +  if (this.request_) {
    +    return this.request_ == req;
    +  }
    +
    +  if (this.requestPool_) {
    +    return this.requestPool_.contains(req);
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Adds a new request to the pool.
    + *
    + * @param {!ChannelRequest} req The new channel request.
    + */
    +ForwardChannelRequestPool.prototype.addRequest = function(req) {
    +  if (this.requestPool_) {
    +    this.requestPool_.add(req);
    +  } else {
    +    this.request_ = req;
    +  }
    +};
    +
    +
    +/**
    + * Removes the given request from the pool.
    + *
    + * @param {ChannelRequest} req The channel request.
    + * @return {boolean} Whether the request has been removed from the pool.
    + */
    +ForwardChannelRequestPool.prototype.removeRequest = function(req) {
    +  if (this.request_ && this.request_ == req) {
    +    this.request_ = null;
    +    return true;
    +  }
    +
    +  if (this.requestPool_ && this.requestPool_.contains(req)) {
    +    this.requestPool_.remove(req);
    +    return true;
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Clears the pool and cancel all the pending requests.
    + */
    +ForwardChannelRequestPool.prototype.cancel = function() {
    +  if (this.request_) {
    +    this.request_.cancel();
    +    this.request_ = null;
    +    return;
    +  }
    +
    +  if (this.requestPool_ && !this.requestPool_.isEmpty()) {
    +    goog.array.forEach(this.requestPool_.getValues(), function(val) {
    +      val.cancel();
    +    });
    +    this.requestPool_.clear();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether there are any pending requests.
    + */
    +ForwardChannelRequestPool.prototype.hasPendingRequest = function() {
    +  return (this.request_ != null) ||
    +      (this.requestPool_ != null && !this.requestPool_.isEmpty());
    +};
    +
    +
    +/**
    + * Cancels all pending requests and force the completion of channel requests.
    + *
    + * Need go through the standard onRequestComplete logic to expose the max-retry
    + * failure in the standard way.
    + *
    + * @param {!function(!ChannelRequest)} onComplete The completion callback.
    + * @return {boolean} true if any request has been forced to complete.
    + */
    +ForwardChannelRequestPool.prototype.forceComplete = function(onComplete) {
    +  if (this.request_ != null) {
    +    this.request_.cancel();
    +    onComplete(this.request_);
    +    return true;
    +  }
    +
    +  if (this.requestPool_ && !this.requestPool_.isEmpty()) {
    +    goog.array.forEach(this.requestPool_.getValues(),
    +        function(val) {
    +          val.cancel();
    +          onComplete(val);
    +        });
    +    return true;
    +  }
    +
    +  return false;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.html
    new file mode 100644
    index 0000000000..04a6dced45
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.net.webChannel.ForwardChannelRequestPool</title>
    +<script src="../../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
    +</script>
    +<div id="debug" style="font-size: small"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.js
    new file mode 100644
    index 0000000000..1d192fdded
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/forwardchannelrequestpool_test.js
    @@ -0,0 +1,119 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for
    + * goog.labs.net.webChannel.ForwardChannelRequestPool.
    + * @suppress {accessControls} Private methods are accessed for test purposes.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
    +
    +goog.require('goog.labs.net.webChannel.ChannelRequest');
    +goog.require('goog.labs.net.webChannel.ForwardChannelRequestPool');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.net.webChannel.forwardChannelRequestPoolTest');
    +
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +var req = new goog.labs.net.webChannel.ChannelRequest(null, null);
    +
    +
    +function setUp() {
    +}
    +
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +}
    +
    +
    +function stubSpdyCheck(spdyEnabled) {
    +  propertyReplacer.set(goog.labs.net.webChannel.ForwardChannelRequestPool,
    +      'isSpdyEnabled_',
    +      function() {
    +        return spdyEnabled;
    +      });
    +}
    +
    +
    +function testSpdyEnabled() {
    +  stubSpdyCheck(true);
    +
    +  var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
    +  assertFalse(pool.isFull());
    +  assertEquals(0, pool.getRequestCount());
    +  pool.addRequest(req);
    +  assertTrue(pool.hasPendingRequest());
    +  assertTrue(pool.hasRequest(req));
    +  pool.removeRequest(req);
    +  assertFalse(pool.hasPendingRequest());
    +
    +  for (var i = 0; i < pool.getMaxSize(); i++) {
    +    pool.addRequest(new goog.labs.net.webChannel.ChannelRequest(null, null));
    +  }
    +  assertTrue(pool.isFull());
    +
    +  // do not fail
    +  pool.addRequest(req);
    +  assertTrue(pool.isFull());
    +}
    +
    +
    +function testSpdyNotEnabled() {
    +  stubSpdyCheck(false);
    +
    +  var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
    +  assertFalse(pool.isFull());
    +  assertEquals(0, pool.getRequestCount());
    +  pool.addRequest(req);
    +  assertTrue(pool.hasPendingRequest());
    +  assertTrue(pool.hasRequest(req));
    +  assertTrue(pool.isFull());
    +  pool.removeRequest(req);
    +  assertFalse(pool.hasPendingRequest());
    +
    +  // do not fail
    +  pool.addRequest(req);
    +  assertTrue(pool.isFull());
    +}
    +
    +
    +function testApplyClientProtocol() {
    +  stubSpdyCheck(false);
    +
    +  var pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
    +  assertEquals(1, pool.getMaxSize());
    +  pool.applyClientProtocol('spdy/3');
    +  assertTrue(pool.getMaxSize() > 1);
    +  pool.applyClientProtocol('foo-bar');   // no effect
    +  assertTrue(pool.getMaxSize() > 1);
    +
    +  pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
    +  assertEquals(1, pool.getMaxSize());
    +  pool.applyClientProtocol('quic/x');
    +  assertTrue(pool.getMaxSize() > 1);
    +
    +  stubSpdyCheck(true);
    +
    +  pool = new goog.labs.net.webChannel.ForwardChannelRequestPool();
    +  assertTrue(pool.getMaxSize() > 1);
    +  pool.applyClientProtocol('foo/3');  // no effect
    +  assertTrue(pool.getMaxSize() > 1);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/netutils.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/netutils.js
    new file mode 100644
    index 0000000000..5b9d7fba68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/netutils.js
    @@ -0,0 +1,162 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility functions for managing networking, such as
    + * testing network connectivity.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.netUtils');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.labs.net.webChannel.WebChannelDebug');
    +
    +goog.scope(function() {
    +var netUtils = goog.labs.net.webChannel.netUtils;
    +var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
    +
    +
    +/**
    + * Default timeout to allow for URI pings.
    + * @type {number}
    + */
    +netUtils.NETWORK_TIMEOUT = 10000;
    +
    +
    +/**
    + * Pings the network with an image URI to check if an error is a server error
    + * or user's network error.
    + *
    + * The caller needs to add a 'rand' parameter to make sure the response is
    + * not fulfilled by browser cache.
    + *
    + * @param {function(boolean)} callback The function to call back with results.
    + * @param {goog.Uri=} opt_imageUri The URI (of an image) to use for the network
    + *     test.
    + */
    +netUtils.testNetwork = function(callback, opt_imageUri) {
    +  var uri = opt_imageUri;
    +  if (!uri) {
    +    // default google.com image
    +    uri = new goog.Uri('//www.google.com/images/cleardot.gif');
    +
    +    if (!(goog.global.location && goog.global.location.protocol == 'http')) {
    +      uri.setScheme('https');  // e.g. chrome-extension
    +    }
    +    uri.makeUnique();
    +  }
    +
    +  netUtils.testLoadImage(uri.toString(), netUtils.NETWORK_TIMEOUT, callback);
    +};
    +
    +
    +/**
    + * Test loading the given image, retrying if necessary.
    + * @param {string} url URL to the image.
    + * @param {number} timeout Milliseconds before giving up.
    + * @param {function(boolean)} callback Function to call with results.
    + * @param {number} retries The number of times to retry.
    + * @param {number=} opt_pauseBetweenRetriesMS Optional number of milliseconds
    + *     between retries - defaults to 0.
    + */
    +netUtils.testLoadImageWithRetries = function(url, timeout, callback,
    +    retries, opt_pauseBetweenRetriesMS) {
    +  var channelDebug = new WebChannelDebug();
    +  channelDebug.debug('TestLoadImageWithRetries: ' + opt_pauseBetweenRetriesMS);
    +  if (retries == 0) {
    +    // no more retries, give up
    +    callback(false);
    +    return;
    +  }
    +
    +  var pauseBetweenRetries = opt_pauseBetweenRetriesMS || 0;
    +  retries--;
    +  netUtils.testLoadImage(url, timeout, function(succeeded) {
    +    if (succeeded) {
    +      callback(true);
    +    } else {
    +      // try again
    +      goog.global.setTimeout(function() {
    +        netUtils.testLoadImageWithRetries(url, timeout, callback,
    +            retries, pauseBetweenRetries);
    +      }, pauseBetweenRetries);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Test loading the given image.
    + * @param {string} url URL to the image.
    + * @param {number} timeout Milliseconds before giving up.
    + * @param {function(boolean)} callback Function to call with results.
    + */
    +netUtils.testLoadImage = function(url, timeout, callback) {
    +  var channelDebug = new WebChannelDebug();
    +  channelDebug.debug('TestLoadImage: loading ' + url);
    +  var img = new Image();
    +  img.onload = goog.partial(netUtils.imageCallback_, channelDebug, img,
    +      'TestLoadImage: loaded', true, callback);
    +  img.onerror = goog.partial(netUtils.imageCallback_, channelDebug, img,
    +      'TestLoadImage: error', false, callback);
    +  img.onabort = goog.partial(netUtils.imageCallback_, channelDebug, img,
    +      'TestLoadImage: abort', false, callback);
    +  img.ontimeout = goog.partial(netUtils.imageCallback_, channelDebug, img,
    +      'TestLoadImage: timeout', false, callback);
    +
    +  goog.global.setTimeout(function() {
    +    if (img.ontimeout) {
    +      img.ontimeout();
    +    }
    +  }, timeout);
    +  img.src = url;
    +};
    +
    +
    +/**
    + * Wrap the image callback with debug and cleanup logic.
    + * @param {!WebChannelDebug} channelDebug The WebChannelDebug object.
    + * @param {!Image} img The image element.
    + * @param {string} debugText The debug text.
    + * @param {boolean} result The result of image loading.
    + * @param {function(boolean)} callback The image callback.
    + * @private
    + */
    +netUtils.imageCallback_ = function(channelDebug, img, debugText, result,
    +    callback) {
    +  try {
    +    channelDebug.debug(debugText);
    +    netUtils.clearImageCallbacks_(img);
    +    callback(result);
    +  } catch (e) {
    +    channelDebug.dumpException(e);
    +  }
    +};
    +
    +
    +/**
    + * Clears handlers to avoid memory leaks.
    + * @param {Image} img The image to clear handlers from.
    + * @private
    + */
    +netUtils.clearImageCallbacks_ = function(img) {
    +  img.onload = null;
    +  img.onerror = null;
    +  img.onabort = null;
    +  img.ontimeout = null;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/requeststats.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/requeststats.js
    new file mode 100644
    index 0000000000..7407eabd92
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/requeststats.js
    @@ -0,0 +1,386 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Static utilities for collecting stats associated with
    + * ChannelRequest.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.requestStats');
    +goog.provide('goog.labs.net.webChannel.requestStats.Event');
    +goog.provide('goog.labs.net.webChannel.requestStats.ServerReachability');
    +goog.provide('goog.labs.net.webChannel.requestStats.ServerReachabilityEvent');
    +goog.provide('goog.labs.net.webChannel.requestStats.Stat');
    +goog.provide('goog.labs.net.webChannel.requestStats.StatEvent');
    +goog.provide('goog.labs.net.webChannel.requestStats.TimingEvent');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +
    +
    +goog.scope(function() {
    +var requestStats = goog.labs.net.webChannel.requestStats;
    +
    +
    +/**
    + * Events fired.
    + * @const
    + */
    +requestStats.Event = {};
    +
    +
    +/**
    + * Singleton event target for firing stat events
    + * @type {goog.events.EventTarget}
    + * @private
    + */
    +requestStats.statEventTarget_ = new goog.events.EventTarget();
    +
    +
    +/**
    + * The type of event that occurs every time some information about how reachable
    + * the server is is discovered.
    + */
    +requestStats.Event.SERVER_REACHABILITY_EVENT = 'serverreachability';
    +
    +
    +/**
    + * Types of events which reveal information about the reachability of the
    + * server.
    + * @enum {number}
    + */
    +requestStats.ServerReachability = {
    +  REQUEST_MADE: 1,
    +  REQUEST_SUCCEEDED: 2,
    +  REQUEST_FAILED: 3,
    +  BACK_CHANNEL_ACTIVITY: 4
    +};
    +
    +
    +
    +/**
    + * Event class for SERVER_REACHABILITY_EVENT.
    + *
    + * @param {goog.events.EventTarget} target The stat event target for
    +       the channel.
    + * @param {requestStats.ServerReachability} reachabilityType
    + *     The reachability event type.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +requestStats.ServerReachabilityEvent = function(target, reachabilityType) {
    +  goog.events.Event.call(this,
    +      requestStats.Event.SERVER_REACHABILITY_EVENT, target);
    +
    +  /**
    +   * @type {requestStats.ServerReachability}
    +   */
    +  this.reachabilityType = reachabilityType;
    +};
    +goog.inherits(requestStats.ServerReachabilityEvent, goog.events.Event);
    +
    +
    +/**
    + * Notify the channel that a particular fine grained network event has occurred.
    + * Should be considered package-private.
    + * @param {requestStats.ServerReachability} reachabilityType
    + *     The reachability event type.
    + */
    +requestStats.notifyServerReachabilityEvent = function(reachabilityType) {
    +  var target = requestStats.statEventTarget_;
    +  target.dispatchEvent(
    +      new requestStats.ServerReachabilityEvent(target, reachabilityType));
    +};
    +
    +
    +/**
    + * Stat Event that fires when things of interest happen that may be useful for
    + * applications to know about for stats or debugging purposes.
    + */
    +requestStats.Event.STAT_EVENT = 'statevent';
    +
    +
    +/**
    + * Enum that identifies events for statistics that are interesting to track.
    + * @enum {number}
    + */
    +requestStats.Stat = {
    +  /** Event indicating a new connection attempt. */
    +  CONNECT_ATTEMPT: 0,
    +
    +  /** Event indicating a connection error due to a general network problem. */
    +  ERROR_NETWORK: 1,
    +
    +  /**
    +   * Event indicating a connection error that isn't due to a general network
    +   * problem.
    +   */
    +  ERROR_OTHER: 2,
    +
    +  /** Event indicating the start of test stage one. */
    +  TEST_STAGE_ONE_START: 3,
    +
    +  /** Event indicating the start of test stage two. */
    +  TEST_STAGE_TWO_START: 4,
    +
    +  /** Event indicating the first piece of test data was received. */
    +  TEST_STAGE_TWO_DATA_ONE: 5,
    +
    +  /**
    +   * Event indicating that the second piece of test data was received and it was
    +   * recieved separately from the first.
    +   */
    +  TEST_STAGE_TWO_DATA_TWO: 6,
    +
    +  /** Event indicating both pieces of test data were received simultaneously. */
    +  TEST_STAGE_TWO_DATA_BOTH: 7,
    +
    +  /** Event indicating stage one of the test request failed. */
    +  TEST_STAGE_ONE_FAILED: 8,
    +
    +  /** Event indicating stage two of the test request failed. */
    +  TEST_STAGE_TWO_FAILED: 9,
    +
    +  /**
    +   * Event indicating that a buffering proxy is likely between the client and
    +   * the server.
    +   */
    +  PROXY: 10,
    +
    +  /**
    +   * Event indicating that no buffering proxy is likely between the client and
    +   * the server.
    +   */
    +  NOPROXY: 11,
    +
    +  /** Event indicating an unknown SID error. */
    +  REQUEST_UNKNOWN_SESSION_ID: 12,
    +
    +  /** Event indicating a bad status code was received. */
    +  REQUEST_BAD_STATUS: 13,
    +
    +  /** Event indicating incomplete data was received */
    +  REQUEST_INCOMPLETE_DATA: 14,
    +
    +  /** Event indicating bad data was received */
    +  REQUEST_BAD_DATA: 15,
    +
    +  /** Event indicating no data was received when data was expected. */
    +  REQUEST_NO_DATA: 16,
    +
    +  /** Event indicating a request timeout. */
    +  REQUEST_TIMEOUT: 17,
    +
    +  /**
    +   * Event indicating that the server never received our hanging GET and so it
    +   * is being retried.
    +   */
    +  BACKCHANNEL_MISSING: 18,
    +
    +  /**
    +   * Event indicating that we have determined that our hanging GET is not
    +   * receiving data when it should be. Thus it is dead dead and will be retried.
    +   */
    +  BACKCHANNEL_DEAD: 19,
    +
    +  /**
    +   * The browser declared itself offline during the lifetime of a request, or
    +   * was offline when a request was initially made.
    +   */
    +  BROWSER_OFFLINE: 20,
    +
    +  /** ActiveX is blocked by the machine's admin settings. */
    +  ACTIVE_X_BLOCKED: 21
    +};
    +
    +
    +
    +/**
    + * Event class for STAT_EVENT.
    + *
    + * @param {goog.events.EventTarget} eventTarget The stat event target for
    +       the channel.
    + * @param {requestStats.Stat} stat The stat.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +requestStats.StatEvent = function(eventTarget, stat) {
    +  goog.events.Event.call(this, requestStats.Event.STAT_EVENT, eventTarget);
    +
    +  /**
    +   * The stat
    +   * @type {requestStats.Stat}
    +   */
    +  this.stat = stat;
    +
    +};
    +goog.inherits(requestStats.StatEvent, goog.events.Event);
    +
    +
    +/**
    + * Returns the singleton event target for stat events.
    + * @return {goog.events.EventTarget} The event target for stat events.
    + */
    +requestStats.getStatEventTarget = function() {
    +  return requestStats.statEventTarget_;
    +};
    +
    +
    +/**
    + * Helper function to call the stat event callback.
    + * @param {requestStats.Stat} stat The stat.
    + */
    +requestStats.notifyStatEvent = function(stat) {
    +  var target = requestStats.statEventTarget_;
    +  target.dispatchEvent(new requestStats.StatEvent(target, stat));
    +};
    +
    +
    +/**
    + * An event that fires when POST requests complete successfully, indicating
    + * the size of the POST and the round trip time.
    + */
    +requestStats.Event.TIMING_EVENT = 'timingevent';
    +
    +
    +
    +/**
    + * Event class for requestStats.Event.TIMING_EVENT
    + *
    + * @param {goog.events.EventTarget} target The stat event target for
    +       the channel.
    + * @param {number} size The number of characters in the POST data.
    + * @param {number} rtt The total round trip time from POST to response in MS.
    + * @param {number} retries The number of times the POST had to be retried.
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +requestStats.TimingEvent = function(target, size, rtt, retries) {
    +  goog.events.Event.call(this,
    +      requestStats.Event.TIMING_EVENT, target);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.size = size;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.rtt = rtt;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.retries = retries;
    +
    +};
    +goog.inherits(requestStats.TimingEvent, goog.events.Event);
    +
    +
    +/**
    + * Helper function to notify listeners about POST request performance.
    + *
    + * @param {number} size Number of characters in the POST data.
    + * @param {number} rtt The amount of time from POST start to response.
    + * @param {number} retries The number of times the POST had to be retried.
    + */
    +requestStats.notifyTimingEvent = function(size, rtt, retries) {
    +  var target = requestStats.statEventTarget_;
    +  target.dispatchEvent(
    +      new requestStats.TimingEvent(
    +          target, size, rtt, retries));
    +};
    +
    +
    +/**
    + * Allows the application to set an execution hooks for when a channel
    + * starts processing requests. This is useful to track timing or logging
    + * special information. The function takes no parameters and return void.
    + * @param {Function} startHook  The function for the start hook.
    + */
    +requestStats.setStartThreadExecutionHook = function(startHook) {
    +  requestStats.startExecutionHook_ = startHook;
    +};
    +
    +
    +/**
    + * Allows the application to set an execution hooks for when a channel
    + * stops processing requests. This is useful to track timing or logging
    + * special information. The function takes no parameters and return void.
    + * @param {Function} endHook  The function for the end hook.
    + */
    +requestStats.setEndThreadExecutionHook = function(endHook) {
    +  requestStats.endExecutionHook_ = endHook;
    +};
    +
    +
    +/**
    + * Application provided execution hook for the start hook.
    + *
    + * @type {Function}
    + * @private
    + */
    +requestStats.startExecutionHook_ = function() { };
    +
    +
    +/**
    + * Application provided execution hook for the end hook.
    + *
    + * @type {Function}
    + * @private
    + */
    +requestStats.endExecutionHook_ = function() { };
    +
    +
    +/**
    + * Helper function to call the start hook
    + */
    +requestStats.onStartExecution = function() {
    +  requestStats.startExecutionHook_();
    +};
    +
    +
    +/**
    + * Helper function to call the end hook
    + */
    +requestStats.onEndExecution = function() {
    +  requestStats.endExecutionHook_();
    +};
    +
    +
    +/**
    + * Wrapper around SafeTimeout which calls the start and end execution hooks
    + * with a try...finally block.
    + * @param {Function} fn The callback function.
    + * @param {number} ms The time in MS for the timer.
    + * @return {number} The ID of the timer.
    + */
    +requestStats.setTimeout = function(fn, ms) {
    +  if (!goog.isFunction(fn)) {
    +    throw Error('Fn must not be null and must be a function');
    +  }
    +  return goog.global.setTimeout(function() {
    +    requestStats.onStartExecution();
    +    try {
    +      fn();
    +    } finally {
    +      requestStats.onEndExecution();
    +    }
    +  }, ms);
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase.js
    new file mode 100644
    index 0000000000..903e721451
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase.js
    @@ -0,0 +1,2097 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base WebChannel implementation.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.WebChannelBase');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.debug.TextFormatter');
    +goog.require('goog.json');
    +goog.require('goog.labs.net.webChannel.BaseTestChannel');
    +goog.require('goog.labs.net.webChannel.Channel');
    +goog.require('goog.labs.net.webChannel.ChannelRequest');
    +goog.require('goog.labs.net.webChannel.ConnectionState');
    +goog.require('goog.labs.net.webChannel.ForwardChannelRequestPool');
    +goog.require('goog.labs.net.webChannel.WebChannelDebug');
    +goog.require('goog.labs.net.webChannel.Wire');
    +goog.require('goog.labs.net.webChannel.WireV8');
    +goog.require('goog.labs.net.webChannel.netUtils');
    +goog.require('goog.labs.net.webChannel.requestStats');
    +goog.require('goog.labs.net.webChannel.requestStats.Stat');
    +goog.require('goog.log');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.structs');
    +goog.require('goog.structs.CircularBuffer');
    +
    +goog.scope(function() {
    +var BaseTestChannel = goog.labs.net.webChannel.BaseTestChannel;
    +var ChannelRequest = goog.labs.net.webChannel.ChannelRequest;
    +var ConnectionState = goog.labs.net.webChannel.ConnectionState;
    +var ForwardChannelRequestPool =
    +    goog.labs.net.webChannel.ForwardChannelRequestPool;
    +var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
    +var Wire = goog.labs.net.webChannel.Wire;
    +var WireV8 = goog.labs.net.webChannel.WireV8;
    +var netUtils = goog.labs.net.webChannel.netUtils;
    +var requestStats = goog.labs.net.webChannel.requestStats;
    +
    +
    +
    +/**
    + * This WebChannel implementation is branched off goog.net.BrowserChannel
    + * for now. Ongoing changes to goog.net.BrowserChannel will be back
    + * ported to this implementation as needed.
    + *
    + * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the
    + *        WebChannel instance.
    + * @param {string=} opt_clientVersion An application-specific version number
    + *        that is sent to the server when connected.
    + * @param {!ConnectionState=} opt_conn Previously determined connection
    + *        conditions.
    + * @constructor
    + * @struct
    + * @implements {goog.labs.net.webChannel.Channel}
    + */
    +goog.labs.net.webChannel.WebChannelBase = function(opt_options,
    +    opt_clientVersion, opt_conn) {
    +  /**
    +   * The application specific version that is passed to the server.
    +   * @private {?string}
    +   */
    +  this.clientVersion_ = opt_clientVersion || null;
    +
    +  /**
    +   * An array of queued maps that need to be sent to the server.
    +   * @private {!Array<Wire.QueuedMap>}
    +   */
    +  this.outgoingMaps_ = [];
    +
    +  /**
    +   * An array of dequeued maps that we have either received a non-successful
    +   * response for, or no response at all, and which therefore may or may not
    +   * have been received by the server.
    +   * @private {!Array<Wire.QueuedMap>}
    +   */
    +  this.pendingMaps_ = [];
    +
    +  /**
    +   * The channel debug used for logging
    +   * @private {!WebChannelDebug}
    +   */
    +  this.channelDebug_ = new WebChannelDebug();
    +
    +  /**
    +   * Previous connectivity test results.
    +   * @private {!ConnectionState}
    +   */
    +  this.ConnState_ = opt_conn || new ConnectionState();
    +
    +  /**
    +   * Extra HTTP headers to add to all the requests sent to the server.
    +   * @private {Object}
    +   */
    +  this.extraHeaders_ = null;
    +
    +  /**
    +   * Extra parameters to add to all the requests sent to the server.
    +   * @private {Object}
    +   */
    +  this.extraParams_ = null;
    +
    +  /**
    +   * The ChannelRequest object for the backchannel.
    +   * @private {ChannelRequest}
    +   */
    +  this.backChannelRequest_ = null;
    +
    +  /**
    +   * The relative path (in the context of the the page hosting the browser
    +   * channel) for making requests to the server.
    +   * @private {?string}
    +   */
    +  this.path_ = null;
    +
    +  /**
    +   * The absolute URI for the forwardchannel request.
    +   * @private {goog.Uri}
    +   */
    +  this.forwardChannelUri_ = null;
    +
    +  /**
    +   * The absolute URI for the backchannel request.
    +   * @private {goog.Uri}
    +   */
    +  this.backChannelUri_ = null;
    +
    +  /**
    +   * A subdomain prefix for using a subdomain in IE for the backchannel
    +   * requests.
    +   * @private {?string}
    +   */
    +  this.hostPrefix_ = null;
    +
    +  /**
    +   * Whether we allow the use of a subdomain in IE for the backchannel requests.
    +   * @private {boolean}
    +   */
    +  this.allowHostPrefix_ = true;
    +
    +  /**
    +   * The next id to use for the RID (request identifier) parameter. This
    +   * identifier uniquely identifies the forward channel request.
    +   * @private {number}
    +   */
    +  this.nextRid_ = 0;
    +
    +  /**
    +   * The id to use for the next outgoing map. This identifier uniquely
    +   * identifies a sent map.
    +   * @private {number}
    +   */
    +  this.nextMapId_ = 0;
    +
    +  /**
    +   * Whether to fail forward-channel requests after one try or a few tries.
    +   * @private {boolean}
    +   */
    +  this.failFast_ = false;
    +
    +  /**
    +   * The handler that receive callbacks for state changes and data.
    +   * @private {goog.labs.net.webChannel.WebChannelBase.Handler}
    +   */
    +  this.handler_ = null;
    +
    +  /**
    +   * Timer identifier for asynchronously making a forward channel request.
    +   * @private {?number}
    +   */
    +  this.forwardChannelTimerId_ = null;
    +
    +  /**
    +   * Timer identifier for asynchronously making a back channel request.
    +   * @private {?number}
    +   */
    +  this.backChannelTimerId_ = null;
    +
    +  /**
    +   * Timer identifier for the timer that waits for us to retry the backchannel
    +   * in the case where it is dead and no longer receiving data.
    +   * @private {?number}
    +   */
    +  this.deadBackChannelTimerId_ = null;
    +
    +  /**
    +   * The TestChannel object which encapsulates the logic for determining
    +   * interesting network conditions about the client.
    +   * @private {BaseTestChannel}
    +   */
    +  this.connectionTest_ = null;
    +
    +  /**
    +   * Whether the client's network conditions can support chunked responses.
    +   * @private {?boolean}
    +   */
    +  this.useChunked_ = null;
    +
    +  /**
    +   * Whether chunked mode is allowed. In certain debugging situations, it's
    +   * useful to disable this.
    +   * @private {boolean}
    +   */
    +  this.allowChunkedMode_ = true;
    +
    +  /**
    +   * The array identifier of the last array received from the server for the
    +   * backchannel request.
    +   * @private {number}
    +   */
    +  this.lastArrayId_ = -1;
    +
    +  /**
    +   * The array id of the last array sent by the server that we know about.
    +   * @private {number}
    +   */
    +  this.lastPostResponseArrayId_ = -1;
    +
    +  /**
    +   * The last status code received.
    +   * @private {number}
    +   */
    +  this.lastStatusCode_ = -1;
    +
    +  /**
    +   * Number of times we have retried the current forward channel request.
    +   * @private {number}
    +   */
    +  this.forwardChannelRetryCount_ = 0;
    +
    +  /**
    +   * Number of times in a row that we have retried the current back channel
    +   * request and received no data.
    +   * @private {number}
    +   */
    +  this.backChannelRetryCount_ = 0;
    +
    +  /**
    +   * The attempt id for the current back channel request. Starts at 1 and
    +   * increments for each reconnect. The server uses this to log if our
    +   * connection is flaky or not.
    +   * @private {number}
    +   */
    +  this.backChannelAttemptId_ = 0;
    +
    +  /**
    +   * The base part of the time before firing next retry request. Default is 5
    +   * seconds. Note that a random delay is added (see {@link retryDelaySeedMs_})
    +   * for all retries, and linear backoff is applied to the sum for subsequent
    +   * retries.
    +   * @private {number}
    +   */
    +  this.baseRetryDelayMs_ = 5 * 1000;
    +
    +  /**
    +   * A random time between 0 and this number of MS is added to the
    +   * {@link baseRetryDelayMs_}. Default is 10 seconds.
    +   * @private {number}
    +   */
    +  this.retryDelaySeedMs_ = 10 * 1000;
    +
    +  /**
    +   * Maximum number of attempts to connect to the server for forward channel
    +   * requests. Defaults to 2.
    +   * @private {number}
    +   */
    +  this.forwardChannelMaxRetries_ = 2;
    +
    +  /**
    +   * The timeout in milliseconds for a forward channel request. Defaults to 20
    +   * seconds. Note that part of this timeout can be randomized.
    +   * @private {number}
    +   */
    +  this.forwardChannelRequestTimeoutMs_ = 20 * 1000;
    +
    +  /**
    +   * A throttle time in ms for readystatechange events for the backchannel.
    +   * Useful for throttling when ready state is INTERACTIVE (partial data).
    +   *
    +   * This throttle is useful if the server sends large data chunks down the
    +   * backchannel.  It prevents examining XHR partial data on every readystate
    +   * change event.  This is useful because large chunks can trigger hundreds
    +   * of readystatechange events, each of which takes ~5ms or so to handle,
    +   * in turn making the UI unresponsive for a significant period.
    +   *
    +   * If set to zero no throttle is used.
    +   * @private {number}
    +   */
    +  this.readyStateChangeThrottleMs_ = 0;
    +
    +  /**
    +   * Whether cross origin requests are supported for the channel.
    +   *
    +   * See {@link goog.net.XhrIo#setWithCredentials}.
    +   * @private {boolean}
    +   */
    +  this.supportsCrossDomainXhrs_ = false;
    +
    +  /**
    +   * The current session id.
    +   * @private {string}
    +   */
    +  this.sid_ = '';
    +
    +  /**
    +   * The current ChannelRequest pool for the forward channel.
    +   * @private {!ForwardChannelRequestPool}
    +   */
    +  this.forwardChannelRequestPool_ = new ForwardChannelRequestPool(
    +      opt_options && opt_options.concurrentRequestLimit);
    +
    +  /**
    +   * The V8 codec.
    +   * @private {!WireV8}
    +   */
    +  this.wireCodec_ = new WireV8();
    +};
    +
    +var WebChannelBase = goog.labs.net.webChannel.WebChannelBase;
    +
    +
    +/**
    + * The channel version that we negotiated with the server for this session.
    + * Starts out as the version we request, and then is changed to the negotiated
    + * version after the initial open.
    + * @private {number}
    + */
    +WebChannelBase.prototype.channelVersion_ = Wire.LATEST_CHANNEL_VERSION;
    +
    +
    +/**
    + * Enum type for the channel state machine.
    + * @enum {number}
    + */
    +WebChannelBase.State = {
    +  /** The channel is closed. */
    +  CLOSED: 0,
    +
    +  /** The channel has been initialized but hasn't yet initiated a connection. */
    +  INIT: 1,
    +
    +  /** The channel is in the process of opening a connection to the server. */
    +  OPENING: 2,
    +
    +  /** The channel is open. */
    +  OPENED: 3
    +};
    +
    +
    +/**
    + * The current state of the WebChannel.
    + * @private {!WebChannelBase.State}
    + */
    +WebChannelBase.prototype.state_ = WebChannelBase.State.INIT;
    +
    +
    +/**
    + * The timeout in milliseconds for a forward channel request.
    + * @type {number}
    + */
    +WebChannelBase.FORWARD_CHANNEL_RETRY_TIMEOUT = 20 * 1000;
    +
    +
    +/**
    + * Maximum number of attempts to connect to the server for back channel
    + * requests.
    + * @type {number}
    + */
    +WebChannelBase.BACK_CHANNEL_MAX_RETRIES = 3;
    +
    +
    +/**
    + * A number in MS of how long we guess the maxmium amount of time a round trip
    + * to the server should take. In the future this could be substituted with a
    + * real measurement of the RTT.
    + * @type {number}
    + */
    +WebChannelBase.RTT_ESTIMATE = 3 * 1000;
    +
    +
    +/**
    + * When retrying for an inactive channel, we will multiply the total delay by
    + * this number.
    + * @type {number}
    + */
    +WebChannelBase.INACTIVE_CHANNEL_RETRY_FACTOR = 2;
    +
    +
    +/**
    + * Enum type for identifying an error.
    + * @enum {number}
    + */
    +WebChannelBase.Error = {
    +  /** Value that indicates no error has occurred. */
    +  OK: 0,
    +
    +  /** An error due to a request failing. */
    +  REQUEST_FAILED: 2,
    +
    +  /** An error due to the user being logged out. */
    +  LOGGED_OUT: 4,
    +
    +  /** An error due to server response which contains no data. */
    +  NO_DATA: 5,
    +
    +  /** An error due to a server response indicating an unknown session id */
    +  UNKNOWN_SESSION_ID: 6,
    +
    +  /** An error due to a server response requesting to stop the channel. */
    +  STOP: 7,
    +
    +  /** A general network error. */
    +  NETWORK: 8,
    +
    +  /** An error due to bad data being returned from the server. */
    +  BAD_DATA: 10,
    +
    +  /** An error due to a response that is not parsable. */
    +  BAD_RESPONSE: 11,
    +
    +  /** ActiveX is blocked by the machine's admin settings. */
    +  ACTIVE_X_BLOCKED: 12
    +};
    +
    +
    +/**
    + * Internal enum type for the two channel types.
    + * @enum {number}
    + * @private
    + */
    +WebChannelBase.ChannelType_ = {
    +  FORWARD_CHANNEL: 1,
    +
    +  BACK_CHANNEL: 2
    +};
    +
    +
    +/**
    + * The maximum number of maps that can be sent in one POST. Should match
    + * MAX_MAPS_PER_REQUEST on the server code.
    + * @type {number}
    + * @private
    + */
    +WebChannelBase.MAX_MAPS_PER_REQUEST_ = 1000;
    +
    +
    +/**
    + * A guess at a cutoff at which to no longer assume the backchannel is dead
    + * when we are slow to receive data. Number in bytes.
    + *
    + * Assumption: The worst bandwidth we work on is 50 kilobits/sec
    + * 50kbits/sec * (1 byte / 8 bits) * 6 sec dead backchannel timeout
    + * @type {number}
    + */
    +WebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF = 37500;
    +
    +
    +/**
    + * @return {!ForwardChannelRequestPool} The forward channel request pool.
    + */
    +WebChannelBase.prototype.getForwardChannelRequestPool = function() {
    +  return this.forwardChannelRequestPool_;
    +};
    +
    +
    +/**
    + * @return {!Object} The codec object, to be used for the test channel.
    + */
    +WebChannelBase.prototype.getWireCodec = function() {
    +  return this.wireCodec_;
    +};
    +
    +
    +/**
    + * Returns the logger.
    + *
    + * @return {!WebChannelDebug} The channel debug object.
    + */
    +WebChannelBase.prototype.getChannelDebug = function() {
    +  return this.channelDebug_;
    +};
    +
    +
    +/**
    + * Sets the logger.
    + *
    + * @param {!WebChannelDebug} channelDebug The channel debug object.
    + */
    +WebChannelBase.prototype.setChannelDebug = function(channelDebug) {
    +  this.channelDebug_ = channelDebug;
    +};
    +
    +
    +/**
    + * Starts the channel. This initiates connections to the server.
    + *
    + * @param {string} testPath  The path for the test connection.
    + * @param {string} channelPath  The path for the channel connection.
    + * @param {!Object=} opt_extraParams Extra parameter keys and values to add to
    + *     the requests.
    + * @param {string=} opt_oldSessionId  Session ID from a previous session.
    + * @param {number=} opt_oldArrayId  The last array ID from a previous session.
    + */
    +WebChannelBase.prototype.connect = function(testPath, channelPath,
    +    opt_extraParams, opt_oldSessionId, opt_oldArrayId) {
    +  this.channelDebug_.debug('connect()');
    +
    +  requestStats.notifyStatEvent(requestStats.Stat.CONNECT_ATTEMPT);
    +
    +  this.path_ = channelPath;
    +  this.extraParams_ = opt_extraParams || {};
    +
    +  // Attach parameters about the previous session if reconnecting.
    +  if (opt_oldSessionId && goog.isDef(opt_oldArrayId)) {
    +    this.extraParams_['OSID'] = opt_oldSessionId;
    +    this.extraParams_['OAID'] = opt_oldArrayId;
    +  }
    +
    +  this.connectTest_(testPath);
    +};
    +
    +
    +/**
    + * Disconnects and closes the channel.
    + */
    +WebChannelBase.prototype.disconnect = function() {
    +  this.channelDebug_.debug('disconnect()');
    +
    +  this.cancelRequests_();
    +
    +  if (this.state_ == WebChannelBase.State.OPENED) {
    +    var rid = this.nextRid_++;
    +    var uri = this.forwardChannelUri_.clone();
    +    uri.setParameterValue('SID', this.sid_);
    +    uri.setParameterValue('RID', rid);
    +    uri.setParameterValue('TYPE', 'terminate');
    +
    +    // Add the reconnect parameters.
    +    this.addAdditionalParams_(uri);
    +
    +    var request = ChannelRequest.createChannelRequest(
    +        this, this.channelDebug_, this.sid_, rid);
    +    request.sendUsingImgTag(uri);
    +  }
    +
    +  this.onClose_();
    +};
    +
    +
    +/**
    + * Returns the session id of the channel. Only available after the
    + * channel has been opened.
    + * @return {string} Session ID.
    + */
    +WebChannelBase.prototype.getSessionId = function() {
    +  return this.sid_;
    +};
    +
    +
    +/**
    + * Starts the test channel to determine network conditions.
    + *
    + * @param {string} testPath  The relative PATH for the test connection.
    + * @private
    + */
    +WebChannelBase.prototype.connectTest_ = function(testPath) {
    +  this.channelDebug_.debug('connectTest_()');
    +  if (!this.okToMakeRequest_()) {
    +    return; // channel is cancelled
    +  }
    +  this.connectionTest_ = new BaseTestChannel(this, this.channelDebug_);
    +  this.connectionTest_.setExtraHeaders(this.extraHeaders_);
    +  this.connectionTest_.connect(testPath);
    +};
    +
    +
    +/**
    + * Starts the regular channel which is run after the test channel is complete.
    + * @private
    + */
    +WebChannelBase.prototype.connectChannel_ = function() {
    +  this.channelDebug_.debug('connectChannel_()');
    +  this.ensureInState_(WebChannelBase.State.INIT, WebChannelBase.State.CLOSED);
    +  this.forwardChannelUri_ =
    +      this.getForwardChannelUri(/** @type {string} */ (this.path_));
    +  this.ensureForwardChannel_();
    +};
    +
    +
    +/**
    + * Cancels all outstanding requests.
    + * @private
    + */
    +WebChannelBase.prototype.cancelRequests_ = function() {
    +  if (this.connectionTest_) {
    +    this.connectionTest_.abort();
    +    this.connectionTest_ = null;
    +  }
    +
    +  if (this.backChannelRequest_) {
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +  }
    +
    +  if (this.backChannelTimerId_) {
    +    goog.global.clearTimeout(this.backChannelTimerId_);
    +    this.backChannelTimerId_ = null;
    +  }
    +
    +  this.clearDeadBackchannelTimer_();
    +
    +  this.forwardChannelRequestPool_.cancel();
    +
    +  if (this.forwardChannelTimerId_) {
    +    goog.global.clearTimeout(this.forwardChannelTimerId_);
    +    this.forwardChannelTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns the extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @return {Object} The HTTP headers, or null.
    + */
    +WebChannelBase.prototype.getExtraHeaders = function() {
    +  return this.extraHeaders_;
    +};
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers, or null.
    + */
    +WebChannelBase.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Sets the throttle for handling onreadystatechange events for the request.
    + *
    + * @param {number} throttle The throttle in ms.  A value of zero indicates
    + *     no throttle.
    + */
    +WebChannelBase.prototype.setReadyStateChangeThrottle = function(throttle) {
    +  this.readyStateChangeThrottleMs_ = throttle;
    +};
    +
    +
    +/**
    + * Sets whether cross origin requests are supported for the channel.
    + *
    + * Setting this allows the creation of requests to secondary domains and
    + * sends XHRs with the CORS withCredentials bit set to true.
    + *
    + * In order for cross-origin requests to work, the server will also need to set
    + * CORS response headers as per:
    + * https://developer.mozilla.org/en-US/docs/HTTP_access_control
    + *
    + * See {@link goog.net.XhrIo#setWithCredentials}.
    + * @param {boolean} supportCrossDomain Whether cross domain XHRs are supported.
    + */
    +WebChannelBase.prototype.setSupportsCrossDomainXhrs =
    +    function(supportCrossDomain) {
    +  this.supportsCrossDomainXhrs_ = supportCrossDomain;
    +};
    +
    +
    +/**
    + * Returns the handler used for channel callback events.
    + *
    + * @return {WebChannelBase.Handler} The handler.
    + */
    +WebChannelBase.prototype.getHandler = function() {
    +  return this.handler_;
    +};
    +
    +
    +/**
    + * Sets the handler used for channel callback events.
    + * @param {WebChannelBase.Handler} handler The handler to set.
    + */
    +WebChannelBase.prototype.setHandler = function(handler) {
    +  this.handler_ = handler;
    +};
    +
    +
    +/**
    + * Returns whether the channel allows the use of a subdomain. There may be
    + * cases where this isn't allowed.
    + * @return {boolean} Whether a host prefix is allowed.
    + */
    +WebChannelBase.prototype.getAllowHostPrefix = function() {
    +  return this.allowHostPrefix_;
    +};
    +
    +
    +/**
    + * Sets whether the channel allows the use of a subdomain. There may be cases
    + * where this isn't allowed, for example, logging in with troutboard where
    + * using a subdomain causes Apache to force the user to authenticate twice.
    + * @param {boolean} allowHostPrefix Whether a host prefix is allowed.
    + */
    +WebChannelBase.prototype.setAllowHostPrefix = function(allowHostPrefix) {
    +  this.allowHostPrefix_ = allowHostPrefix;
    +};
    +
    +
    +/**
    + * Returns whether the channel is buffered or not. This state is valid for
    + * querying only after the test connection has completed. This may be
    + * queried in the WebChannelBase.okToMakeRequest() callback.
    + * A channel may be buffered if the test connection determines that
    + * a chunked response could not be sent down within a suitable time.
    + * @return {boolean} Whether the channel is buffered.
    + */
    +WebChannelBase.prototype.isBuffered = function() {
    +  return !this.useChunked_;
    +};
    +
    +
    +/**
    + * Returns whether chunked mode is allowed. In certain debugging situations,
    + * it's useful for the application to have a way to disable chunked mode for a
    + * user.
    +
    + * @return {boolean} Whether chunked mode is allowed.
    + */
    +WebChannelBase.prototype.getAllowChunkedMode = function() {
    +  return this.allowChunkedMode_;
    +};
    +
    +
    +/**
    + * Sets whether chunked mode is allowed. In certain debugging situations, it's
    + * useful for the application to have a way to disable chunked mode for a user.
    + * @param {boolean} allowChunkedMode  Whether chunked mode is allowed.
    + */
    +WebChannelBase.prototype.setAllowChunkedMode = function(allowChunkedMode) {
    +  this.allowChunkedMode_ = allowChunkedMode;
    +};
    +
    +
    +/**
    + * Sends a request to the server. The format of the request is a Map data
    + * structure of key/value pairs. These maps are then encoded in a format
    + * suitable for the wire and then reconstituted as a Map data structure that
    + * the server can process.
    + * @param {!Object|!goog.structs.Map} map The map to send.
    + * @param {!Object=} opt_context The context associated with the map.
    + */
    +WebChannelBase.prototype.sendMap = function(map, opt_context) {
    +  goog.asserts.assert(this.state_ != WebChannelBase.State.CLOSED,
    +      'Invalid operation: sending map when state is closed');
    +
    +  // We can only send 1000 maps per POST, but typically we should never have
    +  // that much to send, so warn if we exceed that (we still send all the maps).
    +  if (this.outgoingMaps_.length == WebChannelBase.MAX_MAPS_PER_REQUEST_) {
    +    // severe() is temporary so that we get these uploaded and can figure out
    +    // what's causing them. Afterwards can change to warning().
    +    this.channelDebug_.severe(
    +        'Already have ' + WebChannelBase.MAX_MAPS_PER_REQUEST_ +
    +        ' queued maps upon queueing ' + goog.json.serialize(map));
    +  }
    +
    +  this.outgoingMaps_.push(
    +      new Wire.QueuedMap(this.nextMapId_++, map, opt_context));
    +  if (this.state_ == WebChannelBase.State.OPENING ||
    +      this.state_ == WebChannelBase.State.OPENED) {
    +    this.ensureForwardChannel_();
    +  }
    +};
    +
    +
    +/**
    + * When set to true, this changes the behavior of the forward channel so it
    + * will not retry requests; it will fail after one network failure, and if
    + * there was already one network failure, the request will fail immediately.
    + * @param {boolean} failFast  Whether or not to fail fast.
    + */
    +WebChannelBase.prototype.setFailFast = function(failFast) {
    +  this.failFast_ = failFast;
    +  this.channelDebug_.info('setFailFast: ' + failFast);
    +  if ((this.forwardChannelRequestPool_.hasPendingRequest() ||
    +       this.forwardChannelTimerId_) &&
    +      this.forwardChannelRetryCount_ > this.getForwardChannelMaxRetries()) {
    +    this.channelDebug_.info(
    +        'Retry count ' + this.forwardChannelRetryCount_ +
    +        ' > new maxRetries ' + this.getForwardChannelMaxRetries() +
    +        '. Fail immediately!');
    +
    +    if (!this.forwardChannelRequestPool_.forceComplete(
    +        goog.bind(this.onRequestComplete, this))) {
    +      // i.e., this.forwardChannelTimerId_
    +      goog.global.clearTimeout(this.forwardChannelTimerId_);
    +      this.forwardChannelTimerId_ = null;
    +      // The error code from the last failed request is gone, so just use a
    +      // generic one.
    +      this.signalError_(WebChannelBase.Error.REQUEST_FAILED);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The max number of forward-channel retries, which will be 0
    + * in fail-fast mode.
    + */
    +WebChannelBase.prototype.getForwardChannelMaxRetries = function() {
    +  return this.failFast_ ? 0 : this.forwardChannelMaxRetries_;
    +};
    +
    +
    +/**
    + * Sets the maximum number of attempts to connect to the server for forward
    + * channel requests.
    + * @param {number} retries The maximum number of attempts.
    + */
    +WebChannelBase.prototype.setForwardChannelMaxRetries = function(retries) {
    +  this.forwardChannelMaxRetries_ = retries;
    +};
    +
    +
    +/**
    + * Sets the timeout for a forward channel request.
    + * @param {number} timeoutMs The timeout in milliseconds.
    + */
    +WebChannelBase.prototype.setForwardChannelRequestTimeout = function(timeoutMs) {
    +  this.forwardChannelRequestTimeoutMs_ = timeoutMs;
    +};
    +
    +
    +/**
    + * @return {number} The max number of back-channel retries, which is a constant.
    + */
    +WebChannelBase.prototype.getBackChannelMaxRetries = function() {
    +  // Back-channel retries is a constant.
    +  return WebChannelBase.BACK_CHANNEL_MAX_RETRIES;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.isClosed = function() {
    +  return this.state_ == WebChannelBase.State.CLOSED;
    +};
    +
    +
    +/**
    + * Returns the channel state.
    + * @return {WebChannelBase.State} The current state of the channel.
    + */
    +WebChannelBase.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * Return the last status code received for a request.
    + * @return {number} The last status code received for a request.
    + */
    +WebChannelBase.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * @return {number} The last array id received.
    + */
    +WebChannelBase.prototype.getLastArrayId = function() {
    +  return this.lastArrayId_;
    +};
    +
    +
    +/**
    + * Returns whether there are outstanding requests servicing the channel.
    + * @return {boolean} true if there are outstanding requests.
    + */
    +WebChannelBase.prototype.hasOutstandingRequests = function() {
    +  return this.getOutstandingRequests_() != 0;
    +};
    +
    +
    +/**
    + * Returns the number of outstanding requests.
    + * @return {number} The number of outstanding requests to the server.
    + * @private
    + */
    +WebChannelBase.prototype.getOutstandingRequests_ = function() {
    +  var count = 0;
    +  if (this.backChannelRequest_) {
    +    count++;
    +  }
    +  count += this.forwardChannelRequestPool_.getRequestCount();
    +  return count;
    +};
    +
    +
    +/**
    + * Ensures that a forward channel request is scheduled.
    + * @private
    + */
    +WebChannelBase.prototype.ensureForwardChannel_ = function() {
    +  if (this.forwardChannelRequestPool_.isFull()) {
    +    // enough connection in process - no need to start a new request
    +    return;
    +  }
    +
    +  if (this.forwardChannelTimerId_) {
    +    // no need to start a new request - one is already scheduled
    +    return;
    +  }
    +
    +  this.forwardChannelTimerId_ = requestStats.setTimeout(
    +      goog.bind(this.onStartForwardChannelTimer_, this), 0);
    +  this.forwardChannelRetryCount_ = 0;
    +};
    +
    +
    +/**
    + * Schedules a forward-channel retry for the specified request, unless the max
    + * retries has been reached.
    + * @param {ChannelRequest} request The failed request to retry.
    + * @return {boolean} true iff a retry was scheduled.
    + * @private
    + */
    +WebChannelBase.prototype.maybeRetryForwardChannel_ =
    +    function(request) {
    +  if (this.forwardChannelRequestPool_.isFull() || this.forwardChannelTimerId_) {
    +    // Should be impossible to be called in this state.
    +    this.channelDebug_.severe('Request already in progress');
    +    return false;
    +  }
    +
    +  if (this.state_ == WebChannelBase.State.INIT ||  // no retry open_()
    +      (this.forwardChannelRetryCount_ >= this.getForwardChannelMaxRetries())) {
    +    return false;
    +  }
    +
    +  this.channelDebug_.debug('Going to retry POST');
    +
    +  this.forwardChannelTimerId_ = requestStats.setTimeout(
    +      goog.bind(this.onStartForwardChannelTimer_, this, request),
    +      this.getRetryTime_(this.forwardChannelRetryCount_));
    +  this.forwardChannelRetryCount_++;
    +  return true;
    +};
    +
    +
    +/**
    + * Timer callback for ensureForwardChannel
    + * @param {ChannelRequest=} opt_retryRequest A failed request
    + * to retry.
    + * @private
    + */
    +WebChannelBase.prototype.onStartForwardChannelTimer_ = function(
    +    opt_retryRequest) {
    +  this.forwardChannelTimerId_ = null;
    +  this.startForwardChannel_(opt_retryRequest);
    +};
    +
    +
    +/**
    + * Begins a new forward channel operation to the server.
    + * @param {ChannelRequest=} opt_retryRequest A failed request to retry.
    + * @private
    + */
    +WebChannelBase.prototype.startForwardChannel_ = function(
    +    opt_retryRequest) {
    +  this.channelDebug_.debug('startForwardChannel_');
    +  if (!this.okToMakeRequest_()) {
    +    return; // channel is cancelled
    +  } else if (this.state_ == WebChannelBase.State.INIT) {
    +    if (opt_retryRequest) {
    +      this.channelDebug_.severe('Not supposed to retry the open');
    +      return;
    +    }
    +    this.open_();
    +    this.state_ = WebChannelBase.State.OPENING;
    +  } else if (this.state_ == WebChannelBase.State.OPENED) {
    +    if (opt_retryRequest) {
    +      this.makeForwardChannelRequest_(opt_retryRequest);
    +      return;
    +    }
    +
    +    if (this.outgoingMaps_.length == 0) {
    +      this.channelDebug_.debug('startForwardChannel_ returned: ' +
    +                                   'nothing to send');
    +      // no need to start a new forward channel request
    +      return;
    +    }
    +
    +    if (this.forwardChannelRequestPool_.isFull()) {
    +      // Should be impossible to be called in this state.
    +      this.channelDebug_.severe('startForwardChannel_ returned: ' +
    +          'connection already in progress');
    +      return;
    +    }
    +
    +    this.makeForwardChannelRequest_();
    +    this.channelDebug_.debug('startForwardChannel_ finished, sent request');
    +  }
    +};
    +
    +
    +/**
    + * Establishes a new channel session with the the server.
    + * @private
    + */
    +WebChannelBase.prototype.open_ = function() {
    +  this.channelDebug_.debug('open_()');
    +  this.nextRid_ = Math.floor(Math.random() * 100000);
    +
    +  var rid = this.nextRid_++;
    +  var request = ChannelRequest.createChannelRequest(
    +      this, this.channelDebug_, '', rid);
    +  request.setExtraHeaders(this.extraHeaders_);
    +  var requestText = this.dequeueOutgoingMaps_();
    +  var uri = this.forwardChannelUri_.clone();
    +  uri.setParameterValue('RID', rid);
    +  if (this.clientVersion_) {
    +    uri.setParameterValue('CVER', this.clientVersion_);
    +  }
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  this.forwardChannelRequestPool_.addRequest(request);
    +  request.xmlHttpPost(uri, requestText, true);
    +};
    +
    +
    +/**
    + * Makes a forward channel request using XMLHTTP.
    + * @param {!ChannelRequest=} opt_retryRequest A failed request to retry.
    + * @private
    + */
    +WebChannelBase.prototype.makeForwardChannelRequest_ =
    +    function(opt_retryRequest) {
    +  var rid;
    +  var requestText;
    +  if (opt_retryRequest) {
    +    this.requeuePendingMaps_();
    +    rid = this.nextRid_ - 1;  // Must use last RID
    +    requestText = this.dequeueOutgoingMaps_();
    +  } else {
    +    rid = this.nextRid_++;
    +    requestText = this.dequeueOutgoingMaps_();
    +  }
    +
    +  var uri = this.forwardChannelUri_.clone();
    +  uri.setParameterValue('SID', this.sid_);
    +  uri.setParameterValue('RID', rid);
    +  uri.setParameterValue('AID', this.lastArrayId_);
    +  // Add the additional reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  var request = ChannelRequest.createChannelRequest(this, this.channelDebug_,
    +      this.sid_, rid, this.forwardChannelRetryCount_ + 1);
    +  request.setExtraHeaders(this.extraHeaders_);
    +
    +  // Randomize from 50%-100% of the forward channel timeout to avoid
    +  // a big hit if servers happen to die at once.
    +  request.setTimeout(
    +      Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50) +
    +      Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50 * Math.random()));
    +  this.forwardChannelRequestPool_.addRequest(request);
    +  request.xmlHttpPost(uri, requestText, true);
    +};
    +
    +
    +/**
    + * Adds the additional parameters from the handler to the given URI.
    + * @param {!goog.Uri} uri The URI to add the parameters to.
    + * @private
    + */
    +WebChannelBase.prototype.addAdditionalParams_ = function(uri) {
    +  // Add the additional reconnect parameters as needed.
    +  if (this.handler_) {
    +    var params = this.handler_.getAdditionalParams(this);
    +    if (params) {
    +      goog.structs.forEach(params, function(value, key, coll) {
    +        uri.setParameterValue(key, value);
    +      });
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the request text from the outgoing maps and resets it.
    + * @return {string} The encoded request text created from all the currently
    + *                  queued outgoing maps.
    + * @private
    + */
    +WebChannelBase.prototype.dequeueOutgoingMaps_ = function() {
    +  var count = Math.min(this.outgoingMaps_.length,
    +                       WebChannelBase.MAX_MAPS_PER_REQUEST_);
    +  var badMapHandler = this.handler_ ?
    +      goog.bind(this.handler_.badMapError, this.handler_, this) : null;
    +  var result = this.wireCodec_.encodeMessageQueue(
    +      this.outgoingMaps_, count, badMapHandler);
    +  this.pendingMaps_ = this.pendingMaps_.concat(
    +      this.outgoingMaps_.splice(0, count));
    +  return result;
    +};
    +
    +
    +/**
    + * Requeues unacknowledged sent arrays for retransmission in the next forward
    + * channel request.
    + * @private
    + */
    +WebChannelBase.prototype.requeuePendingMaps_ = function() {
    +  this.outgoingMaps_ = this.pendingMaps_.concat(this.outgoingMaps_);
    +  this.pendingMaps_.length = 0;
    +};
    +
    +
    +/**
    + * Ensures there is a backchannel request for receiving data from the server.
    + * @private
    + */
    +WebChannelBase.prototype.ensureBackChannel_ = function() {
    +  if (this.backChannelRequest_) {
    +    // already have one
    +    return;
    +  }
    +
    +  if (this.backChannelTimerId_) {
    +    // no need to start a new request - one is already scheduled
    +    return;
    +  }
    +
    +  this.backChannelAttemptId_ = 1;
    +  this.backChannelTimerId_ = requestStats.setTimeout(
    +      goog.bind(this.onStartBackChannelTimer_, this), 0);
    +  this.backChannelRetryCount_ = 0;
    +};
    +
    +
    +/**
    + * Schedules a back-channel retry, unless the max retries has been reached.
    + * @return {boolean} true iff a retry was scheduled.
    + * @private
    + */
    +WebChannelBase.prototype.maybeRetryBackChannel_ = function() {
    +  if (this.backChannelRequest_ || this.backChannelTimerId_) {
    +    // Should be impossible to be called in this state.
    +    this.channelDebug_.severe('Request already in progress');
    +    return false;
    +  }
    +
    +  if (this.backChannelRetryCount_ >= this.getBackChannelMaxRetries()) {
    +    return false;
    +  }
    +
    +  this.channelDebug_.debug('Going to retry GET');
    +
    +  this.backChannelAttemptId_++;
    +  this.backChannelTimerId_ = requestStats.setTimeout(
    +      goog.bind(this.onStartBackChannelTimer_, this),
    +      this.getRetryTime_(this.backChannelRetryCount_));
    +  this.backChannelRetryCount_++;
    +  return true;
    +};
    +
    +
    +/**
    + * Timer callback for ensureBackChannel_.
    + * @private
    + */
    +WebChannelBase.prototype.onStartBackChannelTimer_ = function() {
    +  this.backChannelTimerId_ = null;
    +  this.startBackChannel_();
    +};
    +
    +
    +/**
    + * Begins a new back channel operation to the server.
    + * @private
    + */
    +WebChannelBase.prototype.startBackChannel_ = function() {
    +  if (!this.okToMakeRequest_()) {
    +    // channel is cancelled
    +    return;
    +  }
    +
    +  this.channelDebug_.debug('Creating new HttpRequest');
    +  this.backChannelRequest_ = ChannelRequest.createChannelRequest(this,
    +      this.channelDebug_, this.sid_, 'rpc', this.backChannelAttemptId_);
    +  this.backChannelRequest_.setExtraHeaders(this.extraHeaders_);
    +  this.backChannelRequest_.setReadyStateChangeThrottle(
    +      this.readyStateChangeThrottleMs_);
    +  var uri = this.backChannelUri_.clone();
    +  uri.setParameterValue('RID', 'rpc');
    +  uri.setParameterValue('SID', this.sid_);
    +  uri.setParameterValue('CI', this.useChunked_ ? '0' : '1');
    +  uri.setParameterValue('AID', this.lastArrayId_);
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  if (!ChannelRequest.supportsXhrStreaming()) {
    +    uri.setParameterValue('TYPE', 'html');
    +    this.backChannelRequest_.tridentGet(uri, Boolean(this.hostPrefix_));
    +  } else {
    +    uri.setParameterValue('TYPE', 'xmlhttp');
    +    this.backChannelRequest_.xmlHttpGet(uri, true /* decodeChunks */,
    +        this.hostPrefix_, false /* opt_noClose */);
    +  }
    +  this.channelDebug_.debug('New Request created');
    +};
    +
    +
    +/**
    + * Gives the handler a chance to return an error code and stop channel
    + * execution. A handler might want to do this to check that the user is still
    + * logged in, for example.
    + * @private
    + * @return {boolean} If it's OK to make a request.
    + */
    +WebChannelBase.prototype.okToMakeRequest_ = function() {
    +  if (this.handler_) {
    +    var result = this.handler_.okToMakeRequest(this);
    +    if (result != WebChannelBase.Error.OK) {
    +      this.channelDebug_.debug(
    +          'Handler returned error code from okToMakeRequest');
    +      this.signalError_(result);
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.testConnectionFinished =
    +    function(testChannel, useChunked) {
    +  this.channelDebug_.debug('Test Connection Finished');
    +
    +  // Forward channel will not be used prior to this method is called
    +  var clientProtocol = testChannel.getClientProtocol();
    +  if (clientProtocol) {
    +    this.forwardChannelRequestPool_.applyClientProtocol(clientProtocol);
    +  }
    +
    +  this.useChunked_ = this.allowChunkedMode_ && useChunked;
    +  this.lastStatusCode_ = testChannel.getLastStatusCode();
    +
    +  this.connectChannel_();
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.testConnectionFailure =
    +    function(testChannel, errorCode) {
    +  this.channelDebug_.debug('Test Connection Failed');
    +  this.lastStatusCode_ = testChannel.getLastStatusCode();
    +  this.signalError_(WebChannelBase.Error.REQUEST_FAILED);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.onRequestData = function(request, responseText) {
    +  if (this.state_ == WebChannelBase.State.CLOSED ||
    +      (this.backChannelRequest_ != request &&
    +       !this.forwardChannelRequestPool_.hasRequest(request))) {
    +    // either CLOSED or a request we don't know about (perhaps an old request)
    +    return;
    +  }
    +  this.lastStatusCode_ = request.getLastStatusCode();
    +
    +  if (this.forwardChannelRequestPool_.hasRequest(request) &&
    +      this.state_ == WebChannelBase.State.OPENED) {
    +    var response;
    +    try {
    +      response = this.wireCodec_.decodeMessage(responseText);
    +    } catch (ex) {
    +      response = null;
    +    }
    +    if (goog.isArray(response) && response.length == 3) {
    +      this.handlePostResponse_(/** @type {!Array<?>} */ (response), request);
    +    } else {
    +      this.channelDebug_.debug('Bad POST response data returned');
    +      this.signalError_(WebChannelBase.Error.BAD_RESPONSE);
    +    }
    +  } else {
    +    if (this.backChannelRequest_ == request) {
    +      this.clearDeadBackchannelTimer_();
    +    }
    +    if (!goog.string.isEmptyOrWhitespace(responseText)) {
    +      var response = this.wireCodec_.decodeMessage(responseText);
    +      this.onInput_(/** @type {!Array<?>} */ (response));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles a POST response from the server.
    + * @param {Array<number>} responseValues The key value pairs in
    + *     the POST response.
    + * @param {!ChannelRequest} forwardReq The forward channel request that
    + * triggers this function call.
    + * @private
    + */
    +WebChannelBase.prototype.handlePostResponse_ = function(
    +    responseValues, forwardReq) {
    +  // The first response value is set to 0 if server is missing backchannel.
    +  if (responseValues[0] == 0) {
    +    this.handleBackchannelMissing_(forwardReq);
    +    return;
    +  }
    +  this.lastPostResponseArrayId_ = responseValues[1];
    +  var outstandingArrays = this.lastPostResponseArrayId_ - this.lastArrayId_;
    +  if (0 < outstandingArrays) {
    +    var numOutstandingBackchannelBytes = responseValues[2];
    +    this.channelDebug_.debug(numOutstandingBackchannelBytes + ' bytes (in ' +
    +        outstandingArrays + ' arrays) are outstanding on the BackChannel');
    +    if (!this.shouldRetryBackChannel_(numOutstandingBackchannelBytes)) {
    +      return;
    +    }
    +    if (!this.deadBackChannelTimerId_) {
    +      // We expect to receive data within 2 RTTs or we retry the backchannel.
    +      this.deadBackChannelTimerId_ = requestStats.setTimeout(
    +          goog.bind(this.onBackChannelDead_, this),
    +          2 * WebChannelBase.RTT_ESTIMATE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles a POST response from the server telling us that it has detected that
    + * we have no hanging GET connection.
    + * @param {!ChannelRequest} forwardReq The forward channel request that
    + * triggers this function call.
    + * @private
    + */
    +WebChannelBase.prototype.handleBackchannelMissing_ = function(forwardReq) {
    +  // As long as the back channel was started before the POST was sent,
    +  // we should retry the backchannel. We give a slight buffer of RTT_ESTIMATE
    +  // so as not to excessively retry the backchannel
    +  this.channelDebug_.debug('Server claims our backchannel is missing.');
    +  if (this.backChannelTimerId_) {
    +    this.channelDebug_.debug('But we are currently starting the request.');
    +    return;
    +  } else if (!this.backChannelRequest_) {
    +    this.channelDebug_.warning(
    +        'We do not have a BackChannel established');
    +  } else if (this.backChannelRequest_.getRequestStartTime() +
    +      WebChannelBase.RTT_ESTIMATE < forwardReq.getRequestStartTime()) {
    +    this.clearDeadBackchannelTimer_();
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +  } else {
    +    return;
    +  }
    +  this.maybeRetryBackChannel_();
    +  requestStats.notifyStatEvent(requestStats.Stat.BACKCHANNEL_MISSING);
    +};
    +
    +
    +/**
    + * Determines whether we should start the process of retrying a possibly
    + * dead backchannel.
    + * @param {number} outstandingBytes The number of bytes for which the server has
    + *     not yet received acknowledgement.
    + * @return {boolean} Whether to start the backchannel retry timer.
    + * @private
    + */
    +WebChannelBase.prototype.shouldRetryBackChannel_ = function(
    +    outstandingBytes) {
    +  // Not too many outstanding bytes, not buffered and not after a retry.
    +  return outstandingBytes <
    +      WebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF &&
    +      !this.isBuffered() &&
    +      this.backChannelRetryCount_ == 0;
    +};
    +
    +
    +/**
    + * Decides which host prefix should be used, if any.  If there is a handler,
    + * allows the handler to validate a host prefix provided by the server, and
    + * optionally override it.
    + * @param {?string} serverHostPrefix The host prefix provided by the server.
    + * @return {?string} The host prefix to actually use, if any. Will return null
    + *     if the use of host prefixes was disabled via setAllowHostPrefix().
    + * @override
    + */
    +WebChannelBase.prototype.correctHostPrefix = function(serverHostPrefix) {
    +  if (this.allowHostPrefix_) {
    +    if (this.handler_) {
    +      return this.handler_.correctHostPrefix(serverHostPrefix);
    +    }
    +    return serverHostPrefix;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Handles the timer that indicates that our backchannel is no longer able to
    + * successfully receive data from the server.
    + * @private
    + */
    +WebChannelBase.prototype.onBackChannelDead_ = function() {
    +  if (goog.isDefAndNotNull(this.deadBackChannelTimerId_)) {
    +    this.deadBackChannelTimerId_ = null;
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +    this.maybeRetryBackChannel_();
    +    requestStats.notifyStatEvent(requestStats.Stat.BACKCHANNEL_DEAD);
    +  }
    +};
    +
    +
    +/**
    + * Clears the timer that indicates that our backchannel is no longer able to
    + * successfully receive data from the server.
    + * @private
    + */
    +WebChannelBase.prototype.clearDeadBackchannelTimer_ = function() {
    +  if (goog.isDefAndNotNull(this.deadBackChannelTimerId_)) {
    +    goog.global.clearTimeout(this.deadBackChannelTimerId_);
    +    this.deadBackChannelTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether or not the given error/status combination is fatal or not.
    + * On fatal errors we immediately close the session rather than retrying the
    + * failed request.
    + * @param {?ChannelRequest.Error} error The error code for the
    + * failed request.
    + * @param {number} statusCode The last HTTP status code.
    + * @return {boolean} Whether or not the error is fatal.
    + * @private
    + */
    +WebChannelBase.isFatalError_ = function(error, statusCode) {
    +  return error == ChannelRequest.Error.UNKNOWN_SESSION_ID ||
    +      error == ChannelRequest.Error.ACTIVE_X_BLOCKED ||
    +      (error == ChannelRequest.Error.STATUS &&
    +       statusCode > 0);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.onRequestComplete = function(request) {
    +  this.channelDebug_.debug('Request complete');
    +  var type;
    +  if (this.backChannelRequest_ == request) {
    +    this.clearDeadBackchannelTimer_();
    +    this.backChannelRequest_ = null;
    +    type = WebChannelBase.ChannelType_.BACK_CHANNEL;
    +  } else if (this.forwardChannelRequestPool_.hasRequest(request)) {
    +    this.forwardChannelRequestPool_.removeRequest(request);
    +    type = WebChannelBase.ChannelType_.FORWARD_CHANNEL;
    +  } else {
    +    // return if it was an old request from a previous session
    +    return;
    +  }
    +
    +  this.lastStatusCode_ = request.getLastStatusCode();
    +
    +  if (this.state_ == WebChannelBase.State.CLOSED) {
    +    return;
    +  }
    +
    +  if (request.getSuccess()) {
    +    // Yay!
    +    if (type == WebChannelBase.ChannelType_.FORWARD_CHANNEL) {
    +      var size = request.getPostData() ? request.getPostData().length : 0;
    +      requestStats.notifyTimingEvent(size,
    +          goog.now() - request.getRequestStartTime(),
    +          this.forwardChannelRetryCount_);
    +      this.ensureForwardChannel_();
    +      this.onSuccess_();
    +      this.pendingMaps_.length = 0;
    +    } else {  // i.e., back-channel
    +      this.ensureBackChannel_();
    +    }
    +    return;
    +  }
    +  // Else unsuccessful. Fall through.
    +
    +  var lastError = request.getLastError();
    +  if (!WebChannelBase.isFatalError_(lastError, this.lastStatusCode_)) {
    +    // Maybe retry.
    +    this.channelDebug_.debug('Maybe retrying, last error: ' +
    +        ChannelRequest.errorStringFromCode(lastError, this.lastStatusCode_));
    +    if (type == WebChannelBase.ChannelType_.FORWARD_CHANNEL) {
    +      if (this.maybeRetryForwardChannel_(request)) {
    +        return;
    +      }
    +    }
    +    if (type == WebChannelBase.ChannelType_.BACK_CHANNEL) {
    +      if (this.maybeRetryBackChannel_()) {
    +        return;
    +      }
    +    }
    +    // Else exceeded max retries. Fall through.
    +    this.channelDebug_.debug('Exceeded max number of retries');
    +  } else {
    +    // Else fatal error. Fall through and mark the pending maps as failed.
    +    this.channelDebug_.debug('Not retrying due to error type');
    +  }
    +
    +
    +  // Can't save this session. :(
    +  this.channelDebug_.debug('Error: HTTP request failed');
    +  switch (lastError) {
    +    case ChannelRequest.Error.NO_DATA:
    +      this.signalError_(WebChannelBase.Error.NO_DATA);
    +      break;
    +    case ChannelRequest.Error.BAD_DATA:
    +      this.signalError_(WebChannelBase.Error.BAD_DATA);
    +      break;
    +    case ChannelRequest.Error.UNKNOWN_SESSION_ID:
    +      this.signalError_(WebChannelBase.Error.UNKNOWN_SESSION_ID);
    +      break;
    +    case ChannelRequest.Error.ACTIVE_X_BLOCKED:
    +      this.signalError_(WebChannelBase.Error.ACTIVE_X_BLOCKED);
    +      break;
    +    default:
    +      this.signalError_(WebChannelBase.Error.REQUEST_FAILED);
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * @param {number} retryCount Number of retries so far.
    + * @return {number} Time in ms before firing next retry request.
    + * @private
    + */
    +WebChannelBase.prototype.getRetryTime_ = function(retryCount) {
    +  var retryTime = this.baseRetryDelayMs_ +
    +      Math.floor(Math.random() * this.retryDelaySeedMs_);
    +  if (!this.isActive()) {
    +    this.channelDebug_.debug('Inactive channel');
    +    retryTime =
    +        retryTime * WebChannelBase.INACTIVE_CHANNEL_RETRY_FACTOR;
    +  }
    +  // Backoff for subsequent retries
    +  retryTime *= retryCount;
    +  return retryTime;
    +};
    +
    +
    +/**
    + * @param {number} baseDelayMs The base part of the retry delay, in ms.
    + * @param {number} delaySeedMs A random delay between 0 and this is added to
    + *     the base part.
    + */
    +WebChannelBase.prototype.setRetryDelay = function(baseDelayMs, delaySeedMs) {
    +  this.baseRetryDelayMs_ = baseDelayMs;
    +  this.retryDelaySeedMs_ = delaySeedMs;
    +};
    +
    +
    +/**
    + * Processes the data returned by the server.
    + * @param {!Array<!Array<?>>} respArray The response array returned
    + *     by the server.
    + * @private
    + */
    +WebChannelBase.prototype.onInput_ = function(respArray) {
    +  var batch = this.handler_ && this.handler_.channelHandleMultipleArrays ?
    +      [] : null;
    +  for (var i = 0; i < respArray.length; i++) {
    +    var nextArray = respArray[i];
    +    this.lastArrayId_ = nextArray[0];
    +    nextArray = nextArray[1];
    +    if (this.state_ == WebChannelBase.State.OPENING) {
    +      if (nextArray[0] == 'c') {
    +        this.sid_ = nextArray[1];
    +        this.hostPrefix_ = this.correctHostPrefix(nextArray[2]);
    +        var negotiatedVersion = nextArray[3];
    +        if (goog.isDefAndNotNull(negotiatedVersion)) {
    +          this.channelVersion_ = negotiatedVersion;
    +        }
    +        this.state_ = WebChannelBase.State.OPENED;
    +        if (this.handler_) {
    +          this.handler_.channelOpened(this);
    +        }
    +        this.backChannelUri_ = this.getBackChannelUri(
    +            this.hostPrefix_, /** @type {string} */ (this.path_));
    +        // Open connection to receive data
    +        this.ensureBackChannel_();
    +      } else if (nextArray[0] == 'stop') {
    +        this.signalError_(WebChannelBase.Error.STOP);
    +      }
    +    } else if (this.state_ == WebChannelBase.State.OPENED) {
    +      if (nextArray[0] == 'stop') {
    +        if (batch && !goog.array.isEmpty(batch)) {
    +          this.handler_.channelHandleMultipleArrays(this, batch);
    +          batch.length = 0;
    +        }
    +        this.signalError_(WebChannelBase.Error.STOP);
    +      } else if (nextArray[0] == 'noop') {
    +        // ignore - noop to keep connection happy
    +      } else {
    +        if (batch) {
    +          batch.push(nextArray);
    +        } else if (this.handler_) {
    +          this.handler_.channelHandleArray(this, nextArray);
    +        }
    +      }
    +      // We have received useful data on the back-channel, so clear its retry
    +      // count. We do this because back-channels by design do not complete
    +      // quickly, so on a flaky connection we could have many fail to complete
    +      // fully but still deliver a lot of data before they fail. We don't want
    +      // to count such failures towards the retry limit, because we don't want
    +      // to give up on a session if we can still receive data.
    +      this.backChannelRetryCount_ = 0;
    +    }
    +  }
    +  if (batch && !goog.array.isEmpty(batch)) {
    +    this.handler_.channelHandleMultipleArrays(this, batch);
    +  }
    +};
    +
    +
    +/**
    + * Helper to ensure the channel is in the expected state.
    + * @param {...number} var_args The channel must be in one of the indicated
    + *     states.
    + * @private
    + */
    +WebChannelBase.prototype.ensureInState_ = function(var_args) {
    +  goog.asserts.assert(goog.array.contains(arguments, this.state_),
    +      'Unexpected channel state: %s', this.state_);
    +};
    +
    +
    +/**
    + * Signals an error has occurred.
    + * @param {WebChannelBase.Error} error The error code for the failure.
    + * @private
    + */
    +WebChannelBase.prototype.signalError_ = function(error) {
    +  this.channelDebug_.info('Error code ' + error);
    +  if (error == WebChannelBase.Error.REQUEST_FAILED) {
    +    // Create a separate Internet connection to check
    +    // if it's a server error or user's network error.
    +    var imageUri = null;
    +    if (this.handler_) {
    +      imageUri = this.handler_.getNetworkTestImageUri(this);
    +    }
    +    netUtils.testNetwork(goog.bind(this.testNetworkCallback_, this), imageUri);
    +  } else {
    +    requestStats.notifyStatEvent(requestStats.Stat.ERROR_OTHER);
    +  }
    +  this.onError_(error);
    +};
    +
    +
    +/**
    + * Callback for netUtils.testNetwork during error handling.
    + * @param {boolean} networkUp Whether the network is up.
    + * @private
    + */
    +WebChannelBase.prototype.testNetworkCallback_ = function(networkUp) {
    +  if (networkUp) {
    +    this.channelDebug_.info('Successfully pinged google.com');
    +    requestStats.notifyStatEvent(requestStats.Stat.ERROR_OTHER);
    +  } else {
    +    this.channelDebug_.info('Failed to ping google.com');
    +    requestStats.notifyStatEvent(requestStats.Stat.ERROR_NETWORK);
    +    // We call onError_ here instead of signalError_ because the latter just
    +    // calls notifyStatEvent, and we don't want to have another stat event.
    +    this.onError_(WebChannelBase.Error.NETWORK);
    +  }
    +};
    +
    +
    +/**
    + * Called when messages have been successfully sent from the queue.
    + * @private
    + */
    +WebChannelBase.prototype.onSuccess_ = function() {
    +  // TODO(user): optimize for request pool (>1)
    +  if (this.handler_) {
    +    this.handler_.channelSuccess(this, this.pendingMaps_);
    +  }
    +};
    +
    +
    +/**
    + * Called when we've determined the final error for a channel. It closes the
    + * notifiers the handler of the error and closes the channel.
    + * @param {WebChannelBase.Error} error  The error code for the failure.
    + * @private
    + */
    +WebChannelBase.prototype.onError_ = function(error) {
    +  this.channelDebug_.debug('HttpChannel: error - ' + error);
    +  this.state_ = WebChannelBase.State.CLOSED;
    +  if (this.handler_) {
    +    this.handler_.channelError(this, error);
    +  }
    +  this.onClose_();
    +  this.cancelRequests_();
    +};
    +
    +
    +/**
    + * Called when the channel has been closed. It notifiers the handler of the
    + * event, and reports any pending or undelivered maps.
    + * @private
    + */
    +WebChannelBase.prototype.onClose_ = function() {
    +  this.state_ = WebChannelBase.State.CLOSED;
    +  this.lastStatusCode_ = -1;
    +  if (this.handler_) {
    +    if (this.pendingMaps_.length == 0 && this.outgoingMaps_.length == 0) {
    +      this.handler_.channelClosed(this);
    +    } else {
    +      this.channelDebug_.debug('Number of undelivered maps' +
    +          ', pending: ' + this.pendingMaps_.length +
    +          ', outgoing: ' + this.outgoingMaps_.length);
    +
    +      var copyOfPendingMaps = goog.array.clone(this.pendingMaps_);
    +      var copyOfUndeliveredMaps = goog.array.clone(this.outgoingMaps_);
    +      this.pendingMaps_.length = 0;
    +      this.outgoingMaps_.length = 0;
    +
    +      this.handler_.channelClosed(this, copyOfPendingMaps,
    +          copyOfUndeliveredMaps);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.getForwardChannelUri = function(path) {
    +  var uri = this.createDataUri(null, path);
    +  this.channelDebug_.debug('GetForwardChannelUri: ' + uri);
    +  return uri;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.getConnectionState = function() {
    +  return this.ConnState_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.getBackChannelUri = function(hostPrefix, path) {
    +  var uri = this.createDataUri(this.shouldUseSecondaryDomains() ?
    +      hostPrefix : null, path);
    +  this.channelDebug_.debug('GetBackChannelUri: ' + uri);
    +  return uri;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.createDataUri =
    +    function(hostPrefix, path, opt_overridePort) {
    +  var uri = goog.Uri.parse(path);
    +  var uriAbsolute = (uri.getDomain() != '');
    +  if (uriAbsolute) {
    +    if (hostPrefix) {
    +      uri.setDomain(hostPrefix + '.' + uri.getDomain());
    +    }
    +
    +    uri.setPort(opt_overridePort || uri.getPort());
    +  } else {
    +    var locationPage = window.location;
    +    var hostName;
    +    if (hostPrefix) {
    +      hostName = hostPrefix + '.' + locationPage.hostname;
    +    } else {
    +      hostName = locationPage.hostname;
    +    }
    +
    +    var port = opt_overridePort || locationPage.port;
    +
    +    uri = goog.Uri.create(locationPage.protocol, null, hostName, port, path);
    +  }
    +
    +  if (this.extraParams_) {
    +    goog.object.forEach(this.extraParams_, function(value, key) {
    +      uri.setParameterValue(key, value);
    +    });
    +  }
    +
    +  // Add the protocol version to the URI.
    +  uri.setParameterValue('VER', this.channelVersion_);
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  return uri;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.createXhrIo = function(hostPrefix) {
    +  if (hostPrefix && !this.supportsCrossDomainXhrs_) {
    +    throw Error('Can\'t create secondary domain capable XhrIo object.');
    +  }
    +  var xhr = new goog.net.XhrIo();
    +  xhr.setWithCredentials(this.supportsCrossDomainXhrs_);
    +  return xhr;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.isActive = function() {
    +  return !!this.handler_ && this.handler_.isActive(this);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBase.prototype.shouldUseSecondaryDomains = function() {
    +  return this.supportsCrossDomainXhrs_ ||
    +      !ChannelRequest.supportsXhrStreaming();
    +};
    +
    +
    +/**
    + * A LogSaver that can be used to accumulate all the debug logs so they
    + * can be sent to the server when a problem is detected.
    + */
    +WebChannelBase.LogSaver = {};
    +
    +
    +/**
    + * Buffer for accumulating the debug log
    + * @type {goog.structs.CircularBuffer}
    + * @private
    + */
    +WebChannelBase.LogSaver.buffer_ = new goog.structs.CircularBuffer(1000);
    +
    +
    +/**
    + * Whether we're currently accumulating the debug log.
    + * @type {boolean}
    + * @private
    + */
    +WebChannelBase.LogSaver.enabled_ = false;
    +
    +
    +/**
    + * Formatter for saving logs.
    + * @type {goog.debug.Formatter}
    + * @private
    + */
    +WebChannelBase.LogSaver.formatter_ = new goog.debug.TextFormatter();
    +
    +
    +/**
    + * Returns whether the LogSaver is enabled.
    + * @return {boolean} Whether saving is enabled or disabled.
    + */
    +WebChannelBase.LogSaver.isEnabled = function() {
    +  return WebChannelBase.LogSaver.enabled_;
    +};
    +
    +
    +/**
    + * Enables of disables the LogSaver.
    + * @param {boolean} enable Whether to enable or disable saving.
    + */
    +WebChannelBase.LogSaver.setEnabled = function(enable) {
    +  if (enable == WebChannelBase.LogSaver.enabled_) {
    +    return;
    +  }
    +
    +  var fn = WebChannelBase.LogSaver.addLogRecord;
    +  var logger = goog.log.getLogger('goog.net');
    +  if (enable) {
    +    goog.log.addHandler(logger, fn);
    +  } else {
    +    goog.log.removeHandler(logger, fn);
    +  }
    +};
    +
    +
    +/**
    + * Adds a log record.
    + * @param {goog.log.LogRecord} logRecord the LogRecord.
    + */
    +WebChannelBase.LogSaver.addLogRecord = function(logRecord) {
    +  WebChannelBase.LogSaver.buffer_.add(
    +      WebChannelBase.LogSaver.formatter_.formatRecord(logRecord));
    +};
    +
    +
    +/**
    + * Returns the log as a single string.
    + * @return {string} The log as a single string.
    + */
    +WebChannelBase.LogSaver.getBuffer = function() {
    +  return WebChannelBase.LogSaver.buffer_.getValues().join('');
    +};
    +
    +
    +/**
    + * Clears the buffer
    + */
    +WebChannelBase.LogSaver.clearBuffer = function() {
    +  WebChannelBase.LogSaver.buffer_.clear();
    +};
    +
    +
    +
    +/**
    + * Abstract base class for the channel handler
    + * @constructor
    + * @struct
    + */
    +WebChannelBase.Handler = function() {};
    +
    +
    +/**
    + * Callback handler for when a batch of response arrays is received from the
    + * server. When null, batched dispatching is disabled.
    + * @type {?function(!WebChannelBase, !Array<!Array<?>>)}
    + */
    +WebChannelBase.Handler.prototype.channelHandleMultipleArrays = null;
    +
    +
    +/**
    + * Whether it's okay to make a request to the server. A handler can return
    + * false if the channel should fail. For example, if the user has logged out,
    + * the handler may want all requests to fail immediately.
    + * @param {WebChannelBase} channel The channel.
    + * @return {WebChannelBase.Error} An error code. The code should
    + * return WebChannelBase.Error.OK to indicate it's okay. Any other
    + * error code will cause a failure.
    + */
    +WebChannelBase.Handler.prototype.okToMakeRequest = function(channel) {
    +  return WebChannelBase.Error.OK;
    +};
    +
    +
    +/**
    + * Indicates the WebChannel has successfully negotiated with the server
    + * and can now send and receive data.
    + * @param {WebChannelBase} channel The channel.
    + */
    +WebChannelBase.Handler.prototype.channelOpened = function(channel) {
    +};
    +
    +
    +/**
    + * New input is available for the application to process.
    + *
    + * @param {WebChannelBase} channel The channel.
    + * @param {Array<?>} array The data array.
    + */
    +WebChannelBase.Handler.prototype.channelHandleArray = function(channel, array) {
    +};
    +
    +
    +/**
    + * Indicates maps were successfully sent on the channel.
    + *
    + * @param {WebChannelBase} channel The channel.
    + * @param {Array<Wire.QueuedMap>} deliveredMaps The
    + *     array of maps that have been delivered to the server. This is a direct
    + *     reference to the internal array, so a copy should be made
    + *     if the caller desires a reference to the data.
    + */
    +WebChannelBase.Handler.prototype.channelSuccess =
    +    function(channel, deliveredMaps) {
    +};
    +
    +
    +/**
    + * Indicates an error occurred on the WebChannel.
    + *
    + * @param {WebChannelBase} channel The channel.
    + * @param {WebChannelBase.Error} error The error code.
    + */
    +WebChannelBase.Handler.prototype.channelError = function(channel, error) {
    +};
    +
    +
    +/**
    + * Indicates the WebChannel is closed. Also notifies about which maps,
    + * if any, that may not have been delivered to the server.
    + * @param {WebChannelBase} channel The channel.
    + * @param {Array<Wire.QueuedMap>=} opt_pendingMaps The
    + *     array of pending maps, which may or may not have been delivered to the
    + *     server.
    + * @param {Array<Wire.QueuedMap>=} opt_undeliveredMaps
    + *     The array of undelivered maps, which have definitely not been delivered
    + *     to the server.
    + */
    +WebChannelBase.Handler.prototype.channelClosed =
    +    function(channel, opt_pendingMaps, opt_undeliveredMaps) {
    +};
    +
    +
    +/**
    + * Gets any parameters that should be added at the time another connection is
    + * made to the server.
    + * @param {WebChannelBase} channel The channel.
    + * @return {!Object} Extra parameter keys and values to add to the
    + *                  requests.
    + */
    +WebChannelBase.Handler.prototype.getAdditionalParams = function(channel) {
    +  return {};
    +};
    +
    +
    +/**
    + * Gets the URI of an image that can be used to test network connectivity.
    + * @param {WebChannelBase} channel The channel.
    + * @return {goog.Uri?} A custom URI to load for the network test.
    + */
    +WebChannelBase.Handler.prototype.getNetworkTestImageUri = function(channel) {
    +  return null;
    +};
    +
    +
    +/**
    + * Gets whether this channel is currently active. This is used to determine the
    + * length of time to wait before retrying.
    + * @param {WebChannelBase} channel The channel.
    + * @return {boolean} Whether the channel is currently active.
    + */
    +WebChannelBase.Handler.prototype.isActive = function(channel) {
    +  return true;
    +};
    +
    +
    +/**
    + * Called by the channel if enumeration of the map throws an exception.
    + * @param {WebChannelBase} channel The channel.
    + * @param {Object} map The map that can't be enumerated.
    + */
    +WebChannelBase.Handler.prototype.badMapError = function(channel, map) {
    +};
    +
    +
    +/**
    + * Allows the handler to override a host prefix provided by the server. Will
    + * be called whenever the channel has received such a prefix and is considering
    + * its use.
    + * @param {?string} serverHostPrefix The host prefix provided by the server.
    + * @return {?string} The host prefix the client should use.
    + */
    +WebChannelBase.Handler.prototype.correctHostPrefix =
    +    function(serverHostPrefix) {
    +  return serverHostPrefix;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.html
    new file mode 100644
    index 0000000000..58918a60d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.net.webChannel.WebChannelBase</title>
    +<script src="../../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.webChannel.webChannelBaseTest');
    +</script>
    +<div id="debug" style="font-size: small"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.js
    new file mode 100644
    index 0000000000..b9a12b2c37
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbase_test.js
    @@ -0,0 +1,1512 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.net.webChannel.WebChannelBase.
    + * @suppress {accessControls} Private methods are accessed for test purposes.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.webChannelBaseTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.functions');
    +goog.require('goog.json');
    +goog.require('goog.labs.net.webChannel.ChannelRequest');
    +goog.require('goog.labs.net.webChannel.ForwardChannelRequestPool');
    +goog.require('goog.labs.net.webChannel.WebChannelBase');
    +goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
    +goog.require('goog.labs.net.webChannel.WebChannelDebug');
    +goog.require('goog.labs.net.webChannel.Wire');
    +goog.require('goog.labs.net.webChannel.netUtils');
    +goog.require('goog.labs.net.webChannel.requestStats');
    +goog.require('goog.labs.net.webChannel.requestStats.Stat');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.net.webChannel.webChannelBaseTest');
    +
    +
    +/**
    + * Delay between a network failure and the next network request.
    + */
    +var RETRY_TIME = 1000;
    +
    +
    +/**
    + * A really long time - used to make sure no more timeouts will fire.
    + */
    +var ALL_DAY_MS = 1000 * 60 * 60 * 24;
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var channel;
    +var deliveredMaps;
    +var handler;
    +var mockClock;
    +var gotError;
    +var numStatEvents;
    +var lastStatEvent;
    +var numTimingEvents;
    +var lastPostSize;
    +var lastPostRtt;
    +var lastPostRetryCount;
    +
    +// Set to true to see the channel debug output in the browser window.
    +var debug = false;
    +// Debug message to print out when debug is true.
    +var debugMessage = '';
    +
    +function debugToWindow(message) {
    +  if (debug) {
    +    debugMessage += message + '<br>';
    +    goog.dom.getElement('debug').innerHTML = debugMessage;
    +  }
    +}
    +
    +
    +/**
    + * Stubs goog.labs.net.webChannel.netUtils to always time out. It maintains the
    + * contract given by goog.labs.net.webChannel.netUtils.testNetwork, but always
    + * times out (calling callback(false)).
    + *
    + * stubNetUtils should be called in tests that require it before
    + * a call to testNetwork happens. It is reset at tearDown.
    + */
    +function stubNetUtils() {
    +  stubs.set(goog.labs.net.webChannel.netUtils, 'testLoadImage',
    +      function(url, timeout, callback) {
    +        goog.Timer.callOnce(goog.partial(callback, false), timeout);
    +      });
    +}
    +
    +
    +/**
    + * Stubs goog.labs.net.webChannel.ForwardChannelRequestPool.isSpdyEnabled_
    + * to manage the max pool size for the forward channel.
    + *
    + * @param {boolean} spdyEnabled Whether SPDY is enabled for the test.
    + */
    +function stubSpdyCheck(spdyEnabled) {
    +  stubs.set(goog.labs.net.webChannel.ForwardChannelRequestPool,
    +      'isSpdyEnabled_',
    +      function() {
    +        return spdyEnabled;
    +      });
    +}
    +
    +
    +
    +/**
    + * Mock ChannelRequest.
    + * @constructor
    + * @struct
    + * @final
    + */
    +var MockChannelRequest = function(channel, channelDebug, opt_sessionId,
    +    opt_requestId, opt_retryId) {
    +  this.channel_ = channel;
    +  this.channelDebug_ = channelDebug;
    +  this.sessionId_ = opt_sessionId;
    +  this.requestId_ = opt_requestId;
    +  this.successful_ = true;
    +  this.lastError_ = null;
    +  this.lastStatusCode_ = 200;
    +
    +  // For debugging, keep track of whether this is a back or forward channel.
    +  this.isBack = !!(opt_requestId == 'rpc');
    +  this.isForward = !this.isBack;
    +};
    +
    +MockChannelRequest.prototype.postData_ = null;
    +
    +MockChannelRequest.prototype.requestStartTime_ = null;
    +
    +MockChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {};
    +
    +MockChannelRequest.prototype.setTimeout = function(timeout) {};
    +
    +MockChannelRequest.prototype.setReadyStateChangeThrottle =
    +    function(throttle) {};
    +
    +MockChannelRequest.prototype.xmlHttpPost = function(uri, postData,
    +    decodeChunks) {
    +  this.channelDebug_.debug('---> POST: ' + uri + ', ' + postData + ', ' +
    +      decodeChunks);
    +  this.postData_ = postData;
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks,
    +    opt_noClose) {
    +  this.channelDebug_.debug('<--- GET: ' + uri + ', ' + decodeChunks + ', ' +
    +      opt_noClose);
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.tridentGet = function(uri, usingSecondaryDomain) {
    +  this.channelDebug_.debug('<---GET (T): ' + uri);
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.sendUsingImgTag = function(uri) {
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.cancel = function() {
    +  this.successful_ = false;
    +};
    +
    +MockChannelRequest.prototype.getSuccess = function() {
    +  return this.successful_;
    +};
    +
    +MockChannelRequest.prototype.getLastError = function() {
    +  return this.lastError_;
    +};
    +
    +MockChannelRequest.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +MockChannelRequest.prototype.getSessionId = function() {
    +  return this.sessionId_;
    +};
    +
    +MockChannelRequest.prototype.getRequestId = function() {
    +  return this.requestId_;
    +};
    +
    +MockChannelRequest.prototype.getPostData = function() {
    +  return this.postData_;
    +};
    +
    +MockChannelRequest.prototype.getRequestStartTime = function() {
    +  return this.requestStartTime_;
    +};
    +
    +MockChannelRequest.prototype.getXhr = function() {
    +  return null;
    +};
    +
    +
    +/**
    + * @suppress {invalidCasts} The cast from MockChannelRequest to
    + * ChannelRequest is invalid and will not compile.
    + */
    +function setUpPage() {
    +  // Use our MockChannelRequests instead of the real ones.
    +  goog.labs.net.webChannel.ChannelRequest.createChannelRequest = function(
    +      channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId) {
    +    return /** @type {!goog.labs.net.webChannel.ChannelRequest} */ (
    +        new MockChannelRequest(channel, channelDebug, opt_sessionId,
    +            opt_requestId, opt_retryId));
    +  };
    +
    +  // Mock out the stat notification code.
    +  goog.labs.net.webChannel.requestStats.notifyStatEvent = function(
    +      stat) {
    +    numStatEvents++;
    +    lastStatEvent = stat;
    +  };
    +
    +  goog.labs.net.webChannel.requestStats.notifyTimingEvent = function(
    +      size, rtt, retries) {
    +    numTimingEvents++;
    +    lastPostSize = size;
    +    lastPostRtt = rtt;
    +    lastPostRetryCount = retries;
    +  };
    +}
    +
    +function setUp() {
    +  numTimingEvents = 0;
    +  lastPostSize = null;
    +  lastPostRtt = null;
    +  lastPostRetryCount = null;
    +
    +  mockClock = new goog.testing.MockClock(true);
    +  channel = new goog.labs.net.webChannel.WebChannelBase('1');
    +  gotError = false;
    +
    +  handler = new goog.labs.net.webChannel.WebChannelBase.Handler();
    +  handler.channelOpened = function() {};
    +  handler.channelError = function(channel, error) {
    +    gotError = true;
    +  };
    +  handler.channelSuccess = function(channel, maps) {
    +    deliveredMaps = goog.array.clone(maps);
    +  };
    +
    +  /**
    +   * @suppress {checkTypes} The callback function type declaration is skipped.
    +   */
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    // Mock out the handler, and let it set a formatted user readable string
    +    // of the undelivered maps which we can use when verifying our assertions.
    +    if (opt_pendingMaps) {
    +      handler.pendingMapsString = formatArrayOfMaps(opt_pendingMaps);
    +    }
    +    if (opt_undeliveredMaps) {
    +      handler.undeliveredMapsString = formatArrayOfMaps(opt_undeliveredMaps);
    +    }
    +  };
    +  handler.channelHandleMultipleArrays = function() {};
    +  handler.channelHandleArray = function() {};
    +
    +  channel.setHandler(handler);
    +
    +  // Provide a predictable retry time for testing.
    +  channel.getRetryTime_ = function(retryCount) {
    +    return RETRY_TIME;
    +  };
    +
    +  var channelDebug = new goog.labs.net.webChannel.WebChannelDebug();
    +  channelDebug.debug = function(message) {
    +    debugToWindow(message);
    +  };
    +  channel.setChannelDebug(channelDebug);
    +
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +}
    +
    +
    +function tearDown() {
    +  mockClock.dispose();
    +  stubs.reset();
    +  debugToWindow('<hr>');
    +}
    +
    +
    +function getSingleForwardRequest() {
    +  var pool = channel.forwardChannelRequestPool_;
    +  if (!pool.hasPendingRequest()) {
    +    return null;
    +  }
    +  return pool.request_ || pool.requestPool_.getValues()[0];
    +}
    +
    +
    +/**
    + * Helper function to return a formatted string representing an array of maps.
    + */
    +function formatArrayOfMaps(arrayOfMaps) {
    +  var result = [];
    +  for (var i = 0; i < arrayOfMaps.length; i++) {
    +    var map = arrayOfMaps[i];
    +    var keys = map.map.getKeys();
    +    for (var j = 0; j < keys.length; j++) {
    +      var tmp = keys[j] + ':' + map.map.get(keys[j]) + (map.context ?
    +          ':' + map.context : '');
    +      result.push(tmp);
    +    }
    +  }
    +  return result.join(', ');
    +}
    +
    +
    +function testFormatArrayOfMaps() {
    +  // This function is used in a non-trivial test, so let's verify that it works.
    +  var map1 = new goog.structs.Map();
    +  map1.set('k1', 'v1');
    +  map1.set('k2', 'v2');
    +  var map2 = new goog.structs.Map();
    +  map2.set('k3', 'v3');
    +  var map3 = new goog.structs.Map();
    +  map3.set('k4', 'v4');
    +  map3.set('k5', 'v5');
    +  map3.set('k6', 'v6');
    +
    +  // One map.
    +  var a = [];
    +  a.push(new goog.labs.net.webChannel.Wire.QueuedMap(0, map1));
    +  assertEquals('k1:v1, k2:v2',
    +      formatArrayOfMaps(a));
    +
    +  // Many maps.
    +  var b = [];
    +  b.push(new goog.labs.net.webChannel.Wire.QueuedMap(0, map1));
    +  b.push(new goog.labs.net.webChannel.Wire.QueuedMap(0, map2));
    +  b.push(new goog.labs.net.webChannel.Wire.QueuedMap(0, map3));
    +  assertEquals('k1:v1, k2:v2, k3:v3, k4:v4, k5:v5, k6:v6',
    +      formatArrayOfMaps(b));
    +
    +  // One map with a context.
    +  var c = [];
    +  c.push(new goog.labs.net.webChannel.Wire.QueuedMap(
    +      0, map1, new String('c1')));
    +  assertEquals('k1:v1:c1, k2:v2:c1',
    +      formatArrayOfMaps(c));
    +}
    +
    +
    +/**
    + * @param {number=} opt_serverVersion
    + * @param {string=} opt_hostPrefix
    + * @param {string=} opt_uriPrefix
    + * @param {boolean=} opt_spdyEnabled
    + */
    +function connectForwardChannel(
    +    opt_serverVersion, opt_hostPrefix, opt_uriPrefix, opt_spdyEnabled) {
    +  stubSpdyCheck(!!opt_spdyEnabled);
    +  var uriPrefix = opt_uriPrefix || '';
    +  channel.connect(uriPrefix + '/test', uriPrefix + '/bind', null);
    +  mockClock.tick(0);
    +  completeTestConnection();
    +  completeForwardChannel(opt_serverVersion, opt_hostPrefix);
    +}
    +
    +
    +/**
    + * @param {number=} opt_serverVersion
    + * @param {string=} opt_hostPrefix
    + * @param {string=} opt_uriPrefix
    + * @param {boolean=} opt_spdyEnabled
    + */
    +function connect(opt_serverVersion, opt_hostPrefix, opt_uriPrefix,
    +    opt_spdyEnabled) {
    +  connectForwardChannel(opt_serverVersion, opt_hostPrefix, opt_uriPrefix,
    +      opt_spdyEnabled);
    +  completeBackChannel();
    +}
    +
    +
    +function disconnect() {
    +  channel.disconnect();
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeTestConnection() {
    +  completeForwardTestConnection();
    +  completeBackTestConnection();
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.OPENING,
    +      channel.getState());
    +}
    +
    +
    +function completeForwardTestConnection() {
    +  channel.connectionTest_.onRequestData(
    +      channel.connectionTest_.request_,
    +      '["b"]');
    +  channel.connectionTest_.onRequestComplete(
    +      channel.connectionTest_.request_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeBackTestConnection() {
    +  channel.connectionTest_.onRequestData(
    +      channel.connectionTest_.request_,
    +      '11111');
    +  mockClock.tick(0);
    +}
    +
    +
    +/**
    + * @param {number=} opt_serverVersion
    + * @param {string=} opt_hostPrefix
    + */
    +function completeForwardChannel(opt_serverVersion, opt_hostPrefix) {
    +  var responseData = '[[0,["c","1234567890ABCDEF",' +
    +      (opt_hostPrefix ? '"' + opt_hostPrefix + '"' : 'null') +
    +      (opt_serverVersion ? ',' + opt_serverVersion : '') +
    +      ']]]';
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      responseData);
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeBackChannel() {
    +  channel.onRequestData(
    +      channel.backChannelRequest_,
    +      '[[1,["foo"]]]');
    +  channel.onRequestComplete(
    +      channel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseDone() {
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      '[1,0,0]');  // mock data
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +/**
    + *
    + * @param {number=} opt_lastArrayIdSentFromServer
    + * @param {number=} opt_outstandingDataSize
    + */
    +function responseNoBackchannel(
    +    opt_lastArrayIdSentFromServer, opt_outstandingDataSize) {
    +  var responseData = goog.json.serialize(
    +      [0, opt_lastArrayIdSentFromServer, opt_outstandingDataSize]);
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      responseData);
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +function response(lastArrayIdSentFromServer, outstandingDataSize) {
    +  var responseData = goog.json.serialize(
    +      [1, lastArrayIdSentFromServer, outstandingDataSize]);
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      responseData
    +  );
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +function receive(data) {
    +  channel.onRequestData(
    +      channel.backChannelRequest_,
    +      '[[1,' + data + ']]');
    +  channel.onRequestComplete(
    +      channel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseTimeout() {
    +  getSingleForwardRequest().lastError_ =
    +      goog.labs.net.webChannel.ChannelRequest.Error.TIMEOUT;
    +  getSingleForwardRequest().successful_ = false;
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +/**
    + * @param {number=} opt_statusCode
    + */
    +function responseRequestFailed(opt_statusCode) {
    +  getSingleForwardRequest().lastError_ =
    +      goog.labs.net.webChannel.ChannelRequest.Error.STATUS;
    +  getSingleForwardRequest().lastStatusCode_ =
    +      opt_statusCode || 503;
    +  getSingleForwardRequest().successful_ = false;
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseUnknownSessionId() {
    +  getSingleForwardRequest().lastError_ =
    +      goog.labs.net.webChannel.ChannelRequest.Error.UNKNOWN_SESSION_ID;
    +  getSingleForwardRequest().successful_ = false;
    +  channel.onRequestComplete(
    +      getSingleForwardRequest());
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseActiveXBlocked() {
    +  channel.backChannelRequest_.lastError_ =
    +      goog.labs.net.webChannel.ChannelRequest.Error.ACTIVE_X_BLOCKED;
    +  channel.backChannelRequest_.successful_ = false;
    +  channel.onRequestComplete(
    +      channel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +/**
    + * @param {string} key
    + * @param {string} value
    + * @param {string=} opt_context
    + */
    +function sendMap(key, value, opt_context) {
    +  var map = new goog.structs.Map();
    +  map.set(key, value);
    +  channel.sendMap(map, opt_context);
    +  mockClock.tick(0);
    +}
    +
    +
    +function hasForwardChannel() {
    +  return !!getSingleForwardRequest();
    +}
    +
    +
    +function hasBackChannel() {
    +  return !!channel.backChannelRequest_;
    +}
    +
    +
    +function hasDeadBackChannelTimer() {
    +  return goog.isDefAndNotNull(channel.deadBackChannelTimerId_);
    +}
    +
    +
    +function assertHasForwardChannel() {
    +  assertTrue('Forward channel missing.', hasForwardChannel());
    +}
    +
    +
    +function assertHasBackChannel() {
    +  assertTrue('Back channel missing.', hasBackChannel());
    +}
    +
    +
    +function testConnect() {
    +  connect();
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.OPENED,
    +      channel.getState());
    +  // If the server specifies no version, the client assumes the latest version
    +  assertEquals(goog.labs.net.webChannel.Wire.LATEST_CHANNEL_VERSION,
    +               channel.channelVersion_);
    +  assertFalse(channel.isBuffered());
    +}
    +
    +function testConnect_backChannelEstablished() {
    +  connect();
    +  assertHasBackChannel();
    +}
    +
    +function testConnect_withServerHostPrefix() {
    +  connect(undefined, 'serverHostPrefix');
    +  assertEquals('serverHostPrefix', channel.hostPrefix_);
    +}
    +
    +function testConnect_withClientHostPrefix() {
    +  handler.correctHostPrefix = function(hostPrefix) {
    +    return 'clientHostPrefix';
    +  };
    +  connect();
    +  assertEquals('clientHostPrefix', channel.hostPrefix_);
    +}
    +
    +function testConnect_overrideServerHostPrefix() {
    +  handler.correctHostPrefix = function(hostPrefix) {
    +    return 'clientHostPrefix';
    +  };
    +  connect(undefined, 'serverHostPrefix');
    +  assertEquals('clientHostPrefix', channel.hostPrefix_);
    +}
    +
    +function testConnect_withServerVersion() {
    +  connect(8);
    +  assertEquals(8, channel.channelVersion_);
    +}
    +
    +function testConnect_notOkToMakeRequestForTest() {
    +  handler.okToMakeRequest = goog.functions.constant(
    +      goog.labs.net.webChannel.WebChannelBase.Error.NETWORK);
    +  channel.connect('/test', '/bind', null);
    +  mockClock.tick(0);
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +               channel.getState());
    +}
    +
    +function testConnect_notOkToMakeRequestForBind() {
    +  channel.connect('/test', '/bind', null);
    +  mockClock.tick(0);
    +  completeTestConnection();
    +  handler.okToMakeRequest = goog.functions.constant(
    +      goog.labs.net.webChannel.WebChannelBase.Error.NETWORK);
    +  completeForwardChannel();
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +               channel.getState());
    +}
    +
    +
    +function testSendMap() {
    +  connect();
    +  sendMapOnce();
    +}
    +
    +
    +function testSendMapWithSpdyEnabled() {
    +  connect(undefined, undefined, undefined, true);
    +  sendMapOnce();
    +}
    +
    +
    +function sendMapOnce() {
    +  assertEquals(1, numTimingEvents);
    +  sendMap('foo', 'bar');
    +  responseDone();
    +  assertEquals(2, numTimingEvents);
    +  assertEquals('foo:bar', formatArrayOfMaps(deliveredMaps));
    +}
    +
    +
    +function testSendMap_twice() {
    +  connect();
    +  sendMapTwice();
    +}
    +
    +
    +function testSendMap_twiceWithSpdyEnabled() {
    +  connect(undefined, undefined, undefined, true);
    +  sendMapTwice();
    +}
    +
    +
    +function sendMapTwice() {
    +  sendMap('foo1', 'bar1');
    +  responseDone();
    +  assertEquals('foo1:bar1', formatArrayOfMaps(deliveredMaps));
    +  sendMap('foo2', 'bar2');
    +  responseDone();
    +  assertEquals('foo2:bar2', formatArrayOfMaps(deliveredMaps));
    +}
    +
    +
    +function testSendMap_andReceive() {
    +  connect();
    +  sendMap('foo', 'bar');
    +  responseDone();
    +  receive('["the server reply"]');
    +}
    +
    +
    +function testReceive() {
    +  connect();
    +  receive('["message from server"]');
    +  assertHasBackChannel();
    +}
    +
    +
    +function testReceive_twice() {
    +  connect();
    +  receive('["message one from server"]');
    +  receive('["message two from server"]');
    +  assertHasBackChannel();
    +}
    +
    +
    +function testReceive_andSendMap() {
    +  connect();
    +  receive('["the server reply"]');
    +  sendMap('foo', 'bar');
    +  responseDone();
    +  assertHasBackChannel();
    +}
    +
    +
    +function testBackChannelRemainsEstablished_afterSingleSendMap() {
    +  connect();
    +
    +  sendMap('foo', 'bar');
    +  responseDone();
    +  receive('["ack"]');
    +
    +  assertHasBackChannel();
    +}
    +
    +
    +function testBackChannelRemainsEstablished_afterDoubleSendMap() {
    +  connect();
    +
    +  sendMap('foo1', 'bar1');
    +  sendMap('foo2', 'bar2');
    +  responseDone();
    +  receive('["ack"]');
    +
    +  // This assertion would fail prior to CL 13302660.
    +  assertHasBackChannel();
    +}
    +
    +
    +function testTimingEvent() {
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +  sendMap('', '');
    +  assertEquals(1, numTimingEvents);
    +  mockClock.tick(20);
    +  var expSize = getSingleForwardRequest().getPostData().length;
    +  responseDone();
    +
    +  assertEquals(2, numTimingEvents);
    +  assertEquals(expSize, lastPostSize);
    +  assertEquals(20, lastPostRtt);
    +  assertEquals(0, lastPostRetryCount);
    +
    +  sendMap('abcdefg', '123456');
    +  expSize = getSingleForwardRequest().getPostData().length;
    +  responseTimeout();
    +  assertEquals(2, numTimingEvents);
    +  mockClock.tick(RETRY_TIME + 1);
    +  responseDone();
    +  assertEquals(3, numTimingEvents);
    +  assertEquals(expSize, lastPostSize);
    +  assertEquals(1, lastPostRetryCount);
    +  assertEquals(1, lastPostRtt);
    +
    +}
    +
    +
    +/**
    + * Make sure that dropping the forward channel retry limit below the retry count
    + * reports an error, and prevents another request from firing.
    + */
    +function testSetFailFastWhileWaitingForRetry() {
    +  stubNetUtils();
    +
    +  connect();
    +  setFailFastWhileWaitingForRetry();
    +}
    +
    +
    +function testSetFailFastWhileWaitingForRetryWithSpdyEnabled() {
    +  stubNetUtils();
    +
    +  connect(undefined, undefined, undefined, true);
    +  setFailFastWhileWaitingForRetry();
    +}
    +
    +
    +function setFailFastWhileWaitingForRetry() {
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +
    +  // Almost finish the between-retry timeout.
    +  mockClock.tick(RETRY_TIME - 1);
    +  assertNotNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +
    +  // Setting max retries to 0 should cancel the timer and raise an error.
    +  channel.setFailFast(true);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  assertEquals(0, deliveredMaps.length);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  // initial failure.
    +  gotError = false;
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +  assertTrue('No error after network ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +/**
    + * Make sure that dropping the forward channel retry limit below the retry count
    + * reports an error, and prevents another request from firing.
    + */
    +function testSetFailFastWhileRetryXhrIsInFlight() {
    +  stubNetUtils();
    +
    +  connect();
    +  setFailFastWhileRetryXhrIsInFlight();
    +}
    +
    +
    +function testSetFailFastWhileRetryXhrIsInFlightWithSpdyEnabled() {
    +  stubNetUtils();
    +
    +  connect(undefined, undefined, undefined, true);
    +  setFailFastWhileRetryXhrIsInFlight();
    +}
    +
    +
    +function setFailFastWhileRetryXhrIsInFlight() {
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +
    +  // Wait for the between-retry timeout.
    +  mockClock.tick(RETRY_TIME);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(1, channel.forwardChannelRetryCount_);
    +
    +  // Simulate a second watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(2, channel.forwardChannelRetryCount_);
    +
    +  // Wait for another between-retry timeout.
    +  mockClock.tick(RETRY_TIME);
    +  // Now the third req is in flight.
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(2, channel.forwardChannelRetryCount_);
    +
    +  // Set fail fast, killing the request
    +  channel.setFailFast(true);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(2, channel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  gotError = false;
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +  assertTrue('No error after network ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(2, channel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +/**
    + * Makes sure that setting fail fast while not retrying doesn't cause a failure.
    + */
    +function testSetFailFastAtRetryCount() {
    +  stubNetUtils();
    +
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +
    +  // Set fail fast.
    +  channel.setFailFast(true);
    +  // Request should still be alive.
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNotNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout. Now we should get an error.
    +  responseTimeout();
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  // initial failure.
    +  gotError = false;
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +  assertTrue('No error after network ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(channel.forwardChannelTimerId_);
    +  assertNull(getSingleForwardRequest());
    +  assertEquals(0, channel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +function testRequestFailedClosesChannel() {
    +  stubNetUtils();
    +
    +  connect();
    +  requestFailedClosesChannel();
    +}
    +
    +
    +function testRequestFailedClosesChannelWithSpdyEnabled() {
    +  stubNetUtils();
    +
    +  connect(undefined, undefined, undefined, true);
    +  requestFailedClosesChannel();
    +}
    +
    +
    +function requestFailedClosesChannel() {
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  responseRequestFailed();
    +
    +  assertEquals('Should be closed immediately after request failed.',
    +      goog.labs.net.webChannel.WebChannelBase.State.CLOSED, channel.getState());
    +
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +
    +  assertEquals('Should remain closed after the ping timeout.',
    +      goog.labs.net.webChannel.WebChannelBase.State.CLOSED, channel.getState());
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce() {
    +  stubNetUtils();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseUnknownSessionId();
    +
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.labs.net.webChannel.requestStats.Stat.ERROR_OTHER,
    +      lastStatEvent);
    +
    +  numStatEvents = 0;
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +  assertEquals('No new stat events should be reported.', 0, numStatEvents);
    +}
    +
    +
    +function testActiveXBlockedEventReportedOnlyOnce() {
    +  stubNetUtils();
    +
    +  connectForwardChannel();
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseActiveXBlocked();
    +
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.labs.net.webChannel.requestStats.Stat.ERROR_OTHER,
    +      lastStatEvent);
    +
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +  assertEquals('No new stat events should be reported.', 1, numStatEvents);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce_onNetworkUp() {
    +  stubNetUtils();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseRequestFailed();
    +
    +  assertEquals('No stat event should be reported before we know the reason.',
    +      0, numStatEvents);
    +
    +  // Let the ping time out.
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +
    +  // Assert we report the correct stat event.
    +  assertEquals(1, numStatEvents);
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.ERROR_NETWORK,
    +      lastStatEvent);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce_onNetworkDown() {
    +  stubNetUtils();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseRequestFailed();
    +
    +  assertEquals('No stat event should be reported before we know the reason.',
    +      0, numStatEvents);
    +
    +  // Wait half the ping timeout period, and then fake the network being up.
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT / 2);
    +  channel.testNetworkCallback_(true);
    +
    +  // Assert we report the correct stat event.
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.labs.net.webChannel.requestStats.Stat.ERROR_OTHER,
    +      lastStatEvent);
    +}
    +
    +
    +function testOutgoingMapsAwaitsResponse() {
    +  connect();
    +  outgoingMapsAwaitsResponse();
    +}
    +
    +
    +function testOutgoingMapsAwaitsResponseWithSpdyEnabled() {
    +  connect(undefined, undefined, undefined, true);
    +  outgoingMapsAwaitsResponse();
    +}
    +
    +
    +function outgoingMapsAwaitsResponse() {
    +  assertEquals(0, channel.outgoingMaps_.length);
    +
    +  sendMap('foo1', 'bar');
    +  assertEquals(0, channel.outgoingMaps_.length);
    +  sendMap('foo2', 'bar');
    +  assertEquals(1, channel.outgoingMaps_.length);
    +  sendMap('foo3', 'bar');
    +  assertEquals(2, channel.outgoingMaps_.length);
    +  sendMap('foo4', 'bar');
    +  assertEquals(3, channel.outgoingMaps_.length);
    +
    +  responseDone();
    +  // Now the forward channel request is completed and a new started, so all maps
    +  // are dequeued from the array of outgoing maps into this new forward request.
    +  assertEquals(0, channel.outgoingMaps_.length);
    +}
    +
    +
    +function testUndeliveredMaps_doesNotNotifyWhenSuccessful() {
    +  /**
    +   * @suppress {checkTypes} The callback function type declaration is skipped.
    +   */
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    if (opt_pendingMaps || opt_undeliveredMaps) {
    +      fail('No pending or undelivered maps should be reported.');
    +    }
    +  };
    +
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  responseDone();
    +  sendMap('foo2', 'bar2');
    +  responseDone();
    +  disconnect();
    +}
    +
    +
    +function testUndeliveredMaps_doesNotNotifyIfNothingWasSent() {
    +  /**
    +   * @suppress {checkTypes} The callback function type declaration is skipped.
    +   */
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    if (opt_pendingMaps || opt_undeliveredMaps) {
    +      fail('No pending or undelivered maps should be reported.');
    +    }
    +  };
    +
    +  connect();
    +  mockClock.tick(ALL_DAY_MS);
    +  disconnect();
    +}
    +
    +
    +function testUndeliveredMaps_clearsPendingMapsAfterNotifying() {
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  sendMap('foo2', 'bar2');
    +  sendMap('foo3', 'bar3');
    +
    +  assertEquals(1, channel.pendingMaps_.length);
    +  assertEquals(2, channel.outgoingMaps_.length);
    +
    +  disconnect();
    +
    +  assertEquals(0, channel.pendingMaps_.length);
    +  assertEquals(0, channel.outgoingMaps_.length);
    +}
    +
    +
    +function testUndeliveredMaps_notifiesWithContext() {
    +  connect();
    +
    +  // First send two messages that succeed.
    +  sendMap('foo1', 'bar1', 'context1');
    +  responseDone();
    +  sendMap('foo2', 'bar2', 'context2');
    +  responseDone();
    +
    +  // Pretend the server hangs and no longer responds.
    +  sendMap('foo3', 'bar3', 'context3');
    +  sendMap('foo4', 'bar4', 'context4');
    +  sendMap('foo5', 'bar5', 'context5');
    +
    +  // Give up.
    +  disconnect();
    +
    +  // Assert that we are informed of any undelivered messages; both about
    +  // #3 that was sent but which we don't know if the server received, and
    +  // #4 and #5 which remain in the outgoing maps and have not yet been sent.
    +  assertEquals('foo3:bar3:context3', handler.pendingMapsString);
    +  assertEquals('foo4:bar4:context4, foo5:bar5:context5',
    +      handler.undeliveredMapsString);
    +}
    +
    +
    +function testUndeliveredMaps_serviceUnavailable() {
    +  // Send a few maps, and let one fail.
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  responseDone();
    +  sendMap('foo2', 'bar2');
    +  responseRequestFailed();
    +
    +  // After a failure, the channel should be closed.
    +  disconnect();
    +
    +  assertEquals('foo2:bar2', handler.pendingMapsString);
    +  assertEquals('', handler.undeliveredMapsString);
    +}
    +
    +
    +function testUndeliveredMaps_onPingTimeout() {
    +  stubNetUtils();
    +
    +  connect();
    +
    +  // Send a message.
    +  sendMap('foo1', 'bar1');
    +
    +  // Fake REQUEST_FAILED, triggering a ping to check the network.
    +  responseRequestFailed();
    +
    +  // Let the ping time out, unsuccessfully.
    +  mockClock.tick(goog.labs.net.webChannel.netUtils.NETWORK_TIMEOUT);
    +
    +  // Assert channel is closed.
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +      channel.getState());
    +
    +  // Assert that the handler is notified about the undelivered messages.
    +  assertEquals('foo1:bar1', handler.pendingMapsString);
    +  assertEquals('', handler.undeliveredMapsString);
    +}
    +
    +
    +function testResponseNoBackchannelPostNotBeforeBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  mockClock.tick(10);
    +  assertFalse(channel.backChannelRequest_.getRequestStartTime() <
    +      getSingleForwardRequest().getRequestStartTime());
    +  responseNoBackchannel();
    +  assertNotEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  response(-1, 0);
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE + 1);
    +  sendMap('foo2', 'bar2');
    +  assertTrue(channel.backChannelRequest_.getRequestStartTime() +
    +      goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE <
    +      getSingleForwardRequest().getRequestStartTime());
    +  responseNoBackchannel();
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannelWithNoBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertNull(channel.backChannelTimerId_);
    +  channel.backChannelRequest_.cancel();
    +  channel.backChannelRequest_ = null;
    +  responseNoBackchannel();
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannelWithStartTimer() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  channel.backChannelRequest_.cancel();
    +  channel.backChannelRequest_ = null;
    +  channel.backChannelTimerId_ = 123;
    +  responseNoBackchannel();
    +  assertNotEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseWithNoArraySent() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  // Send a response as if the server hasn't sent down an array.
    +  response(-1, 0);
    +
    +  // POST response with an array ID lower than our last received is OK.
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +}
    +
    +
    +function testResponseWithArraysMissing() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE * 2);
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testMultipleResponsesWithArraysMissing() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  sendMap('foo2', 'bar2');
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE);
    +  response(8, 119);
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE);
    +  // The original timer should still fire.
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testOnlyRetryOnceBasedOnResponse() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  assertTrue(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE * 2);
    +  assertEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +  assertEquals(1, channel.backChannelRetryCount_);
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE);
    +  sendMap('foo2', 'bar2');
    +  assertFalse(hasDeadBackChannelTimer());
    +  response(8, 119);
    +  assertFalse(hasDeadBackChannelTimer());
    +}
    +
    +
    +function testResponseWithArraysMissingAndLiveChannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE);
    +  assertTrue(hasDeadBackChannelTimer());
    +  receive('["ack"]');
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE);
    +  assertNotEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseWithBigOutstandingData() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays and 50kbytes.
    +  response(7, 50000);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE * 2);
    +  assertNotEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseInBufferedMode() {
    +  connect(8);
    +  channel.useChunked_ = false;
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, channel.lastPostResponseArrayId_);
    +  response(7, 111);
    +
    +  assertEquals(1, channel.lastArrayId_);
    +  assertEquals(7, channel.lastPostResponseArrayId_);
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.labs.net.webChannel.WebChannelBase.RTT_ESTIMATE * 2);
    +  assertNotEquals(
    +      goog.labs.net.webChannel.requestStats.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseWithGarbage() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      'garbage'
    +  );
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +      channel.getState());
    +}
    +
    +
    +function testResponseWithGarbageInArray() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      '["garbage"]'
    +  );
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +      channel.getState());
    +}
    +
    +
    +function testResponseWithEvilData() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  channel.onRequestData(
    +      getSingleForwardRequest(),
    +      'foo=<script>evil()\<\/script>&' + 'bar=<script>moreEvil()\<\/script>');
    +  assertEquals(goog.labs.net.webChannel.WebChannelBase.State.CLOSED,
    +      channel.getState());
    +}
    +
    +
    +function testPathAbsolute() {
    +  connect(8, undefined, '/talkgadget');
    +  assertEquals(channel.backChannelUri_.getDomain(),
    +      window.location.hostname);
    +  assertEquals(channel.forwardChannelUri_.getDomain(),
    +      window.location.hostname);
    +}
    +
    +
    +function testPathRelative() {
    +  connect(8, undefined, 'talkgadget');
    +  assertEquals(channel.backChannelUri_.getDomain(),
    +      window.location.hostname);
    +  assertEquals(channel.forwardChannelUri_.getDomain(),
    +      window.location.hostname);
    +}
    +
    +
    +function testPathWithHost() {
    +  connect(8, undefined, 'https://example.com');
    +  assertEquals(channel.backChannelUri_.getScheme(), 'https');
    +  assertEquals(channel.backChannelUri_.getDomain(), 'example.com');
    +  assertEquals(channel.forwardChannelUri_.getScheme(), 'https');
    +  assertEquals(channel.forwardChannelUri_.getDomain(), 'example.com');
    +}
    +
    +function testCreateXhrIo() {
    +  var xhr = channel.createXhrIo(null);
    +  assertFalse(xhr.getWithCredentials());
    +
    +  assertThrows(
    +      'Error connection to different host without CORS',
    +      goog.bind(channel.createXhrIo, channel, 'some_host'));
    +
    +  channel.setSupportsCrossDomainXhrs(true);
    +
    +  xhr = channel.createXhrIo(null);
    +  assertTrue(xhr.getWithCredentials());
    +
    +  xhr = channel.createXhrIo('some_host');
    +  assertTrue(xhr.getWithCredentials());
    +}
    +
    +function testSpdyLimitOption() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  stubSpdyCheck(true);
    +  var webChannelDefault = webChannelTransport.createWebChannel('/foo');
    +  assertEquals(10,
    +      webChannelDefault.getRuntimeProperties().getConcurrentRequestLimit());
    +  assertTrue(webChannelDefault.getRuntimeProperties().isSpdyEnabled());
    +
    +  var options = {'concurrentRequestLimit': 100};
    +
    +  stubSpdyCheck(false);
    +  var webChannelDisabled = webChannelTransport.createWebChannel(
    +      '/foo', options);
    +  assertEquals(1,
    +      webChannelDisabled.getRuntimeProperties().getConcurrentRequestLimit());
    +  assertFalse(webChannelDisabled.getRuntimeProperties().isSpdyEnabled());
    +
    +  stubSpdyCheck(true);
    +  var webChannelEnabled = webChannelTransport.createWebChannel('/foo', options);
    +  assertEquals(100,
    +      webChannelEnabled.getRuntimeProperties().getConcurrentRequestLimit());
    +  assertTrue(webChannelEnabled.getRuntimeProperties().isSpdyEnabled());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport.js
    new file mode 100644
    index 0000000000..16c19c29fa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport.js
    @@ -0,0 +1,376 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of a WebChannel transport using WebChannelBase.
    + *
    + * When WebChannelBase is used as the underlying transport, the capabilities
    + * of the WebChannel are limited to what's supported by the implementation.
    + * Particularly, multiplexing is not possible, and only strings are
    + * supported as message types.
    + *
    + */
    +
    +goog.provide('goog.labs.net.webChannel.WebChannelBaseTransport');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.labs.net.webChannel.WebChannelBase');
    +goog.require('goog.log');
    +goog.require('goog.net.WebChannel');
    +goog.require('goog.net.WebChannelTransport');
    +goog.require('goog.object');
    +goog.require('goog.string.path');
    +
    +
    +
    +/**
    + * Implementation of {@link goog.net.WebChannelTransport} with
    + * {@link goog.labs.net.webChannel.WebChannelBase} as the underlying channel
    + * implementation.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.net.WebChannelTransport}
    + * @final
    + */
    +goog.labs.net.webChannel.WebChannelBaseTransport = function() {};
    +
    +
    +goog.scope(function() {
    +var WebChannelBaseTransport = goog.labs.net.webChannel.WebChannelBaseTransport;
    +var WebChannelBase = goog.labs.net.webChannel.WebChannelBase;
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.prototype.createWebChannel = function(
    +    url, opt_options) {
    +  return new WebChannelBaseTransport.Channel(url, opt_options);
    +};
    +
    +
    +
    +/**
    + * Implementation of the {@link goog.net.WebChannel} interface.
    + *
    + * @param {string} url The URL path for the new WebChannel instance.
    + * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the
    + *     new WebChannel instance.
    + *
    + * @constructor
    + * @implements {goog.net.WebChannel}
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +WebChannelBaseTransport.Channel = function(url, opt_options) {
    +  WebChannelBaseTransport.Channel.base(this, 'constructor');
    +
    +  /**
    +   * The underlying channel object.
    +   *
    +   * @private {!WebChannelBase}
    +   */
    +  this.channel_ = new WebChannelBase(opt_options);
    +
    +  /**
    +   * The URL of the target server end-point.
    +   *
    +   * @private {string}
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * The test URL of the target server end-point. This value defaults to
    +   * this.url_ + '/test'.
    +   *
    +   * @private {string}
    +   */
    +  this.testUrl_ = (opt_options && opt_options.testUrl) ? opt_options.testUrl :
    +      goog.string.path.join(this.url_, 'test');
    +
    +  /**
    +   * The logger for this class.
    +   * @private {goog.log.Logger}
    +   */
    +  this.logger_ = goog.log.getLogger(
    +      'goog.labs.net.webChannel.WebChannelBaseTransport');
    +
    +  /**
    +   * @private {Object<string, string>} messageUrlParams_ Extra URL parameters
    +   * to be added to each HTTP request.
    +   */
    +  this.messageUrlParams_ =
    +      (opt_options && opt_options.messageUrlParams) || null;
    +
    +  var messageHeaders = (opt_options && opt_options.messageHeaders) || null;
    +
    +  // default is false
    +  if (opt_options && opt_options.clientProtocolHeaderRequired) {
    +    if (messageHeaders) {
    +      goog.object.set(messageHeaders,
    +          goog.net.WebChannel.X_CLIENT_PROTOCOL,
    +          goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);
    +    } else {
    +      messageHeaders = goog.object.create(
    +          goog.net.WebChannel.X_CLIENT_PROTOCOL,
    +          goog.net.WebChannel.X_CLIENT_PROTOCOL_WEB_CHANNEL);
    +    }
    +  }
    +
    +  this.channel_.setExtraHeaders(messageHeaders);
    +
    +  /**
    +   * @private {boolean} supportsCrossDomainXhr_ Whether to enable CORS.
    +   */
    +  this.supportsCrossDomainXhr_ =
    +      (opt_options && opt_options.supportsCrossDomainXhr) || false;
    +};
    +goog.inherits(WebChannelBaseTransport.Channel, goog.events.EventTarget);
    +
    +
    +/**
    + * The channel handler.
    + *
    + * @type {WebChannelBase.Handler}
    + * @private
    + */
    +WebChannelBaseTransport.Channel.prototype.channelHandler_ = null;
    +
    +
    +/**
    + * Test path is always set to "/url/test".
    + *
    + * @override
    + */
    +WebChannelBaseTransport.Channel.prototype.open = function() {
    +  this.channel_.connect(this.testUrl_, this.url_,
    +                        (this.messageUrlParams_ || undefined));
    +
    +  this.channelHandler_ = new WebChannelBaseTransport.Channel.Handler_(this);
    +  this.channel_.setHandler(this.channelHandler_);
    +  if (this.supportsCrossDomainXhr_) {
    +    this.channel_.setSupportsCrossDomainXhrs(true);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.prototype.close = function() {
    +  this.channel_.disconnect();
    +};
    +
    +
    +/**
    + * The WebChannelBase only supports object types.
    + *
    + * @param {!goog.net.WebChannel.MessageData} message The message to send.
    + * @override
    + */
    +WebChannelBaseTransport.Channel.prototype.send = function(message) {
    +  goog.asserts.assert(goog.isObject(message), 'only object type expected');
    +  this.channel_.sendMap(message);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.prototype.disposeInternal = function() {
    +  this.channel_.setHandler(null);
    +  delete this.channelHandler_;
    +  this.channel_.disconnect();
    +  delete this.channel_;
    +
    +  WebChannelBaseTransport.Channel.base(this, 'disposeInternal');
    +};
    +
    +
    +
    +/**
    + * The message event.
    + *
    + * @param {!Array<?>} array The data array from the underlying channel.
    + * @constructor
    + * @extends {goog.net.WebChannel.MessageEvent}
    + * @final
    + */
    +WebChannelBaseTransport.Channel.MessageEvent = function(array) {
    +  WebChannelBaseTransport.Channel.MessageEvent.base(this, 'constructor');
    +
    +  this.data = array;
    +};
    +goog.inherits(WebChannelBaseTransport.Channel.MessageEvent,
    +              goog.net.WebChannel.MessageEvent);
    +
    +
    +
    +/**
    + * The error event.
    + *
    + * @param {WebChannelBase.Error} error The error code.
    + * @constructor
    + * @extends {goog.net.WebChannel.ErrorEvent}
    + * @final
    + */
    +WebChannelBaseTransport.Channel.ErrorEvent = function(error) {
    +  WebChannelBaseTransport.Channel.ErrorEvent.base(this, 'constructor');
    +
    +  /**
    +   * Transport specific error code is not to be propagated with the event.
    +   */
    +  this.status = goog.net.WebChannel.ErrorStatus.NETWORK_ERROR;
    +};
    +goog.inherits(WebChannelBaseTransport.Channel.ErrorEvent,
    +              goog.net.WebChannel.ErrorEvent);
    +
    +
    +
    +/**
    + * Implementation of {@link WebChannelBase.Handler} interface.
    + *
    + * @param {!WebChannelBaseTransport.Channel} channel The enclosing WebChannel.
    + *
    + * @constructor
    + * @extends {WebChannelBase.Handler}
    + * @private
    + */
    +WebChannelBaseTransport.Channel.Handler_ = function(channel) {
    +  WebChannelBaseTransport.Channel.Handler_.base(this, 'constructor');
    +
    +  /**
    +   * @type {!WebChannelBaseTransport.Channel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +};
    +goog.inherits(WebChannelBaseTransport.Channel.Handler_, WebChannelBase.Handler);
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.Handler_.prototype.channelOpened = function(
    +    channel) {
    +  goog.log.info(this.channel_.logger_,
    +      'WebChannel opened on ' + this.channel_.url_);
    +  this.channel_.dispatchEvent(goog.net.WebChannel.EventType.OPEN);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.Handler_.prototype.channelHandleArray =
    +    function(channel, array) {
    +  goog.asserts.assert(array, 'array expected to be defined');
    +  this.channel_.dispatchEvent(
    +      new WebChannelBaseTransport.Channel.MessageEvent(array));
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.Handler_.prototype.channelError = function(
    +    channel, error) {
    +  goog.log.info(this.channel_.logger_,
    +      'WebChannel aborted on ' + this.channel_.url_ +
    +      ' due to channel error: ' + error);
    +  this.channel_.dispatchEvent(
    +      new WebChannelBaseTransport.Channel.ErrorEvent(error));
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.Handler_.prototype.channelClosed = function(
    +    channel, opt_pendingMaps, opt_undeliveredMaps) {
    +  goog.log.info(this.channel_.logger_,
    +      'WebChannel closed on ' + this.channel_.url_);
    +  this.channel_.dispatchEvent(goog.net.WebChannel.EventType.CLOSE);
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.Channel.prototype.getRuntimeProperties = function() {
    +  return new WebChannelBaseTransport.ChannelProperties(this.channel_);
    +};
    +
    +
    +
    +/**
    + * Implementation of the {@link goog.net.WebChannel.RuntimeProperties}.
    + *
    + * @param {!WebChannelBase} channel The underlying channel object.
    + *
    + * @constructor
    + * @implements {goog.net.WebChannel.RuntimeProperties}
    + * @final
    + */
    +WebChannelBaseTransport.ChannelProperties = function(channel) {
    +  /**
    +   * The underlying channel object.
    +   *
    +   * @private {!WebChannelBase}
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The flag to turn on/off server-side flow control.
    +   *
    +   * @private {boolean}
    +   */
    +  this.serverFlowControlEnabled_ = false;
    +
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.ChannelProperties.prototype.getConcurrentRequestLimit =
    +    function() {
    +  return this.channel_.getForwardChannelRequestPool().getMaxSize();
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.ChannelProperties.prototype.isSpdyEnabled =
    +    function() {
    +  return this.getConcurrentRequestLimit() > 1;
    +};
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.ChannelProperties.prototype.setServerFlowControl =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @override
    + */
    +WebChannelBaseTransport.ChannelProperties.prototype.getNonAckedMessageCount =
    +    goog.abstractMethod;
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.html
    new file mode 100644
    index 0000000000..2096e5ee57
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.net.webChannel.WebChannelBaseTransport</title>
    +<script src="../../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.webChannel.webChannelBaseTransportTest');
    +</script>
    +<div id="debug" style="font-size: small"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.js
    new file mode 100644
    index 0000000000..5a334b337e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchannelbasetransport_test.js
    @@ -0,0 +1,254 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.net.webChannel.WebChannelBase.
    + * @suppress {accessControls} Private methods are accessed for test purposes.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.webChannelBaseTransportTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
    +goog.require('goog.net.WebChannel');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.net.webChannel.webChannelBaseTransportTest');
    +
    +
    +var webChannel;
    +var channelUrl = 'http://127.0.0.1:8080/channel';
    +
    +
    +function setUp() {
    +}
    +
    +function tearDown() {
    +  goog.dispose(webChannel);
    +}
    +
    +function testOpenWithUrl() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  webChannel = webChannelTransport.createWebChannel(channelUrl);
    +
    +  var eventFired = false;
    +  goog.events.listen(webChannel, goog.net.WebChannel.EventType.OPEN,
    +      function(e) {
    +        eventFired = true;
    +      });
    +
    +  webChannel.open();
    +  assertFalse(eventFired);
    +
    +  var channel = webChannel.channel_;
    +  assertNotNull(channel);
    +
    +  simulateOpenEvent(channel);
    +  assertTrue(eventFired);
    +}
    +
    +function testOpenWithTestUrl() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {'testUrl': channelUrl + '/footest'};
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  var testPath = webChannel.channel_.connectionTest_.path_;
    +  assertNotNullNorUndefined(testPath);
    +}
    +
    +function testOpenWithCustomHeaders() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {'messageHeaders': {'foo-key': 'foo-value'}};
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  var extraHeaders_ = webChannel.channel_.extraHeaders_;
    +  assertNotNullNorUndefined(extraHeaders_);
    +  assertEquals('foo-value', extraHeaders_['foo-key']);
    +  assertEquals(undefined, extraHeaders_['X-Client-Protocol']);
    +}
    +
    +function testClientProtocolHeaderRequired() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {'clientProtocolHeaderRequired': true};
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  var extraHeaders_ = webChannel.channel_.extraHeaders_;
    +  assertNotNullNorUndefined(extraHeaders_);
    +  assertEquals('webchannel', extraHeaders_['X-Client-Protocol']);
    +}
    +
    +function testClientProtocolHeaderNotRequiredByDefault() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  webChannel = webChannelTransport.createWebChannel(channelUrl);
    +  webChannel.open();
    +
    +  var extraHeaders_ = webChannel.channel_.extraHeaders_;
    +  assertNull(extraHeaders_);
    +}
    +
    +function testClientProtocolHeaderRequiredWithCustomHeader() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {
    +    'clientProtocolHeaderRequired': true,
    +    'messageHeaders': {'foo-key': 'foo-value'}
    +  };
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  var extraHeaders_ = webChannel.channel_.extraHeaders_;
    +  assertNotNullNorUndefined(extraHeaders_);
    +  assertEquals('foo-value', extraHeaders_['foo-key']);
    +  assertEquals('webchannel', extraHeaders_['X-Client-Protocol']);
    +}
    +
    +function testOpenWithCustomParams() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {'messageUrlParams': {'foo-key': 'foo-value'}};
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  var extraParams = webChannel.channel_.extraParams_;
    +  assertNotNullNorUndefined(extraParams);
    +}
    +
    +function testOpenWithCorsEnabled() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  var options = {'supportsCrossDomainXhr': true};
    +  webChannel = webChannelTransport.createWebChannel(channelUrl, options);
    +  webChannel.open();
    +
    +  assertTrue(webChannel.channel_.supportsCrossDomainXhrs_);
    +}
    +
    +function testOpenThenCloseChannel() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  webChannel = webChannelTransport.createWebChannel(channelUrl);
    +
    +  var eventFired = false;
    +  goog.events.listen(webChannel, goog.net.WebChannel.EventType.CLOSE,
    +      function(e) {
    +        eventFired = true;
    +      });
    +
    +  webChannel.open();
    +  assertFalse(eventFired);
    +
    +  var channel = webChannel.channel_;
    +  assertNotNull(channel);
    +
    +  simulateCloseEvent(channel);
    +  assertTrue(eventFired);
    +}
    +
    +
    +function testChannelError() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  webChannel = webChannelTransport.createWebChannel(channelUrl);
    +
    +  var eventFired = false;
    +  goog.events.listen(webChannel, goog.net.WebChannel.EventType.ERROR,
    +      function(e) {
    +        eventFired = true;
    +        assertEquals(goog.net.WebChannel.ErrorStatus.NETWORK_ERROR, e.status);
    +      });
    +
    +  webChannel.open();
    +  assertFalse(eventFired);
    +
    +  var channel = webChannel.channel_;
    +  assertNotNull(channel);
    +
    +  simulateErrorEvent(channel);
    +  assertTrue(eventFired);
    +}
    +
    +
    +function testChannelMessage() {
    +  var webChannelTransport =
    +      new goog.labs.net.webChannel.WebChannelBaseTransport();
    +  webChannel = webChannelTransport.createWebChannel(channelUrl);
    +
    +  var eventFired = false;
    +  var data = 'foo';
    +  goog.events.listen(webChannel, goog.net.WebChannel.EventType.MESSAGE,
    +      function(e) {
    +        eventFired = true;
    +        assertEquals(e.data, data);
    +      });
    +
    +  webChannel.open();
    +  assertFalse(eventFired);
    +
    +  var channel = webChannel.channel_;
    +  assertNotNull(channel);
    +
    +  simulateMessageEvent(channel, data);
    +  assertTrue(eventFired);
    +}
    +
    +
    +/**
    + * Simulates the WebChannelBase firing the open event for the given channel.
    + * @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
    + */
    +function simulateOpenEvent(channel) {
    +  assertNotNull(channel.getHandler());
    +  channel.getHandler().channelOpened();
    +}
    +
    +
    +/**
    + * Simulates the WebChannelBase firing the close event for the given channel.
    + * @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
    + */
    +function simulateCloseEvent(channel) {
    +  assertNotNull(channel.getHandler());
    +  channel.getHandler().channelClosed();
    +}
    +
    +
    +/**
    + * Simulates the WebChannelBase firing the error event for the given channel.
    + * @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
    + */
    +function simulateErrorEvent(channel) {
    +  assertNotNull(channel.getHandler());
    +  channel.getHandler().channelError();
    +}
    +
    +
    +/**
    + * Simulates the WebChannelBase firing the message event for the given channel.
    + * @param {!goog.labs.net.webChannel.WebChannelBase} channel The WebChannelBase.
    + * @param {String} data The message data.
    + */
    +function simulateMessageEvent(channel, data) {
    +  assertNotNull(channel.getHandler());
    +  channel.getHandler().channelHandleArray(channel, data);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchanneldebug.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchanneldebug.js
    new file mode 100644
    index 0000000000..f9ef89dd49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/webchanneldebug.js
    @@ -0,0 +1,300 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a utility for tracing and debugging WebChannel
    + *     requests.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.WebChannelDebug');
    +
    +goog.require('goog.json');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Logs and keeps a buffer of debugging info for the Channel.
    + *
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.labs.net.webChannel.WebChannelDebug = function() {
    +  /**
    +   * The logger instance.
    +   * @const
    +   * @private
    +   */
    +  this.logger_ = goog.log.getLogger('goog.labs.net.webChannel.WebChannelDebug');
    +};
    +
    +
    +goog.scope(function() {
    +var WebChannelDebug = goog.labs.net.webChannel.WebChannelDebug;
    +
    +
    +/**
    + * Gets the logger used by this ChannelDebug.
    + * @return {goog.debug.Logger} The logger used by this WebChannelDebug.
    + */
    +WebChannelDebug.prototype.getLogger = function() {
    +  return this.logger_;
    +};
    +
    +
    +/**
    + * Logs that the browser went offline during the lifetime of a request.
    + * @param {goog.Uri} url The URL being requested.
    + */
    +WebChannelDebug.prototype.browserOfflineResponse = function(url) {
    +  this.info('BROWSER_OFFLINE: ' + url);
    +};
    +
    +
    +/**
    + * Logs an XmlHttp request..
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + * @param {?string} postData The data posted in the request.
    + */
    +WebChannelDebug.prototype.xmlHttpChannelRequest =
    +    function(verb, uri, id, attempt, postData) {
    +  this.info(
    +      'XMLHTTP REQ (' + id + ') [attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri + '\n' +
    +      this.maybeRedactPostData_(postData));
    +};
    +
    +
    +/**
    + * Logs the meta data received from an XmlHttp request.
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + * @param {goog.net.XmlHttp.ReadyState} readyState The ready state.
    + * @param {number} statusCode The HTTP status code.
    + */
    +WebChannelDebug.prototype.xmlHttpChannelResponseMetaData =
    +    function(verb, uri, id, attempt, readyState, statusCode)  {
    +  this.info(
    +      'XMLHTTP RESP (' + id + ') [ attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri + '\n' + readyState + ' ' + statusCode);
    +};
    +
    +
    +/**
    + * Logs the response data received from an XmlHttp request.
    + * @param {string|number|undefined} id The request id.
    + * @param {?string} responseText The response text.
    + * @param {?string=} opt_desc Optional request description.
    + */
    +WebChannelDebug.prototype.xmlHttpChannelResponseText =
    +    function(id, responseText, opt_desc) {
    +  this.info(
    +      'XMLHTTP TEXT (' + id + '): ' +
    +      this.redactResponse_(responseText) +
    +      (opt_desc ? ' ' + opt_desc : ''));
    +};
    +
    +
    +/**
    + * Logs a Trident ActiveX request.
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + */
    +WebChannelDebug.prototype.tridentChannelRequest =
    +    function(verb, uri, id, attempt) {
    +  this.info(
    +      'TRIDENT REQ (' + id + ') [ attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri);
    +};
    +
    +
    +/**
    + * Logs the response text received from a Trident ActiveX request.
    + * @param {string|number|undefined} id The request id.
    + * @param {string} responseText The response text.
    + */
    +WebChannelDebug.prototype.tridentChannelResponseText =
    +    function(id, responseText) {
    +  this.info(
    +      'TRIDENT TEXT (' + id + '): ' +
    +      this.redactResponse_(responseText));
    +};
    +
    +
    +/**
    + * Logs the done response received from a Trident ActiveX request.
    + * @param {string|number|undefined} id The request id.
    + * @param {boolean} successful Whether the request was successful.
    + */
    +WebChannelDebug.prototype.tridentChannelResponseDone =
    +    function(id, successful) {
    +  this.info(
    +      'TRIDENT TEXT (' + id + '): ' + successful ? 'success' : 'failure');
    +};
    +
    +
    +/**
    + * Logs a request timeout.
    + * @param {goog.Uri} uri The uri that timed out.
    + */
    +WebChannelDebug.prototype.timeoutResponse = function(uri) {
    +  this.info('TIMEOUT: ' + uri);
    +};
    +
    +
    +/**
    + * Logs a debug message.
    + * @param {string} text The message.
    + */
    +WebChannelDebug.prototype.debug = function(text) {
    +  this.info(text);
    +};
    +
    +
    +/**
    + * Logs an exception
    + * @param {Error} e The error or error event.
    + * @param {string=} opt_msg The optional message, defaults to 'Exception'.
    + */
    +WebChannelDebug.prototype.dumpException = function(e, opt_msg) {
    +  this.severe((opt_msg || 'Exception') + e);
    +};
    +
    +
    +/**
    + * Logs an info message.
    + * @param {string} text The message.
    + */
    +WebChannelDebug.prototype.info = function(text) {
    +  goog.log.info(this.logger_, text);
    +};
    +
    +
    +/**
    + * Logs a warning message.
    + * @param {string} text The message.
    + */
    +WebChannelDebug.prototype.warning = function(text) {
    +  goog.log.warning(this.logger_, text);
    +};
    +
    +
    +/**
    + * Logs a severe message.
    + * @param {string} text The message.
    + */
    +WebChannelDebug.prototype.severe = function(text) {
    +  goog.log.error(this.logger_, text);
    +};
    +
    +
    +/**
    + * Removes potentially private data from a response so that we don't
    + * accidentally save private and personal data to the server logs.
    + * @param {?string} responseText A JSON response to clean.
    + * @return {?string} The cleaned response.
    + * @private
    + */
    +WebChannelDebug.prototype.redactResponse_ = function(responseText) {
    +  if (!responseText) {
    +    return null;
    +  }
    +  /** @preserveTry */
    +  try {
    +    var responseArray = goog.json.unsafeParse(responseText);
    +    if (responseArray) {
    +      for (var i = 0; i < responseArray.length; i++) {
    +        if (goog.isArray(responseArray[i])) {
    +          this.maybeRedactArray_(responseArray[i]);
    +        }
    +      }
    +    }
    +
    +    return goog.json.serialize(responseArray);
    +  } catch (e) {
    +    this.debug('Exception parsing expected JS array - probably was not JS');
    +    return responseText;
    +  }
    +};
    +
    +
    +/**
    + * Removes data from a response array that may be sensitive.
    + * @param {!Array<?>} array The array to clean.
    + * @private
    + */
    +WebChannelDebug.prototype.maybeRedactArray_ = function(array) {
    +  if (array.length < 2) {
    +    return;
    +  }
    +  var dataPart = array[1];
    +  if (!goog.isArray(dataPart)) {
    +    return;
    +  }
    +  if (dataPart.length < 1) {
    +    return;
    +  }
    +
    +  var type = dataPart[0];
    +  if (type != 'noop' && type != 'stop') {
    +    // redact all fields in the array
    +    for (var i = 1; i < dataPart.length; i++) {
    +      dataPart[i] = '';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes potentially private data from a request POST body so that we don't
    + * accidentally save private and personal data to the server logs.
    + * @param {?string} data The data string to clean.
    + * @return {?string} The data string with sensitive data replaced by 'redacted'.
    + * @private
    + */
    +WebChannelDebug.prototype.maybeRedactPostData_ = function(data) {
    +  if (!data) {
    +    return null;
    +  }
    +  var out = '';
    +  var params = data.split('&');
    +  for (var i = 0; i < params.length; i++) {
    +    var param = params[i];
    +    var keyValue = param.split('=');
    +    if (keyValue.length > 1) {
    +      var key = keyValue[0];
    +      var value = keyValue[1];
    +
    +      var keyParts = key.split('_');
    +      if (keyParts.length >= 2 && keyParts[1] == 'type') {
    +        out += key + '=' + value + '&';
    +      } else {
    +        out += key + '=' + 'redacted' + '&';
    +      }
    +    }
    +  }
    +  return out;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wire.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wire.js
    new file mode 100644
    index 0000000000..78bee596a1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wire.js
    @@ -0,0 +1,75 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Interface and shared data structures for implementing
    + * different wire protocol versions.
    + * @visibility {//closure/goog/bin/sizetests:__pkg__}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.Wire');
    +
    +
    +
    +/**
    + * The interface class.
    + *
    + * @interface
    + */
    +goog.labs.net.webChannel.Wire = function() {};
    +
    +
    +goog.scope(function() {
    +var Wire = goog.labs.net.webChannel.Wire;
    +
    +
    +/**
    + * The latest protocol version that this class supports. We request this version
    + * from the server when opening the connection. Should match
    + * LATEST_CHANNEL_VERSION on the server code.
    + * @type {number}
    + */
    +Wire.LATEST_CHANNEL_VERSION = 8;
    +
    +
    +
    +/**
    + * Simple container class for a (mapId, map) pair.
    + * @param {number} mapId The id for this map.
    + * @param {!Object|!goog.structs.Map} map The map itself.
    + * @param {!Object=} opt_context The context associated with the map.
    + * @constructor
    + * @struct
    + */
    +Wire.QueuedMap = function(mapId, map, opt_context) {
    +  /**
    +   * The id for this map.
    +   * @type {number}
    +   */
    +  this.mapId = mapId;
    +
    +  /**
    +   * The map itself.
    +   * @type {!Object|!goog.structs.Map}
    +   */
    +  this.map = map;
    +
    +  /**
    +   * The context for the map.
    +   * @type {Object}
    +   */
    +  this.context = opt_context || null;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8.js
    new file mode 100644
    index 0000000000..58a5cd5ce1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8.js
    @@ -0,0 +1,136 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Codec functions of the v8 wire protocol. Eventually we'd want
    + * to support pluggable wire-format to improve wire efficiency and to enable
    + * binary encoding. Such support will require an interface class, which
    + * will be added later.
    + *
    + * @visibility {:internal}
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.WireV8');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.json');
    +goog.require('goog.json.NativeJsonProcessor');
    +goog.require('goog.structs');
    +
    +
    +
    +/**
    + * The v8 codec class.
    + *
    + * @constructor
    + * @struct
    + */
    +goog.labs.net.webChannel.WireV8 = function() {
    +  /**
    +   * Parser for a response payload. The parser should return an array.
    +   * @private {!goog.string.Parser}
    +   */
    +  this.parser_ = new goog.json.NativeJsonProcessor();
    +};
    +
    +
    +goog.scope(function() {
    +var WireV8 = goog.labs.net.webChannel.WireV8;
    +var Wire = goog.labs.net.webChannel.Wire;
    +
    +
    +/**
    + * Encodes a standalone message into the wire format.
    + *
    + * May throw exception if the message object contains any invalid elements.
    + *
    + * @param {!Object|!goog.structs.Map} message The message data.
    + *     V8 only support JS objects (or Map).
    + * @param {!Array<string>} buffer The text buffer to write the message to.
    + * @param {string=} opt_prefix The prefix for each field of the object.
    + */
    +WireV8.prototype.encodeMessage = function(message, buffer, opt_prefix) {
    +  var prefix = opt_prefix || '';
    +  try {
    +    goog.structs.forEach(message, function(value, key) {
    +      var encodedValue = value;
    +      if (goog.isObject(value)) {
    +        encodedValue = goog.json.serialize(value);
    +      }  // keep the fast-path for primitive types
    +      buffer.push(prefix + key + '=' + encodeURIComponent(encodedValue));
    +    });
    +  } catch (ex) {
    +    // We send a map here because lots of the retry logic relies on map IDs,
    +    // so we have to send something (possibly redundant).
    +    buffer.push(prefix + 'type' + '=' + encodeURIComponent('_badmap'));
    +    throw ex;
    +  }
    +};
    +
    +
    +/**
    + * Encodes all the buffered messages of the forward channel.
    + *
    + * @param {!Array<Wire.QueuedMap>} messageQueue The message data.
    + *     V8 only support JS objects.
    + * @param {number} count The number of messages to be encoded.
    + * @param {?function(!Object)} badMapHandler Callback for bad messages.
    + */
    +WireV8.prototype.encodeMessageQueue = function(messageQueue, count,
    +    badMapHandler) {
    +  var sb = ['count=' + count];
    +  var offset;
    +  if (count > 0) {
    +    // To save a bit of bandwidth, specify the base mapId and the rest as
    +    // offsets from it.
    +    offset = messageQueue[0].mapId;
    +    sb.push('ofs=' + offset);
    +  } else {
    +    offset = 0;
    +  }
    +  for (var i = 0; i < count; i++) {
    +    var mapId = messageQueue[i].mapId;
    +    var map = messageQueue[i].map;
    +    mapId -= offset;
    +    try {
    +      this.encodeMessage(map, sb, 'req' + mapId + '_');
    +    } catch (ex) {
    +      if (badMapHandler) {
    +        badMapHandler(map);
    +      }
    +    }
    +  }
    +  return sb.join('&');
    +};
    +
    +
    +/**
    + * Decodes a standalone message received from the wire. May throw exception
    + * if text is ill-formatted.
    + *
    + * Must be valid JSON as it is insecure to use eval() to decode JS literals;
    + * and eval() is disallowed in Chrome apps too.
    + *
    + * Invalid JS literals include null array elements, quotas etc.
    + *
    + * @param {string} messageText The string content as received from the wire.
    + * @return {*} The decoded message object.
    + */
    +WireV8.prototype.decodeMessage = function(messageText) {
    +  var response = this.parser_.parse(messageText);
    +  goog.asserts.assert(goog.isArray(response));  // throw exception
    +  return response;
    +};
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.html
    new file mode 100644
    index 0000000000..4635b43654
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.net.webChannel.WireV8</title>
    +<script src="../../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.labs.net.webChannel.WireV8Test');
    +</script>
    +<div id="debug" style="font-size: small"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.js
    new file mode 100644
    index 0000000000..678a48110f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchannel/wirev8_test.js
    @@ -0,0 +1,99 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.net.webChannel.WireV8.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.net.webChannel.WireV8Test');
    +
    +goog.require('goog.labs.net.webChannel.WireV8');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.net.webChannel.WireV8Test');
    +
    +
    +var wireCodec;
    +
    +
    +function setUp() {
    +  wireCodec = new goog.labs.net.webChannel.WireV8();
    +}
    +
    +function tearDown() {
    +}
    +
    +
    +function testEncodeSimpleMessage() {
    +  // scalar types only
    +  var message = {
    +    a: 'a',
    +    b: 'b'
    +  };
    +  var buff = [];
    +  wireCodec.encodeMessage(message, buff, 'prefix_');
    +  assertEquals(2, buff.length);
    +  assertEquals('prefix_a=a', buff[0]);
    +  assertEquals('prefix_b=b', buff[1]);
    +}
    +
    +
    +function testEncodeComplexMessage() {
    +  var message = {
    +    a: 'a',
    +    b: {
    +      x: 1,
    +      y: 2
    +    }
    +  };
    +  var buff = [];
    +  wireCodec.encodeMessage(message, buff, 'prefix_');
    +  assertEquals(2, buff.length);
    +  assertEquals('prefix_a=a', buff[0]);
    +  // a round-trip URI codec
    +  assertEquals('prefix_b={\"x\":1,\"y\":2}', decodeURIComponent(buff[1]));
    +}
    +
    +
    +function testEncodeMessageQueue() {
    +  var message1 = {
    +    a: 'a'
    +  };
    +  var queuedMessage1 = {
    +    map: message1,
    +    mapId: 3
    +  };
    +  var message2 = {
    +    b: 'b'
    +  };
    +  var queuedMessage2 = {
    +    map: message2,
    +    mapId: 4
    +  };
    +  var queue = [queuedMessage1, queuedMessage2];
    +  var result = wireCodec.encodeMessageQueue(queue, 2, null);
    +  assertEquals('count=2&ofs=3&req0_a=a&req1_b=b', result);
    +}
    +
    +
    +function testDecodeMessage() {
    +  var message = wireCodec.decodeMessage('[{"a":"a", "x":1}, {"b":"b"}]');
    +  assertTrue(goog.isArray(message));
    +  assertEquals(2, message.length);
    +  assertEquals('a', message[0].a);
    +  assertEquals(1, message[0].x);
    +  assertEquals('b', message[1].b);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransport.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransport.js
    new file mode 100644
    index 0000000000..5c955b57ff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransport.js
    @@ -0,0 +1,75 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Transport support for WebChannel.
    + *
    + * The <code>WebChannelTransport</code> implementation serves as the factory
    + * for <code>WebChannel</code>, which offers an abstraction for
    + * point-to-point socket-like communication similar to what BrowserChannel
    + * or HTML5 WebSocket offers.
    + *
    + */
    +
    +goog.provide('goog.net.WebChannelTransport');
    +
    +
    +
    +/**
    + * A WebChannelTransport instance represents a shared context of logical
    + * connectivity between a browser client and a remote origin.
    + *
    + * Over a single WebChannelTransport instance, multiple WebChannels may be
    + * created against different URLs, which may all share the same
    + * underlying connectivity (i.e. TCP connection) whenever possible.
    + *
    + * When multi-domains are supported, such as CORS, multiple origins may be
    + * supported over a single WebChannelTransport instance at the same time.
    + *
    + * Sharing between different window contexts such as tabs is not addressed
    + * by WebChannelTransport. Applications may choose HTML5 shared workers
    + * or other techniques to access the same transport instance
    + * across different window contexts.
    + *
    + * @interface
    + */
    +goog.net.WebChannelTransport = function() {};
    +
    +
    +/**
    + * The latest protocol version. The protocol version is requested
    + * from the server which is responsible for terminating the underlying
    + * wire protocols.
    + *
    + * @const
    + * @type {number}
    + * @private
    + */
    +goog.net.WebChannelTransport.LATEST_VERSION_ = 0;
    +
    +
    +/**
    + * Create a new WebChannel instance.
    + *
    + * The new WebChannel is to be opened against the server-side resource
    + * as specified by the given URL. See {@link goog.net.WebChannel} for detailed
    + * semantics.
    + *
    + * @param {string} url The URL path for the new WebChannel instance.
    + * @param {!goog.net.WebChannel.Options=} opt_options Configuration for the
    + *     new WebChannel instance. The configuration object is reusable after
    + *     the new channel instance is created.
    + * @return {!goog.net.WebChannel} the newly created WebChannel instance.
    + */
    +goog.net.WebChannelTransport.prototype.createWebChannel = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransportfactory.js b/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransportfactory.js
    new file mode 100644
    index 0000000000..6f2c5c0456
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/webchanneltransportfactory.js
    @@ -0,0 +1,35 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default factory for <code>WebChannelTransport</code> to
    + * avoid exposing concrete classes to clients.
    + *
    + */
    +
    +goog.provide('goog.net.createWebChannelTransport');
    +
    +goog.require('goog.functions');
    +goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
    +
    +
    +/**
    + * Create a new WebChannelTransport instance using the default implementation.
    + *
    + * @return {!goog.net.WebChannelTransport} the newly created transport instance.
    + */
    +goog.net.createWebChannelTransport =
    +    /** @type {function(): !goog.net.WebChannelTransport} */ (
    +    goog.partial(goog.functions.create,
    +                 goog.labs.net.webChannel.WebChannelBaseTransport));
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/xhr.js b/mapbox-gl/closure-library/closure/goog/labs/net/xhr.js
    new file mode 100644
    index 0000000000..0b83b7d351
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/xhr.js
    @@ -0,0 +1,468 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Offered as an alternative to XhrIo as a way for making requests
    + * via XMLHttpRequest.  Instead of mirroring the XHR interface and exposing
    + * events, results are used as a way to pass a "promise" of the response to
    + * interested parties.
    + *
    + */
    +
    +goog.provide('goog.labs.net.xhr');
    +goog.provide('goog.labs.net.xhr.Error');
    +goog.provide('goog.labs.net.xhr.HttpError');
    +goog.provide('goog.labs.net.xhr.Options');
    +goog.provide('goog.labs.net.xhr.PostData');
    +goog.provide('goog.labs.net.xhr.ResponseType');
    +goog.provide('goog.labs.net.xhr.TimeoutError');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.debug.Error');
    +goog.require('goog.json');
    +goog.require('goog.net.HttpStatus');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('goog.userAgent');
    +
    +
    +
    +goog.scope(function() {
    +var xhr = goog.labs.net.xhr;
    +var HttpStatus = goog.net.HttpStatus;
    +
    +
    +/**
    + * Configuration options for an XMLHttpRequest.
    + * - headers: map of header key/value pairs.
    + * - timeoutMs: number of milliseconds after which the request will be timed
    + *      out by the client. Default is to allow the browser to handle timeouts.
    + * - withCredentials: whether user credentials are to be included in a
    + *      cross-origin request. See:
    + *      http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute
    + * - mimeType: allows the caller to override the content-type and charset for
    + *      the request. See:
    + *      http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-overridemimetype
    + * - responseType: may be set to change the response type to an arraybuffer or
    + *      blob for downloading binary data. See:
    + *      http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-responsetype]
    + * - xmlHttpFactory: allows the caller to override the factory used to create
    + *      XMLHttpRequest objects.
    + * - xssiPrefix: Prefix used for protecting against XSSI attacks, which should
    + *      be removed before parsing the response as JSON.
    + *
    + * @typedef {{
    + *   headers: (Object<string>|undefined),
    + *   mimeType: (string|undefined),
    + *   responseType: (xhr.ResponseType|undefined),
    + *   timeoutMs: (number|undefined),
    + *   withCredentials: (boolean|undefined),
    + *   xmlHttpFactory: (goog.net.XmlHttpFactory|undefined),
    + *   xssiPrefix: (string|undefined)
    + * }}
    + */
    +xhr.Options;
    +
    +
    +/**
    + * Defines the types that are allowed as post data.
    + * @typedef {(ArrayBuffer|Blob|Document|FormData|null|string|undefined)}
    + */
    +xhr.PostData;
    +
    +
    +/**
    + * The Content-Type HTTP header name.
    + * @type {string}
    + */
    +xhr.CONTENT_TYPE_HEADER = 'Content-Type';
    +
    +
    +/**
    + * The Content-Type HTTP header value for a url-encoded form.
    + * @type {string}
    + */
    +xhr.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8';
    +
    +
    +/**
    + * Supported data types for the responseType field.
    + * See: http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-response
    + * @enum {string}
    + */
    +xhr.ResponseType = {
    +  ARRAYBUFFER: 'arraybuffer',
    +  BLOB: 'blob',
    +  DOCUMENT: 'document',
    +  JSON: 'json',
    +  TEXT: 'text'
    +};
    +
    +
    +/**
    + * Sends a get request, returning a promise that will be resolved
    + * with the response text once the request completes.
    + *
    + * @param {string} url The URL to request.
    + * @param {xhr.Options=} opt_options Configuration options for the request.
    + * @return {!goog.Promise<string>} A promise that will be resolved with the
    + *     response text once the request completes.
    + */
    +xhr.get = function(url, opt_options) {
    +  return xhr.send('GET', url, null, opt_options).then(function(request) {
    +    return request.responseText;
    +  });
    +};
    +
    +
    +/**
    + * Sends a post request, returning a promise that will be resolved
    + * with the response text once the request completes.
    + *
    + * @param {string} url The URL to request.
    + * @param {xhr.PostData} data The body of the post request.
    + * @param {xhr.Options=} opt_options Configuration options for the request.
    + * @return {!goog.Promise<string>} A promise that will be resolved with the
    + *     response text once the request completes.
    + */
    +xhr.post = function(url, data, opt_options) {
    +  return xhr.send('POST', url, data, opt_options).then(function(request) {
    +    return request.responseText;
    +  });
    +};
    +
    +
    +/**
    + * Sends a get request, returning a promise that will be resolved with
    + * the parsed response text once the request completes.
    + *
    + * @param {string} url The URL to request.
    + * @param {xhr.Options=} opt_options Configuration options for the request.
    + * @return {!goog.Promise<Object>} A promise that will be resolved with the
    + *     response JSON once the request completes.
    + */
    +xhr.getJson = function(url, opt_options) {
    +  return xhr.send('GET', url, null, opt_options).then(function(request) {
    +    return xhr.parseJson_(request.responseText, opt_options);
    +  });
    +};
    +
    +
    +/**
    + * Sends a get request, returning a promise that will be resolved with the
    + * response as an array of bytes.
    + *
    + * Supported in all XMLHttpRequest level 2 browsers, as well as IE9. IE8 and
    + * earlier are not supported.
    + *
    + * @param {string} url The URL to request.
    + * @param {xhr.Options=} opt_options Configuration options for the request. The
    + *     responseType will be overwritten to 'arraybuffer' if it was set.
    + * @return {!goog.Promise<!Uint8Array|!Array<number>>} A promise that will be
    + *     resolved with an array of bytes once the request completes.
    + */
    +xhr.getBytes = function(url, opt_options) {
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    throw new Error('getBytes is not supported in this browser.');
    +  }
    +
    +  var options = opt_options || {};
    +  options.responseType = xhr.ResponseType.ARRAYBUFFER;
    +
    +  return xhr.send('GET', url, null, options).then(function(request) {
    +    // Use the ArrayBuffer response in browsers that support XMLHttpRequest2.
    +    // This covers nearly all modern browsers: http://caniuse.com/xhr2
    +    if (request.response) {
    +      return new Uint8Array(/** @type {!ArrayBuffer} */ (request.response));
    +    }
    +
    +    // Fallback for IE9: the response may be accessed as an array of bytes with
    +    // the non-standard responseBody property, which can only be accessed as a
    +    // VBArray. IE7 and IE8 require significant amounts of VBScript to extract
    +    // the bytes.
    +    // See: http://stackoverflow.com/questions/1919972/
    +    if (goog.global['VBArray']) {
    +      return new goog.global['VBArray'](request['responseBody']).toArray();
    +    }
    +
    +    // Nearly all common browsers are covered by the cases above. If downloading
    +    // binary files in older browsers is necessary, the MDN article "Sending and
    +    // Receiving Binary Data" provides techniques that may work with
    +    // XMLHttpRequest level 1 browsers: http://goo.gl/7lEuGN
    +    throw new xhr.Error(
    +        'getBytes is not supported in this browser.', url, request);
    +  });
    +};
    +
    +
    +/**
    + * Sends a post request, returning a promise that will be resolved with
    + * the parsed response text once the request completes.
    + *
    + * @param {string} url The URL to request.
    + * @param {xhr.PostData} data The body of the post request.
    + * @param {xhr.Options=} opt_options Configuration options for the request.
    + * @return {!goog.Promise<Object>} A promise that will be resolved with the
    + *     response JSON once the request completes.
    + */
    +xhr.postJson = function(url, data, opt_options) {
    +  return xhr.send('POST', url, data, opt_options).then(function(request) {
    +    return xhr.parseJson_(request.responseText, opt_options);
    +  });
    +};
    +
    +
    +/**
    + * Sends a request, returning a promise that will be resolved
    + * with the XHR object once the request completes.
    + *
    + * If content type hasn't been set in opt_options headers, and hasn't been
    + * explicitly set to null, default to form-urlencoded/UTF8 for POSTs.
    + *
    + * @param {string} method The HTTP method for the request.
    + * @param {string} url The URL to request.
    + * @param {xhr.PostData} data The body of the post request.
    + * @param {xhr.Options=} opt_options Configuration options for the request.
    + * @return {!goog.Promise<!goog.net.XhrLike.OrNative>} A promise that will be
    + *     resolved with the XHR object once the request completes.
    + */
    +xhr.send = function(method, url, data, opt_options) {
    +  return new goog.Promise(function(resolve, reject) {
    +    var options = opt_options || {};
    +    var timer;
    +
    +    var request = options.xmlHttpFactory ?
    +        options.xmlHttpFactory.createInstance() : goog.net.XmlHttp();
    +    try {
    +      request.open(method, url, true);
    +    } catch (e) {
    +      // XMLHttpRequest.open may throw when 'open' is called, for example, IE7
    +      // throws "Access Denied" for cross-origin requests.
    +      reject(new xhr.Error('Error opening XHR: ' + e.message, url, request));
    +    }
    +
    +    // So sad that IE doesn't support onload and onerror.
    +    request.onreadystatechange = function() {
    +      if (request.readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +        goog.global.clearTimeout(timer);
    +        // Note: When developing locally, XHRs to file:// schemes return
    +        // a status code of 0. We mark that case as a success too.
    +        if (HttpStatus.isSuccess(request.status) ||
    +            request.status === 0 && !xhr.isEffectiveSchemeHttp_(url)) {
    +          resolve(request);
    +        } else {
    +          reject(new xhr.HttpError(request.status, url, request));
    +        }
    +      }
    +    };
    +    request.onerror = function() {
    +      reject(new xhr.Error('Network error', url, request));
    +    };
    +
    +    // Set the headers.
    +    var contentType;
    +    if (options.headers) {
    +      for (var key in options.headers) {
    +        var value = options.headers[key];
    +        if (goog.isDefAndNotNull(value)) {
    +          request.setRequestHeader(key, value);
    +        }
    +      }
    +      contentType = options.headers[xhr.CONTENT_TYPE_HEADER];
    +    }
    +
    +    // Browsers will automatically set the content type to multipart/form-data
    +    // when passed a FormData object.
    +    var dataIsFormData = (goog.global['FormData'] &&
    +        (data instanceof goog.global['FormData']));
    +    // If a content type hasn't been set, it hasn't been explicitly set to null,
    +    // and the data isn't a FormData, default to form-urlencoded/UTF8 for POSTs.
    +    // This is because some proxies have been known to reject posts without a
    +    // content-type.
    +    if (method == 'POST' && contentType === undefined && !dataIsFormData) {
    +      request.setRequestHeader(xhr.CONTENT_TYPE_HEADER, xhr.FORM_CONTENT_TYPE);
    +    }
    +
    +    // Set whether to include cookies with cross-domain requests. See:
    +    // http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute
    +    if (options.withCredentials) {
    +      request.withCredentials = options.withCredentials;
    +    }
    +
    +    // Allows setting an alternative response type, such as an ArrayBuffer. See:
    +    // http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-responsetype
    +    if (options.responseType) {
    +      request.responseType = options.responseType;
    +    }
    +
    +    // Allow the request to override the MIME type of the response. See:
    +    // http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-overridemimetype
    +    if (options.mimeType) {
    +      request.overrideMimeType(options.mimeType);
    +    }
    +
    +    // Handle timeouts, if requested.
    +    if (options.timeoutMs > 0) {
    +      timer = goog.global.setTimeout(function() {
    +        // Clear event listener before aborting so the errback will not be
    +        // called twice.
    +        request.onreadystatechange = goog.nullFunction;
    +        request.abort();
    +        reject(new xhr.TimeoutError(url, request));
    +      }, options.timeoutMs);
    +    }
    +
    +    // Trigger the send.
    +    try {
    +      request.send(data);
    +    } catch (e) {
    +      // XMLHttpRequest.send is known to throw on some versions of FF,
    +      // for example if a cross-origin request is disallowed.
    +      request.onreadystatechange = goog.nullFunction;
    +      goog.global.clearTimeout(timer);
    +      reject(new xhr.Error('Error sending XHR: ' + e.message, url, request));
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @param {string} url The URL to test.
    + * @return {boolean} Whether the effective scheme is HTTP or HTTPs.
    + * @private
    + */
    +xhr.isEffectiveSchemeHttp_ = function(url) {
    +  var scheme = goog.uri.utils.getEffectiveScheme(url);
    +  // NOTE(user): Empty-string is for the case under FF3.5 when the location
    +  // is not defined inside a web worker.
    +  return scheme == 'http' || scheme == 'https' || scheme == '';
    +};
    +
    +
    +/**
    + * JSON-parses the given response text, returning an Object.
    + *
    + * @param {string} responseText Response text.
    + * @param {xhr.Options|undefined} options The options object.
    + * @return {Object} The JSON-parsed value of the original responseText.
    + * @private
    + */
    +xhr.parseJson_ = function(responseText, options) {
    +  var prefixStrippedResult = responseText;
    +  if (options && options.xssiPrefix) {
    +    prefixStrippedResult = xhr.stripXssiPrefix_(
    +        options.xssiPrefix, prefixStrippedResult);
    +  }
    +  return goog.json.parse(prefixStrippedResult);
    +};
    +
    +
    +/**
    + * Strips the XSSI prefix from the input string.
    + *
    + * @param {string} prefix The XSSI prefix.
    + * @param {string} string The string to strip the prefix from.
    + * @return {string} The input string without the prefix.
    + * @private
    + */
    +xhr.stripXssiPrefix_ = function(prefix, string) {
    +  if (goog.string.startsWith(string, prefix)) {
    +    string = string.substring(prefix.length);
    +  }
    +  return string;
    +};
    +
    +
    +
    +/**
    + * Generic error that may occur during a request.
    + *
    + * @param {string} message The error message.
    + * @param {string} url The URL that was being requested.
    + * @param {!goog.net.XhrLike.OrNative} request The XHR that failed.
    + * @extends {goog.debug.Error}
    + * @constructor
    + */
    +xhr.Error = function(message, url, request) {
    +  xhr.Error.base(this, 'constructor', message + ', url=' + url);
    +
    +  /**
    +   * The URL that was requested.
    +   * @type {string}
    +   */
    +  this.url = url;
    +
    +  /**
    +   * The XMLHttpRequest corresponding with the failed request.
    +   * @type {!goog.net.XhrLike.OrNative}
    +   */
    +  this.xhr = request;
    +};
    +goog.inherits(xhr.Error, goog.debug.Error);
    +
    +
    +/** @override */
    +xhr.Error.prototype.name = 'XhrError';
    +
    +
    +
    +/**
    + * Class for HTTP errors.
    + *
    + * @param {number} status The HTTP status code of the response.
    + * @param {string} url The URL that was being requested.
    + * @param {!goog.net.XhrLike.OrNative} request The XHR that failed.
    + * @extends {xhr.Error}
    + * @constructor
    + * @final
    + */
    +xhr.HttpError = function(status, url, request) {
    +  xhr.HttpError.base(
    +      this, 'constructor', 'Request Failed, status=' + status, url, request);
    +
    +  /**
    +   * The HTTP status code for the error.
    +   * @type {number}
    +   */
    +  this.status = status;
    +};
    +goog.inherits(xhr.HttpError, xhr.Error);
    +
    +
    +/** @override */
    +xhr.HttpError.prototype.name = 'XhrHttpError';
    +
    +
    +
    +/**
    + * Class for Timeout errors.
    + *
    + * @param {string} url The URL that timed out.
    + * @param {!goog.net.XhrLike.OrNative} request The XHR that failed.
    + * @extends {xhr.Error}
    + * @constructor
    + * @final
    + */
    +xhr.TimeoutError = function(url, request) {
    +  xhr.TimeoutError.base(this, 'constructor', 'Request timed out', url, request);
    +};
    +goog.inherits(xhr.TimeoutError, xhr.Error);
    +
    +
    +/** @override */
    +xhr.TimeoutError.prototype.name = 'XhrTimeoutError';
    +
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.html b/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.html
    new file mode 100644
    index 0000000000..02d348a23a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.net.xhr
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.labs.net.xhrTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.js b/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.js
    new file mode 100644
    index 0000000000..b8d43002e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/net/xhr_test.js
    @@ -0,0 +1,462 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.net.xhrTest');
    +goog.setTestOnly('goog.labs.net.xhrTest');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.labs.net.xhr');
    +goog.require('goog.net.WrapperXmlHttpFactory');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function stubXhrToReturn(status, opt_responseText, opt_latency) {
    +
    +  if (goog.isDefAndNotNull(opt_latency)) {
    +    mockClock = new goog.testing.MockClock(true);
    +  }
    +
    +  var stubXhr = {
    +    sent: false,
    +    aborted: false,
    +    status: 0,
    +    headers: {},
    +    open: function(method, url, async) {
    +      this.method = method;
    +      this.url = url;
    +      this.async = async;
    +    },
    +    setRequestHeader: function(key, value) {
    +      this.headers[key] = value;
    +    },
    +    overrideMimeType: function(mimeType) {
    +      this.mimeType = mimeType;
    +    },
    +    abort: function() {
    +      this.aborted = true;
    +      this.load(0);
    +    },
    +    send: function(data) {
    +      if (mockClock) {
    +        mockClock.tick(opt_latency);
    +      }
    +      this.data = data;
    +      this.sent = true;
    +      this.load(status);
    +    },
    +    load: function(status) {
    +      this.status = status;
    +      if (goog.isDefAndNotNull(opt_responseText)) {
    +        this.responseText = opt_responseText;
    +      }
    +      this.readyState = 4;
    +      if (this.onreadystatechange) this.onreadystatechange();
    +    }
    +  };
    +
    +  stubXmlHttpWith(stubXhr);
    +}
    +
    +function stubXhrToThrow(err) {
    +  stubXmlHttpWith(buildThrowingStubXhr(err));
    +}
    +
    +function buildThrowingStubXhr(err) {
    +  return {
    +    sent: false,
    +    aborted: false,
    +    status: 0,
    +    headers: {},
    +    open: function(method, url, async) {
    +      this.method = method;
    +      this.url = url;
    +      this.async = async;
    +    },
    +    setRequestHeader: function(key, value) {
    +      this.headers[key] = value;
    +    },
    +    overrideMimeType: function(mimeType) {
    +      this.mimeType = mimeType;
    +    },
    +    send: function(data) {
    +      throw err;
    +    }
    +  };
    +}
    +
    +function stubXmlHttpWith(stubXhr) {
    +  goog.net.XmlHttp = function() {
    +    return stubXhr;
    +  };
    +  for (var x in originalXmlHttp) {
    +    goog.net.XmlHttp[x] = originalXmlHttp[x];
    +  }
    +}
    +
    +var xhr = goog.labs.net.xhr;
    +var originalXmlHttp = goog.net.XmlHttp;
    +var mockClock;
    +
    +function tearDown() {
    +  if (mockClock) {
    +    mockClock.dispose();
    +    mockClock = null;
    +  }
    +  goog.net.XmlHttp = originalXmlHttp;
    +}
    +
    +
    +/**
    + * Tests whether the test was loaded from a file: protocol. Tests that use a
    + * real network request cannot be run from the local file system due to
    + * cross-origin restrictions, but will run if the tests are hosted on a server.
    + * A log message is added to the test case to warn users that the a test was
    + * skipped.
    + *
    + * @return {boolean} Whether the test is running on a local file system.
    + */
    +function isRunningLocally() {
    +  if (window.location.protocol == 'file:') {
    +    var testCase = goog.global['G_testRunner'].testCase;
    +    testCase.saveMessage('Test skipped while running on local file system.');
    +    return true;
    +  }
    +  return false;
    +}
    +
    +function testSimpleRequest() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.send('GET', 'testdata/xhr_test_text.data').then(function(xhr) {
    +    assertEquals('Just some data.', xhr.responseText);
    +    assertEquals(200, xhr.status);
    +  });
    +}
    +
    +function testGetText() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.get('testdata/xhr_test_text.data').then(function(responseText) {
    +    assertEquals('Just some data.', responseText);
    +  });
    +}
    +
    +function testGetTextWithJson() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.get('testdata/xhr_test_json.data').then(function(responseText) {
    +    assertEquals('while(1);\n{"stat":"ok","count":12345}\n', responseText);
    +  });
    +}
    +
    +function testPostText() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.post('testdata/xhr_test_text.data', 'post-data').then(
    +      function(responseText) {
    +        // No good way to test post-data gets transported.
    +        assertEquals('Just some data.', responseText);
    +      });
    +}
    +
    +function testGetJson() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.getJson(
    +      'testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'}).then(
    +      function(responseObj) {
    +        assertEquals('ok', responseObj['stat']);
    +        assertEquals(12345, responseObj['count']);
    +      });
    +}
    +
    +function testGetBytes() {
    +  if (isRunningLocally()) return;
    +
    +  // IE8 requires a VBScript fallback to read the bytes from the response.
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentMode(9)) {
    +    return;
    +  }
    +
    +  return xhr.getBytes('testdata/cleardot.gif').then(function(bytes) {
    +    assertElementsEquals([
    +      0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0xFF,
    +      0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00,
    +      0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
    +      0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3B
    +    ], bytes);
    +  });
    +}
    +
    +function testSerialRequests() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.get('testdata/xhr_test_text.data').
    +      then(function(response) {
    +        return xhr.getJson(
    +            'testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'});
    +      }).then(function(responseObj) {
    +        // Data that comes through to callbacks should be from the 2nd request.
    +        assertEquals('ok', responseObj['stat']);
    +        assertEquals(12345, responseObj['count']);
    +      });
    +}
    +
    +function testBadUrlDetectedAsError() {
    +  if (isRunningLocally()) return;
    +
    +  return xhr.getJson('unknown-file.dat').then(
    +      fail /* opt_onFulfilled */,
    +      function(err) {
    +        assertTrue(
    +            'Error should be an HTTP error', err instanceof xhr.HttpError);
    +        assertEquals(404, err.status);
    +        assertNotNull(err.xhr);
    +      });
    +}
    +
    +function testBadOriginTriggersOnErrorHandler() {
    +  return xhr.get('http://www.google.com').then(
    +      fail /* opt_onFulfilled */,
    +      function(err) {
    +        // In IE this will be a goog.labs.net.xhr.Error since it is thrown
    +        //  when calling xhr.open(), other browsers will raise an HttpError.
    +        assertTrue('Error should be an xhr error', err instanceof xhr.Error);
    +        assertNotNull(err.xhr);
    +      });
    +}
    +
    +//============================================================================
    +// The following tests use a stubbed out XMLHttpRequest.
    +//============================================================================
    +
    +function testAbortRequest() {
    +  stubXhrToReturn(200);
    +  var promise = xhr.send('GET', 'test-url', null).thenCatch(
    +      function(error) {
    +        assertTrue(error instanceof goog.Promise.CancellationError);
    +      });
    +  promise.cancel();
    +  return promise;
    +}
    +
    +function testSendNoOptions() {
    +  var called = false;
    +  stubXhrToReturn(200);
    +  assertFalse('Callback should not yet have been called', called);
    +  return xhr.send('GET', 'test-url', null).then(function(stubXhr) {
    +    called = true;
    +    assertEquals('GET', stubXhr.method);
    +    assertEquals('test-url', stubXhr.url);
    +  });
    +}
    +
    +function testSendPostSetsDefaultHeader() {
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', null).then(function(stubXhr) {
    +    assertEquals('POST', stubXhr.method);
    +    assertEquals('test-url', stubXhr.url);
    +    assertEquals('application/x-www-form-urlencoded;charset=utf-8',
    +        stubXhr.headers['Content-Type']);
    +  });
    +}
    +
    +function testSendPostDoesntSetHeaderWithFormData() {
    +  if (!goog.global['FormData']) { return; }
    +  var formData = new goog.global['FormData']();
    +  formData.append('name', 'value');
    +
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', formData).then(function(stubXhr) {
    +    assertEquals('POST', stubXhr.method);
    +    assertEquals('test-url', stubXhr.url);
    +    assertEquals(undefined, stubXhr.headers['Content-Type']);
    +  });
    +}
    +
    +function testSendPostHeaders() {
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', null,
    +      { headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'} }).
    +      then(function(stubXhr) {
    +        assertEquals('POST', stubXhr.method);
    +        assertEquals('test-url', stubXhr.url);
    +        assertEquals('text/plain', stubXhr.headers['Content-Type']);
    +        assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
    +      });
    +}
    +
    +function testSendPostHeadersWithFormData() {
    +  if (!goog.global['FormData']) { return; }
    +  var formData = new goog.global['FormData']();
    +  formData.append('name', 'value');
    +
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', formData,
    +      { headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'} }).
    +      then(function(stubXhr) {
    +        assertEquals('POST', stubXhr.method);
    +        assertEquals('test-url', stubXhr.url);
    +        assertEquals('text/plain', stubXhr.headers['Content-Type']);
    +        assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
    +      });
    +}
    +
    +function testSendNullPostHeaders() {
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', null, {
    +    headers: {
    +      'Content-Type': null,
    +      'X-Made-Up': 'FooBar',
    +      'Y-Made-Up': null
    +    }
    +  }).then(function(stubXhr) {
    +    assertEquals('POST', stubXhr.method);
    +    assertEquals('test-url', stubXhr.url);
    +    assertEquals(undefined, stubXhr.headers['Content-Type']);
    +    assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
    +    assertEquals(undefined, stubXhr.headers['Y-Made-Up']);
    +  });
    +}
    +
    +function testSendNullPostHeadersWithFormData() {
    +  if (!goog.global['FormData']) { return; }
    +  var formData = new goog.global['FormData']();
    +  formData.append('name', 'value');
    +
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', formData, {
    +    headers: {
    +      'Content-Type': null,
    +      'X-Made-Up': 'FooBar',
    +      'Y-Made-Up': null
    +    }
    +  }).then(function(stubXhr) {
    +    assertEquals('POST', stubXhr.method);
    +    assertEquals('test-url', stubXhr.url);
    +    assertEquals(undefined, stubXhr.headers['Content-Type']);
    +    assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
    +    assertEquals(undefined, stubXhr.headers['Y-Made-Up']);
    +  });
    +}
    +
    +function testSendWithCredentials() {
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', null, {withCredentials: true}).
    +      then(function(stubXhr) {
    +        assertTrue('XHR should have been sent', stubXhr.sent);
    +        assertTrue(stubXhr.withCredentials);
    +      });
    +}
    +
    +function testSendWithMimeType() {
    +  stubXhrToReturn(200);
    +  return xhr.send('POST', 'test-url', null, {mimeType: 'text/plain'}).
    +      then(function(stubXhr) {
    +        assertTrue('XHR should have been sent', stubXhr.sent);
    +        assertEquals('text/plain', stubXhr.mimeType);
    +      });
    +}
    +
    +function testSendWithHttpError() {
    +  stubXhrToReturn(500);
    +  return xhr.send('POST', 'test-url', null).then(
    +      fail /* opt_onResolved */,
    +      function(err) {
    +        assertTrue(err instanceof xhr.HttpError);
    +        assertTrue(err.xhr.sent);
    +        assertEquals(500, err.status);
    +      });
    +}
    +
    +function testSendWithTimeoutNotHit() {
    +  stubXhrToReturn(200, null /* opt_responseText */, 1400 /* opt_latency */);
    +  return xhr.send('POST', 'test-url', null, {timeoutMs: 1500}).
    +      then(function(stubXhr) {
    +        assertTrue(mockClock.getTimeoutsMade() > 0);
    +        assertTrue('XHR should have been sent', stubXhr.sent);
    +        assertFalse('XHR should not have been aborted', stubXhr.aborted);
    +      });
    +}
    +
    +function testSendWithTimeoutHit() {
    +  stubXhrToReturn(200, null /* opt_responseText */, 50 /* opt_latency */);
    +  return xhr.send('POST', 'test-url', null, {timeoutMs: 50}).then(
    +      fail /* opt_onResolved */,
    +      function(err) {
    +        assertTrue('XHR should have been sent', err.xhr.sent);
    +        assertTrue('XHR should have been aborted', err.xhr.aborted);
    +        assertTrue(err instanceof xhr.TimeoutError);
    +      });
    +}
    +
    +function testCancelRequest() {
    +  stubXhrToReturn(200, null /* opt_responseText */, 25);
    +  var promise = xhr.send('GET', 'test-url', null, {timeoutMs: 50});
    +  promise.then(
    +      fail /* opt_onResolved */,
    +      function(error) {
    +        assertTrue('XHR should have been sent', error.xhr.sent);
    +        if (error instanceof goog.Promise.CancellationError) {
    +          error.xhr.abort();
    +        }
    +        assertTrue('XHR should have been aborted', error.xhr.aborted);
    +        assertTrue(error instanceof goog.Promise.CancellationError);
    +      });
    +  promise.cancel();
    +  return promise;
    +}
    +
    +function testGetJson() {
    +  var stubXhr = stubXhrToReturn(200, '{"a": 1, "b": 2}');
    +  xhr.getJson('test-url').then(function(responseObj) {
    +    assertObjectEquals({a: 1, b: 2}, responseObj);
    +  });
    +}
    +
    +function testGetJsonWithXssiPrefix() {
    +  stubXhrToReturn(200, 'while(1);\n{"a": 1, "b": 2}');
    +  return xhr.getJson('test-url', {xssiPrefix: 'while(1);\n'}).then(
    +      function(responseObj) {
    +        assertObjectEquals({a: 1, b: 2}, responseObj);
    +      });
    +}
    +
    +function testSendWithClientException() {
    +  stubXhrToThrow(new Error('CORS XHR with file:// schemas not allowed.'));
    +  return xhr.send('POST', 'file://test-url', null).then(
    +      fail /* opt_onResolved */,
    +      function(err) {
    +        assertFalse('XHR should not have been sent', err.xhr.sent);
    +        assertTrue(err instanceof Error);
    +        assertTrue(
    +            /CORS XHR with file:\/\/ schemas not allowed./.test(err.message));
    +      });
    +}
    +
    +function testSendWithFactory() {
    +  stubXhrToReturn(200);
    +  var options = {
    +    xmlHttpFactory: new goog.net.WrapperXmlHttpFactory(
    +        goog.partial(buildThrowingStubXhr, new Error('Bad factory')),
    +        goog.net.XmlHttp.getOptions)
    +  };
    +  return xhr.send('POST', 'file://test-url', null, options).then(
    +      fail /* opt_onResolved */,
    +      function(err) {
    +        assertTrue(err instanceof Error);
    +      });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/object/object.js b/mapbox-gl/closure-library/closure/goog/labs/object/object.js
    new file mode 100644
    index 0000000000..ff961c7c6a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/object/object.js
    @@ -0,0 +1,47 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A labs location for functions destined for Closure's
    + * {@code goog.object} namespace.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.labs.object');
    +
    +
    +/**
    + * Whether two values are not observably distinguishable. This
    + * correctly detects that 0 is not the same as -0 and two NaNs are
    + * practically equivalent.
    + *
    + * The implementation is as suggested by harmony:egal proposal.
    + *
    + * @param {*} v The first value to compare.
    + * @param {*} v2 The second value to compare.
    + * @return {boolean} Whether two values are not observably distinguishable.
    + * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
    + */
    +goog.labs.object.is = function(v, v2) {
    +  if (v === v2) {
    +    // 0 === -0, but they are not identical.
    +    // We need the cast because the compiler requires that v2 is a
    +    // number (although 1/v2 works with non-number). We cast to ? to
    +    // stop the compiler from type-checking this statement.
    +    return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);
    +  }
    +
    +  // NaN is non-reflexive: NaN !== NaN, although they are identical.
    +  return v !== v && v2 !== v2;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/object/object_test.html b/mapbox-gl/closure-library/closure/goog/labs/object/object_test.html
    new file mode 100644
    index 0000000000..0a86b6036e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/object/object_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.object
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.labs.objectTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/object/object_test.js b/mapbox-gl/closure-library/closure/goog/labs/object/object_test.js
    new file mode 100644
    index 0000000000..f4db2f521f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/object/object_test.js
    @@ -0,0 +1,52 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.objectTest');
    +goog.setTestOnly('goog.labs.objectTest');
    +
    +goog.require('goog.labs.object');
    +goog.require('goog.testing.jsunit');
    +
    +function testIs() {
    +  var object = {};
    +  assertTrue(goog.labs.object.is(object, object));
    +  assertFalse(goog.labs.object.is(object, {}));
    +
    +  assertTrue(goog.labs.object.is(NaN, NaN));
    +  assertTrue(goog.labs.object.is(0, 0));
    +  assertTrue(goog.labs.object.is(1, 1));
    +  assertTrue(goog.labs.object.is(-1, -1));
    +  assertTrue(goog.labs.object.is(123, 123));
    +  assertFalse(goog.labs.object.is(0, -0));
    +  assertFalse(goog.labs.object.is(-0, 0));
    +  assertFalse(goog.labs.object.is(0, 1));
    +
    +  assertTrue(goog.labs.object.is(true, true));
    +  assertTrue(goog.labs.object.is(false, false));
    +  assertFalse(goog.labs.object.is(true, false));
    +  assertFalse(goog.labs.object.is(false, true));
    +
    +  assertTrue(goog.labs.object.is('', ''));
    +  assertTrue(goog.labs.object.is('a', 'a'));
    +  assertFalse(goog.labs.object.is('', 'a'));
    +  assertFalse(goog.labs.object.is('a', ''));
    +  assertFalse(goog.labs.object.is('a', 'b'));
    +
    +  assertFalse(goog.labs.object.is(true, 'true'));
    +  assertFalse(goog.labs.object.is('true', true));
    +  assertFalse(goog.labs.object.is(false, 'false'));
    +  assertFalse(goog.labs.object.is('false', false));
    +  assertFalse(goog.labs.object.is(0, '0'));
    +  assertFalse(goog.labs.object.is('0', 0));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub.js b/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub.js
    new file mode 100644
    index 0000000000..08de9d0366
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub.js
    @@ -0,0 +1,564 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.pubsub.BroadcastPubSub');
    +
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.async.run');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.math');
    +goog.require('goog.pubsub.PubSub');
    +goog.require('goog.storage.Storage');
    +goog.require('goog.storage.mechanism.HTML5LocalStorage');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Topic-based publish/subscribe messaging implementation that provides
    + * communication between browsing contexts that share the same origin.
    + *
    + * Wrapper around PubSub that utilizes localStorage to broadcast publications to
    + * all browser windows with the same origin as the publishing context. This
    + * allows for topic-based publish/subscribe implementation of strings shared by
    + * all browser contexts that share the same origin.
    + *
    + * Delivery is guaranteed on all browsers except IE8 where topics expire after a
    + * timeout. Publishing of a topic within a callback function provides no
    + * guarantee on ordering in that there is a possiblilty that separate origin
    + * contexts may see topics in a different order.
    + *
    + * This class is not secure and in certain cases (e.g., a browser crash) data
    + * that is published can persist in localStorage indefinitely. Do not use this
    + * class to communicate private or confidential information.
    + *
    + * On IE8, localStorage is shared by the http and https origins. An attacker
    + * could possibly leverage this to publish to the secure origin.
    + *
    + * goog.labs.pubsub.BroadcastPubSub wraps an instance of PubSub rather than
    + * subclassing because the base PubSub class allows publishing of arbitrary
    + * objects.
    + *
    + * Special handling is done for the IE8 browsers. See the IE8_EVENTS_KEY_
    + * constant and the {@code publish} function for more information.
    + *
    + *
    + * @constructor @struct @extends {goog.Disposable}
    + * @suppress {checkStructDictInheritance}
    + */
    +goog.labs.pubsub.BroadcastPubSub = function() {
    +  goog.labs.pubsub.BroadcastPubSub.base(this, 'constructor');
    +  goog.labs.pubsub.BroadcastPubSub.instances_.push(this);
    +
    +  /** @private @const */
    +  this.pubSub_ = new goog.pubsub.PubSub();
    +  this.registerDisposable(this.pubSub_);
    +
    +  /** @private @const */
    +  this.handler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.handler_);
    +
    +  /** @private @const */
    +  this.logger_ = goog.log.getLogger('goog.labs.pubsub.BroadcastPubSub');
    +
    +  /** @private @const */
    +  this.mechanism_ = new goog.storage.mechanism.HTML5LocalStorage();
    +
    +  /** @private {goog.storage.Storage} */
    +  this.storage_ = null;
    +
    +  /** @private {Object<string, number>} */
    +  this.ie8LastEventTimes_ = null;
    +
    +  /** @private {number} */
    +  this.ie8StartupTimestamp_ = goog.now() - 1;
    +
    +  if (this.mechanism_.isAvailable()) {
    +    this.storage_ = new goog.storage.Storage(this.mechanism_);
    +
    +    var target = window;
    +    if (goog.labs.pubsub.BroadcastPubSub.IS_IE8_) {
    +      this.ie8LastEventTimes_ = {};
    +
    +      target = document;
    +    }
    +    this.handler_.listen(target,
    +        goog.events.EventType.STORAGE,
    +        this.handleStorageEvent_);
    +  }
    +};
    +goog.inherits(goog.labs.pubsub.BroadcastPubSub, goog.Disposable);
    +
    +
    +/** @private @const {!Array<!goog.labs.pubsub.BroadcastPubSub>} */
    +goog.labs.pubsub.BroadcastPubSub.instances_ = [];
    +
    +
    +/**
    + * SitePubSub namespace for localStorage.
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_ = '_closure_bps';
    +
    +
    +/**
    + * Handle the storage event and possibly dispatch topics.
    + * @param {!goog.events.Event} e Event object.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.handleStorageEvent_ =
    +    function(e) {
    +  if (goog.labs.pubsub.BroadcastPubSub.IS_IE8_) {
    +    // Even though we have the event, IE8 doesn't update our localStorage until
    +    // after we handle the actual event.
    +    goog.async.run(this.handleIe8StorageEvent_, this);
    +    return;
    +  }
    +
    +  var browserEvent = e.getBrowserEvent();
    +  if (browserEvent.key !=
    +      goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_) {
    +    return;
    +  }
    +
    +  var data = goog.json.parse(browserEvent.newValue);
    +  var args = goog.isObject(data) && data['args'];
    +  if (goog.isArray(args) && goog.array.every(args, goog.isString)) {
    +    this.dispatch_(args);
    +  } else {
    +    goog.log.warning(this.logger_, 'storage event contained invalid arguments');
    +  }
    +};
    +
    +
    +/**
    + * Dispatches args on the internal pubsub queue.
    + * @param {!Array<string>} args The arguments to publish.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.dispatch_ = function(args) {
    +  goog.pubsub.PubSub.prototype.publish.apply(this.pubSub_, args);
    +};
    +
    +
    +/**
    + * Publishes a message to a topic. Remote subscriptions in other tabs/windows
    + * are dispatched via local storage events. Local subscriptions are called
    + * asynchronously via Timer event in order to simulate remote behavior locally.
    + * @param {string} topic Topic to publish to.
    + * @param {...string} var_args String arguments that are applied to each
    + *     subscription function.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.publish =
    +    function(topic, var_args) {
    +  var args = goog.array.toArray(arguments);
    +
    +  // Dispatch to localStorage.
    +  if (this.storage_) {
    +    // Update topics to use the optional prefix.
    +    var now = goog.now();
    +    var data = {
    +      'args': args,
    +      'timestamp': now
    +    };
    +
    +    if (!goog.labs.pubsub.BroadcastPubSub.IS_IE8_) {
    +      // Generated events will contain all the data in modern browsers.
    +      this.storage_.set(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_, data);
    +      this.storage_.remove(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_);
    +    } else {
    +      // With IE8 we need to manage our own events queue.
    +      var events = null;
    +      /** @preserveTry */
    +      try {
    +        events = this.storage_.get(
    +            goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +      } catch (ex) {
    +        goog.log.error(this.logger_,
    +            'publish encountered invalid event queue at ' +
    +            goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +      }
    +      if (!goog.isArray(events)) {
    +        events = [];
    +      }
    +      // Avoid a race condition where we're publishing in the same
    +      // millisecond that another event that may be getting
    +      // processed. In short, we try go guarantee that whatever event
    +      // we put on the event queue has a timestamp that is older than
    +      // any other timestamp in the queue.
    +      var lastEvent = events[events.length - 1];
    +      var lastTimestamp = lastEvent && lastEvent['timestamp'] ||
    +          this.ie8StartupTimestamp_;
    +      if (lastTimestamp >= now) {
    +        now = lastTimestamp +
    +            goog.labs.pubsub.BroadcastPubSub.IE8_TIMESTAMP_UNIQUE_OFFSET_MS_;
    +        data['timestamp'] = now;
    +      }
    +      events.push(data);
    +      this.storage_.set(
    +          goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_, events);
    +
    +      // Cleanup this event in IE8_EVENT_LIFETIME_MS_ milliseconds.
    +      goog.Timer.callOnce(goog.bind(this.cleanupIe8StorageEvents_, this, now),
    +          goog.labs.pubsub.BroadcastPubSub.IE8_EVENT_LIFETIME_MS_);
    +    }
    +  }
    +
    +  // W3C spec is to not dispatch the storage event to the same window that
    +  // modified localStorage. For conforming browsers we have to manually dispatch
    +  // the publish event to subscriptions on instances of BroadcastPubSub in the
    +  // current window.
    +  if (!goog.userAgent.IE) {
    +    // Dispatch the publish event to local instances asynchronously to fix some
    +    // quirks with timings. The result is that all subscriptions are dispatched
    +    // before any future publishes are processed. The effect is that
    +    // subscriptions in the same window are dispatched as if they are the result
    +    // of a publish from another tab.
    +    goog.array.forEach(goog.labs.pubsub.BroadcastPubSub.instances_,
    +        function(instance) {
    +          goog.async.run(goog.bind(instance.dispatch_, instance, args));
    +        });
    +  }
    +};
    +
    +
    +/**
    + * Unsubscribes a function from a topic. Only deletes the first match found.
    + * Returns a Boolean indicating whether a subscription was removed.
    + * @param {string} topic Topic to unsubscribe from.
    + * @param {Function} fn Function to unsubscribe.
    + * @param {Object=} opt_context Object in whose context the function was to be
    + *     called (the global scope if none).
    + * @return {boolean} Whether a matching subscription was removed.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.unsubscribe =
    +    function(topic, fn, opt_context) {
    +  return this.pubSub_.unsubscribe(topic, fn, opt_context);
    +};
    +
    +
    +/**
    + * Removes a subscription based on the key returned by {@link #subscribe}. No-op
    + * if no matching subscription is found. Returns a Boolean indicating whether a
    + * subscription was removed.
    + * @param {number} key Subscription key.
    + * @return {boolean} Whether a matching subscription was removed.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.unsubscribeByKey = function(key) {
    +  return this.pubSub_.unsubscribeByKey(key);
    +};
    +
    +
    +/**
    + * Subscribes a function to a topic. The function is invoked as a method on the
    + * given {@code opt_context} object, or in the global scope if no context is
    + * specified. Subscribing the same function to the same topic multiple times
    + * will result in multiple function invocations while publishing. Returns a
    + * subscription key that can be used to unsubscribe the function from the topic
    + * via {@link #unsubscribeByKey}.
    + * @param {string} topic Topic to subscribe to.
    + * @param {Function} fn Function to be invoked when a message is published to
    + *     the given topic.
    + * @param {Object=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.subscribe =
    +    function(topic, fn, opt_context) {
    +  return this.pubSub_.subscribe(topic, fn, opt_context);
    +};
    +
    +
    +/**
    + * Subscribes a single-use function to a topic. The function is invoked as a
    + * method on the given {@code opt_context} object, or in the global scope if no
    + * context is specified, and is then unsubscribed. Returns a subscription key
    + * that can be used to unsubscribe the function from the topic via {@link
    + * #unsubscribeByKey}.
    + * @param {string} topic Topic to subscribe to.
    + * @param {Function} fn Function to be invoked once and then unsubscribed when
    + *     a message is published to the given topic.
    + * @param {Object=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.subscribeOnce =
    +    function(topic, fn, opt_context) {
    +  return this.pubSub_.subscribeOnce(topic, fn, opt_context);
    +};
    +
    +
    +/**
    + * Returns the number of subscriptions to the given topic (or all topics if
    + * unspecified).
    + * @param {string=} opt_topic The topic (all topics if unspecified).
    + * @return {number} Number of subscriptions to the topic.
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.getCount = function(opt_topic) {
    +  return this.pubSub_.getCount(opt_topic);
    +};
    +
    +
    +/**
    + * Clears the subscription list for a topic, or all topics if unspecified.
    + * @param {string=} opt_topic Topic to clear (all topics if unspecified).
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.clear = function(opt_topic) {
    +  this.pubSub_.clear(opt_topic);
    +};
    +
    +
    +/** @override */
    +goog.labs.pubsub.BroadcastPubSub.prototype.disposeInternal = function() {
    +  goog.array.remove(goog.labs.pubsub.BroadcastPubSub.instances_, this);
    +  if (goog.labs.pubsub.BroadcastPubSub.IS_IE8_ &&
    +      goog.isDefAndNotNull(this.storage_) &&
    +      goog.labs.pubsub.BroadcastPubSub.instances_.length == 0) {
    +    this.storage_.remove(
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  }
    +  goog.labs.pubsub.BroadcastPubSub.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Prefix for IE8 storage event queue keys.
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_PREFIX_ = '_closure_bps_ie8evt';
    +
    +
    +/**
    + * Time (in milliseconds) that IE8 events should live. If they are not
    + * processed by other windows in this time they will be removed.
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.IE8_EVENT_LIFETIME_MS_ = 1000 * 10;
    +
    +
    +/**
    + * Time (in milliseconds) that the IE8 event queue should live.
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.IE8_QUEUE_LIFETIME_MS_ = 1000 * 30;
    +
    +
    +/**
    + * Time delta that is used to distinguish between timestamps of events that
    + * happen in the same millisecond.
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.IE8_TIMESTAMP_UNIQUE_OFFSET_MS_ = .01;
    +
    +
    +/**
    + * Name for this window/tab's storage key that stores its IE8 event queue.
    + *
    + * The browsers storage events are supposed to track the key which was changed,
    + * the previous value for that key, and the new value of that key. Our
    + * implementation is dependent on this information but IE8 doesn't provide it.
    + * We implement our own event queue using local storage to track this
    + * information in IE8. Since all instances share the same localStorage context
    + * in a particular tab, we share the events queue.
    + *
    + * This key is a static member shared by all instances of BroadcastPubSub in the
    + * same Window context. To avoid read-update-write contention, this key is only
    + * written in a single context in the cleanupIe8StorageEvents_ function. Since
    + * instances in other contexts will read this key there is code in the {@code
    + * publish} function to make sure timestamps are unique even within the same
    + * millisecond.
    + *
    + * @private @const
    + */
    +goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_ =
    +    goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_PREFIX_ +
    +        goog.math.randomInt(1e9);
    +
    +
    +/**
    + * All instances of this object should access elements using strings and not
    + * attributes. Since we are communicating across browser tabs we could be
    + * dealing with different versions of javascript and thus may have different
    + * obfuscation in each tab.
    + * @private @typedef {{'timestamp': number, 'args': !Array<string>}}
    + */
    +goog.labs.pubsub.BroadcastPubSub.Ie8Event_;
    +
    +
    +/** @private @const */
    +goog.labs.pubsub.BroadcastPubSub.IS_IE8_ =
    +    goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE == 8;
    +
    +
    +/**
    + * Validates an event object.
    + * @param {!Object} obj The object to validate as an Event.
    + * @return {?goog.labs.pubsub.BroadcastPubSub.Ie8Event_} A valid
    + *     event object or null if the object is invalid.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.validateIe8Event_ = function(obj) {
    +  if (goog.isObject(obj) && goog.isNumber(obj['timestamp']) &&
    +      goog.array.every(obj['args'], goog.isString)) {
    +    return {'timestamp': obj['timestamp'], 'args': obj['args']};
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns an array of valid IE8 events.
    + * @param {!Array<!Object>} events Possible IE8 events.
    + * @return {!Array<!goog.labs.pubsub.BroadcastPubSub.Ie8Event_>}
    + *     Valid IE8 events.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.filterValidIe8Events_ = function(events) {
    +  return goog.array.filter(goog.array.map(events,
    +      goog.labs.pubsub.BroadcastPubSub.validateIe8Event_),
    +      goog.isDefAndNotNull);
    +};
    +
    +
    +/**
    + * Returns the IE8 events that have a timestamp later than the provided
    + * timestamp.
    + * @param {number} timestamp Expired timestamp.
    + * @param {!Array<!goog.labs.pubsub.BroadcastPubSub.Ie8Event_>} events
    + *     Possible IE8 events.
    + * @return {!Array<!goog.labs.pubsub.BroadcastPubSub.Ie8Event_>}
    + *     Unexpired IE8 events.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.filterNewIe8Events_ =
    +    function(timestamp, events) {
    +  return goog.array.filter(events, function(event) {
    +    return event['timestamp'] > timestamp;
    +  });
    +};
    +
    +
    +/**
    + * Processes the events array for key if all elements are valid IE8 events.
    + * @param {string} key The key in localStorage where the event queue is stored.
    + * @param {!Array<!Object>} events Array of possible events stored at key.
    + * @return {boolean} Return true if all elements in the array are valid
    + *     events, false otherwise.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.maybeProcessIe8Events_ =
    +    function(key, events) {
    +  if (!events.length) {
    +    return false;
    +  }
    +
    +  var validEvents =
    +      goog.labs.pubsub.BroadcastPubSub.filterValidIe8Events_(events);
    +  if (validEvents.length == events.length) {
    +    var lastTimestamp = goog.array.peek(validEvents)['timestamp'];
    +    var previousTime =
    +        this.ie8LastEventTimes_[key] || this.ie8StartupTimestamp_;
    +    if (lastTimestamp > previousTime -
    +        goog.labs.pubsub.BroadcastPubSub.IE8_QUEUE_LIFETIME_MS_) {
    +      this.ie8LastEventTimes_[key] = lastTimestamp;
    +      validEvents = goog.labs.pubsub.BroadcastPubSub.filterNewIe8Events_(
    +          previousTime, validEvents);
    +      for (var i = 0, event; event = validEvents[i]; i++) {
    +        this.dispatch_(event['args']);
    +      }
    +      return true;
    +    }
    +  } else {
    +    goog.log.warning(this.logger_, 'invalid events found in queue ' + key);
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Handle the storage event and possibly dispatch events. Looks through all keys
    + * in localStorage for valid keys.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.handleIe8StorageEvent_ = function() {
    +  var numKeys = this.mechanism_.getCount();
    +  for (var idx = 0; idx < numKeys; idx++) {
    +    var key = this.mechanism_.key(idx);
    +    // Don't process events we generated. The W3C standard says that storage
    +    // events should be queued by the browser for each window whose document's
    +    // storage object is affected by a change in localStorage. Chrome, Firefox,
    +    // and modern IE don't dispatch the event to the window which made the
    +    // change. This code simulates that behavior in IE8.
    +    if (!(goog.isString(key) && goog.string.startsWith(
    +        key, goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_PREFIX_))) {
    +      continue;
    +    }
    +
    +    var events = null;
    +    /** @preserveTry */
    +    try {
    +      events = this.storage_.get(key);
    +    } catch (ex) {
    +      goog.log.warning(this.logger_, 'invalid remote event queue ' + key);
    +    }
    +
    +    if (!(goog.isArray(events) && this.maybeProcessIe8Events_(key, events))) {
    +      // Events is not an array, empty, contains invalid events, or expired.
    +      this.storage_.remove(key);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Cleanup our IE8 event queue by removing any events that come at or before the
    + * given timestamp.
    + * @param {number} timestamp Maximum timestamp to remove from the queue.
    + * @private
    + */
    +goog.labs.pubsub.BroadcastPubSub.prototype.cleanupIe8StorageEvents_ =
    +    function(timestamp) {
    +  var events = null;
    +  /** @preserveTry */
    +  try {
    +    events = this.storage_.get(
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  } catch (ex) {
    +    goog.log.error(this.logger_,
    +        'cleanup encountered invalid event queue key ' +
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  }
    +  if (!goog.isArray(events)) {
    +    this.storage_.remove(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +    return;
    +  }
    +
    +  events = goog.labs.pubsub.BroadcastPubSub.filterNewIe8Events_(
    +      timestamp, goog.labs.pubsub.BroadcastPubSub.filterValidIe8Events_(
    +          events));
    +
    +  if (events.length > 0) {
    +    this.storage_.set(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_, events);
    +  } else {
    +    this.storage_.remove(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub_test.js b/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub_test.js
    new file mode 100644
    index 0000000000..a63005b939
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/pubsub/broadcastpubsub_test.js
    @@ -0,0 +1,1059 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.pubsub.BroadcastPubSubTest');
    +goog.setTestOnly('goog.labs.pubsub.BroadcastPubSubTest');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.debug.Logger');
    +goog.require('goog.json');
    +goog.require('goog.labs.pubsub.BroadcastPubSub');
    +goog.require('goog.storage.Storage');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.testing.mockmatchers.ArgumentMatcher');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +
    +/** @type {goog.labs.pubsub.BroadcastPubSub} */
    +var broadcastPubSub;
    +
    +
    +/** @type {goog.testing.MockControl} */
    +var mockControl;
    +
    +
    +/** @type {goog.testing.MockClock} */
    +var mockClock;
    +
    +
    +/** @type {goog.testing.MockInterface} */
    +var mockStorage;
    +
    +
    +/** @type {goog.testing.MockInterface} */
    +var mockStorageCtor;
    +
    +
    +/** @type {goog.structs.Map} */
    +var mockHtml5LocalStorage;
    +
    +
    +/** @type {goog.testing.MockInterface} */
    +var mockHTML5LocalStorageCtor;
    +
    +
    +/** @const {boolean} */
    +var isIe8 = goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE == 8;
    +
    +
    +/**
    + * Sends a remote storage event with special handling for IE8. With IE8 an
    + * event is pushed to the event queue stored in local storage as a result of
    + * behaviour by the mockHtml5LocalStorage instanciated when using IE8 an event
    + * is automatically generated in the local browser context. For other browsers
    + * this simply creates a new browser event.
    + * @param {{'args': !Array<string>, 'timestamp': number}} data Value stored
    + *     in localStorage which generated the remote event.
    + */
    +function remoteStorageEvent(data) {
    +  if (!isIe8) {
    +    var event = new goog.testing.events.Event('storage', window);
    +    event.key = goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_;
    +    event.newValue = goog.json.serialize(data);
    +    goog.testing.events.fireBrowserEvent(event);
    +  } else {
    +    var uniqueKey =
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_PREFIX_ +
    +        '1234567890';
    +    var ie8Events = mockHtml5LocalStorage.get(uniqueKey);
    +    if (goog.isDefAndNotNull(ie8Events)) {
    +      ie8Events = goog.json.parse(ie8Events);
    +      // Events should never overlap in IE8 mode.
    +      if (ie8Events.length > 0 &&
    +          ie8Events[ie8Events.length - 1]['timestamp'] >=
    +          data['timestamp']) {
    +        data['timestamp'] =
    +            ie8Events[ie8Events.length - 1]['timestamp'] +
    +            goog.labs.pubsub.BroadcastPubSub.
    +            IE8_TIMESTAMP_UNIQUE_OFFSET_MS_;
    +      }
    +    } else {
    +      ie8Events = [];
    +    }
    +    ie8Events.push(data);
    +    // This will cause an event.
    +    mockHtml5LocalStorage.set(uniqueKey, goog.json.serialize(ie8Events));
    +  }
    +}
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +
    +  mockClock = new goog.testing.MockClock(true);
    +  // Time should never be 0...
    +  mockClock.tick();
    +  /** @suppress {missingRequire} */
    +  mockHTML5LocalStorageCtor = mockControl.createConstructorMock(
    +      goog.storage.mechanism, 'HTML5LocalStorage');
    +
    +  mockHtml5LocalStorage = new goog.structs.Map();
    +
    +  // The builtin localStorage returns null instead of undefined.
    +  var originalGetFn = goog.bind(mockHtml5LocalStorage.get,
    +      mockHtml5LocalStorage);
    +  mockHtml5LocalStorage.get = function(key) {
    +    var value = originalGetFn(key);
    +    if (!goog.isDef(value)) {
    +      return null;
    +    }
    +    return value;
    +  };
    +  mockHtml5LocalStorage.key = function(idx) {
    +    return mockHtml5LocalStorage.getKeys()[idx];
    +  };
    +  mockHtml5LocalStorage.isAvailable = function() {
    +    return true;
    +  };
    +
    +
    +  // IE has problems. IE9+ still dispatches storage events locally. IE8 also
    +  // doesn't include the key/value information. So for IE, everytime we get a
    +  // "set" on localStorage we simulate for the appropriate browser.
    +  if (goog.userAgent.IE) {
    +    var target = isIe8 ? document : window;
    +    var originalSetFn = goog.bind(mockHtml5LocalStorage.set,
    +        mockHtml5LocalStorage);
    +    mockHtml5LocalStorage.set = function(key, value) {
    +      originalSetFn(key, value);
    +      var event = new goog.testing.events.Event('storage', target);
    +      if (!isIe8) {
    +        event.key = key;
    +        event.newValue = value;
    +      }
    +      goog.testing.events.fireBrowserEvent(event);
    +    };
    +  }
    +
    +}
    +
    +function tearDown() {
    +  mockControl.$tearDown();
    +  mockClock.dispose();
    +  broadcastPubSub = undefined;
    +}
    +
    +
    +function testConstructor() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  assertNotNullNorUndefined(
    +      'BroadcastChannel instance must not be null', broadcastPubSub);
    +  assertTrue('BroadcastChannel instance must have the expected type',
    +      broadcastPubSub instanceof goog.labs.pubsub.BroadcastPubSub);
    +  assertArrayEquals(
    +      goog.labs.pubsub.BroadcastPubSub.instances_, [broadcastPubSub]);
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +  assertNotNullNorUndefined(
    +      'Storage should not be undefined or null in broadcastPubSub.',
    +      broadcastPubSub.storage_);
    +  assertArrayEquals(goog.labs.pubsub.BroadcastPubSub.instances_, []);
    +}
    +
    +
    +function testConstructor_noLocalStorage() {
    +  mockHTML5LocalStorageCtor().$returns({isAvailable: function() {
    +    return false;
    +  }});
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  assertNotNullNorUndefined(
    +      'BroadcastChannel instance must not be null', broadcastPubSub);
    +  assertTrue('BroadcastChannel instance must have the expected type',
    +      broadcastPubSub instanceof goog.labs.pubsub.BroadcastPubSub);
    +  assertArrayEquals(
    +      goog.labs.pubsub.BroadcastPubSub.instances_, [broadcastPubSub]);
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +  assertNull(
    +      'Storage should be null in broadcastPubSub.', broadcastPubSub.storage_);
    +  assertArrayEquals(goog.labs.pubsub.BroadcastPubSub.instances_, []);
    +}
    +
    +
    +/** Verify we cleanup after ourselves. */
    +function testDispose() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var mockStorage = mockControl.createLooseMock(goog.storage.Storage);
    +
    +  var mockStorageCtor = mockControl.createConstructorMock(
    +      goog.storage, 'Storage');
    +
    +  mockStorageCtor(mockHtml5LocalStorage).$returns(mockStorage);
    +  mockStorageCtor(mockHtml5LocalStorage).$returns(mockStorage);
    +
    +  if (isIe8) {
    +    mockStorage.remove(
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  }
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  var broadcastPubSubExtra = new goog.labs.pubsub.BroadcastPubSub();
    +  assertArrayEquals(goog.labs.pubsub.BroadcastPubSub.instances_,
    +      [broadcastPubSub, broadcastPubSubExtra]);
    +
    +  assertFalse('BroadcastChannel extra instance must not have been disposed of',
    +      broadcastPubSubExtra.isDisposed());
    +  broadcastPubSubExtra.dispose();
    +  assertTrue('BroadcastChannel extra instance must have been disposed of',
    +      broadcastPubSubExtra.isDisposed());
    +  assertFalse('BroadcastChannel instance must not have been disposed of',
    +      broadcastPubSub.isDisposed());
    +
    +  assertArrayEquals(
    +      goog.labs.pubsub.BroadcastPubSub.instances_, [broadcastPubSub]);
    +  assertFalse('BroadcastChannel instance must not have been disposed of',
    +      broadcastPubSub.isDisposed());
    +  broadcastPubSub.dispose();
    +  assertTrue('BroadcastChannel instance must have been disposed of',
    +      broadcastPubSub.isDisposed());
    +  assertArrayEquals(goog.labs.pubsub.BroadcastPubSub.instances_, []);
    +  mockControl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests related to remote events that an instance of BroadcastChannel
    + * should handle.
    + */
    +function testHandleRemoteEvent() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  foo('x', 'y').$times(2);
    +
    +  var context = {'foo': 'bar'};
    +  var bar = goog.testing.recordFunction();
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  var eventData = {
    +    'args': ['someTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  };
    +
    +  broadcastPubSub.subscribe('someTopic', foo);
    +  broadcastPubSub.subscribe('someTopic', bar, context);
    +
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  assertEquals(1, bar.getCallCount());
    +  assertEquals(context, bar.getLastCall().getThis());
    +  assertArrayEquals(['x', 'y'], bar.getLastCall().getArguments());
    +
    +  broadcastPubSub.unsubscribe('someTopic', foo);
    +  eventData['timestamp'] = goog.now();
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  assertEquals(2, bar.getCallCount());
    +  assertEquals(context, bar.getLastCall().getThis());
    +  assertArrayEquals(['x', 'y'], bar.getLastCall().getArguments());
    +
    +  broadcastPubSub.subscribe('someTopic', foo);
    +  broadcastPubSub.unsubscribe('someTopic', bar, context);
    +  eventData['timestamp'] = goog.now();
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  assertEquals(2, bar.getCallCount());
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testHandleRemoteEventSubscribeOnce() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  foo('x', 'y');
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribeOnce('someTopic', foo);
    +  assertEquals('BroadcastChannel must have one subscriber',
    +      1, broadcastPubSub.getCount());
    +
    +  remoteStorageEvent({
    +    'args': ['someTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  });
    +  mockClock.tick();
    +
    +  assertEquals(
    +      'BroadcastChannel must have no subscribers after receiving the event',
    +      0, broadcastPubSub.getCount());
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testHandleQueuedRemoteEvents() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  var bar = mockControl.createFunctionMock();
    +
    +  foo('x', 'y');
    +  bar('d', 'c');
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('fooTopic', foo);
    +  broadcastPubSub.subscribe('barTopic', bar);
    +
    +  var eventData = {
    +    'args': ['fooTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +
    +  var eventData = {
    +    'args': ['barTopic', 'd', 'c'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testHandleRemoteEventsUnsubscribe() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  var bar = mockControl.createFunctionMock();
    +
    +  foo('x', 'y').$does(function() {
    +    broadcastPubSub.unsubscribe('barTopic', bar);
    +  });
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('fooTopic', foo);
    +  broadcastPubSub.subscribe('barTopic', bar);
    +
    +  var eventData = {
    +    'args': ['fooTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  var eventData = {
    +    'args': ['barTopic', 'd', 'c'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testHandleRemoteEventsCalledOnce() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  foo('x', 'y');
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribeOnce('someTopic', foo);
    +
    +  var eventData = {
    +    'args': ['someTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  var eventData = {
    +    'args': ['someTopic', 'x', 'y'],
    +    'timestamp': goog.now()
    +  };
    +  remoteStorageEvent(eventData);
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testHandleRemoteEventNestedPublish() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo1 = mockControl.createFunctionMock();
    +  foo1().$does(function() {
    +    remoteStorageEvent({
    +      'args': ['bar'],
    +      'timestamp': goog.now()
    +    });
    +  });
    +  var foo2 = mockControl.createFunctionMock();
    +  foo2();
    +  var bar1 = mockControl.createFunctionMock();
    +  bar1().$does(function() {
    +    broadcastPubSub.publish('baz');
    +  });
    +  var bar2 = mockControl.createFunctionMock();
    +  bar2();
    +  var baz1 = mockControl.createFunctionMock();
    +  baz1();
    +  var baz2 = mockControl.createFunctionMock();
    +  baz2();
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('foo', foo1);
    +  broadcastPubSub.subscribe('foo', foo2);
    +  broadcastPubSub.subscribe('bar', bar1);
    +  broadcastPubSub.subscribe('bar', bar2);
    +  broadcastPubSub.subscribe('baz', baz1);
    +  broadcastPubSub.subscribe('baz', baz2);
    +
    +  remoteStorageEvent({
    +    'args': ['foo'],
    +    'timestamp': goog.now()
    +  });
    +  mockClock.tick();
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +/**
    + * Local publish that originated from another instance of BroadcastChannel
    + * in the same Javascript context.
    + */
    +function testSecondInstancePublish() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage).$times(2);
    +  var foo = mockControl.createFunctionMock();
    +  foo('x', 'y');
    +  var context = {'foo': 'bar'};
    +  var bar = goog.testing.recordFunction();
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('someTopic', foo);
    +  broadcastPubSub.subscribe('someTopic', bar, context);
    +
    +  var broadcastPubSub2 = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub2.publish('someTopic', 'x', 'y');
    +  mockClock.tick();
    +
    +  assertEquals(1, bar.getCallCount());
    +  assertEquals(context, bar.getLastCall().getThis());
    +  assertArrayEquals(['x', 'y'], bar.getLastCall().getArguments());
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSecondInstanceNestedPublish() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage).$times(2);
    +  var foo = mockControl.createFunctionMock();
    +  foo('m', 'n').$does(function() {
    +    broadcastPubSub.publish('barTopic', 'd', 'c');
    +  });
    +  var bar = mockControl.createFunctionMock();
    +  bar('d', 'c');
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('fooTopic', foo);
    +
    +  var broadcastPubSub2 = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub2.subscribe('barTopic', bar);
    +  broadcastPubSub2.publish('fooTopic', 'm', 'n');
    +  mockClock.tick();
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +/**
    + * Validate the localStorage data is being set as we expect.
    + */
    +function testLocalStorageData() {
    +  var topic = 'someTopic';
    +  var anotherTopic = 'anotherTopic';
    +  var now = goog.now();
    +
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var mockStorage = mockControl.createLooseMock(goog.storage.Storage);
    +
    +  var mockStorageCtor = mockControl.createConstructorMock(
    +      goog.storage, 'Storage');
    +
    +  mockStorageCtor(mockHtml5LocalStorage).$returns(mockStorage);
    +  if (!isIe8) {
    +    mockStorage.set(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_,
    +        {'args': [topic, '10'], 'timestamp': now});
    +    mockStorage.remove(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_);
    +    mockStorage.set(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_,
    +        {'args': [anotherTopic, '13'], 'timestamp': now});
    +    mockStorage.remove(goog.labs.pubsub.BroadcastPubSub.STORAGE_KEY_);
    +  } else {
    +    var firstEventArray = [
    +      {'args': [topic, '10'], 'timestamp': now}
    +    ];
    +    var secondEventArray = [
    +      {'args': [topic, '10'], 'timestamp': now},
    +      {'args': [anotherTopic, '13'], 'timestamp': now +
    +            goog.labs.pubsub.BroadcastPubSub.
    +            IE8_TIMESTAMP_UNIQUE_OFFSET_MS_}
    +    ];
    +
    +    mockStorage.get(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_).
    +        $returns(null);
    +    mockStorage.set(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_,
    +        new goog.testing.mockmatchers.ArgumentMatcher(function(val) {
    +          return goog.testing.mockmatchers.flexibleArrayMatcher(
    +              firstEventArray, val);
    +        }, 'First event array'));
    +
    +    // Make sure to clone or you're going to have a bad time.
    +    mockStorage.get(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_).
    +        $returns(goog.array.clone(firstEventArray));
    +
    +    mockStorage.set(goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_,
    +        new goog.testing.mockmatchers.ArgumentMatcher(function(val) {
    +          return goog.testing.mockmatchers.flexibleArrayMatcher(
    +              secondEventArray, val);
    +        }, 'Second event array'));
    +
    +    mockStorage.remove(
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_);
    +  }
    +
    +  var fn = goog.testing.recordFunction();
    +  fn('10');
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe(topic, fn);
    +
    +  broadcastPubSub.publish(topic, '10');
    +  broadcastPubSub.publish(anotherTopic, '13');
    +
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testBrokenTimestamp() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fn = mockControl.createFunctionMock();
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('someTopic', fn);
    +
    +  remoteStorageEvent({
    +    'args': 'WAT?',
    +    'timestamp': 'wat?'
    +  });
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +/** Test response to bad localStorage data. */
    +function testBrokenEvent() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fn = mockControl.createFunctionMock();
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('someTopic', fn);
    +
    +  if (!isIe8) {
    +    var event = new goog.testing.events.Event('storage', window);
    +    event.key = 'FooBarBaz';
    +    event.newValue = goog.json.serialize({'keyby': 'word'});
    +    goog.testing.events.fireBrowserEvent(event);
    +  } else {
    +    var uniqueKey =
    +        goog.labs.pubsub.BroadcastPubSub.IE8_EVENTS_KEY_PREFIX_ +
    +        '1234567890';
    +    // This will cause an event.
    +    mockHtml5LocalStorage.set(uniqueKey, 'Toothpaste!');
    +  }
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +/**
    + * The following tests are duplicated from pubsub because they depend
    + * on functionality (mostly "publish") that has changed in BroadcastChannel.
    + */
    +function testPublish() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  foo('x', 'y');
    +
    +  var context = {'foo': 'bar'};
    +  var bar = goog.testing.recordFunction();
    +
    +  var baz = mockControl.createFunctionMock();
    +  baz('d', 'c');
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('someTopic', foo);
    +  broadcastPubSub.subscribe('someTopic', bar, context);
    +  broadcastPubSub.subscribe('anotherTopic', baz, context);
    +
    +  broadcastPubSub.publish('someTopic', 'x', 'y');
    +  mockClock.tick();
    +
    +  assertTrue(broadcastPubSub.unsubscribe('someTopic', foo));
    +
    +  broadcastPubSub.publish('anotherTopic', 'd', 'c');
    +  broadcastPubSub.publish('someTopic', 'x', 'y');
    +  mockClock.tick();
    +
    +  broadcastPubSub.subscribe('differentTopic', foo);
    +
    +  broadcastPubSub.publish('someTopic', 'x', 'y');
    +  mockClock.tick();
    +
    +  assertEquals(3, bar.getCallCount());
    +  goog.array.forEach(bar.getCalls(), function(call) {
    +    assertArrayEquals(['x', 'y'], call.getArguments());
    +    assertEquals(context, call.getThis());
    +  });
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testPublishEmptyTopic() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var foo = mockControl.createFunctionMock();
    +  foo();
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  broadcastPubSub.subscribe('someTopic', foo);
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  broadcastPubSub.unsubscribe('someTopic', foo);
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSubscribeWhilePublishing() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  // It's OK for a subscriber to add a new subscriber to its own topic,
    +  // but the newly added subscriber shouldn't be called until the next
    +  // publish cycle.
    +
    +  var fn1 = mockControl.createFunctionMock();
    +  var fn2 = mockControl.createFunctionMock();
    +  fn1().$does(function() {
    +    broadcastPubSub.subscribe('someTopic', fn2);
    +  }).$times(2);
    +  fn2();
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('someTopic', fn1);
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have two subscribers', 2,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have three subscribers', 3,
    +      broadcastPubSub.getCount('someTopic'));
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testUnsubscribeWhilePublishing() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  // It's OK for a subscriber to unsubscribe another subscriber from its
    +  // own topic, but the subscriber in question won't actually be removed
    +  // until after publishing is complete.
    +
    +
    +  var fn1 = mockControl.createFunctionMock();
    +  var fn2 = mockControl.createFunctionMock();
    +  var fn3 = mockControl.createFunctionMock();
    +
    +  fn1().$does(function() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        broadcastPubSub.unsubscribe('X', fn2));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        broadcastPubSub.getCount('X'));
    +  });
    +  fn2().$does(function() {
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        broadcastPubSub.getCount('X'));
    +  });
    +  fn3().$does(function() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        broadcastPubSub.unsubscribe('X', fn1));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        broadcastPubSub.getCount('X'));
    +  });
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('X', fn1);
    +  broadcastPubSub.subscribe('X', fn2);
    +  broadcastPubSub.subscribe('X', fn3);
    +
    +  assertEquals('Topic "X" must have 3 subscribers', 3,
    +      broadcastPubSub.getCount('X'));
    +
    +  broadcastPubSub.publish('X');
    +  mockClock.tick();
    +
    +  assertEquals('Topic "X" must have 1 subscriber after publishing', 1,
    +      broadcastPubSub.getCount('X'));
    +  assertEquals(
    +      'BroadcastChannel must not have any subscriptions pending removal',
    +      0, broadcastPubSub.pubSub_.pendingKeys_.length);
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testUnsubscribeSelfWhilePublishing() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  // It's OK for a subscriber to unsubscribe itself, but it won't actually
    +  // be removed until after publishing is complete.
    +
    +  var fn = mockControl.createFunctionMock();
    +  fn().$does(function() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        broadcastPubSub.unsubscribe('someTopic', fn));
    +    assertEquals('Topic must still have 1 subscriber', 1,
    +        broadcastPubSub.getCount('someTopic'));
    +  });
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribe('someTopic', fn);
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have no subscribers after publishing', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals(
    +      'BroadcastChannel must not have any subscriptions pending removal',
    +      0, broadcastPubSub.pubSub_.pendingKeys_.length);
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testNestedPublish() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var xFn1 = mockControl.createFunctionMock();
    +  xFn1().$does(function() {
    +    broadcastPubSub.publish('Y');
    +    broadcastPubSub.unsubscribe('X', arguments.callee);
    +  });
    +  var xFn2 = mockControl.createFunctionMock();
    +  xFn2();
    +
    +  var yFn1 = mockControl.createFunctionMock();
    +  yFn1().$does(function() {
    +    broadcastPubSub.unsubscribe('Y', arguments.callee);
    +  });
    +  var yFn2 = mockControl.createFunctionMock();
    +  yFn2();
    +
    +  mockControl.$replayAll();
    +
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribe('X', xFn1);
    +  broadcastPubSub.subscribe('X', xFn2);
    +  broadcastPubSub.subscribe('Y', yFn1);
    +  broadcastPubSub.subscribe('Y', yFn2);
    +
    +  broadcastPubSub.publish('X');
    +  mockClock.tick();
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSubscribeOnce() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fn = goog.testing.recordFunction();
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +  broadcastPubSub.subscribeOnce('someTopic', fn);
    +
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have no subscribers', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  var context = {'foo': 'bar'};
    +  broadcastPubSub.subscribeOnce('someTopic', fn, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('Subscriber must not have been called yet',
    +      1, fn.getCallCount());
    +
    +  broadcastPubSub.publish('someTopic');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have no subscribers', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('Subscriber must have been called',
    +      2, fn.getCallCount());
    +  assertEquals(context, fn.getLastCall().getThis());
    +  assertArrayEquals([], fn.getLastCall().getArguments());
    +
    +  context = {'foo': 'bar'};
    +  broadcastPubSub.subscribeOnce('someTopic', fn, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('Subscriber must not have been called',
    +      2, fn.getCallCount());
    +
    +  broadcastPubSub.publish('someTopic', '17');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have no subscribers', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals(context, fn.getLastCall().getThis());
    +  assertEquals('Subscriber must have been called',
    +      3, fn.getCallCount());
    +  assertArrayEquals(['17'], fn.getLastCall().getArguments());
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSubscribeOnce_boundFn() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fn = goog.testing.recordFunction();
    +  var context = {'foo': 'bar'};
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribeOnce('someTopic', goog.bind(fn, context));
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertNull('Subscriber must not have been called yet',
    +      fn.getLastCall());
    +
    +  broadcastPubSub.publish('someTopic', '17');
    +  mockClock.tick();
    +  assertEquals('Topic must have no subscribers', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('Subscriber must have been called', 1, fn.getCallCount());
    +  assertEquals('Must receive correct argument.',
    +      '17', fn.getLastCall().getArgument(0));
    +  assertEquals('Must have appropriate context.',
    +      context, fn.getLastCall().getThis());
    +
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSubscribeOnce_partialFn() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fullFn = mockControl.createFunctionMock();
    +  fullFn(true, '17');
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribeOnce('someTopic', goog.partial(fullFn, true));
    +  assertEquals('Topic must have one subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic', '17');
    +  mockClock.tick();
    +
    +  assertEquals('Topic must have no subscribers', 0,
    +      broadcastPubSub.getCount('someTopic'));
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSelfResubscribe() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var resubscribeFn = mockControl.createFunctionMock();
    +  var resubscribe = function() {
    +    broadcastPubSub.subscribeOnce('someTopic', resubscribeFn);
    +  };
    +  resubscribeFn('foo').$does(resubscribe);
    +  resubscribeFn('bar').$does(resubscribe);
    +  resubscribeFn('baz').$does(resubscribe);
    +
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  broadcastPubSub.subscribeOnce('someTopic', resubscribeFn);
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +
    +  broadcastPubSub.publish('someTopic', 'foo');
    +  mockClock.tick();
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('BroadcastChannel must not have any pending unsubscribe keys', 0,
    +      broadcastPubSub.pubSub_.pendingKeys_.length);
    +
    +  broadcastPubSub.publish('someTopic', 'bar');
    +  mockClock.tick();
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('BroadcastChannel must not have any pending unsubscribe keys', 0,
    +      broadcastPubSub.pubSub_.pendingKeys_.length);
    +
    +  broadcastPubSub.publish('someTopic', 'baz');
    +  mockClock.tick();
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      broadcastPubSub.getCount('someTopic'));
    +  assertEquals('BroadcastChannel must not have any pending unsubscribe keys', 0,
    +      broadcastPubSub.pubSub_.pendingKeys_.length);
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testClear() {
    +  mockHTML5LocalStorageCtor().$returns(mockHtml5LocalStorage);
    +  var fn = mockControl.createFunctionMock();
    +  mockControl.$replayAll();
    +  broadcastPubSub = new goog.labs.pubsub.BroadcastPubSub();
    +  broadcastPubSub.logger_.setLevel(goog.debug.Logger.Level.OFF);
    +
    +  goog.array.forEach(['V', 'W', 'X', 'Y', 'Z'], function(topic) {
    +    broadcastPubSub.subscribe(topic, fn);
    +  });
    +  assertEquals('BroadcastChannel must have 5 subscribers', 5,
    +      broadcastPubSub.getCount());
    +
    +  broadcastPubSub.clear('W');
    +  assertEquals('BroadcastChannel must have 4 subscribers', 4,
    +      broadcastPubSub.getCount());
    +
    +  goog.array.forEach(['X', 'Y'], function(topic) {
    +    broadcastPubSub.clear(topic);
    +  });
    +  assertEquals('BroadcastChannel must have 2 subscriber', 2,
    +      broadcastPubSub.getCount());
    +
    +  broadcastPubSub.clear();
    +  assertEquals('BroadcastChannel must have no subscribers', 0,
    +      broadcastPubSub.getCount());
    +  broadcastPubSub.dispose();
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage.js b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage.js
    new file mode 100644
    index 0000000000..5a6bfdcdd8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage.js
    @@ -0,0 +1,284 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data persistence with data
    + * expiration and number of items limit.
    + *
    + * Setting and removing values keeps a max number of items invariant.
    + * Collecting values can be user initiated. If oversize, first removes
    + * expired items, if still oversize than removes the oldest items until a size
    + * constraint is fulfilled.
    + *
    + */
    +
    +goog.provide('goog.labs.storage.BoundedCollectableStorage');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.iter');
    +goog.require('goog.storage.CollectableStorage');
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.ExpiringStorage');
    +
    +
    +goog.scope(function() {
    +var storage = goog.labs.storage;
    +
    +
    +
    +/**
    + * Provides a storage with bounded number of elements, expiring keys and
    + * a collection method.
    + *
    + * @param {!goog.storage.mechanism.IterableMechanism} mechanism The underlying
    + *     storage mechanism.
    + * @param {number} maxItems Maximum number of items in storage.
    + * @constructor
    + * @extends {goog.storage.CollectableStorage}
    + * @final
    + */
    +storage.BoundedCollectableStorage = function(mechanism, maxItems) {
    +  storage.BoundedCollectableStorage.base(this, 'constructor', mechanism);
    +
    +  /**
    +   * A maximum number of items that should be stored.
    +   * @private {number}
    +   */
    +  this.maxItems_ = maxItems;
    +};
    +goog.inherits(storage.BoundedCollectableStorage,
    +              goog.storage.CollectableStorage);
    +
    +
    +/**
    + * An item key used to store a list of keys.
    + * @const
    + * @private
    + */
    +storage.BoundedCollectableStorage.KEY_LIST_KEY_ = 'bounded-collectable-storage';
    +
    +
    +/**
    + * Recreates a list of keys in order of creation.
    + *
    + * @return {!Array<string>} a list of unexpired keys.
    + * @private
    + */
    +storage.BoundedCollectableStorage.prototype.rebuildIndex_ = function() {
    +  var keys = [];
    +  goog.iter.forEach(this.mechanism.__iterator__(true), function(key) {
    +    if (storage.BoundedCollectableStorage.KEY_LIST_KEY_ == key) {
    +      return;
    +    }
    +
    +    var wrapper;
    +    /** @preserveTry */
    +    try {
    +      wrapper = this.getWrapper(key, true);
    +    } catch (ex) {
    +      if (ex == goog.storage.ErrorCode.INVALID_VALUE) {
    +        // Skip over bad wrappers and continue.
    +        return;
    +      }
    +      // Unknown error, escalate.
    +      throw ex;
    +    }
    +    goog.asserts.assert(wrapper);
    +
    +    var creationTime = goog.storage.ExpiringStorage.getCreationTime(wrapper);
    +    keys.push({key: key, created: creationTime});
    +  }, this);
    +
    +  goog.array.sort(keys, function(a, b) {
    +    return a.created - b.created;
    +  });
    +
    +  return goog.array.map(keys, function(v) {
    +    return v.key;
    +  });
    +};
    +
    +
    +/**
    + * Gets key list from a local storage. If an item does not exist,
    + * may recreate it.
    + *
    + * @param {boolean} rebuild Whether to rebuild a index if no index item exists.
    + * @return {!Array<string>} a list of keys if index exist, otherwise undefined.
    + * @private
    + */
    +storage.BoundedCollectableStorage.prototype.getKeys_ = function(rebuild) {
    +  var keys = storage.BoundedCollectableStorage.superClass_.get.call(this,
    +      storage.BoundedCollectableStorage.KEY_LIST_KEY_) || null;
    +  if (!keys || !goog.isArray(keys)) {
    +    if (rebuild) {
    +      keys = this.rebuildIndex_();
    +    } else {
    +      keys = [];
    +    }
    +  }
    +  return /** @type {!Array<string>} */ (keys);
    +};
    +
    +
    +/**
    + * Saves a list of keys in a local storage.
    + *
    + * @param {Array<string>} keys a list of keys to save.
    + * @private
    + */
    +storage.BoundedCollectableStorage.prototype.setKeys_ = function(keys) {
    +  storage.BoundedCollectableStorage.superClass_.set.call(this,
    +      storage.BoundedCollectableStorage.KEY_LIST_KEY_, keys);
    +};
    +
    +
    +/**
    + * Remove subsequence from a sequence.
    + *
    + * @param {!Array<string>} keys is a sequence.
    + * @param {!Array<string>} keysToRemove subsequence of keys, the order must
    + *     be kept.
    + * @return {!Array<string>} a keys sequence after removing keysToRemove.
    + * @private
    + */
    +storage.BoundedCollectableStorage.removeSubsequence_ =
    +    function(keys, keysToRemove) {
    +  if (keysToRemove.length == 0) {
    +    return goog.array.clone(keys);
    +  }
    +  var keysToKeep = [];
    +  var keysIdx = 0;
    +  var keysToRemoveIdx = 0;
    +
    +  while (keysToRemoveIdx < keysToRemove.length && keysIdx < keys.length) {
    +    var key = keysToRemove[keysToRemoveIdx];
    +    while (keysIdx < keys.length && keys[keysIdx] != key) {
    +      keysToKeep.push(keys[keysIdx]);
    +      ++keysIdx;
    +    }
    +    ++keysToRemoveIdx;
    +  }
    +
    +  goog.asserts.assert(keysToRemoveIdx == keysToRemove.length);
    +  goog.asserts.assert(keysIdx < keys.length);
    +  return goog.array.concat(keysToKeep, goog.array.slice(keys, keysIdx + 1));
    +};
    +
    +
    +/**
    + * Keeps the number of items in storage under maxItems. Removes elements in the
    + * order of creation.
    + *
    + * @param {!Array<string>} keys a list of keys in order of creation.
    + * @param {number} maxSize a number of items to keep.
    + * @return {!Array<string>} keys left after removing oversize data.
    + * @private
    + */
    +storage.BoundedCollectableStorage.prototype.collectOversize_ =
    +    function(keys, maxSize) {
    +  if (keys.length <= maxSize) {
    +    return goog.array.clone(keys);
    +  }
    +  var keysToRemove = goog.array.slice(keys, 0, keys.length - maxSize);
    +  goog.array.forEach(keysToRemove, function(key) {
    +    storage.BoundedCollectableStorage.superClass_.remove.call(this, key);
    +  }, this);
    +  return storage.BoundedCollectableStorage.removeSubsequence_(
    +      keys, keysToRemove);
    +};
    +
    +
    +/**
    + * Cleans up the storage by removing expired keys.
    + *
    + * @param {boolean=} opt_strict Also remove invalid keys.
    + * @override
    + */
    +storage.BoundedCollectableStorage.prototype.collect =
    +    function(opt_strict) {
    +  var keys = this.getKeys_(true);
    +  var keysToRemove = this.collectInternal(keys, opt_strict);
    +  keys = storage.BoundedCollectableStorage.removeSubsequence_(
    +      keys, keysToRemove);
    +  this.setKeys_(keys);
    +};
    +
    +
    +/**
    + * Ensures that we keep only maxItems number of items in a local storage.
    + * @param {boolean=} opt_skipExpired skip removing expired items first.
    + * @param {boolean=} opt_strict Also remove invalid keys.
    + */
    +storage.BoundedCollectableStorage.prototype.collectOversize =
    +    function(opt_skipExpired, opt_strict) {
    +  var keys = this.getKeys_(true);
    +  if (!opt_skipExpired) {
    +    var keysToRemove = this.collectInternal(keys, opt_strict);
    +    keys = storage.BoundedCollectableStorage.removeSubsequence_(
    +        keys, keysToRemove);
    +  }
    +  keys = this.collectOversize_(keys, this.maxItems_);
    +  this.setKeys_(keys);
    +};
    +
    +
    +/**
    + * Set an item in the storage.
    + *
    + * @param {string} key The key to set.
    + * @param {*} value The value to serialize to a string and save.
    + * @param {number=} opt_expiration The number of miliseconds since epoch
    + *     (as in goog.now()) when the value is to expire. If the expiration
    + *     time is not provided, the value will persist as long as possible.
    + * @override
    + */
    +storage.BoundedCollectableStorage.prototype.set =
    +    function(key, value, opt_expiration) {
    +  storage.BoundedCollectableStorage.base(
    +      this, 'set', key, value, opt_expiration);
    +  var keys = this.getKeys_(true);
    +  goog.array.remove(keys, key);
    +
    +  if (goog.isDef(value)) {
    +    keys.push(key);
    +    if (keys.length >= this.maxItems_) {
    +      var keysToRemove = this.collectInternal(keys);
    +      keys = storage.BoundedCollectableStorage.removeSubsequence_(
    +          keys, keysToRemove);
    +      keys = this.collectOversize_(keys, this.maxItems_);
    +    }
    +  }
    +  this.setKeys_(keys);
    +};
    +
    +
    +/**
    + * Remove an item from the data storage.
    + *
    + * @param {string} key The key to remove.
    + * @override
    + */
    +storage.BoundedCollectableStorage.prototype.remove = function(key) {
    +  storage.BoundedCollectableStorage.base(this, 'remove', key);
    +
    +  var keys = this.getKeys_(false);
    +  if (goog.isDef(keys)) {
    +    goog.array.remove(keys, key);
    +    this.setKeys_(keys);
    +  }
    +};
    +
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.html b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.html
    new file mode 100644
    index 0000000000..e17eb0aa71
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.storage.BoundedCollectableStorage
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.storage.BoundedCollectableStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.js b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.js
    new file mode 100644
    index 0000000000..4646be721d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/storage/boundedcollectablestorage_test.js
    @@ -0,0 +1,74 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.storage.BoundedCollectableStorageTest');
    +goog.setTestOnly('goog.labs.storage.BoundedCollectableStorageTest');
    +
    +goog.require('goog.labs.storage.BoundedCollectableStorage');
    +goog.require('goog.storage.collectableStorageTester');
    +goog.require('goog.storage.storage_test');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.storage.FakeMechanism');
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.labs.storage.BoundedCollectableStorage(mechanism, 5);
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +function testExpiredKeyCollection() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.labs.storage.BoundedCollectableStorage(mechanism, 15);
    +
    +  goog.storage.collectableStorageTester.runBasicTests(mechanism, clock,
    +      storage);
    +}
    +
    +function testLimitingNumberOfItems() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.labs.storage.BoundedCollectableStorage(mechanism, 2);
    +
    +  // First item should fit.
    +  storage.set('item-1', 'one', 10000);
    +  clock.tick(100);
    +  assertEquals('one', storage.get('item-1'));
    +
    +  // Second item should fit.
    +  storage.set('item-2', 'two', 10000);
    +  assertEquals('one', storage.get('item-1'));
    +  assertEquals('two', storage.get('item-2'));
    +
    +  // Third item is too much, 'item-1' should be removed.
    +  storage.set('item-3', 'three', 5000);
    +  clock.tick(100);
    +  assertUndefined(storage.get('item-1'));
    +  assertEquals('two', storage.get('item-2'));
    +  assertEquals('three', storage.get('item-3'));
    +
    +  clock.tick(5000);
    +  // 'item-3' item has expired, should be removed instead an older 'item-2'.
    +  storage.set('item-4', 'four', 10000);
    +  assertUndefined(storage.get('item-1'));
    +  assertUndefined(storage.get('item-3'));
    +  assertEquals('two', storage.get('item-2'));
    +  assertEquals('four', storage.get('item-4'));
    +
    +  storage.remove('item-2');
    +  storage.remove('item-4');
    +
    +  clock.uninstall();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/map.js b/mapbox-gl/closure-library/closure/goog/labs/structs/map.js
    new file mode 100644
    index 0000000000..2ea217bd1b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/map.js
    @@ -0,0 +1,348 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A map data structure that offers a convenient API to
    + * manipulate a key, value map. The key must be a string.
    + *
    + * This implementation also ensure that you can use keys that would
    + * not be usable using a normal object literal {}. Some examples
    + * include __proto__ (all newer browsers), toString/hasOwnProperty (IE
    + * <= 8).
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.labs.structs.Map');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.labs.object');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Creates a new map.
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.labs.structs.Map = function() {
    +  // clear() initializes the map to the empty state.
    +  this.clear();
    +};
    +
    +
    +/**
    + * @type {function(this: Object, string): boolean}
    + * @private
    + */
    +goog.labs.structs.Map.objectPropertyIsEnumerable_ =
    +    Object.prototype.propertyIsEnumerable;
    +
    +
    +/**
    + * @type {function(this: Object, string): boolean}
    + * @private
    + */
    +goog.labs.structs.Map.objectHasOwnProperty_ =
    +    Object.prototype.hasOwnProperty;
    +
    +
    +/**
    + * Primary backing store of this map.
    + * @type {!Object}
    + * @private
    + */
    +goog.labs.structs.Map.prototype.map_;
    +
    +
    +/**
    + * Secondary backing store for keys. The index corresponds to the
    + * index for secondaryStoreValues_.
    + * @type {!Array<string>}
    + * @private
    + */
    +goog.labs.structs.Map.prototype.secondaryStoreKeys_;
    +
    +
    +/**
    + * Secondary backing store for keys. The index corresponds to the
    + * index for secondaryStoreValues_.
    + * @type {!Array<*>}
    + * @private
    + */
    +goog.labs.structs.Map.prototype.secondaryStoreValues_;
    +
    +
    +/**
    + * @private {number}
    + */
    +goog.labs.structs.Map.prototype.count_;
    +
    +
    +/**
    + * Adds the (key, value) pair, overriding previous entry with the same
    + * key, if any.
    + * @param {string} key The key.
    + * @param {*} value The value.
    + */
    +goog.labs.structs.Map.prototype.set = function(key, value) {
    +  this.assertKeyIsString_(key);
    +
    +  var newKey = !this.hasKeyInPrimaryStore_(key);
    +  this.map_[key] = value;
    +
    +  // __proto__ is not settable on object.
    +  if (key == '__proto__' ||
    +      // Shadows for built-in properties are not enumerable in IE <= 8 .
    +      (!goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED &&
    +       !goog.labs.structs.Map.objectPropertyIsEnumerable_.call(
    +           this.map_, key))) {
    +    delete this.map_[key];
    +    var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
    +    if ((newKey = index < 0)) {
    +      index = this.secondaryStoreKeys_.length;
    +    }
    +
    +    this.secondaryStoreKeys_[index] = key;
    +    this.secondaryStoreValues_[index] = value;
    +  }
    +
    +  if (newKey) this.count_++;
    +};
    +
    +
    +/**
    + * Gets the value for the given key.
    + * @param {string} key The key whose value we want to retrieve.
    + * @param {*=} opt_default The default value to return if the key does
    + *     not exist in the map, default to undefined.
    + * @return {*} The value corresponding to the given key, or opt_default
    + *     if the key does not exist in this map.
    + */
    +goog.labs.structs.Map.prototype.get = function(key, opt_default) {
    +  this.assertKeyIsString_(key);
    +
    +  if (this.hasKeyInPrimaryStore_(key)) {
    +    return this.map_[key];
    +  }
    +
    +  var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
    +  return index >= 0 ? this.secondaryStoreValues_[index] : opt_default;
    +};
    +
    +
    +/**
    + * Removes the map entry with the given key.
    + * @param {string} key The key to remove.
    + * @return {boolean} True if the entry is removed.
    + */
    +goog.labs.structs.Map.prototype.remove = function(key) {
    +  this.assertKeyIsString_(key);
    +
    +  if (this.hasKeyInPrimaryStore_(key)) {
    +    this.count_--;
    +    delete this.map_[key];
    +    return true;
    +  } else {
    +    var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
    +    if (index >= 0) {
    +      this.count_--;
    +      goog.array.removeAt(this.secondaryStoreKeys_, index);
    +      goog.array.removeAt(this.secondaryStoreValues_, index);
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Adds the content of the map to this map. If a new entry uses a key
    + * that already exists in this map, the existing key is replaced.
    + * @param {!goog.labs.structs.Map} map The map to add.
    + */
    +goog.labs.structs.Map.prototype.addAll = function(map) {
    +  goog.array.forEach(map.getKeys(), function(key) {
    +    this.set(key, map.get(key));
    +  }, this);
    +};
    +
    +
    +/**
    + * @return {boolean} True if the map is empty.
    + */
    +goog.labs.structs.Map.prototype.isEmpty = function() {
    +  return !this.count_;
    +};
    +
    +
    +/**
    + * @return {number} The number of the entries in this map.
    + */
    +goog.labs.structs.Map.prototype.getCount = function() {
    +  return this.count_;
    +};
    +
    +
    +/**
    + * @param {string} key The key to check.
    + * @return {boolean} True if the map contains the given key.
    + */
    +goog.labs.structs.Map.prototype.containsKey = function(key) {
    +  this.assertKeyIsString_(key);
    +  return this.hasKeyInPrimaryStore_(key) ||
    +      goog.array.contains(this.secondaryStoreKeys_, key);
    +};
    +
    +
    +/**
    + * Whether the map contains the given value. The comparison is done
    + * using !== comparator. Also returns true if the passed value is NaN
    + * and a NaN value exists in the map.
    + * @param {*} value Value to check.
    + * @return {boolean} True if the map contains the given value.
    + */
    +goog.labs.structs.Map.prototype.containsValue = function(value) {
    +  var found = goog.object.some(this.map_, function(v, k) {
    +    return this.hasKeyInPrimaryStore_(k) &&
    +        goog.labs.object.is(v, value);
    +  }, this);
    +  return found || goog.array.contains(this.secondaryStoreValues_, value);
    +};
    +
    +
    +/**
    + * @return {!Array<string>} An array of all the keys contained in this map.
    + */
    +goog.labs.structs.Map.prototype.getKeys = function() {
    +  var keys;
    +  if (goog.labs.structs.Map.BrowserFeature.OBJECT_KEYS_SUPPORTED) {
    +    keys = goog.array.clone(Object.keys(this.map_));
    +  } else {
    +    keys = [];
    +    for (var key in this.map_) {
    +      if (goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key)) {
    +        keys.push(key);
    +      }
    +    }
    +  }
    +
    +  goog.array.extend(keys, this.secondaryStoreKeys_);
    +  return keys;
    +};
    +
    +
    +/**
    + * @return {!Array<*>} An array of all the values contained in this map.
    + *     There may be duplicates.
    + */
    +goog.labs.structs.Map.prototype.getValues = function() {
    +  var values = [];
    +  var keys = this.getKeys();
    +  for (var i = 0; i < keys.length; i++) {
    +    values.push(this.get(keys[i]));
    +  }
    +  return values;
    +};
    +
    +
    +/**
    + * @return {!Array<Array<?>>} An array of entries. Each entry is of the
    + *     form [key, value]. Do not rely on consistent ordering of entries.
    + */
    +goog.labs.structs.Map.prototype.getEntries = function() {
    +  var entries = [];
    +  var keys = this.getKeys();
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = keys[i];
    +    entries.push([key, this.get(key)]);
    +  }
    +  return entries;
    +};
    +
    +
    +/**
    + * Clears the map to the initial state.
    + */
    +goog.labs.structs.Map.prototype.clear = function() {
    +  this.map_ = goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED ?
    +      Object.create(null) : {};
    +  this.secondaryStoreKeys_ = [];
    +  this.secondaryStoreValues_ = [];
    +  this.count_ = 0;
    +};
    +
    +
    +/**
    + * Clones this map.
    + * @return {!goog.labs.structs.Map} The clone of this map.
    + */
    +goog.labs.structs.Map.prototype.clone = function() {
    +  var map = new goog.labs.structs.Map();
    +  map.addAll(this);
    +  return map;
    +};
    +
    +
    +/**
    + * @param {string} key The key to check.
    + * @return {boolean} True if the given key has been added successfully
    + *     to the primary store.
    + * @private
    + */
    +goog.labs.structs.Map.prototype.hasKeyInPrimaryStore_ = function(key) {
    +  // New browsers that support Object.create do not allow setting of
    +  // __proto__. In other browsers, hasOwnProperty will return true for
    +  // __proto__ for object created with literal {}, so we need to
    +  // special case it.
    +  if (key == '__proto__') {
    +    return false;
    +  }
    +
    +  if (goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED) {
    +    return key in this.map_;
    +  }
    +
    +  return goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key);
    +};
    +
    +
    +/**
    + * Asserts that the given key is a string.
    + * @param {string} key The key to check.
    + * @private
    + */
    +goog.labs.structs.Map.prototype.assertKeyIsString_ = function(key) {
    +  goog.asserts.assert(goog.isString(key), 'key must be a string.');
    +};
    +
    +
    +/**
    + * Browser feature enum necessary for map.
    + * @enum {boolean}
    + */
    +goog.labs.structs.Map.BrowserFeature = {
    +  // TODO(chrishenry): Replace with goog.userAgent detection.
    +  /**
    +   * Whether Object.create method is supported.
    +   */
    +  OBJECT_CREATE_SUPPORTED: !!Object.create,
    +
    +  /**
    +   * Whether Object.keys method is supported.
    +   */
    +  OBJECT_KEYS_SUPPORTED: !!Object.keys
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/map_perf.js b/mapbox-gl/closure-library/closure/goog/labs/structs/map_perf.js
    new file mode 100644
    index 0000000000..7e7a897596
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/map_perf.js
    @@ -0,0 +1,204 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Performance test for goog.structs.Map and
    + * goog.labs.structs.Map. To run this test fairly, you would have to
    + * compile this via JsCompiler (with --export_test_functions), and
    + * pull the compiled JS into an empty HTML file.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.labs.structs.MapPerf');
    +goog.setTestOnly('goog.labs.structs.MapPerf');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.labs.structs.Map');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.PerformanceTable');
    +goog.require('goog.testing.jsunit');
    +
    +goog.scope(function() {
    +var MapPerf = goog.labs.structs.MapPerf;
    +
    +
    +/**
    + * @typedef {goog.labs.structs.Map|goog.structs.Map}
    + */
    +MapPerf.MapType;
    +
    +
    +/**
    + * @type {goog.testing.PerformanceTable}
    + */
    +MapPerf.perfTable;
    +
    +
    +/**
    + * A key list. This maps loop index to key name to be used during
    + * benchmark. This ensure that we do not need to pay the cost of
    + * string concatenation/GC whenever we derive a key from loop index.
    + *
    + * This is filled once in setUpPage and then remain unchanged for the
    + * rest of the test case.
    + *
    + * @type {!Array<string>}
    + */
    +MapPerf.keyList = [];
    +
    +
    +/**
    + * Maxium number of keys in keyList (and, by extension, the map under
    + * test).
    + * @type {number}
    + */
    +MapPerf.MAX_NUM_KEY = 10000;
    +
    +
    +/**
    + * Fills the given map with generated key-value pair.
    + * @param {MapPerf.MapType} map The map to fill.
    + * @param {number} numKeys The number of key-value pair to fill.
    + */
    +MapPerf.fillMap = function(map, numKeys) {
    +  goog.asserts.assert(numKeys <= MapPerf.MAX_NUM_KEY);
    +
    +  for (var i = 0; i < numKeys; ++i) {
    +    map.set(MapPerf.keyList[i], i);
    +  }
    +};
    +
    +
    +/**
    + * Primes the given map with deletion of keys.
    + * @param {MapPerf.MapType} map The map to prime.
    + * @return {MapPerf.MapType} The primed map (for chaining).
    + */
    +MapPerf.primeMapWithDeletion = function(map) {
    +  for (var i = 0; i < 1000; ++i) {
    +    map.set(MapPerf.keyList[i], i);
    +  }
    +  for (var i = 0; i < 1000; ++i) {
    +    map.remove(MapPerf.keyList[i]);
    +  }
    +  return map;
    +};
    +
    +
    +/**
    + * Runs performance test for Map#get with the given map.
    + * @param {MapPerf.MapType} map The map to stress.
    + * @param {string} message Message to be put in performance table.
    + */
    +MapPerf.runPerformanceTestForMapGet = function(map, message) {
    +  MapPerf.fillMap(map, 10000);
    +
    +  MapPerf.perfTable.run(
    +      function() {
    +        // Creates local alias for map and keyList.
    +        var localMap = map;
    +        var localKeyList = MapPerf.keyList;
    +
    +        for (var i = 0; i < 500; ++i) {
    +          var sum = 0;
    +          for (var j = 0; j < 10000; ++j) {
    +            sum += localMap.get(localKeyList[j]);
    +          }
    +        }
    +      },
    +      message);
    +};
    +
    +
    +/**
    + * Runs performance test for Map#set with the given map.
    + * @param {MapPerf.MapType} map The map to stress.
    + * @param {string} message Message to be put in performance table.
    + */
    +MapPerf.runPerformanceTestForMapSet = function(map, message) {
    +  MapPerf.perfTable.run(
    +      function() {
    +        // Creates local alias for map and keyList.
    +        var localMap = map;
    +        var localKeyList = MapPerf.keyList;
    +
    +        for (var i = 0; i < 500; ++i) {
    +          for (var j = 0; j < 10000; ++j) {
    +            localMap.set(localKeyList[i], i);
    +          }
    +        }
    +      },
    +      message);
    +};
    +
    +
    +goog.global['setUpPage'] = function() {
    +  var content = goog.dom.createDom('div');
    +  goog.dom.insertChildAt(document.body, content, 0);
    +  var ua = navigator.userAgent;
    +  content.innerHTML =
    +      '<h1>Closure Performance Tests - Map</h1>' +
    +      '<p><strong>User-agent: </strong><span id="ua">' + ua + '</span></p>' +
    +      '<div id="perf-table"></div>' +
    +      '<hr>';
    +
    +  MapPerf.perfTable = new goog.testing.PerformanceTable(
    +      goog.dom.getElement('perf-table'));
    +
    +  // Fills keyList.
    +  for (var i = 0; i < MapPerf.MAX_NUM_KEY; ++i) {
    +    MapPerf.keyList.push('k' + i);
    +  }
    +};
    +
    +
    +goog.global['testGetFromLabsMap'] = function() {
    +  MapPerf.runPerformanceTestForMapGet(
    +      new goog.labs.structs.Map(), '#get: no previous deletion (Labs)');
    +};
    +
    +
    +goog.global['testGetFromOriginalMap'] = function() {
    +  MapPerf.runPerformanceTestForMapGet(
    +      new goog.structs.Map(), '#get: no previous deletion (Original)');
    +};
    +
    +
    +goog.global['testGetWithPreviousDeletionFromLabsMap'] = function() {
    +  MapPerf.runPerformanceTestForMapGet(
    +      MapPerf.primeMapWithDeletion(new goog.labs.structs.Map()),
    +      '#get: with previous deletion (Labs)');
    +};
    +
    +
    +goog.global['testGetWithPreviousDeletionFromOriginalMap'] = function() {
    +  MapPerf.runPerformanceTestForMapGet(
    +      MapPerf.primeMapWithDeletion(new goog.structs.Map()),
    +      '#get: with previous deletion (Original)');
    +};
    +
    +
    +goog.global['testSetFromLabsMap'] = function() {
    +  MapPerf.runPerformanceTestForMapSet(
    +      new goog.labs.structs.Map(), '#set: no previous deletion (Labs)');
    +};
    +
    +
    +goog.global['testSetFromOriginalMap'] = function() {
    +  MapPerf.runPerformanceTestForMapSet(
    +      new goog.structs.Map(), '#set: no previous deletion (Original)');
    +};
    +
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.html b/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.html
    new file mode 100644
    index 0000000000..aded3ed2cd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.structs.Map
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.structs.MapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.js b/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.js
    new file mode 100644
    index 0000000000..bf2a61d22b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/map_test.js
    @@ -0,0 +1,432 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.structs.MapTest');
    +goog.setTestOnly('goog.labs.structs.MapTest');
    +
    +goog.require('goog.labs.structs.Map');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var map;
    +var stubs;
    +
    +function setUpPage() {
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function setUp() {
    +  map = new goog.labs.structs.Map();
    +}
    +
    +
    +function testSet() {
    +  var key = 'test';
    +  var value = 'value';
    +  map.set(key, value);
    +  assertEquals(value, map.get(key));
    +}
    +
    +
    +function testSetsWithSameKey() {
    +  var key = 'test';
    +  var value = 'value';
    +  var value2 = 'value2';
    +  map.set(key, value);
    +  map.set(key, value2);
    +  assertEquals(value2, map.get(key));
    +}
    +
    +
    +function testSetWithUndefinedValue() {
    +  var key = 'test';
    +  map.set(key, undefined);
    +  assertUndefined(map.get(key));
    +}
    +
    +
    +function testSetWithUnderUnderProtoUnderUnder() {
    +  var key = '__proto__';
    +  var value = 'value';
    +  var value2 = 'value2';
    +
    +  map.set(key, value);
    +  assertEquals(value, map.get(key));
    +
    +  map.set(key, value2);
    +  assertEquals(value2, map.get(key));
    +}
    +
    +
    +function testSetWithBuiltInPropertyShadows() {
    +  var key = 'toString';
    +  var value = 'value';
    +  var key2 = 'hasOwnProperty';
    +  var value2 = 'value2';
    +
    +  map.set(key, value);
    +  map.set(key2, value2);
    +  assertEquals(value, map.get(key));
    +  assertEquals(value2, map.get(key2));
    +
    +  map.set(key, value2);
    +  map.set(key2, value);
    +  assertEquals(value2, map.get(key));
    +  assertEquals(value, map.get(key2));
    +}
    +
    +
    +function testGetBeforeSetOfUnderUnderProtoUnderUnder() {
    +  assertUndefined(map.get('__proto__'));
    +}
    +
    +
    +function testContainsKey() {
    +  assertFalse(map.containsKey('key'));
    +  assertFalse(map.containsKey('__proto__'));
    +  assertFalse(map.containsKey('toString'));
    +  assertFalse(map.containsKey('hasOwnProperty'));
    +  assertFalse(map.containsKey('key2'));
    +  assertFalse(map.containsKey('key3'));
    +  assertFalse(map.containsKey('key4'));
    +
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v');
    +  map.set('toString', 'v');
    +  map.set('hasOwnProperty', 'v');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('key4', '');
    +
    +  assertTrue(map.containsKey('key'));
    +  assertTrue(map.containsKey('__proto__'));
    +  assertTrue(map.containsKey('toString'));
    +  assertTrue(map.containsKey('hasOwnProperty'));
    +  assertTrue(map.containsKey('key2'));
    +  assertTrue(map.containsKey('key3'));
    +  assertTrue(map.containsKey('key4'));
    +}
    +
    +
    +function testContainsValueWithShadowKeys() {
    +  assertFalse(map.containsValue('v2'));
    +  assertFalse(map.containsValue('v3'));
    +  assertFalse(map.containsValue('v4'));
    +
    +  map.set('__proto__', 'v2');
    +  map.set('toString', 'v3');
    +  map.set('hasOwnProperty', 'v4');
    +
    +  assertTrue(map.containsValue('v2'));
    +  assertTrue(map.containsValue('v3'));
    +  assertTrue(map.containsValue('v4'));
    +
    +  assertFalse(map.containsValue(Object.prototype.toString));
    +  assertFalse(map.containsValue(Object.prototype.hasOwnProperty));
    +}
    +
    +
    +function testContainsValueWithNullAndUndefined() {
    +  assertFalse(map.containsValue(undefined));
    +  assertFalse(map.containsValue(null));
    +
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +
    +  assertTrue(map.containsValue(undefined));
    +  assertTrue(map.containsValue(null));
    +}
    +
    +
    +function testContainsValueWithNumber() {
    +  assertFalse(map.containsValue(-1));
    +  assertFalse(map.containsValue(0));
    +  assertFalse(map.containsValue(1));
    +  map.set('key', -1);
    +  map.set('key2', 0);
    +  map.set('key3', 1);
    +  assertTrue(map.containsValue(-1));
    +  assertTrue(map.containsValue(0));
    +  assertTrue(map.containsValue(1));
    +}
    +
    +
    +function testContainsValueWithNaN() {
    +  assertFalse(map.containsValue(NaN));
    +  map.set('key', NaN);
    +  assertTrue(map.containsValue(NaN));
    +}
    +
    +
    +function testContainsValueWithNegativeZero() {
    +  assertFalse(map.containsValue(-0));
    +  map.set('key', -0);
    +  assertTrue(map.containsValue(-0));
    +  assertFalse(map.containsValue(0));
    +
    +  map.set('key', 0);
    +  assertFalse(map.containsValue(-0));
    +  assertTrue(map.containsValue(0));
    +}
    +
    +
    +function testContainsValueWithStrings() {
    +  assertFalse(map.containsValue(''));
    +  assertFalse(map.containsValue('v'));
    +  map.set('key', '');
    +  map.set('key2', 'v');
    +  assertTrue(map.containsValue(''));
    +  assertTrue(map.containsValue('v'));
    +}
    +
    +function testRemove() {
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v2');
    +  map.set('toString', 'v3');
    +  map.set('hasOwnProperty', 'v4');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('key4', '');
    +
    +  assertFalse(map.remove('key do not exist'));
    +
    +  assertTrue(map.remove('key'));
    +  assertFalse(map.containsKey('key'));
    +  assertFalse(map.remove('key'));
    +
    +  assertTrue(map.remove('__proto__'));
    +  assertFalse(map.containsKey('__proto__'));
    +  assertFalse(map.remove('__proto__'));
    +
    +  assertTrue(map.remove('toString'));
    +  assertFalse(map.containsKey('toString'));
    +  assertFalse(map.remove('toString'));
    +
    +  assertTrue(map.remove('hasOwnProperty'));
    +  assertFalse(map.containsKey('hasOwnProperty'));
    +  assertFalse(map.remove('hasOwnProperty'));
    +
    +  assertTrue(map.remove('key2'));
    +  assertFalse(map.containsKey('key2'));
    +  assertFalse(map.remove('key2'));
    +
    +  assertTrue(map.remove('key3'));
    +  assertFalse(map.containsKey('key3'));
    +  assertFalse(map.remove('key3'));
    +
    +  assertTrue('', map.remove('key4'));
    +  assertFalse(map.containsKey('key4'));
    +  assertFalse(map.remove('key4'));
    +}
    +
    +
    +function testGetCountAndIsEmpty() {
    +  assertEquals(0, map.getCount());
    +  assertTrue(map.isEmpty());
    +
    +  map.set('key', 'v');
    +  assertEquals(1, map.getCount());
    +  map.set('__proto__', 'v2');
    +  assertEquals(2, map.getCount());
    +  map.set('toString', 'v3');
    +  assertEquals(3, map.getCount());
    +  map.set('hasOwnProperty', 'v4');
    +  assertEquals(4, map.getCount());
    +
    +  map.set('key', 'a');
    +  assertEquals(4, map.getCount());
    +  map.set('__proto__', 'a2');
    +  assertEquals(4, map.getCount());
    +  map.set('toString', 'a3');
    +  assertEquals(4, map.getCount());
    +  map.set('hasOwnProperty', 'a4');
    +  assertEquals(4, map.getCount());
    +
    +  map.remove('key');
    +  assertEquals(3, map.getCount());
    +  map.remove('__proto__');
    +  assertEquals(2, map.getCount());
    +  map.remove('toString');
    +  assertEquals(1, map.getCount());
    +  map.remove('hasOwnProperty');
    +  assertEquals(0, map.getCount());
    +}
    +
    +
    +function testClear() {
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v');
    +  map.set('toString', 'v');
    +  map.set('hasOwnProperty', 'v');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('key4', '');
    +
    +  map.clear();
    +
    +  assertFalse(map.containsKey('key'));
    +  assertFalse(map.containsKey('__proto__'));
    +  assertFalse(map.containsKey('toString'));
    +  assertFalse(map.containsKey('hasOwnProperty'));
    +  assertFalse(map.containsKey('key2'));
    +  assertFalse(map.containsKey('key3'));
    +  assertFalse(map.containsKey('key4'));
    +}
    +
    +
    +function testGetEntries() {
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v');
    +  map.set('toString', 'v');
    +  map.set('hasOwnProperty', 'v');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('key4', '');
    +
    +  var entries = map.getEntries();
    +  assertEquals(7, entries.length);
    +  assertContainsEntry(['key', 'v'], entries);
    +  assertContainsEntry(['__proto__', 'v'], entries);
    +  assertContainsEntry(['toString', 'v'], entries);
    +  assertContainsEntry(['hasOwnProperty', 'v'], entries);
    +  assertContainsEntry(['key2', undefined], entries);
    +  assertContainsEntry(['key3', null], entries);
    +  assertContainsEntry(['key4', ''], entries);
    +}
    +
    +
    +function testGetKeys() {
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v');
    +  map.set('toString', 'v');
    +  map.set('hasOwnProperty', 'v');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('k4', '');
    +
    +  var values = map.getKeys();
    +  assertSameElements(
    +      ['key', '__proto__', 'toString', 'hasOwnProperty', 'key2', 'key3', 'k4'],
    +      values);
    +}
    +
    +
    +function testGetValues() {
    +  map.set('key', 'v');
    +  map.set('__proto__', 'v');
    +  map.set('toString', 'v');
    +  map.set('hasOwnProperty', 'v');
    +  map.set('key2', undefined);
    +  map.set('key3', null);
    +  map.set('key4', '');
    +
    +  var values = map.getValues();
    +  assertSameElements(['v', 'v', 'v', 'v', undefined, null, ''], values);
    +}
    +
    +
    +function testAddAllToEmptyMap() {
    +  map.set('key', 'v');
    +  map.set('key2', 'v2');
    +  map.set('key3', 'v3');
    +  map.set('key4', 'v4');
    +
    +  var map2 = new goog.labs.structs.Map();
    +  map2.addAll(map);
    +
    +  assertEquals(4, map2.getCount());
    +  assertEquals('v', map2.get('key'));
    +  assertEquals('v2', map2.get('key2'));
    +  assertEquals('v3', map2.get('key3'));
    +  assertEquals('v4', map2.get('key4'));
    +}
    +
    +
    +function testAddAllToNonEmptyMap() {
    +  map.set('key', 'v');
    +  map.set('key2', 'v2');
    +  map.set('key3', 'v3');
    +  map.set('key4', 'v4');
    +
    +  var map2 = new goog.labs.structs.Map();
    +  map2.set('key0', 'o');
    +  map2.set('key', 'o');
    +  map2.set('key2', 'o2');
    +  map2.set('key3', 'o3');
    +  map2.addAll(map);
    +
    +  assertEquals(5, map2.getCount());
    +  assertEquals('o', map2.get('key0'));
    +  assertEquals('v', map2.get('key'));
    +  assertEquals('v2', map2.get('key2'));
    +  assertEquals('v3', map2.get('key3'));
    +  assertEquals('v4', map2.get('key4'));
    +}
    +
    +
    +function testClone() {
    +  map.set('key', 'v');
    +  map.set('key2', 'v2');
    +  map.set('key3', 'v3');
    +  map.set('key4', 'v4');
    +
    +  var map2 = map.clone();
    +
    +  assertEquals(4, map2.getCount());
    +  assertEquals('v', map2.get('key'));
    +  assertEquals('v2', map2.get('key2'));
    +  assertEquals('v3', map2.get('key3'));
    +  assertEquals('v4', map2.get('key4'));
    +}
    +
    +
    +function testMapWithModifiedObjectPrototype() {
    +  stubs.set(Object.prototype, 'toString', function() {});
    +  stubs.set(Object.prototype, 'foo', function() {});
    +  stubs.set(Object.prototype, 'field', 100);
    +  stubs.set(Object.prototype, 'fooKey', function() {});
    +
    +  map = new goog.labs.structs.Map();
    +  map.set('key', 'v');
    +  map.set('key2', 'v2');
    +  map.set('fooKey', 'v3');
    +
    +  assertTrue(map.containsKey('key'));
    +  assertTrue(map.containsKey('key2'));
    +  assertTrue(map.containsKey('fooKey'));
    +  assertFalse(map.containsKey('toString'));
    +  assertFalse(map.containsKey('foo'));
    +  assertFalse(map.containsKey('field'));
    +
    +  assertTrue(map.containsValue('v'));
    +  assertTrue(map.containsValue('v2'));
    +  assertTrue(map.containsValue('v3'));
    +  assertFalse(map.containsValue(100));
    +
    +  var entries = map.getEntries();
    +  assertEquals(3, entries.length);
    +  assertContainsEntry(['key', 'v'], entries);
    +  assertContainsEntry(['key2', 'v2'], entries);
    +  assertContainsEntry(['fooKey', 'v3'], entries);
    +}
    +
    +
    +function assertContainsEntry(entry, entryList) {
    +  for (var i = 0; i < entryList.length; ++i) {
    +    if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
    +      return;
    +    }
    +  }
    +  fail('Did not find entry: ' + entry + ' in: ' + entryList);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/multimap.js b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap.js
    new file mode 100644
    index 0000000000..ff73f431e1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap.js
    @@ -0,0 +1,282 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A collection similar to
    + * {@code goog.labs.structs.Map}, but also allows associating multiple
    + * values with a single key.
    + *
    + * This implementation ensures that you can use any string keys.
    + *
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.labs.structs.Multimap');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.object');
    +goog.require('goog.labs.structs.Map');
    +
    +
    +
    +/**
    + * Creates a new multimap.
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.labs.structs.Multimap = function() {
    +  this.clear();
    +};
    +
    +
    +/**
    + * The backing map.
    + * @type {!goog.labs.structs.Map}
    + * @private
    + */
    +goog.labs.structs.Multimap.prototype.map_;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.labs.structs.Multimap.prototype.count_ = 0;
    +
    +
    +/**
    + * Clears the multimap.
    + */
    +goog.labs.structs.Multimap.prototype.clear = function() {
    +  this.count_ = 0;
    +  this.map_ = new goog.labs.structs.Map();
    +};
    +
    +
    +/**
    + * Clones this multimap.
    + * @return {!goog.labs.structs.Multimap} A multimap that contains all
    + *     the mapping this multimap has.
    + */
    +goog.labs.structs.Multimap.prototype.clone = function() {
    +  var map = new goog.labs.structs.Multimap();
    +  map.addAllFromMultimap(this);
    +  return map;
    +};
    +
    +
    +/**
    + * Adds the given (key, value) pair to the map. The (key, value) pair
    + * is guaranteed to be added.
    + * @param {string} key The key to add.
    + * @param {*} value The value to add.
    + */
    +goog.labs.structs.Multimap.prototype.add = function(key, value) {
    +  var values = this.map_.get(key);
    +  if (!values) {
    +    this.map_.set(key, (values = []));
    +  }
    +
    +  values.push(value);
    +  this.count_++;
    +};
    +
    +
    +/**
    + * Stores a collection of values to the given key. Does not replace
    + * existing (key, value) pairs.
    + * @param {string} key The key to add.
    + * @param {!Array<*>} values The values to add.
    + */
    +goog.labs.structs.Multimap.prototype.addAllValues = function(key, values) {
    +  goog.array.forEach(values, function(v) {
    +    this.add(key, v);
    +  }, this);
    +};
    +
    +
    +/**
    + * Adds the contents of the given map/multimap to this multimap.
    + * @param {!(goog.labs.structs.Map|goog.labs.structs.Multimap)} map The
    + *     map to add.
    + */
    +goog.labs.structs.Multimap.prototype.addAllFromMultimap = function(map) {
    +  goog.array.forEach(map.getEntries(), function(entry) {
    +    this.add(entry[0], entry[1]);
    +  }, this);
    +};
    +
    +
    +/**
    + * Replaces all the values for the given key with the given values.
    + * @param {string} key The key whose values are to be replaced.
    + * @param {!Array<*>} values The new values. If empty, this is
    + *     equivalent to {@code removaAll(key)}.
    + */
    +goog.labs.structs.Multimap.prototype.replaceValues = function(key, values) {
    +  this.removeAll(key);
    +  this.addAllValues(key, values);
    +};
    +
    +
    +/**
    + * Gets the values correspond to the given key.
    + * @param {string} key The key to retrieve.
    + * @return {!Array<*>} An array of values corresponding to the given
    + *     key. May be empty. Note that the ordering of values are not
    + *     guaranteed to be consistent.
    + */
    +goog.labs.structs.Multimap.prototype.get = function(key) {
    +  var values = /** @type {Array<*>} */ (this.map_.get(key));
    +  return values ? goog.array.clone(values) : [];
    +};
    +
    +
    +/**
    + * Removes a single occurrence of (key, value) pair.
    + * @param {string} key The key to remove.
    + * @param {*} value The value to remove.
    + * @return {boolean} Whether any matching (key, value) pair is removed.
    + */
    +goog.labs.structs.Multimap.prototype.remove = function(key, value) {
    +  var values = /** @type {Array<*>} */ (this.map_.get(key));
    +  if (!values) {
    +    return false;
    +  }
    +
    +  var removed = goog.array.removeIf(values, function(v) {
    +    return goog.labs.object.is(value, v);
    +  });
    +
    +  if (removed) {
    +    this.count_--;
    +    if (values.length == 0) {
    +      this.map_.remove(key);
    +    }
    +  }
    +  return removed;
    +};
    +
    +
    +/**
    + * Removes all values corresponding to the given key.
    + * @param {string} key The key whose values are to be removed.
    + * @return {boolean} Whether any value is removed.
    + */
    +goog.labs.structs.Multimap.prototype.removeAll = function(key) {
    +  // We have to first retrieve the values from the backing map because
    +  // we need to keep track of count (and correctly calculates the
    +  // return value). values may be undefined.
    +  var values = this.map_.get(key);
    +  if (this.map_.remove(key)) {
    +    this.count_ -= values.length;
    +    return true;
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the multimap is empty.
    + */
    +goog.labs.structs.Multimap.prototype.isEmpty = function() {
    +  return !this.count_;
    +};
    +
    +
    +/**
    + * @return {number} The count of (key, value) pairs in the map.
    + */
    +goog.labs.structs.Multimap.prototype.getCount = function() {
    +  return this.count_;
    +};
    +
    +
    +/**
    + * @param {string} key The key to check.
    + * @param {*} value The value to check.
    + * @return {boolean} Whether the (key, value) pair exists in the multimap.
    + */
    +goog.labs.structs.Multimap.prototype.containsEntry = function(key, value) {
    +  var values = /** @type {Array<*>} */ (this.map_.get(key));
    +  if (!values) {
    +    return false;
    +  }
    +
    +  var index = goog.array.findIndex(values, function(v) {
    +    return goog.labs.object.is(v, value);
    +  });
    +  return index >= 0;
    +};
    +
    +
    +/**
    + * @param {string} key The key to check.
    + * @return {boolean} Whether the multimap contains at least one (key,
    + *     value) pair with the given key.
    + */
    +goog.labs.structs.Multimap.prototype.containsKey = function(key) {
    +  return this.map_.containsKey(key);
    +};
    +
    +
    +/**
    + * @param {*} value The value to check.
    + * @return {boolean} Whether the multimap contains at least one (key,
    + *     value) pair with the given value.
    + */
    +goog.labs.structs.Multimap.prototype.containsValue = function(value) {
    +  return goog.array.some(this.map_.getValues(),
    +      function(values) {
    +        return goog.array.some(/** @type {Array<?>} */ (values), function(v) {
    +          return goog.labs.object.is(v, value);
    +        });
    +      });
    +};
    +
    +
    +/**
    + * @return {!Array<string>} An array of unique keys.
    + */
    +goog.labs.structs.Multimap.prototype.getKeys = function() {
    +  return this.map_.getKeys();
    +};
    +
    +
    +/**
    + * @return {!Array<*>} An array of values. There may be duplicates.
    + */
    +goog.labs.structs.Multimap.prototype.getValues = function() {
    +  return goog.array.flatten(this.map_.getValues());
    +};
    +
    +
    +/**
    + * @return {!Array<!Array<?>>} An array of entries. Each entry is of the
    + *     form [key, value].
    + */
    +goog.labs.structs.Multimap.prototype.getEntries = function() {
    +  var keys = this.getKeys();
    +  var entries = [];
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = keys[i];
    +    var values = this.get(key);
    +    for (var j = 0; j < values.length; j++) {
    +      entries.push([key, values[j]]);
    +    }
    +  }
    +  return entries;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.html b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.html
    new file mode 100644
    index 0000000000..cb483fabaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.labs.structs.Multimap
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.structs.MultimapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.js b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.js
    new file mode 100644
    index 0000000000..085ccacc13
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/structs/multimap_test.js
    @@ -0,0 +1,328 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.structs.MultimapTest');
    +goog.setTestOnly('goog.labs.structs.MultimapTest');
    +
    +goog.require('goog.labs.structs.Map');
    +goog.require('goog.labs.structs.Multimap');
    +goog.require('goog.testing.jsunit');
    +
    +var map;
    +
    +
    +function setUp() {
    +  map = new goog.labs.structs.Multimap();
    +}
    +
    +
    +function testGetCountWithEmptyMultimap() {
    +  assertEquals(0, map.getCount());
    +  assertTrue(map.isEmpty());
    +}
    +
    +
    +function testClone() {
    +  map.add('k', 'v');
    +  map.addAllValues('k2', ['v', 'v1', 'v2']);
    +
    +  var map2 = map.clone();
    +
    +  assertSameElements(['v'], map.get('k'));
    +  assertSameElements(['v', 'v1', 'v2'], map.get('k2'));
    +}
    +
    +
    +function testAdd() {
    +  map.add('key', 'v');
    +  assertEquals(1, map.getCount());
    +  map.add('key', 'v2');
    +  assertEquals(2, map.getCount());
    +  map.add('key', 'v3');
    +  assertEquals(3, map.getCount());
    +
    +  var values = map.get('key');
    +  assertEquals(3, values.length);
    +  assertContains('v', values);
    +  assertContains('v2', values);
    +  assertContains('v3', values);
    +}
    +
    +
    +function testAddValues() {
    +  map.addAllValues('key', ['v', 'v2', 'v3']);
    +  assertSameElements(['v', 'v2', 'v3'], map.get('key'));
    +
    +  map.add('key2', 'a');
    +  map.addAllValues('key2', ['v', 'v2', 'v3']);
    +  assertSameElements(['a', 'v', 'v2', 'v3'], map.get('key2'));
    +}
    +
    +
    +function testAddAllWithMultimap() {
    +  map.add('k', 'v');
    +  map.addAllValues('k2', ['v', 'v1', 'v2']);
    +
    +  var map2 = new goog.labs.structs.Multimap();
    +  map2.add('k2', 'v');
    +  map2.addAllValues('k3', ['a', 'a1', 'a2']);
    +
    +  map.addAllFromMultimap(map2);
    +  assertSameElements(['v'], map.get('k'));
    +  assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
    +  assertSameElements(['a', 'a1', 'a2'], map.get('k3'));
    +}
    +
    +
    +function testAddAllWithMap() {
    +  map.add('k', 'v');
    +  map.addAllValues('k2', ['v', 'v1', 'v2']);
    +
    +  var map2 = new goog.labs.structs.Map();
    +  map2.set('k2', 'v');
    +  map2.set('k3', 'a');
    +
    +  map.addAllFromMultimap(map2);
    +  assertSameElements(['v'], map.get('k'));
    +  assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
    +  assertSameElements(['a'], map.get('k3'));
    +}
    +
    +
    +function testReplaceValues() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +
    +  map.replaceValues('key', [0, 1, 2]);
    +  assertSameElements([0, 1, 2], map.get('key'));
    +  assertEquals(3, map.getCount());
    +
    +  map.replaceValues('key', ['v']);
    +  assertSameElements(['v'], map.get('key'));
    +  assertEquals(1, map.getCount());
    +
    +  map.replaceValues('key', []);
    +  assertSameElements([], map.get('key'));
    +  assertEquals(0, map.getCount());
    +}
    +
    +
    +function testRemove() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key', 'v3');
    +
    +  assertTrue(map.remove('key', 'v'));
    +  var values = map.get('key');
    +  assertEquals(2, map.getCount());
    +  assertEquals(2, values.length);
    +  assertContains('v2', values);
    +  assertContains('v3', values);
    +  assertFalse(map.remove('key', 'v'));
    +
    +  assertTrue(map.remove('key', 'v2'));
    +  values = map.get('key');
    +  assertEquals(1, map.getCount());
    +  assertEquals(1, values.length);
    +  assertContains('v3', values);
    +  assertFalse(map.remove('key', 'v2'));
    +
    +  assertTrue(map.remove('key', 'v3'));
    +  map.remove('key', 'v3');
    +  assertTrue(map.isEmpty());
    +  assertEquals(0, map.get('key').length);
    +  assertFalse(map.remove('key', 'v2'));
    +}
    +
    +
    +function testRemoveWithNaN() {
    +  map.add('key', NaN);
    +  map.add('key', NaN);
    +
    +  assertTrue(map.remove('key', NaN));
    +  var values = map.get('key');
    +  assertEquals(1, values.length);
    +  assertTrue(isNaN(values[0]));
    +
    +  assertTrue(map.remove('key', NaN));
    +  assertEquals(0, map.get('key').length);
    +  assertFalse(map.remove('key', NaN));
    +}
    +
    +
    +function testRemoveWithNegativeZero() {
    +  map.add('key', 0);
    +  map.add('key', -0);
    +
    +  assertTrue(map.remove('key', -0));
    +  var values = map.get('key');
    +  assertEquals(1, values.length);
    +  assertTrue(1 / values[0] === 1 / 0);
    +  assertFalse(map.remove('key', -0));
    +
    +  map.add('key', -0);
    +
    +  assertTrue(map.remove('key', 0));
    +  var values = map.get('key');
    +  assertEquals(1, values.length);
    +  assertTrue(1 / values[0] === 1 / -0);
    +  assertFalse(map.remove('key', 0));
    +
    +  assertTrue(map.remove('key', -0));
    +  assertEquals(0, map.get('key').length);
    +}
    +
    +
    +function testRemoveAll() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key', 'v3');
    +  map.add('key', 'v4');
    +  map.add('key2', 'v');
    +
    +  assertTrue(map.removeAll('key'));
    +  assertSameElements([], map.get('key'));
    +  assertSameElements(['v'], map.get('key2'));
    +  assertFalse(map.removeAll('key'));
    +  assertEquals(1, map.getCount());
    +
    +  assertTrue(map.removeAll('key2'));
    +  assertSameElements([], map.get('key2'));
    +  assertFalse(map.removeAll('key2'));
    +  assertTrue(map.isEmpty());
    +}
    +
    +
    +function testAddWithDuplicateValue() {
    +  map.add('key', 'v');
    +  map.add('key', 'v');
    +  map.add('key', 'v');
    +  assertArrayEquals(['v', 'v', 'v'], map.get('key'));
    +}
    +
    +
    +function testContainsEntry() {
    +  assertFalse(map.containsEntry('k', 'v'));
    +  assertFalse(map.containsEntry('k', 'v2'));
    +  assertFalse(map.containsEntry('k2', 'v'));
    +
    +  map.add('k', 'v');
    +  assertTrue(map.containsEntry('k', 'v'));
    +  assertFalse(map.containsEntry('k', 'v2'));
    +  assertFalse(map.containsEntry('k2', 'v'));
    +
    +  map.add('k', 'v2');
    +  assertTrue(map.containsEntry('k', 'v'));
    +  assertTrue(map.containsEntry('k', 'v2'));
    +  assertFalse(map.containsEntry('k2', 'v'));
    +
    +  map.add('k2', 'v');
    +  assertTrue(map.containsEntry('k', 'v'));
    +  assertTrue(map.containsEntry('k', 'v2'));
    +  assertTrue(map.containsEntry('k2', 'v'));
    +}
    +
    +
    +function testContainsKey() {
    +  assertFalse(map.containsKey('k'));
    +  assertFalse(map.containsKey('k2'));
    +
    +  map.add('k', 'v');
    +  assertTrue(map.containsKey('k'));
    +  map.add('k2', 'v');
    +  assertTrue(map.containsKey('k2'));
    +
    +  map.remove('k', 'v');
    +  assertFalse(map.containsKey('k'));
    +  map.remove('k2', 'v');
    +  assertFalse(map.containsKey('k2'));
    +}
    +
    +
    +function testContainsValue() {
    +  assertFalse(map.containsValue('v'));
    +  assertFalse(map.containsValue('v2'));
    +
    +  map.add('key', 'v');
    +  assertTrue(map.containsValue('v'));
    +  map.add('key', 'v2');
    +  assertTrue(map.containsValue('v2'));
    +}
    +
    +
    +function testGetEntries() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key2', 'v3');
    +
    +  var entries = map.getEntries();
    +  assertEquals(3, entries.length);
    +  assertContainsEntry(['key', 'v'], entries);
    +  assertContainsEntry(['key', 'v2'], entries);
    +  assertContainsEntry(['key2', 'v3'], entries);
    +}
    +
    +
    +function testGetKeys() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key2', 'v3');
    +  map.add('key3', 'v4');
    +  map.removeAll('key3');
    +
    +  assertSameElements(['key', 'key2'], map.getKeys());
    +}
    +
    +
    +function testGetKeys() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key2', 'v2');
    +  map.add('key3', 'v4');
    +  map.removeAll('key3');
    +
    +  assertSameElements(['v', 'v2', 'v2'], map.getValues());
    +}
    +
    +
    +function testGetReturnsDefensiveCopyOfUnderlyingData() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key', 'v3');
    +
    +  var values = map.get('key');
    +  values.push('v4');
    +  assertFalse(map.containsEntry('key', 'v4'));
    +}
    +
    +
    +function testClear() {
    +  map.add('key', 'v');
    +  map.add('key', 'v2');
    +  map.add('key2', 'v3');
    +
    +  map.clear();
    +  assertTrue(map.isEmpty());
    +  assertSameElements([], map.getEntries());
    +}
    +
    +
    +function assertContainsEntry(entry, entryList) {
    +  for (var i = 0; i < entryList.length; ++i) {
    +    if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
    +      return;
    +    }
    +  }
    +  fail('Did not find entry: ' + entry + ' in: ' + entryList);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor.js b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor.js
    new file mode 100644
    index 0000000000..bc38b237c5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor.js
    @@ -0,0 +1,179 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility class that monitors pixel density ratio changes.
    + *
    + * @see ../demos/pixeldensitymonitor.html
    + */
    +
    +goog.provide('goog.labs.style.PixelDensityMonitor');
    +goog.provide('goog.labs.style.PixelDensityMonitor.Density');
    +goog.provide('goog.labs.style.PixelDensityMonitor.EventType');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +
    +
    +
    +/**
    + * Monitors the window for changes to the ratio between device and screen
    + * pixels, e.g. when the user moves the window from a high density screen to a
    + * screen with normal density. Dispatches
    + * goog.labs.style.PixelDensityMonitor.EventType.CHANGE events when the density
    + * changes between the two predefined values NORMAL and HIGH.
    + *
    + * This class uses the window.devicePixelRatio value which is supported in
    + * WebKit and FF18. If the value does not exist, it will always return a
    + * NORMAL density. It requires support for MediaQueryList to detect changes to
    + * the devicePixelRatio.
    + *
    + * @param {!goog.dom.DomHelper=} opt_domHelper The DomHelper which contains the
    + *     document associated with the window to listen to. Defaults to the one in
    + *     which this code is executing.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.labs.style.PixelDensityMonitor = function(opt_domHelper) {
    +  goog.labs.style.PixelDensityMonitor.base(this, 'constructor');
    +
    +  /**
    +   * @type {Window}
    +   * @private
    +   */
    +  this.window_ = opt_domHelper ? opt_domHelper.getWindow() : window;
    +
    +  /**
    +   * The last density that was reported so that changes can be detected.
    +   * @type {goog.labs.style.PixelDensityMonitor.Density}
    +   * @private
    +   */
    +  this.lastDensity_ = this.getDensity();
    +
    +  /**
    +   * @type {function (MediaQueryList)}
    +   * @private
    +   */
    +  this.listener_ = goog.bind(this.handleMediaQueryChange_, this);
    +
    +  /**
    +   * The media query list for a query that detects high density, if supported
    +   * by the browser. Because matchMedia returns a new object for every call, it
    +   * needs to be saved here so the listener can be removed when disposing.
    +   * @type {?MediaQueryList}
    +   * @private
    +   */
    +  this.mediaQueryList_ = this.window_.matchMedia ? this.window_.matchMedia(
    +      goog.labs.style.PixelDensityMonitor.HIGH_DENSITY_QUERY_) : null;
    +};
    +goog.inherits(goog.labs.style.PixelDensityMonitor, goog.events.EventTarget);
    +
    +
    +/**
    + * The two different pixel density modes on which the various ratios between
    + * physical and device pixels are mapped.
    + * @enum {number}
    + */
    +goog.labs.style.PixelDensityMonitor.Density = {
    +  /**
    +   * Mode for older portable devices and desktop screens, defined as having a
    +   * device pixel ratio of less than 1.5.
    +   */
    +  NORMAL: 1,
    +
    +  /**
    +   * Mode for newer portable devices with a high resolution screen, defined as
    +   * having a device pixel ratio of more than 1.5.
    +   */
    +  HIGH: 2
    +};
    +
    +
    +/**
    + * The events fired by the PixelDensityMonitor.
    + * @enum {string}
    + */
    +goog.labs.style.PixelDensityMonitor.EventType = {
    +  /**
    +   * Dispatched when density changes between NORMAL and HIGH.
    +   */
    +  CHANGE: goog.events.getUniqueId('change')
    +};
    +
    +
    +/**
    + * Minimum ratio between device and screen pixel needed for high density mode.
    + * @type {number}
    + * @private
    + */
    +goog.labs.style.PixelDensityMonitor.HIGH_DENSITY_RATIO_ = 1.5;
    +
    +
    +/**
    + * Media query that matches for high density.
    + * @type {string}
    + * @private
    + */
    +goog.labs.style.PixelDensityMonitor.HIGH_DENSITY_QUERY_ =
    +    '(min-resolution: 1.5dppx), (-webkit-min-device-pixel-ratio: 1.5)';
    +
    +
    +/**
    + * Starts monitoring for changes in pixel density.
    + */
    +goog.labs.style.PixelDensityMonitor.prototype.start = function() {
    +  if (this.mediaQueryList_) {
    +    this.mediaQueryList_.addListener(this.listener_);
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.labs.style.PixelDensityMonitor.Density} The density for the
    + *     window.
    + */
    +goog.labs.style.PixelDensityMonitor.prototype.getDensity = function() {
    +  if (this.window_.devicePixelRatio >=
    +      goog.labs.style.PixelDensityMonitor.HIGH_DENSITY_RATIO_) {
    +    return goog.labs.style.PixelDensityMonitor.Density.HIGH;
    +  } else {
    +    return goog.labs.style.PixelDensityMonitor.Density.NORMAL;
    +  }
    +};
    +
    +
    +/**
    + * Handles a change to the media query and checks whether the density has
    + * changed since the last call.
    + * @param {MediaQueryList} mql The list of changed media queries.
    + * @private
    + */
    +goog.labs.style.PixelDensityMonitor.prototype.handleMediaQueryChange_ =
    +    function(mql) {
    +  var newDensity = this.getDensity();
    +  if (this.lastDensity_ != newDensity) {
    +    this.lastDensity_ = newDensity;
    +    this.dispatchEvent(goog.labs.style.PixelDensityMonitor.EventType.CHANGE);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.labs.style.PixelDensityMonitor.prototype.disposeInternal = function() {
    +  if (this.mediaQueryList_) {
    +    this.mediaQueryList_.removeListener(this.listener_);
    +  }
    +  goog.labs.style.PixelDensityMonitor.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.html b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.html
    new file mode 100644
    index 0000000000..0eb8d9e5b0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.html
    @@ -0,0 +1,17 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Tests for goog.labs.style.PixelDensityMonitor</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.labs.style.PixelDensityMonitorTest');
    +</script>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.js b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.js
    new file mode 100644
    index 0000000000..a3b1f209fa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/style/pixeldensitymonitor_test.js
    @@ -0,0 +1,146 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests for goog.labs.style.PixelDensityMonitor.
    + *
    + */
    +
    +goog.provide('goog.labs.style.PixelDensityMonitorTest');
    +goog.setTestOnly('goog.labs.style.PixelDensityMonitorTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.events');
    +goog.require('goog.labs.style.PixelDensityMonitor');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var fakeWindow;
    +var recordFunction;
    +var monitor;
    +var mockControl;
    +var mediaQueryLists;
    +
    +function setUp() {
    +  recordFunction = goog.testing.recordFunction();
    +  mediaQueryLists = [];
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function tearDown() {
    +  mockControl.$verifyAll();
    +  goog.dispose(monitor);
    +  goog.dispose(recordFunction);
    +}
    +
    +function setUpMonitor(initialRatio, hasMatchMedia) {
    +  fakeWindow = {
    +    devicePixelRatio: initialRatio
    +  };
    +
    +  if (hasMatchMedia) {
    +    // Every call to matchMedia should return a new media query list with its
    +    // own set of listeners.
    +    fakeWindow.matchMedia = function(query) {
    +      var listeners = [];
    +      var newList = {
    +        addListener: function(listener) {
    +          listeners.push(listener);
    +        },
    +        removeListener: function(listener) {
    +          goog.array.remove(listeners, listener);
    +        },
    +        callListeners: function() {
    +          for (var i = 0; i < listeners.length; i++) {
    +            listeners[i]();
    +          }
    +        },
    +        getListenerCount: function() {
    +          return listeners.length;
    +        }
    +      };
    +      mediaQueryLists.push(newList);
    +      return newList;
    +    };
    +  }
    +
    +  var domHelper = mockControl.createStrictMock(goog.dom.DomHelper);
    +  domHelper.getWindow().$returns(fakeWindow);
    +  mockControl.$replayAll();
    +
    +  monitor = new goog.labs.style.PixelDensityMonitor(domHelper);
    +  goog.events.listen(monitor,
    +      goog.labs.style.PixelDensityMonitor.EventType.CHANGE, recordFunction);
    +}
    +
    +function setNewRatio(newRatio) {
    +  fakeWindow.devicePixelRatio = newRatio;
    +  for (var i = 0; i < mediaQueryLists.length; i++) {
    +    mediaQueryLists[i].callListeners();
    +  }
    +}
    +
    +function testNormalDensity() {
    +  setUpMonitor(1, false);
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.NORMAL,
    +      monitor.getDensity());
    +}
    +
    +function testHighDensity() {
    +  setUpMonitor(1.5, false);
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.HIGH,
    +      monitor.getDensity());
    +}
    +
    +function testNormalDensityIfUndefined() {
    +  setUpMonitor(undefined, false);
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.NORMAL,
    +      monitor.getDensity());
    +}
    +
    +function testChangeEvent() {
    +  setUpMonitor(1, true);
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.NORMAL,
    +      monitor.getDensity());
    +  monitor.start();
    +
    +  setNewRatio(2);
    +  var call = recordFunction.popLastCall();
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.HIGH,
    +      call.getArgument(0).target.getDensity());
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.HIGH,
    +      monitor.getDensity());
    +
    +  setNewRatio(1);
    +  call = recordFunction.popLastCall();
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.NORMAL,
    +      call.getArgument(0).target.getDensity());
    +  assertEquals(goog.labs.style.PixelDensityMonitor.Density.NORMAL,
    +      monitor.getDensity());
    +}
    +
    +function testListenerIsDisposed() {
    +  setUpMonitor(1, true);
    +  monitor.start();
    +
    +  assertEquals(1, mediaQueryLists.length);
    +  assertEquals(1, mediaQueryLists[0].getListenerCount());
    +
    +  goog.dispose(monitor);
    +
    +  assertEquals(1, mediaQueryLists.length);
    +  assertEquals(0, mediaQueryLists[0].getListenerCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat.js b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat.js
    new file mode 100644
    index 0000000000..d0843eb2b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat.js
    @@ -0,0 +1,58 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides main functionality of assertThat. assertThat calls the
    + * matcher's matches method to test if a matcher matches assertThat's arguments.
    + */
    +
    +
    +goog.provide('goog.labs.testing.MatcherError');
    +goog.provide('goog.labs.testing.assertThat');
    +
    +goog.require('goog.debug.Error');
    +
    +
    +/**
    + * Asserts that the actual value evaluated by the matcher is true.
    + *
    + * @param {*} actual The object to assert by the matcher.
    + * @param {!goog.labs.testing.Matcher} matcher A matcher to verify values.
    + * @param {string=} opt_reason Description of what is asserted.
    + *
    + */
    +goog.labs.testing.assertThat = function(actual, matcher, opt_reason) {
    +  if (!matcher.matches(actual)) {
    +    // Prefix the error description with a reason from the assert ?
    +    var prefix = opt_reason ? opt_reason + ': ' : '';
    +    var desc = prefix + matcher.describe(actual);
    +
    +    // some sort of failure here
    +    throw new goog.labs.testing.MatcherError(desc);
    +  }
    +};
    +
    +
    +
    +/**
    + * Error thrown when a Matcher fails to match the input value.
    + * @param {string=} opt_message The error message.
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.labs.testing.MatcherError = function(opt_message) {
    +  goog.labs.testing.MatcherError.base(this, 'constructor', opt_message);
    +};
    +goog.inherits(goog.labs.testing.MatcherError, goog.debug.Error);
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.html
    new file mode 100644
    index 0000000000..a22ca62f5b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.labs.testing.assertThat
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.assertThatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.js
    new file mode 100644
    index 0000000000..6a054bcabc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/assertthat_test.js
    @@ -0,0 +1,69 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.assertThatTest');
    +goog.setTestOnly('goog.labs.testing.assertThatTest');
    +
    +goog.require('goog.labs.testing.MatcherError');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var successMatchesFn, failureMatchesFn, describeFn, successTestMatcher;
    +var failureTestMatcher;
    +
    +function setUp() {
    +  successMatchesFn = new goog.testing.recordFunction(function() {return true;});
    +  failureMatchesFn =
    +      new goog.testing.recordFunction(function() {return false;});
    +  describeFn = new goog.testing.recordFunction();
    +
    +  successTestMatcher = function() {
    +    return { matches: successMatchesFn, describe: describeFn };
    +  };
    +  failureTestMatcher = function() {
    +    return { matches: failureMatchesFn, describe: describeFn };
    +  };
    +}
    +
    +function testAssertthatAlwaysCallsMatches() {
    +  var value = 7;
    +  goog.labs.testing.assertThat(value, successTestMatcher(),
    +      'matches is called on success');
    +
    +  assertEquals(1, successMatchesFn.getCallCount());
    +  var matchesCall = successMatchesFn.popLastCall();
    +  assertEquals(value, matchesCall.getArgument(0));
    +
    +  var e = assertThrows(goog.bind(goog.labs.testing.assertThat, null,
    +      value, failureTestMatcher(), 'matches is called on failure'));
    +
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +
    +  assertEquals(1, failureMatchesFn.getCallCount());
    +}
    +
    +function testAssertthatCallsDescribeOnFailure() {
    +  var value = 7;
    +  var e = assertThrows(goog.bind(goog.labs.testing.assertThat, null,
    +      value, failureTestMatcher(), 'describe is called on failure'));
    +
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +
    +  assertEquals(1, failureMatchesFn.getCallCount());
    +  assertEquals(1, describeFn.getCallCount());
    +
    +  var matchesCall = describeFn.popLastCall();
    +  assertEquals(value, matchesCall.getArgument(0));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher.js
    new file mode 100644
    index 0000000000..d0ad753afa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher.js
    @@ -0,0 +1,95 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in decorators: is, describedAs, anything.
    + */
    +
    +
    +
    +goog.provide('goog.labs.testing.AnythingMatcher');
    +
    +
    +goog.require('goog.labs.testing.Matcher');
    +
    +
    +
    +/**
    + * The Anything matcher. Matches all possible inputs.
    + *
    + * @constructor
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.AnythingMatcher = function() {};
    +
    +
    +/**
    + * Matches anything. Useful if one doesn't care what the object under test is.
    + *
    + * @override
    + */
    +goog.labs.testing.AnythingMatcher.prototype.matches =
    +    function(actualObject) {
    +  return true;
    +};
    +
    +
    +/**
    + * This method is never called but is needed so AnythingMatcher implements the
    + * Matcher interface.
    + *
    + * @override
    + */
    +goog.labs.testing.AnythingMatcher.prototype.describe =
    +    function(actualObject) {
    +  throw Error('AnythingMatcher should never fail!');
    +};
    +
    +
    +/**
    + * Returns a matcher that matches anything.
    + *
    + * @return {!goog.labs.testing.AnythingMatcher} A AnythingMatcher.
    + */
    +function anything() {
    +  return new goog.labs.testing.AnythingMatcher();
    +}
    +
    +
    +/**
    + * Returnes any matcher that is passed to it (aids readability).
    + *
    + * @param {!goog.labs.testing.Matcher} matcher A matcher.
    + * @return {!goog.labs.testing.Matcher} The wrapped matcher.
    + */
    +function is(matcher) {
    +  return matcher;
    +}
    +
    +
    +/**
    + * Returns a matcher with a customized description for the given matcher.
    + *
    + * @param {string} description The custom description for the matcher.
    + * @param {!goog.labs.testing.Matcher} matcher The matcher.
    + *
    + * @return {!goog.labs.testing.Matcher} The matcher with custom description.
    + */
    +function describedAs(description, matcher) {
    +  matcher.describe = function(value) {
    +    return description;
    +  };
    +  return matcher;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.html
    new file mode 100644
    index 0000000000..ac29d26b8a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - Decorator matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.decoratorMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.js
    new file mode 100644
    index 0000000000..4f8792ecef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/decoratormatcher_test.js
    @@ -0,0 +1,41 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.decoratorMatcherTest');
    +goog.setTestOnly('goog.labs.testing.decoratorMatcherTest');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.AnythingMatcher');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.GreaterThanMatcher');
    +goog.require('goog.labs.testing.MatcherError');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testAnythingMatcher() {
    +  goog.labs.testing.assertThat(true, anything(), 'anything matches true');
    +  goog.labs.testing.assertThat(false, anything(), 'false matches anything');
    +}
    +
    +function testIs() {
    +  goog.labs.testing.assertThat(5, is(greaterThan(4)), '5 is > 4');
    +}
    +
    +function testDescribedAs() {
    +  var e = assertThrows(function() {
    +    goog.labs.testing.assertThat(4, describedAs('this is a test',
    +        greaterThan(6)))});
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +  assertEquals('this is a test', e.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher.js
    new file mode 100644
    index 0000000000..cfddd74ed6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher.js
    @@ -0,0 +1,273 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in dictionary matcher methods like
    + *     hasEntry, hasEntries, hasKey, hasValue, etc.
    + */
    +
    +
    +
    +goog.provide('goog.labs.testing.HasEntriesMatcher');
    +goog.provide('goog.labs.testing.HasEntryMatcher');
    +goog.provide('goog.labs.testing.HasKeyMatcher');
    +goog.provide('goog.labs.testing.HasValueMatcher');
    +
    +
    +goog.require('goog.asserts');
    +goog.require('goog.labs.testing.Matcher');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * The HasEntries matcher.
    + *
    + * @param {!Object} entries The entries to check in the object.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.HasEntriesMatcher = function(entries) {
    +  /**
    +   * @type {Object}
    +   * @private
    +   */
    +  this.entries_ = entries;
    +};
    +
    +
    +/**
    + * Determines if an object has particular entries.
    + *
    + * @override
    + */
    +goog.labs.testing.HasEntriesMatcher.prototype.matches =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject, 'Expected an Object');
    +  var object = /** @type {!Object} */(actualObject);
    +  return goog.object.every(this.entries_, function(value, key) {
    +    return goog.object.containsKey(object, key) &&
    +           object[key] === value;
    +  });
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.HasEntriesMatcher.prototype.describe =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject, 'Expected an Object');
    +  var object = /** @type {!Object} */(actualObject);
    +  var errorString = 'Input object did not contain the following entries:\n';
    +  goog.object.forEach(this.entries_, function(value, key) {
    +    if (!goog.object.containsKey(object, key) ||
    +        object[key] !== value) {
    +      errorString += key + ': ' + value + '\n';
    +    }
    +  });
    +  return errorString;
    +};
    +
    +
    +
    +/**
    + * The HasEntry matcher.
    + *
    + * @param {string} key The key for the entry.
    + * @param {*} value The value for the key.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.HasEntryMatcher = function(key, value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.key_ = key;
    +  /**
    +   * @type {*}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if an object has a particular entry.
    + *
    + * @override
    + */
    +goog.labs.testing.HasEntryMatcher.prototype.matches =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject);
    +  return goog.object.containsKey(actualObject, this.key_) &&
    +         actualObject[this.key_] === this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.HasEntryMatcher.prototype.describe =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject);
    +  var errorMsg;
    +  if (goog.object.containsKey(actualObject, this.key_)) {
    +    errorMsg = 'Input object did not contain key: ' + this.key_;
    +  } else {
    +    errorMsg = 'Value for key did not match value: ' + this.value_;
    +  }
    +  return errorMsg;
    +};
    +
    +
    +
    +/**
    + * The HasKey matcher.
    + *
    + * @param {string} key The key to check in the object.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.HasKeyMatcher = function(key) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.key_ = key;
    +};
    +
    +
    +/**
    + * Determines if an object has a key.
    + *
    + * @override
    + */
    +goog.labs.testing.HasKeyMatcher.prototype.matches =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject);
    +  return goog.object.containsKey(actualObject, this.key_);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.HasKeyMatcher.prototype.describe =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject);
    +  return 'Input object did not contain the key: ' + this.key_;
    +};
    +
    +
    +
    +/**
    + * The HasValue matcher.
    + *
    + * @param {*} value The value to check in the object.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.HasValueMatcher = function(value) {
    +  /**
    +   * @type {*}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if an object contains a value
    + *
    + * @override
    + */
    +goog.labs.testing.HasValueMatcher.prototype.matches =
    +    function(actualObject) {
    +  goog.asserts.assertObject(actualObject, 'Expected an Object');
    +  var object = /** @type {!Object} */(actualObject);
    +  return goog.object.containsValue(object, this.value_);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.HasValueMatcher.prototype.describe =
    +    function(actualObject) {
    +  return 'Input object did not contain the value: ' + this.value_;
    +};
    +
    +
    +/**
    + * Gives a matcher that asserts an object contains all the given key-value pairs
    + * in the input object.
    + *
    + * @param {!Object} entries The entries to check for presence in the object.
    + *
    + * @return {!goog.labs.testing.HasEntriesMatcher} A HasEntriesMatcher.
    + */
    +function hasEntries(entries) {
    +  return new goog.labs.testing.HasEntriesMatcher(entries);
    +}
    +
    +
    +/**
    + * Gives a matcher that asserts an object contains the given key-value pair.
    + *
    + * @param {string} key The key to check for presence in the object.
    + * @param {*} value The value to check for presence in the object.
    + *
    + * @return {!goog.labs.testing.HasEntryMatcher} A HasEntryMatcher.
    + */
    +function hasEntry(key, value) {
    +  return new goog.labs.testing.HasEntryMatcher(key, value);
    +}
    +
    +
    +/**
    + * Gives a matcher that asserts an object contains the given key.
    + *
    + * @param {string} key The key to check for presence in the object.
    + *
    + * @return {!goog.labs.testing.HasKeyMatcher} A HasKeyMatcher.
    + */
    +function hasKey(key) {
    +  return new goog.labs.testing.HasKeyMatcher(key);
    +}
    +
    +
    +/**
    + * Gives a matcher that asserts an object contains the given value.
    + *
    + * @param {*} value The value to check for presence in the object.
    + *
    + * @return {!goog.labs.testing.HasValueMatcher} A HasValueMatcher.
    + */
    +function hasValue(value) {
    +  return new goog.labs.testing.HasValueMatcher(value);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html
    new file mode 100644
    index 0000000000..785e367cd1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - Object matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.dictionaryMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.js
    new file mode 100644
    index 0000000000..615c78d877
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/dictionarymatcher_test.js
    @@ -0,0 +1,65 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.dictionaryMatcherTest');
    +goog.setTestOnly('goog.labs.testing.dictionaryMatcherTest');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.HasEntryMatcher');
    +goog.require('goog.labs.testing.MatcherError');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testHasEntries() {
    +  var obj1 = {x: 1, y: 2, z: 3};
    +  goog.labs.testing.assertThat(obj1, hasEntries({x: 1, y: 2}),
    +      'obj1 has entries: {x:1, y:2}');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(obj1, hasEntries({z: 5, a: 4}));
    +  }, 'hasEntries should throw exception when it fails');
    +}
    +
    +function testHasEntry() {
    +  var obj1 = {x: 1, y: 2, z: 3};
    +  goog.labs.testing.assertThat(obj1, hasEntry('x', 1),
    +      'obj1 has entry: {x:1}');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(obj1, hasEntry('z', 5));
    +  }, 'hasEntry should throw exception when it fails');
    +}
    +
    +function testHasKey() {
    +  var obj1 = {x: 1};
    +  goog.labs.testing.assertThat(obj1, hasKey('x'), 'obj1 has key x');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(obj1, hasKey('z'));
    +  }, 'hasKey should throw exception when it fails');
    +}
    +
    +function testHasValue() {
    +  var obj1 = {x: 1};
    +  goog.labs.testing.assertThat(obj1, hasValue(1), 'obj1 has value 1');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(obj1, hasValue(2));
    +  }, 'hasValue should throw exception when it fails');
    +}
    +
    +function assertMatcherError(callable, errorString) {
    +  var e = assertThrows(errorString || 'callable throws exception', callable);
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/environment.js b/mapbox-gl/closure-library/closure/goog/labs/testing/environment.js
    new file mode 100644
    index 0000000000..a3468e4e83
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/environment.js
    @@ -0,0 +1,293 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.Environment');
    +
    +goog.require('goog.array');
    +goog.require('goog.debug.Console');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * JsUnit environments allow developers to customize the existing testing
    + * lifecycle by hitching additional setUp and tearDown behaviors to tests.
    + *
    + * Environments will run their setUp steps in the order in which they
    + * are instantiated and registered. During tearDown, the environments will
    + * unwind the setUp and execute in reverse order.
    + *
    + * See http://go/jsunit-env for more information.
    + */
    +goog.labs.testing.Environment = goog.defineClass(null, {
    +  /** @constructor */
    +  constructor: function() {
    +    goog.labs.testing.EnvironmentTestCase_.getInstance().
    +        registerEnvironment_(this);
    +
    +    /** @type {goog.testing.MockControl} */
    +    this.mockControl = null;
    +
    +    /** @type {goog.testing.MockClock} */
    +    this.mockClock = null;
    +
    +    /** @private {boolean} */
    +    this.shouldMakeMockControl_ = false;
    +
    +    /** @private {boolean} */
    +    this.shouldMakeMockClock_ = false;
    +
    +    /** @const {!goog.debug.Console} */
    +    this.console = goog.labs.testing.Environment.console_;
    +  },
    +
    +
    +  /** Runs immediately before the setUpPage phase of JsUnit tests. */
    +  setUpPage: function() {
    +    if (this.mockClock && this.mockClock.isDisposed()) {
    +      this.mockClock = new goog.testing.MockClock(true);
    +    }
    +  },
    +
    +
    +  /** Runs immediately after the tearDownPage phase of JsUnit tests. */
    +  tearDownPage: function() {
    +    // If we created the mockClock, we'll also dispose it.
    +    if (this.shouldMakeMockClock_) {
    +      this.mockClock.dispose();
    +    }
    +  },
    +
    +  /** Runs immediately before the setUp phase of JsUnit tests. */
    +  setUp: goog.nullFunction,
    +
    +  /** Runs immediately after the tearDown phase of JsUnit tests. */
    +  tearDown: function() {
    +    // Make sure promises and other stuff that may still be scheduled, get a
    +    // chance to run (and throw errors).
    +    if (this.mockClock) {
    +      for (var i = 0; i < 100; i++) {
    +        this.mockClock.tick(1000);
    +      }
    +      // If we created the mockClock, we'll also reset it.
    +      if (this.shouldMakeMockClock_) {
    +        this.mockClock.reset();
    +      }
    +    }
    +    // Make sure the user did not forget to call $replayAll & $verifyAll in
    +    // their test. This is a noop if they did.
    +    // This is important because:
    +    // - Engineers thinks that not all their tests need to replay and verify.
    +    //   That lets tests sneak in that call mocks but never replay those calls.
    +    // - Then some well meaning maintenance engineer wants to update the test
    +    //   with some new mock, adds a replayAll and BOOM the test fails
    +    //   because completely unrelated mocks now get replayed.
    +    if (this.mockControl) {
    +      try {
    +        this.mockControl.$verifyAll();
    +        this.mockControl.$replayAll();
    +        this.mockControl.$verifyAll();
    +      } finally {
    +        this.mockControl.$resetAll();
    +      }
    +      if (this.shouldMakeMockControl_) {
    +        // If we created the mockControl, we'll also tear it down.
    +        this.mockControl.$tearDown();
    +      }
    +    }
    +    // Verifying the mockControl may throw, so if cleanup needs to happen,
    +    // add it further up in the function.
    +  },
    +
    +
    +  /**
    +   * Create a new {@see goog.testing.MockControl} accessible via
    +   * {@code env.mockControl} for each test. If your test has more than one
    +   * testing environment, don't call this on more than one of them.
    +   * @return {!goog.labs.testing.Environment} For chaining.
    +   */
    +  withMockControl: function() {
    +    if (!this.shouldMakeMockControl_) {
    +      this.shouldMakeMockControl_ = true;
    +      this.mockControl = new goog.testing.MockControl();
    +    }
    +    return this;
    +  },
    +
    +
    +  /**
    +   * Create a {@see goog.testing.MockClock} for each test. The clock will be
    +   * installed (override i.e. setTimeout) by default. It can be accessed
    +   * using {@code env.mockClock}. If your test has more than one testing
    +   * environment, don't call this on more than one of them.
    +   * @return {!goog.labs.testing.Environment} For chaining.
    +   */
    +  withMockClock: function() {
    +    if (!this.shouldMakeMockClock_) {
    +      this.shouldMakeMockClock_ = true;
    +      this.mockClock = new goog.testing.MockClock(true);
    +    }
    +    return this;
    +  },
    +
    +
    +  /**
    +   * Creates a basic strict mock of a {@code toMock}. For more advanced mocking,
    +   * please use the MockControl directly.
    +   * @param {Function} toMock
    +   * @return {!goog.testing.StrictMock}
    +   */
    +  mock: function(toMock) {
    +    if (!this.shouldMakeMockControl_) {
    +      throw new Error('MockControl not available on this environment. ' +
    +                      'Call withMockControl if this environment is expected ' +
    +                      'to contain a MockControl.');
    +    }
    +    return this.mockControl.createStrictMock(toMock);
    +  }
    +});
    +
    +
    +/** @private @const {!goog.debug.Console} */
    +goog.labs.testing.Environment.console_ = new goog.debug.Console();
    +
    +
    +// Activate logging to the browser's console by default.
    +goog.labs.testing.Environment.console_.setCapturing(true);
    +
    +
    +
    +/**
    + * An internal TestCase used to hook environments into the JsUnit test runner.
    + * Environments cannot be used in conjunction with custom TestCases for JsUnit.
    + * @private @final @constructor
    + * @extends {goog.testing.TestCase}
    + */
    +goog.labs.testing.EnvironmentTestCase_ = function() {
    +  goog.labs.testing.EnvironmentTestCase_.base(this, 'constructor');
    +
    +  /** @private {!Array<!goog.labs.testing.Environment>}> */
    +  this.environments_ = [];
    +
    +  // Automatically install this TestCase when any environment is used in a test.
    +  goog.testing.TestCase.initializeTestRunner(this);
    +};
    +goog.inherits(goog.labs.testing.EnvironmentTestCase_, goog.testing.TestCase);
    +goog.addSingletonGetter(goog.labs.testing.EnvironmentTestCase_);
    +
    +
    +/**
    + * Override the default global scope discovery of lifecycle functions to prevent
    + * overriding the custom environment setUp(Page)/tearDown(Page) logic.
    + * @override
    + */
    +goog.labs.testing.EnvironmentTestCase_.prototype.autoDiscoverLifecycle =
    +    function() {
    +  if (goog.global['runTests']) {
    +    this.runTests = goog.bind(goog.global['runTests'], goog.global);
    +  }
    +  if (goog.global['shouldRunTests']) {
    +    this.shouldRunTests = goog.bind(goog.global['shouldRunTests'], goog.global);
    +  }
    +};
    +
    +
    +/**
    + * Adds an environment to the JsUnit test.
    + * @param {!goog.labs.testing.Environment} env
    + * @private
    + */
    +goog.labs.testing.EnvironmentTestCase_.prototype.registerEnvironment_ =
    +    function(env) {
    +  this.environments_.push(env);
    +};
    +
    +
    +/** @override */
    +goog.labs.testing.EnvironmentTestCase_.prototype.setUpPage = function() {
    +  goog.array.forEach(this.environments_, function(env) {
    +    env.setUpPage();
    +  });
    +
    +  // User defined setUpPage method.
    +  if (goog.global['setUpPage']) {
    +    goog.global['setUpPage']();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.labs.testing.EnvironmentTestCase_.prototype.setUp = function() {
    +  // User defined configure method.
    +  if (goog.global['configureEnvironment']) {
    +    goog.global['configureEnvironment']();
    +  }
    +
    +  goog.array.forEach(this.environments_, function(env) {
    +    env.setUp();
    +  }, this);
    +
    +  // User defined setUp method.
    +  if (goog.global['setUp']) {
    +    goog.global['setUp']();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.labs.testing.EnvironmentTestCase_.prototype.tearDown = function() {
    +  var firstException;
    +  // User defined tearDown method.
    +  if (goog.global['tearDown']) {
    +    try {
    +      goog.global['tearDown']();
    +    } catch (e) {
    +      if (!firstException) {
    +        firstException = e;
    +      }
    +    }
    +  }
    +
    +  // Execute the tearDown methods for the environment in the reverse order
    +  // in which they were registered to "unfold" the setUp.
    +  goog.array.forEachRight(this.environments_, function(env) {
    +    // For tearDowns between tests make sure they run as much as possible to
    +    // avoid interference between tests.
    +    try {
    +      env.tearDown();
    +    } catch (e) {
    +      if (!firstException) {
    +        firstException = e;
    +      }
    +    }
    +  });
    +  if (firstException) {
    +    throw firstException;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.labs.testing.EnvironmentTestCase_.prototype.tearDownPage = function() {
    +  // User defined tearDownPage method.
    +  if (goog.global['tearDownPage']) {
    +    goog.global['tearDownPage']();
    +  }
    +
    +  goog.array.forEachRight(this.environments_, function(env) {
    +    env.tearDownPage();
    +  });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.html
    new file mode 100644
    index 0000000000..c8d4ab0e2d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - JsUnit Environments
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.environmentTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.js
    new file mode 100644
    index 0000000000..8f3fdf259a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_test.js
    @@ -0,0 +1,210 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.environmentTest');
    +goog.setTestOnly('goog.labs.testing.environmentTest');
    +
    +goog.require('goog.labs.testing.Environment');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.jsunit');
    +
    +var testCase = null;
    +var mockControl = null;
    +
    +// Use this flag to control whether the global JsUnit lifecycle events are being
    +// called as part of the test lifecycle or as part of the "mocked" environment.
    +var testing = false;
    +
    +function setUp() {
    +  if (testing) {
    +    return;
    +  }
    +
    +  // Temporarily override the initializeTestRunner method to avoid installing
    +  // our "test" TestCase.
    +  var initFn = goog.testing.TestCase.initializeTestRunner;
    +  goog.testing.TestCase.initializeTestRunner = function() {};
    +  testCase = new goog.labs.testing.EnvironmentTestCase_();
    +  goog.labs.testing.EnvironmentTestCase_.getInstance = function() {
    +    return testCase;
    +  };
    +  goog.testing.TestCase.initializeTestRunner = initFn;
    +
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function tearDown() {
    +  if (testing) {
    +    return;
    +  }
    +
    +  mockControl.$resetAll();
    +  mockControl.$tearDown();
    +}
    +
    +function testLifecycle() {
    +  testing = true;
    +
    +  var envOne = mockControl.createStrictMock(goog.labs.testing.Environment);
    +  var envTwo = mockControl.createStrictMock(goog.labs.testing.Environment);
    +  var envThree = mockControl.createStrictMock(goog.labs.testing.Environment);
    +  var testMethod = mockControl.createFunctionMock('testMethod');
    +
    +  testCase.addNewTest('testFake', testMethod);
    +
    +  testCase.registerEnvironment_(envOne);
    +  testCase.registerEnvironment_(envTwo);
    +  testCase.registerEnvironment_(envThree);
    +
    +  envOne.setUpPage();
    +  envTwo.setUpPage();
    +  envThree.setUpPage();
    +
    +  envOne.setUp();
    +  envTwo.setUp();
    +  envThree.setUp();
    +
    +  testMethod();
    +
    +  envThree.tearDown();
    +  envTwo.tearDown();
    +  envOne.tearDown();
    +
    +  envThree.tearDownPage();
    +  envTwo.tearDownPage();
    +  envOne.tearDownPage();
    +
    +  mockControl.$replayAll();
    +  testCase.runTests();
    +  mockControl.$verifyAll();
    +
    +  testing = false;
    +}
    +
    +function testTearDownWithMockControl() {
    +  testing = true;
    +
    +  var envWith = new goog.labs.testing.Environment();
    +  var envWithout = new goog.labs.testing.Environment();
    +
    +  var mockControlMock = mockControl.createStrictMock(goog.testing.MockControl);
    +  var mockControlCtorMock = mockControl.createMethodMock(goog.testing,
    +      'MockControl');
    +  mockControlCtorMock().$times(1).$returns(mockControlMock);
    +  // Expecting verify / reset calls twice since two environments use the same
    +  // mockControl, but only one created it and is allowed to tear it down.
    +  mockControlMock.$verifyAll();
    +  mockControlMock.$replayAll();
    +  mockControlMock.$verifyAll();
    +  mockControlMock.$resetAll();
    +  mockControlMock.$tearDown().$times(1);
    +  mockControlMock.$verifyAll();
    +  mockControlMock.$replayAll();
    +  mockControlMock.$verifyAll();
    +  mockControlMock.$resetAll();
    +
    +  mockControl.$replayAll();
    +  envWith.withMockControl();
    +  envWithout.mockControl = mockControlMock;
    +  envWith.tearDown();
    +  envWithout.tearDown();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  testing = false;
    +}
    +
    +function testAutoDiscoverTests() {
    +  testing = true;
    +
    +  var setUpPageFn = testCase.setUpPage;
    +  var setUpFn = testCase.setUp;
    +  var tearDownFn = testCase.tearDownFn;
    +  var tearDownPageFn = testCase.tearDownPageFn;
    +
    +  testCase.autoDiscoverTests();
    +
    +  assertEquals(setUpPageFn, testCase.setUpPage);
    +  assertEquals(setUpFn, testCase.setUp);
    +  assertEquals(tearDownFn, testCase.tearDownFn);
    +  assertEquals(tearDownPageFn, testCase.tearDownPageFn);
    +
    +  // Note that this number changes when more tests are added to this file as
    +  // the environment reflects on the window global scope for JsUnit.
    +  assertEquals(6, testCase.tests_.length);
    +
    +  testing = false;
    +}
    +
    +function testMockClock() {
    +  testing = true;
    +
    +  var env = new goog.labs.testing.Environment().withMockClock();
    +
    +  testCase.addNewTest('testThatThrowsEventually', function() {
    +    setTimeout(function() {
    +      throw new Error('LateErrorMessage');
    +    }, 200);
    +  });
    +
    +  testCase.runTests();
    +  assertTestFailure(testCase, 'testThatThrowsEventually', 'LateErrorMessage');
    +
    +  testing = false;
    +}
    +
    +function testMockControl() {
    +  testing = true;
    +
    +  var env = new goog.labs.testing.Environment().withMockControl();
    +  var test = env.mockControl.createFunctionMock('test');
    +
    +  testCase.addNewTest('testWithoutVerify', function() {
    +    test();
    +    env.mockControl.$replayAll();
    +    test();
    +  });
    +
    +  testCase.runTests();
    +  assertNull(env.mockClock);
    +
    +  testing = false;
    +}
    +
    +function testMock() {
    +  testing = true;
    +
    +  var env = new goog.labs.testing.Environment().withMockControl();
    +  var mock = env.mock({
    +    test: function() {}
    +  });
    +
    +  testCase.addNewTest('testMockCalled', function() {
    +    mock.test().$times(2);
    +
    +    env.mockControl.$replayAll();
    +    mock.test();
    +    mock.test();
    +    env.mockControl.verifyAll();
    +  });
    +
    +  testCase.runTests();
    +
    +  testing = false;
    +}
    +
    +function assertTestFailure(testCase, name, message) {
    +  assertContains(message, testCase.result_.resultsByName[name][0]);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/environment_usage_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_usage_test.js
    new file mode 100644
    index 0000000000..443958ce18
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/environment_usage_test.js
    @@ -0,0 +1,45 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.environmentUsageTest');
    +goog.setTestOnly('goog.labs.testing.environmentUsageTest');
    +
    +goog.require('goog.labs.testing.Environment');
    +
    +var testing = false;
    +var env = new goog.labs.testing.Environment();
    +
    +function setUpPage() {
    +  assertFalse(testing);
    +}
    +
    +function setUp() {
    +  testing = true;
    +}
    +
    +function testOne() {
    +  assertTrue(testing);
    +}
    +
    +function testTwo() {
    +  assertTrue(testing);
    +}
    +
    +function tearDown() {
    +  testing = false;
    +}
    +
    +function tearDownPage() {
    +  assertFalse(testing);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher.js
    new file mode 100644
    index 0000000000..09148e3c45
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher.js
    @@ -0,0 +1,212 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in logic matchers: anyOf, allOf, and isNot.
    + *
    + */
    +
    +
    +goog.provide('goog.labs.testing.AllOfMatcher');
    +goog.provide('goog.labs.testing.AnyOfMatcher');
    +goog.provide('goog.labs.testing.IsNotMatcher');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.testing.Matcher');
    +
    +
    +
    +/**
    + * The AllOf matcher.
    + *
    + * @param {!Array<!goog.labs.testing.Matcher>} matchers Input matchers.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.AllOfMatcher = function(matchers) {
    +  /**
    +   * @type {!Array<!goog.labs.testing.Matcher>}
    +   * @private
    +   */
    +  this.matchers_ = matchers;
    +};
    +
    +
    +/**
    + * Determines if all of the matchers match the input value.
    + *
    + * @override
    + */
    +goog.labs.testing.AllOfMatcher.prototype.matches = function(actualValue) {
    +  return goog.array.every(this.matchers_, function(matcher) {
    +    return matcher.matches(actualValue);
    +  });
    +};
    +
    +
    +/**
    + * Describes why the matcher failed. The returned string is a concatenation of
    + * all the failed matchers' error strings.
    + *
    + * @override
    + */
    +goog.labs.testing.AllOfMatcher.prototype.describe =
    +    function(actualValue) {
    +  // TODO(user) : Optimize this to remove duplication with matches ?
    +  var errorString = '';
    +  goog.array.forEach(this.matchers_, function(matcher) {
    +    if (!matcher.matches(actualValue)) {
    +      errorString += matcher.describe(actualValue) + '\n';
    +    }
    +  });
    +  return errorString;
    +};
    +
    +
    +
    +/**
    + * The AnyOf matcher.
    + *
    + * @param {!Array<!goog.labs.testing.Matcher>} matchers Input matchers.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.AnyOfMatcher = function(matchers) {
    +  /**
    +   * @type {!Array<!goog.labs.testing.Matcher>}
    +   * @private
    +   */
    +  this.matchers_ = matchers;
    +};
    +
    +
    +/**
    + * Determines if any of the matchers matches the input value.
    + *
    + * @override
    + */
    +goog.labs.testing.AnyOfMatcher.prototype.matches = function(actualValue) {
    +  return goog.array.some(this.matchers_, function(matcher) {
    +    return matcher.matches(actualValue);
    +  });
    +};
    +
    +
    +/**
    + * Describes why the matcher failed.
    + *
    + * @override
    + */
    +goog.labs.testing.AnyOfMatcher.prototype.describe =
    +    function(actualValue) {
    +  // TODO(user) : Optimize this to remove duplication with matches ?
    +  var errorString = '';
    +  goog.array.forEach(this.matchers_, function(matcher) {
    +    if (!matcher.matches(actualValue)) {
    +      errorString += matcher.describe(actualValue) + '\n';
    +    }
    +  });
    +  return errorString;
    +};
    +
    +
    +
    +/**
    + * The IsNot matcher.
    + *
    + * @param {!goog.labs.testing.Matcher} matcher The matcher to negate.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.IsNotMatcher = function(matcher) {
    +  /**
    +   * @type {!goog.labs.testing.Matcher}
    +   * @private
    +   */
    +  this.matcher_ = matcher;
    +};
    +
    +
    +/**
    + * Determines if the input value doesn't satisfy a matcher.
    + *
    + * @override
    + */
    +goog.labs.testing.IsNotMatcher.prototype.matches = function(actualValue) {
    +  return !this.matcher_.matches(actualValue);
    +};
    +
    +
    +/**
    + * Describes why the matcher failed.
    + *
    + * @override
    + */
    +goog.labs.testing.IsNotMatcher.prototype.describe =
    +    function(actualValue) {
    +  return 'The following is false: ' + this.matcher_.describe(actualValue);
    +};
    +
    +
    +/**
    + * Creates a matcher that will succeed only if all of the given matchers
    + * succeed.
    + *
    + * @param {...goog.labs.testing.Matcher} var_args The matchers to test
    + *     against.
    + *
    + * @return {!goog.labs.testing.AllOfMatcher} The AllOf matcher.
    + */
    +function allOf(var_args) {
    +  var matchers = goog.array.toArray(arguments);
    +  return new goog.labs.testing.AllOfMatcher(matchers);
    +}
    +
    +
    +/**
    + * Accepts a set of matchers and returns a matcher which matches
    + * values which satisfy the constraints of any of the given matchers.
    + *
    + * @param {...goog.labs.testing.Matcher} var_args The matchers to test
    + *     against.
    + *
    + * @return {!goog.labs.testing.AnyOfMatcher} The AnyOf matcher.
    + */
    +function anyOf(var_args) {
    +  var matchers = goog.array.toArray(arguments);
    +  return new goog.labs.testing.AnyOfMatcher(matchers);
    +}
    +
    +
    +/**
    + * Returns a matcher that negates the input matcher. The returned
    + * matcher matches the values not matched by the input matcher and vice-versa.
    + *
    + * @param {!goog.labs.testing.Matcher} matcher The matcher to test against.
    + *
    + * @return {!goog.labs.testing.IsNotMatcher} The IsNot matcher.
    + */
    +function isNot(matcher) {
    +  return new goog.labs.testing.IsNotMatcher(matcher);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.html
    new file mode 100644
    index 0000000000..d760323d98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - Logic matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.labs.testing.logicMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.js
    new file mode 100644
    index 0000000000..509338be49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/logicmatcher_test.js
    @@ -0,0 +1,57 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.logicMatcherTest');
    +goog.setTestOnly('goog.labs.testing.logicMatcherTest');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.AllOfMatcher');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.GreaterThanMatcher');
    +goog.require('goog.labs.testing.MatcherError');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testAnyOf() {
    +  goog.labs.testing.assertThat(5, anyOf(greaterThan(4), lessThan(3)),
    +      '5 > 4 || 5 < 3');
    +  goog.labs.testing.assertThat(2, anyOf(greaterThan(4), lessThan(3)),
    +      '2 > 4 || 2 < 3');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(4, anyOf(greaterThan(5), lessThan(2)));
    +  }, 'anyOf should throw exception when it fails');
    +}
    +
    +function testAllOf() {
    +  goog.labs.testing.assertThat(5, allOf(greaterThan(4), lessThan(6)),
    +      '5 > 4 && 5 < 6');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(4, allOf(lessThan(5), lessThan(3)));
    +  }, 'allOf should throw exception when it fails');
    +}
    +
    +function testIsNot() {
    +  goog.labs.testing.assertThat(5, isNot(greaterThan(6)), '5 !> 6');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(4, isNot(greaterThan(3)));
    +  }, 'isNot should throw exception when it fails');
    +}
    +
    +function assertMatcherError(callable, errorString) {
    +  var e = assertThrows(errorString || 'callable throws exception', callable);
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/matcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/matcher.js
    new file mode 100644
    index 0000000000..f8dd211231
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/matcher.js
    @@ -0,0 +1,80 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the base Matcher interface. User code should use the
    + * matchers through assertThat statements and not directly.
    + */
    +
    +
    +goog.provide('goog.labs.testing.Matcher');
    +
    +
    +
    +/**
    + * A matcher object to be used in assertThat statements.
    + * @interface
    + */
    +goog.labs.testing.Matcher = function() {};
    +
    +
    +/**
    + * Determines whether a value matches the constraints of the match.
    + *
    + * @param {*} value The object to match.
    + * @return {boolean} Whether the input value matches this matcher.
    + */
    +goog.labs.testing.Matcher.prototype.matches = function(value) {};
    +
    +
    +/**
    + * Describes why the matcher failed.
    + *
    + * @param {*} value The value that didn't match.
    + * @param {string=} opt_description A partial description to which the reason
    + *     will be appended.
    + *
    + * @return {string} Description of why the matcher failed.
    + */
    +goog.labs.testing.Matcher.prototype.describe =
    +    function(value, opt_description) {};
    +
    +
    +/**
    + * Generates a Matcher from the ‘matches’ and ‘describe’ functions passed in.
    + *
    + * @param {!Function} matchesFunction The ‘matches’ function.
    + * @param {Function=} opt_describeFunction The ‘describe’ function.
    + * @return {!Function} The custom matcher.
    + */
    +goog.labs.testing.Matcher.makeMatcher =
    +    function(matchesFunction, opt_describeFunction) {
    +
    +  /**
    +   * @constructor
    +   * @implements {goog.labs.testing.Matcher}
    +   * @final
    +   */
    +  var matcherConstructor = function() {};
    +
    +  /** @override */
    +  matcherConstructor.prototype.matches = matchesFunction;
    +
    +  if (opt_describeFunction) {
    +    /** @override */
    +    matcherConstructor.prototype.describe = opt_describeFunction;
    +  }
    +
    +  return matcherConstructor;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher.js
    new file mode 100644
    index 0000000000..d9c67b339e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher.js
    @@ -0,0 +1,346 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in number matchers like lessThan,
    + * greaterThan, etc.
    + */
    +
    +
    +goog.provide('goog.labs.testing.CloseToMatcher');
    +goog.provide('goog.labs.testing.EqualToMatcher');
    +goog.provide('goog.labs.testing.GreaterThanEqualToMatcher');
    +goog.provide('goog.labs.testing.GreaterThanMatcher');
    +goog.provide('goog.labs.testing.LessThanEqualToMatcher');
    +goog.provide('goog.labs.testing.LessThanMatcher');
    +
    +
    +goog.require('goog.asserts');
    +goog.require('goog.labs.testing.Matcher');
    +
    +
    +
    +/**
    + * The GreaterThan matcher.
    + *
    + * @param {number} value The value to compare.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.GreaterThanMatcher = function(value) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input value is greater than the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.GreaterThanMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue > this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.GreaterThanMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not greater than ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The lessThan matcher.
    + *
    + * @param {number} value The value to compare.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.LessThanMatcher = function(value) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if the input value is less than the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.LessThanMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue < this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.LessThanMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not less than ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The GreaterThanEqualTo matcher.
    + *
    + * @param {number} value The value to compare.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.GreaterThanEqualToMatcher = function(value) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if the input value is greater than equal to the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.GreaterThanEqualToMatcher.prototype.matches =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue >= this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.GreaterThanEqualToMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not greater than equal to ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The LessThanEqualTo matcher.
    + *
    + * @param {number} value The value to compare.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.LessThanEqualToMatcher = function(value) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if the input value is less than or equal to the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.LessThanEqualToMatcher.prototype.matches =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue <= this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.LessThanEqualToMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not less than equal to ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The EqualTo matcher.
    + *
    + * @param {number} value The value to compare.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.EqualToMatcher = function(value) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if the input value is equal to the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.EqualToMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue === this.value_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.EqualToMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not equal to ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The CloseTo matcher.
    + *
    + * @param {number} value The value to compare.
    + * @param {number} range The range to check within.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.CloseToMatcher = function(value, range) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.value_ = value;
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.range_ = range;
    +};
    +
    +
    +/**
    + * Determines if input value is within a certain range of the expected value.
    + *
    + * @override
    + */
    +goog.labs.testing.CloseToMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return Math.abs(this.value_ - actualValue) < this.range_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.CloseToMatcher.prototype.describe =
    +    function(actualValue) {
    +  goog.asserts.assertNumber(actualValue);
    +  return actualValue + ' is not close to(' + this.range_ + ') ' + this.value_;
    +};
    +
    +
    +/**
    + * @param {number} value The expected value.
    + *
    + * @return {!goog.labs.testing.GreaterThanMatcher} A GreaterThanMatcher.
    + */
    +function greaterThan(value) {
    +  return new goog.labs.testing.GreaterThanMatcher(value);
    +}
    +
    +
    +/**
    + * @param {number} value The expected value.
    + *
    + * @return {!goog.labs.testing.GreaterThanEqualToMatcher} A
    + *     GreaterThanEqualToMatcher.
    + */
    +function greaterThanEqualTo(value) {
    +  return new goog.labs.testing.GreaterThanEqualToMatcher(value);
    +}
    +
    +
    +/**
    + * @param {number} value The expected value.
    + *
    + * @return {!goog.labs.testing.LessThanMatcher} A LessThanMatcher.
    + */
    +function lessThan(value) {
    +  return new goog.labs.testing.LessThanMatcher(value);
    +}
    +
    +
    +/**
    + * @param {number} value The expected value.
    + *
    + * @return {!goog.labs.testing.LessThanEqualToMatcher} A LessThanEqualToMatcher.
    + */
    +function lessThanEqualTo(value) {
    +  return new goog.labs.testing.LessThanEqualToMatcher(value);
    +}
    +
    +
    +/**
    + * @param {number} value The expected value.
    + *
    + * @return {!goog.labs.testing.EqualToMatcher} An EqualToMatcher.
    + */
    +function equalTo(value) {
    +  return new goog.labs.testing.EqualToMatcher(value);
    +}
    +
    +
    +/**
    + * @param {number} value The expected value.
    + * @param {number} range The maximum allowed difference from the expected value.
    + *
    + * @return {!goog.labs.testing.CloseToMatcher} A CloseToMatcher.
    + */
    +function closeTo(value, range) {
    +  return new goog.labs.testing.CloseToMatcher(value, range);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.html
    new file mode 100644
    index 0000000000..b1ec361fc0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - Number matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.numberMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.js
    new file mode 100644
    index 0000000000..4365b18e6a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/numbermatcher_test.js
    @@ -0,0 +1,71 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.numberMatcherTest');
    +goog.setTestOnly('goog.labs.testing.numberMatcherTest');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.LessThanMatcher');
    +goog.require('goog.labs.testing.MatcherError');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testGreaterThan() {
    +  goog.labs.testing.assertThat(4, greaterThan(3), '4 > 3');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(2, greaterThan(3));
    +  }, '2 > 3');
    +}
    +
    +function testGreaterThanEqualTo() {
    +  goog.labs.testing.assertThat(5, greaterThanEqualTo(4), '5 >= 4');
    +  goog.labs.testing.assertThat(5, greaterThanEqualTo(5), '5 >= 5');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(3, greaterThanEqualTo(5));
    +  }, '3 >= 5');
    +}
    +
    +function testLessThan() {
    +  goog.labs.testing.assertThat(6, lessThan(7), '6 < 7');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(7, lessThan(5));
    +  }, '7 < 5');
    +}
    +
    +function testLessThanEqualTo() {
    +  goog.labs.testing.assertThat(8, lessThanEqualTo(8), '8 <= 8');
    +  goog.labs.testing.assertThat(8, lessThanEqualTo(9), '8 <= 9');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(7, lessThanEqualTo(5));
    +  }, '7 <= 5');
    +}
    +
    +function testEqualTo() {
    +  goog.labs.testing.assertThat(7, equalTo(7), '7 equals 7');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(7, equalTo(5));
    +  }, '7 == 5');
    +}
    +
    +function testCloseTo() {
    +  goog.labs.testing.assertThat(7, closeTo(10, 4), '7 within range(4) of 10');
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(5, closeTo(10, 3));
    +  }, '5 within range(3) of 10');
    +}
    +
    +function assertMatcherError(callable, errorString) {
    +  var e = assertThrows(errorString || 'callable throws exception', callable);
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher.js
    new file mode 100644
    index 0000000000..a9474fe6d2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher.js
    @@ -0,0 +1,317 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in object matchers like equalsObject,
    + *     hasProperty, instanceOf, etc.
    + */
    +
    +
    +
    +goog.provide('goog.labs.testing.HasPropertyMatcher');
    +goog.provide('goog.labs.testing.InstanceOfMatcher');
    +goog.provide('goog.labs.testing.IsNullMatcher');
    +goog.provide('goog.labs.testing.IsNullOrUndefinedMatcher');
    +goog.provide('goog.labs.testing.IsUndefinedMatcher');
    +goog.provide('goog.labs.testing.ObjectEqualsMatcher');
    +
    +
    +goog.require('goog.labs.testing.Matcher');
    +
    +
    +
    +/**
    + * The Equals matcher.
    + *
    + * @param {!Object} expectedObject The expected object.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) {
    +  /**
    +   * @type {!Object}
    +   * @private
    +   */
    +  this.object_ = expectedObject;
    +};
    +
    +
    +/**
    + * Determines if two objects are the same.
    + *
    + * @override
    + */
    +goog.labs.testing.ObjectEqualsMatcher.prototype.matches =
    +    function(actualObject) {
    +  return actualObject === this.object_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.ObjectEqualsMatcher.prototype.describe =
    +    function(actualObject) {
    +  return 'Input object is not the same as the expected object.';
    +};
    +
    +
    +
    +/**
    + * The HasProperty matcher.
    + *
    + * @param {string} property Name of the property to test.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.HasPropertyMatcher = function(property) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.property_ = property;
    +};
    +
    +
    +/**
    + * Determines if an object has a property.
    + *
    + * @override
    + */
    +goog.labs.testing.HasPropertyMatcher.prototype.matches =
    +    function(actualObject) {
    +  return this.property_ in actualObject;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.HasPropertyMatcher.prototype.describe =
    +    function(actualObject) {
    +  return 'Object does not have property: ' + this.property_;
    +};
    +
    +
    +
    +/**
    + * The InstanceOf matcher.
    + *
    + * @param {!Object} object The expected class object.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.InstanceOfMatcher = function(object) {
    +  /**
    +   * @type {!Object}
    +   * @private
    +   */
    +  this.object_ = object;
    +};
    +
    +
    +/**
    + * Determines if an object is an instance of another object.
    + *
    + * @override
    + */
    +goog.labs.testing.InstanceOfMatcher.prototype.matches =
    +    function(actualObject) {
    +  return actualObject instanceof this.object_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.InstanceOfMatcher.prototype.describe =
    +    function(actualObject) {
    +  return 'Input object is not an instance of the expected object';
    +};
    +
    +
    +
    +/**
    + * The IsNullOrUndefined matcher.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.IsNullOrUndefinedMatcher = function() {};
    +
    +
    +/**
    + * Determines if input value is null or undefined.
    + *
    + * @override
    + */
    +goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches =
    +    function(actualValue) {
    +  return !goog.isDefAndNotNull(actualValue);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' is not null or undefined.';
    +};
    +
    +
    +
    +/**
    + * The IsNull matcher.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.IsNullMatcher = function() {};
    +
    +
    +/**
    + * Determines if input value is null.
    + *
    + * @override
    + */
    +goog.labs.testing.IsNullMatcher.prototype.matches =
    +    function(actualValue) {
    +  return goog.isNull(actualValue);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.IsNullMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' is not null.';
    +};
    +
    +
    +
    +/**
    + * The IsUndefined matcher.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.IsUndefinedMatcher = function() {};
    +
    +
    +/**
    + * Determines if input value is undefined.
    + *
    + * @override
    + */
    +goog.labs.testing.IsUndefinedMatcher.prototype.matches =
    +    function(actualValue) {
    +  return !goog.isDef(actualValue);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.IsUndefinedMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' is not undefined.';
    +};
    +
    +
    +/**
    + * Returns a matcher that matches objects that are equal to the input object.
    + * Equality in this case means the two objects are references to the same
    + * object.
    + *
    + * @param {!Object} object The expected object.
    + *
    + * @return {!goog.labs.testing.ObjectEqualsMatcher} A
    + *     ObjectEqualsMatcher.
    + */
    +function equalsObject(object) {
    +  return new goog.labs.testing.ObjectEqualsMatcher(object);
    +}
    +
    +
    +/**
    + * Returns a matcher that matches objects that contain the input property.
    + *
    + * @param {string} property The property name to check.
    + *
    + * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher.
    + */
    +function hasProperty(property) {
    +  return new goog.labs.testing.HasPropertyMatcher(property);
    +}
    +
    +
    +/**
    + * Returns a matcher that matches instances of the input class.
    + *
    + * @param {!Object} object The class object.
    + *
    + * @return {!goog.labs.testing.InstanceOfMatcher} A
    + *     InstanceOfMatcher.
    + */
    +function instanceOfClass(object) {
    +  return new goog.labs.testing.InstanceOfMatcher(object);
    +}
    +
    +
    +/**
    + * Returns a matcher that matches all null values.
    + *
    + * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher.
    + */
    +function isNull() {
    +  return new goog.labs.testing.IsNullMatcher();
    +}
    +
    +
    +/**
    + * Returns a matcher that matches all null and undefined values.
    + *
    + * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A
    + *     IsNullOrUndefinedMatcher.
    + */
    +function isNullOrUndefined() {
    +  return new goog.labs.testing.IsNullOrUndefinedMatcher();
    +}
    +
    +
    +/**
    + * Returns a matcher that matches undefined values.
    + *
    + * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher.
    + */
    +function isUndefined() {
    +  return new goog.labs.testing.IsUndefinedMatcher();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.html
    new file mode 100644
    index 0000000000..d21e2a12e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - Object matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.labs.testing.objectMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.js
    new file mode 100644
    index 0000000000..3781dfbb08
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/objectmatcher_test.js
    @@ -0,0 +1,95 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.objectMatcherTest');
    +goog.setTestOnly('goog.labs.testing.objectMatcherTest');
    +
    +goog.require('goog.labs.testing.MatcherError');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.ObjectEqualsMatcher');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testObjectEquals() {
    +  var obj1 = {x: 1};
    +  var obj2 = obj1;
    +  goog.labs.testing.assertThat(obj1, equalsObject(obj2), 'obj1 equals obj2');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat({x: 1}, equalsObject({}));
    +  }, 'equalsObject does not throw exception on failure');
    +}
    +
    +function testInstanceOf() {
    +  function expected() {
    +    this.x = 1;
    +  }
    +  var input = new expected();
    +  goog.labs.testing.assertThat(input, instanceOfClass(expected),
    +      'input is an instance of expected');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(5, instanceOfClass(function() {}));
    +  }, 'instanceOfClass does not throw exception on failure');
    +}
    +
    +function testHasProperty() {
    +  goog.labs.testing.assertThat({x: 1}, hasProperty('x'),
    +      '{x:1} has property x}');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat({x: 1}, hasProperty('y'));
    +  }, 'hasProperty does not throw exception on failure');
    +}
    +
    +function testIsNull() {
    +  goog.labs.testing.assertThat(null, isNull(), 'null is null');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(5, isNull());
    +  }, 'isNull does not throw exception on failure');
    +}
    +
    +function testIsNullOrUndefined() {
    +  var x;
    +  goog.labs.testing.assertThat(undefined, isNullOrUndefined(),
    +      'undefined is null or undefined');
    +  goog.labs.testing.assertThat(x, isNullOrUndefined(),
    +      'undefined is null or undefined');
    +  x = null;
    +  goog.labs.testing.assertThat(null, isNullOrUndefined(),
    +      'null is null or undefined');
    +  goog.labs.testing.assertThat(x, isNullOrUndefined(),
    +      'null is null or undefined');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(5, isNullOrUndefined());
    +  }, 'isNullOrUndefined does not throw exception on failure');
    +}
    +
    +function testIsUndefined() {
    +  var x;
    +  goog.labs.testing.assertThat(undefined, isUndefined(),
    +      'undefined is undefined');
    +  goog.labs.testing.assertThat(x, isUndefined(), 'undefined is undefined');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat(5, isUndefined());
    +  }, 'isUndefined does not throw exception on failure');
    +}
    +
    +function assertMatcherError(callable, errorString) {
    +  var e = assertThrows(errorString || 'callable throws exception', callable);
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher.js b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher.js
    new file mode 100644
    index 0000000000..2cc5b67ba6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher.js
    @@ -0,0 +1,415 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the built-in string matchers like containsString,
    + *     startsWith, endsWith, etc.
    + */
    +
    +
    +goog.provide('goog.labs.testing.ContainsStringMatcher');
    +goog.provide('goog.labs.testing.EndsWithMatcher');
    +goog.provide('goog.labs.testing.EqualToIgnoringWhitespaceMatcher');
    +goog.provide('goog.labs.testing.EqualsMatcher');
    +goog.provide('goog.labs.testing.RegexMatcher');
    +goog.provide('goog.labs.testing.StartsWithMatcher');
    +goog.provide('goog.labs.testing.StringContainsInOrderMatcher');
    +
    +
    +goog.require('goog.asserts');
    +goog.require('goog.labs.testing.Matcher');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * The ContainsString matcher.
    + *
    + * @param {string} value The expected string.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.ContainsStringMatcher = function(value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input string contains the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.ContainsStringMatcher.prototype.matches =
    +    function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  return goog.string.contains(actualValue, this.value_);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.ContainsStringMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' does not contain ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The EndsWith matcher.
    + *
    + * @param {string} value The expected string.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.EndsWithMatcher = function(value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input string ends with the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  return goog.string.endsWith(actualValue, this.value_);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.EndsWithMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' does not end with ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The EqualToIgnoringWhitespace matcher.
    + *
    + * @param {string} value The expected string.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input string contains the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches =
    +    function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  var string1 = goog.string.collapseWhitespace(actualValue);
    +
    +  return goog.string.caseInsensitiveCompare(this.value_, string1) === 0;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The Equals matcher.
    + *
    + * @param {string} value The expected string.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.EqualsMatcher = function(value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input string is equal to the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  return this.value_ === actualValue;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.EqualsMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' is not equal to ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The MatchesRegex matcher.
    + *
    + * @param {!RegExp} regex The expected regex.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.RegexMatcher = function(regex) {
    +  /**
    +   * @type {!RegExp}
    +   * @private
    +   */
    +  this.regex_ = regex;
    +};
    +
    +
    +/**
    + * Determines if input string is equal to the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.RegexMatcher.prototype.matches = function(
    +    actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  return this.regex_.test(actualValue);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.RegexMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' does not match ' + this.regex_;
    +};
    +
    +
    +
    +/**
    + * The StartsWith matcher.
    + *
    + * @param {string} value The expected string.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.StartsWithMatcher = function(value) {
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Determines if input string starts with the expected string.
    + *
    + * @override
    + */
    +goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  return goog.string.startsWith(actualValue, this.value_);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.StartsWithMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' does not start with ' + this.value_;
    +};
    +
    +
    +
    +/**
    + * The StringContainsInOrdermatcher.
    + *
    + * @param {Array<string>} values The expected string values.
    + *
    + * @constructor
    + * @struct
    + * @implements {goog.labs.testing.Matcher}
    + * @final
    + */
    +goog.labs.testing.StringContainsInOrderMatcher = function(values) {
    +  /**
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.values_ = values;
    +};
    +
    +
    +/**
    + * Determines if input string contains, in order, the expected array of strings.
    + *
    + * @override
    + */
    +goog.labs.testing.StringContainsInOrderMatcher.prototype.matches =
    +    function(actualValue) {
    +  goog.asserts.assertString(actualValue);
    +  var currentIndex, previousIndex = 0;
    +  for (var i = 0; i < this.values_.length; i++) {
    +    currentIndex = goog.string.contains(actualValue, this.values_[i]);
    +    if (currentIndex < 0 || currentIndex < previousIndex) {
    +      return false;
    +    }
    +    previousIndex = currentIndex;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.labs.testing.StringContainsInOrderMatcher.prototype.describe =
    +    function(actualValue) {
    +  return actualValue + ' does not contain the expected values in order.';
    +};
    +
    +
    +/**
    + * Matches a string containing the given string.
    + *
    + * @param {string} value The expected value.
    + *
    + * @return {!goog.labs.testing.ContainsStringMatcher} A
    + *     ContainsStringMatcher.
    + */
    +function containsString(value) {
    +  return new goog.labs.testing.ContainsStringMatcher(value);
    +}
    +
    +
    +/**
    + * Matches a string that ends with the given string.
    + *
    + * @param {string} value The expected value.
    + *
    + * @return {!goog.labs.testing.EndsWithMatcher} A
    + *     EndsWithMatcher.
    + */
    +function endsWith(value) {
    +  return new goog.labs.testing.EndsWithMatcher(value);
    +}
    +
    +
    +/**
    + * Matches a string that equals (ignoring whitespace) the given string.
    + *
    + * @param {string} value The expected value.
    + *
    + * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A
    + *     EqualToIgnoringWhitespaceMatcher.
    + */
    +function equalToIgnoringWhitespace(value) {
    +  return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value);
    +}
    +
    +
    +/**
    + * Matches a string that equals the given string.
    + *
    + * @param {string} value The expected value.
    + *
    + * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher.
    + */
    +function equals(value) {
    +  return new goog.labs.testing.EqualsMatcher(value);
    +}
    +
    +
    +/**
    + * Matches a string against a regular expression.
    + *
    + * @param {!RegExp} regex The expected regex.
    + *
    + * @return {!goog.labs.testing.RegexMatcher} A RegexMatcher.
    + */
    +function matchesRegex(regex) {
    +  return new goog.labs.testing.RegexMatcher(regex);
    +}
    +
    +
    +/**
    + * Matches a string that starts with the given string.
    + *
    + * @param {string} value The expected value.
    + *
    + * @return {!goog.labs.testing.StartsWithMatcher} A
    + *     StartsWithMatcher.
    + */
    +function startsWith(value) {
    +  return new goog.labs.testing.StartsWithMatcher(value);
    +}
    +
    +
    +/**
    + * Matches a string that contains the given strings in order.
    + *
    + * @param {Array<string>} values The expected value.
    + *
    + * @return {!goog.labs.testing.StringContainsInOrderMatcher} A
    + *     StringContainsInOrderMatcher.
    + */
    +function stringContainsInOrder(values) {
    +  return new goog.labs.testing.StringContainsInOrderMatcher(values);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.html b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.html
    new file mode 100644
    index 0000000000..2fd5b5532f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - String matchers
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.labs.testing.stringMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.js b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.js
    new file mode 100644
    index 0000000000..b4692bb431
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/testing/stringmatcher_test.js
    @@ -0,0 +1,92 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.labs.testing.stringMatcherTest');
    +goog.setTestOnly('goog.labs.testing.stringMatcherTest');
    +
    +goog.require('goog.labs.testing.MatcherError');
    +/** @suppress {extraRequire} */
    +goog.require('goog.labs.testing.StringContainsInOrderMatcher');
    +goog.require('goog.labs.testing.assertThat');
    +goog.require('goog.testing.jsunit');
    +
    +function testContainsString() {
    +  goog.labs.testing.assertThat('hello', containsString('ell'),
    +      'hello contains ell');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('hello', containsString('world!'));
    +  }, 'containsString should throw exception when it fails');
    +}
    +
    +function testEndsWith() {
    +  goog.labs.testing.assertThat('hello', endsWith('llo'), 'hello ends with llo');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('minutes', endsWith('midnight'));
    +  }, 'endsWith should throw exception when it fails');
    +}
    +
    +function testEqualToIgnoringWhitespace() {
    +  goog.labs.testing.assertThat('    h\n   EL L\tO',
    +      equalToIgnoringWhitespace('h el l o'),
    +      '"   h   EL L\tO   " is equal to "h el l o"');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('hybrid', equalToIgnoringWhitespace('theory'));
    +  }, 'equalToIgnoringWhitespace should throw exception when it fails');
    +}
    +
    +function testEquals() {
    +  goog.labs.testing.assertThat('hello', equals('hello'),
    +      'hello equals hello');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('thousand', equals('suns'));
    +  }, 'equals should throw exception when it fails');
    +}
    +
    +function testStartsWith() {
    +  goog.labs.testing.assertThat('hello', startsWith('hel'),
    +      'hello starts with hel');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('linkin', startsWith('park'));
    +  }, 'startsWith should throw exception when it fails');
    +}
    +
    +function testStringContainsInOrder() {
    +  goog.labs.testing.assertThat('hello',
    +      stringContainsInOrder(['h', 'el', 'el', 'l', 'o']),
    +      'hello contains in order: [h, el, l, o]');
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('hybrid', stringContainsInOrder(['hy', 'brid',
    +      'theory']));
    +  }, 'stringContainsInOrder should throw exception when it fails');
    +}
    +
    +function testMatchesRegex() {
    +  goog.labs.testing.assertThat('foobar', matchesRegex(/foobar/));
    +  goog.labs.testing.assertThat('foobar', matchesRegex(/oobar/));
    +
    +  assertMatcherError(function() {
    +    goog.labs.testing.assertThat('foo', matchesRegex(/^foobar$/));
    +  }, 'matchesRegex should throw exception when it fails');
    +}
    +
    +function assertMatcherError(callable, errorString) {
    +  var e = assertThrows(errorString || 'callable throws exception', callable);
    +  assertTrue(e instanceof goog.labs.testing.MatcherError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/browser.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser.js
    new file mode 100644
    index 0000000000..7c5a229205
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser.js
    @@ -0,0 +1,310 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Closure user agent detection (Browser).
    + * @see <a href="http://www.useragentstring.com/">User agent strings</a>
    + * For more information on rendering engine, platform, or device see the other
    + * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,
    + * goog.labs.userAgent.device respectively.)
    + *
    + * @author martone@google.com (Andy Martone)
    + */
    +
    +goog.provide('goog.labs.userAgent.browser');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Opera.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchOpera_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Opera') ||
    +      goog.labs.userAgent.util.matchUserAgent('OPR');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is IE.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchIE_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Trident') ||
    +      goog.labs.userAgent.util.matchUserAgent('MSIE');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Firefox.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchFirefox_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Firefox');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Safari.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchSafari_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Safari') &&
    +      !goog.labs.userAgent.util.matchUserAgent('Chrome') &&
    +      !goog.labs.userAgent.util.matchUserAgent('CriOS') &&
    +      !goog.labs.userAgent.util.matchUserAgent('Android');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
    + *     iOS browser).
    + * @private
    + */
    +goog.labs.userAgent.browser.matchCoast_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Coast');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is iOS Webview.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchIosWebview_ = function() {
    +  // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
    +  // WebKit-based iOS browser, Coast.
    +  return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
    +          goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
    +      !goog.labs.userAgent.browser.matchSafari_() &&
    +      !goog.labs.userAgent.browser.matchChrome_() &&
    +      !goog.labs.userAgent.browser.matchCoast_() &&
    +      goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Chrome.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchChrome_ = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Chrome') ||
    +      goog.labs.userAgent.util.matchUserAgent('CriOS');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is the Android browser.
    + * @private
    + */
    +goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
    +  // Android can appear in the user agent string for Chrome on Android.
    +  // This is not the Android standalone browser if it does.
    +  return !goog.labs.userAgent.browser.isChrome() &&
    +      goog.labs.userAgent.util.matchUserAgent('Android');
    +
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Opera.
    + */
    +goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is IE.
    + */
    +goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Firefox.
    + */
    +goog.labs.userAgent.browser.isFirefox =
    +    goog.labs.userAgent.browser.matchFirefox_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Safari.
    + */
    +goog.labs.userAgent.browser.isSafari =
    +    goog.labs.userAgent.browser.matchSafari_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
    + *     iOS browser).
    + */
    +goog.labs.userAgent.browser.isCoast =
    +    goog.labs.userAgent.browser.matchCoast_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is iOS Webview.
    + */
    +goog.labs.userAgent.browser.isIosWebview =
    +    goog.labs.userAgent.browser.matchIosWebview_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is Chrome.
    + */
    +goog.labs.userAgent.browser.isChrome =
    +    goog.labs.userAgent.browser.matchChrome_;
    +
    +
    +/**
    + * @return {boolean} Whether the user's browser is the Android browser.
    + */
    +goog.labs.userAgent.browser.isAndroidBrowser =
    +    goog.labs.userAgent.browser.matchAndroidBrowser_;
    +
    +
    +/**
    + * For more information, see:
    + * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
    + * @return {boolean} Whether the user's browser is Silk.
    + */
    +goog.labs.userAgent.browser.isSilk = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Silk');
    +};
    +
    +
    +/**
    + * @return {string} The browser version or empty string if version cannot be
    + *     determined. Note that for Internet Explorer, this returns the version of
    + *     the browser, not the version of the rendering engine. (IE 8 in
    + *     compatibility mode will return 8.0 rather than 7.0. To determine the
    + *     rendering engine version, look at document.documentMode instead. See
    + *     http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
    + *     details.)
    + */
    +goog.labs.userAgent.browser.getVersion = function() {
    +  var userAgentString = goog.labs.userAgent.util.getUserAgent();
    +  // Special case IE since IE's version is inside the parenthesis and
    +  // without the '/'.
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
    +  }
    +
    +  var versionTuples = goog.labs.userAgent.util.extractVersionTuples(
    +      userAgentString);
    +
    +  // Construct a map for easy lookup.
    +  var versionMap = {};
    +  goog.array.forEach(versionTuples, function(tuple) {
    +    // Note that the tuple is of length three, but we only care about the
    +    // first two.
    +    var key = tuple[0];
    +    var value = tuple[1];
    +    versionMap[key] = value;
    +  });
    +
    +  var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
    +
    +  // Gives the value with the first key it finds, otherwise empty string.
    +  function lookUpValueWithKeys(keys) {
    +    var key = goog.array.find(keys, versionMapHasKey);
    +    return versionMap[key] || '';
    +  }
    +
    +  // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
    +  // See
    +  // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
    +  if (goog.labs.userAgent.browser.isOpera()) {
    +    // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
    +    // Opera uses 'OPR' for more recent UAs.
    +    return lookUpValueWithKeys(['Version', 'Opera', 'OPR']);
    +  }
    +
    +  if (goog.labs.userAgent.browser.isChrome()) {
    +    return lookUpValueWithKeys(['Chrome', 'CriOS']);
    +  }
    +
    +  // Usually products browser versions are in the third tuple after "Mozilla"
    +  // and the engine.
    +  var tuple = versionTuples[2];
    +  return tuple && tuple[1] || '';
    +};
    +
    +
    +/**
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the browser version is higher or the same as the
    + *     given version.
    + */
    +goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
    +  return goog.string.compareVersions(goog.labs.userAgent.browser.getVersion(),
    +                                     version) >= 0;
    +};
    +
    +
    +/**
    + * Determines IE version. More information:
    + * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
    + * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
    + * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
    + * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
    + *
    + * @param {string} userAgent the User-Agent.
    + * @return {string}
    + * @private
    + */
    +goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
    +  // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
    +  // bug. Example UA:
    +  // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
    +  // like Gecko.
    +  // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
    +  var rv = /rv: *([\d\.]*)/.exec(userAgent);
    +  if (rv && rv[1]) {
    +    return rv[1];
    +  }
    +
    +  var version = '';
    +  var msie = /MSIE +([\d\.]+)/.exec(userAgent);
    +  if (msie && msie[1]) {
    +    // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
    +    // case, use the Trident version to determine the version of IE. For more
    +    // details, see the links above.
    +    var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
    +    if (msie[1] == '7.0') {
    +      if (tridentVersion && tridentVersion[1]) {
    +        switch (tridentVersion[1]) {
    +          case '4.0':
    +            version = '8.0';
    +            break;
    +          case '5.0':
    +            version = '9.0';
    +            break;
    +          case '6.0':
    +            version = '10.0';
    +            break;
    +          case '7.0':
    +            version = '11.0';
    +            break;
    +        }
    +      } else {
    +        version = '7.0';
    +      }
    +    } else {
    +      version = msie[1];
    +    }
    +  }
    +  return version;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.html b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.html
    new file mode 100644
    index 0000000000..1181d1f5f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.userAgent.browser</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.labs.userAgent.browserTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.js
    new file mode 100644
    index 0000000000..68f0c754dc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/browser_test.js
    @@ -0,0 +1,291 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.userAgent.browser.
    + */
    +
    +goog.provide('goog.labs.userAgent.browserTest');
    +
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.userAgent.browserTest');
    +
    +function setUp() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +}
    +
    +function testOpera10() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_10);
    +  assertTrue(goog.labs.userAgent.browser.isOpera());
    +  assertVersion('10.00');
    +  assertVersionBetween('10.00', '10.10');
    +}
    +
    +function testOperaMac() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_MAC);
    +  assertTrue(goog.labs.userAgent.browser.isOpera());
    +  assertVersion('11.52');
    +  assertVersionBetween('11.50', '12.00');
    +}
    +
    +function testOperaLinux() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_LINUX);
    +  assertTrue(goog.labs.userAgent.browser.isOpera());
    +  assertVersion('11.50');
    +  assertVersionBetween('11.00', '12.00');
    +}
    +
    +function testOpera15() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_15);
    +  assertTrue(goog.labs.userAgent.browser.isOpera());
    +  assertVersion('15.0.1147.100');
    +  assertVersionBetween('15.00', '16.00');
    +}
    +
    +function testIE6() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_6);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('6.0');
    +  assertVersionBetween('5.0', '7.0');
    +}
    +
    +function testIE7() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_7);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('7.0');
    +}
    +
    +function testIE8() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_8);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('8.0');
    +  assertVersionBetween('7.0', '9.0');
    +}
    +
    +function testIE8Compatibility() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_8_COMPATIBILITY);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('8.0');
    +}
    +
    +function testIE9() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_9);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('9.0');
    +  assertVersionBetween('8.0', '10.0');
    +}
    +
    +function testIE9Compatibility() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_9_COMPATIBILITY);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('9.0');
    +}
    +
    +function testIE10() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_10);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('10.0');
    +  assertVersionBetween('10.0', '11.0');
    +}
    +
    +function testIE10Compatibility() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_10_COMPATIBILITY);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('10.0');
    +}
    +
    +function testIE10Mobile() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_10_MOBILE);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('10.0');
    +}
    +
    +function testIE11() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_11);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('11.0');
    +  assertVersionBetween('10.0', '12.0');
    +}
    +
    +function testIE11CompatibilityMSIE7() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_11_COMPATIBILITY_MSIE_7);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('11.0');
    +}
    +
    +function testIE11CompatibilityMSIE9() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_11_COMPATIBILITY_MSIE_9);
    +  assertTrue(goog.labs.userAgent.browser.isIE());
    +  assertVersion('11.0');
    +}
    +
    +function testFirefox19() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_19);
    +  assertTrue(goog.labs.userAgent.browser.isFirefox());
    +  assertVersion('19.0');
    +  assertVersionBetween('18.0', '20.0');
    +}
    +
    +function testFirefoxWindows() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_WINDOWS);
    +  assertTrue(goog.labs.userAgent.browser.isFirefox());
    +  assertVersion('14.0.1');
    +  assertVersionBetween('14.0', '15.0');
    +}
    +
    +function testFirefoxLinux() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_LINUX);
    +  assertTrue(goog.labs.userAgent.browser.isFirefox());
    +  assertVersion('15.0.1');
    +}
    +
    +function testChromeAndroid() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_ANDROID);
    +  assertTrue(goog.labs.userAgent.browser.isChrome());
    +  assertVersion('18.0.1025.133');
    +  assertVersionBetween('18.0', '19.0');
    +  assertVersionBetween('17.0', '18.1');
    +}
    +
    +function testChromeIphone() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_IPHONE);
    +  assertTrue(goog.labs.userAgent.browser.isChrome());
    +  assertVersion('22.0.1194.0');
    +  assertVersionBetween('22.0', '23.0');
    +  assertVersionBetween('22.0', '22.10');
    +}
    +
    +function testChromeMac() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_MAC);
    +  assertTrue(goog.labs.userAgent.browser.isChrome());
    +  assertVersion('24.0.1309.0');
    +  assertVersionBetween('24.0', '25.0');
    +  assertVersionBetween('24.0', '24.10');
    +}
    +
    +function testSafariIpad() {
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IPAD_6);
    +  assertTrue(goog.labs.userAgent.browser.isSafari());
    +  assertVersion('6.0');
    +  assertVersionBetween('5.1', '7.0');
    +}
    +
    +function testSafari6() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_6);
    +  assertTrue(goog.labs.userAgent.browser.isSafari());
    +  assertVersion('6.0');
    +  assertVersionBetween('6.0', '7.0');
    +}
    +
    +function testSafariIphone() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPHONE_6);
    +  assertTrue(goog.labs.userAgent.browser.isSafari());
    +  assertVersion('6.0');
    +  assertVersionBetween('5.0', '7.0');
    +}
    +
    +function testCoast() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.COAST);
    +  assertTrue(goog.labs.userAgent.browser.isCoast());
    +}
    +
    +function testWebviewIOS() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.WEBVIEW_IPHONE);
    +  assertTrue(goog.labs.userAgent.browser.isIosWebview());
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.WEBVIEW_IPAD);
    +  assertTrue(goog.labs.userAgent.browser.isIosWebview());
    +}
    +
    +function testAndroidBrowser235() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.ANDROID_BROWSER_235);
    +  assertTrue(goog.labs.userAgent.browser.isAndroidBrowser());
    +  assertVersion('4.0');
    +  assertVersionBetween('3.0', '5.0');
    +}
    +
    +function testAndroidBrowser403() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.ANDROID_BROWSER_403);
    +  assertTrue(goog.labs.userAgent.browser.isAndroidBrowser());
    +  assertVersion('4.0');
    +  assertVersionBetween('3.0', '5.0');
    +}
    +
    +function testAndroidBrowser233() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.ANDROID_BROWSER_233);
    +  assertTrue(goog.labs.userAgent.browser.isAndroidBrowser());
    +  assertVersion('4.0');
    +  assertVersionBetween('3.0', '5.0');
    +}
    +
    +function testAndroidWebView411() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.ANDROID_WEB_VIEW_4_1_1);
    +  assertFalse(goog.labs.userAgent.browser.isChrome());
    +  assertTrue(goog.labs.userAgent.browser.isAndroidBrowser());
    +  assertVersion('4.0');
    +  assertVersionBetween('3.0', '5.0');
    +}
    +
    +function testAndroidWebView44() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.ANDROID_WEB_VIEW_4_4);
    +  assertTrue(goog.labs.userAgent.browser.isChrome());
    +  assertFalse(goog.labs.userAgent.browser.isAndroidBrowser());
    +  assertVersion('30.0.0.0');
    +  assertVersionBetween('29.0', '31.0');
    +}
    +
    +function testSilk() {
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.KINDLE_FIRE);
    +  assertTrue(goog.labs.userAgent.browser.isSilk());
    +  assertVersion('2.1');
    +}
    +
    +function assertVersion(version) {
    +  assertEquals(version, goog.labs.userAgent.browser.getVersion());
    +}
    +
    +function assertVersionBetween(lowVersion, highVersion) {
    +  assertTrue(goog.labs.userAgent.browser.isVersionOrHigher(lowVersion));
    +  assertFalse(goog.labs.userAgent.browser.isVersionOrHigher(highVersion));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/device.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/device.js
    new file mode 100644
    index 0000000000..f1c2b76c6f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/device.js
    @@ -0,0 +1,65 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Closure user device detection (based on user agent).
    + * @see http://en.wikipedia.org/wiki/User_agent
    + * For more information on browser brand, platform, or engine see the other
    + * sub-namespaces in goog.labs.userAgent (browser, platform, and engine).
    + *
    + */
    +
    +goog.provide('goog.labs.userAgent.device');
    +
    +goog.require('goog.labs.userAgent.util');
    +
    +
    +/**
    + * Currently we detect the iPhone, iPod and Android mobiles (devices that have
    + * both Android and Mobile in the user agent string).
    + *
    + * @return {boolean} Whether the user is using a mobile device.
    + */
    +goog.labs.userAgent.device.isMobile = function() {
    +  return !goog.labs.userAgent.device.isTablet() &&
    +      (goog.labs.userAgent.util.matchUserAgent('iPod') ||
    +       goog.labs.userAgent.util.matchUserAgent('iPhone') ||
    +       goog.labs.userAgent.util.matchUserAgent('Android') ||
    +       goog.labs.userAgent.util.matchUserAgent('IEMobile'));
    +};
    +
    +
    +/**
    + * Currently we detect Kindle Fire, iPad, and Android tablets (devices that have
    + * Android but not Mobile in the user agent string).
    + *
    + * @return {boolean} Whether the user is using a tablet.
    + */
    +goog.labs.userAgent.device.isTablet = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('iPad') ||
    +      (goog.labs.userAgent.util.matchUserAgent('Android') &&
    +       !goog.labs.userAgent.util.matchUserAgent('Mobile')) ||
    +      goog.labs.userAgent.util.matchUserAgent('Silk');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user is using a desktop computer (which we
    + *     assume to be the case if they are not using either a mobile or tablet
    + *     device).
    + */
    +goog.labs.userAgent.device.isDesktop = function() {
    +  return !goog.labs.userAgent.device.isMobile() &&
    +      !goog.labs.userAgent.device.isTablet();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.html b/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.html
    new file mode 100644
    index 0000000000..887333356c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.userAgent</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.labs.userAgent.deviceTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.js
    new file mode 100644
    index 0000000000..4bf8e7da08
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/device_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.userAgent.device.
    + */
    +
    +goog.provide('goog.labs.userAgent.deviceTest');
    +
    +goog.require('goog.labs.userAgent.device');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.userAgent.deviceTest');
    +
    +function setUp() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +}
    +
    +function testMobile() {
    +  assertIsMobile(goog.labs.userAgent.testAgents.ANDROID_BROWSER_235);
    +  assertIsMobile(goog.labs.userAgent.testAgents.CHROME_ANDROID);
    +  assertIsMobile(goog.labs.userAgent.testAgents.SAFARI_IPHONE_6);
    +  assertIsMobile(goog.labs.userAgent.testAgents.IE_10_MOBILE);
    +}
    +
    +function testTablet() {
    +  assertIsTablet(goog.labs.userAgent.testAgents.CHROME_ANDROID_TABLET);
    +  assertIsTablet(goog.labs.userAgent.testAgents.KINDLE_FIRE);
    +  assertIsTablet(goog.labs.userAgent.testAgents.IPAD_6);
    +}
    +
    +function testDesktop() {
    +  assertIsDesktop(goog.labs.userAgent.testAgents.CHROME_25);
    +  assertIsDesktop(goog.labs.userAgent.testAgents.OPERA_10);
    +  assertIsDesktop(goog.labs.userAgent.testAgents.FIREFOX_19);
    +  assertIsDesktop(goog.labs.userAgent.testAgents.IE_9);
    +  assertIsDesktop(goog.labs.userAgent.testAgents.IE_10);
    +  assertIsDesktop(goog.labs.userAgent.testAgents.IE_11);
    +}
    +
    +function assertIsMobile(uaString) {
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.device.isMobile());
    +  assertFalse(goog.labs.userAgent.device.isTablet());
    +  assertFalse(goog.labs.userAgent.device.isDesktop());
    +}
    +
    +function assertIsTablet(uaString) {
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.device.isTablet());
    +  assertFalse(goog.labs.userAgent.device.isMobile());
    +  assertFalse(goog.labs.userAgent.device.isDesktop());
    +}
    +
    +function assertIsDesktop(uaString) {
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.device.isDesktop());
    +  assertFalse(goog.labs.userAgent.device.isMobile());
    +  assertFalse(goog.labs.userAgent.device.isTablet());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/engine.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine.js
    new file mode 100644
    index 0000000000..8aa1bfa1d5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine.js
    @@ -0,0 +1,130 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Closure user agent detection.
    + * @see http://en.wikipedia.org/wiki/User_agent
    + * For more information on browser brand, platform, or device see the other
    + * sub-namespaces in goog.labs.userAgent (browser, platform, and device).
    + *
    + */
    +
    +goog.provide('goog.labs.userAgent.engine');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @return {boolean} Whether the rendering engine is Presto.
    + */
    +goog.labs.userAgent.engine.isPresto = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Presto');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the rendering engine is Trident.
    + */
    +goog.labs.userAgent.engine.isTrident = function() {
    +  // IE only started including the Trident token in IE8.
    +  return goog.labs.userAgent.util.matchUserAgent('Trident') ||
    +      goog.labs.userAgent.util.matchUserAgent('MSIE');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the rendering engine is WebKit.
    + */
    +goog.labs.userAgent.engine.isWebKit = function() {
    +  return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the rendering engine is Gecko.
    + */
    +goog.labs.userAgent.engine.isGecko = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Gecko') &&
    +      !goog.labs.userAgent.engine.isWebKit() &&
    +      !goog.labs.userAgent.engine.isTrident();
    +};
    +
    +
    +/**
    + * @return {string} The rendering engine's version or empty string if version
    + *     can't be determined.
    + */
    +goog.labs.userAgent.engine.getVersion = function() {
    +  var userAgentString = goog.labs.userAgent.util.getUserAgent();
    +  if (userAgentString) {
    +    var tuples = goog.labs.userAgent.util.extractVersionTuples(
    +        userAgentString);
    +
    +    var engineTuple = tuples[1];
    +    if (engineTuple) {
    +      // In Gecko, the version string is either in the browser info or the
    +      // Firefox version.  See Gecko user agent string reference:
    +      // http://goo.gl/mULqa
    +      if (engineTuple[0] == 'Gecko') {
    +        return goog.labs.userAgent.engine.getVersionForKey_(
    +            tuples, 'Firefox');
    +      }
    +
    +      return engineTuple[1];
    +    }
    +
    +    // IE has only one version identifier, and the Trident version is
    +    // specified in the parenthetical.
    +    var browserTuple = tuples[0];
    +    var info;
    +    if (browserTuple && (info = browserTuple[2])) {
    +      var match = /Trident\/([^\s;]+)/.exec(info);
    +      if (match) {
    +        return match[1];
    +      }
    +    }
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the rendering engine version is higher or the same
    + *     as the given version.
    + */
    +goog.labs.userAgent.engine.isVersionOrHigher = function(version) {
    +  return goog.string.compareVersions(goog.labs.userAgent.engine.getVersion(),
    +                                     version) >= 0;
    +};
    +
    +
    +/**
    + * @param {!Array<!Array<string>>} tuples Version tuples.
    + * @param {string} key The key to look for.
    + * @return {string} The version string of the given key, if present.
    + *     Otherwise, the empty string.
    + * @private
    + */
    +goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {
    +  // TODO(nnaze): Move to util if useful elsewhere.
    +
    +  var pair = goog.array.find(tuples, function(pair) {
    +    return key == pair[0];
    +  });
    +
    +  return pair && pair[1] || '';
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.html b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.html
    new file mode 100644
    index 0000000000..6fc561cb4b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.userAgent</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.labs.userAgent.engineTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.js
    new file mode 100644
    index 0000000000..fc299c0955
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/engine_test.js
    @@ -0,0 +1,148 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.userAgent.engine.
    + */
    +
    +goog.provide('goog.labs.userAgent.engineTest');
    +
    +goog.require('goog.labs.userAgent.engine');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.userAgent.engineTest');
    +
    +var testAgents = goog.labs.userAgent.testAgents;
    +
    +function setUp() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +}
    +
    +function assertVersion(version) {
    +  assertEquals(version, goog.labs.userAgent.engine.getVersion());
    +}
    +
    +function assertLowAndHighVersions(lowVersion, highVersion) {
    +  assertTrue(goog.labs.userAgent.engine.isVersionOrHigher(lowVersion));
    +  assertFalse(goog.labs.userAgent.engine.isVersionOrHigher(highVersion));
    +}
    +
    +function testPresto() {
    +  goog.labs.userAgent.util.setUserAgent(testAgents.OPERA_LINUX);
    +  assertTrue(goog.labs.userAgent.engine.isPresto());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('2.9.168');
    +  assertLowAndHighVersions('2.9', '2.10');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.OPERA_MAC);
    +  assertTrue(goog.labs.userAgent.engine.isPresto());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('2.9.168');
    +  assertLowAndHighVersions('2.9', '2.10');
    +}
    +
    +function testTrident() {
    +  goog.labs.userAgent.util.setUserAgent(testAgents.IE_6);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.IE_10);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('6.0');
    +  assertLowAndHighVersions('6.0', '7.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.IE_8);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('4.0');
    +  assertLowAndHighVersions('4.0', '5.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.IE_9_COMPATIBILITY);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('5.0');
    +  assertLowAndHighVersions('5.0', '6.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(goog.labs.userAgent.testAgents.IE_11);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('7.0');
    +  assertLowAndHighVersions('6.0', '8.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_10_MOBILE);
    +  assertTrue(goog.labs.userAgent.engine.isTrident());
    +  assertVersion('6.0');
    +}
    +
    +function testWebKit() {
    +  goog.labs.userAgent.util.setUserAgent(testAgents.ANDROID_BROWSER_235);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('533.1');
    +  assertLowAndHighVersions('533.0', '534.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.ANDROID_BROWSER_403_ALT);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('534.30');
    +  assertLowAndHighVersions('533.0', '535.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.CHROME_25);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('535.8');
    +  assertLowAndHighVersions('535.0', '536.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.SAFARI_6);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('536.25');
    +  assertLowAndHighVersions('536.0', '537.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.SAFARI_IPHONE_6);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('536.26');
    +  assertLowAndHighVersions('536.0', '537.0');
    +}
    +
    +function testOpera15() {
    +  goog.labs.userAgent.util.setUserAgent(testAgents.OPERA_15);
    +  assertTrue(goog.labs.userAgent.engine.isWebKit());
    +  assertFalse(goog.labs.userAgent.engine.isPresto());
    +  assertVersion('537.36');
    +}
    +
    +function testGecko() {
    +  goog.labs.userAgent.util.setUserAgent(testAgents.FIREFOX_LINUX);
    +  assertTrue(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('15.0.1');
    +  assertLowAndHighVersions('14.0', '16.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.FIREFOX_19);
    +  assertTrue(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('19.0');
    +  assertLowAndHighVersions('18.0', '20.0');
    +
    +  goog.labs.userAgent.util.setUserAgent(testAgents.FIREFOX_WINDOWS);
    +  assertTrue(goog.labs.userAgent.engine.isGecko());
    +  assertVersion('14.0.1');
    +  assertLowAndHighVersions('14.0', '15.0');
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/platform.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform.js
    new file mode 100644
    index 0000000000..ece7c07ea1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform.js
    @@ -0,0 +1,160 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Closure user agent platform detection.
    + * @see <a href="http://www.useragentstring.com/">User agent strings</a>
    + * For more information on browser brand, rendering engine, or device see the
    + * other sub-namespaces in goog.labs.userAgent (browser, engine, and device
    + * respectively).
    + *
    + */
    +
    +goog.provide('goog.labs.userAgent.platform');
    +
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @return {boolean} Whether the platform is Android.
    + */
    +goog.labs.userAgent.platform.isAndroid = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Android');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is iPod.
    + */
    +goog.labs.userAgent.platform.isIpod = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('iPod');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is iPhone.
    + */
    +goog.labs.userAgent.platform.isIphone = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('iPhone') &&
    +      !goog.labs.userAgent.util.matchUserAgent('iPod') &&
    +      !goog.labs.userAgent.util.matchUserAgent('iPad');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is iPad.
    + */
    +goog.labs.userAgent.platform.isIpad = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('iPad');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is iOS.
    + */
    +goog.labs.userAgent.platform.isIos = function() {
    +  return goog.labs.userAgent.platform.isIphone() ||
    +      goog.labs.userAgent.platform.isIpad() ||
    +      goog.labs.userAgent.platform.isIpod();
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is Mac.
    + */
    +goog.labs.userAgent.platform.isMacintosh = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Macintosh');
    +};
    +
    +
    +/**
    + * Note: ChromeOS is not considered to be Linux as it does not report itself
    + * as Linux in the user agent string.
    + * @return {boolean} Whether the platform is Linux.
    + */
    +goog.labs.userAgent.platform.isLinux = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Linux');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is Windows.
    + */
    +goog.labs.userAgent.platform.isWindows = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('Windows');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the platform is ChromeOS.
    + */
    +goog.labs.userAgent.platform.isChromeOS = function() {
    +  return goog.labs.userAgent.util.matchUserAgent('CrOS');
    +};
    +
    +
    +/**
    + * The version of the platform. We only determine the version for Windows,
    + * Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only
    + * look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given
    + * version 0.0.
    + *
    + * @return {string} The platform version or empty string if version cannot be
    + *     determined.
    + */
    +goog.labs.userAgent.platform.getVersion = function() {
    +  var userAgentString = goog.labs.userAgent.util.getUserAgent();
    +  var version = '', re;
    +  if (goog.labs.userAgent.platform.isWindows()) {
    +    re = /Windows (?:NT|Phone) ([0-9.]+)/;
    +    var match = re.exec(userAgentString);
    +    if (match) {
    +      version = match[1];
    +    } else {
    +      version = '0.0';
    +    }
    +  } else if (goog.labs.userAgent.platform.isIos()) {
    +    re = /(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/;
    +    var match = re.exec(userAgentString);
    +    // Report the version as x.y.z and not x_y_z
    +    version = match && match[1].replace(/_/g, '.');
    +  } else if (goog.labs.userAgent.platform.isMacintosh()) {
    +    re = /Mac OS X ([0-9_.]+)/;
    +    var match = re.exec(userAgentString);
    +    // Note: some old versions of Camino do not report an OSX version.
    +    // Default to 10.
    +    version = match ? match[1].replace(/_/g, '.') : '10';
    +  } else if (goog.labs.userAgent.platform.isAndroid()) {
    +    re = /Android\s+([^\);]+)(\)|;)/;
    +    var match = re.exec(userAgentString);
    +    version = match && match[1];
    +  } else if (goog.labs.userAgent.platform.isChromeOS()) {
    +    re = /(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/;
    +    var match = re.exec(userAgentString);
    +    version = match && match[1];
    +  }
    +  return version || '';
    +};
    +
    +
    +/**
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the browser version is higher or the same as the
    + *     given version.
    + */
    +goog.labs.userAgent.platform.isVersionOrHigher = function(version) {
    +  return goog.string.compareVersions(goog.labs.userAgent.platform.getVersion(),
    +                                     version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.html b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.html
    new file mode 100644
    index 0000000000..23d14b3398
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.userAgent.platform</title>
    +<script src="../../base.js"></script>
    +<script>
    +  goog.require('goog.labs.userAgent.platformTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.js
    new file mode 100644
    index 0000000000..1d11cd9630
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/platform_test.js
    @@ -0,0 +1,247 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.userAgent.platform.
    + */
    +
    +goog.provide('goog.labs.userAgent.platformTest');
    +
    +goog.require('goog.labs.userAgent.platform');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.userAgent.platformTest');
    +
    +function setUp() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +}
    +
    +function testAndroid() {
    +  var uaString = goog.labs.userAgent.testAgents.ANDROID_BROWSER_233;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isAndroid());
    +  assertVersion('2.3.3');
    +  assertVersionBetween('2.3.0', '2.3.5');
    +  assertVersionBetween('2.3', '2.4');
    +  assertVersionBetween('2', '3');
    +
    +  uaString = goog.labs.userAgent.testAgents.ANDROID_BROWSER_221;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isAndroid());
    +  assertVersion('2.2.1');
    +  assertVersionBetween('2.2.0', '2.2.5');
    +  assertVersionBetween('2.2', '2.3');
    +  assertVersionBetween('2', '3');
    +
    +  uaString = goog.labs.userAgent.testAgents.CHROME_ANDROID;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isAndroid());
    +  assertVersion('4.0.2');
    +  assertVersionBetween('4.0.0', '4.1.0');
    +  assertVersionBetween('4.0', '4.1');
    +  assertVersionBetween('4', '5');
    +}
    +
    +function testKindleFire() {
    +  uaString = goog.labs.userAgent.testAgents.KINDLE_FIRE;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isAndroid());
    +  assertVersion('4.0.3');
    +}
    +
    +function testIpod() {
    +  var uaString = goog.labs.userAgent.testAgents.SAFARI_IPOD;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIpod());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('');
    +}
    +
    +function testIphone() {
    +  var uaString = goog.labs.userAgent.testAgents.SAFARI_IPHONE_421;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIphone());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('4.2.1');
    +  assertVersionBetween('4', '5');
    +  assertVersionBetween('4.2', '4.3');
    +
    +  uaString = goog.labs.userAgent.testAgents.SAFARI_IPHONE_6;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIphone());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('6.0');
    +  assertVersionBetween('5', '7');
    +
    +  uaString = goog.labs.userAgent.testAgents.SAFARI_IPHONE_32;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIphone());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('3.2');
    +  assertVersionBetween('3', '4');
    +
    +  uaString = goog.labs.userAgent.testAgents.WEBVIEW_IPAD;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertFalse(goog.labs.userAgent.platform.isIphone());
    +  assertTrue(goog.labs.userAgent.platform.isIpad());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('6.0');
    +  assertVersionBetween('5', '7');
    +}
    +
    +function testIpad() {
    +  var uaString = goog.labs.userAgent.testAgents.IPAD_4;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIpad());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('3.2');
    +  assertVersionBetween('3', '4');
    +  assertVersionBetween('3.1', '4');
    +
    +  uaString = goog.labs.userAgent.testAgents.IPAD_5;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIpad());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('5.1');
    +  assertVersionBetween('5', '6');
    +
    +  uaString = goog.labs.userAgent.testAgents.IPAD_6;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isIpad());
    +  assertTrue(goog.labs.userAgent.platform.isIos());
    +  assertVersion('6.0');
    +  assertVersionBetween('5', '7');
    +}
    +
    +function testMac() {
    +  var uaString = goog.labs.userAgent.testAgents.CHROME_MAC;
    +  var platform = 'IntelMac';
    +  goog.labs.userAgent.util.setUserAgent(uaString, platform);
    +  assertTrue(goog.labs.userAgent.platform.isMacintosh());
    +  assertVersion('10.8.2');
    +  assertVersionBetween('10', '11');
    +  assertVersionBetween('10.8', '10.9');
    +  assertVersionBetween('10.8.1', '10.8.3');
    +
    +  uaString = goog.labs.userAgent.testAgents.OPERA_MAC;
    +  goog.labs.userAgent.util.setUserAgent(uaString, platform);
    +  assertTrue(goog.labs.userAgent.platform.isMacintosh());
    +  assertVersion('10.6.8');
    +  assertVersionBetween('10', '11');
    +  assertVersionBetween('10.6', '10.7');
    +  assertVersionBetween('10.6.5', '10.7.0');
    +
    +  uaString = goog.labs.userAgent.testAgents.SAFARI_MAC;
    +  goog.labs.userAgent.util.setUserAgent(uaString, platform);
    +  assertTrue(goog.labs.userAgent.platform.isMacintosh());
    +  assertVersionBetween('10', '11');
    +  assertVersionBetween('10.6', '10.7');
    +  assertVersionBetween('10.6.5', '10.7.0');
    +
    +  uaString = goog.labs.userAgent.testAgents.FIREFOX_MAC;
    +  goog.labs.userAgent.util.setUserAgent(uaString, platform);
    +  assertTrue(goog.labs.userAgent.platform.isMacintosh());
    +  assertVersion('11.7.9');
    +  assertVersionBetween('11', '12');
    +  assertVersionBetween('11.7', '11.8');
    +  assertVersionBetween('11.7.9', '11.8.0');
    +}
    +
    +function testLinux() {
    +  var uaString = goog.labs.userAgent.testAgents.FIREFOX_LINUX;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isLinux());
    +  assertVersion('');
    +
    +  uaString = goog.labs.userAgent.testAgents.CHROME_LINUX;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isLinux());
    +  assertVersion('');
    +
    +  uaString = goog.labs.userAgent.testAgents.OPERA_LINUX;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isLinux());
    +  assertVersion('');
    +}
    +
    +function testWindows() {
    +  var uaString = goog.labs.userAgent.testAgents.SAFARI_WINDOWS;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('6.1');
    +  assertVersionBetween('6', '7');
    +
    +  uaString = goog.labs.userAgent.testAgents.IE_10;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('6.2');
    +  assertVersionBetween('6', '6.5');
    +
    +  uaString = goog.labs.userAgent.testAgents.CHROME_25;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('5.1');
    +  assertVersionBetween('5', '6');
    +
    +  uaString = goog.labs.userAgent.testAgents.FIREFOX_WINDOWS;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('6.1');
    +  assertVersionBetween('6', '7');
    +
    +  uaString = goog.labs.userAgent.testAgents.IE_11;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('6.3');
    +  assertVersionBetween('6', '6.5');
    +
    +  uaString = goog.labs.userAgent.testAgents.IE_10_MOBILE;
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isWindows());
    +  assertVersion('8.0');
    +}
    +
    +function testChromeOS() {
    +  var uaString = goog.labs.userAgent.testAgents.CHROME_OS_910;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isChromeOS());
    +  assertVersion('9.10.0');
    +  assertVersionBetween('9', '10');
    +
    +  uaString = goog.labs.userAgent.testAgents.CHROME_OS;
    +
    +  goog.labs.userAgent.util.setUserAgent(uaString);
    +  assertTrue(goog.labs.userAgent.platform.isChromeOS());
    +  assertVersion('3701.62.0');
    +  assertVersionBetween('3701', '3702');
    +}
    +
    +function assertVersion(version) {
    +  assertEquals(version, goog.labs.userAgent.platform.getVersion());
    +}
    +
    +function assertVersionBetween(lowVersion, highVersion) {
    +  assertTrue(goog.labs.userAgent.platform.isVersionOrHigher(lowVersion));
    +  assertFalse(goog.labs.userAgent.platform.isVersionOrHigher(highVersion));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/test_agents.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/test_agents.js
    new file mode 100644
    index 0000000000..e7df68d5af
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/test_agents.js
    @@ -0,0 +1,348 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the 'License');
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an 'AS-IS' BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Various User-Agent strings.
    + * See http://go/useragentexamples and http://www.useragentstring.com/ for
    + * examples.
    + *
    + * @author martone@google.com (Andy Martone)
    + */
    +
    +goog.provide('goog.labs.userAgent.testAgents');
    +goog.setTestOnly('goog.labs.userAgent.testAgents');
    +
    +goog.scope(function() {
    +var testAgents = goog.labs.userAgent.testAgents;
    +
    +
    +/** @const {string} */
    +testAgents.ANDROID_BROWSER_235 =
    +    'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; ' +
    +    'HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) ' +
    +    'Version/4.0 Mobile Safari/533.1';
    +
    +
    +/** @const {string} */
    +testAgents.ANDROID_BROWSER_221 =
    +    'Mozilla/5.0 (Linux; U; Android 2.2.1; en-ca; LG-P505R Build/FRG83)' +
    +    ' AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1';
    +
    +
    +/** @const {string} */
    +testAgents.ANDROID_BROWSER_233 =
    +    'Mozilla/5.0 (Linux; U; Android 2.3.3; en-us; HTC_DesireS_S510e' +
    +    ' Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0' +
    +    ' Mobile Safari/533.1';
    +
    +
    +/** @const {string} */
    +testAgents.ANDROID_BROWSER_403 =
    +    'Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K)' +
    +    ' AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30';
    +
    +
    +/** @const {string} */
    +// User agent retrieved from dremel queries for cases matching b/13222688
    +testAgents.ANDROID_BROWSER_403_ALT =
    +    'Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K)' +
    +    ' AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30';
    +
    +
    +// See https://developer.chrome.com/multidevice/user-agent
    +/** @const {string} */
    +testAgents.ANDROID_WEB_VIEW_4_1_1 =
    +    'Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) ' +
    +    'AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30';
    +
    +
    +// See https://developer.chrome.com/multidevice/user-agent
    +/** @const {string} */
    +testAgents.ANDROID_WEB_VIEW_4_4 =
    +    'Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) ' +
    +    'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 ' +
    +    'Chrome/30.0.0.0 Mobile Safari/537.36';
    +
    +
    +/** @const {string} */
    +testAgents.IE_6 =
    +    'Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1;' +
    +    '.NET CLR 2.0.50727)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_7 =
    +    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_8 =
    +    'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_8_COMPATIBILITY =
    +    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_9 =
    +    'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_9_COMPATIBILITY =
    +    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_10 =
    +    'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_10_COMPATIBILITY =
    +    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0)';
    +
    +
    +/**
    + * http://blogs.windows.com/windows_phone/b/wpdev/archive/2012/10/17/getting-websites-ready-for-internet-explorer-10-on-windows-phone-8.aspx
    + * @const {string}
    + */
    +testAgents.IE_10_MOBILE =
    +    'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; ' +
    +    'IEMobile/10.0; ARM; Touch; NOKIA; Lumia 820)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_11 =
    +    'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
    +
    +
    +/** @const {string} */
    +testAgents.IE_11_COMPATIBILITY_MSIE_7 =
    +    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; ' +
    +    '.NET4.0E; .NET4.0C)';
    +
    +
    +/** @const {string} */
    +testAgents.IE_11_COMPATIBILITY_MSIE_9 =
    +    'Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; ' +
    +    'rv:11.0) like Gecko';
    +
    +
    +/** @const {string} */
    +testAgents.FIREFOX_19 =
    +    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) ' +
    +    'Gecko/20100101 Firefox/19.0';
    +
    +
    +/** @const {string} */
    +testAgents.FIREFOX_LINUX =
    +    'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101' +
    +    ' Firefox/15.0.1';
    +
    +
    +/** @const {string} */
    +testAgents.FIREFOX_MAC =
    +    'Mozilla/6.0 (Macintosh; I; Intel Mac OS X 11_7_9; de-LI; rv:1.9b4)' +
    +    ' Gecko/2012010317 Firefox/10.0a4';
    +
    +
    +/** @const {string} */
    +testAgents.FIREFOX_WINDOWS =
    +    'Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507' +
    +    ' Firefox/14.0.1';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_6 =
    +    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) ' +
    +    'AppleWebKit/536.25 (KHTML, like Gecko) ' +
    +    'Version/6.0 Safari/536.25';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_IPHONE_32 =
    +    'Mozilla/5.0(iPhone; U; CPU iPhone OS 3_2 like Mac OS X; en-us)' +
    +    ' AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314' +
    +    ' Safari/531.21.10';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_IPHONE_421 =
    +    'Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru)' +
    +    ' AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a' +
    +    ' Safari/6533.18.5';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_IPHONE_431 =
    +    'Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_1 like Mac OS X; zh-tw)' +
    +    ' AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8G4' +
    +    ' Safari/6533.18.5';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_IPHONE_6 =
    +    'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X)' +
    +    ' AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e' +
    +    ' Safari/8536.25';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_IPOD =
    +    'Mozila/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1' +
    +    ' (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_MAC =
    +    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+' +
    +    ' (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2';
    +
    +
    +/** @const {string} */
    +testAgents.SAFARI_WINDOWS =
    +    'Mozilla/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit/533.20.25' +
    +    ' (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27';
    +
    +
    +/** @const {string} */
    +testAgents.COAST =
    +    'Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1' +
    +    ' (KHTML like Gecko) Coast/1.1.2.64598 Mobile/11B511 Safari/7534.48.3';
    +
    +
    +/** @const {string} */
    +testAgents.WEBVIEW_IPHONE =
    +    'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26' +
    +    ' (KHTML, like Gecko) Mobile/10A403';
    +
    +
    +/** @const {string} */
    +testAgents.WEBVIEW_IPAD =
    +    'Mozilla/5.0 (iPad; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26' +
    +    ' (KHTML, like Gecko) Mobile/10A403';
    +
    +
    +/** @const {string} */
    +testAgents.OPERA_10 =
    +    'Opera/9.80 (S60; SymbOS; Opera Mobi/447; U; en) ' +
    +    'Presto/2.4.18 Version/10.00';
    +
    +
    +/** @const {string} */
    +testAgents.OPERA_LINUX =
    +    'Opera/9.80 (X11; Linux x86_64; U; fr) Presto/2.9.168 Version/11.50';
    +
    +
    +/** @const {string} */
    +testAgents.OPERA_MAC =
    +    'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168' +
    +    ' Version/11.52';
    +
    +
    +/** @const {string} */
    +testAgents.OPERA_15 =
    +    'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 ' +
    +    '(KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.100';
    +
    +
    +/** @const {string} */
    +testAgents.IPAD_4 =
    +    'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)' +
    +    ' AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b' +
    +    ' Safari/531.21.10';
    +
    +
    +/** @const {string} */
    +testAgents.IPAD_5 =
    +    'Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X; en-us) AppleWebKit/534.46' +
    +    ' (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3';
    +
    +
    +/** @const {string} */
    +testAgents.IPAD_6 =
    +    'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) ' +
    +    'AppleWebKit/536.26 (KHTML, like Gecko) ' +
    +    'Version/6.0 Mobile/10A403 Safari/8536.25';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_25 =
    +    'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) ' +
    +    'AppleWebKit/535.8 (KHTML, like Gecko) ' +
    +    'Chrome/25.0.1000.10 Safari/535.8';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_ANDROID =
    +    'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) ' +
    +    'AppleWebKit/535.7 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile ' +
    +    'Safari/535.7';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_ANDROID_TABLET =
    +    'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) ' +
    +    'AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Safari/535.19';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_IPHONE =
    +    'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X; en-us) ' +
    +    'AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/22.0.1194.0 Mobile/11E53 ' +
    +    'Safari/7534.48.3';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_LINUX =
    +    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko)' +
    +    ' Chrome/26.0.1410.33 Safari/537.31';
    +
    +
    +/**
    + * We traditionally use Appversion to detect X11
    + * @const {string}
    + */
    +testAgents.CHROME_LINUX_APPVERVERSION =
    +    '5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko)' +
    +    ' Chrome/26.0.1410.33 Safari/537.31';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_MAC =
    +    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17' +
    +    ' (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_OS =
    +    'Mozilla/5.0 (X11; CrOS x86_64 3701.62.0) AppleWebKit/537.31 ' +
    +    '(KHTML, like Gecko) Chrome/26.0.1410.40 Safari/537.31';
    +
    +
    +/** @const {string} */
    +testAgents.CHROME_OS_910 =
    +    'Mozilla/5.0 (X11; U; CrOS i686 9.10.0; en-US) AppleWebKit/532.5' +
    +    ' (KHTML, like Gecko) Chrome/4.0.253.0 Safari/532.5';
    +
    +
    +/** @const {string} */
    +testAgents.KINDLE_FIRE =
    +    'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K)' +
    +    ' AppleWebKit/535.19 (KHTML, like Gecko) Silk/2.1 Mobile Safari/535.19' +
    +    ' Silk-Accelerated=true';
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/util.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/util.js
    new file mode 100644
    index 0000000000..ebba9b540c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/util.js
    @@ -0,0 +1,148 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities used by goog.labs.userAgent tools. These functions
    + * should not be used outside of goog.labs.userAgent.*.
    + *
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.labs.userAgent.util');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * Gets the native userAgent string from navigator if it exists.
    + * If navigator or navigator.userAgent string is missing, returns an empty
    + * string.
    + * @return {string}
    + * @private
    + */
    +goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
    +  var navigator = goog.labs.userAgent.util.getNavigator_();
    +  if (navigator) {
    +    var userAgent = navigator.userAgent;
    +    if (userAgent) {
    +      return userAgent;
    +    }
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Getter for the native navigator.
    + * This is a separate function so it can be stubbed out in testing.
    + * @return {Navigator}
    + * @private
    + */
    +goog.labs.userAgent.util.getNavigator_ = function() {
    +  return goog.global.navigator;
    +};
    +
    +
    +/**
    + * A possible override for applications which wish to not check
    + * navigator.userAgent but use a specified value for detection instead.
    + * @private {string}
    + */
    +goog.labs.userAgent.util.userAgent_ =
    +    goog.labs.userAgent.util.getNativeUserAgentString_();
    +
    +
    +/**
    + * Applications may override browser detection on the built in
    + * navigator.userAgent object by setting this string. Set to null to use the
    + * browser object instead.
    + * @param {?string=} opt_userAgent The User-Agent override.
    + */
    +goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
    +  goog.labs.userAgent.util.userAgent_ = opt_userAgent ||
    +      goog.labs.userAgent.util.getNativeUserAgentString_();
    +};
    +
    +
    +/**
    + * @return {string} The user agent string.
    + */
    +goog.labs.userAgent.util.getUserAgent = function() {
    +  return goog.labs.userAgent.util.userAgent_;
    +};
    +
    +
    +/**
    + * @param {string} str
    + * @return {boolean} Whether the user agent contains the given string, ignoring
    + *     case.
    + */
    +goog.labs.userAgent.util.matchUserAgent = function(str) {
    +  var userAgent = goog.labs.userAgent.util.getUserAgent();
    +  return goog.string.contains(userAgent, str);
    +};
    +
    +
    +/**
    + * @param {string} str
    + * @return {boolean} Whether the user agent contains the given string.
    + */
    +goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
    +  var userAgent = goog.labs.userAgent.util.getUserAgent();
    +  return goog.string.caseInsensitiveContains(userAgent, str);
    +};
    +
    +
    +/**
    + * Parses the user agent into tuples for each section.
    + * @param {string} userAgent
    + * @return {!Array<!Array<string>>} Tuples of key, version, and the contents
    + *     of the parenthetical.
    + */
    +goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
    +  // Matches each section of a user agent string.
    +  // Example UA:
    +  // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
    +  // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
    +  // This has three version tuples: Mozilla, AppleWebKit, and Mobile.
    +
    +  var versionRegExp = new RegExp(
    +      // Key. Note that a key may have a space.
    +      // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
    +      '(\\w[\\w ]+)' +
    +
    +      '/' +                // slash
    +      '([^\\s]+)' +        // version (i.e. '5.0b')
    +      '\\s*' +             // whitespace
    +      '(?:\\((.*?)\\))?',  // parenthetical info. parentheses not matched.
    +      'g');
    +
    +  var data = [];
    +  var match;
    +
    +  // Iterate and collect the version tuples.  Each iteration will be the
    +  // next regex match.
    +  while (match = versionRegExp.exec(userAgent)) {
    +    data.push([
    +      match[1],  // key
    +      match[2],  // value
    +      // || undefined as this is not undefined in IE7 and IE8
    +      match[3] || undefined  // info
    +    ]);
    +  }
    +
    +  return data;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.html b/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.html
    new file mode 100644
    index 0000000000..24c6d9e3fd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: nnaze@google.com (Nathan Naze)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.labs.userAgent.util</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.labs.userAgent.utilTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.js b/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.js
    new file mode 100644
    index 0000000000..c6ee28c91c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/labs/useragent/util_test.js
    @@ -0,0 +1,105 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file frexcept in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.labs.userAgent.engine.
    + */
    +
    +goog.provide('goog.labs.userAgent.utilTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.labs.userAgent.utilTest');
    +
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +
    +/**
    + * Tests parsing a few example UA strings.
    + */
    +function testExtractVersionTuples() {
    +  // Old Android
    +  var tuples = goog.labs.userAgent.util.extractVersionTuples(
    +      goog.labs.userAgent.testAgents.ANDROID_BROWSER_235);
    +
    +  assertEquals(4, tuples.length);
    +  assertSameElements(
    +      ['Mozilla', '5.0',
    +       'Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40'], tuples[0]);
    +  assertSameElements(['AppleWebKit', '533.1', 'KHTML, like Gecko'], tuples[1]);
    +  assertSameElements(['Version', '4.0', undefined], tuples[2]);
    +  assertSameElements(['Mobile Safari', '533.1', undefined], tuples[3]);
    +
    +  // IE 9
    +  tuples = goog.labs.userAgent.util.extractVersionTuples(
    +      goog.labs.userAgent.testAgents.IE_9);
    +  assertEquals(1, tuples.length);
    +  assertSameElements(
    +      ['Mozilla', '5.0',
    +       'compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0'], tuples[0]);
    +
    +  // Opera
    +  tuples = goog.labs.userAgent.util.extractVersionTuples(
    +      goog.labs.userAgent.testAgents.OPERA_10);
    +  assertEquals(3, tuples.length);
    +  assertSameElements(['Opera', '9.80', 'S60; SymbOS; Opera Mobi/447; U; en'],
    +                     tuples[0]);
    +  assertSameElements(['Presto', '2.4.18', undefined], tuples[1]);
    +  assertSameElements(['Version', '10.00', undefined], tuples[2]);
    +}
    +
    +function testSetUserAgent() {
    +  var ua = 'Five Finger Death Punch';
    +  goog.labs.userAgent.util.setUserAgent(ua);
    +  assertEquals(ua, goog.labs.userAgent.util.getUserAgent());
    +  assertTrue(goog.labs.userAgent.util.matchUserAgent('Punch'));
    +  assertFalse(goog.labs.userAgent.util.matchUserAgent('punch'));
    +  assertFalse(goog.labs.userAgent.util.matchUserAgent('Mozilla'));
    +}
    +
    +function testSetUserAgentIgnoreCase() {
    +  var ua = 'Five Finger Death Punch';
    +  goog.labs.userAgent.util.setUserAgent(ua);
    +  assertEquals(ua, goog.labs.userAgent.util.getUserAgent());
    +  assertTrue(goog.labs.userAgent.util.matchUserAgentIgnoreCase('Punch'));
    +  assertTrue(goog.labs.userAgent.util.matchUserAgentIgnoreCase('punch'));
    +  assertFalse(goog.labs.userAgent.util.matchUserAgentIgnoreCase('Mozilla'));
    +}
    +
    +function testNoNavigator() {
    +  stubs.set(goog.labs.userAgent.util, 'getNavigator_',
    +            goog.functions.constant(undefined));
    +  assertEquals('', goog.labs.userAgent.util.getNativeUserAgentString_());
    +}
    +
    +function testNavigatorWithNoUserAgent() {
    +  stubs.set(goog.labs.userAgent.util, 'getNavigator_',
    +            goog.functions.constant(undefined));
    +  assertEquals('', goog.labs.userAgent.util.getNativeUserAgentString_());
    +}
    +
    +function testNavigatorWithUserAgent() {
    +  stubs.set(goog.labs.userAgent.util, 'getNavigator_',
    +            goog.functions.constant({'userAgent': 'moose'}));
    +  assertEquals('moose', goog.labs.userAgent.util.getNativeUserAgentString_());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/countries.js b/mapbox-gl/closure-library/closure/goog/locale/countries.js
    new file mode 100644
    index 0000000000..880a50283d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/countries.js
    @@ -0,0 +1,291 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Current list of countries of the world. This list uses
    + * CLDR xml files for data. Algorithm is to list all country codes
    + * that are not containments (representing a group of countries) and does
    + * have a territory alias (old countries).
    + *
    + * Warning: this file is automatically generated from CLDR.
    + * Please contact i18n team or change the script and regenerate data.
    + * Code location: http://go/cldr_scripts:generate_countries
    + *
    + */
    +
    +
    +/**
    + * Namespace for current country codes.
    + */
    +goog.provide('goog.locale.countries');
    +
    +/**
    + * List of codes for countries valid today.
    + * @type {!Array<string>}
    + */
    +
    +/* ~!@# Countries #@!~ (special comment meaningful to generation script) */
    +goog.locale.countries = [
    +  'AD',
    +  'AE',
    +  'AF',
    +  'AG',
    +  'AI',
    +  'AL',
    +  'AM',
    +  'AO',
    +  'AQ',
    +  'AR',
    +  'AS',
    +  'AT',
    +  'AU',
    +  'AW',
    +  'AX',
    +  'AZ',
    +  'BA',
    +  'BB',
    +  'BD',
    +  'BE',
    +  'BF',
    +  'BG',
    +  'BH',
    +  'BI',
    +  'BJ',
    +  'BL',
    +  'BM',
    +  'BN',
    +  'BO',
    +  'BQ',
    +  'BR',
    +  'BS',
    +  'BT',
    +  'BV',
    +  'BW',
    +  'BY',
    +  'BZ',
    +  'CA',
    +  'CC',
    +  'CD',
    +  'CF',
    +  'CG',
    +  'CH',
    +  'CI',
    +  'CK',
    +  'CL',
    +  'CM',
    +  'CN',
    +  'CO',
    +  'CR',
    +  'CU',
    +  'CV',
    +  'CW',
    +  'CX',
    +  'CY',
    +  'CZ',
    +  'DE',
    +  'DJ',
    +  'DK',
    +  'DM',
    +  'DO',
    +  'DZ',
    +  'EC',
    +  'EE',
    +  'EG',
    +  'EH',
    +  'ER',
    +  'ES',
    +  'ET',
    +  'FI',
    +  'FJ',
    +  'FK',
    +  'FM',
    +  'FO',
    +  'FR',
    +  'GA',
    +  'GB',
    +  'GD',
    +  'GE',
    +  'GF',
    +  'GG',
    +  'GH',
    +  'GI',
    +  'GL',
    +  'GM',
    +  'GN',
    +  'GP',
    +  'GQ',
    +  'GR',
    +  'GS',
    +  'GT',
    +  'GU',
    +  'GW',
    +  'GY',
    +  'HK',
    +  'HM',
    +  'HN',
    +  'HR',
    +  'HT',
    +  'HU',
    +  'ID',
    +  'IE',
    +  'IL',
    +  'IM',
    +  'IN',
    +  'IO',
    +  'IQ',
    +  'IR',
    +  'IS',
    +  'IT',
    +  'JE',
    +  'JM',
    +  'JO',
    +  'JP',
    +  'KE',
    +  'KG',
    +  'KH',
    +  'KI',
    +  'KM',
    +  'KN',
    +  'KP',
    +  'KR',
    +  'KW',
    +  'KY',
    +  'KZ',
    +  'LA',
    +  'LB',
    +  'LC',
    +  'LI',
    +  'LK',
    +  'LR',
    +  'LS',
    +  'LT',
    +  'LU',
    +  'LV',
    +  'LY',
    +  'MA',
    +  'MC',
    +  'MD',
    +  'ME',
    +  'MF',
    +  'MG',
    +  'MH',
    +  'MK',
    +  'ML',
    +  'MM',
    +  'MN',
    +  'MO',
    +  'MP',
    +  'MQ',
    +  'MR',
    +  'MS',
    +  'MT',
    +  'MU',
    +  'MV',
    +  'MW',
    +  'MX',
    +  'MY',
    +  'MZ',
    +  'NA',
    +  'NC',
    +  'NE',
    +  'NF',
    +  'NG',
    +  'NI',
    +  'NL',
    +  'NO',
    +  'NP',
    +  'NR',
    +  'NU',
    +  'NZ',
    +  'OM',
    +  'PA',
    +  'PE',
    +  'PF',
    +  'PG',
    +  'PH',
    +  'PK',
    +  'PL',
    +  'PM',
    +  'PN',
    +  'PR',
    +  'PS',
    +  'PT',
    +  'PW',
    +  'PY',
    +  'QA',
    +  'RE',
    +  'RO',
    +  'RS',
    +  'RU',
    +  'RW',
    +  'SA',
    +  'SB',
    +  'SC',
    +  'SD',
    +  'SE',
    +  'SG',
    +  'SH',
    +  'SI',
    +  'SJ',
    +  'SK',
    +  'SL',
    +  'SM',
    +  'SN',
    +  'SO',
    +  'SR',
    +  'SS',
    +  'ST',
    +  'SV',
    +  'SX',
    +  'SY',
    +  'SZ',
    +  'TC',
    +  'TD',
    +  'TF',
    +  'TG',
    +  'TH',
    +  'TJ',
    +  'TK',
    +  'TL',
    +  'TM',
    +  'TN',
    +  'TO',
    +  'TR',
    +  'TT',
    +  'TV',
    +  'TW',
    +  'TZ',
    +  'UA',
    +  'UG',
    +  'UM',
    +  'US',
    +  'UY',
    +  'UZ',
    +  'VA',
    +  'VC',
    +  'VE',
    +  'VG',
    +  'VI',
    +  'VN',
    +  'VU',
    +  'WF',
    +  'WS',
    +  'YE',
    +  'YT',
    +  'ZA',
    +  'ZM',
    +  'ZW'
    +];
    +
    +/* ~!@# END #@!~ */
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.html b/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.html
    new file mode 100644
    index 0000000000..269e2118fe
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.locale.LocaleNameConstants
    +  </title>
    +  <!-- UTF-8 needed for character encoding -->
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.locale.countryLanguageNamesTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.js b/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.js
    new file mode 100644
    index 0000000000..83e66f9826
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/countrylanguagenames_test.js
    @@ -0,0 +1,231 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.locale.countryLanguageNamesTest');
    +goog.setTestOnly('goog.locale.countryLanguageNamesTest');
    +
    +goog.require('goog.locale');
    +goog.require('goog.testing.jsunit');
    +
    +var LocaleNameConstants_en;
    +
    +function setUpPage() {
    +  // Test data from //googledata/i18n/js_locale_data/LocaleNameConstants__de.js
    +  var LocaleNameConstants_de = {
    +    LANGUAGE: {
    +      'cad': 'Caddo',
    +      'fr': 'Franz\u00f6sisch',
    +      'fr_CA': 'Canadian French',
    +      'fr_CH': 'Swiss French', 'zh': 'Chinesisch',
    +      'zh_Hans': 'Chinesisch (vereinfacht)',
    +      'zh_Hant': 'Chinesisch (traditionell)'
    +    },
    +    COUNTRY: {
    +      'CN': 'China',
    +      'ES': 'Spanien',
    +      'FR': 'Frankreich'
    +    }
    +  };
    +  registerLocalNameConstants(LocaleNameConstants_de, 'de');
    +
    +  // Test data from //googledata/i18n/js_locale_data/LocaleNameConstants__en.js
    +  LocaleNameConstants_en = {
    +    LANGUAGE: {
    +      'cad': 'Caddo',
    +      'fr': 'French',
    +      'fr_CA': 'Canadian French',
    +      'fr_CH': 'Swiss French',
    +      'zh': 'Chinese',
    +      'zh_Hans': 'Simplified Chinese',
    +      'zh_Hant': 'Traditional Chinese'
    +    },
    +    COUNTRY: {
    +      'CN': 'China',
    +      'ES': 'Spain',
    +      'FR': 'France'
    +    }
    +  };
    +  registerLocalNameConstants(LocaleNameConstants_en, 'en');
    +
    +  goog.locale.setLocale('de');
    +}
    +
    +function testLoadLoacleSymbols() {
    +  var result = goog.locale.getLocalizedCountryName('fr-FR');
    +  assertEquals('Frankreich', result);
    +}
    +
    +function testGetNativeCountryName() {
    +  var result = goog.locale.getNativeCountryName('de-DE');
    +  assertEquals('Deutschland', result);
    +
    +  result = goog.locale.getNativeCountryName('de_DE');
    +  assertEquals('Deutschland', result);
    +
    +  result = goog.locale.getNativeCountryName('und');
    +  assertEquals('und', result);
    +
    +  result = goog.locale.getNativeCountryName('de-CH');
    +  assertEquals('Schweiz', result);
    +
    +  result = goog.locale.getNativeCountryName('fr-CH');
    +  assertEquals('Suisse', result);
    +
    +  result = goog.locale.getNativeCountryName('it-CH');
    +  assertEquals('Svizzera', result);
    +}
    +
    +function testGetLocalizedCountryName() {
    +  var result = goog.locale.getLocalizedCountryName('es-ES');
    +  assertEquals('Spanien', result);
    +
    +  result = goog.locale.getLocalizedCountryName('es-ES', LocaleNameConstants_en);
    +  assertEquals('Spain', result);
    +
    +  result = goog.locale.getLocalizedCountryName('zh-CN-cmn');
    +  assertEquals('China', result);
    +
    +  result = goog.locale.getLocalizedCountryName('zh_CN_cmn');
    +  assertEquals('China', result);
    +
    +  // 'und' is a non-existing locale, default behavior is to
    +  // return the locale name itself if no mapping is found.
    +  result = goog.locale.getLocalizedCountryName('und');
    +  assertEquals('und', result);
    +}
    +
    +function testGetNativeLanguageName() {
    +  var result = goog.locale.getNativeLanguageName('fr');
    +  assertEquals('fran\u00E7ais', result);
    +
    +  result = goog.locale.getNativeLanguageName('fr-latn-FR');
    +  assertEquals('fran\u00E7ais', result);
    +
    +  result = goog.locale.getNativeLanguageName('fr_FR');
    +  assertEquals('fran\u00E7ais', result);
    +
    +  result = goog.locale.getNativeLanguageName('error');
    +  assertEquals('error', result);
    +}
    +
    +function testGetLocalizedLanguageName() {
    +  var result = goog.locale.getLocalizedLanguageName('fr');
    +  assertEquals('Franz\u00F6sisch', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('fr',
    +      LocaleNameConstants_en);
    +  assertEquals('French', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('fr-latn-FR');
    +  assertEquals('Franz\u00F6sisch', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('fr_FR');
    +  assertEquals('Franz\u00F6sisch', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('cad');
    +  assertEquals('Caddo', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('error');
    +  assertEquals('error', result);
    +
    +  result = goog.locale.getLocalizedLanguageName('zh_Hans',
    +      LocaleNameConstants_en);
    +  assertEquals('Simplified Chinese', result);
    +}
    +
    +
    +function testGetLocalizedLanguageNameForGivenSymbolset() {
    +  var result = goog.locale.getLocalizedCountryName('fr-FR');
    +  assertEquals('Frankreich', result);
    +
    +  result = goog.locale.getLocalizedCountryName(
    +      'fr-FR',
    +      LocaleNameConstants_en);
    +  assertEquals('France', result);
    +
    +  result = goog.locale.getLocalizedCountryName('fr-FR');
    +  assertEquals('Frankreich', result);
    +}
    +
    +/**
    + * Valid combination of sub tags:
    + *  1)  LanguageSubtag'-'RegionSubtag
    + *  2)  LanguageSubtag'-'ScriptSubtag'-'RegionSubtag
    + *  3)  LanguageSubtag'-'RegionSubtag'-'VariantSubtag
    + *  4)  LanguageSubtag'-'ScriptSubTag'-'RegionSubtag'-'VariantSubtag
    + */
    +
    +function testGetRegionSubTag() {
    +
    +  var result = goog.locale.getRegionSubTag('de-CH');
    +  assertEquals('CH', result);
    +
    +  result = goog.locale.getRegionSubTag('de-latn-CH');
    +  assertEquals('CH', result);
    +
    +  result = goog.locale.getRegionSubTag('de_latn_CH');
    +  assertEquals('CH', result);
    +
    +  result = goog.locale.getRegionSubTag('de-CH-xxx');
    +  assertEquals('CH', result);
    +
    +  result = goog.locale.getRegionSubTag('de-latn-CH-xxx');
    +  assertEquals('CH', result);
    +
    +  result = goog.locale.getRegionSubTag('es-latn-419-xxx');
    +  assertEquals('419', result);
    +
    +  result = goog.locale.getRegionSubTag('es_latn_419_xxx');
    +  assertEquals('419', result);
    +
    +  // No region sub tag present
    +  result = goog.locale.getRegionSubTag('de');
    +  assertEquals('', result);
    +}
    +
    +function testGetLanguageSubTag() {
    +
    +  var result = goog.locale.getLanguageSubTag('de');
    +  assertEquals('de', result);
    +
    +  result = goog.locale.getLanguageSubTag('de-DE');
    +  assertEquals('de', result);
    +
    +  result = goog.locale.getLanguageSubTag('de-latn-DE-xxx');
    +  assertEquals('de', result);
    +
    +  result = goog.locale.getLanguageSubTag('nds');
    +  assertEquals('nds', result);
    +
    +  result = goog.locale.getLanguageSubTag('nds-DE');
    +  assertEquals('nds', result);
    +}
    +
    +function testGetScriptSubTag() {
    +
    +  var result = goog.locale.getScriptSubTag('fr');
    +  assertEquals('', result);
    +
    +  result = goog.locale.getScriptSubTag('fr-Latn');
    +  assertEquals('Latn', result);
    +
    +  result = goog.locale.getScriptSubTag('fr-Arab-AA');
    +  assertEquals('Arab', result);
    +
    +  result = goog.locale.getScriptSubTag('de-Latin-DE');
    +  assertEquals('', result);
    +
    +  result = goog.locale.getScriptSubTag('srn-Ar-DE');
    +  assertEquals('', result);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/defaultlocalenameconstants.js b/mapbox-gl/closure-library/closure/goog/locale/defaultlocalenameconstants.js
    new file mode 100644
    index 0000000000..992c7c2d31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/defaultlocalenameconstants.js
    @@ -0,0 +1,938 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default list of locale specific country and language names.
    + *
    + * Warning: this file is automatically generated from CLDR.
    + * File generated from CLDR ver. 26
    + * Please contact i18n team or change the script and regenerate data.
    + * Code location: http://go/generate_js_lang_country_constants
    + *
    + */
    +
    +/**
    + * Namespace for locale specific country and lanugage names
    + */
    +
    +goog.provide('goog.locale.defaultLocaleNameConstants');
    +
    +/**
    + * Default list of locale specific country and language names
    + */
    +
    +goog.locale.defaultLocaleNameConstants = {
    +  'COUNTRY': {
    +    '001': 'World',
    +    '002': 'Africa',
    +    '003': 'North America',
    +    '005': 'South America',
    +    '009': 'Oceania',
    +    '011': 'Western Africa',
    +    '013': 'Central America',
    +    '014': 'Eastern Africa',
    +    '015': 'Northern Africa',
    +    '017': 'Middle Africa',
    +    '018': 'Southern Africa',
    +    '019': 'Americas',
    +    '021': 'Northern America',
    +    '029': 'Caribbean',
    +    '030': 'Eastern Asia',
    +    '034': 'Southern Asia',
    +    '035': 'Southeast Asia',
    +    '039': 'Southern Europe',
    +    '053': 'Australasia',
    +    '054': 'Melanesia',
    +    '057': 'Micronesian Region',
    +    '061': 'Polynesia',
    +    '142': 'Asia',
    +    '143': 'Central Asia',
    +    '145': 'Western Asia',
    +    '150': 'Europe',
    +    '151': 'Eastern Europe',
    +    '154': 'Northern Europe',
    +    '155': 'Western Europe',
    +    '419': 'Latin America',
    +    'AC': 'Ascension Island',
    +    'AD': 'Andorra',
    +    'AE': 'United Arab Emirates',
    +    'AF': 'Afghanistan',
    +    'AG': 'Antigua & Barbuda',
    +    'AI': 'Anguilla',
    +    'AL': 'Albania',
    +    'AM': 'Armenia',
    +    'AN': 'Netherlands Antilles',
    +    'AO': 'Angola',
    +    'AQ': 'Antarctica',
    +    'AR': 'Argentina',
    +    'AS': 'American Samoa',
    +    'AT': 'Austria',
    +    'AU': 'Australia',
    +    'AW': 'Aruba',
    +    'AX': '\u00c5land Islands',
    +    'AZ': 'Azerbaijan',
    +    'BA': 'Bosnia & Herzegovina',
    +    'BB': 'Barbados',
    +    'BD': 'Bangladesh',
    +    'BE': 'Belgium',
    +    'BF': 'Burkina Faso',
    +    'BG': 'Bulgaria',
    +    'BH': 'Bahrain',
    +    'BI': 'Burundi',
    +    'BJ': 'Benin',
    +    'BL': 'St. Barth\u00e9lemy',
    +    'BM': 'Bermuda',
    +    'BN': 'Brunei',
    +    'BO': 'Bolivia',
    +    'BQ': 'Caribbean Netherlands',
    +    'BR': 'Brazil',
    +    'BS': 'Bahamas',
    +    'BT': 'Bhutan',
    +    'BV': 'Bouvet Island',
    +    'BW': 'Botswana',
    +    'BY': 'Belarus',
    +    'BZ': 'Belize',
    +    'CA': 'Canada',
    +    'CC': 'Cocos (Keeling) Islands',
    +    'CD': 'Congo (DRC)',
    +    'CF': 'Central African Republic',
    +    'CG': 'Congo (Republic)',
    +    'CH': 'Switzerland',
    +    'CI': 'C\u00f4te d\u2019Ivoire',
    +    'CK': 'Cook Islands',
    +    'CL': 'Chile',
    +    'CM': 'Cameroon',
    +    'CN': 'China',
    +    'CO': 'Colombia',
    +    'CP': 'Clipperton Island',
    +    'CR': 'Costa Rica',
    +    'CU': 'Cuba',
    +    'CV': 'Cape Verde',
    +    'CW': 'Cura\u00e7ao',
    +    'CX': 'Christmas Island',
    +    'CY': 'Cyprus',
    +    'CZ': 'Czech Republic',
    +    'DE': 'Germany',
    +    'DG': 'Diego Garcia',
    +    'DJ': 'Djibouti',
    +    'DK': 'Denmark',
    +    'DM': 'Dominica',
    +    'DO': 'Dominican Republic',
    +    'DZ': 'Algeria',
    +    'EA': 'Ceuta & Melilla',
    +    'EC': 'Ecuador',
    +    'EE': 'Estonia',
    +    'EG': 'Egypt',
    +    'EH': 'Western Sahara',
    +    'ER': 'Eritrea',
    +    'ES': 'Spain',
    +    'ET': 'Ethiopia',
    +    'EU': 'European Union',
    +    'FI': 'Finland',
    +    'FJ': 'Fiji',
    +    'FK': 'Falkland Islands (Islas Malvinas)',
    +    'FM': 'Micronesia',
    +    'FO': 'Faroe Islands',
    +    'FR': 'France',
    +    'GA': 'Gabon',
    +    'GB': 'United Kingdom',
    +    'GD': 'Grenada',
    +    'GE': 'Georgia',
    +    'GF': 'French Guiana',
    +    'GG': 'Guernsey',
    +    'GH': 'Ghana',
    +    'GI': 'Gibraltar',
    +    'GL': 'Greenland',
    +    'GM': 'Gambia',
    +    'GN': 'Guinea',
    +    'GP': 'Guadeloupe',
    +    'GQ': 'Equatorial Guinea',
    +    'GR': 'Greece',
    +    'GS': 'South Georgia & South Sandwich Islands',
    +    'GT': 'Guatemala',
    +    'GU': 'Guam',
    +    'GW': 'Guinea-Bissau',
    +    'GY': 'Guyana',
    +    'HK': 'Hong Kong',
    +    'HM': 'Heard & McDonald Islands',
    +    'HN': 'Honduras',
    +    'HR': 'Croatia',
    +    'HT': 'Haiti',
    +    'HU': 'Hungary',
    +    'IC': 'Canary Islands',
    +    'ID': 'Indonesia',
    +    'IE': 'Ireland',
    +    'IL': 'Israel',
    +    'IM': 'Isle of Man',
    +    'IN': 'India',
    +    'IO': 'British Indian Ocean Territory',
    +    'IQ': 'Iraq',
    +    'IR': 'Iran',
    +    'IS': 'Iceland',
    +    'IT': 'Italy',
    +    'JE': 'Jersey',
    +    'JM': 'Jamaica',
    +    'JO': 'Jordan',
    +    'JP': 'Japan',
    +    'KE': 'Kenya',
    +    'KG': 'Kyrgyzstan',
    +    'KH': 'Cambodia',
    +    'KI': 'Kiribati',
    +    'KM': 'Comoros',
    +    'KN': 'St. Kitts & Nevis',
    +    'KP': 'North Korea',
    +    'KR': 'South Korea',
    +    'KW': 'Kuwait',
    +    'KY': 'Cayman Islands',
    +    'KZ': 'Kazakhstan',
    +    'LA': 'Laos',
    +    'LB': 'Lebanon',
    +    'LC': 'St. Lucia',
    +    'LI': 'Liechtenstein',
    +    'LK': 'Sri Lanka',
    +    'LR': 'Liberia',
    +    'LS': 'Lesotho',
    +    'LT': 'Lithuania',
    +    'LU': 'Luxembourg',
    +    'LV': 'Latvia',
    +    'LY': 'Libya',
    +    'MA': 'Morocco',
    +    'MC': 'Monaco',
    +    'MD': 'Moldova',
    +    'ME': 'Montenegro',
    +    'MF': 'St. Martin',
    +    'MG': 'Madagascar',
    +    'MH': 'Marshall Islands',
    +    'MK': 'Macedonia (FYROM)',
    +    'ML': 'Mali',
    +    'MM': 'Myanmar (Burma)',
    +    'MN': 'Mongolia',
    +    'MO': 'Macau',
    +    'MP': 'Northern Mariana Islands',
    +    'MQ': 'Martinique',
    +    'MR': 'Mauritania',
    +    'MS': 'Montserrat',
    +    'MT': 'Malta',
    +    'MU': 'Mauritius',
    +    'MV': 'Maldives',
    +    'MW': 'Malawi',
    +    'MX': 'Mexico',
    +    'MY': 'Malaysia',
    +    'MZ': 'Mozambique',
    +    'NA': 'Namibia',
    +    'NC': 'New Caledonia',
    +    'NE': 'Niger',
    +    'NF': 'Norfolk Island',
    +    'NG': 'Nigeria',
    +    'NI': 'Nicaragua',
    +    'NL': 'Netherlands',
    +    'NO': 'Norway',
    +    'NP': 'Nepal',
    +    'NR': 'Nauru',
    +    'NU': 'Niue',
    +    'NZ': 'New Zealand',
    +    'OM': 'Oman',
    +    'PA': 'Panama',
    +    'PE': 'Peru',
    +    'PF': 'French Polynesia',
    +    'PG': 'Papua New Guinea',
    +    'PH': 'Philippines',
    +    'PK': 'Pakistan',
    +    'PL': 'Poland',
    +    'PM': 'St. Pierre & Miquelon',
    +    'PN': 'Pitcairn Islands',
    +    'PR': 'Puerto Rico',
    +    'PS': 'Palestine',
    +    'PT': 'Portugal',
    +    'PW': 'Palau',
    +    'PY': 'Paraguay',
    +    'QA': 'Qatar',
    +    'QO': 'Outlying Oceania',
    +    'RE': 'R\u00e9union',
    +    'RO': 'Romania',
    +    'RS': 'Serbia',
    +    'RU': 'Russia',
    +    'RW': 'Rwanda',
    +    'SA': 'Saudi Arabia',
    +    'SB': 'Solomon Islands',
    +    'SC': 'Seychelles',
    +    'SD': 'Sudan',
    +    'SE': 'Sweden',
    +    'SG': 'Singapore',
    +    'SH': 'St. Helena',
    +    'SI': 'Slovenia',
    +    'SJ': 'Svalbard & Jan Mayen',
    +    'SK': 'Slovakia',
    +    'SL': 'Sierra Leone',
    +    'SM': 'San Marino',
    +    'SN': 'Senegal',
    +    'SO': 'Somalia',
    +    'SR': 'Suriname',
    +    'SS': 'South Sudan',
    +    'ST': 'S\u00e3o Tom\u00e9 & Pr\u00edncipe',
    +    'SV': 'El Salvador',
    +    'SX': 'Sint Maarten',
    +    'SY': 'Syria',
    +    'SZ': 'Swaziland',
    +    'TA': 'Tristan da Cunha',
    +    'TC': 'Turks & Caicos Islands',
    +    'TD': 'Chad',
    +    'TF': 'French Southern Territories',
    +    'TG': 'Togo',
    +    'TH': 'Thailand',
    +    'TJ': 'Tajikistan',
    +    'TK': 'Tokelau',
    +    'TL': 'Timor-Leste',
    +    'TM': 'Turkmenistan',
    +    'TN': 'Tunisia',
    +    'TO': 'Tonga',
    +    'TR': 'Turkey',
    +    'TT': 'Trinidad & Tobago',
    +    'TV': 'Tuvalu',
    +    'TW': 'Taiwan',
    +    'TZ': 'Tanzania',
    +    'UA': 'Ukraine',
    +    'UG': 'Uganda',
    +    'UM': 'U.S. Outlying Islands',
    +    'US': 'United States',
    +    'UY': 'Uruguay',
    +    'UZ': 'Uzbekistan',
    +    'VA': 'Vatican City',
    +    'VC': 'St. Vincent & Grenadines',
    +    'VE': 'Venezuela',
    +    'VG': 'British Virgin Islands',
    +    'VI': 'U.S. Virgin Islands',
    +    'VN': 'Vietnam',
    +    'VU': 'Vanuatu',
    +    'WF': 'Wallis & Futuna',
    +    'WS': 'Samoa',
    +    'XK': 'Kosovo',
    +    'YE': 'Yemen',
    +    'YT': 'Mayotte',
    +    'ZA': 'South Africa',
    +    'ZM': 'Zambia',
    +    'ZW': 'Zimbabwe',
    +    'ZZ': 'Unknown Region'
    +  },
    +  'LANGUAGE': {
    +    'aa': 'Afar',
    +    'ab': 'Abkhazian',
    +    'ace': 'Achinese',
    +    'ach': 'Acoli',
    +    'ada': 'Adangme',
    +    'ady': 'Adyghe',
    +    'ae': 'Avestan',
    +    'aeb': 'Tunisian Arabic',
    +    'af': 'Afrikaans',
    +    'afh': 'Afrihili',
    +    'agq': 'Aghem',
    +    'ain': 'Ainu',
    +    'ak': 'Akan',
    +    'akk': 'Akkadian',
    +    'akz': 'Alabama',
    +    'ale': 'Aleut',
    +    'aln': 'Gheg Albanian',
    +    'alt': 'Southern Altai',
    +    'am': 'Amharic',
    +    'an': 'Aragonese',
    +    'ang': 'Old English',
    +    'anp': 'Angika',
    +    'ar': 'Arabic',
    +    'ar_001': 'Modern Standard Arabic',
    +    'arc': 'Aramaic',
    +    'arn': 'Mapuche',
    +    'aro': 'Araona',
    +    'arp': 'Arapaho',
    +    'arq': 'Algerian Arabic',
    +    'arw': 'Arawak',
    +    'ary': 'Moroccan Arabic',
    +    'arz': 'Egyptian Arabic',
    +    'as': 'Assamese',
    +    'asa': 'Asu',
    +    'ase': 'American Sign Language',
    +    'ast': 'Asturian',
    +    'av': 'Avaric',
    +    'avk': 'Kotava',
    +    'awa': 'Awadhi',
    +    'ay': 'Aymara',
    +    'az': 'Azerbaijani',
    +    'azb': 'South Azerbaijani',
    +    'ba': 'Bashkir',
    +    'bal': 'Baluchi',
    +    'ban': 'Balinese',
    +    'bar': 'Bavarian',
    +    'bas': 'Basaa',
    +    'bax': 'Bamun',
    +    'bbc': 'Batak Toba',
    +    'bbj': 'Ghomala',
    +    'be': 'Belarusian',
    +    'bej': 'Beja',
    +    'bem': 'Bemba',
    +    'bew': 'Betawi',
    +    'bez': 'Bena',
    +    'bfd': 'Bafut',
    +    'bfq': 'Badaga',
    +    'bg': 'Bulgarian',
    +    'bho': 'Bhojpuri',
    +    'bi': 'Bislama',
    +    'bik': 'Bikol',
    +    'bin': 'Bini',
    +    'bjn': 'Banjar',
    +    'bkm': 'Kom',
    +    'bla': 'Siksika',
    +    'bm': 'Bambara',
    +    'bn': 'Bengali',
    +    'bo': 'Tibetan',
    +    'bpy': 'Bishnupriya',
    +    'bqi': 'Bakhtiari',
    +    'br': 'Breton',
    +    'bra': 'Braj',
    +    'brh': 'Brahui',
    +    'brx': 'Bodo',
    +    'bs': 'Bosnian',
    +    'bss': 'Akoose',
    +    'bua': 'Buriat',
    +    'bug': 'Buginese',
    +    'bum': 'Bulu',
    +    'byn': 'Blin',
    +    'byv': 'Medumba',
    +    'ca': 'Catalan',
    +    'cad': 'Caddo',
    +    'car': 'Carib',
    +    'cay': 'Cayuga',
    +    'cch': 'Atsam',
    +    'ce': 'Chechen',
    +    'ceb': 'Cebuano',
    +    'cgg': 'Chiga',
    +    'ch': 'Chamorro',
    +    'chb': 'Chibcha',
    +    'chg': 'Chagatai',
    +    'chk': 'Chuukese',
    +    'chm': 'Mari',
    +    'chn': 'Chinook Jargon',
    +    'cho': 'Choctaw',
    +    'chp': 'Chipewyan',
    +    'chr': 'Cherokee',
    +    'chy': 'Cheyenne',
    +    'ckb': 'Sorani Kurdish',
    +    'co': 'Corsican',
    +    'cop': 'Coptic',
    +    'cps': 'Capiznon',
    +    'cr': 'Cree',
    +    'crh': 'Crimean Turkish',
    +    'cs': 'Czech',
    +    'csb': 'Kashubian',
    +    'cu': 'Church Slavic',
    +    'cv': 'Chuvash',
    +    'cy': 'Welsh',
    +    'da': 'Danish',
    +    'dak': 'Dakota',
    +    'dar': 'Dargwa',
    +    'dav': 'Taita',
    +    'de': 'German',
    +    'de_AT': 'Austrian German',
    +    'de_CH': 'Swiss High German',
    +    'del': 'Delaware',
    +    'den': 'Slave',
    +    'dgr': 'Dogrib',
    +    'din': 'Dinka',
    +    'dje': 'Zarma',
    +    'doi': 'Dogri',
    +    'dsb': 'Lower Sorbian',
    +    'dtp': 'Central Dusun',
    +    'dua': 'Duala',
    +    'dum': 'Middle Dutch',
    +    'dv': 'Divehi',
    +    'dyo': 'Jola-Fonyi',
    +    'dyu': 'Dyula',
    +    'dz': 'Dzongkha',
    +    'dzg': 'Dazaga',
    +    'ebu': 'Embu',
    +    'ee': 'Ewe',
    +    'efi': 'Efik',
    +    'egl': 'Emilian',
    +    'egy': 'Ancient Egyptian',
    +    'eka': 'Ekajuk',
    +    'el': 'Greek',
    +    'elx': 'Elamite',
    +    'en': 'English',
    +    'en_AU': 'Australian English',
    +    'en_CA': 'Canadian English',
    +    'en_GB': 'British English',
    +    'en_US': 'American English',
    +    'enm': 'Middle English',
    +    'eo': 'Esperanto',
    +    'es': 'Spanish',
    +    'es_419': 'Latin American Spanish',
    +    'es_ES': 'European Spanish',
    +    'es_MX': 'Mexican Spanish',
    +    'esu': 'Central Yupik',
    +    'et': 'Estonian',
    +    'eu': 'Basque',
    +    'ewo': 'Ewondo',
    +    'ext': 'Extremaduran',
    +    'fa': 'Persian',
    +    'fan': 'Fang',
    +    'fat': 'Fanti',
    +    'ff': 'Fulah',
    +    'fi': 'Finnish',
    +    'fil': 'Filipino',
    +    'fit': 'Tornedalen Finnish',
    +    'fj': 'Fijian',
    +    'fo': 'Faroese',
    +    'fon': 'Fon',
    +    'fr': 'French',
    +    'fr_CA': 'Canadian French',
    +    'fr_CH': 'Swiss French',
    +    'frc': 'Cajun French',
    +    'frm': 'Middle French',
    +    'fro': 'Old French',
    +    'frp': 'Arpitan',
    +    'frr': 'Northern Frisian',
    +    'frs': 'Eastern Frisian',
    +    'fur': 'Friulian',
    +    'fy': 'Western Frisian',
    +    'ga': 'Irish',
    +    'gaa': 'Ga',
    +    'gag': 'Gagauz',
    +    'gan': 'Gan Chinese',
    +    'gay': 'Gayo',
    +    'gba': 'Gbaya',
    +    'gbz': 'Zoroastrian Dari',
    +    'gd': 'Scottish Gaelic',
    +    'gez': 'Geez',
    +    'gil': 'Gilbertese',
    +    'gl': 'Galician',
    +    'glk': 'Gilaki',
    +    'gmh': 'Middle High German',
    +    'gn': 'Guarani',
    +    'goh': 'Old High German',
    +    'gom': 'Goan Konkani',
    +    'gon': 'Gondi',
    +    'gor': 'Gorontalo',
    +    'got': 'Gothic',
    +    'grb': 'Grebo',
    +    'grc': 'Ancient Greek',
    +    'gsw': 'Swiss German',
    +    'gu': 'Gujarati',
    +    'guc': 'Wayuu',
    +    'gur': 'Frafra',
    +    'guz': 'Gusii',
    +    'gv': 'Manx',
    +    'gwi': 'Gwich\u02bcin',
    +    'ha': 'Hausa',
    +    'hai': 'Haida',
    +    'hak': 'Hakka Chinese',
    +    'haw': 'Hawaiian',
    +    'he': 'Hebrew',
    +    'hi': 'Hindi',
    +    'hif': 'Fiji Hindi',
    +    'hil': 'Hiligaynon',
    +    'hit': 'Hittite',
    +    'hmn': 'Hmong',
    +    'ho': 'Hiri Motu',
    +    'hr': 'Croatian',
    +    'hsb': 'Upper Sorbian',
    +    'hsn': 'Xiang Chinese',
    +    'ht': 'Haitian',
    +    'hu': 'Hungarian',
    +    'hup': 'Hupa',
    +    'hy': 'Armenian',
    +    'hz': 'Herero',
    +    'ia': 'Interlingua',
    +    'iba': 'Iban',
    +    'ibb': 'Ibibio',
    +    'id': 'Indonesian',
    +    'ie': 'Interlingue',
    +    'ig': 'Igbo',
    +    'ii': 'Sichuan Yi',
    +    'ik': 'Inupiaq',
    +    'ilo': 'Iloko',
    +    'inh': 'Ingush',
    +    'io': 'Ido',
    +    'is': 'Icelandic',
    +    'it': 'Italian',
    +    'iu': 'Inuktitut',
    +    'izh': 'Ingrian',
    +    'ja': 'Japanese',
    +    'jam': 'Jamaican Creole English',
    +    'jbo': 'Lojban',
    +    'jgo': 'Ngomba',
    +    'jmc': 'Machame',
    +    'jpr': 'Judeo-Persian',
    +    'jrb': 'Judeo-Arabic',
    +    'jut': 'Jutish',
    +    'jv': 'Javanese',
    +    'ka': 'Georgian',
    +    'kaa': 'Kara-Kalpak',
    +    'kab': 'Kabyle',
    +    'kac': 'Kachin',
    +    'kaj': 'Jju',
    +    'kam': 'Kamba',
    +    'kaw': 'Kawi',
    +    'kbd': 'Kabardian',
    +    'kbl': 'Kanembu',
    +    'kcg': 'Tyap',
    +    'kde': 'Makonde',
    +    'kea': 'Kabuverdianu',
    +    'ken': 'Kenyang',
    +    'kfo': 'Koro',
    +    'kg': 'Kongo',
    +    'kgp': 'Kaingang',
    +    'kha': 'Khasi',
    +    'kho': 'Khotanese',
    +    'khq': 'Koyra Chiini',
    +    'khw': 'Khowar',
    +    'ki': 'Kikuyu',
    +    'kiu': 'Kirmanjki',
    +    'kj': 'Kuanyama',
    +    'kk': 'Kazakh',
    +    'kkj': 'Kako',
    +    'kl': 'Kalaallisut',
    +    'kln': 'Kalenjin',
    +    'km': 'Khmer',
    +    'kmb': 'Kimbundu',
    +    'kn': 'Kannada',
    +    'ko': 'Korean',
    +    'koi': 'Komi-Permyak',
    +    'kok': 'Konkani',
    +    'kos': 'Kosraean',
    +    'kpe': 'Kpelle',
    +    'kr': 'Kanuri',
    +    'krc': 'Karachay-Balkar',
    +    'kri': 'Krio',
    +    'krj': 'Kinaray-a',
    +    'krl': 'Karelian',
    +    'kru': 'Kurukh',
    +    'ks': 'Kashmiri',
    +    'ksb': 'Shambala',
    +    'ksf': 'Bafia',
    +    'ksh': 'Colognian',
    +    'ku': 'Kurdish',
    +    'kum': 'Kumyk',
    +    'kut': 'Kutenai',
    +    'kv': 'Komi',
    +    'kw': 'Cornish',
    +    'ky': 'Kyrgyz',
    +    'la': 'Latin',
    +    'lad': 'Ladino',
    +    'lag': 'Langi',
    +    'lah': 'Lahnda',
    +    'lam': 'Lamba',
    +    'lb': 'Luxembourgish',
    +    'lez': 'Lezghian',
    +    'lfn': 'Lingua Franca Nova',
    +    'lg': 'Ganda',
    +    'li': 'Limburgish',
    +    'lij': 'Ligurian',
    +    'liv': 'Livonian',
    +    'lkt': 'Lakota',
    +    'lmo': 'Lombard',
    +    'ln': 'Lingala',
    +    'lo': 'Lao',
    +    'lol': 'Mongo',
    +    'loz': 'Lozi',
    +    'lt': 'Lithuanian',
    +    'ltg': 'Latgalian',
    +    'lu': 'Luba-Katanga',
    +    'lua': 'Luba-Lulua',
    +    'lui': 'Luiseno',
    +    'lun': 'Lunda',
    +    'luo': 'Luo',
    +    'lus': 'Mizo',
    +    'luy': 'Luyia',
    +    'lv': 'Latvian',
    +    'lzh': 'Literary Chinese',
    +    'lzz': 'Laz',
    +    'mad': 'Madurese',
    +    'maf': 'Mafa',
    +    'mag': 'Magahi',
    +    'mai': 'Maithili',
    +    'mak': 'Makasar',
    +    'man': 'Mandingo',
    +    'mas': 'Masai',
    +    'mde': 'Maba',
    +    'mdf': 'Moksha',
    +    'mdr': 'Mandar',
    +    'men': 'Mende',
    +    'mer': 'Meru',
    +    'mfe': 'Morisyen',
    +    'mg': 'Malagasy',
    +    'mga': 'Middle Irish',
    +    'mgh': 'Makhuwa-Meetto',
    +    'mgo': 'Meta\u02bc',
    +    'mh': 'Marshallese',
    +    'mi': 'Maori',
    +    'mic': 'Micmac',
    +    'min': 'Minangkabau',
    +    'mk': 'Macedonian',
    +    'ml': 'Malayalam',
    +    'mn': 'Mongolian',
    +    'mnc': 'Manchu',
    +    'mni': 'Manipuri',
    +    'moh': 'Mohawk',
    +    'mos': 'Mossi',
    +    'mr': 'Marathi',
    +    'mrj': 'Western Mari',
    +    'ms': 'Malay',
    +    'mt': 'Maltese',
    +    'mua': 'Mundang',
    +    'mul': 'Multiple Languages',
    +    'mus': 'Creek',
    +    'mwl': 'Mirandese',
    +    'mwr': 'Marwari',
    +    'mwv': 'Mentawai',
    +    'my': 'Burmese',
    +    'mye': 'Myene',
    +    'myv': 'Erzya',
    +    'mzn': 'Mazanderani',
    +    'na': 'Nauru',
    +    'nan': 'Min Nan Chinese',
    +    'nap': 'Neapolitan',
    +    'naq': 'Nama',
    +    'nb': 'Norwegian Bokm\u00e5l',
    +    'nd': 'North Ndebele',
    +    'nds': 'Low German',
    +    'ne': 'Nepali',
    +    'new': 'Newari',
    +    'ng': 'Ndonga',
    +    'nia': 'Nias',
    +    'niu': 'Niuean',
    +    'njo': 'Ao Naga',
    +    'nl': 'Dutch',
    +    'nl_BE': 'Flemish',
    +    'nmg': 'Kwasio',
    +    'nn': 'Norwegian Nynorsk',
    +    'nnh': 'Ngiemboon',
    +    'no': 'Norwegian',
    +    'nog': 'Nogai',
    +    'non': 'Old Norse',
    +    'nov': 'Novial',
    +    'nqo': 'N\u02bcKo',
    +    'nr': 'South Ndebele',
    +    'nso': 'Northern Sotho',
    +    'nus': 'Nuer',
    +    'nv': 'Navajo',
    +    'nwc': 'Classical Newari',
    +    'ny': 'Nyanja',
    +    'nym': 'Nyamwezi',
    +    'nyn': 'Nyankole',
    +    'nyo': 'Nyoro',
    +    'nzi': 'Nzima',
    +    'oc': 'Occitan',
    +    'oj': 'Ojibwa',
    +    'om': 'Oromo',
    +    'or': 'Oriya',
    +    'os': 'Ossetic',
    +    'osa': 'Osage',
    +    'ota': 'Ottoman Turkish',
    +    'pa': 'Punjabi',
    +    'pag': 'Pangasinan',
    +    'pal': 'Pahlavi',
    +    'pam': 'Pampanga',
    +    'pap': 'Papiamento',
    +    'pau': 'Palauan',
    +    'pcd': 'Picard',
    +    'pdc': 'Pennsylvania German',
    +    'pdt': 'Plautdietsch',
    +    'peo': 'Old Persian',
    +    'pfl': 'Palatine German',
    +    'phn': 'Phoenician',
    +    'pi': 'Pali',
    +    'pl': 'Polish',
    +    'pms': 'Piedmontese',
    +    'pnt': 'Pontic',
    +    'pon': 'Pohnpeian',
    +    'prg': 'Prussian',
    +    'pro': 'Old Proven\u00e7al',
    +    'ps': 'Pashto',
    +    'pt': 'Portuguese',
    +    'pt_BR': 'Brazilian Portuguese',
    +    'pt_PT': 'European Portuguese',
    +    'qu': 'Quechua',
    +    'quc': 'K\u02bciche\u02bc',
    +    'qug': 'Chimborazo Highland Quichua',
    +    'raj': 'Rajasthani',
    +    'rap': 'Rapanui',
    +    'rar': 'Rarotongan',
    +    'rgn': 'Romagnol',
    +    'rif': 'Riffian',
    +    'rm': 'Romansh',
    +    'rn': 'Rundi',
    +    'ro': 'Romanian',
    +    'ro_MD': 'Moldavian',
    +    'rof': 'Rombo',
    +    'rom': 'Romany',
    +    'root': 'Root',
    +    'rtm': 'Rotuman',
    +    'ru': 'Russian',
    +    'rue': 'Rusyn',
    +    'rug': 'Roviana',
    +    'rup': 'Aromanian',
    +    'rw': 'Kinyarwanda',
    +    'rwk': 'Rwa',
    +    'sa': 'Sanskrit',
    +    'sad': 'Sandawe',
    +    'sah': 'Sakha',
    +    'sam': 'Samaritan Aramaic',
    +    'saq': 'Samburu',
    +    'sas': 'Sasak',
    +    'sat': 'Santali',
    +    'saz': 'Saurashtra',
    +    'sba': 'Ngambay',
    +    'sbp': 'Sangu',
    +    'sc': 'Sardinian',
    +    'scn': 'Sicilian',
    +    'sco': 'Scots',
    +    'sd': 'Sindhi',
    +    'sdc': 'Sassarese Sardinian',
    +    'se': 'Northern Sami',
    +    'see': 'Seneca',
    +    'seh': 'Sena',
    +    'sei': 'Seri',
    +    'sel': 'Selkup',
    +    'ses': 'Koyraboro Senni',
    +    'sg': 'Sango',
    +    'sga': 'Old Irish',
    +    'sgs': 'Samogitian',
    +    'sh': 'Serbo-Croatian',
    +    'shi': 'Tachelhit',
    +    'shn': 'Shan',
    +    'shu': 'Chadian Arabic',
    +    'si': 'Sinhala',
    +    'sid': 'Sidamo',
    +    'sk': 'Slovak',
    +    'sl': 'Slovenian',
    +    'sli': 'Lower Silesian',
    +    'sly': 'Selayar',
    +    'sm': 'Samoan',
    +    'sma': 'Southern Sami',
    +    'smj': 'Lule Sami',
    +    'smn': 'Inari Sami',
    +    'sms': 'Skolt Sami',
    +    'sn': 'Shona',
    +    'snk': 'Soninke',
    +    'so': 'Somali',
    +    'sog': 'Sogdien',
    +    'sq': 'Albanian',
    +    'sr': 'Serbian',
    +    'srn': 'Sranan Tongo',
    +    'srr': 'Serer',
    +    'ss': 'Swati',
    +    'ssy': 'Saho',
    +    'st': 'Southern Sotho',
    +    'stq': 'Saterland Frisian',
    +    'su': 'Sundanese',
    +    'suk': 'Sukuma',
    +    'sus': 'Susu',
    +    'sux': 'Sumerian',
    +    'sv': 'Swedish',
    +    'sw': 'Swahili',
    +    'swb': 'Comorian',
    +    'swc': 'Congo Swahili',
    +    'syc': 'Classical Syriac',
    +    'syr': 'Syriac',
    +    'szl': 'Silesian',
    +    'ta': 'Tamil',
    +    'tcy': 'Tulu',
    +    'te': 'Telugu',
    +    'tem': 'Timne',
    +    'teo': 'Teso',
    +    'ter': 'Tereno',
    +    'tet': 'Tetum',
    +    'tg': 'Tajik',
    +    'th': 'Thai',
    +    'ti': 'Tigrinya',
    +    'tig': 'Tigre',
    +    'tiv': 'Tiv',
    +    'tk': 'Turkmen',
    +    'tkl': 'Tokelau',
    +    'tkr': 'Tsakhur',
    +    'tl': 'Tagalog',
    +    'tlh': 'Klingon',
    +    'tli': 'Tlingit',
    +    'tly': 'Talysh',
    +    'tmh': 'Tamashek',
    +    'tn': 'Tswana',
    +    'to': 'Tongan',
    +    'tog': 'Nyasa Tonga',
    +    'tpi': 'Tok Pisin',
    +    'tr': 'Turkish',
    +    'tru': 'Turoyo',
    +    'trv': 'Taroko',
    +    'ts': 'Tsonga',
    +    'tsd': 'Tsakonian',
    +    'tsi': 'Tsimshian',
    +    'tt': 'Tatar',
    +    'ttt': 'Muslim Tat',
    +    'tum': 'Tumbuka',
    +    'tvl': 'Tuvalu',
    +    'tw': 'Twi',
    +    'twq': 'Tasawaq',
    +    'ty': 'Tahitian',
    +    'tyv': 'Tuvinian',
    +    'tzm': 'Central Atlas Tamazight',
    +    'udm': 'Udmurt',
    +    'ug': 'Uyghur',
    +    'uga': 'Ugaritic',
    +    'uk': 'Ukrainian',
    +    'umb': 'Umbundu',
    +    'und': 'Unknown Language',
    +    'ur': 'Urdu',
    +    'uz': 'Uzbek',
    +    'vai': 'Vai',
    +    've': 'Venda',
    +    'vec': 'Venetian',
    +    'vep': 'Veps',
    +    'vi': 'Vietnamese',
    +    'vls': 'West Flemish',
    +    'vmf': 'Main-Franconian',
    +    'vo': 'Volap\u00fck',
    +    'vot': 'Votic',
    +    'vro': 'V\u00f5ro',
    +    'vun': 'Vunjo',
    +    'wa': 'Walloon',
    +    'wae': 'Walser',
    +    'wal': 'Wolaytta',
    +    'war': 'Waray',
    +    'was': 'Washo',
    +    'wo': 'Wolof',
    +    'wuu': 'Wu Chinese',
    +    'xal': 'Kalmyk',
    +    'xh': 'Xhosa',
    +    'xmf': 'Mingrelian',
    +    'xog': 'Soga',
    +    'yao': 'Yao',
    +    'yap': 'Yapese',
    +    'yav': 'Yangben',
    +    'ybb': 'Yemba',
    +    'yi': 'Yiddish',
    +    'yo': 'Yoruba',
    +    'yrl': 'Nheengatu',
    +    'yue': 'Cantonese',
    +    'za': 'Zhuang',
    +    'zap': 'Zapotec',
    +    'zbl': 'Blissymbols',
    +    'zea': 'Zeelandic',
    +    'zen': 'Zenaga',
    +    'zgh': 'Standard Moroccan Tamazight',
    +    'zh': 'Chinese',
    +    'zh_Hans': 'Simplified Chinese',
    +    'zh_Hant': 'Traditional Chinese',
    +    'zu': 'Zulu',
    +    'zun': 'Zuni',
    +    'zxx': 'No linguistic content',
    +    'zza': 'Zaza'
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/genericfontnames.js b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames.js
    new file mode 100644
    index 0000000000..49d124aee9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames.js
    @@ -0,0 +1,73 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions to list locale-specific font list and generic name.
    + * Generic name used for a font family would be locale dependant. For example,
    + * for 'zh'(Chinese) users, the name for Serif family would be in Chinese.
    + * Further documentation at: http://go/genericfontnames.
    + */
    +
    +goog.provide('goog.locale.genericFontNames');
    +
    +
    +/**
    + * This object maps (resourceName, localeName) to a resourceObj.
    + * @type {Object}
    + * @private
    + */
    +goog.locale.genericFontNames.data_ = {};
    +
    +
    +/**
    + * Normalizes the given locale id to standard form. eg: zh_Hant_TW.
    + * Many a times, input locale would be like: zh-tw, zh-hant-tw.
    + * @param {string} locale The locale id to be normalized.
    + * @return {string} Normalized locale id.
    + * @private
    + */
    +goog.locale.genericFontNames.normalize_ = function(locale) {
    +  locale = locale.replace(/-/g, '_');
    +  locale = locale.replace(/_[a-z]{2}$/,
    +      function(str) {
    +        return str.toUpperCase();
    +      });
    +
    +  locale = locale.replace(/[a-z]{4}/,
    +      function(str) {
    +        return str.substring(0, 1).toUpperCase() +
    +               str.substring(1);
    +      });
    +  return locale;
    +};
    +
    +
    +/**
    + * Gets the list of fonts and their generic names for the given locale.
    + * @param {string} locale The locale for which font lists and font family names
    + *     to be produced. The expected locale id is as described in
    + *     http://wiki/Main/IIISynonyms in all lowercase for easy matching.
    + *     Smallest possible id is expected.
    + *     Examples: 'zh', 'zh-tw', 'iw' instead of 'zh-CN', 'zh-Hant-TW', 'he'.
    + * @return {Array<Object>} List of objects with generic name as 'caption' and
    + *     corresponding font name lists as 'value' property.
    + */
    +goog.locale.genericFontNames.getList = function(locale) {
    +
    +  locale = goog.locale.genericFontNames.normalize_(locale);
    +  if (locale in goog.locale.genericFontNames.data_) {
    +    return goog.locale.genericFontNames.data_[locale];
    +  }
    +  return [];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.html b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.html
    new file mode 100644
    index 0000000000..26e9bdeaac
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.locale.genericFontNames
    +  </title>
    +  <!-- UTF-8 needed for character encoding -->
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.locale.genericFontNamesTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.js b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.js
    new file mode 100644
    index 0000000000..6bd99c9e60
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/genericfontnames_test.js
    @@ -0,0 +1,93 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.locale.genericFontNamesTest');
    +goog.setTestOnly('goog.locale.genericFontNamesTest');
    +
    +goog.require('goog.locale.genericFontNames');
    +goog.require('goog.testing.jsunit');
    +
    +goog.locale.genericFontNames.data_['zh_TW'] = [
    +  {
    +    'caption': '\u5fae\u8edf\u6b63\u9ed1\u9ad4',
    +    'value': 'Microsoft JhengHei,\u5fae\u8edf\u6b63\u9ed1\u9ad4,SimHei,' +
    +        '\u9ed1\u4f53,MS Hei,STHeiti,\u534e\u6587\u9ed1\u4f53,Apple ' +
    +        'LiGothic Medium,\u860b\u679c\u5137\u4e2d\u9ed1,LiHei Pro Medium,' +
    +        '\u5137\u9ed1 Pro,STHeiti Light,\u534e\u6587\u7ec6\u9ed1,AR PL ' +
    +        'ZenKai Uni,\u6587\u9f0ePL\u4e2d\u6977Uni,FreeSans,sans-serif'
    +  },
    +  {
    +    'caption': '\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53',
    +    'value': 'Microsoft YaHei,\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53,FreeSans,' +
    +        'sans-serif'
    +  },
    +  {
    +    'caption': '\u65b0\u7d30\u660e\u9ad4',
    +    'value': 'SimSun,\u5b8b\u4f53,MS Song,STSong,\u534e\u6587\u5b8b\u4f53,' +
    +        'Apple LiSung Light,\u860b\u679c\u5137\u7d30\u5b8b,LiSong Pro Light,' +
    +        '\u5137\u5b8b Pro,STFangSong,\u534e\u6587\u4eff\u5b8b,AR PL ' +
    +        'ShanHeiSun Uni,\u6587\u9f0eP' +
    +        'L\u7ec6\u4e0a\u6d77\u5b8bUni,AR PL New Sung,\u6587\u9f0e PL \u65b0' +
    +        '\u5b8b,FreeSerif,serif'
    +  },
    +  {
    +    'caption': '\u7d30\u660e\u9ad4',
    +    'value': 'NSimsun,\u65b0\u5b8b\u4f53,FreeMono,monospace'
    +  }
    +];
    +
    +function testNormalize() {
    +  var result = goog.locale.genericFontNames.normalize_('zh');
    +  assertEquals('zh', result);
    +  var result = goog.locale.genericFontNames.normalize_('zh-hant');
    +  assertEquals('zh_Hant', result);
    +  var result = goog.locale.genericFontNames.normalize_('zh-hant-tw');
    +  assertEquals('zh_Hant_TW', result);
    +}
    +
    +function testInvalid() {
    +  var result = goog.locale.genericFontNames.getList('invalid');
    +  assertArrayEquals([], result);
    +}
    +
    +function testZhHant() {
    +  var result = goog.locale.genericFontNames.getList('zh-tw');
    +  assertObjectEquals([
    +    {
    +      'caption': '\u5fae\u8edf\u6b63\u9ed1\u9ad4',
    +      'value': 'Microsoft JhengHei,\u5fae\u8edf\u6b63\u9ed1\u9ad4,SimHei,' +
    +          '\u9ed1\u4f53,MS Hei,STHeiti,\u534e\u6587\u9ed1\u4f53,Apple ' +
    +          'LiGothic Medium,\u860b\u679c\u5137\u4e2d\u9ed1,LiHei Pro Medium,' +
    +          '\u5137\u9ed1 Pro,STHeiti Light,\u534e\u6587\u7ec6\u9ed1,AR PL ' +
    +          'ZenKai Uni,\u6587\u9f0ePL\u4e2d\u6977Uni,FreeSans,sans-serif'
    +    },
    +    {
    +      'caption': '\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53',
    +      'value': 'Microsoft YaHei,\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53,' +
    +          'FreeSans,sans-serif'
    +    },
    +    {
    +      'caption': '\u65b0\u7d30\u660e\u9ad4',
    +      'value': 'SimSun,\u5b8b\u4f53,MS Song,STSong,\u534e\u6587\u5b8b\u4f53,' +
    +          'Apple LiSung Light,\u860b\u679c\u5137\u7d30\u5b8b,LiSong Pro ' +
    +          'Light,\u5137\u5b8b Pro,STFangSong,\u534e\u6587\u4eff\u5b8b,AR PL ' +
    +          'ShanHeiSun Uni,\u6587\u9f0ePL\u7ec6\u4e0a\u6d77\u5b8bUni,AR PL New' +
    +          ' Sung,\u6587\u9f0e PL \u65b0\u5b8b,FreeSerif,serif'
    +    },
    +    {
    +      'caption': '\u7d30\u660e\u9ad4',
    +      'value': 'NSimsun,\u65b0\u5b8b\u4f53,FreeMono,monospace'
    +    }],
    +  result);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/genericfontnamesdata.js b/mapbox-gl/closure-library/closure/goog/locale/genericfontnamesdata.js
    new file mode 100644
    index 0000000000..0b2e211f2d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/genericfontnamesdata.js
    @@ -0,0 +1,327 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview List of generic font names and font fallbacks.
    + * This file lists the font fallback for each font family for each locale or
    + * script. In this map, each value is an array of pair. The pair is stored
    + * as an array of two elements.
    + *
    + * First element of the pair is the generic name
    + * for the font family in that locale. In case of script indexed entries,
    + * It will be just font family name. Second element in the pair is a string
    + * comma seperated list of font names. API to access this data is provided
    + * thru goog.locale.genericFontNames.
    + *
    + * Warning: this file is automatically generated from CLDR.
    + * Please contact i18n team or change the script and regenerate data.
    + * Code location: http://go/generate_genericfontnames
    + *
    + */
    +
    +
    +/**
    + * Namespace for Generic Font Names
    + */
    +goog.provide('goog.locale.genericFontNamesData');
    +
    +
    +/**
    + * Map from script code or language code to list of pairs of (generic name,
    + * font name fallback list).
    + * @const {!Object<string, !Array<!Array<string>>>}
    + */
    +
    +/* ~!@# genmethods.genericFontNamesData() #@!~ */
    +goog.locale.genericFontNamesData = {
    +  'Arab': [
    +    [
    +      'sans-serif',
    +      'Arial,Al Bayan'
    +    ],
    +    [
    +      'serif',
    +      'Arabic Typesetting,Times New Roman'
    +    ]
    +  ],
    +  'Armn': [[
    +    'serif',
    +    'Sylfaen,Mshtakan'
    +  ]],
    +  'Beng': [[
    +    'sans-serif',
    +    'Vrinda,Lohit Bengali'
    +  ]],
    +  'Cans': [[
    +    'sans-serif',
    +    'Euphemia,Euphemia UCAS'
    +  ]],
    +  'Cher': [[
    +    'serif',
    +    'Plantagenet,Plantagenet Cherokee'
    +  ]],
    +  'Deva': [
    +    [
    +      'sans-serif',
    +      'Mangal,Lohit Hindi'
    +    ],
    +    [
    +      'serif',
    +      'Arial Unicode MS,Devanagari'
    +    ]
    +  ],
    +  'Ethi': [[
    +    'serif',
    +    'Nyala'
    +  ]],
    +  'Geor': [[
    +    'serif',
    +    'Sylfaen'
    +  ]],
    +  'Gujr': [
    +    [
    +      'sans-serif',
    +      'Shruti,Lohit Gujarati'
    +    ],
    +    [
    +      'serif',
    +      'Gujarati'
    +    ]
    +  ],
    +  'Guru': [
    +    [
    +      'sans-serif',
    +      'Raavi,Lohit Punjabi'
    +    ],
    +    [
    +      'serif',
    +      'Gurmukhi'
    +    ]
    +  ],
    +  'Hebr': [
    +    [
    +      'sans-serif',
    +      'Gisha,Aharoni,Arial Hebrew'
    +    ],
    +    [
    +      'serif',
    +      'David'
    +    ],
    +    [
    +      'monospace',
    +      'Miriam Fixed'
    +    ]
    +  ],
    +  'Khmr': [
    +    [
    +      'sans-serif',
    +      'MoolBoran,Khmer OS'
    +    ],
    +    [
    +      'serif',
    +      'DaunPenh'
    +    ]
    +  ],
    +  'Knda': [
    +    [
    +      'sans-serif',
    +      'Tunga'
    +    ],
    +    [
    +      'serif',
    +      'Kedage'
    +    ]
    +  ],
    +  'Laoo': [[
    +    'sans-serif',
    +    'DokChampa,Phetsarath OT'
    +  ]],
    +  'Mlym': [
    +    [
    +      'sans-serif',
    +      'AnjaliOldLipi,Kartika'
    +    ],
    +    [
    +      'serif',
    +      'Rachana'
    +    ]
    +  ],
    +  'Mong': [[
    +    'serif',
    +    'Mongolian Baiti'
    +  ]],
    +  'Nkoo': [[
    +    'serif',
    +    'Conakry'
    +  ]],
    +  'Orya': [[
    +    'sans-serif',
    +    'Kalinga,utkal'
    +  ]],
    +  'Sinh': [[
    +    'serif',
    +    'Iskoola Pota,Malithi Web'
    +  ]],
    +  'Syrc': [[
    +    'sans-serif',
    +    'Estrangelo Edessa'
    +  ]],
    +  'Taml': [
    +    [
    +      'sans-serif',
    +      'Latha,Lohit Tamil'
    +    ],
    +    [
    +      'serif',
    +      'Inai Mathi'
    +    ]
    +  ],
    +  'Telu': [
    +    [
    +      'sans-serif',
    +      'Gautami'
    +    ],
    +    [
    +      'serif',
    +      'Pothana'
    +    ]
    +  ],
    +  'Thaa': [[
    +    'sans-serif',
    +    'MV Boli'
    +  ]],
    +  'Thai': [
    +    [
    +      'sans-serif',
    +      'Tahoma,Thonburi'
    +    ],
    +    [
    +      'monospace',
    +      'Tahoma,Ayuthaya'
    +    ]
    +  ],
    +  'Tibt': [[
    +    'serif',
    +    'Microsoft Himalaya'
    +  ]],
    +  'Yiii': [[
    +    'sans-serif',
    +    'Microsoft Yi Baiti'
    +  ]],
    +  'Zsym': [[
    +    'sans-serif',
    +    'Apple Symbols'
    +  ]],
    +  'jp': [
    +    [
    +      '\uff30\u30b4\u30b7\u30c3\u30af',
    +      'MS PGothic,\uff2d\uff33 \uff30\u30b4\u30b7\u30c3\u30af,Hiragino Kaku G' +
    +     'othic Pro,\u30d2\u30e9\u30ae\u30ce\u89d2\u30b4 Pro W3,Sazanami Gothic' +
    +     ',\u3055\u3056\u306a\u307f\u30b4\u30b7\u30c3\u30af,sans-serif'
    +    ],
    +    [
    +      '\u30e1\u30a4\u30ea\u30aa',
    +      'Meiryo,\u30e1\u30a4\u30ea\u30aa,sans-serif'
    +    ],
    +    [
    +      '\uff30\u660e\u671d',
    +      'MS PMincho,\uff2d\uff33 \uff30\u660e\u671d,Hiragino Mincho Pro,\u30d2' +
    +     '\u30e9\u30ae\u30ce\u660e\u671d Pro W3,Sazanami Mincho,\u3055\u3056' +
    +     '\u306a\u307f\u660e\u671d,serif'
    +    ],
    +    [
    +      '\u7b49\u5e45',
    +      'MS Gothic,\uff2d\uff33 \u30b4\u30b7\u30c3\u30af,Osaka-Mono,Osaka\uff0d' +
    +     '\u7b49\u5e45,monospace'
    +    ]
    +  ],
    +  'ko': [
    +    [
    +      '\uace0\ub515',
    +      'Gulim,\uad74\ub9bc,AppleGothic,\uc560\ud50c\uace0\ub515,UnDotum,\uc740' +
    +     ' \ub3cb\uc6c0,Baekmuk Gulim,\ubc31\ubb35 \uad74\ub9bc,sans-serif'
    +    ],
    +    [
    +      '\ub9d1\uc740\uace0\ub515',
    +      'Malgun Gothic,\ub9d1\uc740\uace0\ub515,sans-serif'
    +    ],
    +    [
    +      '\ubc14\ud0d5',
    +      'Batang,\ubc14\ud0d5,AppleMyungjo,\uc560\ud50c\uba85\uc870,UnBatang,' +
    +     '\uc740 \ubc14\ud0d5,Baekmuk Batang,\ubc31\ubb35 \ubc14\ud0d5,serif'
    +    ],
    +    [
    +      '\uad81\uc11c',
    +      'Gungseo,\uad81\uc11c,serif'
    +    ],
    +    [
    +      '\uace0\uc815\ud3ed',
    +      'GulimChe,\uad74\ub9bc\uccb4,AppleGothic,\uc560\ud50c\uace0\ub515,monos' +
    +     'pace'
    +    ]
    +  ],
    +  'root': [
    +    [
    +      'sans-serif',
    +      'FreeSans'
    +    ],
    +    [
    +      'serif',
    +      'FreeSerif'
    +    ],
    +    [
    +      'monospace',
    +      'FreeMono'
    +    ]
    +  ],
    +  'transpose': {
    +    'zh': {
    +      'zh_Hant': {
    +        '\u5b8b\u4f53': '\u65b0\u7d30\u660e\u9ad4',
    +        '\u9ed1\u4f53': '\u5fae\u8edf\u6b63\u9ed1\u9ad4'
    +      }
    +    }
    +  },
    +  'ug': [[
    +    'serif',
    +    'Microsoft Uighur'
    +  ]],
    +  'zh': [
    +    [
    +      '\u9ed1\u4f53',
    +      'Microsoft JhengHei,\u5fae\u8edf\u6b63\u9ed1\u9ad4,SimHei,\u9ed1\u4f53,' +
    +     'MS Hei,STHeiti,\u534e\u6587\u9ed1\u4f53,Apple LiGothic Medium,\u860b' +
    +     '\u679c\u5137\u4e2d\u9ed1,LiHei Pro Medium,\u5137\u9ed1 Pro,STHeiti Li' +
    +     'ght,\u534e\u6587\u7ec6\u9ed1,AR PL ZenKai Uni,\u6587\u9f0ePL\u4e2d' +
    +     '\u6977Uni,sans-serif'
    +    ],
    +    [
    +      '\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53',
    +      'Microsoft YaHei,\u5fae\u8f6f\u96c5\u9ed1\u5b57\u4f53,sans-serif'
    +    ],
    +    [
    +      '\u5b8b\u4f53',
    +      'SimSun,\u5b8b\u4f53,MS Song,STSong,\u534e\u6587\u5b8b\u4f53,Apple LiSu' +
    +     'ng Light,\u860b\u679c\u5137\u7d30\u5b8b,LiSong Pro Light,\u5137\u5b8b' +
    +     ' Pro,STFangSong,\u534e\u6587\u4eff\u5b8b,AR PL ShanHeiSun Uni,\u6587' +
    +     '\u9f0ePL\u7ec6\u4e0a\u6d77\u5b8bUni,AR PL New Sung,\u6587\u9f0e PL ' +
    +     '\u65b0\u5b8b,serif'
    +    ],
    +    [
    +      '\u7d30\u660e\u9ad4',
    +      'NSimsun,\u65b0\u5b8b\u4f53,monospace'
    +    ]
    +  ]
    +};
    +/* ~!@# END #@!~ */
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/locale.js b/mapbox-gl/closure-library/closure/goog/locale/locale.js
    new file mode 100644
    index 0000000000..5763b4ed7a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/locale.js
    @@ -0,0 +1,403 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for dealing with Date formatting & Parsing,
    + * County and language name, TimeZone list.
    + * @suppress {deprecated} Use goog.i18n instead.
    + */
    +
    +
    +/**
    + * Namespace for locale related functions.
    + */
    +goog.provide('goog.locale');
    +
    +goog.require('goog.locale.nativeNameConstants');
    +
    +
    +/**
    + * Set currnet locale to the specified one.
    + * @param {string} localeName Locale name string. We are following the usage
    + *     in CLDR, but can make a few compromise for existing name compatibility.
    + */
    +goog.locale.setLocale = function(localeName) {
    +  // it is common to see people use '-' as locale part separator, normalize it.
    +  localeName = localeName.replace(/-/g, '_');
    +  goog.locale.activeLocale_ = localeName;
    +};
    +
    +
    +/**
    + * Retrieve the current locale
    + * @return {string} Current locale name string.
    + * @deprecated Use goog.LOCALE and goog.i18n instead.
    + */
    +goog.locale.getLocale = function() {
    +  if (!goog.locale.activeLocale_) {
    +    goog.locale.activeLocale_ = 'en';
    +  }
    +  return goog.locale.activeLocale_;
    +};
    +
    +
    +// Couple of constants to represent predefined Date/Time format type.
    +/**
    + * Enum of resources that can be registered.
    + * @enum {string}
    + */
    +goog.locale.Resource = {
    +  DATE_TIME_CONSTANTS: 'DateTimeConstants',
    +  NUMBER_FORMAT_CONSTANTS: 'NumberFormatConstants',
    +  TIME_ZONE_CONSTANTS: 'TimeZoneConstants',
    +  LOCAL_NAME_CONSTANTS: 'LocaleNameConstants',
    +
    +  TIME_ZONE_SELECTED_IDS: 'TimeZoneSelectedIds',
    +  TIME_ZONE_SELECTED_SHORT_NAMES: 'TimeZoneSelectedShortNames',
    +  TIME_ZONE_SELECTED_LONG_NAMES: 'TimeZoneSelectedLongNames',
    +  TIME_ZONE_ALL_LONG_NAMES: 'TimeZoneAllLongNames'
    +};
    +
    +
    +// BCP 47 language code:
    +//
    +// LanguageCode := LanguageSubtag
    +//                ("-" ScriptSubtag)?
    +//                ("-" RegionSubtag)?
    +//                ("-" VariantSubtag)?
    +//                ("@" Keyword "=" Value ("," Keyword "=" Value)* )?
    +//
    +// e.g. en-Latn-GB
    +//
    +// NOTICE:
    +// No special format checking is performed. If you pass a none valid
    +// language code as parameter to the following functions,
    +// you might get an unexpected result.
    +
    +
    +/**
    + * Returns the language-subtag of the given language code.
    + *
    + * @param {string} languageCode Language code to extract language subtag from.
    + * @return {string} Language subtag (in lowercase).
    + */
    +goog.locale.getLanguageSubTag = function(languageCode) {
    +  var result = languageCode.match(/^\w{2,3}([-_]|$)/);
    +  return result ? result[0].replace(/[_-]/g, '') : '';
    +};
    +
    +
    +/**
    + * Returns the region-sub-tag of the given language code.
    + *
    + * @param {string} languageCode Language code to extract region subtag from.
    + * @return {string} Region sub-tag (in uppercase).
    + */
    +goog.locale.getRegionSubTag = function(languageCode) {
    +  var result = languageCode.match(/[-_]([a-zA-Z]{2}|\d{3})([-_]|$)/);
    +  return result ? result[0].replace(/[_-]/g, '') : '';
    +};
    +
    +
    +/**
    + * Returns the script subtag of the locale with the first alphabet in uppercase
    + * and the rest 3 characters in lower case.
    + *
    + * @param {string} languageCode Language Code to extract script subtag from.
    + * @return {string} Script subtag.
    + */
    +goog.locale.getScriptSubTag = function(languageCode) {
    +  var result = languageCode.split(/[-_]/g);
    +  return result.length > 1 && result[1].match(/^[a-zA-Z]{4}$/) ?
    +      result[1] : '';
    +};
    +
    +
    +/**
    + * Returns the variant-sub-tag of the given language code.
    + *
    + * @param {string} languageCode Language code to extract variant subtag from.
    + * @return {string} Variant sub-tag.
    + */
    +goog.locale.getVariantSubTag = function(languageCode) {
    +  var result = languageCode.match(/[-_]([a-z]{2,})/);
    +  return result ? result[1] : '';
    +};
    +
    +
    +/**
    + * Returns the country name of the provided language code in its native
    + * language.
    + *
    + * This method depends on goog.locale.nativeNameConstants available from
    + * nativenameconstants.js. User of this method has to add dependency to this.
    + *
    + * @param {string} countryCode Code to lookup the country name for.
    + *
    + * @return {string} Country name for the provided language code.
    + */
    +goog.locale.getNativeCountryName = function(countryCode) {
    +  var key = goog.locale.getLanguageSubTag(countryCode) + '_' +
    +            goog.locale.getRegionSubTag(countryCode);
    +  return key in goog.locale.nativeNameConstants['COUNTRY'] ?
    +      goog.locale.nativeNameConstants['COUNTRY'][key] : countryCode;
    +};
    +
    +
    +/**
    + * Returns the localized country name for the provided language code in the
    + * current or provided locale symbols set.
    + *
    + * This method depends on goog.locale.LocaleNameConstants__<locale> available
    + * from http://go/js_locale_data. User of this method has to add dependency to
    + * this.
    + *
    + * @param {string} languageCode Language code to lookup the country name for.
    + * @param {Object=} opt_localeSymbols If omitted the current locale symbol
    + *     set is used.
    + *
    + * @return {string} Localized country name.
    + */
    +goog.locale.getLocalizedCountryName = function(languageCode,
    +                                               opt_localeSymbols) {
    +  if (!opt_localeSymbols) {
    +    opt_localeSymbols = goog.locale.getResource('LocaleNameConstants',
    +        goog.locale.getLocale());
    +  }
    +  var code = goog.locale.getRegionSubTag(languageCode);
    +  return code in opt_localeSymbols['COUNTRY'] ?
    +      opt_localeSymbols['COUNTRY'][code] : languageCode;
    +};
    +
    +
    +/**
    + * Returns the language name of the provided language code in its native
    + * language.
    + *
    + * This method depends on goog.locale.nativeNameConstants available from
    + * nativenameconstants.js. User of this method has to add dependency to this.
    + *
    + * @param {string} languageCode Language code to lookup the language name for.
    + *
    + * @return {string} Language name for the provided language code.
    + */
    +goog.locale.getNativeLanguageName = function(languageCode) {
    +  if (languageCode in goog.locale.nativeNameConstants['LANGUAGE'])
    +    return goog.locale.nativeNameConstants['LANGUAGE'][languageCode];
    +  var code = goog.locale.getLanguageSubTag(languageCode);
    +  return code in goog.locale.nativeNameConstants['LANGUAGE'] ?
    +      goog.locale.nativeNameConstants['LANGUAGE'][code] : languageCode;
    +};
    +
    +
    +/**
    + * Returns the localized language name for the provided language code in
    + * the current or provided locale symbols set.
    + *
    + * This method depends on goog.locale.LocaleNameConstants__<locale> available
    + * from http://go/js_locale_data. User of this method has to add dependency to
    + * this.
    + *
    + * @param {string} languageCode Language code to lookup the language name for.
    + * @param {Object=} opt_localeSymbols locale symbol set if given.
    + *
    + * @return {string} Localized language name of the provided language code.
    + */
    +goog.locale.getLocalizedLanguageName = function(languageCode,
    +                                                opt_localeSymbols) {
    +  if (!opt_localeSymbols) {
    +    opt_localeSymbols = goog.locale.getResource('LocaleNameConstants',
    +        goog.locale.getLocale());
    +  }
    +  if (languageCode in opt_localeSymbols['LANGUAGE'])
    +    return opt_localeSymbols['LANGUAGE'][languageCode];
    +  var code = goog.locale.getLanguageSubTag(languageCode);
    +  return code in opt_localeSymbols['LANGUAGE'] ?
    +      opt_localeSymbols['LANGUAGE'][code] : languageCode;
    +};
    +
    +
    +/**
    + * Register a resource object for certain locale.
    + * @param {Object} dataObj The resource object being registered.
    + * @param {goog.locale.Resource|string} resourceName String that represents
    + *     the type of resource.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerResource = function(dataObj, resourceName, localeName) {
    +  if (!goog.locale.resourceRegistry_[resourceName]) {
    +    goog.locale.resourceRegistry_[resourceName] = {};
    +  }
    +  goog.locale.resourceRegistry_[resourceName][localeName] = dataObj;
    +  // the first registered locale becomes active one. Usually there will be
    +  // only one locale per js binary bundle.
    +  if (!goog.locale.activeLocale_) {
    +    goog.locale.activeLocale_ = localeName;
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the required resource has already been registered.
    + * @param {goog.locale.Resource|string} resourceName String that represents
    + *     the type of resource.
    + * @param {string} localeName Locale ID.
    + * @return {boolean} Whether the required resource has already been registered.
    + */
    +goog.locale.isResourceRegistered = function(resourceName, localeName) {
    +  return resourceName in goog.locale.resourceRegistry_ &&
    +      localeName in goog.locale.resourceRegistry_[resourceName];
    +};
    +
    +
    +/**
    + * This object maps (resourceName, localeName) to a resourceObj.
    + * @type {Object}
    + * @private
    + */
    +goog.locale.resourceRegistry_ = {};
    +
    +
    +/**
    + * Registers the timezone constants object for a given locale name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + * @deprecated Use goog.i18n.TimeZone, no longer need this.
    + */
    +goog.locale.registerTimeZoneConstants = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.TIME_ZONE_CONSTANTS, localeName);
    +};
    +
    +
    +/**
    + * Registers the LocaleNameConstants constants object for a given locale name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerLocaleNameConstants = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.LOCAL_NAME_CONSTANTS, localeName);
    +};
    +
    +
    +/**
    + * Registers the TimeZoneSelectedIds constants object for a given locale name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerTimeZoneSelectedIds = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.TIME_ZONE_SELECTED_IDS, localeName);
    +};
    +
    +
    +/**
    + * Registers the TimeZoneSelectedShortNames constants object for a given
    + *     locale name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerTimeZoneSelectedShortNames = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.TIME_ZONE_SELECTED_SHORT_NAMES, localeName);
    +};
    +
    +
    +/**
    + * Registers the TimeZoneSelectedLongNames constants object for a given locale
    + *     name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerTimeZoneSelectedLongNames = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.TIME_ZONE_SELECTED_LONG_NAMES, localeName);
    +};
    +
    +
    +/**
    + * Registers the TimeZoneAllLongNames constants object for a given locale name.
    + * @param {Object} dataObj The resource object.
    + * @param {string} localeName Locale ID.
    + */
    +goog.locale.registerTimeZoneAllLongNames = function(dataObj, localeName) {
    +  goog.locale.registerResource(
    +      dataObj, goog.locale.Resource.TIME_ZONE_ALL_LONG_NAMES, localeName);
    +};
    +
    +
    +/**
    + * Retrieve specified resource for certain locale.
    + * @param {string} resourceName String that represents the type of resource.
    + * @param {string=} opt_locale Locale ID, if not given, current locale
    + *     will be assumed.
    + * @return {Object|undefined} The resource object that hold all the resource
    + *     data, or undefined if not available.
    + */
    +goog.locale.getResource = function(resourceName, opt_locale) {
    +  var locale = opt_locale ? opt_locale : goog.locale.getLocale();
    +
    +  if (!(resourceName in goog.locale.resourceRegistry_)) {
    +    return undefined;
    +  }
    +  return goog.locale.resourceRegistry_[resourceName][locale];
    +};
    +
    +
    +/**
    + * Retrieve specified resource for certain locale with fallback. For example,
    + * request of 'zh_CN' will be resolved in following order: zh_CN, zh, en.
    + * If none of the above succeeds, of if the resource as indicated by
    + * resourceName does not exist at all, undefined will be returned.
    + *
    + * @param {string} resourceName String that represents the type of resource.
    + * @param {string=} opt_locale locale ID, if not given, current locale
    + *     will be assumed.
    + * @return {Object|undefined} The resource object for desired locale.
    + */
    +goog.locale.getResourceWithFallback = function(resourceName, opt_locale) {
    +  var locale = opt_locale ? opt_locale : goog.locale.getLocale();
    +
    +  if (!(resourceName in goog.locale.resourceRegistry_)) {
    +    return undefined;
    +  }
    +
    +  if (locale in goog.locale.resourceRegistry_[resourceName]) {
    +    return goog.locale.resourceRegistry_[resourceName][locale];
    +  }
    +
    +  // if locale has multiple parts (2 atmost in reality), fallback to base part.
    +  var locale_parts = locale.split('_');
    +  if (locale_parts.length > 1 &&
    +      locale_parts[0] in goog.locale.resourceRegistry_[resourceName]) {
    +    return goog.locale.resourceRegistry_[resourceName][locale_parts[0]];
    +  }
    +
    +  // otherwise, fallback to 'en'
    +  return goog.locale.resourceRegistry_[resourceName]['en'];
    +};
    +
    +
    +// Export global functions that are used by the date time constants files.
    +// See http://go/js_locale_data
    +var registerLocalNameConstants = goog.locale.registerLocaleNameConstants;
    +
    +var registerTimeZoneSelectedIds = goog.locale.registerTimeZoneSelectedIds;
    +var registerTimeZoneSelectedShortNames =
    +    goog.locale.registerTimeZoneSelectedShortNames;
    +var registerTimeZoneSelectedLongNames =
    +    goog.locale.registerTimeZoneSelectedLongNames;
    +var registerTimeZoneAllLongNames = goog.locale.registerTimeZoneAllLongNames;
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/nativenameconstants.js b/mapbox-gl/closure-library/closure/goog/locale/nativenameconstants.js
    new file mode 100644
    index 0000000000..c517101635
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/nativenameconstants.js
    @@ -0,0 +1,1354 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview list of native country and language names.
    + *
    + * Warning: this file is automatically generated from CLDR.
    + * Please contact i18n team or change the script and regenerate data.
    + * Code location: http://go/generate_js_native_names.py
    + *
    + */
    +
    +
    +/**
    + * Namespace for native country and lanugage names
    + */
    +goog.provide('goog.locale.nativeNameConstants');
    +
    +/**
    + * Native country and language names
    + * @const {!Object<string, !Object<string, string>>}
    + */
    +
    +/* ~!@# genmethods.NativeDictAsJson() #@!~ */
    +goog.locale.nativeNameConstants = {
    +  'COUNTRY': {
    +    'AD': 'Andorra',
    +    'AE': '\u0627\u0644\u0627\u0645\u0627\u0631\u0627\u062a \u0627' +
    +        '\u0644\u0639\u0631\u0628\u064a\u0629 \u0627\u0644' +
    +        '\u0645\u062a\u062d\u062f\u0629',
    +    'AF': '\u0627\u0641\u063a\u0627\u0646\u0633\u062a\u0627\u0646',
    +    'AG': 'Antigua and Barbuda',
    +    'AI': 'Anguilla',
    +    'AL': 'Shqip\u00ebria',
    +    'AM': '\u0540\u0561\u0575\u0561\u057d\u057f\u0561\u0576\u056b ' +
    +        '\u0540\u0561\u0576\u0580\u0561\u057a\u0565\u057f\u0578' +
    +        '\u0582\u0569\u056b\u0582\u0576',
    +    'AN': 'Nederlandse Antillen',
    +    'AO': 'Angola',
    +    'AQ': 'Antarctica',
    +    'AR': 'Argentina',
    +    'AS': 'American Samoa',
    +    'AT': '\u00d6sterreich',
    +    'AU': 'Australia',
    +    'AW': 'Aruba',
    +    'AX': '\u00c5land',
    +    'AZ': 'Az\u0259rbaycan',
    +    'BA': 'Bosna i Hercegovina',
    +    'BB': 'Barbados',
    +    'BD': '\u09ac\u09be\u0982\u09b2\u09be\u09a6\u09c7\u09b6',
    +    'BE': 'Belgi\u00eb',
    +    'BF': 'Burkina Faso',
    +    'BG': '\u0411\u044a\u043b\u0433\u0430\u0440\u0438\u044f',
    +    'BH': '\u0627\u0644\u0628\u062d\u0631\u064a\u0646',
    +    'BI': 'Burundi',
    +    'BJ': 'B\u00e9nin',
    +    'BM': 'Bermuda',
    +    'BN': 'Brunei',
    +    'BO': 'Bolivia',
    +    'BR': 'Brasil',
    +    'BS': 'Bahamas',
    +    'BT': '\u092d\u0942\u091f\u093e\u0928',
    +    'BV': 'Bouvet Island',
    +    'BW': 'Botswana',
    +    'BY': '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u044c',
    +    'BZ': 'Belize',
    +    'CA': 'Canada',
    +    'CC': 'Cocos (Keeling) Islands',
    +    'CD': 'R\u00e9publique d\u00e9mocratique du Congo',
    +    'CF': 'R\u00e9publique centrafricaine',
    +    'CG': 'Congo',
    +    'CH': 'Schweiz',
    +    'CI': 'C\u00f4te d\u2019Ivoire',
    +    'CK': 'Cook Islands',
    +    'CL': 'Chile',
    +    'CM': 'Cameroun',
    +    'CN': '\u4e2d\u56fd',
    +    'CO': 'Colombia',
    +    'CR': 'Costa Rica',
    +    'CS': 'Serbia and Montenegro',
    +    'CU': 'Cuba',
    +    'CV': 'Cabo Verde',
    +    'CX': 'Christmas Island',
    +    'CY': '\u039a\u03cd\u03c0\u03c1\u03bf\u03c2',
    +    'CZ': '\u010cesk\u00e1 republika',
    +    'DD': 'East Germany',
    +    'DE': 'Deutschland',
    +    'DJ': 'Jabuuti',
    +    'DK': 'Danmark',
    +    'DM': 'Dominica',
    +    'DO': 'Rep\u00fablica Dominicana',
    +    'DZ': '\u0627\u0644\u062c\u0632\u0627\u0626\u0631',
    +    'EC': 'Ecuador',
    +    'EE': 'Eesti',
    +    'EG': '\u0645\u0635\u0631',
    +    'EH': '\u0627\u0644\u0635\u062d\u0631\u0627\u0621 \u0627\u0644' +
    +        '\u063a\u0631\u0628\u064a\u0629',
    +    'ER': '\u0627\u0631\u064a\u062a\u0631\u064a\u0627',
    +    'ES': 'Espa\u00f1a',
    +    'ET': '\u12a2\u1275\u12ee\u1335\u12eb',
    +    'FI': 'Suomi',
    +    'FJ': '\u092b\u093f\u091c\u0940',
    +    'FK': 'Falkland Islands',
    +    'FM': 'Micronesia',
    +    'FO': 'F\u00f8royar',
    +    'FR': 'France',
    +    'FX': 'Metropolitan France',
    +    'GA': 'Gabon',
    +    'GB': 'United Kingdom',
    +    'GD': 'Grenada',
    +    'GE': '\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4\u10da' +
    +        '\u10dd',
    +    'GF': 'Guyane fran\u00e7aise',
    +    'GG': 'Guernsey',
    +    'GH': 'Ghana',
    +    'GI': 'Gibraltar',
    +    'GL': 'Kalaallit Nunaat',
    +    'GM': 'Gambia',
    +    'GN': 'Guin\u00e9e',
    +    'GP': 'Guadeloupe',
    +    'GQ': 'Guin\u00e9e \u00e9quatoriale',
    +    'GR': '\u0395\u03bb\u03bb\u03ac\u03b4\u03b1',
    +    'GS': 'South Georgia and the South Sandwich Islands',
    +    'GT': 'Guatemala',
    +    'GU': 'Guam',
    +    'GW': 'Guin\u00e9 Bissau',
    +    'GY': 'Guyana',
    +    'HK': '\u9999\u6e2f',
    +    'HM': 'Heard Island and McDonald Islands',
    +    'HN': 'Honduras',
    +    'HR': 'Hrvatska',
    +    'HT': 'Ha\u00efti',
    +    'HU': 'Magyarorsz\u00e1g',
    +    'ID': 'Indonesia',
    +    'IE': 'Ireland',
    +    'IL': '\u05d9\u05e9\u05e8\u05d0\u05dc',
    +    'IM': 'Isle of Man',
    +    'IN': '\u092d\u093e\u0930\u0924',
    +    'IO': 'British Indian Ocean Territory',
    +    'IQ': '\u0627\u0644\u0639\u0631\u0627\u0642',
    +    'IR': '\u0627\u06cc\u0631\u0627\u0646',
    +    'IS': '\u00cdsland',
    +    'IT': 'Italia',
    +    'JE': 'Jersey',
    +    'JM': 'Jamaica',
    +    'JO': '\u0627\u0644\u0623\u0631\u062f\u0646',
    +    'JP': '\u65e5\u672c',
    +    'KE': 'Kenya',
    +    'KG': '\u041a\u044b\u0440\u0433\u044b\u0437\u0441\u0442\u0430' +
    +        '\u043d',
    +    'KH': '\u1780\u1798\u17d2\u1796\u17bb\u1787\u17b6',
    +    'KI': 'Kiribati',
    +    'KM': '\u062c\u0632\u0631 \u0627\u0644\u0642\u0645\u0631',
    +    'KN': 'Saint Kitts and Nevis',
    +    'KP': '\uc870\uc120 \ubbfc\uc8fc\uc8fc\uc758 \uc778\ubbfc ' +
    +        '\uacf5\ud654\uad6d',
    +    'KR': '\ub300\ud55c\ubbfc\uad6d',
    +    'KW': '\u0627\u0644\u0643\u0648\u064a\u062a',
    +    'KY': 'Cayman Islands',
    +    'KZ': '\u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430\u043d',
    +    'LA': '\u0e25\u0e32\u0e27',
    +    'LB': '\u0644\u0628\u0646\u0627\u0646',
    +    'LC': 'Saint Lucia',
    +    'LI': 'Liechtenstein',
    +    'LK': '\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8',
    +    'LR': 'Liberia',
    +    'LS': 'Lesotho',
    +    'LT': 'Lietuva',
    +    'LU': 'Luxembourg',
    +    'LV': 'Latvija',
    +    'LY': '\u0644\u064a\u0628\u064a\u0627',
    +    'MA': '\u0627\u0644\u0645\u063a\u0631\u0628',
    +    'MC': 'Monaco',
    +    'MD': 'Moldova, Republica',
    +    'ME': '\u0426\u0440\u043d\u0430 \u0413\u043e\u0440\u0430',
    +    'MG': 'Madagascar',
    +    'MH': 'Marshall Islands',
    +    'MK': '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0438\u0458' +
    +        '\u0430',
    +    'ML': '\u0645\u0627\u0644\u064a',
    +    'MM': 'Myanmar',
    +    'MN': '\u8499\u53e4',
    +    'MO': '\u6fb3\u95e8',
    +    'MP': 'Northern Mariana Islands',
    +    'MQ': 'Martinique',
    +    'MR': '\u0645\u0648\u0631\u064a\u062a\u0627\u0646\u064a\u0627',
    +    'MS': 'Montserrat',
    +    'MT': 'Malta',
    +    'MU': 'Mauritius',
    +    'MV': 'Maldives',
    +    'MW': 'Malawi',
    +    'MX': 'M\u00e9xico',
    +    'MY': 'Malaysia',
    +    'MZ': 'Mo\u00e7ambique',
    +    'NA': 'Namibia',
    +    'NC': 'Nouvelle-Cal\u00e9donie',
    +    'NE': 'Niger',
    +    'NF': 'Norfolk Island',
    +    'NG': 'Nigeria',
    +    'NI': 'Nicaragua',
    +    'NL': 'Nederland',
    +    'NO': 'Norge',
    +    'NP': '\u0928\u0947\u092a\u093e\u0932',
    +    'NR': 'Nauru',
    +    'NT': 'Neutral Zone',
    +    'NU': 'Niue',
    +    'NZ': 'New Zealand',
    +    'OM': '\u0639\u0645\u0627\u0646',
    +    'PA': 'Panam\u00e1',
    +    'PE': 'Per\u00fa',
    +    'PF': 'Polyn\u00e9sie fran\u00e7aise',
    +    'PG': 'Papua New Guinea',
    +    'PH': 'Philippines',
    +    'PK': '\u067e\u0627\u06a9\u0633\u062a\u0627\u0646',
    +    'PL': 'Polska',
    +    'PM': 'Saint-Pierre-et-Miquelon',
    +    'PN': 'Pitcairn',
    +    'PR': 'Puerto Rico',
    +    'PS': '\u0641\u0644\u0633\u0637\u064a\u0646',
    +    'PT': 'Portugal',
    +    'PW': 'Palau',
    +    'PY': 'Paraguay',
    +    'QA': '\u0642\u0637\u0631',
    +    'QO': 'Outlying Oceania',
    +    'QU': 'European Union',
    +    'RE': 'R\u00e9union',
    +    'RO': 'Rom\u00e2nia',
    +    'RS': '\u0421\u0440\u0431\u0438\u0458\u0430',
    +    'RU': '\u0420\u043e\u0441\u0441\u0438\u044f',
    +    'RW': 'Rwanda',
    +    'SA': '\u0627\u0644\u0645\u0645\u0644\u0643\u0629 \u0627\u0644' +
    +        '\u0639\u0631\u0628\u064a\u0629 \u0627\u0644\u0633' +
    +        '\u0639\u0648\u062f\u064a\u0629',
    +    'SB': 'Solomon Islands',
    +    'SC': 'Seychelles',
    +    'SD': '\u0627\u0644\u0633\u0648\u062f\u0627\u0646',
    +    'SE': 'Sverige',
    +    'SG': '\u65b0\u52a0\u5761',
    +    'SH': 'Saint Helena',
    +    'SI': 'Slovenija',
    +    'SJ': 'Svalbard og Jan Mayen',
    +    'SK': 'Slovensk\u00e1 republika',
    +    'SL': 'Sierra Leone',
    +    'SM': 'San Marino',
    +    'SN': 'S\u00e9n\u00e9gal',
    +    'SO': 'Somali',
    +    'SR': 'Suriname',
    +    'ST': 'S\u00e3o Tom\u00e9 e Pr\u00edncipe',
    +    'SU': 'Union of Soviet Socialist Republics',
    +    'SV': 'El Salvador',
    +    'SY': '\u0633\u0648\u0631\u064a\u0627',
    +    'SZ': 'Swaziland',
    +    'TC': 'Turks and Caicos Islands',
    +    'TD': '\u062a\u0634\u0627\u062f',
    +    'TF': 'French Southern Territories',
    +    'TG': 'Togo',
    +    'TH': '\u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28\u0e44\u0e17\u0e22',
    +    'TJ': '\u062a\u0627\u062c\u06cc\u06a9\u0633\u062a\u0627\u0646',
    +    'TK': 'Tokelau',
    +    'TL': 'Timor Leste',
    +    'TM': '\u0422\u0443\u0440\u043a\u043c\u0435\u043d\u0438\u0441' +
    +        '\u0442\u0430\u043d',
    +    'TN': '\u062a\u0648\u0646\u0633',
    +    'TO': 'Tonga',
    +    'TR': 'T\u00fcrkiye',
    +    'TT': 'Trinidad y Tobago',
    +    'TV': 'Tuvalu',
    +    'TW': '\u53f0\u6e7e',
    +    'TZ': 'Tanzania',
    +    'UA': '\u0423\u043a\u0440\u0430\u0457\u043d\u0430',
    +    'UG': 'Uganda',
    +    'UM': 'United States Minor Outlying Islands',
    +    'US': 'United States',
    +    'UY': 'Uruguay',
    +    'UZ': '\u040e\u0437\u0431\u0435\u043a\u0438\u0441\u0442\u043e' +
    +        '\u043d',
    +    'VA': 'Vaticano',
    +    'VC': 'Saint Vincent and the Grenadines',
    +    'VE': 'Venezuela',
    +    'VG': 'British Virgin Islands',
    +    'VI': 'U.S. Virgin Islands',
    +    'VN': 'Vi\u1ec7t Nam',
    +    'VU': 'Vanuatu',
    +    'WF': 'Wallis-et-Futuna',
    +    'WS': 'Samoa',
    +    'YD': 'People\'s Democratic Republic of Yemen',
    +    'YE': '\u0627\u0644\u064a\u0645\u0646',
    +    'YT': 'Mayotte',
    +    'ZA': 'South Africa',
    +    'ZM': 'Zambia',
    +    'ZW': 'Zimbabwe',
    +    'ZZ': 'Unknown or Invalid Region',
    +    'aa_DJ': 'Jabuuti',
    +    'aa_ER': '\u00c9rythr\u00e9e',
    +    'aa_ER_SAAHO': '\u00c9rythr\u00e9e',
    +    'aa_ET': 'Itoophiyaa',
    +    'af_NA': 'Namibi\u00eb',
    +    'af_ZA': 'Suid-Afrika',
    +    'ak_GH': 'Ghana',
    +    'am_ET': '\u12a2\u1275\u12ee\u1335\u12eb',
    +    'ar_AE': '\u0627\u0644\u0627\u0645\u0627\u0631\u0627\u062a ' +
    +        '\u0627\u0644\u0639\u0631\u0628\u064a\u0629 \u0627' +
    +        '\u0644\u0645\u062a\u062d\u062f\u0629',
    +    'ar_BH': '\u0627\u0644\u0628\u062d\u0631\u064a\u0646',
    +    'ar_DJ': '\u062c\u064a\u0628\u0648\u062a\u064a',
    +    'ar_DZ': '\u0627\u0644\u062c\u0632\u0627\u0626\u0631',
    +    'ar_EG': '\u0645\u0635\u0631',
    +    'ar_EH': '\u0627\u0644\u0635\u062d\u0631\u0627\u0621 \u0627' +
    +        '\u0644\u063a\u0631\u0628\u064a\u0629',
    +    'ar_ER': '\u0627\u0631\u064a\u062a\u0631\u064a\u0627',
    +    'ar_IL': '\u0627\u0633\u0631\u0627\u0626\u064a\u0644',
    +    'ar_IQ': '\u0627\u0644\u0639\u0631\u0627\u0642',
    +    'ar_JO': '\u0627\u0644\u0623\u0631\u062f\u0646',
    +    'ar_KM': '\u062c\u0632\u0631 \u0627\u0644\u0642\u0645\u0631',
    +    'ar_KW': '\u0627\u0644\u0643\u0648\u064a\u062a',
    +    'ar_LB': '\u0644\u0628\u0646\u0627\u0646',
    +    'ar_LY': '\u0644\u064a\u0628\u064a\u0627',
    +    'ar_MA': '\u0627\u0644\u0645\u063a\u0631\u0628',
    +    'ar_MR': '\u0645\u0648\u0631\u064a\u062a\u0627\u0646\u064a' +
    +        '\u0627',
    +    'ar_OM': '\u0639\u0645\u0627\u0646',
    +    'ar_PS': '\u0641\u0644\u0633\u0637\u064a\u0646',
    +    'ar_QA': '\u0642\u0637\u0631',
    +    'ar_SA': '\u0627\u0644\u0645\u0645\u0644\u0643\u0629 \u0627' +
    +        '\u0644\u0639\u0631\u0628\u064a\u0629 \u0627\u0644' +
    +        '\u0633\u0639\u0648\u062f\u064a\u0629',
    +    'ar_SD': '\u0627\u0644\u0633\u0648\u062f\u0627\u0646',
    +    'ar_SY': '\u0633\u0648\u0631\u064a\u0627',
    +    'ar_TD': '\u062a\u0634\u0627\u062f',
    +    'ar_TN': '\u062a\u0648\u0646\u0633',
    +    'ar_YE': '\u0627\u0644\u064a\u0645\u0646',
    +    'as_IN': '\u09ad\u09be\u09f0\u09a4',
    +    'ay_BO': 'Bolivia',
    +    'az_AZ': 'Az\u0259rbaycan',
    +    'az_Cyrl_AZ': '\u0410\u0437\u04d9\u0440\u0431\u0430\u0458' +
    +        '\u04b9\u0430\u043d',
    +    'az_Latn_AZ': 'Azerbaycan',
    +    'be_BY': '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u044c',
    +    'bg_BG': '\u0411\u044a\u043b\u0433\u0430\u0440\u0438\u044f',
    +    'bi_VU': 'Vanuatu',
    +    'bn_BD': '\u09ac\u09be\u0982\u09b2\u09be\u09a6\u09c7\u09b6',
    +    'bn_IN': '\u09ad\u09be\u09b0\u09a4',
    +    'bo_CN': '\u0f62\u0f92\u0fb1\u0f0b\u0f53\u0f42',
    +    'bo_IN': '\u0f62\u0f92\u0fb1\u0f0b\u0f42\u0f62\u0f0b',
    +    'bs_BA': 'Bosna i Hercegovina',
    +    'byn_ER': '\u12a4\u122d\u1275\u122b',
    +    'ca_AD': 'Andorra',
    +    'ca_ES': 'Espanya',
    +    'cch_NG': 'Nigeria',
    +    'ch_GU': 'Guam',
    +    'chk_FM': 'Micronesia',
    +    'cop_Arab_EG': '\u0645\u0635\u0631',
    +    'cop_Arab_US': '\u0627\u0644\u0648\u0644\u0627\u064a\u0627' +
    +        '\u062a \u0627\u0644\u0645\u062a\u062d\u062f' +
    +        '\u0629 \u0627\u0644\u0623\u0645\u0631\u064a' +
    +        '\u0643\u064a\u0629',
    +    'cop_EG': '\u0645\u0635\u0631',
    +    'cop_US': '\u0627\u0644\u0648\u0644\u0627\u064a\u0627\u062a ' +
    +        '\u0627\u0644\u0645\u062a\u062d\u062f\u0629 \u0627' +
    +        '\u0644\u0623\u0645\u0631\u064a\u0643\u064a\u0629',
    +    'cs_CZ': '\u010cesk\u00e1 republika',
    +    'cy_GB': 'Prydain Fawr',
    +    'da_DK': 'Danmark',
    +    'da_GL': 'Gr\u00f8nland',
    +    'de_AT': '\u00d6sterreich',
    +    'de_BE': 'Belgien',
    +    'de_CH': 'Schweiz',
    +    'de_DE': 'Deutschland',
    +    'de_LI': 'Liechtenstein',
    +    'de_LU': 'Luxemburg',
    +    'dv_MV': 'Maldives',
    +    'dz_BT': 'Bhutan',
    +    'ee_GH': 'Ghana',
    +    'ee_TG': 'Togo',
    +    'efi_NG': 'Nigeria',
    +    'el_CY': '\u039a\u03cd\u03c0\u03c1\u03bf\u03c2',
    +    'el_GR': '\u0395\u03bb\u03bb\u03ac\u03b4\u03b1',
    +    'en_AG': 'Antigua and Barbuda',
    +    'en_AI': 'Anguilla',
    +    'en_AS': 'American Samoa',
    +    'en_AU': 'Australia',
    +    'en_BB': 'Barbados',
    +    'en_BE': 'Belgium',
    +    'en_BM': 'Bermuda',
    +    'en_BS': 'Bahamas',
    +    'en_BW': 'Botswana',
    +    'en_BZ': 'Belize',
    +    'en_CA': 'Canada',
    +    'en_CC': 'Cocos Islands',
    +    'en_CK': 'Cook Islands',
    +    'en_CM': 'Cameroon',
    +    'en_CX': 'Christmas Island',
    +    'en_DM': 'Dominica',
    +    'en_FJ': 'Fiji',
    +    'en_FK': 'Falkland Islands',
    +    'en_FM': 'Micronesia',
    +    'en_GB': 'United Kingdom',
    +    'en_GD': 'Grenada',
    +    'en_GG': 'Guernsey',
    +    'en_GH': 'Ghana',
    +    'en_GI': 'Gibraltar',
    +    'en_GM': 'Gambia',
    +    'en_GU': 'Guam',
    +    'en_GY': 'Guyana',
    +    'en_HK': 'Hong Kong',
    +    'en_HN': 'Honduras',
    +    'en_IE': 'Ireland',
    +    'en_IM': 'Isle of Man',
    +    'en_IN': 'India',
    +    'en_JE': 'Jersey',
    +    'en_JM': 'Jamaica',
    +    'en_KE': 'Kenya',
    +    'en_KI': 'Kiribati',
    +    'en_KN': 'Saint Kitts and Nevis',
    +    'en_KY': 'Cayman Islands',
    +    'en_LC': 'Saint Lucia',
    +    'en_LR': 'Liberia',
    +    'en_LS': 'Lesotho',
    +    'en_MH': 'Marshall Islands',
    +    'en_MP': 'Northern Mariana Islands',
    +    'en_MS': 'Montserrat',
    +    'en_MT': 'Malta',
    +    'en_MU': 'Mauritius',
    +    'en_MW': 'Malawi',
    +    'en_NA': 'Namibia',
    +    'en_NF': 'Norfolk Island',
    +    'en_NG': 'Nigeria',
    +    'en_NR': 'Nauru',
    +    'en_NU': 'Niue',
    +    'en_NZ': 'New Zealand',
    +    'en_PG': 'Papua New Guinea',
    +    'en_PH': 'Philippines',
    +    'en_PK': 'Pakistan',
    +    'en_PN': 'Pitcairn',
    +    'en_PR': 'Puerto Rico',
    +    'en_RW': 'Rwanda',
    +    'en_SB': 'Solomon Islands',
    +    'en_SC': 'Seychelles',
    +    'en_SG': 'Singapore',
    +    'en_SH': 'Saint Helena',
    +    'en_SL': 'Sierra Leone',
    +    'en_SZ': 'Swaziland',
    +    'en_TC': 'Turks and Caicos Islands',
    +    'en_TK': 'Tokelau',
    +    'en_TO': 'Tonga',
    +    'en_TT': 'Trinidad and Tobago',
    +    'en_TV': 'Tuvalu',
    +    'en_TZ': 'Tanzania',
    +    'en_UG': 'Uganda',
    +    'en_UM': 'United States Minor Outlying Islands',
    +    'en_US': 'United States',
    +    'en_US_POSIX': 'United States',
    +    'en_VC': 'Saint Vincent and the Grenadines',
    +    'en_VG': 'British Virgin Islands',
    +    'en_VI': 'U.S. Virgin Islands',
    +    'en_VU': 'Vanuatu',
    +    'en_WS': 'Samoa',
    +    'en_ZA': 'South Africa',
    +    'en_ZM': 'Zambia',
    +    'en_ZW': 'Zimbabwe',
    +    'es_AR': 'Argentina',
    +    'es_BO': 'Bolivia',
    +    'es_CL': 'Chile',
    +    'es_CO': 'Colombia',
    +    'es_CR': 'Costa Rica',
    +    'es_CU': 'Cuba',
    +    'es_DO': 'Rep\u00fablica Dominicana',
    +    'es_EC': 'Ecuador',
    +    'es_ES': 'Espa\u00f1a',
    +    'es_GQ': 'Guinea Ecuatorial',
    +    'es_GT': 'Guatemala',
    +    'es_HN': 'Honduras',
    +    'es_MX': 'M\u00e9xico',
    +    'es_NI': 'Nicaragua',
    +    'es_PA': 'Panam\u00e1',
    +    'es_PE': 'Per\u00fa',
    +    'es_PH': 'Filipinas',
    +    'es_PR': 'Puerto Rico',
    +    'es_PY': 'Paraguay',
    +    'es_SV': 'El Salvador',
    +    'es_US': 'Estados Unidos',
    +    'es_UY': 'Uruguay',
    +    'es_VE': 'Venezuela',
    +    'et_EE': 'Eesti',
    +    'eu_ES': 'Espainia',
    +    'fa_AF': '\u0627\u0641\u063a\u0627\u0646\u0633\u062a\u0627' +
    +        '\u0646',
    +    'fa_IR': '\u0627\u06cc\u0631\u0627\u0646',
    +    'fi_FI': 'Suomi',
    +    'fil_PH': 'Philippines',
    +    'fj_FJ': 'Fiji',
    +    'fo_FO': 'F\u00f8royar',
    +    'fr_BE': 'Belgique',
    +    'fr_BF': 'Burkina Faso',
    +    'fr_BI': 'Burundi',
    +    'fr_BJ': 'B\u00e9nin',
    +    'fr_CA': 'Canada',
    +    'fr_CD': 'R\u00e9publique d\u00e9mocratique du Congo',
    +    'fr_CF': 'R\u00e9publique centrafricaine',
    +    'fr_CG': 'Congo',
    +    'fr_CH': 'Suisse',
    +    'fr_CI': 'C\u00f4te d\u2019Ivoire',
    +    'fr_CM': 'Cameroun',
    +    'fr_DJ': 'Djibouti',
    +    'fr_DZ': 'Alg\u00e9rie',
    +    'fr_FR': 'France',
    +    'fr_GA': 'Gabon',
    +    'fr_GF': 'Guyane fran\u00e7aise',
    +    'fr_GN': 'Guin\u00e9e',
    +    'fr_GP': 'Guadeloupe',
    +    'fr_GQ': 'Guin\u00e9e \u00e9quatoriale',
    +    'fr_HT': 'Ha\u00efti',
    +    'fr_KM': 'Comores',
    +    'fr_LU': 'Luxembourg',
    +    'fr_MA': 'Maroc',
    +    'fr_MC': 'Monaco',
    +    'fr_MG': 'Madagascar',
    +    'fr_ML': 'Mali',
    +    'fr_MQ': 'Martinique',
    +    'fr_MU': 'Maurice',
    +    'fr_NC': 'Nouvelle-Cal\u00e9donie',
    +    'fr_NE': 'Niger',
    +    'fr_PF': 'Polyn\u00e9sie fran\u00e7aise',
    +    'fr_PM': 'Saint-Pierre-et-Miquelon',
    +    'fr_RE': 'R\u00e9union',
    +    'fr_RW': 'Rwanda',
    +    'fr_SC': 'Seychelles',
    +    'fr_SN': 'S\u00e9n\u00e9gal',
    +    'fr_SY': 'Syrie',
    +    'fr_TD': 'Tchad',
    +    'fr_TG': 'Togo',
    +    'fr_TN': 'Tunisie',
    +    'fr_VU': 'Vanuatu',
    +    'fr_WF': 'Wallis-et-Futuna',
    +    'fr_YT': 'Mayotte',
    +    'fur_IT': 'Italia',
    +    'ga_IE': '\u00c9ire',
    +    'gaa_GH': 'Ghana',
    +    'gez_ER': '\u12a4\u122d\u1275\u122b',
    +    'gez_ET': '\u12a2\u1275\u12ee\u1335\u12eb',
    +    'gil_KI': 'Kiribati',
    +    'gl_ES': 'Espa\u00f1a',
    +    'gn_PY': 'Paraguay',
    +    'gu_IN': '\u0aad\u0abe\u0ab0\u0aa4',
    +    'gv_GB': 'Rywvaneth Unys',
    +    'ha_Arab_NG': '\u0646\u064a\u062c\u064a\u0631\u064a\u0627',
    +    'ha_GH': '\u063a\u0627\u0646\u0627',
    +    'ha_Latn_GH': 'Ghana',
    +    'ha_Latn_NE': 'Niger',
    +    'ha_Latn_NG': 'Nig\u00e9ria',
    +    'ha_NE': '\u0627\u0644\u0646\u064a\u062c\u0631',
    +    'ha_NG': '\u0646\u064a\u062c\u064a\u0631\u064a\u0627',
    +    'haw_US': '\u02bbAmelika Hui P\u016b \u02bbIa',
    +    'he_IL': '\u05d9\u05e9\u05e8\u05d0\u05dc',
    +    'hi_IN': '\u092d\u093e\u0930\u0924',
    +    'ho_PG': 'Papua New Guinea',
    +    'hr_BA': 'Bosna i Hercegovina',
    +    'hr_HR': 'Hrvatska',
    +    'ht_HT': 'Ha\u00efti',
    +    'hu_HU': 'Magyarorsz\u00e1g',
    +    'hy_AM': '\u0540\u0561\u0575\u0561\u057d\u057f\u0561\u0576' +
    +        '\u056b \u0540\u0561\u0576\u0580\u0561\u057a\u0565' +
    +        '\u057f\u0578\u0582\u0569\u056b\u0582\u0576',
    +    'hy_AM_REVISED': '\u0540\u0561\u0575\u0561\u057d\u057f\u0561' +
    +        '\u0576\u056b \u0540\u0561\u0576\u0580\u0561' +
    +        '\u057a\u0565\u057f\u0578\u0582\u0569\u056b' +
    +        '\u0582\u0576',
    +    'id_ID': 'Indonesia',
    +    'ig_NG': 'Nigeria',
    +    'ii_CN': '\ua34f\ua1e9',
    +    'is_IS': '\u00cdsland',
    +    'it_CH': 'Svizzera',
    +    'it_IT': 'Italia',
    +    'it_SM': 'San Marino',
    +    'ja_JP': '\u65e5\u672c',
    +    'ka_GE': '\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4' +
    +        '\u10da\u10dd',
    +    'kaj_NG': 'Nigeria',
    +    'kam_KE': 'Kenya',
    +    'kcg_NG': 'Nigeria',
    +    'kfo_NG': 'Nig\u00e9ria',
    +    'kk_KZ': '\u049a\u0430\u0437\u0430\u049b\u0441\u0442\u0430' +
    +        '\u043d',
    +    'kl_GL': 'Kalaallit Nunaat',
    +    'km_KH': '\u1780\u1798\u17d2\u1796\u17bb\u1787\u17b6',
    +    'kn_IN': '\u0cad\u0cbe\u0cb0\u0ca4',
    +    'ko_KP': '\uc870\uc120 \ubbfc\uc8fc\uc8fc\uc758 \uc778\ubbfc ' +
    +        '\uacf5\ud654\uad6d',
    +    'ko_KR': '\ub300\ud55c\ubbfc\uad6d',
    +    'kok_IN': '\u092d\u093e\u0930\u0924',
    +    'kos_FM': 'Micronesia',
    +    'kpe_GN': 'Guin\u00e9e',
    +    'kpe_LR': 'Lib\u00e9ria',
    +    'ks_IN': '\u092d\u093e\u0930\u0924',
    +    'ku_IQ': 'Irak',
    +    'ku_IR': '\u0130ran',
    +    'ku_Latn_IQ': 'Irak',
    +    'ku_Latn_IR': '\u0130ran',
    +    'ku_Latn_SY': 'Suriye',
    +    'ku_Latn_TR': 'T\u00fcrkiye',
    +    'ku_SY': 'Suriye',
    +    'ku_TR': 'T\u00fcrkiye',
    +    'kw_GB': 'Rywvaneth Unys',
    +    'ky_Cyrl_KG': '\u041a\u044b\u0440\u0433\u044b\u0437\u0441' +
    +        '\u0442\u0430\u043d',
    +    'ky_KG': 'K\u0131rg\u0131zistan',
    +    'la_VA': 'Vaticano',
    +    'lb_LU': 'Luxembourg',
    +    'ln_CD': 'R\u00e9publique d\u00e9mocratique du Congo',
    +    'ln_CG': 'Kongo',
    +    'lo_LA': 'Laos',
    +    'lt_LT': 'Lietuva',
    +    'lv_LV': 'Latvija',
    +    'mg_MG': 'Madagascar',
    +    'mh_MH': 'Marshall Islands',
    +    'mi_NZ': 'New Zealand',
    +    'mk_MK': '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0438' +
    +        '\u0458\u0430',
    +    'ml_IN': '\u0d07\u0d28\u0d4d\u0d24\u0d4d\u0d2f',
    +    'mn_Cyrl_MN': '\u041c\u043e\u043d\u0433\u043e\u043b\u0438' +
    +        '\u044f',
    +    'mn_MN': '\u041c\u043e\u043d\u0433\u043e\u043b\u0438\u044f',
    +    'mr_IN': '\u092d\u093e\u0930\u0924',
    +    'ms_BN': 'Brunei',
    +    'ms_MY': 'Malaysia',
    +    'ms_SG': 'Singapura',
    +    'mt_MT': 'Malta',
    +    'my_MM': 'Myanmar',
    +    'na_NR': 'Nauru',
    +    'nb_NO': 'Norge',
    +    'nb_SJ': 'Svalbard og Jan Mayen',
    +    'ne_NP': '\u0928\u0947\u092a\u093e\u0932',
    +    'niu_NU': 'Niue',
    +    'nl_AN': 'Nederlandse Antillen',
    +    'nl_AW': 'Aruba',
    +    'nl_BE': 'Belgi\u00eb',
    +    'nl_NL': 'Nederland',
    +    'nl_SR': 'Suriname',
    +    'nn_NO': 'Noreg',
    +    'nr_ZA': 'South Africa',
    +    'nso_ZA': 'South Africa',
    +    'ny_MW': 'Malawi',
    +    'om_ET': 'Itoophiyaa',
    +    'om_KE': 'Keeniyaa',
    +    'or_IN': '\u0b2d\u0b3e\u0b30\u0b24',
    +    'pa_Arab_PK': '\u067e\u0627\u06a9\u0633\u062a\u0627\u0646',
    +    'pa_Guru_IN': '\u0a2d\u0a3e\u0a30\u0a24',
    +    'pa_IN': '\u0a2d\u0a3e\u0a30\u0a24',
    +    'pa_PK': '\u067e\u0627\u06a9\u0633\u062a\u0627\u0646',
    +    'pap_AN': 'Nederlandse Antillen',
    +    'pau_PW': 'Palau',
    +    'pl_PL': 'Polska',
    +    'pon_FM': 'Micronesia',
    +    'ps_AF': '\u0627\u0641\u063a\u0627\u0646\u0633\u062a\u0627' +
    +        '\u0646',
    +    'pt_AO': 'Angola',
    +    'pt_BR': 'Brasil',
    +    'pt_CV': 'Cabo Verde',
    +    'pt_GW': 'Guin\u00e9 Bissau',
    +    'pt_MZ': 'Mo\u00e7ambique',
    +    'pt_PT': 'Portugal',
    +    'pt_ST': 'S\u00e3o Tom\u00e9 e Pr\u00edncipe',
    +    'pt_TL': 'Timor Leste',
    +    'qu_BO': 'Bolivia',
    +    'qu_PE': 'Per\u00fa',
    +    'rm_CH': 'Schweiz',
    +    'rn_BI': 'Burundi',
    +    'ro_MD': 'Moldova, Republica',
    +    'ro_RO': 'Rom\u00e2nia',
    +    'ru_BY': '\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u044c',
    +    'ru_KG': '\u041a\u044b\u0440\u0433\u044b\u0437\u0441\u0442' +
    +        '\u0430\u043d',
    +    'ru_KZ': '\u041a\u0430\u0437\u0430\u0445\u0441\u0442\u0430' +
    +        '\u043d',
    +    'ru_RU': '\u0420\u043e\u0441\u0441\u0438\u044f',
    +    'ru_UA': '\u0423\u043a\u0440\u0430\u0438\u043d\u0430',
    +    'rw_RW': 'Rwanda',
    +    'sa_IN': '\u092d\u093e\u0930\u0924',
    +    'sd_Deva_IN': '\u092d\u093e\u0930\u0924',
    +    'sd_IN': '\u092d\u093e\u0930\u0924',
    +    'se_FI': 'Finland',
    +    'se_NO': 'Norge',
    +    'sg_CF': 'R\u00e9publique centrafricaine',
    +    'sh_BA': 'Bosnia and Herzegovina',
    +    'sh_CS': 'Serbia and Montenegro',
    +    'si_LK': 'Sri Lanka',
    +    'sid_ET': 'Itoophiyaa',
    +    'sk_SK': 'Slovensk\u00e1 republika',
    +    'sl_SI': 'Slovenija',
    +    'sm_AS': 'American Samoa',
    +    'sm_WS': 'Samoa',
    +    'so_DJ': 'Jabuuti',
    +    'so_ET': 'Itoobiya',
    +    'so_KE': 'Kiiniya',
    +    'so_SO': 'Soomaaliya',
    +    'sq_AL': 'Shqip\u00ebria',
    +    'sr_BA': '\u0411\u043e\u0441\u043d\u0430 \u0438 \u0425\u0435' +
    +        '\u0440\u0446\u0435\u0433\u043e\u0432\u0438\u043d' +
    +        '\u0430',
    +    'sr_CS': '\u0421\u0440\u0431\u0438\u0458\u0430 \u0438 \u0426' +
    +        '\u0440\u043d\u0430 \u0413\u043e\u0440\u0430',
    +    'sr_Cyrl_BA': '\u0411\u043e\u0441\u043d\u0438\u044f',
    +    'sr_Cyrl_CS': '\u0421\u0435\u0440\u0431\u0438\u044f \u0438 ' +
    +        '\u0427\u0435\u0440\u043d\u043e\u0433\u043e' +
    +        '\u0440\u0438\u044f',
    +    'sr_Cyrl_ME': '\u0427\u0435\u0440\u043d\u043e\u0433\u043e' +
    +        '\u0440\u0438\u044f',
    +    'sr_Cyrl_RS': '\u0421\u0435\u0440\u0431\u0438\u044f',
    +    'sr_Latn_BA': 'Bosna i Hercegovina',
    +    'sr_Latn_CS': 'Srbija i Crna Gora',
    +    'sr_Latn_ME': 'Crna Gora',
    +    'sr_Latn_RS': 'Srbija',
    +    'sr_ME': '\u0426\u0440\u043d\u0430 \u0413\u043e\u0440\u0430',
    +    'sr_RS': '\u0421\u0440\u0431\u0438\u0458\u0430',
    +    'ss_SZ': 'Swaziland',
    +    'ss_ZA': 'South Africa',
    +    'st_LS': 'Lesotho',
    +    'st_ZA': 'South Africa',
    +    'su_ID': 'Indonesia',
    +    'sv_AX': '\u00c5land',
    +    'sv_FI': 'Finland',
    +    'sv_SE': 'Sverige',
    +    'sw_KE': 'Kenya',
    +    'sw_TZ': 'Tanzania',
    +    'sw_UG': 'Uganda',
    +    'swb_KM': '\u062c\u0632\u0631 \u0627\u0644\u0642\u0645\u0631',
    +    'syr_SY': 'Syria',
    +    'ta_IN': '\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe',
    +    'ta_LK': '\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8',
    +    'ta_SG': '\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa' +
    +        '\u0bc2\u0bb0\u0bcd',
    +    'te_IN': '\u0c2d\u0c3e\u0c30\u0c24 \u0c26\u0c47\u0c33\u0c02',
    +    'tet_TL': 'Timor Leste',
    +    'tg_Cyrl_TJ': '\u0422\u0430\u0434\u0436\u0438\u043a\u0438' +
    +        '\u0441\u0442\u0430\u043d',
    +    'tg_TJ': '\u062a\u0627\u062c\u06a9\u0633\u062a\u0627\u0646',
    +    'th_TH': '\u0e1b\u0e23\u0e30\u0e40\u0e17\u0e28\u0e44\u0e17' +
    +        '\u0e22',
    +    'ti_ER': '\u12a4\u122d\u1275\u122b',
    +    'ti_ET': '\u12a2\u1275\u12ee\u1335\u12eb',
    +    'tig_ER': '\u12a4\u122d\u1275\u122b',
    +    'tk_TM': '\u062a\u0631\u06a9\u0645\u0646\u0633\u062a\u0627' +
    +        '\u0646',
    +    'tkl_TK': 'Tokelau',
    +    'tn_BW': 'Botswana',
    +    'tn_ZA': 'South Africa',
    +    'to_TO': 'Tonga',
    +    'tpi_PG': 'Papua New Guinea',
    +    'tr_CY': 'G\u00fcney K\u0131br\u0131s Rum Kesimi',
    +    'tr_TR': 'T\u00fcrkiye',
    +    'ts_ZA': 'South Africa',
    +    'tt_RU': '\u0420\u043e\u0441\u0441\u0438\u044f',
    +    'tvl_TV': 'Tuvalu',
    +    'ty_PF': 'Polyn\u00e9sie fran\u00e7aise',
    +    'uk_UA': '\u0423\u043a\u0440\u0430\u0457\u043d\u0430',
    +    'uli_FM': 'Micronesia',
    +    'und_ZZ': 'Unknown or Invalid Region',
    +    'ur_IN': '\u0628\u06be\u0627\u0631\u062a',
    +    'ur_PK': '\u067e\u0627\u06a9\u0633\u062a\u0627\u0646',
    +    'uz_AF': 'Afganistan',
    +    'uz_Arab_AF': '\u0627\u0641\u063a\u0627\u0646\u0633\u062a' +
    +        '\u0627\u0646',
    +    'uz_Cyrl_UZ': '\u0423\u0437\u0431\u0435\u043a\u0438\u0441' +
    +        '\u0442\u0430\u043d',
    +    'uz_Latn_UZ': 'O\u02bfzbekiston',
    +    'uz_UZ': '\u040e\u0437\u0431\u0435\u043a\u0438\u0441\u0442' +
    +        '\u043e\u043d',
    +    've_ZA': 'South Africa',
    +    'vi_VN': 'Vi\u1ec7t Nam',
    +    'wal_ET': '\u12a2\u1275\u12ee\u1335\u12eb',
    +    'wo_Arab_SN': '\u0627\u0644\u0633\u0646\u063a\u0627\u0644',
    +    'wo_Latn_SN': 'S\u00e9n\u00e9gal',
    +    'wo_SN': 'S\u00e9n\u00e9gal',
    +    'xh_ZA': 'South Africa',
    +    'yap_FM': 'Micronesia',
    +    'yo_NG': 'Nigeria',
    +    'zh_CN': '\u4e2d\u56fd',
    +    'zh_HK': '\u9999\u6e2f',
    +    'zh_Hans_CN': '\u4e2d\u56fd',
    +    'zh_Hans_SG': '\u65b0\u52a0\u5761',
    +    'zh_Hant_HK': '\u4e2d\u83ef\u4eba\u6c11\u5171\u548c\u570b' +
    +        '\u9999\u6e2f\u7279\u5225\u884c\u653f\u5340',
    +    'zh_Hant_MO': '\u6fb3\u9580',
    +    'zh_Hant_TW': '\u81fa\u7063',
    +    'zh_MO': '\u6fb3\u95e8',
    +    'zh_SG': '\u65b0\u52a0\u5761',
    +    'zh_TW': '\u53f0\u6e7e',
    +    'zu_ZA': 'South Africa'
    +  },
    +  'LANGUAGE': {
    +    'aa': 'afar',
    +    'ab': '\u0430\u0431\u0445\u0430\u0437\u0441\u043a\u0438\u0439',
    +    'ace': 'Aceh',
    +    'ach': 'Acoli',
    +    'ada': 'Adangme',
    +    'ady': '\u0430\u0434\u044b\u0433\u0435\u0439\u0441\u043a' +
    +        '\u0438\u0439',
    +    'ae': 'Avestan',
    +    'af': 'Afrikaans',
    +    'afa': 'Afro-Asiatic Language',
    +    'afh': 'Afrihili',
    +    'ain': 'Ainu',
    +    'ak': 'Akan',
    +    'akk': 'Akkadian',
    +    'ale': 'Aleut',
    +    'alg': 'Algonquian Language',
    +    'alt': 'Southern Altai',
    +    'am': '\u12a0\u121b\u122d\u129b',
    +    'an': 'Aragonese',
    +    'ang': 'Old English',
    +    'anp': 'Angika',
    +    'apa': 'Apache Language',
    +    'ar': '\u0627\u0644\u0639\u0631\u0628\u064a\u0629',
    +    'arc': 'Aramaic',
    +    'arn': 'Araucanian',
    +    'arp': 'Arapaho',
    +    'art': 'Artificial Language',
    +    'arw': 'Arawak',
    +    'as': '\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be',
    +    'ast': 'asturiano',
    +    'ath': 'Athapascan Language',
    +    'aus': 'Australian Language',
    +    'av': '\u0430\u0432\u0430\u0440\u0441\u043a\u0438\u0439',
    +    'awa': 'Awadhi',
    +    'ay': 'aimara',
    +    'az': 'az\u0259rbaycanca',
    +    'az_Arab': '\u062a\u0631\u06a9\u06cc \u0622\u0630\u0631\u0628' +
    +        '\u0627\u06cc\u062c\u0627\u0646\u06cc',
    +    'az_Cyrl': '\u0410\u0437\u04d9\u0440\u0431\u0430\u0458\u04b9' +
    +        '\u0430\u043d',
    +    'az_Latn': 'Azerice',
    +    'ba': '\u0431\u0430\u0448\u043a\u0438\u0440\u0441\u043a\u0438' +
    +        '\u0439',
    +    'bad': 'Banda',
    +    'bai': 'Bamileke Language',
    +    'bal': '\u0628\u0644\u0648\u0686\u06cc',
    +    'ban': 'Balin',
    +    'bas': 'Basa',
    +    'bat': 'Baltic Language',
    +    'be': '\u0431\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430' +
    +        '\u044f',
    +    'bej': 'Beja',
    +    'bem': 'Bemba',
    +    'ber': 'Berber',
    +    'bg': '\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438',
    +    'bh': '\u092c\u093f\u0939\u093e\u0930\u0940',
    +    'bho': 'Bhojpuri',
    +    'bi': 'bichelamar ; bislama',
    +    'bik': 'Bikol',
    +    'bin': 'Bini',
    +    'bla': 'Siksika',
    +    'bm': 'bambara',
    +    'bn': '\u09ac\u09be\u0982\u09b2\u09be',
    +    'bnt': 'Bantu',
    +    'bo': '\u0f54\u0f7c\u0f51\u0f0b\u0f66\u0f90\u0f51\u0f0b',
    +    'br': 'breton',
    +    'bra': 'Braj',
    +    'bs': 'Bosanski',
    +    'btk': 'Batak',
    +    'bua': 'Buriat',
    +    'bug': 'Bugis',
    +    'byn': '\u1265\u120a\u1295',
    +    'ca': 'catal\u00e0',
    +    'cad': 'Caddo',
    +    'cai': 'Central American Indian Language',
    +    'car': 'Carib',
    +    'cau': 'Caucasian Language',
    +    'cch': 'Atsam',
    +    'ce': '\u0447\u0435\u0447\u0435\u043d\u0441\u043a\u0438\u0439',
    +    'ceb': 'Cebuano',
    +    'cel': 'Celtic Language',
    +    'ch': 'Chamorro',
    +    'chb': 'Chibcha',
    +    'chg': 'Chagatai',
    +    'chk': 'Chuukese',
    +    'chm': '\u043c\u0430\u0440\u0438\u0439\u0441\u043a\u0438' +
    +        '\u0439 (\u0447\u0435\u0440\u0435\u043c\u0438\u0441' +
    +        '\u0441\u043a\u0438\u0439)',
    +    'chn': 'Chinook Jargon',
    +    'cho': 'Choctaw',
    +    'chp': 'Chipewyan',
    +    'chr': 'Cherokee',
    +    'chy': 'Cheyenne',
    +    'cmc': 'Chamic Language',
    +    'co': 'corse',
    +    'cop': '\u0642\u0628\u0637\u064a\u0629',
    +    'cop_Arab': '\u0642\u0628\u0637\u064a\u0629',
    +    'cpe': 'English-based Creole or Pidgin',
    +    'cpf': 'French-based Creole or Pidgin',
    +    'cpp': 'Portuguese-based Creole or Pidgin',
    +    'cr': 'Cree',
    +    'crh': 'Crimean Turkish',
    +    'crp': 'Creole or Pidgin',
    +    'cs': '\u010de\u0161tina',
    +    'csb': 'Kashubian',
    +    'cu': 'Church Slavic',
    +    'cus': 'Cushitic Language',
    +    'cv': '\u0447\u0443\u0432\u0430\u0448\u0441\u043a\u0438\u0439',
    +    'cy': 'Cymraeg',
    +    'da': 'dansk',
    +    'dak': 'Dakota',
    +    'dar': '\u0434\u0430\u0440\u0433\u0432\u0430',
    +    'day': 'Dayak',
    +    'de': 'Deutsch',
    +    'del': 'Delaware',
    +    'den': 'Slave',
    +    'dgr': 'Dogrib',
    +    'din': 'Dinka',
    +    'doi': '\u0627\u0644\u062f\u0648\u062c\u0631\u0649',
    +    'dra': 'Dravidian Language',
    +    'dsb': 'Lower Sorbian',
    +    'dua': 'Duala',
    +    'dum': 'Middle Dutch',
    +    'dv': 'Divehi',
    +    'dyu': 'dioula',
    +    'dz': '\u0f62\u0fab\u0f7c\u0f44\u0f0b\u0f41',
    +    'ee': 'Ewe',
    +    'efi': 'Efik',
    +    'egy': 'Ancient Egyptian',
    +    'eka': 'Ekajuk',
    +    'el': '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
    +    'elx': 'Elamite',
    +    'en': 'English',
    +    'enm': 'Middle English',
    +    'eo': 'esperanto',
    +    'es': 'espa\u00f1ol',
    +    'et': 'eesti',
    +    'eu': 'euskara',
    +    'ewo': 'Ewondo',
    +    'fa': '\u0641\u0627\u0631\u0633\u06cc',
    +    'fan': 'fang',
    +    'fat': 'Fanti',
    +    'ff': 'Fulah',
    +    'fi': 'suomi',
    +    'fil': 'Filipino',
    +    'fiu': 'Finno-Ugrian Language',
    +    'fj': 'Fijian',
    +    'fo': 'f\u00f8royskt',
    +    'fon': 'Fon',
    +    'fr': 'fran\u00e7ais',
    +    'frm': 'Middle French',
    +    'fro': 'Old French',
    +    'frr': 'Northern Frisian',
    +    'frs': 'Eastern Frisian',
    +    'fur': 'friulano',
    +    'fy': 'Fries',
    +    'ga': 'Gaeilge',
    +    'gaa': 'Ga',
    +    'gay': 'Gayo',
    +    'gba': 'Gbaya',
    +    'gd': 'Scottish Gaelic',
    +    'gem': 'Germanic Language',
    +    'gez': '\u130d\u12d5\u12dd\u129b',
    +    'gil': 'Gilbertese',
    +    'gl': 'galego',
    +    'gmh': 'Middle High German',
    +    'gn': 'guaran\u00ed',
    +    'goh': 'Old High German',
    +    'gon': 'Gondi',
    +    'gor': 'Gorontalo',
    +    'got': 'Gothic',
    +    'grb': 'Grebo',
    +    'grc': '\u0391\u03c1\u03c7\u03b1\u03af\u03b1 \u0395\u03bb' +
    +        '\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
    +    'gsw': 'Schweizerdeutsch',
    +    'gu': '\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0',
    +    'gv': 'Gaelg',
    +    'gwi': 'Gwich\u02bcin',
    +    'ha': '\u0627\u0644\u0647\u0648\u0633\u0627',
    +    'ha_Arab': '\u0627\u0644\u0647\u0648\u0633\u0627',
    +    'ha_Latn': 'haoussa',
    +    'hai': 'Haida',
    +    'haw': '\u02bb\u014dlelo Hawai\u02bbi',
    +    'he': '\u05e2\u05d1\u05e8\u05d9\u05ea',
    +    'hi': '\u0939\u093f\u0902\u0926\u0940',
    +    'hil': 'Hiligaynon',
    +    'him': 'Himachali',
    +    'hit': 'Hittite',
    +    'hmn': 'Hmong',
    +    'ho': 'Hiri Motu',
    +    'hr': 'hrvatski',
    +    'hsb': 'Upper Sorbian',
    +    'ht': 'ha\u00eftien',
    +    'hu': 'magyar',
    +    'hup': 'Hupa',
    +    'hy': '\u0540\u0561\u0575\u0565\u0580\u0567\u0576',
    +    'hz': 'Herero',
    +    'ia': 'interlingvao',
    +    'iba': 'Iban',
    +    'id': 'Bahasa Indonesia',
    +    'ie': 'Interlingue',
    +    'ig': 'Igbo',
    +    'ii': '\ua188\ua320\ua259',
    +    'ijo': 'Ijo',
    +    'ik': 'Inupiaq',
    +    'ilo': 'Iloko',
    +    'inc': 'Indic Language',
    +    'ine': 'Indo-European Language',
    +    'inh': '\u0438\u043d\u0433\u0443\u0448\u0441\u043a\u0438' +
    +        '\u0439',
    +    'io': 'Ido',
    +    'ira': 'Iranian Language',
    +    'iro': 'Iroquoian Language',
    +    'is': '\u00edslenska',
    +    'it': 'italiano',
    +    'iu': 'Inuktitut',
    +    'ja': '\u65e5\u672c\u8a9e',
    +    'jbo': 'Lojban',
    +    'jpr': 'Judeo-Persian',
    +    'jrb': 'Judeo-Arabic',
    +    'jv': 'Jawa',
    +    'ka': '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
    +    'kaa': '\u043a\u0430\u0440\u0430\u043a\u0430\u043b\u043f' +
    +        '\u0430\u043a\u0441\u043a\u0438\u0439',
    +    'kab': 'kabyle',
    +    'kac': 'Kachin',
    +    'kaj': 'Jju',
    +    'kam': 'Kamba',
    +    'kar': 'Karen',
    +    'kaw': 'Kawi',
    +    'kbd': '\u043a\u0430\u0431\u0430\u0440\u0434\u0438\u043d' +
    +        '\u0441\u043a\u0438\u0439',
    +    'kcg': 'Tyap',
    +    'kfo': 'koro',
    +    'kg': 'Kongo',
    +    'kha': 'Khasi',
    +    'khi': 'Khoisan Language',
    +    'kho': 'Khotanese',
    +    'ki': 'Kikuyu',
    +    'kj': 'Kuanyama',
    +    'kk': '\u049a\u0430\u0437\u0430\u049b',
    +    'kl': 'kalaallisut',
    +    'km': '\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a',
    +    'kmb': 'quimbundo',
    +    'kn': '\u0c95\u0ca8\u0ccd\u0ca8\u0ca1',
    +    'ko': '\ud55c\uad6d\uc5b4',
    +    'kok': '\u0915\u094b\u0902\u0915\u0923\u0940',
    +    'kos': 'Kosraean',
    +    'kpe': 'kpell\u00e9',
    +    'kr': 'Kanuri',
    +    'krc': '\u043a\u0430\u0440\u0430\u0447\u0430\u0435\u0432' +
    +        '\u043e-\u0431\u0430\u043b\u043a\u0430\u0440\u0441' +
    +        '\u043a\u0438\u0439',
    +    'krl': '\u043a\u0430\u0440\u0435\u043b\u044c\u0441\u043a' +
    +        '\u0438\u0439',
    +    'kro': 'Kru',
    +    'kru': 'Kurukh',
    +    'ks': '\u0915\u093e\u0936\u094d\u092e\u093f\u0930\u0940',
    +    'ku': 'K\u00fcrt\u00e7e',
    +    'ku_Arab': '\u0627\u0644\u0643\u0631\u062f\u064a\u0629',
    +    'ku_Latn': 'K\u00fcrt\u00e7e',
    +    'kum': '\u043a\u0443\u043c\u044b\u043a\u0441\u043a\u0438' +
    +        '\u0439',
    +    'kut': 'Kutenai',
    +    'kv': 'Komi',
    +    'kw': 'kernewek',
    +    'ky': 'K\u0131rg\u0131zca',
    +    'ky_Arab': '\u0627\u0644\u0642\u064a\u0631\u063a\u0633\u062a' +
    +        '\u0627\u0646\u064a\u0629',
    +    'ky_Cyrl': '\u043a\u0438\u0440\u0433\u0438\u0437\u0441\u043a' +
    +        '\u0438\u0439',
    +    'la': 'latino',
    +    'lad': '\u05dc\u05d3\u05d9\u05e0\u05d5',
    +    'lah': '\u0644\u0627\u0647\u0646\u062f\u0627',
    +    'lam': 'Lamba',
    +    'lb': 'luxembourgeois',
    +    'lez': '\u043b\u0435\u0437\u0433\u0438\u043d\u0441\u043a' +
    +        '\u0438\u0439',
    +    'lg': 'Ganda',
    +    'li': 'Limburgs',
    +    'ln': 'lingala',
    +    'lo': 'Lao',
    +    'lol': 'mongo',
    +    'loz': 'Lozi',
    +    'lt': 'lietuvi\u0173',
    +    'lu': 'luba-katanga',
    +    'lua': 'luba-lulua',
    +    'lui': 'Luiseno',
    +    'lun': 'Lunda',
    +    'luo': 'Luo',
    +    'lus': 'Lushai',
    +    'lv': 'latvie\u0161u',
    +    'mad': 'Madura',
    +    'mag': 'Magahi',
    +    'mai': 'Maithili',
    +    'mak': 'Makassar',
    +    'man': 'Mandingo',
    +    'map': 'Austronesian',
    +    'mas': 'Masai',
    +    'mdf': '\u043c\u043e\u043a\u0448\u0430',
    +    'mdr': 'Mandar',
    +    'men': 'Mende',
    +    'mg': 'malgache',
    +    'mga': 'Middle Irish',
    +    'mh': 'Marshallese',
    +    'mi': 'Maori',
    +    'mic': 'Micmac',
    +    'min': 'Minangkabau',
    +    'mis': 'Miscellaneous Language',
    +    'mk': '\u043c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a' +
    +        '\u0438',
    +    'mkh': 'Mon-Khmer Language',
    +    'ml': '\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02',
    +    'mn': '\u043c\u043e\u043d\u0433\u043e\u043b\u044c\u0441\u043a' +
    +        '\u0438\u0439',
    +    'mn_Cyrl': '\u043c\u043e\u043d\u0433\u043e\u043b\u044c\u0441' +
    +        '\u043a\u0438\u0439',
    +    'mn_Mong': '\u043c\u043e\u043d\u0433\u043e\u043b\u044c\u0441' +
    +        '\u043a\u0438\u0439',
    +    'mnc': 'Manchu',
    +    'mni': 'Manipuri',
    +    'mno': 'Manobo Language',
    +    'mo': 'Moldavian',
    +    'moh': 'Mohawk',
    +    'mos': 'mor\u00e9 ; mossi',
    +    'mr': '\u092e\u0930\u093e\u0920\u0940',
    +    'ms': 'Bahasa Melayu',
    +    'mt': 'Malti',
    +    'mul': 'Multiple Languages',
    +    'mun': 'Munda Language',
    +    'mus': 'Creek',
    +    'mwl': 'Mirandese',
    +    'mwr': 'Marwari',
    +    'my': 'Burmese',
    +    'myn': 'Mayan Language',
    +    'myv': '\u044d\u0440\u0437\u044f',
    +    'na': 'Nauru',
    +    'nah': 'Nahuatl',
    +    'nai': 'North American Indian Language',
    +    'nap': 'napoletano',
    +    'nb': 'norsk bokm\u00e5l',
    +    'nd': 'North Ndebele',
    +    'nds': 'Low German',
    +    'ne': '\u0928\u0947\u092a\u093e\u0932\u0940',
    +    'new': 'Newari',
    +    'ng': 'Ndonga',
    +    'nia': 'Nias',
    +    'nic': 'Niger-Kordofanian Language',
    +    'niu': 'Niuean',
    +    'nl': 'Nederlands',
    +    'nn': 'nynorsk',
    +    'no': 'Norwegian',
    +    'nog': '\u043d\u043e\u0433\u0430\u0439\u0441\u043a\u0438' +
    +        '\u0439',
    +    'non': 'Old Norse',
    +    'nqo': 'N\u2019Ko',
    +    'nr': 'South Ndebele',
    +    'nso': 'Northern Sotho',
    +    'nub': 'Nubian Language',
    +    'nv': 'Navajo',
    +    'nwc': 'Classical Newari',
    +    'ny': 'nianja; chicheua; cheua',
    +    'nym': 'Nyamwezi',
    +    'nyn': 'Nyankole',
    +    'nyo': 'Nyoro',
    +    'nzi': 'Nzima',
    +    'oc': 'occitan',
    +    'oj': 'Ojibwa',
    +    'om': 'Oromoo',
    +    'or': '\u0b13\u0b21\u0b3c\u0b3f\u0b06',
    +    'os': '\u043e\u0441\u0435\u0442\u0438\u043d\u0441\u043a\u0438' +
    +        '\u0439',
    +    'osa': 'Osage',
    +    'ota': 'Ottoman Turkish',
    +    'oto': 'Otomian Language',
    +    'pa': '\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40',
    +    'pa_Arab': '\u067e\u0646\u062c\u0627\u0628',
    +    'pa_Guru': '\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40',
    +    'paa': 'Papuan Language',
    +    'pag': 'Pangasinan',
    +    'pal': 'Pahlavi',
    +    'pam': 'Pampanga',
    +    'pap': 'Papiamento',
    +    'pau': 'Palauan',
    +    'peo': 'Old Persian',
    +    'phi': 'Philippine Language',
    +    'phn': 'Phoenician',
    +    'pi': '\u0e1a\u0e32\u0e25\u0e35',
    +    'pl': 'polski',
    +    'pon': 'Pohnpeian',
    +    'pra': 'Prakrit Language',
    +    'pro': 'Old Proven\u00e7al',
    +    'ps': '\u067e\u069a\u062a\u0648',
    +    'pt': 'portugu\u00eas',
    +    'qu': 'quechua',
    +    'raj': 'Rajasthani',
    +    'rap': 'Rapanui',
    +    'rar': 'Rarotongan',
    +    'rm': 'R\u00e4toromanisch',
    +    'rn': 'roundi',
    +    'ro': 'rom\u00e2n\u0103',
    +    'roa': 'Romance Language',
    +    'rom': 'Romany',
    +    'ru': '\u0440\u0443\u0441\u0441\u043a\u0438\u0439',
    +    'rup': 'Aromanian',
    +    'rw': 'rwanda',
    +    'sa': '\u0938\u0902\u0938\u094d\u0915\u0943\u0924 \u092d' +
    +        '\u093e\u0937\u093e',
    +    'sad': 'Sandawe',
    +    'sah': '\u044f\u043a\u0443\u0442\u0441\u043a\u0438\u0439',
    +    'sai': 'South American Indian Language',
    +    'sal': 'Salishan Language',
    +    'sam': '\u05d0\u05e8\u05de\u05d9\u05ea \u05e9\u05d5\u05de' +
    +        '\u05e8\u05d5\u05e0\u05d9\u05ea',
    +    'sas': 'Sasak',
    +    'sat': 'Santali',
    +    'sc': 'Sardinian',
    +    'scn': 'siciliano',
    +    'sco': 'Scots',
    +    'sd': '\u0938\u093f\u0928\u094d\u0927\u0940',
    +    'sd_Arab': '\u0633\u0646\u062f\u06cc',
    +    'sd_Deva': '\u0938\u093f\u0928\u094d\u0927\u0940',
    +    'se': 'nordsamiska',
    +    'sel': '\u0441\u0435\u043b\u044c\u043a\u0443\u043f\u0441' +
    +        '\u043a\u0438\u0439',
    +    'sem': 'Semitic Language',
    +    'sg': 'sangho',
    +    'sga': 'Old Irish',
    +    'sgn': 'Sign Language',
    +    'sh': 'Serbo-Croatian',
    +    'shn': 'Shan',
    +    'si': 'Sinhalese',
    +    'sid': 'Sidamo',
    +    'sio': 'Siouan Language',
    +    'sit': 'Sino-Tibetan Language',
    +    'sk': 'slovensk\u00fd',
    +    'sl': 'sloven\u0161\u010dina',
    +    'sla': 'Slavic Language',
    +    'sm': 'Samoan',
    +    'sma': 'sydsamiska',
    +    'smi': 'Sami Language',
    +    'smj': 'lulesamiska',
    +    'smn': 'Inari Sami',
    +    'sms': 'Skolt Sami',
    +    'sn': 'Shona',
    +    'snk': 'sonink\u00e9',
    +    'so': 'Soomaali',
    +    'sog': 'Sogdien',
    +    'son': 'Songhai',
    +    'sq': 'shqipe',
    +    'sr': '\u0421\u0440\u043f\u0441\u043a\u0438',
    +    'sr_Cyrl': '\u0441\u0435\u0440\u0431\u0441\u043a\u0438\u0439',
    +    'sr_Latn': 'Srpski',
    +    'srn': 'Sranantongo',
    +    'srr': 's\u00e9r\u00e8re',
    +    'ss': 'Swati',
    +    'ssa': 'Nilo-Saharan Language',
    +    'st': 'Sesotho',
    +    'su': 'Sundan',
    +    'suk': 'Sukuma',
    +    'sus': 'soussou',
    +    'sux': 'Sumerian',
    +    'sv': 'svenska',
    +    'sw': 'Kiswahili',
    +    'syc': 'Classical Syriac',
    +    'syr': 'Syriac',
    +    'ta': '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
    +    'tai': 'Tai Language',
    +    'te': '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
    +    'tem': 'Timne',
    +    'ter': 'Tereno',
    +    'tet': 't\u00e9tum',
    +    'tg': '\u062a\u0627\u062c\u06a9',
    +    'tg_Arab': '\u062a\u0627\u062c\u06a9',
    +    'tg_Cyrl': '\u0442\u0430\u0434\u0436\u0438\u043a\u0441\u043a' +
    +        '\u0438\u0439',
    +    'th': '\u0e44\u0e17\u0e22',
    +    'ti': '\u1275\u130d\u122d\u129b',
    +    'tig': '\u1275\u130d\u1228',
    +    'tiv': 'Tiv',
    +    'tk': '\u062a\u0631\u06a9\u0645\u0646\u06cc',
    +    'tkl': 'Tokelau',
    +    'tl': 'Tagalog',
    +    'tlh': 'Klingon',
    +    'tli': 'Tlingit',
    +    'tmh': 'tamacheq',
    +    'tn': 'Tswana',
    +    'to': 'Tonga',
    +    'tog': 'Nyasa Tonga',
    +    'tpi': 'Tok Pisin',
    +    'tr': 'T\u00fcrk\u00e7e',
    +    'ts': 'Tsonga',
    +    'tsi': 'Tsimshian',
    +    'tt': '\u0442\u0430\u0442\u0430\u0440\u0441\u043a\u0438\u0439',
    +    'tum': 'Tumbuka',
    +    'tup': 'Tupi Language',
    +    'tut': '\u0430\u043b\u0442\u0430\u0439\u0441\u043a\u0438' +
    +        '\u0435 (\u0434\u0440\u0443\u0433\u0438\u0435)',
    +    'tvl': 'Tuvalu',
    +    'tw': 'Twi',
    +    'ty': 'tahitien',
    +    'tyv': '\u0442\u0443\u0432\u0438\u043d\u0441\u043a\u0438' +
    +        '\u0439',
    +    'udm': '\u0443\u0434\u043c\u0443\u0440\u0442\u0441\u043a' +
    +        '\u0438\u0439',
    +    'ug': '\u0443\u0439\u0433\u0443\u0440\u0441\u043a\u0438\u0439',
    +    'uga': 'Ugaritic',
    +    'uk': '\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a' +
    +        '\u0430',
    +    'umb': 'umbundu',
    +    'und': 'English',
    +    'ur': '\u0627\u0631\u062f\u0648',
    +    'uz': '\u040e\u0437\u0431\u0435\u043a',
    +    'uz_Arab': '\u0627\u06c9\u0632\u0628\u06d0\u06a9',
    +    'uz_Cyrl': '\u0443\u0437\u0431\u0435\u043a\u0441\u043a\u0438' +
    +        '\u0439',
    +    'uz_Latn': 'o\'zbekcha',
    +    'vai': 'Vai',
    +    've': 'Venda',
    +    'vi': 'Ti\u1ebfng Vi\u1ec7t',
    +    'vo': 'volapuko',
    +    'vot': 'Votic',
    +    'wa': 'Wallonisch',
    +    'wak': 'Wakashan Language',
    +    'wal': 'Walamo',
    +    'war': 'Waray',
    +    'was': 'Washo',
    +    'wen': 'Sorbian Language',
    +    'wo': 'wolof',
    +    'wo_Arab': '\u0627\u0644\u0648\u0644\u0648\u0641',
    +    'wo_Latn': 'wolof',
    +    'xal': '\u043a\u0430\u043b\u043c\u044b\u0446\u043a\u0438' +
    +        '\u0439',
    +    'xh': 'Xhosa',
    +    'yao': 'iao',
    +    'yap': 'Yapese',
    +    'yi': '\u05d9\u05d9\u05d3\u05d9\u05e9',
    +    'yo': 'Yoruba',
    +    'ypk': 'Yupik Language',
    +    'za': 'Zhuang',
    +    'zap': 'Zapotec',
    +    'zen': 'Zenaga',
    +    'zh': '\u4e2d\u6587',
    +    'zh_Hans': '\u4e2d\u6587',
    +    'zh_Hant': '\u4e2d\u6587',
    +    'znd': 'Zande',
    +    'zu': 'Zulu',
    +    'zun': 'Zuni',
    +    'zxx': 'No linguistic content',
    +    'zza': 'Zaza'
    +  }
    +};
    +/* ~!@# END #@!~ */
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/scriptToLanguages.js b/mapbox-gl/closure-library/closure/goog/locale/scriptToLanguages.js
    new file mode 100644
    index 0000000000..9be7304555
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/scriptToLanguages.js
    @@ -0,0 +1,482 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Script to Languages mapping. Typically, one script is used by
    + * many languages of the world. This map captures that information as a mapping
    + * from script to array of two letter or three letter language codes.
    + *
    + * This map is used by goog.locale.genericFontNames for listing
    + * font fallbacks for a font family for a locale. That file
    + * uses this map in conjunction with goog.locale.genericFontNamesData.
    + *
    + * Warning: this file is automatically generated from CLDR.
    + * Please contact i18n team or change the script and regenerate data.
    + * Code location: http://go/generate_genericfontnames.py
    + *
    + */
    +
    +
    +/**
    + * Namespace for Script to Languages mapping
    + */
    +goog.provide('goog.locale.scriptToLanguages');
    +
    +goog.require('goog.locale');
    +
    +/**
    + * The script code to list of language codes map.
    + * @type {!Object<string, !Array<string>>}
    + */
    +
    +/* ~!@# genmethods.scriptToLanguages() #@!~ */
    +goog.locale.scriptToLanguages = {
    +  'Arab': [
    +    'prd',
    +    'doi',
    +    'lah',
    +    'uz',
    +    'cjm',
    +    'swb',
    +    'az',
    +    'ps',
    +    'ur',
    +    'ks',
    +    'fa',
    +    'ar',
    +    'tk',
    +    'ku',
    +    'tg',
    +    'bal',
    +    'ha',
    +    'ky',
    +    'ug',
    +    'sd'
    +  ],
    +  'Armn': ['hy'],
    +  'Beng': [
    +    'mni',
    +    'grt',
    +    'bn',
    +    'syl',
    +    'as',
    +    'ril',
    +    'ccp'
    +  ],
    +  'Blis': ['zbl'],
    +  'Cans': [
    +    'cr',
    +    'iu',
    +    'cwd',
    +    'crk'
    +  ],
    +  'Cham': ['cja'],
    +  'Cher': ['chr'],
    +  'Cyrl': [
    +    'ab',
    +    'rom',
    +    'mns',
    +    'mdf',
    +    'ce',
    +    'myv',
    +    'ude',
    +    'sah',
    +    'inh',
    +    'uk',
    +    'tab',
    +    'av',
    +    'yrk',
    +    'az',
    +    'cv',
    +    'koi',
    +    'ru',
    +    'dng',
    +    'sel',
    +    'tt',
    +    'chm',
    +    'ady',
    +    'tyv',
    +    'abq',
    +    'kum',
    +    'xal',
    +    'tg',
    +    'cjs',
    +    'tk',
    +    'be',
    +    'kaa',
    +    'bg',
    +    'kca',
    +    'ba',
    +    'nog',
    +    'krl',
    +    'bxr',
    +    'kbd',
    +    'dar',
    +    'krc',
    +    'lez',
    +    'ttt',
    +    'udm',
    +    'evn',
    +    'kpv',
    +    'uz',
    +    'kk',
    +    'kpy',
    +    'kjh',
    +    'mn',
    +    'gld',
    +    'mk',
    +    'ckt',
    +    'aii',
    +    'kv',
    +    'ku',
    +    'sr',
    +    'lbe',
    +    'ky',
    +    'os'
    +  ],
    +  'Deva': [
    +    'btv',
    +    'kfr',
    +    'bho',
    +    'mr',
    +    'bhb',
    +    'bjj',
    +    'hi',
    +    'mag',
    +    'mai',
    +    'awa',
    +    'lif',
    +    'xsr',
    +    'mwr',
    +    'kok',
    +    'gon',
    +    'hne',
    +    'hoc',
    +    'gbm',
    +    'hoj',
    +    'ne',
    +    'kru',
    +    'ks',
    +    'bra',
    +    'bft',
    +    'new',
    +    'bfy',
    +    'sd'
    +  ],
    +  'Ethi': [
    +    'byn',
    +    'wal',
    +    'ti',
    +    'tig',
    +    'am'
    +  ],
    +  'Geor': ['ka'],
    +  'Grek': ['el'],
    +  'Gujr': ['gu'],
    +  'Guru': ['pa'],
    +  'Hans': [
    +    'zh',
    +    'za'
    +  ],
    +  'Hant': ['zh'],
    +  'Hebr': [
    +    'lad',
    +    'yi',
    +    'he'
    +  ],
    +  'Jpan': ['ja'],
    +  'Khmr': ['km'],
    +  'Knda': [
    +    'kn',
    +    'tcy'
    +  ],
    +  'Kore': ['ko'],
    +  'Laoo': ['lo'],
    +  'Latn': [
    +    'gv',
    +    'sco',
    +    'scn',
    +    'mfe',
    +    'hnn',
    +    'suk',
    +    'tkl',
    +    'gd',
    +    'ga',
    +    'gn',
    +    'gl',
    +    'rom',
    +    'hai',
    +    'lb',
    +    'la',
    +    'ln',
    +    'tsg',
    +    'tr',
    +    'ts',
    +    'li',
    +    'lv',
    +    'to',
    +    'lt',
    +    'lu',
    +    'tk',
    +    'tg',
    +    'fo',
    +    'fil',
    +    'bya',
    +    'bin',
    +    'kcg',
    +    'ceb',
    +    'amo',
    +    'yao',
    +    'mos',
    +    'dyu',
    +    'de',
    +    'tbw',
    +    'da',
    +    'fan',
    +    'st',
    +    'hil',
    +    'fon',
    +    'efi',
    +    'tl',
    +    'qu',
    +    'uz',
    +    'kpe',
    +    'ban',
    +    'bal',
    +    'gor',
    +    'tru',
    +    'mo',
    +    'mdh',
    +    'en',
    +    'tem',
    +    'ee',
    +    'tvl',
    +    'cr',
    +    'eu',
    +    'et',
    +    'tet',
    +    'nbf',
    +    'es',
    +    'rw',
    +    'lut',
    +    'kmb',
    +    'ast',
    +    'sms',
    +    'lua',
    +    'sus',
    +    'smj',
    +    'fy',
    +    'tmh',
    +    'rm',
    +    'rn',
    +    'ro',
    +    'dsb',
    +    'sma',
    +    'luo',
    +    'hsb',
    +    'wa',
    +    'lg',
    +    'wo',
    +    'bm',
    +    'jv',
    +    'men',
    +    'bi',
    +    'tum',
    +    'br',
    +    'bs',
    +    'smn',
    +    'om',
    +    'ace',
    +    'ilo',
    +    'ty',
    +    'oc',
    +    'srr',
    +    'krl',
    +    'tw',
    +    'nds',
    +    'os',
    +    'xh',
    +    'ch',
    +    'co',
    +    'nso',
    +    'ca',
    +    'sn',
    +    'eo',
    +    'son',
    +    'pon',
    +    'cy',
    +    'cs',
    +    'kfo',
    +    'fj',
    +    'tn',
    +    'srn',
    +    'pt',
    +    'sm',
    +    'chk',
    +    'bbc',
    +    'chm',
    +    'lol',
    +    'frs',
    +    'frr',
    +    'chr',
    +    'yap',
    +    'vi',
    +    'kos',
    +    'gil',
    +    'ak',
    +    'pl',
    +    'sid',
    +    'hr',
    +    'ht',
    +    'hu',
    +    'hmn',
    +    'ho',
    +    'gag',
    +    'buc',
    +    'ha',
    +    'bug',
    +    'gaa',
    +    'mg',
    +    'fur',
    +    'bem',
    +    'ibb',
    +    'mi',
    +    'mh',
    +    'war',
    +    'mt',
    +    'uli',
    +    'ms',
    +    'sr',
    +    'haw',
    +    'sq',
    +    'aa',
    +    've',
    +    'af',
    +    'gwi',
    +    'is',
    +    'it',
    +    'sv',
    +    'ii',
    +    'sas',
    +    'ik',
    +    'tpi',
    +    'zu',
    +    'ay',
    +    'kha',
    +    'az',
    +    'tzm',
    +    'id',
    +    'ig',
    +    'pap',
    +    'nl',
    +    'pau',
    +    'nn',
    +    'no',
    +    'na',
    +    'nb',
    +    'nd',
    +    'umb',
    +    'ng',
    +    'ny',
    +    'nap',
    +    'gcr',
    +    'nyn',
    +    'hop',
    +    'lis',
    +    'so',
    +    'nr',
    +    'pam',
    +    'nv',
    +    'kv',
    +    'kab',
    +    'fr',
    +    'nym',
    +    'kaj',
    +    'rcf',
    +    'yo',
    +    'snk',
    +    'kam',
    +    'dgr',
    +    'mad',
    +    'fi',
    +    'mak',
    +    'niu',
    +    'kg',
    +    'pag',
    +    'gsw',
    +    'ss',
    +    'kj',
    +    'ki',
    +    'min',
    +    'sw',
    +    'cpe',
    +    'su',
    +    'kl',
    +    'sk',
    +    'kr',
    +    'kw',
    +    'cch',
    +    'ku',
    +    'sl',
    +    'sg',
    +    'tiv',
    +    'se'
    +  ],
    +  'Lepc': ['lep'],
    +  'Limb': ['lif'],
    +  'Mlym': ['ml'],
    +  'Mong': [
    +    'mnc',
    +    'mn'
    +  ],
    +  'Mymr': [
    +    'my',
    +    'kht',
    +    'shn',
    +    'mnw'
    +  ],
    +  'Nkoo': [
    +    'nqo',
    +    'emk'
    +  ],
    +  'Orya': ['or'],
    +  'Sinh': ['si'],
    +  'Tale': ['tdd'],
    +  'Talu': ['khb'],
    +  'Taml': [
    +    'bfq',
    +    'ta'
    +  ],
    +  'Telu': [
    +    'te',
    +    'gon',
    +    'lmn'
    +  ],
    +  'Tfng': ['tzm'],
    +  'Thaa': ['dv'],
    +  'Thai': [
    +    'tts',
    +    'lwl',
    +    'th',
    +    'kdt',
    +    'lcp'
    +  ],
    +  'Tibt': [
    +    'bo',
    +    'dz'
    +  ],
    +  'Yiii': ['ii'],
    +  'und': ['sat']
    +};
    +/* ~!@# END #@!~ */
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonedetection.js b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection.js
    new file mode 100644
    index 0000000000..3e6f5d24d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection.js
    @@ -0,0 +1,116 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for detecting user's time zone.
    + * This work is based on Charlie Luo and Hong Yan's time zone detection work
    + * for CBG.
    + */
    +goog.provide('goog.locale.timeZoneDetection');
    +
    +goog.require('goog.locale.TimeZoneFingerprint');
    +
    +
    +/**
    + * Array of time instances for checking the time zone offset.
    + * @type {Array<number>}
    + * @private
    + */
    +goog.locale.timeZoneDetection.TZ_POKE_POINTS_ = [
    +  1109635200, 1128902400, 1130657000, 1143333000, 1143806400, 1145000000,
    +  1146380000, 1152489600, 1159800000, 1159500000, 1162095000, 1162075000,
    +  1162105500];
    +
    +
    +/**
    + * Calculates time zone fingerprint by poking time zone offsets for 13
    + * preselected time points.
    + * See {@link goog.locale.timeZoneDetection.TZ_POKE_POINTS_}
    + * @param {Date} date Date for calculating the fingerprint.
    + * @return {number} Fingerprint of user's time zone setting.
    + */
    +goog.locale.timeZoneDetection.getFingerprint = function(date) {
    +  var hash = 0;
    +  var stdOffset;
    +  var isComplex = false;
    +  for (var i = 0;
    +       i < goog.locale.timeZoneDetection.TZ_POKE_POINTS_.length; i++) {
    +    date.setTime(goog.locale.timeZoneDetection.TZ_POKE_POINTS_[i] * 1000);
    +    var offset = date.getTimezoneOffset() / 30 + 48;
    +    if (i == 0) {
    +      stdOffset = offset;
    +    } else if (stdOffset != offset) {
    +      isComplex = true;
    +    }
    +    hash = (hash << 2) ^ offset;
    +  }
    +  return isComplex ? hash : /** @type {number} */ (stdOffset);
    +};
    +
    +
    +/**
    + * Detects browser's time zone setting. If user's country is known, a better
    + * time zone choice could be guessed.
    + * @param {string=} opt_country Two-letter ISO 3166 country code.
    + * @param {Date=} opt_date Date for calculating the fingerprint. Defaults to the
    + *     current date.
    + * @return {string} Time zone ID of best guess.
    + */
    +goog.locale.timeZoneDetection.detectTimeZone = function(opt_country, opt_date) {
    +  var date = opt_date || new Date();
    +  var fingerprint = goog.locale.timeZoneDetection.getFingerprint(date);
    +  var timeZoneList = goog.locale.TimeZoneFingerprint[fingerprint];
    +  // Timezones in goog.locale.TimeZoneDetection.TimeZoneMap are in the format
    +  // US-America/Los_Angeles. Country code needs to be stripped before a
    +  // timezone is returned.
    +  if (timeZoneList) {
    +    if (opt_country) {
    +      for (var i = 0; i < timeZoneList.length; ++i) {
    +        if (timeZoneList[i].indexOf(opt_country) == 0) {
    +          return timeZoneList[i].substring(3);
    +        }
    +      }
    +    }
    +    return timeZoneList[0].substring(3);
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Returns an array of time zones that are consistent with user's platform
    + * setting. If user's country is given, only the time zone for that country is
    + * returned.
    + * @param {string=} opt_country 2 letter ISO 3166 country code. Helps in making
    + *     a better guess for user's time zone.
    + * @param {Date=} opt_date Date for retrieving timezone list. Defaults to the
    + *     current date.
    + * @return {!Array<string>} Array of time zone IDs.
    + */
    +goog.locale.timeZoneDetection.getTimeZoneList = function(opt_country,
    +    opt_date) {
    +  var date = opt_date || new Date();
    +  var fingerprint = goog.locale.timeZoneDetection.getFingerprint(date);
    +  var timeZoneList = goog.locale.TimeZoneFingerprint[fingerprint];
    +  if (!timeZoneList) {
    +    return [];
    +  }
    +  var chosenList = [];
    +  for (var i = 0; i < timeZoneList.length; i++) {
    +    if (!opt_country || timeZoneList[i].indexOf(opt_country) == 0) {
    +      chosenList.push(timeZoneList[i].substring(3));
    +    }
    +  }
    +  return chosenList;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.html b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.html
    new file mode 100644
    index 0000000000..0e132a69d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.locale.timeZoneDetection
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.locale.timeZoneDetectionTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.js b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.js
    new file mode 100644
    index 0000000000..93f661252b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonedetection_test.js
    @@ -0,0 +1,130 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +goog.provide('goog.locale.timeZoneDetectionTest');
    +goog.setTestOnly('goog.locale.timeZoneDetectionTest');
    +
    +goog.require('goog.locale.timeZoneDetection');
    +goog.require('goog.testing.jsunit');
    +
    +
    +
    +/**
    + * Mock date class with simplified properties of Date class for testing.
    + * @constructor
    + */
    +function MockDate() {
    +  /**
    +   * Time zone offset. For time zones with daylight saving, the different
    +   * offsets are represented as array of offsets.
    +   * @type {Array<number>}
    +   * @private
    +   */
    +  this.timezoneOffset_ = [];
    +  /**
    +   * Counter storing the index of next offset value to be returned from the
    +   * array of offset values.
    +   * @type {number}
    +   * @private
    +   */
    +  this.offsetArrayCounter_ = 0;
    +}
    +
    +
    +/**
    + * Does nothing because setting the time to calculate offset is not needed
    + * in the mock class.
    + * @param {number} ms Ignored.
    + */
    +MockDate.prototype.setTime = function(ms) {
    +  // Do nothing.
    +};
    +
    +
    +/**
    + * Sets the time zone offset.
    + * @param {Array<number>} offset Time zone offset.
    + */
    +MockDate.prototype.setTimezoneOffset = function(offset) {
    +  this.timezoneOffset_ = offset;
    +};
    +
    +
    +/**
    + * Returns consecutive offsets from array of time zone offsets on each call.
    + * @return {number} Time zone offset.
    + */
    +MockDate.prototype.getTimezoneOffset = function() {
    +  return this.timezoneOffset_.length > 1 ?
    +      this.timezoneOffset_[this.offsetArrayCounter_++] :
    +      this.timezoneOffset_[0];
    +};
    +
    +function testGetFingerprint() {
    +  var mockDate = new MockDate();
    +  mockDate.setTimezoneOffset([-480]);
    +  var fingerprint = goog.locale.timeZoneDetection.getFingerprint(mockDate);
    +  assertEquals(32, fingerprint);
    +
    +  mockDate = new MockDate();
    +  mockDate.setTimezoneOffset(
    +      [480, 420, 420, 480, 480, 420, 420, 420, 420, 420, 420, 420, 420]);
    +  fingerprint = goog.locale.timeZoneDetection.getFingerprint(mockDate);
    +  assertEquals(1294772902, fingerprint);
    +}
    +
    +function testDetectTimeZone() {
    +  var mockDate = new MockDate();
    +  mockDate.setTimezoneOffset([-480]);
    +  var timeZoneId =
    +      goog.locale.timeZoneDetection.detectTimeZone(undefined, mockDate);
    +  assertEquals('Asia/Hong_Kong', timeZoneId);
    +
    +  mockDate = new MockDate();
    +  mockDate.setTimezoneOffset(
    +      [480, 420, 420, 480, 480, 420, 420, 420, 420, 420, 420, 420, 420]);
    +  timeZoneId = goog.locale.timeZoneDetection.detectTimeZone('US', mockDate);
    +  assertEquals('America/Los_Angeles', timeZoneId);
    +
    +  mockDate = new MockDate();
    +  mockDate.setTimezoneOffset(
    +      [480, 420, 420, 480, 480, 420, 420, 420, 420, 420, 420, 420, 420]);
    +  timeZoneId = goog.locale.timeZoneDetection.detectTimeZone('CA', mockDate);
    +  assertEquals('America/Dawson', timeZoneId);
    +}
    +
    +function testGetTimeZoneList() {
    +  var mockDate = new MockDate();
    +  mockDate.setTimezoneOffset(
    +      [480, 420, 420, 480, 480, 420, 420, 420, 420, 420, 420, 420, 420]);
    +  var timeZoneList =
    +      goog.locale.timeZoneDetection.getTimeZoneList(undefined, mockDate);
    +  assertEquals('America/Los_Angeles', timeZoneList[0]);
    +  assertEquals('America/Whitehorse', timeZoneList[4]);
    +  assertEquals(5, timeZoneList.length);
    +
    +  mockDate = new MockDate();
    +  mockDate.setTimezoneOffset([-480]);
    +  timeZoneList =
    +      goog.locale.timeZoneDetection.getTimeZoneList(undefined, mockDate);
    +  assertEquals('Asia/Hong_Kong', timeZoneList[0]);
    +  assertEquals('Asia/Chongqing', timeZoneList[7]);
    +  assertEquals(16, timeZoneList.length);
    +
    +  timeZoneList =
    +      goog.locale.timeZoneDetection.getTimeZoneList('AU', mockDate);
    +  assertEquals(1, timeZoneList.length);
    +  assertEquals('Australia/Perth', timeZoneList[0]);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonefingerprint.js b/mapbox-gl/closure-library/closure/goog/locale/timezonefingerprint.js
    new file mode 100644
    index 0000000000..5317288006
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonefingerprint.js
    @@ -0,0 +1,248 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Data for time zone detection.
    + *
    + * The following code was generated by the timezone_detect.py script in:
    + * http://go/i18n_tools which uses following files in this directory:
    + * http://go/timezone_data
    + * Files: olson2fingerprint.txt, country2olsons.txt, popular_olsons.txt
    + *
    + * After automatic generation, we added some manual editing. Projecting on
    + * future changes, it is very unlikely that we will need to change the time
    + * zone ID groups. Most of the further modifications will be about relative
    + * time zone order in each time zone group. The easiest way to do that is
    + * to modify this code directly, and that's what we decide to do.
    + *
    + */
    +
    +
    +goog.provide('goog.locale.TimeZoneFingerprint');
    +
    +
    +/**
    + * Time zone fingerprint mapping to time zone list.
    + * @enum {!Array<string>}
    + */
    +goog.locale.TimeZoneFingerprint = {
    +  919994368: ['CA-America/Halifax', 'CA-America/Glace_Bay', 'GL-America/Thule',
    +    'BM-Atlantic/Bermuda'],
    +  6: ['AQ-Antarctica/Rothera'],
    +  8: ['GY-America/Guyana'],
    +  839516172: ['US-America/Denver', 'MX-America/Chihuahua', 'US-America/Boise',
    +    'CA-America/Cambridge_Bay', 'CA-America/Edmonton', 'CA-America/Inuvik',
    +    'MX-America/Mazatlan', 'US-America/Shiprock', 'CA-America/Yellowknife'],
    +  983564836: ['UY-America/Montevideo'],
    +  487587858: ['AU-Australia/Lord_Howe'],
    +  20: ['KI-Pacific/Kiritimati'],
    +  22: ['TO-Pacific/Tongatapu', 'KI-Pacific/Enderbury'],
    +  24: ['FJ-Pacific/Fiji', 'TV-Pacific/Funafuti', 'MH-Pacific/Kwajalein',
    +    'MH-Pacific/Majuro', 'NR-Pacific/Nauru', 'KI-Pacific/Tarawa',
    +    'UM-Pacific/Wake', 'WF-Pacific/Wallis'],
    +  25: ['NF-Pacific/Norfolk'],
    +  26: ['RU-Asia/Magadan', 'VU-Pacific/Efate', 'SB-Pacific/Guadalcanal',
    +    'FM-Pacific/Kosrae', 'NC-Pacific/Noumea', 'FM-Pacific/Ponape'],
    +  28: ['AQ-Antarctica/DumontDUrville', 'AU-Australia/Brisbane',
    +    'AU-Australia/Lindeman', 'GU-Pacific/Guam', 'PG-Pacific/Port_Moresby',
    +    'MP-Pacific/Saipan', 'FM-Pacific/Truk'],
    +  931091802: ['US-America/New_York', 'US-America/Detroit', 'CA-America/Iqaluit',
    +    'US-America/Kentucky/Monticello', 'US-America/Louisville',
    +    'CA-America/Montreal', 'BS-America/Nassau', 'CA-America/Nipigon',
    +    'CA-America/Pangnirtung', 'CA-America/Thunder_Bay', 'CA-America/Toronto'],
    +  30: ['JP-Asia/Tokyo', 'KR-Asia/Seoul', 'TL-Asia/Dili', 'ID-Asia/Jayapura',
    +    'KP-Asia/Pyongyang', 'PW-Pacific/Palau'],
    +  32: ['HK-Asia/Hong_Kong', 'CN-Asia/Shanghai', 'AU-Australia/Perth',
    +    'TW-Asia/Taipei', 'SG-Asia/Singapore', 'AQ-Antarctica/Casey',
    +    'BN-Asia/Brunei', 'CN-Asia/Chongqing', 'CN-Asia/Harbin',
    +    'CN-Asia/Kashgar', 'MY-Asia/Kuala_Lumpur', 'MY-Asia/Kuching',
    +    'MO-Asia/Macau', 'ID-Asia/Makassar', 'PH-Asia/Manila', 'CN-Asia/Urumqi'],
    +  34: ['TH-Asia/Bangkok', 'AQ-Antarctica/Davis', 'ID-Asia/Jakarta',
    +    'KH-Asia/Phnom_Penh', 'ID-Asia/Pontianak', 'VN-Asia/Saigon',
    +    'LA-Asia/Vientiane', 'CX-Indian/Christmas'],
    +  35: ['MM-Asia/Rangoon', 'CC-Indian/Cocos'],
    +  941621262: ['BR-America/Sao_Paulo'],
    +  37: ['IN-Asia/Calcutta'],
    +  38: ['PK-Asia/Karachi', 'KZ-Asia/Aqtobe', 'TM-Asia/Ashgabat',
    +    'TJ-Asia/Dushanbe', 'UZ-Asia/Samarkand', 'UZ-Asia/Tashkent',
    +    'TF-Indian/Kerguelen', 'MV-Indian/Maldives'],
    +  39: ['AF-Asia/Kabul'],
    +  40: ['OM-Asia/Muscat', 'AE-Asia/Dubai', 'SC-Indian/Mahe',
    +    'MU-Indian/Mauritius', 'RE-Indian/Reunion'],
    +  626175324: ['JO-Asia/Amman'],
    +  42: ['KE-Africa/Nairobi', 'SA-Asia/Riyadh', 'ET-Africa/Addis_Ababa',
    +    'ER-Africa/Asmera', 'TZ-Africa/Dar_es_Salaam', 'DJ-Africa/Djibouti',
    +    'UG-Africa/Kampala', 'SD-Africa/Khartoum', 'SO-Africa/Mogadishu',
    +    'AQ-Antarctica/Syowa', 'YE-Asia/Aden', 'BH-Asia/Bahrain',
    +    'KW-Asia/Kuwait', 'QA-Asia/Qatar', 'MG-Indian/Antananarivo',
    +    'KM-Indian/Comoro', 'YT-Indian/Mayotte'],
    +  44: ['ZA-Africa/Johannesburg', 'IL-Asia/Jerusalem', 'MW-Africa/Blantyre',
    +    'BI-Africa/Bujumbura', 'BW-Africa/Gaborone', 'ZW-Africa/Harare',
    +    'RW-Africa/Kigali', 'CD-Africa/Lubumbashi', 'ZM-Africa/Lusaka',
    +    'MZ-Africa/Maputo', 'LS-Africa/Maseru', 'SZ-Africa/Mbabane',
    +    'LY-Africa/Tripoli'],
    +  46: ['NG-Africa/Lagos', 'DZ-Africa/Algiers', 'CF-Africa/Bangui',
    +    'CG-Africa/Brazzaville', 'CM-Africa/Douala', 'CD-Africa/Kinshasa',
    +    'GA-Africa/Libreville', 'AO-Africa/Luanda', 'GQ-Africa/Malabo',
    +    'TD-Africa/Ndjamena', 'NE-Africa/Niamey', 'BJ-Africa/Porto-Novo'],
    +  48: ['MA-Africa/Casablanca', 'CI-Africa/Abidjan', 'GH-Africa/Accra',
    +    'ML-Africa/Bamako', 'GM-Africa/Banjul', 'GW-Africa/Bissau',
    +    'GN-Africa/Conakry', 'SN-Africa/Dakar', 'EH-Africa/El_Aaiun',
    +    'SL-Africa/Freetown', 'TG-Africa/Lome', 'LR-Africa/Monrovia',
    +    'MR-Africa/Nouakchott', 'BF-Africa/Ouagadougou', 'ST-Africa/Sao_Tome',
    +    'GL-America/Danmarkshavn', 'IS-Atlantic/Reykjavik',
    +    'SH-Atlantic/St_Helena'],
    +  570425352: ['GE-Asia/Tbilisi'],
    +  50: ['CV-Atlantic/Cape_Verde'],
    +  52: ['GS-Atlantic/South_Georgia', 'BR-America/Noronha'],
    +  54: ['AR-America/Buenos_Aires', 'BR-America/Araguaina',
    +    'AR-America/Argentina/La_Rioja', 'AR-America/Argentina/Rio_Gallegos',
    +    'AR-America/Argentina/San_Juan', 'AR-America/Argentina/Tucuman',
    +    'AR-America/Argentina/Ushuaia', 'BR-America/Bahia', 'BR-America/Belem',
    +    'AR-America/Catamarca', 'GF-America/Cayenne', 'AR-America/Cordoba',
    +    'BR-America/Fortaleza', 'AR-America/Jujuy', 'BR-America/Maceio',
    +    'AR-America/Mendoza', 'SR-America/Paramaribo', 'BR-America/Recife',
    +    'AQ-Antarctica/Rothera'],
    +  56: ['VE-America/Caracas', 'AI-America/Anguilla', 'AG-America/Antigua',
    +    'AW-America/Aruba', 'BB-America/Barbados', 'BR-America/Boa_Vista',
    +    'AN-America/Curacao', 'DM-America/Dominica', 'GD-America/Grenada',
    +    'GP-America/Guadeloupe', 'GY-America/Guyana', 'CU-America/Havana',
    +    'BO-America/La_Paz', 'BR-America/Manaus', 'MQ-America/Martinique',
    +    'MS-America/Montserrat', 'TT-America/Port_of_Spain',
    +    'BR-America/Porto_Velho', 'PR-America/Puerto_Rico',
    +    'DO-America/Santo_Domingo', 'KN-America/St_Kitts', 'LC-America/St_Lucia',
    +    'VI-America/St_Thomas', 'VC-America/St_Vincent', 'VG-America/Tortola'],
    +  58: ['US-America/Indianapolis', 'US-America/Indianapolis',
    +    'CO-America/Bogota', 'KY-America/Cayman', 'CA-America/Coral_Harbour',
    +    'BR-America/Eirunepe', 'EC-America/Guayaquil', 'US-America/Indiana/Knox',
    +    'JM-America/Jamaica', 'PE-America/Lima', 'PA-America/Panama',
    +    'BR-America/Rio_Branco'],
    +  60: ['NI-America/Managua', 'CA-America/Regina', 'BZ-America/Belize',
    +    'CR-America/Costa_Rica', 'SV-America/El_Salvador',
    +    'CA-America/Swift_Current', 'EC-Pacific/Galapagos'],
    +  62: ['US-America/Phoenix', 'CA-America/Dawson_Creek',
    +    'MX-America/Hermosillo'],
    +  64: ['PN-Pacific/Pitcairn'],
    +  66: ['PF-Pacific/Gambier'],
    +  67: ['PF-Pacific/Marquesas'],
    +  68: ['US-Pacific/Honolulu', 'TK-Pacific/Fakaofo', 'UM-Pacific/Johnston',
    +    'KI-Pacific/Kiritimati', 'CK-Pacific/Rarotonga', 'PF-Pacific/Tahiti'],
    +  70: ['UM-Pacific/Midway', 'WS-Pacific/Apia', 'KI-Pacific/Enderbury',
    +    'NU-Pacific/Niue', 'AS-Pacific/Pago_Pago'],
    +  72: ['MH-Pacific/Kwajalein'],
    +  49938444: ['MX-America/Chihuahua'],
    +  905969678: ['CA-America/Halifax'],
    +  626339164: ['EG-Africa/Cairo'],
    +  939579406: ['FK-Atlantic/Stanley'],
    +  487915538: ['AU-Australia/Lord_Howe'],
    +  937427058: ['CL-Pacific/Easter'],
    +  778043508: ['RU-Asia/Novosibirsk', 'RU-Asia/Omsk'],
    +  474655352: ['RU-Asia/Anadyr', 'RU-Asia/Kamchatka'],
    +  269133956: ['NZ-Pacific/Chatham'],
    +  948087430: ['GL-America/Godthab'],
    +  671787146: ['MN-Asia/Hovd'],
    +  617261764: ['TR-Europe/Istanbul', 'RU-Europe/Kaliningrad', 'BY-Europe/Minsk'],
    +  830603252: ['MX-America/Mexico_City', 'US-America/Chicago',
    +    'MX-America/Cancun', 'US-America/Menominee', 'MX-America/Merida',
    +    'MX-America/Monterrey', 'US-America/North_Dakota/Center',
    +    'CA-America/Rainy_River', 'CA-America/Rankin_Inlet'],
    +  805300897: ['LK-Asia/Colombo'],
    +  805312524: ['MX-America/Mexico_City', 'HN-America/Tegucigalpa'],
    +  984437412: ['GS-Atlantic/South_Georgia'],
    +  850043558: ['MX-America/Chihuahua'],
    +  29: ['AU-Australia/Darwin'],
    +  710950176: ['MN-Asia/Ulaanbaatar'],
    +  617786052: ['RO-Europe/Bucharest', 'FI-Europe/Helsinki', 'CY-Asia/Nicosia',
    +    'GR-Europe/Athens', 'MD-Europe/Chisinau', 'TR-Europe/Istanbul',
    +    'UA-Europe/Kiev', 'LV-Europe/Riga', 'UA-Europe/Simferopol',
    +    'BG-Europe/Sofia', 'EE-Europe/Tallinn', 'UA-Europe/Uzhgorod',
    +    'LT-Europe/Vilnius', 'UA-Europe/Zaporozhye'],
    +  105862464: ['US-America/Juneau'],
    +  581567010: ['IQ-Asia/Baghdad'],
    +  1294772902: ['US-America/Los_Angeles', 'CA-America/Dawson',
    +    'MX-America/Tijuana', 'CA-America/Vancouver', 'CA-America/Whitehorse'],
    +  483044050: ['AU-Australia/Sydney', 'AU-Australia/Melbourne'],
    +  491433170: ['AU-Australia/Hobart'],
    +  36: ['NP-Asia/Katmandu', 'LK-Asia/Colombo', 'BD-Asia/Dhaka',
    +    'AQ-Antarctica/Mawson', 'AQ-Antarctica/Vostok', 'KZ-Asia/Almaty',
    +    'KZ-Asia/Qyzylorda', 'BT-Asia/Thimphu', 'IO-Indian/Chagos'],
    +  626175196: ['IL-Asia/Jerusalem'],
    +  919994592: ['CA-America/Goose_Bay'],
    +  946339336: ['GB-Europe/London', 'ES-Atlantic/Canary', 'FO-Atlantic/Faeroe',
    +    'PT-Atlantic/Madeira', 'IE-Europe/Dublin', 'PT-Europe/Lisbon'],
    +  1037565906: ['PT-Atlantic/Azores', 'GL-America/Scoresbysund'],
    +  670913918: ['TN-Africa/Tunis'],
    +  41: ['IR-Asia/Tehran'],
    +  572522538: ['RU-Europe/Moscow'],
    +  403351686: ['MN-Asia/Choibalsan'],
    +  626338524: ['PS-Asia/Gaza'],
    +  411740806: ['RU-Asia/Yakutsk'],
    +  635437856: ['RU-Asia/Irkutsk'],
    +  617261788: ['RO-Europe/Bucharest', 'LB-Asia/Beirut'],
    +  947956358: ['GL-America/Godthab', 'PM-America/Miquelon'],
    +  12: ['EC-Pacific/Galapagos'],
    +  626306268: ['SY-Asia/Damascus'],
    +  497024903: ['AU-Australia/Adelaide', 'AU-Australia/Broken_Hill'],
    +  456480044: ['RU-Asia/Vladivostok', 'RU-Asia/Sakhalin'],
    +  312471854: ['NZ-Pacific/Auckland', 'AQ-Antarctica/McMurdo'],
    +  626347356: ['EG-Africa/Cairo'],
    +  897537370: ['CU-America/Havana'],
    +  680176266: ['RU-Asia/Krasnoyarsk'],
    +  1465210176: ['US-America/Anchorage'],
    +  805312908: ['NI-America/Managua'],
    +  492088530: ['AU-Australia/Currie', 'AU-Australia/Hobart'],
    +  901076366: ['BR-America/Campo_Grande', 'BR-America/Cuiaba'],
    +  943019406: ['CL-America/Santiago', 'AQ-Antarctica/Palmer'],
    +  928339288: ['US-America/New_York', 'CA-America/Montreal',
    +    'CA-America/Toronto', 'US-America/Detroit'],
    +  939480410: ['US-America/Indiana/Marengo', 'US-America/Indiana/Vevay'],
    +  626392412: ['NA-Africa/Windhoek'],
    +  559943005: ['IR-Asia/Tehran'],
    +  592794974: ['KZ-Asia/Aqtau', 'KZ-Asia/Oral'],
    +  76502378: ['CA-America/Pangnirtung'],
    +  838860812: ['US-America/Denver', 'CA-America/Edmonton'],
    +  931091834: ['TC-America/Grand_Turk', 'HT-America/Port-au-Prince'],
    +  662525310: ['FR-Europe/Paris', 'DE-Europe/Berlin', 'BA-Europe/Sarajevo',
    +    'CS-Europe/Belgrade', 'ES-Africa/Ceuta', 'NL-Europe/Amsterdam',
    +    'AD-Europe/Andorra', 'SK-Europe/Bratislava', 'BE-Europe/Brussels',
    +    'HU-Europe/Budapest', 'DK-Europe/Copenhagen', 'GI-Europe/Gibraltar',
    +    'SI-Europe/Ljubljana', 'LU-Europe/Luxembourg', 'ES-Europe/Madrid',
    +    'MT-Europe/Malta', 'MC-Europe/Monaco', 'NO-Europe/Oslo',
    +    'CZ-Europe/Prague', 'IT-Europe/Rome', 'MK-Europe/Skopje',
    +    'SE-Europe/Stockholm', 'AL-Europe/Tirane', 'LI-Europe/Vaduz',
    +    'AT-Europe/Vienna', 'PL-Europe/Warsaw', 'HR-Europe/Zagreb',
    +    'CH-Europe/Zurich'],
    +  1465865536: ['US-America/Anchorage', 'US-America/Juneau',
    +    'US-America/Nome', 'US-America/Yakutat'],
    +  495058823: ['AU-Australia/Adelaide', 'AU-Australia/Broken_Hill'],
    +  599086472: ['GE-Asia/Tbilisi', 'AM-Asia/Yerevan', 'RU-Europe/Samara'],
    +  805337484: ['GT-America/Guatemala'],
    +  1001739662: ['PY-America/Asuncion'],
    +  836894706: ['CA-America/Winnipeg'],
    +  599086512: ['AZ-Asia/Baku'],
    +  836894708: ['CA-America/Winnipeg'],
    +  41025476: ['US-America/Menominee'],
    +  501219282: ['RU-Asia/Magadan'],
    +  970325971: ['CA-America/St_Johns'],
    +  769654750: ['RU-Asia/Yekaterinburg'],
    +  1286253222: ['US-America/Los_Angeles', 'CA-America/Vancouver',
    +    'CA-America/Whitehorse'],
    +  1373765610: ['US-America/Adak'],
    +  973078513: ['CA-America/St_Johns'],
    +  838860786: ['US-America/Chicago', 'CA-America/Winnipeg'],
    +  970326003: ['CA-America/St_Johns'],
    +  771751924: ['KG-Asia/Bishkek'],
    +  952805774: ['AQ-Antarctica/Palmer'],
    +  483699410: ['AU-Australia/Sydney', 'AU-Australia/Melbourne']
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonelist.js b/mapbox-gl/closure-library/closure/goog/locale/timezonelist.js
    new file mode 100644
    index 0000000000..143ab1eaa1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonelist.js
    @@ -0,0 +1,131 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for listing timezone names.
    + * @suppress {deprecated} Use goog.i18n instead.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.locale.TimeZoneList');
    +
    +goog.require('goog.locale');
    +
    +
    +/**
    + * Returns the displayable list of short timezone names paired with its id for
    + * the current locale, selected based on the region or language provided.
    + *
    + * This method depends on goog.locale.TimeZone*__<locale> available
    + * from http://go/js_locale_data. Users of this method must add a dependency on
    + * this.
    + *
    + * @param {string=} opt_regionOrLang If region tag is provided, timezone ids
    + *    specific this region are considered. If language is provided, all regions
    + *    for which this language is defacto official is considered. If
    + *    this parameter is not speficied, current locale is used to
    + *    extract this information.
    + *
    + * @return {!Array<Object>} Localized and relevant list of timezone names
    + *    and ids.
    + */
    +goog.locale.getTimeZoneSelectedShortNames = function(opt_regionOrLang) {
    +  return goog.locale.getTimeZoneNameList_('TimeZoneSelectedShortNames',
    +      opt_regionOrLang);
    +};
    +
    +
    +/**
    + * Returns the displayable list of long timezone names paired with its id for
    + * the current locale, selected based on the region or language provided.
    + *
    + * This method depends on goog.locale.TimeZone*__<locale> available
    + * from http://go/js_locale_data. Users of this method must add a dependency on
    + * this.
    + *
    + * @param {string=} opt_regionOrLang If region tag is provided, timezone ids
    + *    specific this region are considered. If language is provided, all regions
    + *    for which this language is defacto official is considered. If
    + *    this parameter is not speficied, current locale is used to
    + *    extract this information.
    + *
    + * @return {!Array<Object>} Localized and relevant list of timezone names
    + *    and ids.
    + */
    +goog.locale.getTimeZoneSelectedLongNames = function(opt_regionOrLang) {
    +  return goog.locale.getTimeZoneNameList_('TimeZoneSelectedLongNames',
    +      opt_regionOrLang);
    +};
    +
    +
    +/**
    + * Returns the displayable list of long timezone names paired with its id for
    + * the current locale.
    + *
    + * This method depends on goog.locale.TimeZoneAllLongNames__<locale> available
    + * from http://go/js_locale_data. Users of this method must add a dependency on
    + * this.
    + *
    + * @return {Array<Object>} localized and relevant list of timezone names
    + *    and ids.
    + */
    +goog.locale.getTimeZoneAllLongNames = function() {
    +  var locale = goog.locale.getLocale();
    +  return /** @type {Array<Object>} */ (
    +      goog.locale.getResource('TimeZoneAllLongNames', locale));
    +};
    +
    +
    +/**
    + * Returns the displayable list of timezone names paired with its id for
    + * the current locale, selected based on the region or language provided.
    + *
    + * This method depends on goog.locale.TimeZone*__<locale> available
    + * from http://go/js_locale_data. Users of this method must add a dependency on
    + * this.
    + *
    + * @param {string} nameType Resource name to be loaded to get the names.
    + *
    + * @param {string=} opt_resource If resource is region tag, timezone ids
    + *    specific this region are considered. If it is language, all regions
    + *    for which this language is defacto official is considered. If it is
    + *    undefined, current locale is used to extract this information.
    + *
    + * @return {!Array<Object>} Localized and relevant list of timezone names
    + *    and ids.
    + * @private
    + */
    +goog.locale.getTimeZoneNameList_ = function(nameType, opt_resource) {
    +  var locale = goog.locale.getLocale();
    +
    +  if (!opt_resource) {
    +    opt_resource = goog.locale.getRegionSubTag(locale);
    +  }
    +  // if there is no region subtag, use the language itself as the resource
    +  if (!opt_resource) {
    +    opt_resource = locale;
    +  }
    +
    +  var names = goog.locale.getResource(nameType, locale);
    +  var ids = goog.locale.getResource('TimeZoneSelectedIds', opt_resource);
    +  var len = ids.length;
    +  var result = [];
    +
    +  for (var i = 0; i < len; i++) {
    +    var id = ids[i];
    +    result.push({'id': id, 'name': names[id]});
    +  }
    +  return result;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.html b/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.html
    new file mode 100644
    index 0000000000..2f609190a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.locale.TimeZoneConstants
    +  </title>
    +  <!-- UTF-8 needed for character encoding -->
    +  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.locale.TimeZoneListTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.js b/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.js
    new file mode 100644
    index 0000000000..e182a04587
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/locale/timezonelist_test.js
    @@ -0,0 +1,158 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.locale.TimeZoneListTest');
    +goog.setTestOnly('goog.locale.TimeZoneListTest');
    +
    +goog.require('goog.locale');
    +/** @suppress {extraRequire} */
    +goog.require('goog.locale.TimeZoneList');
    +goog.require('goog.testing.jsunit');
    +
    +function setUpPage() {
    +  // Test data files are in in http://go/js_locale_data
    +
    +  // Test data from TimeZoneSelectedIds__FR.js
    +  var TimeZoneSelectedIds__FR = [
    +    'Etc/GMT+12',
    +    'Pacific/Midway',
    +    'America/Adak',
    +    'Pacific/Honolulu'
    +  ];
    +  goog.locale.registerTimeZoneSelectedIds(TimeZoneSelectedIds__FR, 'FR');
    +
    +  // Test data from TimeZoneSelectedShortNames__de_DE.js
    +  var TimeZoneSelectedShortNames__de_DE = {
    +    'Etc/GMT+12': 'GMT-12:00',
    +    'Etc/GMT+11': 'GMT-11:00',
    +    'Pacific/Pago_Pago': 'Amerikanisch-Samoa',
    +    'Pacific/Midway': 'Midway (Amerikanisch-Ozeanien)',
    +    'Pacific/Honolulu': 'Honolulu (Vereinigte Staaten)',
    +    'Etc/GMT+10': 'GMT-10:00',
    +    'America/Adak': 'Adak (Vereinigte Staaten)'
    +  };
    +  goog.locale.registerTimeZoneSelectedShortNames(
    +      TimeZoneSelectedShortNames__de_DE, 'de_DE');
    +
    +  // Test data from TimeZoneSelectedLongNames__de_DE.js
    +  var TimeZoneSelectedLongNames__de_DE = {
    +    'Etc/GMT+12': 'GMT-12:00',
    +    'Etc/GMT+11': 'GMT-11:00',
    +    'Pacific/Pago_Pago': 'GMT-11:00 Amerikanisch-Samoa',
    +    'Pacific/Midway': 'GMT-11:00 Midway (Amerikanisch-Ozeanien)',
    +    'Pacific/Honolulu': 'GMT-10:00 Honolulu (Vereinigte Staaten)',
    +    'Etc/GMT+10': 'GMT-10:00',
    +    'America/Adak': 'GMT-10:00 Adak (Vereinigte Staaten)'
    +  };
    +  goog.locale.registerTimeZoneSelectedLongNames(
    +      TimeZoneSelectedLongNames__de_DE, 'de_DE');
    +
    +  // Test data from TimeZoneSelectedIds__en.js
    +  var TimeZoneSelectedIds__en = [
    +    'Etc/GMT+12',
    +    'Pacific/Midway',
    +    'America/Adak',
    +    'Pacific/Honolulu'
    +  ];
    +  goog.locale.registerTimeZoneSelectedIds(TimeZoneSelectedIds__en, 'en');
    +
    +  // Test data from TimeZoneSelectedIds__DE.js
    +  var TimeZoneSelectedIds__DE = [
    +    'Etc/GMT+12',
    +    'Pacific/Midway',
    +    'America/Adak',
    +    'Pacific/Honolulu'
    +  ];
    +  goog.locale.registerTimeZoneSelectedIds(TimeZoneSelectedIds__DE, 'DE');
    +
    +  // Test data from TimeZoneAllLongNames__de_DE.js
    +  var TimeZoneAllLongNames__de_DE = [
    +    {id: 'Etc/GMT+12', name: 'GMT-12:00'},
    +    {id: 'Pacific/Apia', name: 'GMT-11:00 Samoa'},
    +    {id: 'Pacific/Midway', name: 'GMT-11:00 Midway (Amerikanisch-Ozeanien)'},
    +    {id: 'Pacific/Niue', name: 'GMT-11:00 Niue'},
    +    {id: 'Pacific/Pago_Pago', name: 'GMT-11:00 Amerikanisch-Samoa'},
    +    {id: 'Etc/GMT+11', name: 'GMT-11:00'},
    +    {id: 'America/Adak', name: 'GMT-10:00 Adak (Vereinigte Staaten)'},
    +    {id: 'Pacific/Fakaofo', name: 'GMT-10:00 Tokelau'}
    +  ];
    +  goog.locale.registerTimeZoneAllLongNames(TimeZoneAllLongNames__de_DE,
    +      'de_DE');
    +
    +  goog.locale.setLocale('de_DE');
    +}
    +
    +/* Uncomment to display complete listing in the unit tested invocations.
    +
    +document.write('Shortnames in German for France:<br>');
    +
    +var idlist = goog.locale.getTimeZoneSelectedShortNames('FR');
    +
    +for (var i = 0; i < idlist.length; i++) {
    +  document.write(i + ') ' + idlist[i].id + ' = ' + idlist[i].name + '<br>');
    +}
    +
    +document.write('<hr>');
    +
    +document.write('long names in German for all en speakers:<br>');
    +var idlist = goog.locale.getTimeZoneSelectedLongNames('en');
    +
    +for (var i = 0; i < idlist.length; i++) {
    +  document.write(i + ') ' + idlist[i].id + ' = ' + idlist[i].name + '<br>');
    +}
    +
    +document.write('<hr>');
    +
    +document.write('Longnames in German for germans:<br>');
    +var idlist = goog.locale.getTimeZoneSelectedLongNames();
    +
    +for (var i = 0; i < idlist.length; i++) {
    +  document.write(i + ') ' + idlist[i].id + ' = ' + idlist[i].name + '<br>');
    +}
    +
    +document.write('<hr>');
    +
    +document.write('All longnames in German:<br>');
    +var idlist = goog.locale.getTimeZoneAllLongNames();
    +
    +for (var i = 0; i < idlist.length; i++) {
    +  var pair = idlist[i];
    +  document.write(i + ') ' + pair.id + ' = ' + pair.name + '<br>');
    +}
    +
    +document.write('<hr>');
    +*/
    +
    +// Test cases.
    +function testTimeZoneSelectedShortNames() {
    +  // Shortnames in German for France.
    +  var result = goog.locale.getTimeZoneSelectedShortNames('FR');
    +  assertEquals('Honolulu (Vereinigte Staaten)', result[3].name);
    +}
    +
    +function testTimeZoneSelectedLongNames() {
    +  // Long names in German for all English speaking regions.
    +  var result = goog.locale.getTimeZoneSelectedLongNames('en');
    +  assertEquals('GMT-11:00 Midway (Amerikanisch-Ozeanien)', result[1].name);
    +
    +  // Long names in German for germans.
    +  var result = goog.locale.getTimeZoneSelectedLongNames();
    +  assertEquals('GMT-10:00 Adak (Vereinigte Staaten)', result[2].name);
    +}
    +
    +function testTimeZoneAllLongNames() {
    +  // All longnames in German
    +  var result = goog.locale.getTimeZoneAllLongNames();
    +  assertEquals('GMT-10:00 Tokelau', result[7].name);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/log/log.js b/mapbox-gl/closure-library/closure/goog/log/log.js
    new file mode 100644
    index 0000000000..b93d835f2f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/log/log.js
    @@ -0,0 +1,197 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Basic strippable logging definitions.
    + * @see http://go/closurelogging
    + *
    + * @author johnlenz@google.com (John Lenz)
    + */
    +
    +goog.provide('goog.log');
    +goog.provide('goog.log.Level');
    +goog.provide('goog.log.LogRecord');
    +goog.provide('goog.log.Logger');
    +
    +goog.require('goog.debug');
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.debug.LogRecord');
    +goog.require('goog.debug.Logger');
    +
    +
    +/** @define {boolean} Whether logging is enabled. */
    +goog.define('goog.log.ENABLED', goog.debug.LOGGING_ENABLED);
    +
    +
    +/** @const */
    +goog.log.ROOT_LOGGER_NAME = goog.debug.Logger.ROOT_LOGGER_NAME;
    +
    +
    +
    +/**
    + * @constructor
    + * @final
    + */
    +goog.log.Logger = goog.debug.Logger;
    +
    +
    +
    +/**
    + * @constructor
    + * @final
    + */
    +goog.log.Level = goog.debug.Logger.Level;
    +
    +
    +
    +/**
    + * @constructor
    + * @final
    + */
    +goog.log.LogRecord = goog.debug.LogRecord;
    +
    +
    +/**
    + * Finds or creates a logger for a named subsystem. If a logger has already been
    + * created with the given name it is returned. Otherwise a new logger is
    + * created. If a new logger is created its log level will be configured based
    + * on the goog.debug.LogManager configuration and it will configured to also
    + * send logging output to its parent's handlers.
    + * @see goog.debug.LogManager
    + *
    + * @param {string} name A name for the logger. This should be a dot-separated
    + *     name and should normally be based on the package name or class name of
    + *     the subsystem, such as goog.net.BrowserChannel.
    + * @param {goog.log.Level=} opt_level If provided, override the
    + *     default logging level with the provided level.
    + * @return {goog.log.Logger} The named logger or null if logging is disabled.
    + */
    +goog.log.getLogger = function(name, opt_level) {
    +  if (goog.log.ENABLED) {
    +    var logger = goog.debug.LogManager.getLogger(name);
    +    if (opt_level && logger) {
    +      logger.setLevel(opt_level);
    +    }
    +    return logger;
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +// TODO(johnlenz): try to tighten the types to these functions.
    +/**
    + * Adds a handler to the logger. This doesn't use the event system because
    + * we want to be able to add logging to the event system.
    + * @param {goog.log.Logger} logger
    + * @param {Function} handler Handler function to add.
    + */
    +goog.log.addHandler = function(logger, handler) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.addHandler(handler);
    +  }
    +};
    +
    +
    +/**
    + * Removes a handler from the logger. This doesn't use the event system because
    + * we want to be able to add logging to the event system.
    + * @param {goog.log.Logger} logger
    + * @param {Function} handler Handler function to remove.
    + * @return {boolean} Whether the handler was removed.
    + */
    +goog.log.removeHandler = function(logger, handler) {
    +  if (goog.log.ENABLED && logger) {
    +    return logger.removeHandler(handler);
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Logs a message. If the logger is currently enabled for the
    + * given message level then the given message is forwarded to all the
    + * registered output Handler objects.
    + * @param {goog.log.Logger} logger
    + * @param {goog.log.Level} level One of the level identifiers.
    + * @param {goog.debug.Loggable} msg The message to log.
    + * @param {Error|Object=} opt_exception An exception associated with the
    + *     message.
    + */
    +goog.log.log = function(logger, level, msg, opt_exception) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.log(level, msg, opt_exception);
    +  }
    +};
    +
    +
    +/**
    + * Logs a message at the Level.SEVERE level.
    + * If the logger is currently enabled for the given message level then the
    + * given message is forwarded to all the registered output Handler objects.
    + * @param {goog.log.Logger} logger
    + * @param {goog.debug.Loggable} msg The message to log.
    + * @param {Error=} opt_exception An exception associated with the message.
    + */
    +goog.log.error = function(logger, msg, opt_exception) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.severe(msg, opt_exception);
    +  }
    +};
    +
    +
    +/**
    + * Logs a message at the Level.WARNING level.
    + * If the logger is currently enabled for the given message level then the
    + * given message is forwarded to all the registered output Handler objects.
    + * @param {goog.log.Logger} logger
    + * @param {goog.debug.Loggable} msg The message to log.
    + * @param {Error=} opt_exception An exception associated with the message.
    + */
    +goog.log.warning = function(logger, msg, opt_exception) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.warning(msg, opt_exception);
    +  }
    +};
    +
    +
    +/**
    + * Logs a message at the Level.INFO level.
    + * If the logger is currently enabled for the given message level then the
    + * given message is forwarded to all the registered output Handler objects.
    + * @param {goog.log.Logger} logger
    + * @param {goog.debug.Loggable} msg The message to log.
    + * @param {Error=} opt_exception An exception associated with the message.
    + */
    +goog.log.info = function(logger, msg, opt_exception) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.info(msg, opt_exception);
    +  }
    +};
    +
    +
    +/**
    + * Logs a message at the Level.Fine level.
    + * If the logger is currently enabled for the given message level then the
    + * given message is forwarded to all the registered output Handler objects.
    + * @param {goog.log.Logger} logger
    + * @param {goog.debug.Loggable} msg The message to log.
    + * @param {Error=} opt_exception An exception associated with the message.
    + */
    +goog.log.fine = function(logger, msg, opt_exception) {
    +  if (goog.log.ENABLED && logger) {
    +    logger.fine(msg, opt_exception);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/log/log_test.js b/mapbox-gl/closure-library/closure/goog/log/log_test.js
    new file mode 100644
    index 0000000000..65f0ac01d5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/log/log_test.js
    @@ -0,0 +1,187 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.log.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.logTest');
    +
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.testing.jsunit');
    +
    +
    +
    +goog.setTestOnly('goog.logTest');
    +
    +
    +
    +/**
    + * A simple log handler that remembers the last record published.
    + * @constructor
    + * @private
    + */
    +function TestHandler_() {
    +  this.logRecord = null;
    +}
    +
    +TestHandler_.prototype.onPublish = function(logRecord) {
    +  this.logRecord = logRecord;
    +};
    +
    +
    +TestHandler_.prototype.reset = function() {
    +  this.logRecord = null;
    +};
    +
    +
    +function testParents() {
    +  var logger2sibling1 = goog.log.getLogger('goog.test');
    +  var logger2sibling2 = goog.log.getLogger('goog.bar');
    +  var logger3sibling1 = goog.log.getLogger('goog.bar.foo');
    +  var logger3siblint2 = goog.log.getLogger('goog.bar.baaz');
    +  var rootLogger = goog.debug.LogManager.getRoot();
    +  var googLogger = goog.log.getLogger('goog');
    +  assertEquals(rootLogger, googLogger.getParent());
    +  assertEquals(googLogger, logger2sibling1.getParent());
    +  assertEquals(googLogger, logger2sibling2.getParent());
    +  assertEquals(logger2sibling2, logger3sibling1.getParent());
    +  assertEquals(logger2sibling2, logger3siblint2.getParent());
    +}
    +
    +function testLogging1() {
    +  var root = goog.debug.LogManager.getRoot();
    +  var handler = new TestHandler_();
    +  var f = goog.bind(handler.onPublish, handler);
    +  goog.log.addHandler(root, f);
    +  var logger = goog.log.getLogger('goog.bar.baaz');
    +  goog.log.log(logger, goog.log.Level.WARNING, 'foo');
    +  assertNotNull(handler.logRecord);
    +  assertEquals(goog.log.Level.WARNING, handler.logRecord.getLevel());
    +  assertEquals('foo', handler.logRecord.getMessage());
    +  handler.logRecord = null;
    +
    +  goog.log.removeHandler(root, f);
    +  goog.log.log(logger, goog.log.Level.WARNING, 'foo');
    +  assertNull(handler.logRecord);
    +}
    +
    +function testLogging2() {
    +  var root = goog.debug.LogManager.getRoot();
    +  var handler = new TestHandler_();
    +  var f = goog.bind(handler.onPublish, handler);
    +  goog.log.addHandler(root, f);
    +  var logger = goog.log.getLogger('goog.bar.baaz');
    +  goog.log.warning(logger, 'foo');
    +  assertNotNull(handler.logRecord);
    +  assertEquals(goog.log.Level.WARNING, handler.logRecord.getLevel());
    +  assertEquals('foo', handler.logRecord.getMessage());
    +  handler.logRecord = null;
    +
    +  goog.log.removeHandler(root, f);
    +  goog.log.log(logger, goog.log.Level.WARNING, 'foo');
    +  assertNull(handler.logRecord);
    +}
    +
    +
    +function testFiltering() {
    +  var root = goog.debug.LogManager.getRoot();
    +  var handler = new TestHandler_();
    +  var f = goog.bind(handler.onPublish, handler);
    +  root.addHandler(f);
    +  var logger1 = goog.log.getLogger('goog.bar.foo', goog.log.Level.WARNING);
    +  var logger2 = goog.log.getLogger('goog.bar.baaz', goog.log.Level.INFO);
    +  goog.log.warning(logger2, 'foo');
    +  assertNotNull(handler.logRecord);
    +  assertEquals(goog.log.Level.WARNING, handler.logRecord.getLevel());
    +  assertEquals('foo', handler.logRecord.getMessage());
    +  handler.reset();
    +  goog.log.info(logger1, 'bar');
    +  assertNull(handler.logRecord);
    +  goog.log.warning(logger1, 'baaz');
    +  assertNotNull(handler.logRecord);
    +  handler.reset();
    +  goog.log.error(logger1, 'baaz');
    +  assertNotNull(handler.logRecord);
    +}
    +
    +
    +function testException() {
    +  var root = goog.debug.LogManager.getRoot();
    +  var handler = new TestHandler_();
    +  var f = goog.bind(handler.onPublish, handler);
    +  root.addHandler(f);
    +  var logger = goog.log.getLogger('goog.debug.logger_test');
    +  var ex = Error('boo!');
    +  goog.log.error(logger, 'hello', ex);
    +  assertNotNull(handler.logRecord);
    +  assertEquals(goog.log.Level.SEVERE, handler.logRecord.getLevel());
    +  assertEquals('hello', handler.logRecord.getMessage());
    +  assertEquals(ex, handler.logRecord.getException());
    +}
    +
    +
    +function testMessageCallbacks() {
    +  var root = goog.debug.LogManager.getRoot();
    +  var handler = new TestHandler_();
    +  var f = goog.bind(handler.onPublish, handler);
    +  root.addHandler(f);
    +  var logger = goog.log.getLogger('goog.bar.foo');
    +  logger.setLevel(goog.log.Level.WARNING);
    +
    +  logger.log(goog.log.Level.INFO, function() {
    +    throw "Message callback shouldn't be called when below logger's level!";
    +  });
    +  assertNull(handler.logRecord);
    +
    +  logger.log(goog.log.Level.WARNING, function() {return 'heya'});
    +  assertNotNull(handler.logRecord);
    +  assertEquals(goog.log.Level.WARNING, handler.logRecord.getLevel());
    +  assertEquals('heya', handler.logRecord.getMessage());
    +}
    +
    +
    +function testGetLogRecord() {
    +  var name = 'test.get.log.record';
    +  var level = goog.log.Level.FINE;
    +  var msg = 'msg';
    +
    +  var logger = goog.log.getLogger(name);
    +  var logRecord = logger.getLogRecord(level, msg);
    +
    +  assertEquals(name, logRecord.getLoggerName());
    +  assertEquals(level, logRecord.getLevel());
    +  assertEquals(msg, logRecord.getMessage());
    +
    +  assertNull(logRecord.getException());
    +}
    +
    +function testGetLogRecordWithException() {
    +  var name = 'test.get.log.record';
    +  var level = goog.log.Level.FINE;
    +  var msg = 'msg';
    +  var ex = Error('Hi');
    +
    +  var logger = goog.log.getLogger(name);
    +  var logRecord = logger.getLogRecord(level, msg, ex);
    +
    +  assertEquals(name, logRecord.getLoggerName());
    +  assertEquals(level, logRecord.getLevel());
    +  assertEquals(msg, logRecord.getMessage());
    +  assertEquals(ex, logRecord.getException());
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/math/affinetransform.js b/mapbox-gl/closure-library/closure/goog/math/affinetransform.js
    new file mode 100644
    index 0000000000..b40f96a7fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/affinetransform.js
    @@ -0,0 +1,589 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Provides an object representation of an AffineTransform and
    + * methods for working with it.
    + */
    +
    +
    +goog.provide('goog.math.AffineTransform');
    +
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Creates a 2D affine transform. An affine transform performs a linear
    + * mapping from 2D coordinates to other 2D coordinates that preserves the
    + * "straightness" and "parallelness" of lines.
    + *
    + * Such a coordinate transformation can be represented by a 3 row by 3 column
    + * matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source
    + * coordinates (x,y) into destination coordinates (x',y') by considering them
    + * to be a column vector and multiplying the coordinate vector by the matrix
    + * according to the following process:
    + * <pre>
    + *      [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
    + *      [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
    + *      [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]
    + * </pre>
    + *
    + * This class is optimized for speed and minimizes calculations based on its
    + * knowledge of the underlying matrix (as opposed to say simply performing
    + * matrix multiplication).
    + *
    + * @param {number=} opt_m00 The m00 coordinate of the transform.
    + * @param {number=} opt_m10 The m10 coordinate of the transform.
    + * @param {number=} opt_m01 The m01 coordinate of the transform.
    + * @param {number=} opt_m11 The m11 coordinate of the transform.
    + * @param {number=} opt_m02 The m02 coordinate of the transform.
    + * @param {number=} opt_m12 The m12 coordinate of the transform.
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.AffineTransform = function(opt_m00, opt_m10, opt_m01,
    +    opt_m11, opt_m02, opt_m12) {
    +  if (arguments.length == 6) {
    +    this.setTransform(/** @type {number} */ (opt_m00),
    +                      /** @type {number} */ (opt_m10),
    +                      /** @type {number} */ (opt_m01),
    +                      /** @type {number} */ (opt_m11),
    +                      /** @type {number} */ (opt_m02),
    +                      /** @type {number} */ (opt_m12));
    +  } else if (arguments.length != 0) {
    +    throw Error('Insufficient matrix parameters');
    +  } else {
    +    this.m00_ = this.m11_ = 1;
    +    this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this transform is the identity transform.
    + */
    +goog.math.AffineTransform.prototype.isIdentity = function() {
    +  return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 &&
    +      this.m11_ == 1 && this.m02_ == 0 && this.m12_ == 0;
    +};
    +
    +
    +/**
    + * @return {!goog.math.AffineTransform} A copy of this transform.
    + */
    +goog.math.AffineTransform.prototype.clone = function() {
    +  return new goog.math.AffineTransform(this.m00_, this.m10_, this.m01_,
    +      this.m11_, this.m02_, this.m12_);
    +};
    +
    +
    +/**
    + * Sets this transform to the matrix specified by the 6 values.
    + *
    + * @param {number} m00 The m00 coordinate of the transform.
    + * @param {number} m10 The m10 coordinate of the transform.
    + * @param {number} m01 The m01 coordinate of the transform.
    + * @param {number} m11 The m11 coordinate of the transform.
    + * @param {number} m02 The m02 coordinate of the transform.
    + * @param {number} m12 The m12 coordinate of the transform.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.setTransform = function(m00, m10, m01,
    +    m11, m02, m12) {
    +  if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||
    +      !goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {
    +    throw Error('Invalid transform parameters');
    +  }
    +  this.m00_ = m00;
    +  this.m10_ = m10;
    +  this.m01_ = m01;
    +  this.m11_ = m11;
    +  this.m02_ = m02;
    +  this.m12_ = m12;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets this transform to be identical to the given transform.
    + *
    + * @param {!goog.math.AffineTransform} tx The transform to copy.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.copyFrom = function(tx) {
    +  this.m00_ = tx.m00_;
    +  this.m10_ = tx.m10_;
    +  this.m01_ = tx.m01_;
    +  this.m11_ = tx.m11_;
    +  this.m02_ = tx.m02_;
    +  this.m12_ = tx.m12_;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.scale = function(sx, sy) {
    +  this.m00_ *= sx;
    +  this.m10_ *= sx;
    +  this.m01_ *= sy;
    +  this.m11_ *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a scaling transformation,
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [sx  0 0] [m00 m01 m02]
    + * [ 0 sy 0] [m10 m11 m12]
    + * [ 0  0 1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.preScale = function(sx, sy) {
    +  this.m00_ *= sx;
    +  this.m01_ *= sx;
    +  this.m02_ *= sx;
    +  this.m10_ *= sy;
    +  this.m11_ *= sy;
    +  this.m12_ *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a translate transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.translate = function(dx, dy) {
    +  this.m02_ += dx * this.m00_ + dy * this.m01_;
    +  this.m12_ += dx * this.m10_ + dy * this.m11_;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a translate transformation,
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [1 0 dx] [m00 m01 m02]
    + * [0 1 dy] [m10 m11 m12]
    + * [0 0  1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.preTranslate = function(dx, dy) {
    +  this.m02_ += dx;
    +  this.m12_ += dy;
    +  return this;
    +};
    +
    +
    +/**
    + * Concatenates this transform with a rotation transformation around an anchor
    + * point.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.rotate = function(theta, x, y) {
    +  return this.concatenate(
    +      goog.math.AffineTransform.getRotateInstance(theta, x, y));
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a rotation transformation around an
    + * anchor point.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.preRotate = function(theta, x, y) {
    +  return this.preConcatenate(
    +      goog.math.AffineTransform.getRotateInstance(theta, x, y));
    +};
    +
    +
    +/**
    + * Concatenates this transform with a shear transformation.
    + *
    + * @param {number} shx The x shear factor.
    + * @param {number} shy The y shear factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.shear = function(shx, shy) {
    +  var m00 = this.m00_;
    +  var m10 = this.m10_;
    +  this.m00_ += shy * this.m01_;
    +  this.m10_ += shy * this.m11_;
    +  this.m01_ += shx * m00;
    +  this.m11_ += shx * m10;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates this transform with a shear transformation.
    + * i.e. calculates the following matrix product:
    + *
    + * <pre>
    + * [  1 shx 0] [m00 m01 m02]
    + * [shy   1 0] [m10 m11 m12]
    + * [  0   0 1] [  0   0   1]
    + * </pre>
    + *
    + * @param {number} shx The x shear factor.
    + * @param {number} shy The y shear factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.preShear = function(shx, shy) {
    +  var m00 = this.m00_;
    +  var m01 = this.m01_;
    +  var m02 = this.m02_;
    +  this.m00_ += shx * this.m10_;
    +  this.m01_ += shx * this.m11_;
    +  this.m02_ += shx * this.m12_;
    +  this.m10_ += shy * m00;
    +  this.m11_ += shy * m01;
    +  this.m12_ += shy * m02;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {string} A string representation of this transform. The format of
    + *     of the string is compatible with SVG matrix notation, i.e.
    + *     "matrix(a,b,c,d,e,f)".
    + * @override
    + */
    +goog.math.AffineTransform.prototype.toString = function() {
    +  return 'matrix(' +
    +      [this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(
    +          ',') +
    +      ')';
    +};
    +
    +
    +/**
    + * @return {number} The scaling factor in the x-direction (m00).
    + */
    +goog.math.AffineTransform.prototype.getScaleX = function() {
    +  return this.m00_;
    +};
    +
    +
    +/**
    + * @return {number} The scaling factor in the y-direction (m11).
    + */
    +goog.math.AffineTransform.prototype.getScaleY = function() {
    +  return this.m11_;
    +};
    +
    +
    +/**
    + * @return {number} The translation in the x-direction (m02).
    + */
    +goog.math.AffineTransform.prototype.getTranslateX = function() {
    +  return this.m02_;
    +};
    +
    +
    +/**
    + * @return {number} The translation in the y-direction (m12).
    + */
    +goog.math.AffineTransform.prototype.getTranslateY = function() {
    +  return this.m12_;
    +};
    +
    +
    +/**
    + * @return {number} The shear factor in the x-direction (m01).
    + */
    +goog.math.AffineTransform.prototype.getShearX = function() {
    +  return this.m01_;
    +};
    +
    +
    +/**
    + * @return {number} The shear factor in the y-direction (m10).
    + */
    +goog.math.AffineTransform.prototype.getShearY = function() {
    +  return this.m10_;
    +};
    +
    +
    +/**
    + * Concatenates an affine transform to this transform.
    + *
    + * @param {!goog.math.AffineTransform} tx The transform to concatenate.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.concatenate = function(tx) {
    +  var m0 = this.m00_;
    +  var m1 = this.m01_;
    +  this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;
    +  this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;
    +  this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;
    +
    +  m0 = this.m10_;
    +  m1 = this.m11_;
    +  this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;
    +  this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;
    +  this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;
    +  return this;
    +};
    +
    +
    +/**
    + * Pre-concatenates an affine transform to this transform.
    + *
    + * @param {!goog.math.AffineTransform} tx The transform to preconcatenate.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.preConcatenate = function(tx) {
    +  var m0 = this.m00_;
    +  var m1 = this.m10_;
    +  this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;
    +  this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;
    +
    +  m0 = this.m01_;
    +  m1 = this.m11_;
    +  this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;
    +  this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;
    +
    +  m0 = this.m02_;
    +  m1 = this.m12_;
    +  this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;
    +  this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;
    +  return this;
    +};
    +
    +
    +/**
    + * Transforms an array of coordinates by this transform and stores the result
    + * into a destination array.
    + *
    + * @param {!Array<number>} src The array containing the source points
    + *     as x, y value pairs.
    + * @param {number} srcOff The offset to the first point to be transformed.
    + * @param {!Array<number>} dst The array into which to store the transformed
    + *     point pairs.
    + * @param {number} dstOff The offset of the location of the first transformed
    + *     point in the destination array.
    + * @param {number} numPts The number of points to tranform.
    + */
    +goog.math.AffineTransform.prototype.transform = function(src, srcOff, dst,
    +    dstOff, numPts) {
    +  var i = srcOff;
    +  var j = dstOff;
    +  var srcEnd = srcOff + 2 * numPts;
    +  while (i < srcEnd) {
    +    var x = src[i++];
    +    var y = src[i++];
    +    dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;
    +    dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The determinant of this transform.
    + */
    +goog.math.AffineTransform.prototype.getDeterminant = function() {
    +  return this.m00_ * this.m11_ - this.m01_ * this.m10_;
    +};
    +
    +
    +/**
    + * Returns whether the transform is invertible. A transform is not invertible
    + * if the determinant is 0 or any value is non-finite or NaN.
    + *
    + * @return {boolean} Whether the transform is invertible.
    + */
    +goog.math.AffineTransform.prototype.isInvertible = function() {
    +  var det = this.getDeterminant();
    +  return goog.math.isFiniteNumber(det) &&
    +      goog.math.isFiniteNumber(this.m02_) &&
    +      goog.math.isFiniteNumber(this.m12_) &&
    +      det != 0;
    +};
    +
    +
    +/**
    + * @return {!goog.math.AffineTransform} An AffineTransform object
    + *     representing the inverse transformation.
    + */
    +goog.math.AffineTransform.prototype.createInverse = function() {
    +  var det = this.getDeterminant();
    +  return new goog.math.AffineTransform(
    +      this.m11_ / det,
    +      -this.m10_ / det,
    +      -this.m01_ / det,
    +      this.m00_ / det,
    +      (this.m01_ * this.m12_ - this.m11_ * this.m02_) / det,
    +      (this.m10_ * this.m02_ - this.m00_ * this.m12_) / det);
    +};
    +
    +
    +/**
    + * Creates a transform representing a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.math.AffineTransform} A transform representing a scaling
    + *     transformation.
    + */
    +goog.math.AffineTransform.getScaleInstance = function(sx, sy) {
    +  return new goog.math.AffineTransform().setToScale(sx, sy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a translation transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.math.AffineTransform} A transform representing a
    + *     translation transformation.
    + */
    +goog.math.AffineTransform.getTranslateInstance = function(dx, dy) {
    +  return new goog.math.AffineTransform().setToTranslation(dx, dy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a shearing transformation.
    + *
    + * @param {number} shx The x-axis shear factor.
    + * @param {number} shy The y-axis shear factor.
    + * @return {!goog.math.AffineTransform} A transform representing a shearing
    + *     transformation.
    + */
    +goog.math.AffineTransform.getShearInstance = function(shx, shy) {
    +  return new goog.math.AffineTransform().setToShear(shx, shy);
    +};
    +
    +
    +/**
    + * Creates a transform representing a rotation transformation.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.math.AffineTransform} A transform representing a rotation
    + *     transformation.
    + */
    +goog.math.AffineTransform.getRotateInstance = function(theta, x, y) {
    +  return new goog.math.AffineTransform().setToRotation(theta, x, y);
    +};
    +
    +
    +/**
    + * Sets this transform to a scaling transformation.
    + *
    + * @param {number} sx The x-axis scaling factor.
    + * @param {number} sy The y-axis scaling factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.setToScale = function(sx, sy) {
    +  return this.setTransform(sx, 0, 0, sy, 0, 0);
    +};
    +
    +
    +/**
    + * Sets this transform to a translation transformation.
    + *
    + * @param {number} dx The distance to translate in the x direction.
    + * @param {number} dy The distance to translate in the y direction.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.setToTranslation = function(dx, dy) {
    +  return this.setTransform(1, 0, 0, 1, dx, dy);
    +};
    +
    +
    +/**
    + * Sets this transform to a shearing transformation.
    + *
    + * @param {number} shx The x-axis shear factor.
    + * @param {number} shy The y-axis shear factor.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.setToShear = function(shx, shy) {
    +  return this.setTransform(1, shy, shx, 1, 0, 0);
    +};
    +
    +
    +/**
    + * Sets this transform to a rotation transformation.
    + *
    + * @param {number} theta The angle of rotation measured in radians.
    + * @param {number} x The x coordinate of the anchor point.
    + * @param {number} y The y coordinate of the anchor point.
    + * @return {!goog.math.AffineTransform} This affine transform.
    + */
    +goog.math.AffineTransform.prototype.setToRotation = function(theta, x, y) {
    +  var cos = Math.cos(theta);
    +  var sin = Math.sin(theta);
    +  return this.setTransform(cos, sin, -sin, cos,
    +      x - x * cos + y * sin, y - x * sin - y * cos);
    +};
    +
    +
    +/**
    + * Compares two affine transforms for equality.
    + *
    + * @param {goog.math.AffineTransform} tx The other affine transform.
    + * @return {boolean} whether the two transforms are equal.
    + */
    +goog.math.AffineTransform.prototype.equals = function(tx) {
    +  if (this == tx) {
    +    return true;
    +  }
    +  if (!tx) {
    +    return false;
    +  }
    +  return this.m00_ == tx.m00_ &&
    +      this.m01_ == tx.m01_ &&
    +      this.m02_ == tx.m02_ &&
    +      this.m10_ == tx.m10_ &&
    +      this.m11_ == tx.m11_ &&
    +      this.m12_ == tx.m12_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.html b/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.html
    new file mode 100644
    index 0000000000..95857eab38
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.math.AffineTransform</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.math.AffineTransformTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.js b/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.js
    new file mode 100644
    index 0000000000..b71e0f3880
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/affinetransform_test.js
    @@ -0,0 +1,359 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.AffineTransformTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +goog.require('goog.math.AffineTransform');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.math.AffineTransformTest');
    +
    +function testGetTranslateInstance() {
    +  var tx = goog.math.AffineTransform.getTranslateInstance(2, 4);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(0, tx.getShearY());
    +  assertEquals(0, tx.getShearX());
    +  assertEquals(1, tx.getScaleY());
    +  assertEquals(2, tx.getTranslateX());
    +  assertEquals(4, tx.getTranslateY());
    +}
    +
    +function testGetScaleInstance() {
    +  var tx = goog.math.AffineTransform.getScaleInstance(2, 4);
    +  assertEquals(2, tx.getScaleX());
    +  assertEquals(0, tx.getShearY());
    +  assertEquals(0, tx.getShearX());
    +  assertEquals(4, tx.getScaleY());
    +  assertEquals(0, tx.getTranslateX());
    +  assertEquals(0, tx.getTranslateY());
    +}
    +
    +function testGetRotateInstance() {
    +  var tx = goog.math.AffineTransform.getRotateInstance(Math.PI / 2, 1, 2);
    +  assertRoughlyEquals(0, tx.getScaleX(), 1e-9);
    +  assertRoughlyEquals(1, tx.getShearY(), 1e-9);
    +  assertRoughlyEquals(-1, tx.getShearX(), 1e-9);
    +  assertRoughlyEquals(0, tx.getScaleY(), 1e-9);
    +  assertRoughlyEquals(3, tx.getTranslateX(), 1e-9);
    +  assertRoughlyEquals(1, tx.getTranslateY(), 1e-9);
    +}
    +
    +function testGetShearInstance() {
    +  var tx = goog.math.AffineTransform.getShearInstance(2, 4);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(4, tx.getShearY());
    +  assertEquals(2, tx.getShearX());
    +  assertEquals(1, tx.getScaleY());
    +  assertEquals(0, tx.getTranslateX());
    +  assertEquals(0, tx.getTranslateY());
    +}
    +
    +function testConstructor() {
    +  assertThrows(function() {
    +    new goog.math.AffineTransform([0, 0]);
    +  });
    +  assertThrows(function() {
    +    new goog.math.AffineTransform({});
    +  });
    +  assertThrows(function() {
    +    new goog.math.AffineTransform(0, 0, 0, 'a', 0, 0);
    +  });
    +
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(2, tx.getShearY());
    +  assertEquals(3, tx.getShearX());
    +  assertEquals(4, tx.getScaleY());
    +  assertEquals(5, tx.getTranslateX());
    +  assertEquals(6, tx.getTranslateY());
    +
    +  tx = new goog.math.AffineTransform();
    +  assert(tx.isIdentity());
    +}
    +
    +function testIsIdentity() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  assertFalse(tx.isIdentity());
    +  tx.setTransform(1, 0, 0, 1, 0, 0);
    +  assert(tx.isIdentity());
    +}
    +
    +function testClone() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  var copy = tx.clone();
    +  assertEquals(copy.getScaleX(), tx.getScaleX());
    +  assertEquals(copy.getShearY(), tx.getShearY());
    +  assertEquals(copy.getShearX(), tx.getShearX());
    +  assertEquals(copy.getScaleY(), tx.getScaleY());
    +  assertEquals(copy.getTranslateX(), tx.getTranslateX());
    +  assertEquals(copy.getTranslateY(), tx.getTranslateY());
    +}
    +
    +function testSetTransform() {
    +  var tx = new goog.math.AffineTransform();
    +  assertThrows(function() {
    +    tx.setTransform(1, 2, 3, 4, 6);
    +  });
    +  assertThrows(function() {
    +    tx.setTransform('a', 2, 3, 4, 5, 6);
    +  });
    +
    +  tx.setTransform(1, 2, 3, 4, 5, 6);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(2, tx.getShearY());
    +  assertEquals(3, tx.getShearX());
    +  assertEquals(4, tx.getScaleY());
    +  assertEquals(5, tx.getTranslateX());
    +  assertEquals(6, tx.getTranslateY());
    +}
    +
    +function testScale() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.scale(2, 3);
    +  assertEquals(2, tx.getScaleX());
    +  assertEquals(4, tx.getShearY());
    +  assertEquals(9, tx.getShearX());
    +  assertEquals(12, tx.getScaleY());
    +  assertEquals(5, tx.getTranslateX());
    +  assertEquals(6, tx.getTranslateY());
    +}
    +
    +function testPreScale() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.preScale(2, 3);
    +  assertEquals(2, tx.getScaleX());
    +  assertEquals(6, tx.getShearY());
    +  assertEquals(6, tx.getShearX());
    +  assertEquals(12, tx.getScaleY());
    +  assertEquals(10, tx.getTranslateX());
    +  assertEquals(18, tx.getTranslateY());
    +}
    +
    +function testTranslate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.translate(2, 3);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(2, tx.getShearY());
    +  assertEquals(3, tx.getShearX());
    +  assertEquals(4, tx.getScaleY());
    +  assertEquals(16, tx.getTranslateX());
    +  assertEquals(22, tx.getTranslateY());
    +}
    +
    +function testPreTranslate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.preTranslate(2, 3);
    +  assertEquals(1, tx.getScaleX());
    +  assertEquals(2, tx.getShearY());
    +  assertEquals(3, tx.getShearX());
    +  assertEquals(4, tx.getScaleY());
    +  assertEquals(7, tx.getTranslateX());
    +  assertEquals(9, tx.getTranslateY());
    +}
    +
    +function testRotate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.rotate(Math.PI / 2, 1, 1);
    +  assertRoughlyEquals(3, tx.getScaleX(), 1e-9);
    +  assertRoughlyEquals(4, tx.getShearY(), 1e-9);
    +  assertRoughlyEquals(-1, tx.getShearX(), 1e-9);
    +  assertRoughlyEquals(-2, tx.getScaleY(), 1e-9);
    +  assertRoughlyEquals(7, tx.getTranslateX(), 1e-9);
    +  assertRoughlyEquals(10, tx.getTranslateY(), 1e-9);
    +}
    +
    +function testPreRotate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.preRotate(Math.PI / 2, 1, 1);
    +  assertRoughlyEquals(-2, tx.getScaleX(), 1e-9);
    +  assertRoughlyEquals(1, tx.getShearY(), 1e-9);
    +  assertRoughlyEquals(-4, tx.getShearX(), 1e-9);
    +  assertRoughlyEquals(3, tx.getScaleY(), 1e-9);
    +  assertRoughlyEquals(-4, tx.getTranslateX(), 1e-9);
    +  assertRoughlyEquals(5, tx.getTranslateY(), 1e-9);
    +}
    +
    +function testShear() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.shear(2, 3);
    +  assertEquals(10, tx.getScaleX());
    +  assertEquals(14, tx.getShearY());
    +  assertEquals(5, tx.getShearX());
    +  assertEquals(8, tx.getScaleY());
    +  assertEquals(5, tx.getTranslateX());
    +  assertEquals(6, tx.getTranslateY());
    +}
    +
    +function testPreShear() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.preShear(2, 3);
    +  assertEquals(5, tx.getScaleX());
    +  assertEquals(5, tx.getShearY());
    +  assertEquals(11, tx.getShearX());
    +  assertEquals(13, tx.getScaleY());
    +  assertEquals(17, tx.getTranslateX());
    +  assertEquals(21, tx.getTranslateY());
    +}
    +
    +function testConcatentate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.concatenate(new goog.math.AffineTransform(2, 1, 6, 5, 4, 3));
    +  assertEquals(5, tx.getScaleX());
    +  assertEquals(8, tx.getShearY());
    +  assertEquals(21, tx.getShearX());
    +  assertEquals(32, tx.getScaleY());
    +  assertEquals(18, tx.getTranslateX());
    +  assertEquals(26, tx.getTranslateY());
    +}
    +
    +function testPreConcatentate() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  tx.preConcatenate(new goog.math.AffineTransform(2, 1, 6, 5, 4, 3));
    +  assertEquals(14, tx.getScaleX());
    +  assertEquals(11, tx.getShearY());
    +  assertEquals(30, tx.getShearX());
    +  assertEquals(23, tx.getScaleY());
    +  assertEquals(50, tx.getTranslateX());
    +  assertEquals(38, tx.getTranslateY());
    +}
    +
    +function testAssociativeConcatenate() {
    +  var x = new goog.math.AffineTransform(2, 3, 5, 7, 11, 13).concatenate(
    +      new goog.math.AffineTransform(17, 19, 23, 29, 31, 37));
    +  var y = new goog.math.AffineTransform(17, 19, 23, 29, 31, 37)
    +      .preConcatenate(new goog.math.AffineTransform(2, 3, 5, 7, 11, 13));
    +  assertEquals(x.getScaleX(), y.getScaleX());
    +  assertEquals(x.getShearY(), y.getShearY());
    +  assertEquals(x.getShearX(), y.getShearX());
    +  assertEquals(x.getScaleY(), y.getScaleY());
    +  assertEquals(x.getTranslateX(), y.getTranslateX());
    +  assertEquals(x.getTranslateY(), y.getTranslateY());
    +}
    +
    +function testTransform() {
    +  var srcPts = [0, 0, 1, 0, 1, 1, 0, 1];
    +  var dstPts = [];
    +  var tx = goog.math.AffineTransform.getScaleInstance(2, 3);
    +  tx.translate(5, 10);
    +  tx.rotate(Math.PI / 4, 5, 10);
    +  tx.transform(srcPts, 0, dstPts, 0, 4);
    +  assert(goog.array.equals(
    +      [27.071068, 28.180195, 28.485281, 30.301516,
    +       27.071068, 32.422836, 25.656855, 30.301516],
    +      dstPts,
    +      goog.math.nearlyEquals));
    +}
    +
    +function testGetDeterminant() {
    +  var tx = goog.math.AffineTransform.getScaleInstance(2, 3);
    +  tx.translate(5, 10);
    +  tx.rotate(Math.PI / 4, 5, 10);
    +  assertRoughlyEquals(6, tx.getDeterminant(), 0.001);
    +}
    +
    +function testIsInvertible() {
    +  assertTrue(new goog.math.AffineTransform(2, 3, 4, 5, 6, 7).
    +      isInvertible());
    +  assertTrue(new goog.math.AffineTransform(1, 0, 0, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(NaN, 0, 0, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, NaN, 0, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, NaN, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, NaN, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, 1, NaN, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, 1, 0, NaN).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(Infinity, 0, 0, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, Infinity, 0, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, Infinity, 1, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, Infinity, 0, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, 1, Infinity, 0).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(1, 0, 0, 1, 0, Infinity).
    +      isInvertible());
    +  assertFalse(new goog.math.AffineTransform(0, 0, 0, 0, 1, 0).
    +      isInvertible());
    +}
    +
    +function testCreateInverse() {
    +  var tx = goog.math.AffineTransform.getScaleInstance(2, 3);
    +  tx.translate(5, 10);
    +  tx.rotate(Math.PI / 4, 5, 10);
    +  var inverse = tx.createInverse();
    +  assert(goog.math.nearlyEquals(0.353553, inverse.getScaleX()));
    +  assert(goog.math.nearlyEquals(-0.353553, inverse.getShearY()));
    +  assert(goog.math.nearlyEquals(0.235702, inverse.getShearX()));
    +  assert(goog.math.nearlyEquals(0.235702, inverse.getScaleY()));
    +  assert(goog.math.nearlyEquals(-16.213203, inverse.getTranslateX()));
    +  assert(goog.math.nearlyEquals(2.928932, inverse.getTranslateY()));
    +}
    +
    +function testCopyFrom() {
    +  var from = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  var to = new goog.math.AffineTransform();
    +  to.copyFrom(from);
    +  assertEquals(from.getScaleX(), to.getScaleX());
    +  assertEquals(from.getShearY(), to.getShearY());
    +  assertEquals(from.getShearX(), to.getShearX());
    +  assertEquals(from.getScaleY(), to.getScaleY());
    +  assertEquals(from.getTranslateX(), to.getTranslateX());
    +  assertEquals(from.getTranslateY(), to.getTranslateY());
    +}
    +
    +function testToString() {
    +  var tx = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  assertEquals('matrix(1,2,3,4,5,6)', tx.toString());
    +}
    +
    +function testEquals() {
    +  var tx1 = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  var tx2 = new goog.math.AffineTransform(1, 2, 3, 4, 5, 6);
    +  assertEqualsMethod(tx1, tx2, true);
    +
    +  tx2 = new goog.math.AffineTransform(-1, 2, 3, 4, 5, 6);
    +  assertEqualsMethod(tx1, tx2, false);
    +
    +  tx2 = new goog.math.AffineTransform(1, -1, 3, 4, 5, 6);
    +  assertEqualsMethod(tx1, tx2, false);
    +
    +  tx2 = new goog.math.AffineTransform(1, 2, -3, 4, 5, 6);
    +  assertEqualsMethod(tx1, tx2, false);
    +
    +  tx2 = new goog.math.AffineTransform(1, 2, 3, -4, 5, 6);
    +  assertEqualsMethod(tx1, tx2, false);
    +
    +  tx2 = new goog.math.AffineTransform(1, 2, 3, 4, -5, 6);
    +  assertEqualsMethod(tx1, tx2, false);
    +
    +  tx2 = new goog.math.AffineTransform(1, 2, 3, 4, 5, -6);
    +  assertEqualsMethod(tx1, tx2, false);
    +}
    +
    +function assertEqualsMethod(tx1, tx2, expected) {
    +  assertEquals(expected, tx1.equals(tx2));
    +  assertEquals(expected, tx2.equals(tx1));
    +  assertEquals(true, tx1.equals(tx1));
    +  assertEquals(true, tx2.equals(tx2));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/bezier.js b/mapbox-gl/closure-library/closure/goog/math/bezier.js
    new file mode 100644
    index 0000000000..8aaa93375e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/bezier.js
    @@ -0,0 +1,340 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a cubic Bezier curve.
    + *
    + * Uses the deCasteljau algorithm to compute points on the curve.
    + * http://en.wikipedia.org/wiki/De_Casteljau's_algorithm
    + *
    + * Currently it uses an unrolled version of the algorithm for speed.  Eventually
    + * it may be useful to use the loop form of the algorithm in order to support
    + * curves of arbitrary degree.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.math.Bezier');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate');
    +
    +
    +
    +/**
    + * Object representing a cubic bezier curve.
    + * @param {number} x0 X coordinate of the start point.
    + * @param {number} y0 Y coordinate of the start point.
    + * @param {number} x1 X coordinate of the first control point.
    + * @param {number} y1 Y coordinate of the first control point.
    + * @param {number} x2 X coordinate of the second control point.
    + * @param {number} y2 Y coordinate of the second control point.
    + * @param {number} x3 X coordinate of the end point.
    + * @param {number} y3 Y coordinate of the end point.
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.Bezier = function(x0, y0, x1, y1, x2, y2, x3, y3) {
    +  /**
    +   * X coordinate of the first point.
    +   * @type {number}
    +   */
    +  this.x0 = x0;
    +
    +  /**
    +   * Y coordinate of the first point.
    +   * @type {number}
    +   */
    +  this.y0 = y0;
    +
    +  /**
    +   * X coordinate of the first control point.
    +   * @type {number}
    +   */
    +  this.x1 = x1;
    +
    +  /**
    +   * Y coordinate of the first control point.
    +   * @type {number}
    +   */
    +  this.y1 = y1;
    +
    +  /**
    +   * X coordinate of the second control point.
    +   * @type {number}
    +   */
    +  this.x2 = x2;
    +
    +  /**
    +   * Y coordinate of the second control point.
    +   * @type {number}
    +   */
    +  this.y2 = y2;
    +
    +  /**
    +   * X coordinate of the end point.
    +   * @type {number}
    +   */
    +  this.x3 = x3;
    +
    +  /**
    +   * Y coordinate of the end point.
    +   * @type {number}
    +   */
    +  this.y3 = y3;
    +};
    +
    +
    +/**
    + * Constant used to approximate ellipses.
    + * See: http://canvaspaint.org/blog/2006/12/ellipse/
    + * @type {number}
    + */
    +goog.math.Bezier.KAPPA = 4 * (Math.sqrt(2) - 1) / 3;
    +
    +
    +/**
    + * @return {!goog.math.Bezier} A copy of this curve.
    + */
    +goog.math.Bezier.prototype.clone = function() {
    +  return new goog.math.Bezier(this.x0, this.y0, this.x1, this.y1, this.x2,
    +      this.y2, this.x3, this.y3);
    +};
    +
    +
    +/**
    + * Test if the given curve is exactly the same as this one.
    + * @param {goog.math.Bezier} other The other curve.
    + * @return {boolean} Whether the given curve is the same as this one.
    + */
    +goog.math.Bezier.prototype.equals = function(other) {
    +  return this.x0 == other.x0 && this.y0 == other.y0 && this.x1 == other.x1 &&
    +         this.y1 == other.y1 && this.x2 == other.x2 && this.y2 == other.y2 &&
    +         this.x3 == other.x3 && this.y3 == other.y3;
    +};
    +
    +
    +/**
    + * Modifies the curve in place to progress in the opposite direction.
    + */
    +goog.math.Bezier.prototype.flip = function() {
    +  var temp = this.x0;
    +  this.x0 = this.x3;
    +  this.x3 = temp;
    +  temp = this.y0;
    +  this.y0 = this.y3;
    +  this.y3 = temp;
    +
    +  temp = this.x1;
    +  this.x1 = this.x2;
    +  this.x2 = temp;
    +  temp = this.y1;
    +  this.y1 = this.y2;
    +  this.y2 = temp;
    +};
    +
    +
    +/**
    + * Computes the curve's X coordinate at a point between 0 and 1.
    + * @param {number} t The point on the curve to find.
    + * @return {number} The computed coordinate.
    + */
    +goog.math.Bezier.prototype.getPointX = function(t) {
    +  // Special case start and end.
    +  if (t == 0) {
    +    return this.x0;
    +  } else if (t == 1) {
    +    return this.x3;
    +  }
    +
    +  // Step one - from 4 points to 3
    +  var ix0 = goog.math.lerp(this.x0, this.x1, t);
    +  var ix1 = goog.math.lerp(this.x1, this.x2, t);
    +  var ix2 = goog.math.lerp(this.x2, this.x3, t);
    +
    +  // Step two - from 3 points to 2
    +  ix0 = goog.math.lerp(ix0, ix1, t);
    +  ix1 = goog.math.lerp(ix1, ix2, t);
    +
    +  // Final step - last point
    +  return goog.math.lerp(ix0, ix1, t);
    +};
    +
    +
    +/**
    + * Computes the curve's Y coordinate at a point between 0 and 1.
    + * @param {number} t The point on the curve to find.
    + * @return {number} The computed coordinate.
    + */
    +goog.math.Bezier.prototype.getPointY = function(t) {
    +  // Special case start and end.
    +  if (t == 0) {
    +    return this.y0;
    +  } else if (t == 1) {
    +    return this.y3;
    +  }
    +
    +  // Step one - from 4 points to 3
    +  var iy0 = goog.math.lerp(this.y0, this.y1, t);
    +  var iy1 = goog.math.lerp(this.y1, this.y2, t);
    +  var iy2 = goog.math.lerp(this.y2, this.y3, t);
    +
    +  // Step two - from 3 points to 2
    +  iy0 = goog.math.lerp(iy0, iy1, t);
    +  iy1 = goog.math.lerp(iy1, iy2, t);
    +
    +  // Final step - last point
    +  return goog.math.lerp(iy0, iy1, t);
    +};
    +
    +
    +/**
    + * Computes the curve at a point between 0 and 1.
    + * @param {number} t The point on the curve to find.
    + * @return {!goog.math.Coordinate} The computed coordinate.
    + */
    +goog.math.Bezier.prototype.getPoint = function(t) {
    +  return new goog.math.Coordinate(this.getPointX(t), this.getPointY(t));
    +};
    +
    +
    +/**
    + * Changes this curve in place to be the portion of itself from [t, 1].
    + * @param {number} t The start of the desired portion of the curve.
    + */
    +goog.math.Bezier.prototype.subdivideLeft = function(t) {
    +  if (t == 1) {
    +    return;
    +  }
    +
    +  // Step one - from 4 points to 3
    +  var ix0 = goog.math.lerp(this.x0, this.x1, t);
    +  var iy0 = goog.math.lerp(this.y0, this.y1, t);
    +
    +  var ix1 = goog.math.lerp(this.x1, this.x2, t);
    +  var iy1 = goog.math.lerp(this.y1, this.y2, t);
    +
    +  var ix2 = goog.math.lerp(this.x2, this.x3, t);
    +  var iy2 = goog.math.lerp(this.y2, this.y3, t);
    +
    +  // Collect our new x1 and y1
    +  this.x1 = ix0;
    +  this.y1 = iy0;
    +
    +  // Step two - from 3 points to 2
    +  ix0 = goog.math.lerp(ix0, ix1, t);
    +  iy0 = goog.math.lerp(iy0, iy1, t);
    +
    +  ix1 = goog.math.lerp(ix1, ix2, t);
    +  iy1 = goog.math.lerp(iy1, iy2, t);
    +
    +  // Collect our new x2 and y2
    +  this.x2 = ix0;
    +  this.y2 = iy0;
    +
    +  // Final step - last point
    +  this.x3 = goog.math.lerp(ix0, ix1, t);
    +  this.y3 = goog.math.lerp(iy0, iy1, t);
    +};
    +
    +
    +/**
    + * Changes this curve in place to be the portion of itself from [0, t].
    + * @param {number} t The end of the desired portion of the curve.
    + */
    +goog.math.Bezier.prototype.subdivideRight = function(t) {
    +  this.flip();
    +  this.subdivideLeft(1 - t);
    +  this.flip();
    +};
    +
    +
    +/**
    + * Changes this curve in place to be the portion of itself from [s, t].
    + * @param {number} s The start of the desired portion of the curve.
    + * @param {number} t The end of the desired portion of the curve.
    + */
    +goog.math.Bezier.prototype.subdivide = function(s, t) {
    +  this.subdivideRight(s);
    +  this.subdivideLeft((t - s) / (1 - s));
    +};
    +
    +
    +/**
    + * Computes the position t of a point on the curve given its x coordinate.
    + * That is, for an input xVal, finds t s.t. getPointX(t) = xVal.
    + * As such, the following should always be true up to some small epsilon:
    + * t ~ solvePositionFromXValue(getPointX(t)) for t in [0, 1].
    + * @param {number} xVal The x coordinate of the point to find on the curve.
    + * @return {number} The position t.
    + */
    +goog.math.Bezier.prototype.solvePositionFromXValue = function(xVal) {
    +  // Desired precision on the computation.
    +  var epsilon = 1e-6;
    +
    +  // Initial estimate of t using linear interpolation.
    +  var t = (xVal - this.x0) / (this.x3 - this.x0);
    +  if (t <= 0) {
    +    return 0;
    +  } else if (t >= 1) {
    +    return 1;
    +  }
    +
    +  // Try gradient descent to solve for t. If it works, it is very fast.
    +  var tMin = 0;
    +  var tMax = 1;
    +  for (var i = 0; i < 8; i++) {
    +    var value = this.getPointX(t);
    +    var derivative = (this.getPointX(t + epsilon) - value) / epsilon;
    +    if (Math.abs(value - xVal) < epsilon) {
    +      return t;
    +    } else if (Math.abs(derivative) < epsilon) {
    +      break;
    +    } else {
    +      if (value < xVal) {
    +        tMin = t;
    +      } else {
    +        tMax = t;
    +      }
    +      t -= (value - xVal) / derivative;
    +    }
    +  }
    +
    +  // If the gradient descent got stuck in a local minimum, e.g. because
    +  // the derivative was close to 0, use a Dichotomy refinement instead.
    +  // We limit the number of interations to 8.
    +  for (var i = 0; Math.abs(value - xVal) > epsilon && i < 8; i++) {
    +    if (value < xVal) {
    +      tMin = t;
    +      t = (t + tMax) / 2;
    +    } else {
    +      tMax = t;
    +      t = (t + tMin) / 2;
    +    }
    +    value = this.getPointX(t);
    +  }
    +  return t;
    +};
    +
    +
    +/**
    + * Computes the y coordinate of a point on the curve given its x coordinate.
    + * @param {number} xVal The x coordinate of the point on the curve.
    + * @return {number} The y coordinate of the point on the curve.
    + */
    +goog.math.Bezier.prototype.solveYValueFromXValue = function(xVal) {
    +  return this.getPointY(this.solvePositionFromXValue(xVal));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/bezier_test.html b/mapbox-gl/closure-library/closure/goog/math/bezier_test.html
    new file mode 100644
    index 0000000000..17c50ac936
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/bezier_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Bezier
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.BezierTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/bezier_test.js b/mapbox-gl/closure-library/closure/goog/math/bezier_test.js
    new file mode 100644
    index 0000000000..662fc9c474
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/bezier_test.js
    @@ -0,0 +1,126 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.BezierTest');
    +goog.setTestOnly('goog.math.BezierTest');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.Bezier');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.jsunit');
    +
    +function testEquals() {
    +  var input = new goog.math.Bezier(1, 2, 3, 4, 5, 6, 7, 8);
    +
    +  assert(input.equals(input));
    +}
    +
    +function testClone() {
    +  var input = new goog.math.Bezier(1, 2, 3, 4, 5, 6, 7, 8);
    +
    +  assertNotEquals('Clone returns a new object', input, input.clone());
    +  assert('Contents of clone match original', input.equals(input.clone()));
    +}
    +
    +function testFlip() {
    +  var input = new goog.math.Bezier(1, 1, 2, 2, 3, 3, 4, 4);
    +  var compare = new goog.math.Bezier(4, 4, 3, 3, 2, 2, 1, 1);
    +
    +  var flipped = input.clone();
    +  flipped.flip();
    +  assert('Flipped behaves as expected', compare.equals(flipped));
    +
    +  flipped.flip();
    +  assert('Flipping twice gives original', input.equals(flipped));
    +}
    +
    +function testGetPoint() {
    +  var input = new goog.math.Bezier(0, 1, 1, 2, 2, 3, 3, 4);
    +
    +  assert(goog.math.Coordinate.equals(input.getPoint(0),
    +      new goog.math.Coordinate(0, 1)));
    +  assert(goog.math.Coordinate.equals(input.getPoint(1),
    +      new goog.math.Coordinate(3, 4)));
    +  assert(goog.math.Coordinate.equals(input.getPoint(0.5),
    +      new goog.math.Coordinate(1.5, 2.5)));
    +}
    +
    +function testGetPointX() {
    +  var input = new goog.math.Bezier(0, 1, 1, 2, 2, 3, 3, 4);
    +
    +  assert(goog.math.nearlyEquals(input.getPointX(0), 0));
    +  assert(goog.math.nearlyEquals(input.getPointX(1), 3));
    +  assert(goog.math.nearlyEquals(input.getPointX(0.5), 1.5));
    +}
    +
    +function testGetPointY() {
    +  var input = new goog.math.Bezier(0, 1, 1, 2, 2, 3, 3, 4);
    +
    +  assert(goog.math.nearlyEquals(input.getPointY(0), 1));
    +  assert(goog.math.nearlyEquals(input.getPointY(1), 4));
    +  assert(goog.math.nearlyEquals(input.getPointY(0.5), 2.5));
    +}
    +
    +function testSubdivide() {
    +  var input = new goog.math.Bezier(0, 1, 1, 2, 2, 3, 3, 4);
    +
    +  input.subdivide(1 / 3, 2 / 3);
    +
    +  assert(goog.math.nearlyEquals(1, input.x0));
    +  assert(goog.math.nearlyEquals(2, input.y0));
    +  assert(goog.math.nearlyEquals(2, input.x3));
    +  assert(goog.math.nearlyEquals(3, input.y3));
    +}
    +
    +function testSolvePositionFromXValue() {
    +  var eps = 1e-6;
    +  var bezier = new goog.math.Bezier(0, 0, 0.25, 0.1, 0.25, 1, 1, 1);
    +  var pt = bezier.getPoint(0.5);
    +  assertRoughlyEquals(0.3125, pt.x, eps);
    +  assertRoughlyEquals(0.5375, pt.y, eps);
    +  assertRoughlyEquals(0.321,
    +      bezier.solvePositionFromXValue(bezier.getPoint(0.321).x), eps);
    +}
    +
    +function testSolveYValueFromXValue() {
    +  var eps = 1e-6;
    +  // The following example is taken from
    +  // http://www.netzgesta.de/dev/cubic-bezier-timing-function.html.
    +  // The timing values shown in that page are 1 - <value> so the
    +  // bezier curves in this test are constructed with 1 - ctrl points.
    +  // E.g. ctrl points (0, 0, 0.25, 0.1, 0.25, 1, 1, 1) become
    +  // (1, 1, 0.75, 0, 0.75, 0.9, 0, 0) here. Since chanding the order of
    +  // the ctrl points does not affect the shape of the curve, once can also
    +  // have (0, 0, 0.75, 0.9, 0.75, 0, 1, 1).
    +
    +  // netzgesta example.
    +  var bezier = new goog.math.Bezier(1, 1, 0.75, 0.9, 0.75, 0, 0, 0);
    +  assertRoughlyEquals(0.024374631, bezier.solveYValueFromXValue(0.2), eps);
    +  assertRoughlyEquals(0.317459494, bezier.solveYValueFromXValue(0.6), eps);
    +  assertRoughlyEquals(0.905205002, bezier.solveYValueFromXValue(0.9), eps);
    +
    +  // netzgesta example with ctrl points in the reverse order so that 1st and
    +  // last ctrl points are (0, 0) and (1, 1). Note the result is exactly the
    +  // same.
    +  bezier = new goog.math.Bezier(0, 0, 0.75, 0, 0.75, 0.9, 1, 1);
    +  assertRoughlyEquals(0.024374631, bezier.solveYValueFromXValue(0.2), eps);
    +  assertRoughlyEquals(0.317459494, bezier.solveYValueFromXValue(0.6), eps);
    +  assertRoughlyEquals(0.905205002, bezier.solveYValueFromXValue(0.9), eps);
    +
    +  // Ease-out css animation timing in webkit.
    +  bezier = new goog.math.Bezier(0, 0, 0, 0, 0.58, 1, 1, 1);
    +  assertRoughlyEquals(0.308366667, bezier.solveYValueFromXValue(0.2), eps);
    +  assertRoughlyEquals(0.785139061, bezier.solveYValueFromXValue(0.6), eps);
    +  assertRoughlyEquals(0.982973389, bezier.solveYValueFromXValue(0.9), eps);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/box.js b/mapbox-gl/closure-library/closure/goog/math/box.js
    new file mode 100644
    index 0000000000..63ebc0dc92
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/box.js
    @@ -0,0 +1,389 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing a numeric box.
    + */
    +
    +
    +goog.provide('goog.math.Box');
    +
    +goog.require('goog.math.Coordinate');
    +
    +
    +
    +/**
    + * Class for representing a box. A box is specified as a top, right, bottom,
    + * and left. A box is useful for representing margins and padding.
    + *
    + * This class assumes 'screen coordinates': larger Y coordinates are further
    + * from the top of the screen.
    + *
    + * @param {number} top Top.
    + * @param {number} right Right.
    + * @param {number} bottom Bottom.
    + * @param {number} left Left.
    + * @struct
    + * @constructor
    + */
    +goog.math.Box = function(top, right, bottom, left) {
    +  /**
    +   * Top
    +   * @type {number}
    +   */
    +  this.top = top;
    +
    +  /**
    +   * Right
    +   * @type {number}
    +   */
    +  this.right = right;
    +
    +  /**
    +   * Bottom
    +   * @type {number}
    +   */
    +  this.bottom = bottom;
    +
    +  /**
    +   * Left
    +   * @type {number}
    +   */
    +  this.left = left;
    +};
    +
    +
    +/**
    + * Creates a Box by bounding a collection of goog.math.Coordinate objects
    + * @param {...goog.math.Coordinate} var_args Coordinates to be included inside
    + *     the box.
    + * @return {!goog.math.Box} A Box containing all the specified Coordinates.
    + */
    +goog.math.Box.boundingBox = function(var_args) {
    +  var box = new goog.math.Box(arguments[0].y, arguments[0].x,
    +                              arguments[0].y, arguments[0].x);
    +  for (var i = 1; i < arguments.length; i++) {
    +    var coord = arguments[i];
    +    box.top = Math.min(box.top, coord.y);
    +    box.right = Math.max(box.right, coord.x);
    +    box.bottom = Math.max(box.bottom, coord.y);
    +    box.left = Math.min(box.left, coord.x);
    +  }
    +  return box;
    +};
    +
    +
    +/**
    + * @return {number} width The width of this Box.
    + */
    +goog.math.Box.prototype.getWidth = function() {
    +  return this.right - this.left;
    +};
    +
    +
    +/**
    + * @return {number} height The height of this Box.
    + */
    +goog.math.Box.prototype.getHeight = function() {
    +  return this.bottom - this.top;
    +};
    +
    +
    +/**
    + * Creates a copy of the box with the same dimensions.
    + * @return {!goog.math.Box} A clone of this Box.
    + */
    +goog.math.Box.prototype.clone = function() {
    +  return new goog.math.Box(this.top, this.right, this.bottom, this.left);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a nice string representing the box.
    +   * @return {string} In the form (50t, 73r, 24b, 13l).
    +   * @override
    +   */
    +  goog.math.Box.prototype.toString = function() {
    +    return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
    +           this.left + 'l)';
    +  };
    +}
    +
    +
    +/**
    + * Returns whether the box contains a coordinate or another box.
    + *
    + * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    + * @return {boolean} Whether the box contains the coordinate or other box.
    + */
    +goog.math.Box.prototype.contains = function(other) {
    +  return goog.math.Box.contains(this, other);
    +};
    +
    +
    +/**
    + * Expands box with the given margins.
    + *
    + * @param {number|goog.math.Box} top Top margin or box with all margins.
    + * @param {number=} opt_right Right margin.
    + * @param {number=} opt_bottom Bottom margin.
    + * @param {number=} opt_left Left margin.
    + * @return {!goog.math.Box} A reference to this Box.
    + */
    +goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
    +    opt_left) {
    +  if (goog.isObject(top)) {
    +    this.top -= top.top;
    +    this.right += top.right;
    +    this.bottom += top.bottom;
    +    this.left -= top.left;
    +  } else {
    +    this.top -= top;
    +    this.right += opt_right;
    +    this.bottom += opt_bottom;
    +    this.left -= opt_left;
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Expand this box to include another box.
    + * NOTE(user): This is used in code that needs to be very fast, please don't
    + * add functionality to this function at the expense of speed (variable
    + * arguments, accepting multiple argument types, etc).
    + * @param {goog.math.Box} box The box to include in this one.
    + */
    +goog.math.Box.prototype.expandToInclude = function(box) {
    +  this.left = Math.min(this.left, box.left);
    +  this.top = Math.min(this.top, box.top);
    +  this.right = Math.max(this.right, box.right);
    +  this.bottom = Math.max(this.bottom, box.bottom);
    +};
    +
    +
    +/**
    + * Compares boxes for equality.
    + * @param {goog.math.Box} a A Box.
    + * @param {goog.math.Box} b A Box.
    + * @return {boolean} True iff the boxes are equal, or if both are null.
    + */
    +goog.math.Box.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.top == b.top && a.right == b.right &&
    +         a.bottom == b.bottom && a.left == b.left;
    +};
    +
    +
    +/**
    + * Returns whether a box contains a coordinate or another box.
    + *
    + * @param {goog.math.Box} box A Box.
    + * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
    + * @return {boolean} Whether the box contains the coordinate or other box.
    + */
    +goog.math.Box.contains = function(box, other) {
    +  if (!box || !other) {
    +    return false;
    +  }
    +
    +  if (other instanceof goog.math.Box) {
    +    return other.left >= box.left && other.right <= box.right &&
    +        other.top >= box.top && other.bottom <= box.bottom;
    +  }
    +
    +  // other is a Coordinate.
    +  return other.x >= box.left && other.x <= box.right &&
    +         other.y >= box.top && other.y <= box.bottom;
    +};
    +
    +
    +/**
    + * Returns the relative x position of a coordinate compared to a box.  Returns
    + * zero if the coordinate is inside the box.
    + *
    + * @param {goog.math.Box} box A Box.
    + * @param {goog.math.Coordinate} coord A Coordinate.
    + * @return {number} The x position of {@code coord} relative to the nearest
    + *     side of {@code box}, or zero if {@code coord} is inside {@code box}.
    + */
    +goog.math.Box.relativePositionX = function(box, coord) {
    +  if (coord.x < box.left) {
    +    return coord.x - box.left;
    +  } else if (coord.x > box.right) {
    +    return coord.x - box.right;
    +  }
    +  return 0;
    +};
    +
    +
    +/**
    + * Returns the relative y position of a coordinate compared to a box.  Returns
    + * zero if the coordinate is inside the box.
    + *
    + * @param {goog.math.Box} box A Box.
    + * @param {goog.math.Coordinate} coord A Coordinate.
    + * @return {number} The y position of {@code coord} relative to the nearest
    + *     side of {@code box}, or zero if {@code coord} is inside {@code box}.
    + */
    +goog.math.Box.relativePositionY = function(box, coord) {
    +  if (coord.y < box.top) {
    +    return coord.y - box.top;
    +  } else if (coord.y > box.bottom) {
    +    return coord.y - box.bottom;
    +  }
    +  return 0;
    +};
    +
    +
    +/**
    + * Returns the distance between a coordinate and the nearest corner/side of a
    + * box. Returns zero if the coordinate is inside the box.
    + *
    + * @param {goog.math.Box} box A Box.
    + * @param {goog.math.Coordinate} coord A Coordinate.
    + * @return {number} The distance between {@code coord} and the nearest
    + *     corner/side of {@code box}, or zero if {@code coord} is inside
    + *     {@code box}.
    + */
    +goog.math.Box.distance = function(box, coord) {
    +  var x = goog.math.Box.relativePositionX(box, coord);
    +  var y = goog.math.Box.relativePositionY(box, coord);
    +  return Math.sqrt(x * x + y * y);
    +};
    +
    +
    +/**
    + * Returns whether two boxes intersect.
    + *
    + * @param {goog.math.Box} a A Box.
    + * @param {goog.math.Box} b A second Box.
    + * @return {boolean} Whether the boxes intersect.
    + */
    +goog.math.Box.intersects = function(a, b) {
    +  return (a.left <= b.right && b.left <= a.right &&
    +          a.top <= b.bottom && b.top <= a.bottom);
    +};
    +
    +
    +/**
    + * Returns whether two boxes would intersect with additional padding.
    + *
    + * @param {goog.math.Box} a A Box.
    + * @param {goog.math.Box} b A second Box.
    + * @param {number} padding The additional padding.
    + * @return {boolean} Whether the boxes intersect.
    + */
    +goog.math.Box.intersectsWithPadding = function(a, b, padding) {
    +  return (a.left <= b.right + padding && b.left <= a.right + padding &&
    +          a.top <= b.bottom + padding && b.top <= a.bottom + padding);
    +};
    +
    +
    +/**
    + * Rounds the fields to the next larger integer values.
    + *
    + * @return {!goog.math.Box} This box with ceil'd fields.
    + */
    +goog.math.Box.prototype.ceil = function() {
    +  this.top = Math.ceil(this.top);
    +  this.right = Math.ceil(this.right);
    +  this.bottom = Math.ceil(this.bottom);
    +  this.left = Math.ceil(this.left);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the fields to the next smaller integer values.
    + *
    + * @return {!goog.math.Box} This box with floored fields.
    + */
    +goog.math.Box.prototype.floor = function() {
    +  this.top = Math.floor(this.top);
    +  this.right = Math.floor(this.right);
    +  this.bottom = Math.floor(this.bottom);
    +  this.left = Math.floor(this.left);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the fields to nearest integer values.
    + *
    + * @return {!goog.math.Box} This box with rounded fields.
    + */
    +goog.math.Box.prototype.round = function() {
    +  this.top = Math.round(this.top);
    +  this.right = Math.round(this.right);
    +  this.bottom = Math.round(this.bottom);
    +  this.left = Math.round(this.left);
    +  return this;
    +};
    +
    +
    +/**
    + * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
    + * is given, then the left and right values are translated by the coordinate's
    + * x value and the top and bottom values are translated by the coordinate's y
    + * value.  Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
    + * and y dimension values.
    + *
    + * @param {number|goog.math.Coordinate} tx The value to translate the x
    + *     dimension values by or the the coordinate to translate this box by.
    + * @param {number=} opt_ty The value to translate y dimension values by.
    + * @return {!goog.math.Box} This box after translating.
    + */
    +goog.math.Box.prototype.translate = function(tx, opt_ty) {
    +  if (tx instanceof goog.math.Coordinate) {
    +    this.left += tx.x;
    +    this.right += tx.x;
    +    this.top += tx.y;
    +    this.bottom += tx.y;
    +  } else {
    +    this.left += tx;
    +    this.right += tx;
    +    if (goog.isNumber(opt_ty)) {
    +      this.top += opt_ty;
    +      this.bottom += opt_ty;
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Scales this coordinate by the given scale factors. The x and y dimension
    + * values are scaled by {@code sx} and {@code opt_sy} respectively.
    + * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
    + *
    + * @param {number} sx The scale factor to use for the x dimension.
    + * @param {number=} opt_sy The scale factor to use for the y dimension.
    + * @return {!goog.math.Box} This box after scaling.
    + */
    +goog.math.Box.prototype.scale = function(sx, opt_sy) {
    +  var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    +  this.left *= sx;
    +  this.right *= sx;
    +  this.top *= sy;
    +  this.bottom *= sy;
    +  return this;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/box_test.html b/mapbox-gl/closure-library/closure/goog/math/box_test.html
    new file mode 100644
    index 0000000000..afc64390ae
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/box_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Box
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.BoxTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/box_test.js b/mapbox-gl/closure-library/closure/goog/math/box_test.js
    new file mode 100644
    index 0000000000..a33e85d56c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/box_test.js
    @@ -0,0 +1,321 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.BoxTest');
    +goog.setTestOnly('goog.math.BoxTest');
    +
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.jsunit');
    +
    +function testBoxEquals() {
    +  var a = new goog.math.Box(1, 2, 3, 4);
    +  var b = new goog.math.Box(1, 2, 3, 4);
    +  assertTrue(goog.math.Box.equals(a, a));
    +  assertTrue(goog.math.Box.equals(a, b));
    +  assertTrue(goog.math.Box.equals(b, a));
    +
    +  assertFalse('Box should not equal null.', goog.math.Box.equals(a, null));
    +  assertFalse('Box should not equal null.', goog.math.Box.equals(null, a));
    +
    +  assertFalse(goog.math.Box.equals(a, new goog.math.Box(4, 2, 3, 4)));
    +  assertFalse(goog.math.Box.equals(a, new goog.math.Box(1, 4, 3, 4)));
    +  assertFalse(goog.math.Box.equals(a, new goog.math.Box(1, 2, 4, 4)));
    +  assertFalse(goog.math.Box.equals(a, new goog.math.Box(1, 2, 3, 1)));
    +
    +  assertTrue('Null boxes should be equal.', goog.math.Box.equals(null, null));
    +}
    +
    +function testBoxClone() {
    +  var b = new goog.math.Box(0, 0, 0, 0);
    +  assertTrue(goog.math.Box.equals(b, b.clone()));
    +
    +  b.left = 0;
    +  b.top = 1;
    +  b.right = 2;
    +  b.bottom = 3;
    +  assertTrue(goog.math.Box.equals(b, b.clone()));
    +}
    +
    +function testBoxRelativePositionX() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  assertEquals(0,
    +      goog.math.Box.relativePositionX(box, new goog.math.Coordinate(75, 0)));
    +  assertEquals(0,
    +      goog.math.Box.relativePositionX(box, new goog.math.Coordinate(75, 75)));
    +  assertEquals(0,
    +      goog.math.Box.relativePositionX(box, new goog.math.Coordinate(75, 105)));
    +  assertEquals(-5,
    +      goog.math.Box.relativePositionX(box, new goog.math.Coordinate(45, 75)));
    +  assertEquals(5,
    +      goog.math.Box.relativePositionX(box, new goog.math.Coordinate(105, 75)));
    +}
    +
    +function testBoxRelativePositionY() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  assertEquals(0,
    +      goog.math.Box.relativePositionY(box, new goog.math.Coordinate(0, 75)));
    +  assertEquals(0,
    +      goog.math.Box.relativePositionY(box, new goog.math.Coordinate(75, 75)));
    +  assertEquals(0,
    +      goog.math.Box.relativePositionY(box, new goog.math.Coordinate(105, 75)));
    +  assertEquals(-5,
    +      goog.math.Box.relativePositionY(box, new goog.math.Coordinate(75, 45)));
    +  assertEquals(5,
    +      goog.math.Box.relativePositionY(box, new goog.math.Coordinate(75, 105)));
    +}
    +
    +function testBoxDistance() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  assertEquals(0,
    +               goog.math.Box.distance(box, new goog.math.Coordinate(75, 75)));
    +  assertEquals(25,
    +               goog.math.Box.distance(box, new goog.math.Coordinate(75, 25)));
    +  assertEquals(10,
    +               goog.math.Box.distance(box, new goog.math.Coordinate(40, 80)));
    +  assertEquals(5,
    +               goog.math.Box.distance(box, new goog.math.Coordinate(46, 47)));
    +  assertEquals(10,
    +               goog.math.Box.distance(box, new goog.math.Coordinate(106, 108)));
    +}
    +
    +function testBoxContains() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  assertTrue(goog.math.Box.contains(box, new goog.math.Coordinate(75, 75)));
    +  assertTrue(goog.math.Box.contains(box, new goog.math.Coordinate(50, 100)));
    +  assertTrue(goog.math.Box.contains(box, new goog.math.Coordinate(100, 99)));
    +  assertFalse(goog.math.Box.contains(box, new goog.math.Coordinate(100, 101)));
    +  assertFalse(goog.math.Box.contains(box, new goog.math.Coordinate(49, 50)));
    +  assertFalse(goog.math.Box.contains(box, new goog.math.Coordinate(25, 25)));
    +}
    +
    +function testBoxContainsBox() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  function assertContains(boxB) {
    +    assertTrue(box + ' expected to contain ' + boxB,
    +        goog.math.Box.contains(box, boxB));
    +  }
    +
    +  function assertNotContains(boxB) {
    +    assertFalse(box + ' expected to not contain ' + boxB,
    +        goog.math.Box.contains(box, boxB));
    +  }
    +
    +  assertContains(new goog.math.Box(60, 90, 90, 60));
    +  assertNotContains(new goog.math.Box(1, 3, 4, 2));
    +  assertNotContains(new goog.math.Box(30, 90, 60, 60));
    +  assertNotContains(new goog.math.Box(60, 110, 60, 60));
    +  assertNotContains(new goog.math.Box(60, 90, 110, 60));
    +  assertNotContains(new goog.math.Box(60, 90, 60, 40));
    +}
    +
    +function testBoxesIntersect() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  function assertIntersects(boxB) {
    +    assertTrue(box + ' expected to intersect ' + boxB,
    +        goog.math.Box.intersects(box, boxB));
    +  }
    +  function assertNotIntersects(boxB) {
    +    assertFalse(box + ' expected to not intersect ' + boxB,
    +        goog.math.Box.intersects(box, boxB));
    +  }
    +
    +  assertIntersects(box);
    +  assertIntersects(new goog.math.Box(20, 80, 80, 20));
    +  assertIntersects(new goog.math.Box(50, 80, 100, 20));
    +  assertIntersects(new goog.math.Box(80, 80, 120, 20));
    +  assertIntersects(new goog.math.Box(20, 100, 80, 50));
    +  assertIntersects(new goog.math.Box(80, 100, 120, 50));
    +  assertIntersects(new goog.math.Box(20, 120, 80, 80));
    +  assertIntersects(new goog.math.Box(50, 120, 100, 80));
    +  assertIntersects(new goog.math.Box(80, 120, 120, 80));
    +  assertIntersects(new goog.math.Box(20, 120, 120, 20));
    +  assertIntersects(new goog.math.Box(70, 80, 80, 70));
    +  assertNotIntersects(new goog.math.Box(10, 30, 30, 10));
    +  assertNotIntersects(new goog.math.Box(10, 70, 30, 30));
    +  assertNotIntersects(new goog.math.Box(10, 100, 30, 50));
    +  assertNotIntersects(new goog.math.Box(10, 120, 30, 80));
    +  assertNotIntersects(new goog.math.Box(10, 140, 30, 120));
    +  assertNotIntersects(new goog.math.Box(30, 30, 70, 10));
    +  assertNotIntersects(new goog.math.Box(30, 140, 70, 120));
    +  assertNotIntersects(new goog.math.Box(50, 30, 100, 10));
    +  assertNotIntersects(new goog.math.Box(50, 140, 100, 120));
    +  assertNotIntersects(new goog.math.Box(80, 30, 120, 10));
    +  assertNotIntersects(new goog.math.Box(80, 140, 120, 120));
    +  assertNotIntersects(new goog.math.Box(120, 30, 140, 10));
    +  assertNotIntersects(new goog.math.Box(120, 70, 140, 30));
    +  assertNotIntersects(new goog.math.Box(120, 100, 140, 50));
    +  assertNotIntersects(new goog.math.Box(120, 120, 140, 80));
    +  assertNotIntersects(new goog.math.Box(120, 140, 140, 120));
    +}
    +
    +function testBoxesIntersectWithPadding() {
    +  var box = new goog.math.Box(50, 100, 100, 50);
    +
    +  function assertIntersects(boxB, padding) {
    +    assertTrue(box + ' expected to intersect ' + boxB + ' with padding ' +
    +        padding, goog.math.Box.intersectsWithPadding(box, boxB, padding));
    +  }
    +  function assertNotIntersects(boxB, padding) {
    +    assertFalse(box + ' expected to not intersect ' + boxB + ' with padding ' +
    +        padding, goog.math.Box.intersectsWithPadding(box, boxB, padding));
    +  }
    +
    +  assertIntersects(box, 10);
    +  assertIntersects(new goog.math.Box(20, 80, 80, 20), 10);
    +  assertIntersects(new goog.math.Box(50, 80, 100, 20), 10);
    +  assertIntersects(new goog.math.Box(80, 80, 120, 20), 10);
    +  assertIntersects(new goog.math.Box(20, 100, 80, 50), 10);
    +  assertIntersects(new goog.math.Box(80, 100, 120, 50), 10);
    +  assertIntersects(new goog.math.Box(20, 120, 80, 80), 10);
    +  assertIntersects(new goog.math.Box(50, 120, 100, 80), 10);
    +  assertIntersects(new goog.math.Box(80, 120, 120, 80), 10);
    +  assertIntersects(new goog.math.Box(20, 120, 120, 20), 10);
    +  assertIntersects(new goog.math.Box(70, 80, 80, 70), 10);
    +  assertIntersects(new goog.math.Box(10, 30, 30, 10), 20);
    +  assertIntersects(new goog.math.Box(10, 70, 30, 30), 20);
    +  assertIntersects(new goog.math.Box(10, 100, 30, 50), 20);
    +  assertIntersects(new goog.math.Box(10, 120, 30, 80), 20);
    +  assertIntersects(new goog.math.Box(10, 140, 30, 120), 20);
    +  assertIntersects(new goog.math.Box(30, 30, 70, 10), 20);
    +  assertIntersects(new goog.math.Box(30, 140, 70, 120), 20);
    +  assertIntersects(new goog.math.Box(50, 30, 100, 10), 20);
    +  assertIntersects(new goog.math.Box(50, 140, 100, 120), 20);
    +  assertIntersects(new goog.math.Box(80, 30, 120, 10), 20);
    +  assertIntersects(new goog.math.Box(80, 140, 120, 120), 20);
    +  assertIntersects(new goog.math.Box(120, 30, 140, 10), 20);
    +  assertIntersects(new goog.math.Box(120, 70, 140, 30), 20);
    +  assertIntersects(new goog.math.Box(120, 100, 140, 50), 20);
    +  assertIntersects(new goog.math.Box(120, 120, 140, 80), 20);
    +  assertIntersects(new goog.math.Box(120, 140, 140, 120), 20);
    +  assertNotIntersects(new goog.math.Box(10, 30, 30, 10), 10);
    +  assertNotIntersects(new goog.math.Box(10, 70, 30, 30), 10);
    +  assertNotIntersects(new goog.math.Box(10, 100, 30, 50), 10);
    +  assertNotIntersects(new goog.math.Box(10, 120, 30, 80), 10);
    +  assertNotIntersects(new goog.math.Box(10, 140, 30, 120), 10);
    +  assertNotIntersects(new goog.math.Box(30, 30, 70, 10), 10);
    +  assertNotIntersects(new goog.math.Box(30, 140, 70, 120), 10);
    +  assertNotIntersects(new goog.math.Box(50, 30, 100, 10), 10);
    +  assertNotIntersects(new goog.math.Box(50, 140, 100, 120), 10);
    +  assertNotIntersects(new goog.math.Box(80, 30, 120, 10), 10);
    +  assertNotIntersects(new goog.math.Box(80, 140, 120, 120), 10);
    +  assertNotIntersects(new goog.math.Box(120, 30, 140, 10), 10);
    +  assertNotIntersects(new goog.math.Box(120, 70, 140, 30), 10);
    +  assertNotIntersects(new goog.math.Box(120, 100, 140, 50), 10);
    +  assertNotIntersects(new goog.math.Box(120, 120, 140, 80), 10);
    +  assertNotIntersects(new goog.math.Box(120, 140, 140, 120), 10);
    +}
    +
    +function testExpandToInclude() {
    +  var box = new goog.math.Box(10, 50, 50, 10);
    +  box.expandToInclude(new goog.math.Box(60, 70, 70, 60));
    +  assertEquals(10, box.left);
    +  assertEquals(10, box.top);
    +  assertEquals(70, box.right);
    +  assertEquals(70, box.bottom);
    +  box.expandToInclude(new goog.math.Box(30, 40, 40, 30));
    +  assertEquals(10, box.left);
    +  assertEquals(10, box.top);
    +  assertEquals(70, box.right);
    +  assertEquals(70, box.bottom);
    +  box.expandToInclude(new goog.math.Box(0, 100, 100, 0));
    +  assertEquals(0, box.left);
    +  assertEquals(0, box.top);
    +  assertEquals(100, box.right);
    +  assertEquals(100, box.bottom);
    +}
    +
    +function testGetWidth() {
    +  var box = new goog.math.Box(10, 50, 30, 25);
    +  assertEquals(25, box.getWidth());
    +}
    +
    +function testGetHeight() {
    +  var box = new goog.math.Box(10, 50, 30, 25);
    +  assertEquals(20, box.getHeight());
    +}
    +
    +function testBoundingBox() {
    +  assertObjectEquals(
    +      new goog.math.Box(1, 10, 11, 0),
    +      goog.math.Box.boundingBox(
    +          new goog.math.Coordinate(5, 5),
    +          new goog.math.Coordinate(5, 11),
    +          new goog.math.Coordinate(0, 5),
    +          new goog.math.Coordinate(5, 1),
    +          new goog.math.Coordinate(10, 5)));
    +}
    +
    +function testBoxCeil() {
    +  var box = new goog.math.Box(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      box, box.ceil());
    +  assertObjectEquals(new goog.math.Box(12, 27, 18, 10), box);
    +}
    +
    +function testBoxFloor() {
    +  var box = new goog.math.Box(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      box, box.floor());
    +  assertObjectEquals(new goog.math.Box(11, 26, 17, 9), box);
    +}
    +
    +function testBoxRound() {
    +  var box = new goog.math.Box(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      box, box.round());
    +  assertObjectEquals(new goog.math.Box(11, 27, 18, 9), box);
    +}
    +
    +function testBoxTranslateCoordinate() {
    +  var box = new goog.math.Box(10, 30, 20, 5);
    +  var c = new goog.math.Coordinate(10, 5);
    +  assertEquals('The function should return the target instance',
    +      box, box.translate(c));
    +  assertObjectEquals(new goog.math.Box(15, 40, 25, 15), box);
    +}
    +
    +function testBoxTranslateXY() {
    +  var box = new goog.math.Box(10, 30, 20, 5);
    +  assertEquals('The function should return the target instance',
    +      box, box.translate(5, 2));
    +  assertObjectEquals(new goog.math.Box(12, 35, 22, 10), box);
    +}
    +
    +function testBoxTranslateX() {
    +  var box = new goog.math.Box(10, 30, 20, 5);
    +  assertEquals('The function should return the target instance',
    +      box, box.translate(3));
    +  assertObjectEquals(new goog.math.Box(10, 33, 20, 8), box);
    +}
    +
    +function testBoxScaleXY() {
    +  var box = new goog.math.Box(10, 20, 30, 5);
    +  assertEquals('The function should return the target instance',
    +      box, box.scale(2, 3));
    +  assertObjectEquals(new goog.math.Box(30, 40, 90, 10), box);
    +}
    +
    +function testBoxScaleFactor() {
    +  var box = new goog.math.Box(10, 20, 30, 5);
    +  assertEquals('The function should return the target instance',
    +      box, box.scale(2));
    +  assertObjectEquals(new goog.math.Box(20, 40, 60, 10), box);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate.js b/mapbox-gl/closure-library/closure/goog/math/coordinate.js
    new file mode 100644
    index 0000000000..798d3851ac
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate.js
    @@ -0,0 +1,268 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing two-dimensional positions.
    + */
    +
    +
    +goog.provide('goog.math.Coordinate');
    +
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Class for representing coordinates and positions.
    + * @param {number=} opt_x Left, defaults to 0.
    + * @param {number=} opt_y Top, defaults to 0.
    + * @struct
    + * @constructor
    + */
    +goog.math.Coordinate = function(opt_x, opt_y) {
    +  /**
    +   * X-value
    +   * @type {number}
    +   */
    +  this.x = goog.isDef(opt_x) ? opt_x : 0;
    +
    +  /**
    +   * Y-value
    +   * @type {number}
    +   */
    +  this.y = goog.isDef(opt_y) ? opt_y : 0;
    +};
    +
    +
    +/**
    + * Returns a new copy of the coordinate.
    + * @return {!goog.math.Coordinate} A clone of this coordinate.
    + */
    +goog.math.Coordinate.prototype.clone = function() {
    +  return new goog.math.Coordinate(this.x, this.y);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a nice string representing the coordinate.
    +   * @return {string} In the form (50, 73).
    +   * @override
    +   */
    +  goog.math.Coordinate.prototype.toString = function() {
    +    return '(' + this.x + ', ' + this.y + ')';
    +  };
    +}
    +
    +
    +/**
    + * Compares coordinates for equality.
    + * @param {goog.math.Coordinate} a A Coordinate.
    + * @param {goog.math.Coordinate} b A Coordinate.
    + * @return {boolean} True iff the coordinates are equal, or if both are null.
    + */
    +goog.math.Coordinate.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.x == b.x && a.y == b.y;
    +};
    +
    +
    +/**
    + * Returns the distance between two coordinates.
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @param {!goog.math.Coordinate} b A Coordinate.
    + * @return {number} The distance between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate.distance = function(a, b) {
    +  var dx = a.x - b.x;
    +  var dy = a.y - b.y;
    +  return Math.sqrt(dx * dx + dy * dy);
    +};
    +
    +
    +/**
    + * Returns the magnitude of a coordinate.
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @return {number} The distance between the origin and {@code a}.
    + */
    +goog.math.Coordinate.magnitude = function(a) {
    +  return Math.sqrt(a.x * a.x + a.y * a.y);
    +};
    +
    +
    +/**
    + * Returns the angle from the origin to a coordinate.
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @return {number} The angle, in degrees, clockwise from the positive X
    + *     axis to {@code a}.
    + */
    +goog.math.Coordinate.azimuth = function(a) {
    +  return goog.math.angle(0, 0, a.x, a.y);
    +};
    +
    +
    +/**
    + * Returns the squared distance between two coordinates. Squared distances can
    + * be used for comparisons when the actual value is not required.
    + *
    + * Performance note: eliminating the square root is an optimization often used
    + * in lower-level languages, but the speed difference is not nearly as
    + * pronounced in JavaScript (only a few percent.)
    + *
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @param {!goog.math.Coordinate} b A Coordinate.
    + * @return {number} The squared distance between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate.squaredDistance = function(a, b) {
    +  var dx = a.x - b.x;
    +  var dy = a.y - b.y;
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * Returns the difference between two coordinates as a new
    + * goog.math.Coordinate.
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @param {!goog.math.Coordinate} b A Coordinate.
    + * @return {!goog.math.Coordinate} A Coordinate representing the difference
    + *     between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate.difference = function(a, b) {
    +  return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
    +};
    +
    +
    +/**
    + * Returns the sum of two coordinates as a new goog.math.Coordinate.
    + * @param {!goog.math.Coordinate} a A Coordinate.
    + * @param {!goog.math.Coordinate} b A Coordinate.
    + * @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
    + *     coordinates.
    + */
    +goog.math.Coordinate.sum = function(a, b) {
    +  return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
    +};
    +
    +
    +/**
    + * Rounds the x and y fields to the next larger integer values.
    + * @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
    + */
    +goog.math.Coordinate.prototype.ceil = function() {
    +  this.x = Math.ceil(this.x);
    +  this.y = Math.ceil(this.y);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the x and y fields to the next smaller integer values.
    + * @return {!goog.math.Coordinate} This coordinate with floored fields.
    + */
    +goog.math.Coordinate.prototype.floor = function() {
    +  this.x = Math.floor(this.x);
    +  this.y = Math.floor(this.y);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the x and y fields to the nearest integer values.
    + * @return {!goog.math.Coordinate} This coordinate with rounded fields.
    + */
    +goog.math.Coordinate.prototype.round = function() {
    +  this.x = Math.round(this.x);
    +  this.y = Math.round(this.y);
    +  return this;
    +};
    +
    +
    +/**
    + * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
    + * is given, then the x and y values are translated by the coordinate's x and y.
    + * Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
    + * respectively.
    + * @param {number|goog.math.Coordinate} tx The value to translate x by or the
    + *     the coordinate to translate this coordinate by.
    + * @param {number=} opt_ty The value to translate y by.
    + * @return {!goog.math.Coordinate} This coordinate after translating.
    + */
    +goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
    +  if (tx instanceof goog.math.Coordinate) {
    +    this.x += tx.x;
    +    this.y += tx.y;
    +  } else {
    +    this.x += tx;
    +    if (goog.isNumber(opt_ty)) {
    +      this.y += opt_ty;
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Scales this coordinate by the given scale factors. The x and y values are
    + * scaled by {@code sx} and {@code opt_sy} respectively.  If {@code opt_sy}
    + * is not given, then {@code sx} is used for both x and y.
    + * @param {number} sx The scale factor to use for the x dimension.
    + * @param {number=} opt_sy The scale factor to use for the y dimension.
    + * @return {!goog.math.Coordinate} This coordinate after scaling.
    + */
    +goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
    +  var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    +  this.x *= sx;
    +  this.y *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Rotates this coordinate clockwise about the origin (or, optionally, the given
    + * center) by the given angle, in radians.
    + * @param {number} radians The angle by which to rotate this coordinate
    + *     clockwise about the given center, in radians.
    + * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
    + *     to (0, 0) if not given.
    + */
    +goog.math.Coordinate.prototype.rotateRadians = function(radians, opt_center) {
    +  var center = opt_center || new goog.math.Coordinate(0, 0);
    +
    +  var x = this.x;
    +  var y = this.y;
    +  var cos = Math.cos(radians);
    +  var sin = Math.sin(radians);
    +
    +  this.x = (x - center.x) * cos - (y - center.y) * sin + center.x;
    +  this.y = (x - center.x) * sin + (y - center.y) * cos + center.y;
    +};
    +
    +
    +/**
    + * Rotates this coordinate clockwise about the origin (or, optionally, the given
    + * center) by the given angle, in degrees.
    + * @param {number} degrees The angle by which to rotate this coordinate
    + *     clockwise about the given center, in degrees.
    + * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
    + *     to (0, 0) if not given.
    + */
    +goog.math.Coordinate.prototype.rotateDegrees = function(degrees, opt_center) {
    +  this.rotateRadians(goog.math.toRadians(degrees), opt_center);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate3.js b/mapbox-gl/closure-library/closure/goog/math/coordinate3.js
    new file mode 100644
    index 0000000000..04a5a69af2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate3.js
    @@ -0,0 +1,170 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing three-dimensional points.
    + *
    + * Based heavily on coordinate.js by:
    + */
    +
    +goog.provide('goog.math.Coordinate3');
    +
    +
    +
    +/**
    + * Class for representing coordinates and positions in 3 dimensions.
    + *
    + * @param {number=} opt_x X coordinate, defaults to 0.
    + * @param {number=} opt_y Y coordinate, defaults to 0.
    + * @param {number=} opt_z Z coordinate, defaults to 0.
    + * @struct
    + * @constructor
    + */
    +goog.math.Coordinate3 = function(opt_x, opt_y, opt_z) {
    +  /**
    +   * X-value
    +   * @type {number}
    +   */
    +  this.x = goog.isDef(opt_x) ? opt_x : 0;
    +
    +  /**
    +   * Y-value
    +   * @type {number}
    +   */
    +  this.y = goog.isDef(opt_y) ? opt_y : 0;
    +
    +  /**
    +   * Z-value
    +   * @type {number}
    +   */
    +  this.z = goog.isDef(opt_z) ? opt_z : 0;
    +};
    +
    +
    +/**
    + * Returns a new copy of the coordinate.
    + *
    + * @return {!goog.math.Coordinate3} A clone of this coordinate.
    + */
    +goog.math.Coordinate3.prototype.clone = function() {
    +  return new goog.math.Coordinate3(this.x, this.y, this.z);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a nice string representing the coordinate.
    +   *
    +   * @return {string} In the form (50, 73, 31).
    +   * @override
    +   */
    +  goog.math.Coordinate3.prototype.toString = function() {
    +    return '(' + this.x + ', ' + this.y + ', ' + this.z + ')';
    +  };
    +}
    +
    +
    +/**
    + * Compares coordinates for equality.
    + *
    + * @param {goog.math.Coordinate3} a A Coordinate3.
    + * @param {goog.math.Coordinate3} b A Coordinate3.
    + * @return {boolean} True iff the coordinates are equal, or if both are null.
    + */
    +goog.math.Coordinate3.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.x == b.x && a.y == b.y && a.z == b.z;
    +};
    +
    +
    +/**
    + * Returns the distance between two coordinates.
    + *
    + * @param {goog.math.Coordinate3} a A Coordinate3.
    + * @param {goog.math.Coordinate3} b A Coordinate3.
    + * @return {number} The distance between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate3.distance = function(a, b) {
    +  var dx = a.x - b.x;
    +  var dy = a.y - b.y;
    +  var dz = a.z - b.z;
    +  return Math.sqrt(dx * dx + dy * dy + dz * dz);
    +};
    +
    +
    +/**
    + * Returns the squared distance between two coordinates. Squared distances can
    + * be used for comparisons when the actual value is not required.
    + *
    + * Performance note: eliminating the square root is an optimization often used
    + * in lower-level languages, but the speed difference is not nearly as
    + * pronounced in JavaScript (only a few percent.)
    + *
    + * @param {goog.math.Coordinate3} a A Coordinate3.
    + * @param {goog.math.Coordinate3} b A Coordinate3.
    + * @return {number} The squared distance between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate3.squaredDistance = function(a, b) {
    +  var dx = a.x - b.x;
    +  var dy = a.y - b.y;
    +  var dz = a.z - b.z;
    +  return dx * dx + dy * dy + dz * dz;
    +};
    +
    +
    +/**
    + * Returns the difference between two coordinates as a new
    + * goog.math.Coordinate3.
    + *
    + * @param {goog.math.Coordinate3} a A Coordinate3.
    + * @param {goog.math.Coordinate3} b A Coordinate3.
    + * @return {!goog.math.Coordinate3} A Coordinate3 representing the difference
    + *     between {@code a} and {@code b}.
    + */
    +goog.math.Coordinate3.difference = function(a, b) {
    +  return new goog.math.Coordinate3(a.x - b.x, a.y - b.y, a.z - b.z);
    +};
    +
    +
    +/**
    + * Returns the contents of this coordinate as a 3 value Array.
    + *
    + * @return {!Array<number>} A new array.
    + */
    +goog.math.Coordinate3.prototype.toArray = function() {
    +  return [this.x, this.y, this.z];
    +};
    +
    +
    +/**
    + * Converts a three element array into a Coordinate3 object.  If the value
    + * passed in is not an array, not array-like, or not of the right length, an
    + * error is thrown.
    + *
    + * @param {Array<number>} a Array of numbers to become a coordinate.
    + * @return {!goog.math.Coordinate3} A new coordinate from the array values.
    + * @throws {Error} When the oject passed in is not valid.
    + */
    +goog.math.Coordinate3.fromArray = function(a) {
    +  if (a.length <= 3) {
    +    return new goog.math.Coordinate3(a[0], a[1], a[2]);
    +  }
    +
    +  throw Error('Conversion from an array requires an array of length 3');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.html b/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.html
    new file mode 100644
    index 0000000000..5e37a62c0c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  Coordinate3 Unit Tests
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Coordinate3
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.Coordinate3Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.js b/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.js
    new file mode 100644
    index 0000000000..27d5a2a9b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate3_test.js
    @@ -0,0 +1,196 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.Coordinate3Test');
    +goog.setTestOnly('goog.math.Coordinate3Test');
    +
    +goog.require('goog.math.Coordinate3');
    +goog.require('goog.testing.jsunit');
    +
    +function assertCoordinate3Equals(a, b) {
    +  assertTrue(b + ' should be equal to ' + a,
    +             goog.math.Coordinate3.equals(a, b));
    +}
    +
    +
    +function testCoordinate3MissingXYZ() {
    +  var noXYZ = new goog.math.Coordinate3();
    +  assertEquals(0, noXYZ.x);
    +  assertEquals(0, noXYZ.y);
    +  assertEquals(0, noXYZ.z);
    +  assertCoordinate3Equals(noXYZ, new goog.math.Coordinate3());
    +}
    +
    +
    +function testCoordinate3MissingYZ() {
    +  var noYZ = new goog.math.Coordinate3(10);
    +  assertEquals(10, noYZ.x);
    +  assertEquals(0, noYZ.y);
    +  assertEquals(0, noYZ.z);
    +  assertCoordinate3Equals(noYZ, new goog.math.Coordinate3(10));
    +}
    +
    +
    +function testCoordinate3MissingZ() {
    +  var noZ = new goog.math.Coordinate3(10, 20);
    +  assertEquals(10, noZ.x);
    +  assertEquals(20, noZ.y);
    +  assertEquals(0, noZ.z);
    +  assertCoordinate3Equals(noZ, new goog.math.Coordinate3(10, 20));
    +}
    +
    +
    +function testCoordinate3IntegerValues() {
    +  var intCoord = new goog.math.Coordinate3(10, 20, -19);
    +  assertEquals(10, intCoord.x);
    +  assertEquals(20, intCoord.y);
    +  assertEquals(-19, intCoord.z);
    +  assertCoordinate3Equals(intCoord, new goog.math.Coordinate3(10, 20, -19));
    +}
    +
    +
    +function testCoordinate3FloatValues() {
    +  var floatCoord = new goog.math.Coordinate3(10.5, 20.897, -71.385);
    +  assertEquals(10.5, floatCoord.x);
    +  assertEquals(20.897, floatCoord.y);
    +  assertEquals(-71.385, floatCoord.z);
    +  assertCoordinate3Equals(floatCoord,
    +      new goog.math.Coordinate3(10.5, 20.897, -71.385));
    +}
    +
    +
    +function testCoordinate3OneNonNumericValue() {
    +  var dim5 = new goog.math.Coordinate3('ten', 1000, 85);
    +  assertTrue(isNaN(dim5.x));
    +  assertEquals(1000, dim5.y);
    +  assertEquals(85, dim5.z);
    +}
    +
    +
    +function testCoordinate3AllNonNumericValues() {
    +  var nonNumeric = new goog.math.Coordinate3('ten',
    +                                             {woop: 'test'},
    +                                             Math.sqrt(-1));
    +  assertTrue(isNaN(nonNumeric.x));
    +  assertTrue(isNaN(nonNumeric.y));
    +  assertTrue(isNaN(nonNumeric.z));
    +}
    +
    +
    +function testCoordinate3Origin() {
    +  var origin = new goog.math.Coordinate3(0, 0, 0);
    +  assertEquals(0, origin.x);
    +  assertEquals(0, origin.y);
    +  assertEquals(0, origin.z);
    +  assertCoordinate3Equals(origin, new goog.math.Coordinate3(0, 0, 0));
    +}
    +
    +
    +function testCoordinate3Clone() {
    +  var c = new goog.math.Coordinate3();
    +  assertCoordinate3Equals(c, c.clone());
    +  c.x = -12;
    +  c.y = 13;
    +  c.z = 5;
    +  assertCoordinate3Equals(c, c.clone());
    +}
    +
    +
    +function testToString() {
    +  assertEquals('(0, 0, 0)', new
    +               goog.math.Coordinate3().toString());
    +  assertEquals('(1, 0, 0)', new
    +               goog.math.Coordinate3(1).toString());
    +  assertEquals('(1, 2, 0)', new
    +               goog.math.Coordinate3(1, 2).toString());
    +  assertEquals('(0, 0, 0)', new goog.math.Coordinate3(0, 0, 0).toString());
    +  assertEquals('(1, 2, 3)', new goog.math.Coordinate3(1, 2, 3).toString());
    +  assertEquals('(-4, 5, -3)', new goog.math.Coordinate3(-4, 5, -3).toString());
    +  assertEquals('(11.25, -71.935, 2.8)',
    +               new goog.math.Coordinate3(11.25, -71.935, 2.8).toString());
    +}
    +
    +
    +function testEquals() {
    +  var a = new goog.math.Coordinate3(3, 4, 5);
    +  var b = new goog.math.Coordinate3(3, 4, 5);
    +  var c = new goog.math.Coordinate3(-3, 4, -5);
    +
    +  assertTrue(goog.math.Coordinate3.equals(null, null));
    +  assertFalse(goog.math.Coordinate3.equals(a, null));
    +  assertTrue(goog.math.Coordinate3.equals(a, a));
    +  assertTrue(goog.math.Coordinate3.equals(a, b));
    +  assertFalse(goog.math.Coordinate3.equals(a, c));
    +}
    +
    +
    +function testCoordinate3Distance() {
    +  var a = new goog.math.Coordinate3(-2, -3, 1);
    +  var b = new goog.math.Coordinate3(2, 0, 1);
    +  assertEquals(5, goog.math.Coordinate3.distance(a, b));
    +}
    +
    +
    +function testCoordinate3SquaredDistance() {
    +  var a = new goog.math.Coordinate3(7, 11, 1);
    +  var b = new goog.math.Coordinate3(3, -1, 1);
    +  assertEquals(160, goog.math.Coordinate3.squaredDistance(a, b));
    +}
    +
    +
    +function testCoordinate3Difference() {
    +  var a = new goog.math.Coordinate3(7, 11, 1);
    +  var b = new goog.math.Coordinate3(3, -1, 1);
    +  assertCoordinate3Equals(goog.math.Coordinate3.difference(a, b),
    +                          new goog.math.Coordinate3(4, 12, 0));
    +}
    +
    +
    +function testToArray() {
    +  var a = new goog.math.Coordinate3(7, 11, 1);
    +  var b = a.toArray();
    +  assertEquals(b.length, 3);
    +  assertEquals(b[0], 7);
    +  assertEquals(b[1], 11);
    +  assertEquals(b[2], 1);
    +
    +  var c = new goog.math.Coordinate3('abc', 'def', 'xyz');
    +  var result = c.toArray();
    +  assertTrue(isNaN(result[0]));
    +  assertTrue(isNaN(result[1]));
    +  assertTrue(isNaN(result[2]));
    +}
    +
    +
    +function testFromArray() {
    +  var a = [1, 2, 3];
    +  var b = goog.math.Coordinate3.fromArray(a);
    +  assertEquals('(1, 2, 3)', b.toString());
    +
    +  var c = [1, 2];
    +  var d = goog.math.Coordinate3.fromArray(c);
    +  assertEquals('(1, 2, 0)', d.toString());
    +
    +  var e = [1];
    +  var f = goog.math.Coordinate3.fromArray(e);
    +  assertEquals('(1, 0, 0)', f.toString());
    +
    +  var g = [];
    +  var h = goog.math.Coordinate3.fromArray(g);
    +  assertEquals('(0, 0, 0)', h.toString());
    +
    +  var tooLong = [1, 2, 3, 4, 5, 6];
    +  assertThrows('Error should be thrown attempting to convert an invalid type.',
    +      goog.partial(goog.math.Coordinate3.fromArray, tooLong));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate_test.html b/mapbox-gl/closure-library/closure/goog/math/coordinate_test.html
    new file mode 100644
    index 0000000000..2c9598d645
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Coordinate
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.CoordinateTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/coordinate_test.js b/mapbox-gl/closure-library/closure/goog/math/coordinate_test.js
    new file mode 100644
    index 0000000000..e802bdc8db
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/coordinate_test.js
    @@ -0,0 +1,170 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.CoordinateTest');
    +goog.setTestOnly('goog.math.CoordinateTest');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.jsunit');
    +
    +function testCoordinate1() {
    +  var dim1 = new goog.math.Coordinate();
    +  assertEquals(0, dim1.x);
    +  assertEquals(0, dim1.y);
    +  assertEquals('(0, 0)', dim1.toString());
    +}
    +
    +function testCoordinate2() {
    +  var dim2 = new goog.math.Coordinate(10);
    +  assertEquals(10, dim2.x);
    +  assertEquals(0, dim2.y);
    +  assertEquals('(10, 0)', dim2.toString());
    +}
    +
    +function testCoordinate3() {
    +  var dim3 = new goog.math.Coordinate(10, 20);
    +  assertEquals(10, dim3.x);
    +  assertEquals(20, dim3.y);
    +  assertEquals('(10, 20)', dim3.toString());
    +}
    +
    +function testCoordinate4() {
    +  var dim4 = new goog.math.Coordinate(10.5, 20.897);
    +  assertEquals(10.5, dim4.x);
    +  assertEquals(20.897, dim4.y);
    +  assertEquals('(10.5, 20.897)', dim4.toString());
    +}
    +
    +function testCoordinate5() {
    +  var dim5 = new goog.math.Coordinate(NaN, 1000);
    +  assertTrue(isNaN(dim5.x));
    +  assertEquals(1000, dim5.y);
    +  assertEquals('(NaN, 1000)', dim5.toString());
    +}
    +
    +function testCoordinateSquaredDistance() {
    +  var a = new goog.math.Coordinate(7, 11);
    +  var b = new goog.math.Coordinate(3, -1);
    +  assertEquals(160, goog.math.Coordinate.squaredDistance(a, b));
    +}
    +
    +function testCoordinateDistance() {
    +  var a = new goog.math.Coordinate(-2, -3);
    +  var b = new goog.math.Coordinate(2, 0);
    +  assertEquals(5, goog.math.Coordinate.distance(a, b));
    +}
    +
    +function testCoordinateMagnitude() {
    +  var a = new goog.math.Coordinate(5, 5);
    +  assertEquals(Math.sqrt(50), goog.math.Coordinate.magnitude(a));
    +}
    +
    +function testCoordinateAzimuth() {
    +  var a = new goog.math.Coordinate(5, 5);
    +  assertEquals(45, goog.math.Coordinate.azimuth(a));
    +}
    +
    +function testCoordinateClone() {
    +  var c = new goog.math.Coordinate();
    +  assertEquals(c.toString(), c.clone().toString());
    +  c.x = -12;
    +  c.y = 13;
    +  assertEquals(c.toString(), c.clone().toString());
    +}
    +
    +function testCoordinateDifference() {
    +  assertObjectEquals(new goog.math.Coordinate(3, -40),
    +      goog.math.Coordinate.difference(
    +          new goog.math.Coordinate(5, 10),
    +          new goog.math.Coordinate(2, 50)));
    +}
    +
    +function testCoordinateSum() {
    +  assertObjectEquals(new goog.math.Coordinate(7, 60),
    +      goog.math.Coordinate.sum(
    +          new goog.math.Coordinate(5, 10),
    +          new goog.math.Coordinate(2, 50)));
    +}
    +
    +function testCoordinateCeil() {
    +  var c = new goog.math.Coordinate(5.2, 7.6);
    +  assertObjectEquals(new goog.math.Coordinate(6, 8), c.ceil());
    +  c = new goog.math.Coordinate(-1.2, -3.9);
    +  assertObjectEquals(new goog.math.Coordinate(-1, -3), c.ceil());
    +}
    +
    +function testCoordinateFloor() {
    +  var c = new goog.math.Coordinate(5.2, 7.6);
    +  assertObjectEquals(new goog.math.Coordinate(5, 7), c.floor());
    +  c = new goog.math.Coordinate(-1.2, -3.9);
    +  assertObjectEquals(new goog.math.Coordinate(-2, -4), c.floor());
    +}
    +
    +function testCoordinateRound() {
    +  var c = new goog.math.Coordinate(5.2, 7.6);
    +  assertObjectEquals(new goog.math.Coordinate(5, 8), c.round());
    +  c = new goog.math.Coordinate(-1.2, -3.9);
    +  assertObjectEquals(new goog.math.Coordinate(-1, -4), c.round());
    +}
    +
    +function testCoordinateTranslateCoordinate() {
    +  var c = new goog.math.Coordinate(10, 20);
    +  var t = new goog.math.Coordinate(5, 10);
    +  // The translate function modifies the coordinate instead of
    +  // returning a new one.
    +  assertEquals(c, c.translate(t));
    +  assertObjectEquals(new goog.math.Coordinate(15, 30), c);
    +}
    +
    +function testCoordinateTranslateXY() {
    +  var c = new goog.math.Coordinate(10, 20);
    +  // The translate function modifies the coordinate instead of
    +  // returning a new one.
    +  assertEquals(c, c.translate(25, 5));
    +  assertObjectEquals(new goog.math.Coordinate(35, 25), c);
    +}
    +
    +function testCoordinateTranslateX() {
    +  var c = new goog.math.Coordinate(10, 20);
    +  // The translate function modifies the coordinate instead of
    +  // returning a new one.
    +  assertEquals(c, c.translate(5));
    +  assertObjectEquals(new goog.math.Coordinate(15, 20), c);
    +}
    +
    +function testCoordinateScaleXY() {
    +  var c = new goog.math.Coordinate(10, 15);
    +  // The scale function modifies the coordinate instead of returning a new one.
    +  assertEquals(c, c.scale(2, 3));
    +  assertObjectEquals(new goog.math.Coordinate(20, 45), c);
    +}
    +
    +function testCoordinateScaleFactor() {
    +  var c = new goog.math.Coordinate(10, 15);
    +  // The scale function modifies the coordinate instead of returning a new one.
    +  assertEquals(c, c.scale(2));
    +  assertObjectEquals(new goog.math.Coordinate(20, 30), c);
    +}
    +
    +function testCoordinateRotateRadians() {
    +  var c = new goog.math.Coordinate(15, 75);
    +  c.rotateRadians(Math.PI / 2, new goog.math.Coordinate(10, 70));
    +  assertObjectEquals(new goog.math.Coordinate(5, 75), c);
    +}
    +
    +function testCoordinateRotateDegrees() {
    +  var c = new goog.math.Coordinate(15, 75);
    +  c.rotateDegrees(90, new goog.math.Coordinate(10, 70));
    +  assertObjectEquals(new goog.math.Coordinate(5, 75), c);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff.js b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff.js
    new file mode 100644
    index 0000000000..952f4746b3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff.js
    @@ -0,0 +1,104 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Utility class to manage the mathematics behind computing an
    + * exponential backoff model.  Given an initial backoff value and a maximum
    + * backoff value, every call to backoff() will double the value until maximum
    + * backoff value is reached.
    + *
    + */
    +
    +
    +goog.provide('goog.math.ExponentialBackoff');
    +
    +goog.require('goog.asserts');
    +
    +
    +
    +/**
    + * @struct
    + * @constructor
    + *
    + * @param {number} initialValue The initial backoff value.
    + * @param {number} maxValue The maximum backoff value.
    + */
    +goog.math.ExponentialBackoff = function(initialValue, maxValue) {
    +  goog.asserts.assert(initialValue > 0,
    +      'Initial value must be greater than zero.');
    +  goog.asserts.assert(maxValue >= initialValue,
    +      'Max value should be at least as large as initial value.');
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.initialValue_ = initialValue;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxValue_ = maxValue;
    +
    +  /**
    +   * The current backoff value.
    +   * @type {number}
    +   * @private
    +   */
    +  this.currValue_ = initialValue;
    +};
    +
    +
    +/**
    + * The number of backoffs that have happened.
    + * @type {number}
    + * @private
    + */
    +goog.math.ExponentialBackoff.prototype.currCount_ = 0;
    +
    +
    +/**
    + * Resets the backoff value to its initial value.
    + */
    +goog.math.ExponentialBackoff.prototype.reset = function() {
    +  this.currValue_ = this.initialValue_;
    +  this.currCount_ = 0;
    +};
    +
    +
    +/**
    + * @return {number} The current backoff value.
    + */
    +goog.math.ExponentialBackoff.prototype.getValue = function() {
    +  return this.currValue_;
    +};
    +
    +
    +/**
    + * @return {number} The number of times this class has backed off.
    + */
    +goog.math.ExponentialBackoff.prototype.getBackoffCount = function() {
    +  return this.currCount_;
    +};
    +
    +
    +/**
    + * Initiates a backoff.
    + */
    +goog.math.ExponentialBackoff.prototype.backoff = function() {
    +  this.currValue_ = Math.min(this.maxValue_, this.currValue_ * 2);
    +  this.currCount_++;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.html b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.html
    new file mode 100644
    index 0000000000..3edcf3c0b3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.ExponentialBackoff
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.ExponentialBackoffTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.js b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.js
    new file mode 100644
    index 0000000000..33a84aa02b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/exponentialbackoff_test.js
    @@ -0,0 +1,63 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.ExponentialBackoffTest');
    +goog.setTestOnly('goog.math.ExponentialBackoffTest');
    +
    +goog.require('goog.math.ExponentialBackoff');
    +goog.require('goog.testing.jsunit');
    +
    +var INITIAL_VALUE = 1;
    +
    +var MAX_VALUE = 10;
    +
    +function assertValueAndCount(value, count, backoff) {
    +  assertEquals('Wrong value', value, backoff.getValue());
    +  assertEquals('Wrong backoff count', count, backoff.getBackoffCount());
    +}
    +
    +
    +function createBackoff() {
    +  return new goog.math.ExponentialBackoff(INITIAL_VALUE, MAX_VALUE);
    +}
    +
    +
    +function testInitialState() {
    +  var backoff = createBackoff();
    +  assertValueAndCount(INITIAL_VALUE, 0, backoff);
    +}
    +
    +
    +function testBackoff() {
    +  var backoff = createBackoff();
    +  backoff.backoff();
    +  assertValueAndCount(2 /* value */, 1 /* count */, backoff);
    +  backoff.backoff();
    +  assertValueAndCount(4 /* value */, 2 /* count */, backoff);
    +  backoff.backoff();
    +  assertValueAndCount(8 /* value */, 3 /* count */, backoff);
    +  backoff.backoff();
    +  assertValueAndCount(MAX_VALUE, 4 /* count */, backoff);
    +  backoff.backoff();
    +  assertValueAndCount(MAX_VALUE, 5 /* count */, backoff);
    +}
    +
    +
    +function testReset() {
    +  var backoff = createBackoff();
    +  backoff.backoff();
    +  backoff.reset();
    +  assertValueAndCount(INITIAL_VALUE, 0 /* count */, backoff);
    +
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/integer.js b/mapbox-gl/closure-library/closure/goog/math/integer.js
    new file mode 100644
    index 0000000000..92f047e656
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/integer.js
    @@ -0,0 +1,739 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines an Integer class for representing (potentially)
    + * infinite length two's-complement integer values.
    + *
    + * For the specific case of 64-bit integers, use goog.math.Long, which is more
    + * efficient.
    + *
    + */
    +
    +goog.provide('goog.math.Integer');
    +
    +
    +
    +/**
    + * Constructs a two's-complement integer an array containing bits of the
    + * integer in 32-bit (signed) pieces, given in little-endian order (i.e.,
    + * lowest-order bits in the first piece), and the sign of -1 or 0.
    + *
    + * See the from* functions below for other convenient ways of constructing
    + * Integers.
    + *
    + * The internal representation of an integer is an array of 32-bit signed
    + * pieces, along with a sign (0 or -1) that indicates the contents of all the
    + * other 32-bit pieces out to infinity.  We use 32-bit pieces because these are
    + * the size of integers on which Javascript performs bit-operations.  For
    + * operations like addition and multiplication, we split each number into 16-bit
    + * pieces, which can easily be multiplied within Javascript's floating-point
    + * representation without overflow or change in sign.
    + *
    + * @struct
    + * @constructor
    + * @param {Array<number>} bits Array containing the bits of the number.
    + * @param {number} sign The sign of the number: -1 for negative and 0 positive.
    + * @final
    + */
    +goog.math.Integer = function(bits, sign) {
    +  /**
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.bits_ = [];
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.sign_ = sign;
    +
    +  // Copy the 32-bit signed integer values passed in.  We prune out those at the
    +  // top that equal the sign since they are redundant.
    +  var top = true;
    +  for (var i = bits.length - 1; i >= 0; i--) {
    +    var val = bits[i] | 0;
    +    if (!top || val != sign) {
    +      this.bits_[i] = val;
    +      top = false;
    +    }
    +  }
    +};
    +
    +
    +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
    +// from* methods on which they depend.
    +
    +
    +/**
    + * A cache of the Integer representations of small integer values.
    + * @type {!Object}
    + * @private
    + */
    +goog.math.Integer.IntCache_ = {};
    +
    +
    +/**
    + * Returns an Integer representing the given (32-bit) integer value.
    + * @param {number} value A 32-bit integer value.
    + * @return {!goog.math.Integer} The corresponding Integer value.
    + */
    +goog.math.Integer.fromInt = function(value) {
    +  if (-128 <= value && value < 128) {
    +    var cachedObj = goog.math.Integer.IntCache_[value];
    +    if (cachedObj) {
    +      return cachedObj;
    +    }
    +  }
    +
    +  var obj = new goog.math.Integer([value | 0], value < 0 ? -1 : 0);
    +  if (-128 <= value && value < 128) {
    +    goog.math.Integer.IntCache_[value] = obj;
    +  }
    +  return obj;
    +};
    +
    +
    +/**
    + * Returns an Integer representing the given value, provided that it is a finite
    + * number.  Otherwise, zero is returned.
    + * @param {number} value The value in question.
    + * @return {!goog.math.Integer} The corresponding Integer value.
    + */
    +goog.math.Integer.fromNumber = function(value) {
    +  if (isNaN(value) || !isFinite(value)) {
    +    return goog.math.Integer.ZERO;
    +  } else if (value < 0) {
    +    return goog.math.Integer.fromNumber(-value).negate();
    +  } else {
    +    var bits = [];
    +    var pow = 1;
    +    for (var i = 0; value >= pow; i++) {
    +      bits[i] = (value / pow) | 0;
    +      pow *= goog.math.Integer.TWO_PWR_32_DBL_;
    +    }
    +    return new goog.math.Integer(bits, 0);
    +  }
    +};
    +
    +
    +/**
    + * Returns a Integer representing the value that comes by concatenating the
    + * given entries, each is assumed to be 32 signed bits, given in little-endian
    + * order (lowest order bits in the lowest index), and sign-extending the highest
    + * order 32-bit value.
    + * @param {Array<number>} bits The bits of the number, in 32-bit signed pieces,
    + *     in little-endian order.
    + * @return {!goog.math.Integer} The corresponding Integer value.
    + */
    +goog.math.Integer.fromBits = function(bits) {
    +  var high = bits[bits.length - 1];
    +  return new goog.math.Integer(bits, high & (1 << 31) ? -1 : 0);
    +};
    +
    +
    +/**
    + * Returns an Integer representation of the given string, written using the
    + * given radix.
    + * @param {string} str The textual representation of the Integer.
    + * @param {number=} opt_radix The radix in which the text is written.
    + * @return {!goog.math.Integer} The corresponding Integer value.
    + */
    +goog.math.Integer.fromString = function(str, opt_radix) {
    +  if (str.length == 0) {
    +    throw Error('number format error: empty string');
    +  }
    +
    +  var radix = opt_radix || 10;
    +  if (radix < 2 || 36 < radix) {
    +    throw Error('radix out of range: ' + radix);
    +  }
    +
    +  if (str.charAt(0) == '-') {
    +    return goog.math.Integer.fromString(str.substring(1), radix).negate();
    +  } else if (str.indexOf('-') >= 0) {
    +    throw Error('number format error: interior "-" character');
    +  }
    +
    +  // Do several (8) digits each time through the loop, so as to
    +  // minimize the calls to the very expensive emulated div.
    +  var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 8));
    +
    +  var result = goog.math.Integer.ZERO;
    +  for (var i = 0; i < str.length; i += 8) {
    +    var size = Math.min(8, str.length - i);
    +    var value = parseInt(str.substring(i, i + size), radix);
    +    if (size < 8) {
    +      var power = goog.math.Integer.fromNumber(Math.pow(radix, size));
    +      result = result.multiply(power).add(goog.math.Integer.fromNumber(value));
    +    } else {
    +      result = result.multiply(radixToPower);
    +      result = result.add(goog.math.Integer.fromNumber(value));
    +    }
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * A number used repeatedly in calculations.  This must appear before the first
    + * call to the from* functions below.
    + * @type {number}
    + * @private
    + */
    +goog.math.Integer.TWO_PWR_32_DBL_ = (1 << 16) * (1 << 16);
    +
    +
    +/** @type {!goog.math.Integer} */
    +goog.math.Integer.ZERO = goog.math.Integer.fromInt(0);
    +
    +
    +/** @type {!goog.math.Integer} */
    +goog.math.Integer.ONE = goog.math.Integer.fromInt(1);
    +
    +
    +/**
    + * @type {!goog.math.Integer}
    + * @private
    + */
    +goog.math.Integer.TWO_PWR_24_ = goog.math.Integer.fromInt(1 << 24);
    +
    +
    +/**
    + * Returns the value, assuming it is a 32-bit integer.
    + * @return {number} The corresponding int value.
    + */
    +goog.math.Integer.prototype.toInt = function() {
    +  return this.bits_.length > 0 ? this.bits_[0] : this.sign_;
    +};
    +
    +
    +/** @return {number} The closest floating-point representation to this value. */
    +goog.math.Integer.prototype.toNumber = function() {
    +  if (this.isNegative()) {
    +    return -this.negate().toNumber();
    +  } else {
    +    var val = 0;
    +    var pow = 1;
    +    for (var i = 0; i < this.bits_.length; i++) {
    +      val += this.getBitsUnsigned(i) * pow;
    +      pow *= goog.math.Integer.TWO_PWR_32_DBL_;
    +    }
    +    return val;
    +  }
    +};
    +
    +
    +/**
    + * @param {number=} opt_radix The radix in which the text should be written.
    + * @return {string} The textual representation of this value.
    + * @override
    + */
    +goog.math.Integer.prototype.toString = function(opt_radix) {
    +  var radix = opt_radix || 10;
    +  if (radix < 2 || 36 < radix) {
    +    throw Error('radix out of range: ' + radix);
    +  }
    +
    +  if (this.isZero()) {
    +    return '0';
    +  } else if (this.isNegative()) {
    +    return '-' + this.negate().toString(radix);
    +  }
    +
    +  // Do several (6) digits each time through the loop, so as to
    +  // minimize the calls to the very expensive emulated div.
    +  var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 6));
    +
    +  var rem = this;
    +  var result = '';
    +  while (true) {
    +    var remDiv = rem.divide(radixToPower);
    +    var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
    +    var digits = intval.toString(radix);
    +
    +    rem = remDiv;
    +    if (rem.isZero()) {
    +      return digits + result;
    +    } else {
    +      while (digits.length < 6) {
    +        digits = '0' + digits;
    +      }
    +      result = '' + digits + result;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the index-th 32-bit (signed) piece of the Integer according to
    + * little-endian order (i.e., index 0 contains the smallest bits).
    + * @param {number} index The index in question.
    + * @return {number} The requested 32-bits as a signed number.
    + */
    +goog.math.Integer.prototype.getBits = function(index) {
    +  if (index < 0) {
    +    return 0;  // Allowing this simplifies bit shifting operations below...
    +  } else if (index < this.bits_.length) {
    +    return this.bits_[index];
    +  } else {
    +    return this.sign_;
    +  }
    +};
    +
    +
    +/**
    + * Returns the index-th 32-bit piece as an unsigned number.
    + * @param {number} index The index in question.
    + * @return {number} The requested 32-bits as an unsigned number.
    + */
    +goog.math.Integer.prototype.getBitsUnsigned = function(index) {
    +  var val = this.getBits(index);
    +  return val >= 0 ? val : goog.math.Integer.TWO_PWR_32_DBL_ + val;
    +};
    +
    +
    +/** @return {number} The sign bit of this number, -1 or 0. */
    +goog.math.Integer.prototype.getSign = function() {
    +  return this.sign_;
    +};
    +
    +
    +/** @return {boolean} Whether this value is zero. */
    +goog.math.Integer.prototype.isZero = function() {
    +  if (this.sign_ != 0) {
    +    return false;
    +  }
    +  for (var i = 0; i < this.bits_.length; i++) {
    +    if (this.bits_[i] != 0) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/** @return {boolean} Whether this value is negative. */
    +goog.math.Integer.prototype.isNegative = function() {
    +  return this.sign_ == -1;
    +};
    +
    +
    +/** @return {boolean} Whether this value is odd. */
    +goog.math.Integer.prototype.isOdd = function() {
    +  return (this.bits_.length == 0) && (this.sign_ == -1) ||
    +         (this.bits_.length > 0) && ((this.bits_[0] & 1) != 0);
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer equals the other.
    + */
    +goog.math.Integer.prototype.equals = function(other) {
    +  if (this.sign_ != other.sign_) {
    +    return false;
    +  }
    +  var len = Math.max(this.bits_.length, other.bits_.length);
    +  for (var i = 0; i < len; i++) {
    +    if (this.getBits(i) != other.getBits(i)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer does not equal the other.
    + */
    +goog.math.Integer.prototype.notEquals = function(other) {
    +  return !this.equals(other);
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer is greater than the other.
    + */
    +goog.math.Integer.prototype.greaterThan = function(other) {
    +  return this.compare(other) > 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer is greater than or equal to the other.
    + */
    +goog.math.Integer.prototype.greaterThanOrEqual = function(other) {
    +  return this.compare(other) >= 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer is less than the other.
    + */
    +goog.math.Integer.prototype.lessThan = function(other) {
    +  return this.compare(other) < 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {boolean} Whether this Integer is less than or equal to the other.
    + */
    +goog.math.Integer.prototype.lessThanOrEqual = function(other) {
    +  return this.compare(other) <= 0;
    +};
    +
    +
    +/**
    + * Compares this Integer with the given one.
    + * @param {goog.math.Integer} other Integer to compare against.
    + * @return {number} 0 if they are the same, 1 if the this is greater, and -1
    + *     if the given one is greater.
    + */
    +goog.math.Integer.prototype.compare = function(other) {
    +  var diff = this.subtract(other);
    +  if (diff.isNegative()) {
    +    return -1;
    +  } else if (diff.isZero()) {
    +    return 0;
    +  } else {
    +    return +1;
    +  }
    +};
    +
    +
    +/**
    + * Returns an integer with only the first numBits bits of this value, sign
    + * extended from the final bit.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Integer} The shorted integer value.
    + */
    +goog.math.Integer.prototype.shorten = function(numBits) {
    +  var arr_index = (numBits - 1) >> 5;
    +  var bit_index = (numBits - 1) % 32;
    +  var bits = [];
    +  for (var i = 0; i < arr_index; i++) {
    +    bits[i] = this.getBits(i);
    +  }
    +  var sigBits = bit_index == 31 ? 0xFFFFFFFF : (1 << (bit_index + 1)) - 1;
    +  var val = this.getBits(arr_index) & sigBits;
    +  if (val & (1 << bit_index)) {
    +    val |= 0xFFFFFFFF - sigBits;
    +    bits[arr_index] = val;
    +    return new goog.math.Integer(bits, -1);
    +  } else {
    +    bits[arr_index] = val;
    +    return new goog.math.Integer(bits, 0);
    +  }
    +};
    +
    +
    +/** @return {!goog.math.Integer} The negation of this value. */
    +goog.math.Integer.prototype.negate = function() {
    +  return this.not().add(goog.math.Integer.ONE);
    +};
    +
    +
    +/**
    + * Returns the sum of this and the given Integer.
    + * @param {goog.math.Integer} other The Integer to add to this.
    + * @return {!goog.math.Integer} The Integer result.
    + */
    +goog.math.Integer.prototype.add = function(other) {
    +  var len = Math.max(this.bits_.length, other.bits_.length);
    +  var arr = [];
    +  var carry = 0;
    +
    +  for (var i = 0; i <= len; i++) {
    +    var a1 = this.getBits(i) >>> 16;
    +    var a0 = this.getBits(i) & 0xFFFF;
    +
    +    var b1 = other.getBits(i) >>> 16;
    +    var b0 = other.getBits(i) & 0xFFFF;
    +
    +    var c0 = carry + a0 + b0;
    +    var c1 = (c0 >>> 16) + a1 + b1;
    +    carry = c1 >>> 16;
    +    c0 &= 0xFFFF;
    +    c1 &= 0xFFFF;
    +    arr[i] = (c1 << 16) | c0;
    +  }
    +  return goog.math.Integer.fromBits(arr);
    +};
    +
    +
    +/**
    + * Returns the difference of this and the given Integer.
    + * @param {goog.math.Integer} other The Integer to subtract from this.
    + * @return {!goog.math.Integer} The Integer result.
    + */
    +goog.math.Integer.prototype.subtract = function(other) {
    +  return this.add(other.negate());
    +};
    +
    +
    +/**
    + * Returns the product of this and the given Integer.
    + * @param {goog.math.Integer} other The Integer to multiply against this.
    + * @return {!goog.math.Integer} The product of this and the other.
    + */
    +goog.math.Integer.prototype.multiply = function(other) {
    +  if (this.isZero()) {
    +    return goog.math.Integer.ZERO;
    +  } else if (other.isZero()) {
    +    return goog.math.Integer.ZERO;
    +  }
    +
    +  if (this.isNegative()) {
    +    if (other.isNegative()) {
    +      return this.negate().multiply(other.negate());
    +    } else {
    +      return this.negate().multiply(other).negate();
    +    }
    +  } else if (other.isNegative()) {
    +    return this.multiply(other.negate()).negate();
    +  }
    +
    +  // If both numbers are small, use float multiplication
    +  if (this.lessThan(goog.math.Integer.TWO_PWR_24_) &&
    +      other.lessThan(goog.math.Integer.TWO_PWR_24_)) {
    +    return goog.math.Integer.fromNumber(this.toNumber() * other.toNumber());
    +  }
    +
    +  // Fill in an array of 16-bit products.
    +  var len = this.bits_.length + other.bits_.length;
    +  var arr = [];
    +  for (var i = 0; i < 2 * len; i++) {
    +    arr[i] = 0;
    +  }
    +  for (var i = 0; i < this.bits_.length; i++) {
    +    for (var j = 0; j < other.bits_.length; j++) {
    +      var a1 = this.getBits(i) >>> 16;
    +      var a0 = this.getBits(i) & 0xFFFF;
    +
    +      var b1 = other.getBits(j) >>> 16;
    +      var b0 = other.getBits(j) & 0xFFFF;
    +
    +      arr[2 * i + 2 * j] += a0 * b0;
    +      goog.math.Integer.carry16_(arr, 2 * i + 2 * j);
    +      arr[2 * i + 2 * j + 1] += a1 * b0;
    +      goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
    +      arr[2 * i + 2 * j + 1] += a0 * b1;
    +      goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
    +      arr[2 * i + 2 * j + 2] += a1 * b1;
    +      goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 2);
    +    }
    +  }
    +
    +  // Combine the 16-bit values into 32-bit values.
    +  for (var i = 0; i < len; i++) {
    +    arr[i] = (arr[2 * i + 1] << 16) | arr[2 * i];
    +  }
    +  for (var i = len; i < 2 * len; i++) {
    +    arr[i] = 0;
    +  }
    +  return new goog.math.Integer(arr, 0);
    +};
    +
    +
    +/**
    + * Carries any overflow from the given index into later entries.
    + * @param {Array<number>} bits Array of 16-bit values in little-endian order.
    + * @param {number} index The index in question.
    + * @private
    + */
    +goog.math.Integer.carry16_ = function(bits, index) {
    +  while ((bits[index] & 0xFFFF) != bits[index]) {
    +    bits[index + 1] += bits[index] >>> 16;
    +    bits[index] &= 0xFFFF;
    +  }
    +};
    +
    +
    +/**
    + * Returns this Integer divided by the given one.
    + * @param {goog.math.Integer} other Th Integer to divide this by.
    + * @return {!goog.math.Integer} This value divided by the given one.
    + */
    +goog.math.Integer.prototype.divide = function(other) {
    +  if (other.isZero()) {
    +    throw Error('division by zero');
    +  } else if (this.isZero()) {
    +    return goog.math.Integer.ZERO;
    +  }
    +
    +  if (this.isNegative()) {
    +    if (other.isNegative()) {
    +      return this.negate().divide(other.negate());
    +    } else {
    +      return this.negate().divide(other).negate();
    +    }
    +  } else if (other.isNegative()) {
    +    return this.divide(other.negate()).negate();
    +  }
    +
    +  // Repeat the following until the remainder is less than other:  find a
    +  // floating-point that approximates remainder / other *from below*, add this
    +  // into the result, and subtract it from the remainder.  It is critical that
    +  // the approximate value is less than or equal to the real value so that the
    +  // remainder never becomes negative.
    +  var res = goog.math.Integer.ZERO;
    +  var rem = this;
    +  while (rem.greaterThanOrEqual(other)) {
    +    // Approximate the result of division. This may be a little greater or
    +    // smaller than the actual value.
    +    var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
    +
    +    // We will tweak the approximate result by changing it in the 48-th digit or
    +    // the smallest non-fractional digit, whichever is larger.
    +    var log2 = Math.ceil(Math.log(approx) / Math.LN2);
    +    var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
    +
    +    // Decrease the approximation until it is smaller than the remainder.  Note
    +    // that if it is too large, the product overflows and is negative.
    +    var approxRes = goog.math.Integer.fromNumber(approx);
    +    var approxRem = approxRes.multiply(other);
    +    while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
    +      approx -= delta;
    +      approxRes = goog.math.Integer.fromNumber(approx);
    +      approxRem = approxRes.multiply(other);
    +    }
    +
    +    // We know the answer can't be zero... and actually, zero would cause
    +    // infinite recursion since we would make no progress.
    +    if (approxRes.isZero()) {
    +      approxRes = goog.math.Integer.ONE;
    +    }
    +
    +    res = res.add(approxRes);
    +    rem = rem.subtract(approxRem);
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * Returns this Integer modulo the given one.
    + * @param {goog.math.Integer} other The Integer by which to mod.
    + * @return {!goog.math.Integer} This value modulo the given one.
    + */
    +goog.math.Integer.prototype.modulo = function(other) {
    +  return this.subtract(this.divide(other).multiply(other));
    +};
    +
    +
    +/** @return {!goog.math.Integer} The bitwise-NOT of this value. */
    +goog.math.Integer.prototype.not = function() {
    +  var len = this.bits_.length;
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    arr[i] = ~this.bits_[i];
    +  }
    +  return new goog.math.Integer(arr, ~this.sign_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-AND of this Integer and the given one.
    + * @param {goog.math.Integer} other The Integer to AND with this.
    + * @return {!goog.math.Integer} The bitwise-AND of this and the other.
    + */
    +goog.math.Integer.prototype.and = function(other) {
    +  var len = Math.max(this.bits_.length, other.bits_.length);
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    arr[i] = this.getBits(i) & other.getBits(i);
    +  }
    +  return new goog.math.Integer(arr, this.sign_ & other.sign_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-OR of this Integer and the given one.
    + * @param {goog.math.Integer} other The Integer to OR with this.
    + * @return {!goog.math.Integer} The bitwise-OR of this and the other.
    + */
    +goog.math.Integer.prototype.or = function(other) {
    +  var len = Math.max(this.bits_.length, other.bits_.length);
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    arr[i] = this.getBits(i) | other.getBits(i);
    +  }
    +  return new goog.math.Integer(arr, this.sign_ | other.sign_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-XOR of this Integer and the given one.
    + * @param {goog.math.Integer} other The Integer to XOR with this.
    + * @return {!goog.math.Integer} The bitwise-XOR of this and the other.
    + */
    +goog.math.Integer.prototype.xor = function(other) {
    +  var len = Math.max(this.bits_.length, other.bits_.length);
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    arr[i] = this.getBits(i) ^ other.getBits(i);
    +  }
    +  return new goog.math.Integer(arr, this.sign_ ^ other.sign_);
    +};
    +
    +
    +/**
    + * Returns this value with bits shifted to the left by the given amount.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Integer} This shifted to the left by the given amount.
    + */
    +goog.math.Integer.prototype.shiftLeft = function(numBits) {
    +  var arr_delta = numBits >> 5;
    +  var bit_delta = numBits % 32;
    +  var len = this.bits_.length + arr_delta + (bit_delta > 0 ? 1 : 0);
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    if (bit_delta > 0) {
    +      arr[i] = (this.getBits(i - arr_delta) << bit_delta) |
    +               (this.getBits(i - arr_delta - 1) >>> (32 - bit_delta));
    +    } else {
    +      arr[i] = this.getBits(i - arr_delta);
    +    }
    +  }
    +  return new goog.math.Integer(arr, this.sign_);
    +};
    +
    +
    +/**
    + * Returns this value with bits shifted to the right by the given amount.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Integer} This shifted to the right by the given amount.
    + */
    +goog.math.Integer.prototype.shiftRight = function(numBits) {
    +  var arr_delta = numBits >> 5;
    +  var bit_delta = numBits % 32;
    +  var len = this.bits_.length - arr_delta;
    +  var arr = [];
    +  for (var i = 0; i < len; i++) {
    +    if (bit_delta > 0) {
    +      arr[i] = (this.getBits(i + arr_delta) >>> bit_delta) |
    +               (this.getBits(i + arr_delta + 1) << (32 - bit_delta));
    +    } else {
    +      arr[i] = this.getBits(i + arr_delta);
    +    }
    +  }
    +  return new goog.math.Integer(arr, this.sign_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/integer_test.html b/mapbox-gl/closure-library/closure/goog/math/integer_test.html
    new file mode 100644
    index 0000000000..b9a065bbf1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/integer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Integer
    +  </title>
    +  <script src="../base.js" type="text/javascript">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.math.IntegerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/integer_test.js b/mapbox-gl/closure-library/closure/goog/math/integer_test.js
    new file mode 100644
    index 0000000000..fac0528681
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/integer_test.js
    @@ -0,0 +1,1651 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.IntegerTest');
    +goog.setTestOnly('goog.math.IntegerTest');
    +
    +goog.require('goog.math.Integer');
    +goog.require('goog.testing.jsunit');
    +
    +// Interprets the given numbers as the bits of a 32-bit int.  In particular,
    +// this takes care of the 32-bit being interpretted as the sign.
    +function toInt32s(arr) {
    +  for (var i = 0; i < arr.length; ++i) {
    +    arr[i] = arr[i] & 0xFFFFFFFF;
    +  }
    +}
    +
    +// Note that these are in numerical order.
    +var TEST_BITS = [0x80000000, 0x00000000,
    +  0xb776d5f5, 0x5634e2db,
    +  0xffefffff, 0xffffffff,
    +  0xfff00000, 0x00000000,
    +  0xfffeffff, 0xffffffff,
    +  0xffff0000, 0x00000000,
    +  0xfffffffe, 0xffffffff,
    +  0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000,
    +  0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000,
    +  0x00000000, 0x00000001,
    +  0x00000000, 0x00000002,
    +  0x00000000, 0x00007fff,
    +  0x00000000, 0x00008000,
    +  0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000,
    +  0x00000000, 0x00ffffff,
    +  0x00000000, 0x01000000,
    +  0x00000000, 0x5634e2db,
    +  0x00000000, 0xb776d5f5,
    +  0x00000000, 0xffffffff,
    +  0x00000001, 0x00000000,
    +  0x0000ffff, 0xffffffff,
    +  0x00010000, 0x00000000,
    +  0x000fffff, 0xffffffff,
    +  0x00100000, 0x00000000,
    +  0x5634e2db, 0xb776d5f5,
    +  0x7fffffff, 0xffffffff];
    +toInt32s(TEST_BITS);
    +
    +var TEST_ADD_BITS = [
    +  0x3776d5f5, 0x5634e2db, 0x7fefffff, 0xffffffff, 0xb766d5f5, 0x5634e2da,
    +  0x7ff00000, 0x00000000, 0xb766d5f5, 0x5634e2db, 0xffdfffff, 0xffffffff,
    +  0x7ffeffff, 0xffffffff, 0xb775d5f5, 0x5634e2da, 0xffeeffff, 0xfffffffe,
    +  0xffeeffff, 0xffffffff, 0x7fff0000, 0x00000000, 0xb775d5f5, 0x5634e2db,
    +  0xffeeffff, 0xffffffff, 0xffef0000, 0x00000000, 0xfffdffff, 0xffffffff,
    +  0x7ffffffe, 0xffffffff, 0xb776d5f4, 0x5634e2da, 0xffeffffe, 0xfffffffe,
    +  0xffeffffe, 0xffffffff, 0xfffefffe, 0xfffffffe, 0xfffefffe, 0xffffffff,
    +  0x7fffffff, 0x00000000, 0xb776d5f4, 0x5634e2db, 0xffeffffe, 0xffffffff,
    +  0xffefffff, 0x00000000, 0xfffefffe, 0xffffffff, 0xfffeffff, 0x00000000,
    +  0xfffffffd, 0xffffffff, 0x7fffffff, 0xfeffffff, 0xb776d5f5, 0x5534e2da,
    +  0xffefffff, 0xfefffffe, 0xffefffff, 0xfeffffff, 0xfffeffff, 0xfefffffe,
    +  0xfffeffff, 0xfeffffff, 0xfffffffe, 0xfefffffe, 0xfffffffe, 0xfeffffff,
    +  0x7fffffff, 0xff000000, 0xb776d5f5, 0x5534e2db, 0xffefffff, 0xfeffffff,
    +  0xffefffff, 0xff000000, 0xfffeffff, 0xfeffffff, 0xfffeffff, 0xff000000,
    +  0xfffffffe, 0xfeffffff, 0xfffffffe, 0xff000000, 0xffffffff, 0xfdffffff,
    +  0x7fffffff, 0xfffeffff, 0xb776d5f5, 0x5633e2da, 0xffefffff, 0xfffefffe,
    +  0xffefffff, 0xfffeffff, 0xfffeffff, 0xfffefffe, 0xfffeffff, 0xfffeffff,
    +  0xfffffffe, 0xfffefffe, 0xfffffffe, 0xfffeffff, 0xffffffff, 0xfefefffe,
    +  0xffffffff, 0xfefeffff, 0x7fffffff, 0xffff0000, 0xb776d5f5, 0x5633e2db,
    +  0xffefffff, 0xfffeffff, 0xffefffff, 0xffff0000, 0xfffeffff, 0xfffeffff,
    +  0xfffeffff, 0xffff0000, 0xfffffffe, 0xfffeffff, 0xfffffffe, 0xffff0000,
    +  0xffffffff, 0xfefeffff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfffdffff,
    +  0x7fffffff, 0xffff7fff, 0xb776d5f5, 0x563462da, 0xffefffff, 0xffff7ffe,
    +  0xffefffff, 0xffff7fff, 0xfffeffff, 0xffff7ffe, 0xfffeffff, 0xffff7fff,
    +  0xfffffffe, 0xffff7ffe, 0xfffffffe, 0xffff7fff, 0xffffffff, 0xfeff7ffe,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xfffe7ffe, 0xffffffff, 0xfffe7fff,
    +  0x7fffffff, 0xffff8000, 0xb776d5f5, 0x563462db, 0xffefffff, 0xffff7fff,
    +  0xffefffff, 0xffff8000, 0xfffeffff, 0xffff7fff, 0xfffeffff, 0xffff8000,
    +  0xfffffffe, 0xffff7fff, 0xfffffffe, 0xffff8000, 0xffffffff, 0xfeff7fff,
    +  0xffffffff, 0xfeff8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfffe8000,
    +  0xffffffff, 0xfffeffff, 0x7fffffff, 0xfffffffe, 0xb776d5f5, 0x5634e2d9,
    +  0xffefffff, 0xfffffffd, 0xffefffff, 0xfffffffe, 0xfffeffff, 0xfffffffd,
    +  0xfffeffff, 0xfffffffe, 0xfffffffe, 0xfffffffd, 0xfffffffe, 0xfffffffe,
    +  0xffffffff, 0xfefffffd, 0xffffffff, 0xfefffffe, 0xffffffff, 0xfffefffd,
    +  0xffffffff, 0xfffefffe, 0xffffffff, 0xffff7ffd, 0xffffffff, 0xffff7ffe,
    +  0x7fffffff, 0xffffffff, 0xb776d5f5, 0x5634e2da, 0xffefffff, 0xfffffffe,
    +  0xffefffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xffffffff,
    +  0xfffffffe, 0xfffffffe, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfefffffe,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff7ffe, 0xffffffff, 0xffff7fff, 0xffffffff, 0xfffffffd,
    +  0x80000000, 0x00000000, 0xb776d5f5, 0x5634e2db, 0xffefffff, 0xffffffff,
    +  0xfff00000, 0x00000000, 0xfffeffff, 0xffffffff, 0xffff0000, 0x00000000,
    +  0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xfffeffff, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x80000000, 0x00000001, 0xb776d5f5, 0x5634e2dc,
    +  0xfff00000, 0x00000000, 0xfff00000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xffff0000, 0x00000001, 0xffffffff, 0x00000000, 0xffffffff, 0x00000001,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xff000001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x80000000, 0x00000002, 0xb776d5f5, 0x5634e2dd, 0xfff00000, 0x00000001,
    +  0xfff00000, 0x00000002, 0xffff0000, 0x00000001, 0xffff0000, 0x00000002,
    +  0xffffffff, 0x00000001, 0xffffffff, 0x00000002, 0xffffffff, 0xff000001,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0002,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff8002, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000003,
    +  0x80000000, 0x00007fff, 0xb776d5f5, 0x563562da, 0xfff00000, 0x00007ffe,
    +  0xfff00000, 0x00007fff, 0xffff0000, 0x00007ffe, 0xffff0000, 0x00007fff,
    +  0xffffffff, 0x00007ffe, 0xffffffff, 0x00007fff, 0xffffffff, 0xff007ffe,
    +  0xffffffff, 0xff007fff, 0xffffffff, 0xffff7ffe, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00007ffd,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00008001, 0x80000000, 0x00008000, 0xb776d5f5, 0x563562db,
    +  0xfff00000, 0x00007fff, 0xfff00000, 0x00008000, 0xffff0000, 0x00007fff,
    +  0xffff0000, 0x00008000, 0xffffffff, 0x00007fff, 0xffffffff, 0x00008000,
    +  0xffffffff, 0xff007fff, 0xffffffff, 0xff008000, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008002, 0x00000000, 0x0000ffff,
    +  0x80000000, 0x0000ffff, 0xb776d5f5, 0x5635e2da, 0xfff00000, 0x0000fffe,
    +  0xfff00000, 0x0000ffff, 0xffff0000, 0x0000fffe, 0xffff0000, 0x0000ffff,
    +  0xffffffff, 0x0000fffe, 0xffffffff, 0x0000ffff, 0xffffffff, 0xff00fffe,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x0000fffd,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00017ffe, 0x00000000, 0x00017fff,
    +  0x80000000, 0x00010000, 0xb776d5f5, 0x5635e2db, 0xfff00000, 0x0000ffff,
    +  0xfff00000, 0x00010000, 0xffff0000, 0x0000ffff, 0xffff0000, 0x00010000,
    +  0xffffffff, 0x0000ffff, 0xffffffff, 0x00010000, 0xffffffff, 0xff00ffff,
    +  0xffffffff, 0xff010000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00008000, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00010000, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010002, 0x00000000, 0x00017fff, 0x00000000, 0x00018000,
    +  0x00000000, 0x0001ffff, 0x80000000, 0x00ffffff, 0xb776d5f5, 0x5734e2da,
    +  0xfff00000, 0x00fffffe, 0xfff00000, 0x00ffffff, 0xffff0000, 0x00fffffe,
    +  0xffff0000, 0x00ffffff, 0xffffffff, 0x00fffffe, 0xffffffff, 0x00ffffff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00fefffe,
    +  0x00000000, 0x00feffff, 0x00000000, 0x00ff7ffe, 0x00000000, 0x00ff7fff,
    +  0x00000000, 0x00fffffd, 0x00000000, 0x00fffffe, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x01000000, 0x00000000, 0x01000001, 0x00000000, 0x01007ffe,
    +  0x00000000, 0x01007fff, 0x00000000, 0x0100fffe, 0x00000000, 0x0100ffff,
    +  0x80000000, 0x01000000, 0xb776d5f5, 0x5734e2db, 0xfff00000, 0x00ffffff,
    +  0xfff00000, 0x01000000, 0xffff0000, 0x00ffffff, 0xffff0000, 0x01000000,
    +  0xffffffff, 0x00ffffff, 0xffffffff, 0x01000000, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00feffff, 0x00000000, 0x00ff0000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00ff8000, 0x00000000, 0x00fffffe,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01000000, 0x00000000, 0x01000001,
    +  0x00000000, 0x01000002, 0x00000000, 0x01007fff, 0x00000000, 0x01008000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x01010000, 0x00000000, 0x01ffffff,
    +  0x80000000, 0x5634e2db, 0xb776d5f5, 0xac69c5b6, 0xfff00000, 0x5634e2da,
    +  0xfff00000, 0x5634e2db, 0xffff0000, 0x5634e2da, 0xffff0000, 0x5634e2db,
    +  0xffffffff, 0x5634e2da, 0xffffffff, 0x5634e2db, 0x00000000, 0x5534e2da,
    +  0x00000000, 0x5534e2db, 0x00000000, 0x5633e2da, 0x00000000, 0x5633e2db,
    +  0x00000000, 0x563462da, 0x00000000, 0x563462db, 0x00000000, 0x5634e2d9,
    +  0x00000000, 0x5634e2da, 0x00000000, 0x5634e2db, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2dd, 0x00000000, 0x563562da, 0x00000000, 0x563562db,
    +  0x00000000, 0x5635e2da, 0x00000000, 0x5635e2db, 0x00000000, 0x5734e2da,
    +  0x00000000, 0x5734e2db, 0x80000000, 0xb776d5f5, 0xb776d5f6, 0x0dabb8d0,
    +  0xfff00000, 0xb776d5f4, 0xfff00000, 0xb776d5f5, 0xffff0000, 0xb776d5f4,
    +  0xffff0000, 0xb776d5f5, 0xffffffff, 0xb776d5f4, 0xffffffff, 0xb776d5f5,
    +  0x00000000, 0xb676d5f4, 0x00000000, 0xb676d5f5, 0x00000000, 0xb775d5f4,
    +  0x00000000, 0xb775d5f5, 0x00000000, 0xb77655f4, 0x00000000, 0xb77655f5,
    +  0x00000000, 0xb776d5f3, 0x00000000, 0xb776d5f4, 0x00000000, 0xb776d5f5,
    +  0x00000000, 0xb776d5f6, 0x00000000, 0xb776d5f7, 0x00000000, 0xb77755f4,
    +  0x00000000, 0xb77755f5, 0x00000000, 0xb777d5f4, 0x00000000, 0xb777d5f5,
    +  0x00000000, 0xb876d5f4, 0x00000000, 0xb876d5f5, 0x00000001, 0x0dabb8d0,
    +  0x80000000, 0xffffffff, 0xb776d5f6, 0x5634e2da, 0xfff00000, 0xfffffffe,
    +  0xfff00000, 0xffffffff, 0xffff0000, 0xfffffffe, 0xffff0000, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0xfefffffe,
    +  0x00000000, 0xfeffffff, 0x00000000, 0xfffefffe, 0x00000000, 0xfffeffff,
    +  0x00000000, 0xffff7ffe, 0x00000000, 0xffff7fff, 0x00000000, 0xfffffffd,
    +  0x00000000, 0xfffffffe, 0x00000000, 0xffffffff, 0x00000001, 0x00000000,
    +  0x00000001, 0x00000001, 0x00000001, 0x00007ffe, 0x00000001, 0x00007fff,
    +  0x00000001, 0x0000fffe, 0x00000001, 0x0000ffff, 0x00000001, 0x00fffffe,
    +  0x00000001, 0x00ffffff, 0x00000001, 0x5634e2da, 0x00000001, 0xb776d5f4,
    +  0x80000001, 0x00000000, 0xb776d5f6, 0x5634e2db, 0xfff00000, 0xffffffff,
    +  0xfff00001, 0x00000000, 0xffff0000, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xfeffffff,
    +  0x00000000, 0xff000000, 0x00000000, 0xfffeffff, 0x00000000, 0xffff0000,
    +  0x00000000, 0xffff7fff, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xffffffff, 0x00000001, 0x00000000, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000002, 0x00000001, 0x00007fff, 0x00000001, 0x00008000,
    +  0x00000001, 0x0000ffff, 0x00000001, 0x00010000, 0x00000001, 0x00ffffff,
    +  0x00000001, 0x01000000, 0x00000001, 0x5634e2db, 0x00000001, 0xb776d5f5,
    +  0x00000001, 0xffffffff, 0x8000ffff, 0xffffffff, 0xb777d5f5, 0x5634e2da,
    +  0xfff0ffff, 0xfffffffe, 0xfff0ffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x0000fffe, 0xfffffffe, 0x0000fffe, 0xffffffff,
    +  0x0000ffff, 0xfefffffe, 0x0000ffff, 0xfeffffff, 0x0000ffff, 0xfffefffe,
    +  0x0000ffff, 0xfffeffff, 0x0000ffff, 0xffff7ffe, 0x0000ffff, 0xffff7fff,
    +  0x0000ffff, 0xfffffffd, 0x0000ffff, 0xfffffffe, 0x0000ffff, 0xffffffff,
    +  0x00010000, 0x00000000, 0x00010000, 0x00000001, 0x00010000, 0x00007ffe,
    +  0x00010000, 0x00007fff, 0x00010000, 0x0000fffe, 0x00010000, 0x0000ffff,
    +  0x00010000, 0x00fffffe, 0x00010000, 0x00ffffff, 0x00010000, 0x5634e2da,
    +  0x00010000, 0xb776d5f4, 0x00010000, 0xfffffffe, 0x00010000, 0xffffffff,
    +  0x80010000, 0x00000000, 0xb777d5f5, 0x5634e2db, 0xfff0ffff, 0xffffffff,
    +  0xfff10000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x0000fffe, 0xffffffff, 0x0000ffff, 0x00000000, 0x0000ffff, 0xfeffffff,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xfffeffff, 0x0000ffff, 0xffff0000,
    +  0x0000ffff, 0xffff7fff, 0x0000ffff, 0xffff8000, 0x0000ffff, 0xfffffffe,
    +  0x0000ffff, 0xffffffff, 0x00010000, 0x00000000, 0x00010000, 0x00000001,
    +  0x00010000, 0x00000002, 0x00010000, 0x00007fff, 0x00010000, 0x00008000,
    +  0x00010000, 0x0000ffff, 0x00010000, 0x00010000, 0x00010000, 0x00ffffff,
    +  0x00010000, 0x01000000, 0x00010000, 0x5634e2db, 0x00010000, 0xb776d5f5,
    +  0x00010000, 0xffffffff, 0x00010001, 0x00000000, 0x0001ffff, 0xffffffff,
    +  0x800fffff, 0xffffffff, 0xb786d5f5, 0x5634e2da, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x000effff, 0xfffffffe, 0x000effff, 0xffffffff,
    +  0x000ffffe, 0xfffffffe, 0x000ffffe, 0xffffffff, 0x000fffff, 0xfefffffe,
    +  0x000fffff, 0xfeffffff, 0x000fffff, 0xfffefffe, 0x000fffff, 0xfffeffff,
    +  0x000fffff, 0xffff7ffe, 0x000fffff, 0xffff7fff, 0x000fffff, 0xfffffffd,
    +  0x000fffff, 0xfffffffe, 0x000fffff, 0xffffffff, 0x00100000, 0x00000000,
    +  0x00100000, 0x00000001, 0x00100000, 0x00007ffe, 0x00100000, 0x00007fff,
    +  0x00100000, 0x0000fffe, 0x00100000, 0x0000ffff, 0x00100000, 0x00fffffe,
    +  0x00100000, 0x00ffffff, 0x00100000, 0x5634e2da, 0x00100000, 0xb776d5f4,
    +  0x00100000, 0xfffffffe, 0x00100000, 0xffffffff, 0x0010ffff, 0xfffffffe,
    +  0x0010ffff, 0xffffffff, 0x80100000, 0x00000000, 0xb786d5f5, 0x5634e2db,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x000f0000, 0x00000000, 0x000ffffe, 0xffffffff, 0x000fffff, 0x00000000,
    +  0x000fffff, 0xfeffffff, 0x000fffff, 0xff000000, 0x000fffff, 0xfffeffff,
    +  0x000fffff, 0xffff0000, 0x000fffff, 0xffff7fff, 0x000fffff, 0xffff8000,
    +  0x000fffff, 0xfffffffe, 0x000fffff, 0xffffffff, 0x00100000, 0x00000000,
    +  0x00100000, 0x00000001, 0x00100000, 0x00000002, 0x00100000, 0x00007fff,
    +  0x00100000, 0x00008000, 0x00100000, 0x0000ffff, 0x00100000, 0x00010000,
    +  0x00100000, 0x00ffffff, 0x00100000, 0x01000000, 0x00100000, 0x5634e2db,
    +  0x00100000, 0xb776d5f5, 0x00100000, 0xffffffff, 0x00100001, 0x00000000,
    +  0x0010ffff, 0xffffffff, 0x00110000, 0x00000000, 0x001fffff, 0xffffffff,
    +  0xd634e2db, 0xb776d5f5, 0x0dabb8d1, 0x0dabb8d0, 0x5624e2db, 0xb776d5f4,
    +  0x5624e2db, 0xb776d5f5, 0x5633e2db, 0xb776d5f4, 0x5633e2db, 0xb776d5f5,
    +  0x5634e2da, 0xb776d5f4, 0x5634e2da, 0xb776d5f5, 0x5634e2db, 0xb676d5f4,
    +  0x5634e2db, 0xb676d5f5, 0x5634e2db, 0xb775d5f4, 0x5634e2db, 0xb775d5f5,
    +  0x5634e2db, 0xb77655f4, 0x5634e2db, 0xb77655f5, 0x5634e2db, 0xb776d5f3,
    +  0x5634e2db, 0xb776d5f4, 0x5634e2db, 0xb776d5f5, 0x5634e2db, 0xb776d5f6,
    +  0x5634e2db, 0xb776d5f7, 0x5634e2db, 0xb77755f4, 0x5634e2db, 0xb77755f5,
    +  0x5634e2db, 0xb777d5f4, 0x5634e2db, 0xb777d5f5, 0x5634e2db, 0xb876d5f4,
    +  0x5634e2db, 0xb876d5f5, 0x5634e2dc, 0x0dabb8d0, 0x5634e2dc, 0x6eedabea,
    +  0x5634e2dc, 0xb776d5f4, 0x5634e2dc, 0xb776d5f5, 0x5635e2db, 0xb776d5f4,
    +  0x5635e2db, 0xb776d5f5, 0x5644e2db, 0xb776d5f4, 0x5644e2db, 0xb776d5f5,
    +  0xffffffff, 0xffffffff, 0x3776d5f5, 0x5634e2da, 0x7fefffff, 0xfffffffe,
    +  0x7fefffff, 0xffffffff, 0x7ffeffff, 0xfffffffe, 0x7ffeffff, 0xffffffff,
    +  0x7ffffffe, 0xfffffffe, 0x7ffffffe, 0xffffffff, 0x7fffffff, 0xfefffffe,
    +  0x7fffffff, 0xfeffffff, 0x7fffffff, 0xfffefffe, 0x7fffffff, 0xfffeffff,
    +  0x7fffffff, 0xffff7ffe, 0x7fffffff, 0xffff7fff, 0x7fffffff, 0xfffffffd,
    +  0x7fffffff, 0xfffffffe, 0x7fffffff, 0xffffffff, 0x80000000, 0x00000000,
    +  0x80000000, 0x00000001, 0x80000000, 0x00007ffe, 0x80000000, 0x00007fff,
    +  0x80000000, 0x0000fffe, 0x80000000, 0x0000ffff, 0x80000000, 0x00fffffe,
    +  0x80000000, 0x00ffffff, 0x80000000, 0x5634e2da, 0x80000000, 0xb776d5f4,
    +  0x80000000, 0xfffffffe, 0x80000000, 0xffffffff, 0x8000ffff, 0xfffffffe,
    +  0x8000ffff, 0xffffffff, 0x800fffff, 0xfffffffe, 0x800fffff, 0xffffffff,
    +  0xd634e2db, 0xb776d5f4
    +];
    +toInt32s(TEST_ADD_BITS);
    +
    +var TEST_SUB_BITS = [
    +  0x00000000, 0x00000000, 0xc8892a0a, 0xa9cb1d25, 0x80100000, 0x00000001,
    +  0x80100000, 0x00000000, 0x80010000, 0x00000001, 0x80010000, 0x00000000,
    +  0x80000001, 0x00000001, 0x80000001, 0x00000000, 0x80000000, 0x01000001,
    +  0x80000000, 0x01000000, 0x80000000, 0x00010001, 0x80000000, 0x00010000,
    +  0x80000000, 0x00008001, 0x80000000, 0x00008000, 0x80000000, 0x00000002,
    +  0x80000000, 0x00000001, 0x80000000, 0x00000000, 0x7fffffff, 0xffffffff,
    +  0x7fffffff, 0xfffffffe, 0x7fffffff, 0xffff8001, 0x7fffffff, 0xffff8000,
    +  0x7fffffff, 0xffff0001, 0x7fffffff, 0xffff0000, 0x7fffffff, 0xff000001,
    +  0x7fffffff, 0xff000000, 0x7fffffff, 0xa9cb1d25, 0x7fffffff, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0x7fffffff, 0x00000000, 0x7fff0000, 0x00000001,
    +  0x7fff0000, 0x00000000, 0x7ff00000, 0x00000001, 0x7ff00000, 0x00000000,
    +  0x29cb1d24, 0x48892a0b, 0x00000000, 0x00000001, 0x3776d5f5, 0x5634e2db,
    +  0x00000000, 0x00000000, 0xb786d5f5, 0x5634e2dc, 0xb786d5f5, 0x5634e2db,
    +  0xb777d5f5, 0x5634e2dc, 0xb777d5f5, 0x5634e2db, 0xb776d5f6, 0x5634e2dc,
    +  0xb776d5f6, 0x5634e2db, 0xb776d5f5, 0x5734e2dc, 0xb776d5f5, 0x5734e2db,
    +  0xb776d5f5, 0x5635e2dc, 0xb776d5f5, 0x5635e2db, 0xb776d5f5, 0x563562dc,
    +  0xb776d5f5, 0x563562db, 0xb776d5f5, 0x5634e2dd, 0xb776d5f5, 0x5634e2dc,
    +  0xb776d5f5, 0x5634e2db, 0xb776d5f5, 0x5634e2da, 0xb776d5f5, 0x5634e2d9,
    +  0xb776d5f5, 0x563462dc, 0xb776d5f5, 0x563462db, 0xb776d5f5, 0x5633e2dc,
    +  0xb776d5f5, 0x5633e2db, 0xb776d5f5, 0x5534e2dc, 0xb776d5f5, 0x5534e2db,
    +  0xb776d5f5, 0x00000000, 0xb776d5f4, 0x9ebe0ce6, 0xb776d5f4, 0x5634e2dc,
    +  0xb776d5f4, 0x5634e2db, 0xb775d5f5, 0x5634e2dc, 0xb775d5f5, 0x5634e2db,
    +  0xb766d5f5, 0x5634e2dc, 0xb766d5f5, 0x5634e2db, 0x6141f319, 0x9ebe0ce6,
    +  0x3776d5f5, 0x5634e2dc, 0x7fefffff, 0xffffffff, 0x48792a0a, 0xa9cb1d24,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xfff10000, 0x00000000,
    +  0xfff0ffff, 0xffffffff, 0xfff00001, 0x00000000, 0xfff00000, 0xffffffff,
    +  0xfff00000, 0x01000000, 0xfff00000, 0x00ffffff, 0xfff00000, 0x00010000,
    +  0xfff00000, 0x0000ffff, 0xfff00000, 0x00008000, 0xfff00000, 0x00007fff,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xffefffff, 0xfffffffd, 0xffefffff, 0xffff8000,
    +  0xffefffff, 0xffff7fff, 0xffefffff, 0xffff0000, 0xffefffff, 0xfffeffff,
    +  0xffefffff, 0xff000000, 0xffefffff, 0xfeffffff, 0xffefffff, 0xa9cb1d24,
    +  0xffefffff, 0x48892a0a, 0xffefffff, 0x00000000, 0xffeffffe, 0xffffffff,
    +  0xffef0000, 0x00000000, 0xffeeffff, 0xffffffff, 0xffe00000, 0x00000000,
    +  0xffdfffff, 0xffffffff, 0xa9bb1d24, 0x48892a0a, 0x7ff00000, 0x00000000,
    +  0x7ff00000, 0x00000000, 0x48792a0a, 0xa9cb1d25, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xfff10000, 0x00000001, 0xfff10000, 0x00000000,
    +  0xfff00001, 0x00000001, 0xfff00001, 0x00000000, 0xfff00000, 0x01000001,
    +  0xfff00000, 0x01000000, 0xfff00000, 0x00010001, 0xfff00000, 0x00010000,
    +  0xfff00000, 0x00008001, 0xfff00000, 0x00008000, 0xfff00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xffefffff, 0xffff8001, 0xffefffff, 0xffff8000,
    +  0xffefffff, 0xffff0001, 0xffefffff, 0xffff0000, 0xffefffff, 0xff000001,
    +  0xffefffff, 0xff000000, 0xffefffff, 0xa9cb1d25, 0xffefffff, 0x48892a0b,
    +  0xffefffff, 0x00000001, 0xffefffff, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffef0000, 0x00000000, 0xffe00000, 0x00000001, 0xffe00000, 0x00000000,
    +  0xa9bb1d24, 0x48892a0b, 0x7ff00000, 0x00000001, 0x7ffeffff, 0xffffffff,
    +  0x48882a0a, 0xa9cb1d24, 0x000f0000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffff0000, 0xffffffff, 0xffff0000, 0x01000000, 0xffff0000, 0x00ffffff,
    +  0xffff0000, 0x00010000, 0xffff0000, 0x0000ffff, 0xffff0000, 0x00008000,
    +  0xffff0000, 0x00007fff, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xfffffffd,
    +  0xfffeffff, 0xffff8000, 0xfffeffff, 0xffff7fff, 0xfffeffff, 0xffff0000,
    +  0xfffeffff, 0xfffeffff, 0xfffeffff, 0xff000000, 0xfffeffff, 0xfeffffff,
    +  0xfffeffff, 0xa9cb1d24, 0xfffeffff, 0x48892a0a, 0xfffeffff, 0x00000000,
    +  0xfffefffe, 0xffffffff, 0xfffe0000, 0x00000000, 0xfffdffff, 0xffffffff,
    +  0xffef0000, 0x00000000, 0xffeeffff, 0xffffffff, 0xa9ca1d24, 0x48892a0a,
    +  0x7fff0000, 0x00000000, 0x7fff0000, 0x00000000, 0x48882a0a, 0xa9cb1d25,
    +  0x000f0000, 0x00000001, 0x000f0000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffff0001, 0x00000001, 0xffff0001, 0x00000000,
    +  0xffff0000, 0x01000001, 0xffff0000, 0x01000000, 0xffff0000, 0x00010001,
    +  0xffff0000, 0x00010000, 0xffff0000, 0x00008001, 0xffff0000, 0x00008000,
    +  0xffff0000, 0x00000002, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xffff8001,
    +  0xfffeffff, 0xffff8000, 0xfffeffff, 0xffff0001, 0xfffeffff, 0xffff0000,
    +  0xfffeffff, 0xff000001, 0xfffeffff, 0xff000000, 0xfffeffff, 0xa9cb1d25,
    +  0xfffeffff, 0x48892a0b, 0xfffeffff, 0x00000001, 0xfffeffff, 0x00000000,
    +  0xfffe0000, 0x00000001, 0xfffe0000, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffef0000, 0x00000000, 0xa9ca1d24, 0x48892a0b, 0x7fff0000, 0x00000001,
    +  0x7ffffffe, 0xffffffff, 0x48892a09, 0xa9cb1d24, 0x000fffff, 0x00000000,
    +  0x000ffffe, 0xffffffff, 0x0000ffff, 0x00000000, 0x0000fffe, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x01000000,
    +  0xffffffff, 0x00ffffff, 0xffffffff, 0x00010000, 0xffffffff, 0x0000ffff,
    +  0xffffffff, 0x00008000, 0xffffffff, 0x00007fff, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffffffe, 0xfffffffd, 0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff7fff,
    +  0xfffffffe, 0xffff0000, 0xfffffffe, 0xfffeffff, 0xfffffffe, 0xff000000,
    +  0xfffffffe, 0xfeffffff, 0xfffffffe, 0xa9cb1d24, 0xfffffffe, 0x48892a0a,
    +  0xfffffffe, 0x00000000, 0xfffffffd, 0xffffffff, 0xfffeffff, 0x00000000,
    +  0xfffefffe, 0xffffffff, 0xffefffff, 0x00000000, 0xffeffffe, 0xffffffff,
    +  0xa9cb1d23, 0x48892a0a, 0x7fffffff, 0x00000000, 0x7fffffff, 0x00000000,
    +  0x48892a09, 0xa9cb1d25, 0x000fffff, 0x00000001, 0x000fffff, 0x00000000,
    +  0x0000ffff, 0x00000001, 0x0000ffff, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0x01000001, 0xffffffff, 0x01000000,
    +  0xffffffff, 0x00010001, 0xffffffff, 0x00010000, 0xffffffff, 0x00008001,
    +  0xffffffff, 0x00008000, 0xffffffff, 0x00000002, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffffffe, 0xffff8001, 0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff0001,
    +  0xfffffffe, 0xffff0000, 0xfffffffe, 0xff000001, 0xfffffffe, 0xff000000,
    +  0xfffffffe, 0xa9cb1d25, 0xfffffffe, 0x48892a0b, 0xfffffffe, 0x00000001,
    +  0xfffffffe, 0x00000000, 0xfffeffff, 0x00000001, 0xfffeffff, 0x00000000,
    +  0xffefffff, 0x00000001, 0xffefffff, 0x00000000, 0xa9cb1d23, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0x7fffffff, 0xfeffffff, 0x48892a0a, 0xa8cb1d24,
    +  0x000fffff, 0xff000000, 0x000fffff, 0xfeffffff, 0x0000ffff, 0xff000000,
    +  0x0000ffff, 0xfeffffff, 0x00000000, 0xff000000, 0x00000000, 0xfeffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xff008000, 0xffffffff, 0xff007fff,
    +  0xffffffff, 0xff000001, 0xffffffff, 0xff000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xfefffffe, 0xffffffff, 0xfefffffd, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfefeffff,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xfdffffff, 0xffffffff, 0xa8cb1d24,
    +  0xffffffff, 0x47892a0a, 0xfffffffe, 0xff000000, 0xfffffffe, 0xfeffffff,
    +  0xfffeffff, 0xff000000, 0xfffeffff, 0xfeffffff, 0xffefffff, 0xff000000,
    +  0xffefffff, 0xfeffffff, 0xa9cb1d24, 0x47892a0a, 0x7fffffff, 0xff000000,
    +  0x7fffffff, 0xff000000, 0x48892a0a, 0xa8cb1d25, 0x000fffff, 0xff000001,
    +  0x000fffff, 0xff000000, 0x0000ffff, 0xff000001, 0x0000ffff, 0xff000000,
    +  0x00000000, 0xff000001, 0x00000000, 0xff000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff010001, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff008001, 0xffffffff, 0xff008000, 0xffffffff, 0xff000002,
    +  0xffffffff, 0xff000001, 0xffffffff, 0xff000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xfefffffe, 0xffffffff, 0xfeff8001, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff0001, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfe000001,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xa8cb1d25, 0xffffffff, 0x47892a0b,
    +  0xfffffffe, 0xff000001, 0xfffffffe, 0xff000000, 0xfffeffff, 0xff000001,
    +  0xfffeffff, 0xff000000, 0xffefffff, 0xff000001, 0xffefffff, 0xff000000,
    +  0xa9cb1d24, 0x47892a0b, 0x7fffffff, 0xff000001, 0x7fffffff, 0xfffeffff,
    +  0x48892a0a, 0xa9ca1d24, 0x000fffff, 0xffff0000, 0x000fffff, 0xfffeffff,
    +  0x0000ffff, 0xffff0000, 0x0000ffff, 0xfffeffff, 0x00000000, 0xffff0000,
    +  0x00000000, 0xfffeffff, 0x00000000, 0x00ff0000, 0x00000000, 0x00feffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffefffd,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffdffff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfefeffff,
    +  0xffffffff, 0xa9ca1d24, 0xffffffff, 0x48882a0a, 0xfffffffe, 0xffff0000,
    +  0xfffffffe, 0xfffeffff, 0xfffeffff, 0xffff0000, 0xfffeffff, 0xfffeffff,
    +  0xffefffff, 0xffff0000, 0xffefffff, 0xfffeffff, 0xa9cb1d24, 0x48882a0a,
    +  0x7fffffff, 0xffff0000, 0x7fffffff, 0xffff0000, 0x48892a0a, 0xa9ca1d25,
    +  0x000fffff, 0xffff0001, 0x000fffff, 0xffff0000, 0x0000ffff, 0xffff0001,
    +  0x0000ffff, 0xffff0000, 0x00000000, 0xffff0001, 0x00000000, 0xffff0000,
    +  0x00000000, 0x00ff0001, 0x00000000, 0x00ff0000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff0002, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffe8001,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe0001, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfeff0001, 0xffffffff, 0xfeff0000, 0xffffffff, 0xa9ca1d25,
    +  0xffffffff, 0x48882a0b, 0xfffffffe, 0xffff0001, 0xfffffffe, 0xffff0000,
    +  0xfffeffff, 0xffff0001, 0xfffeffff, 0xffff0000, 0xffefffff, 0xffff0001,
    +  0xffefffff, 0xffff0000, 0xa9cb1d24, 0x48882a0b, 0x7fffffff, 0xffff0001,
    +  0x7fffffff, 0xffff7fff, 0x48892a0a, 0xa9ca9d24, 0x000fffff, 0xffff8000,
    +  0x000fffff, 0xffff7fff, 0x0000ffff, 0xffff8000, 0x0000ffff, 0xffff7fff,
    +  0x00000000, 0xffff8000, 0x00000000, 0xffff7fff, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00008000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff7ffe,
    +  0xffffffff, 0xffff7ffd, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xa9ca9d24, 0xffffffff, 0x4888aa0a,
    +  0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff7fff, 0xfffeffff, 0xffff8000,
    +  0xfffeffff, 0xffff7fff, 0xffefffff, 0xffff8000, 0xffefffff, 0xffff7fff,
    +  0xa9cb1d24, 0x4888aa0a, 0x7fffffff, 0xffff8000, 0x7fffffff, 0xffff8000,
    +  0x48892a0a, 0xa9ca9d25, 0x000fffff, 0xffff8001, 0x000fffff, 0xffff8000,
    +  0x0000ffff, 0xffff8001, 0x0000ffff, 0xffff8000, 0x00000000, 0xffff8001,
    +  0x00000000, 0xffff8000, 0x00000000, 0x00ff8001, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8002, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff7ffe,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffe8001,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfeff8001, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xa9ca9d25, 0xffffffff, 0x4888aa0b, 0xfffffffe, 0xffff8001,
    +  0xfffffffe, 0xffff8000, 0xfffeffff, 0xffff8001, 0xfffeffff, 0xffff8000,
    +  0xffefffff, 0xffff8001, 0xffefffff, 0xffff8000, 0xa9cb1d24, 0x4888aa0b,
    +  0x7fffffff, 0xffff8001, 0x7fffffff, 0xfffffffe, 0x48892a0a, 0xa9cb1d23,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x00fffffe, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x00007fff, 0x00000000, 0x00007ffe,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffd, 0xffffffff, 0xfffffffc, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff7ffe, 0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xfefffffe, 0xffffffff, 0xa9cb1d23,
    +  0xffffffff, 0x48892a09, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xa9cb1d24, 0x48892a09, 0x7fffffff, 0xffffffff,
    +  0x7fffffff, 0xffffffff, 0x48892a0a, 0xa9cb1d24, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x01000000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x00010000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00008000, 0x00000000, 0x00007fff, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffd, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xfffeffff, 0xffffffff, 0xff000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xa9cb1d24, 0xffffffff, 0x48892a0a,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xa9cb1d24, 0x48892a0a, 0x80000000, 0x00000000, 0x80000000, 0x00000000,
    +  0x48892a0a, 0xa9cb1d25, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0x01000001, 0x00000000, 0x01000000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00010000, 0x00000000, 0x00008001,
    +  0x00000000, 0x00008000, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xff000001, 0xffffffff, 0xff000000,
    +  0xffffffff, 0xa9cb1d25, 0xffffffff, 0x48892a0b, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xa9cb1d24, 0x48892a0b,
    +  0x80000000, 0x00000001, 0x80000000, 0x00000001, 0x48892a0a, 0xa9cb1d26,
    +  0x00100000, 0x00000002, 0x00100000, 0x00000001, 0x00010000, 0x00000002,
    +  0x00010000, 0x00000001, 0x00000001, 0x00000002, 0x00000001, 0x00000001,
    +  0x00000000, 0x01000002, 0x00000000, 0x01000001, 0x00000000, 0x00010002,
    +  0x00000000, 0x00010001, 0x00000000, 0x00008002, 0x00000000, 0x00008001,
    +  0x00000000, 0x00000003, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8002,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff0002, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xff000001, 0xffffffff, 0xa9cb1d26,
    +  0xffffffff, 0x48892a0c, 0xffffffff, 0x00000002, 0xffffffff, 0x00000001,
    +  0xffff0000, 0x00000002, 0xffff0000, 0x00000001, 0xfff00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0xa9cb1d24, 0x48892a0c, 0x80000000, 0x00000002,
    +  0x80000000, 0x00000002, 0x48892a0a, 0xa9cb1d27, 0x00100000, 0x00000003,
    +  0x00100000, 0x00000002, 0x00010000, 0x00000003, 0x00010000, 0x00000002,
    +  0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000000, 0x01000003,
    +  0x00000000, 0x01000002, 0x00000000, 0x00010003, 0x00000000, 0x00010002,
    +  0x00000000, 0x00008003, 0x00000000, 0x00008002, 0x00000000, 0x00000004,
    +  0x00000000, 0x00000003, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8003, 0xffffffff, 0xffff8002,
    +  0xffffffff, 0xffff0003, 0xffffffff, 0xffff0002, 0xffffffff, 0xff000003,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xa9cb1d27, 0xffffffff, 0x48892a0d,
    +  0xffffffff, 0x00000003, 0xffffffff, 0x00000002, 0xffff0000, 0x00000003,
    +  0xffff0000, 0x00000002, 0xfff00000, 0x00000003, 0xfff00000, 0x00000002,
    +  0xa9cb1d24, 0x48892a0d, 0x80000000, 0x00000003, 0x80000000, 0x00007fff,
    +  0x48892a0a, 0xa9cb9d24, 0x00100000, 0x00008000, 0x00100000, 0x00007fff,
    +  0x00010000, 0x00008000, 0x00010000, 0x00007fff, 0x00000001, 0x00008000,
    +  0x00000001, 0x00007fff, 0x00000000, 0x01008000, 0x00000000, 0x01007fff,
    +  0x00000000, 0x00018000, 0x00000000, 0x00017fff, 0x00000000, 0x00010000,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00008001, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00007ffe, 0x00000000, 0x00007ffd,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xff008000, 0xffffffff, 0xff007fff,
    +  0xffffffff, 0xa9cb9d24, 0xffffffff, 0x4889aa0a, 0xffffffff, 0x00008000,
    +  0xffffffff, 0x00007fff, 0xffff0000, 0x00008000, 0xffff0000, 0x00007fff,
    +  0xfff00000, 0x00008000, 0xfff00000, 0x00007fff, 0xa9cb1d24, 0x4889aa0a,
    +  0x80000000, 0x00008000, 0x80000000, 0x00008000, 0x48892a0a, 0xa9cb9d25,
    +  0x00100000, 0x00008001, 0x00100000, 0x00008000, 0x00010000, 0x00008001,
    +  0x00010000, 0x00008000, 0x00000001, 0x00008001, 0x00000001, 0x00008000,
    +  0x00000000, 0x01008001, 0x00000000, 0x01008000, 0x00000000, 0x00018001,
    +  0x00000000, 0x00018000, 0x00000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008002, 0x00000000, 0x00008001, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00007ffe, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xff008001, 0xffffffff, 0xff008000, 0xffffffff, 0xa9cb9d25,
    +  0xffffffff, 0x4889aa0b, 0xffffffff, 0x00008001, 0xffffffff, 0x00008000,
    +  0xffff0000, 0x00008001, 0xffff0000, 0x00008000, 0xfff00000, 0x00008001,
    +  0xfff00000, 0x00008000, 0xa9cb1d24, 0x4889aa0b, 0x80000000, 0x00008001,
    +  0x80000000, 0x0000ffff, 0x48892a0a, 0xa9cc1d24, 0x00100000, 0x00010000,
    +  0x00100000, 0x0000ffff, 0x00010000, 0x00010000, 0x00010000, 0x0000ffff,
    +  0x00000001, 0x00010000, 0x00000001, 0x0000ffff, 0x00000000, 0x01010000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x00020000, 0x00000000, 0x0001ffff,
    +  0x00000000, 0x00018000, 0x00000000, 0x00017fff, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010000, 0x00000000, 0x0000ffff, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x0000fffd, 0x00000000, 0x00008000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xa9cc1d24, 0xffffffff, 0x488a2a0a,
    +  0xffffffff, 0x00010000, 0xffffffff, 0x0000ffff, 0xffff0000, 0x00010000,
    +  0xffff0000, 0x0000ffff, 0xfff00000, 0x00010000, 0xfff00000, 0x0000ffff,
    +  0xa9cb1d24, 0x488a2a0a, 0x80000000, 0x00010000, 0x80000000, 0x00010000,
    +  0x48892a0a, 0xa9cc1d25, 0x00100000, 0x00010001, 0x00100000, 0x00010000,
    +  0x00010000, 0x00010001, 0x00010000, 0x00010000, 0x00000001, 0x00010001,
    +  0x00000001, 0x00010000, 0x00000000, 0x01010001, 0x00000000, 0x01010000,
    +  0x00000000, 0x00020001, 0x00000000, 0x00020000, 0x00000000, 0x00018001,
    +  0x00000000, 0x00018000, 0x00000000, 0x00010002, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010000, 0x00000000, 0x0000ffff, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff010001, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xa9cc1d25, 0xffffffff, 0x488a2a0b, 0xffffffff, 0x00010001,
    +  0xffffffff, 0x00010000, 0xffff0000, 0x00010001, 0xffff0000, 0x00010000,
    +  0xfff00000, 0x00010001, 0xfff00000, 0x00010000, 0xa9cb1d24, 0x488a2a0b,
    +  0x80000000, 0x00010001, 0x80000000, 0x00ffffff, 0x48892a0a, 0xaacb1d24,
    +  0x00100000, 0x01000000, 0x00100000, 0x00ffffff, 0x00010000, 0x01000000,
    +  0x00010000, 0x00ffffff, 0x00000001, 0x01000000, 0x00000001, 0x00ffffff,
    +  0x00000000, 0x02000000, 0x00000000, 0x01ffffff, 0x00000000, 0x01010000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x01008000, 0x00000000, 0x01007fff,
    +  0x00000000, 0x01000001, 0x00000000, 0x01000000, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x00fffffe, 0x00000000, 0x00fffffd, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00ff0000, 0x00000000, 0x00feffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xaacb1d24,
    +  0xffffffff, 0x49892a0a, 0xffffffff, 0x01000000, 0xffffffff, 0x00ffffff,
    +  0xffff0000, 0x01000000, 0xffff0000, 0x00ffffff, 0xfff00000, 0x01000000,
    +  0xfff00000, 0x00ffffff, 0xa9cb1d24, 0x49892a0a, 0x80000000, 0x01000000,
    +  0x80000000, 0x01000000, 0x48892a0a, 0xaacb1d25, 0x00100000, 0x01000001,
    +  0x00100000, 0x01000000, 0x00010000, 0x01000001, 0x00010000, 0x01000000,
    +  0x00000001, 0x01000001, 0x00000001, 0x01000000, 0x00000000, 0x02000001,
    +  0x00000000, 0x02000000, 0x00000000, 0x01010001, 0x00000000, 0x01010000,
    +  0x00000000, 0x01008001, 0x00000000, 0x01008000, 0x00000000, 0x01000002,
    +  0x00000000, 0x01000001, 0x00000000, 0x01000000, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x00fffffe, 0x00000000, 0x00ff8001, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff0001, 0x00000000, 0x00ff0000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xaacb1d25, 0xffffffff, 0x49892a0b,
    +  0xffffffff, 0x01000001, 0xffffffff, 0x01000000, 0xffff0000, 0x01000001,
    +  0xffff0000, 0x01000000, 0xfff00000, 0x01000001, 0xfff00000, 0x01000000,
    +  0xa9cb1d24, 0x49892a0b, 0x80000000, 0x01000001, 0x80000000, 0x5634e2db,
    +  0x48892a0b, 0x00000000, 0x00100000, 0x5634e2dc, 0x00100000, 0x5634e2db,
    +  0x00010000, 0x5634e2dc, 0x00010000, 0x5634e2db, 0x00000001, 0x5634e2dc,
    +  0x00000001, 0x5634e2db, 0x00000000, 0x5734e2dc, 0x00000000, 0x5734e2db,
    +  0x00000000, 0x5635e2dc, 0x00000000, 0x5635e2db, 0x00000000, 0x563562dc,
    +  0x00000000, 0x563562db, 0x00000000, 0x5634e2dd, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2db, 0x00000000, 0x5634e2da, 0x00000000, 0x5634e2d9,
    +  0x00000000, 0x563462dc, 0x00000000, 0x563462db, 0x00000000, 0x5633e2dc,
    +  0x00000000, 0x5633e2db, 0x00000000, 0x5534e2dc, 0x00000000, 0x5534e2db,
    +  0x00000000, 0x00000000, 0xffffffff, 0x9ebe0ce6, 0xffffffff, 0x5634e2dc,
    +  0xffffffff, 0x5634e2db, 0xffff0000, 0x5634e2dc, 0xffff0000, 0x5634e2db,
    +  0xfff00000, 0x5634e2dc, 0xfff00000, 0x5634e2db, 0xa9cb1d24, 0x9ebe0ce6,
    +  0x80000000, 0x5634e2dc, 0x80000000, 0xb776d5f5, 0x48892a0b, 0x6141f31a,
    +  0x00100000, 0xb776d5f6, 0x00100000, 0xb776d5f5, 0x00010000, 0xb776d5f6,
    +  0x00010000, 0xb776d5f5, 0x00000001, 0xb776d5f6, 0x00000001, 0xb776d5f5,
    +  0x00000000, 0xb876d5f6, 0x00000000, 0xb876d5f5, 0x00000000, 0xb777d5f6,
    +  0x00000000, 0xb777d5f5, 0x00000000, 0xb77755f6, 0x00000000, 0xb77755f5,
    +  0x00000000, 0xb776d5f7, 0x00000000, 0xb776d5f6, 0x00000000, 0xb776d5f5,
    +  0x00000000, 0xb776d5f4, 0x00000000, 0xb776d5f3, 0x00000000, 0xb77655f6,
    +  0x00000000, 0xb77655f5, 0x00000000, 0xb775d5f6, 0x00000000, 0xb775d5f5,
    +  0x00000000, 0xb676d5f6, 0x00000000, 0xb676d5f5, 0x00000000, 0x6141f31a,
    +  0x00000000, 0x00000000, 0xffffffff, 0xb776d5f6, 0xffffffff, 0xb776d5f5,
    +  0xffff0000, 0xb776d5f6, 0xffff0000, 0xb776d5f5, 0xfff00000, 0xb776d5f6,
    +  0xfff00000, 0xb776d5f5, 0xa9cb1d25, 0x00000000, 0x80000000, 0xb776d5f6,
    +  0x80000000, 0xffffffff, 0x48892a0b, 0xa9cb1d24, 0x00100001, 0x00000000,
    +  0x00100000, 0xffffffff, 0x00010001, 0x00000000, 0x00010000, 0xffffffff,
    +  0x00000002, 0x00000000, 0x00000001, 0xffffffff, 0x00000001, 0x01000000,
    +  0x00000001, 0x00ffffff, 0x00000001, 0x00010000, 0x00000001, 0x0000ffff,
    +  0x00000001, 0x00008000, 0x00000001, 0x00007fff, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xfffffffd, 0x00000000, 0xffff8000, 0x00000000, 0xffff7fff,
    +  0x00000000, 0xffff0000, 0x00000000, 0xfffeffff, 0x00000000, 0xff000000,
    +  0x00000000, 0xfeffffff, 0x00000000, 0xa9cb1d24, 0x00000000, 0x48892a0a,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffff0000, 0xffffffff, 0xfff00001, 0x00000000, 0xfff00000, 0xffffffff,
    +  0xa9cb1d25, 0x48892a0a, 0x80000001, 0x00000000, 0x80000001, 0x00000000,
    +  0x48892a0b, 0xa9cb1d25, 0x00100001, 0x00000001, 0x00100001, 0x00000000,
    +  0x00010001, 0x00000001, 0x00010001, 0x00000000, 0x00000002, 0x00000001,
    +  0x00000002, 0x00000000, 0x00000001, 0x01000001, 0x00000001, 0x01000000,
    +  0x00000001, 0x00010001, 0x00000001, 0x00010000, 0x00000001, 0x00008001,
    +  0x00000001, 0x00008000, 0x00000001, 0x00000002, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xffff8001, 0x00000000, 0xffff8000, 0x00000000, 0xffff0001,
    +  0x00000000, 0xffff0000, 0x00000000, 0xff000001, 0x00000000, 0xff000000,
    +  0x00000000, 0xa9cb1d25, 0x00000000, 0x48892a0b, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffff0001, 0x00000001, 0xffff0001, 0x00000000,
    +  0xfff00001, 0x00000001, 0xfff00001, 0x00000000, 0xa9cb1d25, 0x48892a0b,
    +  0x80000001, 0x00000001, 0x8000ffff, 0xffffffff, 0x488a2a0a, 0xa9cb1d24,
    +  0x00110000, 0x00000000, 0x0010ffff, 0xffffffff, 0x00020000, 0x00000000,
    +  0x0001ffff, 0xffffffff, 0x00010001, 0x00000000, 0x00010000, 0xffffffff,
    +  0x00010000, 0x01000000, 0x00010000, 0x00ffffff, 0x00010000, 0x00010000,
    +  0x00010000, 0x0000ffff, 0x00010000, 0x00008000, 0x00010000, 0x00007fff,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x0000ffff, 0xfffffffd, 0x0000ffff, 0xffff8000,
    +  0x0000ffff, 0xffff7fff, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xfffeffff,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xfeffffff, 0x0000ffff, 0xa9cb1d24,
    +  0x0000ffff, 0x48892a0a, 0x0000ffff, 0x00000000, 0x0000fffe, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xfff10000, 0x00000000,
    +  0xfff0ffff, 0xffffffff, 0xa9cc1d24, 0x48892a0a, 0x80010000, 0x00000000,
    +  0x80010000, 0x00000000, 0x488a2a0a, 0xa9cb1d25, 0x00110000, 0x00000001,
    +  0x00110000, 0x00000000, 0x00020000, 0x00000001, 0x00020000, 0x00000000,
    +  0x00010001, 0x00000001, 0x00010001, 0x00000000, 0x00010000, 0x01000001,
    +  0x00010000, 0x01000000, 0x00010000, 0x00010001, 0x00010000, 0x00010000,
    +  0x00010000, 0x00008001, 0x00010000, 0x00008000, 0x00010000, 0x00000002,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x0000ffff, 0xffff8001, 0x0000ffff, 0xffff8000,
    +  0x0000ffff, 0xffff0001, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xff000001,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xa9cb1d25, 0x0000ffff, 0x48892a0b,
    +  0x0000ffff, 0x00000001, 0x0000ffff, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xfff10000, 0x00000001, 0xfff10000, 0x00000000,
    +  0xa9cc1d24, 0x48892a0b, 0x80010000, 0x00000001, 0x800fffff, 0xffffffff,
    +  0x48992a0a, 0xa9cb1d24, 0x00200000, 0x00000000, 0x001fffff, 0xffffffff,
    +  0x00110000, 0x00000000, 0x0010ffff, 0xffffffff, 0x00100001, 0x00000000,
    +  0x00100000, 0xffffffff, 0x00100000, 0x01000000, 0x00100000, 0x00ffffff,
    +  0x00100000, 0x00010000, 0x00100000, 0x0000ffff, 0x00100000, 0x00008000,
    +  0x00100000, 0x00007fff, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x000fffff, 0xfffffffd,
    +  0x000fffff, 0xffff8000, 0x000fffff, 0xffff7fff, 0x000fffff, 0xffff0000,
    +  0x000fffff, 0xfffeffff, 0x000fffff, 0xff000000, 0x000fffff, 0xfeffffff,
    +  0x000fffff, 0xa9cb1d24, 0x000fffff, 0x48892a0a, 0x000fffff, 0x00000000,
    +  0x000ffffe, 0xffffffff, 0x000f0000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xa9db1d24, 0x48892a0a,
    +  0x80100000, 0x00000000, 0x80100000, 0x00000000, 0x48992a0a, 0xa9cb1d25,
    +  0x00200000, 0x00000001, 0x00200000, 0x00000000, 0x00110000, 0x00000001,
    +  0x00110000, 0x00000000, 0x00100001, 0x00000001, 0x00100001, 0x00000000,
    +  0x00100000, 0x01000001, 0x00100000, 0x01000000, 0x00100000, 0x00010001,
    +  0x00100000, 0x00010000, 0x00100000, 0x00008001, 0x00100000, 0x00008000,
    +  0x00100000, 0x00000002, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x000fffff, 0xffff8001,
    +  0x000fffff, 0xffff8000, 0x000fffff, 0xffff0001, 0x000fffff, 0xffff0000,
    +  0x000fffff, 0xff000001, 0x000fffff, 0xff000000, 0x000fffff, 0xa9cb1d25,
    +  0x000fffff, 0x48892a0b, 0x000fffff, 0x00000001, 0x000fffff, 0x00000000,
    +  0x000f0000, 0x00000001, 0x000f0000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xa9db1d24, 0x48892a0b, 0x80100000, 0x00000001,
    +  0xd634e2db, 0xb776d5f5, 0x9ebe0ce6, 0x6141f31a, 0x5644e2db, 0xb776d5f6,
    +  0x5644e2db, 0xb776d5f5, 0x5635e2db, 0xb776d5f6, 0x5635e2db, 0xb776d5f5,
    +  0x5634e2dc, 0xb776d5f6, 0x5634e2dc, 0xb776d5f5, 0x5634e2db, 0xb876d5f6,
    +  0x5634e2db, 0xb876d5f5, 0x5634e2db, 0xb777d5f6, 0x5634e2db, 0xb777d5f5,
    +  0x5634e2db, 0xb77755f6, 0x5634e2db, 0xb77755f5, 0x5634e2db, 0xb776d5f7,
    +  0x5634e2db, 0xb776d5f6, 0x5634e2db, 0xb776d5f5, 0x5634e2db, 0xb776d5f4,
    +  0x5634e2db, 0xb776d5f3, 0x5634e2db, 0xb77655f6, 0x5634e2db, 0xb77655f5,
    +  0x5634e2db, 0xb775d5f6, 0x5634e2db, 0xb775d5f5, 0x5634e2db, 0xb676d5f6,
    +  0x5634e2db, 0xb676d5f5, 0x5634e2db, 0x6141f31a, 0x5634e2db, 0x00000000,
    +  0x5634e2da, 0xb776d5f6, 0x5634e2da, 0xb776d5f5, 0x5633e2db, 0xb776d5f6,
    +  0x5633e2db, 0xb776d5f5, 0x5624e2db, 0xb776d5f6, 0x5624e2db, 0xb776d5f5,
    +  0x00000000, 0x00000000, 0xd634e2db, 0xb776d5f6, 0xffffffff, 0xffffffff,
    +  0xc8892a0a, 0xa9cb1d24, 0x80100000, 0x00000000, 0x800fffff, 0xffffffff,
    +  0x80010000, 0x00000000, 0x8000ffff, 0xffffffff, 0x80000001, 0x00000000,
    +  0x80000000, 0xffffffff, 0x80000000, 0x01000000, 0x80000000, 0x00ffffff,
    +  0x80000000, 0x00010000, 0x80000000, 0x0000ffff, 0x80000000, 0x00008000,
    +  0x80000000, 0x00007fff, 0x80000000, 0x00000001, 0x80000000, 0x00000000,
    +  0x7fffffff, 0xffffffff, 0x7fffffff, 0xfffffffe, 0x7fffffff, 0xfffffffd,
    +  0x7fffffff, 0xffff8000, 0x7fffffff, 0xffff7fff, 0x7fffffff, 0xffff0000,
    +  0x7fffffff, 0xfffeffff, 0x7fffffff, 0xff000000, 0x7fffffff, 0xfeffffff,
    +  0x7fffffff, 0xa9cb1d24, 0x7fffffff, 0x48892a0a, 0x7fffffff, 0x00000000,
    +  0x7ffffffe, 0xffffffff, 0x7fff0000, 0x00000000, 0x7ffeffff, 0xffffffff,
    +  0x7ff00000, 0x00000000, 0x7fefffff, 0xffffffff, 0x29cb1d24, 0x48892a0a,
    +  0x00000000, 0x00000000
    +];
    +toInt32s(TEST_SUB_BITS);
    +
    +var TEST_MUL_BITS = [
    +  0x80000000, 0x00000000, 0x80000000, 0x00000000, 0x1ad92a0a, 0xa9cb1d25,
    +  0x00000000, 0x00000000, 0xd2500000, 0x00000000, 0x00100000, 0x00000000,
    +  0x80000000, 0x00000000, 0x65ae2a0a, 0xa9cb1d25, 0x00110000, 0x00000001,
    +  0x00100000, 0x00000000, 0x00000000, 0x00000000, 0x1d250000, 0x00000000,
    +  0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
    +  0x80000000, 0x00000000, 0xf254472f, 0xa9cb1d25, 0x00100001, 0x00000001,
    +  0x00100000, 0x00000000, 0x00010001, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000000, 0x00000000, 0xa9cb1d25, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x5332f527, 0xcecb1d25,
    +  0x00100000, 0x01000001, 0x00100000, 0x00000000, 0x00010000, 0x01000001,
    +  0x00010000, 0x00000000, 0x01000001, 0x01000001, 0x01000001, 0x00000000,
    +  0x00000000, 0x00000000, 0x0aa9cb1d, 0x25000000, 0x00000000, 0x01000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x01000000, 0x00000000, 0x00000000,
    +  0x01000000, 0x01000000, 0x01000000, 0x00000000, 0x00010000, 0x01000000,
    +  0x80000000, 0x00000000, 0x7293d3d5, 0xc6f01d25, 0x00100000, 0x00010001,
    +  0x00100000, 0x00000000, 0x00010000, 0x00010001, 0x00010000, 0x00000000,
    +  0x00010001, 0x00010001, 0x00010001, 0x00000000, 0x00000100, 0x01010001,
    +  0x00000100, 0x01000000, 0x00000000, 0x00000000, 0x2a0aa9cb, 0x1d250000,
    +  0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00010000, 0x00000000,
    +  0x00000100, 0x00010000, 0x00000100, 0x00000000, 0x00000001, 0x00010000,
    +  0x80000000, 0x00000000, 0xdd8e7ef0, 0x385d9d25, 0x00100000, 0x00008001,
    +  0x00100000, 0x00000000, 0x80010000, 0x00008001, 0x80010000, 0x00000000,
    +  0x00008001, 0x00008001, 0x00008001, 0x00000000, 0x00000080, 0x01008001,
    +  0x00000080, 0x01000000, 0x00000000, 0x80018001, 0x00000000, 0x80010000,
    +  0x00000000, 0x00000000, 0x950554e5, 0x8e928000, 0x00000000, 0x00008000,
    +  0x00000000, 0x00000000, 0x80000000, 0x00008000, 0x80000000, 0x00000000,
    +  0x00008000, 0x00008000, 0x00008000, 0x00000000, 0x00000080, 0x00008000,
    +  0x00000080, 0x00000000, 0x00000000, 0x80008000, 0x00000000, 0x80000000,
    +  0x00000000, 0x40008000, 0x00000000, 0x00000000, 0x91125415, 0x53963a4a,
    +  0x00200000, 0x00000002, 0x00200000, 0x00000000, 0x00020000, 0x00000002,
    +  0x00020000, 0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000000,
    +  0x00000000, 0x02000002, 0x00000000, 0x02000000, 0x00000000, 0x00020002,
    +  0x00000000, 0x00020000, 0x00000000, 0x00010002, 0x00000000, 0x00010000,
    +  0x80000000, 0x00000000, 0x48892a0a, 0xa9cb1d25, 0x00100000, 0x00000001,
    +  0x00100000, 0x00000000, 0x00010000, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x01000001,
    +  0x00000000, 0x01000000, 0x00000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x80000000, 0x00000000, 0xb776d5f5, 0x5634e2db,
    +  0xffefffff, 0xffffffff, 0xfff00000, 0x00000000, 0xfffeffff, 0xffffffff,
    +  0xffff0000, 0x00000000, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x6eedabea, 0xac69c5b6, 0xffdfffff, 0xfffffffe,
    +  0xffe00000, 0x00000000, 0xfffdffff, 0xfffffffe, 0xfffe0000, 0x00000000,
    +  0xfffffffd, 0xfffffffe, 0xfffffffe, 0x00000000, 0xffffffff, 0xfdfffffe,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xfffdfffe, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffefffe, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffffffc,
    +  0xffffffff, 0xfffffffe, 0x00000000, 0x00000000, 0x00000000, 0x00000002,
    +  0x80000000, 0x00000000, 0xb383d525, 0x1b389d25, 0x000fffff, 0xffff8001,
    +  0x00100000, 0x00000000, 0x8000ffff, 0xffff8001, 0x80010000, 0x00000000,
    +  0xffff8000, 0xffff8001, 0xffff8001, 0x00000000, 0xffffff80, 0x00ff8001,
    +  0xffffff80, 0x01000000, 0xffffffff, 0x80008001, 0xffffffff, 0x80010000,
    +  0xffffffff, 0xc0000001, 0xffffffff, 0xc0008000, 0xffffffff, 0xffff0002,
    +  0xffffffff, 0xffff8001, 0x00000000, 0x00000000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x00000000, 0x6afaab1a, 0x716d8000,
    +  0xffffffff, 0xffff8000, 0x00000000, 0x00000000, 0x7fffffff, 0xffff8000,
    +  0x80000000, 0x00000000, 0xffff7fff, 0xffff8000, 0xffff8000, 0x00000000,
    +  0xffffff7f, 0xffff8000, 0xffffff80, 0x00000000, 0xffffffff, 0x7fff8000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xbfff8000, 0xffffffff, 0xc0000000,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff8000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00010000, 0x00000000, 0x3fff8000,
    +  0x80000000, 0x00000000, 0x1e7e803f, 0x8ca61d25, 0x000fffff, 0xffff0001,
    +  0x00100000, 0x00000000, 0x0000ffff, 0xffff0001, 0x00010000, 0x00000000,
    +  0xffff0000, 0xffff0001, 0xffff0001, 0x00000000, 0xffffff00, 0x00ff0001,
    +  0xffffff00, 0x01000000, 0xffffffff, 0x00000001, 0xffffffff, 0x00010000,
    +  0xffffffff, 0x7fff8001, 0xffffffff, 0x80008000, 0xffffffff, 0xfffe0002,
    +  0xffffffff, 0xffff0001, 0x00000000, 0x00000000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x0001fffe, 0x00000000, 0x7ffe8001, 0x00000000, 0x7fff8000,
    +  0x00000000, 0x00000000, 0xd5f55634, 0xe2db0000, 0xffffffff, 0xffff0000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff0000, 0x00000000, 0x00000000,
    +  0xfffeffff, 0xffff0000, 0xffff0000, 0x00000000, 0xfffffeff, 0xffff0000,
    +  0xffffff00, 0x00000000, 0xfffffffe, 0xffff0000, 0xffffffff, 0x00000000,
    +  0xffffffff, 0x7fff0000, 0xffffffff, 0x80000000, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00020000, 0x00000000, 0x7fff0000, 0x00000000, 0x80000000,
    +  0x00000000, 0xffff0000, 0x80000000, 0x00000000, 0x3ddf5eed, 0x84cb1d25,
    +  0x000fffff, 0xff000001, 0x00100000, 0x00000000, 0x0000ffff, 0xff000001,
    +  0x00010000, 0x00000000, 0xff000000, 0xff000001, 0xff000001, 0x00000000,
    +  0xffff0000, 0x00000001, 0xffff0000, 0x01000000, 0xfffffeff, 0xff010001,
    +  0xffffff00, 0x00010000, 0xffffff7f, 0xff008001, 0xffffff80, 0x00008000,
    +  0xffffffff, 0xfe000002, 0xffffffff, 0xff000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01fffffe, 0x0000007f, 0xfeff8001,
    +  0x0000007f, 0xffff8000, 0x000000ff, 0xfeff0001, 0x000000ff, 0xffff0000,
    +  0x00000000, 0x00000000, 0xf55634e2, 0xdb000000, 0xffffffff, 0xff000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
    +  0xfeffffff, 0xff000000, 0xff000000, 0x00000000, 0xfffeffff, 0xff000000,
    +  0xffff0000, 0x00000000, 0xfffffeff, 0xff000000, 0xffffff00, 0x00000000,
    +  0xffffff7f, 0xff000000, 0xffffff80, 0x00000000, 0xffffffff, 0xfe000000,
    +  0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000,
    +  0x00000000, 0x02000000, 0x0000007f, 0xff000000, 0x00000080, 0x00000000,
    +  0x000000ff, 0xff000000, 0x00000100, 0x00000000, 0x0000ffff, 0xff000000,
    +  0x80000000, 0x00000000, 0xbc56e5ef, 0x15ff6759, 0xd24fffff, 0xa9cb1d25,
    +  0xd2500000, 0x00000000, 0x1d24ffff, 0xa9cb1d25, 0x1d250000, 0x00000000,
    +  0xa9cb1d24, 0xa9cb1d25, 0xa9cb1d25, 0x00000000, 0xffa9cb1c, 0xcecb1d25,
    +  0xffa9cb1d, 0x25000000, 0xffffa9ca, 0xc6f01d25, 0xffffa9cb, 0x1d250000,
    +  0xffffd4e5, 0x385d9d25, 0xffffd4e5, 0x8e928000, 0xffffffff, 0x53963a4a,
    +  0xffffffff, 0xa9cb1d25, 0x00000000, 0x00000000, 0x00000000, 0x5634e2db,
    +  0x00000000, 0xac69c5b6, 0x00002b1a, 0x1b389d25, 0x00002b1a, 0x716d8000,
    +  0x00005634, 0x8ca61d25, 0x00005634, 0xe2db0000, 0x005634e2, 0x84cb1d25,
    +  0x005634e2, 0xdb000000, 0x80000000, 0x00000000, 0x74756f10, 0x9f4f5297,
    +  0xa0afffff, 0x48892a0b, 0xa0b00000, 0x00000000, 0x2a0affff, 0x48892a0b,
    +  0x2a0b0000, 0x00000000, 0x48892a0a, 0x48892a0b, 0x48892a0b, 0x00000000,
    +  0xff488929, 0x53892a0b, 0xff48892a, 0x0b000000, 0xffff4888, 0x72942a0b,
    +  0xffff4889, 0x2a0b0000, 0xffffa443, 0xdd8eaa0b, 0xffffa444, 0x95058000,
    +  0xfffffffe, 0x91125416, 0xffffffff, 0x48892a0b, 0x00000000, 0x00000000,
    +  0x00000000, 0xb776d5f5, 0x00000001, 0x6eedabea, 0x00005bba, 0xb383aa0b,
    +  0x00005bbb, 0x6afa8000, 0x0000b776, 0x1e7e2a0b, 0x0000b776, 0xd5f50000,
    +  0x00b776d5, 0x3d892a0b, 0x00b776d5, 0xf5000000, 0x3dc7d297, 0x9f4f5297,
    +  0x80000000, 0x00000000, 0x9ebe0ce5, 0xa9cb1d25, 0x000fffff, 0x00000001,
    +  0x00100000, 0x00000000, 0x0000ffff, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000001, 0x00000000, 0xfeffffff, 0x01000001,
    +  0xff000000, 0x01000000, 0xfffeffff, 0x00010001, 0xffff0000, 0x00010000,
    +  0xffff7fff, 0x00008001, 0xffff8000, 0x00008000, 0xfffffffe, 0x00000002,
    +  0xffffffff, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
    +  0x00000001, 0xfffffffe, 0x00007ffe, 0xffff8001, 0x00007fff, 0xffff8000,
    +  0x0000fffe, 0xffff0001, 0x0000ffff, 0xffff0000, 0x00fffffe, 0xff000001,
    +  0x00ffffff, 0xff000000, 0x5634e2da, 0xa9cb1d25, 0xb776d5f4, 0x48892a0b,
    +  0x00000000, 0x00000000, 0x5634e2db, 0x00000000, 0xffffffff, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xfeffffff, 0x00000000,
    +  0xff000000, 0x00000000, 0xfffeffff, 0x00000000, 0xffff0000, 0x00000000,
    +  0xffff7fff, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffe, 0x00000000,
    +  0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000002, 0x00000000, 0x00007fff, 0x00000000, 0x00008000, 0x00000000,
    +  0x0000ffff, 0x00000000, 0x00010000, 0x00000000, 0x00ffffff, 0x00000000,
    +  0x01000000, 0x00000000, 0x5634e2db, 0x00000000, 0xb776d5f5, 0x00000000,
    +  0xffffffff, 0x00000000, 0x80000000, 0x00000000, 0x2b642a0a, 0xa9cb1d25,
    +  0x000f0000, 0x00000001, 0x00100000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00010000, 0x00000000, 0xffff0001, 0x00000001, 0x00000001, 0x00000000,
    +  0xffff0000, 0x01000001, 0x00000000, 0x01000000, 0xffff0000, 0x00010001,
    +  0x00000000, 0x00010000, 0x7fff0000, 0x00008001, 0x80000000, 0x00008000,
    +  0xfffe0000, 0x00000002, 0xffff0000, 0x00000001, 0x00000000, 0x00000000,
    +  0x0000ffff, 0xffffffff, 0x0001ffff, 0xfffffffe, 0x7ffeffff, 0xffff8001,
    +  0x7fffffff, 0xffff8000, 0xfffeffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xfffeffff, 0xff000001, 0xffffffff, 0xff000000, 0xe2daffff, 0xa9cb1d25,
    +  0xd5f4ffff, 0x48892a0b, 0xfffeffff, 0x00000001, 0xffffffff, 0x00000000,
    +  0x00000000, 0x00000000, 0xe2db0000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
    +  0x7fff0000, 0x00000000, 0x80000000, 0x00000000, 0xfffe0000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
    +  0x00020000, 0x00000000, 0x7fff0000, 0x00000000, 0x80000000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xe2db0000, 0x00000000, 0xd5f50000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x80000000, 0x00000000, 0x76392a0a, 0xa9cb1d25, 0x00000000, 0x00000001,
    +  0x00100000, 0x00000000, 0xfff10000, 0x00000001, 0x00010000, 0x00000000,
    +  0xfff00001, 0x00000001, 0x00000001, 0x00000000, 0xfff00000, 0x01000001,
    +  0x00000000, 0x01000000, 0xfff00000, 0x00010001, 0x00000000, 0x00010000,
    +  0xfff00000, 0x00008001, 0x00000000, 0x00008000, 0xffe00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0x00000000, 0x00000000, 0x000fffff, 0xffffffff,
    +  0x001fffff, 0xfffffffe, 0xffefffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffefffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffefffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0x2dafffff, 0xa9cb1d25, 0x5f4fffff, 0x48892a0b,
    +  0xffefffff, 0x00000001, 0xffffffff, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x2db00000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffe00000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00100000, 0x00000000, 0x00200000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0x2db00000, 0x00000000,
    +  0x5f500000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x80000000, 0x00000000, 0x8a74d669, 0x9f4f5297, 0x4a7b1d24, 0x48892a0b,
    +  0xa0b00000, 0x00000000, 0xd3d61d24, 0x48892a0b, 0x2a0b0000, 0x00000000,
    +  0xf254472f, 0x48892a0b, 0x48892a0b, 0x00000000, 0xce13a64e, 0x53892a0b,
    +  0x2448892a, 0x0b000000, 0xc6ef65ad, 0x72942a0b, 0x1d244889, 0x2a0b0000,
    +  0x385d4168, 0xdd8eaa0b, 0x8e922444, 0x95058000, 0x53963a48, 0x91125416,
    +  0xa9cb1d24, 0x48892a0b, 0x00000000, 0x00000000, 0x5634e2db, 0xb776d5f5,
    +  0xac69c5b7, 0x6eedabea, 0x1b38f8df, 0xb383aa0b, 0x716ddbbb, 0x6afa8000,
    +  0x8ca6d49b, 0x1e7e2a0b, 0xe2dbb776, 0xd5f50000, 0x858293fa, 0x3d892a0b,
    +  0xdbb776d5, 0xf5000000, 0x53c739f0, 0x9f4f5297, 0x22ca6fa5, 0x36ad9c79,
    +  0x6141f319, 0x48892a0b, 0xb776d5f5, 0x00000000, 0x7fc01d24, 0x48892a0b,
    +  0xd5f50000, 0x00000000, 0x091b1d24, 0x48892a0b, 0x5f500000, 0x00000000,
    +  0x80000000, 0x00000000, 0xc8892a0a, 0xa9cb1d25, 0x80100000, 0x00000001,
    +  0x00100000, 0x00000000, 0x80010000, 0x00000001, 0x00010000, 0x00000000,
    +  0x80000001, 0x00000001, 0x00000001, 0x00000000, 0x80000000, 0x01000001,
    +  0x00000000, 0x01000000, 0x80000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x80000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000002,
    +  0x80000000, 0x00000001, 0x00000000, 0x00000000, 0x7fffffff, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0x7fffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0x7fffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0x7fffffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0x7fffffff, 0xa9cb1d25, 0x7fffffff, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0xffffffff, 0x00000000, 0x7fff0000, 0x00000001,
    +  0xffff0000, 0x00000000, 0x7ff00000, 0x00000001, 0xfff00000, 0x00000000,
    +  0x29cb1d24, 0x48892a0b
    +];
    +toInt32s(TEST_MUL_BITS);
    +
    +var TEST_DIV_BITS = [
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x000007ff,
    +  0x00000000, 0x00000800, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x7fffffff, 0x00000000, 0x80000000, 0x0000007f, 0xffff8000,
    +  0x00000080, 0x00000000, 0x00007fff, 0x80007fff, 0x00008000, 0x00000000,
    +  0x0000fffe, 0x0003fff8, 0x00010000, 0x00000000, 0x40000000, 0x00000000,
    +  0x80000000, 0x00000000, 0x80000000, 0x00000000, 0xc0000000, 0x00000000,
    +  0xfffefffd, 0xfffbfff8, 0xffff0000, 0x00000000, 0xffff7fff, 0x7fff8000,
    +  0xffff8000, 0x00000000, 0xffffff7f, 0xffff8000, 0xffffff80, 0x00000000,
    +  0xfffffffe, 0x83e3cc1a, 0xffffffff, 0x4d64985a, 0xffffffff, 0x80000000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffff800, 0xffffffff, 0xfffff800, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000488, 0x00000000, 0x00000488, 0x00000000, 0x00004889,
    +  0x00000000, 0x00004889, 0x00000000, 0x48892a0a, 0x00000000, 0x48892a0a,
    +  0x00000048, 0x8929c220, 0x00000048, 0x892a0aa9, 0x00004888, 0xe181c849,
    +  0x00004889, 0x2a0aa9cb, 0x00009111, 0x31f2efb0, 0x00009112, 0x54155396,
    +  0x24449505, 0x54e58e92, 0x48892a0a, 0xa9cb1d25, 0xb776d5f5, 0x5634e2db,
    +  0xdbbb6afa, 0xab1a716e, 0xffff6eec, 0x89c3bff2, 0xffff6eed, 0xabeaac6a,
    +  0xffffb776, 0x8d6be3a1, 0xffffb776, 0xd5f55635, 0xffffffb7, 0x76d5acce,
    +  0xffffffb7, 0x76d5f557, 0xffffffff, 0x2898cfc6, 0xffffffff, 0x9ac930b4,
    +  0xffffffff, 0xb776d5f6, 0xffffffff, 0xb776d5f6, 0xffffffff, 0xffffb777,
    +  0xffffffff, 0xffffb777, 0xffffffff, 0xfffffb78, 0xffffffff, 0xfffffb78,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x0000000f, 0x00000000, 0x00000010, 0x00000000, 0x000fffff,
    +  0x00000000, 0x00100000, 0x00000000, 0x0ffffff0, 0x00000000, 0x10000000,
    +  0x0000000f, 0xfff0000f, 0x00000010, 0x00000000, 0x0000001f, 0xffc0007f,
    +  0x00000020, 0x00000000, 0x00080000, 0x00000000, 0x00100000, 0x00000001,
    +  0xffefffff, 0xffffffff, 0xfff80000, 0x00000000, 0xffffffdf, 0xffbfff80,
    +  0xffffffe0, 0x00000000, 0xffffffef, 0xffeffff0, 0xfffffff0, 0x00000000,
    +  0xffffffff, 0xeffffff0, 0xffffffff, 0xf0000000, 0xffffffff, 0xffd07c7a,
    +  0xffffffff, 0xffe9ac94, 0xffffffff, 0xfff00000, 0xffffffff, 0xfff00000,
    +  0xffffffff, 0xfffffff0, 0xffffffff, 0xfffffff0, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x0000000f, 0x00000000, 0x00000010,
    +  0x00000000, 0x000fffff, 0x00000000, 0x00100000, 0x00000000, 0x0ffffff0,
    +  0x00000000, 0x10000000, 0x0000000f, 0xfff0000f, 0x00000010, 0x00000000,
    +  0x0000001f, 0xffc0007f, 0x00000020, 0x00000000, 0x00080000, 0x00000000,
    +  0x00100000, 0x00000000, 0xfff00000, 0x00000000, 0xfff80000, 0x00000000,
    +  0xffffffdf, 0xffbfff80, 0xffffffe0, 0x00000000, 0xffffffef, 0xffeffff0,
    +  0xfffffff0, 0x00000000, 0xffffffff, 0xeffffff0, 0xffffffff, 0xf0000000,
    +  0xffffffff, 0xffd07c7a, 0xffffffff, 0xffe9ac94, 0xffffffff, 0xfff00000,
    +  0xffffffff, 0xfff00000, 0xffffffff, 0xfffffff0, 0xffffffff, 0xfffffff0,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01000000, 0x00000000, 0xffff0001,
    +  0x00000001, 0x00000000, 0x00000001, 0xfffc0007, 0x00000002, 0x00000000,
    +  0x00008000, 0x00000000, 0x00010000, 0x00000001, 0xfffeffff, 0xffffffff,
    +  0xffff8000, 0x00000000, 0xfffffffd, 0xfffbfff8, 0xfffffffe, 0x00000000,
    +  0xfffffffe, 0xfffeffff, 0xffffffff, 0x00000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xfffd07c8, 0xffffffff, 0xfffe9aca,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000, 0x00000000, 0x00ffffff, 0x00000000, 0x01000000,
    +  0x00000000, 0xffff0000, 0x00000001, 0x00000000, 0x00000001, 0xfffc0007,
    +  0x00000002, 0x00000000, 0x00008000, 0x00000000, 0x00010000, 0x00000000,
    +  0xffff0000, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffd, 0xfffbfff8,
    +  0xfffffffe, 0x00000000, 0xfffffffe, 0xfffeffff, 0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xfffd07c8,
    +  0xffffffff, 0xfffe9aca, 0xffffffff, 0xffff0000, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000100, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x0001fffc, 0x00000000, 0x00020000, 0x00000000, 0x80000000,
    +  0x00000001, 0x00000001, 0xfffffffe, 0xffffffff, 0xffffffff, 0x80000000,
    +  0xffffffff, 0xfffdfffc, 0xffffffff, 0xfffe0000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffffff00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x000000ff, 0x00000000, 0x00000100, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000, 0x00000000, 0x0001fffc, 0x00000000, 0x00020000,
    +  0x00000000, 0x80000000, 0x00000001, 0x00000000, 0xffffffff, 0x00000000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xfffdfffc, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xffff0000, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffff00, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x000000ff, 0x00000000, 0x00000100, 0x00000000, 0x000001ff,
    +  0x00000000, 0x00000200, 0x00000000, 0x00800000, 0x00000000, 0x01000001,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff800000, 0xffffffff, 0xfffffe00,
    +  0xffffffff, 0xfffffe00, 0xffffffff, 0xffffff00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x000000ff, 0x00000000, 0x00000100,
    +  0x00000000, 0x000001ff, 0x00000000, 0x00000200, 0x00000000, 0x00800000,
    +  0x00000000, 0x01000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff800000,
    +  0xffffffff, 0xfffffe00, 0xffffffff, 0xfffffe00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffff00, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000002,
    +  0x00000000, 0x00008000, 0x00000000, 0x00010001, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000002, 0x00000000, 0x00008000, 0x00000000, 0x00010000,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00004000,
    +  0x00000000, 0x00008001, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffffc000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00004000, 0x00000000, 0x00008000, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffffc000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000002,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffc001, 0xffffffff, 0xffff8001, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00003fff, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffc000, 0xffffffff, 0xffff8000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00004000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff0001, 0x00000000, 0x0000ffff, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000002, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff0000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00000002, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffffff01, 0xffffffff, 0xfffffe01,
    +  0xffffffff, 0xfffffe01, 0xffffffff, 0xff800001, 0xffffffff, 0xff000001,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x007fffff, 0x00000000, 0x00000200,
    +  0x00000000, 0x000001ff, 0x00000000, 0x00000100, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffff01, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xfffffe01, 0xffffffff, 0xfffffe00, 0xffffffff, 0xff800000,
    +  0xffffffff, 0xff000000, 0x00000000, 0x01000000, 0x00000000, 0x00800000,
    +  0x00000000, 0x00000200, 0x00000000, 0x00000200, 0x00000000, 0x00000100,
    +  0x00000000, 0x00000100, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffaa, 0xffffffff, 0xffffffaa, 0xffffffff, 0xffffa9cc,
    +  0xffffffff, 0xffffa9cc, 0xffffffff, 0xffff5398, 0xffffffff, 0xffff5397,
    +  0xffffffff, 0xd4e58e93, 0xffffffff, 0xa9cb1d25, 0x00000000, 0x5634e2db,
    +  0x00000000, 0x2b1a716d, 0x00000000, 0x0000ac6b, 0x00000000, 0x0000ac69,
    +  0x00000000, 0x00005635, 0x00000000, 0x00005634, 0x00000000, 0x00000056,
    +  0x00000000, 0x00000056, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffff49, 0xffffffff, 0xffffff49,
    +  0xffffffff, 0xffff488a, 0xffffffff, 0xffff488a, 0xffffffff, 0xfffe9116,
    +  0xffffffff, 0xfffe9113, 0xffffffff, 0xa4449506, 0xffffffff, 0x48892a0b,
    +  0x00000000, 0xb776d5f5, 0x00000000, 0x5bbb6afa, 0x00000000, 0x00016ef0,
    +  0x00000000, 0x00016eed, 0x00000000, 0x0000b777, 0x00000000, 0x0000b776,
    +  0x00000000, 0x000000b7, 0x00000000, 0x000000b7, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffff01,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xfffe0004, 0xffffffff, 0xfffe0001, 0xffffffff, 0x80000001,
    +  0xffffffff, 0x00000001, 0x00000000, 0xffffffff, 0x00000000, 0x7fffffff,
    +  0x00000000, 0x00020004, 0x00000000, 0x0001ffff, 0x00000000, 0x00010001,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00000100, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000002, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffffff00, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xfffe0004, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000000, 0x80000000, 0x00000000, 0x00020004, 0x00000000, 0x00020000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00010000, 0x00000000, 0x00000100,
    +  0x00000000, 0x00000100, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xff000001, 0xffffffff, 0xff000001,
    +  0xffffffff, 0x00010000, 0xffffffff, 0x00000001, 0xfffffffe, 0x0003fff9,
    +  0xfffffffe, 0x00000001, 0xffff8000, 0x00000001, 0xffff0000, 0x00000001,
    +  0x0000ffff, 0xffffffff, 0x00007fff, 0xffffffff, 0x00000002, 0x00040008,
    +  0x00000001, 0xffffffff, 0x00000001, 0x00010001, 0x00000000, 0xffffffff,
    +  0x00000000, 0x01000001, 0x00000000, 0x00ffffff, 0x00000000, 0x0002f838,
    +  0x00000000, 0x00016536, 0x00000000, 0x00010000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffffffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0xffffffff, 0x00010000, 0xffffffff, 0x00000000,
    +  0xfffffffe, 0x0003fff9, 0xfffffffe, 0x00000000, 0xffff8000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00010000, 0x00000000, 0x00008000, 0x00000000,
    +  0x00000002, 0x00040008, 0x00000002, 0x00000000, 0x00000001, 0x00010001,
    +  0x00000001, 0x00000000, 0x00000000, 0x01000001, 0x00000000, 0x01000000,
    +  0x00000000, 0x0002f838, 0x00000000, 0x00016536, 0x00000000, 0x00010000,
    +  0x00000000, 0x00010000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xfffffff1,
    +  0xffffffff, 0xfffffff1, 0xffffffff, 0xfff00001, 0xffffffff, 0xfff00001,
    +  0xffffffff, 0xf0000010, 0xffffffff, 0xf0000001, 0xfffffff0, 0x000ffff1,
    +  0xfffffff0, 0x00000001, 0xffffffe0, 0x003fff81, 0xffffffe0, 0x00000001,
    +  0xfff80000, 0x00000001, 0xfff00000, 0x00000001, 0x000fffff, 0xffffffff,
    +  0x0007ffff, 0xffffffff, 0x00000020, 0x00400080, 0x0000001f, 0xffffffff,
    +  0x00000010, 0x00100010, 0x0000000f, 0xffffffff, 0x00000000, 0x10000010,
    +  0x00000000, 0x0fffffff, 0x00000000, 0x002f8386, 0x00000000, 0x0016536c,
    +  0x00000000, 0x00100000, 0x00000000, 0x000fffff, 0x00000000, 0x00000010,
    +  0x00000000, 0x0000000f, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffffff1, 0xffffffff, 0xfffffff0, 0xffffffff, 0xfff00001,
    +  0xffffffff, 0xfff00000, 0xffffffff, 0xf0000010, 0xffffffff, 0xf0000000,
    +  0xfffffff0, 0x000ffff1, 0xfffffff0, 0x00000000, 0xffffffe0, 0x003fff81,
    +  0xffffffe0, 0x00000000, 0xfff80000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00100000, 0x00000000, 0x00080000, 0x00000000, 0x00000020, 0x00400080,
    +  0x00000020, 0x00000000, 0x00000010, 0x00100010, 0x00000010, 0x00000000,
    +  0x00000000, 0x10000010, 0x00000000, 0x10000000, 0x00000000, 0x002f8386,
    +  0x00000000, 0x0016536c, 0x00000000, 0x00100000, 0x00000000, 0x00100000,
    +  0x00000000, 0x00000010, 0x00000000, 0x00000010, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffa9d,
    +  0xffffffff, 0xfffffa9d, 0xffffffff, 0xffffa9cc, 0xffffffff, 0xffffa9cc,
    +  0xffffffff, 0xa9cb1d25, 0xffffffff, 0xa9cb1d25, 0xffffffa9, 0xcb1d7a7e,
    +  0xffffffa9, 0xcb1d2449, 0xffffa9cb, 0x7358d531, 0xffffa9cb, 0x1d24488a,
    +  0xffff5397, 0x93196ae0, 0xffff5396, 0x3a489113, 0xd4e58e92, 0x24449506,
    +  0xa9cb1d24, 0x48892a0b, 0x5634e2db, 0xb776d5f5, 0x2b1a716d, 0xdbbb6afa,
    +  0x0000ac6b, 0x1e8dac09, 0x0000ac69, 0xc5b76eed, 0x00005635, 0x3910f087,
    +  0x00005634, 0xe2dbb776, 0x00000056, 0x34e331ec, 0x00000056, 0x34e2dbb7,
    +  0x00000001, 0x00000002, 0x00000000, 0x784a3552, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2db, 0x00000000, 0x00005634, 0x00000000, 0x00005634,
    +  0x00000000, 0x00000563, 0x00000000, 0x00000563, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffff801, 0xffffffff, 0xfffff801, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0x80000001, 0xffffffff, 0x80000001,
    +  0xffffff80, 0x00008000, 0xffffff80, 0x00000001, 0xffff8000, 0x7fff8001,
    +  0xffff8000, 0x00000001, 0xffff0001, 0xfffc0008, 0xffff0000, 0x00000001,
    +  0xc0000000, 0x00000001, 0x80000000, 0x00000001, 0x7fffffff, 0xffffffff,
    +  0x3fffffff, 0xffffffff, 0x00010002, 0x00040008, 0x0000ffff, 0xffffffff,
    +  0x00008000, 0x80008000, 0x00007fff, 0xffffffff, 0x00000080, 0x00008000,
    +  0x0000007f, 0xffffffff, 0x00000001, 0x7c1c33e6, 0x00000000, 0xb29b67a6,
    +  0x00000000, 0x80000000, 0x00000000, 0x7fffffff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00000800, 0x00000000, 0x000007ff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001
    +];
    +toInt32s(TEST_DIV_BITS);
    +
    +var TEST_STRINGS = [
    +  '-9223372036854775808',
    +  '-5226755067826871589',
    +  '-4503599627370497',
    +  '-4503599627370496',
    +  '-281474976710657',
    +  '-281474976710656',
    +  '-4294967297',
    +  '-4294967296',
    +  '-16777217',
    +  '-16777216',
    +  '-65537',
    +  '-65536',
    +  '-32769',
    +  '-32768',
    +  '-2',
    +  '-1',
    +  '0',
    +  '1',
    +  '2',
    +  '32767',
    +  '32768',
    +  '65535',
    +  '65536',
    +  '16777215',
    +  '16777216',
    +  '1446306523',
    +  '3078018549',
    +  '4294967295',
    +  '4294967296',
    +  '281474976710655',
    +  '281474976710656',
    +  '4503599627370495',
    +  '4503599627370496',
    +  '6211839219354490357',
    +  '9223372036854775807'
    +];
    +
    +function testToFromBits() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    assertEquals(TEST_BITS[i], val.getBits(1));
    +    assertEquals(TEST_BITS[i + 1], val.getBits(0));
    +  }
    +}
    +
    +function testToFromInt() {
    +  for (var i = 0; i < TEST_BITS.length; i += 1) {
    +    var val = goog.math.Integer.fromInt(TEST_BITS[i]);
    +    assertEquals(TEST_BITS[i], val.toInt());
    +  }
    +}
    +
    +function testToFromNumber() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var num = TEST_BITS[i] * Math.pow(2, 32) + TEST_BITS[i + 1] >= 0 ?
    +        TEST_BITS[i + 1] : Math.pow(2, 32) + TEST_BITS[i + 1];
    +    var val = goog.math.Integer.fromNumber(num);
    +    assertEquals(num, val.toNumber());
    +  }
    +}
    +
    +function testShorten() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = new goog.math.Integer([TEST_BITS[i + 1], TEST_BITS[i]], 0);
    +    val = val.shorten(64);
    +    assertEquals(TEST_BITS[i], val.getBits(1));
    +    assertEquals(TEST_BITS[i + 1], val.getBits(0));
    +  }
    +
    +  val = new goog.math.Integer.fromBits([0x20000000, 0x01010000], 0);
    +
    +  var val58 = val.shorten(58);
    +  assertEquals(0x01010000 | 0, val58.getBits(1));
    +  assertEquals(0x20000000 | 0, val58.getBits(0));
    +
    +  var val57 = val.shorten(57);
    +  assertEquals(0xFF010000 | 0, val57.getBits(1));
    +  assertEquals(0x20000000 | 0, val57.getBits(0));
    +
    +  var val56 = val.shorten(56);
    +  assertEquals(0x00010000 | 0, val56.getBits(1));
    +  assertEquals(0x20000000 | 0, val56.getBits(0));
    +
    +  var val50 = val.shorten(50);
    +  assertEquals(0x00010000 | 0, val50.getBits(1));
    +  assertEquals(0x20000000 | 0, val50.getBits(0));
    +
    +  var val49 = val.shorten(49);
    +  assertEquals(0xFFFF0000 | 0, val49.getBits(1));
    +  assertEquals(0x20000000 | 0, val49.getBits(0));
    +
    +  var val32 = val.shorten(32);
    +  assertEquals(0x00000000 | 0, val32.getBits(1));
    +  assertEquals(0x20000000 | 0, val32.getBits(0));
    +
    +  var val31 = val.shorten(31);
    +  assertEquals(0x00000000 | 0, val31.getBits(1));
    +  assertEquals(0x20000000 | 0, val31.getBits(0));
    +
    +  var val30 = val.shorten(30);
    +  assertEquals(0xFFFFFFFF | 0, val30.getBits(1));
    +  assertEquals(0xE0000000 | 0, val30.getBits(0));
    +
    +  var val29 = val.shorten(29);
    +  assertEquals(0x00000000 | 0, val29.getBits(1));
    +  assertEquals(0x00000000 | 0, val29.getBits(0));
    +}
    +
    +function testIsZero() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    assertEquals(TEST_BITS[i] == 0 && TEST_BITS[i + 1] == 0, val.isZero());
    +  }
    +}
    +
    +function testIsNegative() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    assertEquals((TEST_BITS[i] >> 31) != 0, val.isNegative());
    +  }
    +}
    +
    +function testIsOdd() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    assertEquals((TEST_BITS[i + 1] & 1) != 0, val.isOdd());
    +  }
    +}
    +
    +function createTestComparisons(i) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      assertEquals(i == j, vi.equals(vj));
    +      assertEquals(i != j, vi.notEquals(vj));
    +      assertEquals(i < j, vi.lessThan(vj));
    +      assertEquals(i <= j, vi.lessThanOrEqual(vj));
    +      assertEquals(i > j, vi.greaterThan(vj));
    +      assertEquals(i >= j, vi.greaterThanOrEqual(vj));
    +    }
    +  };
    +}
    +
    +// Here and below, we translate one conceptual test (e.g., "testComparisons")
    +// into a number of test functions that will be run separately by jsunit. This
    +// is necessary because, in some testing configurations, the full combined test
    +// can take so long that it times out. These smaller tests run much faster.
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testComparisons' + i] = createTestComparisons(i);
    +}
    +
    +function createTestBitOperations(i) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    assertEquals(~TEST_BITS[i], vi.not().getBits(1));
    +    assertEquals(~TEST_BITS[i + 1], vi.not().getBits(0));
    +
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      assertEquals(TEST_BITS[i] & TEST_BITS[j], vi.and(vj).getBits(1));
    +      assertEquals(TEST_BITS[i + 1] & TEST_BITS[j + 1], vi.and(vj).getBits(0));
    +      assertEquals(TEST_BITS[i] | TEST_BITS[j], vi.or(vj).getBits(1));
    +      assertEquals(TEST_BITS[i + 1] | TEST_BITS[j + 1], vi.or(vj).getBits(0));
    +      assertEquals(TEST_BITS[i] ^ TEST_BITS[j], vi.xor(vj).getBits(1));
    +      assertEquals(TEST_BITS[i + 1] ^ TEST_BITS[j + 1], vi.xor(vj).getBits(0));
    +    }
    +
    +    assertEquals(TEST_BITS[i], vi.shiftLeft(0).getBits(1));
    +    assertEquals(TEST_BITS[i + 1], vi.shiftLeft(0).getBits(0));
    +    assertEquals(TEST_BITS[i], vi.shiftRight(0).getBits(1));
    +    assertEquals(TEST_BITS[i + 1], vi.shiftRight(0).getBits(0));
    +
    +    for (var len = 1; len < 64; ++len) {
    +      if (len < 32) {
    +        assertEquals((TEST_BITS[i] << len) | (TEST_BITS[i + 1] >>> (32 - len)),
    +                     vi.shiftLeft(len).getBits(1));
    +        assertEquals(TEST_BITS[i + 1] << len, vi.shiftLeft(len).getBits(0));
    +
    +        assertEquals(TEST_BITS[i] >> len, vi.shiftRight(len).getBits(1));
    +        assertEquals((TEST_BITS[i + 1] >>> len) | (TEST_BITS[i] << (32 - len)),
    +                     vi.shiftRight(len).getBits(0));
    +      } else {
    +        assertEquals(TEST_BITS[i + 1] << (len - 32),
    +                     vi.shiftLeft(len).getBits(1));
    +        assertEquals(0, vi.shiftLeft(len).getBits(0));
    +
    +        assertEquals(TEST_BITS[i] >= 0 ? 0 : -1, vi.shiftRight(len).getBits(1));
    +        assertEquals(TEST_BITS[i] >> (len - 32), vi.shiftRight(len).getBits(0));
    +      }
    +    }
    +
    +    assertEquals(0, vi.shiftLeft(64).getBits(1));
    +    assertEquals(0, vi.shiftLeft(64).getBits(0));
    +    assertEquals(TEST_BITS[i] & (1 << 31) ? -1 : 0,
    +                 vi.shiftRight(64).getBits(1));
    +    assertEquals(TEST_BITS[i] & (1 << 31) ? -1 : 0,
    +                 vi.shiftRight(64).getBits(0));
    +  };
    +}
    +
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testBitOperations' + i] = createTestBitOperations(i);
    +}
    +
    +function testNegation() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    if (TEST_BITS[i + 1] == 0) {
    +      assertEquals((~TEST_BITS[i] + 1) | 0, vi.negate().getBits(1));
    +      assertEquals(0, vi.negate().getBits(0));
    +    } else {
    +      assertEquals(~TEST_BITS[i], vi.negate().getBits(1));
    +      assertEquals((~TEST_BITS[i + 1] + 1) | 0, vi.negate().getBits(0));
    +    }
    +  }
    +}
    +
    +function createTestAdd(i, count) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    for (var j = 0; j < i; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      var result = vi.add(vj);
    +      assertEquals(TEST_ADD_BITS[count++], result.getBits(1));
    +      assertEquals(TEST_ADD_BITS[count++], result.getBits(0));
    +    }
    +  };
    +}
    +
    +var countAdd = 0;
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testAdd' + i] = createTestAdd(i, countAdd);
    +  countAdd += i;
    +}
    +
    +function createTestSubtract(i, count) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      var result = vi.subtract(vj);
    +      assertEquals(TEST_SUB_BITS[count++], result.getBits(1));
    +      assertEquals(TEST_SUB_BITS[count++], result.getBits(0));
    +    }
    +  };
    +}
    +
    +var countSubtract = 0;
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testSubtract' + i] = createTestSubtract(i, countSubtract);
    +  countSubtract += TEST_BITS.length;
    +}
    +
    +function createTestMultiply(i, count) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    for (var j = 0; j < i; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      var result = vi.multiply(vj);
    +      assertEquals(TEST_MUL_BITS[count++], result.getBits(1));
    +      assertEquals(TEST_MUL_BITS[count++], result.getBits(0));
    +    }
    +  };
    +}
    +
    +var countMultiply = 0;
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testMultiply' + i] = createTestMultiply(i, countMultiply);
    +  countMultiply += i;
    +}
    +
    +function createTestDivMod(i, count) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +      if (!vj.isZero()) {
    +        var divResult = vi.divide(vj);
    +        assertEquals(TEST_DIV_BITS[count++], divResult.getBits(1));
    +        assertEquals(TEST_DIV_BITS[count++], divResult.getBits(0));
    +
    +        var modResult = vi.modulo(vj);
    +        var combinedResult = divResult.multiply(vj).add(modResult);
    +        assertTrue(vi.equals(combinedResult));
    +      }
    +    }
    +  };
    +}
    +
    +var countPerDivModCall = 0;
    +for (var j = 0; j < TEST_BITS.length; j += 2) {
    +  var vj = goog.math.Integer.fromBits([TEST_BITS[j + 1], TEST_BITS[j]]);
    +  if (!vj.isZero()) {
    +    countPerDivModCall += 2;
    +  }
    +}
    +
    +var countDivMod = 0;
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testDivMod' + i] = createTestDivMod(i, countDivMod);
    +  countDivMod += countPerDivModCall;
    +}
    +
    +function createTestToFromString(i) {
    +  return function() {
    +    var vi = goog.math.Integer.fromBits([TEST_BITS[i + 1], TEST_BITS[i]]);
    +    var str = vi.toString(10);
    +    assertEquals(TEST_STRINGS[i / 2], str);
    +    assertEquals(TEST_BITS[i],
    +                 goog.math.Integer.fromString(str, 10).getBits(1));
    +    assertEquals(TEST_BITS[i + 1],
    +                 goog.math.Integer.fromString(str, 10).getBits(0));
    +
    +    for (var radix = 2; radix <= 36; ++radix) {
    +      var result = vi.toString(radix);
    +      assertEquals(TEST_BITS[i],
    +                   goog.math.Integer.fromString(result, radix).getBits(1));
    +      assertEquals(TEST_BITS[i + 1],
    +                   goog.math.Integer.fromString(result, radix).getBits(0));
    +    }
    +  };
    +}
    +
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testToFromString' + i] = createTestToFromString(i);
    +}
    +
    +function testBigMultiply() {
    +  var a = goog.math.Integer.fromString('2389428394283434234234');
    +  var b = goog.math.Integer.fromString('895489472863784783');
    +  assertEquals('2139707973242632227811083664960586861222',
    +               a.multiply(b).toString());
    +  assertEquals('2139707973242632227811083664960586861222',
    +               b.multiply(a).toString());
    +
    +  a = goog.math.Integer.fromString('123940932409302930429304');
    +  b = goog.math.Integer.fromString('-23940239409234');
    +  assertEquals('-2967175594482401511466585794961793136',
    +               a.multiply(b).toString());
    +
    +  a = goog.math.Integer.fromString('-4895849540949');
    +  b = goog.math.Integer.fromString('5906390354334334989');
    +  assertEquals('-28916798504933355408364838964561',
    +               a.multiply(b).toString());
    +
    +  a = goog.math.Integer.fromString('-23489238492334893');
    +  b = goog.math.Integer.fromString('-2930482394829348293489234');
    +  assertEquals('68834799869735267747353413198446618041962',
    +               a.multiply(b).toString());
    +
    +  a = goog.math.Integer.fromString('-39403940');
    +  b = goog.math.Integer.fromString('-90689586573473848347384834');
    +  assertEquals('3573527027965969111849451155845960',
    +               a.multiply(b).toString());
    +}
    +
    +function testBigShift() {
    +  var a = goog.math.Integer.fromString('3735928559');
    +  assertEquals('591981510028266767381876356163880091648',
    +               a.shiftLeft(97).toString());
    +  assertEquals('-591981510028266767381876356163880091648',
    +               a.negate().shiftLeft(97).toString());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/interpolator1.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/interpolator1.js
    new file mode 100644
    index 0000000000..d654979011
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/interpolator1.js
    @@ -0,0 +1,64 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The base interface for one-dimensional data interpolation.
    + *
    + */
    +
    +goog.provide('goog.math.interpolator.Interpolator1');
    +
    +
    +
    +/**
    + * An interface for one dimensional data interpolation.
    + * @interface
    + */
    +goog.math.interpolator.Interpolator1 = function() {
    +};
    +
    +
    +/**
    + * Sets the data to be interpolated. Note that the data points are expected
    + * to be sorted according to their abscissa values and not have duplicate
    + * values. E.g. calling setData([0, 0, 1], [1, 1, 3]) may give undefined
    + * results, the correct call should be setData([0, 1], [1, 3]).
    + * Calling setData multiple times does not merge the data samples. The last
    + * call to setData is the one used when computing the interpolation.
    + * @param {!Array<number>} x The abscissa of the data points.
    + * @param {!Array<number>} y The ordinate of the data points.
    + */
    +goog.math.interpolator.Interpolator1.prototype.setData;
    +
    +
    +/**
    + * Computes the interpolated value at abscissa x. If x is outside the range
    + * of the data points passed in setData, the value is extrapolated.
    + * @param {number} x The abscissa to sample at.
    + * @return {number} The interpolated value at abscissa x.
    + */
    +goog.math.interpolator.Interpolator1.prototype.interpolate;
    +
    +
    +/**
    + * Computes the inverse interpolator. That is, it returns invInterp s.t.
    + * this.interpolate(invInterp.interpolate(t))) = t. Note that the inverse
    + * interpolator is only well defined if the data being interpolated is
    + * 'invertible', i.e. it represents a bijective function.
    + * In addition, the returned interpolator is only guaranteed to give the exact
    + * inverse at the input data passed in getData.
    + * If 'this' has no data, the returned Interpolator will be empty as well.
    + * @return {!goog.math.interpolator.Interpolator1} The inverse interpolator.
    + */
    +goog.math.interpolator.Interpolator1.prototype.getInverse;
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1.js
    new file mode 100644
    index 0000000000..ba4824f735
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1.js
    @@ -0,0 +1,84 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A one dimensional linear interpolator.
    + *
    + */
    +
    +goog.provide('goog.math.interpolator.Linear1');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.math.interpolator.Interpolator1');
    +
    +
    +
    +/**
    + * A one dimensional linear interpolator.
    + * @implements {goog.math.interpolator.Interpolator1}
    + * @constructor
    + * @final
    + */
    +goog.math.interpolator.Linear1 = function() {
    +  /**
    +   * The abscissa of the data points.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.x_ = [];
    +
    +  /**
    +   * The ordinate of the data points.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.y_ = [];
    +};
    +
    +
    +/** @override */
    +goog.math.interpolator.Linear1.prototype.setData = function(x, y) {
    +  goog.asserts.assert(x.length == y.length,
    +      'input arrays to setData should have the same length');
    +  if (x.length == 1) {
    +    this.x_ = [x[0], x[0] + 1];
    +    this.y_ = [y[0], y[0]];
    +  } else {
    +    this.x_ = x.slice();
    +    this.y_ = y.slice();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.math.interpolator.Linear1.prototype.interpolate = function(x) {
    +  var pos = goog.array.binarySearch(this.x_, x);
    +  if (pos < 0) {
    +    pos = -pos - 2;
    +  }
    +  pos = goog.math.clamp(pos, 0, this.x_.length - 2);
    +
    +  var progress = (x - this.x_[pos]) / (this.x_[pos + 1] - this.x_[pos]);
    +  return goog.math.lerp(this.y_[pos], this.y_[pos + 1], progress);
    +};
    +
    +
    +/** @override */
    +goog.math.interpolator.Linear1.prototype.getInverse = function() {
    +  var interpolator = new goog.math.interpolator.Linear1();
    +  interpolator.setData(this.y_, this.x_);
    +  return interpolator;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.html b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.html
    new file mode 100644
    index 0000000000..3ab7978f54
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.interpolator.Linear1
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.interpolator.Linear1Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.js
    new file mode 100644
    index 0000000000..472c991b50
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/linear1_test.js
    @@ -0,0 +1,84 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.interpolator.Linear1Test');
    +goog.setTestOnly('goog.math.interpolator.Linear1Test');
    +
    +goog.require('goog.math.interpolator.Linear1');
    +goog.require('goog.testing.jsunit');
    +
    +function testLinear() {
    +  // Test special case with no data to interpolate.
    +  var x = [];
    +  var y = [];
    +  var interp = new goog.math.interpolator.Linear1();
    +  interp.setData(x, y);
    +  assertTrue(isNaN(interp.interpolate(1)));
    +
    +  // Test special case with 1 data point.
    +  x = [0];
    +  y = [3];
    +  interp = new goog.math.interpolator.Linear1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(3, interp.interpolate(1), 1e-4);
    +
    +  // Test general case.
    +  x = [0, 1, 3, 6, 7];
    +  y = [0, 0, 0, 0, 0];
    +  for (var i = 0; i < x.length; ++i) {
    +    y[i] = Math.sin(x[i]);
    +  }
    +  interp = new goog.math.interpolator.Linear1();
    +  interp.setData(x, y);
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var expected = [0, 0.4207, 0.8415, 0.4913, 0.1411, 0.0009, -0.1392,
    +    -0.2794, 0.657];
    +  var result = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    result[i] = interp.interpolate(xi[i]);
    +  }
    +  assertElementsRoughlyEqual(expected, result, 1e-4);
    +}
    +
    +
    +function testOutOfBounds() {
    +  var x = [0, 1, 2];
    +  var y = [2, 5, 4];
    +  var interp = new goog.math.interpolator.Linear1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(interp.interpolate(-1), -1, 1e-4);
    +  assertRoughlyEquals(interp.interpolate(4), 2, 1e-4);
    +}
    +
    +
    +function testInverse() {
    +  var x = [0, 1, 3, 6, 7];
    +  var y = [0, 2, 7, 8, 10];
    +
    +  var interp = new goog.math.interpolator.Linear1();
    +  interp.setData(x, y);
    +  var invInterp = interp.getInverse();
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var yi = [0, 1, 2, 4.5, 7, 7.3333, 7.6667, 8, 10];
    +  var resultX = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  var resultY = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    resultY[i] = interp.interpolate(xi[i]);
    +    resultX[i] = invInterp.interpolate(yi[i]);
    +  }
    +  assertElementsRoughlyEqual(xi, resultX, 1e-4);
    +  assertElementsRoughlyEqual(yi, resultY, 1e-4);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1.js
    new file mode 100644
    index 0000000000..0c3a56ea38
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1.js
    @@ -0,0 +1,82 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A one dimensional monotone cubic spline interpolator.
    + *
    + * See http://en.wikipedia.org/wiki/Monotone_cubic_interpolation.
    + *
    + */
    +
    +goog.provide('goog.math.interpolator.Pchip1');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.interpolator.Spline1');
    +
    +
    +
    +/**
    + * A one dimensional monotone cubic spline interpolator.
    + * @extends {goog.math.interpolator.Spline1}
    + * @constructor
    + * @final
    + */
    +goog.math.interpolator.Pchip1 = function() {
    +  goog.math.interpolator.Pchip1.base(this, 'constructor');
    +};
    +goog.inherits(goog.math.interpolator.Pchip1, goog.math.interpolator.Spline1);
    +
    +
    +/** @override */
    +goog.math.interpolator.Pchip1.prototype.computeDerivatives = function(
    +    dx, slope) {
    +  var len = dx.length;
    +  var deriv = new Array(len + 1);
    +  for (var i = 1; i < len; ++i) {
    +    if (goog.math.sign(slope[i - 1]) * goog.math.sign(slope[i]) <= 0) {
    +      deriv[i] = 0;
    +    } else {
    +      var w1 = 2 * dx[i] + dx[i - 1];
    +      var w2 = dx[i] + 2 * dx[i - 1];
    +      deriv[i] = (w1 + w2) / (w1 / slope[i - 1] + w2 / slope[i]);
    +    }
    +  }
    +  deriv[0] = this.computeDerivativeAtBoundary_(
    +      dx[0], dx[1], slope[0], slope[1]);
    +  deriv[len] = this.computeDerivativeAtBoundary_(
    +      dx[len - 1], dx[len - 2], slope[len - 1], slope[len - 2]);
    +  return deriv;
    +};
    +
    +
    +/**
    + * Computes the derivative of a data point at a boundary.
    + * @param {number} dx0 The spacing of the 1st data point.
    + * @param {number} dx1 The spacing of the 2nd data point.
    + * @param {number} slope0 The slope of the 1st data point.
    + * @param {number} slope1 The slope of the 2nd data point.
    + * @return {number} The derivative at the 1st data point.
    + * @private
    + */
    +goog.math.interpolator.Pchip1.prototype.computeDerivativeAtBoundary_ = function(
    +    dx0, dx1, slope0, slope1) {
    +  var deriv = ((2 * dx0 + dx1) * slope0 - dx0 * slope1) / (dx0 + dx1);
    +  if (goog.math.sign(deriv) != goog.math.sign(slope0)) {
    +    deriv = 0;
    +  } else if (goog.math.sign(slope0) != goog.math.sign(slope1) &&
    +      Math.abs(deriv) > Math.abs(3 * slope0)) {
    +    deriv = 3 * slope0;
    +  }
    +  return deriv;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.html b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.html
    new file mode 100644
    index 0000000000..236648dccc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.interpolator.Pchip1
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.interpolator.Pchip1Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.js
    new file mode 100644
    index 0000000000..f7920bbd7e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/pchip1_test.js
    @@ -0,0 +1,71 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.interpolator.Pchip1Test');
    +goog.setTestOnly('goog.math.interpolator.Pchip1Test');
    +
    +goog.require('goog.math.interpolator.Pchip1');
    +goog.require('goog.testing.jsunit');
    +
    +function testSpline() {
    +  var x = [0, 1, 3, 6, 7];
    +  var y = [0, 0, 0, 0, 0];
    +
    +  for (var i = 0; i < x.length; ++i) {
    +    y[i] = Math.sin(x[i]);
    +  }
    +  var interp = new goog.math.interpolator.Pchip1();
    +  interp.setData(x, y);
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var expected = [0, 0.5756, 0.8415, 0.5428, 0.1411, -0.0595, -0.2162,
    +    -0.2794, 0.657];
    +  var result = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    result[i] = interp.interpolate(xi[i]);
    +  }
    +  assertElementsRoughlyEqual(expected, result, 1e-4);
    +}
    +
    +
    +function testOutOfBounds() {
    +  var x = [0, 1, 2, 4];
    +  var y = [2, 5, 4, 2];
    +  var interp = new goog.math.interpolator.Pchip1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(-3, interp.interpolate(-1), 1e-4);
    +  assertRoughlyEquals(1, interp.interpolate(5), 1e-4);
    +}
    +
    +
    +function testInverse() {
    +  var x = [0, 1, 3, 6, 7];
    +  var y = [0, 2, 7, 8, 10];
    +
    +  var interp = new goog.math.interpolator.Pchip1();
    +  interp.setData(x, y);
    +  var invInterp = interp.getInverse();
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var yi = [0, 0.9548, 2, 4.8938, 7, 7.3906, 7.5902, 8, 10];
    +  var expectedX = [0, 0.888, 1, 0.2852, 3, 4.1206, 4.7379, 6, 7];
    +  var resultX = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  var resultY = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    resultY[i] = interp.interpolate(xi[i]);
    +    resultX[i] = invInterp.interpolate(yi[i]);
    +  }
    +  assertElementsRoughlyEqual(expectedX, resultX, 1e-4);
    +  assertElementsRoughlyEqual(yi, resultY, 1e-4);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1.js
    new file mode 100644
    index 0000000000..c0a4435b0b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1.js
    @@ -0,0 +1,203 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A one dimensional cubic spline interpolator with not-a-knot
    + * boundary conditions.
    + *
    + * See http://en.wikipedia.org/wiki/Spline_interpolation.
    + *
    + */
    +
    +goog.provide('goog.math.interpolator.Spline1');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.math.interpolator.Interpolator1');
    +goog.require('goog.math.tdma');
    +
    +
    +
    +/**
    + * A one dimensional cubic spline interpolator with natural boundary conditions.
    + * @implements {goog.math.interpolator.Interpolator1}
    + * @constructor
    + */
    +goog.math.interpolator.Spline1 = function() {
    +  /**
    +   * The abscissa of the data points.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.x_ = [];
    +
    +  /**
    +   * The spline interval coefficients.
    +   * Note that, in general, the length of coeffs and x is not the same.
    +   * @type {!Array<!Array<number>>}
    +   * @private
    +   */
    +  this.coeffs_ = [[0, 0, 0, Number.NaN]];
    +};
    +
    +
    +/** @override */
    +goog.math.interpolator.Spline1.prototype.setData = function(x, y) {
    +  goog.asserts.assert(x.length == y.length,
    +      'input arrays to setData should have the same length');
    +  if (x.length > 0) {
    +    this.coeffs_ = this.computeSplineCoeffs_(x, y);
    +    this.x_ = x.slice();
    +  } else {
    +    this.coeffs_ = [[0, 0, 0, Number.NaN]];
    +    this.x_ = [];
    +  }
    +};
    +
    +
    +/** @override */
    +goog.math.interpolator.Spline1.prototype.interpolate = function(x) {
    +  var pos = goog.array.binarySearch(this.x_, x);
    +  if (pos < 0) {
    +    pos = -pos - 2;
    +  }
    +  pos = goog.math.clamp(pos, 0, this.coeffs_.length - 1);
    +
    +  var d = x - this.x_[pos];
    +  var d2 = d * d;
    +  var d3 = d2 * d;
    +  var coeffs = this.coeffs_[pos];
    +  return coeffs[0] * d3 + coeffs[1] * d2 + coeffs[2] * d + coeffs[3];
    +};
    +
    +
    +/**
    + * Solve for the spline coefficients such that the spline precisely interpolates
    + * the data points.
    + * @param {Array<number>} x The abscissa of the spline data points.
    + * @param {Array<number>} y The ordinate of the spline data points.
    + * @return {!Array<!Array<number>>} The spline interval coefficients.
    + * @private
    + */
    +goog.math.interpolator.Spline1.prototype.computeSplineCoeffs_ = function(x, y) {
    +  var nIntervals = x.length - 1;
    +  var dx = new Array(nIntervals);
    +  var delta = new Array(nIntervals);
    +  for (var i = 0; i < nIntervals; ++i) {
    +    dx[i] = x[i + 1] - x[i];
    +    delta[i] = (y[i + 1] - y[i]) / dx[i];
    +  }
    +
    +  // Compute the spline coefficients from the 1st order derivatives.
    +  var coeffs = [];
    +  if (nIntervals == 0) {
    +    // Nearest neighbor interpolation.
    +    coeffs[0] = [0, 0, 0, y[0]];
    +  } else if (nIntervals == 1) {
    +    // Straight line interpolation.
    +    coeffs[0] = [0, 0, delta[0], y[0]];
    +  } else if (nIntervals == 2) {
    +    // Parabola interpolation.
    +    var c3 = 0;
    +    var c2 = (delta[1] - delta[0]) / (dx[0] + dx[1]);
    +    var c1 = delta[0] - c2 * dx[0];
    +    var c0 = y[0];
    +    coeffs[0] = [c3, c2, c1, c0];
    +  } else {
    +    // General Spline interpolation. Compute the 1st order derivatives from
    +    // the Spline equations.
    +    var deriv = this.computeDerivatives(dx, delta);
    +    for (var i = 0; i < nIntervals; ++i) {
    +      var c3 = (deriv[i] - 2 * delta[i] + deriv[i + 1]) / (dx[i] * dx[i]);
    +      var c2 = (3 * delta[i] - 2 * deriv[i] - deriv[i + 1]) / dx[i];
    +      var c1 = deriv[i];
    +      var c0 = y[i];
    +      coeffs[i] = [c3, c2, c1, c0];
    +    }
    +  }
    +  return coeffs;
    +};
    +
    +
    +/**
    + * Computes the derivative at each point of the spline such that
    + * the curve is C2. It uses not-a-knot boundary conditions.
    + * @param {Array<number>} dx The spacing between consecutive data points.
    + * @param {Array<number>} slope The slopes between consecutive data points.
    + * @return {!Array<number>} The Spline derivative at each data point.
    + * @protected
    + */
    +goog.math.interpolator.Spline1.prototype.computeDerivatives = function(
    +    dx, slope) {
    +  var nIntervals = dx.length;
    +
    +  // Compute the main diagonal of the system of equations.
    +  var mainDiag = new Array(nIntervals + 1);
    +  mainDiag[0] = dx[1];
    +  for (var i = 1; i < nIntervals; ++i) {
    +    mainDiag[i] = 2 * (dx[i] + dx[i - 1]);
    +  }
    +  mainDiag[nIntervals] = dx[nIntervals - 2];
    +
    +  // Compute the sub diagonal of the system of equations.
    +  var subDiag = new Array(nIntervals);
    +  for (var i = 0; i < nIntervals; ++i) {
    +    subDiag[i] = dx[i + 1];
    +  }
    +  subDiag[nIntervals - 1] = dx[nIntervals - 2] + dx[nIntervals - 1];
    +
    +  // Compute the super diagonal of the system of equations.
    +  var supDiag = new Array(nIntervals);
    +  supDiag[0] = dx[0] + dx[1];
    +  for (var i = 1; i < nIntervals; ++i) {
    +    supDiag[i] = dx[i - 1];
    +  }
    +
    +  // Compute the right vector of the system of equations.
    +  var vecRight = new Array(nIntervals + 1);
    +  vecRight[0] = ((dx[0] + 2 * supDiag[0]) * dx[1] * slope[0] +
    +      dx[0] * dx[0] * slope[1]) / supDiag[0];
    +  for (var i = 1; i < nIntervals; ++i) {
    +    vecRight[i] = 3 * (dx[i] * slope[i - 1] + dx[i - 1] * slope[i]);
    +  }
    +  vecRight[nIntervals] = (dx[nIntervals - 1] * dx[nIntervals - 1] *
    +      slope[nIntervals - 2] + (2 * subDiag[nIntervals - 1] +
    +      dx[nIntervals - 1]) * dx[nIntervals - 2] * slope[nIntervals - 1]) /
    +      subDiag[nIntervals - 1];
    +
    +  // Solve the system of equations.
    +  var deriv = goog.math.tdma.solve(
    +      subDiag, mainDiag, supDiag, vecRight);
    +
    +  return deriv;
    +};
    +
    +
    +/**
    + * Note that the inverse of a cubic spline is not a cubic spline in general.
    + * As a result the inverse implementation is only approximate. In
    + * particular, it only guarantees the exact inverse at the original input data
    + * points passed to setData.
    + * @override
    + */
    +goog.math.interpolator.Spline1.prototype.getInverse = function() {
    +  var interpolator = new goog.math.interpolator.Spline1();
    +  var y = [];
    +  for (var i = 0; i < this.x_.length; i++) {
    +    y[i] = this.interpolate(this.x_[i]);
    +  }
    +  interpolator.setData(y, this.x_);
    +  return interpolator;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.html b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.html
    new file mode 100644
    index 0000000000..91a17011e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.interpolator.Spline1
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.interpolator.Spline1Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.js b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.js
    new file mode 100644
    index 0000000000..0c7383e5c3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/interpolator/spline1_test.js
    @@ -0,0 +1,100 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.interpolator.Spline1Test');
    +goog.setTestOnly('goog.math.interpolator.Spline1Test');
    +
    +goog.require('goog.math.interpolator.Spline1');
    +goog.require('goog.testing.jsunit');
    +
    +function testSpline() {
    +  // Test special case with no data to interpolate.
    +  var x = [];
    +  var y = [];
    +  var interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  assertTrue(isNaN(interp.interpolate(1)));
    +
    +  // Test special case with 1 data point.
    +  x = [0];
    +  y = [2];
    +  interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(2, interp.interpolate(1), 1e-4);
    +
    +  // Test special case with 2 data points.
    +  x = [0, 1];
    +  y = [2, 5];
    +  interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(3.5, interp.interpolate(.5), 1e-4);
    +
    +  // Test special case with 3 data points.
    +  x = [0, 1, 2];
    +  y = [2, 5, 4];
    +  interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(4, interp.interpolate(.5), 1e-4);
    +  assertRoughlyEquals(-1, interp.interpolate(3), 1e-4);
    +
    +  // Test general case.
    +  x = [0, 1, 3, 6, 7];
    +  y = [0, 0, 0, 0, 0];
    +  for (var i = 0; i < x.length; ++i) {
    +    y[i] = Math.sin(x[i]);
    +  }
    +  interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var expected = [0, 0.5775, 0.8415, 0.7047, 0.1411, -0.3601, -0.55940,
    +    -0.2794, 0.6570];
    +  var result = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    result[i] = interp.interpolate(xi[i]);
    +  }
    +  assertElementsRoughlyEqual(expected, result, 1e-4);
    +}
    +
    +
    +function testOutOfBounds() {
    +  var x = [0, 1, 2, 4];
    +  var y = [2, 5, 4, 1];
    +  var interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  assertRoughlyEquals(-7.75, interp.interpolate(-1), 1e-4);
    +  assertRoughlyEquals(4.5, interp.interpolate(5), 1e-4);
    +}
    +
    +
    +function testInverse() {
    +  var x = [0, 1, 3, 6, 7];
    +  var y = [0, 2, 7, 8, 10];
    +
    +  var interp = new goog.math.interpolator.Spline1();
    +  interp.setData(x, y);
    +  var invInterp = interp.getInverse();
    +
    +  var xi = [0, 0.5, 1, 2, 3, 4, 5, 6, 7];
    +  var yi = [0, 0.8159, 2, 4.7892, 7, 7.6912, 7.6275, 8, 10];
    +  var expectedX = [0, 0.8142, 1, 0.2638, 3, 5.0534, 4.8544, 6, 7];
    +  var resultX = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  var resultY = [0, 0, 0, 0, 0, 0, 0, 0, 0];
    +  for (var i = 0; i < xi.length; ++i) {
    +    resultY[i] = interp.interpolate(xi[i]);
    +    resultX[i] = invInterp.interpolate(yi[i]);
    +  }
    +  assertElementsRoughlyEqual(expectedX, resultX, 1e-4);
    +  assertElementsRoughlyEqual(yi, resultY, 1e-4);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/line.js b/mapbox-gl/closure-library/closure/goog/math/line.js
    new file mode 100644
    index 0000000000..fe4e45c4c9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/line.js
    @@ -0,0 +1,179 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a line in 2D space.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.math.Line');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate');
    +
    +
    +
    +/**
    + * Object representing a line.
    + * @param {number} x0 X coordinate of the start point.
    + * @param {number} y0 Y coordinate of the start point.
    + * @param {number} x1 X coordinate of the end point.
    + * @param {number} y1 Y coordinate of the end point.
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.Line = function(x0, y0, x1, y1) {
    +  /**
    +   * X coordinate of the first point.
    +   * @type {number}
    +   */
    +  this.x0 = x0;
    +
    +  /**
    +   * Y coordinate of the first point.
    +   * @type {number}
    +   */
    +  this.y0 = y0;
    +
    +  /**
    +   * X coordinate of the first control point.
    +   * @type {number}
    +   */
    +  this.x1 = x1;
    +
    +  /**
    +   * Y coordinate of the first control point.
    +   * @type {number}
    +   */
    +  this.y1 = y1;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Line} A copy of this line.
    + */
    +goog.math.Line.prototype.clone = function() {
    +  return new goog.math.Line(this.x0, this.y0, this.x1, this.y1);
    +};
    +
    +
    +/**
    + * Tests whether the given line is exactly the same as this one.
    + * @param {goog.math.Line} other The other line.
    + * @return {boolean} Whether the given line is the same as this one.
    + */
    +goog.math.Line.prototype.equals = function(other) {
    +  return this.x0 == other.x0 && this.y0 == other.y0 &&
    +         this.x1 == other.x1 && this.y1 == other.y1;
    +};
    +
    +
    +/**
    + * @return {number} The squared length of the line segment used to define the
    + *     line.
    + */
    +goog.math.Line.prototype.getSegmentLengthSquared = function() {
    +  var xdist = this.x1 - this.x0;
    +  var ydist = this.y1 - this.y0;
    +  return xdist * xdist + ydist * ydist;
    +};
    +
    +
    +/**
    + * @return {number} The length of the line segment used to define the line.
    + */
    +goog.math.Line.prototype.getSegmentLength = function() {
    +  return Math.sqrt(this.getSegmentLengthSquared());
    +};
    +
    +
    +/**
    + * Computes the interpolation parameter for the point on the line closest to
    + * a given point.
    + * @param {number|goog.math.Coordinate} x The x coordinate of the point, or
    + *     a coordinate object.
    + * @param {number=} opt_y The y coordinate of the point - required if x is a
    + *     number, ignored if x is a goog.math.Coordinate.
    + * @return {number} The interpolation parameter of the point on the line
    + *     closest to the given point.
    + * @private
    + */
    +goog.math.Line.prototype.getClosestLinearInterpolation_ = function(x, opt_y) {
    +  var y;
    +  if (x instanceof goog.math.Coordinate) {
    +    y = x.y;
    +    x = x.x;
    +  } else {
    +    y = opt_y;
    +  }
    +
    +  var x0 = this.x0;
    +  var y0 = this.y0;
    +
    +  var xChange = this.x1 - x0;
    +  var yChange = this.y1 - y0;
    +
    +  return ((x - x0) * xChange + (y - y0) * yChange) /
    +      this.getSegmentLengthSquared();
    +};
    +
    +
    +/**
    + * Returns the point on the line segment proportional to t, where for t = 0 we
    + * return the starting point and for t = 1 we return the end point.  For t < 0
    + * or t > 1 we extrapolate along the line defined by the line segment.
    + * @param {number} t The interpolation parameter along the line segment.
    + * @return {!goog.math.Coordinate} The point on the line segment at t.
    + */
    +goog.math.Line.prototype.getInterpolatedPoint = function(t) {
    +  return new goog.math.Coordinate(
    +      goog.math.lerp(this.x0, this.x1, t),
    +      goog.math.lerp(this.y0, this.y1, t));
    +};
    +
    +
    +/**
    + * Computes the point on the line closest to a given point.  Note that a line
    + * in this case is defined as the infinite line going through the start and end
    + * points.  To find the closest point on the line segment itself see
    + * {@see #getClosestSegmentPoint}.
    + * @param {number|goog.math.Coordinate} x The x coordinate of the point, or
    + *     a coordinate object.
    + * @param {number=} opt_y The y coordinate of the point - required if x is a
    + *     number, ignored if x is a goog.math.Coordinate.
    + * @return {!goog.math.Coordinate} The point on the line closest to the given
    + *     point.
    + */
    +goog.math.Line.prototype.getClosestPoint = function(x, opt_y) {
    +  return this.getInterpolatedPoint(
    +      this.getClosestLinearInterpolation_(x, opt_y));
    +};
    +
    +
    +/**
    + * Computes the point on the line segment closest to a given point.
    + * @param {number|goog.math.Coordinate} x The x coordinate of the point, or
    + *     a coordinate object.
    + * @param {number=} opt_y The y coordinate of the point - required if x is a
    + *     number, ignored if x is a goog.math.Coordinate.
    + * @return {!goog.math.Coordinate} The point on the line segment closest to the
    + *     given point.
    + */
    +goog.math.Line.prototype.getClosestSegmentPoint = function(x, opt_y) {
    +  return this.getInterpolatedPoint(
    +      goog.math.clamp(this.getClosestLinearInterpolation_(x, opt_y), 0, 1));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/line_test.html b/mapbox-gl/closure-library/closure/goog/math/line_test.html
    new file mode 100644
    index 0000000000..f73ecadf0a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/line_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Line
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.LineTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/line_test.js b/mapbox-gl/closure-library/closure/goog/math/line_test.js
    new file mode 100644
    index 0000000000..13e613bc89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/line_test.js
    @@ -0,0 +1,59 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.LineTest');
    +goog.setTestOnly('goog.math.LineTest');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Line');
    +goog.require('goog.testing.jsunit');
    +
    +function testEquals() {
    +  var input = new goog.math.Line(1, 2, 3, 4);
    +
    +  assert(input.equals(input));
    +}
    +
    +function testClone() {
    +  var input = new goog.math.Line(1, 2, 3, 4);
    +
    +  assertNotEquals('Clone returns a new object', input, input.clone());
    +  assertTrue('Contents of clone match original', input.equals(input.clone()));
    +}
    +
    +function testGetLength() {
    +  var input = new goog.math.Line(0, 0, Math.sqrt(2), Math.sqrt(2));
    +  assertRoughlyEquals(input.getSegmentLengthSquared(), 4, 1e-10);
    +  assertRoughlyEquals(input.getSegmentLength(), 2, 1e-10);
    +}
    +
    +function testGetClosestPoint() {
    +  var input = new goog.math.Line(0, 1, 1, 2);
    +
    +  var point = input.getClosestPoint(0, 3);
    +  assertRoughlyEquals(point.x, 1, 1e-10);
    +  assertRoughlyEquals(point.y, 2, 1e-10);
    +}
    +
    +function testGetClosestSegmentPoint() {
    +  var input = new goog.math.Line(0, 1, 2, 3);
    +
    +  var point = input.getClosestSegmentPoint(4, 4);
    +  assertRoughlyEquals(point.x, 2, 1e-10);
    +  assertRoughlyEquals(point.y, 3, 1e-10);
    +
    +  point = input.getClosestSegmentPoint(new goog.math.Coordinate(-1, -10));
    +  assertRoughlyEquals(point.x, 0, 1e-10);
    +  assertRoughlyEquals(point.y, 1, 1e-10);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/long.js b/mapbox-gl/closure-library/closure/goog/math/long.js
    new file mode 100644
    index 0000000000..1bb4be9b31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/long.js
    @@ -0,0 +1,804 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines a Long class for representing a 64-bit two's-complement
    + * integer value, which faithfully simulates the behavior of a Java "long". This
    + * implementation is derived from LongLib in GWT.
    + *
    + */
    +
    +goog.provide('goog.math.Long');
    +
    +
    +
    +/**
    + * Constructs a 64-bit two's-complement integer, given its low and high 32-bit
    + * values as *signed* integers.  See the from* functions below for more
    + * convenient ways of constructing Longs.
    + *
    + * The internal representation of a long is the two given signed, 32-bit values.
    + * We use 32-bit pieces because these are the size of integers on which
    + * Javascript performs bit-operations.  For operations like addition and
    + * multiplication, we split each number into 16-bit pieces, which can easily be
    + * multiplied within Javascript's floating-point representation without overflow
    + * or change in sign.
    + *
    + * In the algorithms below, we frequently reduce the negative case to the
    + * positive case by negating the input(s) and then post-processing the result.
    + * Note that we must ALWAYS check specially whether those values are MIN_VALUE
    + * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
    + * a positive number, it overflows back into a negative).  Not handling this
    + * case would often result in infinite recursion.
    + *
    + * @param {number} low  The low (signed) 32 bits of the long.
    + * @param {number} high  The high (signed) 32 bits of the long.
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.Long = function(low, high) {
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.low_ = low | 0;  // force into 32 signed bits.
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.high_ = high | 0;  // force into 32 signed bits.
    +};
    +
    +
    +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
    +// from* methods on which they depend.
    +
    +
    +/**
    + * A cache of the Long representations of small integer values.
    + * @type {!Object}
    + * @private
    + */
    +goog.math.Long.IntCache_ = {};
    +
    +
    +/**
    + * Returns a Long representing the given (32-bit) integer value.
    + * @param {number} value The 32-bit integer in question.
    + * @return {!goog.math.Long} The corresponding Long value.
    + */
    +goog.math.Long.fromInt = function(value) {
    +  if (-128 <= value && value < 128) {
    +    var cachedObj = goog.math.Long.IntCache_[value];
    +    if (cachedObj) {
    +      return cachedObj;
    +    }
    +  }
    +
    +  var obj = new goog.math.Long(value | 0, value < 0 ? -1 : 0);
    +  if (-128 <= value && value < 128) {
    +    goog.math.Long.IntCache_[value] = obj;
    +  }
    +  return obj;
    +};
    +
    +
    +/**
    + * Returns a Long representing the given value, provided that it is a finite
    + * number.  Otherwise, zero is returned.
    + * @param {number} value The number in question.
    + * @return {!goog.math.Long} The corresponding Long value.
    + */
    +goog.math.Long.fromNumber = function(value) {
    +  if (isNaN(value) || !isFinite(value)) {
    +    return goog.math.Long.ZERO;
    +  } else if (value <= -goog.math.Long.TWO_PWR_63_DBL_) {
    +    return goog.math.Long.MIN_VALUE;
    +  } else if (value + 1 >= goog.math.Long.TWO_PWR_63_DBL_) {
    +    return goog.math.Long.MAX_VALUE;
    +  } else if (value < 0) {
    +    return goog.math.Long.fromNumber(-value).negate();
    +  } else {
    +    return new goog.math.Long(
    +        (value % goog.math.Long.TWO_PWR_32_DBL_) | 0,
    +        (value / goog.math.Long.TWO_PWR_32_DBL_) | 0);
    +  }
    +};
    +
    +
    +/**
    + * Returns a Long representing the 64-bit integer that comes by concatenating
    + * the given high and low bits.  Each is assumed to use 32 bits.
    + * @param {number} lowBits The low 32-bits.
    + * @param {number} highBits The high 32-bits.
    + * @return {!goog.math.Long} The corresponding Long value.
    + */
    +goog.math.Long.fromBits = function(lowBits, highBits) {
    +  return new goog.math.Long(lowBits, highBits);
    +};
    +
    +
    +/**
    + * Returns a Long representation of the given string, written using the given
    + * radix.
    + * @param {string} str The textual representation of the Long.
    + * @param {number=} opt_radix The radix in which the text is written.
    + * @return {!goog.math.Long} The corresponding Long value.
    + */
    +goog.math.Long.fromString = function(str, opt_radix) {
    +  if (str.length == 0) {
    +    throw Error('number format error: empty string');
    +  }
    +
    +  var radix = opt_radix || 10;
    +  if (radix < 2 || 36 < radix) {
    +    throw Error('radix out of range: ' + radix);
    +  }
    +
    +  if (str.charAt(0) == '-') {
    +    return goog.math.Long.fromString(str.substring(1), radix).negate();
    +  } else if (str.indexOf('-') >= 0) {
    +    throw Error('number format error: interior "-" character: ' + str);
    +  }
    +
    +  // Do several (8) digits each time through the loop, so as to
    +  // minimize the calls to the very expensive emulated div.
    +  var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 8));
    +
    +  var result = goog.math.Long.ZERO;
    +  for (var i = 0; i < str.length; i += 8) {
    +    var size = Math.min(8, str.length - i);
    +    var value = parseInt(str.substring(i, i + size), radix);
    +    if (size < 8) {
    +      var power = goog.math.Long.fromNumber(Math.pow(radix, size));
    +      result = result.multiply(power).add(goog.math.Long.fromNumber(value));
    +    } else {
    +      result = result.multiply(radixToPower);
    +      result = result.add(goog.math.Long.fromNumber(value));
    +    }
    +  }
    +  return result;
    +};
    +
    +
    +// NOTE: the compiler should inline these constant values below and then remove
    +// these variables, so there should be no runtime penalty for these.
    +
    +
    +/**
    + * Number used repeated below in calculations.  This must appear before the
    + * first call to any from* function below.
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_16_DBL_ = 1 << 16;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_24_DBL_ = 1 << 24;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_32_DBL_ =
    +    goog.math.Long.TWO_PWR_16_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_31_DBL_ =
    +    goog.math.Long.TWO_PWR_32_DBL_ / 2;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_48_DBL_ =
    +    goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_64_DBL_ =
    +    goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_32_DBL_;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_63_DBL_ =
    +    goog.math.Long.TWO_PWR_64_DBL_ / 2;
    +
    +
    +/** @type {!goog.math.Long} */
    +goog.math.Long.ZERO = goog.math.Long.fromInt(0);
    +
    +
    +/** @type {!goog.math.Long} */
    +goog.math.Long.ONE = goog.math.Long.fromInt(1);
    +
    +
    +/** @type {!goog.math.Long} */
    +goog.math.Long.NEG_ONE = goog.math.Long.fromInt(-1);
    +
    +
    +/** @type {!goog.math.Long} */
    +goog.math.Long.MAX_VALUE =
    +    goog.math.Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
    +
    +
    +/** @type {!goog.math.Long} */
    +goog.math.Long.MIN_VALUE = goog.math.Long.fromBits(0, 0x80000000 | 0);
    +
    +
    +/**
    + * @type {!goog.math.Long}
    + * @private
    + */
    +goog.math.Long.TWO_PWR_24_ = goog.math.Long.fromInt(1 << 24);
    +
    +
    +/** @return {number} The value, assuming it is a 32-bit integer. */
    +goog.math.Long.prototype.toInt = function() {
    +  return this.low_;
    +};
    +
    +
    +/** @return {number} The closest floating-point representation to this value. */
    +goog.math.Long.prototype.toNumber = function() {
    +  return this.high_ * goog.math.Long.TWO_PWR_32_DBL_ +
    +         this.getLowBitsUnsigned();
    +};
    +
    +
    +/**
    + * @param {number=} opt_radix The radix in which the text should be written.
    + * @return {string} The textual representation of this value.
    + * @override
    + */
    +goog.math.Long.prototype.toString = function(opt_radix) {
    +  var radix = opt_radix || 10;
    +  if (radix < 2 || 36 < radix) {
    +    throw Error('radix out of range: ' + radix);
    +  }
    +
    +  if (this.isZero()) {
    +    return '0';
    +  }
    +
    +  if (this.isNegative()) {
    +    if (this.equals(goog.math.Long.MIN_VALUE)) {
    +      // We need to change the Long value before it can be negated, so we remove
    +      // the bottom-most digit in this base and then recurse to do the rest.
    +      var radixLong = goog.math.Long.fromNumber(radix);
    +      var div = this.div(radixLong);
    +      var rem = div.multiply(radixLong).subtract(this);
    +      return div.toString(radix) + rem.toInt().toString(radix);
    +    } else {
    +      return '-' + this.negate().toString(radix);
    +    }
    +  }
    +
    +  // Do several (6) digits each time through the loop, so as to
    +  // minimize the calls to the very expensive emulated div.
    +  var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 6));
    +
    +  var rem = this;
    +  var result = '';
    +  while (true) {
    +    var remDiv = rem.div(radixToPower);
    +    var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt();
    +    var digits = intval.toString(radix);
    +
    +    rem = remDiv;
    +    if (rem.isZero()) {
    +      return digits + result;
    +    } else {
    +      while (digits.length < 6) {
    +        digits = '0' + digits;
    +      }
    +      result = '' + digits + result;
    +    }
    +  }
    +};
    +
    +
    +/** @return {number} The high 32-bits as a signed value. */
    +goog.math.Long.prototype.getHighBits = function() {
    +  return this.high_;
    +};
    +
    +
    +/** @return {number} The low 32-bits as a signed value. */
    +goog.math.Long.prototype.getLowBits = function() {
    +  return this.low_;
    +};
    +
    +
    +/** @return {number} The low 32-bits as an unsigned value. */
    +goog.math.Long.prototype.getLowBitsUnsigned = function() {
    +  return (this.low_ >= 0) ?
    +      this.low_ : goog.math.Long.TWO_PWR_32_DBL_ + this.low_;
    +};
    +
    +
    +/**
    + * @return {number} Returns the number of bits needed to represent the absolute
    + *     value of this Long.
    + */
    +goog.math.Long.prototype.getNumBitsAbs = function() {
    +  if (this.isNegative()) {
    +    if (this.equals(goog.math.Long.MIN_VALUE)) {
    +      return 64;
    +    } else {
    +      return this.negate().getNumBitsAbs();
    +    }
    +  } else {
    +    var val = this.high_ != 0 ? this.high_ : this.low_;
    +    for (var bit = 31; bit > 0; bit--) {
    +      if ((val & (1 << bit)) != 0) {
    +        break;
    +      }
    +    }
    +    return this.high_ != 0 ? bit + 33 : bit + 1;
    +  }
    +};
    +
    +
    +/** @return {boolean} Whether this value is zero. */
    +goog.math.Long.prototype.isZero = function() {
    +  return this.high_ == 0 && this.low_ == 0;
    +};
    +
    +
    +/** @return {boolean} Whether this value is negative. */
    +goog.math.Long.prototype.isNegative = function() {
    +  return this.high_ < 0;
    +};
    +
    +
    +/** @return {boolean} Whether this value is odd. */
    +goog.math.Long.prototype.isOdd = function() {
    +  return (this.low_ & 1) == 1;
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long equals the other.
    + */
    +goog.math.Long.prototype.equals = function(other) {
    +  return (this.high_ == other.high_) && (this.low_ == other.low_);
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long does not equal the other.
    + */
    +goog.math.Long.prototype.notEquals = function(other) {
    +  return (this.high_ != other.high_) || (this.low_ != other.low_);
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long is less than the other.
    + */
    +goog.math.Long.prototype.lessThan = function(other) {
    +  return this.compare(other) < 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long is less than or equal to the other.
    + */
    +goog.math.Long.prototype.lessThanOrEqual = function(other) {
    +  return this.compare(other) <= 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long is greater than the other.
    + */
    +goog.math.Long.prototype.greaterThan = function(other) {
    +  return this.compare(other) > 0;
    +};
    +
    +
    +/**
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {boolean} Whether this Long is greater than or equal to the other.
    + */
    +goog.math.Long.prototype.greaterThanOrEqual = function(other) {
    +  return this.compare(other) >= 0;
    +};
    +
    +
    +/**
    + * Compares this Long with the given one.
    + * @param {goog.math.Long} other Long to compare against.
    + * @return {number} 0 if they are the same, 1 if the this is greater, and -1
    + *     if the given one is greater.
    + */
    +goog.math.Long.prototype.compare = function(other) {
    +  if (this.equals(other)) {
    +    return 0;
    +  }
    +
    +  var thisNeg = this.isNegative();
    +  var otherNeg = other.isNegative();
    +  if (thisNeg && !otherNeg) {
    +    return -1;
    +  }
    +  if (!thisNeg && otherNeg) {
    +    return 1;
    +  }
    +
    +  // at this point, the signs are the same, so subtraction will not overflow
    +  if (this.subtract(other).isNegative()) {
    +    return -1;
    +  } else {
    +    return 1;
    +  }
    +};
    +
    +
    +/** @return {!goog.math.Long} The negation of this value. */
    +goog.math.Long.prototype.negate = function() {
    +  if (this.equals(goog.math.Long.MIN_VALUE)) {
    +    return goog.math.Long.MIN_VALUE;
    +  } else {
    +    return this.not().add(goog.math.Long.ONE);
    +  }
    +};
    +
    +
    +/**
    + * Returns the sum of this and the given Long.
    + * @param {goog.math.Long} other Long to add to this one.
    + * @return {!goog.math.Long} The sum of this and the given Long.
    + */
    +goog.math.Long.prototype.add = function(other) {
    +  // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
    +
    +  var a48 = this.high_ >>> 16;
    +  var a32 = this.high_ & 0xFFFF;
    +  var a16 = this.low_ >>> 16;
    +  var a00 = this.low_ & 0xFFFF;
    +
    +  var b48 = other.high_ >>> 16;
    +  var b32 = other.high_ & 0xFFFF;
    +  var b16 = other.low_ >>> 16;
    +  var b00 = other.low_ & 0xFFFF;
    +
    +  var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
    +  c00 += a00 + b00;
    +  c16 += c00 >>> 16;
    +  c00 &= 0xFFFF;
    +  c16 += a16 + b16;
    +  c32 += c16 >>> 16;
    +  c16 &= 0xFFFF;
    +  c32 += a32 + b32;
    +  c48 += c32 >>> 16;
    +  c32 &= 0xFFFF;
    +  c48 += a48 + b48;
    +  c48 &= 0xFFFF;
    +  return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
    +};
    +
    +
    +/**
    + * Returns the difference of this and the given Long.
    + * @param {goog.math.Long} other Long to subtract from this.
    + * @return {!goog.math.Long} The difference of this and the given Long.
    + */
    +goog.math.Long.prototype.subtract = function(other) {
    +  return this.add(other.negate());
    +};
    +
    +
    +/**
    + * Returns the product of this and the given long.
    + * @param {goog.math.Long} other Long to multiply with this.
    + * @return {!goog.math.Long} The product of this and the other.
    + */
    +goog.math.Long.prototype.multiply = function(other) {
    +  if (this.isZero()) {
    +    return goog.math.Long.ZERO;
    +  } else if (other.isZero()) {
    +    return goog.math.Long.ZERO;
    +  }
    +
    +  if (this.equals(goog.math.Long.MIN_VALUE)) {
    +    return other.isOdd() ? goog.math.Long.MIN_VALUE : goog.math.Long.ZERO;
    +  } else if (other.equals(goog.math.Long.MIN_VALUE)) {
    +    return this.isOdd() ? goog.math.Long.MIN_VALUE : goog.math.Long.ZERO;
    +  }
    +
    +  if (this.isNegative()) {
    +    if (other.isNegative()) {
    +      return this.negate().multiply(other.negate());
    +    } else {
    +      return this.negate().multiply(other).negate();
    +    }
    +  } else if (other.isNegative()) {
    +    return this.multiply(other.negate()).negate();
    +  }
    +
    +  // If both longs are small, use float multiplication
    +  if (this.lessThan(goog.math.Long.TWO_PWR_24_) &&
    +      other.lessThan(goog.math.Long.TWO_PWR_24_)) {
    +    return goog.math.Long.fromNumber(this.toNumber() * other.toNumber());
    +  }
    +
    +  // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
    +  // We can skip products that would overflow.
    +
    +  var a48 = this.high_ >>> 16;
    +  var a32 = this.high_ & 0xFFFF;
    +  var a16 = this.low_ >>> 16;
    +  var a00 = this.low_ & 0xFFFF;
    +
    +  var b48 = other.high_ >>> 16;
    +  var b32 = other.high_ & 0xFFFF;
    +  var b16 = other.low_ >>> 16;
    +  var b00 = other.low_ & 0xFFFF;
    +
    +  var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
    +  c00 += a00 * b00;
    +  c16 += c00 >>> 16;
    +  c00 &= 0xFFFF;
    +  c16 += a16 * b00;
    +  c32 += c16 >>> 16;
    +  c16 &= 0xFFFF;
    +  c16 += a00 * b16;
    +  c32 += c16 >>> 16;
    +  c16 &= 0xFFFF;
    +  c32 += a32 * b00;
    +  c48 += c32 >>> 16;
    +  c32 &= 0xFFFF;
    +  c32 += a16 * b16;
    +  c48 += c32 >>> 16;
    +  c32 &= 0xFFFF;
    +  c32 += a00 * b32;
    +  c48 += c32 >>> 16;
    +  c32 &= 0xFFFF;
    +  c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
    +  c48 &= 0xFFFF;
    +  return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
    +};
    +
    +
    +/**
    + * Returns this Long divided by the given one.
    + * @param {goog.math.Long} other Long by which to divide.
    + * @return {!goog.math.Long} This Long divided by the given one.
    + */
    +goog.math.Long.prototype.div = function(other) {
    +  if (other.isZero()) {
    +    throw Error('division by zero');
    +  } else if (this.isZero()) {
    +    return goog.math.Long.ZERO;
    +  }
    +
    +  if (this.equals(goog.math.Long.MIN_VALUE)) {
    +    if (other.equals(goog.math.Long.ONE) ||
    +        other.equals(goog.math.Long.NEG_ONE)) {
    +      return goog.math.Long.MIN_VALUE;  // recall that -MIN_VALUE == MIN_VALUE
    +    } else if (other.equals(goog.math.Long.MIN_VALUE)) {
    +      return goog.math.Long.ONE;
    +    } else {
    +      // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
    +      var halfThis = this.shiftRight(1);
    +      var approx = halfThis.div(other).shiftLeft(1);
    +      if (approx.equals(goog.math.Long.ZERO)) {
    +        return other.isNegative() ? goog.math.Long.ONE : goog.math.Long.NEG_ONE;
    +      } else {
    +        var rem = this.subtract(other.multiply(approx));
    +        var result = approx.add(rem.div(other));
    +        return result;
    +      }
    +    }
    +  } else if (other.equals(goog.math.Long.MIN_VALUE)) {
    +    return goog.math.Long.ZERO;
    +  }
    +
    +  if (this.isNegative()) {
    +    if (other.isNegative()) {
    +      return this.negate().div(other.negate());
    +    } else {
    +      return this.negate().div(other).negate();
    +    }
    +  } else if (other.isNegative()) {
    +    return this.div(other.negate()).negate();
    +  }
    +
    +  // Repeat the following until the remainder is less than other:  find a
    +  // floating-point that approximates remainder / other *from below*, add this
    +  // into the result, and subtract it from the remainder.  It is critical that
    +  // the approximate value is less than or equal to the real value so that the
    +  // remainder never becomes negative.
    +  var res = goog.math.Long.ZERO;
    +  var rem = this;
    +  while (rem.greaterThanOrEqual(other)) {
    +    // Approximate the result of division. This may be a little greater or
    +    // smaller than the actual value.
    +    var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
    +
    +    // We will tweak the approximate result by changing it in the 48-th digit or
    +    // the smallest non-fractional digit, whichever is larger.
    +    var log2 = Math.ceil(Math.log(approx) / Math.LN2);
    +    var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
    +
    +    // Decrease the approximation until it is smaller than the remainder.  Note
    +    // that if it is too large, the product overflows and is negative.
    +    var approxRes = goog.math.Long.fromNumber(approx);
    +    var approxRem = approxRes.multiply(other);
    +    while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
    +      approx -= delta;
    +      approxRes = goog.math.Long.fromNumber(approx);
    +      approxRem = approxRes.multiply(other);
    +    }
    +
    +    // We know the answer can't be zero... and actually, zero would cause
    +    // infinite recursion since we would make no progress.
    +    if (approxRes.isZero()) {
    +      approxRes = goog.math.Long.ONE;
    +    }
    +
    +    res = res.add(approxRes);
    +    rem = rem.subtract(approxRem);
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * Returns this Long modulo the given one.
    + * @param {goog.math.Long} other Long by which to mod.
    + * @return {!goog.math.Long} This Long modulo the given one.
    + */
    +goog.math.Long.prototype.modulo = function(other) {
    +  return this.subtract(this.div(other).multiply(other));
    +};
    +
    +
    +/** @return {!goog.math.Long} The bitwise-NOT of this value. */
    +goog.math.Long.prototype.not = function() {
    +  return goog.math.Long.fromBits(~this.low_, ~this.high_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-AND of this Long and the given one.
    + * @param {goog.math.Long} other The Long with which to AND.
    + * @return {!goog.math.Long} The bitwise-AND of this and the other.
    + */
    +goog.math.Long.prototype.and = function(other) {
    +  return goog.math.Long.fromBits(this.low_ & other.low_,
    +                                 this.high_ & other.high_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-OR of this Long and the given one.
    + * @param {goog.math.Long} other The Long with which to OR.
    + * @return {!goog.math.Long} The bitwise-OR of this and the other.
    + */
    +goog.math.Long.prototype.or = function(other) {
    +  return goog.math.Long.fromBits(this.low_ | other.low_,
    +                                 this.high_ | other.high_);
    +};
    +
    +
    +/**
    + * Returns the bitwise-XOR of this Long and the given one.
    + * @param {goog.math.Long} other The Long with which to XOR.
    + * @return {!goog.math.Long} The bitwise-XOR of this and the other.
    + */
    +goog.math.Long.prototype.xor = function(other) {
    +  return goog.math.Long.fromBits(this.low_ ^ other.low_,
    +                                 this.high_ ^ other.high_);
    +};
    +
    +
    +/**
    + * Returns this Long with bits shifted to the left by the given amount.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Long} This shifted to the left by the given amount.
    + */
    +goog.math.Long.prototype.shiftLeft = function(numBits) {
    +  numBits &= 63;
    +  if (numBits == 0) {
    +    return this;
    +  } else {
    +    var low = this.low_;
    +    if (numBits < 32) {
    +      var high = this.high_;
    +      return goog.math.Long.fromBits(
    +          low << numBits,
    +          (high << numBits) | (low >>> (32 - numBits)));
    +    } else {
    +      return goog.math.Long.fromBits(0, low << (numBits - 32));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns this Long with bits shifted to the right by the given amount.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Long} This shifted to the right by the given amount.
    + */
    +goog.math.Long.prototype.shiftRight = function(numBits) {
    +  numBits &= 63;
    +  if (numBits == 0) {
    +    return this;
    +  } else {
    +    var high = this.high_;
    +    if (numBits < 32) {
    +      var low = this.low_;
    +      return goog.math.Long.fromBits(
    +          (low >>> numBits) | (high << (32 - numBits)),
    +          high >> numBits);
    +    } else {
    +      return goog.math.Long.fromBits(
    +          high >> (numBits - 32),
    +          high >= 0 ? 0 : -1);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns this Long with bits shifted to the right by the given amount, with
    + * zeros placed into the new leading bits.
    + * @param {number} numBits The number of bits by which to shift.
    + * @return {!goog.math.Long} This shifted to the right by the given amount, with
    + *     zeros placed into the new leading bits.
    + */
    +goog.math.Long.prototype.shiftRightUnsigned = function(numBits) {
    +  numBits &= 63;
    +  if (numBits == 0) {
    +    return this;
    +  } else {
    +    var high = this.high_;
    +    if (numBits < 32) {
    +      var low = this.low_;
    +      return goog.math.Long.fromBits(
    +          (low >>> numBits) | (high << (32 - numBits)),
    +          high >>> numBits);
    +    } else if (numBits == 32) {
    +      return goog.math.Long.fromBits(high, 0);
    +    } else {
    +      return goog.math.Long.fromBits(high >>> (numBits - 32), 0);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/long_test.html b/mapbox-gl/closure-library/closure/goog/math/long_test.html
    new file mode 100644
    index 0000000000..f9feebddff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/long_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Long
    +  </title>
    +  <script src="../base.js" type="text/javascript">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.math.LongTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/long_test.js b/mapbox-gl/closure-library/closure/goog/math/long_test.js
    new file mode 100644
    index 0000000000..7639819051
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/long_test.js
    @@ -0,0 +1,1571 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.LongTest');
    +goog.setTestOnly('goog.math.LongTest');
    +
    +goog.require('goog.math.Long');
    +goog.require('goog.testing.jsunit');
    +
    +// Interprets the given numbers as the bits of a 32-bit int.  In particular,
    +// this takes care of the 32-bit being interpretted as the sign.
    +function toInt32s(arr) {
    +  for (var i = 0; i < arr.length; ++i) {
    +    arr[i] = arr[i] & 0xFFFFFFFF;
    +  }
    +}
    +
    +// Note that these are in numerical order.
    +var TEST_BITS = [0x80000000, 0x00000000,
    +  0xb776d5f5, 0x5634e2db,
    +  0xffefffff, 0xffffffff,
    +  0xfff00000, 0x00000000,
    +  0xfffeffff, 0xffffffff,
    +  0xffff0000, 0x00000000,
    +  0xfffffffe, 0xffffffff,
    +  0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000,
    +  0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000,
    +  0x00000000, 0x00000001,
    +  0x00000000, 0x00000002,
    +  0x00000000, 0x00007fff,
    +  0x00000000, 0x00008000,
    +  0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000,
    +  0x00000000, 0x00ffffff,
    +  0x00000000, 0x01000000,
    +  0x00000000, 0x5634e2db,
    +  0x00000000, 0xb776d5f5,
    +  0x00000000, 0xffffffff,
    +  0x00000001, 0x00000000,
    +  0x0000ffff, 0xffffffff,
    +  0x00010000, 0x00000000,
    +  0x000fffff, 0xffffffff,
    +  0x00100000, 0x00000000,
    +  0x5634e2db, 0xb776d5f5,
    +  0x7fffffff, 0xffffffff];
    +toInt32s(TEST_BITS);
    +
    +var TEST_ADD_BITS = [
    +  0x3776d5f5, 0x5634e2db, 0x7fefffff, 0xffffffff, 0xb766d5f5, 0x5634e2da,
    +  0x7ff00000, 0x00000000, 0xb766d5f5, 0x5634e2db, 0xffdfffff, 0xffffffff,
    +  0x7ffeffff, 0xffffffff, 0xb775d5f5, 0x5634e2da, 0xffeeffff, 0xfffffffe,
    +  0xffeeffff, 0xffffffff, 0x7fff0000, 0x00000000, 0xb775d5f5, 0x5634e2db,
    +  0xffeeffff, 0xffffffff, 0xffef0000, 0x00000000, 0xfffdffff, 0xffffffff,
    +  0x7ffffffe, 0xffffffff, 0xb776d5f4, 0x5634e2da, 0xffeffffe, 0xfffffffe,
    +  0xffeffffe, 0xffffffff, 0xfffefffe, 0xfffffffe, 0xfffefffe, 0xffffffff,
    +  0x7fffffff, 0x00000000, 0xb776d5f4, 0x5634e2db, 0xffeffffe, 0xffffffff,
    +  0xffefffff, 0x00000000, 0xfffefffe, 0xffffffff, 0xfffeffff, 0x00000000,
    +  0xfffffffd, 0xffffffff, 0x7fffffff, 0xfeffffff, 0xb776d5f5, 0x5534e2da,
    +  0xffefffff, 0xfefffffe, 0xffefffff, 0xfeffffff, 0xfffeffff, 0xfefffffe,
    +  0xfffeffff, 0xfeffffff, 0xfffffffe, 0xfefffffe, 0xfffffffe, 0xfeffffff,
    +  0x7fffffff, 0xff000000, 0xb776d5f5, 0x5534e2db, 0xffefffff, 0xfeffffff,
    +  0xffefffff, 0xff000000, 0xfffeffff, 0xfeffffff, 0xfffeffff, 0xff000000,
    +  0xfffffffe, 0xfeffffff, 0xfffffffe, 0xff000000, 0xffffffff, 0xfdffffff,
    +  0x7fffffff, 0xfffeffff, 0xb776d5f5, 0x5633e2da, 0xffefffff, 0xfffefffe,
    +  0xffefffff, 0xfffeffff, 0xfffeffff, 0xfffefffe, 0xfffeffff, 0xfffeffff,
    +  0xfffffffe, 0xfffefffe, 0xfffffffe, 0xfffeffff, 0xffffffff, 0xfefefffe,
    +  0xffffffff, 0xfefeffff, 0x7fffffff, 0xffff0000, 0xb776d5f5, 0x5633e2db,
    +  0xffefffff, 0xfffeffff, 0xffefffff, 0xffff0000, 0xfffeffff, 0xfffeffff,
    +  0xfffeffff, 0xffff0000, 0xfffffffe, 0xfffeffff, 0xfffffffe, 0xffff0000,
    +  0xffffffff, 0xfefeffff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfffdffff,
    +  0x7fffffff, 0xffff7fff, 0xb776d5f5, 0x563462da, 0xffefffff, 0xffff7ffe,
    +  0xffefffff, 0xffff7fff, 0xfffeffff, 0xffff7ffe, 0xfffeffff, 0xffff7fff,
    +  0xfffffffe, 0xffff7ffe, 0xfffffffe, 0xffff7fff, 0xffffffff, 0xfeff7ffe,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xfffe7ffe, 0xffffffff, 0xfffe7fff,
    +  0x7fffffff, 0xffff8000, 0xb776d5f5, 0x563462db, 0xffefffff, 0xffff7fff,
    +  0xffefffff, 0xffff8000, 0xfffeffff, 0xffff7fff, 0xfffeffff, 0xffff8000,
    +  0xfffffffe, 0xffff7fff, 0xfffffffe, 0xffff8000, 0xffffffff, 0xfeff7fff,
    +  0xffffffff, 0xfeff8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfffe8000,
    +  0xffffffff, 0xfffeffff, 0x7fffffff, 0xfffffffe, 0xb776d5f5, 0x5634e2d9,
    +  0xffefffff, 0xfffffffd, 0xffefffff, 0xfffffffe, 0xfffeffff, 0xfffffffd,
    +  0xfffeffff, 0xfffffffe, 0xfffffffe, 0xfffffffd, 0xfffffffe, 0xfffffffe,
    +  0xffffffff, 0xfefffffd, 0xffffffff, 0xfefffffe, 0xffffffff, 0xfffefffd,
    +  0xffffffff, 0xfffefffe, 0xffffffff, 0xffff7ffd, 0xffffffff, 0xffff7ffe,
    +  0x7fffffff, 0xffffffff, 0xb776d5f5, 0x5634e2da, 0xffefffff, 0xfffffffe,
    +  0xffefffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xffffffff,
    +  0xfffffffe, 0xfffffffe, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfefffffe,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff7ffe, 0xffffffff, 0xffff7fff, 0xffffffff, 0xfffffffd,
    +  0x80000000, 0x00000000, 0xb776d5f5, 0x5634e2db, 0xffefffff, 0xffffffff,
    +  0xfff00000, 0x00000000, 0xfffeffff, 0xffffffff, 0xffff0000, 0x00000000,
    +  0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xfffeffff, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x80000000, 0x00000001, 0xb776d5f5, 0x5634e2dc,
    +  0xfff00000, 0x00000000, 0xfff00000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xffff0000, 0x00000001, 0xffffffff, 0x00000000, 0xffffffff, 0x00000001,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xff000001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x80000000, 0x00000002, 0xb776d5f5, 0x5634e2dd, 0xfff00000, 0x00000001,
    +  0xfff00000, 0x00000002, 0xffff0000, 0x00000001, 0xffff0000, 0x00000002,
    +  0xffffffff, 0x00000001, 0xffffffff, 0x00000002, 0xffffffff, 0xff000001,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0002,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff8002, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000003,
    +  0x80000000, 0x00007fff, 0xb776d5f5, 0x563562da, 0xfff00000, 0x00007ffe,
    +  0xfff00000, 0x00007fff, 0xffff0000, 0x00007ffe, 0xffff0000, 0x00007fff,
    +  0xffffffff, 0x00007ffe, 0xffffffff, 0x00007fff, 0xffffffff, 0xff007ffe,
    +  0xffffffff, 0xff007fff, 0xffffffff, 0xffff7ffe, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00007ffd,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00008001, 0x80000000, 0x00008000, 0xb776d5f5, 0x563562db,
    +  0xfff00000, 0x00007fff, 0xfff00000, 0x00008000, 0xffff0000, 0x00007fff,
    +  0xffff0000, 0x00008000, 0xffffffff, 0x00007fff, 0xffffffff, 0x00008000,
    +  0xffffffff, 0xff007fff, 0xffffffff, 0xff008000, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008002, 0x00000000, 0x0000ffff,
    +  0x80000000, 0x0000ffff, 0xb776d5f5, 0x5635e2da, 0xfff00000, 0x0000fffe,
    +  0xfff00000, 0x0000ffff, 0xffff0000, 0x0000fffe, 0xffff0000, 0x0000ffff,
    +  0xffffffff, 0x0000fffe, 0xffffffff, 0x0000ffff, 0xffffffff, 0xff00fffe,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00007ffe, 0x00000000, 0x00007fff, 0x00000000, 0x0000fffd,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00017ffe, 0x00000000, 0x00017fff,
    +  0x80000000, 0x00010000, 0xb776d5f5, 0x5635e2db, 0xfff00000, 0x0000ffff,
    +  0xfff00000, 0x00010000, 0xffff0000, 0x0000ffff, 0xffff0000, 0x00010000,
    +  0xffffffff, 0x0000ffff, 0xffffffff, 0x00010000, 0xffffffff, 0xff00ffff,
    +  0xffffffff, 0xff010000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00008000, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00010000, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010002, 0x00000000, 0x00017fff, 0x00000000, 0x00018000,
    +  0x00000000, 0x0001ffff, 0x80000000, 0x00ffffff, 0xb776d5f5, 0x5734e2da,
    +  0xfff00000, 0x00fffffe, 0xfff00000, 0x00ffffff, 0xffff0000, 0x00fffffe,
    +  0xffff0000, 0x00ffffff, 0xffffffff, 0x00fffffe, 0xffffffff, 0x00ffffff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00fefffe,
    +  0x00000000, 0x00feffff, 0x00000000, 0x00ff7ffe, 0x00000000, 0x00ff7fff,
    +  0x00000000, 0x00fffffd, 0x00000000, 0x00fffffe, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x01000000, 0x00000000, 0x01000001, 0x00000000, 0x01007ffe,
    +  0x00000000, 0x01007fff, 0x00000000, 0x0100fffe, 0x00000000, 0x0100ffff,
    +  0x80000000, 0x01000000, 0xb776d5f5, 0x5734e2db, 0xfff00000, 0x00ffffff,
    +  0xfff00000, 0x01000000, 0xffff0000, 0x00ffffff, 0xffff0000, 0x01000000,
    +  0xffffffff, 0x00ffffff, 0xffffffff, 0x01000000, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00feffff, 0x00000000, 0x00ff0000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00ff8000, 0x00000000, 0x00fffffe,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01000000, 0x00000000, 0x01000001,
    +  0x00000000, 0x01000002, 0x00000000, 0x01007fff, 0x00000000, 0x01008000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x01010000, 0x00000000, 0x01ffffff,
    +  0x80000000, 0x5634e2db, 0xb776d5f5, 0xac69c5b6, 0xfff00000, 0x5634e2da,
    +  0xfff00000, 0x5634e2db, 0xffff0000, 0x5634e2da, 0xffff0000, 0x5634e2db,
    +  0xffffffff, 0x5634e2da, 0xffffffff, 0x5634e2db, 0x00000000, 0x5534e2da,
    +  0x00000000, 0x5534e2db, 0x00000000, 0x5633e2da, 0x00000000, 0x5633e2db,
    +  0x00000000, 0x563462da, 0x00000000, 0x563462db, 0x00000000, 0x5634e2d9,
    +  0x00000000, 0x5634e2da, 0x00000000, 0x5634e2db, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2dd, 0x00000000, 0x563562da, 0x00000000, 0x563562db,
    +  0x00000000, 0x5635e2da, 0x00000000, 0x5635e2db, 0x00000000, 0x5734e2da,
    +  0x00000000, 0x5734e2db, 0x80000000, 0xb776d5f5, 0xb776d5f6, 0x0dabb8d0,
    +  0xfff00000, 0xb776d5f4, 0xfff00000, 0xb776d5f5, 0xffff0000, 0xb776d5f4,
    +  0xffff0000, 0xb776d5f5, 0xffffffff, 0xb776d5f4, 0xffffffff, 0xb776d5f5,
    +  0x00000000, 0xb676d5f4, 0x00000000, 0xb676d5f5, 0x00000000, 0xb775d5f4,
    +  0x00000000, 0xb775d5f5, 0x00000000, 0xb77655f4, 0x00000000, 0xb77655f5,
    +  0x00000000, 0xb776d5f3, 0x00000000, 0xb776d5f4, 0x00000000, 0xb776d5f5,
    +  0x00000000, 0xb776d5f6, 0x00000000, 0xb776d5f7, 0x00000000, 0xb77755f4,
    +  0x00000000, 0xb77755f5, 0x00000000, 0xb777d5f4, 0x00000000, 0xb777d5f5,
    +  0x00000000, 0xb876d5f4, 0x00000000, 0xb876d5f5, 0x00000001, 0x0dabb8d0,
    +  0x80000000, 0xffffffff, 0xb776d5f6, 0x5634e2da, 0xfff00000, 0xfffffffe,
    +  0xfff00000, 0xffffffff, 0xffff0000, 0xfffffffe, 0xffff0000, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0xfefffffe,
    +  0x00000000, 0xfeffffff, 0x00000000, 0xfffefffe, 0x00000000, 0xfffeffff,
    +  0x00000000, 0xffff7ffe, 0x00000000, 0xffff7fff, 0x00000000, 0xfffffffd,
    +  0x00000000, 0xfffffffe, 0x00000000, 0xffffffff, 0x00000001, 0x00000000,
    +  0x00000001, 0x00000001, 0x00000001, 0x00007ffe, 0x00000001, 0x00007fff,
    +  0x00000001, 0x0000fffe, 0x00000001, 0x0000ffff, 0x00000001, 0x00fffffe,
    +  0x00000001, 0x00ffffff, 0x00000001, 0x5634e2da, 0x00000001, 0xb776d5f4,
    +  0x80000001, 0x00000000, 0xb776d5f6, 0x5634e2db, 0xfff00000, 0xffffffff,
    +  0xfff00001, 0x00000000, 0xffff0000, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xfeffffff,
    +  0x00000000, 0xff000000, 0x00000000, 0xfffeffff, 0x00000000, 0xffff0000,
    +  0x00000000, 0xffff7fff, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xffffffff, 0x00000001, 0x00000000, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000002, 0x00000001, 0x00007fff, 0x00000001, 0x00008000,
    +  0x00000001, 0x0000ffff, 0x00000001, 0x00010000, 0x00000001, 0x00ffffff,
    +  0x00000001, 0x01000000, 0x00000001, 0x5634e2db, 0x00000001, 0xb776d5f5,
    +  0x00000001, 0xffffffff, 0x8000ffff, 0xffffffff, 0xb777d5f5, 0x5634e2da,
    +  0xfff0ffff, 0xfffffffe, 0xfff0ffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x0000fffe, 0xfffffffe, 0x0000fffe, 0xffffffff,
    +  0x0000ffff, 0xfefffffe, 0x0000ffff, 0xfeffffff, 0x0000ffff, 0xfffefffe,
    +  0x0000ffff, 0xfffeffff, 0x0000ffff, 0xffff7ffe, 0x0000ffff, 0xffff7fff,
    +  0x0000ffff, 0xfffffffd, 0x0000ffff, 0xfffffffe, 0x0000ffff, 0xffffffff,
    +  0x00010000, 0x00000000, 0x00010000, 0x00000001, 0x00010000, 0x00007ffe,
    +  0x00010000, 0x00007fff, 0x00010000, 0x0000fffe, 0x00010000, 0x0000ffff,
    +  0x00010000, 0x00fffffe, 0x00010000, 0x00ffffff, 0x00010000, 0x5634e2da,
    +  0x00010000, 0xb776d5f4, 0x00010000, 0xfffffffe, 0x00010000, 0xffffffff,
    +  0x80010000, 0x00000000, 0xb777d5f5, 0x5634e2db, 0xfff0ffff, 0xffffffff,
    +  0xfff10000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x0000fffe, 0xffffffff, 0x0000ffff, 0x00000000, 0x0000ffff, 0xfeffffff,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xfffeffff, 0x0000ffff, 0xffff0000,
    +  0x0000ffff, 0xffff7fff, 0x0000ffff, 0xffff8000, 0x0000ffff, 0xfffffffe,
    +  0x0000ffff, 0xffffffff, 0x00010000, 0x00000000, 0x00010000, 0x00000001,
    +  0x00010000, 0x00000002, 0x00010000, 0x00007fff, 0x00010000, 0x00008000,
    +  0x00010000, 0x0000ffff, 0x00010000, 0x00010000, 0x00010000, 0x00ffffff,
    +  0x00010000, 0x01000000, 0x00010000, 0x5634e2db, 0x00010000, 0xb776d5f5,
    +  0x00010000, 0xffffffff, 0x00010001, 0x00000000, 0x0001ffff, 0xffffffff,
    +  0x800fffff, 0xffffffff, 0xb786d5f5, 0x5634e2da, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0x000effff, 0xfffffffe, 0x000effff, 0xffffffff,
    +  0x000ffffe, 0xfffffffe, 0x000ffffe, 0xffffffff, 0x000fffff, 0xfefffffe,
    +  0x000fffff, 0xfeffffff, 0x000fffff, 0xfffefffe, 0x000fffff, 0xfffeffff,
    +  0x000fffff, 0xffff7ffe, 0x000fffff, 0xffff7fff, 0x000fffff, 0xfffffffd,
    +  0x000fffff, 0xfffffffe, 0x000fffff, 0xffffffff, 0x00100000, 0x00000000,
    +  0x00100000, 0x00000001, 0x00100000, 0x00007ffe, 0x00100000, 0x00007fff,
    +  0x00100000, 0x0000fffe, 0x00100000, 0x0000ffff, 0x00100000, 0x00fffffe,
    +  0x00100000, 0x00ffffff, 0x00100000, 0x5634e2da, 0x00100000, 0xb776d5f4,
    +  0x00100000, 0xfffffffe, 0x00100000, 0xffffffff, 0x0010ffff, 0xfffffffe,
    +  0x0010ffff, 0xffffffff, 0x80100000, 0x00000000, 0xb786d5f5, 0x5634e2db,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x000f0000, 0x00000000, 0x000ffffe, 0xffffffff, 0x000fffff, 0x00000000,
    +  0x000fffff, 0xfeffffff, 0x000fffff, 0xff000000, 0x000fffff, 0xfffeffff,
    +  0x000fffff, 0xffff0000, 0x000fffff, 0xffff7fff, 0x000fffff, 0xffff8000,
    +  0x000fffff, 0xfffffffe, 0x000fffff, 0xffffffff, 0x00100000, 0x00000000,
    +  0x00100000, 0x00000001, 0x00100000, 0x00000002, 0x00100000, 0x00007fff,
    +  0x00100000, 0x00008000, 0x00100000, 0x0000ffff, 0x00100000, 0x00010000,
    +  0x00100000, 0x00ffffff, 0x00100000, 0x01000000, 0x00100000, 0x5634e2db,
    +  0x00100000, 0xb776d5f5, 0x00100000, 0xffffffff, 0x00100001, 0x00000000,
    +  0x0010ffff, 0xffffffff, 0x00110000, 0x00000000, 0x001fffff, 0xffffffff,
    +  0xd634e2db, 0xb776d5f5, 0x0dabb8d1, 0x0dabb8d0, 0x5624e2db, 0xb776d5f4,
    +  0x5624e2db, 0xb776d5f5, 0x5633e2db, 0xb776d5f4, 0x5633e2db, 0xb776d5f5,
    +  0x5634e2da, 0xb776d5f4, 0x5634e2da, 0xb776d5f5, 0x5634e2db, 0xb676d5f4,
    +  0x5634e2db, 0xb676d5f5, 0x5634e2db, 0xb775d5f4, 0x5634e2db, 0xb775d5f5,
    +  0x5634e2db, 0xb77655f4, 0x5634e2db, 0xb77655f5, 0x5634e2db, 0xb776d5f3,
    +  0x5634e2db, 0xb776d5f4, 0x5634e2db, 0xb776d5f5, 0x5634e2db, 0xb776d5f6,
    +  0x5634e2db, 0xb776d5f7, 0x5634e2db, 0xb77755f4, 0x5634e2db, 0xb77755f5,
    +  0x5634e2db, 0xb777d5f4, 0x5634e2db, 0xb777d5f5, 0x5634e2db, 0xb876d5f4,
    +  0x5634e2db, 0xb876d5f5, 0x5634e2dc, 0x0dabb8d0, 0x5634e2dc, 0x6eedabea,
    +  0x5634e2dc, 0xb776d5f4, 0x5634e2dc, 0xb776d5f5, 0x5635e2db, 0xb776d5f4,
    +  0x5635e2db, 0xb776d5f5, 0x5644e2db, 0xb776d5f4, 0x5644e2db, 0xb776d5f5,
    +  0xffffffff, 0xffffffff, 0x3776d5f5, 0x5634e2da, 0x7fefffff, 0xfffffffe,
    +  0x7fefffff, 0xffffffff, 0x7ffeffff, 0xfffffffe, 0x7ffeffff, 0xffffffff,
    +  0x7ffffffe, 0xfffffffe, 0x7ffffffe, 0xffffffff, 0x7fffffff, 0xfefffffe,
    +  0x7fffffff, 0xfeffffff, 0x7fffffff, 0xfffefffe, 0x7fffffff, 0xfffeffff,
    +  0x7fffffff, 0xffff7ffe, 0x7fffffff, 0xffff7fff, 0x7fffffff, 0xfffffffd,
    +  0x7fffffff, 0xfffffffe, 0x7fffffff, 0xffffffff, 0x80000000, 0x00000000,
    +  0x80000000, 0x00000001, 0x80000000, 0x00007ffe, 0x80000000, 0x00007fff,
    +  0x80000000, 0x0000fffe, 0x80000000, 0x0000ffff, 0x80000000, 0x00fffffe,
    +  0x80000000, 0x00ffffff, 0x80000000, 0x5634e2da, 0x80000000, 0xb776d5f4,
    +  0x80000000, 0xfffffffe, 0x80000000, 0xffffffff, 0x8000ffff, 0xfffffffe,
    +  0x8000ffff, 0xffffffff, 0x800fffff, 0xfffffffe, 0x800fffff, 0xffffffff,
    +  0xd634e2db, 0xb776d5f4
    +];
    +toInt32s(TEST_ADD_BITS);
    +
    +var TEST_SUB_BITS = [
    +  0x00000000, 0x00000000, 0xc8892a0a, 0xa9cb1d25, 0x80100000, 0x00000001,
    +  0x80100000, 0x00000000, 0x80010000, 0x00000001, 0x80010000, 0x00000000,
    +  0x80000001, 0x00000001, 0x80000001, 0x00000000, 0x80000000, 0x01000001,
    +  0x80000000, 0x01000000, 0x80000000, 0x00010001, 0x80000000, 0x00010000,
    +  0x80000000, 0x00008001, 0x80000000, 0x00008000, 0x80000000, 0x00000002,
    +  0x80000000, 0x00000001, 0x80000000, 0x00000000, 0x7fffffff, 0xffffffff,
    +  0x7fffffff, 0xfffffffe, 0x7fffffff, 0xffff8001, 0x7fffffff, 0xffff8000,
    +  0x7fffffff, 0xffff0001, 0x7fffffff, 0xffff0000, 0x7fffffff, 0xff000001,
    +  0x7fffffff, 0xff000000, 0x7fffffff, 0xa9cb1d25, 0x7fffffff, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0x7fffffff, 0x00000000, 0x7fff0000, 0x00000001,
    +  0x7fff0000, 0x00000000, 0x7ff00000, 0x00000001, 0x7ff00000, 0x00000000,
    +  0x29cb1d24, 0x48892a0b, 0x00000000, 0x00000001, 0x3776d5f5, 0x5634e2db,
    +  0x00000000, 0x00000000, 0xb786d5f5, 0x5634e2dc, 0xb786d5f5, 0x5634e2db,
    +  0xb777d5f5, 0x5634e2dc, 0xb777d5f5, 0x5634e2db, 0xb776d5f6, 0x5634e2dc,
    +  0xb776d5f6, 0x5634e2db, 0xb776d5f5, 0x5734e2dc, 0xb776d5f5, 0x5734e2db,
    +  0xb776d5f5, 0x5635e2dc, 0xb776d5f5, 0x5635e2db, 0xb776d5f5, 0x563562dc,
    +  0xb776d5f5, 0x563562db, 0xb776d5f5, 0x5634e2dd, 0xb776d5f5, 0x5634e2dc,
    +  0xb776d5f5, 0x5634e2db, 0xb776d5f5, 0x5634e2da, 0xb776d5f5, 0x5634e2d9,
    +  0xb776d5f5, 0x563462dc, 0xb776d5f5, 0x563462db, 0xb776d5f5, 0x5633e2dc,
    +  0xb776d5f5, 0x5633e2db, 0xb776d5f5, 0x5534e2dc, 0xb776d5f5, 0x5534e2db,
    +  0xb776d5f5, 0x00000000, 0xb776d5f4, 0x9ebe0ce6, 0xb776d5f4, 0x5634e2dc,
    +  0xb776d5f4, 0x5634e2db, 0xb775d5f5, 0x5634e2dc, 0xb775d5f5, 0x5634e2db,
    +  0xb766d5f5, 0x5634e2dc, 0xb766d5f5, 0x5634e2db, 0x6141f319, 0x9ebe0ce6,
    +  0x3776d5f5, 0x5634e2dc, 0x7fefffff, 0xffffffff, 0x48792a0a, 0xa9cb1d24,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xfff10000, 0x00000000,
    +  0xfff0ffff, 0xffffffff, 0xfff00001, 0x00000000, 0xfff00000, 0xffffffff,
    +  0xfff00000, 0x01000000, 0xfff00000, 0x00ffffff, 0xfff00000, 0x00010000,
    +  0xfff00000, 0x0000ffff, 0xfff00000, 0x00008000, 0xfff00000, 0x00007fff,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xffefffff, 0xfffffffd, 0xffefffff, 0xffff8000,
    +  0xffefffff, 0xffff7fff, 0xffefffff, 0xffff0000, 0xffefffff, 0xfffeffff,
    +  0xffefffff, 0xff000000, 0xffefffff, 0xfeffffff, 0xffefffff, 0xa9cb1d24,
    +  0xffefffff, 0x48892a0a, 0xffefffff, 0x00000000, 0xffeffffe, 0xffffffff,
    +  0xffef0000, 0x00000000, 0xffeeffff, 0xffffffff, 0xffe00000, 0x00000000,
    +  0xffdfffff, 0xffffffff, 0xa9bb1d24, 0x48892a0a, 0x7ff00000, 0x00000000,
    +  0x7ff00000, 0x00000000, 0x48792a0a, 0xa9cb1d25, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xfff10000, 0x00000001, 0xfff10000, 0x00000000,
    +  0xfff00001, 0x00000001, 0xfff00001, 0x00000000, 0xfff00000, 0x01000001,
    +  0xfff00000, 0x01000000, 0xfff00000, 0x00010001, 0xfff00000, 0x00010000,
    +  0xfff00000, 0x00008001, 0xfff00000, 0x00008000, 0xfff00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xffefffff, 0xffff8001, 0xffefffff, 0xffff8000,
    +  0xffefffff, 0xffff0001, 0xffefffff, 0xffff0000, 0xffefffff, 0xff000001,
    +  0xffefffff, 0xff000000, 0xffefffff, 0xa9cb1d25, 0xffefffff, 0x48892a0b,
    +  0xffefffff, 0x00000001, 0xffefffff, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffef0000, 0x00000000, 0xffe00000, 0x00000001, 0xffe00000, 0x00000000,
    +  0xa9bb1d24, 0x48892a0b, 0x7ff00000, 0x00000001, 0x7ffeffff, 0xffffffff,
    +  0x48882a0a, 0xa9cb1d24, 0x000f0000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffff0000, 0xffffffff, 0xffff0000, 0x01000000, 0xffff0000, 0x00ffffff,
    +  0xffff0000, 0x00010000, 0xffff0000, 0x0000ffff, 0xffff0000, 0x00008000,
    +  0xffff0000, 0x00007fff, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xfffffffd,
    +  0xfffeffff, 0xffff8000, 0xfffeffff, 0xffff7fff, 0xfffeffff, 0xffff0000,
    +  0xfffeffff, 0xfffeffff, 0xfffeffff, 0xff000000, 0xfffeffff, 0xfeffffff,
    +  0xfffeffff, 0xa9cb1d24, 0xfffeffff, 0x48892a0a, 0xfffeffff, 0x00000000,
    +  0xfffefffe, 0xffffffff, 0xfffe0000, 0x00000000, 0xfffdffff, 0xffffffff,
    +  0xffef0000, 0x00000000, 0xffeeffff, 0xffffffff, 0xa9ca1d24, 0x48892a0a,
    +  0x7fff0000, 0x00000000, 0x7fff0000, 0x00000000, 0x48882a0a, 0xa9cb1d25,
    +  0x000f0000, 0x00000001, 0x000f0000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffff0001, 0x00000001, 0xffff0001, 0x00000000,
    +  0xffff0000, 0x01000001, 0xffff0000, 0x01000000, 0xffff0000, 0x00010001,
    +  0xffff0000, 0x00010000, 0xffff0000, 0x00008001, 0xffff0000, 0x00008000,
    +  0xffff0000, 0x00000002, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xfffeffff, 0xffff8001,
    +  0xfffeffff, 0xffff8000, 0xfffeffff, 0xffff0001, 0xfffeffff, 0xffff0000,
    +  0xfffeffff, 0xff000001, 0xfffeffff, 0xff000000, 0xfffeffff, 0xa9cb1d25,
    +  0xfffeffff, 0x48892a0b, 0xfffeffff, 0x00000001, 0xfffeffff, 0x00000000,
    +  0xfffe0000, 0x00000001, 0xfffe0000, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffef0000, 0x00000000, 0xa9ca1d24, 0x48892a0b, 0x7fff0000, 0x00000001,
    +  0x7ffffffe, 0xffffffff, 0x48892a09, 0xa9cb1d24, 0x000fffff, 0x00000000,
    +  0x000ffffe, 0xffffffff, 0x0000ffff, 0x00000000, 0x0000fffe, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x01000000,
    +  0xffffffff, 0x00ffffff, 0xffffffff, 0x00010000, 0xffffffff, 0x0000ffff,
    +  0xffffffff, 0x00008000, 0xffffffff, 0x00007fff, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffffffe, 0xfffffffd, 0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff7fff,
    +  0xfffffffe, 0xffff0000, 0xfffffffe, 0xfffeffff, 0xfffffffe, 0xff000000,
    +  0xfffffffe, 0xfeffffff, 0xfffffffe, 0xa9cb1d24, 0xfffffffe, 0x48892a0a,
    +  0xfffffffe, 0x00000000, 0xfffffffd, 0xffffffff, 0xfffeffff, 0x00000000,
    +  0xfffefffe, 0xffffffff, 0xffefffff, 0x00000000, 0xffeffffe, 0xffffffff,
    +  0xa9cb1d23, 0x48892a0a, 0x7fffffff, 0x00000000, 0x7fffffff, 0x00000000,
    +  0x48892a09, 0xa9cb1d25, 0x000fffff, 0x00000001, 0x000fffff, 0x00000000,
    +  0x0000ffff, 0x00000001, 0x0000ffff, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0x01000001, 0xffffffff, 0x01000000,
    +  0xffffffff, 0x00010001, 0xffffffff, 0x00010000, 0xffffffff, 0x00008001,
    +  0xffffffff, 0x00008000, 0xffffffff, 0x00000002, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffffffe, 0xffff8001, 0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff0001,
    +  0xfffffffe, 0xffff0000, 0xfffffffe, 0xff000001, 0xfffffffe, 0xff000000,
    +  0xfffffffe, 0xa9cb1d25, 0xfffffffe, 0x48892a0b, 0xfffffffe, 0x00000001,
    +  0xfffffffe, 0x00000000, 0xfffeffff, 0x00000001, 0xfffeffff, 0x00000000,
    +  0xffefffff, 0x00000001, 0xffefffff, 0x00000000, 0xa9cb1d23, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0x7fffffff, 0xfeffffff, 0x48892a0a, 0xa8cb1d24,
    +  0x000fffff, 0xff000000, 0x000fffff, 0xfeffffff, 0x0000ffff, 0xff000000,
    +  0x0000ffff, 0xfeffffff, 0x00000000, 0xff000000, 0x00000000, 0xfeffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xff008000, 0xffffffff, 0xff007fff,
    +  0xffffffff, 0xff000001, 0xffffffff, 0xff000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xfefffffe, 0xffffffff, 0xfefffffd, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfefeffff,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xfdffffff, 0xffffffff, 0xa8cb1d24,
    +  0xffffffff, 0x47892a0a, 0xfffffffe, 0xff000000, 0xfffffffe, 0xfeffffff,
    +  0xfffeffff, 0xff000000, 0xfffeffff, 0xfeffffff, 0xffefffff, 0xff000000,
    +  0xffefffff, 0xfeffffff, 0xa9cb1d24, 0x47892a0a, 0x7fffffff, 0xff000000,
    +  0x7fffffff, 0xff000000, 0x48892a0a, 0xa8cb1d25, 0x000fffff, 0xff000001,
    +  0x000fffff, 0xff000000, 0x0000ffff, 0xff000001, 0x0000ffff, 0xff000000,
    +  0x00000000, 0xff000001, 0x00000000, 0xff000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff010001, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff008001, 0xffffffff, 0xff008000, 0xffffffff, 0xff000002,
    +  0xffffffff, 0xff000001, 0xffffffff, 0xff000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xfefffffe, 0xffffffff, 0xfeff8001, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff0001, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfe000001,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xa8cb1d25, 0xffffffff, 0x47892a0b,
    +  0xfffffffe, 0xff000001, 0xfffffffe, 0xff000000, 0xfffeffff, 0xff000001,
    +  0xfffeffff, 0xff000000, 0xffefffff, 0xff000001, 0xffefffff, 0xff000000,
    +  0xa9cb1d24, 0x47892a0b, 0x7fffffff, 0xff000001, 0x7fffffff, 0xfffeffff,
    +  0x48892a0a, 0xa9ca1d24, 0x000fffff, 0xffff0000, 0x000fffff, 0xfffeffff,
    +  0x0000ffff, 0xffff0000, 0x0000ffff, 0xfffeffff, 0x00000000, 0xffff0000,
    +  0x00000000, 0xfffeffff, 0x00000000, 0x00ff0000, 0x00000000, 0x00feffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffefffd,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffdffff, 0xffffffff, 0xfeff0000, 0xffffffff, 0xfefeffff,
    +  0xffffffff, 0xa9ca1d24, 0xffffffff, 0x48882a0a, 0xfffffffe, 0xffff0000,
    +  0xfffffffe, 0xfffeffff, 0xfffeffff, 0xffff0000, 0xfffeffff, 0xfffeffff,
    +  0xffefffff, 0xffff0000, 0xffefffff, 0xfffeffff, 0xa9cb1d24, 0x48882a0a,
    +  0x7fffffff, 0xffff0000, 0x7fffffff, 0xffff0000, 0x48892a0a, 0xa9ca1d25,
    +  0x000fffff, 0xffff0001, 0x000fffff, 0xffff0000, 0x0000ffff, 0xffff0001,
    +  0x0000ffff, 0xffff0000, 0x00000000, 0xffff0001, 0x00000000, 0xffff0000,
    +  0x00000000, 0x00ff0001, 0x00000000, 0x00ff0000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff0002, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe, 0xffffffff, 0xfffe8001,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe0001, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfeff0001, 0xffffffff, 0xfeff0000, 0xffffffff, 0xa9ca1d25,
    +  0xffffffff, 0x48882a0b, 0xfffffffe, 0xffff0001, 0xfffffffe, 0xffff0000,
    +  0xfffeffff, 0xffff0001, 0xfffeffff, 0xffff0000, 0xffefffff, 0xffff0001,
    +  0xffefffff, 0xffff0000, 0xa9cb1d24, 0x48882a0b, 0x7fffffff, 0xffff0001,
    +  0x7fffffff, 0xffff7fff, 0x48892a0a, 0xa9ca9d24, 0x000fffff, 0xffff8000,
    +  0x000fffff, 0xffff7fff, 0x0000ffff, 0xffff8000, 0x0000ffff, 0xffff7fff,
    +  0x00000000, 0xffff8000, 0x00000000, 0xffff7fff, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00008000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff7ffe,
    +  0xffffffff, 0xffff7ffd, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfffe7fff, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xfeff7fff, 0xffffffff, 0xa9ca9d24, 0xffffffff, 0x4888aa0a,
    +  0xfffffffe, 0xffff8000, 0xfffffffe, 0xffff7fff, 0xfffeffff, 0xffff8000,
    +  0xfffeffff, 0xffff7fff, 0xffefffff, 0xffff8000, 0xffefffff, 0xffff7fff,
    +  0xa9cb1d24, 0x4888aa0a, 0x7fffffff, 0xffff8000, 0x7fffffff, 0xffff8000,
    +  0x48892a0a, 0xa9ca9d25, 0x000fffff, 0xffff8001, 0x000fffff, 0xffff8000,
    +  0x0000ffff, 0xffff8001, 0x0000ffff, 0xffff8000, 0x00000000, 0xffff8001,
    +  0x00000000, 0xffff8000, 0x00000000, 0x00ff8001, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8002, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff7ffe,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffe8001,
    +  0xffffffff, 0xfffe8000, 0xffffffff, 0xfeff8001, 0xffffffff, 0xfeff8000,
    +  0xffffffff, 0xa9ca9d25, 0xffffffff, 0x4888aa0b, 0xfffffffe, 0xffff8001,
    +  0xfffffffe, 0xffff8000, 0xfffeffff, 0xffff8001, 0xfffeffff, 0xffff8000,
    +  0xffefffff, 0xffff8001, 0xffefffff, 0xffff8000, 0xa9cb1d24, 0x4888aa0b,
    +  0x7fffffff, 0xffff8001, 0x7fffffff, 0xfffffffe, 0x48892a0a, 0xa9cb1d23,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x00fffffe, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x00007fff, 0x00000000, 0x00007ffe,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffd, 0xffffffff, 0xfffffffc, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff7ffe, 0xffffffff, 0xfffeffff, 0xffffffff, 0xfffefffe,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xfefffffe, 0xffffffff, 0xa9cb1d23,
    +  0xffffffff, 0x48892a09, 0xfffffffe, 0xffffffff, 0xfffffffe, 0xfffffffe,
    +  0xfffeffff, 0xffffffff, 0xfffeffff, 0xfffffffe, 0xffefffff, 0xffffffff,
    +  0xffefffff, 0xfffffffe, 0xa9cb1d24, 0x48892a09, 0x7fffffff, 0xffffffff,
    +  0x7fffffff, 0xffffffff, 0x48892a0a, 0xa9cb1d24, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x01000000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x00010000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00008000, 0x00000000, 0x00007fff, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffd, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff7fff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xfffeffff, 0xffffffff, 0xff000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xa9cb1d24, 0xffffffff, 0x48892a0a,
    +  0xffffffff, 0x00000000, 0xfffffffe, 0xffffffff, 0xffff0000, 0x00000000,
    +  0xfffeffff, 0xffffffff, 0xfff00000, 0x00000000, 0xffefffff, 0xffffffff,
    +  0xa9cb1d24, 0x48892a0a, 0x80000000, 0x00000000, 0x80000000, 0x00000000,
    +  0x48892a0a, 0xa9cb1d25, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0x01000001, 0x00000000, 0x01000000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00010000, 0x00000000, 0x00008001,
    +  0x00000000, 0x00008000, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xff000001, 0xffffffff, 0xff000000,
    +  0xffffffff, 0xa9cb1d25, 0xffffffff, 0x48892a0b, 0xffffffff, 0x00000001,
    +  0xffffffff, 0x00000000, 0xffff0000, 0x00000001, 0xffff0000, 0x00000000,
    +  0xfff00000, 0x00000001, 0xfff00000, 0x00000000, 0xa9cb1d24, 0x48892a0b,
    +  0x80000000, 0x00000001, 0x80000000, 0x00000001, 0x48892a0a, 0xa9cb1d26,
    +  0x00100000, 0x00000002, 0x00100000, 0x00000001, 0x00010000, 0x00000002,
    +  0x00010000, 0x00000001, 0x00000001, 0x00000002, 0x00000001, 0x00000001,
    +  0x00000000, 0x01000002, 0x00000000, 0x01000001, 0x00000000, 0x00010002,
    +  0x00000000, 0x00010001, 0x00000000, 0x00008002, 0x00000000, 0x00008001,
    +  0x00000000, 0x00000003, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8002,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0xffff0002, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xff000001, 0xffffffff, 0xa9cb1d26,
    +  0xffffffff, 0x48892a0c, 0xffffffff, 0x00000002, 0xffffffff, 0x00000001,
    +  0xffff0000, 0x00000002, 0xffff0000, 0x00000001, 0xfff00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0xa9cb1d24, 0x48892a0c, 0x80000000, 0x00000002,
    +  0x80000000, 0x00000002, 0x48892a0a, 0xa9cb1d27, 0x00100000, 0x00000003,
    +  0x00100000, 0x00000002, 0x00010000, 0x00000003, 0x00010000, 0x00000002,
    +  0x00000001, 0x00000003, 0x00000001, 0x00000002, 0x00000000, 0x01000003,
    +  0x00000000, 0x01000002, 0x00000000, 0x00010003, 0x00000000, 0x00010002,
    +  0x00000000, 0x00008003, 0x00000000, 0x00008002, 0x00000000, 0x00000004,
    +  0x00000000, 0x00000003, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8003, 0xffffffff, 0xffff8002,
    +  0xffffffff, 0xffff0003, 0xffffffff, 0xffff0002, 0xffffffff, 0xff000003,
    +  0xffffffff, 0xff000002, 0xffffffff, 0xa9cb1d27, 0xffffffff, 0x48892a0d,
    +  0xffffffff, 0x00000003, 0xffffffff, 0x00000002, 0xffff0000, 0x00000003,
    +  0xffff0000, 0x00000002, 0xfff00000, 0x00000003, 0xfff00000, 0x00000002,
    +  0xa9cb1d24, 0x48892a0d, 0x80000000, 0x00000003, 0x80000000, 0x00007fff,
    +  0x48892a0a, 0xa9cb9d24, 0x00100000, 0x00008000, 0x00100000, 0x00007fff,
    +  0x00010000, 0x00008000, 0x00010000, 0x00007fff, 0x00000001, 0x00008000,
    +  0x00000001, 0x00007fff, 0x00000000, 0x01008000, 0x00000000, 0x01007fff,
    +  0x00000000, 0x00018000, 0x00000000, 0x00017fff, 0x00000000, 0x00010000,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00008001, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00007ffe, 0x00000000, 0x00007ffd,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffff7fff, 0xffffffff, 0xff008000, 0xffffffff, 0xff007fff,
    +  0xffffffff, 0xa9cb9d24, 0xffffffff, 0x4889aa0a, 0xffffffff, 0x00008000,
    +  0xffffffff, 0x00007fff, 0xffff0000, 0x00008000, 0xffff0000, 0x00007fff,
    +  0xfff00000, 0x00008000, 0xfff00000, 0x00007fff, 0xa9cb1d24, 0x4889aa0a,
    +  0x80000000, 0x00008000, 0x80000000, 0x00008000, 0x48892a0a, 0xa9cb9d25,
    +  0x00100000, 0x00008001, 0x00100000, 0x00008000, 0x00010000, 0x00008001,
    +  0x00010000, 0x00008000, 0x00000001, 0x00008001, 0x00000001, 0x00008000,
    +  0x00000000, 0x01008001, 0x00000000, 0x01008000, 0x00000000, 0x00018001,
    +  0x00000000, 0x00018000, 0x00000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008002, 0x00000000, 0x00008001, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00007ffe, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xff008001, 0xffffffff, 0xff008000, 0xffffffff, 0xa9cb9d25,
    +  0xffffffff, 0x4889aa0b, 0xffffffff, 0x00008001, 0xffffffff, 0x00008000,
    +  0xffff0000, 0x00008001, 0xffff0000, 0x00008000, 0xfff00000, 0x00008001,
    +  0xfff00000, 0x00008000, 0xa9cb1d24, 0x4889aa0b, 0x80000000, 0x00008001,
    +  0x80000000, 0x0000ffff, 0x48892a0a, 0xa9cc1d24, 0x00100000, 0x00010000,
    +  0x00100000, 0x0000ffff, 0x00010000, 0x00010000, 0x00010000, 0x0000ffff,
    +  0x00000001, 0x00010000, 0x00000001, 0x0000ffff, 0x00000000, 0x01010000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x00020000, 0x00000000, 0x0001ffff,
    +  0x00000000, 0x00018000, 0x00000000, 0x00017fff, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010000, 0x00000000, 0x0000ffff, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x0000fffd, 0x00000000, 0x00008000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xff00ffff, 0xffffffff, 0xa9cc1d24, 0xffffffff, 0x488a2a0a,
    +  0xffffffff, 0x00010000, 0xffffffff, 0x0000ffff, 0xffff0000, 0x00010000,
    +  0xffff0000, 0x0000ffff, 0xfff00000, 0x00010000, 0xfff00000, 0x0000ffff,
    +  0xa9cb1d24, 0x488a2a0a, 0x80000000, 0x00010000, 0x80000000, 0x00010000,
    +  0x48892a0a, 0xa9cc1d25, 0x00100000, 0x00010001, 0x00100000, 0x00010000,
    +  0x00010000, 0x00010001, 0x00010000, 0x00010000, 0x00000001, 0x00010001,
    +  0x00000001, 0x00010000, 0x00000000, 0x01010001, 0x00000000, 0x01010000,
    +  0x00000000, 0x00020001, 0x00000000, 0x00020000, 0x00000000, 0x00018001,
    +  0x00000000, 0x00018000, 0x00000000, 0x00010002, 0x00000000, 0x00010001,
    +  0x00000000, 0x00010000, 0x00000000, 0x0000ffff, 0x00000000, 0x0000fffe,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff010001, 0xffffffff, 0xff010000,
    +  0xffffffff, 0xa9cc1d25, 0xffffffff, 0x488a2a0b, 0xffffffff, 0x00010001,
    +  0xffffffff, 0x00010000, 0xffff0000, 0x00010001, 0xffff0000, 0x00010000,
    +  0xfff00000, 0x00010001, 0xfff00000, 0x00010000, 0xa9cb1d24, 0x488a2a0b,
    +  0x80000000, 0x00010001, 0x80000000, 0x00ffffff, 0x48892a0a, 0xaacb1d24,
    +  0x00100000, 0x01000000, 0x00100000, 0x00ffffff, 0x00010000, 0x01000000,
    +  0x00010000, 0x00ffffff, 0x00000001, 0x01000000, 0x00000001, 0x00ffffff,
    +  0x00000000, 0x02000000, 0x00000000, 0x01ffffff, 0x00000000, 0x01010000,
    +  0x00000000, 0x0100ffff, 0x00000000, 0x01008000, 0x00000000, 0x01007fff,
    +  0x00000000, 0x01000001, 0x00000000, 0x01000000, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x00fffffe, 0x00000000, 0x00fffffd, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff7fff, 0x00000000, 0x00ff0000, 0x00000000, 0x00feffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xaacb1d24,
    +  0xffffffff, 0x49892a0a, 0xffffffff, 0x01000000, 0xffffffff, 0x00ffffff,
    +  0xffff0000, 0x01000000, 0xffff0000, 0x00ffffff, 0xfff00000, 0x01000000,
    +  0xfff00000, 0x00ffffff, 0xa9cb1d24, 0x49892a0a, 0x80000000, 0x01000000,
    +  0x80000000, 0x01000000, 0x48892a0a, 0xaacb1d25, 0x00100000, 0x01000001,
    +  0x00100000, 0x01000000, 0x00010000, 0x01000001, 0x00010000, 0x01000000,
    +  0x00000001, 0x01000001, 0x00000001, 0x01000000, 0x00000000, 0x02000001,
    +  0x00000000, 0x02000000, 0x00000000, 0x01010001, 0x00000000, 0x01010000,
    +  0x00000000, 0x01008001, 0x00000000, 0x01008000, 0x00000000, 0x01000002,
    +  0x00000000, 0x01000001, 0x00000000, 0x01000000, 0x00000000, 0x00ffffff,
    +  0x00000000, 0x00fffffe, 0x00000000, 0x00ff8001, 0x00000000, 0x00ff8000,
    +  0x00000000, 0x00ff0001, 0x00000000, 0x00ff0000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffffffff, 0xaacb1d25, 0xffffffff, 0x49892a0b,
    +  0xffffffff, 0x01000001, 0xffffffff, 0x01000000, 0xffff0000, 0x01000001,
    +  0xffff0000, 0x01000000, 0xfff00000, 0x01000001, 0xfff00000, 0x01000000,
    +  0xa9cb1d24, 0x49892a0b, 0x80000000, 0x01000001, 0x80000000, 0x5634e2db,
    +  0x48892a0b, 0x00000000, 0x00100000, 0x5634e2dc, 0x00100000, 0x5634e2db,
    +  0x00010000, 0x5634e2dc, 0x00010000, 0x5634e2db, 0x00000001, 0x5634e2dc,
    +  0x00000001, 0x5634e2db, 0x00000000, 0x5734e2dc, 0x00000000, 0x5734e2db,
    +  0x00000000, 0x5635e2dc, 0x00000000, 0x5635e2db, 0x00000000, 0x563562dc,
    +  0x00000000, 0x563562db, 0x00000000, 0x5634e2dd, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2db, 0x00000000, 0x5634e2da, 0x00000000, 0x5634e2d9,
    +  0x00000000, 0x563462dc, 0x00000000, 0x563462db, 0x00000000, 0x5633e2dc,
    +  0x00000000, 0x5633e2db, 0x00000000, 0x5534e2dc, 0x00000000, 0x5534e2db,
    +  0x00000000, 0x00000000, 0xffffffff, 0x9ebe0ce6, 0xffffffff, 0x5634e2dc,
    +  0xffffffff, 0x5634e2db, 0xffff0000, 0x5634e2dc, 0xffff0000, 0x5634e2db,
    +  0xfff00000, 0x5634e2dc, 0xfff00000, 0x5634e2db, 0xa9cb1d24, 0x9ebe0ce6,
    +  0x80000000, 0x5634e2dc, 0x80000000, 0xb776d5f5, 0x48892a0b, 0x6141f31a,
    +  0x00100000, 0xb776d5f6, 0x00100000, 0xb776d5f5, 0x00010000, 0xb776d5f6,
    +  0x00010000, 0xb776d5f5, 0x00000001, 0xb776d5f6, 0x00000001, 0xb776d5f5,
    +  0x00000000, 0xb876d5f6, 0x00000000, 0xb876d5f5, 0x00000000, 0xb777d5f6,
    +  0x00000000, 0xb777d5f5, 0x00000000, 0xb77755f6, 0x00000000, 0xb77755f5,
    +  0x00000000, 0xb776d5f7, 0x00000000, 0xb776d5f6, 0x00000000, 0xb776d5f5,
    +  0x00000000, 0xb776d5f4, 0x00000000, 0xb776d5f3, 0x00000000, 0xb77655f6,
    +  0x00000000, 0xb77655f5, 0x00000000, 0xb775d5f6, 0x00000000, 0xb775d5f5,
    +  0x00000000, 0xb676d5f6, 0x00000000, 0xb676d5f5, 0x00000000, 0x6141f31a,
    +  0x00000000, 0x00000000, 0xffffffff, 0xb776d5f6, 0xffffffff, 0xb776d5f5,
    +  0xffff0000, 0xb776d5f6, 0xffff0000, 0xb776d5f5, 0xfff00000, 0xb776d5f6,
    +  0xfff00000, 0xb776d5f5, 0xa9cb1d25, 0x00000000, 0x80000000, 0xb776d5f6,
    +  0x80000000, 0xffffffff, 0x48892a0b, 0xa9cb1d24, 0x00100001, 0x00000000,
    +  0x00100000, 0xffffffff, 0x00010001, 0x00000000, 0x00010000, 0xffffffff,
    +  0x00000002, 0x00000000, 0x00000001, 0xffffffff, 0x00000001, 0x01000000,
    +  0x00000001, 0x00ffffff, 0x00000001, 0x00010000, 0x00000001, 0x0000ffff,
    +  0x00000001, 0x00008000, 0x00000001, 0x00007fff, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xfffffffd, 0x00000000, 0xffff8000, 0x00000000, 0xffff7fff,
    +  0x00000000, 0xffff0000, 0x00000000, 0xfffeffff, 0x00000000, 0xff000000,
    +  0x00000000, 0xfeffffff, 0x00000000, 0xa9cb1d24, 0x00000000, 0x48892a0a,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffff0001, 0x00000000,
    +  0xffff0000, 0xffffffff, 0xfff00001, 0x00000000, 0xfff00000, 0xffffffff,
    +  0xa9cb1d25, 0x48892a0a, 0x80000001, 0x00000000, 0x80000001, 0x00000000,
    +  0x48892a0b, 0xa9cb1d25, 0x00100001, 0x00000001, 0x00100001, 0x00000000,
    +  0x00010001, 0x00000001, 0x00010001, 0x00000000, 0x00000002, 0x00000001,
    +  0x00000002, 0x00000000, 0x00000001, 0x01000001, 0x00000001, 0x01000000,
    +  0x00000001, 0x00010001, 0x00000001, 0x00010000, 0x00000001, 0x00008001,
    +  0x00000001, 0x00008000, 0x00000001, 0x00000002, 0x00000001, 0x00000001,
    +  0x00000001, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xfffffffe,
    +  0x00000000, 0xffff8001, 0x00000000, 0xffff8000, 0x00000000, 0xffff0001,
    +  0x00000000, 0xffff0000, 0x00000000, 0xff000001, 0x00000000, 0xff000000,
    +  0x00000000, 0xa9cb1d25, 0x00000000, 0x48892a0b, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xffff0001, 0x00000001, 0xffff0001, 0x00000000,
    +  0xfff00001, 0x00000001, 0xfff00001, 0x00000000, 0xa9cb1d25, 0x48892a0b,
    +  0x80000001, 0x00000001, 0x8000ffff, 0xffffffff, 0x488a2a0a, 0xa9cb1d24,
    +  0x00110000, 0x00000000, 0x0010ffff, 0xffffffff, 0x00020000, 0x00000000,
    +  0x0001ffff, 0xffffffff, 0x00010001, 0x00000000, 0x00010000, 0xffffffff,
    +  0x00010000, 0x01000000, 0x00010000, 0x00ffffff, 0x00010000, 0x00010000,
    +  0x00010000, 0x0000ffff, 0x00010000, 0x00008000, 0x00010000, 0x00007fff,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x0000ffff, 0xfffffffd, 0x0000ffff, 0xffff8000,
    +  0x0000ffff, 0xffff7fff, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xfffeffff,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xfeffffff, 0x0000ffff, 0xa9cb1d24,
    +  0x0000ffff, 0x48892a0a, 0x0000ffff, 0x00000000, 0x0000fffe, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xfff10000, 0x00000000,
    +  0xfff0ffff, 0xffffffff, 0xa9cc1d24, 0x48892a0a, 0x80010000, 0x00000000,
    +  0x80010000, 0x00000000, 0x488a2a0a, 0xa9cb1d25, 0x00110000, 0x00000001,
    +  0x00110000, 0x00000000, 0x00020000, 0x00000001, 0x00020000, 0x00000000,
    +  0x00010001, 0x00000001, 0x00010001, 0x00000000, 0x00010000, 0x01000001,
    +  0x00010000, 0x01000000, 0x00010000, 0x00010001, 0x00010000, 0x00010000,
    +  0x00010000, 0x00008001, 0x00010000, 0x00008000, 0x00010000, 0x00000002,
    +  0x00010000, 0x00000001, 0x00010000, 0x00000000, 0x0000ffff, 0xffffffff,
    +  0x0000ffff, 0xfffffffe, 0x0000ffff, 0xffff8001, 0x0000ffff, 0xffff8000,
    +  0x0000ffff, 0xffff0001, 0x0000ffff, 0xffff0000, 0x0000ffff, 0xff000001,
    +  0x0000ffff, 0xff000000, 0x0000ffff, 0xa9cb1d25, 0x0000ffff, 0x48892a0b,
    +  0x0000ffff, 0x00000001, 0x0000ffff, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xfff10000, 0x00000001, 0xfff10000, 0x00000000,
    +  0xa9cc1d24, 0x48892a0b, 0x80010000, 0x00000001, 0x800fffff, 0xffffffff,
    +  0x48992a0a, 0xa9cb1d24, 0x00200000, 0x00000000, 0x001fffff, 0xffffffff,
    +  0x00110000, 0x00000000, 0x0010ffff, 0xffffffff, 0x00100001, 0x00000000,
    +  0x00100000, 0xffffffff, 0x00100000, 0x01000000, 0x00100000, 0x00ffffff,
    +  0x00100000, 0x00010000, 0x00100000, 0x0000ffff, 0x00100000, 0x00008000,
    +  0x00100000, 0x00007fff, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x000fffff, 0xfffffffd,
    +  0x000fffff, 0xffff8000, 0x000fffff, 0xffff7fff, 0x000fffff, 0xffff0000,
    +  0x000fffff, 0xfffeffff, 0x000fffff, 0xff000000, 0x000fffff, 0xfeffffff,
    +  0x000fffff, 0xa9cb1d24, 0x000fffff, 0x48892a0a, 0x000fffff, 0x00000000,
    +  0x000ffffe, 0xffffffff, 0x000f0000, 0x00000000, 0x000effff, 0xffffffff,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xa9db1d24, 0x48892a0a,
    +  0x80100000, 0x00000000, 0x80100000, 0x00000000, 0x48992a0a, 0xa9cb1d25,
    +  0x00200000, 0x00000001, 0x00200000, 0x00000000, 0x00110000, 0x00000001,
    +  0x00110000, 0x00000000, 0x00100001, 0x00000001, 0x00100001, 0x00000000,
    +  0x00100000, 0x01000001, 0x00100000, 0x01000000, 0x00100000, 0x00010001,
    +  0x00100000, 0x00010000, 0x00100000, 0x00008001, 0x00100000, 0x00008000,
    +  0x00100000, 0x00000002, 0x00100000, 0x00000001, 0x00100000, 0x00000000,
    +  0x000fffff, 0xffffffff, 0x000fffff, 0xfffffffe, 0x000fffff, 0xffff8001,
    +  0x000fffff, 0xffff8000, 0x000fffff, 0xffff0001, 0x000fffff, 0xffff0000,
    +  0x000fffff, 0xff000001, 0x000fffff, 0xff000000, 0x000fffff, 0xa9cb1d25,
    +  0x000fffff, 0x48892a0b, 0x000fffff, 0x00000001, 0x000fffff, 0x00000000,
    +  0x000f0000, 0x00000001, 0x000f0000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0xa9db1d24, 0x48892a0b, 0x80100000, 0x00000001,
    +  0xd634e2db, 0xb776d5f5, 0x9ebe0ce6, 0x6141f31a, 0x5644e2db, 0xb776d5f6,
    +  0x5644e2db, 0xb776d5f5, 0x5635e2db, 0xb776d5f6, 0x5635e2db, 0xb776d5f5,
    +  0x5634e2dc, 0xb776d5f6, 0x5634e2dc, 0xb776d5f5, 0x5634e2db, 0xb876d5f6,
    +  0x5634e2db, 0xb876d5f5, 0x5634e2db, 0xb777d5f6, 0x5634e2db, 0xb777d5f5,
    +  0x5634e2db, 0xb77755f6, 0x5634e2db, 0xb77755f5, 0x5634e2db, 0xb776d5f7,
    +  0x5634e2db, 0xb776d5f6, 0x5634e2db, 0xb776d5f5, 0x5634e2db, 0xb776d5f4,
    +  0x5634e2db, 0xb776d5f3, 0x5634e2db, 0xb77655f6, 0x5634e2db, 0xb77655f5,
    +  0x5634e2db, 0xb775d5f6, 0x5634e2db, 0xb775d5f5, 0x5634e2db, 0xb676d5f6,
    +  0x5634e2db, 0xb676d5f5, 0x5634e2db, 0x6141f31a, 0x5634e2db, 0x00000000,
    +  0x5634e2da, 0xb776d5f6, 0x5634e2da, 0xb776d5f5, 0x5633e2db, 0xb776d5f6,
    +  0x5633e2db, 0xb776d5f5, 0x5624e2db, 0xb776d5f6, 0x5624e2db, 0xb776d5f5,
    +  0x00000000, 0x00000000, 0xd634e2db, 0xb776d5f6, 0xffffffff, 0xffffffff,
    +  0xc8892a0a, 0xa9cb1d24, 0x80100000, 0x00000000, 0x800fffff, 0xffffffff,
    +  0x80010000, 0x00000000, 0x8000ffff, 0xffffffff, 0x80000001, 0x00000000,
    +  0x80000000, 0xffffffff, 0x80000000, 0x01000000, 0x80000000, 0x00ffffff,
    +  0x80000000, 0x00010000, 0x80000000, 0x0000ffff, 0x80000000, 0x00008000,
    +  0x80000000, 0x00007fff, 0x80000000, 0x00000001, 0x80000000, 0x00000000,
    +  0x7fffffff, 0xffffffff, 0x7fffffff, 0xfffffffe, 0x7fffffff, 0xfffffffd,
    +  0x7fffffff, 0xffff8000, 0x7fffffff, 0xffff7fff, 0x7fffffff, 0xffff0000,
    +  0x7fffffff, 0xfffeffff, 0x7fffffff, 0xff000000, 0x7fffffff, 0xfeffffff,
    +  0x7fffffff, 0xa9cb1d24, 0x7fffffff, 0x48892a0a, 0x7fffffff, 0x00000000,
    +  0x7ffffffe, 0xffffffff, 0x7fff0000, 0x00000000, 0x7ffeffff, 0xffffffff,
    +  0x7ff00000, 0x00000000, 0x7fefffff, 0xffffffff, 0x29cb1d24, 0x48892a0a,
    +  0x00000000, 0x00000000
    +];
    +toInt32s(TEST_SUB_BITS);
    +
    +var TEST_MUL_BITS = [
    +  0x80000000, 0x00000000, 0x80000000, 0x00000000, 0x1ad92a0a, 0xa9cb1d25,
    +  0x00000000, 0x00000000, 0xd2500000, 0x00000000, 0x00100000, 0x00000000,
    +  0x80000000, 0x00000000, 0x65ae2a0a, 0xa9cb1d25, 0x00110000, 0x00000001,
    +  0x00100000, 0x00000000, 0x00000000, 0x00000000, 0x1d250000, 0x00000000,
    +  0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
    +  0x80000000, 0x00000000, 0xf254472f, 0xa9cb1d25, 0x00100001, 0x00000001,
    +  0x00100000, 0x00000000, 0x00010001, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000000, 0x00000000, 0xa9cb1d25, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x5332f527, 0xcecb1d25,
    +  0x00100000, 0x01000001, 0x00100000, 0x00000000, 0x00010000, 0x01000001,
    +  0x00010000, 0x00000000, 0x01000001, 0x01000001, 0x01000001, 0x00000000,
    +  0x00000000, 0x00000000, 0x0aa9cb1d, 0x25000000, 0x00000000, 0x01000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x01000000, 0x00000000, 0x00000000,
    +  0x01000000, 0x01000000, 0x01000000, 0x00000000, 0x00010000, 0x01000000,
    +  0x80000000, 0x00000000, 0x7293d3d5, 0xc6f01d25, 0x00100000, 0x00010001,
    +  0x00100000, 0x00000000, 0x00010000, 0x00010001, 0x00010000, 0x00000000,
    +  0x00010001, 0x00010001, 0x00010001, 0x00000000, 0x00000100, 0x01010001,
    +  0x00000100, 0x01000000, 0x00000000, 0x00000000, 0x2a0aa9cb, 0x1d250000,
    +  0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00010000, 0x00000000,
    +  0x00000100, 0x00010000, 0x00000100, 0x00000000, 0x00000001, 0x00010000,
    +  0x80000000, 0x00000000, 0xdd8e7ef0, 0x385d9d25, 0x00100000, 0x00008001,
    +  0x00100000, 0x00000000, 0x80010000, 0x00008001, 0x80010000, 0x00000000,
    +  0x00008001, 0x00008001, 0x00008001, 0x00000000, 0x00000080, 0x01008001,
    +  0x00000080, 0x01000000, 0x00000000, 0x80018001, 0x00000000, 0x80010000,
    +  0x00000000, 0x00000000, 0x950554e5, 0x8e928000, 0x00000000, 0x00008000,
    +  0x00000000, 0x00000000, 0x80000000, 0x00008000, 0x80000000, 0x00000000,
    +  0x00008000, 0x00008000, 0x00008000, 0x00000000, 0x00000080, 0x00008000,
    +  0x00000080, 0x00000000, 0x00000000, 0x80008000, 0x00000000, 0x80000000,
    +  0x00000000, 0x40008000, 0x00000000, 0x00000000, 0x91125415, 0x53963a4a,
    +  0x00200000, 0x00000002, 0x00200000, 0x00000000, 0x00020000, 0x00000002,
    +  0x00020000, 0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000000,
    +  0x00000000, 0x02000002, 0x00000000, 0x02000000, 0x00000000, 0x00020002,
    +  0x00000000, 0x00020000, 0x00000000, 0x00010002, 0x00000000, 0x00010000,
    +  0x80000000, 0x00000000, 0x48892a0a, 0xa9cb1d25, 0x00100000, 0x00000001,
    +  0x00100000, 0x00000000, 0x00010000, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x01000001,
    +  0x00000000, 0x01000000, 0x00000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x80000000, 0x00000000, 0xb776d5f5, 0x5634e2db,
    +  0xffefffff, 0xffffffff, 0xfff00000, 0x00000000, 0xfffeffff, 0xffffffff,
    +  0xffff0000, 0x00000000, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x6eedabea, 0xac69c5b6, 0xffdfffff, 0xfffffffe,
    +  0xffe00000, 0x00000000, 0xfffdffff, 0xfffffffe, 0xfffe0000, 0x00000000,
    +  0xfffffffd, 0xfffffffe, 0xfffffffe, 0x00000000, 0xffffffff, 0xfdfffffe,
    +  0xffffffff, 0xfe000000, 0xffffffff, 0xfffdfffe, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffefffe, 0xffffffff, 0xffff0000, 0xffffffff, 0xfffffffc,
    +  0xffffffff, 0xfffffffe, 0x00000000, 0x00000000, 0x00000000, 0x00000002,
    +  0x80000000, 0x00000000, 0xb383d525, 0x1b389d25, 0x000fffff, 0xffff8001,
    +  0x00100000, 0x00000000, 0x8000ffff, 0xffff8001, 0x80010000, 0x00000000,
    +  0xffff8000, 0xffff8001, 0xffff8001, 0x00000000, 0xffffff80, 0x00ff8001,
    +  0xffffff80, 0x01000000, 0xffffffff, 0x80008001, 0xffffffff, 0x80010000,
    +  0xffffffff, 0xc0000001, 0xffffffff, 0xc0008000, 0xffffffff, 0xffff0002,
    +  0xffffffff, 0xffff8001, 0x00000000, 0x00000000, 0x00000000, 0x00007fff,
    +  0x00000000, 0x0000fffe, 0x00000000, 0x00000000, 0x6afaab1a, 0x716d8000,
    +  0xffffffff, 0xffff8000, 0x00000000, 0x00000000, 0x7fffffff, 0xffff8000,
    +  0x80000000, 0x00000000, 0xffff7fff, 0xffff8000, 0xffff8000, 0x00000000,
    +  0xffffff7f, 0xffff8000, 0xffffff80, 0x00000000, 0xffffffff, 0x7fff8000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xbfff8000, 0xffffffff, 0xc0000000,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff8000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00010000, 0x00000000, 0x3fff8000,
    +  0x80000000, 0x00000000, 0x1e7e803f, 0x8ca61d25, 0x000fffff, 0xffff0001,
    +  0x00100000, 0x00000000, 0x0000ffff, 0xffff0001, 0x00010000, 0x00000000,
    +  0xffff0000, 0xffff0001, 0xffff0001, 0x00000000, 0xffffff00, 0x00ff0001,
    +  0xffffff00, 0x01000000, 0xffffffff, 0x00000001, 0xffffffff, 0x00010000,
    +  0xffffffff, 0x7fff8001, 0xffffffff, 0x80008000, 0xffffffff, 0xfffe0002,
    +  0xffffffff, 0xffff0001, 0x00000000, 0x00000000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x0001fffe, 0x00000000, 0x7ffe8001, 0x00000000, 0x7fff8000,
    +  0x00000000, 0x00000000, 0xd5f55634, 0xe2db0000, 0xffffffff, 0xffff0000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffff0000, 0x00000000, 0x00000000,
    +  0xfffeffff, 0xffff0000, 0xffff0000, 0x00000000, 0xfffffeff, 0xffff0000,
    +  0xffffff00, 0x00000000, 0xfffffffe, 0xffff0000, 0xffffffff, 0x00000000,
    +  0xffffffff, 0x7fff0000, 0xffffffff, 0x80000000, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00020000, 0x00000000, 0x7fff0000, 0x00000000, 0x80000000,
    +  0x00000000, 0xffff0000, 0x80000000, 0x00000000, 0x3ddf5eed, 0x84cb1d25,
    +  0x000fffff, 0xff000001, 0x00100000, 0x00000000, 0x0000ffff, 0xff000001,
    +  0x00010000, 0x00000000, 0xff000000, 0xff000001, 0xff000001, 0x00000000,
    +  0xffff0000, 0x00000001, 0xffff0000, 0x01000000, 0xfffffeff, 0xff010001,
    +  0xffffff00, 0x00010000, 0xffffff7f, 0xff008001, 0xffffff80, 0x00008000,
    +  0xffffffff, 0xfe000002, 0xffffffff, 0xff000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01fffffe, 0x0000007f, 0xfeff8001,
    +  0x0000007f, 0xffff8000, 0x000000ff, 0xfeff0001, 0x000000ff, 0xffff0000,
    +  0x00000000, 0x00000000, 0xf55634e2, 0xdb000000, 0xffffffff, 0xff000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
    +  0xfeffffff, 0xff000000, 0xff000000, 0x00000000, 0xfffeffff, 0xff000000,
    +  0xffff0000, 0x00000000, 0xfffffeff, 0xff000000, 0xffffff00, 0x00000000,
    +  0xffffff7f, 0xff000000, 0xffffff80, 0x00000000, 0xffffffff, 0xfe000000,
    +  0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000,
    +  0x00000000, 0x02000000, 0x0000007f, 0xff000000, 0x00000080, 0x00000000,
    +  0x000000ff, 0xff000000, 0x00000100, 0x00000000, 0x0000ffff, 0xff000000,
    +  0x80000000, 0x00000000, 0xbc56e5ef, 0x15ff6759, 0xd24fffff, 0xa9cb1d25,
    +  0xd2500000, 0x00000000, 0x1d24ffff, 0xa9cb1d25, 0x1d250000, 0x00000000,
    +  0xa9cb1d24, 0xa9cb1d25, 0xa9cb1d25, 0x00000000, 0xffa9cb1c, 0xcecb1d25,
    +  0xffa9cb1d, 0x25000000, 0xffffa9ca, 0xc6f01d25, 0xffffa9cb, 0x1d250000,
    +  0xffffd4e5, 0x385d9d25, 0xffffd4e5, 0x8e928000, 0xffffffff, 0x53963a4a,
    +  0xffffffff, 0xa9cb1d25, 0x00000000, 0x00000000, 0x00000000, 0x5634e2db,
    +  0x00000000, 0xac69c5b6, 0x00002b1a, 0x1b389d25, 0x00002b1a, 0x716d8000,
    +  0x00005634, 0x8ca61d25, 0x00005634, 0xe2db0000, 0x005634e2, 0x84cb1d25,
    +  0x005634e2, 0xdb000000, 0x80000000, 0x00000000, 0x74756f10, 0x9f4f5297,
    +  0xa0afffff, 0x48892a0b, 0xa0b00000, 0x00000000, 0x2a0affff, 0x48892a0b,
    +  0x2a0b0000, 0x00000000, 0x48892a0a, 0x48892a0b, 0x48892a0b, 0x00000000,
    +  0xff488929, 0x53892a0b, 0xff48892a, 0x0b000000, 0xffff4888, 0x72942a0b,
    +  0xffff4889, 0x2a0b0000, 0xffffa443, 0xdd8eaa0b, 0xffffa444, 0x95058000,
    +  0xfffffffe, 0x91125416, 0xffffffff, 0x48892a0b, 0x00000000, 0x00000000,
    +  0x00000000, 0xb776d5f5, 0x00000001, 0x6eedabea, 0x00005bba, 0xb383aa0b,
    +  0x00005bbb, 0x6afa8000, 0x0000b776, 0x1e7e2a0b, 0x0000b776, 0xd5f50000,
    +  0x00b776d5, 0x3d892a0b, 0x00b776d5, 0xf5000000, 0x3dc7d297, 0x9f4f5297,
    +  0x80000000, 0x00000000, 0x9ebe0ce5, 0xa9cb1d25, 0x000fffff, 0x00000001,
    +  0x00100000, 0x00000000, 0x0000ffff, 0x00000001, 0x00010000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000001, 0x00000000, 0xfeffffff, 0x01000001,
    +  0xff000000, 0x01000000, 0xfffeffff, 0x00010001, 0xffff0000, 0x00010000,
    +  0xffff7fff, 0x00008001, 0xffff8000, 0x00008000, 0xfffffffe, 0x00000002,
    +  0xffffffff, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
    +  0x00000001, 0xfffffffe, 0x00007ffe, 0xffff8001, 0x00007fff, 0xffff8000,
    +  0x0000fffe, 0xffff0001, 0x0000ffff, 0xffff0000, 0x00fffffe, 0xff000001,
    +  0x00ffffff, 0xff000000, 0x5634e2da, 0xa9cb1d25, 0xb776d5f4, 0x48892a0b,
    +  0x00000000, 0x00000000, 0x5634e2db, 0x00000000, 0xffffffff, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xfeffffff, 0x00000000,
    +  0xff000000, 0x00000000, 0xfffeffff, 0x00000000, 0xffff0000, 0x00000000,
    +  0xffff7fff, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffe, 0x00000000,
    +  0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000002, 0x00000000, 0x00007fff, 0x00000000, 0x00008000, 0x00000000,
    +  0x0000ffff, 0x00000000, 0x00010000, 0x00000000, 0x00ffffff, 0x00000000,
    +  0x01000000, 0x00000000, 0x5634e2db, 0x00000000, 0xb776d5f5, 0x00000000,
    +  0xffffffff, 0x00000000, 0x80000000, 0x00000000, 0x2b642a0a, 0xa9cb1d25,
    +  0x000f0000, 0x00000001, 0x00100000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00010000, 0x00000000, 0xffff0001, 0x00000001, 0x00000001, 0x00000000,
    +  0xffff0000, 0x01000001, 0x00000000, 0x01000000, 0xffff0000, 0x00010001,
    +  0x00000000, 0x00010000, 0x7fff0000, 0x00008001, 0x80000000, 0x00008000,
    +  0xfffe0000, 0x00000002, 0xffff0000, 0x00000001, 0x00000000, 0x00000000,
    +  0x0000ffff, 0xffffffff, 0x0001ffff, 0xfffffffe, 0x7ffeffff, 0xffff8001,
    +  0x7fffffff, 0xffff8000, 0xfffeffff, 0xffff0001, 0xffffffff, 0xffff0000,
    +  0xfffeffff, 0xff000001, 0xffffffff, 0xff000000, 0xe2daffff, 0xa9cb1d25,
    +  0xd5f4ffff, 0x48892a0b, 0xfffeffff, 0x00000001, 0xffffffff, 0x00000000,
    +  0x00000000, 0x00000000, 0xe2db0000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
    +  0x7fff0000, 0x00000000, 0x80000000, 0x00000000, 0xfffe0000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
    +  0x00020000, 0x00000000, 0x7fff0000, 0x00000000, 0x80000000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x00000000, 0x00000000, 0xe2db0000, 0x00000000, 0xd5f50000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000,
    +  0x80000000, 0x00000000, 0x76392a0a, 0xa9cb1d25, 0x00000000, 0x00000001,
    +  0x00100000, 0x00000000, 0xfff10000, 0x00000001, 0x00010000, 0x00000000,
    +  0xfff00001, 0x00000001, 0x00000001, 0x00000000, 0xfff00000, 0x01000001,
    +  0x00000000, 0x01000000, 0xfff00000, 0x00010001, 0x00000000, 0x00010000,
    +  0xfff00000, 0x00008001, 0x00000000, 0x00008000, 0xffe00000, 0x00000002,
    +  0xfff00000, 0x00000001, 0x00000000, 0x00000000, 0x000fffff, 0xffffffff,
    +  0x001fffff, 0xfffffffe, 0xffefffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0xffefffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffefffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0x2dafffff, 0xa9cb1d25, 0x5f4fffff, 0x48892a0b,
    +  0xffefffff, 0x00000001, 0xffffffff, 0x00000000, 0xffef0000, 0x00000001,
    +  0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0x2db00000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffe00000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00100000, 0x00000000, 0x00200000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00000000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0x2db00000, 0x00000000,
    +  0x5f500000, 0x00000000, 0xfff00000, 0x00000000, 0x00000000, 0x00000000,
    +  0xfff00000, 0x00000000, 0x00000000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x80000000, 0x00000000, 0x8a74d669, 0x9f4f5297, 0x4a7b1d24, 0x48892a0b,
    +  0xa0b00000, 0x00000000, 0xd3d61d24, 0x48892a0b, 0x2a0b0000, 0x00000000,
    +  0xf254472f, 0x48892a0b, 0x48892a0b, 0x00000000, 0xce13a64e, 0x53892a0b,
    +  0x2448892a, 0x0b000000, 0xc6ef65ad, 0x72942a0b, 0x1d244889, 0x2a0b0000,
    +  0x385d4168, 0xdd8eaa0b, 0x8e922444, 0x95058000, 0x53963a48, 0x91125416,
    +  0xa9cb1d24, 0x48892a0b, 0x00000000, 0x00000000, 0x5634e2db, 0xb776d5f5,
    +  0xac69c5b7, 0x6eedabea, 0x1b38f8df, 0xb383aa0b, 0x716ddbbb, 0x6afa8000,
    +  0x8ca6d49b, 0x1e7e2a0b, 0xe2dbb776, 0xd5f50000, 0x858293fa, 0x3d892a0b,
    +  0xdbb776d5, 0xf5000000, 0x53c739f0, 0x9f4f5297, 0x22ca6fa5, 0x36ad9c79,
    +  0x6141f319, 0x48892a0b, 0xb776d5f5, 0x00000000, 0x7fc01d24, 0x48892a0b,
    +  0xd5f50000, 0x00000000, 0x091b1d24, 0x48892a0b, 0x5f500000, 0x00000000,
    +  0x80000000, 0x00000000, 0xc8892a0a, 0xa9cb1d25, 0x80100000, 0x00000001,
    +  0x00100000, 0x00000000, 0x80010000, 0x00000001, 0x00010000, 0x00000000,
    +  0x80000001, 0x00000001, 0x00000001, 0x00000000, 0x80000000, 0x01000001,
    +  0x00000000, 0x01000000, 0x80000000, 0x00010001, 0x00000000, 0x00010000,
    +  0x80000000, 0x00008001, 0x00000000, 0x00008000, 0x00000000, 0x00000002,
    +  0x80000000, 0x00000001, 0x00000000, 0x00000000, 0x7fffffff, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0x7fffffff, 0xffff8001, 0xffffffff, 0xffff8000,
    +  0x7fffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0x7fffffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0x7fffffff, 0xa9cb1d25, 0x7fffffff, 0x48892a0b,
    +  0x7fffffff, 0x00000001, 0xffffffff, 0x00000000, 0x7fff0000, 0x00000001,
    +  0xffff0000, 0x00000000, 0x7ff00000, 0x00000001, 0xfff00000, 0x00000000,
    +  0x29cb1d24, 0x48892a0b
    +];
    +toInt32s(TEST_MUL_BITS);
    +
    +var TEST_DIV_BITS = [
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x000007ff,
    +  0x00000000, 0x00000800, 0x00000000, 0x00007fff, 0x00000000, 0x00008000,
    +  0x00000000, 0x7fffffff, 0x00000000, 0x80000000, 0x0000007f, 0xffff8000,
    +  0x00000080, 0x00000000, 0x00007fff, 0x80007fff, 0x00008000, 0x00000000,
    +  0x0000fffe, 0x0003fff8, 0x00010000, 0x00000000, 0x40000000, 0x00000000,
    +  0x80000000, 0x00000000, 0x80000000, 0x00000000, 0xc0000000, 0x00000000,
    +  0xfffefffd, 0xfffbfff8, 0xffff0000, 0x00000000, 0xffff7fff, 0x7fff8000,
    +  0xffff8000, 0x00000000, 0xffffff7f, 0xffff8000, 0xffffff80, 0x00000000,
    +  0xfffffffe, 0x83e3cc1a, 0xffffffff, 0x4d64985a, 0xffffffff, 0x80000000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xffff8000, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xfffff800, 0xffffffff, 0xfffff800, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000488, 0x00000000, 0x00000488, 0x00000000, 0x00004889,
    +  0x00000000, 0x00004889, 0x00000000, 0x48892a0a, 0x00000000, 0x48892a0a,
    +  0x00000048, 0x8929c220, 0x00000048, 0x892a0aa9, 0x00004888, 0xe181c849,
    +  0x00004889, 0x2a0aa9cb, 0x00009111, 0x31f2efb0, 0x00009112, 0x54155396,
    +  0x24449505, 0x54e58e92, 0x48892a0a, 0xa9cb1d25, 0xb776d5f5, 0x5634e2db,
    +  0xdbbb6afa, 0xab1a716e, 0xffff6eec, 0x89c3bff2, 0xffff6eed, 0xabeaac6a,
    +  0xffffb776, 0x8d6be3a1, 0xffffb776, 0xd5f55635, 0xffffffb7, 0x76d5acce,
    +  0xffffffb7, 0x76d5f557, 0xffffffff, 0x2898cfc6, 0xffffffff, 0x9ac930b4,
    +  0xffffffff, 0xb776d5f6, 0xffffffff, 0xb776d5f6, 0xffffffff, 0xffffb777,
    +  0xffffffff, 0xffffb777, 0xffffffff, 0xfffffb78, 0xffffffff, 0xfffffb78,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x0000000f, 0x00000000, 0x00000010, 0x00000000, 0x000fffff,
    +  0x00000000, 0x00100000, 0x00000000, 0x0ffffff0, 0x00000000, 0x10000000,
    +  0x0000000f, 0xfff0000f, 0x00000010, 0x00000000, 0x0000001f, 0xffc0007f,
    +  0x00000020, 0x00000000, 0x00080000, 0x00000000, 0x00100000, 0x00000001,
    +  0xffefffff, 0xffffffff, 0xfff80000, 0x00000000, 0xffffffdf, 0xffbfff80,
    +  0xffffffe0, 0x00000000, 0xffffffef, 0xffeffff0, 0xfffffff0, 0x00000000,
    +  0xffffffff, 0xeffffff0, 0xffffffff, 0xf0000000, 0xffffffff, 0xffd07c7a,
    +  0xffffffff, 0xffe9ac94, 0xffffffff, 0xfff00000, 0xffffffff, 0xfff00000,
    +  0xffffffff, 0xfffffff0, 0xffffffff, 0xfffffff0, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x0000000f, 0x00000000, 0x00000010,
    +  0x00000000, 0x000fffff, 0x00000000, 0x00100000, 0x00000000, 0x0ffffff0,
    +  0x00000000, 0x10000000, 0x0000000f, 0xfff0000f, 0x00000010, 0x00000000,
    +  0x0000001f, 0xffc0007f, 0x00000020, 0x00000000, 0x00080000, 0x00000000,
    +  0x00100000, 0x00000000, 0xfff00000, 0x00000000, 0xfff80000, 0x00000000,
    +  0xffffffdf, 0xffbfff80, 0xffffffe0, 0x00000000, 0xffffffef, 0xffeffff0,
    +  0xfffffff0, 0x00000000, 0xffffffff, 0xeffffff0, 0xffffffff, 0xf0000000,
    +  0xffffffff, 0xffd07c7a, 0xffffffff, 0xffe9ac94, 0xffffffff, 0xfff00000,
    +  0xffffffff, 0xfff00000, 0xffffffff, 0xfffffff0, 0xffffffff, 0xfffffff0,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x01000000, 0x00000000, 0xffff0001,
    +  0x00000001, 0x00000000, 0x00000001, 0xfffc0007, 0x00000002, 0x00000000,
    +  0x00008000, 0x00000000, 0x00010000, 0x00000001, 0xfffeffff, 0xffffffff,
    +  0xffff8000, 0x00000000, 0xfffffffd, 0xfffbfff8, 0xfffffffe, 0x00000000,
    +  0xfffffffe, 0xfffeffff, 0xffffffff, 0x00000000, 0xffffffff, 0xfeffffff,
    +  0xffffffff, 0xff000000, 0xffffffff, 0xfffd07c8, 0xffffffff, 0xfffe9aca,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff0000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000, 0x00000000, 0x00ffffff, 0x00000000, 0x01000000,
    +  0x00000000, 0xffff0000, 0x00000001, 0x00000000, 0x00000001, 0xfffc0007,
    +  0x00000002, 0x00000000, 0x00008000, 0x00000000, 0x00010000, 0x00000000,
    +  0xffff0000, 0x00000000, 0xffff8000, 0x00000000, 0xfffffffd, 0xfffbfff8,
    +  0xfffffffe, 0x00000000, 0xfffffffe, 0xfffeffff, 0xffffffff, 0x00000000,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xfffd07c8,
    +  0xffffffff, 0xfffe9aca, 0xffffffff, 0xffff0000, 0xffffffff, 0xffff0000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000100, 0x00000000, 0x0000ffff, 0x00000000, 0x00010000,
    +  0x00000000, 0x0001fffc, 0x00000000, 0x00020000, 0x00000000, 0x80000000,
    +  0x00000001, 0x00000001, 0xfffffffe, 0xffffffff, 0xffffffff, 0x80000000,
    +  0xffffffff, 0xfffdfffc, 0xffffffff, 0xfffe0000, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffffff00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x000000ff, 0x00000000, 0x00000100, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00010000, 0x00000000, 0x0001fffc, 0x00000000, 0x00020000,
    +  0x00000000, 0x80000000, 0x00000001, 0x00000000, 0xffffffff, 0x00000000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0xfffdfffc, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0xfffeffff, 0xffffffff, 0xffff0000, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffff00, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x000000ff, 0x00000000, 0x00000100, 0x00000000, 0x000001ff,
    +  0x00000000, 0x00000200, 0x00000000, 0x00800000, 0x00000000, 0x01000001,
    +  0xffffffff, 0xfeffffff, 0xffffffff, 0xff800000, 0xffffffff, 0xfffffe00,
    +  0xffffffff, 0xfffffe00, 0xffffffff, 0xffffff00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x000000ff, 0x00000000, 0x00000100,
    +  0x00000000, 0x000001ff, 0x00000000, 0x00000200, 0x00000000, 0x00800000,
    +  0x00000000, 0x01000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff800000,
    +  0xffffffff, 0xfffffe00, 0xffffffff, 0xfffffe00, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xffffff00, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000002,
    +  0x00000000, 0x00008000, 0x00000000, 0x00010001, 0xffffffff, 0xfffeffff,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000002, 0x00000000, 0x00008000, 0x00000000, 0x00010000,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xffff8000, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00004000,
    +  0x00000000, 0x00008001, 0xffffffff, 0xffff7fff, 0xffffffff, 0xffffc000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00004000, 0x00000000, 0x00008000, 0xffffffff, 0xffff8000,
    +  0xffffffff, 0xffffc000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000002,
    +  0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000001, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffffffe, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffc001, 0xffffffff, 0xffff8001, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00003fff, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffc000, 0xffffffff, 0xffff8000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00004000, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff0001, 0x00000000, 0x0000ffff, 0x00000000, 0x00007fff,
    +  0x00000000, 0x00000002, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,
    +  0xffffffff, 0xffff8000, 0xffffffff, 0xffff0000, 0x00000000, 0x00010000,
    +  0x00000000, 0x00008000, 0x00000000, 0x00000002, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffffff01, 0xffffffff, 0xfffffe01,
    +  0xffffffff, 0xfffffe01, 0xffffffff, 0xff800001, 0xffffffff, 0xff000001,
    +  0x00000000, 0x00ffffff, 0x00000000, 0x007fffff, 0x00000000, 0x00000200,
    +  0x00000000, 0x000001ff, 0x00000000, 0x00000100, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffff, 0xffffffff, 0xffffff01, 0xffffffff, 0xffffff00,
    +  0xffffffff, 0xfffffe01, 0xffffffff, 0xfffffe00, 0xffffffff, 0xff800000,
    +  0xffffffff, 0xff000000, 0x00000000, 0x01000000, 0x00000000, 0x00800000,
    +  0x00000000, 0x00000200, 0x00000000, 0x00000200, 0x00000000, 0x00000100,
    +  0x00000000, 0x00000100, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0xffffffff, 0xffffffaa, 0xffffffff, 0xffffffaa, 0xffffffff, 0xffffa9cc,
    +  0xffffffff, 0xffffa9cc, 0xffffffff, 0xffff5398, 0xffffffff, 0xffff5397,
    +  0xffffffff, 0xd4e58e93, 0xffffffff, 0xa9cb1d25, 0x00000000, 0x5634e2db,
    +  0x00000000, 0x2b1a716d, 0x00000000, 0x0000ac6b, 0x00000000, 0x0000ac69,
    +  0x00000000, 0x00005635, 0x00000000, 0x00005634, 0x00000000, 0x00000056,
    +  0x00000000, 0x00000056, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffff49, 0xffffffff, 0xffffff49,
    +  0xffffffff, 0xffff488a, 0xffffffff, 0xffff488a, 0xffffffff, 0xfffe9116,
    +  0xffffffff, 0xfffe9113, 0xffffffff, 0xa4449506, 0xffffffff, 0x48892a0b,
    +  0x00000000, 0xb776d5f5, 0x00000000, 0x5bbb6afa, 0x00000000, 0x00016ef0,
    +  0x00000000, 0x00016eed, 0x00000000, 0x0000b777, 0x00000000, 0x0000b776,
    +  0x00000000, 0x000000b7, 0x00000000, 0x000000b7, 0x00000000, 0x00000002,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffff01,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffff0001, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xfffe0004, 0xffffffff, 0xfffe0001, 0xffffffff, 0x80000001,
    +  0xffffffff, 0x00000001, 0x00000000, 0xffffffff, 0x00000000, 0x7fffffff,
    +  0x00000000, 0x00020004, 0x00000000, 0x0001ffff, 0x00000000, 0x00010001,
    +  0x00000000, 0x0000ffff, 0x00000000, 0x00000100, 0x00000000, 0x000000ff,
    +  0x00000000, 0x00000002, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffffff01, 0xffffffff, 0xffffff00, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0000, 0xffffffff, 0xfffe0004, 0xffffffff, 0xfffe0000,
    +  0xffffffff, 0x80000000, 0xffffffff, 0x00000000, 0x00000001, 0x00000000,
    +  0x00000000, 0x80000000, 0x00000000, 0x00020004, 0x00000000, 0x00020000,
    +  0x00000000, 0x00010001, 0x00000000, 0x00010000, 0x00000000, 0x00000100,
    +  0x00000000, 0x00000100, 0x00000000, 0x00000002, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffff0001,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xff000001, 0xffffffff, 0xff000001,
    +  0xffffffff, 0x00010000, 0xffffffff, 0x00000001, 0xfffffffe, 0x0003fff9,
    +  0xfffffffe, 0x00000001, 0xffff8000, 0x00000001, 0xffff0000, 0x00000001,
    +  0x0000ffff, 0xffffffff, 0x00007fff, 0xffffffff, 0x00000002, 0x00040008,
    +  0x00000001, 0xffffffff, 0x00000001, 0x00010001, 0x00000000, 0xffffffff,
    +  0x00000000, 0x01000001, 0x00000000, 0x00ffffff, 0x00000000, 0x0002f838,
    +  0x00000000, 0x00016536, 0x00000000, 0x00010000, 0x00000000, 0x0000ffff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xffff0001, 0xffffffff, 0xffff0000, 0xffffffff, 0xff000001,
    +  0xffffffff, 0xff000000, 0xffffffff, 0x00010000, 0xffffffff, 0x00000000,
    +  0xfffffffe, 0x0003fff9, 0xfffffffe, 0x00000000, 0xffff8000, 0x00000000,
    +  0xffff0000, 0x00000000, 0x00010000, 0x00000000, 0x00008000, 0x00000000,
    +  0x00000002, 0x00040008, 0x00000002, 0x00000000, 0x00000001, 0x00010001,
    +  0x00000001, 0x00000000, 0x00000000, 0x01000001, 0x00000000, 0x01000000,
    +  0x00000000, 0x0002f838, 0x00000000, 0x00016536, 0x00000000, 0x00010000,
    +  0x00000000, 0x00010000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xfffffff1,
    +  0xffffffff, 0xfffffff1, 0xffffffff, 0xfff00001, 0xffffffff, 0xfff00001,
    +  0xffffffff, 0xf0000010, 0xffffffff, 0xf0000001, 0xfffffff0, 0x000ffff1,
    +  0xfffffff0, 0x00000001, 0xffffffe0, 0x003fff81, 0xffffffe0, 0x00000001,
    +  0xfff80000, 0x00000001, 0xfff00000, 0x00000001, 0x000fffff, 0xffffffff,
    +  0x0007ffff, 0xffffffff, 0x00000020, 0x00400080, 0x0000001f, 0xffffffff,
    +  0x00000010, 0x00100010, 0x0000000f, 0xffffffff, 0x00000000, 0x10000010,
    +  0x00000000, 0x0fffffff, 0x00000000, 0x002f8386, 0x00000000, 0x0016536c,
    +  0x00000000, 0x00100000, 0x00000000, 0x000fffff, 0x00000000, 0x00000010,
    +  0x00000000, 0x0000000f, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffffff1, 0xffffffff, 0xfffffff0, 0xffffffff, 0xfff00001,
    +  0xffffffff, 0xfff00000, 0xffffffff, 0xf0000010, 0xffffffff, 0xf0000000,
    +  0xfffffff0, 0x000ffff1, 0xfffffff0, 0x00000000, 0xffffffe0, 0x003fff81,
    +  0xffffffe0, 0x00000000, 0xfff80000, 0x00000000, 0xfff00000, 0x00000000,
    +  0x00100000, 0x00000000, 0x00080000, 0x00000000, 0x00000020, 0x00400080,
    +  0x00000020, 0x00000000, 0x00000010, 0x00100010, 0x00000010, 0x00000000,
    +  0x00000000, 0x10000010, 0x00000000, 0x10000000, 0x00000000, 0x002f8386,
    +  0x00000000, 0x0016536c, 0x00000000, 0x00100000, 0x00000000, 0x00100000,
    +  0x00000000, 0x00000010, 0x00000000, 0x00000010, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
    +  0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffa9d,
    +  0xffffffff, 0xfffffa9d, 0xffffffff, 0xffffa9cc, 0xffffffff, 0xffffa9cc,
    +  0xffffffff, 0xa9cb1d25, 0xffffffff, 0xa9cb1d25, 0xffffffa9, 0xcb1d7a7e,
    +  0xffffffa9, 0xcb1d2449, 0xffffa9cb, 0x7358d531, 0xffffa9cb, 0x1d24488a,
    +  0xffff5397, 0x93196ae0, 0xffff5396, 0x3a489113, 0xd4e58e92, 0x24449506,
    +  0xa9cb1d24, 0x48892a0b, 0x5634e2db, 0xb776d5f5, 0x2b1a716d, 0xdbbb6afa,
    +  0x0000ac6b, 0x1e8dac09, 0x0000ac69, 0xc5b76eed, 0x00005635, 0x3910f087,
    +  0x00005634, 0xe2dbb776, 0x00000056, 0x34e331ec, 0x00000056, 0x34e2dbb7,
    +  0x00000001, 0x00000002, 0x00000000, 0x784a3552, 0x00000000, 0x5634e2dc,
    +  0x00000000, 0x5634e2db, 0x00000000, 0x00005634, 0x00000000, 0x00005634,
    +  0x00000000, 0x00000563, 0x00000000, 0x00000563, 0x00000000, 0x00000001,
    +  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
    +  0xffffffff, 0xfffff801, 0xffffffff, 0xfffff801, 0xffffffff, 0xffff8001,
    +  0xffffffff, 0xffff8001, 0xffffffff, 0x80000001, 0xffffffff, 0x80000001,
    +  0xffffff80, 0x00008000, 0xffffff80, 0x00000001, 0xffff8000, 0x7fff8001,
    +  0xffff8000, 0x00000001, 0xffff0001, 0xfffc0008, 0xffff0000, 0x00000001,
    +  0xc0000000, 0x00000001, 0x80000000, 0x00000001, 0x7fffffff, 0xffffffff,
    +  0x3fffffff, 0xffffffff, 0x00010002, 0x00040008, 0x0000ffff, 0xffffffff,
    +  0x00008000, 0x80008000, 0x00007fff, 0xffffffff, 0x00000080, 0x00008000,
    +  0x0000007f, 0xffffffff, 0x00000001, 0x7c1c33e6, 0x00000000, 0xb29b67a6,
    +  0x00000000, 0x80000000, 0x00000000, 0x7fffffff, 0x00000000, 0x00008000,
    +  0x00000000, 0x00007fff, 0x00000000, 0x00000800, 0x00000000, 0x000007ff,
    +  0x00000000, 0x00000001, 0x00000000, 0x00000001
    +];
    +toInt32s(TEST_DIV_BITS);
    +
    +var TEST_STRINGS = [
    +  '-9223372036854775808',
    +  '-5226755067826871589',
    +  '-4503599627370497',
    +  '-4503599627370496',
    +  '-281474976710657',
    +  '-281474976710656',
    +  '-4294967297',
    +  '-4294967296',
    +  '-16777217',
    +  '-16777216',
    +  '-65537',
    +  '-65536',
    +  '-32769',
    +  '-32768',
    +  '-2',
    +  '-1',
    +  '0',
    +  '1',
    +  '2',
    +  '32767',
    +  '32768',
    +  '65535',
    +  '65536',
    +  '16777215',
    +  '16777216',
    +  '1446306523',
    +  '3078018549',
    +  '4294967295',
    +  '4294967296',
    +  '281474976710655',
    +  '281474976710656',
    +  '4503599627370495',
    +  '4503599627370496',
    +  '6211839219354490357',
    +  '9223372036854775807'
    +];
    +
    +function testToFromBits() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    assertEquals(TEST_BITS[i], val.getHighBits());
    +    assertEquals(TEST_BITS[i + 1], val.getLowBits());
    +  }
    +}
    +
    +function testToFromInt() {
    +  for (var i = 0; i < TEST_BITS.length; i += 1) {
    +    var val = goog.math.Long.fromInt(TEST_BITS[i]);
    +    assertEquals(TEST_BITS[i], val.toInt());
    +  }
    +}
    +
    +function testToFromNumber() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var num = TEST_BITS[i] * Math.pow(2, 32) + TEST_BITS[i + 1] >= 0 ?
    +        TEST_BITS[i + 1] : Math.pow(2, 32) + TEST_BITS[i + 1];
    +    var val = goog.math.Long.fromNumber(num);
    +    assertEquals(num, val.toNumber());
    +  }
    +}
    +
    +function testIsZero() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    assertEquals(TEST_BITS[i] == 0 && TEST_BITS[i + 1] == 0, val.isZero());
    +  }
    +}
    +
    +function testIsNegative() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    assertEquals((TEST_BITS[i] >> 31) != 0, val.isNegative());
    +  }
    +}
    +
    +function testIsOdd() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var val = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    assertEquals((TEST_BITS[i + 1] & 1) != 0, val.isOdd());
    +  }
    +}
    +
    +function createTestComparisons(i) {
    +  return function() {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      assertEquals(i == j, vi.equals(vj));
    +      assertEquals(i != j, vi.notEquals(vj));
    +      assertEquals(i < j, vi.lessThan(vj));
    +      assertEquals(i <= j, vi.lessThanOrEqual(vj));
    +      assertEquals(i > j, vi.greaterThan(vj));
    +      assertEquals(i >= j, vi.greaterThanOrEqual(vj));
    +    }
    +  };
    +}
    +
    +// Here and below, we translate one conceptual test (e.g., "testComparisons")
    +// into a number of test functions that will be run separately by jsunit. This
    +// is necessary because, in some testing configurations, the full combined test
    +// can take so long that it times out. These smaller tests run much faster.
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testComparisons' + i] = createTestComparisons(i);
    +}
    +
    +function createTestBitOperations(i) {
    +  return function() {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    assertEquals(~TEST_BITS[i], vi.not().getHighBits());
    +    assertEquals(~TEST_BITS[i + 1], vi.not().getLowBits());
    +
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      assertEquals(TEST_BITS[i] & TEST_BITS[j], vi.and(vj).getHighBits());
    +      assertEquals(
    +          TEST_BITS[i + 1] & TEST_BITS[j + 1], vi.and(vj).getLowBits());
    +      assertEquals(TEST_BITS[i] | TEST_BITS[j], vi.or(vj).getHighBits());
    +      assertEquals(TEST_BITS[i + 1] | TEST_BITS[j + 1], vi.or(vj).getLowBits());
    +      assertEquals(TEST_BITS[i] ^ TEST_BITS[j], vi.xor(vj).getHighBits());
    +      assertEquals(
    +          TEST_BITS[i + 1] ^ TEST_BITS[j + 1], vi.xor(vj).getLowBits());
    +    }
    +
    +    assertEquals(TEST_BITS[i], vi.shiftLeft(0).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftLeft(0).getLowBits());
    +    assertEquals(TEST_BITS[i], vi.shiftRight(0).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftRight(0).getLowBits());
    +    assertEquals(TEST_BITS[i], vi.shiftRightUnsigned(0).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftRightUnsigned(0).getLowBits());
    +
    +    for (var len = 1; len < 64; ++len) {
    +      if (len < 32) {
    +        assertEquals((TEST_BITS[i] << len) | (TEST_BITS[i + 1] >>> (32 - len)),
    +                     vi.shiftLeft(len).getHighBits());
    +        assertEquals(TEST_BITS[i + 1] << len, vi.shiftLeft(len).getLowBits());
    +
    +        assertEquals(TEST_BITS[i] >> len, vi.shiftRight(len).getHighBits());
    +        assertEquals((TEST_BITS[i + 1] >>> len) | (TEST_BITS[i] << (32 - len)),
    +                     vi.shiftRight(len).getLowBits());
    +
    +        assertEquals(TEST_BITS[i] >>> len,
    +                     vi.shiftRightUnsigned(len).getHighBits());
    +        assertEquals((TEST_BITS[i + 1] >>> len) | (TEST_BITS[i] << (32 - len)),
    +                     vi.shiftRightUnsigned(len).getLowBits());
    +      } else {
    +        assertEquals(TEST_BITS[i + 1] << (len - 32),
    +                     vi.shiftLeft(len).getHighBits());
    +        assertEquals(0, vi.shiftLeft(len).getLowBits());
    +
    +        assertEquals(TEST_BITS[i] >= 0 ? 0 : -1,
    +                     vi.shiftRight(len).getHighBits());
    +        assertEquals(TEST_BITS[i] >> (len - 32),
    +                     vi.shiftRight(len).getLowBits());
    +
    +        assertEquals(0, vi.shiftRightUnsigned(len).getHighBits());
    +        if (len == 32) {
    +          assertEquals(TEST_BITS[i], vi.shiftRightUnsigned(len).getLowBits());
    +        } else {
    +          assertEquals(TEST_BITS[i] >>> (len - 32),
    +                       vi.shiftRightUnsigned(len).getLowBits());
    +        }
    +      }
    +    }
    +
    +    assertEquals(TEST_BITS[i], vi.shiftLeft(64).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftLeft(64).getLowBits());
    +    assertEquals(TEST_BITS[i], vi.shiftRight(64).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftRight(64).getLowBits());
    +    assertEquals(TEST_BITS[i], vi.shiftRightUnsigned(64).getHighBits());
    +    assertEquals(TEST_BITS[i + 1], vi.shiftRightUnsigned(64).getLowBits());
    +  };
    +}
    +
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testBitOperations' + i] = createTestBitOperations(i);
    +}
    +
    +function testNegation() {
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    if (TEST_BITS[i + 1] == 0) {
    +      assertEquals((~TEST_BITS[i] + 1) | 0, vi.negate().getHighBits());
    +      assertEquals(0, vi.negate().getLowBits());
    +    } else {
    +      assertEquals(~TEST_BITS[i], vi.negate().getHighBits());
    +      assertEquals((~TEST_BITS[i + 1] + 1) | 0, vi.negate().getLowBits());
    +    }
    +  }
    +}
    +
    +function testAdd() {
    +  var count = 0;
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    for (var j = 0; j < i; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      var result = vi.add(vj);
    +      assertEquals(TEST_ADD_BITS[count++], result.getHighBits());
    +      assertEquals(TEST_ADD_BITS[count++], result.getLowBits());
    +    }
    +  }
    +}
    +
    +function testSubtract() {
    +  var count = 0;
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      var result = vi.subtract(vj);
    +      assertEquals(TEST_SUB_BITS[count++], result.getHighBits());
    +      assertEquals(TEST_SUB_BITS[count++], result.getLowBits());
    +    }
    +  }
    +}
    +
    +function testMultiply() {
    +  var count = 0;
    +  for (var i = 0; i < TEST_BITS.length; i += 2) {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    for (var j = 0; j < i; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      var result = vi.multiply(vj);
    +      assertEquals(TEST_MUL_BITS[count++], result.getHighBits());
    +      assertEquals(TEST_MUL_BITS[count++], result.getLowBits());
    +    }
    +  }
    +}
    +
    +function createTestDivMod(i, count) {
    +  return function() {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    for (var j = 0; j < TEST_BITS.length; j += 2) {
    +      var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +      if (!vj.isZero()) {
    +        var divResult = vi.div(vj);
    +        assertEquals(TEST_DIV_BITS[count++], divResult.getHighBits());
    +        assertEquals(TEST_DIV_BITS[count++], divResult.getLowBits());
    +
    +        var modResult = vi.modulo(vj);
    +        var combinedResult = divResult.multiply(vj).add(modResult);
    +        assertTrue(vi.equals(combinedResult));
    +      }
    +    }
    +  }
    +}
    +
    +var countPerDivModCall = 0;
    +for (var j = 0; j < TEST_BITS.length; j += 2) {
    +  var vj = goog.math.Long.fromBits(TEST_BITS[j + 1], TEST_BITS[j]);
    +  if (!vj.isZero()) {
    +    countPerDivModCall += 2;
    +  }
    +}
    +
    +var countDivMod = 0;
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testDivMod' + i] = createTestDivMod(i, countDivMod);
    +  countDivMod += countPerDivModCall;
    +}
    +
    +function createTestToFromString(i) {
    +  return function() {
    +    var vi = goog.math.Long.fromBits(TEST_BITS[i + 1], TEST_BITS[i]);
    +    var str = vi.toString(10);
    +    assertEquals(TEST_STRINGS[i / 2], str);
    +    assertEquals(TEST_BITS[i],
    +                 goog.math.Long.fromString(str, 10).getHighBits());
    +    assertEquals(TEST_BITS[i + 1],
    +                 goog.math.Long.fromString(str, 10).getLowBits());
    +
    +    for (var radix = 2; radix <= 36; ++radix) {
    +      var result = vi.toString(radix);
    +      assertEquals(TEST_BITS[i],
    +                   goog.math.Long.fromString(result, radix).getHighBits());
    +      assertEquals(TEST_BITS[i + 1],
    +                   goog.math.Long.fromString(result, radix).getLowBits());
    +    }
    +  }
    +}
    +
    +for (var i = 0; i < TEST_BITS.length; i += 2) {
    +  goog.global['testToFromString' + i] = createTestToFromString(i);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/math.js b/mapbox-gl/closure-library/closure/goog/math/math.js
    new file mode 100644
    index 0000000000..2947cf8f71
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/math.js
    @@ -0,0 +1,435 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Additional mathematical functions.
    + */
    +
    +goog.provide('goog.math');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * Returns a random integer greater than or equal to 0 and less than {@code a}.
    + * @param {number} a  The upper bound for the random integer (exclusive).
    + * @return {number} A random integer N such that 0 <= N < a.
    + */
    +goog.math.randomInt = function(a) {
    +  return Math.floor(Math.random() * a);
    +};
    +
    +
    +/**
    + * Returns a random number greater than or equal to {@code a} and less than
    + * {@code b}.
    + * @param {number} a  The lower bound for the random number (inclusive).
    + * @param {number} b  The upper bound for the random number (exclusive).
    + * @return {number} A random number N such that a <= N < b.
    + */
    +goog.math.uniformRandom = function(a, b) {
    +  return a + Math.random() * (b - a);
    +};
    +
    +
    +/**
    + * Takes a number and clamps it to within the provided bounds.
    + * @param {number} value The input number.
    + * @param {number} min The minimum value to return.
    + * @param {number} max The maximum value to return.
    + * @return {number} The input number if it is within bounds, or the nearest
    + *     number within the bounds.
    + */
    +goog.math.clamp = function(value, min, max) {
    +  return Math.min(Math.max(value, min), max);
    +};
    +
    +
    +/**
    + * The % operator in JavaScript returns the remainder of a / b, but differs from
    + * some other languages in that the result will have the same sign as the
    + * dividend. For example, -1 % 8 == -1, whereas in some other languages
    + * (such as Python) the result would be 7. This function emulates the more
    + * correct modulo behavior, which is useful for certain applications such as
    + * calculating an offset index in a circular list.
    + *
    + * @param {number} a The dividend.
    + * @param {number} b The divisor.
    + * @return {number} a % b where the result is between 0 and b (either 0 <= x < b
    + *     or b < x <= 0, depending on the sign of b).
    + */
    +goog.math.modulo = function(a, b) {
    +  var r = a % b;
    +  // If r and b differ in sign, add b to wrap the result to the correct sign.
    +  return (r * b < 0) ? r + b : r;
    +};
    +
    +
    +/**
    + * Performs linear interpolation between values a and b. Returns the value
    + * between a and b proportional to x (when x is between 0 and 1. When x is
    + * outside this range, the return value is a linear extrapolation).
    + * @param {number} a A number.
    + * @param {number} b A number.
    + * @param {number} x The proportion between a and b.
    + * @return {number} The interpolated value between a and b.
    + */
    +goog.math.lerp = function(a, b, x) {
    +  return a + x * (b - a);
    +};
    +
    +
    +/**
    + * Tests whether the two values are equal to each other, within a certain
    + * tolerance to adjust for floating point errors.
    + * @param {number} a A number.
    + * @param {number} b A number.
    + * @param {number=} opt_tolerance Optional tolerance range. Defaults
    + *     to 0.000001. If specified, should be greater than 0.
    + * @return {boolean} Whether {@code a} and {@code b} are nearly equal.
    + */
    +goog.math.nearlyEquals = function(a, b, opt_tolerance) {
    +  return Math.abs(a - b) <= (opt_tolerance || 0.000001);
    +};
    +
    +
    +// TODO(user): Rename to normalizeAngle, retaining old name as deprecated
    +// alias.
    +/**
    + * Normalizes an angle to be in range [0-360). Angles outside this range will
    + * be normalized to be the equivalent angle with that range.
    + * @param {number} angle Angle in degrees.
    + * @return {number} Standardized angle.
    + */
    +goog.math.standardAngle = function(angle) {
    +  return goog.math.modulo(angle, 360);
    +};
    +
    +
    +/**
    + * Normalizes an angle to be in range [0-2*PI). Angles outside this range will
    + * be normalized to be the equivalent angle with that range.
    + * @param {number} angle Angle in radians.
    + * @return {number} Standardized angle.
    + */
    +goog.math.standardAngleInRadians = function(angle) {
    +  return goog.math.modulo(angle, 2 * Math.PI);
    +};
    +
    +
    +/**
    + * Converts degrees to radians.
    + * @param {number} angleDegrees Angle in degrees.
    + * @return {number} Angle in radians.
    + */
    +goog.math.toRadians = function(angleDegrees) {
    +  return angleDegrees * Math.PI / 180;
    +};
    +
    +
    +/**
    + * Converts radians to degrees.
    + * @param {number} angleRadians Angle in radians.
    + * @return {number} Angle in degrees.
    + */
    +goog.math.toDegrees = function(angleRadians) {
    +  return angleRadians * 180 / Math.PI;
    +};
    +
    +
    +/**
    + * For a given angle and radius, finds the X portion of the offset.
    + * @param {number} degrees Angle in degrees (zero points in +X direction).
    + * @param {number} radius Radius.
    + * @return {number} The x-distance for the angle and radius.
    + */
    +goog.math.angleDx = function(degrees, radius) {
    +  return radius * Math.cos(goog.math.toRadians(degrees));
    +};
    +
    +
    +/**
    + * For a given angle and radius, finds the Y portion of the offset.
    + * @param {number} degrees Angle in degrees (zero points in +X direction).
    + * @param {number} radius Radius.
    + * @return {number} The y-distance for the angle and radius.
    + */
    +goog.math.angleDy = function(degrees, radius) {
    +  return radius * Math.sin(goog.math.toRadians(degrees));
    +};
    +
    +
    +/**
    + * Computes the angle between two points (x1,y1) and (x2,y2).
    + * Angle zero points in the +X direction, 90 degrees points in the +Y
    + * direction (down) and from there we grow clockwise towards 360 degrees.
    + * @param {number} x1 x of first point.
    + * @param {number} y1 y of first point.
    + * @param {number} x2 x of second point.
    + * @param {number} y2 y of second point.
    + * @return {number} Standardized angle in degrees of the vector from
    + *     x1,y1 to x2,y2.
    + */
    +goog.math.angle = function(x1, y1, x2, y2) {
    +  return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(y2 - y1,
    +                                                                x2 - x1)));
    +};
    +
    +
    +/**
    + * Computes the difference between startAngle and endAngle (angles in degrees).
    + * @param {number} startAngle  Start angle in degrees.
    + * @param {number} endAngle  End angle in degrees.
    + * @return {number} The number of degrees that when added to
    + *     startAngle will result in endAngle. Positive numbers mean that the
    + *     direction is clockwise. Negative numbers indicate a counter-clockwise
    + *     direction.
    + *     The shortest route (clockwise vs counter-clockwise) between the angles
    + *     is used.
    + *     When the difference is 180 degrees, the function returns 180 (not -180)
    + *     angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
    + *     angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
    + */
    +goog.math.angleDifference = function(startAngle, endAngle) {
    +  var d = goog.math.standardAngle(endAngle) -
    +          goog.math.standardAngle(startAngle);
    +  if (d > 180) {
    +    d = d - 360;
    +  } else if (d <= -180) {
    +    d = 360 + d;
    +  }
    +  return d;
    +};
    +
    +
    +/**
    + * Returns the sign of a number as per the "sign" or "signum" function.
    + * @param {number} x The number to take the sign of.
    + * @return {number} -1 when negative, 1 when positive, 0 when 0.
    + */
    +goog.math.sign = function(x) {
    +  return x == 0 ? 0 : (x < 0 ? -1 : 1);
    +};
    +
    +
    +/**
    + * JavaScript implementation of Longest Common Subsequence problem.
    + * http://en.wikipedia.org/wiki/Longest_common_subsequence
    + *
    + * Returns the longest possible array that is subarray of both of given arrays.
    + *
    + * @param {Array<Object>} array1 First array of objects.
    + * @param {Array<Object>} array2 Second array of objects.
    + * @param {Function=} opt_compareFn Function that acts as a custom comparator
    + *     for the array ojects. Function should return true if objects are equal,
    + *     otherwise false.
    + * @param {Function=} opt_collectorFn Function used to decide what to return
    + *     as a result subsequence. It accepts 2 arguments: index of common element
    + *     in the first array and index in the second. The default function returns
    + *     element from the first array.
    + * @return {!Array<Object>} A list of objects that are common to both arrays
    + *     such that there is no common subsequence with size greater than the
    + *     length of the list.
    + */
    +goog.math.longestCommonSubsequence = function(
    +    array1, array2, opt_compareFn, opt_collectorFn) {
    +
    +  var compare = opt_compareFn || function(a, b) {
    +    return a == b;
    +  };
    +
    +  var collect = opt_collectorFn || function(i1, i2) {
    +    return array1[i1];
    +  };
    +
    +  var length1 = array1.length;
    +  var length2 = array2.length;
    +
    +  var arr = [];
    +  for (var i = 0; i < length1 + 1; i++) {
    +    arr[i] = [];
    +    arr[i][0] = 0;
    +  }
    +
    +  for (var j = 0; j < length2 + 1; j++) {
    +    arr[0][j] = 0;
    +  }
    +
    +  for (i = 1; i <= length1; i++) {
    +    for (j = 1; j <= length2; j++) {
    +      if (compare(array1[i - 1], array2[j - 1])) {
    +        arr[i][j] = arr[i - 1][j - 1] + 1;
    +      } else {
    +        arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
    +      }
    +    }
    +  }
    +
    +  // Backtracking
    +  var result = [];
    +  var i = length1, j = length2;
    +  while (i > 0 && j > 0) {
    +    if (compare(array1[i - 1], array2[j - 1])) {
    +      result.unshift(collect(i - 1, j - 1));
    +      i--;
    +      j--;
    +    } else {
    +      if (arr[i - 1][j] > arr[i][j - 1]) {
    +        i--;
    +      } else {
    +        j--;
    +      }
    +    }
    +  }
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Returns the sum of the arguments.
    + * @param {...number} var_args Numbers to add.
    + * @return {number} The sum of the arguments (0 if no arguments were provided,
    + *     {@code NaN} if any of the arguments is not a valid number).
    + */
    +goog.math.sum = function(var_args) {
    +  return /** @type {number} */ (goog.array.reduce(arguments,
    +      function(sum, value) {
    +        return sum + value;
    +      }, 0));
    +};
    +
    +
    +/**
    + * Returns the arithmetic mean of the arguments.
    + * @param {...number} var_args Numbers to average.
    + * @return {number} The average of the arguments ({@code NaN} if no arguments
    + *     were provided or any of the arguments is not a valid number).
    + */
    +goog.math.average = function(var_args) {
    +  return goog.math.sum.apply(null, arguments) / arguments.length;
    +};
    +
    +
    +/**
    + * Returns the unbiased sample variance of the arguments. For a definition,
    + * see e.g. http://en.wikipedia.org/wiki/Variance
    + * @param {...number} var_args Number samples to analyze.
    + * @return {number} The unbiased sample variance of the arguments (0 if fewer
    + *     than two samples were provided, or {@code NaN} if any of the samples is
    + *     not a valid number).
    + */
    +goog.math.sampleVariance = function(var_args) {
    +  var sampleSize = arguments.length;
    +  if (sampleSize < 2) {
    +    return 0;
    +  }
    +
    +  var mean = goog.math.average.apply(null, arguments);
    +  var variance = goog.math.sum.apply(null, goog.array.map(arguments,
    +      function(val) {
    +        return Math.pow(val - mean, 2);
    +      })) / (sampleSize - 1);
    +
    +  return variance;
    +};
    +
    +
    +/**
    + * Returns the sample standard deviation of the arguments.  For a definition of
    + * sample standard deviation, see e.g.
    + * http://en.wikipedia.org/wiki/Standard_deviation
    + * @param {...number} var_args Number samples to analyze.
    + * @return {number} The sample standard deviation of the arguments (0 if fewer
    + *     than two samples were provided, or {@code NaN} if any of the samples is
    + *     not a valid number).
    + */
    +goog.math.standardDeviation = function(var_args) {
    +  return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
    +};
    +
    +
    +/**
    + * Returns whether the supplied number represents an integer, i.e. that is has
    + * no fractional component.  No range-checking is performed on the number.
    + * @param {number} num The number to test.
    + * @return {boolean} Whether {@code num} is an integer.
    + */
    +goog.math.isInt = function(num) {
    +  return isFinite(num) && num % 1 == 0;
    +};
    +
    +
    +/**
    + * Returns whether the supplied number is finite and not NaN.
    + * @param {number} num The number to test.
    + * @return {boolean} Whether {@code num} is a finite number.
    + */
    +goog.math.isFiniteNumber = function(num) {
    +  return isFinite(num) && !isNaN(num);
    +};
    +
    +
    +/**
    + * Returns the precise value of floor(log10(num)).
    + * Simpler implementations didn't work because of floating point rounding
    + * errors. For example
    + * <ul>
    + * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
    + * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
    + * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
    + * </ul>
    + * @param {number} num A floating point number.
    + * @return {number} Its logarithm to base 10 rounded down to the nearest
    + *     integer if num > 0. -Infinity if num == 0. NaN if num < 0.
    + */
    +goog.math.log10Floor = function(num) {
    +  if (num > 0) {
    +    var x = Math.round(Math.log(num) * Math.LOG10E);
    +    return x - (parseFloat('1e' + x) > num);
    +  }
    +  return num == 0 ? -Infinity : NaN;
    +};
    +
    +
    +/**
    + * A tweaked variant of {@code Math.floor} which tolerates if the passed number
    + * is infinitesimally smaller than the closest integer. It often happens with
    + * the results of floating point calculations because of the finite precision
    + * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
    + * Math.LN10) == 2}, not 3 as one would expect.
    + * @param {number} num A number.
    + * @param {number=} opt_epsilon An infinitesimally small positive number, the
    + *     rounding error to tolerate.
    + * @return {number} The largest integer less than or equal to {@code num}.
    + */
    +goog.math.safeFloor = function(num, opt_epsilon) {
    +  goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
    +  return Math.floor(num + (opt_epsilon || 2e-15));
    +};
    +
    +
    +/**
    + * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
    + * details.
    + * @param {number} num A number.
    + * @param {number=} opt_epsilon An infinitesimally small positive number, the
    + *     rounding error to tolerate.
    + * @return {number} The smallest integer greater than or equal to {@code num}.
    + */
    +goog.math.safeCeil = function(num, opt_epsilon) {
    +  goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
    +  return Math.ceil(num - (opt_epsilon || 2e-15));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/math_test.html b/mapbox-gl/closure-library/closure/goog/math/math_test.html
    new file mode 100644
    index 0000000000..400c0b93cf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/math_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.mathTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/math_test.js b/mapbox-gl/closure-library/closure/goog/math/math_test.js
    new file mode 100644
    index 0000000000..c3f31eb3bc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/math_test.js
    @@ -0,0 +1,332 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.mathTest');
    +goog.setTestOnly('goog.mathTest');
    +
    +goog.require('goog.math');
    +goog.require('goog.testing.jsunit');
    +
    +function testRandomInt() {
    +  assertEquals(0, goog.math.randomInt(0));
    +  assertEquals(0, goog.math.randomInt(1));
    +
    +  var r = goog.math.randomInt(3);
    +  assertTrue(0 <= r && r < 3);
    +}
    +
    +function testUniformRandom() {
    +  assertEquals(5.2, goog.math.uniformRandom(5.2, 5.2));
    +  assertEquals(-6, goog.math.uniformRandom(-6, -6));
    +
    +  var r = goog.math.uniformRandom(-0.5, 0.5);
    +  assertTrue(-0.5 <= r && r < 0.5);
    +}
    +
    +function testClamp() {
    +  assertEquals(3, goog.math.clamp(3, -5, 5));
    +  assertEquals(5, goog.math.clamp(5, -5, 5));
    +  assertEquals(-5, goog.math.clamp(-5, -5, 5));
    +
    +  assertEquals(-5, goog.math.clamp(-22, -5, 5));
    +  assertEquals(5, goog.math.clamp(6, -5, 5));
    +}
    +
    +function testModulo() {
    +  assertEquals(0, goog.math.modulo(256, 8));
    +
    +  assertEquals(7, goog.math.modulo(7, 8));
    +  assertEquals(7, goog.math.modulo(23, 8));
    +  assertEquals(7, goog.math.modulo(-1, 8));
    +
    +  // Safari 5.1.7 has a bug in its JS engine where modulo is computed
    +  // incorrectly when using variables. We avoid using
    +  // goog.testing.ExpectedFailure here since it pulls in a bunch of
    +  // extra dependencies for maintaining a DOM console.
    +  var a = 1;
    +  var b = -5;
    +  if (a % b === 1 % -5) {
    +    assertEquals(-4, goog.math.modulo(1, -5));
    +    assertEquals(-4, goog.math.modulo(6, -5));
    +  }
    +  assertEquals(-4, goog.math.modulo(-4, -5));
    +}
    +
    +function testLerp() {
    +  assertEquals(0, goog.math.lerp(0, 0, 0));
    +  assertEquals(3, goog.math.lerp(0, 6, 0.5));
    +  assertEquals(3, goog.math.lerp(-1, 1, 2));
    +}
    +
    +function testNearlyEquals() {
    +  assertTrue('Numbers inside default tolerance should be equal',
    +             goog.math.nearlyEquals(0.000001, 0.000001001));
    +  assertFalse('Numbers outside default tolerance should be unequal',
    +              goog.math.nearlyEquals(0.000001, 0.000003));
    +  assertTrue('Numbers inside custom tolerance should be equal',
    +             goog.math.nearlyEquals(0.001, 0.002, 0.1));
    +  assertFalse('Numbers outside custom tolerance should be unequal',
    +              goog.math.nearlyEquals(0.001, -0.1, 0.1));
    +  assertTrue('Integer tolerance greater than one should succeed',
    +             goog.math.nearlyEquals(87, 85, 3));
    +}
    +
    +function testStandardAngleInRadians() {
    +  assertRoughlyEquals(0, goog.math.standardAngleInRadians(2 * Math.PI), 1e-10);
    +  assertRoughlyEquals(
    +      Math.PI, goog.math.standardAngleInRadians(Math.PI), 1e-10);
    +  assertRoughlyEquals(
    +      Math.PI, goog.math.standardAngleInRadians(-1 * Math.PI), 1e-10);
    +  assertRoughlyEquals(
    +      Math.PI / 2, goog.math.standardAngleInRadians(-1.5 * Math.PI), 1e-10);
    +  assertRoughlyEquals(
    +      Math.PI, goog.math.standardAngleInRadians(5 * Math.PI), 1e-10);
    +  assertEquals(0.01, goog.math.standardAngleInRadians(0.01));
    +  assertEquals(0, goog.math.standardAngleInRadians(0));
    +}
    +
    +function testStandardAngle() {
    +  assertEquals(359.5, goog.math.standardAngle(-360.5));
    +  assertEquals(0, goog.math.standardAngle(-360));
    +  assertEquals(359.5, goog.math.standardAngle(-0.5));
    +  assertEquals(0, goog.math.standardAngle(0));
    +  assertEquals(0.5, goog.math.standardAngle(0.5));
    +  assertEquals(0, goog.math.standardAngle(360));
    +  assertEquals(1, goog.math.standardAngle(721));
    +}
    +
    +function testToRadians() {
    +  assertEquals(-Math.PI, goog.math.toRadians(-180));
    +  assertEquals(0, goog.math.toRadians(0));
    +  assertEquals(Math.PI, goog.math.toRadians(180));
    +}
    +
    +function testToDegrees() {
    +  assertEquals(-180, goog.math.toDegrees(-Math.PI));
    +  assertEquals(0, goog.math.toDegrees(0));
    +  assertEquals(180, goog.math.toDegrees(Math.PI));
    +}
    +
    +function testAngleDx() {
    +  assertRoughlyEquals(0, goog.math.angleDx(0, 0), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDx(90, 0), 1e-10);
    +  assertRoughlyEquals(100, goog.math.angleDx(0, 100), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDx(90, 100), 1e-10);
    +  assertRoughlyEquals(-100, goog.math.angleDx(180, 100), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDx(270, 100), 1e-10);
    +}
    +
    +function testAngleDy() {
    +  assertRoughlyEquals(0, goog.math.angleDy(0, 0), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDy(90, 0), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDy(0, 100), 1e-10);
    +  assertRoughlyEquals(100, goog.math.angleDy(90, 100), 1e-10);
    +  assertRoughlyEquals(0, goog.math.angleDy(180, 100), 1e-10);
    +  assertRoughlyEquals(-100, goog.math.angleDy(270, 100), 1e-10);
    +}
    +
    +function testAngle() {
    +  assertRoughlyEquals(0, goog.math.angle(10, 10, 20, 10), 1e-10);
    +  assertRoughlyEquals(90, goog.math.angle(10, 10, 10, 20), 1e-10);
    +  assertRoughlyEquals(225, goog.math.angle(10, 10, 0, 0), 1e-10);
    +  assertRoughlyEquals(270, goog.math.angle(10, 10, 10, 0), 1e-10);
    +
    +  // 0 is the conventional result, but mathematically this is undefined.
    +  assertEquals(0, goog.math.angle(10, 10, 10, 10));
    +}
    +
    +function testAngleDifference() {
    +  assertEquals(10, goog.math.angleDifference(30, 40));
    +  assertEquals(-10, goog.math.angleDifference(40, 30));
    +  assertEquals(180, goog.math.angleDifference(10, 190));
    +  assertEquals(180, goog.math.angleDifference(190, 10));
    +  assertEquals(20, goog.math.angleDifference(350, 10));
    +  assertEquals(-20, goog.math.angleDifference(10, 350));
    +  assertEquals(100, goog.math.angleDifference(350, 90));
    +  assertEquals(-80, goog.math.angleDifference(350, 270));
    +  assertEquals(0, goog.math.angleDifference(15, 15));
    +}
    +
    +function testSign() {
    +  assertEquals(-1, goog.math.sign(-1));
    +  assertEquals(1, goog.math.sign(1));
    +  assertEquals(0, goog.math.sign(0));
    +  assertEquals(0, goog.math.sign(-0));
    +  assertEquals(1, goog.math.sign(0.0001));
    +  assertEquals(-1, goog.math.sign(-0.0001));
    +  assertEquals(-1, goog.math.sign(-Infinity));
    +  assertEquals(1, goog.math.sign(Infinity));
    +  assertEquals(1, goog.math.sign(3141592653589793));
    +}
    +
    +function testLongestCommonSubsequence() {
    +  var func = goog.math.longestCommonSubsequence;
    +
    +  assertArrayEquals([2], func([1, 2], [2, 1]));
    +  assertArrayEquals([1, 2], func([1, 2, 5], [2, 1, 2]));
    +  assertArrayEquals([1, 2, 3, 4, 5],
    +      func([1, 0, 2, 3, 8, 4, 9, 5], [8, 1, 2, 4, 3, 6, 4, 5]));
    +  assertArrayEquals([1, 1, 1, 1, 1], func([1, 1, 1, 1, 1], [1, 1, 1, 1, 1]));
    +  assertArrayEquals([5], func([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]));
    +  assertArrayEquals([1, 8, 11],
    +      func([1, 6, 8, 11, 13], [1, 3, 5, 8, 9, 11, 12]));
    +}
    +
    +function testLongestCommonSubsequenceWithCustomComparator() {
    +  var func = goog.math.longestCommonSubsequence;
    +
    +  var compareFn = function(a, b) {
    +    return a.field == b.field;
    +  };
    +
    +  var a1 = {field: 'a1', field2: 'hello'};
    +  var a2 = {field: 'a2', field2: 33};
    +  var a3 = {field: 'a3'};
    +  var a4 = {field: 'a3'};
    +
    +  assertArrayEquals([a1, a2], func([a1, a2, a3], [a3, a1, a2], compareFn));
    +  assertArrayEquals([a1, a3], func([a1, a3], [a1, a4], compareFn));
    +  // testing the same arrays without compare function
    +  assertArrayEquals([a1], func([a1, a3], [a1, a4]));
    +}
    +
    +function testLongestCommonSubsequenceWithCustomCollector() {
    +  var func = goog.math.longestCommonSubsequence;
    +
    +  var collectorFn = function(a, b) {
    +    return b;
    +  };
    +
    +  assertArrayEquals([1, 2, 4, 6, 7],
    +      func([1, 0, 2, 3, 8, 4, 9, 5], [8, 1, 2, 4, 3, 6, 4, 5],
    +      null, collectorFn));
    +}
    +
    +function testSum() {
    +  assertEquals('sum() must return 0 if there are no arguments',
    +      0, goog.math.sum());
    +  assertEquals('sum() must return its argument if there is only one',
    +      17, goog.math.sum(17));
    +  assertEquals('sum() must handle positive integers',
    +      10, goog.math.sum(1, 2, 3, 4));
    +  assertEquals('sum() must handle real numbers',
    +      -2.5, goog.math.sum(1, -2, 3, -4.5));
    +  assertTrue('sum() must return NaN if one of the arguments isn\'t numeric',
    +      isNaN(goog.math.sum(1, 2, 'foo', 3)));
    +}
    +
    +function testAverage() {
    +  assertTrue('average() must return NaN if there are no arguments',
    +      isNaN(goog.math.average()));
    +  assertEquals('average() must return its argument if there is only one',
    +      17, goog.math.average(17));
    +  assertEquals('average() must handle positive integers',
    +      3, goog.math.average(1, 2, 3, 4, 5));
    +  assertEquals('average() must handle real numbers',
    +      -0.625, goog.math.average(1, -2, 3, -4.5));
    +  assertTrue('average() must return NaN if one of the arguments isn\'t ' +
    +      'numeric', isNaN(goog.math.average(1, 2, 'foo', 3)));
    +}
    +
    +function testSampleVariance() {
    +  assertEquals('sampleVariance() must return 0 if there are no samples',
    +      0, goog.math.sampleVariance());
    +  assertEquals('sampleVariance() must return 0 if there is only one ' +
    +      'sample', 0, goog.math.sampleVariance(17));
    +  assertRoughlyEquals('sampleVariance() must handle positive integers',
    +      48, goog.math.sampleVariance(3, 7, 7, 19),
    +      0.0001);
    +  assertRoughlyEquals('sampleVariance() must handle real numbers',
    +      12.0138, goog.math.sampleVariance(1.23, -2.34, 3.14, -4.56),
    +      0.0001);
    +}
    +
    +function testStandardDeviation() {
    +  assertEquals('standardDeviation() must return 0 if there are no samples',
    +      0, goog.math.standardDeviation());
    +  assertEquals('standardDeviation() must return 0 if there is only one ' +
    +      'sample', 0, goog.math.standardDeviation(17));
    +  assertRoughlyEquals('standardDeviation() must handle positive integers',
    +      6.9282, goog.math.standardDeviation(3, 7, 7, 19),
    +      0.0001);
    +  assertRoughlyEquals('standardDeviation() must handle real numbers',
    +      3.4660, goog.math.standardDeviation(1.23, -2.34, 3.14, -4.56),
    +      0.0001);
    +}
    +
    +function testIsInt() {
    +  assertFalse(goog.math.isInt(12345.67));
    +  assertFalse(goog.math.isInt(0.123));
    +  assertFalse(goog.math.isInt(.1));
    +  assertFalse(goog.math.isInt(-23.43));
    +  assertFalse(goog.math.isInt(-.1));
    +  assertFalse(goog.math.isInt(1e-1));
    +  assertTrue(goog.math.isInt(1));
    +  assertTrue(goog.math.isInt(0));
    +  assertTrue(goog.math.isInt(-2));
    +  assertTrue(goog.math.isInt(-2.0));
    +  assertTrue(goog.math.isInt(10324231));
    +  assertTrue(goog.math.isInt(1.));
    +  assertTrue(goog.math.isInt(1e3));
    +}
    +
    +function testIsFiniteNumber() {
    +  assertFalse(goog.math.isFiniteNumber(NaN));
    +  assertFalse(goog.math.isFiniteNumber(-Infinity));
    +  assertFalse(goog.math.isFiniteNumber(+Infinity));
    +  assertTrue(goog.math.isFiniteNumber(0));
    +  assertTrue(goog.math.isFiniteNumber(1));
    +  assertTrue(goog.math.isFiniteNumber(Math.PI));
    +}
    +
    +function testLog10Floor() {
    +  // The greatest floating point number that is less than 1.
    +  var oneMinusEpsilon = 1 - Math.pow(2, -53);
    +  for (var i = -30; i <= 30; i++) {
    +    assertEquals(i, goog.math.log10Floor(parseFloat('1e' + i)));
    +    assertEquals(i - 1,
    +        goog.math.log10Floor(parseFloat('1e' + i) * oneMinusEpsilon));
    +  }
    +  assertEquals(-Infinity, goog.math.log10Floor(0));
    +  assertTrue(isNaN(goog.math.log10Floor(-1)));
    +}
    +
    +function testSafeFloor() {
    +  assertEquals(0, goog.math.safeFloor(0));
    +  assertEquals(0, goog.math.safeFloor(1e-15));
    +  assertEquals(0, goog.math.safeFloor(-1e-15));
    +  assertEquals(-1, goog.math.safeFloor(-3e-15));
    +  assertEquals(4, goog.math.safeFloor(5 - 3e-15));
    +  assertEquals(5, goog.math.safeFloor(5 - 1e-15));
    +  assertEquals(-5, goog.math.safeFloor(-5 - 1e-15));
    +  assertEquals(-6, goog.math.safeFloor(-5 - 3e-15));
    +  assertEquals(3, goog.math.safeFloor(2.91, 0.1));
    +  assertEquals(2, goog.math.safeFloor(2.89, 0.1));
    +  // Tests some real life examples with the default epsilon value.
    +  assertEquals(0, goog.math.safeFloor(Math.log(1000) / Math.LN10 - 3));
    +  assertEquals(21, goog.math.safeFloor(Math.log(1e+21) / Math.LN10));
    +}
    +
    +function testSafeCeil() {
    +  assertEquals(0, goog.math.safeCeil(0));
    +  assertEquals(0, goog.math.safeCeil(1e-15));
    +  assertEquals(0, goog.math.safeCeil(-1e-15));
    +  assertEquals(1, goog.math.safeCeil(3e-15));
    +  assertEquals(6, goog.math.safeCeil(5 + 3e-15));
    +  assertEquals(5, goog.math.safeCeil(5 + 1e-15));
    +  assertEquals(-4, goog.math.safeCeil(-5 + 3e-15));
    +  assertEquals(-5, goog.math.safeCeil(-5 + 1e-15));
    +  assertEquals(3, goog.math.safeCeil(3.09, 0.1));
    +  assertEquals(4, goog.math.safeCeil(3.11, 0.1));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/matrix.js b/mapbox-gl/closure-library/closure/goog/math/matrix.js
    new file mode 100644
    index 0000000000..4f67fa49f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/matrix.js
    @@ -0,0 +1,681 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for representing matrices and static helper functions.
    + */
    +
    +goog.provide('goog.math.Matrix');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +goog.require('goog.math.Size');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Class for representing and manipulating matrices.
    + *
    + * The entry that lies in the i-th row and the j-th column of a matrix is
    + * typically referred to as the i,j entry of the matrix.
    + *
    + * The m-by-n matrix A would have its entries referred to as:
    + *   [ a0,0   a0,1   a0,2   ...   a0,j  ...  a0,n ]
    + *   [ a1,0   a1,1   a1,2   ...   a1,j  ...  a1,n ]
    + *   [ a2,0   a2,1   a2,2   ...   a2,j  ...  a2,n ]
    + *   [  .      .      .            .          .   ]
    + *   [  .      .      .            .          .   ]
    + *   [  .      .      .            .          .   ]
    + *   [ ai,0   ai,1   ai,2   ...   ai,j  ...  ai,n ]
    + *   [  .      .      .            .          .   ]
    + *   [  .      .      .            .          .   ]
    + *   [  .      .      .            .          .   ]
    + *   [ am,0   am,1   am,2   ...   am,j  ...  am,n ]
    + *
    + * @param {!goog.math.Matrix|!Array<!Array<number>>|!goog.math.Size|number} m
    + *     A matrix to copy, a 2D-array to take as a template, a size object for
    + *     dimensions, or the number of rows.
    + * @param {number=} opt_n Number of columns of the matrix (only applicable if
    + *     the first argument is also numeric).
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.Matrix = function(m, opt_n) {
    +  if (m instanceof goog.math.Matrix) {
    +    this.array_ = m.toArray();
    +  } else if (goog.isArrayLike(m) &&
    +             goog.math.Matrix.isValidArray(
    +                 /** @type {!Array<!Array<number>>} */ (m))) {
    +    this.array_ = goog.array.clone(/** @type {!Array<!Array<number>>} */ (m));
    +  } else if (m instanceof goog.math.Size) {
    +    this.array_ = goog.math.Matrix.createZeroPaddedArray_(m.height, m.width);
    +  } else if (goog.isNumber(m) && goog.isNumber(opt_n) && m > 0 && opt_n > 0) {
    +    this.array_ = goog.math.Matrix.createZeroPaddedArray_(
    +        /** @type {number} */ (m), opt_n);
    +  } else {
    +    throw Error('Invalid argument(s) for Matrix contructor');
    +  }
    +
    +  this.size_ = new goog.math.Size(this.array_[0].length, this.array_.length);
    +};
    +
    +
    +/**
    + * Creates a square identity matrix. i.e. for n = 3:
    + * <pre>
    + * [ 1 0 0 ]
    + * [ 0 1 0 ]
    + * [ 0 0 1 ]
    + * </pre>
    + * @param {number} n The size of the square identity matrix.
    + * @return {!goog.math.Matrix} Identity matrix of width and height {@code n}.
    + */
    +goog.math.Matrix.createIdentityMatrix = function(n) {
    +  var rv = [];
    +  for (var i = 0; i < n; i++) {
    +    rv[i] = [];
    +    for (var j = 0; j < n; j++) {
    +      rv[i][j] = i == j ? 1 : 0;
    +    }
    +  }
    +  return new goog.math.Matrix(rv);
    +};
    +
    +
    +/**
    + * Calls a function for each cell in a matrix.
    + * @param {goog.math.Matrix} matrix The matrix to iterate over.
    + * @param {function(this:T, number, number, number, !goog.math.Matrix)} fn
    + *     The function to call for every element. This function
    + *     takes 4 arguments (value, i, j, and the matrix)
    + *     and the return value is irrelevant.
    + * @param {T=} opt_obj The object to be used as the value of 'this'
    + *     within {@code fn}.
    + * @template T
    + */
    +goog.math.Matrix.forEach = function(matrix, fn, opt_obj) {
    +  for (var i = 0; i < matrix.getSize().height; i++) {
    +    for (var j = 0; j < matrix.getSize().width; j++) {
    +      fn.call(opt_obj, matrix.array_[i][j], i, j, matrix);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Tests whether an array is a valid matrix.  A valid array is an array of
    + * arrays where all arrays are of the same length and all elements are numbers.
    + * @param {!Array<!Array<number>>} arr An array to test.
    + * @return {boolean} Whether the array is a valid matrix.
    + */
    +goog.math.Matrix.isValidArray = function(arr) {
    +  var len = 0;
    +  for (var i = 0; i < arr.length; i++) {
    +    if (!goog.isArrayLike(arr[i]) || len > 0 && arr[i].length != len) {
    +      return false;
    +    }
    +    for (var j = 0; j < arr[i].length; j++) {
    +      if (!goog.isNumber(arr[i][j])) {
    +        return false;
    +      }
    +    }
    +    if (len == 0) {
    +      len = arr[i].length;
    +    }
    +  }
    +  return len != 0;
    +};
    +
    +
    +/**
    + * Calls a function for every cell in a matrix and inserts the result into a
    + * new matrix of equal dimensions.
    + * @param {!goog.math.Matrix} matrix The matrix to iterate over.
    + * @param {function(this:T, number, number, number, !goog.math.Matrix): number}
    + *     fn The function to call for every element. This function
    + *     takes 4 arguments (value, i, j and the matrix)
    + *     and should return a number, which will be inserted into a new matrix.
    + * @param {T=} opt_obj The object to be used as the value of 'this'
    + *     within {@code fn}.
    + * @return {!goog.math.Matrix} A new matrix with the results from {@code fn}.
    + * @template T
    + */
    +goog.math.Matrix.map = function(matrix, fn, opt_obj) {
    +  var m = new goog.math.Matrix(matrix.getSize());
    +  goog.math.Matrix.forEach(matrix, function(value, i, j) {
    +    m.array_[i][j] = fn.call(opt_obj, value, i, j, matrix);
    +  });
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a new zero padded matix.
    + * @param {number} m Height of matrix.
    + * @param {number} n Width of matrix.
    + * @return {!Array<!Array<number>>} The new zero padded matrix.
    + * @private
    + */
    +goog.math.Matrix.createZeroPaddedArray_ = function(m, n) {
    +  var rv = [];
    +  for (var i = 0; i < m; i++) {
    +    rv[i] = [];
    +    for (var j = 0; j < n; j++) {
    +      rv[i][j] = 0;
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Internal array representing the matrix.
    + * @type {!Array<!Array<number>>}
    + * @private
    + */
    +goog.math.Matrix.prototype.array_;
    +
    +
    +/**
    + * After construction the Matrix's size is constant and stored in this object.
    + * @type {!goog.math.Size}
    + * @private
    + */
    +goog.math.Matrix.prototype.size_;
    +
    +
    +/**
    + * Returns a new matrix that is the sum of this and the provided matrix.
    + * @param {goog.math.Matrix} m The matrix to add to this one.
    + * @return {!goog.math.Matrix} Resultant sum.
    + */
    +goog.math.Matrix.prototype.add = function(m) {
    +  if (!goog.math.Size.equals(this.size_, m.getSize())) {
    +    throw Error('Matrix summation is only supported on arrays of equal size');
    +  }
    +  return goog.math.Matrix.map(this, function(val, i, j) {
    +    return val + m.array_[i][j];
    +  });
    +};
    +
    +
    +/**
    + * Appends the given matrix to the right side of this matrix.
    + * @param {goog.math.Matrix} m The matrix to augment this matrix with.
    + * @return {!goog.math.Matrix} A new matrix with additional columns on the
    + *     right.
    + */
    +goog.math.Matrix.prototype.appendColumns = function(m) {
    +  if (this.size_.height != m.getSize().height) {
    +    throw Error('The given matrix has height ' + m.size_.height + ', but ' +
    +        ' needs to have height ' + this.size_.height + '.');
    +  }
    +  var result = new goog.math.Matrix(this.size_.height,
    +      this.size_.width + m.size_.width);
    +  goog.math.Matrix.forEach(this, function(value, i, j) {
    +    result.array_[i][j] = value;
    +  });
    +  goog.math.Matrix.forEach(m, function(value, i, j) {
    +    result.array_[i][this.size_.width + j] = value;
    +  }, this);
    +  return result;
    +};
    +
    +
    +/**
    + * Appends the given matrix to the bottom of this matrix.
    + * @param {goog.math.Matrix} m The matrix to augment this matrix with.
    + * @return {!goog.math.Matrix} A new matrix with added columns on the bottom.
    + */
    +goog.math.Matrix.prototype.appendRows = function(m) {
    +  if (this.size_.width != m.getSize().width) {
    +    throw Error('The given matrix has width ' + m.size_.width + ', but ' +
    +        ' needs to have width ' + this.size_.width + '.');
    +  }
    +  var result = new goog.math.Matrix(this.size_.height + m.size_.height,
    +      this.size_.width);
    +  goog.math.Matrix.forEach(this, function(value, i, j) {
    +    result.array_[i][j] = value;
    +  });
    +  goog.math.Matrix.forEach(m, function(value, i, j) {
    +    result.array_[this.size_.height + i][j] = value;
    +  }, this);
    +  return result;
    +};
    +
    +
    +/**
    + * Returns whether the given matrix equals this matrix.
    + * @param {goog.math.Matrix} m The matrix to compare to this one.
    + * @param {number=} opt_tolerance The tolerance when comparing array entries.
    + * @return {boolean} Whether the given matrix equals this matrix.
    + */
    +goog.math.Matrix.prototype.equals = function(m, opt_tolerance) {
    +  if (this.size_.width != m.size_.width) {
    +    return false;
    +  }
    +  if (this.size_.height != m.size_.height) {
    +    return false;
    +  }
    +
    +  var tolerance = opt_tolerance || 0;
    +  for (var i = 0; i < this.size_.height; i++) {
    +    for (var j = 0; j < this.size_.width; j++) {
    +      if (!goog.math.nearlyEquals(this.array_[i][j], m.array_[i][j],
    +          tolerance)) {
    +        return false;
    +      }
    +    }
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Returns the determinant of this matrix.  The determinant of a matrix A is
    + * often denoted as |A| and can only be applied to a square matrix.
    + * @return {number} The determinant of this matrix.
    + */
    +goog.math.Matrix.prototype.getDeterminant = function() {
    +  if (!this.isSquare()) {
    +    throw Error('A determinant can only be take on a square matrix');
    +  }
    +
    +  return this.getDeterminant_();
    +};
    +
    +
    +/**
    + * Returns the inverse of this matrix if it exists or null if the matrix is
    + * not invertible.
    + * @return {goog.math.Matrix} A new matrix which is the inverse of this matrix.
    + */
    +goog.math.Matrix.prototype.getInverse = function() {
    +  if (!this.isSquare()) {
    +    throw Error('An inverse can only be taken on a square matrix.');
    +  }
    +  if (this.getSize().width == 1) {
    +    var a = this.getValueAt(0, 0);
    +    return a == 0 ? null : new goog.math.Matrix([[1 / a]]);
    +  }
    +  var identity = goog.math.Matrix.createIdentityMatrix(this.size_.height);
    +  var mi = this.appendColumns(identity).getReducedRowEchelonForm();
    +  var i = mi.getSubmatrixByCoordinates_(
    +      0, 0, identity.size_.width - 1, identity.size_.height - 1);
    +  if (!i.equals(identity)) {
    +    return null;  // This matrix was not invertible
    +  }
    +  return mi.getSubmatrixByCoordinates_(0, identity.size_.width);
    +};
    +
    +
    +/**
    + * Transforms this matrix into reduced row echelon form.
    + * @return {!goog.math.Matrix} A new matrix reduced row echelon form.
    + */
    +goog.math.Matrix.prototype.getReducedRowEchelonForm = function() {
    +  var result = new goog.math.Matrix(this);
    +  var col = 0;
    +  // Each iteration puts one row in reduced row echelon form
    +  for (var row = 0; row < result.size_.height; row++) {
    +    if (col >= result.size_.width) {
    +      return result;
    +    }
    +
    +    // Scan each column starting from this row on down for a non-zero value
    +    var i = row;
    +    while (result.array_[i][col] == 0) {
    +      i++;
    +      if (i == result.size_.height) {
    +        i = row;
    +        col++;
    +        if (col == result.size_.width) {
    +          return result;
    +        }
    +      }
    +    }
    +
    +    // Make the row we found the current row with a leading 1
    +    this.swapRows_(i, row);
    +    var divisor = result.array_[row][col];
    +    for (var j = col; j < result.size_.width; j++) {
    +      result.array_[row][j] = result.array_[row][j] / divisor;
    +    }
    +
    +    // Subtract a multiple of this row from each other row
    +    // so that all the other entries in this column are 0
    +    for (i = 0; i < result.size_.height; i++) {
    +      if (i != row) {
    +        var multiple = result.array_[i][col];
    +        for (var j = col; j < result.size_.width; j++) {
    +          result.array_[i][j] -= multiple * result.array_[row][j];
    +        }
    +      }
    +    }
    +
    +    // Move on to the next column
    +    col++;
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Size} The dimensions of the matrix.
    + */
    +goog.math.Matrix.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * Return the transpose of this matrix.  For an m-by-n matrix, the transpose
    + * is the n-by-m matrix which results from turning rows into columns and columns
    + * into rows
    + * @return {!goog.math.Matrix} A new matrix A^T.
    + */
    +goog.math.Matrix.prototype.getTranspose = function() {
    +  var m = new goog.math.Matrix(this.size_.width, this.size_.height);
    +  goog.math.Matrix.forEach(this, function(value, i, j) {
    +    m.array_[j][i] = value;
    +  });
    +  return m;
    +};
    +
    +
    +/**
    + * Retrieves the value of a particular coordinate in the matrix or null if the
    + * requested coordinates are out of range.
    + * @param {number} i The i index of the coordinate.
    + * @param {number} j The j index of the coordinate.
    + * @return {?number} The value at the specified coordinate.
    + */
    +goog.math.Matrix.prototype.getValueAt = function(i, j) {
    +  if (!this.isInBounds_(i, j)) {
    +    return null;
    +  }
    +  return this.array_[i][j];
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the horizontal and vertical dimensions of this
    + *     matrix are the same.
    + */
    +goog.math.Matrix.prototype.isSquare = function() {
    +  return this.size_.width == this.size_.height;
    +};
    +
    +
    +/**
    + * Sets the value at a particular coordinate (if the coordinate is within the
    + * bounds of the matrix).
    + * @param {number} i The i index of the coordinate.
    + * @param {number} j The j index of the coordinate.
    + * @param {number} value The new value for the coordinate.
    + */
    +goog.math.Matrix.prototype.setValueAt = function(i, j, value) {
    +  if (!this.isInBounds_(i, j)) {
    +    throw Error(
    +        'Index out of bounds when setting matrix value, (' + i + ',' + j +
    +        ') in size (' + this.size_.height + ',' + this.size_.width + ')');
    +  }
    +  this.array_[i][j] = value;
    +};
    +
    +
    +/**
    + * Performs matrix or scalar multiplication on a matrix and returns the
    + * resultant matrix.
    + *
    + * Matrix multiplication is defined between two matrices only if the number of
    + * columns of the first matrix is the same as the number of rows of the second
    + * matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
    + * product AB is an m-by-p matrix
    + *
    + * Scalar multiplication returns a matrix of the same size as the original,
    + * each value multiplied by the given value.
    + *
    + * @param {goog.math.Matrix|number} m Matrix/number to multiply the matrix by.
    + * @return {!goog.math.Matrix} Resultant product.
    + */
    +goog.math.Matrix.prototype.multiply = function(m) {
    +  if (m instanceof goog.math.Matrix) {
    +    if (this.size_.width != m.getSize().height) {
    +      throw Error('Invalid matrices for multiplication. Second matrix ' +
    +          'should have the same number of rows as the first has columns.');
    +    }
    +    return this.matrixMultiply_(/** @type {!goog.math.Matrix} */ (m));
    +  } else if (goog.isNumber(m)) {
    +    return this.scalarMultiply_(/** @type {number} */ (m));
    +  } else {
    +    throw Error('A matrix can only be multiplied by' +
    +        ' a number or another matrix.');
    +  }
    +};
    +
    +
    +/**
    + * Returns a new matrix that is the difference of this and the provided matrix.
    + * @param {goog.math.Matrix} m The matrix to subtract from this one.
    + * @return {!goog.math.Matrix} Resultant difference.
    + */
    +goog.math.Matrix.prototype.subtract = function(m) {
    +  if (!goog.math.Size.equals(this.size_, m.getSize())) {
    +    throw Error(
    +        'Matrix subtraction is only supported on arrays of equal size.');
    +  }
    +  return goog.math.Matrix.map(this, function(val, i, j) {
    +    return val - m.array_[i][j];
    +  });
    +};
    +
    +
    +/**
    + * @return {!Array<!Array<number>>} A 2D internal array representing this
    + *     matrix.  Not a clone.
    + */
    +goog.math.Matrix.prototype.toArray = function() {
    +  return this.array_;
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a string representation of the matrix.  e.g.
    +   * <pre>
    +   * [ 12  5  9  1 ]
    +   * [  4 16  0 17 ]
    +   * [ 12  5  1 23 ]
    +   * </pre>
    +   *
    +   * @return {string} A string representation of this matrix.
    +   * @override
    +   */
    +  goog.math.Matrix.prototype.toString = function() {
    +    // Calculate correct padding for optimum display of matrix
    +    var maxLen = 0;
    +    goog.math.Matrix.forEach(this, function(val) {
    +      var len = String(val).length;
    +      if (len > maxLen) {
    +        maxLen = len;
    +      }
    +    });
    +
    +    // Build the string
    +    var sb = [];
    +    goog.array.forEach(this.array_, function(row, x) {
    +      sb.push('[ ');
    +      goog.array.forEach(row, function(val, y) {
    +        var strval = String(val);
    +        sb.push(goog.string.repeat(' ', maxLen - strval.length) + strval + ' ');
    +      });
    +      sb.push(']\n');
    +    });
    +
    +    return sb.join('');
    +  };
    +}
    +
    +
    +/**
    + * Returns the signed minor.
    + * @param {number} i The row index.
    + * @param {number} j The column index.
    + * @return {number} The cofactor C[i,j] of this matrix.
    + * @private
    + */
    +goog.math.Matrix.prototype.getCofactor_ = function(i, j) {
    +  return (i + j % 2 == 0 ? 1 : -1) * this.getMinor_(i, j);
    +};
    +
    +
    +/**
    + * Returns the determinant of this matrix.  The determinant of a matrix A is
    + * often denoted as |A| and can only be applied to a square matrix.  Same as
    + * public method but without validation.  Implemented using Laplace's formula.
    + * @return {number} The determinant of this matrix.
    + * @private
    + */
    +goog.math.Matrix.prototype.getDeterminant_ = function() {
    +  if (this.getSize().area() == 1) {
    +    return this.array_[0][0];
    +  }
    +
    +  // We might want to use matrix decomposition to improve running time
    +  // For now we'll do a Laplace expansion along the first row
    +  var determinant = 0;
    +  for (var j = 0; j < this.size_.width; j++) {
    +    determinant += (this.array_[0][j] * this.getCofactor_(0, j));
    +  }
    +  return determinant;
    +};
    +
    +
    +/**
    + * Returns the determinant of the submatrix resulting from the deletion of row i
    + * and column j.
    + * @param {number} i The row to delete.
    + * @param {number} j The column to delete.
    + * @return {number} The first minor M[i,j] of this matrix.
    + * @private
    + */
    +goog.math.Matrix.prototype.getMinor_ = function(i, j) {
    +  return this.getSubmatrixByDeletion_(i, j).getDeterminant_();
    +};
    +
    +
    +/**
    + * Returns a submatrix contained within this matrix.
    + * @param {number} i1 The upper row index.
    + * @param {number} j1 The left column index.
    + * @param {number=} opt_i2 The lower row index.
    + * @param {number=} opt_j2 The right column index.
    + * @return {!goog.math.Matrix} The submatrix contained within the given bounds.
    + * @private
    + */
    +goog.math.Matrix.prototype.getSubmatrixByCoordinates_ =
    +    function(i1, j1, opt_i2, opt_j2) {
    +  var i2 = opt_i2 ? opt_i2 : this.size_.height - 1;
    +  var j2 = opt_j2 ? opt_j2 : this.size_.width - 1;
    +  var result = new goog.math.Matrix(i2 - i1 + 1, j2 - j1 + 1);
    +  goog.math.Matrix.forEach(result, function(value, i, j) {
    +    result.array_[i][j] = this.array_[i1 + i][j1 + j];
    +  }, this);
    +  return result;
    +};
    +
    +
    +/**
    + * Returns a new matrix equal to this one, but with row i and column j deleted.
    + * @param {number} i The row index of the coordinate.
    + * @param {number} j The column index of the coordinate.
    + * @return {!goog.math.Matrix} The value at the specified coordinate.
    + * @private
    + */
    +goog.math.Matrix.prototype.getSubmatrixByDeletion_ = function(i, j) {
    +  var m = new goog.math.Matrix(this.size_.width - 1, this.size_.height - 1);
    +  goog.math.Matrix.forEach(m, function(value, x, y) {
    +    m.setValueAt(x, y, this.array_[x >= i ? x + 1 : x][y >= j ? y + 1 : y]);
    +  }, this);
    +  return m;
    +};
    +
    +
    +/**
    + * Returns whether the given coordinates are contained within the bounds of the
    + * matrix.
    + * @param {number} i The i index of the coordinate.
    + * @param {number} j The j index of the coordinate.
    + * @return {boolean} The value at the specified coordinate.
    + * @private
    + */
    +goog.math.Matrix.prototype.isInBounds_ = function(i, j) {
    +  return i >= 0 && i < this.size_.height &&
    +         j >= 0 && j < this.size_.width;
    +};
    +
    +
    +/**
    + * Matrix multiplication is defined between two matrices only if the number of
    + * columns of the first matrix is the same as the number of rows of the second
    + * matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
    + * product AB is an m-by-p matrix
    + *
    + * @param {goog.math.Matrix} m Matrix to multiply the matrix by.
    + * @return {!goog.math.Matrix} Resultant product.
    + * @private
    + */
    +goog.math.Matrix.prototype.matrixMultiply_ = function(m) {
    +  var resultMatrix = new goog.math.Matrix(this.size_.height, m.getSize().width);
    +  goog.math.Matrix.forEach(resultMatrix, function(val, x, y) {
    +    var newVal = 0;
    +    for (var i = 0; i < this.size_.width; i++) {
    +      newVal += this.getValueAt(x, i) * m.getValueAt(i, y);
    +    }
    +    resultMatrix.setValueAt(x, y, newVal);
    +  }, this);
    +  return resultMatrix;
    +};
    +
    +
    +/**
    + * Scalar multiplication returns a matrix of the same size as the original,
    + * each value multiplied by the given value.
    + *
    + * @param {number} m number to multiply the matrix by.
    + * @return {!goog.math.Matrix} Resultant product.
    + * @private
    + */
    +goog.math.Matrix.prototype.scalarMultiply_ = function(m) {
    +  return goog.math.Matrix.map(this, function(val, x, y) {
    +    return val * m;
    +  });
    +};
    +
    +
    +/**
    + * Swaps two rows.
    + * @param {number} i1 The index of the first row to swap.
    + * @param {number} i2 The index of the second row to swap.
    + * @private
    + */
    +goog.math.Matrix.prototype.swapRows_ = function(i1, i2) {
    +  var tmp = this.array_[i1];
    +  this.array_[i1] = this.array_[i2];
    +  this.array_[i2] = tmp;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/matrix_test.html b/mapbox-gl/closure-library/closure/goog/math/matrix_test.html
    new file mode 100644
    index 0000000000..832316ebd2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/matrix_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Matrix
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.MatrixTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/matrix_test.js b/mapbox-gl/closure-library/closure/goog/math/matrix_test.js
    new file mode 100644
    index 0000000000..181ebea402
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/matrix_test.js
    @@ -0,0 +1,429 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.MatrixTest');
    +goog.setTestOnly('goog.math.MatrixTest');
    +
    +goog.require('goog.math.Matrix');
    +goog.require('goog.testing.jsunit');
    +
    +function testConstuctorWithGoodArray() {
    +  var a1 = [[1, 2], [2, 3], [4, 5]];
    +  var m1 = new goog.math.Matrix(a1);
    +  assertArrayEquals('1. Internal array should be the same', m1.toArray(), a1);
    +  assertEquals(3, m1.getSize().height);
    +  assertEquals(2, m1.getSize().width);
    +
    +  var a2 = [[-61, 45, 123], [11112, 343, 1235]];
    +  var m2 = new goog.math.Matrix(a2);
    +  assertArrayEquals('2. Internal array should be the same', m2.toArray(), a2);
    +  assertEquals(2, m2.getSize().height);
    +  assertEquals(3, m2.getSize().width);
    +
    +  var a3 = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
    +  var m3 = new goog.math.Matrix(a3);
    +  assertArrayEquals('3. Internal array should be the same', m3.toArray(), a3);
    +  assertEquals(4, m3.getSize().height);
    +  assertEquals(4, m3.getSize().width);
    +}
    +
    +
    +function testConstructorWithBadArray() {
    +  assertThrows('1. All arrays should be of equal length', function() {
    +    new goog.math.Matrix([[1, 2, 3], [1, 2], [1]]);
    +  });
    +
    +  assertThrows('2. All arrays should be of equal length', function() {
    +    new goog.math.Matrix([[1, 2], [1, 2], [1, 2, 3, 4]]);
    +  });
    +
    +  assertThrows('3. Arrays should contain only numeric values', function() {
    +    new goog.math.Matrix([[1, 2], [1, 2], [1, 'a']]);
    +  });
    +
    +  assertThrows('4. Arrays should contain only numeric values', function() {
    +    new goog.math.Matrix([[1, 2], [1, 2], [1, {a: 3}]]);
    +  });
    +
    +  assertThrows('5. Arrays should contain only numeric values', function() {
    +    new goog.math.Matrix([[1, 2], [1, 2], [1, [1, 2, 3]]]);
    +  });
    +}
    +
    +
    +function testConstructorWithGoodNumbers() {
    +  var m1 = new goog.math.Matrix(2, 2);
    +  assertEquals('Height should be 2', 2, m1.getSize().height);
    +  assertEquals('Width should be 2', 2, m1.getSize().width);
    +
    +  var m2 = new goog.math.Matrix(4, 2);
    +  assertEquals('Height should be 4', 4, m2.getSize().height);
    +  assertEquals('Width should be 2', 2, m2.getSize().width);
    +
    +  var m3 = new goog.math.Matrix(4, 6);
    +  assertEquals('Height should be 4', 4, m3.getSize().height);
    +  assertEquals('Width should be 6', 6, m3.getSize().width);
    +}
    +
    +
    +function testConstructorWithBadNumbers() {
    +  assertThrows('1. Negative argument should have errored', function() {
    +    new goog.math.Matrix(-4, 6);
    +  });
    +
    +  assertThrows('2. Negative argument should have errored', function() {
    +    new goog.math.Matrix(4, -6);
    +  });
    +
    +  assertThrows('3. Zero argument should have errored', function() {
    +    new goog.math.Matrix(4, 0);
    +  });
    +
    +  assertThrows('4. Zero argument should have errored', function() {
    +    new goog.math.Matrix(0, 1);
    +  });
    +}
    +
    +
    +function testConstructorWithMatrix() {
    +  var a1 = [[1, 2], [2, 3], [4, 5]];
    +  var m1 = new goog.math.Matrix(a1);
    +  var m2 = new goog.math.Matrix(m1);
    +  assertArrayEquals(
    +      'Internal arrays should be the same', m1.toArray(), m2.toArray());
    +  assertNotEquals(
    +      'Should be different objects', goog.getUid(m1), goog.getUid(m2));
    +}
    +
    +
    +function testCreateIdentityMatrix() {
    +  var m1 = goog.math.Matrix.createIdentityMatrix(3);
    +  assertArrayEquals([[1, 0, 0], [0, 1, 0], [0, 0, 1]], m1.toArray());
    +
    +  var m2 = goog.math.Matrix.createIdentityMatrix(4);
    +  assertArrayEquals(
    +      [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], m2.toArray());
    +}
    +
    +
    +function testIsValidArrayWithGoodArrays() {
    +  var fn = goog.math.Matrix.isValidArray;
    +  assertTrue('2x2 array should be fine', fn([[1, 2], [3, 5]]));
    +  assertTrue('3x2 array should be fine', fn([[1, 2, 3], [3, 5, 6]]));
    +  assertTrue(
    +      '3x3 array should be fine', fn([[1, 2, 3], [3, 5, 6], [10, 10, 10]]));
    +  assertTrue('[[1]] should be fine', fn([[1]]));
    +  assertTrue('1D array should work', fn([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]));
    +  assertTrue('Negs and decimals should be ok',
    +      fn([[0], [-4], [-10], [1.2345], [123.53]]));
    +  assertTrue('Hex, Es and decimals are ok', fn([[0x100, 10E-2], [1.213, 213]]));
    +}
    +
    +
    +function testIsValidArrayWithBadArrays() {
    +  var fn = goog.math.Matrix.isValidArray;
    +  assertFalse('Arrays should have same size', fn([[1, 2], [3]]));
    +  assertFalse('Arrays should have same size 2', fn([[1, 2], [3, 4, 5]]));
    +  assertFalse('2D arrays are ok', fn([[1, 2], [3, 4], []]));
    +  assertFalse('Values should be numeric', fn([[1, 2], [3, 'a']]));
    +  assertFalse('Values can not be strings', fn([['bah'], ['foo']]));
    +  assertFalse('Flat array not supported', fn([1, 2, 3, 4, 5]));
    +}
    +
    +
    +function testForEach() {
    +  var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  var count = 0, sum = 0, xs = '', ys = '';
    +  goog.math.Matrix.forEach(m, function(val, x, y) {
    +    count++;
    +    sum += val;
    +    xs += x;
    +    ys += y;
    +  });
    +  assertEquals('forEach should have visited every item', 9, count);
    +  assertEquals('forEach should have summed all values', 45, sum);
    +  assertEquals('Xs should have been visited in order', '000111222', xs);
    +  assertEquals('Ys should have been visited sequentially', '012012012', ys);
    +}
    +
    +
    +function testMap() {
    +  var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  var m2 = goog.math.Matrix.map(m1, function(val, x, y) {
    +    return val + 1;
    +  });
    +  assertArrayEquals([[2, 3, 4], [5, 6, 7], [8, 9, 10]], m2.toArray());
    +}
    +
    +
    +function testSetValueAt() {
    +  var m = new goog.math.Matrix(3, 3);
    +  for (var x = 0; x < 3; x++) {
    +    for (var y = 0; y < 3; y++) {
    +      m.setValueAt(x, y, 3 * x - y);
    +    }
    +  }
    +  assertArrayEquals([[0, -1, -2], [3, 2, 1], [6, 5, 4]], m.toArray());
    +}
    +
    +
    +function testGetValueAt() {
    +  var m = new goog.math.Matrix([[0, -1, -2], [3, 2, 1], [6, 5, 4]]);
    +  for (var x = 0; x < 3; x++) {
    +    for (var y = 0; y < 3; y++) {
    +      assertEquals(
    +          'Value at (x, y) should equal 3x - y',
    +          3 * x - y, m.getValueAt(x, y));
    +    }
    +  }
    +  assertNull('Out of bounds value should be null', m.getValueAt(-1, 2));
    +  assertNull('Out of bounds value should be null', m.getValueAt(-1, 0));
    +  assertNull('Out of bounds value should be null', m.getValueAt(0, 4));
    +}
    +
    +
    +function testSum1() {
    +  var m1 = new goog.math.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
    +  var m2 = new goog.math.Matrix([[3, 3, 3], [2, 2, 2], [1, 1, 1]]);
    +  assertArrayEquals('Sum should be all the 4s',
    +      [[4, 4, 4], [4, 4, 4], [4, 4, 4]], m1.add(m2).toArray());
    +  assertArrayEquals('Addition should be commutative',
    +      m1.add(m2).toArray(), m2.add(m1).toArray());
    +}
    +
    +
    +function testSum2() {
    +  var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  var m2 = new goog.math.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
    +  assertArrayEquals('Sum should be all 0s',
    +      [[0, 0, 0], [0, 0, 0], [0, 0, 0]], m1.add(m2).toArray());
    +  assertArrayEquals('Addition should be commutative',
    +      m1.add(m2).toArray(), m2.add(m1).toArray());
    +}
    +
    +
    +function testSubtract1() {
    +  var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  var m2 = new goog.math.Matrix([[5, 5, 5], [5, 5, 5], [5, 5, 5]]);
    +
    +  assertArrayEquals([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]],
    +                    m1.subtract(m2).toArray());
    +  assertArrayEquals([[4, 3, 2], [1, 0, -1], [-2, -3, -4]],
    +                    m2.subtract(m1).toArray());
    +}
    +
    +
    +function testSubtract2() {
    +  var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  var m2 = new goog.math.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
    +  assertArrayEquals([[2, 4, 6], [8, 10, 12], [14, 16, 18]],
    +                    m1.subtract(m2).toArray());
    +  assertArrayEquals([[-2, -4, -6], [-8, -10, -12], [-14, -16, -18]],
    +                    m2.subtract(m1).toArray());
    +}
    +
    +
    +function testScalarMultiplication() {
    +  var m1 = new goog.math.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
    +  assertArrayEquals(
    +      [[2, 2, 2], [4, 4, 4], [6, 6, 6]], m1.multiply(2).toArray());
    +  assertArrayEquals(
    +      [[3, 3, 3], [6, 6, 6], [9, 9, 9]], m1.multiply(3).toArray());
    +  assertArrayEquals(
    +      [[4, 4, 4], [8, 8, 8], [12, 12, 12]], m1.multiply(4).toArray());
    +
    +  var m2 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  assertArrayEquals(
    +      [[2, 4, 6], [8, 10, 12], [14, 16, 18]], m2.multiply(2).toArray());
    +}
    +
    +
    +function testMatrixMultiplication() {
    +  var m1 = new goog.math.Matrix([[1, 2], [3, 4]]);
    +  var m2 = new goog.math.Matrix([[3, 4], [5, 6]]);
    +  // m1 * m2
    +  assertArrayEquals([[1 * 3 + 2 * 5, 1 * 4 + 2 * 6],
    +                     [3 * 3 + 4 * 5, 3 * 4 + 4 * 6]],
    +                    m1.multiply(m2).toArray());
    +  // m2 * m1 != m1 * m2
    +  assertArrayEquals([[3 * 1 + 4 * 3, 3 * 2 + 4 * 4],
    +                     [5 * 1 + 6 * 3, 5 * 2 + 6 * 4]],
    +                    m2.multiply(m1).toArray());
    +  var m3 = new goog.math.Matrix([[1, 2, 3, 4],
    +                                 [5, 6, 7, 8]]);
    +  var m4 = new goog.math.Matrix([[1, 2, 3],
    +                                 [4, 5, 6],
    +                                 [7, 8, 9],
    +                                 [10, 11, 12]]);
    +  // m3 * m4
    +  assertArrayEquals([[1 * 1 + 2 * 4 + 3 * 7 + 4 * 10,
    +                      1 * 2 + 2 * 5 + 3 * 8 + 4 * 11,
    +                      1 * 3 + 2 * 6 + 3 * 9 + 4 * 12],
    +  [5 * 1 + 6 * 4 + 7 * 7 + 8 * 10,
    +   5 * 2 + 6 * 5 + 7 * 8 + 8 * 11,
    +   5 * 3 + 6 * 6 + 7 * 9 + 8 * 12]],
    +  m3.multiply(m4).toArray());
    +  assertThrows('Matrix dimensions should not line up.',
    +               function() { m4.multiply(m3); });
    +}
    +
    +function testMatrixMultiplicationIsAssociative() {
    +  var A = new goog.math.Matrix([[1, 2], [3, 4]]);
    +  var B = new goog.math.Matrix([[3, 4], [5, 6]]);
    +  var C = new goog.math.Matrix([[2, 7], [9, 1]]);
    +
    +  assertArrayEquals('A(BC) == (AB)C',
    +                    A.multiply(B.multiply(C)).toArray(),
    +                    A.multiply(B).multiply(C).toArray());
    +}
    +
    +
    +function testMatrixMultiplicationIsDistributive() {
    +  var A = new goog.math.Matrix([[1, 2], [3, 4]]);
    +  var B = new goog.math.Matrix([[3, 4], [5, 6]]);
    +  var C = new goog.math.Matrix([[2, 7], [9, 1]]);
    +
    +  assertArrayEquals('A(B + C) = AB + AC',
    +                    A.multiply(B.add(C)).toArray(),
    +                    A.multiply(B).add(A.multiply(C)).toArray());
    +
    +  assertArrayEquals('(A + B)C = AC + BC',
    +                    A.add(B).multiply(C).toArray(),
    +                    A.multiply(C).add(B.multiply(C)).toArray());
    +}
    +
    +
    +function testTranspose() {
    +  var m = new goog.math.Matrix([[1, 3, 1], [0, -6, 0]]);
    +  var t = [[1, 0], [3, -6], [1, 0]];
    +  assertArrayEquals(t, m.getTranspose().toArray());
    +}
    +
    +
    +function testAppendColumns() {
    +  var m = new goog.math.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
    +  var b = new goog.math.Matrix([[4], [3], [1]]);
    +  var result = [[1, 3, 2, 4], [2, 0, 1, 3], [5, 2, 2, 1]];
    +  assertArrayEquals(result, m.appendColumns(b).toArray());
    +}
    +
    +
    +function testAppendRows() {
    +  var m = new goog.math.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
    +  var b = new goog.math.Matrix([[4, 3, 1]]);
    +  var result = [[1, 3, 2], [2, 0, 1], [5, 2, 2], [4, 3, 1]];
    +  assertArrayEquals(result, m.appendRows(b).toArray());
    +}
    +
    +
    +function testSubmatrixByDeletion() {
    +  var m = new goog.math.Matrix([[1, 2, 3, 4], [5, 6, 7, 8],
    +        [9, 10, 11, 12], [13, 14, 15, 16]]);
    +  var arr = [[1, 2, 3], [5, 6, 7], [13, 14, 15]];
    +  assertArrayEquals(arr, m.getSubmatrixByDeletion_(2, 3).toArray());
    +}
    +
    +
    +function testMinor() {
    +  var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  assertEquals(-3, m.getMinor_(0, 0));
    +}
    +
    +
    +function testCofactor() {
    +  var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  assertEquals(6, m.getCofactor_(0, 1));
    +}
    +
    +
    +function testDeterminantForOneByOneMatrix() {
    +  var m = new goog.math.Matrix([[3]]);
    +  assertEquals(3, m.getDeterminant());
    +}
    +
    +
    +function testDeterminant() {
    +  var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
    +  assertEquals(0, m.getDeterminant());
    +}
    +
    +
    +function testGetSubmatrix() {
    +  var m = new goog.math.Matrix([[2, -1, 0, 1, 0, 0],
    +                                [-1, 2, -1, 0, 1, 0],
    +                                [0, -1, 2, 0, 0, 1]]);
    +  var sub1 = [[2, -1, 0], [-1, 2, -1], [0, -1, 2]];
    +  assertArrayEquals(sub1,
    +                    m.getSubmatrixByCoordinates_(0, 0, 2, 2).toArray());
    +
    +  var sub2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
    +  assertArrayEquals(sub2, m.getSubmatrixByCoordinates_(0, 3).toArray());
    +}
    +
    +
    +function testGetReducedRowEchelonForm() {
    +  var m = new goog.math.Matrix([[2, -1, 0, 1, 0, 0],
    +                                [-1, 2, -1, 0, 1, 0],
    +                                [0, -1, 2, 0, 0, 1]]);
    +
    +  var expected = new goog.math.Matrix([[1, 0, 0, .75, .5, .25],
    +                                       [0, 1, 0, .5, 1, .5],
    +                                       [0, 0, 1, .25, .5, .75]]);
    +
    +  assertTrue(expected.equals(m.getReducedRowEchelonForm()));
    +}
    +
    +
    +function testInverse() {
    +  var m1 = new goog.math.Matrix([[2, -1, 0],
    +                                 [-1, 2, -1],
    +                                 [0, -1, 2]]);
    +  var expected1 = new goog.math.Matrix([[.75, .5, .25],
    +                                        [.5, 1, .5],
    +                                        [.25, .5, .75]]);
    +  assertTrue(expected1.equals(m1.getInverse()));
    +
    +  var m2 = new goog.math.Matrix([[4, 8],
    +                                 [7, -2]]);
    +  var expected2 = new goog.math.Matrix([[.03125, .125],
    +                                        [.10936, -.0625]]);
    +  assertTrue(expected2.equals(m2.getInverse(), .0001));
    +  var m3 = new goog.math.Matrix([[0, 0],
    +                                 [0, 0]]);
    +  assertNull(m3.getInverse());
    +  var m4 = new goog.math.Matrix([[2]]);
    +  var expected4 = new goog.math.Matrix([[.5]]);
    +  assertTrue(expected4.equals(m4.getInverse(), .0001));
    +  var m5 = new goog.math.Matrix([[0]]);
    +  assertNull(m5.getInverse());
    +}
    +
    +
    +function testEquals() {
    +  var a1 = new goog.math.Matrix([[1, 0, 0, .75, .5, .25],
    +                                 [0, 1, 0, .5, 1, .5],
    +                                 [0, 0, 1, .25, .5, .75]]);
    +
    +  var a2 = new goog.math.Matrix([[1, 0, 0, .75, .5, .25],
    +                                 [0, 1, 0, .5, 1, .5],
    +                                 [0, 0, 1, .25, .5, .75]]);
    +
    +  var a3 = new goog.math.Matrix([[1, 0, 0, .749, .5, .25],
    +                                 [0, 1, 0, .5, 1, .5],
    +                                 [0, 0, 1, .25, .5, .75]]);
    +
    +  assertTrue(a1.equals(a2));
    +  assertTrue(a1.equals(a3, .01));
    +  assertFalse(a1.equals(a3, .001));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/path.js b/mapbox-gl/closure-library/closure/goog/math/path.js
    new file mode 100644
    index 0000000000..3eae7b0bf2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/path.js
    @@ -0,0 +1,598 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Represents a path used with a Graphics implementation.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.math.Path');
    +goog.provide('goog.math.Path.Segment');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Creates a path object. A path is a sequence of segments and may be open or
    + * closed. Path uses the EVEN-ODD fill rule for determining the interior of the
    + * path. A path must start with a moveTo command.
    + *
    + * A "simple" path does not contain any arcs and may be transformed using
    + * the {@code transform} method.
    + *
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.Path = function() {
    +  /**
    +   * The segment types that constitute this path.
    +   * @private {!Array<goog.math.Path.Segment>}
    +   */
    +  this.segments_ = [];
    +
    +  /**
    +   * The number of repeated segments of the current type.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.count_ = [];
    +
    +  /**
    +   * The arguments corresponding to each of the segments.
    +   * @type {!Array<number>}
    +   * @private
    +   */
    +  this.arguments_ = [];
    +
    +  /**
    +   * The coordinates of the point which closes the path (the point of the
    +   * last moveTo command).
    +   * @type {Array<number>?}
    +   * @private
    +   */
    +  this.closePoint_ = null;
    +
    +  /**
    +   * The coordinates most recently added to the end of the path.
    +   * @type {Array<number>?}
    +   * @private
    +   */
    +  this.currentPoint_ = null;
    +
    +  /**
    +   * Flag for whether this is a simple path (contains no arc segments).
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.simple_ = true;
    +};
    +
    +
    +/**
    + * Path segment types.
    + * @enum {number}
    + */
    +goog.math.Path.Segment = {
    +  MOVETO: 0,
    +  LINETO: 1,
    +  CURVETO: 2,
    +  ARCTO: 3,
    +  CLOSE: 4
    +};
    +
    +
    +/**
    + * The number of points for each segment type.
    + * @type {!Array<number>}
    + * @private
    + */
    +goog.math.Path.segmentArgCounts_ = (function() {
    +  var counts = [];
    +  counts[goog.math.Path.Segment.MOVETO] = 2;
    +  counts[goog.math.Path.Segment.LINETO] = 2;
    +  counts[goog.math.Path.Segment.CURVETO] = 6;
    +  counts[goog.math.Path.Segment.ARCTO] = 6;
    +  counts[goog.math.Path.Segment.CLOSE] = 0;
    +  return counts;
    +})();
    +
    +
    +/**
    + * Returns an array of the segment types in this path, in the order of their
    + * appearance. Adjacent segments of the same type are collapsed into a single
    + * entry in the array. The returned array is a copy; modifications are not
    + * reflected in the Path object.
    + * @return {!Array<number>}
    + */
    +goog.math.Path.prototype.getSegmentTypes = function() {
    +  return this.segments_.concat();
    +};
    +
    +
    +/**
    + * Returns an array of the number of times each segment type repeats in this
    + * path, in order. The returned array is a copy; modifications are not reflected
    + * in the Path object.
    + * @return {!Array<number>}
    + */
    +goog.math.Path.prototype.getSegmentCounts = function() {
    +  return this.count_.concat();
    +};
    +
    +
    +/**
    + * Returns an array of all arguments for the segments of this path object, in
    + * order. The returned array is a copy; modifications are not reflected in the
    + * Path object.
    + * @return {!Array<number>}
    + */
    +goog.math.Path.prototype.getSegmentArgs = function() {
    +  return this.arguments_.concat();
    +};
    +
    +
    +/**
    + * Returns the number of points for a segment type.
    + *
    + * @param {number} segment The segment type.
    + * @return {number} The number of points.
    + */
    +goog.math.Path.getSegmentCount = function(segment) {
    +  return goog.math.Path.segmentArgCounts_[segment];
    +};
    +
    +
    +/**
    + * Appends another path to the end of this path.
    + *
    + * @param {!goog.math.Path} path The path to append.
    + * @return {!goog.math.Path} This path.
    + */
    +goog.math.Path.prototype.appendPath = function(path) {
    +  if (path.currentPoint_) {
    +    Array.prototype.push.apply(this.segments_, path.segments_);
    +    Array.prototype.push.apply(this.count_, path.count_);
    +    Array.prototype.push.apply(this.arguments_, path.arguments_);
    +    this.currentPoint_ = path.currentPoint_.concat();
    +    this.closePoint_ = path.closePoint_.concat();
    +    this.simple_ = this.simple_ && path.simple_;
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Clears the path.
    + *
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.clear = function() {
    +  this.segments_.length = 0;
    +  this.count_.length = 0;
    +  this.arguments_.length = 0;
    +  this.closePoint_ = null;
    +  this.currentPoint_ = null;
    +  this.simple_ = true;
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a point to the path by moving to the specified point. Repeated moveTo
    + * commands are collapsed into a single moveTo.
    + *
    + * @param {number} x X coordinate of destination point.
    + * @param {number} y Y coordinate of destination point.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.moveTo = function(x, y) {
    +  if (goog.array.peek(this.segments_) == goog.math.Path.Segment.MOVETO) {
    +    this.arguments_.length -= 2;
    +  } else {
    +    this.segments_.push(goog.math.Path.Segment.MOVETO);
    +    this.count_.push(1);
    +  }
    +  this.arguments_.push(x, y);
    +  this.currentPoint_ = this.closePoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing a straight line to each point.
    + *
    + * @param {...number} var_args The coordinates of each destination point as x, y
    + *     value pairs.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.lineTo = function(var_args) {
    +  return this.lineTo_(arguments);
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing a straight line to each point.
    + *
    + * @param {!Array<number>} coordinates The coordinates of each
    + *     destination point as x, y value pairs.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.lineToFromArray = function(coordinates) {
    +  return this.lineTo_(coordinates);
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing a straight line to each point.
    + *
    + * @param {!Array<number>|Arguments} coordinates The coordinates of each
    + *     destination point as x, y value pairs.
    + * @return {!goog.math.Path} The path itself.
    + * @private
    + */
    +goog.math.Path.prototype.lineTo_ = function(coordinates) {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with lineTo');
    +  }
    +  if (lastSegment != goog.math.Path.Segment.LINETO) {
    +    this.segments_.push(goog.math.Path.Segment.LINETO);
    +    this.count_.push(0);
    +  }
    +  for (var i = 0; i < coordinates.length; i += 2) {
    +    var x = coordinates[i];
    +    var y = coordinates[i + 1];
    +    this.arguments_.push(x, y);
    +  }
    +  this.count_[this.count_.length - 1] += i / 2;
    +  this.currentPoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing cubic Bezier curves. Each curve is
    + * specified using 3 points (6 coordinates) - two control points and the end
    + * point of the curve.
    + *
    + * @param {...number} var_args The coordinates specifiying each curve in sets of
    + *     6 points: {@code [x1, y1]} the first control point, {@code [x2, y2]} the
    + *     second control point and {@code [x, y]} the end point.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.curveTo = function(var_args) {
    +  return this.curveTo_(arguments);
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing cubic Bezier curves. Each curve is
    + * specified using 3 points (6 coordinates) - two control points and the end
    + * point of the curve.
    + *
    + * @param {!Array<number>} coordinates The coordinates specifiying
    + *     each curve in sets of 6 points: {@code [x1, y1]} the first control point,
    + *     {@code [x2, y2]} the second control point and {@code [x, y]} the end
    + *     point.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.curveToFromArray = function(coordinates) {
    +  return this.curveTo_(coordinates);
    +};
    +
    +
    +/**
    + * Adds points to the path by drawing cubic Bezier curves. Each curve is
    + * specified using 3 points (6 coordinates) - two control points and the end
    + * point of the curve.
    + *
    + * @param {!Array<number>|Arguments} coordinates The coordinates specifiying
    + *     each curve in sets of 6 points: {@code [x1, y1]} the first control point,
    + *     {@code [x2, y2]} the second control point and {@code [x, y]} the end
    + *     point.
    + * @return {!goog.math.Path} The path itself.
    + * @private
    + */
    +goog.math.Path.prototype.curveTo_ = function(coordinates) {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with curve');
    +  }
    +  if (lastSegment != goog.math.Path.Segment.CURVETO) {
    +    this.segments_.push(goog.math.Path.Segment.CURVETO);
    +    this.count_.push(0);
    +  }
    +  for (var i = 0; i < coordinates.length; i += 6) {
    +    var x = coordinates[i + 4];
    +    var y = coordinates[i + 5];
    +    this.arguments_.push(coordinates[i], coordinates[i + 1],
    +        coordinates[i + 2], coordinates[i + 3], x, y);
    +  }
    +  this.count_[this.count_.length - 1] += i / 6;
    +  this.currentPoint_ = [x, y];
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a path command to close the path by connecting the
    + * last point to the first point.
    + *
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.close = function() {
    +  var lastSegment = goog.array.peek(this.segments_);
    +  if (lastSegment == null) {
    +    throw Error('Path cannot start with close');
    +  }
    +  if (lastSegment != goog.math.Path.Segment.CLOSE) {
    +    this.segments_.push(goog.math.Path.Segment.CLOSE);
    +    this.count_.push(1);
    +    this.currentPoint_ = this.closePoint_;
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a path command to draw an arc centered at the point {@code (cx, cy)}
    + * with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
    + * {@code startAngle} through {@code extent} degrees. Positive rotation is in
    + * the direction from positive x-axis to positive y-axis.
    + *
    + * @param {number} cx X coordinate of center of ellipse.
    + * @param {number} cy Y coordinate of center of ellipse.
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @param {boolean} connect If true, the starting point of the arc is connected
    + *     to the current point.
    + * @return {!goog.math.Path} The path itself.
    + * @deprecated Use {@code arcTo} or {@code arcToAsCurves} instead.
    + */
    +goog.math.Path.prototype.arc = function(cx, cy, rx, ry,
    +    fromAngle, extent, connect) {
    +  var startX = cx + goog.math.angleDx(fromAngle, rx);
    +  var startY = cy + goog.math.angleDy(fromAngle, ry);
    +  if (connect) {
    +    if (!this.currentPoint_ || startX != this.currentPoint_[0] ||
    +        startY != this.currentPoint_[1]) {
    +      this.lineTo(startX, startY);
    +    }
    +  } else {
    +    this.moveTo(startX, startY);
    +  }
    +  return this.arcTo(rx, ry, fromAngle, extent);
    +};
    +
    +
    +/**
    + * Adds a path command to draw an arc starting at the path's current point,
    + * with radius {@code rx} along the x-axis and {@code ry} along the y-axis from
    + * {@code startAngle} through {@code extent} degrees. Positive rotation is in
    + * the direction from positive x-axis to positive y-axis.
    + *
    + * This method makes the path non-simple.
    + *
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.arcTo = function(rx, ry, fromAngle, extent) {
    +  var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
    +  var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
    +  var ex = cx + goog.math.angleDx(fromAngle + extent, rx);
    +  var ey = cy + goog.math.angleDy(fromAngle + extent, ry);
    +  this.segments_.push(goog.math.Path.Segment.ARCTO);
    +  this.count_.push(1);
    +  this.arguments_.push(rx, ry, fromAngle, extent, ex, ey);
    +  this.simple_ = false;
    +  this.currentPoint_ = [ex, ey];
    +  return this;
    +};
    +
    +
    +/**
    + * Same as {@code arcTo}, but approximates the arc using bezier curves.
    +.* As a result, this method does not affect the simplified status of this path.
    + * The algorithm is adapted from {@code java.awt.geom.ArcIterator}.
    + *
    + * @param {number} rx Radius of ellipse on x axis.
    + * @param {number} ry Radius of ellipse on y axis.
    + * @param {number} fromAngle Starting angle measured in degrees from the
    + *     positive x-axis.
    + * @param {number} extent The span of the arc in degrees.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.arcToAsCurves = function(
    +    rx, ry, fromAngle, extent) {
    +  var cx = this.currentPoint_[0] - goog.math.angleDx(fromAngle, rx);
    +  var cy = this.currentPoint_[1] - goog.math.angleDy(fromAngle, ry);
    +  var extentRad = goog.math.toRadians(extent);
    +  var arcSegs = Math.ceil(Math.abs(extentRad) / Math.PI * 2);
    +  var inc = extentRad / arcSegs;
    +  var angle = goog.math.toRadians(fromAngle);
    +  for (var j = 0; j < arcSegs; j++) {
    +    var relX = Math.cos(angle);
    +    var relY = Math.sin(angle);
    +    var z = 4 / 3 * Math.sin(inc / 2) / (1 + Math.cos(inc / 2));
    +    var c0 = cx + (relX - z * relY) * rx;
    +    var c1 = cy + (relY + z * relX) * ry;
    +    angle += inc;
    +    relX = Math.cos(angle);
    +    relY = Math.sin(angle);
    +    this.curveTo(c0, c1,
    +        cx + (relX + z * relY) * rx,
    +        cy + (relY - z * relX) * ry,
    +        cx + relX * rx,
    +        cy + relY * ry);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Iterates over the path calling the supplied callback once for each path
    + * segment. The arguments to the callback function are the segment type and
    + * an array of its arguments.
    + *
    + * The {@code LINETO} and {@code CURVETO} arrays can contain multiple
    + * segments of the same type. The number of segments is the length of the
    + * array divided by the segment length (2 for lines, 6 for  curves).
    + *
    + * As a convenience the {@code ARCTO} segment also includes the end point as the
    + * last two arguments: {@code rx, ry, fromAngle, extent, x, y}.
    + *
    + * @param {function(!goog.math.Path.Segment, !Array<number>)} callback
    + *     The function to call with each path segment.
    + */
    +goog.math.Path.prototype.forEachSegment = function(callback) {
    +  var points = this.arguments_;
    +  var index = 0;
    +  for (var i = 0, length = this.segments_.length; i < length; i++) {
    +    var seg = this.segments_[i];
    +    var n = goog.math.Path.segmentArgCounts_[seg] * this.count_[i];
    +    callback(seg, points.slice(index, index + n));
    +    index += n;
    +  }
    +};
    +
    +
    +/**
    + * Returns the coordinates most recently added to the end of the path.
    + *
    + * @return {Array<number>?} An array containing the ending coordinates of the
    + *     path of the form {@code [x, y]}.
    + */
    +goog.math.Path.prototype.getCurrentPoint = function() {
    +  return this.currentPoint_ && this.currentPoint_.concat();
    +};
    +
    +
    +/**
    + * @return {!goog.math.Path} A copy of this path.
    + */
    +goog.math.Path.prototype.clone = function() {
    +  var path = new goog.math.Path();
    +  path.segments_ = this.segments_.concat();
    +  path.count_ = this.count_.concat();
    +  path.arguments_ = this.arguments_.concat();
    +  path.closePoint_ = this.closePoint_ && this.closePoint_.concat();
    +  path.currentPoint_ = this.currentPoint_ && this.currentPoint_.concat();
    +  path.simple_ = this.simple_;
    +  return path;
    +};
    +
    +
    +/**
    + * Returns true if this path contains no arcs. Simplified paths can be
    + * created using {@code createSimplifiedPath}.
    + *
    + * @return {boolean} True if the path contains no arcs.
    + */
    +goog.math.Path.prototype.isSimple = function() {
    +  return this.simple_;
    +};
    +
    +
    +/**
    + * A map from segment type to the path function to call to simplify a path.
    + * @private {!Object<goog.math.Path.Segment, function(this: goog.math.Path)>}
    + */
    +goog.math.Path.simplifySegmentMap_ = (function() {
    +  var map = {};
    +  map[goog.math.Path.Segment.MOVETO] = goog.math.Path.prototype.moveTo;
    +  map[goog.math.Path.Segment.LINETO] = goog.math.Path.prototype.lineTo;
    +  map[goog.math.Path.Segment.CLOSE] = goog.math.Path.prototype.close;
    +  map[goog.math.Path.Segment.CURVETO] =
    +      goog.math.Path.prototype.curveTo;
    +  map[goog.math.Path.Segment.ARCTO] =
    +      goog.math.Path.prototype.arcToAsCurves;
    +  return map;
    +})();
    +
    +
    +/**
    + * Creates a copy of the given path, replacing {@code arcTo} with
    + * {@code arcToAsCurves}. The resulting path is simplified and can
    + * be transformed.
    + *
    + * @param {!goog.math.Path} src The path to simplify.
    + * @return {!goog.math.Path} A new simplified path.
    + */
    +goog.math.Path.createSimplifiedPath = function(src) {
    +  if (src.isSimple()) {
    +    return src.clone();
    +  }
    +  var path = new goog.math.Path();
    +  src.forEachSegment(function(segment, args) {
    +    goog.math.Path.simplifySegmentMap_[segment].apply(path, args);
    +  });
    +  return path;
    +};
    +
    +
    +// TODO(chrisn): Delete this method
    +/**
    + * Creates a transformed copy of this path. The path is simplified
    + * {@see #createSimplifiedPath} prior to transformation.
    + *
    + * @param {!goog.math.AffineTransform} tx The transformation to perform.
    + * @return {!goog.math.Path} A new, transformed path.
    + */
    +goog.math.Path.prototype.createTransformedPath = function(tx) {
    +  var path = goog.math.Path.createSimplifiedPath(this);
    +  path.transform(tx);
    +  return path;
    +};
    +
    +
    +/**
    + * Transforms the path. Only simple paths are transformable. Attempting
    + * to transform a non-simple path will throw an error.
    + *
    + * @param {!goog.math.AffineTransform} tx The transformation to perform.
    + * @return {!goog.math.Path} The path itself.
    + */
    +goog.math.Path.prototype.transform = function(tx) {
    +  if (!this.isSimple()) {
    +    throw Error('Non-simple path');
    +  }
    +  tx.transform(this.arguments_, 0, this.arguments_, 0,
    +      this.arguments_.length / 2);
    +  if (this.closePoint_) {
    +    tx.transform(this.closePoint_, 0, this.closePoint_, 0, 1);
    +  }
    +  if (this.currentPoint_ && this.closePoint_ != this.currentPoint_) {
    +    tx.transform(this.currentPoint_, 0, this.currentPoint_, 0, 1);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the path is empty.
    + */
    +goog.math.Path.prototype.isEmpty = function() {
    +  return this.segments_.length == 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/path_test.html b/mapbox-gl/closure-library/closure/goog/math/path_test.html
    new file mode 100644
    index 0000000000..9370915c52
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/path_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.math.Path</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.math.PathTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/path_test.js b/mapbox-gl/closure-library/closure/goog/math/path_test.js
    new file mode 100644
    index 0000000000..830f5d9dd9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/path_test.js
    @@ -0,0 +1,518 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.PathTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.math.AffineTransform');
    +goog.require('goog.math.Path');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.math.PathTest');
    +
    +
    +/**
    + * Array mapping numeric segment constant to a descriptive character.
    + * @type {!Array<string>}
    + * @private
    + */
    +var SEGMENT_NAMES_ = function() {
    +  var arr = [];
    +  arr[goog.math.Path.Segment.MOVETO] = 'M';
    +  arr[goog.math.Path.Segment.LINETO] = 'L';
    +  arr[goog.math.Path.Segment.CURVETO] = 'C';
    +  arr[goog.math.Path.Segment.ARCTO] = 'A';
    +  arr[goog.math.Path.Segment.CLOSE] = 'X';
    +  return arr;
    +}();
    +
    +
    +/**
    + * Test if the given path matches the expected array of commands and parameters.
    + * @param {Array<string|number>} expected The expected array of commands and
    + *     parameters.
    + * @param {goog.math.Path} path The path to test against.
    + */
    +var assertPathEquals = function(expected, path) {
    +  var actual = [];
    +  path.forEachSegment(function(seg, args) {
    +    actual.push(SEGMENT_NAMES_[seg]);
    +    Array.prototype.push.apply(actual, args);
    +  });
    +  assertEquals(expected.length, actual.length);
    +  for (var i = 0; i < expected.length; i++) {
    +    if (goog.isNumber(expected[i])) {
    +      assertTrue(goog.isNumber(actual[i]));
    +      assertRoughlyEquals(expected[i], actual[i], 0.01);
    +    } else {
    +      assertEquals(expected[i], actual[i]);
    +    }
    +  }
    +};
    +
    +
    +function testConstructor() {
    +  var path = new goog.math.Path();
    +  assertTrue(path.isSimple());
    +  assertNull(path.getCurrentPoint());
    +  assertPathEquals([], path);
    +}
    +
    +
    +function testGetSegmentCount() {
    +  assertArrayEquals([2, 2, 6, 6, 0], goog.array.map([
    +    goog.math.Path.Segment.MOVETO,
    +    goog.math.Path.Segment.LINETO,
    +    goog.math.Path.Segment.CURVETO,
    +    goog.math.Path.Segment.ARCTO,
    +    goog.math.Path.Segment.CLOSE
    +  ], goog.math.Path.getSegmentCount));
    +}
    +
    +
    +function testSimpleMoveTo() {
    +  var path = new goog.math.Path();
    +  path.moveTo(30, 50);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([30, 50], path.getCurrentPoint());
    +  assertPathEquals(['M', 30, 50], path);
    +}
    +
    +
    +function testRepeatedMoveTo() {
    +  var path = new goog.math.Path();
    +  path.moveTo(30, 50);
    +  path.moveTo(40, 60);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([40, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 40, 60], path);
    +}
    +
    +
    +function testSimpleLineTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  var e = assertThrows(function() {
    +    path.lineTo(30, 50);
    +  });
    +  assertEquals('Path cannot start with lineTo', e.message);
    +  path.moveTo(0, 0);
    +  path.lineTo(30, 50);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([30, 50], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50], path);
    +}
    +
    +
    +function testSimpleLineTo_fromArray() {
    +  var path = new goog.math.Path();
    +  var e = assertThrows(function() {
    +    path.lineToFromArray([30, 50]);
    +  });
    +  assertEquals('Path cannot start with lineTo', e.message);
    +  path.moveTo(0, 0);
    +  path.lineToFromArray([30, 50]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([30, 50], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50], path);
    +}
    +
    +
    +function testMultiArgLineTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(30, 50, 40 , 60);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([40, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
    +}
    +
    +
    +function testMultiArgLineTo_fromArray() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineToFromArray([30, 50, 40 , 60]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([40, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
    +}
    +
    +
    +function testRepeatedLineTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(30, 50);
    +  path.lineTo(40, 60);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([40, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
    +}
    +
    +
    +function testRepeatedLineTo_fromArray() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineToFromArray([30, 50]);
    +  path.lineToFromArray([40, 60]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([40, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
    +}
    +
    +
    +function testSimpleCurveTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  var e = assertThrows(function() {
    +    path.curveTo(10, 20, 30, 40, 50, 60);
    +  });
    +  assertEquals('Path cannot start with curve', e.message);
    +  path.moveTo(0, 0);
    +  path.curveTo(10, 20, 30, 40, 50, 60);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([50, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60], path);
    +}
    +
    +
    +function testSimpleCurveTo_fromArray() {
    +  var path = new goog.math.Path();
    +  var e = assertThrows(function() {
    +    path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    +  });
    +  assertEquals('Path cannot start with curve', e.message);
    +  path.moveTo(0, 0);
    +  path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([50, 60], path.getCurrentPoint());
    +  assertPathEquals(['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60], path);
    +}
    +
    +
    +function testMultiCurveTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.curveTo(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([110, 120], path.getCurrentPoint());
    +  assertPathEquals(
    +      ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +      path);
    +}
    +
    +
    +function testMultiCurveTo_fromArray() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.curveToFromArray([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([110, 120], path.getCurrentPoint());
    +  assertPathEquals(
    +      ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +      path);
    +}
    +
    +
    +function testRepeatedCurveTo_fromArgs() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.curveTo(10, 20, 30, 40, 50, 60);
    +  path.curveTo(70, 80, 90, 100, 110, 120);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([110, 120], path.getCurrentPoint());
    +  assertPathEquals(
    +      ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +      path);
    +}
    +
    +
    +function testRepeatedCurveTo_fromArray() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    +  path.curveToFromArray([70, 80, 90, 100, 110, 120]);
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([110, 120], path.getCurrentPoint());
    +  assertPathEquals(
    +      ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
    +      path);
    +}
    +
    +
    +function testSimpleArc() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  assertFalse(path.isSimple());
    +  var p = path.getCurrentPoint();
    +  assertEquals(55, p[0]);
    +  assertRoughlyEquals(77.32, p[1], 0.01);
    +  assertPathEquals(
    +      ['M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32], path);
    +}
    +
    +
    +function testArcNonConnectClose() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.arc(10, 10, 10, 10, -90, 180, false);
    +  assertObjectEquals([10, 20], path.getCurrentPoint());
    +  path.close();
    +  assertObjectEquals([10, 0], path.getCurrentPoint());
    +}
    +
    +
    +function testRepeatedArc() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  path.arc(50, 60, 10, 20, 60, 30, false);
    +  assertFalse(path.isSimple());
    +  assertObjectEquals([50, 80], path.getCurrentPoint());
    +  assertPathEquals(['M', 58.66, 70,
    +    'A', 10, 20, 30, 30, 55, 77.32,
    +    'M', 55, 77.32,
    +    'A', 10, 20, 60, 30, 50, 80], path);
    +}
    +
    +
    +function testRepeatedArc2() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  path.arc(50, 60, 10, 20, 60, 30, true);
    +  assertPathEquals(['M', 58.66, 70,
    +    'A', 10, 20, 30, 30, 55, 77.32,
    +    'A', 10, 20, 60, 30, 50, 80], path);
    +}
    +
    +
    +function testCompleteCircle() {
    +  var path = new goog.math.Path();
    +  path.arc(0, 0, 10, 10, 0, 360, false);
    +  assertFalse(path.isSimple());
    +  var p = path.getCurrentPoint();
    +  assertRoughlyEquals(10, p[0], 0.01);
    +  assertRoughlyEquals(0, p[1], 0.01);
    +  assertPathEquals(
    +      ['M', 10, 0, 'A', 10, 10, 0, 360, 10, 0], path);
    +}
    +
    +
    +function testClose() {
    +  var path = new goog.math.Path();
    +  assertThrows('Path cannot start with close',
    +      function() {
    +        path.close();
    +      });
    +
    +  path.moveTo(0, 0);
    +  path.lineTo(10, 20, 30, 40, 50, 60);
    +  path.close();
    +  assertTrue(path.isSimple());
    +  assertObjectEquals([0, 0], path.getCurrentPoint());
    +  assertPathEquals(
    +      ['M', 0, 0, 'L', 10, 20, 30, 40, 50, 60, 'X'], path);
    +}
    +
    +
    +function testClear() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  path.clear();
    +  assertTrue(path.isSimple());
    +  assertNull(path.getCurrentPoint());
    +  assertPathEquals([], path);
    +}
    +
    +
    +function testCreateSimplifiedPath() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  assertFalse(path.isSimple());
    +  path = goog.math.Path.createSimplifiedPath(path);
    +  assertTrue(path.isSimple());
    +  var p = path.getCurrentPoint();
    +  assertEquals(55, p[0]);
    +  assertRoughlyEquals(77.32, p[1], 0.01);
    +  assertPathEquals(['M', 58.66, 70,
    +    'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
    +}
    +
    +
    +function testCreateSimplifiedPath2() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  path.arc(50, 60, 10, 20, 60, 30, false);
    +  assertFalse(path.isSimple());
    +  path = goog.math.Path.createSimplifiedPath(path);
    +  assertTrue(path.isSimple());
    +  assertPathEquals(['M', 58.66, 70,
    +    'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32,
    +    'M', 55, 77.32,
    +    'C', 53.48, 79.08, 51.76, 80, 50, 80], path);
    +}
    +
    +
    +function testCreateSimplifiedPath3() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  path.arc(50, 60, 10, 20, 60, 30, true);
    +  path.close();
    +  path = goog.math.Path.createSimplifiedPath(path);
    +  assertPathEquals(['M', 58.66, 70,
    +    'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32,
    +    53.48, 79.08, 51.76, 80, 50, 80, 'X'], path);
    +  var p = path.getCurrentPoint();
    +  assertRoughlyEquals(58.66, p[0], 0.01);
    +  assertRoughlyEquals(70, p[1], 0.01);
    +}
    +
    +
    +function testArcToAsCurves() {
    +  var path = new goog.math.Path();
    +  path.moveTo(58.66, 70);
    +  path.arcToAsCurves(10, 20, 30, 30);
    +  assertPathEquals(['M', 58.66, 70,
    +    'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
    +}
    +
    +
    +function testCreateTransformedPath() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(0, 10, 10, 10, 10, 0);
    +  path.close();
    +  var tx = new goog.math.AffineTransform(2, 0, 0, 3, 10, 20);
    +  var path2 = path.createTransformedPath(tx);
    +  assertPathEquals(
    +      ['M', 0, 0, 'L', 0, 10, 10, 10, 10, 0, 'X'], path);
    +  assertPathEquals(
    +      ['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
    +}
    +
    +
    +function testTransform() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(0, 10, 10, 10, 10, 0);
    +  path.close();
    +  var tx = new goog.math.AffineTransform(2, 0, 0, 3, 10, 20);
    +  var path2 = path.transform(tx);
    +  assertTrue(path === path2);
    +  assertPathEquals(
    +      ['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
    +}
    +
    +
    +function testTransformCurrentAndClosePoints() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  assertObjectEquals([0, 0], path.getCurrentPoint());
    +  path.transform(new goog.math.AffineTransform(1, 0, 0, 1, 10, 20));
    +  assertObjectEquals([10, 20], path.getCurrentPoint());
    +  path.lineTo(50, 50);
    +  path.close();
    +  assertObjectEquals([10, 20], path.getCurrentPoint());
    +}
    +
    +
    +function testTransformNonSimple() {
    +  var path = new goog.math.Path();
    +  path.arc(50, 60, 10, 20, 30, 30, false);
    +  assertThrows(function() {
    +    path.transform(new goog.math.AffineTransform(1, 0, 0, 1, 10, 20));
    +  });
    +}
    +
    +
    +function testAppendPath() {
    +  var path1 = new goog.math.Path();
    +  path1.moveTo(0, 0);
    +  path1.lineTo(0, 10, 10, 10, 10, 0);
    +  path1.close();
    +
    +  var path2 = new goog.math.Path();
    +  path2.arc(50, 60, 10, 20, 30, 30, false);
    +
    +  assertTrue(path1.isSimple());
    +  path1.appendPath(path2);
    +  assertFalse(path1.isSimple());
    +  assertPathEquals([
    +    'M', 0, 0, 'L', 0, 10, 10, 10, 10, 0, 'X',
    +    'M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32
    +  ], path1);
    +}
    +
    +
    +function testIsEmpty() {
    +  var path = new goog.math.Path();
    +  assertTrue('Initially path is empty', path.isEmpty());
    +
    +  path.moveTo(0, 0);
    +  assertFalse('After command addition, path is not empty', path.isEmpty());
    +
    +  path.clear();
    +  assertTrue('After clear, path is empty again', path.isEmpty());
    +}
    +
    +
    +function testGetSegmentTypes() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(10, 20, 30, 40);
    +  path.close();
    +
    +  var Segment = goog.math.Path.Segment;
    +  var segmentTypes = path.getSegmentTypes();
    +  assertArrayEquals(
    +      'The returned segment types do not match the expected values',
    +      [Segment.MOVETO, Segment.LINETO, Segment.CLOSE], segmentTypes);
    +
    +  segmentTypes[2] = Segment.LINETO;
    +  assertArrayEquals('Modifying the returned segment types changed the path',
    +      [Segment.MOVETO, Segment.LINETO, Segment.CLOSE], path.getSegmentTypes());
    +}
    +
    +
    +function testGetSegmentCounts() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(10, 20, 30, 40);
    +  path.close();
    +
    +  var segmentTypes = path.getSegmentCounts();
    +  assertArrayEquals(
    +      'The returned segment counts do not match the expected values',
    +      [1, 2, 1], segmentTypes);
    +
    +  segmentTypes[1] = 3;
    +  assertArrayEquals('Modifying the returned segment counts changed the path',
    +      [1, 2, 1], path.getSegmentCounts());
    +}
    +
    +
    +function testGetSegmentArgs() {
    +  var path = new goog.math.Path();
    +  path.moveTo(0, 0);
    +  path.lineTo(10, 20, 30, 40);
    +  path.close();
    +
    +  var segmentTypes = path.getSegmentArgs();
    +  assertArrayEquals(
    +      'The returned segment args do not match the expected values',
    +      [0, 0, 10, 20, 30, 40], segmentTypes);
    +
    +  segmentTypes[1] = -10;
    +  assertArrayEquals(
    +      'Modifying the returned segment args changed the path',
    +      [0, 0, 10, 20, 30, 40], path.getSegmentArgs());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/paths.js b/mapbox-gl/closure-library/closure/goog/math/paths.js
    new file mode 100644
    index 0000000000..26c740bbcd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/paths.js
    @@ -0,0 +1,86 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Factories for common path types.
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +
    +goog.provide('goog.math.paths');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Path');
    +
    +
    +/**
    + * Defines a regular n-gon by specifing the center, a vertex, and the total
    + * number of vertices.
    + * @param {goog.math.Coordinate} center The center point.
    + * @param {goog.math.Coordinate} vertex The vertex, which implicitly defines
    + *     a radius as well.
    + * @param {number} n The number of vertices.
    + * @return {!goog.math.Path} The path.
    + */
    +goog.math.paths.createRegularNGon = function(center, vertex, n) {
    +  var path = new goog.math.Path();
    +  path.moveTo(vertex.x, vertex.y);
    +
    +  var startAngle = Math.atan2(vertex.y - center.y, vertex.x - center.x);
    +  var radius = goog.math.Coordinate.distance(center, vertex);
    +  for (var i = 1; i < n; i++) {
    +    var angle = startAngle + 2 * Math.PI * (i / n);
    +    path.lineTo(center.x + radius * Math.cos(angle),
    +                center.y + radius * Math.sin(angle));
    +  }
    +  path.close();
    +  return path;
    +};
    +
    +
    +/**
    + * Defines an arrow.
    + * @param {goog.math.Coordinate} a Point A.
    + * @param {goog.math.Coordinate} b Point B.
    + * @param {?number} aHead The size of the arrow head at point A.
    + *     0 omits the head.
    + * @param {?number} bHead The size of the arrow head at point B.
    + *     0 omits the head.
    + * @return {!goog.math.Path} The path.
    + */
    +goog.math.paths.createArrow = function(a, b, aHead, bHead) {
    +  var path = new goog.math.Path();
    +  path.moveTo(a.x, a.y);
    +  path.lineTo(b.x, b.y);
    +
    +  var angle = Math.atan2(b.y - a.y, b.x - a.x);
    +  if (aHead) {
    +    path.appendPath(
    +        goog.math.paths.createRegularNGon(
    +            new goog.math.Coordinate(
    +                a.x + aHead * Math.cos(angle),
    +                a.y + aHead * Math.sin(angle)),
    +            a, 3));
    +  }
    +  if (bHead) {
    +    path.appendPath(
    +        goog.math.paths.createRegularNGon(
    +            new goog.math.Coordinate(
    +                b.x + bHead * Math.cos(angle + Math.PI),
    +                b.y + bHead * Math.sin(angle + Math.PI)),
    +            b, 3));
    +  }
    +  return path;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/paths_test.html b/mapbox-gl/closure-library/closure/goog/math/paths_test.html
    new file mode 100644
    index 0000000000..3cbc159ff5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/paths_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>JsUnit tests for goog.math.paths</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.math.pathsTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/paths_test.js b/mapbox-gl/closure-library/closure/goog/math/paths_test.js
    new file mode 100644
    index 0000000000..2f8baf30c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/paths_test.js
    @@ -0,0 +1,50 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.math.paths.
    + */
    +
    +goog.provide('goog.math.pathsTest');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.paths');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.math.pathsTest');
    +
    +
    +var regularNGon = goog.math.paths.createRegularNGon;
    +var arrow = goog.math.paths.createArrow;
    +
    +function testSquare() {
    +  var square = regularNGon(
    +      $coord(10, 10), $coord(0, 10), 4);
    +  assertArrayRoughlyEquals(
    +      [0, 10, 10, 0, 20, 10, 10, 20], square.arguments_, 0.05);
    +}
    +
    +function assertArrayRoughlyEquals(expected, actual, delta) {
    +  var message = 'Expected: ' + expected + ', Actual: ' + actual;
    +  assertEquals('Wrong length. ' + message, expected.length, actual.length);
    +  for (var i = 0; i < expected.length; i++) {
    +    assertRoughlyEquals(
    +        'Wrong item at ' + i + '. ' + message,
    +        expected[i], actual[i], delta);
    +  }
    +}
    +
    +function $coord(x, y) {
    +  return new goog.math.Coordinate(x, y);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/range.js b/mapbox-gl/closure-library/closure/goog/math/range.js
    new file mode 100644
    index 0000000000..5812bffe1a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/range.js
    @@ -0,0 +1,186 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing a numeric range.
    + */
    +
    +
    +goog.provide('goog.math.Range');
    +
    +goog.require('goog.asserts');
    +
    +
    +
    +/**
    + * A number range.
    + * @param {number} a One end of the range.
    + * @param {number} b The other end of the range.
    + * @struct
    + * @constructor
    + */
    +goog.math.Range = function(a, b) {
    +  /**
    +   * The lowest value in the range.
    +   * @type {number}
    +   */
    +  this.start = a < b ? a : b;
    +
    +  /**
    +   * The highest value in the range.
    +   * @type {number}
    +   */
    +  this.end = a < b ? b : a;
    +};
    +
    +
    +/**
    + * Creates a goog.math.Range from an array of two numbers.
    + * @param {!Array<number>} pair
    + * @return {!goog.math.Range}
    + */
    +goog.math.Range.fromPair = function(pair) {
    +  goog.asserts.assert(pair.length == 2);
    +  return new goog.math.Range(pair[0], pair[1]);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Range} A clone of this Range.
    + */
    +goog.math.Range.prototype.clone = function() {
    +  return new goog.math.Range(this.start, this.end);
    +};
    +
    +
    +/**
    + * @return {number} Length of the range.
    + */
    +goog.math.Range.prototype.getLength = function() {
    +  return this.end - this.start;
    +};
    +
    +
    +/**
    + * Extends this range to include the given point.
    + * @param {number} point
    + */
    +goog.math.Range.prototype.includePoint = function(point) {
    +  this.start = Math.min(this.start, point);
    +  this.end = Math.max(this.end, point);
    +};
    +
    +
    +/**
    + * Extends this range to include the given range.
    + * @param {!goog.math.Range} range
    + */
    +goog.math.Range.prototype.includeRange = function(range) {
    +  this.start = Math.min(this.start, range.start);
    +  this.end = Math.max(this.end, range.end);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a string representing the range.
    +   * @return {string} In the form [-3.5, 8.13].
    +   * @override
    +   */
    +  goog.math.Range.prototype.toString = function() {
    +    return '[' + this.start + ', ' + this.end + ']';
    +  };
    +}
    +
    +
    +/**
    + * Compares ranges for equality.
    + * @param {goog.math.Range} a A Range.
    + * @param {goog.math.Range} b A Range.
    + * @return {boolean} True iff both the starts and the ends of the ranges are
    + *     equal, or if both ranges are null.
    + */
    +goog.math.Range.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.start == b.start && a.end == b.end;
    +};
    +
    +
    +/**
    + * Given two ranges on the same dimension, this method returns the intersection
    + * of those ranges.
    + * @param {goog.math.Range} a A Range.
    + * @param {goog.math.Range} b A Range.
    + * @return {goog.math.Range} A new Range representing the intersection of two
    + *     ranges, or null if there is no intersection. Ranges are assumed to
    + *     include their end points, and the intersection can be a point.
    + */
    +goog.math.Range.intersection = function(a, b) {
    +  var c0 = Math.max(a.start, b.start);
    +  var c1 = Math.min(a.end, b.end);
    +  return (c0 <= c1) ? new goog.math.Range(c0, c1) : null;
    +};
    +
    +
    +/**
    + * Given two ranges on the same dimension, determines whether they intersect.
    + * @param {goog.math.Range} a A Range.
    + * @param {goog.math.Range} b A Range.
    + * @return {boolean} Whether they intersect.
    + */
    +goog.math.Range.hasIntersection = function(a, b) {
    +  return Math.max(a.start, b.start) <= Math.min(a.end, b.end);
    +};
    +
    +
    +/**
    + * Given two ranges on the same dimension, this returns a range that covers
    + * both ranges.
    + * @param {goog.math.Range} a A Range.
    + * @param {goog.math.Range} b A Range.
    + * @return {!goog.math.Range} A new Range representing the bounding
    + *     range.
    + */
    +goog.math.Range.boundingRange = function(a, b) {
    +  return new goog.math.Range(Math.min(a.start, b.start),
    +                             Math.max(a.end, b.end));
    +};
    +
    +
    +/**
    + * Given two ranges, returns true if the first range completely overlaps the
    + * second.
    + * @param {goog.math.Range} a The first Range.
    + * @param {goog.math.Range} b The second Range.
    + * @return {boolean} True if b is contained inside a, false otherwise.
    + */
    +goog.math.Range.contains = function(a, b) {
    +  return a.start <= b.start && a.end >= b.end;
    +};
    +
    +
    +/**
    + * Given a range and a point, returns true if the range contains the point.
    + * @param {goog.math.Range} range The range.
    + * @param {number} p The point.
    + * @return {boolean} True if p is contained inside range, false otherwise.
    + */
    +goog.math.Range.containsPoint = function(range, p) {
    +  return range.start <= p && range.end >= p;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/range_test.html b/mapbox-gl/closure-library/closure/goog/math/range_test.html
    new file mode 100644
    index 0000000000..3f1c925fc0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/range_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Range
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.math.RangeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/range_test.js b/mapbox-gl/closure-library/closure/goog/math/range_test.js
    new file mode 100644
    index 0000000000..ba771bc880
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/range_test.js
    @@ -0,0 +1,142 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.RangeTest');
    +goog.setTestOnly('goog.math.RangeTest');
    +
    +goog.require('goog.math.Range');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Produce legible assertion results. If two ranges are not equal, the error
    + * message will be of the form
    + * "Expected <[1, 2]> (Object) but was <[3, 4]> (Object)"
    + */
    +function assertRangesEqual(expected, actual) {
    +  if (!goog.math.Range.equals(expected, actual)) {
    +    assertEquals(expected, actual);
    +  }
    +}
    +
    +function createRange(a) {
    +  return a ? new goog.math.Range(a[0], a[1]) : null;
    +}
    +
    +function testFromPair() {
    +  var range = goog.math.Range.fromPair([1, 2]);
    +  assertEquals(1, range.start);
    +  assertEquals(2, range.end);
    +  range = goog.math.Range.fromPair([2, 1]);
    +  assertEquals(1, range.start);
    +  assertEquals(2, range.end);
    +}
    +
    +function testRangeIntersection() {
    +  var tests = [[[1, 2], [3, 4], null],
    +               [[1, 3], [2, 4], [2, 3]],
    +               [[1, 4], [2, 3], [2, 3]],
    +               [[-1, 2], [-1, 2], [-1, 2]],
    +               [[1, 2], [2, 3], [2, 2]],
    +               [[1, 1], [1, 1], [1, 1]]];
    +  for (var i = 0; i < tests.length; ++i) {
    +    var t = tests[i];
    +    var r0 = createRange(t[0]);
    +    var r1 = createRange(t[1]);
    +    var expected = createRange(t[2]);
    +    assertRangesEqual(expected, goog.math.Range.intersection(r0, r1));
    +    assertRangesEqual(expected, goog.math.Range.intersection(r1, r0));
    +
    +    assertEquals(expected != null, goog.math.Range.hasIntersection(r0, r1));
    +    assertEquals(expected != null, goog.math.Range.hasIntersection(r1, r0));
    +  }
    +}
    +
    +function testBoundingRange() {
    +  var tests = [[[1, 2], [3, 4], [1, 4]],
    +               [[1, 3], [2, 4], [1, 4]],
    +               [[1, 4], [2, 3], [1, 4]],
    +               [[-1, 2], [-1, 2], [-1, 2]],
    +               [[1, 2], [2, 3], [1, 3]],
    +               [[1, 1], [1, 1], [1, 1]]];
    +  for (var i = 0; i < tests.length; ++i) {
    +    var t = tests[i];
    +    var r0 = createRange(t[0]);
    +    var r1 = createRange(t[1]);
    +    var expected = createRange(t[2]);
    +    assertRangesEqual(expected, goog.math.Range.boundingRange(r0, r1));
    +    assertRangesEqual(expected, goog.math.Range.boundingRange(r1, r0));
    +  }
    +}
    +
    +function testRangeContains() {
    +  var tests = [[[0, 4], [2, 1], true],
    +               [[-4, -1], [-2, -3], true],
    +               [[1, 3], [2, 4], false],
    +               [[-1, 0], [0, 1], false],
    +               [[0, 2], [3, 5], false]];
    +  for (var i = 0; i < tests.length; ++i) {
    +    var t = tests[i];
    +    var r0 = createRange(t[0]);
    +    var r1 = createRange(t[1]);
    +    var expected = t[2];
    +    assertEquals(expected, goog.math.Range.contains(r0, r1));
    +  }
    +}
    +
    +function testRangeClone() {
    +  var r = new goog.math.Range(5.6, -3.4);
    +  assertRangesEqual(r, r.clone());
    +}
    +
    +function testGetLength() {
    +  assertEquals(2, new goog.math.Range(1, 3).getLength());
    +  assertEquals(2, new goog.math.Range(3, 1).getLength());
    +}
    +
    +function testRangeContainsPoint() {
    +  var r = new goog.math.Range(0, 1);
    +  assert(goog.math.Range.containsPoint(r, 0));
    +  assert(goog.math.Range.containsPoint(r, 1));
    +  assertFalse(goog.math.Range.containsPoint(r, -1));
    +  assertFalse(goog.math.Range.containsPoint(r, 2));
    +}
    +
    +function testIncludePoint() {
    +  var r = new goog.math.Range(0, 2);
    +  r.includePoint(0);
    +  assertObjectEquals(new goog.math.Range(0, 2), r);
    +  r.includePoint(1);
    +  assertObjectEquals(new goog.math.Range(0, 2), r);
    +  r.includePoint(2);
    +  assertObjectEquals(new goog.math.Range(0, 2), r);
    +  r.includePoint(-1);
    +  assertObjectEquals(new goog.math.Range(-1, 2), r);
    +  r.includePoint(3);
    +  assertObjectEquals(new goog.math.Range(-1, 3), r);
    +}
    +
    +function testIncludeRange() {
    +  var r = new goog.math.Range(0, 4);
    +  r.includeRange(r);
    +  assertObjectEquals(new goog.math.Range(0, 4), r);
    +  r.includeRange(new goog.math.Range(1, 3));
    +  assertObjectEquals(new goog.math.Range(0, 4), r);
    +  r.includeRange(new goog.math.Range(-1, 2));
    +  assertObjectEquals(new goog.math.Range(-1, 4), r);
    +  r.includeRange(new goog.math.Range(2, 5));
    +  assertObjectEquals(new goog.math.Range(-1, 5), r);
    +  r.includeRange(new goog.math.Range(-2, 6));
    +  assertObjectEquals(new goog.math.Range(-2, 6), r);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rangeset.js b/mapbox-gl/closure-library/closure/goog/math/rangeset.js
    new file mode 100644
    index 0000000000..b21138825d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rangeset.js
    @@ -0,0 +1,396 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A RangeSet is a structure that manages a list of ranges.
    + * Numeric ranges may be added and removed from the RangeSet, and the set may
    + * be queried for the presence or absence of individual values or ranges of
    + * values.
    + *
    + * This may be used, for example, to track the availability of sparse elements
    + * in an array without iterating over the entire array.
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +goog.provide('goog.math.RangeSet');
    +
    +goog.require('goog.array');
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.math.Range');
    +
    +
    +
    +/**
    + * Constructs a new RangeSet, which can store numeric ranges.
    + *
    + * Ranges are treated as half-closed: that is, they are exclusive of their end
    + * value [start, end).
    + *
    + * New ranges added to the set which overlap the values in one or more existing
    + * ranges will be merged.
    + *
    + * @struct
    + * @constructor
    + * @final
    + */
    +goog.math.RangeSet = function() {
    +  /**
    +   * A sorted list of ranges that represent the values in the set.
    +   * @type {!Array<!goog.math.Range>}
    +   * @private
    +   */
    +  this.ranges_ = [];
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * @return {string} A debug string in the form [[1, 5], [8, 9], [15, 30]].
    +   * @override
    +   */
    +  goog.math.RangeSet.prototype.toString = function() {
    +    return '[' + this.ranges_.join(', ') + ']';
    +  };
    +}
    +
    +
    +/**
    + * Compares two sets for equality.
    + *
    + * @param {goog.math.RangeSet} a A range set.
    + * @param {goog.math.RangeSet} b A range set.
    + * @return {boolean} Whether both sets contain the same values.
    + */
    +goog.math.RangeSet.equals = function(a, b) {
    +  // Fast check for object equality. Also succeeds if a and b are both null.
    +  return a == b || !!(a && b && goog.array.equals(a.ranges_, b.ranges_,
    +      goog.math.Range.equals));
    +};
    +
    +
    +/**
    + * @return {!goog.math.RangeSet} A new RangeSet containing the same values as
    + *      this one.
    + */
    +goog.math.RangeSet.prototype.clone = function() {
    +  var set = new goog.math.RangeSet();
    +
    +  for (var i = this.ranges_.length; i--;) {
    +    set.ranges_[i] = this.ranges_[i].clone();
    +  }
    +
    +  return set;
    +};
    +
    +
    +/**
    + * Adds a range to the set. If the new range overlaps existing values, those
    + * ranges will be merged.
    + *
    + * @param {goog.math.Range} a The range to add.
    + */
    +goog.math.RangeSet.prototype.add = function(a) {
    +  if (a.end <= a.start) {
    +    // Empty ranges are ignored.
    +    return;
    +  }
    +
    +  a = a.clone();
    +
    +  // Find the insertion point.
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (a.start <= b.end) {
    +      a.start = Math.min(a.start, b.start);
    +      break;
    +    }
    +  }
    +
    +  var insertionPoint = i;
    +
    +  for (; b = this.ranges_[i]; i++) {
    +    if (a.end < b.start) {
    +      break;
    +    }
    +    a.end = Math.max(a.end, b.end);
    +  }
    +
    +  this.ranges_.splice(insertionPoint, i - insertionPoint, a);
    +};
    +
    +
    +/**
    + * Removes a range of values from the set.
    + *
    + * @param {goog.math.Range} a The range to remove.
    + */
    +goog.math.RangeSet.prototype.remove = function(a) {
    +  if (a.end <= a.start) {
    +    // Empty ranges are ignored.
    +    return;
    +  }
    +
    +  // Find the insertion point.
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (a.start < b.end) {
    +      break;
    +    }
    +  }
    +
    +  if (!b || a.end < b.start) {
    +    // The range being removed doesn't overlap any existing range. Exit early.
    +    return;
    +  }
    +
    +  var insertionPoint = i;
    +
    +  if (a.start > b.start) {
    +    // There is an overlap with the nearest range. Modify it accordingly.
    +    insertionPoint++;
    +
    +    if (a.end < b.end) {
    +      goog.array.insertAt(this.ranges_,
    +                          new goog.math.Range(a.end, b.end),
    +                          insertionPoint);
    +    }
    +    b.end = a.start;
    +  }
    +
    +  for (i = insertionPoint; b = this.ranges_[i]; i++) {
    +    b.start = Math.max(a.end, b.start);
    +    if (a.end < b.end) {
    +      break;
    +    }
    +  }
    +
    +  this.ranges_.splice(insertionPoint, i - insertionPoint);
    +};
    +
    +
    +/**
    + * Determines whether a given range is in the set. Only succeeds if the entire
    + * range is available.
    + *
    + * @param {goog.math.Range} a The query range.
    + * @return {boolean} Whether the entire requested range is set.
    + */
    +goog.math.RangeSet.prototype.contains = function(a) {
    +  if (a.end <= a.start) {
    +    return false;
    +  }
    +
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (a.start < b.end) {
    +      if (a.end >= b.start) {
    +        return goog.math.Range.contains(b, a);
    +      }
    +      break;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Determines whether a given value is set in the RangeSet.
    + *
    + * @param {number} value The value to test.
    + * @return {boolean} Whether the given value is in the set.
    + */
    +goog.math.RangeSet.prototype.containsValue = function(value) {
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (value < b.end) {
    +      if (value >= b.start) {
    +        return true;
    +      }
    +      break;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns the union of this RangeSet with another.
    + *
    + * @param {goog.math.RangeSet} set Another RangeSet.
    + * @return {!goog.math.RangeSet} A new RangeSet containing all values from
    + *     either set.
    + */
    +goog.math.RangeSet.prototype.union = function(set) {
    +  // TODO(brenneman): A linear-time merge would be preferable if it is ever a
    +  // bottleneck.
    +  set = set.clone();
    +
    +  for (var i = 0, a; a = this.ranges_[i]; i++) {
    +    set.add(a);
    +  }
    +
    +  return set;
    +};
    +
    +
    +/**
    + * Subtracts the ranges of another set from this one, returning the result
    + * as a new RangeSet.
    + *
    + * @param {!goog.math.RangeSet} set The RangeSet to subtract.
    + * @return {!goog.math.RangeSet} A new RangeSet containing all values in this
    + *     set minus the values of the input set.
    + */
    +goog.math.RangeSet.prototype.difference = function(set) {
    +  var ret = this.clone();
    +
    +  for (var i = 0, a; a = set.ranges_[i]; i++) {
    +    ret.remove(a);
    +  }
    +
    +  return ret;
    +};
    +
    +
    +/**
    + * Intersects this RangeSet with another.
    + *
    + * @param {goog.math.RangeSet} set The RangeSet to intersect with.
    + * @return {!goog.math.RangeSet} A new RangeSet containing all values set in
    + *     both this and the input set.
    + */
    +goog.math.RangeSet.prototype.intersection = function(set) {
    +  if (this.isEmpty() || set.isEmpty()) {
    +    return new goog.math.RangeSet();
    +  }
    +
    +  return this.difference(set.inverse(this.getBounds()));
    +};
    +
    +
    +/**
    + * Creates a subset of this set over the input range.
    + *
    + * @param {goog.math.Range} range The range to copy into the slice.
    + * @return {!goog.math.RangeSet} A new RangeSet with a copy of the values in the
    + *     input range.
    + */
    +goog.math.RangeSet.prototype.slice = function(range) {
    +  var set = new goog.math.RangeSet();
    +  if (range.start >= range.end) {
    +    return set;
    +  }
    +
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (b.end <= range.start) {
    +      continue;
    +    }
    +    if (b.start > range.end) {
    +      break;
    +    }
    +
    +    set.add(new goog.math.Range(Math.max(range.start, b.start),
    +                                Math.min(range.end, b.end)));
    +  }
    +
    +  return set;
    +};
    +
    +
    +/**
    + * Creates an inverted slice of this set over the input range.
    + *
    + * @param {goog.math.Range} range The range to copy into the slice.
    + * @return {!goog.math.RangeSet} A new RangeSet containing inverted values from
    + *     the original over the input range.
    + */
    +goog.math.RangeSet.prototype.inverse = function(range) {
    +  var set = new goog.math.RangeSet();
    +
    +  set.add(range);
    +  for (var i = 0, b; b = this.ranges_[i]; i++) {
    +    if (range.start >= b.end) {
    +      continue;
    +    }
    +    if (range.end < b.start) {
    +      break;
    +    }
    +
    +    set.remove(b);
    +  }
    +
    +  return set;
    +};
    +
    +
    +/**
    + * @return {number} The sum of the lengths of ranges covered in the set.
    + */
    +goog.math.RangeSet.prototype.coveredLength = function() {
    +  return /** @type {number} */ (goog.array.reduce(
    +      this.ranges_,
    +      function(res, range) {
    +        return res + range.end - range.start;
    +      }, 0));
    +};
    +
    +
    +/**
    + * @return {goog.math.Range} The total range this set covers, ignoring any
    + *     gaps between ranges.
    + */
    +goog.math.RangeSet.prototype.getBounds = function() {
    +  if (this.ranges_.length) {
    +    return new goog.math.Range(this.ranges_[0].start,
    +                               goog.array.peek(this.ranges_).end);
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether any ranges are currently in the set.
    + */
    +goog.math.RangeSet.prototype.isEmpty = function() {
    +  return this.ranges_.length == 0;
    +};
    +
    +
    +/**
    + * Removes all values in the set.
    + */
    +goog.math.RangeSet.prototype.clear = function() {
    +  this.ranges_.length = 0;
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the ranges in the RangeSet.
    + *
    + * @param {boolean=} opt_keys Ignored for RangeSets.
    + * @return {!goog.iter.Iterator} An iterator over the values in the set.
    + */
    +goog.math.RangeSet.prototype.__iterator__ = function(opt_keys) {
    +  var i = 0;
    +  var list = this.ranges_;
    +
    +  var iterator = new goog.iter.Iterator();
    +  iterator.next = function() {
    +    if (i >= list.length) {
    +      throw goog.iter.StopIteration;
    +    }
    +    return list[i++].clone();
    +  };
    +
    +  return iterator;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rangeset_test.html b/mapbox-gl/closure-library/closure/goog/math/rangeset_test.html
    new file mode 100644
    index 0000000000..53adb50140
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rangeset_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.RangeSet
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.RangeSetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rangeset_test.js b/mapbox-gl/closure-library/closure/goog/math/rangeset_test.js
    new file mode 100644
    index 0000000000..3c072418da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rangeset_test.js
    @@ -0,0 +1,660 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.RangeSetTest');
    +goog.setTestOnly('goog.math.RangeSetTest');
    +
    +goog.require('goog.iter');
    +goog.require('goog.math.Range');
    +goog.require('goog.math.RangeSet');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Produce legible assertion results for comparing ranges. The expected range
    + * may be defined as a goog.math.Range or as a two-element array of numbers. If
    + * two ranges are not equal, the error message will be in the format:
    + * "Expected <[1, 2]> (Object) but was <[3, 4]> (Object)"
    + *
    + * @param {!goog.math.Range|!Array<number>|string} a A descriptive string or
    + *     the expected range.
    + * @param {!goog.math.Range|!Array<number>} b The expected range when a
    + *     descriptive string is present, or the range to compare.
    + * @param {goog.math.Range=} opt_c The range to compare when a descriptive
    + *     string is present.
    + */
    +function assertRangesEqual(a, b, opt_c) {
    +  var message = opt_c ? a : '';
    +  var expected = opt_c ? b : a;
    +  var actual = opt_c ? opt_c : b;
    +
    +  if (goog.isArray(expected)) {
    +    assertEquals(message + '\n' +
    +                 'Expected ranges must be specified as goog.math.Range ' +
    +                 'objects or as 2-element number arrays. Found [' +
    +                 expected.join(', ') + ']',
    +                 2, expected.length);
    +    expected = new goog.math.Range(expected[0], expected[1]);
    +  }
    +
    +  if (!goog.math.Range.equals(/** @type {!goog.math.Range} */ (expected),
    +                              /** @type {!goog.math.Range} */ (actual))) {
    +    if (message) {
    +      assertEquals(message, expected, actual);
    +    } else {
    +      assertEquals(expected, actual);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Produce legible assertion results for comparing two lists of ranges. Expected
    + * lists may be specified as a list of goog.math.Ranges, or as a list of
    + * two-element arrays of numbers.
    + *
    + * @param {Array<goog.math.Range|Array<number>>|string} a A help
    + *     string or the list of expected ranges.
    + * @param {Array<goog.math.Range|Array<number>>} b The list of
    + *     expected ranges when a descriptive string is present, or the list of
    + *     ranges to compare.
    + * @param {Array<goog.math.Range>=} opt_c The list of ranges to compare when a
    + *     descriptive string is present.
    + */
    +function assertRangeListsEqual(a, b, opt_c) {
    +  var message = opt_c ? a + '\n' : '';
    +  var expected = opt_c ? b : a;
    +  var actual = opt_c ? opt_c : b;
    +
    +  assertEquals(message + 'Array lengths unequal.',
    +               expected.length, actual.length);
    +
    +  for (var i = 0; i < expected.length; i++) {
    +    assertRangesEqual(message + 'Range ' + i + ' mismatch.',
    +                      expected[i], actual[i]);
    +  }
    +}
    +
    +
    +function testClone() {
    +  var r = new goog.math.RangeSet();
    +
    +  var test = new goog.math.RangeSet(r);
    +  assertRangeListsEqual([], test.ranges_);
    +
    +  r.add(new goog.math.Range(-10, -2));
    +  r.add(new goog.math.Range(2.72, 3.14));
    +  r.add(new goog.math.Range(8, 11));
    +
    +  test = r.clone();
    +  assertRangeListsEqual([[-10, -2], [2.72, 3.14], [8, 11]], test.ranges_);
    +
    +  var test2 = r.clone();
    +  assertRangeListsEqual(test.ranges_, test2.ranges_);
    +
    +  assertNotEquals('The clones should not share the same list reference.',
    +                  test.ranges_, test2.ranges_);
    +
    +  for (var i = 0; i < test.ranges_.length; i++) {
    +    assertNotEquals('The clones should not share references to ranges.',
    +                    test.ranges_[i], test2.ranges_[i]);
    +  }
    +}
    +
    +
    +function testAddNoCorruption() {
    +  var r = new goog.math.RangeSet();
    +
    +  var range = new goog.math.Range(1, 2);
    +  r.add(range);
    +
    +  assertNotEquals('Only a copy of the input range should be stored.',
    +                  range, r.ranges_[0]);
    +
    +  range.end = 5;
    +  assertRangeListsEqual('Modifying an input range after use should not ' +
    +                        'affect the set.',
    +                        [[1, 2]], r.ranges_);
    +}
    +
    +
    +function testAdd() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(7, 12));
    +  assertRangeListsEqual([[7, 12]], r.ranges_);
    +
    +  r.add(new goog.math.Range(1, 3));
    +  assertRangeListsEqual([[1, 3], [7, 12]], r.ranges_);
    +
    +  r.add(new goog.math.Range(13, 18));
    +  assertRangeListsEqual([[1, 3], [7, 12], [13, 18]], r.ranges_);
    +
    +  r.add(new goog.math.Range(5, 5));
    +  assertRangeListsEqual('Zero length ranges should be ignored.',
    +                        [[1, 3], [7, 12], [13, 18]], r.ranges_);
    +
    +  var badRange = new goog.math.Range(5, 5);
    +  badRange.end = 4;
    +  r.add(badRange);
    +  assertRangeListsEqual('Negative length ranges should be ignored.',
    +                        [[1, 3], [7, 12], [13, 18]], r.ranges_);
    +
    +  r.add(new goog.math.Range(-22, -15));
    +  assertRangeListsEqual('Negative ranges should work fine.',
    +                        [[-22, -15], [1, 3], [7, 12], [13, 18]], r.ranges_);
    +
    +  r.add(new goog.math.Range(3.1, 6.9));
    +  assertRangeListsEqual('Non-integer ranges should work fine.',
    +                        [[-22, -15], [1, 3], [3.1, 6.9], [7, 12], [13, 18]],
    +                        r.ranges_);
    +}
    +
    +
    +function testAddWithOverlaps() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(7, 12));
    +  r.add(new goog.math.Range(5, 8));
    +  assertRangeListsEqual([[5, 12]], r.ranges_);
    +
    +  r.add(new goog.math.Range(15, 20));
    +  r.add(new goog.math.Range(18, 25));
    +  assertRangeListsEqual([[5, 12], [15, 25]], r.ranges_);
    +
    +  r.add(new goog.math.Range(10, 17));
    +  assertRangeListsEqual([[5, 25]], r.ranges_);
    +
    +  r.add(new goog.math.Range(-4, 4.5));
    +  assertRangeListsEqual([[-4, 4.5], [5, 25]], r.ranges_);
    +
    +  r.add(new goog.math.Range(4.2, 5.3));
    +  assertRangeListsEqual([[-4, 25]], r.ranges_);
    +}
    +
    +
    +function testAddWithAdjacentSpans() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(7, 12));
    +  r.add(new goog.math.Range(13, 19));
    +  assertRangeListsEqual([[7, 12], [13, 19]], r.ranges_);
    +
    +  r.add(new goog.math.Range(4, 6));
    +  assertRangeListsEqual([[4, 6], [7, 12], [13, 19]], r.ranges_);
    +
    +  r.add(new goog.math.Range(6, 7));
    +  assertRangeListsEqual([[4, 12], [13, 19]], r.ranges_);
    +
    +  r.add(new goog.math.Range(12, 13));
    +  assertRangeListsEqual([[4, 19]], r.ranges_);
    +
    +  r.add(new goog.math.Range(19.1, 22));
    +  assertRangeListsEqual([[4, 19], [19.1, 22]], r.ranges_);
    +
    +  r.add(new goog.math.Range(19, 19.1));
    +  assertRangeListsEqual([[4, 22]], r.ranges_);
    +
    +  r.add(new goog.math.Range(-3, -2));
    +  assertRangeListsEqual([[-3, -2], [4, 22]], r.ranges_);
    +
    +  r.add(new goog.math.Range(-2, 4));
    +  assertRangeListsEqual([[-3, 22]], r.ranges_);
    +}
    +
    +
    +function testAddWithSubsets() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(7, 12));
    +  assertRangeListsEqual([[7, 12]], r.ranges_);
    +
    +  r.add(new goog.math.Range(7, 12));
    +  assertRangeListsEqual([[7, 12]], r.ranges_);
    +
    +  r.add(new goog.math.Range(8, 11));
    +  assertRangeListsEqual([[7, 12]], r.ranges_);
    +
    +  for (var i = 20; i < 30; i += 2) {
    +    r.add(new goog.math.Range(i, i + 1));
    +  }
    +  assertRangeListsEqual(
    +      [[7, 12], [20, 21], [22, 23], [24, 25], [26, 27], [28, 29]],
    +      r.ranges_);
    +
    +  r.add(new goog.math.Range(1, 30));
    +  assertRangeListsEqual([[1, 30]], r.ranges_);
    +}
    +
    +
    +function testRemove() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(1, 3));
    +  r.add(new goog.math.Range(7, 8));
    +  r.add(new goog.math.Range(10, 20));
    +
    +  r.remove(new goog.math.Range(3, 6));
    +  assertRangeListsEqual([[1, 3], [7, 8], [10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(7, 8));
    +  assertRangeListsEqual([[1, 3], [10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(1, 3));
    +  assertRangeListsEqual([[10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(8, 11));
    +  assertRangeListsEqual([[11, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(18, 25));
    +  assertRangeListsEqual([[11, 18]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(15, 16));
    +  assertRangeListsEqual([[11, 15], [16, 18]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(11, 15));
    +  assertRangeListsEqual([[16, 18]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(16, 16));
    +  assertRangeListsEqual('Empty ranges should be ignored.',
    +                        [[16, 18]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(16, 17));
    +  assertRangeListsEqual([[17, 18]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(17, 18));
    +  assertRangeListsEqual([], r.ranges_);
    +}
    +
    +
    +function testRemoveWithNonOverlappingRanges() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(10, 20));
    +
    +  r.remove(new goog.math.Range(5, 8));
    +  assertRangeListsEqual('Non-overlapping ranges should be ignored.',
    +                        [[10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(20, 30));
    +  assertRangeListsEqual('Non-overlapping ranges should be ignored.',
    +                        [[10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(15, 15));
    +  assertRangeListsEqual('Zero-length ranges should be ignored.',
    +                        [[10, 20]], r.ranges_);
    +}
    +
    +
    +function testRemoveWithIdenticalRanges() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(10, 20));
    +  r.add(new goog.math.Range(30, 40));
    +  r.add(new goog.math.Range(50, 60));
    +  assertRangeListsEqual([[10, 20], [30, 40], [50, 60]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(30, 40));
    +  assertRangeListsEqual([[10, 20], [50, 60]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(50, 60));
    +  assertRangeListsEqual([[10, 20]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(10, 20));
    +  assertRangeListsEqual([], r.ranges_);
    +}
    +
    +
    +function testRemoveWithOverlappingSubsets() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(1, 10));
    +
    +  r.remove(new goog.math.Range(1, 4));
    +  assertRangeListsEqual([[4, 10]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(8, 10));
    +  assertRangeListsEqual([[4, 8]], r.ranges_);
    +}
    +
    +
    +function testRemoveMultiple() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(5, 8));
    +  r.add(new goog.math.Range(10, 20));
    +  r.add(new goog.math.Range(30, 35));
    +
    +  for (var i = 20; i < 30; i += 2) {
    +    r.add(new goog.math.Range(i, i + 1));
    +  }
    +
    +  assertRangeListsEqual(
    +      'Setting up the test data seems to have failed, how embarrassing.',
    +      [[5, 8], [10, 21], [22, 23], [24, 25], [26, 27], [28, 29], [30, 35]],
    +      r.ranges_);
    +
    +  r.remove(new goog.math.Range(15, 32));
    +  assertRangeListsEqual([[5, 8], [10, 15], [32, 35]],
    +                        r.ranges_);
    +}
    +
    +
    +function testRemoveWithRealNumbers() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(2, 4));
    +
    +  r.remove(new goog.math.Range(1.1, 2.72));
    +  assertRangeListsEqual([[2.72, 4]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(3.14, 5));
    +  assertRangeListsEqual([[2.72, 3.14]], r.ranges_);
    +
    +  r.remove(new goog.math.Range(2.8, 3));
    +  assertRangeListsEqual([[2.72, 2.8], [3, 3.14]], r.ranges_);
    +}
    +
    +
    +function testEquals() {
    +  var a = new goog.math.RangeSet();
    +  var b = new goog.math.RangeSet();
    +
    +  assertTrue(goog.math.RangeSet.equals(a, b));
    +
    +  a.add(new goog.math.Range(3, 9));
    +  assertFalse(goog.math.RangeSet.equals(a, b));
    +
    +  b.add(new goog.math.Range(4, 9));
    +  assertFalse(goog.math.RangeSet.equals(a, b));
    +
    +  b.add(new goog.math.Range(3, 4));
    +  assertTrue(goog.math.RangeSet.equals(a, b));
    +
    +  a.add(new goog.math.Range(12, 14));
    +  b.add(new goog.math.Range(11, 14));
    +  assertFalse(goog.math.RangeSet.equals(a, b));
    +
    +  a.add(new goog.math.Range(11, 12));
    +  assertTrue(goog.math.RangeSet.equals(a, b));
    +}
    +
    +
    +function testContains() {
    +  var r = new goog.math.RangeSet();
    +
    +  assertFalse(r.contains(7, 9));
    +
    +  r.add(new goog.math.Range(5, 6));
    +  r.add(new goog.math.Range(10, 20));
    +
    +  assertFalse(r.contains(new goog.math.Range(7, 9)));
    +  assertFalse(r.contains(new goog.math.Range(9, 11)));
    +  assertFalse(r.contains(new goog.math.Range(18, 22)));
    +
    +  assertTrue(r.contains(new goog.math.Range(17, 19)));
    +  assertTrue(r.contains(new goog.math.Range(5, 6)));
    +
    +  assertTrue(r.contains(new goog.math.Range(5.9, 5.999)));
    +
    +  assertFalse('An empty input range should always return false.',
    +              r.contains(new goog.math.Range(15, 15)));
    +
    +  var badRange = new goog.math.Range(15, 15);
    +  badRange.end = 14;
    +  assertFalse('An invalid range should always return false.',
    +              r.contains(badRange));
    +}
    +
    +
    +function testContainsValue() {
    +  var r = new goog.math.RangeSet();
    +
    +  assertFalse(r.containsValue(5));
    +
    +  r.add(new goog.math.Range(1, 4));
    +  r.add(new goog.math.Range(10, 20));
    +
    +  assertFalse(r.containsValue(0));
    +  assertFalse(r.containsValue(0.999));
    +  assertFalse(r.containsValue(5));
    +  assertFalse(r.containsValue(25));
    +  assertFalse(r.containsValue(20));
    +
    +  assertTrue(r.containsValue(3));
    +  assertTrue(r.containsValue(10));
    +  assertTrue(r.containsValue(19));
    +  assertTrue(r.containsValue(19.999));
    +}
    +
    +
    +function testUnion() {
    +  var a = new goog.math.RangeSet();
    +
    +  a.add(new goog.math.Range(1, 5));
    +  a.add(new goog.math.Range(10, 11));
    +  a.add(new goog.math.Range(15, 20));
    +
    +  var b = new goog.math.RangeSet();
    +
    +  b.add(new goog.math.Range(0, 5));
    +  b.add(new goog.math.Range(8, 18));
    +
    +  var test = a.union(b);
    +  assertRangeListsEqual([[0, 5], [8, 20]], test.ranges_);
    +
    +  var test = b.union(a);
    +  assertRangeListsEqual([[0, 5], [8, 20]], test.ranges_);
    +
    +  var test = a.union(a);
    +  assertRangeListsEqual(a.ranges_, test.ranges_);
    +}
    +
    +
    +function testDifference() {
    +  var a = new goog.math.RangeSet();
    +
    +  a.add(new goog.math.Range(1, 5));
    +  a.add(new goog.math.Range(10, 11));
    +  a.add(new goog.math.Range(15, 20));
    +
    +  var b = new goog.math.RangeSet();
    +
    +  b.add(new goog.math.Range(0, 5));
    +  b.add(new goog.math.Range(8, 18));
    +
    +  var test = a.difference(b);
    +  assertRangeListsEqual([[18, 20]], test.ranges_);
    +
    +  var test = b.difference(a);
    +  assertRangeListsEqual([[0, 1], [8, 10], [11, 15]], test.ranges_);
    +
    +  var test = a.difference(a);
    +  assertRangeListsEqual([], test.ranges_);
    +
    +  var test = b.difference(b);
    +  assertRangeListsEqual([], test.ranges_);
    +}
    +
    +
    +function testIntersection() {
    +  var a = new goog.math.RangeSet();
    +
    +  a.add(new goog.math.Range(1, 5));
    +  a.add(new goog.math.Range(10, 11));
    +  a.add(new goog.math.Range(15, 20));
    +
    +  var b = new goog.math.RangeSet();
    +
    +  b.add(new goog.math.Range(0, 5));
    +  b.add(new goog.math.Range(8, 18));
    +
    +  var test = a.intersection(b);
    +  assertRangeListsEqual([[1, 5], [10, 11], [15, 18]], test.ranges_);
    +
    +  var test = b.intersection(a);
    +  assertRangeListsEqual([[1, 5], [10, 11], [15, 18]], test.ranges_);
    +
    +  var test = a.intersection(a);
    +  assertRangeListsEqual(a.ranges_, test.ranges_);
    +}
    +
    +
    +function testSlice() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(2, 4));
    +  r.add(new goog.math.Range(5, 6));
    +  r.add(new goog.math.Range(9, 15));
    +
    +  var test = r.slice(new goog.math.Range(0, 2));
    +  assertRangeListsEqual([], test.ranges_);
    +
    +  test = r.slice(new goog.math.Range(2, 4));
    +  assertRangeListsEqual([[2, 4]], test.ranges_);
    +
    +  test = r.slice(new goog.math.Range(7, 20));
    +  assertRangeListsEqual([[9, 15]], test.ranges_);
    +
    +  test = r.slice(new goog.math.Range(4, 30));
    +  assertRangeListsEqual([[5, 6], [9, 15]], test.ranges_);
    +
    +  test = r.slice(new goog.math.Range(2, 15));
    +  assertRangeListsEqual([[2, 4], [5, 6], [9, 15]], test.ranges_);
    +
    +  test = r.slice(new goog.math.Range(10, 10));
    +  assertRangeListsEqual('An empty range should produce an empty set.',
    +                        [], test.ranges_);
    +
    +  var badRange = new goog.math.Range(10, 10);
    +  badRange.end = 9;
    +  test = r.slice(badRange);
    +  assertRangeListsEqual('An invalid range should produce an empty set.',
    +                        [], test.ranges_);
    +}
    +
    +
    +function testInverse() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(1, 3));
    +  r.add(new goog.math.Range(5, 6));
    +  r.add(new goog.math.Range(8, 10));
    +
    +  var test = r.inverse(new goog.math.Range(10, 20));
    +  assertRangeListsEqual([[10, 20]], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(1, 3));
    +  assertRangeListsEqual([], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(0, 2));
    +  assertRangeListsEqual([[0, 1]], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(9, 12));
    +  assertRangeListsEqual([[10, 12]], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(2, 9));
    +  assertRangeListsEqual([[3, 5], [6, 8]], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(4, 9));
    +  assertRangeListsEqual([[4, 5], [6, 8]], test.ranges_);
    +
    +  test = r.inverse(new goog.math.Range(9, 9));
    +  assertRangeListsEqual('An empty range should produce an empty set.',
    +                        [], test.ranges_);
    +
    +  var badRange = new goog.math.Range(9, 9);
    +  badRange.end = 8;
    +  test = r.inverse(badRange);
    +  assertRangeListsEqual('An invalid range should produce an empty set.',
    +                        [], test.ranges_);
    +}
    +
    +
    +function testCoveredLength() {
    +  var r = new goog.math.RangeSet();
    +  assertEquals(0, r.coveredLength());
    +
    +  r.add(new goog.math.Range(5, 9));
    +  assertEquals(4, r.coveredLength());
    +
    +  r.add(new goog.math.Range(0, 3));
    +  r.add(new goog.math.Range(12, 13));
    +  assertEquals(8, r.coveredLength());
    +
    +  r.add(new goog.math.Range(-1, 13));
    +  assertEquals(14, r.coveredLength());
    +
    +  r.add(new goog.math.Range(13, 13.5));
    +  assertEquals(14.5, r.coveredLength());
    +}
    +
    +
    +function testGetBounds() {
    +  var r = new goog.math.RangeSet();
    +
    +  assertNull(r.getBounds());
    +
    +  r.add(new goog.math.Range(12, 54));
    +  assertRangesEqual([12, 54], r.getBounds());
    +
    +  r.add(new goog.math.Range(108, 139));
    +  assertRangesEqual([12, 139], r.getBounds());
    +}
    +
    +
    +function testIsEmpty() {
    +  var r = new goog.math.RangeSet();
    +  assertTrue(r.isEmpty());
    +
    +  r.add(new goog.math.Range(0, 1));
    +  assertFalse(r.isEmpty());
    +
    +  r.remove(new goog.math.Range(0, 1));
    +  assertTrue(r.isEmpty());
    +}
    +
    +
    +function testClear() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(1, 2));
    +  r.add(new goog.math.Range(3, 5));
    +  r.add(new goog.math.Range(8, 13));
    +
    +  assertFalse(r.isEmpty());
    +
    +  r.clear();
    +  assertTrue(r.isEmpty());
    +}
    +
    +
    +function testIter() {
    +  var r = new goog.math.RangeSet();
    +
    +  r.add(new goog.math.Range(1, 3));
    +  r.add(new goog.math.Range(5, 6));
    +  r.add(new goog.math.Range(8, 10));
    +
    +  assertRangeListsEqual([[1, 3], [5, 6], [8, 10]], goog.iter.toArray(r));
    +
    +  var i = 0;
    +  goog.iter.forEach(r, function(testRange) {
    +    assertRangesEqual('Iterated set values should match the originals.',
    +                      r.ranges_[i], testRange);
    +    assertNotEquals('Iterated range should not be a reference to the original.',
    +                    r.ranges_[i], testRange);
    +    i++;
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rect.js b/mapbox-gl/closure-library/closure/goog/math/rect.js
    new file mode 100644
    index 0000000000..c44b80e841
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rect.js
    @@ -0,0 +1,464 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing rectangles.
    + */
    +
    +goog.provide('goog.math.Rect');
    +
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Size');
    +
    +
    +
    +/**
    + * Class for representing rectangular regions.
    + * @param {number} x Left.
    + * @param {number} y Top.
    + * @param {number} w Width.
    + * @param {number} h Height.
    + * @struct
    + * @constructor
    + */
    +goog.math.Rect = function(x, y, w, h) {
    +  /** @type {number} */
    +  this.left = x;
    +
    +  /** @type {number} */
    +  this.top = y;
    +
    +  /** @type {number} */
    +  this.width = w;
    +
    +  /** @type {number} */
    +  this.height = h;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Rect} A new copy of this Rectangle.
    + */
    +goog.math.Rect.prototype.clone = function() {
    +  return new goog.math.Rect(this.left, this.top, this.width, this.height);
    +};
    +
    +
    +/**
    + * Returns a new Box object with the same position and dimensions as this
    + * rectangle.
    + * @return {!goog.math.Box} A new Box representation of this Rectangle.
    + */
    +goog.math.Rect.prototype.toBox = function() {
    +  var right = this.left + this.width;
    +  var bottom = this.top + this.height;
    +  return new goog.math.Box(this.top,
    +                           right,
    +                           bottom,
    +                           this.left);
    +};
    +
    +
    +/**
    + * Creates a new Rect object with the same position and dimensions as a given
    + * Box.  Note that this is only the inverse of toBox if left/top are defined.
    + * @param {goog.math.Box} box A box.
    + * @return {!goog.math.Rect} A new Rect initialized with the box's position
    + *     and size.
    + */
    +goog.math.Rect.createFromBox = function(box) {
    +  return new goog.math.Rect(box.left, box.top,
    +      box.right - box.left, box.bottom - box.top);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a nice string representing size and dimensions of rectangle.
    +   * @return {string} In the form (50, 73 - 75w x 25h).
    +   * @override
    +   */
    +  goog.math.Rect.prototype.toString = function() {
    +    return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
    +           this.height + 'h)';
    +  };
    +}
    +
    +
    +/**
    + * Compares rectangles for equality.
    + * @param {goog.math.Rect} a A Rectangle.
    + * @param {goog.math.Rect} b A Rectangle.
    + * @return {boolean} True iff the rectangles have the same left, top, width,
    + *     and height, or if both are null.
    + */
    +goog.math.Rect.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.left == b.left && a.width == b.width &&
    +         a.top == b.top && a.height == b.height;
    +};
    +
    +
    +/**
    + * Computes the intersection of this rectangle and the rectangle parameter.  If
    + * there is no intersection, returns false and leaves this rectangle as is.
    + * @param {goog.math.Rect} rect A Rectangle.
    + * @return {boolean} True iff this rectangle intersects with the parameter.
    + */
    +goog.math.Rect.prototype.intersection = function(rect) {
    +  var x0 = Math.max(this.left, rect.left);
    +  var x1 = Math.min(this.left + this.width, rect.left + rect.width);
    +
    +  if (x0 <= x1) {
    +    var y0 = Math.max(this.top, rect.top);
    +    var y1 = Math.min(this.top + this.height, rect.top + rect.height);
    +
    +    if (y0 <= y1) {
    +      this.left = x0;
    +      this.top = y0;
    +      this.width = x1 - x0;
    +      this.height = y1 - y0;
    +
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns the intersection of two rectangles. Two rectangles intersect if they
    + * touch at all, for example, two zero width and height rectangles would
    + * intersect if they had the same top and left.
    + * @param {goog.math.Rect} a A Rectangle.
    + * @param {goog.math.Rect} b A Rectangle.
    + * @return {goog.math.Rect} A new intersection rect (even if width and height
    + *     are 0), or null if there is no intersection.
    + */
    +goog.math.Rect.intersection = function(a, b) {
    +  // There is no nice way to do intersection via a clone, because any such
    +  // clone might be unnecessary if this function returns null.  So, we duplicate
    +  // code from above.
    +
    +  var x0 = Math.max(a.left, b.left);
    +  var x1 = Math.min(a.left + a.width, b.left + b.width);
    +
    +  if (x0 <= x1) {
    +    var y0 = Math.max(a.top, b.top);
    +    var y1 = Math.min(a.top + a.height, b.top + b.height);
    +
    +    if (y0 <= y1) {
    +      return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns whether two rectangles intersect. Two rectangles intersect if they
    + * touch at all, for example, two zero width and height rectangles would
    + * intersect if they had the same top and left.
    + * @param {goog.math.Rect} a A Rectangle.
    + * @param {goog.math.Rect} b A Rectangle.
    + * @return {boolean} Whether a and b intersect.
    + */
    +goog.math.Rect.intersects = function(a, b) {
    +  return (a.left <= b.left + b.width && b.left <= a.left + a.width &&
    +      a.top <= b.top + b.height && b.top <= a.top + a.height);
    +};
    +
    +
    +/**
    + * Returns whether a rectangle intersects this rectangle.
    + * @param {goog.math.Rect} rect A rectangle.
    + * @return {boolean} Whether rect intersects this rectangle.
    + */
    +goog.math.Rect.prototype.intersects = function(rect) {
    +  return goog.math.Rect.intersects(this, rect);
    +};
    +
    +
    +/**
    + * Computes the difference regions between two rectangles. The return value is
    + * an array of 0 to 4 rectangles defining the remaining regions of the first
    + * rectangle after the second has been subtracted.
    + * @param {goog.math.Rect} a A Rectangle.
    + * @param {goog.math.Rect} b A Rectangle.
    + * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
    + *     together define the difference area of rectangle a minus rectangle b.
    + */
    +goog.math.Rect.difference = function(a, b) {
    +  var intersection = goog.math.Rect.intersection(a, b);
    +  if (!intersection || !intersection.height || !intersection.width) {
    +    return [a.clone()];
    +  }
    +
    +  var result = [];
    +
    +  var top = a.top;
    +  var height = a.height;
    +
    +  var ar = a.left + a.width;
    +  var ab = a.top + a.height;
    +
    +  var br = b.left + b.width;
    +  var bb = b.top + b.height;
    +
    +  // Subtract off any area on top where A extends past B
    +  if (b.top > a.top) {
    +    result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
    +    top = b.top;
    +    // If we're moving the top down, we also need to subtract the height diff.
    +    height -= b.top - a.top;
    +  }
    +  // Subtract off any area on bottom where A extends past B
    +  if (bb < ab) {
    +    result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
    +    height = bb - top;
    +  }
    +  // Subtract any area on left where A extends past B
    +  if (b.left > a.left) {
    +    result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
    +  }
    +  // Subtract any area on right where A extends past B
    +  if (br < ar) {
    +    result.push(new goog.math.Rect(br, top, ar - br, height));
    +  }
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Computes the difference regions between this rectangle and {@code rect}. The
    + * return value is an array of 0 to 4 rectangles defining the remaining regions
    + * of this rectangle after the other has been subtracted.
    + * @param {goog.math.Rect} rect A Rectangle.
    + * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
    + *     together define the difference area of rectangle a minus rectangle b.
    + */
    +goog.math.Rect.prototype.difference = function(rect) {
    +  return goog.math.Rect.difference(this, rect);
    +};
    +
    +
    +/**
    + * Expand this rectangle to also include the area of the given rectangle.
    + * @param {goog.math.Rect} rect The other rectangle.
    + */
    +goog.math.Rect.prototype.boundingRect = function(rect) {
    +  // We compute right and bottom before we change left and top below.
    +  var right = Math.max(this.left + this.width, rect.left + rect.width);
    +  var bottom = Math.max(this.top + this.height, rect.top + rect.height);
    +
    +  this.left = Math.min(this.left, rect.left);
    +  this.top = Math.min(this.top, rect.top);
    +
    +  this.width = right - this.left;
    +  this.height = bottom - this.top;
    +};
    +
    +
    +/**
    + * Returns a new rectangle which completely contains both input rectangles.
    + * @param {goog.math.Rect} a A rectangle.
    + * @param {goog.math.Rect} b A rectangle.
    + * @return {goog.math.Rect} A new bounding rect, or null if either rect is
    + *     null.
    + */
    +goog.math.Rect.boundingRect = function(a, b) {
    +  if (!a || !b) {
    +    return null;
    +  }
    +
    +  var clone = a.clone();
    +  clone.boundingRect(b);
    +
    +  return clone;
    +};
    +
    +
    +/**
    + * Tests whether this rectangle entirely contains another rectangle or
    + * coordinate.
    + *
    + * @param {goog.math.Rect|goog.math.Coordinate} another The rectangle or
    + *     coordinate to test for containment.
    + * @return {boolean} Whether this rectangle contains given rectangle or
    + *     coordinate.
    + */
    +goog.math.Rect.prototype.contains = function(another) {
    +  if (another instanceof goog.math.Rect) {
    +    return this.left <= another.left &&
    +           this.left + this.width >= another.left + another.width &&
    +           this.top <= another.top &&
    +           this.top + this.height >= another.top + another.height;
    +  } else { // (another instanceof goog.math.Coordinate)
    +    return another.x >= this.left &&
    +           another.x <= this.left + this.width &&
    +           another.y >= this.top &&
    +           another.y <= this.top + this.height;
    +  }
    +};
    +
    +
    +/**
    + * @param {!goog.math.Coordinate} point A coordinate.
    + * @return {number} The squared distance between the point and the closest
    + *     point inside the rectangle. Returns 0 if the point is inside the
    + *     rectangle.
    + */
    +goog.math.Rect.prototype.squaredDistance = function(point) {
    +  var dx = point.x < this.left ?
    +      this.left - point.x : Math.max(point.x - (this.left + this.width), 0);
    +  var dy = point.y < this.top ?
    +      this.top - point.y : Math.max(point.y - (this.top + this.height), 0);
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * @param {!goog.math.Coordinate} point A coordinate.
    + * @return {number} The distance between the point and the closest point
    + *     inside the rectangle. Returns 0 if the point is inside the rectangle.
    + */
    +goog.math.Rect.prototype.distance = function(point) {
    +  return Math.sqrt(this.squaredDistance(point));
    +};
    +
    +
    +/**
    + * @return {!goog.math.Size} The size of this rectangle.
    + */
    +goog.math.Rect.prototype.getSize = function() {
    +  return new goog.math.Size(this.width, this.height);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
    + *     the rectangle.
    + */
    +goog.math.Rect.prototype.getTopLeft = function() {
    +  return new goog.math.Coordinate(this.left, this.top);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Coordinate} A new coordinate for the center of the
    + *     rectangle.
    + */
    +goog.math.Rect.prototype.getCenter = function() {
    +  return new goog.math.Coordinate(
    +      this.left + this.width / 2, this.top + this.height / 2);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
    + *     of the rectangle.
    + */
    +goog.math.Rect.prototype.getBottomRight = function() {
    +  return new goog.math.Coordinate(
    +      this.left + this.width, this.top + this.height);
    +};
    +
    +
    +/**
    + * Rounds the fields to the next larger integer values.
    + * @return {!goog.math.Rect} This rectangle with ceil'd fields.
    + */
    +goog.math.Rect.prototype.ceil = function() {
    +  this.left = Math.ceil(this.left);
    +  this.top = Math.ceil(this.top);
    +  this.width = Math.ceil(this.width);
    +  this.height = Math.ceil(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the fields to the next smaller integer values.
    + * @return {!goog.math.Rect} This rectangle with floored fields.
    + */
    +goog.math.Rect.prototype.floor = function() {
    +  this.left = Math.floor(this.left);
    +  this.top = Math.floor(this.top);
    +  this.width = Math.floor(this.width);
    +  this.height = Math.floor(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the fields to nearest integer values.
    + * @return {!goog.math.Rect} This rectangle with rounded fields.
    + */
    +goog.math.Rect.prototype.round = function() {
    +  this.left = Math.round(this.left);
    +  this.top = Math.round(this.top);
    +  this.width = Math.round(this.width);
    +  this.height = Math.round(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * Translates this rectangle by the given offsets. If a
    + * {@code goog.math.Coordinate} is given, then the left and top values are
    + * translated by the coordinate's x and y values. Otherwise, top and left are
    + * translated by {@code tx} and {@code opt_ty} respectively.
    + * @param {number|goog.math.Coordinate} tx The value to translate left by or the
    + *     the coordinate to translate this rect by.
    + * @param {number=} opt_ty The value to translate top by.
    + * @return {!goog.math.Rect} This rectangle after translating.
    + */
    +goog.math.Rect.prototype.translate = function(tx, opt_ty) {
    +  if (tx instanceof goog.math.Coordinate) {
    +    this.left += tx.x;
    +    this.top += tx.y;
    +  } else {
    +    this.left += tx;
    +    if (goog.isNumber(opt_ty)) {
    +      this.top += opt_ty;
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Scales this rectangle by the given scale factors. The left and width values
    + * are scaled by {@code sx} and the top and height values are scaled by
    + * {@code opt_sy}.  If {@code opt_sy} is not given, then all fields are scaled
    + * by {@code sx}.
    + * @param {number} sx The scale factor to use for the x dimension.
    + * @param {number=} opt_sy The scale factor to use for the y dimension.
    + * @return {!goog.math.Rect} This rectangle after scaling.
    + */
    +goog.math.Rect.prototype.scale = function(sx, opt_sy) {
    +  var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    +  this.left *= sx;
    +  this.width *= sx;
    +  this.top *= sy;
    +  this.height *= sy;
    +  return this;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rect_test.html b/mapbox-gl/closure-library/closure/goog/math/rect_test.html
    new file mode 100644
    index 0000000000..aacd9d3267
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rect_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Rect
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.RectTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/rect_test.js b/mapbox-gl/closure-library/closure/goog/math/rect_test.js
    new file mode 100644
    index 0000000000..849fae43b7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/rect_test.js
    @@ -0,0 +1,441 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.RectTest');
    +goog.setTestOnly('goog.math.RectTest');
    +
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Rect');
    +goog.require('goog.math.Size');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Produce legible assertion results. If two rects are not equal, the error
    + * message will be of the form
    + * "Expected <(1, 2 - 10 x 10)> (Object) but was <(3, 4 - 20 x 20)> (Object)"
    + */
    +function assertRectsEqual(expected, actual) {
    +  if (!goog.math.Rect.equals(expected, actual)) {
    +    assertEquals(expected, actual);
    +  }
    +}
    +
    +function createRect(a) {
    +  return a ? new goog.math.Rect(a[0], a[1], a[2] - a[0], a[3] - a[1]) : null;
    +}
    +
    +function testRectClone() {
    +  var r = new goog.math.Rect(0, 0, 0, 0);
    +  assertRectsEqual(r, r.clone());
    +  r.left = -10;
    +  r.top = -20;
    +  r.width = 10;
    +  r.height = 20;
    +  assertRectsEqual(r, r.clone());
    +}
    +
    +function testRectIntersection() {
    +  var tests = [[[10, 10, 20, 20], [15, 15, 25, 25], [15, 15, 20, 20]],
    +               [[10, 10, 20, 20], [20, 0, 30, 10], [20, 10, 20, 10]],
    +               [[0, 0, 1, 1], [10, 11, 12, 13], null],
    +               [[11, 12, 98, 99], [22, 23, 34, 35], [22, 23, 34, 35]]];
    +  for (var i = 0; i < tests.length; ++i) {
    +    var t = tests[i];
    +    var r0 = createRect(t[0]);
    +    var r1 = createRect(t[1]);
    +
    +    var expected = createRect(t[2]);
    +
    +    assertRectsEqual(expected, goog.math.Rect.intersection(r0, r1));
    +    assertRectsEqual(expected, goog.math.Rect.intersection(r1, r0));
    +
    +    // Test in place methods.
    +    var clone = r0.clone();
    +
    +    assertRectsEqual(expected, clone.intersection(r1) ? clone : null);
    +    assertRectsEqual(expected, r1.intersection(r0) ? r1 : null);
    +  }
    +}
    +
    +function testRectIntersects() {
    +  var r0 = createRect([10, 10, 20, 20]);
    +  var r1 = createRect([15, 15, 25, 25]);
    +  var r2 = createRect([0, 0, 1, 1]);
    +
    +  assertTrue(goog.math.Rect.intersects(r0, r1));
    +  assertTrue(goog.math.Rect.intersects(r1, r0));
    +  assertTrue(r0.intersects(r1));
    +  assertTrue(r1.intersects(r0));
    +
    +  assertFalse(goog.math.Rect.intersects(r0, r2));
    +  assertFalse(goog.math.Rect.intersects(r2, r0));
    +  assertFalse(r0.intersects(r2));
    +  assertFalse(r2.intersects(r0));
    +}
    +
    +function testRectBoundingRect() {
    +  var tests = [[[10, 10, 20, 20], [15, 15, 25, 25], [10, 10, 25, 25]],
    +               [[10, 10, 20, 20], [20, 0, 30, 10], [10, 0, 30, 20]],
    +               [[0, 0, 1, 1], [10, 11, 12, 13], [0, 0, 12, 13]],
    +               [[11, 12, 98, 99], [22, 23, 34, 35], [11, 12, 98, 99]]];
    +  for (var i = 0; i < tests.length; ++i) {
    +    var t = tests[i];
    +    var r0 = createRect(t[0]);
    +    var r1 = createRect(t[1]);
    +    var expected = createRect(t[2]);
    +    assertRectsEqual(expected, goog.math.Rect.boundingRect(r0, r1));
    +    assertRectsEqual(expected, goog.math.Rect.boundingRect(r1, r0));
    +
    +    // Test in place methods.
    +    var clone = r0.clone();
    +
    +    clone.boundingRect(r1);
    +    assertRectsEqual(expected, clone);
    +
    +    r1.boundingRect(r0);
    +    assertRectsEqual(expected, r1);
    +  }
    +}
    +
    +function testRectDifference() {
    +  // B is the same as A.
    +  assertDifference([10, 10, 20, 20], [10, 10, 20, 20], []);
    +  // B does not touch A.
    +  assertDifference([10, 10, 20, 20], [0, 0, 5, 5], [[10, 10, 20, 20]]);
    +  // B overlaps top half of A.
    +  assertDifference([10, 10, 20, 20], [5, 15, 25, 25],
    +      [[10, 10, 20, 15]]);
    +  // B overlaps bottom half of A.
    +  assertDifference([10, 10, 20, 20], [5, 5, 25, 15],
    +      [[10, 15, 20, 20]]);
    +  // B overlaps right half of A.
    +  assertDifference([10, 10, 20, 20], [15, 5, 25, 25],
    +      [[10, 10, 15, 20]]);
    +  // B overlaps left half of A.
    +  assertDifference([10, 10, 20, 20], [5, 5, 15, 25],
    +      [[15, 10, 20, 20]]);
    +  // B touches A at its bottom right corner
    +  assertDifference([10, 10, 20, 20], [20, 20, 30, 30],
    +      [[10, 10, 20, 20]]);
    +  // B touches A at its top left corner
    +  assertDifference([10, 10, 20, 20], [5, 5, 10, 10],
    +      [[10, 10, 20, 20]]);
    +  // B touches A along its bottom edge
    +  assertDifference([10, 10, 20, 20], [12, 20, 17, 25],
    +      [[10, 10, 20, 20]]);
    +  // B splits A horizontally.
    +  assertDifference([10, 10, 20, 20], [5, 12, 25, 18],
    +      [[10, 10, 20, 12], [10, 18, 20, 20]]);
    +  // B splits A vertically.
    +  assertDifference([10, 10, 20, 20], [12, 5, 18, 25],
    +      [[10, 10, 12, 20], [18, 10, 20, 20]]);
    +  // B subtracts a notch from the top of A.
    +  assertDifference([10, 10, 20, 20], [12, 5, 18, 15],
    +      [[10, 15, 20, 20], [10, 10, 12, 15], [18, 10, 20, 15]]);
    +  // B subtracts a notch from the bottom left of A
    +  assertDifference([1, 6, 3, 9], [1, 7, 2, 9],
    +      [[1, 6, 3, 7], [2, 7, 3, 9]]);
    +  // B subtracts a notch from the bottom right of A
    +  assertDifference([1, 6, 3, 9], [2, 7, 3, 9],
    +      [[1, 6, 3, 7], [1, 7, 2, 9]]);
    +  // B subtracts a notch from the top left of A
    +  assertDifference([1, 6, 3, 9], [1, 6, 2, 8],
    +      [[1, 8, 3, 9], [2, 6, 3, 8]]);
    +  // B subtracts a notch from the top left of A (no coinciding edge)
    +  assertDifference([1, 6, 3, 9], [0, 5, 2, 8],
    +      [[1, 8, 3, 9], [2, 6, 3, 8]]);
    +  // B subtracts a hole from the center of A.
    +  assertDifference([-20, -20, -10, -10], [-18, -18, -12, -12],
    +      [[-20, -20, -10, -18], [-20, -12, -10, -10],
    +       [-20, -18, -18, -12], [-12, -18, -10, -12]]);
    +}
    +
    +function assertDifference(a, b, expected) {
    +  var r0 = createRect(a);
    +  var r1 = createRect(b);
    +  var diff = goog.math.Rect.difference(r0, r1);
    +
    +  assertEquals('Wrong number of rectangles in difference ',
    +      expected.length, diff.length);
    +
    +  for (var j = 0; j < expected.length; ++j) {
    +    var e = createRect(expected[j]);
    +    if (!goog.math.Rect.equals(e, diff[j])) {
    +      alert(j + ': ' + e + ' != ' + diff[j]);
    +    }
    +    assertRectsEqual(e, diff[j]);
    +  }
    +
    +  // Test in place version
    +  var diff = r0.difference(r1);
    +
    +  assertEquals('Wrong number of rectangles in in-place difference ',
    +      expected.length, diff.length);
    +
    +  for (var j = 0; j < expected.length; ++j) {
    +    var e = createRect(expected[j]);
    +    if (!goog.math.Rect.equals(e, diff[j])) {
    +      alert(j + ': ' + e + ' != ' + diff[j]);
    +    }
    +    assertRectsEqual(e, diff[j]);
    +  }
    +}
    +
    +function testRectToBox() {
    +  var r = new goog.math.Rect(0, 0, 0, 0);
    +  assertObjectEquals(new goog.math.Box(0, 0, 0, 0), r.toBox());
    +
    +  r.top = 10;
    +  r.left = 10;
    +  r.width = 20;
    +  r.height = 20;
    +  assertObjectEquals(new goog.math.Box(10, 30, 30, 10), r.toBox());
    +
    +  r.top = -10;
    +  r.left = 0;
    +  r.width = 10;
    +  r.height = 10;
    +  assertObjectEquals(new goog.math.Box(-10, 10, 0, 0), r.toBox());
    +}
    +
    +function testBoxToRect() {
    +  var box = new goog.math.Box(0, 0, 0, 0);
    +  assertObjectEquals(new goog.math.Rect(0, 0, 0, 0),
    +                     goog.math.Rect.createFromBox(box));
    +
    +  box.top = 10;
    +  box.left = 15;
    +  box.right = 23;
    +  box.bottom = 27;
    +  assertObjectEquals(new goog.math.Rect(15, 10, 8, 17),
    +                     goog.math.Rect.createFromBox(box));
    +
    +  box.top = -10;
    +  box.left = 3;
    +  box.right = 12;
    +  box.bottom = 7;
    +  assertObjectEquals(new goog.math.Rect(3, -10, 9, 17),
    +                     goog.math.Rect.createFromBox(box));
    +}
    +
    +function testBoxToRectAndBack() {
    +  rectToBoxAndBackTest(new goog.math.Rect(8, 11, 20, 23));
    +  rectToBoxAndBackTest(new goog.math.Rect(9, 13, NaN, NaN));
    +  rectToBoxAndBackTest(new goog.math.Rect(10, 13, NaN, 21));
    +  rectToBoxAndBackTest(new goog.math.Rect(5, 7, 14, NaN));
    +}
    +
    +function rectToBoxAndBackTest(rect) {
    +  var box = rect.toBox();
    +  var rect2 = goog.math.Rect.createFromBox(box);
    +
    +  // Use toString for this test since otherwise NaN != NaN.
    +  assertObjectEquals(rect.toString(), rect2.toString());
    +}
    +
    +function testRectToBoxAndBack() {
    +  // This doesn't work if left or top is undefined.
    +  boxToRectAndBackTest(new goog.math.Box(11, 13, 20, 17));
    +  boxToRectAndBackTest(new goog.math.Box(10, NaN, NaN, 11));
    +  boxToRectAndBackTest(new goog.math.Box(9, 14, NaN, 11));
    +  boxToRectAndBackTest(new goog.math.Box(10, NaN, 22, 15));
    +}
    +
    +function boxToRectAndBackTest(box) {
    +  var rect = goog.math.Rect.createFromBox(box);
    +  var box2 = rect.toBox();
    +
    +  // Use toString for this test since otherwise NaN != NaN.
    +  assertEquals(box.toString(), box2.toString());
    +}
    +
    +function testRectContainsRect() {
    +  var r = new goog.math.Rect(-10, 0, 20, 10);
    +  assertTrue(r.contains(r));
    +  assertFalse(r.contains(new goog.math.Rect(NaN, NaN, NaN, NaN)));
    +  var r2 = new goog.math.Rect(0, 2, 5, 5);
    +  assertTrue(r.contains(r2));
    +  assertFalse(r2.contains(r));
    +  r2.left = -11;
    +  assertFalse(r.contains(r2));
    +  r2.left = 0;
    +  r2.width = 15;
    +  assertFalse(r.contains(r2));
    +  r2.width = 5;
    +  r2.height = 10;
    +  assertFalse(r.contains(r2));
    +  r2.top = 0;
    +  assertTrue(r.contains(r2));
    +}
    +
    +function testRectContainsCoordinate() {
    +  var r = new goog.math.Rect(20, 40, 60, 80);
    +
    +  // Test middle.
    +  assertTrue(r.contains(new goog.math.Coordinate(50, 80)));
    +
    +  // Test edges.
    +  assertTrue(r.contains(new goog.math.Coordinate(20, 40)));
    +  assertTrue(r.contains(new goog.math.Coordinate(50, 40)));
    +  assertTrue(r.contains(new goog.math.Coordinate(80, 40)));
    +  assertTrue(r.contains(new goog.math.Coordinate(80, 80)));
    +  assertTrue(r.contains(new goog.math.Coordinate(80, 120)));
    +  assertTrue(r.contains(new goog.math.Coordinate(50, 120)));
    +  assertTrue(r.contains(new goog.math.Coordinate(20, 120)));
    +  assertTrue(r.contains(new goog.math.Coordinate(20, 80)));
    +
    +  // Test outside.
    +  assertFalse(r.contains(new goog.math.Coordinate(0, 0)));
    +  assertFalse(r.contains(new goog.math.Coordinate(50, 0)));
    +  assertFalse(r.contains(new goog.math.Coordinate(100, 0)));
    +  assertFalse(r.contains(new goog.math.Coordinate(100, 80)));
    +  assertFalse(r.contains(new goog.math.Coordinate(100, 160)));
    +  assertFalse(r.contains(new goog.math.Coordinate(50, 160)));
    +  assertFalse(r.contains(new goog.math.Coordinate(0, 160)));
    +  assertFalse(r.contains(new goog.math.Coordinate(0, 80)));
    +}
    +
    +function testGetSize() {
    +  assertObjectEquals(new goog.math.Size(60, 80),
    +                     new goog.math.Rect(20, 40, 60, 80).getSize());
    +}
    +
    +function testGetBottomRight() {
    +  assertObjectEquals(new goog.math.Coordinate(40, 60),
    +                     new goog.math.Rect(10, 20, 30, 40).getBottomRight());
    +}
    +
    +function testGetCenter() {
    +  assertObjectEquals(new goog.math.Coordinate(25, 40),
    +                     new goog.math.Rect(10, 20, 30, 40).getCenter());
    +}
    +
    +function testGetTopLeft() {
    +  assertObjectEquals(new goog.math.Coordinate(10, 20),
    +                     new goog.math.Rect(10, 20, 30, 40).getTopLeft());
    +}
    +
    +function testRectCeil() {
    +  var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.ceil());
    +  assertRectsEqual(new goog.math.Rect(12, 27, 18, 10), rect);
    +}
    +
    +function testRectFloor() {
    +  var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.floor());
    +  assertRectsEqual(new goog.math.Rect(11, 26, 17, 9), rect);
    +}
    +
    +function testRectRound() {
    +  var rect = new goog.math.Rect(11.4, 26.6, 17.8, 9.2);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.round());
    +  assertRectsEqual(new goog.math.Rect(11, 27, 18, 9), rect);
    +}
    +
    +function testRectTranslateCoordinate() {
    +  var rect = new goog.math.Rect(10, 40, 30, 20);
    +  var c = new goog.math.Coordinate(10, 5);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.translate(c));
    +  assertRectsEqual(new goog.math.Rect(20, 45, 30, 20), rect);
    +}
    +
    +function testRectTranslateXY() {
    +  var rect = new goog.math.Rect(10, 20, 40, 35);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.translate(15, 10));
    +  assertRectsEqual(new goog.math.Rect(25, 30, 40, 35), rect);
    +}
    +
    +function testRectTranslateX() {
    +  var rect = new goog.math.Rect(12, 34, 113, 88);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.translate(10));
    +  assertRectsEqual(new goog.math.Rect(22, 34, 113, 88), rect);
    +}
    +
    +function testRectScaleXY() {
    +  var rect = new goog.math.Rect(10, 30, 100, 60);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.scale(2, 5));
    +  assertRectsEqual(new goog.math.Rect(20, 150, 200, 300), rect);
    +}
    +
    +function testRectScaleFactor() {
    +  var rect = new goog.math.Rect(12, 34, 113, 88);
    +  assertEquals('The function should return the target instance',
    +      rect, rect.scale(10));
    +  assertRectsEqual(new goog.math.Rect(120, 340, 1130, 880), rect);
    +}
    +
    +function testSquaredDistance() {
    +  var rect = new goog.math.Rect(-10, -20, 15, 25);
    +
    +  // Test regions:
    +  // 1  2  3
    +  //   +-+
    +  // 4 |5| 6
    +  //   +-+
    +  // 7  8  9
    +
    +  // Region 5 (inside the rectangle).
    +  assertEquals(0, rect.squaredDistance(new goog.math.Coordinate(-10, 5)));
    +  assertEquals(0, rect.squaredDistance(new goog.math.Coordinate(5, -20)));
    +
    +  // 1, 2, and 3.
    +  assertEquals(25, rect.squaredDistance(new goog.math.Coordinate(9, 8)));
    +  assertEquals(36, rect.squaredDistance(new goog.math.Coordinate(2, 11)));
    +  assertEquals(53, rect.squaredDistance(new goog.math.Coordinate(12, 7)));
    +
    +  // 4 and 6.
    +  assertEquals(81, rect.squaredDistance(new goog.math.Coordinate(-19, -10)));
    +  assertEquals(64, rect.squaredDistance(new goog.math.Coordinate(13, 0)));
    +
    +  // 7, 8, and 9.
    +  assertEquals(20, rect.squaredDistance(new goog.math.Coordinate(-12, -24)));
    +  assertEquals(9, rect.squaredDistance(new goog.math.Coordinate(0, -23)));
    +  assertEquals(34, rect.squaredDistance(new goog.math.Coordinate(8, -25)));
    +}
    +
    +
    +function testDistance() {
    +  var rect = new goog.math.Rect(2, 4, 8, 16);
    +
    +  // Region 5 (inside the rectangle).
    +  assertEquals(0, rect.distance(new goog.math.Coordinate(2, 4)));
    +  assertEquals(0, rect.distance(new goog.math.Coordinate(10, 20)));
    +
    +  // 1, 2, and 3.
    +  assertRoughlyEquals(
    +      Math.sqrt(8), rect.distance(new goog.math.Coordinate(0, 22)), .0001);
    +  assertEquals(8, rect.distance(new goog.math.Coordinate(9, 28)));
    +  assertRoughlyEquals(
    +      Math.sqrt(50), rect.distance(new goog.math.Coordinate(15, 25)), .0001);
    +
    +  // 4 and 6.
    +  assertEquals(7, rect.distance(new goog.math.Coordinate(-5, 6)));
    +  assertEquals(10, rect.distance(new goog.math.Coordinate(20, 10)));
    +
    +  // 7, 8, and 9.
    +  assertEquals(5, rect.distance(new goog.math.Coordinate(-2, 1)));
    +  assertEquals(2, rect.distance(new goog.math.Coordinate(5, 2)));
    +  assertRoughlyEquals(
    +      Math.sqrt(10), rect.distance(new goog.math.Coordinate(1, 1)), .0001);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/size.js b/mapbox-gl/closure-library/closure/goog/math/size.js
    new file mode 100644
    index 0000000000..2bf29f68a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/size.js
    @@ -0,0 +1,208 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for representing two-dimensional sizes.
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +
    +goog.provide('goog.math.Size');
    +
    +
    +
    +/**
    + * Class for representing sizes consisting of a width and height. Undefined
    + * width and height support is deprecated and results in compiler warning.
    + * @param {number} width Width.
    + * @param {number} height Height.
    + * @struct
    + * @constructor
    + */
    +goog.math.Size = function(width, height) {
    +  /**
    +   * Width
    +   * @type {number}
    +   */
    +  this.width = width;
    +
    +  /**
    +   * Height
    +   * @type {number}
    +   */
    +  this.height = height;
    +};
    +
    +
    +/**
    + * Compares sizes for equality.
    + * @param {goog.math.Size} a A Size.
    + * @param {goog.math.Size} b A Size.
    + * @return {boolean} True iff the sizes have equal widths and equal
    + *     heights, or if both are null.
    + */
    +goog.math.Size.equals = function(a, b) {
    +  if (a == b) {
    +    return true;
    +  }
    +  if (!a || !b) {
    +    return false;
    +  }
    +  return a.width == b.width && a.height == b.height;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Size} A new copy of the Size.
    + */
    +goog.math.Size.prototype.clone = function() {
    +  return new goog.math.Size(this.width, this.height);
    +};
    +
    +
    +if (goog.DEBUG) {
    +  /**
    +   * Returns a nice string representing size.
    +   * @return {string} In the form (50 x 73).
    +   * @override
    +   */
    +  goog.math.Size.prototype.toString = function() {
    +    return '(' + this.width + ' x ' + this.height + ')';
    +  };
    +}
    +
    +
    +/**
    + * @return {number} The longer of the two dimensions in the size.
    + */
    +goog.math.Size.prototype.getLongest = function() {
    +  return Math.max(this.width, this.height);
    +};
    +
    +
    +/**
    + * @return {number} The shorter of the two dimensions in the size.
    + */
    +goog.math.Size.prototype.getShortest = function() {
    +  return Math.min(this.width, this.height);
    +};
    +
    +
    +/**
    + * @return {number} The area of the size (width * height).
    + */
    +goog.math.Size.prototype.area = function() {
    +  return this.width * this.height;
    +};
    +
    +
    +/**
    + * @return {number} The perimeter of the size (width + height) * 2.
    + */
    +goog.math.Size.prototype.perimeter = function() {
    +  return (this.width + this.height) * 2;
    +};
    +
    +
    +/**
    + * @return {number} The ratio of the size's width to its height.
    + */
    +goog.math.Size.prototype.aspectRatio = function() {
    +  return this.width / this.height;
    +};
    +
    +
    +/**
    + * @return {boolean} True if the size has zero area, false if both dimensions
    + *     are non-zero numbers.
    + */
    +goog.math.Size.prototype.isEmpty = function() {
    +  return !this.area();
    +};
    +
    +
    +/**
    + * Clamps the width and height parameters upward to integer values.
    + * @return {!goog.math.Size} This size with ceil'd components.
    + */
    +goog.math.Size.prototype.ceil = function() {
    +  this.width = Math.ceil(this.width);
    +  this.height = Math.ceil(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * @param {!goog.math.Size} target The target size.
    + * @return {boolean} True if this Size is the same size or smaller than the
    + *     target size in both dimensions.
    + */
    +goog.math.Size.prototype.fitsInside = function(target) {
    +  return this.width <= target.width && this.height <= target.height;
    +};
    +
    +
    +/**
    + * Clamps the width and height parameters downward to integer values.
    + * @return {!goog.math.Size} This size with floored components.
    + */
    +goog.math.Size.prototype.floor = function() {
    +  this.width = Math.floor(this.width);
    +  this.height = Math.floor(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * Rounds the width and height parameters to integer values.
    + * @return {!goog.math.Size} This size with rounded components.
    + */
    +goog.math.Size.prototype.round = function() {
    +  this.width = Math.round(this.width);
    +  this.height = Math.round(this.height);
    +  return this;
    +};
    +
    +
    +/**
    + * Scales this size by the given scale factors. The width and height are scaled
    + * by {@code sx} and {@code opt_sy} respectively.  If {@code opt_sy} is not
    + * given, then {@code sx} is used for both the width and height.
    + * @param {number} sx The scale factor to use for the width.
    + * @param {number=} opt_sy The scale factor to use for the height.
    + * @return {!goog.math.Size} This Size object after scaling.
    + */
    +goog.math.Size.prototype.scale = function(sx, opt_sy) {
    +  var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
    +  this.width *= sx;
    +  this.height *= sy;
    +  return this;
    +};
    +
    +
    +/**
    + * Uniformly scales the size to fit inside the dimensions of a given size. The
    + * original aspect ratio will be preserved.
    + *
    + * This function assumes that both Sizes contain strictly positive dimensions.
    + * @param {!goog.math.Size} target The target size.
    + * @return {!goog.math.Size} This Size object, after optional scaling.
    + */
    +goog.math.Size.prototype.scaleToFit = function(target) {
    +  var s = this.aspectRatio() > target.aspectRatio() ?
    +      target.width / this.width :
    +      target.height / this.height;
    +
    +  return this.scale(s);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/size_test.html b/mapbox-gl/closure-library/closure/goog/math/size_test.html
    new file mode 100644
    index 0000000000..209ce840c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/size_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Size
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.SizeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/size_test.js b/mapbox-gl/closure-library/closure/goog/math/size_test.js
    new file mode 100644
    index 0000000000..7ff1c451de
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/size_test.js
    @@ -0,0 +1,175 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.SizeTest');
    +goog.setTestOnly('goog.math.SizeTest');
    +
    +goog.require('goog.math.Size');
    +goog.require('goog.testing.jsunit');
    +
    +function testSize1() {
    +  var s = new goog.math.Size(undefined, undefined);
    +  assertUndefined(s.width);
    +  assertUndefined(s.height);
    +  assertEquals('(undefined x undefined)', s.toString());
    +}
    +
    +function testSize3() {
    +  var s = new goog.math.Size(10, 20);
    +  assertEquals(10, s.width);
    +  assertEquals(20, s.height);
    +  assertEquals('(10 x 20)', s.toString());
    +}
    +
    +function testSize4() {
    +  var s = new goog.math.Size(10.5, 20.897);
    +  assertEquals(10.5, s.width);
    +  assertEquals(20.897, s.height);
    +  assertEquals('(10.5 x 20.897)', s.toString());
    +}
    +
    +function testSizeClone() {
    +  var s = new goog.math.Size(undefined, undefined);
    +  assertEquals(s.toString(), s.clone().toString());
    +  s.width = 4;
    +  s.height = 5;
    +  assertEquals(s.toString(), s.clone().toString());
    +}
    +
    +function testSizeEquals() {
    +  var a = new goog.math.Size(4, 5);
    +
    +  assertTrue(goog.math.Size.equals(a, a));
    +  assertFalse(goog.math.Size.equals(a, null));
    +  assertFalse(goog.math.Size.equals(null, a));
    +
    +  var b = new goog.math.Size(4, 5);
    +  var c = new goog.math.Size(4, 6);
    +  assertTrue(goog.math.Size.equals(a, b));
    +  assertFalse(goog.math.Size.equals(a, c));
    +}
    +
    +function testSizeArea() {
    +  var s = new goog.math.Size(4, 5);
    +  assertEquals(20, s.area());
    +}
    +
    +function testSizePerimeter() {
    +  var s = new goog.math.Size(4, 5);
    +  assertEquals(18, s.perimeter());
    +}
    +
    +function testSizeAspectRatio() {
    +  var s = new goog.math.Size(undefined, undefined);
    +  assertNaN(s.aspectRatio());
    +
    +  s.width = 4;
    +  s.height = 0;
    +  assertEquals(Infinity, s.aspectRatio());
    +
    +  s.height = 5;
    +  assertEquals(0.8, s.aspectRatio());
    +}
    +
    +function testSizeFitsInside() {
    +  var target = new goog.math.Size(10, 10);
    +
    +  var a = new goog.math.Size(5, 8);
    +  var b = new goog.math.Size(5, 12);
    +  var c = new goog.math.Size(19, 7);
    +
    +
    +  assertTrue(a.fitsInside(target));
    +  assertFalse(b.fitsInside(target));
    +  assertFalse(c.fitsInside(target));
    +}
    +
    +function testSizeScaleToFit() {
    +  var target = new goog.math.Size(512, 640);
    +
    +  var a = new goog.math.Size(1600, 1200);
    +  var b = new goog.math.Size(1200, 1600);
    +  var c = new goog.math.Size(400, 300);
    +  var d = new goog.math.Size(undefined, undefined);
    +
    +  assertEquals('(512 x 384)', a.scaleToFit(target).toString());
    +  assertEquals('(480 x 640)', b.scaleToFit(target).toString());
    +  assertEquals('(512 x 384)', c.scaleToFit(target).toString());
    +  assertEquals('(512 x 640)', target.scaleToFit(target).toString());
    +
    +  assertEquals('(NaN x NaN)', d.scaleToFit(target).toString());
    +  assertEquals('(NaN x NaN)', a.scaleToFit(d).toString());
    +}
    +
    +function testSizeIsEmpty() {
    +  var s = new goog.math.Size(undefined, undefined);
    +  assertTrue(s.isEmpty());
    +  s.width = 0;
    +  s.height = 5;
    +  assertTrue(s.isEmpty());
    +  s.width = 4;
    +  assertFalse(s.isEmpty());
    +}
    +
    +function testSizeScaleFactor() {
    +  var s = new goog.math.Size(4, 5);
    +  assertEquals('(8 x 10)', s.scale(2).toString());
    +  assertEquals('(0.8 x 1)', s.scale(0.1).toString());
    +}
    +
    +function testSizeCeil() {
    +  var s = new goog.math.Size(2.3, 4.7);
    +  assertEquals('(3 x 5)', s.ceil().toString());
    +}
    +
    +function testSizeFloor() {
    +  var s = new goog.math.Size(2.3, 4.7);
    +  assertEquals('(2 x 4)', s.floor().toString());
    +}
    +
    +function testSizeRound() {
    +  var s = new goog.math.Size(2.3, 4.7);
    +  assertEquals('(2 x 5)', s.round().toString());
    +}
    +
    +function testSizeGetLongest() {
    +  var s = new goog.math.Size(3, 4);
    +  assertEquals(4, s.getLongest());
    +
    +  s.height = 3;
    +  assertEquals(3, s.getLongest());
    +
    +  s.height = 2;
    +  assertEquals(3, s.getLongest());
    +
    +  assertNaN(new goog.math.Size(undefined, undefined).getLongest());
    +}
    +
    +function testSizeGetShortest() {
    +  var s = new goog.math.Size(3, 4);
    +  assertEquals(3, s.getShortest());
    +
    +  s.height = 3;
    +  assertEquals(3, s.getShortest());
    +
    +  s.height = 2;
    +  assertEquals(2, s.getShortest());
    +
    +  assertNaN(new goog.math.Size(undefined, undefined).getShortest());
    +}
    +
    +function testSizeScaleXY() {
    +  var s = new goog.math.Size(5, 10);
    +  assertEquals('(20 x 30)', s.scale(4, 3).toString());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/tdma.js b/mapbox-gl/closure-library/closure/goog/math/tdma.js
    new file mode 100644
    index 0000000000..6f2f5e8115
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/tdma.js
    @@ -0,0 +1,73 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The Tridiagonal matrix algorithm solver solves a special
    + * version of a sparse linear system Ax = b where A is tridiagonal.
    + *
    + * See http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
    + *
    + */
    +
    +goog.provide('goog.math.tdma');
    +
    +
    +/**
    + * Solves a linear system where the matrix is square tri-diagonal. That is,
    + * given a system of equations:
    + *
    + * A * result = vecRight,
    + *
    + * this class computes result = inv(A) * vecRight, where A has the special form
    + * of a tri-diagonal matrix:
    + *
    + *    |dia(0) sup(0)   0    0     ...   0|
    + *    |sub(0) dia(1) sup(1) 0     ...   0|
    + * A =|                ...               |
    + *    |0 ... 0 sub(n-2) dia(n-1) sup(n-1)|
    + *    |0 ... 0    0     sub(n-1)   dia(n)|
    + *
    + * @param {!Array<number>} subDiag The sub diagonal of the matrix.
    + * @param {!Array<number>} mainDiag The main diagonal of the matrix.
    + * @param {!Array<number>} supDiag The super diagonal of the matrix.
    + * @param {!Array<number>} vecRight The right vector of the system
    + *     of equations.
    + * @param {Array<number>=} opt_result The optional array to store the result.
    + * @return {!Array<number>} The vector that is the solution to the system.
    + */
    +goog.math.tdma.solve = function(
    +    subDiag, mainDiag, supDiag, vecRight, opt_result) {
    +  // Make a local copy of the main diagonal and the right vector.
    +  mainDiag = mainDiag.slice();
    +  vecRight = vecRight.slice();
    +
    +  // The dimension of the matrix.
    +  var nDim = mainDiag.length;
    +
    +  // Construct a modified linear system of equations with the same solution
    +  // as the input one.
    +  for (var i = 1; i < nDim; ++i) {
    +    var m = subDiag[i - 1] / mainDiag[i - 1];
    +    mainDiag[i] = mainDiag[i] - m * supDiag[i - 1];
    +    vecRight[i] = vecRight[i] - m * vecRight[i - 1];
    +  }
    +
    +  // Solve the new system of equations by simple back-substitution.
    +  var result = opt_result || new Array(vecRight.length);
    +  result[nDim - 1] = vecRight[nDim - 1] / mainDiag[nDim - 1];
    +  for (i = nDim - 2; i >= 0; --i) {
    +    result[i] = (vecRight[i] - supDiag[i] * result[i + 1]) / mainDiag[i];
    +  }
    +  return result;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/tdma_test.html b/mapbox-gl/closure-library/closure/goog/math/tdma_test.html
    new file mode 100644
    index 0000000000..3b7131b97a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/tdma_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.tdma
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.tdmaTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/tdma_test.js b/mapbox-gl/closure-library/closure/goog/math/tdma_test.js
    new file mode 100644
    index 0000000000..5338980b54
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/tdma_test.js
    @@ -0,0 +1,30 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.tdmaTest');
    +goog.setTestOnly('goog.math.tdmaTest');
    +
    +goog.require('goog.math.tdma');
    +goog.require('goog.testing.jsunit');
    +
    +function testTdmaSolver() {
    +  var supDiag = [1, 1, 1, 1, 1];
    +  var mainDiag = [-1, -2, -2, -2, -2, -2];
    +  var subDiag = [1, 1, 1, 1, 1];
    +  var vecRight = [1, 2, 3, 4, 5, 6];
    +  var expected = [-56, -55, -52, -46, -36, -21];
    +  var result = [];
    +  goog.math.tdma.solve(subDiag, mainDiag, supDiag, vecRight, result);
    +  assertElementsEquals(expected, result);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec2.js b/mapbox-gl/closure-library/closure/goog/math/vec2.js
    new file mode 100644
    index 0000000000..9b47a3a592
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec2.js
    @@ -0,0 +1,284 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines a 2-element vector class that can be used for
    + * coordinate math, useful for animation systems and point manipulation.
    + *
    + * Vec2 objects inherit from goog.math.Coordinate and may be used wherever a
    + * Coordinate is required. Where appropriate, Vec2 functions accept both Vec2
    + * and Coordinate objects as input.
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +goog.provide('goog.math.Vec2');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate');
    +
    +
    +
    +/**
    + * Class for a two-dimensional vector object and assorted functions useful for
    + * manipulating points.
    + *
    + * @param {number} x The x coordinate for the vector.
    + * @param {number} y The y coordinate for the vector.
    + * @struct
    + * @constructor
    + * @extends {goog.math.Coordinate}
    + */
    +goog.math.Vec2 = function(x, y) {
    +  /**
    +   * X-value
    +   * @type {number}
    +   */
    +  this.x = x;
    +
    +  /**
    +   * Y-value
    +   * @type {number}
    +   */
    +  this.y = y;
    +};
    +goog.inherits(goog.math.Vec2, goog.math.Coordinate);
    +
    +
    +/**
    + * @return {!goog.math.Vec2} A random unit-length vector.
    + */
    +goog.math.Vec2.randomUnit = function() {
    +  var angle = Math.random() * Math.PI * 2;
    +  return new goog.math.Vec2(Math.cos(angle), Math.sin(angle));
    +};
    +
    +
    +/**
    + * @return {!goog.math.Vec2} A random vector inside the unit-disc.
    + */
    +goog.math.Vec2.random = function() {
    +  var mag = Math.sqrt(Math.random());
    +  var angle = Math.random() * Math.PI * 2;
    +
    +  return new goog.math.Vec2(Math.cos(angle) * mag, Math.sin(angle) * mag);
    +};
    +
    +
    +/**
    + * Returns a new Vec2 object from a given coordinate.
    + * @param {!goog.math.Coordinate} a The coordinate.
    + * @return {!goog.math.Vec2} A new vector object.
    + */
    +goog.math.Vec2.fromCoordinate = function(a) {
    +  return new goog.math.Vec2(a.x, a.y);
    +};
    +
    +
    +/**
    + * @return {!goog.math.Vec2} A new vector with the same coordinates as this one.
    + * @override
    + */
    +goog.math.Vec2.prototype.clone = function() {
    +  return new goog.math.Vec2(this.x, this.y);
    +};
    +
    +
    +/**
    + * Returns the magnitude of the vector measured from the origin.
    + * @return {number} The length of the vector.
    + */
    +goog.math.Vec2.prototype.magnitude = function() {
    +  return Math.sqrt(this.x * this.x + this.y * this.y);
    +};
    +
    +
    +/**
    + * Returns the squared magnitude of the vector measured from the origin.
    + * NOTE(brenneman): Leaving out the square root is not a significant
    + * optimization in JavaScript.
    + * @return {number} The length of the vector, squared.
    + */
    +goog.math.Vec2.prototype.squaredMagnitude = function() {
    +  return this.x * this.x + this.y * this.y;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Vec2} This coordinate after scaling.
    + * @override
    + */
    +goog.math.Vec2.prototype.scale =
    +    /** @type {function(number, number=):!goog.math.Vec2} */
    +    (goog.math.Coordinate.prototype.scale);
    +
    +
    +/**
    + * Reverses the sign of the vector. Equivalent to scaling the vector by -1.
    + * @return {!goog.math.Vec2} The inverted vector.
    + */
    +goog.math.Vec2.prototype.invert = function() {
    +  this.x = -this.x;
    +  this.y = -this.y;
    +  return this;
    +};
    +
    +
    +/**
    + * Normalizes the current vector to have a magnitude of 1.
    + * @return {!goog.math.Vec2} The normalized vector.
    + */
    +goog.math.Vec2.prototype.normalize = function() {
    +  return this.scale(1 / this.magnitude());
    +};
    +
    +
    +/**
    + * Adds another vector to this vector in-place.
    + * @param {!goog.math.Coordinate} b The vector to add.
    + * @return {!goog.math.Vec2}  This vector with {@code b} added.
    + */
    +goog.math.Vec2.prototype.add = function(b) {
    +  this.x += b.x;
    +  this.y += b.y;
    +  return this;
    +};
    +
    +
    +/**
    + * Subtracts another vector from this vector in-place.
    + * @param {!goog.math.Coordinate} b The vector to subtract.
    + * @return {!goog.math.Vec2} This vector with {@code b} subtracted.
    + */
    +goog.math.Vec2.prototype.subtract = function(b) {
    +  this.x -= b.x;
    +  this.y -= b.y;
    +  return this;
    +};
    +
    +
    +/**
    + * Rotates this vector in-place by a given angle, specified in radians.
    + * @param {number} angle The angle, in radians.
    + * @return {!goog.math.Vec2} This vector rotated {@code angle} radians.
    + */
    +goog.math.Vec2.prototype.rotate = function(angle) {
    +  var cos = Math.cos(angle);
    +  var sin = Math.sin(angle);
    +  var newX = this.x * cos - this.y * sin;
    +  var newY = this.y * cos + this.x * sin;
    +  this.x = newX;
    +  this.y = newY;
    +  return this;
    +};
    +
    +
    +/**
    + * Rotates a vector by a given angle, specified in radians, relative to a given
    + * axis rotation point. The returned vector is a newly created instance - no
    + * in-place changes are done.
    + * @param {!goog.math.Vec2} v A vector.
    + * @param {!goog.math.Vec2} axisPoint The rotation axis point.
    + * @param {number} angle The angle, in radians.
    + * @return {!goog.math.Vec2} The rotated vector in a newly created instance.
    + */
    +goog.math.Vec2.rotateAroundPoint = function(v, axisPoint, angle) {
    +  var res = v.clone();
    +  return res.subtract(axisPoint).rotate(angle).add(axisPoint);
    +};
    +
    +
    +/**
    + * Compares this vector with another for equality.
    + * @param {!goog.math.Vec2} b The other vector.
    + * @return {boolean} Whether this vector has the same x and y as the given
    + *     vector.
    + */
    +goog.math.Vec2.prototype.equals = function(b) {
    +  return this == b || !!b && this.x == b.x && this.y == b.y;
    +};
    +
    +
    +/**
    + * Returns the distance between two vectors.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {number} The distance.
    + */
    +goog.math.Vec2.distance = goog.math.Coordinate.distance;
    +
    +
    +/**
    + * Returns the squared distance between two vectors.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {number} The squared distance.
    + */
    +goog.math.Vec2.squaredDistance = goog.math.Coordinate.squaredDistance;
    +
    +
    +/**
    + * Compares vectors for equality.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {boolean} Whether the vectors have the same x and y coordinates.
    + */
    +goog.math.Vec2.equals = goog.math.Coordinate.equals;
    +
    +
    +/**
    + * Returns the sum of two vectors as a new Vec2.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {!goog.math.Vec2} The sum vector.
    + */
    +goog.math.Vec2.sum = function(a, b) {
    +  return new goog.math.Vec2(a.x + b.x, a.y + b.y);
    +};
    +
    +
    +/**
    + * Returns the difference between two vectors as a new Vec2.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {!goog.math.Vec2} The difference vector.
    + */
    +goog.math.Vec2.difference = function(a, b) {
    +  return new goog.math.Vec2(a.x - b.x, a.y - b.y);
    +};
    +
    +
    +/**
    + * Returns the dot-product of two vectors.
    + * @param {!goog.math.Coordinate} a The first vector.
    + * @param {!goog.math.Coordinate} b The second vector.
    + * @return {number} The dot-product of the two vectors.
    + */
    +goog.math.Vec2.dot = function(a, b) {
    +  return a.x * b.x + a.y * b.y;
    +};
    +
    +
    +/**
    + * Returns a new Vec2 that is the linear interpolant between vectors a and b at
    + * scale-value x.
    + * @param {!goog.math.Coordinate} a Vector a.
    + * @param {!goog.math.Coordinate} b Vector b.
    + * @param {number} x The proportion between a and b.
    + * @return {!goog.math.Vec2} The interpolated vector.
    + */
    +goog.math.Vec2.lerp = function(a, b, x) {
    +  return new goog.math.Vec2(goog.math.lerp(a.x, b.x, x),
    +                            goog.math.lerp(a.y, b.y, x));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec2_test.html b/mapbox-gl/closure-library/closure/goog/math/vec2_test.html
    new file mode 100644
    index 0000000000..c9ff08a048
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec2_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Vec2
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.Vec2Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec2_test.js b/mapbox-gl/closure-library/closure/goog/math/vec2_test.js
    new file mode 100644
    index 0000000000..571e716387
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec2_test.js
    @@ -0,0 +1,220 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.Vec2Test');
    +goog.setTestOnly('goog.math.Vec2Test');
    +
    +goog.require('goog.math.Vec2');
    +goog.require('goog.testing.jsunit');
    +
    +function assertVectorEquals(a, b) {
    +  assertTrue(b + ' should be equal to ' + a, goog.math.Vec2.equals(a, b));
    +}
    +
    +
    +function testVec2() {
    +  var v = new goog.math.Vec2(3.14, 2.78);
    +  assertEquals(3.14, v.x);
    +  assertEquals(2.78, v.y);
    +}
    +
    +
    +function testRandomUnit() {
    +  var a = goog.math.Vec2.randomUnit();
    +  assertRoughlyEquals(1.0, a.magnitude(), 1e-10);
    +}
    +
    +
    +function testRandom() {
    +  var a = goog.math.Vec2.random();
    +  assertTrue(a.magnitude() <= 1.0);
    +}
    +
    +
    +function testClone() {
    +  var a = new goog.math.Vec2(1, 2);
    +  var b = a.clone();
    +
    +  assertEquals(a.x, b.x);
    +  assertEquals(a.y, b.y);
    +}
    +
    +
    +function testMagnitude() {
    +  var a = new goog.math.Vec2(0, 10);
    +  var b = new goog.math.Vec2(3, 4);
    +
    +  assertEquals(10, a.magnitude());
    +  assertEquals(5, b.magnitude());
    +}
    +
    +
    +function testSquaredMagnitude() {
    +  var a = new goog.math.Vec2(-3, -4);
    +  assertEquals(25, a.squaredMagnitude());
    +}
    +
    +
    +function testScaleFactor() {
    +  var a = new goog.math.Vec2(1, 2);
    +  var scaled = a.scale(0.5);
    +
    +  assertTrue('The type of the return value should be goog.math.Vec2',
    +      scaled instanceof goog.math.Vec2);
    +  assertVectorEquals(new goog.math.Vec2(0.5, 1), a);
    +}
    +
    +
    +function testScaleXY() {
    +  var a = new goog.math.Vec2(10, 15);
    +  var scaled = a.scale(2, 3);
    +  assertEquals('The function should return the target instance', a, scaled);
    +  assertTrue('The type of the return value should be goog.math.Vec2',
    +      scaled instanceof goog.math.Vec2);
    +  assertVectorEquals(new goog.math.Vec2(20, 45), a);
    +}
    +
    +
    +function testInvert() {
    +  var a = new goog.math.Vec2(3, 4);
    +  a.invert();
    +
    +  assertEquals(-3, a.x);
    +  assertEquals(-4, a.y);
    +}
    +
    +
    +function testNormalize() {
    +  var a = new goog.math.Vec2(5, 5);
    +  a.normalize();
    +  assertRoughlyEquals(1.0, a.magnitude(), 1e-10);
    +}
    +
    +
    +function testAdd() {
    +  var a = new goog.math.Vec2(1, -1);
    +  a.add(new goog.math.Vec2(3, 3));
    +  assertVectorEquals(new goog.math.Vec2(4, 2), a);
    +}
    +
    +
    +function testSubtract() {
    +  var a = new goog.math.Vec2(1, -1);
    +  a.subtract(new goog.math.Vec2(3, 3));
    +  assertVectorEquals(new goog.math.Vec2(-2, -4), a);
    +}
    +
    +
    +function testRotate() {
    +  var a = new goog.math.Vec2(1, -1);
    +  a.rotate(Math.PI / 2);
    +  assertRoughlyEquals(1, a.x, 0.000001);
    +  assertRoughlyEquals(1, a.y, 0.000001);
    +  a.rotate(-Math.PI);
    +  assertRoughlyEquals(-1, a.x, 0.000001);
    +  assertRoughlyEquals(-1, a.y, 0.000001);
    +}
    +
    +
    +function testRotateAroundPoint() {
    +  var a = goog.math.Vec2.rotateAroundPoint(
    +      new goog.math.Vec2(1, -1), new goog.math.Vec2(1, 0), Math.PI / 2);
    +  assertRoughlyEquals(2, a.x, 0.000001);
    +  assertRoughlyEquals(0, a.y, 0.000001);
    +}
    +
    +
    +function testEquals() {
    +  var a = new goog.math.Vec2(1, 2);
    +
    +  assertFalse(a.equals(null));
    +  assertFalse(a.equals(new goog.math.Vec2(1, 3)));
    +  assertFalse(a.equals(new goog.math.Vec2(2, 2)));
    +
    +  assertTrue(a.equals(a));
    +  assertTrue(a.equals(new goog.math.Vec2(1, 2)));
    +}
    +
    +
    +function testSum() {
    +  var a = new goog.math.Vec2(0.5, 0.25);
    +  var b = new goog.math.Vec2(0.5, 0.75);
    +
    +  var c = goog.math.Vec2.sum(a, b);
    +  assertVectorEquals(new goog.math.Vec2(1, 1), c);
    +}
    +
    +
    +function testDifference() {
    +  var a = new goog.math.Vec2(0.5, 0.25);
    +  var b = new goog.math.Vec2(0.5, 0.75);
    +
    +  var c = goog.math.Vec2.difference(a, b);
    +  assertVectorEquals(new goog.math.Vec2(0, -0.5), c);
    +}
    +
    +
    +function testDistance() {
    +  var a = new goog.math.Vec2(3, 4);
    +  var b = new goog.math.Vec2(-3, -4);
    +
    +  assertEquals(10, goog.math.Vec2.distance(a, b));
    +}
    +
    +
    +function testSquaredDistance() {
    +  var a = new goog.math.Vec2(3, 4);
    +  var b = new goog.math.Vec2(-3, -4);
    +
    +  assertEquals(100, goog.math.Vec2.squaredDistance(a, b));
    +}
    +
    +
    +function testVec2Equals() {
    +  assertTrue(goog.math.Vec2.equals(null, null));
    +  assertFalse(goog.math.Vec2.equals(null, new goog.math.Vec2()));
    +
    +  var a = new goog.math.Vec2(1, 3);
    +  assertTrue(goog.math.Vec2.equals(a, a));
    +  assertTrue(goog.math.Vec2.equals(a, new goog.math.Vec2(1, 3)));
    +  assertFalse(goog.math.Vec2.equals(1, new goog.math.Vec2(3, 1)));
    +}
    +
    +
    +function testDot() {
    +  var a = new goog.math.Vec2(0, 5);
    +  var b = new goog.math.Vec2(3, 0);
    +  assertEquals(0, goog.math.Vec2.dot(a, b));
    +
    +  var c = new goog.math.Vec2(-5, -5);
    +  var d = new goog.math.Vec2(0, 7);
    +  assertEquals(-35, goog.math.Vec2.dot(c, d));
    +}
    +
    +
    +function testLerp() {
    +  var a = new goog.math.Vec2(0, 0);
    +  var b = new goog.math.Vec2(10, 10);
    +
    +  for (var i = 0; i <= 10; i++) {
    +    var c = goog.math.Vec2.lerp(a, b, i / 10);
    +    assertEquals(i, c.x);
    +    assertEquals(i, c.y);
    +  }
    +}
    +
    +
    +function testToString() {
    +  testEquals('(0, 0)', new goog.math.Vec2(0, 0).toString());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec3.js b/mapbox-gl/closure-library/closure/goog/math/vec3.js
    new file mode 100644
    index 0000000000..253a66f5c5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec3.js
    @@ -0,0 +1,310 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines a 3-element vector class that can be used for
    + * coordinate math, useful for animation systems and point manipulation.
    + *
    + * Based heavily on code originally by:
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +
    +goog.provide('goog.math.Vec3');
    +
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate3');
    +
    +
    +
    +/**
    + * Class for a three-dimensional vector object and assorted functions useful for
    + * manipulation.
    + *
    + * Inherits from goog.math.Coordinate3 so that a Vec3 may be passed in to any
    + * function that requires a Coordinate.
    + *
    + * @param {number} x The x value for the vector.
    + * @param {number} y The y value for the vector.
    + * @param {number} z The z value for the vector.
    + * @struct
    + * @constructor
    + * @extends {goog.math.Coordinate3}
    + */
    +goog.math.Vec3 = function(x, y, z) {
    +  /**
    +   * X-value
    +   * @type {number}
    +   */
    +  this.x = x;
    +
    +  /**
    +   * Y-value
    +   * @type {number}
    +   */
    +  this.y = y;
    +
    +  /**
    +   * Z-value
    +   * @type {number}
    +   */
    +  this.z = z;
    +};
    +goog.inherits(goog.math.Vec3, goog.math.Coordinate3);
    +
    +
    +/**
    + * Generates a random unit vector.
    + *
    + * http://mathworld.wolfram.com/SpherePointPicking.html
    + * Using (6), (7), and (8) to generate coordinates.
    + * @return {!goog.math.Vec3} A random unit-length vector.
    + */
    +goog.math.Vec3.randomUnit = function() {
    +  var theta = Math.random() * Math.PI * 2;
    +  var phi = Math.random() * Math.PI * 2;
    +
    +  var z = Math.cos(phi);
    +  var x = Math.sqrt(1 - z * z) * Math.cos(theta);
    +  var y = Math.sqrt(1 - z * z) * Math.sin(theta);
    +
    +  return new goog.math.Vec3(x, y, z);
    +};
    +
    +
    +/**
    + * Generates a random vector inside the unit sphere.
    + *
    + * @return {!goog.math.Vec3} A random vector.
    + */
    +goog.math.Vec3.random = function() {
    +  return goog.math.Vec3.randomUnit().scale(Math.random());
    +};
    +
    +
    +/**
    + * Returns a new Vec3 object from a given coordinate.
    + *
    + * @param {goog.math.Coordinate3} a The coordinate.
    + * @return {!goog.math.Vec3} A new vector object.
    + */
    +goog.math.Vec3.fromCoordinate3 = function(a) {
    +  return new goog.math.Vec3(a.x, a.y, a.z);
    +};
    +
    +
    +/**
    + * Creates a new copy of this Vec3.
    + *
    + * @return {!goog.math.Vec3} A new vector with the same coordinates as this one.
    + * @override
    + */
    +goog.math.Vec3.prototype.clone = function() {
    +  return new goog.math.Vec3(this.x, this.y, this.z);
    +};
    +
    +
    +/**
    + * Returns the magnitude of the vector measured from the origin.
    + *
    + * @return {number} The length of the vector.
    + */
    +goog.math.Vec3.prototype.magnitude = function() {
    +  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    +};
    +
    +
    +/**
    + * Returns the squared magnitude of the vector measured from the origin.
    + * NOTE(brenneman): Leaving out the square root is not a significant
    + * optimization in JavaScript.
    + *
    + * @return {number} The length of the vector, squared.
    + */
    +goog.math.Vec3.prototype.squaredMagnitude = function() {
    +  return this.x * this.x + this.y * this.y + this.z * this.z;
    +};
    +
    +
    +/**
    + * Scales the current vector by a constant.
    + *
    + * @param {number} s The scale factor.
    + * @return {!goog.math.Vec3} This vector, scaled.
    + */
    +goog.math.Vec3.prototype.scale = function(s) {
    +  this.x *= s;
    +  this.y *= s;
    +  this.z *= s;
    +  return this;
    +};
    +
    +
    +/**
    + * Reverses the sign of the vector. Equivalent to scaling the vector by -1.
    + *
    + * @return {!goog.math.Vec3} This vector, inverted.
    + */
    +goog.math.Vec3.prototype.invert = function() {
    +  this.x = -this.x;
    +  this.y = -this.y;
    +  this.z = -this.z;
    +  return this;
    +};
    +
    +
    +/**
    + * Normalizes the current vector to have a magnitude of 1.
    + *
    + * @return {!goog.math.Vec3} This vector, normalized.
    + */
    +goog.math.Vec3.prototype.normalize = function() {
    +  return this.scale(1 / this.magnitude());
    +};
    +
    +
    +/**
    + * Adds another vector to this vector in-place.
    + *
    + * @param {goog.math.Vec3} b The vector to add.
    + * @return {!goog.math.Vec3} This vector with {@code b} added.
    + */
    +goog.math.Vec3.prototype.add = function(b) {
    +  this.x += b.x;
    +  this.y += b.y;
    +  this.z += b.z;
    +  return this;
    +};
    +
    +
    +/**
    + * Subtracts another vector from this vector in-place.
    + *
    + * @param {goog.math.Vec3} b The vector to subtract.
    + * @return {!goog.math.Vec3} This vector with {@code b} subtracted.
    + */
    +goog.math.Vec3.prototype.subtract = function(b) {
    +  this.x -= b.x;
    +  this.y -= b.y;
    +  this.z -= b.z;
    +  return this;
    +};
    +
    +
    +/**
    + * Compares this vector with another for equality.
    + *
    + * @param {goog.math.Vec3} b The other vector.
    + * @return {boolean} True if this vector's x, y and z equal the given vector's
    + *     x, y, and z, respectively.
    + */
    +goog.math.Vec3.prototype.equals = function(b) {
    +  return this == b || !!b && this.x == b.x && this.y == b.y && this.z == b.z;
    +};
    +
    +
    +/**
    + * Returns the distance between two vectors.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {number} The distance.
    + */
    +goog.math.Vec3.distance = goog.math.Coordinate3.distance;
    +
    +
    +/**
    + * Returns the squared distance between two vectors.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {number} The squared distance.
    + */
    +goog.math.Vec3.squaredDistance = goog.math.Coordinate3.squaredDistance;
    +
    +
    +/**
    + * Compares vectors for equality.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {boolean} True if the vectors have equal x, y, and z coordinates.
    + */
    +goog.math.Vec3.equals = goog.math.Coordinate3.equals;
    +
    +
    +/**
    + * Returns the sum of two vectors as a new Vec3.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {!goog.math.Vec3} The sum vector.
    + */
    +goog.math.Vec3.sum = function(a, b) {
    +  return new goog.math.Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
    +};
    +
    +
    +/**
    + * Returns the difference of two vectors as a new Vec3.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {!goog.math.Vec3} The difference vector.
    + */
    +goog.math.Vec3.difference = function(a, b) {
    +  return new goog.math.Vec3(a.x - b.x, a.y - b.y, a.z - b.z);
    +};
    +
    +
    +/**
    + * Returns the dot-product of two vectors.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {number} The dot-product of the two vectors.
    + */
    +goog.math.Vec3.dot = function(a, b) {
    +  return a.x * b.x + a.y * b.y + a.z * b.z;
    +};
    +
    +
    +/**
    + * Returns the cross-product of two vectors.
    + *
    + * @param {goog.math.Vec3} a The first vector.
    + * @param {goog.math.Vec3} b The second vector.
    + * @return {!goog.math.Vec3} The cross-product of the two vectors.
    + */
    +goog.math.Vec3.cross = function(a, b) {
    +  return new goog.math.Vec3(a.y * b.z - a.z * b.y,
    +                            a.z * b.x - a.x * b.z,
    +                            a.x * b.y - a.y * b.x);
    +};
    +
    +
    +/**
    + * Returns a new Vec3 that is the linear interpolant between vectors a and b at
    + * scale-value x.
    + *
    + * @param {goog.math.Vec3} a Vector a.
    + * @param {goog.math.Vec3} b Vector b.
    + * @param {number} x The proportion between a and b.
    + * @return {!goog.math.Vec3} The interpolated vector.
    + */
    +goog.math.Vec3.lerp = function(a, b, x) {
    +  return new goog.math.Vec3(goog.math.lerp(a.x, b.x, x),
    +                            goog.math.lerp(a.y, b.y, x),
    +                            goog.math.lerp(a.z, b.z, x));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec3_test.html b/mapbox-gl/closure-library/closure/goog/math/vec3_test.html
    new file mode 100644
    index 0000000000..be3c632aaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec3_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  Vec3 Unit Tests
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.math.Vec3
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.math.Vec3Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/math/vec3_test.js b/mapbox-gl/closure-library/closure/goog/math/vec3_test.js
    new file mode 100644
    index 0000000000..ce0216abb6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/math/vec3_test.js
    @@ -0,0 +1,212 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.math.Vec3Test');
    +goog.setTestOnly('goog.math.Vec3Test');
    +
    +goog.require('goog.math.Coordinate3');
    +goog.require('goog.math.Vec3');
    +goog.require('goog.testing.jsunit');
    +
    +function assertVec3Equals(a, b) {
    +  assertTrue(b + ' should be equal to ' + a, goog.math.Vec3.equals(a, b));
    +}
    +
    +function testVec3() {
    +  var v = new goog.math.Vec3(3.14, 2.78, -7.21);
    +  assertEquals(3.14, v.x);
    +  assertEquals(2.78, v.y);
    +  assertEquals(-7.21, v.z);
    +}
    +
    +
    +function testRandomUnit() {
    +  var a = goog.math.Vec3.randomUnit();
    +  assertRoughlyEquals(1.0, a.magnitude(), 1e-10);
    +}
    +
    +
    +function testRandom() {
    +  var a = goog.math.Vec3.random();
    +  assertTrue(a.magnitude() <= 1.0);
    +}
    +
    +
    +function testFromCoordinate3() {
    +  var a = new goog.math.Coordinate3(-2, 10, 4);
    +  var b = goog.math.Vec3.fromCoordinate3(a);
    +  assertEquals(-2, b.x);
    +  assertEquals(10, b.y);
    +  assertEquals(4, b.z);
    +}
    +
    +
    +function testClone() {
    +  var a = new goog.math.Vec3(1, 2, 5);
    +  var b = a.clone();
    +
    +  assertEquals(a.x, b.x);
    +  assertEquals(a.y, b.y);
    +  assertEquals(a.z, b.z);
    +}
    +
    +
    +function testMagnitude() {
    +  var a = new goog.math.Vec3(0, 10, 0);
    +  var b = new goog.math.Vec3(3, 4, 5);
    +  var c = new goog.math.Vec3(-4, 3, 8);
    +
    +  assertEquals(10, a.magnitude());
    +  assertEquals(Math.sqrt(50), b.magnitude());
    +  assertEquals(Math.sqrt(89), c.magnitude());
    +}
    +
    +
    +function testSquaredMagnitude() {
    +  var a = new goog.math.Vec3(-3, -4, -5);
    +  assertEquals(50, a.squaredMagnitude());
    +}
    +
    +
    +function testScale() {
    +  var a = new goog.math.Vec3(1, 2, 3);
    +  a.scale(0.5);
    +
    +  assertEquals(0.5, a.x);
    +  assertEquals(1, a.y);
    +  assertEquals(1.5, a.z);
    +}
    +
    +
    +function testInvert() {
    +  var a = new goog.math.Vec3(3, 4, 5);
    +  a.invert();
    +
    +  assertEquals(-3, a.x);
    +  assertEquals(-4, a.y);
    +  assertEquals(-5, a.z);
    +}
    +
    +
    +function testNormalize() {
    +  var a = new goog.math.Vec3(5, 5, 5);
    +  a.normalize();
    +  assertRoughlyEquals(1.0, a.magnitude(), 1e-10);
    +}
    +
    +
    +function testAdd() {
    +  var a = new goog.math.Vec3(1, -1, 7);
    +  a.add(new goog.math.Vec3(3, 3, 3));
    +  assertVec3Equals(new goog.math.Vec3(4, 2, 10), a);
    +}
    +
    +
    +function testSubtract() {
    +  var a = new goog.math.Vec3(1, -1, 4);
    +  a.subtract(new goog.math.Vec3(3, 3, 3));
    +  assertVec3Equals(new goog.math.Vec3(-2, -4, 1), a);
    +}
    +
    +
    +function testEquals() {
    +  var a = new goog.math.Vec3(1, 2, 5);
    +
    +  assertFalse(a.equals(null));
    +  assertFalse(a.equals(new goog.math.Vec3(1, 3, 5)));
    +  assertFalse(a.equals(new goog.math.Vec3(2, 2, 2)));
    +
    +  assertTrue(a.equals(a));
    +  assertTrue(a.equals(new goog.math.Vec3(1, 2, 5)));
    +}
    +
    +
    +function testSum() {
    +  var a = new goog.math.Vec3(0.5, 0.25, 1.2);
    +  var b = new goog.math.Vec3(0.5, 0.75, -0.6);
    +
    +  var c = goog.math.Vec3.sum(a, b);
    +  assertVec3Equals(new goog.math.Vec3(1, 1, 0.6), c);
    +}
    +
    +
    +function testDifference() {
    +  var a = new goog.math.Vec3(0.5, 0.25, 3);
    +  var b = new goog.math.Vec3(0.5, 0.75, 5);
    +
    +  var c = goog.math.Vec3.difference(a, b);
    +  assertVec3Equals(new goog.math.Vec3(0, -0.5, -2), c);
    +}
    +
    +
    +function testDistance() {
    +  var a = new goog.math.Vec3(3, 4, 5);
    +  var b = new goog.math.Vec3(-3, -4, 5);
    +
    +  assertEquals(10, goog.math.Vec3.distance(a, b));
    +}
    +
    +
    +function testSquaredDistance() {
    +  var a = new goog.math.Vec3(3, 4, 5);
    +  var b = new goog.math.Vec3(-3, -4, 5);
    +
    +  assertEquals(100, goog.math.Vec3.squaredDistance(a, b));
    +}
    +
    +
    +function testVec3Equals() {
    +  assertTrue(goog.math.Vec3.equals(null, null, null));
    +  assertFalse(goog.math.Vec3.equals(null, new goog.math.Vec3()));
    +
    +  var a = new goog.math.Vec3(1, 3, 5);
    +  assertTrue(goog.math.Vec3.equals(a, a));
    +  assertTrue(goog.math.Vec3.equals(a, new goog.math.Vec3(1, 3, 5)));
    +  assertFalse(goog.math.Vec3.equals(1, new goog.math.Vec3(3, 1, 5)));
    +}
    +
    +
    +function testDot() {
    +  var a = new goog.math.Vec3(0, 5, 2);
    +  var b = new goog.math.Vec3(3, 0, 5);
    +  assertEquals(10, goog.math.Vec3.dot(a, b));
    +
    +  var c = new goog.math.Vec3(-5, -5, 5);
    +  var d = new goog.math.Vec3(0, 7, -2);
    +  assertEquals(-45, goog.math.Vec3.dot(c, d));
    +}
    +
    +
    +function testCross() {
    +  var a = new goog.math.Vec3(3, 0, 0);
    +  var b = new goog.math.Vec3(0, 2, 0);
    +  assertVec3Equals(new goog.math.Vec3(0, 0, 6), goog.math.Vec3.cross(a, b));
    +
    +  var c = new goog.math.Vec3(1, 2, 3);
    +  var d = new goog.math.Vec3(4, 5, 6);
    +  assertVec3Equals(new goog.math.Vec3(-3, 6, -3), goog.math.Vec3.cross(c, d));
    +}
    +
    +
    +function testLerp() {
    +  var a = new goog.math.Vec3(0, 0, 0);
    +  var b = new goog.math.Vec3(10, 10, 10);
    +
    +  for (var i = 0; i <= 10; i++) {
    +    var c = goog.math.Vec3.lerp(a, b, i / 10);
    +    assertEquals(i, c.x);
    +    assertEquals(i, c.y);
    +    assertEquals(i, c.z);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/memoize/memoize.js b/mapbox-gl/closure-library/closure/goog/memoize/memoize.js
    new file mode 100644
    index 0000000000..cd7df3be02
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/memoize/memoize.js
    @@ -0,0 +1,104 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tool for caching the result of expensive deterministic
    + * functions.
    + *
    + * @see http://en.wikipedia.org/wiki/Memoization
    + *
    + */
    +
    +goog.provide('goog.memoize');
    +
    +
    +/**
    + * Decorator around functions that caches the inner function's return values.
    + *
    + * To cache parameterless functions, see goog.functions.cacheReturnValue.
    + *
    + * @param {Function} f The function to wrap. Its return value may only depend
    + *     on its arguments and 'this' context. There may be further restrictions
    + *     on the arguments depending on the capabilities of the serializer used.
    + * @param {function(number, Object): string=} opt_serializer A function to
    + *     serialize f's arguments. It must have the same signature as
    + *     goog.memoize.simpleSerializer. It defaults to that function.
    + * @this {Object} The object whose function is being wrapped.
    + * @return {!Function} The wrapped function.
    + */
    +goog.memoize = function(f, opt_serializer) {
    +  var serializer = opt_serializer || goog.memoize.simpleSerializer;
    +
    +  return function() {
    +    if (goog.memoize.ENABLE_MEMOIZE) {
    +      // In the strict mode, when this function is called as a global function,
    +      // the value of 'this' is undefined instead of a global object. See:
    +      // https://developer.mozilla.org/en/JavaScript/Strict_mode
    +      var thisOrGlobal = this || goog.global;
    +      // Maps the serialized list of args to the corresponding return value.
    +      var cache = thisOrGlobal[goog.memoize.CACHE_PROPERTY_] ||
    +          (thisOrGlobal[goog.memoize.CACHE_PROPERTY_] = {});
    +      var key = serializer(goog.getUid(f), arguments);
    +      return cache.hasOwnProperty(key) ? cache[key] :
    +          (cache[key] = f.apply(this, arguments));
    +    } else {
    +      return f.apply(this, arguments);
    +    }
    +  };
    +};
    +
    +
    +/**
    + * @define {boolean} Flag to disable memoization in unit tests.
    + */
    +goog.define('goog.memoize.ENABLE_MEMOIZE', true);
    +
    +
    +/**
    + * Clears the memoization cache on the given object.
    + * @param {Object} cacheOwner The owner of the cache. This is the {@code this}
    + *     context of the memoized function.
    + */
    +goog.memoize.clearCache = function(cacheOwner) {
    +  cacheOwner[goog.memoize.CACHE_PROPERTY_] = {};
    +};
    +
    +
    +/**
    + * Name of the property used by goog.memoize as cache.
    + * @type {string}
    + * @private
    + */
    +goog.memoize.CACHE_PROPERTY_ = 'closure_memoize_cache_';
    +
    +
    +/**
    + * Simple and fast argument serializer function for goog.memoize.
    + * Supports string, number, boolean, null and undefined arguments. Doesn't
    + * support \x0B characters in the strings.
    + * @param {number} functionUid Unique identifier of the function whose result
    + *     is cached.
    + * @param {Object} args The arguments that the function to memoize is called
    + *     with. Note: it is an array-like object, because supports indexing and
    + *     has the length property.
    + * @return {string} The list of arguments with type information concatenated
    + *     with the functionUid argument, serialized as \x0B-separated string.
    + */
    +goog.memoize.simpleSerializer = function(functionUid, args) {
    +  var context = [functionUid];
    +  for (var i = args.length - 1; i >= 0; --i) {
    +    context.push(typeof args[i], args[i]);
    +  }
    +  return context.join('\x0B');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.html b/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.html
    new file mode 100644
    index 0000000000..e7560e0550
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.memoize
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.memoizeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.js b/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.js
    new file mode 100644
    index 0000000000..ce986e954d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/memoize/memoize_test.js
    @@ -0,0 +1,153 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.memoizeTest');
    +goog.setTestOnly('goog.memoizeTest');
    +
    +goog.require('goog.memoize');
    +goog.require('goog.testing.jsunit');
    +
    +function testNoArgs() {
    +  var called = 0;
    +  var f = goog.memoize(function() {
    +    called++;
    +    return 10;
    +  });
    +
    +  assertEquals('f() 1st call', 10, f());
    +  assertEquals('f() 2nd call', 10, f());
    +  assertEquals('f() 3rd call', 10, f.call());
    +  assertEquals('f() called once', 1, called);
    +}
    +
    +function testOneOptionalArgSimple() {
    +  var called = 0;
    +  var f = goog.memoize(function(opt_x) {
    +    called++;
    +    return arguments.length == 0 ? 'no args' : opt_x;
    +  });
    +
    +  assertEquals('f() 1st call', 'no args', f());
    +  assertEquals('f() 2nd call', 'no args', f());
    +  assertEquals('f(0) 1st call', 0, f(0));
    +  assertEquals('f(0) 2nd call', 0, f(0));
    +  assertEquals('f("") 1st call', '', f(''));
    +  assertEquals('f("") 2nd call', '', f(''));
    +  assertEquals('f("0") 1st call', '0', f('0'));
    +  assertEquals('f("0") 1st call', '0', f('0'));
    +  assertEquals('f(null) 1st call', null, f(null));
    +  assertEquals('f(null) 2nd call', null, f(null));
    +  assertEquals('f(undefined) 1st call', undefined, f(undefined));
    +  assertEquals('f(undefined) 2nd call', undefined, f(undefined));
    +
    +  assertEquals('f(opt_x) called 6 times', 6, called);
    +}
    +
    +function testProtoFunctions() {
    +  var fcalled = 0;
    +  var gcalled = 0;
    +  var Class = function(x) {
    +    this.x = x;
    +    this.f = goog.memoize(function(y) {
    +      fcalled++;
    +      return this.x + y;
    +    });
    +  };
    +  Class.prototype.g = goog.memoize(function(z) {
    +    gcalled++;
    +    return this.x - z;
    +  });
    +
    +  var obj1 = new Class(10);
    +  var obj2 = new Class(20);
    +
    +  assertEquals('10+1', 11, obj1.f(1));
    +  assertEquals('10+2', 12, obj1.f(2));
    +  assertEquals('10+2 again', 12, obj1.f(2));
    +  assertEquals('f called twice', 2, fcalled);
    +
    +  assertEquals('10-1', 9, obj1.g(1));
    +  assertEquals('10-2', 8, obj1.g(2));
    +  assertEquals('10-2 again', 8, obj1.g(2));
    +  assertEquals('g called twice', 2, gcalled);
    +
    +  assertEquals('20+1', 21, obj2.f(1));
    +  assertEquals('20+2', 22, obj2.f(2));
    +  assertEquals('20+2 again', 22, obj2.f(2));
    +  assertEquals('f called 4 times', 4, fcalled);
    +
    +  assertEquals('20-1', 19, obj2.g(1));
    +  assertEquals('20-2', 18, obj2.g(2));
    +  assertEquals('20-2 again', 18, obj2.g(2));
    +  assertEquals('g called 4 times', 4, gcalled);
    +}
    +
    +function testCustomSerializer() {
    +  var called = 0;
    +  var serializer = function(this_context, args) {
    +    return String(args[0].getTime());
    +  };
    +  var getYear = goog.memoize(function(date) {
    +    called++;
    +    return date.getFullYear();
    +  }, serializer);
    +
    +  assertEquals('getYear(2008, 0, 1), 1st', 2008, getYear(new Date(2008, 0, 1)));
    +  assertEquals('getYear(2008, 0, 1), 2nd', 2008, getYear(new Date(2008, 0, 1)));
    +  assertEquals('getYear called once', 1, called);
    +
    +  assertEquals('getYear(2007, 0, 1)', 2007, getYear(new Date(2007, 0, 1)));
    +  assertEquals('getYear called twice', 2, called);
    +}
    +
    +function testClearCache() {
    +  var computed = 0;
    +  var identity = goog.memoize(function(x) {
    +    computed++;
    +    return x;
    +  });
    +  assertEquals('identity(1)==1', 1, identity(1));
    +  assertEquals('identity(1)==1', 1, identity(1));
    +  assertEquals('identity(1)==1', 1, identity(1));
    +  assertEquals('Expected memozation', 1, computed);
    +
    +  goog.memoize.clearCache(goog.global);
    +  assertEquals('identity(1)==1', 1, identity(1));
    +  assertEquals('identity(1)==1', 1, identity(1));
    +  assertEquals('Expected cleared memoization cache', 2, computed);
    +}
    +
    +function testDisableMemoize() {
    +  var computed = 0;
    +  var identity = goog.memoize(function(x) {
    +    computed++;
    +    return x;
    +  });
    +
    +  assertEquals('return value on first call', 1, identity(1));
    +  assertEquals('return value on second call (memoized)', 1, identity(1));
    +  assertEquals('computed once', 1, computed);
    +
    +  goog.memoize.ENABLE_MEMOIZE = false;
    +
    +  try {
    +    assertEquals('return value after disabled memoization', 1, identity(1));
    +    assertEquals('computed again', 2, computed);
    +  } finally {
    +    goog.memoize.ENABLE_MEMOIZE = true;
    +  }
    +
    +  assertEquals('return value after reenabled memoization', 1, identity(1));
    +  assertEquals('not computed again', 2, computed);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel.js b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel.js
    new file mode 100644
    index 0000000000..ea5440a64d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel.js
    @@ -0,0 +1,209 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An abstract superclass for message channels that handles the
    + * repetitive details of registering and dispatching to services. This is more
    + * useful for full-fledged channels than for decorators, since decorators
    + * generally delegate service registering anyway.
    + *
    + */
    +
    +
    +goog.provide('goog.messaging.AbstractChannel');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.messaging.MessageChannel'); // interface
    +
    +
    +
    +/**
    + * Creates an abstract message channel.
    + *
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.messaging.MessageChannel}
    + */
    +goog.messaging.AbstractChannel = function() {
    +  goog.messaging.AbstractChannel.base(this, 'constructor');
    +
    +  /**
    +   * The services registered for this channel.
    +   * @type {Object<string, {callback: function((string|!Object)),
    +                             objectPayload: boolean}>}
    +   * @private
    +   */
    +  this.services_ = {};
    +};
    +goog.inherits(goog.messaging.AbstractChannel, goog.Disposable);
    +
    +
    +/**
    + * The default service to be run when no other services match.
    + *
    + * @type {?function(string, (string|!Object))}
    + * @private
    + */
    +goog.messaging.AbstractChannel.prototype.defaultService_;
    +
    +
    +/**
    + * Logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + */
    +goog.messaging.AbstractChannel.prototype.logger =
    +    goog.log.getLogger('goog.messaging.AbstractChannel');
    +
    +
    +/**
    + * Immediately calls opt_connectCb if given, and is otherwise a no-op. If
    + * subclasses have configuration that needs to happen before the channel is
    + * connected, they should override this and {@link #isConnected}.
    + * @override
    + */
    +goog.messaging.AbstractChannel.prototype.connect = function(opt_connectCb) {
    +  if (opt_connectCb) {
    +    opt_connectCb();
    +  }
    +};
    +
    +
    +/**
    + * Always returns true. If subclasses have configuration that needs to happen
    + * before the channel is connected, they should override this and
    + * {@link #connect}.
    + * @override
    + */
    +goog.messaging.AbstractChannel.prototype.isConnected = function() {
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.messaging.AbstractChannel.prototype.registerService =
    +    function(serviceName, callback, opt_objectPayload) {
    +  this.services_[serviceName] = {
    +    callback: callback,
    +    objectPayload: !!opt_objectPayload
    +  };
    +};
    +
    +
    +/** @override */
    +goog.messaging.AbstractChannel.prototype.registerDefaultService =
    +    function(callback) {
    +  this.defaultService_ = callback;
    +};
    +
    +
    +/** @override */
    +goog.messaging.AbstractChannel.prototype.send = goog.abstractMethod;
    +
    +
    +/**
    + * Delivers a message to the appropriate service. This is meant to be called by
    + * subclasses when they receive messages.
    + *
    + * This method takes into account both explicitly-registered and default
    + * services, as well as making sure that JSON payloads are decoded when
    + * necessary. If the subclass is capable of passing objects as payloads, those
    + * objects can be passed in to this method directly. Otherwise, the (potentially
    + * JSON-encoded) strings should be passed in.
    + *
    + * @param {string} serviceName The name of the service receiving the message.
    + * @param {string|!Object} payload The contents of the message.
    + * @protected
    + */
    +goog.messaging.AbstractChannel.prototype.deliver = function(
    +    serviceName, payload) {
    +  var service = this.getService(serviceName, payload);
    +  if (!service) {
    +    return;
    +  }
    +
    +  var decodedPayload =
    +      this.decodePayload(serviceName, payload, service.objectPayload);
    +  if (goog.isDefAndNotNull(decodedPayload)) {
    +    service.callback(decodedPayload);
    +  }
    +};
    +
    +
    +/**
    + * Find the service object for a given service name. If there's no service
    + * explicitly registered, but there is a default service, a service object is
    + * constructed for it.
    + *
    + * @param {string} serviceName The name of the service receiving the message.
    + * @param {string|!Object} payload The contents of the message.
    + * @return {?{callback: function((string|!Object)), objectPayload: boolean}} The
    + *     service object for the given service, or null if none was found.
    + * @protected
    + */
    +goog.messaging.AbstractChannel.prototype.getService = function(
    +    serviceName, payload) {
    +  var service = this.services_[serviceName];
    +  if (service) {
    +    return service;
    +  } else if (this.defaultService_) {
    +    var callback = goog.partial(this.defaultService_, serviceName);
    +    var objectPayload = goog.isObject(payload);
    +    return {callback: callback, objectPayload: objectPayload};
    +  }
    +
    +  goog.log.warning(this.logger, 'Unknown service name "' + serviceName + '"');
    +  return null;
    +};
    +
    +
    +/**
    + * Converts the message payload into the format expected by the registered
    + * service (either JSON or string).
    + *
    + * @param {string} serviceName The name of the service receiving the message.
    + * @param {string|!Object} payload The contents of the message.
    + * @param {boolean} objectPayload Whether the service expects an object or a
    + *     plain string.
    + * @return {string|Object} The payload in the format expected by the service, or
    + *     null if something went wrong.
    + * @protected
    + */
    +goog.messaging.AbstractChannel.prototype.decodePayload = function(
    +    serviceName, payload, objectPayload) {
    +  if (objectPayload && goog.isString(payload)) {
    +    try {
    +      return goog.json.parse(payload);
    +    } catch (err) {
    +      goog.log.warning(this.logger,
    +          'Expected JSON payload for ' + serviceName +
    +          ', was "' + payload + '"');
    +      return null;
    +    }
    +  } else if (!objectPayload && !goog.isString(payload)) {
    +    return goog.json.serialize(payload);
    +  }
    +  return payload;
    +};
    +
    +
    +/** @override */
    +goog.messaging.AbstractChannel.prototype.disposeInternal = function() {
    +  goog.messaging.AbstractChannel.base(this, 'disposeInternal');
    +  delete this.logger;
    +  delete this.services_;
    +  delete this.defaultService_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.html
    new file mode 100644
    index 0000000000..bcf7079649
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.AbstractChannel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.AbstractChannelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.js b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.js
    new file mode 100644
    index 0000000000..6d7e22722a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/abstractchannel_test.js
    @@ -0,0 +1,82 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.AbstractChannelTest');
    +goog.setTestOnly('goog.messaging.AbstractChannelTest');
    +
    +goog.require('goog.messaging.AbstractChannel');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.jsunit');
    +
    +var mockControl;
    +var mockWorker;
    +var asyncMockControl;
    +var channel;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  asyncMockControl = new goog.testing.async.MockControl(mockControl);
    +  channel = new goog.messaging.AbstractChannel();
    +}
    +
    +function tearDown() {
    +  channel.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +function testConnect() {
    +  channel.connect(
    +      asyncMockControl.createCallbackMock('connectCallback', function() {}));
    +}
    +
    +function testIsConnected() {
    +  assertTrue('Channel should be connected by default', channel.isConnected());
    +}
    +
    +function testDeliverString() {
    +  channel.registerService(
    +      'foo',
    +      asyncMockControl.asyncAssertEquals(
    +          'should pass string to service', 'bar'),
    +      false /* opt_json */);
    +  channel.deliver('foo', 'bar');
    +}
    +
    +function testDeliverDeserializedString() {
    +  channel.registerService(
    +      'foo',
    +      asyncMockControl.asyncAssertEquals(
    +          'should pass string to service', '{"bar":"baz"}'),
    +      false /* opt_json */);
    +  channel.deliver('foo', {bar: 'baz'});
    +}
    +
    +function testDeliverObject() {
    +  channel.registerService(
    +      'foo',
    +      asyncMockControl.asyncAssertEquals(
    +          'should pass string to service', {bar: 'baz'}),
    +      true /* opt_json */);
    +  channel.deliver('foo', {bar: 'baz'});
    +}
    +
    +function testDeliverSerializedObject() {
    +  channel.registerService(
    +      'foo',
    +      asyncMockControl.asyncAssertEquals(
    +          'should pass string to service', {bar: 'baz'}),
    +      true /* opt_json */);
    +  channel.deliver('foo', '{"bar":"baz"}');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel.js b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel.js
    new file mode 100644
    index 0000000000..a059325000
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel.js
    @@ -0,0 +1,287 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for asynchronous message-passing channels that buffer
    + * their output until both ends of the channel are connected.
    + *
    + */
    +
    +goog.provide('goog.messaging.BufferedChannel');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.events');
    +goog.require('goog.log');
    +goog.require('goog.messaging.MessageChannel');
    +goog.require('goog.messaging.MultiChannel');
    +
    +
    +
    +/**
    + * Creates a new BufferedChannel, which operates like its underlying channel
    + * except that it buffers calls to send until it receives a message from its
    + * peer claiming that the peer is ready to receive.  The peer is also expected
    + * to be a BufferedChannel, though this is not enforced.
    + *
    + * @param {!goog.messaging.MessageChannel} messageChannel The MessageChannel
    + *     we're wrapping.
    + * @param {number=} opt_interval Polling interval for sending ready
    + *     notifications to peer, in ms.  Default is 50.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.messaging.MessageChannel};
    + * @final
    + */
    +goog.messaging.BufferedChannel = function(messageChannel, opt_interval) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Buffer of messages to be sent when the channel's peer is ready.
    +   *
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.buffer_ = [];
    +
    +  /**
    +   * Channel dispatcher wrapping the underlying delegate channel.
    +   *
    +   * @type {!goog.messaging.MultiChannel}
    +   * @private
    +   */
    +  this.multiChannel_ = new goog.messaging.MultiChannel(messageChannel);
    +
    +  /**
    +   * Virtual channel for carrying the user's messages.
    +   *
    +   * @type {!goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.userChannel_ = this.multiChannel_.createVirtualChannel(
    +      goog.messaging.BufferedChannel.USER_CHANNEL_NAME_);
    +
    +  /**
    +   * Virtual channel for carrying control messages for BufferedChannel.
    +   *
    +   * @type {!goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.controlChannel_ = this.multiChannel_.createVirtualChannel(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_);
    +
    +  /**
    +   * Timer for the peer ready ping loop.
    +   *
    +   * @type {goog.Timer}
    +   * @private
    +   */
    +  this.timer_ = new goog.Timer(
    +      opt_interval || goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +
    +  this.timer_.start();
    +  goog.events.listen(
    +      this.timer_, goog.Timer.TICK, this.sendReadyPing_, false, this);
    +
    +  this.controlChannel_.registerService(
    +      goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
    +      goog.bind(this.setPeerReady_, this));
    +};
    +goog.inherits(goog.messaging.BufferedChannel, goog.Disposable);
    +
    +
    +/**
    + * Default polling interval (in ms) for setPeerReady_ notifications.
    + *
    + * @type {number}
    + * @const
    + * @private
    + */
    +goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_ = 50;
    +
    +
    +/**
    + * The name of the private service which handles peer ready pings.  The
    + * service registered with this name is bound to this.setPeerReady_, an internal
    + * part of BufferedChannel's implementation that clients should not send to
    + * directly.
    + *
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_ = 'setPeerReady_';
    +
    +
    +/**
    + * The name of the virtual channel along which user messages are sent.
    + *
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ = 'user';
    +
    +
    +/**
    + * The name of the virtual channel along which internal control messages are
    + * sent.
    + *
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ = 'control';
    +
    +
    +/** @override */
    +goog.messaging.BufferedChannel.prototype.connect = function(opt_connectCb) {
    +  if (opt_connectCb) {
    +    opt_connectCb();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.messaging.BufferedChannel.prototype.isConnected = function() {
    +  return true;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the channel's peer is ready.
    + */
    +goog.messaging.BufferedChannel.prototype.isPeerReady = function() {
    +  return this.peerReady_;
    +};
    +
    +
    +/**
    + * Logger.
    + *
    + * @type {goog.log.Logger}
    + * @const
    + * @private
    + */
    +goog.messaging.BufferedChannel.prototype.logger_ = goog.log.getLogger(
    +    'goog.messaging.bufferedchannel');
    +
    +
    +/**
    + * Handles one tick of our peer ready notification loop.  This entails sending a
    + * ready ping to the peer and shutting down the loop if we've received a ping
    + * ourselves.
    + *
    + * @private
    + */
    +goog.messaging.BufferedChannel.prototype.sendReadyPing_ = function() {
    +  try {
    +    this.controlChannel_.send(
    +        goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
    +        /* payload */ this.isPeerReady() ? '1' : '');
    +  } catch (e) {
    +    this.timer_.stop();  // So we don't keep calling send and re-throwing.
    +    throw e;
    +  }
    +};
    +
    +
    +/**
    +  * Whether or not the peer channel is ready to receive messages.
    +  *
    +  * @type {boolean}
    +  * @private
    +  */
    +goog.messaging.BufferedChannel.prototype.peerReady_;
    +
    +
    +/** @override */
    +goog.messaging.BufferedChannel.prototype.registerService = function(
    +    serviceName, callback, opt_objectPayload) {
    +  this.userChannel_.registerService(serviceName, callback, opt_objectPayload);
    +};
    +
    +
    +/** @override */
    +goog.messaging.BufferedChannel.prototype.registerDefaultService = function(
    +    callback) {
    +  this.userChannel_.registerDefaultService(callback);
    +};
    +
    +
    +/**
    + * Send a message over the channel.  If the peer is not ready, the message will
    + * be buffered and sent once we've received a ready message from our peer.
    + *
    + * @param {string} serviceName The name of the service this message should be
    + *     delivered to.
    + * @param {string|!Object} payload The value of the message. If this is an
    + *     Object, it is serialized to JSON before sending.  It's the responsibility
    + *     of implementors of this class to perform the serialization.
    + * @see goog.net.xpc.BufferedChannel.send
    + * @override
    + */
    +goog.messaging.BufferedChannel.prototype.send = function(serviceName, payload) {
    +  if (this.isPeerReady()) {
    +    this.userChannel_.send(serviceName, payload);
    +  } else {
    +    goog.log.fine(goog.messaging.BufferedChannel.prototype.logger_,
    +        'buffering message ' + serviceName);
    +    this.buffer_.push({serviceName: serviceName, payload: payload});
    +  }
    +};
    +
    +
    +/**
    + * Marks the channel's peer as ready, then sends buffered messages and nulls the
    + * buffer.  Subsequent calls to setPeerReady_ have no effect.
    + *
    + * @param {(!Object|string)} peerKnowsWeKnowItsReady Passed by the peer to
    + *     indicate whether it knows that we've received its ping and that it's
    + *     ready.  Non-empty if true, empty if false.
    + * @private
    + */
    +goog.messaging.BufferedChannel.prototype.setPeerReady_ = function(
    +    peerKnowsWeKnowItsReady) {
    +  if (peerKnowsWeKnowItsReady) {
    +    this.timer_.stop();
    +  } else {
    +    // Our peer doesn't know we're ready, so restart (or continue) pinging.
    +    // Restarting may be needed if the peer iframe was reloaded after the
    +    // connection was first established.
    +    this.timer_.start();
    +  }
    +
    +  if (this.peerReady_) {
    +    return;
    +  }
    +  this.peerReady_ = true;
    +  // Send one last ping so that the peer knows we know it's ready.
    +  this.sendReadyPing_();
    +  for (var i = 0; i < this.buffer_.length; i++) {
    +    var message = this.buffer_[i];
    +    goog.log.fine(goog.messaging.BufferedChannel.prototype.logger_,
    +        'sending buffered message ' + message.serviceName);
    +    this.userChannel_.send(message.serviceName, message.payload);
    +  }
    +  this.buffer_ = null;
    +};
    +
    +
    +/** @override */
    +goog.messaging.BufferedChannel.prototype.disposeInternal = function() {
    +  goog.dispose(this.multiChannel_);
    +  goog.dispose(this.timer_);
    +  goog.messaging.BufferedChannel.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.html
    new file mode 100644
    index 0000000000..acbc0a9ec0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.html
    @@ -0,0 +1,34 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.BufferedChannel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.BufferedChannelTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="debug-container"
    +       style="border: 1px solid black; font-size: small; font-family: courier;">
    +   Debug Window
    +  [
    +   <a href="#" onclick="document.getElementById('debug-div').innerHTML = '';">
    +    clear
    +   </a>
    +   ]
    +   <div id="debug-div">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.js b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.js
    new file mode 100644
    index 0000000000..25eb7386f0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/bufferedchannel_test.js
    @@ -0,0 +1,268 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.BufferedChannelTest');
    +goog.setTestOnly('goog.messaging.BufferedChannelTest');
    +
    +goog.require('goog.debug.Console');
    +goog.require('goog.dom');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.messaging.BufferedChannel');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var clock;
    +var messages = [
    +  {serviceName: 'firstService', payload: 'firstPayload'},
    +  {serviceName: 'secondService', payload: 'secondPayload'}
    +];
    +var mockControl;
    +var asyncMockControl;
    +
    +function setUpPage() {
    +  if (goog.global.console) {
    +    new goog.debug.Console().setCapturing(true);
    +  }
    +  var logger = goog.log.getLogger('goog.messaging');
    +  logger.setLevel(goog.log.Level.ALL);
    +  goog.log.addHandler(logger, function(logRecord) {
    +    var msg = goog.dom.createDom('div');
    +    msg.innerHTML = logRecord.getMessage();
    +    goog.dom.appendChild(goog.dom.getElement('debug-div'), msg);
    +  });
    +  clock = new goog.testing.MockClock();
    +  mockControl = new goog.testing.MockControl();
    +  asyncMockControl = new goog.testing.async.MockControl(mockControl);
    +}
    +
    +
    +function setUp() {
    +  clock.install();
    +}
    +
    +
    +function tearDown() {
    +  clock.uninstall();
    +  mockControl.$tearDown();
    +}
    +
    +
    +function assertMessageArraysEqual(ma1, ma2) {
    +  assertEquals('message array lengths differ', ma1.length, ma2.length);
    +  for (var i = 0; i < ma1.length; i++) {
    +    assertEquals(
    +        'message array serviceNames differ',
    +        ma1[i].serviceName, ma2[i].serviceName);
    +    assertEquals(
    +        'message array payloads differ',
    +        ma1[i].payload, ma2[i].payload);
    +  }
    +}
    +
    +
    +function testDelegationToWrappedChannel() {
    +  var mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var channel = new goog.messaging.BufferedChannel(mockChannel);
    +
    +  channel.registerDefaultService(
    +      asyncMockControl.asyncAssertEquals(
    +          'default service should be delegated',
    +          'defaultServiceName', 'default service payload'));
    +  channel.registerService(
    +      'normalServiceName',
    +      asyncMockControl.asyncAssertEquals(
    +          'normal service should be delegated',
    +          'normal service payload'));
    +  mockChannel.send(
    +      goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':message',
    +      'payload');
    +
    +  mockControl.$replayAll();
    +  channel.peerReady_ = true;  // Prevent buffering so we delegate send calls.
    +  mockChannel.receive(
    +      goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':defaultServiceName',
    +      'default service payload');
    +  mockChannel.receive(
    +      goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':normalServiceName',
    +      'normal service payload');
    +  channel.send('message', 'payload');
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testOptionalConnectCallbackExecutes() {
    +  var mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var channel = new goog.messaging.BufferedChannel(mockChannel);
    +  var mockConnectCb = mockControl.createFunctionMock('mockConnectCb');
    +  mockConnectCb();
    +
    +  mockControl.$replayAll();
    +  channel.connect(mockConnectCb);
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testSendExceptionsInSendReadyPingStopsTimerAndReraises() {
    +  var mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var channel = new goog.messaging.BufferedChannel(mockChannel);
    +
    +  var errorMessage = 'errorMessage';
    +  mockChannel.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':' +
    +      goog.messaging.BufferedChannel.PEER_READY_SERVICE_NAME_,
    +      /* payload */ '').$throws(Error(errorMessage));
    +  channel.timer_.enabled = true;
    +
    +  mockControl.$replayAll();
    +  var exception = assertThrows(function() {
    +    channel.sendReadyPing_();
    +  });
    +  assertContains(errorMessage, exception.message);
    +  assertFalse(channel.timer_.enabled);
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testPollingIntervalDefaultAndOverride() {
    +  var mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var channel = new goog.messaging.BufferedChannel(mockChannel);
    +
    +  assertEquals(
    +      goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_,
    +      channel.timer_.getInterval());
    +  var interval = 100;
    +  var longIntervalChannel = new goog.messaging.BufferedChannel(
    +      new goog.testing.messaging.MockMessageChannel(mockControl), interval);
    +  assertEquals(interval, longIntervalChannel.timer_.getInterval());
    +}
    +
    +
    +function testBidirectionalCommunicationBuffersUntilReadyPingsSucceed() {
    +  var mockChannel1 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var mockChannel2 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var bufferedChannel1 = new goog.messaging.BufferedChannel(mockChannel1);
    +  var bufferedChannel2 = new goog.messaging.BufferedChannel(mockChannel2);
    +  mockChannel1.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '').$does(function() {
    +    bufferedChannel2.setPeerReady_('');
    +  });
    +  mockChannel2.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel1.setPeerReady_('1');
    +  });
    +  mockChannel1.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel2.setPeerReady_('1');
    +  });
    +  mockChannel1.send(goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':' +
    +                    messages[0].serviceName,
    +                    messages[0].payload);
    +  mockChannel2.send(goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':' +
    +                    messages[1].serviceName,
    +                    messages[1].payload);
    +
    +  mockControl.$replayAll();
    +  bufferedChannel1.send(messages[0].serviceName, messages[0].payload);
    +  bufferedChannel2.send(messages[1].serviceName, messages[1].payload);
    +  assertMessageArraysEqual([messages[0]], bufferedChannel1.buffer_);
    +  assertMessageArraysEqual([messages[1]], bufferedChannel2.buffer_);
    +  // First tick causes setPeerReady_ to fire, which in turn flushes the buffers.
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  assertEquals(bufferedChannel1.buffer_, null);
    +  assertEquals(bufferedChannel2.buffer_, null);
    +  // Now that peers are ready, a second tick causes no more sends.
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  mockControl.$verifyAll();
    +}
    +
    +
    +function testBidirectionalCommunicationReconnectsAfterOneSideRestarts() {
    +  var mockChannel1 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var mockChannel2 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var mockChannel3 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var bufferedChannel1 = new goog.messaging.BufferedChannel(mockChannel1);
    +  var bufferedChannel2 = new goog.messaging.BufferedChannel(mockChannel2);
    +  var bufferedChannel3 = new goog.messaging.BufferedChannel(mockChannel3);
    +
    +  // First tick
    +  mockChannel1.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '').$does(function() {
    +    bufferedChannel2.setPeerReady_('');
    +  });
    +  mockChannel2.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel1.setPeerReady_('1');
    +  });
    +  mockChannel1.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel2.setPeerReady_('1');
    +  });
    +  mockChannel3.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '');  // pretend it's not ready to connect yet
    +
    +  // Second tick
    +  mockChannel3.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '').$does(function() {
    +    bufferedChannel1.setPeerReady_('');
    +  });
    +
    +  // Third tick
    +  mockChannel1.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel3.setPeerReady_('1');
    +  });
    +  mockChannel3.send(
    +      goog.messaging.BufferedChannel.CONTROL_CHANNEL_NAME_ + ':setPeerReady_',
    +      '1').$does(function() {
    +    bufferedChannel1.setPeerReady_('1');
    +  });
    +
    +  mockChannel1.send(goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':' +
    +                    messages[0].serviceName,
    +                    messages[0].payload);
    +  mockChannel3.send(goog.messaging.BufferedChannel.USER_CHANNEL_NAME_ + ':' +
    +                    messages[1].serviceName,
    +                    messages[1].payload);
    +
    +  mockControl.$replayAll();
    +  // First tick causes setPeerReady_ to fire, which sets up the connection
    +  // between channels 1 and 2.
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  assertTrue(bufferedChannel1.peerReady_);
    +  assertTrue(bufferedChannel2.peerReady_);
    +  // Now pretend that channel 2 went down and was replaced by channel 3, which
    +  // is trying to connect with channel 1.
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  assertTrue(bufferedChannel1.peerReady_);
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  assertTrue(bufferedChannel3.peerReady_);
    +  bufferedChannel1.send(messages[0].serviceName, messages[0].payload);
    +  bufferedChannel3.send(messages[1].serviceName, messages[1].payload);
    +  // All timers stopped, nothing happens on the fourth tick.
    +  clock.tick(goog.messaging.BufferedChannel.DEFAULT_INTERVAL_MILLIS_);
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel.js b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel.js
    new file mode 100644
    index 0000000000..5baac69038
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel.js
    @@ -0,0 +1,98 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A MessageChannel decorator that wraps a deferred MessageChannel
    + * and enqueues messages and service registrations until that channel exists.
    + *
    + */
    +
    +goog.provide('goog.messaging.DeferredChannel');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.messaging.MessageChannel'); // interface
    +
    +
    +
    +/**
    + * Creates a new DeferredChannel, which wraps a deferred MessageChannel and
    + * enqueues messages to be sent once the wrapped channel is resolved.
    + *
    + * @param {!goog.async.Deferred} deferredChannel The underlying deferred
    + *     MessageChannel.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.messaging.MessageChannel}
    + * @final
    + */
    +goog.messaging.DeferredChannel = function(deferredChannel) {
    +  goog.messaging.DeferredChannel.base(this, 'constructor');
    +  this.deferred_ = deferredChannel;
    +};
    +goog.inherits(goog.messaging.DeferredChannel, goog.Disposable);
    +
    +
    +/**
    + * Cancels the wrapped Deferred.
    + */
    +goog.messaging.DeferredChannel.prototype.cancel = function() {
    +  this.deferred_.cancel();
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.connect = function(opt_connectCb) {
    +  if (opt_connectCb) {
    +    opt_connectCb();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.isConnected = function() {
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.registerService = function(
    +    serviceName, callback, opt_objectPayload) {
    +  this.deferred_.addCallback(function(resolved) {
    +    resolved.registerService(serviceName, callback, opt_objectPayload);
    +  });
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.registerDefaultService =
    +    function(callback) {
    +  this.deferred_.addCallback(function(resolved) {
    +    resolved.registerDefaultService(callback);
    +  });
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.send = function(serviceName, payload) {
    +  this.deferred_.addCallback(function(resolved) {
    +    resolved.send(serviceName, payload);
    +  });
    +};
    +
    +
    +/** @override */
    +goog.messaging.DeferredChannel.prototype.disposeInternal = function() {
    +  this.cancel();
    +  goog.messaging.DeferredChannel.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.html
    new file mode 100644
    index 0000000000..954a0d00c4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.DeferredChannel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.DeferredChannelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.js b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.js
    new file mode 100644
    index 0000000000..9a1cabafca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/deferredchannel_test.js
    @@ -0,0 +1,106 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.DeferredChannelTest');
    +goog.setTestOnly('goog.messaging.DeferredChannelTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.messaging.DeferredChannel');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var mockControl, asyncMockControl;
    +var mockChannel, deferredChannel;
    +var cancelled;
    +var deferred;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  asyncMockControl = new goog.testing.async.MockControl(mockControl);
    +  mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  cancelled = false;
    +  deferred = new goog.async.Deferred(function() { cancelled = true; });
    +  deferredChannel = new goog.messaging.DeferredChannel(deferred);
    +}
    +
    +function tearDown() {
    +  mockControl.$verifyAll();
    +}
    +
    +function testDeferredResolvedBeforeSend() {
    +  mockChannel.send('test', 'val');
    +  mockControl.$replayAll();
    +  deferred.callback(mockChannel);
    +  deferredChannel.send('test', 'val');
    +}
    +
    +function testDeferredResolvedBeforeRegister() {
    +  deferred.callback(mockChannel);
    +  deferredChannel.registerService(
    +      'test', asyncMockControl.asyncAssertEquals('passes on register', 'val'));
    +  mockChannel.receive('test', 'val');
    +}
    +
    +function testDeferredResolvedBeforeRegisterObject() {
    +  deferred.callback(mockChannel);
    +  deferredChannel.registerService(
    +      'test',
    +      asyncMockControl.asyncAssertEquals('passes on register', {'key': 'val'}),
    +      true);
    +  mockChannel.receive('test', {'key': 'val'});
    +}
    +
    +function testDeferredResolvedBeforeRegisterDefault() {
    +  deferred.callback(mockChannel);
    +  deferredChannel.registerDefaultService(
    +      asyncMockControl.asyncAssertEquals('passes on register', 'test', 'val'));
    +  mockChannel.receive('test', 'val');
    +}
    +
    +function testDeferredResolvedAfterSend() {
    +  mockChannel.send('test', 'val');
    +  mockControl.$replayAll();
    +  deferredChannel.send('test', 'val');
    +  deferred.callback(mockChannel);
    +}
    +
    +function testDeferredResolvedAfterRegister() {
    +  deferredChannel.registerService(
    +      'test', asyncMockControl.asyncAssertEquals('passes on register', 'val'));
    +  deferred.callback(mockChannel);
    +  mockChannel.receive('test', 'val');
    +}
    +
    +function testDeferredResolvedAfterRegisterObject() {
    +  deferredChannel.registerService(
    +      'test',
    +      asyncMockControl.asyncAssertEquals('passes on register', {'key': 'val'}),
    +      true);
    +  deferred.callback(mockChannel);
    +  mockChannel.receive('test', {'key': 'val'});
    +}
    +
    +function testDeferredResolvedAfterRegisterDefault() {
    +  deferredChannel.registerDefaultService(
    +      asyncMockControl.asyncAssertEquals('passes on register', 'test', 'val'));
    +  deferred.callback(mockChannel);
    +  mockChannel.receive('test', 'val');
    +}
    +
    +function testCancel() {
    +  deferredChannel.cancel();
    +  assertTrue(cancelled);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerclient.js b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient.js
    new file mode 100644
    index 0000000000..52e276c791
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient.js
    @@ -0,0 +1,132 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This class sends logging messages over a message channel to a
    + * server on the main page that prints them using standard logging mechanisms.
    + *
    + */
    +
    +goog.provide('goog.messaging.LoggerClient');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.debug');
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.debug.Logger');
    +
    +
    +
    +/**
    + * Creates a logger client that sends messages along a message channel for the
    + * remote end to log. The remote end of the channel should use a
    + * {goog.messaging.LoggerServer} with the same service name.
    + *
    + * @param {!goog.messaging.MessageChannel} channel The channel that on which to
    + *     send the log messages.
    + * @param {string} serviceName The name of the logging service to use.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.messaging.LoggerClient = function(channel, serviceName) {
    +  if (goog.messaging.LoggerClient.instance_) {
    +    return goog.messaging.LoggerClient.instance_;
    +  }
    +
    +  goog.messaging.LoggerClient.base(this, 'constructor');
    +
    +  /**
    +   * The channel on which to send the log messages.
    +   * @type {!goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The name of the logging service to use.
    +   * @type {string}
    +   * @private
    +   */
    +  this.serviceName_ = serviceName;
    +
    +  /**
    +   * The bound handler function for handling log messages. This is kept in a
    +   * variable so that it can be deregistered when the logger client is disposed.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.publishHandler_ = goog.bind(this.sendLog_, this);
    +  goog.debug.LogManager.getRoot().addHandler(this.publishHandler_);
    +
    +  goog.messaging.LoggerClient.instance_ = this;
    +};
    +goog.inherits(goog.messaging.LoggerClient, goog.Disposable);
    +
    +
    +/**
    + * The singleton instance, if any.
    + * @type {goog.messaging.LoggerClient}
    + * @private
    + */
    +goog.messaging.LoggerClient.instance_ = null;
    +
    +
    +/**
    + * Sends a log message through the channel.
    + * @param {!goog.debug.LogRecord} logRecord The log message.
    + * @private
    + */
    +goog.messaging.LoggerClient.prototype.sendLog_ = function(logRecord) {
    +  var name = logRecord.getLoggerName();
    +  var level = logRecord.getLevel();
    +  var msg = logRecord.getMessage();
    +  var originalException = logRecord.getException();
    +
    +  var exception;
    +  if (originalException) {
    +    var normalizedException =
    +        goog.debug.normalizeErrorObject(originalException);
    +    exception = {
    +      'name': normalizedException.name,
    +      'message': normalizedException.message,
    +      'lineNumber': normalizedException.lineNumber,
    +      'fileName': normalizedException.fileName,
    +      // Normalized exceptions without a stack have 'stack' set to 'Not
    +      // available', so we check for the existance of 'stack' on the original
    +      // exception instead.
    +      'stack': originalException.stack ||
    +          goog.debug.getStacktrace(goog.debug.Logger.prototype.log)
    +    };
    +
    +    if (goog.isObject(originalException)) {
    +      // Add messageN to the exception in case it was added using
    +      // goog.debug.enhanceError.
    +      for (var i = 0; 'message' + i in originalException; i++) {
    +        exception['message' + i] = String(originalException['message' + i]);
    +      }
    +    }
    +  }
    +  this.channel_.send(this.serviceName_, {
    +    'name': name, 'level': level.value, 'message': msg, 'exception': exception
    +  });
    +};
    +
    +
    +/** @override */
    +goog.messaging.LoggerClient.prototype.disposeInternal = function() {
    +  goog.messaging.LoggerClient.base(this, 'disposeInternal');
    +  goog.debug.LogManager.getRoot().removeHandler(this.publishHandler_);
    +  delete this.channel_;
    +  goog.messaging.LoggerClient.instance_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.html b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.html
    new file mode 100644
    index 0000000000..0d629322db
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.LoggerClient
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.LoggerClientTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.js b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.js
    new file mode 100644
    index 0000000000..cf5f4faa58
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerclient_test.js
    @@ -0,0 +1,97 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.LoggerClientTest');
    +goog.setTestOnly('goog.messaging.LoggerClientTest');
    +
    +goog.require('goog.debug');
    +goog.require('goog.debug.Logger');
    +goog.require('goog.messaging.LoggerClient');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var mockControl;
    +var channel;
    +var client;
    +var logger;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  channel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  client = new goog.messaging.LoggerClient(channel, 'log');
    +  logger = goog.debug.Logger.getLogger('test.logging.Object');
    +}
    +
    +function tearDown() {
    +  channel.dispose();
    +  client.dispose();
    +}
    +
    +function testCommand() {
    +  channel.send('log', {
    +    name: 'test.logging.Object',
    +    level: goog.debug.Logger.Level.WARNING.value,
    +    message: 'foo bar',
    +    exception: undefined
    +  });
    +  mockControl.$replayAll();
    +  logger.warning('foo bar');
    +  mockControl.$verifyAll();
    +}
    +
    +function testCommandWithException() {
    +  var ex = Error('oh no');
    +  ex.stack = ['one', 'two'];
    +  ex.message0 = 'message 0';
    +  ex.message1 = 'message 1';
    +  ex.ignoredProperty = 'ignored';
    +
    +  channel.send('log', {
    +    name: 'test.logging.Object',
    +    level: goog.debug.Logger.Level.WARNING.value,
    +    message: 'foo bar',
    +    exception: {
    +      name: 'Error',
    +      message: ex.message,
    +      stack: ex.stack,
    +      lineNumber: ex.lineNumber || 'Not available',
    +      fileName: ex.fileName || window.location.href,
    +      message0: ex.message0,
    +      message1: ex.message1
    +    }
    +  });
    +  mockControl.$replayAll();
    +  logger.warning('foo bar', ex);
    +  mockControl.$verifyAll();
    +}
    +
    +function testCommandWithStringException() {
    +  channel.send('log', {
    +    name: 'test.logging.Object',
    +    level: goog.debug.Logger.Level.WARNING.value,
    +    message: 'foo bar',
    +    exception: {
    +      name: 'Unknown error',
    +      message: 'oh no',
    +      stack: '[Anonymous](object, foo bar, oh no)\n' +
    +          '[Anonymous](foo bar, oh no)\n' + goog.debug.getStacktrace(),
    +      lineNumber: 'Not available',
    +      fileName: window.location.href
    +    }
    +  });
    +  mockControl.$replayAll();
    +  logger.warning('foo bar', 'oh no');
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerserver.js b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver.js
    new file mode 100644
    index 0000000000..3bd4e4903d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver.js
    @@ -0,0 +1,100 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This class listens on a message channel for logger commands and
    + * logs them on the local page. This is useful when dealing with message
    + * channels to contexts that don't have access to their own logging facilities.
    + *
    + */
    +
    +goog.provide('goog.messaging.LoggerServer');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +
    +
    +
    +/**
    + * Creates a logger server that logs messages on behalf of the remote end of a
    + * message channel. The remote end of the channel should use a
    + * {goog.messaging.LoggerClient} with the same service name.
    + *
    + * @param {!goog.messaging.MessageChannel} channel The channel that is sending
    + *     the log messages.
    + * @param {string} serviceName The name of the logging service to listen for.
    + * @param {string=} opt_channelName The name of this channel. Used to help
    + *     distinguish this client's messages.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.messaging.LoggerServer = function(channel, serviceName, opt_channelName) {
    +  goog.messaging.LoggerServer.base(this, 'constructor');
    +
    +  /**
    +   * The channel that is sending the log messages.
    +   * @type {!goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The name of the logging service to listen for.
    +   * @type {string}
    +   * @private
    +   */
    +  this.serviceName_ = serviceName;
    +
    +  /**
    +   * The name of the channel.
    +   * @type {string}
    +   * @private
    +   */
    +  this.channelName_ = opt_channelName || 'remote logger';
    +
    +  this.channel_.registerService(
    +      this.serviceName_, goog.bind(this.log_, this), true /* opt_json */);
    +};
    +goog.inherits(goog.messaging.LoggerServer, goog.Disposable);
    +
    +
    +/**
    + * Handles logging messages from the client.
    + * @param {!Object|string} message
    + *     The logging information from the client.
    + * @private
    + */
    +goog.messaging.LoggerServer.prototype.log_ = function(message) {
    +  var args =
    +      /**
    +       * @type {{level: number, message: string,
    +       *           name: string, exception: Object}}
    +       */ (message);
    +  var level = goog.log.Level.getPredefinedLevelByValue(args['level']);
    +  if (level) {
    +    var msg = '[' + this.channelName_ + '] ' + args['message'];
    +    goog.log.getLogger(args['name'])
    +        .log(level, msg, args['exception']);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.messaging.LoggerServer.prototype.disposeInternal = function() {
    +  goog.messaging.LoggerServer.base(this, 'disposeInternal');
    +  this.channel_.registerService(this.serviceName_, goog.nullFunction, true);
    +  delete this.channel_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.html b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.html
    new file mode 100644
    index 0000000000..ed4de99bdb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.LoggerServer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.LoggerServerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.js b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.js
    new file mode 100644
    index 0000000000..98ac7e2a96
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/loggerserver_test.js
    @@ -0,0 +1,101 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.LoggerServerTest');
    +goog.setTestOnly('goog.messaging.LoggerServerTest');
    +
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.debug.Logger');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.messaging.LoggerServer');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var mockControl;
    +var channel;
    +var stubs;
    +
    +function setUpPage() {
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  channel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  stubs.set(goog.debug.LogManager, 'getLogger',
    +            mockControl.createFunctionMock('goog.log.getLogger'));
    +}
    +
    +function tearDown() {
    +  channel.dispose();
    +  stubs.reset();
    +}
    +
    +function testCommandWithoutChannelName() {
    +  var mockLogger = mockControl.createStrictMock(goog.debug.Logger);
    +  goog.log.getLogger('test.object.Name').$returns(mockLogger);
    +  goog.log.log(mockLogger,
    +      goog.log.Level.SEVERE, '[remote logger] foo bar', null);
    +  mockControl.$replayAll();
    +
    +  var server = new goog.messaging.LoggerServer(channel, 'log');
    +  channel.receive('log', {
    +    name: 'test.object.Name',
    +    level: goog.log.Level.SEVERE.value,
    +    message: 'foo bar',
    +    exception: null
    +  });
    +  mockControl.$verifyAll();
    +  server.dispose();
    +}
    +
    +function testCommandWithChannelName() {
    +  var mockLogger = mockControl.createStrictMock(goog.debug.Logger);
    +  goog.log.getLogger('test.object.Name').$returns(mockLogger);
    +  goog.log.log(mockLogger,
    +      goog.log.Level.SEVERE, '[some channel] foo bar', null);
    +  mockControl.$replayAll();
    +
    +  var server = new goog.messaging.LoggerServer(channel, 'log', 'some channel');
    +  channel.receive('log', {
    +    name: 'test.object.Name',
    +    level: goog.log.Level.SEVERE.value,
    +    message: 'foo bar',
    +    exception: null
    +  });
    +  mockControl.$verifyAll();
    +  server.dispose();
    +}
    +
    +function testCommandWithException() {
    +  var mockLogger = mockControl.createStrictMock(goog.debug.Logger);
    +  goog.log.getLogger('test.object.Name').$returns(mockLogger);
    +  goog.log.log(mockLogger,
    +      goog.log.Level.SEVERE, '[some channel] foo bar',
    +      {message: 'Bad things', stack: ['foo', 'bar']});
    +  mockControl.$replayAll();
    +
    +  var server = new goog.messaging.LoggerServer(channel, 'log', 'some channel');
    +  channel.receive('log', {
    +    name: 'test.object.Name',
    +    level: goog.log.Level.SEVERE.value,
    +    message: 'foo bar',
    +    exception: {message: 'Bad things', stack: ['foo', 'bar']}
    +  });
    +  mockControl.$verifyAll();
    +  server.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/messagechannel.js b/mapbox-gl/closure-library/closure/goog/messaging/messagechannel.js
    new file mode 100644
    index 0000000000..8f99471931
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/messagechannel.js
    @@ -0,0 +1,116 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for asynchronous message-passing channels.
    + *
    + * This interface is useful for writing code in a message-passing style that's
    + * independent of the underlying communication medium. It's also useful for
    + * adding decorators that wrap message channels and add extra functionality on
    + * top. For example, {@link goog.messaging.BufferedChannel} enqueues messages
    + * until communication is established, while {@link goog.messaging.MultiChannel}
    + * splits a single underlying channel into multiple virtual ones.
    + *
    + * Decorators should be passed their underlying channel(s) in the constructor,
    + * and should assume that those channels are already connected. Decorators are
    + * responsible for disposing of the channels they wrap when the decorators
    + * themselves are disposed. Decorators should also follow the APIs of the
    + * individual methods listed below.
    + *
    + */
    +
    +
    +goog.provide('goog.messaging.MessageChannel');
    +
    +
    +
    +/**
    + * @interface
    + */
    +goog.messaging.MessageChannel = function() {};
    +
    +
    +/**
    + * Initiates the channel connection. When this method is called, all the
    + * information needed to connect the channel has to be available.
    + *
    + * Implementers should only require this method to be called if the channel
    + * needs to be configured in some way between when it's created and when it
    + * becomes active. Otherwise, the channel should be immediately active and this
    + * method should do nothing but immediately call opt_connectCb.
    + *
    + * @param {Function=} opt_connectCb Called when the channel has been connected
    + *     and is ready to use.
    + */
    +goog.messaging.MessageChannel.prototype.connect = function(opt_connectCb) {};
    +
    +
    +/**
    + * Gets whether the channel is connected.
    + *
    + * If {@link #connect} is not required for this class, this should always return
    + * true. Otherwise, this should return true by the time the callback passed to
    + * {@link #connect} has been called and always after that.
    + *
    + * @return {boolean} Whether the channel is connected.
    + */
    +goog.messaging.MessageChannel.prototype.isConnected = function() {};
    +
    +
    +/**
    + * Registers a service to be called when a message is received.
    + *
    + * Implementers shouldn't impose any restrictions on the service names that may
    + * be registered. If some services are needed as control codes,
    + * {@link goog.messaging.MultiMessageChannel} can be used to safely split the
    + * channel into "public" and "control" virtual channels.
    + *
    + * @param {string} serviceName The name of the service.
    + * @param {function((string|!Object))} callback The callback to process the
    + *     incoming messages. Passed the payload. If opt_objectPayload is set, the
    + *     payload is decoded and passed as an object.
    + * @param {boolean=} opt_objectPayload If true, incoming messages for this
    + *     service are expected to contain an object, and will be deserialized from
    + *     a string automatically if necessary. It's the responsibility of
    + *     implementors of this class to perform the deserialization.
    + */
    +goog.messaging.MessageChannel.prototype.registerService =
    +    function(serviceName, callback, opt_objectPayload) {};
    +
    +
    +/**
    + * Registers a service to be called when a message is received that doesn't
    + * match any other services.
    + *
    + * @param {function(string, (string|!Object))} callback The callback to process
    + *     the incoming messages. Passed the service name and the payload. Since
    + *     some channels can pass objects natively, the payload may be either an
    + *     object or a string.
    + */
    +goog.messaging.MessageChannel.prototype.registerDefaultService =
    +    function(callback) {};
    +
    +
    +/**
    + * Sends a message over the channel.
    + *
    + * @param {string} serviceName The name of the service this message should be
    + *     delivered to.
    + * @param {string|!Object} payload The value of the message. If this is an
    + *     Object, it is serialized to a string before sending if necessary. It's
    + *     the responsibility of implementors of this class to perform the
    + *     serialization.
    + */
    +goog.messaging.MessageChannel.prototype.send =
    +    function(serviceName, payload) {};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/messaging.js b/mapbox-gl/closure-library/closure/goog/messaging/messaging.js
    new file mode 100644
    index 0000000000..d96d67a1e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/messaging.js
    @@ -0,0 +1,32 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for manipulating message channels.
    + *
    + */
    +
    +goog.provide('goog.messaging');
    +
    +
    +/**
    + * Creates a bidirectional pipe between two message channels.
    + *
    + * @param {goog.messaging.MessageChannel} channel1 The first channel.
    + * @param {goog.messaging.MessageChannel} channel2 The second channel.
    + */
    +goog.messaging.pipe = function(channel1, channel2) {
    +  channel1.registerDefaultService(goog.bind(channel2.send, channel2));
    +  channel2.registerDefaultService(goog.bind(channel1.send, channel1));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.html b/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.html
    new file mode 100644
    index 0000000000..e7dd63eb54
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.messaging.MockMessageChannelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.js b/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.js
    new file mode 100644
    index 0000000000..a3610cc262
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/messaging_test.js
    @@ -0,0 +1,35 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.messaging.MockMessageChannelTest');
    +goog.setTestOnly('goog.testing.messaging.MockMessageChannelTest');
    +
    +goog.require('goog.messaging');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +function testPipe() {
    +  var mockControl = new goog.testing.MockControl();
    +  var ch1 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  var ch2 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  ch1.send('ping', 'HELLO');
    +  ch2.send('pong', {key: 'value'});
    +  goog.messaging.pipe(ch1, ch2);
    +
    +  mockControl.$replayAll();
    +  ch2.receive('ping', 'HELLO');
    +  ch1.receive('pong', {key: 'value'});
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/multichannel.js b/mapbox-gl/closure-library/closure/goog/messaging/multichannel.js
    new file mode 100644
    index 0000000000..b3bf68474f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/multichannel.js
    @@ -0,0 +1,303 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of goog.messaging.MultiChannel, which uses a
    + * single underlying MessageChannel to carry several independent virtual message
    + * channels.
    + *
    + */
    +
    +
    +goog.provide('goog.messaging.MultiChannel');
    +goog.provide('goog.messaging.MultiChannel.VirtualChannel');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.log');
    +goog.require('goog.messaging.MessageChannel'); // interface
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Creates a new MultiChannel wrapping a single MessageChannel. The
    + * underlying channel shouldn't have any other listeners registered, but it
    + * should be connected.
    + *
    + * Note that the other side of the channel should also be connected to a
    + * MultiChannel with the same number of virtual channels.
    + *
    + * @param {goog.messaging.MessageChannel} underlyingChannel The underlying
    + *     channel to use as transport for the virtual channels.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.messaging.MultiChannel = function(underlyingChannel) {
    +  goog.messaging.MultiChannel.base(this, 'constructor');
    +
    +  /**
    +   * The underlying channel across which all requests are sent.
    +   * @type {goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.underlyingChannel_ = underlyingChannel;
    +
    +  /**
    +   * All the virtual channels that are registered for this MultiChannel.
    +   * These are null if they've been disposed.
    +   * @type {Object<?goog.messaging.MultiChannel.VirtualChannel>}
    +   * @private
    +   */
    +  this.virtualChannels_ = {};
    +
    +  this.underlyingChannel_.registerDefaultService(
    +      goog.bind(this.handleDefault_, this));
    +};
    +goog.inherits(goog.messaging.MultiChannel, goog.Disposable);
    +
    +
    +/**
    + * Logger object for goog.messaging.MultiChannel.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.messaging.MultiChannel.prototype.logger_ =
    +    goog.log.getLogger('goog.messaging.MultiChannel');
    +
    +
    +/**
    + * Creates a new virtual channel that will communicate across the underlying
    + * channel.
    + * @param {string} name The name of the virtual channel. Must be unique for this
    + *     MultiChannel. Cannot contain colons.
    + * @return {!goog.messaging.MultiChannel.VirtualChannel} The new virtual
    + *     channel.
    + */
    +goog.messaging.MultiChannel.prototype.createVirtualChannel = function(name) {
    +  if (name.indexOf(':') != -1) {
    +    throw Error(
    +        'Virtual channel name "' + name + '" should not contain colons');
    +  }
    +
    +  if (name in this.virtualChannels_) {
    +    throw Error('Virtual channel "' + name + '" was already created for ' +
    +                'this multichannel.');
    +  }
    +
    +  var channel =
    +      new goog.messaging.MultiChannel.VirtualChannel(this, name);
    +  this.virtualChannels_[name] = channel;
    +  return channel;
    +};
    +
    +
    +/**
    + * Handles the default service for the underlying channel. This dispatches any
    + * unrecognized services to the appropriate virtual channel.
    + *
    + * @param {string} serviceName The name of the service being called.
    + * @param {string|!Object} payload The message payload.
    + * @private
    + */
    +goog.messaging.MultiChannel.prototype.handleDefault_ = function(
    +    serviceName, payload) {
    +  var match = serviceName.match(/^([^:]*):(.*)/);
    +  if (!match) {
    +    goog.log.warning(this.logger_,
    +        'Invalid service name "' + serviceName + '": no ' +
    +        'virtual channel specified');
    +    return;
    +  }
    +
    +  var channelName = match[1];
    +  serviceName = match[2];
    +  if (!(channelName in this.virtualChannels_)) {
    +    goog.log.warning(this.logger_,
    +        'Virtual channel "' + channelName + ' does not ' +
    +        'exist, but a message was received for it: "' + serviceName + '"');
    +    return;
    +  }
    +
    +  var virtualChannel = this.virtualChannels_[channelName];
    +  if (!virtualChannel) {
    +    goog.log.warning(this.logger_,
    +        'Virtual channel "' + channelName + ' has been ' +
    +        'disposed, but a message was received for it: "' + serviceName + '"');
    +    return;
    +  }
    +
    +  if (!virtualChannel.defaultService_) {
    +    goog.log.warning(this.logger_,
    +        'Service "' + serviceName + '" is not registered ' +
    +        'on virtual channel "' + channelName + '"');
    +    return;
    +  }
    +
    +  virtualChannel.defaultService_(serviceName, payload);
    +};
    +
    +
    +/** @override */
    +goog.messaging.MultiChannel.prototype.disposeInternal = function() {
    +  goog.object.forEach(this.virtualChannels_, function(channel) {
    +    goog.dispose(channel);
    +  });
    +  goog.dispose(this.underlyingChannel_);
    +  delete this.virtualChannels_;
    +  delete this.underlyingChannel_;
    +};
    +
    +
    +
    +/**
    + * A message channel that proxies its messages over another underlying channel.
    + *
    + * @param {goog.messaging.MultiChannel} parent The MultiChannel
    + *     which created this channel, and which contains the underlying
    + *     MessageChannel that's used as the transport.
    + * @param {string} name The name of this virtual channel. Unique among the
    + *     virtual channels in parent.
    + * @constructor
    + * @implements {goog.messaging.MessageChannel}
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.messaging.MultiChannel.VirtualChannel = function(parent, name) {
    +  goog.messaging.MultiChannel.VirtualChannel.base(this, 'constructor');
    +
    +  /**
    +   * The MultiChannel containing the underlying transport channel.
    +   * @type {goog.messaging.MultiChannel}
    +   * @private
    +   */
    +  this.parent_ = parent;
    +
    +  /**
    +   * The name of this virtual channel.
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = name;
    +};
    +goog.inherits(goog.messaging.MultiChannel.VirtualChannel,
    +              goog.Disposable);
    +
    +
    +/**
    + * The default service to run if no other services match.
    + * @type {?function(string, (string|!Object))}
    + * @private
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.defaultService_;
    +
    +
    +/**
    + * Logger object for goog.messaging.MultiChannel.VirtualChannel.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.logger_ =
    +    goog.log.getLogger(
    +        'goog.messaging.MultiChannel.VirtualChannel');
    +
    +
    +/**
    + * This is a no-op, since the underlying channel is expected to already be
    + * initialized when it's passed in.
    + *
    + * @override
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.connect =
    +    function(opt_connectCb) {
    +  if (opt_connectCb) {
    +    opt_connectCb();
    +  }
    +};
    +
    +
    +/**
    + * This always returns true, since the underlying channel is expected to already
    + * be initialized when it's passed in.
    + *
    + * @override
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.isConnected =
    +    function() {
    +  return true;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.registerService =
    +    function(serviceName, callback, opt_objectPayload) {
    +  this.parent_.underlyingChannel_.registerService(
    +      this.name_ + ':' + serviceName,
    +      goog.bind(this.doCallback_, this, callback),
    +      opt_objectPayload);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.
    +    registerDefaultService = function(callback) {
    +  this.defaultService_ = goog.bind(this.doCallback_, this, callback);
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.send =
    +    function(serviceName, payload) {
    +  if (this.isDisposed()) {
    +    throw Error('#send called for disposed VirtualChannel.');
    +  }
    +
    +  this.parent_.underlyingChannel_.send(this.name_ + ':' + serviceName,
    +                                       payload);
    +};
    +
    +
    +/**
    + * Wraps a callback with a function that will log a warning and abort if it's
    + * called when this channel is disposed.
    + *
    + * @param {function()} callback The callback to wrap.
    + * @param {...*} var_args Other arguments, passed to the callback.
    + * @private
    + */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.doCallback_ =
    +    function(callback, var_args) {
    +  if (this.isDisposed()) {
    +    goog.log.warning(this.logger_,
    +        'Virtual channel "' + this.name_ + '" received ' +
    +        ' a message after being disposed.');
    +    return;
    +  }
    +
    +  callback.apply({}, Array.prototype.slice.call(arguments, 1));
    +};
    +
    +
    +/** @override */
    +goog.messaging.MultiChannel.VirtualChannel.prototype.disposeInternal =
    +    function() {
    +  this.parent_.virtualChannels_[this.name_] = null;
    +  this.parent_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.html
    new file mode 100644
    index 0000000000..ac61e0952e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.MultiChannel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.MultiChannelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.js b/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.js
    new file mode 100644
    index 0000000000..ddea8efe87
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/multichannel_test.js
    @@ -0,0 +1,116 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.MultiChannelTest');
    +goog.setTestOnly('goog.messaging.MultiChannelTest');
    +
    +goog.require('goog.messaging.MultiChannel');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +goog.require('goog.testing.mockmatchers.IgnoreArgument');
    +
    +var mockControl;
    +var mockChannel;
    +var multiChannel;
    +var channel1;
    +var channel2;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  multiChannel = new goog.messaging.MultiChannel(mockChannel);
    +  channel0 = multiChannel.createVirtualChannel('foo');
    +  channel1 = multiChannel.createVirtualChannel('bar');
    +}
    +
    +function expectedFn(name, callback) {
    +  var ignored = new goog.testing.mockmatchers.IgnoreArgument();
    +  var fn = mockControl.createFunctionMock(name);
    +  fn(ignored).$does(function(args) {
    +    callback.apply(this, args);
    +  });
    +  return function() { fn(arguments); };
    +}
    +
    +function notExpectedFn() {
    +  return mockControl.createFunctionMock('notExpectedFn');
    +}
    +
    +function assertEqualsFn() {
    +  var expectedArgs = Array.prototype.slice.call(arguments);
    +  return expectedFn('assertEqualsFn', function() {
    +    assertObjectEquals(expectedArgs, Array.prototype.slice.call(arguments));
    +  });
    +}
    +
    +function tearDown() {
    +  multiChannel.dispose();
    +  mockControl.$verifyAll();
    +  assertTrue(mockChannel.disposed);
    +}
    +
    +function testSend0() {
    +  mockChannel.send('foo:fooBar', {foo: 'bar'});
    +  mockControl.$replayAll();
    +  channel0.send('fooBar', {foo: 'bar'});
    +}
    +
    +function testSend1() {
    +  mockChannel.send('bar:fooBar', {foo: 'bar'});
    +  mockControl.$replayAll();
    +  channel1.send('fooBar', {foo: 'bar'});
    +}
    +
    +function testReceive0() {
    +  channel0.registerService('fooBar', assertEqualsFn('Baz bang'));
    +  channel1.registerService('fooBar', notExpectedFn());
    +  mockControl.$replayAll();
    +  mockChannel.receive('foo:fooBar', 'Baz bang');
    +}
    +
    +function testReceive1() {
    +  channel1.registerService('fooBar', assertEqualsFn('Baz bang'));
    +  channel0.registerService('fooBar', notExpectedFn());
    +  mockControl.$replayAll();
    +  mockChannel.receive('bar:fooBar', 'Baz bang');
    +}
    +
    +function testDefaultReceive0() {
    +  channel0.registerDefaultService(assertEqualsFn('fooBar', 'Baz bang'));
    +  channel1.registerDefaultService(notExpectedFn());
    +  mockControl.$replayAll();
    +  mockChannel.receive('foo:fooBar', 'Baz bang');
    +}
    +
    +function testDefaultReceive1() {
    +  channel1.registerDefaultService(assertEqualsFn('fooBar', 'Baz bang'));
    +  channel0.registerDefaultService(notExpectedFn());
    +  mockControl.$replayAll();
    +  mockChannel.receive('bar:fooBar', 'Baz bang');
    +}
    +
    +function testReceiveAfterDisposed() {
    +  channel0.registerService('fooBar', notExpectedFn());
    +  mockControl.$replayAll();
    +  channel0.dispose();
    +  mockChannel.receive('foo:fooBar', 'Baz bang');
    +}
    +
    +function testReceiveAfterParentDisposed() {
    +  channel0.registerService('fooBar', notExpectedFn());
    +  mockControl.$replayAll();
    +  multiChannel.dispose();
    +  mockChannel.receive('foo:fooBar', 'Baz bang');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portcaller.js b/mapbox-gl/closure-library/closure/goog/messaging/portcaller.js
    new file mode 100644
    index 0000000000..7a9f7a4b59
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portcaller.js
    @@ -0,0 +1,152 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The leaf node of a {@link goog.messaging.PortNetwork}. Callers
    + * connect to the operator, and request connections with other contexts from it.
    + *
    + */
    +
    +goog.provide('goog.messaging.PortCaller');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.messaging.DeferredChannel');
    +goog.require('goog.messaging.PortChannel');
    +goog.require('goog.messaging.PortNetwork'); // interface
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * The leaf node of a network.
    + *
    + * @param {!goog.messaging.MessageChannel} operatorPort The channel for
    + *     communicating with the operator. The other side of this channel should be
    + *     passed to {@link goog.messaging.PortOperator#addPort}. Must be either a
    + *     {@link goog.messaging.PortChannel} or a decorator wrapping a PortChannel;
    + *     in particular, it must be able to send and receive {@link MessagePort}s.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.messaging.PortNetwork}
    + * @final
    + */
    +goog.messaging.PortCaller = function(operatorPort) {
    +  goog.messaging.PortCaller.base(this, 'constructor');
    +
    +  /**
    +   * The channel to the {@link goog.messaging.PortOperator} for this network.
    +   *
    +   * @type {!goog.messaging.MessageChannel}
    +   * @private
    +   */
    +  this.operatorPort_ = operatorPort;
    +
    +  /**
    +   * The collection of channels for communicating with other contexts in the
    +   * network. Each value can contain a {@link goog.aync.Deferred} and/or a
    +   * {@link goog.messaging.MessageChannel}.
    +   *
    +   * If the value contains a Deferred, then the channel is a
    +   * {@link goog.messaging.DeferredChannel} wrapping that Deferred. The Deferred
    +   * will be resolved with a {@link goog.messaging.PortChannel} once we receive
    +   * the appropriate port from the operator. This is the situation when this
    +   * caller requests a connection to another context; the DeferredChannel is
    +   * used to queue up messages until we receive the port from the operator.
    +   *
    +   * If the value does not contain a Deferred, then the channel is simply a
    +   * {@link goog.messaging.PortChannel} communicating with the given context.
    +   * This is the situation when this context received a port for the other
    +   * context before it was requested.
    +   *
    +   * If a value exists for a given key, it must contain a channel, but it
    +   * doesn't necessarily contain a Deferred.
    +   *
    +   * @type {!Object<{deferred: goog.async.Deferred,
    +   *                  channel: !goog.messaging.MessageChannel}>}
    +   * @private
    +   */
    +  this.connections_ = {};
    +
    +  this.operatorPort_.registerService(
    +      goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
    +      goog.bind(this.connectionGranted_, this),
    +      true /* opt_json */);
    +};
    +goog.inherits(goog.messaging.PortCaller, goog.Disposable);
    +
    +
    +/** @override */
    +goog.messaging.PortCaller.prototype.dial = function(name) {
    +  if (name in this.connections_) {
    +    return this.connections_[name].channel;
    +  }
    +
    +  this.operatorPort_.send(
    +      goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, name);
    +  var deferred = new goog.async.Deferred();
    +  var channel = new goog.messaging.DeferredChannel(deferred);
    +  this.connections_[name] = {deferred: deferred, channel: channel};
    +  return channel;
    +};
    +
    +
    +/**
    + * Registers a connection to another context in the network. This is called when
    + * the operator sends us one end of a {@link MessageChannel}, either because
    + * this caller requested a connection with another context, or because that
    + * context requested a connection with this caller.
    + *
    + * It's possible that the remote context and this one request each other roughly
    + * concurrently. The operator doesn't keep track of which contexts have been
    + * connected, so it will create two separate {@link MessageChannel}s in this
    + * case. However, the first channel created will reach both contexts first, so
    + * we simply ignore all connections with a given context after the first.
    + *
    + * @param {!Object|string} message The name of the context
    + *     being connected and the port connecting the context.
    + * @private
    + */
    +goog.messaging.PortCaller.prototype.connectionGranted_ = function(message) {
    +  var args = /** @type {{name: string, port: MessagePort}} */ (message);
    +  var port = args['port'];
    +  var entry = this.connections_[args['name']];
    +  if (entry && (!entry.deferred || entry.deferred.hasFired())) {
    +    // If two PortCallers request one another at the same time, the operator may
    +    // send out a channel for connecting them multiple times. Since both callers
    +    // will receive the first channel's ports first, we can safely ignore and
    +    // close any future ports.
    +    port.close();
    +  } else if (!args['success']) {
    +    throw Error(args['message']);
    +  } else {
    +    port.start();
    +    var channel = new goog.messaging.PortChannel(port);
    +    if (entry) {
    +      entry.deferred.callback(channel);
    +    } else {
    +      this.connections_[args['name']] = {channel: channel, deferred: null};
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.messaging.PortCaller.prototype.disposeInternal = function() {
    +  goog.dispose(this.operatorPort_);
    +  goog.object.forEach(this.connections_, goog.dispose);
    +  delete this.operatorPort_;
    +  delete this.connections_;
    +  goog.messaging.PortCaller.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.html b/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.html
    new file mode 100644
    index 0000000000..2bf256574f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.PortCaller
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.PortCallerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.js b/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.js
    new file mode 100644
    index 0000000000..3f4a002427
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portcaller_test.js
    @@ -0,0 +1,58 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.PortCallerTest');
    +goog.setTestOnly('goog.messaging.PortCallerTest');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.messaging.PortCaller');
    +goog.require('goog.messaging.PortNetwork');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var mockControl;
    +var mockChannel;
    +var caller;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  mockChannel = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  caller = new goog.messaging.PortCaller(mockChannel);
    +}
    +
    +function tearDown() {
    +  goog.dispose(caller);
    +  mockControl.$verifyAll();
    +}
    +
    +function MockMessagePort(index, port) {
    +  goog.base(this);
    +  this.index = index;
    +  this.port = port;
    +  this.started = false;
    +}
    +goog.inherits(MockMessagePort, goog.events.EventTarget);
    +
    +
    +MockMessagePort.prototype.start = function() {
    +  this.started = true;
    +};
    +
    +function testGetPort() {
    +  mockChannel.send(
    +      goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, 'foo');
    +  mockControl.$replayAll();
    +  caller.dial('foo');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portchannel.js b/mapbox-gl/closure-library/closure/goog/messaging/portchannel.js
    new file mode 100644
    index 0000000000..1904a476ec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portchannel.js
    @@ -0,0 +1,401 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class that wraps several types of HTML5 message-passing
    + * entities ({@link MessagePort}s, {@link WebWorker}s, and {@link Window}s),
    + * providing a unified interface.
    + *
    + * This is tested under Chrome, Safari, and Firefox. Since Firefox 3.6 has an
    + * incomplete implementation of web workers, it doesn't support sending ports
    + * over Window connections. IE has no web worker support at all, and so is
    + * unsupported by this class.
    + *
    + */
    +
    +goog.provide('goog.messaging.PortChannel');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.debug');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.messaging.AbstractChannel');
    +goog.require('goog.messaging.DeferredChannel');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A wrapper for several types of HTML5 message-passing entities
    + * ({@link MessagePort}s and {@link WebWorker}s). This class implements the
    + * {@link goog.messaging.MessageChannel} interface.
    + *
    + * This class can be used in conjunction with other communication on the port.
    + * It sets {@link goog.messaging.PortChannel.FLAG} to true on all messages it
    + * sends.
    + *
    + * @param {!MessagePort|!WebWorker} underlyingPort The message-passing
    + *     entity to wrap. If this is a {@link MessagePort}, it should be started.
    + *     The remote end should also be wrapped in a PortChannel. This will be
    + *     disposed along with the PortChannel; this means terminating it if it's a
    + *     worker or removing it from the DOM if it's an iframe.
    + * @constructor
    + * @extends {goog.messaging.AbstractChannel}
    + * @final
    + */
    +goog.messaging.PortChannel = function(underlyingPort) {
    +  goog.messaging.PortChannel.base(this, 'constructor');
    +
    +  /**
    +   * The wrapped message-passing entity.
    +   * @type {!MessagePort|!WebWorker}
    +   * @private
    +   */
    +  this.port_ = underlyingPort;
    +
    +  /**
    +   * The key for the event listener.
    +   * @type {goog.events.Key}
    +   * @private
    +   */
    +  this.listenerKey_ = goog.events.listen(
    +      this.port_, goog.events.EventType.MESSAGE, this.deliver_, false, this);
    +};
    +goog.inherits(goog.messaging.PortChannel, goog.messaging.AbstractChannel);
    +
    +
    +/**
    + * Create a PortChannel that communicates with a window embedded in the current
    + * page (e.g. an iframe contentWindow). The code within the window should call
    + * {@link forGlobalWindow} to establish the connection.
    + *
    + * It's possible to use this channel in conjunction with other messages to the
    + * embedded window. However, only one PortChannel should be used for a given
    + * window at a time.
    + *
    + * @param {!Window} window The window object to communicate with.
    + * @param {string} peerOrigin The expected origin of the window. See
    + *     http://dev.w3.org/html5/postmsg/#dom-window-postmessage.
    + * @param {goog.Timer=} opt_timer The timer that regulates how often the initial
    + *     connection message is attempted. This will be automatically disposed once
    + *     the connection is established, or when the connection is cancelled.
    + * @return {!goog.messaging.DeferredChannel} The PortChannel. Although this is
    + *     not actually an instance of the PortChannel class, it will behave like
    + *     one in that MessagePorts may be sent across it. The DeferredChannel may
    + *     be cancelled before a connection is established in order to abort the
    + *     attempt to make a connection.
    + */
    +goog.messaging.PortChannel.forEmbeddedWindow = function(
    +    window, peerOrigin, opt_timer) {
    +  var timer = opt_timer || new goog.Timer(50);
    +
    +  var disposeTimer = goog.partial(goog.dispose, timer);
    +  var deferred = new goog.async.Deferred(disposeTimer);
    +  deferred.addBoth(disposeTimer);
    +
    +  timer.start();
    +  // Every tick, attempt to set up a connection by sending in one end of an
    +  // HTML5 MessageChannel. If the inner window posts a response along a channel,
    +  // then we'll use that channel to create the PortChannel.
    +  //
    +  // As per http://dev.w3.org/html5/postmsg/#ports-and-garbage-collection, any
    +  // ports that are not ultimately used to set up the channel will be garbage
    +  // collected (since there are no references in this context, and the remote
    +  // context hasn't seen them).
    +  goog.events.listen(timer, goog.Timer.TICK, function() {
    +    var channel = new MessageChannel();
    +    var gotMessage = function(e) {
    +      channel.port1.removeEventListener(
    +          goog.events.EventType.MESSAGE, gotMessage, true);
    +      // If the connection has been cancelled, don't create the channel.
    +      if (!timer.isDisposed()) {
    +        deferred.callback(new goog.messaging.PortChannel(channel.port1));
    +      }
    +    };
    +    channel.port1.start();
    +    // Don't use goog.events because we don't want any lingering references to
    +    // the ports to prevent them from getting GCed. Only modern browsers support
    +    // these APIs anyway, so we don't need to worry about event API
    +    // compatibility.
    +    channel.port1.addEventListener(
    +        goog.events.EventType.MESSAGE, gotMessage, true);
    +
    +    var msg = {};
    +    msg[goog.messaging.PortChannel.FLAG] = true;
    +    window.postMessage(msg, peerOrigin, [channel.port2]);
    +  });
    +
    +  return new goog.messaging.DeferredChannel(deferred);
    +};
    +
    +
    +/**
    + * Create a PortChannel that communicates with the document in which this window
    + * is embedded (e.g. within an iframe). The enclosing document should call
    + * {@link forEmbeddedWindow} to establish the connection.
    + *
    + * It's possible to use this channel in conjunction with other messages posted
    + * to the global window. However, only one PortChannel should be used for the
    + * global window at a time.
    + *
    + * @param {string} peerOrigin The expected origin of the enclosing document. See
    + *     http://dev.w3.org/html5/postmsg/#dom-window-postmessage.
    + * @return {!goog.messaging.MessageChannel} The PortChannel. Although this may
    + *     not actually be an instance of the PortChannel class, it will behave like
    + *     one in that MessagePorts may be sent across it.
    + */
    +goog.messaging.PortChannel.forGlobalWindow = function(peerOrigin) {
    +  var deferred = new goog.async.Deferred();
    +  // Wait for the external page to post a message containing the message port
    +  // which we'll use to set up the PortChannel. Ignore all other messages. Once
    +  // we receive the port, notify the other end and then set up the PortChannel.
    +  var key = goog.events.listen(
    +      window, goog.events.EventType.MESSAGE, function(e) {
    +        var browserEvent = e.getBrowserEvent();
    +        var data = browserEvent.data;
    +        if (!goog.isObject(data) || !data[goog.messaging.PortChannel.FLAG]) {
    +          return;
    +        }
    +
    +        if (peerOrigin != '*' && peerOrigin != browserEvent.origin) {
    +          return;
    +        }
    +
    +        var port = browserEvent.ports[0];
    +        // Notify the other end of the channel that we've received our port
    +        port.postMessage({});
    +
    +        port.start();
    +        deferred.callback(new goog.messaging.PortChannel(port));
    +        goog.events.unlistenByKey(key);
    +      });
    +  return new goog.messaging.DeferredChannel(deferred);
    +};
    +
    +
    +/**
    + * The flag added to messages that are sent by a PortChannel, and are meant to
    + * be handled by one on the other side.
    + * @type {string}
    + */
    +goog.messaging.PortChannel.FLAG = '--goog.messaging.PortChannel';
    +
    +
    +/**
    + * Whether the messages sent across the channel must be JSON-serialized. This is
    + * required for older versions of Webkit, which can only send string messages.
    + *
    + * Although Safari and Chrome have separate implementations of message passing,
    + * both of them support passing objects by Webkit 533.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.messaging.PortChannel.REQUIRES_SERIALIZATION_ = goog.userAgent.WEBKIT &&
    +    goog.string.compareVersions(goog.userAgent.VERSION, '533') < 0;
    +
    +
    +/**
    + * Logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.messaging.PortChannel.prototype.logger =
    +    goog.log.getLogger('goog.messaging.PortChannel');
    +
    +
    +/**
    + * Sends a message over the channel.
    + *
    + * As an addition to the basic MessageChannel send API, PortChannels can send
    + * objects that contain MessagePorts. Note that only plain Objects and Arrays,
    + * not their subclasses, can contain MessagePorts.
    + *
    + * As per {@link http://www.w3.org/TR/html5/comms.html#clone-a-port}, once a
    + * port is copied to be sent across a channel, the original port will cease
    + * being able to send or receive messages.
    + *
    + * @override
    + * @param {string} serviceName The name of the service this message should be
    + *     delivered to.
    + * @param {string|!Object|!MessagePort} payload The value of the message. May
    + *     contain MessagePorts or be a MessagePort.
    + */
    +goog.messaging.PortChannel.prototype.send = function(serviceName, payload) {
    +  var ports = [];
    +  payload = this.extractPorts_(ports, payload);
    +  var message = {'serviceName': serviceName, 'payload': payload};
    +  message[goog.messaging.PortChannel.FLAG] = true;
    +
    +  if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
    +    message = goog.json.serialize(message);
    +  }
    +
    +  this.port_.postMessage(message, ports);
    +};
    +
    +
    +/**
    + * Delivers a message to the appropriate service handler. If this message isn't
    + * a GearsWorkerChannel message, it's ignored and passed on to other handlers.
    + *
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.messaging.PortChannel.prototype.deliver_ = function(e) {
    +  var browserEvent = e.getBrowserEvent();
    +  var data = browserEvent.data;
    +
    +  if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
    +    try {
    +      data = goog.json.parse(data);
    +    } catch (error) {
    +      // Ignore any non-JSON messages.
    +      return;
    +    }
    +  }
    +
    +  if (!goog.isObject(data) || !data[goog.messaging.PortChannel.FLAG]) {
    +    return;
    +  }
    +
    +  if (this.validateMessage_(data)) {
    +    var serviceName = data['serviceName'];
    +    var payload = data['payload'];
    +    var service = this.getService(serviceName, payload);
    +    if (!service) {
    +      return;
    +    }
    +
    +    payload = this.decodePayload(
    +        serviceName,
    +        this.injectPorts_(browserEvent.ports || [], payload),
    +        service.objectPayload);
    +    if (goog.isDefAndNotNull(payload)) {
    +      service.callback(payload);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Checks whether the message is invalid in some way.
    + *
    + * @param {Object} data The contents of the message.
    + * @return {boolean} True if the message is valid, false otherwise.
    + * @private
    + */
    +goog.messaging.PortChannel.prototype.validateMessage_ = function(data) {
    +  if (!('serviceName' in data)) {
    +    goog.log.warning(this.logger,
    +        'Message object doesn\'t contain service name: ' +
    +        goog.debug.deepExpose(data));
    +    return false;
    +  }
    +
    +  if (!('payload' in data)) {
    +    goog.log.warning(this.logger,
    +        'Message object doesn\'t contain payload: ' +
    +        goog.debug.deepExpose(data));
    +    return false;
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Extracts all MessagePort objects from a message to be sent into an array.
    + *
    + * The message ports are replaced by placeholder objects that will be replaced
    + * with the ports again on the other side of the channel.
    + *
    + * @param {Array<MessagePort>} ports The array that will contain ports
    + *     extracted from the message. Will be destructively modified. Should be
    + *     empty initially.
    + * @param {string|!Object} message The message from which ports will be
    + *     extracted.
    + * @return {string|!Object} The message with ports extracted.
    + * @private
    + */
    +goog.messaging.PortChannel.prototype.extractPorts_ = function(ports, message) {
    +  // Can't use instanceof here because MessagePort is undefined in workers
    +  if (message &&
    +      Object.prototype.toString.call(/** @type {!Object} */ (message)) ==
    +      '[object MessagePort]') {
    +    ports.push(message);
    +    return {'_port': {'type': 'real', 'index': ports.length - 1}};
    +  } else if (goog.isArray(message)) {
    +    return goog.array.map(message, goog.bind(this.extractPorts_, this, ports));
    +  // We want to compare the exact constructor here because we only want to
    +  // recurse into object literals, not native objects like Date.
    +  } else if (message && message.constructor == Object) {
    +    return goog.object.map(/** @type {!Object} */(message), function(val, key) {
    +      val = this.extractPorts_(ports, val);
    +      return key == '_port' ? {'type': 'escaped', 'val': val} : val;
    +    }, this);
    +  } else {
    +    return message;
    +  }
    +};
    +
    +
    +/**
    + * Injects MessagePorts back into a message received from across the channel.
    + *
    + * @param {Array<MessagePort>} ports The array of ports to be injected into the
    + *     message.
    + * @param {string|!Object} message The message into which the ports will be
    + *     injected.
    + * @return {string|!Object} The message with ports injected.
    + * @private
    + */
    +goog.messaging.PortChannel.prototype.injectPorts_ = function(ports, message) {
    +  if (goog.isArray(message)) {
    +    return goog.array.map(message, goog.bind(this.injectPorts_, this, ports));
    +  } else if (message && message.constructor == Object) {
    +    message = /** @type {!Object} */ (message);
    +    if (message['_port'] && message['_port']['type'] == 'real') {
    +      return /** @type {!MessagePort} */ (ports[message['_port']['index']]);
    +    }
    +    return goog.object.map(message, function(val, key) {
    +      return this.injectPorts_(ports, key == '_port' ? val['val'] : val);
    +    }, this);
    +  } else {
    +    return message;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.messaging.PortChannel.prototype.disposeInternal = function() {
    +  goog.events.unlistenByKey(this.listenerKey_);
    +  // Can't use instanceof here because MessagePort is undefined in workers and
    +  // in Firefox
    +  if (Object.prototype.toString.call(this.port_) == '[object MessagePort]') {
    +    this.port_.close();
    +  // Worker is undefined in workers as well as of Chrome 9
    +  } else if (Object.prototype.toString.call(this.port_) == '[object Worker]') {
    +    this.port_.terminate();
    +  }
    +  delete this.port_;
    +  goog.messaging.PortChannel.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portchannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/portchannel_test.html
    new file mode 100644
    index 0000000000..d0f6f5f374
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portchannel_test.html
    @@ -0,0 +1,392 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<title>
    +  Closure Unit Tests - goog.messaging.PortChannel
    +</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.messaging.PortChannel');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageEvent');
    +goog.require('goog.userAgent.product');
    +</script>
    +</head>
    +<body>
    +<div id="frame"></div>
    +<script>
    +
    +var mockControl;
    +var asyncMockControl;
    +var mockPort;
    +var portChannel;
    +
    +var workerChannel;
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var timer;
    +
    +// Use a relatively long timeout because workers can take a while to start up.
    +asyncTestCase.stepTimeout = 3 * 1000;
    +
    +function setUpPage() {
    +  if (!('Worker' in goog.global)) {
    +    return;
    +  }
    +  workerChannel = new goog.messaging.PortChannel(
    +      new Worker('testdata/portchannel_worker.js'));
    +}
    +
    +function tearDownPage() {
    +  goog.dispose(workerChannel);
    +}
    +
    +function setUp() {
    +  timer = new goog.Timer(50);
    +  mockControl = new goog.testing.MockControl();
    +  asyncMockControl = new goog.testing.async.MockControl(mockControl);
    +  mockPort = new goog.events.EventTarget();
    +  mockPort.postMessage = mockControl.createFunctionMock('postMessage');
    +  portChannel = new goog.messaging.PortChannel(mockPort);
    +}
    +
    +function tearDown() {
    +  goog.dispose(timer);
    +  portChannel.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +function makeMessage(serviceName, payload) {
    +  var msg = {'serviceName': serviceName, 'payload': payload};
    +  msg[goog.messaging.PortChannel.FLAG] = true;
    +  if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
    +    msg = goog.json.serialize(msg);
    +  }
    +  return msg;
    +}
    +
    +function expectNoMessage() {
    +  portChannel.registerDefaultService(
    +    mockControl.createFunctionMock('expectNoMessage'));
    +}
    +
    +function receiveMessage(serviceName, payload, opt_origin, opt_ports) {
    +  mockPort.dispatchEvent(
    +      goog.testing.messaging.MockMessageEvent.wrap(
    +          makeMessage(serviceName, payload),
    +          opt_origin || 'http://google.com',
    +          undefined, undefined, opt_ports));
    +}
    +
    +function receiveNonChannelMessage(data) {
    +  if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_ &&
    +      !goog.isString(data)) {
    +    data = goog.json.serialize(data);
    +  }
    +  mockPort.dispatchEvent(
    +      goog.testing.messaging.MockMessageEvent.wrap(
    +          data, 'http://google.com'));
    +}
    +
    +function testPostMessage() {
    +  mockPort.postMessage(makeMessage('foobar', 'This is a value'), []);
    +  mockControl.$replayAll();
    +  portChannel.send('foobar', 'This is a value');
    +}
    +
    +function testPostMessageWithPorts() {
    +  if (!('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  var channel = new MessageChannel();
    +  var port1 = channel.port1;
    +  var port2 = channel.port2;
    +  mockPort.postMessage(makeMessage('foobar', {'val': [
    +    {'_port': {'type': 'real', 'index': 0}},
    +    {'_port': {'type': 'real', 'index': 1}}
    +  ]}), [port1, port2]);
    +  mockControl.$replayAll();
    +  portChannel.send('foobar', {'val': [port1, port2]});
    +}
    +
    +function testReceiveMessage() {
    +  portChannel.registerService(
    +      'foobar', asyncMockControl.asyncAssertEquals(
    +          'testReceiveMessage', 'This is a string'));
    +  mockControl.$replayAll();
    +  receiveMessage('foobar', 'This is a string');
    +}
    +
    +function testReceiveMessageWithPorts() {
    +  if (!('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  var channel = new MessageChannel();
    +  var port1 = channel.port1;
    +  var port2 = channel.port2;
    +  portChannel.registerService(
    +      'foobar', asyncMockControl.asyncAssertEquals(
    +          'testReceiveMessage', {'val': [port1, port2]}),
    +      true);
    +  mockControl.$replayAll();
    +  receiveMessage('foobar', {'val': [
    +    {'_port': {'type': 'real', 'index': 0}},
    +    {'_port': {'type': 'real', 'index': 1}}
    +  ]}, null, [port1, port2]);
    +}
    +
    +function testReceiveNonChannelMessageWithStringBody() {
    +  expectNoMessage();
    +  mockControl.$replayAll();
    +  receiveNonChannelMessage('Foo bar');
    +}
    +
    +function testReceiveNonChannelMessageWithArrayBody() {
    +  expectNoMessage();
    +  mockControl.$replayAll();
    +  receiveNonChannelMessage([5, 'Foo bar']);
    +}
    +
    +function testReceiveNonChannelMessageWithNoFlag() {
    +  expectNoMessage();
    +  mockControl.$replayAll();
    +  receiveNonChannelMessage({
    +    serviceName: 'foobar',
    +    payload: 'this is a payload'
    +  });
    +}
    +
    +function testReceiveNonChannelMessageWithFalseFlag() {
    +  expectNoMessage();
    +  mockControl.$replayAll();
    +  var body = {
    +    serviceName: 'foobar',
    +    payload: 'this is a payload'
    +  };
    +  body[goog.messaging.PortChannel.FLAG] = false;
    +  receiveNonChannelMessage(body);
    +}
    +
    +// Integration tests
    +
    +function testWorker() {
    +  if (!('Worker' in goog.global)) {
    +    return;
    +  }
    +  workerChannel.registerService('pong', function(msg) {
    +    assertObjectEquals({'val': 'fizzbang'}, msg);
    +    asyncTestCase.continueTesting();
    +  }, true);
    +  workerChannel.send('ping', {'val': 'fizzbang'});
    +  asyncTestCase.waitForAsync('worker response');
    +}
    +
    +function testWorkerWithPorts() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  var messageChannel = new MessageChannel();
    +  workerChannel.registerService('pong', function(msg) {
    +    assertPortsEntangled(msg['port'], messageChannel.port2, function() {
    +      asyncTestCase.continueTesting();
    +    });
    +  }, true);
    +  workerChannel.send('ping', {'port': messageChannel.port1});
    +  asyncTestCase.waitForAsync('worker response');
    +}
    +
    +function testPort() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  var messageChannel = new MessageChannel();
    +  workerChannel.send('addPort', messageChannel.port1);
    +  messageChannel.port2.start();
    +  var realPortChannel = new goog.messaging.PortChannel(messageChannel.port2);
    +  realPortChannel.registerService('pong', function(msg) {
    +    assertObjectEquals({'val': 'fizzbang'}, msg);
    +
    +    messageChannel.port2.close();
    +    realPortChannel.dispose();
    +    asyncTestCase.continueTesting();
    +  }, true);
    +  realPortChannel.send('ping', {'val': 'fizzbang'});
    +  asyncTestCase.waitForAsync('port response');
    +}
    +
    +function testPortIgnoresOrigin() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  var messageChannel = new MessageChannel();
    +  workerChannel.send('addPort', messageChannel.port1);
    +  messageChannel.port2.start();
    +  var realPortChannel = new goog.messaging.PortChannel(
    +      messageChannel.port2, 'http://somewhere-else.com');
    +  realPortChannel.registerService('pong', function(msg) {
    +    assertObjectEquals({'val': 'fizzbang'}, msg);
    +
    +    messageChannel.port2.close();
    +    realPortChannel.dispose();
    +    asyncTestCase.continueTesting();
    +  }, true);
    +  realPortChannel.send('ping', {'val': 'fizzbang'});
    +  asyncTestCase.waitForAsync('port response');
    +}
    +
    +function testWindow() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +
    +  // NOTE(nicksantos): This test is having problems in Safari4 on the
    +  // test farm, but no one can reproduce them locally. Just turn it off.
    +  if (goog.userAgent.product.SAFARI) {
    +    return;
    +  }
    +
    +  withIframe(function() {
    +    var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
    +        window.frames['inner'], '*', timer);
    +    iframeChannel.registerService('pong', function(msg) {
    +      assertEquals('fizzbang', msg);
    +
    +      goog.dispose(iframeChannel);
    +      asyncTestCase.continueTesting();
    +    });
    +    iframeChannel.send('ping', 'fizzbang');
    +    asyncTestCase.waitForAsync('window response');
    +  });
    +}
    +
    +function testWindowCancelled() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  withIframe(function() {
    +    var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
    +        window.frames['inner'], '*', timer);
    +    iframeChannel.cancel();
    +
    +    iframeChannel.registerService('pong', function(msg) {
    +      fail('no messages should be received due to cancellation');
    +      goog.dispose(iframeChannel);
    +      asyncTestCase.continueTesting();
    +    });
    +
    +    iframeChannel.send('ping', 'fizzbang');
    +    asyncTestCase.waitForAsync('window response');
    +
    +    // Leave plenty of time for the connection to be made if the test fails, but
    +    // stop the test before the asyncTestCase timeout is hit.
    +    setTimeout(goog.bind(asyncTestCase.continueTesting, asyncTestCase),
    +               asyncTestCase.stepTimeout / 3);
    +  });
    +}
    +
    +function testWindowWontSendToWrongOrigin() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  withIframe(function() {
    +    var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
    +        window.frames['inner'], 'http://somewhere-else.com', timer);
    +    iframeChannel.registerService('pong', function(msg) {
    +      fail('Should not receive pong from unexpected origin');
    +      iframeChannel.dispose();
    +      asyncTestCase.continueTesting();
    +    });
    +    iframeChannel.send('ping', 'fizzbang');
    +
    +    setTimeout(function() {
    +      iframeChannel.dispose();
    +      asyncTestCase.continueTesting();
    +    }, asyncTestCase.stepTimeout - 500);
    +    asyncTestCase.waitForAsync('window response');
    +  });
    +}
    +
    +function testWindowWontReceiveFromWrongOrigin() {
    +  if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +  withIframe(function() {
    +    var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
    +        window.frames['inner'], '*', timer);
    +    iframeChannel.registerService('pong', function(msg) {
    +      fail('Should not receive pong from unexpected origin');
    +      iframeChannel.dispose();
    +      asyncTestCase.continueTesting();
    +    });
    +    iframeChannel.send('ping', 'fizzbang');
    +
    +    setTimeout(function() {
    +      iframeChannel.dispose();
    +      asyncTestCase.continueTesting();
    +    }, asyncTestCase.stepTimeout - 500);
    +    asyncTestCase.waitForAsync('window response');
    +  }, 'testdata/portchannel_wrong_origin_inner.html');
    +}
    +
    +/**
    + * Assert that two HTML5 MessagePorts are entangled by posting messages from
    + * each to the other.
    + *
    + * @param {!MessagePort} port1
    + * @param {!MessagePort} port2
    + * @param {function()} callback Called when the assertion is finished.
    + */
    +function assertPortsEntangled(port1, port2, callback) {
    +  port1.onmessage = function(e) {
    +    assertEquals('port 2 should send messages to port 1',
    +                 'port2 to port1', e.data);
    +    callback();
    +  };
    +
    +  port2.onmessage = function(e) {
    +    assertEquals('port 1 should send messages to port 2',
    +                 'port1 to port2', e.data);
    +    port2.postMessage('port2 to port1');
    +    asyncTestCase.waitForAsync('port 1 receiving message');
    +  };
    +
    +  port1.postMessage('port1 to port2');
    +  asyncTestCase.waitForAsync('port 2 receiving message');
    +}
    +
    +function withIframe(callback, opt_url) {
    +  var frameDiv = goog.dom.getElement('frame');
    +  goog.dom.removeChildren(frameDiv);
    +  goog.dom.appendChild(frameDiv, goog.dom.createDom('iframe', {
    +    style: 'display: none',
    +    name: 'inner',
    +    id: 'inner',
    +    src: opt_url || 'testdata/portchannel_inner.html'
    +  }));
    +
    +  asyncTestCase.waitForAsync('creating iframe');
    +  // We need to pass control back to the event loop to give the iframe a chance
    +  // to load.
    +  setTimeout(function() {
    +    asyncTestCase.continueTesting();
    +    callback();
    +  }, 0);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portnetwork.js b/mapbox-gl/closure-library/closure/goog/messaging/portnetwork.js
    new file mode 100644
    index 0000000000..758d699298
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portnetwork.js
    @@ -0,0 +1,78 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for classes that connect a collection of HTML5
    + * message-passing entities ({@link MessagePort}s, {@link Worker}s, and
    + * {@link Window}s) and allow them to seamlessly communicate with one another.
    + *
    + * Conceptually, a PortNetwork is a collection of JS contexts, such as pages (in
    + * or outside of iframes) or web workers. Each context has a unique name, and
    + * each one can communicate with any of the others in the same network. This
    + * communication takes place through a {@link goog.messaging.PortChannel} that
    + * is retrieved via {#link goog.messaging.PortNetwork#dial}.
    + *
    + * One context (usually the main page) has a
    + * {@link goog.messaging.PortOperator}, which is in charge of connecting each
    + * context to each other context. All other contexts have
    + * {@link goog.messaging.PortCaller}s which connect to the operator.
    + *
    + */
    +
    +goog.provide('goog.messaging.PortNetwork');
    +
    +
    +
    +/**
    + * @interface
    + */
    +goog.messaging.PortNetwork = function() {};
    +
    +
    +/**
    + * Returns a message channel that communicates with the named context. If no
    + * such port exists, an error will either be thrown immediately or after a round
    + * trip with the operator, depending on whether this pool is the operator or a
    + * caller.
    + *
    + * If context A calls dial('B') and context B calls dial('A'), the two
    + * ports returned will be connected to one another.
    + *
    + * @param {string} name The name of the context to get.
    + * @return {goog.messaging.MessageChannel} The channel communicating with the
    + *     given context. This is either a {@link goog.messaging.PortChannel} or a
    + *     decorator around a PortChannel, so it's safe to send {@link MessagePorts}
    + *     across it. This will be disposed along with the PortNetwork.
    + */
    +goog.messaging.PortNetwork.prototype.dial = function(name) {};
    +
    +
    +/**
    + * The name of the service exported by the operator for creating a connection
    + * between two callers.
    + *
    + * @type {string}
    + * @const
    + */
    +goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE = 'requestConnection';
    +
    +
    +/**
    + * The name of the service exported by the callers for adding a connection to
    + * another context.
    + *
    + * @type {string}
    + * @const
    + */
    +goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE = 'grantConnection';
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portnetwork_test.html b/mapbox-gl/closure-library/closure/goog/messaging/portnetwork_test.html
    new file mode 100644
    index 0000000000..5945259005
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portnetwork_test.html
    @@ -0,0 +1,74 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<title>
    +  Closure Unit Tests - goog.messaging.PortNetwork
    +</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.messaging.PortChannel');
    +goog.require('goog.messaging.PortOperator');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.messaging.MockMessageEvent');
    +goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<iframe style="display: none;" name="inner" id="inner"
    +        src="testdata/portnetwork_inner.html"></iframe>
    +<script>
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +// Use a relatively long timeout because workers can take a while to start up.
    +asyncTestCase.stepTimeout = 5 * 1000;
    +
    +var timer;
    +
    +function setUp() {
    +  timer = new goog.Timer(50);
    +}
    +
    +function tearDown() {
    +  goog.dispose(timer);
    +}
    +
    +function testRouteMessageThroughWorkers() {
    +  if (!('MessageChannel' in goog.global)) {
    +    return;
    +  }
    +
    +  var master = new goog.messaging.PortOperator('main');
    +  master.addPort('worker1', new goog.messaging.PortChannel(
    +      new Worker('testdata/portnetwork_worker1.js')));
    +  master.addPort('worker2', new goog.messaging.PortChannel(
    +      new Worker('testdata/portnetwork_worker2.js')));
    +  master.addPort(
    +      'frame', goog.messaging.PortChannel.forEmbeddedWindow(
    +          window.frames['inner'], '*', timer));
    +
    +  master.dial('worker1').registerService('result', function(msg) {
    +    assertArrayEquals(['main', 'worker2', 'frame', 'worker1'], msg);
    +    master.dispose();
    +    asyncTestCase.continueTesting();
    +  }, true);
    +
    +  master.dial('worker2').send('sendToFrame', ['main']);
    +
    +  asyncTestCase.waitForAsync('routing messages');
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portoperator.js b/mapbox-gl/closure-library/closure/goog/messaging/portoperator.js
    new file mode 100644
    index 0000000000..90e02aeba9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portoperator.js
    @@ -0,0 +1,198 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The central node of a {@link goog.messaging.PortNetwork}. The
    + * operator is responsible for providing the two-way communication channels (via
    + * {@link MessageChannel}s) between each pair of nodes in the network that need
    + * to communicate with one another. Each network should have one and only one
    + * operator.
    + *
    + */
    +
    +goog.provide('goog.messaging.PortOperator');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.log');
    +goog.require('goog.messaging.PortChannel');
    +goog.require('goog.messaging.PortNetwork'); // interface
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * The central node of a PortNetwork.
    + *
    + * @param {string} name The name of this node.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @implements {goog.messaging.PortNetwork}
    + * @final
    + */
    +goog.messaging.PortOperator = function(name) {
    +  goog.messaging.PortOperator.base(this, 'constructor');
    +
    +  /**
    +   * The collection of channels for communicating with other contexts in the
    +   * network. These are the channels that are returned to the user, as opposed
    +   * to the channels used for internal network communication. This is lazily
    +   * populated as the user requests communication with other contexts, or other
    +   * contexts request communication with the operator.
    +   *
    +   * @type {!Object<!goog.messaging.PortChannel>}
    +   * @private
    +   */
    +  this.connections_ = {};
    +
    +  /**
    +   * The collection of channels for internal network communication with other
    +   * contexts. This is not lazily populated, and always contains entries for
    +   * each member of the network.
    +   *
    +   * @type {!Object<!goog.messaging.MessageChannel>}
    +   * @private
    +   */
    +  this.switchboard_ = {};
    +
    +  /**
    +   * The name of the operator context.
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = name;
    +};
    +goog.inherits(goog.messaging.PortOperator, goog.Disposable);
    +
    +
    +/**
    + * The logger for PortOperator.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.messaging.PortOperator.prototype.logger_ =
    +    goog.log.getLogger('goog.messaging.PortOperator');
    +
    +
    +/** @override */
    +goog.messaging.PortOperator.prototype.dial = function(name) {
    +  this.connectSelfToPort_(name);
    +  return this.connections_[name];
    +};
    +
    +
    +/**
    + * Adds a caller to the network with the given name. This port should have no
    + * services registered on it. It will be disposed along with the PortOperator.
    + *
    + * @param {string} name The name of the port to add.
    + * @param {!goog.messaging.MessageChannel} port The port to add. Must be either
    + *     a {@link goog.messaging.PortChannel} or a decorator wrapping a
    + *     PortChannel; in particular, it must be able to send and receive
    + *     {@link MessagePort}s.
    + */
    +goog.messaging.PortOperator.prototype.addPort = function(name, port) {
    +  this.switchboard_[name] = port;
    +  port.registerService(goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE,
    +                       goog.bind(this.requestConnection_, this, name));
    +};
    +
    +
    +/**
    + * Connects two contexts by creating a {@link MessageChannel} and sending one
    + * end to one context and the other end to the other. Called when we receive a
    + * request from a caller to connect it to another context (including potentially
    + * the operator).
    + *
    + * @param {string} sourceName The name of the context requesting the connection.
    + * @param {!Object|string} message The name of the context to which
    + *     the connection is requested.
    + * @private
    + */
    +goog.messaging.PortOperator.prototype.requestConnection_ = function(
    +    sourceName, message) {
    +  var requestedName = /** @type {string} */ (message);
    +  if (requestedName == this.name_) {
    +    this.connectSelfToPort_(sourceName);
    +    return;
    +  }
    +
    +  var sourceChannel = this.switchboard_[sourceName];
    +  var requestedChannel = this.switchboard_[requestedName];
    +
    +  goog.asserts.assert(goog.isDefAndNotNull(sourceChannel));
    +  if (!requestedChannel) {
    +    var err = 'Port "' + sourceName + '" requested a connection to port "' +
    +        requestedName + '", which doesn\'t exist';
    +    goog.log.warning(this.logger_, err);
    +    sourceChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE,
    +                       {'success': false, 'message': err});
    +    return;
    +  }
    +
    +  var messageChannel = new MessageChannel();
    +  sourceChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    'success': true,
    +    'name': requestedName,
    +    'port': messageChannel.port1
    +  });
    +  requestedChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    'success': true,
    +    'name': sourceName,
    +    'port': messageChannel.port2
    +  });
    +};
    +
    +
    +/**
    + * Connects together the operator and a caller by creating a
    + * {@link MessageChannel} and sending one end to the remote context.
    + *
    + * @param {string} contextName The name of the context to which to connect the
    + *     operator.
    + * @private
    + */
    +goog.messaging.PortOperator.prototype.connectSelfToPort_ = function(
    +    contextName) {
    +  if (contextName in this.connections_) {
    +    // We've already established a connection with this port.
    +    return;
    +  }
    +
    +  var contextChannel = this.switchboard_[contextName];
    +  if (!contextChannel) {
    +    throw Error('Port "' + contextName + '" doesn\'t exist');
    +  }
    +
    +  var messageChannel = new MessageChannel();
    +  contextChannel.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    'success': true,
    +    'name': this.name_,
    +    'port': messageChannel.port1
    +  });
    +  messageChannel.port2.start();
    +  this.connections_[contextName] =
    +      new goog.messaging.PortChannel(messageChannel.port2);
    +};
    +
    +
    +/** @override */
    +goog.messaging.PortOperator.prototype.disposeInternal = function() {
    +  goog.object.forEach(this.switchboard_, goog.dispose);
    +  goog.object.forEach(this.connections_, goog.dispose);
    +  delete this.switchboard_;
    +  delete this.connections_;
    +  goog.messaging.PortOperator.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.html b/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.html
    new file mode 100644
    index 0000000000..9b0b6ae03f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging.PortOperator
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.messaging.PortOperatorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.js b/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.js
    new file mode 100644
    index 0000000000..05656fe7c9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/portoperator_test.js
    @@ -0,0 +1,107 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.PortOperatorTest');
    +goog.setTestOnly('goog.messaging.PortOperatorTest');
    +
    +goog.require('goog.messaging.PortNetwork');
    +goog.require('goog.messaging.PortOperator');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +goog.require('goog.testing.messaging.MockMessagePort');
    +
    +var stubs;
    +
    +var mockControl;
    +var mockChannel1;
    +var mockChannel2;
    +var operator;
    +
    +function setUpPage() {
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  var index = 0;
    +  stubs.set(goog.global, 'MessageChannel', function() {
    +    this.port1 = makeMockPort(index, 1);
    +    this.port2 = makeMockPort(index, 2);
    +    index += 1;
    +  });
    +
    +  mockChannel1 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  mockChannel2 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  operator = new goog.messaging.PortOperator('operator');
    +  operator.addPort('1', mockChannel1);
    +  operator.addPort('2', mockChannel2);
    +}
    +
    +function tearDown() {
    +  goog.dispose(operator);
    +  mockControl.$verifyAll();
    +  stubs.reset();
    +}
    +
    +function makeMockPort(index, port) {
    +  return new goog.testing.messaging.MockMessagePort(
    +      {index: index, port: port}, mockControl);
    +}
    +
    +function testConnectSelfToPortViaRequestConnection() {
    +  mockChannel1.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    success: true, name: 'operator', port: makeMockPort(0, 1)
    +  });
    +  mockControl.$replayAll();
    +  mockChannel1.receive(
    +      goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, 'operator');
    +  var port = operator.dial('1').port_;
    +  assertObjectEquals({index: 0, port: 2}, port.id);
    +  assertEquals(true, port.started);
    +}
    +
    +function testConnectSelfToPortViaGetPort() {
    +  mockChannel1.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    success: true, name: 'operator', port: makeMockPort(0, 1)
    +  });
    +  mockControl.$replayAll();
    +  var port = operator.dial('1').port_;
    +  assertObjectEquals({index: 0, port: 2}, port.id);
    +  assertEquals(true, port.started);
    +}
    +
    +function testConnectTwoCallers() {
    +  mockChannel1.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    success: true, name: '2', port: makeMockPort(0, 1)
    +  });
    +  mockChannel2.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    success: true, name: '1', port: makeMockPort(0, 2)
    +  });
    +  mockControl.$replayAll();
    +  mockChannel1.receive(
    +      goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, '2');
    +}
    +
    +function testConnectCallerToNonexistentCaller() {
    +  mockChannel1.send(goog.messaging.PortNetwork.GRANT_CONNECTION_SERVICE, {
    +    success: false,
    +    message: 'Port "1" requested a connection to port "no", which doesn\'t ' +
    +        'exist'
    +  });
    +  mockControl.$replayAll();
    +  mockChannel1.receive(
    +      goog.messaging.PortNetwork.REQUEST_CONNECTION_SERVICE, 'no');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel.js b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel.js
    new file mode 100644
    index 0000000000..678205ab71
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel.js
    @@ -0,0 +1,234 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of goog.messaging.RespondingChannel, which wraps a
    + * MessageChannel and allows the user to get the response from the services.
    + *
    + */
    +
    +
    +goog.provide('goog.messaging.RespondingChannel');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.log');
    +goog.require('goog.messaging.MultiChannel');
    +
    +
    +
    +/**
    + * Creates a new RespondingChannel wrapping a single MessageChannel.
    + * @param {goog.messaging.MessageChannel} messageChannel The messageChannel to
    + *     to wrap and allow for responses. This channel must not have any existing
    + *     services registered. All service registration must be done through the
    + *     {@link RespondingChannel#registerService} api instead. The other end of
    + *     channel must also be a RespondingChannel.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.messaging.RespondingChannel = function(messageChannel) {
    +  goog.messaging.RespondingChannel.base(this, 'constructor');
    +
    +  /**
    +   * The message channel wrapped in a MultiChannel so we can send private and
    +   * public messages on it.
    +   * @type {goog.messaging.MultiChannel}
    +   * @private
    +   */
    +  this.messageChannel_ = new goog.messaging.MultiChannel(messageChannel);
    +
    +  /**
    +   * Map of invocation signatures to function callbacks. These are used to keep
    +   * track of the asyncronous service invocations so the result of a service
    +   * call can be passed back to a callback in the calling frame.
    +   * @type {Object<number, function(Object)>}
    +   * @private
    +   */
    +  this.sigCallbackMap_ = {};
    +
    +  /**
    +   * The virtual channel to send private messages on.
    +   * @type {goog.messaging.MultiChannel.VirtualChannel}
    +   * @private
    +   */
    +  this.privateChannel_ = this.messageChannel_.createVirtualChannel(
    +      goog.messaging.RespondingChannel.PRIVATE_CHANNEL_);
    +
    +  /**
    +   * The virtual channel to send public messages on.
    +   * @type {goog.messaging.MultiChannel.VirtualChannel}
    +   * @private
    +   */
    +  this.publicChannel_ = this.messageChannel_.createVirtualChannel(
    +      goog.messaging.RespondingChannel.PUBLIC_CHANNEL_);
    +
    +  this.privateChannel_.registerService(
    +      goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
    +      goog.bind(this.callbackServiceHandler_, this),
    +      true);
    +};
    +goog.inherits(goog.messaging.RespondingChannel, goog.Disposable);
    +
    +
    +/**
    + * The name of the method invocation callback service (used internally).
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.RespondingChannel.CALLBACK_SERVICE_ = 'mics';
    +
    +
    +/**
    + * The name of the channel to send private control messages on.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.RespondingChannel.PRIVATE_CHANNEL_ = 'private';
    +
    +
    +/**
    + * The name of the channel to send public messages on.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.messaging.RespondingChannel.PUBLIC_CHANNEL_ = 'public';
    +
    +
    +/**
    + * The next signature index to save the callback against.
    + * @type {number}
    + * @private
    + */
    +goog.messaging.RespondingChannel.prototype.nextSignatureIndex_ = 0;
    +
    +
    +/**
    + * Logger object for goog.messaging.RespondingChannel.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.messaging.RespondingChannel.prototype.logger_ =
    +    goog.log.getLogger('goog.messaging.RespondingChannel');
    +
    +
    +/**
    + * Gets a random number to use for method invocation results.
    + * @return {number} A unique random signature.
    + * @private
    + */
    +goog.messaging.RespondingChannel.prototype.getNextSignature_ = function() {
    +  return this.nextSignatureIndex_++;
    +};
    +
    +
    +/** @override */
    +goog.messaging.RespondingChannel.prototype.disposeInternal = function() {
    +  goog.dispose(this.messageChannel_);
    +  delete this.messageChannel_;
    +  // Note: this.publicChannel_ and this.privateChannel_ get disposed by
    +  //     this.messageChannel_
    +  delete this.publicChannel_;
    +  delete this.privateChannel_;
    +};
    +
    +
    +/**
    + * Sends a message over the channel.
    + * @param {string} serviceName The name of the service this message should be
    + *     delivered to.
    + * @param {string|!Object} payload The value of the message. If this is an
    + *     Object, it is serialized to a string before sending if necessary.
    + * @param {function(?Object)} callback The callback invoked with
    + *     the result of the service call.
    + */
    +goog.messaging.RespondingChannel.prototype.send = function(
    +    serviceName,
    +    payload,
    +    callback) {
    +
    +  var signature = this.getNextSignature_();
    +  this.sigCallbackMap_[signature] = callback;
    +
    +  var message = {};
    +  message['signature'] = signature;
    +  message['data'] = payload;
    +
    +  this.publicChannel_.send(serviceName, message);
    +};
    +
    +
    +/**
    + * Receives the results of the peer's service results.
    + * @param {!Object|string} message The results from the remote service
    + *     invocation.
    + * @private
    + */
    +goog.messaging.RespondingChannel.prototype.callbackServiceHandler_ = function(
    +    message) {
    +
    +  var signature = message['signature'];
    +  var result = message['data'];
    +
    +  if (signature in this.sigCallbackMap_) {
    +    var callback = /** @type {function(Object)} */ (this.sigCallbackMap_[
    +        signature]);
    +    callback(result);
    +    delete this.sigCallbackMap_[signature];
    +  } else {
    +    goog.log.warning(this.logger_, 'Received signature is invalid');
    +  }
    +};
    +
    +
    +/**
    + * Registers a service to be called when a message is received.
    + * @param {string} serviceName The name of the service.
    + * @param {function(!Object)} callback The callback to process the
    + *     incoming messages. Passed the payload.
    + */
    +goog.messaging.RespondingChannel.prototype.registerService = function(
    +    serviceName, callback) {
    +  this.publicChannel_.registerService(
    +      serviceName,
    +      goog.bind(this.callbackProxy_, this, callback),
    +      true);
    +};
    +
    +
    +/**
    + * A intermediary proxy for service callbacks to be invoked and return their
    + * their results to the remote caller's callback.
    + * @param {function((string|!Object))} callback The callback to process the
    + *     incoming messages. Passed the payload.
    + * @param {!Object|string} message The message containing the signature and
    + *     the data to invoke the service callback with.
    + * @private
    + */
    +goog.messaging.RespondingChannel.prototype.callbackProxy_ = function(
    +    callback, message) {
    +
    +  var resultMessage = {};
    +  resultMessage['data'] = callback(message['data']);
    +  resultMessage['signature'] = message['signature'];
    +  // The callback invoked above may have disposed the channel so check if it
    +  // exists.
    +  if (this.privateChannel_) {
    +    this.privateChannel_.send(
    +        goog.messaging.RespondingChannel.CALLBACK_SERVICE_,
    +        resultMessage);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.html b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.html
    new file mode 100644
    index 0000000000..d04c081bb7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.messaging
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.messaging.RespondingChannelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.js b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.js
    new file mode 100644
    index 0000000000..e271f3b139
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/respondingchannel_test.js
    @@ -0,0 +1,152 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.messaging.RespondingChannelTest');
    +goog.setTestOnly('goog.messaging.RespondingChannelTest');
    +
    +goog.require('goog.messaging.RespondingChannel');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +var CH1_REQUEST = {'request': 'quux1'};
    +var CH2_REQUEST = {'request': 'quux2'};
    +var CH1_RESPONSE = {'response': 'baz1'};
    +var CH2_RESPONSE = {'response': 'baz2'};
    +var SERVICE_NAME = 'serviceName';
    +
    +var mockControl;
    +var ch1;
    +var ch2;
    +var respondingCh1;
    +var respondingCh2;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +
    +  ch1 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +  ch2 = new goog.testing.messaging.MockMessageChannel(mockControl);
    +
    +  respondingCh1 = new goog.messaging.RespondingChannel(ch1);
    +  respondingCh2 = new goog.messaging.RespondingChannel(ch2);
    +}
    +
    +function tearDown() {
    +  respondingCh1.dispose();
    +  respondingCh2.dispose();
    +  mockControl.$verifyAll();
    +}
    +
    +function testSendWithSignature() {
    +  // 1 to 2 and back.
    +  var message1Ch1Request = {'data': CH1_REQUEST,
    +    'signature': 0};
    +  var message1Ch2Response = {'data': CH2_RESPONSE,
    +    'signature': 0};
    +  var message2Ch1Request = {'data': CH1_REQUEST,
    +    'signature': 1};
    +  var message2Ch2Response = {'data': CH2_RESPONSE,
    +    'signature': 1};
    +  // 2 to 1 and back.
    +  var message3Ch2Request = {'data': CH2_REQUEST,
    +    'signature': 0};
    +  var message3Ch1Response = {'data': CH1_RESPONSE,
    +    'signature': 0};
    +  var message4Ch2Request = {'data': CH2_REQUEST,
    +    'signature': 1};
    +  var message4Ch1Response = {'data': CH1_RESPONSE,
    +    'signature': 1};
    +
    +  // 1 to 2 and back.
    +  ch1.send(
    +      'public:' + SERVICE_NAME,
    +      message1Ch1Request);
    +  ch2.send(
    +      'private:mics',
    +      message1Ch2Response);
    +  ch1.send(
    +      'public:' + SERVICE_NAME,
    +      message2Ch1Request);
    +  ch2.send(
    +      'private:mics',
    +      message2Ch2Response);
    +
    +  // 2 to 1 and back.
    +  ch2.send(
    +      'public:' + SERVICE_NAME,
    +      message3Ch2Request);
    +  ch1.send(
    +      'private:mics',
    +      message3Ch1Response);
    +  ch2.send(
    +      'public:' + SERVICE_NAME,
    +      message4Ch2Request);
    +  ch1.send(
    +      'private:mics',
    +      message4Ch1Response);
    +
    +  mockControl.$replayAll();
    +
    +  var hasInvokedCh1 = false;
    +  var hasInvokedCh2 = false;
    +  var hasReturnedFromCh1 = false;
    +  var hasReturnedFromCh2 = false;
    +
    +  var serviceCallback1 = function(message) {
    +    hasInvokedCh1 = true;
    +    assertObjectEquals(CH2_REQUEST, message);
    +    return CH1_RESPONSE;
    +  };
    +
    +  var serviceCallback2 = function(message) {
    +    hasInvokedCh2 = true;
    +    assertObjectEquals(CH1_REQUEST, message);
    +    return CH2_RESPONSE;
    +  };
    +
    +  var invocationCallback1 = function(message) {
    +    hasReturnedFromCh2 = true;
    +    assertObjectEquals(CH2_RESPONSE, message);
    +  };
    +
    +  var invocationCallback2 = function(message) {
    +    hasReturnedFromCh1 = true;
    +    assertObjectEquals(CH1_RESPONSE, message);
    +  };
    +
    +  respondingCh1.registerService(SERVICE_NAME, serviceCallback1);
    +  respondingCh2.registerService(SERVICE_NAME, serviceCallback2);
    +
    +  respondingCh1.send(SERVICE_NAME, CH1_REQUEST, invocationCallback1);
    +  ch2.receive('public:' + SERVICE_NAME, message1Ch1Request);
    +  ch1.receive('private:mics', message1Ch2Response);
    +
    +  respondingCh1.send(SERVICE_NAME, CH1_REQUEST, invocationCallback1);
    +  ch2.receive('public:' + SERVICE_NAME, message2Ch1Request);
    +  ch1.receive('private:mics', message2Ch2Response);
    +
    +  respondingCh2.send(SERVICE_NAME, CH2_REQUEST, invocationCallback2);
    +  ch1.receive('public:' + SERVICE_NAME, message3Ch2Request);
    +  ch2.receive('private:mics', message3Ch1Response);
    +
    +  respondingCh2.send(SERVICE_NAME, CH2_REQUEST, invocationCallback2);
    +  ch1.receive('public:' + SERVICE_NAME, message4Ch2Request);
    +  ch2.receive('private:mics', message4Ch1Response);
    +
    +  assertTrue(
    +      hasInvokedCh1 &&
    +      hasInvokedCh2 &&
    +      hasReturnedFromCh1 &&
    +      hasReturnedFromCh2);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_inner.html b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_inner.html
    new file mode 100644
    index 0000000000..6c9a09e489
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_inner.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<title>PortChannel test inner document</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.messaging.PortChannel');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var channel = goog.messaging.PortChannel.forGlobalWindow('*');
    +channel.registerService('ping', function(msg) {
    +  channel.send('pong', msg);
    +});
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_worker.js b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_worker.js
    new file mode 100644
    index 0000000000..cba4b884be
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_worker.js
    @@ -0,0 +1,37 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +// Use of this source code is governed by the Apache License, Version 2.0.
    +// See the COPYING file for details.
    +
    +/**
    + * @fileoverview A web worker for integration testing the PortChannel class.
    + *
    + * @nocompile
    + */
    +
    +self.CLOSURE_BASE_PATH = '../../';
    +importScripts('../../bootstrap/webworkers.js');
    +importScripts('../../base.js');
    +
    +// The provide is necessary to stop the jscompiler from thinking this is an
    +// entry point and adding it into the manifest incorrectly.
    +goog.provide('goog.messaging.testdata.portchannel_worker');
    +goog.require('goog.messaging.PortChannel');
    +
    +function registerPing(channel) {
    +  channel.registerService('ping', function(msg) {
    +    channel.send('pong', msg);
    +  }, true);
    +}
    +
    +function startListening() {
    +  var channel = new goog.messaging.PortChannel(self);
    +  registerPing(channel);
    +
    +  channel.registerService('addPort', function(port) {
    +    port.start();
    +    registerPing(new goog.messaging.PortChannel(port));
    +  }, true);
    +}
    +
    +startListening();
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_wrong_origin_inner.html b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_wrong_origin_inner.html
    new file mode 100644
    index 0000000000..344d8c65fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portchannel_wrong_origin_inner.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<title>PortChannel test inner document</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.messaging.PortChannel');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var channel = goog.messaging.PortChannel.forGlobalWindow(
    +    'http://somewhere-else.com');
    +channel.registerService('ping', function(msg) {
    +  channel.send('pong', msg);
    +});
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_inner.html b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_inner.html
    new file mode 100644
    index 0000000000..c24407e40a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_inner.html
    @@ -0,0 +1,34 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<title>
    +  Closure Unit Tests - goog.messaging.PortNetwork iframe page
    +</title>
    +<script src="../../base.js"></script>
    +<script>
    +goog.require('goog.messaging.PortCaller');
    +goog.require('goog.messaging.PortChannel');
    +</script>
    +</head>
    +<body>
    +<script>
    +var caller = new goog.messaging.PortCaller(
    +    goog.messaging.PortChannel.forGlobalWindow('*'));
    +
    +caller.dial('worker2').registerService('sendToWorker1', function(msg) {
    +  msg.push('frame');
    +  caller.dial('worker1').send('sendToMain', msg);
    +}, true);
    +
    +</script>
    +</body>
    +</html>
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js
    new file mode 100644
    index 0000000000..e7248a51b7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker1.js
    @@ -0,0 +1,32 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +// Use of this source code is governed by the Apache License, Version 2.0.
    +// See the COPYING file for details.
    +
    +/**
    + * @fileoverview A web worker for integration testing the PortPool class.
    + *
    + * @nocompile
    + */
    +
    +self.CLOSURE_BASE_PATH = '../../';
    +importScripts('../../bootstrap/webworkers.js');
    +importScripts('../../base.js');
    +
    +// The provide is necessary to stop the jscompiler from thinking this is an
    +// entry point and adding it into the manifest incorrectly.
    +goog.provide('goog.messaging.testdata.portnetwork_worker1');
    +goog.require('goog.messaging.PortCaller');
    +goog.require('goog.messaging.PortChannel');
    +
    +function startListening() {
    +  var caller = new goog.messaging.PortCaller(
    +      new goog.messaging.PortChannel(self));
    +
    +  caller.dial('frame').registerService('sendToMain', function(msg) {
    +    msg.push('worker1');
    +    caller.dial('main').send('result', msg);
    +  }, true);
    +}
    +
    +startListening();
    diff --git a/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js
    new file mode 100644
    index 0000000000..97adc93253
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/messaging/testdata/portnetwork_worker2.js
    @@ -0,0 +1,32 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +// Use of this source code is governed by the Apache License, Version 2.0.
    +// See the COPYING file for details.
    +
    +/**
    + * @fileoverview A web worker for integration testing the PortPool class.
    + *
    + * @nocompile
    + */
    +
    +self.CLOSURE_BASE_PATH = '../../';
    +importScripts('../../bootstrap/webworkers.js');
    +importScripts('../../base.js');
    +
    +// The provide is necessary to stop the jscompiler from thinking this is an
    +// entry point and adding it into the manifest incorrectly.
    +goog.provide('goog.messaging.testdata.portnetwork_worker2');
    +goog.require('goog.messaging.PortCaller');
    +goog.require('goog.messaging.PortChannel');
    +
    +function startListening() {
    +  var caller = new goog.messaging.PortCaller(
    +      new goog.messaging.PortChannel(self));
    +
    +  caller.dial('main').registerService('sendToFrame', function(msg) {
    +    msg.push('worker2');
    +    caller.dial('frame').send('sendToWorker1', msg);
    +  }, true);
    +}
    +
    +startListening();
    diff --git a/mapbox-gl/closure-library/closure/goog/module/abstractmoduleloader.js b/mapbox-gl/closure-library/closure/goog/module/abstractmoduleloader.js
    new file mode 100644
    index 0000000000..e280af06ea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/abstractmoduleloader.js
    @@ -0,0 +1,58 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for module loading.
    + *
    + */
    +
    +goog.provide('goog.module.AbstractModuleLoader');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +
    +
    +/**
    + * An interface that loads JavaScript modules.
    + * @interface
    + */
    +goog.module.AbstractModuleLoader = function() {};
    +
    +
    +/**
    + * Loads a list of JavaScript modules.
    + *
    + * @param {Array<string>} ids The module ids in dependency order.
    + * @param {Object} moduleInfoMap A mapping from module id to ModuleInfo object.
    + * @param {function()?=} opt_successFn The callback if module loading is a
    + *     success.
    + * @param {function(?number)?=} opt_errorFn The callback if module loading is an
    + *     error.
    + * @param {function()?=} opt_timeoutFn The callback if module loading times out.
    + * @param {boolean=} opt_forceReload Whether to bypass cache while loading the
    + *     module.
    + */
    +goog.module.AbstractModuleLoader.prototype.loadModules = function(
    +    ids, moduleInfoMap, opt_successFn, opt_errorFn, opt_timeoutFn,
    +    opt_forceReload) {};
    +
    +
    +/**
    + * Pre-fetches a JavaScript module.
    + *
    + * @param {string} id The module id.
    + * @param {!goog.module.ModuleInfo} moduleInfo The module info.
    + */
    +goog.module.AbstractModuleLoader.prototype.prefetchModule = function(
    +    id, moduleInfo) {};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/basemodule.js b/mapbox-gl/closure-library/closure/goog/module/basemodule.js
    new file mode 100644
    index 0000000000..f0d924a4b0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/basemodule.js
    @@ -0,0 +1,47 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines the base class for a module. This is used to allow the
    + * code to be modularized, giving the benefits of lazy loading and loading on
    + * demand.
    + *
    + */
    +
    +goog.provide('goog.module.BaseModule');
    +
    +goog.require('goog.Disposable');
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +
    +
    +
    +/**
    + * A basic module object that represents a module of Javascript code that can
    + * be dynamically loaded.
    + *
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.module.BaseModule = function() {
    +  goog.Disposable.call(this);
    +};
    +goog.inherits(goog.module.BaseModule, goog.Disposable);
    +
    +
    +/**
    + * Performs any load-time initialization that the module requires.
    + * @param {Object} context The module context.
    + */
    +goog.module.BaseModule.prototype.initialize = function(context) {};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/loader.js b/mapbox-gl/closure-library/closure/goog/module/loader.js
    new file mode 100644
    index 0000000000..decf39ac6c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/loader.js
    @@ -0,0 +1,345 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + *
    + * @fileoverview This class supports the dynamic loading of compiled
    + * javascript modules at runtime, as descibed in the designdoc.
    + *
    + *   <http://go/js_modules_design>
    + *
    + */
    +
    +goog.provide('goog.module.Loader');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * The dynamic loading functionality is defined as a class. The class
    + * will be used as singleton. There is, however, a two step
    + * initialization procedure because parameters need to be passed to
    + * the goog.module.Loader instance.
    + *
    + * @constructor
    + * @final
    + */
    +goog.module.Loader = function() {
    +  /**
    +   * Map of module name/array of {symbol name, callback} pairs that are pending
    +   * to be loaded.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.pending_ = {};
    +
    +  /**
    +   * Provides associative access to each module and the symbols of each module
    +   * that have aready been loaded (one lookup for the module, another lookup
    +   * on the module for the symbol).
    +   * @type {Object}
    +   * @private
    +   */
    +  this.modules_ = {};
    +
    +  /**
    +   * Map of module name to module url. Used to avoid fetching the same URL
    +   * twice by keeping track of in-flight URLs.
    +   * Note: this allows two modules to be bundled into the same file.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.pendingModuleUrls_ = {};
    +
    +  /**
    +   * The base url to load modules from. This property will be set in init().
    +   * @type {?string}
    +   * @private
    +   */
    +  this.urlBase_ = null;
    +
    +  /**
    +   * Array of modules that have been requested before init() was called.
    +   * If require() is called before init() was called, the required
    +   * modules can obviously not yet be loaded, because their URL is
    +   * unknown. The modules that are requested before init() are
    +   * therefore stored in this array, and they are loaded at init()
    +   * time.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.pendingBeforeInit_ = [];
    +};
    +goog.addSingletonGetter(goog.module.Loader);
    +
    +
    +/**
    + * Wrapper of goog.module.Loader.require() for use in modules.
    + * See method goog.module.Loader.require() for
    + * explanation of params.
    + *
    + * @param {string} module The name of the module. Usually, the value
    + *     is defined as a constant whose name starts with MOD_.
    + * @param {number|string} symbol The ID of the symbol. Usually, the value is
    + *     defined as a constant whose name starts with SYM_.
    + * @param {Function} callback This function will be called with the
    + *     resolved symbol as the argument once the module is loaded.
    + */
    +goog.module.Loader.require = function(module, symbol, callback) {
    +  goog.module.Loader.getInstance().require(module, symbol, callback);
    +};
    +
    +
    +/**
    + * Wrapper of goog.module.Loader.provide() for use in modules
    + * See method goog.module.Loader.provide() for explanation of params.
    + *
    + * @param {string} module The name of the module. Cf. parameter module
    + *     of method require().
    + * @param {number|string=} opt_symbol The symbol being defined, or nothing
    + *     when all symbols of the module are defined. Cf. parameter symbol of
    + *     method require().
    + * @param {Object=} opt_object The object bound to the symbol, or nothing when
    + *     all symbols of the module are defined.
    + */
    +goog.module.Loader.provide = function(module, opt_symbol, opt_object) {
    +  goog.module.Loader.getInstance().provide(
    +      module, opt_symbol, opt_object);
    +};
    +
    +
    +/**
    + * Wrapper of init() so that we only need to export this single
    + * identifier instead of three. See method goog.module.Loader.init() for
    + * explanation of param.
    + *
    + * @param {string} urlBase The URL of the base library.
    + * @param {Function=} opt_urlFunction Function that creates the URL for the
    + *     module file. It will be passed the base URL for module files and the
    + *     module name and should return the fully-formed URL to the module file to
    + *     load.
    + */
    +goog.module.Loader.init = function(urlBase, opt_urlFunction) {
    +  goog.module.Loader.getInstance().init(urlBase, opt_urlFunction);
    +};
    +
    +
    +/**
    + * Produces a function that delegates all its arguments to a
    + * dynamically loaded function. This is used to export dynamically
    + * loaded functions.
    + *
    + * @param {string} module The module to load from.
    + * @param {number|string} symbol The ID of the symbol to load from the module.
    + *     This symbol must resolve to a function.
    + * @return {!Function} A function that forwards all its arguments to
    + *     the dynamically loaded function specified by module and symbol.
    + */
    +goog.module.Loader.loaderCall = function(module, symbol) {
    +  return function() {
    +    var args = arguments;
    +    goog.module.Loader.require(module, symbol, function(f) {
    +      f.apply(null, args);
    +    });
    +  };
    +};
    +
    +
    +/**
    + * Creates a full URL to the compiled module code given a base URL and a
    + * module name. By default it's urlBase + '_' + module + '.js'.
    + * @param {string} urlBase URL to the module files.
    + * @param {string} module Module name.
    + * @return {string} The full url to the module binary.
    + * @private
    + */
    +goog.module.Loader.prototype.getModuleUrl_ = function(urlBase, module) {
    +  return urlBase + '_' + module + '.js';
    +};
    +
    +
    +/**
    + * The globally exported name of the load callback. Matches the
    + * definition in the js_modular_binary() BUILD rule.
    + * @type {string}
    + */
    +goog.module.Loader.LOAD_CALLBACK = '__gjsload__';
    +
    +
    +/**
    + * Loads the module by evaluating the javascript text in the current
    + * scope. Uncompiled, base identifiers are visible in the global scope;
    + * when compiled they are visible in the closure of the anonymous
    + * namespace. Notice that this cannot be replaced by the global eval,
    + * because the global eval isn't in the scope of the anonymous
    + * namespace function that the jscompiled code lives in.
    + *
    + * @param {string} t_ The javascript text to evaluate. IMPORTANT: The
    + *   name of the identifier is chosen so that it isn't compiled and
    + *   hence cannot shadow compiled identifiers in the surrounding scope.
    + * @private
    + */
    +goog.module.Loader.loaderEval_ = function(t_) {
    +  eval(t_);
    +};
    +
    +
    +/**
    + * Initializes the Loader to be fully functional. Also executes load
    + * requests that were received before initialization. Must be called
    + * exactly once, with the URL of the base library. Module URLs are
    + * derived from the URL of the base library by inserting the module
    + * name, preceded by a period, before the .js prefix of the base URL.
    + *
    + * @param {string} baseUrl The URL of the base library.
    + * @param {Function=} opt_urlFunction Function that creates the URL for the
    + *     module file. It will be passed the base URL for module files and the
    + *     module name and should return the fully-formed URL to the module file to
    + *     load.
    + */
    +goog.module.Loader.prototype.init = function(baseUrl, opt_urlFunction) {
    +  // For the use by the module wrappers, loaderEval_ is exported to
    +  // the page. Note that, despite the name, this is not part of the
    +  // API, so it is here and not in api_app.js. Cf. BUILD. Note this is
    +  // done before the first load requests are sent.
    +  goog.exportSymbol(goog.module.Loader.LOAD_CALLBACK,
    +      goog.module.Loader.loaderEval_);
    +
    +  this.urlBase_ = baseUrl.replace(/\.js$/, '');
    +  if (opt_urlFunction) {
    +    this.getModuleUrl_ = opt_urlFunction;
    +  }
    +
    +  goog.array.forEach(this.pendingBeforeInit_, function(module) {
    +    this.load_(module);
    +  }, this);
    +  goog.array.clear(this.pendingBeforeInit_);
    +};
    +
    +
    +/**
    + * Requests the loading of a symbol from a module. When the module is
    + * loaded, the requested symbol will be passed as argument to the
    + * function callback.
    + *
    + * @param {string} module The name of the module. Usually, the value
    + *     is defined as a constant whose name starts with MOD_.
    + * @param {number|string} symbol The ID of the symbol. Usually, the value is
    + *     defined as a constant whose name starts with SYM_.
    + * @param {Function} callback This function will be called with the
    + *     resolved symbol as the argument once the module is loaded.
    + */
    +goog.module.Loader.prototype.require = function(module, symbol, callback) {
    +  var pending = this.pending_;
    +  var modules = this.modules_;
    +  if (modules[module]) {
    +    // already loaded
    +    callback(modules[module][symbol]);
    +  } else if (pending[module]) {
    +    // loading is pending from another require of the same module
    +    pending[module].push([symbol, callback]);
    +  } else {
    +    // not loaded, and not requested
    +    pending[module] = [[symbol, callback]];  // Yes, really [[ ]].
    +    // Defer loading to initialization if Loader is not yet
    +    // initialized, otherwise load the module.
    +    if (goog.isString(this.urlBase_)) {
    +      this.load_(module);
    +    } else {
    +      this.pendingBeforeInit_.push(module);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Registers a symbol in a loaded module. When called without symbol,
    + * registers the module to be fully loaded and executes all callbacks
    + * from pending require() callbacks for this module.
    + *
    + * @param {string} module The name of the module. Cf. parameter module
    + *     of method require().
    + * @param {number|string=} opt_symbol The symbol being defined, or nothing when
    + *     all symbols of the module are defined. Cf. parameter symbol of method
    + *     require().
    + * @param {Object=} opt_object The object bound to the symbol, or nothing when
    + *     all symbols of the module are defined.
    + */
    +goog.module.Loader.prototype.provide = function(
    +    module, opt_symbol, opt_object) {
    +  var modules = this.modules_;
    +  var pending = this.pending_;
    +  if (!modules[module]) {
    +    modules[module] = {};
    +  }
    +  if (opt_object) {
    +    // When an object is provided, just register it.
    +    modules[module][opt_symbol] = opt_object;
    +  } else if (pending[module]) {
    +    // When no object is provided, and there are pending require()
    +    // callbacks for this module, execute them.
    +    for (var i = 0; i < pending[module].length; ++i) {
    +      var symbol = pending[module][i][0];
    +      var callback = pending[module][i][1];
    +      callback(modules[module][symbol]);
    +    }
    +    delete pending[module];
    +    delete this.pendingModuleUrls_[module];
    +  }
    +};
    +
    +
    +/**
    + * Starts to load a module. Assumes that init() was called.
    + *
    + * @param {string} module The name of the module.
    + * @private
    + */
    +goog.module.Loader.prototype.load_ = function(module) {
    +  // NOTE(user): If the module request happens inside a click handler
    +  // (presumably inside any user event handler, but the onload event
    +  // handler is fine), IE will load the script but not execute
    +  // it. Thus we break out of the current flow of control before we do
    +  // the load. For the record, for IE it would have been enough to
    +  // just defer the assignment to src. Safari doesn't execute the
    +  // script if the assignment to src happens *after* the script
    +  // element is inserted into the DOM.
    +  goog.Timer.callOnce(function() {
    +    // The module might have been registered in the interim (if fetched as part
    +    // of another module fetch because they share the same url)
    +    if (this.modules_[module]) {
    +      return;
    +    }
    +
    +    goog.asserts.assertString(this.urlBase_);
    +    var url = this.getModuleUrl_(this.urlBase_, module);
    +
    +    // Check if specified URL is already in flight
    +    var urlInFlight = goog.object.containsValue(this.pendingModuleUrls_, url);
    +    this.pendingModuleUrls_[module] = url;
    +    if (urlInFlight) {
    +      return;
    +    }
    +
    +    var s = goog.dom.createDom('script',
    +        {'type': 'text/javascript', 'src': url});
    +    document.body.appendChild(s);
    +  }, 0, this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/module.js b/mapbox-gl/closure-library/closure/goog/module/module.js
    new file mode 100644
    index 0000000000..689e0396e0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/module.js
    @@ -0,0 +1,33 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + *
    + * @fileoverview This class supports the dynamic loading of compiled
    + * javascript modules at runtime, as descibed in the designdoc.
    + *
    + *   <http://go/js_modules_design>
    + *
    + */
    +
    +goog.provide('goog.module');
    +
    +// TODO(johnlenz): Here we explicitly initialize the namespace to avoid
    +// problems with the goog.module method in base.js.  Once the goog.module has
    +// landed and compiler updated and released and everyone is on that release
    +// we can remove this file.
    +//
    +// Alternately, we can move everthing out of the goog.module namespace.
    +//
    +goog.module = goog.module || {};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleinfo.js b/mapbox-gl/closure-library/closure/goog/module/moduleinfo.js
    new file mode 100644
    index 0000000000..b35327a2e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleinfo.js
    @@ -0,0 +1,338 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines the goog.module.ModuleInfo class.
    + *
    + */
    +
    +goog.provide('goog.module.ModuleInfo');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.functions');
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +goog.require('goog.module.BaseModule');
    +goog.require('goog.module.ModuleLoadCallback');
    +
    +
    +
    +/**
    + * A ModuleInfo object is used by the ModuleManager to hold information about a
    + * module of js code that may or may not yet be loaded into the environment.
    + *
    + * @param {Array<string>} deps Ids of the modules that must be loaded before
    + *     this one. The ids must be in dependency order (i.e. if the ith module
    + *     depends on the jth module, then i > j).
    + * @param {string} id The module's ID.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.module.ModuleInfo = function(deps, id) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * A list of the ids of the modules that must be loaded before this module.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.deps_ = deps;
    +
    +  /**
    +   * The module's ID.
    +   * @type {string}
    +   * @private
    +   */
    +  this.id_ = id;
    +
    +  /**
    +   * Callbacks to execute once this module is loaded.
    +   * @type {Array<goog.module.ModuleLoadCallback>}
    +   * @private
    +   */
    +  this.onloadCallbacks_ = [];
    +
    +  /**
    +   * Callbacks to execute if the module load errors.
    +   * @type {Array<goog.module.ModuleLoadCallback>}
    +   * @private
    +   */
    +  this.onErrorCallbacks_ = [];
    +
    +  /**
    +   * Early callbacks to execute once this module is loaded. Called after
    +   * module initialization but before regular onload callbacks.
    +   * @type {Array<goog.module.ModuleLoadCallback>}
    +   * @private
    +   */
    +  this.earlyOnloadCallbacks_ = [];
    +};
    +goog.inherits(goog.module.ModuleInfo, goog.Disposable);
    +
    +
    +/**
    + * The uris that can be used to retrieve this module's code.
    + * @type {Array<string>?}
    + * @private
    + */
    +goog.module.ModuleInfo.prototype.uris_ = null;
    +
    +
    +/**
    + * The constructor to use to instantiate the module object after the module
    + * code is loaded. This must be either goog.module.BaseModule or a subclass of
    + * it.
    + * @type {Function}
    + * @private
    + */
    +goog.module.ModuleInfo.prototype.moduleConstructor_ = goog.module.BaseModule;
    +
    +
    +/**
    + * The module object. This will be null until the module is loaded.
    + * @type {goog.module.BaseModule?}
    + * @private
    + */
    +goog.module.ModuleInfo.prototype.module_ = null;
    +
    +
    +/**
    + * Gets the dependencies of this module.
    + * @return {Array<string>} The ids of the modules that this module depends on.
    + */
    +goog.module.ModuleInfo.prototype.getDependencies = function() {
    +  return this.deps_;
    +};
    +
    +
    +/**
    + * Gets the ID of this module.
    + * @return {string} The ID.
    + */
    +goog.module.ModuleInfo.prototype.getId = function() {
    +  return this.id_;
    +};
    +
    +
    +/**
    + * Sets the uris of this module.
    + * @param {Array<string>} uris Uris for this module's code.
    + */
    +goog.module.ModuleInfo.prototype.setUris = function(uris) {
    +  this.uris_ = uris;
    +};
    +
    +
    +/**
    + * Gets the uris of this module.
    + * @return {Array<string>?} Uris for this module's code.
    + */
    +goog.module.ModuleInfo.prototype.getUris = function() {
    +  return this.uris_;
    +};
    +
    +
    +/**
    + * Sets the constructor to use to instantiate the module object after the
    + * module code is loaded.
    + * @param {Function} constructor The constructor of a goog.module.BaseModule
    + *     subclass.
    + */
    +goog.module.ModuleInfo.prototype.setModuleConstructor = function(
    +    constructor) {
    +  if (this.moduleConstructor_ === goog.module.BaseModule) {
    +    this.moduleConstructor_ = constructor;
    +  } else {
    +    throw Error('Cannot set module constructor more than once.');
    +  }
    +};
    +
    +
    +/**
    + * Registers a function that should be called after the module is loaded. These
    + * early callbacks are called after {@link Module#initialize} is called but
    + * before the other callbacks are called.
    + * @param {Function} fn A callback function that takes a single argument which
    + *    is the module context.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @return {!goog.module.ModuleLoadCallback} Reference to the callback
    + *     object.
    + */
    +goog.module.ModuleInfo.prototype.registerEarlyCallback = function(
    +    fn, opt_handler) {
    +  return this.registerCallback_(this.earlyOnloadCallbacks_, fn, opt_handler);
    +};
    +
    +
    +/**
    + * Registers a function that should be called after the module is loaded.
    + * @param {Function} fn A callback function that takes a single argument which
    + *    is the module context.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @return {!goog.module.ModuleLoadCallback} Reference to the callback
    + *     object.
    + */
    +goog.module.ModuleInfo.prototype.registerCallback = function(
    +    fn, opt_handler) {
    +  return this.registerCallback_(this.onloadCallbacks_, fn, opt_handler);
    +};
    +
    +
    +/**
    + * Registers a function that should be called if the module load fails.
    + * @param {Function} fn A callback function that takes a single argument which
    + *    is the failure type.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @return {!goog.module.ModuleLoadCallback} Reference to the callback
    + *     object.
    + */
    +goog.module.ModuleInfo.prototype.registerErrback = function(
    +    fn, opt_handler) {
    +  return this.registerCallback_(this.onErrorCallbacks_, fn, opt_handler);
    +};
    +
    +
    +/**
    + * Registers a function that should be called after the module is loaded.
    + * @param {Array<goog.module.ModuleLoadCallback>} callbacks The array to
    + *     add the callback to.
    + * @param {Function} fn A callback function that takes a single argument which
    + *     is the module context.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @return {!goog.module.ModuleLoadCallback} Reference to the callback
    + *     object.
    + * @private
    + */
    +goog.module.ModuleInfo.prototype.registerCallback_ = function(
    +    callbacks, fn, opt_handler) {
    +  var callback = new goog.module.ModuleLoadCallback(fn, opt_handler);
    +  callbacks.push(callback);
    +  return callback;
    +};
    +
    +
    +/**
    + * Determines whether the module has been loaded.
    + * @return {boolean} Whether the module has been loaded.
    + */
    +goog.module.ModuleInfo.prototype.isLoaded = function() {
    +  return !!this.module_;
    +};
    +
    +
    +/**
    + * Gets the module.
    + * @return {goog.module.BaseModule?} The module if it has been loaded.
    + *     Otherwise, null.
    + */
    +goog.module.ModuleInfo.prototype.getModule = function() {
    +  return this.module_;
    +};
    +
    +
    +/**
    + * Sets this module as loaded.
    + * @param {function() : Object} contextProvider A function that provides the
    + *     module context.
    + * @return {boolean} Whether any errors occurred while executing the onload
    + *     callbacks.
    + */
    +goog.module.ModuleInfo.prototype.onLoad = function(contextProvider) {
    +  // Instantiate and initialize the module object.
    +  var module = new this.moduleConstructor_;
    +  module.initialize(contextProvider());
    +
    +  // Keep an internal reference to the module.
    +  this.module_ = module;
    +
    +  // Fire any early callbacks that were waiting for the module to be loaded.
    +  var errors =
    +      !!this.callCallbacks_(this.earlyOnloadCallbacks_, contextProvider());
    +
    +  // Fire any callbacks that were waiting for the module to be loaded.
    +  errors = errors ||
    +      !!this.callCallbacks_(this.onloadCallbacks_, contextProvider());
    +
    +  if (!errors) {
    +    // Clear the errbacks.
    +    this.onErrorCallbacks_.length = 0;
    +  }
    +
    +  return errors;
    +};
    +
    +
    +/**
    + * Calls the error callbacks for the module.
    + * @param {goog.module.ModuleManager.FailureType} cause What caused the error.
    + */
    +goog.module.ModuleInfo.prototype.onError = function(cause) {
    +  var result = this.callCallbacks_(this.onErrorCallbacks_, cause);
    +  if (result) {
    +    // Throw an exception asynchronously. Do not let the exception leak
    +    // up to the caller, or it will blow up the module loading framework.
    +    window.setTimeout(
    +        goog.functions.error('Module errback failures: ' + result), 0);
    +  }
    +  this.earlyOnloadCallbacks_.length = 0;
    +  this.onloadCallbacks_.length = 0;
    +};
    +
    +
    +/**
    + * Helper to call the callbacks after module load.
    + * @param {Array<goog.module.ModuleLoadCallback>} callbacks The callbacks
    + *     to call and then clear.
    + * @param {*} context The module context.
    + * @return {Array<*>} Any errors encountered while calling the callbacks,
    + *     or null if there were no errors.
    + * @private
    + */
    +goog.module.ModuleInfo.prototype.callCallbacks_ = function(callbacks, context) {
    +  // NOTE(nicksantos):
    +  // In practice, there are two error-handling scenarios:
    +  // 1) The callback does some mandatory initialization of the module.
    +  // 2) The callback is for completion of some optional UI event.
    +  // There's no good way to handle both scenarios.
    +  //
    +  // Our strategy here is to protect module manager from exceptions, so that
    +  // the failure of one module doesn't affect the loading of other modules.
    +  // Then, we try to report the exception as best we can.
    +
    +  // Call each callback in the order they were registered
    +  var errors = [];
    +  for (var i = 0; i < callbacks.length; i++) {
    +    try {
    +      callbacks[i].execute(context);
    +    } catch (e) {
    +      errors.push(e);
    +    }
    +  }
    +
    +  // Clear the list of callbacks.
    +  callbacks.length = 0;
    +  return errors.length ? errors : null;
    +};
    +
    +
    +/** @override */
    +goog.module.ModuleInfo.prototype.disposeInternal = function() {
    +  goog.module.ModuleInfo.superClass_.disposeInternal.call(this);
    +  goog.dispose(this.module_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.html b/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.html
    new file mode 100644
    index 0000000000..8351b9171f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.module.ModuleInfo
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.module.ModuleInfoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.js b/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.js
    new file mode 100644
    index 0000000000..02fd9ab40a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleinfo_test.js
    @@ -0,0 +1,104 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.module.ModuleInfoTest');
    +goog.setTestOnly('goog.module.ModuleInfoTest');
    +
    +goog.require('goog.module.BaseModule');
    +goog.require('goog.module.ModuleInfo');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Test initial state of module info.
    + */
    +function testNotLoadedAtStart() {
    +  var m = new goog.module.ModuleInfo();
    +  assertFalse('Shouldn\'t be loaded', m.isLoaded());
    +}
    +
    +var TestModule = function() {
    +  goog.module.BaseModule.call(this);
    +};
    +goog.inherits(TestModule, goog.module.BaseModule);
    +
    +
    +/**
    + * Test loaded module info.
    + */
    +function testOnLoad() {
    +  var m = new goog.module.ModuleInfo();
    +
    +  m.setModuleConstructor(TestModule);
    +  m.onLoad(goog.nullFunction);
    +  assertTrue(m.isLoaded());
    +
    +  var module = m.getModule();
    +  assertNotNull(module);
    +  assertTrue(module instanceof TestModule);
    +
    +  m.dispose();
    +  assertTrue(m.isDisposed());
    +  assertTrue('Disposing of ModuleInfo should dispose of its module',
    +      module.isDisposed());
    +}
    +
    +
    +/**
    + * Test callbacks on module load.
    + */
    +function testCallbacks() {
    +  var m = new goog.module.ModuleInfo();
    +  m.setModuleConstructor(TestModule);
    +  var index = 0;
    +  var a = -1, b = -1, c = -1, d = -1;
    +  var ca = m.registerCallback(function() { a = index++; });
    +  var cb = m.registerCallback(function() { b = index++; });
    +  var cc = m.registerCallback(function() { c = index++; });
    +  var cd = m.registerEarlyCallback(function() { d = index++; });
    +  cb.abort();
    +  m.onLoad(goog.nullFunction);
    +
    +  assertTrue('callback A should have fired', a >= 0);
    +  assertFalse('callback B should have been aborted', b >= 0);
    +  assertTrue('callback C should have fired', c >= 0);
    +  assertTrue('early callback d should have fired', d >= 0);
    +
    +  assertEquals('ordering of callbacks was wrong', 0, d);
    +  assertEquals('ordering of callbacks was wrong', 1, a);
    +  assertEquals('ordering of callbacks was wrong', 2, c);
    +}
    +
    +
    +/**
    + * Tests the error callbacks.
    + */
    +function testErrbacks() {
    +  var m = new goog.module.ModuleInfo();
    +  m.setModuleConstructor(TestModule);
    +  var index = 0;
    +  var a = -1, b = -1, c = -1, d = -1;
    +  var ca = m.registerErrback(function() { a = index++; });
    +  var cb = m.registerErrback(function() { b = index++; });
    +  var cc = m.registerErrback(function() { c = index++; });
    +  m.onError('foo');
    +
    +  assertTrue('callback A should have fired', a >= 0);
    +  assertTrue('callback B should have fired', b >= 0);
    +  assertTrue('callback C should have fired', c >= 0);
    +
    +  assertEquals('ordering of callbacks was wrong', 0, a);
    +  assertEquals('ordering of callbacks was wrong', 1, b);
    +  assertEquals('ordering of callbacks was wrong', 2, c);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback.js b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback.js
    new file mode 100644
    index 0000000000..bd7c1d72b7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback.js
    @@ -0,0 +1,87 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A simple callback mechanism for notification about module
    + * loads. Should be considered package-private to goog.module.
    + *
    + */
    +
    +goog.provide('goog.module.ModuleLoadCallback');
    +
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.debug.errorHandlerWeakDep');
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +
    +
    +
    +/**
    + * Class used to encapsulate the callbacks to be called when a module loads.
    + * @param {Function} fn Callback function.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @constructor
    + * @final
    + */
    +goog.module.ModuleLoadCallback = function(fn, opt_handler) {
    +  /**
    +   * Callback function.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.fn_ = fn;
    +
    +  /**
    +   * Optional handler under whose scope to execute the callback.
    +   * @type {Object|undefined}
    +   * @private
    +   */
    +  this.handler_ = opt_handler;
    +};
    +
    +
    +/**
    + * Completes the operation and calls the callback function if appropriate.
    + * @param {*} context The module context.
    + */
    +goog.module.ModuleLoadCallback.prototype.execute = function(context) {
    +  if (this.fn_) {
    +    this.fn_.call(this.handler_ || null, context);
    +    this.handler_ = null;
    +    this.fn_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Abort the callback, but not the actual module load.
    + */
    +goog.module.ModuleLoadCallback.prototype.abort = function() {
    +  this.fn_ = null;
    +  this.handler_ = null;
    +};
    +
    +
    +// Register the browser event handler as an entry point, so that
    +// it can be monitored for exception handling, etc.
    +goog.debug.entryPointRegistry.register(
    +    /**
    +     * @param {function(!Function): !Function} transformer The transforming
    +     *     function.
    +     */
    +    function(transformer) {
    +      goog.module.ModuleLoadCallback.prototype.execute =
    +          transformer(goog.module.ModuleLoadCallback.prototype.execute);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.html b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.html
    new file mode 100644
    index 0000000000..f7a1b9eefc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   JsUnit tests for goog.module.ModuleLoadCallback
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.module.ModuleLoadCallbackTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.js b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.js
    new file mode 100644
    index 0000000000..72ca928138
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloadcallback_test.js
    @@ -0,0 +1,45 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.module.ModuleLoadCallbackTest');
    +goog.setTestOnly('goog.module.ModuleLoadCallbackTest');
    +
    +goog.require('goog.debug.ErrorHandler');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.functions');
    +goog.require('goog.module.ModuleLoadCallback');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +function testProtectEntryPoint() {
    +  // Test a callback created before the protect method is called.
    +  var callback1 = new goog.module.ModuleLoadCallback(
    +      goog.functions.error('callback1'));
    +
    +  var errorFn = goog.testing.recordFunction();
    +  var errorHandler = new goog.debug.ErrorHandler(errorFn);
    +  goog.debug.entryPointRegistry.monitorAll(errorHandler);
    +
    +  assertEquals(0, errorFn.getCallCount());
    +  assertThrows(goog.bind(callback1.execute, callback1));
    +  assertEquals(1, errorFn.getCallCount());
    +  assertContains('callback1', errorFn.getLastCall().getArguments()[0].message);
    +
    +  // Test a callback created after the protect method is called.
    +  var callback2 = new goog.module.ModuleLoadCallback(
    +      goog.functions.error('callback2'));
    +  assertThrows(goog.bind(callback1.execute, callback2));
    +  assertEquals(2, errorFn.getCallCount());
    +  assertContains('callback2', errorFn.getLastCall().getArguments()[0].message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloader.js b/mapbox-gl/closure-library/closure/goog/module/moduleloader.js
    new file mode 100644
    index 0000000000..7e3496d392
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloader.js
    @@ -0,0 +1,461 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The module loader for loading modules across the network.
    + *
    + * Browsers do not guarantee that scripts appended to the document
    + * are executed in the order they are added. For production mode, we use
    + * XHRs to load scripts, because they do not have this problem and they
    + * have superior mechanisms for handling failure. However, XHR-evaled
    + * scripts are harder to debug.
    + *
    + * In debugging mode, we use normal script tags. In order to make this work,
    + * we load the scripts in serial: we do not execute script B to the document
    + * until we are certain that script A is finished loading.
    + *
    + */
    +
    +goog.provide('goog.module.ModuleLoader');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.log');
    +goog.require('goog.module.AbstractModuleLoader');
    +goog.require('goog.net.BulkLoader');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.jsloader');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +
    +
    +/**
    + * A class that loads Javascript modules.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @implements {goog.module.AbstractModuleLoader}
    + */
    +goog.module.ModuleLoader = function() {
    +  goog.module.ModuleLoader.base(this, 'constructor');
    +
    +  /**
    +   * Event handler for managing handling events.
    +   * @type {goog.events.EventHandler<!goog.module.ModuleLoader>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * A map from module IDs to goog.module.ModuleLoader.LoadStatus.
    +   * @type {!Object<Array<string>, goog.module.ModuleLoader.LoadStatus>}
    +   * @private
    +   */
    +  this.loadingModulesStatus_ = {};
    +};
    +goog.inherits(goog.module.ModuleLoader, goog.events.EventTarget);
    +
    +
    +/**
    + * A logger.
    + * @type {goog.log.Logger}
    + * @protected
    + */
    +goog.module.ModuleLoader.prototype.logger = goog.log.getLogger(
    +    'goog.module.ModuleLoader');
    +
    +
    +/**
    + * Whether debug mode is enabled.
    + * @type {boolean}
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.debugMode_ = false;
    +
    +
    +/**
    + * Whether source url injection is enabled.
    + * @type {boolean}
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.sourceUrlInjection_ = false;
    +
    +
    +/**
    + * @return {boolean} Whether sourceURL affects stack traces.
    + *     Chrome is currently the only browser that does this, but
    + *     we believe other browsers are working on this.
    + * @see http://bugzilla.mozilla.org/show_bug.cgi?id=583083
    + */
    +goog.module.ModuleLoader.supportsSourceUrlStackTraces = function() {
    +  return goog.userAgent.product.CHROME;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether sourceURL affects the debugger.
    + */
    +goog.module.ModuleLoader.supportsSourceUrlDebugger = function() {
    +  return goog.userAgent.product.CHROME || goog.userAgent.GECKO;
    +};
    +
    +
    +/**
    + * Gets the debug mode for the loader.
    + * @return {boolean} Whether the debug mode is enabled.
    + */
    +goog.module.ModuleLoader.prototype.getDebugMode = function() {
    +  return this.debugMode_;
    +};
    +
    +
    +/**
    + * Sets the debug mode for the loader.
    + * @param {boolean} debugMode Whether the debug mode is enabled.
    + */
    +goog.module.ModuleLoader.prototype.setDebugMode = function(debugMode) {
    +  this.debugMode_ = debugMode;
    +};
    +
    +
    +/**
    + * When enabled, we will add a sourceURL comment to the end of all scripts
    + * to mark their origin.
    + *
    + * On WebKit, stack traces will refect the sourceURL comment, so this is
    + * useful for debugging webkit stack traces in production.
    + *
    + * Notice that in debug mode, we will use source url injection + eval rather
    + * then appending script nodes to the DOM, because the scripts will load far
    + * faster.  (Appending script nodes is very slow, because we can't parallelize
    + * the downloading and evaling of the script).
    + *
    + * The cost of appending sourceURL information is negligible when compared to
    + * the cost of evaling the script. Almost all clients will want this on.
    + *
    + * TODO(nicksantos): Turn this on by default. We may want to turn this off
    + * for clients that inject their own sourceURL.
    + *
    + * @param {boolean} enabled Whether source url injection is enabled.
    + */
    +goog.module.ModuleLoader.prototype.setSourceUrlInjection = function(enabled) {
    +  this.sourceUrlInjection_ = enabled;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we're using source url injection.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.usingSourceUrlInjection_ = function() {
    +  return this.sourceUrlInjection_ ||
    +      (this.getDebugMode() &&
    +       goog.module.ModuleLoader.supportsSourceUrlStackTraces());
    +};
    +
    +
    +/** @override */
    +goog.module.ModuleLoader.prototype.loadModules = function(
    +    ids, moduleInfoMap, opt_successFn, opt_errorFn, opt_timeoutFn,
    +    opt_forceReload) {
    +  var loadStatus = this.loadingModulesStatus_[ids] ||
    +      new goog.module.ModuleLoader.LoadStatus();
    +  loadStatus.loadRequested = true;
    +  loadStatus.successFn = opt_successFn || null;
    +  loadStatus.errorFn = opt_errorFn || null;
    +
    +  if (!this.loadingModulesStatus_[ids]) {
    +    // Modules were not prefetched.
    +    this.loadingModulesStatus_[ids] = loadStatus;
    +    this.downloadModules_(ids, moduleInfoMap);
    +    // TODO(user): Need to handle timeouts in the module loading code.
    +  } else if (goog.isDefAndNotNull(loadStatus.responseTexts)) {
    +    // Modules prefetch is complete.
    +    this.evaluateCode_(ids);
    +  }
    +  // Otherwise modules prefetch is in progress, and these modules will be
    +  // executed after the prefetch is complete.
    +};
    +
    +
    +/**
    + * Evaluate the JS code.
    + * @param {Array<string>} moduleIds The module ids.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.evaluateCode_ = function(moduleIds) {
    +  this.dispatchEvent(new goog.module.ModuleLoader.Event(
    +      goog.module.ModuleLoader.EventType.REQUEST_SUCCESS, moduleIds));
    +
    +  goog.log.info(this.logger, 'evaluateCode ids:' + moduleIds);
    +  var success = true;
    +  var loadStatus = this.loadingModulesStatus_[moduleIds];
    +  var uris = loadStatus.requestUris;
    +  var texts = loadStatus.responseTexts;
    +  try {
    +    if (this.usingSourceUrlInjection_()) {
    +      for (var i = 0; i < uris.length; i++) {
    +        var uri = uris[i];
    +        goog.globalEval(texts[i] + ' //@ sourceURL=' + uri);
    +      }
    +    } else {
    +      goog.globalEval(texts.join('\n'));
    +    }
    +  } catch (e) {
    +    success = false;
    +    // TODO(user): Consider throwing an exception here.
    +    goog.log.warning(this.logger, 'Loaded incomplete code for module(s): ' +
    +        moduleIds, e);
    +  }
    +
    +  this.dispatchEvent(
    +      new goog.module.ModuleLoader.Event(
    +          goog.module.ModuleLoader.EventType.EVALUATE_CODE, moduleIds));
    +
    +  if (!success) {
    +    this.handleErrorHelper_(moduleIds, loadStatus.errorFn, null /* status */);
    +  } else if (loadStatus.successFn) {
    +    loadStatus.successFn();
    +  }
    +  delete this.loadingModulesStatus_[moduleIds];
    +};
    +
    +
    +/**
    + * Handles a successful response to a request for prefetch or load one or more
    + * modules.
    + *
    + * @param {goog.net.BulkLoader} bulkLoader The bulk loader.
    + * @param {Array<string>} moduleIds The ids of the modules requested.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.handleSuccess_ = function(
    +    bulkLoader, moduleIds) {
    +  goog.log.info(this.logger, 'Code loaded for module(s): ' + moduleIds);
    +
    +  var loadStatus = this.loadingModulesStatus_[moduleIds];
    +  loadStatus.responseTexts = bulkLoader.getResponseTexts();
    +
    +  if (loadStatus.loadRequested) {
    +    this.evaluateCode_(moduleIds);
    +  }
    +
    +  // NOTE: A bulk loader instance is used for loading a set of module ids.
    +  // Once these modules have been loaded successfully or in error the bulk
    +  // loader should be disposed as it is not needed anymore. A new bulk loader
    +  // is instantiated for any new modules to be loaded. The dispose is called
    +  // on a timer so that the bulkloader has a chance to release its
    +  // objects.
    +  goog.Timer.callOnce(bulkLoader.dispose, 5, bulkLoader);
    +};
    +
    +
    +/** @override */
    +goog.module.ModuleLoader.prototype.prefetchModule = function(
    +    id, moduleInfo) {
    +  // Do not prefetch in debug mode.
    +  if (this.getDebugMode()) {
    +    return;
    +  }
    +  var loadStatus = this.loadingModulesStatus_[[id]];
    +  if (loadStatus) {
    +    return;
    +  }
    +
    +  var moduleInfoMap = {};
    +  moduleInfoMap[id] = moduleInfo;
    +  this.loadingModulesStatus_[[id]] = new goog.module.ModuleLoader.LoadStatus();
    +  this.downloadModules_([id], moduleInfoMap);
    +};
    +
    +
    +/**
    + * Downloads a list of JavaScript modules.
    + *
    + * @param {Array<string>} ids The module ids in dependency order.
    + * @param {Object} moduleInfoMap A mapping from module id to ModuleInfo object.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.downloadModules_ = function(
    +    ids, moduleInfoMap) {
    +  var uris = [];
    +  for (var i = 0; i < ids.length; i++) {
    +    goog.array.extend(uris, moduleInfoMap[ids[i]].getUris());
    +  }
    +  goog.log.info(this.logger, 'downloadModules ids:' + ids + ' uris:' + uris);
    +
    +  if (this.getDebugMode() &&
    +      !this.usingSourceUrlInjection_()) {
    +    // In debug mode use <script> tags rather than XHRs to load the files.
    +    // This makes it possible to debug and inspect stack traces more easily.
    +    // It's also possible to use it to load JavaScript files that are hosted on
    +    // another domain.
    +    // The scripts need to load serially, so this is much slower than parallel
    +    // script loads with source url injection.
    +    goog.net.jsloader.loadMany(uris);
    +  } else {
    +    var loadStatus = this.loadingModulesStatus_[ids];
    +    loadStatus.requestUris = uris;
    +
    +    var bulkLoader = new goog.net.BulkLoader(uris);
    +
    +    var eventHandler = this.eventHandler_;
    +    eventHandler.listen(
    +        bulkLoader,
    +        goog.net.EventType.SUCCESS,
    +        goog.bind(this.handleSuccess_, this, bulkLoader, ids));
    +    eventHandler.listen(
    +        bulkLoader,
    +        goog.net.EventType.ERROR,
    +        goog.bind(this.handleError_, this, bulkLoader, ids));
    +    bulkLoader.load();
    +  }
    +};
    +
    +
    +/**
    + * Handles an error during a request for one or more modules.
    + * @param {goog.net.BulkLoader} bulkLoader The bulk loader.
    + * @param {Array<string>} moduleIds The ids of the modules requested.
    + * @param {number} status The response status.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.handleError_ = function(
    +    bulkLoader, moduleIds, status) {
    +  var loadStatus = this.loadingModulesStatus_[moduleIds];
    +  // The bulk loader doesn't cancel other requests when a request fails. We will
    +  // delete the loadStatus in the first failure, so it will be undefined in
    +  // subsequent errors.
    +  if (loadStatus) {
    +    delete this.loadingModulesStatus_[moduleIds];
    +    this.handleErrorHelper_(moduleIds, loadStatus.errorFn, status);
    +  }
    +
    +  // NOTE: A bulk loader instance is used for loading a set of module ids. Once
    +  // these modules have been loaded successfully or in error the bulk loader
    +  // should be disposed as it is not needed anymore. A new bulk loader is
    +  // instantiated for any new modules to be loaded. The dispose is called
    +  // on another thread so that the bulkloader has a chance to release its
    +  // objects.
    +  goog.Timer.callOnce(bulkLoader.dispose, 5, bulkLoader);
    +};
    +
    +
    +/**
    + * Handles an error during a request for one or more modules.
    + * @param {Array<string>} moduleIds The ids of the modules requested.
    + * @param {?function(?number)} errorFn The function to call on failure.
    + * @param {?number} status The response status.
    + * @private
    + */
    +goog.module.ModuleLoader.prototype.handleErrorHelper_ = function(
    +    moduleIds, errorFn, status) {
    +  this.dispatchEvent(
    +      new goog.module.ModuleLoader.Event(
    +          goog.module.ModuleLoader.EventType.REQUEST_ERROR, moduleIds));
    +
    +  goog.log.warning(this.logger, 'Request failed for module(s): ' + moduleIds);
    +
    +  if (errorFn) {
    +    errorFn(status);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.module.ModuleLoader.prototype.disposeInternal = function() {
    +  goog.module.ModuleLoader.superClass_.disposeInternal.call(this);
    +
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +};
    +
    +
    +/**
    + * @enum {string}
    + */
    +goog.module.ModuleLoader.EventType = {
    +  /** Called after the code for a module is evaluated. */
    +  EVALUATE_CODE: goog.events.getUniqueId('evaluateCode'),
    +
    +  /** Called when the BulkLoader finishes successfully. */
    +  REQUEST_SUCCESS: goog.events.getUniqueId('requestSuccess'),
    +
    +  /** Called when the BulkLoader fails, or code loading fails. */
    +  REQUEST_ERROR: goog.events.getUniqueId('requestError')
    +};
    +
    +
    +
    +/**
    + * @param {goog.module.ModuleLoader.EventType} type The type.
    + * @param {Array<string>} moduleIds The ids of the modules being evaluated.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.module.ModuleLoader.Event = function(type, moduleIds) {
    +  goog.module.ModuleLoader.Event.base(this, 'constructor', type);
    +
    +  /**
    +   * @type {Array<string>}
    +   */
    +  this.moduleIds = moduleIds;
    +};
    +goog.inherits(goog.module.ModuleLoader.Event, goog.events.Event);
    +
    +
    +
    +/**
    + * A class that keeps the state of the module during the loading process. It is
    + * used to save loading information between modules download and evaluation.
    + * @constructor
    + * @final
    + */
    +goog.module.ModuleLoader.LoadStatus = function() {
    +  /**
    +   * The request uris.
    +   * @type {Array<string>}
    +   */
    +  this.requestUris = null;
    +
    +  /**
    +   * The response texts.
    +   * @type {Array<string>}
    +   */
    +  this.responseTexts = null;
    +
    +  /**
    +   * Whether loadModules was called for the set of modules referred by this
    +   * status.
    +   * @type {boolean}
    +   */
    +  this.loadRequested = false;
    +
    +  /**
    +   * Success callback.
    +   * @type {?function()}
    +   */
    +  this.successFn = null;
    +
    +  /**
    +   * Error callback.
    +   * @type {?function(?number)}
    +   */
    +  this.errorFn = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.html b/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.html
    new file mode 100644
    index 0000000000..221505d473
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.html
    @@ -0,0 +1,30 @@
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +
    +<!--
    +  A regression test for goog.module.ModuleLoader.
    +
    +  Unlike the unit tests for goog.module.ModuleManager, this uses
    +  asynchronous test cases and real XHRs.
    +
    +  Author: nicksantos@google.com (Nick Santos)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>JsUnit tests for goog.module.ModuleLoader</title>
    +<script src='../base.js'></script>
    +<script>
    +goog.require('goog.module.ModuleLoaderTest');
    +</script>
    +</head>
    +<body>
    +<b>Note:</b>: If you are running this test off local disk on Chrome, it
    +will fail unless you start Chrome with
    +<code>--allow-file-access-from-files</code>.
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.js b/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.js
    new file mode 100644
    index 0000000000..9ac2ada2da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/moduleloader_test.js
    @@ -0,0 +1,439 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests for goog.module.ModuleLoader.
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.module.ModuleLoaderTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.module.ModuleLoader');
    +goog.require('goog.module.ModuleManager');
    +goog.require('goog.net.BulkLoader');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events.EventObserver');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +goog.setTestOnly('goog.module.ModuleLoaderTest');
    +
    +
    +var modA1Loaded = false;
    +var modA2Loaded = false;
    +var modB1Loaded = false;
    +
    +var moduleLoader = null;
    +var moduleManager = null;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var testCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
    +testCase.stepTimeout = 5 * 1000; // 5 seconds
    +
    +var EventType = goog.module.ModuleLoader.EventType;
    +var observer;
    +
    +testCase.setUp = function() {
    +  modA1Loaded = false;
    +  modA2Loaded = false;
    +  modB1Loaded = false;
    +
    +  goog.provide = goog.nullFunction;
    +  moduleManager = goog.module.ModuleManager.getInstance();
    +  stubs.replace(moduleManager, 'getBackOff_', goog.functions.constant(0));
    +
    +  moduleLoader = new goog.module.ModuleLoader();
    +  observer = new goog.testing.events.EventObserver();
    +
    +  goog.events.listen(
    +      moduleLoader, goog.object.getValues(EventType), observer);
    +
    +  moduleManager.setLoader(moduleLoader);
    +  moduleManager.setAllModuleInfo({
    +    'modA': [],
    +    'modB': ['modA']
    +  });
    +  moduleManager.setModuleUris({
    +    'modA': ['testdata/modA_1.js', 'testdata/modA_2.js'],
    +    'modB': ['testdata/modB_1.js']
    +  });
    +
    +  assertNotLoaded('modA');
    +  assertNotLoaded('modB');
    +  assertFalse(modA1Loaded);
    +};
    +
    +testCase.tearDown = function() {
    +  stubs.reset();
    +
    +  // Ensure that the module manager was created.
    +  assertNotNull(goog.module.ModuleManager.getInstance());
    +  moduleManager = goog.module.ModuleManager.instance_ = null;
    +
    +  // tear down the module loaded flag.
    +  modA1Loaded = false;
    +
    +  // Remove all the fake scripts.
    +  var scripts = goog.array.clone(
    +      document.getElementsByTagName('SCRIPT'));
    +  for (var i = 0; i < scripts.length; i++) {
    +    if (scripts[i].src.indexOf('testdata') != -1) {
    +      goog.dom.removeNode(scripts[i]);
    +    }
    +  }
    +};
    +
    +function testLoadModuleA() {
    +  testCase.waitForAsync('wait for module A load');
    +  moduleManager.execOnLoad('modA', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertNotLoaded('modB');
    +    assertTrue(modA1Loaded);
    +
    +    assertEquals('EVALUATE_CODE',
    +        0, observer.getEvents(EventType.EVALUATE_CODE).length);
    +    assertEquals('REQUEST_SUCCESS',
    +        1, observer.getEvents(EventType.REQUEST_SUCCESS).length);
    +    assertArrayEquals(
    +        ['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
    +    assertEquals('REQUEST_ERROR',
    +        0, observer.getEvents(EventType.REQUEST_ERROR).length);
    +  });
    +}
    +
    +function testLoadModuleB() {
    +  testCase.waitForAsync('wait for module B load');
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +    assertTrue(modA1Loaded);
    +  });
    +}
    +
    +function testLoadDebugModuleA() {
    +  testCase.waitForAsync('wait for module A load');
    +  moduleLoader.setDebugMode(true);
    +  moduleManager.execOnLoad('modA', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertNotLoaded('modB');
    +    assertTrue(modA1Loaded);
    +  });
    +}
    +
    +function testLoadDebugModuleB() {
    +  testCase.waitForAsync('wait for module B load');
    +  moduleLoader.setDebugMode(true);
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +    assertTrue(modA1Loaded);
    +  });
    +}
    +
    +function testLoadDebugModuleAThenB() {
    +  // Swap the script tags of module A, to introduce a race condition.
    +  // See the comments on this in ModuleLoader's debug loader.
    +  moduleManager.setModuleUris({
    +    'modA': ['testdata/modA_2.js', 'testdata/modA_1.js'],
    +    'modB': ['testdata/modB_1.js']
    +  });
    +  testCase.waitForAsync('wait for module B load');
    +  moduleLoader.setDebugMode(true);
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +
    +    var scripts = goog.array.clone(
    +        document.getElementsByTagName('SCRIPT'));
    +    var seenLastScriptOfModuleA = false;
    +    for (var i = 0; i < scripts.length; i++) {
    +      var uri = scripts[i].src;
    +      if (uri.indexOf('modA_1.js') >= 0) {
    +        seenLastScriptOfModuleA = true;
    +      } else if (uri.indexOf('modB') >= 0) {
    +        assertTrue(seenLastScriptOfModuleA);
    +      }
    +    }
    +  });
    +}
    +
    +function testSourceInjection() {
    +  moduleLoader.setSourceUrlInjection(true);
    +  assertSourceInjection();
    +}
    +
    +function testSourceInjectionViaDebugMode() {
    +  moduleLoader.setDebugMode(true);
    +  assertSourceInjection();
    +}
    +
    +function assertSourceInjection() {
    +  testCase.waitForAsync('wait for module B load');
    +
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +
    +    assertTrue(!!throwErrorInModuleB);
    +
    +    var ex = assertThrows(function() {
    +      throwErrorInModuleB();
    +    });
    +
    +    var stackTrace = ex.stack.toString();
    +    var expectedString = 'testdata/modB_1.js';
    +
    +    if (goog.module.ModuleLoader.supportsSourceUrlStackTraces()) {
    +      // Source URL should be added in eval or in jsloader.
    +      assertContains(expectedString, stackTrace);
    +    } else if (moduleLoader.isDebugMode()) {
    +      // Browsers used jsloader, thus URLs are present.
    +      assertContains(expectedString, stackTrace);
    +    } else {
    +      // Browser used eval, does not support source URL.
    +      assertNotContains(expectedString, stackTrace);
    +    }
    +  });
    +}
    +
    +function testModuleLoaderRecursesTooDeep(opt_numModules) {
    +  // There was a bug in the module loader where it would retry recursively
    +  // whenever there was a synchronous failure in the module load. When you
    +  // asked for modB, it would try to load its dependency modA. When modA
    +  // failed, it would move onto modB, and then start over, repeating until it
    +  // ran out of stack.
    +  var numModules = opt_numModules || 1;
    +  var uris = {};
    +  var deps = {};
    +  var mods = [];
    +  for (var num = 0; num < numModules; num++) {
    +    var modName = 'mod' + num;
    +    mods.unshift(modName);
    +    uris[modName] = [];
    +    deps[modName] = num ? ['mod' + (num - 1)] : [];
    +    for (var i = 0; i < 5; i++) {
    +      uris[modName].push(
    +          'http://www.google.com/crossdomain' + num + 'x' + i + '.js');
    +    }
    +  }
    +
    +  moduleManager.setAllModuleInfo(deps);
    +  moduleManager.setModuleUris(uris);
    +
    +  // Make all XHRs throw an error, so that we test the error-handling
    +  // functionality.
    +  var oldXmlHttp = goog.net.XmlHttp;
    +  stubs.set(goog.net, 'XmlHttp', function() {
    +    return {
    +      open: goog.functions.error('mock error'),
    +      abort: goog.nullFunction
    +    };
    +  });
    +  goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
    +
    +  var errorCount = 0;
    +  var errorIds = [];
    +  var errorHandler = function(ignored, modId) {
    +    errorCount++;
    +    errorIds.push(modId);
    +  };
    +  moduleManager.registerCallback(
    +      goog.module.ModuleManager.CallbackType.ERROR,
    +      errorHandler);
    +
    +  moduleManager.execOnLoad(mods[0], function() {
    +    fail('modB should not load successfully');
    +  });
    +
    +  assertEquals(mods.length, errorCount);
    +
    +  goog.array.sort(mods);
    +  goog.array.sort(errorIds);
    +  assertArrayEquals(mods, errorIds);
    +
    +  assertArrayEquals([], moduleManager.requestedModuleIdsQueue_);
    +  assertArrayEquals([], moduleManager.userInitiatedLoadingModuleIds_);
    +}
    +
    +function testModuleLoaderRecursesTooDeep2modules() {
    +  testModuleLoaderRecursesTooDeep(2);
    +}
    +
    +function testModuleLoaderRecursesTooDeep3modules() {
    +  testModuleLoaderRecursesTooDeep(3);
    +}
    +
    +function testModuleLoaderRecursesTooDeep4modules() {
    +  testModuleLoaderRecursesTooDeep(3);
    +}
    +
    +function testErrback() {
    +  // Don't run this test on IE, because the way the test runner catches
    +  // errors on IE plays badly with the simulated errors in the test.
    +  if (goog.userAgent.IE) return;
    +
    +  // Modules will throw an exception if this boolean is set to true.
    +  modA1Loaded = true;
    +
    +  var errorHandler = function() {
    +    testCase.continueTesting();
    +    assertNotLoaded('modA');
    +  };
    +  moduleManager.registerCallback(
    +      goog.module.ModuleManager.CallbackType.ERROR,
    +      errorHandler);
    +
    +  moduleManager.execOnLoad('modA', function() {
    +    fail('modA should not load successfully');
    +  });
    +
    +  testCase.waitForAsync('wait for the error callback');
    +}
    +
    +function testPrefetchThenLoadModuleA() {
    +  moduleManager.prefetchModule('modA');
    +  stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
    +    fail('modA should not be reloaded');
    +  });
    +
    +  testCase.waitForAsync('wait for module A load');
    +  moduleManager.execOnLoad('modA', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertEquals('REQUEST_SUCCESS',
    +        1, observer.getEvents(EventType.REQUEST_SUCCESS).length);
    +    assertArrayEquals(
    +        ['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
    +    assertEquals('REQUEST_ERROR',
    +        0, observer.getEvents(EventType.REQUEST_ERROR).length);
    +  });
    +}
    +
    +function testPrefetchThenLoadModuleB() {
    +  moduleManager.prefetchModule('modB');
    +  stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
    +    fail('modA and modB should not be reloaded');
    +  });
    +
    +  testCase.waitForAsync('wait for module B load');
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +    assertEquals('REQUEST_SUCCESS',
    +        2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
    +    assertArrayEquals(
    +        ['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
    +    assertArrayEquals(
    +        ['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
    +    assertEquals('REQUEST_ERROR',
    +        0, observer.getEvents(EventType.REQUEST_ERROR).length);
    +  });
    +}
    +
    +function testPrefetchModuleAThenLoadModuleB() {
    +  moduleManager.prefetchModule('modA');
    +
    +  testCase.waitForAsync('wait for module A load');
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +    assertEquals('REQUEST_SUCCESS',
    +        2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
    +    assertArrayEquals(
    +        ['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
    +    assertArrayEquals(
    +        ['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
    +    assertEquals('REQUEST_ERROR',
    +        0, observer.getEvents(EventType.REQUEST_ERROR).length);
    +  });
    +}
    +
    +function testLoadModuleBThenPrefetchModuleA() {
    +  testCase.waitForAsync('wait for module A load');
    +  moduleManager.execOnLoad('modB', function() {
    +    testCase.continueTesting();
    +    assertLoaded('modA');
    +    assertLoaded('modB');
    +    assertEquals('REQUEST_SUCCESS',
    +        2, observer.getEvents(EventType.REQUEST_SUCCESS).length);
    +    assertArrayEquals(
    +        ['modA'], observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
    +    assertArrayEquals(
    +        ['modB'], observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
    +    assertEquals('REQUEST_ERROR',
    +        0, observer.getEvents(EventType.REQUEST_ERROR).length);
    +    assertThrows('Module load already requested: modB',
    +        function() {
    +          moduleManager.prefetchModule('modA');
    +        });
    +  });
    +}
    +
    +function testPrefetchModuleWithBatchModeEnabled() {
    +  moduleManager.setBatchModeEnabled(true);
    +  assertThrows('Modules prefetching is not supported in batch mode',
    +      function() {
    +        moduleManager.prefetchModule('modA');
    +      });
    +}
    +
    +function testLoadErrorCallbackExecutedWhenPrefetchFails() {
    +  // Make all XHRs throw an error, so that we test the error-handling
    +  // functionality.
    +  var oldXmlHttp = goog.net.XmlHttp;
    +  stubs.set(goog.net, 'XmlHttp', function() {
    +    return {
    +      open: goog.functions.error('mock error'),
    +      abort: goog.nullFunction
    +    };
    +  });
    +  goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
    +
    +  var errorCount = 0;
    +  var errorHandler = function() {
    +    errorCount++;
    +  };
    +  moduleManager.registerCallback(
    +      goog.module.ModuleManager.CallbackType.ERROR,
    +      errorHandler);
    +
    +  moduleLoader.prefetchModule('modA', moduleManager.moduleInfoMap_['modA']);
    +  moduleLoader.loadModules(['modA'], moduleManager.moduleInfoMap_,
    +      function() {
    +        fail('modA should not load successfully');
    +      }, errorHandler);
    +
    +  assertEquals(1, errorCount);
    +}
    +
    +function assertLoaded(id) {
    +  assertTrue(moduleManager.getModuleInfo(id).isLoaded());
    +}
    +
    +function assertNotLoaded(id) {
    +  assertFalse(moduleManager.getModuleInfo(id).isLoaded());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/module/modulemanager.js b/mapbox-gl/closure-library/closure/goog/module/modulemanager.js
    new file mode 100644
    index 0000000000..58723b1b39
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/modulemanager.js
    @@ -0,0 +1,1358 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A singleton object for managing Javascript code modules.
    + *
    + */
    +
    +goog.provide('goog.module.ModuleManager');
    +goog.provide('goog.module.ModuleManager.CallbackType');
    +goog.provide('goog.module.ModuleManager.FailureType');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.debug.Trace');
    +/** @suppress {extraRequire} */
    +goog.require('goog.dispose');
    +goog.require('goog.log');
    +/** @suppress {extraRequire} */
    +goog.require('goog.module');
    +goog.require('goog.module.ModuleInfo');
    +goog.require('goog.module.ModuleLoadCallback');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * The ModuleManager keeps track of all modules in the environment.
    + * Since modules may not have their code loaded, we must keep track of them.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @struct
    + * @suppress {checkStructDictInheritance}
    + */
    +goog.module.ModuleManager = function() {
    +  goog.module.ModuleManager.base(this, 'constructor');
    +
    +  /**
    +   * A mapping from module id to ModuleInfo object.
    +   * @private {Object<string, !goog.module.ModuleInfo>}
    +   */
    +  this.moduleInfoMap_ = {};
    +
    +  // TODO (malteubl): Switch this to a reentrant design.
    +  /**
    +   * The ids of the currently loading modules. If batch mode is disabled, then
    +   * this array will never contain more than one element at a time.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.loadingModuleIds_ = [];
    +
    +  /**
    +   * The requested ids of the currently loading modules. This does not include
    +   * module dependencies that may also be loading.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.requestedLoadingModuleIds_ = [];
    +
    +  // TODO(user): Make these and other arrays that are used as sets be
    +  // actual sets.
    +  /**
    +   * All module ids that have ever been requested. In concurrent loading these
    +   * are the ones to subtract from future requests.
    +   * @type {!Array<string>}
    +   * @private
    +   */
    +  this.requestedModuleIds_ = [];
    +
    +  /**
    +   * A queue of the ids of requested but not-yet-loaded modules. The zero
    +   * position is the front of the queue. This is a 2-D array to group modules
    +   * together with other modules that should be batch loaded with them, if
    +   * batch loading is enabled.
    +   * @type {Array<Array<string>>}
    +   * @private
    +   */
    +  this.requestedModuleIdsQueue_ = [];
    +
    +  /**
    +   * The ids of the currently loading modules which have been initiated by user
    +   * actions.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.userInitiatedLoadingModuleIds_ = [];
    +
    +  /**
    +   * A map of callback types to the functions to call for the specified
    +   * callback type.
    +   * @type {Object<goog.module.ModuleManager.CallbackType, Array<Function>>}
    +   * @private
    +   */
    +  this.callbackMap_ = {};
    +
    +  /**
    +   * Module info for the base module (the one that contains the module
    +   * manager code), which we set as the loading module so one can
    +   * register initialization callbacks in the base module.
    +   *
    +   * The base module is considered loaded when #setAllModuleInfo is called or
    +   * #setModuleContext is called, whichever comes first.
    +   *
    +   * @type {goog.module.ModuleInfo}
    +   * @private
    +   */
    +  this.baseModuleInfo_ = new goog.module.ModuleInfo([], '');
    +
    +  /**
    +   * The module that is currently loading, or null if not loading anything.
    +   * @type {goog.module.ModuleInfo}
    +   * @private
    +   */
    +  this.currentlyLoadingModule_ = this.baseModuleInfo_;
    +
    +  /**
    +   * The id of the last requested initial module. When it loaded
    +   * the deferred in {@code this.initialModulesLoaded_} resolves.
    +   * @private {?string}
    +   */
    +  this.lastInitialModuleId_ = null;
    +
    +  /**
    +   * Deferred for when all initial modules have loaded. We currently block
    +   * sending additional module requests until this deferred resolves. In a
    +   * future optimization it may be possible to use the initial modules as
    +   * seeds for the module loader "requested module ids" and start making new
    +   * requests even sooner.
    +   * @private {!goog.async.Deferred}
    +   */
    +  this.initialModulesLoaded_ = new goog.async.Deferred();
    +
    +  /**
    +   * A logger.
    +   * @private {goog.log.Logger}
    +   */
    +  this.logger_ = goog.log.getLogger('goog.module.ModuleManager');
    +
    +  /**
    +   * Whether the batch mode (i.e. the loading of multiple modules with just one
    +   * request) has been enabled.
    +   * @private {boolean}
    +   */
    +  this.batchModeEnabled_ = false;
    +
    +  /**
    +   * Whether the module requests may be sent out of order.
    +   * @private {boolean}
    +   */
    +  this.concurrentLoadingEnabled_ = false;
    +
    +  /**
    +   * A loader for the modules that implements loadModules(ids, moduleInfoMap,
    +   * opt_successFn, opt_errorFn, opt_timeoutFn, opt_forceReload) method.
    +   * @private {goog.module.AbstractModuleLoader}
    +   */
    +  this.loader_ = null;
    +
    +  // TODO(user): Remove tracer.
    +  /**
    +   * Tracer that measures how long it takes to load a module.
    +   * @private {?number}
    +   */
    +  this.loadTracer_ = null;
    +
    +  /**
    +   * The number of consecutive failures that have happened upon module load
    +   * requests.
    +   * @private {number}
    +   */
    +  this.consecutiveFailures_ = 0;
    +
    +  /**
    +   * Determines if the module manager was just active before the processing of
    +   * the last data.
    +   * @private {boolean}
    +   */
    +  this.lastActive_ = false;
    +
    +  /**
    +   * Determines if the module manager was just user active before the processing
    +   * of the last data. The module manager is user active if any of the
    +   * user-initiated modules are loading or queued up to load.
    +   * @private {boolean}
    +   */
    +  this.userLastActive_ = false;
    +
    +  /**
    +   * The module context needed for module initialization.
    +   * @private {Object}
    +   */
    +  this.moduleContext_ = null;
    +};
    +goog.inherits(goog.module.ModuleManager, goog.Disposable);
    +goog.addSingletonGetter(goog.module.ModuleManager);
    +
    +
    +/**
    +* The type of callbacks that can be registered with the module manager,.
    +* @enum {string}
    +*/
    +goog.module.ModuleManager.CallbackType = {
    +  /**
    +   * Fired when an error has occurred.
    +   */
    +  ERROR: 'error',
    +
    +  /**
    +   * Fired when it becomes idle and has no more module loads to process.
    +   */
    +  IDLE: 'idle',
    +
    +  /**
    +   * Fired when it becomes active and has module loads to process.
    +   */
    +  ACTIVE: 'active',
    +
    +  /**
    +   * Fired when it becomes idle and has no more user-initiated module loads to
    +   * process.
    +   */
    +  USER_IDLE: 'userIdle',
    +
    +  /**
    +   * Fired when it becomes active and has user-initiated module loads to
    +   * process.
    +   */
    +  USER_ACTIVE: 'userActive'
    +};
    +
    +
    +/**
    + * A non-HTTP status code indicating a corruption in loaded module.
    + * This should be used by a ModuleLoader as a replacement for the HTTP code
    + * given to the error handler function to indicated that the module was
    + * corrupted.
    + * This will set the forceReload flag on the loadModules method when retrying
    + * module loading.
    + * @type {number}
    + */
    +goog.module.ModuleManager.CORRUPT_RESPONSE_STATUS_CODE = 8001;
    +
    +
    +/**
    + * Sets the batch mode as enabled or disabled for the module manager.
    + * @param {boolean} enabled Whether the batch mode is to be enabled or not.
    + */
    +goog.module.ModuleManager.prototype.setBatchModeEnabled = function(
    +    enabled) {
    +  this.batchModeEnabled_ = enabled;
    +};
    +
    +
    +/**
    + * Sets the concurrent loading mode as enabled or disabled for the module
    + * manager. Requires a moduleloader implementation that supports concurrent
    + * loads. The default {@see goog.module.ModuleLoader} does not.
    + * @param {boolean} enabled
    + */
    +goog.module.ModuleManager.prototype.setConcurrentLoadingEnabled = function(
    +    enabled) {
    +  this.concurrentLoadingEnabled_ = enabled;
    +};
    +
    +
    +/**
    + * Sets the module info for all modules. Should only be called once.
    + *
    + * @param {Object<Array<string>>} infoMap An object that contains a mapping
    + *    from module id (String) to list of required module ids (Array).
    + */
    +goog.module.ModuleManager.prototype.setAllModuleInfo = function(infoMap) {
    +  for (var id in infoMap) {
    +    this.moduleInfoMap_[id] = new goog.module.ModuleInfo(infoMap[id], id);
    +  }
    +  if (!this.initialModulesLoaded_.hasFired()) {
    +    this.initialModulesLoaded_.callback();
    +  }
    +  this.maybeFinishBaseLoad_();
    +};
    +
    +
    +/**
    + * Sets the module info for all modules. Should only be called once. Also
    + * marks modules that are currently being loaded.
    + *
    + * @param {string=} opt_info A string representation of the module dependency
    + *      graph, in the form: module1:dep1,dep2/module2:dep1,dep2 etc.
    + *     Where depX is the base-36 encoded position of the dep in the module list.
    + * @param {Array<string>=} opt_loadingModuleIds A list of moduleIds that
    + *     are currently being loaded.
    + */
    +goog.module.ModuleManager.prototype.setAllModuleInfoString = function(
    +    opt_info, opt_loadingModuleIds) {
    +  if (!goog.isString(opt_info)) {
    +    // The call to this method is generated in two steps, the argument is added
    +    // after some of the compilation passes.  This means that the initial code
    +    // doesn't have any arguments and causes compiler errors.  We make it
    +    // optional to satisfy this constraint.
    +    return;
    +  }
    +
    +  var modules = opt_info.split('/');
    +  var moduleIds = [];
    +
    +  // Split the string into the infoMap of id->deps
    +  for (var i = 0; i < modules.length; i++) {
    +    var parts = modules[i].split(':');
    +    var id = parts[0];
    +    var deps;
    +    if (parts[1]) {
    +      deps = parts[1].split(',');
    +      for (var j = 0; j < deps.length; j++) {
    +        var index = parseInt(deps[j], 36);
    +        goog.asserts.assert(
    +            moduleIds[index], 'No module @ %s, dep of %s @ %s', index, id, i);
    +        deps[j] = moduleIds[index];
    +      }
    +    } else {
    +      deps = [];
    +    }
    +    moduleIds.push(id);
    +    this.moduleInfoMap_[id] = new goog.module.ModuleInfo(deps, id);
    +  }
    +  if (opt_loadingModuleIds && opt_loadingModuleIds.length) {
    +    goog.array.extend(this.loadingModuleIds_, opt_loadingModuleIds);
    +    // The last module in the list of initial modules. When it has loaded all
    +    // initial modules have loaded.
    +    this.lastInitialModuleId_ = /** @type {?string}  */ (
    +        goog.array.peek(opt_loadingModuleIds));
    +  } else {
    +    if (!this.initialModulesLoaded_.hasFired()) {
    +      this.initialModulesLoaded_.callback();
    +    }
    +  }
    +  this.maybeFinishBaseLoad_();
    +};
    +
    +
    +/**
    + * Gets a module info object by id.
    + * @param {string} id A module identifier.
    + * @return {!goog.module.ModuleInfo} The module info.
    + */
    +goog.module.ModuleManager.prototype.getModuleInfo = function(id) {
    +  return this.moduleInfoMap_[id];
    +};
    +
    +
    +/**
    + * Sets the module uris.
    + *
    + * @param {Object} moduleUriMap The map of id/uris pairs for each module.
    + */
    +goog.module.ModuleManager.prototype.setModuleUris = function(moduleUriMap) {
    +  for (var id in moduleUriMap) {
    +    this.moduleInfoMap_[id].setUris(moduleUriMap[id]);
    +  }
    +};
    +
    +
    +/**
    + * Gets the application-specific module loader.
    + * @return {goog.module.AbstractModuleLoader} An object that has a
    + *     loadModules(ids, moduleInfoMap, opt_successFn, opt_errFn,
    + *         opt_timeoutFn, opt_forceReload) method.
    + */
    +goog.module.ModuleManager.prototype.getLoader = function() {
    +  return this.loader_;
    +};
    +
    +
    +/**
    + * Sets the application-specific module loader.
    + * @param {goog.module.AbstractModuleLoader} loader An object that has a
    + *     loadModules(ids, moduleInfoMap, opt_successFn, opt_errFn,
    + *         opt_timeoutFn, opt_forceReload) method.
    + */
    +goog.module.ModuleManager.prototype.setLoader = function(loader) {
    +  this.loader_ = loader;
    +};
    +
    +
    +/**
    + * Gets the module context to use to initialize the module.
    + * @return {Object} The context.
    + */
    +goog.module.ModuleManager.prototype.getModuleContext = function() {
    +  return this.moduleContext_;
    +};
    +
    +
    +/**
    + * Sets the module context to use to initialize the module.
    + * @param {Object} context The context.
    + */
    +goog.module.ModuleManager.prototype.setModuleContext = function(context) {
    +  this.moduleContext_ = context;
    +  this.maybeFinishBaseLoad_();
    +};
    +
    +
    +/**
    + * Determines if the ModuleManager is active
    + * @return {boolean} TRUE iff the ModuleManager is active (i.e., not idle).
    + */
    +goog.module.ModuleManager.prototype.isActive = function() {
    +  return this.loadingModuleIds_.length > 0;
    +};
    +
    +
    +/**
    + * Determines if the ModuleManager is user active
    + * @return {boolean} TRUE iff the ModuleManager is user active (i.e., not idle).
    + */
    +goog.module.ModuleManager.prototype.isUserActive = function() {
    +  return this.userInitiatedLoadingModuleIds_.length > 0;
    +};
    +
    +
    +/**
    + * Dispatches an ACTIVE or IDLE event if necessary.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.dispatchActiveIdleChangeIfNeeded_ =
    +    function() {
    +  var lastActive = this.lastActive_;
    +  var active = this.isActive();
    +  if (active != lastActive) {
    +    this.executeCallbacks_(active ?
    +        goog.module.ModuleManager.CallbackType.ACTIVE :
    +        goog.module.ModuleManager.CallbackType.IDLE);
    +
    +    // Flip the last active value.
    +    this.lastActive_ = active;
    +  }
    +
    +  // Check if the module manager is user active i.e., there are user initiated
    +  // modules being loaded or queued up to be loaded.
    +  var userLastActive = this.userLastActive_;
    +  var userActive = this.isUserActive();
    +  if (userActive != userLastActive) {
    +    this.executeCallbacks_(userActive ?
    +        goog.module.ModuleManager.CallbackType.USER_ACTIVE :
    +        goog.module.ModuleManager.CallbackType.USER_IDLE);
    +
    +    // Flip the last user active value.
    +    this.userLastActive_ = userActive;
    +  }
    +};
    +
    +
    +/**
    + * Preloads a module after a short delay.
    + *
    + * @param {string} id The id of the module to preload.
    + * @param {number=} opt_timeout The number of ms to wait before adding the
    + *     module id to the loading queue (defaults to 0 ms). Note that the module
    + *     will be loaded asynchronously regardless of the value of this parameter.
    + * @return {!goog.async.Deferred} A deferred object.
    + */
    +goog.module.ModuleManager.prototype.preloadModule = function(
    +    id, opt_timeout) {
    +  var d = new goog.async.Deferred();
    +  window.setTimeout(
    +      goog.bind(this.addLoadModule_, this, id, d),
    +      opt_timeout || 0);
    +  return d;
    +};
    +
    +
    +/**
    + * Prefetches a JavaScript module and its dependencies, which means that the
    + * module will be downloaded, but not evaluated. To complete the module load,
    + * the caller should also call load or execOnLoad after prefetching the module.
    + *
    + * @param {string} id The id of the module to prefetch.
    + */
    +goog.module.ModuleManager.prototype.prefetchModule = function(id) {
    +  var moduleInfo = this.getModuleInfo(id);
    +  if (moduleInfo.isLoaded() || this.isModuleLoading(id)) {
    +    throw Error('Module load already requested: ' + id);
    +  } else if (this.batchModeEnabled_) {
    +    throw Error('Modules prefetching is not supported in batch mode');
    +  } else {
    +    var idWithDeps = this.getNotYetLoadedTransitiveDepIds_(id);
    +    for (var i = 0; i < idWithDeps.length; i++) {
    +      this.loader_.prefetchModule(idWithDeps[i],
    +          this.moduleInfoMap_[idWithDeps[i]]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Loads a single module for use with a given deferred.
    + *
    + * @param {string} id The id of the module to load.
    + * @param {goog.async.Deferred} d A deferred object.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.addLoadModule_ = function(id, d) {
    +  var moduleInfo = this.getModuleInfo(id);
    +  if (moduleInfo.isLoaded()) {
    +    d.callback(this.moduleContext_);
    +    return;
    +  }
    +
    +  this.registerModuleLoadCallbacks_(id, moduleInfo, false, d);
    +  if (!this.isModuleLoading(id)) {
    +    this.loadModulesOrEnqueue_([id]);
    +  }
    +};
    +
    +
    +/**
    + * Loads a list of modules or, if some other module is currently being loaded,
    + * appends the ids to the queue of requested module ids. Registers callbacks a
    + * module that is currently loading and returns a fired deferred for a module
    + * that is already loaded.
    + *
    + * @param {Array<string>} ids The id of the module to load.
    + * @param {boolean=} opt_userInitiated If the load is a result of a user action.
    + * @return {!Object<string, !goog.async.Deferred>} A mapping from id (String)
    + *     to deferred objects that will callback or errback when the load for that
    + *     id is finished.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.loadModulesOrEnqueueIfNotLoadedOrLoading_ =
    +    function(ids, opt_userInitiated) {
    +  var uniqueIds = [];
    +  goog.array.removeDuplicates(ids, uniqueIds);
    +  var idsToLoad = [];
    +  var deferredMap = {};
    +  for (var i = 0; i < uniqueIds.length; i++) {
    +    var id = uniqueIds[i];
    +    var moduleInfo = this.getModuleInfo(id);
    +    if (!moduleInfo) {
    +      throw new Error('Unknown module: ' + id);
    +    }
    +    var d = new goog.async.Deferred();
    +    deferredMap[id] = d;
    +    if (moduleInfo.isLoaded()) {
    +      d.callback(this.moduleContext_);
    +    } else {
    +      this.registerModuleLoadCallbacks_(id, moduleInfo, !!opt_userInitiated, d);
    +      if (!this.isModuleLoading(id)) {
    +        idsToLoad.push(id);
    +      }
    +    }
    +  }
    +
    +  // If there are ids to load, load them, otherwise, they are all loading or
    +  // loaded.
    +  if (idsToLoad.length > 0) {
    +    this.loadModulesOrEnqueue_(idsToLoad);
    +  }
    +  return deferredMap;
    +};
    +
    +
    +/**
    + * Registers the callbacks and handles logic if it is a user initiated module
    + * load.
    + *
    + * @param {string} id The id of the module to possibly load.
    + * @param {!goog.module.ModuleInfo} moduleInfo The module identifier for the
    + *     given id.
    + * @param {boolean} userInitiated If the load was user initiated.
    + * @param {goog.async.Deferred} d A deferred object.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.registerModuleLoadCallbacks_ =
    +    function(id, moduleInfo, userInitiated, d) {
    +  moduleInfo.registerCallback(d.callback, d);
    +  moduleInfo.registerErrback(function(err) { d.errback(Error(err)); });
    +  // If it's already loading, we don't have to do anything besides handle
    +  // if it was user initiated
    +  if (this.isModuleLoading(id)) {
    +    if (userInitiated) {
    +      goog.log.info(this.logger_,
    +          'User initiated module already loading: ' + id);
    +      this.addUserInitiatedLoadingModule_(id);
    +      this.dispatchActiveIdleChangeIfNeeded_();
    +    }
    +  } else {
    +    if (userInitiated) {
    +      goog.log.info(this.logger_, 'User initiated module load: ' + id);
    +      this.addUserInitiatedLoadingModule_(id);
    +    } else {
    +      goog.log.info(this.logger_, 'Initiating module load: ' + id);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Initiates loading of a list of modules or, if a module is currently being
    + * loaded, appends the modules to the queue of requested module ids.
    + *
    + * The caller should verify that the requested modules are not already loaded or
    + * loading. {@link #loadModulesOrEnqueueIfNotLoadedOrLoading_} is a more lenient
    + * alternative to this method.
    + *
    + * @param {Array<string>} ids The ids of the modules to load.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.loadModulesOrEnqueue_ = function(ids) {
    +  // With concurrent loading we always just send off the request.
    +  if (this.concurrentLoadingEnabled_) {
    +    // For now we wait for initial modules to have downloaded as this puts the
    +    // loader in a good state for calculating the needed deps of additional
    +    // loads.
    +    // TODO(user): Make this wait unnecessary.
    +    this.initialModulesLoaded_.addCallback(
    +        goog.bind(this.loadModules_, this, ids));
    +  } else {
    +    if (goog.array.isEmpty(this.loadingModuleIds_)) {
    +      this.loadModules_(ids);
    +    } else {
    +      this.requestedModuleIdsQueue_.push(ids);
    +      this.dispatchActiveIdleChangeIfNeeded_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the amount of delay to wait before sending a request for more modules.
    + * If a certain module request fails, we backoff a little bit and try again.
    + * @return {number} Delay, in ms.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.getBackOff_ = function() {
    +  // 5 seconds after one error, 20 seconds after 2.
    +  return Math.pow(this.consecutiveFailures_, 2) * 5000;
    +};
    +
    +
    +/**
    + * Loads a list of modules and any of their not-yet-loaded prerequisites.
    + * If batch mode is enabled, the prerequisites will be loaded together with the
    + * requested modules and all requested modules will be loaded at the same time.
    + *
    + * The caller should verify that the requested modules are not already loaded
    + * and that no modules are currently loading before calling this method.
    + *
    + * @param {Array<string>} ids The ids of the modules to load.
    + * @param {boolean=} opt_isRetry If the load is a retry of a previous load
    + *     attempt.
    + * @param {boolean=} opt_forceReload Whether to bypass cache while loading the
    + *     module.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.loadModules_ = function(
    +    ids, opt_isRetry, opt_forceReload) {
    +  if (!opt_isRetry) {
    +    this.consecutiveFailures_ = 0;
    +  }
    +
    +  // Not all modules may be loaded immediately if batch mode is not enabled.
    +  var idsToLoadImmediately = this.processModulesForLoad_(ids);
    +
    +  goog.log.info(this.logger_, 'Loading module(s): ' + idsToLoadImmediately);
    +  this.loadingModuleIds_ = idsToLoadImmediately;
    +
    +  if (this.batchModeEnabled_) {
    +    this.requestedLoadingModuleIds_ = ids;
    +  } else {
    +    // If batch mode is disabled, we treat each dependency load as a separate
    +    // load.
    +    this.requestedLoadingModuleIds_ = goog.array.clone(idsToLoadImmediately);
    +  }
    +
    +  // Dispatch an active/idle change if needed.
    +  this.dispatchActiveIdleChangeIfNeeded_();
    +
    +  if (goog.array.isEmpty(idsToLoadImmediately)) {
    +    // All requested modules and deps have been either loaded already or have
    +    // already been requested.
    +    return;
    +  }
    +
    +  this.requestedModuleIds_.push.apply(this.requestedModuleIds_,
    +      idsToLoadImmediately);
    +
    +  var loadFn = goog.bind(this.loader_.loadModules, this.loader_,
    +      goog.array.clone(idsToLoadImmediately),
    +      this.moduleInfoMap_,
    +      null,
    +      goog.bind(this.handleLoadError_, this, this.requestedLoadingModuleIds_,
    +          idsToLoadImmediately),
    +      goog.bind(this.handleLoadTimeout_, this),
    +      !!opt_forceReload);
    +
    +  var delay = this.getBackOff_();
    +  if (delay) {
    +    window.setTimeout(loadFn, delay);
    +  } else {
    +    loadFn();
    +  }
    +};
    +
    +
    +/**
    + * Processes a list of module ids for loading. Checks if any of the modules are
    + * already loaded and then gets transitive deps. Queues any necessary modules
    + * if batch mode is not enabled. Returns the list of ids that should be loaded.
    + *
    + * @param {Array<string>} ids The ids that need to be loaded.
    + * @return {!Array<string>} The ids to load, including dependencies.
    + * @throws {Error} If the module is already loaded.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.processModulesForLoad_ = function(ids) {
    +  for (var i = 0; i < ids.length; i++) {
    +    var moduleInfo = this.moduleInfoMap_[ids[i]];
    +    if (moduleInfo.isLoaded()) {
    +      throw Error('Module already loaded: ' + ids[i]);
    +    }
    +  }
    +
    +  // Build a list of the ids of this module and any of its not-yet-loaded
    +  // prerequisite modules in dependency order.
    +  var idsWithDeps = [];
    +  for (var i = 0; i < ids.length; i++) {
    +    idsWithDeps = idsWithDeps.concat(
    +        this.getNotYetLoadedTransitiveDepIds_(ids[i]));
    +  }
    +  goog.array.removeDuplicates(idsWithDeps);
    +
    +  if (!this.batchModeEnabled_ && idsWithDeps.length > 1) {
    +    var idToLoad = idsWithDeps.shift();
    +    goog.log.info(this.logger_,
    +        'Must load ' + idToLoad + ' module before ' + ids);
    +
    +    // Insert the requested module id and any other not-yet-loaded prereqs
    +    // that it has at the front of the queue.
    +    var queuedModules = goog.array.map(idsWithDeps, function(id) {
    +      return [id];
    +    });
    +    this.requestedModuleIdsQueue_ = queuedModules.concat(
    +        this.requestedModuleIdsQueue_);
    +    return [idToLoad];
    +  } else {
    +    return idsWithDeps;
    +  }
    +};
    +
    +
    +/**
    + * Builds a list of the ids of the not-yet-loaded modules that a particular
    + * module transitively depends on, including itself.
    + *
    + * @param {string} id The id of a not-yet-loaded module.
    + * @return {!Array<string>} An array of module ids in dependency order that's
    + *     guaranteed to end with the provided module id.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.getNotYetLoadedTransitiveDepIds_ =
    +    function(id) {
    +  // NOTE(user): We want the earliest occurrance of a module, not the first
    +  // dependency we find. Therefore we strip duplicates at the end rather than
    +  // during.  See the tests for concrete examples.
    +  var ids = [];
    +  if (!goog.array.contains(this.requestedModuleIds_, id)) {
    +    ids.push(id);
    +  }
    +  var depIds = goog.array.clone(this.getModuleInfo(id).getDependencies());
    +  while (depIds.length) {
    +    var depId = depIds.pop();
    +    if (!this.getModuleInfo(depId).isLoaded() &&
    +        !goog.array.contains(this.requestedModuleIds_, depId)) {
    +      ids.unshift(depId);
    +      // We need to process direct dependencies first.
    +      Array.prototype.unshift.apply(depIds,
    +          this.getModuleInfo(depId).getDependencies());
    +    }
    +  }
    +  goog.array.removeDuplicates(ids);
    +  return ids;
    +};
    +
    +
    +/**
    + * If we are still loading the base module, consider the load complete.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.maybeFinishBaseLoad_ = function() {
    +  if (this.currentlyLoadingModule_ == this.baseModuleInfo_) {
    +    this.currentlyLoadingModule_ = null;
    +    var error = this.baseModuleInfo_.onLoad(
    +        goog.bind(this.getModuleContext, this));
    +    if (error) {
    +      this.dispatchModuleLoadFailed_(
    +          goog.module.ModuleManager.FailureType.INIT_ERROR);
    +    }
    +
    +    this.dispatchActiveIdleChangeIfNeeded_();
    +  }
    +};
    +
    +
    +/**
    + * Records that a module was loaded. Also initiates loading the next module if
    + * any module requests are queued. This method is called by code that is
    + * generated and appended to each dynamic module's code at compilation time.
    + *
    + * @param {string} id A module id.
    + */
    +goog.module.ModuleManager.prototype.setLoaded = function(id) {
    +  if (this.isDisposed()) {
    +    goog.log.warning(this.logger_,
    +        'Module loaded after module manager was disposed: ' + id);
    +    return;
    +  }
    +
    +  goog.log.info(this.logger_, 'Module loaded: ' + id);
    +
    +  var error = this.moduleInfoMap_[id].onLoad(
    +      goog.bind(this.getModuleContext, this));
    +  if (error) {
    +    this.dispatchModuleLoadFailed_(
    +        goog.module.ModuleManager.FailureType.INIT_ERROR);
    +  }
    +
    +  // Remove the module id from the user initiated set if it existed there.
    +  goog.array.remove(this.userInitiatedLoadingModuleIds_, id);
    +
    +  // Remove the module id from the loading modules if it exists there.
    +  goog.array.remove(this.loadingModuleIds_, id);
    +
    +  if (goog.array.isEmpty(this.loadingModuleIds_)) {
    +    // No more modules are currently being loaded (e.g. arriving later in the
    +    // same HTTP response), so proceed to load the next module in the queue.
    +    this.loadNextModules_();
    +  }
    +
    +  if (this.lastInitialModuleId_ && id == this.lastInitialModuleId_) {
    +    if (!this.initialModulesLoaded_.hasFired()) {
    +      this.initialModulesLoaded_.callback();
    +    }
    +  }
    +
    +  // Dispatch an active/idle change if needed.
    +  this.dispatchActiveIdleChangeIfNeeded_();
    +};
    +
    +
    +/**
    + * Gets whether a module is currently loading or in the queue, waiting to be
    + * loaded.
    + * @param {string} id A module id.
    + * @return {boolean} TRUE iff the module is loading.
    + */
    +goog.module.ModuleManager.prototype.isModuleLoading = function(id) {
    +  if (goog.array.contains(this.loadingModuleIds_, id)) {
    +    return true;
    +  }
    +  for (var i = 0; i < this.requestedModuleIdsQueue_.length; i++) {
    +    if (goog.array.contains(this.requestedModuleIdsQueue_[i], id)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Requests that a function be called once a particular module is loaded.
    + * Client code can use this method to safely call into modules that may not yet
    + * be loaded. For consistency, this method always calls the function
    + * asynchronously -- even if the module is already loaded. Initiates loading of
    + * the module if necessary, unless opt_noLoad is true.
    + *
    + * @param {string} moduleId A module id.
    + * @param {Function} fn Function to execute when the module has loaded.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + * @param {boolean=} opt_noLoad TRUE iff not to initiate loading of the module.
    + * @param {boolean=} opt_userInitiated TRUE iff the loading of the module was
    + *     user initiated.
    + * @param {boolean=} opt_preferSynchronous TRUE iff the function should be
    + *     executed synchronously if the module has already been loaded.
    + * @return {!goog.module.ModuleLoadCallback} A callback wrapper that exposes
    + *     an abort and execute method.
    + */
    +goog.module.ModuleManager.prototype.execOnLoad = function(
    +    moduleId, fn, opt_handler, opt_noLoad,
    +    opt_userInitiated, opt_preferSynchronous) {
    +  var moduleInfo = this.moduleInfoMap_[moduleId];
    +  var callbackWrapper;
    +
    +  if (moduleInfo.isLoaded()) {
    +    goog.log.info(this.logger_, moduleId + ' module already loaded');
    +    // Call async so that code paths don't change between loaded and unloaded
    +    // cases.
    +    callbackWrapper = new goog.module.ModuleLoadCallback(fn, opt_handler);
    +    if (opt_preferSynchronous) {
    +      callbackWrapper.execute(this.moduleContext_);
    +    } else {
    +      window.setTimeout(
    +          goog.bind(callbackWrapper.execute, callbackWrapper), 0);
    +    }
    +  } else if (this.isModuleLoading(moduleId)) {
    +    goog.log.info(this.logger_, moduleId + ' module already loading');
    +    callbackWrapper = moduleInfo.registerCallback(fn, opt_handler);
    +    if (opt_userInitiated) {
    +      goog.log.info(this.logger_,
    +          'User initiated module already loading: ' + moduleId);
    +      this.addUserInitiatedLoadingModule_(moduleId);
    +      this.dispatchActiveIdleChangeIfNeeded_();
    +    }
    +  } else {
    +    goog.log.info(this.logger_,
    +        'Registering callback for module: ' + moduleId);
    +    callbackWrapper = moduleInfo.registerCallback(fn, opt_handler);
    +    if (!opt_noLoad) {
    +      if (opt_userInitiated) {
    +        goog.log.info(this.logger_, 'User initiated module load: ' + moduleId);
    +        this.addUserInitiatedLoadingModule_(moduleId);
    +      }
    +      goog.log.info(this.logger_, 'Initiating module load: ' + moduleId);
    +      this.loadModulesOrEnqueue_([moduleId]);
    +    }
    +  }
    +  return callbackWrapper;
    +};
    +
    +
    +/**
    + * Loads a module, returning a goog.async.Deferred for keeping track of the
    + * result.
    + *
    + * @param {string} moduleId A module id.
    + * @param {boolean=} opt_userInitiated If the load is a result of a user action.
    + * @return {goog.async.Deferred} A deferred object.
    + */
    +goog.module.ModuleManager.prototype.load = function(
    +    moduleId, opt_userInitiated) {
    +  return this.loadModulesOrEnqueueIfNotLoadedOrLoading_(
    +      [moduleId], opt_userInitiated)[moduleId];
    +};
    +
    +
    +/**
    + * Loads a list of modules, returning a goog.async.Deferred for keeping track of
    + * the result.
    + *
    + * @param {Array<string>} moduleIds A list of module ids.
    + * @param {boolean=} opt_userInitiated If the load is a result of a user action.
    + * @return {!Object<string, !goog.async.Deferred>} A mapping from id (String)
    + *     to deferred objects that will callback or errback when the load for that
    + *     id is finished.
    + */
    +goog.module.ModuleManager.prototype.loadMultiple = function(
    +    moduleIds, opt_userInitiated) {
    +  return this.loadModulesOrEnqueueIfNotLoadedOrLoading_(
    +      moduleIds, opt_userInitiated);
    +};
    +
    +
    +/**
    + * Ensures that the module with the given id is listed as a user-initiated
    + * module that is being loaded. This method guarantees that a module will never
    + * get listed more than once.
    + * @param {string} id Identifier of the module.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.addUserInitiatedLoadingModule_ = function(
    +    id) {
    +  if (!goog.array.contains(this.userInitiatedLoadingModuleIds_, id)) {
    +    this.userInitiatedLoadingModuleIds_.push(id);
    +  }
    +};
    +
    +
    +/**
    + * Method called just before a module code is loaded.
    + * @param {string} id Identifier of the module.
    + */
    +goog.module.ModuleManager.prototype.beforeLoadModuleCode = function(id) {
    +  this.loadTracer_ = goog.debug.Trace.startTracer('Module Load: ' + id,
    +      'Module Load');
    +  if (this.currentlyLoadingModule_) {
    +    goog.log.error(this.logger_,
    +        'beforeLoadModuleCode called with module "' + id +
    +        '" while module "' +
    +        this.currentlyLoadingModule_.getId() +
    +        '" is loading');
    +  }
    +  this.currentlyLoadingModule_ = this.getModuleInfo(id);
    +};
    +
    +
    +/**
    + * Method called just after module code is loaded
    + * @param {string} id Identifier of the module.
    + */
    +goog.module.ModuleManager.prototype.afterLoadModuleCode = function(id) {
    +  if (!this.currentlyLoadingModule_ ||
    +      id != this.currentlyLoadingModule_.getId()) {
    +    goog.log.error(this.logger_,
    +        'afterLoadModuleCode called with module "' + id +
    +        '" while loading module "' +
    +        (this.currentlyLoadingModule_ &&
    +        this.currentlyLoadingModule_.getId()) + '"');
    +
    +  }
    +  this.currentlyLoadingModule_ = null;
    +  goog.debug.Trace.stopTracer(this.loadTracer_);
    +};
    +
    +
    +/**
    + * Register an initialization callback for the currently loading module. This
    + * should only be called by script that is executed during the evaluation of
    + * a module's javascript. This is almost equivalent to calling the function
    + * inline, but ensures that all the code from the currently loading module
    + * has been loaded. This makes it cleaner and more robust than calling the
    + * function inline.
    + *
    + * If this function is called from the base module (the one that contains
    + * the module manager code), the callback is held until #setAllModuleInfo
    + * is called, or until #setModuleContext is called, whichever happens first.
    + *
    + * @param {Function} fn A callback function that takes a single argument
    + *    which is the module context.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + */
    +goog.module.ModuleManager.prototype.registerInitializationCallback = function(
    +    fn, opt_handler) {
    +  if (!this.currentlyLoadingModule_) {
    +    goog.log.error(this.logger_, 'No module is currently loading');
    +  } else {
    +    this.currentlyLoadingModule_.registerEarlyCallback(fn, opt_handler);
    +  }
    +};
    +
    +
    +/**
    + * Register a late initialization callback for the currently loading module.
    + * Callbacks registered via this function are executed similar to
    + * {@see registerInitializationCallback}, but they are fired after all
    + * initialization callbacks are called.
    + *
    + * @param {Function} fn A callback function that takes a single argument
    + *    which is the module context.
    + * @param {Object=} opt_handler Optional handler under whose scope to execute
    + *     the callback.
    + */
    +goog.module.ModuleManager.prototype.registerLateInitializationCallback =
    +    function(fn, opt_handler) {
    +  if (!this.currentlyLoadingModule_) {
    +    goog.log.error(this.logger_, 'No module is currently loading');
    +  } else {
    +    this.currentlyLoadingModule_.registerCallback(fn, opt_handler);
    +  }
    +};
    +
    +
    +/**
    + * Sets the constructor to use for the module object for the currently
    + * loading module. The constructor should derive from {@see
    + * goog.module.BaseModule}.
    + * @param {Function} fn The constructor function.
    + */
    +goog.module.ModuleManager.prototype.setModuleConstructor = function(fn) {
    +  if (!this.currentlyLoadingModule_) {
    +    goog.log.error(this.logger_, 'No module is currently loading');
    +    return;
    +  }
    +  this.currentlyLoadingModule_.setModuleConstructor(fn);
    +};
    +
    +
    +/**
    + * The possible reasons for a module load failure callback being fired.
    + * @enum {number}
    + */
    +goog.module.ModuleManager.FailureType = {
    +  /** 401 Status. */
    +  UNAUTHORIZED: 0,
    +
    +  /** Error status (not 401) returned multiple times. */
    +  CONSECUTIVE_FAILURES: 1,
    +
    +  /** Request timeout. */
    +  TIMEOUT: 2,
    +
    +  /** 410 status, old code gone. */
    +  OLD_CODE_GONE: 3,
    +
    +  /** The onLoad callbacks failed. */
    +  INIT_ERROR: 4
    +};
    +
    +
    +/**
    + * Handles a module load failure.
    + *
    + * @param {!Array<string>} requestedLoadingModuleIds Modules ids that were
    + *     requested in failed request. Does not included calculated dependencies.
    + * @param {!Array<string>} requestedModuleIdsWithDeps All module ids requested
    + *     in the failed request including all dependencies.
    + * @param {?number} status The error status.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.handleLoadError_ = function(
    +    requestedLoadingModuleIds, requestedModuleIdsWithDeps, status) {
    +  this.consecutiveFailures_++;
    +  // Module manager was not designed to be reentrant. Reinstate the instance
    +  // var with actual value when request failed (Other requests may have
    +  // started already.)
    +  this.requestedLoadingModuleIds_ = requestedLoadingModuleIds;
    +  // Pretend we never requested the failed modules.
    +  goog.array.forEach(requestedModuleIdsWithDeps,
    +      goog.partial(goog.array.remove, this.requestedModuleIds_), this);
    +
    +  if (status == 401) {
    +    // The user is not logged in. They've cleared their cookies or logged out
    +    // from another window.
    +    goog.log.info(this.logger_, 'Module loading unauthorized');
    +    this.dispatchModuleLoadFailed_(
    +        goog.module.ModuleManager.FailureType.UNAUTHORIZED);
    +    // Drop any additional module requests.
    +    this.requestedModuleIdsQueue_.length = 0;
    +  } else if (status == 410) {
    +    // The requested module js is old and not available.
    +    this.requeueBatchOrDispatchFailure_(
    +        goog.module.ModuleManager.FailureType.OLD_CODE_GONE);
    +    this.loadNextModules_();
    +  } else if (this.consecutiveFailures_ >= 3) {
    +    goog.log.info(this.logger_, 'Aborting after failure to load: ' +
    +                      this.loadingModuleIds_);
    +    this.requeueBatchOrDispatchFailure_(
    +        goog.module.ModuleManager.FailureType.CONSECUTIVE_FAILURES);
    +    this.loadNextModules_();
    +  } else {
    +    goog.log.info(this.logger_, 'Retrying after failure to load: ' +
    +                      this.loadingModuleIds_);
    +    var forceReload =
    +        status == goog.module.ModuleManager.CORRUPT_RESPONSE_STATUS_CODE;
    +    this.loadModules_(this.requestedLoadingModuleIds_, true, forceReload);
    +  }
    +};
    +
    +
    +/**
    + * Handles a module load timeout.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.handleLoadTimeout_ = function() {
    +  goog.log.info(this.logger_,
    +      'Aborting after timeout: ' + this.loadingModuleIds_);
    +  this.requeueBatchOrDispatchFailure_(
    +      goog.module.ModuleManager.FailureType.TIMEOUT);
    +  this.loadNextModules_();
    +};
    +
    +
    +/**
    + * Requeues batch loads that had more than one requested module
    + * (i.e. modules that were not included as dependencies) as separate loads or
    + * if there was only one requested module, fails that module with the received
    + * cause.
    + * @param {goog.module.ModuleManager.FailureType} cause The reason for the
    + *     failure.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.requeueBatchOrDispatchFailure_ =
    +    function(cause) {
    +  // The load failed, so if there are more than one requested modules, then we
    +  // need to retry each one as a separate load. Otherwise, if there is only one
    +  // requested module, remove it and its dependencies from the queue.
    +  if (this.requestedLoadingModuleIds_.length > 1) {
    +    var queuedModules = goog.array.map(this.requestedLoadingModuleIds_,
    +        function(id) {
    +          return [id];
    +        });
    +    this.requestedModuleIdsQueue_ = queuedModules.concat(
    +        this.requestedModuleIdsQueue_);
    +  } else {
    +    this.dispatchModuleLoadFailed_(cause);
    +  }
    +};
    +
    +
    +/**
    + * Handles when a module load failed.
    + * @param {goog.module.ModuleManager.FailureType} cause The reason for the
    + *     failure.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.dispatchModuleLoadFailed_ = function(
    +    cause) {
    +  var failedIds = this.requestedLoadingModuleIds_;
    +  this.loadingModuleIds_.length = 0;
    +  // If any pending modules depend on the id that failed,
    +  // they need to be removed from the queue.
    +  var idsToCancel = [];
    +  for (var i = 0; i < this.requestedModuleIdsQueue_.length; i++) {
    +    var dependentModules = goog.array.filter(
    +        this.requestedModuleIdsQueue_[i],
    +        /**
    +         * Returns true if the requestedId has dependencies on the modules that
    +         * just failed to load.
    +         * @param {string} requestedId The module to check for dependencies.
    +         * @return {boolean} True if the module depends on failed modules.
    +         */
    +        function(requestedId) {
    +          var requestedDeps = this.getNotYetLoadedTransitiveDepIds_(
    +              requestedId);
    +          return goog.array.some(failedIds, function(id) {
    +            return goog.array.contains(requestedDeps, id);
    +          });
    +        }, this);
    +    goog.array.extend(idsToCancel, dependentModules);
    +  }
    +
    +  // Also insert the ids that failed to load as ids to cancel.
    +  for (var i = 0; i < failedIds.length; i++) {
    +    goog.array.insert(idsToCancel, failedIds[i]);
    +  }
    +
    +  // Remove ids to cancel from the queues.
    +  for (var i = 0; i < idsToCancel.length; i++) {
    +    for (var j = 0; j < this.requestedModuleIdsQueue_.length; j++) {
    +      goog.array.remove(this.requestedModuleIdsQueue_[j], idsToCancel[i]);
    +    }
    +    goog.array.remove(this.userInitiatedLoadingModuleIds_, idsToCancel[i]);
    +  }
    +
    +  // Call the functions for error notification.
    +  var errorCallbacks = this.callbackMap_[
    +      goog.module.ModuleManager.CallbackType.ERROR];
    +  if (errorCallbacks) {
    +    for (var i = 0; i < errorCallbacks.length; i++) {
    +      var callback = errorCallbacks[i];
    +      for (var j = 0; j < idsToCancel.length; j++) {
    +        callback(goog.module.ModuleManager.CallbackType.ERROR, idsToCancel[j],
    +            cause);
    +      }
    +    }
    +  }
    +
    +  // Call the errbacks on the module info.
    +  for (var i = 0; i < failedIds.length; i++) {
    +    if (this.moduleInfoMap_[failedIds[i]]) {
    +      this.moduleInfoMap_[failedIds[i]].onError(cause);
    +    }
    +  }
    +
    +  // Clear the requested loading module ids.
    +  this.requestedLoadingModuleIds_.length = 0;
    +
    +  this.dispatchActiveIdleChangeIfNeeded_();
    +};
    +
    +
    +/**
    + * Loads the next modules on the queue.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.loadNextModules_ = function() {
    +  while (this.requestedModuleIdsQueue_.length) {
    +    // Remove modules that are already loaded.
    +    var nextIds = goog.array.filter(this.requestedModuleIdsQueue_.shift(),
    +        /** @param {string} id The module id. */
    +        function(id) {
    +          return !this.getModuleInfo(id).isLoaded();
    +        }, this);
    +    if (nextIds.length > 0) {
    +      this.loadModules_(nextIds);
    +      return;
    +    }
    +  }
    +
    +  // Dispatch an active/idle change if needed.
    +  this.dispatchActiveIdleChangeIfNeeded_();
    +};
    +
    +
    +/**
    + * The function to call if the module manager is in error.
    + * @param {goog.module.ModuleManager.CallbackType|Array<goog.module.ModuleManager.CallbackType>} types
    + *  The callback type.
    + * @param {Function} fn The function to register as a callback.
    + */
    +goog.module.ModuleManager.prototype.registerCallback = function(
    +    types, fn) {
    +  if (!goog.isArray(types)) {
    +    types = [types];
    +  }
    +
    +  for (var i = 0; i < types.length; i++) {
    +    this.registerCallback_(types[i], fn);
    +  }
    +};
    +
    +
    +/**
    + * Register a callback for the specified callback type.
    + * @param {goog.module.ModuleManager.CallbackType} type The callback type.
    + * @param {Function} fn The callback function.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.registerCallback_ = function(type, fn) {
    +  var callbackMap = this.callbackMap_;
    +  if (!callbackMap[type]) {
    +    callbackMap[type] = [];
    +  }
    +  callbackMap[type].push(fn);
    +};
    +
    +
    +/**
    + * Call the callback functions of the specified type.
    + * @param {goog.module.ModuleManager.CallbackType} type The callback type.
    + * @private
    + */
    +goog.module.ModuleManager.prototype.executeCallbacks_ = function(type) {
    +  var callbacks = this.callbackMap_[type];
    +  for (var i = 0; callbacks && i < callbacks.length; i++) {
    +    callbacks[i](type);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.module.ModuleManager.prototype.disposeInternal = function() {
    +  goog.module.ModuleManager.base(this, 'disposeInternal');
    +
    +  // Dispose of each ModuleInfo object.
    +  goog.disposeAll(
    +      goog.object.getValues(this.moduleInfoMap_), this.baseModuleInfo_);
    +  this.moduleInfoMap_ = null;
    +  this.loadingModuleIds_ = null;
    +  this.requestedLoadingModuleIds_ = null;
    +  this.userInitiatedLoadingModuleIds_ = null;
    +  this.requestedModuleIdsQueue_ = null;
    +  this.callbackMap_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.html b/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.html
    new file mode 100644
    index 0000000000..e8804c419f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.module.ModuleManager
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.module.ModuleManagerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.js b/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.js
    new file mode 100644
    index 0000000000..39ae6f9eb3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/modulemanager_test.js
    @@ -0,0 +1,2200 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.module.ModuleManagerTest');
    +goog.setTestOnly('goog.module.ModuleManagerTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.functions');
    +goog.require('goog.module.BaseModule');
    +goog.require('goog.module.ModuleManager');
    +goog.require('goog.testing');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +var clock;
    +var requestCount = 0;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function setUp() {
    +  requestCount = 0;
    +}
    +
    +function getModuleManager(infoMap) {
    +  var mm = new goog.module.ModuleManager();
    +  mm.setAllModuleInfo(infoMap);
    +
    +  mm.isModuleLoaded = function(id) {
    +    return this.getModuleInfo(id).isLoaded();
    +  };
    +  return mm;
    +}
    +
    +function createSuccessfulBatchLoader(moduleMgr) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      requestCount++;
    +      setTimeout(goog.bind(this.onLoad, this, ids.concat(), 0), 5);
    +    },
    +    onLoad: function(ids, idxLoaded) {
    +      moduleMgr.beforeLoadModuleCode(ids[idxLoaded]);
    +      moduleMgr.setLoaded(ids[idxLoaded]);
    +      moduleMgr.afterLoadModuleCode(ids[idxLoaded]);
    +      var idx = idxLoaded + 1;
    +      if (idx < ids.length) {
    +        setTimeout(goog.bind(this.onLoad, this, ids, idx), 2);
    +      }
    +    }};
    +}
    +
    +function createSuccessfulNonBatchLoader(moduleMgr) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      requestCount++;
    +      setTimeout(function() {
    +        moduleMgr.beforeLoadModuleCode(ids[0]);
    +        moduleMgr.setLoaded(ids[0]);
    +        moduleMgr.afterLoadModuleCode(ids[0]);
    +        if (opt_successFn) {
    +          opt_successFn();
    +        }
    +      }, 5);
    +    }};
    +}
    +
    +function createUnsuccessfulLoader(moduleMgr, status) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      moduleMgr.beforeLoadModuleCode(ids[0]);
    +      setTimeout(function() { opt_errFn(status); }, 5);
    +    }};
    +}
    +
    +function createUnsuccessfulBatchLoader(moduleMgr, status) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      setTimeout(function() { opt_errFn(status); }, 5);
    +    }};
    +}
    +
    +function createTimeoutLoader(moduleMgr, status) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      setTimeout(function() { opt_timeoutFn(status); }, 5);
    +    }};
    +}
    +
    +
    +/**
    + * Tests loading a module under different conditions i.e. unloaded
    + * module, already loaded module, module loaded through user initiated
    + * actions, synchronous callback for a module that has been already
    + * loaded. Test both batch and non-batch loaders.
    + */
    +function testExecOnLoad() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +  execOnLoad_(mm);
    +
    +  mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  mm.setBatchModeEnabled(true);
    +  execOnLoad_(mm);
    +}
    +
    +
    +/**
    + * Tests execOnLoad with the specified module manager.
    + * @param {goog.module.ModuleManager} mm The module manager.
    + */
    +function execOnLoad_(mm) {
    +  // When module is unloaded, execOnLoad is async.
    +  var execCalled1 = false;
    +  mm.execOnLoad('a', function() { execCalled1 = true; });
    +  assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
    +  assertTrue('module "a" should be loading', mm.isModuleLoading('a'));
    +  assertFalse('execCalled1 should not be set yet', execCalled1);
    +  assertTrue('ModuleManager should be active', mm.isActive());
    +  assertFalse(
    +      'ModuleManager should not be user active', mm.isUserActive());
    +  clock.tick(5);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse(
    +      'module "a" should not be loading', mm.isModuleLoading('a'));
    +  assertTrue('execCalled1 should be set', execCalled1);
    +  assertFalse('ModuleManager should not be active', mm.isActive());
    +  assertFalse(
    +      'ModuleManager should not be user active', mm.isUserActive());
    +
    +  // When module is already loaded, execOnLoad is still async unless
    +  // specified otherwise.
    +  var execCalled2 = false;
    +  mm.execOnLoad('a', function() { execCalled2 = true; });
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse(
    +      'module "a" should not be loading', mm.isModuleLoading('a'));
    +  assertFalse('execCalled2 should not be set yet', execCalled2);
    +  clock.tick(5);
    +  assertTrue('execCalled2 should be set', execCalled2);
    +
    +  // When module is unloaded, execOnLoad is async (user active).
    +  var execCalled5 = false;
    +  mm.execOnLoad('c',
    +      function() { execCalled5 = true; }, null, null, true);
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +  assertTrue('module "c" should be loading', mm.isModuleLoading('c'));
    +  assertFalse('execCalled1 should not be set yet', execCalled5);
    +  assertTrue('ModuleManager should be active', mm.isActive());
    +  assertTrue('ModuleManager should be user active', mm.isUserActive());
    +  clock.tick(5);
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +  assertFalse(
    +      'module "c" should not be loading', mm.isModuleLoading('c'));
    +  assertTrue('execCalled1 should be set', execCalled5);
    +  assertFalse('ModuleManager should not be active', mm.isActive());
    +  assertFalse(
    +      'ModuleManager should not be user active', mm.isUserActive());
    +
    +  // When module is already loaded, execOnLoad is still synchronous when
    +  // so specified
    +  var execCalled6 = false;
    +  mm.execOnLoad('c', function() { execCalled6 = true; },
    +      undefined, undefined, undefined, true);
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +  assertFalse(
    +      'module "c" should not be loading', mm.isModuleLoading('c'));
    +  assertTrue('execCalled6 should be set', execCalled6);
    +  clock.tick(5);
    +  assertTrue('execCalled6 should still be set', execCalled6);
    +
    +}
    +
    +
    +/**
    + * Test aborting the callback called on module load.
    + */
    +function testExecOnLoadAbort() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  // When module is unloaded and abort is called, module still gets
    +  // loaded, but callback is cancelled.
    +  var execCalled1 = false;
    +  var callback1 = mm.execOnLoad('b', function() { execCalled1 = true; });
    +  callback1.abort();
    +  clock.tick(5);
    +  assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('execCalled3 should not be set', execCalled1);
    +
    +  // When module is already loaded, execOnLoad is still async, so calling
    +  // abort should still cancel the callback.
    +  var execCalled2 = false;
    +  var callback2 = mm.execOnLoad('a', function() { execCalled2 = true; });
    +  callback2.abort();
    +  clock.tick(5);
    +  assertFalse('execCalled2 should not be set', execCalled2);
    +}
    +
    +
    +/**
    + * Test preloading modules and ensure that the before load, after load
    + * and set load called are called only once per module.
    + */
    +function testExecOnLoadWhilePreloadingAndViceVersa() {
    +  var mm = getModuleManager({'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +  execOnLoadWhilePreloadingAndViceVersa_(mm);
    +
    +  mm = getModuleManager({'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  mm.setBatchModeEnabled(true);
    +  execOnLoadWhilePreloadingAndViceVersa_(mm);
    +}
    +
    +
    +/**
    + * Perform tests with the specified module manager.
    + * @param {goog.module.ModuleManager} mm The module manager.
    + */
    +function execOnLoadWhilePreloadingAndViceVersa_(mm) {
    +  var mm = getModuleManager({'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var origSetLoaded = mm.setLoaded;
    +  var calls = [0, 0, 0];
    +  mm.beforeLoadModuleCode = function(id) {
    +    calls[0]++;
    +  };
    +  mm.setLoaded = function(id) {
    +    calls[1]++;
    +    origSetLoaded.call(mm, id);
    +  };
    +  mm.afterLoadModuleCode = function(id) {
    +    calls[2]++;
    +  };
    +
    +  mm.preloadModule('c', 2);
    +  assertFalse(
    +      'module "c" should not be loading yet', mm.isModuleLoading('c'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "c" should now be loading', mm.isModuleLoading('c'));
    +  mm.execOnLoad('c', function() {});
    +  assertTrue(
    +      'module "c" should still be loading', mm.isModuleLoading('c'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "c" should be done loading', mm.isModuleLoading('c'));
    +  assertEquals(
    +      'beforeLoad should only be called once for "c"', 1, calls[0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "c"', 1, calls[1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "c"', 1, calls[2]);
    +
    +  mm.execOnLoad('d', function() {});
    +  assertTrue(
    +      'module "d" should now be loading', mm.isModuleLoading('d'));
    +  mm.preloadModule('d', 2);
    +  clock.tick(5);
    +  assertFalse(
    +      'module "d" should be done loading', mm.isModuleLoading('d'));
    +  assertTrue(
    +      'module "d" should now be loaded', mm.isModuleLoaded('d'));
    +  assertEquals(
    +      'beforeLoad should only be called once for "d"', 2, calls[0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "d"', 2, calls[1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "d"', 2, calls[2]);
    +}
    +
    +
    +/**
    + * Tests that multiple callbacks on the same module don't cause
    + * confusion about the active state after the module is finally loaded.
    + */
    +function testUserInitiatedExecOnLoadEventuallyLeavesManagerIdle() {
    +  var mm = getModuleManager({'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack1 = false;
    +  var calledBack2 = false;
    +
    +  mm.execOnLoad(
    +      'c',
    +      function() {
    +        calledBack1 = true;
    +      },
    +      undefined,
    +      undefined,
    +      true);
    +  mm.execOnLoad(
    +      'c',
    +      function() {
    +        calledBack2 = true;
    +      },
    +      undefined,
    +      undefined,
    +      true);
    +  mm.load('c');
    +
    +  assertTrue(
    +      'Manager should be active while waiting for load', mm.isUserActive());
    +
    +  clock.tick(5);
    +
    +  assertTrue('First callback should be called', calledBack1);
    +  assertTrue('Second callback should be called', calledBack2);
    +  assertFalse(
    +      'Manager should be inactive after loading is complete',
    +      mm.isUserActive());
    +}
    +
    +
    +/**
    + * Tests loading a module by requesting a Deferred object.
    + */
    +function testLoad() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  var d = mm.load('a');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  d.addErrback(function(err) {
    +    error = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertNull(error);
    +  assertFalse(mm.isUserActive());
    +
    +  clock.tick(5);
    +
    +  assertTrue(calledBack);
    +  assertNull(error);
    +}
    +
    +
    +/**
    + * Tests loading 2 modules asserting that the loads happen in parallel
    + * in one unit of time.
    + */
    +function testLoad_concurrent() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setConcurrentLoadingEnabled(true);
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  mm.load('a');
    +  mm.load('b');
    +  assertEquals(2, requestCount);
    +  // Only time for one serialized download.
    +  clock.tick(5);
    +
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +  assertTrue(mm.getModuleInfo('b').isLoaded());
    +}
    +
    +function testLoad_concurrentSecondIsDepOfFist() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setConcurrentLoadingEnabled(true);
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  mm.loadMultiple(['a', 'b']);
    +  mm.load('b');
    +  assertEquals('No 2nd request expected', 1, requestCount);
    +  // Only time for one serialized download.
    +  clock.tick(5);
    +  clock.tick(2); // Makes second module come in from batch requst.
    +
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +  assertTrue(mm.getModuleInfo('b').isLoaded());
    +}
    +
    +function testLoad_nonConcurrent() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  mm.load('a');
    +  mm.load('b');
    +  assertEquals(1, requestCount);
    +  // Only time for one serialized download.
    +  clock.tick(5);
    +
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +  assertFalse(mm.getModuleInfo('b').isLoaded());
    +}
    +
    +function testLoadUnknown() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +  var e = assertThrows(function() {
    +    mm.load('DoesNotExist');
    +  });
    +  assertEquals('Unknown module: DoesNotExist', e.message);
    +}
    +
    +
    +/**
    + * Tests loading multiple modules by requesting a Deferred object.
    + */
    +function testLoadMultiple() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +
    +  clock.tick(5);
    +  assertTrue(calledBack);
    +  assertFalse(calledBack2);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(2);
    +
    +  assertTrue(calledBack);
    +  assertTrue(calledBack2);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +  assertNull(error);
    +  assertNull(error2);
    +}
    +
    +
    +/**
    + * Tests loading multiple modules with deps by requesting a Deferred object.
    + */
    +function testLoadMultipleWithDeps() {
    +  var mm = getModuleManager({'a': [], 'b': ['c'], 'c': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +
    +  clock.tick(5);
    +  assertTrue(calledBack);
    +  assertFalse(calledBack2);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(2);
    +
    +  assertFalse(calledBack2);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(2);
    +
    +  assertTrue(calledBack2);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +  assertNull(error);
    +  assertNull(error2);
    +}
    +
    +
    +/**
    + * Tests loading multiple modules by requesting a Deferred object when
    + * a server error occurs.
    + */
    +function testLoadMultipleWithErrors() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setLoader(createUnsuccessfulLoader(mm, 500));
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +  var calledBack3 = false;
    +  var error3 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b', 'c']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +  dMap['c'].addCallback(function(ctx) {
    +    calledBack3 = true;
    +  });
    +  dMap['c'].addErrback(function(err) {
    +    error3 = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +
    +  clock.tick(4);
    +
    +  // A module request is now underway using the unsuccessful loader.
    +  // We substitute a successful loader for future module load requests.
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  clock.tick(1);
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  // Retry should happen after a backoff
    +  clock.tick(5 + mm.getBackOff_());
    +
    +  assertTrue(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(2);
    +  assertTrue(calledBack2);
    +  assertFalse(calledBack3);
    +  assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(2);
    +  assertTrue(calledBack3);
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +
    +  assertNull(error);
    +  assertNull(error2);
    +  assertNull(error3);
    +}
    +
    +
    +/**
    + * Tests loading multiple modules by requesting a Deferred object when
    + * consecutive server error occur and the loader falls back to serial
    + * loads.
    + */
    +function testLoadMultipleWithErrorsFallbackOnSerial() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setLoader(createUnsuccessfulLoader(mm, 500));
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +  var calledBack3 = false;
    +  var error3 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b', 'c']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +  dMap['c'].addCallback(function(ctx) {
    +    calledBack3 = true;
    +  });
    +  dMap['c'].addErrback(function(err) {
    +    error3 = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +
    +  clock.tick(5);
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  // Retry should happen and fail after a backoff
    +  clock.tick(5 + mm.getBackOff_());
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  // A second retry should happen after a backoff
    +  clock.tick(4 + mm.getBackOff_());
    +  // The second retry is now underway using the unsuccessful loader.
    +  // We substitute a successful loader for future module load requests.
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  clock.tick(1);
    +
    +  // A second retry should fail now
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  // Each module should be loaded individually now, each taking 5 ticks
    +
    +  clock.tick(5);
    +  assertTrue(calledBack);
    +  assertFalse(calledBack2);
    +  assertFalse(calledBack3);
    +  assertTrue('module "a" should be loaded', mm.isModuleLoaded('a'));
    +  assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(5);
    +  assertTrue(calledBack2);
    +  assertFalse(calledBack3);
    +  assertTrue('module "b" should be loaded', mm.isModuleLoaded('b'));
    +  assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c'));
    +
    +  clock.tick(5);
    +  assertTrue(calledBack3);
    +  assertTrue('module "c" should be loaded', mm.isModuleLoaded('c'));
    +
    +  assertNull(error);
    +  assertNull(error2);
    +  assertNull(error3);
    +}
    +
    +
    +/**
    + * Tests loading a module by user action by requesting a Deferred object.
    + */
    +function testLoadForUser() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  var d = mm.load('a', true);
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  d.addErrback(function(err) {
    +    error = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertNull(error);
    +  assertTrue(mm.isUserActive());
    +
    +  clock.tick(5);
    +
    +  assertTrue(calledBack);
    +  assertNull(error);
    +}
    +
    +
    +/**
    + * Tests that preloading a module calls back the deferred object.
    + */
    +function testPreloadDeferredWhenNotLoaded() {
    +  var mm = getModuleManager({'a': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +
    +  var d = mm.preloadModule('a');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +
    +  // First load should take five ticks.
    +  assertFalse('module "a" should not be loaded yet', calledBack);
    +  clock.tick(5);
    +  assertTrue('module "a" should be loaded', calledBack);
    +}
    +
    +
    +/**
    + * Tests preloading an already loaded module.
    + */
    +function testPreloadDeferredWhenLoaded() {
    +  var mm = getModuleManager({'a': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +
    +  mm.preloadModule('a');
    +  clock.tick(5);
    +
    +  var d = mm.preloadModule('a');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +
    +  // Module is already loaded, should be called back after the setTimeout
    +  // in preloadModule.
    +  assertFalse('deferred for module "a" should not be called yet', calledBack);
    +  clock.tick(1);
    +  assertTrue('module "a" should be loaded', calledBack);
    +}
    +
    +
    +/**
    + * Tests preloading a module that is currently loading.
    + */
    +function testPreloadDeferredWhenLoading() {
    +  var mm = getModuleManager({'a': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  mm.preloadModule('a');
    +  clock.tick(1);
    +
    +  // 'b' is in the middle of loading, should get called back when it's done.
    +  var calledBack = false;
    +  var d = mm.preloadModule('a');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +
    +  assertFalse('module "a" should not be loaded yet', calledBack);
    +  clock.tick(4);
    +  assertTrue('module "a" should be loaded', calledBack);
    +}
    +
    +
    +/**
    + * Tests that load doesn't trigger another load if a module is already
    + * preloading.
    + */
    +function testLoadWhenPreloading() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var origSetLoaded = mm.setLoaded;
    +  var calls = [0, 0, 0];
    +  mm.beforeLoadModuleCode = function(id) {
    +    calls[0]++;
    +  };
    +  mm.setLoaded = function(id) {
    +    calls[1]++;
    +    origSetLoaded.call(mm, id);
    +  };
    +  mm.afterLoadModuleCode = function(id) {
    +    calls[2]++;
    +  };
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  mm.preloadModule('c', 2);
    +  assertFalse(
    +      'module "c" should not be loading yet', mm.isModuleLoading('c'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "c" should now be loading', mm.isModuleLoading('c'));
    +
    +  var d = mm.load('c');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  d.addErrback(function(err) {
    +    error = err;
    +  });
    +
    +  assertTrue(
    +      'module "c" should still be loading', mm.isModuleLoading('c'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "c" should be done loading', mm.isModuleLoading('c'));
    +  assertEquals(
    +      'beforeLoad should only be called once for "c"', 1, calls[0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "c"', 1, calls[1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "c"', 1, calls[2]);
    +
    +  assertTrue(calledBack);
    +  assertNull(error);
    +}
    +
    +
    +/**
    + * Tests that load doesn't trigger another load if a module is already
    + * preloading.
    + */
    +function testLoadMultipleWhenPreloading() {
    +  var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  mm.setBatchModeEnabled(true);
    +
    +  var origSetLoaded = mm.setLoaded;
    +  var calls = {'a': [0, 0, 0], 'b': [0, 0, 0],
    +    'c': [0, 0, 0], 'd': [0, 0, 0]};
    +  mm.beforeLoadModuleCode = function(id) {
    +    calls[id][0]++;
    +  };
    +  mm.setLoaded = function(id) {
    +    calls[id][1]++;
    +    origSetLoaded.call(mm, id);
    +  };
    +  mm.afterLoadModuleCode = function(id) {
    +    calls[id][2]++;
    +  };
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +  var calledBack3 = false;
    +  var error3 = null;
    +
    +  mm.preloadModule('c', 2);
    +  mm.preloadModule('d', 3);
    +  assertFalse(
    +      'module "c" should not be loading yet', mm.isModuleLoading('c'));
    +  assertFalse(
    +      'module "d" should not be loading yet', mm.isModuleLoading('d'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "c" should now be loading', mm.isModuleLoading('c'));
    +  clock.tick(1);
    +  assertTrue(
    +      'module "d" should now be loading', mm.isModuleLoading('d'));
    +
    +  var dMap = mm.loadMultiple(['a', 'b', 'c']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +  dMap['c'].addCallback(function(ctx) {
    +    calledBack3 = true;
    +  });
    +  dMap['c'].addErrback(function(err) {
    +    error3 = err;
    +  });
    +
    +  assertTrue(
    +      'module "a" should be loading', mm.isModuleLoading('a'));
    +  assertTrue(
    +      'module "b" should be loading', mm.isModuleLoading('b'));
    +  assertTrue(
    +      'module "c" should still be loading', mm.isModuleLoading('c'));
    +  clock.tick(4);
    +  assertTrue(calledBack3);
    +
    +  assertFalse(
    +      'module "c" should be done loading', mm.isModuleLoading('c'));
    +  assertTrue(
    +      'module "d" should still be loading', mm.isModuleLoading('d'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "d" should be done loading', mm.isModuleLoading('d'));
    +
    +  assertFalse(calledBack);
    +  assertFalse(calledBack2);
    +  assertTrue(
    +      'module "a" should still be loading', mm.isModuleLoading('a'));
    +  assertTrue(
    +      'module "b" should still be loading', mm.isModuleLoading('b'));
    +  clock.tick(7);
    +
    +  assertTrue(calledBack);
    +  assertTrue(calledBack2);
    +  assertFalse(
    +      'module "a" should be done loading', mm.isModuleLoading('a'));
    +  assertFalse(
    +      'module "b" should be done loading', mm.isModuleLoading('b'));
    +
    +  assertEquals(
    +      'beforeLoad should only be called once for "a"', 1, calls['a'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "a"', 1, calls['a'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "a"', 1, calls['a'][2]);
    +  assertEquals(
    +      'beforeLoad should only be called once for "b"', 1, calls['b'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "b"', 1, calls['b'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "b"', 1, calls['b'][2]);
    +  assertEquals(
    +      'beforeLoad should only be called once for "c"', 1, calls['c'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "c"', 1, calls['c'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "c"', 1, calls['c'][2]);
    +  assertEquals(
    +      'beforeLoad should only be called once for "d"', 1, calls['d'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "d"', 1, calls['d'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "d"', 1, calls['d'][2]);
    +
    +  assertNull(error);
    +  assertNull(error2);
    +  assertNull(error3);
    +}
    +
    +
    +/**
    + * Tests that the deferred is still called when loadMultiple loads modules
    + * that are already preloading.
    + */
    +function testLoadMultipleWhenPreloadingSameModules() {
    +  var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []});
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  mm.setBatchModeEnabled(true);
    +
    +  var origSetLoaded = mm.setLoaded;
    +  var calls = {'c': [0, 0, 0], 'd': [0, 0, 0]};
    +  mm.beforeLoadModuleCode = function(id) {
    +    calls[id][0]++;
    +  };
    +  mm.setLoaded = function(id) {
    +    calls[id][1]++;
    +    origSetLoaded.call(mm, id);
    +  };
    +  mm.afterLoadModuleCode = function(id) {
    +    calls[id][2]++;
    +  };
    +
    +  var calledBack = false;
    +  var error = null;
    +  var calledBack2 = false;
    +  var error2 = null;
    +
    +  mm.preloadModule('c', 2);
    +  mm.preloadModule('d', 3);
    +  assertFalse(
    +      'module "c" should not be loading yet', mm.isModuleLoading('c'));
    +  assertFalse(
    +      'module "d" should not be loading yet', mm.isModuleLoading('d'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "c" should now be loading', mm.isModuleLoading('c'));
    +  clock.tick(1);
    +  assertTrue(
    +      'module "d" should now be loading', mm.isModuleLoading('d'));
    +
    +  var dMap = mm.loadMultiple(['c', 'd']);
    +  dMap['c'].addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  dMap['c'].addErrback(function(err) {
    +    error = err;
    +  });
    +  dMap['d'].addCallback(function(ctx) {
    +    calledBack2 = true;
    +  });
    +  dMap['d'].addErrback(function(err) {
    +    error2 = err;
    +  });
    +
    +  assertTrue(
    +      'module "c" should still be loading', mm.isModuleLoading('c'));
    +  clock.tick(4);
    +  assertFalse(
    +      'module "c" should be done loading', mm.isModuleLoading('c'));
    +  assertTrue(
    +      'module "d" should still be loading', mm.isModuleLoading('d'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "d" should be done loading', mm.isModuleLoading('d'));
    +
    +  assertTrue(calledBack);
    +  assertTrue(calledBack2);
    +
    +  assertEquals(
    +      'beforeLoad should only be called once for "c"', 1, calls['c'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "c"', 1, calls['c'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "c"', 1, calls['c'][2]);
    +  assertEquals(
    +      'beforeLoad should only be called once for "d"', 1, calls['d'][0]);
    +  assertEquals(
    +      'setLoaded should only be called once for "d"', 1, calls['d'][1]);
    +  assertEquals(
    +      'afterLoad should only be called once for "d"', 1, calls['d'][2]);
    +
    +  assertNull(error);
    +  assertNull(error2);
    +}
    +
    +
    +/**
    + * Tests loading a module via load when the module is already
    + * loaded.  The deferred's callback should be called immediately.
    + */
    +function testLoadWhenLoaded() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var calledBack = false;
    +  var error = null;
    +
    +  mm.preloadModule('b', 2);
    +  clock.tick(10);
    +
    +  assertFalse(
    +      'module "b" should be done loading', mm.isModuleLoading('b'));
    +
    +  var d = mm.load('b');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  d.addErrback(function(err) {
    +    error = err;
    +  });
    +
    +  assertTrue(calledBack);
    +  assertNull(error);
    +}
    +
    +
    +/**
    + * Tests that the deferred's errbacks are called if the module fails to load.
    + */
    +function testLoadWithFailingModule() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 401));
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +            cause);
    +        firedLoadFailed = true;
    +      });
    +  var calledBack = false;
    +  var error = null;
    +
    +  var d = mm.load('a');
    +  d.addCallback(function(ctx) {
    +    calledBack = true;
    +  });
    +  d.addErrback(function(err) {
    +    error = err;
    +  });
    +
    +  assertFalse(calledBack);
    +  assertNull(error);
    +
    +  clock.tick(500);
    +
    +  assertFalse(calledBack);
    +
    +  // NOTE: Deferred always calls errbacks with an Error object.  For now the
    +  // module manager just passes the FailureType which gets set as the Error
    +  // object's message.
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error.message));
    +}
    +
    +
    +/**
    + * Tests that the deferred's errbacks are called if a module fails to load.
    + */
    +function testLoadMultipleWithFailingModule() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 401));
    +  mm.setBatchModeEnabled(true);
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +            cause);
    +      });
    +  var calledBack11 = false;
    +  var error11 = null;
    +  var calledBack12 = false;
    +  var error12 = null;
    +  var calledBack21 = false;
    +  var error21 = null;
    +  var calledBack22 = false;
    +  var error22 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack11 = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error11 = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack12 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error12 = err;
    +  });
    +
    +  var dMap2 = mm.loadMultiple(['b', 'c']);
    +  dMap2['b'].addCallback(function(ctx) {
    +    calledBack21 = true;
    +  });
    +  dMap2['b'].addErrback(function(err) {
    +    error21 = err;
    +  });
    +  dMap2['c'].addCallback(function(ctx) {
    +    calledBack22 = true;
    +  });
    +  dMap2['c'].addErrback(function(err) {
    +    error22 = err;
    +  });
    +
    +  assertFalse(calledBack11);
    +  assertFalse(calledBack12);
    +  assertFalse(calledBack21);
    +  assertFalse(calledBack22);
    +  assertNull(error11);
    +  assertNull(error12);
    +  assertNull(error21);
    +  assertNull(error22);
    +
    +  clock.tick(5);
    +
    +  assertFalse(calledBack11);
    +  assertFalse(calledBack12);
    +  assertFalse(calledBack21);
    +  assertFalse(calledBack22);
    +
    +  // NOTE: Deferred always calls errbacks with an Error object.  For now the
    +  // module manager just passes the FailureType which gets set as the Error
    +  // object's message.
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error11.message));
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error12.message));
    +
    +  // The first deferred of the second load should be called since it asks for
    +  // one of the failed modules.
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error21.message));
    +
    +  // The last deferred should be dropped so it is neither called back nor an
    +  // error.
    +  assertFalse(calledBack22);
    +  assertNull(error22);
    +}
    +
    +
    +/**
    + * Tests that the right dependencies are cancelled on a loadMultiple failure.
    + */
    +function testLoadMultipleWithFailingModuleDependencies() {
    +  var mm = getModuleManager(
    +      {'a': [], 'b': [], 'c': ['b'], 'd': ['c'], 'e': []});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 401));
    +  mm.setBatchModeEnabled(true);
    +  var cancelledIds = [];
    +
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +            cause);
    +        cancelledIds.push(id);
    +      });
    +  var calledBack11 = false;
    +  var error11 = null;
    +  var calledBack12 = false;
    +  var error12 = null;
    +  var calledBack21 = false;
    +  var error21 = null;
    +  var calledBack22 = false;
    +  var error22 = null;
    +  var calledBack23 = false;
    +  var error23 = null;
    +
    +  var dMap = mm.loadMultiple(['a', 'b']);
    +  dMap['a'].addCallback(function(ctx) {
    +    calledBack11 = true;
    +  });
    +  dMap['a'].addErrback(function(err) {
    +    error11 = err;
    +  });
    +  dMap['b'].addCallback(function(ctx) {
    +    calledBack12 = true;
    +  });
    +  dMap['b'].addErrback(function(err) {
    +    error12 = err;
    +  });
    +
    +  var dMap2 = mm.loadMultiple(['c', 'd', 'e']);
    +  dMap2['c'].addCallback(function(ctx) {
    +    calledBack21 = true;
    +  });
    +  dMap2['c'].addErrback(function(err) {
    +    error21 = err;
    +  });
    +  dMap2['d'].addCallback(function(ctx) {
    +    calledBack22 = true;
    +  });
    +  dMap2['d'].addErrback(function(err) {
    +    error22 = err;
    +  });
    +  dMap2['e'].addCallback(function(ctx) {
    +    calledBack23 = true;
    +  });
    +  dMap2['e'].addErrback(function(err) {
    +    error23 = err;
    +  });
    +
    +  assertFalse(calledBack11);
    +  assertFalse(calledBack12);
    +  assertFalse(calledBack21);
    +  assertFalse(calledBack22);
    +  assertFalse(calledBack23);
    +  assertNull(error11);
    +  assertNull(error12);
    +  assertNull(error21);
    +  assertNull(error22);
    +  assertNull(error23);
    +
    +  clock.tick(5);
    +
    +  assertFalse(calledBack11);
    +  assertFalse(calledBack12);
    +  assertFalse(calledBack21);
    +  assertFalse(calledBack22);
    +  assertFalse(calledBack23);
    +
    +  // NOTE: Deferred always calls errbacks with an Error object.  For now the
    +  // module manager just passes the FailureType which gets set as the Error
    +  // object's message.
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error11.message));
    +  assertEquals('Failure cause was not as expected',
    +      goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +      Number(error12.message));
    +
    +  // Check that among the failed modules, 'c' and 'd' are also cancelled
    +  // due to dependencies.
    +  assertTrue(goog.array.equals(['a', 'b', 'c', 'd'], cancelledIds.sort()));
    +}
    +
    +
    +/**
    + * Tests that when loading multiple modules, the input array is not modified
    + * when it has duplicates.
    + */
    +function testLoadMultipleWithDuplicates() {
    +  var mm = getModuleManager({'a': [], 'b': []});
    +  mm.setBatchModeEnabled(true);
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +
    +  var listWithDuplicates = ['a', 'a', 'b'];
    +  mm.loadMultiple(listWithDuplicates);
    +  assertArrayEquals('loadMultiple should not modify its input',
    +      ['a', 'a', 'b'], listWithDuplicates);
    +}
    +
    +
    +/**
    + * Test loading dependencies transitively.
    + */
    +function testLoadingDepsInNonBatchMode1() {
    +  var mm = getModuleManager({
    +    'i': [],
    +    'j': [],
    +    'k': ['j'],
    +    'l': ['i', 'j', 'k']});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  mm.preloadModule('j');
    +  clock.tick(5);
    +  assertTrue('module "j" should be loaded', mm.isModuleLoaded('j'));
    +  assertFalse(
    +      'module "i" should not be loaded (1)', mm.isModuleLoaded('i'));
    +  assertFalse(
    +      'module "k" should not be loaded (1)', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (1)', mm.isModuleLoaded('l'));
    +
    +  // When loading a module in non-batch mode, its dependencies should be
    +  // requested independently, and in dependency order.
    +  mm.preloadModule('l');
    +  clock.tick(5);
    +  assertTrue('module "i" should be loaded', mm.isModuleLoaded('i'));
    +  assertFalse(
    +      'module "k" should not be loaded (2)', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (2)', mm.isModuleLoaded('l'));
    +  clock.tick(5);
    +  assertTrue('module "k" should be loaded', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (3)', mm.isModuleLoaded('l'));
    +  clock.tick(5);
    +  assertTrue(
    +      'module "l" should be loaded', mm.isModuleLoaded('l'));
    +}
    +
    +
    +/**
    + * Test loading dependencies transitively and in dependency order.
    + */
    +function testLoadingDepsInNonBatchMode2() {
    +  var mm = getModuleManager({
    +    'h': [],
    +    'i': ['h'],
    +    'j': ['i'],
    +    'k': ['j'],
    +    'l': ['i', 'j', 'k'],
    +    'm': ['l']});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  // When loading a module in non-batch mode, its dependencies should be
    +  // requested independently, and in dependency order. The order in this
    +  // case should be h,i,j,k,l,m.
    +  mm.preloadModule('m');
    +  clock.tick(5);
    +  assertTrue('module "h" should be loaded', mm.isModuleLoaded('h'));
    +  assertFalse(
    +      'module "i" should not be loaded (1)', mm.isModuleLoaded('i'));
    +  assertFalse(
    +      'module "j" should not be loaded (1)', mm.isModuleLoaded('j'));
    +  assertFalse(
    +      'module "k" should not be loaded (1)', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (1)', mm.isModuleLoaded('l'));
    +  assertFalse(
    +      'module "m" should not be loaded (1)', mm.isModuleLoaded('m'));
    +
    +  clock.tick(5);
    +  assertTrue('module "i" should be loaded', mm.isModuleLoaded('i'));
    +  assertFalse(
    +      'module "j" should not be loaded (2)', mm.isModuleLoaded('j'));
    +  assertFalse(
    +      'module "k" should not be loaded (2)', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (2)', mm.isModuleLoaded('l'));
    +  assertFalse(
    +      'module "m" should not be loaded (2)', mm.isModuleLoaded('m'));
    +
    +  clock.tick(5);
    +  assertTrue('module "j" should be loaded', mm.isModuleLoaded('j'));
    +  assertFalse(
    +      'module "k" should not be loaded (3)', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (3)', mm.isModuleLoaded('l'));
    +  assertFalse(
    +      'module "m" should not be loaded (3)', mm.isModuleLoaded('m'));
    +
    +  clock.tick(5);
    +  assertTrue('module "k" should be loaded', mm.isModuleLoaded('k'));
    +  assertFalse(
    +      'module "l" should not be loaded (4)', mm.isModuleLoaded('l'));
    +  assertFalse(
    +      'module "m" should not be loaded (4)', mm.isModuleLoaded('m'));
    +
    +  clock.tick(5);
    +  assertTrue('module "l" should be loaded', mm.isModuleLoaded('l'));
    +  assertFalse(
    +      'module "m" should not be loaded (5)', mm.isModuleLoaded('m'));
    +
    +  clock.tick(5);
    +  assertTrue('module "m" should be loaded', mm.isModuleLoaded('m'));
    +}
    +
    +function testLoadingDepsInBatchMode() {
    +  var mm = getModuleManager({
    +    'e': [],
    +    'f': [],
    +    'g': ['f'],
    +    'h': ['e', 'f', 'g']});
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  mm.setBatchModeEnabled(true);
    +
    +  mm.preloadModule('f');
    +  clock.tick(5);
    +  assertTrue('module "f" should be loaded', mm.isModuleLoaded('f'));
    +  assertFalse(
    +      'module "e" should not be loaded (1)', mm.isModuleLoaded('e'));
    +  assertFalse(
    +      'module "g" should not be loaded (1)', mm.isModuleLoaded('g'));
    +  assertFalse(
    +      'module "h" should not be loaded (1)', mm.isModuleLoaded('h'));
    +
    +  // When loading a module in batch mode, its not-yet-loaded dependencies
    +  // should be requested at the same time, and in dependency order.
    +  mm.preloadModule('h');
    +  clock.tick(5);
    +  assertTrue('module "e" should be loaded', mm.isModuleLoaded('e'));
    +  assertFalse(
    +      'module "g" should not be loaded (2)', mm.isModuleLoaded('g'));
    +  assertFalse(
    +      'module "h" should not be loaded (2)', mm.isModuleLoaded('h'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "g" should be loaded', mm.isModuleLoaded('g'));
    +  assertFalse(
    +      'module "h" should not be loaded (3)', mm.isModuleLoaded('h'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "h" should be loaded', mm.isModuleLoaded('h'));
    +}
    +
    +
    +/**
    + * Test unauthorized errors while loading modules.
    + */
    +function testUnauthorizedLoading() {
    +  var mm = getModuleManager({
    +    'm': [],
    +    'n': [],
    +    'o': ['n']});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 401));
    +
    +  // Callback checks for an unauthorized error
    +  var firedLoadFailed = false;
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +                     goog.module.ModuleManager.FailureType.UNAUTHORIZED,
    +                     cause);
    +        firedLoadFailed = true;
    +      });
    +  mm.execOnLoad('o', function() {});
    +  assertTrue('module "o" should be loading', mm.isModuleLoading('o'));
    +  assertTrue('module "n" should be loading', mm.isModuleLoading('n'));
    +  clock.tick(5);
    +  assertTrue(
    +      'should have called unauthorized module callback', firedLoadFailed);
    +  assertFalse(
    +      'module "o" should not be loaded', mm.isModuleLoaded('o'));
    +  assertFalse(
    +      'module "o" should not be loading', mm.isModuleLoading('o'));
    +  assertFalse(
    +      'module "n" should not be loaded', mm.isModuleLoaded('n'));
    +  assertFalse(
    +      'module "n" should not be loading', mm.isModuleLoading('n'));
    +}
    +
    +
    +/**
    + * Test error loading modules which are retried.
    + */
    +function testErrorLoadingModule() {
    +  var mm = getModuleManager({
    +    'p': ['q'],
    +    'q': [],
    +    'r': ['q', 'p']});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 500));
    +
    +  mm.preloadModule('r');
    +  clock.tick(4);
    +
    +  // A module request is now underway using the unsuccessful loader.
    +  // We substitute a successful loader for future module load requests.
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +  clock.tick(1);
    +  assertFalse(
    +      'module "q" should not be loaded (1)', mm.isModuleLoaded('q'));
    +  assertFalse(
    +      'module "p" should not be loaded (1)', mm.isModuleLoaded('p'));
    +  assertFalse(
    +      'module "r" should not be loaded (1)', mm.isModuleLoaded('r'));
    +
    +  // Failed loads are automatically retried after a backOff.
    +  clock.tick(5 + mm.getBackOff_());
    +  assertTrue('module "q" should be loaded', mm.isModuleLoaded('q'));
    +  assertFalse(
    +      'module "p" should not be loaded (2)', mm.isModuleLoaded('p'));
    +  assertFalse(
    +      'module "r" should not be loaded (2)', mm.isModuleLoaded('r'));
    +
    +  // A successful load decrements the backOff.
    +  clock.tick(5);
    +  assertTrue('module "p" should be loaded', mm.isModuleLoaded('p'));
    +  assertFalse(
    +      'module "r" should not be loaded (3)', mm.isModuleLoaded('r'));
    +  clock.tick(5);
    +  assertTrue(
    +      'module "r" should be loaded', mm.isModuleLoaded('r'));
    +}
    +
    +
    +/**
    + * Tests error loading modules which are retried.
    + */
    +function testErrorLoadingModule_batchMode() {
    +  var mm = getModuleManager({
    +    'p': ['q'],
    +    'q': [],
    +    'r': ['q', 'p']});
    +  mm.setLoader(createUnsuccessfulBatchLoader(mm, 500));
    +  mm.setBatchModeEnabled(true);
    +
    +  mm.preloadModule('r');
    +  clock.tick(4);
    +
    +  // A module request is now underway using the unsuccessful loader.
    +  // We substitute a successful loader for future module load requests.
    +  mm.setLoader(createSuccessfulBatchLoader(mm));
    +  clock.tick(1);
    +  assertFalse(
    +      'module "q" should not be loaded (1)', mm.isModuleLoaded('q'));
    +  assertFalse(
    +      'module "p" should not be loaded (1)', mm.isModuleLoaded('p'));
    +  assertFalse(
    +      'module "r" should not be loaded (1)', mm.isModuleLoaded('r'));
    +
    +  // Failed loads are automatically retried after a backOff.
    +  clock.tick(5 + mm.getBackOff_());
    +  assertTrue('module "q" should be loaded', mm.isModuleLoaded('q'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "p" should not be loaded (2)', mm.isModuleLoaded('p'));
    +  clock.tick(2);
    +  assertTrue(
    +      'module "r" should not be loaded (2)', mm.isModuleLoaded('r'));
    +}
    +
    +
    +/**
    + * Test consecutive errors in loading modules.
    + */
    +function testConsecutiveErrors() {
    +  var mm = getModuleManager({'s': []});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 500));
    +
    +  // Register an error callback for consecutive failures.
    +  var firedLoadFailed = false;
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.CONSECUTIVE_FAILURES,
    +            cause);
    +        firedLoadFailed = true;
    +      });
    +
    +  mm.preloadModule('s');
    +  assertFalse(
    +      'module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
    +
    +  // Fail twice.
    +  for (var i = 0; i < 2; i++) {
    +    clock.tick(5 + mm.getBackOff_());
    +    assertFalse(
    +        'module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
    +    assertFalse(
    +        'should not fire failed callback (1)', firedLoadFailed);
    +  }
    +
    +  // Fail a third time and check that the callback is fired.
    +  clock.tick(5 + mm.getBackOff_());
    +  assertFalse(
    +      'module "s" should not be loaded (2)', mm.isModuleLoaded('s'));
    +  assertTrue(
    +      'should have fired failed callback', firedLoadFailed);
    +
    +  // Check that it doesn't attempt to load the module anymore after it has
    +  // failed.
    +  var triedLoad = false;
    +  mm.setLoader({
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn) {
    +      triedLoad = true;
    +    }});
    +
    +  // Also reset the failed callback flag and make sure it isn't called
    +  // again.
    +  firedLoadFailed = false;
    +  clock.tick(10 + mm.getBackOff_());
    +  assertFalse(
    +      'module "s" should not be loaded (3)', mm.isModuleLoaded('s'));
    +  assertFalse('No more loads should have been tried', triedLoad);
    +  assertFalse('The load failed callback should be fired only once',
    +      firedLoadFailed);
    +}
    +
    +
    +/**
    + * Test loading errors due to old code.
    + */
    +function testOldCodeGoneError() {
    +  var mm = getModuleManager({'s': []});
    +  mm.setLoader(createUnsuccessfulLoader(mm, 410));
    +
    +  // Callback checks for an old code failure
    +  var firedLoadFailed = false;
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.OLD_CODE_GONE,
    +            cause);
    +        firedLoadFailed = true;
    +      });
    +
    +  mm.preloadModule('s', 0);
    +  assertFalse(
    +      'module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
    +  assertTrue(
    +      'should have called old code gone callback', firedLoadFailed);
    +}
    +
    +
    +/**
    + * Test timeout.
    + */
    +function testTimeout() {
    +  var mm = getModuleManager({'s': []});
    +  mm.setLoader(createTimeoutLoader(mm));
    +
    +  // Callback checks for timeout
    +  var firedTimeout = false;
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      function(callbackType, id, cause) {
    +        assertEquals('Failure cause was not as expected',
    +            goog.module.ModuleManager.FailureType.TIMEOUT,
    +            cause);
    +        firedTimeout = true;
    +      });
    +
    +  mm.preloadModule('s', 0);
    +  assertFalse(
    +      'module "s" should not be loaded (0)', mm.isModuleLoaded('s'));
    +  clock.tick(5);
    +  assertFalse(
    +      'module "s" should not be loaded (1)', mm.isModuleLoaded('s'));
    +  assertTrue(
    +      'should have called timeout callback', firedTimeout);
    +}
    +
    +
    +/**
    + * Tests that an error during execOnLoad will trigger the error callback.
    + */
    +function testExecOnLoadError() {
    +  // Expect two callbacks, each of which will be called with callback type
    +  // ERROR, the right module id and failure type INIT_ERROR.
    +  var errorCallback1 = goog.testing.createFunctionMock('callback1');
    +  errorCallback1(goog.module.ModuleManager.CallbackType.ERROR, 'b',
    +      goog.module.ModuleManager.FailureType.INIT_ERROR);
    +
    +  var errorCallback2 = goog.testing.createFunctionMock('callback2');
    +  errorCallback2(goog.module.ModuleManager.CallbackType.ERROR, 'b',
    +      goog.module.ModuleManager.FailureType.INIT_ERROR);
    +
    +  errorCallback1.$replay();
    +  errorCallback2.$replay();
    +
    +  var mm = new goog.module.ModuleManager();
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  // Register the first callback before setting the module info map.
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      errorCallback1);
    +
    +  mm.setAllModuleInfo({'a': [], 'b': [], 'c': []});
    +
    +  // Register the second callback after setting the module info map.
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      errorCallback2);
    +
    +  var execOnLoadBCalled = false;
    +  mm.execOnLoad('b', function() {
    +    execOnLoadBCalled = true;
    +    throw new Error();
    +  });
    +
    +  clock.tick(5);
    +
    +  assertTrue('execOnLoad should have been called on module b.',
    +      execOnLoadBCalled);
    +  errorCallback1.$verify();
    +  errorCallback2.$verify();
    +}
    +
    +
    +/**
    + * Tests that an error during execOnLoad will trigger the error callback.
    + * Uses setAllModuleInfoString rather than setAllModuleInfo.
    + */
    +function testExecOnLoadErrorModuleInfoString() {
    +  // Expect a callback to be called with callback type ERROR, the right module
    +  // id and failure type INIT_ERROR.
    +  var errorCallback = goog.testing.createFunctionMock('callback');
    +  errorCallback(goog.module.ModuleManager.CallbackType.ERROR, 'b',
    +      goog.module.ModuleManager.FailureType.INIT_ERROR);
    +
    +  errorCallback.$replay();
    +
    +  var mm = new goog.module.ModuleManager();
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  // Register the first callback before setting the module info map.
    +  mm.registerCallback(goog.module.ModuleManager.CallbackType.ERROR,
    +      errorCallback);
    +
    +  mm.setAllModuleInfoString('a/b/c');
    +
    +  var execOnLoadBCalled = false;
    +  mm.execOnLoad('b', function() {
    +    execOnLoadBCalled = true;
    +    throw new Error();
    +  });
    +
    +  clock.tick(5);
    +
    +  assertTrue('execOnLoad should have been called on module b.',
    +      execOnLoadBCalled);
    +  errorCallback.$verify();
    +}
    +
    +
    +/**
    + * Make sure ModuleInfo objects in moduleInfoMap_ get disposed.
    + */
    +function testDispose() {
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +
    +  var moduleInfoA = mm.getModuleInfo('a');
    +  assertNotNull(moduleInfoA);
    +  var moduleInfoB = mm.getModuleInfo('b');
    +  assertNotNull(moduleInfoB);
    +  var moduleInfoC = mm.getModuleInfo('c');
    +  assertNotNull(moduleInfoC);
    +
    +  mm.dispose();
    +  assertTrue(moduleInfoA.isDisposed());
    +  assertTrue(moduleInfoB.isDisposed());
    +  assertTrue(moduleInfoC.isDisposed());
    +}
    +
    +function testDependencyOrderingWithSimpleDeps() {
    +  var mm = getModuleManager({
    +    'a': ['b', 'c'],
    +    'b': ['d'],
    +    'c': ['e', 'f'],
    +    'd': [],
    +    'e': [],
    +    'f': []
    +  });
    +  var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
    +  assertDependencyOrder(ids, mm);
    +  assertArrayEquals(['d', 'e', 'f', 'b', 'c', 'a'], ids);
    +}
    +
    +function testDependencyOrderingWithCommonDepsInDeps() {
    +  // Tests to make sure that if dependencies of the root are loaded before
    +  // their common dependencies.
    +  var mm = getModuleManager({
    +    'a': ['b', 'c'],
    +    'b': ['d'],
    +    'c': ['d'],
    +    'd': []
    +  });
    +  var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
    +  assertDependencyOrder(ids, mm);
    +  assertArrayEquals(['d', 'b', 'c', 'a'], ids);
    +}
    +
    +function testDependencyOrderingWithCommonDepsInRoot1() {
    +  // Tests the case where a dependency of the root depends on another
    +  // dependency of the root.  Irregardless of ordering in the root's
    +  // deps.
    +  var mm = getModuleManager({
    +    'a': ['b', 'c'],
    +    'b': ['c'],
    +    'c': []
    +  });
    +  var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
    +  assertDependencyOrder(ids, mm);
    +  assertArrayEquals(['c', 'b', 'a'], ids);
    +}
    +
    +function testDependencyOrderingWithCommonDepsInRoot2() {
    +  // Tests the case where a dependency of the root depends on another
    +  // dependency of the root.  Irregardless of ordering in the root's
    +  // deps.
    +  var mm = getModuleManager({
    +    'a': ['b', 'c'],
    +    'b': [],
    +    'c': ['b']
    +  });
    +  var ids = mm.getNotYetLoadedTransitiveDepIds_('a');
    +  assertDependencyOrder(ids, mm);
    +  assertArrayEquals(['b', 'c', 'a'], ids);
    +}
    +
    +function testDependencyOrderingWithGmailExample() {
    +  // Real dependency graph taken from gmail.
    +  var mm = getModuleManager({
    +    's': ['dp', 'ml', 'md'],
    +    'dp': ['a'],
    +    'ml': ['ld', 'm'],
    +    'ld': ['a'],
    +    'm': ['ad', 'mh', 'n'],
    +    'md': ['mh', 'ld'],
    +    'a': [],
    +    'mh': [],
    +    'ad': [],
    +    'n': []
    +  });
    +
    +  mm.setLoaded('a');
    +  mm.setLoaded('m');
    +  mm.setLoaded('n');
    +  mm.setLoaded('ad');
    +  mm.setLoaded('mh');
    +
    +  var ids = mm.getNotYetLoadedTransitiveDepIds_('s');
    +  assertDependencyOrder(ids, mm);
    +  assertArrayEquals(['ld', 'dp', 'ml', 'md', 's'], ids);
    +}
    +
    +function assertDependencyOrder(list, mm) {
    +  var seen = {};
    +  for (var i = 0; i < list.length; i++) {
    +    var id = list[i];
    +    seen[id] = true;
    +    var deps = mm.getModuleInfo(id).getDependencies();
    +    for (var j = 0; j < deps.length; j++) {
    +      var dep = deps[j];
    +      assertTrue('Unresolved dependency [' + dep + '] for [' + id + '].',
    +          seen[dep] || mm.getModuleInfo(dep).isLoaded());
    +    }
    +  }
    +}
    +
    +function testRegisterInitializationCallback() {
    +  var initCalled = 0;
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithRegisterInitCallback(mm,
    +      function() {
    +        ++initCalled;
    +      }));
    +  execOnLoad_(mm);
    +  // execOnLoad_ loads modules a and c
    +  assertTrue(initCalled == 2);
    +}
    +
    +function createSuccessfulNonBatchLoaderWithRegisterInitCallback(
    +    moduleMgr, fn) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      moduleMgr.beforeLoadModuleCode(ids[0]);
    +      moduleMgr.registerInitializationCallback(fn);
    +      setTimeout(function() {
    +        moduleMgr.setLoaded(ids[0]);
    +        moduleMgr.afterLoadModuleCode(ids[0]);
    +        if (opt_successFn) {
    +          opt_successFn();
    +        }
    +      }, 5);
    +    }};
    +}
    +
    +function testSetModuleConstructor() {
    +  var initCalled = 0;
    +  var mm = getModuleManager({'a': [], 'b': [], 'c': []});
    +  var info = {
    +    'a': { ctor: AModule, count: 0 },
    +    'b': { ctor: BModule, count: 0 },
    +    'c': { ctor: CModule, count: 0 }
    +  };
    +  function AModule() {
    +    ++info['a'].count;
    +    goog.module.BaseModule.call(this);
    +  }
    +  goog.inherits(AModule, goog.module.BaseModule);
    +  function BModule() {
    +    ++info['b'].count;
    +    goog.module.BaseModule.call(this);
    +  }
    +  goog.inherits(BModule, goog.module.BaseModule);
    +  function CModule() {
    +    ++info['c'].count;
    +    goog.module.BaseModule.call(this);
    +  }
    +  goog.inherits(CModule, goog.module.BaseModule);
    +
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(mm, info));
    +  execOnLoad_(mm);
    +  assertTrue(info['a'].count == 1);
    +  assertTrue(info['b'].count == 0);
    +  assertTrue(info['c'].count == 1);
    +  assertTrue(mm.getModuleInfo('a').getModule() instanceof AModule);
    +  assertTrue(mm.getModuleInfo('c').getModule() instanceof CModule);
    +}
    +
    +
    +/**
    + * Tests that a call to load the loading module during module initialization
    + * doesn't trigger a second load.
    + */
    +function testLoadWhenInitializing() {
    +  var mm = getModuleManager({'a': []});
    +  mm.setLoader(createSuccessfulNonBatchLoader(mm));
    +
    +  var info = {
    +    'a': { ctor: AModule, count: 0 }
    +  };
    +  function AModule() {
    +    ++info['a'].count;
    +    goog.module.BaseModule.call(this);
    +  }
    +  goog.inherits(AModule, goog.module.BaseModule);
    +  AModule.prototype.initialize = function() {
    +    mm.load('a');
    +  };
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(mm, info));
    +  mm.preloadModule('a');
    +  clock.tick(5);
    +  assertEquals(info['a'].count, 1);
    +}
    +
    +function testErrorInEarlyCallback() {
    +  var errback = goog.testing.recordFunction();
    +  var callback = goog.testing.recordFunction();
    +  var mm = getModuleManager({'a': [], 'b': ['a']});
    +  mm.getModuleInfo('a').registerEarlyCallback(goog.functions.error('error'));
    +  mm.getModuleInfo('a').registerCallback(callback);
    +  mm.getModuleInfo('a').registerErrback(errback);
    +
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(
    +      mm, createModulesFor('a', 'b')));
    +  mm.preloadModule('b');
    +  clock.tick(5);
    +
    +  assertEquals(0, callback.getCallCount());
    +  assertEquals(1, errback.getCallCount());
    +  assertEquals(goog.module.ModuleManager.FailureType.INIT_ERROR,
    +      errback.getLastCall().getArguments()[0]);
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +  assertFalse(mm.getModuleInfo('b').isLoaded());
    +
    +  clock.tick(5);
    +  assertTrue(mm.getModuleInfo('b').isLoaded());
    +}
    +
    +function testErrorInNormalCallback() {
    +  var earlyCallback = goog.testing.recordFunction();
    +  var errback = goog.testing.recordFunction();
    +  var mm = getModuleManager({'a': [], 'b': ['a']});
    +  mm.getModuleInfo('a').registerEarlyCallback(earlyCallback);
    +  mm.getModuleInfo('a').registerEarlyCallback(goog.functions.error('error'));
    +  mm.getModuleInfo('a').registerErrback(errback);
    +
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(
    +      mm, createModulesFor('a', 'b')));
    +  mm.preloadModule('b');
    +  clock.tick(10);
    +
    +  assertEquals(1, errback.getCallCount());
    +  assertEquals(goog.module.ModuleManager.FailureType.INIT_ERROR,
    +      errback.getLastCall().getArguments()[0]);
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +  assertTrue(mm.getModuleInfo('b').isLoaded());
    +}
    +
    +function testErrorInErrback() {
    +  var mm = getModuleManager({'a': [], 'b': ['a']});
    +  mm.getModuleInfo('a').registerCallback(goog.functions.error('error1'));
    +  mm.getModuleInfo('a').registerErrback(goog.functions.error('error2'));
    +
    +  mm.setLoader(createSuccessfulNonBatchLoaderWithConstructor(
    +      mm, createModulesFor('a', 'b')));
    +  mm.preloadModule('a');
    +  var e = assertThrows(function() {
    +    clock.tick(10);
    +  });
    +  assertContains('Module errback failure', e.message);
    +  if (!goog.userAgent.IE) {
    +    assertContains('error2', e.message);
    +  }
    +
    +  assertTrue(mm.getModuleInfo('a').isLoaded());
    +}
    +
    +function createModulesFor(var_args) {
    +  var result = {};
    +  for (var i = 0; i < arguments.length; i++) {
    +    var key = arguments[i];
    +    result[key] = {ctor: goog.module.BaseModule};
    +  }
    +  return result;
    +}
    +
    +function createSuccessfulNonBatchLoaderWithConstructor(moduleMgr, info) {
    +  return {
    +    loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn,
    +        opt_timeoutFn) {
    +      setTimeout(function() {
    +        moduleMgr.beforeLoadModuleCode(ids[0]);
    +        moduleMgr.setModuleConstructor(info[ids[0]].ctor);
    +        moduleMgr.setLoaded(ids[0]);
    +        moduleMgr.afterLoadModuleCode(ids[0]);
    +        if (opt_successFn) {
    +          opt_successFn();
    +        }
    +      }, 5);
    +    }};
    +}
    +
    +function testInitCallbackInBaseModule() {
    +  var mm = new goog.module.ModuleManager();
    +  var called = false;
    +  var context;
    +  mm.registerInitializationCallback(function(mcontext) {
    +    called = true;
    +    context = mcontext;
    +  });
    +  mm.setAllModuleInfo({'a': [], 'b': ['a']});
    +  assertTrue('Base initialization not called', called);
    +  assertNull('Context should still be null', context);
    +
    +  var mm = new goog.module.ModuleManager();
    +  called = false;
    +  mm.registerInitializationCallback(function(mcontext) {
    +    called = true;
    +    context = mcontext;
    +  });
    +  var appContext = {};
    +  mm.setModuleContext(appContext);
    +  assertTrue('Base initialization not called after setModuleContext', called);
    +  assertEquals('Did not receive module context', appContext, context);
    +}
    +
    +function testSetAllModuleInfoString() {
    +  var info = 'base/one:0/two:0/three:0,1,2/four:0,3/five:';
    +  var mm = new goog.module.ModuleManager();
    +  mm.setAllModuleInfoString(info);
    +
    +  assertNotNull('Base should exist', mm.getModuleInfo('base'));
    +  assertNotNull('One should exist', mm.getModuleInfo('one'));
    +  assertNotNull('Two should exist', mm.getModuleInfo('two'));
    +  assertNotNull('Three should exist', mm.getModuleInfo('three'));
    +  assertNotNull('Four should exist', mm.getModuleInfo('four'));
    +  assertNotNull('Five should exist', mm.getModuleInfo('five'));
    +
    +  assertArrayEquals(['base', 'one', 'two'],
    +      mm.getModuleInfo('three').getDependencies());
    +  assertArrayEquals(['base', 'three'],
    +      mm.getModuleInfo('four').getDependencies());
    +  assertArrayEquals([],
    +      mm.getModuleInfo('five').getDependencies());
    +}
    +
    +function testSetAllModuleInfoStringWithEmptyString() {
    +  var mm = new goog.module.ModuleManager();
    +  var called = false;
    +  var context;
    +  mm.registerInitializationCallback(function(mcontext) {
    +    called = true;
    +    context = mcontext;
    +  });
    +  mm.setAllModuleInfoString('');
    +  assertTrue('Initialization not called', called);
    +}
    +
    +function testBackOffAmounts() {
    +  var mm = new goog.module.ModuleManager();
    +  assertEquals(0, mm.getBackOff_());
    +
    +  mm.consecutiveFailures_++;
    +  assertEquals(5000, mm.getBackOff_());
    +
    +  mm.consecutiveFailures_++;
    +  assertEquals(20000, mm.getBackOff_());
    +}
    +
    +
    +/**
    + * Tests that the IDLE callbacks are executed for active->idle transitions
    + * after setAllModuleInfoString with currently loading modules.
    + */
    +function testIdleCallbackWithInitialModules() {
    +  var callback = goog.testing.recordFunction();
    +
    +  var mm = new goog.module.ModuleManager();
    +  mm.setAllModuleInfoString('a', ['a']);
    +  mm.registerCallback(
    +      goog.module.ModuleManager.CallbackType.IDLE, callback);
    +
    +  assertTrue(mm.isActive());
    +
    +  mm.beforeLoadModuleCode('a');
    +
    +  assertEquals(0, callback.getCallCount());
    +
    +  mm.setLoaded('a');
    +  mm.afterLoadModuleCode('a');
    +
    +  assertFalse(mm.isActive());
    +
    +  assertEquals(1, callback.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/module/testdata/modA_1.js b/mapbox-gl/closure-library/closure/goog/module/testdata/modA_1.js
    new file mode 100644
    index 0000000000..8a013b9aea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/testdata/modA_1.js
    @@ -0,0 +1,26 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview File #1 of module A.
    + */
    +
    +goog.provide('goog.module.testdata.modA_1');
    +
    +
    +goog.setTestOnly('goog.module.testdata.modA_1');
    +
    +if (window.modA1Loaded) throw Error('modA_1 loaded twice');
    +window.modA1Loaded = true;
    diff --git a/mapbox-gl/closure-library/closure/goog/module/testdata/modA_2.js b/mapbox-gl/closure-library/closure/goog/module/testdata/modA_2.js
    new file mode 100644
    index 0000000000..06c0828f1a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/testdata/modA_2.js
    @@ -0,0 +1,29 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview File #2 of module A.
    + */
    +
    +goog.provide('goog.module.testdata.modA_2');
    +
    +goog.setTestOnly('goog.module.testdata.modA_2');
    +
    +goog.require('goog.module.ModuleManager');
    +
    +if (window.modA2Loaded) throw Error('modA_2 loaded twice');
    +window.modA2Loaded = true;
    +
    +goog.module.ModuleManager.getInstance().setLoaded('modA');
    diff --git a/mapbox-gl/closure-library/closure/goog/module/testdata/modB_1.js b/mapbox-gl/closure-library/closure/goog/module/testdata/modB_1.js
    new file mode 100644
    index 0000000000..308913753b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/module/testdata/modB_1.js
    @@ -0,0 +1,33 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview File #1 of module B.
    + */
    +
    +goog.provide('goog.module.testdata.modB_1');
    +
    +goog.setTestOnly('goog.module.testdata.modB_1');
    +
    +goog.require('goog.module.ModuleManager');
    +
    +function throwErrorInModuleB() {
    +  throw Error();
    +}
    +
    +if (window.modB1Loaded) throw Error('modB_1 loaded twice');
    +window.modB1Loaded = true;
    +
    +goog.module.ModuleManager.getInstance().setLoaded('modB');
    diff --git a/mapbox-gl/closure-library/closure/goog/net/browserchannel.js b/mapbox-gl/closure-library/closure/goog/net/browserchannel.js
    new file mode 100644
    index 0000000000..9ebfe93b5b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/browserchannel.js
    @@ -0,0 +1,2765 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the BrowserChannel class.  A BrowserChannel
    + * simulates a bidirectional socket over HTTP. It is the basis of the
    + * Gmail Chat IM connections to the server.
    + *
    + * Typical usage will look like
    + *  var handler = [handler object];
    + *  var channel = new BrowserChannel(clientVersion);
    + *  channel.setHandler(handler);
    + *  channel.connect('channel/test', 'channel/bind');
    + *
    + * See goog.net.BrowserChannel.Handler for the handler interface.
    + *
    + */
    +
    +
    +goog.provide('goog.net.BrowserChannel');
    +goog.provide('goog.net.BrowserChannel.Error');
    +goog.provide('goog.net.BrowserChannel.Event');
    +goog.provide('goog.net.BrowserChannel.Handler');
    +goog.provide('goog.net.BrowserChannel.LogSaver');
    +goog.provide('goog.net.BrowserChannel.QueuedMap');
    +goog.provide('goog.net.BrowserChannel.ServerReachability');
    +goog.provide('goog.net.BrowserChannel.ServerReachabilityEvent');
    +goog.provide('goog.net.BrowserChannel.Stat');
    +goog.provide('goog.net.BrowserChannel.StatEvent');
    +goog.provide('goog.net.BrowserChannel.State');
    +goog.provide('goog.net.BrowserChannel.TimingEvent');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.debug.TextFormatter');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.json.EvalJsonProcessor');
    +goog.require('goog.log');
    +goog.require('goog.net.BrowserTestChannel');
    +goog.require('goog.net.ChannelDebug');
    +goog.require('goog.net.ChannelRequest');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.tmpnetwork');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.structs');
    +goog.require('goog.structs.CircularBuffer');
    +
    +
    +
    +/**
    + * Encapsulates the logic for a single BrowserChannel.
    + *
    + * @param {string=} opt_clientVersion An application-specific version number
    + *        that is sent to the server when connected.
    + * @param {Array<string>=} opt_firstTestResults Previously determined results
    + *        of the first browser channel test.
    + * @param {boolean=} opt_secondTestResults Previously determined results
    + *        of the second browser channel test.
    + * @constructor
    + */
    +goog.net.BrowserChannel = function(opt_clientVersion, opt_firstTestResults,
    +    opt_secondTestResults) {
    +  /**
    +   * The application specific version that is passed to the server.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.clientVersion_ = opt_clientVersion || null;
    +
    +  /**
    +   * The current state of the BrowserChannel. It should be one of the
    +   * goog.net.BrowserChannel.State constants.
    +   * @type {!goog.net.BrowserChannel.State}
    +   * @private
    +   */
    +  this.state_ = goog.net.BrowserChannel.State.INIT;
    +
    +  /**
    +   * An array of queued maps that need to be sent to the server.
    +   * @type {Array<goog.net.BrowserChannel.QueuedMap>}
    +   * @private
    +   */
    +  this.outgoingMaps_ = [];
    +
    +  /**
    +   * An array of dequeued maps that we have either received a non-successful
    +   * response for, or no response at all, and which therefore may or may not
    +   * have been received by the server.
    +   * @type {Array<goog.net.BrowserChannel.QueuedMap>}
    +   * @private
    +   */
    +  this.pendingMaps_ = [];
    +
    +  /**
    +   * The channel debug used for browserchannel logging
    +   * @type {!goog.net.ChannelDebug}
    +   * @private
    +   */
    +  this.channelDebug_ = new goog.net.ChannelDebug();
    +
    +  /**
    +   * Parser for a response payload. Defaults to use
    +   * {@code goog.json.unsafeParse}. The parser should return an array.
    +   * @type {!goog.string.Parser}
    +   * @private
    +   */
    +  this.parser_ = new goog.json.EvalJsonProcessor(null, true);
    +
    +  /**
    +   * An array of results for the first browser channel test call.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.firstTestResults_ = opt_firstTestResults || null;
    +
    +  /**
    +   * The results of the second browser channel test. True implies the
    +   * connection is buffered, False means unbuffered, null means that
    +   * the results are not available.
    +   * @private
    +   */
    +  this.secondTestResults_ = goog.isDefAndNotNull(opt_secondTestResults) ?
    +      opt_secondTestResults : null;
    +};
    +
    +
    +
    +/**
    + * Simple container class for a (mapId, map) pair.
    + * @param {number} mapId The id for this map.
    + * @param {Object|goog.structs.Map} map The map itself.
    + * @param {Object=} opt_context The context associated with the map.
    + * @constructor
    + * @final
    + */
    +goog.net.BrowserChannel.QueuedMap = function(mapId, map, opt_context) {
    +  /**
    +   * The id for this map.
    +   * @type {number}
    +   */
    +  this.mapId = mapId;
    +
    +  /**
    +   * The map itself.
    +   * @type {Object|goog.structs.Map}
    +   */
    +  this.map = map;
    +
    +  /**
    +   * The context for the map.
    +   * @type {Object}
    +   */
    +  this.context = opt_context || null;
    +};
    +
    +
    +/**
    + * Extra HTTP headers to add to all the requests sent to the server.
    + * @type {Object}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.extraHeaders_ = null;
    +
    +
    +/**
    + * Extra parameters to add to all the requests sent to the server.
    + * @type {Object}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.extraParams_ = null;
    +
    +
    +/**
    + * The current ChannelRequest object for the forwardchannel.
    + * @type {goog.net.ChannelRequest?}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelRequest_ = null;
    +
    +
    +/**
    + * The ChannelRequest object for the backchannel.
    + * @type {goog.net.ChannelRequest?}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.backChannelRequest_ = null;
    +
    +
    +/**
    + * The relative path (in the context of the the page hosting the browser
    + * channel) for making requests to the server.
    + * @type {?string}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.path_ = null;
    +
    +
    +/**
    + * The absolute URI for the forwardchannel request.
    + * @type {goog.Uri}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelUri_ = null;
    +
    +
    +/**
    + * The absolute URI for the backchannel request.
    + * @type {goog.Uri}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.backChannelUri_ = null;
    +
    +
    +/**
    + * A subdomain prefix for using a subdomain in IE for the backchannel
    + * requests.
    + * @type {?string}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.hostPrefix_ = null;
    +
    +
    +/**
    + * Whether we allow the use of a subdomain in IE for the backchannel requests.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.allowHostPrefix_ = true;
    +
    +
    +/**
    + * The next id to use for the RID (request identifier) parameter. This
    + * identifier uniquely identifies the forward channel request.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.nextRid_ = 0;
    +
    +
    +/**
    + * The id to use for the next outgoing map. This identifier uniquely
    + * identifies a sent map.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.nextMapId_ = 0;
    +
    +
    +/**
    + * Whether to fail forward-channel requests after one try, or after a few tries.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.failFast_ = false;
    +
    +
    +/**
    + * The handler that receive callbacks for state changes and data.
    + * @type {goog.net.BrowserChannel.Handler}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.handler_ = null;
    +
    +
    +/**
    + * Timer identifier for asynchronously making a forward channel request.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelTimerId_ = null;
    +
    +
    +/**
    + * Timer identifier for asynchronously making a back channel request.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.backChannelTimerId_ = null;
    +
    +
    +/**
    + * Timer identifier for the timer that waits for us to retry the backchannel in
    + * the case where it is dead and no longer receiving data.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.deadBackChannelTimerId_ = null;
    +
    +
    +/**
    + * The BrowserTestChannel object which encapsulates the logic for determining
    + * interesting network conditions about the client.
    + * @type {goog.net.BrowserTestChannel?}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.connectionTest_ = null;
    +
    +
    +/**
    + * Whether the client's network conditions can support chunked responses.
    + * @type {?boolean}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.useChunked_ = null;
    +
    +
    +/**
    + * Whether chunked mode is allowed. In certain debugging situations, it's
    + * useful to disable this.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.allowChunkedMode_ = true;
    +
    +
    +/**
    + * The array identifier of the last array received from the server for the
    + * backchannel request.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.lastArrayId_ = -1;
    +
    +
    +/**
    + * The array identifier of the last array sent by the server that we know about.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.lastPostResponseArrayId_ = -1;
    +
    +
    +/**
    + * The last status code received.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.lastStatusCode_ = -1;
    +
    +
    +/**
    + * Number of times we have retried the current forward channel request.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelRetryCount_ = 0;
    +
    +
    +/**
    + * Number of times it a row that we have retried the current back channel
    + * request and received no data.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.backChannelRetryCount_ = 0;
    +
    +
    +/**
    + * The attempt id for the current back channel request. Starts at 1 and
    + * increments for each reconnect. The server uses this to log if our connection
    + * is flaky or not.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.backChannelAttemptId_;
    +
    +
    +/**
    + * The base part of the time before firing next retry request. Default is 5
    + * seconds. Note that a random delay is added (see {@link retryDelaySeedMs_})
    + * for all retries, and linear backoff is applied to the sum for subsequent
    + * retries.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.baseRetryDelayMs_ = 5 * 1000;
    +
    +
    +/**
    + * A random time between 0 and this number of MS is added to the
    + * {@link baseRetryDelayMs_}. Default is 10 seconds.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.retryDelaySeedMs_ = 10 * 1000;
    +
    +
    +/**
    + * Maximum number of attempts to connect to the server for forward channel
    + * requests. Defaults to 2.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelMaxRetries_ = 2;
    +
    +
    +/**
    + * The timeout in milliseconds for a forward channel request. Defaults to 20
    + * seconds. Note that part of this timeout can be randomized.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.forwardChannelRequestTimeoutMs_ = 20 * 1000;
    +
    +
    +/**
    + * A throttle time in ms for readystatechange events for the backchannel.
    + * Useful for throttling when ready state is INTERACTIVE (partial data).
    + *
    + * This throttle is useful if the server sends large data chunks down the
    + * backchannel.  It prevents examining XHR partial data on every
    + * readystate change event.  This is useful because large chunks can
    + * trigger hundreds of readystatechange events, each of which takes ~5ms
    + * or so to handle, in turn making the UI unresponsive for a significant period.
    + *
    + * If set to zero no throttle is used.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_ = 0;
    +
    +
    +/**
    + * Whether cross origin requests are supported for the browser channel.
    + *
    + * See {@link goog.net.XhrIo#setWithCredentials}.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.supportsCrossDomainXhrs_ = false;
    +
    +
    +/**
    + * The latest protocol version that this class supports. We request this version
    + * from the server when opening the connection. Should match
    + * com.google.net.browserchannel.BrowserChannel.LATEST_CHANNEL_VERSION.
    + * @type {number}
    + */
    +goog.net.BrowserChannel.LATEST_CHANNEL_VERSION = 8;
    +
    +
    +/**
    + * The channel version that we negotiated with the server for this session.
    + * Starts out as the version we request, and then is changed to the negotiated
    + * version after the initial open.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.channelVersion_ =
    +    goog.net.BrowserChannel.LATEST_CHANNEL_VERSION;
    +
    +
    +/**
    + * Enum type for the browser channel state machine.
    + * @enum {number}
    + */
    +goog.net.BrowserChannel.State = {
    +  /** The channel is closed. */
    +  CLOSED: 0,
    +
    +  /** The channel has been initialized but hasn't yet initiated a connection. */
    +  INIT: 1,
    +
    +  /** The channel is in the process of opening a connection to the server. */
    +  OPENING: 2,
    +
    +  /** The channel is open. */
    +  OPENED: 3
    +};
    +
    +
    +/**
    + * The timeout in milliseconds for a forward channel request.
    + * @type {number}
    + */
    +goog.net.BrowserChannel.FORWARD_CHANNEL_RETRY_TIMEOUT = 20 * 1000;
    +
    +
    +/**
    + * Maximum number of attempts to connect to the server for back channel
    + * requests.
    + * @type {number}
    + */
    +goog.net.BrowserChannel.BACK_CHANNEL_MAX_RETRIES = 3;
    +
    +
    +/**
    + * A number in MS of how long we guess the maxmium amount of time a round trip
    + * to the server should take. In the future this could be substituted with a
    + * real measurement of the RTT.
    + * @type {number}
    + */
    +goog.net.BrowserChannel.RTT_ESTIMATE = 3 * 1000;
    +
    +
    +/**
    + * When retrying for an inactive channel, we will multiply the total delay by
    + * this number.
    + * @type {number}
    + */
    +goog.net.BrowserChannel.INACTIVE_CHANNEL_RETRY_FACTOR = 2;
    +
    +
    +/**
    + * Enum type for identifying a BrowserChannel error.
    + * @enum {number}
    + */
    +goog.net.BrowserChannel.Error = {
    +  /** Value that indicates no error has occurred. */
    +  OK: 0,
    +
    +  /** An error due to a request failing. */
    +  REQUEST_FAILED: 2,
    +
    +  /** An error due to the user being logged out. */
    +  LOGGED_OUT: 4,
    +
    +  /** An error due to server response which contains no data. */
    +  NO_DATA: 5,
    +
    +  /** An error due to a server response indicating an unknown session id */
    +  UNKNOWN_SESSION_ID: 6,
    +
    +  /** An error due to a server response requesting to stop the channel. */
    +  STOP: 7,
    +
    +  /** A general network error. */
    +  NETWORK: 8,
    +
    +  /** An error due to the channel being blocked by a network administrator. */
    +  BLOCKED: 9,
    +
    +  /** An error due to bad data being returned from the server. */
    +  BAD_DATA: 10,
    +
    +  /** An error due to a response that doesn't start with the magic cookie. */
    +  BAD_RESPONSE: 11,
    +
    +  /** ActiveX is blocked by the machine's admin settings. */
    +  ACTIVE_X_BLOCKED: 12
    +};
    +
    +
    +/**
    + * Internal enum type for the two browser channel channel types.
    + * @enum {number}
    + * @private
    + */
    +goog.net.BrowserChannel.ChannelType_ = {
    +  FORWARD_CHANNEL: 1,
    +
    +  BACK_CHANNEL: 2
    +};
    +
    +
    +/**
    + * The maximum number of maps that can be sent in one POST. Should match
    + * com.google.net.browserchannel.BrowserChannel.MAX_MAPS_PER_REQUEST.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserChannel.MAX_MAPS_PER_REQUEST_ = 1000;
    +
    +
    +/**
    + * Singleton event target for firing stat events
    + * @type {goog.events.EventTarget}
    + * @private
    + */
    +goog.net.BrowserChannel.statEventTarget_ = new goog.events.EventTarget();
    +
    +
    +/**
    + * Events fired by BrowserChannel and associated objects
    + * @const
    + */
    +goog.net.BrowserChannel.Event = {};
    +
    +
    +/**
    + * Stat Event that fires when things of interest happen that may be useful for
    + * applications to know about for stats or debugging purposes. This event fires
    + * on the EventTarget returned by getStatEventTarget.
    + */
    +goog.net.BrowserChannel.Event.STAT_EVENT = 'statevent';
    +
    +
    +
    +/**
    + * Event class for goog.net.BrowserChannel.Event.STAT_EVENT
    + *
    + * @param {goog.events.EventTarget} eventTarget The stat event target for
    +       the browser channel.
    + * @param {goog.net.BrowserChannel.Stat} stat The stat.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.net.BrowserChannel.StatEvent = function(eventTarget, stat) {
    +  goog.events.Event.call(this, goog.net.BrowserChannel.Event.STAT_EVENT,
    +      eventTarget);
    +
    +  /**
    +   * The stat
    +   * @type {goog.net.BrowserChannel.Stat}
    +   */
    +  this.stat = stat;
    +
    +};
    +goog.inherits(goog.net.BrowserChannel.StatEvent, goog.events.Event);
    +
    +
    +/**
    + * An event that fires when POST requests complete successfully, indicating
    + * the size of the POST and the round trip time.
    + * This event fires on the EventTarget returned by getStatEventTarget.
    + */
    +goog.net.BrowserChannel.Event.TIMING_EVENT = 'timingevent';
    +
    +
    +
    +/**
    + * Event class for goog.net.BrowserChannel.Event.TIMING_EVENT
    + *
    + * @param {goog.events.EventTarget} target The stat event target for
    +       the browser channel.
    + * @param {number} size The number of characters in the POST data.
    + * @param {number} rtt The total round trip time from POST to response in MS.
    + * @param {number} retries The number of times the POST had to be retried.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.net.BrowserChannel.TimingEvent = function(target, size, rtt, retries) {
    +  goog.events.Event.call(this, goog.net.BrowserChannel.Event.TIMING_EVENT,
    +      target);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.size = size;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.rtt = rtt;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.retries = retries;
    +
    +};
    +goog.inherits(goog.net.BrowserChannel.TimingEvent, goog.events.Event);
    +
    +
    +/**
    + * The type of event that occurs every time some information about how reachable
    + * the server is is discovered.
    + */
    +goog.net.BrowserChannel.Event.SERVER_REACHABILITY_EVENT =
    +    'serverreachability';
    +
    +
    +/**
    + * Types of events which reveal information about the reachability of the
    + * server.
    + * @enum {number}
    + */
    +goog.net.BrowserChannel.ServerReachability = {
    +  REQUEST_MADE: 1,
    +  REQUEST_SUCCEEDED: 2,
    +  REQUEST_FAILED: 3,
    +  BACK_CHANNEL_ACTIVITY: 4
    +};
    +
    +
    +
    +/**
    + * Event class for goog.net.BrowserChannel.Event.SERVER_REACHABILITY_EVENT.
    + *
    + * @param {goog.events.EventTarget} target The stat event target for
    +       the browser channel.
    + * @param {goog.net.BrowserChannel.ServerReachability} reachabilityType The
    + *     reachability event type.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.net.BrowserChannel.ServerReachabilityEvent = function(target,
    +    reachabilityType) {
    +  goog.events.Event.call(this,
    +      goog.net.BrowserChannel.Event.SERVER_REACHABILITY_EVENT, target);
    +
    +  /**
    +   * @type {goog.net.BrowserChannel.ServerReachability}
    +   */
    +  this.reachabilityType = reachabilityType;
    +};
    +goog.inherits(goog.net.BrowserChannel.ServerReachabilityEvent,
    +    goog.events.Event);
    +
    +
    +/**
    + * Enum that identifies events for statistics that are interesting to track.
    + * TODO(user) - Change name not to use Event or use EventTarget
    + * @enum {number}
    + */
    +goog.net.BrowserChannel.Stat = {
    +  /** Event indicating a new connection attempt. */
    +  CONNECT_ATTEMPT: 0,
    +
    +  /** Event indicating a connection error due to a general network problem. */
    +  ERROR_NETWORK: 1,
    +
    +  /**
    +   * Event indicating a connection error that isn't due to a general network
    +   * problem.
    +   */
    +  ERROR_OTHER: 2,
    +
    +  /** Event indicating the start of test stage one. */
    +  TEST_STAGE_ONE_START: 3,
    +
    +
    +  /** Event indicating the channel is blocked by a network administrator. */
    +  CHANNEL_BLOCKED: 4,
    +
    +  /** Event indicating the start of test stage two. */
    +  TEST_STAGE_TWO_START: 5,
    +
    +  /** Event indicating the first piece of test data was received. */
    +  TEST_STAGE_TWO_DATA_ONE: 6,
    +
    +  /**
    +   * Event indicating that the second piece of test data was received and it was
    +   * recieved separately from the first.
    +   */
    +  TEST_STAGE_TWO_DATA_TWO: 7,
    +
    +  /** Event indicating both pieces of test data were received simultaneously. */
    +  TEST_STAGE_TWO_DATA_BOTH: 8,
    +
    +  /** Event indicating stage one of the test request failed. */
    +  TEST_STAGE_ONE_FAILED: 9,
    +
    +  /** Event indicating stage two of the test request failed. */
    +  TEST_STAGE_TWO_FAILED: 10,
    +
    +  /**
    +   * Event indicating that a buffering proxy is likely between the client and
    +   * the server.
    +   */
    +  PROXY: 11,
    +
    +  /**
    +   * Event indicating that no buffering proxy is likely between the client and
    +   * the server.
    +   */
    +  NOPROXY: 12,
    +
    +  /** Event indicating an unknown SID error. */
    +  REQUEST_UNKNOWN_SESSION_ID: 13,
    +
    +  /** Event indicating a bad status code was received. */
    +  REQUEST_BAD_STATUS: 14,
    +
    +  /** Event indicating incomplete data was received */
    +  REQUEST_INCOMPLETE_DATA: 15,
    +
    +  /** Event indicating bad data was received */
    +  REQUEST_BAD_DATA: 16,
    +
    +  /** Event indicating no data was received when data was expected. */
    +  REQUEST_NO_DATA: 17,
    +
    +  /** Event indicating a request timeout. */
    +  REQUEST_TIMEOUT: 18,
    +
    +  /**
    +   * Event indicating that the server never received our hanging GET and so it
    +   * is being retried.
    +   */
    +  BACKCHANNEL_MISSING: 19,
    +
    +  /**
    +   * Event indicating that we have determined that our hanging GET is not
    +   * receiving data when it should be. Thus it is dead dead and will be retried.
    +   */
    +  BACKCHANNEL_DEAD: 20,
    +
    +  /**
    +   * The browser declared itself offline during the lifetime of a request, or
    +   * was offline when a request was initially made.
    +   */
    +  BROWSER_OFFLINE: 21,
    +
    +  /** ActiveX is blocked by the machine's admin settings. */
    +  ACTIVE_X_BLOCKED: 22
    +};
    +
    +
    +/**
    + * The normal response for forward channel requests.
    + * Used only before version 8 of the protocol.
    + * @type {string}
    + */
    +goog.net.BrowserChannel.MAGIC_RESPONSE_COOKIE = 'y2f%';
    +
    +
    +/**
    + * A guess at a cutoff at which to no longer assume the backchannel is dead
    + * when we are slow to receive data. Number in bytes.
    + *
    + * Assumption: The worst bandwidth we work on is 50 kilobits/sec
    + * 50kbits/sec * (1 byte / 8 bits) * 6 sec dead backchannel timeout
    + * @type {number}
    + */
    +goog.net.BrowserChannel.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF = 37500;
    +
    +
    +/**
    + * Returns the browserchannel logger.
    + *
    + * @return {!goog.net.ChannelDebug} The channel debug object.
    + */
    +goog.net.BrowserChannel.prototype.getChannelDebug = function() {
    +  return this.channelDebug_;
    +};
    +
    +
    +/**
    + * Set the browserchannel logger.
    + * TODO(user): Add interface for channel loggers or remove this function.
    + *
    + * @param {goog.net.ChannelDebug} channelDebug The channel debug object.
    + */
    +goog.net.BrowserChannel.prototype.setChannelDebug = function(
    +    channelDebug) {
    +  if (goog.isDefAndNotNull(channelDebug)) {
    +    this.channelDebug_ = channelDebug;
    +  }
    +};
    +
    +
    +/**
    + * Allows the application to set an execution hooks for when BrowserChannel
    + * starts processing requests. This is useful to track timing or logging
    + * special information. The function takes no parameters and return void.
    + * @param {Function} startHook  The function for the start hook.
    + */
    +goog.net.BrowserChannel.setStartThreadExecutionHook = function(startHook) {
    +  goog.net.BrowserChannel.startExecutionHook_ = startHook;
    +};
    +
    +
    +/**
    + * Allows the application to set an execution hooks for when BrowserChannel
    + * stops processing requests. This is useful to track timing or logging
    + * special information. The function takes no parameters and return void.
    + * @param {Function} endHook  The function for the end hook.
    + */
    +goog.net.BrowserChannel.setEndThreadExecutionHook = function(endHook) {
    +  goog.net.BrowserChannel.endExecutionHook_ = endHook;
    +};
    +
    +
    +/**
    + * Application provided execution hook for the start hook.
    + *
    + * @type {Function}
    + * @private
    + */
    +goog.net.BrowserChannel.startExecutionHook_ = function() { };
    +
    +
    +/**
    + * Application provided execution hook for the end hook.
    + *
    + * @type {Function}
    + * @private
    + */
    +goog.net.BrowserChannel.endExecutionHook_ = function() { };
    +
    +
    +/**
    + * Instantiates a ChannelRequest with the given parameters. Overidden in tests.
    + *
    + * @param {goog.net.BrowserChannel|goog.net.BrowserTestChannel} channel
    + *     The BrowserChannel that owns this request.
    + * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for
    + *     logging.
    + * @param {string=} opt_sessionId  The session id for the channel.
    + * @param {string|number=} opt_requestId  The request id for this request.
    + * @param {number=} opt_retryId  The retry id for this request.
    + * @return {!goog.net.ChannelRequest} The created channel request.
    + */
    +goog.net.BrowserChannel.createChannelRequest = function(channel, channelDebug,
    +    opt_sessionId, opt_requestId, opt_retryId) {
    +  return new goog.net.ChannelRequest(
    +      channel,
    +      channelDebug,
    +      opt_sessionId,
    +      opt_requestId,
    +      opt_retryId);
    +};
    +
    +
    +/**
    + * Starts the channel. This initiates connections to the server.
    + *
    + * @param {string} testPath  The path for the test connection.
    + * @param {string} channelPath  The path for the channel connection.
    + * @param {Object=} opt_extraParams  Extra parameter keys and values to add to
    + *     the requests.
    + * @param {string=} opt_oldSessionId  Session ID from a previous session.
    + * @param {number=} opt_oldArrayId  The last array ID from a previous session.
    + */
    +goog.net.BrowserChannel.prototype.connect = function(testPath, channelPath,
    +    opt_extraParams, opt_oldSessionId, opt_oldArrayId) {
    +  this.channelDebug_.debug('connect()');
    +
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      goog.net.BrowserChannel.Stat.CONNECT_ATTEMPT);
    +
    +  this.path_ = channelPath;
    +  this.extraParams_ = opt_extraParams || {};
    +
    +  // Attach parameters about the previous session if reconnecting.
    +  if (opt_oldSessionId && goog.isDef(opt_oldArrayId)) {
    +    this.extraParams_['OSID'] = opt_oldSessionId;
    +    this.extraParams_['OAID'] = opt_oldArrayId;
    +  }
    +
    +  this.connectTest_(testPath);
    +};
    +
    +
    +/**
    + * Disconnects and closes the channel.
    + */
    +goog.net.BrowserChannel.prototype.disconnect = function() {
    +  this.channelDebug_.debug('disconnect()');
    +
    +  this.cancelRequests_();
    +
    +  if (this.state_ == goog.net.BrowserChannel.State.OPENED) {
    +    var rid = this.nextRid_++;
    +    var uri = this.forwardChannelUri_.clone();
    +    uri.setParameterValue('SID', this.sid_);
    +    uri.setParameterValue('RID', rid);
    +    uri.setParameterValue('TYPE', 'terminate');
    +
    +    // Add the reconnect parameters.
    +    this.addAdditionalParams_(uri);
    +
    +    var request = goog.net.BrowserChannel.createChannelRequest(
    +        this, this.channelDebug_, this.sid_, rid);
    +    request.sendUsingImgTag(uri);
    +  }
    +
    +  this.onClose_();
    +};
    +
    +
    +/**
    + * Returns the session id of the channel. Only available after the
    + * channel has been opened.
    + * @return {string} Session ID.
    + */
    +goog.net.BrowserChannel.prototype.getSessionId = function() {
    +  return this.sid_;
    +};
    +
    +
    +/**
    + * Starts the test channel to determine network conditions.
    + *
    + * @param {string} testPath  The relative PATH for the test connection.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.connectTest_ = function(testPath) {
    +  this.channelDebug_.debug('connectTest_()');
    +  if (!this.okToMakeRequest_()) {
    +    return; // channel is cancelled
    +  }
    +  this.connectionTest_ = new goog.net.BrowserTestChannel(
    +      this, this.channelDebug_);
    +  this.connectionTest_.setExtraHeaders(this.extraHeaders_);
    +  this.connectionTest_.setParser(this.parser_);
    +  this.connectionTest_.connect(testPath);
    +};
    +
    +
    +/**
    + * Starts the regular channel which is run after the test channel is complete.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.connectChannel_ = function() {
    +  this.channelDebug_.debug('connectChannel_()');
    +  this.ensureInState_(goog.net.BrowserChannel.State.INIT,
    +      goog.net.BrowserChannel.State.CLOSED);
    +  this.forwardChannelUri_ =
    +      this.getForwardChannelUri(/** @type {string} */ (this.path_));
    +  this.ensureForwardChannel_();
    +};
    +
    +
    +/**
    + * Cancels all outstanding requests.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.cancelRequests_ = function() {
    +  if (this.connectionTest_) {
    +    this.connectionTest_.abort();
    +    this.connectionTest_ = null;
    +  }
    +
    +  if (this.backChannelRequest_) {
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +  }
    +
    +  if (this.backChannelTimerId_) {
    +    goog.global.clearTimeout(this.backChannelTimerId_);
    +    this.backChannelTimerId_ = null;
    +  }
    +
    +  this.clearDeadBackchannelTimer_();
    +
    +  if (this.forwardChannelRequest_) {
    +    this.forwardChannelRequest_.cancel();
    +    this.forwardChannelRequest_ = null;
    +  }
    +
    +  if (this.forwardChannelTimerId_) {
    +    goog.global.clearTimeout(this.forwardChannelTimerId_);
    +    this.forwardChannelTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns the extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @return {Object} The HTTP headers, or null.
    + */
    +goog.net.BrowserChannel.prototype.getExtraHeaders = function() {
    +  return this.extraHeaders_;
    +};
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers, or null.
    + */
    +goog.net.BrowserChannel.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Sets the throttle for handling onreadystatechange events for the request.
    + *
    + * @param {number} throttle The throttle in ms.  A value of zero indicates
    + *     no throttle.
    + */
    +goog.net.BrowserChannel.prototype.setReadyStateChangeThrottle = function(
    +    throttle) {
    +  this.readyStateChangeThrottleMs_ = throttle;
    +};
    +
    +
    +/**
    + * Sets whether cross origin requests are supported for the browser channel.
    + *
    + * Setting this allows the creation of requests to secondary domains and
    + * sends XHRs with the CORS withCredentials bit set to true.
    + *
    + * In order for cross-origin requests to work, the server will also need to set
    + * CORS response headers as per:
    + * https://developer.mozilla.org/en-US/docs/HTTP_access_control
    + *
    + * See {@link goog.net.XhrIo#setWithCredentials}.
    + * @param {boolean} supportCrossDomain Whether cross domain XHRs are supported.
    + */
    +goog.net.BrowserChannel.prototype.setSupportsCrossDomainXhrs = function(
    +    supportCrossDomain) {
    +  this.supportsCrossDomainXhrs_ = supportCrossDomain;
    +};
    +
    +
    +/**
    + * Returns the handler used for channel callback events.
    + *
    + * @return {goog.net.BrowserChannel.Handler} The handler.
    + */
    +goog.net.BrowserChannel.prototype.getHandler = function() {
    +  return this.handler_;
    +};
    +
    +
    +/**
    + * Sets the handler used for channel callback events.
    + * @param {goog.net.BrowserChannel.Handler} handler The handler to set.
    + */
    +goog.net.BrowserChannel.prototype.setHandler = function(handler) {
    +  this.handler_ = handler;
    +};
    +
    +
    +/**
    + * Returns whether the channel allows the use of a subdomain. There may be
    + * cases where this isn't allowed.
    + * @return {boolean} Whether a host prefix is allowed.
    + */
    +goog.net.BrowserChannel.prototype.getAllowHostPrefix = function() {
    +  return this.allowHostPrefix_;
    +};
    +
    +
    +/**
    + * Sets whether the channel allows the use of a subdomain. There may be cases
    + * where this isn't allowed, for example, logging in with troutboard where
    + * using a subdomain causes Apache to force the user to authenticate twice.
    + * @param {boolean} allowHostPrefix Whether a host prefix is allowed.
    + */
    +goog.net.BrowserChannel.prototype.setAllowHostPrefix =
    +    function(allowHostPrefix) {
    +  this.allowHostPrefix_ = allowHostPrefix;
    +};
    +
    +
    +/**
    + * Returns whether the channel is buffered or not. This state is valid for
    + * querying only after the test connection has completed. This may be
    + * queried in the goog.net.BrowserChannel.okToMakeRequest() callback.
    + * A channel may be buffered if the test connection determines that
    + * a chunked response could not be sent down within a suitable time.
    + * @return {boolean} Whether the channel is buffered.
    + */
    +goog.net.BrowserChannel.prototype.isBuffered = function() {
    +  return !this.useChunked_;
    +};
    +
    +
    +/**
    + * Returns whether chunked mode is allowed. In certain debugging situations,
    + * it's useful for the application to have a way to disable chunked mode for a
    + * user.
    +
    + * @return {boolean} Whether chunked mode is allowed.
    + */
    +goog.net.BrowserChannel.prototype.getAllowChunkedMode =
    +    function() {
    +  return this.allowChunkedMode_;
    +};
    +
    +
    +/**
    + * Sets whether chunked mode is allowed. In certain debugging situations, it's
    + * useful for the application to have a way to disable chunked mode for a user.
    + * @param {boolean} allowChunkedMode  Whether chunked mode is allowed.
    + */
    +goog.net.BrowserChannel.prototype.setAllowChunkedMode =
    +    function(allowChunkedMode) {
    +  this.allowChunkedMode_ = allowChunkedMode;
    +};
    +
    +
    +/**
    + * Sends a request to the server. The format of the request is a Map data
    + * structure of key/value pairs. These maps are then encoded in a format
    + * suitable for the wire and then reconstituted as a Map data structure that
    + * the server can process.
    + * @param {Object|goog.structs.Map} map  The map to send.
    + * @param {?Object=} opt_context The context associated with the map.
    + */
    +goog.net.BrowserChannel.prototype.sendMap = function(map, opt_context) {
    +  if (this.state_ == goog.net.BrowserChannel.State.CLOSED) {
    +    throw Error('Invalid operation: sending map when state is closed');
    +  }
    +
    +  // We can only send 1000 maps per POST, but typically we should never have
    +  // that much to send, so warn if we exceed that (we still send all the maps).
    +  if (this.outgoingMaps_.length ==
    +      goog.net.BrowserChannel.MAX_MAPS_PER_REQUEST_) {
    +    // severe() is temporary so that we get these uploaded and can figure out
    +    // what's causing them. Afterwards can change to warning().
    +    this.channelDebug_.severe(
    +        'Already have ' + goog.net.BrowserChannel.MAX_MAPS_PER_REQUEST_ +
    +        ' queued maps upon queueing ' + goog.json.serialize(map));
    +  }
    +
    +  this.outgoingMaps_.push(
    +      new goog.net.BrowserChannel.QueuedMap(this.nextMapId_++, map,
    +                                            opt_context));
    +  if (this.state_ == goog.net.BrowserChannel.State.OPENING ||
    +      this.state_ == goog.net.BrowserChannel.State.OPENED) {
    +    this.ensureForwardChannel_();
    +  }
    +};
    +
    +
    +/**
    + * When set to true, this changes the behavior of the forward channel so it
    + * will not retry requests; it will fail after one network failure, and if
    + * there was already one network failure, the request will fail immediately.
    + * @param {boolean} failFast  Whether or not to fail fast.
    + */
    +goog.net.BrowserChannel.prototype.setFailFast = function(failFast) {
    +  this.failFast_ = failFast;
    +  this.channelDebug_.info('setFailFast: ' + failFast);
    +  if ((this.forwardChannelRequest_ || this.forwardChannelTimerId_) &&
    +      this.forwardChannelRetryCount_ > this.getForwardChannelMaxRetries()) {
    +    this.channelDebug_.info(
    +        'Retry count ' + this.forwardChannelRetryCount_ +
    +        ' > new maxRetries ' + this.getForwardChannelMaxRetries() +
    +        '. Fail immediately!');
    +    if (this.forwardChannelRequest_) {
    +      this.forwardChannelRequest_.cancel();
    +      // Go through the standard onRequestComplete logic to expose the max-retry
    +      // failure in the standard way.
    +      this.onRequestComplete(this.forwardChannelRequest_);
    +    } else {  // i.e., this.forwardChannelTimerId_
    +      goog.global.clearTimeout(this.forwardChannelTimerId_);
    +      this.forwardChannelTimerId_ = null;
    +      // The error code from the last failed request is gone, so just use a
    +      // generic one.
    +      this.signalError_(goog.net.BrowserChannel.Error.REQUEST_FAILED);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The max number of forward-channel retries, which will be 0
    + * in fail-fast mode.
    + */
    +goog.net.BrowserChannel.prototype.getForwardChannelMaxRetries = function() {
    +  return this.failFast_ ? 0 : this.forwardChannelMaxRetries_;
    +};
    +
    +
    +/**
    + * Sets the maximum number of attempts to connect to the server for forward
    + * channel requests.
    + * @param {number} retries The maximum number of attempts.
    + */
    +goog.net.BrowserChannel.prototype.setForwardChannelMaxRetries =
    +    function(retries) {
    +  this.forwardChannelMaxRetries_ = retries;
    +};
    +
    +
    +/**
    + * Sets the timeout for a forward channel request.
    + * @param {number} timeoutMs The timeout in milliseconds.
    + */
    +goog.net.BrowserChannel.prototype.setForwardChannelRequestTimeout =
    +    function(timeoutMs) {
    +  this.forwardChannelRequestTimeoutMs_ = timeoutMs;
    +};
    +
    +
    +/**
    + * @return {number} The max number of back-channel retries, which is a constant.
    + */
    +goog.net.BrowserChannel.prototype.getBackChannelMaxRetries = function() {
    +  // Back-channel retries is a constant.
    +  return goog.net.BrowserChannel.BACK_CHANNEL_MAX_RETRIES;
    +};
    +
    +
    +/**
    + * Returns whether the channel is closed
    + * @return {boolean} true if the channel is closed.
    + */
    +goog.net.BrowserChannel.prototype.isClosed = function() {
    +  return this.state_ == goog.net.BrowserChannel.State.CLOSED;
    +};
    +
    +
    +/**
    + * Returns the browser channel state.
    + * @return {goog.net.BrowserChannel.State} The current state of the browser
    + * channel.
    + */
    +goog.net.BrowserChannel.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * Return the last status code received for a request.
    + * @return {number} The last status code received for a request.
    + */
    +goog.net.BrowserChannel.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * @return {number} The last array id received.
    + */
    +goog.net.BrowserChannel.prototype.getLastArrayId = function() {
    +  return this.lastArrayId_;
    +};
    +
    +
    +/**
    + * Returns whether there are outstanding requests servicing the channel.
    + * @return {boolean} true if there are outstanding requests.
    + */
    +goog.net.BrowserChannel.prototype.hasOutstandingRequests = function() {
    +  return this.outstandingRequests_() != 0;
    +};
    +
    +
    +/**
    + * Sets a new parser for the response payload. A custom parser may be set to
    + * avoid using eval(), for example. By default, the parser uses
    + * {@code goog.json.unsafeParse}.
    + * @param {!goog.string.Parser} parser Parser.
    + */
    +goog.net.BrowserChannel.prototype.setParser = function(parser) {
    +  this.parser_ = parser;
    +};
    +
    +
    +/**
    + * Returns the number of outstanding requests.
    + * @return {number} The number of outstanding requests to the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.outstandingRequests_ = function() {
    +  var count = 0;
    +  if (this.backChannelRequest_) {
    +    count++;
    +  }
    +  if (this.forwardChannelRequest_) {
    +    count++;
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Ensures that a forward channel request is scheduled.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.ensureForwardChannel_ = function() {
    +  if (this.forwardChannelRequest_) {
    +    // connection in process - no need to start a new request
    +    return;
    +  }
    +
    +  if (this.forwardChannelTimerId_) {
    +    // no need to start a new request - one is already scheduled
    +    return;
    +  }
    +
    +  this.forwardChannelTimerId_ = goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onStartForwardChannelTimer_, this), 0);
    +  this.forwardChannelRetryCount_ = 0;
    +};
    +
    +
    +/**
    + * Schedules a forward-channel retry for the specified request, unless the max
    + * retries has been reached.
    + * @param {goog.net.ChannelRequest} request The failed request to retry.
    + * @return {boolean} true iff a retry was scheduled.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.maybeRetryForwardChannel_ =
    +    function(request) {
    +  if (this.forwardChannelRequest_ || this.forwardChannelTimerId_) {
    +    // Should be impossible to be called in this state.
    +    this.channelDebug_.severe('Request already in progress');
    +    return false;
    +  }
    +
    +  if (this.state_ == goog.net.BrowserChannel.State.INIT ||  // no retry open_()
    +      (this.forwardChannelRetryCount_ >= this.getForwardChannelMaxRetries())) {
    +    return false;
    +  }
    +
    +  this.channelDebug_.debug('Going to retry POST');
    +
    +  this.forwardChannelTimerId_ = goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onStartForwardChannelTimer_, this, request),
    +      this.getRetryTime_(this.forwardChannelRetryCount_));
    +  this.forwardChannelRetryCount_++;
    +  return true;
    +};
    +
    +
    +/**
    + * Timer callback for ensureForwardChannel
    + * @param {goog.net.ChannelRequest=} opt_retryRequest A failed request to retry.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onStartForwardChannelTimer_ = function(
    +    opt_retryRequest) {
    +  this.forwardChannelTimerId_ = null;
    +  this.startForwardChannel_(opt_retryRequest);
    +};
    +
    +
    +/**
    + * Begins a new forward channel operation to the server.
    + * @param {goog.net.ChannelRequest=} opt_retryRequest A failed request to retry.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.startForwardChannel_ = function(
    +    opt_retryRequest) {
    +  this.channelDebug_.debug('startForwardChannel_');
    +  if (!this.okToMakeRequest_()) {
    +    return; // channel is cancelled
    +  } else if (this.state_ == goog.net.BrowserChannel.State.INIT) {
    +    if (opt_retryRequest) {
    +      this.channelDebug_.severe('Not supposed to retry the open');
    +      return;
    +    }
    +    this.open_();
    +    this.state_ = goog.net.BrowserChannel.State.OPENING;
    +  } else if (this.state_ == goog.net.BrowserChannel.State.OPENED) {
    +    if (opt_retryRequest) {
    +      this.makeForwardChannelRequest_(opt_retryRequest);
    +      return;
    +    }
    +
    +    if (this.outgoingMaps_.length == 0) {
    +      this.channelDebug_.debug('startForwardChannel_ returned: ' +
    +                                   'nothing to send');
    +      // no need to start a new forward channel request
    +      return;
    +    }
    +
    +    if (this.forwardChannelRequest_) {
    +      // Should be impossible to be called in this state.
    +      this.channelDebug_.severe('startForwardChannel_ returned: ' +
    +                                    'connection already in progress');
    +      return;
    +    }
    +
    +    this.makeForwardChannelRequest_();
    +    this.channelDebug_.debug('startForwardChannel_ finished, sent request');
    +  }
    +};
    +
    +
    +/**
    + * Establishes a new channel session with the the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.open_ = function() {
    +  this.channelDebug_.debug('open_()');
    +  this.nextRid_ = Math.floor(Math.random() * 100000);
    +
    +  var rid = this.nextRid_++;
    +  var request = goog.net.BrowserChannel.createChannelRequest(
    +      this, this.channelDebug_, '', rid);
    +  request.setExtraHeaders(this.extraHeaders_);
    +  var requestText = this.dequeueOutgoingMaps_();
    +  var uri = this.forwardChannelUri_.clone();
    +  uri.setParameterValue('RID', rid);
    +  if (this.clientVersion_) {
    +    uri.setParameterValue('CVER', this.clientVersion_);
    +  }
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  request.xmlHttpPost(uri, requestText, true);
    +  this.forwardChannelRequest_ = request;
    +};
    +
    +
    +/**
    + * Makes a forward channel request using XMLHTTP.
    + * @param {goog.net.ChannelRequest=} opt_retryRequest A failed request to retry.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.makeForwardChannelRequest_ =
    +    function(opt_retryRequest) {
    +  var rid;
    +  var requestText;
    +  if (opt_retryRequest) {
    +    if (this.channelVersion_ > 6) {
    +      // In version 7 and up we can tack on new arrays to a retry.
    +      this.requeuePendingMaps_();
    +      rid = this.nextRid_ - 1;  // Must use last RID
    +      requestText = this.dequeueOutgoingMaps_();
    +    } else {
    +      // TODO(user): Remove this code and the opt_retryRequest passing
    +      // once server-side support for ver 7 is ubiquitous.
    +      rid = opt_retryRequest.getRequestId();
    +      requestText = /** @type {string} */ (opt_retryRequest.getPostData());
    +    }
    +  } else {
    +    rid = this.nextRid_++;
    +    requestText = this.dequeueOutgoingMaps_();
    +  }
    +
    +  var uri = this.forwardChannelUri_.clone();
    +  uri.setParameterValue('SID', this.sid_);
    +  uri.setParameterValue('RID', rid);
    +  uri.setParameterValue('AID', this.lastArrayId_);
    +  // Add the additional reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  var request = goog.net.BrowserChannel.createChannelRequest(
    +      this,
    +      this.channelDebug_,
    +      this.sid_,
    +      rid,
    +      this.forwardChannelRetryCount_ + 1);
    +  request.setExtraHeaders(this.extraHeaders_);
    +
    +  // randomize from 50%-100% of the forward channel timeout to avoid
    +  // a big hit if servers happen to die at once.
    +  request.setTimeout(
    +      Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50) +
    +      Math.round(this.forwardChannelRequestTimeoutMs_ * 0.50 * Math.random()));
    +  this.forwardChannelRequest_ = request;
    +  request.xmlHttpPost(uri, requestText, true);
    +};
    +
    +
    +/**
    + * Adds the additional parameters from the handler to the given URI.
    + * @param {goog.Uri} uri The URI to add the parameters to.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.addAdditionalParams_ = function(uri) {
    +  // Add the additional reconnect parameters as needed.
    +  if (this.handler_) {
    +    var params = this.handler_.getAdditionalParams(this);
    +    if (params) {
    +      goog.object.forEach(params, function(value, key) {
    +        uri.setParameterValue(key, value);
    +      });
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the request text from the outgoing maps and resets it.
    + * @return {string} The encoded request text created from all the currently
    + *                  queued outgoing maps.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.dequeueOutgoingMaps_ = function() {
    +  var count = Math.min(this.outgoingMaps_.length,
    +                       goog.net.BrowserChannel.MAX_MAPS_PER_REQUEST_);
    +  var sb = ['count=' + count];
    +  var offset;
    +  if (this.channelVersion_ > 6 && count > 0) {
    +    // To save a bit of bandwidth, specify the base mapId and the rest as
    +    // offsets from it.
    +    offset = this.outgoingMaps_[0].mapId;
    +    sb.push('ofs=' + offset);
    +  } else {
    +    offset = 0;
    +  }
    +  for (var i = 0; i < count; i++) {
    +    var mapId = this.outgoingMaps_[i].mapId;
    +    var map = this.outgoingMaps_[i].map;
    +    if (this.channelVersion_ <= 6) {
    +      // Map IDs were not used in ver 6 and before, just indexes in the request.
    +      mapId = i;
    +    } else {
    +      mapId -= offset;
    +    }
    +    try {
    +      goog.structs.forEach(map, function(value, key, coll) {
    +        sb.push('req' + mapId + '_' + key + '=' + encodeURIComponent(value));
    +      });
    +    } catch (ex) {
    +      // We send a map here because lots of the retry logic relies on map IDs,
    +      // so we have to send something.
    +      sb.push('req' + mapId + '_' + 'type' + '=' +
    +              encodeURIComponent('_badmap'));
    +      if (this.handler_) {
    +        this.handler_.badMapError(this, map);
    +      }
    +    }
    +  }
    +  this.pendingMaps_ = this.pendingMaps_.concat(
    +      this.outgoingMaps_.splice(0, count));
    +  return sb.join('&');
    +};
    +
    +
    +/**
    + * Requeues unacknowledged sent arrays for retransmission in the next forward
    + * channel request.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.requeuePendingMaps_ = function() {
    +  this.outgoingMaps_ = this.pendingMaps_.concat(this.outgoingMaps_);
    +  this.pendingMaps_.length = 0;
    +};
    +
    +
    +/**
    + * Ensures there is a backchannel request for receiving data from the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.ensureBackChannel_ = function() {
    +  if (this.backChannelRequest_) {
    +    // already have one
    +    return;
    +  }
    +
    +  if (this.backChannelTimerId_) {
    +    // no need to start a new request - one is already scheduled
    +    return;
    +  }
    +
    +  this.backChannelAttemptId_ = 1;
    +  this.backChannelTimerId_ = goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onStartBackChannelTimer_, this), 0);
    +  this.backChannelRetryCount_ = 0;
    +};
    +
    +
    +/**
    + * Schedules a back-channel retry, unless the max retries has been reached.
    + * @return {boolean} true iff a retry was scheduled.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.maybeRetryBackChannel_ = function() {
    +  if (this.backChannelRequest_ || this.backChannelTimerId_) {
    +    // Should be impossible to be called in this state.
    +    this.channelDebug_.severe('Request already in progress');
    +    return false;
    +  }
    +
    +  if (this.backChannelRetryCount_ >= this.getBackChannelMaxRetries()) {
    +    return false;
    +  }
    +
    +  this.channelDebug_.debug('Going to retry GET');
    +
    +  this.backChannelAttemptId_++;
    +  this.backChannelTimerId_ = goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onStartBackChannelTimer_, this),
    +      this.getRetryTime_(this.backChannelRetryCount_));
    +  this.backChannelRetryCount_++;
    +  return true;
    +};
    +
    +
    +/**
    + * Timer callback for ensureBackChannel_.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onStartBackChannelTimer_ = function() {
    +  this.backChannelTimerId_ = null;
    +  this.startBackChannel_();
    +};
    +
    +
    +/**
    + * Begins a new back channel operation to the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.startBackChannel_ = function() {
    +  if (!this.okToMakeRequest_()) {
    +    // channel is cancelled
    +    return;
    +  }
    +
    +  this.channelDebug_.debug('Creating new HttpRequest');
    +  this.backChannelRequest_ = goog.net.BrowserChannel.createChannelRequest(
    +      this,
    +      this.channelDebug_,
    +      this.sid_,
    +      'rpc',
    +      this.backChannelAttemptId_);
    +  this.backChannelRequest_.setExtraHeaders(this.extraHeaders_);
    +  this.backChannelRequest_.setReadyStateChangeThrottle(
    +      this.readyStateChangeThrottleMs_);
    +  var uri = this.backChannelUri_.clone();
    +  uri.setParameterValue('RID', 'rpc');
    +  uri.setParameterValue('SID', this.sid_);
    +  uri.setParameterValue('CI', this.useChunked_ ? '0' : '1');
    +  uri.setParameterValue('AID', this.lastArrayId_);
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  if (!goog.net.ChannelRequest.supportsXhrStreaming()) {
    +    uri.setParameterValue('TYPE', 'html');
    +    this.backChannelRequest_.tridentGet(uri, Boolean(this.hostPrefix_));
    +  } else {
    +    uri.setParameterValue('TYPE', 'xmlhttp');
    +    this.backChannelRequest_.xmlHttpGet(uri, true /* decodeChunks */,
    +        this.hostPrefix_, false /* opt_noClose */);
    +  }
    +  this.channelDebug_.debug('New Request created');
    +};
    +
    +
    +/**
    + * Gives the handler a chance to return an error code and stop channel
    + * execution. A handler might want to do this to check that the user is still
    + * logged in, for example.
    + * @private
    + * @return {boolean} If it's OK to make a request.
    + */
    +goog.net.BrowserChannel.prototype.okToMakeRequest_ = function() {
    +  if (this.handler_) {
    +    var result = this.handler_.okToMakeRequest(this);
    +    if (result != goog.net.BrowserChannel.Error.OK) {
    +      this.channelDebug_.debug('Handler returned error code from ' +
    +                                   'okToMakeRequest');
    +      this.signalError_(result);
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Callback from BrowserTestChannel for when the channel is finished.
    + * @param {goog.net.BrowserTestChannel} testChannel The BrowserTestChannel.
    + * @param {boolean} useChunked  Whether we can chunk responses.
    + */
    +goog.net.BrowserChannel.prototype.testConnectionFinished =
    +    function(testChannel, useChunked) {
    +  this.channelDebug_.debug('Test Connection Finished');
    +
    +  this.useChunked_ = this.allowChunkedMode_ && useChunked;
    +  this.lastStatusCode_ = testChannel.getLastStatusCode();
    +  this.connectChannel_();
    +};
    +
    +
    +/**
    + * Callback from BrowserTestChannel for when the channel has an error.
    + * @param {goog.net.BrowserTestChannel} testChannel The BrowserTestChannel.
    + * @param {goog.net.ChannelRequest.Error} errorCode  The error code of the
    +       failure.
    + */
    +goog.net.BrowserChannel.prototype.testConnectionFailure =
    +    function(testChannel, errorCode) {
    +  this.channelDebug_.debug('Test Connection Failed');
    +  this.lastStatusCode_ = testChannel.getLastStatusCode();
    +  this.signalError_(goog.net.BrowserChannel.Error.REQUEST_FAILED);
    +};
    +
    +
    +/**
    + * Callback from BrowserTestChannel for when the channel is blocked.
    + * @param {goog.net.BrowserTestChannel} testChannel The BrowserTestChannel.
    + */
    +goog.net.BrowserChannel.prototype.testConnectionBlocked =
    +    function(testChannel) {
    +  this.channelDebug_.debug('Test Connection Blocked');
    +  this.lastStatusCode_ = this.connectionTest_.getLastStatusCode();
    +  this.signalError_(goog.net.BrowserChannel.Error.BLOCKED);
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest for when new data is received
    + * @param {goog.net.ChannelRequest} request  The request object.
    + * @param {string} responseText The text of the response.
    + */
    +goog.net.BrowserChannel.prototype.onRequestData =
    +    function(request, responseText) {
    +  if (this.state_ == goog.net.BrowserChannel.State.CLOSED ||
    +      (this.backChannelRequest_ != request &&
    +       this.forwardChannelRequest_ != request)) {
    +    // either CLOSED or a request we don't know about (perhaps an old request)
    +    return;
    +  }
    +  this.lastStatusCode_ = request.getLastStatusCode();
    +
    +  if (this.forwardChannelRequest_ == request &&
    +      this.state_ == goog.net.BrowserChannel.State.OPENED) {
    +    if (this.channelVersion_ > 7) {
    +      var response;
    +      try {
    +        response = this.parser_.parse(responseText);
    +      } catch (ex) {
    +        response = null;
    +      }
    +      if (goog.isArray(response) && response.length == 3) {
    +        this.handlePostResponse_(response);
    +      } else {
    +        this.channelDebug_.debug('Bad POST response data returned');
    +        this.signalError_(goog.net.BrowserChannel.Error.BAD_RESPONSE);
    +      }
    +    } else if (responseText != goog.net.BrowserChannel.MAGIC_RESPONSE_COOKIE) {
    +      this.channelDebug_.debug('Bad data returned - missing/invald ' +
    +                                   'magic cookie');
    +      this.signalError_(goog.net.BrowserChannel.Error.BAD_RESPONSE);
    +    }
    +  } else {
    +    if (this.backChannelRequest_ == request) {
    +      this.clearDeadBackchannelTimer_();
    +    }
    +    if (!goog.string.isEmptyOrWhitespace(responseText)) {
    +      var response = this.parser_.parse(responseText);
    +      goog.asserts.assert(goog.isArray(response));
    +      this.onInput_(/** @type {!Array<?>} */ (response));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles a POST response from the server.
    + * @param {Array<number>} responseValues The key value pairs in the POST
    + *     response.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.handlePostResponse_ = function(
    +    responseValues) {
    +  // The first response value is set to 0 if server is missing backchannel.
    +  if (responseValues[0] == 0) {
    +    this.handleBackchannelMissing_();
    +    return;
    +  }
    +  this.lastPostResponseArrayId_ = responseValues[1];
    +  var outstandingArrays = this.lastPostResponseArrayId_ - this.lastArrayId_;
    +  if (0 < outstandingArrays) {
    +    var numOutstandingBackchannelBytes = responseValues[2];
    +    this.channelDebug_.debug(numOutstandingBackchannelBytes + ' bytes (in ' +
    +        outstandingArrays + ' arrays) are outstanding on the BackChannel');
    +    if (!this.shouldRetryBackChannel_(numOutstandingBackchannelBytes)) {
    +      return;
    +    }
    +    if (!this.deadBackChannelTimerId_) {
    +      // We expect to receive data within 2 RTTs or we retry the backchannel.
    +      this.deadBackChannelTimerId_ = goog.net.BrowserChannel.setTimeout(
    +          goog.bind(this.onBackChannelDead_, this),
    +          2 * goog.net.BrowserChannel.RTT_ESTIMATE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles a POST response from the server telling us that it has detected that
    + * we have no hanging GET connection.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.handleBackchannelMissing_ = function() {
    +  // As long as the back channel was started before the POST was sent,
    +  // we should retry the backchannel. We give a slight buffer of RTT_ESTIMATE
    +  // so as not to excessively retry the backchannel
    +  this.channelDebug_.debug('Server claims our backchannel is missing.');
    +  if (this.backChannelTimerId_) {
    +    this.channelDebug_.debug('But we are currently starting the request.');
    +    return;
    +  } else if (!this.backChannelRequest_) {
    +    this.channelDebug_.warning(
    +        'We do not have a BackChannel established');
    +  } else if (this.backChannelRequest_.getRequestStartTime() +
    +      goog.net.BrowserChannel.RTT_ESTIMATE <
    +      this.forwardChannelRequest_.getRequestStartTime()) {
    +    this.clearDeadBackchannelTimer_();
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +  } else {
    +    return;
    +  }
    +  this.maybeRetryBackChannel_();
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      goog.net.BrowserChannel.Stat.BACKCHANNEL_MISSING);
    +};
    +
    +
    +/**
    + * Determines whether we should start the process of retrying a possibly
    + * dead backchannel.
    + * @param {number} outstandingBytes The number of bytes for which the server has
    + *     not yet received acknowledgement.
    + * @return {boolean} Whether to start the backchannel retry timer.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.shouldRetryBackChannel_ = function(
    +    outstandingBytes) {
    +  // Not too many outstanding bytes, not buffered and not after a retry.
    +  return outstandingBytes <
    +      goog.net.BrowserChannel.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF &&
    +      !this.isBuffered() &&
    +      this.backChannelRetryCount_ == 0;
    +};
    +
    +
    +/**
    + * Decides which host prefix should be used, if any.  If there is a handler,
    + * allows the handler to validate a host prefix provided by the server, and
    + * optionally override it.
    + * @param {?string} serverHostPrefix The host prefix provided by the server.
    + * @return {?string} The host prefix to actually use, if any. Will return null
    + *     if the use of host prefixes was disabled via setAllowHostPrefix().
    + */
    +goog.net.BrowserChannel.prototype.correctHostPrefix = function(
    +    serverHostPrefix) {
    +  if (this.allowHostPrefix_) {
    +    if (this.handler_) {
    +      return this.handler_.correctHostPrefix(serverHostPrefix);
    +    }
    +    return serverHostPrefix;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Handles the timer that indicates that our backchannel is no longer able to
    + * successfully receive data from the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onBackChannelDead_ = function() {
    +  if (goog.isDefAndNotNull(this.deadBackChannelTimerId_)) {
    +    this.deadBackChannelTimerId_ = null;
    +    this.backChannelRequest_.cancel();
    +    this.backChannelRequest_ = null;
    +    this.maybeRetryBackChannel_();
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD);
    +  }
    +};
    +
    +
    +/**
    + * Clears the timer that indicates that our backchannel is no longer able to
    + * successfully receive data from the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.clearDeadBackchannelTimer_ = function() {
    +  if (goog.isDefAndNotNull(this.deadBackChannelTimerId_)) {
    +    goog.global.clearTimeout(this.deadBackChannelTimerId_);
    +    this.deadBackChannelTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether or not the given error/status combination is fatal or not.
    + * On fatal errors we immediately close the session rather than retrying the
    + * failed request.
    + * @param {goog.net.ChannelRequest.Error?} error The error code for the failed
    + * request.
    + * @param {number} statusCode The last HTTP status code.
    + * @return {boolean} Whether or not the error is fatal.
    + * @private
    + */
    +goog.net.BrowserChannel.isFatalError_ =
    +    function(error, statusCode) {
    +  return error == goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID ||
    +      error == goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED ||
    +      (error == goog.net.ChannelRequest.Error.STATUS &&
    +       statusCode > 0);
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest that indicates a request has completed.
    + * @param {goog.net.ChannelRequest} request  The request object.
    + */
    +goog.net.BrowserChannel.prototype.onRequestComplete =
    +    function(request) {
    +  this.channelDebug_.debug('Request complete');
    +  var type;
    +  if (this.backChannelRequest_ == request) {
    +    this.clearDeadBackchannelTimer_();
    +    this.backChannelRequest_ = null;
    +    type = goog.net.BrowserChannel.ChannelType_.BACK_CHANNEL;
    +  } else if (this.forwardChannelRequest_ == request) {
    +    this.forwardChannelRequest_ = null;
    +    type = goog.net.BrowserChannel.ChannelType_.FORWARD_CHANNEL;
    +  } else {
    +    // return if it was an old request from a previous session
    +    return;
    +  }
    +
    +  this.lastStatusCode_ = request.getLastStatusCode();
    +
    +  if (this.state_ == goog.net.BrowserChannel.State.CLOSED) {
    +    return;
    +  }
    +
    +  if (request.getSuccess()) {
    +    // Yay!
    +    if (type == goog.net.BrowserChannel.ChannelType_.FORWARD_CHANNEL) {
    +      var size = request.getPostData() ? request.getPostData().length : 0;
    +      goog.net.BrowserChannel.notifyTimingEvent(size,
    +          goog.now() - request.getRequestStartTime(),
    +          this.forwardChannelRetryCount_);
    +      this.ensureForwardChannel_();
    +      this.onSuccess_();
    +      this.pendingMaps_.length = 0;
    +    } else {  // i.e., back-channel
    +      this.ensureBackChannel_();
    +    }
    +    return;
    +  }
    +  // Else unsuccessful. Fall through.
    +
    +  var lastError = request.getLastError();
    +  if (!goog.net.BrowserChannel.isFatalError_(lastError,
    +                                             this.lastStatusCode_)) {
    +    // Maybe retry.
    +    this.channelDebug_.debug('Maybe retrying, last error: ' +
    +        goog.net.ChannelRequest.errorStringFromCode(
    +            /** @type {goog.net.ChannelRequest.Error} */ (lastError),
    +            this.lastStatusCode_));
    +    if (type == goog.net.BrowserChannel.ChannelType_.FORWARD_CHANNEL) {
    +      if (this.maybeRetryForwardChannel_(request)) {
    +        return;
    +      }
    +    }
    +    if (type == goog.net.BrowserChannel.ChannelType_.BACK_CHANNEL) {
    +      if (this.maybeRetryBackChannel_()) {
    +        return;
    +      }
    +    }
    +    // Else exceeded max retries. Fall through.
    +    this.channelDebug_.debug('Exceeded max number of retries');
    +  } else {
    +    // Else fatal error. Fall through and mark the pending maps as failed.
    +    this.channelDebug_.debug('Not retrying due to error type');
    +  }
    +
    +
    +  // Can't save this session. :(
    +  this.channelDebug_.debug('Error: HTTP request failed');
    +  switch (lastError) {
    +    case goog.net.ChannelRequest.Error.NO_DATA:
    +      this.signalError_(goog.net.BrowserChannel.Error.NO_DATA);
    +      break;
    +    case goog.net.ChannelRequest.Error.BAD_DATA:
    +      this.signalError_(goog.net.BrowserChannel.Error.BAD_DATA);
    +      break;
    +    case goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID:
    +      this.signalError_(goog.net.BrowserChannel.Error.UNKNOWN_SESSION_ID);
    +      break;
    +    case goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED:
    +      this.signalError_(goog.net.BrowserChannel.Error.ACTIVE_X_BLOCKED);
    +      break;
    +    default:
    +      this.signalError_(goog.net.BrowserChannel.Error.REQUEST_FAILED);
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * @param {number} retryCount Number of retries so far.
    + * @return {number} Time in ms before firing next retry request.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.getRetryTime_ = function(retryCount) {
    +  var retryTime = this.baseRetryDelayMs_ +
    +      Math.floor(Math.random() * this.retryDelaySeedMs_);
    +  if (!this.isActive()) {
    +    this.channelDebug_.debug('Inactive channel');
    +    retryTime =
    +        retryTime * goog.net.BrowserChannel.INACTIVE_CHANNEL_RETRY_FACTOR;
    +  }
    +  // Backoff for subsequent retries
    +  retryTime = retryTime * retryCount;
    +  return retryTime;
    +};
    +
    +
    +/**
    + * @param {number} baseDelayMs The base part of the retry delay, in ms.
    + * @param {number} delaySeedMs A random delay between 0 and this is added to
    + *     the base part.
    + */
    +goog.net.BrowserChannel.prototype.setRetryDelay = function(baseDelayMs,
    +    delaySeedMs) {
    +  this.baseRetryDelayMs_ = baseDelayMs;
    +  this.retryDelaySeedMs_ = delaySeedMs;
    +};
    +
    +
    +/**
    + * Processes the data returned by the server.
    + * @param {!Array<!Array<?>>} respArray The response array returned
    + *     by the server.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onInput_ = function(respArray) {
    +  var batch = this.handler_ && this.handler_.channelHandleMultipleArrays ?
    +      [] : null;
    +  for (var i = 0; i < respArray.length; i++) {
    +    var nextArray = respArray[i];
    +    this.lastArrayId_ = nextArray[0];
    +    nextArray = nextArray[1];
    +    if (this.state_ == goog.net.BrowserChannel.State.OPENING) {
    +      if (nextArray[0] == 'c') {
    +        this.sid_ = nextArray[1];
    +        this.hostPrefix_ = this.correctHostPrefix(nextArray[2]);
    +        var negotiatedVersion = nextArray[3];
    +        if (goog.isDefAndNotNull(negotiatedVersion)) {
    +          this.channelVersion_ = negotiatedVersion;
    +        } else {
    +          // Servers prior to version 7 did not send this, so assume version 6.
    +          this.channelVersion_ = 6;
    +        }
    +        this.state_ = goog.net.BrowserChannel.State.OPENED;
    +        if (this.handler_) {
    +          this.handler_.channelOpened(this);
    +        }
    +        this.backChannelUri_ = this.getBackChannelUri(
    +            this.hostPrefix_, /** @type {string} */ (this.path_));
    +        // Open connection to receive data
    +        this.ensureBackChannel_();
    +      } else if (nextArray[0] == 'stop') {
    +        this.signalError_(goog.net.BrowserChannel.Error.STOP);
    +      }
    +    } else if (this.state_ == goog.net.BrowserChannel.State.OPENED) {
    +      if (nextArray[0] == 'stop') {
    +        if (batch && !goog.array.isEmpty(batch)) {
    +          this.handler_.channelHandleMultipleArrays(this, batch);
    +          batch.length = 0;
    +        }
    +        this.signalError_(goog.net.BrowserChannel.Error.STOP);
    +      } else if (nextArray[0] == 'noop') {
    +        // ignore - noop to keep connection happy
    +      } else {
    +        if (batch) {
    +          batch.push(nextArray);
    +        } else if (this.handler_) {
    +          this.handler_.channelHandleArray(this, nextArray);
    +        }
    +      }
    +      // We have received useful data on the back-channel, so clear its retry
    +      // count. We do this because back-channels by design do not complete
    +      // quickly, so on a flaky connection we could have many fail to complete
    +      // fully but still deliver a lot of data before they fail. We don't want
    +      // to count such failures towards the retry limit, because we don't want
    +      // to give up on a session if we can still receive data.
    +      this.backChannelRetryCount_ = 0;
    +    }
    +  }
    +  if (batch && !goog.array.isEmpty(batch)) {
    +    this.handler_.channelHandleMultipleArrays(this, batch);
    +  }
    +};
    +
    +
    +/**
    + * Helper to ensure the BrowserChannel is in the expected state.
    + * @param {...number} var_args The channel must be in one of the indicated
    + *     states.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.ensureInState_ = function(var_args) {
    +  if (!goog.array.contains(arguments, this.state_)) {
    +    throw Error('Unexpected channel state: ' + this.state_);
    +  }
    +};
    +
    +
    +/**
    + * Signals an error has occurred.
    + * @param {goog.net.BrowserChannel.Error} error  The error code for the failure.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.signalError_ = function(error) {
    +  this.channelDebug_.info('Error code ' + error);
    +  if (error == goog.net.BrowserChannel.Error.REQUEST_FAILED ||
    +      error == goog.net.BrowserChannel.Error.BLOCKED) {
    +    // Ping google to check if it's a server error or user's network error.
    +    var imageUri = null;
    +    if (this.handler_) {
    +      imageUri = this.handler_.getNetworkTestImageUri(this);
    +    }
    +    goog.net.tmpnetwork.testGoogleCom(
    +        goog.bind(this.testGoogleComCallback_, this), imageUri);
    +  } else {
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.ERROR_OTHER);
    +  }
    +  this.onError_(error);
    +};
    +
    +
    +/**
    + * Callback for testGoogleCom during error handling.
    + * @param {boolean} networkUp Whether the network is up.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.testGoogleComCallback_ = function(networkUp) {
    +  if (networkUp) {
    +    this.channelDebug_.info('Successfully pinged google.com');
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.ERROR_OTHER);
    +  } else {
    +    this.channelDebug_.info('Failed to ping google.com');
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.ERROR_NETWORK);
    +    // We call onError_ here instead of signalError_ because the latter just
    +    // calls notifyStatEvent, and we don't want to have another stat event.
    +    this.onError_(goog.net.BrowserChannel.Error.NETWORK);
    +  }
    +};
    +
    +
    +/**
    + * Called when messages have been successfully sent from the queue.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onSuccess_ = function() {
    +  if (this.handler_) {
    +    this.handler_.channelSuccess(this, this.pendingMaps_);
    +  }
    +};
    +
    +
    +/**
    + * Called when we've determined the final error for a channel. It closes the
    + * notifiers the handler of the error and closes the channel.
    + * @param {goog.net.BrowserChannel.Error} error  The error code for the failure.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onError_ = function(error) {
    +  this.channelDebug_.debug('HttpChannel: error - ' + error);
    +  this.state_ = goog.net.BrowserChannel.State.CLOSED;
    +  if (this.handler_) {
    +    this.handler_.channelError(this, error);
    +  }
    +  this.onClose_();
    +  this.cancelRequests_();
    +};
    +
    +
    +/**
    + * Called when the channel has been closed. It notifiers the handler of the
    + * event, and reports any pending or undelivered maps.
    + * @private
    + */
    +goog.net.BrowserChannel.prototype.onClose_ = function() {
    +  this.state_ = goog.net.BrowserChannel.State.CLOSED;
    +  this.lastStatusCode_ = -1;
    +  if (this.handler_) {
    +    if (this.pendingMaps_.length == 0 && this.outgoingMaps_.length == 0) {
    +      this.handler_.channelClosed(this);
    +    } else {
    +      this.channelDebug_.debug('Number of undelivered maps' +
    +          ', pending: ' + this.pendingMaps_.length +
    +          ', outgoing: ' + this.outgoingMaps_.length);
    +
    +      var copyOfPendingMaps = goog.array.clone(this.pendingMaps_);
    +      var copyOfUndeliveredMaps = goog.array.clone(this.outgoingMaps_);
    +      this.pendingMaps_.length = 0;
    +      this.outgoingMaps_.length = 0;
    +
    +      this.handler_.channelClosed(this,
    +          copyOfPendingMaps,
    +          copyOfUndeliveredMaps);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the Uri used for the connection that sends data to the server.
    + * @param {string} path The path on the host.
    + * @return {!goog.Uri} The forward channel URI.
    + */
    +goog.net.BrowserChannel.prototype.getForwardChannelUri =
    +    function(path) {
    +  var uri = this.createDataUri(null, path);
    +  this.channelDebug_.debug('GetForwardChannelUri: ' + uri);
    +  return uri;
    +};
    +
    +
    +/**
    + * Gets the results for the first browser channel test
    + * @return {Array<string>} The results.
    + */
    +goog.net.BrowserChannel.prototype.getFirstTestResults =
    +    function() {
    +  return this.firstTestResults_;
    +};
    +
    +
    +/**
    + * Gets the results for the second browser channel test
    + * @return {?boolean} The results. True -> buffered connection,
    + *      False -> unbuffered, null -> unknown.
    + */
    +goog.net.BrowserChannel.prototype.getSecondTestResults = function() {
    +  return this.secondTestResults_;
    +};
    +
    +
    +/**
    + * Gets the Uri used for the connection that receives data from the server.
    + * @param {?string} hostPrefix The host prefix.
    + * @param {string} path The path on the host.
    + * @return {!goog.Uri} The back channel URI.
    + */
    +goog.net.BrowserChannel.prototype.getBackChannelUri =
    +    function(hostPrefix, path) {
    +  var uri = this.createDataUri(this.shouldUseSecondaryDomains() ?
    +      hostPrefix : null, path);
    +  this.channelDebug_.debug('GetBackChannelUri: ' + uri);
    +  return uri;
    +};
    +
    +
    +/**
    + * Creates a data Uri applying logic for secondary hostprefix, port
    + * overrides, and versioning.
    + * @param {?string} hostPrefix The host prefix.
    + * @param {string} path The path on the host (may be absolute or relative).
    + * @param {number=} opt_overridePort Optional override port.
    + * @return {!goog.Uri} The data URI.
    + */
    +goog.net.BrowserChannel.prototype.createDataUri =
    +    function(hostPrefix, path, opt_overridePort) {
    +  var uri = goog.Uri.parse(path);
    +  var uriAbsolute = (uri.getDomain() != '');
    +  if (uriAbsolute) {
    +    if (hostPrefix) {
    +      uri.setDomain(hostPrefix + '.' + uri.getDomain());
    +    }
    +
    +    uri.setPort(opt_overridePort || uri.getPort());
    +  } else {
    +    var locationPage = window.location;
    +    var hostName;
    +    if (hostPrefix) {
    +      hostName = hostPrefix + '.' + locationPage.hostname;
    +    } else {
    +      hostName = locationPage.hostname;
    +    }
    +
    +    var port = opt_overridePort || locationPage.port;
    +
    +    uri = goog.Uri.create(locationPage.protocol, null, hostName, port, path);
    +  }
    +
    +  if (this.extraParams_) {
    +    goog.object.forEach(this.extraParams_, function(value, key) {
    +      uri.setParameterValue(key, value);
    +    });
    +  }
    +
    +  // Add the protocol version to the URI.
    +  uri.setParameterValue('VER', this.channelVersion_);
    +
    +  // Add the reconnect parameters.
    +  this.addAdditionalParams_(uri);
    +
    +  return uri;
    +};
    +
    +
    +/**
    + * Called when BC needs to create an XhrIo object.  Override in a subclass if
    + * you need to customize the behavior, for example to enable the creation of
    + * XHR's capable of calling a secondary domain. Will also allow calling
    + * a secondary domain if withCredentials (CORS) is enabled.
    + * @param {?string} hostPrefix The host prefix, if we need an XhrIo object
    + *     capable of calling a secondary domain.
    + * @return {!goog.net.XhrIo} A new XhrIo object.
    + */
    +goog.net.BrowserChannel.prototype.createXhrIo = function(hostPrefix) {
    +  if (hostPrefix && !this.supportsCrossDomainXhrs_) {
    +    throw Error('Can\'t create secondary domain capable XhrIo object.');
    +  }
    +  var xhr = new goog.net.XhrIo();
    +  xhr.setWithCredentials(this.supportsCrossDomainXhrs_);
    +  return xhr;
    +};
    +
    +
    +/**
    + * Gets whether this channel is currently active. This is used to determine the
    + * length of time to wait before retrying. This call delegates to the handler.
    + * @return {boolean} Whether the channel is currently active.
    + */
    +goog.net.BrowserChannel.prototype.isActive = function() {
    +  return !!this.handler_ && this.handler_.isActive(this);
    +};
    +
    +
    +/**
    + * Wrapper around SafeTimeout which calls the start and end execution hooks
    + * with a try...finally block.
    + * @param {Function} fn The callback function.
    + * @param {number} ms The time in MS for the timer.
    + * @return {number} The ID of the timer.
    + */
    +goog.net.BrowserChannel.setTimeout = function(fn, ms) {
    +  if (!goog.isFunction(fn)) {
    +    throw Error('Fn must not be null and must be a function');
    +  }
    +  return goog.global.setTimeout(function() {
    +    goog.net.BrowserChannel.onStartExecution();
    +    try {
    +      fn();
    +    } finally {
    +      goog.net.BrowserChannel.onEndExecution();
    +    }
    +  }, ms);
    +};
    +
    +
    +/**
    + * Helper function to call the start hook
    + */
    +goog.net.BrowserChannel.onStartExecution = function() {
    +  goog.net.BrowserChannel.startExecutionHook_();
    +};
    +
    +
    +/**
    + * Helper function to call the end hook
    + */
    +goog.net.BrowserChannel.onEndExecution = function() {
    +  goog.net.BrowserChannel.endExecutionHook_();
    +};
    +
    +
    +/**
    + * Returns the singleton event target for stat events.
    + * @return {goog.events.EventTarget} The event target for stat events.
    + */
    +goog.net.BrowserChannel.getStatEventTarget = function() {
    +  return goog.net.BrowserChannel.statEventTarget_;
    +};
    +
    +
    +/**
    + * Notify the channel that a particular fine grained network event has occurred.
    + * Should be considered package-private.
    + * @param {goog.net.BrowserChannel.ServerReachability} reachabilityType The
    + *     reachability event type.
    + */
    +goog.net.BrowserChannel.prototype.notifyServerReachabilityEvent = function(
    +    reachabilityType) {
    +  var target = goog.net.BrowserChannel.statEventTarget_;
    +  target.dispatchEvent(new goog.net.BrowserChannel.ServerReachabilityEvent(
    +      target, reachabilityType));
    +};
    +
    +
    +/**
    + * Helper function to call the stat event callback.
    + * @param {goog.net.BrowserChannel.Stat} stat The stat.
    + */
    +goog.net.BrowserChannel.notifyStatEvent = function(stat) {
    +  var target = goog.net.BrowserChannel.statEventTarget_;
    +  target.dispatchEvent(
    +      new goog.net.BrowserChannel.StatEvent(target, stat));
    +};
    +
    +
    +/**
    + * Helper function to notify listeners about POST request performance.
    + *
    + * @param {number} size Number of characters in the POST data.
    + * @param {number} rtt The amount of time from POST start to response.
    + * @param {number} retries The number of times the POST had to be retried.
    + */
    +goog.net.BrowserChannel.notifyTimingEvent = function(size, rtt, retries) {
    +  var target = goog.net.BrowserChannel.statEventTarget_;
    +  target.dispatchEvent(
    +      new goog.net.BrowserChannel.TimingEvent(target, size, rtt, retries));
    +};
    +
    +
    +/**
    + * Determines whether to use a secondary domain when the server gives us
    + * a host prefix. This allows us to work around browser per-domain
    + * connection limits.
    + *
    + * Currently, we  use secondary domains when using Trident's ActiveXObject,
    + * because it supports cross-domain requests out of the box.  Note that in IE10
    + * we no longer use ActiveX since it's not supported in Metro mode and IE10
    + * supports XHR streaming.
    + *
    + * If you need to use secondary domains on other browsers and IE10,
    + * you have two choices:
    + *     1) If you only care about browsers that support CORS
    + *        (https://developer.mozilla.org/en-US/docs/HTTP_access_control), you
    + *        can use {@link #setSupportsCrossDomainXhrs} and set the appropriate
    + *        CORS response headers on the server.
    + *     2) Or, override this method in a subclass, and make sure that those
    + *        browsers use some messaging mechanism that works cross-domain (e.g
    + *        iframes and window.postMessage).
    + *
    + * @return {boolean} Whether to use secondary domains.
    + * @see http://code.google.com/p/closure-library/issues/detail?id=339
    + */
    +goog.net.BrowserChannel.prototype.shouldUseSecondaryDomains = function() {
    +  return this.supportsCrossDomainXhrs_ ||
    +      !goog.net.ChannelRequest.supportsXhrStreaming();
    +};
    +
    +
    +/**
    + * A LogSaver that can be used to accumulate all the debug logs for
    + * BrowserChannels so they can be sent to the server when a problem is
    + * detected.
    + * @const
    + */
    +goog.net.BrowserChannel.LogSaver = {};
    +
    +
    +/**
    + * Buffer for accumulating the debug log
    + * @type {goog.structs.CircularBuffer}
    + * @private
    + */
    +goog.net.BrowserChannel.LogSaver.buffer_ =
    +    new goog.structs.CircularBuffer(1000);
    +
    +
    +/**
    + * Whether we're currently accumulating the debug log.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.BrowserChannel.LogSaver.enabled_ = false;
    +
    +
    +/**
    + * Formatter for saving logs.
    + * @type {goog.debug.Formatter}
    + * @private
    + */
    +goog.net.BrowserChannel.LogSaver.formatter_ = new goog.debug.TextFormatter();
    +
    +
    +/**
    + * Returns whether the LogSaver is enabled.
    + * @return {boolean} Whether saving is enabled or disabled.
    + */
    +goog.net.BrowserChannel.LogSaver.isEnabled = function() {
    +  return goog.net.BrowserChannel.LogSaver.enabled_;
    +};
    +
    +
    +/**
    + * Enables of disables the LogSaver.
    + * @param {boolean} enable Whether to enable or disable saving.
    + */
    +goog.net.BrowserChannel.LogSaver.setEnabled = function(enable) {
    +  if (enable == goog.net.BrowserChannel.LogSaver.enabled_) {
    +    return;
    +  }
    +
    +  var fn = goog.net.BrowserChannel.LogSaver.addLogRecord;
    +  var logger = goog.log.getLogger('goog.net');
    +  if (enable) {
    +    goog.log.addHandler(logger, fn);
    +  } else {
    +    goog.log.removeHandler(logger, fn);
    +  }
    +};
    +
    +
    +/**
    + * Adds a log record.
    + * @param {goog.log.LogRecord} logRecord the LogRecord.
    + */
    +goog.net.BrowserChannel.LogSaver.addLogRecord = function(logRecord) {
    +  goog.net.BrowserChannel.LogSaver.buffer_.add(
    +      goog.net.BrowserChannel.LogSaver.formatter_.formatRecord(logRecord));
    +};
    +
    +
    +/**
    + * Returns the log as a single string.
    + * @return {string} The log as a single string.
    + */
    +goog.net.BrowserChannel.LogSaver.getBuffer = function() {
    +  return goog.net.BrowserChannel.LogSaver.buffer_.getValues().join('');
    +};
    +
    +
    +/**
    + * Clears the buffer
    + */
    +goog.net.BrowserChannel.LogSaver.clearBuffer = function() {
    +  goog.net.BrowserChannel.LogSaver.buffer_.clear();
    +};
    +
    +
    +
    +/**
    + * Abstract base class for the browser channel handler
    + * @constructor
    + */
    +goog.net.BrowserChannel.Handler = function() {
    +};
    +
    +
    +/**
    + * Callback handler for when a batch of response arrays is received from the
    + * server.
    + * @type {?function(!goog.net.BrowserChannel, !Array<!Array<?>>)}
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelHandleMultipleArrays = null;
    +
    +
    +/**
    + * Whether it's okay to make a request to the server. A handler can return
    + * false if the channel should fail. For example, if the user has logged out,
    + * the handler may want all requests to fail immediately.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @return {goog.net.BrowserChannel.Error} An error code. The code should
    + * return goog.net.BrowserChannel.Error.OK to indicate it's okay. Any other
    + * error code will cause a failure.
    + */
    +goog.net.BrowserChannel.Handler.prototype.okToMakeRequest =
    +    function(browserChannel) {
    +  return goog.net.BrowserChannel.Error.OK;
    +};
    +
    +
    +/**
    + * Indicates the BrowserChannel has successfully negotiated with the server
    + * and can now send and receive data.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelOpened =
    +    function(browserChannel) {
    +};
    +
    +
    +/**
    + * New input is available for the application to process.
    + *
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @param {Array<?>} array The data array.
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelHandleArray =
    +    function(browserChannel, array) {
    +};
    +
    +
    +/**
    + * Indicates maps were successfully sent on the BrowserChannel.
    + *
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @param {Array<goog.net.BrowserChannel.QueuedMap>} deliveredMaps The
    + *     array of maps that have been delivered to the server. This is a direct
    + *     reference to the internal BrowserChannel array, so a copy should be made
    + *     if the caller desires a reference to the data.
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelSuccess =
    +    function(browserChannel, deliveredMaps) {
    +};
    +
    +
    +/**
    + * Indicates an error occurred on the BrowserChannel.
    + *
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @param {goog.net.BrowserChannel.Error} error The error code.
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelError =
    +    function(browserChannel, error) {
    +};
    +
    +
    +/**
    + * Indicates the BrowserChannel is closed. Also notifies about which maps,
    + * if any, that may not have been delivered to the server.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @param {Array<goog.net.BrowserChannel.QueuedMap>=} opt_pendingMaps The
    + *     array of pending maps, which may or may not have been delivered to the
    + *     server.
    + * @param {Array<goog.net.BrowserChannel.QueuedMap>=} opt_undeliveredMaps
    + *     The array of undelivered maps, which have definitely not been delivered
    + *     to the server.
    + */
    +goog.net.BrowserChannel.Handler.prototype.channelClosed =
    +    function(browserChannel, opt_pendingMaps, opt_undeliveredMaps) {
    +};
    +
    +
    +/**
    + * Gets any parameters that should be added at the time another connection is
    + * made to the server.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @return {Object} Extra parameter keys and values to add to the
    + *                  requests.
    + */
    +goog.net.BrowserChannel.Handler.prototype.getAdditionalParams =
    +    function(browserChannel) {
    +  return {};
    +};
    +
    +
    +/**
    + * Gets the URI of an image that can be used to test network connectivity.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @return {goog.Uri?} A custom URI to load for the network test.
    + */
    +goog.net.BrowserChannel.Handler.prototype.getNetworkTestImageUri =
    +    function(browserChannel) {
    +  return null;
    +};
    +
    +
    +/**
    + * Gets whether this channel is currently active. This is used to determine the
    + * length of time to wait before retrying.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @return {boolean} Whether the channel is currently active.
    + */
    +goog.net.BrowserChannel.Handler.prototype.isActive = function(browserChannel) {
    +  return true;
    +};
    +
    +
    +/**
    + * Called by the channel if enumeration of the map throws an exception.
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @param {Object} map The map that can't be enumerated.
    + */
    +goog.net.BrowserChannel.Handler.prototype.badMapError =
    +    function(browserChannel, map) {
    +  return;
    +};
    +
    +
    +/**
    + * Allows the handler to override a host prefix provided by the server.  Will
    + * be called whenever the channel has received such a prefix and is considering
    + * its use.
    + * @param {?string} serverHostPrefix The host prefix provided by the server.
    + * @return {?string} The host prefix the client should use.
    + */
    +goog.net.BrowserChannel.Handler.prototype.correctHostPrefix =
    +    function(serverHostPrefix) {
    +  return serverHostPrefix;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.html b/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.html
    new file mode 100644
    index 0000000000..b7bb6437e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.BrowserChannel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.net.BrowserChannelTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="debug" style="font-size: small">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.js b/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.js
    new file mode 100644
    index 0000000000..a7066acb89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/browserchannel_test.js
    @@ -0,0 +1,1325 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.BrowserChannelTest');
    +goog.setTestOnly('goog.net.BrowserChannelTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.functions');
    +goog.require('goog.json');
    +goog.require('goog.net.BrowserChannel');
    +goog.require('goog.net.ChannelDebug');
    +goog.require('goog.net.ChannelRequest');
    +goog.require('goog.net.tmpnetwork');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +
    +/**
    + * Delay between a network failure and the next network request.
    + */
    +var RETRY_TIME = 1000;
    +
    +
    +/**
    + * A really long time - used to make sure no more timeouts will fire.
    + */
    +var ALL_DAY_MS = 1000 * 60 * 60 * 24;
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var browserChannel;
    +var deliveredMaps;
    +var handler;
    +var mockClock;
    +var gotError;
    +var numStatEvents;
    +var lastStatEvent;
    +var numTimingEvents;
    +var lastPostSize;
    +var lastPostRtt;
    +var lastPostRetryCount;
    +
    +// Set to true to see the channel debug output in the browser window.
    +var debug = false;
    +// Debug message to print out when debug is true.
    +var debugMessage = '';
    +
    +function debugToWindow(message) {
    +  if (debug) {
    +    debugMessage += message + '<br>';
    +    goog.dom.getElement('debug').innerHTML = debugMessage;
    +  }
    +}
    +
    +
    +/**
    + * Stubs goog.net.tmpnetwork to always time out. It maintains the
    + * contract given by goog.net.tmpnetwork.testGoogleCom, but always
    + * times out (calling callback(false)).
    + *
    + * stubTmpnetwork should be called in tests that require it before
    + * a call to testGoogleCom happens. It is reset at tearDown.
    + */
    +function stubTmpnetwork() {
    +  stubs.set(goog.net.tmpnetwork, 'testLoadImage',
    +      function(url, timeout, callback) {
    +        goog.Timer.callOnce(goog.partial(callback, false), timeout);
    +      });
    +}
    +
    +
    +
    +/**
    + * Mock ChannelRequest.
    + * @constructor
    + */
    +var MockChannelRequest = function(channel, channelDebug, opt_sessionId,
    +    opt_requestId, opt_retryId) {
    +  this.channel_ = channel;
    +  this.channelDebug_ = channelDebug;
    +  this.sessionId_ = opt_sessionId;
    +  this.requestId_ = opt_requestId;
    +  this.successful_ = true;
    +  this.lastError_ = null;
    +  this.lastStatusCode_ = 200;
    +
    +  // For debugging, keep track of whether this is a back or forward channel.
    +  this.isBack = !!(opt_requestId == 'rpc');
    +  this.isForward = !this.isBack;
    +};
    +
    +MockChannelRequest.prototype.postData_ = null;
    +
    +MockChannelRequest.prototype.requestStartTime_ = null;
    +
    +MockChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {};
    +
    +MockChannelRequest.prototype.setTimeout = function(timeout) {};
    +
    +MockChannelRequest.prototype.setReadyStateChangeThrottle =
    +    function(throttle) {};
    +
    +MockChannelRequest.prototype.xmlHttpPost = function(uri, postData,
    +    decodeChunks) {
    +  this.channelDebug_.debug('---> POST: ' + uri + ', ' + postData + ', ' +
    +      decodeChunks);
    +  this.postData_ = postData;
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks,
    +    opt_noClose) {
    +  this.channelDebug_.debug('<--- GET: ' + uri + ', ' + decodeChunks + ', ' +
    +      opt_noClose);
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.tridentGet = function(uri, usingSecondaryDomain) {
    +  this.channelDebug_.debug('<---GET (T): ' + uri);
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.sendUsingImgTag = function(uri) {
    +  this.requestStartTime_ = goog.now();
    +};
    +
    +MockChannelRequest.prototype.cancel = function() {
    +  this.successful_ = false;
    +};
    +
    +MockChannelRequest.prototype.getSuccess = function() {
    +  return this.successful_;
    +};
    +
    +MockChannelRequest.prototype.getLastError = function() {
    +  return this.lastError_;
    +};
    +
    +MockChannelRequest.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +MockChannelRequest.prototype.getSessionId = function() {
    +  return this.sessionId_;
    +};
    +
    +MockChannelRequest.prototype.getRequestId = function() {
    +  return this.requestId_;
    +};
    +
    +MockChannelRequest.prototype.getPostData = function() {
    +  return this.postData_;
    +};
    +
    +MockChannelRequest.prototype.getRequestStartTime = function() {
    +  return this.requestStartTime_;
    +};
    +
    +
    +function setUpPage() {
    +  // Use our MockChannelRequests instead of the real ones.
    +  goog.net.BrowserChannel.createChannelRequest = function(
    +      channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId) {
    +    return new MockChannelRequest(
    +        channel, channelDebug, opt_sessionId, opt_requestId, opt_retryId);
    +  };
    +
    +  // Mock out the stat notification code.
    +  goog.net.BrowserChannel.notifyStatEvent = function(stat) {
    +    numStatEvents++;
    +    lastStatEvent = stat;
    +  };
    +
    +  goog.net.BrowserChannel.notifyTimingEvent = function(size, rtt, retries) {
    +    numTimingEvents++;
    +    lastPostSize = size;
    +    lastPostRtt = rtt;
    +    lastPostRetryCount = retries;
    +  };
    +}
    +
    +
    +function setUp() {
    +  numTimingEvents = 0;
    +  lastPostSize = null;
    +  lastPostRtt = null;
    +  lastPostRetryCount = null;
    +
    +  mockClock = new goog.testing.MockClock(true);
    +  browserChannel = new goog.net.BrowserChannel('1');
    +  gotError = false;
    +
    +  handler = new goog.net.BrowserChannel.Handler();
    +  handler.channelOpened = function() {};
    +  handler.channelError = function(channel, error) {
    +    gotError = true;
    +  };
    +  handler.channelSuccess = function(channel, maps) {
    +    deliveredMaps = goog.array.clone(maps);
    +  };
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    // Mock out the handler, and let it set a formatted user readable string
    +    // of the undelivered maps which we can use when verifying our assertions.
    +    if (opt_pendingMaps) {
    +      this.pendingMapsString = formatArrayOfMaps(opt_pendingMaps);
    +    }
    +    if (opt_undeliveredMaps) {
    +      this.undeliveredMapsString = formatArrayOfMaps(opt_undeliveredMaps);
    +    }
    +  };
    +  handler.channelHandleMultipleArrays = function() {};
    +  handler.channelHandleArray = function() {};
    +
    +  browserChannel.setHandler(handler);
    +
    +  // Provide a predictable retry time for testing.
    +  browserChannel.getRetryTime_ = function(retryCount) {
    +    return RETRY_TIME;
    +  };
    +
    +  var channelDebug = new goog.net.ChannelDebug();
    +  channelDebug.debug = function(message) {
    +    debugToWindow(message);
    +  };
    +  browserChannel.setChannelDebug(channelDebug);
    +
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +}
    +
    +
    +function tearDown() {
    +  mockClock.dispose();
    +  stubs.reset();
    +  debugToWindow('<hr>');
    +}
    +
    +
    +/**
    + * Helper function to return a formatted string representing an array of maps.
    + */
    +function formatArrayOfMaps(arrayOfMaps) {
    +  var result = [];
    +  for (var i = 0; i < arrayOfMaps.length; i++) {
    +    var map = arrayOfMaps[i];
    +    var keys = map.map.getKeys();
    +    for (var j = 0; j < keys.length; j++) {
    +      var tmp = keys[j] + ':' + map.map.get(keys[j]) + (map.context ?
    +          ':' + map.context : '');
    +      result.push(tmp);
    +    }
    +  }
    +  return result.join(', ');
    +}
    +
    +
    +function testFormatArrayOfMaps() {
    +  // This function is used in a non-trivial test, so let's verify that it works.
    +  var map1 = new goog.structs.Map();
    +  map1.set('k1', 'v1');
    +  map1.set('k2', 'v2');
    +  var map2 = new goog.structs.Map();
    +  map2.set('k3', 'v3');
    +  var map3 = new goog.structs.Map();
    +  map3.set('k4', 'v4');
    +  map3.set('k5', 'v5');
    +  map3.set('k6', 'v6');
    +
    +  // One map.
    +  var a = [];
    +  a.push(new goog.net.BrowserChannel.QueuedMap(0, map1));
    +  assertEquals('k1:v1, k2:v2',
    +      formatArrayOfMaps(a));
    +
    +  // Many maps.
    +  var b = [];
    +  b.push(new goog.net.BrowserChannel.QueuedMap(0, map1));
    +  b.push(new goog.net.BrowserChannel.QueuedMap(0, map2));
    +  b.push(new goog.net.BrowserChannel.QueuedMap(0, map3));
    +  assertEquals('k1:v1, k2:v2, k3:v3, k4:v4, k5:v5, k6:v6',
    +      formatArrayOfMaps(b));
    +
    +  // One map with a context.
    +  var c = [];
    +  c.push(new goog.net.BrowserChannel.QueuedMap(0, map1, 'c1'));
    +  assertEquals('k1:v1:c1, k2:v2:c1',
    +      formatArrayOfMaps(c));
    +}
    +
    +
    +function connectForwardChannel(
    +    opt_serverVersion, opt_hostPrefix, opt_uriPrefix) {
    +  var uriPrefix = opt_uriPrefix || '';
    +  browserChannel.connect(uriPrefix + '/test', uriPrefix + '/bind', null);
    +  mockClock.tick(0);
    +  completeTestConnection();
    +  completeForwardChannel(opt_serverVersion, opt_hostPrefix);
    +}
    +
    +
    +function connect(opt_serverVersion, opt_hostPrefix, opt_uriPrefix) {
    +  connectForwardChannel(opt_serverVersion, opt_hostPrefix, opt_uriPrefix);
    +  completeBackChannel();
    +}
    +
    +
    +function disconnect() {
    +  browserChannel.disconnect();
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeTestConnection() {
    +  completeForwardTestConnection();
    +  completeBackTestConnection();
    +  assertEquals(goog.net.BrowserChannel.State.OPENING,
    +      browserChannel.getState());
    +}
    +
    +
    +function completeForwardTestConnection() {
    +  browserChannel.connectionTest_.onRequestData(
    +      browserChannel.connectionTest_,
    +      '["b"]');
    +  browserChannel.connectionTest_.onRequestComplete(
    +      browserChannel.connectionTest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeBackTestConnection() {
    +  browserChannel.connectionTest_.onRequestData(
    +      browserChannel.connectionTest_,
    +      '11111');
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeForwardChannel(opt_serverVersion, opt_hostPrefix) {
    +  var responseData = '[[0,["c","1234567890ABCDEF",' +
    +      (opt_hostPrefix ? '"' + opt_hostPrefix + '"' : 'null') +
    +      (opt_serverVersion ? ',' + opt_serverVersion : '') +
    +      ']]]';
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      responseData);
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function completeBackChannel() {
    +  browserChannel.onRequestData(
    +      browserChannel.backChannelRequest_,
    +      '[[1,["foo"]]]');
    +  browserChannel.onRequestComplete(
    +      browserChannel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseVersion7() {
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      goog.net.BrowserChannel.MAGIC_RESPONSE_COOKIE);
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +function responseNoBackchannel(lastArrayIdSentFromServer, outstandingDataSize) {
    +  responseData = goog.json.serialize(
    +      [0, lastArrayIdSentFromServer, outstandingDataSize]);
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      responseData);
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +function response(lastArrayIdSentFromServer, outstandingDataSize) {
    +  responseData = goog.json.serialize(
    +      [1, lastArrayIdSentFromServer, outstandingDataSize]);
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      responseData
    +  );
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function receive(data) {
    +  browserChannel.onRequestData(
    +      browserChannel.backChannelRequest_,
    +      '[[1,' + data + ']]');
    +  browserChannel.onRequestComplete(
    +      browserChannel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseTimeout() {
    +  browserChannel.forwardChannelRequest_lastError_ =
    +      goog.net.ChannelRequest.Error.TIMEOUT;
    +  browserChannel.forwardChannelRequest_.successful_ = false;
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseRequestFailed(opt_statusCode) {
    +  browserChannel.forwardChannelRequest_.lastError_ =
    +      goog.net.ChannelRequest.Error.STATUS;
    +  browserChannel.forwardChannelRequest_.lastStatusCode_ =
    +      opt_statusCode || 503;
    +  browserChannel.forwardChannelRequest_.successful_ = false;
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseUnknownSessionId() {
    +  browserChannel.forwardChannelRequest_.lastError_ =
    +      goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID;
    +  browserChannel.forwardChannelRequest_.successful_ = false;
    +  browserChannel.onRequestComplete(
    +      browserChannel.forwardChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function responseActiveXBlocked() {
    +  browserChannel.backChannelRequest_.lastError_ =
    +      goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED;
    +  browserChannel.backChannelRequest_.successful_ = false;
    +  browserChannel.onRequestComplete(
    +      browserChannel.backChannelRequest_);
    +  mockClock.tick(0);
    +}
    +
    +
    +function sendMap(key, value, opt_context) {
    +  var map = new goog.structs.Map();
    +  map.set(key, value);
    +  browserChannel.sendMap(map, opt_context);
    +  mockClock.tick(0);
    +}
    +
    +
    +function hasForwardChannel() {
    +  return !!browserChannel.forwardChannelRequest_;
    +}
    +
    +
    +function hasBackChannel() {
    +  return !!browserChannel.backChannelRequest_;
    +}
    +
    +
    +function hasDeadBackChannelTimer() {
    +  return goog.isDefAndNotNull(browserChannel.deadBackChannelTimerId_);
    +}
    +
    +
    +function assertHasForwardChannel() {
    +  assertTrue('Forward channel missing.', hasForwardChannel());
    +}
    +
    +
    +function assertHasBackChannel() {
    +  assertTrue('Back channel missing.', hasBackChannel());
    +}
    +
    +
    +function testConnect() {
    +  connect();
    +  assertEquals(goog.net.BrowserChannel.State.OPENED,
    +      browserChannel.getState());
    +  // If the server specifies no version, the client assumes 6
    +  assertEquals(6, browserChannel.channelVersion_);
    +  assertFalse(browserChannel.isBuffered());
    +}
    +
    +function testConnect_backChannelEstablished() {
    +  connect();
    +  assertHasBackChannel();
    +}
    +
    +function testConnect_withServerHostPrefix() {
    +  connect(undefined, 'serverHostPrefix');
    +  assertEquals('serverHostPrefix', browserChannel.hostPrefix_);
    +}
    +
    +function testConnect_withClientHostPrefix() {
    +  handler.correctHostPrefix = function(hostPrefix) {
    +    return 'clientHostPrefix';
    +  };
    +  connect();
    +  assertEquals('clientHostPrefix', browserChannel.hostPrefix_);
    +}
    +
    +function testConnect_overrideServerHostPrefix() {
    +  handler.correctHostPrefix = function(hostPrefix) {
    +    return 'clientHostPrefix';
    +  };
    +  connect(undefined, 'serverHostPrefix');
    +  assertEquals('clientHostPrefix', browserChannel.hostPrefix_);
    +}
    +
    +function testConnect_withServerVersion() {
    +  connect(8);
    +  assertEquals(8, browserChannel.channelVersion_);
    +}
    +
    +function testConnect_notOkToMakeRequestForTest() {
    +  handler.okToMakeRequest =
    +      goog.functions.constant(goog.net.BrowserChannel.Error.NETWORK);
    +  browserChannel.connect('/test', '/bind', null);
    +  mockClock.tick(0);
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED, browserChannel.getState());
    +}
    +
    +function testConnect_notOkToMakeRequestForBind() {
    +  browserChannel.connect('/test', '/bind', null);
    +  mockClock.tick(0);
    +  completeTestConnection();
    +  handler.okToMakeRequest =
    +      goog.functions.constant(goog.net.BrowserChannel.Error.NETWORK);
    +  completeForwardChannel();
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED, browserChannel.getState());
    +}
    +
    +
    +function testSendMap() {
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +  sendMap('foo', 'bar');
    +  responseVersion7();
    +  assertEquals(2, numTimingEvents);
    +  assertEquals('foo:bar', formatArrayOfMaps(deliveredMaps));
    +}
    +
    +
    +function testSendMap_twice() {
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  responseVersion7();
    +  assertEquals('foo1:bar1', formatArrayOfMaps(deliveredMaps));
    +  sendMap('foo2', 'bar2');
    +  responseVersion7();
    +  assertEquals('foo2:bar2', formatArrayOfMaps(deliveredMaps));
    +}
    +
    +
    +function testSendMap_andReceive() {
    +  connect();
    +  sendMap('foo', 'bar');
    +  responseVersion7();
    +  receive('["the server reply"]');
    +}
    +
    +
    +function testReceive() {
    +  connect();
    +  receive('["message from server"]');
    +  assertHasBackChannel();
    +}
    +
    +
    +function testReceive_twice() {
    +  connect();
    +  receive('["message one from server"]');
    +  receive('["message two from server"]');
    +  assertHasBackChannel();
    +}
    +
    +
    +function testReceive_andSendMap() {
    +  connect();
    +  receive('["the server reply"]');
    +  sendMap('foo', 'bar');
    +  responseVersion7();
    +  assertHasBackChannel();
    +}
    +
    +
    +function testBackChannelRemainsEstablished_afterSingleSendMap() {
    +  connect();
    +
    +  sendMap('foo', 'bar');
    +  responseVersion7();
    +  receive('["ack"]');
    +
    +  assertHasBackChannel();
    +}
    +
    +
    +function testBackChannelRemainsEstablished_afterDoubleSendMap() {
    +  connect();
    +
    +  sendMap('foo1', 'bar1');
    +  sendMap('foo2', 'bar2');
    +  responseVersion7();
    +  receive('["ack"]');
    +
    +  // This assertion would fail prior to CL 13302660.
    +  assertHasBackChannel();
    +}
    +
    +
    +function testTimingEvent() {
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +  sendMap('', '');
    +  assertEquals(1, numTimingEvents);
    +  mockClock.tick(20);
    +  var expSize = browserChannel.forwardChannelRequest_.getPostData().length;
    +  responseVersion7();
    +
    +  assertEquals(2, numTimingEvents);
    +  assertEquals(expSize, lastPostSize);
    +  assertEquals(20, lastPostRtt);
    +  assertEquals(0, lastPostRetryCount);
    +
    +  sendMap('abcdefg', '123456');
    +  expSize = browserChannel.forwardChannelRequest_.getPostData().length;
    +  responseTimeout();
    +  assertEquals(2, numTimingEvents);
    +  mockClock.tick(RETRY_TIME + 1);
    +  responseVersion7();
    +  assertEquals(3, numTimingEvents);
    +  assertEquals(expSize, lastPostSize);
    +  assertEquals(1, lastPostRetryCount);
    +  assertEquals(1, lastPostRtt);
    +
    +}
    +
    +
    +/**
    + * Make sure that dropping the forward channel retry limit below the retry count
    + * reports an error, and prevents another request from firing.
    + */
    +function testSetFailFastWhileWaitingForRetry() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +
    +  // Almost finish the between-retry timeout.
    +  mockClock.tick(RETRY_TIME - 1);
    +  assertNotNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +
    +  // Setting max retries to 0 should cancel the timer and raise an error.
    +  browserChannel.setFailFast(true);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  assertEquals(0, deliveredMaps.length);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  // initial failure.
    +  gotError = false;
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +  assertTrue('No error after tmpnetwork ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +/**
    + * Make sure that dropping the forward channel retry limit below the retry count
    + * reports an error, and prevents another request from firing.
    + */
    +function testSetFailFastWhileRetryXhrIsInFlight() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +
    +  // Wait for the between-retry timeout.
    +  mockClock.tick(RETRY_TIME);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(1, browserChannel.forwardChannelRetryCount_);
    +
    +  // Simulate a second watchdog timeout.
    +  responseTimeout();
    +  assertNotNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(2, browserChannel.forwardChannelRetryCount_);
    +
    +  // Wait for another between-retry timeout.
    +  mockClock.tick(RETRY_TIME);
    +  // Now the third req is in flight.
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(2, browserChannel.forwardChannelRetryCount_);
    +
    +  // Set fail fast, killing the request
    +  browserChannel.setFailFast(true);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(2, browserChannel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  gotError = false;
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +  assertTrue('No error after tmpnetwork ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(2, browserChannel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +/**
    + * Makes sure that setting fail fast while not retrying doesn't cause a failure.
    + */
    +function testSetFailFastAtRetryCount() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +
    +  // Set fail fast.
    +  browserChannel.setFailFast(true);
    +  // Request should still be alive.
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNotNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +
    +  // Watchdog timeout. Now we should get an error.
    +  responseTimeout();
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +
    +  assertTrue(gotError);
    +  // We get the error immediately before starting to ping google.com.
    +  // Simulate that timing out. We should get a network error in addition to the
    +  // initial failure.
    +  gotError = false;
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +  assertTrue('No error after tmpnetwork ping timed out.', gotError);
    +
    +  // Make sure no more retry timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertNull(browserChannel.forwardChannelTimerId_);
    +  assertNull(browserChannel.forwardChannelRequest_);
    +  assertEquals(0, browserChannel.forwardChannelRetryCount_);
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +function testRequestFailedClosesChannel() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  assertEquals(1, numTimingEvents);
    +
    +  sendMap('foo', 'bar');
    +  responseRequestFailed();
    +
    +  assertEquals('Should be closed immediately after request failed.',
    +      goog.net.BrowserChannel.State.CLOSED, browserChannel.getState());
    +
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +
    +  assertEquals('Should remain closed after the ping timeout.',
    +      goog.net.BrowserChannel.State.CLOSED, browserChannel.getState());
    +  assertEquals(1, numTimingEvents);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseUnknownSessionId();
    +
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.net.BrowserChannel.Stat.ERROR_OTHER, lastStatEvent);
    +
    +  numStatEvents = 0;
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +  assertEquals('No new stat events should be reported.', 0, numStatEvents);
    +}
    +
    +
    +function testActiveXBlockedEventReportedOnlyOnce() {
    +  stubTmpnetwork();
    +
    +  connectForwardChannel();
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseActiveXBlocked();
    +
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.net.BrowserChannel.Stat.ERROR_OTHER, lastStatEvent);
    +
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +  assertEquals('No new stat events should be reported.', 1, numStatEvents);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce_onNetworkUp() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseRequestFailed();
    +
    +  assertEquals('No stat event should be reported before we know the reason.',
    +      0, numStatEvents);
    +
    +  // Let the ping time out.
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +
    +  // Assert we report the correct stat event.
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.net.BrowserChannel.Stat.ERROR_NETWORK, lastStatEvent);
    +}
    +
    +
    +function testStatEventReportedOnlyOnce_onNetworkDown() {
    +  stubTmpnetwork();
    +
    +  connect();
    +  sendMap('foo', 'bar');
    +  numStatEvents = 0;
    +  lastStatEvent = null;
    +  responseRequestFailed();
    +
    +  assertEquals('No stat event should be reported before we know the reason.',
    +      0, numStatEvents);
    +
    +  // Wait half the ping timeout period, and then fake the network being up.
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT / 2);
    +  browserChannel.testGoogleComCallback_(true);
    +
    +  // Assert we report the correct stat event.
    +  assertEquals(1, numStatEvents);
    +  assertEquals(goog.net.BrowserChannel.Stat.ERROR_OTHER, lastStatEvent);
    +}
    +
    +
    +function testOutgoingMapsAwaitsResponse() {
    +  connect();
    +  assertEquals(0, browserChannel.outgoingMaps_.length);
    +
    +  sendMap('foo1', 'bar');
    +  assertEquals(0, browserChannel.outgoingMaps_.length);
    +  sendMap('foo2', 'bar');
    +  assertEquals(1, browserChannel.outgoingMaps_.length);
    +  sendMap('foo3', 'bar');
    +  assertEquals(2, browserChannel.outgoingMaps_.length);
    +  sendMap('foo4', 'bar');
    +  assertEquals(3, browserChannel.outgoingMaps_.length);
    +
    +  responseVersion7();
    +  // Now the forward channel request is completed and a new started, so all maps
    +  // are dequeued from the array of outgoing maps into this new forward request.
    +  assertEquals(0, browserChannel.outgoingMaps_.length);
    +}
    +
    +
    +function testUndeliveredMaps_doesNotNotifyWhenSuccessful() {
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    if (opt_pendingMaps || opt_undeliveredMaps) {
    +      fail('No pending or undelivered maps should be reported.');
    +    }
    +  };
    +
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  responseVersion7();
    +  sendMap('foo2', 'bar2');
    +  responseVersion7();
    +  disconnect();
    +}
    +
    +
    +function testUndeliveredMaps_doesNotNotifyIfNothingWasSent() {
    +  handler.channelClosed = function(
    +      channel, opt_pendingMaps, opt_undeliveredMaps) {
    +    if (opt_pendingMaps || opt_undeliveredMaps) {
    +      fail('No pending or undelivered maps should be reported.');
    +    }
    +  };
    +
    +  connect();
    +  mockClock.tick(ALL_DAY_MS);
    +  disconnect();
    +}
    +
    +
    +function testUndeliveredMaps_clearsPendingMapsAfterNotifying() {
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  sendMap('foo2', 'bar2');
    +  sendMap('foo3', 'bar3');
    +
    +  assertEquals(1, browserChannel.pendingMaps_.length);
    +  assertEquals(2, browserChannel.outgoingMaps_.length);
    +
    +  disconnect();
    +
    +  assertEquals(0, browserChannel.pendingMaps_.length);
    +  assertEquals(0, browserChannel.outgoingMaps_.length);
    +}
    +
    +
    +function testUndeliveredMaps_notifiesWithContext() {
    +  connect();
    +
    +  // First send two messages that succeed.
    +  sendMap('foo1', 'bar1', 'context1');
    +  responseVersion7();
    +  sendMap('foo2', 'bar2', 'context2');
    +  responseVersion7();
    +
    +  // Pretend the server hangs and no longer responds.
    +  sendMap('foo3', 'bar3', 'context3');
    +  sendMap('foo4', 'bar4', 'context4');
    +  sendMap('foo5', 'bar5', 'context5');
    +
    +  // Give up.
    +  disconnect();
    +
    +  // Assert that we are informed of any undelivered messages; both about
    +  // #3 that was sent but which we don't know if the server received, and
    +  // #4 and #5 which remain in the outgoing maps and have not yet been sent.
    +  assertEquals('foo3:bar3:context3', handler.pendingMapsString);
    +  assertEquals('foo4:bar4:context4, foo5:bar5:context5',
    +      handler.undeliveredMapsString);
    +}
    +
    +
    +function testUndeliveredMaps_serviceUnavailable() {
    +  // Send a few maps, and let one fail.
    +  connect();
    +  sendMap('foo1', 'bar1');
    +  responseVersion7();
    +  sendMap('foo2', 'bar2');
    +  responseRequestFailed();
    +
    +  // After a failure, the channel should be closed.
    +  disconnect();
    +
    +  assertEquals('foo2:bar2', handler.pendingMapsString);
    +  assertEquals('', handler.undeliveredMapsString);
    +}
    +
    +
    +function testUndeliveredMaps_onPingTimeout() {
    +  stubTmpnetwork();
    +
    +  connect();
    +
    +  // Send a message.
    +  sendMap('foo1', 'bar1');
    +
    +  // Fake REQUEST_FAILED, triggering a ping to check the network.
    +  responseRequestFailed();
    +
    +  // Let the ping time out, unsuccessfully.
    +  mockClock.tick(goog.net.tmpnetwork.GOOGLECOM_TIMEOUT);
    +
    +  // Assert channel is closed.
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED,
    +      browserChannel.getState());
    +
    +  // Assert that the handler is notified about the undelivered messages.
    +  assertEquals('foo1:bar1', handler.pendingMapsString);
    +  assertEquals('', handler.undeliveredMapsString);
    +}
    +
    +
    +function testResponseNoBackchannelPostNotBeforeBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  mockClock.tick(10);
    +  assertFalse(browserChannel.backChannelRequest_.getRequestStartTime() <
    +      browserChannel.forwardChannelRequest_.getRequestStartTime());
    +  responseNoBackchannel();
    +  assertNotEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  response(-1, 0);
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE + 1);
    +  sendMap('foo2', 'bar2');
    +  assertTrue(browserChannel.backChannelRequest_.getRequestStartTime() +
    +      goog.net.BrowserChannel.RTT_ESTIMATE <
    +      browserChannel.forwardChannelRequest_.getRequestStartTime());
    +  responseNoBackchannel();
    +  assertEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_MISSING, lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannelWithNoBackchannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertNull(browserChannel.backChannelTimerId_);
    +  browserChannel.backChannelRequest_.cancel();
    +  browserChannel.backChannelRequest_ = null;
    +  responseNoBackchannel();
    +  assertEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_MISSING, lastStatEvent);
    +}
    +
    +
    +function testResponseNoBackchannelWithStartTimer() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  browserChannel.backChannelRequest_.cancel();
    +  browserChannel.backChannelRequest_ = null;
    +  browserChannel.backChannelTimerId_ = 123;
    +  responseNoBackchannel();
    +  assertNotEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_MISSING,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseWithNoArraySent() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +
    +  // Send a response as if the server hasn't sent down an array.
    +  response(-1, 0);
    +
    +  // POST response with an array ID lower than our last received is OK.
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +}
    +
    +
    +function testResponseWithArraysMissing() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE * 2);
    +  assertEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD, lastStatEvent);
    +}
    +
    +
    +function testMultipleResponsesWithArraysMissing() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  sendMap('foo2', 'bar2');
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE);
    +  response(8, 119);
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE);
    +  // The original timer should still fire.
    +  assertEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD, lastStatEvent);
    +}
    +
    +
    +function testOnlyRetryOnceBasedOnResponse() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  assertTrue(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE * 2);
    +  assertEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD, lastStatEvent);
    +  assertEquals(1, browserChannel.backChannelRetryCount_);
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE);
    +  sendMap('foo2', 'bar2');
    +  assertFalse(hasDeadBackChannelTimer());
    +  response(8, 119);
    +  assertFalse(hasDeadBackChannelTimer());
    +}
    +
    +
    +function testResponseWithArraysMissingAndLiveChannel() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays.
    +  response(7, 111);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE);
    +  assertTrue(hasDeadBackChannelTimer());
    +  receive('["ack"]');
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE);
    +  assertNotEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD, lastStatEvent);
    +}
    +
    +
    +function testResponseWithBigOutstandingData() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +
    +  // Send a response as if the server has sent down seven arrays and 50kbytes.
    +  response(7, 50000);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE * 2);
    +  assertNotEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseInBufferedMode() {
    +  connect(8);
    +  browserChannel.useChunked_ = false;
    +  sendMap('foo1', 'bar1');
    +  assertEquals(-1, browserChannel.lastPostResponseArrayId_);
    +  response(7, 111);
    +
    +  assertEquals(1, browserChannel.lastArrayId_);
    +  assertEquals(7, browserChannel.lastPostResponseArrayId_);
    +  assertFalse(hasDeadBackChannelTimer());
    +  mockClock.tick(goog.net.BrowserChannel.RTT_ESTIMATE * 2);
    +  assertNotEquals(goog.net.BrowserChannel.Stat.BACKCHANNEL_DEAD,
    +      lastStatEvent);
    +}
    +
    +
    +function testResponseWithGarbage() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      'garbage'
    +  );
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED,
    +      browserChannel.getState());
    +}
    +
    +
    +function testResponseWithGarbageInArray() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      '["garbage"]'
    +  );
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED,
    +      browserChannel.getState());
    +}
    +
    +
    +function testResponseWithEvilData() {
    +  connect(8);
    +  sendMap('foo1', 'bar1');
    +  browserChannel.onRequestData(
    +      browserChannel.forwardChannelRequest_,
    +      goog.net.BrowserChannel.LAST_ARRAY_ID_RESPONSE_PREFIX +
    +          '=<script>evil()\<\/script>&' +
    +      goog.net.BrowserChannel.OUTSTANDING_DATA_RESPONSE_PREFIX +
    +          '=<script>moreEvil()\<\/script>');
    +  assertEquals(goog.net.BrowserChannel.State.CLOSED,
    +      browserChannel.getState());
    +}
    +
    +
    +function testPathAbsolute() {
    +  connect(8, undefined, '/talkgadget');
    +  assertEquals(browserChannel.backChannelUri_.getDomain(),
    +      window.location.hostname);
    +  assertEquals(browserChannel.forwardChannelUri_.getDomain(),
    +      window.location.hostname);
    +}
    +
    +
    +function testPathRelative() {
    +  connect(8, undefined, 'talkgadget');
    +  assertEquals(browserChannel.backChannelUri_.getDomain(),
    +      window.location.hostname);
    +  assertEquals(browserChannel.forwardChannelUri_.getDomain(),
    +      window.location.hostname);
    +}
    +
    +
    +function testPathWithHost() {
    +  connect(8, undefined, 'https://example.com');
    +  assertEquals(browserChannel.backChannelUri_.getScheme(), 'https');
    +  assertEquals(browserChannel.backChannelUri_.getDomain(), 'example.com');
    +  assertEquals(browserChannel.forwardChannelUri_.getScheme(), 'https');
    +  assertEquals(browserChannel.forwardChannelUri_.getDomain(), 'example.com');
    +}
    +
    +function testCreateXhrIo() {
    +  var xhr = browserChannel.createXhrIo(null);
    +  assertFalse(xhr.getWithCredentials());
    +
    +  assertThrows(
    +      'Error connection to different host without CORS',
    +      goog.bind(browserChannel.createXhrIo, browserChannel, 'some_host'));
    +
    +  browserChannel.setSupportsCrossDomainXhrs(true);
    +
    +  xhr = browserChannel.createXhrIo(null);
    +  assertTrue(xhr.getWithCredentials());
    +
    +  xhr = browserChannel.createXhrIo('some_host');
    +  assertTrue(xhr.getWithCredentials());
    +}
    +
    +function testSetParser() {
    +  var recordUnsafeParse = goog.testing.recordFunction(
    +      goog.json.unsafeParse);
    +  var parser = {};
    +  parser.parse = recordUnsafeParse;
    +  browserChannel.setParser(parser);
    +
    +  connect();
    +  assertEquals(3, recordUnsafeParse.getCallCount());
    +
    +  var call3 = recordUnsafeParse.popLastCall();
    +  var call2 = recordUnsafeParse.popLastCall();
    +  var call1 = recordUnsafeParse.popLastCall();
    +
    +  assertEquals(1, call1.getArguments().length);
    +  assertEquals('["b"]', call1.getArgument(0));
    +
    +  assertEquals(1, call2.getArguments().length);
    +  assertEquals('[[0,["c","1234567890ABCDEF",null]]]', call2.getArgument(0));
    +
    +  assertEquals(1, call3.getArguments().length);
    +  assertEquals('[[1,["foo"]]]', call3.getArgument(0));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/browsertestchannel.js b/mapbox-gl/closure-library/closure/goog/net/browsertestchannel.js
    new file mode 100644
    index 0000000000..0245800c5d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/browsertestchannel.js
    @@ -0,0 +1,619 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the BrowserTestChannel class.  A
    + * BrowserTestChannel is used during the first part of channel negotiation
    + * with the server to create the channel. It helps us determine whether we're
    + * behind a buffering proxy. It also runs the logic to see if the channel
    + * has been blocked by a network administrator. This class is part of the
    + * BrowserChannel implementation and is not for use by normal application code.
    + *
    + */
    +
    +
    +
    +goog.provide('goog.net.BrowserTestChannel');
    +
    +goog.require('goog.json.EvalJsonProcessor');
    +goog.require('goog.net.ChannelRequest');
    +goog.require('goog.net.ChannelRequest.Error');
    +goog.require('goog.net.tmpnetwork');
    +goog.require('goog.string.Parser');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Encapsulates the logic for a single BrowserTestChannel.
    + *
    + * @constructor
    + * @param {goog.net.BrowserChannel} channel  The BrowserChannel that owns this
    + *     test channel.
    + * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for
    + *     logging.
    + * @final
    + */
    +goog.net.BrowserTestChannel = function(channel, channelDebug) {
    +  /**
    +   * The BrowserChannel that owns this test channel
    +   * @type {goog.net.BrowserChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The channel debug to use for logging
    +   * @type {goog.net.ChannelDebug}
    +   * @private
    +   */
    +  this.channelDebug_ = channelDebug;
    +
    +  /**
    +   * Parser for a response payload. Defaults to use
    +   * {@code goog.json.unsafeParse}. The parser should return an array.
    +   * @type {goog.string.Parser}
    +   * @private
    +   */
    +  this.parser_ = new goog.json.EvalJsonProcessor(null, true);
    +};
    +
    +
    +/**
    + * Extra HTTP headers to add to all the requests sent to the server.
    + * @type {Object}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.extraHeaders_ = null;
    +
    +
    +/**
    + * The test request.
    + * @type {goog.net.ChannelRequest}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.request_ = null;
    +
    +
    +/**
    + * Whether we have received the first result as an intermediate result. This
    + * helps us determine whether we're behind a buffering proxy.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.receivedIntermediateResult_ = false;
    +
    +
    +/**
    + * The time when the test request was started. We use timing in IE as
    + * a heuristic for whether we're behind a buffering proxy.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.startTime_ = null;
    +
    +
    +/**
    + * The time for of the first result part. We use timing in IE as a
    + * heuristic for whether we're behind a buffering proxy.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.firstTime_ = null;
    +
    +
    +/**
    + * The time for of the last result part. We use timing in IE as a
    + * heuristic for whether we're behind a buffering proxy.
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.lastTime_ = null;
    +
    +
    +/**
    + * The relative path for test requests.
    + * @type {?string}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.path_ = null;
    +
    +
    +/**
    + * The state of the state machine for this object.
    + *
    + * @type {?number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.state_ = null;
    +
    +
    +/**
    + * The last status code received.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.lastStatusCode_ = -1;
    +
    +
    +/**
    + * A subdomain prefix for using a subdomain in IE for the backchannel
    + * requests.
    + * @type {?string}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.hostPrefix_ = null;
    +
    +
    +/**
    + * A subdomain prefix for testing whether the channel was disabled by
    + * a network administrator;
    + * @type {?string}
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.blockedPrefix_ = null;
    +
    +
    +/**
    + * Enum type for the browser test channel state machine
    + * @enum {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.State_ = {
    +  /**
    +   * The state for the BrowserTestChannel state machine where we making the
    +   * initial call to get the server configured parameters.
    +   */
    +  INIT: 0,
    +
    +  /**
    +   * The state for the BrowserTestChannel state machine where we're checking to
    +   * see if the channel has been blocked.
    +   */
    +  CHECKING_BLOCKED: 1,
    +
    +  /**
    +   * The  state for the BrowserTestChannel state machine where we're checking to
    +   * se if we're behind a buffering proxy.
    +   */
    +  CONNECTION_TESTING: 2
    +};
    +
    +
    +/**
    + * Time in MS for waiting for the request to see if the channel is blocked.
    + * If the response takes longer than this many ms, we assume the request has
    + * failed.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.BLOCKED_TIMEOUT_ = 5000;
    +
    +
    +/**
    + * Number of attempts to try to see if the check to see if we're blocked
    + * succeeds. Sometimes the request can fail because of flaky network conditions
    + * and checking multiple times reduces false positives.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.BLOCKED_RETRIES_ = 3;
    +
    +
    +/**
    + * Time in ms between retries of the blocked request
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_ = 2000;
    +
    +
    +/**
    + * Time between chunks in the test connection that indicates that we
    + * are not behind a buffering proxy. This value should be less than or
    + * equals to the time between chunks sent from the server.
    + * @type {number}
    + * @private
    + */
    +goog.net.BrowserTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_ = 500;
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers.
    + */
    +goog.net.BrowserTestChannel.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Sets a new parser for the response payload. A custom parser may be set to
    + * avoid using eval(), for example.
    + * By default, the parser uses {@code goog.json.unsafeParse}.
    + * @param {!goog.string.Parser} parser Parser.
    + */
    +goog.net.BrowserTestChannel.prototype.setParser = function(parser) {
    +  this.parser_ = parser;
    +};
    +
    +
    +/**
    + * Starts the test channel. This initiates connections to the server.
    + *
    + * @param {string} path The relative uri for the test connection.
    + */
    +goog.net.BrowserTestChannel.prototype.connect = function(path) {
    +  this.path_ = path;
    +  var sendDataUri = this.channel_.getForwardChannelUri(this.path_);
    +
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      goog.net.BrowserChannel.Stat.TEST_STAGE_ONE_START);
    +  this.startTime_ = goog.now();
    +
    +  // If the channel already has the result of the first test, then skip it.
    +  var firstTestResults = this.channel_.getFirstTestResults();
    +  if (goog.isDefAndNotNull(firstTestResults)) {
    +    this.hostPrefix_ = this.channel_.correctHostPrefix(firstTestResults[0]);
    +    this.blockedPrefix_ = firstTestResults[1];
    +    if (this.blockedPrefix_) {
    +      this.state_ = goog.net.BrowserTestChannel.State_.CHECKING_BLOCKED;
    +      this.checkBlocked_();
    +    } else {
    +      this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
    +      this.connectStage2_();
    +    }
    +    return;
    +  }
    +
    +  // the first request returns server specific parameters
    +  sendDataUri.setParameterValues('MODE', 'init');
    +  this.request_ = goog.net.BrowserChannel.createChannelRequest(
    +      this, this.channelDebug_);
    +  this.request_.setExtraHeaders(this.extraHeaders_);
    +  this.request_.xmlHttpGet(sendDataUri, false /* decodeChunks */,
    +      null /* hostPrefix */, true /* opt_noClose */);
    +  this.state_ = goog.net.BrowserTestChannel.State_.INIT;
    +};
    +
    +
    +/**
    + * Checks to see whether the channel is blocked. This is for implementing the
    + * feature that allows network administrators to block Gmail Chat. The
    + * strategy to determine if we're blocked is to try to load an image off a
    + * special subdomain that network administrators will block access to if they
    + * are trying to block chat. For Gmail Chat, the subdomain is
    + * chatenabled.mail.google.com.
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.checkBlocked_ = function() {
    +  var uri = this.channel_.createDataUri(this.blockedPrefix_,
    +      '/mail/images/cleardot.gif');
    +  uri.makeUnique();
    +  goog.net.tmpnetwork.testLoadImageWithRetries(uri.toString(),
    +      goog.net.BrowserTestChannel.BLOCKED_TIMEOUT_,
    +      goog.bind(this.checkBlockedCallback_, this),
    +      goog.net.BrowserTestChannel.BLOCKED_RETRIES_,
    +      goog.net.BrowserTestChannel.BLOCKED_PAUSE_BETWEEN_RETRIES_);
    +  this.notifyServerReachabilityEvent(
    +      goog.net.BrowserChannel.ServerReachability.REQUEST_MADE);
    +};
    +
    +
    +/**
    + * Callback for testLoadImageWithRetries to check if browser channel is
    + * blocked.
    + * @param {boolean} succeeded Whether the request succeeded.
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.checkBlockedCallback_ = function(
    +    succeeded) {
    +  if (succeeded) {
    +    this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
    +    this.connectStage2_();
    +  } else {
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.CHANNEL_BLOCKED);
    +    this.channel_.testConnectionBlocked(this);
    +  }
    +
    +  // We don't dispatch a REQUEST_FAILED server reachability event when the
    +  // block request fails, as such a failure is not a good signal that the
    +  // server has actually become unreachable.
    +  if (succeeded) {
    +    this.notifyServerReachabilityEvent(
    +        goog.net.BrowserChannel.ServerReachability.REQUEST_SUCCEEDED);
    +  }
    +};
    +
    +
    +/**
    + * Begins the second stage of the test channel where we test to see if we're
    + * behind a buffering proxy. The server sends back a multi-chunked response
    + * with the first chunk containing the content '1' and then two seconds later
    + * sending the second chunk containing the content '2'. Depending on how we
    + * receive the content, we can tell if we're behind a buffering proxy.
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.connectStage2_ = function() {
    +  this.channelDebug_.debug('TestConnection: starting stage 2');
    +
    +  // If the second test results are available, skip its execution.
    +  var secondTestResults = this.channel_.getSecondTestResults();
    +  if (goog.isDefAndNotNull(secondTestResults)) {
    +    this.channelDebug_.debug(
    +        'TestConnection: skipping stage 2, precomputed result is '
    +        + secondTestResults ? 'Buffered' : 'Unbuffered');
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_START);
    +    if (secondTestResults) { // Buffered/Proxy connection
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.PROXY);
    +      this.channel_.testConnectionFinished(this, false);
    +    } else { // Unbuffered/NoProxy connection
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.NOPROXY);
    +      this.channel_.testConnectionFinished(this, true);
    +    }
    +    return; // Skip the test
    +  }
    +  this.request_ = goog.net.BrowserChannel.createChannelRequest(
    +      this, this.channelDebug_);
    +  this.request_.setExtraHeaders(this.extraHeaders_);
    +  var recvDataUri = this.channel_.getBackChannelUri(this.hostPrefix_,
    +      /** @type {string} */ (this.path_));
    +
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_START);
    +  if (!goog.net.ChannelRequest.supportsXhrStreaming()) {
    +    recvDataUri.setParameterValues('TYPE', 'html');
    +    this.request_.tridentGet(recvDataUri, Boolean(this.hostPrefix_));
    +  } else {
    +    recvDataUri.setParameterValues('TYPE', 'xmlhttp');
    +    this.request_.xmlHttpGet(recvDataUri, false /** decodeChunks */,
    +        this.hostPrefix_, false /** opt_noClose */);
    +  }
    +};
    +
    +
    +/**
    + * Factory method for XhrIo objects.
    + * @param {?string} hostPrefix The host prefix, if we need an XhrIo object
    + *     capable of calling a secondary domain.
    + * @return {!goog.net.XhrIo} New XhrIo object.
    + */
    +goog.net.BrowserTestChannel.prototype.createXhrIo = function(hostPrefix) {
    +  return this.channel_.createXhrIo(hostPrefix);
    +};
    +
    +
    +/**
    + * Aborts the test channel.
    + */
    +goog.net.BrowserTestChannel.prototype.abort = function() {
    +  if (this.request_) {
    +    this.request_.cancel();
    +    this.request_ = null;
    +  }
    +  this.lastStatusCode_ = -1;
    +};
    +
    +
    +/**
    + * Returns whether the test channel is closed. The ChannelRequest object expects
    + * this method to be implemented on its handler.
    + *
    + * @return {boolean} Whether the channel is closed.
    + */
    +goog.net.BrowserTestChannel.prototype.isClosed = function() {
    +  return false;
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest for when new data is received
    + *
    + * @param {goog.net.ChannelRequest} req  The request object.
    + * @param {string} responseText The text of the response.
    + */
    +goog.net.BrowserTestChannel.prototype.onRequestData =
    +    function(req, responseText) {
    +  this.lastStatusCode_ = req.getLastStatusCode();
    +  if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
    +    this.channelDebug_.debug('TestConnection: Got data for stage 1');
    +    if (!responseText) {
    +      this.channelDebug_.debug('TestConnection: Null responseText');
    +      // The server should always send text; something is wrong here
    +      this.channel_.testConnectionFailure(this,
    +          goog.net.ChannelRequest.Error.BAD_DATA);
    +      return;
    +    }
    +    /** @preserveTry */
    +    try {
    +      var respArray = this.parser_.parse(responseText);
    +    } catch (e) {
    +      this.channelDebug_.dumpException(e);
    +      this.channel_.testConnectionFailure(this,
    +          goog.net.ChannelRequest.Error.BAD_DATA);
    +      return;
    +    }
    +    this.hostPrefix_ = this.channel_.correctHostPrefix(respArray[0]);
    +    this.blockedPrefix_ = respArray[1];
    +  } else if (this.state_ ==
    +             goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
    +    if (this.receivedIntermediateResult_) {
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_TWO);
    +      this.lastTime_ = goog.now();
    +    } else {
    +      // '11111' is used instead of '1' to prevent a small amount of buffering
    +      // by Safari.
    +      if (responseText == '11111') {
    +        goog.net.BrowserChannel.notifyStatEvent(
    +            goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_ONE);
    +        this.receivedIntermediateResult_ = true;
    +        this.firstTime_ = goog.now();
    +        if (this.checkForEarlyNonBuffered_()) {
    +          // If early chunk detection is on, and we passed the tests,
    +          // assume HTTP_OK, cancel the test and turn on noproxy mode.
    +          this.lastStatusCode_ = 200;
    +          this.request_.cancel();
    +          this.channelDebug_.debug(
    +              'Test connection succeeded; using streaming connection');
    +          goog.net.BrowserChannel.notifyStatEvent(
    +              goog.net.BrowserChannel.Stat.NOPROXY);
    +          this.channel_.testConnectionFinished(this, true);
    +        }
    +      } else {
    +        goog.net.BrowserChannel.notifyStatEvent(
    +            goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_DATA_BOTH);
    +        this.firstTime_ = this.lastTime_ = goog.now();
    +        this.receivedIntermediateResult_ = false;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Callback from ChannelRequest that indicates a request has completed.
    + *
    + * @param {goog.net.ChannelRequest} req  The request object.
    + */
    +goog.net.BrowserTestChannel.prototype.onRequestComplete =
    +    function(req) {
    +  this.lastStatusCode_ = this.request_.getLastStatusCode();
    +  if (!this.request_.getSuccess()) {
    +    this.channelDebug_.debug(
    +        'TestConnection: request failed, in state ' + this.state_);
    +    if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.TEST_STAGE_ONE_FAILED);
    +    } else if (this.state_ ==
    +               goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.TEST_STAGE_TWO_FAILED);
    +    }
    +    this.channel_.testConnectionFailure(this,
    +        /** @type {goog.net.ChannelRequest.Error} */
    +        (this.request_.getLastError()));
    +    return;
    +  }
    +
    +  if (this.state_ == goog.net.BrowserTestChannel.State_.INIT) {
    +    this.channelDebug_.debug(
    +        'TestConnection: request complete for initial check');
    +    if (this.blockedPrefix_) {
    +      this.state_ = goog.net.BrowserTestChannel.State_.CHECKING_BLOCKED;
    +      this.checkBlocked_();
    +    } else {
    +      this.state_ = goog.net.BrowserTestChannel.State_.CONNECTION_TESTING;
    +      this.connectStage2_();
    +    }
    +  } else if (this.state_ ==
    +             goog.net.BrowserTestChannel.State_.CONNECTION_TESTING) {
    +    this.channelDebug_.debug('TestConnection: request complete for stage 2');
    +    var goodConn = false;
    +
    +    if (!goog.net.ChannelRequest.supportsXhrStreaming()) {
    +      // we always get Trident responses in separate calls to
    +      // onRequestData, so we have to check the time they came
    +      var ms = this.lastTime_ - this.firstTime_;
    +      if (ms < 200) {
    +        // TODO: need to empirically verify that this number is OK
    +        // for slow computers
    +        goodConn = false;
    +      } else {
    +        goodConn = true;
    +      }
    +    } else {
    +      goodConn = this.receivedIntermediateResult_;
    +    }
    +
    +    if (goodConn) {
    +      this.channelDebug_.debug(
    +          'Test connection succeeded; using streaming connection');
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.NOPROXY);
    +      this.channel_.testConnectionFinished(this, true);
    +    } else {
    +      this.channelDebug_.debug(
    +          'Test connection failed; not using streaming');
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          goog.net.BrowserChannel.Stat.PROXY);
    +      this.channel_.testConnectionFinished(this, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the last status code received for a request.
    + * @return {number} The last status code received for a request.
    + */
    +goog.net.BrowserTestChannel.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we should be using secondary domains when the
    + *     server instructs us to do so.
    + */
    +goog.net.BrowserTestChannel.prototype.shouldUseSecondaryDomains = function() {
    +  return this.channel_.shouldUseSecondaryDomains();
    +};
    +
    +
    +/**
    + * Gets whether this channel is currently active. This is used to determine the
    + * length of time to wait before retrying.
    + *
    + * @param {goog.net.BrowserChannel} browserChannel The browser channel.
    + * @return {boolean} Whether the channel is currently active.
    + */
    +goog.net.BrowserTestChannel.prototype.isActive =
    +    function(browserChannel) {
    +  return this.channel_.isActive();
    +};
    +
    +
    +/**
    + * @return {boolean} True if test stage 2 detected a non-buffered
    + *     channel early and early no buffering detection is enabled.
    + * @private
    + */
    +goog.net.BrowserTestChannel.prototype.checkForEarlyNonBuffered_ =
    +    function() {
    +  var ms = this.firstTime_ - this.startTime_;
    +
    +  // we always get Trident responses in separate calls to
    +  // onRequestData, so we have to check the time that the first came in
    +  // and verify that the data arrived before the second portion could
    +  // have been sent. For all other browser's we skip the timing test.
    +  return goog.net.ChannelRequest.supportsXhrStreaming() ||
    +      ms < goog.net.BrowserTestChannel.MIN_TIME_EXPECTED_BETWEEN_DATA_;
    +};
    +
    +
    +/**
    + * Notifies the channel of a fine grained network event.
    + * @param {goog.net.BrowserChannel.ServerReachability} reachabilityType The
    + *     reachability event type.
    + */
    +goog.net.BrowserTestChannel.prototype.notifyServerReachabilityEvent =
    +    function(reachabilityType) {
    +  this.channel_.notifyServerReachabilityEvent(reachabilityType);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/bulkloader.js b/mapbox-gl/closure-library/closure/goog/net/bulkloader.js
    new file mode 100644
    index 0000000000..a42a4e97fe
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/bulkloader.js
    @@ -0,0 +1,182 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Loads a list of URIs in bulk. All requests must be a success
    + * in order for the load to be considered a success.
    + *
    + */
    +
    +goog.provide('goog.net.BulkLoader');
    +
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.log');
    +goog.require('goog.net.BulkLoaderHelper');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +
    +
    +
    +/**
    + * Class used to load multiple URIs.
    + * @param {Array<string|goog.Uri>} uris The URIs to load.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.net.BulkLoader = function(uris) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * The bulk loader helper.
    +   * @type {goog.net.BulkLoaderHelper}
    +   * @private
    +   */
    +  this.helper_ = new goog.net.BulkLoaderHelper(uris);
    +
    +  /**
    +   * The handler for managing events.
    +   * @type {goog.events.EventHandler<!goog.net.BulkLoader>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +};
    +goog.inherits(goog.net.BulkLoader, goog.events.EventTarget);
    +
    +
    +/**
    + * A logger.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.BulkLoader.prototype.logger_ =
    +    goog.log.getLogger('goog.net.BulkLoader');
    +
    +
    +/**
    + * Gets the response texts, in order.
    + * @return {Array<string>} The response texts.
    + */
    +goog.net.BulkLoader.prototype.getResponseTexts = function() {
    +  return this.helper_.getResponseTexts();
    +};
    +
    +
    +/**
    + * Gets the request Uris.
    + * @return {Array<string>} The request URIs, in order.
    + */
    +goog.net.BulkLoader.prototype.getRequestUris = function() {
    +  return this.helper_.getUris();
    +};
    +
    +
    +/**
    + * Starts the process of loading the URIs.
    + */
    +goog.net.BulkLoader.prototype.load = function() {
    +  var eventHandler = this.eventHandler_;
    +  var uris = this.helper_.getUris();
    +  goog.log.info(this.logger_,
    +      'Starting load of code with ' + uris.length + ' uris.');
    +
    +  for (var i = 0; i < uris.length; i++) {
    +    var xhrIo = new goog.net.XhrIo();
    +    eventHandler.listen(xhrIo,
    +        goog.net.EventType.COMPLETE,
    +        goog.bind(this.handleEvent_, this, i));
    +
    +    xhrIo.send(uris[i]);
    +  }
    +};
    +
    +
    +/**
    + * Handles all events fired by the XhrManager.
    + * @param {number} id The id of the request.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.net.BulkLoader.prototype.handleEvent_ = function(id, e) {
    +  goog.log.info(this.logger_, 'Received event "' + e.type + '" for id ' + id +
    +      ' with uri ' + this.helper_.getUri(id));
    +  var xhrIo = /** @type {goog.net.XhrIo} */ (e.target);
    +  if (xhrIo.isSuccess()) {
    +    this.handleSuccess_(id, xhrIo);
    +  } else {
    +    this.handleError_(id, xhrIo);
    +  }
    +};
    +
    +
    +/**
    + * Handles when a request is successful (i.e., completed and response received).
    + * Stores thhe responseText and checks if loading is complete.
    + * @param {number} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo objects that was used.
    + * @private
    + */
    +goog.net.BulkLoader.prototype.handleSuccess_ = function(
    +    id, xhrIo) {
    +  // Save the response text.
    +  this.helper_.setResponseText(id, xhrIo.getResponseText());
    +
    +  // Check if all response texts have been received.
    +  if (this.helper_.isLoadComplete()) {
    +    this.finishLoad_();
    +  }
    +  xhrIo.dispose();
    +};
    +
    +
    +/**
    + * Handles when a request has ended in error (i.e., all retries completed and
    + * none were successful). Cancels loading of the URI's.
    + * @param {number|string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo objects that was used.
    + * @private
    + */
    +goog.net.BulkLoader.prototype.handleError_ = function(
    +    id, xhrIo) {
    +  // TODO(user): Abort all pending requests.
    +
    +  // Dispatch the ERROR event.
    +  this.dispatchEvent(goog.net.EventType.ERROR);
    +  xhrIo.dispose();
    +};
    +
    +
    +/**
    + * Finishes the load of the URI's. Dispatches the SUCCESS event.
    + * @private
    + */
    +goog.net.BulkLoader.prototype.finishLoad_ = function() {
    +  goog.log.info(this.logger_, 'All uris loaded.');
    +
    +  // Dispatch the SUCCESS event.
    +  this.dispatchEvent(goog.net.EventType.SUCCESS);
    +};
    +
    +
    +/** @override */
    +goog.net.BulkLoader.prototype.disposeInternal = function() {
    +  goog.net.BulkLoader.superClass_.disposeInternal.call(this);
    +
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +
    +  this.helper_.dispose();
    +  this.helper_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.html b/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.html
    new file mode 100644
    index 0000000000..0dc5de1e1b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.BulkLoader
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.net.BulkLoaderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.js b/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.js
    new file mode 100644
    index 0000000000..5426857537
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/bulkloader_test.js
    @@ -0,0 +1,235 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.BulkLoaderTest');
    +goog.setTestOnly('goog.net.BulkLoaderTest');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.net.BulkLoader');
    +goog.require('goog.net.EventType');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Test interval between sending uri requests to the server.
    + */
    +var DELAY_INTERVAL_BETWEEN_URI_REQUESTS = 5;
    +
    +
    +/**
    + * Test interval before a response is received for a URI request.
    + */
    +var DELAY_INTERVAL_FOR_URI_LOAD = 15;
    +
    +var clock;
    +var loadSuccess, loadError;
    +var successResponseTexts;
    +
    +function setUpPage() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDownPage() {
    +  clock.dispose();
    +}
    +
    +function setUp() {
    +  loadSuccess = false;
    +  loadError = false;
    +  successResponseTexts = [];
    +}
    +
    +
    +/**
    + * Gets the successful bulkloader for the specified uris with some
    + * modifications for testability.
    + * <ul>
    + *   <li> Added onSuccess methods to simulate success while loading uris.
    + *   <li> The send function of the XhrManager used by the bulkloader
    + *        calls the onSuccess function after a specified time interval.
    + * </ul>
    + * @param {Array<string>} uris The URIs.
    + */
    +function getSuccessfulBulkLoader(uris) {
    +  var bulkLoader = new goog.net.BulkLoader(uris);
    +  bulkLoader.load = function() {
    +    var uris = this.helper_.getUris();
    +    for (var i = 0; i < uris.length; i++) {
    +      // This clock tick simulates a delay for processing every URI.
    +      clock.tick(DELAY_INTERVAL_BETWEEN_URI_REQUESTS);
    +      // This timeout determines how many ticks after the send request
    +      // all the URIs will complete loading. This delays the load of
    +      // the first uri and every subsequent uri by 15 ticks.
    +      setTimeout(goog.bind(this.onSuccess, this, i, uris[i]),
    +          DELAY_INTERVAL_FOR_URI_LOAD);
    +    }
    +  };
    +
    +  bulkLoader.onSuccess = function(id, uri) {
    +    var xhrIo = {
    +      getResponseText: function() {return uri;},
    +      isSuccess: function() {return true;},
    +      dispose: function() {}
    +    };
    +    this.handleEvent_(id, new goog.events.Event(
    +        goog.net.EventType.COMPLETE, xhrIo));
    +  };
    +
    +  var eventHandler = new goog.events.EventHandler();
    +  eventHandler.listen(bulkLoader,
    +      goog.net.EventType.SUCCESS,
    +      handleSuccess);
    +  eventHandler.listen(bulkLoader,
    +      goog.net.EventType.ERROR,
    +      handleError);
    +
    +  return bulkLoader;
    +}
    +
    +
    +/**
    + * Gets the non-successful bulkloader for the specified uris with some
    + * modifications for testability.
    + * <ul>
    + *   <li> Added onSuccess and onError methods to simulate success and error
    + *        while loading uris.
    + *   <li> The send function of the XhrManager used by the bulkloader
    + *        calls the onSuccess or onError function after a specified time
    + *        interval.
    + * </ul>
    + * @param {Array<string>} uris The URIs.
    + */
    +function getNonSuccessfulBulkLoader(uris) {
    +  var bulkLoader = new goog.net.BulkLoader(uris);
    +  bulkLoader.load = function() {
    +    var uris = this.helper_.getUris();
    +    for (var i = 0; i < uris.length; i++) {
    +      // This clock tick simulates a delay for processing every URI.
    +      clock.tick(DELAY_INTERVAL_BETWEEN_URI_REQUESTS);
    +
    +      // This timeout determines how many ticks after the send request
    +      // all the URIs will complete loading in error. This delays the load
    +      // of the first uri and every subsequent uri by 15 ticks. The URI
    +      // with id == 2 is in error.
    +      if (i != 2) {
    +        setTimeout(goog.bind(this.onSuccess, this, i, uris[i]),
    +            DELAY_INTERVAL_FOR_URI_LOAD);
    +      } else {
    +        setTimeout(goog.bind(this.onError, this, i, uris[i]),
    +            DELAY_INTERVAL_FOR_URI_LOAD);
    +      }
    +    }
    +  };
    +
    +  bulkLoader.onSuccess = function(id, uri) {
    +    var xhrIo = {
    +      getResponseText: function() {return uri;},
    +      isSuccess: function() {return true;},
    +      dispose: function() {}
    +    };
    +    this.handleEvent_(id, new goog.events.Event(
    +        goog.net.EventType.COMPLETE, xhrIo));
    +  };
    +
    +  bulkLoader.onError = function(id) {
    +    var xhrIo = {
    +      getResponseText: function() {return null;},
    +      isSuccess: function() {return false;},
    +      dispose: function() {}
    +    };
    +    this.handleEvent_(id, new goog.events.Event(
    +        goog.net.EventType.ERROR, xhrIo));
    +  };
    +
    +  var eventHandler = new goog.events.EventHandler();
    +  eventHandler.listen(bulkLoader,
    +      goog.net.EventType.SUCCESS,
    +      handleSuccess);
    +  eventHandler.listen(bulkLoader,
    +      goog.net.EventType.ERROR,
    +      handleError);
    +
    +  return bulkLoader;
    +}
    +
    +function handleSuccess(e) {
    +  loadSuccess = true;
    +  successResponseTexts = e.target.getResponseTexts();
    +}
    +
    +function handleError(e) {
    +  loadError = true;
    +}
    +
    +
    +/**
    + * Test successful loading of URIs using the bulkloader.
    + */
    +function testBulkLoaderLoadSuccess() {
    +  var uris = ['a', 'b', 'c'];
    +  var bulkLoader = getSuccessfulBulkLoader(uris);
    +  assertArrayEquals(uris, bulkLoader.getRequestUris());
    +
    +  bulkLoader.load();
    +
    +  clock.tick(2);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 2 ticks)', loadSuccess);
    +
    +  clock.tick(3);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 5 ticks)', loadSuccess);
    +
    +  clock.tick(5);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 10 ticks)', loadSuccess);
    +
    +  clock.tick(5);
    +  assertTrue('The bulk loader is loaded (after 15 ticks)', loadSuccess);
    +
    +  assertArrayEquals('Ensure that the response texts are present',
    +      successResponseTexts, uris);
    +}
    +
    +
    +/**
    + * Test error loading URIs using the bulkloader.
    + */
    +function testBulkLoaderLoadError() {
    +  var uris = ['a', 'b', 'c'];
    +  var bulkLoader = getNonSuccessfulBulkLoader(uris);
    +
    +  bulkLoader.load();
    +
    +  clock.tick(2);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 2 ticks)', loadError);
    +
    +  clock.tick(3);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 5 ticks)', loadError);
    +
    +  clock.tick(5);
    +  assertFalse(
    +      'The bulk loader is not yet loaded (after 10 ticks)', loadError);
    +
    +  clock.tick(5);
    +  assertFalse(
    +      'The bulk loader is not loaded successfully (after 15 ticks)',
    +      loadSuccess);
    +  assertTrue(
    +      'The bulk loader is loaded in error (after 15 ticks)', loadError);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/bulkloaderhelper.js b/mapbox-gl/closure-library/closure/goog/net/bulkloaderhelper.js
    new file mode 100644
    index 0000000000..807d53cf51
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/bulkloaderhelper.js
    @@ -0,0 +1,128 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Helper class to load a list of URIs in bulk. All URIs
    + * must be a successfully loaded in order for the entire load to be considered
    + * a success.
    + *
    + */
    +
    +goog.provide('goog.net.BulkLoaderHelper');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Helper class used to load multiple URIs.
    + * @param {Array<string|goog.Uri>} uris The URIs to load.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.net.BulkLoaderHelper = function(uris) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The URIs to load.
    +   * @type {Array<string|goog.Uri>}
    +   * @private
    +   */
    +  this.uris_ = uris;
    +
    +  /**
    +   * The response from the XHR's.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.responseTexts_ = [];
    +};
    +goog.inherits(goog.net.BulkLoaderHelper, goog.Disposable);
    +
    +
    +/**
    + * A logger.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.BulkLoaderHelper.prototype.logger_ =
    +    goog.log.getLogger('goog.net.BulkLoaderHelper');
    +
    +
    +/**
    + * Gets the URI by id.
    + * @param {number} id The id.
    + * @return {string|goog.Uri} The URI specified by the id.
    + */
    +goog.net.BulkLoaderHelper.prototype.getUri = function(id) {
    +  return this.uris_[id];
    +};
    +
    +
    +/**
    + * Gets the URIs.
    + * @return {Array<string|goog.Uri>} The URIs.
    + */
    +goog.net.BulkLoaderHelper.prototype.getUris = function() {
    +  return this.uris_;
    +};
    +
    +
    +/**
    + * Gets the response texts.
    + * @return {Array<string>} The response texts.
    + */
    +goog.net.BulkLoaderHelper.prototype.getResponseTexts = function() {
    +  return this.responseTexts_;
    +};
    +
    +
    +/**
    + * Sets the response text by id.
    + * @param {number} id The id.
    + * @param {string} responseText The response texts.
    + */
    +goog.net.BulkLoaderHelper.prototype.setResponseText = function(
    +    id, responseText) {
    +  this.responseTexts_[id] = responseText;
    +};
    +
    +
    +/**
    + * Determines if the load of the URIs is complete.
    + * @return {boolean} TRUE iff the load is complete.
    + */
    +goog.net.BulkLoaderHelper.prototype.isLoadComplete = function() {
    +  var responseTexts = this.responseTexts_;
    +  if (responseTexts.length == this.uris_.length) {
    +    for (var i = 0; i < responseTexts.length; i++) {
    +      if (!goog.isDefAndNotNull(responseTexts[i])) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.net.BulkLoaderHelper.prototype.disposeInternal = function() {
    +  goog.net.BulkLoaderHelper.superClass_.disposeInternal.call(this);
    +
    +  this.uris_ = null;
    +  this.responseTexts_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/channeldebug.js b/mapbox-gl/closure-library/closure/goog/net/channeldebug.js
    new file mode 100644
    index 0000000000..9ea22390c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/channeldebug.js
    @@ -0,0 +1,300 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the ChannelDebug class. ChannelDebug provides
    + * a utility for tracing and debugging the BrowserChannel requests.
    + *
    + */
    +
    +
    +/**
    + * Namespace for BrowserChannel
    + */
    +goog.provide('goog.net.ChannelDebug');
    +
    +goog.require('goog.json');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Logs and keeps a buffer of debugging info for the Channel.
    + *
    + * @constructor
    + */
    +goog.net.ChannelDebug = function() {
    +  /**
    +   * The logger instance.
    +   * @const
    +   * @private
    +   */
    +  this.logger_ = goog.log.getLogger('goog.net.BrowserChannel');
    +};
    +
    +
    +/**
    + * Gets the logger used by this ChannelDebug.
    + * @return {goog.debug.Logger} The logger used by this ChannelDebug.
    + */
    +goog.net.ChannelDebug.prototype.getLogger = function() {
    +  return this.logger_;
    +};
    +
    +
    +/**
    + * Logs that the browser went offline during the lifetime of a request.
    + * @param {goog.Uri} url The URL being requested.
    + */
    +goog.net.ChannelDebug.prototype.browserOfflineResponse = function(url) {
    +  this.info('BROWSER_OFFLINE: ' + url);
    +};
    +
    +
    +/**
    + * Logs an XmlHttp request..
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + * @param {?string} postData The data posted in the request.
    + */
    +goog.net.ChannelDebug.prototype.xmlHttpChannelRequest =
    +    function(verb, uri, id, attempt, postData) {
    +  this.info(
    +      'XMLHTTP REQ (' + id + ') [attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri + '\n' +
    +      this.maybeRedactPostData_(postData));
    +};
    +
    +
    +/**
    + * Logs the meta data received from an XmlHttp request.
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + * @param {goog.net.XmlHttp.ReadyState} readyState The ready state.
    + * @param {number} statusCode The HTTP status code.
    + */
    +goog.net.ChannelDebug.prototype.xmlHttpChannelResponseMetaData =
    +    function(verb, uri, id, attempt, readyState, statusCode)  {
    +  this.info(
    +      'XMLHTTP RESP (' + id + ') [ attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri + '\n' + readyState + ' ' + statusCode);
    +};
    +
    +
    +/**
    + * Logs the response data received from an XmlHttp request.
    + * @param {string|number|undefined} id The request id.
    + * @param {?string} responseText The response text.
    + * @param {?string=} opt_desc Optional request description.
    + */
    +goog.net.ChannelDebug.prototype.xmlHttpChannelResponseText =
    +    function(id, responseText, opt_desc) {
    +  this.info(
    +      'XMLHTTP TEXT (' + id + '): ' +
    +      this.redactResponse_(responseText) +
    +      (opt_desc ? ' ' + opt_desc : ''));
    +};
    +
    +
    +/**
    + * Logs a Trident ActiveX request.
    + * @param {string} verb The request type (GET/POST).
    + * @param {goog.Uri} uri The request destination.
    + * @param {string|number|undefined} id The request id.
    + * @param {number} attempt Which attempt # the request was.
    + */
    +goog.net.ChannelDebug.prototype.tridentChannelRequest =
    +    function(verb, uri, id, attempt) {
    +  this.info(
    +      'TRIDENT REQ (' + id + ') [ attempt ' + attempt + ']: ' +
    +      verb + '\n' + uri);
    +};
    +
    +
    +/**
    + * Logs the response text received from a Trident ActiveX request.
    + * @param {string|number|undefined} id The request id.
    + * @param {string} responseText The response text.
    + */
    +goog.net.ChannelDebug.prototype.tridentChannelResponseText =
    +    function(id, responseText) {
    +  this.info(
    +      'TRIDENT TEXT (' + id + '): ' +
    +      this.redactResponse_(responseText));
    +};
    +
    +
    +/**
    + * Logs the done response received from a Trident ActiveX request.
    + * @param {string|number|undefined} id The request id.
    + * @param {boolean} successful Whether the request was successful.
    + */
    +goog.net.ChannelDebug.prototype.tridentChannelResponseDone =
    +    function(id, successful) {
    +  this.info(
    +      'TRIDENT TEXT (' + id + '): ' + successful ? 'success' : 'failure');
    +};
    +
    +
    +/**
    + * Logs a request timeout.
    + * @param {goog.Uri} uri The uri that timed out.
    + */
    +goog.net.ChannelDebug.prototype.timeoutResponse = function(uri) {
    +  this.info('TIMEOUT: ' + uri);
    +};
    +
    +
    +/**
    + * Logs a debug message.
    + * @param {string} text The message.
    + */
    +goog.net.ChannelDebug.prototype.debug = function(text) {
    +  this.info(text);
    +};
    +
    +
    +/**
    + * Logs an exception
    + * @param {Error} e The error or error event.
    + * @param {string=} opt_msg The optional message, defaults to 'Exception'.
    + */
    +goog.net.ChannelDebug.prototype.dumpException = function(e, opt_msg) {
    +  this.severe((opt_msg || 'Exception') + e);
    +};
    +
    +
    +/**
    + * Logs an info message.
    + * @param {string} text The message.
    + */
    +goog.net.ChannelDebug.prototype.info = function(text) {
    +  goog.log.info(this.logger_, text);
    +};
    +
    +
    +/**
    + * Logs a warning message.
    + * @param {string} text The message.
    + */
    +goog.net.ChannelDebug.prototype.warning = function(text) {
    +  goog.log.warning(this.logger_, text);
    +};
    +
    +
    +/**
    + * Logs a severe message.
    + * @param {string} text The message.
    + */
    +goog.net.ChannelDebug.prototype.severe = function(text) {
    +  goog.log.error(this.logger_, text);
    +};
    +
    +
    +/**
    + * Removes potentially private data from a response so that we don't
    + * accidentally save private and personal data to the server logs.
    + * @param {?string} responseText A JSON response to clean.
    + * @return {?string} The cleaned response.
    + * @private
    + */
    +goog.net.ChannelDebug.prototype.redactResponse_ = function(responseText) {
    +  // first check if it's not JS - the only non-JS should be the magic cookie
    +  if (!responseText ||
    +      /** @suppress {missingRequire}.  The require creates a circular
    +       *  dependency.
    +       */
    +      responseText == goog.net.BrowserChannel.MAGIC_RESPONSE_COOKIE) {
    +    return responseText;
    +  }
    +  /** @preserveTry */
    +  try {
    +    var responseArray = goog.json.unsafeParse(responseText);
    +    if (responseArray) {
    +      for (var i = 0; i < responseArray.length; i++) {
    +        if (goog.isArray(responseArray[i])) {
    +          this.maybeRedactArray_(responseArray[i]);
    +        }
    +      }
    +    }
    +
    +    return goog.json.serialize(responseArray);
    +  } catch (e) {
    +    this.debug('Exception parsing expected JS array - probably was not JS');
    +    return responseText;
    +  }
    +};
    +
    +
    +/**
    + * Removes data from a response array that may be sensitive.
    + * @param {Array<?>} array The array to clean.
    + * @private
    + */
    +goog.net.ChannelDebug.prototype.maybeRedactArray_ = function(array) {
    +  if (array.length < 2) {
    +    return;
    +  }
    +  var dataPart = array[1];
    +  if (!goog.isArray(dataPart)) {
    +    return;
    +  }
    +  if (dataPart.length < 1) {
    +    return;
    +  }
    +
    +  var type = dataPart[0];
    +  if (type != 'noop' && type != 'stop') {
    +    // redact all fields in the array
    +    for (var i = 1; i < dataPart.length; i++) {
    +      dataPart[i] = '';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes potentially private data from a request POST body so that we don't
    + * accidentally save private and personal data to the server logs.
    + * @param {?string} data The data string to clean.
    + * @return {?string} The data string with sensitive data replaced by 'redacted'.
    + * @private
    + */
    +goog.net.ChannelDebug.prototype.maybeRedactPostData_ = function(data) {
    +  if (!data) {
    +    return null;
    +  }
    +  var out = '';
    +  var params = data.split('&');
    +  for (var i = 0; i < params.length; i++) {
    +    var param = params[i];
    +    var keyValue = param.split('=');
    +    if (keyValue.length > 1) {
    +      var key = keyValue[0];
    +      var value = keyValue[1];
    +
    +      var keyParts = key.split('_');
    +      if (keyParts.length >= 2 && keyParts[1] == 'type') {
    +        out += key + '=' + value + '&';
    +      } else {
    +        out += key + '=' + 'redacted' + '&';
    +      }
    +    }
    +  }
    +  return out;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/channelrequest.js b/mapbox-gl/closure-library/closure/goog/net/channelrequest.js
    new file mode 100644
    index 0000000000..453714d198
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/channelrequest.js
    @@ -0,0 +1,1312 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the ChannelRequest class. The ChannelRequest
    + * object encapsulates the logic for making a single request, either for the
    + * forward channel, back channel, or test channel, to the server. It contains
    + * the logic for the three types of transports we use in the BrowserChannel:
    + * XMLHTTP, Trident ActiveX (ie only), and Image request. It provides timeout
    + * detection. This class is part of the BrowserChannel implementation and is not
    + * for use by normal application code.
    + *
    + */
    +
    +
    +goog.provide('goog.net.ChannelRequest');
    +goog.provide('goog.net.ChannelRequest.Error');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.async.Throttle');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.userAgent');
    +
    +// TODO(nnaze): This file depends on goog.net.BrowserChannel and vice versa (a
    +// circular dependency).  Usages of BrowserChannel are marked as
    +// "missingRequire" below for now.  This should be fixed through refactoring.
    +
    +
    +
    +/**
    + * Creates a ChannelRequest object which encapsulates a request to the server.
    + * A new ChannelRequest is created for each request to the server.
    + *
    + * @param {goog.net.BrowserChannel|goog.net.BrowserTestChannel} channel
    + *     The BrowserChannel that owns this request.
    + * @param {goog.net.ChannelDebug} channelDebug A ChannelDebug to use for
    + *     logging.
    + * @param {string=} opt_sessionId  The session id for the channel.
    + * @param {string|number=} opt_requestId  The request id for this request.
    + * @param {number=} opt_retryId  The retry id for this request.
    + * @constructor
    + */
    +goog.net.ChannelRequest = function(channel, channelDebug, opt_sessionId,
    +    opt_requestId, opt_retryId) {
    +  /**
    +   * The BrowserChannel object that owns the request.
    +   * @type {goog.net.BrowserChannel|goog.net.BrowserTestChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The channel debug to use for logging
    +   * @type {goog.net.ChannelDebug}
    +   * @private
    +   */
    +  this.channelDebug_ = channelDebug;
    +
    +  /**
    +   * The Session ID for the channel.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.sid_ = opt_sessionId;
    +
    +  /**
    +   * The RID (request ID) for the request.
    +   * @type {string|number|undefined}
    +   * @private
    +   */
    +  this.rid_ = opt_requestId;
    +
    +
    +  /**
    +   * The attempt number of the current request.
    +   * @type {number}
    +   * @private
    +   */
    +  this.retryId_ = opt_retryId || 1;
    +
    +
    +  /**
    +   * The timeout in ms before failing the request.
    +   * @type {number}
    +   * @private
    +   */
    +  this.timeout_ = goog.net.ChannelRequest.TIMEOUT_MS;
    +
    +  /**
    +   * An object to keep track of the channel request event listeners.
    +   * @type {!goog.events.EventHandler<!goog.net.ChannelRequest>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * A timer for polling responseText in browsers that don't fire
    +   * onreadystatechange during incremental loading of responseText.
    +   * @type {goog.Timer}
    +   * @private
    +   */
    +  this.pollingTimer_ = new goog.Timer();
    +
    +  this.pollingTimer_.setInterval(goog.net.ChannelRequest.POLLING_INTERVAL_MS);
    +};
    +
    +
    +/**
    + * Extra HTTP headers to add to all the requests sent to the server.
    + * @type {Object}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.extraHeaders_ = null;
    +
    +
    +/**
    + * Whether the request was successful. This is only set to true after the
    + * request successfuly completes.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.successful_ = false;
    +
    +
    +/**
    + * The TimerID of the timer used to detect if the request has timed-out.
    + * @type {?number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.watchDogTimerId_ = null;
    +
    +
    +/**
    + * The time in the future when the request will timeout.
    + * @type {?number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.watchDogTimeoutTime_ = null;
    +
    +
    +/**
    + * The time the request started.
    + * @type {?number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.requestStartTime_ = null;
    +
    +
    +/**
    + * The type of request (XMLHTTP, IMG, Trident)
    + * @type {?number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.type_ = null;
    +
    +
    +/**
    + * The base Uri for the request. The includes all the parameters except the
    + * one that indicates the retry number.
    + * @type {goog.Uri?}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.baseUri_ = null;
    +
    +
    +/**
    + * The request Uri that was actually used for the most recent request attempt.
    + * @type {goog.Uri?}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.requestUri_ = null;
    +
    +
    +/**
    + * The post data, if the request is a post.
    + * @type {?string}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.postData_ = null;
    +
    +
    +/**
    + * The XhrLte request if the request is using XMLHTTP
    + * @type {goog.net.XhrIo}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.xmlHttp_ = null;
    +
    +
    +/**
    + * The position of where the next unprocessed chunk starts in the response
    + * text.
    + * @type {number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.xmlHttpChunkStart_ = 0;
    +
    +
    +/**
    + * The Trident instance if the request is using Trident.
    + * @type {ActiveXObject}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.trident_ = null;
    +
    +
    +/**
    + * The verb (Get or Post) for the request.
    + * @type {?string}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.verb_ = null;
    +
    +
    +/**
    + * The last error if the request failed.
    + * @type {?goog.net.ChannelRequest.Error}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.lastError_ = null;
    +
    +
    +/**
    + * The last status code received.
    + * @type {number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.lastStatusCode_ = -1;
    +
    +
    +/**
    + * Whether to send the Connection:close header as part of the request.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.sendClose_ = true;
    +
    +
    +/**
    + * Whether the request has been cancelled due to a call to cancel.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.cancelled_ = false;
    +
    +
    +/**
    + * A throttle time in ms for readystatechange events for the backchannel.
    + * Useful for throttling when ready state is INTERACTIVE (partial data).
    + * If set to zero no throttle is used.
    + *
    + * @see goog.net.BrowserChannel.prototype.readyStateChangeThrottleMs_
    + *
    + * @type {number}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.readyStateChangeThrottleMs_ = 0;
    +
    +
    +/**
    + * The throttle for readystatechange events for the current request, or null
    + * if there is none.
    + * @type {goog.async.Throttle}
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.readyStateChangeThrottle_ = null;
    +
    +
    +/**
    + * Default timeout in MS for a request. The server must return data within this
    + * time limit for the request to not timeout.
    + * @type {number}
    + */
    +goog.net.ChannelRequest.TIMEOUT_MS = 45 * 1000;
    +
    +
    +/**
    + * How often to poll (in MS) for changes to responseText in browsers that don't
    + * fire onreadystatechange during incremental loading of responseText.
    + * @type {number}
    + */
    +goog.net.ChannelRequest.POLLING_INTERVAL_MS = 250;
    +
    +
    +/**
    + * Minimum version of Safari that receives a non-null responseText in ready
    + * state interactive.
    + * @type {string}
    + * @private
    + */
    +goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_ = '420+';
    +
    +
    +/**
    + * Enum for channel requests type
    + * @enum {number}
    + * @private
    + */
    +goog.net.ChannelRequest.Type_ = {
    +  /**
    +   * XMLHTTP requests.
    +   */
    +  XML_HTTP: 1,
    +
    +  /**
    +   * IMG requests.
    +   */
    +  IMG: 2,
    +
    +  /**
    +   * Requests that use the MSHTML ActiveX control.
    +   */
    +  TRIDENT: 3
    +};
    +
    +
    +/**
    + * Enum type for identifying a ChannelRequest error.
    + * @enum {number}
    + */
    +goog.net.ChannelRequest.Error = {
    +  /**
    +   * Errors due to a non-200 status code.
    +   */
    +  STATUS: 0,
    +
    +  /**
    +   * Errors due to no data being returned.
    +   */
    +  NO_DATA: 1,
    +
    +  /**
    +   * Errors due to a timeout.
    +   */
    +  TIMEOUT: 2,
    +
    +  /**
    +   * Errors due to the server returning an unknown.
    +   */
    +  UNKNOWN_SESSION_ID: 3,
    +
    +  /**
    +   * Errors due to bad data being received.
    +   */
    +  BAD_DATA: 4,
    +
    +  /**
    +   * Errors due to the handler throwing an exception.
    +   */
    +  HANDLER_EXCEPTION: 5,
    +
    +  /**
    +   * The browser declared itself offline during the request.
    +   */
    +  BROWSER_OFFLINE: 6,
    +
    +  /**
    +   * IE is blocking ActiveX streaming.
    +   */
    +  ACTIVE_X_BLOCKED: 7
    +};
    +
    +
    +/**
    + * Returns a useful error string for debugging based on the specified error
    + * code.
    + * @param {goog.net.ChannelRequest.Error} errorCode The error code.
    + * @param {number} statusCode The HTTP status code.
    + * @return {string} The error string for the given code combination.
    + */
    +goog.net.ChannelRequest.errorStringFromCode = function(errorCode, statusCode) {
    +  switch (errorCode) {
    +    case goog.net.ChannelRequest.Error.STATUS:
    +      return 'Non-200 return code (' + statusCode + ')';
    +    case goog.net.ChannelRequest.Error.NO_DATA:
    +      return 'XMLHTTP failure (no data)';
    +    case goog.net.ChannelRequest.Error.TIMEOUT:
    +      return 'HttpConnection timeout';
    +    default:
    +      return 'Unknown error';
    +  }
    +};
    +
    +
    +/**
    + * Sentinel value used to indicate an invalid chunk in a multi-chunk response.
    + * @type {Object}
    + * @private
    + */
    +goog.net.ChannelRequest.INVALID_CHUNK_ = {};
    +
    +
    +/**
    + * Sentinel value used to indicate an incomplete chunk in a multi-chunk
    + * response.
    + * @type {Object}
    + * @private
    + */
    +goog.net.ChannelRequest.INCOMPLETE_CHUNK_ = {};
    +
    +
    +/**
    + * Returns whether XHR streaming is supported on this browser.
    + *
    + * If XHR streaming is not supported, we will try to use an ActiveXObject
    + * to create a Forever IFrame.
    + *
    + * @return {boolean} Whether XHR streaming is supported.
    + * @see http://code.google.com/p/closure-library/issues/detail?id=346
    + */
    +goog.net.ChannelRequest.supportsXhrStreaming = function() {
    +  return !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(10);
    +};
    +
    +
    +/**
    + * Sets extra HTTP headers to add to all the requests sent to the server.
    + *
    + * @param {Object} extraHeaders The HTTP headers.
    + */
    +goog.net.ChannelRequest.prototype.setExtraHeaders = function(extraHeaders) {
    +  this.extraHeaders_ = extraHeaders;
    +};
    +
    +
    +/**
    + * Sets the timeout for a request
    + *
    + * @param {number} timeout   The timeout in MS for when we fail the request.
    + */
    +goog.net.ChannelRequest.prototype.setTimeout = function(timeout) {
    +  this.timeout_ = timeout;
    +};
    +
    +
    +/**
    + * Sets the throttle for handling onreadystatechange events for the request.
    + *
    + * @param {number} throttle The throttle in ms.  A value of zero indicates
    + *     no throttle.
    + */
    +goog.net.ChannelRequest.prototype.setReadyStateChangeThrottle = function(
    +    throttle) {
    +  this.readyStateChangeThrottleMs_ = throttle;
    +};
    +
    +
    +/**
    + * Uses XMLHTTP to send an HTTP POST to the server.
    + *
    + * @param {goog.Uri} uri  The uri of the request.
    + * @param {string} postData  The data for the post body.
    + * @param {boolean} decodeChunks  Whether to the result is expected to be
    + *     encoded for chunking and thus requires decoding.
    + */
    +goog.net.ChannelRequest.prototype.xmlHttpPost = function(uri, postData,
    +                                                         decodeChunks) {
    +  this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.postData_ = postData;
    +  this.decodeChunks_ = decodeChunks;
    +  this.sendXmlHttp_(null /* hostPrefix */);
    +};
    +
    +
    +/**
    + * Uses XMLHTTP to send an HTTP GET to the server.
    + *
    + * @param {goog.Uri} uri  The uri of the request.
    + * @param {boolean} decodeChunks  Whether to the result is expected to be
    + *     encoded for chunking and thus requires decoding.
    + * @param {?string} hostPrefix  The host prefix, if we might be using a
    + *     secondary domain.  Note that it should also be in the URL, adding this
    + *     won't cause it to be added to the URL.
    + * @param {boolean=} opt_noClose   Whether to request that the tcp/ip connection
    + *     should be closed.
    + */
    +goog.net.ChannelRequest.prototype.xmlHttpGet = function(uri, decodeChunks,
    +    hostPrefix, opt_noClose) {
    +  this.type_ = goog.net.ChannelRequest.Type_.XML_HTTP;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.postData_ = null;
    +  this.decodeChunks_ = decodeChunks;
    +  if (opt_noClose) {
    +    this.sendClose_ = false;
    +  }
    +  this.sendXmlHttp_(hostPrefix);
    +};
    +
    +
    +/**
    + * Sends a request via XMLHTTP according to the current state of the
    + * ChannelRequest object.
    + *
    + * @param {?string} hostPrefix The host prefix, if we might be using a secondary
    + *     domain.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.sendXmlHttp_ = function(hostPrefix) {
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +
    +  // clone the base URI to create the request URI. The request uri has the
    +  // attempt number as a parameter which helps in debugging.
    +  this.requestUri_ = this.baseUri_.clone();
    +  this.requestUri_.setParameterValues('t', this.retryId_);
    +
    +  // send the request either as a POST or GET
    +  this.xmlHttpChunkStart_ = 0;
    +  var useSecondaryDomains = this.channel_.shouldUseSecondaryDomains();
    +  this.xmlHttp_ = this.channel_.createXhrIo(useSecondaryDomains ?
    +      hostPrefix : null);
    +
    +  if (this.readyStateChangeThrottleMs_ > 0) {
    +    this.readyStateChangeThrottle_ = new goog.async.Throttle(
    +        goog.bind(this.xmlHttpHandler_, this, this.xmlHttp_),
    +        this.readyStateChangeThrottleMs_);
    +  }
    +
    +  this.eventHandler_.listen(this.xmlHttp_,
    +      goog.net.EventType.READY_STATE_CHANGE,
    +      this.readyStateChangeHandler_);
    +
    +  var headers = this.extraHeaders_ ? goog.object.clone(this.extraHeaders_) : {};
    +  if (this.postData_) {
    +    // todo (jonp) - use POST constant when Dan defines it
    +    this.verb_ = 'POST';
    +    headers['Content-Type'] = 'application/x-www-form-urlencoded';
    +    this.xmlHttp_.send(this.requestUri_, this.verb_, this.postData_, headers);
    +  } else {
    +    // todo (jonp) - use GET constant when Dan defines it
    +    this.verb_ = 'GET';
    +
    +    // If the user agent is webkit, we cannot send the close header since it is
    +    // disallowed by the browser.  If we attempt to set the "Connection: close"
    +    // header in WEBKIT browser, it will actually causes an error message.
    +    if (this.sendClose_ && !goog.userAgent.WEBKIT) {
    +      headers['Connection'] = 'close';
    +    }
    +    this.xmlHttp_.send(this.requestUri_, this.verb_, null, headers);
    +  }
    +  this.channel_.notifyServerReachabilityEvent(
    +      /** @suppress {missingRequire} */ (
    +      goog.net.BrowserChannel.ServerReachability.REQUEST_MADE));
    +  this.channelDebug_.xmlHttpChannelRequest(this.verb_,
    +      this.requestUri_, this.rid_, this.retryId_,
    +      this.postData_);
    +};
    +
    +
    +/**
    + * Handles a readystatechange event.
    + * @param {goog.events.Event} evt The event.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.readyStateChangeHandler_ = function(evt) {
    +  var xhr = /** @type {goog.net.XhrIo} */ (evt.target);
    +  var throttle = this.readyStateChangeThrottle_;
    +  if (throttle &&
    +      xhr.getReadyState() == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
    +    // Only throttle in the partial data case.
    +    this.channelDebug_.debug('Throttling readystatechange.');
    +    throttle.fire();
    +  } else {
    +    // If we haven't throttled, just handle response directly.
    +    this.xmlHttpHandler_(xhr);
    +  }
    +};
    +
    +
    +/**
    + * XmlHttp handler
    + * @param {goog.net.XhrIo} xmlhttp The XhrIo object for the current request.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.xmlHttpHandler_ = function(xmlhttp) {
    +  /** @suppress {missingRequire} */
    +  goog.net.BrowserChannel.onStartExecution();
    +
    +  /** @preserveTry */
    +  try {
    +    if (xmlhttp == this.xmlHttp_) {
    +      this.onXmlHttpReadyStateChanged_();
    +    } else {
    +      this.channelDebug_.warning('Called back with an ' +
    +                                     'unexpected xmlhttp');
    +    }
    +  } catch (ex) {
    +    this.channelDebug_.debug('Failed call to OnXmlHttpReadyStateChanged_');
    +    if (this.xmlHttp_ && this.xmlHttp_.getResponseText()) {
    +      this.channelDebug_.dumpException(ex,
    +          'ResponseText: ' + this.xmlHttp_.getResponseText());
    +    } else {
    +      this.channelDebug_.dumpException(ex, 'No response text');
    +    }
    +  } finally {
    +    /** @suppress {missingRequire} */
    +    goog.net.BrowserChannel.onEndExecution();
    +  }
    +};
    +
    +
    +/**
    + * Called by the readystate handler for XMLHTTP requests.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onXmlHttpReadyStateChanged_ = function() {
    +  var readyState = this.xmlHttp_.getReadyState();
    +  var errorCode = this.xmlHttp_.getLastErrorCode();
    +  var statusCode = this.xmlHttp_.getStatus();
    +  // If it is Safari less than 420+, there is a bug that causes null to be
    +  // in the responseText on ready state interactive so we must wait for
    +  // ready state complete.
    +  if (!goog.net.ChannelRequest.supportsXhrStreaming() ||
    +      (goog.userAgent.WEBKIT &&
    +       !goog.userAgent.isVersionOrHigher(
    +           goog.net.ChannelRequest.MIN_WEBKIT_FOR_INTERACTIVE_))) {
    +    if (readyState < goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      // not yet ready
    +      return;
    +    }
    +  } else {
    +    // we get partial results in browsers that support ready state interactive.
    +    // We also make sure that getResponseText is not null in interactive mode
    +    // before we continue.  However, we don't do it in Opera because it only
    +    // fire readyState == INTERACTIVE once.  We need the following code to poll
    +    if (readyState < goog.net.XmlHttp.ReadyState.INTERACTIVE ||
    +        readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
    +        !goog.userAgent.OPERA && !this.xmlHttp_.getResponseText()) {
    +      // not yet ready
    +      return;
    +    }
    +  }
    +
    +  // Dispatch any appropriate network events.
    +  if (!this.cancelled_ && readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
    +      errorCode != goog.net.ErrorCode.ABORT) {
    +
    +    // Pretty conservative, these are the only known scenarios which we'd
    +    // consider indicative of a truly non-functional network connection.
    +    if (errorCode == goog.net.ErrorCode.TIMEOUT ||
    +        statusCode <= 0) {
    +      this.channel_.notifyServerReachabilityEvent(
    +          /** @suppress {missingRequire} */
    +          goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED);
    +    } else {
    +      this.channel_.notifyServerReachabilityEvent(
    +          /** @suppress {missingRequire} */
    +          goog.net.BrowserChannel.ServerReachability.REQUEST_SUCCEEDED);
    +    }
    +  }
    +
    +  // got some data so cancel the watchdog timer
    +  this.cancelWatchDogTimer_();
    +
    +  var status = this.xmlHttp_.getStatus();
    +  this.lastStatusCode_ = status;
    +  var responseText = this.xmlHttp_.getResponseText();
    +  if (!responseText) {
    +    this.channelDebug_.debug('No response text for uri ' +
    +        this.requestUri_ + ' status ' + status);
    +  }
    +  this.successful_ = (status == 200);
    +
    +  this.channelDebug_.xmlHttpChannelResponseMetaData(
    +      /** @type {string} */ (this.verb_),
    +      this.requestUri_, this.rid_, this.retryId_, readyState,
    +      status);
    +
    +  if (!this.successful_) {
    +    if (status == 400 &&
    +        responseText.indexOf('Unknown SID') > 0) {
    +      // the server error string will include 'Unknown SID' which indicates the
    +      // server doesn't know about the session (maybe it got restarted, maybe
    +      // the user got moved to another server, etc.,). Handlers can special
    +      // case this error
    +      this.lastError_ = goog.net.ChannelRequest.Error.UNKNOWN_SESSION_ID;
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          /** @suppress {missingRequire} */
    +          goog.net.BrowserChannel.Stat.REQUEST_UNKNOWN_SESSION_ID);
    +      this.channelDebug_.warning('XMLHTTP Unknown SID (' + this.rid_ + ')');
    +    } else {
    +      this.lastError_ = goog.net.ChannelRequest.Error.STATUS;
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          /** @suppress {missingRequire} */
    +          goog.net.BrowserChannel.Stat.REQUEST_BAD_STATUS);
    +      this.channelDebug_.warning(
    +          'XMLHTTP Bad status ' + status + ' (' + this.rid_ + ')');
    +    }
    +    this.cleanup_();
    +    this.dispatchFailure_();
    +    return;
    +  }
    +
    +  if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +    this.cleanup_();
    +  }
    +
    +  if (this.decodeChunks_) {
    +    this.decodeNextChunks_(readyState, responseText);
    +    if (goog.userAgent.OPERA && this.successful_ &&
    +        readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE) {
    +      this.startPolling_();
    +    }
    +  } else {
    +    this.channelDebug_.xmlHttpChannelResponseText(
    +        this.rid_, responseText, null);
    +    this.safeOnRequestData_(responseText);
    +  }
    +
    +  if (!this.successful_) {
    +    return;
    +  }
    +
    +  if (!this.cancelled_) {
    +    if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      this.channel_.onRequestComplete(this);
    +    } else {
    +      // The default is false, the result from this callback shouldn't carry
    +      // over to the next callback, otherwise the request looks successful if
    +      // the watchdog timer gets called
    +      this.successful_ = false;
    +      this.ensureWatchDogTimer_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Decodes the next set of available chunks in the response.
    + * @param {number} readyState The value of readyState.
    + * @param {string} responseText The value of responseText.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.decodeNextChunks_ = function(readyState,
    +        responseText) {
    +  var decodeNextChunksSuccessful = true;
    +  while (!this.cancelled_ &&
    +         this.xmlHttpChunkStart_ < responseText.length) {
    +    var chunkText = this.getNextChunk_(responseText);
    +    if (chunkText == goog.net.ChannelRequest.INCOMPLETE_CHUNK_) {
    +      if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +        // should have consumed entire response when the request is done
    +        this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA;
    +        /** @suppress {missingRequire} */
    +        goog.net.BrowserChannel.notifyStatEvent(
    +            /** @suppress {missingRequire} */
    +            goog.net.BrowserChannel.Stat.REQUEST_INCOMPLETE_DATA);
    +        decodeNextChunksSuccessful = false;
    +      }
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, null, '[Incomplete Response]');
    +      break;
    +    } else if (chunkText == goog.net.ChannelRequest.INVALID_CHUNK_) {
    +      this.lastError_ = goog.net.ChannelRequest.Error.BAD_DATA;
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.notifyStatEvent(
    +          /** @suppress {missingRequire} */
    +          goog.net.BrowserChannel.Stat.REQUEST_BAD_DATA);
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, responseText, '[Invalid Chunk]');
    +      decodeNextChunksSuccessful = false;
    +      break;
    +    } else {
    +      this.channelDebug_.xmlHttpChannelResponseText(
    +          this.rid_, /** @type {string} */ (chunkText), null);
    +      this.safeOnRequestData_(/** @type {string} */ (chunkText));
    +    }
    +  }
    +  if (readyState == goog.net.XmlHttp.ReadyState.COMPLETE &&
    +      responseText.length == 0) {
    +    // also an error if we didn't get any response
    +    this.lastError_ = goog.net.ChannelRequest.Error.NO_DATA;
    +    /** @suppress {missingRequire} */
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        /** @suppress {missingRequire} */
    +        goog.net.BrowserChannel.Stat.REQUEST_NO_DATA);
    +    decodeNextChunksSuccessful = false;
    +  }
    +  this.successful_ = this.successful_ && decodeNextChunksSuccessful;
    +  if (!decodeNextChunksSuccessful) {
    +    // malformed response - we make this trigger retry logic
    +    this.channelDebug_.xmlHttpChannelResponseText(
    +        this.rid_, responseText, '[Invalid Chunked Response]');
    +    this.cleanup_();
    +    this.dispatchFailure_();
    +  }
    +};
    +
    +
    +/**
    + * Polls the response for new data.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.pollResponse_ = function() {
    +  var readyState = this.xmlHttp_.getReadyState();
    +  var responseText = this.xmlHttp_.getResponseText();
    +  if (this.xmlHttpChunkStart_ < responseText.length) {
    +    this.cancelWatchDogTimer_();
    +    this.decodeNextChunks_(readyState, responseText);
    +    if (this.successful_ &&
    +        readyState != goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      this.ensureWatchDogTimer_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Starts a polling interval for changes to responseText of the
    + * XMLHttpRequest, for browsers that don't fire onreadystatechange
    + * as data comes in incrementally.  This timer is disabled in
    + * cleanup_().
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.startPolling_ = function() {
    +  this.eventHandler_.listen(this.pollingTimer_, goog.Timer.TICK,
    +      this.pollResponse_);
    +  this.pollingTimer_.start();
    +};
    +
    +
    +/**
    + * Called when the browser declares itself offline at the start of a request or
    + * during its lifetime.  Abandons that request.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.cancelRequestAsBrowserIsOffline_ =
    +    function() {
    +  if (this.successful_) {
    +    // Should never happen.
    +    this.channelDebug_.severe(
    +        'Received browser offline event even though request completed ' +
    +        'successfully');
    +  }
    +
    +  this.channelDebug_.browserOfflineResponse(this.requestUri_);
    +  this.cleanup_();
    +
    +  // set error and dispatch failure
    +  this.lastError_ = goog.net.ChannelRequest.Error.BROWSER_OFFLINE;
    +  /** @suppress {missingRequire} */
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.Stat.BROWSER_OFFLINE);
    +  this.dispatchFailure_();
    +};
    +
    +
    +/**
    + * Returns the next chunk of a chunk-encoded response. This is not standard
    + * HTTP chunked encoding because browsers don't expose the chunk boundaries to
    + * the application through XMLHTTP. So we have an additional chunk encoding at
    + * the application level that lets us tell where the beginning and end of
    + * individual responses are so that we can only try to eval a complete JS array.
    + *
    + * The encoding is the size of the chunk encoded as a decimal string followed
    + * by a newline followed by the data.
    + *
    + * @param {string} responseText The response text from the XMLHTTP response.
    + * @return {string|Object} The next chunk string or a sentinel object
    + *                         indicating a special condition.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.getNextChunk_ = function(responseText) {
    +  var sizeStartIndex = this.xmlHttpChunkStart_;
    +  var sizeEndIndex = responseText.indexOf('\n', sizeStartIndex);
    +  if (sizeEndIndex == -1) {
    +    return goog.net.ChannelRequest.INCOMPLETE_CHUNK_;
    +  }
    +
    +  var sizeAsString = responseText.substring(sizeStartIndex, sizeEndIndex);
    +  var size = Number(sizeAsString);
    +  if (isNaN(size)) {
    +    return goog.net.ChannelRequest.INVALID_CHUNK_;
    +  }
    +
    +  var chunkStartIndex = sizeEndIndex + 1;
    +  if (chunkStartIndex + size > responseText.length) {
    +    return goog.net.ChannelRequest.INCOMPLETE_CHUNK_;
    +  }
    +
    +  var chunkText = responseText.substr(chunkStartIndex, size);
    +  this.xmlHttpChunkStart_ = chunkStartIndex + size;
    +  return chunkText;
    +};
    +
    +
    +/**
    + * Uses the Trident htmlfile ActiveX control to send a GET request in IE. This
    + * is the innovation discovered that lets us get intermediate results in
    + * Internet Explorer.  Thanks to http://go/kev
    + * @param {goog.Uri} uri The uri to request from.
    + * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
    + */
    +goog.net.ChannelRequest.prototype.tridentGet = function(uri,
    +    usingSecondaryDomain) {
    +  this.type_ = goog.net.ChannelRequest.Type_.TRIDENT;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.tridentGet_(usingSecondaryDomain);
    +};
    +
    +
    +/**
    + * Starts the Trident request.
    + * @param {boolean} usingSecondaryDomain Whether to use a secondary domain.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.tridentGet_ = function(usingSecondaryDomain) {
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +
    +  var hostname = usingSecondaryDomain ? window.location.hostname : '';
    +  this.requestUri_ = this.baseUri_.clone();
    +  this.requestUri_.setParameterValue('DOMAIN', hostname);
    +  this.requestUri_.setParameterValue('t', this.retryId_);
    +
    +  try {
    +    this.trident_ = new ActiveXObject('htmlfile');
    +  } catch (e) {
    +    this.channelDebug_.severe('ActiveX blocked');
    +    this.cleanup_();
    +
    +    this.lastError_ = goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED;
    +    /** @suppress {missingRequire} */
    +    goog.net.BrowserChannel.notifyStatEvent(
    +        /** @suppress {missingRequire} */
    +        goog.net.BrowserChannel.Stat.ACTIVE_X_BLOCKED);
    +    this.dispatchFailure_();
    +    return;
    +  }
    +
    +  var body = '<html><body>';
    +  if (usingSecondaryDomain) {
    +    body += '<script>document.domain="' + hostname + '"</scr' + 'ipt>';
    +  }
    +  body += '</body></html>';
    +
    +  this.trident_.open();
    +  this.trident_.write(body);
    +  this.trident_.close();
    +
    +  this.trident_.parentWindow['m'] = goog.bind(this.onTridentRpcMessage_, this);
    +  this.trident_.parentWindow['d'] = goog.bind(this.onTridentDone_, this, true);
    +  this.trident_.parentWindow['rpcClose'] =
    +      goog.bind(this.onTridentDone_, this, false);
    +
    +  var div = this.trident_.createElement('div');
    +  this.trident_.parentWindow.document.body.appendChild(div);
    +  div.innerHTML = '<iframe src="' + this.requestUri_ + '"></iframe>';
    +  this.channelDebug_.tridentChannelRequest('GET',
    +      this.requestUri_, this.rid_, this.retryId_);
    +  this.channel_.notifyServerReachabilityEvent(
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.ServerReachability.REQUEST_MADE);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when a new message
    + * is received.
    + *
    + * @param {string} msg The data payload.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onTridentRpcMessage_ = function(msg) {
    +  // need to do async b/c this gets called off of the context of the ActiveX
    +  /** @suppress {missingRequire} */
    +  goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onTridentRpcMessageAsync_, this, msg), 0);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when a new message
    + * is received.
    + *
    + * @param {string} msg  The data payload.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onTridentRpcMessageAsync_ = function(msg) {
    +  if (this.cancelled_) {
    +    return;
    +  }
    +  this.channelDebug_.tridentChannelResponseText(this.rid_, msg);
    +  this.cancelWatchDogTimer_();
    +  this.safeOnRequestData_(msg);
    +  this.ensureWatchDogTimer_();
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when the request
    + * is complete
    + *
    + * @param {boolean} successful Whether the request successfully completed.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onTridentDone_ = function(successful) {
    +  // need to do async b/c this gets called off of the context of the ActiveX
    +  /** @suppress {missingRequire} */
    +  goog.net.BrowserChannel.setTimeout(
    +      goog.bind(this.onTridentDoneAsync_, this, successful), 0);
    +};
    +
    +
    +/**
    + * Callback from the Trident htmlfile ActiveX control for when the request
    + * is complete
    + *
    + * @param {boolean} successful Whether the request successfully completed.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onTridentDoneAsync_ = function(successful) {
    +  if (this.cancelled_) {
    +    return;
    +  }
    +  this.channelDebug_.tridentChannelResponseDone(
    +      this.rid_, successful);
    +  this.cleanup_();
    +  this.successful_ = successful;
    +  this.channel_.onRequestComplete(this);
    +  this.channel_.notifyServerReachabilityEvent(
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY);
    +};
    +
    +
    +/**
    + * Uses an IMG tag to send an HTTP get to the server. This is only currently
    + * used to terminate the connection, as an IMG tag is the most reliable way to
    + * send something to the server while the page is getting torn down.
    + * @param {goog.Uri} uri The uri to send a request to.
    + */
    +goog.net.ChannelRequest.prototype.sendUsingImgTag = function(uri) {
    +  this.type_ = goog.net.ChannelRequest.Type_.IMG;
    +  this.baseUri_ = uri.clone().makeUnique();
    +  this.imgTagGet_();
    +};
    +
    +
    +/**
    + * Starts the IMG request.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.imgTagGet_ = function() {
    +  var eltImg = new Image();
    +  eltImg.src = this.baseUri_;
    +  this.requestStartTime_ = goog.now();
    +  this.ensureWatchDogTimer_();
    +};
    +
    +
    +/**
    + * Cancels the request no matter what the underlying transport is.
    + */
    +goog.net.ChannelRequest.prototype.cancel = function() {
    +  this.cancelled_ = true;
    +  this.cleanup_();
    +};
    +
    +
    +/**
    + * Ensures that there is watchdog timeout which is used to ensure that
    + * the connection completes in time.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.ensureWatchDogTimer_ = function() {
    +  this.watchDogTimeoutTime_ = goog.now() + this.timeout_;
    +  this.startWatchDogTimer_(this.timeout_);
    +};
    +
    +
    +/**
    + * Starts the watchdog timer which is used to ensure that the connection
    + * completes in time.
    + * @param {number} time The number of milliseconds to wait.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.startWatchDogTimer_ = function(time) {
    +  if (this.watchDogTimerId_ != null) {
    +    // assertion
    +    throw Error('WatchDog timer not null');
    +  }
    +  this.watchDogTimerId_ =   /** @suppress {missingRequire} */ (
    +      goog.net.BrowserChannel.setTimeout(
    +          goog.bind(this.onWatchDogTimeout_, this), time));
    +};
    +
    +
    +/**
    + * Cancels the watchdog timer if it has been started.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.cancelWatchDogTimer_ = function() {
    +  if (this.watchDogTimerId_) {
    +    goog.global.clearTimeout(this.watchDogTimerId_);
    +    this.watchDogTimerId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Called when the watchdog timer is triggered. It also handles a case where it
    + * is called too early which we suspect may be happening sometimes
    + * (not sure why)
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.onWatchDogTimeout_ = function() {
    +  this.watchDogTimerId_ = null;
    +  var now = goog.now();
    +  if (now - this.watchDogTimeoutTime_ >= 0) {
    +    this.handleTimeout_();
    +  } else {
    +    // got called too early for some reason
    +    this.channelDebug_.warning('WatchDog timer called too early');
    +    this.startWatchDogTimer_(this.watchDogTimeoutTime_ - now);
    +  }
    +};
    +
    +
    +/**
    + * Called when the request has actually timed out. Will cleanup and notify the
    + * channel of the failure.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.handleTimeout_ = function() {
    +  if (this.successful_) {
    +    // Should never happen.
    +    this.channelDebug_.severe(
    +        'Received watchdog timeout even though request loaded successfully');
    +  }
    +
    +  this.channelDebug_.timeoutResponse(this.requestUri_);
    +  // IMG requests never notice if they were successful, and always 'time out'.
    +  // This fact says nothing about reachability.
    +  if (this.type_ != goog.net.ChannelRequest.Type_.IMG) {
    +    this.channel_.notifyServerReachabilityEvent(
    +        /** @suppress {missingRequire} */
    +        goog.net.BrowserChannel.ServerReachability.REQUEST_FAILED);
    +  }
    +  this.cleanup_();
    +
    +  // set error and dispatch failure
    +  this.lastError_ = goog.net.ChannelRequest.Error.TIMEOUT;
    +  /** @suppress {missingRequire} */
    +  goog.net.BrowserChannel.notifyStatEvent(
    +      /** @suppress {missingRequire} */
    +      goog.net.BrowserChannel.Stat.REQUEST_TIMEOUT);
    +  this.dispatchFailure_();
    +};
    +
    +
    +/**
    + * Notifies the channel that this request failed.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.dispatchFailure_ = function() {
    +  if (this.channel_.isClosed() || this.cancelled_) {
    +    return;
    +  }
    +
    +  this.channel_.onRequestComplete(this);
    +};
    +
    +
    +/**
    + * Cleans up the objects used to make the request. This function is
    + * idempotent.
    + *
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.cleanup_ = function() {
    +  this.cancelWatchDogTimer_();
    +
    +  goog.dispose(this.readyStateChangeThrottle_);
    +  this.readyStateChangeThrottle_ = null;
    +
    +  // Stop the polling timer, if necessary.
    +  this.pollingTimer_.stop();
    +
    +  // Unhook all event handlers.
    +  this.eventHandler_.removeAll();
    +
    +  if (this.xmlHttp_) {
    +    // clear out this.xmlHttp_ before aborting so we handle getting reentered
    +    // inside abort
    +    var xmlhttp = this.xmlHttp_;
    +    this.xmlHttp_ = null;
    +    xmlhttp.abort();
    +    xmlhttp.dispose();
    +  }
    +
    +  if (this.trident_) {
    +    this.trident_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Indicates whether the request was successful. Only valid after the handler
    + * is called to indicate completion of the request.
    + *
    + * @return {boolean} True if the request succeeded.
    + */
    +goog.net.ChannelRequest.prototype.getSuccess = function() {
    +  return this.successful_;
    +};
    +
    +
    +/**
    + * If the request was not successful, returns the reason.
    + *
    + * @return {?goog.net.ChannelRequest.Error}  The last error.
    + */
    +goog.net.ChannelRequest.prototype.getLastError = function() {
    +  return this.lastError_;
    +};
    +
    +
    +/**
    + * Returns the status code of the last request.
    + * @return {number} The status code of the last request.
    + */
    +goog.net.ChannelRequest.prototype.getLastStatusCode = function() {
    +  return this.lastStatusCode_;
    +};
    +
    +
    +/**
    + * Returns the session id for this channel.
    + *
    + * @return {string|undefined} The session ID.
    + */
    +goog.net.ChannelRequest.prototype.getSessionId = function() {
    +  return this.sid_;
    +};
    +
    +
    +/**
    + * Returns the request id for this request. Each request has a unique request
    + * id and the request IDs are a sequential increasing count.
    + *
    + * @return {string|number|undefined} The request ID.
    + */
    +goog.net.ChannelRequest.prototype.getRequestId = function() {
    +  return this.rid_;
    +};
    +
    +
    +/**
    + * Returns the data for a post, if this request is a post.
    + *
    + * @return {?string} The POST data provided by the request initiator.
    + */
    +goog.net.ChannelRequest.prototype.getPostData = function() {
    +  return this.postData_;
    +};
    +
    +
    +/**
    + * Returns the time that the request started, if it has started.
    + *
    + * @return {?number} The time the request started, as returned by goog.now().
    + */
    +goog.net.ChannelRequest.prototype.getRequestStartTime = function() {
    +  return this.requestStartTime_;
    +};
    +
    +
    +/**
    + * Helper to call the callback's onRequestData, which catches any
    + * exception and cleans up the request.
    + * @param {string} data The request data.
    + * @private
    + */
    +goog.net.ChannelRequest.prototype.safeOnRequestData_ = function(data) {
    +  /** @preserveTry */
    +  try {
    +    this.channel_.onRequestData(this, data);
    +    this.channel_.notifyServerReachabilityEvent(
    +        /** @suppress {missingRequire} */
    +        goog.net.BrowserChannel.ServerReachability.BACK_CHANNEL_ACTIVITY);
    +  } catch (e) {
    +    // Dump debug info, but keep going without closing the channel.
    +    this.channelDebug_.dumpException(
    +        e, 'Error in httprequest callback');
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.html b/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.html
    new file mode 100644
    index 0000000000..10fba0732d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.ChannelRequest
    +  </title>
    +  <script src="../base.js" type="text/javascript">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.net.ChannelRequestTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.js b/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.js
    new file mode 100644
    index 0000000000..75011ef0c6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/channelrequest_test.js
    @@ -0,0 +1,282 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.ChannelRequestTest');
    +goog.setTestOnly('goog.net.ChannelRequestTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.functions');
    +goog.require('goog.net.BrowserChannel');
    +goog.require('goog.net.ChannelDebug');
    +goog.require('goog.net.ChannelRequest');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIo');
    +goog.require('goog.testing.recordFunction');
    +
    +var channelRequest;
    +var mockBrowserChannel;
    +var mockClock;
    +var stubs;
    +var xhrIo;
    +
    +
    +/**
    + * Time to wait for a network request to time out, before aborting.
    + */
    +var WATCHDOG_TIME = 2000;
    +
    +
    +/**
    + * Time to throttle readystatechange events.
    + */
    +var THROTTLE_TIME = 500;
    +
    +
    +/**
    + * A really long time - used to make sure no more timeouts will fire.
    + */
    +var ALL_DAY_MS = 1000 * 60 * 60 * 24;
    +
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +
    +function tearDown() {
    +  stubs.reset();
    +  mockClock.uninstall();
    +}
    +
    +
    +
    +/**
    + * Constructs a duck-type BrowserChannel that tracks the completed requests.
    +  * @constructor
    + */
    +function MockBrowserChannel() {
    +  this.reachabilityEvents = {};
    +  this.isClosed = function() {
    +    return false;
    +  };
    +  this.isActive = function() {
    +    return true;
    +  };
    +  this.shouldUseSecondaryDomains = function() {
    +    return false;
    +  };
    +  this.completedRequests = [];
    +  this.notifyServerReachabilityEvent = function(reachabilityType) {
    +    if (!this.reachabilityEvents[reachabilityType]) {
    +      this.reachabilityEvents[reachabilityType] = 0;
    +    }
    +    this.reachabilityEvents[reachabilityType]++;
    +  };
    +  this.onRequestComplete = function(request) {
    +    this.completedRequests.push(request);
    +  };
    +  this.onRequestData = function(request, data) {};
    +}
    +
    +
    +/**
    + * Creates a real ChannelRequest object, with some modifications for
    + * testability:
    + * <ul>
    + * <li>The BrowserChannel is a MockBrowserChannel.
    + * <li>The new watchdogTimeoutCallCount property tracks onWatchDogTimeout_()
    + *     calls.
    + * <li>The timeout is set to WATCHDOG_TIME.
    + * </ul>
    + */
    +function createChannelRequest() {
    +  xhrIo = new goog.testing.net.XhrIo();
    +  xhrIo.abort = xhrIo.abort || function() {
    +    this.active_ = false;
    +  };
    +
    +  // Install mock browser channel and no-op debug logger.
    +  mockBrowserChannel = new MockBrowserChannel();
    +  channelRequest = new goog.net.ChannelRequest(
    +      mockBrowserChannel,
    +      new goog.net.ChannelDebug());
    +
    +  // Install test XhrIo.
    +  mockBrowserChannel.createXhrIo = function() {
    +    return xhrIo;
    +  };
    +
    +  // Install watchdogTimeoutCallCount.
    +  channelRequest.watchdogTimeoutCallCount = 0;
    +  channelRequest.originalOnWatchDogTimeout = channelRequest.onWatchDogTimeout_;
    +  channelRequest.onWatchDogTimeout_ = function() {
    +    this.watchdogTimeoutCallCount++;
    +    return this.originalOnWatchDogTimeout();
    +  };
    +
    +  channelRequest.setTimeout(WATCHDOG_TIME);
    +}
    +
    +
    +/**
    + * Run through the lifecycle of a long lived request, checking that the right
    + * network events are reported.
    + */
    +function testNetworkEvents() {
    +  createChannelRequest();
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  checkReachabilityEvents(1, 0, 0, 0);
    +  if (goog.net.ChannelRequest.supportsXhrStreaming()) {
    +    xhrIo.simulatePartialResponse('17\nI am a BC Message');
    +    checkReachabilityEvents(1, 0, 0, 1);
    +    xhrIo.simulatePartialResponse('23\nI am another BC Message');
    +    checkReachabilityEvents(1, 0, 0, 2);
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 2);
    +  } else {
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 0);
    +  }
    +}
    +
    +
    +/**
    + * Test throttling of readystatechange events.
    + */
    +function testNetworkEvents_throttleReadyStateChange() {
    +  createChannelRequest();
    +  channelRequest.setReadyStateChangeThrottle(THROTTLE_TIME);
    +
    +  var recordedHandler =
    +      goog.testing.recordFunction(channelRequest.xmlHttpHandler_);
    +  stubs.set(channelRequest, 'xmlHttpHandler_', recordedHandler);
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  assertEquals(1, recordedHandler.getCallCount());
    +
    +  checkReachabilityEvents(1, 0, 0, 0);
    +  if (goog.net.ChannelRequest.supportsXhrStreaming()) {
    +    xhrIo.simulatePartialResponse('17\nI am a BC Message');
    +    checkReachabilityEvents(1, 0, 0, 1);
    +    assertEquals(3, recordedHandler.getCallCount());
    +
    +    // Second event should be throttled
    +    xhrIo.simulatePartialResponse('23\nI am another BC Message');
    +    assertEquals(3, recordedHandler.getCallCount());
    +
    +    xhrIo.simulatePartialResponse('27\nI am yet another BC Message');
    +    assertEquals(3, recordedHandler.getCallCount());
    +    mockClock.tick(THROTTLE_TIME);
    +
    +    checkReachabilityEvents(1, 0, 0, 3);
    +    // Only one more call because of throttling.
    +    assertEquals(4, recordedHandler.getCallCount());
    +
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 3);
    +    assertEquals(5, recordedHandler.getCallCount());
    +  } else {
    +    xhrIo.simulateResponse(200, '16\Final BC Message');
    +    checkReachabilityEvents(1, 1, 0, 0);
    +  }
    +}
    +
    +
    +/**
    + * Make sure that the request "completes" with an error when the timeout
    + * expires.
    + */
    +function testRequestTimeout() {
    +  createChannelRequest();
    +
    +  channelRequest.xmlHttpPost(new goog.Uri('some_uri'), 'some_postdata', true);
    +  assertEquals(0, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(0, channelRequest.channel_.completedRequests.length);
    +
    +  // Watchdog timeout.
    +  mockClock.tick(WATCHDOG_TIME);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +  assertFalse(channelRequest.getSuccess());
    +
    +  // Make sure no more timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +
    +  checkReachabilityEvents(1, 0, 1, 0);
    +}
    +
    +
    +function testRequestTimeoutWithUnexpectedException() {
    +  createChannelRequest();
    +  channelRequest.channel_.createXhrIo = goog.functions.error('Weird error');
    +
    +  try {
    +    channelRequest.xmlHttpGet(new goog.Uri('some_uri'), true, null);
    +    fail('Expected error');
    +  } catch (e) {
    +    assertEquals('Weird error', e.message);
    +  }
    +
    +
    +  assertEquals(0, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(0, channelRequest.channel_.completedRequests.length);
    +
    +  // Watchdog timeout.
    +  mockClock.tick(WATCHDOG_TIME);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +  assertFalse(channelRequest.getSuccess());
    +
    +  // Make sure no more timers are firing.
    +  mockClock.tick(ALL_DAY_MS);
    +  assertEquals(1, channelRequest.watchdogTimeoutCallCount);
    +  assertEquals(1, channelRequest.channel_.completedRequests.length);
    +
    +  checkReachabilityEvents(0, 0, 1, 0);
    +}
    +
    +function testActiveXBlocked() {
    +  createChannelRequest();
    +  stubs.set(goog.global, 'ActiveXObject',
    +      goog.functions.error('Active X blocked'));
    +
    +  channelRequest.tridentGet(new goog.Uri('some_uri'), false);
    +  assertFalse(channelRequest.getSuccess());
    +  assertEquals(goog.net.ChannelRequest.Error.ACTIVE_X_BLOCKED,
    +      channelRequest.getLastError());
    +
    +  checkReachabilityEvents(0, 0, 0, 0);
    +}
    +
    +function checkReachabilityEvents(reqMade, reqSucceeded, reqFail, backChannel) {
    +  var Reachability = goog.net.BrowserChannel.ServerReachability;
    +  assertEquals(reqMade,
    +      mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_MADE] || 0);
    +  assertEquals(reqSucceeded,
    +      mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_SUCCEEDED] ||
    +      0);
    +  assertEquals(reqFail,
    +      mockBrowserChannel.reachabilityEvents[Reachability.REQUEST_FAILED] || 0);
    +  assertEquals(backChannel,
    +      mockBrowserChannel.reachabilityEvents[
    +          Reachability.BACK_CHANNEL_ACTIVITY] ||
    +      0);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/cookies.js b/mapbox-gl/closure-library/closure/goog/net/cookies.js
    new file mode 100644
    index 0000000000..16bc41981a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/cookies.js
    @@ -0,0 +1,371 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for setting, getting and deleting cookies.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.net.Cookies');
    +goog.provide('goog.net.cookies');
    +
    +
    +
    +/**
    + * A class for handling browser cookies.
    + * @param {Document} context The context document to get/set cookies on.
    + * @constructor
    + * @final
    + */
    +goog.net.Cookies = function(context) {
    +  /**
    +   * The context document to get/set cookies on
    +   * @type {Document}
    +   * @private
    +   */
    +  this.document_ = context;
    +};
    +
    +
    +/**
    + * Static constant for the size of cookies. Per the spec, there's a 4K limit
    + * to the size of a cookie. To make sure users can't break this limit, we
    + * should truncate long cookies at 3950 bytes, to be extra careful with dumb
    + * browsers/proxies that interpret 4K as 4000 rather than 4096.
    + * @type {number}
    + */
    +goog.net.Cookies.MAX_COOKIE_LENGTH = 3950;
    +
    +
    +/**
    + * RegExp used to split the cookies string.
    + * @type {RegExp}
    + * @private
    + */
    +goog.net.Cookies.SPLIT_RE_ = /\s*;\s*/;
    +
    +
    +/**
    + * Returns true if cookies are enabled.
    + * @return {boolean} True if cookies are enabled.
    + */
    +goog.net.Cookies.prototype.isEnabled = function() {
    +  return navigator.cookieEnabled;
    +};
    +
    +
    +/**
    + * We do not allow '=', ';', or white space in the name.
    + *
    + * NOTE: The following are allowed by this method, but should be avoided for
    + * cookies handled by the server.
    + * - any name starting with '$'
    + * - 'Comment'
    + * - 'Domain'
    + * - 'Expires'
    + * - 'Max-Age'
    + * - 'Path'
    + * - 'Secure'
    + * - 'Version'
    + *
    + * @param {string} name Cookie name.
    + * @return {boolean} Whether name is valid.
    + *
    + * @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
    + * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
    + */
    +goog.net.Cookies.prototype.isValidName = function(name) {
    +  return !(/[;=\s]/.test(name));
    +};
    +
    +
    +/**
    + * We do not allow ';' or line break in the value.
    + *
    + * Spec does not mention any illegal characters, but in practice semi-colons
    + * break parsing and line breaks truncate the name.
    + *
    + * @param {string} value Cookie value.
    + * @return {boolean} Whether value is valid.
    + *
    + * @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
    + * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
    + */
    +goog.net.Cookies.prototype.isValidValue = function(value) {
    +  return !(/[;\r\n]/.test(value));
    +};
    +
    +
    +/**
    + * Sets a cookie.  The max_age can be -1 to set a session cookie. To remove and
    + * expire cookies, use remove() instead.
    + *
    + * Neither the {@code name} nor the {@code value} are encoded in any way. It is
    + * up to the callers of {@code get} and {@code set} (as well as all the other
    + * methods) to handle any possible encoding and decoding.
    + *
    + * @throws {!Error} If the {@code name} fails #goog.net.cookies.isValidName.
    + * @throws {!Error} If the {@code value} fails #goog.net.cookies.isValidValue.
    + *
    + * @param {string} name  The cookie name.
    + * @param {string} value  The cookie value.
    + * @param {number=} opt_maxAge  The max age in seconds (from now). Use -1 to
    + *     set a session cookie. If not provided, the default is -1
    + *     (i.e. set a session cookie).
    + * @param {?string=} opt_path  The path of the cookie. If not present then this
    + *     uses the full request path.
    + * @param {?string=} opt_domain  The domain of the cookie, or null to not
    + *     specify a domain attribute (browser will use the full request host name).
    + *     If not provided, the default is null (i.e. let browser use full request
    + *     host name).
    + * @param {boolean=} opt_secure Whether the cookie should only be sent over
    + *     a secure channel.
    + */
    +goog.net.Cookies.prototype.set = function(
    +    name, value, opt_maxAge, opt_path, opt_domain, opt_secure) {
    +  if (!this.isValidName(name)) {
    +    throw Error('Invalid cookie name "' + name + '"');
    +  }
    +  if (!this.isValidValue(value)) {
    +    throw Error('Invalid cookie value "' + value + '"');
    +  }
    +
    +  if (!goog.isDef(opt_maxAge)) {
    +    opt_maxAge = -1;
    +  }
    +
    +  var domainStr = opt_domain ? ';domain=' + opt_domain : '';
    +  var pathStr = opt_path ? ';path=' + opt_path : '';
    +  var secureStr = opt_secure ? ';secure' : '';
    +
    +  var expiresStr;
    +
    +  // Case 1: Set a session cookie.
    +  if (opt_maxAge < 0) {
    +    expiresStr = '';
    +
    +  // Case 2: Remove the cookie.
    +  // Note: We don't tell people about this option in the function doc because
    +  // we prefer people to use remove() to remove cookies.
    +  } else if (opt_maxAge == 0) {
    +    // Note: Don't use Jan 1, 1970 for date because NS 4.76 will try to convert
    +    // it to local time, and if the local time is before Jan 1, 1970, then the
    +    // browser will ignore the Expires attribute altogether.
    +    var pastDate = new Date(1970, 1 /*Feb*/, 1);  // Feb 1, 1970
    +    expiresStr = ';expires=' + pastDate.toUTCString();
    +
    +  // Case 3: Set a persistent cookie.
    +  } else {
    +    var futureDate = new Date(goog.now() + opt_maxAge * 1000);
    +    expiresStr = ';expires=' + futureDate.toUTCString();
    +  }
    +
    +  this.setCookie_(name + '=' + value + domainStr + pathStr +
    +                  expiresStr + secureStr);
    +};
    +
    +
    +/**
    + * Returns the value for the first cookie with the given name.
    + * @param {string} name  The name of the cookie to get.
    + * @param {string=} opt_default  If not found this is returned instead.
    + * @return {string|undefined}  The value of the cookie. If no cookie is set this
    + *     returns opt_default or undefined if opt_default is not provided.
    + */
    +goog.net.Cookies.prototype.get = function(name, opt_default) {
    +  var nameEq = name + '=';
    +  var parts = this.getParts_();
    +  for (var i = 0, part; part = parts[i]; i++) {
    +    // startsWith
    +    if (part.lastIndexOf(nameEq, 0) == 0) {
    +      return part.substr(nameEq.length);
    +    }
    +    if (part == name) {
    +      return '';
    +    }
    +  }
    +  return opt_default;
    +};
    +
    +
    +/**
    + * Removes and expires a cookie.
    + * @param {string} name  The cookie name.
    + * @param {string=} opt_path  The path of the cookie, or null to expire a cookie
    + *     set at the full request path. If not provided, the default is '/'
    + *     (i.e. path=/).
    + * @param {string=} opt_domain  The domain of the cookie, or null to expire a
    + *     cookie set at the full request host name. If not provided, the default is
    + *     null (i.e. cookie at full request host name).
    + * @return {boolean} Whether the cookie existed before it was removed.
    + */
    +goog.net.Cookies.prototype.remove = function(name, opt_path, opt_domain) {
    +  var rv = this.containsKey(name);
    +  this.set(name, '', 0, opt_path, opt_domain);
    +  return rv;
    +};
    +
    +
    +/**
    + * Gets the names for all the cookies.
    + * @return {Array<string>} An array with the names of the cookies.
    + */
    +goog.net.Cookies.prototype.getKeys = function() {
    +  return this.getKeyValues_().keys;
    +};
    +
    +
    +/**
    + * Gets the values for all the cookies.
    + * @return {Array<string>} An array with the values of the cookies.
    + */
    +goog.net.Cookies.prototype.getValues = function() {
    +  return this.getKeyValues_().values;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether there are any cookies for this document.
    + */
    +goog.net.Cookies.prototype.isEmpty = function() {
    +  return !this.getCookie_();
    +};
    +
    +
    +/**
    + * @return {number} The number of cookies for this document.
    + */
    +goog.net.Cookies.prototype.getCount = function() {
    +  var cookie = this.getCookie_();
    +  if (!cookie) {
    +    return 0;
    +  }
    +  return this.getParts_().length;
    +};
    +
    +
    +/**
    + * Returns whether there is a cookie with the given name.
    + * @param {string} key The name of the cookie to test for.
    + * @return {boolean} Whether there is a cookie by that name.
    + */
    +goog.net.Cookies.prototype.containsKey = function(key) {
    +  // substring will return empty string if the key is not found, so the get
    +  // function will only return undefined
    +  return goog.isDef(this.get(key));
    +};
    +
    +
    +/**
    + * Returns whether there is a cookie with the given value. (This is an O(n)
    + * operation.)
    + * @param {string} value  The value to check for.
    + * @return {boolean} Whether there is a cookie with that value.
    + */
    +goog.net.Cookies.prototype.containsValue = function(value) {
    +  // this O(n) in any case so lets do the trivial thing.
    +  var values = this.getKeyValues_().values;
    +  for (var i = 0; i < values.length; i++) {
    +    if (values[i] == value) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Removes all cookies for this document.  Note that this will only remove
    + * cookies from the current path and domain.  If there are cookies set using a
    + * subpath and/or another domain these will still be there.
    + */
    +goog.net.Cookies.prototype.clear = function() {
    +  var keys = this.getKeyValues_().keys;
    +  for (var i = keys.length - 1; i >= 0; i--) {
    +    this.remove(keys[i]);
    +  }
    +};
    +
    +
    +/**
    + * Private helper function to allow testing cookies without depending on the
    + * browser.
    + * @param {string} s The cookie string to set.
    + * @private
    + */
    +goog.net.Cookies.prototype.setCookie_ = function(s) {
    +  this.document_.cookie = s;
    +};
    +
    +
    +/**
    + * Private helper function to allow testing cookies without depending on the
    + * browser. IE6 can return null here.
    + * @return {string} Returns the {@code document.cookie}.
    + * @private
    + */
    +goog.net.Cookies.prototype.getCookie_ = function() {
    +  return this.document_.cookie;
    +};
    +
    +
    +/**
    + * @return {!Array<string>} The cookie split on semi colons.
    + * @private
    + */
    +goog.net.Cookies.prototype.getParts_ = function() {
    +  return (this.getCookie_() || '').
    +      split(goog.net.Cookies.SPLIT_RE_);
    +};
    +
    +
    +/**
    + * Gets the names and values for all the cookies.
    + * @return {!Object} An object with keys and values.
    + * @private
    + */
    +goog.net.Cookies.prototype.getKeyValues_ = function() {
    +  var parts = this.getParts_();
    +  var keys = [], values = [], index, part;
    +  for (var i = 0; part = parts[i]; i++) {
    +    index = part.indexOf('=');
    +
    +    if (index == -1) { // empty name
    +      keys.push('');
    +      values.push(part);
    +    } else {
    +      keys.push(part.substring(0, index));
    +      values.push(part.substring(index + 1));
    +    }
    +  }
    +  return {keys: keys, values: values};
    +};
    +
    +
    +/**
    + * A static default instance.
    + * @type {goog.net.Cookies}
    + */
    +goog.net.cookies = new goog.net.Cookies(document);
    +
    +
    +/**
    + * Define the constant on the instance in order not to break many references to
    + * it.
    + * @type {number}
    + * @deprecated Use goog.net.Cookies.MAX_COOKIE_LENGTH instead.
    + */
    +goog.net.cookies.MAX_COOKIE_LENGTH = goog.net.Cookies.MAX_COOKIE_LENGTH;
    diff --git a/mapbox-gl/closure-library/closure/goog/net/cookies_test.html b/mapbox-gl/closure-library/closure/goog/net/cookies_test.html
    new file mode 100644
    index 0000000000..f8da4a2a61
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/cookies_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.cookies
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.cookiesTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/cookies_test.js b/mapbox-gl/closure-library/closure/goog/net/cookies_test.js
    new file mode 100644
    index 0000000000..925bf2c845
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/cookies_test.js
    @@ -0,0 +1,265 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.cookiesTest');
    +goog.setTestOnly('goog.net.cookiesTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.net.cookies');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var cookies = goog.net.cookies;
    +var baseCount = 0;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function checkForCookies() {
    +  if (!cookies.isEnabled()) {
    +    var message = 'Cookies must be enabled to run this test.';
    +    if (location.protocol == 'file:') {
    +      message += '\nNote that cookies for local files are disabled in some ' +
    +          'browsers.\nThey can be enabled in Chrome with the ' +
    +          '--enable-file-cookies flag.';
    +    }
    +
    +    fail(message);
    +  }
    +}
    +
    +function setUp() {
    +  checkForCookies();
    +
    +  // Make sure there are no cookies set by previous, bad tests.
    +  cookies.clear();
    +  baseCount = cookies.getCount();
    +}
    +
    +function tearDown() {
    +  // Clear up after ourselves.
    +  cookies.clear();
    +  stubs.reset();
    +}
    +
    +function testIsEnabled() {
    +  assertEquals(navigator.cookieEnabled, cookies.isEnabled());
    +}
    +
    +function testCount() {
    +  // setUp empties the cookies
    +
    +  cookies.set('testa', 'A');
    +  assertEquals(baseCount + 1, cookies.getCount());
    +  cookies.set('testb', 'B');
    +  cookies.set('testc', 'C');
    +  assertEquals(baseCount + 3, cookies.getCount());
    +  cookies.remove('testa');
    +  cookies.remove('testb');
    +  assertEquals(baseCount + 1, cookies.getCount());
    +  cookies.remove('testc');
    +  assertEquals(baseCount + 0, cookies.getCount());
    +}
    +
    +function testSet() {
    +  cookies.set('testa', 'testb');
    +  assertEquals('testb', cookies.get('testa'));
    +  cookies.remove('testa');
    +  assertEquals(undefined, cookies.get('testa'));
    +  // check for invalid characters in name and value
    +}
    +
    +function testGetKeys() {
    +  cookies.set('testa', 'A');
    +  cookies.set('testb', 'B');
    +  cookies.set('testc', 'C');
    +  var keys = cookies.getKeys();
    +  assertTrue(goog.array.contains(keys, 'testa'));
    +  assertTrue(goog.array.contains(keys, 'testb'));
    +  assertTrue(goog.array.contains(keys, 'testc'));
    +}
    +
    +
    +function testGetValues() {
    +  cookies.set('testa', 'A');
    +  cookies.set('testb', 'B');
    +  cookies.set('testc', 'C');
    +  var values = cookies.getValues();
    +  assertTrue(goog.array.contains(values, 'A'));
    +  assertTrue(goog.array.contains(values, 'B'));
    +  assertTrue(goog.array.contains(values, 'C'));
    +}
    +
    +
    +function testContainsKey() {
    +  assertFalse(cookies.containsKey('testa'));
    +  cookies.set('testa', 'A');
    +  assertTrue(cookies.containsKey('testa'));
    +  cookies.set('testb', 'B');
    +  assertTrue(cookies.containsKey('testb'));
    +  cookies.remove('testb');
    +  assertFalse(cookies.containsKey('testb'));
    +  cookies.remove('testa');
    +  assertFalse(cookies.containsKey('testa'));
    +}
    +
    +
    +function testContainsValue() {
    +  assertFalse(cookies.containsValue('A'));
    +  cookies.set('testa', 'A');
    +  assertTrue(cookies.containsValue('A'));
    +  cookies.set('testb', 'B');
    +  assertTrue(cookies.containsValue('B'));
    +  cookies.remove('testb');
    +  assertFalse(cookies.containsValue('B'));
    +  cookies.remove('testa');
    +  assertFalse(cookies.containsValue('A'));
    +}
    +
    +
    +function testIsEmpty() {
    +  // we cannot guarantee that we have no cookies so testing for the true
    +  // case cannot be done without a mock document.cookie
    +  cookies.set('testa', 'A');
    +  assertFalse(cookies.isEmpty());
    +  cookies.set('testb', 'B');
    +  assertFalse(cookies.isEmpty());
    +  cookies.remove('testb');
    +  assertFalse(cookies.isEmpty());
    +  cookies.remove('testa');
    +}
    +
    +
    +function testRemove() {
    +  assertFalse(
    +      '1. Cookie should not contain "testa"', cookies.containsKey('testa'));
    +  cookies.set('testa', 'A', undefined, '/');
    +  assertTrue('2. Cookie should contain "testa"', cookies.containsKey('testa'));
    +  cookies.remove('testa', '/');
    +  assertFalse(
    +      '3. Cookie should not contain "testa"', cookies.containsKey('testa'));
    +
    +  cookies.set('testa', 'A');
    +  assertTrue('4. Cookie should contain "testa"', cookies.containsKey('testa'));
    +  cookies.remove('testa');
    +  assertFalse(
    +      '5. Cookie should not contain "testa"', cookies.containsKey('testa'));
    +}
    +
    +function testStrangeValue() {
    +  // This ensures that the pattern key2=value in the value does not match
    +  // the key2 cookie.
    +  var value = 'testb=bbb';
    +  var value2 = 'ccc';
    +
    +  cookies.set('testa', value);
    +  cookies.set('testb', value2);
    +
    +  assertEquals(value, cookies.get('testa'));
    +  assertEquals(value2, cookies.get('testb'));
    +}
    +
    +function testSetCookiePath() {
    +  assertEquals('foo=bar;path=/xyz',
    +      mockSetCookie('foo', 'bar', -1, '/xyz'));
    +}
    +
    +function testSetCookieDomain() {
    +  assertEquals('foo=bar;domain=google.com',
    +      mockSetCookie('foo', 'bar', -1, null, 'google.com'));
    +}
    +
    +function testSetCookieSecure() {
    +  assertEquals('foo=bar;secure',
    +      mockSetCookie('foo', 'bar', -1, null, null, true));
    +}
    +
    +function testSetCookieMaxAgeZero() {
    +  var result = mockSetCookie('foo', 'bar', 0);
    +  var pattern = new RegExp(
    +      'foo=bar;expires=' + new Date(1970, 1, 1).toUTCString());
    +  if (!result.match(pattern)) {
    +    fail('expected match against ' + pattern + ' got ' + result);
    +  }
    +}
    +
    +function testGetEmptyCookie() {
    +  var value = '';
    +
    +  cookies.set('test', value);
    +
    +  assertEquals(value, cookies.get('test'));
    +}
    +
    +function testGetEmptyCookieIE() {
    +  stubs.set(cookies, 'getCookie_', function() {
    +    return 'test1; test2; test3';
    +  });
    +
    +  assertEquals('', cookies.get('test1'));
    +  assertEquals('', cookies.get('test2'));
    +  assertEquals('', cookies.get('test3'));
    +}
    +
    +// TODO(chrisn): Testing max age > 0 requires a mock clock.
    +
    +function mockSetCookie(var_args) {
    +  var setCookie = cookies.setCookie_;
    +  try {
    +    var result;
    +    cookies.setCookie_ = function(arg) {
    +      result = arg;
    +    };
    +    cookies.set.apply(cookies, arguments);
    +    return result;
    +  } finally {
    +    cookies.setCookie_ = setCookie;
    +  }
    +}
    +
    +function assertValidName(name) {
    +  assertTrue(name + ' should be valid', cookies.isValidName(name));
    +}
    +
    +function assertInvalidName(name) {
    +  assertFalse(name + ' should be invalid', cookies.isValidName(name));
    +  assertThrows(function() {
    +    cookies.set(name, 'value');
    +  });
    +}
    +
    +function assertValidValue(val) {
    +  assertTrue(val + ' should be valid', cookies.isValidValue(val));
    +}
    +
    +function assertInvalidValue(val) {
    +  assertFalse(val + ' should be invalid', cookies.isValidValue(val));
    +  assertThrows(function() {
    +    cookies.set('name', val);
    +  });
    +}
    +
    +function testValidName() {
    +  assertValidName('foo');
    +  assertInvalidName('foo bar');
    +  assertInvalidName('foo=bar');
    +  assertInvalidName('foo;bar');
    +  assertInvalidName('foo\nbar');
    +}
    +
    +function testValidValue() {
    +  assertValidValue('foo');
    +  assertValidValue('foo bar');
    +  assertValidValue('foo=bar');
    +  assertInvalidValue('foo;bar');
    +  assertInvalidValue('foo\nbar');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory.js b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory.js
    new file mode 100644
    index 0000000000..4f8f1455de
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory.js
    @@ -0,0 +1,272 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file contain classes that add support for cross-domain XHR
    + * requests (see http://www.w3.org/TR/cors/). Most modern browsers are able to
    + * use a regular XMLHttpRequest for that, but IE 8 use XDomainRequest object
    + * instead. This file provides an adapter from this object to a goog.net.XhrLike
    + * and a factory to allow using this with a goog.net.XhrIo instance.
    + *
    + * IE 7 and older versions are not supported (given that they do not support
    + * CORS requests).
    + */
    +goog.provide('goog.net.CorsXmlHttpFactory');
    +goog.provide('goog.net.IeCorsXhrAdapter');
    +
    +goog.require('goog.net.HttpStatus');
    +goog.require('goog.net.XhrLike');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.net.XmlHttpFactory');
    +
    +
    +
    +/**
    + * A factory of XML http request objects that supports cross domain requests.
    + * This class should be instantiated and passed as the parameter of a
    + * goog.net.XhrIo constructor to allow cross-domain requests in every browser.
    + *
    + * @extends {goog.net.XmlHttpFactory}
    + * @constructor
    + * @final
    + */
    +goog.net.CorsXmlHttpFactory = function() {
    +  goog.net.XmlHttpFactory.call(this);
    +};
    +goog.inherits(goog.net.CorsXmlHttpFactory, goog.net.XmlHttpFactory);
    +
    +
    +/** @override */
    +goog.net.CorsXmlHttpFactory.prototype.createInstance = function() {
    +  var xhr = new XMLHttpRequest();
    +  if (('withCredentials' in xhr)) {
    +    return xhr;
    +  } else if (typeof XDomainRequest != 'undefined') {
    +    return new goog.net.IeCorsXhrAdapter();
    +  } else {
    +    throw Error('Unsupported browser');
    +  }
    +};
    +
    +
    +/** @override */
    +goog.net.CorsXmlHttpFactory.prototype.internalGetOptions = function() {
    +  return {};
    +};
    +
    +
    +
    +/**
    + * An adapter around Internet Explorer's XDomainRequest object that makes it
    + * look like a standard XMLHttpRequest. This can be used instead of
    + * XMLHttpRequest to support CORS.
    + *
    + * @implements {goog.net.XhrLike}
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.net.IeCorsXhrAdapter = function() {
    +  /**
    +   * The underlying XDomainRequest used to make the HTTP request.
    +   * @type {!XDomainRequest}
    +   * @private
    +   */
    +  this.xdr_ = new XDomainRequest();
    +
    +  /**
    +   * The simulated ready state.
    +   * @type {number}
    +   */
    +  this.readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +
    +  /**
    +   * The simulated ready state change callback function.
    +   * @type {Function}
    +   */
    +  this.onreadystatechange = null;
    +
    +  /**
    +   * The simulated response text parameter.
    +   * @type {?string}
    +   */
    +  this.responseText = null;
    +
    +  /**
    +   * The simulated status code
    +   * @type {number}
    +   */
    +  this.status = -1;
    +
    +  /** @override */
    +  this.responseXML = null;
    +
    +  /** @override */
    +  this.statusText = null;
    +
    +  this.xdr_.onload = goog.bind(this.handleLoad_, this);
    +  this.xdr_.onerror = goog.bind(this.handleError_, this);
    +  this.xdr_.onprogress = goog.bind(this.handleProgress_, this);
    +  this.xdr_.ontimeout = goog.bind(this.handleTimeout_, this);
    +};
    +
    +
    +/**
    + * Opens a connection to the provided URL.
    + * @param {string} method The HTTP method to use. Valid methods include GET and
    + *     POST.
    + * @param {string} url The URL to contact. The authority of this URL must match
    + *     the authority of the current page's URL (e.g. http or https).
    + * @param {?boolean=} opt_async Whether the request is asynchronous, defaulting
    + *     to true. XDomainRequest does not support syncronous requests, so setting
    + *     it to false will actually raise an exception.
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.open = function(method, url, opt_async) {
    +  if (goog.isDefAndNotNull(opt_async) && (!opt_async)) {
    +    throw new Error('Only async requests are supported.');
    +  }
    +  this.xdr_.open(method, url);
    +};
    +
    +
    +/**
    + * Sends the request to the remote server. Before calling this function, always
    + * call {@link open}.
    + * @param {(ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string)=}
    + *     opt_content The content to send as POSTDATA, if any. Only string data is
    + *     supported by this implementation.
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.send = function(opt_content) {
    +  if (opt_content) {
    +    if (typeof opt_content == 'string') {
    +      this.xdr_.send(opt_content);
    +    } else {
    +      throw new Error('Only string data is supported');
    +    }
    +  } else {
    +    this.xdr_.send();
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.abort = function() {
    +  this.xdr_.abort();
    +};
    +
    +
    +/**
    + * Sets a request header to send to the remote server. Because this
    + * implementation does not support request headers, this function does nothing.
    + * @param {string} key The name of the HTTP header to set. Ignored.
    + * @param {string} value The value to set for the HTTP header. Ignored.
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.setRequestHeader = function(key, value) {
    +  // Unsupported; ignore the header.
    +};
    +
    +
    +/**
    + * Returns the value of the response header identified by key. This
    + * implementation only supports the 'content-type' header.
    + * @param {string} key The request header to fetch. If this parameter is set to
    + *     'content-type' (case-insensitive), this function returns the value of
    + *     the 'content-type' request header. If this parameter is set to any other
    + *     value, this function always returns an empty string.
    + * @return {string} The value of the response header, or an empty string if key
    + *     is not 'content-type' (case-insensitive).
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.getResponseHeader = function(key) {
    +  if (key.toLowerCase() == 'content-type') {
    +    return this.xdr_.contentType;
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Handles a request that has fully loaded successfully.
    + * @private
    + */
    +goog.net.IeCorsXhrAdapter.prototype.handleLoad_ = function() {
    +  // IE only calls onload if the status is 200, so the status code must be OK.
    +  this.status = goog.net.HttpStatus.OK;
    +  this.responseText = this.xdr_.responseText;
    +  this.setReadyState_(goog.net.XmlHttp.ReadyState.COMPLETE);
    +};
    +
    +
    +/**
    + * Handles a request that has failed to load.
    + * @private
    + */
    +goog.net.IeCorsXhrAdapter.prototype.handleError_ = function() {
    +  // IE doesn't tell us what the status code actually is (other than the fact
    +  // that it is not 200), so simulate an INTERNAL_SERVER_ERROR.
    +  this.status = goog.net.HttpStatus.INTERNAL_SERVER_ERROR;
    +  this.responseText = null;
    +  this.setReadyState_(goog.net.XmlHttp.ReadyState.COMPLETE);
    +};
    +
    +
    +/**
    + * Handles a request that timed out.
    + * @private
    + */
    +goog.net.IeCorsXhrAdapter.prototype.handleTimeout_ = function() {
    +  this.handleError_();
    +};
    +
    +
    +/**
    + * Handles a request that is in the process of loading.
    + * @private
    + */
    +goog.net.IeCorsXhrAdapter.prototype.handleProgress_ = function() {
    +  // IE only calls onprogress if the status is 200, so the status code must be
    +  // OK.
    +  this.status = goog.net.HttpStatus.OK;
    +  this.setReadyState_(goog.net.XmlHttp.ReadyState.LOADING);
    +};
    +
    +
    +/**
    + * Sets this XHR's ready state and fires the onreadystatechange listener (if one
    + * is set).
    + * @param {number} readyState The new ready state.
    + * @private
    + */
    +goog.net.IeCorsXhrAdapter.prototype.setReadyState_ = function(readyState) {
    +  this.readyState = readyState;
    +  if (this.onreadystatechange) {
    +    this.onreadystatechange();
    +  }
    +};
    +
    +
    +/**
    + * Returns the response headers from the server. This implemntation only returns
    + * the 'content-type' header.
    + * @return {string} The headers returned from the server.
    + * @override
    + */
    +goog.net.IeCorsXhrAdapter.prototype.getAllResponseHeaders = function() {
    +  return 'content-type: ' + this.xdr_.contentType;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.html b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.html
    new file mode 100644
    index 0000000000..9b192f06cf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.CorsXmlHttpFactory
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.net.CorsXmlHttpFactoryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.js b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.js
    new file mode 100644
    index 0000000000..7d38a5d92c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/corsxmlhttpfactory_test.js
    @@ -0,0 +1,44 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.CorsXmlHttpFactoryTest');
    +goog.setTestOnly('goog.net.CorsXmlHttpFactoryTest');
    +
    +goog.require('goog.net.CorsXmlHttpFactory');
    +goog.require('goog.net.IeCorsXhrAdapter');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function testBrowserSupport() {
    +  var requestFactory = new goog.net.CorsXmlHttpFactory();
    +  if (goog.userAgent.IE) {
    +    if (goog.userAgent.isVersionOrHigher('10')) {
    +      // Continue: IE10 supports CORS requests using native XMLHttpRequest.
    +    } else if (goog.userAgent.isVersionOrHigher('8')) {
    +      assertTrue(
    +          requestFactory.createInstance() instanceof goog.net.IeCorsXhrAdapter);
    +      return;
    +    } else {
    +      try {
    +        requestFactory.createInstance();
    +        fail('Error expected.');
    +      } catch (e) {
    +        assertEquals('Unsupported browser', e.message);
    +        return;
    +      }
    +    }
    +  }
    +  // All other browsers support CORS requests using native XMLHttpRequest.
    +  assertTrue(requestFactory.createInstance() instanceof XMLHttpRequest);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc.js b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc.js
    new file mode 100644
    index 0000000000..4f47fb4721
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc.js
    @@ -0,0 +1,883 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Cross domain RPC library using the <a
    + * href="http://go/xd2_design" target="_top">XD2 approach</a>.
    + *
    + * <h5>Protocol</h5>
    + * Client sends a request across domain via a form submission.  Server
    + * receives these parameters: "xdpe:request-id", "xdpe:dummy-uri" ("xdpe" for
    + * "cross domain parameter to echo back") and other user parameters prefixed
    + * with "xdp" (for "cross domain parameter").  Headers are passed as parameters
    + * prefixed with "xdh" (for "cross domain header").  Only strings are supported
    + * for parameters and headers.  A GET method is mapped to a form GET.  All
    + * other methods are mapped to a POST.  Server is expected to produce a
    + * HTML response such as the following:
    + * <pre>
    + * &lt;body&gt;
    + * &lt;script type="text/javascript"
    + *     src="path-to-crossdomainrpc.js"&gt;&lt;/script&gt;
    + * var currentDirectory = location.href.substring(
    + *     0, location.href.lastIndexOf('/')
    + * );
    + *
    + * // echo all parameters prefixed with "xdpe:"
    + * var echo = {};
    + * echo[goog.net.CrossDomainRpc.PARAM_ECHO_REQUEST_ID] =
    + *     &lt;value of parameter "xdpe:request-id"&gt;;
    + * echo[goog.net.CrossDomainRpc.PARAM_ECHO_DUMMY_URI] =
    + *     &lt;value of parameter "xdpe:dummy-uri"&gt;;
    + *
    + * goog.net.CrossDomainRpc.sendResponse(
    + *     '({"result":"&lt;responseInJSON"})',
    + *     true,    // is JSON
    + *     echo,    // parameters to echo back
    + *     status,  // response status code
    + *     headers  // response headers
    + * );
    + * &lt;/script&gt;
    + * &lt;/body&gt;
    + * </pre>
    + *
    + * <h5>Server Side</h5>
    + * For an example of the server side, refer to the following files:
    + * <ul>
    + * <li>http://go/xdservletfilter.java</li>
    + * <li>http://go/xdservletrequest.java</li>
    + * <li>http://go/xdservletresponse.java</li>
    + * </ul>
    + *
    + * <h5>System Requirements</h5>
    + * Tested on IE6, IE7, Firefox 2.0 and Safari nightly r23841.
    + *
    + */
    +
    +goog.provide('goog.net.CrossDomainRpc');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.HttpStatus');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Creates a new instance of cross domain RPC
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + * @final
    + */
    +goog.net.CrossDomainRpc = function() {
    +  goog.events.EventTarget.call(this);
    +};
    +goog.inherits(goog.net.CrossDomainRpc, goog.events.EventTarget);
    +
    +
    +/**
    + * Cross-domain response iframe marker.
    + * @type {string}
    + * @private
    + */
    +goog.net.CrossDomainRpc.RESPONSE_MARKER_ = 'xdrp';
    +
    +
    +/**
    + * Use a fallback dummy resource if none specified or detected.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.CrossDomainRpc.useFallBackDummyResource_ = true;
    +
    +
    +/** @type {Object} */
    +goog.net.CrossDomainRpc.prototype.responseHeaders;
    +
    +
    +/** @type {string} */
    +goog.net.CrossDomainRpc.prototype.responseText;
    +
    +
    +/** @type {number} */
    +goog.net.CrossDomainRpc.prototype.status;
    +
    +
    +/** @type {number} */
    +goog.net.CrossDomainRpc.prototype.timeWaitedAfterResponseReady_;
    +
    +
    +/** @private {boolean} */
    +goog.net.CrossDomainRpc.prototype.responseTextIsJson_;
    +
    +
    +/** @private {boolean} */
    +goog.net.CrossDomainRpc.prototype.responseReady_;
    +
    +
    +/** @private {!HTMLIFrameElement} */
    +goog.net.CrossDomainRpc.prototype.requestFrame_;
    +
    +
    +/** @private {goog.events.Key} */
    +goog.net.CrossDomainRpc.prototype.loadListenerKey_;
    +
    +
    +/**
    + * Checks to see if we are executing inside a response iframe.  This is the
    + * case when this page is used as a dummy resource to gain caller's domain.
    + * @return {*} True if we are executing inside a response iframe; false
    + *     otherwise.
    + * @private
    + */
    +goog.net.CrossDomainRpc.isInResponseIframe_ = function() {
    +  return window.location && (window.location.hash.indexOf(
    +      goog.net.CrossDomainRpc.RESPONSE_MARKER_) == 1 ||
    +      window.location.search.indexOf(
    +          goog.net.CrossDomainRpc.RESPONSE_MARKER_) == 1);
    +};
    +
    +
    +/**
    + * Stops execution of the rest of the page if this page is loaded inside a
    + *    response iframe.
    + */
    +if (goog.net.CrossDomainRpc.isInResponseIframe_()) {
    +  if (goog.userAgent.IE) {
    +    document.execCommand('Stop');
    +  } else if (goog.userAgent.GECKO) {
    +    window.stop();
    +  } else {
    +    throw Error('stopped');
    +  }
    +}
    +
    +
    +/**
    + * Sets the URI for a dummy resource on caller's domain.  This function is
    + * used for specifying a particular resource to use rather than relying on
    + * auto detection.
    + * @param {string} dummyResourceUri URI to dummy resource on the same domain
    + *    of caller's page.
    + */
    +goog.net.CrossDomainRpc.setDummyResourceUri = function(dummyResourceUri) {
    +  goog.net.CrossDomainRpc.dummyResourceUri_ = dummyResourceUri;
    +};
    +
    +
    +/**
    + * Sets whether a fallback dummy resource ("/robots.txt" on Firefox and Safari
    + * and current page on IE) should be used when a suitable dummy resource is
    + * not available.
    + * @param {boolean} useFallBack Whether to use fallback or not.
    + */
    +goog.net.CrossDomainRpc.setUseFallBackDummyResource = function(useFallBack) {
    +  goog.net.CrossDomainRpc.useFallBackDummyResource_ = useFallBack;
    +};
    +
    +
    +/**
    + * Sends a request across domain.
    + * @param {string} uri Uri to make request to.
    + * @param {Function=} opt_continuation Continuation function to be called
    + *     when request is completed.  Takes one argument of an event object
    + *     whose target has the following properties: "status" is the HTTP
    + *     response status code, "responseText" is the response text,
    + *     and "headers" is an object with all response headers.  The event
    + *     target's getResponseJson() method returns a JavaScript object evaluated
    + *     from the JSON response or undefined if response is not JSON.
    + * @param {string=} opt_method Method of request. Default is POST.
    + * @param {Object=} opt_params Parameters. Each property is turned into a
    + *     request parameter.
    + * @param {Object=} opt_headers Map of headers of the request.
    + */
    +goog.net.CrossDomainRpc.send =
    +    function(uri, opt_continuation, opt_method, opt_params, opt_headers) {
    +  var xdrpc = new goog.net.CrossDomainRpc();
    +  if (opt_continuation) {
    +    goog.events.listen(xdrpc, goog.net.EventType.COMPLETE, opt_continuation);
    +  }
    +  goog.events.listen(xdrpc, goog.net.EventType.READY, xdrpc.reset);
    +  xdrpc.sendRequest(uri, opt_method, opt_params, opt_headers);
    +};
    +
    +
    +/**
    + * Sets debug mode to true or false.  When debug mode is on, response iframes
    + * are visible and left behind after their use is finished.
    + * @param {boolean} flag Flag to indicate intention to turn debug model on
    + *     (true) or off (false).
    + */
    +goog.net.CrossDomainRpc.setDebugMode = function(flag) {
    +  goog.net.CrossDomainRpc.debugMode_ = flag;
    +};
    +
    +
    +/**
    + * Logger for goog.net.CrossDomainRpc
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.CrossDomainRpc.logger_ =
    +    goog.log.getLogger('goog.net.CrossDomainRpc');
    +
    +
    +/**
    + * Creates the HTML of an input element
    + * @param {string} name Name of input element.
    + * @param {*} value Value of input element.
    + * @return {string} HTML of input element with that name and value.
    + * @private
    + */
    +goog.net.CrossDomainRpc.createInputHtml_ = function(name, value) {
    +  return '<textarea name="' + name + '">' +
    +      goog.net.CrossDomainRpc.escapeAmpersand_(value) + '</textarea>';
    +};
    +
    +
    +/**
    + * Escapes ampersand so that XML/HTML entities are submitted as is because
    + * browser unescapes them when they are put into a text area.
    + * @param {*} value Value to escape.
    + * @return {*} Value with ampersand escaped, if value is a string;
    + *     otherwise the value itself is returned.
    + * @private
    + */
    +goog.net.CrossDomainRpc.escapeAmpersand_ = function(value) {
    +  return value && (goog.isString(value) || value.constructor == String) ?
    +      value.replace(/&/g, '&amp;') : value;
    +};
    +
    +
    +/**
    + * Finds a dummy resource that can be used by response to gain domain of
    + * requester's page.
    + * @return {string} URI of the resource to use.
    + * @private
    + */
    +goog.net.CrossDomainRpc.getDummyResourceUri_ = function() {
    +  if (goog.net.CrossDomainRpc.dummyResourceUri_) {
    +    return goog.net.CrossDomainRpc.dummyResourceUri_;
    +  }
    +
    +  // find a style sheet if not on IE, which will attempt to save style sheet
    +  if (goog.userAgent.GECKO) {
    +    var links = document.getElementsByTagName('link');
    +    for (var i = 0; i < links.length; i++) {
    +      var link = links[i];
    +      // find a link which is on the same domain as this page
    +      // cannot use one with '?' or '#' in its URL as it will confuse
    +      // goog.net.CrossDomainRpc.getFramePayload_()
    +      if (link.rel == 'stylesheet' &&
    +          goog.Uri.haveSameDomain(link.href, window.location.href) &&
    +          link.href.indexOf('?') < 0) {
    +        return goog.net.CrossDomainRpc.removeHash_(link.href);
    +      }
    +    }
    +  }
    +
    +  var images = document.getElementsByTagName('img');
    +  for (var i = 0; i < images.length; i++) {
    +    var image = images[i];
    +    // find a link which is on the same domain as this page
    +    // cannot use one with '?' or '#' in its URL as it will confuse
    +    // goog.net.CrossDomainRpc.getFramePayload_()
    +    if (goog.Uri.haveSameDomain(image.src, window.location.href) &&
    +        image.src.indexOf('?') < 0) {
    +      return goog.net.CrossDomainRpc.removeHash_(image.src);
    +    }
    +  }
    +
    +  if (!goog.net.CrossDomainRpc.useFallBackDummyResource_) {
    +    throw Error(
    +        'No suitable dummy resource specified or detected for this page');
    +  }
    +
    +  if (goog.userAgent.IE) {
    +    // use this page as the dummy resource; remove hash from URL if any
    +    return goog.net.CrossDomainRpc.removeHash_(window.location.href);
    +  } else {
    +    /**
    +     * Try to use "http://<this-domain>/robots.txt" which may exist.  Even if
    +     * it does not, an error page is returned and is a good dummy resource to
    +     * use on Firefox and Safari.  An existing resource is faster because it
    +     * is cached.
    +     */
    +    var locationHref = window.location.href;
    +    var rootSlash = locationHref.indexOf('/', locationHref.indexOf('//') + 2);
    +    var rootHref = locationHref.substring(0, rootSlash);
    +    return rootHref + '/robots.txt';
    +  }
    +};
    +
    +
    +/**
    + * Removes everything at and after hash from URI
    + * @param {string} uri Uri to to remove hash.
    + * @return {string} Uri with its hash and all characters after removed.
    + * @private
    + */
    +goog.net.CrossDomainRpc.removeHash_ = function(uri) {
    +  return uri.split('#')[0];
    +};
    +
    +
    +// ------------
    +// request side
    +
    +
    +/**
    + * next request id used to support multiple XD requests at the same time
    + * @type {number}
    + * @private
    + */
    +goog.net.CrossDomainRpc.nextRequestId_ = 0;
    +
    +
    +/**
    + * Header prefix.
    + * @type {string}
    + */
    +goog.net.CrossDomainRpc.HEADER = 'xdh:';
    +
    +
    +/**
    + * Parameter prefix.
    + * @type {string}
    + */
    +goog.net.CrossDomainRpc.PARAM = 'xdp:';
    +
    +
    +/**
    + * Parameter to echo prefix.
    + * @type {string}
    + */
    +goog.net.CrossDomainRpc.PARAM_ECHO = 'xdpe:';
    +
    +
    +/**
    + * Parameter to echo: request id
    + * @type {string}
    + */
    +goog.net.CrossDomainRpc.PARAM_ECHO_REQUEST_ID =
    +    goog.net.CrossDomainRpc.PARAM_ECHO + 'request-id';
    +
    +
    +/**
    + * Parameter to echo: dummy resource URI
    + * @type {string}
    + */
    +goog.net.CrossDomainRpc.PARAM_ECHO_DUMMY_URI =
    +    goog.net.CrossDomainRpc.PARAM_ECHO + 'dummy-uri';
    +
    +
    +/**
    + * Cross-domain request marker.
    + * @type {string}
    + * @private
    + */
    +goog.net.CrossDomainRpc.REQUEST_MARKER_ = 'xdrq';
    +
    +
    +/**
    + * Sends a request across domain.
    + * @param {string} uri Uri to make request to.
    + * @param {string=} opt_method Method of request. Default is POST.
    + * @param {Object=} opt_params Parameters. Each property is turned into a
    + *     request parameter.
    + * @param {Object=} opt_headers Map of headers of the request.
    + */
    +goog.net.CrossDomainRpc.prototype.sendRequest =
    +    function(uri, opt_method, opt_params, opt_headers) {
    +  // create request frame
    +  var requestFrame = this.requestFrame_ = /** @type {!HTMLIFrameElement} */ (
    +      document.createElement('iframe'));
    +  var requestId = goog.net.CrossDomainRpc.nextRequestId_++;
    +  requestFrame.id = goog.net.CrossDomainRpc.REQUEST_MARKER_ + '-' + requestId;
    +  if (!goog.net.CrossDomainRpc.debugMode_) {
    +    requestFrame.style.position = 'absolute';
    +    requestFrame.style.top = '-5000px';
    +    requestFrame.style.left = '-5000px';
    +  }
    +  document.body.appendChild(requestFrame);
    +
    +  // build inputs
    +  var inputs = [];
    +
    +  // add request id
    +  inputs.push(goog.net.CrossDomainRpc.createInputHtml_(
    +      goog.net.CrossDomainRpc.PARAM_ECHO_REQUEST_ID, requestId));
    +
    +  // add dummy resource uri
    +  var dummyUri = goog.net.CrossDomainRpc.getDummyResourceUri_();
    +  goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +      'dummyUri: ' + dummyUri);
    +  inputs.push(goog.net.CrossDomainRpc.createInputHtml_(
    +      goog.net.CrossDomainRpc.PARAM_ECHO_DUMMY_URI, dummyUri));
    +
    +  // add parameters
    +  if (opt_params) {
    +    for (var name in opt_params) {
    +      var value = opt_params[name];
    +      inputs.push(goog.net.CrossDomainRpc.createInputHtml_(
    +          goog.net.CrossDomainRpc.PARAM + name, value));
    +    }
    +  }
    +
    +  // add headers
    +  if (opt_headers) {
    +    for (var name in opt_headers) {
    +      var value = opt_headers[name];
    +      inputs.push(goog.net.CrossDomainRpc.createInputHtml_(
    +          goog.net.CrossDomainRpc.HEADER + name, value));
    +    }
    +  }
    +
    +  var requestFrameContent = '<body><form method="' +
    +      (opt_method == 'GET' ? 'GET' : 'POST') + '" action="' +
    +      uri + '">' + inputs.join('') + '</form></body>';
    +  var requestFrameDoc = goog.dom.getFrameContentDocument(requestFrame);
    +  requestFrameDoc.open();
    +  requestFrameDoc.write(requestFrameContent);
    +  requestFrameDoc.close();
    +
    +  requestFrameDoc.forms[0].submit();
    +  requestFrameDoc = null;
    +
    +  this.loadListenerKey_ = goog.events.listen(
    +      requestFrame, goog.events.EventType.LOAD, function() {
    +        goog.log.fine(goog.net.CrossDomainRpc.logger_, 'response ready');
    +        this.responseReady_ = true;
    +      }, false, this);
    +
    +  this.receiveResponse_();
    +};
    +
    +
    +/**
    + * period of response polling (ms)
    + * @type {number}
    + * @private
    + */
    +goog.net.CrossDomainRpc.RESPONSE_POLLING_PERIOD_ = 50;
    +
    +
    +/**
    + * timeout from response comes back to sendResponse is called (ms)
    + * @type {number}
    + * @private
    + */
    +goog.net.CrossDomainRpc.SEND_RESPONSE_TIME_OUT_ = 500;
    +
    +
    +/**
    + * Receives response by polling to check readiness of response and then
    + *     reads response frames and assembles response data
    + * @private
    + */
    +goog.net.CrossDomainRpc.prototype.receiveResponse_ = function() {
    +  this.timeWaitedAfterResponseReady_ = 0;
    +  var responseDetectorHandle = window.setInterval(goog.bind(function() {
    +    this.detectResponse_(responseDetectorHandle);
    +  }, this), goog.net.CrossDomainRpc.RESPONSE_POLLING_PERIOD_);
    +};
    +
    +
    +/**
    + * Detects response inside request frame
    + * @param {number} responseDetectorHandle Handle of detector.
    + * @private
    + */
    +goog.net.CrossDomainRpc.prototype.detectResponse_ =
    +    function(responseDetectorHandle) {
    +  var requestFrameWindow = this.requestFrame_.contentWindow;
    +  var grandChildrenLength = requestFrameWindow.frames.length;
    +  var responseInfoFrame = null;
    +  if (grandChildrenLength > 0 &&
    +      goog.net.CrossDomainRpc.isResponseInfoFrame_(responseInfoFrame =
    +      requestFrameWindow.frames[grandChildrenLength - 1])) {
    +    goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +        'xd response ready');
    +
    +    var responseInfoPayload = goog.net.CrossDomainRpc.getFramePayload_(
    +        responseInfoFrame).substring(1);
    +    var params = new goog.Uri.QueryData(responseInfoPayload);
    +
    +    var chunks = [];
    +    var numChunks = Number(params.get('n'));
    +    goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +        'xd response number of chunks: ' + numChunks);
    +    for (var i = 0; i < numChunks; i++) {
    +      var responseFrame = requestFrameWindow.frames[i];
    +      if (!responseFrame || !responseFrame.location ||
    +          !responseFrame.location.href) {
    +        // On Safari 3.0, it is sometimes the case that the
    +        // iframe exists but doesn't have a same domain href yet.
    +        goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +            'xd response iframe not ready');
    +        return;
    +      }
    +      var responseChunkPayload =
    +          goog.net.CrossDomainRpc.getFramePayload_(responseFrame);
    +      // go past "chunk="
    +      var chunkIndex = responseChunkPayload.indexOf(
    +          goog.net.CrossDomainRpc.PARAM_CHUNK_) +
    +          goog.net.CrossDomainRpc.PARAM_CHUNK_.length + 1;
    +      var chunk = responseChunkPayload.substring(chunkIndex);
    +      chunks.push(chunk);
    +    }
    +
    +    window.clearInterval(responseDetectorHandle);
    +
    +    var responseData = chunks.join('');
    +    // Payload is not encoded to begin with on IE. Decode in other cases only.
    +    if (!goog.userAgent.IE) {
    +      responseData = decodeURIComponent(responseData);
    +    }
    +
    +    this.status = Number(params.get('status'));
    +    this.responseText = responseData;
    +    this.responseTextIsJson_ = params.get('isDataJson') == 'true';
    +    this.responseHeaders = goog.json.unsafeParse(
    +        /** @type {string} */ (params.get('headers')));
    +
    +    this.dispatchEvent(goog.net.EventType.READY);
    +    this.dispatchEvent(goog.net.EventType.COMPLETE);
    +  } else {
    +    if (this.responseReady_) {
    +      /* The response has come back. But the first response iframe has not
    +       * been created yet. If this lasts long enough, it is an error.
    +       */
    +      this.timeWaitedAfterResponseReady_ +=
    +          goog.net.CrossDomainRpc.RESPONSE_POLLING_PERIOD_;
    +      if (this.timeWaitedAfterResponseReady_ >
    +          goog.net.CrossDomainRpc.SEND_RESPONSE_TIME_OUT_) {
    +        goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +            'xd response timed out');
    +        window.clearInterval(responseDetectorHandle);
    +
    +        this.status = goog.net.HttpStatus.INTERNAL_SERVER_ERROR;
    +        this.responseText = 'response timed out';
    +
    +        this.dispatchEvent(goog.net.EventType.READY);
    +        this.dispatchEvent(goog.net.EventType.ERROR);
    +        this.dispatchEvent(goog.net.EventType.COMPLETE);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Checks whether a frame is response info frame.
    + * @param {Object} frame Frame to check.
    + * @return {boolean} True if frame is a response info frame; false otherwise.
    + * @private
    + */
    +goog.net.CrossDomainRpc.isResponseInfoFrame_ = function(frame) {
    +  /** @preserveTry */
    +  try {
    +    return goog.net.CrossDomainRpc.getFramePayload_(frame).indexOf(
    +        goog.net.CrossDomainRpc.RESPONSE_INFO_MARKER_) == 1;
    +  } catch (e) {
    +    // frame not ready for same-domain access yet
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Returns the payload of a frame (value after # or ? on the URL).  This value
    + * is URL encoded except IE, where the value is not encoded to begin with.
    + * @param {Object} frame Frame.
    + * @return {string} Payload of that frame.
    + * @private
    + */
    +goog.net.CrossDomainRpc.getFramePayload_ = function(frame) {
    +  var href = frame.location.href;
    +  var question = href.indexOf('?');
    +  var hash = href.indexOf('#');
    +  // On IE, beucase the URL is not encoded, we can have a case where ?
    +  // is the delimiter before payload and # in payload or # as the delimiter
    +  // and ? in payload.  So here we treat whoever is the first as the delimiter.
    +  var delimiter = question < 0 ? hash :
    +      hash < 0 ? question : Math.min(question, hash);
    +  return href.substring(delimiter);
    +};
    +
    +
    +/**
    + * If response is JSON, evaluates it to a JavaScript object and
    + * returns it; otherwise returns undefined.
    + * @return {Object|undefined} JavaScript object if response is in JSON
    + *     or undefined.
    + */
    +goog.net.CrossDomainRpc.prototype.getResponseJson = function() {
    +  return this.responseTextIsJson_ ?
    +      goog.json.unsafeParse(this.responseText) : undefined;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the request completed with a success.
    + */
    +goog.net.CrossDomainRpc.prototype.isSuccess = function() {
    +  // Definition similar to goog.net.XhrIo.prototype.isSuccess.
    +  switch (this.status) {
    +    case goog.net.HttpStatus.OK:
    +    case goog.net.HttpStatus.NOT_MODIFIED:
    +      return true;
    +
    +    default:
    +      return false;
    +  }
    +};
    +
    +
    +/**
    + * Removes request iframe used.
    + */
    +goog.net.CrossDomainRpc.prototype.reset = function() {
    +  if (!goog.net.CrossDomainRpc.debugMode_) {
    +    goog.log.fine(goog.net.CrossDomainRpc.logger_,
    +        'request frame removed: ' + this.requestFrame_.id);
    +    goog.events.unlistenByKey(this.loadListenerKey_);
    +    this.requestFrame_.parentNode.removeChild(this.requestFrame_);
    +  }
    +  delete this.requestFrame_;
    +};
    +
    +
    +// -------------
    +// response side
    +
    +
    +/**
    + * Name of response info iframe.
    + * @type {string}
    + * @private
    + */
    +goog.net.CrossDomainRpc.RESPONSE_INFO_MARKER_ =
    +    goog.net.CrossDomainRpc.RESPONSE_MARKER_ + '-info';
    +
    +
    +/**
    + * Maximal chunk size.  IE can only handle 4095 bytes on its URL.
    + * 16MB has been tested on Firefox.  But 1MB is a practical size.
    + * @type {number}
    + * @private
    + */
    +goog.net.CrossDomainRpc.MAX_CHUNK_SIZE_ =
    +    goog.userAgent.IE ? 4095 : 1024 * 1024;
    +
    +
    +/**
    + * Query parameter 'chunk'.
    + * @type {string}
    + * @private
    + */
    +goog.net.CrossDomainRpc.PARAM_CHUNK_ = 'chunk';
    +
    +
    +/**
    + * Prefix before data chunk for passing other parameters.
    + * type String
    + * @private
    + */
    +goog.net.CrossDomainRpc.CHUNK_PREFIX_ =
    +    goog.net.CrossDomainRpc.RESPONSE_MARKER_ + '=1&' +
    +    goog.net.CrossDomainRpc.PARAM_CHUNK_ + '=';
    +
    +
    +/**
    + * Makes response available for grandparent (requester)'s receiveResponse
    + * call to pick up by creating a series of iframes pointed to the dummy URI
    + * with a payload (value after either ? or #) carrying a chunk of response
    + * data and a response info iframe that tells the grandparent (requester) the
    + * readiness of response.
    + * @param {string} data Response data (string or JSON string).
    + * @param {boolean} isDataJson true if data is a JSON string; false if just a
    + *     string.
    + * @param {Object} echo Parameters to echo back
    + *     "xdpe:request-id": Server that produces the response needs to
    + *     copy it here to support multiple current XD requests on the same page.
    + *     "xdpe:dummy-uri": URI to a dummy resource that response
    + *     iframes point to to gain the domain of the client.  This can be an
    + *     image (IE) or a CSS file (FF) found on the requester's page.
    + *     Server should copy value from request parameter "xdpe:dummy-uri".
    + * @param {number} status HTTP response status code.
    + * @param {string} headers Response headers in JSON format.
    + */
    +goog.net.CrossDomainRpc.sendResponse =
    +    function(data, isDataJson, echo, status, headers) {
    +  var dummyUri = echo[goog.net.CrossDomainRpc.PARAM_ECHO_DUMMY_URI];
    +
    +  // since the dummy-uri can be specified by the user, verify that it doesn't
    +  // use any other protocols. (Specifically we don't want users to use a
    +  // dummy-uri beginning with "javascript:").
    +  if (!goog.string.caseInsensitiveStartsWith(dummyUri, 'http://') &&
    +      !goog.string.caseInsensitiveStartsWith(dummyUri, 'https://')) {
    +    dummyUri = 'http://' + dummyUri;
    +  }
    +
    +  // usable chunk size is max less dummy URI less chunk prefix length
    +  // TODO(user): Figure out why we need to do "- 1" below
    +  var chunkSize = goog.net.CrossDomainRpc.MAX_CHUNK_SIZE_ - dummyUri.length -
    +      1 - // payload delimiter ('#' or '?')
    +      goog.net.CrossDomainRpc.CHUNK_PREFIX_.length - 1;
    +
    +  /*
    +   * Here we used to do URI encoding of data before we divide it into chunks
    +   * and decode on the receiving end.  We don't do this any more on IE for the
    +   * following reasons.
    +   *
    +   * 1) On IE, calling decodeURIComponent on a relatively large string is
    +   *   extremely slow (~22s for 160KB).  So even a moderate amount of data
    +   *   makes this library pretty much useless.  Fortunately, we can actually
    +   *   put unencoded data on IE's URL and get it back reliably.  So we are
    +   *   completely skipping encoding and decoding on IE.  When we call
    +   *   getFrameHash_ to get it back, the value is still intact(*) and unencoded.
    +   * 2) On Firefox, we have to call decodeURIComponent because location.hash
    +   *   does decoding by itself.  Fortunately, decodeURIComponent is not slow
    +   *   on Firefox.
    +   * 3) Safari automatically encodes everything you put on URL and it does not
    +   *   automatically decode when you access it via location.hash or
    +   *   location.href.  So we encode it here and decode it in detectResponse_().
    +   *
    +   * Note(*): IE actually does encode only space to %20 and decodes that
    +   *   automatically when you do location.href or location.hash.
    +   */
    +  if (!goog.userAgent.IE) {
    +    data = encodeURIComponent(data);
    +  }
    +
    +  var numChunksToSend = Math.ceil(data.length / chunkSize);
    +  if (numChunksToSend == 0) {
    +    goog.net.CrossDomainRpc.createResponseInfo_(
    +        dummyUri, numChunksToSend, isDataJson, status, headers);
    +  } else {
    +    var numChunksSent = 0;
    +    var checkToCreateResponseInfo_ = function() {
    +      if (++numChunksSent == numChunksToSend) {
    +        goog.net.CrossDomainRpc.createResponseInfo_(
    +            dummyUri, numChunksToSend, isDataJson, status, headers);
    +      }
    +    };
    +
    +    for (var i = 0; i < numChunksToSend; i++) {
    +      var chunkStart = i * chunkSize;
    +      var chunkEnd = chunkStart + chunkSize;
    +      var chunk = chunkEnd > data.length ?
    +          data.substring(chunkStart) :
    +          data.substring(chunkStart, chunkEnd);
    +
    +      var responseFrame = document.createElement('iframe');
    +      responseFrame.src = dummyUri +
    +          goog.net.CrossDomainRpc.getPayloadDelimiter_(dummyUri) +
    +          goog.net.CrossDomainRpc.CHUNK_PREFIX_ + chunk;
    +      document.body.appendChild(responseFrame);
    +
    +      // We used to call the function below when handling load event of
    +      // responseFrame.  But that event does not fire on IE when current
    +      // page is used as the dummy resource (because its loading is stopped?).
    +      // It also does not fire sometimes on Firefox.  So now we call it
    +      // directly.
    +      checkToCreateResponseInfo_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Creates a response info iframe to indicate completion of sendResponse
    + * @param {string} dummyUri URI to a dummy resource.
    + * @param {number} numChunks Total number of chunks.
    + * @param {boolean} isDataJson Whether response is a JSON string or just string.
    + * @param {number} status HTTP response status code.
    + * @param {string} headers Response headers in JSON format.
    + * @private
    + */
    +goog.net.CrossDomainRpc.createResponseInfo_ =
    +    function(dummyUri, numChunks, isDataJson, status, headers) {
    +  var responseInfoFrame = document.createElement('iframe');
    +  document.body.appendChild(responseInfoFrame);
    +  responseInfoFrame.src = dummyUri +
    +      goog.net.CrossDomainRpc.getPayloadDelimiter_(dummyUri) +
    +      goog.net.CrossDomainRpc.RESPONSE_INFO_MARKER_ +
    +      '=1&n=' + numChunks + '&isDataJson=' + isDataJson + '&status=' + status +
    +      '&headers=' + encodeURIComponent(headers);
    +};
    +
    +
    +/**
    + * Returns payload delimiter, either "#" when caller's page is not used as
    + * the dummy resource or "?" when it is, in which case caching issues prevent
    + * response frames to gain the caller's domain.
    + * @param {string} dummyUri URI to resource being used as dummy resource.
    + * @return {string} Either "?" when caller's page is used as dummy resource or
    + *     "#" if it is not.
    + * @private
    + */
    +goog.net.CrossDomainRpc.getPayloadDelimiter_ = function(dummyUri) {
    +  return goog.net.CrossDomainRpc.REFERRER_ == dummyUri ? '?' : '#';
    +};
    +
    +
    +/**
    + * Removes all parameters (after ? or #) from URI.
    + * @param {string} uri URI to remove parameters from.
    + * @return {string} URI with all parameters removed.
    + * @private
    + */
    +goog.net.CrossDomainRpc.removeUriParams_ = function(uri) {
    +  // remove everything after question mark
    +  var question = uri.indexOf('?');
    +  if (question > 0) {
    +    uri = uri.substring(0, question);
    +  }
    +
    +  // remove everything after hash mark
    +  var hash = uri.indexOf('#');
    +  if (hash > 0) {
    +    uri = uri.substring(0, hash);
    +  }
    +
    +  return uri;
    +};
    +
    +
    +/**
    + * Gets a response header.
    + * @param {string} name Name of response header.
    + * @return {string|undefined} Value of response header; undefined if not found.
    + */
    +goog.net.CrossDomainRpc.prototype.getResponseHeader = function(name) {
    +  return goog.isObject(this.responseHeaders) ?
    +      this.responseHeaders[name] : undefined;
    +};
    +
    +
    +/**
    + * Referrer of current document with all parameters after "?" and "#" stripped.
    + * @type {string}
    + * @private
    + */
    +goog.net.CrossDomainRpc.REFERRER_ =
    +    goog.net.CrossDomainRpc.removeUriParams_(document.referrer);
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.css b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.css
    new file mode 100644
    index 0000000000..73cc31122e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.css
    @@ -0,0 +1,7 @@
    +/*
    + * Copyright 2010 The Closure Library Authors. All Rights Reserved.
    + *
    + * Use of this source code is governed by the Apache License, Version 2.0.
    + * See the COPYING file for details.
    + */
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.gif b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.gif
    new file mode 100644
    index 0000000000..e69de29bb2
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.html b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.html
    new file mode 100644
    index 0000000000..dcc8920b38
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.CrossDomainRpc
    +  </title>
    +  <link href="CrossDomainRpc_test.css?123" rel="stylesheet" type="text/css" />
    +  <link href="CrossDomainRpc_test.css#123" rel="stylesheet" type="text/css" />
    +  <link href="CrossDomainRpc_test.css" rel="stylesheet" type="text/css" />
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.net.CrossDomainRpcTest');
    +  </script>
    + </head>
    + <body>
    +  <img src="crossdomainrpc_test.gif?123" alt="dummy resource" />
    +  <img src="crossdomainrpc_test.gif#123" alt="dummy resource" />
    +  <img src="crossdomainrpc_test.gif" alt="dummy resource" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.js b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.js
    new file mode 100644
    index 0000000000..1bd1f6f0ae
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test.js
    @@ -0,0 +1,119 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.CrossDomainRpcTest');
    +goog.setTestOnly('goog.net.CrossDomainRpcTest');
    +
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.CrossDomainRpc');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +function print(o) {
    +  if (Object.prototype.toSource) {
    +    return o.toSource();
    +  } else {
    +    var fragments = [];
    +    fragments.push('{');
    +    var first = true;
    +    for (var p in o) {
    +      if (!first) fragments.push(',');
    +      fragments.push(p);
    +      fragments.push(':"');
    +      fragments.push(o[p]);
    +      fragments.push('"');
    +      first = false;
    +    }
    +    return fragments.join('');
    +  }
    +}
    +
    +
    +function testNormalRequest() {
    +  var start = new Date();
    +  goog.net.CrossDomainRpc.send(
    +      'crossdomainrpc_test_response.html',
    +      goog.partial(continueTestNormalRequest, start),
    +      'POST',
    +      {xyz: '01234567891123456789'}
    +  );
    +
    +  asyncTestCase.waitForAsync('testNormalRequest');
    +}
    +
    +function continueTestNormalRequest(start, e) {
    +  asyncTestCase.continueTesting();
    +  if (e.target.status < 300) {
    +    var elapsed = new Date() - start;
    +    var responseData = eval(e.target.responseText);
    +    goog.log.log(goog.net.CrossDomainRpc.logger_, goog.log.Level.FINE,
    +                 elapsed + 'ms: [' + responseData.result.length + '] ' +
    +                     print(responseData));
    +    assertEquals(16 * 1024, responseData.result.length);
    +    assertEquals(e.target.status, 123);
    +    assertEquals(e.target.responseHeaders.a, 1);
    +    assertEquals(e.target.responseHeaders.b, '2');
    +  } else {
    +    goog.log.log(goog.net.CrossDomainRpc.logger_, goog.log.Level.FINE,
    +                 print(e));
    +    fail();
    +  }
    +}
    +
    +
    +function testErrorRequest() {
    +  // Firefox does not give a valid error event.
    +  if (goog.userAgent.GECKO) {
    +    return;
    +  }
    +
    +  goog.net.CrossDomainRpc.send(
    +      'http://hoodjimcwaadji.google.com/index.html',
    +      continueTestErrorRequest,
    +      'POST',
    +      {xyz: '01234567891123456789'}
    +  );
    +
    +  asyncTestCase.waitForAsync('testErrorRequest');
    +}
    +
    +function continueTestErrorRequest(e) {
    +  asyncTestCase.continueTesting();
    +
    +  if (e.target.status < 300) {
    +    fail('should have failed requesting a non-existent URI');
    +  } else {
    +    goog.log.log(goog.net.CrossDomainRpc.logger_, goog.log.Level.FINE,
    +                 'expected error seen; event=' + print(e));
    +  }
    +}
    +
    +function testGetDummyResourceUri() {
    +  var url = goog.net.CrossDomainRpc.getDummyResourceUri_();
    +  assertTrue(
    +      'dummy resource URL should not contain "?"', url.indexOf('?') < 0);
    +  assertTrue(
    +      'dummy resource URL should not contain "#"', url.indexOf('#') < 0);
    +}
    +
    +
    +function testRemoveHash() {
    +  assertEquals('abc', goog.net.CrossDomainRpc.removeHash_('abc#123'));
    +  assertEquals('abc', goog.net.CrossDomainRpc.removeHash_('abc#12#3'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test_response.html b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test_response.html
    new file mode 100644
    index 0000000000..f05c988302
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/crossdomainrpc_test_response.html
    @@ -0,0 +1,59 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +
    + In reality, this response comes from a different domain.  For simplicity of
    + testing, this response is one the same domain, while exercising the same
    + functionality.
    +-->
    +<title>crossdomainrpc test response</title>
    +<body>
    +<script type="text/javascript" src="../base.js"></script>
    +<script type="text/javascript">
    +goog.require('goog.Uri.QueryData');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.net.EventType');
    +goog.require('goog.userAgent');
    +</script>
    +<script type="text/javascript" src="crossdomainrpc.js"></script>
    +<script type="text/javascript">
    +function createPayload(size) {
    +  var chars = [];
    +  for (var i = 0; i < size; i++) {
    +    chars.push('0');
    +  }
    +  return chars.join('');
    +};
    +
    +var payload = createPayload(16 * 1024);
    +
    +var currentDirectory = location.href.substring(
    +    0, location.href.lastIndexOf('/')
    +);
    +
    +var echo = {};
    +echo[goog.net.CrossDomainRpc.PARAM_ECHO_REQUEST_ID] = 0;
    +echo[goog.net.CrossDomainRpc.PARAM_ECHO_DUMMY_URI] = goog.userAgent.IE ?
    +    currentDirectory + '/crossdomainrpc_test.gif' :
    +    currentDirectory + '/crossdomainrpc_test.css';
    +
    +goog.net.CrossDomainRpc.sendResponse(
    +    '({"result":"' + payload + '"})',
    +    true,              // is JSON
    +    echo,              // parameters to echo back
    +    123,               // response code
    +    '{"a":1,"b":"2"}'  // response headers
    +);
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/errorcode.js b/mapbox-gl/closure-library/closure/goog/net/errorcode.js
    new file mode 100644
    index 0000000000..4d6d834f89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/errorcode.js
    @@ -0,0 +1,130 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Error codes shared between goog.net.IframeIo and
    + * goog.net.XhrIo.
    + */
    +
    +goog.provide('goog.net.ErrorCode');
    +
    +
    +/**
    + * Error codes
    + * @enum {number}
    + */
    +goog.net.ErrorCode = {
    +
    +  /**
    +   * There is no error condition.
    +   */
    +  NO_ERROR: 0,
    +
    +  /**
    +   * The most common error from iframeio, unfortunately, is that the browser
    +   * responded with an error page that is classed as a different domain. The
    +   * situations, are when a browser error page  is shown -- 404, access denied,
    +   * DNS failure, connection reset etc.)
    +   *
    +   */
    +  ACCESS_DENIED: 1,
    +
    +  /**
    +   * Currently the only case where file not found will be caused is when the
    +   * code is running on the local file system and a non-IE browser makes a
    +   * request to a file that doesn't exist.
    +   */
    +  FILE_NOT_FOUND: 2,
    +
    +  /**
    +   * If Firefox shows a browser error page, such as a connection reset by
    +   * server or access denied, then it will fail silently without the error or
    +   * load handlers firing.
    +   */
    +  FF_SILENT_ERROR: 3,
    +
    +  /**
    +   * Custom error provided by the client through the error check hook.
    +   */
    +  CUSTOM_ERROR: 4,
    +
    +  /**
    +   * Exception was thrown while processing the request.
    +   */
    +  EXCEPTION: 5,
    +
    +  /**
    +   * The Http response returned a non-successful http status code.
    +   */
    +  HTTP_ERROR: 6,
    +
    +  /**
    +   * The request was aborted.
    +   */
    +  ABORT: 7,
    +
    +  /**
    +   * The request timed out.
    +   */
    +  TIMEOUT: 8,
    +
    +  /**
    +   * The resource is not available offline.
    +   */
    +  OFFLINE: 9
    +};
    +
    +
    +/**
    + * Returns a friendly error message for an error code. These messages are for
    + * debugging and are not localized.
    + * @param {goog.net.ErrorCode} errorCode An error code.
    + * @return {string} A message for debugging.
    + */
    +goog.net.ErrorCode.getDebugMessage = function(errorCode) {
    +  switch (errorCode) {
    +    case goog.net.ErrorCode.NO_ERROR:
    +      return 'No Error';
    +
    +    case goog.net.ErrorCode.ACCESS_DENIED:
    +      return 'Access denied to content document';
    +
    +    case goog.net.ErrorCode.FILE_NOT_FOUND:
    +      return 'File not found';
    +
    +    case goog.net.ErrorCode.FF_SILENT_ERROR:
    +      return 'Firefox silently errored';
    +
    +    case goog.net.ErrorCode.CUSTOM_ERROR:
    +      return 'Application custom error';
    +
    +    case goog.net.ErrorCode.EXCEPTION:
    +      return 'An exception occurred';
    +
    +    case goog.net.ErrorCode.HTTP_ERROR:
    +      return 'Http response at 400 or 500 level';
    +
    +    case goog.net.ErrorCode.ABORT:
    +      return 'Request was aborted';
    +
    +    case goog.net.ErrorCode.TIMEOUT:
    +      return 'Request timed out';
    +
    +    case goog.net.ErrorCode.OFFLINE:
    +      return 'The resource is not available offline';
    +
    +    default:
    +      return 'Unrecognized error code';
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/eventtype.js b/mapbox-gl/closure-library/closure/goog/net/eventtype.js
    new file mode 100644
    index 0000000000..ac869792f2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/eventtype.js
    @@ -0,0 +1,37 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Common events for the network classes.
    + */
    +
    +
    +goog.provide('goog.net.EventType');
    +
    +
    +/**
    + * Event names for network events
    + * @enum {string}
    + */
    +goog.net.EventType = {
    +  COMPLETE: 'complete',
    +  SUCCESS: 'success',
    +  ERROR: 'error',
    +  ABORT: 'abort',
    +  READY: 'ready',
    +  READY_STATE_CHANGE: 'readystatechange',
    +  TIMEOUT: 'timeout',
    +  INCREMENTAL_DATA: 'incrementaldata',
    +  PROGRESS: 'progress'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/filedownloader.js b/mapbox-gl/closure-library/closure/goog/net/filedownloader.js
    new file mode 100644
    index 0000000000..699d6b8c19
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/filedownloader.js
    @@ -0,0 +1,746 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for downloading remote files and storing them
    + * locally using the HTML5 FileSystem API.
    + *
    + * The directory structure is of the form /HASH/URL/BASENAME:
    + *
    + * The HASH portion is a three-character slice of the hash of the URL. Since the
    + * filesystem has a limit of about 5000 files per directory, this should divide
    + * the downloads roughly evenly among about 5000 directories, thus allowing for
    + * at most 5000^2 downloads.
    + *
    + * The URL portion is the (sanitized) full URL used for downloading the file.
    + * This is used to ensure that each file ends up in a different location, even
    + * if the HASH and BASENAME are the same.
    + *
    + * The BASENAME portion is the basename of the URL. It's used for the filename
    + * proper so that the local filesystem: URL will be downloaded to a file with a
    + * recognizable name.
    + *
    + */
    +
    +goog.provide('goog.net.FileDownloader');
    +goog.provide('goog.net.FileDownloader.Error');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.crypt.hash32');
    +goog.require('goog.debug.Error');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.fs');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XhrIoPool');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * A class for downloading remote files and storing them locally using the
    + * HTML5 filesystem API.
    + *
    + * @param {!goog.fs.DirectoryEntry} dir The directory in which the downloaded
    + *     files are stored. This directory should be solely managed by
    + *     FileDownloader.
    + * @param {goog.net.XhrIoPool=} opt_pool The pool of XhrIo objects to use for
    + *     downloading files.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.net.FileDownloader = function(dir, opt_pool) {
    +  goog.net.FileDownloader.base(this, 'constructor');
    +
    +  /**
    +   * The directory in which the downloaded files are stored.
    +   * @type {!goog.fs.DirectoryEntry}
    +   * @private
    +   */
    +  this.dir_ = dir;
    +
    +  /**
    +   * The pool of XHRs to use for capturing.
    +   * @type {!goog.net.XhrIoPool}
    +   * @private
    +   */
    +  this.pool_ = opt_pool || new goog.net.XhrIoPool();
    +
    +  /**
    +   * A map from URLs to active downloads running for those URLs.
    +   * @type {!Object<!goog.net.FileDownloader.Download_>}
    +   * @private
    +   */
    +  this.downloads_ = {};
    +
    +  /**
    +   * The handler for URL capturing events.
    +   * @type {!goog.events.EventHandler<!goog.net.FileDownloader>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +};
    +goog.inherits(goog.net.FileDownloader, goog.Disposable);
    +
    +
    +/**
    + * Download a remote file and save its contents to the filesystem. A given file
    + * is uniquely identified by its URL string; this means that the relative and
    + * absolute URLs for a single file are considered different for the purposes of
    + * the FileDownloader.
    + *
    + * Returns a Deferred that will contain the downloaded blob. If there's an error
    + * while downloading the URL, this Deferred will be passed the
    + * {@link goog.net.FileDownloader.Error} object as an errback.
    + *
    + * If a download is already in progress for the given URL, this will return the
    + * deferred blob for that download. If the URL has already been downloaded, this
    + * will fail once it tries to save the downloaded blob.
    + *
    + * When a download is in progress, all Deferreds returned for that download will
    + * be branches of a single parent. If all such branches are cancelled, or if one
    + * is cancelled with opt_deepCancel set, then the download will be cancelled as
    + * well.
    + *
    + * @param {string} url The URL of the file to download.
    + * @return {!goog.async.Deferred} The deferred result blob.
    + */
    +goog.net.FileDownloader.prototype.download = function(url) {
    +  if (this.isDownloading(url)) {
    +    return this.downloads_[url].deferred.branch(true /* opt_propagateCancel */);
    +  }
    +
    +  var download = new goog.net.FileDownloader.Download_(url, this);
    +  this.downloads_[url] = download;
    +  this.pool_.getObject(goog.bind(this.gotXhr_, this, download));
    +  return download.deferred.branch(true /* opt_propagateCancel */);
    +};
    +
    +
    +/**
    + * Return a Deferred that will fire once no download is active for a given URL.
    + * If there's no download active for that URL when this is called, the deferred
    + * will fire immediately; otherwise, it will fire once the download is complete,
    + * whether or not it succeeds.
    + *
    + * @param {string} url The URL of the download to wait for.
    + * @return {!goog.async.Deferred} The Deferred that will fire when the download
    + *     is complete.
    + */
    +goog.net.FileDownloader.prototype.waitForDownload = function(url) {
    +  var deferred = new goog.async.Deferred();
    +  if (this.isDownloading(url)) {
    +    this.downloads_[url].deferred.addBoth(function() {
    +      deferred.callback(null);
    +    }, this);
    +  } else {
    +    deferred.callback(null);
    +  }
    +  return deferred;
    +};
    +
    +
    +/**
    + * Returns whether or not there is an active download for a given URL.
    + *
    + * @param {string} url The URL of the download to check.
    + * @return {boolean} Whether or not there is an active download for the URL.
    + */
    +goog.net.FileDownloader.prototype.isDownloading = function(url) {
    +  return url in this.downloads_;
    +};
    +
    +
    +/**
    + * Load a downloaded blob from the filesystem. Will fire a deferred error if the
    + * given URL has not yet been downloaded.
    + *
    + * @param {string} url The URL of the blob to load.
    + * @return {!goog.async.Deferred} The deferred Blob object. The callback will be
    + *     passed the blob. If a file API error occurs while loading the blob, that
    + *     error will be passed to the errback.
    + */
    +goog.net.FileDownloader.prototype.getDownloadedBlob = function(url) {
    +  return this.getFile_(url).
    +      addCallback(function(fileEntry) { return fileEntry.file(); });
    +};
    +
    +
    +/**
    + * Get the local filesystem: URL for a downloaded file. This is different from
    + * the blob: URL that's available from getDownloadedBlob(). If the end user
    + * accesses the filesystem: URL, the resulting file's name will be determined by
    + * the download filename as opposed to an arbitrary GUID. In addition, the
    + * filesystem: URL is connected to a filesystem location, so if the download is
    + * removed then that URL will become invalid.
    + *
    + * Warning: in Chrome 12, some filesystem: URLs are opened inline. This means
    + * that e.g. HTML pages given to the user via filesystem: URLs will be opened
    + * and processed by the browser.
    + *
    + * @param {string} url The URL of the file to get the URL of.
    + * @return {!goog.async.Deferred} The deferred filesystem: URL. The callback
    + *     will be passed the URL. If a file API error occurs while loading the
    + *     blob, that error will be passed to the errback.
    + */
    +goog.net.FileDownloader.prototype.getLocalUrl = function(url) {
    +  return this.getFile_(url).
    +      addCallback(function(fileEntry) { return fileEntry.toUrl(); });
    +};
    +
    +
    +/**
    + * Return (deferred) whether or not a URL has been downloaded. Will fire a
    + * deferred error if something goes wrong when determining this.
    + *
    + * @param {string} url The URL to check.
    + * @return {!goog.async.Deferred} The deferred boolean. The callback will be
    + *     passed the boolean. If a file API error occurs while checking the
    + *     existence of the downloaded URL, that error will be passed to the
    + *     errback.
    + */
    +goog.net.FileDownloader.prototype.isDownloaded = function(url) {
    +  var deferred = new goog.async.Deferred();
    +  var blobDeferred = this.getDownloadedBlob(url);
    +  blobDeferred.addCallback(function() {
    +    deferred.callback(true);
    +  });
    +  blobDeferred.addErrback(function(err) {
    +    if (err.code == goog.fs.Error.ErrorCode.NOT_FOUND) {
    +      deferred.callback(false);
    +    } else {
    +      deferred.errback(err);
    +    }
    +  });
    +  return deferred;
    +};
    +
    +
    +/**
    + * Remove a URL from the FileDownloader.
    + *
    + * This returns a Deferred. If the removal is completed successfully, its
    + * callback will be called without any value. If the removal fails, its errback
    + * will be called with the {@link goog.fs.Error}.
    + *
    + * @param {string} url The URL to remove.
    + * @return {!goog.async.Deferred} The deferred used for registering callbacks on
    + *     success or on error.
    + */
    +goog.net.FileDownloader.prototype.remove = function(url) {
    +  return this.getDir_(url, goog.fs.DirectoryEntry.Behavior.DEFAULT).
    +      addCallback(function(dir) { return dir.removeRecursively(); });
    +};
    +
    +
    +/**
    + * Save a blob for a given URL. This works just as through the blob were
    + * downloaded form that URL, except you specify the blob and no HTTP request is
    + * made.
    + *
    + * If the URL is currently being downloaded, it's indeterminate whether the blob
    + * being set or the blob being downloaded will end up in the filesystem.
    + * Whichever one doesn't get saved will have an error. To ensure that one or the
    + * other takes precedence, use {@link #waitForDownload} to allow the download to
    + * complete before setting the blob.
    + *
    + * @param {string} url The URL at which to set the blob.
    + * @param {!Blob} blob The blob to set.
    + * @param {string=} opt_name The name of the file. If this isn't given, it's
    + *     determined from the URL.
    + * @return {!goog.async.Deferred} The deferred used for registering callbacks on
    + *     success or on error. This can be cancelled just like a {@link #download}
    + *     Deferred. The objects passed to the errback will be
    + *     {@link goog.net.FileDownloader.Error}s.
    + */
    +goog.net.FileDownloader.prototype.setBlob = function(url, blob, opt_name) {
    +  var name = this.sanitize_(opt_name || this.urlToName_(url));
    +  var download = new goog.net.FileDownloader.Download_(url, this);
    +  this.downloads_[url] = download;
    +  download.blob = blob;
    +  this.getDir_(download.url, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE).
    +      addCallback(function(dir) {
    +        return dir.getFile(
    +            name, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
    +      }).
    +      addCallback(goog.bind(this.fileSuccess_, this, download)).
    +      addErrback(goog.bind(this.error_, this, download));
    +  return download.deferred.branch(true /* opt_propagateCancel */);
    +};
    +
    +
    +/**
    + * The callback called when an XHR becomes available from the XHR pool.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @param {!goog.net.XhrIo} xhr The XhrIo object for downloading the page.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.gotXhr_ = function(download, xhr) {
    +  if (download.cancelled) {
    +    this.freeXhr_(xhr);
    +    return;
    +  }
    +
    +  this.eventHandler_.listen(
    +      xhr, goog.net.EventType.SUCCESS,
    +      goog.bind(this.xhrSuccess_, this, download));
    +  this.eventHandler_.listen(
    +      xhr, [goog.net.EventType.ERROR, goog.net.EventType.ABORT],
    +      goog.bind(this.error_, this, download));
    +  this.eventHandler_.listen(
    +      xhr, goog.net.EventType.READY,
    +      goog.bind(this.freeXhr_, this, xhr));
    +
    +  download.xhr = xhr;
    +  xhr.setResponseType(goog.net.XhrIo.ResponseType.ARRAY_BUFFER);
    +  xhr.send(download.url);
    +};
    +
    +
    +/**
    + * The callback called when an XHR succeeds in downloading a remote file.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.xhrSuccess_ = function(download) {
    +  if (download.cancelled) {
    +    return;
    +  }
    +
    +  var name = this.sanitize_(this.getName_(
    +      /** @type {!goog.net.XhrIo} */ (download.xhr)));
    +  var resp = /** @type {ArrayBuffer} */ (download.xhr.getResponse());
    +  if (!resp) {
    +    // This should never happen - it indicates the XHR hasn't completed, has
    +    // failed or has been cleaned up.  If it does happen (eg. due to a bug
    +    // somewhere) we don't want to pass null to getBlob - it's not valid and
    +    // triggers a bug in some versions of WebKit causing it to crash.
    +    this.error_(download);
    +    return;
    +  }
    +
    +  download.blob = goog.fs.getBlob(resp);
    +  delete download.xhr;
    +
    +  this.getDir_(download.url, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE).
    +      addCallback(function(dir) {
    +        return dir.getFile(
    +            name, goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
    +      }).
    +      addCallback(goog.bind(this.fileSuccess_, this, download)).
    +      addErrback(goog.bind(this.error_, this, download));
    +};
    +
    +
    +/**
    + * The callback called when a file that will be used for saving a file is
    + * successfully opened.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @param {!goog.fs.FileEntry} file The newly-opened file object.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.fileSuccess_ = function(download, file) {
    +  if (download.cancelled) {
    +    file.remove();
    +    return;
    +  }
    +
    +  download.file = file;
    +  file.createWriter().
    +      addCallback(goog.bind(this.fileWriterSuccess_, this, download)).
    +      addErrback(goog.bind(this.error_, this, download));
    +};
    +
    +
    +/**
    + * The callback called when a file writer is succesfully created for writing a
    + * file to the filesystem.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @param {!goog.fs.FileWriter} writer The newly-created file writer object.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.fileWriterSuccess_ = function(
    +    download, writer) {
    +  if (download.cancelled) {
    +    download.file.remove();
    +    return;
    +  }
    +
    +  download.writer = writer;
    +  writer.write(/** @type {!Blob} */ (download.blob));
    +  this.eventHandler_.listenOnce(
    +      writer,
    +      goog.fs.FileSaver.EventType.WRITE_END,
    +      goog.bind(this.writeEnd_, this, download));
    +};
    +
    +
    +/**
    + * The callback called when file writing ends, whether or not it's successful.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.writeEnd_ = function(download) {
    +  if (download.cancelled || download.writer.getError()) {
    +    this.error_(download, download.writer.getError());
    +    return;
    +  }
    +
    +  delete this.downloads_[download.url];
    +  download.deferred.callback(download.blob);
    +};
    +
    +
    +/**
    + * The error callback for all asynchronous operations. Ensures that all stages
    + * of a given download are cleaned up, and emits the error event.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     this download.
    + * @param {goog.fs.Error=} opt_err The file error object. Only defined if the
    + *     error was raised by the file API.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.error_ = function(download, opt_err) {
    +  if (download.file) {
    +    download.file.remove();
    +  }
    +
    +  if (download.cancelled) {
    +    return;
    +  }
    +
    +  delete this.downloads_[download.url];
    +  download.deferred.errback(
    +      new goog.net.FileDownloader.Error(download, opt_err));
    +};
    +
    +
    +/**
    + * Abort the download of the given URL.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download to abort.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.cancel_ = function(download) {
    +  goog.dispose(download);
    +  delete this.downloads_[download.url];
    +};
    +
    +
    +/**
    + * Get the directory for a given URL. If the directory already exists when this
    + * is called, it will contain exactly one file: the downloaded file.
    + *
    + * This not only calls the FileSystem API's getFile method, but attempts to
    + * distribute the files so that they don't overload the filesystem. The spec
    + * says directories can't contain more than 5000 files
    + * (http://www.w3.org/TR/file-system-api/#directories), so this ensures that
    + * each file is put into a subdirectory based on its SHA1 hash.
    + *
    + * All parameters are the same as in the FileSystem API's Entry#getFile method.
    + *
    + * @param {string} url The URL corresponding to the directory to get.
    + * @param {goog.fs.DirectoryEntry.Behavior} behavior The behavior to pass to the
    + *     underlying method.
    + * @return {!goog.async.Deferred} The deferred DirectoryEntry object.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.getDir_ = function(url, behavior) {
    +  // 3 hex digits provide 16**3 = 4096 different possible dirnames, which is
    +  // less than the maximum of 5000 entries. Downloaded files should be
    +  // distributed roughly evenly throughout the directories due to the hash
    +  // function, allowing many more than 5000 files to be downloaded.
    +  //
    +  // The leading ` ensures that no illegal dirnames are accidentally used. % was
    +  // previously used, but Chrome has a bug (as of 12.0.725.0 dev) where
    +  // filenames are URL-decoded before checking their validity, so filenames
    +  // containing e.g. '%3f' (the URL-encoding of :, an invalid character) are
    +  // rejected.
    +  var dirname = '`' + Math.abs(goog.crypt.hash32.encodeString(url)).
    +      toString(16).substring(0, 3);
    +
    +  return this.dir_.
    +      getDirectory(dirname, goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(dir) {
    +        return dir.getDirectory(this.sanitize_(url), behavior);
    +      }, this);
    +};
    +
    +
    +/**
    + * Get the file for a given URL. This will only retrieve files that have already
    + * been saved; it shouldn't be used for creating the file in the first place.
    + * This is because the filename isn't necessarily determined by the URL, but by
    + * the headers of the XHR response.
    + *
    + * @param {string} url The URL corresponding to the file to get.
    + * @return {!goog.async.Deferred} The deferred FileEntry object.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.getFile_ = function(url) {
    +  return this.getDir_(url, goog.fs.DirectoryEntry.Behavior.DEFAULT).
    +      addCallback(function(dir) {
    +        return dir.listDirectory().addCallback(function(files) {
    +          goog.asserts.assert(files.length == 1);
    +          // If the filesystem somehow gets corrupted and we end up with an
    +          // empty directory here, it makes sense to just return the normal
    +          // file-not-found error.
    +          return files[0] || dir.getFile('file');
    +        });
    +      });
    +};
    +
    +
    +/**
    + * Sanitize a string so it can be safely used as a file or directory name for
    + * the FileSystem API.
    + *
    + * @param {string} str The string to sanitize.
    + * @return {string} The sanitized string.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.sanitize_ = function(str) {
    +  // Add a prefix, since certain prefixes are disallowed for paths. None of the
    +  // disallowed prefixes start with '`'. We use ` rather than % for escaping the
    +  // filename due to a Chrome bug (as of 12.0.725.0 dev) where filenames are
    +  // URL-decoded before checking their validity, so filenames containing e.g.
    +  // '%3f' (the URL-encoding of :, an invalid character) are rejected.
    +  return '`' + str.replace(/[\/\\<>:?*"|%`]/g, encodeURIComponent).
    +      replace(/%/g, '`');
    +};
    +
    +
    +/**
    + * Gets the filename specified by the XHR. This first attempts to parse the
    + * Content-Disposition header for a filename and, failing that, falls back on
    + * deriving the filename from the URL.
    + *
    + * @param {!goog.net.XhrIo} xhr The XHR containing the response headers.
    + * @return {string} The filename.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.getName_ = function(xhr) {
    +  var disposition = xhr.getResponseHeader('Content-Disposition');
    +  var match = disposition &&
    +      disposition.match(/^attachment *; *filename="(.*)"$/i);
    +  if (match) {
    +    // The Content-Disposition header allows for arbitrary backslash-escaped
    +    // characters (usually " and \). We want to unescape them before using them
    +    // in the filename.
    +    return match[1].replace(/\\(.)/g, '$1');
    +  }
    +
    +  return this.urlToName_(xhr.getLastUri());
    +};
    +
    +
    +/**
    + * Extracts the basename from a URL.
    + *
    + * @param {string} url The URL.
    + * @return {string} The basename.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.urlToName_ = function(url) {
    +  var segments = url.split('/');
    +  return segments[segments.length - 1];
    +};
    +
    +
    +/**
    + * Remove all event listeners for an XHR and release it back into the pool.
    + *
    + * @param {!goog.net.XhrIo} xhr The XHR to free.
    + * @private
    + */
    +goog.net.FileDownloader.prototype.freeXhr_ = function(xhr) {
    +  goog.events.removeAll(xhr);
    +  this.pool_.addFreeObject(xhr);
    +};
    +
    +
    +/** @override */
    +goog.net.FileDownloader.prototype.disposeInternal = function() {
    +  delete this.dir_;
    +  goog.dispose(this.eventHandler_);
    +  delete this.eventHandler_;
    +  goog.object.forEach(this.downloads_, function(download) {
    +    download.deferred.cancel();
    +  }, this);
    +  delete this.downloads_;
    +  goog.dispose(this.pool_);
    +  delete this.pool_;
    +
    +  goog.net.FileDownloader.base(this, 'disposeInternal');
    +};
    +
    +
    +
    +/**
    + * The error object for FileDownloader download errors.
    + *
    + * @param {!goog.net.FileDownloader.Download_} download The download object for
    + *     the download in question.
    + * @param {goog.fs.Error=} opt_fsErr The file error object, if this was a file
    + *     error.
    + *
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.net.FileDownloader.Error = function(download, opt_fsErr) {
    +  goog.net.FileDownloader.Error.base(
    +      this, 'constructor', 'Error capturing URL ' + download.url);
    +
    +  /**
    +   * The URL the event relates to.
    +   * @type {string}
    +   */
    +  this.url = download.url;
    +
    +  if (download.xhr) {
    +    this.xhrStatus = download.xhr.getStatus();
    +    this.xhrErrorCode = download.xhr.getLastErrorCode();
    +    this.message += ': XHR failed with status ' + this.xhrStatus +
    +        ' (error code ' + this.xhrErrorCode + ')';
    +  } else if (opt_fsErr) {
    +    this.fileError = opt_fsErr;
    +    this.message += ': file API failed (' + opt_fsErr.message + ')';
    +  }
    +};
    +goog.inherits(goog.net.FileDownloader.Error, goog.debug.Error);
    +
    +
    +/**
    + * The status of the XHR. Only set if the error was caused by an XHR failure.
    + * @type {number|undefined}
    + */
    +goog.net.FileDownloader.Error.prototype.xhrStatus;
    +
    +
    +/**
    + * The error code of the XHR. Only set if the error was caused by an XHR
    + * failure.
    + * @type {goog.net.ErrorCode|undefined}
    + */
    +goog.net.FileDownloader.Error.prototype.xhrErrorCode;
    +
    +
    +/**
    + * The file API error. Only set if the error was caused by the file API.
    + * @type {goog.fs.Error|undefined}
    + */
    +goog.net.FileDownloader.Error.prototype.fileError;
    +
    +
    +
    +/**
    + * A struct containing the data for a single download.
    + *
    + * @param {string} url The URL for the file being downloaded.
    + * @param {!goog.net.FileDownloader} downloader The parent FileDownloader.
    + * @extends {goog.Disposable}
    + * @constructor
    + * @private
    + */
    +goog.net.FileDownloader.Download_ = function(url, downloader) {
    +  goog.net.FileDownloader.Download_.base(this, 'constructor');
    +
    +  /**
    +   * The URL for the file being downloaded.
    +   * @type {string}
    +   */
    +  this.url = url;
    +
    +  /**
    +   * The Deferred that will be fired when the download is complete.
    +   * @type {!goog.async.Deferred}
    +   */
    +  this.deferred = new goog.async.Deferred(
    +      goog.bind(downloader.cancel_, downloader, this));
    +
    +  /**
    +   * Whether this download has been cancelled by the user.
    +   * @type {boolean}
    +   */
    +  this.cancelled = false;
    +
    +  /**
    +   * The XhrIo object for downloading the file. Only set once it's been
    +   * retrieved from the pool.
    +   * @type {goog.net.XhrIo}
    +   */
    +  this.xhr = null;
    +
    +  /**
    +   * The name of the blob being downloaded. Only sey once the XHR has completed,
    +   * if it completed successfully.
    +   * @type {?string}
    +   */
    +  this.name = null;
    +
    +  /**
    +   * The downloaded blob. Only set once the XHR has completed, if it completed
    +   * successfully.
    +   * @type {Blob}
    +   */
    +  this.blob = null;
    +
    +  /**
    +   * The file entry where the blob is to be stored. Only set once it's been
    +   * loaded from the filesystem.
    +   * @type {goog.fs.FileEntry}
    +   */
    +  this.file = null;
    +
    +  /**
    +   * The file writer for writing the blob to the filesystem. Only set once it's
    +   * been loaded from the filesystem.
    +   * @type {goog.fs.FileWriter}
    +   */
    +  this.writer = null;
    +};
    +goog.inherits(goog.net.FileDownloader.Download_, goog.Disposable);
    +
    +
    +/** @override */
    +goog.net.FileDownloader.Download_.prototype.disposeInternal = function() {
    +  this.cancelled = true;
    +  if (this.xhr) {
    +    this.xhr.abort();
    +  } else if (this.writer && this.writer.getReadyState() ==
    +             goog.fs.FileSaver.ReadyState.WRITING) {
    +    this.writer.abort();
    +  }
    +
    +  goog.net.FileDownloader.Download_.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.html b/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.html
    new file mode 100644
    index 0000000000..fa72f0c57a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.net.FileDownloader
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.FileDownloaderTest')
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.js b/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.js
    new file mode 100644
    index 0000000000..d92c1f3ee1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/filedownloader_test.js
    @@ -0,0 +1,371 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.FileDownloaderTest');
    +goog.setTestOnly('goog.net.FileDownloaderTest');
    +
    +goog.require('goog.fs.Error');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.FileDownloader');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.fs');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIoPool');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var xhrIoPool, xhr, fs, dir, downloader;
    +
    +function setUpPage() {
    +  goog.testing.fs.install(new goog.testing.PropertyReplacer());
    +}
    +
    +function setUp() {
    +  xhrIoPool = new goog.testing.net.XhrIoPool();
    +  xhr = xhrIoPool.getXhr();
    +  fs = new goog.testing.fs.FileSystem();
    +  dir = fs.getRoot();
    +  downloader = new goog.net.FileDownloader(dir, xhrIoPool);
    +}
    +
    +function tearDown() {
    +  goog.dispose(downloader);
    +}
    +
    +function testDownload() {
    +  downloader.download('/foo/bar').addCallback(function(blob) {
    +    var fileEntry = dir.getFileSync('`3fa/``2Ffoo`2Fbar/`bar');
    +    assertEquals('data', blob.toString());
    +    assertEquals('data', fileEntry.fileSync().toString());
    +    asyncTestCase.continueTesting();
    +  }).addErrback(function(err) { throw err; });
    +
    +  assertEquals('/foo/bar', xhr.getLastUri());
    +  assertEquals(goog.net.XhrIo.ResponseType.ARRAY_BUFFER, xhr.getResponseType());
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testDownload');
    +}
    +
    +function testGetDownloadedBlob() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addCallback(function(blob) { assertEquals('data', blob.toString()); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testGetDownloadedBlob');
    +}
    +
    +function testGetLocalUrl() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() { return downloader.getLocalUrl('/foo/bar'); }).
    +      addCallback(function(url) { assertMatches(/\/`bar$/, url); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testGetLocalUrl');
    +}
    +
    +function testLocalUrlWithContentDisposition() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() { return downloader.getLocalUrl('/foo/bar'); }).
    +      addCallback(function(url) { assertMatches(/\/`qux`22bap$/, url); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  xhr.simulateResponse(
    +      200, 'data', {'Content-Disposition': 'attachment; filename="qux\\"bap"'});
    +  asyncTestCase.waitForAsync('testGetLocalUrl');
    +}
    +
    +function testIsDownloaded() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() { return downloader.isDownloaded('/foo/bar'); }).
    +      addCallback(assertTrue).
    +      addCallback(function() { return downloader.isDownloaded('/foo/baz'); }).
    +      addCallback(assertFalse).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testIsDownloaded');
    +}
    +
    +function testRemove() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() { return downloader.remove('/foo/bar'); }).
    +      addCallback(function() { return downloader.isDownloaded('/foo/bar'); }).
    +      addCallback(assertFalse).
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.NOT_FOUND, err.code);
    +        var download = downloader.download('/foo/bar');
    +        xhr.simulateResponse(200, 'more data');
    +        return download;
    +      }).
    +      addCallback(function() { return downloader.isDownloaded('/foo/bar'); }).
    +      addCallback(assertTrue).
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addCallback(function(blob) {
    +        assertEquals('more data', blob.toString());
    +      }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testRemove');
    +}
    +
    +function testSetBlob() {
    +  downloader.setBlob('/foo/bar', goog.testing.fs.getBlob('data')).
    +      addCallback(function() { return downloader.isDownloaded('/foo/bar'); }).
    +      addCallback(assertTrue).
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addCallback(function(blob) {
    +        assertEquals('data', blob.toString());
    +      }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  asyncTestCase.waitForAsync('testSetBlob');
    +}
    +
    +function testSetBlobWithName() {
    +  downloader.setBlob('/foo/bar', goog.testing.fs.getBlob('data'), 'qux').
    +      addCallback(function() { return downloader.getLocalUrl('/foo/bar'); }).
    +      addCallback(function(url) { assertMatches(/\/`qux$/, url); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase)).
    +      addErrback(function(err) { throw err; });
    +
    +  asyncTestCase.waitForAsync('testSetBlob');
    +}
    +
    +function testDownloadDuringDownload() {
    +  var download1 = downloader.download('/foo/bar');
    +  var download2 = downloader.download('/foo/bar');
    +
    +  download1.
    +      addCallback(function() { return download2; }).
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addCallback(function(blob) { assertEquals('data', blob.toString()); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  // There should only need to be one response for both downloads, since the
    +  // second should return the same deferred as the first.
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testDownloadeduringDownload');
    +}
    +
    +function testGetDownloadedBlobDuringDownload() {
    +  var download = downloader.download('/foo/bar');
    +  downloader.waitForDownload('/foo/bar').addCallback(function() {
    +    return downloader.getDownloadedBlob('/foo/bar');
    +  }).addCallback(function(blob) {
    +    assertTrue(download.hasFired());
    +    assertEquals('data', blob.toString());
    +    asyncTestCase.continueTesting();
    +  });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testGetDownloadedBlobDuringDownload');
    +}
    +
    +function testIsDownloadedDuringDownload() {
    +  var download = downloader.download('/foo/bar');
    +  downloader.waitForDownload('/foo/bar').addCallback(function() {
    +    return downloader.isDownloaded('/foo/bar');
    +  }).addCallback(function(isDownloaded) {
    +    assertTrue(download.hasFired());
    +    assertTrue(isDownloaded);
    +    asyncTestCase.continueTesting();
    +  });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testIsDownloadedDuringDownload');
    +}
    +
    +function testRemoveDuringDownload() {
    +  var download = downloader.download('/foo/bar');
    +  downloader.
    +      waitForDownload('/foo/bar').
    +      addCallback(function() { return downloader.remove('/foo/bar'); }).
    +      addCallback(function() { assertTrue(download.hasFired()); }).
    +      addCallback(function() { return downloader.isDownloaded('/foo/bar'); }).
    +      addCallback(assertFalse).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testRemoveDuringDownload');
    +}
    +
    +function testSetBlobDuringDownload() {
    +  var download = downloader.download('/foo/bar');
    +  downloader.
    +      waitForDownload('/foo/bar').
    +      addCallback(function() {
    +        return downloader.setBlob(
    +            '/foo/bar', goog.testing.fs.getBlob('blob data'));
    +      }).
    +      addErrback(function(err) {
    +        assertEquals(
    +            goog.fs.Error.ErrorCode.INVALID_MODIFICATION, err.fileError.code);
    +        return download;
    +      }).
    +      addCallback(function() {
    +        return downloader.getDownloadedBlob('/foo/bar');
    +      }).
    +      addCallback(function(b) { assertEquals('xhr data', b.toString()); }).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  xhr.simulateResponse(200, 'xhr data');
    +  asyncTestCase.waitForAsync('testSetBlobDuringDownload');
    +}
    +
    +function testDownloadCancelledBeforeXhr() {
    +  var download = downloader.download('/foo/bar');
    +  download.cancel();
    +
    +  download.
    +      addErrback(function() {
    +    assertEquals('/foo/bar', xhr.getLastUri());
    +    assertEquals(goog.net.ErrorCode.ABORT, xhr.getLastErrorCode());
    +    assertFalse(xhr.isActive());
    +
    +    return downloader.isDownloaded('/foo/bar');
    +  }).
    +      addCallback(assertFalse).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  asyncTestCase.waitForAsync('testDownloadCancelledBeforeXhr');
    +}
    +
    +function testDownloadCancelledAfterXhr() {
    +  var download = downloader.download('/foo/bar');
    +  xhr.simulateResponse(200, 'data');
    +  download.cancel();
    +
    +  download.
    +      addErrback(function() {
    +    assertEquals('/foo/bar', xhr.getLastUri());
    +    assertEquals(goog.net.ErrorCode.NO_ERROR, xhr.getLastErrorCode());
    +    assertFalse(xhr.isActive());
    +
    +    return downloader.isDownloaded('/foo/bar');
    +  }).
    +      addCallback(assertFalse).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  asyncTestCase.waitForAsync('testDownloadCancelledAfterXhr');
    +}
    +
    +function testFailedXhr() {
    +  downloader.download('/foo/bar').
    +      addErrback(function(err) {
    +        assertEquals('/foo/bar', err.url);
    +        assertEquals(404, err.xhrStatus);
    +        assertEquals(goog.net.ErrorCode.HTTP_ERROR, err.xhrErrorCode);
    +        assertUndefined(err.fileError);
    +
    +        return downloader.isDownloaded('/foo/bar');
    +      }).
    +      addCallback(assertFalse).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +
    +  xhr.simulateResponse(404);
    +  asyncTestCase.waitForAsync('testFailedXhr');
    +}
    +
    +function testFailedDownloadSave() {
    +  downloader.download('/foo/bar').
    +      addCallback(function() {
    +        var download = downloader.download('/foo/bar');
    +        xhr.simulateResponse(200, 'data');
    +        return download;
    +      }).
    +      addErrback(function(err) {
    +        assertEquals('/foo/bar', err.url);
    +        assertUndefined(err.xhrStatus);
    +        assertUndefined(err.xhrErrorCode);
    +        assertEquals(
    +            goog.fs.Error.ErrorCode.INVALID_MODIFICATION, err.fileError.code);
    +
    +        asyncTestCase.continueTesting();
    +      });
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testFailedDownloadSave');
    +}
    +
    +function testFailedGetDownloadedBlob() {
    +  downloader.getDownloadedBlob('/foo/bar').
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.NOT_FOUND, err.code);
    +        asyncTestCase.continueTesting();
    +      });
    +
    +  asyncTestCase.waitForAsync('testFailedGetDownloadedBlob');
    +}
    +
    +function testFailedRemove() {
    +  downloader.remove('/foo/bar').
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.NOT_FOUND, err.code);
    +        asyncTestCase.continueTesting();
    +      });
    +
    +  asyncTestCase.waitForAsync('testFailedRemove');
    +}
    +
    +function testIsDownloading() {
    +  assertFalse(downloader.isDownloading('/foo/bar'));
    +  downloader.download('/foo/bar').addCallback(function() {
    +    assertFalse(downloader.isDownloading('/foo/bar'));
    +    asyncTestCase.continueTesting();
    +  });
    +
    +  assertTrue(downloader.isDownloading('/foo/bar'));
    +
    +  xhr.simulateResponse(200, 'data');
    +  asyncTestCase.waitForAsync('testIsDownloading');
    +}
    +
    +function testIsDownloadingWhenCancelled() {
    +  assertFalse(downloader.isDownloading('/foo/bar'));
    +  var deferred = downloader.download('/foo/bar').addErrback(function() {
    +    assertFalse(downloader.isDownloading('/foo/bar'));
    +  });
    +
    +  assertTrue(downloader.isDownloading('/foo/bar'));
    +  deferred.cancel();
    +}
    +
    +function assertMatches(expected, actual) {
    +  assert(
    +      'Expected "' + actual + '" to match ' + expected,
    +      expected.test(actual));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/httpstatus.js b/mapbox-gl/closure-library/closure/goog/net/httpstatus.js
    new file mode 100644
    index 0000000000..a525c0fa4f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/httpstatus.js
    @@ -0,0 +1,116 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Constants for HTTP status codes.
    + */
    +
    +goog.provide('goog.net.HttpStatus');
    +
    +
    +/**
    + * HTTP Status Codes defined in RFC 2616 and RFC 6585.
    + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    + * @see http://tools.ietf.org/html/rfc6585
    + * @enum {number}
    + */
    +goog.net.HttpStatus = {
    +  // Informational 1xx
    +  CONTINUE: 100,
    +  SWITCHING_PROTOCOLS: 101,
    +
    +  // Successful 2xx
    +  OK: 200,
    +  CREATED: 201,
    +  ACCEPTED: 202,
    +  NON_AUTHORITATIVE_INFORMATION: 203,
    +  NO_CONTENT: 204,
    +  RESET_CONTENT: 205,
    +  PARTIAL_CONTENT: 206,
    +
    +  // Redirection 3xx
    +  MULTIPLE_CHOICES: 300,
    +  MOVED_PERMANENTLY: 301,
    +  FOUND: 302,
    +  SEE_OTHER: 303,
    +  NOT_MODIFIED: 304,
    +  USE_PROXY: 305,
    +  TEMPORARY_REDIRECT: 307,
    +
    +  // Client Error 4xx
    +  BAD_REQUEST: 400,
    +  UNAUTHORIZED: 401,
    +  PAYMENT_REQUIRED: 402,
    +  FORBIDDEN: 403,
    +  NOT_FOUND: 404,
    +  METHOD_NOT_ALLOWED: 405,
    +  NOT_ACCEPTABLE: 406,
    +  PROXY_AUTHENTICATION_REQUIRED: 407,
    +  REQUEST_TIMEOUT: 408,
    +  CONFLICT: 409,
    +  GONE: 410,
    +  LENGTH_REQUIRED: 411,
    +  PRECONDITION_FAILED: 412,
    +  REQUEST_ENTITY_TOO_LARGE: 413,
    +  REQUEST_URI_TOO_LONG: 414,
    +  UNSUPPORTED_MEDIA_TYPE: 415,
    +  REQUEST_RANGE_NOT_SATISFIABLE: 416,
    +  EXPECTATION_FAILED: 417,
    +  PRECONDITION_REQUIRED: 428,
    +  TOO_MANY_REQUESTS: 429,
    +  REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
    +
    +  // Server Error 5xx
    +  INTERNAL_SERVER_ERROR: 500,
    +  NOT_IMPLEMENTED: 501,
    +  BAD_GATEWAY: 502,
    +  SERVICE_UNAVAILABLE: 503,
    +  GATEWAY_TIMEOUT: 504,
    +  HTTP_VERSION_NOT_SUPPORTED: 505,
    +  NETWORK_AUTHENTICATION_REQUIRED: 511,
    +
    +  /*
    +   * IE returns this code for 204 due to its use of URLMon, which returns this
    +   * code for 'Operation Aborted'. The status text is 'Unknown', the response
    +   * headers are ''. Known to occur on IE 6 on XP through IE9 on Win7.
    +   */
    +  QUIRK_IE_NO_CONTENT: 1223
    +};
    +
    +
    +/**
    + * Returns whether the given status should be considered successful.
    + *
    + * Successful codes are OK (200), CREATED (201), ACCEPTED (202),
    + * NO CONTENT (204), PARTIAL CONTENT (206), NOT MODIFIED (304),
    + * and IE's no content code (1223).
    + *
    + * @param {number} status The status code to test.
    + * @return {boolean} Whether the status code should be considered successful.
    + */
    +goog.net.HttpStatus.isSuccess = function(status) {
    +  switch (status) {
    +    case goog.net.HttpStatus.OK:
    +    case goog.net.HttpStatus.CREATED:
    +    case goog.net.HttpStatus.ACCEPTED:
    +    case goog.net.HttpStatus.NO_CONTENT:
    +    case goog.net.HttpStatus.PARTIAL_CONTENT:
    +    case goog.net.HttpStatus.NOT_MODIFIED:
    +    case goog.net.HttpStatus.QUIRK_IE_NO_CONTENT:
    +      return true;
    +
    +    default:
    +      return false;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.html b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.html
    new file mode 100644
    index 0000000000..b54a22a576
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.html
    @@ -0,0 +1,36 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - Iframe/XHR Execution Context
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.net.iframeXhrTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   XmlHttpRequests that initiate from code executed in an iframe, that is then
    +    destroyed, result in an error in FireFox.  This test case is used to verify
    +    that Closure's IframeIo and XhrLite do not suffer from this problem.  See
    +   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=369939">
    +    https://bugzilla.mozilla.org/show_bug.cgi?id=369939
    +   </a>
    +   .
    +  </p>
    +  <p>
    +   NOTE(pupius): 14/11/2011 The XhrMonitor code has been removed since the
    +    above bug doesn't manifest in any currently supported versions.  This test
    +    is left in place as a way of verifying the problem doesn't resurface.
    +  </p>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.js b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.js
    new file mode 100644
    index 0000000000..7318ea7a97
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test.js
    @@ -0,0 +1,144 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.iframeXhrTest');
    +goog.setTestOnly('goog.net.iframeXhrTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.debug.Console');
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.debug.Logger');
    +goog.require('goog.events');
    +goog.require('goog.net.IframeIo');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var c = new goog.debug.Console;
    +c.setCapturing(true);
    +goog.debug.LogManager.getRoot().setLevel(goog.debug.Logger.Level.ALL);
    +
    +// Can't use exportSymbol if we want JsUnit support
    +top.GG_iframeFn = goog.net.IframeIo.handleIncrementalData;
    +
    +// Make the dispose time short enough that it will cause the bug to appear
    +goog.net.IframeIo.IFRAME_DISPOSE_DELAY_MS = 0;
    +
    +
    +var fileName = 'iframe_xhr_test_response.html';
    +var iframeio;
    +
    +// Create an async test case
    +var testCase = new goog.testing.AsyncTestCase(document.title);
    +testCase.stepTimeout = 4 * 1000;
    +testCase.resultCount = 0;
    +testCase.xhrCount = 0;
    +testCase.error = null;
    +
    +
    +/**
    + * Set up the iframe io and request the test response page.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +testCase.setUpPage = function() {
    +  testCase.waitForAsync('setUpPage');
    +  iframeio = new goog.net.IframeIo();
    +  goog.events.listen(
    +      iframeio, 'incrementaldata', this.onIframeData, false, this);
    +  goog.events.listen(
    +      iframeio, 'ready', this.onIframeReady, false, this);
    +  iframeio.send(fileName);
    +};
    +
    +
    +/** Disposes the iframe object. */
    +testCase.tearDownPage = function() {
    +  iframeio.dispose();
    +};
    +
    +
    +/**
    + * Handles the packets received  from the Iframe incremental results.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +testCase.onIframeData = function(e) {
    +  this.log('Data received  : ' + e.data);
    +  this.resultCount++;
    +  goog.net.XhrIo.send(fileName, goog.bind(this.onXhrData, this));
    +};
    +
    +
    +/**
    + * Handles the iframe becoming ready.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +testCase.onIframeReady = function(e) {
    +  this.log('Iframe ready');
    +  var me = this;
    +  goog.net.XhrIo.send(fileName, goog.bind(this.onXhrData, this));
    +};
    +
    +
    +/**
    + * Handles the response from an Xhr request.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +testCase.onXhrData = function(e) {
    +  this.xhrCount++;
    +  // We access status directly so that XhrLite doesn't mask the error that
    +  // would be thrown in FF if this worked correctly.
    +  try {
    +    this.log('Xhr Received: ' + e.target.xhr_.status);
    +  } catch (e) {
    +    this.log('ERROR: ' + e.message);
    +    this.error = e;
    +  }
    +  if (this.xhrCount == 4 && this.resultCount == 3) {
    +    // Wait for the async iframe disposal to fire.
    +    this.log('Test set up finished, waiting 500ms for iframe disposal');
    +    goog.Timer.callOnce(goog.bind(this.continueTesting, this), 0);
    +  }
    +};
    +
    +
    +/** The main test function that validates the results were as expected. */
    +testCase.addNewTest('testResults', function() {
    +  assertEquals('There should be 3 data packets', 3, this.resultCount);
    +  // 3 results + 1 ready
    +  assertEquals('There should be 4 XHR results', 4, this.xhrCount);
    +  if (this.error) {
    +    throw this.error;
    +  }
    +
    +  assertEquals('There should be no iframes left', 0,
    +      document.getElementsByTagName('iframe').length);
    +});
    +
    +
    +/** This test only runs on GECKO browsers. */
    +if (goog.userAgent.GECKO) {
    +  /** Used by the JsUnit test runner. */
    +  var testXhrMonitorWorksForIframeIoRequests = function() {
    +    testCase.reset();
    +    testCase.cycleTests();
    +  };
    +}
    +
    +// Standalone Closure Test Runner.
    +if (goog.userAgent.GECKO) {
    +  G_testRunner.initialize(testCase);
    +} else {
    +  G_testRunner.setStrict(false);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test_response.html b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test_response.html
    new file mode 100644
    index 0000000000..3dafbefb20
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframe_xhr_test_response.html
    @@ -0,0 +1,17 @@
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +  <head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    +  </head>
    +  <body>
    +    <script>top.GG_iframeFn(window, [1, 1, 2, 4, 8]);</script>
    +    <script>top.GG_iframeFn(window, [12, 20, 32, 52, 84]);</script>
    +    <script>top.GG_iframeFn(window, [136, 220, 356, 576, 932]);</script>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio.js b/mapbox-gl/closure-library/closure/goog/net/iframeio.js
    new file mode 100644
    index 0000000000..456a358aa7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio.js
    @@ -0,0 +1,1372 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for managing requests via iFrames.  Supports a number of
    + * methods of transfer.
    + *
    + * Gets and Posts can be performed and the resultant page read in as text,
    + * JSON, or from the HTML DOM.
    + *
    + * Using an iframe causes the throbber to spin, this is good for providing
    + * feedback to the user that an action has occurred.
    + *
    + * Requests do not affect the history stack, see goog.History if you require
    + * this behavior.
    + *
    + * The responseText and responseJson methods assume the response is plain,
    + * text.  You can access the Iframe's DOM through responseXml if you need
    + * access to the raw HTML.
    + *
    + * Tested:
    + *    + FF2.0 (Win Linux)
    + *    + IE6, IE7
    + *    + Opera 9.1,
    + *    + Chrome
    + *    - Opera 8.5 fails because of no textContent and buggy innerText support
    + *
    + * NOTE: Safari doesn't fire the onload handler when loading plain text files
    + *
    + * This has been tested with Drip in IE to ensure memory usage is as constant
    + * as possible. When making making thousands of requests, memory usage stays
    + * constant for a while but then starts increasing (<500k for 2000
    + * requests) -- this hasn't yet been tracked down yet, though it is cleared up
    + * after a refresh.
    + *
    + *
    + * BACKGROUND FILE UPLOAD:
    + * By posting an arbitrary form through an IframeIo object, it is possible to
    + * implement background file uploads.  Here's how to do it:
    + *
    + * - Create a form:
    + *   <pre>
    + *   &lt;form id="form" enctype="multipart/form-data" method="POST"&gt;
    + *      &lt;input name="userfile" type="file" /&gt;
    + *   &lt;/form&gt;
    + *   </pre>
    + *
    + * - Have the user click the file input
    + * - Create an IframeIo instance
    + *   <pre>
    + *   var io = new goog.net.IframeIo;
    + *   goog.events.listen(io, goog.net.EventType.COMPLETE,
    + *       function() { alert('Sent'); });
    + *   io.sendFromForm(document.getElementById('form'));
    + *   </pre>
    + *
    + *
    + * INCREMENTAL LOADING:
    + * Gmail sends down multiple script blocks which get executed as they are
    + * received by the client. This allows incremental rendering of the thread
    + * list and conversations.
    + *
    + * This requires collaboration with the server that is sending the requested
    + * page back.  To set incremental loading up, you should:
    + *
    + * A) In the application code there should be an externed reference to
    + * <code>handleIncrementalData()</code>.  e.g.
    + * goog.exportSymbol('GG_iframeFn', goog.net.IframeIo.handleIncrementalData);
    + *
    + * B) The response page should them call this method directly, an example
    + * response would look something like this:
    + * <pre>
    + *   &lt;html&gt;
    + *   &lt;head&gt;
    + *     &lt;meta content="text/html;charset=UTF-8" http-equiv="content-type"&gt;
    + *   &lt;/head&gt;
    + *   &lt;body&gt;
    + *     &lt;script&gt;
    + *       D = top.P ? function(d) { top.GG_iframeFn(window, d) } : function() {};
    + *     &lt;/script&gt;
    + *
    + *     &lt;script&gt;D([1, 2, 3, 4, 5]);&lt;/script&gt;
    + *     &lt;script&gt;D([6, 7, 8, 9, 10]);&lt;/script&gt;
    + *     &lt;script&gt;D([11, 12, 13, 14, 15]);&lt;/script&gt;
    + *   &lt;/body&gt;
    + *   &lt;/html&gt;
    + * </pre>
    + *
    + * Your application should then listen, on the IframeIo instance, to the event
    + * goog.net.EventType.INCREMENTAL_DATA.  The event object contains a
    + * 'data' member which is the content from the D() calls above.
    + *
    + * NOTE: There can be problems if you save a reference to the data object in IE.
    + * If you save an array, and the iframe is dispose, then the array looses its
    + * prototype and thus array methods like .join().  You can get around this by
    + * creating arrays using the parent window's Array constructor, or you can
    + * clone the array.
    + *
    + *
    + * EVENT MODEL:
    + * The various send methods work asynchronously. You can be notified about
    + * the current status of the request (completed, success or error) by
    + * listening for events on the IframeIo object itself. The following events
    + * will be sent:
    + * - goog.net.EventType.COMPLETE: when the request is completed
    + *   (either sucessfully or unsuccessfully). You can find out about the result
    + *   using the isSuccess() and getLastError
    + *   methods.
    + * - goog.net.EventType.SUCCESS</code>: when the request was completed
    + *   successfully
    + * - goog.net.EventType.ERROR: when the request failed
    + * - goog.net.EventType.ABORT: when the request has been aborted
    + *
    + * Example:
    + * <pre>
    + * var io = new goog.net.IframeIo();
    + * goog.events.listen(io, goog.net.EventType.COMPLETE,
    + *   function() { alert('request complete'); });
    + * io.sendFromForm(...);
    + * </pre>
    + *
    + */
    +
    +goog.provide('goog.net.IframeIo');
    +goog.provide('goog.net.IframeIo.IncrementalDataEvent');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.Uri');
    +goog.require('goog.asserts');
    +goog.require('goog.debug');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.reflect');
    +goog.require('goog.string');
    +goog.require('goog.structs');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Class for managing requests via iFrames.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.net.IframeIo = function() {
    +  goog.net.IframeIo.base(this, 'constructor');
    +
    +  /**
    +   * Name for this IframeIo and frame
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = goog.net.IframeIo.getNextName_();
    +
    +  /**
    +   * An array of iframes that have been finished with.  We need them to be
    +   * disposed async, so we don't confuse the browser (see below).
    +   * @type {Array<Element>}
    +   * @private
    +   */
    +  this.iframesForDisposal_ = [];
    +
    +  // Create a lookup from names to instances of IframeIo.  This is a helper
    +  // function to be used in conjunction with goog.net.IframeIo.getInstanceByName
    +  // to find the IframeIo object associated with a particular iframe.  Used in
    +  // incremental scripts etc.
    +  goog.net.IframeIo.instances_[this.name_] = this;
    +
    +};
    +goog.inherits(goog.net.IframeIo, goog.events.EventTarget);
    +
    +
    +/**
    + * Object used as a map to lookup instances of IframeIo objects by name.
    + * @type {Object}
    + * @private
    + */
    +goog.net.IframeIo.instances_ = {};
    +
    +
    +/**
    + * Prefix for frame names
    + * @type {string}
    + */
    +goog.net.IframeIo.FRAME_NAME_PREFIX = 'closure_frame';
    +
    +
    +/**
    + * Suffix that is added to inner frames used for sending requests in non-IE
    + * browsers
    + * @type {string}
    + */
    +goog.net.IframeIo.INNER_FRAME_SUFFIX = '_inner';
    +
    +
    +/**
    + * The number of milliseconds after a request is completed to dispose the
    + * iframes.  This can be done lazily so we wait long enough for any processing
    + * that occurred as a result of the response to finish.
    + * @type {number}
    + */
    +goog.net.IframeIo.IFRAME_DISPOSE_DELAY_MS = 2000;
    +
    +
    +/**
    + * Counter used when creating iframes
    + * @type {number}
    + * @private
    + */
    +goog.net.IframeIo.counter_ = 0;
    +
    +
    +/**
    + * Form element to post to.
    + * @type {HTMLFormElement}
    + * @private
    + */
    +goog.net.IframeIo.form_;
    +
    +
    +/**
    + * Static send that creates a short lived instance of IframeIo to send the
    + * request.
    + * @param {goog.Uri|string} uri Uri of the request, it is up the caller to
    + *     manage query string params.
    + * @param {Function=} opt_callback Event handler for when request is completed.
    + * @param {string=} opt_method Default is GET, POST uses a form to submit the
    + *     request.
    + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid
    + *     caching.
    + * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs that
    + *     will be posted to the server via the iframe's form.
    + */
    +goog.net.IframeIo.send = function(
    +    uri, opt_callback, opt_method, opt_noCache, opt_data) {
    +
    +  var io = new goog.net.IframeIo();
    +  goog.events.listen(io, goog.net.EventType.READY, io.dispose, false, io);
    +  if (opt_callback) {
    +    goog.events.listen(io, goog.net.EventType.COMPLETE, opt_callback);
    +  }
    +  io.send(uri, opt_method, opt_noCache, opt_data);
    +};
    +
    +
    +/**
    + * Find an iframe by name (assumes the context is goog.global since that is
    + * where IframeIo's iframes are kept).
    + * @param {string} fname The name to find.
    + * @return {HTMLIFrameElement} The iframe element with that name.
    + */
    +goog.net.IframeIo.getIframeByName = function(fname) {
    +  return window.frames[fname];
    +};
    +
    +
    +/**
    + * Find an instance of the IframeIo object by name.
    + * @param {string} fname The name to find.
    + * @return {goog.net.IframeIo} The instance of IframeIo.
    + */
    +goog.net.IframeIo.getInstanceByName = function(fname) {
    +  return goog.net.IframeIo.instances_[fname];
    +};
    +
    +
    +/**
    + * Handles incremental data and routes it to the correct iframeIo instance.
    + * The HTML page requested by the IframeIo instance should contain script blocks
    + * that call an externed reference to this method.
    + * @param {Window} win The window object.
    + * @param {Object} data The data object.
    + */
    +goog.net.IframeIo.handleIncrementalData = function(win, data) {
    +  // If this is the inner-frame, then we need to use the parent instead.
    +  var iframeName = goog.string.endsWith(win.name,
    +      goog.net.IframeIo.INNER_FRAME_SUFFIX) ? win.parent.name : win.name;
    +
    +  var iframeIoName = iframeName.substring(0, iframeName.lastIndexOf('_'));
    +  var iframeIo = goog.net.IframeIo.getInstanceByName(iframeIoName);
    +  if (iframeIo && iframeName == iframeIo.iframeName_) {
    +    iframeIo.handleIncrementalData_(data);
    +  } else {
    +    var logger = goog.log.getLogger('goog.net.IframeIo');
    +    goog.log.info(logger,
    +        'Incremental iframe data routed for unknown iframe');
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The next iframe name.
    + * @private
    + */
    +goog.net.IframeIo.getNextName_ = function() {
    +  return goog.net.IframeIo.FRAME_NAME_PREFIX + goog.net.IframeIo.counter_++;
    +};
    +
    +
    +/**
    + * Gets a static form, one for all instances of IframeIo since IE6 leaks form
    + * nodes that are created/removed from the document.
    + * @return {!HTMLFormElement} The static form.
    + * @private
    + */
    +goog.net.IframeIo.getForm_ = function() {
    +  if (!goog.net.IframeIo.form_) {
    +    goog.net.IframeIo.form_ =
    +        /** @type {!HTMLFormElement} */(goog.dom.createDom('form'));
    +    goog.net.IframeIo.form_.acceptCharset = 'utf-8';
    +
    +    // Hide the form and move it off screen
    +    var s = goog.net.IframeIo.form_.style;
    +    s.position = 'absolute';
    +    s.visibility = 'hidden';
    +    s.top = s.left = '-10px';
    +    s.width = s.height = '10px';
    +    s.overflow = 'hidden';
    +
    +    goog.dom.getDocument().body.appendChild(goog.net.IframeIo.form_);
    +  }
    +  return goog.net.IframeIo.form_;
    +};
    +
    +
    +/**
    + * Adds the key value pairs from a map like data structure to a form
    + * @param {HTMLFormElement} form The form to add to.
    + * @param {Object|goog.structs.Map|goog.Uri.QueryData} data The data to add.
    + * @private
    + */
    +goog.net.IframeIo.addFormInputs_ = function(form, data) {
    +  var helper = goog.dom.getDomHelper(form);
    +  goog.structs.forEach(data, function(value, key) {
    +    var inp = helper.createDom('input',
    +        {'type': 'hidden', 'name': key, 'value': value});
    +    form.appendChild(inp);
    +  });
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we can use readyState to monitor iframe loading.
    + * @private
    + */
    +goog.net.IframeIo.useIeReadyStateCodePath_ = function() {
    +  // ReadyState is only available on iframes up to IE10.
    +  return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('11');
    +};
    +
    +
    +/**
    + * Reference to a logger for the IframeIo objects
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.IframeIo.prototype.logger_ =
    +    goog.log.getLogger('goog.net.IframeIo');
    +
    +
    +/**
    + * Reference to form element that gets reused for requests to the iframe.
    + * @type {HTMLFormElement}
    + * @private
    + */
    +goog.net.IframeIo.prototype.form_ = null;
    +
    +
    +/**
    + * Reference to the iframe being used for the current request, or null if no
    + * request is currently active.
    + * @type {HTMLIFrameElement}
    + * @private
    + */
    +goog.net.IframeIo.prototype.iframe_ = null;
    +
    +
    +/**
    + * Name of the iframe being used for the current request, or null if no
    + * request is currently active.
    + * @type {?string}
    + * @private
    + */
    +goog.net.IframeIo.prototype.iframeName_ = null;
    +
    +
    +/**
    + * Next id so that iframe names are unique.
    + * @type {number}
    + * @private
    + */
    +goog.net.IframeIo.prototype.nextIframeId_ = 0;
    +
    +
    +/**
    + * Whether the object is currently active with a request.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.IframeIo.prototype.active_ = false;
    +
    +
    +/**
    + * Whether the last request is complete.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.IframeIo.prototype.complete_ = false;
    +
    +
    +/**
    + * Whether the last request was a success.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.IframeIo.prototype.success_ = false;
    +
    +
    +/**
    + * The URI for the last request.
    + * @type {goog.Uri}
    + * @private
    + */
    +goog.net.IframeIo.prototype.lastUri_ = null;
    +
    +
    +/**
    + * The text content of the last request.
    + * @type {?string}
    + * @private
    + */
    +goog.net.IframeIo.prototype.lastContent_ = null;
    +
    +
    +/**
    + * Last error code
    + * @type {goog.net.ErrorCode}
    + * @private
    + */
    +goog.net.IframeIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +
    +
    +/**
    + * Window timeout ID used to cancel the timeout event handler if the request
    + * completes successfully.
    + * @type {?number}
    + * @private
    + */
    +goog.net.IframeIo.prototype.timeoutId_ = null;
    +
    +
    +/**
    + * Window timeout ID used to detect when firefox silently fails.
    + * @type {?number}
    + * @private
    + */
    +goog.net.IframeIo.prototype.firefoxSilentErrorTimeout_ = null;
    +
    +
    +/**
    + * Window timeout ID used by the timer that disposes the iframes.
    + * @type {?number}
    + * @private
    + */
    +goog.net.IframeIo.prototype.iframeDisposalTimer_ = null;
    +
    +
    +/**
    + * This is used to ensure that we don't handle errors twice for the same error.
    + * We can reach the {@link #handleError_} method twice in IE if the form is
    + * submitted while IE is offline and the URL is not available.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.IframeIo.prototype.errorHandled_;
    +
    +
    +/**
    + * Whether to suppress the listeners that determine when the iframe loads.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.IframeIo.prototype.ignoreResponse_ = false;
    +
    +
    +/** @private {Function} */
    +goog.net.IframeIo.prototype.errorChecker_;
    +
    +
    +/** @private {Object} */
    +goog.net.IframeIo.prototype.lastCustomError_;
    +
    +
    +/** @private {?string} */
    +goog.net.IframeIo.prototype.lastContentHtml_;
    +
    +
    +/**
    + * Sends a request via an iframe.
    + *
    + * A HTML form is used and submitted to the iframe, this simplifies the
    + * difference between GET and POST requests. The iframe needs to be created and
    + * destroyed for each request otherwise the request will contribute to the
    + * history stack.
    + *
    + * sendFromForm does some clever trickery (thanks jlim) in non-IE browsers to
    + * stop a history entry being added for POST requests.
    + *
    + * @param {goog.Uri|string} uri Uri of the request.
    + * @param {string=} opt_method Default is GET, POST uses a form to submit the
    + *     request.
    + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid
    + *     caching.
    + * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs.
    + */
    +goog.net.IframeIo.prototype.send = function(
    +    uri, opt_method, opt_noCache, opt_data) {
    +
    +  if (this.active_) {
    +    throw Error('[goog.net.IframeIo] Unable to send, already active.');
    +  }
    +
    +  var uriObj = new goog.Uri(uri);
    +  this.lastUri_ = uriObj;
    +  var method = opt_method ? opt_method.toUpperCase() : 'GET';
    +
    +  if (opt_noCache) {
    +    uriObj.makeUnique();
    +  }
    +
    +  goog.log.info(this.logger_,
    +      'Sending iframe request: ' + uriObj + ' [' + method + ']');
    +
    +  // Build a form for this request
    +  this.form_ = goog.net.IframeIo.getForm_();
    +
    +  if (method == 'GET') {
    +    // For GET requests, we assume that the caller didn't want the queryparams
    +    // already specified in the URI to be clobbered by the form, so we add the
    +    // params here.
    +    goog.net.IframeIo.addFormInputs_(this.form_, uriObj.getQueryData());
    +  }
    +
    +  if (opt_data) {
    +    // Create form fields for each of the data values
    +    goog.net.IframeIo.addFormInputs_(this.form_, opt_data);
    +  }
    +
    +  // Set the URI that the form will be posted
    +  this.form_.action = uriObj.toString();
    +  this.form_.method = method;
    +
    +  this.sendFormInternal_();
    +  this.clearForm_();
    +};
    +
    +
    +/**
    + * Sends the data stored in an existing form to the server. The HTTP method
    + * should be specified on the form, the action can also be specified but can
    + * be overridden by the optional URI param.
    + *
    + * This can be used in conjunction will a file-upload input to upload a file in
    + * the background without affecting history.
    + *
    + * Example form:
    + * <pre>
    + *   &lt;form action="/server/" enctype="multipart/form-data" method="POST"&gt;
    + *     &lt;input name="userfile" type="file"&gt;
    + *   &lt;/form&gt;
    + * </pre>
    + *
    + * @param {HTMLFormElement} form Form element used to send the request to the
    + *     server.
    + * @param {string=} opt_uri Uri to set for the destination of the request, by
    + *     default the uri will come from the form.
    + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid
    + *     caching.
    + */
    +goog.net.IframeIo.prototype.sendFromForm = function(form, opt_uri,
    +    opt_noCache) {
    +  if (this.active_) {
    +    throw Error('[goog.net.IframeIo] Unable to send, already active.');
    +  }
    +
    +  var uri = new goog.Uri(opt_uri || form.action);
    +  if (opt_noCache) {
    +    uri.makeUnique();
    +  }
    +
    +  goog.log.info(this.logger_, 'Sending iframe request from form: ' + uri);
    +
    +  this.lastUri_ = uri;
    +  this.form_ = form;
    +  this.form_.action = uri.toString();
    +  this.sendFormInternal_();
    +};
    +
    +
    +/**
    + * Abort the current Iframe request
    + * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
    + *     defaults to ABORT.
    + */
    +goog.net.IframeIo.prototype.abort = function(opt_failureCode) {
    +  if (this.active_) {
    +    goog.log.info(this.logger_, 'Request aborted');
    +    var requestIframe = this.getRequestIframe();
    +    goog.asserts.assert(requestIframe);
    +    goog.events.removeAll(requestIframe);
    +    this.complete_ = false;
    +    this.active_ = false;
    +    this.success_ = false;
    +    this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
    +
    +    this.dispatchEvent(goog.net.EventType.ABORT);
    +
    +    this.makeReady_();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.net.IframeIo.prototype.disposeInternal = function() {
    +  goog.log.fine(this.logger_, 'Disposing iframeIo instance');
    +
    +  // If there is an active request, abort it
    +  if (this.active_) {
    +    goog.log.fine(this.logger_, 'Aborting active request');
    +    this.abort();
    +  }
    +
    +  // Call super-classes implementation (remove listeners)
    +  goog.net.IframeIo.superClass_.disposeInternal.call(this);
    +
    +  // Add the current iframe to the list of iframes for disposal.
    +  if (this.iframe_) {
    +    this.scheduleIframeDisposal_();
    +  }
    +
    +  // Disposes of the form
    +  this.disposeForm_();
    +
    +  // Nullify anything that might cause problems and clear state
    +  delete this.errorChecker_;
    +  this.form_ = null;
    +  this.lastCustomError_ = this.lastContent_ = this.lastContentHtml_ = null;
    +  this.lastUri_ = null;
    +  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +
    +  delete goog.net.IframeIo.instances_[this.name_];
    +};
    +
    +
    +/**
    + * @return {boolean} True if transfer is complete.
    + */
    +goog.net.IframeIo.prototype.isComplete = function() {
    +  return this.complete_;
    +};
    +
    +
    +/**
    + * @return {boolean} True if transfer was successful.
    + */
    +goog.net.IframeIo.prototype.isSuccess = function() {
    +  return this.success_;
    +};
    +
    +
    +/**
    + * @return {boolean} True if a transfer is in progress.
    + */
    +goog.net.IframeIo.prototype.isActive = function() {
    +  return this.active_;
    +};
    +
    +
    +/**
    + * Returns the last response text (i.e. the text content of the iframe).
    + * Assumes plain text!
    + * @return {?string} Result from the server.
    + */
    +goog.net.IframeIo.prototype.getResponseText = function() {
    +  return this.lastContent_;
    +};
    +
    +
    +/**
    + * Returns the last response html (i.e. the innerHtml of the iframe).
    + * @return {?string} Result from the server.
    + */
    +goog.net.IframeIo.prototype.getResponseHtml = function() {
    +  return this.lastContentHtml_;
    +};
    +
    +
    +/**
    + * Parses the content as JSON. This is a safe parse and may throw an error
    + * if the response is malformed.
    + * Use goog.json.unsafeparse(this.getResponseText()) if you are sure of the
    + * state of the returned content.
    + * @return {Object} The parsed content.
    + */
    +goog.net.IframeIo.prototype.getResponseJson = function() {
    +  return goog.json.parse(this.lastContent_);
    +};
    +
    +
    +/**
    + * Returns the document object from the last request.  Not truely XML, but
    + * used to mirror the XhrIo interface.
    + * @return {HTMLDocument} The document object from the last request.
    + */
    +goog.net.IframeIo.prototype.getResponseXml = function() {
    +  if (!this.iframe_) return null;
    +
    +  return this.getContentDocument_();
    +};
    +
    +
    +/**
    + * Get the uri of the last request.
    + * @return {goog.Uri} Uri of last request.
    + */
    +goog.net.IframeIo.prototype.getLastUri = function() {
    +  return this.lastUri_;
    +};
    +
    +
    +/**
    + * Gets the last error code.
    + * @return {goog.net.ErrorCode} Last error code.
    + */
    +goog.net.IframeIo.prototype.getLastErrorCode = function() {
    +  return this.lastErrorCode_;
    +};
    +
    +
    +/**
    + * Gets the last error message.
    + * @return {string} Last error message.
    + */
    +goog.net.IframeIo.prototype.getLastError = function() {
    +  return goog.net.ErrorCode.getDebugMessage(this.lastErrorCode_);
    +};
    +
    +
    +/**
    + * Gets the last custom error.
    + * @return {Object} Last custom error.
    + */
    +goog.net.IframeIo.prototype.getLastCustomError = function() {
    +  return this.lastCustomError_;
    +};
    +
    +
    +/**
    + * Sets the callback function used to check if a loaded IFrame is in an error
    + * state.
    + * @param {Function} fn Callback that expects a document object as it's single
    + *     argument.
    + */
    +goog.net.IframeIo.prototype.setErrorChecker = function(fn) {
    +  this.errorChecker_ = fn;
    +};
    +
    +
    +/**
    + * Gets the callback function used to check if a loaded IFrame is in an error
    + * state.
    + * @return {Function} A callback that expects a document object as it's single
    + *     argument.
    + */
    +goog.net.IframeIo.prototype.getErrorChecker = function() {
    +  return this.errorChecker_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the server response is being ignored.
    + */
    +goog.net.IframeIo.prototype.isIgnoringResponse = function() {
    +  return this.ignoreResponse_;
    +};
    +
    +
    +/**
    + * Sets whether to ignore the response from the server by not adding any event
    + * handlers to fire when the iframe loads. This is necessary when using IframeIo
    + * to submit to a server on another domain, to avoid same-origin violations when
    + * trying to access the response. If this is set to true, the IframeIo instance
    + * will be a single-use instance that is only usable for one request.  It will
    + * only clean up its resources (iframes and forms) when it is disposed.
    + * @param {boolean} ignore Whether to ignore the server response.
    + */
    +goog.net.IframeIo.prototype.setIgnoreResponse = function(ignore) {
    +  this.ignoreResponse_ = ignore;
    +};
    +
    +
    +/**
    + * Submits the internal form to the iframe.
    + * @private
    + */
    +goog.net.IframeIo.prototype.sendFormInternal_ = function() {
    +  this.active_ = true;
    +  this.complete_ = false;
    +  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +
    +  // Make Iframe
    +  this.createIframe_();
    +
    +  if (goog.net.IframeIo.useIeReadyStateCodePath_()) {
    +    // In IE<11 we simply create the frame, wait until it is ready, then post
    +    // the form to the iframe and wait for the readystate to change to
    +    // 'complete'
    +
    +    // Set the target to the iframe's name
    +    this.form_.target = this.iframeName_ || '';
    +    this.appendIframe_();
    +    if (!this.ignoreResponse_) {
    +      goog.events.listen(this.iframe_, goog.events.EventType.READYSTATECHANGE,
    +          this.onIeReadyStateChange_, false, this);
    +    }
    +
    +    /** @preserveTry */
    +    try {
    +      this.errorHandled_ = false;
    +      this.form_.submit();
    +    } catch (e) {
    +      // If submit threw an exception then it probably means the page that the
    +      // code is running on the local file system and the form's action was
    +      // pointing to a file that doesn't exist, causing the browser to fire an
    +      // exception.  IE also throws an exception when it is working offline and
    +      // the URL is not available.
    +
    +      if (!this.ignoreResponse_) {
    +        goog.events.unlisten(
    +            this.iframe_,
    +            goog.events.EventType.READYSTATECHANGE,
    +            this.onIeReadyStateChange_,
    +            false,
    +            this);
    +      }
    +
    +      this.handleError_(goog.net.ErrorCode.ACCESS_DENIED);
    +    }
    +
    +  } else {
    +    // For all other browsers we do some trickery to ensure that there is no
    +    // entry on the history stack. Thanks go to jlim for the prototype for this
    +
    +    goog.log.fine(this.logger_, 'Setting up iframes and cloning form');
    +
    +    this.appendIframe_();
    +
    +    var innerFrameName = this.iframeName_ +
    +                         goog.net.IframeIo.INNER_FRAME_SUFFIX;
    +
    +    // Open and document.write another iframe into the iframe
    +    var doc = goog.dom.getFrameContentDocument(this.iframe_);
    +    var html = '<body><iframe id=' + innerFrameName +
    +               ' name=' + innerFrameName + '></iframe>';
    +    if (document.baseURI) {
    +      // On Safari 4 and 5 the new iframe doesn't inherit the current baseURI.
    +      html = '<head><base href="' + goog.string.htmlEscape(document.baseURI) +
    +             '"></head>' + html;
    +    }
    +    if (goog.userAgent.OPERA) {
    +      // Opera adds a history entry when document.write is used.
    +      // Change the innerHTML of the page instead.
    +      doc.documentElement.innerHTML = html;
    +    } else {
    +      doc.write(html);
    +    }
    +
    +    // Listen for the iframe's load
    +    if (!this.ignoreResponse_) {
    +      goog.events.listen(doc.getElementById(innerFrameName),
    +          goog.events.EventType.LOAD, this.onIframeLoaded_, false, this);
    +    }
    +
    +    // Fix text areas, since importNode won't clone changes to the value
    +    var textareas = this.form_.getElementsByTagName('textarea');
    +    for (var i = 0, n = textareas.length; i < n; i++) {
    +      // The childnodes represent the initial child nodes for the text area
    +      // appending a text node essentially resets the initial value ready for
    +      // it to be clones - while maintaining HTML escaping.
    +      var value = textareas[i].value;
    +      if (goog.dom.getRawTextContent(textareas[i]) != value) {
    +        goog.dom.setTextContent(textareas[i], value);
    +        textareas[i].value = value;
    +      }
    +    }
    +
    +    // Append a cloned form to the iframe
    +    var clone = doc.importNode(this.form_, true);
    +    clone.target = innerFrameName;
    +    // Work around crbug.com/66987
    +    clone.action = this.form_.action;
    +    doc.body.appendChild(clone);
    +
    +    // Fix select boxes, importNode won't override the default value
    +    var selects = this.form_.getElementsByTagName('select');
    +    var clones = clone.getElementsByTagName('select');
    +    for (var i = 0, n = selects.length; i < n; i++) {
    +      var selectsOptions = selects[i].getElementsByTagName('option');
    +      var clonesOptions = clones[i].getElementsByTagName('option');
    +      for (var j = 0, m = selectsOptions.length; j < m; j++) {
    +        clonesOptions[j].selected = selectsOptions[j].selected;
    +      }
    +    }
    +
    +    // IE and some versions of Firefox (1.5 - 1.5.07?) fail to clone the value
    +    // attribute for <input type="file"> nodes, which results in an empty
    +    // upload if the clone is submitted.  Check, and if the clone failed, submit
    +    // using the original form instead.
    +    var inputs = this.form_.getElementsByTagName('input');
    +    var inputClones = clone.getElementsByTagName('input');
    +    for (var i = 0, n = inputs.length; i < n; i++) {
    +      if (inputs[i].type == 'file') {
    +        if (inputs[i].value != inputClones[i].value) {
    +          goog.log.fine(this.logger_,
    +              'File input value not cloned properly.  Will ' +
    +              'submit using original form.');
    +          this.form_.target = innerFrameName;
    +          clone = this.form_;
    +          break;
    +        }
    +      }
    +    }
    +
    +    goog.log.fine(this.logger_, 'Submitting form');
    +
    +    /** @preserveTry */
    +    try {
    +      this.errorHandled_ = false;
    +      clone.submit();
    +      doc.close();
    +
    +      if (goog.userAgent.GECKO) {
    +        // This tests if firefox silently fails, this can happen, for example,
    +        // when the server resets the connection because of a large file upload
    +        this.firefoxSilentErrorTimeout_ =
    +            goog.Timer.callOnce(this.testForFirefoxSilentError_, 250, this);
    +      }
    +
    +    } catch (e) {
    +      // If submit threw an exception then it probably means the page that the
    +      // code is running on the local file system and the form's action was
    +      // pointing to a file that doesn't exist, causing the browser to fire an
    +      // exception.
    +
    +      goog.log.error(this.logger_,
    +          'Error when submitting form: ' + goog.debug.exposeException(e));
    +
    +      if (!this.ignoreResponse_) {
    +        goog.events.unlisten(doc.getElementById(innerFrameName),
    +            goog.events.EventType.LOAD, this.onIframeLoaded_, false, this);
    +      }
    +
    +      doc.close();
    +
    +      this.handleError_(goog.net.ErrorCode.FILE_NOT_FOUND);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles the load event of the iframe for IE, determines if the request was
    + * successful or not, handles clean up and dispatching of appropriate events.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.net.IframeIo.prototype.onIeReadyStateChange_ = function(e) {
    +  if (this.iframe_.readyState == 'complete') {
    +    goog.events.unlisten(this.iframe_, goog.events.EventType.READYSTATECHANGE,
    +        this.onIeReadyStateChange_, false, this);
    +    var doc;
    +    /** @preserveTry */
    +    try {
    +      doc = goog.dom.getFrameContentDocument(this.iframe_);
    +
    +      // IE serves about:blank when it cannot load the resource while offline.
    +      if (goog.userAgent.IE && doc.location == 'about:blank' &&
    +          !navigator.onLine) {
    +        this.handleError_(goog.net.ErrorCode.OFFLINE);
    +        return;
    +      }
    +    } catch (ex) {
    +      this.handleError_(goog.net.ErrorCode.ACCESS_DENIED);
    +      return;
    +    }
    +    this.handleLoad_(/** @type {!HTMLDocument} */(doc));
    +  }
    +};
    +
    +
    +/**
    + * Handles the load event of the iframe for non-IE browsers.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.net.IframeIo.prototype.onIframeLoaded_ = function(e) {
    +  // In Opera, the default "about:blank" page of iframes fires an onload
    +  // event that we'd like to ignore.
    +  if (goog.userAgent.OPERA &&
    +      this.getContentDocument_().location == 'about:blank') {
    +    return;
    +  }
    +  goog.events.unlisten(this.getRequestIframe(),
    +      goog.events.EventType.LOAD, this.onIframeLoaded_, false, this);
    +  try {
    +    this.handleLoad_(this.getContentDocument_());
    +  } catch (ex) {
    +    this.handleError_(goog.net.ErrorCode.ACCESS_DENIED);
    +  }
    +};
    +
    +
    +/**
    + * Handles generic post-load
    + * @param {HTMLDocument} contentDocument The frame's document.
    + * @private
    + */
    +goog.net.IframeIo.prototype.handleLoad_ = function(contentDocument) {
    +  goog.log.fine(this.logger_, 'Iframe loaded');
    +
    +  this.complete_ = true;
    +  this.active_ = false;
    +
    +  var errorCode;
    +
    +  // Try to get the innerHTML.  If this fails then it can be an access denied
    +  // error or the document may just not have a body, typical case is if there
    +  // is an IE's default 404.
    +  /** @preserveTry */
    +  try {
    +    var body = contentDocument.body;
    +    this.lastContent_ = body.textContent || body.innerText;
    +    this.lastContentHtml_ = body.innerHTML;
    +  } catch (ex) {
    +    errorCode = goog.net.ErrorCode.ACCESS_DENIED;
    +  }
    +
    +  // Use a callback function, defined by the application, to analyse the
    +  // contentDocument and determine if it is an error page.  Applications
    +  // may send down markers in the document, define JS vars, or some other test.
    +  var customError;
    +  if (!errorCode && typeof this.errorChecker_ == 'function') {
    +    customError = this.errorChecker_(contentDocument);
    +    if (customError) {
    +      errorCode = goog.net.ErrorCode.CUSTOM_ERROR;
    +    }
    +  }
    +
    +  goog.log.log(this.logger_, goog.log.Level.FINER,
    +      'Last content: ' + this.lastContent_);
    +  goog.log.log(this.logger_, goog.log.Level.FINER,
    +      'Last uri: ' + this.lastUri_);
    +
    +  if (errorCode) {
    +    goog.log.fine(this.logger_, 'Load event occurred but failed');
    +    this.handleError_(errorCode, customError);
    +
    +  } else {
    +    goog.log.fine(this.logger_, 'Load succeeded');
    +    this.success_ = true;
    +    this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +    this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    this.dispatchEvent(goog.net.EventType.SUCCESS);
    +
    +    this.makeReady_();
    +  }
    +};
    +
    +
    +/**
    + * Handles errors.
    + * @param {goog.net.ErrorCode} errorCode Error code.
    + * @param {Object=} opt_customError If error is CUSTOM_ERROR, this is the
    + *     client-provided custom error.
    + * @private
    + */
    +goog.net.IframeIo.prototype.handleError_ = function(errorCode,
    +                                                    opt_customError) {
    +  if (!this.errorHandled_) {
    +    this.success_ = false;
    +    this.active_ = false;
    +    this.complete_ = true;
    +    this.lastErrorCode_ = errorCode;
    +    if (errorCode == goog.net.ErrorCode.CUSTOM_ERROR) {
    +      goog.asserts.assert(goog.isDef(opt_customError));
    +      this.lastCustomError_ = opt_customError;
    +    }
    +    this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    this.dispatchEvent(goog.net.EventType.ERROR);
    +
    +    this.makeReady_();
    +
    +    this.errorHandled_ = true;
    +  }
    +};
    +
    +
    +/**
    + * Dispatches an event indicating that the IframeIo instance has received a data
    + * packet via incremental loading.  The event object has a 'data' member.
    + * @param {Object} data Data.
    + * @private
    + */
    +goog.net.IframeIo.prototype.handleIncrementalData_ = function(data) {
    +  this.dispatchEvent(new goog.net.IframeIo.IncrementalDataEvent(data));
    +};
    +
    +
    +/**
    + * Finalizes the request, schedules the iframe for disposal, and maybe disposes
    + * the form.
    + * @private
    + */
    +goog.net.IframeIo.prototype.makeReady_ = function() {
    +  goog.log.info(this.logger_, 'Ready for new requests');
    +  var iframe = this.iframe_;
    +  this.scheduleIframeDisposal_();
    +  this.disposeForm_();
    +  this.dispatchEvent(goog.net.EventType.READY);
    +};
    +
    +
    +/**
    + * Creates an iframe to be used with a request.  We use a new iframe for each
    + * request so that requests don't create history entries.
    + * @private
    + */
    +goog.net.IframeIo.prototype.createIframe_ = function() {
    +  goog.log.fine(this.logger_, 'Creating iframe');
    +
    +  this.iframeName_ = this.name_ + '_' + (this.nextIframeId_++).toString(36);
    +
    +  var iframeAttributes = {'name': this.iframeName_, 'id': this.iframeName_};
    +  // Setting the source to javascript:"" is a fix to remove IE6 mixed content
    +  // warnings when being used in an https page.
    +  if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    +    iframeAttributes.src = 'javascript:""';
    +  }
    +
    +  this.iframe_ = /** @type {!HTMLIFrameElement} */(
    +      goog.dom.getDomHelper(this.form_).createDom('iframe', iframeAttributes));
    +
    +  var s = this.iframe_.style;
    +  s.visibility = 'hidden';
    +  s.width = s.height = '10px';
    +  // Chrome sometimes shows scrollbars when visibility is hidden, but not when
    +  // display is none.
    +  s.display = 'none';
    +
    +  // There are reports that safari 2.0.3 has a bug where absolutely positioned
    +  // iframes can't have their src set.
    +  if (!goog.userAgent.WEBKIT) {
    +    s.position = 'absolute';
    +    s.top = s.left = '-10px';
    +  } else {
    +    s.marginTop = s.marginLeft = '-10px';
    +  }
    +};
    +
    +
    +/**
    + * Appends the Iframe to the document body.
    + * @private
    + */
    +goog.net.IframeIo.prototype.appendIframe_ = function() {
    +  goog.dom.getDomHelper(this.form_).getDocument().body.appendChild(
    +      this.iframe_);
    +};
    +
    +
    +/**
    + * Schedules an iframe for disposal, async.  We can't remove the iframes in the
    + * same execution context as the response, otherwise some versions of Firefox
    + * will not detect that the response has correctly finished and the loading bar
    + * will stay active forever.
    + * @private
    + */
    +goog.net.IframeIo.prototype.scheduleIframeDisposal_ = function() {
    +  var iframe = this.iframe_;
    +
    +  // There shouldn't be a case where the iframe is null and we get to this
    +  // stage, but the error reports in http://b/909448 indicate it is possible.
    +  if (iframe) {
    +    // NOTE(user): Stops Internet Explorer leaking the iframe object. This
    +    // shouldn't be needed, since the events have all been removed, which
    +    // should in theory clean up references.  Oh well...
    +    iframe.onreadystatechange = null;
    +    iframe.onload = null;
    +    iframe.onerror = null;
    +
    +    this.iframesForDisposal_.push(iframe);
    +  }
    +
    +  if (this.iframeDisposalTimer_) {
    +    goog.Timer.clear(this.iframeDisposalTimer_);
    +    this.iframeDisposalTimer_ = null;
    +  }
    +
    +  if (goog.userAgent.GECKO || goog.userAgent.OPERA) {
    +    // For FF and Opera, we must dispose the iframe async,
    +    // but it doesn't need to be done as soon as possible.
    +    // We therefore schedule it for 2s out, so as not to
    +    // affect any other actions that may have been triggered by the request.
    +    this.iframeDisposalTimer_ = goog.Timer.callOnce(
    +        this.disposeIframes_, goog.net.IframeIo.IFRAME_DISPOSE_DELAY_MS, this);
    +
    +  } else {
    +    // For non-Gecko browsers we dispose straight away.
    +    this.disposeIframes_();
    +  }
    +
    +  // Nullify reference
    +  this.iframe_ = null;
    +  this.iframeName_ = null;
    +};
    +
    +
    +/**
    + * Disposes any iframes.
    + * @private
    + */
    +goog.net.IframeIo.prototype.disposeIframes_ = function() {
    +  if (this.iframeDisposalTimer_) {
    +    // Clear the timer
    +    goog.Timer.clear(this.iframeDisposalTimer_);
    +    this.iframeDisposalTimer_ = null;
    +  }
    +
    +  while (this.iframesForDisposal_.length != 0) {
    +    var iframe = this.iframesForDisposal_.pop();
    +    goog.log.info(this.logger_, 'Disposing iframe');
    +    goog.dom.removeNode(iframe);
    +  }
    +};
    +
    +
    +/**
    + * Removes all the child nodes from the static form so it can be reused again.
    + * This should happen right after sending a request. Otherwise, there can be
    + * issues when another iframe uses this form right after the first iframe.
    + * @private
    + */
    +goog.net.IframeIo.prototype.clearForm_ = function() {
    +  if (this.form_ && this.form_ == goog.net.IframeIo.form_) {
    +    goog.dom.removeChildren(this.form_);
    +  }
    +};
    +
    +
    +/**
    + * Disposes of the Form.  Since IE6 leaks form nodes, this just cleans up the
    + * DOM and nullifies the instances reference so the form can be used for another
    + * request.
    + * @private
    + */
    +goog.net.IframeIo.prototype.disposeForm_ = function() {
    +  this.clearForm_();
    +  this.form_ = null;
    +};
    +
    +
    +/**
    + * @return {HTMLDocument} The appropriate content document.
    + * @private
    + */
    +goog.net.IframeIo.prototype.getContentDocument_ = function() {
    +  if (this.iframe_) {
    +    return /** @type {!HTMLDocument} */(goog.dom.getFrameContentDocument(
    +        this.getRequestIframe()));
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @return {HTMLIFrameElement} The appropriate iframe to use for requests
    + *     (created in sendForm_).
    + */
    +goog.net.IframeIo.prototype.getRequestIframe = function() {
    +  if (this.iframe_) {
    +    return /** @type {HTMLIFrameElement} */(
    +        goog.net.IframeIo.useIeReadyStateCodePath_() ?
    +            this.iframe_ :
    +            goog.dom.getFrameContentDocument(this.iframe_).getElementById(
    +                this.iframeName_ + goog.net.IframeIo.INNER_FRAME_SUFFIX));
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Tests for a silent failure by firefox that can occur when the connection is
    + * reset by the server or is made to an illegal URL.
    + * @private
    + */
    +goog.net.IframeIo.prototype.testForFirefoxSilentError_ = function() {
    +  if (this.active_) {
    +    var doc = this.getContentDocument_();
    +
    +    // This is a hack to test of the document has loaded with a page that
    +    // we can't access, such as a network error, that won't report onload
    +    // or onerror events.
    +    if (doc && !goog.reflect.canAccessProperty(doc, 'documentUri')) {
    +      if (!this.ignoreResponse_) {
    +        goog.events.unlisten(this.getRequestIframe(),
    +            goog.events.EventType.LOAD, this.onIframeLoaded_, false, this);
    +      }
    +
    +      if (navigator.onLine) {
    +        goog.log.warning(this.logger_, 'Silent Firefox error detected');
    +        this.handleError_(goog.net.ErrorCode.FF_SILENT_ERROR);
    +      } else {
    +        goog.log.warning(this.logger_,
    +            'Firefox is offline so report offline error ' +
    +            'instead of silent error');
    +        this.handleError_(goog.net.ErrorCode.OFFLINE);
    +      }
    +      return;
    +    }
    +    this.firefoxSilentErrorTimeout_ =
    +        goog.Timer.callOnce(this.testForFirefoxSilentError_, 250, this);
    +  }
    +};
    +
    +
    +
    +/**
    + * Class for representing incremental data events.
    + * @param {Object} data The data associated with the event.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.net.IframeIo.IncrementalDataEvent = function(data) {
    +  goog.events.Event.call(this, goog.net.EventType.INCREMENTAL_DATA);
    +
    +  /**
    +   * The data associated with the event.
    +   * @type {Object}
    +   */
    +  this.data = data;
    +};
    +goog.inherits(goog.net.IframeIo.IncrementalDataEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.data b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.data
    new file mode 100644
    index 0000000000..fed4357204
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.data
    @@ -0,0 +1,2 @@
    +This is just a file that iframeio_different_base_test.html requests to test
    +iframeIo.
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.html b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.html
    new file mode 100644
    index 0000000000..f19c8207a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.html
    @@ -0,0 +1,27 @@
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.net.IframeIo (with different base URL)
    +  </title>
    +  <script>
    +    // We use a different base to reproduce the conditions of crbug.com/66987
    +    var href = window.location.href;
    +    var newHref = href.replace(/net.*/, '');
    +    document.write('<base href="' + newHref + '">');
    +
    +    var baseScript = 'base.js';
    +    document.write('<script src="' + baseScript + '"><\/script>');
    +  </script>
    +  <script>
    +    goog.require('goog.net.iframeIoDifferentBaseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.js b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.js
    new file mode 100644
    index 0000000000..b524b0e407
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio_different_base_test.js
    @@ -0,0 +1,36 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.iframeIoDifferentBaseTest');
    +goog.setTestOnly('goog.net.iframeIoDifferentBaseTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.IframeIo');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +function testDifferentBaseUri() {
    +  var io = new goog.net.IframeIo();
    +  goog.events.listen(io, goog.net.EventType.COMPLETE,
    +      function() {
    +        assertNotEquals('File should have expected content.',
    +            -1, io.getResponseText().indexOf('just a file'));
    +        asyncTestCase.continueTesting();
    +      });
    +  io.send('net/iframeio_different_base_test.data');
    +  asyncTestCase.waitForAsync('Waiting for iframeIo respons.');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio_test.html b/mapbox-gl/closure-library/closure/goog/net/iframeio_test.html
    new file mode 100644
    index 0000000000..4ca4781fc2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio_test.html
    @@ -0,0 +1,115 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.IframeIo
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.IframeIoTest');
    +  </script>
    +<style>
    +  html, body {
    +    width: 100%;
    +    height: 100%;
    +    overflow:hidden;
    +  }
    +
    +  #log {
    +    position: absolute;
    +    top: 0px;
    +    width: 50%;
    +    right: 0%;
    +    height: 100%;
    +    overflow: auto;
    +  }
    +
    +  p, input {
    +    font-family: verdana, helvetica, arial, sans-serif;
    +    font-size: small;
    +    margin: 0px;
    +  }
    +
    +  input {
    +    font-family: verdana, helvetica, arial, sans-serif;
    +    font-size: x-small;
    +  }
    +
    +  i {
    +    font-size: 85%;
    +  }
    +</style>
    +</head>
    +<body>
    +<p>
    +  <b>IframeIo manual tests:</b><br><br>
    +  <i>All operations should have no effect on history.</i><br>
    +  <br>
    +  <i>These tests require the ClosureTestServer<br>
    +  to be running with the IframeIoTestServlet.</i><br>
    +<br></p>
    +
    +<p>
    +  <a href="javascript:simpleGet()">Simple GET</a><br>
    +  <a href="javascript:simplePost()">Simple POST</a><br>
    +  <a href="javascript:jsonEcho('GET')">JSON echo (get)</a><br>
    +  <a href="javascript:jsonEcho('POST')">JSON echo (post)</a><br>
    +  <a href="javascript:abort()">Test abort</a>
    +</p>
    +<form id="uploadform" action="/iframeio/upload" enctype="multipart/form-data" method="POST">
    +  <p><a href="javascript:sendFromForm()">Upload</a> <input name="userfile" type="file"> (big files should fail)</p>
    +</form>
    +<p>
    +  <a href="javascript:incremental()">Incremental results</a><br>
    +  <a href="javascript:redirect1()">Redirect (google.com)</a><br>
    +  <a href="javascript:redirect2()">Redirect (/iframeio/ping)</a><br>
    +  <a href="javascript:localUrl1()">Local request (Win path)</a><br>
    +  <a href="javascript:localUrl2()">Local request (Linux path)</a><br>
    +  <a href="javascript:badUrl()">Out of domain request</a><br>
    +  <a href="javascript:getServerTime(false)">Test cache</a> (Date should stay the same for subsequent tests)<br>
    +  <a href="javascript:getServerTime(true)">Test no-cache</a><br>
    +  <a href="javascript:errorGse404()">GSE 404 Error</a><br>
    +  <a href="javascript:errorGfe()">Simulated GFE Error</a><br>
    +  <a href="javascript:errorGmail()">Simulated Gmail Server Error</a><br><br>
    +</p>
    +<form id="testfrm" action="/iframeio/jsonecho" method="POST">
    +  <p><b>Comprehensive Form Post Test:</b><br>
    +  <input name="textinput" type="text" value="Default"> Text Input<br>
    +  Text Area<br>
    +  <textarea name="textarea">Default</textarea><br>
    +  <input name="checkbox1" type="checkbox" checked="checked"> Checkbox, default on<br>
    +  <input name="checkbox2" type="checkbox"> Checkbox, default off<br>
    +  Radio: <input name="radio" type="radio" value="Default" checked="checked"> Default,
    +  <input name="radio" type="radio" value="Foo"> Foo,
    +  <input name="radio" type="radio" value="Bar"> Bar<br>
    +  <select name="select">
    +    <option>One</option>
    +    <option>Two</option>
    +    <option selected="selected">Three (Default)</option>
    +    <option>Four</option>
    +    <option>Five</option>
    +  </select><br>
    +  <select name="selectmultiple">
    +    <option>One</option>
    +    <option selected="selected">Two (Default checked)</option>
    +    <option selected="selected">Three (Default checked)</option>
    +    <option>Four</option>
    +  </select>
    +  <a href="javascript:postForm()">Submit this form</a>
    +  </p>
    +</form>
    +<p><br><br>
    +TODO(pupius):<br>
    +- Local timeout
    +</p>
    +<div id="log"></div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeio_test.js b/mapbox-gl/closure-library/closure/goog/net/iframeio_test.js
    new file mode 100644
    index 0000000000..bfe880a592
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeio_test.js
    @@ -0,0 +1,310 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.IframeIoTest');
    +goog.setTestOnly('goog.net.IframeIoTest');
    +
    +goog.require('goog.debug');
    +goog.require('goog.debug.DivConsole');
    +goog.require('goog.debug.LogManager');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.IframeIo');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +// MANUAL TESTS - The tests should be run in the browser from the Closure Test
    +// Server
    +
    +// Set up a logger to track responses
    +goog.debug.LogManager.getRoot().setLevel(goog.log.Level.INFO);
    +var logconsole;
    +var testLogger = goog.log.getLogger('test');
    +
    +function setUpPage() {
    +  var logconsole = new goog.debug.DivConsole(document.getElementById('log'));
    +  logconsole.setCapturing(true);
    +}
    +
    +
    +/** Creates an iframeIo instance and sets up the test environment */
    +function getTestIframeIo() {
    +  logconsole.addSeparator();
    +  logconsole.getFormatter().resetRelativeTimeStart();
    +
    +  var io = new goog.net.IframeIo();
    +  io.setErrorChecker(checkForError);
    +
    +  goog.events.listen(io, 'success', onSuccess);
    +  goog.events.listen(io, 'error', onError);
    +  goog.events.listen(io, 'ready', onReady);
    +
    +  return io;
    +}
    +
    +
    +/**
    + * Checks for error strings returned by the GSE and error variables that
    + * the Gmail server and GFE set on certain errors.
    + */
    +function checkForError(doc) {
    +  var win = goog.dom.getWindow(doc);
    +  var text = doc.body.textContent || doc.body.innerText || '';
    +  var gseError = text.match(/([^\n]+)\nError ([0-9]{3})/);
    +  if (gseError) {
    +    return '(Error ' + gseError[2] + ') ' + gseError[1];
    +  } else if (win.gmail_error) {
    +    return win.gmail_error + 700;
    +  } else if (win.rc) {
    +    return 600 + win.rc % 100;
    +  } else {
    +    return null;
    +  }
    +}
    +
    +
    +/** Logs the status of an iframeIo object */
    +function logStatus(i) {
    +  goog.log.fine(testLogger, 'Is complete/success/active: ' +
    +      [i.isComplete(), i.isSuccess(), i.isActive()].join('/'));
    +}
    +
    +function onSuccess(e) {
    +  goog.log.warning(testLogger, 'Request Succeeded');
    +  logStatus(e.target);
    +}
    +
    +function onError(e) {
    +  goog.log.warning(testLogger, 'Request Errored: ' + e.target.getLastError());
    +  logStatus(e.target);
    +}
    +
    +function onReady(e) {
    +  goog.log.info(testLogger,
    +      'Test finished and iframe ready, disposing test object');
    +  e.target.dispose();
    +}
    +
    +
    +
    +function simpleGet() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onSimpleTestComplete);
    +  io.send('/iframeio/ping', 'GET');
    +}
    +
    +
    +function simplePost() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onSimpleTestComplete);
    +  io.send('/iframeio/ping', 'POST');
    +}
    +
    +function onSimpleTestComplete(e) {
    +  goog.log.info(testLogger, 'ResponseText: ' + e.target.getResponseText());
    +}
    +
    +function abort() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onAbortComplete);
    +  goog.events.listen(io, 'abort', onAbort);
    +  io.send('/iframeio/ping', 'GET');
    +  io.abort();
    +}
    +
    +function onAbortComplete(e) {
    +  goog.log.info(testLogger, 'Hmm, request should have been aborted');
    +}
    +
    +function onAbort(e) {
    +  goog.log.info(testLogger, 'Request aborted');
    +}
    +
    +
    +function errorGse404() {
    +  var io = getTestIframeIo();
    +  io.send('/iframeio/404', 'GET');
    +}
    +
    +function jsonEcho(method) {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onJsonComplete);
    +  var data = {'p1': 'x', 'p2': 'y', 'p3': 'z', 'r': 10};
    +  io.send('/iframeio/jsonecho?q1=a&q2=b&q3=c&r=5', method, false, data);
    +}
    +
    +function onJsonComplete(e) {
    +  goog.log.info(testLogger, 'ResponseText: ' + e.target.getResponseText());
    +  var json = e.target.getResponseJson();
    +  goog.log.info(testLogger,
    +      'ResponseJson:\n' + goog.debug.deepExpose(json, true));
    +}
    +
    +
    +
    +function sendFromForm() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'success', onUploadSuccess);
    +  goog.events.listen(io, 'error', onUploadError);
    +  io.sendFromForm(document.getElementById('uploadform'));
    +}
    +
    +function onUploadSuccess(e) {
    +  goog.log.log(testLogger, goog.log.Level.SHOUT, 'Upload Succeeded');
    +  goog.log.info(testLogger, 'ResponseText: ' + e.target.getResponseText());
    +}
    +
    +function onUploadError(e) {
    +  goog.log.log(testLogger, goog.log.Level.SHOUT, 'Upload Errored');
    +  goog.log.info(testLogger, 'ResponseText: ' + e.target.getResponseText());
    +}
    +
    +
    +function redirect1() {
    +  var io = getTestIframeIo();
    +  io.send('/iframeio/redirect', 'GET');
    +}
    +
    +function redirect2() {
    +  var io = getTestIframeIo();
    +  io.send('/iframeio/move', 'GET');
    +}
    +
    +function badUrl() {
    +  var io = getTestIframeIo();
    +  io.send('http://news.bbc.co.uk', 'GET');
    +}
    +
    +function localUrl1() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onLocalSuccess);
    +  io.send('c:\test.txt', 'GET');
    +}
    +
    +function localUrl2() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'success', onLocalSuccess);
    +  io.send('//test.txt', 'GET');
    +}
    +
    +function onLocalSuccess(e) {
    +  goog.log.info(testLogger,
    +      'The file was found:\n' + e.target.getResponseText());
    +}
    +
    +function getServerTime(noCache) {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'success', onTestCacheSuccess);
    +  io.send('/iframeio/datetime', 'GET', noCache);
    +}
    +
    +function onTestCacheSuccess(e) {
    +  goog.log.info(testLogger, 'Date reported: ' + e.target.getResponseText());
    +}
    +
    +
    +function errorGmail() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'error', onGmailError);
    +  io.send('/iframeio/gmailerror', 'GET');
    +}
    +
    +function onGmailError(e) {
    +  goog.log.info(testLogger, 'Gmail error: ' + e.target.getLastError());
    +}
    +
    +
    +function errorGfe() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'error', onGfeError);
    +  io.send('/iframeio/gfeerror', 'GET');
    +}
    +
    +function onGfeError(e) {
    +  goog.log.info(testLogger, 'GFE error: ' + e.target.getLastError());
    +}
    +
    +
    +
    +function incremental() {
    +  var io = getTestIframeIo();
    +  io.send('/iframeio/incremental', 'GET');
    +}
    +
    +window['P'] = function(iframe, data) {
    +  var iframeIo = goog.net.IframeIo.getInstanceByName(iframe.name);
    +  goog.log.info(testLogger, 'Data recieved - ' + data);
    +};
    +
    +
    +function postForm() {
    +  var io = getTestIframeIo();
    +  goog.events.listen(io, 'complete', onJsonComplete);
    +  io.sendFromForm(document.getElementById('testfrm'));
    +}
    +// UNIT TESTS - to be run via the JsUnit testRunner
    +
    +// TODO(user): How to unit test all of this?  Creating a MockIframe could
    +// help for the IE code path, but since the other browsers require weird
    +// behaviors this becomes very tricky.
    +
    +
    +function testGetForm() {
    +  var frm1 = goog.net.IframeIo.getForm_;
    +  var frm2 = goog.net.IframeIo.getForm_;
    +  assertEquals(frm1, frm2);
    +}
    +
    +
    +function testAddFormInputs() {
    +  var form = document.createElement('form');
    +  goog.net.IframeIo.addFormInputs_(form, {'a': 1, 'b': 2, 'c': 3});
    +  var inputs = form.getElementsByTagName('input');
    +  assertEquals(3, inputs.length);
    +  for (var i = 0; i < inputs.length; i++) {
    +    assertEquals('hidden', inputs[i].type);
    +    var n = inputs[i].name;
    +    assertEquals(n == 'a' ? '1' : n == 'b' ? '2' : '3', inputs[i].value);
    +  }
    +}
    +
    +function testNotIgnoringResponse() {
    +  // This test can't run in IE because we can't forge the check for
    +  // iframe.readyState = 'complete'.
    +  if (goog.userAgent.IE) {
    +    return;
    +  }
    +  var iframeIo = new goog.net.IframeIo();
    +  iframeIo.send('about:blank');
    +  // Simulate the frame finishing loading.
    +  goog.testing.events.fireBrowserEvent(new goog.testing.events.Event(
    +      goog.events.EventType.LOAD, iframeIo.getRequestIframe()));
    +  assertTrue(iframeIo.isComplete());
    +}
    +
    +function testIgnoreResponse() {
    +  var iframeIo = new goog.net.IframeIo();
    +  iframeIo.setIgnoreResponse(true);
    +  iframeIo.send('about:blank');
    +  // Simulate the frame finishing loading.
    +  goog.testing.events.fireBrowserEvent(new goog.testing.events.Event(
    +      goog.events.EventType.LOAD, iframeIo.getRequestIframe()));
    +  // Although the request is complete, the IframeIo isn't paying attention.
    +  assertFalse(iframeIo.isComplete());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor.js b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor.js
    new file mode 100644
    index 0000000000..6172a60131
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor.js
    @@ -0,0 +1,204 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class that can be used to determine when an iframe is loaded.
    + */
    +
    +goog.provide('goog.net.IframeLoadMonitor');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * The correct way to determine whether a same-domain iframe has completed
    + * loading is different in IE and Firefox.  This class abstracts above these
    + * differences, providing a consistent interface for:
    + * <ol>
    + * <li> Determing if an iframe is currently loaded
    + * <li> Listening for an iframe that is not currently loaded, to finish loading
    + * </ol>
    + *
    + * @param {HTMLIFrameElement} iframe An iframe.
    + * @param {boolean=} opt_hasContent Does the loaded iframe have content.
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + * @final
    + */
    +goog.net.IframeLoadMonitor = function(iframe, opt_hasContent) {
    +  goog.net.IframeLoadMonitor.base(this, 'constructor');
    +
    +  /**
    +   * Iframe whose load state is monitored by this IframeLoadMonitor
    +   * @type {HTMLIFrameElement}
    +   * @private
    +   */
    +  this.iframe_ = iframe;
    +
    +  /**
    +   * Whether or not the loaded iframe has any content.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.hasContent_ = !!opt_hasContent;
    +
    +  /**
    +   * Whether or not the iframe is loaded.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isLoaded_ = this.isLoadedHelper_();
    +
    +  if (!this.isLoaded_) {
    +    // IE 6 (and lower?) does not reliably fire load events, so listen to
    +    // readystatechange.
    +    // IE 7 does not reliably fire readystatechange events but listening on load
    +    // seems to work just fine.
    +    var isIe6OrLess =
    +        goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7');
    +    var loadEvtType = isIe6OrLess ?
    +        goog.events.EventType.READYSTATECHANGE : goog.events.EventType.LOAD;
    +    this.onloadListenerKey_ = goog.events.listen(
    +        this.iframe_, loadEvtType, this.handleLoad_, false, this);
    +
    +    // Sometimes we still don't get the event callback, so we'll poll just to
    +    // be safe.
    +    this.intervalId_ = window.setInterval(
    +        goog.bind(this.handleLoad_, this),
    +        goog.net.IframeLoadMonitor.POLL_INTERVAL_MS_);
    +  }
    +};
    +goog.inherits(goog.net.IframeLoadMonitor, goog.events.EventTarget);
    +
    +
    +/**
    + * Event type dispatched by a goog.net.IframeLoadMonitor when it internal iframe
    + * finishes loading for the first time after construction of the
    + * goog.net.IframeLoadMonitor
    + * @type {string}
    + */
    +goog.net.IframeLoadMonitor.LOAD_EVENT = 'ifload';
    +
    +
    +/**
    + * Poll interval for polling iframe load states in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.net.IframeLoadMonitor.POLL_INTERVAL_MS_ = 100;
    +
    +
    +/**
    + * Key for iframe load listener, or null if not currently listening on the
    + * iframe for a load event.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.net.IframeLoadMonitor.prototype.onloadListenerKey_ = null;
    +
    +
    +/**
    + * Returns whether or not the iframe is loaded.
    + * @return {boolean} whether or not the iframe is loaded.
    + */
    +goog.net.IframeLoadMonitor.prototype.isLoaded = function() {
    +  return this.isLoaded_;
    +};
    +
    +
    +/**
    + * Stops the poll timer if this IframeLoadMonitor is currently polling.
    + * @private
    + */
    +goog.net.IframeLoadMonitor.prototype.maybeStopTimer_ = function() {
    +  if (this.intervalId_) {
    +    window.clearInterval(this.intervalId_);
    +    this.intervalId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns the iframe whose load state this IframeLoader monitors.
    + * @return {HTMLIFrameElement} the iframe whose load state this IframeLoader
    + *     monitors.
    + */
    +goog.net.IframeLoadMonitor.prototype.getIframe = function() {
    +  return this.iframe_;
    +};
    +
    +
    +/** @override */
    +goog.net.IframeLoadMonitor.prototype.disposeInternal = function() {
    +  delete this.iframe_;
    +  this.maybeStopTimer_();
    +  goog.events.unlistenByKey(this.onloadListenerKey_);
    +  goog.net.IframeLoadMonitor.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Returns whether or not the iframe is loaded.  Determines this by inspecting
    + * browser dependent properties of the iframe.
    + * @return {boolean} whether or not the iframe is loaded.
    + * @private
    + */
    +goog.net.IframeLoadMonitor.prototype.isLoadedHelper_ = function() {
    +  var isLoaded = false;
    +  /** @preserveTry */
    +  try {
    +    // IE versions before IE11 will reliably have readyState set to complete if
    +    // the iframe is loaded. For everything else, the iframe is loaded if there
    +    // is a body and if the body should have content the firstChild exists.
    +    // Firefox can fire the LOAD event and then a few hundred ms later replace
    +    // the contentDocument once the content is loaded.
    +    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('11')) {
    +      isLoaded = this.iframe_.readyState == 'complete';
    +    } else {
    +      isLoaded = !!goog.dom.getFrameContentDocument(this.iframe_).body &&
    +          (!this.hasContent_ ||
    +          !!goog.dom.getFrameContentDocument(this.iframe_).body.firstChild);
    +    }
    +  } catch (e) {
    +    // Ignore these errors. This just means that the iframe is not loaded
    +    // IE will throw error reading readyState if the iframe is not appended
    +    // to the dom yet.
    +    // Firefox will throw error getting the iframe body if the iframe is not
    +    // fully loaded.
    +  }
    +  return isLoaded;
    +};
    +
    +
    +/**
    + * Handles an event indicating that the loading status of the iframe has
    + * changed.  In Firefox this is a goog.events.EventType.LOAD event, in IE
    + * this is a goog.events.EventType.READYSTATECHANGED
    + * @private
    + */
    +goog.net.IframeLoadMonitor.prototype.handleLoad_ = function() {
    +  // Only do the handler if the iframe is loaded.
    +  if (this.isLoadedHelper_()) {
    +    this.maybeStopTimer_();
    +    goog.events.unlistenByKey(this.onloadListenerKey_);
    +    this.onloadListenerKey_ = null;
    +    this.isLoaded_ = true;
    +    this.dispatchEvent(goog.net.IframeLoadMonitor.LOAD_EVENT);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.html b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.html
    new file mode 100644
    index 0000000000..81cf981931
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.IframeLoadMonitor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.IframeLoadMonitorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="frame_parent">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.js b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.js
    new file mode 100644
    index 0000000000..4c7850f283
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test.js
    @@ -0,0 +1,105 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.IframeLoadMonitorTest');
    +goog.setTestOnly('goog.net.IframeLoadMonitorTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.net.IframeLoadMonitor');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_FRAME_SRCS = ['iframeloadmonitor_test_frame.html',
    +  'iframeloadmonitor_test_frame2.html',
    +  'iframeloadmonitor_test_frame3.html'];
    +
    +// Create a new test case.
    +var iframeLoaderTestCase = new goog.testing.AsyncTestCase(document.title);
    +iframeLoaderTestCase.stepTimeout = 4 * 1000;
    +
    +// Array holding all iframe load monitors.
    +iframeLoaderTestCase.iframeLoadMonitors_ = [];
    +
    +// How many single frames finished loading
    +iframeLoaderTestCase.singleComplete_ = 0;
    +
    +
    +/**
    + * Sets up the test environment, adds tests and sets up the worker pools.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.setUpPage = function() {
    +  this.log('Setting tests up');
    +  iframeLoaderTestCase.waitForAsync('loading iframes');
    +
    +  var dom = goog.dom.getDomHelper();
    +
    +  // Load single frame
    +  var frame = dom.createDom('iframe');
    +  this.iframeLoadMonitors_.push(new goog.net.IframeLoadMonitor(frame));
    +  goog.events.listen(this.iframeLoadMonitors_[0],
    +      goog.net.IframeLoadMonitor.LOAD_EVENT, this);
    +  var frameParent = dom.getElement('frame_parent');
    +  dom.appendChild(frameParent, frame);
    +  this.log('Loading frame at: ' + TEST_FRAME_SRCS[0]);
    +  frame.src = TEST_FRAME_SRCS[0];
    +
    +  // Load single frame with content check
    +  var frame1 = dom.createDom('iframe');
    +  this.iframeLoadMonitors_.push(new goog.net.IframeLoadMonitor(frame1, true));
    +  goog.events.listen(this.iframeLoadMonitors_[1],
    +      goog.net.IframeLoadMonitor.LOAD_EVENT, this);
    +  var frameParent = dom.getElement('frame_parent');
    +  dom.appendChild(frameParent, frame1);
    +  this.log('Loading frame with content check at: ' + TEST_FRAME_SRCS[0]);
    +  frame1.src = TEST_FRAME_SRCS[0];
    +};
    +
    +
    +/**
    + * Handles any events fired
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.handleEvent = function(e) {
    +  this.log('handleEvent, type: ' + e.type);
    +  if (e.type == goog.net.IframeLoadMonitor.LOAD_EVENT) {
    +    this.singleComplete_++;
    +    this.callbacksComplete();
    +  }
    +};
    +
    +
    +/**
    + * Checks if all the load callbacks are done
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.callbacksComplete = function() {
    +  if (this.singleComplete_ == 2) {
    +    iframeLoaderTestCase.continueTesting();
    +  }
    +};
    +
    +
    +/** Tests the results. */
    +iframeLoaderTestCase.addNewTest('testResults', function() {
    +  this.log('getting test results');
    +  for (var i = 0; i < this.iframeLoadMonitors_.length; i++) {
    +    assertTrue(this.iframeLoadMonitors_[i].isLoaded());
    +  }
    +});
    +
    +
    +/** Standalone Closure Test Runner. */
    +G_testRunner.initialize(iframeLoaderTestCase);
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame.html b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame.html
    new file mode 100644
    index 0000000000..9fbcbfa9ee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame.html
    @@ -0,0 +1,12 @@
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head><title>Iframe Load Test Frame 1</title></head>
    +<body>
    +  Iframe Load Test Frame 1
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame2.html b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame2.html
    new file mode 100644
    index 0000000000..7d436e253c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame2.html
    @@ -0,0 +1,12 @@
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head><title>Iframe Load Test Frame 2</title></head>
    +<body>
    +  Iframe Load Test Frame 2
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame3.html b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame3.html
    new file mode 100644
    index 0000000000..910b639d40
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/iframeloadmonitor_test_frame3.html
    @@ -0,0 +1,12 @@
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head><title>Iframe Load Test Frame 3</title></head>
    +<body>
    +  Iframe Load Test Frame 3
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader.js b/mapbox-gl/closure-library/closure/goog/net/imageloader.js
    new file mode 100644
    index 0000000000..a9f93eee43
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/imageloader.js
    @@ -0,0 +1,337 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Image loader utility class.  Useful when an application needs
    + * to preload multiple images, for example so they can be sized.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.net.ImageLoader');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.EventType');
    +goog.require('goog.object');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Image loader utility class.  Raises a {@link goog.events.EventType.LOAD}
    + * event for each image loaded, with an {@link Image} object as the target of
    + * the event, normalized to have {@code naturalHeight} and {@code naturalWidth}
    + * attributes.
    + *
    + * To use this class, run:
    + *
    + * <pre>
    + *   var imageLoader = new goog.net.ImageLoader();
    + *   goog.events.listen(imageLoader, goog.net.EventType.COMPLETE,
    + *       function(e) { ... });
    + *   imageLoader.addImage("image_id", "http://path/to/image.gif");
    + *   imageLoader.start();
    + * </pre>
    + *
    + * The start() method must be called to start image loading.  Images can be
    + * added and removed after loading has started, but only those images added
    + * before start() was called will be loaded until start() is called again.
    + * A goog.net.EventType.COMPLETE event will be dispatched only once all
    + * outstanding images have completed uploading.
    + *
    + * @param {Element=} opt_parent An optional parent element whose document object
    + *     should be used to load images.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.net.ImageLoader = function(opt_parent) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Map of image IDs to their request including their image src, used to keep
    +   * track of the images to load.  Once images have started loading, they're
    +   * removed from this map.
    +   * @type {!Object<!goog.net.ImageLoader.ImageRequest_>}
    +   * @private
    +   */
    +  this.imageIdToRequestMap_ = {};
    +
    +  /**
    +   * Map of image IDs to their image element, used only for images that are in
    +   * the process of loading.  Used to clean-up event listeners and to know
    +   * when we've completed loading images.
    +   * @type {!Object<string, !Element>}
    +   * @private
    +   */
    +  this.imageIdToImageMap_ = {};
    +
    +  /**
    +   * Event handler object, used to keep track of onload and onreadystatechange
    +   * listeners.
    +   * @type {!goog.events.EventHandler<!goog.net.ImageLoader>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The parent element whose document object will be used to load images.
    +   * Useful if you want to load the images from a window other than the current
    +   * window in order to control the Referer header sent when the image is
    +   * loaded.
    +   * @type {Element|undefined}
    +   * @private
    +   */
    +  this.parent_ = opt_parent;
    +};
    +goog.inherits(goog.net.ImageLoader, goog.events.EventTarget);
    +
    +
    +/**
    + * The type of image request to dispatch, if this is a CORS-enabled image
    + * request. CORS-enabled images can be reused in canvas elements without them
    + * being tainted. The server hosting the image should include the appropriate
    + * CORS header.
    + * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
    + * @enum {string}
    + */
    +goog.net.ImageLoader.CorsRequestType = {
    +  ANONYMOUS: 'anonymous',
    +  USE_CREDENTIALS: 'use-credentials'
    +};
    +
    +
    +/**
    + * Describes a request for an image. This includes its URL and its CORS-request
    + * type, if any.
    + * @typedef {{
    + *   src: string,
    + *   corsRequestType: ?goog.net.ImageLoader.CorsRequestType
    + * }}
    + * @private
    + */
    +goog.net.ImageLoader.ImageRequest_;
    +
    +
    +/**
    + * An array of event types to listen to on images.  This is browser dependent.
    + *
    + * For IE 10 and below, Internet Explorer doesn't reliably raise LOAD events
    + * on images, so we must use READY_STATE_CHANGE.  Since the image is cached
    + * locally, IE won't fire the LOAD event while the onreadystate event is fired
    + * always. On the other hand, the ERROR event is always fired whenever the image
    + * is not loaded successfully no matter whether it's cached or not.
    + *
    + * In IE 11, onreadystatechange is removed and replaced with onload:
    + *
    + * http://msdn.microsoft.com/en-us/library/ie/ms536957(v=vs.85).aspx
    + * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx
    + *
    + * @type {!Array<string>}
    + * @private
    + */
    +goog.net.ImageLoader.IMAGE_LOAD_EVENTS_ = [
    +  goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('11') ?
    +      goog.net.EventType.READY_STATE_CHANGE :
    +      goog.events.EventType.LOAD,
    +  goog.net.EventType.ABORT,
    +  goog.net.EventType.ERROR
    +];
    +
    +
    +/**
    + * Adds an image to the image loader, and associates it with the given ID
    + * string.  If an image with that ID already exists, it is silently replaced.
    + * When the image in question is loaded, the target of the LOAD event will be
    + * an {@code Image} object with {@code id} and {@code src} attributes based on
    + * these arguments.
    + * @param {string} id The ID of the image to load.
    + * @param {string|Image} image Either the source URL of the image or the HTML
    + *     image element itself (or any object with a {@code src} property, really).
    + * @param {!goog.net.ImageLoader.CorsRequestType=} opt_corsRequestType The type
    + *     of CORS request to use, if any.
    + */
    +goog.net.ImageLoader.prototype.addImage = function(
    +    id, image, opt_corsRequestType) {
    +  var src = goog.isString(image) ? image : image.src;
    +  if (src) {
    +    // For now, we just store the source URL for the image.
    +    this.imageIdToRequestMap_[id] = {
    +      src: src,
    +      corsRequestType: goog.isDef(opt_corsRequestType) ?
    +          opt_corsRequestType : null
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Removes the image associated with the given ID string from the image loader.
    + * If the image was previously loading, removes any listeners for its events
    + * and dispatches a COMPLETE event if all remaining images have now completed.
    + * @param {string} id The ID of the image to remove.
    + */
    +goog.net.ImageLoader.prototype.removeImage = function(id) {
    +  delete this.imageIdToRequestMap_[id];
    +
    +  var image = this.imageIdToImageMap_[id];
    +  if (image) {
    +    delete this.imageIdToImageMap_[id];
    +
    +    // Stop listening for events on the image.
    +    this.handler_.unlisten(image, goog.net.ImageLoader.IMAGE_LOAD_EVENTS_,
    +        this.onNetworkEvent_);
    +
    +    // If this was the last image, raise a COMPLETE event.
    +    if (goog.object.isEmpty(this.imageIdToImageMap_) &&
    +        goog.object.isEmpty(this.imageIdToRequestMap_)) {
    +      this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Starts loading all images in the image loader in parallel.  Raises a LOAD
    + * event each time an image finishes loading, and a COMPLETE event after all
    + * images have finished loading.
    + */
    +goog.net.ImageLoader.prototype.start = function() {
    +  // Iterate over the keys, rather than the full object, to essentially clone
    +  // the initial queued images in case any event handlers decide to add more
    +  // images before this loop has finished executing.
    +  var imageIdToRequestMap = this.imageIdToRequestMap_;
    +  goog.array.forEach(goog.object.getKeys(imageIdToRequestMap),
    +      function(id) {
    +        var imageRequest = imageIdToRequestMap[id];
    +        if (imageRequest) {
    +          delete imageIdToRequestMap[id];
    +          this.loadImage_(imageRequest, id);
    +        }
    +      }, this);
    +};
    +
    +
    +/**
    + * Creates an {@code Image} object with the specified ID and source URL, and
    + * listens for network events raised as the image is loaded.
    + * @param {!goog.net.ImageLoader.ImageRequest_} imageRequest The request data.
    + * @param {string} id The unique ID of the image to load.
    + * @private
    + */
    +goog.net.ImageLoader.prototype.loadImage_ = function(imageRequest, id) {
    +  if (this.isDisposed()) {
    +    // When loading an image in IE7 (and maybe IE8), the error handler
    +    // may fire before we yield JS control. If the error handler
    +    // dispose the ImageLoader, this method will throw exception.
    +    return;
    +  }
    +
    +  var image;
    +  if (this.parent_) {
    +    var dom = goog.dom.getDomHelper(this.parent_);
    +    image = dom.createDom('img');
    +  } else {
    +    image = new Image();
    +  }
    +
    +  if (imageRequest.corsRequestType) {
    +    image.crossOrigin = imageRequest.corsRequestType;
    +  }
    +
    +  this.handler_.listen(image, goog.net.ImageLoader.IMAGE_LOAD_EVENTS_,
    +      this.onNetworkEvent_);
    +  this.imageIdToImageMap_[id] = image;
    +
    +  image.id = id;
    +  image.src = imageRequest.src;
    +};
    +
    +
    +/**
    + * Handles net events (READY_STATE_CHANGE, LOAD, ABORT, and ERROR).
    + * @param {goog.events.Event} evt The network event to handle.
    + * @private
    + */
    +goog.net.ImageLoader.prototype.onNetworkEvent_ = function(evt) {
    +  var image = /** @type {Element} */ (evt.currentTarget);
    +
    +  if (!image) {
    +    return;
    +  }
    +
    +  if (evt.type == goog.net.EventType.READY_STATE_CHANGE) {
    +    // This implies that the user agent is IE; see loadImage_().
    +    // Noe that this block is used to check whether the image is ready to
    +    // dispatch the COMPLETE event.
    +    if (image.readyState == goog.net.EventType.COMPLETE) {
    +      // This is the IE equivalent of a LOAD event.
    +      evt.type = goog.events.EventType.LOAD;
    +    } else {
    +      // This may imply that the load failed.
    +      // Note that the image has only the following states:
    +      //   * uninitialized
    +      //   * loading
    +      //   * complete
    +      // When the ERROR or the ABORT event is fired, the readyState
    +      // will be either uninitialized or loading and we'd ignore those states
    +      // since they will be handled separately (eg: evt.type = 'ERROR').
    +
    +      // Notes from MSDN : The states through which an object passes are
    +      // determined by that object. An object can skip certain states
    +      // (for example, interactive) if the state does not apply to that object.
    +      // see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx
    +
    +      // The image is not loaded, ignore.
    +      return;
    +    }
    +  }
    +
    +  // Add natural width/height properties for non-Gecko browsers.
    +  if (typeof image.naturalWidth == 'undefined') {
    +    if (evt.type == goog.events.EventType.LOAD) {
    +      image.naturalWidth = image.width;
    +      image.naturalHeight = image.height;
    +    } else {
    +      // This implies that the image fails to be loaded.
    +      image.naturalWidth = 0;
    +      image.naturalHeight = 0;
    +    }
    +  }
    +
    +  // Redispatch the event on behalf of the image. Note that the external
    +  // listener may dispose this instance.
    +  this.dispatchEvent({type: evt.type, target: image});
    +
    +  if (this.isDisposed()) {
    +    // If instance was disposed by listener, exit this function.
    +    return;
    +  }
    +
    +  this.removeImage(image.id);
    +};
    +
    +
    +/** @override */
    +goog.net.ImageLoader.prototype.disposeInternal = function() {
    +  delete this.imageIdToRequestMap_;
    +  delete this.imageIdToImageMap_;
    +  goog.dispose(this.handler_);
    +
    +  goog.net.ImageLoader.superClass_.disposeInternal.call(this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader_test.html b/mapbox-gl/closure-library/closure/goog/net/imageloader_test.html
    new file mode 100644
    index 0000000000..7a816c21ff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/imageloader_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.ImageLoader
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.ImageLoaderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader_test.js b/mapbox-gl/closure-library/closure/goog/net/imageloader_test.js
    new file mode 100644
    index 0000000000..2aa9eee9ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/imageloader_test.js
    @@ -0,0 +1,330 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.ImageLoaderTest');
    +goog.setTestOnly('goog.net.ImageLoaderTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dispose');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.ImageLoader');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
    +
    +// Set the AsyncTestCase timeout to larger value to allow more time
    +// for images to load.
    +asyncTestCase.stepTimeout = 5000;
    +
    +
    +var TEST_EVENT_TYPES = [
    +  goog.events.EventType.LOAD,
    +  goog.net.EventType.COMPLETE,
    +  goog.net.EventType.ERROR
    +];
    +
    +
    +/**
    + * Mapping from test image file name to:
    + * [expected width, expected height, expected event to be fired].
    + */
    +var TEST_IMAGES = {
    +  'imageloader_testimg1.gif': [20, 20, goog.events.EventType.LOAD],
    +  'imageloader_testimg2.gif': [20, 20, goog.events.EventType.LOAD],
    +  'imageloader_testimg3.gif': [32, 32, goog.events.EventType.LOAD],
    +
    +  'this-is-not-image-1.gif': [0, 0, goog.net.EventType.ERROR],
    +  'this-is-not-image-2.gif': [0, 0, goog.net.EventType.ERROR]
    +};
    +
    +
    +var startTime;
    +var loader;
    +
    +
    +function setUp() {
    +  startTime = goog.now();
    +
    +  loader = new goog.net.ImageLoader();
    +
    +  // Adds test images to the loader.
    +  var i = 0;
    +  for (var key in TEST_IMAGES) {
    +    var imageId = 'img_' + i++;
    +    loader.addImage(imageId, key);
    +  }
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(loader);
    +}
    +
    +
    +/**
    + * Tests loading image and disposing before loading completes.
    + */
    +function testDisposeInTheMiddleOfLoadingWorks() {
    +  goog.events.listen(loader, TEST_EVENT_TYPES,
    +      goog.partial(handleDisposalImageLoaderEvent, loader));
    +  // waitForAsync before starting loader just in case
    +  // handleDisposalImageLoaderEvent is called from within loader.start
    +  // (before we yield control). This may happen in IE7/IE8.
    +  asyncTestCase.waitForAsync('Waiting for loader handler to fire.');
    +  loader.start();
    +}
    +
    +
    +function handleDisposalImageLoaderEvent(loader, e) {
    +  assertFalse('Handler is still invoked after loader is disposed.',
    +      loader.isDisposed());
    +
    +  switch (e.type) {
    +    case goog.net.EventType.COMPLETE:
    +      fail('This test should never get COMPLETE event.');
    +      return;
    +
    +    case goog.events.EventType.LOAD:
    +    case goog.net.EventType.ERROR:
    +      loader.dispose();
    +      break;
    +  }
    +
    +  // Make sure that handler is never called again after disposal before
    +  // marking test as successful.
    +  asyncTestCase.waitForAsync('Wait to ensure that COMPLETE is never fired');
    +  goog.Timer.callOnce(function() {
    +    asyncTestCase.continueTesting();
    +  }, 500);
    +}
    +
    +
    +/**
    + * Tests loading of images until completion.
    + */
    +function testLoadingUntilCompletion() {
    +  var results = {};
    +  goog.events.listen(loader, TEST_EVENT_TYPES,
    +      function(e) {
    +        switch (e.type) {
    +          case goog.events.EventType.LOAD:
    +            var image = e.target;
    +            results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
    +                [image.naturalWidth, image.naturalHeight, e.type];
    +            return;
    +
    +          case goog.net.EventType.ERROR:
    +            var image = e.target;
    +            results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
    +                [image.naturalWidth, image.naturalHeight, e.type];
    +            return;
    +
    +          case goog.net.EventType.COMPLETE:
    +            // Test completes successfully.
    +            asyncTestCase.continueTesting();
    +
    +            assertImagesAreCorrect(results);
    +            return;
    +        }
    +      });
    +
    +  // waitForAsync before starting loader just in case handleImageLoaderEvent
    +  // is called from within loader.start (before we yield control).
    +  // This may happen in IE7/IE8.
    +  asyncTestCase.waitForAsync('Waiting for loader handler to fire.');
    +  loader.start();
    +}
    +
    +
    +function assertImagesAreCorrect(results) {
    +  assertEquals(
    +      goog.object.getCount(TEST_IMAGES), goog.object.getCount(results));
    +  goog.object.forEach(TEST_IMAGES, function(value, key) {
    +    // Check if fires the COMPLETE event.
    +    assertTrue('Image is not loaded completely.', key in results);
    +
    +    var image = results[key];
    +
    +    // Check image size.
    +    assertEquals('Image width is not correct', value[0], image[0]);
    +    assertEquals('Image length is not correct', value[1], image[1]);
    +
    +    // Check if fired the correct event.
    +    assertEquals('Event *' + value[2] + '* must be fired', value[2], image[2]);
    +  });
    +}
    +
    +
    +/**
    + * Overrides the loader's loadImage_ method so that it dispatches an image
    + * loaded event immediately, causing any event listners to receive them
    + * synchronously.  This allows tests to assume synchronous execution.
    + */
    +function makeLoaderSynchronous(loader) {
    +  var originalLoadImage = loader.loadImage_;
    +  loader.loadImage_ = function(request, id) {
    +    originalLoadImage.call(this, request, id);
    +
    +    var event = new goog.events.Event(goog.events.EventType.LOAD);
    +    event.currentTarget = this.imageIdToImageMap_[id];
    +    loader.onNetworkEvent_(event);
    +  };
    +
    +  // Make listen() a no-op.
    +  loader.handler_.listen = goog.nullFunction;
    +}
    +
    +
    +/**
    + * Verifies that if an additional image is added after start() was called, but
    + * before COMPLETE was dispatched, no COMPLETE event is sent.  Verifies COMPLETE
    + * is finally sent when .start() is called again and all images have now
    + * completed loading.
    + */
    +function testImagesAddedAfterStart() {
    +  // Use synchronous image loading.
    +  makeLoaderSynchronous(loader);
    +
    +  // Add another image once the first images finishes loading.
    +  goog.events.listenOnce(loader, goog.events.EventType.LOAD, function() {
    +    loader.addImage('extra_image', 'extra_image.gif');
    +  });
    +
    +  // Keep track of the total # of image loads.
    +  var loadRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
    +
    +  // Keep track of how many times COMPLETE was dispatched.
    +  var completeRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
    +
    +  // Start testing.
    +  loader.start();
    +  assertEquals(
    +      'COMPLETE event should not have been dispatched yet: An image was ' +
    +          'added after the initial batch was started.',
    +      0, completeRecordFn.getCallCount());
    +  assertEquals('Just the test images should have loaded',
    +      goog.object.getCount(TEST_IMAGES), loadRecordFn.getCallCount());
    +
    +  loader.start();
    +  assertEquals('COMPLETE should have been dispatched once.',
    +      1, completeRecordFn.getCallCount());
    +  assertEquals('All images should have been loaded',
    +      goog.object.getCount(TEST_IMAGES) + 1, loadRecordFn.getCallCount());
    +}
    +
    +
    +/**
    + * Verifies that more images can be added after an upload starts, and start()
    + * can be called for them, resulting in just one COMPLETE event once all the
    + * images have completed.
    + */
    +function testImagesAddedAndStartedAfterStart() {
    +  // Use synchronous image loading.
    +  makeLoaderSynchronous(loader);
    +
    +  // Keep track of the total # of image loads.
    +  var loadRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
    +
    +  // Add more images once the first images finishes loading, and call start()
    +  // to get them going.
    +  goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
    +    loader.addImage('extra_image', 'extra_image.gif');
    +    loader.addImage('extra_image2', 'extra_image2.gif');
    +    loader.start();
    +  });
    +
    +  // Keep track of how many times COMPLETE was dispatched.
    +  var completeRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
    +
    +  // Start testing.  Make sure all 7 images loaded.
    +  loader.start();
    +  assertEquals('COMPLETE should have been dispatched once.',
    +      1, completeRecordFn.getCallCount());
    +  assertEquals('All images should have been loaded',
    +      goog.object.getCount(TEST_IMAGES) + 2, loadRecordFn.getCallCount());
    +}
    +
    +
    +/**
    + * Verifies that if images are removed after loading has started, COMPLETE
    + * is dispatched once the remaining images have finished.
    + */
    +function testImagesRemovedAfterStart() {
    +  // Use synchronous image loading.
    +  makeLoaderSynchronous(loader);
    +
    +  // Remove 2 images once the first image finishes loading.
    +  goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
    +    loader.removeImage(
    +        goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
    +    loader.removeImage(
    +        goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
    +  });
    +
    +  // Keep track of the total # of image loads.
    +  var loadRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
    +
    +  // Keep track of how many times COMPLETE was dispatched.
    +  var completeRecordFn = goog.testing.recordFunction();
    +  goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
    +
    +  // Start testing.  Make sure only the 3 images remaining loaded.
    +  loader.start();
    +  assertEquals('COMPLETE should have been dispatched once.',
    +      1, completeRecordFn.getCallCount());
    +  assertEquals('All images should have been loaded',
    +      goog.object.getCount(TEST_IMAGES) - 2, loadRecordFn.getCallCount());
    +}
    +
    +
    +/**
    + * Verifies that the correct image attribute is set when using CORS requests.
    + */
    +function testSetsCorsAttribute() {
    +  // Use synchronous image loading.
    +  makeLoaderSynchronous(loader);
    +
    +  // Verify the crossOrigin attribute of the requested images.
    +  goog.events.listen(loader, goog.events.EventType.LOAD, function(e) {
    +    var image = e.target;
    +    if (image.id == 'cors_request') {
    +      assertEquals(
    +          'CORS requested image should have a crossOrigin attribute set',
    +          'anonymous', image.crossOrigin);
    +    } else {
    +      assertTrue(
    +          'Non-CORS requested images should not have a crossOrigin attribute',
    +          goog.string.isEmptyOrWhitespace(goog.string.makeSafe(image.crossOrigin)));
    +    }
    +  });
    +
    +  // Make a new request for one of the images, this time using CORS.
    +  var srcs = goog.object.getKeys(TEST_IMAGES);
    +  loader.addImage(
    +      'cors_request', srcs[0], goog.net.ImageLoader.CorsRequestType.ANONYMOUS);
    +  loader.start();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg1.gif b/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg1.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..18b295b3fce339ca2f7c123acd2c4abdc4c8a317
    GIT binary patch
    literal 453
    zcmV;$0XqIiNk%w1VH5xq0M$PLBucCP{$*Bn)&Blf`1$+g<ut#>-Jh&v_4Q26&^!PC
    zr|0YUbdclG*LCUb#_a58)7k7mVU?P%>;M0q%-690|BL?ql>PmC|Nnyi{&G`rw5htZ
    zc8<$tgWcEO^Zfj{Q*M3j?LcaW$%mfiY=*n-?N*<%y3EnvJYvR!nUe-Ix%T#?sk`oC
    zd%BLHgul!0hnL2cs>-&()M9_m;N$DH#D9mKrR3(IdXH}I@v`#rfAH~%;p1%c^_lqi
    zYXATLA^8LW002J#EC2ui02BZe000K!z@Kn9WW9*Uq;N4qd6dxKXk(j2lMBvj2cTVv
    zBhzG;;(Ub!lwzXe0SHQhhc#j_#Ffh>h3YUQ5O50u3^6hkISLkeK0Z7RH7*(|3okPc
    zA0G;978n^B0RaP}2_Q03A}gvBofn{@Aq52>FlZelH$97<paZd`EpSUBExLKH2qC2;
    z!bBY=ou8q}AT3Z58x@|R1IakGN(>Z?J_zO^2O3ZUJs1xUB_-ws2>@|36+ApH04fI|
    vvEE=I(cnOY77!rJh;X1lgcC_TpopO16dD5>KmdS<gn$f=NnV_276br0a~#-q
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg2.gif b/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg2.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..691683264d44cdce4df7125be26d1a4054b663ab
    GIT binary patch
    literal 460
    zcmV;-0W<zbNk%w1VH5xq0M$PLft~UF{r#D;|E9O-`T6<r^#6pT|Dw77+TG>U)YOx$
    z|K8r-?ehN8-T!->{%eo@+S=N}(fxOo@oka*;_3gt%IxUq=;r3;<mBYy;^Ox9_ww@c
    z?Ck97>gw<B@A~`y;Nam>eE*)Y?1`rT^z`(iw)5TP|M2kesJ;H#;{RTS{Py|(>hJ%J
    zssGK{|E<CQyUY4=llO|E>$=0=&(ht|)Y_D&<=^4x#K+Rf*Z<Af_t@Is-{kY+<L9!%
    z^#A|=A^8LW002J#EC2ui02BZe000K*z@Kn9L`8_mq%akGP?XSLYamiYlPSp#q+(r(
    zBheyLI(&sJ)dGaMO&H38>m?XD#F<N{^e`Au4FNwX4*)g;Gc;=$79AcuIuR%aAq)``
    zI0*?55Mvh>93C4Q1OY59E<XnVnw@No92+DW9|0pTLLUM$HJy2&qNEH80v|&K3IJOe
    zdK@RS8VU!-NCpESpP{k@0#-@~1I(Z&qXaKzN-Y(P+9ah722c|S+2o`F1rJILG3-7n
    zAz;)B@L&ZN06i)=P{4wM0tY!hICz4@L<|518Vm{0U_^@tCX%dhkpfBs6M}>U0RTI|
    Cndxr;
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg3.gif b/mapbox-gl/closure-library/closure/goog/net/imageloader_testimg3.gif
    new file mode 100644
    index 0000000000000000000000000000000000000000..fe5e37883e1a0ee511120eb2506b83e6784c0a3b
    GIT binary patch
    literal 13446
    zcmeI3e_Rt~9LEn)Fn$RNC^F(W%YL!l?#8dpZHO!s1XMsJP3zbWn6Ta2jy5Ks`6Csy
    zG$c^dG|@_s6fl1xR{qSA%u-Y`v;c(+1QfM^Zh(OPVXx;8|9kGW*LHjE{rx`oyr0j%
    z&*bp1ATfm^C~_P@y1To(y1F_$+V0-Ff3vCOVSUYmYv-#j*R`K0IezYHTgCRf<x5(M
    zbJ~kc&4r6w3w149HMjGqrrhMl)$^KiX5CsgtsyJCAv2<3VOWD%QE!+qBPFL|mS=o?
    zyh5Sin)`Iy$45;^6%<QxztUinj#8%3ESf+kskJh!`EVtMs#P*<wtti`N*78etEZUt
    zbgVf#PH9e2id9&!+)iqem^3;K%~GgIld3gHOfrm8F$uJUySEJE7}|rdDKad?@&L8W
    z8OrEs)K5V0l|qpp>K7m&14(gUfG-+Lv1kOPMae*v6biX7QX&fEK4{;C$?dp1sa};R
    ziBW|0&B^_fVaY73li;|~XcQPl0!E*N6JoI#7m_$h@;QQUNY}EIiLW(`>uvI=kAgNR
    z^=cifX0)iKFO|Tgu`&#^Ow@n%EsoOlPoy;ndKM~BG8)`O>2N|I#QQsQg;H)%5~`;u
    zmeI#COsc$h`C)23ZDjN*1~fEQB(+pN1`W(QP)~DPM9VEc_(Fm&^o!%H5JHKFm@}xp
    z7kZ*RiczT((+8D{`2=ApCj%w^L4D<epuTb`ZV@p^OpkTTnw}W{KVd3mzb{>yKDB3C
    zR7#vqr8TscHE_;|$DOH^l0-(Yp;)<ELnYC8k~&d}4-7u_{d(V;iv<@;yg!$ZUcG-S
    zpUKUD_h-_+w<(+iNG{X`bpaUwf?>L#E+7LyFiaQJ1!MpShUtR3fD8b^FkMg=kO3eV
    zrVHu<G5`d_bU|G}27q9gE~pF001yn*1$6-#0D@t<pe`T-Krl=f)CFV!2!`o`x_}G-
    z!7yD=7mxuU7^VyA0x|#u!*oGiKn8$dm@cRb$N&%w(*<<_832M|x}Yu~13)lL7t{r0
    z00@3+y6gr{deB<#c!!ZY$6?jo)%ou~4<Fp`xOca`t@Tbz^WV4sYHGZ7v!TB3&p&Ql
    zul@bnZ#7r1T&})!@z-B2{CxhWs&ha7aQ6E%r%#<cas0b)zd2TUwBpF&LkAD+|9W3}
    zS?S(AUzO}GF530w7oYFkQMmoHZCgLxviXxu8#ff>f1I~I_oH=dKm1_L>YVIVD_6X~
    zeA&_^?=4=Gm6@^7oNh9{o5mXS@8}q<CN*V&IyotkRw)yxx8IsS@6EY$-iV()Yi3+*
    z%#7*NqN5_GMubn99QL{*bW+H~V7W|+NW?*b0sej>k`UqozVB<VzVh-*<6nGX9Oi?1
    zKkvo!9P2S=w7Z+@sF5ztBb*!^?CqW#Zfi4asPzyl1o3$=)XEm|Mtl+OvK+uchTW^}
    z@CsLNNzTr`5#nHlR<Rmu>%Du==(;D?M6W;Kw)E7MmRtu((%Je89y`)Ys*3}r7M+uB
    z-eZb%H>O`K-Iy_c=cSf_Hpkg#H`N~U%I<3HAm@|mi<CxoIkh5dRbqyA^%}37rDk4!
    z!G?mhdAfXi#}Uphu5Rw`9%J(j>lX0r?Z=dsm5tq19LsYoKT^6sCWJSlY=~1uCE<Sz
    z;W?L)HlxZb<p*7=M0Wdlk@!K^YSFNxHQt^LH*Yoa+^!?TullyOwej4mZAM<GYwyH(
    tqgN6^C#$?=P9Ekvhv+@twsY+=iPZS!ZM$>E%ycU}Jt_CpOdkZ9@E=m6@fZLA
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/closure-library/closure/goog/net/ipaddress.js b/mapbox-gl/closure-library/closure/goog/net/ipaddress.js
    new file mode 100644
    index 0000000000..250847593e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/ipaddress.js
    @@ -0,0 +1,515 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file contains classes to handle IPv4 and IPv6 addresses.
    + * This implementation is mostly based on Google's project:
    + * http://code.google.com/p/ipaddr-py/.
    + *
    + */
    +
    +goog.provide('goog.net.IpAddress');
    +goog.provide('goog.net.Ipv4Address');
    +goog.provide('goog.net.Ipv6Address');
    +
    +goog.require('goog.array');
    +goog.require('goog.math.Integer');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Abstract class defining an IP Address.
    + *
    + * Please use goog.net.IpAddress static methods or
    + * goog.net.Ipv4Address/Ipv6Address classes.
    + *
    + * @param {!goog.math.Integer} address The Ip Address.
    + * @param {number} version The version number (4, 6).
    + * @constructor
    + */
    +goog.net.IpAddress = function(address, version) {
    +  /**
    +   * The IP Address.
    +   * @type {!goog.math.Integer}
    +   * @private
    +   */
    +  this.ip_ = address;
    +
    +  /**
    +   * The IP Address version.
    +   * @type {number}
    +   * @private
    +   */
    +  this.version_ = version;
    +
    +  /**
    +   * The IPAddress, as string.
    +   * @type {string}
    +   * @private
    +   */
    +  this.ipStr_ = '';
    +};
    +
    +
    +/**
    + * @return {number} The IP Address version.
    + */
    +goog.net.IpAddress.prototype.getVersion = function() {
    +  return this.version_;
    +};
    +
    +
    +/**
    + * @param {!goog.net.IpAddress} other The other IP Address.
    + * @return {boolean} true if the IP Addresses are equal.
    + */
    +goog.net.IpAddress.prototype.equals = function(other) {
    +  return (this.version_ == other.getVersion() &&
    +          this.ip_.equals(other.toInteger()));
    +};
    +
    +
    +/**
    + * @return {!goog.math.Integer} The IP Address, as an Integer.
    + */
    +goog.net.IpAddress.prototype.toInteger = function() {
    +  return /** @type {!goog.math.Integer} */ (goog.object.clone(this.ip_));
    +};
    +
    +
    +/**
    + * @return {string} The IP Address, as an URI string following RFC 3986.
    + */
    +goog.net.IpAddress.prototype.toUriString = goog.abstractMethod;
    +
    +
    +/**
    + * @return {string} The IP Address, as a string.
    + * @override
    + */
    +goog.net.IpAddress.prototype.toString = goog.abstractMethod;
    +
    +
    +/**
    + * Parses an IP Address in a string.
    + * If the string is malformed, the function will simply return null
    + * instead of raising an exception.
    + *
    + * @param {string} address The IP Address.
    + * @see {goog.net.Ipv4Address}
    + * @see {goog.net.Ipv6Address}
    + * @return {goog.net.IpAddress} The IP Address or null.
    + */
    +goog.net.IpAddress.fromString = function(address) {
    +  try {
    +    if (address.indexOf(':') != -1) {
    +      return new goog.net.Ipv6Address(address);
    +    }
    +
    +    return new goog.net.Ipv4Address(address);
    +  } catch (e) {
    +    // Both constructors raise exception if the address is malformed (ie.
    +    // invalid). The user of this function should not care about catching
    +    // the exception, espcially if it's used to validate an user input.
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Tries to parse a string represented as a host portion of an URI.
    + * See RFC 3986 for more details on IPv6 addresses inside URI.
    + * If the string is malformed, the function will simply return null
    + * instead of raising an exception.
    + *
    + * @param {string} address A RFC 3986 encoded IP address.
    + * @see {goog.net.Ipv4Address}
    + * @see {goog.net.Ipv6Address}
    + * @return {goog.net.IpAddress} The IP Address.
    + */
    +goog.net.IpAddress.fromUriString = function(address) {
    +  try {
    +    if (goog.string.startsWith(address, '[') &&
    +        goog.string.endsWith(address, ']')) {
    +      return new goog.net.Ipv6Address(
    +          address.substring(1, address.length - 1));
    +    }
    +
    +    return new goog.net.Ipv4Address(address);
    +  } catch (e) {
    +    // Both constructors raise exception if the address is malformed (ie.
    +    // invalid). The user of this function should not care about catching
    +    // the exception, espcially if it's used to validate an user input.
    +    return null;
    +  }
    +};
    +
    +
    +
    +/**
    + * Takes a string or a number and returns a IPv4 Address.
    + *
    + * This constructor accepts strings and instance of goog.math.Integer.
    + * If you pass a goog.math.Integer, make sure that its sign is set to positive.
    + * @param {(string|!goog.math.Integer)} address The address to store.
    + * @extends {goog.net.IpAddress}
    + * @constructor
    + * @final
    + */
    +goog.net.Ipv4Address = function(address) {
    +  var ip = goog.math.Integer.ZERO;
    +  if (address instanceof goog.math.Integer) {
    +    if (address.getSign() != 0 ||
    +        address.lessThan(goog.math.Integer.ZERO) ||
    +        address.greaterThan(goog.net.Ipv4Address.MAX_ADDRESS_)) {
    +      throw Error('The address does not look like an IPv4.');
    +    } else {
    +      ip = goog.object.clone(address);
    +    }
    +  } else {
    +    if (!goog.net.Ipv4Address.REGEX_.test(address)) {
    +      throw Error(address + ' does not look like an IPv4 address.');
    +    }
    +
    +    var octets = address.split('.');
    +    if (octets.length != 4) {
    +      throw Error(address + ' does not look like an IPv4 address.');
    +    }
    +
    +    for (var i = 0; i < octets.length; i++) {
    +      var parsedOctet = goog.string.toNumber(octets[i]);
    +      if (isNaN(parsedOctet) ||
    +          parsedOctet < 0 || parsedOctet > 255 ||
    +          (octets[i].length != 1 && goog.string.startsWith(octets[i], '0'))) {
    +        throw Error('In ' + address + ', octet ' + i + ' is not valid');
    +      }
    +      var intOctet = goog.math.Integer.fromNumber(parsedOctet);
    +      ip = ip.shiftLeft(8).or(intOctet);
    +    }
    +  }
    +  goog.net.Ipv4Address.base(
    +      this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 4);
    +};
    +goog.inherits(goog.net.Ipv4Address, goog.net.IpAddress);
    +
    +
    +/**
    + * Regular expression matching all the allowed chars for IPv4.
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.net.Ipv4Address.REGEX_ = /^[0-9.]*$/;
    +
    +
    +/**
    + * The Maximum length for a netmask (aka, the number of bits for IPv4).
    + * @type {number}
    + * @const
    + */
    +goog.net.Ipv4Address.MAX_NETMASK_LENGTH = 32;
    +
    +
    +/**
    + * The Maximum address possible for IPv4.
    + * @type {goog.math.Integer}
    + * @private
    + * @const
    + */
    +goog.net.Ipv4Address.MAX_ADDRESS_ = goog.math.Integer.ONE.shiftLeft(
    +    goog.net.Ipv4Address.MAX_NETMASK_LENGTH).subtract(goog.math.Integer.ONE);
    +
    +
    +/**
    + * @override
    + */
    +goog.net.Ipv4Address.prototype.toString = function() {
    +  if (this.ipStr_) {
    +    return this.ipStr_;
    +  }
    +
    +  var ip = this.ip_.getBitsUnsigned(0);
    +  var octets = [];
    +  for (var i = 3; i >= 0; i--) {
    +    octets[i] = String((ip & 0xff));
    +    ip = ip >>> 8;
    +  }
    +
    +  this.ipStr_ = octets.join('.');
    +
    +  return this.ipStr_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.net.Ipv4Address.prototype.toUriString = function() {
    +  return this.toString();
    +};
    +
    +
    +
    +/**
    + * Takes a string or a number and returns an IPv6 Address.
    + *
    + * This constructor accepts strings and instance of goog.math.Integer.
    + * If you pass a goog.math.Integer, make sure that its sign is set to positive.
    + * @param {(string|!goog.math.Integer)} address The address to store.
    + * @constructor
    + * @extends {goog.net.IpAddress}
    + * @final
    + */
    +goog.net.Ipv6Address = function(address) {
    +  var ip = goog.math.Integer.ZERO;
    +  if (address instanceof goog.math.Integer) {
    +    if (address.getSign() != 0 ||
    +        address.lessThan(goog.math.Integer.ZERO) ||
    +        address.greaterThan(goog.net.Ipv6Address.MAX_ADDRESS_)) {
    +      throw Error('The address does not look like a valid IPv6.');
    +    } else {
    +      ip = goog.object.clone(address);
    +    }
    +  } else {
    +    if (!goog.net.Ipv6Address.REGEX_.test(address)) {
    +      throw Error(address + ' is not a valid IPv6 address.');
    +    }
    +
    +    var splitColon = address.split(':');
    +    if (splitColon[splitColon.length - 1].indexOf('.') != -1) {
    +      var newHextets = goog.net.Ipv6Address.dottedQuadtoHextets_(
    +          splitColon[splitColon.length - 1]);
    +      goog.array.removeAt(splitColon, splitColon.length - 1);
    +      goog.array.extend(splitColon, newHextets);
    +      address = splitColon.join(':');
    +    }
    +
    +    var splitDoubleColon = address.split('::');
    +    if (splitDoubleColon.length > 2 ||
    +        (splitDoubleColon.length == 1 && splitColon.length != 8)) {
    +      throw Error(address + ' is not a valid IPv6 address.');
    +    }
    +
    +    var ipArr;
    +    if (splitDoubleColon.length > 1) {
    +      ipArr = goog.net.Ipv6Address.explode_(splitDoubleColon);
    +    } else {
    +      ipArr = splitColon;
    +    }
    +
    +    if (ipArr.length != 8) {
    +      throw Error(address + ' is not a valid IPv6 address');
    +    }
    +
    +    for (var i = 0; i < ipArr.length; i++) {
    +      var parsedHextet = goog.math.Integer.fromString(ipArr[i], 16);
    +      if (parsedHextet.lessThan(goog.math.Integer.ZERO) ||
    +          parsedHextet.greaterThan(goog.net.Ipv6Address.MAX_HEXTET_VALUE_)) {
    +        throw Error(ipArr[i] + ' in ' + address + ' is not a valid hextet.');
    +      }
    +      ip = ip.shiftLeft(16).or(parsedHextet);
    +    }
    +  }
    +  goog.net.Ipv6Address.base(
    +      this, 'constructor', /** @type {!goog.math.Integer} */ (ip), 6);
    +};
    +goog.inherits(goog.net.Ipv6Address, goog.net.IpAddress);
    +
    +
    +/**
    + * Regular expression matching all allowed chars for an IPv6.
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.net.Ipv6Address.REGEX_ = /^([a-fA-F0-9]*:){2}[a-fA-F0-9:.]*$/;
    +
    +
    +/**
    + * The Maximum length for a netmask (aka, the number of bits for IPv6).
    + * @type {number}
    + * @const
    + */
    +goog.net.Ipv6Address.MAX_NETMASK_LENGTH = 128;
    +
    +
    +/**
    + * The maximum value of a hextet.
    + * @type {goog.math.Integer}
    + * @private
    + * @const
    + */
    +goog.net.Ipv6Address.MAX_HEXTET_VALUE_ = goog.math.Integer.fromInt(65535);
    +
    +
    +/**
    + * The Maximum address possible for IPv6.
    + * @type {goog.math.Integer}
    + * @private
    + * @const
    + */
    +goog.net.Ipv6Address.MAX_ADDRESS_ = goog.math.Integer.ONE.shiftLeft(
    +    goog.net.Ipv6Address.MAX_NETMASK_LENGTH).subtract(goog.math.Integer.ONE);
    +
    +
    +/**
    + * @override
    + */
    +goog.net.Ipv6Address.prototype.toString = function() {
    +  if (this.ipStr_) {
    +    return this.ipStr_;
    +  }
    +
    +  var outputArr = [];
    +  for (var i = 3; i >= 0; i--) {
    +    var bits = this.ip_.getBitsUnsigned(i);
    +    var firstHextet = bits >>> 16;
    +    var secondHextet = bits & 0xffff;
    +    outputArr.push(firstHextet.toString(16));
    +    outputArr.push(secondHextet.toString(16));
    +  }
    +
    +  outputArr = goog.net.Ipv6Address.compress_(outputArr);
    +  this.ipStr_ = outputArr.join(':');
    +  return this.ipStr_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.net.Ipv6Address.prototype.toUriString = function() {
    +  return '[' + this.toString() + ']';
    +};
    +
    +
    +/**
    + * This method is in charge of expanding/exploding an IPv6 string from its
    + * compressed form.
    + * @private
    + * @param {!Array<string>} address An IPv6 address split around '::'.
    + * @return {!Array<string>} The expanded version of the IPv6.
    + */
    +goog.net.Ipv6Address.explode_ = function(address) {
    +  var basePart = address[0].split(':');
    +  var secondPart = address[1].split(':');
    +
    +  if (basePart.length == 1 && basePart[0] == '') {
    +    basePart = [];
    +  }
    +  if (secondPart.length == 1 && secondPart[0] == '') {
    +    secondPart = [];
    +  }
    +
    +  // Now we fill the gap with 0.
    +  var gap = 8 - (basePart.length + secondPart.length);
    +
    +  if (gap < 1) {
    +    return [];
    +  }
    +
    +  goog.array.extend(basePart, goog.array.repeat('0', gap));
    +
    +  // Now we merge the basePart + gap + secondPart
    +  goog.array.extend(basePart, secondPart);
    +
    +  return basePart;
    +};
    +
    +
    +/**
    + * This method is in charge of compressing an expanded IPv6 array of hextets.
    + * @private
    + * @param {!Array<string>} hextets The array of hextet.
    + * @return {!Array<string>} The compressed version of this array.
    + */
    +goog.net.Ipv6Address.compress_ = function(hextets) {
    +  var bestStart = -1;
    +  var start = -1;
    +  var bestSize = 0;
    +  var size = 0;
    +  for (var i = 0; i < hextets.length; i++) {
    +    if (hextets[i] == '0') {
    +      size++;
    +      if (start == -1) {
    +        start = i;
    +      }
    +      if (size > bestSize) {
    +        bestSize = size;
    +        bestStart = start;
    +      }
    +    } else {
    +      start = -1;
    +      size = 0;
    +    }
    +  }
    +
    +  if (bestSize > 0) {
    +    if ((bestStart + bestSize) == hextets.length) {
    +      hextets.push('');
    +    }
    +    hextets.splice(bestStart, bestSize, '');
    +
    +    if (bestStart == 0) {
    +      hextets = [''].concat(hextets);
    +    }
    +  }
    +  return hextets;
    +};
    +
    +
    +/**
    + * This method will convert an IPv4 to a list of 2 hextets.
    + *
    + * For instance, 1.2.3.4 will be converted to ['0102', '0304'].
    + * @private
    + * @param {string} quads An IPv4 as a string.
    + * @return {!Array<string>} A list of 2 hextets.
    + */
    +goog.net.Ipv6Address.dottedQuadtoHextets_ = function(quads) {
    +  var ip4 = new goog.net.Ipv4Address(quads).toInteger();
    +  var bits = ip4.getBitsUnsigned(0);
    +  var hextets = [];
    +
    +  hextets.push(((bits >>> 16) & 0xffff).toString(16));
    +  hextets.push((bits & 0xffff).toString(16));
    +
    +  return hextets;
    +};
    +
    +
    +/**
    + * @return {boolean} true if the IPv6 contains a mapped IPv4.
    + */
    +goog.net.Ipv6Address.prototype.isMappedIpv4Address = function() {
    +  return (this.ip_.getBitsUnsigned(3) == 0 &&
    +          this.ip_.getBitsUnsigned(2) == 0 &&
    +          this.ip_.getBitsUnsigned(1) == 0xffff);
    +};
    +
    +
    +/**
    + * Will return the mapped IPv4 address in this IPv6 address.
    + * @return {goog.net.Ipv4Address} an IPv4 or null.
    + */
    +goog.net.Ipv6Address.prototype.getMappedIpv4Address = function() {
    +  if (!this.isMappedIpv4Address()) {
    +    return null;
    +  }
    +
    +  var newIpv4 = new goog.math.Integer([this.ip_.getBitsUnsigned(0)], 0);
    +  return new goog.net.Ipv4Address(newIpv4);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.html b/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.html
    new file mode 100644
    index 0000000000..2d7f426dfc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Test suite inspired from http://code.google.com/p/ipaddr-py/ and
    +Google's Guava InetAddresses test suite available on
    +http://code.google.com/p/guava-libraries/
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Test - goog.net.IpAddress
    +  </title>
    +  <script src="../base.js" type="text/javascript">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.net.IpAddressTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.js b/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.js
    new file mode 100644
    index 0000000000..1032c54fb0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/ipaddress_test.js
    @@ -0,0 +1,220 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.IpAddressTest');
    +goog.setTestOnly('goog.net.IpAddressTest');
    +
    +goog.require('goog.math.Integer');
    +goog.require('goog.net.IpAddress');
    +goog.require('goog.net.Ipv4Address');
    +goog.require('goog.net.Ipv6Address');
    +goog.require('goog.testing.jsunit');
    +
    +function testInvalidStrings() {
    +  assertEquals(null, goog.net.IpAddress.fromString(''));
    +  assertEquals(null, goog.net.IpAddress.fromString('016.016.016.016'));
    +  assertEquals(null, goog.net.IpAddress.fromString('016.016.016'));
    +  assertEquals(null, goog.net.IpAddress.fromString('016.016'));
    +  assertEquals(null, goog.net.IpAddress.fromString('016'));
    +  assertEquals(null, goog.net.IpAddress.fromString('000.000.000.000'));
    +  assertEquals(null, goog.net.IpAddress.fromString('000'));
    +  assertEquals(null,
    +      goog.net.IpAddress.fromString('0x0a.0x0a.0x0a.0x0a'));
    +  assertEquals(null, goog.net.IpAddress.fromString('0x0a.0x0a.0x0a'));
    +  assertEquals(null, goog.net.IpAddress.fromString('0x0a.0x0a'));
    +  assertEquals(null, goog.net.IpAddress.fromString('0x0a'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42..42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42..42.42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.42.'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.42...'));
    +  assertEquals(null, goog.net.IpAddress.fromString('.42.42.42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('...42.42.42.42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.-0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.+0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('.'));
    +  assertEquals(null, goog.net.IpAddress.fromString('...'));
    +  assertEquals(null, goog.net.IpAddress.fromString('bogus'));
    +  assertEquals(null, goog.net.IpAddress.fromString('bogus.com'));
    +  assertEquals(null, goog.net.IpAddress.fromString('192.168.0.1.com'));
    +  assertEquals(null,
    +      goog.net.IpAddress.fromString('12345.67899.-54321.-98765'));
    +  assertEquals(null, goog.net.IpAddress.fromString('257.0.0.0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('42.42.42.-42'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ff3:::1'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::1.net'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::1::1'));
    +  assertEquals(null, goog.net.IpAddress.fromString('1::2::3::4:5'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::7:6:5:4:3:2:'));
    +  assertEquals(null, goog.net.IpAddress.fromString(':6:5:4:3:2:1::'));
    +  assertEquals(null, goog.net.IpAddress.fromString('2001::db:::1'));
    +  assertEquals(null, goog.net.IpAddress.fromString('FEDC:9878'));
    +  assertEquals(null, goog.net.IpAddress.fromString('+1.+2.+3.4'));
    +  assertEquals(null, goog.net.IpAddress.fromString('1.2.3.4e0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::7:6:5:4:3:2:1:0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('7:6:5:4:3:2:1:0::'));
    +  assertEquals(null, goog.net.IpAddress.fromString('9:8:7:6:5:4:3::2:1'));
    +  assertEquals(null, goog.net.IpAddress.fromString('0:1:2:3::4:5:6:7'));
    +  assertEquals(null,
    +      goog.net.IpAddress.fromString('3ffe:0:0:0:0:0:0:0:1'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::10000'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::goog'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::-0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::+0'));
    +  assertEquals(null, goog.net.IpAddress.fromString('3ffe::-1'));
    +  assertEquals(null, goog.net.IpAddress.fromString(':'));
    +  assertEquals(null, goog.net.IpAddress.fromString(':::'));
    +  assertEquals(null, goog.net.IpAddress.fromString('a:'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::a:'));
    +  assertEquals(null, goog.net.IpAddress.fromString('0xa::'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::1.2.3'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::1.2.3.4.5'));
    +  assertEquals(null, goog.net.IpAddress.fromString('::1.2.3.4:'));
    +  assertEquals(null, goog.net.IpAddress.fromString('1.2.3.4::'));
    +  assertEquals(null, goog.net.IpAddress.fromString('2001:db8::1:'));
    +  assertEquals(null, goog.net.IpAddress.fromString(':2001:db8::1'));
    +}
    +
    +function testVersion() {
    +  var ip4 = goog.net.IpAddress.fromString('1.2.3.4');
    +  assertEquals(ip4.getVersion(), 4);
    +
    +  var ip6 = goog.net.IpAddress.fromString('2001:dead::beef:1');
    +  assertEquals(ip6.getVersion(), 6);
    +
    +  ip6 = goog.net.IpAddress.fromString('::192.168.1.1');
    +  assertEquals(ip6.getVersion(), 6);
    +}
    +
    +function testStringIpv4Address() {
    +  assertEquals('192.168.1.1',
    +      new goog.net.Ipv4Address('192.168.1.1').toString());
    +  assertEquals('1.1.1.1',
    +      new goog.net.Ipv4Address('1.1.1.1').toString());
    +  assertEquals('224.56.33.2',
    +      new goog.net.Ipv4Address('224.56.33.2').toString());
    +  assertEquals('255.255.255.255',
    +      new goog.net.Ipv4Address('255.255.255.255').toString());
    +  assertEquals('0.0.0.0',
    +      new goog.net.Ipv4Address('0.0.0.0').toString());
    +}
    +
    +function testIntIpv4Address() {
    +  var ip4Str = new goog.net.Ipv4Address('1.1.1.1');
    +  var ip4Int = new goog.net.Ipv4Address(
    +      new goog.math.Integer([16843009], 0));
    +
    +  assertTrue(ip4Str.equals(ip4Int));
    +  assertEquals(ip4Str.toString(), ip4Int.toString());
    +
    +  assertThrows('Ipv4(-1)', goog.partial(goog.net.Ipv4Address,
    +                                        goog.math.Integer.fromInt(-1)));
    +  assertThrows('Ipv4(2**32)',
    +               goog.partial(goog.net.Ipv4Address,
    +                            goog.math.Integer.ONE.shiftLeft(32)));
    +}
    +
    +function testStringIpv6Address() {
    +  assertEquals('1:2:3:4:5:6:7:8',
    +      new goog.net.Ipv6Address('1:2:3:4:5:6:7:8').toString());
    +  assertEquals('::1:2:3:4:5:6:7',
    +      new goog.net.Ipv6Address('::1:2:3:4:5:6:7').toString());
    +  assertEquals('1:2:3:4:5:6:7::',
    +      new goog.net.Ipv6Address('1:2:3:4:5:6:7:0').toString());
    +  assertEquals('2001:0:0:4::8',
    +      new goog.net.Ipv6Address('2001:0:0:4:0:0:0:8').toString());
    +  assertEquals('2001::4:5:6:7:8',
    +      new goog.net.Ipv6Address('2001:0:0:4:5:6:7:8').toString());
    +  assertEquals('2001::3:4:5:6:7:8',
    +      new goog.net.Ipv6Address('2001:0:3:4:5:6:7:8').toString());
    +  assertEquals('0:0:3::ffff',
    +      new goog.net.Ipv6Address('0:0:3:0:0:0:0:ffff').toString());
    +  assertEquals('::4:0:0:0:ffff',
    +      new goog.net.Ipv6Address('0:0:0:4:0:0:0:ffff').toString());
    +  assertEquals('::5:0:0:ffff',
    +      new goog.net.Ipv6Address('0:0:0:0:5:0:0:ffff').toString());
    +  assertEquals('1::4:0:0:7:8',
    +      new goog.net.Ipv6Address('1:0:0:4:0:0:7:8').toString());
    +  assertEquals('::',
    +      new goog.net.Ipv6Address('0:0:0:0:0:0:0:0').toString());
    +  assertEquals('::1',
    +      new goog.net.Ipv6Address('0:0:0:0:0:0:0:1').toString());
    +  assertEquals('2001:658:22a:cafe::',
    +      new goog.net.Ipv6Address(
    +          '2001:0658:022a:cafe:0000:0000:0000:0000').toString());
    +  assertEquals('::102:304',
    +      new goog.net.Ipv6Address('::1.2.3.4').toString());
    +  assertEquals('::ffff:303:303',
    +      new goog.net.Ipv6Address('::ffff:3.3.3.3').toString());
    +  assertEquals('::ffff:ffff',
    +      new goog.net.Ipv6Address('::255.255.255.255').toString());
    +}
    +
    +function testIntIpv6Address() {
    +  var ip6Str = new goog.net.Ipv6Address('2001::dead:beef:1');
    +  var ip6Int = new goog.net.Ipv6Address(
    +      new goog.math.Integer([3203334145, 57005, 0, 536936448], 0));
    +
    +  assertTrue(ip6Str.equals(ip6Int));
    +  assertEquals(ip6Str.toString(), ip6Int.toString());
    +
    +  assertThrows('Ipv6(-1)', goog.partial(goog.net.Ipv6Address,
    +                                        goog.math.Integer.fromInt(-1)));
    +  assertThrows('Ipv6(2**128)',
    +               goog.partial(goog.net.Ipv6Address,
    +                            goog.math.Integer.ONE.shiftLeft(128)));
    +
    +}
    +
    +function testDottedQuadIpv6() {
    +  var ip6 = new goog.net.Ipv6Address('7::0.128.0.127');
    +  ip6 = new goog.net.Ipv6Address('7::0.128.0.128');
    +  ip6 = new goog.net.Ipv6Address('7::128.128.0.127');
    +  ip6 = new goog.net.Ipv6Address('7::0.128.128.127');
    +}
    +
    +function testMappedIpv4Address() {
    +  var testAddresses = ['::ffff:1.2.3.4', '::FFFF:102:304'];
    +  var ipv4Str = '1.2.3.4';
    +
    +  var ip1 = new goog.net.Ipv6Address(testAddresses[0]);
    +  var ip2 = new goog.net.Ipv6Address(testAddresses[1]);
    +  var ipv4 = new goog.net.Ipv4Address(ipv4Str);
    +
    +  assertTrue(ip1.isMappedIpv4Address());
    +  assertTrue(ip2.isMappedIpv4Address());
    +  assertTrue(ip1.equals(ip2));
    +  assertTrue(ipv4.equals(ip1.getMappedIpv4Address()));
    +  assertTrue(ipv4.equals(ip2.getMappedIpv4Address()));
    +}
    +
    +
    +function testUriString() {
    +  var ip4Str = '192.168.1.1';
    +  var ip4Uri = goog.net.IpAddress.fromUriString(ip4Str);
    +  var ip4 = goog.net.IpAddress.fromString(ip4Str);
    +  assertTrue(ip4Uri.equals(ip4));
    +
    +  var ip6Str = '2001:dead::beef:1';
    +  assertEquals(null, goog.net.IpAddress.fromUriString(ip6Str));
    +
    +  var ip6Uri = goog.net.IpAddress.fromUriString('[' + ip6Str + ']');
    +  var ip6 = goog.net.IpAddress.fromString(ip6Str);
    +  assertTrue(ip6Uri.equals(ip6));
    +  assertEquals(ip6Uri.toString(), ip6Str);
    +  assertEquals(ip6Uri.toUriString(), '[' + ip6Str + ']');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsloader.js b/mapbox-gl/closure-library/closure/goog/net/jsloader.js
    new file mode 100644
    index 0000000000..527e8b090a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsloader.js
    @@ -0,0 +1,367 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility to load JavaScript files via DOM script tags.
    + * Refactored from goog.net.Jsonp. Works cross-domain.
    + *
    + */
    +
    +goog.provide('goog.net.jsloader');
    +goog.provide('goog.net.jsloader.Error');
    +goog.provide('goog.net.jsloader.ErrorCode');
    +goog.provide('goog.net.jsloader.Options');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.debug.Error');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +
    +
    +/**
    + * The name of the property of goog.global under which the JavaScript
    + * verification object is stored by the loaded script.
    + * @type {string}
    + * @private
    + */
    +goog.net.jsloader.GLOBAL_VERIFY_OBJS_ = 'closure_verification';
    +
    +
    +/**
    + * The default length of time, in milliseconds, we are prepared to wait for a
    + * load request to complete.
    + * @type {number}
    + */
    +goog.net.jsloader.DEFAULT_TIMEOUT = 5000;
    +
    +
    +/**
    + * Optional parameters for goog.net.jsloader.send.
    + * timeout: The length of time, in milliseconds, we are prepared to wait
    + *     for a load request to complete. Default it 5 seconds.
    + * document: The HTML document under which to load the JavaScript. Default is
    + *     the current document.
    + * cleanupWhenDone: If true clean up the script tag after script completes to
    + *     load. This is important if you just want to read data from the JavaScript
    + *     and then throw it away. Default is false.
    + *
    + * @typedef {{
    + *   timeout: (number|undefined),
    + *   document: (HTMLDocument|undefined),
    + *   cleanupWhenDone: (boolean|undefined)
    + * }}
    + */
    +goog.net.jsloader.Options;
    +
    +
    +/**
    + * Scripts (URIs) waiting to be loaded.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.net.jsloader.scriptsToLoad_ = [];
    +
    +
    +/**
    + * Loads and evaluates the JavaScript files at the specified URIs, guaranteeing
    + * the order of script loads.
    + *
    + * Because we have to load the scripts in serial (load script 1, exec script 1,
    + * load script 2, exec script 2, and so on), this will be slower than doing
    + * the network fetches in parallel.
    + *
    + * If you need to load a large number of scripts but dependency order doesn't
    + * matter, you should just call goog.net.jsloader.load N times.
    + *
    + * If you need to load a large number of scripts on the same domain,
    + * you may want to use goog.module.ModuleLoader.
    + *
    + * @param {Array<string>} uris The URIs to load.
    + * @param {goog.net.jsloader.Options=} opt_options Optional parameters. See
    + *     goog.net.jsloader.options documentation for details.
    + */
    +goog.net.jsloader.loadMany = function(uris, opt_options) {
    +  // Loading the scripts in serial introduces asynchronosity into the flow.
    +  // Therefore, there are race conditions where client A can kick off the load
    +  // sequence for client B, even though client A's scripts haven't all been
    +  // loaded yet.
    +  //
    +  // To work around this issue, all module loads share a queue.
    +  if (!uris.length) {
    +    return;
    +  }
    +
    +  var isAnotherModuleLoading = goog.net.jsloader.scriptsToLoad_.length;
    +  goog.array.extend(goog.net.jsloader.scriptsToLoad_, uris);
    +  if (isAnotherModuleLoading) {
    +    // jsloader is still loading some other scripts.
    +    // In order to prevent the race condition noted above, we just add
    +    // these URIs to the end of the scripts' queue and return.
    +    return;
    +  }
    +
    +  uris = goog.net.jsloader.scriptsToLoad_;
    +  var popAndLoadNextScript = function() {
    +    var uri = uris.shift();
    +    var deferred = goog.net.jsloader.load(uri, opt_options);
    +    if (uris.length) {
    +      deferred.addBoth(popAndLoadNextScript);
    +    }
    +  };
    +  popAndLoadNextScript();
    +};
    +
    +
    +/**
    + * Loads and evaluates a JavaScript file.
    + * When the script loads, a user callback is called.
    + * It is the client's responsibility to verify that the script ran successfully.
    + *
    + * @param {string} uri The URI of the JavaScript.
    + * @param {goog.net.jsloader.Options=} opt_options Optional parameters. See
    + *     goog.net.jsloader.Options documentation for details.
    + * @return {!goog.async.Deferred} The deferred result, that may be used to add
    + *     callbacks and/or cancel the transmission.
    + *     The error callback will be called with a single goog.net.jsloader.Error
    + *     parameter.
    + */
    +goog.net.jsloader.load = function(uri, opt_options) {
    +  var options = opt_options || {};
    +  var doc = options.document || document;
    +
    +  var script = goog.dom.createElement(goog.dom.TagName.SCRIPT);
    +  var request = {script_: script, timeout_: undefined};
    +  var deferred = new goog.async.Deferred(goog.net.jsloader.cancel_, request);
    +
    +  // Set a timeout.
    +  var timeout = null;
    +  var timeoutDuration = goog.isDefAndNotNull(options.timeout) ?
    +      options.timeout : goog.net.jsloader.DEFAULT_TIMEOUT;
    +  if (timeoutDuration > 0) {
    +    timeout = window.setTimeout(function() {
    +      goog.net.jsloader.cleanup_(script, true);
    +      deferred.errback(new goog.net.jsloader.Error(
    +          goog.net.jsloader.ErrorCode.TIMEOUT,
    +          'Timeout reached for loading script ' + uri));
    +    }, timeoutDuration);
    +    request.timeout_ = timeout;
    +  }
    +
    +  // Hang the user callback to be called when the script completes to load.
    +  // NOTE(user): This callback will be called in IE even upon error. In any
    +  // case it is the client's responsibility to verify that the script ran
    +  // successfully.
    +  script.onload = script.onreadystatechange = function() {
    +    if (!script.readyState || script.readyState == 'loaded' ||
    +        script.readyState == 'complete') {
    +      var removeScriptNode = options.cleanupWhenDone || false;
    +      goog.net.jsloader.cleanup_(script, removeScriptNode, timeout);
    +      deferred.callback(null);
    +    }
    +  };
    +
    +  // Add an error callback.
    +  // NOTE(user): Not supported in IE.
    +  script.onerror = function() {
    +    goog.net.jsloader.cleanup_(script, true, timeout);
    +    deferred.errback(new goog.net.jsloader.Error(
    +        goog.net.jsloader.ErrorCode.LOAD_ERROR,
    +        'Error while loading script ' + uri));
    +  };
    +
    +  // Add the script element to the document.
    +  goog.dom.setProperties(script, {
    +    'type': 'text/javascript',
    +    'charset': 'UTF-8',
    +    // NOTE(user): Safari never loads the script if we don't set
    +    // the src attribute before appending.
    +    'src': uri
    +  });
    +  var scriptParent = goog.net.jsloader.getScriptParentElement_(doc);
    +  scriptParent.appendChild(script);
    +
    +  return deferred;
    +};
    +
    +
    +/**
    + * Loads a JavaScript file and verifies it was evaluated successfully, using a
    + * verification object.
    + * The verification object is set by the loaded JavaScript at the end of the
    + * script.
    + * We verify this object was set and return its value in the success callback.
    + * If the object is not defined we trigger an error callback.
    + *
    + * @param {string} uri The URI of the JavaScript.
    + * @param {string} verificationObjName The name of the verification object that
    + *     the loaded script should set.
    + * @param {goog.net.jsloader.Options} options Optional parameters. See
    + *     goog.net.jsloader.Options documentation for details.
    + * @return {!goog.async.Deferred} The deferred result, that may be used to add
    + *     callbacks and/or cancel the transmission.
    + *     The success callback will be called with a single parameter containing
    + *     the value of the verification object.
    + *     The error callback will be called with a single goog.net.jsloader.Error
    + *     parameter.
    + */
    +goog.net.jsloader.loadAndVerify = function(uri, verificationObjName, options) {
    +  // Define the global objects variable.
    +  if (!goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_]) {
    +    goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_] = {};
    +  }
    +  var verifyObjs = goog.global[goog.net.jsloader.GLOBAL_VERIFY_OBJS_];
    +
    +  // Verify that the expected object does not exist yet.
    +  if (goog.isDef(verifyObjs[verificationObjName])) {
    +    // TODO(user): Error or reset variable?
    +    return goog.async.Deferred.fail(new goog.net.jsloader.Error(
    +        goog.net.jsloader.ErrorCode.VERIFY_OBJECT_ALREADY_EXISTS,
    +        'Verification object ' + verificationObjName + ' already defined.'));
    +  }
    +
    +  // Send request to load the JavaScript.
    +  var sendDeferred = goog.net.jsloader.load(uri, options);
    +
    +  // Create a deferred object wrapping the send result.
    +  var deferred = new goog.async.Deferred(
    +      goog.bind(sendDeferred.cancel, sendDeferred));
    +
    +  // Call user back with object that was set by the script.
    +  sendDeferred.addCallback(function() {
    +    var result = verifyObjs[verificationObjName];
    +    if (goog.isDef(result)) {
    +      deferred.callback(result);
    +      delete verifyObjs[verificationObjName];
    +    } else {
    +      // Error: script was not loaded properly.
    +      deferred.errback(new goog.net.jsloader.Error(
    +          goog.net.jsloader.ErrorCode.VERIFY_ERROR,
    +          'Script ' + uri + ' loaded, but verification object ' +
    +          verificationObjName + ' was not defined.'));
    +    }
    +  });
    +
    +  // Pass error to new deferred object.
    +  sendDeferred.addErrback(function(error) {
    +    if (goog.isDef(verifyObjs[verificationObjName])) {
    +      delete verifyObjs[verificationObjName];
    +    }
    +    deferred.errback(error);
    +  });
    +
    +  return deferred;
    +};
    +
    +
    +/**
    + * Gets the DOM element under which we should add new script elements.
    + * How? Take the first head element, and if not found take doc.documentElement,
    + * which always exists.
    + *
    + * @param {!HTMLDocument} doc The relevant document.
    + * @return {!Element} The script parent element.
    + * @private
    + */
    +goog.net.jsloader.getScriptParentElement_ = function(doc) {
    +  var headElements = doc.getElementsByTagName(goog.dom.TagName.HEAD);
    +  if (!headElements || goog.array.isEmpty(headElements)) {
    +    return doc.documentElement;
    +  } else {
    +    return headElements[0];
    +  }
    +};
    +
    +
    +/**
    + * Cancels a given request.
    + * @this {{script_: Element, timeout_: number}} The request context.
    + * @private
    + */
    +goog.net.jsloader.cancel_ = function() {
    +  var request = this;
    +  if (request && request.script_) {
    +    var scriptNode = request.script_;
    +    if (scriptNode && scriptNode.tagName == 'SCRIPT') {
    +      goog.net.jsloader.cleanup_(scriptNode, true, request.timeout_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes the script node and the timeout.
    + *
    + * @param {Node} scriptNode The node to be cleaned up.
    + * @param {boolean} removeScriptNode If true completely remove the script node.
    + * @param {?number=} opt_timeout The timeout handler to cleanup.
    + * @private
    + */
    +goog.net.jsloader.cleanup_ = function(scriptNode, removeScriptNode,
    +                                      opt_timeout) {
    +  if (goog.isDefAndNotNull(opt_timeout)) {
    +    goog.global.clearTimeout(opt_timeout);
    +  }
    +
    +  scriptNode.onload = goog.nullFunction;
    +  scriptNode.onerror = goog.nullFunction;
    +  scriptNode.onreadystatechange = goog.nullFunction;
    +
    +  // Do this after a delay (removing the script node of a running script can
    +  // confuse older IEs).
    +  if (removeScriptNode) {
    +    window.setTimeout(function() {
    +      goog.dom.removeNode(scriptNode);
    +    }, 0);
    +  }
    +};
    +
    +
    +/**
    + * Possible error codes for jsloader.
    + * @enum {number}
    + */
    +goog.net.jsloader.ErrorCode = {
    +  LOAD_ERROR: 0,
    +  TIMEOUT: 1,
    +  VERIFY_ERROR: 2,
    +  VERIFY_OBJECT_ALREADY_EXISTS: 3
    +};
    +
    +
    +
    +/**
    + * A jsloader error.
    + *
    + * @param {goog.net.jsloader.ErrorCode} code The error code.
    + * @param {string=} opt_message Additional message.
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.net.jsloader.Error = function(code, opt_message) {
    +  var msg = 'Jsloader error (code #' + code + ')';
    +  if (opt_message) {
    +    msg += ': ' + opt_message;
    +  }
    +  goog.net.jsloader.Error.base(this, 'constructor', msg);
    +
    +  /**
    +   * The code for this error.
    +   *
    +   * @type {goog.net.jsloader.ErrorCode}
    +   */
    +  this.code = code;
    +};
    +goog.inherits(goog.net.jsloader.Error, goog.debug.Error);
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsloader_test.html b/mapbox-gl/closure-library/closure/goog/net/jsloader_test.html
    new file mode 100644
    index 0000000000..e584d6a9ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsloader_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.jsloader
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.jsloaderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsloader_test.js b/mapbox-gl/closure-library/closure/goog/net/jsloader_test.js
    new file mode 100644
    index 0000000000..e423ab0bc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsloader_test.js
    @@ -0,0 +1,137 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.jsloaderTest');
    +goog.setTestOnly('goog.net.jsloaderTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.net.jsloader');
    +goog.require('goog.net.jsloader.ErrorCode');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +// Initialize the AsyncTestCase.
    +var testCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
    +testCase.stepTimeout = 5 * 1000; // 5 seconds
    +
    +
    +testCase.setUp = function() {
    +  goog.provide = goog.nullFunction;
    +};
    +
    +
    +testCase.tearDown = function() {
    +  // Remove all the fake scripts.
    +  var scripts = goog.array.clone(
    +      document.getElementsByTagName('SCRIPT'));
    +  for (var i = 0; i < scripts.length; i++) {
    +    if (scripts[i].src.indexOf('testdata') != -1) {
    +      goog.dom.removeNode(scripts[i]);
    +    }
    +  }
    +};
    +
    +
    +// Sunny day scenario for load function.
    +function testLoad() {
    +  testCase.waitForAsync('testLoad');
    +
    +  window.test1 = null;
    +  var testUrl = 'testdata/jsloader_test1.js';
    +  var result = goog.net.jsloader.load(testUrl);
    +  result.addCallback(function() {
    +    testCase.continueTesting();
    +
    +    var script = result.defaultScope_.script_;
    +
    +    assertNotNull('script created', script);
    +    assertEquals('encoding is utf-8', 'UTF-8', script.charset);
    +
    +    // Check that the URI matches ours.
    +    assertTrue('server URI', script.src.indexOf(testUrl) >= 0);
    +
    +    // Check that the script was really loaded.
    +    assertEquals('verification object', 'Test #1 loaded', window.test1);
    +  });
    +}
    +
    +
    +// Sunny day scenario for loadAndVerify function.
    +function testLoadAndVerify() {
    +  testCase.waitForAsync('testLoadAndVerify');
    +
    +  var testUrl = 'testdata/jsloader_test2.js';
    +  var result = goog.net.jsloader.loadAndVerify(testUrl, 'test2');
    +  result.addCallback(function(verifyObj) {
    +    testCase.continueTesting();
    +
    +    // Check that the verification object has passed ok.
    +    assertEquals('verification object', 'Test #2 loaded', verifyObj);
    +  });
    +}
    +
    +
    +// What happens when the verification object is not set by the loaded script?
    +function testLoadAndVerifyError() {
    +  testCase.waitForAsync('testLoadAndVerifyError');
    +
    +  var testUrl = 'testdata/jsloader_test2.js';
    +  var result = goog.net.jsloader.loadAndVerify(testUrl, 'fake');
    +  result.addErrback(function(error) {
    +    testCase.continueTesting();
    +
    +    // Check that the error code is right.
    +    assertEquals('verification error', goog.net.jsloader.ErrorCode.VERIFY_ERROR,
    +        error.code);
    +  });
    +}
    +
    +
    +// Tests that callers can cancel the deferred without error.
    +function testLoadAndVerifyCancelled() {
    +  var testUrl = 'testdata/jsloader_test2.js';
    +  var result = goog.net.jsloader.loadAndVerify(testUrl, 'test2');
    +  result.cancel();
    +}
    +
    +
    +// Test the loadMany function.
    +function testLoadMany() {
    +  testCase.waitForAsync('testLoadMany');
    +
    +  // Load test #3 and then #1.
    +  window.test1 = null;
    +  var testUrls1 = ['testdata/jsloader_test3.js', 'testdata/jsloader_test1.js'];
    +  goog.net.jsloader.loadMany(testUrls1);
    +
    +  window.test3Callback = function(msg) {
    +    testCase.continueTesting();
    +
    +    // Check that the 1st test was not loaded yet.
    +    assertEquals('verification object', null, window.test1);
    +
    +    // Load test #4, which is supposed to wait for #1 to load.
    +    testCase.waitForAsync('testLoadMany');
    +    var testUrls2 = ['testdata/jsloader_test4.js'];
    +    goog.net.jsloader.loadMany(testUrls2);
    +  };
    +
    +  window.test4Callback = function(msg) {
    +    testCase.continueTesting();
    +
    +    // Check that the 1st test was already loaded.
    +    assertEquals('verification object', 'Test #1 loaded', window.test1);
    +  };
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsonp.js b/mapbox-gl/closure-library/closure/goog/net/jsonp.js
    new file mode 100644
    index 0000000000..f4b1ea8a43
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsonp.js
    @@ -0,0 +1,340 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +// The original file lives here: http://go/cross_domain_channel.js
    +
    +/**
    + * @fileoverview Implements a cross-domain communication channel. A
    + * typical web page is prevented by browser security from sending
    + * request, such as a XMLHttpRequest, to other servers than the ones
    + * from which it came. The Jsonp class provides a workaround by
    + * using dynamically generated script tags. Typical usage:.
    + *
    + * var jsonp = new goog.net.Jsonp(new goog.Uri('http://my.host.com/servlet'));
    + * var payload = { 'foo': 1, 'bar': true };
    + * jsonp.send(payload, function(reply) { alert(reply) });
    + *
    + * This script works in all browsers that are currently supported by
    + * the Google Maps API, which is IE 6.0+, Firefox 0.8+, Safari 1.2.4+,
    + * Netscape 7.1+, Mozilla 1.4+, Opera 8.02+.
    + *
    + */
    +
    +goog.provide('goog.net.Jsonp');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.net.jsloader');
    +
    +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    +//
    +// This class allows us (Google) to send data from non-Google and thus
    +// UNTRUSTED pages to our servers. Under NO CIRCUMSTANCES return
    +// anything sensitive, such as session or cookie specific data. Return
    +// only data that you want parties external to Google to have. Also
    +// NEVER use this method to send data from web pages to untrusted
    +// servers, or redirects to unknown servers (www.google.com/cache,
    +// /q=xx&btnl, /url, www.googlepages.com, etc.)
    +//
    +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    +
    +
    +
    +/**
    + * Creates a new cross domain channel that sends data to the specified
    + * host URL. By default, if no reply arrives within 5s, the channel
    + * assumes the call failed to complete successfully.
    + *
    + * @param {goog.Uri|string} uri The Uri of the server side code that receives
    + *     data posted through this channel (e.g.,
    + *     "http://maps.google.com/maps/geo").
    + *
    + * @param {string=} opt_callbackParamName The parameter name that is used to
    + *     specify the callback. Defaults to "callback".
    + *
    + * @constructor
    + * @final
    + */
    +goog.net.Jsonp = function(uri, opt_callbackParamName) {
    +  /**
    +   * The uri_ object will be used to encode the payload that is sent to the
    +   * server.
    +   * @type {goog.Uri}
    +   * @private
    +   */
    +  this.uri_ = new goog.Uri(uri);
    +
    +  /**
    +   * This is the callback parameter name that is added to the uri.
    +   * @type {string}
    +   * @private
    +   */
    +  this.callbackParamName_ = opt_callbackParamName ?
    +      opt_callbackParamName : 'callback';
    +
    +  /**
    +   * The length of time, in milliseconds, this channel is prepared
    +   * to wait for for a request to complete. The default value is 5 seconds.
    +   * @type {number}
    +   * @private
    +   */
    +  this.timeout_ = 5000;
    +};
    +
    +
    +/**
    + * The name of the property of goog.global under which the callback is
    + * stored.
    + */
    +goog.net.Jsonp.CALLBACKS = '_callbacks_';
    +
    +
    +/**
    + * Used to generate unique callback IDs. The counter must be global because
    + * all channels share a common callback object.
    + * @private
    + */
    +goog.net.Jsonp.scriptCounter_ = 0;
    +
    +
    +/**
    + * Sets the length of time, in milliseconds, this channel is prepared
    + * to wait for for a request to complete. If the call is not competed
    + * within the set time span, it is assumed to have failed. To wait
    + * indefinitely for a request to complete set the timout to a negative
    + * number.
    + *
    + * @param {number} timeout The length of time before calls are
    + * interrupted.
    + */
    +goog.net.Jsonp.prototype.setRequestTimeout = function(timeout) {
    +  this.timeout_ = timeout;
    +};
    +
    +
    +/**
    + * Returns the current timeout value, in milliseconds.
    + *
    + * @return {number} The timeout value.
    + */
    +goog.net.Jsonp.prototype.getRequestTimeout = function() {
    +  return this.timeout_;
    +};
    +
    +
    +/**
    + * Sends the given payload to the URL specified at the construction
    + * time. The reply is delivered to the given replyCallback. If the
    + * errorCallback is specified and the reply does not arrive within the
    + * timeout period set on this channel, the errorCallback is invoked
    + * with the original payload.
    + *
    + * If no reply callback is specified, then the response is expected to
    + * consist of calls to globally registered functions. No &callback=
    + * URL parameter will be sent in the request, and the script element
    + * will be cleaned up after the timeout.
    + *
    + * @param {Object=} opt_payload Name-value pairs.  If given, these will be
    + *     added as parameters to the supplied URI as GET parameters to the
    + *     given server URI.
    + *
    + * @param {Function=} opt_replyCallback A function expecting one
    + *     argument, called when the reply arrives, with the response data.
    + *
    + * @param {Function=} opt_errorCallback A function expecting one
    + *     argument, called on timeout, with the payload (if given), otherwise
    + *     null.
    + *
    + * @param {string=} opt_callbackParamValue Value to be used as the
    + *     parameter value for the callback parameter (callbackParamName).
    + *     To be used when the value needs to be fixed by the client for a
    + *     particular request, to make use of the cached responses for the request.
    + *     NOTE: If multiple requests are made with the same
    + *     opt_callbackParamValue, only the last call will work whenever the
    + *     response comes back.
    + *
    + * @return {!Object} A request descriptor that may be used to cancel this
    + *     transmission, or null, if the message may not be cancelled.
    + */
    +goog.net.Jsonp.prototype.send = function(opt_payload,
    +                                         opt_replyCallback,
    +                                         opt_errorCallback,
    +                                         opt_callbackParamValue) {
    +
    +  var payload = opt_payload || null;
    +
    +  var id = opt_callbackParamValue ||
    +      '_' + (goog.net.Jsonp.scriptCounter_++).toString(36) +
    +      goog.now().toString(36);
    +
    +  if (!goog.global[goog.net.Jsonp.CALLBACKS]) {
    +    goog.global[goog.net.Jsonp.CALLBACKS] = {};
    +  }
    +
    +  // Create a new Uri object onto which this payload will be added
    +  var uri = this.uri_.clone();
    +  if (payload) {
    +    goog.net.Jsonp.addPayloadToUri_(payload, uri);
    +  }
    +
    +  if (opt_replyCallback) {
    +    var reply = goog.net.Jsonp.newReplyHandler_(id, opt_replyCallback);
    +    goog.global[goog.net.Jsonp.CALLBACKS][id] = reply;
    +
    +    uri.setParameterValues(this.callbackParamName_,
    +                           goog.net.Jsonp.CALLBACKS + '.' + id);
    +  }
    +
    +  var deferred = goog.net.jsloader.load(uri.toString(),
    +      {timeout: this.timeout_, cleanupWhenDone: true});
    +  var error = goog.net.Jsonp.newErrorHandler_(id, payload, opt_errorCallback);
    +  deferred.addErrback(error);
    +
    +  return {id_: id, deferred_: deferred};
    +};
    +
    +
    +/**
    + * Cancels a given request. The request must be exactly the object returned by
    + * the send method.
    + *
    + * @param {Object} request The request object returned by the send method.
    + */
    +goog.net.Jsonp.prototype.cancel = function(request) {
    +  if (request) {
    +    if (request.deferred_) {
    +      request.deferred_.cancel();
    +    }
    +    if (request.id_) {
    +      goog.net.Jsonp.cleanup_(request.id_, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Creates a timeout callback that calls the given timeoutCallback with the
    + * original payload.
    + *
    + * @param {string} id The id of the script node.
    + * @param {Object} payload The payload that was sent to the server.
    + * @param {Function=} opt_errorCallback The function called on timeout.
    + * @return {!Function} A zero argument function that handles callback duties.
    + * @private
    + */
    +goog.net.Jsonp.newErrorHandler_ = function(id,
    +                                           payload,
    +                                           opt_errorCallback) {
    +  /**
    +   * When we call across domains with a request, this function is the
    +   * timeout handler. Once it's done executing the user-specified
    +   * error-handler, it removes the script node and original function.
    +   */
    +  return function() {
    +    goog.net.Jsonp.cleanup_(id, false);
    +    if (opt_errorCallback) {
    +      opt_errorCallback(payload);
    +    }
    +  };
    +};
    +
    +
    +/**
    + * Creates a reply callback that calls the given replyCallback with data
    + * returned by the server.
    + *
    + * @param {string} id The id of the script node.
    + * @param {Function} replyCallback The function called on reply.
    + * @return {!Function} A reply callback function.
    + * @private
    + */
    +goog.net.Jsonp.newReplyHandler_ = function(id, replyCallback) {
    +  /**
    +   * This function is the handler for the all-is-well response. It
    +   * clears the error timeout handler, calls the user's handler, then
    +   * removes the script node and itself.
    +   *
    +   * @param {...Object} var_args The response data sent from the server.
    +   */
    +  var handler = function(var_args) {
    +    goog.net.Jsonp.cleanup_(id, true);
    +    replyCallback.apply(undefined, arguments);
    +  };
    +  return handler;
    +};
    +
    +
    +/**
    + * Removes the script node and reply handler with the given id.
    + *
    + * @param {string} id The id of the script node to be removed.
    + * @param {boolean} deleteReplyHandler If true, delete the reply handler
    + *     instead of setting it to nullFunction (if we know the callback could
    + *     never be called again).
    + * @private
    + */
    +goog.net.Jsonp.cleanup_ = function(id, deleteReplyHandler) {
    +  if (goog.global[goog.net.Jsonp.CALLBACKS][id]) {
    +    if (deleteReplyHandler) {
    +      delete goog.global[goog.net.Jsonp.CALLBACKS][id];
    +    } else {
    +      // Removing the script tag doesn't necessarily prevent the script
    +      // from firing, so we make the callback a noop.
    +      goog.global[goog.net.Jsonp.CALLBACKS][id] = goog.nullFunction;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns URL encoded payload. The payload should be a map of name-value
    + * pairs, in the form {"foo": 1, "bar": true, ...}.  If the map is empty,
    + * the URI will be unchanged.
    + *
    + * <p>The method uses hasOwnProperty() to assure the properties are on the
    + * object, not on its prototype.
    + *
    + * @param {!Object} payload A map of value name pairs to be encoded.
    + *     A value may be specified as an array, in which case a query parameter
    + *     will be created for each value, e.g.:
    + *     {"foo": [1,2]} will encode to "foo=1&foo=2".
    + *
    + * @param {!goog.Uri} uri A Uri object onto which the payload key value pairs
    + *     will be encoded.
    + *
    + * @return {!goog.Uri} A reference to the Uri sent as a parameter.
    + * @private
    + */
    +goog.net.Jsonp.addPayloadToUri_ = function(payload, uri) {
    +  for (var name in payload) {
    +    // NOTE(user): Safari/1.3 doesn't have hasOwnProperty(). In that
    +    // case, we iterate over all properties as a very lame workaround.
    +    if (!payload.hasOwnProperty || payload.hasOwnProperty(name)) {
    +      uri.setParameterValues(name, payload[name]);
    +    }
    +  }
    +  return uri;
    +};
    +
    +
    +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    +//
    +// This class allows us (Google) to send data from non-Google and thus
    +// UNTRUSTED pages to our servers. Under NO CIRCUMSTANCES return
    +// anything sensitive, such as session or cookie specific data. Return
    +// only data that you want parties external to Google to have. Also
    +// NEVER use this method to send data from web pages to untrusted
    +// servers, or redirects to unknown servers (www.google.com/cache,
    +// /q=xx&btnl, /url, www.googlepages.com, etc.)
    +//
    +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsonp_test.html b/mapbox-gl/closure-library/closure/goog/net/jsonp_test.html
    new file mode 100644
    index 0000000000..45898f4421
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsonp_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.Jsonp
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.JsonpTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/jsonp_test.js b/mapbox-gl/closure-library/closure/goog/net/jsonp_test.js
    new file mode 100644
    index 0000000000..0a1e19c98c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/jsonp_test.js
    @@ -0,0 +1,321 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.JsonpTest');
    +goog.setTestOnly('goog.net.JsonpTest');
    +
    +goog.require('goog.net.Jsonp');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +// Global vars to facilitate a shared set up function.
    +
    +var timeoutWasCalled;
    +var timeoutHandler;
    +
    +var fakeUrl = 'http://fake-site.eek/';
    +
    +var originalTimeout;
    +function setUp() {
    +  timeoutWasCalled = false;
    +  timeoutHandler = null;
    +  originalTimeout = window.setTimeout;
    +  window.setTimeout = function(handler, time) {
    +    timeoutWasCalled = true;
    +    timeoutHandler = handler;
    +  };
    +}
    +
    +// Firefox throws a JS error when a script is not found.  We catch that here and
    +// ensure the test case doesn't fail because of it.
    +var originalOnError = window.onerror;
    +window.onerror = function(msg, url, line) {
    +  // TODO(user): Safari 3 on the farm returns an object instead of the typcial
    +  // params.  Pass through errors for safari for now.
    +  if (goog.userAgent.WEBKIT ||
    +      msg == 'Error loading script' && url.indexOf('fake-site') != -1) {
    +    return true;
    +  } else {
    +    return originalOnError && originalOnError(msg, url, line);
    +  }
    +};
    +
    +function tearDown() {
    +  window.setTimeout = originalTimeout;
    +}
    +
    +// Quick function records the before-state of the DOM, and then return a
    +// a function to check that XDC isn't leaving stuff behind.
    +function newCleanupGuard() {
    +  var bodyChildCount = document.body.childNodes.length;
    +
    +  return function() {
    +    // let any timeout queues finish before we check these:
    +    window.setTimeout(function() {
    +      var propCounter = 0;
    +
    +      // All callbacks should have been deleted or be the null function.
    +      for (var id in goog.global[goog.net.Jsonp.CALLBACKS]) {
    +        if (goog.global[goog.net.Jsonp.CALLBACKS][id] != goog.nullFunction) {
    +          propCounter++;
    +        }
    +      }
    +
    +      assertEquals(
    +          'script cleanup', bodyChildCount, document.body.childNodes.length);
    +      assertEquals('window jsonp array empty', 0, propCounter);
    +    }, 0);
    +  }
    +}
    +
    +function getScriptElement(result) {
    +  return result.deferred_.defaultScope_.script_;
    +}
    +
    +
    +// Check that send function is sane when things go well.
    +function testSend() {
    +  var replyReceived;
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +
    +  var checkCleanup = newCleanupGuard();
    +
    +  var userCallback = function(data) {
    +    replyReceived = data;
    +  };
    +
    +  var payload = {atisket: 'atasket', basket: 'yellow'};
    +  var result = jsonp.send(payload, userCallback);
    +
    +  var script = getScriptElement(result);
    +
    +  assertNotNull('script created', script);
    +  assertEquals('encoding is utf-8', 'UTF-8', script.charset);
    +
    +  // Check that the URL matches our payload.
    +  assertTrue('payload in url', script.src.indexOf('basket=yellow') > -1);
    +  assertTrue('server url', script.src.indexOf(fakeUrl) == 0);
    +
    +  // Now, we have to track down the name of the callback function, so we can
    +  // call that to simulate a returned request + verify that the callback
    +  // function does not break if it receives a second unexpected parameter.
    +  var callbackName = /callback=([^&]+)/.exec(script.src)[1];
    +  var callbackFunc = eval(callbackName);
    +  callbackFunc({some: 'data', another: ['data', 'right', 'here']},
    +      'unexpected');
    +  assertEquals('input was received', 'right', replyReceived.another[1]);
    +
    +  // Because the callbackFunc calls cleanUp_ and that calls setTimeout which
    +  // we have overwritten, we have to call the timeoutHandler to actually do
    +  // the cleaning.
    +  timeoutHandler();
    +
    +  checkCleanup();
    +  timeoutHandler();
    +}
    +
    +
    +// Check that send function is sane when things go well.
    +function testSendWhenCallbackHasTwoParameters() {
    +  var replyReceived, replyReceived2;
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +
    +  var checkCleanup = newCleanupGuard();
    +
    +  var userCallback = function(data, opt_data2) {
    +    replyReceived = data;
    +    replyReceived2 = opt_data2;
    +  };
    +
    +  var payload = {atisket: 'atasket', basket: 'yellow'};
    +  var result = jsonp.send(payload, userCallback);
    +  var script = getScriptElement(result);
    +
    +  // Test a callback function that receives two parameters.
    +  var callbackName = /callback=([^&]+)/.exec(script.src)[1];
    +  var callbackFunc = eval(callbackName);
    +  callbackFunc('param1', {some: 'data', another: ['data', 'right', 'here']});
    +  assertEquals('input was received', 'param1', replyReceived);
    +  assertEquals('second input was received', 'right',
    +      replyReceived2.another[1]);
    +
    +  // Because the callbackFunc calls cleanUp_ and that calls setTimeout which
    +  // we have overwritten, we have to call the timeoutHandler to actually do
    +  // the cleaning.
    +  timeoutHandler();
    +
    +  checkCleanup();
    +  timeoutHandler();
    +}
    +
    +// Check that send function works correctly when callback param value is
    +// specified.
    +function testSendWithCallbackParamValue() {
    +  var replyReceived;
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +
    +  var checkCleanup = newCleanupGuard();
    +
    +  var userCallback = function(data) {
    +    replyReceived = data;
    +  };
    +
    +  var payload = {atisket: 'atasket', basket: 'yellow'};
    +  var result = jsonp.send(payload, userCallback, undefined, 'dummyId');
    +
    +  var script = getScriptElement(result);
    +
    +  assertNotNull('script created', script);
    +  assertEquals('encoding is utf-8', 'UTF-8', script.charset);
    +
    +  // Check that the URL matches our payload.
    +  assertTrue('payload in url', script.src.indexOf('basket=yellow') > -1);
    +  assertTrue('dummyId in url',
    +      script.src.indexOf('callback=_callbacks_.dummyId') > -1);
    +  assertTrue('server url', script.src.indexOf(fakeUrl) == 0);
    +
    +  // Now, we simulate a returned request using the known callback function
    +  // name.
    +  var callbackFunc = _callbacks_.dummyId;
    +  callbackFunc({some: 'data', another: ['data', 'right', 'here']});
    +  assertEquals('input was received', 'right', replyReceived.another[1]);
    +
    +  // Because the callbackFunc calls cleanUp_ and that calls setTimeout which
    +  // we have overwritten, we have to call the timeoutHandler to actually do
    +  // the cleaning.
    +  timeoutHandler();
    +
    +  checkCleanup();
    +  timeoutHandler();
    +}
    +
    +
    +// Check that the send function is sane when the thing goes south.
    +function testSendFailure() {
    +  var replyReceived = false;
    +  var errorReplyReceived = false;
    +
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +
    +  var checkCleanup = newCleanupGuard();
    +
    +  var userCallback = function(data) {
    +    replyReceived = data;
    +  };
    +  var userErrorCallback = function(data) {
    +    errorReplyReceived = data;
    +  };
    +
    +  var payload = { justa: 'test' };
    +
    +  jsonp.send(payload, userCallback, userErrorCallback);
    +
    +  assertTrue('timeout called', timeoutWasCalled);
    +
    +  // Now, simulate the time running out, so we go into error mode.
    +  // After jsonp.send(), the timeoutHandler now is the Jsonp.cleanUp_ function.
    +  timeoutHandler();
    +  // But that function also calls a setTimeout(), so it changes the timeout
    +  // handler once again, so to actually clean up we have to call the
    +  // timeoutHandler() once again. Fun!
    +  timeoutHandler();
    +
    +  assertFalse('standard callback not called', replyReceived);
    +
    +  // The user's error handler should be called back with the same payload
    +  // passed back to it.
    +  assertEquals('error handler called', 'test', errorReplyReceived.justa);
    +
    +  // Check that the relevant cleanup has occurred.
    +  checkCleanup();
    +  // Check cleanup just calls setTimeout so we have to call the handler to
    +  // actually check that the cleanup worked.
    +  timeoutHandler();
    +}
    +
    +
    +// Check that a cancel call works and cleans up after itself.
    +function testCancel() {
    +  var checkCleanup = newCleanupGuard();
    +
    +  var successCalled = false;
    +  var successCallback = function() {
    +    successCalled = true;
    +  };
    +
    +  // Send and cancel a request, then make sure it was cleaned up.
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +  var requestObject = jsonp.send({test: 'foo'}, successCallback);
    +  jsonp.cancel(requestObject);
    +
    +  for (var key in goog.global[goog.net.Jsonp.CALLBACKS]) {
    +    assertNotEquals('The success callback should have been removed',
    +                    goog.global[goog.net.Jsonp.CALLBACKS][key],
    +                    successCallback);
    +  }
    +
    +  // Make sure cancelling removes the script tag
    +  checkCleanup();
    +  timeoutHandler();
    +}
    +
    +function testPayloadParameters() {
    +  var checkCleanup = newCleanupGuard();
    +
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +  var result = jsonp.send({
    +    'foo': 3,
    +    'bar': 'baz'
    +  });
    +
    +  var script = getScriptElement(result);
    +  assertEquals('Payload parameters should have been added to url.',
    +               fakeUrl + '?foo=3&bar=baz',
    +               script.src);
    +
    +  checkCleanup();
    +  timeoutHandler();
    +}
    +
    +function testOptionalPayload() {
    +  var checkCleanup = newCleanupGuard();
    +
    +  var errorCallback = goog.testing.recordFunction();
    +
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(goog.global, 'setTimeout', function(errorHandler) {
    +    errorHandler();
    +  });
    +
    +  var jsonp = new goog.net.Jsonp(fakeUrl);
    +  var result = jsonp.send(null, null, errorCallback);
    +
    +  var script = getScriptElement(result);
    +  assertEquals('Parameters should not have been added to url.',
    +               fakeUrl, script.src);
    +
    +  // Clear the script hooks because we triggered the error manually.
    +  script.onload = goog.nullFunction;
    +  script.onerror = goog.nullFunction;
    +  script.onreadystatechange = goog.nullFunction;
    +
    +  var errorCallbackArguments = errorCallback.getLastCall().getArguments();
    +  assertEquals(1, errorCallbackArguments.length);
    +  assertNull(errorCallbackArguments[0]);
    +
    +  checkCleanup();
    +  stubs.reset();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/mockiframeio.js b/mapbox-gl/closure-library/closure/goog/net/mockiframeio.js
    new file mode 100644
    index 0000000000..058817abc6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/mockiframeio.js
    @@ -0,0 +1,308 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock of IframeIo for unit testing.
    + */
    +
    +goog.provide('goog.net.MockIFrameIo');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.IframeIo');
    +
    +
    +
    +/**
    + * Mock implenetation of goog.net.IframeIo. This doesn't provide a mock
    + * implementation for all cases, but it's not too hard to add them as needed.
    + * @param {goog.testing.TestQueue} testQueue Test queue for inserting test
    + *     events.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.net.MockIFrameIo = function(testQueue) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Queue of events write to
    +   * @type {goog.testing.TestQueue}
    +   * @private
    +   */
    +  this.testQueue_ = testQueue;
    +
    +};
    +goog.inherits(goog.net.MockIFrameIo, goog.events.EventTarget);
    +
    +
    +/**
    + * Whether MockIFrameIo is active.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.active_ = false;
    +
    +
    +/**
    + * Last content.
    + * @type {string}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.lastContent_ = '';
    +
    +
    +/**
    + * Last error code.
    + * @type {goog.net.ErrorCode}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +
    +
    +/**
    + * Last error message.
    + * @type {string}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.lastError_ = '';
    +
    +
    +/**
    + * Last custom error.
    + * @type {Object}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.lastCustomError_ = null;
    +
    +
    +/**
    + * Last URI.
    + * @type {goog.Uri}
    + * @private
    + */
    +goog.net.MockIFrameIo.prototype.lastUri_ = null;
    +
    +
    +/** @private {Function} */
    +goog.net.MockIFrameIo.prototype.errorChecker_;
    +
    +
    +/** @private {boolean} */
    +goog.net.MockIFrameIo.prototype.success_;
    +
    +
    +/** @private {boolean} */
    +goog.net.MockIFrameIo.prototype.complete_;
    +
    +
    +/**
    + * Simulates the iframe send.
    + *
    + * @param {goog.Uri|string} uri Uri of the request.
    + * @param {string=} opt_method Default is GET, POST uses a form to submit the
    + *     request.
    + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid
    + *     caching.
    + * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs.
    + */
    +goog.net.MockIFrameIo.prototype.send = function(uri, opt_method, opt_noCache,
    +                                                opt_data) {
    +  if (this.active_) {
    +    throw Error('[goog.net.IframeIo] Unable to send, already active.');
    +  }
    +
    +  this.testQueue_.enqueue(['s', uri, opt_method, opt_noCache, opt_data]);
    +  this.complete_ = false;
    +  this.active_ = true;
    +};
    +
    +
    +/**
    + * Simulates the iframe send from a form.
    + * @param {Element} form Form element used to send the request to the server.
    + * @param {string=} opt_uri Uri to set for the destination of the request, by
    + *     default the uri will come from the form.
    + * @param {boolean=} opt_noCache Append a timestamp to the request to avoid
    + *     caching.
    + */
    +goog.net.MockIFrameIo.prototype.sendFromForm = function(form, opt_uri,
    +    opt_noCache) {
    +  if (this.active_) {
    +    throw Error('[goog.net.IframeIo] Unable to send, already active.');
    +  }
    +
    +  this.testQueue_.enqueue(['s', form, opt_uri, opt_noCache]);
    +  this.complete_ = false;
    +  this.active_ = true;
    +};
    +
    +
    +/**
    + * Simulates aborting the current Iframe request.
    + * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
    + *     defaults to ABORT.
    + */
    +goog.net.MockIFrameIo.prototype.abort = function(opt_failureCode) {
    +  if (this.active_) {
    +    this.testQueue_.enqueue(['a', opt_failureCode]);
    +    this.complete_ = false;
    +    this.active_ = false;
    +    this.success_ = false;
    +    this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
    +    this.dispatchEvent(goog.net.EventType.ABORT);
    +    this.simulateReady();
    +  }
    +};
    +
    +
    +/**
    + * Simulates receive of incremental data.
    + * @param {Object} data Data.
    + */
    +goog.net.MockIFrameIo.prototype.simulateIncrementalData = function(data) {
    +  this.dispatchEvent(new goog.net.IframeIo.IncrementalDataEvent(data));
    +};
    +
    +
    +/**
    + * Simulates the iframe is done.
    + * @param {goog.net.ErrorCode} errorCode The error code for any error that
    + *     should be simulated.
    + */
    +goog.net.MockIFrameIo.prototype.simulateDone = function(errorCode) {
    +  if (errorCode) {
    +    this.success_ = false;
    +    this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
    +    this.lastError_ = this.getLastError();
    +    this.dispatchEvent(goog.net.EventType.ERROR);
    +  } else {
    +    this.success_ = true;
    +    this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +    this.dispatchEvent(goog.net.EventType.SUCCESS);
    +  }
    +  this.complete_ = true;
    +  this.dispatchEvent(goog.net.EventType.COMPLETE);
    +};
    +
    +
    +/**
    + * Simulates the IFrame is ready for the next request.
    + */
    +goog.net.MockIFrameIo.prototype.simulateReady = function() {
    +  this.dispatchEvent(goog.net.EventType.READY);
    +};
    +
    +
    +/**
    + * @return {boolean} True if transfer is complete.
    + */
    +goog.net.MockIFrameIo.prototype.isComplete = function() {
    +  return this.complete_;
    +};
    +
    +
    +/**
    + * @return {boolean} True if transfer was successful.
    + */
    +goog.net.MockIFrameIo.prototype.isSuccess = function() {
    +  return this.success_;
    +};
    +
    +
    +/**
    + * @return {boolean} True if a transfer is in progress.
    + */
    +goog.net.MockIFrameIo.prototype.isActive = function() {
    +  return this.active_;
    +};
    +
    +
    +/**
    + * Returns the last response text (i.e. the text content of the iframe).
    + * Assumes plain text!
    + * @return {string} Result from the server.
    + */
    +goog.net.MockIFrameIo.prototype.getResponseText = function() {
    +  return this.lastContent_;
    +};
    +
    +
    +/**
    + * Parses the content as JSON. This is a safe parse and may throw an error
    + * if the response is malformed.
    + * @return {Object} The parsed content.
    + */
    +goog.net.MockIFrameIo.prototype.getResponseJson = function() {
    +  return goog.json.parse(this.lastContent_);
    +};
    +
    +
    +/**
    + * Get the uri of the last request.
    + * @return {goog.Uri} Uri of last request.
    + */
    +goog.net.MockIFrameIo.prototype.getLastUri = function() {
    +  return this.lastUri_;
    +};
    +
    +
    +/**
    + * Gets the last error code.
    + * @return {goog.net.ErrorCode} Last error code.
    + */
    +goog.net.MockIFrameIo.prototype.getLastErrorCode = function() {
    +  return this.lastErrorCode_;
    +};
    +
    +
    +/**
    + * Gets the last error message.
    + * @return {string} Last error message.
    + */
    +goog.net.MockIFrameIo.prototype.getLastError = function() {
    +  return goog.net.ErrorCode.getDebugMessage(this.lastErrorCode_);
    +};
    +
    +
    +/**
    + * Gets the last custom error.
    + * @return {Object} Last custom error.
    + */
    +goog.net.MockIFrameIo.prototype.getLastCustomError = function() {
    +  return this.lastCustomError_;
    +};
    +
    +
    +/**
    + * Sets the callback function used to check if a loaded IFrame is in an error
    + * state.
    + * @param {Function} fn Callback that expects a document object as it's single
    + *     argument.
    + */
    +goog.net.MockIFrameIo.prototype.setErrorChecker = function(fn) {
    +  this.errorChecker_ = fn;
    +};
    +
    +
    +/**
    + * Gets the callback function used to check if a loaded IFrame is in an error
    + * state.
    + * @return {Function} A callback that expects a document object as it's single
    + *     argument.
    + */
    +goog.net.MockIFrameIo.prototype.getErrorChecker = function() {
    +  return this.errorChecker_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor.js b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor.js
    new file mode 100644
    index 0000000000..76d0ae47b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor.js
    @@ -0,0 +1,118 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class that can be used to determine when multiple iframes have
    + * been loaded. Refactored from static APIs in IframeLoadMonitor.
    + */
    +goog.provide('goog.net.MultiIframeLoadMonitor');
    +
    +goog.require('goog.events');
    +goog.require('goog.net.IframeLoadMonitor');
    +
    +
    +
    +/**
    + * Provides a wrapper around IframeLoadMonitor, to allow the caller to wait for
    + * multiple iframes to load.
    + *
    + * @param {Array<HTMLIFrameElement>} iframes Array of iframe elements to
    + *     wait until they are loaded.
    + * @param {function():void} callback The callback to invoke once the frames have
    + *     loaded.
    + * @param {boolean=} opt_hasContent true if the monitor should wait until the
    + *     iframes have content (body.firstChild != null).
    + * @constructor
    + * @final
    + */
    +goog.net.MultiIframeLoadMonitor = function(iframes, callback, opt_hasContent) {
    +  /**
    +   * Array of IframeLoadMonitors we use to track the loaded status of any
    +   * currently unloaded iframes.
    +   * @type {Array<goog.net.IframeLoadMonitor>}
    +   * @private
    +   */
    +  this.pendingIframeLoadMonitors_ = [];
    +
    +  /**
    +   * Callback which is invoked when all of the iframes are loaded.
    +   * @type {function():void}
    +   * @private
    +   */
    +  this.callback_ = callback;
    +
    +  for (var i = 0; i < iframes.length; i++) {
    +    var iframeLoadMonitor = new goog.net.IframeLoadMonitor(
    +        iframes[i], opt_hasContent);
    +    if (iframeLoadMonitor.isLoaded()) {
    +      // Already loaded - don't need to wait
    +      iframeLoadMonitor.dispose();
    +    } else {
    +      // Iframe isn't loaded yet - register to be notified when it is
    +      // loaded, and track this monitor so we can dispose later as
    +      // required.
    +      this.pendingIframeLoadMonitors_.push(iframeLoadMonitor);
    +      goog.events.listen(
    +          iframeLoadMonitor, goog.net.IframeLoadMonitor.LOAD_EVENT, this);
    +    }
    +  }
    +  if (!this.pendingIframeLoadMonitors_.length) {
    +    // All frames were already loaded
    +    this.callback_();
    +  }
    +};
    +
    +
    +/**
    + * Handles a pending iframe load monitor load event.
    + * @param {goog.events.Event} e The goog.net.IframeLoadMonitor.LOAD_EVENT event.
    + */
    +goog.net.MultiIframeLoadMonitor.prototype.handleEvent = function(e) {
    +  var iframeLoadMonitor = e.target;
    +  // iframeLoadMonitor is now loaded, remove it from the array of
    +  // pending iframe load monitors.
    +  for (var i = 0; i < this.pendingIframeLoadMonitors_.length; i++) {
    +    if (this.pendingIframeLoadMonitors_[i] == iframeLoadMonitor) {
    +      this.pendingIframeLoadMonitors_.splice(i, 1);
    +      break;
    +    }
    +  }
    +
    +  // Disposes of the iframe load monitor.  We created this iframe load monitor
    +  // and installed the single listener on it, so it is safe to dispose it
    +  // in the middle of this event handler.
    +  iframeLoadMonitor.dispose();
    +
    +  // If there are no more pending iframe load monitors, all the iframes
    +  // have loaded, and so we invoke the callback.
    +  if (!this.pendingIframeLoadMonitors_.length) {
    +    this.callback_();
    +  }
    +};
    +
    +
    +/**
    + * Stops monitoring the iframes, cleaning up any associated resources. In
    + * general, the object cleans up its own resources before invoking the
    + * callback, so this API should only be used if the caller wants to stop the
    + * monitoring before the iframes are loaded (for example, if the caller is
    + * implementing a timeout).
    + */
    +goog.net.MultiIframeLoadMonitor.prototype.stopMonitoring = function() {
    +  for (var i = 0; i < this.pendingIframeLoadMonitors_.length; i++) {
    +    this.pendingIframeLoadMonitors_[i].dispose();
    +  }
    +  this.pendingIframeLoadMonitors_.length = 0;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.html b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.html
    new file mode 100644
    index 0000000000..99a73a6b64
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.MultiIframeLoadMonitor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.MultiIframeLoadMonitorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="frame_parent">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.js b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.js
    new file mode 100644
    index 0000000000..0b58eec567
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/multiiframeloadmonitor_test.js
    @@ -0,0 +1,162 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.MultiIframeLoadMonitorTest');
    +goog.setTestOnly('goog.net.MultiIframeLoadMonitorTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.net.IframeLoadMonitor');
    +goog.require('goog.net.MultiIframeLoadMonitor');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_FRAME_SRCS = ['iframeloadmonitor_test_frame.html',
    +  'iframeloadmonitor_test_frame2.html',
    +  'iframeloadmonitor_test_frame3.html'];
    +
    +// Create a new test case.
    +var iframeLoaderTestCase = new goog.testing.AsyncTestCase(document.title);
    +iframeLoaderTestCase.stepTimeout = 4 * 1000;
    +
    +// How many multpile frames finished loading
    +iframeLoaderTestCase.multipleComplete_ = 0;
    +
    +iframeLoaderTestCase.numMonitors = 0;
    +iframeLoaderTestCase.disposeCalled = 0;
    +
    +
    +/**
    + * Sets up the test environment, adds tests and sets up the worker pools.
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.setUpPage = function() {
    +  this.log('Setting tests up');
    +  iframeLoaderTestCase.waitForAsync('loading iframes');
    +
    +  var dom = goog.dom.getDomHelper();
    +
    +  // Load multiple with callback
    +  var frame1 = dom.createDom('iframe');
    +  var frame2 = dom.createDom('iframe');
    +  var multiMonitor = new goog.net.MultiIframeLoadMonitor(
    +      [frame1, frame2], goog.bind(this.multipleCallback, this));
    +  this.log('Loading frames at: ' + TEST_FRAME_SRCS[0] + ' and ' +
    +      TEST_FRAME_SRCS[1]);
    +  // Make sure they don't look loaded yet.
    +  assertEquals(0, this.multipleComplete_);
    +  var frameParent = dom.getElement('frame_parent');
    +  dom.appendChild(frameParent, frame1);
    +  frame1.src = TEST_FRAME_SRCS[0];
    +  dom.appendChild(frameParent, frame2);
    +  frame2.src = TEST_FRAME_SRCS[1];
    +
    +  // Load multiple with callback and content check
    +  var frame3 = dom.createDom('iframe');
    +  var frame4 = dom.createDom('iframe');
    +  var multiMonitor = new goog.net.MultiIframeLoadMonitor(
    +      [frame3, frame4], goog.bind(this.multipleContentCallback, this), true);
    +  this.log('Loading frames with content check at: ' + TEST_FRAME_SRCS[1] +
    +      ' and ' + TEST_FRAME_SRCS[2]);
    +  dom.appendChild(frameParent, frame3);
    +  frame3.src = TEST_FRAME_SRCS[1];
    +  dom.appendChild(frameParent, frame4);
    +  frame4.src = TEST_FRAME_SRCS[2];
    +
    +
    +};
    +
    +
    +/**
    + * Callback for the multiple frame load test case
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.multipleCallback = function() {
    +  this.log('multiple frames finished loading');
    +  this.multipleComplete_++;
    +  this.multipleCompleteNoContent_ = true;
    +  this.callbacksComplete();
    +};
    +
    +
    +/**
    + * Callback for the multiple frame with content load test case
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.multipleContentCallback = function() {
    +  this.log('multiple frames with content finished loading');
    +  this.multipleComplete_++;
    +  this.multipleCompleteContent_ = true;
    +  this.callbacksComplete();
    +};
    +
    +
    +/**
    + * Checks if all the load callbacks are done
    + * @this {goog.testing.AsyncTestCase}
    + */
    +iframeLoaderTestCase.callbacksComplete = function() {
    +  if (this.multipleComplete_ == 2) {
    +    iframeLoaderTestCase.continueTesting();
    +  }
    +};
    +
    +
    +/** Tests the results. */
    +iframeLoaderTestCase.addNewTest('testResults', function() {
    +  this.log('getting test results');
    +  assertTrue(this.multipleCompleteNoContent_);
    +  assertTrue(this.multipleCompleteContent_);
    +});
    +
    +iframeLoaderTestCase.fakeLoadMonitor = function() {
    +  // Replaces IframeLoadMonitor with a fake version that just tracks
    +  // instantiations/disposals
    +  this.loadMonitorConstructor = goog.net.IframeLoadMonitor;
    +  var that = this;
    +  goog.net.IframeLoadMonitor = function() {
    +    that.numMonitors++;
    +    return {
    +      isLoaded: function() { return false; },
    +      dispose: function() { that.disposeCalled++; },
    +      attachEvent: function() {}
    +    };
    +  };
    +  goog.net.IframeLoadMonitor.LOAD_EVENT = 'ifload';
    +};
    +
    +iframeLoaderTestCase.unfakeLoadMonitor = function() {
    +  goog.net.IframeLoadMonitor = this.loadMonitorConstructor;
    +};
    +
    +iframeLoaderTestCase.addNewTest('stopMonitoring', function() {
    +  // create two unloaded frames, make sure that load monitors are loaded
    +  // behind the scenes, then make sure they are disposed properly.
    +  this.fakeLoadMonitor();
    +  var dom = goog.dom.getDomHelper();
    +  var frames = [dom.createDom('iframe'), dom.createDom('iframe')];
    +  var multiMonitor = new goog.net.MultiIframeLoadMonitor(
    +      frames,
    +      function() {
    +        fail('should not invoke callback for unloaded rames');
    +      });
    +  assertEquals(frames.length, this.numMonitors);
    +  assertEquals(0, this.disposeCalled);
    +  multiMonitor.stopMonitoring();
    +  assertEquals(frames.length, this.disposeCalled);
    +  this.unfakeLoadMonitor();
    +});
    +
    +
    +/** Standalone Closure Test Runner. */
    +G_testRunner.initialize(iframeLoaderTestCase);
    diff --git a/mapbox-gl/closure-library/closure/goog/net/networkstatusmonitor.js b/mapbox-gl/closure-library/closure/goog/net/networkstatusmonitor.js
    new file mode 100644
    index 0000000000..b0f81d2b69
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/networkstatusmonitor.js
    @@ -0,0 +1,47 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for objects monitoring and exposing runtime
    + * network status information.
    + */
    +
    +goog.provide('goog.net.NetworkStatusMonitor');
    +
    +goog.require('goog.events.Listenable');
    +
    +
    +
    +/**
    + * Base class for network status information providers.
    + * @interface
    + * @extends {goog.events.Listenable}
    + */
    +goog.net.NetworkStatusMonitor = function() {};
    +
    +
    +/**
    + * Enum for the events dispatched by the OnlineHandler.
    + * @enum {string}
    + */
    +goog.net.NetworkStatusMonitor.EventType = {
    +  ONLINE: 'online',
    +  OFFLINE: 'offline'
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the system is online or otherwise.
    + */
    +goog.net.NetworkStatusMonitor.prototype.isOnline;
    diff --git a/mapbox-gl/closure-library/closure/goog/net/networktester.js b/mapbox-gl/closure-library/closure/goog/net/networktester.js
    new file mode 100644
    index 0000000000..eec925b889
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/networktester.js
    @@ -0,0 +1,397 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of goog.net.NetworkTester.
    + */
    +
    +goog.provide('goog.net.NetworkTester');
    +goog.require('goog.Timer');
    +goog.require('goog.Uri');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Creates an instance of goog.net.NetworkTester which can be used to test
    + * for internet connectivity by seeing if an image can be loaded from
    + * google.com. It can also be tested with other URLs.
    + * @param {Function} callback Callback that is called when the test completes.
    + *     The callback takes a single boolean parameter. True indicates the URL
    + *     was reachable, false indicates it wasn't.
    + * @param {Object=} opt_handler Handler object for the callback.
    + * @param {goog.Uri=} opt_uri URI to use for testing.
    + * @constructor @struct
    + * @final
    + */
    +goog.net.NetworkTester = function(callback, opt_handler, opt_uri) {
    +  /**
    +   * Callback that is called when the test completes.
    +   * The callback takes a single boolean parameter. True indicates the URL was
    +   * reachable, false indicates it wasn't.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.callback_ = callback;
    +
    +  /**
    +   * Handler object for the callback.
    +   * @type {Object|undefined}
    +   * @private
    +   */
    +  this.handler_ = opt_handler;
    +
    +  if (!opt_uri) {
    +    // set the default URI to be based on the cleardot image at google.com
    +    // We need to add a 'rand' to make sure the response is not fulfilled
    +    // by browser cache. Use protocol-relative URLs to avoid insecure content
    +    // warnings in IE.
    +    opt_uri = new goog.Uri('//www.google.com/images/cleardot.gif');
    +    opt_uri.makeUnique();
    +  }
    +
    +  /**
    +   * Uri to use for test. Defaults to using an image off of google.com
    +   * @type {goog.Uri}
    +   * @private
    +   */
    +  this.uri_ = opt_uri;
    +};
    +
    +
    +/**
    + * Default timeout
    + * @type {number}
    + */
    +goog.net.NetworkTester.DEFAULT_TIMEOUT_MS = 10000;
    +
    +
    +/**
    + * Logger object
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.logger_ =
    +    goog.log.getLogger('goog.net.NetworkTester');
    +
    +
    +/**
    + * Timeout for test
    + * @type {number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.timeoutMs_ =
    +    goog.net.NetworkTester.DEFAULT_TIMEOUT_MS;
    +
    +
    +/**
    + * Whether we've already started running.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.running_ = false;
    +
    +
    +/**
    + * Number of retries to attempt
    + * @type {number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.retries_ = 0;
    +
    +
    +/**
    + * Attempt number we're on
    + * @type {number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.attempt_ = 0;
    +
    +
    +/**
    + * Pause between retries in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.pauseBetweenRetriesMs_ = 0;
    +
    +
    +/**
    + * Timer for timeouts.
    + * @type {?number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.timeoutTimer_ = null;
    +
    +
    +/**
    + * Timer for pauses between retries.
    + * @type {?number}
    + * @private
    + */
    +goog.net.NetworkTester.prototype.pauseTimer_ = null;
    +
    +
    +/** @private {?Image} */
    +goog.net.NetworkTester.prototype.image_;
    +
    +
    +/**
    + * Returns the timeout in milliseconds.
    + * @return {number} Timeout in milliseconds.
    + */
    +goog.net.NetworkTester.prototype.getTimeout = function() {
    +  return this.timeoutMs_;
    +};
    +
    +
    +/**
    + * Sets the timeout in milliseconds.
    + * @param {number} timeoutMs Timeout in milliseconds.
    + */
    +goog.net.NetworkTester.prototype.setTimeout = function(timeoutMs) {
    +  this.timeoutMs_ = timeoutMs;
    +};
    +
    +
    +/**
    + * Returns the numer of retries to attempt.
    + * @return {number} Number of retries to attempt.
    + */
    +goog.net.NetworkTester.prototype.getNumRetries = function() {
    +  return this.retries_;
    +};
    +
    +
    +/**
    + * Sets the timeout in milliseconds.
    + * @param {number} retries Number of retries to attempt.
    + */
    +goog.net.NetworkTester.prototype.setNumRetries = function(retries) {
    +  this.retries_ = retries;
    +};
    +
    +
    +/**
    + * Returns the pause between retries in milliseconds.
    + * @return {number} Pause between retries in milliseconds.
    + */
    +goog.net.NetworkTester.prototype.getPauseBetweenRetries = function() {
    +  return this.pauseBetweenRetriesMs_;
    +};
    +
    +
    +/**
    + * Sets the pause between retries in milliseconds.
    + * @param {number} pauseMs Pause between retries in milliseconds.
    + */
    +goog.net.NetworkTester.prototype.setPauseBetweenRetries = function(pauseMs) {
    +  this.pauseBetweenRetriesMs_ = pauseMs;
    +};
    +
    +
    +/**
    + * Returns the uri to use for the test.
    + * @return {goog.Uri} The uri for the test.
    + */
    +goog.net.NetworkTester.prototype.getUri = function() {
    +  return this.uri_;
    +};
    +
    +
    +/**
    + * Returns the current attempt count.
    + * @return {number} The attempt count.
    + */
    +goog.net.NetworkTester.prototype.getAttemptCount = function() {
    +  return this.attempt_;
    +};
    +
    +
    +/**
    + * Sets the uri to use for the test.
    + * @param {goog.Uri} uri The uri for the test.
    + */
    +goog.net.NetworkTester.prototype.setUri = function(uri) {
    +  this.uri_ = uri;
    +};
    +
    +
    +/**
    + * Returns whether the tester is currently running.
    + * @return {boolean} True if it's running, false if it's not running.
    + */
    +goog.net.NetworkTester.prototype.isRunning = function() {
    +  return this.running_;
    +};
    +
    +
    +/**
    + * Starts the process of testing the network.
    + */
    +goog.net.NetworkTester.prototype.start = function() {
    +  if (this.running_) {
    +    throw Error('NetworkTester.start called when already running');
    +  }
    +  this.running_ = true;
    +
    +  goog.log.info(this.logger_, 'Starting');
    +  this.attempt_ = 0;
    +  this.startNextAttempt_();
    +};
    +
    +
    +/**
    + * Stops the testing of the network. This is a noop if not running.
    + */
    +goog.net.NetworkTester.prototype.stop = function() {
    +  this.cleanupCallbacks_();
    +  this.running_ = false;
    +};
    +
    +
    +/**
    + * Starts the next attempt to load an image.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.startNextAttempt_ = function() {
    +  this.attempt_++;
    +
    +  if (goog.net.NetworkTester.getNavigatorOffline_()) {
    +    goog.log.info(this.logger_, 'Browser is set to work offline.');
    +    // Call in a timeout to make async like the rest.
    +    goog.Timer.callOnce(goog.bind(this.onResult, this, false), 0);
    +  } else {
    +    goog.log.info(this.logger_, 'Loading image (attempt ' + this.attempt_ +
    +                      ') at ' + this.uri_);
    +    this.image_ = new Image();
    +    this.image_.onload = goog.bind(this.onImageLoad_, this);
    +    this.image_.onerror = goog.bind(this.onImageError_, this);
    +    this.image_.onabort = goog.bind(this.onImageAbort_, this);
    +
    +    this.timeoutTimer_ = goog.Timer.callOnce(this.onImageTimeout_,
    +        this.timeoutMs_, this);
    +    this.image_.src = String(this.uri_);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether navigator.onLine returns false.
    + * @private
    + */
    +goog.net.NetworkTester.getNavigatorOffline_ = function() {
    +  return 'onLine' in navigator && !navigator.onLine;
    +};
    +
    +
    +/**
    + * Callback for the image successfully loading.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.onImageLoad_ = function() {
    +  goog.log.info(this.logger_, 'Image loaded');
    +  this.onResult(true);
    +};
    +
    +
    +/**
    + * Callback for the image failing to load.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.onImageError_ = function() {
    +  goog.log.info(this.logger_, 'Image load error');
    +  this.onResult(false);
    +};
    +
    +
    +/**
    + * Callback for the image load being aborted.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.onImageAbort_ = function() {
    +  goog.log.info(this.logger_, 'Image load aborted');
    +  this.onResult(false);
    +};
    +
    +
    +/**
    + * Callback for the image load timing out.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.onImageTimeout_ = function() {
    +  goog.log.info(this.logger_, 'Image load timed out');
    +  this.onResult(false);
    +};
    +
    +
    +/**
    + * Handles a successful or failed result.
    + * @param {boolean} succeeded Whether the image load succeeded.
    + */
    +goog.net.NetworkTester.prototype.onResult = function(succeeded) {
    +  this.cleanupCallbacks_();
    +
    +  if (succeeded) {
    +    this.running_ = false;
    +    this.callback_.call(this.handler_, true);
    +  } else {
    +    if (this.attempt_ <= this.retries_) {
    +      if (this.pauseBetweenRetriesMs_) {
    +        this.pauseTimer_ = goog.Timer.callOnce(this.onPauseFinished_,
    +            this.pauseBetweenRetriesMs_, this);
    +      } else {
    +        this.startNextAttempt_();
    +      }
    +    } else {
    +      this.running_ = false;
    +      this.callback_.call(this.handler_, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Callback for the pause between retry timer.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.onPauseFinished_ = function() {
    +  this.pauseTimer_ = null;
    +  this.startNextAttempt_();
    +};
    +
    +
    +/**
    + * Cleans up the handlers and timer associated with the image.
    + * @private
    + */
    +goog.net.NetworkTester.prototype.cleanupCallbacks_ = function() {
    +  // clear handlers to avoid memory leaks
    +  // NOTE(user): Nullified individually to avoid compiler warnings
    +  // (BUG 658126)
    +  if (this.image_) {
    +    this.image_.onload = null;
    +    this.image_.onerror = null;
    +    this.image_.onabort = null;
    +    this.image_ = null;
    +  }
    +  if (this.timeoutTimer_) {
    +    goog.Timer.clear(this.timeoutTimer_);
    +    this.timeoutTimer_ = null;
    +  }
    +  if (this.pauseTimer_) {
    +    goog.Timer.clear(this.pauseTimer_);
    +    this.pauseTimer_ = null;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/networktester_test.html b/mapbox-gl/closure-library/closure/goog/net/networktester_test.html
    new file mode 100644
    index 0000000000..0d433a53df
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/networktester_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.NetworkTester
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.net.NetworkTesterTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/networktester_test.js b/mapbox-gl/closure-library/closure/goog/net/networktester_test.js
    new file mode 100644
    index 0000000000..85a3c5d087
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/networktester_test.js
    @@ -0,0 +1,237 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.NetworkTesterTest');
    +goog.setTestOnly('goog.net.NetworkTesterTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.net.NetworkTester');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var clock;
    +
    +function setUp() {
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDown() {
    +  clock.dispose();
    +}
    +
    +function testSuccess() {
    +  // set up the tster
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // simulate the image load and verify
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onload.call(null);
    +  assertTrue(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +}
    +
    +function testFailure() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // simulate the image failure and verify
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onerror.call(null);
    +  assertFalse(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +}
    +
    +function testAbort() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // simulate the image abort and verify
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onabort.call(null);
    +  assertFalse(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +}
    +
    +function testTimeout() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // simulate the image timeout and verify
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  clock.tick(10000);
    +  assertFalse(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +}
    +
    +function testRetries() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  tester.setNumRetries(1);
    +  assertEquals(tester.getAttemptCount(), 0);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +  assertEquals(tester.getAttemptCount(), 1);
    +
    +  // try number 1 fails
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onerror.call(null);
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +  assertEquals(tester.getAttemptCount(), 2);
    +
    +  // try number 2 succeeds
    +  image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onload.call(null);
    +  assertTrue(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +  assertEquals(tester.getAttemptCount(), 2);
    +}
    +
    +function testPauseBetweenRetries() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  tester.setNumRetries(1);
    +  tester.setPauseBetweenRetries(1000);
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // try number 1 fails
    +  var image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onerror.call(null);
    +  assertTrue(handler.isEmpty());
    +  assertTrue(tester.isRunning());
    +
    +  // need to pause 1000 ms for the second attempt
    +  assertNull(tester.image_);
    +  clock.tick(1000);
    +
    +  // try number 2 succeeds
    +  image = tester.image_;
    +  assertEquals(String(tester.getUri()), image.src);
    +  assertTrue(handler.isEmpty());
    +  image.onload.call(null);
    +  assertTrue(handler.dequeue());
    +  assertFalse(tester.isRunning());
    +}
    +
    +function testNonDefaultUri() {
    +  var handler = new Handler();
    +  var newUri = new goog.Uri('//www.google.com/images/cleardot2.gif');
    +  var tester = new goog.net.NetworkTester(handler.callback, handler, newUri);
    +  var testerUri = tester.getUri();
    +  assertTrue(testerUri.toString().indexOf('cleardot2') > -1);
    +}
    +
    +function testOffline() {
    +
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  var orgGetNavigatorOffline = goog.net.NetworkTester.getNavigatorOffline_;
    +  goog.net.NetworkTester.getNavigatorOffline_ = function() {
    +    return true;
    +  };
    +  try {
    +    assertFalse(tester.isRunning());
    +    tester.start();
    +    assertTrue(handler.isEmpty());
    +    assertTrue(tester.isRunning());
    +
    +    // the call is done async
    +    clock.tick(1);
    +
    +    assertFalse(handler.dequeue());
    +    assertFalse(tester.isRunning());
    +  } finally {
    +    // Clean up!
    +    goog.net.NetworkTester.getNavigatorOffline_ = orgGetNavigatorOffline;
    +  }
    +}
    +
    +// Handler object for verifying callback
    +function Handler() {
    +  this.events_ = [];
    +}
    +
    +function testGetAttemptCount() {
    +  // set up the tester
    +  var handler = new Handler();
    +  var tester = new goog.net.NetworkTester(handler.callback, handler);
    +  assertEquals(tester.getAttemptCount(), 0);
    +  assertTrue(tester.attempt_ === tester.getAttemptCount());
    +  assertFalse(tester.isRunning());
    +  tester.start();
    +  assertTrue(tester.isRunning());
    +  assertTrue(tester.attempt_ === tester.getAttemptCount());
    +}
    +
    +Handler.prototype.callback = function(result) {
    +  this.events_.push(result);
    +};
    +
    +Handler.prototype.isEmpty = function() {
    +  return this.events_.length == 0;
    +};
    +
    +Handler.prototype.dequeue = function() {
    +  if (this.isEmpty()) {
    +    throw Error('Handler is empty');
    +  }
    +  return this.events_.shift();
    +};
    +
    +// override image constructor for test - can't use a real image due to
    +// async load of images - have to simulate it
    +function Image() {
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test1.js b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test1.js
    new file mode 100644
    index 0000000000..59d21621ba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test1.js
    @@ -0,0 +1,23 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview Test #1 of jsloader.
    + */
    +
    +goog.provide('goog.net.testdata.jsloader_test1');
    +goog.setTestOnly('jsloader_test1');
    +
    +window['test1'] = 'Test #1 loaded';
    diff --git a/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test2.js b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test2.js
    new file mode 100644
    index 0000000000..8690539840
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test2.js
    @@ -0,0 +1,23 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview Test #2 of jsloader.
    + */
    +
    +goog.provide('goog.net.testdata.jsloader_test2');
    +goog.setTestOnly('jsloader_test2');
    +
    +window['closure_verification']['test2'] = 'Test #2 loaded';
    diff --git a/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test3.js b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test3.js
    new file mode 100644
    index 0000000000..7c1181dcc1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test3.js
    @@ -0,0 +1,23 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview Test #3 of jsloader.
    + */
    +
    +goog.provide('goog.net.testdata.jsloader_test3');
    +goog.setTestOnly('jsloader_test3');
    +
    +window['test3Callback']('Test #3 loaded');
    diff --git a/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test4.js b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test4.js
    new file mode 100644
    index 0000000000..591209c2bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/testdata/jsloader_test4.js
    @@ -0,0 +1,23 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview Test #4 of jsloader.
    + */
    +
    +goog.provide('goog.net.testdata.jsloader_test4');
    +goog.setTestOnly('jsloader_test4');
    +
    +window['test4Callback']('Test #4 loaded');
    diff --git a/mapbox-gl/closure-library/closure/goog/net/tmpnetwork.js b/mapbox-gl/closure-library/closure/goog/net/tmpnetwork.js
    new file mode 100644
    index 0000000000..53954a6ca7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/tmpnetwork.js
    @@ -0,0 +1,164 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview tmpnetwork.js contains some temporary networking functions
    + * for browserchannel which will be moved at a later date.
    + */
    +
    +
    +/**
    + * Namespace for BrowserChannel
    + */
    +goog.provide('goog.net.tmpnetwork');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.net.ChannelDebug');
    +
    +
    +/**
    + * Default timeout to allow for google.com pings.
    + * @type {number}
    + */
    +goog.net.tmpnetwork.GOOGLECOM_TIMEOUT = 10000;
    +
    +
    +/**
    + * Pings the network to check if an error is a server error or user's network
    + * error.
    + *
    + * @param {Function} callback The function to call back with results.
    + * @param {goog.Uri?=} opt_imageUri The URI of an image to use for the network
    + *     test. You *must* provide an image URI; the default behavior is provided
    + *     for compatibility with existing code, but the search team does not want
    + *     people using images served off of google.com for this purpose. The
    + *     default will go away when all usages have been changed.
    + */
    +goog.net.tmpnetwork.testGoogleCom = function(callback, opt_imageUri) {
    +  // We need to add a 'rand' to make sure the response is not fulfilled
    +  // by browser cache.
    +  var uri = opt_imageUri;
    +  if (!uri) {
    +    uri = new goog.Uri('//www.google.com/images/cleardot.gif');
    +    uri.makeUnique();
    +  }
    +  goog.net.tmpnetwork.testLoadImage(uri.toString(),
    +      goog.net.tmpnetwork.GOOGLECOM_TIMEOUT, callback);
    +};
    +
    +
    +/**
    + * Test loading the given image, retrying if necessary.
    + * @param {string} url URL to the iamge.
    + * @param {number} timeout Milliseconds before giving up.
    + * @param {Function} callback Function to call with results.
    + * @param {number} retries The number of times to retry.
    + * @param {number=} opt_pauseBetweenRetriesMS Optional number of milliseconds
    + *     between retries - defaults to 0.
    + */
    +goog.net.tmpnetwork.testLoadImageWithRetries = function(url, timeout, callback,
    +    retries, opt_pauseBetweenRetriesMS) {
    +  var channelDebug = new goog.net.ChannelDebug();
    +  channelDebug.debug('TestLoadImageWithRetries: ' + opt_pauseBetweenRetriesMS);
    +  if (retries == 0) {
    +    // no more retries, give up
    +    callback(false);
    +    return;
    +  }
    +
    +  var pauseBetweenRetries = opt_pauseBetweenRetriesMS || 0;
    +  retries--;
    +  goog.net.tmpnetwork.testLoadImage(url, timeout, function(succeeded) {
    +    if (succeeded) {
    +      callback(true);
    +    } else {
    +      // try again
    +      goog.global.setTimeout(function() {
    +        goog.net.tmpnetwork.testLoadImageWithRetries(url, timeout, callback,
    +            retries, pauseBetweenRetries);
    +      }, pauseBetweenRetries);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Test loading the given image.
    + * @param {string} url URL to the iamge.
    + * @param {number} timeout Milliseconds before giving up.
    + * @param {Function} callback Function to call with results.
    + */
    +goog.net.tmpnetwork.testLoadImage = function(url, timeout, callback) {
    +  var channelDebug = new goog.net.ChannelDebug();
    +  channelDebug.debug('TestLoadImage: loading ' + url);
    +  var img = new Image();
    +  img.onload = function() {
    +    try {
    +      channelDebug.debug('TestLoadImage: loaded');
    +      goog.net.tmpnetwork.clearImageCallbacks_(img);
    +      callback(true);
    +    } catch (e) {
    +      channelDebug.dumpException(e);
    +    }
    +  };
    +  img.onerror = function() {
    +    try {
    +      channelDebug.debug('TestLoadImage: error');
    +      goog.net.tmpnetwork.clearImageCallbacks_(img);
    +      callback(false);
    +    } catch (e) {
    +      channelDebug.dumpException(e);
    +    }
    +  };
    +  img.onabort = function() {
    +    try {
    +      channelDebug.debug('TestLoadImage: abort');
    +      goog.net.tmpnetwork.clearImageCallbacks_(img);
    +      callback(false);
    +    } catch (e) {
    +      channelDebug.dumpException(e);
    +    }
    +  };
    +  img.ontimeout = function() {
    +    try {
    +      channelDebug.debug('TestLoadImage: timeout');
    +      goog.net.tmpnetwork.clearImageCallbacks_(img);
    +      callback(false);
    +    } catch (e) {
    +      channelDebug.dumpException(e);
    +    }
    +  };
    +
    +  goog.global.setTimeout(function() {
    +    if (img.ontimeout) {
    +      img.ontimeout();
    +    }
    +  }, timeout);
    +  img.src = url;
    +};
    +
    +
    +/**
    + * Clear handlers to avoid memory leaks.
    + * @param {Image} img The image to clear handlers from.
    + * @private
    + */
    +goog.net.tmpnetwork.clearImageCallbacks_ = function(img) {
    +  // NOTE(user): Nullified individually to avoid compiler warnings
    +  // (BUG 658126)
    +  img.onload = null;
    +  img.onerror = null;
    +  img.onabort = null;
    +  img.ontimeout = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/websocket.js b/mapbox-gl/closure-library/closure/goog/net/websocket.js
    new file mode 100644
    index 0000000000..b3f7ace097
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/websocket.js
    @@ -0,0 +1,513 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the WebSocket class.  A WebSocket provides a
    + * bi-directional, full-duplex communications channel, over a single TCP socket.
    + *
    + * See http://dev.w3.org/html5/websockets/
    + * for the full HTML5 WebSocket API.
    + *
    + * Typical usage will look like this:
    + *
    + *  var ws = new goog.net.WebSocket();
    + *
    + *  var handler = new goog.events.EventHandler();
    + *  handler.listen(ws, goog.net.WebSocket.EventType.OPENED, onOpen);
    + *  handler.listen(ws, goog.net.WebSocket.EventType.MESSAGE, onMessage);
    + *
    + *  try {
    + *    ws.open('ws://127.0.0.1:4200');
    + *  } catch (e) {
    + *    ...
    + *  }
    + *
    + */
    +
    +goog.provide('goog.net.WebSocket');
    +goog.provide('goog.net.WebSocket.ErrorEvent');
    +goog.provide('goog.net.WebSocket.EventType');
    +goog.provide('goog.net.WebSocket.MessageEvent');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.log');
    +
    +
    +
    +/**
    + * Class encapsulating the logic for using a WebSocket.
    + *
    + * @param {boolean=} opt_autoReconnect True if the web socket should
    + *     automatically reconnect or not.  This is true by default.
    + * @param {function(number):number=} opt_getNextReconnect A function for
    + *     obtaining the time until the next reconnect attempt. Given the reconnect
    + *     attempt count (which is a positive integer), the function should return a
    + *     positive integer representing the milliseconds to the next reconnect
    + *     attempt.  The default function used is an exponential back-off. Note that
    + *     this function is never called if auto reconnect is disabled.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.net.WebSocket = function(opt_autoReconnect, opt_getNextReconnect) {
    +  goog.net.WebSocket.base(this, 'constructor');
    +
    +  /**
    +   * True if the web socket should automatically reconnect or not.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.autoReconnect_ = goog.isDef(opt_autoReconnect) ?
    +      opt_autoReconnect : true;
    +
    +  /**
    +   * A function for obtaining the time until the next reconnect attempt.
    +   * Given the reconnect attempt count (which is a positive integer), the
    +   * function should return a positive integer representing the milliseconds to
    +   * the next reconnect attempt.
    +   * @type {function(number):number}
    +   * @private
    +   */
    +  this.getNextReconnect_ = opt_getNextReconnect ||
    +      goog.net.WebSocket.EXPONENTIAL_BACKOFF_;
    +
    +  /**
    +   * The time, in milliseconds, that must elapse before the next attempt to
    +   * reconnect.
    +   * @type {number}
    +   * @private
    +   */
    +  this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
    +};
    +goog.inherits(goog.net.WebSocket, goog.events.EventTarget);
    +
    +
    +/**
    + * The actual web socket that will be used to send/receive messages.
    + * @type {WebSocket}
    + * @private
    + */
    +goog.net.WebSocket.prototype.webSocket_ = null;
    +
    +
    +/**
    + * The URL to which the web socket will connect.
    + * @type {?string}
    + * @private
    + */
    +goog.net.WebSocket.prototype.url_ = null;
    +
    +
    +/**
    + * The subprotocol name used when establishing the web socket connection.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.net.WebSocket.prototype.protocol_ = undefined;
    +
    +
    +/**
    + * True if a call to the close callback is expected or not.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.WebSocket.prototype.closeExpected_ = false;
    +
    +
    +/**
    + * Keeps track of the number of reconnect attempts made since the last
    + * successful connection.
    + * @type {number}
    + * @private
    + */
    +goog.net.WebSocket.prototype.reconnectAttempt_ = 0;
    +
    +
    +/** @private {?number} */
    +goog.net.WebSocket.prototype.reconnectTimer_ = null;
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.net.WebSocket.prototype.logger_ = goog.log.getLogger(
    +    'goog.net.WebSocket');
    +
    +
    +/**
    + * The events fired by the web socket.
    + * @enum {string} The event types for the web socket.
    + */
    +goog.net.WebSocket.EventType = {
    +
    +  /**
    +   * Fired when an attempt to open the WebSocket fails or there is a connection
    +   * failure after a successful connection has been established.
    +   */
    +  CLOSED: goog.events.getUniqueId('closed'),
    +
    +  /**
    +   * Fired when the WebSocket encounters an error.
    +   */
    +  ERROR: goog.events.getUniqueId('error'),
    +
    +  /**
    +   * Fired when a new message arrives from the WebSocket.
    +   */
    +  MESSAGE: goog.events.getUniqueId('message'),
    +
    +  /**
    +   * Fired when the WebSocket connection has been established.
    +   */
    +  OPENED: goog.events.getUniqueId('opened')
    +};
    +
    +
    +/**
    + * The various states of the web socket.
    + * @enum {number} The states of the web socket.
    + * @private
    + */
    +goog.net.WebSocket.ReadyState_ = {
    +  // This is the initial state during construction.
    +  CONNECTING: 0,
    +  // This is when the socket is actually open and ready for data.
    +  OPEN: 1,
    +  // This is when the socket is in the middle of a close handshake.
    +  // Note that this is a valid state even if the OPEN state was never achieved.
    +  CLOSING: 2,
    +  // This is when the socket is actually closed.
    +  CLOSED: 3
    +};
    +
    +
    +/**
    + * The maximum amount of time between reconnect attempts for the exponential
    + * back-off in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.net.WebSocket.EXPONENTIAL_BACKOFF_CEILING_ = 60 * 1000;
    +
    +
    +/**
    + * Computes the next reconnect time given the number of reconnect attempts since
    + * the last successful connection.
    + *
    + * @param {number} attempt The number of reconnect attempts since the last
    + *     connection.
    + * @return {number} The time, in milliseconds, until the next reconnect attempt.
    + * @const
    + * @private
    + */
    +goog.net.WebSocket.EXPONENTIAL_BACKOFF_ = function(attempt) {
    +  var time = Math.pow(2, attempt) * 1000;
    +  return Math.min(time, goog.net.WebSocket.EXPONENTIAL_BACKOFF_CEILING_);
    +};
    +
    +
    +/**
    + * Installs exception protection for all entry points introduced by
    + * goog.net.WebSocket instances which are not protected by
    + * {@link goog.debug.ErrorHandler#protectWindowSetTimeout},
    + * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or
    + * {@link goog.events.protectBrowserEventEntryPoint}.
    + *
    + * @param {!goog.debug.ErrorHandler} errorHandler Error handler with which to
    + *     protect the entry points.
    + */
    +goog.net.WebSocket.protectEntryPoints = function(errorHandler) {
    +  goog.net.WebSocket.prototype.onOpen_ = errorHandler.protectEntryPoint(
    +      goog.net.WebSocket.prototype.onOpen_);
    +  goog.net.WebSocket.prototype.onClose_ = errorHandler.protectEntryPoint(
    +      goog.net.WebSocket.prototype.onClose_);
    +  goog.net.WebSocket.prototype.onMessage_ = errorHandler.protectEntryPoint(
    +      goog.net.WebSocket.prototype.onMessage_);
    +  goog.net.WebSocket.prototype.onError_ = errorHandler.protectEntryPoint(
    +      goog.net.WebSocket.prototype.onError_);
    +};
    +
    +
    +/**
    + * Creates and opens the actual WebSocket.  Only call this after attaching the
    + * appropriate listeners to this object.  If listeners aren't registered, then
    + * the {@code goog.net.WebSocket.EventType.OPENED} event might be missed.
    + *
    + * @param {string} url The URL to which to connect.
    + * @param {string=} opt_protocol The subprotocol to use.  The connection will
    + *     only be established if the server reports that it has selected this
    + *     subprotocol. The subprotocol name must all be a non-empty ASCII string
    + *     with no control characters and no spaces in them (i.e. only characters
    + *     in the range U+0021 to U+007E).
    + */
    +goog.net.WebSocket.prototype.open = function(url, opt_protocol) {
    +  // Sanity check.  This works only in modern browsers.
    +  goog.asserts.assert(goog.global['WebSocket'],
    +      'This browser does not support WebSocket');
    +
    +  // Don't do anything if the web socket is already open.
    +  goog.asserts.assert(!this.isOpen(), 'The WebSocket is already open');
    +
    +  // Clear any pending attempts to reconnect.
    +  this.clearReconnectTimer_();
    +
    +  // Construct the web socket.
    +  this.url_ = url;
    +  this.protocol_ = opt_protocol;
    +
    +  // This check has to be made otherwise you get protocol mismatch exceptions
    +  // for passing undefined, null, '', or [].
    +  if (this.protocol_) {
    +    goog.log.info(this.logger_, 'Opening the WebSocket on ' + this.url_ +
    +        ' with protocol ' + this.protocol_);
    +    this.webSocket_ = new WebSocket(this.url_, this.protocol_);
    +  } else {
    +    goog.log.info(this.logger_, 'Opening the WebSocket on ' + this.url_);
    +    this.webSocket_ = new WebSocket(this.url_);
    +  }
    +
    +  // Register the event handlers.  Note that it is not possible for these
    +  // callbacks to be missed because it is registered after the web socket is
    +  // instantiated.  Because of the synchronous nature of JavaScript, this code
    +  // will execute before the browser creates the resource and makes any calls
    +  // to these callbacks.
    +  this.webSocket_.onopen = goog.bind(this.onOpen_, this);
    +  this.webSocket_.onclose = goog.bind(this.onClose_, this);
    +  this.webSocket_.onmessage = goog.bind(this.onMessage_, this);
    +  this.webSocket_.onerror = goog.bind(this.onError_, this);
    +};
    +
    +
    +/**
    + * Closes the web socket connection.
    + */
    +goog.net.WebSocket.prototype.close = function() {
    +
    +  // Clear any pending attempts to reconnect.
    +  this.clearReconnectTimer_();
    +
    +  // Attempt to close only if the web socket was created.
    +  if (this.webSocket_) {
    +    goog.log.info(this.logger_, 'Closing the WebSocket.');
    +
    +    // Close is expected here since it was a direct call.  Close is considered
    +    // unexpected when opening the connection fails or there is some other form
    +    // of connection loss after being connected.
    +    this.closeExpected_ = true;
    +    this.webSocket_.close();
    +    this.webSocket_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Sends the message over the web socket.
    + *
    + * @param {string} message The message to send.
    + */
    +goog.net.WebSocket.prototype.send = function(message) {
    +  // Make sure the socket is ready to go before sending a message.
    +  goog.asserts.assert(this.isOpen(), 'Cannot send without an open socket');
    +
    +  // Send the message and let onError_ be called if it fails thereafter.
    +  this.webSocket_.send(message);
    +};
    +
    +
    +/**
    + * Checks to see if the web socket is open or not.
    + *
    + * @return {boolean} True if the web socket is open, false otherwise.
    + */
    +goog.net.WebSocket.prototype.isOpen = function() {
    +  return !!this.webSocket_ &&
    +      this.webSocket_.readyState == goog.net.WebSocket.ReadyState_.OPEN;
    +};
    +
    +
    +/**
    + * Called when the web socket has connected.
    + *
    + * @private
    + */
    +goog.net.WebSocket.prototype.onOpen_ = function() {
    +  goog.log.info(this.logger_, 'WebSocket opened on ' + this.url_);
    +  this.dispatchEvent(goog.net.WebSocket.EventType.OPENED);
    +
    +  // Set the next reconnect interval.
    +  this.reconnectAttempt_ = 0;
    +  this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
    +};
    +
    +
    +/**
    + * Called when the web socket has closed.
    + *
    + * @param {!Event} event The close event.
    + * @private
    + */
    +goog.net.WebSocket.prototype.onClose_ = function(event) {
    +  goog.log.info(this.logger_, 'The WebSocket on ' + this.url_ + ' closed.');
    +
    +  // Firing this event allows handlers to query the URL.
    +  this.dispatchEvent(goog.net.WebSocket.EventType.CLOSED);
    +
    +  // Always clear out the web socket on a close event.
    +  this.webSocket_ = null;
    +
    +  // See if this is an expected call to onClose_.
    +  if (this.closeExpected_) {
    +    goog.log.info(this.logger_, 'The WebSocket closed normally.');
    +    // Only clear out the URL if this is a normal close.
    +    this.url_ = null;
    +    this.protocol_ = undefined;
    +  } else {
    +    // Unexpected, so try to reconnect.
    +    goog.log.error(this.logger_, 'The WebSocket disconnected unexpectedly: ' +
    +        event.data);
    +
    +    // Only try to reconnect if it is enabled.
    +    if (this.autoReconnect_) {
    +      // Log the reconnect attempt.
    +      var seconds = Math.floor(this.nextReconnect_ / 1000);
    +      goog.log.info(this.logger_,
    +          'Seconds until next reconnect attempt: ' + seconds);
    +
    +      // Actually schedule the timer.
    +      this.reconnectTimer_ = goog.Timer.callOnce(
    +          goog.bind(this.open, this, this.url_, this.protocol_),
    +          this.nextReconnect_, this);
    +
    +      // Set the next reconnect interval.
    +      this.reconnectAttempt_++;
    +      this.nextReconnect_ = this.getNextReconnect_(this.reconnectAttempt_);
    +    }
    +  }
    +  this.closeExpected_ = false;
    +};
    +
    +
    +/**
    + * Called when a new message arrives from the server.
    + *
    + * @param {MessageEvent<string>} event The web socket message event.
    + * @private
    + */
    +goog.net.WebSocket.prototype.onMessage_ = function(event) {
    +  var message = event.data;
    +  this.dispatchEvent(new goog.net.WebSocket.MessageEvent(message));
    +};
    +
    +
    +/**
    + * Called when there is any error in communication.
    + *
    + * @param {Event} event The error event containing the error data.
    + * @private
    + */
    +goog.net.WebSocket.prototype.onError_ = function(event) {
    +  var data = /** @type {string} */ (event.data);
    +  goog.log.error(this.logger_, 'An error occurred: ' + data);
    +  this.dispatchEvent(new goog.net.WebSocket.ErrorEvent(data));
    +};
    +
    +
    +/**
    + * Clears the reconnect timer.
    + *
    + * @private
    + */
    +goog.net.WebSocket.prototype.clearReconnectTimer_ = function() {
    +  if (goog.isDefAndNotNull(this.reconnectTimer_)) {
    +    goog.Timer.clear(this.reconnectTimer_);
    +  }
    +  this.reconnectTimer_ = null;
    +};
    +
    +
    +/** @override */
    +goog.net.WebSocket.prototype.disposeInternal = function() {
    +  goog.net.WebSocket.base(this, 'disposeInternal');
    +  this.close();
    +};
    +
    +
    +
    +/**
    + * Object representing a new incoming message event.
    + *
    + * @param {string} message The raw message coming from the web socket.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.net.WebSocket.MessageEvent = function(message) {
    +  goog.net.WebSocket.MessageEvent.base(
    +      this, 'constructor', goog.net.WebSocket.EventType.MESSAGE);
    +
    +  /**
    +   * The new message from the web socket.
    +   * @type {string}
    +   */
    +  this.message = message;
    +};
    +goog.inherits(goog.net.WebSocket.MessageEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * Object representing an error event. This is fired whenever an error occurs
    + * on the web socket.
    + *
    + * @param {string} data The error data.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.net.WebSocket.ErrorEvent = function(data) {
    +  goog.net.WebSocket.ErrorEvent.base(
    +      this, 'constructor', goog.net.WebSocket.EventType.ERROR);
    +
    +  /**
    +   * The error data coming from the web socket.
    +   * @type {string}
    +   */
    +  this.data = data;
    +};
    +goog.inherits(goog.net.WebSocket.ErrorEvent, goog.events.Event);
    +
    +
    +// Register the WebSocket as an entry point, so that it can be monitored for
    +// exception handling, etc.
    +goog.debug.entryPointRegistry.register(
    +    /**
    +     * @param {function(!Function): !Function} transformer The transforming
    +     *     function.
    +     */
    +    function(transformer) {
    +      goog.net.WebSocket.prototype.onOpen_ =
    +          transformer(goog.net.WebSocket.prototype.onOpen_);
    +      goog.net.WebSocket.prototype.onClose_ =
    +          transformer(goog.net.WebSocket.prototype.onClose_);
    +      goog.net.WebSocket.prototype.onMessage_ =
    +          transformer(goog.net.WebSocket.prototype.onMessage_);
    +      goog.net.WebSocket.prototype.onError_ =
    +          transformer(goog.net.WebSocket.prototype.onError_);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/net/websocket_test.html b/mapbox-gl/closure-library/closure/goog/net/websocket_test.html
    new file mode 100644
    index 0000000000..1d5e8333e5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/websocket_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.WebSocket
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.net.WebSocketTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/websocket_test.js b/mapbox-gl/closure-library/closure/goog/net/websocket_test.js
    new file mode 100644
    index 0000000000..36539cc4ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/websocket_test.js
    @@ -0,0 +1,363 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.WebSocketTest');
    +goog.setTestOnly('goog.net.WebSocketTest');
    +
    +goog.require('goog.debug.EntryPointMonitor');
    +goog.require('goog.debug.ErrorHandler');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.net.WebSocket');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var webSocket;
    +var mockClock;
    +var pr;
    +var testUrl;
    +
    +var originalOnOpen = goog.net.WebSocket.prototype.onOpen_;
    +var originalOnClose = goog.net.WebSocket.prototype.onClose_;
    +var originalOnMessage = goog.net.WebSocket.prototype.onMessage_;
    +var originalOnError = goog.net.WebSocket.prototype.onError_;
    +
    +function setUp() {
    +  pr = new goog.testing.PropertyReplacer();
    +  pr.set(goog.global, 'WebSocket', MockWebSocket);
    +  mockClock = new goog.testing.MockClock(true);
    +  testUrl = 'ws://127.0.0.1:4200';
    +  testProtocol = 'xmpp';
    +}
    +
    +function tearDown() {
    +  pr.reset();
    +  goog.net.WebSocket.prototype.onOpen_ = originalOnOpen;
    +  goog.net.WebSocket.prototype.onClose_ = originalOnClose;
    +  goog.net.WebSocket.prototype.onMessage_ = originalOnMessage;
    +  goog.net.WebSocket.prototype.onError_ = originalOnError;
    +  goog.dispose(mockClock);
    +  goog.dispose(webSocket);
    +}
    +
    +function testOpenInUnsupportingBrowserThrowsException() {
    +  // Null out WebSocket to simulate lack of support.
    +  if (goog.global.WebSocket) {
    +    goog.global.WebSocket = null;
    +  }
    +
    +  webSocket = new goog.net.WebSocket();
    +  assertThrows('Open should fail if WebSocket is not defined.',
    +      function() {
    +        webSocket.open(testUrl);
    +      });
    +}
    +
    +function testOpenTwiceThrowsException() {
    +  webSocket = new goog.net.WebSocket();
    +  webSocket.open(testUrl);
    +  simulateOpenEvent(webSocket.webSocket_);
    +
    +  assertThrows('Attempting to open a second time should fail.',
    +      function() {
    +        webSocket.open(testUrl);
    +      });
    +}
    +
    +function testSendWithoutOpeningThrowsException() {
    +  webSocket = new goog.net.WebSocket();
    +
    +  assertThrows('Send should fail if the web socket was not first opened.',
    +      function() {
    +        webSocket.send('test message');
    +      });
    +}
    +
    +function testOpenWithProtocol() {
    +  webSocket = new goog.net.WebSocket();
    +  webSocket.open(testUrl, testProtocol);
    +  var ws = webSocket.webSocket_;
    +  simulateOpenEvent(ws);
    +  assertEquals(testUrl, ws.url);
    +  assertEquals(testProtocol, ws.protocol);
    +}
    +
    +function testOpenAndClose() {
    +  webSocket = new goog.net.WebSocket();
    +  assertFalse(webSocket.isOpen());
    +  webSocket.open(testUrl);
    +  var ws = webSocket.webSocket_;
    +  simulateOpenEvent(ws);
    +  assertTrue(webSocket.isOpen());
    +  assertEquals(testUrl, ws.url);
    +  webSocket.close();
    +  simulateCloseEvent(ws);
    +  assertFalse(webSocket.isOpen());
    +}
    +
    +function testReconnectionDisabled() {
    +  // Construct the web socket and disable reconnection.
    +  webSocket = new goog.net.WebSocket(false);
    +
    +  // Record how many times open is called.
    +  pr.set(webSocket, 'open', goog.testing.recordFunction(webSocket.open));
    +
    +  // Open the web socket.
    +  webSocket.open(testUrl);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +  assertFalse(webSocket.isOpen());
    +
    +  // Simulate failure.
    +  var ws = webSocket.webSocket_;
    +  simulateCloseEvent(ws);
    +  assertFalse(webSocket.isOpen());
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +
    +  // Make sure a reconnection doesn't happen.
    +  mockClock.tick(100000);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +}
    +
    +function testReconnectionWithFailureOnFirstOpen() {
    +  // Construct the web socket with a linear back-off.
    +  webSocket = new goog.net.WebSocket(true, linearBackOff);
    +
    +  // Record how many times open is called.
    +  pr.set(webSocket, 'open', goog.testing.recordFunction(webSocket.open));
    +
    +  // Open the web socket.
    +  webSocket.open(testUrl, testProtocol);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +  assertFalse(webSocket.isOpen());
    +
    +  // Simulate failure.
    +  var ws = webSocket.webSocket_;
    +  simulateCloseEvent(ws);
    +  assertFalse(webSocket.isOpen());
    +  assertEquals(1, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +
    +  // Make sure the reconnect doesn't happen before it should.
    +  mockClock.tick(linearBackOff(0) - 1);
    +  assertEquals(1, webSocket.open.getCallCount());
    +  mockClock.tick(1);
    +  assertEquals(2, webSocket.open.getCallCount());
    +
    +  // Simulate another failure.
    +  simulateCloseEvent(ws);
    +  assertFalse(webSocket.isOpen());
    +  assertEquals(2, webSocket.reconnectAttempt_);
    +  assertEquals(2, webSocket.open.getCallCount());
    +
    +  // Make sure the reconnect doesn't happen before it should.
    +  mockClock.tick(linearBackOff(1) - 1);
    +  assertEquals(2, webSocket.open.getCallCount());
    +  mockClock.tick(1);
    +  assertEquals(3, webSocket.open.getCallCount());
    +
    +  // Simulate connection success.
    +  simulateOpenEvent(ws);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(3, webSocket.open.getCallCount());
    +
    +  // Make sure the reconnection has the same url and protocol.
    +  assertEquals(testUrl, ws.url);
    +  assertEquals(testProtocol, ws.protocol);
    +
    +  // Ensure no further calls to open are made.
    +  mockClock.tick(linearBackOff(10));
    +  assertEquals(3, webSocket.open.getCallCount());
    +}
    +
    +function testReconnectionWithFailureAfterOpen() {
    +  // Construct the web socket with a linear back-off.
    +  webSocket = new goog.net.WebSocket(true, fibonacciBackOff);
    +
    +  // Record how many times open is called.
    +  pr.set(webSocket, 'open', goog.testing.recordFunction(webSocket.open));
    +
    +  // Open the web socket.
    +  webSocket.open(testUrl);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +  assertFalse(webSocket.isOpen());
    +
    +  // Simulate connection success.
    +  var ws = webSocket.webSocket_;
    +  simulateOpenEvent(ws);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +
    +  // Let some time pass, then fail the connection.
    +  mockClock.tick(100000);
    +  simulateCloseEvent(ws);
    +  assertFalse(webSocket.isOpen());
    +  assertEquals(1, webSocket.reconnectAttempt_);
    +  assertEquals(1, webSocket.open.getCallCount());
    +
    +  // Make sure the reconnect doesn't happen before it should.
    +  mockClock.tick(fibonacciBackOff(0) - 1);
    +  assertEquals(1, webSocket.open.getCallCount());
    +  mockClock.tick(1);
    +  assertEquals(2, webSocket.open.getCallCount());
    +
    +  // Simulate connection success.
    +  ws = webSocket.webSocket_;
    +  simulateOpenEvent(ws);
    +  assertEquals(0, webSocket.reconnectAttempt_);
    +  assertEquals(2, webSocket.open.getCallCount());
    +
    +  // Ensure no further calls to open are made.
    +  mockClock.tick(fibonacciBackOff(10));
    +  assertEquals(2, webSocket.open.getCallCount());
    +}
    +
    +function testExponentialBackOff() {
    +  assertEquals(1000, goog.net.WebSocket.EXPONENTIAL_BACKOFF_(0));
    +  assertEquals(2000, goog.net.WebSocket.EXPONENTIAL_BACKOFF_(1));
    +  assertEquals(4000, goog.net.WebSocket.EXPONENTIAL_BACKOFF_(2));
    +  assertEquals(60000, goog.net.WebSocket.EXPONENTIAL_BACKOFF_(6));
    +  assertEquals(60000, goog.net.WebSocket.EXPONENTIAL_BACKOFF_(7));
    +}
    +
    +function testEntryPointRegistry() {
    +  var monitor = new goog.debug.EntryPointMonitor();
    +  var replacement = function() {};
    +  monitor.wrap = goog.testing.recordFunction(
    +      goog.functions.constant(replacement));
    +
    +  goog.debug.entryPointRegistry.monitorAll(monitor);
    +  assertTrue(monitor.wrap.getCallCount() >= 1);
    +  assertEquals(replacement, goog.net.WebSocket.prototype.onOpen_);
    +  assertEquals(replacement, goog.net.WebSocket.prototype.onClose_);
    +  assertEquals(replacement, goog.net.WebSocket.prototype.onMessage_);
    +  assertEquals(replacement, goog.net.WebSocket.prototype.onError_);
    +}
    +
    +function testErrorHandlerCalled() {
    +  var errorHandlerCalled = false;
    +  var errorHandler = new goog.debug.ErrorHandler(function() {
    +    errorHandlerCalled = true;
    +  });
    +  goog.net.WebSocket.protectEntryPoints(errorHandler);
    +
    +  webSocket = new goog.net.WebSocket();
    +  goog.events.listenOnce(webSocket, goog.net.WebSocket.EventType.OPENED,
    +      function() {
    +        throw Error();
    +      });
    +
    +  webSocket.open(testUrl);
    +  var ws = webSocket.webSocket_;
    +  assertThrows(function() {
    +    simulateOpenEvent(ws);
    +  });
    +
    +  assertTrue('Error handler callback should be called when registered as ' +
    +      'protecting the entry points.', errorHandlerCalled);
    +}
    +
    +
    +/**
    + * Simulates the browser firing the open event for the given web socket.
    + * @param {MockWebSocket} ws The mock web socket.
    + */
    +function simulateOpenEvent(ws) {
    +  ws.readyState = goog.net.WebSocket.ReadyState_.OPEN;
    +  ws.onopen();
    +}
    +
    +
    +/**
    + * Simulates the browser firing the close event for the given web socket.
    + * @param {MockWebSocket} ws The mock web socket.
    + */
    +function simulateCloseEvent(ws) {
    +  ws.readyState = goog.net.WebSocket.ReadyState_.CLOSED;
    +  ws.onclose({data: 'mock close event'});
    +}
    +
    +
    +/**
    + * Strategy for reconnection that backs off linearly with a 1 second offset.
    + * @param {number} attempt The number of reconnects since the last connection.
    + * @return {number} The amount of time to the next reconnect, in milliseconds.
    + */
    +function linearBackOff(attempt) {
    +  return (attempt * 1000) + 1000;
    +}
    +
    +
    +/**
    + * Strategy for reconnection that backs off with the fibonacci pattern.  It is
    + * offset by 5 seconds so the first attempt will happen after 5 seconds.
    + * @param {number} attempt The number of reconnects since the last connection.
    + * @return {number} The amount of time to the next reconnect, in milliseconds.
    + */
    +function fibonacciBackOff(attempt) {
    +  return (fibonacci(attempt) * 1000) + 5000;
    +}
    +
    +
    +/**
    + * Computes the desired fibonacci number.
    + * @param {number} n The nth desired fibonacci number.
    + * @return {number} The nth fibonacci number.
    + */
    +function fibonacci(n) {
    +  if (n == 0) {
    +    return 0;
    +  } else if (n == 1) {
    +    return 1;
    +  } else {
    +    return fibonacci(n - 2) + fibonacci(n - 1);
    +  }
    +}
    +
    +
    +
    +/**
    + * Mock WebSocket constructor.
    + * @param {string} url The url to the web socket server.
    + * @param {string} protocol The protocol to use.
    + * @constructor
    + */
    +MockWebSocket = function(url, protocol) {
    +  this.url = url;
    +  this.protocol = protocol;
    +  this.readyState = goog.net.WebSocket.ReadyState_.CONNECTING;
    +};
    +
    +
    +/**
    + * Mocks out the close method of the WebSocket.
    + */
    +MockWebSocket.prototype.close = function() {
    +  this.readyState = goog.net.WebSocket.ReadyState_.CLOSING;
    +};
    +
    +
    +/**
    + * Mocks out the send method of the WebSocket.
    + */
    +MockWebSocket.prototype.send = function() {
    +  // Nothing to do here.
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/wrapperxmlhttpfactory.js b/mapbox-gl/closure-library/closure/goog/net/wrapperxmlhttpfactory.js
    new file mode 100644
    index 0000000000..76156932ef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/wrapperxmlhttpfactory.js
    @@ -0,0 +1,71 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of XmlHttpFactory which allows construction from
    + * simple factory methods.
    + * @author dbk@google.com (David Barrett-Kahn)
    + */
    +
    +goog.provide('goog.net.WrapperXmlHttpFactory');
    +
    +/** @suppress {extraRequire} Typedef. */
    +goog.require('goog.net.XhrLike');
    +goog.require('goog.net.XmlHttpFactory');
    +
    +
    +
    +/**
    + * An xhr factory subclass which can be constructed using two factory methods.
    + * This exists partly to allow the preservation of goog.net.XmlHttp.setFactory()
    + * with an unchanged signature.
    + * @param {function():!goog.net.XhrLike.OrNative} xhrFactory
    + *     A function which returns a new XHR object.
    + * @param {function():!Object} optionsFactory A function which returns the
    + *     options associated with xhr objects from this factory.
    + * @extends {goog.net.XmlHttpFactory}
    + * @constructor
    + * @final
    + */
    +goog.net.WrapperXmlHttpFactory = function(xhrFactory, optionsFactory) {
    +  goog.net.XmlHttpFactory.call(this);
    +
    +  /**
    +   * XHR factory method.
    +   * @type {function() : !goog.net.XhrLike.OrNative}
    +   * @private
    +   */
    +  this.xhrFactory_ = xhrFactory;
    +
    +  /**
    +   * Options factory method.
    +   * @type {function() : !Object}
    +   * @private
    +   */
    +  this.optionsFactory_ = optionsFactory;
    +};
    +goog.inherits(goog.net.WrapperXmlHttpFactory, goog.net.XmlHttpFactory);
    +
    +
    +/** @override */
    +goog.net.WrapperXmlHttpFactory.prototype.createInstance = function() {
    +  return this.xhrFactory_();
    +};
    +
    +
    +/** @override */
    +goog.net.WrapperXmlHttpFactory.prototype.getOptions = function() {
    +  return this.optionsFactory_();
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrio.js b/mapbox-gl/closure-library/closure/goog/net/xhrio.js
    new file mode 100644
    index 0000000000..2edb794a58
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrio.js
    @@ -0,0 +1,1224 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wrapper class for handling XmlHttpRequests.
    + *
    + * One off requests can be sent through goog.net.XhrIo.send() or an
    + * instance can be created to send multiple requests.  Each request uses its
    + * own XmlHttpRequest object and handles clearing of the event callback to
    + * ensure no leaks.
    + *
    + * XhrIo is event based, it dispatches events when a request finishes, fails or
    + * succeeds or when the ready-state changes. The ready-state or timeout event
    + * fires first, followed by a generic completed event. Then the abort, error,
    + * or success event is fired as appropriate. Lastly, the ready event will fire
    + * to indicate that the object may be used to make another request.
    + *
    + * The error event may also be called before completed and
    + * ready-state-change if the XmlHttpRequest.open() or .send() methods throw.
    + *
    + * This class does not support multiple requests, queuing, or prioritization.
    + *
    + * Tested = IE6, FF1.5, Safari, Opera 8.5
    + *
    + * TODO(user): Error cases aren't playing nicely in Safari.
    + *
    + */
    +
    +
    +goog.provide('goog.net.XhrIo');
    +goog.provide('goog.net.XhrIo.ResponseType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.HttpStatus');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Map');
    +goog.require('goog.uri.utils');
    +goog.require('goog.userAgent');
    +
    +goog.forwardDeclare('goog.Uri');
    +
    +
    +
    +/**
    + * Basic class for handling XMLHttpRequests.
    + * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when
    + *     creating XMLHttpRequest objects.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.net.XhrIo = function(opt_xmlHttpFactory) {
    +  goog.net.XhrIo.base(this, 'constructor');
    +
    +  /**
    +   * Map of default headers to add to every request, use:
    +   * XhrIo.headers.set(name, value)
    +   * @type {!goog.structs.Map}
    +   */
    +  this.headers = new goog.structs.Map();
    +
    +  /**
    +   * Optional XmlHttpFactory
    +   * @private {goog.net.XmlHttpFactory}
    +   */
    +  this.xmlHttpFactory_ = opt_xmlHttpFactory || null;
    +
    +  /**
    +   * Whether XMLHttpRequest is active.  A request is active from the time send()
    +   * is called until onReadyStateChange() is complete, or error() or abort()
    +   * is called.
    +   * @private {boolean}
    +   */
    +  this.active_ = false;
    +
    +  /**
    +   * The XMLHttpRequest object that is being used for the transfer.
    +   * @private {?goog.net.XhrLike.OrNative}
    +   */
    +  this.xhr_ = null;
    +
    +  /**
    +   * The options to use with the current XMLHttpRequest object.
    +   * @private {Object}
    +   */
    +  this.xhrOptions_ = null;
    +
    +  /**
    +   * Last URL that was requested.
    +   * @private {string|goog.Uri}
    +   */
    +  this.lastUri_ = '';
    +
    +  /**
    +   * Method for the last request.
    +   * @private {string}
    +   */
    +  this.lastMethod_ = '';
    +
    +  /**
    +   * Last error code.
    +   * @private {!goog.net.ErrorCode}
    +   */
    +  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +
    +  /**
    +   * Last error message.
    +   * @private {Error|string}
    +   */
    +  this.lastError_ = '';
    +
    +  /**
    +   * Used to ensure that we don't dispatch an multiple ERROR events. This can
    +   * happen in IE when it does a synchronous load and one error is handled in
    +   * the ready statte change and one is handled due to send() throwing an
    +   * exception.
    +   * @private {boolean}
    +   */
    +  this.errorDispatched_ = false;
    +
    +  /**
    +   * Used to make sure we don't fire the complete event from inside a send call.
    +   * @private {boolean}
    +   */
    +  this.inSend_ = false;
    +
    +  /**
    +   * Used in determining if a call to {@link #onReadyStateChange_} is from
    +   * within a call to this.xhr_.open.
    +   * @private {boolean}
    +   */
    +  this.inOpen_ = false;
    +
    +  /**
    +   * Used in determining if a call to {@link #onReadyStateChange_} is from
    +   * within a call to this.xhr_.abort.
    +   * @private {boolean}
    +   */
    +  this.inAbort_ = false;
    +
    +  /**
    +   * Number of milliseconds after which an incomplete request will be aborted
    +   * and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout
    +   * is set.
    +   * @private {number}
    +   */
    +  this.timeoutInterval_ = 0;
    +
    +  /**
    +   * Timer to track request timeout.
    +   * @private {?number}
    +   */
    +  this.timeoutId_ = null;
    +
    +  /**
    +   * The requested type for the response. The empty string means use the default
    +   * XHR behavior.
    +   * @private {goog.net.XhrIo.ResponseType}
    +   */
    +  this.responseType_ = goog.net.XhrIo.ResponseType.DEFAULT;
    +
    +  /**
    +   * Whether a "credentialed" request is to be sent (one that is aware of
    +   * cookies and authentication). This is applicable only for cross-domain
    +   * requests and more recent browsers that support this part of the HTTP Access
    +   * Control standard.
    +   *
    +   * @see http://www.w3.org/TR/XMLHttpRequest/#the-withcredentials-attribute
    +   *
    +   * @private {boolean}
    +   */
    +  this.withCredentials_ = false;
    +
    +  /**
    +   * True if we can use XMLHttpRequest's timeout directly.
    +   * @private {boolean}
    +   */
    +  this.useXhr2Timeout_ = false;
    +};
    +goog.inherits(goog.net.XhrIo, goog.events.EventTarget);
    +
    +
    +/**
    + * Response types that may be requested for XMLHttpRequests.
    + * @enum {string}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetype-attribute
    + */
    +goog.net.XhrIo.ResponseType = {
    +  DEFAULT: '',
    +  TEXT: 'text',
    +  DOCUMENT: 'document',
    +  // Not supported as of Chrome 10.0.612.1 dev
    +  BLOB: 'blob',
    +  ARRAY_BUFFER: 'arraybuffer'
    +};
    +
    +
    +/**
    + * A reference to the XhrIo logger
    + * @private {goog.debug.Logger}
    + * @const
    + */
    +goog.net.XhrIo.prototype.logger_ =
    +    goog.log.getLogger('goog.net.XhrIo');
    +
    +
    +/**
    + * The Content-Type HTTP header name
    + * @type {string}
    + */
    +goog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type';
    +
    +
    +/**
    + * The pattern matching the 'http' and 'https' URI schemes
    + * @type {!RegExp}
    + */
    +goog.net.XhrIo.HTTP_SCHEME_PATTERN = /^https?$/i;
    +
    +
    +/**
    + * The methods that typically come along with form data.  We set different
    + * headers depending on whether the HTTP action is one of these.
    + */
    +goog.net.XhrIo.METHODS_WITH_FORM_DATA = ['POST', 'PUT'];
    +
    +
    +/**
    + * The Content-Type HTTP header value for a url-encoded form
    + * @type {string}
    + */
    +goog.net.XhrIo.FORM_CONTENT_TYPE =
    +    'application/x-www-form-urlencoded;charset=utf-8';
    +
    +
    +/**
    + * The XMLHttpRequest Level two timeout delay ms property name.
    + *
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
    + *
    + * @private {string}
    + * @const
    + */
    +goog.net.XhrIo.XHR2_TIMEOUT_ = 'timeout';
    +
    +
    +/**
    + * The XMLHttpRequest Level two ontimeout handler property name.
    + *
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
    + *
    + * @private {string}
    + * @const
    + */
    +goog.net.XhrIo.XHR2_ON_TIMEOUT_ = 'ontimeout';
    +
    +
    +/**
    + * All non-disposed instances of goog.net.XhrIo created
    + * by {@link goog.net.XhrIo.send} are in this Array.
    + * @see goog.net.XhrIo.cleanup
    + * @private {!Array<!goog.net.XhrIo>}
    + */
    +goog.net.XhrIo.sendInstances_ = [];
    +
    +
    +/**
    + * Static send that creates a short lived instance of XhrIo to send the
    + * request.
    + * @see goog.net.XhrIo.cleanup
    + * @param {string|goog.Uri} url Uri to make request to.
    + * @param {?function(this:goog.net.XhrIo, ?)=} opt_callback Callback function
    + *     for when request is complete.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
    + *     opt_content Body data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + * @param {number=} opt_timeoutInterval Number of milliseconds after which an
    + *     incomplete request will be aborted; 0 means no timeout is set.
    + * @param {boolean=} opt_withCredentials Whether to send credentials with the
    + *     request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
    + * @return {!goog.net.XhrIo} The sent XhrIo.
    + */
    +goog.net.XhrIo.send = function(url, opt_callback, opt_method, opt_content,
    +                               opt_headers, opt_timeoutInterval,
    +                               opt_withCredentials) {
    +  var x = new goog.net.XhrIo();
    +  goog.net.XhrIo.sendInstances_.push(x);
    +  if (opt_callback) {
    +    x.listen(goog.net.EventType.COMPLETE, opt_callback);
    +  }
    +  x.listenOnce(goog.net.EventType.READY, x.cleanupSend_);
    +  if (opt_timeoutInterval) {
    +    x.setTimeoutInterval(opt_timeoutInterval);
    +  }
    +  if (opt_withCredentials) {
    +    x.setWithCredentials(opt_withCredentials);
    +  }
    +  x.send(url, opt_method, opt_content, opt_headers);
    +  return x;
    +};
    +
    +
    +/**
    + * Disposes all non-disposed instances of goog.net.XhrIo created by
    + * {@link goog.net.XhrIo.send}.
    + * {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance
    + * it creates when the request completes or fails.  However, if
    + * the request never completes, then the goog.net.XhrIo is not disposed.
    + * This can occur if the window is unloaded before the request completes.
    + * We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo
    + * it creates and make the client of {@link goog.net.XhrIo.send} be
    + * responsible for disposing it in this case.  However, this makes things
    + * significantly more complicated for the client, and the whole point
    + * of {@link goog.net.XhrIo.send} is that it's simple and easy to use.
    + * Clients of {@link goog.net.XhrIo.send} should call
    + * {@link goog.net.XhrIo.cleanup} when doing final
    + * cleanup on window unload.
    + */
    +goog.net.XhrIo.cleanup = function() {
    +  var instances = goog.net.XhrIo.sendInstances_;
    +  while (instances.length) {
    +    instances.pop().dispose();
    +  }
    +};
    +
    +
    +/**
    + * Installs exception protection for all entry point introduced by
    + * goog.net.XhrIo instances which are not protected by
    + * {@link goog.debug.ErrorHandler#protectWindowSetTimeout},
    + * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or
    + * {@link goog.events.protectBrowserEventEntryPoint}.
    + *
    + * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
    + *     protect the entry point(s).
    + */
    +goog.net.XhrIo.protectEntryPoints = function(errorHandler) {
    +  goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
    +      errorHandler.protectEntryPoint(
    +          goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
    +};
    +
    +
    +/**
    + * Disposes of the specified goog.net.XhrIo created by
    + * {@link goog.net.XhrIo.send} and removes it from
    + * {@link goog.net.XhrIo.pendingStaticSendInstances_}.
    + * @private
    + */
    +goog.net.XhrIo.prototype.cleanupSend_ = function() {
    +  this.dispose();
    +  goog.array.remove(goog.net.XhrIo.sendInstances_, this);
    +};
    +
    +
    +/**
    + * Returns the number of milliseconds after which an incomplete request will be
    + * aborted, or 0 if no timeout is set.
    + * @return {number} Timeout interval in milliseconds.
    + */
    +goog.net.XhrIo.prototype.getTimeoutInterval = function() {
    +  return this.timeoutInterval_;
    +};
    +
    +
    +/**
    + * Sets the number of milliseconds after which an incomplete request will be
    + * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
    + * timeout is set.
    + * @param {number} ms Timeout interval in milliseconds; 0 means none.
    + */
    +goog.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
    +  this.timeoutInterval_ = Math.max(0, ms);
    +};
    +
    +
    +/**
    + * Sets the desired type for the response. At time of writing, this is only
    + * supported in very recent versions of WebKit (10.0.612.1 dev and later).
    + *
    + * If this is used, the response may only be accessed via {@link #getResponse}.
    + *
    + * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
    + */
    +goog.net.XhrIo.prototype.setResponseType = function(type) {
    +  this.responseType_ = type;
    +};
    +
    +
    +/**
    + * Gets the desired type for the response.
    + * @return {goog.net.XhrIo.ResponseType} The desired type for the response.
    + */
    +goog.net.XhrIo.prototype.getResponseType = function() {
    +  return this.responseType_;
    +};
    +
    +
    +/**
    + * Sets whether a "credentialed" request that is aware of cookie and
    + * authentication information should be made. This option is only supported by
    + * browsers that support HTTP Access Control. As of this writing, this option
    + * is not supported in IE.
    + *
    + * @param {boolean} withCredentials Whether this should be a "credentialed"
    + *     request.
    + */
    +goog.net.XhrIo.prototype.setWithCredentials = function(withCredentials) {
    +  this.withCredentials_ = withCredentials;
    +};
    +
    +
    +/**
    + * Gets whether a "credentialed" request is to be sent.
    + * @return {boolean} The desired type for the response.
    + */
    +goog.net.XhrIo.prototype.getWithCredentials = function() {
    +  return this.withCredentials_;
    +};
    +
    +
    +/**
    + * Instance send that actually uses XMLHttpRequest to make a server call.
    + * @param {string|goog.Uri} url Uri to make request to.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
    + *     opt_content Body data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + */
    +goog.net.XhrIo.prototype.send = function(url, opt_method, opt_content,
    +                                         opt_headers) {
    +  if (this.xhr_) {
    +    throw Error('[goog.net.XhrIo] Object is active with another request=' +
    +        this.lastUri_ + '; newUri=' + url);
    +  }
    +
    +  var method = opt_method ? opt_method.toUpperCase() : 'GET';
    +
    +  this.lastUri_ = url;
    +  this.lastError_ = '';
    +  this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
    +  this.lastMethod_ = method;
    +  this.errorDispatched_ = false;
    +  this.active_ = true;
    +
    +  // Use the factory to create the XHR object and options
    +  this.xhr_ = this.createXhr();
    +  this.xhrOptions_ = this.xmlHttpFactory_ ?
    +      this.xmlHttpFactory_.getOptions() : goog.net.XmlHttp.getOptions();
    +
    +  // Set up the onreadystatechange callback
    +  this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this);
    +
    +  /**
    +   * Try to open the XMLHttpRequest (always async), if an error occurs here it
    +   * is generally permission denied
    +   * @preserveTry
    +   */
    +  try {
    +    goog.log.fine(this.logger_, this.formatMsg_('Opening Xhr'));
    +    this.inOpen_ = true;
    +    this.xhr_.open(method, String(url), true);  // Always async!
    +    this.inOpen_ = false;
    +  } catch (err) {
    +    goog.log.fine(this.logger_,
    +        this.formatMsg_('Error opening Xhr: ' + err.message));
    +    this.error_(goog.net.ErrorCode.EXCEPTION, err);
    +    return;
    +  }
    +
    +  // We can't use null since this won't allow requests with form data to have a
    +  // content length specified which will cause some proxies to return a 411
    +  // error.
    +  var content = opt_content || '';
    +
    +  var headers = this.headers.clone();
    +
    +  // Add headers specific to this request
    +  if (opt_headers) {
    +    goog.structs.forEach(opt_headers, function(value, key) {
    +      headers.set(key, value);
    +    });
    +  }
    +
    +  // Find whether a content type header is set, ignoring case.
    +  // HTTP header names are case-insensitive.  See:
    +  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
    +  var contentTypeKey = goog.array.find(headers.getKeys(),
    +      goog.net.XhrIo.isContentTypeHeader_);
    +
    +  var contentIsFormData = (goog.global['FormData'] &&
    +      (content instanceof goog.global['FormData']));
    +  if (goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA, method) &&
    +      !contentTypeKey && !contentIsFormData) {
    +    // For requests typically with form data, default to the url-encoded form
    +    // content type unless this is a FormData request.  For FormData,
    +    // the browser will automatically add a multipart/form-data content type
    +    // with an appropriate multipart boundary.
    +    headers.set(goog.net.XhrIo.CONTENT_TYPE_HEADER,
    +                goog.net.XhrIo.FORM_CONTENT_TYPE);
    +  }
    +
    +  // Add the headers to the Xhr object
    +  headers.forEach(function(value, key) {
    +    this.xhr_.setRequestHeader(key, value);
    +  }, this);
    +
    +  if (this.responseType_) {
    +    this.xhr_.responseType = this.responseType_;
    +  }
    +
    +  if (goog.object.containsKey(this.xhr_, 'withCredentials')) {
    +    this.xhr_.withCredentials = this.withCredentials_;
    +  }
    +
    +  /**
    +   * Try to send the request, or other wise report an error (404 not found).
    +   * @preserveTry
    +   */
    +  try {
    +    this.cleanUpTimeoutTimer_(); // Paranoid, should never be running.
    +    if (this.timeoutInterval_ > 0) {
    +      this.useXhr2Timeout_ = goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_);
    +      goog.log.fine(this.logger_, this.formatMsg_('Will abort after ' +
    +          this.timeoutInterval_ + 'ms if incomplete, xhr2 ' +
    +          this.useXhr2Timeout_));
    +      if (this.useXhr2Timeout_) {
    +        this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_] = this.timeoutInterval_;
    +        this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] =
    +            goog.bind(this.timeout_, this);
    +      } else {
    +        this.timeoutId_ = goog.Timer.callOnce(this.timeout_,
    +            this.timeoutInterval_, this);
    +      }
    +    }
    +    goog.log.fine(this.logger_, this.formatMsg_('Sending request'));
    +    this.inSend_ = true;
    +    this.xhr_.send(content);
    +    this.inSend_ = false;
    +
    +  } catch (err) {
    +    goog.log.fine(this.logger_, this.formatMsg_('Send error: ' + err.message));
    +    this.error_(goog.net.ErrorCode.EXCEPTION, err);
    +  }
    +};
    +
    +
    +/**
    + * Determines if the argument is an XMLHttpRequest that supports the level 2
    + * timeout value and event.
    + *
    + * Currently, FF 21.0 OS X has the fields but won't actually call the timeout
    + * handler.  Perhaps the confusion in the bug referenced below hasn't
    + * entirely been resolved.
    + *
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute
    + * @see https://bugzilla.mozilla.org/show_bug.cgi?id=525816
    + *
    + * @param {!goog.net.XhrLike.OrNative} xhr The request.
    + * @return {boolean} True if the request supports level 2 timeout.
    + * @private
    + */
    +goog.net.XhrIo.shouldUseXhr2Timeout_ = function(xhr) {
    +  return goog.userAgent.IE &&
    +      goog.userAgent.isVersionOrHigher(9) &&
    +      goog.isNumber(xhr[goog.net.XhrIo.XHR2_TIMEOUT_]) &&
    +      goog.isDef(xhr[goog.net.XhrIo.XHR2_ON_TIMEOUT_]);
    +};
    +
    +
    +/**
    + * @param {string} header An HTTP header key.
    + * @return {boolean} Whether the key is a content type header (ignoring
    + *     case.
    + * @private
    + */
    +goog.net.XhrIo.isContentTypeHeader_ = function(header) {
    +  return goog.string.caseInsensitiveEquals(
    +      goog.net.XhrIo.CONTENT_TYPE_HEADER, header);
    +};
    +
    +
    +/**
    + * Creates a new XHR object.
    + * @return {!goog.net.XhrLike.OrNative} The newly created XHR object.
    + * @protected
    + */
    +goog.net.XhrIo.prototype.createXhr = function() {
    +  return this.xmlHttpFactory_ ?
    +      this.xmlHttpFactory_.createInstance() : goog.net.XmlHttp();
    +};
    +
    +
    +/**
    + * The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_}
    + * milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts
    + * the request.
    + * @private
    + */
    +goog.net.XhrIo.prototype.timeout_ = function() {
    +  if (typeof goog == 'undefined') {
    +    // If goog is undefined then the callback has occurred as the application
    +    // is unloading and will error.  Thus we let it silently fail.
    +  } else if (this.xhr_) {
    +    this.lastError_ = 'Timed out after ' + this.timeoutInterval_ +
    +                      'ms, aborting';
    +    this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
    +    goog.log.fine(this.logger_, this.formatMsg_(this.lastError_));
    +    this.dispatchEvent(goog.net.EventType.TIMEOUT);
    +    this.abort(goog.net.ErrorCode.TIMEOUT);
    +  }
    +};
    +
    +
    +/**
    + * Something errorred, so inactivate, fire error callback and clean up
    + * @param {goog.net.ErrorCode} errorCode The error code.
    + * @param {Error} err The error object.
    + * @private
    + */
    +goog.net.XhrIo.prototype.error_ = function(errorCode, err) {
    +  this.active_ = false;
    +  if (this.xhr_) {
    +    this.inAbort_ = true;
    +    this.xhr_.abort();  // Ensures XHR isn't hung (FF)
    +    this.inAbort_ = false;
    +  }
    +  this.lastError_ = err;
    +  this.lastErrorCode_ = errorCode;
    +  this.dispatchErrors_();
    +  this.cleanUpXhr_();
    +};
    +
    +
    +/**
    + * Dispatches COMPLETE and ERROR in case of an error. This ensures that we do
    + * not dispatch multiple error events.
    + * @private
    + */
    +goog.net.XhrIo.prototype.dispatchErrors_ = function() {
    +  if (!this.errorDispatched_) {
    +    this.errorDispatched_ = true;
    +    this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    this.dispatchEvent(goog.net.EventType.ERROR);
    +  }
    +};
    +
    +
    +/**
    + * Abort the current XMLHttpRequest
    + * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
    + *     defaults to ABORT.
    + */
    +goog.net.XhrIo.prototype.abort = function(opt_failureCode) {
    +  if (this.xhr_ && this.active_) {
    +    goog.log.fine(this.logger_, this.formatMsg_('Aborting'));
    +    this.active_ = false;
    +    this.inAbort_ = true;
    +    this.xhr_.abort();
    +    this.inAbort_ = false;
    +    this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
    +    this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    this.dispatchEvent(goog.net.EventType.ABORT);
    +    this.cleanUpXhr_();
    +  }
    +};
    +
    +
    +/**
    + * Nullifies all callbacks to reduce risks of leaks.
    + * @override
    + * @protected
    + */
    +goog.net.XhrIo.prototype.disposeInternal = function() {
    +  if (this.xhr_) {
    +    // We explicitly do not call xhr_.abort() unless active_ is still true.
    +    // This is to avoid unnecessarily aborting a successful request when
    +    // dispose() is called in a callback triggered by a complete response, but
    +    // in which browser cleanup has not yet finished.
    +    // (See http://b/issue?id=1684217.)
    +    if (this.active_) {
    +      this.active_ = false;
    +      this.inAbort_ = true;
    +      this.xhr_.abort();
    +      this.inAbort_ = false;
    +    }
    +    this.cleanUpXhr_(true);
    +  }
    +
    +  goog.net.XhrIo.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Internal handler for the XHR object's readystatechange event.  This method
    + * checks the status and the readystate and fires the correct callbacks.
    + * If the request has ended, the handlers are cleaned up and the XHR object is
    + * nullified.
    + * @private
    + */
    +goog.net.XhrIo.prototype.onReadyStateChange_ = function() {
    +  if (this.isDisposed()) {
    +    // This method is the target of an untracked goog.Timer.callOnce().
    +    return;
    +  }
    +  if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) {
    +    // Were not being called from within a call to this.xhr_.send
    +    // this.xhr_.abort, or this.xhr_.open, so this is an entry point
    +    this.onReadyStateChangeEntryPoint_();
    +  } else {
    +    this.onReadyStateChangeHelper_();
    +  }
    +};
    +
    +
    +/**
    + * Used to protect the onreadystatechange handler entry point.  Necessary
    + * as {#onReadyStateChange_} maybe called from within send or abort, this
    + * method is only called when {#onReadyStateChange_} is called as an
    + * entry point.
    + * {@see #protectEntryPoints}
    + * @private
    + */
    +goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {
    +  this.onReadyStateChangeHelper_();
    +};
    +
    +
    +/**
    + * Helper for {@link #onReadyStateChange_}.  This is used so that
    + * entry point calls to {@link #onReadyStateChange_} can be routed through
    + * {@link #onReadyStateChangeEntryPoint_}.
    + * @private
    + */
    +goog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() {
    +  if (!this.active_) {
    +    // can get called inside abort call
    +    return;
    +  }
    +
    +  if (typeof goog == 'undefined') {
    +    // NOTE(user): If goog is undefined then the callback has occurred as the
    +    // application is unloading and will error.  Thus we let it silently fail.
    +
    +  } else if (
    +      this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] &&
    +      this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE &&
    +      this.getStatus() == 2) {
    +    // NOTE(user): In IE if send() errors on a *local* request the readystate
    +    // is still changed to COMPLETE.  We need to ignore it and allow the
    +    // try/catch around send() to pick up the error.
    +    goog.log.fine(this.logger_, this.formatMsg_(
    +        'Local request error detected and ignored'));
    +
    +  } else {
    +
    +    // In IE when the response has been cached we sometimes get the callback
    +    // from inside the send call and this usually breaks code that assumes that
    +    // XhrIo is asynchronous.  If that is the case we delay the callback
    +    // using a timer.
    +    if (this.inSend_ &&
    +        this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      goog.Timer.callOnce(this.onReadyStateChange_, 0, this);
    +      return;
    +    }
    +
    +    this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
    +
    +    // readyState indicates the transfer has finished
    +    if (this.isComplete()) {
    +      goog.log.fine(this.logger_, this.formatMsg_('Request complete'));
    +
    +      this.active_ = false;
    +
    +      try {
    +        // Call the specific callbacks for success or failure. Only call the
    +        // success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED)
    +        if (this.isSuccess()) {
    +          this.dispatchEvent(goog.net.EventType.COMPLETE);
    +          this.dispatchEvent(goog.net.EventType.SUCCESS);
    +        } else {
    +          this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
    +          this.lastError_ =
    +              this.getStatusText() + ' [' + this.getStatus() + ']';
    +          this.dispatchErrors_();
    +        }
    +      } finally {
    +        this.cleanUpXhr_();
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Remove the listener to protect against leaks, and nullify the XMLHttpRequest
    + * object.
    + * @param {boolean=} opt_fromDispose If this is from the dispose (don't want to
    + *     fire any events).
    + * @private
    + */
    +goog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) {
    +  if (this.xhr_) {
    +    // Cancel any pending timeout event handler.
    +    this.cleanUpTimeoutTimer_();
    +
    +    // Save reference so we can mark it as closed after the READY event.  The
    +    // READY event may trigger another request, thus we must nullify this.xhr_
    +    var xhr = this.xhr_;
    +    var clearedOnReadyStateChange =
    +        this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ?
    +            goog.nullFunction : null;
    +    this.xhr_ = null;
    +    this.xhrOptions_ = null;
    +
    +    if (!opt_fromDispose) {
    +      this.dispatchEvent(goog.net.EventType.READY);
    +    }
    +
    +    try {
    +      // NOTE(user): Not nullifying in FireFox can still leak if the callbacks
    +      // are defined in the same scope as the instance of XhrIo. But, IE doesn't
    +      // allow you to set the onreadystatechange to NULL so nullFunction is
    +      // used.
    +      xhr.onreadystatechange = clearedOnReadyStateChange;
    +    } catch (e) {
    +      // This seems to occur with a Gears HTTP request. Delayed the setting of
    +      // this onreadystatechange until after READY is sent out and catching the
    +      // error to see if we can track down the problem.
    +      goog.log.error(this.logger_,
    +          'Problem encountered resetting onreadystatechange: ' + e.message);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Make sure the timeout timer isn't running.
    + * @private
    + */
    +goog.net.XhrIo.prototype.cleanUpTimeoutTimer_ = function() {
    +  if (this.xhr_ && this.useXhr2Timeout_) {
    +    this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_] = null;
    +  }
    +  if (goog.isNumber(this.timeoutId_)) {
    +    goog.Timer.clear(this.timeoutId_);
    +    this.timeoutId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether there is an active request.
    + */
    +goog.net.XhrIo.prototype.isActive = function() {
    +  return !!this.xhr_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the request has completed.
    + */
    +goog.net.XhrIo.prototype.isComplete = function() {
    +  return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the request completed with a success.
    + */
    +goog.net.XhrIo.prototype.isSuccess = function() {
    +  var status = this.getStatus();
    +  // A zero status code is considered successful for local files.
    +  return goog.net.HttpStatus.isSuccess(status) ||
    +      status === 0 && !this.isLastUriEffectiveSchemeHttp_();
    +};
    +
    +
    +/**
    + * @return {boolean} whether the effective scheme of the last URI that was
    + *     fetched was 'http' or 'https'.
    + * @private
    + */
    +goog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {
    +  var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));
    +  return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(scheme);
    +};
    +
    +
    +/**
    + * Get the readystate from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*.
    + */
    +goog.net.XhrIo.prototype.getReadyState = function() {
    +  return this.xhr_ ?
    +      /** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) :
    +      goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +};
    +
    +
    +/**
    + * Get the status from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * @return {number} Http status.
    + */
    +goog.net.XhrIo.prototype.getStatus = function() {
    +  /**
    +   * IE doesn't like you checking status until the readystate is greater than 2
    +   * (i.e. it is receiving or complete).  The try/catch is used for when the
    +   * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
    +   * @preserveTry
    +   */
    +  try {
    +    return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
    +        this.xhr_.status : -1;
    +  } catch (e) {
    +    return -1;
    +  }
    +};
    +
    +
    +/**
    + * Get the status text from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * @return {string} Status text.
    + */
    +goog.net.XhrIo.prototype.getStatusText = function() {
    +  /**
    +   * IE doesn't like you checking status until the readystate is greater than 2
    +   * (i.e. it is recieving or complete).  The try/catch is used for when the
    +   * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_.
    +   * @preserveTry
    +   */
    +  try {
    +    return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ?
    +        this.xhr_.statusText : '';
    +  } catch (e) {
    +    goog.log.fine(this.logger_, 'Can not get status: ' + e.message);
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * Get the last Uri that was requested
    + * @return {string} Last Uri.
    + */
    +goog.net.XhrIo.prototype.getLastUri = function() {
    +  return String(this.lastUri_);
    +};
    +
    +
    +/**
    + * Get the response text from the Xhr object
    + * Will only return correct result when called from the context of a callback.
    + * @return {string} Result from the server, or '' if no result available.
    + */
    +goog.net.XhrIo.prototype.getResponseText = function() {
    +  /** @preserveTry */
    +  try {
    +    return this.xhr_ ? this.xhr_.responseText : '';
    +  } catch (e) {
    +    // http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
    +    // states that responseText should return '' (and responseXML null)
    +    // when the state is not LOADING or DONE. Instead, IE can
    +    // throw unexpected exceptions, for example when a request is aborted
    +    // or no data is available yet.
    +    goog.log.fine(this.logger_, 'Can not get responseText: ' + e.message);
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * Get the response body from the Xhr object. This property is only available
    + * in IE since version 7 according to MSDN:
    + * http://msdn.microsoft.com/en-us/library/ie/ms534368(v=vs.85).aspx
    + * Will only return correct result when called from the context of a callback.
    + *
    + * One option is to construct a VBArray from the returned object and convert
    + * it to a JavaScript array using the toArray method:
    + * {@code (new window['VBArray'](xhrIo.getResponseBody())).toArray()}
    + * This will result in an array of numbers in the range of [0..255]
    + *
    + * Another option is to use the VBScript CStr method to convert it into a
    + * string as outlined in http://stackoverflow.com/questions/1919972
    + *
    + * @return {Object} Binary result from the server or null if not available.
    + */
    +goog.net.XhrIo.prototype.getResponseBody = function() {
    +  /** @preserveTry */
    +  try {
    +    if (this.xhr_ && 'responseBody' in this.xhr_) {
    +      return this.xhr_['responseBody'];
    +    }
    +  } catch (e) {
    +    // IE can throw unexpected exceptions, for example when a request is aborted
    +    // or no data is yet available.
    +    goog.log.fine(this.logger_, 'Can not get responseBody: ' + e.message);
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Get the response XML from the Xhr object
    + * Will only return correct result when called from the context of a callback.
    + * @return {Document} The DOM Document representing the XML file, or null
    + * if no result available.
    + */
    +goog.net.XhrIo.prototype.getResponseXml = function() {
    +  /** @preserveTry */
    +  try {
    +    return this.xhr_ ? this.xhr_.responseXML : null;
    +  } catch (e) {
    +    goog.log.fine(this.logger_, 'Can not get responseXML: ' + e.message);
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Get the response and evaluates it as JSON from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
    + *     stripping of the response before parsing. This needs to be set only if
    + *     your backend server prepends the same prefix string to the JSON response.
    + * @return {Object|undefined} JavaScript object.
    + */
    +goog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
    +  if (!this.xhr_) {
    +    return undefined;
    +  }
    +
    +  var responseText = this.xhr_.responseText;
    +  if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
    +    responseText = responseText.substring(opt_xssiPrefix.length);
    +  }
    +
    +  return goog.json.parse(responseText);
    +};
    +
    +
    +/**
    + * Get the response as the type specificed by {@link #setResponseType}. At time
    + * of writing, this is only directly supported in very recent versions of WebKit
    + * (10.0.612.1 dev and later). If the field is not supported directly, we will
    + * try to emulate it.
    + *
    + * Emulating the response means following the rules laid out at
    + * http://www.w3.org/TR/XMLHttpRequest/#the-response-attribute
    + *
    + * On browsers with no support for this (Chrome < 10, Firefox < 4, etc), only
    + * response types of DEFAULT or TEXT may be used, and the response returned will
    + * be the text response.
    + *
    + * On browsers with Mozilla's draft support for array buffers (Firefox 4, 5),
    + * only response types of DEFAULT, TEXT, and ARRAY_BUFFER may be used, and the
    + * response returned will be either the text response or the Mozilla
    + * implementation of the array buffer response.
    + *
    + * On browsers will full support, any valid response type supported by the
    + * browser may be used, and the response provided by the browser will be
    + * returned.
    + *
    + * @return {*} The response.
    + */
    +goog.net.XhrIo.prototype.getResponse = function() {
    +  /** @preserveTry */
    +  try {
    +    if (!this.xhr_) {
    +      return null;
    +    }
    +    if ('response' in this.xhr_) {
    +      return this.xhr_.response;
    +    }
    +    switch (this.responseType_) {
    +      case goog.net.XhrIo.ResponseType.DEFAULT:
    +      case goog.net.XhrIo.ResponseType.TEXT:
    +        return this.xhr_.responseText;
    +        // DOCUMENT and BLOB don't need to be handled here because they are
    +        // introduced in the same spec that adds the .response field, and would
    +        // have been caught above.
    +        // ARRAY_BUFFER needs an implementation for Firefox 4, where it was
    +        // implemented using a draft spec rather than the final spec.
    +      case goog.net.XhrIo.ResponseType.ARRAY_BUFFER:
    +        if ('mozResponseArrayBuffer' in this.xhr_) {
    +          return this.xhr_.mozResponseArrayBuffer;
    +        }
    +    }
    +    // Fell through to a response type that is not supported on this browser.
    +    goog.log.error(this.logger_,
    +        'Response type ' + this.responseType_ + ' is not ' +
    +        'supported on this browser');
    +    return null;
    +  } catch (e) {
    +    goog.log.fine(this.logger_, 'Can not get response: ' + e.message);
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Get the value of the response-header with the given name from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * and the request has completed
    + * @param {string} key The name of the response-header to retrieve.
    + * @return {string|undefined} The value of the response-header named key.
    + */
    +goog.net.XhrIo.prototype.getResponseHeader = function(key) {
    +  return this.xhr_ && this.isComplete() ?
    +      this.xhr_.getResponseHeader(key) : undefined;
    +};
    +
    +
    +/**
    + * Gets the text of all the headers in the response.
    + * Will only return correct result when called from the context of a callback
    + * and the request has completed.
    + * @return {string} The value of the response headers or empty string.
    + */
    +goog.net.XhrIo.prototype.getAllResponseHeaders = function() {
    +  return this.xhr_ && this.isComplete() ?
    +      this.xhr_.getAllResponseHeaders() : '';
    +};
    +
    +
    +/**
    + * Returns all response headers as a key-value map.
    + * Multiple values for the same header key can be combined into one,
    + * separated by a comma and a space.
    + * Note that the native getResponseHeader method for retrieving a single header
    + * does a case insensitive match on the header name. This method does not
    + * include any case normalization logic, it will just return a key-value
    + * representation of the headers.
    + * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
    + * @return {!Object<string, string>} An object with the header keys as keys
    + *     and header values as values.
    + */
    +goog.net.XhrIo.prototype.getResponseHeaders = function() {
    +  var headersObject = {};
    +  var headersArray = this.getAllResponseHeaders().split('\r\n');
    +  for (var i = 0; i < headersArray.length; i++) {
    +    if (goog.string.isEmptyOrWhitespace(headersArray[i])) {
    +      continue;
    +    }
    +    var keyValue = goog.string.splitLimit(headersArray[i], ': ', 2);
    +    if (headersObject[keyValue[0]]) {
    +      headersObject[keyValue[0]] += ', ' + keyValue[1];
    +    } else {
    +      headersObject[keyValue[0]] = keyValue[1];
    +    }
    +  }
    +  return headersObject;
    +};
    +
    +
    +/**
    + * Get the last error message
    + * @return {goog.net.ErrorCode} Last error code.
    + */
    +goog.net.XhrIo.prototype.getLastErrorCode = function() {
    +  return this.lastErrorCode_;
    +};
    +
    +
    +/**
    + * Get the last error message
    + * @return {string} Last error message.
    + */
    +goog.net.XhrIo.prototype.getLastError = function() {
    +  return goog.isString(this.lastError_) ? this.lastError_ :
    +      String(this.lastError_);
    +};
    +
    +
    +/**
    + * Adds the last method, status and URI to the message.  This is used to add
    + * this information to the logging calls.
    + * @param {string} msg The message text that we want to add the extra text to.
    + * @return {string} The message with the extra text appended.
    + * @private
    + */
    +goog.net.XhrIo.prototype.formatMsg_ = function(msg) {
    +  return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' +
    +      this.getStatus() + ']';
    +};
    +
    +
    +// Register the xhr handler as an entry point, so that
    +// it can be monitored for exception handling, etc.
    +goog.debug.entryPointRegistry.register(
    +    /**
    +     * @param {function(!Function): !Function} transformer The transforming
    +     *     function.
    +     */
    +    function(transformer) {
    +      goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ =
    +          transformer(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrio_test.html b/mapbox-gl/closure-library/closure/goog/net/xhrio_test.html
    new file mode 100644
    index 0000000000..6c9745ba6f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrio_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.XhrIo
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.XhrIoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrio_test.js b/mapbox-gl/closure-library/closure/goog/net/xhrio_test.js
    new file mode 100644
    index 0000000000..39801404c5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrio_test.js
    @@ -0,0 +1,794 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.XhrIoTest');
    +goog.setTestOnly('goog.net.XhrIoTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.debug.EntryPointMonitor');
    +goog.require('goog.debug.ErrorHandler');
    +goog.require('goog.debug.entryPointRegistry');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.WrapperXmlHttpFactory');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIo');
    +goog.require('goog.testing.recordFunction');
    +
    +function MockXmlHttp() {
    +  /**
    +   * The headers for this XmlHttpRequest.
    +   * @type {!Object<string>}
    +   */
    +  this.headers = {};
    +}
    +
    +MockXmlHttp.prototype.readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +
    +MockXmlHttp.prototype.status = 200;
    +
    +MockXmlHttp.syncSend = false;
    +
    +MockXmlHttp.prototype.send = function(opt_data) {
    +  this.readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +
    +  if (MockXmlHttp.syncSend) {
    +    this.complete();
    +  }
    +
    +};
    +
    +MockXmlHttp.prototype.complete = function() {
    +  this.readyState = goog.net.XmlHttp.ReadyState.LOADING;
    +  this.onreadystatechange();
    +
    +  this.readyState = goog.net.XmlHttp.ReadyState.LOADED;
    +  this.onreadystatechange();
    +
    +  this.readyState = goog.net.XmlHttp.ReadyState.INTERACTIVE;
    +  this.onreadystatechange();
    +
    +  this.readyState = goog.net.XmlHttp.ReadyState.COMPLETE;
    +  this.onreadystatechange();
    +};
    +
    +
    +MockXmlHttp.prototype.open = function(verb, uri, async) {
    +};
    +
    +MockXmlHttp.prototype.abort = function() {};
    +
    +MockXmlHttp.prototype.setRequestHeader = function(key, value) {
    +  this.headers[key] = value;
    +};
    +
    +var lastMockXmlHttp;
    +goog.net.XmlHttp.setGlobalFactory(new goog.net.WrapperXmlHttpFactory(
    +    function() {
    +      lastMockXmlHttp = new MockXmlHttp();
    +      return lastMockXmlHttp;
    +    },
    +    function() {
    +      return {};
    +    }));
    +
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +var clock;
    +var originalEntryPoint =
    +    goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_;
    +
    +function setUp() {
    +  lastMockXmlHttp = null;
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +  clock.dispose();
    +  goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = originalEntryPoint;
    +}
    +
    +
    +function testSyncSend() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertTrue('Should be succesful', e.target.isSuccess());
    +    count++;
    +
    +  });
    +
    +  var inSend = true;
    +  x.send('url');
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +function testSyncSendFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('url');
    +  lastMockXmlHttp.status = 404;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendRelativeZeroStatus() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertEquals('Should be the same as ', e.target.isSuccess(),
    +        window.location.href.toLowerCase().indexOf('file:') == 0);
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('relative');
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendRelativeUriZeroStatus() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertEquals('Should be the same as ', e.target.isSuccess(),
    +        window.location.href.toLowerCase().indexOf('file:') == 0);
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('relative'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('http://foo');
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpUpperZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('HTTP://foo');
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpUpperUriZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('HTTP://foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpUriZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('http://foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpUriZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('HTTP://foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendHttpsZeroStatusFailure() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertFalse('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('https://foo');
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendFileUpperZeroStatusSuccess() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertTrue('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send('FILE:///foo');
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendFileUriZeroStatusSuccess() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertTrue('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('file:///foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendDummyUriZeroStatusSuccess() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertTrue('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('dummy:///foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendFileUpperUriZeroStatusSuccess() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertFalse('Should not fire complete from inside send', inSend);
    +    assertTrue('Should not be succesful', e.target.isSuccess());
    +    count++;
    +  });
    +
    +  var inSend = true;
    +  x.send(goog.Uri.parse('FILE:///foo'));
    +  lastMockXmlHttp.status = 0;
    +  inSend = false;
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testSendFromListener() {
    +  MockXmlHttp.syncSend = true;
    +  var count = 0;
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    count++;
    +
    +    var e = assertThrows(function() {
    +      x.send('url2');
    +    });
    +    assertEquals('[goog.net.XhrIo] Object is active with another request=url' +
    +        '; newUri=url2', e.message);
    +  });
    +
    +  x.send('url');
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +
    +  assertEquals('Complete should have been called once', 1, count);
    +}
    +
    +
    +function testStatesDuringEvents() {
    +  MockXmlHttp.syncSend = true;
    +
    +  var x = new goog.net.XhrIo;
    +  var readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +  goog.events.listen(x, goog.net.EventType.READY_STATE_CHANGE, function(e) {
    +    readyState++;
    +    assertObjectEquals(e.target, x);
    +    assertEquals(x.getReadyState(), readyState);
    +    assertTrue(x.isActive());
    +  });
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    assertObjectEquals(e.target, x);
    +    assertTrue(x.isActive());
    +  });
    +  goog.events.listen(x, goog.net.EventType.SUCCESS, function(e) {
    +    assertObjectEquals(e.target, x);
    +    assertTrue(x.isActive());
    +  });
    +  goog.events.listen(x, goog.net.EventType.READY, function(e) {
    +    assertObjectEquals(e.target, x);
    +    assertFalse(x.isActive());
    +  });
    +
    +  x.send('url');
    +
    +  clock.tick(1); // callOnce(f, 0, ...)
    +}
    +
    +
    +function testProtectEntryPointCalledOnAsyncSend() {
    +  MockXmlHttp.syncSend = false;
    +
    +  var errorHandlerCallbackCalled = false;
    +  var errorHandler = new goog.debug.ErrorHandler(function() {
    +    errorHandlerCallbackCalled = true;
    +  });
    +
    +  goog.net.XhrIo.protectEntryPoints(errorHandler);
    +
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.READY_STATE_CHANGE, function(e) {
    +    throw Error();
    +  });
    +
    +  x.send('url');
    +  assertThrows(function() {
    +    lastMockXmlHttp.complete();
    +  });
    +
    +  assertTrue('Error handler callback should be called on async send.',
    +      errorHandlerCallbackCalled);
    +}
    +
    +function testXHRIsDiposedEvenIfAListenerThrowsAnExceptionOnComplete() {
    +  MockXmlHttp.syncSend = false;
    +
    +  var x = new goog.net.XhrIo;
    +
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    throw Error();
    +  }, false, x);
    +
    +  x.send('url');
    +  assertThrows(function() {
    +    lastMockXmlHttp.complete();
    +  });
    +
    +  // The XHR should have been disposed, even though the listener threw an
    +  // exception.
    +  assertNull(x.xhr_);
    +}
    +
    +function testDisposeInternalDoesNotAbortXhrRequestObjectWhenActiveIsFalse() {
    +  MockXmlHttp.syncSend = false;
    +
    +  var xmlHttp = goog.net.XmlHttp;
    +  var abortCalled = false;
    +  var x = new goog.net.XhrIo;
    +
    +  goog.net.XmlHttp.prototype.abort = function() { abortCalled = true; };
    +
    +  goog.events.listen(x, goog.net.EventType.COMPLETE, function(e) {
    +    this.active_ = false;
    +    this.dispose();
    +  }, false, x);
    +
    +  x.send('url');
    +  lastMockXmlHttp.complete();
    +
    +  goog.net.XmlHttp = xmlHttp;
    +  assertFalse(abortCalled);
    +}
    +
    +function testCallingAbortFromWithinAbortCallbackDoesntLoop() {
    +  var x = new goog.net.XhrIo;
    +  goog.events.listen(x, goog.net.EventType.ABORT, function(e) {
    +    x.abort(); // Shouldn't get a stack overflow
    +  });
    +  x.send('url');
    +  x.abort();
    +}
    +
    +function testPostSetsContentTypeHeader() {
    +  var x = new goog.net.XhrIo;
    +
    +  x.send('url', 'POST', 'content');
    +  var headers = lastMockXmlHttp.headers;
    +  assertEquals(1, goog.object.getCount(headers));
    +  assertEquals(
    +      headers[goog.net.XhrIo.CONTENT_TYPE_HEADER],
    +      goog.net.XhrIo.FORM_CONTENT_TYPE);
    +}
    +
    +function testNonPostSetsContentTypeHeader() {
    +  var x = new goog.net.XhrIo;
    +
    +  x.send('url', 'PUT', 'content');
    +  headers = lastMockXmlHttp.headers;
    +  assertEquals(1, goog.object.getCount(headers));
    +  assertEquals(
    +      headers[goog.net.XhrIo.CONTENT_TYPE_HEADER],
    +      goog.net.XhrIo.FORM_CONTENT_TYPE);
    +}
    +
    +function testContentTypeIsTreatedCaseInsensitively() {
    +  var x = new goog.net.XhrIo;
    +
    +  x.send('url', 'POST', 'content', {'content-type': 'testing'});
    +
    +  assertObjectEquals(
    +      'Headers should not be modified since they already contain a ' +
    +      'content type definition',
    +      {'content-type': 'testing'},
    +      lastMockXmlHttp.headers);
    +}
    +
    +function testIsContentTypeHeader_() {
    +  assertTrue(goog.net.XhrIo.isContentTypeHeader_('content-type'));
    +  assertTrue(goog.net.XhrIo.isContentTypeHeader_('Content-type'));
    +  assertTrue(goog.net.XhrIo.isContentTypeHeader_('CONTENT-TYPE'));
    +  assertTrue(goog.net.XhrIo.isContentTypeHeader_('Content-Type'));
    +  assertFalse(goog.net.XhrIo.isContentTypeHeader_('Content Type'));
    +}
    +
    +function testPostFormDataDoesNotSetContentTypeHeader() {
    +  function FakeFormData() {}
    +
    +  propertyReplacer.set(goog.global, 'FormData', FakeFormData);
    +
    +  var x = new goog.net.XhrIo;
    +  x.send('url', 'POST', new FakeFormData());
    +  var headers = lastMockXmlHttp.headers;
    +  assertTrue(goog.object.isEmpty(headers));
    +}
    +
    +function testNonPostFormDataDoesNotSetContentTypeHeader() {
    +  function FakeFormData() {}
    +
    +  propertyReplacer.set(goog.global, 'FormData', FakeFormData);
    +
    +  var x = new goog.net.XhrIo;
    +  x.send('url', 'PUT', new FakeFormData());
    +  headers = lastMockXmlHttp.headers;
    +  assertTrue(goog.object.isEmpty(headers));
    +}
    +
    +function testFactoryInjection() {
    +  var xhr = new MockXmlHttp();
    +  var optionsFactoryCalled = 0;
    +  var xhrFactoryCalled = 0;
    +  var wrapperFactory = new goog.net.WrapperXmlHttpFactory(
    +      function() {
    +        xhrFactoryCalled++;
    +        return xhr;
    +      },
    +      function() {
    +        optionsFactoryCalled++;
    +        return {};
    +      });
    +  var xhrIo = new goog.net.XhrIo(wrapperFactory);
    +
    +  xhrIo.send('url');
    +
    +  assertEquals('XHR factory should have been called', 1, xhrFactoryCalled);
    +  assertEquals('Options factory should have been called', 1,
    +      optionsFactoryCalled);
    +}
    +
    +function testGoogTestingNetXhrIoIsInSync() {
    +  var xhrIo = new goog.net.XhrIo();
    +  var testingXhrIo = new goog.testing.net.XhrIo();
    +
    +  var propertyComparator = function(value, key, obj) {
    +    if (goog.string.endsWith(key, '_')) {
    +      // Ignore private properties/methods
    +      return true;
    +    } else if (typeof value == 'function' && typeof this[key] != 'function') {
    +      // Only type check is sufficient for functions
    +      fail('Mismatched property:' + key + ': gooo.net.XhrIo has:<' +
    +          value + '>; while goog.testing.net.XhrIo has:<' + this[key] + '>');
    +      return true;
    +    } else {
    +      // Ignore all other type of properties.
    +      return true;
    +    }
    +  };
    +
    +  goog.object.every(xhrIo, propertyComparator, testingXhrIo);
    +}
    +
    +function testEntryPointRegistry() {
    +  var monitor = new goog.debug.EntryPointMonitor();
    +  var replacement = function() {};
    +  monitor.wrap = goog.testing.recordFunction(
    +      goog.functions.constant(replacement));
    +
    +  goog.debug.entryPointRegistry.monitorAll(monitor);
    +  assertTrue(monitor.wrap.getCallCount() >= 1);
    +  assertEquals(
    +      replacement,
    +      goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_);
    +}
    +
    +function testSetWithCredentials() {
    +  // Test on XHR objects that don't have the withCredentials property (older
    +  // browsers).
    +  var x = new goog.net.XhrIo;
    +  x.setWithCredentials(true);
    +  x.send('url');
    +  assertFalse(
    +      'withCredentials should not be set on an XHR object if the property ' +
    +      'does not exist.',
    +      goog.object.containsKey(lastMockXmlHttp, 'withCredentials'));
    +
    +  // Test on XHR objects that have the withCredentials property.
    +  MockXmlHttp.prototype.withCredentials = false;
    +  x = new goog.net.XhrIo;
    +  x.setWithCredentials(true);
    +  x.send('url');
    +  assertTrue(
    +      'withCredentials should be set on an XHR object if the property exists',
    +      goog.object.containsKey(lastMockXmlHttp, 'withCredentials'));
    +
    +  assertTrue(
    +      'withCredentials value not set on XHR object',
    +      lastMockXmlHttp.withCredentials);
    +
    +  // Reset the prototype so it does not effect other tests.
    +  delete MockXmlHttp.prototype.withCredentials;
    +}
    +
    +function testGetResponse() {
    +  var x = new goog.net.XhrIo;
    +
    +  // No XHR yet
    +  assertEquals(null, x.getResponse());
    +
    +  // XHR with no .response and no response type, gets text.
    +  x.xhr_ = {};
    +  x.xhr_.responseText = 'text';
    +  assertEquals('text', x.getResponse());
    +
    +  // Response type of text gets text as well.
    +  x.setResponseType(goog.net.XhrIo.ResponseType.TEXT);
    +  x.xhr_.responseText = '';
    +  assertEquals('', x.getResponse());
    +
    +  // Response type of array buffer gets the array buffer.
    +  x.xhr_.mozResponseArrayBuffer = 'ab';
    +  x.setResponseType(goog.net.XhrIo.ResponseType.ARRAY_BUFFER);
    +  assertEquals('ab', x.getResponse());
    +
    +  // With a response field, it is returned no matter what value it has.
    +  x.xhr_.response = undefined;
    +  assertEquals(undefined, x.getResponse());
    +
    +  x.xhr_.response = null;
    +  assertEquals(null, x.getResponse());
    +
    +  x.xhr_.response = '';
    +  assertEquals('', x.getResponse());
    +
    +  x.xhr_.response = 'resp';
    +  assertEquals('resp', x.getResponse());
    +}
    +
    +function testGetResponseHeaders() {
    +  var x = new goog.net.XhrIo();
    +
    +  // No XHR yet
    +  assertEquals(0, goog.object.getCount(x.getResponseHeaders()));
    +
    +  // Simulate an XHR with 2 headers.
    +  var headersRaw = 'test1: foo\r\ntest2: bar';
    +
    +  propertyReplacer.set(x, 'getAllResponseHeaders',
    +                       goog.functions.constant(headersRaw));
    +
    +  var headers = x.getResponseHeaders();
    +  assertEquals(2, goog.object.getCount(headers));
    +  assertEquals('foo', headers['test1']);
    +  assertEquals('bar', headers['test2']);
    +}
    +
    +function testGetResponseHeadersWithColonInValue() {
    +  var x = new goog.net.XhrIo();
    +
    +  // Simulate an XHR with a colon in the http header value.
    +  var headersRaw = 'test1: f:o:o';
    +
    +  propertyReplacer.set(x, 'getAllResponseHeaders',
    +                       goog.functions.constant(headersRaw));
    +
    +  var headers = x.getResponseHeaders();
    +  assertEquals(1, goog.object.getCount(headers));
    +  assertEquals('f:o:o', headers['test1']);
    +}
    +
    +function testGetResponseHeadersMultipleValuesForOneKey() {
    +  var x = new goog.net.XhrIo();
    +
    +  // No XHR yet
    +  assertEquals(0, goog.object.getCount(x.getResponseHeaders()));
    +
    +  // Simulate an XHR with 2 headers.
    +  var headersRaw = 'test1: foo\r\ntest1: bar';
    +
    +  propertyReplacer.set(x, 'getAllResponseHeaders',
    +                       goog.functions.constant(headersRaw));
    +
    +  var headers = x.getResponseHeaders();
    +  assertEquals(1, goog.object.getCount(headers));
    +  assertEquals('foo, bar', headers['test1']);
    +}
    +
    +function testGetResponseHeadersEmptyHeader() {
    +  var x = new goog.net.XhrIo();
    +
    +  // No XHR yet
    +  assertEquals(0, goog.object.getCount(x.getResponseHeaders()));
    +
    +  // Simulate an XHR with 2 headers, the last of which is empty.
    +  var headersRaw = 'test2: bar\r\n';
    +
    +  propertyReplacer.set(x, 'getAllResponseHeaders',
    +                       goog.functions.constant(headersRaw));
    +
    +  var headers = x.getResponseHeaders();
    +  assertEquals(1, goog.object.getCount(headers));
    +  assertEquals('bar', headers['test2']);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhriopool.js b/mapbox-gl/closure-library/closure/goog/net/xhriopool.js
    new file mode 100644
    index 0000000000..ffc61e16c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhriopool.js
    @@ -0,0 +1,79 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Creates a pool of XhrIo objects to use. This allows multiple
    + * XhrIo objects to be grouped together and requests will use next available
    + * XhrIo object.
    + *
    + */
    +
    +goog.provide('goog.net.XhrIoPool');
    +
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.structs.PriorityPool');
    +
    +
    +
    +/**
    + * A pool of XhrIo objects.
    + * @param {goog.structs.Map=} opt_headers Map of default headers to add to every
    + *     request.
    + * @param {number=} opt_minCount Minimum number of objects (Default: 1).
    + * @param {number=} opt_maxCount Maximum number of objects (Default: 10).
    + * @constructor
    + * @extends {goog.structs.PriorityPool}
    + */
    +goog.net.XhrIoPool = function(opt_headers, opt_minCount, opt_maxCount) {
    +  goog.structs.PriorityPool.call(this, opt_minCount, opt_maxCount);
    +
    +  /**
    +   * Map of default headers to add to every request.
    +   * @type {goog.structs.Map|undefined}
    +   * @private
    +   */
    +  this.headers_ = opt_headers;
    +};
    +goog.inherits(goog.net.XhrIoPool, goog.structs.PriorityPool);
    +
    +
    +/**
    + * Creates an instance of an XhrIo object to use in the pool.
    + * @return {!goog.net.XhrIo} The created object.
    + * @override
    + */
    +goog.net.XhrIoPool.prototype.createObject = function() {
    +  var xhrIo = new goog.net.XhrIo();
    +  var headers = this.headers_;
    +  if (headers) {
    +    headers.forEach(function(value, key) {
    +      xhrIo.headers.set(key, value);
    +    });
    +  }
    +  return xhrIo;
    +};
    +
    +
    +/**
    + * Determine if an object has become unusable and should not be used.
    + * @param {Object} obj The object to test.
    + * @return {boolean} Whether the object can be reused, which is true if the
    + *     object is not disposed and not active.
    + * @override
    + */
    +goog.net.XhrIoPool.prototype.objectCanBeReused = function(obj) {
    +  // An active XhrIo object should never be used.
    +  var xhr = /** @type {goog.net.XhrIo} */ (obj);
    +  return !xhr.isDisposed() && !xhr.isActive();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrlike.js b/mapbox-gl/closure-library/closure/goog/net/xhrlike.js
    new file mode 100644
    index 0000000000..4cb26f2698
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrlike.js
    @@ -0,0 +1,124 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.XhrLike');
    +
    +
    +
    +/**
    + * Interface for the common parts of XMLHttpRequest.
    + *
    + * Mostly copied from externs/w3c_xml.js.
    + *
    + * @interface
    + * @see http://www.w3.org/TR/XMLHttpRequest/
    + */
    +goog.net.XhrLike = function() {};
    +
    +
    +/**
    + * Typedef that refers to either native or custom-implemented XHR objects.
    + * @typedef {!goog.net.XhrLike|!XMLHttpRequest}
    + */
    +goog.net.XhrLike.OrNative;
    +
    +
    +/**
    + * @type {function()|null|undefined}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange
    + */
    +goog.net.XhrLike.prototype.onreadystatechange;
    +
    +
    +/**
    + * @type {string}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
    + */
    +goog.net.XhrLike.prototype.responseText;
    +
    +
    +/**
    + * @type {Document}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute
    + */
    +goog.net.XhrLike.prototype.responseXML;
    +
    +
    +/**
    + * @type {number}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#readystate
    + */
    +goog.net.XhrLike.prototype.readyState;
    +
    +
    +/**
    + * @type {number}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#status
    + */
    +goog.net.XhrLike.prototype.status;
    +
    +
    +/**
    + * @type {string}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#statustext
    + */
    +goog.net.XhrLike.prototype.statusText;
    +
    +
    +/**
    + * @param {string} method
    + * @param {string} url
    + * @param {?boolean=} opt_async
    + * @param {?string=} opt_user
    + * @param {?string=} opt_password
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-open()-method
    + */
    +goog.net.XhrLike.prototype.open = function(method, url, opt_async, opt_user,
    +    opt_password) {};
    +
    +
    +/**
    + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} opt_data
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-send()-method
    + */
    +goog.net.XhrLike.prototype.send = function(opt_data) {};
    +
    +
    +/**
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method
    + */
    +goog.net.XhrLike.prototype.abort = function() {};
    +
    +
    +/**
    + * @param {string} header
    + * @param {string} value
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method
    + */
    +goog.net.XhrLike.prototype.setRequestHeader = function(header, value) {};
    +
    +
    +/**
    + * @param {string} header
    + * @return {string}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
    + */
    +goog.net.XhrLike.prototype.getResponseHeader = function(header) {};
    +
    +
    +/**
    + * @return {string}
    + * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method
    + */
    +goog.net.XhrLike.prototype.getAllResponseHeaders = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrmanager.js b/mapbox-gl/closure-library/closure/goog/net/xhrmanager.js
    new file mode 100644
    index 0000000000..50f5229efd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrmanager.js
    @@ -0,0 +1,772 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Manages a pool of XhrIo's. This handles all the details of
    + * dealing with the XhrPool and provides a simple interface for sending requests
    + * and managing events.
    + *
    + * This class supports queueing & prioritization of requests (XhrIoPool
    + * handles this) and retrying of requests.
    + *
    + * The events fired by the XhrManager are an aggregation of the events of
    + * each of its XhrIo objects (with some filtering, i.e., ERROR only called
    + * when there are no more retries left). For this reason, all send requests have
    + * to have an id, so that the user of this object can know which event is for
    + * which request.
    + *
    + */
    +
    +goog.provide('goog.net.XhrManager');
    +goog.provide('goog.net.XhrManager.Event');
    +goog.provide('goog.net.XhrManager.Request');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XhrIoPool');
    +goog.require('goog.structs.Map');
    +
    +// TODO(user): Add some time in between retries.
    +
    +
    +
    +/**
    + * A manager of an XhrIoPool.
    + * @param {number=} opt_maxRetries Max. number of retries (Default: 1).
    + * @param {goog.structs.Map=} opt_headers Map of default headers to add to every
    + *     request.
    + * @param {number=} opt_minCount Min. number of objects (Default: 1).
    + * @param {number=} opt_maxCount Max. number of objects (Default: 10).
    + * @param {number=} opt_timeoutInterval Timeout (in ms) before aborting an
    + *     attempt (Default: 0ms).
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.net.XhrManager = function(
    +    opt_maxRetries,
    +    opt_headers,
    +    opt_minCount,
    +    opt_maxCount,
    +    opt_timeoutInterval) {
    +  goog.net.XhrManager.base(this, 'constructor');
    +
    +  /**
    +   * Maximum number of retries for a given request
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxRetries_ = goog.isDef(opt_maxRetries) ? opt_maxRetries : 1;
    +
    +  /**
    +   * Timeout interval for an attempt of a given request.
    +   * @type {number}
    +   * @private
    +   */
    +  this.timeoutInterval_ =
    +      goog.isDef(opt_timeoutInterval) ? Math.max(0, opt_timeoutInterval) : 0;
    +
    +  /**
    +   * The pool of XhrIo's to use.
    +   * @type {goog.net.XhrIoPool}
    +   * @private
    +   */
    +  this.xhrPool_ = new goog.net.XhrIoPool(
    +      opt_headers, opt_minCount, opt_maxCount);
    +
    +  /**
    +   * Map of ID's to requests.
    +   * @type {goog.structs.Map<string, !goog.net.XhrManager.Request>}
    +   * @private
    +   */
    +  this.requests_ = new goog.structs.Map();
    +
    +  /**
    +   * The event handler.
    +   * @type {goog.events.EventHandler<!goog.net.XhrManager>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +};
    +goog.inherits(goog.net.XhrManager, goog.events.EventTarget);
    +
    +
    +/**
    + * Error to throw when a send is attempted with an ID that the manager already
    + * has registered for another request.
    + * @type {string}
    + * @private
    + */
    +goog.net.XhrManager.ERROR_ID_IN_USE_ = '[goog.net.XhrManager] ID in use';
    +
    +
    +/**
    + * The goog.net.EventType's to listen/unlisten for on the XhrIo object.
    + * @type {Array<goog.net.EventType>}
    + * @private
    + */
    +goog.net.XhrManager.XHR_EVENT_TYPES_ = [
    +  goog.net.EventType.READY,
    +  goog.net.EventType.COMPLETE,
    +  goog.net.EventType.SUCCESS,
    +  goog.net.EventType.ERROR,
    +  goog.net.EventType.ABORT,
    +  goog.net.EventType.TIMEOUT];
    +
    +
    +/**
    + * Sets the number of milliseconds after which an incomplete request will be
    + * aborted. Zero means no timeout is set.
    + * @param {number} ms Timeout interval in milliseconds; 0 means none.
    + */
    +goog.net.XhrManager.prototype.setTimeoutInterval = function(ms) {
    +  this.timeoutInterval_ = Math.max(0, ms);
    +};
    +
    +
    +/**
    + * Returns the number of requests either in flight, or waiting to be sent.
    + * The count will include the current request if used within a COMPLETE event
    + * handler or callback.
    + * @return {number} The number of requests in flight or pending send.
    + */
    +goog.net.XhrManager.prototype.getOutstandingCount = function() {
    +  return this.requests_.getCount();
    +};
    +
    +
    +/**
    + * Returns an array of request ids that are either in flight, or waiting to
    + * be sent. The id of the current request will be included if used within a
    + * COMPLETE event handler or callback.
    + * @return {!Array<string>} Request ids in flight or pending send.
    + */
    +goog.net.XhrManager.prototype.getOutstandingRequestIds = function() {
    +  return this.requests_.getKeys();
    +};
    +
    +
    +/**
    + * Registers the given request to be sent. Throws an error if a request
    + * already exists with the given ID.
    + * NOTE: It is not sent immediately. It is queued and will be sent when an
    + * XhrIo object becomes available, taking into account the request's
    + * priority.
    + * @param {string} id The id of the request.
    + * @param {string} url Uri to make the request too.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
    + *     opt_content Post data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + * @param {number=} opt_priority The priority of the request. A smaller value
    + *     means a higher priority.
    + * @param {Function=} opt_callback Callback function for when request is
    + *     complete. The only param is the event object from the COMPLETE event.
    + * @param {number=} opt_maxRetries The maximum number of times the request
    + *     should be retried.
    + * @param {goog.net.XhrIo.ResponseType=} opt_responseType The response type of
    + *     this request; defaults to goog.net.XhrIo.ResponseType.DEFAULT.
    + * @return {!goog.net.XhrManager.Request} The queued request object.
    + */
    +goog.net.XhrManager.prototype.send = function(
    +    id,
    +    url,
    +    opt_method,
    +    opt_content,
    +    opt_headers,
    +    opt_priority,
    +    opt_callback,
    +    opt_maxRetries,
    +    opt_responseType) {
    +  var requests = this.requests_;
    +  // Check if there is already a request with the given id.
    +  if (requests.get(id)) {
    +    throw Error(goog.net.XhrManager.ERROR_ID_IN_USE_);
    +  }
    +
    +  // Make the Request object.
    +  var request = new goog.net.XhrManager.Request(
    +      url,
    +      goog.bind(this.handleEvent_, this, id),
    +      opt_method,
    +      opt_content,
    +      opt_headers,
    +      opt_callback,
    +      goog.isDef(opt_maxRetries) ? opt_maxRetries : this.maxRetries_,
    +      opt_responseType);
    +  this.requests_.set(id, request);
    +
    +  // Setup the callback for the pool.
    +  var callback = goog.bind(this.handleAvailableXhr_, this, id);
    +  this.xhrPool_.getObject(callback, opt_priority);
    +
    +  return request;
    +};
    +
    +
    +/**
    + * Aborts the request associated with id.
    + * @param {string} id The id of the request to abort.
    + * @param {boolean=} opt_force If true, remove the id now so it can be reused.
    + *     No events are fired and the callback is not called when forced.
    + */
    +goog.net.XhrManager.prototype.abort = function(id, opt_force) {
    +  var request = this.requests_.get(id);
    +  if (request) {
    +    var xhrIo = request.xhrIo;
    +    request.setAborted(true);
    +    if (opt_force) {
    +      if (xhrIo) {
    +        // We remove listeners to make sure nothing gets called if a new request
    +        // with the same id is made.
    +        this.removeXhrListener_(xhrIo, request.getXhrEventCallback());
    +        goog.events.listenOnce(
    +            xhrIo,
    +            goog.net.EventType.READY,
    +            function() { this.xhrPool_.releaseObject(xhrIo); },
    +            false,
    +            this);
    +      }
    +      this.requests_.remove(id);
    +    }
    +    if (xhrIo) {
    +      xhrIo.abort();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles when an XhrIo object becomes available. Sets up the events, fires
    + * the READY event, and starts the process to send the request.
    + * @param {string} id The id of the request the XhrIo is for.
    + * @param {goog.net.XhrIo} xhrIo The available XhrIo object.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleAvailableXhr_ = function(id, xhrIo) {
    +  var request = this.requests_.get(id);
    +  // Make sure the request doesn't already have an XhrIo attached. This can
    +  // happen if a forced abort occurs before an XhrIo is available, and a new
    +  // request with the same id is made.
    +  if (request && !request.xhrIo) {
    +    this.addXhrListener_(xhrIo, request.getXhrEventCallback());
    +
    +    // Set properties for the XhrIo.
    +    xhrIo.setTimeoutInterval(this.timeoutInterval_);
    +    xhrIo.setResponseType(request.getResponseType());
    +
    +    // Add a reference to the XhrIo object to the request.
    +    request.xhrIo = xhrIo;
    +
    +    // Notify the listeners.
    +    this.dispatchEvent(new goog.net.XhrManager.Event(
    +        goog.net.EventType.READY, this, id, xhrIo));
    +
    +    // Send the request.
    +    this.retry_(id, xhrIo);
    +
    +    // If the request was aborted before it got an XhrIo object, abort it now.
    +    if (request.getAborted()) {
    +      xhrIo.abort();
    +    }
    +  } else {
    +    // If the request has an XhrIo object already, or no request exists, just
    +    // return the XhrIo back to the pool.
    +    this.xhrPool_.releaseObject(xhrIo);
    +  }
    +};
    +
    +
    +/**
    + * Handles all events fired by the XhrIo object for a given request.
    + * @param {string} id The id of the request.
    + * @param {goog.events.Event} e The event.
    + * @return {Object} The return value from the handler, if any.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleEvent_ = function(id, e) {
    +  var xhrIo = /** @type {goog.net.XhrIo} */(e.target);
    +  switch (e.type) {
    +    case goog.net.EventType.READY:
    +      this.retry_(id, xhrIo);
    +      break;
    +
    +    case goog.net.EventType.COMPLETE:
    +      return this.handleComplete_(id, xhrIo, e);
    +
    +    case goog.net.EventType.SUCCESS:
    +      this.handleSuccess_(id, xhrIo);
    +      break;
    +
    +    // A timeout is handled like an error.
    +    case goog.net.EventType.TIMEOUT:
    +    case goog.net.EventType.ERROR:
    +      this.handleError_(id, xhrIo);
    +      break;
    +
    +    case goog.net.EventType.ABORT:
    +      this.handleAbort_(id, xhrIo);
    +      break;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Attempts to retry the given request. If the request has already attempted
    + * the maximum number of retries, then it removes the request and releases
    + * the XhrIo object back into the pool.
    + * @param {string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object.
    + * @private
    + */
    +goog.net.XhrManager.prototype.retry_ = function(id, xhrIo) {
    +  var request = this.requests_.get(id);
    +
    +  // If the request has not completed and it is below its max. retries.
    +  if (request && !request.getCompleted() && !request.hasReachedMaxRetries()) {
    +    request.increaseAttemptCount();
    +    xhrIo.send(request.getUrl(), request.getMethod(), request.getContent(),
    +        request.getHeaders());
    +  } else {
    +    if (request) {
    +      // Remove the events on the XhrIo objects.
    +      this.removeXhrListener_(xhrIo, request.getXhrEventCallback());
    +
    +      // Remove the request.
    +      this.requests_.remove(id);
    +    }
    +    // Release the XhrIo object back into the pool.
    +    this.xhrPool_.releaseObject(xhrIo);
    +  }
    +};
    +
    +
    +/**
    + * Handles the complete of a request. Dispatches the COMPLETE event and sets the
    + * the request as completed if the request has succeeded, or is done retrying.
    + * @param {string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object.
    + * @param {goog.events.Event} e The original event.
    + * @return {Object} The return value from the callback, if any.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleComplete_ = function(id, xhrIo, e) {
    +  // Only if the request is done processing should a COMPLETE event be fired.
    +  var request = this.requests_.get(id);
    +  if (xhrIo.getLastErrorCode() == goog.net.ErrorCode.ABORT ||
    +      xhrIo.isSuccess() || request.hasReachedMaxRetries()) {
    +    this.dispatchEvent(new goog.net.XhrManager.Event(
    +        goog.net.EventType.COMPLETE, this, id, xhrIo));
    +
    +    // If the request exists, we mark it as completed and call the callback
    +    if (request) {
    +      request.setCompleted(true);
    +      // Call the complete callback as if it was set as a COMPLETE event on the
    +      // XhrIo directly.
    +      if (request.getCompleteCallback()) {
    +        return request.getCompleteCallback().call(xhrIo, e);
    +      }
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Handles the abort of an underlying XhrIo object.
    + * @param {string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleAbort_ = function(id, xhrIo) {
    +  // Fire event.
    +  // NOTE: The complete event should always be fired before the abort event, so
    +  // the bulk of the work is done in handleComplete.
    +  this.dispatchEvent(new goog.net.XhrManager.Event(
    +      goog.net.EventType.ABORT, this, id, xhrIo));
    +};
    +
    +
    +/**
    + * Handles the success of a request. Dispatches the SUCCESS event and sets the
    + * the request as completed.
    + * @param {string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleSuccess_ = function(id, xhrIo) {
    +  // Fire event.
    +  // NOTE: We don't release the XhrIo object from the pool here.
    +  // It is released in the retry method, when we know it is back in the
    +  // ready state.
    +  this.dispatchEvent(new goog.net.XhrManager.Event(
    +      goog.net.EventType.SUCCESS, this, id, xhrIo));
    +};
    +
    +
    +/**
    + * Handles the error of a request. If the request has not reach its maximum
    + * number of retries, then it lets the request retry naturally (will let the
    + * request hit the READY state). Else, it dispatches the ERROR event.
    + * @param {string} id The id of the request.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object.
    + * @private
    + */
    +goog.net.XhrManager.prototype.handleError_ = function(id, xhrIo) {
    +  var request = this.requests_.get(id);
    +
    +  // If the maximum number of retries has been reached.
    +  if (request.hasReachedMaxRetries()) {
    +    // Fire event.
    +    // NOTE: We don't release the XhrIo object from the pool here.
    +    // It is released in the retry method, when we know it is back in the
    +    // ready state.
    +    this.dispatchEvent(new goog.net.XhrManager.Event(
    +        goog.net.EventType.ERROR, this, id, xhrIo));
    +  }
    +};
    +
    +
    +/**
    + * Remove listeners for XHR events on an XhrIo object.
    + * @param {goog.net.XhrIo} xhrIo The object to stop listenening to events on.
    + * @param {Function} func The callback to remove from event handling.
    + * @param {string|Array<string>=} opt_types Event types to remove listeners
    + *     for. Defaults to XHR_EVENT_TYPES_.
    + * @private
    + */
    +goog.net.XhrManager.prototype.removeXhrListener_ = function(xhrIo,
    +                                                            func,
    +                                                            opt_types) {
    +  var types = opt_types || goog.net.XhrManager.XHR_EVENT_TYPES_;
    +  this.eventHandler_.unlisten(xhrIo, types, func);
    +};
    +
    +
    +/**
    + * Adds a listener for XHR events on an XhrIo object.
    + * @param {goog.net.XhrIo} xhrIo The object listen to events on.
    + * @param {Function} func The callback when the event occurs.
    + * @param {string|Array<string>=} opt_types Event types to attach listeners to.
    + *     Defaults to XHR_EVENT_TYPES_.
    + * @private
    + */
    +goog.net.XhrManager.prototype.addXhrListener_ = function(xhrIo,
    +                                                         func,
    +                                                         opt_types) {
    +  var types = opt_types || goog.net.XhrManager.XHR_EVENT_TYPES_;
    +  this.eventHandler_.listen(xhrIo, types, func);
    +};
    +
    +
    +/** @override */
    +goog.net.XhrManager.prototype.disposeInternal = function() {
    +  goog.net.XhrManager.superClass_.disposeInternal.call(this);
    +
    +  this.xhrPool_.dispose();
    +  this.xhrPool_ = null;
    +
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +
    +  this.requests_.clear();
    +  this.requests_ = null;
    +};
    +
    +
    +
    +/**
    + * An event dispatched by XhrManager.
    + *
    + * @param {goog.net.EventType} type Event Type.
    + * @param {goog.net.XhrManager} target Reference to the object that is the
    + *     target of this event.
    + * @param {string} id The id of the request this event is for.
    + * @param {goog.net.XhrIo} xhrIo The XhrIo object of the request.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.net.XhrManager.Event = function(type, target, id, xhrIo) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * The id of the request this event is for.
    +   * @type {string}
    +   */
    +  this.id = id;
    +
    +  /**
    +   * The XhrIo object of the request.
    +   * @type {goog.net.XhrIo}
    +   */
    +  this.xhrIo = xhrIo;
    +};
    +goog.inherits(goog.net.XhrManager.Event, goog.events.Event);
    +
    +
    +
    +/**
    + * An encapsulation of everything needed to make a Xhr request.
    + * NOTE: This is used internal to the XhrManager.
    + *
    + * @param {string} url Uri to make the request too.
    + * @param {Function} xhrEventCallback Callback attached to the events of the
    + *     XhrIo object of the request.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=}
    + *     opt_content Post data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + * @param {Function=} opt_callback Callback function for when request is
    + *     complete. NOTE: Only 1 callback supported across all events.
    + * @param {number=} opt_maxRetries The maximum number of times the request
    + *     should be retried (Default: 1).
    + * @param {goog.net.XhrIo.ResponseType=} opt_responseType The response type of
    + *     this request; defaults to goog.net.XhrIo.ResponseType.DEFAULT.
    + *
    + * @constructor
    + * @final
    + */
    +goog.net.XhrManager.Request = function(url, xhrEventCallback, opt_method,
    +    opt_content, opt_headers, opt_callback, opt_maxRetries, opt_responseType) {
    +  /**
    +   * Uri to make the request too.
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * Send method.
    +   * @type {string}
    +   * @private
    +   */
    +  this.method_ = opt_method || 'GET';
    +
    +  /**
    +   * Post data.
    +   * @type {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string|undefined}
    +   * @private
    +   */
    +  this.content_ = opt_content;
    +
    +  /**
    +   *  Map of headers
    +   * @type {Object|goog.structs.Map|null}
    +   * @private
    +   */
    +  this.headers_ = opt_headers || null;
    +
    +  /**
    +   * The maximum number of times the request should be retried.
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxRetries_ = goog.isDef(opt_maxRetries) ? opt_maxRetries : 1;
    +
    +  /**
    +   * The number of attempts  so far.
    +   * @type {number}
    +   * @private
    +   */
    +  this.attemptCount_ = 0;
    +
    +  /**
    +   * Whether the request has been completed.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.completed_ = false;
    +
    +  /**
    +   * Whether the request has been aborted.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.aborted_ = false;
    +
    +  /**
    +   * Callback attached to the events of the XhrIo object.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.xhrEventCallback_ = xhrEventCallback;
    +
    +  /**
    +   * Callback function called when request is complete.
    +   * @type {Function|undefined}
    +   * @private
    +   */
    +  this.completeCallback_ = opt_callback;
    +
    +  /**
    +   * A response type to set on this.xhrIo when it's populated.
    +   * @type {!goog.net.XhrIo.ResponseType}
    +   * @private
    +   */
    +  this.responseType_ = opt_responseType || goog.net.XhrIo.ResponseType.DEFAULT;
    +
    +  /**
    +   * The XhrIo instance handling this request. Set in handleAvailableXhr.
    +   * @type {goog.net.XhrIo}
    +   */
    +  this.xhrIo = null;
    +
    +};
    +
    +
    +/**
    + * Gets the uri.
    + * @return {string} The uri to make the request to.
    + */
    +goog.net.XhrManager.Request.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * Gets the send method.
    + * @return {string} The send method.
    + */
    +goog.net.XhrManager.Request.prototype.getMethod = function() {
    +  return this.method_;
    +};
    +
    +
    +/**
    + * Gets the post data.
    + * @return {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string|undefined}
    + *     The post data.
    + */
    +goog.net.XhrManager.Request.prototype.getContent = function() {
    +  return this.content_;
    +};
    +
    +
    +/**
    + * Gets the map of headers.
    + * @return {Object|goog.structs.Map} The map of headers.
    + */
    +goog.net.XhrManager.Request.prototype.getHeaders = function() {
    +  return this.headers_;
    +};
    +
    +
    +/**
    + * Gets the maximum number of times the request should be retried.
    + * @return {number} The maximum number of times the request should be retried.
    + */
    +goog.net.XhrManager.Request.prototype.getMaxRetries = function() {
    +  return this.maxRetries_;
    +};
    +
    +
    +/**
    + * Gets the number of attempts so far.
    + * @return {number} The number of attempts so far.
    + */
    +goog.net.XhrManager.Request.prototype.getAttemptCount = function() {
    +  return this.attemptCount_;
    +};
    +
    +
    +/**
    + * Increases the number of attempts so far.
    + */
    +goog.net.XhrManager.Request.prototype.increaseAttemptCount = function() {
    +  this.attemptCount_++;
    +};
    +
    +
    +/**
    + * Returns whether the request has reached the maximum number of retries.
    + * @return {boolean} Whether the request has reached the maximum number of
    + *     retries.
    + */
    +goog.net.XhrManager.Request.prototype.hasReachedMaxRetries = function() {
    +  return this.attemptCount_ > this.maxRetries_;
    +};
    +
    +
    +/**
    + * Sets the completed status.
    + * @param {boolean} complete The completed status.
    + */
    +goog.net.XhrManager.Request.prototype.setCompleted = function(complete) {
    +  this.completed_ = complete;
    +};
    +
    +
    +/**
    + * Gets the completed status.
    + * @return {boolean} The completed status.
    + */
    +goog.net.XhrManager.Request.prototype.getCompleted = function() {
    +  return this.completed_;
    +};
    +
    +
    +/**
    + * Sets the aborted status.
    + * @param {boolean} aborted True if the request was aborted, otherwise False.
    + */
    +goog.net.XhrManager.Request.prototype.setAborted = function(aborted) {
    +  this.aborted_ = aborted;
    +};
    +
    +
    +/**
    + * Gets the aborted status.
    + * @return {boolean} True if request was aborted, otherwise False.
    + */
    +goog.net.XhrManager.Request.prototype.getAborted = function() {
    +  return this.aborted_;
    +};
    +
    +
    +/**
    + * Gets the callback attached to the events of the XhrIo object.
    + * @return {Function} The callback attached to the events of the
    + *     XhrIo object.
    + */
    +goog.net.XhrManager.Request.prototype.getXhrEventCallback = function() {
    +  return this.xhrEventCallback_;
    +};
    +
    +
    +/**
    + * Gets the callback for when the request is complete.
    + * @return {Function|undefined} The callback for when the request is complete.
    + */
    +goog.net.XhrManager.Request.prototype.getCompleteCallback = function() {
    +  return this.completeCallback_;
    +};
    +
    +
    +/**
    + * Gets the response type that will be set on this request's XhrIo when it's
    + * available.
    + * @return {!goog.net.XhrIo.ResponseType} The response type to be set
    + *     when an XhrIo becomes available to this request.
    + */
    +goog.net.XhrManager.Request.prototype.getResponseType = function() {
    +  return this.responseType_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.html b/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.html
    new file mode 100644
    index 0000000000..fed27129fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.net.XhrManager
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.XhrManagerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.js b/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.js
    new file mode 100644
    index 0000000000..1c85541c31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xhrmanager_test.js
    @@ -0,0 +1,120 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.XhrManagerTest');
    +goog.setTestOnly('goog.net.XhrManagerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XhrManager');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIoPool');
    +goog.require('goog.testing.recordFunction');
    +
    +
    +/** @type {goog.net.XhrManager} */
    +var xhrManager;
    +
    +
    +/** @type {goog.testing.net.XhrIo} */
    +var xhrIo;
    +
    +
    +function setUp() {
    +  xhrManager = new goog.net.XhrManager();
    +  xhrManager.xhrPool_ = new goog.testing.net.XhrIoPool();
    +  xhrIo = xhrManager.xhrPool_.getXhr();
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(xhrManager);
    +}
    +
    +
    +function testGetOutstandingRequestIds() {
    +  assertArrayEquals(
    +      'No outstanding requests', [], xhrManager.getOutstandingRequestIds());
    +
    +  xhrManager.send('test1', '/test1');
    +  assertArrayEquals(
    +      'Single outstanding request', ['test1'],
    +      xhrManager.getOutstandingRequestIds());
    +
    +  xhrManager.send('test2', '/test2');
    +  assertArrayEquals(
    +      'Two outstanding requests', ['test1', 'test2'],
    +      xhrManager.getOutstandingRequestIds());
    +
    +  xhrIo.simulateResponse(200, 'data');
    +  assertArrayEquals(
    +      'Single outstanding request', ['test2'],
    +      xhrManager.getOutstandingRequestIds());
    +
    +  xhrIo.simulateResponse(200, 'data');
    +  assertArrayEquals(
    +      'No outstanding requests', [], xhrManager.getOutstandingRequestIds());
    +}
    +
    +
    +function testForceAbortQueuedRequest() {
    +  xhrManager.send('test', '/test');
    +  xhrManager.send('queued', '/queued');
    +
    +  assertNotThrows(
    +      'Forced abort of queued request should not throw an error',
    +      goog.bind(xhrManager.abort, xhrManager, 'queued', true));
    +
    +  assertNotThrows(
    +      'Forced abort of normal request should not throw an error',
    +      goog.bind(xhrManager.abort, xhrManager, 'test', true));
    +}
    +
    +
    +function testDefaultResponseType() {
    +  var callback = goog.testing.recordFunction(function(e) {
    +    assertEquals('test1', e.id);
    +    assertEquals(
    +        goog.net.XhrIo.ResponseType.DEFAULT, e.xhrIo.getResponseType());
    +    eventCalled = true;
    +  });
    +  goog.events.listenOnce(xhrManager, goog.net.EventType.READY, callback);
    +  xhrManager.send('test1', '/test2');
    +  assertEquals(1, callback.getCallCount());
    +
    +  xhrIo.simulateResponse(200, 'data');  // Do this to make tearDown() happy.
    +}
    +
    +
    +function testNonDefaultResponseType() {
    +  var callback = goog.testing.recordFunction(function(e) {
    +    assertEquals('test2', e.id);
    +    assertEquals(
    +        goog.net.XhrIo.ResponseType.ARRAY_BUFFER, e.xhrIo.getResponseType());
    +    eventCalled = true;
    +  });
    +  goog.events.listenOnce(xhrManager, goog.net.EventType.READY, callback);
    +  xhrManager.send('test2', '/test2',
    +      undefined /* opt_method */,
    +      undefined /* opt_content */,
    +      undefined /* opt_headers */,
    +      undefined /* opt_priority */,
    +      undefined /* opt_callback */,
    +      undefined /* opt_maxRetries */,
    +      goog.net.XhrIo.ResponseType.ARRAY_BUFFER);
    +  assertEquals(1, callback.getCallCount());
    +
    +  xhrIo.simulateResponse(200, 'data');  // Do this to make tearDown() happy.
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xmlhttp.js b/mapbox-gl/closure-library/closure/goog/net/xmlhttp.js
    new file mode 100644
    index 0000000000..8d59f00ee2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xmlhttp.js
    @@ -0,0 +1,246 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Low level handling of XMLHttpRequest.
    + * @author arv@google.com (Erik Arvidsson)
    + * @author dbk@google.com (David Barrett-Kahn)
    + */
    +
    +goog.provide('goog.net.DefaultXmlHttpFactory');
    +goog.provide('goog.net.XmlHttp');
    +goog.provide('goog.net.XmlHttp.OptionType');
    +goog.provide('goog.net.XmlHttp.ReadyState');
    +goog.provide('goog.net.XmlHttpDefines');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.net.WrapperXmlHttpFactory');
    +goog.require('goog.net.XmlHttpFactory');
    +
    +
    +/**
    + * Static class for creating XMLHttpRequest objects.
    + * @return {!goog.net.XhrLike.OrNative} A new XMLHttpRequest object.
    + */
    +goog.net.XmlHttp = function() {
    +  return goog.net.XmlHttp.factory_.createInstance();
    +};
    +
    +
    +/**
    + * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to
    + *     true bypasses the ActiveX probing code.
    + * NOTE(ruilopes): Due to the way JSCompiler works, this define *will not* strip
    + * out the ActiveX probing code from binaries.  To achieve this, use
    + * {@code goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR} instead.
    + * TODO(ruilopes): Collapse both defines.
    + */
    +goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false);
    +
    +
    +/** @const */
    +goog.net.XmlHttpDefines = {};
    +
    +
    +/**
    + * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to
    + *     true eliminates the ActiveX probing code.
    + */
    +goog.define('goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR', false);
    +
    +
    +/**
    + * Gets the options to use with the XMLHttpRequest objects obtained using
    + * the static methods.
    + * @return {Object} The options.
    + */
    +goog.net.XmlHttp.getOptions = function() {
    +  return goog.net.XmlHttp.factory_.getOptions();
    +};
    +
    +
    +/**
    + * Type of options that an XmlHttp object can have.
    + * @enum {number}
    + */
    +goog.net.XmlHttp.OptionType = {
    +  /**
    +   * Whether a goog.nullFunction should be used to clear the onreadystatechange
    +   * handler instead of null.
    +   */
    +  USE_NULL_FUNCTION: 0,
    +
    +  /**
    +   * NOTE(user): In IE if send() errors on a *local* request the readystate
    +   * is still changed to COMPLETE.  We need to ignore it and allow the
    +   * try/catch around send() to pick up the error.
    +   */
    +  LOCAL_REQUEST_ERROR: 1
    +};
    +
    +
    +/**
    + * Status constants for XMLHTTP, matches:
    + * http://msdn.microsoft.com/library/default.asp?url=/library/
    + *   en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp
    + * @enum {number}
    + */
    +goog.net.XmlHttp.ReadyState = {
    +  /**
    +   * Constant for when xmlhttprequest.readyState is uninitialized
    +   */
    +  UNINITIALIZED: 0,
    +
    +  /**
    +   * Constant for when xmlhttprequest.readyState is loading.
    +   */
    +  LOADING: 1,
    +
    +  /**
    +   * Constant for when xmlhttprequest.readyState is loaded.
    +   */
    +  LOADED: 2,
    +
    +  /**
    +   * Constant for when xmlhttprequest.readyState is in an interactive state.
    +   */
    +  INTERACTIVE: 3,
    +
    +  /**
    +   * Constant for when xmlhttprequest.readyState is completed
    +   */
    +  COMPLETE: 4
    +};
    +
    +
    +/**
    + * The global factory instance for creating XMLHttpRequest objects.
    + * @type {goog.net.XmlHttpFactory}
    + * @private
    + */
    +goog.net.XmlHttp.factory_;
    +
    +
    +/**
    + * Sets the factories for creating XMLHttpRequest objects and their options.
    + * @param {Function} factory The factory for XMLHttpRequest objects.
    + * @param {Function} optionsFactory The factory for options.
    + * @deprecated Use setGlobalFactory instead.
    + */
    +goog.net.XmlHttp.setFactory = function(factory, optionsFactory) {
    +  goog.net.XmlHttp.setGlobalFactory(new goog.net.WrapperXmlHttpFactory(
    +      goog.asserts.assert(factory),
    +      goog.asserts.assert(optionsFactory)));
    +};
    +
    +
    +/**
    + * Sets the global factory object.
    + * @param {!goog.net.XmlHttpFactory} factory New global factory object.
    + */
    +goog.net.XmlHttp.setGlobalFactory = function(factory) {
    +  goog.net.XmlHttp.factory_ = factory;
    +};
    +
    +
    +
    +/**
    + * Default factory to use when creating xhr objects.  You probably shouldn't be
    + * instantiating this directly, but rather using it via goog.net.XmlHttp.
    + * @extends {goog.net.XmlHttpFactory}
    + * @constructor
    + */
    +goog.net.DefaultXmlHttpFactory = function() {
    +  goog.net.XmlHttpFactory.call(this);
    +};
    +goog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory);
    +
    +
    +/** @override */
    +goog.net.DefaultXmlHttpFactory.prototype.createInstance = function() {
    +  var progId = this.getProgId_();
    +  if (progId) {
    +    return new ActiveXObject(progId);
    +  } else {
    +    return new XMLHttpRequest();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.net.DefaultXmlHttpFactory.prototype.internalGetOptions = function() {
    +  var progId = this.getProgId_();
    +  var options = {};
    +  if (progId) {
    +    options[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] = true;
    +    options[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] = true;
    +  }
    +  return options;
    +};
    +
    +
    +/**
    + * The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.net.DefaultXmlHttpFactory.prototype.ieProgId_;
    +
    +
    +/**
    + * Initialize the private state used by other functions.
    + * @return {string} The ActiveX PROG ID string to use to create xhr's in IE.
    + * @private
    + */
    +goog.net.DefaultXmlHttpFactory.prototype.getProgId_ = function() {
    +  if (goog.net.XmlHttp.ASSUME_NATIVE_XHR ||
    +      goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR) {
    +    return '';
    +  }
    +
    +  // The following blog post describes what PROG IDs to use to create the
    +  // XMLHTTP object in Internet Explorer:
    +  // http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx
    +  // However we do not (yet) fully trust that this will be OK for old versions
    +  // of IE on Win9x so we therefore keep the last 2.
    +  if (!this.ieProgId_ && typeof XMLHttpRequest == 'undefined' &&
    +      typeof ActiveXObject != 'undefined') {
    +    // Candidate Active X types.
    +    var ACTIVE_X_IDENTS = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0',
    +                           'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
    +    for (var i = 0; i < ACTIVE_X_IDENTS.length; i++) {
    +      var candidate = ACTIVE_X_IDENTS[i];
    +      /** @preserveTry */
    +      try {
    +        new ActiveXObject(candidate);
    +        // NOTE(user): cannot assign progid and return candidate in one line
    +        // because JSCompiler complaings: BUG 658126
    +        this.ieProgId_ = candidate;
    +        return candidate;
    +      } catch (e) {
    +        // do nothing; try next choice
    +      }
    +    }
    +
    +    // couldn't find any matches
    +    throw Error('Could not create ActiveXObject. ActiveX might be disabled,' +
    +                ' or MSXML might not be installed');
    +  }
    +
    +  return /** @type {string} */ (this.ieProgId_);
    +};
    +
    +
    +//Set the global factory to an instance of the default factory.
    +goog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory());
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xmlhttpfactory.js b/mapbox-gl/closure-library/closure/goog/net/xmlhttpfactory.js
    new file mode 100644
    index 0000000000..8187edb471
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xmlhttpfactory.js
    @@ -0,0 +1,67 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Interface for a factory for creating XMLHttpRequest objects
    + * and metadata about them.
    + * @author dbk@google.com (David Barrett-Kahn)
    + */
    +
    +goog.provide('goog.net.XmlHttpFactory');
    +
    +/** @suppress {extraRequire} Typedef. */
    +goog.require('goog.net.XhrLike');
    +
    +
    +
    +/**
    + * Abstract base class for an XmlHttpRequest factory.
    + * @constructor
    + */
    +goog.net.XmlHttpFactory = function() {
    +};
    +
    +
    +/**
    + * Cache of options - we only actually call internalGetOptions once.
    + * @type {Object}
    + * @private
    + */
    +goog.net.XmlHttpFactory.prototype.cachedOptions_ = null;
    +
    +
    +/**
    + * @return {!goog.net.XhrLike.OrNative} A new XhrLike instance.
    + */
    +goog.net.XmlHttpFactory.prototype.createInstance = goog.abstractMethod;
    +
    +
    +/**
    + * @return {Object} Options describing how xhr objects obtained from this
    + *     factory should be used.
    + */
    +goog.net.XmlHttpFactory.prototype.getOptions = function() {
    +  return this.cachedOptions_ ||
    +      (this.cachedOptions_ = this.internalGetOptions());
    +};
    +
    +
    +/**
    + * Override this method in subclasses to preserve the caching offered by
    + * getOptions().
    + * @return {Object} Options describing how xhr objects obtained from this
    + *     factory should be used.
    + * @protected
    + */
    +goog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel.js b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel.js
    new file mode 100644
    index 0000000000..a8e3a6f5a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel.js
    @@ -0,0 +1,861 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the class CrossPageChannel, the main class in
    + * goog.net.xpc.
    + *
    + * @see ../../demos/xpc/index.html
    + */
    +
    +goog.provide('goog.net.xpc.CrossPageChannel');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.async.Delay');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.json');
    +goog.require('goog.log');
    +goog.require('goog.messaging.AbstractChannel');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.ChannelStates');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.DirectTransport');
    +goog.require('goog.net.xpc.FrameElementMethodTransport');
    +goog.require('goog.net.xpc.IframePollingTransport');
    +goog.require('goog.net.xpc.IframeRelayTransport');
    +goog.require('goog.net.xpc.NativeMessagingTransport');
    +goog.require('goog.net.xpc.NixTransport');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.net.xpc.UriCfgFields');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A communication channel between two documents from different domains.
    + * Provides asynchronous messaging.
    + *
    + * @param {Object} cfg Channel configuration object.
    + * @param {goog.dom.DomHelper=} opt_domHelper The optional dom helper to
    + *     use for looking up elements in the dom.
    + * @constructor
    + * @extends {goog.messaging.AbstractChannel}
    + */
    +goog.net.xpc.CrossPageChannel = function(cfg, opt_domHelper) {
    +  goog.net.xpc.CrossPageChannel.base(this, 'constructor');
    +
    +  for (var i = 0, uriField; uriField = goog.net.xpc.UriCfgFields[i]; i++) {
    +    if (uriField in cfg && !/^https?:\/\//.test(cfg[uriField])) {
    +      throw Error('URI ' + cfg[uriField] + ' is invalid for field ' + uriField);
    +    }
    +  }
    +
    +  /**
    +   * The configuration for this channel.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.cfg_ = cfg;
    +
    +  /**
    +   * The name of the channel. Please use
    +   * <code>updateChannelNameAndCatalog</code> to change this from the transports
    +   * vs changing the property directly.
    +   * @type {string}
    +   */
    +  this.name = this.cfg_[goog.net.xpc.CfgFields.CHANNEL_NAME] ||
    +      goog.net.xpc.getRandomString(10);
    +
    +  /**
    +   * The dom helper to use for accessing the dom.
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Collects deferred function calls which will be made once the connection
    +   * has been fully set up.
    +   * @type {!Array<function()>}
    +   * @private
    +   */
    +  this.deferredDeliveries_ = [];
    +
    +  /**
    +   * An event handler used to listen for load events on peer iframes.
    +   * @type {!goog.events.EventHandler<!goog.net.xpc.CrossPageChannel>}
    +   * @private
    +   */
    +  this.peerLoadHandler_ = new goog.events.EventHandler(this);
    +
    +  // If LOCAL_POLL_URI or PEER_POLL_URI is not available, try using
    +  // robots.txt from that host.
    +  cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] =
    +      cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] ||
    +      goog.uri.utils.getHost(this.domHelper_.getWindow().location.href) +
    +          '/robots.txt';
    +  // PEER_URI is sometimes undefined in tests.
    +  cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] =
    +      cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] ||
    +      goog.uri.utils.getHost(cfg[goog.net.xpc.CfgFields.PEER_URI] || '') +
    +          '/robots.txt';
    +
    +  goog.net.xpc.channels[this.name] = this;
    +
    +  if (!goog.events.getListener(window, goog.events.EventType.UNLOAD,
    +      goog.net.xpc.CrossPageChannel.disposeAll_)) {
    +    // Set listener to dispose all registered channels on page unload.
    +    goog.events.listenOnce(window, goog.events.EventType.UNLOAD,
    +        goog.net.xpc.CrossPageChannel.disposeAll_);
    +  }
    +
    +  goog.log.info(goog.net.xpc.logger, 'CrossPageChannel created: ' + this.name);
    +};
    +goog.inherits(goog.net.xpc.CrossPageChannel, goog.messaging.AbstractChannel);
    +
    +
    +/**
    + * Regexp for escaping service names.
    + * @type {RegExp}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_ESCAPE_RE_ =
    +    new RegExp('^%*' + goog.net.xpc.TRANSPORT_SERVICE_ + '$');
    +
    +
    +/**
    + * Regexp for unescaping service names.
    + * @type {RegExp}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_UNESCAPE_RE_ =
    +    new RegExp('^%+' + goog.net.xpc.TRANSPORT_SERVICE_ + '$');
    +
    +
    +/**
    + * A delay between the transport reporting as connected and the calling of the
    + * connection callback.  Sometimes used to paper over timing vulnerabilities.
    + * @type {goog.async.Delay}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.connectionDelay_ = null;
    +
    +
    +/**
    + * A deferred which is set to non-null while a peer iframe is being created
    + * but has not yet thrown its load event, and which fires when that load event
    + * arrives.
    + * @type {goog.async.Deferred}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.peerWindowDeferred_ = null;
    +
    +
    +/**
    + * The transport.
    + * @type {goog.net.xpc.Transport?}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.transport_ = null;
    +
    +
    +/**
    + * The channel state.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.state_ =
    +    goog.net.xpc.ChannelStates.NOT_CONNECTED;
    +
    +
    +/**
    + * @override
    + * @return {boolean} Whether the channel is connected.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.isConnected = function() {
    +  return this.state_ == goog.net.xpc.ChannelStates.CONNECTED;
    +};
    +
    +
    +/**
    + * Reference to the window-object of the peer page.
    + * @type {Object}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.peerWindowObject_ = null;
    +
    +
    +/**
    + * Reference to the iframe-element.
    + * @type {Object}
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.iframeElement_ = null;
    +
    +
    +/**
    + * Returns the configuration object for this channel.
    + * Package private. Do not call from outside goog.net.xpc.
    + *
    + * @return {Object} The configuration object for this channel.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getConfig = function() {
    +  return this.cfg_;
    +};
    +
    +
    +/**
    + * Returns a reference to the iframe-element.
    + * Package private. Do not call from outside goog.net.xpc.
    + *
    + * @return {Object} A reference to the iframe-element.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getIframeElement = function() {
    +  return this.iframeElement_;
    +};
    +
    +
    +/**
    + * Sets the window object the foreign document resides in.
    + *
    + * @param {Object} peerWindowObject The window object of the peer.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.setPeerWindowObject =
    +    function(peerWindowObject) {
    +  this.peerWindowObject_ = peerWindowObject;
    +};
    +
    +
    +/**
    + * Returns the window object the foreign document resides in.
    + *
    + * @return {Object} The window object of the peer.
    + * @package
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getPeerWindowObject = function() {
    +  return this.peerWindowObject_;
    +};
    +
    +
    +/**
    + * Determines whether the peer window is available (e.g. not closed).
    + *
    + * @return {boolean} Whether the peer window is available.
    + * @package
    + */
    +goog.net.xpc.CrossPageChannel.prototype.isPeerAvailable = function() {
    +  // NOTE(user): This check is not reliable in IE, where a document in an
    +  // iframe does not get unloaded when removing the iframe element from the DOM.
    +  // TODO(user): Find something that works in IE as well.
    +  // NOTE(user): "!this.peerWindowObject_.closed" evaluates to 'false' in IE9
    +  // sometimes even though typeof(this.peerWindowObject_.closed) is boolean and
    +  // this.peerWindowObject_.closed evaluates to 'false'. Casting it to a Boolean
    +  // results in sane evaluation. When this happens, it's in the inner iframe
    +  // when querying its parent's 'closed' status. Note that this is a different
    +  // case than mibuerge@'s note above.
    +  try {
    +    return !!this.peerWindowObject_ && !Boolean(this.peerWindowObject_.closed);
    +  } catch (e) {
    +    // If the window is closing, an error may be thrown.
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Determine which transport type to use for this channel / useragent.
    + * @return {goog.net.xpc.TransportTypes|undefined} The best transport type.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.determineTransportType_ = function() {
    +  var transportType;
    +  if (goog.isFunction(document.postMessage) ||
    +      goog.isFunction(window.postMessage) ||
    +      // IE8 supports window.postMessage, but
    +      // typeof window.postMessage returns "object"
    +      (goog.userAgent.IE && window.postMessage)) {
    +    transportType = goog.net.xpc.TransportTypes.NATIVE_MESSAGING;
    +  } else if (goog.userAgent.GECKO) {
    +    transportType = goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
    +  } else if (goog.userAgent.IE &&
    +             this.cfg_[goog.net.xpc.CfgFields.PEER_RELAY_URI]) {
    +    transportType = goog.net.xpc.TransportTypes.IFRAME_RELAY;
    +  } else if (goog.userAgent.IE && goog.net.xpc.NixTransport.isNixSupported()) {
    +    transportType = goog.net.xpc.TransportTypes.NIX;
    +  } else {
    +    transportType = goog.net.xpc.TransportTypes.IFRAME_POLLING;
    +  }
    +  return transportType;
    +};
    +
    +
    +/**
    + * Creates the transport for this channel. Chooses from the available
    + * transport based on the user agent and the configuration.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.createTransport_ = function() {
    +  // return, if the transport has already been created
    +  if (this.transport_) {
    +    return;
    +  }
    +
    +  // TODO(user): Use goog.scope.
    +  var CfgFields = goog.net.xpc.CfgFields;
    +
    +  if (!this.cfg_[CfgFields.TRANSPORT]) {
    +    this.cfg_[CfgFields.TRANSPORT] =
    +        this.determineTransportType_();
    +  }
    +
    +  switch (this.cfg_[CfgFields.TRANSPORT]) {
    +    case goog.net.xpc.TransportTypes.NATIVE_MESSAGING:
    +      var protocolVersion = this.cfg_[
    +          CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION] || 2;
    +      this.transport_ = new goog.net.xpc.NativeMessagingTransport(
    +          this,
    +          this.cfg_[CfgFields.PEER_HOSTNAME],
    +          this.domHelper_,
    +          !!this.cfg_[CfgFields.ONE_SIDED_HANDSHAKE],
    +          protocolVersion);
    +      break;
    +    case goog.net.xpc.TransportTypes.NIX:
    +      this.transport_ = new goog.net.xpc.NixTransport(this, this.domHelper_);
    +      break;
    +    case goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD:
    +      this.transport_ =
    +          new goog.net.xpc.FrameElementMethodTransport(this, this.domHelper_);
    +      break;
    +    case goog.net.xpc.TransportTypes.IFRAME_RELAY:
    +      this.transport_ =
    +          new goog.net.xpc.IframeRelayTransport(this, this.domHelper_);
    +      break;
    +    case goog.net.xpc.TransportTypes.IFRAME_POLLING:
    +      this.transport_ =
    +          new goog.net.xpc.IframePollingTransport(this, this.domHelper_);
    +      break;
    +    case goog.net.xpc.TransportTypes.DIRECT:
    +      if (this.peerWindowObject_ &&
    +          goog.net.xpc.DirectTransport.isSupported(/** @type {!Window} */ (
    +              this.peerWindowObject_))) {
    +        this.transport_ =
    +            new goog.net.xpc.DirectTransport(this, this.domHelper_);
    +      } else {
    +        goog.log.info(
    +            goog.net.xpc.logger,
    +            'DirectTransport not supported for this window, peer window in' +
    +            ' different security context or not set yet.');
    +      }
    +      break;
    +  }
    +
    +  if (this.transport_) {
    +    goog.log.info(goog.net.xpc.logger,
    +        'Transport created: ' + this.transport_.getName());
    +  } else {
    +    throw Error('CrossPageChannel: No suitable transport found!');
    +  }
    +};
    +
    +
    +/**
    + * Returns the transport type in use for this channel.
    + * @return {number} Transport-type identifier.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getTransportType = function() {
    +  return this.transport_.getType();
    +};
    +
    +
    +/**
    + * Returns the tranport name in use for this channel.
    + * @return {string} The transport name.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getTransportName = function() {
    +  return this.transport_.getName();
    +};
    +
    +
    +/**
    + * @return {!Object} Configuration-object to be used by the peer to
    + *     initialize the channel.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getPeerConfiguration = function() {
    +  var peerCfg = {};
    +  peerCfg[goog.net.xpc.CfgFields.CHANNEL_NAME] = this.name;
    +  peerCfg[goog.net.xpc.CfgFields.TRANSPORT] =
    +      this.cfg_[goog.net.xpc.CfgFields.TRANSPORT];
    +  peerCfg[goog.net.xpc.CfgFields.ONE_SIDED_HANDSHAKE] =
    +      this.cfg_[goog.net.xpc.CfgFields.ONE_SIDED_HANDSHAKE];
    +
    +  if (this.cfg_[goog.net.xpc.CfgFields.LOCAL_RELAY_URI]) {
    +    peerCfg[goog.net.xpc.CfgFields.PEER_RELAY_URI] =
    +        this.cfg_[goog.net.xpc.CfgFields.LOCAL_RELAY_URI];
    +  }
    +  if (this.cfg_[goog.net.xpc.CfgFields.LOCAL_POLL_URI]) {
    +    peerCfg[goog.net.xpc.CfgFields.PEER_POLL_URI] =
    +        this.cfg_[goog.net.xpc.CfgFields.LOCAL_POLL_URI];
    +  }
    +  if (this.cfg_[goog.net.xpc.CfgFields.PEER_POLL_URI]) {
    +    peerCfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] =
    +        this.cfg_[goog.net.xpc.CfgFields.PEER_POLL_URI];
    +  }
    +  var role = this.cfg_[goog.net.xpc.CfgFields.ROLE];
    +  if (role) {
    +    peerCfg[goog.net.xpc.CfgFields.ROLE] =
    +        role == goog.net.xpc.CrossPageChannelRole.INNER ?
    +            goog.net.xpc.CrossPageChannelRole.OUTER :
    +            goog.net.xpc.CrossPageChannelRole.INNER;
    +  }
    +
    +  return peerCfg;
    +};
    +
    +
    +/**
    + * Creates the iframe containing the peer page in a specified parent element.
    + * This method does not connect the channel, connect() still has to be called
    + * separately.
    + *
    + * @param {!Element} parentElm The container element the iframe is appended to.
    + * @param {Function=} opt_configureIframeCb If present, this function gets
    + *     called with the iframe element as parameter to allow setting properties
    + *     on it before it gets added to the DOM. If absent, the iframe's width and
    + *     height are set to '100%'.
    + * @param {boolean=} opt_addCfgParam Whether to add the peer configuration as
    + *     URL parameter (default: true).
    + * @return {!HTMLIFrameElement} The iframe element.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.createPeerIframe = function(
    +    parentElm, opt_configureIframeCb, opt_addCfgParam) {
    +  goog.log.info(goog.net.xpc.logger, 'createPeerIframe()');
    +
    +  var iframeId = this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID];
    +  if (!iframeId) {
    +    // Create a randomized ID for the iframe element to avoid
    +    // bfcache-related issues.
    +    iframeId = this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID] =
    +        'xpcpeer' + goog.net.xpc.getRandomString(4);
    +  }
    +
    +  // TODO(user) Opera creates a history-entry when creating an iframe
    +  // programmatically as follows. Find a way which avoids this.
    +
    +  var iframeElm = goog.dom.getDomHelper(parentElm).createElement('IFRAME');
    +  iframeElm.id = iframeElm.name = iframeId;
    +  if (opt_configureIframeCb) {
    +    opt_configureIframeCb(iframeElm);
    +  } else {
    +    iframeElm.style.width = iframeElm.style.height = '100%';
    +  }
    +
    +  this.cleanUpIncompleteConnection_();
    +  this.peerWindowDeferred_ =
    +      new goog.async.Deferred(undefined, this);
    +  var peerUri = this.getPeerUri(opt_addCfgParam);
    +  this.peerLoadHandler_.listenOnceWithScope(iframeElm, 'load',
    +      this.peerWindowDeferred_.callback, false, this.peerWindowDeferred_);
    +
    +  if (goog.userAgent.GECKO || goog.userAgent.WEBKIT) {
    +    // Appending the iframe in a timeout to avoid a weird fastback issue, which
    +    // is present in Safari and Gecko.
    +    window.setTimeout(
    +        goog.bind(function() {
    +          parentElm.appendChild(iframeElm);
    +          iframeElm.src = peerUri.toString();
    +          goog.log.info(goog.net.xpc.logger,
    +              'peer iframe created (' + iframeId + ')');
    +        }, this), 1);
    +  } else {
    +    iframeElm.src = peerUri.toString();
    +    parentElm.appendChild(iframeElm);
    +    goog.log.info(goog.net.xpc.logger,
    +        'peer iframe created (' + iframeId + ')');
    +  }
    +
    +  return /** @type {!HTMLIFrameElement} */ (iframeElm);
    +};
    +
    +
    +/**
    + * Clean up after any incomplete attempt to establish and connect to a peer
    + * iframe.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.cleanUpIncompleteConnection_ =
    +    function() {
    +  if (this.peerWindowDeferred_) {
    +    this.peerWindowDeferred_.cancel();
    +    this.peerWindowDeferred_ = null;
    +  }
    +  this.deferredDeliveries_.length = 0;
    +  this.peerLoadHandler_.removeAll();
    +};
    +
    +
    +/**
    + * Returns the peer URI, with an optional URL parameter for configuring the peer
    + * window.
    + *
    + * @param {boolean=} opt_addCfgParam Whether to add the peer configuration as
    + *     URL parameter (default: true).
    + * @return {!goog.Uri} The peer URI.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getPeerUri = function(opt_addCfgParam) {
    +  var peerUri = this.cfg_[goog.net.xpc.CfgFields.PEER_URI];
    +  if (goog.isString(peerUri)) {
    +    peerUri = this.cfg_[goog.net.xpc.CfgFields.PEER_URI] =
    +        new goog.Uri(peerUri);
    +  }
    +
    +  // Add the channel configuration used by the peer as URL parameter.
    +  if (opt_addCfgParam !== false) {
    +    peerUri.setParameterValue('xpc',
    +                              goog.json.serialize(
    +                                  this.getPeerConfiguration()));
    +  }
    +
    +  return peerUri;
    +};
    +
    +
    +/**
    + * Initiates connecting the channel. When this method is called, all the
    + * information needed to connect the channel has to be available.
    + *
    + * @override
    + * @param {Function=} opt_connectCb The function to be called when the
    + * channel has been connected and is ready to be used.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.connect = function(opt_connectCb) {
    +  this.connectCb_ = opt_connectCb || goog.nullFunction;
    +
    +  // If this channel was previously closed, transition back to the NOT_CONNECTED
    +  // state to ensure that the connection can proceed (xpcDeliver blocks
    +  // transport messages while the connection state is CLOSED).
    +  if (this.state_ == goog.net.xpc.ChannelStates.CLOSED) {
    +    this.state_ = goog.net.xpc.ChannelStates.NOT_CONNECTED;
    +  }
    +
    +  // If we know of a peer window whose creation has been requested but is not
    +  // complete, peerWindowDeferred_ will be non-null, and we should block on it.
    +  if (this.peerWindowDeferred_) {
    +    this.peerWindowDeferred_.addCallback(this.continueConnection_);
    +  } else {
    +    this.continueConnection_();
    +  }
    +};
    +
    +
    +/**
    + * Continues the connection process once we're as sure as we can be that the
    + * peer iframe has been created.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.continueConnection_ = function() {
    +  goog.log.info(goog.net.xpc.logger, 'continueConnection_()');
    +  this.peerWindowDeferred_ = null;
    +  if (this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]) {
    +    this.iframeElement_ = this.domHelper_.getElement(
    +        this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]);
    +  }
    +  if (this.iframeElement_) {
    +    var winObj = this.iframeElement_.contentWindow;
    +    // accessing the window using contentWindow doesn't work in safari
    +    if (!winObj) {
    +      winObj = window.frames[this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]];
    +    }
    +    this.setPeerWindowObject(winObj);
    +  }
    +
    +  // if the peer window object has not been set at this point, we assume
    +  // being in an iframe and the channel is meant to be to the containing page
    +  if (!this.peerWindowObject_) {
    +    // throw an error if we are in the top window (== not in an iframe)
    +    if (window == window.top) {
    +      throw Error(
    +          "CrossPageChannel: Can't connect, peer window-object not set.");
    +    } else {
    +      this.setPeerWindowObject(window.parent);
    +    }
    +  }
    +
    +  this.createTransport_();
    +
    +  this.transport_.connect();
    +
    +  // Now we run any deferred deliveries collected while connection was deferred.
    +  while (this.deferredDeliveries_.length > 0) {
    +    this.deferredDeliveries_.shift()();
    +  }
    +};
    +
    +
    +/**
    + * Closes the channel.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.close = function() {
    +  this.cleanUpIncompleteConnection_();
    +  this.state_ = goog.net.xpc.ChannelStates.CLOSED;
    +  goog.dispose(this.transport_);
    +  this.transport_ = null;
    +  this.connectCb_ = null;
    +  goog.dispose(this.connectionDelay_);
    +  this.connectionDelay_ = null;
    +  goog.log.info(goog.net.xpc.logger, 'Channel "' + this.name + '" closed');
    +};
    +
    +
    +/**
    + * Package-private.
    + * Called by the transport when the channel is connected.
    + * @param {number=} opt_delay Delay this number of milliseconds before calling
    + *     the connection callback. Usage is discouraged, but can be used to paper
    + *     over timing vulnerabilities when there is no alternative.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.notifyConnected = function(opt_delay) {
    +  if (this.isConnected() ||
    +      (this.connectionDelay_ && this.connectionDelay_.isActive())) {
    +    return;
    +  }
    +  this.state_ = goog.net.xpc.ChannelStates.CONNECTED;
    +  goog.log.info(goog.net.xpc.logger, 'Channel "' + this.name + '" connected');
    +  goog.dispose(this.connectionDelay_);
    +  if (goog.isDef(opt_delay)) {
    +    this.connectionDelay_ =
    +        new goog.async.Delay(this.connectCb_, opt_delay);
    +    this.connectionDelay_.start();
    +  } else {
    +    this.connectionDelay_ = null;
    +    this.connectCb_();
    +  }
    +};
    +
    +
    +/**
    + * Alias for notifyConected, for backward compatibility reasons.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.notifyConnected_ =
    +    goog.net.xpc.CrossPageChannel.prototype.notifyConnected;
    +
    +
    +/**
    + * Called by the transport in case of an unrecoverable failure.
    + * Package private. Do not call from outside goog.net.xpc.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.notifyTransportError = function() {
    +  goog.log.info(goog.net.xpc.logger, 'Transport Error');
    +  this.close();
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.CrossPageChannel.prototype.send = function(serviceName, payload) {
    +  if (!this.isConnected()) {
    +    goog.log.error(goog.net.xpc.logger, 'Can\'t send. Channel not connected.');
    +    return;
    +  }
    +  // Check if the peer is still around.
    +  if (!this.isPeerAvailable()) {
    +    goog.log.error(goog.net.xpc.logger, 'Peer has disappeared.');
    +    this.close();
    +    return;
    +  }
    +  if (goog.isObject(payload)) {
    +    payload = goog.json.serialize(payload);
    +  }
    +
    +  // Partially URL-encode the service name because some characters (: and |) are
    +  // used as delimiters for some transports, and we want to allow those
    +  // characters in service names.
    +  this.transport_.send(this.escapeServiceName_(serviceName), payload);
    +};
    +
    +
    +/**
    + * Delivers messages to the appropriate service-handler. Named xpcDeliver to
    + * avoid name conflict with {@code deliver} function in superclass
    + * goog.messaging.AbstractChannel.
    + *
    + * @param {string} serviceName The name of the port.
    + * @param {string} payload The payload.
    + * @param {string=} opt_origin An optional origin for the message, where the
    + *     underlying transport makes that available.  If this is specified, and
    + *     the PEER_HOSTNAME parameter was provided, they must match or the message
    + *     will be rejected.
    + * @package
    + */
    +goog.net.xpc.CrossPageChannel.prototype.xpcDeliver = function(
    +    serviceName, payload, opt_origin) {
    +
    +  // This check covers the very rare (but producable) case where the inner frame
    +  // becomes ready and sends its setup message while the outer frame is
    +  // deferring its connect method waiting for the inner frame to be ready. The
    +  // resulting deferral ensures the message will not be processed until the
    +  // channel is fully configured.
    +  if (this.peerWindowDeferred_) {
    +    this.deferredDeliveries_.push(
    +        goog.bind(this.xpcDeliver, this, serviceName, payload, opt_origin));
    +    return;
    +  }
    +
    +  // Check whether the origin of the message is as expected.
    +  if (!this.isMessageOriginAcceptable_(opt_origin)) {
    +    goog.log.warning(goog.net.xpc.logger,
    +        'Message received from unapproved origin "' +
    +        opt_origin + '" - rejected.');
    +    return;
    +  }
    +
    +  // If there is another channel still open, the native transport's global
    +  // postMessage listener will still be active.  This will mean that messages
    +  // being sent to the now-closed channel will still be received and delivered,
    +  // such as transport service traffic from its previous correspondent in the
    +  // other frame.  Ensure these messages don't cause exceptions.
    +  // Example: http://b/12419303
    +  if (this.isDisposed() || this.state_ == goog.net.xpc.ChannelStates.CLOSED) {
    +    goog.log.warning(goog.net.xpc.logger,
    +        'CrossPageChannel::xpcDeliver(): Channel closed.');
    +  } else if (!serviceName ||
    +      serviceName == goog.net.xpc.TRANSPORT_SERVICE_) {
    +    this.transport_.transportServiceHandler(payload);
    +  } else {
    +    // only deliver messages if connected
    +    if (this.isConnected()) {
    +      this.deliver(this.unescapeServiceName_(serviceName), payload);
    +    } else {
    +      goog.log.info(goog.net.xpc.logger,
    +          'CrossPageChannel::xpcDeliver(): Not connected.');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Escape the user-provided service name for sending across the channel. This
    + * URL-encodes certain special characters so they don't conflict with delimiters
    + * used by some of the transports, and adds a special prefix if the name
    + * conflicts with the reserved transport service name.
    + *
    + * This is the opposite of {@link #unescapeServiceName_}.
    + *
    + * @param {string} name The name of the service to escape.
    + * @return {string} The escaped service name.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.escapeServiceName_ = function(name) {
    +  if (goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_ESCAPE_RE_.test(name)) {
    +    name = '%' + name;
    +  }
    +  return name.replace(/[%:|]/g, encodeURIComponent);
    +};
    +
    +
    +/**
    + * Unescape the escaped service name that was sent across the channel. This is
    + * the opposite of {@link #escapeServiceName_}.
    + *
    + * @param {string} name The name of the service to unescape.
    + * @return {string} The unescaped service name.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.unescapeServiceName_ = function(name) {
    +  name = name.replace(/%[0-9a-f]{2}/gi, decodeURIComponent);
    +  if (goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_UNESCAPE_RE_.test(name)) {
    +    return name.substring(1);
    +  } else {
    +    return name;
    +  }
    +};
    +
    +
    +/**
    + * Returns the role of this channel (either inner or outer).
    + * @return {number} The role of this channel.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.getRole = function() {
    +  var role = this.cfg_[goog.net.xpc.CfgFields.ROLE];
    +  if (goog.isNumber(role)) {
    +    return role;
    +  } else {
    +    return window.parent == this.peerWindowObject_ ?
    +        goog.net.xpc.CrossPageChannelRole.INNER :
    +        goog.net.xpc.CrossPageChannelRole.OUTER;
    +  }
    +};
    +
    +
    +/**
    + * Sets the channel name. Note, this doesn't establish a unique channel to
    + * communicate on.
    + * @param {string} name The new channel name.
    + */
    +goog.net.xpc.CrossPageChannel.prototype.updateChannelNameAndCatalog = function(
    +    name) {
    +  goog.log.fine(goog.net.xpc.logger, 'changing channel name to ' + name);
    +  delete goog.net.xpc.channels[this.name];
    +  this.name = name;
    +  goog.net.xpc.channels[name] = this;
    +};
    +
    +
    +/**
    + * Returns whether an incoming message with the given origin is acceptable.
    + * If an incoming request comes with a specified (non-empty) origin, and the
    + * PEER_HOSTNAME config parameter has also been provided, the two must match,
    + * or the message is unacceptable.
    + * @param {string=} opt_origin The origin associated with the incoming message.
    + * @return {boolean} Whether the message is acceptable.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.prototype.isMessageOriginAcceptable_ = function(
    +    opt_origin) {
    +  var peerHostname = this.cfg_[goog.net.xpc.CfgFields.PEER_HOSTNAME];
    +  return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(opt_origin)) ||
    +      goog.string.isEmptyOrWhitespace(goog.string.makeSafe(peerHostname)) ||
    +      opt_origin == this.cfg_[goog.net.xpc.CfgFields.PEER_HOSTNAME];
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.CrossPageChannel.prototype.disposeInternal = function() {
    +  this.close();
    +
    +  this.peerWindowObject_ = null;
    +  this.iframeElement_ = null;
    +  delete goog.net.xpc.channels[this.name];
    +  goog.dispose(this.peerLoadHandler_);
    +  delete this.peerLoadHandler_;
    +  goog.net.xpc.CrossPageChannel.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Disposes all channels.
    + * @private
    + */
    +goog.net.xpc.CrossPageChannel.disposeAll_ = function() {
    +  for (var name in goog.net.xpc.channels) {
    +    goog.dispose(goog.net.xpc.channels[name]);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.html b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.html
    new file mode 100644
    index 0000000000..4ec5dc94a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.html
    @@ -0,0 +1,36 @@
    +<!DOCTYPE html>
    +<!--
    +  Author: dbk@google.com (David Barrett-Kahn)
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   CrossPageChannel: End-to-End Test
    +  </title>
    +  <script type="text/javascript" src="../../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.net.xpc.CrossPageChannelTest');
    +  </script>
    + </head>
    + <body>
    +  <!-- Debug box. -->
    +  <div style="position:absolute; float: right; top: 10px; right: 10px; width: 500px">
    +   Debug [
    +   <a href="#" onclick="document.getElementById('debugDiv').innerHTML = '';">
    +    clear
    +   </a>
    +   ]:
    +   <br />
    +   <div id="debugDiv" style="border: 1px #000000 solid; font-size:xx-small; background: #fff;">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.js b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.js
    new file mode 100644
    index 0000000000..7d56ef853b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannel_test.js
    @@ -0,0 +1,1081 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.xpc.CrossPageChannelTest');
    +goog.setTestOnly('goog.net.xpc.CrossPageChannelTest');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Uri');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.dom');
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannel');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.object');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +// Set this to false when working on this test.  It needs to be true for
    +// automated testing, as some browsers (eg IE8) choke on the large numbers of
    +// iframes this test would otherwise leave active.
    +var CLEAN_UP_IFRAMES = true;
    +
    +var IFRAME_LOAD_WAIT_MS = 1000;
    +var stubs = new goog.testing.PropertyReplacer();
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(
    +    document.title);
    +var uniqueId = 0;
    +var driver;
    +var canAccessSameDomainIframe = true;
    +var accessCheckIframes = [];
    +
    +function setUpPage() {
    +  // This test is insanely slow on IE8 for some reason.
    +  asyncTestCase.stepTimeout = 20 * 1000;
    +
    +  // Show debug log
    +  var debugDiv = goog.dom.getElement('debugDiv');
    +  var logger = goog.log.getLogger('goog.net.xpc');
    +  logger.setLevel(goog.log.Level.ALL);
    +  goog.log.addHandler(logger, function(logRecord) {
    +    var msgElm = goog.dom.createDom('div');
    +    msgElm.innerHTML = logRecord.getMessage();
    +    goog.dom.appendChild(debugDiv, msgElm);
    +  });
    +  asyncTestCase.waitForAsync('Checking if we can access same domain iframes');
    +  checkSameDomainIframeAccess();
    +}
    +
    +
    +function setUp() {
    +  driver = new Driver();
    +}
    +
    +
    +function tearDown() {
    +  stubs.reset();
    +  driver.dispose();
    +}
    +
    +
    +function checkSameDomainIframeAccess() {
    +  accessCheckIframes.push(
    +      create1x1Iframe('nonexistant', 'testdata/i_am_non_existant.html'));
    +  window.setTimeout(function() {
    +    accessCheckIframes.push(
    +        create1x1Iframe('existant', 'testdata/access_checker.html'));
    +  }, 10);
    +}
    +
    +
    +function create1x1Iframe(iframeId, src) {
    +  var iframeAccessChecker = goog.dom.createElement('IFRAME');
    +  iframeAccessChecker.id = iframeAccessChecker.name = iframeId;
    +  iframeAccessChecker.style.width = iframeAccessChecker.style.height = '1px';
    +  iframeAccessChecker.src = src;
    +  document.body.insertBefore(iframeAccessChecker, document.body.firstChild);
    +  return iframeAccessChecker;
    +}
    +
    +
    +function sameDomainIframeAccessComplete(canAccess) {
    +  canAccessSameDomainIframe = canAccess;
    +  for (var i = 0; i < accessCheckIframes.length; i++) {
    +    document.body.removeChild(accessCheckIframes[i]);
    +  }
    +  asyncTestCase.continueTesting();
    +}
    +
    +
    +function testCreateIframeSpecifyId() {
    +  driver.createPeerIframe('new_iframe');
    +
    +  asyncTestCase.waitForAsync('iframe load');
    +  window.setTimeout(function() {
    +    driver.checkPeerIframe();
    +    asyncTestCase.continueTesting();
    +  }, IFRAME_LOAD_WAIT_MS);
    +}
    +
    +
    +function testCreateIframeRandomId() {
    +  driver.createPeerIframe();
    +
    +  asyncTestCase.waitForAsync('iframe load');
    +  window.setTimeout(function() {
    +    driver.checkPeerIframe();
    +    asyncTestCase.continueTesting();
    +  }, IFRAME_LOAD_WAIT_MS);
    +}
    +
    +
    +function testGetRole() {
    +  var cfg = {};
    +  cfg[goog.net.xpc.CfgFields.ROLE] = goog.net.xpc.CrossPageChannelRole.OUTER;
    +  var channel = new goog.net.xpc.CrossPageChannel(cfg);
    +  // If the configured role is ignored, this will cause the dynamicly
    +  // determined role to become INNER.
    +  channel.peerWindowObject_ = window.parent;
    +  assertEquals('Channel should use role from the config.',
    +      goog.net.xpc.CrossPageChannelRole.OUTER, channel.getRole());
    +  channel.dispose();
    +}
    +
    +
    +// The following batch of tests:
    +// * Establishes a peer iframe
    +// * Connects an XPC channel between the frames
    +// * From the connection callback in each frame, sends an 'echo' request, and
    +//   expects a 'response' response.
    +// * Reconnects the inner frame, sends an 'echo', expects a 'response'.
    +// * Optionally, reconnects the outer frame, sends an 'echo', expects a
    +//   'response'.
    +// * Optionally, reconnects the inner frame, but first reconfigures it to the
    +//   alternate protocol version, simulating an inner frame navigation that
    +//   picks up a new/old version.
    +//
    +// Every valid combination of protocol versions is tested, with both single and
    +// double ended handshakes.  Two timing scenarios are tested per combination,
    +// which is what the 'reverse' parameter distinguishes.
    +//
    +// Where single sided handshake is in use, reconnection by the outer frame is
    +// not supported, and therefore is not tested.
    +//
    +// The only known issue migrating to V2 is that once two V2 peers have
    +// connected, replacing either peer with a V1 peer will not work.  Upgrading V1
    +// peers to v2 is supported, as is replacing the only v2 peer in a connection
    +// with a v1.
    +
    +
    +function testLifeCycle_v1_v1() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v1_rev() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v1_onesided() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v1_onesided_rev() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v2() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v2_rev() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v2_onesided() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v1_v2_onesided_rev() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v1() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v1_rev() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v1_onesided() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v1_onesided_rev() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      true /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v2() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      false /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v2_rev() {
    +  checkLifeCycle(
    +      false /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* outerFrameReconnectSupported */,
    +      false /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v2_onesided() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      false /* innerFrameMigrationSupported */,
    +      false /* reverse */);
    +}
    +
    +
    +function testLifeCycle_v2_v2_onesided_rev() {
    +  checkLifeCycle(
    +      true /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* outerFrameReconnectSupported */,
    +      false /* innerFrameMigrationSupported */,
    +      true /* reverse */);
    +}
    +
    +
    +function checkLifeCycle(oneSidedHandshake, innerProtocolVersion,
    +    outerProtocolVersion, outerFrameReconnectSupported,
    +    innerFrameMigrationSupported, reverse) {
    +  driver.createPeerIframe('new_iframe', oneSidedHandshake,
    +      innerProtocolVersion, outerProtocolVersion);
    +  driver.connect(true /* fullLifeCycleTest */, outerFrameReconnectSupported,
    +      innerFrameMigrationSupported, reverse);
    +}
    +
    +// testConnectMismatchedNames have been flaky on IEs.
    +// Flakiness is tracked in http://b/18595666
    +// For now, not running these tests on IE.
    +
    +function testConnectMismatchedNames_v1_v1() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v1_v1_rev() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      1 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v1_v2() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v1_v2_rev() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      1 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v2_v1() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      false /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v2_v1_rev() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      2 /* innerProtocolVersion */,
    +      1 /* outerProtocolVersion */,
    +      true /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v2_v2() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      false /* reverse */);
    +}
    +
    +
    +function testConnectMismatchedNames_v2_v2_rev() {
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  checkConnectMismatchedNames(
    +      2 /* innerProtocolVersion */,
    +      2 /* outerProtocolVersion */,
    +      true /* reverse */);
    +}
    +
    +
    +function checkConnectMismatchedNames(innerProtocolVersion,
    +    outerProtocolVersion, reverse) {
    +  driver.createPeerIframe('new_iframe', false /* oneSidedHandshake */,
    +      innerProtocolVersion,
    +      outerProtocolVersion, true /* opt_randomChannelNames */);
    +  driver.connect(false /* fullLifeCycleTest */,
    +      false /* outerFrameReconnectSupported */,
    +      false /* innerFrameMigrationSupported */,
    +      reverse /* reverse */);
    +}
    +
    +
    +function testEscapeServiceName() {
    +  var escape = goog.net.xpc.CrossPageChannel.prototype.escapeServiceName_;
    +  assertEquals('Shouldn\'t escape alphanumeric name',
    +               'fooBar123', escape('fooBar123'));
    +  assertEquals('Shouldn\'t escape most non-alphanumeric characters',
    +               '`~!@#$^&*()_-=+ []{}\'";,<.>/?\\',
    +               escape('`~!@#$^&*()_-=+ []{}\'";,<.>/?\\'));
    +  assertEquals('Should escape %, |, and :',
    +               'foo%3ABar%7C123%25', escape('foo:Bar|123%'));
    +  assertEquals('Should escape tp', '%25tp', escape('tp'));
    +  assertEquals('Should escape %tp', '%25%25tp', escape('%tp'));
    +  assertEquals('Should not escape stp', 'stp', escape('stp'));
    +  assertEquals('Should not escape s%tp', 's%25tp', escape('s%tp'));
    +}
    +
    +
    +function testSameDomainCheck_noMessageOrigin() {
    +  var channel = new goog.net.xpc.CrossPageChannel(goog.object.create(
    +      goog.net.xpc.CfgFields.PEER_HOSTNAME, 'http://foo.com'));
    +  assertTrue(channel.isMessageOriginAcceptable_(undefined));
    +}
    +
    +
    +function testSameDomainCheck_noPeerHostname() {
    +  var channel = new goog.net.xpc.CrossPageChannel({});
    +  assertTrue(channel.isMessageOriginAcceptable_('http://foo.com'));
    +}
    +
    +
    +function testSameDomainCheck_unconfigured() {
    +  var channel = new goog.net.xpc.CrossPageChannel({});
    +  assertTrue(channel.isMessageOriginAcceptable_(undefined));
    +}
    +
    +
    +function testSameDomainCheck_originsMatch() {
    +  var channel = new goog.net.xpc.CrossPageChannel(goog.object.create(
    +      goog.net.xpc.CfgFields.PEER_HOSTNAME, 'http://foo.com'));
    +  assertTrue(channel.isMessageOriginAcceptable_('http://foo.com'));
    +}
    +
    +
    +function testSameDomainCheck_originsMismatch() {
    +  var channel = new goog.net.xpc.CrossPageChannel(goog.object.create(
    +      goog.net.xpc.CfgFields.PEER_HOSTNAME, 'http://foo.com'));
    +  assertFalse(channel.isMessageOriginAcceptable_('http://nasty.com'));
    +}
    +
    +
    +function testUnescapeServiceName() {
    +  var unescape = goog.net.xpc.CrossPageChannel.prototype.unescapeServiceName_;
    +  assertEquals('Shouldn\'t modify alphanumeric name',
    +               'fooBar123', unescape('fooBar123'));
    +  assertEquals('Shouldn\'t modify most non-alphanumeric characters',
    +               '`~!@#$^&*()_-=+ []{}\'";,<.>/?\\',
    +               unescape('`~!@#$^&*()_-=+ []{}\'";,<.>/?\\'));
    +  assertEquals('Should unescape URL-escapes',
    +               'foo:Bar|123%', unescape('foo%3ABar%7C123%25'));
    +  assertEquals('Should unescape tp', 'tp', unescape('%25tp'));
    +  assertEquals('Should unescape %tp', '%tp', unescape('%25%25tp'));
    +  assertEquals('Should not escape stp', 'stp', unescape('stp'));
    +  assertEquals('Should not escape s%tp', 's%tp', unescape('s%25tp'));
    +}
    +
    +
    +/**
    + * Tests the case where the channel is disposed before it is fully connected.
    + */
    +function testDisposeBeforeConnect() {
    +  asyncTestCase.waitForAsync('Checking disposal before connection.');
    +  driver.createPeerIframe('new_iframe', false /* oneSidedHandshake */,
    +      2 /* innerProtocolVersion */, 2 /* outerProtocolVersion */,
    +      true /* opt_randomChannelNames */);
    +  driver.connectOuterAndDispose();
    +}
    +
    +
    +
    +/**
    + * Driver for the tests for CrossPageChannel.
    + *
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +Driver = function() {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The peer iframe.
    +   * @type {!Element}
    +   * @private
    +   */
    +  this.iframe_ = null;
    +
    +  /**
    +   * The channel to use.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = null;
    +
    +  /**
    +   * Outer frame configuration object.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.outerFrameCfg_ = null;
    +
    +  /**
    +   * The initial name of the outer channel.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.initialOuterChannelName_ = null;
    +
    +  /**
    +   * Inner frame configuration object.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.innerFrameCfg_ = null;
    +
    +  /**
    +   * The contents of the payload of the 'echo' request sent by the inner frame.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.innerFrameEchoPayload_ = null;
    +
    +  /**
    +   * The contents of the payload of the 'echo' request sent by the outer frame.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.outerFrameEchoPayload_ = null;
    +
    +  /**
    +   * A deferred which fires when the inner frame receives its echo response.
    +   * @type {goog.async.Deferred}
    +   * @private
    +   */
    +  this.innerFrameResponseReceived_ = new goog.async.Deferred();
    +
    +  /**
    +   * A deferred which fires when the outer frame receives its echo response.
    +   * @type {goog.async.Deferred}
    +   * @private
    +   */
    +  this.outerFrameResponseReceived_ = new goog.async.Deferred();
    +
    +};
    +goog.inherits(Driver, goog.Disposable);
    +
    +
    +/** @override */
    +Driver.prototype.disposeInternal = function() {
    +  // Required to make this test perform acceptably (and pass) on slow browsers,
    +  // esp IE8.
    +  if (CLEAN_UP_IFRAMES) {
    +    goog.dom.removeNode(this.iframe_);
    +    delete this.iframe_;
    +  }
    +  goog.dispose(this.channel_);
    +  this.innerFrameResponseReceived_.cancel();
    +  this.innerFrameResponseReceived_ = null;
    +  this.outerFrameResponseReceived_.cancel();
    +  this.outerFrameResponseReceived_ = null;
    +  Driver.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Returns the child peer's window object.
    + * @return {Window} Child peer's window.
    + * @private
    + */
    +Driver.prototype.getInnerPeer_ = function() {
    +  return this.iframe_.contentWindow;
    +};
    +
    +
    +/**
    + * Sets up the configuration objects for the inner and outer frames.
    + * @param {string=} opt_iframeId If present, the ID of the iframe to use,
    + *     otherwise, tells the channel to generate an iframe ID.
    + * @param {boolean=} opt_oneSidedHandshake Whether the one sided handshake
    + *     config option should be set.
    + * @param {string=} opt_channelName The name of the channel to use, or null
    + *     to generate one.
    + * @param {number=} opt_innerProtocolVersion The native transport protocol
    + *     version used in the inner iframe.
    + * @param {number=} opt_outerProtocolVersion The native transport protocol
    + *     version used in the outer iframe.
    + * @param {boolean=} opt_randomChannelNames Whether the different ends of the
    + *     channel should be allowed to pick differing, random names.
    + * @return {string} The name of the created channel.
    + * @private
    + */
    +Driver.prototype.setConfiguration_ = function(opt_iframeId,
    +    opt_oneSidedHandshake, opt_channelName, opt_innerProtocolVersion,
    +    opt_outerProtocolVersion, opt_randomChannelNames) {
    +  var cfg = {};
    +  if (opt_iframeId) {
    +    cfg[goog.net.xpc.CfgFields.IFRAME_ID] = opt_iframeId;
    +  }
    +  cfg[goog.net.xpc.CfgFields.PEER_URI] = 'testdata/inner_peer.html';
    +  if (!opt_randomChannelNames) {
    +    var channelName = opt_channelName || 'test_channel' + uniqueId++;
    +    cfg[goog.net.xpc.CfgFields.CHANNEL_NAME] = channelName;
    +  }
    +  cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] = 'does-not-exist.html';
    +  cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] = 'does-not-exist.html';
    +  cfg[goog.net.xpc.CfgFields.ONE_SIDED_HANDSHAKE] = !!opt_oneSidedHandshake;
    +  cfg[goog.net.xpc.CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION] =
    +      opt_outerProtocolVersion;
    +  function resolveUri(fieldName) {
    +    cfg[fieldName] =
    +        goog.Uri.resolve(window.location.href, cfg[fieldName]).toString();
    +  }
    +  resolveUri(goog.net.xpc.CfgFields.PEER_URI);
    +  resolveUri(goog.net.xpc.CfgFields.LOCAL_POLL_URI);
    +  resolveUri(goog.net.xpc.CfgFields.PEER_POLL_URI);
    +  this.outerFrameCfg_ = cfg;
    +  this.innerFrameCfg_ = goog.object.clone(cfg);
    +  this.innerFrameCfg_[
    +      goog.net.xpc.CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION] =
    +      opt_innerProtocolVersion;
    +};
    +
    +
    +/**
    + * Creates an outer frame channel object.
    + * @private
    + */
    +Driver.prototype.createChannel_ = function() {
    +  if (this.channel_) {
    +    this.channel_.dispose();
    +  }
    +  this.channel_ = new goog.net.xpc.CrossPageChannel(this.outerFrameCfg_);
    +  this.channel_.registerService('echo',
    +      goog.bind(this.echoHandler_, this));
    +  this.channel_.registerService('response',
    +      goog.bind(this.responseHandler_, this));
    +
    +  return this.channel_.name;
    +};
    +
    +
    +/**
    + * Checks the names of the inner and outer frames meet expectations.
    + * @private
    + */
    +Driver.prototype.checkChannelNames_ = function() {
    +  var outerName = this.channel_.name;
    +  var innerName = this.getInnerPeer_().channel.name;
    +  var configName = this.innerFrameCfg_[goog.net.xpc.CfgFields.CHANNEL_NAME] ||
    +      null;
    +
    +  // The outer channel never changes its name.
    +  assertEquals(this.initialOuterChannelName_, outerName);
    +  // The name should be as configured, if it was configured.
    +  if (configName) {
    +    assertEquals(configName, innerName);
    +  }
    +  // The names of both ends of the channel should match.
    +  assertEquals(innerName, outerName);
    +  G_testRunner.log('Channel name: ' + innerName);
    +};
    +
    +
    +/**
    + * Returns the configuration of the xpc.
    + * @return {?Object} The configuration of the xpc.
    + */
    +Driver.prototype.getInnerFrameConfiguration = function() {
    +  return this.innerFrameCfg_;
    +};
    +
    +
    +/**
    + * Creates the peer iframe.
    + * @param {string=} opt_iframeId If present, the ID of the iframe to create,
    + *     otherwise, generates an iframe ID.
    + * @param {boolean=} opt_oneSidedHandshake Whether a one sided handshake is
    + *     specified.
    + * @param {number=} opt_innerProtocolVersion The native transport protocol
    + *     version used in the inner iframe.
    + * @param {number=} opt_outerProtocolVersion The native transport protocol
    + *     version used in the outer iframe.
    + * @param {boolean=} opt_randomChannelNames Whether the ends of the channel
    + *     should be allowed to pick differing, random names.
    + * @return {!Array<string>} The id of the created iframe and the name of the
    + *     created channel.
    + */
    +Driver.prototype.createPeerIframe = function(opt_iframeId,
    +    opt_oneSidedHandshake, opt_innerProtocolVersion, opt_outerProtocolVersion,
    +    opt_randomChannelNames) {
    +  var expectedIframeId;
    +
    +  if (opt_iframeId) {
    +    expectedIframeId = opt_iframeId = opt_iframeId + uniqueId++;
    +  } else {
    +    // Have createPeerIframe() generate an ID
    +    stubs.set(goog.net.xpc, 'getRandomString', function(length) {
    +      return '' + length;
    +    });
    +    expectedIframeId = 'xpcpeer4';
    +  }
    +  assertNull('element[id=' + expectedIframeId + '] exists',
    +      goog.dom.getElement(expectedIframeId));
    +
    +  this.setConfiguration_(opt_iframeId, opt_oneSidedHandshake,
    +      undefined /* opt_channelName */, opt_innerProtocolVersion,
    +      opt_outerProtocolVersion, opt_randomChannelNames);
    +  var channelName = this.createChannel_();
    +  this.initialOuterChannelName_ = channelName;
    +  this.iframe_ = this.channel_.createPeerIframe(document.body);
    +
    +  assertEquals(expectedIframeId, this.iframe_.id);
    +};
    +
    +
    +/**
    + * Checks if the peer iframe has been created.
    + */
    +Driver.prototype.checkPeerIframe = function() {
    +  assertNotNull(this.iframe_);
    +  var peer = this.getInnerPeer_();
    +  assertNotNull(peer);
    +  assertNotNull(peer.document);
    +};
    +
    +
    +/**
    + * Starts the connection. The connection happens asynchronously.
    + */
    +Driver.prototype.connect = function(fullLifeCycleTest,
    +    outerFrameReconnectSupported, innerFrameMigrationSupported, reverse) {
    +  if (!this.isTransportTestable_()) {
    +    asyncTestCase.continueTesting();
    +    return;
    +  }
    +
    +  asyncTestCase.waitForAsync('parent and child connect');
    +
    +  // Set the criteria for the initial handshake portion of the test.
    +  this.reinitializeDeferreds_();
    +  this.innerFrameResponseReceived_.awaitDeferred(
    +      this.outerFrameResponseReceived_);
    +  this.innerFrameResponseReceived_.addCallback(
    +      goog.bind(this.checkChannelNames_, this));
    +
    +  if (fullLifeCycleTest) {
    +    this.innerFrameResponseReceived_.addCallback(
    +        goog.bind(this.testReconnects_, this,
    +            outerFrameReconnectSupported, innerFrameMigrationSupported));
    +  } else {
    +    this.innerFrameResponseReceived_.addCallback(
    +        goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +  }
    +
    +  this.continueConnect_(reverse);
    +};
    +
    +
    +Driver.prototype.continueConnect_ = function(reverse) {
    +  // Wait until the peer is fully established.  Establishment is sometimes very
    +  // slow indeed, especially on virtual machines, so a fixed timeout is not
    +  // suitable.  This wait is required because we want to take precise control
    +  // of the channel startup timing, and shouldn't be needed in production use,
    +  // where the inner frame's channel is typically not started by a DOM call as
    +  // it is here.
    +  if (!this.getInnerPeer_() || !this.getInnerPeer_().instantiateChannel) {
    +    window.setTimeout(goog.bind(this.continueConnect_, this, reverse), 100);
    +    return;
    +  }
    +
    +  var connectFromOuterFrame = goog.bind(this.channel_.connect, this.channel_,
    +      goog.bind(this.outerFrameConnected_, this));
    +  var innerConfig = this.innerFrameCfg_;
    +  var connectFromInnerFrame = goog.bind(this.getInnerPeer_().instantiateChannel,
    +      this.getInnerPeer_(), innerConfig);
    +
    +  // Take control of the timing and reverse of each frame's first SETUP call. If
    +  // these happen to fire right on top of each other, that tends to mask
    +  // problems that reliably occur when there is a short delay.
    +  window.setTimeout(connectFromOuterFrame, reverse ? 1 : 10);
    +  window.setTimeout(connectFromInnerFrame, reverse ? 10 : 1);
    +};
    +
    +
    +/**
    + * Called by the outer frame connection callback.
    + * @private
    + */
    +Driver.prototype.outerFrameConnected_ = function() {
    +  var payload = this.outerFrameEchoPayload_ =
    +      goog.net.xpc.getRandomString(10);
    +  this.channel_.send('echo', payload);
    +};
    +
    +
    +/**
    + * Called by the inner frame connection callback.
    + */
    +Driver.prototype.innerFrameConnected = function() {
    +  var payload = this.innerFrameEchoPayload_ =
    +      goog.net.xpc.getRandomString(10);
    +  this.getInnerPeer_().sendEcho(payload);
    +};
    +
    +
    +/**
    + * The handler function for incoming echo requests.
    + * @param {string} payload The message payload.
    + * @private
    + */
    +Driver.prototype.echoHandler_ = function(payload) {
    +  assertTrue('outer frame should be connected', this.channel_.isConnected());
    +  var peer = this.getInnerPeer_();
    +  assertTrue('child should be connected', peer.isConnected());
    +  this.channel_.send('response', payload);
    +};
    +
    +
    +/**
    + * The handler function for incoming echo responses.
    + * @param {string} payload The message payload.
    + * @private
    + */
    +Driver.prototype.responseHandler_ = function(payload) {
    +  assertTrue('outer frame should be connected', this.channel_.isConnected());
    +  var peer = this.getInnerPeer_();
    +  assertTrue('child should be connected', peer.isConnected());
    +  assertEquals(this.outerFrameEchoPayload_, payload);
    +  this.outerFrameResponseReceived_.callback(true);
    +};
    +
    +
    +/**
    + * The handler function for incoming echo replies.
    + * @param {string} payload The message payload.
    + */
    +Driver.prototype.innerFrameGotResponse = function(payload) {
    +  assertTrue('outer frame should be connected', this.channel_.isConnected());
    +  var peer = this.getInnerPeer_();
    +  assertTrue('child should be connected', peer.isConnected());
    +  assertEquals(this.innerFrameEchoPayload_, payload);
    +  this.innerFrameResponseReceived_.callback(true);
    +};
    +
    +
    +/**
    + * The second phase of the standard test, where reconnections of both the inner
    + * and outer frames are performed.
    + * @param {boolean} outerFrameReconnectSupported Whether outer frame reconnects
    + *     are supported, and should be tested.
    + * @private
    + */
    +Driver.prototype.testReconnects_ = function(outerFrameReconnectSupported,
    +    innerFrameMigrationSupported) {
    +  G_testRunner.log('Performing inner frame reconnect');
    +  this.reinitializeDeferreds_();
    +  this.innerFrameResponseReceived_.addCallback(
    +      goog.bind(this.checkChannelNames_, this));
    +
    +  if (outerFrameReconnectSupported) {
    +    this.innerFrameResponseReceived_.addCallback(
    +        goog.bind(this.performOuterFrameReconnect_, this,
    +            innerFrameMigrationSupported));
    +  } else if (innerFrameMigrationSupported) {
    +    this.innerFrameResponseReceived_.addCallback(
    +        goog.bind(this.migrateInnerFrame_, this));
    +  } else {
    +    this.innerFrameResponseReceived_.addCallback(
    +        goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +  }
    +
    +  this.performInnerFrameReconnect_();
    +};
    +
    +
    +/**
    + * Initializes the deferreds and clears the echo payloads, ready for another
    + * sub-test.
    + * @private
    + */
    +Driver.prototype.reinitializeDeferreds_ = function() {
    +  this.innerFrameEchoPayload_ = null;
    +  this.outerFrameEchoPayload_ = null;
    +  this.innerFrameResponseReceived_.cancel();
    +  this.innerFrameResponseReceived_ = new goog.async.Deferred();
    +  this.outerFrameResponseReceived_.cancel();
    +  this.outerFrameResponseReceived_ = new goog.async.Deferred();
    +};
    +
    +
    +/**
    + * Get the inner frame to reconnect, and repeat the echo test.
    + * @private
    + */
    +Driver.prototype.performInnerFrameReconnect_ = function() {
    +  var peer = this.getInnerPeer_();
    +  peer.instantiateChannel(this.innerFrameCfg_);
    +};
    +
    +
    +/**
    + * Get the outer frame to reconnect, and repeat the echo test.
    + * @private
    + */
    +Driver.prototype.performOuterFrameReconnect_ = function(
    +    innerFrameMigrationSupported) {
    +  G_testRunner.log('Closing channel');
    +  this.channel_.close();
    +
    +  // If there is another channel still open, the native transport's global
    +  // postMessage listener will still be active.  This will mean that messages
    +  // being sent to the now-closed channel will still be received and delivered,
    +  // such as transport service traffic from its previous correspondent in the
    +  // other frame.  Ensure these messages don't cause exceptions.
    +  try {
    +    this.channel_.xpcDeliver(goog.net.xpc.TRANSPORT_SERVICE_, 'payload');
    +  } catch (e) {
    +    fail('Should not throw exception');
    +  }
    +
    +  G_testRunner.log('Reconnecting outer frame');
    +  this.reinitializeDeferreds_();
    +  this.innerFrameResponseReceived_.addCallback(
    +      goog.bind(this.checkChannelNames_, this));
    +  if (innerFrameMigrationSupported) {
    +    this.outerFrameResponseReceived_.addCallback(
    +        goog.bind(this.migrateInnerFrame_, this));
    +  } else {
    +    this.outerFrameResponseReceived_.addCallback(
    +        goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +  }
    +  this.channel_.connect(goog.bind(this.outerFrameConnected_, this));
    +};
    +
    +
    +/**
    + * Migrate the inner frame to the alternate protocol version and reconnect it.
    + * @private
    + */
    +Driver.prototype.migrateInnerFrame_ = function() {
    +  G_testRunner.log('Migrating inner frame');
    +  this.reinitializeDeferreds_();
    +  var innerFrameProtoVersion = this.innerFrameCfg_[
    +      goog.net.xpc.CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION];
    +  this.innerFrameResponseReceived_.addCallback(
    +      goog.bind(this.checkChannelNames_, this));
    +  this.innerFrameResponseReceived_.addCallback(
    +      goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +  this.innerFrameCfg_[
    +      goog.net.xpc.CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION] =
    +      innerFrameProtoVersion == 1 ? 2 : 1;
    +  this.performInnerFrameReconnect_();
    +};
    +
    +
    +/**
    + * Determines if the transport type for the channel is testable.
    + * Some transports are misusing global state or making other
    + * assumptions that cause connections to fail.
    + * @return {boolean} Whether the transport is testable.
    + * @private
    + */
    +Driver.prototype.isTransportTestable_ = function() {
    +  var testable = false;
    +
    +  var transportType = this.channel_.determineTransportType_();
    +  switch (transportType) {
    +    case goog.net.xpc.TransportTypes.IFRAME_RELAY:
    +    case goog.net.xpc.TransportTypes.IFRAME_POLLING:
    +      testable = canAccessSameDomainIframe;
    +      break;
    +    case goog.net.xpc.TransportTypes.NATIVE_MESSAGING:
    +    case goog.net.xpc.TransportTypes.FLASH:
    +    case goog.net.xpc.TransportTypes.DIRECT:
    +    case goog.net.xpc.TransportTypes.NIX:
    +      testable = true;
    +      break;
    +  }
    +
    +  return testable;
    +};
    +
    +
    +/**
    + * Connect the outer channel but not the inner one.  Wait a short time, then
    + * dispose the outer channel and make sure it was torn down properly.
    + */
    +Driver.prototype.connectOuterAndDispose = function() {
    +  this.channel_.connect();
    +  window.setTimeout(goog.bind(this.disposeAndCheck_, this), 2000);
    +};
    +
    +
    +/**
    + * Dispose the cross-page channel. Check that the transport was also
    + * disposed, and allow to run briefly to make sure no timers which will cause
    + * failures are still running.
    + * @private
    + */
    +Driver.prototype.disposeAndCheck_ = function() {
    +  assertFalse(this.channel_.isConnected());
    +  var transport = this.channel_.transport_;
    +  this.channel_.dispose();
    +  assertNull(this.channel_.transport_);
    +  assertTrue(this.channel_.isDisposed());
    +  assertTrue(transport.isDisposed());
    +
    +  // Let any errors caused by erroneous retries happen.
    +  window.setTimeout(goog.bind(asyncTestCase.continueTesting, asyncTestCase),
    +      2000);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannelrole.js b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannelrole.js
    new file mode 100644
    index 0000000000..0d31fe9405
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/crosspagechannelrole.js
    @@ -0,0 +1,30 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the enum for the role of the CrossPageChannel.
    + *
    + */
    +
    +goog.provide('goog.net.xpc.CrossPageChannelRole');
    +
    +
    +/**
    + * The role of the peer.
    + * @enum {number}
    + */
    +goog.net.xpc.CrossPageChannelRole = {
    +  OUTER: 0,
    +  INNER: 1
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport.js
    new file mode 100644
    index 0000000000..cd504d0b11
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport.js
    @@ -0,0 +1,635 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides an implementation of a transport that can call methods
    + * directly on a frame. Useful if you want to use XPC for crossdomain messaging
    + * (using another transport), or same domain messaging (using this transport).
    + */
    +
    +
    +goog.provide('goog.net.xpc.DirectTransport');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.log');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.object');
    +
    +
    +goog.scope(function() {
    +var CfgFields = goog.net.xpc.CfgFields;
    +var CrossPageChannelRole = goog.net.xpc.CrossPageChannelRole;
    +var Deferred = goog.async.Deferred;
    +var EventHandler = goog.events.EventHandler;
    +var Timer = goog.Timer;
    +var Transport = goog.net.xpc.Transport;
    +
    +
    +
    +/**
    + * A direct window to window method transport.
    + *
    + * If the windows are in the same security context, this transport calls
    + * directly into the other window without using any additional mechanism. This
    + * is mainly used in scenarios where you want to optionally use a cross domain
    + * transport in cross security context situations, or optionally use a direct
    + * transport in same security context situations.
    + *
    + * Note: Global properties are exported by using this transport. One to
    + * communicate with the other window by, currently crosswindowmessaging.channel,
    + * and by using goog.getUid on window, currently closure_uid_[0-9]+.
    + *
    + * @param {!goog.net.xpc.CrossPageChannel} channel The channel this
    + *     transport belongs to.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for
    + *     finding the correct window/document. If omitted, uses the current
    + *     document.
    + * @constructor
    + * @extends {Transport}
    + */
    +goog.net.xpc.DirectTransport = function(channel, opt_domHelper) {
    +  goog.net.xpc.DirectTransport.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @private {!goog.net.xpc.CrossPageChannel}
    +   */
    +  this.channel_ = channel;
    +
    +  /** @private {!EventHandler<!goog.net.xpc.DirectTransport>} */
    +  this.eventHandler_ = new EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  /**
    +   * Timer for connection reattempts.
    +   * @private {!Timer}
    +   */
    +  this.maybeAttemptToConnectTimer_ = new Timer(
    +      DirectTransport.CONNECTION_ATTEMPT_INTERVAL_MS_,
    +      this.getWindow());
    +  this.registerDisposable(this.maybeAttemptToConnectTimer_);
    +
    +  /**
    +   * Fires once we've received our SETUP_ACK message.
    +   * @private {!Deferred}
    +   */
    +  this.setupAckReceived_ = new Deferred();
    +
    +  /**
    +   * Fires once we've sent our SETUP_ACK message.
    +   * @private {!Deferred}
    +   */
    +  this.setupAckSent_ = new Deferred();
    +
    +  /**
    +   * Fires once we're marked connected.
    +   * @private {!Deferred}
    +   */
    +  this.connected_ = new Deferred();
    +
    +  /**
    +   * The unique ID of this side of the connection. Used to determine when a peer
    +   * is reloaded.
    +   * @private {string}
    +   */
    +  this.endpointId_ = goog.net.xpc.getRandomString(10);
    +
    +  /**
    +   * The unique ID of the peer. If we get a message from a peer with an ID we
    +   * don't expect, we reset the connection.
    +   * @private {?string}
    +   */
    +  this.peerEndpointId_ = null;
    +
    +  /**
    +   * The map of sending messages.
    +   * @private {Object}
    +   */
    +  this.asyncSendsMap_ = {};
    +
    +  /**
    +   * The original channel name.
    +   * @private {string}
    +   */
    +  this.originalChannelName_ = this.channel_.name;
    +
    +  // We reconfigure the channel name to include the role so that we can
    +  // communicate in the same window between the different roles on the
    +  // same channel.
    +  this.channel_.updateChannelNameAndCatalog(
    +      DirectTransport.getRoledChannelName_(this.channel_.name,
    +                                           this.channel_.getRole()));
    +
    +  /**
    +   * Flag indicating if this instance of the transport has been initialized.
    +   * @private {boolean}
    +   */
    +  this.initialized_ = false;
    +
    +  // We don't want to mark ourselves connected until we have sent whatever
    +  // message will cause our counterpart in the other frame to also declare
    +  // itself connected, if there is such a message.  Otherwise we risk a user
    +  // message being sent in advance of that message, and it being discarded.
    +
    +  // Two sided handshake:
    +  // SETUP_ACK has to have been received, and sent.
    +  this.connected_.awaitDeferred(this.setupAckReceived_);
    +  this.connected_.awaitDeferred(this.setupAckSent_);
    +
    +  this.connected_.addCallback(this.notifyConnected_, this);
    +  this.connected_.callback(true);
    +
    +  this.eventHandler_.
    +      listen(this.maybeAttemptToConnectTimer_, Timer.TICK,
    +          this.maybeAttemptToConnect_);
    +
    +  goog.log.info(
    +      goog.net.xpc.logger,
    +      'DirectTransport created. role=' + this.channel_.getRole());
    +};
    +goog.inherits(goog.net.xpc.DirectTransport, Transport);
    +var DirectTransport = goog.net.xpc.DirectTransport;
    +
    +
    +/**
    + * @private {number}
    + * @const
    + */
    +DirectTransport.CONNECTION_ATTEMPT_INTERVAL_MS_ = 100;
    +
    +
    +/**
    + * The delay to notify the xpc of a successful connection. This is used
    + * to allow both parties to be connected if one party's connection callback
    + * invokes an immediate send.
    + * @private {number}
    + * @const
    + */
    +DirectTransport.CONNECTION_DELAY_INTERVAL_MS_ = 0;
    +
    +
    +/**
    + * @param {!Window} peerWindow The peer window to check if DirectTranport is
    + *     supported on.
    + * @return {boolean} Whether this transport is supported.
    + */
    +DirectTransport.isSupported = function(peerWindow) {
    +  /** @preserveTry */
    +  try {
    +    return window.document.domain == peerWindow.document.domain;
    +  } catch (e) {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Tracks the number of DirectTransport channels that have been
    + * initialized but not disposed yet in a map keyed by the UID of the window
    + * object.  This allows for multiple windows to be initiallized and listening
    + * for messages.
    + * @private {!Object<number>}
    + */
    +DirectTransport.activeCount_ = {};
    +
    +
    +/**
    + * Path of global message proxy.
    + * @private {string}
    + * @const
    + */
    +// TODO(user): Make this configurable using the CfgFields.
    +DirectTransport.GLOBAL_TRANPORT_PATH_ = 'crosswindowmessaging.channel';
    +
    +
    +/**
    + * The delimiter used for transport service messages.
    + * @private {string}
    + * @const
    + */
    +DirectTransport.MESSAGE_DELIMITER_ = ',';
    +
    +
    +/**
    + * Initializes this transport. Registers a method for 'message'-events in the
    + * global scope.
    + * @param {!Window} listenWindow The window to listen to events on.
    + * @private
    + */
    +DirectTransport.initialize_ = function(listenWindow) {
    +  var uid = goog.getUid(listenWindow);
    +  var value = DirectTransport.activeCount_[uid] || 0;
    +  if (value == 0) {
    +    // Set up a handler on the window to proxy messages to class.
    +    var globalProxy = goog.getObjectByName(
    +        DirectTransport.GLOBAL_TRANPORT_PATH_,
    +        listenWindow);
    +    if (globalProxy == null) {
    +      goog.exportSymbol(
    +          DirectTransport.GLOBAL_TRANPORT_PATH_,
    +          DirectTransport.messageReceivedHandler_,
    +          listenWindow);
    +    }
    +  }
    +  DirectTransport.activeCount_[uid]++;
    +};
    +
    +
    +/**
    + * @param {string} channelName The channel name.
    + * @param {string|number} role The role.
    + * @return {string} The formatted channel name including role.
    + * @private
    + */
    +DirectTransport.getRoledChannelName_ = function(channelName, role) {
    +  return channelName + '_' + role;
    +};
    +
    +
    +/**
    + * @param {!Object} literal The literal unrenamed message.
    + * @return {boolean} Whether the message was successfully delivered to a
    + *     channel.
    + * @private
    + */
    +DirectTransport.messageReceivedHandler_ = function(literal) {
    +  var msg = DirectTransport.Message_.fromLiteral(literal);
    +
    +  var channelName = msg.channelName;
    +  var service = msg.service;
    +  var payload = msg.payload;
    +
    +  goog.log.fine(goog.net.xpc.logger,
    +      'messageReceived: channel=' + channelName +
    +      ', service=' + service + ', payload=' + payload);
    +
    +  // Attempt to deliver message to the channel. Keep in mind that it may not
    +  // exist for several reasons, including but not limited to:
    +  //  - a malformed message
    +  //  - the channel simply has not been created
    +  //  - channel was created in a different namespace
    +  //  - message was sent to the wrong window
    +  //  - channel has become stale (e.g. caching iframes and back clicks)
    +  var channel = goog.net.xpc.channels[channelName];
    +  if (channel) {
    +    channel.xpcDeliver(service, payload);
    +    return true;
    +  }
    +
    +  var transportMessageType = DirectTransport.parseTransportPayload_(payload)[0];
    +
    +  // Check if there are any stale channel names that can be updated.
    +  for (var staleChannelName in goog.net.xpc.channels) {
    +    var staleChannel = goog.net.xpc.channels[staleChannelName];
    +    if (staleChannel.getRole() == CrossPageChannelRole.INNER &&
    +        !staleChannel.isConnected() &&
    +        service == goog.net.xpc.TRANSPORT_SERVICE_ &&
    +        transportMessageType == goog.net.xpc.SETUP) {
    +      // Inner peer received SETUP message but channel names did not match.
    +      // Start using the channel name sent from outer peer. The channel name
    +      // of the inner peer can easily become out of date, as iframe's and their
    +      // JS state get cached in many browsers upon page reload or history
    +      // navigation (particularly Firefox 1.5+).
    +      staleChannel.updateChannelNameAndCatalog(channelName);
    +      staleChannel.xpcDeliver(service, payload);
    +      return true;
    +    }
    +  }
    +
    +  // Failed to find a channel to deliver this message to, so simply ignore it.
    +  goog.log.info(goog.net.xpc.logger, 'channel name mismatch; message ignored.');
    +  return false;
    +};
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @override
    + */
    +DirectTransport.prototype.transportType = goog.net.xpc.TransportTypes.DIRECT;
    +
    +
    +/**
    + * Handles transport service messages.
    + * @param {string} payload The message content.
    + * @override
    + */
    +DirectTransport.prototype.transportServiceHandler = function(payload) {
    +  var transportParts = DirectTransport.parseTransportPayload_(payload);
    +  var transportMessageType = transportParts[0];
    +  var peerEndpointId = transportParts[1];
    +  switch (transportMessageType) {
    +    case goog.net.xpc.SETUP_ACK_:
    +      if (!this.setupAckReceived_.hasFired()) {
    +        this.setupAckReceived_.callback(true);
    +      }
    +      break;
    +    case goog.net.xpc.SETUP:
    +      this.sendSetupAckMessage_();
    +      if ((this.peerEndpointId_ != null) &&
    +          (this.peerEndpointId_ != peerEndpointId)) {
    +        // Send a new SETUP message since the peer has been replaced.
    +        goog.log.info(goog.net.xpc.logger,
    +            'Sending SETUP and changing peer ID to: ' + peerEndpointId);
    +        this.sendSetupMessage_();
    +      }
    +      this.peerEndpointId_ = peerEndpointId;
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Sends a SETUP transport service message.
    + * @private
    + */
    +DirectTransport.prototype.sendSetupMessage_ = function() {
    +  // Although we could send real objects, since some other transports are
    +  // limited to strings we also keep this requirement.
    +  var payload = goog.net.xpc.SETUP;
    +  payload += DirectTransport.MESSAGE_DELIMITER_;
    +  payload += this.endpointId_;
    +  this.send(goog.net.xpc.TRANSPORT_SERVICE_, payload);
    +};
    +
    +
    +/**
    + * Sends a SETUP_ACK transport service message.
    + * @private
    + */
    +DirectTransport.prototype.sendSetupAckMessage_ = function() {
    +  this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
    +  if (!this.setupAckSent_.hasFired()) {
    +    this.setupAckSent_.callback(true);
    +  }
    +};
    +
    +
    +/** @override */
    +DirectTransport.prototype.connect = function() {
    +  var win = this.getWindow();
    +  if (win) {
    +    DirectTransport.initialize_(win);
    +    this.initialized_ = true;
    +    this.maybeAttemptToConnect_();
    +  } else {
    +    goog.log.fine(goog.net.xpc.logger, 'connect(): no window to initialize.');
    +  }
    +};
    +
    +
    +/**
    + * Connects to other peer. In the case of the outer peer, the setup messages are
    + * likely sent before the inner peer is ready to receive them. Therefore, this
    + * function will continue trying to send the SETUP message until the inner peer
    + * responds. In the case of the inner peer, it will occasionally have its
    + * channel name fall out of sync with the outer peer, particularly during
    + * soft-reloads and history navigations.
    + * @private
    + */
    +DirectTransport.prototype.maybeAttemptToConnect_ = function() {
    +  var outerRole = this.channel_.getRole() == CrossPageChannelRole.OUTER;
    +  if (this.channel_.isConnected()) {
    +    this.maybeAttemptToConnectTimer_.stop();
    +    return;
    +  }
    +  this.maybeAttemptToConnectTimer_.start();
    +  this.sendSetupMessage_();
    +};
    +
    +
    +/**
    + * Prepares to send a message.
    + * @param {string} service The name of the service the message is to be
    + *     delivered to.
    + * @param {string} payload The message content.
    + * @override
    + */
    +DirectTransport.prototype.send = function(service, payload) {
    +  if (!this.channel_.getPeerWindowObject()) {
    +    goog.log.fine(goog.net.xpc.logger, 'send(): window not ready');
    +    return;
    +  }
    +  var channelName = DirectTransport.getRoledChannelName_(
    +      this.originalChannelName_,
    +      this.getPeerRole_());
    +
    +  var message = new DirectTransport.Message_(
    +      channelName,
    +      service,
    +      payload);
    +
    +  if (this.channel_.getConfig()[CfgFields.DIRECT_TRANSPORT_SYNC_MODE]) {
    +    this.executeScheduledSend_(message);
    +  } else {
    +    // Note: goog.async.nextTick doesn't support cancelling or disposal so
    +    // leaving as 0ms timer, though this may have performance implications.
    +    this.asyncSendsMap_[goog.getUid(message)] =
    +        Timer.callOnce(goog.bind(this.executeScheduledSend_, this, message), 0);
    +  }
    +};
    +
    +
    +/**
    + * Sends the message.
    + * @param {!DirectTransport.Message_} message The message to send.
    + * @private
    + */
    +DirectTransport.prototype.executeScheduledSend_ = function(message) {
    +  var messageId = goog.getUid(message);
    +  if (this.asyncSendsMap_[messageId]) {
    +    delete this.asyncSendsMap_[messageId];
    +  }
    +
    +  /** @preserveTry */
    +  try {
    +    var peerProxy = goog.getObjectByName(
    +        DirectTransport.GLOBAL_TRANPORT_PATH_,
    +        this.channel_.getPeerWindowObject());
    +  } catch (error) {
    +    goog.log.warning(
    +        goog.net.xpc.logger,
    +        'Can\'t access other window, ignoring.',
    +        error);
    +    return;
    +  }
    +
    +  if (goog.isNull(peerProxy)) {
    +    goog.log.warning(
    +        goog.net.xpc.logger,
    +        'Peer window had no global function.');
    +    return;
    +  }
    +
    +  /** @preserveTry */
    +  try {
    +    peerProxy(message.toLiteral());
    +    goog.log.info(
    +        goog.net.xpc.logger,
    +        'send(): channelName=' + message.channelName +
    +        ' service=' + message.service +
    +        ' payload=' + message.payload);
    +  } catch (error) {
    +    goog.log.warning(
    +        goog.net.xpc.logger,
    +        'Error performing call, ignoring.',
    +        error);
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.net.xpc.CrossPageChannelRole} The role of peer channel (either
    + *     inner or outer).
    + * @private
    + */
    +DirectTransport.prototype.getPeerRole_ = function() {
    +  var role = this.channel_.getRole();
    +  return role == goog.net.xpc.CrossPageChannelRole.OUTER ?
    +      goog.net.xpc.CrossPageChannelRole.INNER :
    +      goog.net.xpc.CrossPageChannelRole.OUTER;
    +};
    +
    +
    +/**
    + * Notifies the channel that this transport is connected.
    + * @private
    + */
    +DirectTransport.prototype.notifyConnected_ = function() {
    +  // Add a delay as the connection callback will break if this transport is
    +  // synchronous and the callback invokes send() immediately.
    +  this.channel_.notifyConnected(
    +      this.channel_.getConfig()[CfgFields.DIRECT_TRANSPORT_SYNC_MODE] ?
    +      DirectTransport.CONNECTION_DELAY_INTERVAL_MS_ : 0);
    +};
    +
    +
    +/** @override */
    +DirectTransport.prototype.disposeInternal = function() {
    +  if (this.initialized_) {
    +    var listenWindow = this.getWindow();
    +    var uid = goog.getUid(listenWindow);
    +    var value = --DirectTransport.activeCount_[uid];
    +    if (value == 1) {
    +      goog.exportSymbol(
    +          DirectTransport.GLOBAL_TRANPORT_PATH_,
    +          null,
    +          listenWindow);
    +    }
    +  }
    +
    +  if (this.asyncSendsMap_) {
    +    goog.object.forEach(this.asyncSendsMap_, function(timerId) {
    +      Timer.clear(timerId);
    +    });
    +    this.asyncSendsMap_ = null;
    +  }
    +
    +  // Deferred's aren't disposables.
    +  if (this.setupAckReceived_) {
    +    this.setupAckReceived_.cancel();
    +    delete this.setupAckReceived_;
    +  }
    +  if (this.setupAckSent_) {
    +    this.setupAckSent_.cancel();
    +    delete this.setupAckSent_;
    +  }
    +  if (this.connected_) {
    +    this.connected_.cancel();
    +    delete this.connected_;
    +  }
    +
    +  DirectTransport.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Parses a transport service payload message.
    + * @param {string} payload The payload.
    + * @return {!Array<?string>} An array with the message type as the first member
    + *     and the endpoint id as the second, if one was sent, or null otherwise.
    + * @private
    + */
    +DirectTransport.parseTransportPayload_ = function(payload) {
    +  var transportParts = /** @type {!Array<?string>} */ (payload.split(
    +      DirectTransport.MESSAGE_DELIMITER_));
    +  transportParts[1] = transportParts[1] || null; // Usually endpointId.
    +  return transportParts;
    +};
    +
    +
    +
    +/**
    + * Message container that gets passed back and forth between windows.
    + * @param {string} channelName The channel name to tranport messages on.
    + * @param {string} service The service to send the payload to.
    + * @param {string} payload The payload to send.
    + * @constructor
    + * @struct
    + * @private
    + */
    +DirectTransport.Message_ = function(channelName, service, payload) {
    +  /**
    +   * The name of the channel.
    +   * @type {string}
    +   */
    +  this.channelName = channelName;
    +
    +  /**
    +   * The service on the channel.
    +   * @type {string}
    +   */
    +  this.service = service;
    +
    +  /**
    +   * The payload.
    +   * @type {string}
    +   */
    +  this.payload = payload;
    +};
    +
    +
    +/**
    + * Converts a message to a literal object.
    + * @return {!Object} The message as a literal object.
    + */
    +DirectTransport.Message_.prototype.toLiteral = function() {
    +  return {
    +    'channelName': this.channelName,
    +    'service': this.service,
    +    'payload': this.payload
    +  };
    +};
    +
    +
    +/**
    + * Creates a Message_ from a literal object.
    + * @param {!Object} literal The literal to convert to Message.
    + * @return {!DirectTransport.Message_} The Message.
    + */
    +DirectTransport.Message_.fromLiteral = function(literal) {
    +  return new DirectTransport.Message_(
    +      literal['channelName'],
    +      literal['service'],
    +      literal['payload']);
    +};
    +
    +});  // goog.scope
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport_test.js b/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport_test.js
    new file mode 100644
    index 0000000000..bf969e7598
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/directtransport_test.js
    @@ -0,0 +1,290 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests the direct transport.
    + */
    +
    +goog.provide('goog.net.xpc.DirectTransportTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannel');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.setTestOnly('goog.net.xpc.DirectTransportTest');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(
    +    'Direct transport tests.');
    +
    +
    +/**
    + * Echo service name.
    + * @type {string}
    + * @const
    + */
    +var ECHO_SERVICE_NAME = 'echo';
    +
    +
    +/**
    + * Response service name.
    + * @type {string}
    + * @const
    + */
    +var RESPONSE_SERVICE_NAME = 'response';
    +
    +
    +/**
    + * Test Payload.
    + * @type {string}
    + * @const
    + */
    +var MESSAGE_PAYLOAD_1 = 'This is message payload 1.';
    +
    +
    +/**
    + * The name id of the peer iframe.
    + * @type {string}
    + * @const
    + */
    +var PEER_IFRAME_ID = 'peer-iframe';
    +
    +
    +// Class aliases.
    +var CfgFields;
    +var CrossPageChannel;
    +var CrossPageChannelRole;
    +var TransportTypes;
    +
    +var outerXpc;
    +var innerXpc;
    +var peerIframe;
    +var channelName;
    +var messageIsSync = false;
    +
    +function setUpPage() {
    +  CfgFields = goog.net.xpc.CfgFields;
    +  CrossPageChannel = goog.net.xpc.CrossPageChannel;
    +  CrossPageChannelRole = goog.net.xpc.CrossPageChannelRole;
    +  TransportTypes = goog.net.xpc.TransportTypes;
    +
    +  // Show debug log
    +  var debugDiv = document.createElement('debugDiv');
    +  document.body.appendChild(debugDiv);
    +  var logger = goog.log.getLogger('goog.net.xpc');
    +  logger.setLevel(goog.log.Level.ALL);
    +  goog.log.addHandler(logger, function(logRecord) {
    +    var msgElm = goog.dom.createDom('div');
    +    msgElm.innerHTML = logRecord.getMessage();
    +    goog.dom.appendChild(debugDiv, msgElm);
    +  });
    +}
    +
    +
    +function tearDown() {
    +  if (peerIframe) {
    +    document.body.removeChild(peerIframe);
    +    peerIframe = null;
    +  }
    +  if (outerXpc) {
    +    outerXpc.dispose();
    +    outerXpc = null;
    +  }
    +  if (innerXpc) {
    +    innerXpc.dispose();
    +    innerXpc = null;
    +  }
    +  window.iframeLoadHandler = null;
    +  channelName = null;
    +  messageIsSync = false;
    +}
    +
    +
    +function createIframe() {
    +  peerIframe = document.createElement('iframe');
    +  peerIframe.id = PEER_IFRAME_ID;
    +  document.body.insertBefore(peerIframe, document.body.firstChild);
    +}
    +
    +
    +/**
    + * Tests 2 same domain frames using direct transport.
    + */
    +function testDirectTransport() {
    +  // This test has been flaky on IE 8-11 on Win7.
    +  // For now, disable.
    +  // Flakiness is tracked in http://b/18595666
    +  if (goog.labs.userAgent.browser.isIE()) {
    +    return;
    +  }
    +
    +  createIframe();
    +  channelName = goog.net.xpc.getRandomString(10);
    +  outerXpc = new CrossPageChannel(
    +      getConfiguration(CrossPageChannelRole.OUTER, PEER_IFRAME_ID));
    +  // Outgoing service.
    +  outerXpc.registerService(ECHO_SERVICE_NAME, goog.nullFunction);
    +  // Incoming service.
    +  outerXpc.registerService(
    +      RESPONSE_SERVICE_NAME,
    +      responseMessageHandler_testDirectTransport);
    +  asyncTestCase.waitForAsync('Waiting for xpc connect.');
    +  outerXpc.connect(onConnect_testDirectTransport);
    +  // inner_peer.html calls this method at end of html.
    +  window.iframeLoadHandler = onIframeLoaded_testDirectTransport;
    +  peerIframe.src = 'testdata/inner_peer.html';
    +}
    +
    +
    +function onIframeLoaded_testDirectTransport() {
    +  peerIframe.contentWindow.instantiateChannel(
    +      getConfiguration(CrossPageChannelRole.INNER));
    +}
    +
    +
    +function onConnect_testDirectTransport() {
    +  assertTrue('XPC over direct channel is connected', outerXpc.isConnected());
    +  outerXpc.send(ECHO_SERVICE_NAME, MESSAGE_PAYLOAD_1);
    +}
    +
    +
    +function responseMessageHandler_testDirectTransport(message) {
    +  assertEquals(
    +      'Received payload is equal to sent payload.',
    +      message,
    +      MESSAGE_PAYLOAD_1);
    +  asyncTestCase.continueTesting();
    +}
    +
    +
    +/**
    + * Tests 2 xpc's communicating with each other in the same window.
    + */
    +function testSameWindowDirectTransport() {
    +  channelName = goog.net.xpc.getRandomString(10);
    +
    +  outerXpc = new CrossPageChannel(getConfiguration(CrossPageChannelRole.OUTER));
    +  outerXpc.setPeerWindowObject(self);
    +
    +  // Outgoing service.
    +  outerXpc.registerService(ECHO_SERVICE_NAME, goog.nullFunction);
    +  // Incoming service.
    +  outerXpc.registerService(
    +      RESPONSE_SERVICE_NAME,
    +      outerResponseMessageHandler_testSameWindowDirectTransport);
    +  asyncTestCase.waitForAsync('Waiting for outer xpc connect.');
    +  outerXpc.connect(onOuterConnect_testSameWindowDirectTransport);
    +
    +  innerXpc = new CrossPageChannel(getConfiguration(CrossPageChannelRole.INNER));
    +  innerXpc.setPeerWindowObject(self);
    +  // Incoming service.
    +  innerXpc.registerService(
    +      ECHO_SERVICE_NAME,
    +      innerEchoMessageHandler_testSameWindowDirectTransport);
    +  // Outgoing service.
    +  innerXpc.registerService(
    +      RESPONSE_SERVICE_NAME,
    +      goog.nullFunction);
    +  innerXpc.connect();
    +}
    +
    +
    +function onOuterConnect_testSameWindowDirectTransport() {
    +  assertTrue(
    +      'XPC over direct channel, same window, is connected',
    +      outerXpc.isConnected());
    +  outerXpc.send(ECHO_SERVICE_NAME, MESSAGE_PAYLOAD_1);
    +}
    +
    +
    +function outerResponseMessageHandler_testSameWindowDirectTransport(message) {
    +  assertEquals(
    +      'Received payload is equal to sent payload.',
    +      message,
    +      MESSAGE_PAYLOAD_1);
    +  asyncTestCase.continueTesting();
    +}
    +
    +
    +function innerEchoMessageHandler_testSameWindowDirectTransport(message) {
    +  innerXpc.send(RESPONSE_SERVICE_NAME, message);
    +}
    +
    +
    +function getConfiguration(role, opt_peerFrameId) {
    +  var cfg = {};
    +  cfg[CfgFields.TRANSPORT] = TransportTypes.DIRECT;
    +  if (goog.isDefAndNotNull(opt_peerFrameId)) {
    +    cfg[CfgFields.IFRAME_ID] = opt_peerFrameId;
    +  }
    +  cfg[CfgFields.CHANNEL_NAME] = channelName;
    +  cfg[CfgFields.ROLE] = role;
    +  return cfg;
    +}
    +
    +
    +/**
    + * Tests 2 same domain frames using direct transport using sync mode.
    + */
    +function testSyncMode() {
    +  createIframe();
    +  channelName = goog.net.xpc.getRandomString(10);
    +
    +  var cfg = getConfiguration(CrossPageChannelRole.OUTER, PEER_IFRAME_ID);
    +  cfg[CfgFields.DIRECT_TRANSPORT_SYNC_MODE] = true;
    +
    +  outerXpc = new CrossPageChannel(cfg);
    +  // Outgoing service.
    +  outerXpc.registerService(ECHO_SERVICE_NAME, goog.nullFunction);
    +  // Incoming service.
    +  outerXpc.registerService(
    +      RESPONSE_SERVICE_NAME,
    +      responseMessageHandler_testSyncMode);
    +  asyncTestCase.waitForAsync('Waiting for xpc connect.');
    +  outerXpc.connect(onConnect_testSyncMode);
    +  // inner_peer.html calls this method at end of html.
    +  window.iframeLoadHandler = onIframeLoaded_testSyncMode;
    +  peerIframe.src = 'testdata/inner_peer.html';
    +}
    +
    +
    +function onIframeLoaded_testSyncMode() {
    +  var cfg = getConfiguration(CrossPageChannelRole.INNER);
    +  cfg[CfgFields.DIRECT_TRANSPORT_SYNC_MODE] = true;
    +  peerIframe.contentWindow.instantiateChannel(cfg);
    +}
    +
    +
    +function onConnect_testSyncMode() {
    +  assertTrue('XPC over direct channel is connected', outerXpc.isConnected());
    +  messageIsSync = true;
    +  outerXpc.send(ECHO_SERVICE_NAME, MESSAGE_PAYLOAD_1);
    +  messageIsSync = false;
    +}
    +
    +
    +function responseMessageHandler_testSyncMode(message) {
    +  assertTrue('The message response was syncronous', messageIsSync);
    +  assertEquals(
    +      'Received payload is equal to sent payload.',
    +      message,
    +      MESSAGE_PAYLOAD_1);
    +  asyncTestCase.continueTesting();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/frameelementmethodtransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/frameelementmethodtransport.js
    new file mode 100644
    index 0000000000..20aed4a4ce
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/frameelementmethodtransport.js
    @@ -0,0 +1,270 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the frame element method transport for cross-domain
    + * communication. It exploits the fact that FF lets a page in an
    + * iframe call a method on the iframe-element it is contained in, even if the
    + * containing page is from a different domain.
    + *
    + */
    +
    +
    +goog.provide('goog.net.xpc.FrameElementMethodTransport');
    +
    +goog.require('goog.log');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +
    +
    +
    +/**
    + * Frame-element method transport.
    + *
    + * Firefox allows a document within an iframe to call methods on the
    + * iframe-element added by the containing document.
    + * NOTE(user): Tested in all FF versions starting from 1.0
    + *
    + * @param {goog.net.xpc.CrossPageChannel} channel The channel this transport
    + *     belongs to.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
    + *     the correct window.
    + * @constructor
    + * @extends {goog.net.xpc.Transport}
    + * @final
    + */
    +goog.net.xpc.FrameElementMethodTransport = function(channel, opt_domHelper) {
    +  goog.net.xpc.FrameElementMethodTransport.base(
    +      this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  // To transfer messages, this transport basically uses normal function calls,
    +  // which are synchronous. To avoid endless recursion, the delivery has to
    +  // be artificially made asynchronous.
    +
    +  /**
    +   * Array for queued messages.
    +   * @type {Array<{serviceName: string, payload: string}>}
    +   * @private
    +   */
    +  this.queue_ = [];
    +
    +  /**
    +   * Callback function which wraps deliverQueued_.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.deliverQueuedCb_ = goog.bind(this.deliverQueued_, this);
    +};
    +goog.inherits(goog.net.xpc.FrameElementMethodTransport, goog.net.xpc.Transport);
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @protected
    + * @override
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.transportType =
    +    goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
    +
    +
    +/** @private */
    +goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetupCb_;
    +
    +
    +/** @private */
    +goog.net.xpc.FrameElementMethodTransport.prototype.outgoing_;
    +
    +
    +/** @private */
    +goog.net.xpc.FrameElementMethodTransport.prototype.iframeElm_;
    +
    +
    +/**
    + * Flag used to enforce asynchronous messaging semantics.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.recursive_ = false;
    +
    +
    +/**
    + * Timer used to enforce asynchronous message delivery.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.timer_ = 0;
    +
    +
    +/**
    + * Holds the function to send messages to the peer
    + * (once it becomes available).
    + * @type {Function}
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.outgoing_ = null;
    +
    +
    +/**
    + * Connect this transport.
    + * @override
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.connect = function() {
    +  if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
    +    // get shortcut to iframe-element
    +    this.iframeElm_ = this.channel_.getIframeElement();
    +
    +    // add the gateway function to the iframe-element
    +    // (to be called by the peer)
    +    this.iframeElm_['XPC_toOuter'] = goog.bind(this.incoming_, this);
    +
    +    // at this point we just have to wait for a notification from the peer...
    +
    +  } else {
    +    this.attemptSetup_();
    +  }
    +};
    +
    +
    +/**
    + * Only used from within an iframe. Attempts to attach the method
    + * to be used for sending messages by the containing document. Has to
    + * wait until the containing document has finished. Therefore calls
    + * itself in a timeout if not successful.
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetup_ = function() {
    +  var retry = true;
    +  /** @preserveTry */
    +  try {
    +    if (!this.iframeElm_) {
    +      // throws security exception when called too early
    +      this.iframeElm_ = this.getWindow().frameElement;
    +    }
    +    // check if iframe-element and the gateway-function to the
    +    // outer-frame are present
    +    // TODO(user) Make sure the following code doesn't throw any exceptions
    +    if (this.iframeElm_ && this.iframeElm_['XPC_toOuter']) {
    +      // get a reference to the gateway function
    +      this.outgoing_ = this.iframeElm_['XPC_toOuter'];
    +      // attach the gateway function the other document will use
    +      this.iframeElm_['XPC_toOuter']['XPC_toInner'] =
    +          goog.bind(this.incoming_, this);
    +      // stop retrying
    +      retry = false;
    +      // notify outer frame
    +      this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
    +      // notify channel that the transport is ready
    +      this.channel_.notifyConnected();
    +    }
    +  }
    +  catch (e) {
    +    goog.log.error(goog.net.xpc.logger,
    +        'exception caught while attempting setup: ' + e);
    +  }
    +  // retry necessary?
    +  if (retry) {
    +    if (!this.attemptSetupCb_) {
    +      this.attemptSetupCb_ = goog.bind(this.attemptSetup_, this);
    +    }
    +    this.getWindow().setTimeout(this.attemptSetupCb_, 100);
    +  }
    +};
    +
    +
    +/**
    + * Handles transport service messages.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.transportServiceHandler =
    +    function(payload) {
    +  if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER &&
    +      !this.channel_.isConnected() && payload == goog.net.xpc.SETUP_ACK_) {
    +    // get a reference to the gateway function
    +    this.outgoing_ = this.iframeElm_['XPC_toOuter']['XPC_toInner'];
    +    // notify the channel we're ready
    +    this.channel_.notifyConnected();
    +  } else {
    +    throw Error('Got unexpected transport message.');
    +  }
    +};
    +
    +
    +/**
    + * Process incoming message.
    + * @param {string} serviceName The name of the service the message is to be
    + * delivered to.
    + * @param {string} payload The message to process.
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.incoming_ =
    +    function(serviceName, payload) {
    +  if (!this.recursive_ && this.queue_.length == 0) {
    +    this.channel_.xpcDeliver(serviceName, payload);
    +  }
    +  else {
    +    this.queue_.push({serviceName: serviceName, payload: payload});
    +    if (this.queue_.length == 1) {
    +      this.timer_ = this.getWindow().setTimeout(this.deliverQueuedCb_, 1);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Delivers queued messages.
    + * @private
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.deliverQueued_ =
    +    function() {
    +  while (this.queue_.length) {
    +    var msg = this.queue_.shift();
    +    this.channel_.xpcDeliver(msg.serviceName, msg.payload);
    +  }
    +};
    +
    +
    +/**
    + * Send a message
    + * @param {string} service The name off the service the message is to be
    + * delivered to.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.FrameElementMethodTransport.prototype.send =
    +    function(service, payload) {
    +  this.recursive_ = true;
    +  this.outgoing_(service, payload);
    +  this.recursive_ = false;
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.FrameElementMethodTransport.prototype.disposeInternal =
    +    function() {
    +  goog.net.xpc.FrameElementMethodTransport.superClass_.disposeInternal.call(
    +      this);
    +  this.outgoing_ = null;
    +  this.iframeElm_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport.js
    new file mode 100644
    index 0000000000..e92dcabf96
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport.js
    @@ -0,0 +1,984 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the iframe polling transport.
    + */
    +
    +
    +goog.provide('goog.net.xpc.IframePollingTransport');
    +goog.provide('goog.net.xpc.IframePollingTransport.Receiver');
    +goog.provide('goog.net.xpc.IframePollingTransport.Sender');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Iframe polling transport. Uses hidden iframes to transfer data
    + * in the fragment identifier of the URL. The peer polls the iframe's location
    + * for changes.
    + * Unfortunately, in Safari this screws up the history, because Safari doesn't
    + * allow to call location.replace() on a window containing a document from a
    + * different domain (last version tested: 2.0.4).
    + *
    + * @param {goog.net.xpc.CrossPageChannel} channel The channel this
    + *     transport belongs to.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
    + *     the correct window.
    + * @constructor
    + * @extends {goog.net.xpc.Transport}
    + * @final
    + */
    +goog.net.xpc.IframePollingTransport = function(channel, opt_domHelper) {
    +  goog.net.xpc.IframePollingTransport.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The URI used to send messages.
    +   * @type {string}
    +   * @private
    +   */
    +  this.sendUri_ =
    +      this.channel_.getConfig()[goog.net.xpc.CfgFields.PEER_POLL_URI];
    +
    +  /**
    +   * The URI which is polled for incoming messages.
    +   * @type {string}
    +   * @private
    +   */
    +  this.rcvUri_ =
    +      this.channel_.getConfig()[goog.net.xpc.CfgFields.LOCAL_POLL_URI];
    +
    +  /**
    +   * The queue to hold messages which can't be sent immediately.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.sendQueue_ = [];
    +};
    +goog.inherits(goog.net.xpc.IframePollingTransport, goog.net.xpc.Transport);
    +
    +
    +/**
    + * The number of times the inner frame will check for evidence of the outer
    + * frame before it tries its reconnection sequence.  These occur at 100ms
    + * intervals, making this an effective max waiting period of 500ms.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.pollsBeforeReconnect_ = 5;
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @protected
    + * @override
    + */
    +goog.net.xpc.IframePollingTransport.prototype.transportType =
    +    goog.net.xpc.TransportTypes.IFRAME_POLLING;
    +
    +
    +/**
    + * Sequence counter.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.sequence_ = 0;
    +
    +
    +/**
    + * Flag indicating whether we are waiting for an acknoledgement.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.waitForAck_ = false;
    +
    +
    +/**
    + * Flag indicating if channel has been initialized.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.initialized_ = false;
    +
    +
    +/**
    + * Reconnection iframe created by inner peer.
    + * @type {Element}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.reconnectFrame_ = null;
    +
    +
    +/** @private {goog.net.xpc.IframePollingTransport.Receiver} */
    +goog.net.xpc.IframePollingTransport.prototype.ackReceiver_;
    +
    +
    +/** @private {goog.net.xpc.IframePollingTransport.Sender} */
    +goog.net.xpc.IframePollingTransport.prototype.ackSender_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.ackIframeElm_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.ackWinObj_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.checkLocalFramesPresentCb_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.deliveryQueue_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.msgIframeElm_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.msgReceiver_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.msgSender_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.msgWinObj_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.rcvdConnectionSetupAck_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.sentConnectionSetupAck_;
    +
    +
    +/** @private {boolean} */
    +goog.net.xpc.IframePollingTransport.prototype.sentConnectionSetup_;
    +
    +
    +/** @private */
    +goog.net.xpc.IframePollingTransport.prototype.parts_;
    +
    +
    +/**
    + * The string used to prefix all iframe names and IDs.
    + * @type {string}
    + */
    +goog.net.xpc.IframePollingTransport.IFRAME_PREFIX = 'googlexpc';
    +
    +
    +/**
    + * Returns the name/ID of the message frame.
    + * @return {string} Name of message frame.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.getMsgFrameName_ = function() {
    +  return goog.net.xpc.IframePollingTransport.IFRAME_PREFIX + '_' +
    +      this.channel_.name + '_msg';
    +};
    +
    +
    +/**
    + * Returns the name/ID of the ack frame.
    + * @return {string} Name of ack frame.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.getAckFrameName_ = function() {
    +  return goog.net.xpc.IframePollingTransport.IFRAME_PREFIX + '_' +
    +      this.channel_.name + '_ack';
    +};
    +
    +
    +/**
    + * Determines whether the channel is still available. The channel is
    + * unavailable if the transport was disposed or the peer is no longer
    + * available.
    + * @return {boolean} Whether the channel is available.
    + */
    +goog.net.xpc.IframePollingTransport.prototype.isChannelAvailable = function() {
    +  return !this.isDisposed() && this.channel_.isPeerAvailable();
    +};
    +
    +
    +/**
    + * Safely retrieves the frames from the peer window. If an error is thrown
    + * (e.g. the window is closing) an empty frame object is returned.
    + * @return {!Object<!Window>} The frames from the peer window.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.getPeerFrames_ = function() {
    +  try {
    +    if (this.isChannelAvailable()) {
    +      return this.channel_.getPeerWindowObject().frames || {};
    +    }
    +  } catch (e) {
    +    // An error may be thrown if the window is closing.
    +    goog.log.fine(goog.net.xpc.logger, 'error retrieving peer frames');
    +  }
    +  return {};
    +};
    +
    +
    +/**
    + * Safely retrieves the peer frame with the specified name.
    + * @param {string} frameName The name of the peer frame to retrieve.
    + * @return {!Window} The peer frame with the specified name.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.getPeerFrame_ = function(
    +    frameName) {
    +  return this.getPeerFrames_()[frameName];
    +};
    +
    +
    +/**
    + * Connects this transport.
    + * @override
    + */
    +goog.net.xpc.IframePollingTransport.prototype.connect = function() {
    +  if (!this.isChannelAvailable()) {
    +    // When the channel is unavailable there is no peer to poll so stop trying
    +    // to connect.
    +    return;
    +  }
    +
    +  goog.log.fine(goog.net.xpc.logger, 'transport connect called');
    +  if (!this.initialized_) {
    +    goog.log.fine(goog.net.xpc.logger, 'initializing...');
    +    this.constructSenderFrames_();
    +    this.initialized_ = true;
    +  }
    +  this.checkForeignFramesReady_();
    +};
    +
    +
    +/**
    + * Creates the iframes which are used to send messages (and acknowledgements)
    + * to the peer. Sender iframes contain a document from a different origin and
    + * therefore their content can't be accessed.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.constructSenderFrames_ =
    +    function() {
    +  var name = this.getMsgFrameName_();
    +  this.msgIframeElm_ = this.constructSenderFrame_(name);
    +  this.msgWinObj_ = this.getWindow().frames[name];
    +
    +  name = this.getAckFrameName_();
    +  this.ackIframeElm_ = this.constructSenderFrame_(name);
    +  this.ackWinObj_ = this.getWindow().frames[name];
    +};
    +
    +
    +/**
    + * Constructs a sending frame the the given id.
    + * @param {string} id The id.
    + * @return {!Element} The constructed frame.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.constructSenderFrame_ =
    +    function(id) {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'constructing sender frame: ' + id);
    +  var ifr = goog.dom.createElement('iframe');
    +  var s = ifr.style;
    +  s.position = 'absolute';
    +  s.top = '-10px'; s.left = '10px'; s.width = '1px'; s.height = '1px';
    +  ifr.id = ifr.name = id;
    +  ifr.src = this.sendUri_ + '#INITIAL';
    +  this.getWindow().document.body.appendChild(ifr);
    +  return ifr;
    +};
    +
    +
    +/**
    + * The protocol for reconnecting is for the inner frame to change channel
    + * names, and then communicate the new channel name to the outer peer.
    + * The outer peer looks in a predefined location for the channel name
    + * upate. It is important to use a completely new channel name, as this
    + * will ensure that all messaging iframes are not in the bfcache.
    + * Otherwise, Safari may pollute the history when modifying the location
    + * of bfcached iframes.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.maybeInnerPeerReconnect_ =
    +    function() {
    +  // Reconnection has been found to not function on some browsers (eg IE7), so
    +  // it's important that the mechanism only be triggered as a last resort.  As
    +  // such, we poll a number of times to find the outer iframe before triggering
    +  // it.
    +  if (this.reconnectFrame_ || this.pollsBeforeReconnect_-- > 0) {
    +    return;
    +  }
    +
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'Inner peer reconnect triggered.');
    +  this.channel_.updateChannelNameAndCatalog(goog.net.xpc.getRandomString(10));
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'switching channels: ' + this.channel_.name);
    +  this.deconstructSenderFrames_();
    +  this.initialized_ = false;
    +  // Communicate new channel name to outer peer.
    +  this.reconnectFrame_ = this.constructSenderFrame_(
    +      goog.net.xpc.IframePollingTransport.IFRAME_PREFIX +
    +          '_reconnect_' + this.channel_.name);
    +};
    +
    +
    +/**
    + * Scans inner peer for a reconnect message, which will be used to update
    + * the outer peer's channel name. If a reconnect message is found, the
    + * sender frames will be cleaned up to make way for the new sender frames.
    + * Only called by the outer peer.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.outerPeerReconnect_ = function() {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'outerPeerReconnect called');
    +  var frames = this.getPeerFrames_();
    +  var length = frames.length;
    +  for (var i = 0; i < length; i++) {
    +    var frameName;
    +    try {
    +      if (frames[i] && frames[i].name) {
    +        frameName = frames[i].name;
    +      }
    +    } catch (e) {
    +      // Do nothing.
    +    }
    +    if (!frameName) {
    +      continue;
    +    }
    +    var message = frameName.split('_');
    +    if (message.length == 3 &&
    +        message[0] == goog.net.xpc.IframePollingTransport.IFRAME_PREFIX &&
    +        message[1] == 'reconnect') {
    +      // This is a legitimate reconnect message from the peer. Start using
    +      // the peer provided channel name, and start a connection over from
    +      // scratch.
    +      this.channel_.name = message[2];
    +      this.deconstructSenderFrames_();
    +      this.initialized_ = false;
    +      break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Cleans up the existing sender frames owned by this peer. Only called by
    + * the outer peer.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.deconstructSenderFrames_ =
    +    function() {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'deconstructSenderFrames called');
    +  if (this.msgIframeElm_) {
    +    this.msgIframeElm_.parentNode.removeChild(this.msgIframeElm_);
    +    this.msgIframeElm_ = null;
    +    this.msgWinObj_ = null;
    +  }
    +  if (this.ackIframeElm_) {
    +    this.ackIframeElm_.parentNode.removeChild(this.ackIframeElm_);
    +    this.ackIframeElm_ = null;
    +    this.ackWinObj_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Checks if the frames in the peer's page are ready. These contain a
    + * document from the own domain and are the ones messages are received through.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.checkForeignFramesReady_ =
    +    function() {
    +  // check if the connected iframe ready
    +  if (!(this.isRcvFrameReady_(this.getMsgFrameName_()) &&
    +        this.isRcvFrameReady_(this.getAckFrameName_()))) {
    +    goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +        'foreign frames not (yet) present');
    +
    +    if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.INNER) {
    +      // The outer peer might need a short time to get its frames ready, as
    +      // CrossPageChannel prevents them from getting created until the inner
    +      // peer's frame has thrown its loaded event.  This method is a noop for
    +      // the first few times it's called, and then allows the reconnection
    +      // sequence to begin.
    +      this.maybeInnerPeerReconnect_();
    +    } else if (this.channel_.getRole() ==
    +               goog.net.xpc.CrossPageChannelRole.OUTER) {
    +      // The inner peer is either not loaded yet, or the receiving
    +      // frames are simply missing. Since we cannot discern the two cases, we
    +      // should scan for a reconnect message from the inner peer.
    +      this.outerPeerReconnect_();
    +    }
    +
    +    // start a timer to check again
    +    this.getWindow().setTimeout(goog.bind(this.connect, this), 100);
    +  } else {
    +    goog.log.fine(goog.net.xpc.logger, 'foreign frames present');
    +
    +    // Create receivers.
    +    this.msgReceiver_ = new goog.net.xpc.IframePollingTransport.Receiver(
    +        this,
    +        this.getPeerFrame_(this.getMsgFrameName_()),
    +        goog.bind(this.processIncomingMsg, this));
    +    this.ackReceiver_ = new goog.net.xpc.IframePollingTransport.Receiver(
    +        this,
    +        this.getPeerFrame_(this.getAckFrameName_()),
    +        goog.bind(this.processIncomingAck, this));
    +
    +    this.checkLocalFramesPresent_();
    +  }
    +};
    +
    +
    +/**
    + * Checks if the receiving frame is ready.
    + * @param {string} frameName Which receiving frame to check.
    + * @return {boolean} Whether the receiving frame is ready.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.isRcvFrameReady_ =
    +    function(frameName) {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'checking for receive frame: ' + frameName);
    +  /** @preserveTry */
    +  try {
    +    var winObj = this.getPeerFrame_(frameName);
    +    if (!winObj || winObj.location.href.indexOf(this.rcvUri_) != 0) {
    +      return false;
    +    }
    +  } catch (e) {
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Checks if the iframes created in the own document are ready.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.checkLocalFramesPresent_ =
    +    function() {
    +
    +  // Are the sender frames ready?
    +  // These contain a document from the peer's domain, therefore we can only
    +  // check if the frame itself is present.
    +  var frames = this.getPeerFrames_();
    +  if (!(frames[this.getAckFrameName_()] &&
    +        frames[this.getMsgFrameName_()])) {
    +    // start a timer to check again
    +    if (!this.checkLocalFramesPresentCb_) {
    +      this.checkLocalFramesPresentCb_ = goog.bind(
    +          this.checkLocalFramesPresent_, this);
    +    }
    +    this.getWindow().setTimeout(this.checkLocalFramesPresentCb_, 100);
    +    goog.log.fine(goog.net.xpc.logger, 'local frames not (yet) present');
    +  } else {
    +    // Create senders.
    +    this.msgSender_ = new goog.net.xpc.IframePollingTransport.Sender(
    +        this.sendUri_, this.msgWinObj_);
    +    this.ackSender_ = new goog.net.xpc.IframePollingTransport.Sender(
    +        this.sendUri_, this.ackWinObj_);
    +
    +    goog.log.fine(goog.net.xpc.logger, 'local frames ready');
    +
    +    this.getWindow().setTimeout(goog.bind(function() {
    +      this.msgSender_.send(goog.net.xpc.SETUP);
    +      this.sentConnectionSetup_ = true;
    +      this.waitForAck_ = true;
    +      goog.log.fine(goog.net.xpc.logger, 'SETUP sent');
    +    }, this), 100);
    +  }
    +};
    +
    +
    +/**
    + * Check if connection is ready.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.checkIfConnected_ = function() {
    +  if (this.sentConnectionSetupAck_ && this.rcvdConnectionSetupAck_) {
    +    this.channel_.notifyConnected();
    +
    +    if (this.deliveryQueue_) {
    +      goog.log.fine(goog.net.xpc.logger, 'delivering queued messages ' +
    +          '(' + this.deliveryQueue_.length + ')');
    +
    +      for (var i = 0, m; i < this.deliveryQueue_.length; i++) {
    +        m = this.deliveryQueue_[i];
    +        this.channel_.xpcDeliver(m.service, m.payload);
    +      }
    +      delete this.deliveryQueue_;
    +    }
    +  } else {
    +    goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +        'checking if connected: ' +
    +        'ack sent:' + this.sentConnectionSetupAck_ +
    +        ', ack rcvd: ' + this.rcvdConnectionSetupAck_);
    +  }
    +};
    +
    +
    +/**
    + * Processes an incoming message.
    + * @param {string} raw The complete received string.
    + */
    +goog.net.xpc.IframePollingTransport.prototype.processIncomingMsg =
    +    function(raw) {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'msg received: ' + raw);
    +
    +  if (raw == goog.net.xpc.SETUP) {
    +    if (!this.ackSender_) {
    +      // Got SETUP msg, but we can't send an ack.
    +      return;
    +    }
    +
    +    this.ackSender_.send(goog.net.xpc.SETUP_ACK_);
    +    goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST, 'SETUP_ACK sent');
    +
    +    this.sentConnectionSetupAck_ = true;
    +    this.checkIfConnected_();
    +
    +  } else if (this.channel_.isConnected() || this.sentConnectionSetupAck_) {
    +
    +    var pos = raw.indexOf('|');
    +    var head = raw.substring(0, pos);
    +    var frame = raw.substring(pos + 1);
    +
    +    // check if it is a framed message
    +    pos = head.indexOf(',');
    +    if (pos == -1) {
    +      var seq = head;
    +      // send acknowledgement
    +      this.ackSender_.send('ACK:' + seq);
    +      this.deliverPayload_(frame);
    +    } else {
    +      var seq = head.substring(0, pos);
    +      // send acknowledgement
    +      this.ackSender_.send('ACK:' + seq);
    +
    +      var partInfo = head.substring(pos + 1).split('/');
    +      var part0 = parseInt(partInfo[0], 10);
    +      var part1 = parseInt(partInfo[1], 10);
    +      // create an array to accumulate the parts if this is the
    +      // first frame of a message
    +      if (part0 == 1) {
    +        this.parts_ = [];
    +      }
    +      this.parts_.push(frame);
    +      // deliver the message if this was the last frame of a message
    +      if (part0 == part1) {
    +        this.deliverPayload_(this.parts_.join(''));
    +        delete this.parts_;
    +      }
    +    }
    +  } else {
    +    goog.log.warning(goog.net.xpc.logger,
    +        'received msg, but channel is not connected');
    +  }
    +};
    +
    +
    +/**
    + * Process an incoming acknowdedgement.
    + * @param {string} msgStr The incoming ack string to process.
    + */
    +goog.net.xpc.IframePollingTransport.prototype.processIncomingAck =
    +    function(msgStr) {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'ack received: ' + msgStr);
    +
    +  if (msgStr == goog.net.xpc.SETUP_ACK_) {
    +    this.waitForAck_ = false;
    +    this.rcvdConnectionSetupAck_ = true;
    +    // send the next frame
    +    this.checkIfConnected_();
    +
    +  } else if (this.channel_.isConnected()) {
    +    if (!this.waitForAck_) {
    +      goog.log.warning(goog.net.xpc.logger, 'got unexpected ack');
    +      return;
    +    }
    +
    +    var seq = parseInt(msgStr.split(':')[1], 10);
    +    if (seq == this.sequence_) {
    +      this.waitForAck_ = false;
    +      this.sendNextFrame_();
    +    } else {
    +      goog.log.warning(goog.net.xpc.logger, 'got ack with wrong sequence');
    +    }
    +  } else {
    +    goog.log.warning(goog.net.xpc.logger,
    +        'received ack, but channel not connected');
    +  }
    +};
    +
    +
    +/**
    + * Sends a frame (message part).
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.sendNextFrame_ = function() {
    +  // do nothing if we are waiting for an acknowledgement or the
    +  // queue is emtpy
    +  if (this.waitForAck_ || !this.sendQueue_.length) {
    +    return;
    +  }
    +
    +  var s = this.sendQueue_.shift();
    +  ++this.sequence_;
    +  this.msgSender_.send(this.sequence_ + s);
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +      'msg sent: ' + this.sequence_ + s);
    +
    +
    +  this.waitForAck_ = true;
    +};
    +
    +
    +/**
    + * Delivers a message.
    + * @param {string} s The complete message string ("<service_name>:<payload>").
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.deliverPayload_ = function(s) {
    +  // determine the service name and the payload
    +  var pos = s.indexOf(':');
    +  var service = s.substr(0, pos);
    +  var payload = s.substring(pos + 1);
    +
    +  // deliver the message
    +  if (!this.channel_.isConnected()) {
    +    // as valid messages can come in before a SETUP_ACK has
    +    // been received (because subchannels for msgs and acks are independent),
    +    // delay delivery of early messages until after 'connect'-event
    +    (this.deliveryQueue_ || (this.deliveryQueue_ = [])).
    +        push({service: service, payload: payload});
    +    goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +        'queued delivery');
    +  } else {
    +    this.channel_.xpcDeliver(service, payload);
    +  }
    +};
    +
    +
    +// ---- send message ----
    +
    +
    +/**
    + * Maximal frame length.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.prototype.MAX_FRAME_LENGTH_ = 3800;
    +
    +
    +/**
    + * Sends a message. Splits it in multiple frames if too long (exceeds IE's
    + * URL-length maximum.
    + * Wireformat: <seq>[,<frame_no>/<#frames>]|<frame_content>
    + *
    + * @param {string} service Name of service this the message has to be delivered.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.IframePollingTransport.prototype.send =
    +    function(service, payload) {
    +  var frame = service + ':' + payload;
    +  // put in queue
    +  if (!goog.userAgent.IE || payload.length <= this.MAX_FRAME_LENGTH_) {
    +    this.sendQueue_.push('|' + frame);
    +  }
    +  else {
    +    var l = payload.length;
    +    var num = Math.ceil(l / this.MAX_FRAME_LENGTH_); // number of frames
    +    var pos = 0;
    +    var i = 1;
    +    while (pos < l) {
    +      this.sendQueue_.push(',' + i + '/' + num + '|' +
    +                           frame.substr(pos, this.MAX_FRAME_LENGTH_));
    +      i++;
    +      pos += this.MAX_FRAME_LENGTH_;
    +    }
    +  }
    +  this.sendNextFrame_();
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.IframePollingTransport.prototype.disposeInternal = function() {
    +  goog.net.xpc.IframePollingTransport.base(this, 'disposeInternal');
    +
    +  var receivers = goog.net.xpc.IframePollingTransport.receivers_;
    +  goog.array.remove(receivers, this.msgReceiver_);
    +  goog.array.remove(receivers, this.ackReceiver_);
    +  this.msgReceiver_ = this.ackReceiver_ = null;
    +
    +  goog.dom.removeNode(this.msgIframeElm_);
    +  goog.dom.removeNode(this.ackIframeElm_);
    +  this.msgIframeElm_ = this.ackIframeElm_ = null;
    +  this.msgWinObj_ = this.ackWinObj_ = null;
    +};
    +
    +
    +/**
    + * Array holding all Receiver-instances.
    + * @type {Array<goog.net.xpc.IframePollingTransport.Receiver>}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.receivers_ = [];
    +
    +
    +/**
    + * Short polling interval.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.TIME_POLL_SHORT_ = 10;
    +
    +
    +/**
    + * Long polling interval.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.TIME_POLL_LONG_ = 100;
    +
    +
    +/**
    + * Period how long to use TIME_POLL_SHORT_ before raising polling-interval
    + * to TIME_POLL_LONG_ after an activity.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.TIME_SHORT_POLL_AFTER_ACTIVITY_ =
    +    1000;
    +
    +
    +/**
    + * Polls all receivers.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.receive_ = function() {
    +  var receivers = goog.net.xpc.IframePollingTransport.receivers_;
    +  var receiver;
    +  var rcvd = false;
    +
    +  /** @preserveTry */
    +  try {
    +    for (var i = 0; receiver = receivers[i]; i++) {
    +      rcvd = rcvd || receiver.receive();
    +    }
    +  } catch (e) {
    +    goog.log.info(goog.net.xpc.logger, 'receive_() failed: ' + e);
    +
    +    // Notify the channel that the transport had an error.
    +    receiver.transport_.channel_.notifyTransportError();
    +
    +    // notifyTransportError() closes the channel and disposes the transport.
    +    // If there are no other channels present, this.receivers_ will now be empty
    +    // and there is no need to keep polling.
    +    if (!receivers.length) {
    +      return;
    +    }
    +  }
    +
    +  var now = goog.now();
    +  if (rcvd) {
    +    goog.net.xpc.IframePollingTransport.lastActivity_ = now;
    +  }
    +
    +  // Schedule next check.
    +  var t = now - goog.net.xpc.IframePollingTransport.lastActivity_ <
    +      goog.net.xpc.IframePollingTransport.TIME_SHORT_POLL_AFTER_ACTIVITY_ ?
    +      goog.net.xpc.IframePollingTransport.TIME_POLL_SHORT_ :
    +      goog.net.xpc.IframePollingTransport.TIME_POLL_LONG_;
    +  goog.net.xpc.IframePollingTransport.rcvTimer_ = window.setTimeout(
    +      goog.net.xpc.IframePollingTransport.receiveCb_, t);
    +};
    +
    +
    +/**
    + * Callback that wraps receive_ to be used in timers.
    + * @type {Function}
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.receiveCb_ = goog.bind(
    +    goog.net.xpc.IframePollingTransport.receive_,
    +    goog.net.xpc.IframePollingTransport);
    +
    +
    +/**
    + * Starts the polling loop.
    + * @private
    + */
    +goog.net.xpc.IframePollingTransport.startRcvTimer_ = function() {
    +  goog.log.fine(goog.net.xpc.logger, 'starting receive-timer');
    +  goog.net.xpc.IframePollingTransport.lastActivity_ = goog.now();
    +  if (goog.net.xpc.IframePollingTransport.rcvTimer_) {
    +    window.clearTimeout(goog.net.xpc.IframePollingTransport.rcvTimer_);
    +  }
    +  goog.net.xpc.IframePollingTransport.rcvTimer_ = window.setTimeout(
    +      goog.net.xpc.IframePollingTransport.receiveCb_,
    +      goog.net.xpc.IframePollingTransport.TIME_POLL_SHORT_);
    +};
    +
    +
    +
    +/**
    + * goog.net.xpc.IframePollingTransport.Sender
    + *
    + * Utility class to send message-parts to a document from a different origin.
    + *
    + * @constructor
    + * @param {string} url The url the other document will use for polling.
    + * @param {Object} windowObj The frame used for sending information to.
    + * @final
    + */
    +goog.net.xpc.IframePollingTransport.Sender = function(url, windowObj) {
    +  /**
    +   * The URI used to sending messages.
    +   * @type {string}
    +   * @private
    +   */
    +  this.sendUri_ = url;
    +
    +  /**
    +   * The window object of the iframe used to send messages.
    +   * The script instantiating the Sender won't have access to
    +   * the content of sendFrame_.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.sendFrame_ = windowObj;
    +
    +  /**
    +   * Cycle counter (used to make sure that sending two identical messages sent
    +   * in direct succession can be recognized as such by the receiver).
    +   * @type {number}
    +   * @private
    +   */
    +  this.cycle_ = 0;
    +};
    +
    +
    +/**
    + * Sends a message-part (frame) to the peer.
    + * The message-part is encoded and put in the fragment identifier
    + * of the URL used for sending (and belongs to the origin/domain of the peer).
    + * @param {string} payload The message to send.
    + */
    +goog.net.xpc.IframePollingTransport.Sender.prototype.send = function(payload) {
    +  this.cycle_ = ++this.cycle_ % 2;
    +
    +  var url = this.sendUri_ + '#' + this.cycle_ + encodeURIComponent(payload);
    +
    +  // TODO(user) Find out if try/catch is still needed
    +  /** @preserveTry */
    +  try {
    +    // safari doesn't allow to call location.replace()
    +    if (goog.userAgent.WEBKIT) {
    +      this.sendFrame_.location.href = url;
    +    } else {
    +      this.sendFrame_.location.replace(url);
    +    }
    +  } catch (e) {
    +    goog.log.error(goog.net.xpc.logger, 'sending failed', e);
    +  }
    +
    +  // Restart receiver timer on short polling interval, to support use-cases
    +  // where we need to capture responses quickly.
    +  goog.net.xpc.IframePollingTransport.startRcvTimer_();
    +};
    +
    +
    +
    +/**
    + * goog.net.xpc.IframePollingTransport.Receiver
    + *
    + * @constructor
    + * @param {goog.net.xpc.IframePollingTransport} transport The transport to
    + *     receive from.
    + * @param {Object} windowObj The window-object to poll for location-changes.
    + * @param {Function} callback The callback-function to be called when
    + *     location has changed.
    + * @final
    + */
    +goog.net.xpc.IframePollingTransport.Receiver = function(transport,
    +                                                        windowObj,
    +                                                        callback) {
    +  /**
    +   * The transport to receive from.
    +   * @type {goog.net.xpc.IframePollingTransport}
    +   * @private
    +   */
    +  this.transport_ = transport;
    +  this.rcvFrame_ = windowObj;
    +
    +  this.cb_ = callback;
    +  this.currentLoc_ = this.rcvFrame_.location.href.split('#')[0] + '#INITIAL';
    +
    +  goog.net.xpc.IframePollingTransport.receivers_.push(this);
    +  goog.net.xpc.IframePollingTransport.startRcvTimer_();
    +};
    +
    +
    +/**
    + * Polls the location of the receiver-frame for changes.
    + * @return {boolean} Whether a change has been detected.
    + */
    +goog.net.xpc.IframePollingTransport.Receiver.prototype.receive = function() {
    +  var loc = this.rcvFrame_.location.href;
    +
    +  if (loc != this.currentLoc_) {
    +    this.currentLoc_ = loc;
    +    var payload = loc.split('#')[1];
    +    if (payload) {
    +      payload = payload.substr(1); // discard first character (cycle)
    +      this.cb_(decodeURIComponent(payload));
    +    }
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.html b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.html
    new file mode 100644
    index 0000000000..d054daae58
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-IframePollingTransport-Compatible" content="IE=edge" />
    +  <title>
    +   NativeMessagingTransport Unit-Tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.net.xpc.IframePollingTransportTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.js b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.js
    new file mode 100644
    index 0000000000..912cb7847f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/iframepollingtransport_test.js
    @@ -0,0 +1,289 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.xpc.IframePollingTransportTest');
    +goog.setTestOnly('goog.net.xpc.IframePollingTransportTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.functions');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannel');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.object');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var mockClock = null;
    +var outerChannel = null;
    +var innerChannel = null;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true /* opt_autoInstall */);
    +
    +  // Create the peer windows.
    +  var outerPeerHostName = 'https://www.youtube.com';
    +  var outerPeerWindow = createMockPeerWindow(outerPeerHostName);
    +
    +  var innerPeerHostName = 'https://www.google.com';
    +  var innerPeerWindow = createMockPeerWindow(innerPeerHostName);
    +
    +  // Create the channels.
    +  outerChannel = createChannel(goog.net.xpc.CrossPageChannelRole.OUTER, 'test',
    +      outerPeerHostName, outerPeerWindow, innerPeerHostName, innerPeerWindow);
    +  innerChannel = createChannel(goog.net.xpc.CrossPageChannelRole.INNER, 'test',
    +      innerPeerHostName, innerPeerWindow, outerPeerHostName, outerPeerWindow);
    +}
    +
    +
    +function tearDown() {
    +  outerChannel.dispose();
    +  innerChannel.dispose();
    +  mockClock.uninstall();
    +}
    +
    +
    +/** Tests that connection happens normally and callbacks are invoked. */
    +function testConnect() {
    +  var outerConnectCallback = goog.testing.recordFunction();
    +  var innerConnectCallback = goog.testing.recordFunction();
    +
    +  // Connect the two channels.
    +  outerChannel.connect(outerConnectCallback);
    +  innerChannel.connect(innerConnectCallback);
    +  mockClock.tick(1000);
    +
    +  // Check that channels were connected and callbacks invoked.
    +  assertEquals(1, outerConnectCallback.getCallCount());
    +  assertEquals(1, innerConnectCallback.getCallCount());
    +  assertTrue(outerChannel.isConnected());
    +  assertTrue(innerChannel.isConnected());
    +}
    +
    +
    +/** Tests that messages are successfully delivered to the inner peer. */
    +function testSend_outerToInner() {
    +  var serviceCallback = goog.testing.recordFunction();
    +
    +  // Register a service handler in the inner channel.
    +  innerChannel.registerService('svc', function(payload) {
    +    assertEquals('hello', payload);
    +    serviceCallback();
    +  });
    +
    +  // Connect the two channels.
    +  outerChannel.connect();
    +  innerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Send a message.
    +  outerChannel.send('svc', 'hello');
    +  mockClock.tick(1000);
    +
    +  // Check that the message was handled.
    +  assertEquals(1, serviceCallback.getCallCount());
    +}
    +
    +
    +/** Tests that messages are successfully delivered to the outer peer. */
    +function testSend_innerToOuter() {
    +  var serviceCallback = goog.testing.recordFunction();
    +
    +  // Register a service handler in the inner channel.
    +  outerChannel.registerService('svc', function(payload) {
    +    assertEquals('hello', payload);
    +    serviceCallback();
    +  });
    +
    +  // Connect the two channels.
    +  outerChannel.connect();
    +  innerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Send a message.
    +  innerChannel.send('svc', 'hello');
    +  mockClock.tick(1000);
    +
    +  // Check that the message was handled.
    +  assertEquals(1, serviceCallback.getCallCount());
    +}
    +
    +
    +/** Tests that closing the outer peer does not cause an error. */
    +function testSend_outerPeerClosed() {
    +  // Connect the inner channel.
    +  innerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Close the outer peer before it has a chance to connect.
    +  closeWindow(innerChannel.getPeerWindowObject());
    +
    +  // Allow timers to execute (and fail).
    +  mockClock.tick(1000);
    +}
    +
    +
    +/** Tests that closing the inner peer does not cause an error. */
    +function testSend_innerPeerClosed() {
    +  // Connect the outer channel.
    +  outerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Close the inner peer before it has a chance to connect.
    +  closeWindow(outerChannel.getPeerWindowObject());
    +
    +  // Allow timers to execute (and fail).
    +  mockClock.tick(1000);
    +}
    +
    +
    +/** Tests that partially closing the outer peer does not cause an error. */
    +function testSend_outerPeerClosing() {
    +  // Connect the inner channel.
    +  innerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Close the outer peer before it has a chance to connect, but
    +  // leave closed set to false to simulate a partially closed window.
    +  closeWindow(innerChannel.getPeerWindowObject());
    +  innerChannel.getPeerWindowObject().closed = false;
    +
    +  // Allow timers to execute (and fail).
    +  mockClock.tick(1000);
    +}
    +
    +
    +/** Tests that partially closing the inner peer does not cause an error. */
    +function testSend_innerPeerClosing() {
    +  // Connect the outer channel.
    +  outerChannel.connect();
    +  mockClock.tick(1000);
    +
    +  // Close the inner peer before it has a chance to connect, but
    +  // leave closed set to false to simulate a partially closed window.
    +  closeWindow(outerChannel.getPeerWindowObject());
    +  outerChannel.getPeerWindowObject().closed = false;
    +
    +  // Allow timers to execute (and fail).
    +  mockClock.tick(1000);
    +}
    +
    +
    +/**
    + * Creates a channel with the specified configuration, using frame polling.
    + * @param {!goog.net.xpc.CrossPageChannelRole} role The channel role.
    + * @param {string} channelName The channel name.
    + * @param {string} fromHostName The host name of the window hosting the channel.
    + * @param {!Object} fromWindow The window hosting the channel.
    + * @param {string} toHostName The host name of the peer window.
    + * @param {!Object} toWindow The peer window.
    + */
    +function createChannel(role, channelName, fromHostName, fromWindow, toHostName,
    +    toWindow) {
    +
    +  // Build a channel config using frame polling.
    +  var channelConfig = goog.object.create(
    +      goog.net.xpc.CfgFields.ROLE,
    +      role,
    +      goog.net.xpc.CfgFields.PEER_HOSTNAME,
    +      toHostName,
    +      goog.net.xpc.CfgFields.CHANNEL_NAME,
    +      channelName,
    +      goog.net.xpc.CfgFields.LOCAL_POLL_URI,
    +      fromHostName + '/robots.txt',
    +      goog.net.xpc.CfgFields.PEER_POLL_URI,
    +      toHostName + '/robots.txt',
    +      goog.net.xpc.CfgFields.TRANSPORT,
    +      goog.net.xpc.TransportTypes.IFRAME_POLLING);
    +
    +  // Build the channel.
    +  var channel = new goog.net.xpc.CrossPageChannel(channelConfig);
    +  channel.setPeerWindowObject(toWindow);
    +
    +  // Update the transport's getWindow, to return the correct host window.
    +  channel.createTransport_();
    +  channel.transport_.getWindow = goog.functions.constant(fromWindow);
    +  return channel;
    +}
    +
    +
    +/**
    + * Creates a mock window to use as a peer. The peer window will host the frame
    + * elements.
    + * @param {string} url The peer window's initial URL.
    + */
    +function createMockPeerWindow(url) {
    +  var mockPeer = createMockWindow(url);
    +
    +  // Update the appendChild method to use a mock frame window.
    +  mockPeer.document.body.appendChild = function(el) {
    +    assertEquals(goog.dom.TagName.IFRAME, el.tagName);
    +    mockPeer.frames[el.name] = createMockWindow(el.src);
    +    mockPeer.document.body.element.appendChild(el);
    +  };
    +
    +  return mockPeer;
    +}
    +
    +
    +/**
    + * Creates a mock window.
    + * @param {string} url The window's initial URL.
    + */
    +function createMockWindow(url) {
    +  // Create the mock window, document and body.
    +  var mockWindow = {};
    +  var mockDocument = {};
    +  var mockBody = {};
    +  var mockLocation = {};
    +
    +  // Configure the mock window's document body.
    +  mockBody.element = goog.dom.createDom(goog.dom.TagName.BODY);
    +
    +  // Configure the mock window's document.
    +  mockDocument.body = mockBody;
    +
    +  // Configure the mock window's location.
    +  mockLocation.href = url;
    +  mockLocation.replace = function(value) { mockLocation.href = value; };
    +
    +  // Configure the mock window.
    +  mockWindow.document = mockDocument;
    +  mockWindow.frames = {};
    +  mockWindow.location = mockLocation;
    +  mockWindow.setTimeout = goog.Timer.callOnce;
    +
    +  return mockWindow;
    +}
    +
    +
    +/**
    + * Emulates closing the specified window by clearing frames, document and
    + * location.
    + */
    +function closeWindow(targetWindow) {
    +  // Close any child frame windows.
    +  for (var frameName in targetWindow.frames) {
    +    closeWindow(targetWindow.frames[frameName]);
    +  }
    +
    +  // Clear the target window, set closed to true.
    +  targetWindow.closed = true;
    +  targetWindow.frames = null;
    +  targetWindow.document = null;
    +  targetWindow.location = null;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/iframerelaytransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/iframerelaytransport.js
    new file mode 100644
    index 0000000000..1342fc8999
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/iframerelaytransport.js
    @@ -0,0 +1,409 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the iframe relay tranport.
    + */
    +
    +
    +goog.provide('goog.net.xpc.IframeRelayTransport');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.safe');
    +goog.require('goog.events');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.string');
    +goog.require('goog.string.Const');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Iframe relay transport. Creates hidden iframes containing a document
    + * from the peer's origin. Data is transferred in the fragment identifier.
    + * Therefore the document loaded in the iframes can be served from the
    + * browser's cache.
    + *
    + * @param {goog.net.xpc.CrossPageChannel} channel The channel this
    + *     transport belongs to.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
    + *     the correct window.
    + * @constructor
    + * @extends {goog.net.xpc.Transport}
    + * @final
    + */
    +goog.net.xpc.IframeRelayTransport = function(channel, opt_domHelper) {
    +  goog.net.xpc.IframeRelayTransport.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The URI used to relay data to the peer.
    +   * @type {string}
    +   * @private
    +   */
    +  this.peerRelayUri_ =
    +      this.channel_.getConfig()[goog.net.xpc.CfgFields.PEER_RELAY_URI];
    +
    +  /**
    +   * The id of the iframe the peer page lives in.
    +   * @type {string}
    +   * @private
    +   */
    +  this.peerIframeId_ =
    +      this.channel_.getConfig()[goog.net.xpc.CfgFields.IFRAME_ID];
    +
    +  if (goog.userAgent.WEBKIT) {
    +    goog.net.xpc.IframeRelayTransport.startCleanupTimer_();
    +  }
    +};
    +goog.inherits(goog.net.xpc.IframeRelayTransport, goog.net.xpc.Transport);
    +
    +
    +if (goog.userAgent.WEBKIT) {
    +  /**
    +   * Array to keep references to the relay-iframes. Used only if
    +   * there is no way to detect when the iframes are loaded. In that
    +   * case the relay-iframes are removed after a timeout.
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.iframeRefs_ = [];
    +
    +
    +  /**
    +   * Interval at which iframes are destroyed.
    +   * @type {number}
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_ = 1000;
    +
    +
    +  /**
    +   * Time after which a relay-iframe is destroyed.
    +   * @type {number}
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.IFRAME_MAX_AGE_ = 3000;
    +
    +
    +  /**
    +   * The cleanup timer id.
    +   * @type {number}
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.cleanupTimer_ = 0;
    +
    +
    +  /**
    +   * Starts the cleanup timer.
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.startCleanupTimer_ = function() {
    +    if (!goog.net.xpc.IframeRelayTransport.cleanupTimer_) {
    +      goog.net.xpc.IframeRelayTransport.cleanupTimer_ = window.setTimeout(
    +          function() { goog.net.xpc.IframeRelayTransport.cleanup_(); },
    +          goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_);
    +    }
    +  };
    +
    +
    +  /**
    +   * Remove all relay-iframes which are older than the maximal age.
    +   * @param {number=} opt_maxAge The maximal age in milliseconds.
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.cleanup_ = function(opt_maxAge) {
    +    var now = goog.now();
    +    var maxAge =
    +        opt_maxAge || goog.net.xpc.IframeRelayTransport.IFRAME_MAX_AGE_;
    +
    +    while (goog.net.xpc.IframeRelayTransport.iframeRefs_.length &&
    +           now - goog.net.xpc.IframeRelayTransport.iframeRefs_[0].timestamp >=
    +           maxAge) {
    +      var ifr = goog.net.xpc.IframeRelayTransport.iframeRefs_.
    +          shift().iframeElement;
    +      goog.dom.removeNode(ifr);
    +      goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST,
    +          'iframe removed');
    +    }
    +
    +    goog.net.xpc.IframeRelayTransport.cleanupTimer_ = window.setTimeout(
    +        goog.net.xpc.IframeRelayTransport.cleanupCb_,
    +        goog.net.xpc.IframeRelayTransport.CLEANUP_INTERVAL_);
    +  };
    +
    +
    +  /**
    +   * Function which wraps cleanup_().
    +   * @private
    +   */
    +  goog.net.xpc.IframeRelayTransport.cleanupCb_ = function() {
    +    goog.net.xpc.IframeRelayTransport.cleanup_();
    +  };
    +}
    +
    +
    +/**
    + * Maximum sendable size of a payload via a single iframe in IE.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.IframeRelayTransport.IE_PAYLOAD_MAX_SIZE_ = 1800;
    +
    +
    +/**
    + * @typedef {{fragments: !Array<string>, received: number, expected: number}}
    + */
    +goog.net.xpc.IframeRelayTransport.FragmentInfo;
    +
    +
    +/**
    + * Used to track incoming payload fragments. The implementation can process
    + * incoming fragments from several channels at a time, even if data is
    + * out-of-order or interleaved.
    + *
    + * @type {!Object<string, !goog.net.xpc.IframeRelayTransport.FragmentInfo>}
    + * @private
    + */
    +goog.net.xpc.IframeRelayTransport.fragmentMap_ = {};
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @override
    + */
    +goog.net.xpc.IframeRelayTransport.prototype.transportType =
    +    goog.net.xpc.TransportTypes.IFRAME_RELAY;
    +
    +
    +/**
    + * Connects this transport.
    + * @override
    + */
    +goog.net.xpc.IframeRelayTransport.prototype.connect = function() {
    +  if (!this.getWindow()['xpcRelay']) {
    +    this.getWindow()['xpcRelay'] =
    +        goog.net.xpc.IframeRelayTransport.receiveMessage_;
    +  }
    +
    +  this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP);
    +};
    +
    +
    +/**
    + * Processes an incoming message.
    + *
    + * @param {string} channelName The name of the channel.
    + * @param {string} frame The raw frame content.
    + * @private
    + */
    +goog.net.xpc.IframeRelayTransport.receiveMessage_ =
    +    function(channelName, frame) {
    +  var pos = frame.indexOf(':');
    +  var header = frame.substr(0, pos);
    +  var payload = frame.substr(pos + 1);
    +
    +  if (!goog.userAgent.IE || (pos = header.indexOf('|')) == -1) {
    +    // First, the easy case.
    +    var service = header;
    +  } else {
    +    // There was a fragment id in the header, so this is a message
    +    // fragment, not a whole message.
    +    var service = header.substr(0, pos);
    +    var fragmentIdStr = header.substr(pos + 1);
    +
    +    // Separate the message id string and the fragment number. Note that
    +    // there may be a single leading + in the argument to parseInt, but
    +    // this is harmless.
    +    pos = fragmentIdStr.indexOf('+');
    +    var messageIdStr = fragmentIdStr.substr(0, pos);
    +    var fragmentNum = parseInt(fragmentIdStr.substr(pos + 1), 10);
    +    var fragmentInfo =
    +        goog.net.xpc.IframeRelayTransport.fragmentMap_[messageIdStr];
    +    if (!fragmentInfo) {
    +      fragmentInfo =
    +          goog.net.xpc.IframeRelayTransport.fragmentMap_[messageIdStr] =
    +          {fragments: [], received: 0, expected: 0};
    +    }
    +
    +    if (goog.string.contains(fragmentIdStr, '++')) {
    +      fragmentInfo.expected = fragmentNum + 1;
    +    }
    +    fragmentInfo.fragments[fragmentNum] = payload;
    +    fragmentInfo.received++;
    +
    +    if (fragmentInfo.received != fragmentInfo.expected) {
    +      return;
    +    }
    +
    +    // We've received all outstanding fragments; combine what we've received
    +    // into payload and fall out to the call to xpcDeliver.
    +    payload = fragmentInfo.fragments.join('');
    +    delete goog.net.xpc.IframeRelayTransport.fragmentMap_[messageIdStr];
    +  }
    +
    +  goog.net.xpc.channels[channelName].
    +      xpcDeliver(service, decodeURIComponent(payload));
    +};
    +
    +
    +/**
    + * Handles transport service messages (internal signalling).
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.IframeRelayTransport.prototype.transportServiceHandler =
    +    function(payload) {
    +  if (payload == goog.net.xpc.SETUP) {
    +    // TODO(user) Safari swallows the SETUP_ACK from the iframe to the
    +    // container after hitting reload.
    +    this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
    +    this.channel_.notifyConnected();
    +  }
    +  else if (payload == goog.net.xpc.SETUP_ACK_) {
    +    this.channel_.notifyConnected();
    +  }
    +};
    +
    +
    +/**
    + * Sends a message.
    + *
    + * @param {string} service Name of service this the message has to be delivered.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.IframeRelayTransport.prototype.send = function(service, payload) {
    +  // If we're on IE and the post-encoding payload is large, split it
    +  // into multiple payloads and send each one separately. Otherwise,
    +  // just send the whole thing.
    +  var encodedPayload = encodeURIComponent(payload);
    +  var encodedLen = encodedPayload.length;
    +  var maxSize = goog.net.xpc.IframeRelayTransport.IE_PAYLOAD_MAX_SIZE_;
    +
    +  if (goog.userAgent.IE && encodedLen > maxSize) {
    +    // A probabilistically-unique string used to link together all fragments
    +    // in this message.
    +    var messageIdStr = goog.string.getRandomString();
    +
    +    for (var startIndex = 0, fragmentNum = 0; startIndex < encodedLen;
    +         fragmentNum++) {
    +      var payloadFragment = encodedPayload.substr(startIndex, maxSize);
    +      startIndex += maxSize;
    +      var fragmentIdStr =
    +          messageIdStr + (startIndex >= encodedLen ? '++' : '+') + fragmentNum;
    +      this.send_(service, payloadFragment, fragmentIdStr);
    +    }
    +  } else {
    +    this.send_(service, encodedPayload);
    +  }
    +};
    +
    +
    +/**
    + * Sends an encoded message or message fragment.
    + * @param {string} service Name of service this the message has to be delivered.
    + * @param {string} encodedPayload The message content, URI encoded.
    + * @param {string=} opt_fragmentIdStr If sending a fragment, a string that
    + *     identifies the fragment.
    + * @private
    + */
    +goog.net.xpc.IframeRelayTransport.prototype.send_ =
    +    function(service, encodedPayload, opt_fragmentIdStr) {
    +  // IE requires that we create the onload attribute inline, otherwise the
    +  // handler is not triggered
    +  if (goog.userAgent.IE) {
    +    var div = this.getWindow().document.createElement('div');
    +    // TODO(user): It might be possible to set the sandbox attribute
    +    // to restrict the privileges of the created iframe.
    +    goog.dom.safe.setInnerHtml(div,
    +        goog.html.SafeHtml.createIframe(null, null, {
    +          'onload': goog.string.Const.from('this.xpcOnload()'),
    +          'sandbox': null
    +        }));
    +    var ifr = div.childNodes[0];
    +    div = null;
    +    ifr['xpcOnload'] = goog.net.xpc.IframeRelayTransport.iframeLoadHandler_;
    +  } else {
    +    var ifr = this.getWindow().document.createElement('iframe');
    +
    +    if (goog.userAgent.WEBKIT) {
    +      // safari doesn't fire load-events on iframes.
    +      // keep a reference and remove after a timeout.
    +      goog.net.xpc.IframeRelayTransport.iframeRefs_.push({
    +        timestamp: goog.now(),
    +        iframeElement: ifr
    +      });
    +    } else {
    +      goog.events.listen(ifr, 'load',
    +                         goog.net.xpc.IframeRelayTransport.iframeLoadHandler_);
    +    }
    +  }
    +
    +  var style = ifr.style;
    +  style.visibility = 'hidden';
    +  style.width = ifr.style.height = '0px';
    +  style.position = 'absolute';
    +
    +  var url = this.peerRelayUri_;
    +  url += '#' + this.channel_.name;
    +  if (this.peerIframeId_) {
    +    url += ',' + this.peerIframeId_;
    +  }
    +  url += '|' + service;
    +  if (opt_fragmentIdStr) {
    +    url += '|' + opt_fragmentIdStr;
    +  }
    +  url += ':' + encodedPayload;
    +
    +  ifr.src = url;
    +
    +  this.getWindow().document.body.appendChild(ifr);
    +
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST, 'msg sent: ' + url);
    +};
    +
    +
    +/**
    + * The iframe load handler. Gets called as method on the iframe element.
    + * @private
    + * @this Element
    + */
    +goog.net.xpc.IframeRelayTransport.iframeLoadHandler_ = function() {
    +  goog.log.log(goog.net.xpc.logger, goog.log.Level.FINEST, 'iframe-load');
    +  goog.dom.removeNode(this);
    +  this.xpcOnload = null;
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.IframeRelayTransport.prototype.disposeInternal = function() {
    +  goog.net.xpc.IframeRelayTransport.base(this, 'disposeInternal');
    +  if (goog.userAgent.WEBKIT) {
    +    goog.net.xpc.IframeRelayTransport.cleanup_(0);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport.js
    new file mode 100644
    index 0000000000..2a14ef0fad
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport.js
    @@ -0,0 +1,648 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the class which uses native messaging
    + * facilities for cross domain communication.
    + *
    + */
    +
    +
    +goog.provide('goog.net.xpc.NativeMessagingTransport');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.log');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +
    +
    +
    +/**
    + * The native messaging transport
    + *
    + * Uses document.postMessage() to send messages to other documents.
    + * Receiving is done by listening on 'message'-events on the document.
    + *
    + * @param {goog.net.xpc.CrossPageChannel} channel The channel this
    + *     transport belongs to.
    + * @param {string} peerHostname The hostname (protocol, domain, and port) of the
    + *     peer.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for
    + *     finding the correct window/document.
    + * @param {boolean=} opt_oneSidedHandshake If this is true, only the outer
    + *     transport sends a SETUP message and expects a SETUP_ACK.  The inner
    + *     transport goes connected when it receives the SETUP.
    + * @param {number=} opt_protocolVersion Which version of its setup protocol the
    + *     transport should use.  The default is '2'.
    + * @constructor
    + * @extends {goog.net.xpc.Transport}
    + * @final
    + */
    +goog.net.xpc.NativeMessagingTransport = function(channel, peerHostname,
    +    opt_domHelper, opt_oneSidedHandshake, opt_protocolVersion) {
    +  goog.net.xpc.NativeMessagingTransport.base(
    +      this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * Which version of the transport's protocol should be used.
    +   * @type {number}
    +   * @private
    +   */
    +  this.protocolVersion_ = opt_protocolVersion || 2;
    +  goog.asserts.assert(this.protocolVersion_ >= 1);
    +  goog.asserts.assert(this.protocolVersion_ <= 2);
    +
    +  /**
    +   * The hostname of the peer. This parameterizes all calls to postMessage, and
    +   * should contain the precise protocol, domain, and port of the peer window.
    +   * @type {string}
    +   * @private
    +   */
    +  this.peerHostname_ = peerHostname || '*';
    +
    +  /**
    +   * The event handler.
    +   * @type {!goog.events.EventHandler<!goog.net.xpc.NativeMessagingTransport>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Timer for connection reattempts.
    +   * @type {!goog.Timer}
    +   * @private
    +   */
    +  this.maybeAttemptToConnectTimer_ = new goog.Timer(100, this.getWindow());
    +
    +  /**
    +   * Whether one-sided handshakes are enabled.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.oneSidedHandshake_ = !!opt_oneSidedHandshake;
    +
    +  /**
    +   * Fires once we've received our SETUP_ACK message.
    +   * @type {!goog.async.Deferred}
    +   * @private
    +   */
    +  this.setupAckReceived_ = new goog.async.Deferred();
    +
    +  /**
    +   * Fires once we've sent our SETUP_ACK message.
    +   * @type {!goog.async.Deferred}
    +   * @private
    +   */
    +  this.setupAckSent_ = new goog.async.Deferred();
    +
    +  /**
    +   * Fires once we're marked connected.
    +   * @type {!goog.async.Deferred}
    +   * @private
    +   */
    +  this.connected_ = new goog.async.Deferred();
    +
    +  /**
    +   * The unique ID of this side of the connection. Used to determine when a peer
    +   * is reloaded.
    +   * @type {string}
    +   * @private
    +   */
    +  this.endpointId_ = goog.net.xpc.getRandomString(10);
    +
    +  /**
    +   * The unique ID of the peer. If we get a message from a peer with an ID we
    +   * don't expect, we reset the connection.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.peerEndpointId_ = null;
    +
    +  // We don't want to mark ourselves connected until we have sent whatever
    +  // message will cause our counterpart in the other frame to also declare
    +  // itself connected, if there is such a message.  Otherwise we risk a user
    +  // message being sent in advance of that message, and it being discarded.
    +  if (this.oneSidedHandshake_) {
    +    if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.INNER) {
    +      // One sided handshake, inner frame:
    +      // SETUP_ACK must be received.
    +      this.connected_.awaitDeferred(this.setupAckReceived_);
    +    } else {
    +      // One sided handshake, outer frame:
    +      // SETUP_ACK must be sent.
    +      this.connected_.awaitDeferred(this.setupAckSent_);
    +    }
    +  } else {
    +    // Two sided handshake:
    +    // SETUP_ACK has to have been received, and sent.
    +    this.connected_.awaitDeferred(this.setupAckReceived_);
    +    if (this.protocolVersion_ == 2) {
    +      this.connected_.awaitDeferred(this.setupAckSent_);
    +    }
    +  }
    +  this.connected_.addCallback(this.notifyConnected_, this);
    +  this.connected_.callback(true);
    +
    +  this.eventHandler_.
    +      listen(this.maybeAttemptToConnectTimer_, goog.Timer.TICK,
    +          this.maybeAttemptToConnect_);
    +
    +  goog.log.info(goog.net.xpc.logger, 'NativeMessagingTransport created.  ' +
    +      'protocolVersion=' + this.protocolVersion_ + ', oneSidedHandshake=' +
    +      this.oneSidedHandshake_ + ', role=' + this.channel_.getRole());
    +};
    +goog.inherits(goog.net.xpc.NativeMessagingTransport, goog.net.xpc.Transport);
    +
    +
    +/**
    + * Length of the delay in milliseconds between the channel being connected and
    + * the connection callback being called, in cases where coverage of timing flaws
    + * is required.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ = 200;
    +
    +
    +/**
    + * Current determination of peer's protocol version, or null for unknown.
    + * @type {?number}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.peerProtocolVersion_ = null;
    +
    +
    +/**
    + * Flag indicating if this instance of the transport has been initialized.
    + * @type {boolean}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.initialized_ = false;
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @override
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.transportType =
    +    goog.net.xpc.TransportTypes.NATIVE_MESSAGING;
    +
    +
    +/**
    + * The delimiter used for transport service messages.
    + * @type {string}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_ = ',';
    +
    +
    +/**
    + * Tracks the number of NativeMessagingTransport channels that have been
    + * initialized but not disposed yet in a map keyed by the UID of the window
    + * object.  This allows for multiple windows to be initiallized and listening
    + * for messages.
    + * @type {Object<number>}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.activeCount_ = {};
    +
    +
    +/**
    + * Id of a timer user during postMessage sends.
    + * @type {number}
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.sendTimerId_ = 0;
    +
    +
    +/**
    + * Checks whether the peer transport protocol version could be as indicated.
    + * @param {number} version The version to check for.
    + * @return {boolean} Whether the peer transport protocol version is as
    + *     indicated, or null.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.couldPeerVersionBe_ =
    +    function(version) {
    +  return this.peerProtocolVersion_ == null ||
    +      this.peerProtocolVersion_ == version;
    +};
    +
    +
    +/**
    + * Initializes this transport. Registers a listener for 'message'-events
    + * on the document.
    + * @param {Window} listenWindow The window to listen to events on.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.initialize_ = function(listenWindow) {
    +  var uid = goog.getUid(listenWindow);
    +  var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
    +  if (!goog.isNumber(value)) {
    +    value = 0;
    +  }
    +  if (value == 0) {
    +    // Listen for message-events. These are fired on window in FF3 and on
    +    // document in Opera.
    +    goog.events.listen(
    +        listenWindow.postMessage ? listenWindow : listenWindow.document,
    +        'message',
    +        goog.net.xpc.NativeMessagingTransport.messageReceived_,
    +        false,
    +        goog.net.xpc.NativeMessagingTransport);
    +  }
    +  goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value + 1;
    +};
    +
    +
    +/**
    + * Processes an incoming message-event.
    + * @param {goog.events.BrowserEvent} msgEvt The message event.
    + * @return {boolean} True if message was successfully delivered to a channel.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.messageReceived_ = function(msgEvt) {
    +  var data = msgEvt.getBrowserEvent().data;
    +
    +  if (!goog.isString(data)) {
    +    return false;
    +  }
    +
    +  var headDelim = data.indexOf('|');
    +  var serviceDelim = data.indexOf(':');
    +
    +  // make sure we got something reasonable
    +  if (headDelim == -1 || serviceDelim == -1) {
    +    return false;
    +  }
    +
    +  var channelName = data.substring(0, headDelim);
    +  var service = data.substring(headDelim + 1, serviceDelim);
    +  var payload = data.substring(serviceDelim + 1);
    +
    +  goog.log.fine(goog.net.xpc.logger,
    +      'messageReceived: channel=' + channelName +
    +      ', service=' + service + ', payload=' + payload);
    +
    +  // Attempt to deliver message to the channel. Keep in mind that it may not
    +  // exist for several reasons, including but not limited to:
    +  //  - a malformed message
    +  //  - the channel simply has not been created
    +  //  - channel was created in a different namespace
    +  //  - message was sent to the wrong window
    +  //  - channel has become stale (e.g. caching iframes and back clicks)
    +  var channel = goog.net.xpc.channels[channelName];
    +  if (channel) {
    +    channel.xpcDeliver(service, payload,
    +        /** @type {!MessageEvent} */ (msgEvt.getBrowserEvent()).origin);
    +    return true;
    +  }
    +
    +  var transportMessageType =
    +      goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload)[0];
    +
    +  // Check if there are any stale channel names that can be updated.
    +  for (var staleChannelName in goog.net.xpc.channels) {
    +    var staleChannel = goog.net.xpc.channels[staleChannelName];
    +    if (staleChannel.getRole() == goog.net.xpc.CrossPageChannelRole.INNER &&
    +        !staleChannel.isConnected() &&
    +        service == goog.net.xpc.TRANSPORT_SERVICE_ &&
    +        (transportMessageType == goog.net.xpc.SETUP ||
    +        transportMessageType == goog.net.xpc.SETUP_NTPV2)) {
    +      // Inner peer received SETUP message but channel names did not match.
    +      // Start using the channel name sent from outer peer. The channel name
    +      // of the inner peer can easily become out of date, as iframe's and their
    +      // JS state get cached in many browsers upon page reload or history
    +      // navigation (particularly Firefox 1.5+). We can trust the outer peer,
    +      // since we only accept postMessage messages from the same hostname that
    +      // originally setup the channel.
    +      staleChannel.updateChannelNameAndCatalog(channelName);
    +      staleChannel.xpcDeliver(service, payload);
    +      return true;
    +    }
    +  }
    +
    +  // Failed to find a channel to deliver this message to, so simply ignore it.
    +  goog.log.info(goog.net.xpc.logger, 'channel name mismatch; message ignored"');
    +  return false;
    +};
    +
    +
    +/**
    + * Handles transport service messages.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.transportServiceHandler =
    +    function(payload) {
    +  var transportParts =
    +      goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload);
    +  var transportMessageType = transportParts[0];
    +  var peerEndpointId = transportParts[1];
    +  switch (transportMessageType) {
    +    case goog.net.xpc.SETUP_ACK_:
    +      this.setPeerProtocolVersion_(1);
    +      if (!this.setupAckReceived_.hasFired()) {
    +        this.setupAckReceived_.callback(true);
    +      }
    +      break;
    +    case goog.net.xpc.SETUP_ACK_NTPV2:
    +      if (this.protocolVersion_ == 2) {
    +        this.setPeerProtocolVersion_(2);
    +        if (!this.setupAckReceived_.hasFired()) {
    +          this.setupAckReceived_.callback(true);
    +        }
    +      }
    +      break;
    +    case goog.net.xpc.SETUP:
    +      this.setPeerProtocolVersion_(1);
    +      this.sendSetupAckMessage_(1);
    +      break;
    +    case goog.net.xpc.SETUP_NTPV2:
    +      if (this.protocolVersion_ == 2) {
    +        var prevPeerProtocolVersion = this.peerProtocolVersion_;
    +        this.setPeerProtocolVersion_(2);
    +        this.sendSetupAckMessage_(2);
    +        if ((prevPeerProtocolVersion == 1 || this.peerEndpointId_ != null) &&
    +            this.peerEndpointId_ != peerEndpointId) {
    +          // Send a new SETUP message since the peer has been replaced.
    +          goog.log.info(goog.net.xpc.logger,
    +              'Sending SETUP and changing peer ID to: ' + peerEndpointId);
    +          this.sendSetupMessage_();
    +        }
    +        this.peerEndpointId_ = peerEndpointId;
    +      }
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Sends a SETUP transport service message of the correct protocol number for
    + * our current situation.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.sendSetupMessage_ =
    +    function() {
    +  // 'real' (legacy) v1 transports don't know about there being v2 ones out
    +  // there, and we shouldn't either.
    +  goog.asserts.assert(!(this.protocolVersion_ == 1 &&
    +      this.peerProtocolVersion_ == 2));
    +
    +  if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2)) {
    +    var payload = goog.net.xpc.SETUP_NTPV2;
    +    payload += goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_;
    +    payload += this.endpointId_;
    +    this.send(goog.net.xpc.TRANSPORT_SERVICE_, payload);
    +  }
    +
    +  // For backward compatibility reasons, the V1 SETUP message can be sent by
    +  // both V1 and V2 transports.  Once a V2 transport has 'heard' another V2
    +  // transport it starts ignoring V1 messages, so the V2 message must be sent
    +  // first.
    +  if (this.couldPeerVersionBe_(1)) {
    +    this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP);
    +  }
    +};
    +
    +
    +/**
    + * Sends a SETUP_ACK transport service message of the correct protocol number
    + * for our current situation.
    + * @param {number} protocolVersion The protocol version of the SETUP message
    + *     which gave rise to this ack message.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.sendSetupAckMessage_ =
    +    function(protocolVersion) {
    +  goog.asserts.assert(this.protocolVersion_ != 1 || protocolVersion != 2,
    +      'Shouldn\'t try to send a v2 setup ack in v1 mode.');
    +  if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2) &&
    +      protocolVersion == 2) {
    +    this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_NTPV2);
    +  } else if (this.couldPeerVersionBe_(1) && protocolVersion == 1) {
    +    this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
    +  } else {
    +    return;
    +  }
    +
    +  if (!this.setupAckSent_.hasFired()) {
    +    this.setupAckSent_.callback(true);
    +  }
    +};
    +
    +
    +/**
    + * Attempts to set the peer protocol number.  Downgrades from 2 to 1 are not
    + * permitted.
    + * @param {number} version The new protocol number.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.setPeerProtocolVersion_ =
    +    function(version) {
    +  if (version > this.peerProtocolVersion_) {
    +    this.peerProtocolVersion_ = version;
    +  }
    +  if (this.peerProtocolVersion_ == 1) {
    +    if (!this.setupAckSent_.hasFired() && !this.oneSidedHandshake_) {
    +      this.setupAckSent_.callback(true);
    +    }
    +    this.peerEndpointId_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Connects this transport.
    + * @override
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.connect = function() {
    +  goog.net.xpc.NativeMessagingTransport.initialize_(this.getWindow());
    +  this.initialized_ = true;
    +  this.maybeAttemptToConnect_();
    +};
    +
    +
    +/**
    + * Connects to other peer. In the case of the outer peer, the setup messages are
    + * likely sent before the inner peer is ready to receive them. Therefore, this
    + * function will continue trying to send the SETUP message until the inner peer
    + * responds. In the case of the inner peer, it will occasionally have its
    + * channel name fall out of sync with the outer peer, particularly during
    + * soft-reloads and history navigations.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.maybeAttemptToConnect_ =
    +    function() {
    +  // In a one-sided handshake, the outer frame does not send a SETUP message,
    +  // but the inner frame does.
    +  var outerFrame = this.channel_.getRole() ==
    +      goog.net.xpc.CrossPageChannelRole.OUTER;
    +  if ((this.oneSidedHandshake_ && outerFrame) ||
    +      this.channel_.isConnected() ||
    +      this.isDisposed()) {
    +    this.maybeAttemptToConnectTimer_.stop();
    +    return;
    +  }
    +  this.maybeAttemptToConnectTimer_.start();
    +  this.sendSetupMessage_();
    +};
    +
    +
    +/**
    + * Sends a message.
    + * @param {string} service The name off the service the message is to be
    + * delivered to.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.send = function(service,
    +                                                                payload) {
    +  var win = this.channel_.getPeerWindowObject();
    +  if (!win) {
    +    goog.log.fine(goog.net.xpc.logger, 'send(): window not ready');
    +    return;
    +  }
    +
    +  this.send = function(service, payload) {
    +    // In IE8 (and perhaps elsewhere), it seems like postMessage is sometimes
    +    // implemented as a synchronous call.  That is, calling it synchronously
    +    // calls whatever listeners it has, and control is not returned to the
    +    // calling thread until those listeners are run.  This produces different
    +    // ordering to all other browsers, and breaks this protocol.  This timer
    +    // callback is introduced to produce standard behavior across all browsers.
    +    var transport = this;
    +    var channelName = this.channel_.name;
    +    var sendFunctor = function() {
    +      transport.sendTimerId_ = 0;
    +
    +      try {
    +        // postMessage is a method of the window object, except in some
    +        // versions of Opera, where it is a method of the document object.  It
    +        // also seems that the appearance of postMessage on the peer window
    +        // object can sometimes be delayed.
    +        var obj = win.postMessage ? win : win.document;
    +        if (!obj.postMessage) {
    +          goog.log.warning(goog.net.xpc.logger,
    +              'Peer window had no postMessage function.');
    +          return;
    +        }
    +
    +        obj.postMessage(channelName + '|' + service + ':' + payload,
    +            transport.peerHostname_);
    +        goog.log.fine(goog.net.xpc.logger, 'send(): service=' + service +
    +            ' payload=' + payload + ' to hostname=' + transport.peerHostname_);
    +      } catch (error) {
    +        // There is some evidence (not totally convincing) that postMessage can
    +        // be missing or throw errors during a narrow timing window during
    +        // startup.  This protects against that.
    +        goog.log.warning(goog.net.xpc.logger,
    +            'Error performing postMessage, ignoring.', error);
    +      }
    +    };
    +    this.sendTimerId_ = goog.Timer.callOnce(sendFunctor, 0);
    +  };
    +  this.send(service, payload);
    +};
    +
    +
    +/**
    + * Notify the channel that this transport is connected.  If either transport is
    + * protocol v1, a short delay is required to paper over timing vulnerabilities
    + * in that protocol version.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.prototype.notifyConnected_ =
    +    function() {
    +  var delay = (this.protocolVersion_ == 1 || this.peerProtocolVersion_ == 1) ?
    +      goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ : undefined;
    +  this.channel_.notifyConnected(delay);
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.NativeMessagingTransport.prototype.disposeInternal = function() {
    +  if (this.initialized_) {
    +    var listenWindow = this.getWindow();
    +    var uid = goog.getUid(listenWindow);
    +    var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
    +    goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value - 1;
    +    if (value == 1) {
    +      goog.events.unlisten(
    +          listenWindow.postMessage ? listenWindow : listenWindow.document,
    +          'message',
    +          goog.net.xpc.NativeMessagingTransport.messageReceived_,
    +          false,
    +          goog.net.xpc.NativeMessagingTransport);
    +    }
    +  }
    +
    +  if (this.sendTimerId_) {
    +    goog.Timer.clear(this.sendTimerId_);
    +    this.sendTimerId_ = 0;
    +  }
    +
    +  goog.dispose(this.eventHandler_);
    +  delete this.eventHandler_;
    +
    +  goog.dispose(this.maybeAttemptToConnectTimer_);
    +  delete this.maybeAttemptToConnectTimer_;
    +
    +  this.setupAckReceived_.cancel();
    +  delete this.setupAckReceived_;
    +  this.setupAckSent_.cancel();
    +  delete this.setupAckSent_;
    +  this.connected_.cancel();
    +  delete this.connected_;
    +
    +  // Cleaning up this.send as it is an instance method, created in
    +  // goog.net.xpc.NativeMessagingTransport.prototype.send and has a closure over
    +  // this.channel_.peerWindowObject_.
    +  delete this.send;
    +
    +  goog.net.xpc.NativeMessagingTransport.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Parse a transport service payload message.  For v1, it is simply expected to
    + * be 'SETUP' or 'SETUP_ACK'.  For v2, an example setup message is
    + * 'SETUP_NTPV2,abc123', where the second part is the endpoint id.  The v2 setup
    + * ack message is simply 'SETUP_ACK_NTPV2'.
    + * @param {string} payload The payload.
    + * @return {!Array<?string>} An array with the message type as the first member
    + *     and the endpoint id as the second, if one was sent, or null otherwise.
    + * @private
    + */
    +goog.net.xpc.NativeMessagingTransport.parseTransportPayload_ =
    +    function(payload) {
    +  var transportParts = /** @type {!Array<?string>} */ (payload.split(
    +      goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_));
    +  transportParts[1] = transportParts[1] || null;
    +  return transportParts;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.html b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.html
    new file mode 100644
    index 0000000000..4edcb262c1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<!--
    +-->
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   NativeMessagingTransport Unit-Tests
    +  </title>
    +  <script src="../../base.js" type="text/javascript">
    +  </script>
    +  <script>
    +    goog.require('goog.net.xpc.NativeMessagingTransportTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.js b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.js
    new file mode 100644
    index 0000000000..cc904c9980
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/nativemessagingtransport_test.js
    @@ -0,0 +1,300 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.net.xpc.NativeMessagingTransportTest');
    +goog.setTestOnly('goog.net.xpc.NativeMessagingTransportTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannel');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.NativeMessagingTransport');
    +goog.require('goog.testing.jsunit');
    +
    +// This test only tests the native messaing transport protocol version 2.
    +// Testing of previous versions and of backward/forward compatibility is done
    +// in crosspagechannel_test.html.
    +
    +
    +function tearDown() {
    +  goog.net.xpc.NativeMessagingTransport.activeCount_ = {};
    +  goog.events.removeAll(window.postMessage ? window : document, 'message');
    +}
    +
    +
    +function testConstructor() {
    +  var xpc = getTestChannel();
    +
    +  var t = new goog.net.xpc.NativeMessagingTransport(xpc, 'http://g.com:80',
    +      undefined /* opt_domHelper */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  assertEquals('http://g.com:80', t.peerHostname_);
    +
    +  var t = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, undefined /* opt_domHelper */,
    +      false /* opt_oneSidedHandshake */, 2 /* opt_protocolVersion */);
    +  assertEquals('*', t.peerHostname_);
    +  t.dispose();
    +}
    +
    +
    +function testConstructorDom() {
    +  var xpc = getTestChannel();
    +
    +  var t = new goog.net.xpc.NativeMessagingTransport(
    +      xpc, 'http://g.com:80', goog.dom.getDomHelper(),
    +      false /* opt_oneSidedHandshake */, 2 /* opt_protocolVersion */);
    +  assertEquals('http://g.com:80', t.peerHostname_);
    +
    +  var t = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  assertEquals('*', t.peerHostname_);
    +  t.dispose();
    +}
    +
    +
    +function testDispose() {
    +  var xpc = getTestChannel();
    +  var listenedObj = window.postMessage ? window : document;
    +
    +  var t0 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +  t0.dispose();
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +
    +  var t1 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  t1.connect();
    +  t1.dispose();
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +
    +  var t2 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  var t3 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  t2.connect();
    +  t3.connect();
    +  t2.dispose();
    +  assertEquals(1, goog.events.removeAll(listenedObj, 'message'));
    +}
    +
    +
    +function testDisposeWithDom() {
    +  var xpc = getTestChannel(goog.dom.getDomHelper());
    +  var listenedObj = window.postMessage ? window : document;
    +
    +  var t0 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +  t0.dispose();
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +
    +  var t1 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  t1.connect();
    +  t1.dispose();
    +  assertEquals(0, goog.events.removeAll(listenedObj, 'message'));
    +
    +  var t2 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  var t3 = new goog.net.xpc.NativeMessagingTransport(xpc,
    +      null /* peerHostName */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +  t2.connect();
    +  t3.connect();
    +  t2.dispose();
    +  assertEquals(1, goog.events.removeAll(listenedObj, 'message'));
    +}
    +
    +
    +function testBogusMessages() {
    +  var e = createMockEvent('bogus_message');
    +  assertFalse(goog.net.xpc.NativeMessagingTransport.messageReceived_(e));
    +
    +  e = createMockEvent('bogus|message');
    +  assertFalse(goog.net.xpc.NativeMessagingTransport.messageReceived_(e));
    +
    +  e = createMockEvent('bogus|message:data');
    +  assertFalse(goog.net.xpc.NativeMessagingTransport.messageReceived_(e));
    +}
    +
    +
    +function testSendingMessagesToUnconnectedInnerPeer() {
    +  var xpc = getTestChannel();
    +
    +  var serviceResult, payloadResult;
    +  xpc.xpcDeliver = function(service, payload) {
    +    serviceResult = service;
    +    payloadResult = payload;
    +  };
    +
    +  // Construct an unconnected inner peer.
    +  xpc.getRole = function() {
    +    return goog.net.xpc.CrossPageChannelRole.INNER;
    +  };
    +  xpc.isConnected = function() {
    +    return false;
    +  };
    +  var t = new goog.net.xpc.NativeMessagingTransport(xpc, 'http://g.com',
    +      false /* opt_oneSidedHandshake */, 2 /* opt_protocolVersion */);
    +
    +  // Test a valid message.
    +  var e = createMockEvent('test_channel|test_service:test_payload');
    +  assertTrue(goog.net.xpc.NativeMessagingTransport.messageReceived_(e));
    +  assertEquals('test_service', serviceResult);
    +  assertEquals('test_payload', payloadResult);
    +  assertEquals('Ensure channel name has not been changed.',
    +               'test_channel',
    +               t.channel_.name);
    +
    +  // Test updating a stale inner peer.
    +  var e = createMockEvent('new_channel|tp:SETUP');
    +  assertTrue(goog.net.xpc.NativeMessagingTransport.messageReceived_(e));
    +  assertEquals('tp', serviceResult);
    +  assertEquals('SETUP', payloadResult);
    +  assertEquals('Ensure channel name has been updated.',
    +               'new_channel',
    +               t.channel_.name);
    +  t.dispose();
    +}
    +
    +
    +function testSignalConnected_innerFrame() {
    +  checkSignalConnected(false /* oneSidedHandshake */,
    +      true /* innerFrame */);
    +}
    +
    +
    +function testSignalConnected_outerFrame() {
    +  checkSignalConnected(false /* oneSidedHandshake */,
    +      false /* innerFrame */);
    +}
    +
    +
    +function testSignalConnected_singleSided_innerFrame() {
    +  checkSignalConnected(true /* oneSidedHandshake */,
    +      true /* innerFrame */);
    +}
    +
    +
    +function testSignalConnected_singleSided_outerFrame() {
    +  checkSignalConnected(true /* oneSidedHandshake */,
    +      false /* innerFrame */);
    +}
    +
    +
    +function checkSignalConnected(oneSidedHandshake, innerFrame,
    +    peerProtocolVersion, protocolVersion) {
    +  var xpc = getTestChannel();
    +  var connected = false;
    +  xpc.notifyConnected = function() {
    +    if (connected) {
    +      fail();
    +    } else {
    +      connected = true;
    +    }
    +  };
    +  xpc.getRole = function() {
    +    return innerFrame ? goog.net.xpc.CrossPageChannelRole.INNER :
    +        goog.net.xpc.CrossPageChannelRole.OUTER;
    +  };
    +  xpc.isConnected = function() {
    +    return false;
    +  };
    +
    +  var transport = new goog.net.xpc.NativeMessagingTransport(xpc, 'http://g.com',
    +      undefined /* opt_domHelper */,
    +      oneSidedHandshake /* opt_oneSidedHandshake */,
    +      2 /* protocolVerion */);
    +  var sentPayloads = [];
    +  transport.send = function(service, payload) {
    +    assertEquals(goog.net.xpc.TRANSPORT_SERVICE_, service);
    +    sentPayloads.push(payload);
    +  };
    +  function assertSent(payloads) {
    +    assertArrayEquals(payloads, sentPayloads);
    +    sentPayloads = [];
    +  }
    +  var endpointId = transport.endpointId_;
    +  var peerEndpointId1 = 'abc123';
    +  var peerEndpointId2 = 'def234';
    +
    +  assertFalse(connected);
    +  if (!oneSidedHandshake || innerFrame) {
    +    transport.transportServiceHandler(goog.net.xpc.SETUP_NTPV2 + ',' +
    +        peerEndpointId1);
    +    transport.transportServiceHandler(goog.net.xpc.SETUP);
    +    assertSent([goog.net.xpc.SETUP_ACK_NTPV2]);
    +    assertFalse(connected);
    +    transport.transportServiceHandler(goog.net.xpc.SETUP_ACK_NTPV2);
    +    assertSent([]);
    +    assertTrue(connected);
    +  } else {
    +    transport.transportServiceHandler(goog.net.xpc.SETUP_ACK_NTPV2);
    +    assertSent([]);
    +    assertFalse(connected);
    +    transport.transportServiceHandler(goog.net.xpc.SETUP_NTPV2 + ',' +
    +        peerEndpointId1);
    +    transport.transportServiceHandler(goog.net.xpc.SETUP);
    +    assertSent([goog.net.xpc.SETUP_ACK_NTPV2]);
    +    assertTrue(connected);
    +  }
    +
    +  // Verify that additional transport service traffic doesn't cause duplicate
    +  // notifications.
    +  transport.transportServiceHandler(goog.net.xpc.SETUP_NTPV2 + ',' +
    +      peerEndpointId1);
    +  transport.transportServiceHandler(goog.net.xpc.SETUP);
    +  assertSent([goog.net.xpc.SETUP_ACK_NTPV2]);
    +  transport.transportServiceHandler(goog.net.xpc.SETUP_ACK_NTPV2);
    +  assertSent([]);
    +
    +  // Simulate a reconnection by sending a SETUP message from a frame with a
    +  // different endpoint id.  No further connection callbacks should fire, but
    +  // a new SETUP message should be triggered.
    +  transport.transportServiceHandler(goog.net.xpc.SETUP_NTPV2 + ',' +
    +      peerEndpointId2);
    +  transport.transportServiceHandler(goog.net.xpc.SETUP);
    +  assertSent([goog.net.xpc.SETUP_ACK_NTPV2, goog.net.xpc.SETUP_NTPV2 + ',' +
    +        endpointId]);
    +  transport.transportServiceHandler(goog.net.xpc.SETUP_ACK_NTPV2);
    +  assertSent([]);
    +}
    +
    +
    +function createMockEvent(data) {
    +  var event = {};
    +  event.getBrowserEvent = function() { return {data: data} };
    +  return event;
    +}
    +
    +
    +function getTestChannel(opt_domHelper) {
    +  var cfg = {};
    +  cfg[goog.net.xpc.CfgFields.CHANNEL_NAME] = 'test_channel';
    +  return new goog.net.xpc.CrossPageChannel(cfg, opt_domHelper,
    +      undefined /* opt_domHelper */, false /* opt_oneSidedHandshake */,
    +      2 /* opt_protocolVersion */);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/nixtransport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/nixtransport.js
    new file mode 100644
    index 0000000000..a7639c0c49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/nixtransport.js
    @@ -0,0 +1,483 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the NIX (Native IE XDC) method transport for
    + * cross-domain communication. It exploits the fact that Internet Explorer
    + * allows a window that is the parent of an iframe to set said iframe window's
    + * opener property to an object. This object can be a function that in turn
    + * can be used to send a message despite same-origin constraints. Note that
    + * this function, if a pure JavaScript object, opens up the possibilitiy of
    + * gaining a hold of the context of the other window and in turn, attacking
    + * it. This implementation therefore wraps the JavaScript objects used inside
    + * a VBScript class. Since VBScript objects are passed in JavaScript as a COM
    + * wrapper (like DOM objects), they are thus opaque to JavaScript
    + * (except for the interface they expose). This therefore provides a safe
    + * method of transport.
    + *
    + *
    + * Initially based on FrameElementTransport which shares some similarities
    + * to this method.
    + */
    +
    +goog.provide('goog.net.xpc.NixTransport');
    +
    +goog.require('goog.log');
    +goog.require('goog.net.xpc');
    +goog.require('goog.net.xpc.CfgFields');
    +goog.require('goog.net.xpc.CrossPageChannelRole');
    +goog.require('goog.net.xpc.Transport');
    +goog.require('goog.net.xpc.TransportTypes');
    +goog.require('goog.reflect');
    +
    +
    +
    +/**
    + * NIX method transport.
    + *
    + * NOTE(user): NIX method tested in all IE versions starting from 6.0.
    + *
    + * @param {goog.net.xpc.CrossPageChannel} channel The channel this transport
    + *     belongs to.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
    + *     the correct window.
    + * @constructor
    + * @extends {goog.net.xpc.Transport}
    + * @final
    + */
    +goog.net.xpc.NixTransport = function(channel, opt_domHelper) {
    +  goog.net.xpc.NixTransport.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The channel this transport belongs to.
    +   * @type {goog.net.xpc.CrossPageChannel}
    +   * @private
    +   */
    +  this.channel_ = channel;
    +
    +  /**
    +   * The authorization token, if any, used by this transport.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.authToken_ = channel[goog.net.xpc.CfgFields.AUTH_TOKEN] || '';
    +
    +  /**
    +   * The authorization token, if any, that must be sent by the other party
    +   * for setup to occur.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.remoteAuthToken_ =
    +      channel[goog.net.xpc.CfgFields.REMOTE_AUTH_TOKEN] || '';
    +
    +  // Conduct the setup work for NIX in general, if need be.
    +  goog.net.xpc.NixTransport.conductGlobalSetup_(this.getWindow());
    +
    +  // Setup aliases so that VBScript can call these methods
    +  // on the transport class, even if they are renamed during
    +  // compression.
    +  this[goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE] = this.handleMessage_;
    +  this[goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL] = this.createChannel_;
    +};
    +goog.inherits(goog.net.xpc.NixTransport, goog.net.xpc.Transport);
    +
    +
    +// Consts for NIX. VBScript doesn't allow items to start with _ for some
    +// reason, so we need to make these names quite unique, as they will go into
    +// the global namespace.
    +
    +
    +/**
    + * Global name of the Wrapper VBScript class.
    + * Note that this class will be stored in the *global*
    + * namespace (i.e. window in browsers).
    + * @type {string}
    + */
    +goog.net.xpc.NixTransport.NIX_WRAPPER = 'GCXPC____NIXVBS_wrapper';
    +
    +
    +/**
    + * Global name of the GetWrapper VBScript function. This
    + * constant is used by JavaScript to call this function.
    + * Note that this function will be stored in the *global*
    + * namespace (i.e. window in browsers).
    + * @type {string}
    + */
    +goog.net.xpc.NixTransport.NIX_GET_WRAPPER = 'GCXPC____NIXVBS_get_wrapper';
    +
    +
    +/**
    + * The name of the handle message method used by the wrapper class
    + * when calling the transport.
    + * @type {string}
    + */
    +goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE = 'GCXPC____NIXJS_handle_message';
    +
    +
    +/**
    + * The name of the create channel method used by the wrapper class
    + * when calling the transport.
    + * @type {string}
    + */
    +goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL = 'GCXPC____NIXJS_create_channel';
    +
    +
    +/**
    + * A "unique" identifier that is stored in the wrapper
    + * class so that the wrapper can be distinguished from
    + * other objects easily.
    + * @type {string}
    + */
    +goog.net.xpc.NixTransport.NIX_ID_FIELD = 'GCXPC____NIXVBS_container';
    +
    +
    +/**
    + * Determines if the installed version of IE supports accessing window.opener
    + * after it has been set to a non-Window/null value. NIX relies on this being
    + * possible.
    + * @return {boolean} Whether window.opener behavior is compatible with NIX.
    + */
    +goog.net.xpc.NixTransport.isNixSupported = function() {
    +  var isSupported = false;
    +  try {
    +    var oldOpener = window.opener;
    +    // The compiler complains (as it should!) if we set window.opener to
    +    // something other than a window or null.
    +    window.opener = /** @type {Window} */ ({});
    +    isSupported = goog.reflect.canAccessProperty(window, 'opener');
    +    window.opener = oldOpener;
    +  } catch (e) { }
    +  return isSupported;
    +};
    +
    +
    +/**
    + * Conducts the global setup work for the NIX transport method.
    + * This function creates and then injects into the page the
    + * VBScript code necessary to create the NIX wrapper class.
    + * Note that this method can be called multiple times, as
    + * it internally checks whether the work is necessary before
    + * proceeding.
    + * @param {Window} listenWindow The window containing the affected page.
    + * @private
    + */
    +goog.net.xpc.NixTransport.conductGlobalSetup_ = function(listenWindow) {
    +  if (listenWindow['nix_setup_complete']) {
    +    return;
    +  }
    +
    +  // Inject the VBScript code needed.
    +  var vbscript =
    +      // We create a class to act as a wrapper for
    +      // a Javascript call, to prevent a break in of
    +      // the context.
    +      'Class ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n ' +
    +
    +      // An internal member for keeping track of the
    +      // transport for which this wrapper exists.
    +      'Private m_Transport\n' +
    +
    +      // An internal member for keeping track of the
    +      // auth token associated with the context that
    +      // created this wrapper. Used for validation
    +      // purposes.
    +      'Private m_Auth\n' +
    +
    +      // Method for internally setting the value
    +      // of the m_Transport property. We have the
    +      // isEmpty check to prevent the transport
    +      // from being overridden with an illicit
    +      // object by a malicious party.
    +      'Public Sub SetTransport(transport)\n' +
    +      'If isEmpty(m_Transport) Then\n' +
    +      'Set m_Transport = transport\n' +
    +      'End If\n' +
    +      'End Sub\n' +
    +
    +      // Method for internally setting the value
    +      // of the m_Auth property. We have the
    +      // isEmpty check to prevent the transport
    +      // from being overridden with an illicit
    +      // object by a malicious party.
    +      'Public Sub SetAuth(auth)\n' +
    +      'If isEmpty(m_Auth) Then\n' +
    +      'm_Auth = auth\n' +
    +      'End If\n' +
    +      'End Sub\n' +
    +
    +      // Returns the auth token to the gadget, so it can
    +      // confirm a match before initiating the connection
    +      'Public Function GetAuthToken()\n ' +
    +      'GetAuthToken = m_Auth\n' +
    +      'End Function\n' +
    +
    +      // A wrapper method which causes a
    +      // message to be sent to the other context.
    +      'Public Sub SendMessage(service, payload)\n ' +
    +      'Call m_Transport.' +
    +      goog.net.xpc.NixTransport.NIX_HANDLE_MESSAGE + '(service, payload)\n' +
    +      'End Sub\n' +
    +
    +      // Method for setting up the inner->outer
    +      // channel.
    +      'Public Sub CreateChannel(channel)\n ' +
    +      'Call m_Transport.' +
    +      goog.net.xpc.NixTransport.NIX_CREATE_CHANNEL + '(channel)\n' +
    +      'End Sub\n' +
    +
    +      // An empty field with a unique identifier to
    +      // prevent the code from confusing this wrapper
    +      // with a run-of-the-mill value found in window.opener.
    +      'Public Sub ' + goog.net.xpc.NixTransport.NIX_ID_FIELD + '()\n ' +
    +      'End Sub\n' +
    +      'End Class\n ' +
    +
    +      // Function to get a reference to the wrapper.
    +      'Function ' +
    +      goog.net.xpc.NixTransport.NIX_GET_WRAPPER + '(transport, auth)\n' +
    +      'Dim wrap\n' +
    +      'Set wrap = New ' + goog.net.xpc.NixTransport.NIX_WRAPPER + '\n' +
    +      'wrap.SetTransport transport\n' +
    +      'wrap.SetAuth auth\n' +
    +      'Set ' + goog.net.xpc.NixTransport.NIX_GET_WRAPPER + ' = wrap\n' +
    +      'End Function';
    +
    +  try {
    +    listenWindow.execScript(vbscript, 'vbscript');
    +    listenWindow['nix_setup_complete'] = true;
    +  }
    +  catch (e) {
    +    goog.log.error(goog.net.xpc.logger,
    +        'exception caught while attempting global setup: ' + e);
    +  }
    +};
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @protected
    + * @override
    + */
    +goog.net.xpc.NixTransport.prototype.transportType =
    +    goog.net.xpc.TransportTypes.NIX;
    +
    +
    +/**
    + * Keeps track of whether the local setup has completed (i.e.
    + * the initial work towards setting the channel up has been
    + * completed for this end).
    + * @type {boolean}
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.localSetupCompleted_ = false;
    +
    +
    +/**
    + * The NIX channel used to talk to the other page. This
    + * object is in fact a reference to a VBScript class
    + * (see above) and as such, is in fact a COM wrapper.
    + * When using this object, make sure to not access methods
    + * without calling them, otherwise a COM error will be thrown.
    + * @type {Object}
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.nixChannel_ = null;
    +
    +
    +/**
    + * Connect this transport.
    + * @override
    + */
    +goog.net.xpc.NixTransport.prototype.connect = function() {
    +  if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
    +    this.attemptOuterSetup_();
    +  } else {
    +    this.attemptInnerSetup_();
    +  }
    +};
    +
    +
    +/**
    + * Attempts to setup the channel from the perspective
    + * of the outer (read: container) page. This method
    + * will attempt to create a NIX wrapper for this transport
    + * and place it into the "opener" property of the inner
    + * page's window object. If it fails, it will continue
    + * to loop until it does so.
    + *
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.attemptOuterSetup_ = function() {
    +  if (this.localSetupCompleted_) {
    +    return;
    +  }
    +
    +  // Get shortcut to iframe-element that contains the inner
    +  // page.
    +  var innerFrame = this.channel_.getIframeElement();
    +
    +  try {
    +    // Attempt to place the NIX wrapper object into the inner
    +    // frame's opener property.
    +    var theWindow = this.getWindow();
    +    var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
    +    innerFrame.contentWindow.opener = getWrapper(this, this.authToken_);
    +    this.localSetupCompleted_ = true;
    +  }
    +  catch (e) {
    +    goog.log.error(goog.net.xpc.logger,
    +        'exception caught while attempting setup: ' + e);
    +  }
    +
    +  // If the retry is necessary, reattempt this setup.
    +  if (!this.localSetupCompleted_) {
    +    this.getWindow().setTimeout(goog.bind(this.attemptOuterSetup_, this), 100);
    +  }
    +};
    +
    +
    +/**
    + * Attempts to setup the channel from the perspective
    + * of the inner (read: iframe) page. This method
    + * will attempt to *read* the opener object from the
    + * page's opener property. If it succeeds, this object
    + * is saved into nixChannel_ and the channel is confirmed
    + * with the container by calling CreateChannel with an instance
    + * of a wrapper for *this* page. Note that if this method
    + * fails, it will continue to loop until it succeeds.
    + *
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.attemptInnerSetup_ = function() {
    +  if (this.localSetupCompleted_) {
    +    return;
    +  }
    +
    +  try {
    +    var opener = this.getWindow().opener;
    +
    +    // Ensure that the object contained inside the opener
    +    // property is in fact a NIX wrapper.
    +    if (opener && goog.net.xpc.NixTransport.NIX_ID_FIELD in opener) {
    +      this.nixChannel_ = opener;
    +
    +      // Ensure that the NIX channel given to use is valid.
    +      var remoteAuthToken = this.nixChannel_['GetAuthToken']();
    +
    +      if (remoteAuthToken != this.remoteAuthToken_) {
    +        goog.log.error(goog.net.xpc.logger,
    +            'Invalid auth token from other party');
    +        return;
    +      }
    +
    +      // Complete the construction of the channel by sending our own
    +      // wrapper to the container via the channel they gave us.
    +      var theWindow = this.getWindow();
    +      var getWrapper = theWindow[goog.net.xpc.NixTransport.NIX_GET_WRAPPER];
    +      this.nixChannel_['CreateChannel'](getWrapper(this, this.authToken_));
    +
    +      this.localSetupCompleted_ = true;
    +
    +      // Notify channel that the transport is ready.
    +      this.channel_.notifyConnected();
    +    }
    +  }
    +  catch (e) {
    +    goog.log.error(goog.net.xpc.logger,
    +        'exception caught while attempting setup: ' + e);
    +    return;
    +  }
    +
    +  // If the retry is necessary, reattempt this setup.
    +  if (!this.localSetupCompleted_) {
    +    this.getWindow().setTimeout(goog.bind(this.attemptInnerSetup_, this), 100);
    +  }
    +};
    +
    +
    +/**
    + * Internal method called by the inner page, via the
    + * NIX wrapper, to complete the setup of the channel.
    + *
    + * @param {Object} channel The NIX wrapper of the
    + *  inner page.
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.createChannel_ = function(channel) {
    +  // Verify that the channel is in fact a NIX wrapper.
    +  if (typeof channel != 'unknown' ||
    +      !(goog.net.xpc.NixTransport.NIX_ID_FIELD in channel)) {
    +    goog.log.error(goog.net.xpc.logger,
    +        'Invalid NIX channel given to createChannel_');
    +  }
    +
    +  this.nixChannel_ = channel;
    +
    +  // Ensure that the NIX channel given to use is valid.
    +  var remoteAuthToken = this.nixChannel_['GetAuthToken']();
    +
    +  if (remoteAuthToken != this.remoteAuthToken_) {
    +    goog.log.error(goog.net.xpc.logger, 'Invalid auth token from other party');
    +    return;
    +  }
    +
    +  // Indicate to the CrossPageChannel that the channel is setup
    +  // and ready to use.
    +  this.channel_.notifyConnected();
    +};
    +
    +
    +/**
    + * Internal method called by the other page, via the NIX wrapper,
    + * to deliver a message.
    + * @param {string} serviceName The name of the service the message is to be
    + *   delivered to.
    + * @param {string} payload The message to process.
    + * @private
    + */
    +goog.net.xpc.NixTransport.prototype.handleMessage_ =
    +    function(serviceName, payload) {
    +  /** @this {goog.net.xpc.NixTransport} */
    +  var deliveryHandler = function() {
    +    this.channel_.xpcDeliver(serviceName, payload);
    +  };
    +  this.getWindow().setTimeout(goog.bind(deliveryHandler, this), 1);
    +};
    +
    +
    +/**
    + * Sends a message.
    + * @param {string} service The name of the service the message is to be
    + *   delivered to.
    + * @param {string} payload The message content.
    + * @override
    + */
    +goog.net.xpc.NixTransport.prototype.send = function(service, payload) {
    +  // Verify that the NIX channel we have is valid.
    +  if (typeof(this.nixChannel_) !== 'unknown') {
    +    goog.log.error(goog.net.xpc.logger, 'NIX channel not connected');
    +  }
    +
    +  // Send the message via the NIX wrapper object.
    +  this.nixChannel_['SendMessage'](service, payload);
    +};
    +
    +
    +/** @override */
    +goog.net.xpc.NixTransport.prototype.disposeInternal = function() {
    +  goog.net.xpc.NixTransport.base(this, 'disposeInternal');
    +  this.nixChannel_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/relay.js b/mapbox-gl/closure-library/closure/goog/net/xpc/relay.js
    new file mode 100644
    index 0000000000..03238b672e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/relay.js
    @@ -0,0 +1,73 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Standalone script to be included in the relay-document
    + * used by goog.net.xpc.IframeRelayTransport. This script will decode the
    + * fragment identifier, determine the target window object and deliver
    + * the data to it.
    + *
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.net.xpc.relay');
    +
    +(function() {
    +  // Decode the fragement identifier.
    +  // location.href is expected to be structured as follows:
    +  // <url>#<channel_name>[,<iframe_id>]|<data>
    +
    +  // Get the fragment identifier.
    +  var raw = window.location.hash;
    +  if (!raw) {
    +    return;
    +  }
    +  if (raw.charAt(0) == '#') {
    +    raw = raw.substring(1);
    +  }
    +  var pos = raw.indexOf('|');
    +  var head = raw.substring(0, pos).split(',');
    +  var channelName = head[0];
    +  var iframeId = head.length == 2 ? head[1] : null;
    +  var frame = raw.substring(pos + 1);
    +
    +  // Find the window object of the peer.
    +  //
    +  // The general structure of the frames looks like this:
    +  // - peer1
    +  //   - relay2
    +  //   - peer2
    +  //     - relay1
    +  //
    +  // We are either relay1 or relay2.
    +
    +  var win;
    +  if (iframeId) {
    +    // We are relay2 and need to deliver the data to peer2.
    +    win = window.parent.frames[iframeId];
    +  } else {
    +    // We are relay1 and need to deliver the data to peer1.
    +    win = window.parent.parent;
    +  }
    +
    +  // Deliver the data.
    +  try {
    +    win['xpcRelay'](channelName, frame);
    +  } catch (e) {
    +    // Nothing useful can be done here.
    +    // It would be great to inform the sender the delivery of this message
    +    // failed, but this is not possible because we are already in the receiver's
    +    // domain at this point.
    +  }
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/access_checker.html b/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/access_checker.html
    new file mode 100644
    index 0000000000..10a421e167
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/access_checker.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<!--
    +
    +   This file checks whether the current browser can access properties from same
    +   domain iframes. This is currently a problem on the matrix brower ie6 and xp.
    +   For some reason it can't access same domain iframes.
    +   TODO(user): Figure out why it can't.
    +  -->
    +<html>
    +  <!--
    +     Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +     Use of this source code is governed by the Apache License, Version 2.0.
    +     See the COPYING file for details.
    +    -->
    +  <head>
    +    <title>The access checking iframe</title>
    +  </head>
    +  <body>
    +    <script type="text/javascript">
    +      try {
    +        var sameDomainIframeHref = parent.frames['nonexistant'].location.href;
    +        parent.sameDomainIframeAccessComplete(true);
    +      } catch (e) {
    +        parent.sameDomainIframeAccessComplete(false);
    +      }
    +    </script>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/inner_peer.html b/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/inner_peer.html
    new file mode 100644
    index 0000000000..ce6506a9a0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/testdata/inner_peer.html
    @@ -0,0 +1,99 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  This file is responsible for setting up the inner peer half of an XPC
    +  communication channel. It instantiates a CrossPageChannel and attempts to
    +  connect to the outer peer. The XPC configuration should match that of the
    +  outer peer (i.e. same channel name, polling URIs, etc).
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +
    +<head>
    +<title>XPC test inner frame</title>
    +<script src="../../../base.js" type="text/javascript"></script>
    +<script type="text/javascript">
    +goog.require('goog.debug.Logger');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.xpc.CrossPageChannel');
    +</script>
    +<script type="text/javascript">
    +var channel;
    +var queuedMessage;
    +
    +var OBJECT_RESULT_FROM_SERVICE = {'favorites': 'pie'};
    +
    +function clearDebug() {
    +  document.getElementById('debugDiv').innerHTML = '';
    +}
    +
    +function instantiateChannel(cfg) {
    +  if (window.channel) {
    +    window.channel.dispose();
    +  }
    +  window.channel = new goog.net.xpc.CrossPageChannel(cfg);
    +  window.channel.registerService('echo', echoHandler);
    +  window.channel.registerService('response', responseHandler);
    +  connectChannel(
    +      parent.driver && parent.driver.innerFrameConnected ?
    +      goog.bind(parent.driver.innerFrameConnected, parent.driver) : null);
    +}
    +
    +function connectChannel(opt_callback) {
    +  window.channel.connect(opt_callback || goog.nullFunction);
    +}
    +
    +function sendEcho(payload) {
    +  window.channel.send('echo', payload);
    +}
    +
    +function echoHandler(payload) {
    +  window.channel.send('response', payload);
    +  return OBJECT_RESULT_FROM_SERVICE;
    +}
    +
    +function isConnected() {
    +  return window.channel && window.channel.isConnected();
    +}
    +
    +function responseHandler(payload) {
    +  if (parent.driver && parent.driver.innerFrameGotResponse) {
    +    parent.driver.innerFrameGotResponse(payload);
    +  }
    +}
    +
    +</script>
    +</head>
    +
    +<body>
    +
    +<div style="position:absolute">
    +  Debug [<a href="#" onclick="clearDebug()">clear</a>]: <br>
    +  <div id=debugDiv style="border: 1px #000000 solid; font-size:xx-small"></div>
    +</div>
    +
    +<script type="text/javascript">
    +var debugDiv = goog.dom.getElement('debugDiv');
    +var logger = goog.debug.Logger.getLogger('goog.net.xpc');
    +logger.setLevel(goog.debug.Logger.Level.ALL);
    +logger.addHandler(function(logRecord) {
    +  var msgElm = goog.dom.createDom('div');
    +  msgElm.innerHTML = logRecord.getMessage();
    +  goog.dom.appendChild(debugDiv, msgElm);
    +});
    +
    +if (parent && parent.iframeLoadHandler) {
    +  parent.iframeLoadHandler();
    +}
    +</script>
    +
    +</body>
    +
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/transport.js b/mapbox-gl/closure-library/closure/goog/net/xpc/transport.js
    new file mode 100644
    index 0000000000..edb14611f8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/transport.js
    @@ -0,0 +1,105 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Contains the base class for transports.
    + *
    + */
    +
    +
    +goog.provide('goog.net.xpc.Transport');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.dom');
    +goog.require('goog.net.xpc.TransportNames');
    +
    +
    +
    +/**
    + * The base class for transports.
    + * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for
    + *     finding the window objects.
    + * @constructor
    + * @extends {goog.Disposable};
    + */
    +goog.net.xpc.Transport = function(opt_domHelper) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The dom helper to use for finding the window objects to reference.
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
    +};
    +goog.inherits(goog.net.xpc.Transport, goog.Disposable);
    +
    +
    +/**
    + * The transport type.
    + * @type {number}
    + * @protected
    + */
    +goog.net.xpc.Transport.prototype.transportType = 0;
    +
    +
    +/**
    + * @return {number} The transport type identifier.
    + */
    +goog.net.xpc.Transport.prototype.getType = function() {
    +  return this.transportType;
    +};
    +
    +
    +/**
    + * Returns the window associated with this transport instance.
    + * @return {!Window} The window to use.
    + */
    +goog.net.xpc.Transport.prototype.getWindow = function() {
    +  return this.domHelper_.getWindow();
    +};
    +
    +
    +/**
    + * Return the transport name.
    + * @return {string} the transport name.
    + */
    +goog.net.xpc.Transport.prototype.getName = function() {
    +  return goog.net.xpc.TransportNames[String(this.transportType)] || '';
    +};
    +
    +
    +/**
    + * Handles transport service messages (internal signalling).
    + * @param {string} payload The message content.
    + */
    +goog.net.xpc.Transport.prototype.transportServiceHandler = goog.abstractMethod;
    +
    +
    +/**
    + * Connects this transport.
    + * The transport implementation is expected to call
    + * CrossPageChannel.prototype.notifyConnected when the channel is ready
    + * to be used.
    + */
    +goog.net.xpc.Transport.prototype.connect = goog.abstractMethod;
    +
    +
    +/**
    + * Sends a message.
    + * @param {string} service The name off the service the message is to be
    + * delivered to.
    + * @param {string} payload The message content.
    + */
    +goog.net.xpc.Transport.prototype.send = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/net/xpc/xpc.js b/mapbox-gl/closure-library/closure/goog/net/xpc/xpc.js
    new file mode 100644
    index 0000000000..a0fd2ff87e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/net/xpc/xpc.js
    @@ -0,0 +1,300 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the namesspace for client-side communication
    + * between pages originating from different domains (it works also
    + * with pages from the same domain, but doing that is kinda
    + * pointless).
    + *
    + * The only publicly visible class is goog.net.xpc.CrossPageChannel.
    + *
    + * Note: The preferred name for the main class would have been
    + * CrossDomainChannel.  But as there already is a class named like
    + * that (which serves a different purpose) in the maps codebase,
    + * CrossPageChannel was chosen to avoid confusion.
    + *
    + * CrossPageChannel abstracts the underlying transport mechanism to
    + * provide a common interface in all browsers.
    + *
    + */
    +
    +/*
    +TODO(user)
    +- resolve fastback issues in Safari (IframeRelayTransport)
    + */
    +
    +
    +/**
    + * Namespace for CrossPageChannel
    + */
    +goog.provide('goog.net.xpc');
    +goog.provide('goog.net.xpc.CfgFields');
    +goog.provide('goog.net.xpc.ChannelStates');
    +goog.provide('goog.net.xpc.TransportNames');
    +goog.provide('goog.net.xpc.TransportTypes');
    +goog.provide('goog.net.xpc.UriCfgFields');
    +
    +goog.require('goog.log');
    +
    +
    +/**
    + * Enum used to identify transport types.
    + * @enum {number}
    + */
    +goog.net.xpc.TransportTypes = {
    +  NATIVE_MESSAGING: 1,
    +  FRAME_ELEMENT_METHOD: 2,
    +  IFRAME_RELAY: 3,
    +  IFRAME_POLLING: 4,
    +  FLASH: 5,
    +  NIX: 6,
    +  DIRECT: 7
    +};
    +
    +
    +/**
    + * Enum containing transport names. These need to correspond to the
    + * transport class names for createTransport_() to work.
    + * @const {!Object<string,string>}
    + */
    +goog.net.xpc.TransportNames = {
    +  '1': 'NativeMessagingTransport',
    +  '2': 'FrameElementMethodTransport',
    +  '3': 'IframeRelayTransport',
    +  '4': 'IframePollingTransport',
    +  '5': 'FlashTransport',
    +  '6': 'NixTransport',
    +  '7': 'DirectTransport'
    +};
    +
    +
    +// TODO(user): Add auth token support to other methods.
    +
    +
    +/**
    + * Field names used on configuration object.
    + * @const
    + */
    +goog.net.xpc.CfgFields = {
    +  /**
    +   * Channel name identifier.
    +   * Both peers have to be initialized with
    +   * the same channel name.  If not present, a channel name is
    +   * generated (which then has to transferred to the peer somehow).
    +   */
    +  CHANNEL_NAME: 'cn',
    +  /**
    +   * Authorization token. If set, NIX will use this authorization token
    +   * to validate the setup.
    +   */
    +  AUTH_TOKEN: 'at',
    +  /**
    +   * Remote party's authorization token. If set, NIX will validate this
    +   * authorization token against that sent by the other party.
    +   */
    +  REMOTE_AUTH_TOKEN: 'rat',
    +  /**
    +   * The URI of the peer page.
    +   */
    +  PEER_URI: 'pu',
    +  /**
    +   * Ifame-ID identifier.
    +   * The id of the iframe element the peer-document lives in.
    +   */
    +  IFRAME_ID: 'ifrid',
    +  /**
    +   * Transport type identifier.
    +   * The transport type to use. Possible values are entries from
    +   * goog.net.xpc.TransportTypes. If not present, the transport is
    +   * determined automatically based on the useragent's capabilities.
    +   */
    +  TRANSPORT: 'tp',
    +  /**
    +   * Local relay URI identifier (IframeRelayTransport-specific).
    +   * The URI (can't contain a fragment identifier) used by the peer to
    +   * relay data through.
    +   */
    +  LOCAL_RELAY_URI: 'lru',
    +  /**
    +   * Peer relay URI identifier (IframeRelayTransport-specific).
    +   * The URI (can't contain a fragment identifier) used to relay data
    +   * to the peer.
    +   */
    +  PEER_RELAY_URI: 'pru',
    +  /**
    +   * Local poll URI identifier (IframePollingTransport-specific).
    +   * The URI  (can't contain a fragment identifier)which is polled
    +   * to receive data from the peer.
    +   */
    +  LOCAL_POLL_URI: 'lpu',
    +  /**
    +   * Local poll URI identifier (IframePollingTransport-specific).
    +   * The URI (can't contain a fragment identifier) used to send data
    +   * to the peer.
    +   */
    +  PEER_POLL_URI: 'ppu',
    +  /**
    +   * The hostname of the peer window, including protocol, domain, and port
    +   * (if specified). Used for security sensitive applications that make
    +   * use of NativeMessagingTransport (i.e. most applications).
    +   */
    +  PEER_HOSTNAME: 'ph',
    +  /**
    +   * Usually both frames using a connection initially send a SETUP message to
    +   * each other, and each responds with a SETUP_ACK.  A frame marks itself
    +   * connected when it receives that SETUP_ACK.  If this parameter is true
    +   * however, the channel it is passed to will not send a SETUP, but rather will
    +   * wait for one from its peer and mark itself connected when that arrives.
    +   * Peer iframes created using such a channel will send SETUP however, and will
    +   * wait for SETUP_ACK before marking themselves connected.  The goal is to
    +   * cope with a situation where the availability of the URL for the peer frame
    +   * cannot be relied on, eg when the application is offline.  Without this
    +   * setting, the primary frame will attempt to send its SETUP message every
    +   * 100ms, forever.  This floods the javascript console with uncatchable
    +   * security warnings, and fruitlessly burns CPU.  There is one scenario this
    +   * mode will not support, and that is reconnection by the outer frame, ie the
    +   * creation of a new channel object to connect to a peer iframe which was
    +   * already communicating with a previous channel object of the same name.  If
    +   * that behavior is needed, this mode should not be used.  Reconnection by
    +   * inner frames is supported in this mode however.
    +   */
    +  ONE_SIDED_HANDSHAKE: 'osh',
    +  /**
    +   * The frame role (inner or outer). Used to explicitly indicate the role for
    +   * each peer whenever the role cannot be reliably determined (e.g. the two
    +   * peer windows are not parent/child frames). If unspecified, the role will
    +   * be dynamically determined, assuming a parent/child frame setup.
    +   */
    +  ROLE: 'role',
    +  /**
    +   * Which version of the native transport startup protocol should be used, the
    +   * default being '2'.  Version 1 had various timing vulnerabilities, which
    +   * had to be compensated for by introducing delays, and is deprecated.  V1
    +   * and V2 are broadly compatible, although the more robust timing and lack
    +   * of delays is not gained unless both sides are using V2.  The only
    +   * unsupported case of cross-protocol interoperation is where a connection
    +   * starts out with V2 at both ends, and one of the ends reconnects as a V1.
    +   * All other initial startup and reconnection scenarios are supported.
    +   */
    +  NATIVE_TRANSPORT_PROTOCOL_VERSION: 'nativeProtocolVersion',
    +  /**
    +   * Whether the direct transport runs in synchronous mode. The default is to
    +   * emulate the other transports and run asyncronously but there are some
    +   * circumstances where syncronous calls are required. If this property is
    +   * set to true, the transport will send the messages synchronously.
    +   */
    +  DIRECT_TRANSPORT_SYNC_MODE: 'directSyncMode'
    +};
    +
    +
    +/**
    + * Config properties that need to be URL sanitized.
    + * @type {Array<string>}
    + */
    +goog.net.xpc.UriCfgFields = [
    +  goog.net.xpc.CfgFields.PEER_URI,
    +  goog.net.xpc.CfgFields.LOCAL_RELAY_URI,
    +  goog.net.xpc.CfgFields.PEER_RELAY_URI,
    +  goog.net.xpc.CfgFields.LOCAL_POLL_URI,
    +  goog.net.xpc.CfgFields.PEER_POLL_URI
    +];
    +
    +
    +/**
    + * @enum {number}
    + */
    +goog.net.xpc.ChannelStates = {
    +  NOT_CONNECTED: 1,
    +  CONNECTED: 2,
    +  CLOSED: 3
    +};
    +
    +
    +/**
    + * The name of the transport service (used for internal signalling).
    + * @type {string}
    + * @suppress {underscore|visibility}
    + */
    +goog.net.xpc.TRANSPORT_SERVICE_ = 'tp';
    +
    +
    +/**
    + * Transport signaling message: setup.
    + * @type {string}
    + */
    +goog.net.xpc.SETUP = 'SETUP';
    +
    +
    +/**
    + * Transport signaling message: setup for native transport protocol v2.
    + * @type {string}
    + */
    +goog.net.xpc.SETUP_NTPV2 = 'SETUP_NTPV2';
    +
    +
    +/**
    + * Transport signaling message: setup acknowledgement.
    + * @type {string}
    + * @suppress {underscore|visibility}
    + */
    +goog.net.xpc.SETUP_ACK_ = 'SETUP_ACK';
    +
    +
    +/**
    + * Transport signaling message: setup acknowledgement.
    + * @type {string}
    + */
    +goog.net.xpc.SETUP_ACK_NTPV2 = 'SETUP_ACK_NTPV2';
    +
    +
    +/**
    + * Object holding active channels.
    + *
    + * @package {Object<string, goog.net.xpc.CrossPageChannel>}
    + */
    +goog.net.xpc.channels = {};
    +
    +
    +/**
    + * Returns a random string.
    + * @param {number} length How many characters the string shall contain.
    + * @param {string=} opt_characters The characters used.
    + * @return {string} The random string.
    + */
    +goog.net.xpc.getRandomString = function(length, opt_characters) {
    +  var chars = opt_characters || goog.net.xpc.randomStringCharacters_;
    +  var charsLength = chars.length;
    +  var s = '';
    +  while (length-- > 0) {
    +    s += chars.charAt(Math.floor(Math.random() * charsLength));
    +  }
    +  return s;
    +};
    +
    +
    +/**
    + * The default characters used for random string generation.
    + * @type {string}
    + * @private
    + */
    +goog.net.xpc.randomStringCharacters_ =
    +    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    +
    +
    +/**
    + * The logger.
    + * @type {goog.log.Logger}
    + */
    +goog.net.xpc.logger = goog.log.getLogger('goog.net.xpc');
    diff --git a/mapbox-gl/closure-library/closure/goog/object/object.js b/mapbox-gl/closure-library/closure/goog/object/object.js
    new file mode 100644
    index 0000000000..20a77f789a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/object/object.js
    @@ -0,0 +1,686 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for manipulating objects/maps/hashes.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.object');
    +
    +
    +/**
    + * Calls a function for each element in an object/map/hash.
    + *
    + * @param {Object<K,V>} obj The object over which to iterate.
    + * @param {function(this:T,V,?,Object<K,V>):?} f The function to call
    + *     for every element. This function takes 3 arguments (the element, the
    + *     index and the object) and the return value is ignored.
    + * @param {T=} opt_obj This is used as the 'this' object within f.
    + * @template T,K,V
    + */
    +goog.object.forEach = function(obj, f, opt_obj) {
    +  for (var key in obj) {
    +    f.call(opt_obj, obj[key], key, obj);
    +  }
    +};
    +
    +
    +/**
    + * Calls a function for each element in an object/map/hash. If that call returns
    + * true, adds the element to a new object.
    + *
    + * @param {Object<K,V>} obj The object over which to iterate.
    + * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
    + *     for every element. This
    + *     function takes 3 arguments (the element, the index and the object)
    + *     and should return a boolean. If the return value is true the
    + *     element is added to the result object. If it is false the
    + *     element is not included.
    + * @param {T=} opt_obj This is used as the 'this' object within f.
    + * @return {!Object<K,V>} a new object in which only elements that passed the
    + *     test are present.
    + * @template T,K,V
    + */
    +goog.object.filter = function(obj, f, opt_obj) {
    +  var res = {};
    +  for (var key in obj) {
    +    if (f.call(opt_obj, obj[key], key, obj)) {
    +      res[key] = obj[key];
    +    }
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * For every element in an object/map/hash calls a function and inserts the
    + * result into a new object.
    + *
    + * @param {Object<K,V>} obj The object over which to iterate.
    + * @param {function(this:T,V,?,Object<K,V>):R} f The function to call
    + *     for every element. This function
    + *     takes 3 arguments (the element, the index and the object)
    + *     and should return something. The result will be inserted
    + *     into a new object.
    + * @param {T=} opt_obj This is used as the 'this' object within f.
    + * @return {!Object<K,R>} a new object with the results from f.
    + * @template T,K,V,R
    + */
    +goog.object.map = function(obj, f, opt_obj) {
    +  var res = {};
    +  for (var key in obj) {
    +    res[key] = f.call(opt_obj, obj[key], key, obj);
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * Calls a function for each element in an object/map/hash. If any
    + * call returns true, returns true (without checking the rest). If
    + * all calls return false, returns false.
    + *
    + * @param {Object<K,V>} obj The object to check.
    + * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
    + *     call for every element. This function
    + *     takes 3 arguments (the element, the index and the object) and should
    + *     return a boolean.
    + * @param {T=} opt_obj This is used as the 'this' object within f.
    + * @return {boolean} true if any element passes the test.
    + * @template T,K,V
    + */
    +goog.object.some = function(obj, f, opt_obj) {
    +  for (var key in obj) {
    +    if (f.call(opt_obj, obj[key], key, obj)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Calls a function for each element in an object/map/hash. If
    + * all calls return true, returns true. If any call returns false, returns
    + * false at this point and does not continue to check the remaining elements.
    + *
    + * @param {Object<K,V>} obj The object to check.
    + * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
    + *     call for every element. This function
    + *     takes 3 arguments (the element, the index and the object) and should
    + *     return a boolean.
    + * @param {T=} opt_obj This is used as the 'this' object within f.
    + * @return {boolean} false if any element fails the test.
    + * @template T,K,V
    + */
    +goog.object.every = function(obj, f, opt_obj) {
    +  for (var key in obj) {
    +    if (!f.call(opt_obj, obj[key], key, obj)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Returns the number of key-value pairs in the object map.
    + *
    + * @param {Object} obj The object for which to get the number of key-value
    + *     pairs.
    + * @return {number} The number of key-value pairs in the object map.
    + */
    +goog.object.getCount = function(obj) {
    +  // JS1.5 has __count__ but it has been deprecated so it raises a warning...
    +  // in other words do not use. Also __count__ only includes the fields on the
    +  // actual object and not in the prototype chain.
    +  var rv = 0;
    +  for (var key in obj) {
    +    rv++;
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Returns one key from the object map, if any exists.
    + * For map literals the returned key will be the first one in most of the
    + * browsers (a know exception is Konqueror).
    + *
    + * @param {Object} obj The object to pick a key from.
    + * @return {string|undefined} The key or undefined if the object is empty.
    + */
    +goog.object.getAnyKey = function(obj) {
    +  for (var key in obj) {
    +    return key;
    +  }
    +};
    +
    +
    +/**
    + * Returns one value from the object map, if any exists.
    + * For map literals the returned value will be the first one in most of the
    + * browsers (a know exception is Konqueror).
    + *
    + * @param {Object<K,V>} obj The object to pick a value from.
    + * @return {V|undefined} The value or undefined if the object is empty.
    + * @template K,V
    + */
    +goog.object.getAnyValue = function(obj) {
    +  for (var key in obj) {
    +    return obj[key];
    +  }
    +};
    +
    +
    +/**
    + * Whether the object/hash/map contains the given object as a value.
    + * An alias for goog.object.containsValue(obj, val).
    + *
    + * @param {Object<K,V>} obj The object in which to look for val.
    + * @param {V} val The object for which to check.
    + * @return {boolean} true if val is present.
    + * @template K,V
    + */
    +goog.object.contains = function(obj, val) {
    +  return goog.object.containsValue(obj, val);
    +};
    +
    +
    +/**
    + * Returns the values of the object/map/hash.
    + *
    + * @param {Object<K,V>} obj The object from which to get the values.
    + * @return {!Array<V>} The values in the object/map/hash.
    + * @template K,V
    + */
    +goog.object.getValues = function(obj) {
    +  var res = [];
    +  var i = 0;
    +  for (var key in obj) {
    +    res[i++] = obj[key];
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * Returns the keys of the object/map/hash.
    + *
    + * @param {Object} obj The object from which to get the keys.
    + * @return {!Array<string>} Array of property keys.
    + */
    +goog.object.getKeys = function(obj) {
    +  var res = [];
    +  var i = 0;
    +  for (var key in obj) {
    +    res[i++] = key;
    +  }
    +  return res;
    +};
    +
    +
    +/**
    + * Get a value from an object multiple levels deep.  This is useful for
    + * pulling values from deeply nested objects, such as JSON responses.
    + * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
    + *
    + * @param {!Object} obj An object to get the value from.  Can be array-like.
    + * @param {...(string|number|!Array<number|string>)} var_args A number of keys
    + *     (as strings, or numbers, for array-like objects).  Can also be
    + *     specified as a single array of keys.
    + * @return {*} The resulting value.  If, at any point, the value for a key
    + *     is undefined, returns undefined.
    + */
    +goog.object.getValueByKeys = function(obj, var_args) {
    +  var isArrayLike = goog.isArrayLike(var_args);
    +  var keys = isArrayLike ? var_args : arguments;
    +
    +  // Start with the 2nd parameter for the variable parameters syntax.
    +  for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
    +    obj = obj[keys[i]];
    +    if (!goog.isDef(obj)) {
    +      break;
    +    }
    +  }
    +
    +  return obj;
    +};
    +
    +
    +/**
    + * Whether the object/map/hash contains the given key.
    + *
    + * @param {Object} obj The object in which to look for key.
    + * @param {*} key The key for which to check.
    + * @return {boolean} true If the map contains the key.
    + */
    +goog.object.containsKey = function(obj, key) {
    +  return key in obj;
    +};
    +
    +
    +/**
    + * Whether the object/map/hash contains the given value. This is O(n).
    + *
    + * @param {Object<K,V>} obj The object in which to look for val.
    + * @param {V} val The value for which to check.
    + * @return {boolean} true If the map contains the value.
    + * @template K,V
    + */
    +goog.object.containsValue = function(obj, val) {
    +  for (var key in obj) {
    +    if (obj[key] == val) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Searches an object for an element that satisfies the given condition and
    + * returns its key.
    + * @param {Object<K,V>} obj The object to search in.
    + * @param {function(this:T,V,string,Object<K,V>):boolean} f The
    + *      function to call for every element. Takes 3 arguments (the value,
    + *     the key and the object) and should return a boolean.
    + * @param {T=} opt_this An optional "this" context for the function.
    + * @return {string|undefined} The key of an element for which the function
    + *     returns true or undefined if no such element is found.
    + * @template T,K,V
    + */
    +goog.object.findKey = function(obj, f, opt_this) {
    +  for (var key in obj) {
    +    if (f.call(opt_this, obj[key], key, obj)) {
    +      return key;
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * Searches an object for an element that satisfies the given condition and
    + * returns its value.
    + * @param {Object<K,V>} obj The object to search in.
    + * @param {function(this:T,V,string,Object<K,V>):boolean} f The function
    + *     to call for every element. Takes 3 arguments (the value, the key
    + *     and the object) and should return a boolean.
    + * @param {T=} opt_this An optional "this" context for the function.
    + * @return {V} The value of an element for which the function returns true or
    + *     undefined if no such element is found.
    + * @template T,K,V
    + */
    +goog.object.findValue = function(obj, f, opt_this) {
    +  var key = goog.object.findKey(obj, f, opt_this);
    +  return key && obj[key];
    +};
    +
    +
    +/**
    + * Whether the object/map/hash is empty.
    + *
    + * @param {Object} obj The object to test.
    + * @return {boolean} true if obj is empty.
    + */
    +goog.object.isEmpty = function(obj) {
    +  for (var key in obj) {
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Removes all key value pairs from the object/map/hash.
    + *
    + * @param {Object} obj The object to clear.
    + */
    +goog.object.clear = function(obj) {
    +  for (var i in obj) {
    +    delete obj[i];
    +  }
    +};
    +
    +
    +/**
    + * Removes a key-value pair based on the key.
    + *
    + * @param {Object} obj The object from which to remove the key.
    + * @param {*} key The key to remove.
    + * @return {boolean} Whether an element was removed.
    + */
    +goog.object.remove = function(obj, key) {
    +  var rv;
    +  if ((rv = key in obj)) {
    +    delete obj[key];
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Adds a key-value pair to the object. Throws an exception if the key is
    + * already in use. Use set if you want to change an existing pair.
    + *
    + * @param {Object<K,V>} obj The object to which to add the key-value pair.
    + * @param {string} key The key to add.
    + * @param {V} val The value to add.
    + * @template K,V
    + */
    +goog.object.add = function(obj, key, val) {
    +  if (key in obj) {
    +    throw Error('The object already contains the key "' + key + '"');
    +  }
    +  goog.object.set(obj, key, val);
    +};
    +
    +
    +/**
    + * Returns the value for the given key.
    + *
    + * @param {Object<K,V>} obj The object from which to get the value.
    + * @param {string} key The key for which to get the value.
    + * @param {R=} opt_val The value to return if no item is found for the given
    + *     key (default is undefined).
    + * @return {V|R|undefined} The value for the given key.
    + * @template K,V,R
    + */
    +goog.object.get = function(obj, key, opt_val) {
    +  if (key in obj) {
    +    return obj[key];
    +  }
    +  return opt_val;
    +};
    +
    +
    +/**
    + * Adds a key-value pair to the object/map/hash.
    + *
    + * @param {Object<K,V>} obj The object to which to add the key-value pair.
    + * @param {string} key The key to add.
    + * @param {V} value The value to add.
    + * @template K,V
    + */
    +goog.object.set = function(obj, key, value) {
    +  obj[key] = value;
    +};
    +
    +
    +/**
    + * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
    + *
    + * @param {Object<K,V>} obj The object to which to add the key-value pair.
    + * @param {string} key The key to add.
    + * @param {V} value The value to add if the key wasn't present.
    + * @return {V} The value of the entry at the end of the function.
    + * @template K,V
    + */
    +goog.object.setIfUndefined = function(obj, key, value) {
    +  return key in obj ? obj[key] : (obj[key] = value);
    +};
    +
    +
    +/**
    + * Sets a key and value to an object if the key is not set. The value will be
    + * the return value of the given function. If the key already exists, the
    + * object will not be changed and the function will not be called (the function
    + * will be lazily evaluated -- only called if necessary).
    + *
    + * This function is particularly useful for use with a map used a as a cache.
    + *
    + * @param {!Object<K,V>} obj The object to which to add the key-value pair.
    + * @param {string} key The key to add.
    + * @param {function():V} f The value to add if the key wasn't present.
    + * @return {V} The value of the entry at the end of the function.
    + * @template K,V
    + */
    +goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
    +  if (key in obj) {
    +    return obj[key];
    +  }
    +
    +  var val = f();
    +  obj[key] = val;
    +  return val;
    +};
    +
    +
    +/**
    + * Compares two objects for equality using === on the values.
    + *
    + * @param {!Object<K,V>} a
    + * @param {!Object<K,V>} b
    + * @return {boolean}
    + * @template K,V
    + */
    +goog.object.equals = function(a, b) {
    +  for (var k in a) {
    +    if (!(k in b) || a[k] !== b[k]) {
    +      return false;
    +    }
    +  }
    +  for (var k in b) {
    +    if (!(k in a)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Does a flat clone of the object.
    + *
    + * @param {Object<K,V>} obj Object to clone.
    + * @return {!Object<K,V>} Clone of the input object.
    + * @template K,V
    + */
    +goog.object.clone = function(obj) {
    +  // We cannot use the prototype trick because a lot of methods depend on where
    +  // the actual key is set.
    +
    +  var res = {};
    +  for (var key in obj) {
    +    res[key] = obj[key];
    +  }
    +  return res;
    +  // We could also use goog.mixin but I wanted this to be independent from that.
    +};
    +
    +
    +/**
    + * Clones a value. The input may be an Object, Array, or basic type. Objects and
    + * arrays will be cloned recursively.
    + *
    + * WARNINGS:
    + * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
    + * that refer to themselves will cause infinite recursion.
    + *
    + * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
    + * copies UIDs created by <code>getUid</code> into cloned results.
    + *
    + * @param {*} obj The value to clone.
    + * @return {*} A clone of the input value.
    + */
    +goog.object.unsafeClone = function(obj) {
    +  var type = goog.typeOf(obj);
    +  if (type == 'object' || type == 'array') {
    +    if (obj.clone) {
    +      return obj.clone();
    +    }
    +    var clone = type == 'array' ? [] : {};
    +    for (var key in obj) {
    +      clone[key] = goog.object.unsafeClone(obj[key]);
    +    }
    +    return clone;
    +  }
    +
    +  return obj;
    +};
    +
    +
    +/**
    + * Returns a new object in which all the keys and values are interchanged
    + * (keys become values and values become keys). If multiple keys map to the
    + * same value, the chosen transposed value is implementation-dependent.
    + *
    + * @param {Object} obj The object to transpose.
    + * @return {!Object} The transposed object.
    + */
    +goog.object.transpose = function(obj) {
    +  var transposed = {};
    +  for (var key in obj) {
    +    transposed[obj[key]] = key;
    +  }
    +  return transposed;
    +};
    +
    +
    +/**
    + * The names of the fields that are defined on Object.prototype.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.object.PROTOTYPE_FIELDS_ = [
    +  'constructor',
    +  'hasOwnProperty',
    +  'isPrototypeOf',
    +  'propertyIsEnumerable',
    +  'toLocaleString',
    +  'toString',
    +  'valueOf'
    +];
    +
    +
    +/**
    + * Extends an object with another object.
    + * This operates 'in-place'; it does not create a new Object.
    + *
    + * Example:
    + * var o = {};
    + * goog.object.extend(o, {a: 0, b: 1});
    + * o; // {a: 0, b: 1}
    + * goog.object.extend(o, {b: 2, c: 3});
    + * o; // {a: 0, b: 2, c: 3}
    + *
    + * @param {Object} target The object to modify. Existing properties will be
    + *     overwritten if they are also present in one of the objects in
    + *     {@code var_args}.
    + * @param {...Object} var_args The objects from which values will be copied.
    + */
    +goog.object.extend = function(target, var_args) {
    +  var key, source;
    +  for (var i = 1; i < arguments.length; i++) {
    +    source = arguments[i];
    +    for (key in source) {
    +      target[key] = source[key];
    +    }
    +
    +    // For IE the for-in-loop does not contain any properties that are not
    +    // enumerable on the prototype object (for example isPrototypeOf from
    +    // Object.prototype) and it will also not include 'replace' on objects that
    +    // extend String and change 'replace' (not that it is common for anyone to
    +    // extend anything except Object).
    +
    +    for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
    +      key = goog.object.PROTOTYPE_FIELDS_[j];
    +      if (Object.prototype.hasOwnProperty.call(source, key)) {
    +        target[key] = source[key];
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Creates a new object built from the key-value pairs provided as arguments.
    + * @param {...*} var_args If only one argument is provided and it is an array
    + *     then this is used as the arguments,  otherwise even arguments are used as
    + *     the property names and odd arguments are used as the property values.
    + * @return {!Object} The new object.
    + * @throws {Error} If there are uneven number of arguments or there is only one
    + *     non array argument.
    + */
    +goog.object.create = function(var_args) {
    +  var argLength = arguments.length;
    +  if (argLength == 1 && goog.isArray(arguments[0])) {
    +    return goog.object.create.apply(null, arguments[0]);
    +  }
    +
    +  if (argLength % 2) {
    +    throw Error('Uneven number of arguments');
    +  }
    +
    +  var rv = {};
    +  for (var i = 0; i < argLength; i += 2) {
    +    rv[arguments[i]] = arguments[i + 1];
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Creates a new object where the property names come from the arguments but
    + * the value is always set to true
    + * @param {...*} var_args If only one argument is provided and it is an array
    + *     then this is used as the arguments,  otherwise the arguments are used
    + *     as the property names.
    + * @return {!Object} The new object.
    + */
    +goog.object.createSet = function(var_args) {
    +  var argLength = arguments.length;
    +  if (argLength == 1 && goog.isArray(arguments[0])) {
    +    return goog.object.createSet.apply(null, arguments[0]);
    +  }
    +
    +  var rv = {};
    +  for (var i = 0; i < argLength; i++) {
    +    rv[arguments[i]] = true;
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Creates an immutable view of the underlying object, if the browser
    + * supports immutable objects.
    + *
    + * In default mode, writes to this view will fail silently. In strict mode,
    + * they will throw an error.
    + *
    + * @param {!Object<K,V>} obj An object.
    + * @return {!Object<K,V>} An immutable view of that object, or the
    + *     original object if this browser does not support immutables.
    + * @template K,V
    + */
    +goog.object.createImmutableView = function(obj) {
    +  var result = obj;
    +  if (Object.isFrozen && !Object.isFrozen(obj)) {
    +    result = Object.create(obj);
    +    Object.freeze(result);
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * @param {!Object} obj An object.
    + * @return {boolean} Whether this is an immutable view of the object.
    + */
    +goog.object.isImmutableView = function(obj) {
    +  return !!Object.isFrozen && Object.isFrozen(obj);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/object/object_test.html b/mapbox-gl/closure-library/closure/goog/object/object_test.html
    new file mode 100644
    index 0000000000..126eff7f9f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/object/object_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.object
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.objectTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/object/object_test.js b/mapbox-gl/closure-library/closure/goog/object/object_test.js
    new file mode 100644
    index 0000000000..2c55aa0b9d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/object/object_test.js
    @@ -0,0 +1,530 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.objectTest');
    +goog.setTestOnly('goog.objectTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +function stringifyObject(m) {
    +  var keys = goog.object.getKeys(m);
    +  var s = '';
    +  for (var i = 0; i < keys.length; i++) {
    +    s += keys[i] + goog.object.get(m, keys[i]);
    +  }
    +  return s;
    +}
    +
    +function getObject() {
    +  return {
    +    a: 0,
    +    b: 1,
    +    c: 2,
    +    d: 3
    +  };
    +}
    +
    +function testKeys() {
    +  var m = getObject();
    +  assertEquals('getKeys, The keys should be a,b,c',
    +               'a,b,c,d',
    +               goog.object.getKeys(m).join(','));
    +}
    +
    +function testValues() {
    +  var m = getObject();
    +  assertEquals('getValues, The values should be 0,1,2',
    +      '0,1,2,3', goog.object.getValues(m).join(','));
    +}
    +
    +function testGetAnyKey() {
    +  var m = getObject();
    +  assertTrue('getAnyKey, The key should be a,b,c or d',
    +             goog.object.getAnyKey(m) in m);
    +  assertUndefined('getAnyKey, The key should be undefined',
    +                  goog.object.getAnyKey({}));
    +}
    +
    +function testGetAnyValue() {
    +  var m = getObject();
    +  assertTrue('getAnyValue, The value should be 0,1,2 or 3',
    +             goog.object.containsValue(m, goog.object.getAnyValue(m)));
    +  assertUndefined('getAnyValue, The value should be undefined',
    +                  goog.object.getAnyValue({}));
    +}
    +
    +function testContainsKey() {
    +  var m = getObject();
    +  assertTrue("containsKey, Should contain the 'a' key",
    +             goog.object.containsKey(m, 'a'));
    +  assertFalse("containsKey, Should not contain the 'e' key",
    +              goog.object.containsKey(m, 'e'));
    +}
    +
    +function testContainsValue() {
    +  var m = getObject();
    +  assertTrue('containsValue, Should contain the value 0',
    +             goog.object.containsValue(m, 0));
    +  assertFalse('containsValue, Should not contain the value 4',
    +              goog.object.containsValue(m, 4));
    +  assertTrue('isEmpty, The map should not be empty', !goog.object.isEmpty(m));
    +}
    +
    +function testFindKey() {
    +  var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
    +  var key = goog.object.findKey(dict, function(v, k, d) {
    +    assertEquals('valid 3rd argument', dict, d);
    +    assertTrue('valid 1st argument', goog.object.containsValue(d, v));
    +    assertTrue('valid 2nd argument', k in d);
    +    return v % 3 == 0;
    +  });
    +  assertEquals('key "c" found', 'c', key);
    +
    +  var pred = function(value) {
    +    return value > 5;
    +  };
    +  assertUndefined('no match', goog.object.findKey(dict, pred));
    +}
    +
    +function testFindValue() {
    +  var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
    +  var value = goog.object.findValue(dict, function(v, k, d) {
    +    assertEquals('valid 3rd argument', dict, d);
    +    assertTrue('valid 1st argument', goog.object.containsValue(d, v));
    +    assertTrue('valid 2nd argument', k in d);
    +    return k.toUpperCase() == 'C';
    +  });
    +  assertEquals('value 3 found', 3, value);
    +
    +  var pred = function(value, key) {
    +    return key > 'd';
    +  };
    +  assertUndefined('no match', goog.object.findValue(dict, pred));
    +}
    +
    +function testClear() {
    +  var m = getObject();
    +  goog.object.clear(m);
    +  assertTrue('cleared so it should be empty', goog.object.isEmpty(m));
    +  assertFalse("cleared so it should not contain 'a' key",
    +              goog.object.containsKey(m, 'a'));
    +}
    +
    +function testClone() {
    +  var m = getObject();
    +  var m2 = goog.object.clone(m);
    +  assertFalse('clone so it should not be empty', goog.object.isEmpty(m2));
    +  assertTrue("clone so it should contain 'c' key",
    +             goog.object.containsKey(m2, 'c'));
    +}
    +
    +function testUnsafeClonePrimitive() {
    +  assertEquals('cloning a primitive should return an equal primitive',
    +      5, goog.object.unsafeClone(5));
    +}
    +
    +function testUnsafeCloneObjectThatHasACloneMethod() {
    +  var original = {
    +    name: 'original',
    +    clone: goog.functions.constant({name: 'clone'})
    +  };
    +
    +  var clone = goog.object.unsafeClone(original);
    +  assertEquals('original', original.name);
    +  assertEquals('clone', clone.name);
    +}
    +
    +function testUnsafeCloneFlatObject() {
    +  var original = {a: 1, b: 2, c: 3};
    +  var clone = goog.object.unsafeClone(original);
    +  assertNotEquals(original, clone);
    +  assertObjectEquals(original, clone);
    +}
    +
    +function testUnsafeCloneDeepObject() {
    +  var original = {
    +    a: 1,
    +    b: {c: 2, d: 3},
    +    e: {f: {g: 4, h: 5}}
    +  };
    +  var clone = goog.object.unsafeClone(original);
    +
    +  assertNotEquals(original, clone);
    +  assertNotEquals(original.b, clone.b);
    +  assertNotEquals(original.e, clone.e);
    +
    +  assertEquals(1, clone.a);
    +  assertEquals(2, clone.b.c);
    +  assertEquals(3, clone.b.d);
    +  assertEquals(4, clone.e.f.g);
    +  assertEquals(5, clone.e.f.h);
    +}
    +
    +function testUnsafeCloneFunctions() {
    +  var original = {
    +    f: goog.functions.constant('hi')
    +  };
    +  var clone = goog.object.unsafeClone(original);
    +
    +  assertNotEquals(original, clone);
    +  assertEquals('hi', clone.f());
    +  assertEquals(original.f, clone.f);
    +}
    +
    +function testForEach() {
    +  var m = getObject();
    +  var s = '';
    +  goog.object.forEach(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    s += key + val;
    +  });
    +  assertEquals(s, 'a0b1c2d3');
    +}
    +
    +function testFilter() {
    +  var m = getObject();
    +
    +  var m2 = goog.object.filter(m, function(val, key, m3) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m3);
    +    return val > 1;
    +  });
    +  assertEquals(stringifyObject(m2), 'c2d3');
    +}
    +
    +
    +function testMap() {
    +  var m = getObject();
    +  var m2 = goog.object.map(m, function(val, key, m3) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m3);
    +    return val * val;
    +  });
    +  assertEquals(stringifyObject(m2), 'a0b1c4d9');
    +}
    +
    +function testSome() {
    +  var m = getObject();
    +  var b = goog.object.some(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 1;
    +  });
    +  assertTrue(b);
    +  var b = goog.object.some(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 100;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testEvery() {
    +  var m = getObject();
    +  var b = goog.object.every(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val >= 0;
    +  });
    +  assertTrue(b);
    +  b = goog.object.every(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 1;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testContains() {
    +  var m = getObject();
    +  assertTrue(goog.object.contains(m, 3));
    +  assertFalse(goog.object.contains(m, 4));
    +}
    +
    +function testObjectProperties() {
    +  var m = {};
    +
    +  goog.object.set(m, 'toString', 'once');
    +  goog.object.set(m, 'valueOf', 'upon');
    +  goog.object.set(m, 'eval', 'a');
    +  goog.object.set(m, 'toSource', 'midnight');
    +  goog.object.set(m, 'prototype', 'dreary');
    +  goog.object.set(m, 'hasOwnProperty', 'dark');
    +
    +  assertEquals(goog.object.get(m, 'toString'), 'once');
    +  assertEquals(goog.object.get(m, 'valueOf'), 'upon');
    +  assertEquals(goog.object.get(m, 'eval'), 'a');
    +  assertEquals(goog.object.get(m, 'toSource'), 'midnight');
    +  assertEquals(goog.object.get(m, 'prototype'), 'dreary');
    +  assertEquals(goog.object.get(m, 'hasOwnProperty'), 'dark');
    +}
    +
    +function testSetDefault() {
    +  var dict = {};
    +  assertEquals(1, goog.object.setIfUndefined(dict, 'a', 1));
    +  assertEquals(1, dict['a']);
    +  assertEquals(1, goog.object.setIfUndefined(dict, 'a', 2));
    +  assertEquals(1, dict['a']);
    +}
    +
    +function createRecordedGetFoo() {
    +  return goog.testing.recordFunction(goog.functions.constant('foo'));
    +}
    +
    +function testSetWithReturnValueNotSet_KeyIsSet() {
    +  var f = createRecordedGetFoo();
    +  var obj = {};
    +  obj['key'] = 'bar';
    +  assertEquals(
    +      'bar',
    +      goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
    +  f.assertCallCount(0);
    +}
    +
    +function testSetWithReturnValueNotSet_KeyIsNotSet() {
    +  var f = createRecordedGetFoo();
    +  var obj = {};
    +  assertEquals(
    +      'foo',
    +      goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
    +  f.assertCallCount(1);
    +}
    +
    +function testSetWithReturnValueNotSet_KeySetValueIsUndefined() {
    +  var f = createRecordedGetFoo();
    +  var obj = {};
    +  obj['key'] = undefined;
    +  assertEquals(
    +      undefined,
    +      goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
    +  f.assertCallCount(0);
    +}
    +
    +function testTranspose() {
    +  var m = getObject();
    +  var b = goog.object.transpose(m);
    +  assertEquals('a', b[0]);
    +  assertEquals('b', b[1]);
    +  assertEquals('c', b[2]);
    +  assertEquals('d', b[3]);
    +}
    +
    +function testExtend() {
    +  var o = {};
    +  var o2 = {a: 0, b: 1};
    +  goog.object.extend(o, o2);
    +  assertEquals(0, o.a);
    +  assertEquals(1, o.b);
    +  assertTrue('a' in o);
    +  assertTrue('b' in o);
    +
    +  o2 = {c: 2};
    +  goog.object.extend(o, o2);
    +  assertEquals(2, o.c);
    +  assertTrue('c' in o);
    +
    +  o2 = {c: 3};
    +  goog.object.extend(o, o2);
    +  assertEquals(3, o.c);
    +  assertTrue('c' in o);
    +
    +  o = {};
    +  o2 = {c: 2};
    +  var o3 = {c: 3};
    +  goog.object.extend(o, o2, o3);
    +  assertEquals(3, o.c);
    +  assertTrue('c' in o);
    +
    +  o = {};
    +  o2 = {a: 0, b: 1};
    +  o3 = {c: 2, d: 3};
    +  goog.object.extend(o, o2, o3);
    +  assertEquals(0, o.a);
    +  assertEquals(1, o.b);
    +  assertEquals(2, o.c);
    +  assertEquals(3, o.d);
    +  assertTrue('a' in o);
    +  assertTrue('b' in o);
    +  assertTrue('c' in o);
    +  assertTrue('d' in o);
    +
    +  o = {};
    +  o2 = {
    +    'constructor': 0,
    +    'hasOwnProperty': 1,
    +    'isPrototypeOf': 2,
    +    'propertyIsEnumerable': 3,
    +    'toLocaleString': 4,
    +    'toString': 5,
    +    'valueOf': 6
    +  };
    +  goog.object.extend(o, o2);
    +  assertEquals(0, o['constructor']);
    +  assertEquals(1, o['hasOwnProperty']);
    +  assertEquals(2, o['isPrototypeOf']);
    +  assertEquals(3, o['propertyIsEnumerable']);
    +  assertEquals(4, o['toLocaleString']);
    +  assertEquals(5, o['toString']);
    +  assertEquals(6, o['valueOf']);
    +  assertTrue('constructor' in o);
    +  assertTrue('hasOwnProperty' in o);
    +  assertTrue('isPrototypeOf' in o);
    +  assertTrue('propertyIsEnumerable' in o);
    +  assertTrue('toLocaleString' in o);
    +  assertTrue('toString' in o);
    +  assertTrue('valueOf' in o);
    +}
    +
    +function testCreate() {
    +  assertObjectEquals('With multiple arguments',
    +                     {a: 0, b: 1}, goog.object.create('a', 0, 'b', 1));
    +  assertObjectEquals('With an array argument',
    +                     {a: 0, b: 1}, goog.object.create(['a', 0, 'b', 1]));
    +
    +  assertObjectEquals('With no arguments',
    +                     {}, goog.object.create());
    +  assertObjectEquals('With an ampty array argument',
    +                     {}, goog.object.create([]));
    +
    +  assertThrows('Should throw due to uneven arguments', function() {
    +    goog.object.create('a');
    +  });
    +  assertThrows('Should throw due to uneven arguments', function() {
    +    goog.object.create('a', 0, 'b');
    +  });
    +  assertThrows('Should throw due to uneven length array', function() {
    +    goog.object.create(['a']);
    +  });
    +  assertThrows('Should throw due to uneven length array', function() {
    +    goog.object.create(['a', 0, 'b']);
    +  });
    +}
    +
    +function testCreateSet() {
    +  assertObjectEquals('With multiple arguments',
    +                     {a: true, b: true}, goog.object.createSet('a', 'b'));
    +  assertObjectEquals('With an array argument',
    +                     {a: true, b: true}, goog.object.createSet(['a', 'b']));
    +
    +  assertObjectEquals('With no arguments',
    +                     {}, goog.object.createSet());
    +  assertObjectEquals('With an ampty array argument',
    +                     {}, goog.object.createSet([]));
    +}
    +
    +function createTestDeepObject() {
    +  var obj = {};
    +  obj.a = {};
    +  obj.a.b = {};
    +  obj.a.b.c = {};
    +  obj.a.b.c.fooArr = [5, 6, 7, 8];
    +  obj.a.b.c.knownNull = null;
    +  return obj;
    +}
    +
    +function testGetValueByKeys() {
    +  var obj = createTestDeepObject();
    +  assertEquals(obj, goog.object.getValueByKeys(obj));
    +  assertEquals(obj.a, goog.object.getValueByKeys(obj, 'a'));
    +  assertEquals(obj.a.b, goog.object.getValueByKeys(obj, 'a', 'b'));
    +  assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, 'a', 'b', 'c'));
    +  assertEquals(obj.a.b.c.d,
    +               goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'd'));
    +  assertEquals(8, goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'fooArr', 3));
    +  assertNull(goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'knownNull'));
    +  assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g'));
    +}
    +
    +function testGetValueByKeysArraySyntax() {
    +  var obj = createTestDeepObject();
    +  assertEquals(obj, goog.object.getValueByKeys(obj, []));
    +  assertEquals(obj.a, goog.object.getValueByKeys(obj, ['a']));
    +
    +  assertEquals(obj.a.b, goog.object.getValueByKeys(obj, ['a', 'b']));
    +  assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, ['a', 'b', 'c']));
    +  assertEquals(obj.a.b.c.d,
    +      goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'd']));
    +  assertEquals(8,
    +      goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'fooArr', 3]));
    +  assertNull(goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'knownNull']));
    +  assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g'));
    +}
    +
    +function testImmutableView() {
    +  if (!Object.isFrozen) {
    +    return;
    +  }
    +  var x = {propA: 3};
    +  var y = goog.object.createImmutableView(x);
    +  x.propA = 4;
    +  x.propB = 6;
    +  y.propA = 5;
    +  y.propB = 7;
    +  assertEquals(4, x.propA);
    +  assertEquals(6, x.propB);
    +  assertFalse(goog.object.isImmutableView(x));
    +
    +  assertEquals(4, y.propA);
    +  assertEquals(6, y.propB);
    +  assertTrue(goog.object.isImmutableView(y));
    +
    +  assertFalse('x and y should be different references', x == y);
    +  assertTrue(
    +      'createImmutableView should not create a new view of an immutable object',
    +      y == goog.object.createImmutableView(y));
    +}
    +
    +function testImmutableViewStrict() {
    +  'use strict';
    +
    +  // IE9 supports isFrozen, but does not support strict mode. Exit early if we
    +  // are not actually running in strict mode.
    +  var isStrict = (function() { return !this; })();
    +
    +  if (!Object.isFrozen || !isStrict) {
    +    return;
    +  }
    +  var x = {propA: 3};
    +  var y = goog.object.createImmutableView(x);
    +  assertThrows(function() {
    +    y.propA = 4;
    +  });
    +  assertThrows(function() {
    +    y.propB = 4;
    +  });
    +}
    +
    +function testEmptyObjectsAreEqual() {
    +  assertTrue(goog.object.equals({}, {}));
    +}
    +
    +function testObjectsWithDifferentKeysAreUnequal() {
    +  assertFalse(goog.object.equals({'a': 1}, {'b': 1}));
    +}
    +
    +function testObjectsWithDifferentValuesAreUnequal() {
    +  assertFalse(goog.object.equals({'a': 1}, {'a': 2}));
    +}
    +
    +function testObjectsWithSameKeysAndValuesAreEqual() {
    +  assertTrue(goog.object.equals({'a': 1}, {'a': 1}));
    +}
    +
    +function testObjectsWithSameKeysInDifferentOrderAreEqual() {
    +  assertTrue(goog.object.equals({'a': 1, 'b': 2}, {'b': 2, 'a': 1}));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/absoluteposition.js b/mapbox-gl/closure-library/closure/goog/positioning/absoluteposition.js
    new file mode 100644
    index 0000000000..c5ff190377
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/absoluteposition.js
    @@ -0,0 +1,73 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Client viewport positioning class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.AbsolutePosition');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AbstractPosition');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup absolutely positioned by
    + * setting the left/top style elements directly to the specified values.
    + * The position is generally relative to the element's offsetParent. Normally,
    + * this is the document body, but can be another element if the popup element
    + * is scoped by an element with relative position.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + */
    +goog.positioning.AbsolutePosition = function(arg1, opt_arg2) {
    +  /**
    +   * Coordinate to position popup at.
    +   * @type {goog.math.Coordinate}
    +   */
    +  this.coordinate = arg1 instanceof goog.math.Coordinate ? arg1 :
    +      new goog.math.Coordinate(/** @type {number} */ (arg1), opt_arg2);
    +};
    +goog.inherits(goog.positioning.AbsolutePosition,
    +              goog.positioning.AbstractPosition);
    +
    +
    +/**
    + * Repositions the popup according to the current state.
    + *
    + * @param {Element} movableElement The DOM element to position.
    + * @param {goog.positioning.Corner} movableCorner The corner of the movable
    + *     element that should be positioned at the specified position.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize Prefered size of the
    + *     movableElement.
    + * @override
    + */
    +goog.positioning.AbsolutePosition.prototype.reposition = function(
    +    movableElement, movableCorner, opt_margin, opt_preferredSize) {
    +  goog.positioning.positionAtCoordinate(this.coordinate,
    +                                        movableElement,
    +                                        movableCorner,
    +                                        opt_margin,
    +                                        null,
    +                                        null,
    +                                        opt_preferredSize);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/abstractposition.js b/mapbox-gl/closure-library/closure/goog/positioning/abstractposition.js
    new file mode 100644
    index 0000000000..439c4f18b7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/abstractposition.js
    @@ -0,0 +1,44 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Abstract base class for positioning implementations.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.AbstractPosition');
    +
    +
    +
    +/**
    + * Abstract position object. Encapsulates position and overflow handling.
    + *
    + * @constructor
    + */
    +goog.positioning.AbstractPosition = function() {};
    +
    +
    +/**
    + * Repositions the element. Abstract method, should be overloaded.
    + *
    + * @param {Element} movableElement Element to position.
    + * @param {goog.positioning.Corner} corner Corner of the movable element that
    + *     should be positioned adjacent to the anchored element.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize PreferredSize of the
    + *     movableElement.
    + */
    +goog.positioning.AbstractPosition.prototype.reposition =
    +    function(movableElement, corner, opt_margin, opt_preferredSize) { };
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition.js b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition.js
    new file mode 100644
    index 0000000000..7374860a8d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition.js
    @@ -0,0 +1,92 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Client positioning class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.AnchoredPosition');
    +
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AbstractPosition');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is anchored at a corner of
    + * an element.
    + *
    + * When using AnchoredPosition, it is recommended that the popup element
    + * specified in the Popup constructor or Popup.setElement be absolutely
    + * positioned.
    + *
    + * @param {Element} anchorElement Element the movable element should be
    + *     anchored against.
    + * @param {goog.positioning.Corner} corner Corner of anchored element the
    + *     movable element should be positioned at.
    + * @param {number=} opt_overflow Overflow handling mode. Defaults to IGNORE if
    + *     not specified. Bitmap, {@see goog.positioning.Overflow}.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + */
    +goog.positioning.AnchoredPosition = function(anchorElement,
    +                                             corner,
    +                                             opt_overflow) {
    +  /**
    +   * Element the movable element should be anchored against.
    +   * @type {Element}
    +   */
    +  this.element = anchorElement;
    +
    +  /**
    +   * Corner of anchored element the movable element should be positioned at.
    +   * @type {goog.positioning.Corner}
    +   */
    +  this.corner = corner;
    +
    +  /**
    +   * Overflow handling mode. Defaults to IGNORE if not specified.
    +   * Bitmap, {@see goog.positioning.Overflow}.
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.overflow_ = opt_overflow;
    +};
    +goog.inherits(goog.positioning.AnchoredPosition,
    +              goog.positioning.AbstractPosition);
    +
    +
    +/**
    + * Repositions the movable element.
    + *
    + * @param {Element} movableElement Element to position.
    + * @param {goog.positioning.Corner} movableCorner Corner of the movable element
    + *     that should be positioned adjacent to the anchored element.
    + * @param {goog.math.Box=} opt_margin A margin specifin pixels.
    + * @param {goog.math.Size=} opt_preferredSize PreferredSize of the
    + *     movableElement (unused in this class).
    + * @override
    + */
    +goog.positioning.AnchoredPosition.prototype.reposition = function(
    +    movableElement, movableCorner, opt_margin, opt_preferredSize) {
    +  goog.positioning.positionAtAnchor(this.element,
    +                                    this.corner,
    +                                    movableElement,
    +                                    movableCorner,
    +                                    undefined,
    +                                    opt_margin,
    +                                    this.overflow_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.html b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.html
    new file mode 100644
    index 0000000000..e3c813cf13
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE HTML>
    +<!--
    +
    +-->
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.positioning.AnchoredPosition
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.positioning.AnchoredPositionTest');
    +  </script>
    + </head>
    + <body>
    +  <!-- Use IFRAME to avoid non-deterministic window size problems in Selenium. -->
    +  <iframe id="frame1" style="width:200px; height:200px;" src="anchoredviewportposition_test_iframe.html">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.js b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.js
    new file mode 100644
    index 0000000000..4603b7d3d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredposition_test.js
    @@ -0,0 +1,68 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.positioning.AnchoredPositionTest');
    +goog.setTestOnly('goog.positioning.AnchoredPositionTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +
    +var frame, doc, dom, viewportSize, anchor, popup;
    +var corner = goog.positioning.Corner;
    +var popupLength = 20;
    +var anchorLength = 100;
    +
    +function setUp() {
    +  frame = document.getElementById('frame1');
    +  doc = goog.dom.getFrameContentDocument(frame);
    +  dom = goog.dom.getDomHelper(doc);
    +  viewportSize = dom.getViewportSize();
    +  anchor = dom.getElement('anchor');
    +  popup = dom.getElement('popup');
    +  goog.style.setSize(popup, popupLength, popupLength);
    +  goog.style.setPosition(popup, popupLength, popupLength);
    +  goog.style.setSize(anchor, anchorLength, anchorLength);
    +}
    +
    +// No enough space at the bottom and no overflow adjustment.
    +function testRepositionWithDefaultOverflow() {
    +  var avp = new goog.positioning.AnchoredPosition(
    +      anchor, corner.BOTTOM_LEFT);
    +  var newTop = viewportSize.height - anchorLength;
    +  goog.style.setPosition(anchor, 50, newTop);
    +  var anchorRect = goog.style.getBounds(anchor);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.top + anchorRect.height, popupRect.top);
    +}
    +
    +// No enough space at the bottom and ADJUST_Y overflow adjustment.
    +function testRepositionWithOverflow() {
    +  var avp = new goog.positioning.AnchoredPosition(
    +      anchor, corner.BOTTOM_LEFT,
    +      goog.positioning.Overflow.ADJUST_Y);
    +  var newTop = viewportSize.height - anchorLength;
    +  goog.style.setPosition(anchor, 50, newTop);
    +  var anchorRect = goog.style.getBounds(anchor);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.top + anchorRect.height,
    +      popupRect.top + popupRect.height);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition.js b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition.js
    new file mode 100644
    index 0000000000..dfc7c1ed27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition.js
    @@ -0,0 +1,189 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Anchored viewport positioning class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.AnchoredViewportPosition');
    +
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.OverflowStatus');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is anchored at a corner of
    + * an element. The corners are swapped if dictated by the viewport. For instance
    + * if a popup is anchored with its top left corner to the bottom left corner of
    + * the anchor the popup is either displayed below the anchor (as specified) or
    + * above it if there's not enough room to display it below.
    + *
    + * When using this positioning object it's recommended that the movable element
    + * be absolutely positioned.
    + *
    + * @param {Element} anchorElement Element the movable element should be
    + *     anchored against.
    + * @param {goog.positioning.Corner} corner Corner of anchored element the
    + *     movable element should be positioned at.
    + * @param {boolean=} opt_adjust Whether the positioning should be adjusted until
    + *     the element fits inside the viewport even if that means that the anchored
    + *     corners are ignored.
    + * @param {goog.math.Box=} opt_overflowConstraint Box object describing the
    + *     dimensions in which the movable element could be shown.
    + * @constructor
    + * @extends {goog.positioning.AnchoredPosition}
    + */
    +goog.positioning.AnchoredViewportPosition = function(anchorElement,
    +                                                     corner,
    +                                                     opt_adjust,
    +                                                     opt_overflowConstraint) {
    +  goog.positioning.AnchoredPosition.call(this, anchorElement, corner);
    +
    +  /**
    +   * The last resort algorithm to use if the algorithm can't fit inside
    +   * the viewport.
    +   *
    +   * IGNORE = do nothing, just display at the preferred position.
    +   *
    +   * ADJUST_X | ADJUST_Y = Adjust until the element fits, even if that means
    +   * that the anchored corners are ignored.
    +   *
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastResortOverflow_ = opt_adjust ?
    +      (goog.positioning.Overflow.ADJUST_X |
    +       goog.positioning.Overflow.ADJUST_Y) :
    +      goog.positioning.Overflow.IGNORE;
    +
    +  /**
    +   * The dimensions in which the movable element could be shown.
    +   * @type {goog.math.Box|undefined}
    +   * @private
    +   */
    +  this.overflowConstraint_ = opt_overflowConstraint || undefined;
    +};
    +goog.inherits(goog.positioning.AnchoredViewportPosition,
    +              goog.positioning.AnchoredPosition);
    +
    +
    +/**
    + * @return {goog.math.Box|undefined} The box object describing the
    + *     dimensions in which the movable element will be shown.
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.getOverflowConstraint =
    +    function() {
    +  return this.overflowConstraint_;
    +};
    +
    +
    +/**
    + * @param {goog.math.Box|undefined} overflowConstraint Box object describing the
    + *     dimensions in which the movable element could be shown.
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.setOverflowConstraint =
    +    function(overflowConstraint) {
    +  this.overflowConstraint_ = overflowConstraint;
    +};
    +
    +
    +/**
    + * @return {number} A bitmask for the "last resort" overflow.
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.getLastResortOverflow =
    +    function() {
    +  return this.lastResortOverflow_;
    +};
    +
    +
    +/**
    + * @param {number} lastResortOverflow A bitmask for the "last resort" overflow,
    + *     if we fail to fit the element on-screen.
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.setLastResortOverflow =
    +    function(lastResortOverflow) {
    +  this.lastResortOverflow_ = lastResortOverflow;
    +};
    +
    +
    +/**
    + * Repositions the movable element.
    + *
    + * @param {Element} movableElement Element to position.
    + * @param {goog.positioning.Corner} movableCorner Corner of the movable element
    + *     that should be positioned adjacent to the anchored element.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize The preferred size of the
    + *     movableElement.
    + * @override
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.reposition = function(
    +    movableElement, movableCorner, opt_margin, opt_preferredSize) {
    +  var status = goog.positioning.positionAtAnchor(this.element, this.corner,
    +      movableElement, movableCorner, null, opt_margin,
    +      goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y,
    +      opt_preferredSize, this.overflowConstraint_);
    +
    +  // If the desired position is outside the viewport try mirroring the corners
    +  // horizontally or vertically.
    +  if (status & goog.positioning.OverflowStatus.FAILED) {
    +    var cornerFallback = this.adjustCorner(status, this.corner);
    +    var movableCornerFallback = this.adjustCorner(status, movableCorner);
    +
    +    status = goog.positioning.positionAtAnchor(this.element, cornerFallback,
    +        movableElement, movableCornerFallback, null, opt_margin,
    +        goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y,
    +        opt_preferredSize, this.overflowConstraint_);
    +
    +    if (status & goog.positioning.OverflowStatus.FAILED) {
    +      // If that also fails, pick the best corner from the two tries,
    +      // and adjust the position until it fits.
    +      cornerFallback = this.adjustCorner(status, cornerFallback);
    +      movableCornerFallback = this.adjustCorner(
    +          status, movableCornerFallback);
    +
    +      goog.positioning.positionAtAnchor(this.element, cornerFallback,
    +          movableElement, movableCornerFallback, null, opt_margin,
    +          this.getLastResortOverflow(), opt_preferredSize,
    +          this.overflowConstraint_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adjusts the corner if X or Y positioning failed.
    + * @param {number} status The status of the last positionAtAnchor call.
    + * @param {goog.positioning.Corner} corner The corner to adjust.
    + * @return {goog.positioning.Corner} The adjusted corner.
    + * @protected
    + */
    +goog.positioning.AnchoredViewportPosition.prototype.adjustCorner = function(
    +    status, corner) {
    +  if (status & goog.positioning.OverflowStatus.FAILED_HORIZONTAL) {
    +    corner = goog.positioning.flipCornerHorizontal(corner);
    +  }
    +
    +  if (status & goog.positioning.OverflowStatus.FAILED_VERTICAL) {
    +    corner = goog.positioning.flipCornerVertical(corner);
    +  }
    +
    +  return corner;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.html b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.html
    new file mode 100644
    index 0000000000..1f8d326847
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE HTML>
    +<!--
    +
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.positioning.AnchoredViewportPosition
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.positioning.AnchoredViewportPositionTest');
    +  </script>
    + </head>
    + <body>
    +  <!-- Use IFRAME to avoid non-deterministic window size problems in Selenium. -->
    +  <iframe id="frame1" style="width:200px; height:200px;" src="anchoredviewportposition_test_iframe.html">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.js b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.js
    new file mode 100644
    index 0000000000..2f336b3a84
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test.js
    @@ -0,0 +1,165 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.positioning.AnchoredViewportPositionTest');
    +goog.setTestOnly('goog.positioning.AnchoredViewportPositionTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.math.Box');
    +goog.require('goog.positioning.AnchoredViewportPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +
    +var frame, doc, dom, viewportSize, anchor, popup;
    +var corner = goog.positioning.Corner;
    +
    +function setUp() {
    +  frame = document.getElementById('frame1');
    +  doc = goog.dom.getFrameContentDocument(frame);
    +  dom = goog.dom.getDomHelper(doc);
    +  viewportSize = dom.getViewportSize();
    +  anchor = dom.getElement('anchor');
    +  popup = dom.getElement('popup');
    +  goog.style.setSize(popup, 20, 20);
    +}
    +
    +// The frame has enough space at the bottom of the anchor.
    +function testRepositionBottom() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, false);
    +  goog.style.setSize(anchor, 100, 100);
    +  goog.style.setPosition(anchor, 0, 0);
    +  assertTrue(viewportSize.height >= 100 + 20);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  assertEquals(anchorRect.top + anchorRect.height,
    +               goog.style.getPageOffset(popup).y);
    +}
    +
    +// No enough space at the bottom, but at the top.
    +function testRepositionTop() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, false);
    +  var newTop = viewportSize.height - 100;
    +  goog.style.setSize(anchor, 100, 100);
    +  goog.style.setPosition(anchor, 50, newTop);
    +  assertTrue(newTop >= 20);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.top, popupRect.top + popupRect.height);
    +}
    +
    +// Not enough space either at the bottom or right but there is enough space at
    +// top left.
    +function testRepositionBottomRight() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_RIGHT, false);
    +  goog.style.setSize(anchor, 100, 100);
    +  goog.style.setPosition(anchor, viewportSize.width - 110,
    +      viewportSize.height - 110);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.top, popupRect.top + popupRect.height);
    +  assertEquals(anchorRect.left, popupRect.left + popupRect.width);
    +}
    +
    +// Enough space at neither the bottom nor the top.  Adjustment flag is false.
    +function testRepositionNoSpaceWithoutAdjustment() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, false);
    +  goog.style.setPosition(anchor, 50, 10);
    +  goog.style.setSize(anchor, 100, viewportSize.height - 20);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.top + anchorRect.height, popupRect.top);
    +  assertTrue(popupRect.top + popupRect.height > viewportSize.height);
    +}
    +
    +// Enough space at neither the bottom nor the top.  Adjustment flag is true.
    +function testRepositionNoSpaceWithAdjustment() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, true);
    +  goog.style.setPosition(anchor, 50, 10);
    +  goog.style.setSize(anchor, 100, viewportSize.height - 20);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertTrue(anchorRect.top + anchorRect.height > popupRect.top);
    +  assertEquals(viewportSize.height, popupRect.top + popupRect.height);
    +}
    +
    +function testAdjustCorner() {
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT);
    +  assertEquals(corner.BOTTOM_LEFT, avp.adjustCorner(0, corner.BOTTOM_LEFT));
    +  assertEquals(corner.BOTTOM_RIGHT, avp.adjustCorner(
    +      goog.positioning.OverflowStatus.FAILED_HORIZONTAL, corner.BOTTOM_LEFT));
    +  assertEquals(corner.TOP_LEFT, avp.adjustCorner(
    +      goog.positioning.OverflowStatus.FAILED_VERTICAL, corner.BOTTOM_LEFT));
    +  assertEquals(corner.TOP_RIGHT, avp.adjustCorner(
    +      goog.positioning.OverflowStatus.FAILED_VERTICAL |
    +      goog.positioning.OverflowStatus.FAILED_HORIZONTAL,
    +      corner.BOTTOM_LEFT));
    +}
    +
    +// No space to fit, so uses fallback.
    +function testOverflowConstraint() {
    +  var tinyBox = new goog.math.Box(0, 0, 0, 0);
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, false, tinyBox);
    +  assertEquals(tinyBox, avp.getOverflowConstraint());
    +
    +  goog.style.setSize(anchor, 50, 50);
    +  goog.style.setPosition(anchor, 80, 80);
    +  avp.reposition(popup, corner.TOP_LEFT);
    +
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertEquals(anchorRect.left, popupRect.left);
    +  assertEquals(anchorRect.top + anchorRect.height, popupRect.top);
    +}
    +
    +// Initially no space to fit above, then changes to have room.
    +function testChangeOverflowConstraint() {
    +  var tinyBox = new goog.math.Box(0, 0, 0, 0);
    +  var avp = new goog.positioning.AnchoredViewportPosition(
    +      anchor, corner.BOTTOM_LEFT, false, tinyBox);
    +  assertEquals(tinyBox, avp.getOverflowConstraint());
    +
    +  goog.style.setSize(anchor, 50, 50);
    +  goog.style.setPosition(anchor, 80, 80);
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  popupRect = goog.style.getBounds(popup);
    +  assertNotEquals(60, popupRect.top);
    +
    +  var movedBox = new goog.math.Box(60, 100, 100, 60);
    +  avp.setOverflowConstraint(movedBox);
    +  assertEquals(movedBox, avp.getOverflowConstraint());
    +
    +  avp.reposition(popup, corner.TOP_LEFT);
    +  popupRect = goog.style.getBounds(popup);
    +  assertEquals(80, popupRect.left);
    +  assertEquals(60, popupRect.top);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test_iframe.html b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test_iframe.html
    new file mode 100644
    index 0000000000..a9b7e5eb33
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/anchoredviewportposition_test_iframe.html
    @@ -0,0 +1,35 @@
    +<!DOCTYPE HTML>
    +<!--
    +
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <style>
    +    .bar {
    +      position: absolute;
    +      text-align: center;
    +      overflow: hidden;
    +    }
    +
    +    #anchor {
    +      background: blue;
    +    }
    +
    +    #popup {
    +      background: red;
    +    }
    +  </style>
    +</head>
    +<body>
    +  <div id="anchor" class="bar">anchor</div>
    +  <div id="popup" class="bar">popup</div>
    +</body>
    +</html>
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/clientposition.js b/mapbox-gl/closure-library/closure/goog/positioning/clientposition.js
    new file mode 100644
    index 0000000000..05e4f01e14
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/clientposition.js
    @@ -0,0 +1,89 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Client positioning class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.positioning.ClientPosition');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AbstractPosition');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned relative to the
    + * window (client) coordinates. This calculates the correct position to
    + * use even if the element is relatively positioned to some other element. This
    + * is for trying to position an element at the spot of the mouse cursor in
    + * a MOUSEMOVE event. Just use the event.clientX and event.clientY as the
    + * parameters.
    + *
    + * @param {number|goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + */
    +goog.positioning.ClientPosition = function(arg1, opt_arg2) {
    +  /**
    +   * Coordinate to position popup at.
    +   * @type {goog.math.Coordinate}
    +   */
    +  this.coordinate = arg1 instanceof goog.math.Coordinate ? arg1 :
    +      new goog.math.Coordinate(/** @type {number} */ (arg1), opt_arg2);
    +};
    +goog.inherits(goog.positioning.ClientPosition,
    +              goog.positioning.AbstractPosition);
    +
    +
    +/**
    + * Repositions the popup according to the current state
    + *
    + * @param {Element} movableElement The DOM element of the popup.
    + * @param {goog.positioning.Corner} movableElementCorner The corner of
    + *     the popup element that that should be positioned adjacent to
    + *     the anchorElement.  One of the goog.positioning.Corner
    + *     constants.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize Preferred size of the element.
    + * @override
    + */
    +goog.positioning.ClientPosition.prototype.reposition = function(
    +    movableElement, movableElementCorner, opt_margin, opt_preferredSize) {
    +  goog.asserts.assert(movableElement);
    +
    +  // Translates the coordinate to be relative to the page.
    +  var viewportOffset = goog.style.getViewportPageOffset(
    +      goog.dom.getOwnerDocument(movableElement));
    +  var x = this.coordinate.x + viewportOffset.x;
    +  var y = this.coordinate.y + viewportOffset.y;
    +
    +  // Translates the coordinate to be relative to the offset parent.
    +  var movableParentTopLeft =
    +      goog.positioning.getOffsetParentPageOffset(movableElement);
    +  x -= movableParentTopLeft.x;
    +  y -= movableParentTopLeft.y;
    +
    +  goog.positioning.positionAtCoordinate(
    +      new goog.math.Coordinate(x, y), movableElement, movableElementCorner,
    +      opt_margin, null, null, opt_preferredSize);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.html b/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.html
    new file mode 100644
    index 0000000000..b2c26bf933
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.positioning.ClientPosition</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.positioning.clientPositionTest');
    +</script>
    +</head>
    +<body>
    +<div id="test-area"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.js b/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.js
    new file mode 100644
    index 0000000000..9e63476b5d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/clientposition_test.js
    @@ -0,0 +1,124 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * Tests for {@code goog.positioning.ClientPosition}
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.positioning.clientPositionTest');
    +goog.setTestOnly('goog.positioning.clientPositionTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.positioning.ClientPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Prefabricated popup element for convenient. This is created during
    + * setUp and is not attached to the document at the beginning of the
    + * test.
    + * @type {Element}
    + */
    +var popupElement;
    +var testArea;
    +var POPUP_HEIGHT = 100;
    +var POPUP_WIDTH = 150;
    +
    +
    +function setUp() {
    +  testArea = goog.dom.getElement('test-area');
    +
    +  // Enlarges the test area to 5000x5000px so that we can be confident
    +  // that scrolling the document to some small (x,y) value would work.
    +  goog.style.setSize(testArea, 5000, 5000);
    +
    +  window.scrollTo(0, 0);
    +
    +  popupElement = goog.dom.createDom('div');
    +  goog.style.setSize(popupElement, POPUP_WIDTH, POPUP_HEIGHT);
    +  popupElement.style.position = 'absolute';
    +
    +  // For ease of debugging.
    +  popupElement.style.background = 'blue';
    +}
    +
    +
    +function tearDown() {
    +  popupElement = null;
    +  testArea.innerHTML = '';
    +  testArea.setAttribute('style', '');
    +}
    +
    +
    +function testClientPositionWithZeroViewportOffset() {
    +  goog.dom.appendChild(testArea, popupElement);
    +
    +  var x = 300;
    +  var y = 200;
    +  var pos = new goog.positioning.ClientPosition(x, y);
    +
    +  pos.reposition(popupElement, goog.positioning.Corner.TOP_LEFT);
    +  assertPageOffset(x, y, popupElement);
    +
    +  pos.reposition(popupElement, goog.positioning.Corner.TOP_RIGHT);
    +  assertPageOffset(x - POPUP_WIDTH, y, popupElement);
    +
    +  pos.reposition(popupElement, goog.positioning.Corner.BOTTOM_LEFT);
    +  assertPageOffset(x, y - POPUP_HEIGHT, popupElement);
    +
    +  pos.reposition(popupElement, goog.positioning.Corner.BOTTOM_RIGHT);
    +  assertPageOffset(x - POPUP_WIDTH, y - POPUP_HEIGHT, popupElement);
    +}
    +
    +
    +function testClientPositionWithSomeViewportOffset() {
    +  goog.dom.appendChild(testArea, popupElement);
    +
    +  var x = 300;
    +  var y = 200;
    +  var scrollX = 135;
    +  var scrollY = 270;
    +  window.scrollTo(scrollX, scrollY);
    +
    +  var pos = new goog.positioning.ClientPosition(x, y);
    +  pos.reposition(popupElement, goog.positioning.Corner.TOP_LEFT);
    +  assertPageOffset(scrollX + x, scrollY + y, popupElement);
    +}
    +
    +
    +function testClientPositionWithPositionContext() {
    +  var contextAbsoluteX = 90;
    +  var contextAbsoluteY = 110;
    +  var x = 300;
    +  var y = 200;
    +
    +  var contextElement = goog.dom.createDom('div', undefined, popupElement);
    +  goog.style.setPosition(contextElement, contextAbsoluteX, contextAbsoluteY);
    +  contextElement.style.position = 'absolute';
    +  goog.dom.appendChild(testArea, contextElement);
    +
    +  var pos = new goog.positioning.ClientPosition(x, y);
    +  pos.reposition(popupElement, goog.positioning.Corner.TOP_LEFT);
    +  assertPageOffset(x, y, popupElement);
    +}
    +
    +
    +function assertPageOffset(expectedX, expectedY, el) {
    +  var offsetCoordinate = goog.style.getPageOffset(el);
    +  assertEquals('x-coord page offset is wrong.', expectedX, offsetCoordinate.x);
    +  assertEquals('y-coord page offset is wrong.', expectedY, offsetCoordinate.y);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition.js b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition.js
    new file mode 100644
    index 0000000000..652e62c347
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition.js
    @@ -0,0 +1,66 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Anchored viewport positioning class with both adjust and
    + *     resize options for the popup.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.MenuAnchoredPosition');
    +
    +goog.require('goog.positioning.AnchoredViewportPosition');
    +goog.require('goog.positioning.Overflow');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is anchored at a corner of
    + * an element.  The positioning behavior changes based on the values of
    + * opt_adjust and opt_resize.
    + *
    + * When using this positioning object it's recommended that the movable element
    + * be absolutely positioned.
    + *
    + * @param {Element} anchorElement Element the movable element should be
    + *     anchored against.
    + * @param {goog.positioning.Corner} corner Corner of anchored element the
    + *     movable element should be positioned at.
    + * @param {boolean=} opt_adjust Whether the positioning should be adjusted until
    + *     the element fits inside the viewport even if that means that the anchored
    + *     corners are ignored.
    + * @param {boolean=} opt_resize Whether the positioning should be adjusted until
    + *     the element fits inside the viewport on the X axis and its height is
    + *     resized so if fits in the viewport. This take precedence over opt_adjust.
    + * @constructor
    + * @extends {goog.positioning.AnchoredViewportPosition}
    + */
    +goog.positioning.MenuAnchoredPosition = function(anchorElement,
    +                                                 corner,
    +                                                 opt_adjust,
    +                                                 opt_resize) {
    +  goog.positioning.AnchoredViewportPosition.call(this, anchorElement, corner,
    +                                                 opt_adjust || opt_resize);
    +
    +  if (opt_adjust || opt_resize) {
    +    var overflowX = goog.positioning.Overflow.ADJUST_X_EXCEPT_OFFSCREEN;
    +    var overflowY = opt_resize ?
    +        goog.positioning.Overflow.RESIZE_HEIGHT :
    +        goog.positioning.Overflow.ADJUST_Y_EXCEPT_OFFSCREEN;
    +    this.setLastResortOverflow(overflowX | overflowY);
    +  }
    +};
    +goog.inherits(goog.positioning.MenuAnchoredPosition,
    +              goog.positioning.AnchoredViewportPosition);
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.html b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.html
    new file mode 100644
    index 0000000000..c90693ec85
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.html
    @@ -0,0 +1,39 @@
    +<!DOCTYPE HTML>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.positioning.MenuAnchoredPosition
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.positioning.MenuAnchoredPositionTest');
    +  </script>
    + </head>
    + <!-- Force offscreen menus to count as FAIL_X -->
    + <body style="overflow: hidden">
    +  <div id="offscreen-anchor" style="position: absolute; left: -1000px; top: -1000px">
    +  </div>
    +  <div id="onscreen-anchor" style="position: absolute; left: 5px; top: 5px">
    +  </div>
    +  <!-- The x and y positon of this anchor will be reset on each setUp -->
    +  <div id="custom-anchor" style="position: absolute;">
    +  </div>
    +  <div id="menu" style="position: absolute; left: 20px; top: 20px">
    +   Menu Item 1
    +   <br />
    +   Menu Item 2
    +   <br />
    +   Menu Item 3
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.js b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.js
    new file mode 100644
    index 0000000000..41015cab67
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/menuanchoredposition_test.js
    @@ -0,0 +1,95 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.positioning.MenuAnchoredPositionTest');
    +goog.setTestOnly('goog.positioning.MenuAnchoredPositionTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.MenuAnchoredPosition');
    +goog.require('goog.testing.jsunit');
    +
    +var offscreenAnchor;
    +var onscreenAnchor;
    +var customAnchor;
    +var menu;
    +var corner = goog.positioning.Corner;
    +var savedMenuHtml;
    +
    +function setUp() {
    +  offscreenAnchor = goog.dom.getElement('offscreen-anchor');
    +  onscreenAnchor = goog.dom.getElement('onscreen-anchor');
    +  customAnchor = goog.dom.getElement('custom-anchor');
    +  customAnchor.style.left = '0';
    +  customAnchor.style.top = '0';
    +
    +  menu = goog.dom.getElement('menu');
    +  savedMenuHtml = menu.innerHTML;
    +  menu.style.left = '20px';
    +  menu.style.top = '20px';
    +}
    +
    +function tearDown() {
    +  menu.innerHTML = savedMenuHtml;
    +}
    +
    +function testRepositionWithAdjustAndOnscreenAnchor() {
    +  // Add so many children that it can't possibly fit onscreen.
    +  for (var i = 0; i < 200; i++) {
    +    menu.appendChild(goog.dom.createDom('div', null, 'New Item ' + i));
    +  }
    +
    +  var pos = new goog.positioning.MenuAnchoredPosition(
    +      onscreenAnchor, corner.TOP_LEFT, true);
    +  pos.reposition(menu, corner.TOP_LEFT);
    +
    +  var offset = 0;
    +  assertEquals(offset, menu.offsetTop);
    +  assertEquals(5, menu.offsetLeft);
    +}
    +
    +function testRepositionWithAdjustAndOffscreenAnchor() {
    +  // This does not get adjusted because it's too far offscreen.
    +  var pos = new goog.positioning.MenuAnchoredPosition(
    +      offscreenAnchor, corner.TOP_LEFT, true);
    +  pos.reposition(menu, corner.TOP_LEFT);
    +
    +  assertEquals(-1000, menu.offsetTop);
    +  assertEquals(-1000, menu.offsetLeft);
    +}
    +
    +function testRespositionFailoverEvenWhenResizeHeightIsOn() {
    +  var pos = new goog.positioning.MenuAnchoredPosition(
    +      onscreenAnchor, corner.TOP_LEFT, true, true);
    +  pos.reposition(menu, corner.TOP_RIGHT);
    +
    +  // The menu should not get positioned offscreen.
    +  assertEquals(5, menu.offsetTop);
    +  assertEquals(5, menu.offsetLeft);
    +}
    +
    +function testRepositionToBottomLeftWhenBottomFailsAndRightFailsAndResizeOn() {
    +  var pageSize = goog.dom.getViewportSize();
    +  customAnchor.style.left = (pageSize.width - 10) + 'px';
    +
    +  // Add so many children that it can't possibly fit onscreen.
    +  for (var i = 0; i < 200; i++) {
    +    menu.appendChild(goog.dom.createDom('div', null, 'New Item ' + i));
    +  }
    +
    +  var pos = new goog.positioning.MenuAnchoredPosition(
    +      customAnchor, corner.TOP_LEFT, true, true);
    +  pos.reposition(menu, corner.TOP_LEFT);
    +  assertEquals(menu.offsetLeft + menu.offsetWidth, customAnchor.offsetLeft);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning.js b/mapbox-gl/closure-library/closure/goog/positioning/positioning.js
    new file mode 100644
    index 0000000000..9385a886e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning.js
    @@ -0,0 +1,607 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Common positioning code.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning');
    +goog.provide('goog.positioning.Corner');
    +goog.provide('goog.positioning.CornerBit');
    +goog.provide('goog.positioning.Overflow');
    +goog.provide('goog.positioning.OverflowStatus');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Rect');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +
    +
    +/**
    + * Enum for representing an element corner for positioning the popup.
    + *
    + * The START constants map to LEFT if element directionality is left
    + * to right and RIGHT if the directionality is right to left.
    + * Likewise END maps to RIGHT or LEFT depending on the directionality.
    + *
    + * @enum {number}
    + */
    +goog.positioning.Corner = {
    +  TOP_LEFT: 0,
    +  TOP_RIGHT: 2,
    +  BOTTOM_LEFT: 1,
    +  BOTTOM_RIGHT: 3,
    +  TOP_START: 4,
    +  TOP_END: 6,
    +  BOTTOM_START: 5,
    +  BOTTOM_END: 7
    +};
    +
    +
    +/**
    + * Enum for bits in the {@see goog.positioning.Corner) bitmap.
    + *
    + * @enum {number}
    + */
    +goog.positioning.CornerBit = {
    +  BOTTOM: 1,
    +  RIGHT: 2,
    +  FLIP_RTL: 4
    +};
    +
    +
    +/**
    + * Enum for representing position handling in cases where the element would be
    + * positioned outside the viewport.
    + *
    + * @enum {number}
    + */
    +goog.positioning.Overflow = {
    +  /** Ignore overflow */
    +  IGNORE: 0,
    +
    +  /** Try to fit horizontally in the viewport at all costs. */
    +  ADJUST_X: 1,
    +
    +  /** If the element can't fit horizontally, report positioning failure. */
    +  FAIL_X: 2,
    +
    +  /** Try to fit vertically in the viewport at all costs. */
    +  ADJUST_Y: 4,
    +
    +  /** If the element can't fit vertically, report positioning failure. */
    +  FAIL_Y: 8,
    +
    +  /** Resize the element's width to fit in the viewport. */
    +  RESIZE_WIDTH: 16,
    +
    +  /** Resize the element's height to fit in the viewport. */
    +  RESIZE_HEIGHT: 32,
    +
    +  /**
    +   * If the anchor goes off-screen in the x-direction, position the movable
    +   * element off-screen. Otherwise, try to fit horizontally in the viewport.
    +   */
    +  ADJUST_X_EXCEPT_OFFSCREEN: 64 | 1,
    +
    +  /**
    +   * If the anchor goes off-screen in the y-direction, position the movable
    +   * element off-screen. Otherwise, try to fit vertically in the viewport.
    +   */
    +  ADJUST_Y_EXCEPT_OFFSCREEN: 128 | 4
    +};
    +
    +
    +/**
    + * Enum for representing the outcome of a positioning call.
    + *
    + * @enum {number}
    + */
    +goog.positioning.OverflowStatus = {
    +  NONE: 0,
    +  ADJUSTED_X: 1,
    +  ADJUSTED_Y: 2,
    +  WIDTH_ADJUSTED: 4,
    +  HEIGHT_ADJUSTED: 8,
    +  FAILED_LEFT: 16,
    +  FAILED_RIGHT: 32,
    +  FAILED_TOP: 64,
    +  FAILED_BOTTOM: 128,
    +  FAILED_OUTSIDE_VIEWPORT: 256
    +};
    +
    +
    +/**
    + * Shorthand to check if a status code contains any fail code.
    + * @type {number}
    + */
    +goog.positioning.OverflowStatus.FAILED =
    +    goog.positioning.OverflowStatus.FAILED_LEFT |
    +    goog.positioning.OverflowStatus.FAILED_RIGHT |
    +    goog.positioning.OverflowStatus.FAILED_TOP |
    +    goog.positioning.OverflowStatus.FAILED_BOTTOM |
    +    goog.positioning.OverflowStatus.FAILED_OUTSIDE_VIEWPORT;
    +
    +
    +/**
    + * Shorthand to check if horizontal positioning failed.
    + * @type {number}
    + */
    +goog.positioning.OverflowStatus.FAILED_HORIZONTAL =
    +    goog.positioning.OverflowStatus.FAILED_LEFT |
    +    goog.positioning.OverflowStatus.FAILED_RIGHT;
    +
    +
    +/**
    + * Shorthand to check if vertical positioning failed.
    + * @type {number}
    + */
    +goog.positioning.OverflowStatus.FAILED_VERTICAL =
    +    goog.positioning.OverflowStatus.FAILED_TOP |
    +    goog.positioning.OverflowStatus.FAILED_BOTTOM;
    +
    +
    +/**
    + * Positions a movable element relative to an anchor element. The caller
    + * specifies the corners that should touch. This functions then moves the
    + * movable element accordingly.
    + *
    + * @param {Element} anchorElement The element that is the anchor for where
    + *    the movable element should position itself.
    + * @param {goog.positioning.Corner} anchorElementCorner The corner of the
    + *     anchorElement for positioning the movable element.
    + * @param {Element} movableElement The element to move.
    + * @param {goog.positioning.Corner} movableElementCorner The corner of the
    + *     movableElement that that should be positioned adjacent to the anchor
    + *     element.
    + * @param {goog.math.Coordinate=} opt_offset An offset specified in pixels.
    + *    After the normal positioning algorithm is applied, the offset is then
    + *    applied. Positive coordinates move the popup closer to the center of the
    + *    anchor element. Negative coordinates move the popup away from the center
    + *    of the anchor element.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + *    After the normal positioning algorithm is applied and any offset, the
    + *    margin is then applied. Positive coordinates move the popup away from the
    + *    spot it was positioned towards its center. Negative coordinates move it
    + *    towards the spot it was positioned away from its center.
    + * @param {?number=} opt_overflow Overflow handling mode. Defaults to IGNORE if
    + *     not specified. Bitmap, {@see goog.positioning.Overflow}.
    + * @param {goog.math.Size=} opt_preferredSize The preferred size of the
    + *     movableElement.
    + * @param {goog.math.Box=} opt_viewport Box object describing the dimensions of
    + *     the viewport. The viewport is specified relative to offsetParent of
    + *     {@code movableElement}. In other words, the viewport can be thought of as
    + *     describing a "position: absolute" element contained in the offsetParent.
    + *     It defaults to visible area of nearest scrollable ancestor of
    + *     {@code movableElement} (see {@code goog.style.getVisibleRectForElement}).
    + * @return {goog.positioning.OverflowStatus} Status bitmap,
    + *     {@see goog.positioning.OverflowStatus}.
    + */
    +goog.positioning.positionAtAnchor = function(anchorElement,
    +                                             anchorElementCorner,
    +                                             movableElement,
    +                                             movableElementCorner,
    +                                             opt_offset,
    +                                             opt_margin,
    +                                             opt_overflow,
    +                                             opt_preferredSize,
    +                                             opt_viewport) {
    +
    +  goog.asserts.assert(movableElement);
    +  var movableParentTopLeft =
    +      goog.positioning.getOffsetParentPageOffset(movableElement);
    +
    +  // Get the visible part of the anchor element.  anchorRect is
    +  // relative to anchorElement's page.
    +  var anchorRect = goog.positioning.getVisiblePart_(anchorElement);
    +
    +  // Translate anchorRect to be relative to movableElement's page.
    +  goog.style.translateRectForAnotherFrame(
    +      anchorRect,
    +      goog.dom.getDomHelper(anchorElement),
    +      goog.dom.getDomHelper(movableElement));
    +
    +  // Offset based on which corner of the element we want to position against.
    +  var corner = goog.positioning.getEffectiveCorner(anchorElement,
    +                                                   anchorElementCorner);
    +  // absolutePos is a candidate position relative to the
    +  // movableElement's window.
    +  var absolutePos = new goog.math.Coordinate(
    +      corner & goog.positioning.CornerBit.RIGHT ?
    +          anchorRect.left + anchorRect.width : anchorRect.left,
    +      corner & goog.positioning.CornerBit.BOTTOM ?
    +          anchorRect.top + anchorRect.height : anchorRect.top);
    +
    +  // Translate absolutePos to be relative to the offsetParent.
    +  absolutePos =
    +      goog.math.Coordinate.difference(absolutePos, movableParentTopLeft);
    +
    +  // Apply offset, if specified
    +  if (opt_offset) {
    +    absolutePos.x += (corner & goog.positioning.CornerBit.RIGHT ? -1 : 1) *
    +        opt_offset.x;
    +    absolutePos.y += (corner & goog.positioning.CornerBit.BOTTOM ? -1 : 1) *
    +        opt_offset.y;
    +  }
    +
    +  // Determine dimension of viewport.
    +  var viewport;
    +  if (opt_overflow) {
    +    if (opt_viewport) {
    +      viewport = opt_viewport;
    +    } else {
    +      viewport = goog.style.getVisibleRectForElement(movableElement);
    +      if (viewport) {
    +        viewport.top -= movableParentTopLeft.y;
    +        viewport.right -= movableParentTopLeft.x;
    +        viewport.bottom -= movableParentTopLeft.y;
    +        viewport.left -= movableParentTopLeft.x;
    +      }
    +    }
    +  }
    +
    +  return goog.positioning.positionAtCoordinate(absolutePos,
    +                                               movableElement,
    +                                               movableElementCorner,
    +                                               opt_margin,
    +                                               viewport,
    +                                               opt_overflow,
    +                                               opt_preferredSize);
    +};
    +
    +
    +/**
    + * Calculates the page offset of the given element's
    + * offsetParent. This value can be used to translate any x- and
    + * y-offset relative to the page to an offset relative to the
    + * offsetParent, which can then be used directly with as position
    + * coordinate for {@code positionWithCoordinate}.
    + * @param {!Element} movableElement The element to calculate.
    + * @return {!goog.math.Coordinate} The page offset, may be (0, 0).
    + */
    +goog.positioning.getOffsetParentPageOffset = function(movableElement) {
    +  // Ignore offset for the BODY element unless its position is non-static.
    +  // For cases where the offset parent is HTML rather than the BODY (such as in
    +  // IE strict mode) there's no need to get the position of the BODY as it
    +  // doesn't affect the page offset.
    +  var movableParentTopLeft;
    +  var parent = movableElement.offsetParent;
    +  if (parent) {
    +    var isBody = parent.tagName == goog.dom.TagName.HTML ||
    +        parent.tagName == goog.dom.TagName.BODY;
    +    if (!isBody ||
    +        goog.style.getComputedPosition(parent) != 'static') {
    +      // Get the top-left corner of the parent, in page coordinates.
    +      movableParentTopLeft = goog.style.getPageOffset(parent);
    +
    +      if (!isBody) {
    +        movableParentTopLeft = goog.math.Coordinate.difference(
    +            movableParentTopLeft,
    +            new goog.math.Coordinate(goog.style.bidi.getScrollLeft(parent),
    +                parent.scrollTop));
    +      }
    +    }
    +  }
    +
    +  return movableParentTopLeft || new goog.math.Coordinate();
    +};
    +
    +
    +/**
    + * Returns intersection of the specified element and
    + * goog.style.getVisibleRectForElement for it.
    + *
    + * @param {Element} el The target element.
    + * @return {!goog.math.Rect} Intersection of getVisibleRectForElement
    + *     and the current bounding rectangle of the element.  If the
    + *     intersection is empty, returns the bounding rectangle.
    + * @private
    + */
    +goog.positioning.getVisiblePart_ = function(el) {
    +  var rect = goog.style.getBounds(el);
    +  var visibleBox = goog.style.getVisibleRectForElement(el);
    +  if (visibleBox) {
    +    rect.intersection(goog.math.Rect.createFromBox(visibleBox));
    +  }
    +  return rect;
    +};
    +
    +
    +/**
    + * Positions the specified corner of the movable element at the
    + * specified coordinate.
    + *
    + * @param {goog.math.Coordinate} absolutePos The coordinate to position the
    + *     element at.
    + * @param {Element} movableElement The element to be positioned.
    + * @param {goog.positioning.Corner} movableElementCorner The corner of the
    + *     movableElement that that should be positioned.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + *    After the normal positioning algorithm is applied and any offset, the
    + *    margin is then applied. Positive coordinates move the popup away from the
    + *    spot it was positioned towards its center. Negative coordinates move it
    + *    towards the spot it was positioned away from its center.
    + * @param {goog.math.Box=} opt_viewport Box object describing the dimensions of
    + *     the viewport. Required if opt_overflow is specified.
    + * @param {?number=} opt_overflow Overflow handling mode. Defaults to IGNORE if
    + *     not specified, {@see goog.positioning.Overflow}.
    + * @param {goog.math.Size=} opt_preferredSize The preferred size of the
    + *     movableElement. Defaults to the current size.
    + * @return {goog.positioning.OverflowStatus} Status bitmap.
    + */
    +goog.positioning.positionAtCoordinate = function(absolutePos,
    +                                                 movableElement,
    +                                                 movableElementCorner,
    +                                                 opt_margin,
    +                                                 opt_viewport,
    +                                                 opt_overflow,
    +                                                 opt_preferredSize) {
    +  absolutePos = absolutePos.clone();
    +  var status = goog.positioning.OverflowStatus.NONE;
    +
    +  // Offset based on attached corner and desired margin.
    +  var corner = goog.positioning.getEffectiveCorner(movableElement,
    +                                                   movableElementCorner);
    +  var elementSize = goog.style.getSize(movableElement);
    +  var size = opt_preferredSize ? opt_preferredSize.clone() :
    +      elementSize.clone();
    +
    +  var positionResult = goog.positioning.getPositionAtCoordinate(absolutePos,
    +      size, corner, opt_margin, opt_viewport, opt_overflow);
    +
    +  if (positionResult.status & goog.positioning.OverflowStatus.FAILED) {
    +    return positionResult.status;
    +  }
    +
    +  goog.style.setPosition(movableElement, positionResult.rect.getTopLeft());
    +  size = positionResult.rect.getSize();
    +  if (!goog.math.Size.equals(elementSize, size)) {
    +    goog.style.setBorderBoxSize(movableElement, size);
    +  }
    +
    +  return positionResult.status;
    +};
    +
    +
    +/**
    + * Computes the position for an element to be placed on-screen at the
    + * specified coordinates. Returns an object containing both the resulting
    + * rectangle, and the overflow status bitmap.
    + *
    + * @param {!goog.math.Coordinate} absolutePos The coordinate to position the
    + *     element at.
    + * @param {!goog.math.Size} elementSize The size of the element to be
    + *     positioned.
    + * @param {goog.positioning.Corner} elementCorner The corner of the
    + *     movableElement that that should be positioned.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + *    After the normal positioning algorithm is applied and any offset, the
    + *    margin is then applied. Positive coordinates move the popup away from the
    + *    spot it was positioned towards its center. Negative coordinates move it
    + *    towards the spot it was positioned away from its center.
    + * @param {goog.math.Box=} opt_viewport Box object describing the dimensions of
    + *     the viewport. Required if opt_overflow is specified.
    + * @param {?number=} opt_overflow Overflow handling mode. Defaults to IGNORE
    + *     if not specified, {@see goog.positioning.Overflow}.
    + * @return {{rect:!goog.math.Rect, status:goog.positioning.OverflowStatus}}
    + *     Object containing the computed position and status bitmap.
    + */
    +goog.positioning.getPositionAtCoordinate = function(
    +    absolutePos,
    +    elementSize,
    +    elementCorner,
    +    opt_margin,
    +    opt_viewport,
    +    opt_overflow) {
    +  absolutePos = absolutePos.clone();
    +  elementSize = elementSize.clone();
    +  var status = goog.positioning.OverflowStatus.NONE;
    +
    +  if (opt_margin || elementCorner != goog.positioning.Corner.TOP_LEFT) {
    +    if (elementCorner & goog.positioning.CornerBit.RIGHT) {
    +      absolutePos.x -= elementSize.width + (opt_margin ? opt_margin.right : 0);
    +    } else if (opt_margin) {
    +      absolutePos.x += opt_margin.left;
    +    }
    +    if (elementCorner & goog.positioning.CornerBit.BOTTOM) {
    +      absolutePos.y -= elementSize.height +
    +          (opt_margin ? opt_margin.bottom : 0);
    +    } else if (opt_margin) {
    +      absolutePos.y += opt_margin.top;
    +    }
    +  }
    +
    +  // Adjust position to fit inside viewport.
    +  if (opt_overflow) {
    +    status = opt_viewport ?
    +        goog.positioning.adjustForViewport_(
    +            absolutePos, elementSize, opt_viewport, opt_overflow) :
    +        goog.positioning.OverflowStatus.FAILED_OUTSIDE_VIEWPORT;
    +  }
    +
    +  var rect = new goog.math.Rect(0, 0, 0, 0);
    +  rect.left = absolutePos.x;
    +  rect.top = absolutePos.y;
    +  rect.width = elementSize.width;
    +  rect.height = elementSize.height;
    +  return {rect: rect, status: status};
    +};
    +
    +
    +/**
    + * Adjusts the position and/or size of an element, identified by its position
    + * and size, to fit inside the viewport. If the position or size of the element
    + * is adjusted the pos or size objects, respectively, are modified.
    + *
    + * @param {goog.math.Coordinate} pos Position of element, updated if the
    + *     position is adjusted.
    + * @param {goog.math.Size} size Size of element, updated if the size is
    + *     adjusted.
    + * @param {goog.math.Box} viewport Bounding box describing the viewport.
    + * @param {number} overflow Overflow handling mode,
    + *     {@see goog.positioning.Overflow}.
    + * @return {goog.positioning.OverflowStatus} Status bitmap,
    + *     {@see goog.positioning.OverflowStatus}.
    + * @private
    + */
    +goog.positioning.adjustForViewport_ = function(pos, size, viewport, overflow) {
    +  var status = goog.positioning.OverflowStatus.NONE;
    +
    +  var ADJUST_X_EXCEPT_OFFSCREEN =
    +      goog.positioning.Overflow.ADJUST_X_EXCEPT_OFFSCREEN;
    +  var ADJUST_Y_EXCEPT_OFFSCREEN =
    +      goog.positioning.Overflow.ADJUST_Y_EXCEPT_OFFSCREEN;
    +  if ((overflow & ADJUST_X_EXCEPT_OFFSCREEN) == ADJUST_X_EXCEPT_OFFSCREEN &&
    +      (pos.x < viewport.left || pos.x >= viewport.right)) {
    +    overflow &= ~goog.positioning.Overflow.ADJUST_X;
    +  }
    +  if ((overflow & ADJUST_Y_EXCEPT_OFFSCREEN) == ADJUST_Y_EXCEPT_OFFSCREEN &&
    +      (pos.y < viewport.top || pos.y >= viewport.bottom)) {
    +    overflow &= ~goog.positioning.Overflow.ADJUST_Y;
    +  }
    +
    +  // Left edge outside viewport, try to move it.
    +  if (pos.x < viewport.left && overflow & goog.positioning.Overflow.ADJUST_X) {
    +    pos.x = viewport.left;
    +    status |= goog.positioning.OverflowStatus.ADJUSTED_X;
    +  }
    +
    +  // Left edge inside and right edge outside viewport, try to resize it.
    +  if (pos.x < viewport.left &&
    +      pos.x + size.width > viewport.right &&
    +      overflow & goog.positioning.Overflow.RESIZE_WIDTH) {
    +    size.width = Math.max(
    +        size.width - ((pos.x + size.width) - viewport.right), 0);
    +    status |= goog.positioning.OverflowStatus.WIDTH_ADJUSTED;
    +  }
    +
    +  // Right edge outside viewport, try to move it.
    +  if (pos.x + size.width > viewport.right &&
    +      overflow & goog.positioning.Overflow.ADJUST_X) {
    +    pos.x = Math.max(viewport.right - size.width, viewport.left);
    +    status |= goog.positioning.OverflowStatus.ADJUSTED_X;
    +  }
    +
    +  // Left or right edge still outside viewport, fail if the FAIL_X option was
    +  // specified, ignore it otherwise.
    +  if (overflow & goog.positioning.Overflow.FAIL_X) {
    +    status |= (pos.x < viewport.left ?
    +                   goog.positioning.OverflowStatus.FAILED_LEFT : 0) |
    +              (pos.x + size.width > viewport.right ?
    +                   goog.positioning.OverflowStatus.FAILED_RIGHT : 0);
    +  }
    +
    +  // Top edge outside viewport, try to move it.
    +  if (pos.y < viewport.top && overflow & goog.positioning.Overflow.ADJUST_Y) {
    +    pos.y = viewport.top;
    +    status |= goog.positioning.OverflowStatus.ADJUSTED_Y;
    +  }
    +
    +  // Bottom edge inside and top edge outside viewport, try to resize it.
    +  if (pos.y <= viewport.top &&
    +      pos.y + size.height < viewport.bottom &&
    +      overflow & goog.positioning.Overflow.RESIZE_HEIGHT) {
    +    size.height = Math.max(size.height - (viewport.top - pos.y), 0);
    +    pos.y = viewport.top;
    +    status |= goog.positioning.OverflowStatus.HEIGHT_ADJUSTED;
    +  }
    +
    +  // Top edge inside and bottom edge outside viewport, try to resize it.
    +  if (pos.y >= viewport.top &&
    +      pos.y + size.height > viewport.bottom &&
    +      overflow & goog.positioning.Overflow.RESIZE_HEIGHT) {
    +    size.height = Math.max(
    +        size.height - ((pos.y + size.height) - viewport.bottom), 0);
    +    status |= goog.positioning.OverflowStatus.HEIGHT_ADJUSTED;
    +  }
    +
    +  // Bottom edge outside viewport, try to move it.
    +  if (pos.y + size.height > viewport.bottom &&
    +      overflow & goog.positioning.Overflow.ADJUST_Y) {
    +    pos.y = Math.max(viewport.bottom - size.height, viewport.top);
    +    status |= goog.positioning.OverflowStatus.ADJUSTED_Y;
    +  }
    +
    +  // Top or bottom edge still outside viewport, fail if the FAIL_Y option was
    +  // specified, ignore it otherwise.
    +  if (overflow & goog.positioning.Overflow.FAIL_Y) {
    +    status |= (pos.y < viewport.top ?
    +                   goog.positioning.OverflowStatus.FAILED_TOP : 0) |
    +              (pos.y + size.height > viewport.bottom ?
    +                   goog.positioning.OverflowStatus.FAILED_BOTTOM : 0);
    +  }
    +
    +  return status;
    +};
    +
    +
    +/**
    + * Returns an absolute corner (top/bottom left/right) given an absolute
    + * or relative (top/bottom start/end) corner and the direction of an element.
    + * Absolute corners remain unchanged.
    + * @param {Element} element DOM element to test for RTL direction.
    + * @param {goog.positioning.Corner} corner The popup corner used for
    + *     positioning.
    + * @return {goog.positioning.Corner} Effective corner.
    + */
    +goog.positioning.getEffectiveCorner = function(element, corner) {
    +  return /** @type {goog.positioning.Corner} */ (
    +      (corner & goog.positioning.CornerBit.FLIP_RTL &&
    +          goog.style.isRightToLeft(element) ?
    +          corner ^ goog.positioning.CornerBit.RIGHT :
    +          corner
    +      ) & ~goog.positioning.CornerBit.FLIP_RTL);
    +};
    +
    +
    +/**
    + * Returns the corner opposite the given one horizontally.
    + * @param {goog.positioning.Corner} corner The popup corner used to flip.
    + * @return {goog.positioning.Corner} The opposite corner horizontally.
    + */
    +goog.positioning.flipCornerHorizontal = function(corner) {
    +  return /** @type {goog.positioning.Corner} */ (corner ^
    +      goog.positioning.CornerBit.RIGHT);
    +};
    +
    +
    +/**
    + * Returns the corner opposite the given one vertically.
    + * @param {goog.positioning.Corner} corner The popup corner used to flip.
    + * @return {goog.positioning.Corner} The opposite corner vertically.
    + */
    +goog.positioning.flipCornerVertical = function(corner) {
    +  return /** @type {goog.positioning.Corner} */ (corner ^
    +      goog.positioning.CornerBit.BOTTOM);
    +};
    +
    +
    +/**
    + * Returns the corner opposite the given one horizontally and vertically.
    + * @param {goog.positioning.Corner} corner The popup corner used to flip.
    + * @return {goog.positioning.Corner} The opposite corner horizontally and
    + *     vertically.
    + */
    +goog.positioning.flipCorner = function(corner) {
    +  return /** @type {goog.positioning.Corner} */ (corner ^
    +      goog.positioning.CornerBit.BOTTOM ^
    +      goog.positioning.CornerBit.RIGHT);
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.html b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.html
    new file mode 100644
    index 0000000000..70f53498f0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.html
    @@ -0,0 +1,199 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: eae@google.com (Emil A Eklund)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.positioning</title>
    +<script src="../base.js"></script>
    +
    +
    +<style>
    +  .box1 {
    +    border: 1px solid black;
    +    margin: 10px;
    +    padding: 5px;
    +    height: 150px;
    +  }
    +  .outerbox {
    +    border: 1px solid gray;
    +    padding: 3px;
    +    margin: 5px 5px 5px 100px;
    +  }
    +  .box2 {
    +    position: relative;
    +    padding: 0px; /* If the padding has >0 value, IE6 fails some tests. */
    +    margin: -2px;
    +  }
    +  .box8 {
    +    position: absolute;
    +    padding: 0px; /* If the padding has >0 value, IE6 fails some tests. */
    +    margin: -2px;
    +    width: 500px;
    +    height: 100px;
    +  }
    +  .box9 {
    +    border: 1px solid black;
    +    margin: 10px;
    +    padding: 5px;
    +    height: 150px;
    +    width: 150px;
    +  }
    +  .anchorFrame {
    +    overflow: auto;
    +    width: 100px;
    +    height: 100px;
    +  }
    +  #popup1, #popup2, #popup3, #popup5, #popup6, #popup7 {
    +    position: absolute;
    +    border: 1px solid red;
    +    width: 100px;
    +    height: 100px;
    +  }
    +  #popup9 {
    +    border: 1px solid green;
    +    height: 100px;
    +    left: 0;
    +    position: absolute;
    +    top: 0;
    +    width: 100px;
    +  }
    +  #popup8 {
    +    position: absolute;
    +    border: 1px solid red;
    +    width: 100px;
    +    height: 100px;
    +  }
    +  #anchor1 {
    +    border: 1px solid blue;
    +  }
    +  #anchor4 {
    +    position: absolute;
    +    left: 2px;
    +  }
    +
    +  #test-area {
    +    height: 1000px;
    +    position: relative;
    +    width: 1000px;
    +  }
    +  .overflow-hidden {
    +    overflow: hidden;
    +  }
    +  .overflow-auto {
    +    overflow: auto;
    +  }
    +</style>
    +</head>
    +<body>
    +
    +<div id='offscreen-anchor'
    +   style='position: absolute; left: -1000px; top: -1000px'></div>
    +
    +  <div id="ltr" dir="ltr">
    +    Left to right element.
    +  </div>
    +
    +  <div id="rtl" dir="rtl">
    +    Right to left element.
    +  </div>
    +
    +  <div class="outerbox">
    +    <div id="box1" class="box1">
    +      <span id="anchor1">Anchor LTR.</span>
    +    </div>
    +
    +    <div class="box2">
    +      <div id="popup1">
    +        <div>Popup ltr.</div>
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div class="outerbox" dir="rtl">
    +    <div class="box1">
    +      <span id="anchor2">Anchor RTL.</span>
    +    </div>
    +
    +    <div class="box2">
    +      <div id="popup2">
    +        <div>Popup rtl.</div>
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div id="anchor4">
    +    Anchor 4.
    +  </div>
    +
    +  <div id="popup3">
    +    Popup.
    +  </div>
    +
    +<div dir="rtl" style="border: 1px solid red;">
    +  <div dir="rtl" style="position: relative; overflow: auto; width: 150px; height: 100px; border: 1px solid black;">
    +    <div style="height: 200px;">
    +      <span id="anchor5">Anchor 5.</span>
    +    </div>
    +    <div id="popup5">
    +      Popup.
    +    </div>
    +  </div>
    +</div>
    +
    +<iframe id="iframe-standard" src="positioning_test_standard.html" class="anchorFrame">
    +</iframe>
    +<iframe id="iframe-quirk" src="positioning_test_quirk.html" class="anchorFrame">
    +</iframe>
    +<div id="popup6">Popup6</div>
    +
    +<div style="position:relative;height:100px;width:100px;overflow:auto;">
    +  I hate positioning!
    +  <div>1</div>
    +  <div>2</div>
    +  <div>3</div>
    +  <div>4</div>
    +  <div>5</div>
    +  <div>6</div>
    +  <div>7</div>
    +  <div id="popup7">Popup7</div>
    +</div>
    +
    +<iframe id="nested-outer" src="positioning_test_iframe1.html"
    + style="overflow:auto;width:150px;height:150px;"></iframe>
    +
    +<div class="outerbox" dir="rtl">
    +  <div class="box1">
    +    <span id="anchor8">Anchor8 RTL.</span>
    +  </div>
    +
    +  <div class="box8 overflow-auto">
    +    <div id="popup8">
    +      <div>Popup8 rtl.</div>
    +    </div>
    +    <div style="width:10000px;">&nbsp;</div>
    +  </div>
    +
    +</div>
    +
    +<div id="box9" class="box9">
    +  <div id="popup9">
    +    <div>Popup9</div>
    +  </div>
    +  <span id="anchor9">Anchor9</span>
    +</div>
    +
    +<div id="test-area"></div>
    +
    +<script>
    +goog.require('goog.positioningTest');
    +</script>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.js b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.js
    new file mode 100644
    index 0000000000..b1dcb904ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test.js
    @@ -0,0 +1,1197 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.position.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.positioningTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Size');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +goog.setTestOnly('goog.positioningTest');
    +
    +// Allow positions to be off by one in gecko as it reports scrolling
    +// offsets in steps of 2.  Otherwise, allow for subpixel difference
    +// as seen in IE10+
    +var ALLOWED_OFFSET = goog.userAgent.GECKO ? 1 : 0.1;
    +// Error bar for positions since some browsers are not super accurate
    +// in reporting them.
    +var EPSILON = 2;
    +
    +var expectedFailures = new goog.testing.ExpectedFailures();
    +
    +var corner = goog.positioning.Corner;
    +var overflow = goog.positioning.Overflow;
    +var testArea;
    +
    +function setUp() {
    +  window.scrollTo(0, 0);
    +
    +  var viewportSize = goog.dom.getViewportSize();
    +  // Some tests need enough size viewport.
    +  if (viewportSize.width < 600 || viewportSize.height < 600) {
    +    window.moveTo(0, 0);
    +    window.resizeTo(640, 640);
    +  }
    +
    +  testArea = goog.dom.getElement('test-area');
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  testArea.setAttribute('style', '');
    +  testArea.innerHTML = '';
    +}
    +
    +
    +/**
    + * This is used to round pixel values on FF3 Mac.
    + */
    +function assertRoundedEquals(a, b, c) {
    +  function round(x) {
    +    return goog.userAgent.GECKO && (goog.userAgent.MAC || goog.userAgent.X11) &&
    +        goog.userAgent.isVersionOrHigher('1.9') ? Math.round(x) : x;
    +  }
    +  if (arguments.length == 3) {
    +    assertRoughlyEquals(a, round(b), round(c), ALLOWED_OFFSET);
    +  } else {
    +    assertRoughlyEquals(round(a), round(b), ALLOWED_OFFSET);
    +  }
    +}
    +
    +function testPositionAtAnchorLeftToRight() {
    +  var anchor = document.getElementById('anchor1');
    +  var popup = document.getElementById('popup1');
    +
    +  // Anchor top left to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top);
    +
    +  // Anchor top left to bottom left.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_LEFT,
    +                                    popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +
    +  // Anchor top left to top right.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_RIGHT,
    +                                    popup, corner.TOP_LEFT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Popup should be positioned just right of the anchor.',
    +                      anchorRect.left + anchorRect.width,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top);
    +
    +  // Anchor top right to bottom right.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_RIGHT,
    +                                    popup, corner.TOP_RIGHT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Right edge of popup should line up with right edge ' +
    +                      'of anchor.',
    +                      anchorRect.left + anchorRect.width,
    +                      popupRect.left + popupRect.width);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +
    +  // Anchor top start to bottom start.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_START,
    +                                    popup, corner.TOP_START);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +}
    +
    +
    +function testPositionAtAnchorWithOffset() {
    +  var anchor = document.getElementById('anchor1');
    +  var popup = document.getElementById('popup1');
    +
    +  // Anchor top left to top left with an offset moving the popup away from the
    +  // anchor.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT,
    +                                    newCoord(-15, -20));
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should be fifteen pixels from ' +
    +                      'anchor.',
    +                      anchorRect.left,
    +                      popupRect.left + 15);
    +  assertRoundedEquals('Top edge of popup should be twenty pixels from anchor.',
    +                      anchorRect.top,
    +                      popupRect.top + 20);
    +
    +  // Anchor top left to top left with an offset moving the popup towards the
    +  // anchor.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT,
    +                                    newCoord(3, 1));
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should be three pixels right of ' +
    +                      'the anchor\'s left edge',
    +                      anchorRect.left,
    +                      popupRect.left - 3);
    +  assertRoundedEquals('Top edge of popup should be one pixel below of the ' +
    +                      'anchor\'s top edge',
    +                      anchorRect.top,
    +                      popupRect.top - 1);
    +}
    +
    +
    +function testPositionAtAnchorOverflowLeftEdgeRightToLeft() {
    +  var anchor = document.getElementById('anchor5');
    +  var popup = document.getElementById('popup5');
    +
    +  var status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                                 popup, corner.TOP_RIGHT,
    +                                                 undefined, undefined,
    +                                                 overflow.FAIL_X);
    +  assertFalse('Positioning operation should have failed.',
    +              (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +
    +  // Change overflow strategy to ADJUST.
    +  status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                             popup, corner.TOP_RIGHT,
    +                                             undefined, undefined,
    +                                             overflow.ADJUST_X);
    +
    +  // Fails in Chrome because of infrastructure issues, temporarily disabled.
    +  // See b/4274723.
    +  expectedFailures.expectFailureFor(goog.userAgent.product.CHROME);
    +  try {
    +    assertTrue('Positioning operation should have been successful.',
    +               (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +    assertTrue('Positioning operation should have been adjusted.',
    +               (status & goog.positioning.OverflowStatus.ADJUSTED_X) != 0);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  var parentRect = goog.style.getBounds(anchor.parentNode);
    +  assertTrue('Position should have been adjusted so that the left edge of ' +
    +             'the popup is left of the anchor but still within the bounding ' +
    +             'box of the parent container.',
    +      anchorRect.left <= popupRect.left <= parentRect.left);
    +
    +}
    +
    +
    +function testPositionAtAnchorWithMargin() {
    +  var anchor = document.getElementById('anchor1');
    +  var popup = document.getElementById('popup1');
    +
    +  // Anchor top left to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT, undefined,
    +                                    new goog.math.Box(1, 2, 3, 4));
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Left edge of popup should be four pixels from anchor.',
    +                      anchorRect.left,
    +                      popupRect.left - 4);
    +  assertRoundedEquals('Top edge of popup should be one pixels from anchor.',
    +                      anchorRect.top,
    +                      popupRect.top - 1);
    +
    +  // Anchor top right to bottom right.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_RIGHT,
    +                                    popup, corner.TOP_RIGHT, undefined,
    +                                    new goog.math.Box(1, 2, 3, 4));
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +
    +  var visibleAnchorRect = goog.positioning.getVisiblePart_(anchor);
    +
    +  assertRoundedEquals('Right edge of popup should line up with right edge ' +
    +                      'of anchor.',
    +                      visibleAnchorRect.left + visibleAnchorRect.width,
    +                      popupRect.left + popupRect.width + 2);
    +
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      visibleAnchorRect.top + visibleAnchorRect.height,
    +                      popupRect.top - 1);
    +
    +}
    +
    +
    +function testPositionAtAnchorRightToLeft() {
    +  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('6')) {
    +    // These tests fails with IE6.
    +    // TODO(user): Investigate the reason.
    +    return;
    +  }
    +
    +  var anchor = document.getElementById('anchor2');
    +  var popup = document.getElementById('popup2');
    +
    +  // Anchor top left to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top);
    +
    +  // Anchor top start to bottom start.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_START,
    +                                    popup, corner.TOP_START);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Right edge of popup should line up with right edge ' +
    +                      'of anchor.',
    +                      anchorRect.left + anchorRect.width,
    +                      popupRect.left + popupRect.width);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +}
    +
    +function testPositionAtAnchorRightToLeftWithScroll() {
    +  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('6')) {
    +    // These tests fails with IE6.
    +    // TODO(user): Investigate the reason.
    +    return;
    +  }
    +
    +  var anchor = document.getElementById('anchor8');
    +  var popup = document.getElementById('popup8');
    +
    +  // Anchor top left to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top);
    +
    +  // Anchor top start to bottom start.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_START,
    +                                    popup, corner.TOP_START);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +
    +  var visibleAnchorRect = goog.positioning.getVisiblePart_(anchor);
    +  var visibleAnchorBox = visibleAnchorRect.toBox();
    +
    +  assertRoundedEquals('Right edge of popup should line up with right edge ' +
    +                      'of anchor.',
    +                      anchorRect.left + anchorRect.width,
    +                      popupRect.left + popupRect.width);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      visibleAnchorBox.bottom,
    +                      popupRect.top);
    +}
    +
    +function testPositionAtAnchorBodyViewport() {
    +  var anchor = document.getElementById('anchor1');
    +  var popup = document.getElementById('popup3');
    +
    +  // Anchor top left to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_LEFT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals('Left edge of popup should line up with left edge of anchor.',
    +               anchorRect.left,
    +               popupRect.left);
    +  assertRoughlyEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top,
    +                      1);
    +
    +  // Anchor top start to bottom right.
    +  goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_RIGHT,
    +                                    popup, corner.TOP_RIGHT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertEquals('Right edge of popup should line up with right edge of anchor.',
    +               anchorRect.left + anchorRect.width,
    +               popupRect.left + popupRect.width);
    +  assertRoughlyEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top,
    +                      1);
    +
    +  // Anchor top right to top left.
    +  goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                    popup, corner.TOP_RIGHT);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertEquals('Right edge of popup should line up with left edge of anchor.',
    +               anchorRect.left,
    +               popupRect.left + popupRect.width);
    +  assertRoughlyEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top,
    +                      1);
    +}
    +
    +function testPositionAtAnchorSpecificViewport() {
    +  var anchor = document.getElementById('anchor1');
    +  var popup = document.getElementById('popup3');
    +
    +  // Anchor top right to top left within outerbox.
    +  var status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                                 popup, corner.TOP_RIGHT,
    +                                                 undefined, undefined,
    +                                                 overflow.FAIL_X);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertTrue('Positioning operation should have been successful.',
    +             (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +  assertTrue('X position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
    +  assertTrue('Y position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
    +  assertEquals('Right edge of popup should line up with left edge of anchor.',
    +               anchorRect.left,
    +               popupRect.left + popupRect.width);
    +  assertRoughlyEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top,
    +                      1);
    +
    +  // position again within box1.
    +  var box = document.getElementById('box1');
    +  var viewport = goog.style.getBounds(box);
    +  status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                             popup, corner.TOP_RIGHT,
    +                                             undefined, undefined,
    +                                             overflow.FAIL_X, undefined,
    +                                             viewport);
    +  assertFalse('Positioning operation should have failed.',
    +              (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +
    +  // Change overflow strategy to adjust.
    +  status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                             popup, corner.TOP_RIGHT,
    +                                             undefined, undefined,
    +                                             overflow.ADJUST_X, undefined,
    +                                             viewport);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertTrue('Positioning operation should have been successful.',
    +             (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +  assertFalse('X position should have been adjusted.',
    +              (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
    +  assertTrue('Y position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
    +  assertRoughlyEquals(
    +      'Left edge of popup should line up with left edge of viewport.',
    +      viewport.left, popupRect.left, EPSILON);
    +  assertRoughlyEquals('Popup should have the same y position as the anchor.',
    +                      anchorRect.top,
    +                      popupRect.top,
    +                      1);
    +}
    +
    +function testPositionAtAnchorOutsideViewport() {
    +  var anchor = document.getElementById('anchor4');
    +  var popup = document.getElementById('popup1');
    +
    +  var status = goog.positioning.positionAtAnchor(anchor, corner.TOP_LEFT,
    +                                                 popup, corner.TOP_RIGHT);
    +  var anchorRect = goog.style.getBounds(anchor);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertTrue('Positioning operation should have been successful.',
    +             (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +  assertTrue('X position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
    +  assertTrue('Y position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
    +
    +  assertEquals('Right edge of popup should line up with left edge of anchor.',
    +               anchorRect.left,
    +               popupRect.left + popupRect.width);
    +
    +  // Change overflow strategy to fail.
    +  status = goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_RIGHT,
    +                                             popup, corner.TOP_RIGHT,
    +                                             undefined, undefined,
    +                                             overflow.FAIL_X);
    +  assertFalse('Positioning operation should have failed.',
    +              (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +
    +  // Change overflow strategy to adjust.
    +  status = goog.positioning.positionAtAnchor(anchor, corner.BOTTOM_RIGHT,
    +                                             popup, corner.TOP_RIGHT,
    +                                             undefined, undefined,
    +                                             overflow.ADJUST_X);
    +  anchorRect = goog.style.getBounds(anchor);
    +  popupRect = goog.style.getBounds(popup);
    +  assertTrue('Positioning operation should have been successful.',
    +             (status & goog.positioning.OverflowStatus.FAILED) == 0);
    +  assertFalse('X position should have been adjusted.',
    +              (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
    +  assertTrue('Y position should not have been adjusted.',
    +             (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
    +  assertRoughlyEquals(
    +      'Left edge of popup should line up with left edge of viewport.',
    +      0, popupRect.left, EPSILON);
    +  assertEquals('Popup should be positioned just below the anchor.',
    +               anchorRect.top + anchorRect.height,
    +               popupRect.top);
    +}
    +
    +
    +function testAdjustForViewportFailIgnore() {
    +  var f = goog.positioning.adjustForViewport_;
    +  var viewport = new goog.math.Box(100, 200, 200, 100);
    +  var overflow = goog.positioning.Overflow.IGNORE;
    +
    +  var pos = newCoord(150, 150);
    +  var size = newSize(50, 50);
    +  assertEquals('Viewport overflow should be ignored.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(150, 150);
    +  size = newSize(100, 50);
    +  assertEquals('Viewport overflow should be ignored.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(50, 50);
    +  size = newSize(50, 50);
    +  assertEquals('Viewport overflow should be ignored.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +}
    +
    +
    +function testAdjustForViewportFailXY() {
    +  var f = goog.positioning.adjustForViewport_;
    +  var viewport = new goog.math.Box(100, 200, 200, 100);
    +  var overflow = goog.positioning.Overflow.FAIL_X |
    +                 goog.positioning.Overflow.FAIL_Y;
    +
    +  var pos = newCoord(150, 150);
    +  var size = newSize(50, 50);
    +  assertEquals('Element should not overflow viewport.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(150, 150);
    +  size = newSize(100, 50);
    +  assertEquals('Element should overflow the right edge of viewport.',
    +               goog.positioning.OverflowStatus.FAILED_RIGHT,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(150, 150);
    +  size = newSize(50, 100);
    +  assertEquals('Element should overflow the bottom edge of viewport.',
    +               goog.positioning.OverflowStatus.FAILED_BOTTOM,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(50, 150);
    +  size = newSize(50, 50);
    +  assertEquals('Element should overflow the left edge of viewport.',
    +               goog.positioning.OverflowStatus.FAILED_LEFT,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(150, 50);
    +  size = newSize(50, 50);
    +  assertEquals('Element should overflow the top edge of viewport.',
    +               goog.positioning.OverflowStatus.FAILED_TOP,
    +               f(pos, size, viewport, overflow));
    +
    +  pos = newCoord(50, 50);
    +  size = newSize(50, 50);
    +  assertEquals('Element should overflow the left & top edges of viewport.',
    +               goog.positioning.OverflowStatus.FAILED_LEFT |
    +               goog.positioning.OverflowStatus.FAILED_TOP,
    +               f(pos, size, viewport, overflow));
    +}
    +
    +
    +function testAdjustForViewportAdjustXFailY() {
    +  var f = goog.positioning.adjustForViewport_;
    +  var viewport = new goog.math.Box(100, 200, 200, 100);
    +  var overflow = goog.positioning.Overflow.ADJUST_X |
    +                 goog.positioning.Overflow.FAIL_Y;
    +
    +  var pos = newCoord(150, 150);
    +  var size = newSize(50, 50);
    +  assertEquals('Element should not overflow viewport.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('X Position should not have been changed.', 150, pos.x);
    +  assertEquals('Y Position should not have been changed.', 150, pos.y);
    +
    +  pos = newCoord(150, 150);
    +  size = newSize(100, 50);
    +  assertEquals('Element position should be adjusted not to overflow right ' +
    +               'edge of viewport.',
    +               goog.positioning.OverflowStatus.ADJUSTED_X,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
    +  assertEquals('Y Position should not have been changed.', 150, pos.y);
    +
    +  pos = newCoord(50, 150);
    +  size = newSize(100, 50);
    +  assertEquals('Element position should be adjusted not to overflow left ' +
    +               'edge of viewport.',
    +               goog.positioning.OverflowStatus.ADJUSTED_X,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
    +  assertEquals('Y Position should not have been changed.', 150, pos.y);
    +
    +
    +  pos = newCoord(50, 50);
    +  size = newSize(100, 50);
    +  assertEquals('Element position should be adjusted not to overflow left ' +
    +               'edge of viewport, should overflow bottom edge.',
    +               goog.positioning.OverflowStatus.ADJUSTED_X |
    +               goog.positioning.OverflowStatus.FAILED_TOP,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
    +  assertEquals('Y Position should not have been changed.', 50, pos.y);
    +}
    +
    +
    +function testAdjustForViewportResizeHeight() {
    +  var f = goog.positioning.adjustForViewport_;
    +  var viewport = new goog.math.Box(0, 200, 200, 0);
    +  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT;
    +
    +  var pos = newCoord(150, 150);
    +  var size = newSize(25, 100);
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('Height should be resized to 50.',
    +               50, size.height);
    +  assertTrue('Output box is within viewport',
    +             viewport.contains(new goog.math.Box(pos.y, pos.x + size.width,
    +                                                 pos.y + size.height, pos.x)));
    +
    +  var pos = newCoord(0, 0);
    +  var size = newSize(50, 250);
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('Height should be resized to 200.',
    +               200, size.height);
    +  assertTrue('Output box is within viewport',
    +             viewport.contains(new goog.math.Box(pos.y, pos.x + size.width,
    +                                                 pos.y + size.height, pos.x)));
    +
    +  var pos = newCoord(0, -50);
    +  var size = newSize(50, 240);
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
    +               f(pos, size, viewport, overflow));
    +  assertEquals('Height should be resized to 190.',
    +               190, size.height);
    +  assertTrue('Output box is within viewport',
    +             viewport.contains(new goog.math.Box(pos.y, pos.x + size.width,
    +                                                 pos.y + size.height, pos.x)));
    +
    +  pos = newCoord(150, 150);
    +  size = newSize(50, 50);
    +  assertEquals('No Viewport overflow.',
    +               goog.positioning.OverflowStatus.NONE,
    +               f(pos, size, viewport, overflow));
    +  assertTrue('Output box is within viewport',
    +             viewport.contains(new goog.math.Box(pos.y, pos.x + size.width,
    +                                                 pos.y + size.height, pos.x)));
    +
    +  var offsetViewport = new goog.math.Box(100, 200, 300, 0);
    +  var pos = newCoord(0, 50);
    +  var size = newSize(50, 240);
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
    +               f(pos, size, offsetViewport, overflow));
    +  assertEquals('Height should be resized to 190.',
    +               190, size.height);
    +  assertTrue('Output box is within viewport',
    +             offsetViewport.contains(new goog.math.Box(pos.y,
    +                                                       pos.x + size.width,
    +                                                       pos.y + size.height,
    +                                                       pos.x)));
    +}
    +
    +
    +function testPositionAtAnchorWithResizeHeight() {
    +  var anchor = document.getElementById('anchor9');
    +  var popup = document.getElementById('popup9');
    +  var box = document.getElementById('box9');
    +  var viewport = goog.style.getBounds(box);
    +
    +  var status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_START, popup, corner.TOP_START,
    +      new goog.math.Coordinate(0, -20), null,
    +      goog.positioning.Overflow.RESIZE_HEIGHT, null,
    +      viewport.toBox());
    +  assertEquals('Status should be HEIGHT_ADJUSTED.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED, status);
    +
    +  var TOLERANCE = 0.1;
    +  // Adjust the viewport to allow some tolerance for subpixel positioning,
    +  // this is required for this test to pass on IE10,11
    +  viewport.top -= TOLERANCE;
    +  viewport.left -= TOLERANCE;
    +
    +  assertTrue('Popup ' + goog.style.getBounds(popup) +
    +             ' not is within viewport' + viewport,
    +             viewport.contains(goog.style.getBounds(popup)));
    +}
    +
    +
    +function testPositionAtCoordinateResizeHeight() {
    +  var f = goog.positioning.positionAtCoordinate;
    +  var viewport = new goog.math.Box(0, 50, 50, 0);
    +  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT |
    +      goog.positioning.Overflow.ADJUST_Y;
    +  var popup = document.getElementById('popup1');
    +  var corner = goog.positioning.Corner.BOTTOM_LEFT;
    +
    +  var pos = newCoord(100, 100);
    +
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED |
    +               goog.positioning.OverflowStatus.ADJUSTED_Y,
    +               f(pos, popup, corner, undefined, viewport, overflow));
    +  var bounds = goog.style.getSize(popup);
    +  assertEquals('Height should be resized to the size of the viewport.',
    +               50, bounds.height);
    +}
    +
    +
    +function testGetPositionAtCoordinateResizeHeight() {
    +  var f = goog.positioning.getPositionAtCoordinate;
    +  var viewport = new goog.math.Box(0, 50, 50, 0);
    +  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT |
    +      goog.positioning.Overflow.ADJUST_Y;
    +  var popup = document.getElementById('popup1');
    +  var corner = goog.positioning.Corner.BOTTOM_LEFT;
    +
    +  var pos = newCoord(100, 100);
    +  var size = goog.style.getSize(popup);
    +
    +  var result = f(pos, size, corner, undefined, viewport, overflow);
    +  assertEquals('Viewport height should be resized.',
    +               goog.positioning.OverflowStatus.HEIGHT_ADJUSTED |
    +               goog.positioning.OverflowStatus.ADJUSTED_Y,
    +               result.status);
    +  assertEquals('Height should be resized to the size of the viewport.',
    +               50, result.rect.height);
    +}
    +
    +
    +function testGetEffectiveCornerLeftToRight() {
    +  var f = goog.positioning.getEffectiveCorner;
    +  var el = document.getElementById('ltr');
    +
    +  assertEquals('TOP_LEFT should be unchanged for ltr.',
    +               corner.TOP_LEFT,
    +               f(el, corner.TOP_LEFT));
    +  assertEquals('TOP_RIGHT should be unchanged for ltr.',
    +               corner.TOP_RIGHT,
    +               f(el, corner.TOP_RIGHT));
    +  assertEquals('BOTTOM_LEFT should be unchanged for ltr.',
    +               corner.BOTTOM_LEFT,
    +               f(el, corner.BOTTOM_LEFT));
    +  assertEquals('BOTTOM_RIGHT should be unchanged for ltr.',
    +               corner.BOTTOM_RIGHT,
    +               f(el, corner.BOTTOM_RIGHT));
    +
    +  assertEquals('TOP_START should be TOP_LEFT for ltr.',
    +               corner.TOP_LEFT,
    +               f(el, corner.TOP_START));
    +  assertEquals('TOP_END should be TOP_RIGHT for ltr.',
    +               corner.TOP_RIGHT,
    +               f(el, corner.TOP_END));
    +  assertEquals('BOTTOM_START should be BOTTOM_LEFT for ltr.',
    +               corner.BOTTOM_LEFT,
    +               f(el, corner.BOTTOM_START));
    +  assertEquals('BOTTOM_END should be BOTTOM_RIGHT for ltr.',
    +               corner.BOTTOM_RIGHT,
    +               f(el, corner.BOTTOM_END));
    +}
    +
    +
    +function testGetEffectiveCornerRightToLeft() {
    +  var f = goog.positioning.getEffectiveCorner;
    +  var el = document.getElementById('rtl');
    +
    +  assertEquals('TOP_LEFT should be unchanged for rtl.',
    +               corner.TOP_LEFT,
    +               f(el, corner.TOP_LEFT));
    +  assertEquals('TOP_RIGHT should be unchanged for rtl.',
    +               corner.TOP_RIGHT,
    +               f(el, corner.TOP_RIGHT));
    +  assertEquals('BOTTOM_LEFT should be unchanged for rtl.',
    +               corner.BOTTOM_LEFT,
    +               f(el, corner.BOTTOM_LEFT));
    +  assertEquals('BOTTOM_RIGHT should be unchanged for rtl.',
    +               corner.BOTTOM_RIGHT,
    +               f(el, corner.BOTTOM_RIGHT));
    +
    +  assertEquals('TOP_START should be TOP_RIGHT for rtl.',
    +               corner.TOP_RIGHT,
    +               f(el, corner.TOP_START));
    +  assertEquals('TOP_END should be TOP_LEFT for rtl.',
    +               corner.TOP_LEFT,
    +               f(el, corner.TOP_END));
    +  assertEquals('BOTTOM_START should be BOTTOM_RIGHT for rtl.',
    +               corner.BOTTOM_RIGHT,
    +               f(el, corner.BOTTOM_START));
    +  assertEquals('BOTTOM_END should be BOTTOM_LEFT for rtl.',
    +               corner.BOTTOM_LEFT,
    +               f(el, corner.BOTTOM_END));
    +}
    +
    +
    +function testFlipCornerHorizontal() {
    +  var f = goog.positioning.flipCornerHorizontal;
    +
    +  assertEquals('TOP_LEFT should be flipped to TOP_RIGHT.',
    +               corner.TOP_RIGHT,
    +               f(corner.TOP_LEFT));
    +  assertEquals('TOP_RIGHT should be flipped to TOP_LEFT.',
    +               corner.TOP_LEFT,
    +               f(corner.TOP_RIGHT));
    +  assertEquals('BOTTOM_LEFT should be flipped to BOTTOM_RIGHT.',
    +               corner.BOTTOM_RIGHT,
    +               f(corner.BOTTOM_LEFT));
    +  assertEquals('BOTTOM_RIGHT should be flipped to BOTTOM_LEFT.',
    +               corner.BOTTOM_LEFT,
    +               f(corner.BOTTOM_RIGHT));
    +
    +  assertEquals('TOP_START should be flipped to TOP_END.',
    +               corner.TOP_END,
    +               f(corner.TOP_START));
    +  assertEquals('TOP_END should be flipped to TOP_START.',
    +               corner.TOP_START,
    +               f(corner.TOP_END));
    +  assertEquals('BOTTOM_START should be flipped to BOTTOM_END.',
    +               corner.BOTTOM_END,
    +               f(corner.BOTTOM_START));
    +  assertEquals('BOTTOM_END should be flipped to BOTTOM_START.',
    +               corner.BOTTOM_START,
    +               f(corner.BOTTOM_END));
    +}
    +
    +
    +function testFlipCornerVertical() {
    +  var f = goog.positioning.flipCornerVertical;
    +
    +  assertEquals('TOP_LEFT should be flipped to BOTTOM_LEFT.',
    +               corner.BOTTOM_LEFT,
    +               f(corner.TOP_LEFT));
    +  assertEquals('TOP_RIGHT should be flipped to BOTTOM_RIGHT.',
    +               corner.BOTTOM_RIGHT,
    +               f(corner.TOP_RIGHT));
    +  assertEquals('BOTTOM_LEFT should be flipped to TOP_LEFT.',
    +               corner.TOP_LEFT,
    +               f(corner.BOTTOM_LEFT));
    +  assertEquals('BOTTOM_RIGHT should be flipped to TOP_RIGHT.',
    +               corner.TOP_RIGHT,
    +               f(corner.BOTTOM_RIGHT));
    +
    +  assertEquals('TOP_START should be flipped to BOTTOM_START.',
    +               corner.BOTTOM_START,
    +               f(corner.TOP_START));
    +  assertEquals('TOP_END should be flipped to BOTTOM_END.',
    +               corner.BOTTOM_END,
    +               f(corner.TOP_END));
    +  assertEquals('BOTTOM_START should be flipped to TOP_START.',
    +               corner.TOP_START,
    +               f(corner.BOTTOM_START));
    +  assertEquals('BOTTOM_END should be flipped to TOP_END.',
    +               corner.TOP_END,
    +               f(corner.BOTTOM_END));
    +}
    +
    +
    +function testFlipCorner() {
    +  var f = goog.positioning.flipCorner;
    +
    +  assertEquals('TOP_LEFT should be flipped to BOTTOM_RIGHT.',
    +               corner.BOTTOM_RIGHT,
    +               f(corner.TOP_LEFT));
    +  assertEquals('TOP_RIGHT should be flipped to BOTTOM_LEFT.',
    +               corner.BOTTOM_LEFT,
    +               f(corner.TOP_RIGHT));
    +  assertEquals('BOTTOM_LEFT should be flipped to TOP_RIGHT.',
    +               corner.TOP_RIGHT,
    +               f(corner.BOTTOM_LEFT));
    +  assertEquals('BOTTOM_RIGHT should be flipped to TOP_LEFT.',
    +               corner.TOP_LEFT,
    +               f(corner.BOTTOM_RIGHT));
    +
    +  assertEquals('TOP_START should be flipped to BOTTOM_END.',
    +               corner.BOTTOM_END,
    +               f(corner.TOP_START));
    +  assertEquals('TOP_END should be flipped to BOTTOM_START.',
    +               corner.BOTTOM_START,
    +               f(corner.TOP_END));
    +  assertEquals('BOTTOM_START should be flipped to TOP_END.',
    +               corner.TOP_END,
    +               f(corner.BOTTOM_START));
    +  assertEquals('BOTTOM_END should be flipped to TOP_START.',
    +               corner.TOP_START,
    +               f(corner.BOTTOM_END));
    +}
    +
    +function testPositionAtAnchorFrameViewportStandard() {
    +  var iframe = document.getElementById('iframe-standard');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  assertTrue(new goog.dom.DomHelper(iframeDoc).isCss1CompatMode());
    +
    +  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
    +  var anchor = iframeDoc.getElementById('anchor1');
    +  var popup = document.getElementById('popup6');
    +
    +  var status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
    +  var iframeRect = goog.style.getBounds(iframe);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals('Status should not have any ADJUSTED and FAILED.',
    +               goog.positioning.OverflowStatus.NONE, status);
    +  assertRoundedEquals('Popup should be positioned just above the iframe, ' +
    +                      'not above the anchor element inside the iframe',
    +                      iframeRect.top,
    +                      popupRect.top + popupRect.height);
    +}
    +
    +function testPositionAtAnchorFrameViewportQuirk() {
    +  var iframe = document.getElementById('iframe-quirk');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  assertFalse(new goog.dom.DomHelper(iframeDoc).isCss1CompatMode());
    +
    +  window.scrollTo(0, 100);
    +  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
    +  var anchor = iframeDoc.getElementById('anchor1');
    +  var popup = document.getElementById('popup6');
    +
    +  var status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
    +  var iframeRect = goog.style.getBounds(iframe);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals('Status should not have any ADJUSTED and FAILED.',
    +               goog.positioning.OverflowStatus.NONE, status);
    +  assertRoundedEquals('Popup should be positioned just above the iframe, ' +
    +                      'not above the anchor element inside the iframe',
    +                      iframeRect.top,
    +                      popupRect.top + popupRect.height);
    +}
    +
    +function testPositionAtAnchorFrameViewportWithPopupInScroller() {
    +  var iframe = document.getElementById('iframe-standard');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +
    +  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
    +  var anchor = iframeDoc.getElementById('anchor1');
    +  var popup = document.getElementById('popup7');
    +  popup.offsetParent.scrollTop = 50;
    +
    +  var status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
    +  var iframeRect = goog.style.getBounds(iframe);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertEquals('Status should not have any ADJUSTED and FAILED.',
    +               goog.positioning.OverflowStatus.NONE, status);
    +  assertRoughlyEquals('Popup should be positioned just above the iframe, ' +
    +      'not above the anchor element inside the iframe',
    +      iframeRect.top,
    +      popupRect.top + popupRect.height,
    +      ALLOWED_OFFSET);
    +}
    +
    +function testPositionAtAnchorNestedFrames() {
    +  var outerIframe = document.getElementById('nested-outer');
    +  var outerDoc = goog.dom.getFrameContentDocument(outerIframe);
    +  var popup = outerDoc.getElementById('popup1');
    +  var innerIframe = outerDoc.getElementById('inner-frame');
    +  var innerDoc = goog.dom.getFrameContentDocument(innerIframe);
    +  var anchor = innerDoc.getElementById('anchor1');
    +
    +  var status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.BOTTOM_LEFT);
    +  assertEquals('Status should not have any ADJUSTED and FAILED.',
    +               goog.positioning.OverflowStatus.NONE, status);
    +  var innerIframeRect = goog.style.getBounds(innerIframe);
    +  var popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Top of frame should align with bottom of the popup',
    +                      innerIframeRect.top, popupRect.top + popupRect.height);
    +
    +  // The anchor is scrolled up by 10px.
    +  // Popup position should be the same as above.
    +  goog.dom.getWindow(innerDoc).scrollTo(0, 10);
    +  status = goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.BOTTOM_LEFT);
    +  assertEquals('Status should not have any ADJUSTED and FAILED.',
    +               goog.positioning.OverflowStatus.NONE, status);
    +  innerIframeRect = goog.style.getBounds(innerIframe);
    +  popupRect = goog.style.getBounds(popup);
    +  assertRoundedEquals('Top of frame should align with bottom of the popup',
    +                      innerIframeRect.top, popupRect.top + popupRect.height);
    +}
    +
    +function testPositionAtAnchorOffscreen() {
    +  var offset = 0;
    +  var anchor = goog.dom.getElement('offscreen-anchor');
    +  var popup = goog.dom.getElement('popup3');
    +
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectEquals(
    +      newCoord(offset, offset), goog.style.getPageOffset(popup));
    +
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X_EXCEPT_OFFSCREEN | overflow.ADJUST_Y);
    +  assertObjectEquals(
    +      newCoord(-1000, offset), goog.style.getPageOffset(popup));
    +
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y_EXCEPT_OFFSCREEN);
    +  assertObjectEquals(
    +      newCoord(offset, -1000), goog.style.getPageOffset(popup));
    +
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X_EXCEPT_OFFSCREEN | overflow.ADJUST_Y_EXCEPT_OFFSCREEN);
    +  assertObjectEquals(
    +      newCoord(-1000, -1000),
    +      goog.style.getPageOffset(popup));
    +}
    +
    +function testPositionAtAnchorWithOverflowScrollOffsetParent() {
    +  var testAreaOffset = goog.style.getPageOffset(testArea);
    +  var scrollbarWidth = goog.style.getScrollbarWidth();
    +  window.scrollTo(testAreaOffset.x, testAreaOffset.y);
    +
    +  var overflowDiv = goog.dom.createElement('div');
    +  overflowDiv.style.overflow = 'scroll';
    +  overflowDiv.style.position = 'relative';
    +  goog.style.setSize(overflowDiv, 200 /* width */, 100 /* height */);
    +
    +  var anchor = goog.dom.createElement('div');
    +  anchor.style.position = 'absolute';
    +  goog.style.setSize(anchor, 50 /* width */, 50 /* height */);
    +  goog.style.setPosition(anchor, 300 /* left */, 300 /* top */);
    +
    +  var popup = createPopupDiv(75 /* width */, 50 /* height */);
    +
    +  goog.dom.append(testArea, overflowDiv, anchor);
    +  goog.dom.append(overflowDiv, popup);
    +
    +  // Popup should always be positioned within the overflowDiv
    +  goog.style.setPosition(overflowDiv, 0 /* left */, 0 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 200 - 75 - scrollbarWidth,
    +          testAreaOffset.y + 100 - 50 - scrollbarWidth),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 400 /* left */, 0 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_RIGHT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 400, testAreaOffset.y + 100 - 50 - scrollbarWidth),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 0 /* left */, 400 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.BOTTOM_LEFT, popup, corner.BOTTOM_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 200 - 75 - scrollbarWidth, testAreaOffset.y + 400),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 400 /* left */, 400 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.BOTTOM_RIGHT, popup, corner.BOTTOM_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 400, testAreaOffset.y + 400),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  // No overflow.
    +  goog.style.setPosition(overflowDiv, 300 - 50 /* left */, 300 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 300 - 50, testAreaOffset.y + 300),
    +      goog.style.getPageOffset(popup),
    +      1);
    +}
    +
    +function testPositionAtAnchorWithOverflowHiddenParent() {
    +  var testAreaOffset = goog.style.getPageOffset(testArea);
    +  window.scrollTo(testAreaOffset.x, testAreaOffset.y);
    +
    +  var overflowDiv = goog.dom.createElement('div');
    +  overflowDiv.style.overflow = 'hidden';
    +  overflowDiv.style.position = 'relative';
    +  goog.style.setSize(overflowDiv, 200 /* width */, 100 /* height */);
    +
    +  var anchor = goog.dom.createElement('div');
    +  anchor.style.position = 'absolute';
    +  goog.style.setSize(anchor, 50 /* width */, 50 /* height */);
    +  goog.style.setPosition(anchor, 300 /* left */, 300 /* top */);
    +
    +  var popup = createPopupDiv(75 /* width */, 50 /* height */);
    +
    +  goog.dom.append(testArea, overflowDiv, anchor);
    +  goog.dom.append(overflowDiv, popup);
    +
    +  // Popup should always be positioned within the overflowDiv
    +  goog.style.setPosition(overflowDiv, 0 /* left */, 0 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 200 - 75, testAreaOffset.y + 100 - 50),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 400 /* left */, 0 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_RIGHT, popup, corner.TOP_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 400, testAreaOffset.y + 100 - 50),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 0 /* left */, 400 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.BOTTOM_LEFT, popup, corner.BOTTOM_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 200 - 75, testAreaOffset.y + 400),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  goog.style.setPosition(overflowDiv, 400 /* left */, 400 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.BOTTOM_RIGHT, popup, corner.BOTTOM_LEFT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 400, testAreaOffset.y + 400),
    +      goog.style.getPageOffset(popup),
    +      1);
    +
    +  // No overflow.
    +  goog.style.setPosition(overflowDiv, 300 - 50 /* left */, 300 /* top */);
    +  goog.positioning.positionAtAnchor(
    +      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
    +      overflow.ADJUST_X | overflow.ADJUST_Y);
    +  assertObjectRoughlyEquals(
    +      new goog.math.Coordinate(
    +          testAreaOffset.x + 300 - 50, testAreaOffset.y + 300),
    +      goog.style.getPageOffset(popup),
    +      1);
    +}
    +
    +function createPopupDiv(width, height) {
    +  var popupDiv = goog.dom.createElement('div');
    +  popupDiv.style.position = 'absolute';
    +  goog.style.setSize(popupDiv, width, height);
    +  goog.style.setPosition(popupDiv, 0 /* left */, 250 /* top */);
    +  return popupDiv;
    +}
    +
    +function newCoord(x, y) {
    +  return new goog.math.Coordinate(x, y);
    +}
    +
    +function newSize(w, h) {
    +  return new goog.math.Size(w, h);
    +}
    +
    +function newBox(coord, size) {
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe1.html b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe1.html
    new file mode 100644
    index 0000000000..6288f7a7b0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe1.html
    @@ -0,0 +1,16 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    +            "http://www.w3.org/TR/html4/loose.dtd">
    +<!--
    +-->
    +<html><body>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<p>The following IFRAME's Y-position is not 0.</p>
    +<iframe id="inner-frame" src="positioning_test_iframe2.html"
    + style="overflow:auto;width:200px;height:200px;border:0px;"></iframe>
    +<div id="popup1" style="position:absolute;width:16px;height:16px;background-color:#088;"></div>
    +</body></html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe2.html b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe2.html
    new file mode 100644
    index 0000000000..5c35b29bee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_iframe2.html
    @@ -0,0 +1,13 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    +            "http://www.w3.org/TR/html4/loose.dtd">
    +<!--
    +-->
    +<html><body style="border:0px;margin:0px;">
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<div id="anchor1" style="width:300px;height:300px;border:0px;background-color:#008;"></div>
    +</body></html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_quirk.html b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_quirk.html
    new file mode 100644
    index 0000000000..5906053f67
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_quirk.html
    @@ -0,0 +1,9 @@
    +<!--
    +-->
    +<html><body style="border:0px;"><div id="anchor1" style="width:400px;height:400px;"></body></html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_standard.html b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_standard.html
    new file mode 100644
    index 0000000000..36d7784d22
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/positioning_test_standard.html
    @@ -0,0 +1,13 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    +            "http://www.w3.org/TR/html4/loose.dtd">
    +<!--
    +-->
    +<html><body>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<div id="anchor1" style="width:400px;height:400px;"></div>
    +</body></html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition.js b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition.js
    new file mode 100644
    index 0000000000..0f7e1a01be
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition.js
    @@ -0,0 +1,124 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Client viewport positioning class.
    + *
    + * @author robbyw@google.com (Robert Walker)
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.ViewportClientPosition');
    +
    +goog.require('goog.dom');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.ClientPosition');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned relative to the
    + * window (client) coordinates, and made to stay within the viewport.
    + *
    + * @param {number|goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position if arg1 is a number representing the
    + *     left position, ignored otherwise.
    + * @constructor
    + * @extends {goog.positioning.ClientPosition}
    + */
    +goog.positioning.ViewportClientPosition = function(arg1, opt_arg2) {
    +  goog.positioning.ClientPosition.call(this, arg1, opt_arg2);
    +};
    +goog.inherits(goog.positioning.ViewportClientPosition,
    +              goog.positioning.ClientPosition);
    +
    +
    +/**
    + * The last-resort overflow strategy, if the popup fails to fit.
    + * @type {number}
    + * @private
    + */
    +goog.positioning.ViewportClientPosition.prototype.lastResortOverflow_ = 0;
    +
    +
    +/**
    + * Set the last-resort overflow strategy, if the popup fails to fit.
    + * @param {number} overflow A bitmask of goog.positioning.Overflow strategies.
    + */
    +goog.positioning.ViewportClientPosition.prototype.setLastResortOverflow =
    +    function(overflow) {
    +  this.lastResortOverflow_ = overflow;
    +};
    +
    +
    +/**
    + * Repositions the popup according to the current state.
    + *
    + * @param {Element} element The DOM element of the popup.
    + * @param {goog.positioning.Corner} popupCorner The corner of the popup
    + *     element that that should be positioned adjacent to the anchorElement.
    + *     One of the goog.positioning.Corner constants.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize Preferred size fo the element.
    + * @override
    + */
    +goog.positioning.ViewportClientPosition.prototype.reposition = function(
    +    element, popupCorner, opt_margin, opt_preferredSize) {
    +  var viewportElt = goog.style.getClientViewportElement(element);
    +  var viewport = goog.style.getVisibleRectForElement(viewportElt);
    +  var scrollEl = goog.dom.getDomHelper(element).getDocumentScrollElement();
    +  var clientPos = new goog.math.Coordinate(
    +      this.coordinate.x + scrollEl.scrollLeft,
    +      this.coordinate.y + scrollEl.scrollTop);
    +
    +  var failXY = goog.positioning.Overflow.FAIL_X |
    +               goog.positioning.Overflow.FAIL_Y;
    +  var corner = popupCorner;
    +
    +  // Try the requested position.
    +  var status = goog.positioning.positionAtCoordinate(clientPos, element, corner,
    +      opt_margin, viewport, failXY, opt_preferredSize);
    +  if ((status & goog.positioning.OverflowStatus.FAILED) == 0) {
    +    return;
    +  }
    +
    +  // Outside left or right edge of viewport, try try to flip it horizontally.
    +  if (status & goog.positioning.OverflowStatus.FAILED_LEFT ||
    +      status & goog.positioning.OverflowStatus.FAILED_RIGHT) {
    +    corner = goog.positioning.flipCornerHorizontal(corner);
    +  }
    +
    +  // Outside top or bottom edge of viewport, try try to flip it vertically.
    +  if (status & goog.positioning.OverflowStatus.FAILED_TOP ||
    +      status & goog.positioning.OverflowStatus.FAILED_BOTTOM) {
    +    corner = goog.positioning.flipCornerVertical(corner);
    +  }
    +
    +  // Try flipped position.
    +  status = goog.positioning.positionAtCoordinate(clientPos, element, corner,
    +      opt_margin, viewport, failXY, opt_preferredSize);
    +  if ((status & goog.positioning.OverflowStatus.FAILED) == 0) {
    +    return;
    +  }
    +
    +  // If that failed, the viewport is simply too small to contain the popup.
    +  // Revert to the original position.
    +  goog.positioning.positionAtCoordinate(
    +      clientPos, element, popupCorner, opt_margin, viewport,
    +      this.lastResortOverflow_, opt_preferredSize);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.html b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.html
    new file mode 100644
    index 0000000000..ec8ecc71d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE HTML>
    +<!--
    +
    +  Author: eae@google.com (Emil A Eklund)
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.positioning.ViewportClientPosition
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.positioning.ViewportClientPositionTest');
    +  </script>
    + </head>
    + <body>
    +  <!--
    +    Uses an iframe to avoid window size problems when running under Selenium.
    +  -->
    +  <iframe id="frame1" style="width: 200px; height: 200px;" src="anchoredviewportposition_test_iframe.html">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.js b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.js
    new file mode 100644
    index 0000000000..7fac480705
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/viewportclientposition_test.js
    @@ -0,0 +1,178 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.positioning.ViewportClientPositionTest');
    +goog.setTestOnly('goog.positioning.ViewportClientPositionTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.ViewportClientPosition');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var viewportSize, anchor, popup, dom, frameRect;
    +var corner = goog.positioning.Corner;
    +
    +// Allow positions to be off by one in gecko as it reports scrolling
    +// offsets in steps of 2.
    +var ALLOWED_OFFSET = goog.userAgent.GECKO ? 1 : 0;
    +
    +
    +function setUp() {
    +  var frame = document.getElementById('frame1');
    +  var doc = goog.dom.getFrameContentDocument(frame);
    +
    +  dom = goog.dom.getDomHelper(doc);
    +  viewportSize = dom.getViewportSize();
    +  anchor = dom.getElement('anchor');
    +  popup = dom.getElement('popup');
    +  popup.style.overflowY = 'visible';
    +  goog.style.setSize(popup, 20, 20);
    +  frameRect = goog.style.getVisibleRectForElement(doc.body);
    +}
    +
    +
    +function testPositionAtCoordinateTopLeft() {
    +  var pos = new goog.positioning.ViewportClientPosition(100, 100);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +
    +  var offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should be at specified x coordinate.',
    +               100,
    +               offset.x);
    +  assertEquals('Top edge of popup should be at specified y coordinate.',
    +               100,
    +               offset.y);
    +}
    +
    +
    +function testPositionAtCoordinateBottomRight() {
    +  var pos = new goog.positioning.ViewportClientPosition(100, 100);
    +  pos.reposition(popup, corner.BOTTOM_RIGHT);
    +
    +  var bounds = goog.style.getBounds(popup);
    +  assertEquals('Right edge of popup should be at specified x coordinate.',
    +               100,
    +               bounds.left + bounds.width);
    +  assertEquals('Bottom edge of popup should be at specified x coordinate.',
    +               100,
    +               bounds.top + bounds.height);
    +}
    +
    +
    +function testPositionAtCoordinateTopLeftWithScroll() {
    +  dom.getDocument().body.style.paddingTop = '300px';
    +  dom.getDocument().body.style.height = '3000px';
    +  dom.getDocumentScrollElement().scrollTop = 50;
    +  dom.getDocument().body.scrollTop = 50;
    +
    +  var pos = new goog.positioning.ViewportClientPosition(0, 0);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +
    +  var offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should be at specified x coordinate.',
    +               0,
    +               offset.x);
    +  assertTrue('Top edge of popup should be at specified y coordinate ' +
    +             'adjusted for scroll.',
    +             Math.abs(offset.y - 50) <= ALLOWED_OFFSET);
    +
    +  dom.getDocument().body.style.paddingLeft = '1000px';
    +  dom.getDocumentScrollElement().scrollLeft = 500;
    +
    +  pos.reposition(popup, corner.TOP_LEFT);
    +  offset = goog.style.getPageOffset(popup);
    +  assertTrue('Left edge of popup should be at specified x coordinate ' +
    +             'adjusted for scroll.',
    +             Math.abs(offset.x - 500) <= ALLOWED_OFFSET);
    +
    +  dom.getDocumentScrollElement().scrollLeft = 0;
    +  dom.getDocumentScrollElement().scrollTop = 0;
    +  dom.getDocument().body.style.paddingLeft = '';
    +  dom.getDocument().body.style.paddingTop = '';
    +
    +  pos.reposition(popup, corner.TOP_LEFT);
    +  offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should be at specified x coordinate.',
    +               0,
    +               offset.x);
    +  assertEquals('Top edge of popup should be at specified y coordinate.',
    +               0,
    +               offset.y);
    +}
    +
    +
    +function testOverflowRightFlipHor() {
    +  var pos = new goog.positioning.ViewportClientPosition(frameRect.right,
    +                                                        100);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +
    +  var offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should have been adjusted so that it ' +
    +      'fits inside the viewport.',
    +               frameRect.right - popup.offsetWidth,
    +               offset.x);
    +  assertEquals('Top edge of popup should be at specified y coordinate.',
    +               100,
    +               offset.y);
    +}
    +
    +
    +function testOverflowTopFlipVer() {
    +  var pos = new goog.positioning.ViewportClientPosition(100, 0);
    +  pos.reposition(popup, corner.TOP_RIGHT);
    +
    +  var offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should be at specified x coordinate.',
    +               80,
    +               offset.x);
    +  assertEquals('Top edge of popup should have been adjusted so that it ' +
    +      'fits inside the viewport.',
    +               0,
    +               offset.y);
    +}
    +
    +
    +function testOverflowBottomRightFlipBoth() {
    +  var pos = new goog.positioning.ViewportClientPosition(frameRect.right,
    +                                                        frameRect.bottom);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +
    +  var offset = goog.style.getPageOffset(popup);
    +  assertEquals('Left edge of popup should have been adjusted so that it ' +
    +      'fits inside the viewport.',
    +               frameRect.right - popup.offsetWidth,
    +               offset.x);
    +  assertEquals('Top edge of popup should have been adjusted so that it ' +
    +      'fits inside the viewport.',
    +               frameRect.bottom - popup.offsetHeight,
    +               offset.y);
    +}
    +
    +
    +function testLastRespotOverflow() {
    +  var large = 2000;
    +  goog.style.setSize(popup, 20, large);
    +  popup.style.overflowY = 'auto';
    +
    +  var pos = new goog.positioning.ViewportClientPosition(0, 0);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +
    +  assertEquals(large, popup.offsetHeight);
    +  pos.setLastResortOverflow(goog.positioning.Overflow.RESIZE_HEIGHT);
    +  pos.reposition(popup, corner.TOP_LEFT);
    +  assertNotEquals(large, popup.offsetHeight);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/positioning/viewportposition.js b/mapbox-gl/closure-library/closure/goog/positioning/viewportposition.js
    new file mode 100644
    index 0000000000..3b1dc07e5d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/positioning/viewportposition.js
    @@ -0,0 +1,66 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Client positioning class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.positioning.ViewportPosition');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AbstractPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned according to
    + * coordinates relative to the  element's viewport (page). This calculates the
    + * correct position to use even if the element is relatively positioned to some
    + * other element.
    + *
    + * @param {number|goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + */
    +goog.positioning.ViewportPosition = function(arg1, opt_arg2) {
    +  this.coordinate = arg1 instanceof goog.math.Coordinate ? arg1 :
    +      new goog.math.Coordinate(/** @type {number} */ (arg1), opt_arg2);
    +};
    +goog.inherits(goog.positioning.ViewportPosition,
    +              goog.positioning.AbstractPosition);
    +
    +
    +/**
    + * Repositions the popup according to the current state
    + *
    + * @param {Element} element The DOM element of the popup.
    + * @param {goog.positioning.Corner} popupCorner The corner of the popup
    + *     element that that should be positioned adjacent to the anchorElement.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {goog.math.Size=} opt_preferredSize Preferred size of the element.
    + * @override
    + */
    +goog.positioning.ViewportPosition.prototype.reposition = function(
    +    element, popupCorner, opt_margin, opt_preferredSize) {
    +  goog.positioning.positionAtAnchor(
    +      goog.style.getClientViewportElement(element),
    +      goog.positioning.Corner.TOP_LEFT, element, popupCorner,
    +      this.coordinate, opt_margin, null, opt_preferredSize);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/promise.js b/mapbox-gl/closure-library/closure/goog/promise/promise.js
    new file mode 100644
    index 0000000000..4ac8c00e2d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/promise.js
    @@ -0,0 +1,1004 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.Promise');
    +
    +goog.require('goog.Thenable');
    +goog.require('goog.asserts');
    +goog.require('goog.async.run');
    +goog.require('goog.async.throwException');
    +goog.require('goog.debug.Error');
    +goog.require('goog.promise.Resolver');
    +
    +
    +
    +/**
    + * Promises provide a result that may be resolved asynchronously. A Promise may
    + * be resolved by being fulfilled or rejected with a value, which will be known
    + * as the fulfillment value or the rejection reason. Whether fulfilled or
    + * rejected, the Promise result is immutable once it is set.
    + *
    + * Promises may represent results of any type, including undefined. Rejection
    + * reasons are typically Errors, but may also be of any type. Closure Promises
    + * allow for optional type annotations that enforce that fulfillment values are
    + * of the appropriate types at compile time.
    + *
    + * The result of a Promise is accessible by calling {@code then} and registering
    + * {@code onFulfilled} and {@code onRejected} callbacks. Once the Promise
    + * resolves, the relevant callbacks are invoked with the fulfillment value or
    + * rejection reason as argument. Callbacks are always invoked in the order they
    + * were registered, even when additional {@code then} calls are made from inside
    + * another callback. A callback is always run asynchronously sometime after the
    + * scope containing the registering {@code then} invocation has returned.
    + *
    + * If a Promise is resolved with another Promise, the first Promise will block
    + * until the second is resolved, and then assumes the same result as the second
    + * Promise. This allows Promises to depend on the results of other Promises,
    + * linking together multiple asynchronous operations.
    + *
    + * This implementation is compatible with the Promises/A+ specification and
    + * passes that specification's conformance test suite. A Closure Promise may be
    + * resolved with a Promise instance (or sufficiently compatible Promise-like
    + * object) created by other Promise implementations. From the specification,
    + * Promise-like objects are known as "Thenables".
    + *
    + * @see http://promisesaplus.com/
    + *
    + * @param {function(
    + *             this:RESOLVER_CONTEXT,
    + *             function((TYPE|IThenable<TYPE>|Thenable)=),
    + *             function(*)): void} resolver
    + *     Initialization function that is invoked immediately with {@code resolve}
    + *     and {@code reject} functions as arguments. The Promise is resolved or
    + *     rejected with the first argument passed to either function.
    + * @param {RESOLVER_CONTEXT=} opt_context An optional context for executing the
    + *     resolver function. If unspecified, the resolver function will be executed
    + *     in the default scope.
    + * @constructor
    + * @struct
    + * @final
    + * @implements {goog.Thenable<TYPE>}
    + * @template TYPE,RESOLVER_CONTEXT
    + */
    +goog.Promise = function(resolver, opt_context) {
    +  /**
    +   * The internal state of this Promise. Either PENDING, FULFILLED, REJECTED, or
    +   * BLOCKED.
    +   * @private {goog.Promise.State_}
    +   */
    +  this.state_ = goog.Promise.State_.PENDING;
    +
    +  /**
    +   * The resolved result of the Promise. Immutable once set with either a
    +   * fulfillment value or rejection reason.
    +   * @private {*}
    +   */
    +  this.result_ = undefined;
    +
    +  /**
    +   * For Promises created by calling {@code then()}, the originating parent.
    +   * @private {goog.Promise}
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * The list of {@code onFulfilled} and {@code onRejected} callbacks added to
    +   * this Promise by calls to {@code then()}.
    +   * @private {Array<goog.Promise.CallbackEntry_>}
    +   */
    +  this.callbackEntries_ = null;
    +
    +  /**
    +   * Whether the Promise is in the queue of Promises to execute.
    +   * @private {boolean}
    +   */
    +  this.executing_ = false;
    +
    +  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
    +    /**
    +     * A timeout ID used when the {@code UNHANDLED_REJECTION_DELAY} is greater
    +     * than 0 milliseconds. The ID is set when the Promise is rejected, and
    +     * cleared only if an {@code onRejected} callback is invoked for the
    +     * Promise (or one of its descendants) before the delay is exceeded.
    +     *
    +     * If the rejection is not handled before the timeout completes, the
    +     * rejection reason is passed to the unhandled rejection handler.
    +     * @private {number}
    +     */
    +    this.unhandledRejectionId_ = 0;
    +  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
    +    /**
    +     * When the {@code UNHANDLED_REJECTION_DELAY} is set to 0 milliseconds, a
    +     * boolean that is set if the Promise is rejected, and reset to false if an
    +     * {@code onRejected} callback is invoked for the Promise (or one of its
    +     * descendants). If the rejection is not handled before the next timestep,
    +     * the rejection reason is passed to the unhandled rejection handler.
    +     * @private {boolean}
    +     */
    +    this.hadUnhandledRejection_ = false;
    +  }
    +
    +  if (goog.Promise.LONG_STACK_TRACES) {
    +    /**
    +     * A list of stack trace frames pointing to the locations where this Promise
    +     * was created or had callbacks added to it. Saved to add additional context
    +     * to stack traces when an exception is thrown.
    +     * @private {!Array<string>}
    +     */
    +    this.stack_ = [];
    +    this.addStackTrace_(new Error('created'));
    +
    +    /**
    +     * Index of the most recently executed stack frame entry.
    +     * @private {number}
    +     */
    +    this.currentStep_ = 0;
    +  }
    +
    +  try {
    +    var self = this;
    +    resolver.call(
    +        opt_context,
    +        function(value) {
    +          self.resolve_(goog.Promise.State_.FULFILLED, value);
    +        },
    +        function(reason) {
    +          if (goog.DEBUG &&
    +              !(reason instanceof goog.Promise.CancellationError)) {
    +            try {
    +              // Promise was rejected. Step up one call frame to see why.
    +              if (reason instanceof Error) {
    +                throw reason;
    +              } else {
    +                throw new Error('Promise rejected.');
    +              }
    +            } catch (e) {
    +              // Only thrown so browser dev tools can catch rejections of
    +              // promises when the option to break on caught exceptions is
    +              // activated.
    +            }
    +          }
    +          self.resolve_(goog.Promise.State_.REJECTED, reason);
    +        });
    +  } catch (e) {
    +    this.resolve_(goog.Promise.State_.REJECTED, e);
    +  }
    +};
    +
    +
    +/**
    + * @define {boolean} Whether traces of {@code then} calls should be included in
    + * exceptions thrown
    + */
    +goog.define('goog.Promise.LONG_STACK_TRACES', false);
    +
    +
    +/**
    + * @define {number} The delay in milliseconds before a rejected Promise's reason
    + * is passed to the rejection handler. By default, the rejection handler
    + * rethrows the rejection reason so that it appears in the developer console or
    + * {@code window.onerror} handler.
    + *
    + * Rejections are rethrown as quickly as possible by default. A negative value
    + * disables rejection handling entirely.
    + */
    +goog.define('goog.Promise.UNHANDLED_REJECTION_DELAY', 0);
    +
    +
    +/**
    + * The possible internal states for a Promise. These states are not directly
    + * observable to external callers.
    + * @enum {number}
    + * @private
    + */
    +goog.Promise.State_ = {
    +  /** The Promise is waiting for resolution. */
    +  PENDING: 0,
    +
    +  /** The Promise is blocked waiting for the result of another Thenable. */
    +  BLOCKED: 1,
    +
    +  /** The Promise has been resolved with a fulfillment value. */
    +  FULFILLED: 2,
    +
    +  /** The Promise has been resolved with a rejection reason. */
    +  REJECTED: 3
    +};
    +
    +
    +/**
    + * Typedef for entries in the callback chain. Each call to {@code then},
    + * {@code thenCatch}, or {@code thenAlways} creates an entry containing the
    + * functions that may be invoked once the Promise is resolved.
    + *
    + * @typedef {{
    + *   child: goog.Promise,
    + *   onFulfilled: function(*),
    + *   onRejected: function(*)
    + * }}
    + * @private
    + */
    +goog.Promise.CallbackEntry_;
    +
    +
    +/**
    + * @param {(TYPE|goog.Thenable<TYPE>|Thenable)=} opt_value
    + * @return {!goog.Promise<TYPE>} A new Promise that is immediately resolved
    + *     with the given value.
    + * @template TYPE
    + */
    +goog.Promise.resolve = function(opt_value) {
    +  return new goog.Promise(function(resolve, reject) {
    +    resolve(opt_value);
    +  });
    +};
    +
    +
    +/**
    + * @param {*=} opt_reason
    + * @return {!goog.Promise} A new Promise that is immediately rejected with the
    + *     given reason.
    + */
    +goog.Promise.reject = function(opt_reason) {
    +  return new goog.Promise(function(resolve, reject) {
    +    reject(opt_reason);
    +  });
    +};
    +
    +
    +/**
    + * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    + * @return {!goog.Promise<TYPE>} A Promise that receives the result of the
    + *     first Promise (or Promise-like) input to complete.
    + * @template TYPE
    + */
    +goog.Promise.race = function(promises) {
    +  return new goog.Promise(function(resolve, reject) {
    +    if (!promises.length) {
    +      resolve(undefined);
    +    }
    +    for (var i = 0, promise; promise = promises[i]; i++) {
    +      promise.then(resolve, reject);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    + * @return {!goog.Promise<!Array<TYPE>>} A Promise that receives a list of
    + *     every fulfilled value once every input Promise (or Promise-like) is
    + *     successfully fulfilled, or is rejected by the first rejection result.
    + * @template TYPE
    + */
    +goog.Promise.all = function(promises) {
    +  return new goog.Promise(function(resolve, reject) {
    +    var toFulfill = promises.length;
    +    var values = [];
    +
    +    if (!toFulfill) {
    +      resolve(values);
    +      return;
    +    }
    +
    +    var onFulfill = function(index, value) {
    +      toFulfill--;
    +      values[index] = value;
    +      if (toFulfill == 0) {
    +        resolve(values);
    +      }
    +    };
    +
    +    var onReject = function(reason) {
    +      reject(reason);
    +    };
    +
    +    for (var i = 0, promise; promise = promises[i]; i++) {
    +      promise.then(goog.partial(onFulfill, i), onReject);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @param {!Array<!(goog.Thenable<TYPE>|Thenable)>} promises
    + * @return {!goog.Promise<TYPE>} A Promise that receives the value of the first
    + *     input to be fulfilled, or is rejected with a list of every rejection
    + *     reason if all inputs are rejected.
    + * @template TYPE
    + */
    +goog.Promise.firstFulfilled = function(promises) {
    +  return new goog.Promise(function(resolve, reject) {
    +    var toReject = promises.length;
    +    var reasons = [];
    +
    +    if (!toReject) {
    +      resolve(undefined);
    +      return;
    +    }
    +
    +    var onFulfill = function(value) {
    +      resolve(value);
    +    };
    +
    +    var onReject = function(index, reason) {
    +      toReject--;
    +      reasons[index] = reason;
    +      if (toReject == 0) {
    +        reject(reasons);
    +      }
    +    };
    +
    +    for (var i = 0, promise; promise = promises[i]; i++) {
    +      promise.then(onFulfill, goog.partial(onReject, i));
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @return {!goog.promise.Resolver<TYPE>} Resolver wrapping the promise and its
    + *     resolve / reject functions. Resolving or rejecting the resolver
    + *     resolves or rejects the promise.
    + * @template TYPE
    + */
    +goog.Promise.withResolver = function() {
    +  var resolve, reject;
    +  var promise = new goog.Promise(function(rs, rj) {
    +    resolve = rs;
    +    reject = rj;
    +  });
    +  return new goog.Promise.Resolver_(promise, resolve, reject);
    +};
    +
    +
    +/**
    + * Adds callbacks that will operate on the result of the Promise, returning a
    + * new child Promise.
    + *
    + * If the Promise is fulfilled, the {@code onFulfilled} callback will be invoked
    + * with the fulfillment value as argument, and the child Promise will be
    + * fulfilled with the return value of the callback. If the callback throws an
    + * exception, the child Promise will be rejected with the thrown value instead.
    + *
    + * If the Promise is rejected, the {@code onRejected} callback will be invoked
    + * with the rejection reason as argument, and the child Promise will be resolved
    + * with the return value or rejected with the thrown value of the callback.
    + *
    + * @override
    + */
    +goog.Promise.prototype.then = function(
    +    opt_onFulfilled, opt_onRejected, opt_context) {
    +
    +  if (opt_onFulfilled != null) {
    +    goog.asserts.assertFunction(opt_onFulfilled,
    +        'opt_onFulfilled should be a function.');
    +  }
    +  if (opt_onRejected != null) {
    +    goog.asserts.assertFunction(opt_onRejected,
    +        'opt_onRejected should be a function. Did you pass opt_context ' +
    +        'as the second argument instead of the third?');
    +  }
    +
    +  if (goog.Promise.LONG_STACK_TRACES) {
    +    this.addStackTrace_(new Error('then'));
    +  }
    +
    +  return this.addChildPromise_(
    +      goog.isFunction(opt_onFulfilled) ? opt_onFulfilled : null,
    +      goog.isFunction(opt_onRejected) ? opt_onRejected : null,
    +      opt_context);
    +};
    +goog.Thenable.addImplementation(goog.Promise);
    +
    +
    +/**
    + * Adds a callback that will be invoked whether the Promise is fulfilled or
    + * rejected. The callback receives no argument, and no new child Promise is
    + * created. This is useful for ensuring that cleanup takes place after certain
    + * asynchronous operations. Callbacks added with {@code thenAlways} will be
    + * executed in the same order with other calls to {@code then},
    + * {@code thenAlways}, or {@code thenCatch}.
    + *
    + * Since it does not produce a new child Promise, cancellation propagation is
    + * not prevented by adding callbacks with {@code thenAlways}. A Promise that has
    + * a cleanup handler added with {@code thenAlways} will be canceled if all of
    + * its children created by {@code then} (or {@code thenCatch}) are canceled.
    + * Additionally, since any rejections are not passed to the callback, it does
    + * not stop the unhandled rejection handler from running.
    + *
    + * @param {function(this:THIS): void} onResolved A function that will be invoked
    + *     when the Promise is resolved.
    + * @param {THIS=} opt_context An optional context object that will be the
    + *     execution context for the callbacks. By default, functions are executed
    + *     in the global scope.
    + * @return {!goog.Promise<TYPE>} This Promise, for chaining additional calls.
    + * @template THIS
    + */
    +goog.Promise.prototype.thenAlways = function(onResolved, opt_context) {
    +  if (goog.Promise.LONG_STACK_TRACES) {
    +    this.addStackTrace_(new Error('thenAlways'));
    +  }
    +
    +  var callback = function() {
    +    try {
    +      // Ensure that no arguments are passed to onResolved.
    +      onResolved.call(opt_context);
    +    } catch (err) {
    +      goog.Promise.handleRejection_.call(null, err);
    +    }
    +  };
    +
    +  this.addCallbackEntry_({
    +    child: null,
    +    onRejected: callback,
    +    onFulfilled: callback
    +  });
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a callback that will be invoked only if the Promise is rejected. This
    + * is equivalent to {@code then(null, onRejected)}.
    + *
    + * @param {!function(this:THIS, *): *} onRejected A function that will be
    + *     invoked with the rejection reason if the Promise is rejected.
    + * @param {THIS=} opt_context An optional context object that will be the
    + *     execution context for the callbacks. By default, functions are executed
    + *     in the global scope.
    + * @return {!goog.Promise} A new Promise that will receive the result of the
    + *     callback.
    + * @template THIS
    + */
    +goog.Promise.prototype.thenCatch = function(onRejected, opt_context) {
    +  if (goog.Promise.LONG_STACK_TRACES) {
    +    this.addStackTrace_(new Error('thenCatch'));
    +  }
    +  return this.addChildPromise_(null, onRejected, opt_context);
    +};
    +
    +
    +/**
    + * Cancels the Promise if it is still pending by rejecting it with a cancel
    + * Error. No action is performed if the Promise is already resolved.
    + *
    + * All child Promises of the canceled Promise will be rejected with the same
    + * cancel error, as with normal Promise rejection. If the Promise to be canceled
    + * is the only child of a pending Promise, the parent Promise will also be
    + * canceled. Cancellation may propagate upward through multiple generations.
    + *
    + * @param {string=} opt_message An optional debugging message for describing the
    + *     cancellation reason.
    + */
    +goog.Promise.prototype.cancel = function(opt_message) {
    +  if (this.state_ == goog.Promise.State_.PENDING) {
    +    goog.async.run(function() {
    +      var err = new goog.Promise.CancellationError(opt_message);
    +      this.cancelInternal_(err);
    +    }, this);
    +  }
    +};
    +
    +
    +/**
    + * Cancels this Promise with the given error.
    + *
    + * @param {!Error} err The cancellation error.
    + * @private
    + */
    +goog.Promise.prototype.cancelInternal_ = function(err) {
    +  if (this.state_ == goog.Promise.State_.PENDING) {
    +    if (this.parent_) {
    +      // Cancel the Promise and remove it from the parent's child list.
    +      this.parent_.cancelChild_(this, err);
    +    } else {
    +      this.resolve_(goog.Promise.State_.REJECTED, err);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Cancels a child Promise from the list of callback entries. If the Promise has
    + * not already been resolved, reject it with a cancel error. If there are no
    + * other children in the list of callback entries, propagate the cancellation
    + * by canceling this Promise as well.
    + *
    + * @param {!goog.Promise} childPromise The Promise to cancel.
    + * @param {!Error} err The cancel error to use for rejecting the Promise.
    + * @private
    + */
    +goog.Promise.prototype.cancelChild_ = function(childPromise, err) {
    +  if (!this.callbackEntries_) {
    +    return;
    +  }
    +  var childCount = 0;
    +  var childIndex = -1;
    +
    +  // Find the callback entry for the childPromise, and count whether there are
    +  // additional child Promises.
    +  for (var i = 0, entry; entry = this.callbackEntries_[i]; i++) {
    +    var child = entry.child;
    +    if (child) {
    +      childCount++;
    +      if (child == childPromise) {
    +        childIndex = i;
    +      }
    +      if (childIndex >= 0 && childCount > 1) {
    +        break;
    +      }
    +    }
    +  }
    +
    +  // If the child Promise was the only child, cancel this Promise as well.
    +  // Otherwise, reject only the child Promise with the cancel error.
    +  if (childIndex >= 0) {
    +    if (this.state_ == goog.Promise.State_.PENDING && childCount == 1) {
    +      this.cancelInternal_(err);
    +    } else {
    +      var callbackEntry = this.callbackEntries_.splice(childIndex, 1)[0];
    +      this.executeCallback_(
    +          callbackEntry, goog.Promise.State_.REJECTED, err);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adds a callback entry to the current Promise, and schedules callback
    + * execution if the Promise has already been resolved.
    + *
    + * @param {goog.Promise.CallbackEntry_} callbackEntry Record containing
    + *     {@code onFulfilled} and {@code onRejected} callbacks to execute after
    + *     the Promise is resolved.
    + * @private
    + */
    +goog.Promise.prototype.addCallbackEntry_ = function(callbackEntry) {
    +  if ((!this.callbackEntries_ || !this.callbackEntries_.length) &&
    +      (this.state_ == goog.Promise.State_.FULFILLED ||
    +       this.state_ == goog.Promise.State_.REJECTED)) {
    +    this.scheduleCallbacks_();
    +  }
    +  if (!this.callbackEntries_) {
    +    this.callbackEntries_ = [];
    +  }
    +  this.callbackEntries_.push(callbackEntry);
    +};
    +
    +
    +/**
    + * Creates a child Promise and adds it to the callback entry list. The result of
    + * the child Promise is determined by the state of the parent Promise and the
    + * result of the {@code onFulfilled} or {@code onRejected} callbacks as
    + * specified in the Promise resolution procedure.
    + *
    + * @see http://promisesaplus.com/#the__method
    + *
    + * @param {?function(this:THIS, TYPE):
    + *          (RESULT|goog.Promise<RESULT>|Thenable)} onFulfilled A callback that
    + *     will be invoked if the Promise is fullfilled, or null.
    + * @param {?function(this:THIS, *): *} onRejected A callback that will be
    + *     invoked if the Promise is rejected, or null.
    + * @param {THIS=} opt_context An optional execution context for the callbacks.
    + *     in the default calling context.
    + * @return {!goog.Promise} The child Promise.
    + * @template RESULT,THIS
    + * @private
    + */
    +goog.Promise.prototype.addChildPromise_ = function(
    +    onFulfilled, onRejected, opt_context) {
    +
    +  var callbackEntry = {
    +    child: null,
    +    onFulfilled: null,
    +    onRejected: null
    +  };
    +
    +  callbackEntry.child = new goog.Promise(function(resolve, reject) {
    +    // Invoke onFulfilled, or resolve with the parent's value if absent.
    +    callbackEntry.onFulfilled = onFulfilled ? function(value) {
    +      try {
    +        var result = onFulfilled.call(opt_context, value);
    +        resolve(result);
    +      } catch (err) {
    +        reject(err);
    +      }
    +    } : resolve;
    +
    +    // Invoke onRejected, or reject with the parent's reason if absent.
    +    callbackEntry.onRejected = onRejected ? function(reason) {
    +      try {
    +        var result = onRejected.call(opt_context, reason);
    +        if (!goog.isDef(result) &&
    +            reason instanceof goog.Promise.CancellationError) {
    +          // Propagate cancellation to children if no other result is returned.
    +          reject(reason);
    +        } else {
    +          resolve(result);
    +        }
    +      } catch (err) {
    +        reject(err);
    +      }
    +    } : reject;
    +  });
    +
    +  callbackEntry.child.parent_ = this;
    +  this.addCallbackEntry_(
    +      /** @type {goog.Promise.CallbackEntry_} */ (callbackEntry));
    +  return callbackEntry.child;
    +};
    +
    +
    +/**
    + * Unblocks the Promise and fulfills it with the given value.
    + *
    + * @param {TYPE} value
    + * @private
    + */
    +goog.Promise.prototype.unblockAndFulfill_ = function(value) {
    +  goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
    +  this.state_ = goog.Promise.State_.PENDING;
    +  this.resolve_(goog.Promise.State_.FULFILLED, value);
    +};
    +
    +
    +/**
    + * Unblocks the Promise and rejects it with the given rejection reason.
    + *
    + * @param {*} reason
    + * @private
    + */
    +goog.Promise.prototype.unblockAndReject_ = function(reason) {
    +  goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
    +  this.state_ = goog.Promise.State_.PENDING;
    +  this.resolve_(goog.Promise.State_.REJECTED, reason);
    +};
    +
    +
    +/**
    + * Attempts to resolve a Promise with a given resolution state and value. This
    + * is a no-op if the given Promise has already been resolved.
    + *
    + * If the given result is a Thenable (such as another Promise), the Promise will
    + * be resolved with the same state and result as the Thenable once it is itself
    + * resolved.
    + *
    + * If the given result is not a Thenable, the Promise will be fulfilled or
    + * rejected with that result based on the given state.
    + *
    + * @see http://promisesaplus.com/#the_promise_resolution_procedure
    + *
    + * @param {goog.Promise.State_} state
    + * @param {*} x The result to apply to the Promise.
    + * @private
    + */
    +goog.Promise.prototype.resolve_ = function(state, x) {
    +  if (this.state_ != goog.Promise.State_.PENDING) {
    +    return;
    +  }
    +
    +  if (this == x) {
    +    state = goog.Promise.State_.REJECTED;
    +    x = new TypeError('Promise cannot resolve to itself');
    +
    +  } else if (goog.Thenable.isImplementedBy(x)) {
    +    x = /** @type {!goog.Thenable} */ (x);
    +    this.state_ = goog.Promise.State_.BLOCKED;
    +    x.then(this.unblockAndFulfill_, this.unblockAndReject_, this);
    +    return;
    +
    +  } else if (goog.isObject(x)) {
    +    try {
    +      var then = x['then'];
    +      if (goog.isFunction(then)) {
    +        this.tryThen_(x, then);
    +        return;
    +      }
    +    } catch (e) {
    +      state = goog.Promise.State_.REJECTED;
    +      x = e;
    +    }
    +  }
    +
    +  this.result_ = x;
    +  this.state_ = state;
    +  this.scheduleCallbacks_();
    +
    +  if (state == goog.Promise.State_.REJECTED &&
    +      !(x instanceof goog.Promise.CancellationError)) {
    +    goog.Promise.addUnhandledRejection_(this, x);
    +  }
    +};
    +
    +
    +/**
    + * Attempts to call the {@code then} method on an object in the hopes that it is
    + * a Promise-compatible instance. This allows interoperation between different
    + * Promise implementations, however a non-compliant object may cause a Promise
    + * to hang indefinitely. If the {@code then} method throws an exception, the
    + * dependent Promise will be rejected with the thrown value.
    + *
    + * @see http://promisesaplus.com/#point-70
    + *
    + * @param {Thenable} thenable An object with a {@code then} method that may be
    + *     compatible with the Promise/A+ specification.
    + * @param {!Function} then The {@code then} method of the Thenable object.
    + * @private
    + */
    +goog.Promise.prototype.tryThen_ = function(thenable, then) {
    +  this.state_ = goog.Promise.State_.BLOCKED;
    +  var promise = this;
    +  var called = false;
    +
    +  var resolve = function(value) {
    +    if (!called) {
    +      called = true;
    +      promise.unblockAndFulfill_(value);
    +    }
    +  };
    +
    +  var reject = function(reason) {
    +    if (!called) {
    +      called = true;
    +      promise.unblockAndReject_(reason);
    +    }
    +  };
    +
    +  try {
    +    then.call(thenable, resolve, reject);
    +  } catch (e) {
    +    reject(e);
    +  }
    +};
    +
    +
    +/**
    + * Executes the pending callbacks of a resolved Promise after a timeout.
    + *
    + * Section 2.2.4 of the Promises/A+ specification requires that Promise
    + * callbacks must only be invoked from a call stack that only contains Promise
    + * implementation code, which we accomplish by invoking callback execution after
    + * a timeout. If {@code startExecution_} is called multiple times for the same
    + * Promise, the callback chain will be evaluated only once. Additional callbacks
    + * may be added during the evaluation phase, and will be executed in the same
    + * event loop.
    + *
    + * All Promises added to the waiting list during the same browser event loop
    + * will be executed in one batch to avoid using a separate timeout per Promise.
    + *
    + * @private
    + */
    +goog.Promise.prototype.scheduleCallbacks_ = function() {
    +  if (!this.executing_) {
    +    this.executing_ = true;
    +    goog.async.run(this.executeCallbacks_, this);
    +  }
    +};
    +
    +
    +/**
    + * Executes all pending callbacks for this Promise.
    + *
    + * @private
    + */
    +goog.Promise.prototype.executeCallbacks_ = function() {
    +  while (this.callbackEntries_ && this.callbackEntries_.length) {
    +    var entries = this.callbackEntries_;
    +    this.callbackEntries_ = [];
    +
    +    for (var i = 0; i < entries.length; i++) {
    +      if (goog.Promise.LONG_STACK_TRACES) {
    +        this.currentStep_++;
    +      }
    +      this.executeCallback_(entries[i], this.state_, this.result_);
    +    }
    +  }
    +  this.executing_ = false;
    +};
    +
    +
    +/**
    + * Executes a pending callback for this Promise. Invokes an {@code onFulfilled}
    + * or {@code onRejected} callback based on the resolved state of the Promise.
    + *
    + * @param {!goog.Promise.CallbackEntry_} callbackEntry An entry containing the
    + *     onFulfilled and/or onRejected callbacks for this step.
    + * @param {goog.Promise.State_} state The resolution status of the Promise,
    + *     either FULFILLED or REJECTED.
    + * @param {*} result The resolved result of the Promise.
    + * @private
    + */
    +goog.Promise.prototype.executeCallback_ = function(
    +    callbackEntry, state, result) {
    +  if (state == goog.Promise.State_.FULFILLED) {
    +    callbackEntry.onFulfilled(result);
    +  } else {
    +    if (callbackEntry.child) {
    +      this.removeUnhandledRejection_();
    +    }
    +    callbackEntry.onRejected(result);
    +  }
    +};
    +
    +
    +/**
    + * Records a stack trace entry for functions that call {@code then} or the
    + * Promise constructor. May be disabled by unsetting {@code LONG_STACK_TRACES}.
    + *
    + * @param {!Error} err An Error object created by the calling function for
    + *     providing a stack trace.
    + * @private
    + */
    +goog.Promise.prototype.addStackTrace_ = function(err) {
    +  if (goog.Promise.LONG_STACK_TRACES && goog.isString(err.stack)) {
    +    // Extract the third line of the stack trace, which is the entry for the
    +    // user function that called into Promise code.
    +    var trace = err.stack.split('\n', 4)[3];
    +    var message = err.message;
    +
    +    // Pad the message to align the traces.
    +    message += Array(11 - message.length).join(' ');
    +    this.stack_.push(message + trace);
    +  }
    +};
    +
    +
    +/**
    + * Adds extra stack trace information to an exception for the list of
    + * asynchronous {@code then} calls that have been run for this Promise. Stack
    + * trace information is recorded in {@see #addStackTrace_}, and appended to
    + * rethrown errors when {@code LONG_STACK_TRACES} is enabled.
    + *
    + * @param {*} err An unhandled exception captured during callback execution.
    + * @private
    + */
    +goog.Promise.prototype.appendLongStack_ = function(err) {
    +  if (goog.Promise.LONG_STACK_TRACES &&
    +      err && goog.isString(err.stack) && this.stack_.length) {
    +    var longTrace = ['Promise trace:'];
    +
    +    for (var promise = this; promise; promise = promise.parent_) {
    +      for (var i = this.currentStep_; i >= 0; i--) {
    +        longTrace.push(promise.stack_[i]);
    +      }
    +      longTrace.push('Value: ' +
    +          '[' + (promise.state_ == goog.Promise.State_.REJECTED ?
    +              'REJECTED' : 'FULFILLED') + '] ' +
    +          '<' + String(promise.result_) + '>');
    +    }
    +    err.stack += '\n\n' + longTrace.join('\n');
    +  }
    +};
    +
    +
    +/**
    + * Marks this rejected Promise as having being handled. Also marks any parent
    + * Promises in the rejected state as handled. The rejection handler will no
    + * longer be invoked for this Promise (if it has not been called already).
    + *
    + * @private
    + */
    +goog.Promise.prototype.removeUnhandledRejection_ = function() {
    +  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
    +    for (var p = this; p && p.unhandledRejectionId_; p = p.parent_) {
    +      goog.global.clearTimeout(p.unhandledRejectionId_);
    +      p.unhandledRejectionId_ = 0;
    +    }
    +  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
    +    for (var p = this; p && p.hadUnhandledRejection_; p = p.parent_) {
    +      p.hadUnhandledRejection_ = false;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Marks this rejected Promise as unhandled. If no {@code onRejected} callback
    + * is called for this Promise before the {@code UNHANDLED_REJECTION_DELAY}
    + * expires, the reason will be passed to the unhandled rejection handler. The
    + * handler typically rethrows the rejection reason so that it becomes visible in
    + * the developer console.
    + *
    + * @param {!goog.Promise} promise The rejected Promise.
    + * @param {*} reason The Promise rejection reason.
    + * @private
    + */
    +goog.Promise.addUnhandledRejection_ = function(promise, reason) {
    +  if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
    +    promise.unhandledRejectionId_ = goog.global.setTimeout(function() {
    +      promise.appendLongStack_(reason);
    +      goog.Promise.handleRejection_.call(null, reason);
    +    }, goog.Promise.UNHANDLED_REJECTION_DELAY);
    +
    +  } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
    +    promise.hadUnhandledRejection_ = true;
    +    goog.async.run(function() {
    +      if (promise.hadUnhandledRejection_) {
    +        promise.appendLongStack_(reason);
    +        goog.Promise.handleRejection_.call(null, reason);
    +      }
    +    });
    +  }
    +};
    +
    +
    +/**
    + * A method that is invoked with the rejection reasons for Promises that are
    + * rejected but have no {@code onRejected} callbacks registered yet.
    + * @type {function(*)}
    + * @private
    + */
    +goog.Promise.handleRejection_ = goog.async.throwException;
    +
    +
    +/**
    + * Sets a handler that will be called with reasons from unhandled rejected
    + * Promises. If the rejected Promise (or one of its descendants) has an
    + * {@code onRejected} callback registered, the rejection will be considered
    + * handled, and the rejection handler will not be called.
    + *
    + * By default, unhandled rejections are rethrown so that the error may be
    + * captured by the developer console or a {@code window.onerror} handler.
    + *
    + * @param {function(*)} handler A function that will be called with reasons from
    + *     rejected Promises. Defaults to {@code goog.async.throwException}.
    + */
    +goog.Promise.setUnhandledRejectionHandler = function(handler) {
    +  goog.Promise.handleRejection_ = handler;
    +};
    +
    +
    +
    +/**
    + * Error used as a rejection reason for canceled Promises.
    + *
    + * @param {string=} opt_message
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + */
    +goog.Promise.CancellationError = function(opt_message) {
    +  goog.Promise.CancellationError.base(this, 'constructor', opt_message);
    +};
    +goog.inherits(goog.Promise.CancellationError, goog.debug.Error);
    +
    +
    +/** @override */
    +goog.Promise.CancellationError.prototype.name = 'cancel';
    +
    +
    +
    +/**
    + * Internal implementation of the resolver interface.
    + *
    + * @param {!goog.Promise<TYPE>} promise
    + * @param {function((TYPE|goog.Promise<TYPE>|Thenable)=)} resolve
    + * @param {function(*): void} reject
    + * @implements {goog.promise.Resolver<TYPE>}
    + * @final @struct
    + * @constructor
    + * @private
    + * @template TYPE
    + */
    +goog.Promise.Resolver_ = function(promise, resolve, reject) {
    +  /** @const */
    +  this.promise = promise;
    +
    +  /** @const */
    +  this.resolve = resolve;
    +
    +  /** @const */
    +  this.reject = reject;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/promise_test.html b/mapbox-gl/closure-library/closure/goog/promise/promise_test.html
    new file mode 100644
    index 0000000000..edf95df019
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/promise_test.html
    @@ -0,0 +1,18 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.Promise</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.PromiseTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/promise_test.js b/mapbox-gl/closure-library/closure/goog/promise/promise_test.js
    new file mode 100644
    index 0000000000..e51976351c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/promise_test.js
    @@ -0,0 +1,1488 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.PromiseTest');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.functions');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +goog.setTestOnly('goog.PromiseTest');
    +
    +
    +// TODO(brenneman):
    +// - Add tests for interoperability with native Promises where available.
    +// - Make most tests use the MockClock (though some tests should still verify
    +//   real asynchronous behavior.
    +// - Add tests for long stack traces.
    +
    +
    +var mockClock;
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
    +var stubs = new goog.testing.PropertyReplacer();
    +var unhandledRejections;
    +
    +
    +// Simple shared objects used as test values.
    +var dummy = {toString: goog.functions.constant('[object dummy]')};
    +var sentinel = {toString: goog.functions.constant('[object sentinel]')};
    +
    +
    +function setUpPage() {
    +  asyncTestCase.stepTimeout = 200;
    +  mockClock = new goog.testing.MockClock();
    +}
    +
    +
    +function setUp() {
    +  unhandledRejections = goog.testing.recordFunction();
    +  goog.Promise.setUnhandledRejectionHandler(unhandledRejections);
    +}
    +
    +
    +function tearDown() {
    +  if (mockClock) {
    +    // The system should leave no pending unhandled rejections. Advance the mock
    +    // clock to the end of time to catch any rethrows waiting in the queue.
    +    mockClock.tick(Infinity);
    +    mockClock.uninstall();
    +    mockClock.reset();
    +  }
    +  stubs.reset();
    +}
    +
    +
    +function tearDownPage() {
    +  goog.dispose(mockClock);
    +}
    +
    +
    +function continueTesting() {
    +  asyncTestCase.continueTesting();
    +}
    +
    +
    +/**
    + * Dummy onfulfilled or onrejected function that should not be called.
    + *
    + * @param {*} result The result passed into the callback.
    + */
    +function shouldNotCall(result) {
    +  fail('This should not have been called (result: ' + String(result) + ')');
    +}
    +
    +
    +function fulfillSoon(value, delay) {
    +  return new goog.Promise(function(resolve, reject) {
    +    window.setTimeout(function() {
    +      resolve(value);
    +    }, delay);
    +  });
    +}
    +
    +
    +function rejectSoon(reason, delay) {
    +  return new goog.Promise(function(resolve, reject) {
    +    window.setTimeout(function() {
    +      reject(reason);
    +    }, delay);
    +  });
    +}
    +
    +
    +function testThenIsFulfilled() {
    +  asyncTestCase.waitForAsync();
    +  var timesCalled = 0;
    +
    +  var p = new goog.Promise(function(resolve, reject) {
    +    resolve(sentinel);
    +  });
    +  p.then(function(value) {
    +    timesCalled++;
    +    assertEquals(sentinel, value);
    +    assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
    +  });
    +  p.thenAlways(continueTesting);
    +
    +  assertEquals('then() must return before callbacks are invoked.',
    +               0, timesCalled);
    +}
    +
    +
    +function testThenIsRejected() {
    +  asyncTestCase.waitForAsync();
    +  var timesCalled = 0;
    +
    +  var p = new goog.Promise(function(resolve, reject) {
    +    reject(sentinel);
    +  });
    +  p.then(shouldNotCall, function(value) {
    +    timesCalled++;
    +    assertEquals(sentinel, value);
    +    assertEquals('onRejected must be called exactly once.', 1, timesCalled);
    +  });
    +  p.thenAlways(continueTesting);
    +
    +  assertEquals('then() must return before callbacks are invoked.',
    +               0, timesCalled);
    +}
    +
    +function testThenAsserts() {
    +  var p = goog.Promise.resolve();
    +
    +  var m = assertThrows(function() {
    +    p.then({});
    +  });
    +  assertContains('opt_onFulfilled should be a function.', m.message);
    +
    +  m = assertThrows(function() {
    +    p.then(function() {}, {});
    +  });
    +  assertContains('opt_onRejected should be a function.', m.message);
    +}
    +
    +
    +function testOptionalOnFulfilled() {
    +  asyncTestCase.waitForAsync();
    +
    +  goog.Promise.resolve(sentinel).
    +      then(null, null).
    +      then(null, shouldNotCall).
    +      then(function(value) {
    +        assertEquals(sentinel, value);
    +      }).
    +      thenAlways(continueTesting);
    +}
    +
    +
    +function testOptionalOnRejected() {
    +  asyncTestCase.waitForAsync();
    +
    +  goog.Promise.reject(sentinel).
    +      then(null, null).
    +      then(shouldNotCall).
    +      then(null, function(reason) {
    +        assertEquals(sentinel, reason);
    +      }).
    +      thenAlways(continueTesting);
    +}
    +
    +
    +function testMultipleResolves() {
    +  asyncTestCase.waitForAsync();
    +  var timesCalled = 0;
    +  var resolvePromise;
    +
    +  var p = new goog.Promise(function(resolve, reject) {
    +    resolvePromise = resolve;
    +    resolve('foo');
    +    resolve('bar');
    +  });
    +
    +  p.then(function(value) {
    +    timesCalled++;
    +    assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
    +  });
    +
    +  // Add one more test for fulfilling after a delay.
    +  window.setTimeout(function() {
    +    resolvePromise('baz');
    +    assertEquals(1, timesCalled);
    +    continueTesting();
    +  }, 10);
    +}
    +
    +
    +function testMultipleRejects() {
    +  asyncTestCase.waitForAsync();
    +  var timesCalled = 0;
    +  var rejectPromise;
    +
    +  var p = new goog.Promise(function(resolve, reject) {
    +    rejectPromise = reject;
    +    reject('foo');
    +    reject('bar');
    +  });
    +
    +  p.then(shouldNotCall, function(value) {
    +    timesCalled++;
    +    assertEquals('onRejected must be called exactly once.', 1, timesCalled);
    +  });
    +
    +  // Add one more test for rejecting after a delay.
    +  window.setTimeout(function() {
    +    rejectPromise('baz');
    +    assertEquals(1, timesCalled);
    +    continueTesting();
    +  }, 10);
    +}
    +
    +
    +function testAsynchronousThenCalls() {
    +  asyncTestCase.waitForAsync();
    +  var timesCalled = [0, 0, 0, 0];
    +  var p = new goog.Promise(function(resolve, reject) {
    +    window.setTimeout(function() {
    +      resolve();
    +    }, 30);
    +  });
    +
    +  p.then(function() {
    +    timesCalled[0]++;
    +    assertArrayEquals([1, 0, 0, 0], timesCalled);
    +  });
    +
    +  window.setTimeout(function() {
    +    p.then(function() {
    +      timesCalled[1]++;
    +      assertArrayEquals([1, 1, 0, 0], timesCalled);
    +    });
    +  }, 10);
    +
    +  window.setTimeout(function() {
    +    p.then(function() {
    +      timesCalled[2]++;
    +      assertArrayEquals([1, 1, 1, 0], timesCalled);
    +    });
    +  }, 20);
    +
    +  window.setTimeout(function() {
    +    p.then(function() {
    +      timesCalled[3]++;
    +      assertArrayEquals([1, 1, 1, 1], timesCalled);
    +    });
    +    p.thenAlways(continueTesting);
    +  }, 40);
    +}
    +
    +
    +function testResolveWithPromise() {
    +  asyncTestCase.waitForAsync();
    +  var resolveBlocker;
    +  var hasFulfilled = false;
    +  var blocker = new goog.Promise(function(resolve, reject) {
    +    resolveBlocker = resolve;
    +  });
    +
    +  var p = goog.Promise.resolve(blocker);
    +  p.then(function(value) {
    +    hasFulfilled = true;
    +    assertEquals(sentinel, value);
    +  }, shouldNotCall);
    +  p.thenAlways(function() {
    +    assertTrue(hasFulfilled);
    +    continueTesting();
    +  });
    +
    +  assertFalse(hasFulfilled);
    +  resolveBlocker(sentinel);
    +}
    +
    +
    +function testResolveWithRejectedPromise() {
    +  asyncTestCase.waitForAsync();
    +  var rejectBlocker;
    +  var hasRejected = false;
    +  var blocker = new goog.Promise(function(resolve, reject) {
    +    rejectBlocker = reject;
    +  });
    +
    +  var p = goog.Promise.resolve(blocker);
    +  p.then(shouldNotCall, function(reason) {
    +    hasRejected = true;
    +    assertEquals(sentinel, reason);
    +  });
    +  p.thenAlways(function() {
    +    assertTrue(hasRejected);
    +    continueTesting();
    +  });
    +
    +  assertFalse(hasRejected);
    +  rejectBlocker(sentinel);
    +}
    +
    +
    +function testRejectWithPromise() {
    +  asyncTestCase.waitForAsync();
    +  var resolveBlocker;
    +  var hasFulfilled = false;
    +  var blocker = new goog.Promise(function(resolve, reject) {
    +    resolveBlocker = resolve;
    +  });
    +
    +  var p = goog.Promise.reject(blocker);
    +  p.then(function(value) {
    +    hasFulfilled = true;
    +    assertEquals(sentinel, value);
    +  }, shouldNotCall);
    +  p.thenAlways(function() {
    +    assertTrue(hasFulfilled);
    +    continueTesting();
    +  });
    +
    +  assertFalse(hasFulfilled);
    +  resolveBlocker(sentinel);
    +}
    +
    +
    +function testRejectWithRejectedPromise() {
    +  asyncTestCase.waitForAsync();
    +  var rejectBlocker;
    +  var hasRejected = false;
    +  var blocker = new goog.Promise(function(resolve, reject) {
    +    rejectBlocker = reject;
    +  });
    +
    +  var p = goog.Promise.reject(blocker);
    +  p.then(shouldNotCall, function(reason) {
    +    hasRejected = true;
    +    assertEquals(sentinel, reason);
    +  });
    +  p.thenAlways(function() {
    +    assertTrue(hasRejected);
    +    continueTesting();
    +  });
    +
    +  assertFalse(hasRejected);
    +  rejectBlocker(sentinel);
    +}
    +
    +
    +function testResolveAndReject() {
    +  asyncTestCase.waitForAsync();
    +  var onFulfilledCalled = false;
    +  var onRejectedCalled = false;
    +  var p = new goog.Promise(function(resolve, reject) {
    +    resolve();
    +    reject();
    +  });
    +
    +  p.then(function() {
    +    onFulfilledCalled = true;
    +  }, function() {
    +    onRejectedCalled = true;
    +  });
    +
    +  p.thenAlways(function() {
    +    assertTrue(onFulfilledCalled);
    +    assertFalse(onRejectedCalled);
    +    continueTesting();
    +  });
    +}
    +
    +
    +function testRejectAndResolve() {
    +  asyncTestCase.waitForAsync();
    +  var onFulfilledCalled = false;
    +  var onRejectedCalled = false;
    +  var p = new goog.Promise(function(resolve, reject) {
    +    reject();
    +    resolve();
    +  });
    +
    +  p.then(function() {
    +    onFulfilledCalled = true;
    +  }, function() {
    +    onRejectedCalled = true;
    +  });
    +
    +  p.thenAlways(function() {
    +    assertTrue(onRejectedCalled);
    +    assertFalse(onFulfilledCalled);
    +    continueTesting();
    +  });
    +}
    +
    +
    +function testThenReturnsBeforeCallbackWithFulfill() {
    +  asyncTestCase.waitForAsync();
    +  var thenHasReturned = false;
    +  var p = goog.Promise.resolve();
    +
    +  p.then(function() {
    +    assertTrue(
    +        'Callback must be called only after then() has returned.',
    +        thenHasReturned);
    +  });
    +  p.thenAlways(continueTesting);
    +  thenHasReturned = true;
    +}
    +
    +
    +function testThenReturnsBeforeCallbackWithReject() {
    +  asyncTestCase.waitForAsync();
    +  var thenHasReturned = false;
    +  var p = goog.Promise.reject();
    +
    +  p.then(null, function() {
    +    assertTrue(thenHasReturned);
    +  });
    +  p.thenAlways(continueTesting);
    +  thenHasReturned = true;
    +}
    +
    +
    +function testResolutionOrder() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +  var p = goog.Promise.resolve();
    +
    +  p.then(function() { callbacks.push(1); }, shouldNotCall);
    +  p.then(function() { callbacks.push(2); }, shouldNotCall);
    +  p.then(function() { callbacks.push(3); }, shouldNotCall);
    +
    +  p.then(function() {
    +    assertArrayEquals([1, 2, 3], callbacks);
    +  });
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testResolutionOrderWithThrow() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +  var p = goog.Promise.resolve();
    +
    +  p.then(function() { callbacks.push(1); }, shouldNotCall);
    +  var child = p.then(function() {
    +    callbacks.push(2);
    +    throw Error();
    +  }, shouldNotCall);
    +
    +  child.then(shouldNotCall, function() {
    +    // The parent callbacks should be evaluated before the child.
    +    callbacks.push(4);
    +  });
    +
    +  p.then(function() { callbacks.push(3); }, shouldNotCall);
    +
    +  child.then(shouldNotCall, function() {
    +    callbacks.push(5);
    +    assertArrayEquals([1, 2, 3, 4, 5], callbacks);
    +  });
    +
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testResolutionOrderWithNestedThen() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +  var p = goog.Promise.resolve();
    +
    +  p.then(function() {
    +    callbacks.push(1);
    +    p.then(function() {
    +      callbacks.push(3);
    +    });
    +  });
    +  p.then(function() { callbacks.push(2); });
    +
    +  window.setTimeout(function() {
    +    assertArrayEquals([1, 2, 3], callbacks);
    +    continueTesting();
    +  }, 100);
    +}
    +
    +
    +function testRejectionOrder() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +  var p = goog.Promise.reject();
    +
    +  p.then(shouldNotCall, function() { callbacks.push(1); });
    +  p.then(shouldNotCall, function() { callbacks.push(2); });
    +  p.then(shouldNotCall, function() { callbacks.push(3); });
    +
    +  p.then(shouldNotCall, function() {
    +    assertArrayEquals([1, 2, 3], callbacks);
    +  });
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testRejectionOrderWithThrow() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +  var p = goog.Promise.reject();
    +
    +  p.then(shouldNotCall, function() { callbacks.push(1); });
    +  p.then(shouldNotCall, function() {
    +    callbacks.push(2);
    +    throw Error();
    +  });
    +  p.then(shouldNotCall, function() { callbacks.push(3); });
    +
    +  p.then(shouldNotCall, function() {
    +    assertArrayEquals([1, 2, 3], callbacks);
    +  });
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testRejectionOrderWithNestedThen() {
    +  asyncTestCase.waitForAsync();
    +  var callbacks = [];
    +
    +  var p = goog.Promise.reject();
    +
    +  p.then(shouldNotCall, function() {
    +    callbacks.push(1);
    +    p.then(shouldNotCall, function() {
    +      callbacks.push(3);
    +    });
    +  });
    +  p.then(shouldNotCall, function() { callbacks.push(2); });
    +
    +  window.setTimeout(function() {
    +    assertArrayEquals([1, 2, 3], callbacks);
    +    continueTesting();
    +  }, 0);
    +}
    +
    +
    +function testBranching() {
    +  asyncTestCase.waitForSignals(3);
    +  var p = goog.Promise.resolve(2);
    +
    +  p.then(function(value) {
    +    assertEquals('then functions should see the same value', 2, value);
    +    return value / 2;
    +  }).then(function(value) {
    +    assertEquals('branch should receive the returned value', 1, value);
    +    asyncTestCase.signal();
    +  });
    +
    +  p.then(function(value) {
    +    assertEquals('then functions should see the same value', 2, value);
    +    throw value + 1;
    +  }).then(shouldNotCall, function(reason) {
    +    assertEquals('branch should receive the thrown value', 3, reason);
    +    asyncTestCase.signal();
    +  });
    +
    +  p.then(function(value) {
    +    assertEquals('then functions should see the same value', 2, value);
    +    return value * 2;
    +  }).then(function(value) {
    +    assertEquals('branch should receive the returned value', 4, value);
    +    asyncTestCase.signal();
    +  });
    +}
    +
    +
    +function testThenReturnsPromise() {
    +  var parent = goog.Promise.resolve();
    +  var child = parent.then();
    +
    +  assertTrue(child instanceof goog.Promise);
    +  assertNotEquals('The returned Promise must be different from the input.',
    +                  parent, child);
    +}
    +
    +
    +function testBlockingPromise() {
    +  asyncTestCase.waitForAsync();
    +  var p = goog.Promise.resolve();
    +  var wasFulfilled = false;
    +  var wasRejected = false;
    +
    +  var p2 = p.then(function() {
    +    return new goog.Promise(function(resolve, reject) {});
    +  });
    +
    +  p2.then(function() {
    +    wasFulfilled = true;
    +  }, function() {
    +    wasRejected = true;
    +  });
    +
    +  window.setTimeout(function() {
    +    assertFalse('p2 should be blocked on the returned Promise', wasFulfilled);
    +    assertFalse('p2 should be blocked on the returned Promise', wasRejected);
    +    continueTesting();
    +  }, 100);
    +}
    +
    +
    +function testBlockingPromiseFulfilled() {
    +  asyncTestCase.waitForAsync();
    +  var blockingPromise = new goog.Promise(function(resolve, reject) {
    +    window.setTimeout(function() {
    +      resolve(sentinel);
    +    }, 0);
    +  });
    +
    +  var p = goog.Promise.resolve(dummy);
    +  var p2 = p.then(function(value) {
    +    return blockingPromise;
    +  });
    +
    +  p2.then(function(value) {
    +    assertEquals(sentinel, value);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testBlockingPromiseRejected() {
    +  asyncTestCase.waitForAsync();
    +  var blockingPromise = new goog.Promise(function(resolve, reject) {
    +    window.setTimeout(function() {
    +      reject(sentinel);
    +    }, 0);
    +  });
    +
    +  var p = goog.Promise.resolve(blockingPromise);
    +
    +  p.then(shouldNotCall, function(reason) {
    +    assertEquals(sentinel, reason);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testBlockingThenableFulfilled() {
    +  asyncTestCase.waitForAsync();
    +  var thenable = {
    +    then: function(onFulfill, onReject) { onFulfill(sentinel); }
    +  };
    +
    +  var p = goog.Promise.resolve(thenable).
    +      then(function(reason) {
    +        assertEquals(sentinel, reason);
    +      }, shouldNotCall).thenAlways(continueTesting);
    +}
    +
    +
    +function testBlockingThenableRejected() {
    +  asyncTestCase.waitForAsync();
    +  var thenable = {
    +    then: function(onFulfill, onReject) { onReject(sentinel); }
    +  };
    +
    +  var p = goog.Promise.resolve(thenable).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals(sentinel, reason);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testBlockingThenableThrows() {
    +  asyncTestCase.waitForAsync();
    +  var thenable = {
    +    then: function(onFulfill, onReject) { throw sentinel; }
    +  };
    +
    +  var p = goog.Promise.resolve(thenable).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals(sentinel, reason);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testBlockingThenableMisbehaves() {
    +  asyncTestCase.waitForAsync();
    +  var thenable = {
    +    then: function(onFulfill, onReject) {
    +      onFulfill(sentinel);
    +      onFulfill(dummy);
    +      onReject(dummy);
    +      throw dummy;
    +    }
    +  };
    +
    +  var p = goog.Promise.resolve(thenable).
    +      then(function(value) {
    +        assertEquals(
    +            'Only the first resolution of the Thenable should have a result.',
    +            sentinel, value);
    +      }, shouldNotCall).thenAlways(continueTesting);
    +}
    +
    +
    +function testNestingThenables() {
    +  asyncTestCase.waitForAsync();
    +  var thenableA = {
    +    then: function(onFulfill, onReject) { onFulfill(sentinel); }
    +  };
    +  var thenableB = {
    +    then: function(onFulfill, onReject) { onFulfill(thenableA); }
    +  };
    +  var thenableC = {
    +    then: function(onFulfill, onReject) { onFulfill(thenableB); }
    +  };
    +
    +  var p = goog.Promise.resolve(thenableC).
    +      then(function(value) {
    +        assertEquals(
    +            'Should resolve to the fulfillment value of thenableA',
    +            sentinel, value);
    +      }, shouldNotCall).thenAlways(continueTesting);
    +}
    +
    +
    +function testNestingThenablesRejected() {
    +  asyncTestCase.waitForAsync();
    +  var thenableA = {
    +    then: function(onFulfill, onReject) { onReject(sentinel); }
    +  };
    +  var thenableB = {
    +    then: function(onFulfill, onReject) { onReject(thenableA); }
    +  };
    +  var thenableC = {
    +    then: function(onFulfill, onReject) { onReject(thenableB); }
    +  };
    +
    +  var p = goog.Promise.reject(thenableC).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals(
    +            'Should resolve to rejection reason of thenableA',
    +            sentinel, reason);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testThenCatch() {
    +  asyncTestCase.waitForAsync();
    +  var catchCalled = false;
    +  var p = goog.Promise.reject();
    +
    +  var p2 = p.thenCatch(function(reason) {
    +    catchCalled = true;
    +    return sentinel;
    +  });
    +
    +  p2.then(function(value) {
    +    assertTrue(catchCalled);
    +    assertEquals(sentinel, value);
    +  }, shouldNotCall);
    +  p2.thenAlways(continueTesting);
    +}
    +
    +
    +function testRaceWithEmptyList() {
    +  asyncTestCase.waitForAsync();
    +  goog.Promise.race([]).then(function(value) {
    +    assertUndefined(value);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testRaceWithFulfill() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = fulfillSoon('a', 40);
    +  var b = fulfillSoon('b', 30);
    +  var c = fulfillSoon('c', 10);
    +  var d = fulfillSoon('d', 20);
    +
    +  goog.Promise.race([a, b, c, d]).
    +      then(function(value) {
    +        assertEquals('c', value);
    +        // Return the slowest input promise to wait for it to complete.
    +        return a;
    +      }).
    +      then(function(value) {
    +        assertEquals('The slowest promise should resolve eventually.',
    +                     'a', value);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testRaceWithReject() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = rejectSoon('rejected-a', 40);
    +  var b = rejectSoon('rejected-b', 30);
    +  var c = rejectSoon('rejected-c', 10);
    +  var d = rejectSoon('rejected-d', 20);
    +
    +  var p = goog.Promise.race([a, b, c, d]).
    +      then(shouldNotCall, function(value) {
    +        assertEquals('rejected-c', value);
    +        return a;
    +      }).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals('The slowest promise should resolve eventually.',
    +                     'rejected-a', reason);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testAllWithEmptyList() {
    +  asyncTestCase.waitForAsync();
    +  goog.Promise.all([]).then(function(value) {
    +    assertArrayEquals([], value);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testAllWithFulfill() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = fulfillSoon('a', 40);
    +  var b = fulfillSoon('b', 30);
    +  var c = fulfillSoon('c', 10);
    +  var d = fulfillSoon('d', 20);
    +
    +  goog.Promise.all([a, b, c, d]).then(function(value) {
    +    assertArrayEquals(['a', 'b', 'c', 'd'], value);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testAllWithReject() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = fulfillSoon('a', 40);
    +  var b = rejectSoon('rejected-b', 30);
    +  var c = fulfillSoon('c', 10);
    +  var d = fulfillSoon('d', 20);
    +
    +  goog.Promise.all([a, b, c, d]).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals('rejected-b', reason);
    +        return a;
    +      }).
    +      then(function(value) {
    +        assertEquals('Promise "a" should be fulfilled even though the all()' +
    +                     'was rejected.', 'a', value);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testFirstFulfilledWithEmptyList() {
    +  asyncTestCase.waitForAsync();
    +  goog.Promise.firstFulfilled([]).then(function(value) {
    +    assertUndefined(value);
    +  }).thenAlways(continueTesting);
    +}
    +
    +
    +function testFirstFulfilledWithFulfill() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = fulfillSoon('a', 40);
    +  var b = rejectSoon('rejected-b', 30);
    +  var c = rejectSoon('rejected-c', 10);
    +  var d = fulfillSoon('d', 20);
    +
    +  goog.Promise.firstFulfilled([a, b, c, d]).
    +      then(function(value) {
    +        assertEquals('d', value);
    +        return c;
    +      }).
    +      then(shouldNotCall, function(reason) {
    +        assertEquals(
    +            'Promise "c" should have been rejected before the some() resolved.',
    +            'rejected-c', reason);
    +        return a;
    +      }).
    +      then(function(reason) {
    +        assertEquals(
    +            'Promise "a" should be fulfilled even after some() has resolved.',
    +            'a', value);
    +      }, shouldNotCall).thenAlways(continueTesting);
    +}
    +
    +
    +function testFirstFulfilledWithReject() {
    +  asyncTestCase.waitForAsync();
    +
    +  var a = rejectSoon('rejected-a', 40);
    +  var b = rejectSoon('rejected-b', 30);
    +  var c = rejectSoon('rejected-c', 10);
    +  var d = rejectSoon('rejected-d', 20);
    +
    +  var p = goog.Promise.firstFulfilled([a, b, c, d]).
    +      then(shouldNotCall, function(reason) {
    +        assertArrayEquals(
    +            ['rejected-a', 'rejected-b', 'rejected-c', 'rejected-d'], reason);
    +      }).thenAlways(continueTesting);
    +}
    +
    +
    +function testThenAlwaysWithFulfill() {
    +  asyncTestCase.waitForAsync();
    +  var p = goog.Promise.resolve().
    +      thenAlways(function() {
    +        assertEquals(0, arguments.length);
    +      }).
    +      then(continueTesting, shouldNotCall);
    +}
    +
    +
    +function testThenAlwaysWithReject() {
    +  asyncTestCase.waitForAsync();
    +  var p = goog.Promise.reject().
    +      thenAlways(function() {
    +        assertEquals(0, arguments.length);
    +      }).
    +      then(shouldNotCall, continueTesting);
    +}
    +
    +
    +function testThenAlwaysCalledMultipleTimes() {
    +  asyncTestCase.waitForAsync();
    +  var calls = [];
    +
    +  var p = goog.Promise.resolve(sentinel);
    +  p.then(function(value) {
    +    assertEquals(sentinel, value);
    +    calls.push(1);
    +    return value;
    +  });
    +  p.thenAlways(function() {
    +    assertEquals(0, arguments.length);
    +    calls.push(2);
    +    throw Error('thenAlways throw');
    +  });
    +  p.then(function(value) {
    +    assertEquals(
    +        'Promise result should not mutate after throw from thenAlways.',
    +        sentinel, value);
    +    calls.push(3);
    +  });
    +  p.thenAlways(function() {
    +    assertArrayEquals([1, 2, 3], calls);
    +  });
    +  p.thenAlways(function() {
    +    assertEquals(
    +        'Should be one unhandled exception from the "thenAlways throw".',
    +        1, unhandledRejections.getCallCount());
    +    var rejectionCall = unhandledRejections.popLastCall();
    +    assertEquals(1, rejectionCall.getArguments().length);
    +    var err = rejectionCall.getArguments()[0];
    +    assertEquals('thenAlways throw', err.message);
    +    assertEquals(goog.global, rejectionCall.getThis());
    +  });
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testContextWithInit() {
    +  var initContext;
    +  var p = new goog.Promise(function(resolve, reject) {
    +    initContext = this;
    +  }, sentinel);
    +  assertEquals(sentinel, initContext);
    +}
    +
    +
    +function testContextWithInitDefault() {
    +  var initContext;
    +  var p = new goog.Promise(function(resolve, reject) {
    +    initContext = this;
    +  });
    +  assertEquals(
    +      'initFunc should default to being called in the global scope',
    +      goog.global, initContext);
    +}
    +
    +
    +function testContextWithFulfillment() {
    +  asyncTestCase.waitForAsync();
    +  var context = sentinel;
    +  var p = goog.Promise.resolve();
    +
    +  p.then(function() {
    +    assertEquals(
    +        'Call should be made in the global scope if no context is specified.',
    +        goog.global, this);
    +  });
    +  p.then(function() {
    +    assertEquals(sentinel, this);
    +  }, shouldNotCall, sentinel);
    +  p.thenAlways(function() {
    +    assertEquals(sentinel, this);
    +    continueTesting();
    +  }, sentinel);
    +}
    +
    +
    +function testContextWithRejection() {
    +  asyncTestCase.waitForAsync();
    +  var context = sentinel;
    +  var p = goog.Promise.reject();
    +
    +  p.then(shouldNotCall, function() {
    +    assertEquals(
    +        'Call should be made in the global scope if no context is specified.',
    +        goog.global, this);
    +  });
    +  p.then(shouldNotCall, function() {
    +    assertEquals(sentinel, this);
    +  }, sentinel);
    +  p.thenCatch(function() {
    +    assertEquals(sentinel, this);
    +  }, sentinel);
    +  p.thenAlways(function() {
    +    assertEquals(sentinel, this);
    +    continueTesting();
    +  }, sentinel);
    +}
    +
    +
    +function testCancel() {
    +  asyncTestCase.waitForAsync();
    +  var p = new goog.Promise(goog.nullFunction);
    +  p.then(shouldNotCall, function(reason) {
    +    assertTrue(reason instanceof goog.Promise.CancellationError);
    +    assertEquals('cancellation message', reason.message);
    +    continueTesting();
    +  });
    +  p.cancel('cancellation message');
    +}
    +
    +
    +function testCancelAfterResolve() {
    +  asyncTestCase.waitForAsync();
    +  var p = goog.Promise.resolve();
    +  p.cancel();
    +  p.then(null, shouldNotCall);
    +  p.thenAlways(continueTesting);
    +}
    +
    +
    +function testCancelAfterReject() {
    +  asyncTestCase.waitForAsync();
    +  var p = goog.Promise.reject(sentinel);
    +  p.cancel();
    +  p.then(shouldNotCall, function(reason) {
    +    assertEquals(sentinel, reason);
    +    continueTesting();
    +  });
    +}
    +
    +
    +function testCancelPropagation() {
    +  asyncTestCase.waitForSignals(2);
    +  var cancelError;
    +  var p = new goog.Promise(goog.nullFunction);
    +
    +  var p2 = p.then(shouldNotCall, function(reason) {
    +    cancelError = reason;
    +    assertTrue(reason instanceof goog.Promise.CancellationError);
    +    assertEquals('parent cancel message', reason.message);
    +    return sentinel;
    +  });
    +  p2.then(function(value) {
    +    assertEquals(
    +        'Child promises should receive the returned value of the parent.',
    +        sentinel, value);
    +    asyncTestCase.signal();
    +  }, shouldNotCall);
    +
    +  var p3 = p.then(shouldNotCall, function(reason) {
    +    assertEquals(
    +        'Every onRejected handler should receive the same cancel error.',
    +        cancelError, reason);
    +    assertEquals('parent cancel message', reason.message);
    +    asyncTestCase.signal();
    +  });
    +
    +  p.cancel('parent cancel message');
    +}
    +
    +
    +function testCancelPropagationUpward() {
    +  asyncTestCase.waitForAsync();
    +  var cancelError;
    +  var cancelCalls = [];
    +  var parent = new goog.Promise(goog.nullFunction);
    +
    +  var child = parent.then(shouldNotCall, function(reason) {
    +    assertTrue(reason instanceof goog.Promise.CancellationError);
    +    assertEquals('grandChild cancel message', reason.message);
    +    cancelError = reason;
    +    cancelCalls.push('parent');
    +  });
    +
    +  var grandChild = child.then(shouldNotCall, function(reason) {
    +    assertEquals('Child should receive the same cancel error.',
    +                 cancelError, reason);
    +    cancelCalls.push('child');
    +  });
    +
    +  grandChild.then(shouldNotCall, function(reason) {
    +    assertEquals('GrandChild should receive the same cancel error.',
    +                 cancelError, reason);
    +    cancelCalls.push('grandChild');
    +  });
    +
    +  grandChild.then(shouldNotCall, function(reason) {
    +    assertArrayEquals(
    +        'Each promise in the hierarchy has a single child, so canceling the ' +
    +        'grandChild should cancel each ancestor in order.',
    +        ['parent', 'child', 'grandChild'], cancelCalls);
    +  }).thenAlways(continueTesting);
    +
    +  grandChild.cancel('grandChild cancel message');
    +}
    +
    +
    +function testCancelPropagationUpwardWithMultipleChildren() {
    +  asyncTestCase.waitForAsync();
    +  var cancelError;
    +  var cancelCalls = [];
    +  var parent = fulfillSoon(sentinel, 0);
    +
    +  parent.then(function(value) {
    +    assertEquals(
    +        'Non-canceled callbacks should be called after a sibling is canceled.',
    +        sentinel, value);
    +    continueTesting();
    +  });
    +
    +  var child = parent.then(shouldNotCall, function(reason) {
    +    assertTrue(reason instanceof goog.Promise.CancellationError);
    +    assertEquals('grandChild cancel message', reason.message);
    +    cancelError = reason;
    +    cancelCalls.push('child');
    +  });
    +
    +  var grandChild = child.then(shouldNotCall, function(reason) {
    +    assertEquals(reason, cancelError);
    +    cancelCalls.push('grandChild');
    +  });
    +
    +  grandChild.then(shouldNotCall, function(reason) {
    +    assertEquals(reason, cancelError);
    +    assertArrayEquals(
    +        'The parent promise has multiple children, so only the child and ' +
    +        'grandChild should be canceled.',
    +        ['child', 'grandChild'], cancelCalls);
    +  });
    +
    +  grandChild.cancel('grandChild cancel message');
    +}
    +
    +
    +function testCancelRecovery() {
    +  asyncTestCase.waitForSignals(2);
    +  var cancelError;
    +  var cancelCalls = [];
    +
    +  var parent = fulfillSoon(sentinel, 100);
    +
    +  var sibling1 = parent.then(function(value) {
    +    assertEquals(
    +        'Non-canceled callbacks should be called after a sibling is canceled.',
    +        sentinel, value);
    +  });
    +
    +  var sibling2 = parent.then(shouldNotCall, function(reason) {
    +    assertTrue(reason instanceof goog.Promise.CancellationError);
    +    cancelError = reason;
    +    cancelCalls.push('sibling2');
    +    return sentinel;
    +  });
    +
    +  parent.thenAlways(function() {
    +    asyncTestCase.signal();
    +  });
    +
    +  var grandChild = sibling2.then(function(value) {
    +    cancelCalls.push('child');
    +    assertEquals(
    +        'Returning a non-cancel value should uncancel the grandChild.',
    +        value, sentinel);
    +    assertArrayEquals(['sibling2', 'child'], cancelCalls);
    +  }, shouldNotCall).thenAlways(function() {
    +    asyncTestCase.signal();
    +  });
    +
    +  grandChild.cancel();
    +}
    +
    +
    +function testCancellationError() {
    +  var err = new goog.Promise.CancellationError('cancel message');
    +  assertTrue(err instanceof Error);
    +  assertTrue(err instanceof goog.Promise.CancellationError);
    +  assertEquals('cancel', err.name);
    +  assertEquals('cancel message', err.message);
    +}
    +
    +
    +function testMockClock() {
    +  mockClock.install();
    +
    +  var resolveA;
    +  var resolveB;
    +  var calls = [];
    +
    +  var p = new goog.Promise(function(resolve, reject) {
    +    resolveA = resolve;
    +  });
    +
    +  p.then(function(value) {
    +    assertEquals(sentinel, value);
    +    calls.push('then');
    +  });
    +
    +  var fulfilledChild = p.then(function(value) {
    +    assertEquals(sentinel, value);
    +    return goog.Promise.resolve(1);
    +  }).then(function(value) {
    +    assertEquals(1, value);
    +    calls.push('fulfilledChild');
    +
    +  });
    +
    +  var rejectedChild = p.then(function(value) {
    +    assertEquals(sentinel, value);
    +    return goog.Promise.reject(2);
    +  }).then(shouldNotCall, function(reason) {
    +    assertEquals(2, reason);
    +    calls.push('rejectedChild');
    +  });
    +
    +  var unresolvedChild = p.then(function(value) {
    +    assertEquals(sentinel, value);
    +    return new goog.Promise(function(r) {
    +      resolveB = r;
    +    });
    +  }).then(function(value) {
    +    assertEquals(3, value);
    +    calls.push('unresolvedChild');
    +  });
    +
    +  resolveA(sentinel);
    +  assertArrayEquals(
    +      'Calls must not be resolved until the clock ticks.',
    +      [], calls);
    +
    +  mockClock.tick();
    +  assertArrayEquals(
    +      'All resolved Promises should execute in the same timestep.',
    +      ['then', 'fulfilledChild', 'rejectedChild'], calls);
    +
    +  resolveB(3);
    +  assertArrayEquals(
    +      'New calls must not resolve until the clock ticks.',
    +      ['then', 'fulfilledChild', 'rejectedChild'], calls);
    +
    +  mockClock.tick();
    +  assertArrayEquals(
    +      'All callbacks should have executed.',
    +      ['then', 'fulfilledChild', 'rejectedChild', 'unresolvedChild'], calls);
    +}
    +
    +
    +function testHandledRejection() {
    +  mockClock.install();
    +  goog.Promise.reject(sentinel).then(shouldNotCall, function(reason) {});
    +
    +  mockClock.tick();
    +  assertEquals(0, unhandledRejections.getCallCount());
    +}
    +
    +
    +function testUnhandledRejection() {
    +  mockClock.install();
    +  goog.Promise.reject(sentinel);
    +
    +  mockClock.tick();
    +  assertEquals(1, unhandledRejections.getCallCount());
    +  var rejectionCall = unhandledRejections.popLastCall();
    +  assertArrayEquals([sentinel], rejectionCall.getArguments());
    +  assertEquals(goog.global, rejectionCall.getThis());
    +}
    +
    +
    +function testUnhandledRejection_asyncTestCase() {
    +  goog.Promise.reject(sentinel);
    +
    +  goog.Promise.setUnhandledRejectionHandler(function(error) {
    +    assertEquals(sentinel, error);
    +    asyncTestCase.continueTesting();
    +  });
    +}
    +
    +
    +function testUnhandledThrow_asyncTestCase() {
    +  goog.Promise.resolve().then(function() {
    +    throw sentinel;
    +  });
    +
    +  goog.Promise.setUnhandledRejectionHandler(function(error) {
    +    assertEquals(sentinel, error);
    +    asyncTestCase.continueTesting();
    +  });
    +}
    +
    +
    +function testUnhandledBlockingRejection() {
    +  mockClock.install();
    +  var blocker = goog.Promise.reject(sentinel);
    +  goog.Promise.resolve(blocker);
    +
    +  mockClock.tick();
    +  assertEquals(1, unhandledRejections.getCallCount());
    +  var rejectionCall = unhandledRejections.popLastCall();
    +  assertArrayEquals([sentinel], rejectionCall.getArguments());
    +  assertEquals(goog.global, rejectionCall.getThis());
    +}
    +
    +
    +function testUnhandledRejectionAfterThenAlways() {
    +  mockClock.install();
    +  var resolver = goog.Promise.withResolver();
    +  resolver.promise.thenAlways(function() {});
    +  resolver.reject(sentinel);
    +
    +  mockClock.tick();
    +  assertEquals(1, unhandledRejections.getCallCount());
    +  var rejectionCall = unhandledRejections.popLastCall();
    +  assertArrayEquals([sentinel], rejectionCall.getArguments());
    +  assertEquals(goog.global, rejectionCall.getThis());
    +}
    +
    +
    +function testHandledBlockingRejection() {
    +  mockClock.install();
    +  var blocker = goog.Promise.reject(sentinel);
    +  goog.Promise.resolve(blocker).then(shouldNotCall, function(reason) {});
    +
    +  mockClock.tick();
    +  assertEquals(0, unhandledRejections.getCallCount());
    +}
    +
    +
    +function testUnhandledRejectionWithTimeout() {
    +  mockClock.install();
    +  stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', 200);
    +  goog.Promise.reject(sentinel);
    +
    +  mockClock.tick(199);
    +  assertEquals(0, unhandledRejections.getCallCount());
    +
    +  mockClock.tick(1);
    +  assertEquals(1, unhandledRejections.getCallCount());
    +}
    +
    +
    +function testHandledRejectionWithTimeout() {
    +  mockClock.install();
    +  stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', 200);
    +  var p = goog.Promise.reject(sentinel);
    +
    +  mockClock.tick(199);
    +  p.then(shouldNotCall, function(reason) {});
    +
    +  mockClock.tick(1);
    +  assertEquals(0, unhandledRejections.getCallCount());
    +}
    +
    +
    +function testUnhandledRejectionDisabled() {
    +  mockClock.install();
    +  stubs.replace(goog.Promise, 'UNHANDLED_REJECTION_DELAY', -1);
    +  goog.Promise.reject(sentinel);
    +
    +  mockClock.tick();
    +  assertEquals(0, unhandledRejections.getCallCount());
    +}
    +
    +
    +function testThenableInterface() {
    +  var promise = new goog.Promise(function(resolve, reject) {});
    +  assertTrue(goog.Thenable.isImplementedBy(promise));
    +
    +  assertFalse(goog.Thenable.isImplementedBy({}));
    +  assertFalse(goog.Thenable.isImplementedBy('string'));
    +  assertFalse(goog.Thenable.isImplementedBy(1));
    +  assertFalse(goog.Thenable.isImplementedBy({then: function() {}}));
    +
    +  function T() {}
    +  T.prototype.then = function(opt_a, opt_b, opt_c) {};
    +  goog.Thenable.addImplementation(T);
    +  assertTrue(goog.Thenable.isImplementedBy(new T));
    +
    +  // Test COMPILED code path.
    +  try {
    +    COMPIlED = true;
    +    function C() {}
    +    C.prototype.then = function(opt_a, opt_b, opt_c) {};
    +    goog.Thenable.addImplementation(C);
    +    assertTrue(goog.Thenable.isImplementedBy(new C));
    +  } finally {
    +    COMPILED = false;
    +  }
    +}
    +
    +
    +function testCreateWithResolver_Resolved() {
    +  mockClock.install();
    +  var timesCalled = 0;
    +
    +  var resolver = goog.Promise.withResolver();
    +
    +  resolver.promise.then(function(value) {
    +    timesCalled++;
    +    assertEquals(sentinel, value);
    +  }, fail);
    +
    +  assertEquals('then() must return before callbacks are invoked.',
    +      0, timesCalled);
    +
    +  mockClock.tick();
    +
    +  assertEquals('promise is not resolved until resolver is invoked.',
    +      0, timesCalled);
    +
    +  resolver.resolve(sentinel);
    +
    +  assertEquals('resolution is delayed until the next tick',
    +      0, timesCalled);
    +
    +  mockClock.tick();
    +
    +  assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
    +}
    +
    +
    +function testCreateWithResolver_Rejected() {
    +  mockClock.install();
    +  var timesCalled = 0;
    +
    +  var resolver = goog.Promise.withResolver();
    +
    +  resolver.promise.then(fail, function(reason) {
    +    timesCalled++;
    +    assertEquals(sentinel, reason);
    +  });
    +
    +  assertEquals('then() must return before callbacks are invoked.',
    +      0, timesCalled);
    +
    +  mockClock.tick();
    +
    +  assertEquals('promise is not resolved until resolver is invoked.',
    +      0, timesCalled);
    +
    +  resolver.reject(sentinel);
    +
    +  assertEquals('resolution is delayed until the next tick',
    +      0, timesCalled);
    +
    +  mockClock.tick();
    +
    +  assertEquals('onFulfilled must be called exactly once.', 1, timesCalled);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/resolver.js b/mapbox-gl/closure-library/closure/goog/promise/resolver.js
    new file mode 100644
    index 0000000000..06ee2e5d1b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/resolver.js
    @@ -0,0 +1,48 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.promise.Resolver');
    +
    +
    +
    +/**
    + * Resolver interface for promises. The resolver is a convenience interface that
    + * bundles the promise and its associated resolve and reject functions together,
    + * for cases where the resolver needs to be persisted internally.
    + *
    + * @interface
    + * @template TYPE
    + */
    +goog.promise.Resolver = function() {};
    +
    +
    +/**
    + * The promise that created this resolver.
    + * @type {!goog.Promise<TYPE>}
    + */
    +goog.promise.Resolver.prototype.promise;
    +
    +
    +/**
    + * Resolves this resolver with the specified value.
    + * @type {function((TYPE|goog.Promise<TYPE>|Thenable)=)}
    + */
    +goog.promise.Resolver.prototype.resolve;
    +
    +
    +/**
    + * Rejects this resolver with the specified reason.
    + * @type {function(*): void}
    + */
    +goog.promise.Resolver.prototype.reject;
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/testsuiteadapter.js b/mapbox-gl/closure-library/closure/goog/promise/testsuiteadapter.js
    new file mode 100644
    index 0000000000..57e2e4ddd2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/testsuiteadapter.js
    @@ -0,0 +1,74 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Test adapter for testing Closure Promises against the
    + * Promises/A+ Compliance Test Suite, which is implemented as a Node.js module.
    + *
    + * This test suite adapter may not be run in Node.js directly, but must first be
    + * compiled with the Closure Compiler to pull in the required dependencies.
    + *
    + * @see https://npmjs.org/package/promises-aplus-tests
    + */
    +
    +goog.provide('goog.promise.testSuiteAdapter');
    +
    +goog.require('goog.Promise');
    +
    +goog.setTestOnly('goog.promise.testSuiteAdapter');
    +
    +
    +var promisesAplusTests = /** @type {function(!Object, function(*))} */ (
    +    require('promises_aplus_tests'));
    +
    +
    +/**
    + * Adapter for specifying Promise-creating functions to the Promises test suite.
    + * @type {!Object}
    + */
    +goog.promise.testSuiteAdapter = {
    +  /** @type {function(*): !goog.Promise} */
    +  'resolved': goog.Promise.resolve,
    +
    +  /** @type {function(*): !goog.Promise} */
    +  'rejected': goog.Promise.reject,
    +
    +  /** @return {!Object} */
    +  'deferred': function() {
    +    var promiseObj = {};
    +    promiseObj['promise'] = new goog.Promise(function(resolve, reject) {
    +      promiseObj['resolve'] = resolve;
    +      promiseObj['reject'] = reject;
    +    });
    +    return promiseObj;
    +  }
    +};
    +
    +
    +// Node.js defines setTimeout globally, but Closure relies on finding it
    +// defined on goog.global.
    +goog.exportSymbol('setTimeout', setTimeout);
    +
    +
    +// Rethrowing an error to the global scope kills Node immediately. Suppress
    +// error rethrowing for running this test suite.
    +goog.Promise.setUnhandledRejectionHandler(goog.nullFunction);
    +
    +
    +// Run the tests, exiting with a failure code if any of the tests fail.
    +promisesAplusTests(goog.promise.testSuiteAdapter, function(err) {
    +  if (err) {
    +    process.exit(1);
    +  }
    +});
    diff --git a/mapbox-gl/closure-library/closure/goog/promise/thenable.js b/mapbox-gl/closure-library/closure/goog/promise/thenable.js
    new file mode 100644
    index 0000000000..96bbf95898
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/promise/thenable.js
    @@ -0,0 +1,111 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.Thenable');
    +
    +
    +
    +/**
    + * Provides a more strict interface for Thenables in terms of
    + * http://promisesaplus.com for interop with {@see goog.Promise}.
    + *
    + * @interface
    + * @extends {IThenable<TYPE>}
    + * @template TYPE
    + */
    +goog.Thenable = function() {};
    +
    +
    +/**
    + * Adds callbacks that will operate on the result of the Thenable, returning a
    + * new child Promise.
    + *
    + * If the Thenable is fulfilled, the {@code onFulfilled} callback will be
    + * invoked with the fulfillment value as argument, and the child Promise will
    + * be fulfilled with the return value of the callback. If the callback throws
    + * an exception, the child Promise will be rejected with the thrown value
    + * instead.
    + *
    + * If the Thenable is rejected, the {@code onRejected} callback will be invoked
    + * with the rejection reason as argument, and the child Promise will be rejected
    + * with the return value of the callback or thrown value.
    + *
    + * @param {?(function(this:THIS, TYPE):
    + *             (RESULT|IThenable<RESULT>|Thenable))=} opt_onFulfilled A
    + *     function that will be invoked with the fulfillment value if the Promise
    + *     is fullfilled.
    + * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
    + *     be invoked with the rejection reason if the Promise is rejected.
    + * @param {THIS=} opt_context An optional context object that will be the
    + *     execution context for the callbacks. By default, functions are executed
    + *     with the default this.
    + * @return {!goog.Promise<RESULT>} A new Promise that will receive the result
    + *     of the fulfillment or rejection callback.
    + * @template RESULT,THIS
    + */
    +goog.Thenable.prototype.then = function(opt_onFulfilled, opt_onRejected,
    +    opt_context) {};
    +
    +
    +/**
    + * An expando property to indicate that an object implements
    + * {@code goog.Thenable}.
    + *
    + * {@see addImplementation}.
    + *
    + * @const
    + */
    +goog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable';
    +
    +
    +/**
    + * Marks a given class (constructor) as an implementation of Thenable, so
    + * that we can query that fact at runtime. The class must have already
    + * implemented the interface.
    + * Exports a 'then' method on the constructor prototype, so that the objects
    + * also implement the extern {@see goog.Thenable} interface for interop with
    + * other Promise implementations.
    + * @param {function(new:goog.Thenable,...?)} ctor The class constructor. The
    + *     corresponding class must have already implemented the interface.
    + */
    +goog.Thenable.addImplementation = function(ctor) {
    +  goog.exportProperty(ctor.prototype, 'then', ctor.prototype.then);
    +  if (COMPILED) {
    +    ctor.prototype[goog.Thenable.IMPLEMENTED_BY_PROP] = true;
    +  } else {
    +    // Avoids dictionary access in uncompiled mode.
    +    ctor.prototype.$goog_Thenable = true;
    +  }
    +};
    +
    +
    +/**
    + * @param {*} object
    + * @return {boolean} Whether a given instance implements {@code goog.Thenable}.
    + *     The class/superclass of the instance must call {@code addImplementation}.
    + */
    +goog.Thenable.isImplementedBy = function(object) {
    +  if (!object) {
    +    return false;
    +  }
    +  try {
    +    if (COMPILED) {
    +      return !!object[goog.Thenable.IMPLEMENTED_BY_PROP];
    +    }
    +    return !!object.$goog_Thenable;
    +  } catch (e) {
    +    // Property access seems to be forbidden.
    +    return false;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto/proto.js b/mapbox-gl/closure-library/closure/goog/proto/proto.js
    new file mode 100644
    index 0000000000..f505760a57
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto/proto.js
    @@ -0,0 +1,44 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol buffer serializer.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.proto');
    +
    +
    +goog.require('goog.proto.Serializer');
    +
    +
    +/**
    + * Instance of the serializer object.
    + * @type {goog.proto.Serializer}
    + * @private
    + */
    +goog.proto.serializer_ = null;
    +
    +
    +/**
    + * Serializes an object or a value to a protocol buffer string.
    + * @param {Object} object The object to serialize.
    + * @return {string} The serialized protocol buffer string.
    + */
    +goog.proto.serialize = function(object) {
    +  if (!goog.proto.serializer_) {
    +    goog.proto.serializer_ = new goog.proto.Serializer;
    +  }
    +  return goog.proto.serializer_.serialize(object);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto/serializer.js b/mapbox-gl/closure-library/closure/goog/proto/serializer.js
    new file mode 100644
    index 0000000000..6fe3b0de7f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto/serializer.js
    @@ -0,0 +1,70 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol buffer serializer.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +// TODO(arv): Serialize booleans as 0 and 1
    +
    +
    +goog.provide('goog.proto.Serializer');
    +
    +
    +goog.require('goog.json.Serializer');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Object that can serialize objects or values to a protocol buffer string.
    + * @constructor
    + * @extends {goog.json.Serializer}
    + * @final
    + */
    +goog.proto.Serializer = function() {
    +  goog.json.Serializer.call(this);
    +};
    +goog.inherits(goog.proto.Serializer, goog.json.Serializer);
    +
    +
    +/**
    + * Serializes an array to a protocol buffer string. This overrides the JSON
    + * method to output empty slots when the value is null or undefined.
    + * @param {Array<*>} arr The array to serialize.
    + * @param {Array<string>} sb Array used as a string builder.
    + * @override
    + */
    +goog.proto.Serializer.prototype.serializeArray = function(arr, sb) {
    +  var l = arr.length;
    +  sb.push('[');
    +  var emptySlots = 0;
    +  var sep = '';
    +  for (var i = 0; i < l; i++) {
    +    if (arr[i] == null) { // catches undefined as well
    +      emptySlots++;
    +    } else {
    +      if (emptySlots > 0) {
    +        sb.push(goog.string.repeat(',', emptySlots));
    +        emptySlots = 0;
    +      }
    +      sb.push(sep);
    +      this.serializeInternal(arr[i], sb);
    +      sep = ',';
    +    }
    +  }
    +  sb.push(']');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto/serializer_test.html b/mapbox-gl/closure-library/closure/goog/proto/serializer_test.html
    new file mode 100644
    index 0000000000..09841cf1f4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto/serializer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.protoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto/serializer_test.js b/mapbox-gl/closure-library/closure/goog/proto/serializer_test.js
    new file mode 100644
    index 0000000000..6965a9178f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto/serializer_test.js
    @@ -0,0 +1,38 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.protoTest');
    +goog.setTestOnly('goog.protoTest');
    +
    +goog.require('goog.proto');
    +goog.require('goog.testing.jsunit');
    +
    +var serialize = goog.proto.serialize;
    +
    +function testArraySerialize() {
    +
    +  assertEquals('Empty array', serialize([]), '[]');
    +
    +  assertEquals('Normal array', serialize([0, 1, 2]), '[0,1,2]');
    +  assertEquals('Empty start', serialize([, 1, 2]), '[,1,2]');
    +  assertEquals('Empty start', serialize([,,, 3, 4]), '[,,,3,4]');
    +  assertEquals('Empty middle', serialize([0,, 2]), '[0,,2]');
    +  assertEquals('Empty middle', serialize([0,,, 3]), '[0,,,3]');
    +  assertEquals('Empty end', serialize([0, 1, 2]), '[0,1,2]');
    +  assertEquals('Empty end', serialize([0, 1, 2,,]), '[0,1,2]');
    +  assertEquals('Empty start and end', serialize([,, 2,, 4]), '[,,2,,4]');
    +  assertEquals('All elements empty', serialize([,,,]), '[]');
    +
    +  assertEquals('Nested', serialize([, 1, [, 1, [, 1]]]), '[,1,[,1,[,1]]]');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/descriptor.js b/mapbox-gl/closure-library/closure/goog/proto2/descriptor.js
    new file mode 100644
    index 0000000000..16a9ef93cd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/descriptor.js
    @@ -0,0 +1,202 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer (Message) Descriptor class.
    + */
    +
    +goog.provide('goog.proto2.Descriptor');
    +goog.provide('goog.proto2.Metadata');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @typedef {{name: (string|undefined),
    + *            fullName: (string|undefined),
    + *            containingType: (goog.proto2.Message|undefined)}}
    + */
    +goog.proto2.Metadata;
    +
    +
    +
    +/**
    + * A class which describes a Protocol Buffer 2 Message.
    + *
    + * @param {function(new:goog.proto2.Message)} messageType Constructor for
    + *      the message class that this descriptor describes.
    + * @param {!goog.proto2.Metadata} metadata The metadata about the message that
    + *      will be used to construct this descriptor.
    + * @param {Array<!goog.proto2.FieldDescriptor>} fields The fields of the
    + *      message described by this descriptor.
    + *
    + * @constructor
    + * @final
    + */
    +goog.proto2.Descriptor = function(messageType, metadata, fields) {
    +
    +  /**
    +   * @type {function(new:goog.proto2.Message)}
    +   * @private
    +   */
    +  this.messageType_ = messageType;
    +
    +  /**
    +   * @type {?string}
    +   * @private
    +   */
    +  this.name_ = metadata.name || null;
    +
    +  /**
    +   * @type {?string}
    +   * @private
    +   */
    +  this.fullName_ = metadata.fullName || null;
    +
    +  /**
    +   * @type {goog.proto2.Message|undefined}
    +   * @private
    +   */
    +  this.containingType_ = metadata.containingType;
    +
    +  /**
    +   * The fields of the message described by this descriptor.
    +   * @type {!Object<number, !goog.proto2.FieldDescriptor>}
    +   * @private
    +   */
    +  this.fields_ = {};
    +
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +    this.fields_[field.getTag()] = field;
    +  }
    +};
    +
    +
    +/**
    + * Returns the name of the message, if any.
    + *
    + * @return {?string} The name.
    + */
    +goog.proto2.Descriptor.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * Returns the full name of the message, if any.
    + *
    + * @return {?string} The name.
    + */
    +goog.proto2.Descriptor.prototype.getFullName = function() {
    +  return this.fullName_;
    +};
    +
    +
    +/**
    + * Returns the descriptor of the containing message type or null if none.
    + *
    + * @return {goog.proto2.Descriptor} The descriptor.
    + */
    +goog.proto2.Descriptor.prototype.getContainingType = function() {
    +  if (!this.containingType_) {
    +    return null;
    +  }
    +
    +  return this.containingType_.getDescriptor();
    +};
    +
    +
    +/**
    + * Returns the fields in the message described by this descriptor ordered by
    + * tag.
    + *
    + * @return {!Array<!goog.proto2.FieldDescriptor>} The array of field
    + *     descriptors.
    + */
    +goog.proto2.Descriptor.prototype.getFields = function() {
    +  /**
    +   * @param {!goog.proto2.FieldDescriptor} fieldA First field.
    +   * @param {!goog.proto2.FieldDescriptor} fieldB Second field.
    +   * @return {number} Negative if fieldA's tag number is smaller, positive
    +   *     if greater, zero if the same.
    +   */
    +  function tagComparator(fieldA, fieldB) {
    +    return fieldA.getTag() - fieldB.getTag();
    +  };
    +
    +  var fields = goog.object.getValues(this.fields_);
    +  goog.array.sort(fields, tagComparator);
    +
    +  return fields;
    +};
    +
    +
    +/**
    + * Returns the fields in the message as a key/value map, where the key is
    + * the tag number of the field. DO NOT MODIFY THE RETURNED OBJECT. We return
    + * the actual, internal, fields map for performance reasons, and changing the
    + * map can result in undefined behavior of this library.
    + *
    + * @return {!Object<number, !goog.proto2.FieldDescriptor>} The field map.
    + */
    +goog.proto2.Descriptor.prototype.getFieldsMap = function() {
    +  return this.fields_;
    +};
    +
    +
    +/**
    + * Returns the field matching the given name, if any. Note that
    + * this method searches over the *original* name of the field,
    + * not the camelCase version.
    + *
    + * @param {string} name The field name for which to search.
    + *
    + * @return {goog.proto2.FieldDescriptor} The field found, if any.
    + */
    +goog.proto2.Descriptor.prototype.findFieldByName = function(name) {
    +  var valueFound = goog.object.findValue(this.fields_,
    +      function(field, key, obj) {
    +        return field.getName() == name;
    +      });
    +
    +  return /** @type {goog.proto2.FieldDescriptor} */ (valueFound) || null;
    +};
    +
    +
    +/**
    + * Returns the field matching the given tag number, if any.
    + *
    + * @param {number|string} tag The field tag number for which to search.
    + *
    + * @return {goog.proto2.FieldDescriptor} The field found, if any.
    + */
    +goog.proto2.Descriptor.prototype.findFieldByTag = function(tag) {
    +  goog.asserts.assert(goog.string.isNumeric(tag));
    +  return this.fields_[parseInt(tag, 10)] || null;
    +};
    +
    +
    +/**
    + * Creates an instance of the message type that this descriptor
    + * describes.
    + *
    + * @return {!goog.proto2.Message} The instance of the message.
    + */
    +goog.proto2.Descriptor.prototype.createMessageInstance = function() {
    +  return new this.messageType_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.html b/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.html
    new file mode 100644
    index 0000000000..6904769263
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - descriptor.js
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.DescriptorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.js b/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.js
    new file mode 100644
    index 0000000000..48b11dbc24
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/descriptor_test.js
    @@ -0,0 +1,78 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.DescriptorTest');
    +goog.setTestOnly('goog.proto2.DescriptorTest');
    +
    +goog.require('goog.proto2.Descriptor');
    +goog.require('goog.proto2.Message');
    +goog.require('goog.testing.jsunit');
    +
    +function testDescriptorConstruction() {
    +  var messageType = function() {};
    +  var descriptor = new goog.proto2.Descriptor(messageType, {
    +    name: 'test',
    +    fullName: 'this.is.a.test'
    +  }, []);
    +
    +  assertEquals('test', descriptor.getName());
    +  assertEquals('this.is.a.test', descriptor.getFullName());
    +  assertEquals(null, descriptor.getContainingType());
    +}
    +
    +function testParentDescriptor() {
    +  var parentType = function() {};
    +  var messageType = function() {};
    +
    +  var parentDescriptor = new goog.proto2.Descriptor(parentType, {
    +    name: 'parent',
    +    fullName: 'this.is.a.parent'
    +  }, []);
    +
    +  parentType.getDescriptor = function() {
    +    return parentDescriptor;
    +  };
    +
    +  var descriptor = new goog.proto2.Descriptor(messageType, {
    +    name: 'test',
    +    fullName: 'this.is.a.test',
    +    containingType: parentType
    +  }, []);
    +
    +  assertEquals(parentDescriptor, descriptor.getContainingType());
    +}
    +
    +function testStaticGetDescriptorCachesResults() {
    +  var messageType = function() {};
    +
    +  // This method would be provided by proto_library() BUILD rule.
    +  messageType.prototype.getDescriptor = function() {
    +    if (!messageType.descriptor_) {
    +      // The descriptor is created lazily when we instantiate a new instance.
    +      var descriptorObj = {
    +        0: {
    +          name: 'test',
    +          fullName: 'this.is.a.test'
    +        }
    +      };
    +      messageType.descriptor_ = goog.proto2.Message.createDescriptor(
    +          messageType, descriptorObj);
    +    }
    +    return messageType.descriptor_;
    +  };
    +  messageType.getDescriptor = messageType.prototype.getDescriptor;
    +
    +  var descriptor = messageType.getDescriptor();
    +  assertEquals(descriptor, messageType.getDescriptor());  // same instance
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor.js b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor.js
    new file mode 100644
    index 0000000000..4a2ba240d2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor.js
    @@ -0,0 +1,306 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer Field Descriptor class.
    + */
    +
    +goog.provide('goog.proto2.FieldDescriptor');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * A class which describes a field in a Protocol Buffer 2 Message.
    + *
    + * @param {function(new:goog.proto2.Message)} messageType Constructor for the
    + *     message class to which the field described by this class belongs.
    + * @param {number|string} tag The field's tag index.
    + * @param {Object} metadata The metadata about this field that will be used
    + *     to construct this descriptor.
    + *
    + * @constructor
    + * @final
    + */
    +goog.proto2.FieldDescriptor = function(messageType, tag, metadata) {
    +  /**
    +   * The message type that contains the field that this
    +   * descriptor describes.
    +   * @private {function(new:goog.proto2.Message)}
    +   */
    +  this.parent_ = messageType;
    +
    +  // Ensure that the tag is numeric.
    +  goog.asserts.assert(goog.string.isNumeric(tag));
    +
    +  /**
    +   * The field's tag number.
    +   * @private {number}
    +   */
    +  this.tag_ = /** @type {number} */ (tag);
    +
    +  /**
    +   * The field's name.
    +   * @private {string}
    +   */
    +  this.name_ = metadata.name;
    +
    +  /** @type {goog.proto2.FieldDescriptor.FieldType} */
    +  metadata.fieldType;
    +
    +  /** @type {*} */
    +  metadata.repeated;
    +
    +  /** @type {*} */
    +  metadata.required;
    +
    +  /** @type {*} */
    +  metadata.packed;
    +
    +  /**
    +   * If true, this field is a packed field.
    +   * @private {boolean}
    +   */
    +  this.isPacked_ = !!metadata.packed;
    +
    +  /**
    +   * If true, this field is a repeating field.
    +   * @private {boolean}
    +   */
    +  this.isRepeated_ = !!metadata.repeated;
    +
    +  /**
    +   * If true, this field is required.
    +   * @private {boolean}
    +   */
    +  this.isRequired_ = !!metadata.required;
    +
    +  /**
    +   * The field type of this field.
    +   * @private {goog.proto2.FieldDescriptor.FieldType}
    +   */
    +  this.fieldType_ = metadata.fieldType;
    +
    +  /**
    +   * If this field is a primitive: The native (ECMAScript) type of this field.
    +   * If an enumeration: The enumeration object.
    +   * If a message or group field: The Message function.
    +   * @private {Function}
    +   */
    +  this.nativeType_ = metadata.type;
    +
    +  /**
    +   * Is it permissible on deserialization to convert between numbers and
    +   * well-formed strings?  Is true for 64-bit integral field types and float and
    +   * double types, false for all other field types.
    +   * @private {boolean}
    +   */
    +  this.deserializationConversionPermitted_ = false;
    +
    +  switch (this.fieldType_) {
    +    case goog.proto2.FieldDescriptor.FieldType.INT64:
    +    case goog.proto2.FieldDescriptor.FieldType.UINT64:
    +    case goog.proto2.FieldDescriptor.FieldType.FIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.SINT64:
    +    case goog.proto2.FieldDescriptor.FieldType.FLOAT:
    +    case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
    +      this.deserializationConversionPermitted_ = true;
    +      break;
    +  }
    +
    +  /**
    +   * The default value of this field, if different from the default, default
    +   * value.
    +   * @private {*}
    +   */
    +  this.defaultValue_ = metadata.defaultValue;
    +};
    +
    +
    +/**
    + * An enumeration defining the possible field types.
    + * Should be a mirror of that defined in descriptor.h.
    + *
    + * @enum {number}
    + */
    +goog.proto2.FieldDescriptor.FieldType = {
    +  DOUBLE: 1,
    +  FLOAT: 2,
    +  INT64: 3,
    +  UINT64: 4,
    +  INT32: 5,
    +  FIXED64: 6,
    +  FIXED32: 7,
    +  BOOL: 8,
    +  STRING: 9,
    +  GROUP: 10,
    +  MESSAGE: 11,
    +  BYTES: 12,
    +  UINT32: 13,
    +  ENUM: 14,
    +  SFIXED32: 15,
    +  SFIXED64: 16,
    +  SINT32: 17,
    +  SINT64: 18
    +};
    +
    +
    +/**
    + * Returns the tag of the field that this descriptor represents.
    + *
    + * @return {number} The tag number.
    + */
    +goog.proto2.FieldDescriptor.prototype.getTag = function() {
    +  return this.tag_;
    +};
    +
    +
    +/**
    + * Returns the descriptor describing the message that defined this field.
    + * @return {!goog.proto2.Descriptor} The descriptor.
    + */
    +goog.proto2.FieldDescriptor.prototype.getContainingType = function() {
    +  return this.parent_.getDescriptor();
    +};
    +
    +
    +/**
    + * Returns the name of the field that this descriptor represents.
    + * @return {string} The name.
    + */
    +goog.proto2.FieldDescriptor.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * Returns the default value of this field.
    + * @return {*} The default value.
    + */
    +goog.proto2.FieldDescriptor.prototype.getDefaultValue = function() {
    +  if (this.defaultValue_ === undefined) {
    +    // Set the default value based on a new instance of the native type.
    +    // This will be (0, false, "") for (number, boolean, string) and will
    +    // be a new instance of a group/message if the field is a message type.
    +    var nativeType = this.nativeType_;
    +    if (nativeType === Boolean) {
    +      this.defaultValue_ = false;
    +    } else if (nativeType === Number) {
    +      this.defaultValue_ = 0;
    +    } else if (nativeType === String) {
    +      if (this.deserializationConversionPermitted_) {
    +        // This field is a 64 bit integer represented as a string.
    +        this.defaultValue_ = '0';
    +      } else {
    +        this.defaultValue_ = '';
    +      }
    +    } else {
    +      return new nativeType;
    +    }
    +  }
    +
    +  return this.defaultValue_;
    +};
    +
    +
    +/**
    + * Returns the field type of the field described by this descriptor.
    + * @return {goog.proto2.FieldDescriptor.FieldType} The field type.
    + */
    +goog.proto2.FieldDescriptor.prototype.getFieldType = function() {
    +  return this.fieldType_;
    +};
    +
    +
    +/**
    + * Returns the native (i.e. ECMAScript) type of the field described by this
    + * descriptor.
    + *
    + * @return {Object} The native type.
    + */
    +goog.proto2.FieldDescriptor.prototype.getNativeType = function() {
    +  return this.nativeType_;
    +};
    +
    +
    +/**
    + * Returns true if simple conversions between numbers and strings are permitted
    + * during deserialization for this field.
    + *
    + * @return {boolean} Whether conversion is permitted.
    + */
    +goog.proto2.FieldDescriptor.prototype.deserializationConversionPermitted =
    +    function() {
    +  return this.deserializationConversionPermitted_;
    +};
    +
    +
    +/**
    + * Returns the descriptor of the message type of this field. Only valid
    + * for fields of type GROUP and MESSAGE.
    + *
    + * @return {!goog.proto2.Descriptor} The message descriptor.
    + */
    +goog.proto2.FieldDescriptor.prototype.getFieldMessageType = function() {
    +  return this.nativeType_.getDescriptor();
    +};
    +
    +
    +/**
    + * @return {boolean} True if the field stores composite data or repeated
    + *     composite data (message or group).
    + */
    +goog.proto2.FieldDescriptor.prototype.isCompositeType = function() {
    +  return this.fieldType_ == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
    +      this.fieldType_ == goog.proto2.FieldDescriptor.FieldType.GROUP;
    +};
    +
    +
    +/**
    + * Returns whether the field described by this descriptor is packed.
    + * @return {boolean} Whether the field is packed.
    + */
    +goog.proto2.FieldDescriptor.prototype.isPacked = function() {
    +  return this.isPacked_;
    +};
    +
    +
    +/**
    + * Returns whether the field described by this descriptor is repeating.
    + * @return {boolean} Whether the field is repeated.
    + */
    +goog.proto2.FieldDescriptor.prototype.isRepeated = function() {
    +  return this.isRepeated_;
    +};
    +
    +
    +/**
    + * Returns whether the field described by this descriptor is required.
    + * @return {boolean} Whether the field is required.
    + */
    +goog.proto2.FieldDescriptor.prototype.isRequired = function() {
    +  return this.isRequired_;
    +};
    +
    +
    +/**
    + * Returns whether the field described by this descriptor is optional.
    + * @return {boolean} Whether the field is optional.
    + */
    +goog.proto2.FieldDescriptor.prototype.isOptional = function() {
    +  return !this.isRepeated_ && !this.isRequired_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.html b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.html
    new file mode 100644
    index 0000000000..6e67ec7cc8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - fielddescriptor.js
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.FieldDescriptorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.js b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.js
    new file mode 100644
    index 0000000000..0bd3737a87
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/fielddescriptor_test.js
    @@ -0,0 +1,139 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.FieldDescriptorTest');
    +goog.setTestOnly('goog.proto2.FieldDescriptorTest');
    +
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.proto2.Message');
    +goog.require('goog.testing.jsunit');
    +
    +function testFieldDescriptorConstruction() {
    +  var messageType = {};
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor(messageType, 10, {
    +    name: 'test',
    +    repeated: true,
    +    packed: true,
    +    fieldType: goog.proto2.FieldDescriptor.FieldType.INT32,
    +    type: Number
    +  });
    +
    +  assertEquals(10, fieldDescriptor.getTag());
    +  assertEquals('test', fieldDescriptor.getName());
    +
    +  assertEquals(true, fieldDescriptor.isRepeated());
    +
    +  assertEquals(true, fieldDescriptor.isPacked());
    +
    +  assertEquals(goog.proto2.FieldDescriptor.FieldType.INT32,
    +      fieldDescriptor.getFieldType());
    +  assertEquals(Number, fieldDescriptor.getNativeType());
    +  assertEquals(0, fieldDescriptor.getDefaultValue());
    +}
    +
    +function testGetDefaultValueOfString() {
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor({}, 10, {
    +    name: 'test',
    +    fieldType: goog.proto2.FieldDescriptor.FieldType.STRING,
    +    type: String
    +  });
    +
    +  assertEquals('', fieldDescriptor.getDefaultValue());
    +}
    +
    +function testGetDefaultValueOfBool() {
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor({}, 10, {
    +    name: 'test',
    +    fieldType: goog.proto2.FieldDescriptor.FieldType.BOOL,
    +    type: Boolean
    +  });
    +
    +  assertEquals(false, fieldDescriptor.getDefaultValue());
    +}
    +
    +function testGetDefaultValueOfInt64() {
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor({}, 10, {
    +    name: 'test',
    +    fieldType: goog.proto2.FieldDescriptor.FieldType.INT64,
    +    type: String
    +  });
    +
    +  assertEquals('0', fieldDescriptor.getDefaultValue());
    +}
    +
    +function testRepeatedField() {
    +  var messageType = {};
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor(messageType, 10, {
    +    name: 'test',
    +    repeated: true,
    +    fieldType: 7,
    +    type: Number
    +  });
    +
    +  assertEquals(true, fieldDescriptor.isRepeated());
    +  assertEquals(false, fieldDescriptor.isRequired());
    +  assertEquals(false, fieldDescriptor.isOptional());
    +}
    +
    +function testRequiredField() {
    +  var messageType = {};
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor(messageType, 10, {
    +    name: 'test',
    +    required: true,
    +    fieldType: 7,
    +    type: Number
    +  });
    +
    +  assertEquals(false, fieldDescriptor.isRepeated());
    +  assertEquals(true, fieldDescriptor.isRequired());
    +  assertEquals(false, fieldDescriptor.isOptional());
    +}
    +
    +function testOptionalField() {
    +  var messageType = {};
    +  var fieldDescriptor = new goog.proto2.FieldDescriptor(messageType, 10, {
    +    name: 'test',
    +    fieldType: 7,
    +    type: Number
    +  });
    +
    +  assertEquals(false, fieldDescriptor.isRepeated());
    +  assertEquals(false, fieldDescriptor.isRequired());
    +  assertEquals(true, fieldDescriptor.isOptional());
    +}
    +
    +function testContaingType() {
    +  var MessageType = function() {
    +    MessageType.base(this, 'constructor');
    +  };
    +  goog.inherits(MessageType, goog.proto2.Message);
    +
    +  var descriptorObj = {
    +    0: {
    +      name: 'test_message',
    +      fullName: 'this.is.a.test_message'
    +    },
    +    10: {
    +      name: 'test',
    +      fieldType: 7,
    +      type: Number
    +    }
    +  };
    +  goog.proto2.Message.set$Metadata(MessageType, descriptorObj);
    +
    +  var descriptor = MessageType.getDescriptor();
    +  var fieldDescriptor = descriptor.getFields()[0];
    +  assertEquals('10', fieldDescriptor.getTag());
    +  assertEquals(descriptor, fieldDescriptor.getContainingType());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/lazydeserializer.js b/mapbox-gl/closure-library/closure/goog/proto2/lazydeserializer.js
    new file mode 100644
    index 0000000000..270e6ace02
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/lazydeserializer.js
    @@ -0,0 +1,70 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for all PB2 lazy deserializer. A lazy deserializer
    + *   is a serializer whose deserialization occurs on the fly as data is
    + *   requested. In order to use a lazy deserializer, the serialized form
    + *   of the data must be an object or array that can be indexed by the tag
    + *   number.
    + *
    + */
    +
    +goog.provide('goog.proto2.LazyDeserializer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.proto2.Message');
    +goog.require('goog.proto2.Serializer');
    +
    +
    +
    +/**
    + * Base class for all lazy deserializers.
    + *
    + * @constructor
    + * @extends {goog.proto2.Serializer}
    + */
    +goog.proto2.LazyDeserializer = function() {};
    +goog.inherits(goog.proto2.LazyDeserializer, goog.proto2.Serializer);
    +
    +
    +/** @override */
    +goog.proto2.LazyDeserializer.prototype.deserialize =
    +    function(descriptor, data) {
    +  var message = descriptor.createMessageInstance();
    +  message.initializeForLazyDeserializer(this, data);
    +  goog.asserts.assert(message instanceof goog.proto2.Message);
    +  return message;
    +};
    +
    +
    +/** @override */
    +goog.proto2.LazyDeserializer.prototype.deserializeTo = function(message, data) {
    +  throw new Error('Unimplemented');
    +};
    +
    +
    +/**
    + * Deserializes a message field from the expected format and places the
    + * data in the given message
    + *
    + * @param {goog.proto2.Message} message The message in which to
    + *     place the information.
    + * @param {goog.proto2.FieldDescriptor} field The field for which to set the
    + *     message value.
    + * @param {*} data The serialized data for the field.
    + *
    + * @return {*} The deserialized data or null for no value found.
    + */
    +goog.proto2.LazyDeserializer.prototype.deserializeField = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/message.js b/mapbox-gl/closure-library/closure/goog/proto2/message.js
    new file mode 100644
    index 0000000000..95d1751955
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/message.js
    @@ -0,0 +1,774 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer Message base class.
    + */
    +
    +goog.provide('goog.proto2.Message');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.proto2.Descriptor');
    +goog.require('goog.proto2.FieldDescriptor');
    +
    +
    +
    +/**
    + * Abstract base class for all Protocol Buffer 2 messages. It will be
    + * subclassed in the code generated by the Protocol Compiler. Any other
    + * subclasses are prohibited.
    + * @constructor
    + */
    +goog.proto2.Message = function() {
    +  /**
    +   * Stores the field values in this message. Keyed by the tag of the fields.
    +   * @type {*}
    +   * @private
    +   */
    +  this.values_ = {};
    +
    +  /**
    +   * Stores the field information (i.e. metadata) about this message.
    +   * @type {Object<number, !goog.proto2.FieldDescriptor>}
    +   * @private
    +   */
    +  this.fields_ = this.getDescriptor().getFieldsMap();
    +
    +  /**
    +   * The lazy deserializer for this message instance, if any.
    +   * @type {goog.proto2.LazyDeserializer}
    +   * @private
    +   */
    +  this.lazyDeserializer_ = null;
    +
    +  /**
    +   * A map of those fields deserialized, from tag number to their deserialized
    +   * value.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.deserializedFields_ = null;
    +};
    +
    +
    +/**
    + * An enumeration defining the possible field types.
    + * Should be a mirror of that defined in descriptor.h.
    + *
    + * TODO(user): Remove this alias.  The code generator generates code that
    + * references this enum, so it needs to exist until the code generator is
    + * changed.  The enum was moved to from Message to FieldDescriptor to avoid a
    + * dependency cycle.
    + *
    + * Use goog.proto2.FieldDescriptor.FieldType instead.
    + *
    + * @enum {number}
    + */
    +goog.proto2.Message.FieldType = {
    +  DOUBLE: 1,
    +  FLOAT: 2,
    +  INT64: 3,
    +  UINT64: 4,
    +  INT32: 5,
    +  FIXED64: 6,
    +  FIXED32: 7,
    +  BOOL: 8,
    +  STRING: 9,
    +  GROUP: 10,
    +  MESSAGE: 11,
    +  BYTES: 12,
    +  UINT32: 13,
    +  ENUM: 14,
    +  SFIXED32: 15,
    +  SFIXED64: 16,
    +  SINT32: 17,
    +  SINT64: 18
    +};
    +
    +
    +/**
    + * All instances of goog.proto2.Message should have a static descriptorObj_
    + * property. This is a JSON representation of a Descriptor. The real Descriptor
    + * will be deserialized lazily in the getDescriptor() method.
    + *
    + * This declaration is just here for documentation purposes.
    + * goog.proto2.Message does not have its own descriptor.
    + *
    + * TODO(user): Delete after components update for cl/76695317.
    + *
    + * @type {undefined}
    + * @private
    + */
    +goog.proto2.Message.descriptorObj_;
    +
    +
    +/**
    + * All instances of goog.proto2.Message should have a static descriptor_
    + * property. The Descriptor will be deserialized lazily in the getDescriptor()
    + * method.
    + *
    + * This declaration is just here for documentation purposes.
    + * goog.proto2.Message does not have its own descriptor.
    + *
    + * @type {undefined}
    + * @private
    + */
    +goog.proto2.Message.descriptor_;
    +
    +
    +/**
    + * Initializes the message with a lazy deserializer and its associated data.
    + * This method should be called by internal methods ONLY.
    + *
    + * @param {goog.proto2.LazyDeserializer} deserializer The lazy deserializer to
    + *   use to decode the data on the fly.
    + *
    + * @param {*} data The data to decode/deserialize.
    + */
    +goog.proto2.Message.prototype.initializeForLazyDeserializer = function(
    +    deserializer, data) {
    +
    +  this.lazyDeserializer_ = deserializer;
    +  this.values_ = data;
    +  this.deserializedFields_ = {};
    +};
    +
    +
    +/**
    + * Sets the value of an unknown field, by tag.
    + *
    + * @param {number} tag The tag of an unknown field (must be >= 1).
    + * @param {*} value The value for that unknown field.
    + */
    +goog.proto2.Message.prototype.setUnknown = function(tag, value) {
    +  goog.asserts.assert(!this.fields_[tag],
    +      'Field is not unknown in this message');
    +  goog.asserts.assert(tag >= 1, 'Tag is not valid');
    +  goog.asserts.assert(value !== null, 'Value cannot be null');
    +
    +  this.values_[tag] = value;
    +  if (this.deserializedFields_) {
    +    delete this.deserializedFields_[tag];
    +  }
    +};
    +
    +
    +/**
    + * Iterates over all the unknown fields in the message.
    + *
    + * @param {function(number, *)} callback A callback method
    + *     which gets invoked for each unknown field.
    + * @param {Object=} opt_scope The scope under which to execute the callback.
    + *     If not given, the current message will be used.
    + */
    +goog.proto2.Message.prototype.forEachUnknown = function(callback, opt_scope) {
    +  var scope = opt_scope || this;
    +  for (var key in this.values_) {
    +    var keyNum = Number(key);
    +    if (!this.fields_[keyNum]) {
    +      callback.call(scope, keyNum, this.values_[key]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the descriptor which describes the current message.
    + *
    + * This only works if we assume people never subclass protobufs.
    + *
    + * TODO(user): Replace with goog.abstractMethod after components update
    + *     with cl/76695317.
    + *
    + * @return {!goog.proto2.Descriptor} The descriptor.
    + */
    +goog.proto2.Message.prototype.getDescriptor = function() {
    +  // NOTE(nicksantos): These sorts of indirect references to descriptor
    +  // through this.constructor are fragile. See the comments
    +  // in set$Metadata for more info.
    +  var Ctor = this.constructor;
    +  return Ctor.descriptor_ ||
    +      (Ctor.descriptor_ = goog.proto2.Message.createDescriptor(
    +          Ctor, Ctor.descriptorObj_));
    +};
    +
    +
    +/**
    + * Returns whether there is a value stored at the field specified by the
    + * given field descriptor.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to check
    + *     if there is a value.
    + *
    + * @return {boolean} True if a value was found.
    + */
    +goog.proto2.Message.prototype.has = function(field) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  return this.has$Value(field.getTag());
    +};
    +
    +
    +/**
    + * Returns the array of values found for the given repeated field.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to
    + *     return the values.
    + *
    + * @return {!Array<?>} The values found.
    + */
    +goog.proto2.Message.prototype.arrayOf = function(field) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  return this.array$Values(field.getTag());
    +};
    +
    +
    +/**
    + * Returns the number of values stored in the given field.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to count
    + *     the number of values.
    + *
    + * @return {number} The count of the values in the given field.
    + */
    +goog.proto2.Message.prototype.countOf = function(field) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  return this.count$Values(field.getTag());
    +};
    +
    +
    +/**
    + * Returns the value stored at the field specified by the
    + * given field descriptor.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to get the
    + *     value.
    + * @param {number=} opt_index If the field is repeated, the index to use when
    + *     looking up the value.
    + *
    + * @return {*} The value found or null if none.
    + */
    +goog.proto2.Message.prototype.get = function(field, opt_index) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  return this.get$Value(field.getTag(), opt_index);
    +};
    +
    +
    +/**
    + * Returns the value stored at the field specified by the
    + * given field descriptor or the default value if none exists.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to get the
    + *     value.
    + * @param {number=} opt_index If the field is repeated, the index to use when
    + *     looking up the value.
    + *
    + * @return {*} The value found or the default if none.
    + */
    +goog.proto2.Message.prototype.getOrDefault = function(field, opt_index) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  return this.get$ValueOrDefault(field.getTag(), opt_index);
    +};
    +
    +
    +/**
    + * Stores the given value to the field specified by the
    + * given field descriptor. Note that the field must not be repeated.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field for which to set
    + *     the value.
    + * @param {*} value The new value for the field.
    + */
    +goog.proto2.Message.prototype.set = function(field, value) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  this.set$Value(field.getTag(), value);
    +};
    +
    +
    +/**
    + * Adds the given value to the field specified by the
    + * given field descriptor. Note that the field must be repeated.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field in which to add the
    + *     the value.
    + * @param {*} value The new value to add to the field.
    + */
    +goog.proto2.Message.prototype.add = function(field, value) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  this.add$Value(field.getTag(), value);
    +};
    +
    +
    +/**
    + * Clears the field specified.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field to clear.
    + */
    +goog.proto2.Message.prototype.clear = function(field) {
    +  goog.asserts.assert(
    +      field.getContainingType() == this.getDescriptor(),
    +      'The current message does not contain the given field');
    +
    +  this.clear$Field(field.getTag());
    +};
    +
    +
    +/**
    + * Compares this message with another one ignoring the unknown fields.
    + * @param {*} other The other message.
    + * @return {boolean} Whether they are equal. Returns false if the {@code other}
    + *     argument is a different type of message or not a message.
    + */
    +goog.proto2.Message.prototype.equals = function(other) {
    +  if (!other || this.constructor != other.constructor) {
    +    return false;
    +  }
    +
    +  var fields = this.getDescriptor().getFields();
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +    var tag = field.getTag();
    +    if (this.has$Value(tag) != other.has$Value(tag)) {
    +      return false;
    +    }
    +
    +    if (this.has$Value(tag)) {
    +      var isComposite = field.isCompositeType();
    +
    +      var fieldsEqual = function(value1, value2) {
    +        return isComposite ? value1.equals(value2) : value1 == value2;
    +      };
    +
    +      var thisValue = this.getValueForTag_(tag);
    +      var otherValue = other.getValueForTag_(tag);
    +
    +      if (field.isRepeated()) {
    +        // In this case thisValue and otherValue are arrays.
    +        if (thisValue.length != otherValue.length) {
    +          return false;
    +        }
    +        for (var j = 0; j < thisValue.length; j++) {
    +          if (!fieldsEqual(thisValue[j], otherValue[j])) {
    +            return false;
    +          }
    +        }
    +      } else if (!fieldsEqual(thisValue, otherValue)) {
    +        return false;
    +      }
    +    }
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Recursively copies the known fields from the given message to this message.
    + * Removes the fields which are not present in the source message.
    + * @param {!goog.proto2.Message} message The source message.
    + */
    +goog.proto2.Message.prototype.copyFrom = function(message) {
    +  goog.asserts.assert(this.constructor == message.constructor,
    +      'The source message must have the same type.');
    +
    +  if (this != message) {
    +    this.values_ = {};
    +    if (this.deserializedFields_) {
    +      this.deserializedFields_ = {};
    +    }
    +    this.mergeFrom(message);
    +  }
    +};
    +
    +
    +/**
    + * Merges the given message into this message.
    + *
    + * Singular fields will be overwritten, except for embedded messages which will
    + * be merged. Repeated fields will be concatenated.
    + * @param {!goog.proto2.Message} message The source message.
    + */
    +goog.proto2.Message.prototype.mergeFrom = function(message) {
    +  goog.asserts.assert(this.constructor == message.constructor,
    +      'The source message must have the same type.');
    +  var fields = this.getDescriptor().getFields();
    +
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +    var tag = field.getTag();
    +    if (message.has$Value(tag)) {
    +      if (this.deserializedFields_) {
    +        delete this.deserializedFields_[field.getTag()];
    +      }
    +
    +      var isComposite = field.isCompositeType();
    +      if (field.isRepeated()) {
    +        var values = message.array$Values(tag);
    +        for (var j = 0; j < values.length; j++) {
    +          this.add$Value(tag, isComposite ? values[j].clone() : values[j]);
    +        }
    +      } else {
    +        var value = message.getValueForTag_(tag);
    +        if (isComposite) {
    +          var child = this.getValueForTag_(tag);
    +          if (child) {
    +            child.mergeFrom(value);
    +          } else {
    +            this.set$Value(tag, value.clone());
    +          }
    +        } else {
    +          this.set$Value(tag, value);
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {!goog.proto2.Message} Recursive clone of the message only including
    + *     the known fields.
    + */
    +goog.proto2.Message.prototype.clone = function() {
    +  /** @type {!goog.proto2.Message} */
    +  var clone = new this.constructor;
    +  clone.copyFrom(this);
    +  return clone;
    +};
    +
    +
    +/**
    + * Fills in the protocol buffer with default values. Any fields that are
    + * already set will not be overridden.
    + * @param {boolean} simpleFieldsToo If true, all fields will be initialized;
    + *     if false, only the nested messages and groups.
    + */
    +goog.proto2.Message.prototype.initDefaults = function(simpleFieldsToo) {
    +  var fields = this.getDescriptor().getFields();
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +    var tag = field.getTag();
    +    var isComposite = field.isCompositeType();
    +
    +    // Initialize missing fields.
    +    if (!this.has$Value(tag) && !field.isRepeated()) {
    +      if (isComposite) {
    +        this.values_[tag] = new /** @type {Function} */ (field.getNativeType());
    +      } else if (simpleFieldsToo) {
    +        this.values_[tag] = field.getDefaultValue();
    +      }
    +    }
    +
    +    // Fill in the existing composite fields recursively.
    +    if (isComposite) {
    +      if (field.isRepeated()) {
    +        var values = this.array$Values(tag);
    +        for (var j = 0; j < values.length; j++) {
    +          values[j].initDefaults(simpleFieldsToo);
    +        }
    +      } else {
    +        this.get$Value(tag).initDefaults(simpleFieldsToo);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the whether or not the field indicated by the given tag
    + * has a value.
    + *
    + * GENERATED CODE USE ONLY. Basis of the has{Field} methods.
    + *
    + * @param {number} tag The tag.
    + *
    + * @return {boolean} Whether the message has a value for the field.
    + */
    +goog.proto2.Message.prototype.has$Value = function(tag) {
    +  return this.values_[tag] != null;
    +};
    +
    +
    +/**
    + * Returns the value for the given tag number. If a lazy deserializer is
    + * instantiated, lazily deserializes the field if required before returning the
    + * value.
    + *
    + * @param {number} tag The tag number.
    + * @return {*} The corresponding value, if any.
    + * @private
    + */
    +goog.proto2.Message.prototype.getValueForTag_ = function(tag) {
    +  // Retrieve the current value, which may still be serialized.
    +  var value = this.values_[tag];
    +  if (!goog.isDefAndNotNull(value)) {
    +    return null;
    +  }
    +
    +  // If we have a lazy deserializer, then ensure that the field is
    +  // properly deserialized.
    +  if (this.lazyDeserializer_) {
    +    // If the tag is not deserialized, then we must do so now. Deserialize
    +    // the field's value via the deserializer.
    +    if (!(tag in this.deserializedFields_)) {
    +      var deserializedValue = this.lazyDeserializer_.deserializeField(
    +          this, this.fields_[tag], value);
    +      this.deserializedFields_[tag] = deserializedValue;
    +      return deserializedValue;
    +    }
    +
    +    return this.deserializedFields_[tag];
    +  }
    +
    +  // Otherwise, just return the value.
    +  return value;
    +};
    +
    +
    +/**
    + * Gets the value at the field indicated by the given tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the get{Field} methods.
    + *
    + * @param {number} tag The field's tag index.
    + * @param {number=} opt_index If the field is a repeated field, the index
    + *     at which to get the value.
    + *
    + * @return {*} The value found or null for none.
    + * @protected
    + */
    +goog.proto2.Message.prototype.get$Value = function(tag, opt_index) {
    +  var value = this.getValueForTag_(tag);
    +
    +  if (this.fields_[tag].isRepeated()) {
    +    var index = opt_index || 0;
    +    goog.asserts.assert(
    +        index >= 0 && index < value.length,
    +        'Given index %s is out of bounds.  Repeated field length: %s',
    +        index, value.length);
    +    return value[index];
    +  }
    +
    +  return value;
    +};
    +
    +
    +/**
    + * Gets the value at the field indicated by the given tag or the default value
    + * if none.
    + *
    + * GENERATED CODE USE ONLY. Basis of the get{Field} methods.
    + *
    + * @param {number} tag The field's tag index.
    + * @param {number=} opt_index If the field is a repeated field, the index
    + *     at which to get the value.
    + *
    + * @return {*} The value found or the default value if none set.
    + * @protected
    + */
    +goog.proto2.Message.prototype.get$ValueOrDefault = function(tag, opt_index) {
    +  if (!this.has$Value(tag)) {
    +    // Return the default value.
    +    var field = this.fields_[tag];
    +    return field.getDefaultValue();
    +  }
    +
    +  return this.get$Value(tag, opt_index);
    +};
    +
    +
    +/**
    + * Gets the values at the field indicated by the given tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the {field}Array methods.
    + *
    + * @param {number} tag The field's tag index.
    + *
    + * @return {!Array<*>} The values found. If none, returns an empty array.
    + * @protected
    + */
    +goog.proto2.Message.prototype.array$Values = function(tag) {
    +  var value = this.getValueForTag_(tag);
    +  return /** @type {Array<*>} */ (value) || [];
    +};
    +
    +
    +/**
    + * Returns the number of values stored in the field by the given tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the {field}Count methods.
    + *
    + * @param {number} tag The tag.
    + *
    + * @return {number} The number of values.
    + * @protected
    + */
    +goog.proto2.Message.prototype.count$Values = function(tag) {
    +  var field = this.fields_[tag];
    +  if (field.isRepeated()) {
    +    return this.has$Value(tag) ? this.values_[tag].length : 0;
    +  } else {
    +    return this.has$Value(tag) ? 1 : 0;
    +  }
    +};
    +
    +
    +/**
    + * Sets the value of the *non-repeating* field indicated by the given tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the set{Field} methods.
    + *
    + * @param {number} tag The field's tag index.
    + * @param {*} value The field's value.
    + * @protected
    + */
    +goog.proto2.Message.prototype.set$Value = function(tag, value) {
    +  if (goog.asserts.ENABLE_ASSERTS) {
    +    var field = this.fields_[tag];
    +    this.checkFieldType_(field, value);
    +  }
    +
    +  this.values_[tag] = value;
    +  if (this.deserializedFields_) {
    +    this.deserializedFields_[tag] = value;
    +  }
    +};
    +
    +
    +/**
    + * Adds the value to the *repeating* field indicated by the given tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the add{Field} methods.
    + *
    + * @param {number} tag The field's tag index.
    + * @param {*} value The value to add.
    + * @protected
    + */
    +goog.proto2.Message.prototype.add$Value = function(tag, value) {
    +  if (goog.asserts.ENABLE_ASSERTS) {
    +    var field = this.fields_[tag];
    +    this.checkFieldType_(field, value);
    +  }
    +
    +  if (!this.values_[tag]) {
    +    this.values_[tag] = [];
    +  }
    +
    +  this.values_[tag].push(value);
    +  if (this.deserializedFields_) {
    +    delete this.deserializedFields_[tag];
    +  }
    +};
    +
    +
    +/**
    + * Ensures that the value being assigned to the given field
    + * is valid.
    + *
    + * @param {!goog.proto2.FieldDescriptor} field The field being assigned.
    + * @param {*} value The value being assigned.
    + * @private
    + */
    +goog.proto2.Message.prototype.checkFieldType_ = function(field, value) {
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) {
    +    goog.asserts.assertNumber(value);
    +  } else {
    +    goog.asserts.assert(value.constructor == field.getNativeType());
    +  }
    +};
    +
    +
    +/**
    + * Clears the field specified by tag.
    + *
    + * GENERATED CODE USE ONLY. Basis of the clear{Field} methods.
    + *
    + * @param {number} tag The tag of the field to clear.
    + * @protected
    + */
    +goog.proto2.Message.prototype.clear$Field = function(tag) {
    +  delete this.values_[tag];
    +  if (this.deserializedFields_) {
    +    delete this.deserializedFields_[tag];
    +  }
    +};
    +
    +
    +/**
    + * Creates the metadata descriptor representing the definition of this message.
    + *
    + * @param {function(new:goog.proto2.Message)} messageType Constructor for the
    + *     message type to which this metadata applies.
    + * @param {!Object} metadataObj The object containing the metadata.
    + * @return {!goog.proto2.Descriptor} The new descriptor.
    + */
    +goog.proto2.Message.createDescriptor = function(messageType, metadataObj) {
    +  var fields = [];
    +  var descriptorInfo = metadataObj[0];
    +
    +  for (var key in metadataObj) {
    +    if (key != 0) {
    +      // Create the field descriptor.
    +      fields.push(
    +          new goog.proto2.FieldDescriptor(messageType, key, metadataObj[key]));
    +    }
    +  }
    +
    +  return new goog.proto2.Descriptor(messageType, descriptorInfo, fields);
    +};
    +
    +
    +/**
    + * Sets the metadata that represents the definition of this message.
    + *
    + * GENERATED CODE USE ONLY. Called when constructing message classes.
    + *
    + * TODO(user): Delete after components update with cl/76695317.
    + *
    + * @param {!Function} messageType Constructor for the
    + *     message type to which this metadata applies.
    + * @param {Object} metadataObj The object containing the metadata.
    + */
    +goog.proto2.Message.set$Metadata = function(messageType, metadataObj) {
    +  // NOTE(nicksantos): JSCompiler's type-based optimizations really do not
    +  // like indirectly defined methods (both prototype methods and
    +  // static methods). This is very fragile in compiled code. I think it only
    +  // really works by accident, and is highly likely to break in the future.
    +  messageType.descriptorObj_ = metadataObj;
    +  messageType.getDescriptor = function() {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    return messageType.descriptor_ ||
    +        (new messageType()).getDescriptor();
    +  };
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/message_test.html b/mapbox-gl/closure-library/closure/goog/proto2/message_test.html
    new file mode 100644
    index 0000000000..1b03ea5f1d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/message_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - message.js
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.MessageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/message_test.js b/mapbox-gl/closure-library/closure/goog/proto2/message_test.js
    new file mode 100644
    index 0000000000..9a8a56fb24
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/message_test.js
    @@ -0,0 +1,466 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.MessageTest');
    +goog.setTestOnly('goog.proto2.MessageTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('proto2.TestAllTypes');
    +goog.require('proto2.TestAllTypes.NestedEnum');
    +goog.require('proto2.TestAllTypes.NestedMessage');
    +goog.require('proto2.TestAllTypes.OptionalGroup');
    +goog.require('proto2.TestAllTypes.RepeatedGroup');
    +
    +function testEqualsWithEmptyMessages() {
    +  var message1 = new proto2.TestAllTypes();
    +  assertTrue('same message object', message1.equals(message1));
    +  assertFalse('comparison with null', message1.equals(null));
    +  assertFalse('comparison with undefined', message1.equals(undefined));
    +
    +  var message2 = new proto2.TestAllTypes();
    +  assertTrue('two empty message objects', message1.equals(message2));
    +
    +  var message3 = new proto2.TestAllTypes.NestedMessage();
    +  assertFalse('different message types', message3.equals(message1));
    +}
    +
    +function testEqualsWithSingleInt32Field() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +
    +  message1.setOptionalInt32(1);
    +  assertFalse('message1 has an extra int32 field', message1.equals(message2));
    +
    +  message2.setOptionalInt32(1);
    +  assertTrue('same int32 field in both messages', message1.equals(message2));
    +
    +  message2.setOptionalInt32(2);
    +  assertFalse('different int32 field', message1.equals(message2));
    +
    +  message1.clearOptionalInt32();
    +  assertFalse('message2 has an extra int32 field', message1.equals(message2));
    +}
    +
    +function testEqualsWithRepeatedInt32Fields() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +
    +  message1.addRepeatedInt32(0);
    +  message2.addRepeatedInt32(0);
    +  assertTrue('equal repeated int32 field', message1.equals(message2));
    +
    +  message1.addRepeatedInt32(1);
    +  assertFalse('message1 has more items', message1.equals(message2));
    +
    +  message2.addRepeatedInt32(1);
    +  message2.addRepeatedInt32(1);
    +  assertFalse('message2 has more items', message1.equals(message2));
    +
    +  message1.addRepeatedInt32(2);
    +  assertFalse('different int32 items', message1.equals(message2));
    +}
    +
    +function testEqualsWithDefaultValue() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +  message1.setOptionalInt64('1');
    +
    +  assertEquals('message1.getOptionalInt64OrDefault should return 1',
    +      '1', message1.getOptionalInt64OrDefault());
    +  assertEquals('message2.getOptionalInt64OrDefault should return 1 too',
    +      '1', message2.getOptionalInt64OrDefault());
    +  assertTrue('message1.hasOptionalInt64() should be true',
    +      message1.hasOptionalInt64());
    +  assertFalse('message2.hasOptionalInt64() should be false',
    +      message2.hasOptionalInt64());
    +  assertFalse('as a result they are not equal', message1.equals(message2));
    +}
    +
    +function testEqualsWithOptionalGroup() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +  var group1 = new proto2.TestAllTypes.OptionalGroup();
    +  var group2 = new proto2.TestAllTypes.OptionalGroup();
    +
    +  message1.setOptionalgroup(group1);
    +  assertFalse('only message1 has OptionalGroup field',
    +      message1.equals(message2));
    +
    +  message2.setOptionalgroup(group2);
    +  assertTrue('both messages have OptionalGroup field',
    +      message1.equals(message2));
    +
    +  group1.setA(0);
    +  group2.setA(1);
    +  assertFalse('different value in the optional group',
    +      message1.equals(message2));
    +
    +  message1.clearOptionalgroup();
    +  assertFalse('only message2 has OptionalGroup field',
    +      message1.equals(message2));
    +}
    +
    +function testEqualsWithRepeatedGroup() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +  var group1 = new proto2.TestAllTypes.RepeatedGroup();
    +  var group2 = new proto2.TestAllTypes.RepeatedGroup();
    +
    +  message1.addRepeatedgroup(group1);
    +  assertFalse('message1 has more RepeatedGroups',
    +      message1.equals(message2));
    +
    +  message2.addRepeatedgroup(group2);
    +  assertTrue('both messages have one RepeatedGroup',
    +      message1.equals(message2));
    +
    +  group1.addA(1);
    +  assertFalse('message1 has more int32s in RepeatedGroup',
    +      message1.equals(message2));
    +
    +  group2.addA(1);
    +  assertTrue('both messages have one int32 in RepeatedGroup',
    +      message1.equals(message2));
    +
    +  group1.addA(1);
    +  group2.addA(2);
    +  assertFalse('the messages have different int32s in RepeatedGroup',
    +      message1.equals(message2));
    +}
    +
    +function testEqualsWithNestedMessage() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +  var nested1 = new proto2.TestAllTypes.NestedMessage();
    +  var nested2 = new proto2.TestAllTypes.NestedMessage();
    +
    +  message1.setOptionalNestedMessage(nested1);
    +  assertFalse('only message1 has nested message', message1.equals(message2));
    +
    +  message2.setOptionalNestedMessage(nested2);
    +  assertTrue('both messages have nested message', message1.equals(message2));
    +
    +  nested1.setB(1);
    +  assertFalse('different int32 in the nested messages',
    +      message1.equals(message2));
    +
    +  message1.clearOptionalNestedMessage();
    +  assertFalse('only message2 has nested message', message1.equals(message2));
    +}
    +
    +function testEqualsWithNestedEnum() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +
    +  message1.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  assertFalse('only message1 has nested enum', message1.equals(message2));
    +
    +  message2.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  assertTrue('both messages have nested enum', message1.equals(message2));
    +
    +  message2.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.BAR);
    +  assertFalse('different enum value', message1.equals(message2));
    +
    +  message1.clearOptionalNestedEnum();
    +  assertFalse('only message2 has nested enum', message1.equals(message2));
    +}
    +
    +function testEqualsWithUnknownFields() {
    +  var message1 = new proto2.TestAllTypes();
    +  var message2 = new proto2.TestAllTypes();
    +  message1.setUnknown(999, 'foo');
    +  message1.setUnknown(999, 'bar');
    +  assertTrue('unknown fields are ignored', message1.equals(message2));
    +}
    +
    +function testCloneEmptyMessage() {
    +  var message = new proto2.TestAllTypes();
    +  var clone = message.clone();
    +  assertObjectEquals('cloned empty message', message, clone);
    +}
    +
    +function testCloneMessageWithSeveralFields() {
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalInt32(1);
    +  message.addRepeatedInt32(2);
    +  var optionalGroup = new proto2.TestAllTypes.OptionalGroup();
    +  optionalGroup.setA(3);
    +  message.setOptionalgroup(optionalGroup);
    +  var repeatedGroup = new proto2.TestAllTypes.RepeatedGroup();
    +  repeatedGroup.addA(4);
    +  message.addRepeatedgroup(repeatedGroup);
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(5);
    +  message.setOptionalNestedMessage(nestedMessage);
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  message.setUnknown(999, 'foo');
    +
    +  var clone = message.clone();
    +  assertNotEquals('different OptionalGroup instance',
    +      message.getOptionalgroup(), clone.getOptionalgroup());
    +  assertNotEquals('different RepeatedGroup array instance',
    +      message.repeatedgroupArray(), clone.repeatedgroupArray());
    +  assertNotEquals('different RepeatedGroup array item instance',
    +      message.getRepeatedgroup(0), clone.getRepeatedgroup(0));
    +  assertNotEquals('different NestedMessage instance',
    +      message.getOptionalNestedMessage(), clone.getOptionalNestedMessage());
    +}
    +
    +function testCloneWithUnknownFields() {
    +  var message = new proto2.TestAllTypes();
    +  message.setUnknown(999, 'foo');
    +
    +  var clone = message.clone();
    +  assertTrue('clone.equals(message) returns true', clone.equals(message));
    +  clone.forEachUnknown(function(tag, value) {
    +    fail('the unknown fields should not have been cloned');
    +  });
    +
    +  clone.setUnknown(999, 'foo');
    +  assertObjectEquals('the original and the cloned message are equal except ' +
    +      'for the unknown fields', message, clone);
    +}
    +
    +function testCopyFromSameMessage() {
    +  var source = new proto2.TestAllTypes();
    +  source.setOptionalInt32(32);
    +  source.copyFrom(source);
    +  assertEquals(32, source.getOptionalInt32());
    +}
    +
    +function testCopyFromFlatMessage() {
    +  // Recursive copying is implicitly tested in the testClone... methods.
    +
    +  var source = new proto2.TestAllTypes();
    +  source.setOptionalInt32(32);
    +  source.setOptionalInt64('64');
    +  source.addRepeatedInt32(32);
    +
    +  var target = new proto2.TestAllTypes();
    +  target.setOptionalInt32(33);
    +  target.setOptionalUint32(33);
    +  target.addRepeatedInt32(33);
    +
    +  target.copyFrom(source);
    +  assertObjectEquals('source and target are equal after copyFrom', source,
    +      target);
    +
    +  target.copyFrom(source);
    +  assertObjectEquals('second copyFrom call has no effect', source, target);
    +
    +  source.setUnknown(999, 'foo');
    +  target.setUnknown(999, 'bar');
    +  target.copyFrom(source);
    +  assertThrows('unknown fields are not copied',
    +      goog.partial(assertObjectEquals, source, target));
    +}
    +
    +function testMergeFromEmptyMessage() {
    +  var source = new proto2.TestAllTypes();
    +  source.setOptionalInt32(32);
    +  source.setOptionalInt64('64');
    +  var nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(66);
    +  source.setOptionalNestedMessage(nested);
    +
    +  var target = new proto2.TestAllTypes();
    +  target.mergeFrom(source);
    +  assertObjectEquals('source and target are equal after mergeFrom', source,
    +      target);
    +}
    +
    +function testMergeFromFlatMessage() {
    +  var source = new proto2.TestAllTypes();
    +  source.setOptionalInt32(32);
    +  source.setOptionalString('foo');
    +  source.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  var target = new proto2.TestAllTypes();
    +  target.setOptionalInt64('64');
    +  target.setOptionalString('bar');
    +  target.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.BAR);
    +
    +  var expected = new proto2.TestAllTypes();
    +  expected.setOptionalInt32(32);
    +  expected.setOptionalInt64('64');
    +  expected.setOptionalString('foo');
    +  expected.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  target.mergeFrom(source);
    +  assertObjectEquals('expected and target are equal after mergeFrom', expected,
    +      target);
    +}
    +
    +function testMergeFromNestedMessage() {
    +  var source = new proto2.TestAllTypes();
    +  var nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(66);
    +  source.setOptionalNestedMessage(nested);
    +
    +  var target = new proto2.TestAllTypes();
    +  nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setC(77);
    +  target.setOptionalNestedMessage(nested);
    +
    +  var expected = new proto2.TestAllTypes();
    +  nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(66);
    +  nested.setC(77);
    +  expected.setOptionalNestedMessage(nested);
    +
    +  target.mergeFrom(source);
    +  assertObjectEquals('expected and target are equal after mergeFrom', expected,
    +      target);
    +}
    +
    +function testMergeFromRepeatedMessage() {
    +  var source = new proto2.TestAllTypes();
    +  source.addRepeatedInt32(2);
    +  source.addRepeatedInt32(3);
    +
    +  var target = new proto2.TestAllTypes();
    +  target.addRepeatedInt32(1);
    +
    +  target.mergeFrom(source);
    +  assertArrayEquals('repeated_int32 array has elements from both messages',
    +      [1, 2, 3], target.repeatedInt32Array());
    +}
    +
    +function testInitDefaultsWithEmptyMessage() {
    +  var message = new proto2.TestAllTypes();
    +  message.initDefaults(false);
    +
    +  assertFalse('int32 field is not set', message.hasOptionalInt32());
    +  assertFalse('int64 [default=1] field is not set', message.hasOptionalInt64());
    +  assertTrue('optional group field is set', message.hasOptionalgroup());
    +  assertFalse('int32 inside the group is not set',
    +      message.getOptionalgroup().hasA());
    +  assertObjectEquals('value of the optional group',
    +      new proto2.TestAllTypes.OptionalGroup(), message.getOptionalgroup());
    +  assertTrue('nested message is set', message.hasOptionalNestedMessage());
    +  assertObjectEquals('value of the nested message',
    +      new proto2.TestAllTypes.NestedMessage(),
    +      message.getOptionalNestedMessage());
    +  assertFalse('nested enum is not set', message.hasOptionalNestedEnum());
    +  assertFalse('repeated int32 is not set', message.hasRepeatedInt32());
    +  assertFalse('repeated nested message is not set',
    +      message.hasRepeatedNestedMessage());
    +
    +  message = new proto2.TestAllTypes();
    +  message.initDefaults(true);
    +
    +  assertTrue('int32 field is set', message.hasOptionalInt32());
    +  assertEquals('value of the int32 field', 0, message.getOptionalInt32());
    +  assertTrue('int64 [default=1] field is set', message.hasOptionalInt64());
    +  assertEquals('value of the int64 field', '1', message.getOptionalInt64());
    +  assertTrue('int32 inside nested message is set',
    +      message.getOptionalNestedMessage().hasB());
    +  assertEquals('value of the int32 field inside the nested message', 0,
    +      message.getOptionalNestedMessage().getB());
    +}
    +
    +function testInitDefaultsWithNonEmptyMessage() {
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalInt32(32);
    +  message.setOptionalInt64('64');
    +  message.setOptionalgroup(new proto2.TestAllTypes.OptionalGroup());
    +  var nested1 = new proto2.TestAllTypes.NestedMessage();
    +  nested1.setB(66);
    +  message.setOptionalNestedMessage(nested1);
    +  var nested2 = new proto2.TestAllTypes.NestedMessage();
    +  message.addRepeatedNestedMessage(nested2);
    +  var nested3 = new proto2.TestAllTypes.NestedMessage();
    +  nested3.setB(66);
    +  message.addRepeatedNestedMessage(nested3);
    +
    +  message.initDefaults(true);
    +  assertEquals('int32 field is unchanged', 32, message.getOptionalInt32());
    +  assertEquals('int64 [default=1] field is unchanged', '64',
    +      message.getOptionalInt64());
    +  assertTrue('bool field is initialized', message.hasOptionalBool());
    +  assertFalse('value of the bool field', message.getOptionalBool());
    +  assertTrue('int32 inside the optional group is initialized',
    +      message.getOptionalgroup().hasA());
    +  assertEquals('value of the int32 inside the optional group', 0,
    +      message.getOptionalgroup().getA());
    +  assertEquals('int32 inside nested message is unchanged', 66,
    +      message.getOptionalNestedMessage().getB());
    +  assertTrue('int32 at index 0 of the repeated nested message is initialized',
    +      message.getRepeatedNestedMessage(0).hasB());
    +  assertEquals('value of int32 at index 0 of the repeated nested message', 0,
    +      message.getRepeatedNestedMessage(0).getB());
    +  assertEquals('int32 at index 1 of the repeated nested message is unchanged',
    +      66, message.getRepeatedNestedMessage(1).getB());
    +}
    +
    +function testInitDefaultsTwice() {
    +  var message = new proto2.TestAllTypes();
    +  message.initDefaults(false);
    +  var clone = message.clone();
    +  clone.initDefaults(false);
    +  assertObjectEquals('second call of initDefaults(false) has no effect',
    +      message, clone);
    +
    +  message = new proto2.TestAllTypes();
    +  message.initDefaults(true);
    +  clone = message.clone();
    +  clone.initDefaults(true);
    +  assertObjectEquals('second call of initDefaults(true) has no effect',
    +      message, clone);
    +}
    +
    +function testInitDefaultsThenClone() {
    +  var message = new proto2.TestAllTypes();
    +  message.initDefaults(true);
    +  assertObjectEquals('message is cloned properly', message, message.clone());
    +}
    +
    +function testClassGetDescriptorEqualToInstanceGetDescriptor() {
    +  var classDescriptor = proto2.TestAllTypes.getDescriptor();
    +  var instanceDescriptor = new proto2.TestAllTypes().getDescriptor();
    +  assertEquals(classDescriptor, instanceDescriptor);
    +}
    +
    +function testGetAfterSetWithLazyDeserializer() {
    +  // Test makes sure that the lazy deserializer for a field is not
    +  // erroneously called when get$Value is called after set$Value.
    +  var message = new proto2.TestAllTypes();
    +
    +  var fakeDeserializer = {}; // stub with no methods defined; fails hard
    +  message.initializeForLazyDeserializer(fakeDeserializer, {} /* data */);
    +  message.setOptionalBool(true);
    +  assertEquals(true, message.getOptionalBool());
    +}
    +
    +function testHasOnLazyDeserializer() {
    +  // Test that null values for fields are treated as absent by the lazy
    +  // deserializer.
    +  var message = new proto2.TestAllTypes();
    +
    +  var fakeDeserializer = {}; // stub with no methods defined; fails hard
    +  message.initializeForLazyDeserializer(fakeDeserializer,
    +      {13: false} /* data */);
    +  assertEquals(true, message.hasOptionalBool());
    +}
    +
    +function testHasOnLazyDeserializerWithNulls() {
    +  // Test that null values for fields are treated as absent by the lazy
    +  // deserializer.
    +  var message = new proto2.TestAllTypes();
    +
    +  var fakeDeserializer = {}; // stub with no methods defined; fails hard
    +  message.initializeForLazyDeserializer(fakeDeserializer,
    +      {13: null} /* data */);
    +  assertEquals(false, message.hasOptionalBool());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/objectserializer.js b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer.js
    new file mode 100644
    index 0000000000..d7b1b24102
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer.js
    @@ -0,0 +1,176 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer 2 Serializer which serializes messages
    + *  into anonymous, simplified JSON objects.
    + *
    + */
    +
    +goog.provide('goog.proto2.ObjectSerializer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.proto2.Serializer');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * ObjectSerializer, a serializer which turns Messages into simplified
    + * ECMAScript objects.
    + *
    + * @param {goog.proto2.ObjectSerializer.KeyOption=} opt_keyOption If specified,
    + *     which key option to use when serializing/deserializing.
    + * @constructor
    + * @extends {goog.proto2.Serializer}
    + */
    +goog.proto2.ObjectSerializer = function(opt_keyOption) {
    +  this.keyOption_ = opt_keyOption;
    +};
    +goog.inherits(goog.proto2.ObjectSerializer, goog.proto2.Serializer);
    +
    +
    +/**
    + * An enumeration of the options for how to emit the keys in
    + * the generated simplified object.
    + *
    + * @enum {number}
    + */
    +goog.proto2.ObjectSerializer.KeyOption = {
    +  /**
    +   * Use the tag of the field as the key (default)
    +   */
    +  TAG: 0,
    +
    +  /**
    +   * Use the name of the field as the key. Unknown fields
    +   * will still use their tags as keys.
    +   */
    +  NAME: 1
    +};
    +
    +
    +/**
    + * Serializes a message to an object.
    + *
    + * @param {goog.proto2.Message} message The message to be serialized.
    + * @return {!Object} The serialized form of the message.
    + * @override
    + */
    +goog.proto2.ObjectSerializer.prototype.serialize = function(message) {
    +  var descriptor = message.getDescriptor();
    +  var fields = descriptor.getFields();
    +
    +  var objectValue = {};
    +
    +  // Add the defined fields, recursively.
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +
    +    var key =
    +        this.keyOption_ == goog.proto2.ObjectSerializer.KeyOption.NAME ?
    +        field.getName() : field.getTag();
    +
    +
    +    if (message.has(field)) {
    +      if (field.isRepeated()) {
    +        var array = [];
    +        objectValue[key] = array;
    +
    +        for (var j = 0; j < message.countOf(field); j++) {
    +          array.push(this.getSerializedValue(field, message.get(field, j)));
    +        }
    +
    +      } else {
    +        objectValue[key] = this.getSerializedValue(field, message.get(field));
    +      }
    +    }
    +  }
    +
    +  // Add the unknown fields, if any.
    +  message.forEachUnknown(function(tag, value) {
    +    objectValue[tag] = value;
    +  });
    +
    +  return objectValue;
    +};
    +
    +
    +/** @override */
    +goog.proto2.ObjectSerializer.prototype.getDeserializedValue =
    +    function(field, value) {
    +
    +  // Gracefully handle the case where a boolean is represented by 0/1.
    +  // Some serialization libraries, such as GWT, can use this notation.
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL &&
    +      goog.isNumber(value)) {
    +    return Boolean(value);
    +  }
    +
    +  return goog.proto2.ObjectSerializer.base(
    +      this, 'getDeserializedValue', field, value);
    +};
    +
    +
    +/**
    + * Deserializes a message from an object and places the
    + * data in the message.
    + *
    + * @param {goog.proto2.Message} message The message in which to
    + *     place the information.
    + * @param {*} data The data of the message.
    + * @override
    + */
    +goog.proto2.ObjectSerializer.prototype.deserializeTo = function(message, data) {
    +  var descriptor = message.getDescriptor();
    +
    +  for (var key in data) {
    +    var field;
    +    var value = data[key];
    +
    +    var isNumeric = goog.string.isNumeric(key);
    +
    +    if (isNumeric) {
    +      field = descriptor.findFieldByTag(key);
    +    } else {
    +      // We must be in Key == NAME mode to lookup by name.
    +      goog.asserts.assert(
    +          this.keyOption_ == goog.proto2.ObjectSerializer.KeyOption.NAME);
    +
    +      field = descriptor.findFieldByName(key);
    +    }
    +
    +    if (field) {
    +      if (field.isRepeated()) {
    +        goog.asserts.assert(goog.isArray(value));
    +
    +        for (var j = 0; j < value.length; j++) {
    +          message.add(field, this.getDeserializedValue(field, value[j]));
    +        }
    +      } else {
    +        goog.asserts.assert(!goog.isArray(value));
    +        message.set(field, this.getDeserializedValue(field, value));
    +      }
    +    } else {
    +      if (isNumeric) {
    +        // We have an unknown field.
    +        message.setUnknown(Number(key), value);
    +      } else {
    +        // Named fields must be present.
    +        goog.asserts.fail('Failed to find field: ' + field);
    +      }
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.html b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.html
    new file mode 100644
    index 0000000000..6c5a46a8a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - objectserializer.js
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.ObjectSerializerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.js b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.js
    new file mode 100644
    index 0000000000..026bdac09e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/objectserializer_test.js
    @@ -0,0 +1,567 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.ObjectSerializerTest');
    +goog.setTestOnly('goog.proto2.ObjectSerializerTest');
    +
    +goog.require('goog.proto2.ObjectSerializer');
    +goog.require('goog.proto2.Serializer');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('proto2.TestAllTypes');
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +}
    +
    +function testSerialization() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Singular.
    +  message.setOptionalInt32(101);
    +  message.setOptionalInt64('102');
    +  message.setOptionalUint32(103);
    +  message.setOptionalUint64('104');
    +  message.setOptionalSint32(105);
    +  message.setOptionalSint64('106');
    +  message.setOptionalFixed32(107);
    +  message.setOptionalFixed64('108');
    +  message.setOptionalSfixed32(109);
    +  message.setOptionalSfixed64('110');
    +  message.setOptionalFloat(111.5);
    +  message.setOptionalDouble(112.5);
    +  message.setOptionalBool(true);
    +  message.setOptionalString('test');
    +  message.setOptionalBytes('abcd');
    +
    +  var group = new proto2.TestAllTypes.OptionalGroup();
    +  group.setA(111);
    +
    +  message.setOptionalgroup(group);
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(112);
    +
    +  message.setOptionalNestedMessage(nestedMessage);
    +
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  // Repeated.
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +
    +  // Serialize to a simplified object.
    +  var simplified = new goog.proto2.ObjectSerializer().serialize(message);
    +
    +  // Assert that everything serialized properly.
    +  assertEquals(101, simplified[1]);
    +  assertEquals('102', simplified[2]);
    +  assertEquals(103, simplified[3]);
    +  assertEquals('104', simplified[4]);
    +  assertEquals(105, simplified[5]);
    +  assertEquals('106', simplified[6]);
    +  assertEquals(107, simplified[7]);
    +  assertEquals('108', simplified[8]);
    +  assertEquals(109, simplified[9]);
    +  assertEquals('110', simplified[10]);
    +  assertEquals(111.5, simplified[11]);
    +  assertEquals(112.5, simplified[12]);
    +  assertEquals(true, simplified[13]);
    +  assertEquals('test', simplified[14]);
    +  assertEquals('abcd', simplified[15]);
    +
    +  assertEquals(111, simplified[16][17]);
    +  assertEquals(112, simplified[18][1]);
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO, simplified[21]);
    +
    +  assertEquals(201, simplified[31][0]);
    +  assertEquals(202, simplified[31][1]);
    +
    +  // Serialize to a simplified object (with key as name).
    +  simplified = new goog.proto2.ObjectSerializer(
    +      goog.proto2.ObjectSerializer.KeyOption.NAME).serialize(message);
    +
    +  // Assert that everything serialized properly.
    +  assertEquals(101, simplified['optional_int32']);
    +  assertEquals('102', simplified['optional_int64']);
    +  assertEquals(103, simplified['optional_uint32']);
    +  assertEquals('104', simplified['optional_uint64']);
    +  assertEquals(105, simplified['optional_sint32']);
    +  assertEquals('106', simplified['optional_sint64']);
    +  assertEquals(107, simplified['optional_fixed32']);
    +  assertEquals('108', simplified['optional_fixed64']);
    +  assertEquals(109, simplified['optional_sfixed32']);
    +  assertEquals('110', simplified['optional_sfixed64']);
    +  assertEquals(111.5, simplified['optional_float']);
    +  assertEquals(112.5, simplified['optional_double']);
    +  assertEquals(true, simplified['optional_bool']);
    +  assertEquals('test', simplified['optional_string']);
    +  assertEquals('abcd', simplified['optional_bytes']);
    +
    +  assertEquals(111, simplified['optionalgroup']['a']);
    +  assertEquals(112, simplified['optional_nested_message']['b']);
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO,
    +      simplified['optional_nested_enum']);
    +
    +  assertEquals(201, simplified['repeated_int32'][0]);
    +  assertEquals(202, simplified['repeated_int32'][1]);
    +}
    +
    +
    +function testSerializationOfUnknown() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Known.
    +  message.setOptionalInt32(101);
    +  message.setOptionalInt64('102');
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +
    +  // Unknown.
    +  message.setUnknown(1000, 301);
    +  message.setUnknown(1001, 302);
    +
    +  // Serialize.
    +  var simplified = new goog.proto2.ObjectSerializer().serialize(message);
    +
    +  assertEquals(101, simplified['1']);
    +  assertEquals('102', simplified['2']);
    +
    +  assertEquals(201, simplified['31'][0]);
    +  assertEquals(202, simplified['31'][1]);
    +
    +  assertEquals(301, simplified['1000']);
    +  assertEquals(302, simplified['1001']);
    +}
    +
    +function testDeserializationOfUnknown() {
    +  var simplified = {
    +    1: 101,
    +    2: '102',
    +    1000: 103,
    +    1001: 104
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +  assertTrue(message.hasOptionalInt32());
    +  assertTrue(message.hasOptionalInt64());
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals('102', message.getOptionalInt64());
    +
    +  var count = 0;
    +
    +  message.forEachUnknown(function(tag, value) {
    +    if (tag == 1000) {
    +      assertEquals(103, value);
    +    }
    +
    +    if (tag == 1001) {
    +      assertEquals(104, value);
    +    }
    +
    +    ++count;
    +  });
    +
    +  assertEquals(2, count);
    +}
    +
    +function testDeserializationRepeated() {
    +  var simplified = {
    +    31: [101, 102],
    +    41: [201.5, 202.5, 203.5],
    +    42: [],
    +    43: [true, false],
    +    44: ['he', 'llo'],
    +    46: [{ 47: [101] } , { 47: [102] }],
    +    48: [{ 1: 201 }, { 1: 202 }]
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  // Ensure the fields are set as expected.
    +  assertTrue(message.hasRepeatedInt32());
    +  assertTrue(message.hasRepeatedFloat());
    +
    +  assertFalse(message.hasRepeatedDouble());
    +
    +  assertTrue(message.hasRepeatedBool());
    +  assertTrue(message.hasRepeatedgroup());
    +  assertTrue(message.hasRepeatedNestedMessage());
    +
    +  // Ensure the counts match.
    +  assertEquals(2, message.repeatedInt32Count());
    +  assertEquals(3, message.repeatedFloatCount());
    +
    +  assertEquals(0, message.repeatedDoubleCount());
    +
    +  assertEquals(2, message.repeatedBoolCount());
    +  assertEquals(2, message.repeatedStringCount());
    +  assertEquals(2, message.repeatedgroupCount());
    +  assertEquals(2, message.repeatedNestedMessageCount());
    +
    +  // Ensure the values match.
    +  assertEquals(101, message.getRepeatedInt32(0));
    +  assertEquals(102, message.getRepeatedInt32(1));
    +
    +  assertEquals(201.5, message.getRepeatedFloat(0));
    +  assertEquals(202.5, message.getRepeatedFloat(1));
    +  assertEquals(203.5, message.getRepeatedFloat(2));
    +
    +  assertEquals(true, message.getRepeatedBool(0));
    +  assertEquals(false, message.getRepeatedBool(1));
    +
    +  assertEquals('he', message.getRepeatedString(0));
    +  assertEquals('llo', message.getRepeatedString(1));
    +
    +  assertEquals(101, message.getRepeatedgroup(0).getA(0));
    +  assertEquals(102, message.getRepeatedgroup(1).getA(0));
    +
    +  assertEquals(201, message.getRepeatedNestedMessage(0).getB());
    +  assertEquals(202, message.getRepeatedNestedMessage(1).getB());
    +}
    +
    +function testDeserialization() {
    +  var simplified = {
    +    1: 101,
    +    2: '102',
    +    3: 103,
    +    4: '104',
    +    5: 105,
    +    6: '106',
    +    7: 107,
    +    8: '108',
    +    9: 109,
    +    10: '110',
    +    11: 111.5,
    +    12: 112.5,
    +    13: true,
    +    14: 'test',
    +    15: 'abcd',
    +    16: { 17 : 113 },
    +    18: { 1 : 114 },
    +    21: proto2.TestAllTypes.NestedEnum.FOO
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  assertTrue(message.hasOptionalInt32());
    +  assertTrue(message.hasOptionalInt64());
    +  assertTrue(message.hasOptionalUint32());
    +  assertTrue(message.hasOptionalUint64());
    +  assertTrue(message.hasOptionalSint32());
    +  assertTrue(message.hasOptionalSint64());
    +  assertTrue(message.hasOptionalFixed32());
    +  assertTrue(message.hasOptionalFixed64());
    +  assertTrue(message.hasOptionalSfixed32());
    +  assertTrue(message.hasOptionalSfixed64());
    +  assertTrue(message.hasOptionalFloat());
    +  assertTrue(message.hasOptionalDouble());
    +  assertTrue(message.hasOptionalBool());
    +  assertTrue(message.hasOptionalString());
    +  assertTrue(message.hasOptionalBytes());
    +  assertTrue(message.hasOptionalgroup());
    +  assertTrue(message.hasOptionalNestedMessage());
    +  assertTrue(message.hasOptionalNestedEnum());
    +
    +  assertEquals(1, message.optionalInt32Count());
    +  assertEquals(1, message.optionalInt64Count());
    +  assertEquals(1, message.optionalUint32Count());
    +  assertEquals(1, message.optionalUint64Count());
    +  assertEquals(1, message.optionalSint32Count());
    +  assertEquals(1, message.optionalSint64Count());
    +  assertEquals(1, message.optionalFixed32Count());
    +  assertEquals(1, message.optionalFixed64Count());
    +  assertEquals(1, message.optionalSfixed32Count());
    +  assertEquals(1, message.optionalSfixed64Count());
    +  assertEquals(1, message.optionalFloatCount());
    +  assertEquals(1, message.optionalDoubleCount());
    +  assertEquals(1, message.optionalBoolCount());
    +  assertEquals(1, message.optionalStringCount());
    +  assertEquals(1, message.optionalBytesCount());
    +  assertEquals(1, message.optionalgroupCount());
    +  assertEquals(1, message.optionalNestedMessageCount());
    +  assertEquals(1, message.optionalNestedEnumCount());
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals('102', message.getOptionalInt64());
    +  assertEquals(103, message.getOptionalUint32());
    +  assertEquals('104', message.getOptionalUint64());
    +  assertEquals(105, message.getOptionalSint32());
    +  assertEquals('106', message.getOptionalSint64());
    +  assertEquals(107, message.getOptionalFixed32());
    +  assertEquals('108', message.getOptionalFixed64());
    +  assertEquals(109, message.getOptionalSfixed32());
    +  assertEquals('110', message.getOptionalSfixed64());
    +  assertEquals(111.5, message.getOptionalFloat());
    +  assertEquals(112.5, message.getOptionalDouble());
    +  assertEquals(true, message.getOptionalBool());
    +  assertEquals('test', message.getOptionalString());
    +  assertEquals('abcd', message.getOptionalBytes());
    +  assertEquals(113, message.getOptionalgroup().getA());
    +  assertEquals(114, message.getOptionalNestedMessage().getB());
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO,
    +      message.getOptionalNestedEnum());
    +}
    +
    +function testDeserializationUnknownEnumValue() {
    +  var simplified = {
    +    21: 1001
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  assertEquals(1001, message.getOptionalNestedEnum());
    +}
    +
    +function testDeserializationSymbolicEnumValue() {
    +  var simplified = {
    +    21: 'BAR'
    +  };
    +
    +  propertyReplacer.set(goog.proto2.Serializer, 'DECODE_SYMBOLIC_ENUMS', true);
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.BAR,
    +      message.getOptionalNestedEnum());
    +}
    +
    +function testDeserializationSymbolicEnumValueTurnedOff() {
    +  var simplified = {
    +    21: 'BAR'
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  assertThrows('Should have an assertion failure in deserialization',
    +      function() {
    +        serializer.deserialize(proto2.TestAllTypes.getDescriptor(), simplified);
    +      });
    +}
    +
    +function testDeserializationUnknownSymbolicEnumValue() {
    +  var simplified = {
    +    21: 'BARRED'
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  assertThrows('Should have an assertion failure in deserialization',
    +      function() {
    +        serializer.deserialize(proto2.TestAllTypes.getDescriptor(), simplified);
    +      });
    +}
    +
    +function testDeserializationNumbersOrStrings() {
    +  // 64-bit types may have been serialized as numbers or strings.
    +  // Deserialization should be able to handle either.
    +
    +  var simplifiedWithNumbers = {
    +    50: 5000,
    +    51: 5100,
    +    52: [5200, 5201],
    +    53: [5300, 5301]
    +  };
    +
    +  var simplifiedWithStrings = {
    +    50: '5000',
    +    51: '5100',
    +    52: ['5200', '5201'],
    +    53: ['5300', '5301']
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplifiedWithNumbers);
    +
    +  assertNotNull(message);
    +
    +  assertEquals(5000, message.getOptionalInt64Number());
    +  assertEquals('5100', message.getOptionalInt64String());
    +  assertEquals(5200, message.getRepeatedInt64Number(0));
    +  assertEquals(5201, message.getRepeatedInt64Number(1));
    +  assertEquals('5300', message.getRepeatedInt64String(0));
    +  assertEquals('5301', message.getRepeatedInt64String(1));
    +
    +  assertArrayEquals([5200, 5201], message.repeatedInt64NumberArray());
    +  assertArrayEquals(['5300', '5301'], message.repeatedInt64StringArray());
    +
    +  message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplifiedWithStrings);
    +
    +  assertNotNull(message);
    +
    +  assertEquals(5000, message.getOptionalInt64Number());
    +  assertEquals('5100', message.getOptionalInt64String());
    +  assertEquals(5200, message.getRepeatedInt64Number(0));
    +  assertEquals(5201, message.getRepeatedInt64Number(1));
    +  assertEquals('5300', message.getRepeatedInt64String(0));
    +  assertEquals('5301', message.getRepeatedInt64String(1));
    +
    +  assertArrayEquals([5200, 5201], message.repeatedInt64NumberArray());
    +  assertArrayEquals(['5300', '5301'], message.repeatedInt64StringArray());
    +}
    +
    +function testSerializationSpecialFloatDoubleValues() {
    +  // NaN, Infinity and -Infinity should get serialized as strings.
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalFloat(Infinity);
    +  message.setOptionalDouble(-Infinity);
    +  message.addRepeatedFloat(Infinity);
    +  message.addRepeatedFloat(-Infinity);
    +  message.addRepeatedFloat(NaN);
    +  message.addRepeatedDouble(Infinity);
    +  message.addRepeatedDouble(-Infinity);
    +  message.addRepeatedDouble(NaN);
    +  var simplified = new goog.proto2.ObjectSerializer().serialize(message);
    +
    +  // Assert that everything serialized properly.
    +  assertEquals('Infinity', simplified[11]);
    +  assertEquals('-Infinity', simplified[12]);
    +  assertEquals('Infinity', simplified[41][0]);
    +  assertEquals('-Infinity', simplified[41][1]);
    +  assertEquals('NaN', simplified[41][2]);
    +  assertEquals('Infinity', simplified[42][0]);
    +  assertEquals('-Infinity', simplified[42][1]);
    +  assertEquals('NaN', simplified[42][2]);
    +}
    +
    +function testDeserializationSpecialFloatDoubleValues() {
    +  // NaN, Infinity and -Infinity values should be de-serialized from their
    +  // string representation.
    +  var simplified = {
    +    41: ['Infinity', '-Infinity', 'NaN'],
    +    42: ['Infinity', '-Infinity', 'NaN']
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  var floatArray = message.repeatedFloatArray();
    +  assertEquals(Infinity, floatArray[0]);
    +  assertEquals(-Infinity, floatArray[1]);
    +  assertTrue(isNaN(floatArray[2]));
    +
    +  var doubleArray = message.repeatedDoubleArray();
    +  assertEquals(Infinity, doubleArray[0]);
    +  assertEquals(-Infinity, doubleArray[1]);
    +  assertTrue(isNaN(doubleArray[2]));
    +}
    +
    +function testDeserializationConversionProhibited() {
    +  // 64-bit types may have been serialized as numbers or strings.
    +  // But 32-bit types must be serialized as numbers.
    +  // Test deserialization fails on 32-bit numbers as strings.
    +
    +  var simplified = {
    +    1: '1000'   // optionalInt32
    +  };
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  assertThrows('Should have an assertion failure in deserialization',
    +      function() {
    +        serializer.deserialize(proto2.TestAllTypes.getDescriptor(), simplified);
    +      });
    +}
    +
    +function testDefaultValueNumbersOrStrings() {
    +  // 64-bit types may have been serialized as numbers or strings.
    +  // The default values should have the correct type.
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +  var message = serializer.deserialize(proto2.TestAllTypes.getDescriptor(), {});
    +
    +  assertNotNull(message);
    +
    +  // Default when using Number is a number, and precision is lost.
    +  var value = message.getOptionalInt64NumberOrDefault();
    +  assertTrue('Expecting a number', typeof value === 'number');
    +  assertEquals(1000000000000000000, value);
    +  assertEquals(1000000000000000001, value);
    +  assertEquals(1000000000000000002, value);
    +  assertEquals('1000000000000000000', String(value));  // Value is rounded!
    +
    +  // When using a String, the value is preserved.
    +  assertEquals('1000000000000000001',
    +               message.getOptionalInt64StringOrDefault());
    +}
    +
    +function testBooleanAsNumberFalse() {
    +  // Some libraries, such as GWT, can serialize boolean values as 0/1
    +
    +  var simplified = {
    +    13: 0
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  assertFalse(message.getOptionalBool());
    +}
    +
    +function testBooleanAsNumberTrue() {
    +  var simplified = {
    +    13: 1
    +  };
    +
    +  var serializer = new goog.proto2.ObjectSerializer();
    +
    +  var message = serializer.deserialize(
    +      proto2.TestAllTypes.getDescriptor(), simplified);
    +
    +  assertNotNull(message);
    +
    +  assertTrue(message.getOptionalBool());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/package_test.pb.js b/mapbox-gl/closure-library/closure/goog/proto2/package_test.pb.js
    new file mode 100644
    index 0000000000..e887131075
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/package_test.pb.js
    @@ -0,0 +1,167 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All other code copyright its respective owners(s).
    +
    +/**
    + * @fileoverview Generated Protocol Buffer code for file
    + * closure/goog/proto2/package_test.proto.
    + */
    +
    +goog.provide('someprotopackage.TestPackageTypes');
    +
    +goog.require('goog.proto2.Message');
    +goog.require('proto2.TestAllTypes');
    +
    +goog.setTestOnly('package_test.pb');
    +
    +
    +
    +/**
    + * Message TestPackageTypes.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + * @final
    + */
    +someprotopackage.TestPackageTypes = function() {
    +  goog.proto2.Message.apply(this);
    +};
    +goog.inherits(someprotopackage.TestPackageTypes, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!someprotopackage.TestPackageTypes} The cloned message.
    + * @override
    + */
    +someprotopackage.TestPackageTypes.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the optional_int32 field.
    + * @return {?number} The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.getOptionalInt32 = function() {
    +  return /** @type {?number} */ (this.get$Value(1));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.getOptionalInt32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(1));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_int32 field.
    + * @param {number} value The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.setOptionalInt32 = function(value) {
    +  this.set$Value(1, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_int32 field has a value.
    + */
    +someprotopackage.TestPackageTypes.prototype.hasOptionalInt32 = function() {
    +  return this.has$Value(1);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_int32 field.
    + */
    +someprotopackage.TestPackageTypes.prototype.optionalInt32Count = function() {
    +  return this.count$Values(1);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_int32 field.
    + */
    +someprotopackage.TestPackageTypes.prototype.clearOptionalInt32 = function() {
    +  this.clear$Field(1);
    +};
    +
    +
    +/**
    + * Gets the value of the other_all field.
    + * @return {proto2.TestAllTypes} The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.getOtherAll = function() {
    +  return /** @type {proto2.TestAllTypes} */ (this.get$Value(2));
    +};
    +
    +
    +/**
    + * Gets the value of the other_all field or the default value if not set.
    + * @return {!proto2.TestAllTypes} The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.getOtherAllOrDefault = function() {
    +  return /** @type {!proto2.TestAllTypes} */ (this.get$ValueOrDefault(2));
    +};
    +
    +
    +/**
    + * Sets the value of the other_all field.
    + * @param {!proto2.TestAllTypes} value The value.
    + */
    +someprotopackage.TestPackageTypes.prototype.setOtherAll = function(value) {
    +  this.set$Value(2, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the other_all field has a value.
    + */
    +someprotopackage.TestPackageTypes.prototype.hasOtherAll = function() {
    +  return this.has$Value(2);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the other_all field.
    + */
    +someprotopackage.TestPackageTypes.prototype.otherAllCount = function() {
    +  return this.count$Values(2);
    +};
    +
    +
    +/**
    + * Clears the values in the other_all field.
    + */
    +someprotopackage.TestPackageTypes.prototype.clearOtherAll = function() {
    +  this.clear$Field(2);
    +};
    +
    +
    +goog.proto2.Message.set$Metadata(someprotopackage.TestPackageTypes, {
    +  0: {
    +    name: 'TestPackageTypes',
    +    fullName: 'someprotopackage.TestPackageTypes'
    +  },
    +  1: {
    +    name: 'optional_int32',
    +    fieldType: goog.proto2.Message.FieldType.INT32,
    +    type: Number
    +  },
    +  2: {
    +    name: 'other_all',
    +    fieldType: goog.proto2.Message.FieldType.MESSAGE,
    +    type: proto2.TestAllTypes
    +  }
    +});
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer.js b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer.js
    new file mode 100644
    index 0000000000..2a9260c9f9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer.js
    @@ -0,0 +1,199 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer 2 Serializer which serializes messages
    + *  into PB-Lite ("JsPbLite") format.
    + *
    + * PB-Lite format is an array where each index corresponds to the associated tag
    + * number. For example, a message like so:
    + *
    + * message Foo {
    + *   optional int bar = 1;
    + *   optional int baz = 2;
    + *   optional int bop = 4;
    + * }
    + *
    + * would be represented as such:
    + *
    + * [, (bar data), (baz data), (nothing), (bop data)]
    + *
    + * Note that since the array index is used to represent the tag number, sparsely
    + * populated messages with tag numbers that are not continuous (and/or are very
    + * large) will have many (empty) spots and thus, are inefficient.
    + *
    + *
    + */
    +
    +goog.provide('goog.proto2.PbLiteSerializer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.proto2.LazyDeserializer');
    +goog.require('goog.proto2.Serializer');
    +
    +
    +
    +/**
    + * PB-Lite serializer.
    + *
    + * @constructor
    + * @extends {goog.proto2.LazyDeserializer}
    + */
    +goog.proto2.PbLiteSerializer = function() {};
    +goog.inherits(goog.proto2.PbLiteSerializer, goog.proto2.LazyDeserializer);
    +
    +
    +/**
    + * If true, fields will be serialized with 0-indexed tags (i.e., the proto
    + * field with tag id 1 will have index 0 in the array).
    + * @type {boolean}
    + * @private
    + */
    +goog.proto2.PbLiteSerializer.prototype.zeroIndexing_ = false;
    +
    +
    +/**
    + * By default, the proto tag with id 1 will have index 1 in the serialized
    + * array.
    + *
    + * If the serializer is set to use zero-indexing, the tag with id 1 will have
    + * index 0.
    + *
    + * @param {boolean} zeroIndexing Whether this serializer should deal with
    + *     0-indexed protos.
    + */
    +goog.proto2.PbLiteSerializer.prototype.setZeroIndexed = function(zeroIndexing) {
    +  this.zeroIndexing_ = zeroIndexing;
    +};
    +
    +
    +/**
    + * Serializes a message to a PB-Lite object.
    + *
    + * @param {goog.proto2.Message} message The message to be serialized.
    + * @return {!Array<?>} The serialized form of the message.
    + * @override
    + */
    +goog.proto2.PbLiteSerializer.prototype.serialize = function(message) {
    +  var descriptor = message.getDescriptor();
    +  var fields = descriptor.getFields();
    +
    +  var serialized = [];
    +  var zeroIndexing = this.zeroIndexing_;
    +
    +  // Add the known fields.
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +
    +    if (!message.has(field)) {
    +      continue;
    +    }
    +
    +    var tag = field.getTag();
    +    var index = zeroIndexing ? tag - 1 : tag;
    +
    +    if (field.isRepeated()) {
    +      serialized[index] = [];
    +
    +      for (var j = 0; j < message.countOf(field); j++) {
    +        serialized[index][j] =
    +            this.getSerializedValue(field, message.get(field, j));
    +      }
    +    } else {
    +      serialized[index] = this.getSerializedValue(field, message.get(field));
    +    }
    +  }
    +
    +  // Add any unknown fields.
    +  message.forEachUnknown(function(tag, value) {
    +    var index = zeroIndexing ? tag - 1 : tag;
    +    serialized[index] = value;
    +  });
    +
    +  return serialized;
    +};
    +
    +
    +/** @override */
    +goog.proto2.PbLiteSerializer.prototype.deserializeField =
    +    function(message, field, value) {
    +
    +  if (value == null) {
    +    // Since value double-equals null, it may be either null or undefined.
    +    // Ensure we return the same one, since they have different meanings.
    +    // TODO(user): If the field is repeated, this method should probably
    +    // return [] instead of null.
    +    return value;
    +  }
    +
    +  if (field.isRepeated()) {
    +    var data = [];
    +
    +    goog.asserts.assert(goog.isArray(value), 'Value must be array: %s', value);
    +
    +    for (var i = 0; i < value.length; i++) {
    +      data[i] = this.getDeserializedValue(field, value[i]);
    +    }
    +
    +    return data;
    +  } else {
    +    return this.getDeserializedValue(field, value);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.proto2.PbLiteSerializer.prototype.getSerializedValue =
    +    function(field, value) {
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) {
    +    // Booleans are serialized in numeric form.
    +    return value ? 1 : 0;
    +  }
    +
    +  return goog.proto2.Serializer.prototype.getSerializedValue.apply(this,
    +                                                                   arguments);
    +};
    +
    +
    +/** @override */
    +goog.proto2.PbLiteSerializer.prototype.getDeserializedValue =
    +    function(field, value) {
    +
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) {
    +    goog.asserts.assert(goog.isNumber(value) || goog.isBoolean(value),
    +        'Value is expected to be a number or boolean');
    +    return !!value;
    +  }
    +
    +  return goog.proto2.Serializer.prototype.getDeserializedValue.apply(this,
    +                                                                     arguments);
    +};
    +
    +
    +/** @override */
    +goog.proto2.PbLiteSerializer.prototype.deserialize =
    +    function(descriptor, data) {
    +  var toConvert = data;
    +  if (this.zeroIndexing_) {
    +    // Make the data align with tag-IDs (1-indexed) by shifting everything
    +    // up one.
    +    toConvert = [];
    +    for (var key in data) {
    +      toConvert[parseInt(key, 10) + 1] = data[key];
    +    }
    +  }
    +  return goog.proto2.PbLiteSerializer.base(
    +      this, 'deserialize', descriptor, toConvert);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.html b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.html
    new file mode 100644
    index 0000000000..f94cf1cdcb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - pbliteserializer.js
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.PbLiteSerializerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.js b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.js
    new file mode 100644
    index 0000000000..889b08bf25
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/pbliteserializer_test.js
    @@ -0,0 +1,499 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.PbLiteSerializerTest');
    +goog.setTestOnly('goog.proto2.PbLiteSerializerTest');
    +
    +goog.require('goog.proto2.PbLiteSerializer');
    +goog.require('goog.testing.jsunit');
    +goog.require('proto2.TestAllTypes');
    +
    +function testSerializationAndDeserialization() {
    +  var message = createPopulatedMessage();
    +
    +  // Serialize.
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  var pblite = serializer.serialize(message);
    +
    +  assertTrue(goog.isArray(pblite));
    +
    +  // Assert that everything serialized properly.
    +  assertEquals(101, pblite[1]);
    +  assertEquals('102', pblite[2]);
    +  assertEquals(103, pblite[3]);
    +  assertEquals('104', pblite[4]);
    +  assertEquals(105, pblite[5]);
    +  assertEquals('106', pblite[6]);
    +  assertEquals(107, pblite[7]);
    +  assertEquals('108', pblite[8]);
    +  assertEquals(109, pblite[9]);
    +  assertEquals('110', pblite[10]);
    +  assertEquals(111.5, pblite[11]);
    +  assertEquals(112.5, pblite[12]);
    +  assertEquals(1, pblite[13]); // true is serialized as 1
    +  assertEquals('test', pblite[14]);
    +  assertEquals('abcd', pblite[15]);
    +
    +  assertEquals(111, pblite[16][17]);
    +  assertEquals(112, pblite[18][1]);
    +
    +  assertTrue(pblite[19] === undefined);
    +  assertTrue(pblite[20] === undefined);
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO, pblite[21]);
    +
    +  assertEquals(201, pblite[31][0]);
    +  assertEquals(202, pblite[31][1]);
    +  assertEquals('foo', pblite[44][0]);
    +  assertEquals('bar', pblite[44][1]);
    +
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  // Deserialize.
    +  var messageCopy =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pblite);
    +
    +  assertNotEquals(messageCopy, message);
    +
    +  assertDeserializationMatches(messageCopy);
    +}
    +
    +function testZeroBasedSerializationAndDeserialization() {
    +  var message = createPopulatedMessage();
    +
    +  // Serialize.
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  serializer.setZeroIndexed(true);
    +
    +  var pblite = serializer.serialize(message);
    +
    +  assertTrue(goog.isArray(pblite));
    +
    +  // Assert that everything serialized properly.
    +  assertEquals(101, pblite[0]);
    +  assertEquals('102', pblite[1]);
    +  assertEquals(103, pblite[2]);
    +  assertEquals('104', pblite[3]);
    +  assertEquals(105, pblite[4]);
    +  assertEquals('106', pblite[5]);
    +  assertEquals(107, pblite[6]);
    +  assertEquals('108', pblite[7]);
    +  assertEquals(109, pblite[8]);
    +  assertEquals('110', pblite[9]);
    +  assertEquals(111.5, pblite[10]);
    +  assertEquals(112.5, pblite[11]);
    +  assertEquals(1, pblite[12]); // true is serialized as 1
    +  assertEquals('test', pblite[13]);
    +  assertEquals('abcd', pblite[14]);
    +
    +  assertEquals(111, pblite[15][16]);
    +  assertEquals(112, pblite[17][0]);
    +
    +  assertTrue(pblite[18] === undefined);
    +  assertTrue(pblite[19] === undefined);
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO, pblite[20]);
    +
    +  assertEquals(201, pblite[30][0]);
    +  assertEquals(202, pblite[30][1]);
    +  assertEquals('foo', pblite[43][0]);
    +  assertEquals('bar', pblite[43][1]);
    +
    +  // Deserialize.
    +  var messageCopy =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pblite);
    +
    +  assertNotEquals(messageCopy, message);
    +
    +  assertEquals(message.getOptionalInt32(), messageCopy.getOptionalInt32());
    +  assertDeserializationMatches(messageCopy);
    +}
    +
    +function createPopulatedMessage() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Singular.
    +  message.setOptionalInt32(101);
    +  message.setOptionalInt64('102');
    +  message.setOptionalUint32(103);
    +  message.setOptionalUint64('104');
    +  message.setOptionalSint32(105);
    +  message.setOptionalSint64('106');
    +  message.setOptionalFixed32(107);
    +  message.setOptionalFixed64('108');
    +  message.setOptionalSfixed32(109);
    +  message.setOptionalSfixed64('110');
    +  message.setOptionalFloat(111.5);
    +  message.setOptionalDouble(112.5);
    +  message.setOptionalBool(true);
    +  message.setOptionalString('test');
    +  message.setOptionalBytes('abcd');
    +
    +  var group = new proto2.TestAllTypes.OptionalGroup();
    +  group.setA(111);
    +
    +  message.setOptionalgroup(group);
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(112);
    +
    +  message.setOptionalNestedMessage(nestedMessage);
    +
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  // Repeated.
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +
    +  // Skip a few repeated fields so we can test how null array values are
    +  // handled.
    +  message.addRepeatedString('foo');
    +  message.addRepeatedString('bar');
    +  return message;
    +}
    +
    +function testDeserializationFromExternalSource() {
    +  // Test deserialization where the JSON array is initialized from something
    +  // outside the Closure proto2 library, such as the JsPbLite library, or
    +  // manually as in this test.
    +  var pblite = [
    +    , // 0
    +    101, // 1
    +    '102', // 2
    +    103, // 3
    +    '104', // 4
    +    105, // 5
    +    '106', // 6
    +    107, // 7
    +    '108', // 8
    +    109, // 9
    +    '110', // 10
    +    111.5, // 11
    +    112.5, // 12
    +    1, // 13
    +    'test', // 14
    +    'abcd', // 15
    +    [,,,,,,,,,,,,,,,,, 111], // 16, note the 17 commas so value is index 17
    +    , // 17
    +    [, 112], // 18
    +    ,, // 19-20
    +    proto2.TestAllTypes.NestedEnum.FOO, // 21
    +    ,,,,,,,,, // 22-30
    +    [201, 202], // 31
    +    ,,,,,,,,,,,, // 32-43
    +    ['foo', 'bar'], // 44
    +    ,,,, // 45-49
    +  ];
    +
    +  // Deserialize.
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  var messageCopy =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pblite);
    +
    +  assertDeserializationMatches(messageCopy);
    +
    +  // http://b/issue?id=2928075
    +  assertFalse(messageCopy.hasRepeatedInt64());
    +  assertEquals(0, messageCopy.repeatedInt64Count());
    +  messageCopy.repeatedInt64Array();
    +  assertFalse(messageCopy.hasRepeatedInt64());
    +  assertEquals(0, messageCopy.repeatedInt64Count());
    +
    +  // Access a nested message to ensure it is deserialized.
    +  assertNotNull(messageCopy.getOptionalNestedMessage());
    +
    +  // Verify that the pblite array itself has not been replaced by the
    +  // deserialization.
    +  assertEquals('array', goog.typeOf(pblite[16]));
    +
    +  // Update some fields and verify that the changes work with the lazy
    +  // deserializer.
    +  messageCopy.setOptionalBool(true);
    +  assertTrue(messageCopy.getOptionalBool());
    +
    +  messageCopy.setOptionalBool(false);
    +  assertFalse(messageCopy.getOptionalBool());
    +
    +  messageCopy.setOptionalInt32(1234);
    +  assertEquals(1234, messageCopy.getOptionalInt32());
    +}
    +
    +function testModifyLazyDeserializedMessage() {
    +  var pblite = [
    +    , // 0
    +    101, // 1
    +    '102', // 2
    +    103, // 3
    +    '104', // 4
    +    105, // 5
    +    '106', // 6
    +    107, // 7
    +    '108', // 8
    +    109, // 9
    +    '110', // 10
    +    111.5, // 11
    +    112.5, // 12
    +    1, // 13
    +    'test', // 14
    +    'abcd', // 15
    +    [,,,,,,,,,,,,,,,,, 111], // 16, note the 17 commas so value is index 17
    +    , // 17
    +    [, 112], // 18
    +    ,, // 19-20
    +    proto2.TestAllTypes.NestedEnum.FOO, // 21
    +    ,,,,,,,,, // 22-30
    +    [201, 202], // 31
    +    ,,,,,,,,,,,, // 32-43
    +    ['foo', 'bar'], // 44
    +    ,,,, // 45-49
    +  ];
    +
    +  // Deserialize.
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  var message =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pblite);
    +
    +  // Conduct some operations, ensuring that they all work as expected, even with
    +  // the lazily deserialized data.
    +  assertEquals(101, message.getOptionalInt32());
    +  message.setOptionalInt32(401);
    +  assertEquals(401, message.getOptionalInt32());
    +
    +  assertEquals(2, message.repeatedInt32Count());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +
    +  message.clearRepeatedInt32();
    +  assertEquals(0, message.repeatedInt32Count());
    +
    +  message.addRepeatedInt32(101);
    +  assertEquals(1, message.repeatedInt32Count());
    +  assertEquals(101, message.getRepeatedInt32(0));
    +
    +  message.setUnknown(12345, 601);
    +  message.forEachUnknown(function(tag, value) {
    +    assertEquals(12345, tag);
    +    assertEquals(601, value);
    +  });
    +
    +  // Create a copy of the message.
    +  var messageCopy = new proto2.TestAllTypes();
    +  messageCopy.copyFrom(message);
    +
    +  assertEquals(1, messageCopy.repeatedInt32Count());
    +  assertEquals(101, messageCopy.getRepeatedInt32(0));
    +}
    +
    +function testModifyLazyDeserializedMessageByAddingMessage() {
    +  var pblite = [
    +    , // 0
    +    101, // 1
    +    '102', // 2
    +    103, // 3
    +    '104', // 4
    +    105, // 5
    +    '106', // 6
    +    107, // 7
    +    '108', // 8
    +    109, // 9
    +    '110', // 10
    +    111.5, // 11
    +    112.5, // 12
    +    1, // 13
    +    'test', // 14
    +    'abcd', // 15
    +    [,,,,,,,,,,,,,,,,, 111], // 16, note the 17 commas so value is index 17
    +    , // 17
    +    [, 112], // 18
    +    ,, // 19-20
    +    proto2.TestAllTypes.NestedEnum.FOO, // 21
    +    ,,,,,,,,, // 22-30
    +    [201, 202], // 31
    +    ,,,,,,,,,,,, // 32-43
    +    ['foo', 'bar'], // 44
    +    ,,,, // 45-49
    +  ];
    +
    +  // Deserialize.
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +  var message =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pblite);
    +
    +  // Add a new nested message.
    +  var nested1 = new proto2.TestAllTypes.NestedMessage();
    +  nested1.setB(1234);
    +
    +  var nested2 = new proto2.TestAllTypes.NestedMessage();
    +  nested2.setB(4567);
    +
    +  message.addRepeatedNestedMessage(nested1);
    +
    +  // Check the new nested message.
    +  assertEquals(1, message.repeatedNestedMessageArray().length);
    +  assertTrue(message.repeatedNestedMessageArray()[0].equals(nested1));
    +
    +  // Add another nested message.
    +  message.addRepeatedNestedMessage(nested2);
    +
    +  // Check both nested messages.
    +  assertEquals(2, message.repeatedNestedMessageArray().length);
    +  assertTrue(message.repeatedNestedMessageArray()[0].equals(nested1));
    +  assertTrue(message.repeatedNestedMessageArray()[1].equals(nested2));
    +}
    +
    +function assertDeserializationMatches(messageCopy) {
    +  assertNotNull(messageCopy);
    +
    +  assertTrue(messageCopy.hasOptionalInt32());
    +  assertTrue(messageCopy.hasOptionalInt64());
    +  assertTrue(messageCopy.hasOptionalUint32());
    +  assertTrue(messageCopy.hasOptionalUint64());
    +  assertTrue(messageCopy.hasOptionalSint32());
    +  assertTrue(messageCopy.hasOptionalSint64());
    +  assertTrue(messageCopy.hasOptionalFixed32());
    +  assertTrue(messageCopy.hasOptionalFixed64());
    +  assertTrue(messageCopy.hasOptionalSfixed32());
    +  assertTrue(messageCopy.hasOptionalSfixed64());
    +  assertTrue(messageCopy.hasOptionalFloat());
    +  assertTrue(messageCopy.hasOptionalDouble());
    +  assertTrue(messageCopy.hasOptionalBool());
    +  assertTrue(messageCopy.hasOptionalString());
    +  assertTrue(messageCopy.hasOptionalBytes());
    +  assertTrue(messageCopy.hasOptionalgroup());
    +  assertTrue(messageCopy.hasOptionalNestedMessage());
    +  assertTrue(messageCopy.hasOptionalNestedEnum());
    +
    +  assertTrue(messageCopy.hasRepeatedInt32());
    +  assertFalse(messageCopy.hasRepeatedInt64());
    +  assertFalse(messageCopy.hasRepeatedUint32());
    +  assertFalse(messageCopy.hasRepeatedUint64());
    +  assertFalse(messageCopy.hasRepeatedSint32());
    +  assertFalse(messageCopy.hasRepeatedSint64());
    +  assertFalse(messageCopy.hasRepeatedFixed32());
    +  assertFalse(messageCopy.hasRepeatedFixed64());
    +  assertFalse(messageCopy.hasRepeatedSfixed32());
    +  assertFalse(messageCopy.hasRepeatedSfixed64());
    +  assertFalse(messageCopy.hasRepeatedFloat());
    +  assertFalse(messageCopy.hasRepeatedDouble());
    +  assertFalse(messageCopy.hasRepeatedBool());
    +  assertTrue(messageCopy.hasRepeatedString());
    +  assertFalse(messageCopy.hasRepeatedBytes());
    +  assertFalse(messageCopy.hasRepeatedgroup());
    +  assertFalse(messageCopy.hasRepeatedNestedMessage());
    +  assertFalse(messageCopy.hasRepeatedNestedEnum());
    +
    +  assertEquals(1, messageCopy.optionalInt32Count());
    +  assertEquals(1, messageCopy.optionalInt64Count());
    +  assertEquals(1, messageCopy.optionalUint32Count());
    +  assertEquals(1, messageCopy.optionalUint64Count());
    +  assertEquals(1, messageCopy.optionalSint32Count());
    +  assertEquals(1, messageCopy.optionalSint64Count());
    +  assertEquals(1, messageCopy.optionalFixed32Count());
    +  assertEquals(1, messageCopy.optionalFixed64Count());
    +  assertEquals(1, messageCopy.optionalSfixed32Count());
    +  assertEquals(1, messageCopy.optionalSfixed64Count());
    +  assertEquals(1, messageCopy.optionalFloatCount());
    +  assertEquals(1, messageCopy.optionalDoubleCount());
    +  assertEquals(1, messageCopy.optionalBoolCount());
    +  assertEquals(1, messageCopy.optionalStringCount());
    +  assertEquals(1, messageCopy.optionalBytesCount());
    +  assertEquals(1, messageCopy.optionalgroupCount());
    +  assertEquals(1, messageCopy.optionalNestedMessageCount());
    +  assertEquals(1, messageCopy.optionalNestedEnumCount());
    +
    +  assertEquals(2, messageCopy.repeatedInt32Count());
    +  assertEquals(0, messageCopy.repeatedInt64Count());
    +  assertEquals(0, messageCopy.repeatedUint32Count());
    +  assertEquals(0, messageCopy.repeatedUint64Count());
    +  assertEquals(0, messageCopy.repeatedSint32Count());
    +  assertEquals(0, messageCopy.repeatedSint64Count());
    +  assertEquals(0, messageCopy.repeatedFixed32Count());
    +  assertEquals(0, messageCopy.repeatedFixed64Count());
    +  assertEquals(0, messageCopy.repeatedSfixed32Count());
    +  assertEquals(0, messageCopy.repeatedSfixed64Count());
    +  assertEquals(0, messageCopy.repeatedFloatCount());
    +  assertEquals(0, messageCopy.repeatedDoubleCount());
    +  assertEquals(0, messageCopy.repeatedBoolCount());
    +  assertEquals(2, messageCopy.repeatedStringCount());
    +  assertEquals(0, messageCopy.repeatedBytesCount());
    +  assertEquals(0, messageCopy.repeatedgroupCount());
    +  assertEquals(0, messageCopy.repeatedNestedMessageCount());
    +  assertEquals(0, messageCopy.repeatedNestedEnumCount());
    +
    +  assertEquals(101, messageCopy.getOptionalInt32());
    +  assertEquals('102', messageCopy.getOptionalInt64());
    +  assertEquals(103, messageCopy.getOptionalUint32());
    +  assertEquals('104', messageCopy.getOptionalUint64());
    +  assertEquals(105, messageCopy.getOptionalSint32());
    +  assertEquals('106', messageCopy.getOptionalSint64());
    +  assertEquals(107, messageCopy.getOptionalFixed32());
    +  assertEquals('108', messageCopy.getOptionalFixed64());
    +  assertEquals(109, messageCopy.getOptionalSfixed32());
    +  assertEquals('110', messageCopy.getOptionalSfixed64());
    +  assertEquals(111.5, messageCopy.getOptionalFloat());
    +  assertEquals(112.5, messageCopy.getOptionalDouble());
    +  assertEquals(true, messageCopy.getOptionalBool());
    +  assertEquals('test', messageCopy.getOptionalString());
    +  assertEquals('abcd', messageCopy.getOptionalBytes());
    +  assertEquals(111, messageCopy.getOptionalgroup().getA());
    +
    +  assertEquals(112, messageCopy.getOptionalNestedMessage().getB());
    +
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO,
    +      messageCopy.getOptionalNestedEnum());
    +
    +  assertEquals(201, messageCopy.getRepeatedInt32(0));
    +  assertEquals(202, messageCopy.getRepeatedInt32(1));
    +}
    +
    +function testMergeFromLazyTarget() {
    +  var serializer = new goog.proto2.PbLiteSerializer();
    +
    +  var source = new proto2.TestAllTypes();
    +  var nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(66);
    +  source.setOptionalNestedMessage(nested);
    +  source.setOptionalInt32(32);
    +  source.setOptionalString('foo');
    +  source.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  source.addRepeatedInt32(2);
    +
    +  var target = new proto2.TestAllTypes();
    +  nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setC(77);
    +  target.setOptionalNestedMessage(nested);
    +  target.setOptionalInt64('64');
    +  target.setOptionalString('bar');
    +  target.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.BAR);
    +  target.addRepeatedInt32(1);
    +  var pbliteTarget = serializer.serialize(target);
    +  var lazyTarget =
    +      serializer.deserialize(proto2.TestAllTypes.getDescriptor(), pbliteTarget);
    +
    +  var expected = new proto2.TestAllTypes();
    +  nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(66);
    +  nested.setC(77);
    +  expected.setOptionalNestedMessage(nested);
    +  expected.setOptionalInt32(32);
    +  expected.setOptionalInt64('64');
    +  expected.setOptionalString('foo');
    +  expected.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  expected.addRepeatedInt32(1);
    +  expected.addRepeatedInt32(2);
    +
    +  lazyTarget.mergeFrom(source);
    +  assertTrue('expected and lazyTarget are equal after mergeFrom',
    +      lazyTarget.equals(expected));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/proto_test.html b/mapbox-gl/closure-library/closure/goog/proto2/proto_test.html
    new file mode 100644
    index 0000000000..394e590d95
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/proto_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.proto2 - Message Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.proto2.messageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/proto_test.js b/mapbox-gl/closure-library/closure/goog/proto2/proto_test.js
    new file mode 100644
    index 0000000000..374a072b31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/proto_test.js
    @@ -0,0 +1,755 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.proto2.messageTest');
    +goog.setTestOnly('goog.proto2.messageTest');
    +
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.testing.jsunit');
    +goog.require('proto2.TestAllTypes');
    +goog.require('someprotopackage.TestPackageTypes');
    +
    +function testPackage() {
    +  var message = new someprotopackage.TestPackageTypes();
    +  message.setOptionalInt32(45);
    +  message.setOtherAll(new proto2.TestAllTypes());
    +}
    +
    +function testFields() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Ensure that the fields are not set.
    +  assertFalse(message.hasOptionalInt32());
    +  assertFalse(message.hasOptionalInt64());
    +  assertFalse(message.hasOptionalUint32());
    +  assertFalse(message.hasOptionalUint64());
    +  assertFalse(message.hasOptionalSint32());
    +  assertFalse(message.hasOptionalSint64());
    +  assertFalse(message.hasOptionalFixed32());
    +  assertFalse(message.hasOptionalFixed64());
    +  assertFalse(message.hasOptionalSfixed32());
    +  assertFalse(message.hasOptionalSfixed64());
    +  assertFalse(message.hasOptionalFloat());
    +  assertFalse(message.hasOptionalDouble());
    +  assertFalse(message.hasOptionalBool());
    +  assertFalse(message.hasOptionalString());
    +  assertFalse(message.hasOptionalBytes());
    +  assertFalse(message.hasOptionalgroup());
    +  assertFalse(message.hasOptionalNestedMessage());
    +  assertFalse(message.hasOptionalNestedEnum());
    +
    +  // Check non-set values.
    +  assertNull(message.getOptionalInt32());
    +  assertNull(message.getOptionalInt64());
    +  assertNull(message.getOptionalFloat());
    +  assertNull(message.getOptionalString());
    +  assertNull(message.getOptionalBytes());
    +  assertNull(message.getOptionalNestedMessage());
    +  assertNull(message.getOptionalNestedEnum());
    +
    +  // Check default values.
    +  assertEquals(0, message.getOptionalInt32OrDefault());
    +  assertEquals('1', message.getOptionalInt64OrDefault());
    +  assertEquals(1.5, message.getOptionalFloatOrDefault());
    +  assertEquals('', message.getOptionalStringOrDefault());
    +  assertEquals('moo', message.getOptionalBytesOrDefault());
    +
    +  // Set the fields.
    +  message.setOptionalInt32(101);
    +  message.setOptionalInt64('102');
    +  message.setOptionalUint32(103);
    +  message.setOptionalUint64('104');
    +  message.setOptionalSint32(105);
    +  message.setOptionalSint64('106');
    +  message.setOptionalFixed32(107);
    +  message.setOptionalFixed64('108');
    +  message.setOptionalSfixed32(109);
    +  message.setOptionalSfixed64('110');
    +  message.setOptionalFloat(111.5);
    +  message.setOptionalDouble(112.5);
    +  message.setOptionalBool(true);
    +  message.setOptionalString('test');
    +  message.setOptionalBytes('abcd');
    +
    +  var group = new proto2.TestAllTypes.OptionalGroup();
    +  group.setA(111);
    +
    +  message.setOptionalgroup(group);
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(112);
    +
    +  message.setOptionalNestedMessage(nestedMessage);
    +
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  // Ensure that the fields are set.
    +  assertTrue(message.hasOptionalInt32());
    +  assertTrue(message.hasOptionalInt64());
    +  assertTrue(message.hasOptionalUint32());
    +  assertTrue(message.hasOptionalUint64());
    +  assertTrue(message.hasOptionalSint32());
    +  assertTrue(message.hasOptionalSint64());
    +  assertTrue(message.hasOptionalFixed32());
    +  assertTrue(message.hasOptionalFixed64());
    +  assertTrue(message.hasOptionalSfixed32());
    +  assertTrue(message.hasOptionalSfixed64());
    +  assertTrue(message.hasOptionalFloat());
    +  assertTrue(message.hasOptionalDouble());
    +  assertTrue(message.hasOptionalBool());
    +  assertTrue(message.hasOptionalString());
    +  assertTrue(message.hasOptionalBytes());
    +  assertTrue(message.hasOptionalgroup());
    +  assertTrue(message.hasOptionalNestedMessage());
    +  assertTrue(message.hasOptionalNestedEnum());
    +
    +  // Ensure that there is a count of 1 for each of the fields.
    +  assertEquals(1, message.optionalInt32Count());
    +  assertEquals(1, message.optionalInt64Count());
    +  assertEquals(1, message.optionalUint32Count());
    +  assertEquals(1, message.optionalUint64Count());
    +  assertEquals(1, message.optionalSint32Count());
    +  assertEquals(1, message.optionalSint64Count());
    +  assertEquals(1, message.optionalFixed32Count());
    +  assertEquals(1, message.optionalFixed64Count());
    +  assertEquals(1, message.optionalSfixed32Count());
    +  assertEquals(1, message.optionalSfixed64Count());
    +  assertEquals(1, message.optionalFloatCount());
    +  assertEquals(1, message.optionalDoubleCount());
    +  assertEquals(1, message.optionalBoolCount());
    +  assertEquals(1, message.optionalStringCount());
    +  assertEquals(1, message.optionalBytesCount());
    +  assertEquals(1, message.optionalgroupCount());
    +  assertEquals(1, message.optionalNestedMessageCount());
    +  assertEquals(1, message.optionalNestedEnumCount());
    +
    +  // Ensure that the fields have the values expected.
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals('102', message.getOptionalInt64());
    +  assertEquals(103, message.getOptionalUint32());
    +  assertEquals('104', message.getOptionalUint64());
    +  assertEquals(105, message.getOptionalSint32());
    +  assertEquals('106', message.getOptionalSint64());
    +  assertEquals(107, message.getOptionalFixed32());
    +  assertEquals('108', message.getOptionalFixed64());
    +  assertEquals(109, message.getOptionalSfixed32());
    +  assertEquals('110', message.getOptionalSfixed64());
    +  assertEquals(111.5, message.getOptionalFloat());
    +  assertEquals(112.5, message.getOptionalDouble());
    +  assertEquals(true, message.getOptionalBool());
    +  assertEquals('test', message.getOptionalString());
    +  assertEquals('abcd', message.getOptionalBytes());
    +  assertEquals(group, message.getOptionalgroup());
    +  assertEquals(nestedMessage, message.getOptionalNestedMessage());
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO,
    +               message.getOptionalNestedEnum());
    +}
    +
    +function testRepeated() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Ensure that the fields are not set.
    +  assertFalse(message.hasRepeatedInt32());
    +  assertFalse(message.hasRepeatedInt64());
    +  assertFalse(message.hasRepeatedUint32());
    +  assertFalse(message.hasRepeatedUint64());
    +  assertFalse(message.hasRepeatedSint32());
    +  assertFalse(message.hasRepeatedSint64());
    +  assertFalse(message.hasRepeatedFixed32());
    +  assertFalse(message.hasRepeatedFixed64());
    +  assertFalse(message.hasRepeatedSfixed32());
    +  assertFalse(message.hasRepeatedSfixed64());
    +  assertFalse(message.hasRepeatedFloat());
    +  assertFalse(message.hasRepeatedDouble());
    +  assertFalse(message.hasRepeatedBool());
    +  assertFalse(message.hasRepeatedString());
    +  assertFalse(message.hasRepeatedBytes());
    +  assertFalse(message.hasRepeatedgroup());
    +  assertFalse(message.hasRepeatedNestedMessage());
    +  assertFalse(message.hasRepeatedNestedEnum());
    +
    +  // Expect the arrays to be empty.
    +  assertEquals(0, message.repeatedInt32Array().length);
    +  assertEquals(0, message.repeatedInt64Array().length);
    +  assertEquals(0, message.repeatedUint32Array().length);
    +  assertEquals(0, message.repeatedUint64Array().length);
    +  assertEquals(0, message.repeatedSint32Array().length);
    +  assertEquals(0, message.repeatedSint64Array().length);
    +  assertEquals(0, message.repeatedFixed32Array().length);
    +  assertEquals(0, message.repeatedFixed64Array().length);
    +  assertEquals(0, message.repeatedSfixed32Array().length);
    +  assertEquals(0, message.repeatedSfixed64Array().length);
    +  assertEquals(0, message.repeatedFloatArray().length);
    +  assertEquals(0, message.repeatedDoubleArray().length);
    +  assertEquals(0, message.repeatedBoolArray().length);
    +  assertEquals(0, message.repeatedStringArray().length);
    +  assertEquals(0, message.repeatedBytesArray().length);
    +  assertEquals(0, message.repeatedgroupArray().length);
    +  assertEquals(0, message.repeatedNestedMessageArray().length);
    +  assertEquals(0, message.repeatedNestedEnumArray().length);
    +
    +  // Set the fields.
    +  message.addRepeatedInt32(101);
    +  message.addRepeatedInt64('102');
    +  message.addRepeatedUint32(103);
    +  message.addRepeatedUint64('104');
    +  message.addRepeatedSint32(105);
    +  message.addRepeatedSint64('106');
    +  message.addRepeatedFixed32(107);
    +  message.addRepeatedFixed64('108');
    +  message.addRepeatedSfixed32(109);
    +  message.addRepeatedSfixed64('110');
    +  message.addRepeatedFloat(111.5);
    +  message.addRepeatedDouble(112.5);
    +  message.addRepeatedBool(true);
    +  message.addRepeatedString('test');
    +  message.addRepeatedBytes('abcd');
    +
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt64('202');
    +  message.addRepeatedUint32(203);
    +  message.addRepeatedUint64('204');
    +  message.addRepeatedSint32(205);
    +  message.addRepeatedSint64('206');
    +  message.addRepeatedFixed32(207);
    +  message.addRepeatedFixed64('208');
    +  message.addRepeatedSfixed32(209);
    +  message.addRepeatedSfixed64('210');
    +  message.addRepeatedFloat(211.5);
    +  message.addRepeatedDouble(212.5);
    +  message.addRepeatedBool(true);
    +  message.addRepeatedString('test#2');
    +  message.addRepeatedBytes('efgh');
    +
    +
    +  var group1 = new proto2.TestAllTypes.RepeatedGroup();
    +  group1.addA(111);
    +
    +  message.addRepeatedgroup(group1);
    +
    +  var group2 = new proto2.TestAllTypes.RepeatedGroup();
    +  group2.addA(211);
    +
    +  message.addRepeatedgroup(group2);
    +
    +  var nestedMessage1 = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage1.setB(112);
    +  message.addRepeatedNestedMessage(nestedMessage1);
    +
    +  var nestedMessage2 = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage2.setB(212);
    +  message.addRepeatedNestedMessage(nestedMessage2);
    +
    +  message.addRepeatedNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +  message.addRepeatedNestedEnum(proto2.TestAllTypes.NestedEnum.BAR);
    +
    +  // Ensure that the fields are set.
    +  assertTrue(message.hasRepeatedInt32());
    +  assertTrue(message.hasRepeatedInt64());
    +  assertTrue(message.hasRepeatedUint32());
    +  assertTrue(message.hasRepeatedUint64());
    +  assertTrue(message.hasRepeatedSint32());
    +  assertTrue(message.hasRepeatedSint64());
    +  assertTrue(message.hasRepeatedFixed32());
    +  assertTrue(message.hasRepeatedFixed64());
    +  assertTrue(message.hasRepeatedSfixed32());
    +  assertTrue(message.hasRepeatedSfixed64());
    +  assertTrue(message.hasRepeatedFloat());
    +  assertTrue(message.hasRepeatedDouble());
    +  assertTrue(message.hasRepeatedBool());
    +  assertTrue(message.hasRepeatedString());
    +  assertTrue(message.hasRepeatedBytes());
    +  assertTrue(message.hasRepeatedgroup());
    +  assertTrue(message.hasRepeatedNestedMessage());
    +  assertTrue(message.hasRepeatedNestedEnum());
    +
    +  // Ensure that there is a count of 2 for each of the fields.
    +  assertEquals(2, message.repeatedInt32Count());
    +  assertEquals(2, message.repeatedInt64Count());
    +  assertEquals(2, message.repeatedUint32Count());
    +  assertEquals(2, message.repeatedUint64Count());
    +  assertEquals(2, message.repeatedSint32Count());
    +  assertEquals(2, message.repeatedSint64Count());
    +  assertEquals(2, message.repeatedFixed32Count());
    +  assertEquals(2, message.repeatedFixed64Count());
    +  assertEquals(2, message.repeatedSfixed32Count());
    +  assertEquals(2, message.repeatedSfixed64Count());
    +  assertEquals(2, message.repeatedFloatCount());
    +  assertEquals(2, message.repeatedDoubleCount());
    +  assertEquals(2, message.repeatedBoolCount());
    +  assertEquals(2, message.repeatedStringCount());
    +  assertEquals(2, message.repeatedBytesCount());
    +  assertEquals(2, message.repeatedgroupCount());
    +  assertEquals(2, message.repeatedNestedMessageCount());
    +  assertEquals(2, message.repeatedNestedEnumCount());
    +
    +  // Ensure that the fields have the values expected.
    +  assertEquals(101, message.getRepeatedInt32(0));
    +  assertEquals('102', message.getRepeatedInt64(0));
    +  assertEquals(103, message.getRepeatedUint32(0));
    +  assertEquals('104', message.getRepeatedUint64(0));
    +  assertEquals(105, message.getRepeatedSint32(0));
    +  assertEquals('106', message.getRepeatedSint64(0));
    +  assertEquals(107, message.getRepeatedFixed32(0));
    +  assertEquals('108', message.getRepeatedFixed64(0));
    +  assertEquals(109, message.getRepeatedSfixed32(0));
    +  assertEquals('110', message.getRepeatedSfixed64(0));
    +  assertEquals(111.5, message.getRepeatedFloat(0));
    +  assertEquals(112.5, message.getRepeatedDouble(0));
    +  assertEquals(true, message.getRepeatedBool(0));
    +  assertEquals('test', message.getRepeatedString(0));
    +  assertEquals('abcd', message.getRepeatedBytes(0));
    +  assertEquals(group1, message.getRepeatedgroup(0));
    +  assertEquals(nestedMessage1, message.getRepeatedNestedMessage(0));
    +  assertEquals(proto2.TestAllTypes.NestedEnum.FOO,
    +               message.getRepeatedNestedEnum(0));
    +
    +  assertEquals(201, message.getRepeatedInt32(1));
    +  assertEquals('202', message.getRepeatedInt64(1));
    +  assertEquals(203, message.getRepeatedUint32(1));
    +  assertEquals('204', message.getRepeatedUint64(1));
    +  assertEquals(205, message.getRepeatedSint32(1));
    +  assertEquals('206', message.getRepeatedSint64(1));
    +  assertEquals(207, message.getRepeatedFixed32(1));
    +  assertEquals('208', message.getRepeatedFixed64(1));
    +  assertEquals(209, message.getRepeatedSfixed32(1));
    +  assertEquals('210', message.getRepeatedSfixed64(1));
    +  assertEquals(211.5, message.getRepeatedFloat(1));
    +  assertEquals(212.5, message.getRepeatedDouble(1));
    +  assertEquals(true, message.getRepeatedBool(1));
    +  assertEquals('test#2', message.getRepeatedString(1));
    +  assertEquals('efgh', message.getRepeatedBytes(1));
    +  assertEquals(group2, message.getRepeatedgroup(1));
    +  assertEquals(nestedMessage2, message.getRepeatedNestedMessage(1));
    +  assertEquals(proto2.TestAllTypes.NestedEnum.BAR,
    +               message.getRepeatedNestedEnum(1));
    +
    +  // Check the array lengths.
    +  assertEquals(2, message.repeatedInt32Array().length);
    +  assertEquals(2, message.repeatedInt64Array().length);
    +  assertEquals(2, message.repeatedUint32Array().length);
    +  assertEquals(2, message.repeatedUint64Array().length);
    +  assertEquals(2, message.repeatedSint32Array().length);
    +  assertEquals(2, message.repeatedSint64Array().length);
    +  assertEquals(2, message.repeatedFixed32Array().length);
    +  assertEquals(2, message.repeatedFixed64Array().length);
    +  assertEquals(2, message.repeatedSfixed32Array().length);
    +  assertEquals(2, message.repeatedSfixed64Array().length);
    +  assertEquals(2, message.repeatedFloatArray().length);
    +  assertEquals(2, message.repeatedDoubleArray().length);
    +  assertEquals(2, message.repeatedBoolArray().length);
    +  assertEquals(2, message.repeatedStringArray().length);
    +  assertEquals(2, message.repeatedBytesArray().length);
    +  assertEquals(2, message.repeatedgroupArray().length);
    +  assertEquals(2, message.repeatedNestedMessageArray().length);
    +  assertEquals(2, message.repeatedNestedEnumArray().length);
    +
    +  // Check the array values.
    +  assertEquals(message.getRepeatedInt32(0), message.repeatedInt32Array()[0]);
    +  assertEquals(message.getRepeatedInt64(0), message.repeatedInt64Array()[0]);
    +  assertEquals(message.getRepeatedUint32(0), message.repeatedUint32Array()[0]);
    +  assertEquals(message.getRepeatedUint64(0), message.repeatedUint64Array()[0]);
    +  assertEquals(message.getRepeatedSint32(0), message.repeatedSint32Array()[0]);
    +  assertEquals(message.getRepeatedSint64(0), message.repeatedSint64Array()[0]);
    +  assertEquals(message.getRepeatedFixed32(0),
    +               message.repeatedFixed32Array()[0]);
    +  assertEquals(message.getRepeatedFixed64(0),
    +               message.repeatedFixed64Array()[0]);
    +  assertEquals(message.getRepeatedSfixed32(0),
    +               message.repeatedSfixed32Array()[0]);
    +  assertEquals(message.getRepeatedSfixed64(0),
    +               message.repeatedSfixed64Array()[0]);
    +  assertEquals(message.getRepeatedFloat(0), message.repeatedFloatArray()[0]);
    +  assertEquals(message.getRepeatedDouble(0), message.repeatedDoubleArray()[0]);
    +  assertEquals(message.getRepeatedBool(0), message.repeatedBoolArray()[0]);
    +  assertEquals(message.getRepeatedString(0), message.repeatedStringArray()[0]);
    +  assertEquals(message.getRepeatedBytes(0), message.repeatedBytesArray()[0]);
    +  assertEquals(message.getRepeatedgroup(0), message.repeatedgroupArray()[0]);
    +  assertEquals(message.getRepeatedNestedMessage(0),
    +               message.repeatedNestedMessageArray()[0]);
    +  assertEquals(message.getRepeatedNestedEnum(0),
    +               message.repeatedNestedEnumArray()[0]);
    +
    +  assertEquals(message.getRepeatedInt32(1), message.repeatedInt32Array()[1]);
    +  assertEquals(message.getRepeatedInt64(1), message.repeatedInt64Array()[1]);
    +  assertEquals(message.getRepeatedUint32(1), message.repeatedUint32Array()[1]);
    +  assertEquals(message.getRepeatedUint64(1), message.repeatedUint64Array()[1]);
    +  assertEquals(message.getRepeatedSint32(1), message.repeatedSint32Array()[1]);
    +  assertEquals(message.getRepeatedSint64(1), message.repeatedSint64Array()[1]);
    +  assertEquals(message.getRepeatedFixed32(1),
    +               message.repeatedFixed32Array()[1]);
    +  assertEquals(message.getRepeatedFixed64(1),
    +               message.repeatedFixed64Array()[1]);
    +  assertEquals(message.getRepeatedSfixed32(1),
    +               message.repeatedSfixed32Array()[1]);
    +  assertEquals(message.getRepeatedSfixed64(1),
    +               message.repeatedSfixed64Array()[1]);
    +  assertEquals(message.getRepeatedFloat(1), message.repeatedFloatArray()[1]);
    +  assertEquals(message.getRepeatedDouble(1), message.repeatedDoubleArray()[1]);
    +  assertEquals(message.getRepeatedBool(1), message.repeatedBoolArray()[1]);
    +  assertEquals(message.getRepeatedString(1), message.repeatedStringArray()[1]);
    +  assertEquals(message.getRepeatedBytes(1), message.repeatedBytesArray()[1]);
    +  assertEquals(message.getRepeatedgroup(1), message.repeatedgroupArray()[1]);
    +  assertEquals(message.getRepeatedNestedMessage(1),
    +               message.repeatedNestedMessageArray()[1]);
    +  assertEquals(message.getRepeatedNestedEnum(1),
    +               message.repeatedNestedEnumArray()[1]);
    +}
    +
    +function testDescriptor() {
    +  var message = new proto2.TestAllTypes();
    +  var descriptor = message.getDescriptor();
    +
    +  assertEquals('TestAllTypes', descriptor.getName());
    +  assertEquals('TestAllTypes', descriptor.getFullName());
    +  assertEquals(null, descriptor.getContainingType());
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  var nestedDescriptor = nestedMessage.getDescriptor();
    +
    +  assertEquals('NestedMessage', nestedDescriptor.getName());
    +  assertEquals('TestAllTypes.NestedMessage',
    +               nestedDescriptor.getFullName());
    +  assertEquals(descriptor, nestedDescriptor.getContainingType());
    +}
    +
    +function testFieldDescriptor() {
    +  var message = new proto2.TestAllTypes();
    +  var descriptor = message.getDescriptor();
    +  var fields = descriptor.getFields();
    +
    +  assertEquals(53, fields.length);
    +
    +  // Check the containing types.
    +  for (var i = 0; i < fields.length; ++i) {
    +    assertEquals(descriptor, fields[i].getContainingType());
    +  }
    +
    +  // Check the field names.
    +  assertEquals('optional_int32', fields[0].getName());
    +  assertEquals('optional_int64', fields[1].getName());
    +  assertEquals('optional_uint32', fields[2].getName());
    +  assertEquals('optional_uint64', fields[3].getName());
    +  assertEquals('optional_sint32', fields[4].getName());
    +  assertEquals('optional_sint64', fields[5].getName());
    +  assertEquals('optional_fixed32', fields[6].getName());
    +  assertEquals('optional_fixed64', fields[7].getName());
    +  assertEquals('optional_sfixed32', fields[8].getName());
    +  assertEquals('optional_sfixed64', fields[9].getName());
    +  assertEquals('optional_float', fields[10].getName());
    +  assertEquals('optional_double', fields[11].getName());
    +  assertEquals('optional_bool', fields[12].getName());
    +  assertEquals('optional_string', fields[13].getName());
    +  assertEquals('optional_bytes', fields[14].getName());
    +  assertEquals('optionalgroup', fields[15].getName());
    +  assertEquals('optional_nested_message', fields[16].getName());
    +  assertEquals('optional_nested_enum', fields[17].getName());
    +
    +  assertEquals('repeated_int32', fields[18].getName());
    +  assertEquals('repeated_int64', fields[19].getName());
    +  assertEquals('repeated_uint32', fields[20].getName());
    +  assertEquals('repeated_uint64', fields[21].getName());
    +  assertEquals('repeated_sint32', fields[22].getName());
    +  assertEquals('repeated_sint64', fields[23].getName());
    +  assertEquals('repeated_fixed32', fields[24].getName());
    +  assertEquals('repeated_fixed64', fields[25].getName());
    +  assertEquals('repeated_sfixed32', fields[26].getName());
    +  assertEquals('repeated_sfixed64', fields[27].getName());
    +  assertEquals('repeated_float', fields[28].getName());
    +  assertEquals('repeated_double', fields[29].getName());
    +  assertEquals('repeated_bool', fields[30].getName());
    +  assertEquals('repeated_string', fields[31].getName());
    +  assertEquals('repeated_bytes', fields[32].getName());
    +  assertEquals('repeatedgroup', fields[33].getName());
    +  assertEquals('repeated_nested_message', fields[34].getName());
    +  assertEquals('repeated_nested_enum', fields[35].getName());
    +
    +  assertEquals('optional_int64_number', fields[36].getName());
    +  assertEquals('optional_int64_string', fields[37].getName());
    +  assertEquals('repeated_int64_number', fields[38].getName());
    +  assertEquals('repeated_int64_string', fields[39].getName());
    +
    +  assertEquals('packed_int32', fields[40].getName());
    +  assertEquals('packed_int64', fields[41].getName());
    +  assertEquals('packed_uint32', fields[42].getName());
    +  assertEquals('packed_uint64', fields[43].getName());
    +  assertEquals('packed_sint32', fields[44].getName());
    +  assertEquals('packed_sint64', fields[45].getName());
    +  assertEquals('packed_fixed32', fields[46].getName());
    +  assertEquals('packed_fixed64', fields[47].getName());
    +  assertEquals('packed_sfixed32', fields[48].getName());
    +  assertEquals('packed_sfixed64', fields[49].getName());
    +  assertEquals('packed_float', fields[50].getName());
    +  assertEquals('packed_double', fields[51].getName());
    +  assertEquals('packed_bool', fields[52].getName());
    +
    +  // Check the field types.
    +  var FieldType = goog.proto2.FieldDescriptor.FieldType;
    +  assertEquals(FieldType.INT32, fields[0].getFieldType());
    +  assertEquals(FieldType.INT64, fields[1].getFieldType());
    +  assertEquals(FieldType.UINT32, fields[2].getFieldType());
    +  assertEquals(FieldType.UINT64, fields[3].getFieldType());
    +  assertEquals(FieldType.SINT32, fields[4].getFieldType());
    +  assertEquals(FieldType.SINT64, fields[5].getFieldType());
    +  assertEquals(FieldType.FIXED32, fields[6].getFieldType());
    +  assertEquals(FieldType.FIXED64, fields[7].getFieldType());
    +  assertEquals(FieldType.SFIXED32, fields[8].getFieldType());
    +  assertEquals(FieldType.SFIXED64, fields[9].getFieldType());
    +  assertEquals(FieldType.FLOAT, fields[10].getFieldType());
    +  assertEquals(FieldType.DOUBLE, fields[11].getFieldType());
    +  assertEquals(FieldType.BOOL, fields[12].getFieldType());
    +  assertEquals(FieldType.STRING, fields[13].getFieldType());
    +  assertEquals(FieldType.BYTES, fields[14].getFieldType());
    +  assertEquals(FieldType.GROUP, fields[15].getFieldType());
    +  assertEquals(FieldType.MESSAGE, fields[16].getFieldType());
    +  assertEquals(FieldType.ENUM, fields[17].getFieldType());
    +
    +  assertEquals(FieldType.INT32, fields[18].getFieldType());
    +  assertEquals(FieldType.INT64, fields[19].getFieldType());
    +  assertEquals(FieldType.UINT32, fields[20].getFieldType());
    +  assertEquals(FieldType.UINT64, fields[21].getFieldType());
    +  assertEquals(FieldType.SINT32, fields[22].getFieldType());
    +  assertEquals(FieldType.SINT64, fields[23].getFieldType());
    +  assertEquals(FieldType.FIXED32, fields[24].getFieldType());
    +  assertEquals(FieldType.FIXED64, fields[25].getFieldType());
    +  assertEquals(FieldType.SFIXED32, fields[26].getFieldType());
    +  assertEquals(FieldType.SFIXED64, fields[27].getFieldType());
    +  assertEquals(FieldType.FLOAT, fields[28].getFieldType());
    +  assertEquals(FieldType.DOUBLE, fields[29].getFieldType());
    +  assertEquals(FieldType.BOOL, fields[30].getFieldType());
    +  assertEquals(FieldType.STRING, fields[31].getFieldType());
    +  assertEquals(FieldType.BYTES, fields[32].getFieldType());
    +  assertEquals(FieldType.GROUP, fields[33].getFieldType());
    +  assertEquals(FieldType.MESSAGE, fields[34].getFieldType());
    +  assertEquals(FieldType.ENUM, fields[35].getFieldType());
    +
    +  assertEquals(FieldType.INT64, fields[36].getFieldType());
    +  assertEquals(FieldType.INT64, fields[37].getFieldType());
    +  assertEquals(FieldType.INT64, fields[38].getFieldType());
    +  assertEquals(FieldType.INT64, fields[39].getFieldType());
    +
    +  assertEquals(FieldType.INT32, fields[40].getFieldType());
    +  assertEquals(FieldType.INT64, fields[41].getFieldType());
    +  assertEquals(FieldType.UINT32, fields[42].getFieldType());
    +  assertEquals(FieldType.UINT64, fields[43].getFieldType());
    +  assertEquals(FieldType.SINT32, fields[44].getFieldType());
    +  assertEquals(FieldType.SINT64, fields[45].getFieldType());
    +  assertEquals(FieldType.FIXED32, fields[46].getFieldType());
    +  assertEquals(FieldType.FIXED64, fields[47].getFieldType());
    +  assertEquals(FieldType.SFIXED32, fields[48].getFieldType());
    +  assertEquals(FieldType.SFIXED64, fields[49].getFieldType());
    +  assertEquals(FieldType.FLOAT, fields[50].getFieldType());
    +  assertEquals(FieldType.DOUBLE, fields[51].getFieldType());
    +  assertEquals(FieldType.BOOL, fields[52].getFieldType());
    +
    +  // Check the field native types.
    +  // Singular.
    +  assertEquals(Number, fields[0].getNativeType());
    +  assertEquals(String, fields[1].getNativeType()); // 64 bit values are strings.
    +  assertEquals(Number, fields[2].getNativeType());
    +  assertEquals(String, fields[3].getNativeType());
    +  assertEquals(Number, fields[4].getNativeType());
    +  assertEquals(String, fields[5].getNativeType());
    +  assertEquals(Number, fields[6].getNativeType());
    +  assertEquals(String, fields[7].getNativeType());
    +  assertEquals(Number, fields[8].getNativeType());
    +  assertEquals(String, fields[9].getNativeType());
    +  assertEquals(Number, fields[10].getNativeType());
    +  assertEquals(Number, fields[11].getNativeType());
    +
    +  assertEquals(Boolean, fields[12].getNativeType());
    +
    +  assertEquals(String, fields[13].getNativeType());
    +  assertEquals(String, fields[14].getNativeType());
    +
    +  assertEquals(proto2.TestAllTypes.OptionalGroup, fields[15].getNativeType());
    +  assertEquals(proto2.TestAllTypes.NestedMessage, fields[16].getNativeType());
    +  assertEquals(proto2.TestAllTypes.NestedEnum, fields[17].getNativeType());
    +
    +  assertEquals(Number, fields[36].getNativeType());  // [jstype="number"]
    +  assertEquals(String, fields[37].getNativeType());
    +
    +  // Repeated.
    +  assertEquals(Number, fields[18].getNativeType());
    +  assertEquals(String, fields[19].getNativeType());
    +  assertEquals(Number, fields[20].getNativeType());
    +  assertEquals(String, fields[21].getNativeType());
    +  assertEquals(Number, fields[22].getNativeType());
    +  assertEquals(String, fields[23].getNativeType());
    +  assertEquals(Number, fields[24].getNativeType());
    +  assertEquals(String, fields[25].getNativeType());
    +  assertEquals(Number, fields[26].getNativeType());
    +  assertEquals(String, fields[27].getNativeType());
    +  assertEquals(Number, fields[28].getNativeType());
    +  assertEquals(Number, fields[29].getNativeType());
    +
    +  assertEquals(Boolean, fields[30].getNativeType());
    +
    +  assertEquals(String, fields[31].getNativeType());
    +  assertEquals(String, fields[32].getNativeType());
    +
    +  assertEquals(proto2.TestAllTypes.RepeatedGroup, fields[33].getNativeType());
    +  assertEquals(proto2.TestAllTypes.NestedMessage, fields[34].getNativeType());
    +  assertEquals(proto2.TestAllTypes.NestedEnum, fields[35].getNativeType());
    +
    +  assertEquals(Number, fields[38].getNativeType());  // [jstype="number"]
    +  assertEquals(String, fields[39].getNativeType());
    +
    +  // Packed (only numeric types can be packed).
    +  assertEquals(Number, fields[40].getNativeType());
    +  assertEquals(Number, fields[41].getNativeType());
    +  assertEquals(Number, fields[42].getNativeType());
    +  assertEquals(Number, fields[43].getNativeType());
    +  assertEquals(Number, fields[44].getNativeType());
    +  assertEquals(Number, fields[45].getNativeType());
    +  assertEquals(Number, fields[46].getNativeType());
    +  assertEquals(Number, fields[47].getNativeType());
    +  assertEquals(Number, fields[48].getNativeType());
    +  assertEquals(Number, fields[49].getNativeType());
    +  assertEquals(Number, fields[50].getNativeType());
    +  assertEquals(Number, fields[51].getNativeType());
    +  assertEquals(Boolean, fields[52].getNativeType());
    +}
    +
    +function testUnknown() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set some unknown fields.
    +  message.setUnknown(1000, 101);
    +  message.setUnknown(1001, -102);
    +  message.setUnknown(1002, true);
    +  message.setUnknown(1003, 'abcd');
    +  message.setUnknown(1004, ['he', 'llo']);
    +
    +  // Ensure we find them all.
    +  var count = 0;
    +
    +  message.forEachUnknown(function(tag, value) {
    +    if (tag == 1000) {
    +      assertEquals(101, value);
    +    }
    +
    +    if (tag == 1001) {
    +      assertEquals(-102, value);
    +    }
    +
    +    if (tag == 1002) {
    +      assertEquals(true, value);
    +    }
    +
    +    if (tag == 1003) {
    +      assertEquals('abcd', value);
    +    }
    +
    +    if (tag == 1004) {
    +      assertEquals('he', value[0]);
    +      assertEquals('llo', value[1]);
    +    }
    +
    +    count++;
    +  });
    +
    +  assertEquals(5, count);
    +}
    +
    +function testReflection() {
    +  var message = new proto2.TestAllTypes();
    +  var descriptor = message.getDescriptor();
    +  var optionalInt = descriptor.findFieldByName('optional_int32');
    +  var optionalString = descriptor.findFieldByName('optional_string');
    +  var repeatedInt64 = descriptor.findFieldByName('repeated_int64');
    +  var optionalWrong = descriptor.findFieldByName('foo_bar');
    +
    +  assertFalse(optionalInt == null);
    +  assertFalse(optionalString == null);
    +  assertFalse(repeatedInt64 == null);
    +  assertTrue(optionalWrong == null);
    +
    +  // Check to ensure the fields are empty.
    +  assertFalse(message.has(optionalInt));
    +  assertFalse(message.has(optionalString));
    +  assertFalse(message.has(repeatedInt64));
    +
    +  assertEquals(0, message.arrayOf(repeatedInt64).length);
    +
    +  // Check default values.
    +  assertEquals(0, message.getOrDefault(optionalInt));
    +  assertEquals('', message.getOrDefault(optionalString));
    +
    +  // Set some of the fields.
    +  message.set(optionalString, 'hello!');
    +
    +  message.add(repeatedInt64, '101');
    +  message.add(repeatedInt64, '102');
    +
    +  // Check the fields.
    +  assertFalse(message.has(optionalInt));
    +
    +  assertTrue(message.has(optionalString));
    +  assertTrue(message.hasOptionalString());
    +
    +  assertTrue(message.has(repeatedInt64));
    +  assertTrue(message.hasRepeatedInt64());
    +
    +  // Check the values.
    +  assertEquals('hello!', message.get(optionalString));
    +  assertEquals('hello!', message.getOptionalString());
    +
    +  assertEquals('101', message.get(repeatedInt64, 0));
    +  assertEquals('102', message.get(repeatedInt64, 1));
    +
    +  assertEquals('101', message.getRepeatedInt64(0));
    +  assertEquals('102', message.getRepeatedInt64(1));
    +
    +  // Check the count.
    +  assertEquals(0, message.countOf(optionalInt));
    +
    +  assertEquals(1, message.countOf(optionalString));
    +  assertEquals(1, message.optionalStringCount());
    +
    +  assertEquals(2, message.countOf(repeatedInt64));
    +  assertEquals(2, message.repeatedInt64Count());
    +
    +  // Check the array.
    +  assertEquals(2, message.arrayOf(repeatedInt64).length);
    +
    +  assertEquals(message.get(repeatedInt64, 0),
    +      message.arrayOf(repeatedInt64)[0]);
    +
    +  assertEquals(message.get(repeatedInt64, 1),
    +      message.arrayOf(repeatedInt64)[1]);
    +}
    +
    +function testDefaultValuesForMessages() {
    +  var message = new proto2.TestDefaultParent();
    +  // Ideally this object would be immutable, but the current API does not
    +  // enforce that behavior, so get**OrDefault returns a new instance every time.
    +  var child = message.getChildOrDefault();
    +  child.setFoo(false);
    +  // Changing the value returned by get**OrDefault does not actually change
    +  // the value stored in the parent message.
    +  assertFalse(message.hasChild());
    +  assertNull(message.getChild());
    +
    +  var message2 = new proto2.TestDefaultParent();
    +  var child2 = message2.getChildOrDefault();
    +  assertNull(message2.getChild());
    +
    +  // The parent message returns a different object for the default.
    +  assertNotEquals(child, child2);
    +
    +  // You've only changed the value of child, so child2 should be unaffected.
    +  assertFalse(child2.hasFoo());
    +  assertTrue(child2.getFooOrDefault());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/serializer.js b/mapbox-gl/closure-library/closure/goog/proto2/serializer.js
    new file mode 100644
    index 0000000000..37828134fd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/serializer.js
    @@ -0,0 +1,182 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for all Protocol Buffer 2 serializers.
    + */
    +
    +goog.provide('goog.proto2.Serializer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.proto2.Message');
    +
    +
    +
    +/**
    + * Abstract base class for PB2 serializers. A serializer is a class which
    + * implements the serialization and deserialization of a Protocol Buffer Message
    + * to/from a specific format.
    + *
    + * @constructor
    + */
    +goog.proto2.Serializer = function() {};
    +
    +
    +/**
    + * @define {boolean} Whether to decode and convert symbolic enum values to
    + * actual enum values or leave them as strings.
    + */
    +goog.define('goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS', false);
    +
    +
    +/**
    + * Serializes a message to the expected format.
    + *
    + * @param {goog.proto2.Message} message The message to be serialized.
    + *
    + * @return {*} The serialized form of the message.
    + */
    +goog.proto2.Serializer.prototype.serialize = goog.abstractMethod;
    +
    +
    +/**
    + * Returns the serialized form of the given value for the given field if the
    + * field is a Message or Group and returns the value unchanged otherwise, except
    + * for Infinity, -Infinity and NaN numerical values which are converted to
    + * string representation.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field from which this
    + *     value came.
    + *
    + * @param {*} value The value of the field.
    + *
    + * @return {*} The value.
    + * @protected
    + */
    +goog.proto2.Serializer.prototype.getSerializedValue = function(field, value) {
    +  if (field.isCompositeType()) {
    +    return this.serialize(/** @type {goog.proto2.Message} */ (value));
    +  } else if (goog.isNumber(value) && !isFinite(value)) {
    +    return value.toString();
    +  } else {
    +    return value;
    +  }
    +};
    +
    +
    +/**
    + * Deserializes a message from the expected format.
    + *
    + * @param {goog.proto2.Descriptor} descriptor The descriptor of the message
    + *     to be created.
    + * @param {*} data The data of the message.
    + *
    + * @return {!goog.proto2.Message} The message created.
    + */
    +goog.proto2.Serializer.prototype.deserialize = function(descriptor, data) {
    +  var message = descriptor.createMessageInstance();
    +  this.deserializeTo(message, data);
    +  goog.asserts.assert(message instanceof goog.proto2.Message);
    +  return message;
    +};
    +
    +
    +/**
    + * Deserializes a message from the expected format and places the
    + * data in the message.
    + *
    + * @param {goog.proto2.Message} message The message in which to
    + *     place the information.
    + * @param {*} data The data of the message.
    + */
    +goog.proto2.Serializer.prototype.deserializeTo = goog.abstractMethod;
    +
    +
    +/**
    + * Returns the deserialized form of the given value for the given field if the
    + * field is a Message or Group and returns the value, converted or unchanged,
    + * for primitive field types otherwise.
    + *
    + * @param {goog.proto2.FieldDescriptor} field The field from which this
    + *     value came.
    + *
    + * @param {*} value The value of the field.
    + *
    + * @return {*} The value.
    + * @protected
    + */
    +goog.proto2.Serializer.prototype.getDeserializedValue = function(field, value) {
    +  // Composite types are deserialized recursively.
    +  if (field.isCompositeType()) {
    +    if (value instanceof goog.proto2.Message) {
    +      return value;
    +    }
    +
    +    return this.deserialize(field.getFieldMessageType(), value);
    +  }
    +
    +  // Decode enum values.
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) {
    +    // If it's a string, get enum value by name.
    +    // NB: In order this feature to work, property renaming should be turned off
    +    // for the respective enums.
    +    if (goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS && goog.isString(value)) {
    +      // enumType is a regular Javascript enum as defined in field's metadata.
    +      var enumType = field.getNativeType();
    +      if (enumType.hasOwnProperty(value)) {
    +        return enumType[value];
    +      }
    +    }
    +    // Return unknown values as is for backward compatibility.
    +    return value;
    +  }
    +
    +  // Return the raw value if the field does not allow the JSON input to be
    +  // converted.
    +  if (!field.deserializationConversionPermitted()) {
    +    return value;
    +  }
    +
    +  // Convert to native type of field.  Return the converted value or fall
    +  // through to return the raw value.  The JSON encoding of int64 value 123
    +  // might be either the number 123 or the string "123".  The field native type
    +  // could be either Number or String (depending on field options in the .proto
    +  // file).  All four combinations should work correctly.
    +  var nativeType = field.getNativeType();
    +  if (nativeType === String) {
    +    // JSON numbers can be converted to strings.
    +    if (goog.isNumber(value)) {
    +      return String(value);
    +    }
    +  } else if (nativeType === Number) {
    +    // JSON strings are sometimes used for large integer numeric values, as well
    +    // as Infinity, -Infinity and NaN.
    +    if (goog.isString(value)) {
    +      // Handle +/- Infinity and NaN values.
    +      if (value === 'Infinity' || value === '-Infinity' || value === 'NaN') {
    +        return Number(value);
    +      }
    +
    +      // Validate the string.  If the string is not an integral number, we would
    +      // rather have an assertion or error in the caller than a mysterious NaN
    +      // value.
    +      if (/^-?[0-9]+$/.test(value)) {
    +        return Number(value);
    +      }
    +    }
    +  }
    +
    +  return value;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/test.pb.js b/mapbox-gl/closure-library/closure/goog/proto2/test.pb.js
    new file mode 100644
    index 0000000000..5832edaed6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/test.pb.js
    @@ -0,0 +1,4028 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All other code copyright its respective owners(s).
    +
    +/**
    + * @fileoverview Generated Protocol Buffer code for file
    + * closure/goog/proto2/test.proto.
    + */
    +
    +goog.provide('proto2.TestAllTypes');
    +goog.provide('proto2.TestAllTypes.NestedMessage');
    +goog.provide('proto2.TestAllTypes.OptionalGroup');
    +goog.provide('proto2.TestAllTypes.RepeatedGroup');
    +goog.provide('proto2.TestAllTypes.NestedEnum');
    +goog.provide('proto2.TestDefaultParent');
    +goog.provide('proto2.TestDefaultChild');
    +
    +goog.require('goog.proto2.Message');
    +
    +
    +
    +/**
    + * Message TestAllTypes.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestAllTypes = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestAllTypes, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestAllTypes} The cloned message.
    + * @override
    + */
    +proto2.TestAllTypes.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the optional_int32 field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt32 = function() {
    +  return /** @type {?number} */ (this.get$Value(1));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(1));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_int32 field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalInt32 = function(value) {
    +  this.set$Value(1, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_int32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalInt32 = function() {
    +  return this.has$Value(1);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_int32 field.
    + */
    +proto2.TestAllTypes.prototype.optionalInt32Count = function() {
    +  return this.count$Values(1);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_int32 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalInt32 = function() {
    +  this.clear$Field(1);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64 field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64 = function() {
    +  return /** @type {?string} */ (this.get$Value(2));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64 field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64OrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(2));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_int64 field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalInt64 = function(value) {
    +  this.set$Value(2, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_int64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalInt64 = function() {
    +  return this.has$Value(2);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_int64 field.
    + */
    +proto2.TestAllTypes.prototype.optionalInt64Count = function() {
    +  return this.count$Values(2);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_int64 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalInt64 = function() {
    +  this.clear$Field(2);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_uint32 field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalUint32 = function() {
    +  return /** @type {?number} */ (this.get$Value(3));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_uint32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalUint32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(3));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_uint32 field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalUint32 = function(value) {
    +  this.set$Value(3, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_uint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalUint32 = function() {
    +  return this.has$Value(3);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.optionalUint32Count = function() {
    +  return this.count$Values(3);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalUint32 = function() {
    +  this.clear$Field(3);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_uint64 field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalUint64 = function() {
    +  return /** @type {?string} */ (this.get$Value(4));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_uint64 field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalUint64OrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(4));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_uint64 field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalUint64 = function(value) {
    +  this.set$Value(4, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_uint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalUint64 = function() {
    +  return this.has$Value(4);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.optionalUint64Count = function() {
    +  return this.count$Values(4);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalUint64 = function() {
    +  this.clear$Field(4);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sint32 field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSint32 = function() {
    +  return /** @type {?number} */ (this.get$Value(5));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sint32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSint32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(5));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_sint32 field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalSint32 = function(value) {
    +  this.set$Value(5, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_sint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalSint32 = function() {
    +  return this.has$Value(5);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.optionalSint32Count = function() {
    +  return this.count$Values(5);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalSint32 = function() {
    +  this.clear$Field(5);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sint64 field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSint64 = function() {
    +  return /** @type {?string} */ (this.get$Value(6));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sint64 field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSint64OrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(6));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_sint64 field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalSint64 = function(value) {
    +  this.set$Value(6, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_sint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalSint64 = function() {
    +  return this.has$Value(6);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.optionalSint64Count = function() {
    +  return this.count$Values(6);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalSint64 = function() {
    +  this.clear$Field(6);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_fixed32 field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFixed32 = function() {
    +  return /** @type {?number} */ (this.get$Value(7));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_fixed32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFixed32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(7));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_fixed32 field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalFixed32 = function(value) {
    +  this.set$Value(7, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_fixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalFixed32 = function() {
    +  return this.has$Value(7);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.optionalFixed32Count = function() {
    +  return this.count$Values(7);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalFixed32 = function() {
    +  this.clear$Field(7);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_fixed64 field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFixed64 = function() {
    +  return /** @type {?string} */ (this.get$Value(8));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_fixed64 field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFixed64OrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(8));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_fixed64 field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalFixed64 = function(value) {
    +  this.set$Value(8, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_fixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalFixed64 = function() {
    +  return this.has$Value(8);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.optionalFixed64Count = function() {
    +  return this.count$Values(8);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalFixed64 = function() {
    +  this.clear$Field(8);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sfixed32 field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSfixed32 = function() {
    +  return /** @type {?number} */ (this.get$Value(9));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sfixed32 field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSfixed32OrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(9));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_sfixed32 field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalSfixed32 = function(value) {
    +  this.set$Value(9, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_sfixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalSfixed32 = function() {
    +  return this.has$Value(9);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.optionalSfixed32Count = function() {
    +  return this.count$Values(9);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalSfixed32 = function() {
    +  this.clear$Field(9);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sfixed64 field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSfixed64 = function() {
    +  return /** @type {?string} */ (this.get$Value(10));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_sfixed64 field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalSfixed64OrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(10));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_sfixed64 field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalSfixed64 = function(value) {
    +  this.set$Value(10, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_sfixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalSfixed64 = function() {
    +  return this.has$Value(10);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.optionalSfixed64Count = function() {
    +  return this.count$Values(10);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalSfixed64 = function() {
    +  this.clear$Field(10);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_float field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFloat = function() {
    +  return /** @type {?number} */ (this.get$Value(11));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_float field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalFloatOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(11));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_float field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalFloat = function(value) {
    +  this.set$Value(11, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_float field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalFloat = function() {
    +  return this.has$Value(11);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_float field.
    + */
    +proto2.TestAllTypes.prototype.optionalFloatCount = function() {
    +  return this.count$Values(11);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_float field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalFloat = function() {
    +  this.clear$Field(11);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_double field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalDouble = function() {
    +  return /** @type {?number} */ (this.get$Value(12));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_double field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalDoubleOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(12));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_double field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalDouble = function(value) {
    +  this.set$Value(12, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_double field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalDouble = function() {
    +  return this.has$Value(12);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_double field.
    + */
    +proto2.TestAllTypes.prototype.optionalDoubleCount = function() {
    +  return this.count$Values(12);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_double field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalDouble = function() {
    +  this.clear$Field(12);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_bool field.
    + * @return {?boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalBool = function() {
    +  return /** @type {?boolean} */ (this.get$Value(13));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_bool field or the default value if not set.
    + * @return {boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalBoolOrDefault = function() {
    +  return /** @type {boolean} */ (this.get$ValueOrDefault(13));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_bool field.
    + * @param {boolean} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalBool = function(value) {
    +  this.set$Value(13, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_bool field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalBool = function() {
    +  return this.has$Value(13);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_bool field.
    + */
    +proto2.TestAllTypes.prototype.optionalBoolCount = function() {
    +  return this.count$Values(13);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_bool field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalBool = function() {
    +  this.clear$Field(13);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_string field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalString = function() {
    +  return /** @type {?string} */ (this.get$Value(14));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_string field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalStringOrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(14));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_string field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalString = function(value) {
    +  this.set$Value(14, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_string field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalString = function() {
    +  return this.has$Value(14);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_string field.
    + */
    +proto2.TestAllTypes.prototype.optionalStringCount = function() {
    +  return this.count$Values(14);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_string field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalString = function() {
    +  this.clear$Field(14);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_bytes field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalBytes = function() {
    +  return /** @type {?string} */ (this.get$Value(15));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_bytes field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalBytesOrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(15));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_bytes field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalBytes = function(value) {
    +  this.set$Value(15, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_bytes field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalBytes = function() {
    +  return this.has$Value(15);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_bytes field.
    + */
    +proto2.TestAllTypes.prototype.optionalBytesCount = function() {
    +  return this.count$Values(15);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_bytes field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalBytes = function() {
    +  this.clear$Field(15);
    +};
    +
    +
    +/**
    + * Gets the value of the optionalgroup field.
    + * @return {proto2.TestAllTypes.OptionalGroup} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalgroup = function() {
    +  return /** @type {proto2.TestAllTypes.OptionalGroup} */ (this.get$Value(16));
    +};
    +
    +
    +/**
    + * Gets the value of the optionalgroup field or the default value if not set.
    + * @return {!proto2.TestAllTypes.OptionalGroup} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalgroupOrDefault = function() {
    +  return /** @type {!proto2.TestAllTypes.OptionalGroup} */ (this.get$ValueOrDefault(16));
    +};
    +
    +
    +/**
    + * Sets the value of the optionalgroup field.
    + * @param {!proto2.TestAllTypes.OptionalGroup} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalgroup = function(value) {
    +  this.set$Value(16, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optionalgroup field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalgroup = function() {
    +  return this.has$Value(16);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optionalgroup field.
    + */
    +proto2.TestAllTypes.prototype.optionalgroupCount = function() {
    +  return this.count$Values(16);
    +};
    +
    +
    +/**
    + * Clears the values in the optionalgroup field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalgroup = function() {
    +  this.clear$Field(16);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_nested_message field.
    + * @return {proto2.TestAllTypes.NestedMessage} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalNestedMessage = function() {
    +  return /** @type {proto2.TestAllTypes.NestedMessage} */ (this.get$Value(18));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_nested_message field or the default value if not set.
    + * @return {!proto2.TestAllTypes.NestedMessage} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalNestedMessageOrDefault = function() {
    +  return /** @type {!proto2.TestAllTypes.NestedMessage} */ (this.get$ValueOrDefault(18));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_nested_message field.
    + * @param {!proto2.TestAllTypes.NestedMessage} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalNestedMessage = function(value) {
    +  this.set$Value(18, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_nested_message field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalNestedMessage = function() {
    +  return this.has$Value(18);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_nested_message field.
    + */
    +proto2.TestAllTypes.prototype.optionalNestedMessageCount = function() {
    +  return this.count$Values(18);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_nested_message field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalNestedMessage = function() {
    +  this.clear$Field(18);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_nested_enum field.
    + * @return {?proto2.TestAllTypes.NestedEnum} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalNestedEnum = function() {
    +  return /** @type {?proto2.TestAllTypes.NestedEnum} */ (this.get$Value(21));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_nested_enum field or the default value if not set.
    + * @return {proto2.TestAllTypes.NestedEnum} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalNestedEnumOrDefault = function() {
    +  return /** @type {proto2.TestAllTypes.NestedEnum} */ (this.get$ValueOrDefault(21));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_nested_enum field.
    + * @param {proto2.TestAllTypes.NestedEnum} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalNestedEnum = function(value) {
    +  this.set$Value(21, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_nested_enum field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalNestedEnum = function() {
    +  return this.has$Value(21);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_nested_enum field.
    + */
    +proto2.TestAllTypes.prototype.optionalNestedEnumCount = function() {
    +  return this.count$Values(21);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_nested_enum field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalNestedEnum = function() {
    +  this.clear$Field(21);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64_number field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64Number = function() {
    +  return /** @type {?number} */ (this.get$Value(50));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64_number field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64NumberOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(50));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_int64_number field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalInt64Number = function(value) {
    +  this.set$Value(50, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_int64_number field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalInt64Number = function() {
    +  return this.has$Value(50);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_int64_number field.
    + */
    +proto2.TestAllTypes.prototype.optionalInt64NumberCount = function() {
    +  return this.count$Values(50);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_int64_number field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalInt64Number = function() {
    +  this.clear$Field(50);
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64_string field.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64String = function() {
    +  return /** @type {?string} */ (this.get$Value(51));
    +};
    +
    +
    +/**
    + * Gets the value of the optional_int64_string field or the default value if not set.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getOptionalInt64StringOrDefault = function() {
    +  return /** @type {string} */ (this.get$ValueOrDefault(51));
    +};
    +
    +
    +/**
    + * Sets the value of the optional_int64_string field.
    + * @param {string} value The value.
    + */
    +proto2.TestAllTypes.prototype.setOptionalInt64String = function(value) {
    +  this.set$Value(51, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the optional_int64_string field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasOptionalInt64String = function() {
    +  return this.has$Value(51);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the optional_int64_string field.
    + */
    +proto2.TestAllTypes.prototype.optionalInt64StringCount = function() {
    +  return this.count$Values(51);
    +};
    +
    +
    +/**
    + * Clears the values in the optional_int64_string field.
    + */
    +proto2.TestAllTypes.prototype.clearOptionalInt64String = function() {
    +  this.clear$Field(51);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(31, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(31, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_int32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedInt32 = function(value) {
    +  this.add$Value(31, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_int32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(31));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_int32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedInt32 = function() {
    +  return this.has$Value(31);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_int32 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt32Count = function() {
    +  return this.count$Values(31);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_int32 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedInt32 = function() {
    +  this.clear$Field(31);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64 = function(index) {
    +  return /** @type {?string} */ (this.get$Value(32, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64OrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(32, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_int64 field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedInt64 = function(value) {
    +  this.add$Value(32, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_int64 field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64Array = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(32));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_int64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedInt64 = function() {
    +  return this.has$Value(32);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_int64 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64Count = function() {
    +  return this.count$Values(32);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_int64 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedInt64 = function() {
    +  this.clear$Field(32);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_uint32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedUint32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(33, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_uint32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedUint32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(33, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_uint32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedUint32 = function(value) {
    +  this.add$Value(33, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_uint32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedUint32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(33));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_uint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedUint32 = function() {
    +  return this.has$Value(33);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedUint32Count = function() {
    +  return this.count$Values(33);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedUint32 = function() {
    +  this.clear$Field(33);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_uint64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedUint64 = function(index) {
    +  return /** @type {?string} */ (this.get$Value(34, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_uint64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedUint64OrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(34, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_uint64 field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedUint64 = function(value) {
    +  this.add$Value(34, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_uint64 field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedUint64Array = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(34));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_uint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedUint64 = function() {
    +  return this.has$Value(34);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedUint64Count = function() {
    +  return this.count$Values(34);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedUint64 = function() {
    +  this.clear$Field(34);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sint32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSint32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(35, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sint32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSint32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(35, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_sint32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedSint32 = function(value) {
    +  this.add$Value(35, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_sint32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSint32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(35));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_sint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedSint32 = function() {
    +  return this.has$Value(35);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSint32Count = function() {
    +  return this.count$Values(35);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedSint32 = function() {
    +  this.clear$Field(35);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sint64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSint64 = function(index) {
    +  return /** @type {?string} */ (this.get$Value(36, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sint64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSint64OrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(36, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_sint64 field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedSint64 = function(value) {
    +  this.add$Value(36, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_sint64 field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSint64Array = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(36));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_sint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedSint64 = function() {
    +  return this.has$Value(36);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSint64Count = function() {
    +  return this.count$Values(36);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedSint64 = function() {
    +  this.clear$Field(36);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_fixed32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFixed32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(37, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_fixed32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFixed32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(37, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_fixed32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedFixed32 = function(value) {
    +  this.add$Value(37, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_fixed32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFixed32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(37));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_fixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedFixed32 = function() {
    +  return this.has$Value(37);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFixed32Count = function() {
    +  return this.count$Values(37);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedFixed32 = function() {
    +  this.clear$Field(37);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_fixed64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFixed64 = function(index) {
    +  return /** @type {?string} */ (this.get$Value(38, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_fixed64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFixed64OrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(38, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_fixed64 field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedFixed64 = function(value) {
    +  this.add$Value(38, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_fixed64 field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFixed64Array = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(38));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_fixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedFixed64 = function() {
    +  return this.has$Value(38);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFixed64Count = function() {
    +  return this.count$Values(38);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedFixed64 = function() {
    +  this.clear$Field(38);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sfixed32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSfixed32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(39, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sfixed32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSfixed32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(39, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_sfixed32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedSfixed32 = function(value) {
    +  this.add$Value(39, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_sfixed32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSfixed32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(39));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_sfixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedSfixed32 = function() {
    +  return this.has$Value(39);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSfixed32Count = function() {
    +  return this.count$Values(39);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedSfixed32 = function() {
    +  this.clear$Field(39);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sfixed64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSfixed64 = function(index) {
    +  return /** @type {?string} */ (this.get$Value(40, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_sfixed64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedSfixed64OrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(40, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_sfixed64 field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedSfixed64 = function(value) {
    +  this.add$Value(40, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_sfixed64 field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSfixed64Array = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(40));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_sfixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedSfixed64 = function() {
    +  return this.has$Value(40);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.repeatedSfixed64Count = function() {
    +  return this.count$Values(40);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedSfixed64 = function() {
    +  this.clear$Field(40);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_float field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFloat = function(index) {
    +  return /** @type {?number} */ (this.get$Value(41, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_float field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedFloatOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(41, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_float field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedFloat = function(value) {
    +  this.add$Value(41, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_float field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFloatArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(41));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_float field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedFloat = function() {
    +  return this.has$Value(41);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_float field.
    + */
    +proto2.TestAllTypes.prototype.repeatedFloatCount = function() {
    +  return this.count$Values(41);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_float field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedFloat = function() {
    +  this.clear$Field(41);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_double field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedDouble = function(index) {
    +  return /** @type {?number} */ (this.get$Value(42, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_double field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedDoubleOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(42, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_double field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedDouble = function(value) {
    +  this.add$Value(42, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_double field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedDoubleArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(42));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_double field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedDouble = function() {
    +  return this.has$Value(42);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_double field.
    + */
    +proto2.TestAllTypes.prototype.repeatedDoubleCount = function() {
    +  return this.count$Values(42);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_double field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedDouble = function() {
    +  this.clear$Field(42);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_bool field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedBool = function(index) {
    +  return /** @type {?boolean} */ (this.get$Value(43, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_bool field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedBoolOrDefault = function(index) {
    +  return /** @type {boolean} */ (this.get$ValueOrDefault(43, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_bool field.
    + * @param {boolean} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedBool = function(value) {
    +  this.add$Value(43, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_bool field.
    + * @return {!Array.<boolean>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedBoolArray = function() {
    +  return /** @type {!Array.<boolean>} */ (this.array$Values(43));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_bool field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedBool = function() {
    +  return this.has$Value(43);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_bool field.
    + */
    +proto2.TestAllTypes.prototype.repeatedBoolCount = function() {
    +  return this.count$Values(43);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_bool field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedBool = function() {
    +  this.clear$Field(43);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_string field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedString = function(index) {
    +  return /** @type {?string} */ (this.get$Value(44, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_string field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedStringOrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(44, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_string field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedString = function(value) {
    +  this.add$Value(44, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_string field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedStringArray = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(44));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_string field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedString = function() {
    +  return this.has$Value(44);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_string field.
    + */
    +proto2.TestAllTypes.prototype.repeatedStringCount = function() {
    +  return this.count$Values(44);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_string field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedString = function() {
    +  this.clear$Field(44);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_bytes field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedBytes = function(index) {
    +  return /** @type {?string} */ (this.get$Value(45, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_bytes field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedBytesOrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(45, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_bytes field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedBytes = function(value) {
    +  this.add$Value(45, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_bytes field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedBytesArray = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(45));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_bytes field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedBytes = function() {
    +  return this.has$Value(45);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_bytes field.
    + */
    +proto2.TestAllTypes.prototype.repeatedBytesCount = function() {
    +  return this.count$Values(45);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_bytes field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedBytes = function() {
    +  this.clear$Field(45);
    +};
    +
    +
    +/**
    + * Gets the value of the repeatedgroup field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {proto2.TestAllTypes.RepeatedGroup} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedgroup = function(index) {
    +  return /** @type {proto2.TestAllTypes.RepeatedGroup} */ (this.get$Value(46, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeatedgroup field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {!proto2.TestAllTypes.RepeatedGroup} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedgroupOrDefault = function(index) {
    +  return /** @type {!proto2.TestAllTypes.RepeatedGroup} */ (this.get$ValueOrDefault(46, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeatedgroup field.
    + * @param {!proto2.TestAllTypes.RepeatedGroup} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedgroup = function(value) {
    +  this.add$Value(46, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeatedgroup field.
    + * @return {!Array.<!proto2.TestAllTypes.RepeatedGroup>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedgroupArray = function() {
    +  return /** @type {!Array.<!proto2.TestAllTypes.RepeatedGroup>} */ (this.array$Values(46));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeatedgroup field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedgroup = function() {
    +  return this.has$Value(46);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeatedgroup field.
    + */
    +proto2.TestAllTypes.prototype.repeatedgroupCount = function() {
    +  return this.count$Values(46);
    +};
    +
    +
    +/**
    + * Clears the values in the repeatedgroup field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedgroup = function() {
    +  this.clear$Field(46);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_nested_message field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {proto2.TestAllTypes.NestedMessage} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedNestedMessage = function(index) {
    +  return /** @type {proto2.TestAllTypes.NestedMessage} */ (this.get$Value(48, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_nested_message field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {!proto2.TestAllTypes.NestedMessage} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedNestedMessageOrDefault = function(index) {
    +  return /** @type {!proto2.TestAllTypes.NestedMessage} */ (this.get$ValueOrDefault(48, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_nested_message field.
    + * @param {!proto2.TestAllTypes.NestedMessage} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedNestedMessage = function(value) {
    +  this.add$Value(48, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_nested_message field.
    + * @return {!Array.<!proto2.TestAllTypes.NestedMessage>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedNestedMessageArray = function() {
    +  return /** @type {!Array.<!proto2.TestAllTypes.NestedMessage>} */ (this.array$Values(48));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_nested_message field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedNestedMessage = function() {
    +  return this.has$Value(48);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_nested_message field.
    + */
    +proto2.TestAllTypes.prototype.repeatedNestedMessageCount = function() {
    +  return this.count$Values(48);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_nested_message field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedNestedMessage = function() {
    +  this.clear$Field(48);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_nested_enum field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?proto2.TestAllTypes.NestedEnum} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedNestedEnum = function(index) {
    +  return /** @type {?proto2.TestAllTypes.NestedEnum} */ (this.get$Value(49, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_nested_enum field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {proto2.TestAllTypes.NestedEnum} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedNestedEnumOrDefault = function(index) {
    +  return /** @type {proto2.TestAllTypes.NestedEnum} */ (this.get$ValueOrDefault(49, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_nested_enum field.
    + * @param {proto2.TestAllTypes.NestedEnum} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedNestedEnum = function(value) {
    +  this.add$Value(49, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_nested_enum field.
    + * @return {!Array.<proto2.TestAllTypes.NestedEnum>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedNestedEnumArray = function() {
    +  return /** @type {!Array.<proto2.TestAllTypes.NestedEnum>} */ (this.array$Values(49));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_nested_enum field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedNestedEnum = function() {
    +  return this.has$Value(49);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_nested_enum field.
    + */
    +proto2.TestAllTypes.prototype.repeatedNestedEnumCount = function() {
    +  return this.count$Values(49);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_nested_enum field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedNestedEnum = function() {
    +  this.clear$Field(49);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64_number field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64Number = function(index) {
    +  return /** @type {?number} */ (this.get$Value(52, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64_number field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64NumberOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(52, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_int64_number field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedInt64Number = function(value) {
    +  this.add$Value(52, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_int64_number field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64NumberArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(52));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_int64_number field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedInt64Number = function() {
    +  return this.has$Value(52);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_int64_number field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64NumberCount = function() {
    +  return this.count$Values(52);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_int64_number field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedInt64Number = function() {
    +  this.clear$Field(52);
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64_string field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64String = function(index) {
    +  return /** @type {?string} */ (this.get$Value(53, index));
    +};
    +
    +
    +/**
    + * Gets the value of the repeated_int64_string field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {string} The value.
    + */
    +proto2.TestAllTypes.prototype.getRepeatedInt64StringOrDefault = function(index) {
    +  return /** @type {string} */ (this.get$ValueOrDefault(53, index));
    +};
    +
    +
    +/**
    + * Adds a value to the repeated_int64_string field.
    + * @param {string} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addRepeatedInt64String = function(value) {
    +  this.add$Value(53, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the repeated_int64_string field.
    + * @return {!Array.<string>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64StringArray = function() {
    +  return /** @type {!Array.<string>} */ (this.array$Values(53));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the repeated_int64_string field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasRepeatedInt64String = function() {
    +  return this.has$Value(53);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the repeated_int64_string field.
    + */
    +proto2.TestAllTypes.prototype.repeatedInt64StringCount = function() {
    +  return this.count$Values(53);
    +};
    +
    +
    +/**
    + * Clears the values in the repeated_int64_string field.
    + */
    +proto2.TestAllTypes.prototype.clearRepeatedInt64String = function() {
    +  this.clear$Field(53);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_int32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedInt32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(54, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_int32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedInt32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(54, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_int32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedInt32 = function(value) {
    +  this.add$Value(54, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_int32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedInt32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(54));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_int32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedInt32 = function() {
    +  return this.has$Value(54);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_int32 field.
    + */
    +proto2.TestAllTypes.prototype.packedInt32Count = function() {
    +  return this.count$Values(54);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_int32 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedInt32 = function() {
    +  this.clear$Field(54);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_int64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedInt64 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(55, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_int64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedInt64OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(55, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_int64 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedInt64 = function(value) {
    +  this.add$Value(55, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_int64 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedInt64Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(55));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_int64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedInt64 = function() {
    +  return this.has$Value(55);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_int64 field.
    + */
    +proto2.TestAllTypes.prototype.packedInt64Count = function() {
    +  return this.count$Values(55);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_int64 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedInt64 = function() {
    +  this.clear$Field(55);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_uint32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedUint32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(56, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_uint32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedUint32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(56, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_uint32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedUint32 = function(value) {
    +  this.add$Value(56, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_uint32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedUint32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(56));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_uint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedUint32 = function() {
    +  return this.has$Value(56);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.packedUint32Count = function() {
    +  return this.count$Values(56);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_uint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedUint32 = function() {
    +  this.clear$Field(56);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_uint64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedUint64 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(57, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_uint64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedUint64OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(57, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_uint64 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedUint64 = function(value) {
    +  this.add$Value(57, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_uint64 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedUint64Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(57));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_uint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedUint64 = function() {
    +  return this.has$Value(57);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.packedUint64Count = function() {
    +  return this.count$Values(57);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_uint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedUint64 = function() {
    +  this.clear$Field(57);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sint32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSint32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(58, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sint32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSint32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(58, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_sint32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedSint32 = function(value) {
    +  this.add$Value(58, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_sint32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedSint32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(58));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_sint32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedSint32 = function() {
    +  return this.has$Value(58);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.packedSint32Count = function() {
    +  return this.count$Values(58);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_sint32 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedSint32 = function() {
    +  this.clear$Field(58);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sint64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSint64 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(59, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sint64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSint64OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(59, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_sint64 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedSint64 = function(value) {
    +  this.add$Value(59, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_sint64 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedSint64Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(59));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_sint64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedSint64 = function() {
    +  return this.has$Value(59);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.packedSint64Count = function() {
    +  return this.count$Values(59);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_sint64 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedSint64 = function() {
    +  this.clear$Field(59);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_fixed32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFixed32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(60, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_fixed32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFixed32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(60, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_fixed32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedFixed32 = function(value) {
    +  this.add$Value(60, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_fixed32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedFixed32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(60));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_fixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedFixed32 = function() {
    +  return this.has$Value(60);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.packedFixed32Count = function() {
    +  return this.count$Values(60);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_fixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedFixed32 = function() {
    +  this.clear$Field(60);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_fixed64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFixed64 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(61, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_fixed64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFixed64OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(61, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_fixed64 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedFixed64 = function(value) {
    +  this.add$Value(61, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_fixed64 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedFixed64Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(61));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_fixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedFixed64 = function() {
    +  return this.has$Value(61);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.packedFixed64Count = function() {
    +  return this.count$Values(61);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_fixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedFixed64 = function() {
    +  this.clear$Field(61);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sfixed32 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSfixed32 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(62, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sfixed32 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSfixed32OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(62, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_sfixed32 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedSfixed32 = function(value) {
    +  this.add$Value(62, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_sfixed32 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedSfixed32Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(62));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_sfixed32 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedSfixed32 = function() {
    +  return this.has$Value(62);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.packedSfixed32Count = function() {
    +  return this.count$Values(62);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_sfixed32 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedSfixed32 = function() {
    +  this.clear$Field(62);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sfixed64 field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSfixed64 = function(index) {
    +  return /** @type {?number} */ (this.get$Value(63, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_sfixed64 field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedSfixed64OrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(63, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_sfixed64 field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedSfixed64 = function(value) {
    +  this.add$Value(63, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_sfixed64 field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedSfixed64Array = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(63));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_sfixed64 field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedSfixed64 = function() {
    +  return this.has$Value(63);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.packedSfixed64Count = function() {
    +  return this.count$Values(63);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_sfixed64 field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedSfixed64 = function() {
    +  this.clear$Field(63);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_float field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFloat = function(index) {
    +  return /** @type {?number} */ (this.get$Value(64, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_float field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedFloatOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(64, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_float field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedFloat = function(value) {
    +  this.add$Value(64, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_float field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedFloatArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(64));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_float field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedFloat = function() {
    +  return this.has$Value(64);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_float field.
    + */
    +proto2.TestAllTypes.prototype.packedFloatCount = function() {
    +  return this.count$Values(64);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_float field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedFloat = function() {
    +  this.clear$Field(64);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_double field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedDouble = function(index) {
    +  return /** @type {?number} */ (this.get$Value(65, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_double field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedDoubleOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(65, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_double field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedDouble = function(value) {
    +  this.add$Value(65, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_double field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedDoubleArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(65));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_double field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedDouble = function() {
    +  return this.has$Value(65);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_double field.
    + */
    +proto2.TestAllTypes.prototype.packedDoubleCount = function() {
    +  return this.count$Values(65);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_double field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedDouble = function() {
    +  this.clear$Field(65);
    +};
    +
    +
    +/**
    + * Gets the value of the packed_bool field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedBool = function(index) {
    +  return /** @type {?boolean} */ (this.get$Value(66, index));
    +};
    +
    +
    +/**
    + * Gets the value of the packed_bool field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {boolean} The value.
    + */
    +proto2.TestAllTypes.prototype.getPackedBoolOrDefault = function(index) {
    +  return /** @type {boolean} */ (this.get$ValueOrDefault(66, index));
    +};
    +
    +
    +/**
    + * Adds a value to the packed_bool field.
    + * @param {boolean} value The value to add.
    + */
    +proto2.TestAllTypes.prototype.addPackedBool = function(value) {
    +  this.add$Value(66, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the packed_bool field.
    + * @return {!Array.<boolean>} The values in the field.
    + */
    +proto2.TestAllTypes.prototype.packedBoolArray = function() {
    +  return /** @type {!Array.<boolean>} */ (this.array$Values(66));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the packed_bool field has a value.
    + */
    +proto2.TestAllTypes.prototype.hasPackedBool = function() {
    +  return this.has$Value(66);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the packed_bool field.
    + */
    +proto2.TestAllTypes.prototype.packedBoolCount = function() {
    +  return this.count$Values(66);
    +};
    +
    +
    +/**
    + * Clears the values in the packed_bool field.
    + */
    +proto2.TestAllTypes.prototype.clearPackedBool = function() {
    +  this.clear$Field(66);
    +};
    +
    +
    +/**
    + * Enumeration NestedEnum.
    + * @enum {number}
    + */
    +proto2.TestAllTypes.NestedEnum = {
    +  FOO: 0,
    +  BAR: 2,
    +  BAZ: 3
    +};
    +
    +
    +
    +/**
    + * Message NestedMessage.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestAllTypes.NestedMessage = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestAllTypes.NestedMessage, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestAllTypes.NestedMessage} The cloned message.
    + * @override
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the b field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.getB = function() {
    +  return /** @type {?number} */ (this.get$Value(1));
    +};
    +
    +
    +/**
    + * Gets the value of the b field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.getBOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(1));
    +};
    +
    +
    +/**
    + * Sets the value of the b field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.setB = function(value) {
    +  this.set$Value(1, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the b field has a value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.hasB = function() {
    +  return this.has$Value(1);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the b field.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.bCount = function() {
    +  return this.count$Values(1);
    +};
    +
    +
    +/**
    + * Clears the values in the b field.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.clearB = function() {
    +  this.clear$Field(1);
    +};
    +
    +
    +/**
    + * Gets the value of the c field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.getC = function() {
    +  return /** @type {?number} */ (this.get$Value(2));
    +};
    +
    +
    +/**
    + * Gets the value of the c field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.getCOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(2));
    +};
    +
    +
    +/**
    + * Sets the value of the c field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.setC = function(value) {
    +  this.set$Value(2, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the c field has a value.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.hasC = function() {
    +  return this.has$Value(2);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the c field.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.cCount = function() {
    +  return this.count$Values(2);
    +};
    +
    +
    +/**
    + * Clears the values in the c field.
    + */
    +proto2.TestAllTypes.NestedMessage.prototype.clearC = function() {
    +  this.clear$Field(2);
    +};
    +
    +
    +
    +/**
    + * Message OptionalGroup.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestAllTypes.OptionalGroup = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestAllTypes.OptionalGroup, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestAllTypes.OptionalGroup} The cloned message.
    + * @override
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the a field.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.getA = function() {
    +  return /** @type {?number} */ (this.get$Value(17));
    +};
    +
    +
    +/**
    + * Gets the value of the a field or the default value if not set.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.getAOrDefault = function() {
    +  return /** @type {number} */ (this.get$ValueOrDefault(17));
    +};
    +
    +
    +/**
    + * Sets the value of the a field.
    + * @param {number} value The value.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.setA = function(value) {
    +  this.set$Value(17, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the a field has a value.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.hasA = function() {
    +  return this.has$Value(17);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the a field.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.aCount = function() {
    +  return this.count$Values(17);
    +};
    +
    +
    +/**
    + * Clears the values in the a field.
    + */
    +proto2.TestAllTypes.OptionalGroup.prototype.clearA = function() {
    +  this.clear$Field(17);
    +};
    +
    +
    +
    +/**
    + * Message RepeatedGroup.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestAllTypes.RepeatedGroup = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestAllTypes.RepeatedGroup, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestAllTypes.RepeatedGroup} The cloned message.
    + * @override
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the a field at the index given.
    + * @param {number} index The index to lookup.
    + * @return {?number} The value.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.getA = function(index) {
    +  return /** @type {?number} */ (this.get$Value(47, index));
    +};
    +
    +
    +/**
    + * Gets the value of the a field at the index given or the default value if not set.
    + * @param {number} index The index to lookup.
    + * @return {number} The value.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.getAOrDefault = function(index) {
    +  return /** @type {number} */ (this.get$ValueOrDefault(47, index));
    +};
    +
    +
    +/**
    + * Adds a value to the a field.
    + * @param {number} value The value to add.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.addA = function(value) {
    +  this.add$Value(47, value);
    +};
    +
    +
    +/**
    + * Returns the array of values in the a field.
    + * @return {!Array.<number>} The values in the field.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.aArray = function() {
    +  return /** @type {!Array.<number>} */ (this.array$Values(47));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the a field has a value.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.hasA = function() {
    +  return this.has$Value(47);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the a field.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.aCount = function() {
    +  return this.count$Values(47);
    +};
    +
    +
    +/**
    + * Clears the values in the a field.
    + */
    +proto2.TestAllTypes.RepeatedGroup.prototype.clearA = function() {
    +  this.clear$Field(47);
    +};
    +
    +
    +
    +/**
    + * Message TestDefaultParent.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestDefaultParent = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestDefaultParent, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestDefaultParent} The cloned message.
    + * @override
    + */
    +proto2.TestDefaultParent.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the child field.
    + * @return {proto2.TestDefaultChild} The value.
    + */
    +proto2.TestDefaultParent.prototype.getChild = function() {
    +  return /** @type {proto2.TestDefaultChild} */ (this.get$Value(1));
    +};
    +
    +
    +/**
    + * Gets the value of the child field or the default value if not set.
    + * @return {!proto2.TestDefaultChild} The value.
    + */
    +proto2.TestDefaultParent.prototype.getChildOrDefault = function() {
    +  return /** @type {!proto2.TestDefaultChild} */ (this.get$ValueOrDefault(1));
    +};
    +
    +
    +/**
    + * Sets the value of the child field.
    + * @param {!proto2.TestDefaultChild} value The value.
    + */
    +proto2.TestDefaultParent.prototype.setChild = function(value) {
    +  this.set$Value(1, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the child field has a value.
    + */
    +proto2.TestDefaultParent.prototype.hasChild = function() {
    +  return this.has$Value(1);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the child field.
    + */
    +proto2.TestDefaultParent.prototype.childCount = function() {
    +  return this.count$Values(1);
    +};
    +
    +
    +/**
    + * Clears the values in the child field.
    + */
    +proto2.TestDefaultParent.prototype.clearChild = function() {
    +  this.clear$Field(1);
    +};
    +
    +
    +
    +/**
    + * Message TestDefaultChild.
    + * @constructor
    + * @extends {goog.proto2.Message}
    + */
    +proto2.TestDefaultChild = function() {
    +  goog.proto2.Message.call(this);
    +};
    +goog.inherits(proto2.TestDefaultChild, goog.proto2.Message);
    +
    +
    +/**
    + * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
    + * @return {!proto2.TestDefaultChild} The cloned message.
    + * @override
    + */
    +proto2.TestDefaultChild.prototype.clone;
    +
    +
    +/**
    + * Gets the value of the foo field.
    + * @return {?boolean} The value.
    + */
    +proto2.TestDefaultChild.prototype.getFoo = function() {
    +  return /** @type {?boolean} */ (this.get$Value(1));
    +};
    +
    +
    +/**
    + * Gets the value of the foo field or the default value if not set.
    + * @return {boolean} The value.
    + */
    +proto2.TestDefaultChild.prototype.getFooOrDefault = function() {
    +  return /** @type {boolean} */ (this.get$ValueOrDefault(1));
    +};
    +
    +
    +/**
    + * Sets the value of the foo field.
    + * @param {boolean} value The value.
    + */
    +proto2.TestDefaultChild.prototype.setFoo = function(value) {
    +  this.set$Value(1, value);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the foo field has a value.
    + */
    +proto2.TestDefaultChild.prototype.hasFoo = function() {
    +  return this.has$Value(1);
    +};
    +
    +
    +/**
    + * @return {number} The number of values in the foo field.
    + */
    +proto2.TestDefaultChild.prototype.fooCount = function() {
    +  return this.count$Values(1);
    +};
    +
    +
    +/**
    + * Clears the values in the foo field.
    + */
    +proto2.TestDefaultChild.prototype.clearFoo = function() {
    +  this.clear$Field(1);
    +};
    +
    +
    +/** @override */
    +proto2.TestAllTypes.prototype.getDescriptor = function() {
    +  if (!proto2.TestAllTypes.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'TestAllTypes',
    +        fullName: 'TestAllTypes'
    +      },
    +      1: {
    +        name: 'optional_int32',
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      },
    +      2: {
    +        name: 'optional_int64',
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        defaultValue: '1',
    +        type: String
    +      },
    +      3: {
    +        name: 'optional_uint32',
    +        fieldType: goog.proto2.Message.FieldType.UINT32,
    +        type: Number
    +      },
    +      4: {
    +        name: 'optional_uint64',
    +        fieldType: goog.proto2.Message.FieldType.UINT64,
    +        type: String
    +      },
    +      5: {
    +        name: 'optional_sint32',
    +        fieldType: goog.proto2.Message.FieldType.SINT32,
    +        type: Number
    +      },
    +      6: {
    +        name: 'optional_sint64',
    +        fieldType: goog.proto2.Message.FieldType.SINT64,
    +        type: String
    +      },
    +      7: {
    +        name: 'optional_fixed32',
    +        fieldType: goog.proto2.Message.FieldType.FIXED32,
    +        type: Number
    +      },
    +      8: {
    +        name: 'optional_fixed64',
    +        fieldType: goog.proto2.Message.FieldType.FIXED64,
    +        type: String
    +      },
    +      9: {
    +        name: 'optional_sfixed32',
    +        fieldType: goog.proto2.Message.FieldType.SFIXED32,
    +        type: Number
    +      },
    +      10: {
    +        name: 'optional_sfixed64',
    +        fieldType: goog.proto2.Message.FieldType.SFIXED64,
    +        type: String
    +      },
    +      11: {
    +        name: 'optional_float',
    +        fieldType: goog.proto2.Message.FieldType.FLOAT,
    +        defaultValue: 1.5,
    +        type: Number
    +      },
    +      12: {
    +        name: 'optional_double',
    +        fieldType: goog.proto2.Message.FieldType.DOUBLE,
    +        type: Number
    +      },
    +      13: {
    +        name: 'optional_bool',
    +        fieldType: goog.proto2.Message.FieldType.BOOL,
    +        type: Boolean
    +      },
    +      14: {
    +        name: 'optional_string',
    +        fieldType: goog.proto2.Message.FieldType.STRING,
    +        type: String
    +      },
    +      15: {
    +        name: 'optional_bytes',
    +        fieldType: goog.proto2.Message.FieldType.BYTES,
    +        defaultValue: 'moo',
    +        type: String
    +      },
    +      16: {
    +        name: 'optionalgroup',
    +        fieldType: goog.proto2.Message.FieldType.GROUP,
    +        type: proto2.TestAllTypes.OptionalGroup
    +      },
    +      18: {
    +        name: 'optional_nested_message',
    +        fieldType: goog.proto2.Message.FieldType.MESSAGE,
    +        type: proto2.TestAllTypes.NestedMessage
    +      },
    +      21: {
    +        name: 'optional_nested_enum',
    +        fieldType: goog.proto2.Message.FieldType.ENUM,
    +        defaultValue: proto2.TestAllTypes.NestedEnum.FOO,
    +        type: proto2.TestAllTypes.NestedEnum
    +      },
    +      50: {
    +        name: 'optional_int64_number',
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        defaultValue: 1000000000000000001,
    +        type: Number
    +      },
    +      51: {
    +        name: 'optional_int64_string',
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        defaultValue: '1000000000000000001',
    +        type: String
    +      },
    +      31: {
    +        name: 'repeated_int32',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      },
    +      32: {
    +        name: 'repeated_int64',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        type: String
    +      },
    +      33: {
    +        name: 'repeated_uint32',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.UINT32,
    +        type: Number
    +      },
    +      34: {
    +        name: 'repeated_uint64',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.UINT64,
    +        type: String
    +      },
    +      35: {
    +        name: 'repeated_sint32',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.SINT32,
    +        type: Number
    +      },
    +      36: {
    +        name: 'repeated_sint64',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.SINT64,
    +        type: String
    +      },
    +      37: {
    +        name: 'repeated_fixed32',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.FIXED32,
    +        type: Number
    +      },
    +      38: {
    +        name: 'repeated_fixed64',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.FIXED64,
    +        type: String
    +      },
    +      39: {
    +        name: 'repeated_sfixed32',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.SFIXED32,
    +        type: Number
    +      },
    +      40: {
    +        name: 'repeated_sfixed64',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.SFIXED64,
    +        type: String
    +      },
    +      41: {
    +        name: 'repeated_float',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.FLOAT,
    +        type: Number
    +      },
    +      42: {
    +        name: 'repeated_double',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.DOUBLE,
    +        type: Number
    +      },
    +      43: {
    +        name: 'repeated_bool',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.BOOL,
    +        type: Boolean
    +      },
    +      44: {
    +        name: 'repeated_string',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.STRING,
    +        type: String
    +      },
    +      45: {
    +        name: 'repeated_bytes',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.BYTES,
    +        type: String
    +      },
    +      46: {
    +        name: 'repeatedgroup',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.GROUP,
    +        type: proto2.TestAllTypes.RepeatedGroup
    +      },
    +      48: {
    +        name: 'repeated_nested_message',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.MESSAGE,
    +        type: proto2.TestAllTypes.NestedMessage
    +      },
    +      49: {
    +        name: 'repeated_nested_enum',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.ENUM,
    +        defaultValue: proto2.TestAllTypes.NestedEnum.FOO,
    +        type: proto2.TestAllTypes.NestedEnum
    +      },
    +      52: {
    +        name: 'repeated_int64_number',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        type: Number
    +      },
    +      53: {
    +        name: 'repeated_int64_string',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        type: String
    +      },
    +      54: {
    +        name: 'packed_int32',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      },
    +      55: {
    +        name: 'packed_int64',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.INT64,
    +        type: Number
    +      },
    +      56: {
    +        name: 'packed_uint32',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.UINT32,
    +        type: Number
    +      },
    +      57: {
    +        name: 'packed_uint64',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.UINT64,
    +        type: Number
    +      },
    +      58: {
    +        name: 'packed_sint32',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.SINT32,
    +        type: Number
    +      },
    +      59: {
    +        name: 'packed_sint64',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.SINT64,
    +        type: Number
    +      },
    +      60: {
    +        name: 'packed_fixed32',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.FIXED32,
    +        type: Number
    +      },
    +      61: {
    +        name: 'packed_fixed64',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.FIXED64,
    +        type: Number
    +      },
    +      62: {
    +        name: 'packed_sfixed32',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.SFIXED32,
    +        type: Number
    +      },
    +      63: {
    +        name: 'packed_sfixed64',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.SFIXED64,
    +        type: Number
    +      },
    +      64: {
    +        name: 'packed_float',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.FLOAT,
    +        type: Number
    +      },
    +      65: {
    +        name: 'packed_double',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.DOUBLE,
    +        type: Number
    +      },
    +      66: {
    +        name: 'packed_bool',
    +        repeated: true,
    +        packed: true,
    +        fieldType: goog.proto2.Message.FieldType.BOOL,
    +        type: Boolean
    +      }
    +    };
    +    proto2.TestAllTypes.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestAllTypes, descriptorObj);
    +  }
    +  return proto2.TestAllTypes.descriptor_;
    +};
    +
    +
    +proto2.TestAllTypes['ctor'] = proto2.TestAllTypes;proto2.TestAllTypes['ctor'].getDescriptor =
    +    proto2.TestAllTypes.prototype.getDescriptor;
    +
    +
    +/** @override */
    +proto2.TestAllTypes.NestedMessage.prototype.getDescriptor = function() {
    +  if (!proto2.TestAllTypes.NestedMessage.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'NestedMessage',
    +        containingType: proto2.TestAllTypes,
    +        fullName: 'TestAllTypes.NestedMessage'
    +      },
    +      1: {
    +        name: 'b',
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      },
    +      2: {
    +        name: 'c',
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      }
    +    };
    +    proto2.TestAllTypes.NestedMessage.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestAllTypes.NestedMessage, descriptorObj);
    +  }
    +  return proto2.TestAllTypes.NestedMessage.descriptor_;
    +};
    +
    +
    +proto2.TestAllTypes.NestedMessage['ctor'] = proto2.TestAllTypes.NestedMessage;proto2.TestAllTypes.NestedMessage['ctor'].getDescriptor =
    +    proto2.TestAllTypes.NestedMessage.prototype.getDescriptor;
    +
    +
    +/** @override */
    +proto2.TestAllTypes.OptionalGroup.prototype.getDescriptor = function() {
    +  if (!proto2.TestAllTypes.OptionalGroup.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'OptionalGroup',
    +        containingType: proto2.TestAllTypes,
    +        fullName: 'TestAllTypes.OptionalGroup'
    +      },
    +      17: {
    +        name: 'a',
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      }
    +    };
    +    proto2.TestAllTypes.OptionalGroup.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestAllTypes.OptionalGroup, descriptorObj);
    +  }
    +  return proto2.TestAllTypes.OptionalGroup.descriptor_;
    +};
    +
    +
    +proto2.TestAllTypes.OptionalGroup['ctor'] = proto2.TestAllTypes.OptionalGroup;proto2.TestAllTypes.OptionalGroup['ctor'].getDescriptor =
    +    proto2.TestAllTypes.OptionalGroup.prototype.getDescriptor;
    +
    +
    +/** @override */
    +proto2.TestAllTypes.RepeatedGroup.prototype.getDescriptor = function() {
    +  if (!proto2.TestAllTypes.RepeatedGroup.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'RepeatedGroup',
    +        containingType: proto2.TestAllTypes,
    +        fullName: 'TestAllTypes.RepeatedGroup'
    +      },
    +      47: {
    +        name: 'a',
    +        repeated: true,
    +        fieldType: goog.proto2.Message.FieldType.INT32,
    +        type: Number
    +      }
    +    };
    +    proto2.TestAllTypes.RepeatedGroup.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestAllTypes.RepeatedGroup, descriptorObj);
    +  }
    +  return proto2.TestAllTypes.RepeatedGroup.descriptor_;
    +};
    +
    +
    +proto2.TestAllTypes.RepeatedGroup['ctor'] = proto2.TestAllTypes.RepeatedGroup;proto2.TestAllTypes.RepeatedGroup['ctor'].getDescriptor =
    +    proto2.TestAllTypes.RepeatedGroup.prototype.getDescriptor;
    +
    +
    +/** @override */
    +proto2.TestDefaultParent.prototype.getDescriptor = function() {
    +  if (!proto2.TestDefaultParent.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'TestDefaultParent',
    +        fullName: 'TestDefaultParent'
    +      },
    +      1: {
    +        name: 'child',
    +        fieldType: goog.proto2.Message.FieldType.MESSAGE,
    +        type: proto2.TestDefaultChild
    +      }
    +    };
    +    proto2.TestDefaultParent.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestDefaultParent, descriptorObj);
    +  }
    +  return proto2.TestDefaultParent.descriptor_;
    +};
    +
    +
    +proto2.TestDefaultParent['ctor'] = proto2.TestDefaultParent;proto2.TestDefaultParent['ctor'].getDescriptor =
    +    proto2.TestDefaultParent.prototype.getDescriptor;
    +
    +
    +/** @override */
    +proto2.TestDefaultChild.prototype.getDescriptor = function() {
    +  if (!proto2.TestDefaultChild.descriptor_) {
    +    // The descriptor is created lazily when we instantiate a new instance.
    +    var descriptorObj = {
    +      0: {
    +        name: 'TestDefaultChild',
    +        fullName: 'TestDefaultChild'
    +      },
    +      1: {
    +        name: 'foo',
    +        fieldType: goog.proto2.Message.FieldType.BOOL,
    +        defaultValue: true,
    +        type: Boolean
    +      }
    +    };
    +    proto2.TestDefaultChild.descriptor_ =
    +        goog.proto2.Message.createDescriptor(
    +             proto2.TestDefaultChild, descriptorObj);
    +  }
    +  return proto2.TestDefaultChild.descriptor_;
    +};
    +
    +
    +proto2.TestDefaultChild['ctor'] = proto2.TestDefaultChild;proto2.TestDefaultChild['ctor'].getDescriptor =
    +    proto2.TestDefaultChild.prototype.getDescriptor;
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer.js b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer.js
    new file mode 100644
    index 0000000000..a1161f344d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer.js
    @@ -0,0 +1,1070 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Protocol Buffer 2 Serializer which serializes messages
    + *  into a user-friendly text format. Note that this code can run a bit
    + *  slowly (especially for parsing) and should therefore not be used for
    + *  time or space-critical applications.
    + *
    + * @see http://goo.gl/QDmDr
    + */
    +
    +goog.provide('goog.proto2.TextFormatSerializer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.json');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('goog.proto2.FieldDescriptor');
    +goog.require('goog.proto2.Message');
    +goog.require('goog.proto2.Serializer');
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * TextFormatSerializer, a serializer which turns Messages into the human
    + * readable text format.
    + * @param {boolean=} opt_ignoreMissingFields If true, then fields that cannot be
    + *     found on the proto when parsing the text format will be ignored.
    + * @param {boolean=} opt_useEnumValues If true, serialization code for enums
    + *     will use enum integer values instead of human-readable symbolic names.
    + * @constructor
    + * @extends {goog.proto2.Serializer}
    + * @final
    + */
    +goog.proto2.TextFormatSerializer = function(
    +    opt_ignoreMissingFields, opt_useEnumValues) {
    +  /**
    +   * Whether to ignore fields not defined on the proto when parsing the text
    +   * format.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.ignoreMissingFields_ = !!opt_ignoreMissingFields;
    +
    +  /**
    +   * Whether to use integer enum values during enum serialization.
    +   * If false, symbolic names will be used.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useEnumValues_ = !!opt_useEnumValues;
    +};
    +goog.inherits(goog.proto2.TextFormatSerializer, goog.proto2.Serializer);
    +
    +
    +/**
    + * Deserializes a message from text format and places the data in the message.
    + * @param {goog.proto2.Message} message The message in which to
    + *     place the information.
    + * @param {*} data The text format data.
    + * @return {?string} The parse error or null on success.
    + * @override
    + */
    +goog.proto2.TextFormatSerializer.prototype.deserializeTo =
    +    function(message, data) {
    +  var descriptor = message.getDescriptor();
    +  var textData = data.toString();
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  if (!parser.parse(message, textData, this.ignoreMissingFields_)) {
    +    return parser.getError();
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Serializes a message to a string.
    + * @param {goog.proto2.Message} message The message to be serialized.
    + * @return {string} The serialized form of the message.
    + * @override
    + */
    +goog.proto2.TextFormatSerializer.prototype.serialize = function(message) {
    +  var printer = new goog.proto2.TextFormatSerializer.Printer_();
    +  this.serializeMessage_(message, printer);
    +  return printer.toString();
    +};
    +
    +
    +/**
    + * Serializes the message and prints the text form into the given printer.
    + * @param {goog.proto2.Message} message The message to serialize.
    + * @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
    + *    which the text format will be printed.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.prototype.serializeMessage_ =
    +    function(message, printer) {
    +  var descriptor = message.getDescriptor();
    +  var fields = descriptor.getFields();
    +
    +  // Add the defined fields, recursively.
    +  goog.array.forEach(fields, function(field) {
    +    this.printField_(message, field, printer);
    +  }, this);
    +
    +  // Add the unknown fields, if any.
    +  message.forEachUnknown(function(tag, value) {
    +    this.serializeUnknown_(tag, value, printer);
    +  }, this);
    +};
    +
    +
    +/**
    + * Serializes an unknown field. When parsed from the JsPb object format, this
    + * manifests as either a primitive type, an array, or a raw object with integer
    + * keys. There is no descriptor available to interpret the types of nested
    + * messages.
    + * @param {number} tag The tag for the field. Since it's unknown, this is a
    + *     number rather than a string.
    + * @param {*} value The value of the field.
    + * @param {!goog.proto2.TextFormatSerializer.Printer_} printer The printer to
    + *     which the text format will be serialized.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.prototype.serializeUnknown_ =
    +    function(tag, value, printer) {
    +  if (!goog.isDefAndNotNull(value)) {
    +    return;
    +  }
    +
    +  if (goog.isArray(value)) {
    +    goog.array.forEach(value, function(val) {
    +      this.serializeUnknown_(tag, val, printer);
    +    }, this);
    +    return;
    +  }
    +
    +  if (goog.isObject(value)) {
    +    printer.append(tag);
    +    printer.append(' {');
    +    printer.appendLine();
    +    printer.indent();
    +    if (value instanceof goog.proto2.Message) {
    +      // Note(user): This conditional is here to make the
    +      // testSerializationOfUnknown unit test pass, but in practice we should
    +      // never have a Message for an "unknown" field.
    +      this.serializeMessage_(value, printer);
    +    } else {
    +      // For an unknown message, fields are keyed by positive integers. We
    +      // don't have a 'length' property to use for enumeration, so go through
    +      // all properties and ignore the ones that aren't legal keys.
    +      for (var key in value) {
    +        var keyAsNumber = goog.string.parseInt(key);
    +        goog.asserts.assert(goog.math.isInt(keyAsNumber));
    +        this.serializeUnknown_(keyAsNumber, value[key], printer);
    +      }
    +    }
    +    printer.dedent();
    +    printer.append('}');
    +    printer.appendLine();
    +    return;
    +  }
    +
    +  if (goog.isString(value)) {
    +    value = goog.string.quote(value);
    +  }
    +  printer.append(tag);
    +  printer.append(': ');
    +  printer.append(value.toString());
    +  printer.appendLine();
    +};
    +
    +
    +/**
    + * Prints the serialized value for the given field to the printer.
    + * @param {*} value The field's value.
    + * @param {goog.proto2.FieldDescriptor} field The field whose value is being
    + *    printed.
    + * @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
    + *    which the value will be printed.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.prototype.printFieldValue_ =
    +    function(value, field, printer) {
    +  switch (field.getFieldType()) {
    +    case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
    +    case goog.proto2.FieldDescriptor.FieldType.FLOAT:
    +    case goog.proto2.FieldDescriptor.FieldType.INT64:
    +    case goog.proto2.FieldDescriptor.FieldType.UINT64:
    +    case goog.proto2.FieldDescriptor.FieldType.INT32:
    +    case goog.proto2.FieldDescriptor.FieldType.UINT32:
    +    case goog.proto2.FieldDescriptor.FieldType.FIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.FIXED32:
    +    case goog.proto2.FieldDescriptor.FieldType.BOOL:
    +    case goog.proto2.FieldDescriptor.FieldType.SFIXED32:
    +    case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.SINT32:
    +    case goog.proto2.FieldDescriptor.FieldType.SINT64:
    +      printer.append(value);
    +      break;
    +
    +    case goog.proto2.FieldDescriptor.FieldType.BYTES:
    +    case goog.proto2.FieldDescriptor.FieldType.STRING:
    +      value = goog.string.quote(value.toString());
    +      printer.append(value);
    +      break;
    +
    +    case goog.proto2.FieldDescriptor.FieldType.ENUM:
    +      if (!this.useEnumValues_) {
    +        // Search the enum type for a matching key.
    +        var found = false;
    +        goog.object.forEach(field.getNativeType(), function(eValue, key) {
    +          if (eValue == value) {
    +            printer.append(key);
    +            found = true;
    +          }
    +        });
    +      }
    +
    +      if (!found || this.useEnumValues_) {
    +        // Otherwise, just print the numeric value.
    +        printer.append(value.toString());
    +      }
    +      break;
    +
    +    case goog.proto2.FieldDescriptor.FieldType.GROUP:
    +    case goog.proto2.FieldDescriptor.FieldType.MESSAGE:
    +      this.serializeMessage_(
    +          /** @type {goog.proto2.Message} */ (value), printer);
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Prints the serialized field to the printer.
    + * @param {goog.proto2.Message} message The parent message.
    + * @param {goog.proto2.FieldDescriptor} field The field to print.
    + * @param {goog.proto2.TextFormatSerializer.Printer_} printer The printer to
    + *    which the field will be printed.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.prototype.printField_ =
    +    function(message, field, printer) {
    +  // Skip fields not present.
    +  if (!message.has(field)) {
    +    return;
    +  }
    +
    +  var count = message.countOf(field);
    +  for (var i = 0; i < count; ++i) {
    +    // Field name.
    +    printer.append(field.getName());
    +
    +    // Field delimiter.
    +    if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
    +        field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
    +      printer.append(' {');
    +      printer.appendLine();
    +      printer.indent();
    +    } else {
    +      printer.append(': ');
    +    }
    +
    +    // Write the field value.
    +    this.printFieldValue_(message.get(field, i), field, printer);
    +
    +    // Close the field.
    +    if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
    +        field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
    +      printer.dedent();
    +      printer.append('}');
    +      printer.appendLine();
    +    } else {
    +      printer.appendLine();
    +    }
    +  }
    +};
    +
    +
    +////////////////////////////////////////////////////////////////////////////////
    +
    +
    +
    +/**
    + * Helper class used by the text format serializer for pretty-printing text.
    + * @constructor
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Printer_ = function() {
    +  /**
    +   * The current indentation count.
    +   * @type {number}
    +   * @private
    +   */
    +  this.indentation_ = 0;
    +
    +  /**
    +   * The buffer of string pieces.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.buffer_ = [];
    +
    +  /**
    +   * Whether indentation is required before the next append of characters.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.requiresIndentation_ = true;
    +};
    +
    +
    +/**
    + * @return {string} The contents of the printer.
    + * @override
    + */
    +goog.proto2.TextFormatSerializer.Printer_.prototype.toString = function() {
    +  return this.buffer_.join('');
    +};
    +
    +
    +/**
    + * Increases the indentation in the printer.
    + */
    +goog.proto2.TextFormatSerializer.Printer_.prototype.indent = function() {
    +  this.indentation_ += 2;
    +};
    +
    +
    +/**
    + * Decreases the indentation in the printer.
    + */
    +goog.proto2.TextFormatSerializer.Printer_.prototype.dedent = function() {
    +  this.indentation_ -= 2;
    +  goog.asserts.assert(this.indentation_ >= 0);
    +};
    +
    +
    +/**
    + * Appends the given value to the printer.
    + * @param {*} value The value to append.
    + */
    +goog.proto2.TextFormatSerializer.Printer_.prototype.append = function(value) {
    +  if (this.requiresIndentation_) {
    +    for (var i = 0; i < this.indentation_; ++i) {
    +      this.buffer_.push(' ');
    +    }
    +    this.requiresIndentation_ = false;
    +  }
    +
    +  this.buffer_.push(value.toString());
    +};
    +
    +
    +/**
    + * Appends a newline to the printer.
    + */
    +goog.proto2.TextFormatSerializer.Printer_.prototype.appendLine = function() {
    +  this.buffer_.push('\n');
    +  this.requiresIndentation_ = true;
    +};
    +
    +
    +////////////////////////////////////////////////////////////////////////////////
    +
    +
    +
    +/**
    + * Helper class for tokenizing the text format.
    + * @param {string} data The string data to tokenize.
    + * @param {boolean=} opt_ignoreWhitespace If true, whitespace tokens will not
    + *    be reported by the tokenizer.
    + * @param {boolean=} opt_ignoreComments If true, comment tokens will not be
    + *    reported by the tokenizer.
    + * @constructor
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_ =
    +    function(data, opt_ignoreWhitespace, opt_ignoreComments) {
    +
    +  /**
    +   * Whether to skip whitespace tokens on output.
    +   * @private {boolean}
    +   */
    +  this.ignoreWhitespace_ = !!opt_ignoreWhitespace;
    +
    +  /**
    +   * Whether to skip comment tokens on output.
    +   * @private {boolean}
    +   */
    +  this.ignoreComments_ = !!opt_ignoreComments;
    +
    +  /**
    +   * The data being tokenized.
    +   * @private {string}
    +   */
    +  this.data_ = data;
    +
    +  /**
    +   * The current index in the data.
    +   * @private {number}
    +   */
    +  this.index_ = 0;
    +
    +  /**
    +   * The data string starting at the current index.
    +   * @private {string}
    +   */
    +  this.currentData_ = data;
    +
    +  /**
    +   * The current token type.
    +   * @private {goog.proto2.TextFormatSerializer.Tokenizer_.Token}
    +   */
    +  this.current_ = {
    +    type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes.END,
    +    value: null
    +  };
    +};
    +
    +
    +/**
    + * @typedef {{type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes,
    + *            value: ?string}}
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_.Token;
    +
    +
    +/**
    + * @return {goog.proto2.TextFormatSerializer.Tokenizer_.Token} The current
    + *     token.
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_.prototype.getCurrent = function() {
    +  return this.current_;
    +};
    +
    +
    +/**
    + * An enumeration of all the token types.
    + * @enum {!RegExp}
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes = {
    +  END: /---end---/,
    +  // Leading "-" to identify "-infinity"."
    +  IDENTIFIER: /^-?[a-zA-Z][a-zA-Z0-9_]*/,
    +  NUMBER: /^(0x[0-9a-f]+)|(([-])?[0-9][0-9]*(\.?[0-9]+)?(e[+-]?[0-9]+|[f])?)/,
    +  COMMENT: /^#.*/,
    +  OPEN_BRACE: /^{/,
    +  CLOSE_BRACE: /^}/,
    +  OPEN_TAG: /^</,
    +  CLOSE_TAG: /^>/,
    +  OPEN_LIST: /^\[/,
    +  CLOSE_LIST: /^\]/,
    +  STRING: new RegExp('^"([^"\\\\]|\\\\.)*"'),
    +  COLON: /^:/,
    +  COMMA: /^,/,
    +  SEMI: /^;/,
    +  WHITESPACE: /^\s/
    +};
    +
    +
    +/**
    + * Advances to the next token.
    + * @return {boolean} True if a valid token was found, false if the end was
    + *    reached or no valid token was found.
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_.prototype.next = function() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +
    +  // Skip any whitespace if requested.
    +  while (this.nextInternal_()) {
    +    var type = this.getCurrent().type;
    +    if ((type != types.WHITESPACE && type != types.COMMENT) ||
    +        (type == types.WHITESPACE && !this.ignoreWhitespace_) ||
    +        (type == types.COMMENT && !this.ignoreComments_)) {
    +      return true;
    +    }
    +  }
    +
    +  // If we reach this point, set the current token to END.
    +  this.current_ = {
    +    type: goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes.END,
    +    value: null
    +  };
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Internal method for determining the next token.
    + * @return {boolean} True if a next token was found, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Tokenizer_.prototype.nextInternal_ =
    +    function() {
    +  if (this.index_ >= this.data_.length) {
    +    return false;
    +  }
    +
    +  var data = this.currentData_;
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  var next = null;
    +
    +  // Loop through each token type and try to match the beginning of the string
    +  // with the token's regular expression.
    +  goog.object.some(types, function(type, id) {
    +    if (next || type == types.END) {
    +      return false;
    +    }
    +
    +    // Note: This regular expression check is at, minimum, O(n).
    +    var info = type.exec(data);
    +    if (info && info.index == 0) {
    +      next = {
    +        type: type,
    +        value: info[0]
    +      };
    +    }
    +
    +    return !!next;
    +  });
    +
    +  // Advance the index by the length of the token.
    +  if (next) {
    +    this.current_ =
    +        /** @type {goog.proto2.TextFormatSerializer.Tokenizer_.Token} */ (next);
    +    this.index_ += next.value.length;
    +    this.currentData_ = this.currentData_.substring(next.value.length);
    +  }
    +
    +  return !!next;
    +};
    +
    +
    +////////////////////////////////////////////////////////////////////////////////
    +
    +
    +
    +/**
    + * Helper class for parsing the text format.
    + * @constructor
    + * @final
    + */
    +goog.proto2.TextFormatSerializer.Parser = function() {
    +  /**
    +   * The error during parsing, if any.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.error_ = null;
    +
    +  /**
    +   * The current tokenizer.
    +   * @type {goog.proto2.TextFormatSerializer.Tokenizer_}
    +   * @private
    +   */
    +  this.tokenizer_ = null;
    +
    +  /**
    +   * Whether to ignore missing fields in the proto when parsing.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.ignoreMissingFields_ = false;
    +};
    +
    +
    +/**
    + * Parses the given data, filling the message as it goes.
    + * @param {goog.proto2.Message} message The message to fill.
    + * @param {string} data The text format data.
    + * @param {boolean=} opt_ignoreMissingFields If true, fields missing in the
    + *     proto will be ignored.
    + * @return {boolean} True on success, false on failure. On failure, the
    + *     getError method can be called to get the reason for failure.
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.parse =
    +    function(message, data, opt_ignoreMissingFields) {
    +  this.error_ = null;
    +  this.ignoreMissingFields_ = !!opt_ignoreMissingFields;
    +  this.tokenizer_ =
    +      new goog.proto2.TextFormatSerializer.Tokenizer_(data, true, true);
    +  this.tokenizer_.next();
    +  return this.consumeMessage_(message, '');
    +};
    +
    +
    +/**
    + * @return {?string} The parse error, if any.
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.getError = function() {
    +  return this.error_;
    +};
    +
    +
    +/**
    + * Reports a parse error.
    + * @param {string} msg The error message.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.reportError_ =
    +    function(msg) {
    +  this.error_ = msg;
    +};
    +
    +
    +/**
    + * Attempts to consume the given message.
    + * @param {goog.proto2.Message} message The message to consume and fill. If
    + *    null, then the message contents will be consumed without ever being set
    + *    to anything.
    + * @param {string} delimiter The delimiter expected at the end of the message.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeMessage_ =
    +    function(message, delimiter) {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  while (!this.lookingAt_('>') && !this.lookingAt_('}') &&
    +         !this.lookingAtType_(types.END)) {
    +    if (!this.consumeField_(message)) { return false; }
    +  }
    +
    +  if (delimiter) {
    +    if (!this.consume_(delimiter)) { return false; }
    +  } else {
    +    if (!this.lookingAtType_(types.END)) {
    +      this.reportError_('Expected END token');
    +    }
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Attempts to consume the value of the given field.
    + * @param {goog.proto2.Message} message The parent message.
    + * @param {goog.proto2.FieldDescriptor} field The field.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeFieldValue_ =
    +    function(message, field) {
    +  var value = this.getFieldValue_(field);
    +  if (goog.isNull(value)) { return false; }
    +
    +  if (field.isRepeated()) {
    +    message.add(field, value);
    +  } else {
    +    message.set(field, value);
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Attempts to convert a string to a number.
    + * @param {string} num in hexadecimal or float format.
    + * @return {!number} The converted number or null on error.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.getNumberFromString_ =
    +    function(num) {
    +
    +  var returnValue = goog.string.contains(num, '.') ?
    +      parseFloat(num) : // num is a float.
    +      goog.string.parseInt(num); // num is an int.
    +
    +  goog.asserts.assert(!isNaN(returnValue));
    +  goog.asserts.assert(isFinite(returnValue));
    +
    +  return returnValue;
    +};
    +
    +
    +/**
    + * Parse NaN, positive infinity, or negative infinity from a string.
    + * @param {string} identifier An identifier string to check.
    + * @return {?number} Infinity, negative infinity, NaN, or null if none
    + *     of the constants could be parsed.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.parseNumericalConstant_ =
    +    function(identifier) {
    +  if (/^-?inf(?:inity)?f?$/i.test(identifier)) {
    +    return Infinity * (goog.string.startsWith(identifier, '-') ? -1 : 1);
    +  }
    +
    +  if (/^nanf?$/i.test(identifier)) {
    +    return NaN;
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Attempts to parse the given field's value from the stream.
    + * @param {goog.proto2.FieldDescriptor} field The field.
    + * @return {*} The field's value or null if none.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.getFieldValue_ =
    +    function(field) {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  switch (field.getFieldType()) {
    +    case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
    +    case goog.proto2.FieldDescriptor.FieldType.FLOAT:
    +
    +      var identifier = this.consumeIdentifier_();
    +      if (identifier) {
    +        var numericalIdentifier =
    +            goog.proto2.TextFormatSerializer.Parser.parseNumericalConstant_(
    +                identifier);
    +        // Use isDefAndNotNull since !!NaN is false.
    +        if (goog.isDefAndNotNull(numericalIdentifier)) {
    +          return numericalIdentifier;
    +        }
    +      }
    +
    +    case goog.proto2.FieldDescriptor.FieldType.INT32:
    +    case goog.proto2.FieldDescriptor.FieldType.UINT32:
    +    case goog.proto2.FieldDescriptor.FieldType.FIXED32:
    +    case goog.proto2.FieldDescriptor.FieldType.SFIXED32:
    +    case goog.proto2.FieldDescriptor.FieldType.SINT32:
    +      var num = this.consumeNumber_();
    +      if (!num) {
    +        return null;
    +      }
    +
    +      return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(num);
    +
    +    case goog.proto2.FieldDescriptor.FieldType.INT64:
    +    case goog.proto2.FieldDescriptor.FieldType.UINT64:
    +    case goog.proto2.FieldDescriptor.FieldType.FIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
    +    case goog.proto2.FieldDescriptor.FieldType.SINT64:
    +      var num = this.consumeNumber_();
    +      if (!num) {
    +        return null;
    +      }
    +
    +      if (field.getNativeType() == Number) {
    +        // 64-bit number stored as a number.
    +        return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(
    +            num);
    +      }
    +
    +      return num; // 64-bit numbers are by default stored as strings.
    +
    +    case goog.proto2.FieldDescriptor.FieldType.BOOL:
    +      var ident = this.consumeIdentifier_();
    +      if (!ident) {
    +        return null;
    +      }
    +
    +      switch (ident) {
    +        case 'true': return true;
    +        case 'false': return false;
    +        default:
    +          this.reportError_('Unknown type for bool: ' + ident);
    +          return null;
    +      }
    +
    +    case goog.proto2.FieldDescriptor.FieldType.ENUM:
    +      if (this.lookingAtType_(types.NUMBER)) {
    +        var num = this.consumeNumber_();
    +        if (!num) {
    +          return null;
    +        }
    +
    +        return goog.proto2.TextFormatSerializer.Parser.getNumberFromString_(
    +            num);
    +      } else {
    +        // Search the enum type for a matching key.
    +        var name = this.consumeIdentifier_();
    +        if (!name) {
    +          return null;
    +        }
    +
    +        var enumValue = field.getNativeType()[name];
    +        if (enumValue == null) {
    +          this.reportError_('Unknown enum value: ' + name);
    +          return null;
    +        }
    +
    +        return enumValue;
    +      }
    +
    +    case goog.proto2.FieldDescriptor.FieldType.BYTES:
    +    case goog.proto2.FieldDescriptor.FieldType.STRING:
    +      return this.consumeString_();
    +  }
    +};
    +
    +
    +/**
    + * Attempts to consume a nested message.
    + * @param {goog.proto2.Message} message The parent message.
    + * @param {goog.proto2.FieldDescriptor} field The field.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeNestedMessage_ =
    +    function(message, field) {
    +  var delimiter = '';
    +
    +  // Messages support both < > and { } as delimiters for legacy reasons.
    +  if (this.tryConsume_('<')) {
    +    delimiter = '>';
    +  } else {
    +    if (!this.consume_('{')) { return false; }
    +    delimiter = '}';
    +  }
    +
    +  var msg = field.getFieldMessageType().createMessageInstance();
    +  var result = this.consumeMessage_(msg, delimiter);
    +  if (!result) { return false; }
    +
    +  // Add the message to the parent message.
    +  if (field.isRepeated()) {
    +    message.add(field, msg);
    +  } else {
    +    message.set(field, msg);
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Attempts to consume the value of an unknown field. This method uses
    + * heuristics to try to consume just the right tokens.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeUnknownFieldValue_ =
    +    function() {
    +  // : is optional.
    +  this.tryConsume_(':');
    +
    +  // Handle form: [.. , ... , ..]
    +  if (this.tryConsume_('[')) {
    +    while (true) {
    +      this.tokenizer_.next();
    +      if (this.tryConsume_(']')) {
    +        break;
    +      }
    +      if (!this.consume_(',')) { return false; }
    +    }
    +
    +    return true;
    +  }
    +
    +  // Handle nested messages/groups.
    +  if (this.tryConsume_('<')) {
    +    return this.consumeMessage_(null /* unknown */, '>');
    +  } else if (this.tryConsume_('{')) {
    +    return this.consumeMessage_(null /* unknown */, '}');
    +  } else {
    +    // Otherwise, consume a single token for the field value.
    +    this.tokenizer_.next();
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Attempts to consume a field under a message.
    + * @param {goog.proto2.Message} message The parent message. If null, then the
    + *     field value will be consumed without being assigned to anything.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeField_ =
    +    function(message) {
    +  var fieldName = this.consumeIdentifier_();
    +  if (!fieldName) {
    +    this.reportError_('Missing field name');
    +    return false;
    +  }
    +
    +  var field = null;
    +  if (message) {
    +    field = message.getDescriptor().findFieldByName(fieldName.toString());
    +  }
    +
    +  if (field == null) {
    +    if (this.ignoreMissingFields_) {
    +      return this.consumeUnknownFieldValue_();
    +    } else {
    +      this.reportError_('Unknown field: ' + fieldName);
    +      return false;
    +    }
    +  }
    +
    +  if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
    +      field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.GROUP) {
    +    // : is optional here.
    +    this.tryConsume_(':');
    +    if (!this.consumeNestedMessage_(message, field)) { return false; }
    +  } else {
    +    // Long Format: "someField: 123"
    +    // Short Format: "someField: [123, 456, 789]"
    +    if (!this.consume_(':')) { return false; }
    +
    +    if (field.isRepeated() && this.tryConsume_('[')) {
    +      // Short repeated format, e.g.  "foo: [1, 2, 3]"
    +      while (true) {
    +        if (!this.consumeFieldValue_(message, field)) { return false; }
    +        if (this.tryConsume_(']')) {
    +          break;
    +        }
    +        if (!this.consume_(',')) { return false; }
    +      }
    +    } else {
    +      // Normal field format.
    +      if (!this.consumeFieldValue_(message, field)) { return false; }
    +    }
    +  }
    +
    +  // For historical reasons, fields may optionally be separated by commas or
    +  // semicolons.
    +  this.tryConsume_(',') || this.tryConsume_(';');
    +  return true;
    +};
    +
    +
    +/**
    + * Attempts to consume a token with the given string value.
    + * @param {string} value The string value for the token.
    + * @return {boolean} True if the token matches and was consumed, false
    + *    otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.tryConsume_ =
    +    function(value) {
    +  if (this.lookingAt_(value)) {
    +    this.tokenizer_.next();
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Consumes a token of the given type.
    + * @param {goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes} type The type
    + *     of the token to consume.
    + * @return {?string} The string value of the token or null on error.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeToken_ =
    +    function(type) {
    +  if (!this.lookingAtType_(type)) {
    +    this.reportError_('Expected token type: ' + type);
    +    return null;
    +  }
    +
    +  var value = this.tokenizer_.getCurrent().value;
    +  this.tokenizer_.next();
    +  return value;
    +};
    +
    +
    +/**
    + * Consumes an IDENTIFIER token.
    + * @return {?string} The string value or null on error.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeIdentifier_ =
    +    function() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  return this.consumeToken_(types.IDENTIFIER);
    +};
    +
    +
    +/**
    + * Consumes a NUMBER token.
    + * @return {?string} The string value or null on error.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeNumber_ =
    +    function() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  return this.consumeToken_(types.NUMBER);
    +};
    +
    +
    +/**
    + * Consumes a STRING token. Strings may come in multiple adjacent tokens which
    + * are automatically concatenated, like in C or Python.
    + * @return {?string} The *deescaped* string value or null on error.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consumeString_ =
    +    function() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  var value = this.consumeToken_(types.STRING);
    +  if (!value) {
    +    return null;
    +  }
    +
    +  var stringValue = goog.json.parse(value).toString();
    +  while (this.lookingAtType_(types.STRING)) {
    +    value = this.consumeToken_(types.STRING);
    +    stringValue += goog.json.parse(value).toString();
    +  }
    +
    +  return stringValue;
    +};
    +
    +
    +/**
    + * Consumes a token with the given value. If not found, reports an error.
    + * @param {string} value The string value expected for the token.
    + * @return {boolean} True on success, false otherwise.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.consume_ = function(value) {
    +  if (!this.tryConsume_(value)) {
    +    this.reportError_('Expected token "' + value + '"');
    +    return false;
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {string} value The value to check against.
    + * @return {boolean} True if the current token has the given string value.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.lookingAt_ =
    +    function(value) {
    +  return this.tokenizer_.getCurrent().value == value;
    +};
    +
    +
    +/**
    + * @param {goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes} type The
    + *     token type.
    + * @return {boolean} True if the current token has the given type.
    + * @private
    + */
    +goog.proto2.TextFormatSerializer.Parser.prototype.lookingAtType_ =
    +    function(type) {
    +  return this.tokenizer_.getCurrent().type == type;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.html b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.html
    new file mode 100644
    index 0000000000..bd8e43d276
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.proto2 - textformatserializer.js</title>
    +<script src="../base.js"></script>
    +</head>
    +<body>
    +<script>
    +goog.require('goog.proto2.TextFormatSerializerTest');
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.js b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.js
    new file mode 100644
    index 0000000000..62e994d496
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/textformatserializer_test.js
    @@ -0,0 +1,807 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.proto2.TextFormatSerializer.
    + *
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.proto2.TextFormatSerializerTest');
    +
    +goog.require('goog.proto2.ObjectSerializer');
    +goog.require('goog.proto2.TextFormatSerializer');
    +goog.require('goog.testing.jsunit');
    +goog.require('proto2.TestAllTypes');
    +
    +goog.setTestOnly('goog.proto2.TextFormatSerializerTest');
    +
    +function testSerialization() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Singular.
    +  message.setOptionalInt32(101);
    +  message.setOptionalUint32(103);
    +  message.setOptionalSint32(105);
    +  message.setOptionalFixed32(107);
    +  message.setOptionalSfixed32(109);
    +  message.setOptionalInt64('102');
    +  message.setOptionalFloat(111.5);
    +  message.setOptionalDouble(112.5);
    +  message.setOptionalBool(true);
    +  message.setOptionalString('test');
    +  message.setOptionalBytes('abcd');
    +
    +  var group = new proto2.TestAllTypes.OptionalGroup();
    +  group.setA(111);
    +
    +  message.setOptionalgroup(group);
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(112);
    +
    +  message.setOptionalNestedMessage(nestedMessage);
    +
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  // Repeated.
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +
    +  // Serialize to a simplified text format.
    +  var simplified = new goog.proto2.TextFormatSerializer().serialize(message);
    +  var expected = 'optional_int32: 101\n' +
    +      'optional_int64: 102\n' +
    +      'optional_uint32: 103\n' +
    +      'optional_sint32: 105\n' +
    +      'optional_fixed32: 107\n' +
    +      'optional_sfixed32: 109\n' +
    +      'optional_float: 111.5\n' +
    +      'optional_double: 112.5\n' +
    +      'optional_bool: true\n' +
    +      'optional_string: "test"\n' +
    +      'optional_bytes: "abcd"\n' +
    +      'optionalgroup {\n' +
    +      '  a: 111\n' +
    +      '}\n' +
    +      'optional_nested_message {\n' +
    +      '  b: 112\n' +
    +      '}\n' +
    +      'optional_nested_enum: FOO\n' +
    +      'repeated_int32: 201\n' +
    +      'repeated_int32: 202\n';
    +
    +  assertEquals(expected, simplified);
    +}
    +
    +function testSerializationOfUnknown() {
    +  var nestedUnknown = new proto2.TestAllTypes();
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Known.
    +  message.setOptionalInt32(101);
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +
    +  nestedUnknown.addRepeatedInt32(301);
    +  nestedUnknown.addRepeatedInt32(302);
    +
    +  // Unknown.
    +  message.setUnknown(1000, 301);
    +  message.setUnknown(1001, 302);
    +  message.setUnknown(1002, 'hello world');
    +  message.setUnknown(1002, nestedUnknown);
    +
    +  nestedUnknown.setUnknown(2000, 401);
    +
    +  // Serialize.
    +  var simplified = new goog.proto2.TextFormatSerializer().serialize(message);
    +  var expected = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'repeated_int32: 202\n' +
    +      '1000: 301\n' +
    +      '1001: 302\n' +
    +      '1002 {\n' +
    +      '  repeated_int32: 301\n' +
    +      '  repeated_int32: 302\n' +
    +      '  2000: 401\n' +
    +      '}\n';
    +
    +  assertEquals(expected, simplified);
    +}
    +
    +function testSerializationOfUnknownParsedFromObject() {
    +  // Construct the object-serialized representation of the message constructed
    +  // programmatically in the test above.
    +  var serialized = {
    +    1: 101,
    +    31: [201, 202],
    +    1000: 301,
    +    1001: 302,
    +    1002: {
    +      31: [301, 302],
    +      2000: 401
    +    }
    +  };
    +
    +  // Deserialize that representation into a TestAllTypes message.
    +  var objectSerializer = new goog.proto2.ObjectSerializer();
    +  var message = new proto2.TestAllTypes();
    +  objectSerializer.deserializeTo(message, serialized);
    +
    +  // Check that the text format matches what we expect.
    +  var simplified = new goog.proto2.TextFormatSerializer().serialize(message);
    +  var expected = (
    +      'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'repeated_int32: 202\n' +
    +      '1000: 301\n' +
    +      '1001: 302\n' +
    +      '1002 {\n' +
    +      '  31: 301\n' +
    +      '  31: 302\n' +
    +      '  2000: 401\n' +
    +      '}\n'
    +      );
    +  assertEquals(expected, simplified);
    +}
    +
    +
    +/**
    + * Asserts that the given string value parses into the given set of tokens.
    + * @param {string} value The string value to parse.
    + * @param {Array<Object> | Object} tokens The tokens to check against. If not
    + *     an array, a single token is expected.
    + * @param {boolean=} opt_ignoreWhitespace Whether whitespace tokens should be
    + *     skipped by the tokenizer.
    + */
    +function assertTokens(value, tokens, opt_ignoreWhitespace) {
    +  var tokenizer = new goog.proto2.TextFormatSerializer.Tokenizer_(
    +      value, opt_ignoreWhitespace);
    +  var tokensFound = [];
    +
    +  while (tokenizer.next()) {
    +    tokensFound.push(tokenizer.getCurrent());
    +  }
    +
    +  if (goog.typeOf(tokens) != 'array') {
    +    tokens = [tokens];
    +  }
    +
    +  assertEquals(tokens.length, tokensFound.length);
    +  for (var i = 0; i < tokens.length; ++i) {
    +    assertToken(tokens[i], tokensFound[i]);
    +  }
    +}
    +
    +function assertToken(expected, found) {
    +  assertEquals(expected.type, found.type);
    +  if (expected.value) {
    +    assertEquals(expected.value, found.value);
    +  }
    +}
    +
    +function testTokenizer() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens('{ 123 }', [
    +    { type: types.OPEN_BRACE },
    +    { type: types.WHITESPACE, value: ' ' },
    +    { type: types.NUMBER, value: '123' },
    +    { type: types.WHITESPACE, value: ' '},
    +    { type: types.CLOSE_BRACE }
    +  ]);
    +  // The c++ proto serializer might represent a float in exponential
    +  // notation:
    +  assertTokens('{ 1.2345e+3 }', [
    +    { type: types.OPEN_BRACE },
    +    { type: types.WHITESPACE, value: ' ' },
    +    { type: types.NUMBER, value: '1.2345e+3' },
    +    { type: types.WHITESPACE, value: ' '},
    +    { type: types.CLOSE_BRACE }
    +  ]);
    +}
    +
    +function testTokenizerExponentialFloatProblem() {
    +  var input = 'merchant: {              # blah blah\n' +
    +      '    total_price: 3.2186e+06      # 3_218_600; 3.07Mi\n' +
    +      '    taxes      : 2.17199e+06\n' +
    +      '}';
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens(input, [
    +    { type: types.IDENTIFIER, value: 'merchant' },
    +    { type: types.COLON, value: ':' },
    +    { type: types.OPEN_BRACE, value: '{' },
    +    { type: types.COMMENT, value: '# blah blah' },
    +    { type: types.IDENTIFIER, value: 'total_price' },
    +    { type: types.COLON, value: ':' },
    +    { type: types.NUMBER, value: '3.2186e+06' },
    +    { type: types.COMMENT, value: '# 3_218_600; 3.07Mi' },
    +    { type: types.IDENTIFIER, value: 'taxes' },
    +    { type: types.COLON, value: ':' },
    +    { type: types.NUMBER, value: '2.17199e+06' },
    +    { type: types.CLOSE_BRACE, value: '}' }
    +  ],
    +  true);
    +}
    +
    +function testTokenizerNoWhitespace() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens('{ "hello world" }', [
    +    { type: types.OPEN_BRACE },
    +    { type: types.STRING, value: '"hello world"' },
    +    { type: types.CLOSE_BRACE }
    +  ], true);
    +}
    +
    +
    +function assertIdentifier(identifier) {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens(identifier, { type: types.IDENTIFIER, value: identifier });
    +}
    +
    +function assertComment(comment) {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens(comment, { type: types.COMMENT, value: comment });
    +}
    +
    +function assertString(str) {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens(str, { type: types.STRING, value: str });
    +}
    +
    +function assertNumber(num) {
    +  num = num.toString();
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens(num, { type: types.NUMBER, value: num });
    +}
    +
    +function testTokenizerSingleTokens() {
    +  var types = goog.proto2.TextFormatSerializer.Tokenizer_.TokenTypes;
    +  assertTokens('{', { type: types.OPEN_BRACE });
    +  assertTokens('}', { type: types.CLOSE_BRACE });
    +  assertTokens('<', { type: types.OPEN_TAG });
    +  assertTokens('>', { type: types.CLOSE_TAG });
    +  assertTokens(':', { type: types.COLON });
    +  assertTokens(',', { type: types.COMMA });
    +  assertTokens(';', { type: types.SEMI });
    +
    +  assertIdentifier('abcd');
    +  assertIdentifier('Abcd');
    +  assertIdentifier('ABcd');
    +  assertIdentifier('ABcD');
    +  assertIdentifier('a123nc');
    +  assertIdentifier('a45_bC');
    +  assertIdentifier('A45_bC');
    +
    +  assertIdentifier('inf');
    +  assertIdentifier('infinity');
    +  assertIdentifier('nan');
    +
    +  assertNumber(0);
    +  assertNumber(10);
    +  assertNumber(123);
    +  assertNumber(1234);
    +  assertNumber(123.56);
    +  assertNumber(-124);
    +  assertNumber(-1234);
    +  assertNumber(-123.56);
    +  assertNumber('123f');
    +  assertNumber('123.6f');
    +  assertNumber('-123f');
    +  assertNumber('-123.8f');
    +  assertNumber('0x1234');
    +  assertNumber('0x12ac34');
    +  assertNumber('0x49e281db686fb');
    +  // Floating point numbers might be serialized in exponential
    +  // notation:
    +  assertNumber('1.2345e+3');
    +  assertNumber('1.2345e3');
    +  assertNumber('1.2345e-2');
    +
    +  assertString('""');
    +  assertString('"hello world"');
    +  assertString('"hello # world"');
    +  assertString('"hello #\\" world"');
    +  assertString('"|"');
    +  assertString('"\\"\\""');
    +  assertString('"\\"foo\\""');
    +  assertString('"\\"foo\\" and \\"bar\\""');
    +  assertString('"foo \\"and\\" bar"');
    +
    +  assertComment('# foo bar baz');
    +  assertComment('# foo ## bar baz');
    +  assertComment('# foo "bar" baz');
    +}
    +
    +function testSerializationOfStringWithQuotes() {
    +  var nestedUnknown = new proto2.TestAllTypes();
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalString('hello "world"');
    +
    +  // Serialize.
    +  var simplified = new goog.proto2.TextFormatSerializer().serialize(message);
    +  var expected = 'optional_string: "hello \\"world\\""\n';
    +  assertEquals(expected, simplified);
    +}
    +
    +function testDeserialization() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationOfList() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: [201, 202]\n' +
    +      'optional_float: 123.4';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationOfIntegerAsHexadecimalString() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 0x1\n' +
    +      'optional_sint32: 0xf\n' +
    +      'optional_uint32: 0xffffffff\n' +
    +      'repeated_int32: [0x0, 0xff]\n';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(1, message.getOptionalInt32());
    +  assertEquals(15, message.getOptionalSint32());
    +  assertEquals(4294967295, message.getOptionalUint32());
    +  assertEquals(0, message.getRepeatedInt32(0));
    +  assertEquals(255, message.getRepeatedInt32(1));
    +}
    +
    +function testDeserializationOfInt64AsHexadecimalString() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int64: 0xf';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals('0xf', message.getOptionalInt64());
    +}
    +
    +function testDeserializationOfZeroFalseAndEmptyString() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 0\n' +
    +      'optional_bool: false\n' +
    +      'optional_string: ""';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(0, message.getOptionalInt32());
    +  assertEquals(false, message.getOptionalBool());
    +  assertEquals('', message.getOptionalString());
    +}
    +
    +function testDeserializationOfConcatenatedString() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 123\n' +
    +      'optional_string:\n' +
    +      '    "FirstLine"\n' +
    +      '    "SecondLine"\n' +
    +      'optional_float: 456.7';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(123, message.getOptionalInt32());
    +  assertEquals('FirstLineSecondLine', message.getOptionalString());
    +  assertEquals(456.7, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipComment() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      '# Some comment.\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertTrue(parser.parse(message, value));
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipTrailingComment() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'repeated_int32: 202  # Some trailing comment.\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertTrue(parser.parse(message, value));
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipUnknown() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'some_unknown: true\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertTrue(parser.parse(message, value, true));
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipUnknownList() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'some_unknown: [true, 1, 201, "hello"]\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertTrue(parser.parse(message, value, true));
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipUnknownNested() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'some_unknown: <\n' +
    +      '  a: 1\n' +
    +      '  b: 2\n' +
    +      '>\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertTrue(parser.parse(message, value, true));
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationSkipUnknownNestedInvalid() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'some_unknown: <\n' +
    +      '  a: \n' + // Missing value.
    +      '  b: 2\n' +
    +      '>\n' +
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertFalse(parser.parse(message, value, true));
    +}
    +
    +function testDeserializationSkipUnknownNestedInvalid2() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'repeated_int32: 201\n' +
    +      'some_unknown: <\n' +
    +      '  a: 2\n' +
    +      '  b: 2\n' +
    +      '}\n' + // Delimiter mismatch
    +      'repeated_int32: 202\n' +
    +      'optional_float: 123.4';
    +
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  assertFalse(parser.parse(message, value, true));
    +}
    +
    +
    +function testDeserializationLegacyFormat() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101,\n' +
    +      'repeated_int32: 201,\n' +
    +      'repeated_int32: 202;\n' +
    +      'optional_float: 123.4';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(201, message.getRepeatedInt32(0));
    +  assertEquals(202, message.getRepeatedInt32(1));
    +  assertEquals(123.4, message.getOptionalFloat());
    +}
    +
    +function testDeserializationVariedNumbers() {
    +  var message = new proto2.TestAllTypes();
    +  var value = (
    +      'repeated_int32: 23\n' +
    +      'repeated_int32: -3\n' +
    +      'repeated_int32: 0xdeadbeef\n' +
    +      'repeated_float: 123.0\n' +
    +      'repeated_float: -3.27\n' +
    +      'repeated_float: -35.5f\n'
    +      );
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(23, message.getRepeatedInt32(0));
    +  assertEquals(-3, message.getRepeatedInt32(1));
    +  assertEquals(3735928559, message.getRepeatedInt32(2));
    +  assertEquals(123.0, message.getRepeatedFloat(0));
    +  assertEquals(-3.27, message.getRepeatedFloat(1));
    +  assertEquals(-35.5, message.getRepeatedFloat(2));
    +}
    +
    +function testDeserializationScientificNotation() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'repeated_float: 1.1e5\n' +
    +      'repeated_float: 1.1e-5\n' +
    +      'repeated_double: 1.1e5\n' +
    +      'repeated_double: 1.1e-5\n';
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +  assertEquals(1.1e5, message.getRepeatedFloat(0));
    +  assertEquals(1.1e-5, message.getRepeatedFloat(1));
    +  assertEquals(1.1e5, message.getRepeatedDouble(0));
    +  assertEquals(1.1e-5, message.getRepeatedDouble(1));
    +}
    +
    +function testParseNumericalConstant() {
    +  var parseNumericalConstant =
    +      goog.proto2.TextFormatSerializer.Parser.parseNumericalConstant_;
    +
    +  assertEquals(Infinity, parseNumericalConstant('inf'));
    +  assertEquals(Infinity, parseNumericalConstant('inff'));
    +  assertEquals(Infinity, parseNumericalConstant('infinity'));
    +  assertEquals(Infinity, parseNumericalConstant('infinityf'));
    +  assertEquals(Infinity, parseNumericalConstant('Infinityf'));
    +
    +  assertEquals(-Infinity, parseNumericalConstant('-inf'));
    +  assertEquals(-Infinity, parseNumericalConstant('-inff'));
    +  assertEquals(-Infinity, parseNumericalConstant('-infinity'));
    +  assertEquals(-Infinity, parseNumericalConstant('-infinityf'));
    +  assertEquals(-Infinity, parseNumericalConstant('-Infinity'));
    +
    +  assertNull(parseNumericalConstant('-infin'));
    +  assertNull(parseNumericalConstant('infin'));
    +  assertNull(parseNumericalConstant('-infinite'));
    +
    +  assertNull(parseNumericalConstant('-infin'));
    +  assertNull(parseNumericalConstant('infin'));
    +  assertNull(parseNumericalConstant('-infinite'));
    +
    +  assertTrue(isNaN(parseNumericalConstant('Nan')));
    +  assertTrue(isNaN(parseNumericalConstant('NaN')));
    +  assertTrue(isNaN(parseNumericalConstant('NAN')));
    +  assertTrue(isNaN(parseNumericalConstant('nan')));
    +  assertTrue(isNaN(parseNumericalConstant('nanf')));
    +  assertTrue(isNaN(parseNumericalConstant('NaNf')));
    +
    +  assertEquals(Number.POSITIVE_INFINITY, parseNumericalConstant('infinity'));
    +  assertEquals(Number.NEGATIVE_INFINITY, parseNumericalConstant('-inf'));
    +  assertEquals(Number.NEGATIVE_INFINITY, parseNumericalConstant('-infinity'));
    +
    +  assertNull(parseNumericalConstant('na'));
    +  assertNull(parseNumericalConstant('-nan'));
    +  assertNull(parseNumericalConstant('none'));
    +}
    +
    +function testDeserializationOfNumericalConstants() {
    +
    +  var message = new proto2.TestAllTypes();
    +  var value = (
    +      'repeated_float: inf\n' +
    +      'repeated_float: -inf\n' +
    +      'repeated_float: nan\n' +
    +      'repeated_float: 300.2\n'
    +      );
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(Infinity, message.getRepeatedFloat(0));
    +  assertEquals(-Infinity, message.getRepeatedFloat(1));
    +  assertTrue(isNaN(message.getRepeatedFloat(2)));
    +  assertEquals(300.2, message.getRepeatedFloat(3));
    +}
    +
    +var floatFormatCases = [{given: '1.69e+06', expect: 1.69e+06},
    +                        {given: '1.69e6', expect: 1.69e+06},
    +                        {given: '2.468e-2', expect: 0.02468}
    +                       ];
    +
    +function testGetNumberFromStringExponentialNotation() {
    +  for (var i = 0; i < floatFormatCases.length; ++i) {
    +    var thistest = floatFormatCases[i];
    +    var result = goog.proto2.TextFormatSerializer.Parser.
    +        getNumberFromString_(thistest.given);
    +    assertEquals(thistest.expect, result);
    +  }
    +}
    +
    +function testDeserializationExponentialFloat() {
    +  var parser = new goog.proto2.TextFormatSerializer.Parser();
    +  for (var i = 0; i < floatFormatCases.length; ++i) {
    +    var thistest = floatFormatCases[i];
    +    var message = new proto2.TestAllTypes();
    +    var value = 'optional_float: ' + thistest.given;
    +    assertTrue(parser.parse(message, value, true));
    +    assertEquals(thistest.expect, message.getOptionalFloat());
    +  }
    +}
    +
    +function testGetNumberFromString() {
    +  var getNumberFromString =
    +      goog.proto2.TextFormatSerializer.Parser.getNumberFromString_;
    +
    +  assertEquals(3735928559, getNumberFromString('0xdeadbeef'));
    +  assertEquals(4276215469, getNumberFromString('0xFEE1DEAD'));
    +  assertEquals(123.1, getNumberFromString('123.1'));
    +  assertEquals(123.0, getNumberFromString('123.0'));
    +  assertEquals(-29.3, getNumberFromString('-29.3f'));
    +  assertEquals(23, getNumberFromString('23'));
    +  assertEquals(-3, getNumberFromString('-3'));
    +  assertEquals(-3.27, getNumberFromString('-3.27'));
    +
    +  assertThrows(goog.partial(getNumberFromString, 'cat'));
    +  assertThrows(goog.partial(getNumberFromString, 'NaN'));
    +  assertThrows(goog.partial(getNumberFromString, 'inf'));
    +}
    +
    +function testDeserializationError() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int33: 101\n';
    +  var result =
    +      new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +  assertEquals(result, 'Unknown field: optional_int33');
    +}
    +
    +function testNestedDeserialization() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'optional_nested_message: {\n' +
    +      '  b: 301\n' +
    +      '}';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(301, message.getOptionalNestedMessage().getB());
    +}
    +
    +function testNestedDeserializationLegacyFormat() {
    +  var message = new proto2.TestAllTypes();
    +  var value = 'optional_int32: 101\n' +
    +      'optional_nested_message: <\n' +
    +      '  b: 301\n' +
    +      '>';
    +
    +  new goog.proto2.TextFormatSerializer().deserializeTo(message, value);
    +
    +  assertEquals(101, message.getOptionalInt32());
    +  assertEquals(301, message.getOptionalNestedMessage().getB());
    +}
    +
    +function testBidirectional() {
    +  var message = new proto2.TestAllTypes();
    +
    +  // Set the fields.
    +  // Singular.
    +  message.setOptionalInt32(101);
    +  message.setOptionalInt64('102');
    +  message.setOptionalUint32(103);
    +  message.setOptionalUint64('104');
    +  message.setOptionalSint32(105);
    +  message.setOptionalSint64('106');
    +  message.setOptionalFixed32(107);
    +  message.setOptionalFixed64('108');
    +  message.setOptionalSfixed32(109);
    +  message.setOptionalSfixed64('110');
    +  message.setOptionalFloat(111.5);
    +  message.setOptionalDouble(112.5);
    +  message.setOptionalBool(true);
    +  message.setOptionalString('test');
    +  message.setOptionalBytes('abcd');
    +
    +  var group = new proto2.TestAllTypes.OptionalGroup();
    +  group.setA(111);
    +
    +  message.setOptionalgroup(group);
    +
    +  var nestedMessage = new proto2.TestAllTypes.NestedMessage();
    +  nestedMessage.setB(112);
    +
    +  message.setOptionalNestedMessage(nestedMessage);
    +
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  // Repeated.
    +  message.addRepeatedInt32(201);
    +  message.addRepeatedInt32(202);
    +  message.addRepeatedString('hello "world"');
    +
    +  // Serialize the message to text form.
    +  var serializer = new goog.proto2.TextFormatSerializer();
    +  var textform = serializer.serialize(message);
    +
    +  // Create a copy and deserialize into the copy.
    +  var copy = new proto2.TestAllTypes();
    +  serializer.deserializeTo(copy, textform);
    +
    +  // Assert that the messages are structurally equivalent.
    +  assertTrue(copy.equals(message));
    +}
    +
    +function testBidirectional64BitNumber() {
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalInt64Number(10000000);
    +  message.setOptionalInt64String('200000000000000000');
    +
    +  // Serialize the message to text form.
    +  var serializer = new goog.proto2.TextFormatSerializer();
    +  var textform = serializer.serialize(message);
    +
    +  // Create a copy and deserialize into the copy.
    +  var copy = new proto2.TestAllTypes();
    +  serializer.deserializeTo(copy, textform);
    +
    +  // Assert that the messages are structurally equivalent.
    +  assertTrue(copy.equals(message));
    +}
    +
    +function testUseEnumValues() {
    +  var message = new proto2.TestAllTypes();
    +  message.setOptionalNestedEnum(proto2.TestAllTypes.NestedEnum.FOO);
    +
    +  var serializer = new goog.proto2.TextFormatSerializer(false, true);
    +  var textform = serializer.serialize(message);
    +
    +  var expected = 'optional_nested_enum: 0\n';
    +
    +  assertEquals(expected, textform);
    +
    +  var deserializedMessage = new proto2.TestAllTypes();
    +  serializer.deserializeTo(deserializedMessage, textform);
    +
    +  assertEquals(
    +      proto2.TestAllTypes.NestedEnum.FOO,
    +      deserializedMessage.getOptionalNestedEnum());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/proto2/util.js b/mapbox-gl/closure-library/closure/goog/proto2/util.js
    new file mode 100644
    index 0000000000..62c1e44dc2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/proto2/util.js
    @@ -0,0 +1,54 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility methods for Protocol Buffer 2 implementation.
    + */
    +
    +goog.provide('goog.proto2.Util');
    +
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * @define {boolean} Defines a PBCHECK constant that can be turned off by
    + * clients of PB2. This for is clients that do not want assertion/checking
    + * running even in non-COMPILED builds.
    + */
    +goog.define('goog.proto2.Util.PBCHECK', !COMPILED);
    +
    +
    +/**
    + * Asserts that the given condition is true, if and only if the PBCHECK
    + * flag is on.
    + *
    + * @param {*} condition The condition to check.
    + * @param {string=} opt_message Error message in case of failure.
    + * @throws {Error} Assertion failed, the condition evaluates to false.
    + */
    +goog.proto2.Util.assert = function(condition, opt_message) {
    +  if (goog.proto2.Util.PBCHECK) {
    +    goog.asserts.assert(condition, opt_message);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if debug assertions (checks) are on.
    + *
    + * @return {boolean} The value of the PBCHECK constant.
    + */
    +goog.proto2.Util.conductChecks = function() {
    +  return goog.proto2.Util.PBCHECK;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/pubsub.js b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub.js
    new file mode 100644
    index 0000000000..e7a2698ace
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub.js
    @@ -0,0 +1,335 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview  Topic-based publish/subscribe channel implementation.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.pubsub.PubSub');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.array');
    +
    +
    +
    +/**
    + * Topic-based publish/subscribe channel.  Maintains a map of topics to
    + * subscriptions.  When a message is published to a topic, all functions
    + * subscribed to that topic are invoked in the order they were added.
    + * Uncaught errors abort publishing.
    + *
    + * Topics may be identified by any nonempty string, <strong>except</strong>
    + * strings corresponding to native Object properties, e.g. "constructor",
    + * "toString", "hasOwnProperty", etc.
    + *
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.pubsub.PubSub = function() {
    +  goog.Disposable.call(this);
    +  this.subscriptions_ = [];
    +  this.topics_ = {};
    +};
    +goog.inherits(goog.pubsub.PubSub, goog.Disposable);
    +
    +
    +/**
    + * Sparse array of subscriptions.  Each subscription is represented by a tuple
    + * comprising a topic identifier, a function, and an optional context object.
    + * Each tuple occupies three consecutive positions in the array, with the topic
    + * identifier at index n, the function at index (n + 1), the context object at
    + * index (n + 2), the next topic at index (n + 3), etc.  (This representation
    + * minimizes the number of object allocations and has been shown to be faster
    + * than an array of objects with three key-value pairs or three parallel arrays,
    + * especially on IE.)  Once a subscription is removed via {@link #unsubscribe}
    + * or {@link #unsubscribeByKey}, the three corresponding array elements are
    + * deleted, and never reused.  This means the total number of subscriptions
    + * during the lifetime of the pubsub channel is limited by the maximum length
    + * of a JavaScript array to (2^32 - 1) / 3 = 1,431,655,765 subscriptions, which
    + * should suffice for most applications.
    + *
    + * @type {!Array<?>}
    + * @private
    + */
    +goog.pubsub.PubSub.prototype.subscriptions_;
    +
    +
    +/**
    + * The next available subscription key.  Internally, this is an index into the
    + * sparse array of subscriptions.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.pubsub.PubSub.prototype.key_ = 1;
    +
    +
    +/**
    + * Map of topics to arrays of subscription keys.
    + *
    + * @type {!Object<!Array<number>>}
    + * @private
    + */
    +goog.pubsub.PubSub.prototype.topics_;
    +
    +
    +/**
    + * Array of subscription keys pending removal once publishing is done.
    + *
    + * @type {Array<number>}
    + * @private
    + */
    +goog.pubsub.PubSub.prototype.pendingKeys_;
    +
    +
    +/**
    + * Lock to prevent the removal of subscriptions during publishing.  Incremented
    + * at the beginning of {@link #publish}, and decremented at the end.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.pubsub.PubSub.prototype.publishDepth_ = 0;
    +
    +
    +/**
    + * Subscribes a function to a topic.  The function is invoked as a method on
    + * the given {@code opt_context} object, or in the global scope if no context
    + * is specified.  Subscribing the same function to the same topic multiple
    + * times will result in multiple function invocations while publishing.
    + * Returns a subscription key that can be used to unsubscribe the function from
    + * the topic via {@link #unsubscribeByKey}.
    + *
    + * @param {string} topic Topic to subscribe to.
    + * @param {Function} fn Function to be invoked when a message is published to
    + *     the given topic.
    + * @param {Object=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + */
    +goog.pubsub.PubSub.prototype.subscribe = function(topic, fn, opt_context) {
    +  var keys = this.topics_[topic];
    +  if (!keys) {
    +    // First subscription to this topic; initialize subscription key array.
    +    keys = this.topics_[topic] = [];
    +  }
    +
    +  // Push the tuple representing the subscription onto the subscription array.
    +  var key = this.key_;
    +  this.subscriptions_[key] = topic;
    +  this.subscriptions_[key + 1] = fn;
    +  this.subscriptions_[key + 2] = opt_context;
    +  this.key_ = key + 3;
    +
    +  // Push the subscription key onto the list of subscriptions for the topic.
    +  keys.push(key);
    +
    +  // Return the subscription key.
    +  return key;
    +};
    +
    +
    +/**
    + * Subscribes a single-use function to a topic.  The function is invoked as a
    + * method on the given {@code opt_context} object, or in the global scope if
    + * no context is specified, and is then unsubscribed.  Returns a subscription
    + * key that can be used to unsubscribe the function from the topic via
    + * {@link #unsubscribeByKey}.
    + *
    + * @param {string} topic Topic to subscribe to.
    + * @param {Function} fn Function to be invoked once and then unsubscribed when
    + *     a message is published to the given topic.
    + * @param {Object=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + */
    +goog.pubsub.PubSub.prototype.subscribeOnce = function(topic, fn, opt_context) {
    +  // Behold the power of lexical closures!
    +  var key = this.subscribe(topic, function(var_args) {
    +    fn.apply(opt_context, arguments);
    +    this.unsubscribeByKey(key);
    +  }, this);
    +  return key;
    +};
    +
    +
    +/**
    + * Unsubscribes a function from a topic.  Only deletes the first match found.
    + * Returns a Boolean indicating whether a subscription was removed.
    + *
    + * @param {string} topic Topic to unsubscribe from.
    + * @param {Function} fn Function to unsubscribe.
    + * @param {Object=} opt_context Object in whose context the function was to be
    + *     called (the global scope if none).
    + * @return {boolean} Whether a matching subscription was removed.
    + */
    +goog.pubsub.PubSub.prototype.unsubscribe = function(topic, fn, opt_context) {
    +  var keys = this.topics_[topic];
    +  if (keys) {
    +    // Find the subscription key for the given combination of topic, function,
    +    // and context object.
    +    var subscriptions = this.subscriptions_;
    +    var key = goog.array.find(keys, function(k) {
    +      return subscriptions[k + 1] == fn && subscriptions[k + 2] == opt_context;
    +    });
    +    // Zero is not a valid key.
    +    if (key) {
    +      return this.unsubscribeByKey(/** @type {number} */ (key));
    +    }
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Removes a subscription based on the key returned by {@link #subscribe}.
    + * No-op if no matching subscription is found.  Returns a Boolean indicating
    + * whether a subscription was removed.
    + *
    + * @param {number} key Subscription key.
    + * @return {boolean} Whether a matching subscription was removed.
    + */
    +goog.pubsub.PubSub.prototype.unsubscribeByKey = function(key) {
    +  if (this.publishDepth_ != 0) {
    +    // Defer removal until after publishing is complete.
    +    if (!this.pendingKeys_) {
    +      this.pendingKeys_ = [];
    +    }
    +    this.pendingKeys_.push(key);
    +    return false;
    +  }
    +
    +  var topic = this.subscriptions_[key];
    +  if (topic) {
    +    // Subscription tuple found.
    +    var keys = this.topics_[topic];
    +    if (keys) {
    +      goog.array.remove(keys, key);
    +    }
    +    delete this.subscriptions_[key];
    +    delete this.subscriptions_[key + 1];
    +    delete this.subscriptions_[key + 2];
    +  }
    +
    +  return !!topic;
    +};
    +
    +
    +/**
    + * Publishes a message to a topic.  Calls functions subscribed to the topic in
    + * the order in which they were added, passing all arguments along.  If any of
    + * the functions throws an uncaught error, publishing is aborted.
    + *
    + * @param {string} topic Topic to publish to.
    + * @param {...*} var_args Arguments that are applied to each subscription
    + *     function.
    + * @return {boolean} Whether any subscriptions were called.
    + */
    +goog.pubsub.PubSub.prototype.publish = function(topic, var_args) {
    +  var keys = this.topics_[topic];
    +  if (keys) {
    +    // We must lock subscriptions and remove them at the end, so we don't
    +    // adversely affect the performance of the common case by cloning the key
    +    // array.
    +    this.publishDepth_++;
    +
    +    // Copy var_args to a new array so they can be passed to subscribers.
    +    // Note that we can't use Array.slice or goog.array.toArray for this for
    +    // performance reasons. Using those with the arguments object will cause
    +    // deoptimization.
    +    var args = new Array(arguments.length - 1);
    +    for (var i = 1, len = arguments.length; i < len; i++) {
    +      args[i - 1] = arguments[i];
    +    }
    +
    +    // For each key in the list of subscription keys for the topic, apply the
    +    // function to the arguments in the appropriate context.  The length of the
    +    // array mush be fixed during the iteration, since subscribers may add new
    +    // subscribers during publishing.
    +    for (var i = 0, len = keys.length; i < len; i++) {
    +      var key = keys[i];
    +      this.subscriptions_[key + 1].apply(this.subscriptions_[key + 2], args);
    +    }
    +
    +    // Unlock subscriptions.
    +    this.publishDepth_--;
    +
    +    if (this.pendingKeys_ && this.publishDepth_ == 0) {
    +      var pendingKey;
    +      while ((pendingKey = this.pendingKeys_.pop())) {
    +        this.unsubscribeByKey(pendingKey);
    +      }
    +    }
    +
    +    // At least one subscriber was called.
    +    return i != 0;
    +  }
    +
    +  // No subscribers were found.
    +  return false;
    +};
    +
    +
    +/**
    + * Clears the subscription list for a topic, or all topics if unspecified.
    + * @param {string=} opt_topic Topic to clear (all topics if unspecified).
    + */
    +goog.pubsub.PubSub.prototype.clear = function(opt_topic) {
    +  if (opt_topic) {
    +    var keys = this.topics_[opt_topic];
    +    if (keys) {
    +      goog.array.forEach(keys, this.unsubscribeByKey, this);
    +      delete this.topics_[opt_topic];
    +    }
    +  } else {
    +    this.subscriptions_.length = 0;
    +    this.topics_ = {};
    +    // We don't reset key_ on purpose, because we want subscription keys to be
    +    // unique throughout the lifetime of the application.  Reusing subscription
    +    // keys could lead to subtle errors in client code.
    +  }
    +};
    +
    +
    +/**
    + * Returns the number of subscriptions to the given topic (or all topics if
    + * unspecified).
    + * @param {string=} opt_topic The topic (all topics if unspecified).
    + * @return {number} Number of subscriptions to the topic.
    + */
    +goog.pubsub.PubSub.prototype.getCount = function(opt_topic) {
    +  if (opt_topic) {
    +    var keys = this.topics_[opt_topic];
    +    return keys ? keys.length : 0;
    +  }
    +
    +  var count = 0;
    +  for (var topic in this.topics_) {
    +    count += this.getCount(topic);
    +  }
    +
    +  return count;
    +};
    +
    +
    +/** @override */
    +goog.pubsub.PubSub.prototype.disposeInternal = function() {
    +  goog.pubsub.PubSub.superClass_.disposeInternal.call(this);
    +  delete this.subscriptions_;
    +  delete this.topics_;
    +  delete this.pendingKeys_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_perf.html b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_perf.html
    new file mode 100644
    index 0000000000..17f31bd8fa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_perf.html
    @@ -0,0 +1,290 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.pubsub.PubSub</title>
    +  <link rel="stylesheet" href="../testing/performancetable.css" />
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.events');
    +    goog.require('goog.events.EventTarget');
    +    goog.require('goog.pubsub.PubSub');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.PerformanceTimer');
    +    goog.require('goog.testing.jsunit');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.pubsub.PubSub Performance Tests</h1>
    +  <p>
    +    <b>User-agent:</b> <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <p>
    +    Compares the performance of the event system (<code>goog.events.*</code>)
    +    with the <code>goog.pubsub.PubSub</code> class.
    +  </p>
    +  <p>
    +    The baseline test creates 1000 event targets and 1000 objects that handle
    +    events dispatched by the event targets, and has each event target dispatch
    +    2 events 5 times each.
    +  </p>
    +  <p>
    +    The single-<code>PubSub</code> test creates 1000 publishers, 1000
    +    subscribers, and a single pubsub channel.  Each subscriber subscribes to
    +    topics on the same pubsub channel.  Each publisher publishes 5 messages to
    +    2 topics each via the pubsub channel.
    +  </p>
    +  <p>
    +    The multi-<code>PubSub</code> test creates 1000 publishers that are
    +    subclasses of <code>goog.pubsub.PubSub</code> and 1000 subscribers.  Each
    +    subscriber subscribes to its own publisher.  Each publisher publishes 5
    +    messages to 2 topics each via its own pubsub channel.
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +  <script>
    +    var targets, publishers, pubsubs, handlers;
    +
    +    // Number of objects to test per run.
    +    var SAMPLES_PER_RUN = 1000;
    +
    +    // The performance table & performance timer.
    +    var table, timer;
    +
    +    // Event/topic identifiers.
    +    var ACTION = 'action';
    +    var CHANGE = 'change';
    +
    +    // Number of times handlers have been called.
    +    var actionCount = 0;
    +    var changeCount = 0;
    +
    +    // Generic event handler class.
    +    function Handler() {
    +    }
    +    Handler.prototype.handleAction = function() {
    +      actionCount++;
    +    };
    +    Handler.prototype.handleChange = function() {
    +      changeCount++;
    +    };
    +
    +    // Generic publisher class that uses a global pubsub channel.
    +    function Publisher(pubsub, id) {
    +      this.pubsub = pubsub;
    +      this.id = id;
    +    }
    +    Publisher.prototype.publish = function(topic) {
    +      this.pubsub.publish(this.id + '.' + topic);
    +    };
    +
    +    // PubSub subclass; allows clients to subscribe and uses itself to publish.
    +    function PubSub() {
    +      goog.pubsub.PubSub.call(this);
    +    }
    +    goog.inherits(PubSub, goog.pubsub.PubSub);
    +
    +    // EventTarget subclass; uses goog.events.* to dispatch events.
    +    function Target() {
    +      goog.events.EventTarget.call(this);
    +    }
    +    goog.inherits(Target, goog.events.EventTarget);
    +    Target.prototype.fireEvent = function(type) {
    +      this.dispatchEvent(type);
    +    };
    +
    +    function initHandlers(count) {
    +      for (var i = 0; i < count; i++) {
    +        handlers[i] = new Handler();
    +      }
    +    }
    +
    +    function initPublishers(pubsub, count) {
    +      for (var i = 0; i < count; i++) {
    +        publishers[i] = new Publisher(pubsub, i);
    +      }
    +    }
    +
    +    function initPubSubs(count) {
    +      for (var i = 0; i < count; i++) {
    +        pubsubs[i] = new PubSub();
    +      }
    +    }
    +
    +    function initTargets(count) {
    +      for (var i = 0; i < count; i++) {
    +        targets[i] = new Target();
    +      }
    +    }
    +
    +    function createEventListeners(count) {
    +      initHandlers(count);
    +      initTargets(count);
    +      for (var i = 0; i < count; i++) {
    +        goog.events.listen(targets[i], ACTION, Handler.prototype.handleAction,
    +            false, handlers[i]);
    +        goog.events.listen(targets[i], CHANGE, Handler.prototype.handleChange,
    +            false, handlers[i]);
    +      }
    +    }
    +
    +    function createGlobalSubscriptions(pubsub, count) {
    +      initHandlers(count);
    +      initPublishers(pubsub, count);
    +      for (var i = 0; i < count; i++) {
    +        pubsub.subscribe(i + '.' + ACTION, Handler.prototype.handleAction,
    +            handlers[i]);
    +        pubsub.subscribe(i + '.' + CHANGE, Handler.prototype.handleChange,
    +            handlers[i]);
    +      }
    +    }
    +
    +    function createSubscriptions(count) {
    +      initHandlers(count);
    +      initPubSubs(count);
    +      for (var i = 0; i < count; i++) {
    +        pubsubs[i].subscribe(ACTION, Handler.prototype.handleAction,
    +            handlers[i]);
    +        pubsubs[i].subscribe(CHANGE, Handler.prototype.handleChange,
    +            handlers[i]);
    +      }
    +    }
    +
    +    function dispatchEvents(count) {
    +      for (var i = 0; i < count; i++) {
    +        for (var j = 0; j < 5; j++) {
    +          targets[i].fireEvent(ACTION);
    +          targets[i].fireEvent(CHANGE);
    +        }
    +      }
    +    }
    +
    +    function publishGlobalMessages(count) {
    +      for (var i = 0; i < count; i++) {
    +        for (var j = 0; j < 5; j++) {
    +          publishers[i].publish(ACTION);
    +          publishers[i].publish(CHANGE);
    +        }
    +      }
    +    }
    +
    +    function publishMessages(count) {
    +      for (var i = 0; i < count; i++) {
    +        for (var j = 0; j < 5; j++) {
    +          pubsubs[i].publish(ACTION);
    +          pubsubs[i].publish(CHANGE);
    +        }
    +      }
    +    }
    +
    +    function setUpPage() {
    +      timer = new goog.testing.PerformanceTimer();
    +      timer.setNumSamples(10);
    +      timer.setTimeoutInterval(9000);
    +      timer.setDiscardOutliers(true);
    +      table = new goog.testing.PerformanceTable(
    +          goog.dom.getElement('perfTable'), timer);
    +    }
    +
    +    function setUp() {
    +      actionCount = 0;
    +      changeCount = 0;
    +      handlers = [];
    +      publishers = [];
    +      pubsubs = [];
    +      targets = [];
    +    }
    +
    +    function testCreateEventListeners() {
    +      table.run(goog.partial(createEventListeners, SAMPLES_PER_RUN),
    +          '1A: Create event listeners');
    +      assertEquals(0, actionCount);
    +      assertEquals(0, changeCount);
    +    }
    +
    +    function testCreateGlobalSubscriptions() {
    +      var pubsub = new goog.pubsub.PubSub();
    +      table.run(
    +          goog.partial(createGlobalSubscriptions, pubsub, SAMPLES_PER_RUN),
    +          '1B: Create global subscriptions');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 2,
    +          pubsub.getCount());
    +      assertEquals(0, actionCount);
    +      assertEquals(0, changeCount);
    +      pubsub.dispose();
    +    }
    +
    +    function testCreateSubscripions() {
    +      table.run(goog.partial(createSubscriptions, SAMPLES_PER_RUN),
    +          '1C: Create subscriptions');
    +      assertEquals(0, actionCount);
    +      assertEquals(0, changeCount);
    +    }
    +
    +    function testDispatchEvents() {
    +      createEventListeners(SAMPLES_PER_RUN);
    +      table.run(goog.partial(dispatchEvents, SAMPLES_PER_RUN),
    +          '2A: Dispatch events');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +    }
    +
    +    function testPublishGlobalMessages() {
    +      var pubsub = new goog.pubsub.PubSub();
    +      createGlobalSubscriptions(pubsub, SAMPLES_PER_RUN);
    +      table.run(
    +          goog.partial(publishGlobalMessages, SAMPLES_PER_RUN),
    +          '2B: Publish global messages');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +      pubsub.dispose();
    +    }
    +
    +    function testPublishMessages() {
    +      createSubscriptions(SAMPLES_PER_RUN);
    +      table.run(goog.partial(publishMessages, SAMPLES_PER_RUN),
    +          '2C: Publish messages');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +    }
    +
    +    function testEvents() {
    +      table.run(function() {
    +        createEventListeners(SAMPLES_PER_RUN);
    +        dispatchEvents(SAMPLES_PER_RUN);
    +      }, '3A: Events');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +    }
    +
    +    function testSinglePubSub() {
    +      table.run(function() {
    +        var pubsub = new goog.pubsub.PubSub();
    +        createGlobalSubscriptions(pubsub, SAMPLES_PER_RUN);
    +        publishGlobalMessages(SAMPLES_PER_RUN);
    +        pubsub.dispose();
    +      }, '3B: Single PubSub');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +    }
    +
    +    function testMultiPubSub() {
    +      table.run(function() {
    +        createSubscriptions(SAMPLES_PER_RUN);
    +        publishMessages(SAMPLES_PER_RUN);
    +      }, '3C: Multi PubSub');
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, actionCount);
    +      assertEquals(SAMPLES_PER_RUN * timer.getNumSamples() * 5, changeCount);
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.html b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.html
    new file mode 100644
    index 0000000000..95add1cf29
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.pubsub.PubSub
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.pubsub.PubSubTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.js b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.js
    new file mode 100644
    index 0000000000..191720785b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/pubsub_test.js
    @@ -0,0 +1,631 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.pubsub.PubSubTest');
    +goog.setTestOnly('goog.pubsub.PubSubTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.pubsub.PubSub');
    +goog.require('goog.testing.jsunit');
    +
    +var pubsub;
    +
    +function setUp() {
    +  pubsub = new goog.pubsub.PubSub();
    +}
    +
    +function tearDown() {
    +  pubsub.dispose();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('PubSub instance must not be null', pubsub);
    +  assertTrue('PubSub instance must have the expected type',
    +      pubsub instanceof goog.pubsub.PubSub);
    +}
    +
    +function testDispose() {
    +  assertFalse('PubSub instance must not have been disposed of',
    +      pubsub.isDisposed());
    +  pubsub.dispose();
    +  assertTrue('PubSub instance must have been disposed of',
    +      pubsub.isDisposed());
    +}
    +
    +function testSubscribeUnsubscribe() {
    +  function foo1() {
    +  }
    +  function bar1() {
    +  }
    +  function foo2() {
    +  }
    +  function bar2() {
    +  }
    +
    +  assertEquals('Topic "foo" must not have any subscribers', 0,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must not have any subscribers', 0,
    +      pubsub.getCount('bar'));
    +
    +  pubsub.subscribe('foo', foo1);
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must not have any subscribers', 0,
    +      pubsub.getCount('bar'));
    +
    +  pubsub.subscribe('bar', bar1);
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount('bar'));
    +
    +  pubsub.subscribe('foo', foo2);
    +  assertEquals('Topic "foo" must have 2 subscribers', 2,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount('bar'));
    +
    +  pubsub.subscribe('bar', bar2);
    +  assertEquals('Topic "foo" must have 2 subscribers', 2,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount('bar'));
    +
    +  assertTrue(pubsub.unsubscribe('foo', foo1));
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount('bar'));
    +
    +  assertTrue(pubsub.unsubscribe('foo', foo2));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount('bar'));
    +
    +  assertTrue(pubsub.unsubscribe('bar', bar1));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount('bar'));
    +
    +  assertTrue(pubsub.unsubscribe('bar', bar2));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount('foo'));
    +  assertEquals('Topic "bar" must have no subscribers', 0,
    +      pubsub.getCount('bar'));
    +
    +  assertFalse('Unsubscribing a nonexistent topic must return false',
    +      pubsub.unsubscribe('baz', foo1));
    +
    +  assertFalse('Unsubscribing a nonexistent function must return false',
    +      pubsub.unsubscribe('foo', function() {}));
    +}
    +
    +function testSubscribeUnsubscribeWithContext() {
    +  function foo() {
    +  }
    +  function bar() {
    +  }
    +
    +  var contextA = {};
    +  var contextB = {};
    +
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount('X'));
    +
    +  pubsub.subscribe('X', foo, contextA);
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +
    +  pubsub.subscribe('X', bar);
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount('X'));
    +
    +  pubsub.subscribe('X', bar, contextB);
    +  assertEquals('Topic "X" must have 3 subscribers', 3,
    +      pubsub.getCount('X'));
    +
    +  assertFalse('Unknown function/context combination return false',
    +      pubsub.unsubscribe('X', foo, contextB));
    +
    +  assertTrue(pubsub.unsubscribe('X', foo, contextA));
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount('X'));
    +
    +  assertTrue(pubsub.unsubscribe('X', bar));
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +
    +  assertTrue(pubsub.unsubscribe('X', bar, contextB));
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount('X'));
    +}
    +
    +function testSubscribeOnce() {
    +  var called, context;
    +
    +  called = false;
    +  pubsub.subscribeOnce('someTopic', function() {
    +    called = true;
    +  });
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('Subscriber must not have been called yet', called);
    +
    +  pubsub.publish('someTopic');
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('Subscriber must have been called', called);
    +
    +  context = {called: false};
    +  pubsub.subscribeOnce('someTopic', function() {
    +    this.called = true;
    +  }, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +
    +  pubsub.publish('someTopic');
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('Subscriber must have been called', context.called);
    +
    +  context = {called: false, value: 0};
    +  pubsub.subscribeOnce('someTopic', function(value) {
    +    this.called = true;
    +    this.value = value;
    +  }, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +  assertEquals('Value must have expected value', 0, context.value);
    +
    +  pubsub.publish('someTopic', 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('Subscriber must have been called', context.called);
    +  assertEquals('Value must have been updated', 17, context.value);
    +}
    +
    +function testSubscribeOnce_boundFn() {
    +  var context = {called: false, value: 0};
    +
    +  function subscriber(value) {
    +    this.called = true;
    +    this.value = value;
    +  }
    +
    +  pubsub.subscribeOnce('someTopic', goog.bind(subscriber, context));
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +  assertEquals('Value must have expected value', 0, context.value);
    +
    +  pubsub.publish('someTopic', 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('Subscriber must have been called', context.called);
    +  assertEquals('Value must have been updated', 17, context.value);
    +}
    +
    +function testSubscribeOnce_partialFn() {
    +  var called = false;
    +  var value = 0;
    +
    +  function subscriber(hasBeenCalled, newValue) {
    +    called = hasBeenCalled;
    +    value = newValue;
    +  }
    +
    +  pubsub.subscribeOnce('someTopic', goog.partial(subscriber, true));
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('Subscriber must not have been called yet', called);
    +  assertEquals('Value must have expected value', 0, value);
    +
    +  pubsub.publish('someTopic', 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('Subscriber must have been called', called);
    +  assertEquals('Value must have been updated', 17, value);
    +}
    +
    +function testSelfResubscribe() {
    +  var value = null;
    +
    +  function resubscribe(iteration, newValue) {
    +    pubsub.subscribeOnce('someTopic',
    +        goog.partial(resubscribe, iteration + 1));
    +    value = newValue + ':' + iteration;
    +  }
    +
    +  pubsub.subscribeOnce('someTopic', goog.partial(resubscribe, 0));
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertNull('Value must be null', value);
    +
    +  pubsub.publish('someTopic', 'foo');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertEquals('Pubsub must not have any pending unsubscribe keys', 0,
    +      pubsub.pendingKeys_.length);
    +  assertEquals('Value be as expected', 'foo:0', value);
    +
    +  pubsub.publish('someTopic', 'bar');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertEquals('Pubsub must not have any pending unsubscribe keys', 0,
    +      pubsub.pendingKeys_.length);
    +  assertEquals('Value be as expected', 'bar:1', value);
    +
    +  pubsub.publish('someTopic', 'baz');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertEquals('Pubsub must not have any pending unsubscribe keys', 0,
    +      pubsub.pendingKeys_.length);
    +  assertEquals('Value be as expected', 'baz:2', value);
    +}
    +
    +function testUnsubscribeByKey() {
    +  var key1, key2, key3;
    +
    +  key1 = pubsub.subscribe('X', function() {});
    +  key2 = pubsub.subscribe('Y', function() {});
    +
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +  assertNotEquals('Subscription keys must be distinct', key1, key2);
    +
    +  pubsub.unsubscribeByKey(key1);
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +
    +  key3 = pubsub.subscribe('X', function() {});
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +  assertNotEquals('Subscription keys must be distinct', key1, key3);
    +  assertNotEquals('Subscription keys must be distinct', key2, key3);
    +
    +  pubsub.unsubscribeByKey(key1); // Obsolete key; should be no-op.
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +
    +  pubsub.unsubscribeByKey(key2);
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have no subscribers', 0,
    +      pubsub.getCount('Y'));
    +
    +  pubsub.unsubscribeByKey(key3);
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have no subscribers', 0,
    +      pubsub.getCount('Y'));
    +}
    +
    +function testSubscribeUnsubscribeMultiple() {
    +  function foo() {
    +  }
    +  function bar() {
    +  }
    +
    +  var context = {};
    +
    +  assertEquals('Pubsub channel must not have any subscribers', 0,
    +      pubsub.getCount());
    +
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must not have any subscribers', 0,
    +      pubsub.getCount('Y'));
    +  assertEquals('Topic "Z" must not have any subscribers', 0,
    +      pubsub.getCount('Z'));
    +
    +  goog.array.forEach(['X', 'Y', 'Z'], function(topic) {
    +    pubsub.subscribe(topic, foo);
    +  });
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +  assertEquals('Topic "Z" must have 1 subscriber', 1,
    +      pubsub.getCount('Z'));
    +
    +  goog.array.forEach(['X', 'Y', 'Z'], function(topic) {
    +    pubsub.subscribe(topic, bar, context);
    +  });
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 2 subscribers', 2,
    +      pubsub.getCount('Y'));
    +  assertEquals('Topic "Z" must have 2 subscribers', 2,
    +      pubsub.getCount('Z'));
    +
    +  assertEquals('Pubsub channel must have a total of 6 subscribers', 6,
    +      pubsub.getCount());
    +
    +  goog.array.forEach(['X', 'Y', 'Z'], function(topic) {
    +    pubsub.unsubscribe(topic, foo);
    +  });
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount('Y'));
    +  assertEquals('Topic "Z" must have 1 subscriber', 1,
    +      pubsub.getCount('Z'));
    +
    +  goog.array.forEach(['X', 'Y', 'Z'], function(topic) {
    +    pubsub.unsubscribe(topic, bar, context);
    +  });
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount('X'));
    +  assertEquals('Topic "Y" must not have any subscribers', 0,
    +      pubsub.getCount('Y'));
    +  assertEquals('Topic "Z" must not have any subscribers', 0,
    +      pubsub.getCount('Z'));
    +
    +  assertEquals('Pubsub channel must not have any subscribers', 0,
    +      pubsub.getCount());
    +}
    +
    +function testPublish() {
    +  var context = {};
    +  var fooCalled = false;
    +  var barCalled = false;
    +
    +  function foo(x, y) {
    +    fooCalled = true;
    +    assertEquals('x must have expected value', 'x', x);
    +    assertEquals('y must have expected value', 'y', y);
    +  }
    +
    +  function bar(x, y) {
    +    barCalled = true;
    +    assertEquals('Context must have expected value', context, this);
    +    assertEquals('x must have expected value', 'x', x);
    +    assertEquals('y must have expected value', 'y', y);
    +  }
    +
    +  pubsub.subscribe('someTopic', foo);
    +  pubsub.subscribe('someTopic', bar, context);
    +
    +  assertTrue(pubsub.publish('someTopic', 'x', 'y'));
    +  assertTrue('foo() must have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +
    +  fooCalled = false;
    +  barCalled = false;
    +  assertTrue(pubsub.unsubscribe('someTopic', foo));
    +
    +  assertTrue(pubsub.publish('someTopic', 'x', 'y'));
    +  assertFalse('foo() must not have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +
    +  fooCalled = false;
    +  barCalled = false;
    +  pubsub.subscribe('differentTopic', foo);
    +
    +  assertTrue(pubsub.publish('someTopic', 'x', 'y'));
    +  assertFalse('foo() must not have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +}
    +
    +function testPublishEmptyTopic() {
    +  var fooCalled = false;
    +  function foo() {
    +    fooCalled = true;
    +  }
    +
    +  assertFalse('Publishing to nonexistent topic must return false',
    +      pubsub.publish('someTopic'));
    +
    +  pubsub.subscribe('someTopic', foo);
    +  assertTrue('Publishing to topic with subscriber must return true',
    +      pubsub.publish('someTopic'));
    +  assertTrue('Foo must have been called', fooCalled);
    +
    +  pubsub.unsubscribe('someTopic', foo);
    +  fooCalled = false;
    +  assertFalse('Publishing to topic without subscribers must return false',
    +      pubsub.publish('someTopic'));
    +  assertFalse('Foo must nothave been called', fooCalled);
    +}
    +
    +function testSubscribeWhilePublishing() {
    +  // It's OK for a subscriber to add a new subscriber to its own topic,
    +  // but the newly added subscriber shouldn't be called until the next
    +  // publish cycle.
    +
    +  var firstCalled = false;
    +  var secondCalled = false;
    +
    +  pubsub.subscribe('someTopic', function() {
    +    pubsub.subscribe('someTopic', function() {
    +      secondCalled = true;
    +    });
    +    firstCalled = true;
    +  });
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('No subscriber must have been called yet',
    +      firstCalled || secondCalled);
    +
    +  pubsub.publish('someTopic');
    +  assertEquals('Topic must have two subscribers', 2,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('The first subscriber must have been called',
    +      firstCalled);
    +  assertFalse('The second subscriber must not have been called yet',
    +      secondCalled);
    +
    +  pubsub.publish('someTopic');
    +  assertEquals('Topic must have three subscribers', 3,
    +      pubsub.getCount('someTopic'));
    +  assertTrue('The first subscriber must have been called',
    +      firstCalled);
    +  assertTrue('The second subscriber must also have been called',
    +      secondCalled);
    +}
    +
    +function testUnsubscribeWhilePublishing() {
    +  // It's OK for a subscriber to unsubscribe another subscriber from its
    +  // own topic, but the subscriber in question won't actually be removed
    +  // until after publishing is complete.
    +
    +  var firstCalled = false;
    +  var secondCalled = false;
    +  var thirdCalled = false;
    +
    +  function first() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe('X', second));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount('X'));
    +    firstCalled = true;
    +  }
    +  pubsub.subscribe('X', first);
    +
    +  function second() {
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount('X'));
    +    secondCalled = true;
    +  }
    +  pubsub.subscribe('X', second);
    +
    +  function third() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe('X', first));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount('X'));
    +    thirdCalled = true;
    +  }
    +  pubsub.subscribe('X', third);
    +
    +  assertEquals('Topic "X" must have 3 subscribers', 3,
    +      pubsub.getCount('X'));
    +  assertFalse('No subscribers must have been called yet',
    +      firstCalled || secondCalled || thirdCalled);
    +
    +  assertTrue(pubsub.publish('X'));
    +  assertTrue('First function must have been called', firstCalled);
    +  assertTrue('Second function must have been called', secondCalled);
    +  assertTrue('Third function must have been called', thirdCalled);
    +  assertEquals('Topic "X" must have 1 subscriber after publishing', 1,
    +      pubsub.getCount('X'));
    +  assertEquals('PubSub must not have any subscriptions pending removal', 0,
    +      pubsub.pendingKeys_.length);
    +}
    +
    +function testUnsubscribeSelfWhilePublishing() {
    +  // It's OK for a subscriber to unsubscribe itself, but it won't actually
    +  // be removed until after publishing is complete.
    +
    +  var selfDestructCalled = false;
    +
    +  function selfDestruct() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe('someTopic', arguments.callee));
    +    assertEquals('Topic must still have 1 subscriber', 1,
    +        pubsub.getCount('someTopic'));
    +    selfDestructCalled = true;
    +  }
    +
    +  pubsub.subscribe('someTopic', selfDestruct);
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount('someTopic'));
    +  assertFalse('selfDestruct() must not have been called yet',
    +      selfDestructCalled);
    +
    +  pubsub.publish('someTopic');
    +  assertTrue('selfDestruct() must have been called', selfDestructCalled);
    +  assertEquals('Topic must have no subscribers after publishing', 0,
    +      pubsub.getCount('someTopic'));
    +  assertEquals('PubSub must not have any subscriptions pending removal', 0,
    +      pubsub.pendingKeys_.length);
    +}
    +
    +function testPublishReturnValue() {
    +  pubsub.subscribe('X', function() {
    +    pubsub.unsubscribe('X', arguments.callee);
    +  });
    +  assertTrue('publish() must return true even if the only subscriber ' +
    +      'removes itself during publishing', pubsub.publish('X'));
    +}
    +
    +function testNestedPublish() {
    +  var x1 = false;
    +  var x2 = false;
    +  var y1 = false;
    +  var y2 = false;
    +
    +  pubsub.subscribe('X', function() {
    +    pubsub.publish('Y');
    +    pubsub.unsubscribe('X', arguments.callee);
    +    x1 = true;
    +  });
    +
    +  pubsub.subscribe('X', function() {
    +    x2 = true;
    +  });
    +
    +  pubsub.subscribe('Y', function() {
    +    pubsub.unsubscribe('Y', arguments.callee);
    +    y1 = true;
    +  });
    +
    +  pubsub.subscribe('Y', function() {
    +    y2 = true;
    +  });
    +
    +  pubsub.publish('X');
    +
    +  assertTrue('x1 must be true', x1);
    +  assertTrue('x2 must be true', x2);
    +  assertTrue('y1 must be true', y1);
    +  assertTrue('y2 must be true', y2);
    +}
    +
    +function testClear() {
    +  function fn() {
    +  }
    +
    +  goog.array.forEach(['W', 'X', 'Y', 'Z'], function(topic) {
    +    pubsub.subscribe(topic, fn);
    +  });
    +  assertEquals('Pubsub channel must have 4 subscribers', 4,
    +      pubsub.getCount());
    +
    +  pubsub.clear('W');
    +  assertEquals('Pubsub channel must have 3 subscribers', 3,
    +      pubsub.getCount());
    +
    +  goog.array.forEach(['X', 'Y'], function(topic) {
    +    pubsub.clear(topic);
    +  });
    +  assertEquals('Pubsub channel must have 1 subscriber', 1,
    +      pubsub.getCount());
    +
    +  pubsub.clear();
    +  assertEquals('Pubsub channel must have no subscribers', 0,
    +      pubsub.getCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/topicid.js b/mapbox-gl/closure-library/closure/goog/pubsub/topicid.js
    new file mode 100644
    index 0000000000..36dcbf8e8c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/topicid.js
    @@ -0,0 +1,61 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.pubsub.TopicId');
    +
    +
    +
    +/**
    + * A templated class that is used to register {@code goog.pubsub.PubSub}
    + * subscribers.
    + *
    + * Typical usage for a publisher:
    + * <code>
    + *   /** @type {!goog.pubsub.TopicId<!zorg.State>}
    + *   zorg.TopicId.STATE_CHANGE = new goog.pubsub.TopicId(
    + *       goog.events.getUniqueId('state-change'));
    + *
    + *   // Compiler enforces that these types are correct.
    + *   pubSub.publish(zorg.TopicId.STATE_CHANGE, zorg.State.STARTED);
    + * </code>
    + *
    + * Typical usage for a subscriber:
    + * <code>
    + *   // Compiler enforces the callback parameter type.
    + *   pubSub.subscribe(zorg.TopicId.STATE_CHANGE, function(state) {
    + *     if (state == zorg.State.STARTED) {
    + *       // Handle STARTED state.
    + *     }
    + *   });
    + * </code>
    + *
    + * @param {string} topicId
    + * @template PAYLOAD
    + * @constructor
    + * @final
    + * @struct
    + */
    +goog.pubsub.TopicId = function(topicId) {
    +  /**
    +   * @const
    +   * @private
    +   */
    +  this.topicId_ = topicId;
    +};
    +
    +
    +/** @override */
    +goog.pubsub.TopicId.prototype.toString = function() {
    +  return this.topicId_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub.js b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub.js
    new file mode 100644
    index 0000000000..957db59dea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub.js
    @@ -0,0 +1,126 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.pubsub.TypedPubSub');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.pubsub.PubSub');
    +
    +
    +
    +/**
    + * This object is a temporary shim that provides goog.pubsub.TopicId support
    + * for goog.pubsub.PubSub.  See b/12477087 for more info.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.pubsub.TypedPubSub = function() {
    +  goog.pubsub.TypedPubSub.base(this, 'constructor');
    +
    +  this.pubSub_ = new goog.pubsub.PubSub();
    +  this.registerDisposable(this.pubSub_);
    +};
    +goog.inherits(goog.pubsub.TypedPubSub, goog.Disposable);
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.subscribe}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>} topic Topic to subscribe to.
    + * @param {function(this:CONTEXT, PAYLOAD)} fn Function to be invoked when a
    + *     message is published to the given topic.
    + * @param {CONTEXT=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + * @template PAYLOAD, CONTEXT
    + */
    +goog.pubsub.TypedPubSub.prototype.subscribe = function(topic, fn, opt_context) {
    +  return this.pubSub_.subscribe(topic.toString(), fn, opt_context);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.subscribeOnce}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>} topic Topic to subscribe to.
    + * @param {function(this:CONTEXT, PAYLOAD)} fn Function to be invoked once and
    + *     then unsubscribed when a message is published to the given topic.
    + * @param {CONTEXT=} opt_context Object in whose context the function is to be
    + *     called (the global scope if none).
    + * @return {number} Subscription key.
    + * @template PAYLOAD, CONTEXT
    + */
    +goog.pubsub.TypedPubSub.prototype.subscribeOnce = function(
    +    topic, fn, opt_context) {
    +  return this.pubSub_.subscribeOnce(topic.toString(), fn, opt_context);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.unsubscribe}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>} topic Topic to unsubscribe from.
    + * @param {function(this:CONTEXT, PAYLOAD)} fn Function to unsubscribe.
    + * @param {CONTEXT=} opt_context Object in whose context the function was to be
    + *     called (the global scope if none).
    + * @return {boolean} Whether a matching subscription was removed.
    + * @template PAYLOAD, CONTEXT
    + */
    +goog.pubsub.TypedPubSub.prototype.unsubscribe = function(
    +    topic, fn, opt_context) {
    +  return this.pubSub_.unsubscribe(topic.toString(), fn, opt_context);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.unsubscribeByKey}.
    + * @param {number} key Subscription key.
    + * @return {boolean} Whether a matching subscription was removed.
    + */
    +goog.pubsub.TypedPubSub.prototype.unsubscribeByKey = function(key) {
    +  return this.pubSub_.unsubscribeByKey(key);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.publish}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>} topic Topic to publish to.
    + * @param {PAYLOAD} payload Payload passed to each subscription function.
    + * @return {boolean} Whether any subscriptions were called.
    + * @template PAYLOAD
    + */
    +goog.pubsub.TypedPubSub.prototype.publish = function(topic, payload) {
    +  return this.pubSub_.publish(topic.toString(), payload);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.clear}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>=} opt_topic Topic to clear (all topics
    + *     if unspecified).
    + * @template PAYLOAD
    + */
    +goog.pubsub.TypedPubSub.prototype.clear = function(opt_topic) {
    +  this.pubSub_.clear(goog.isDef(opt_topic) ? opt_topic.toString() : undefined);
    +};
    +
    +
    +/**
    + * See {@code goog.pubsub.PubSub.getCount}.
    + * @param {!goog.pubsub.TopicId<PAYLOAD>=} opt_topic The topic (all topics if
    + *     unspecified).
    + * @return {number} Number of subscriptions to the topic.
    + * @template PAYLOAD
    + */
    +goog.pubsub.TypedPubSub.prototype.getCount = function(opt_topic) {
    +  return this.pubSub_.getCount(
    +      goog.isDef(opt_topic) ? opt_topic.toString() : undefined);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.html b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.html
    new file mode 100644
    index 0000000000..0f517ffd0a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.pubsub.TypedPubSub
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.pubsub.TypedPubSubTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.js b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.js
    new file mode 100644
    index 0000000000..ca69325762
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/pubsub/typedpubsub_test.js
    @@ -0,0 +1,663 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.pubsub.TypedPubSubTest');
    +goog.setTestOnly('goog.pubsub.TypedPubSubTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.pubsub.TopicId');
    +goog.require('goog.pubsub.TypedPubSub');
    +goog.require('goog.testing.jsunit');
    +
    +var pubsub;
    +
    +function setUp() {
    +  pubsub = new goog.pubsub.TypedPubSub();
    +}
    +
    +function tearDown() {
    +  pubsub.dispose();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('PubSub instance must not be null', pubsub);
    +  assertTrue('PubSub instance must have the expected type',
    +      pubsub instanceof goog.pubsub.TypedPubSub);
    +}
    +
    +function testDispose() {
    +  assertFalse('PubSub instance must not have been disposed of',
    +      pubsub.isDisposed());
    +  pubsub.dispose();
    +  assertTrue('PubSub instance must have been disposed of',
    +      pubsub.isDisposed());
    +}
    +
    +function testSubscribeUnsubscribe() {
    +  function foo1() {
    +  }
    +  function bar1() {
    +  }
    +  function foo2() {
    +  }
    +  function bar2() {
    +  }
    +
    +  /** const */ var FOO = new goog.pubsub.TopicId('foo');
    +  /** const */ var BAR = new goog.pubsub.TopicId('bar');
    +  /** const */ var BAZ = new goog.pubsub.TopicId('baz');
    +
    +  assertEquals('Topic "foo" must not have any subscribers', 0,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must not have any subscribers', 0,
    +      pubsub.getCount(BAR));
    +
    +  pubsub.subscribe(FOO, foo1);
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must not have any subscribers', 0,
    +      pubsub.getCount(BAR));
    +
    +  pubsub.subscribe(BAR, bar1);
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount(BAR));
    +
    +  pubsub.subscribe(FOO, foo2);
    +  assertEquals('Topic "foo" must have 2 subscribers', 2,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount(BAR));
    +
    +  pubsub.subscribe(BAR, bar2);
    +  assertEquals('Topic "foo" must have 2 subscribers', 2,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount(BAR));
    +
    +  assertTrue(pubsub.unsubscribe(FOO, foo1));
    +  assertEquals('Topic "foo" must have 1 subscriber', 1,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount(BAR));
    +
    +  assertTrue(pubsub.unsubscribe(FOO, foo2));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 2 subscribers', 2,
    +      pubsub.getCount(BAR));
    +
    +  assertTrue(pubsub.unsubscribe(BAR, bar1));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have 1 subscriber', 1,
    +      pubsub.getCount(BAR));
    +
    +  assertTrue(pubsub.unsubscribe(BAR, bar2));
    +  assertEquals('Topic "foo" must have no subscribers', 0,
    +      pubsub.getCount(FOO));
    +  assertEquals('Topic "bar" must have no subscribers', 0,
    +      pubsub.getCount(BAR));
    +
    +  assertFalse('Unsubscribing a nonexistent topic must return false',
    +      pubsub.unsubscribe(BAZ, foo1));
    +
    +  assertFalse('Unsubscribing a nonexistent function must return false',
    +      pubsub.unsubscribe(FOO, function() {}));
    +}
    +
    +function testSubscribeUnsubscribeWithContext() {
    +  function foo() {
    +  }
    +  function bar() {
    +  }
    +
    +  var contextA = {};
    +  var contextB = {};
    +
    +  /** const */ var TOPIC_X = new goog.pubsub.TopicId('X');
    +
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +
    +  pubsub.subscribe(TOPIC_X, foo, contextA);
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +
    +  pubsub.subscribe(TOPIC_X, bar);
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount(TOPIC_X));
    +
    +  pubsub.subscribe(TOPIC_X, bar, contextB);
    +  assertEquals('Topic "X" must have 3 subscribers', 3,
    +      pubsub.getCount(TOPIC_X));
    +
    +  assertFalse('Unknown function/context combination return false',
    +      pubsub.unsubscribe(TOPIC_X, foo, contextB));
    +
    +  assertTrue(pubsub.unsubscribe(TOPIC_X, foo, contextA));
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount(TOPIC_X));
    +
    +  assertTrue(pubsub.unsubscribe(TOPIC_X, bar));
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +
    +  assertTrue(pubsub.unsubscribe(TOPIC_X, bar, contextB));
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +}
    +
    +function testSubscribeOnce() {
    +  var called, context;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  called = false;
    +  pubsub.subscribeOnce(SOME_TOPIC, function() {
    +    called = true;
    +  });
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('Subscriber must not have been called yet', called);
    +
    +  pubsub.publish(SOME_TOPIC);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('Subscriber must have been called', called);
    +
    +  context = {called: false};
    +  pubsub.subscribeOnce(SOME_TOPIC, function() {
    +    this.called = true;
    +  }, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +
    +  pubsub.publish(SOME_TOPIC);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('Subscriber must have been called', context.called);
    +
    +  context = {called: false, value: 0};
    +  pubsub.subscribeOnce(SOME_TOPIC, function(value) {
    +    this.called = true;
    +    this.value = value;
    +  }, context);
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +  assertEquals('Value must have expected value', 0, context.value);
    +
    +  pubsub.publish(SOME_TOPIC, 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('Subscriber must have been called', context.called);
    +  assertEquals('Value must have been updated', 17, context.value);
    +}
    +
    +function testSubscribeOnce_boundFn() {
    +  var context = {called: false, value: 0};
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  function subscriber(value) {
    +    this.called = true;
    +    this.value = value;
    +  }
    +
    +  pubsub.subscribeOnce(SOME_TOPIC, goog.bind(subscriber, context));
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('Subscriber must not have been called yet', context.called);
    +  assertEquals('Value must have expected value', 0, context.value);
    +
    +  pubsub.publish(SOME_TOPIC, 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('Subscriber must have been called', context.called);
    +  assertEquals('Value must have been updated', 17, context.value);
    +}
    +
    +function testSubscribeOnce_partialFn() {
    +  var called = false;
    +  var value = 0;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  function subscriber(hasBeenCalled, newValue) {
    +    called = hasBeenCalled;
    +    value = newValue;
    +  }
    +
    +  pubsub.subscribeOnce(SOME_TOPIC, goog.partial(subscriber, true));
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('Subscriber must not have been called yet', called);
    +  assertEquals('Value must have expected value', 0, value);
    +
    +  pubsub.publish(SOME_TOPIC, 17);
    +  assertEquals('Topic must have no subscribers', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('Subscriber must have been called', called);
    +  assertEquals('Value must have been updated', 17, value);
    +}
    +
    +function testSelfResubscribe() {
    +  var value = null;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  function resubscribe(iteration, newValue) {
    +    pubsub.subscribeOnce(SOME_TOPIC,
    +        goog.partial(resubscribe, iteration + 1));
    +    value = newValue + ':' + iteration;
    +  }
    +
    +  pubsub.subscribeOnce(SOME_TOPIC, goog.partial(resubscribe, 0));
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertNull('Value must be null', value);
    +
    +  pubsub.publish(SOME_TOPIC, 'foo');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertEquals('Value be as expected', 'foo:0', value);
    +
    +  pubsub.publish(SOME_TOPIC, 'bar');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertEquals('Value be as expected', 'bar:1', value);
    +
    +  pubsub.publish(SOME_TOPIC, 'baz');
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertEquals('Value be as expected', 'baz:2', value);
    +}
    +
    +function testUnsubscribeByKey() {
    +  var key1, key2, key3;
    +
    +  /** const */ var TOPIC_X = new goog.pubsub.TopicId('X');
    +  /** const */ var TOPIC_Y = new goog.pubsub.TopicId('Y');
    +
    +  key1 = pubsub.subscribe(TOPIC_X, function() {});
    +  key2 = pubsub.subscribe(TOPIC_Y, function() {});
    +
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +  assertNotEquals('Subscription keys must be distinct', key1, key2);
    +
    +  pubsub.unsubscribeByKey(key1);
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +
    +  key3 = pubsub.subscribe(TOPIC_X, function() {});
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +  assertNotEquals('Subscription keys must be distinct', key1, key3);
    +  assertNotEquals('Subscription keys must be distinct', key2, key3);
    +
    +  pubsub.unsubscribeByKey(key1); // Obsolete key; should be no-op.
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +
    +  pubsub.unsubscribeByKey(key2);
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have no subscribers', 0,
    +      pubsub.getCount(TOPIC_Y));
    +
    +  pubsub.unsubscribeByKey(key3);
    +  assertEquals('Topic "X" must have no subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have no subscribers', 0,
    +      pubsub.getCount(TOPIC_Y));
    +}
    +
    +function testSubscribeUnsubscribeMultiple() {
    +  function foo() {
    +  }
    +  function bar() {
    +  }
    +
    +  var context = {};
    +
    +  /** const */ var TOPIC_X = new goog.pubsub.TopicId('X');
    +  /** const */ var TOPIC_Y = new goog.pubsub.TopicId('Y');
    +  /** const */ var TOPIC_Z = new goog.pubsub.TopicId('Z');
    +
    +  assertEquals('Pubsub channel must not have any subscribers', 0,
    +      pubsub.getCount());
    +
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_Y));
    +  assertEquals('Topic "Z" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_Z));
    +
    +  goog.array.forEach([TOPIC_X, TOPIC_Y, TOPIC_Z], function(topic) {
    +    pubsub.subscribe(topic, foo);
    +  });
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +  assertEquals('Topic "Z" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Z));
    +
    +  goog.array.forEach([TOPIC_X, TOPIC_Y, TOPIC_Z], function(topic) {
    +    pubsub.subscribe(topic, bar, context);
    +  });
    +  assertEquals('Topic "X" must have 2 subscribers', 2,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 2 subscribers', 2,
    +      pubsub.getCount(TOPIC_Y));
    +  assertEquals('Topic "Z" must have 2 subscribers', 2,
    +      pubsub.getCount(TOPIC_Z));
    +
    +  assertEquals('Pubsub channel must have a total of 6 subscribers', 6,
    +      pubsub.getCount());
    +
    +  goog.array.forEach([TOPIC_X, TOPIC_Y, TOPIC_Z], function(topic) {
    +    pubsub.unsubscribe(topic, foo);
    +  });
    +  assertEquals('Topic "X" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Y));
    +  assertEquals('Topic "Z" must have 1 subscriber', 1,
    +      pubsub.getCount(TOPIC_Z));
    +
    +  goog.array.forEach([TOPIC_X, TOPIC_Y, TOPIC_Z], function(topic) {
    +    pubsub.unsubscribe(topic, bar, context);
    +  });
    +  assertEquals('Topic "X" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_X));
    +  assertEquals('Topic "Y" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_Y));
    +  assertEquals('Topic "Z" must not have any subscribers', 0,
    +      pubsub.getCount(TOPIC_Z));
    +
    +  assertEquals('Pubsub channel must not have any subscribers', 0,
    +      pubsub.getCount());
    +}
    +
    +function testPublish() {
    +  var context = {};
    +  var fooCalled = false;
    +  var barCalled = false;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  function foo(record) {
    +    fooCalled = true;
    +    assertEquals('x must have expected value', 'x', record.x);
    +    assertEquals('y must have expected value', 'y', record.y);
    +  }
    +
    +  function bar(record) {
    +    barCalled = true;
    +    assertEquals('Context must have expected value', context, this);
    +    assertEquals('x must have expected value', 'x', record.x);
    +    assertEquals('y must have expected value', 'y', record.y);
    +  }
    +
    +  pubsub.subscribe(SOME_TOPIC, foo);
    +  pubsub.subscribe(SOME_TOPIC, bar, context);
    +
    +  assertTrue(pubsub.publish(SOME_TOPIC, {x: 'x', y: 'y'}));
    +  assertTrue('foo() must have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +
    +  fooCalled = false;
    +  barCalled = false;
    +  assertTrue(pubsub.unsubscribe(SOME_TOPIC, foo));
    +
    +  assertTrue(pubsub.publish(SOME_TOPIC, {x: 'x', y: 'y'}));
    +  assertFalse('foo() must not have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +
    +  fooCalled = false;
    +  barCalled = false;
    +  pubsub.subscribe('differentTopic', foo);
    +
    +  assertTrue(pubsub.publish(SOME_TOPIC, {x: 'x', y: 'y'}));
    +  assertFalse('foo() must not have been called', fooCalled);
    +  assertTrue('bar() must have been called', barCalled);
    +}
    +
    +function testPublishEmptyTopic() {
    +  var fooCalled = false;
    +  function foo() {
    +    fooCalled = true;
    +  }
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  assertFalse('Publishing to nonexistent topic must return false',
    +      pubsub.publish(SOME_TOPIC));
    +
    +  pubsub.subscribe(SOME_TOPIC, foo);
    +  assertTrue('Publishing to topic with subscriber must return true',
    +      pubsub.publish(SOME_TOPIC));
    +  assertTrue('Foo must have been called', fooCalled);
    +
    +  pubsub.unsubscribe(SOME_TOPIC, foo);
    +  fooCalled = false;
    +  assertFalse('Publishing to topic without subscribers must return false',
    +      pubsub.publish(SOME_TOPIC));
    +  assertFalse('Foo must nothave been called', fooCalled);
    +}
    +
    +function testSubscribeWhilePublishing() {
    +  // It's OK for a subscriber to add a new subscriber to its own topic,
    +  // but the newly added subscriber shouldn't be called until the next
    +  // publish cycle.
    +
    +  var firstCalled = false;
    +  var secondCalled = false;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  pubsub.subscribe(SOME_TOPIC, function() {
    +    pubsub.subscribe(SOME_TOPIC, function() {
    +      secondCalled = true;
    +    });
    +    firstCalled = true;
    +  });
    +  assertEquals('Topic must have one subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('No subscriber must have been called yet',
    +      firstCalled || secondCalled);
    +
    +  pubsub.publish(SOME_TOPIC);
    +  assertEquals('Topic must have two subscribers', 2,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('The first subscriber must have been called',
    +      firstCalled);
    +  assertFalse('The second subscriber must not have been called yet',
    +      secondCalled);
    +
    +  pubsub.publish(SOME_TOPIC);
    +  assertEquals('Topic must have three subscribers', 3,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertTrue('The first subscriber must have been called',
    +      firstCalled);
    +  assertTrue('The second subscriber must also have been called',
    +      secondCalled);
    +}
    +
    +function testUnsubscribeWhilePublishing() {
    +  // It's OK for a subscriber to unsubscribe another subscriber from its
    +  // own topic, but the subscriber in question won't actually be removed
    +  // until after publishing is complete.
    +
    +  var firstCalled = false;
    +  var secondCalled = false;
    +  var thirdCalled = false;
    +
    +  /** const */ var TOPIC_X = new goog.pubsub.TopicId('X');
    +
    +  function first() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe(TOPIC_X, second));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount(TOPIC_X));
    +    firstCalled = true;
    +  }
    +  pubsub.subscribe(TOPIC_X, first);
    +
    +  function second() {
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount(TOPIC_X));
    +    secondCalled = true;
    +  }
    +  pubsub.subscribe(TOPIC_X, second);
    +
    +  function third() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe(TOPIC_X, first));
    +    assertEquals('Topic "X" must still have 3 subscribers', 3,
    +        pubsub.getCount(TOPIC_X));
    +    thirdCalled = true;
    +  }
    +  pubsub.subscribe(TOPIC_X, third);
    +
    +  assertEquals('Topic "X" must have 3 subscribers', 3,
    +      pubsub.getCount(TOPIC_X));
    +  assertFalse('No subscribers must have been called yet',
    +      firstCalled || secondCalled || thirdCalled);
    +
    +  assertTrue(pubsub.publish(TOPIC_X));
    +  assertTrue('First function must have been called', firstCalled);
    +  assertTrue('Second function must have been called', secondCalled);
    +  assertTrue('Third function must have been called', thirdCalled);
    +  assertEquals('Topic "X" must have 1 subscriber after publishing', 1,
    +      pubsub.getCount(TOPIC_X));
    +}
    +
    +function testUnsubscribeSelfWhilePublishing() {
    +  // It's OK for a subscriber to unsubscribe itself, but it won't actually
    +  // be removed until after publishing is complete.
    +
    +  var selfDestructCalled = false;
    +
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +
    +  function selfDestruct() {
    +    assertFalse('unsubscribe() must return false during publishing',
    +        pubsub.unsubscribe(SOME_TOPIC, arguments.callee));
    +    assertEquals('Topic must still have 1 subscriber', 1,
    +        pubsub.getCount(SOME_TOPIC));
    +    selfDestructCalled = true;
    +  }
    +
    +  pubsub.subscribe(SOME_TOPIC, selfDestruct);
    +  assertEquals('Topic must have 1 subscriber', 1,
    +      pubsub.getCount(SOME_TOPIC));
    +  assertFalse('selfDestruct() must not have been called yet',
    +      selfDestructCalled);
    +
    +  pubsub.publish(SOME_TOPIC);
    +  assertTrue('selfDestruct() must have been called', selfDestructCalled);
    +  assertEquals('Topic must have no subscribers after publishing', 0,
    +      pubsub.getCount(SOME_TOPIC));
    +}
    +
    +function testPublishReturnValue() {
    +  /** @const */ SOME_TOPIC = new goog.pubsub.TopicId('someTopic');
    +  pubsub.subscribe(SOME_TOPIC, function() {
    +    pubsub.unsubscribe(SOME_TOPIC, arguments.callee);
    +  });
    +  assertTrue('publish() must return true even if the only subscriber ' +
    +      'removes itself during publishing', pubsub.publish(SOME_TOPIC));
    +}
    +
    +function testNestedPublish() {
    +  var x1 = false;
    +  var x2 = false;
    +  var y1 = false;
    +  var y2 = false;
    +
    +  /** @const */ TOPIC_X = new goog.pubsub.TopicId('X');
    +  /** @const */ TOPIC_Y = new goog.pubsub.TopicId('Y');
    +
    +  pubsub.subscribe(TOPIC_X, function() {
    +    pubsub.publish(TOPIC_Y);
    +    pubsub.unsubscribe(TOPIC_X, arguments.callee);
    +    x1 = true;
    +  });
    +
    +  pubsub.subscribe(TOPIC_X, function() {
    +    x2 = true;
    +  });
    +
    +  pubsub.subscribe(TOPIC_Y, function() {
    +    pubsub.unsubscribe(TOPIC_Y, arguments.callee);
    +    y1 = true;
    +  });
    +
    +  pubsub.subscribe(TOPIC_Y, function() {
    +    y2 = true;
    +  });
    +
    +  pubsub.publish(TOPIC_X);
    +
    +  assertTrue('x1 must be true', x1);
    +  assertTrue('x2 must be true', x2);
    +  assertTrue('y1 must be true', y1);
    +  assertTrue('y2 must be true', y2);
    +}
    +
    +function testClear() {
    +  function fn() {
    +  }
    +
    +  var topics = [
    +    new goog.pubsub.TopicId('W'),
    +    new goog.pubsub.TopicId('X'),
    +    new goog.pubsub.TopicId('Y'),
    +    new goog.pubsub.TopicId('Z')
    +  ];
    +
    +  goog.array.forEach(topics, function(topic) {
    +    pubsub.subscribe(topic, fn);
    +  });
    +  assertEquals('Pubsub channel must have 4 subscribers', 4,
    +      pubsub.getCount());
    +
    +  pubsub.clear(topics[0]);
    +  assertEquals('Pubsub channel must have 3 subscribers', 3,
    +      pubsub.getCount());
    +
    +  pubsub.clear(topics[1]);
    +  pubsub.clear(topics[2]);
    +  assertEquals('Pubsub channel must have 1 subscriber', 1,
    +      pubsub.getCount());
    +
    +  pubsub.clear();
    +  assertEquals('Pubsub channel must have no subscribers', 0,
    +      pubsub.getCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/reflect/reflect.js b/mapbox-gl/closure-library/closure/goog/reflect/reflect.js
    new file mode 100644
    index 0000000000..c49050f58a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/reflect/reflect.js
    @@ -0,0 +1,78 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Useful compiler idioms.
    + *
    + * @author johnlenz@google.com (John Lenz)
    + */
    +
    +goog.provide('goog.reflect');
    +
    +
    +/**
    + * Syntax for object literal casts.
    + * @see http://go/jscompiler-renaming
    + * @see http://code.google.com/p/closure-compiler/wiki/
    + *      ExperimentalTypeBasedPropertyRenaming
    + *
    + * Use this if you have an object literal whose keys need to have the same names
    + * as the properties of some class even after they are renamed by the compiler.
    + *
    + * @param {!Function} type Type to cast to.
    + * @param {Object} object Object literal to cast.
    + * @return {Object} The object literal.
    + */
    +goog.reflect.object = function(type, object) {
    +  return object;
    +};
    +
    +
    +/**
    + * To assert to the compiler that an operation is needed when it would
    + * otherwise be stripped. For example:
    + * <code>
    + *     // Force a layout
    + *     goog.reflect.sinkValue(dialog.offsetHeight);
    + * </code>
    + * @type {!Function}
    + */
    +goog.reflect.sinkValue = function(x) {
    +  goog.reflect.sinkValue[' '](x);
    +  return x;
    +};
    +
    +
    +/**
    + * The compiler should optimize this function away iff no one ever uses
    + * goog.reflect.sinkValue.
    + */
    +goog.reflect.sinkValue[' '] = goog.nullFunction;
    +
    +
    +/**
    + * Check if a property can be accessed without throwing an exception.
    + * @param {Object} obj The owner of the property.
    + * @param {string} prop The property name.
    + * @return {boolean} Whether the property is accessible. Will also return true
    + *     if obj is null.
    + */
    +goog.reflect.canAccessProperty = function(obj, prop) {
    +  /** @preserveTry */
    +  try {
    +    goog.reflect.sinkValue(obj[prop]);
    +    return true;
    +  } catch (e) {}
    +  return false;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/result/chain_test.html b/mapbox-gl/closure-library/closure/goog/result/chain_test.html
    new file mode 100644
    index 0000000000..c56c93d24a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/chain_test.html
    @@ -0,0 +1,236 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.chain</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.Timer');
    +goog.require('goog.result');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var givenResult, dependentResult, counter, actionCallback;
    +var mockClock;
    +
    +function setUpPage() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +}
    +
    +function setUp() {
    +  mockClock.reset();
    +  givenResult = new goog.result.SimpleResult();
    +  dependentResult = new goog.result.SimpleResult();
    +  counter = new goog.testing.recordFunction();
    +  actionCallback = goog.testing.recordFunction(function(result) {
    +    return dependentResult;
    +  });
    +}
    +
    +function tearDown() {
    +  givenResult = dependentResult = counter = null;
    +}
    +
    +function tearDownPage() {
    +  mockClock.uninstall();
    +}
    +
    +// SYNCHRONOUS TESTS:
    +
    +function testChainWhenBothResultsSuccess() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  givenResult.setValue(1);
    +  dependentResult.setValue(2);
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +  assertSuccess(counter, finalResult, 2);
    +}
    +
    +function testChainWhenFirstResultError() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  givenResult.setError(4);
    +
    +  assertNoCall(actionCallback);
    +  assertError(counter, finalResult, 4);
    +}
    +
    +function testChainWhenSecondResultError() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  givenResult.setValue(1);
    +  dependentResult.setError(5);
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +  assertError(counter, finalResult, 5);
    +}
    +
    +function testChainCancelFirstResult() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.result.cancelParentResults(finalResult);
    +
    +  assertNoCall(actionCallback);
    +  assertTrue(givenResult.isCanceled());
    +  assertTrue(finalResult.isCanceled());
    +}
    +
    +function testChainCancelSecondResult() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  givenResult.setValue(1);
    +  goog.result.cancelParentResults(finalResult);
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +  assertTrue(dependentResult.isCanceled());
    +  assertTrue(finalResult.isCanceled());
    +}
    +
    +function testDoubleChainCancel() {
    +  var intermediateResult = goog.result.chain(givenResult, actionCallback);
    +  var finalResult = goog.result.chain(intermediateResult, actionCallback);
    +
    +  assertTrue(goog.result.cancelParentResults(finalResult));
    +  assertTrue(finalResult.isCanceled());
    +  assertTrue(intermediateResult.isCanceled());
    +  assertFalse(givenResult.isCanceled());
    +  assertFalse(goog.result.cancelParentResults(finalResult));
    +}
    +
    +function testCustomScope() {
    +  var scope = {};
    +  var finalResult = goog.result.chain(givenResult, actionCallback, scope);
    +  goog.result.wait(finalResult, counter);
    +
    +  givenResult.setValue(1);
    +  dependentResult.setValue(2);
    +
    +  assertEquals(scope, actionCallback.popLastCall().getThis());
    +}
    +
    +
    +// ASYNCHRONOUS TESTS:
    +
    +function testChainAsyncWhenBothResultsSuccess() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.Timer.callOnce(function() { givenResult.setValue(1); });
    +  mockClock.tick();
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +
    +  goog.Timer.callOnce(function() { dependentResult.setValue(2); });
    +  mockClock.tick();
    +
    +  assertSuccess(counter, finalResult, 2);
    +}
    +
    +function testChainAsyncWhenFirstResultError() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.Timer.callOnce(function() { givenResult.setError(6); });
    +  mockClock.tick();
    +
    +  assertNoCall(actionCallback);
    +  assertError(counter, finalResult, 6);
    +}
    +
    +function testChainAsyncWhenSecondResultError() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.Timer.callOnce(function() { givenResult.setValue(1); });
    +  mockClock.tick();
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +
    +  goog.Timer.callOnce(function() { dependentResult.setError(7); });
    +  mockClock.tick();
    +
    +  assertError(counter, finalResult, 7);
    +}
    +
    +function testChainAsyncCancelFirstResult() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.Timer.callOnce(function() {
    +    goog.result.cancelParentResults(finalResult);
    +  });
    +  mockClock.tick();
    +
    +  assertNoCall(actionCallback);
    +  assertTrue(givenResult.isCanceled());
    +  assertTrue(finalResult.isCanceled());
    +}
    +
    +function testChainAsyncCancelSecondResult() {
    +  var finalResult = goog.result.chain(givenResult, actionCallback);
    +  goog.result.wait(finalResult, counter);
    +
    +  goog.Timer.callOnce(function() { givenResult.setValue(1); });
    +  mockClock.tick();
    +
    +  assertSuccess(actionCallback, givenResult, 1);
    +
    +  goog.Timer.callOnce(function() {
    +    goog.result.cancelParentResults(finalResult);
    +  });
    +  mockClock.tick();
    +
    +  assertTrue(dependentResult.isCanceled());
    +  assertTrue(finalResult.isCanceled());
    +}
    +
    +// HELPER FUNCTIONS:
    +
    +// Assert that the recordFunction was called once with an argument of
    +// 'result' (the second argument) which has a state of SUCCESS and
    +// a value of 'value' (the third argument).
    +function assertSuccess(recordFunction, result, value) {
    +  assertEquals(1, recordFunction.getCallCount());
    +  var res = recordFunction.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +  assertEquals(goog.result.Result.State.SUCCESS, res.getState());
    +  assertEquals(value, res.getValue());
    +}
    +
    +// Assert that the recordFunction was called once with an argument of
    +// 'result' (the second argument) which has a state of ERROR.
    +function assertError(recordFunction, result, value) {
    +  assertEquals(1, recordFunction.getCallCount());
    +  var res = recordFunction.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +  assertEquals(goog.result.Result.State.ERROR, res.getState());
    +  assertEquals(value, res.getError());
    +}
    +
    +// Assert that the recordFunction wasn't called
    +function assertNoCall(recordFunction) {
    +  assertEquals(0, recordFunction.getCallCount());
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/combine_test.html b/mapbox-gl/closure-library/closure/goog/result/combine_test.html
    new file mode 100644
    index 0000000000..c8a31bba96
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/combine_test.html
    @@ -0,0 +1,229 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.combine</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.result');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var result1, result2, result3, result4, resultCallback;
    +var combinedResult, successCombinedResult, mockClock;
    +
    +function setUpPage() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +}
    +
    +function tearDownPage() {
    +  goog.dispose(mockClock);
    +}
    +
    +function setUp() {
    +  mockClock.reset();
    +  result1 = new goog.result.SimpleResult();
    +  result2 = new goog.result.SimpleResult();
    +  result3 = new goog.result.SimpleResult();
    +  result4 = new goog.result.SimpleResult();
    +
    +  combinedResult = goog.result.combine(result1, result2, result3, result4);
    +
    +  successCombinedResult =
    +      goog.result.combineOnSuccess(result1, result2, result3, result4);
    +
    +  resultCallback = goog.testing.recordFunction();
    +}
    +
    +function tearDown() {
    +  result1 = result2 = result3 = result4 = resultCallback = null;
    +  combinedResult = successCombinedResult = null;
    +}
    +
    +function testSynchronousCombine() {
    +  resolveAllGivenResultsToSuccess();
    +
    +  newCombinedResult = goog.result.combine(result1, result2, result3, result4);
    +
    +  goog.result.wait(newCombinedResult, resultCallback);
    +
    +  assertSuccessCall(newCombinedResult, resultCallback);
    +}
    +
    +function testCombineWhenAllResultsSuccess() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  resolveAllGivenResultsToSuccess();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineWhenAllResultsSuccess() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
    +  mockClock.tick();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testCombineWhenAllResultsFail() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  resolveAllGivenResultsToError();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineWhenAllResultsFail() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
    +  mockClock.tick();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testCombineWhenSomeResultsSuccess() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  resolveSomeGivenResultsToSuccess();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineWhenSomeResultsSuccess() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
    +  mockClock.tick();
    +
    +  assertSuccessCall(combinedResult, resultCallback);
    +}
    +
    +function testCombineOnSuccessWhenAllResultsSuccess() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  resolveAllGivenResultsToSuccess();
    +
    +  assertSuccessCall(successCombinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineOnSuccessWhenAllResultsSuccess() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveAllGivenResultsToSuccess(); });
    +  mockClock.tick();
    +
    +  assertSuccessCall(successCombinedResult, resultCallback);
    +}
    +
    +function testCombineOnSuccessWhenAllResultsFail() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  resolveAllGivenResultsToError();
    +
    +  assertErrorCall(successCombinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineOnSuccessWhenAllResultsFail() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveAllGivenResultsToError(); });
    +  mockClock.tick();
    +
    +  assertErrorCall(successCombinedResult, resultCallback);
    +}
    +
    +function testCombineOnSuccessWhenSomeResultsSuccess() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  resolveSomeGivenResultsToSuccess();
    +
    +  assertErrorCall(successCombinedResult, resultCallback);
    +}
    +
    +function testAsyncCombineOnSuccessWhenSomeResultsSuccess() {
    +  goog.result.wait(successCombinedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() { resolveSomeGivenResultsToSuccess(); });
    +  mockClock.tick();
    +
    +  assertErrorCall(successCombinedResult, resultCallback);
    +}
    +
    +function testCancelParentResults() {
    +  goog.result.wait(combinedResult, resultCallback);
    +
    +  goog.result.cancelParentResults(combinedResult);
    +
    +  assertArgumentContainsGivenResults(combinedResult.getValue())
    +  goog.array.forEach([result1, result2, result3, result4],
    +      function(result) {
    +        assertTrue(result.isCanceled());
    +      });
    +}
    +
    +function assertSuccessCall(combinedResult, resultCallback) {
    +  assertEquals(goog.result.Result.State.SUCCESS, combinedResult.getState());
    +  assertEquals(1, resultCallback.getCallCount());
    +
    +  var result = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(combinedResult, result);
    +  assertArgumentContainsGivenResults(result.getValue());
    +}
    +
    +function assertErrorCall(combinedResult, resultCallback) {
    +  assertEquals(goog.result.Result.State.ERROR,
    +                 combinedResult.getState());
    +  assertEquals(1, resultCallback.getCallCount());
    +
    +  var result = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(combinedResult, result);
    +  assertArgumentContainsGivenResults(combinedResult.getError());
    +}
    +
    +function assertArgumentContainsGivenResults(resultsArray) {
    +  assertEquals(4, resultsArray.length);
    +
    +  goog.array.forEach([result1, result2, result3, result4], function(res) {
    +      assertTrue(goog.array.contains(resultsArray, res));
    +  });
    +}
    +
    +function resolveAllGivenResultsToSuccess() {
    +  goog.array.forEach([result1, result2, result3, result4], function(res) {
    +      res.setValue(1);
    +  });
    +}
    +
    +function resolveAllGivenResultsToError() {
    +  goog.array.forEach([result1, result2, result3, result4], function(res) {
    +      res.setError();
    +  });
    +}
    +
    +function resolveSomeGivenResultsToSuccess() {
    +  goog.array.forEach([result2, result3, result4], function(res) {
    +      res.setValue(1);
    +  });
    +  result1.setError();
    +}
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/deferredadaptor.js b/mapbox-gl/closure-library/closure/goog/result/deferredadaptor.js
    new file mode 100644
    index 0000000000..d55a3f65c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/deferredadaptor.js
    @@ -0,0 +1,59 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An adaptor from a Result to a Deferred.
    + *
    + * TODO (vbhasin): cancel() support.
    + * TODO (vbhasin): See if we can make this a static.
    + * TODO (gboyer, vbhasin): Rename to "Adapter" once this graduates; this is the
    + * proper programmer spelling.
    + */
    +
    +
    +goog.provide('goog.result.DeferredAdaptor');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.result');
    +goog.require('goog.result.Result');
    +
    +
    +
    +/**
    + * An adaptor from Result to a Deferred, for use with existing Deferred chains.
    + *
    + * @param {!goog.result.Result} result A result.
    + * @constructor
    + * @extends {goog.async.Deferred}
    + * @final
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.DeferredAdaptor = function(result) {
    +  goog.result.DeferredAdaptor.base(this, 'constructor');
    +  goog.result.wait(result, function(result) {
    +    if (this.hasFired()) {
    +      return;
    +    }
    +    if (result.getState() == goog.result.Result.State.SUCCESS) {
    +      this.callback(result.getValue());
    +    } else if (result.getState() == goog.result.Result.State.ERROR) {
    +      if (result.getError() instanceof goog.result.Result.CancelError) {
    +        this.cancel();
    +      } else {
    +        this.errback(result.getError());
    +      }
    +    }
    +  }, this);
    +};
    +goog.inherits(goog.result.DeferredAdaptor, goog.async.Deferred);
    diff --git a/mapbox-gl/closure-library/closure/goog/result/deferredadaptor_test.html b/mapbox-gl/closure-library/closure/goog/result/deferredadaptor_test.html
    new file mode 100644
    index 0000000000..02df7b7b8c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/deferredadaptor_test.html
    @@ -0,0 +1,86 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.DeferredAdaptor</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.result');
    +goog.require('goog.result.DeferredAdaptor');
    +goog.require('goog.result.SimpleResult');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var result, deferred, record;
    +
    +function setUp() {
    +  result = new goog.result.SimpleResult();
    +  deferred = new goog.result.DeferredAdaptor(result);
    +  record = new goog.testing.recordFunction();
    +}
    +
    +function tearDown() {
    +  result = deferred = record = null;
    +}
    +
    +function testResultSuccesfulResolution() {
    +  deferred.addCallback(record);
    +  result.setValue(1);
    +  assertEquals(1, record.getCallCount());
    +  var call = record.popLastCall();
    +  assertEquals(1, call.getArgument(0));
    +}
    +
    +function testResultErrorResolution() {
    +  deferred.addErrback(record);
    +  result.setError(2);
    +  assertEquals(1, record.getCallCount());
    +  var call = record.popLastCall();
    +  assertEquals(2, call.getArgument(0));
    +}
    +
    +function testResultCancelResolution() {
    +  deferred.addCallback(record);
    +  var cancelCallback = new goog.testing.recordFunction();
    +  deferred.addErrback(cancelCallback);
    +  result.cancel();
    +  assertEquals(0, record.getCallCount());
    +  assertEquals(1, cancelCallback.getCallCount());
    +  var call = cancelCallback.popLastCall();
    +  assertTrue(call.getArgument(0) instanceof
    +             goog.async.Deferred.CanceledError);
    +}
    +
    +function testAddCallbackOnResolvedResult() {
    +  result.setValue(1);
    +  assertEquals(1, result.getValue());
    +  deferred.addCallback(record);
    +
    +  // callback should be called immediately when result is already resolved.
    +  assertEquals(1, record.getCallCount());
    +  assertEquals(1, record.popLastCall().getArgument(0));
    +}
    +
    +function testAddErrbackOnErroredResult() {
    +  result.setError(1);
    +  assertEquals(1, result.getError());
    +
    +  // errback should be called immediately when result already errored.
    +  deferred.addErrback(record);
    +  assertEquals(1, record.getCallCount());
    +  assertEquals(1, record.popLastCall().getArgument(0));
    +}
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/dependentresult.js b/mapbox-gl/closure-library/closure/goog/result/dependentresult.js
    new file mode 100644
    index 0000000000..11898c8ced
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/dependentresult.js
    @@ -0,0 +1,45 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface for Results whose eventual value depends on the
    + *     value of one or more other Results.
    + */
    +
    +goog.provide('goog.result.DependentResult');
    +
    +goog.require('goog.result.Result');
    +
    +
    +
    +/**
    + * A DependentResult represents a Result whose eventual value depends on the
    + * value of one or more other Results. For example, the Result returned by
    + * @see goog.result.chain or @see goog.result.combine is dependent on the
    + * Results given as arguments.
    + * @interface
    + * @extends {goog.result.Result}
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.DependentResult = function() {};
    +
    +
    +/**
    + *
    + * @return {!Array<!goog.result.Result>} A list of Results which will affect
    + *     the eventual value of this Result. The returned Results may themselves
    + *     have parent results, which would be grandparents of this Result;
    + *     grandparents (and any other ancestors) are not included in this list.
    + */
    +goog.result.DependentResult.prototype.getParentResults = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/result/result_interface.js b/mapbox-gl/closure-library/closure/goog/result/result_interface.js
    new file mode 100644
    index 0000000000..68c3c8d334
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/result_interface.js
    @@ -0,0 +1,119 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines an interface that represents a Result.
    + *
    + * NOTE: goog.result is soft deprecated - we expect to replace this and
    + * {@link goog.async.Deferred} with {@link goog.Promise}.
    + */
    +
    +goog.provide('goog.result.Result');
    +
    +goog.require('goog.Thenable');
    +
    +
    +
    +/**
    + * A Result object represents a value returned by an asynchronous
    + * operation at some point in the future (e.g. a network fetch). This is akin
    + * to a 'Promise' or a 'Future' in other languages and frameworks.
    + *
    + * @interface
    + * @extends {goog.Thenable}
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.Result = function() {};
    +
    +
    +/**
    + * Attaches handlers to be called when the value of this Result is available.
    + * Handlers are called in the order they were added by wait.
    + *
    + * @param {!function(this:T, !goog.result.Result)} handler The function called
    + *     when the value is available. The function is passed the Result object as
    + *     the only argument.
    + * @param {T=} opt_scope Optional scope for the handler.
    + * @template T
    + */
    +goog.result.Result.prototype.wait = function(handler, opt_scope) {};
    +
    +
    +/**
    + * The States this object can be in.
    + *
    + * @enum {string}
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.Result.State = {
    +  /** The operation was a success and the value is available. */
    +  SUCCESS: 'success',
    +
    +  /** The operation resulted in an error. */
    +  ERROR: 'error',
    +
    +  /** The operation is incomplete and the value is not yet available. */
    +  PENDING: 'pending'
    +};
    +
    +
    +/**
    + * @return {!goog.result.Result.State} The state of this Result.
    + */
    +goog.result.Result.prototype.getState = function() {};
    +
    +
    +/**
    + * @return {*} The value of this Result. Will return undefined if the Result is
    + *     pending or was an error.
    + */
    +goog.result.Result.prototype.getValue = function() {};
    +
    +
    +/**
    + * @return {*} The error slug for this Result. Will return undefined if the
    + *     Result was a success, the error slug was not set, or if the Result is
    + *     pending.
    + */
    +goog.result.Result.prototype.getError = function() {};
    +
    +
    +/**
    + * Cancels the current Result, invoking the canceler function, if set.
    + *
    + * @return {boolean} Whether the Result was canceled.
    + */
    +goog.result.Result.prototype.cancel = function() {};
    +
    +
    +/**
    + * @return {boolean} Whether this Result was canceled.
    + */
    +goog.result.Result.prototype.isCanceled = function() {};
    +
    +
    +
    +/**
    + * The value to be passed to the error handlers invoked upon cancellation.
    + * @constructor
    + * @extends {Error}
    + * @final
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.Result.CancelError = function() {
    +  // Note that this does not derive from goog.debug.Error in order to prevent
    +  // stack trace capture and reduce the amount of garbage generated during a
    +  // cancel() operation.
    +};
    +goog.inherits(goog.result.Result.CancelError, Error);
    diff --git a/mapbox-gl/closure-library/closure/goog/result/resultutil.js b/mapbox-gl/closure-library/closure/goog/result/resultutil.js
    new file mode 100644
    index 0000000000..91ffef404f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/resultutil.js
    @@ -0,0 +1,556 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file provides primitives and tools (wait, transform,
    + *     chain, combine) that make it easier to work with Results. This section
    + *     gives an overview of their functionality along with some examples and the
    + *     actual definitions have detailed descriptions next to them.
    + *
    + *
    + * NOTE: goog.result is soft deprecated - we expect to replace this and
    + * goog.async.Deferred with a wrapper around W3C Promises:
    + * http://dom.spec.whatwg.org/#promises.
    + */
    +
    +goog.provide('goog.result');
    +
    +goog.require('goog.array');
    +goog.require('goog.result.DependentResult');
    +goog.require('goog.result.Result');
    +goog.require('goog.result.SimpleResult');
    +
    +
    +/**
    + * Returns a successful result containing the provided value.
    + *
    + * Example:
    + * <pre>
    + *
    + * var value = 'some-value';
    + * var result = goog.result.immediateResult(value);
    + * assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    + * assertEquals(value, result.getValue());
    + *
    + * </pre>
    + *
    + * @param {*} value The value of the result.
    + * @return {!goog.result.Result} A Result object that has already been resolved
    + *     to the supplied value.
    + */
    +goog.result.successfulResult = function(value) {
    +  var result = new goog.result.SimpleResult();
    +  result.setValue(value);
    +  return result;
    +};
    +
    +
    +/**
    + * Returns a failed result with the optional error slug set.
    + *
    + * Example:
    + * <pre>
    + *
    + * var error = new Error('something-failed');
    + * var result = goog.result.failedResult(error);
    + * assertEquals(goog.result.Result.State.ERROR, result.getState());
    + * assertEquals(error, result.getError());
    + *
    + * </pre>
    + *
    + * @param {*=} opt_error The error to which the result should resolve.
    + * @return {!goog.result.Result} A Result object that has already been resolved
    + *     to the supplied Error.
    + */
    +goog.result.failedResult = function(opt_error) {
    +  var result = new goog.result.SimpleResult();
    +  result.setError(opt_error);
    +  return result;
    +};
    +
    +
    +/**
    + * Returns a canceled result.
    + * The result will be resolved to an error of type CancelError.
    + *
    + * Example:
    + * <pre>
    + *
    + * var result = goog.result.canceledResult();
    + * assertEquals(goog.result.Result.State.ERROR, result.getState());
    + * var error = result.getError();
    + * assertTrue(error instanceof goog.result.Result.CancelError);
    + *
    + * </pre>
    + *
    + * @return {!goog.result.Result} A canceled Result.
    + */
    +goog.result.canceledResult = function() {
    +  var result = new goog.result.SimpleResult();
    +  result.cancel();
    +  return result;
    +};
    +
    +
    +/**
    + * Calls the handler on resolution of the result (success or failure).
    + * The handler is passed the result object as the only parameter. The call will
    + * be immediate if the result is no longer pending.
    + *
    + * Example:
    + * <pre>
    + *
    + * var result = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Wait for the result to be resolved and alert it's state.
    + * goog.result.wait(result, function(result) {
    + *   alert('State: ' + result.getState());
    + * });
    + * </pre>
    + *
    + * @param {!goog.result.Result} result The result to install the handlers.
    + * @param {function(this:T, !goog.result.Result)} handler The handler to be
    + *     called. The handler is passed the result object as the only parameter.
    + * @param {T=} opt_scope Optional scope for the handler.
    + * @template T
    + */
    +goog.result.wait = function(result, handler, opt_scope) {
    +  result.wait(handler, opt_scope);
    +};
    +
    +
    +/**
    + * Calls the handler if the result succeeds. The result object is the only
    + * parameter passed to the handler. The call will be immediate if the result
    + * has already succeeded.
    + *
    + * Example:
    + * <pre>
    + *
    + * var result = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // attach a success handler.
    + * goog.result.waitOnSuccess(result, function(resultValue, result) {
    + *   var datavalue = result.getvalue();
    + *   alert('value: ' + datavalue + ' == ' + resultValue);
    + * });
    + * </pre>
    + *
    + * @param {!goog.result.Result} result The result to install the handlers.
    + * @param {function(this:T, ?, !goog.result.Result)} handler The handler to be
    + *     called. The handler is passed the result value and the result as
    + *     parameters.
    + * @param {T=} opt_scope Optional scope for the handler.
    + * @template T
    + */
    +goog.result.waitOnSuccess = function(result, handler, opt_scope) {
    +  goog.result.wait(result, function(res) {
    +    if (res.getState() == goog.result.Result.State.SUCCESS) {
    +      // 'this' refers to opt_scope
    +      handler.call(this, res.getValue(), res);
    +    }
    +  }, opt_scope);
    +};
    +
    +
    +/**
    + * Calls the handler if the result action errors. The result object is passed as
    + * the only parameter to the handler. The call will be immediate if the result
    + * object has already resolved to an error.
    + *
    + * Example:
    + *
    + * <pre>
    + *
    + * var result = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Attach a failure handler.
    + * goog.result.waitOnError(result, function(error) {
    + *  // Failed asynchronous call!
    + * });
    + * </pre>
    + *
    + * @param {!goog.result.Result} result The result to install the handlers.
    + * @param {function(this:T, ?, !goog.result.Result)} handler The handler to be
    + *     called. The handler is passed the error and the result object as
    + *     parameters.
    + * @param {T=} opt_scope Optional scope for the handler.
    + * @template T
    + */
    +goog.result.waitOnError = function(result, handler, opt_scope) {
    +  goog.result.wait(result, function(res) {
    +    if (res.getState() == goog.result.Result.State.ERROR) {
    +      // 'this' refers to opt_scope
    +      handler.call(this, res.getError(), res);
    +    }
    +  }, opt_scope);
    +};
    +
    +
    +/**
    + * Given a result and a transform function, returns a new result whose value,
    + * on success, will be the value of the given result after having been passed
    + * through the transform function.
    + *
    + * If the given result is an error, the returned result is also an error and the
    + * transform will not be called.
    + *
    + * Example:
    + * <pre>
    + *
    + * var result = xhr.getJson('testdata/xhr_test_json.data');
    + *
    + * // Transform contents of returned data using 'processJson' and create a
    + * // transformed result to use returned JSON.
    + * var transformedResult = goog.result.transform(result, processJson);
    + *
    + * // Attach success and failure handlers to the tranformed result.
    + * goog.result.waitOnSuccess(transformedResult, function(resultValue, result) {
    + *   var jsonData = resultValue;
    + *   assertEquals('ok', jsonData['stat']);
    + * });
    + *
    + * goog.result.waitOnError(transformedResult, function(error) {
    + *   // Failed getJson call
    + * });
    + * </pre>
    + *
    + * @param {!goog.result.Result} result The result whose value will be
    + *     transformed.
    + * @param {function(?):?} transformer The transformer
    + *     function. The return value of this function will become the value of the
    + *     returned result.
    + *
    + * @return {!goog.result.DependentResult} A new Result whose eventual value will
    + *     be the returned value of the transformer function.
    + */
    +goog.result.transform = function(result, transformer) {
    +  var returnedResult = new goog.result.DependentResultImpl_([result]);
    +
    +  goog.result.wait(result, function(res) {
    +    if (res.getState() == goog.result.Result.State.SUCCESS) {
    +      returnedResult.setValue(transformer(res.getValue()));
    +    } else {
    +      returnedResult.setError(res.getError());
    +    }
    +  });
    +
    +  return returnedResult;
    +};
    +
    +
    +/**
    + * The chain function aids in chaining of asynchronous Results. This provides a
    + * convenience for use cases where asynchronous operations must happen serially
    + * i.e. subsequent asynchronous operations are dependent on data returned by
    + * prior asynchronous operations.
    + *
    + * It accepts a result and an action callback as arguments and returns a
    + * result. The action callback is called when the first result succeeds and is
    + * supposed to return a second result. The returned result is resolved when one
    + * of both of the results resolve (depending on their success or failure.) The
    + * state and value of the returned result in the various cases is documented
    + * below:
    + * <pre>
    + *
    + * First Result State:    Second Result State:    Returned Result State:
    + * SUCCESS                SUCCESS                 SUCCESS
    + * SUCCESS                ERROR                   ERROR
    + * ERROR                  Not created             ERROR
    + * </pre>
    + *
    + * The value of the returned result, in the case both results succeed, is the
    + * value of the second result (the result returned by the action callback.)
    + *
    + * Example:
    + * <pre>
    + *
    + * var testDataResult = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Chain this result to perform another asynchronous operation when this
    + * // Result is resolved.
    + * var chainedResult = goog.result.chain(testDataResult,
    + *     function(testDataResult) {
    + *
    + *       // The result value of testDataResult is the URL for JSON data.
    + *       var jsonDataUrl = testDataResult.getValue();
    + *
    + *       // Create a new Result object when the original result is resolved.
    + *       var jsonResult = xhr.getJson(jsonDataUrl);
    + *
    + *       // Return the newly created Result.
    + *       return jsonResult;
    + *     });
    + *
    + * // The chained result resolves to success when both results resolve to
    + * // success.
    + * goog.result.waitOnSuccess(chainedResult, function(resultValue, result) {
    + *
    + *   // At this point, both results have succeeded and we can use the JSON
    + *   // data returned by the second asynchronous call.
    + *   var jsonData = resultValue;
    + *   assertEquals('ok', jsonData['stat']);
    + * });
    + *
    + * // Attach the error handler to be called when either Result fails.
    + * goog.result.waitOnError(chainedResult, function(result) {
    + *   alert('chained result failed!');
    + * });
    + * </pre>
    + *
    + * @param {!goog.result.Result} result The result to chain.
    + * @param {function(this:T, !goog.result.Result):!goog.result.Result}
    + *     actionCallback The callback called when the result is resolved. This
    + *     callback must return a Result.
    + * @param {T=} opt_scope Optional scope for the action callback.
    + * @return {!goog.result.DependentResult} A result that is resolved when both
    + *     the given Result and the Result returned by the actionCallback have
    + *     resolved.
    + * @template T
    + */
    +goog.result.chain = function(result, actionCallback, opt_scope) {
    +  var dependentResult = new goog.result.DependentResultImpl_([result]);
    +
    +  // Wait for the first action.
    +  goog.result.wait(result, function(result) {
    +    if (result.getState() == goog.result.Result.State.SUCCESS) {
    +
    +      // The first action succeeded. Chain the contingent action.
    +      var contingentResult = actionCallback.call(opt_scope, result);
    +      dependentResult.addParentResult(contingentResult);
    +      goog.result.wait(contingentResult, function(contingentResult) {
    +
    +        // The contingent action completed. Set the dependent result based on
    +        // the contingent action's outcome.
    +        if (contingentResult.getState() ==
    +            goog.result.Result.State.SUCCESS) {
    +          dependentResult.setValue(contingentResult.getValue());
    +        } else {
    +          dependentResult.setError(contingentResult.getError());
    +        }
    +      });
    +    } else {
    +      // First action failed, the dependent result should also fail.
    +      dependentResult.setError(result.getError());
    +    }
    +  });
    +
    +  return dependentResult;
    +};
    +
    +
    +/**
    + * Returns a result that waits on all given results to resolve. Once all have
    + * resolved, the returned result will succeed (and never error).
    + *
    + * Example:
    + * <pre>
    + *
    + * var result1 = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Get a second independent Result.
    + * var result2 = xhr.getJson('testdata/xhr_test_json.data');
    + *
    + * // Create a Result that resolves when both prior results resolve.
    + * var combinedResult = goog.result.combine(result1, result2);
    + *
    + * // Process data after resolution of both results.
    + * goog.result.waitOnSuccess(combinedResult, function(results) {
    + *   goog.array.forEach(results, function(result) {
    + *       alert(result.getState());
    + *   });
    + * });
    + * </pre>
    + *
    + * @param {...!goog.result.Result} var_args The results to wait on.
    + *
    + * @return {!goog.result.DependentResult} A new Result whose eventual value will
    + *     be the resolved given Result objects.
    + */
    +goog.result.combine = function(var_args) {
    +  /** @type {!Array<!goog.result.Result>} */
    +  var results = goog.array.clone(arguments);
    +  var combinedResult = new goog.result.DependentResultImpl_(results);
    +
    +  var isResolved = function(res) {
    +    return res.getState() != goog.result.Result.State.PENDING;
    +  };
    +
    +  var checkResults = function() {
    +    if (combinedResult.getState() == goog.result.Result.State.PENDING &&
    +        goog.array.every(results, isResolved)) {
    +      combinedResult.setValue(results);
    +    }
    +  };
    +
    +  goog.array.forEach(results, function(result) {
    +    goog.result.wait(result, checkResults);
    +  });
    +
    +  return combinedResult;
    +};
    +
    +
    +/**
    + * Returns a result that waits on all given results to resolve. Once all have
    + * resolved, the returned result will succeed if and only if all given results
    + * succeeded. Otherwise it will error.
    + *
    + * Example:
    + * <pre>
    + *
    + * var result1 = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Get a second independent Result.
    + * var result2 = xhr.getJson('testdata/xhr_test_json.data');
    + *
    + * // Create a Result that resolves when both prior results resolve.
    + * var combinedResult = goog.result.combineOnSuccess(result1, result2);
    + *
    + * // Process data after successful resolution of both results.
    + * goog.result.waitOnSuccess(combinedResult, function(results) {
    + *   var textData = results[0].getValue();
    + *   var jsonData = results[1].getValue();
    + *   assertEquals('Just some data.', textData);
    + *   assertEquals('ok', jsonData['stat']);
    + * });
    + *
    + * // Handle errors when either or both results failed.
    + * goog.result.waitOnError(combinedResult, function(combined) {
    + *   var results = combined.getError();
    + *
    + *   if (results[0].getState() == goog.result.Result.State.ERROR) {
    + *     alert('result1 failed');
    + *   }
    + *
    + *   if (results[1].getState() == goog.result.Result.State.ERROR) {
    + *     alert('result2 failed');
    + *   }
    + * });
    + * </pre>
    + *
    + * @param {...!goog.result.Result} var_args The results to wait on.
    + *
    + * @return {!goog.result.DependentResult} A new Result whose eventual value will
    + *     be an array of values of the given Result objects.
    + */
    +goog.result.combineOnSuccess = function(var_args) {
    +  var results = goog.array.clone(arguments);
    +  var combinedResult = new goog.result.DependentResultImpl_(results);
    +
    +  var resolvedSuccessfully = function(res) {
    +    return res.getState() == goog.result.Result.State.SUCCESS;
    +  };
    +
    +  goog.result.wait(
    +      goog.result.combine.apply(goog.result.combine, results),
    +      // The combined result never ERRORs
    +      function(res) {
    +        var results = /** @type {Array<!goog.result.Result>} */ (
    +            res.getValue());
    +        if (goog.array.every(results, resolvedSuccessfully)) {
    +          combinedResult.setValue(results);
    +        } else {
    +          combinedResult.setError(results);
    +        }
    +      });
    +
    +  return combinedResult;
    +};
    +
    +
    +/**
    + * Given a DependentResult, cancels the Results it depends on (that is, the
    + * results returned by getParentResults). This function does not recurse,
    + * so e.g. parents of parents are not canceled; only the immediate parents of
    + * the given Result are canceled.
    + *
    + * Example using @see goog.result.combine:
    + * <pre>
    + * var result1 = xhr.get('testdata/xhr_test_text.data');
    + *
    + * // Get a second independent Result.
    + * var result2 = xhr.getJson('testdata/xhr_test_json.data');
    + *
    + * // Create a Result that resolves when both prior results resolve.
    + * var combinedResult = goog.result.combineOnSuccess(result1, result2);
    + *
    + * combinedResult.wait(function() {
    + *   if (combinedResult.isCanceled()) {
    + *     goog.result.cancelParentResults(combinedResult);
    + *   }
    + * });
    + *
    + * // Now, canceling combinedResult will cancel both result1 and result2.
    + * combinedResult.cancel();
    + * </pre>
    + * @param {!goog.result.DependentResult} dependentResult A Result that is
    + *     dependent on the values of other Results (for example the Result of a
    + *     goog.result.combine, goog.result.chain, or goog.result.transform call).
    + * @return {boolean} True if any results were successfully canceled; otherwise
    + *     false.
    + * TODO(user): Implement a recursive version of this that cancels all
    + * ancestor results.
    + */
    +goog.result.cancelParentResults = function(dependentResult) {
    +  var anyCanceled = false;
    +  var results = dependentResult.getParentResults();
    +  for (var n = 0; n < results.length; n++) {
    +    anyCanceled |= results[n].cancel();
    +  }
    +  return !!anyCanceled;
    +};
    +
    +
    +
    +/**
    + * A DependentResult represents a Result whose eventual value depends on the
    + * value of one or more other Results. For example, the Result returned by
    + * @see goog.result.chain or @see goog.result.combine is dependent on the
    + * Results given as arguments.
    + *
    + * @param {!Array<!goog.result.Result>} parentResults A list of Results that
    + *     will affect the eventual value of this Result.
    + * @constructor
    + * @implements {goog.result.DependentResult}
    + * @extends {goog.result.SimpleResult}
    + * @private
    + */
    +goog.result.DependentResultImpl_ = function(parentResults) {
    +  goog.result.DependentResultImpl_.base(this, 'constructor');
    +  /**
    +   * A list of Results that will affect the eventual value of this Result.
    +   * @type {!Array<!goog.result.Result>}
    +   * @private
    +   */
    +  this.parentResults_ = parentResults;
    +};
    +goog.inherits(goog.result.DependentResultImpl_, goog.result.SimpleResult);
    +
    +
    +/**
    + * Adds a Result to the list of Results that affect this one.
    + * @param {!goog.result.Result} parentResult A result whose value affects the
    + *     value of this Result.
    + */
    +goog.result.DependentResultImpl_.prototype.addParentResult = function(
    +    parentResult) {
    +  this.parentResults_.push(parentResult);
    +};
    +
    +
    +/** @override */
    +goog.result.DependentResultImpl_.prototype.getParentResults = function() {
    +  return this.parentResults_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/result/resultutil_test.html b/mapbox-gl/closure-library/closure/goog/result/resultutil_test.html
    new file mode 100644
    index 0000000000..d26898ccfa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/resultutil_test.html
    @@ -0,0 +1,46 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.*</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.result');
    +goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +function testSuccessfulResult() {
    +  var value = 'some-value';
    +  var result = goog.result.successfulResult(value);
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(value, result.getValue());
    +}
    +
    +
    +function testFailedResult() {
    +  var error = new Error('something-failed');
    +  var result = goog.result.failedResult(error);
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(error, result.getError());
    +}
    +
    +
    +function testCanceledResult() {
    +  var result = goog.result.canceledResult();
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +
    +  var error = result.getError();
    +  assertTrue(error instanceof goog.result.Result.CancelError);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/simpleresult.js b/mapbox-gl/closure-library/closure/goog/result/simpleresult.js
    new file mode 100644
    index 0000000000..9e1af70a55
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/simpleresult.js
    @@ -0,0 +1,260 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A SimpleResult object that implements goog.result.Result.
    + * See below for a more detailed description.
    + */
    +
    +goog.provide('goog.result.SimpleResult');
    +goog.provide('goog.result.SimpleResult.StateError');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.debug.Error');
    +goog.require('goog.result.Result');
    +
    +
    +
    +/**
    + * A SimpleResult object is a basic implementation of the
    + * goog.result.Result interface. This could be subclassed(e.g. XHRResult)
    + * or instantiated and returned by another class as a form of result. The caller
    + * receiving the result could then attach handlers to be called when the result
    + * is resolved(success or error).
    + *
    + * @constructor
    + * @implements {goog.result.Result}
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.SimpleResult = function() {
    +  /**
    +   * The current state of this Result.
    +   * @type {goog.result.Result.State}
    +   * @private
    +   */
    +  this.state_ = goog.result.Result.State.PENDING;
    +
    +  /**
    +   * The list of handlers to call when this Result is resolved.
    +   * @type {!Array<!goog.result.SimpleResult.HandlerEntry_>}
    +   * @private
    +   */
    +  this.handlers_ = [];
    +
    +  // The value_ and error_ properties are initialized in the constructor to
    +  // ensure that all SimpleResult instances share the same hidden class in
    +  // modern JavaScript engines.
    +
    +  /**
    +   * The 'value' of this Result.
    +   * @type {*}
    +   * @private
    +   */
    +  this.value_ = undefined;
    +
    +  /**
    +   * The error slug for this Result.
    +   * @type {*}
    +   * @private
    +   */
    +  this.error_ = undefined;
    +};
    +goog.Thenable.addImplementation(goog.result.SimpleResult);
    +
    +
    +/**
    + * A waiting handler entry.
    + * @typedef {{
    + *   callback: !function(goog.result.SimpleResult),
    + *   scope: Object
    + * }}
    + * @private
    + */
    +goog.result.SimpleResult.HandlerEntry_;
    +
    +
    +
    +/**
    + * Error thrown if there is an attempt to set the value or error for this result
    + * more than once.
    + *
    + * @constructor
    + * @extends {goog.debug.Error}
    + * @final
    + * @deprecated Use {@link goog.Promise} instead - http://go/promisemigration
    + */
    +goog.result.SimpleResult.StateError = function() {
    +  goog.result.SimpleResult.StateError.base(this, 'constructor',
    +      'Multiple attempts to set the state of this Result');
    +};
    +goog.inherits(goog.result.SimpleResult.StateError, goog.debug.Error);
    +
    +
    +/** @override */
    +goog.result.SimpleResult.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/** @override */
    +goog.result.SimpleResult.prototype.getValue = function() {
    +  return this.value_;
    +};
    +
    +
    +/** @override */
    +goog.result.SimpleResult.prototype.getError = function() {
    +  return this.error_;
    +};
    +
    +
    +/**
    + * Attaches handlers to be called when the value of this Result is available.
    + *
    + * @param {!function(this:T, !goog.result.SimpleResult)} handler The function
    + *     called when the value is available. The function is passed the Result
    + *     object as the only argument.
    + * @param {T=} opt_scope Optional scope for the handler.
    + * @template T
    + * @override
    + */
    +goog.result.SimpleResult.prototype.wait = function(handler, opt_scope) {
    +  if (this.isPending_()) {
    +    this.handlers_.push({
    +      callback: handler,
    +      scope: opt_scope || null
    +    });
    +  } else {
    +    handler.call(opt_scope, this);
    +  }
    +};
    +
    +
    +/**
    + * Sets the value of this Result, changing the state.
    + *
    + * @param {*} value The value to set for this Result.
    + */
    +goog.result.SimpleResult.prototype.setValue = function(value) {
    +  if (this.isPending_()) {
    +    this.value_ = value;
    +    this.state_ = goog.result.Result.State.SUCCESS;
    +    this.callHandlers_();
    +  } else if (!this.isCanceled()) {
    +    // setValue is a no-op if this Result has been canceled.
    +    throw new goog.result.SimpleResult.StateError();
    +  }
    +};
    +
    +
    +/**
    + * Sets the Result to be an error Result.
    + *
    + * @param {*=} opt_error Optional error slug to set for this Result.
    + */
    +goog.result.SimpleResult.prototype.setError = function(opt_error) {
    +  if (this.isPending_()) {
    +    this.error_ = opt_error;
    +    this.state_ = goog.result.Result.State.ERROR;
    +    this.callHandlers_();
    +  } else if (!this.isCanceled()) {
    +    // setError is a no-op if this Result has been canceled.
    +    throw new goog.result.SimpleResult.StateError();
    +  }
    +};
    +
    +
    +/**
    + * Calls the handlers registered for this Result.
    + *
    + * @private
    + */
    +goog.result.SimpleResult.prototype.callHandlers_ = function() {
    +  var handlers = this.handlers_;
    +  this.handlers_ = [];
    +  for (var n = 0; n < handlers.length; n++) {
    +    var handlerEntry = handlers[n];
    +    handlerEntry.callback.call(handlerEntry.scope, this);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the Result is pending.
    + * @private
    + */
    +goog.result.SimpleResult.prototype.isPending_ = function() {
    +  return this.state_ == goog.result.Result.State.PENDING;
    +};
    +
    +
    +/**
    + * Cancels the Result.
    + *
    + * @return {boolean} Whether the result was canceled. It will not be canceled if
    + *    the result was already canceled or has already resolved.
    + * @override
    + */
    +goog.result.SimpleResult.prototype.cancel = function() {
    +  // cancel is a no-op if the result has been resolved.
    +  if (this.isPending_()) {
    +    this.setError(new goog.result.Result.CancelError());
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.result.SimpleResult.prototype.isCanceled = function() {
    +  return this.state_ == goog.result.Result.State.ERROR &&
    +         this.error_ instanceof goog.result.Result.CancelError;
    +};
    +
    +
    +/** @override */
    +goog.result.SimpleResult.prototype.then = function(
    +    opt_onFulfilled, opt_onRejected, opt_context) {
    +  var resolve, reject;
    +  // Copy the resolvers to outer scope, so that they are available
    +  // when the callback to wait() fires (which may be synchronous).
    +  var promise = new goog.Promise(function(res, rej) {
    +    resolve = res;
    +    reject = rej;
    +  });
    +  this.wait(function(result) {
    +    if (result.isCanceled()) {
    +      promise.cancel();
    +    } else if (result.getState() == goog.result.Result.State.SUCCESS) {
    +      resolve(result.getValue());
    +    } else if (result.getState() == goog.result.Result.State.ERROR) {
    +      reject(result.getError());
    +    }
    +  });
    +  return promise.then(opt_onFulfilled, opt_onRejected, opt_context);
    +};
    +
    +
    +/**
    + * Creates a SimpleResult that fires when the given promise resolves.
    + * Use only during migration to Promises.
    + * @param {!goog.Promise<?>} promise
    + * @return {!goog.result.Result}
    + */
    +goog.result.SimpleResult.fromPromise = function(promise) {
    +  var result = new goog.result.SimpleResult();
    +  promise.then(result.setValue, result.setError, result);
    +  return result;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/result/simpleresult_test.html b/mapbox-gl/closure-library/closure/goog/result/simpleresult_test.html
    new file mode 100644
    index 0000000000..4c0e9faa5d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/simpleresult_test.html
    @@ -0,0 +1,375 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.SimpleResult</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.Timer');
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.result');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.testing.jsunit');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var result, mockClock, resultCallback;
    +
    +function setUpPage() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +}
    +
    +function setUp() {
    +  mockClock.reset();
    +  resultCallback = new goog.testing.recordFunction();
    +  resultCallback1 = new goog.testing.recordFunction();
    +  resultCallback2 = new goog.testing.recordFunction();
    +  result = new goog.result.SimpleResult();
    +}
    +
    +function tearDown() {
    +  resultCallback = resultCallback1 = resultCallback2 = result = null;
    +}
    +
    +function tearDownPage() {
    +  mockClock.uninstall();
    +  goog.dispose(mockClock);
    +}
    +
    +function testHandlersCalledOnSuccess() {
    +  result.wait(resultCallback1);
    +  result.wait(resultCallback2);
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  assertEquals(0, resultCallback1.getCallCount());
    +  assertEquals(0, resultCallback2.getCallCount());
    +
    +  result.setValue(2);
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(2, result.getValue());
    +  assertEquals(1, resultCallback1.getCallCount());
    +  assertEquals(1, resultCallback2.getCallCount());
    +
    +  var res1 = resultCallback1.popLastCall().getArgument(0);
    +  assertObjectEquals(result, res1);
    +
    +  var res2 = resultCallback2.popLastCall().getArgument(0);
    +  assertObjectEquals(result, res2);
    +}
    +
    +function testCustomHandlerScope() {
    +  result.wait(resultCallback1);
    +  var scope = {};
    +  result.wait(resultCallback2, scope);
    +
    +  result.setValue(2);
    +
    +  assertEquals(1, resultCallback1.getCallCount());
    +  assertEquals(1, resultCallback2.getCallCount());
    +
    +  var this1 = resultCallback1.popLastCall().getThis();
    +  assertObjectEquals(goog.global, this1);
    +
    +  var this2 = resultCallback2.popLastCall().getThis();
    +  assertObjectEquals(scope, this2);
    +}
    +
    +function testHandlersCalledOnError() {
    +  result.wait(resultCallback1);
    +  result.wait(resultCallback2);
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  var error = "Network Error";
    +  result.setError(error);
    +
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(error, result.getError());
    +  assertEquals(1, resultCallback1.getCallCount());
    +  assertEquals(1, resultCallback2.getCallCount());
    +
    +  var res1 = resultCallback1.popLastCall().getArgument(0);
    +  assertObjectEquals(result, res1);
    +  var res2 = resultCallback2.popLastCall().getArgument(0);
    +  assertObjectEquals(result, res2);
    +}
    +
    +function testAttachingHandlerOnSuccessfulResult() {
    +  result.setValue(2);
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(2, result.getValue());
    +  // resultCallback should be called immediately on a resolved Result
    +  assertEquals(0, resultCallback.getCallCount());
    +
    +  result.wait(resultCallback);
    +
    +  assertEquals(1, resultCallback.getCallCount());
    +  var res = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +}
    +
    +function testAttachingHandlerOnErrorResult() {
    +  var error = { code: -1, errorString: "Invalid JSON" };
    +  result.setError(error);
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(error, result.getError());
    +  // resultCallback should be called immediately on a resolved Result
    +  assertEquals(0, resultCallback.getCallCount());
    +
    +  result.wait(resultCallback);
    +
    +  assertEquals(1, resultCallback.getCallCount());
    +  var res = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +}
    +
    +function testExceptionThrownOnMultipleSuccessfulResolutionAttempts() {
    +  result.setValue(1);
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +
    +  // Try to set the value again
    +  var e = assertThrows(goog.bind(result.setValue, result, 3));
    +  assertTrue(e instanceof goog.result.SimpleResult.StateError);
    +}
    +
    +function testExceptionThrownOnMultipleErrorResolutionAttempts() {
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  result.setError(5);
    +
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(5, result.getError());
    +  // Try to set error again
    +  var e = assertThrows(goog.bind(result.setError, result, 4));
    +  assertTrue(e instanceof goog.result.SimpleResult.StateError);
    +}
    +
    +function testExceptionThrownOnSuccessThenErrorResolutionAttempt() {
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  result.setValue(1);
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +
    +  // Try to set error after setting value
    +  var e = assertThrows(goog.bind(result.setError, result, 3));
    +  assertTrue(e instanceof goog.result.SimpleResult.StateError);
    +}
    +
    +function testExceptionThrownOnErrorThenSuccessResolutionAttempt() {
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  var error = "fail";
    +  result.setError(error);
    +
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(error, result.getError());
    +  // Try to set value after setting error
    +  var e = assertThrows(goog.bind(result.setValue, result, 1));
    +  assertTrue(e instanceof goog.result.SimpleResult.StateError);
    +}
    +
    +function testSuccessfulAsyncResolution() {
    +  result.wait(resultCallback);
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  goog.Timer.callOnce(function() {
    +    result.setValue(1);
    +  });
    +  mockClock.tick();
    +
    +  assertEquals(1, resultCallback.getCallCount());
    +
    +  var res = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(goog.result.Result.State.SUCCESS, res.getState());
    +  assertEquals(1, res.getValue());
    +}
    +
    +function testErrorAsyncResolution() {
    +  result.wait(resultCallback);
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  var error = 'Network failure';
    +  goog.Timer.callOnce(function() {
    +    result.setError(error);
    +  });
    +  mockClock.tick();
    +
    +  assertEquals(1, resultCallback.getCallCount());
    +  var res = resultCallback.popLastCall().getArgument(0);
    +  assertEquals(goog.result.Result.State.ERROR, res.getState());
    +  assertEquals(error, res.getError());
    +}
    +
    +function testCancelStateAndReturn() {
    +  assertFalse(result.isCanceled());
    +  var canceled = result.cancel();
    +  assertTrue(result.isCanceled());
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertTrue(result.getError() instanceof goog.result.Result.CancelError);
    +  assertTrue(canceled);
    +}
    +
    +function testErrorHandlersFireOnCancel() {
    +  result.wait(resultCallback);
    +  result.cancel();
    +
    +  assertEquals(1, resultCallback.getCallCount());
    +  var lastCall = resultCallback.popLastCall();
    +  var res = lastCall.getArgument(0);
    +  assertEquals(goog.result.Result.State.ERROR, res.getState());
    +  assertTrue(res.getError() instanceof goog.result.Result.CancelError);
    +}
    +
    +function testCancelAfterSetValue() {
    +  // cancel after setValue/setError => no-op
    +  result.wait(resultCallback);
    +  result.setValue(1);
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +  assertEquals(1, resultCallback.getCallCount());
    +
    +  result.cancel();
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +  assertEquals(1, resultCallback.getCallCount());
    +}
    +
    +function testSetValueAfterCancel() {
    +  // setValue/setError after cancel => no-op
    +  result.wait(resultCallback);
    +
    +  result.cancel();
    +  assertTrue(result.isCanceled());
    +  assertTrue(result.getError() instanceof goog.result.Result.CancelError);
    +
    +  result.setValue(1);
    +  assertTrue(result.isCanceled());
    +  assertTrue(result.getError() instanceof goog.result.Result.CancelError);
    +
    +  result.setError(3);
    +  assertTrue(result.isCanceled());
    +  assertTrue(result.getError() instanceof goog.result.Result.CancelError);
    +}
    +
    +function testFromResolvedPromise() {
    +  var promise = goog.Promise.resolve('resolved');
    +  result = goog.result.SimpleResult.fromPromise(promise);
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  mockClock.tick();
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals('resolved', result.getValue());
    +  assertEquals(undefined, result.getError());
    +}
    +
    +function testFromRejectedPromise() {
    +  var promise = goog.Promise.reject('rejected');
    +  result = goog.result.SimpleResult.fromPromise(promise);
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  mockClock.tick();
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertEquals(undefined, result.getValue());
    +  assertEquals('rejected', result.getError());
    +}
    +
    +function testThen() {
    +  var value1, value2;
    +  result.then(function(val1) {
    +    return value1 = val1;
    +  }).then(function(val2) {
    +    value2 = val2;
    +  });
    +  result.setValue('done');
    +  assertUndefined(value1);
    +  assertUndefined(value2);
    +  mockClock.tick();
    +  assertEquals('done', value1);
    +  assertEquals('done', value2);
    +}
    +
    +function testThen_reject() {
    +  var value, reason;
    +  result.then(
    +    function(v) { value = v; },
    +    function(r) { reason = r; });
    +  result.setError(new Error('oops'));
    +  assertUndefined(value);
    +  mockClock.tick();
    +  assertUndefined(value);
    +  assertEquals('oops', reason.message);
    +}
    +
    +function testPromiseAll() {
    +  var promise = goog.Promise.resolve('promise');
    +  goog.Promise.all([result, promise]).then(function(values) {
    +    assertEquals(2, values.length);
    +    assertEquals('result', values[0]);
    +    assertEquals('promise', values[1]);
    +  });
    +  result.setValue('result');
    +  mockClock.tick();
    +}
    +
    +function testResolvingPromiseBlocksResult() {
    +  var value;
    +  goog.Promise.resolve('promise').then(function(value) {
    +    result.setValue(value);
    +  });
    +  result.wait(function(r) {
    +    value = r.getValue();
    +  });
    +  assertUndefined(value);
    +  mockClock.tick();
    +  assertEquals('promise', value);
    +}
    +
    +function testRejectingPromiseBlocksResult() {
    +  var err;
    +  goog.Promise.reject(new Error('oops')).then(
    +    undefined /* opt_onResolved */,
    +    function(reason) {
    +      result.setError(reason);
    +    });
    +  result.wait(function(r) {
    +    err = r.getError();
    +  });
    +  assertUndefined(err);
    +  mockClock.tick();
    +  assertEquals('oops', err.message);
    +}
    +
    +function testPromiseFromCanceledResult() {
    +  var reason;
    +  result.cancel();
    +  result.then(
    +    undefined /* opt_onResolved */,
    +    function(r) {
    +      reason = r;
    +    });
    +  mockClock.tick();
    +  assertTrue(reason instanceof goog.Promise.CancellationError);
    +}
    +
    +function testThenableInterface() {
    +  assertTrue(goog.Thenable.isImplementedBy(result));
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/transform_test.html b/mapbox-gl/closure-library/closure/goog/result/transform_test.html
    new file mode 100644
    index 0000000000..f21281e033
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/transform_test.html
    @@ -0,0 +1,153 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.transform</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.Timer');
    +goog.require('goog.result.SimpleResult');
    +goog.require('goog.result');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var result, resultCallback, multiplyResult, mockClock;
    +
    +function setUpPage() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +}
    +
    +function setUp() {
    +  mockClock.reset();
    +  result = new goog.result.SimpleResult();
    +  resultCallback = new goog.testing.recordFunction();
    +  multiplyResult = goog.testing.recordFunction(function(value) {
    +      return value * 2;
    +    });
    +}
    +
    +function tearDown() {
    +  result = multiplyResult = null;
    +}
    +
    +function tearDownPage() {
    +  mockClock.uninstall();
    +  goog.dispose(mockClock);
    +}
    +
    +function testTransformWhenResultSuccess() {
    +  var transformedResult = goog.result.transform(result, multiplyResult);
    +  goog.result.wait(transformedResult, resultCallback);
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  result.setValue(1);
    +  assertTransformerCall(multiplyResult, 1);
    +  assertSuccessCall(resultCallback, transformedResult, 2);
    +}
    +
    +function testTransformWhenResultSuccessAsync() {
    +  var transformedResult = goog.result.transform(result, multiplyResult);
    +  goog.result.wait(transformedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() {
    +    result.setValue(1);
    +  });
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  mockClock.tick();
    +  assertTransformerCall(multiplyResult, 1);
    +  assertSuccessCall(resultCallback, transformedResult, 2);
    +}
    +
    +function testTransformWhenResultError() {
    +  var transformedResult = goog.result.transform(result, multiplyResult);
    +  goog.result.wait(transformedResult, resultCallback);
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  result.setError(4);
    +  assertNoCall(multiplyResult);
    +  assertErrorCall(resultCallback, transformedResult, 4);
    +}
    +
    +function testTransformWhenResultErrorAsync() {
    +  var transformedResult = goog.result.transform(result, multiplyResult);
    +
    +  goog.result.wait(transformedResult, resultCallback);
    +
    +  goog.Timer.callOnce(function() {
    +    result.setError(5);
    +  });
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  mockClock.tick();
    +  assertNoCall(multiplyResult);
    +  assertErrorCall(resultCallback, transformedResult, 5);
    +}
    +
    +function testCancelParentResults() {
    +  var transformedResult = goog.result.transform(result, multiplyResult);
    +  goog.result.wait(transformedResult, resultCallback);
    +
    +  goog.result.cancelParentResults(transformedResult);
    +
    +  assertTrue(result.isCanceled());
    +  result.setValue(1);
    +  assertNoCall(multiplyResult);
    +}
    +
    +function testDoubleTransformCancel() {
    +  var step1Result = goog.result.transform(result, multiplyResult);
    +  var step2Result = goog.result.transform(step1Result, multiplyResult);
    +
    +  goog.result.cancelParentResults(step2Result);
    +
    +  assertFalse(result.isCanceled());
    +  assertTrue(step1Result.isCanceled());
    +  assertTrue(step2Result.isCanceled());
    +}
    +
    +function assertSuccessCall(recordFunction, result, value) {
    +  assertEquals(1, recordFunction.getCallCount());
    +
    +  var res = recordFunction.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +  assertEquals(goog.result.Result.State.SUCCESS, res.getState());
    +  assertEquals(value, res.getValue());
    +}
    +
    +function assertErrorCall(recordFunction, result, value) {
    +  assertEquals(1, recordFunction.getCallCount());
    +
    +  var res = recordFunction.popLastCall().getArgument(0);
    +  assertEquals(result, res);
    +  assertEquals(goog.result.Result.State.ERROR, res.getState());
    +  assertEquals(value, res.getError());
    +}
    +
    +function assertNoCall(recordFunction) {
    +  assertEquals(0, recordFunction.getCallCount());
    +}
    +
    +function assertTransformerCall(recordFunction, value) {
    +  assertEquals(1, recordFunction.getCallCount());
    +
    +  var argValue = recordFunction.popLastCall().getArgument(0);
    +  assertEquals(value, argValue);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/result/wait_test.html b/mapbox-gl/closure-library/closure/goog/result/wait_test.html
    new file mode 100644
    index 0000000000..fdba6dcae6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/result/wait_test.html
    @@ -0,0 +1,211 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.result.wait</title>
    +<script src="../base.js"></script>
    +<script>
    +
    +goog.require('goog.Timer');
    +goog.require('goog.result.SimpleResult');
    +goog.require('goog.result');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var result, waitCallback, waitOnSuccessCallback, waitOnErrorCallback;
    +
    +var mockClock, propertyReplacer;
    +
    +function setUpPage() {
    +  mockClock = new goog.testing.MockClock();
    +  mockClock.install();
    +}
    +
    +function setUp() {
    +  mockClock.reset();
    +  result = new goog.result.SimpleResult();
    +  propertyReplacer = new goog.testing.PropertyReplacer();
    +  waitCallback = new goog.testing.recordFunction();
    +  waitOnSuccessCallback = new goog.testing.recordFunction();
    +  waitOnErrorCallback = new goog.testing.recordFunction();
    +}
    +
    +function tearDown() {
    +  result = waitCallback = waitOnSuccessCallback = waitOnErrorCallback = null;
    +  propertyReplacer.reset();
    +}
    +
    +function tearDownPage() {
    +  mockClock.uninstall();
    +}
    +
    +function testSynchronousSuccess() {
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  assertUndefined(result.getValue());
    +
    +  goog.result.wait(result, waitCallback);
    +  goog.result.waitOnSuccess(result, waitOnSuccessCallback);
    +  goog.result.waitOnError(result, waitOnErrorCallback);
    +
    +  result.setValue(1);
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +
    +  assertWaitCall(waitCallback, result);
    +  assertCall(waitOnSuccessCallback, 1, result);
    +  assertNoCall(waitOnErrorCallback);
    +}
    +
    +function testAsynchronousSuccess() {
    +  goog.result.wait(result, waitCallback);
    +  goog.result.waitOnSuccess(result, waitOnSuccessCallback);
    +  goog.result.waitOnError(result, waitOnErrorCallback);
    +
    +  goog.Timer.callOnce(function() {
    +    result.setValue(1);
    +  });
    +
    +  assertUndefined(result.getValue());
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +
    +  assertNoCall(waitCallback);
    +  assertNoCall(waitOnSuccessCallback);
    +  assertNoCall(waitOnErrorCallback);
    +
    +  mockClock.tick();
    +
    +  assertEquals(goog.result.Result.State.SUCCESS, result.getState());
    +  assertEquals(1, result.getValue());
    +
    +  assertWaitCall(waitCallback, result);
    +  assertCall(waitOnSuccessCallback, 1, result);
    +  assertNoCall(waitOnErrorCallback);
    +}
    +
    +function testSynchronousError() {
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  assertUndefined(result.getValue());
    +
    +  goog.result.wait(result, waitCallback);
    +  goog.result.waitOnSuccess(result, waitOnSuccessCallback);
    +  goog.result.waitOnError(result, waitOnErrorCallback);
    +
    +  result.setError();
    +
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertUndefined(result.getValue());
    +
    +  assertWaitCall(waitCallback, result);
    +  assertNoCall(waitOnSuccessCallback);
    +  assertCall(waitOnErrorCallback, undefined, result);
    +}
    +
    +function testAsynchronousError() {
    +  goog.result.wait(result, waitCallback);
    +  goog.result.waitOnSuccess(result, waitOnSuccessCallback);
    +  goog.result.waitOnError(result, waitOnErrorCallback);
    +
    +  goog.Timer.callOnce(function() {
    +    result.setError();
    +  });
    +
    +  assertEquals(goog.result.Result.State.PENDING, result.getState());
    +  assertUndefined(result.getValue());
    +
    +  assertNoCall(waitCallback);
    +  assertNoCall(waitOnSuccessCallback);
    +  assertNoCall(waitOnErrorCallback);
    +
    +  mockClock.tick();
    +
    +  assertEquals(goog.result.Result.State.ERROR, result.getState());
    +  assertUndefined(result.getValue());
    +
    +  assertWaitCall(waitCallback, result);
    +  assertNoCall(waitOnSuccessCallback);
    +  assertCall(waitOnErrorCallback, undefined, result);
    +}
    +
    +function testCustomScope() {
    +  var scope = {};
    +  goog.result.wait(result, waitCallback, scope);
    +  result.setValue(1);
    +  assertEquals(scope, waitCallback.popLastCall().getThis());
    +}
    +
    +function testDefaultScope() {
    +  goog.result.wait(result, waitCallback);
    +  result.setValue(1);
    +  assertEquals(goog.global, waitCallback.popLastCall().getThis());
    +}
    +
    +function testOnSuccessScope() {
    +  var scope = {};
    +  goog.result.waitOnSuccess(result, waitOnSuccessCallback, scope);
    +  result.setValue(1);
    +  assertCall(waitOnSuccessCallback, 1, result, scope);
    +}
    +
    +function testOnErrorScope() {
    +  var scope = {};
    +  goog.result.waitOnError(result, waitOnErrorCallback, scope);
    +  result.setError();
    +  assertCall(waitOnErrorCallback, undefined, result, scope);
    +}
    +
    +/**
    + * Assert that a callback function stubbed out with goog.recordFunction was
    + * called with the expected arguments by goog.result.waitOnSuccess/Error.
    + * @param {Function} recordedFunction The callback function.
    + * @param {?} value The value stored in the result.
    + * @param {!goog.result.Result} result The result that was resolved to SUCCESS
    + *     or ERROR.
    + * @param {Object=} opt_scope Optional scope that the test function should be
    + *     called in. By default, it is goog.global.
    + */
    +function assertCall(recordedFunction, value, result, opt_scope) {
    +  var scope = opt_scope || goog.global;
    +  assertEquals(1, recordedFunction.getCallCount());
    +  var call = recordedFunction.popLastCall();
    +  assertEquals(2, call.getArguments().length);
    +  assertEquals(value, call.getArgument(0));
    +  assertEquals(result, call.getArgument(1));
    +  assertEquals(scope, call.getThis());
    +}
    +
    +/**
    + * Assert that a callback function stubbed out with goog.recordFunction was
    + * called with the expected arguments by goog.result.wait.
    + * @param {Function} recordedFunction The callback function.
    + * @param {!goog.result.Result} result The result that was resolved to SUCCESS
    + *     or ERROR.
    + * @param {Object=} opt_scope Optional scope that the test function should be
    + *     called in. By default, it is goog.global.
    + */
    +function assertWaitCall(recordedFunction, result, opt_scope) {
    +  var scope = opt_scope || goog.global;
    +  assertEquals(1, recordedFunction.getCallCount());
    +  var call = recordedFunction.popLastCall();
    +  assertEquals(1, call.getArguments().length);
    +  assertEquals(result, call.getArgument(0));
    +  assertEquals(scope, call.getThis());
    +}
    +
    +function assertNoCall(recordedFunction) {
    +  assertEquals(0, recordedFunction.getCallCount());
    +}
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/data.js b/mapbox-gl/closure-library/closure/goog/soy/data.js
    new file mode 100644
    index 0000000000..f2b00d2a41
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/data.js
    @@ -0,0 +1,160 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Soy data primitives.
    + *
    + * The goal is to encompass data types used by Soy, especially to mark content
    + * as known to be "safe".
    + *
    + * @author gboyer@google.com (Garrett Boyer)
    + */
    +
    +goog.provide('goog.soy.data.SanitizedContent');
    +goog.provide('goog.soy.data.SanitizedContentKind');
    +
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.uncheckedconversions');
    +goog.require('goog.string.Const');
    +
    +
    +/**
    + * A type of textual content.
    + *
    + * This is an enum of type Object so that these values are unforgeable.
    + *
    + * @enum {!Object}
    + */
    +goog.soy.data.SanitizedContentKind = {
    +
    +  /**
    +   * A snippet of HTML that does not start or end inside a tag, comment, entity,
    +   * or DOCTYPE; and that does not contain any executable code
    +   * (JS, {@code <object>}s, etc.) from a different trust domain.
    +   */
    +  HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},
    +
    +  /**
    +   * Executable Javascript code or expression, safe for insertion in a
    +   * script-tag or event handler context, known to be free of any
    +   * attacker-controlled scripts. This can either be side-effect-free
    +   * Javascript (such as JSON) or Javascript that's entirely under Google's
    +   * control.
    +   */
    +  JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},
    +
    +  /** A properly encoded portion of a URI. */
    +  URI: goog.DEBUG ? {sanitizedContentUri: true} : {},
    +
    +  /**
    +   * Repeated attribute names and values. For example,
    +   * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
    +   */
    +  ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
    +
    +  // TODO: Consider separating rules, declarations, and values into
    +  // separate types, but for simplicity, we'll treat explicitly blessed
    +  // SanitizedContent as allowed in all of these contexts.
    +  /**
    +   * A CSS3 declaration, property, value or group of semicolon separated
    +   * declarations.
    +   */
    +  CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},
    +
    +  /**
    +   * Unsanitized plain-text content.
    +   *
    +   * This is effectively the "null" entry of this enum, and is sometimes used
    +   * to explicitly mark content that should never be used unescaped. Since any
    +   * string is safe to use as text, being of ContentKind.TEXT makes no
    +   * guarantees about its safety in any other context such as HTML.
    +   */
    +  TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}
    +};
    +
    +
    +
    +/**
    + * A string-like object that carries a content-type and a content direction.
    + *
    + * IMPORTANT! Do not create these directly, nor instantiate the subclasses.
    + * Instead, use a trusted, centrally reviewed library as endorsed by your team
    + * to generate these objects. Otherwise, you risk accidentally creating
    + * SanitizedContent that is attacker-controlled and gets evaluated unescaped in
    + * templates.
    + *
    + * @constructor
    + */
    +goog.soy.data.SanitizedContent = function() {
    +  throw Error('Do not instantiate directly');
    +};
    +
    +
    +/**
    + * The context in which this content is safe from XSS attacks.
    + * @type {goog.soy.data.SanitizedContentKind}
    + */
    +goog.soy.data.SanitizedContent.prototype.contentKind;
    +
    +
    +/**
    + * The content's direction; null if unknown and thus to be estimated when
    + * necessary.
    + * @type {?goog.i18n.bidi.Dir}
    + */
    +goog.soy.data.SanitizedContent.prototype.contentDir = null;
    +
    +
    +/**
    + * The already-safe content.
    + * @protected {string}
    + */
    +goog.soy.data.SanitizedContent.prototype.content;
    +
    +
    +/**
    + * Gets the already-safe content.
    + * @return {string}
    + */
    +goog.soy.data.SanitizedContent.prototype.getContent = function() {
    +  return this.content;
    +};
    +
    +
    +/** @override */
    +goog.soy.data.SanitizedContent.prototype.toString = function() {
    +  return this.content;
    +};
    +
    +
    +/**
    + * Converts sanitized content of kind TEXT or HTML into SafeHtml. HTML content
    + * is converted without modification, while text content is HTML-escaped.
    + * @return {!goog.html.SafeHtml}
    + * @throws {Error} when the content kind is not TEXT or HTML.
    + */
    +goog.soy.data.SanitizedContent.prototype.toSafeHtml = function() {
    +  if (this.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {
    +    return goog.html.SafeHtml.htmlEscape(this.toString());
    +  }
    +  if (this.contentKind !== goog.soy.data.SanitizedContentKind.HTML) {
    +    throw Error('Sanitized content was not of kind TEXT or HTML.');
    +  }
    +  return goog.html.uncheckedconversions.
    +      safeHtmlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from(
    +              'Soy SanitizedContent of kind HTML produces ' +
    +                  'SafeHtml-contract-compliant value.'),
    +          this.toString(), this.contentDir);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/data_test.html b/mapbox-gl/closure-library/closure/goog/soy/data_test.html
    new file mode 100644
    index 0000000000..0f1ac73318
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/data_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.soy.data
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.soy.dataTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/data_test.js b/mapbox-gl/closure-library/closure/goog/soy/data_test.js
    new file mode 100644
    index 0000000000..af14ae6e31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/data_test.js
    @@ -0,0 +1,33 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.soy.dataTest');
    +goog.setTestOnly('goog.soy.dataTest');
    +
    +goog.require('goog.html.SafeHtml');
    +/** @suppress {extraRequire} */
    +goog.require('goog.soy.testHelper');
    +goog.require('goog.testing.jsunit');
    +
    +
    +function testToSafeHtml() {
    +  var html;
    +
    +  html = example.unsanitizedTextTemplate().toSafeHtml();
    +  assertEquals('I &lt;3 Puppies &amp; Kittens',
    +      goog.html.SafeHtml.unwrap(html));
    +
    +  html = example.sanitizedHtmlTemplate().toSafeHtml();
    +  assertEquals('Hello <b>World</b>', goog.html.SafeHtml.unwrap(html));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/renderer.js b/mapbox-gl/closure-library/closure/goog/soy/renderer.js
    new file mode 100644
    index 0000000000..359be20c02
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/renderer.js
    @@ -0,0 +1,314 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a soy renderer that allows registration of
    + * injected data ("globals") that will be passed into the rendered
    + * templates.
    + *
    + * There is also an interface {@link goog.soy.InjectedDataSupplier} that
    + * user should implement to provide the injected data for a specific
    + * application. The injected data format is a JavaScript object:
    + * <pre>
    + * {'dataKey': 'value', 'otherDataKey': 'otherValue'}
    + * </pre>
    + *
    + * To use injected data, you need to enable the soy-to-js compiler
    + * option {@code --isUsingIjData}. The injected data can then be
    + * referred to in any soy templates as part of a magic "ij"
    + * parameter. For example, {@code $ij.dataKey} will evaluate to
    + * 'value' with the above injected data.
    + *
    + * @author henrywong@google.com (Henry Wong)
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.soy.InjectedDataSupplier');
    +goog.provide('goog.soy.Renderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.soy');
    +goog.require('goog.soy.data.SanitizedContent');
    +goog.require('goog.soy.data.SanitizedContentKind');
    +
    +
    +
    +/**
    + * Creates a new soy renderer. Note that the renderer will only be
    + * guaranteed to work correctly within the document scope provided in
    + * the DOM helper.
    + *
    + * @param {goog.soy.InjectedDataSupplier=} opt_injectedDataSupplier A supplier
    + *     that provides an injected data.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper;
    + *     defaults to that provided by {@code goog.dom.getDomHelper()}.
    + * @constructor
    + */
    +goog.soy.Renderer = function(opt_injectedDataSupplier, opt_domHelper) {
    +  /**
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * @type {goog.soy.InjectedDataSupplier}
    +   * @private
    +   */
    +  this.supplier_ = opt_injectedDataSupplier || null;
    +
    +  /**
    +   * Map from template name to the data used to render that template.
    +   * @type {!goog.soy.Renderer.SavedTemplateRender}
    +   * @private
    +   */
    +  this.savedTemplateRenders_ = [];
    +};
    +
    +
    +/**
    + * @typedef {Array<{template: string, data: Object, ijData: Object}>}
    + */
    +goog.soy.Renderer.SavedTemplateRender;
    +
    +
    +/**
    + * Renders a Soy template into a single node or a document fragment.
    + * Delegates to {@code goog.soy.renderAsFragment}.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @return {!Node} The resulting node or document fragment.
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.renderAsFragment = function(template,
    +                                                        opt_templateData) {
    +  this.saveTemplateRender_(template, opt_templateData);
    +  var node = goog.soy.renderAsFragment(template, opt_templateData,
    +                                       this.getInjectedData_(), this.dom_);
    +  this.handleRender(node);
    +  return node;
    +};
    +
    +
    +/**
    + * Renders a Soy template into a single node. If the rendered HTML
    + * string represents a single node, then that node is returned.
    + * Otherwise, a DIV element is returned containing the rendered nodes.
    + * Delegates to {@code goog.soy.renderAsElement}.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @return {!Element} Rendered template contents, wrapped in a parent DIV
    + *     element if necessary.
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.renderAsElement = function(template,
    +                                                       opt_templateData) {
    +  this.saveTemplateRender_(template, opt_templateData);
    +  var element = goog.soy.renderAsElement(template, opt_templateData,
    +                                         this.getInjectedData_(), this.dom_);
    +  this.handleRender(element);
    +  return element;
    +};
    +
    +
    +/**
    + * Renders a Soy template and then set the output string as the
    + * innerHTML of the given element. Delegates to {@code goog.soy.renderElement}.
    + *
    + * @param {Element} element The element whose content we are rendering.
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.renderElement = function(element, template,
    +                                                     opt_templateData) {
    +  this.saveTemplateRender_(template, opt_templateData);
    +  goog.soy.renderElement(
    +      element, template, opt_templateData, this.getInjectedData_());
    +  this.handleRender(element);
    +};
    +
    +
    +/**
    + * Renders a Soy template and returns the output string.
    + * If the template is strict, it must be of kind HTML. To render strict
    + * templates of other kinds, use {@code renderText} (for {@code kind="text"}) or
    + * {@code renderStrict}.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template to render.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @return {string} The return value of rendering the template directly.
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.render = function(template, opt_templateData) {
    +  var result = template(
    +      opt_templateData || {}, undefined, this.getInjectedData_());
    +  goog.asserts.assert(!(result instanceof goog.soy.data.SanitizedContent) ||
    +      result.contentKind === goog.soy.data.SanitizedContentKind.HTML,
    +      'render was called with a strict template of kind other than "html"' +
    +          ' (consider using renderText or renderStrict)');
    +  this.saveTemplateRender_(template, opt_templateData);
    +  this.handleRender();
    +  return String(result);
    +};
    +
    +
    +/**
    + * Renders a strict Soy template of kind="text" and returns the output string.
    + * It is an error to use renderText on non-strict templates, or strict templates
    + * of kinds other than "text".
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):
    + *     goog.soy.data.SanitizedContent} template The Soy template to render.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @return {string} The return value of rendering the template directly.
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.renderText = function(template, opt_templateData) {
    +  var result = template(
    +      opt_templateData || {}, undefined, this.getInjectedData_());
    +  goog.asserts.assertInstanceof(result, goog.soy.data.SanitizedContent,
    +      'renderText cannot be called on a non-strict soy template');
    +  goog.asserts.assert(
    +      result.contentKind === goog.soy.data.SanitizedContentKind.TEXT,
    +      'renderText was called with a template of kind other than "text"');
    +  this.saveTemplateRender_(template, opt_templateData);
    +  this.handleRender();
    +  return String(result);
    +};
    +
    +
    +/**
    + * Renders a strict Soy template and returns the output SanitizedContent object.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):RETURN_TYPE}
    + *     template The Soy template to render.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @param {goog.soy.data.SanitizedContentKind=} opt_kind The output kind to
    + *     assert. If null, the template must be of kind="html" (i.e., opt_kind
    + *     defaults to goog.soy.data.SanitizedContentKind.HTML).
    + * @return {RETURN_TYPE} The SanitizedContent object. This return type is
    + *     generic based on the return type of the template, such as
    + *     soy.SanitizedHtml.
    + * @template ARG_TYPES, RETURN_TYPE
    + */
    +goog.soy.Renderer.prototype.renderStrict = function(
    +    template, opt_templateData, opt_kind) {
    +  var result = template(
    +      opt_templateData || {}, undefined, this.getInjectedData_());
    +  goog.asserts.assertInstanceof(result, goog.soy.data.SanitizedContent,
    +      'renderStrict cannot be called on a non-strict soy template');
    +  goog.asserts.assert(
    +      result.contentKind ===
    +          (opt_kind || goog.soy.data.SanitizedContentKind.HTML),
    +      'renderStrict was called with the wrong kind of template');
    +  this.saveTemplateRender_(template, opt_templateData);
    +  this.handleRender();
    +  return result;
    +};
    +
    +
    +/**
    + * Renders a strict Soy template of kind="html" and returns the result as
    + * a goog.html.SafeHtml object.
    + *
    + * Rendering a template that is not a strict template of kind="html" results in
    + * a runtime error.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):
    + *     goog.soy.data.SanitizedContent} template The Soy template to render.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @return {!goog.html.SafeHtml}
    + * @template ARG_TYPES
    + */
    +goog.soy.Renderer.prototype.renderSafeHtml = function(
    +    template, opt_templateData) {
    +  var result = this.renderStrict(template, opt_templateData);
    +  return result.toSafeHtml();
    +};
    +
    +
    +/**
    + * @return {!goog.soy.Renderer.SavedTemplateRender} Saved template data for
    + *     the renders that have happened so far.
    + */
    +goog.soy.Renderer.prototype.getSavedTemplateRenders = function() {
    +  return this.savedTemplateRenders_;
    +};
    +
    +
    +/**
    + * Observes rendering of templates by this renderer.
    + * @param {Node=} opt_node Relevant node, if available. The node may or may
    + *     not be in the document, depending on whether Soy is creating an element
    + *     or writing into an existing one.
    + * @protected
    + */
    +goog.soy.Renderer.prototype.handleRender = goog.nullFunction;
    +
    +
    +/**
    + * Saves information about the current template render for debug purposes.
    + * @param {Function} template The Soy template defining the element's content.
    + * @param {Object=} opt_templateData The data for the template.
    + * @private
    + * @suppress {missingProperties} SoyJs compiler adds soyTemplateName to the
    + *     template.
    + */
    +goog.soy.Renderer.prototype.saveTemplateRender_ = function(
    +    template, opt_templateData) {
    +  if (goog.DEBUG) {
    +    this.savedTemplateRenders_.push({
    +      template: template.soyTemplateName,
    +      data: opt_templateData,
    +      ijData: this.getInjectedData_()
    +    });
    +  }
    +};
    +
    +
    +/**
    + * Creates the injectedParams map if necessary and calls the configuration
    + * service to prepopulate it.
    + * @return {Object} The injected params.
    + * @private
    + */
    +goog.soy.Renderer.prototype.getInjectedData_ = function() {
    +  return this.supplier_ ? this.supplier_.getData() : {};
    +};
    +
    +
    +
    +/**
    + * An interface for a supplier that provides Soy injected data.
    + * @interface
    + */
    +goog.soy.InjectedDataSupplier = function() {};
    +
    +
    +/**
    + * Gets the injected data. Implementation may assume that
    + * {@code goog.soy.Renderer} will treat the returned data as
    + * immutable.  The renderer will call this every time one of its
    + * {@code render*} methods is called.
    + * @return {Object} A key-value pair representing the injected data.
    + */
    +goog.soy.InjectedDataSupplier.prototype.getData = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/renderer_test.html b/mapbox-gl/closure-library/closure/goog/soy/renderer_test.html
    new file mode 100644
    index 0000000000..7475a8dcb0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/renderer_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.soy.Renderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.soy.RendererTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/renderer_test.js b/mapbox-gl/closure-library/closure/goog/soy/renderer_test.js
    new file mode 100644
    index 0000000000..1ef8d6acc6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/renderer_test.js
    @@ -0,0 +1,209 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.soy.RendererTest');
    +goog.setTestOnly('goog.soy.RendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.soy.Renderer');
    +goog.require('goog.soy.data.SanitizedContentKind');
    +/** @suppress {extraRequire} */
    +goog.require('goog.soy.testHelper');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var handleRender;
    +
    +
    +function setUp() {
    +  // Replace the empty default implementation.
    +  handleRender = goog.soy.Renderer.prototype.handleRender =
    +      goog.testing.recordFunction(goog.soy.Renderer.prototype.handleRender);
    +}
    +
    +
    +var dataSupplier = {
    +  getData: function() {
    +    return {name: 'IjValue'};
    +  }
    +};
    +
    +
    +function testRenderElement() {
    +  var testDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    +
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  renderer.renderElement(
    +      testDiv, example.injectedDataTemplate, {name: 'Value'});
    +  assertEquals('ValueIjValue', elementToInnerHtml(testDiv));
    +  assertEquals(testDiv, handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderElementWithNoTemplateData() {
    +  var testDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    +
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  renderer.renderElement(testDiv, example.noDataTemplate);
    +  assertEquals('<div>Hello</div>', elementToInnerHtml(testDiv));
    +  assertEquals(testDiv, handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderAsFragment() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var fragment = renderer.renderAsFragment(
    +      example.injectedDataTemplate, {name: 'Value'});
    +  assertEquals('ValueIjValue', fragmentToHtml(fragment));
    +  assertEquals(fragment, handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderAsFragmentWithNoTemplateData() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var fragment = renderer.renderAsFragment(example.noDataTemplate);
    +  assertEquals(goog.dom.NodeType.ELEMENT, fragment.nodeType);
    +  assertEquals('<div>Hello</div>', fragmentToHtml(fragment));
    +  assertEquals(fragment, handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderAsElement() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var element = renderer.renderAsElement(
    +      example.injectedDataTemplate, {name: 'Value'});
    +  assertEquals('ValueIjValue', elementToInnerHtml(element));
    +  assertEquals(element, handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderAsElementWithNoTemplateData() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var elem = renderer.renderAsElement(example.noDataTemplate);
    +  assertEquals('Hello', elementToInnerHtml(elem));
    +  assertEquals(elem, handleRender.getLastCall().getArguments()[0]);
    +}
    +
    +
    +function testRenderConvertsToString() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  assertEquals('Output should be a string',
    +      'Hello <b>World</b>', renderer.render(example.sanitizedHtmlTemplate));
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderRejectsNonHtmlStrictTemplates() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'render was called with a strict template of kind other than "html"' +
    +      ' (consider using renderText or renderStrict)',
    +      assertThrows(function() {
    +        renderer.render(example.unsanitizedTextTemplate, {});
    +      }).message);
    +  handleRender.assertCallCount(0);
    +}
    +
    +
    +function testRenderStrictDoesNotConvertToString() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var result = renderer.renderStrict(example.sanitizedHtmlTemplate);
    +  assertEquals('Hello <b>World</b>', result.content);
    +  assertEquals(goog.soy.data.SanitizedContentKind.HTML, result.contentKind);
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(1);
    +}
    +
    +
    +function testRenderStrictValidatesOutput() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  // Passes.
    +  renderer.renderStrict(example.sanitizedHtmlTemplate, {});
    +  // No SanitizedContent at all.
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'renderStrict cannot be called on a non-strict soy template',
    +      assertThrows(function() {
    +        renderer.renderStrict(example.noDataTemplate, {});
    +      }).message);
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +  // Passes.
    +  renderer.renderStrict(example.sanitizedHtmlTemplate, {},
    +      goog.soy.data.SanitizedContentKind.HTML);
    +  // Wrong content kind.
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'renderStrict was called with the wrong kind of template',
    +      assertThrows(function() {
    +        renderer.renderStrict(example.sanitizedHtmlTemplate, {},
    +            goog.soy.data.SanitizedContentKind.JS);
    +      }).message);
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +
    +  // renderStrict's opt_kind parameter defaults to SanitizedContentKind.HTML:
    +  // Passes.
    +  renderer.renderStrict(example.sanitizedHtmlTemplate, {});
    +  // Rendering non-HTML template fails:
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'renderStrict was called with the wrong kind of template',
    +      assertThrows(function() {
    +        renderer.renderStrict(example.unsanitizedTextTemplate, {});
    +      }).message);
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +  handleRender.assertCallCount(3);
    +}
    +
    +
    +function testRenderText() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  // RenderText converts to string.
    +  assertEquals('Output of renderText should be a string',
    +      'I <3 Puppies & Kittens',
    +      renderer.renderText(example.unsanitizedTextTemplate));
    +  assertUndefined(handleRender.getLastCall().getArguments()[0]);
    +  // RenderText on non-strict template fails.
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'renderText cannot be called on a non-strict soy template',
    +      assertThrows(function() {
    +        renderer.renderText(example.noDataTemplate, {});
    +      }).message);
    +  // RenderText on non-text template fails.
    +  assertEquals(
    +      'Assertion failed: ' +
    +      'renderText was called with a template of kind other than "text"',
    +      assertThrows(function() {
    +        renderer.renderText(example.sanitizedHtmlTemplate, {});
    +      }).message);
    +  handleRender.assertCallCount(1);
    +}
    +
    +function testRenderSafeHtml() {
    +  var renderer = new goog.soy.Renderer(dataSupplier);
    +  var result = renderer.renderSafeHtml(example.sanitizedHtmlTemplate);
    +  assertEquals('Hello <b>World</b>', goog.html.SafeHtml.unwrap(result));
    +  assertEquals(goog.i18n.bidi.Dir.LTR, result.getDirection());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/soy.js b/mapbox-gl/closure-library/closure/goog/soy/soy.js
    new file mode 100644
    index 0000000000..65b9aadaee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/soy.js
    @@ -0,0 +1,218 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides utility methods to render soy template.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.soy');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.soy.data.SanitizedContent');
    +goog.require('goog.soy.data.SanitizedContentKind');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @define {boolean} Whether to require all Soy templates to be "strict html".
    + * Soy templates that use strict autoescaping forbid noAutoescape along with
    + * many dangerous directives, and return a runtime type SanitizedContent that
    + * marks them as safe.
    + *
    + * If this flag is enabled, Soy templates will fail to render if a template
    + * returns plain text -- indicating it is a non-strict template.
    + */
    +goog.define('goog.soy.REQUIRE_STRICT_AUTOESCAPE', false);
    +
    +
    +/**
    + * Renders a Soy template and then set the output string as
    + * the innerHTML of an element. It is recommended to use this helper function
    + * instead of directly setting innerHTML in your hand-written code, so that it
    + * will be easier to audit the code for cross-site scripting vulnerabilities.
    + *
    + * @param {Element} element The element whose content we are rendering into.
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @param {Object=} opt_injectedData The injected data for the template.
    + * @template ARG_TYPES
    + */
    +goog.soy.renderElement = function(element, template, opt_templateData,
    +                                  opt_injectedData) {
    +  // Soy template parameter is only nullable for historical reasons.
    +  goog.asserts.assert(template, 'Soy template may not be null.');
    +  element.innerHTML = goog.soy.ensureTemplateOutputHtml_(template(
    +      opt_templateData || goog.soy.defaultTemplateData_, undefined,
    +      opt_injectedData));
    +};
    +
    +
    +/**
    + * Renders a Soy template into a single node or a document
    + * fragment. If the rendered HTML string represents a single node, then that
    + * node is returned (note that this is *not* a fragment, despite them name of
    + * the method). Otherwise a document fragment is returned containing the
    + * rendered nodes.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @param {Object=} opt_injectedData The injected data for the template.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
    + *     create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
    + * @return {!Node} The resulting node or document fragment.
    + * @template ARG_TYPES
    + */
    +goog.soy.renderAsFragment = function(template, opt_templateData,
    +                                     opt_injectedData, opt_domHelper) {
    +  // Soy template parameter is only nullable for historical reasons.
    +  goog.asserts.assert(template, 'Soy template may not be null.');
    +  var dom = opt_domHelper || goog.dom.getDomHelper();
    +  var html = goog.soy.ensureTemplateOutputHtml_(
    +      template(opt_templateData || goog.soy.defaultTemplateData_,
    +               undefined, opt_injectedData));
    +  goog.soy.assertFirstTagValid_(html);
    +  return dom.htmlToDocumentFragment(html);
    +};
    +
    +
    +/**
    + * Renders a Soy template into a single node. If the rendered
    + * HTML string represents a single node, then that node is returned. Otherwise,
    + * a DIV element is returned containing the rendered nodes.
    + *
    + * @param {null|function(ARG_TYPES, null=, Object<string, *>=):*} template
    + *     The Soy template defining the element's content.
    + * @param {ARG_TYPES=} opt_templateData The data for the template.
    + * @param {Object=} opt_injectedData The injected data for the template.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
    + *     create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
    + * @return {!Element} Rendered template contents, wrapped in a parent DIV
    + *     element if necessary.
    + * @template ARG_TYPES
    + */
    +goog.soy.renderAsElement = function(template, opt_templateData,
    +                                    opt_injectedData, opt_domHelper) {
    +  // Soy template parameter is only nullable for historical reasons.
    +  goog.asserts.assert(template, 'Soy template may not be null.');
    +  var dom = opt_domHelper || goog.dom.getDomHelper();
    +  var wrapper = dom.createElement(goog.dom.TagName.DIV);
    +  var html = goog.soy.ensureTemplateOutputHtml_(template(
    +      opt_templateData || goog.soy.defaultTemplateData_,
    +      undefined, opt_injectedData));
    +  goog.soy.assertFirstTagValid_(html);
    +  wrapper.innerHTML = html;
    +
    +  // If the template renders as a single element, return it.
    +  if (wrapper.childNodes.length == 1) {
    +    var firstChild = wrapper.firstChild;
    +    if (firstChild.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return /** @type {!Element} */ (firstChild);
    +    }
    +  }
    +
    +  // Otherwise, return the wrapper DIV.
    +  return wrapper;
    +};
    +
    +
    +/**
    + * Ensures the result is "safe" to insert as HTML.
    + *
    + * Note if the template has non-strict autoescape, the guarantees here are very
    + * weak. It is recommended applications switch to requiring strict
    + * autoescaping over time by tweaking goog.soy.REQUIRE_STRICT_AUTOESCAPE.
    + *
    + * In the case the argument is a SanitizedContent object, it either must
    + * already be of kind HTML, or if it is kind="text", the output will be HTML
    + * escaped.
    + *
    + * @param {*} templateResult The template result.
    + * @return {string} The assumed-safe HTML output string.
    + * @private
    + */
    +goog.soy.ensureTemplateOutputHtml_ = function(templateResult) {
    +  // Allow strings as long as strict autoescaping is not mandated. Note we
    +  // allow everything that isn't an object, because some non-escaping templates
    +  // end up returning non-strings if their only print statement is a
    +  // non-escaped argument, plus some unit tests spoof templates.
    +  // TODO(gboyer): Track down and fix these cases.
    +  if (!goog.soy.REQUIRE_STRICT_AUTOESCAPE && !goog.isObject(templateResult)) {
    +    return String(templateResult);
    +  }
    +
    +  // Allow SanitizedContent of kind HTML.
    +  if (templateResult instanceof goog.soy.data.SanitizedContent) {
    +    templateResult = /** @type {!goog.soy.data.SanitizedContent} */ (
    +        templateResult);
    +    var ContentKind = goog.soy.data.SanitizedContentKind;
    +    if (templateResult.contentKind === ContentKind.HTML) {
    +      return goog.asserts.assertString(templateResult.getContent());
    +    }
    +    if (templateResult.contentKind === ContentKind.TEXT) {
    +      // Allow text to be rendered, as long as we escape it. Other content
    +      // kinds will fail, since we don't know what to do with them.
    +      // TODO(gboyer): Perhaps also include URI in this case.
    +      return goog.string.htmlEscape(templateResult.getContent());
    +    }
    +  }
    +
    +  goog.asserts.fail('Soy template output is unsafe for use as HTML: ' +
    +      templateResult);
    +
    +  // In production, return a safe string, rather than failing hard.
    +  return 'zSoyz';
    +};
    +
    +
    +/**
    + * Checks that the rendered HTML does not start with an invalid tag that would
    + * likely cause unexpected output from renderAsElement or renderAsFragment.
    + * See {@link http://www.w3.org/TR/html5/semantics.html#semantics} for reference
    + * as to which HTML elements can be parents of each other.
    + * @param {string} html The output of a template.
    + * @private
    + */
    +goog.soy.assertFirstTagValid_ = function(html) {
    +  if (goog.asserts.ENABLE_ASSERTS) {
    +    var matches = html.match(goog.soy.INVALID_TAG_TO_RENDER_);
    +    goog.asserts.assert(!matches, 'This template starts with a %s, which ' +
    +        'cannot be a child of a <div>, as required by soy internals. ' +
    +        'Consider using goog.soy.renderElement instead.\nTemplate output: %s',
    +        matches && matches[0], html);
    +  }
    +};
    +
    +
    +/**
    + * A pattern to find templates that cannot be rendered by renderAsElement or
    + * renderAsFragment, as these elements cannot exist as the child of a <div>.
    + * @type {!RegExp}
    + * @private
    + */
    +goog.soy.INVALID_TAG_TO_RENDER_ =
    +    /^<(body|caption|col|colgroup|head|html|tr|td|tbody|thead|tfoot)>/i;
    +
    +
    +/**
    + * Immutable object that is passed into templates that are rendered
    + * without any data.
    + * @private @const
    + */
    +goog.soy.defaultTemplateData_ = {};
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/soy_test.html b/mapbox-gl/closure-library/closure/goog/soy/soy_test.html
    new file mode 100644
    index 0000000000..67faae1d44
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/soy_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.soy
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.soyTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/soy_test.js b/mapbox-gl/closure-library/closure/goog/soy/soy_test.js
    new file mode 100644
    index 0000000000..934e45531b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/soy_test.js
    @@ -0,0 +1,225 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.soyTest');
    +goog.setTestOnly('goog.soyTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.functions');
    +goog.require('goog.soy');
    +/** @suppress {extraRequire} */
    +goog.require('goog.soy.testHelper');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +var stubs;
    +
    +function setUp() {
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testRenderElement() {
    +  var testDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    +  goog.soy.renderElement(testDiv, example.multiRootTemplate, {name: 'Boo'});
    +  assertEquals('<div>Hello</div><div>Boo</div>', elementToInnerHtml(testDiv));
    +}
    +
    +
    +function testRenderElementWithNoTemplateData() {
    +  var testDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    +  goog.soy.renderElement(testDiv, example.noDataTemplate);
    +  assertEquals('<div>Hello</div>', elementToInnerHtml(testDiv));
    +}
    +
    +
    +function testRenderAsFragmentTextNode() {
    +  var fragment = goog.soy.renderAsFragment(
    +      example.textNodeTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.TEXT, fragment.nodeType);
    +  assertEquals('Boo', fragmentToHtml(fragment));
    +}
    +
    +
    +function testRenderAsFragmentInjectedData() {
    +  var fragment = goog.soy.renderAsFragment(example.injectedDataTemplate,
    +      {name: 'Boo'}, {name: 'ijBoo'});
    +  assertEquals(goog.dom.NodeType.TEXT, fragment.nodeType);
    +  assertEquals('BooijBoo', fragmentToHtml(fragment));
    +}
    +
    +
    +function testRenderAsFragmentSingleRoot() {
    +  var fragment = goog.soy.renderAsFragment(
    +      example.singleRootTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.ELEMENT, fragment.nodeType);
    +  assertEquals(goog.dom.TagName.SPAN, fragment.tagName);
    +  assertEquals('Boo', fragment.innerHTML);
    +}
    +
    +
    +function testRenderAsFragmentMultiRoot() {
    +  var fragment = goog.soy.renderAsFragment(
    +      example.multiRootTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.DOCUMENT_FRAGMENT, fragment.nodeType);
    +  assertEquals('<div>Hello</div><div>Boo</div>', fragmentToHtml(fragment));
    +}
    +
    +
    +function testRenderAsFragmentNoData() {
    +  var fragment = goog.soy.renderAsFragment(example.noDataTemplate);
    +  assertEquals(goog.dom.NodeType.ELEMENT, fragment.nodeType);
    +  assertEquals('<div>Hello</div>', fragmentToHtml(fragment));
    +}
    +
    +
    +function testRenderAsElementTextNode() {
    +  var elem = goog.soy.renderAsElement(example.textNodeTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.ELEMENT, elem.nodeType);
    +  assertEquals(goog.dom.TagName.DIV, elem.tagName);
    +  assertEquals('Boo', elementToInnerHtml(elem));
    +}
    +
    +function testRenderAsElementInjectedData() {
    +  var elem = goog.soy.renderAsElement(example.injectedDataTemplate,
    +      {name: 'Boo'}, {name: 'ijBoo'});
    +  assertEquals(goog.dom.NodeType.ELEMENT, elem.nodeType);
    +  assertEquals(goog.dom.TagName.DIV, elem.tagName);
    +  assertEquals('BooijBoo', elementToInnerHtml(elem));
    +}
    +
    +
    +function testRenderAsElementSingleRoot() {
    +  var elem = goog.soy.renderAsElement(
    +      example.singleRootTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.ELEMENT, elem.nodeType);
    +  assertEquals(goog.dom.TagName.SPAN, elem.tagName);
    +  assertEquals('Boo', elementToInnerHtml(elem));
    +}
    +
    +
    +function testRenderAsElementMultiRoot() {
    +  var elem = goog.soy.renderAsElement(example.multiRootTemplate, {name: 'Boo'});
    +  assertEquals(goog.dom.NodeType.ELEMENT, elem.nodeType);
    +  assertEquals(goog.dom.TagName.DIV, elem.tagName);
    +  assertEquals('<div>Hello</div><div>Boo</div>', elementToInnerHtml(elem));
    +}
    +
    +function testRenderAsElementWithNoData() {
    +  var elem = goog.soy.renderAsElement(example.noDataTemplate);
    +  assertEquals('Hello', elementToInnerHtml(elem));
    +}
    +
    +
    +/**
    + * Asserts that the function throws an error for unsafe templates.
    + * @param {Function} function Callback to test.
    + */
    +function assertUnsafeTemplateOutputErrorThrown(func) {
    +  stubs.set(goog.asserts, 'ENABLE_ASSERTS', true);
    +  assertContains('Soy template output is unsafe for use as HTML',
    +      assertThrows(func).message);
    +  stubs.set(goog.asserts, 'ENABLE_ASSERTS', false);
    +  assertEquals('zSoyz', func());
    +}
    +
    +function testAllowButEscapeUnsanitizedText() {
    +  var div = goog.dom.createElement(goog.dom.TagName.DIV);
    +  goog.soy.renderElement(div, example.unsanitizedTextTemplate);
    +  assertEquals('I &lt;3 Puppies &amp; Kittens', div.innerHTML);
    +  var fragment = goog.soy.renderAsFragment(example.unsanitizedTextTemplate);
    +  assertEquals('I <3 Puppies & Kittens', fragment.nodeValue);
    +  assertEquals('I &lt;3 Puppies &amp; Kittens',
    +      goog.soy.renderAsElement(example.unsanitizedTextTemplate).innerHTML);
    +}
    +
    +function testRejectSanitizedCss() {
    +  assertUnsafeTemplateOutputErrorThrown(function() {
    +    goog.soy.renderAsElement(example.sanitizedCssTemplate);
    +  });
    +}
    +
    +function testRejectSanitizedCss() {
    +  assertUnsafeTemplateOutputErrorThrown(function() {
    +    return goog.soy.renderAsElement(
    +        example.templateSpoofingSanitizedContentString).innerHTML;
    +  });
    +}
    +
    +function testRejectStringTemplatesWhenModeIsSet() {
    +  stubs.set(goog.soy, 'REQUIRE_STRICT_AUTOESCAPE', true);
    +  assertUnsafeTemplateOutputErrorThrown(function() {
    +    return goog.soy.renderAsElement(example.noDataTemplate).innerHTML;
    +  });
    +}
    +
    +function testAcceptSanitizedHtml() {
    +  assertEquals('Hello World', goog.dom.getTextContent(
    +      goog.soy.renderAsElement(example.sanitizedHtmlTemplate)));
    +}
    +
    +function testRejectSanitizedHtmlAttributes() {
    +  // Attributes context has nothing to do with html.
    +  assertUnsafeTemplateOutputErrorThrown(function() {
    +    return goog.dom.getTextContent(
    +        goog.soy.renderAsElement(example.sanitizedHtmlAttributesTemplate));
    +  });
    +}
    +
    +function testAcceptNonObject() {
    +  // Some templates, or things that spoof templates in unit tests, might return
    +  // non-strings in unusual cases.
    +  assertEquals('null', goog.dom.getTextContent(
    +      goog.soy.renderAsElement(goog.functions.constant(null))));
    +}
    +
    +function testDebugAssertionWithBadFirstTag() {
    +  try {
    +    goog.soy.renderAsElement(example.tableRowTemplate);
    +    // Expect no exception in production code.
    +    assert(!goog.DEBUG);
    +  } catch (e) {
    +    // Expect exception in debug code.
    +    assert(goog.DEBUG);
    +    // Make sure to let the developer know which tag caused the problem.
    +    assertContains('<tr>', e.message);
    +  }
    +
    +  try {
    +    goog.soy.renderAsFragment(example.tableRowTemplate);
    +    // Expect no exception in production code.
    +    assert(!goog.DEBUG);
    +  } catch (e) {
    +    // Expect exception in debug code.
    +    assert(goog.DEBUG);
    +    // Make sure to let the developer know which tag caused the problem.
    +    assertContains('<tr>', e.message);
    +  }
    +
    +  try {
    +    goog.soy.renderAsElement(example.colGroupTemplateCaps);
    +    // Expect no exception in production code.
    +    assert(!goog.DEBUG);
    +  } catch (e) {
    +    // Expect exception in debug code.
    +    assert(goog.DEBUG);
    +    // Make sure to let the developer know which tag caused the problem.
    +    assertContains('<COLGROUP>', e.message);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/soy/soy_testhelper.js b/mapbox-gl/closure-library/closure/goog/soy/soy_testhelper.js
    new file mode 100644
    index 0000000000..657c6e7791
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/soy/soy_testhelper.js
    @@ -0,0 +1,181 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides test helpers for Soy tests.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.soy.testHelper');
    +goog.setTestOnly('goog.soy.testHelper');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.soy.data.SanitizedContent');
    +goog.require('goog.soy.data.SanitizedContentKind');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Instantiable subclass of SanitizedContent.
    + *
    + * This is a spoof for sanitized content that isn't robust enough to get
    + * through Soy's escaping functions but is good enough for the checks here.
    + *
    + * @constructor
    + * @param {string} content The text.
    + * @param {goog.soy.data.SanitizedContentKind} kind The kind of safe content.
    + * @extends {goog.soy.data.SanitizedContent}
    + */
    +function SanitizedContentSubclass(content, kind) {
    +  // IMPORTANT! No superclass chaining to avoid exception being thrown.
    +  this.content = content;
    +  this.contentKind = kind;
    +}
    +goog.inherits(SanitizedContentSubclass, goog.soy.data.SanitizedContent);
    +
    +
    +function makeSanitizedContent(content, kind) {
    +  return new SanitizedContentSubclass(content, kind);
    +}
    +
    +
    +
    +//
    +// Fake Soy-generated template functions.
    +//
    +
    +var example = {};
    +
    +
    +example.textNodeTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  assertNotNull(opt_data);
    +  assertNotUndefined(opt_data);
    +  return goog.string.htmlEscape(opt_data.name);
    +};
    +
    +
    +example.singleRootTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  assertNotNull(opt_data);
    +  assertNotUndefined(opt_data);
    +  return '<span>' + goog.string.htmlEscape(opt_data.name) + '</span>';
    +};
    +
    +
    +example.multiRootTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  assertNotNull(opt_data);
    +  assertNotUndefined(opt_data);
    +  return '<div>Hello</div><div>' + goog.string.htmlEscape(opt_data.name) +
    +      '</div>';
    +};
    +
    +
    +example.injectedDataTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  assertNotNull(opt_data);
    +  assertNotUndefined(opt_data);
    +  return goog.string.htmlEscape(opt_data.name) +
    +      goog.string.htmlEscape(opt_injectedData.name);
    +};
    +
    +
    +example.noDataTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  assertNotNull(opt_data);
    +  assertNotUndefined(opt_data);
    +  return '<div>Hello</div>';
    +};
    +
    +
    +example.sanitizedHtmlTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  // Test the SanitizedContent constructor.
    +  var sanitized = makeSanitizedContent('Hello <b>World</b>',
    +      goog.soy.data.SanitizedContentKind.HTML);
    +  sanitized.contentDir = goog.i18n.bidi.Dir.LTR;
    +  return sanitized;
    +};
    +
    +
    +example.sanitizedHtmlAttributesTemplate =
    +    function(opt_data, opt_sb, opt_injectedData) {
    +  return makeSanitizedContent('foo="bar"',
    +      goog.soy.data.SanitizedContentKind.ATTRIBUTES);
    +};
    +
    +
    +example.sanitizedCssTemplate =
    +    function(opt_data, opt_sb, opt_injectedData) {
    +  return makeSanitizedContent('display:none',
    +      goog.soy.data.SanitizedContentKind.CSS);
    +};
    +
    +
    +example.unsanitizedTextTemplate =
    +    function(opt_data, opt_sb, opt_injectedData) {
    +  return makeSanitizedContent('I <3 Puppies & Kittens',
    +      goog.soy.data.SanitizedContentKind.TEXT);
    +};
    +
    +
    +example.templateSpoofingSanitizedContentString =
    +    function(opt_data, opt_sb, opt_injectedData) {
    +  return makeSanitizedContent('Hello World',
    +      // This is to ensure we're using triple-equals against a unique Javascript
    +      // object.  For example, in Javascript, consider ({}) == '[Object object]'
    +      // is true.
    +      goog.soy.data.SanitizedContentKind.HTML.toString());
    +};
    +
    +
    +example.tableRowTemplate = function(opt_data, opt_sb, opt_injectedData) {
    +  return '<tr><td></td></tr>';
    +};
    +
    +
    +example.colGroupTemplateCaps = function(opt_data, opt_sb, opt_injectedData) {
    +  return '<COLGROUP></COLGROUP>';
    +};
    +
    +
    +//
    +// Test helper functions.
    +//
    +
    +
    +/**
    + * Retrieves the content of document fragment as HTML.
    + * @param {Node} fragment The document fragment.
    + * @return {string} Content of the document fragment as HTML.
    + */
    +function fragmentToHtml(fragment) {
    +  var testDiv = goog.dom.createElement(goog.dom.TagName.DIV);
    +  testDiv.appendChild(fragment);
    +  return elementToInnerHtml(testDiv);
    +}
    +
    +
    +/**
    + * Retrieves the content of an element as HTML.
    + * @param {Element} elem The element.
    + * @return {string} Content of the element as HTML.
    + */
    +function elementToInnerHtml(elem) {
    +  var innerHtml = elem.innerHTML;
    +  if (goog.userAgent.IE) {
    +    innerHtml = innerHtml.replace(/DIV/g, 'div').replace(/\s/g, '');
    +  }
    +  return innerHtml;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/spell/spellcheck.js b/mapbox-gl/closure-library/closure/goog/spell/spellcheck.js
    new file mode 100644
    index 0000000000..3462d3961c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/spell/spellcheck.js
    @@ -0,0 +1,478 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Support class for spell checker components.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.spell.SpellCheck');
    +goog.provide('goog.spell.SpellCheck.WordChangedEvent');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.structs.Set');
    +
    +
    +
    +/**
    + * Support class for spell checker components. Provides basic functionality
    + * such as word lookup and caching.
    + *
    + * @param {Function=} opt_lookupFunction Function to use for word lookup. Must
    + *     accept an array of words, an object reference and a callback function as
    + *     parameters. It must also call the callback function (as a method on the
    + *     object), once ready, with an array containing the original words, their
    + *     spelling status and optionally an array of suggestions.
    + * @param {string=} opt_language Content language.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.spell.SpellCheck = function(opt_lookupFunction, opt_language) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Function used to lookup spelling of words.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.lookupFunction_ = opt_lookupFunction || null;
    +
    +  /**
    +   * Cache for words not yet checked with lookup function.
    +   * @type {goog.structs.Set}
    +   * @private
    +   */
    +  this.unknownWords_ = new goog.structs.Set();
    +
    +  this.setLanguage(opt_language);
    +};
    +goog.inherits(goog.spell.SpellCheck, goog.events.EventTarget);
    +
    +
    +/**
    + * Delay, in ms, to wait for additional words to be entered before a lookup
    + * operation is triggered.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.spell.SpellCheck.LOOKUP_DELAY_ = 100;
    +
    +
    +/**
    + * Constants for event names
    + *
    + * @enum {string}
    + */
    +goog.spell.SpellCheck.EventType = {
    +  /**
    +   * Fired when all pending words have been processed.
    +   */
    +  READY: 'ready',
    +
    +  /**
    +   * Fired when all lookup function failed.
    +   */
    +  ERROR: 'error',
    +
    +  /**
    +   * Fired when a word's status is changed.
    +   */
    +  WORD_CHANGED: 'wordchanged'
    +};
    +
    +
    +/**
    + * Cache. Shared across all spell checker instances. Map with langauge as the
    + * key and a cache for that language as the value.
    + *
    + * @type {Object}
    + * @private
    + */
    +goog.spell.SpellCheck.cache_ = {};
    +
    +
    +/**
    + * Content Language.
    + * @type {string}
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.language_ = '';
    +
    +
    +/**
    + * Cache for set language. Reference to the element corresponding to the set
    + * language in the static goog.spell.SpellCheck.cache_.
    + *
    + * @type {Object|undefined}
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.cache_;
    +
    +
    +/**
    + * Id for timer processing the pending queue.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.queueTimer_ = 0;
    +
    +
    +/**
    + * Whether a lookup operation is in progress.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.lookupInProgress_ = false;
    +
    +
    +/**
    + * Codes representing the status of an individual word.
    + *
    + * @enum {number}
    + */
    +goog.spell.SpellCheck.WordStatus = {
    +  UNKNOWN: 0,
    +  VALID: 1,
    +  INVALID: 2,
    +  IGNORED: 3,
    +  CORRECTED: 4 // Temporary status, not stored in cache
    +};
    +
    +
    +/**
    + * Fields for word array in cache.
    + *
    + * @enum {number}
    + */
    +goog.spell.SpellCheck.CacheIndex = {
    +  STATUS: 0,
    +  SUGGESTIONS: 1
    +};
    +
    +
    +/**
    + * Regular expression for identifying word boundaries.
    + *
    + * @type {string}
    + */
    +goog.spell.SpellCheck.WORD_BOUNDARY_CHARS =
    +    '\t\r\n\u00A0 !\"#$%&()*+,\-.\/:;<=>?@\[\\\]^_`{|}~';
    +
    +
    +/**
    + * Regular expression for identifying word boundaries.
    + *
    + * @type {RegExp}
    + */
    +goog.spell.SpellCheck.WORD_BOUNDARY_REGEX = new RegExp(
    +    '[' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']');
    +
    +
    +/**
    + * Regular expression for splitting a string into individual words and blocks of
    + * separators. Matches zero or one word followed by zero or more separators.
    + *
    + * @type {RegExp}
    + */
    +goog.spell.SpellCheck.SPLIT_REGEX = new RegExp(
    +    '([^' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)' +
    +    '([' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)');
    +
    +
    +/**
    + * Sets the lookup function.
    + *
    + * @param {Function} f Function to use for word lookup. Must accept an array of
    + *     words, an object reference and a callback function as parameters.
    + *     It must also call the callback function (as a method on the object),
    + *     once ready, with an array containing the original words, their
    + *     spelling status and optionally an array of suggestions.
    + */
    +goog.spell.SpellCheck.prototype.setLookupFunction = function(f) {
    +  this.lookupFunction_ = f;
    +};
    +
    +
    +/**
    + * Sets language.
    + *
    + * @param {string=} opt_language Content language.
    + */
    +goog.spell.SpellCheck.prototype.setLanguage = function(opt_language) {
    +  this.language_ = opt_language || '';
    +
    +  if (!goog.spell.SpellCheck.cache_[this.language_]) {
    +    goog.spell.SpellCheck.cache_[this.language_] = {};
    +  }
    +  this.cache_ = goog.spell.SpellCheck.cache_[this.language_];
    +};
    +
    +
    +/**
    + * Returns language.
    + *
    + * @return {string} Content language.
    + */
    +goog.spell.SpellCheck.prototype.getLanguage = function() {
    +  return this.language_;
    +};
    +
    +
    +/**
    + * Checks spelling for a block of text.
    + *
    + * @param {string} text Block of text to spell check.
    + */
    +goog.spell.SpellCheck.prototype.checkBlock = function(text) {
    +  var words = text.split(goog.spell.SpellCheck.WORD_BOUNDARY_REGEX);
    +
    +  var len = words.length;
    +  for (var word, i = 0; i < len; i++) {
    +    word = words[i];
    +    this.checkWord_(word);
    +  }
    +
    +  if (!this.queueTimer_ && !this.lookupInProgress_ &&
    +      this.unknownWords_.getCount()) {
    +    this.processPending_();
    +  }
    +  else if (this.unknownWords_.getCount() == 0) {
    +    this.dispatchEvent(goog.spell.SpellCheck.EventType.READY);
    +  }
    +};
    +
    +
    +/**
    + * Checks spelling for a single word. Returns the status of the supplied word,
    + * or UNKNOWN if it's not cached. If it's not cached the word is added to a
    + * queue and checked with the verification implementation with a short delay.
    + *
    + * @param {string} word Word to check spelling of.
    + * @return {goog.spell.SpellCheck.WordStatus} The status of the supplied word,
    + *     or UNKNOWN if it's not cached.
    + */
    +goog.spell.SpellCheck.prototype.checkWord = function(word) {
    +  var status = this.checkWord_(word);
    +
    +  if (status == goog.spell.SpellCheck.WordStatus.UNKNOWN &&
    +      !this.queueTimer_ && !this.lookupInProgress_) {
    +    this.queueTimer_ = goog.Timer.callOnce(this.processPending_,
    +        goog.spell.SpellCheck.LOOKUP_DELAY_, this);
    +  }
    +
    +  return status;
    +};
    +
    +
    +/**
    + * Checks spelling for a single word. Returns the status of the supplied word,
    + * or UNKNOWN if it's not cached.
    + *
    + * @param {string} word Word to check spelling of.
    + * @return {goog.spell.SpellCheck.WordStatus} The status of the supplied word,
    + *     or UNKNOWN if it's not cached.
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.checkWord_ = function(word) {
    +  if (!word) {
    +    return goog.spell.SpellCheck.WordStatus.INVALID;
    +  }
    +
    +  var cacheEntry = this.cache_[word];
    +  if (!cacheEntry) {
    +    this.unknownWords_.add(word);
    +    return goog.spell.SpellCheck.WordStatus.UNKNOWN;
    +  }
    +
    +  return cacheEntry[goog.spell.SpellCheck.CacheIndex.STATUS];
    +};
    +
    +
    +/**
    + * Processes pending words unless a lookup operation has already been queued or
    + * is in progress.
    + *
    + * @throws {Error}
    + */
    +goog.spell.SpellCheck.prototype.processPending = function() {
    +  if (this.unknownWords_.getCount()) {
    +    if (!this.queueTimer_ && !this.lookupInProgress_) {
    +      this.processPending_();
    +    }
    +  } else {
    +    this.dispatchEvent(goog.spell.SpellCheck.EventType.READY);
    +  }
    +};
    +
    +
    +/**
    + * Processes pending words using the verification callback.
    + *
    + * @throws {Error}
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.processPending_ = function() {
    +  if (!this.lookupFunction_) {
    +    throw Error('No lookup function provided for spell checker.');
    +  }
    +
    +  if (this.unknownWords_.getCount()) {
    +    this.lookupInProgress_ = true;
    +    var func = this.lookupFunction_;
    +    func(this.unknownWords_.getValues(), this, this.lookupCallback_);
    +  } else {
    +    this.dispatchEvent(goog.spell.SpellCheck.EventType.READY);
    +  }
    +
    +  this.queueTimer_ = 0;
    +};
    +
    +
    +/**
    + * Callback for lookup function.
    + *
    + * @param {Array<Array<?>>} data Data array. Each word is represented by an
    + *     array containing the word, the status and optionally an array of
    + *     suggestions. Passing null indicates that the operation failed.
    + * @private
    + *
    + * Example:
    + * obj.lookupCallback_([
    + *   ['word', VALID],
    + *   ['wrod', INVALID, ['word', 'wood', 'rod']]
    + * ]);
    + */
    +goog.spell.SpellCheck.prototype.lookupCallback_ = function(data) {
    +
    +  // Lookup function failed; abort then dispatch error event.
    +  if (data == null) {
    +    if (this.queueTimer_) {
    +      goog.Timer.clear(this.queueTimer_);
    +      this.queueTimer_ = 0;
    +    }
    +    this.lookupInProgress_ = false;
    +
    +    this.dispatchEvent(goog.spell.SpellCheck.EventType.ERROR);
    +    return;
    +  }
    +
    +  for (var a, i = 0; a = data[i]; i++) {
    +    this.setWordStatus_(a[0], a[1], a[2]);
    +  }
    +  this.lookupInProgress_ = false;
    +
    +  // Fire ready event if all pending words have been processed.
    +  if (this.unknownWords_.getCount() == 0) {
    +    this.dispatchEvent(goog.spell.SpellCheck.EventType.READY);
    +
    +  // Process pending
    +  } else if (!this.queueTimer_) {
    +    this.queueTimer_ = goog.Timer.callOnce(this.processPending_,
    +        goog.spell.SpellCheck.LOOKUP_DELAY_, this);
    +  }
    +};
    +
    +
    +/**
    + * Sets a words spelling status.
    + *
    + * @param {string} word Word to set status for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @param {Array<string>=} opt_suggestions Suggestions.
    + *
    + * Example:
    + * obj.setWordStatus('word', VALID);
    + * obj.setWordStatus('wrod', INVALID, ['word', 'wood', 'rod']);.
    + */
    +goog.spell.SpellCheck.prototype.setWordStatus =
    +    function(word, status, opt_suggestions) {
    +  this.setWordStatus_(word, status, opt_suggestions);
    +};
    +
    +
    +/**
    + * Sets a words spelling status.
    + *
    + * @param {string} word Word to set status for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @param {Array<string>=} opt_suggestions Suggestions.
    + * @private
    + */
    +goog.spell.SpellCheck.prototype.setWordStatus_ =
    +    function(word, status, opt_suggestions) {
    +  var suggestions = opt_suggestions || [];
    +  this.cache_[word] = [status, suggestions];
    +  this.unknownWords_.remove(word);
    +
    +  this.dispatchEvent(
    +      new goog.spell.SpellCheck.WordChangedEvent(this, word, status));
    +};
    +
    +
    +/**
    + * Returns suggestions for the given word.
    + *
    + * @param {string} word Word to get suggestions for.
    + * @return {Array<string>} An array of suggestions for the given word.
    + */
    +goog.spell.SpellCheck.prototype.getSuggestions = function(word) {
    +  var cacheEntry = this.cache_[word];
    +
    +  if (!cacheEntry) {
    +    this.checkWord(word);
    +    return [];
    +  }
    +
    +  return cacheEntry[goog.spell.SpellCheck.CacheIndex.STATUS] ==
    +      goog.spell.SpellCheck.WordStatus.INVALID ?
    +      cacheEntry[goog.spell.SpellCheck.CacheIndex.SUGGESTIONS] : [];
    +};
    +
    +
    +
    +/**
    + * Object representing a word changed event. Fired when the status of a word
    + * changes.
    + *
    + * @param {goog.spell.SpellCheck} target Spellcheck object initiating event.
    + * @param {string} word Word to set status for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.spell.SpellCheck.WordChangedEvent = function(target, word, status) {
    +  goog.events.Event.call(this, goog.spell.SpellCheck.EventType.WORD_CHANGED,
    +      target);
    +
    +  /**
    +   * Word the status has changed for.
    +   * @type {string}
    +   */
    +  this.word = word;
    +
    +  /**
    +   * New status
    +   * @type {goog.spell.SpellCheck.WordStatus}
    +   */
    +  this.status = status;
    +};
    +goog.inherits(goog.spell.SpellCheck.WordChangedEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.html b/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.html
    new file mode 100644
    index 0000000000..69b342164e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.spell.SpellCheck
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.spell.SpellCheckTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.js b/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.js
    new file mode 100644
    index 0000000000..b72b0db8e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/spell/spellcheck_test.js
    @@ -0,0 +1,110 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.spell.SpellCheckTest');
    +goog.setTestOnly('goog.spell.SpellCheckTest');
    +
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_DATA = {
    +  'Test': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'strnig': [goog.spell.SpellCheck.WordStatus.INVALID, []],
    +  'wtih': [goog.spell.SpellCheck.WordStatus.INVALID, []],
    +  'a': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'few': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'misspeled': [goog.spell.SpellCheck.WordStatus.INVALID,
    +    ['misspelled', 'misapplied', 'misspell']],
    +  'words': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'Testing': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'set': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'status': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'vaild': [goog.spell.SpellCheck.WordStatus.INVALID, []],
    +  'invalid': [goog.spell.SpellCheck.WordStatus.VALID, []],
    +  'ignoerd': [goog.spell.SpellCheck.WordStatus.INVALID, []]
    +};
    +
    +function mockSpellCheckingFunction(words, spellChecker, callback) {
    +  var len = words.length;
    +  var data = [];
    +  for (var i = 0; i < len; i++) {
    +    var word = words[i];
    +    var status = TEST_DATA[word][0];
    +    var suggestions = TEST_DATA[word][1];
    +    data.push([word, status, suggestions]);
    +  }
    +  callback.call(spellChecker, data);
    +}
    +
    +
    +function testWordMatching() {
    +  var spell = new goog.spell.SpellCheck(mockSpellCheckingFunction);
    +
    +  var valid = goog.spell.SpellCheck.WordStatus.VALID;
    +  var invalid = goog.spell.SpellCheck.WordStatus.INVALID;
    +
    +  spell.checkBlock('Test strnig wtih a few misspeled words.');
    +  assertEquals(valid, spell.checkWord('Test'));
    +  assertEquals(invalid, spell.checkWord('strnig'));
    +  assertEquals(invalid, spell.checkWord('wtih'));
    +  assertEquals(valid, spell.checkWord('a'));
    +  assertEquals(valid, spell.checkWord('few'));
    +  assertEquals(invalid, spell.checkWord('misspeled'));
    +  assertEquals(valid, spell.checkWord('words'));
    +}
    +
    +
    +function testSetWordStatusValid() {
    +  var spell = new goog.spell.SpellCheck(mockSpellCheckingFunction);
    +
    +  var valid = goog.spell.SpellCheck.WordStatus.VALID;
    +
    +  spell.checkBlock('Testing set status vaild.');
    +  spell.setWordStatus('vaild', valid);
    +
    +  assertEquals(valid, spell.checkWord('vaild'));
    +}
    +
    +function testSetWordStatusInvalid() {
    +  var spell = new goog.spell.SpellCheck(mockSpellCheckingFunction);
    +
    +  var valid = goog.spell.SpellCheck.WordStatus.VALID;
    +  var invalid = goog.spell.SpellCheck.WordStatus.INVALID;
    +
    +  spell.checkBlock('Testing set status invalid.');
    +  spell.setWordStatus('invalid', invalid);
    +
    +  assertEquals(invalid, spell.checkWord('invalid'));
    +}
    +
    +
    +function testSetWordStatusIgnored() {
    +  var spell = new goog.spell.SpellCheck(mockSpellCheckingFunction);
    +
    +  var ignored = goog.spell.SpellCheck.WordStatus.IGNORED;
    +
    +  spell.checkBlock('Testing set status ignoerd.');
    +  spell.setWordStatus('ignoerd', ignored);
    +
    +  assertEquals(ignored, spell.checkWord('ignoerd'));
    +}
    +
    +
    +function testGetSuggestions() {
    +  var spell = new goog.spell.SpellCheck(mockSpellCheckingFunction);
    +
    +  spell.checkBlock('Test strnig wtih a few misspeled words.');
    +  var suggestions = spell.getSuggestions('misspeled');
    +  assertEquals(3, suggestions.length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/stats/basicstat.js b/mapbox-gl/closure-library/closure/goog/stats/basicstat.js
    new file mode 100644
    index 0000000000..7439294f53
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/stats/basicstat.js
    @@ -0,0 +1,270 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A basic statistics tracker.
    + *
    + */
    +
    +goog.provide('goog.stats.BasicStat');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.log');
    +goog.require('goog.string.format');
    +goog.require('goog.structs.CircularBuffer');
    +
    +
    +
    +/**
    + * Tracks basic statistics over a specified time interval.
    + *
    + * Statistics are kept in a fixed number of slots, each representing
    + * an equal portion of the time interval.
    + *
    + * Most methods optionally allow passing in the current time, so that
    + * higher level stats can synchronize operations on multiple child
    + * objects.  Under normal usage, the default of goog.now() should be
    + * sufficient.
    + *
    + * @param {number} interval The stat interval, in milliseconds.
    + * @constructor
    + * @final
    + */
    +goog.stats.BasicStat = function(interval) {
    +  goog.asserts.assert(interval > 50);
    +
    +  /**
    +   * The time interval that this statistic aggregates over.
    +   * @type {number}
    +   * @private
    +   */
    +  this.interval_ = interval;
    +
    +  /**
    +   * The number of milliseconds in each slot.
    +   * @type {number}
    +   * @private
    +   */
    +  this.slotInterval_ = Math.floor(interval / goog.stats.BasicStat.NUM_SLOTS_);
    +
    +  /**
    +   * The array of slots.
    +   * @type {goog.structs.CircularBuffer}
    +   * @private
    +   */
    +  this.slots_ =
    +      new goog.structs.CircularBuffer(goog.stats.BasicStat.NUM_SLOTS_);
    +};
    +
    +
    +/**
    + * The number of slots. This value limits the accuracy of the get()
    + * method to (this.interval_ / NUM_SLOTS).  A 1-minute statistic would
    + * be accurate to within 2 seconds.
    + * @type {number}
    + * @private
    + */
    +goog.stats.BasicStat.NUM_SLOTS_ = 50;
    +
    +
    +/**
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.stats.BasicStat.prototype.logger_ =
    +    goog.log.getLogger('goog.stats.BasicStat');
    +
    +
    +/**
    + * @return {number} The interval which over statistics are being
    + *     accumulated, in milliseconds.
    + */
    +goog.stats.BasicStat.prototype.getInterval = function() {
    +  return this.interval_;
    +};
    +
    +
    +/**
    + * Increments the count of this statistic by the specified amount.
    + *
    + * @param {number} amt The amount to increase the count by.
    + * @param {number=} opt_now The time, in milliseconds, to be treated
    + *     as the "current" time.  The current time must always be greater
    + *     than or equal to the last time recorded by this stat tracker.
    + */
    +goog.stats.BasicStat.prototype.incBy = function(amt, opt_now) {
    +  var now = opt_now ? opt_now : goog.now();
    +  this.checkForTimeTravel_(now);
    +  var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.getLast());
    +  if (!slot || now >= slot.end) {
    +    slot = new goog.stats.BasicStat.Slot_(this.getSlotBoundary_(now));
    +    this.slots_.add(slot);
    +  }
    +  slot.count += amt;
    +  slot.min = Math.min(amt, slot.min);
    +  slot.max = Math.max(amt, slot.max);
    +};
    +
    +
    +/**
    + * Returns the count of the statistic over its configured time
    + * interval.
    + * @param {number=} opt_now The time, in milliseconds, to be treated
    + *     as the "current" time.  The current time must always be greater
    + *     than or equal to the last time recorded by this stat tracker.
    + * @return {number} The total count over the tracked interval.
    + */
    +goog.stats.BasicStat.prototype.get = function(opt_now) {
    +  return this.reduceSlots_(opt_now,
    +      function(sum, slot) { return sum + slot.count; },
    +      0);
    +};
    +
    +
    +/**
    + * Returns the magnitute of the largest atomic increment that occurred
    + * during the watched time interval.
    + * @param {number=} opt_now The time, in milliseconds, to be treated
    + *     as the "current" time.  The current time must always be greater
    + *     than or equal to the last time recorded by this stat tracker.
    + * @return {number} The maximum count of this statistic.
    + */
    +goog.stats.BasicStat.prototype.getMax = function(opt_now) {
    +  return this.reduceSlots_(opt_now,
    +      function(max, slot) { return Math.max(max, slot.max); },
    +      Number.MIN_VALUE);
    +};
    +
    +
    +/**
    + * Returns the magnitute of the smallest atomic increment that
    + * occurred during the watched time interval.
    + * @param {number=} opt_now The time, in milliseconds, to be treated
    + *     as the "current" time.  The current time must always be greater
    + *     than or equal to the last time recorded by this stat tracker.
    + * @return {number} The minimum count of this statistic.
    + */
    +goog.stats.BasicStat.prototype.getMin = function(opt_now) {
    +  return this.reduceSlots_(opt_now,
    +      function(min, slot) { return Math.min(min, slot.min); },
    +      Number.MAX_VALUE);
    +};
    +
    +
    +/**
    + * Passes each active slot into a function and accumulates the result.
    + *
    + * @param {number|undefined} now The current time, in milliseconds.
    + * @param {function(number, goog.stats.BasicStat.Slot_): number} func
    + *     The function to call for every active slot.  This function
    + *     takes two arguments: the previous result and the new slot to
    + *     include in the reduction.
    + * @param {number} val The initial value for the reduction.
    + * @return {number} The result of the reduction.
    + * @private
    + */
    +goog.stats.BasicStat.prototype.reduceSlots_ = function(now, func, val) {
    +  now = now || goog.now();
    +  this.checkForTimeTravel_(now);
    +  var rval = val;
    +  var start = this.getSlotBoundary_(now) - this.interval_;
    +  for (var i = this.slots_.getCount() - 1; i >= 0; --i) {
    +    var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.get(i));
    +    if (slot.end <= start) {
    +      break;
    +    }
    +    rval = func(rval, slot);
    +  }
    +  return rval;
    +};
    +
    +
    +/**
    + * Computes the end time for the slot that should contain the count
    + * around the given time.  This method ensures that every bucket is
    + * aligned on a "this.slotInterval_" millisecond boundary.
    + * @param {number} time The time to compute a boundary for.
    + * @return {number} The computed boundary.
    + * @private
    + */
    +goog.stats.BasicStat.prototype.getSlotBoundary_ = function(time) {
    +  return this.slotInterval_ * (Math.floor(time / this.slotInterval_) + 1);
    +};
    +
    +
    +/**
    + * Checks that time never goes backwards.  If it does (for example,
    + * the user changes their system clock), the object state is cleared.
    + * @param {number} now The current time, in milliseconds.
    + * @private
    + */
    +goog.stats.BasicStat.prototype.checkForTimeTravel_ = function(now) {
    +  var slot = /** @type {goog.stats.BasicStat.Slot_} */ (this.slots_.getLast());
    +  if (slot) {
    +    var slotStart = slot.end - this.slotInterval_;
    +    if (now < slotStart) {
    +      goog.log.warning(this.logger_, goog.string.format(
    +          'Went backwards in time: now=%d, slotStart=%d.  Resetting state.',
    +          now, slotStart));
    +      this.reset_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Clears any statistics tracked by this object, as though it were
    + * freshly created.
    + * @private
    + */
    +goog.stats.BasicStat.prototype.reset_ = function() {
    +  this.slots_.clear();
    +};
    +
    +
    +
    +/**
    + * A struct containing information for each sub-interval.
    + * @param {number} end The end time for this slot, in milliseconds.
    + * @constructor
    + * @private
    + */
    +goog.stats.BasicStat.Slot_ = function(end) {
    +  /**
    +   * End time of this slot, exclusive.
    +   * @type {number}
    +   */
    +  this.end = end;
    +};
    +
    +
    +/**
    + * Aggregated count within this slot.
    + * @type {number}
    + */
    +goog.stats.BasicStat.Slot_.prototype.count = 0;
    +
    +
    +/**
    + * The smallest atomic increment of the count within this slot.
    + * @type {number}
    + */
    +goog.stats.BasicStat.Slot_.prototype.min = Number.MAX_VALUE;
    +
    +
    +/**
    + * The largest atomic increment of the count within this slot.
    + * @type {number}
    + */
    +goog.stats.BasicStat.Slot_.prototype.max = Number.MIN_VALUE;
    diff --git a/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.html b/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.html
    new file mode 100644
    index 0000000000..78ba14bf58
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.stats.BasicStat
    +  </title>
    +  <script src="../base.js" type="text/javascript">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.stats.BasicStatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.js b/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.js
    new file mode 100644
    index 0000000000..912ed23705
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/stats/basicstat_test.js
    @@ -0,0 +1,165 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.stats.BasicStatTest');
    +goog.setTestOnly('goog.stats.BasicStatTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.stats.BasicStat');
    +goog.require('goog.string.format');
    +goog.require('goog.testing.PseudoRandom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function testGetSlotBoundary() {
    +  var stat = new goog.stats.BasicStat(1654);
    +  assertEquals('Checking interval', 33, stat.slotInterval_);
    +
    +  assertEquals(132, stat.getSlotBoundary_(125));
    +  assertEquals(165, stat.getSlotBoundary_(132));
    +  assertEquals(132, stat.getSlotBoundary_(99));
    +  assertEquals(99, stat.getSlotBoundary_(98));
    +}
    +
    +function testCheckForTimeTravel() {
    +  var stat = new goog.stats.BasicStat(1000);
    +
    +  // no slots yet, should always be OK
    +  stat.checkForTimeTravel_(100);
    +  stat.checkForTimeTravel_(-1);
    +
    +  stat.incBy(1, 125); // creates a first bucket, ending at t=140
    +
    +  // Even though these go backwards in time, our basic fuzzy check passes
    +  // because we just check that the time is within the latest interval bucket.
    +  stat.checkForTimeTravel_(141);
    +  stat.checkForTimeTravel_(140);
    +  stat.checkForTimeTravel_(139);
    +  stat.checkForTimeTravel_(125);
    +  stat.checkForTimeTravel_(124);
    +  stat.checkForTimeTravel_(120);
    +
    +  // State should still be the same, all of the above times are valid.
    +  assertEquals('State unchanged when called with good times', 1, stat.get(125));
    +
    +  stat.checkForTimeTravel_(119);
    +  assertEquals('Reset after called with a bad time', 0, stat.get(125));
    +}
    +
    +function testConstantIncrementPerSlot() {
    +  var stat = new goog.stats.BasicStat(1000);
    +
    +  var now = 1000;
    +  for (var i = 0; i < 50; ++i) {
    +    var newMax = 1000 + i;
    +    var newMin = 1000 - i;
    +    stat.incBy(newMin, now);
    +    stat.incBy(newMax, now);
    +
    +    var msg = goog.string.format(
    +        'now=%d i=%d newMin=%d newMax=%d', now, i, newMin, newMax);
    +    assertEquals(msg, 2000 * (i + 1), stat.get(now));
    +    assertEquals(msg, newMax, stat.getMax(now));
    +    assertEquals(msg, newMin, stat.getMin(now));
    +
    +    now += 20; // push into the next slots
    +  }
    +
    +  // The next increment should cause old data to fall off.
    +  stat.incBy(1, now);
    +  assertEquals(2000 * 49 + 1, stat.get(now));
    +  assertEquals(1, stat.getMin(now));
    +  assertEquals(1049, stat.getMax(now));
    +
    +  now += 20; // drop off another bucket
    +  stat.incBy(1, now);
    +  assertEquals(2000 * 48 + 2, stat.get(now));
    +  assertEquals(1, stat.getMin(now));
    +  assertEquals(1049, stat.getMax(now));
    +}
    +
    +function testSparseBuckets() {
    +  var stat = new goog.stats.BasicStat(1000);
    +  var now = 1000;
    +
    +  stat.incBy(10, now);
    +  assertEquals(10, stat.get(now));
    +
    +  now += 5000; // the old slot is now still in memory, but should be ignored
    +  stat.incBy(1, now);
    +  assertEquals(1, stat.get(now));
    +}
    +
    +function testFuzzy() {
    +  var stat = new goog.stats.BasicStat(1000);
    +  var test = new PerfectlySlowStat(1000);
    +  var rand = new goog.testing.PseudoRandom(58849020);
    +  var eventCount = 0;
    +
    +  // test over 5 simulated seconds (2 for IE, due to timeouts)
    +  var simulationDuration = goog.userAgent.IE ? 2000 : 5000;
    +  for (var now = 1000; now < simulationDuration; ) {
    +    var count = Math.floor(rand.random() * 2147483648);
    +    var delay = Math.floor(rand.random() * 25);
    +    for (var i = 0; i <= delay; ++i) {
    +      var time = now + i;
    +      var msg = goog.string.format('now=%d eventCount=%d', time, eventCount);
    +      var expected = test.getStats(now + i);
    +      assertEquals(expected.count, stat.get(time));
    +      assertEquals(expected.min, stat.getMin(time));
    +      assertEquals(expected.max, stat.getMax(time));
    +    }
    +
    +    now += delay;
    +    stat.incBy(count, now);
    +    test.incBy(count, now);
    +    eventCount++;
    +  }
    +}
    +
    +
    +
    +/**
    + * A horribly inefficient implementation of BasicStat that stores
    + * every event in an array and dynamically filters to perform
    + * aggregations.
    + * @constructor
    + */
    +var PerfectlySlowStat = function(interval) {
    +  this.interval_ = interval;
    +  this.slotSize_ = Math.floor(interval / goog.stats.BasicStat.NUM_SLOTS_);
    +  this.events_ = [];
    +};
    +
    +PerfectlySlowStat.prototype.incBy = function(amt, now) {
    +  this.events_.push({'time': now, 'count': amt});
    +};
    +
    +PerfectlySlowStat.prototype.getStats = function(now) {
    +  var end = Math.floor(now / this.slotSize_) * this.slotSize_ + this.slotSize_;
    +  var start = end - this.interval_;
    +  var events = goog.array.filter(this.events_,
    +      function(e) { return e.time >= start });
    +  return {
    +    'count': goog.array.reduce(events,
    +        function(sum, e) { return sum + e.count },
    +        0),
    +    'min': goog.array.reduce(events,
    +        function(min, e) { return Math.min(min, e.count); },
    +        Number.MAX_VALUE),
    +    'max': goog.array.reduce(events,
    +        function(max, e) { return Math.max(max, e.count); },
    +        Number.MIN_VALUE)
    +  };
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/collectablestorage.js b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage.js
    new file mode 100644
    index 0000000000..9624406970
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage.js
    @@ -0,0 +1,131 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data persistence with data
    + * expiration and user-initiated expired key collection.
    + *
    + */
    +
    +goog.provide('goog.storage.CollectableStorage');
    +
    +goog.require('goog.array');
    +goog.require('goog.iter');
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.ExpiringStorage');
    +goog.require('goog.storage.RichStorage');
    +
    +
    +
    +/**
    + * Provides a storage with expirning keys and a collection method.
    + *
    + * @param {!goog.storage.mechanism.IterableMechanism} mechanism The underlying
    + *     storage mechanism.
    + * @constructor
    + * @extends {goog.storage.ExpiringStorage}
    + */
    +goog.storage.CollectableStorage = function(mechanism) {
    +  goog.storage.CollectableStorage.base(this, 'constructor', mechanism);
    +};
    +goog.inherits(goog.storage.CollectableStorage, goog.storage.ExpiringStorage);
    +
    +
    +/**
    + * Iterate over keys and returns those that expired.
    + *
    + * @param {goog.iter.Iterable} keys keys to iterate over.
    + * @param {boolean=} opt_strict Also return invalid keys.
    + * @return {!Array<string>} Keys of values that expired.
    + * @private
    + */
    +goog.storage.CollectableStorage.prototype.getExpiredKeys_ =
    +    function(keys, opt_strict) {
    +  var keysToRemove = [];
    +  goog.iter.forEach(keys, function(key) {
    +    // Get the wrapper.
    +    var wrapper;
    +    /** @preserveTry */
    +    try {
    +      wrapper = goog.storage.CollectableStorage.prototype.getWrapper.call(
    +          this, key, true);
    +    } catch (ex) {
    +      if (ex == goog.storage.ErrorCode.INVALID_VALUE) {
    +        // Bad wrappers are removed in strict mode.
    +        if (opt_strict) {
    +          keysToRemove.push(key);
    +        }
    +        // Skip over bad wrappers and continue.
    +        return;
    +      }
    +      // Unknown error, escalate.
    +      throw ex;
    +    }
    +    if (!goog.isDef(wrapper)) {
    +      // A value for a given key is no longer available. Clean it up.
    +      keysToRemove.push(key);
    +      return;
    +    }
    +    // Remove expired objects.
    +    if (goog.storage.ExpiringStorage.isExpired(wrapper)) {
    +      keysToRemove.push(key);
    +      // Continue with the next key.
    +      return;
    +    }
    +    // Objects which can't be decoded are removed in strict mode.
    +    if (opt_strict) {
    +      /** @preserveTry */
    +      try {
    +        goog.storage.RichStorage.Wrapper.unwrap(wrapper);
    +      } catch (ex) {
    +        if (ex == goog.storage.ErrorCode.INVALID_VALUE) {
    +          keysToRemove.push(key);
    +          // Skip over bad wrappers and continue.
    +          return;
    +        }
    +        // Unknown error, escalate.
    +        throw ex;
    +      }
    +    }
    +  }, this);
    +  return keysToRemove;
    +};
    +
    +
    +/**
    + * Cleans up the storage by removing expired keys.
    + *
    + * @param {Array<string>} keys List of all keys.
    + * @param {boolean=} opt_strict Also remove invalid keys.
    + * @return {!Array<string>} a list of expired keys.
    + * @protected
    + */
    +goog.storage.CollectableStorage.prototype.collectInternal = function(
    +    keys, opt_strict) {
    +  var keysToRemove = this.getExpiredKeys_(keys, opt_strict);
    +  goog.array.forEach(keysToRemove, function(key) {
    +    goog.storage.CollectableStorage.prototype.remove.call(this, key);
    +  }, this);
    +  return keysToRemove;
    +};
    +
    +
    +/**
    + * Cleans up the storage by removing expired keys.
    + *
    + * @param {boolean=} opt_strict Also remove invalid keys.
    + */
    +goog.storage.CollectableStorage.prototype.collect = function(opt_strict) {
    +  this.collectInternal(this.mechanism.__iterator__(true), opt_strict);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.html
    new file mode 100644
    index 0000000000..9b9d9c4c5e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.CollectableStorage
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.CollectableStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.js
    new file mode 100644
    index 0000000000..f1b78f12eb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/collectablestorage_test.js
    @@ -0,0 +1,38 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.CollectableStorageTest');
    +goog.setTestOnly('goog.storage.CollectableStorageTest');
    +
    +goog.require('goog.storage.CollectableStorage');
    +goog.require('goog.storage.collectableStorageTester');
    +goog.require('goog.storage.storage_test');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.storage.FakeMechanism');
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.CollectableStorage(mechanism);
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +function testExpiredKeyCollection() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.storage.CollectableStorage(mechanism);
    +
    +  goog.storage.collectableStorageTester.runBasicTests(mechanism, clock,
    +      storage);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/collectablestoragetester.js b/mapbox-gl/closure-library/closure/goog/storage/collectablestoragetester.js
    new file mode 100644
    index 0000000000..439c42d6a8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/collectablestoragetester.js
    @@ -0,0 +1,71 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for the collectable storage interface.
    + *
    + */
    +
    +goog.provide('goog.storage.collectableStorageTester');
    +
    +goog.require('goog.testing.asserts');
    +goog.setTestOnly('collectablestorage_test');
    +
    +
    +/**
    + * Tests basic operation: expiration and collection of collectable storage.
    + *
    + * @param {goog.storage.mechanism.IterableMechanism} mechanism
    + * @param {goog.testing.MockClock} clock
    + * @param {goog.storage.CollectableStorage} storage
    +  */
    +goog.storage.collectableStorageTester.runBasicTests =
    +    function(mechanism, clock, storage) {
    +  // No expiration.
    +  storage.set('first', 'three seconds', 3000);
    +  storage.set('second', 'one second', 1000);
    +  storage.set('third', 'permanent');
    +  storage.set('fourth', 'two seconds', 2000);
    +  clock.tick(100);
    +  storage.collect();
    +  assertEquals('three seconds', storage.get('first'));
    +  assertEquals('one second', storage.get('second'));
    +  assertEquals('permanent', storage.get('third'));
    +  assertEquals('two seconds', storage.get('fourth'));
    +
    +  // A key has expired.
    +  clock.tick(1000);
    +  storage.collect();
    +  assertNull(mechanism.get('second'));
    +  assertEquals('three seconds', storage.get('first'));
    +  assertUndefined(storage.get('second'));
    +  assertEquals('permanent', storage.get('third'));
    +  assertEquals('two seconds', storage.get('fourth'));
    +
    +  // Another two keys have expired.
    +  clock.tick(2000);
    +  storage.collect();
    +  assertNull(mechanism.get('first'));
    +  assertNull(mechanism.get('fourth'));
    +  assertUndefined(storage.get('first'));
    +  assertEquals('permanent', storage.get('third'));
    +  assertUndefined(storage.get('fourth'));
    +
    +  // Clean up.
    +  storage.remove('third');
    +  assertNull(mechanism.get('third'));
    +  assertUndefined(storage.get('third'));
    +  storage.collect();
    +  clock.uninstall();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage.js b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage.js
    new file mode 100644
    index 0000000000..5bd9ede6c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage.js
    @@ -0,0 +1,202 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data persistence with key and
    + * object encryption. Without a valid secret, the existence of a particular
    + * key can't be verified and values can't be decrypted. The value encryption
    + * is salted, so subsequent writes of the same cleartext result in different
    + * ciphertext. The ciphertext is *not* authenticated, so there is no protection
    + * against data manipulation.
    + *
    + * The metadata is *not* encrypted, so expired keys can be cleaned up without
    + * decrypting them. If sensitive metadata is added in subclasses, it is up
    + * to the subclass to protect this information, perhaps by embedding it in
    + * the object.
    + *
    + */
    +
    +goog.provide('goog.storage.EncryptedStorage');
    +
    +goog.require('goog.crypt');
    +goog.require('goog.crypt.Arc4');
    +goog.require('goog.crypt.Sha1');
    +goog.require('goog.crypt.base64');
    +goog.require('goog.json');
    +goog.require('goog.json.Serializer');
    +goog.require('goog.storage.CollectableStorage');
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.RichStorage');
    +
    +
    +
    +/**
    + * Provides an encrypted storage. The keys are hashed with a secret, so
    + * their existence cannot be verified without the knowledge of the secret.
    + * The values are encrypted using the key, a salt, and the secret, so
    + * stream cipher initialization varies for each stored value.
    + *
    + * @param {!goog.storage.mechanism.IterableMechanism} mechanism The underlying
    + *     storage mechanism.
    + * @param {string} secret The secret key used to encrypt the storage.
    + * @constructor
    + * @extends {goog.storage.CollectableStorage}
    + * @final
    + */
    +goog.storage.EncryptedStorage = function(mechanism, secret) {
    +  goog.storage.EncryptedStorage.base(this, 'constructor', mechanism);
    +  this.secret_ = goog.crypt.stringToByteArray(secret);
    +  this.cleartextSerializer_ = new goog.json.Serializer();
    +};
    +goog.inherits(goog.storage.EncryptedStorage, goog.storage.CollectableStorage);
    +
    +
    +/**
    + * Metadata key under which the salt is stored.
    + *
    + * @type {string}
    + * @protected
    + */
    +goog.storage.EncryptedStorage.SALT_KEY = 'salt';
    +
    +
    +/**
    + * The secret used to encrypt the storage.
    + *
    + * @type {Array<number>}
    + * @private
    + */
    +goog.storage.EncryptedStorage.prototype.secret_ = null;
    +
    +
    +/**
    + * The JSON serializer used to serialize values before encryption. This can
    + * be potentially different from serializing for the storage mechanism (see
    + * goog.storage.Storage), so a separate serializer is kept here.
    + *
    + * @type {goog.json.Serializer}
    + * @private
    + */
    +goog.storage.EncryptedStorage.prototype.cleartextSerializer_ = null;
    +
    +
    +/**
    + * Hashes a key using the secret.
    + *
    + * @param {string} key The key.
    + * @return {string} The hash.
    + * @private
    + */
    +goog.storage.EncryptedStorage.prototype.hashKeyWithSecret_ = function(key) {
    +  var sha1 = new goog.crypt.Sha1();
    +  sha1.update(goog.crypt.stringToByteArray(key));
    +  sha1.update(this.secret_);
    +  return goog.crypt.base64.encodeByteArray(sha1.digest(), true);
    +};
    +
    +
    +/**
    + * Encrypts a value using a key, a salt, and the secret.
    + *
    + * @param {!Array<number>} salt The salt.
    + * @param {string} key The key.
    + * @param {string} value The cleartext value.
    + * @return {string} The encrypted value.
    + * @private
    + */
    +goog.storage.EncryptedStorage.prototype.encryptValue_ = function(
    +    salt, key, value) {
    +  if (!(salt.length > 0)) {
    +    throw Error('Non-empty salt must be provided');
    +  }
    +  var sha1 = new goog.crypt.Sha1();
    +  sha1.update(goog.crypt.stringToByteArray(key));
    +  sha1.update(salt);
    +  sha1.update(this.secret_);
    +  var arc4 = new goog.crypt.Arc4();
    +  arc4.setKey(sha1.digest());
    +  // Warm up the streamcypher state, see goog.crypt.Arc4 for details.
    +  arc4.discard(1536);
    +  var bytes = goog.crypt.stringToByteArray(value);
    +  arc4.crypt(bytes);
    +  return goog.crypt.byteArrayToString(bytes);
    +};
    +
    +
    +/**
    + * Decrypts a value using a key, a salt, and the secret.
    + *
    + * @param {!Array<number>} salt The salt.
    + * @param {string} key The key.
    + * @param {string} value The encrypted value.
    + * @return {string} The decrypted value.
    + * @private
    + */
    +goog.storage.EncryptedStorage.prototype.decryptValue_ = function(
    +    salt, key, value) {
    +  // ARC4 is symmetric.
    +  return this.encryptValue_(salt, key, value);
    +};
    +
    +
    +/** @override */
    +goog.storage.EncryptedStorage.prototype.set = function(
    +    key, value, opt_expiration) {
    +  if (!goog.isDef(value)) {
    +    goog.storage.EncryptedStorage.prototype.remove.call(this, key);
    +    return;
    +  }
    +  var salt = [];
    +  // 64-bit random salt.
    +  for (var i = 0; i < 8; ++i) {
    +    salt[i] = Math.floor(Math.random() * 0x100);
    +  }
    +  var wrapper = new goog.storage.RichStorage.Wrapper(
    +      this.encryptValue_(salt, key,
    +                         this.cleartextSerializer_.serialize(value)));
    +  wrapper[goog.storage.EncryptedStorage.SALT_KEY] = salt;
    +  goog.storage.EncryptedStorage.base(this, 'set',
    +      this.hashKeyWithSecret_(key), wrapper, opt_expiration);
    +};
    +
    +
    +/** @override */
    +goog.storage.EncryptedStorage.prototype.getWrapper = function(
    +    key, opt_expired) {
    +  var wrapper = goog.storage.EncryptedStorage.base(this, 'getWrapper',
    +      this.hashKeyWithSecret_(key), opt_expired);
    +  if (!wrapper) {
    +    return undefined;
    +  }
    +  var value = goog.storage.RichStorage.Wrapper.unwrap(wrapper);
    +  var salt = wrapper[goog.storage.EncryptedStorage.SALT_KEY];
    +  if (!goog.isString(value) || !goog.isArray(salt) || !salt.length) {
    +    throw goog.storage.ErrorCode.INVALID_VALUE;
    +  }
    +  var json = this.decryptValue_(salt, key, value);
    +  /** @preserveTry */
    +  try {
    +    wrapper[goog.storage.RichStorage.DATA_KEY] = goog.json.parse(json);
    +  } catch (e) {
    +    throw goog.storage.ErrorCode.DECRYPTION_ERROR;
    +  }
    +  return wrapper;
    +};
    +
    +
    +/** @override */
    +goog.storage.EncryptedStorage.prototype.remove = function(key) {
    +  goog.storage.EncryptedStorage.base(
    +      this, 'remove', this.hashKeyWithSecret_(key));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.html
    new file mode 100644
    index 0000000000..15c34d6fa9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.EncryptedStorage
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.EncryptedStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.js
    new file mode 100644
    index 0000000000..bd41907dd9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/encryptedstorage_test.js
    @@ -0,0 +1,167 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.EncryptedStorageTest');
    +goog.setTestOnly('goog.storage.EncryptedStorageTest');
    +
    +goog.require('goog.json');
    +goog.require('goog.storage.EncryptedStorage');
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.RichStorage');
    +goog.require('goog.storage.collectableStorageTester');
    +goog.require('goog.storage.storage_test');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PseudoRandom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.storage.FakeMechanism');
    +
    +function getEncryptedWrapper(storage, key) {
    +  return goog.json.parse(
    +      storage.mechanism.get(storage.hashKeyWithSecret_(key)));
    +}
    +
    +function getEncryptedData(storage, key) {
    +  return getEncryptedWrapper(storage, key)[goog.storage.RichStorage.DATA_KEY];
    +}
    +
    +function decryptWrapper(storage, key, wrapper) {
    +  return goog.json.parse(
    +      storage.decryptValue_(wrapper[goog.storage.EncryptedStorage.SALT_KEY],
    +          key, wrapper[goog.storage.RichStorage.DATA_KEY]));
    +}
    +
    +function hammingDistance(a, b) {
    +  if (a.length != b.length) {
    +    throw Error('Lengths must be the same for Hamming distance');
    +  }
    +  var distance = 0;
    +  for (var i = 0; i < a.length; ++i) {
    +    if (a.charAt(i) != b.charAt(i)) {
    +      ++distance;
    +    }
    +  }
    +  return distance;
    +}
    +
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +
    +function testExpiredKeyCollection() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
    +
    +  goog.storage.collectableStorageTester.runBasicTests(mechanism, clock,
    +      storage);
    +}
    +
    +
    +function testEncryption() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
    +  var mallory = new goog.storage.EncryptedStorage(mechanism, 'guess');
    +
    +  // Simple Objects.
    +  storage.set('first', 'Hello world!');
    +  storage.set('second', ['one', 'two', 'three'], 1000);
    +  storage.set('third', {'a': 97, 'b': 98});
    +
    +  // Wrong secret can't find keys.
    +  assertNull(mechanism.get('first'));
    +  assertNull(mechanism.get('second'));
    +  assertNull(mechanism.get('third'));
    +  assertUndefined(mallory.get('first'));
    +  assertUndefined(mallory.get('second'));
    +  assertUndefined(mallory.get('third'));
    +
    +  // Wrong secret can't overwrite keys.
    +  mallory.set('first', 'Ho ho ho!');
    +  assertObjectEquals('Ho ho ho!', mallory.get('first'));
    +  assertObjectEquals('Hello world!', storage.get('first'));
    +  mallory.remove('first');
    +
    +  // Correct key decrypts properly.
    +  assertObjectEquals('Hello world!', storage.get('first'));
    +  assertObjectEquals(['one', 'two', 'three'], storage.get('second'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +
    +  // Wrong secret can't decode values even if the key is revealed.
    +  var encryptedWrapper = getEncryptedWrapper(storage, 'first');
    +  assertObjectEquals('Hello world!',
    +      decryptWrapper(storage, 'first', encryptedWrapper));
    +  assertThrows(function() {
    +    decryptWrapper(mallory, 'first', encryptedWrapper);
    +  });
    +
    +  // If the value is overwritten, it can't be decrypted.
    +  encryptedWrapper[goog.storage.RichStorage.DATA_KEY] = 'kaboom';
    +  mechanism.set(storage.hashKeyWithSecret_('first'),
    +                goog.json.serialize(encryptedWrapper));
    +  assertEquals(goog.storage.ErrorCode.DECRYPTION_ERROR,
    +               assertThrows(function() {storage.get('first')}));
    +
    +  // Test garbage collection.
    +  storage.collect();
    +  assertNotNull(getEncryptedWrapper(storage, 'first'));
    +  assertObjectEquals(['one', 'two', 'three'], storage.get('second'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +  clock.tick(2000);
    +  storage.collect();
    +  assertNotNull(getEncryptedWrapper(storage, 'first'));
    +  assertUndefined(storage.get('second'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +  mechanism.set(storage.hashKeyWithSecret_('first'), '"kaboom"');
    +  storage.collect();
    +  assertNotNull(getEncryptedWrapper(storage, 'first'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +  storage.collect(true);
    +  assertUndefined(storage.get('first'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +
    +  // Clean up.
    +  storage.remove('third');
    +  assertUndefined(storage.get('third'));
    +  clock.uninstall();
    +}
    +
    +function testSalting() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var randomMock = new goog.testing.PseudoRandom(0, true);
    +  var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
    +
    +  // Same value under two different keys should appear very different,
    +  // even with the same salt.
    +  storage.set('one', 'Hello world!');
    +  randomMock.seed(0); // Reset the generator so we get the same salt.
    +  storage.set('two', 'Hello world!');
    +  var golden = getEncryptedData(storage, 'one');
    +  assertRoughlyEquals('Ciphertext did not change with keys', golden.length,
    +      hammingDistance(golden, getEncryptedData(storage, 'two')), 2);
    +
    +  // Same key-value pair written second time should appear very different.
    +  storage.set('one', 'Hello world!');
    +  assertRoughlyEquals('Salting seems to have failed', golden.length,
    +      hammingDistance(golden, getEncryptedData(storage, 'one')), 2);
    +
    +  // Clean up.
    +  storage.remove('1');
    +  storage.remove('2');
    +  randomMock.uninstall();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/errorcode.js b/mapbox-gl/closure-library/closure/goog/storage/errorcode.js
    new file mode 100644
    index 0000000000..8fd19ecf94
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/errorcode.js
    @@ -0,0 +1,30 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines errors to be thrown by the storage.
    + *
    + */
    +
    +goog.provide('goog.storage.ErrorCode');
    +
    +
    +/**
    + * Errors thrown by the storage.
    + * @enum {string}
    + */
    +goog.storage.ErrorCode = {
    +  INVALID_VALUE: 'Storage: Invalid value was encountered',
    +  DECRYPTION_ERROR: 'Storage: The value could not be decrypted'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/expiringstorage.js b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage.js
    new file mode 100644
    index 0000000000..3a432e8166
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage.js
    @@ -0,0 +1,140 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data persistence with expiration.
    + *
    + */
    +
    +goog.provide('goog.storage.ExpiringStorage');
    +
    +goog.require('goog.storage.RichStorage');
    +
    +
    +
    +/**
    + * Provides a storage with expirning keys.
    + *
    + * @param {!goog.storage.mechanism.Mechanism} mechanism The underlying
    + *     storage mechanism.
    + * @constructor
    + * @extends {goog.storage.RichStorage}
    + */
    +goog.storage.ExpiringStorage = function(mechanism) {
    +  goog.storage.ExpiringStorage.base(this, 'constructor', mechanism);
    +};
    +goog.inherits(goog.storage.ExpiringStorage, goog.storage.RichStorage);
    +
    +
    +/**
    + * Metadata key under which the expiration time is stored.
    + *
    + * @type {string}
    + * @protected
    + */
    +goog.storage.ExpiringStorage.EXPIRATION_TIME_KEY = 'expiration';
    +
    +
    +/**
    + * Metadata key under which the creation time is stored.
    + *
    + * @type {string}
    + * @protected
    + */
    +goog.storage.ExpiringStorage.CREATION_TIME_KEY = 'creation';
    +
    +
    +/**
    + * Returns the wrapper creation time.
    + *
    + * @param {!Object} wrapper The wrapper.
    + * @return {number|undefined} Wrapper creation time.
    + */
    +goog.storage.ExpiringStorage.getCreationTime = function(wrapper) {
    +  return wrapper[goog.storage.ExpiringStorage.CREATION_TIME_KEY];
    +};
    +
    +
    +/**
    + * Returns the wrapper expiration time.
    + *
    + * @param {!Object} wrapper The wrapper.
    + * @return {number|undefined} Wrapper expiration time.
    + */
    +goog.storage.ExpiringStorage.getExpirationTime = function(wrapper) {
    +  return wrapper[goog.storage.ExpiringStorage.EXPIRATION_TIME_KEY];
    +};
    +
    +
    +/**
    + * Checks if the data item has expired.
    + *
    + * @param {!Object} wrapper The wrapper.
    + * @return {boolean} True if the item has expired.
    + */
    +goog.storage.ExpiringStorage.isExpired = function(wrapper) {
    +  var creation = goog.storage.ExpiringStorage.getCreationTime(wrapper);
    +  var expiration = goog.storage.ExpiringStorage.getExpirationTime(wrapper);
    +  return !!expiration && expiration < goog.now() ||
    +         !!creation && creation > goog.now();
    +};
    +
    +
    +/**
    + * Set an item in the storage.
    + *
    + * @param {string} key The key to set.
    + * @param {*} value The value to serialize to a string and save.
    + * @param {number=} opt_expiration The number of miliseconds since epoch
    + *     (as in goog.now()) when the value is to expire. If the expiration
    + *     time is not provided, the value will persist as long as possible.
    + * @override
    + */
    +goog.storage.ExpiringStorage.prototype.set = function(
    +    key, value, opt_expiration) {
    +  var wrapper = goog.storage.RichStorage.Wrapper.wrapIfNecessary(value);
    +  if (wrapper) {
    +    if (opt_expiration) {
    +      if (opt_expiration < goog.now()) {
    +        goog.storage.ExpiringStorage.prototype.remove.call(this, key);
    +        return;
    +      }
    +      wrapper[goog.storage.ExpiringStorage.EXPIRATION_TIME_KEY] =
    +          opt_expiration;
    +    }
    +    wrapper[goog.storage.ExpiringStorage.CREATION_TIME_KEY] = goog.now();
    +  }
    +  goog.storage.ExpiringStorage.base(this, 'set', key, wrapper);
    +};
    +
    +
    +/**
    + * Get an item wrapper (the item and its metadata) from the storage.
    + *
    + * @param {string} key The key to get.
    + * @param {boolean=} opt_expired If true, return expired wrappers as well.
    + * @return {(!Object|undefined)} The wrapper, or undefined if not found.
    + * @override
    + */
    +goog.storage.ExpiringStorage.prototype.getWrapper = function(key, opt_expired) {
    +  var wrapper = goog.storage.ExpiringStorage.base(this, 'getWrapper', key);
    +  if (!wrapper) {
    +    return undefined;
    +  }
    +  if (!opt_expired && goog.storage.ExpiringStorage.isExpired(wrapper)) {
    +    goog.storage.ExpiringStorage.prototype.remove.call(this, key);
    +    return undefined;
    +  }
    +  return wrapper;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.html
    new file mode 100644
    index 0000000000..ae27966df9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.ExpiringStorage
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.ExpiringStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.js
    new file mode 100644
    index 0000000000..fa40243d97
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/expiringstorage_test.js
    @@ -0,0 +1,85 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.ExpiringStorageTest');
    +goog.setTestOnly('goog.storage.ExpiringStorageTest');
    +
    +goog.require('goog.storage.ExpiringStorage');
    +goog.require('goog.storage.storage_test');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.storage.FakeMechanism');
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.ExpiringStorage(mechanism);
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +function testExpiration() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var clock = new goog.testing.MockClock(true);
    +  var storage = new goog.storage.ExpiringStorage(mechanism);
    +
    +  // No expiration.
    +  storage.set('first', 'one second', 1000);
    +  storage.set('second', 'permanent');
    +  storage.set('third', 'two seconds', 2000);
    +  storage.set('fourth', 'permanent');
    +  clock.tick(100);
    +  assertEquals('one second', storage.get('first'));
    +  assertEquals('permanent', storage.get('second'));
    +  assertEquals('two seconds', storage.get('third'));
    +  assertEquals('permanent', storage.get('fourth'));
    +
    +  // A key has expired.
    +  clock.tick(1000);
    +  assertUndefined(storage.get('first'));
    +  assertEquals('permanent', storage.get('second'));
    +  assertEquals('two seconds', storage.get('third'));
    +  assertEquals('permanent', storage.get('fourth'));
    +  assertNull(mechanism.get('first'));
    +
    +  // Add an already expired key.
    +  storage.set('fourth', 'one second again', 1000);
    +  assertNull(mechanism.get('fourth'));
    +  assertUndefined(storage.get('fourth'));
    +
    +  // Another key has expired.
    +  clock.tick(1000);
    +  assertEquals('permanent', storage.get('second'));
    +  assertUndefined(storage.get('third'));
    +  assertNull(mechanism.get('third'));
    +
    +  // Clean up.
    +  storage.remove('second');
    +  assertNull(mechanism.get('second'));
    +  assertUndefined(storage.get('second'));
    +  clock.uninstall();
    +}
    +
    +function testClockSkew() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.ExpiringStorage(mechanism);
    +  var clock = new goog.testing.MockClock(true);
    +
    +  // Simulate clock skew.
    +  clock.tick(100);
    +  storage.set('first', 'one second', 1000);
    +  clock.reset();
    +  assertUndefined(storage.get('first'));
    +  assertNull(mechanism.get('first'));
    +
    +  clock.uninstall();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorcode.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorcode.js
    new file mode 100644
    index 0000000000..3d643503ff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorcode.js
    @@ -0,0 +1,31 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines error codes to be thrown by storage mechanisms.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.ErrorCode');
    +
    +
    +/**
    + * Errors thrown by storage mechanisms.
    + * @enum {string}
    + */
    +goog.storage.mechanism.ErrorCode = {
    +  INVALID_VALUE: 'Storage mechanism: Invalid value was encountered',
    +  QUOTA_EXCEEDED: 'Storage mechanism: Quota exceeded',
    +  STORAGE_DISABLED: 'Storage mechanism: Storage disabled'
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism.js
    new file mode 100644
    index 0000000000..1daab57d69
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism.js
    @@ -0,0 +1,130 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wraps a storage mechanism with a custom error handler.
    + *
    + * @author ruilopes@google.com (Rui do Nascimento Dias Lopes)
    + */
    +
    +goog.provide('goog.storage.mechanism.ErrorHandlingMechanism');
    +
    +goog.require('goog.storage.mechanism.Mechanism');
    +
    +
    +
    +/**
    + * Wraps a storage mechanism with a custom error handler.
    + *
    + * @param {!goog.storage.mechanism.Mechanism} mechanism Underlying storage
    + *     mechanism.
    + * @param {goog.storage.mechanism.ErrorHandlingMechanism.ErrorHandler}
    + *     errorHandler An error handler.
    + * @constructor
    + * @extends {goog.storage.mechanism.Mechanism}
    + * @final
    + */
    +goog.storage.mechanism.ErrorHandlingMechanism = function(mechanism,
    +                                                         errorHandler) {
    +  goog.storage.mechanism.ErrorHandlingMechanism.base(this, 'constructor');
    +
    +  /**
    +   * The mechanism to be wrapped.
    +   * @type {!goog.storage.mechanism.Mechanism}
    +   * @private
    +   */
    +  this.mechanism_ = mechanism;
    +
    +  /**
    +   * The error handler.
    +   * @type {goog.storage.mechanism.ErrorHandlingMechanism.ErrorHandler}
    +   * @private
    +   */
    +  this.errorHandler_ = errorHandler;
    +};
    +goog.inherits(goog.storage.mechanism.ErrorHandlingMechanism,
    +              goog.storage.mechanism.Mechanism);
    +
    +
    +/**
    + * Valid storage mechanism operations.
    + * @enum {string}
    + */
    +goog.storage.mechanism.ErrorHandlingMechanism.Operation = {
    +  SET: 'set',
    +  GET: 'get',
    +  REMOVE: 'remove'
    +};
    +
    +
    +/**
    + * A function that handles errors raised in goog.storage.  Since some places in
    + * the goog.storage codebase throw strings instead of Error objects, we accept
    + * these as a valid parameter type.  It supports the following arguments:
    + *
    + * 1) The raised error (either in Error or string form);
    + * 2) The operation name which triggered the error, as defined per the
    + *    ErrorHandlingMechanism.Operation enum;
    + * 3) The key that is passed to a storage method;
    + * 4) An optional value that is passed to a storage method (only used in set
    + *    operations).
    + *
    + * @typedef {function(
    + *   (!Error|string),
    + *   goog.storage.mechanism.ErrorHandlingMechanism.Operation,
    + *   string,
    + *   *=)}
    + */
    +goog.storage.mechanism.ErrorHandlingMechanism.ErrorHandler;
    +
    +
    +/** @override */
    +goog.storage.mechanism.ErrorHandlingMechanism.prototype.set = function(key,
    +                                                                       value) {
    +  try {
    +    this.mechanism_.set(key, value);
    +  } catch (e) {
    +    this.errorHandler_(
    +        e,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.SET,
    +        key,
    +        value);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.ErrorHandlingMechanism.prototype.get = function(key) {
    +  try {
    +    return this.mechanism_.get(key);
    +  } catch (e) {
    +    this.errorHandler_(
    +        e,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.GET,
    +        key);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.ErrorHandlingMechanism.prototype.remove = function(key) {
    +  try {
    +    this.mechanism_.remove(key);
    +  } catch (e) {
    +    this.errorHandler_(
    +        e,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.REMOVE,
    +        key);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.html
    new file mode 100644
    index 0000000000..dbf13b2498
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author:  ruilopes@google.com (Rui do Nascimento Dias Lopes)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.ErrorHandlingMechanism
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.ErrorHandlingMechanismTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.js
    new file mode 100644
    index 0000000000..3de6aeb885
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/errorhandlingmechanism_test.js
    @@ -0,0 +1,77 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.ErrorHandlingMechanismTest');
    +goog.setTestOnly('goog.storage.mechanism.ErrorHandlingMechanismTest');
    +
    +goog.require('goog.storage.mechanism.ErrorHandlingMechanism');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var error = new Error();
    +
    +var submechanism = {
    +  get: function() { throw error; },
    +  set: function() { throw error; },
    +  remove: function() { throw error; }
    +};
    +
    +var handler = goog.testing.recordFunction(goog.nullFunction);
    +var mechanism;
    +
    +function setUp() {
    +  mechanism = new goog.storage.mechanism.ErrorHandlingMechanism(
    +      submechanism, handler);
    +}
    +
    +function tearDown() {
    +  handler.reset();
    +}
    +
    +function testSet() {
    +  mechanism.set('foo', 'bar');
    +  assertEquals(1, handler.getCallCount());
    +  assertArrayEquals(
    +      [
    +        error,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.SET,
    +        'foo',
    +        'bar'
    +      ],
    +      handler.getLastCall().getArguments());
    +}
    +
    +function testGet() {
    +  mechanism.get('foo');
    +  assertEquals(1, handler.getCallCount());
    +  assertArrayEquals(
    +      [
    +        error,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.GET,
    +        'foo'
    +      ],
    +      handler.getLastCall().getArguments());
    +}
    +
    +function testRemove() {
    +  mechanism.remove('foo');
    +  assertEquals(1, handler.getCallCount());
    +  assertArrayEquals(
    +      [
    +        error,
    +        goog.storage.mechanism.ErrorHandlingMechanism.Operation.REMOVE,
    +        'foo'
    +      ],
    +      handler.getLastCall().getArguments());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage.js
    new file mode 100644
    index 0000000000..e35ad5c97c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage.js
    @@ -0,0 +1,45 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides data persistence using HTML5 local storage
    + * mechanism. Local storage must be available under window.localStorage,
    + * see: http://www.w3.org/TR/webstorage/#the-localstorage-attribute.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.HTML5LocalStorage');
    +
    +goog.require('goog.storage.mechanism.HTML5WebStorage');
    +
    +
    +
    +/**
    + * Provides a storage mechanism that uses HTML5 local storage.
    + *
    + * @constructor
    + * @extends {goog.storage.mechanism.HTML5WebStorage}
    + */
    +goog.storage.mechanism.HTML5LocalStorage = function() {
    +  var storage = null;
    +  /** @preserveTry */
    +  try {
    +    // May throw an exception in cases where the local storage object
    +    // is visible but access to it is disabled.
    +    storage = window.localStorage || null;
    +  } catch (e) {}
    +  goog.storage.mechanism.HTML5LocalStorage.base(this, 'constructor', storage);
    +};
    +goog.inherits(goog.storage.mechanism.HTML5LocalStorage,
    +              goog.storage.mechanism.HTML5WebStorage);
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.html
    new file mode 100644
    index 0000000000..e1df6bd421
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.HTML5LocalStorage
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.HTML5LocalStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.js
    new file mode 100644
    index 0000000000..36ea04e43b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5localstorage_test.js
    @@ -0,0 +1,58 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.HTML5LocalStorageTest');
    +goog.setTestOnly('goog.storage.mechanism.HTML5LocalStorageTest');
    +
    +goog.require('goog.storage.mechanism.HTML5LocalStorage');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSeparationTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSharingTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismTestDefinition');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  var localStorage = new goog.storage.mechanism.HTML5LocalStorage();
    +  if (localStorage.isAvailable()) {
    +    mechanism = localStorage;
    +    // There should be at least 2 MiB.
    +    minimumQuota = 2 * 1024 * 1024;
    +    mechanism_shared = new goog.storage.mechanism.HTML5LocalStorage();
    +  }
    +}
    +
    +function tearDown() {
    +  if (!!mechanism) {
    +    mechanism.clear();
    +    mechanism = null;
    +  }
    +  if (!!mechanism_shared) {
    +    mechanism_shared.clear();
    +    mechanism_shared = null;
    +  }
    +}
    +
    +function testAvailability() {
    +  if (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('532.5') ||
    +      goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1') ||
    +      goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8')) {
    +    assertNotNull(mechanism);
    +    assertTrue(mechanism.isAvailable());
    +    assertNotNull(mechanism_shared);
    +    assertTrue(mechanism_shared.isAvailable());
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage.js
    new file mode 100644
    index 0000000000..688079a6d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage.js
    @@ -0,0 +1,46 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides data persistence using HTML5 session storage
    + * mechanism. Session storage must be available under window.sessionStorage,
    + * see: http://www.w3.org/TR/webstorage/#the-sessionstorage-attribute.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.HTML5SessionStorage');
    +
    +goog.require('goog.storage.mechanism.HTML5WebStorage');
    +
    +
    +
    +/**
    + * Provides a storage mechanism that uses HTML5 session storage.
    + *
    + * @constructor
    + * @extends {goog.storage.mechanism.HTML5WebStorage}
    + */
    +goog.storage.mechanism.HTML5SessionStorage = function() {
    +  var storage = null;
    +  /** @preserveTry */
    +  try {
    +    // May throw an exception in cases where the session storage object is
    +    // visible but access to it is disabled. For example, accessing the file
    +    // in local mode in Firefox throws 'Operation is not supported' exception.
    +    storage = window.sessionStorage || null;
    +  } catch (e) {}
    +  goog.storage.mechanism.HTML5SessionStorage.base(this, 'constructor', storage);
    +};
    +goog.inherits(goog.storage.mechanism.HTML5SessionStorage,
    +              goog.storage.mechanism.HTML5WebStorage);
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.html
    new file mode 100644
    index 0000000000..bae9a13583
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.HTML5SessionStorage
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.HTML5SessionStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.js
    new file mode 100644
    index 0000000000..b1fed0b679
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5sessionstorage_test.js
    @@ -0,0 +1,59 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.HTML5SessionStorageTest');
    +goog.setTestOnly('goog.storage.mechanism.HTML5SessionStorageTest');
    +
    +goog.require('goog.storage.mechanism.HTML5SessionStorage');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSeparationTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSharingTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismTestDefinition');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  var sessionStorage = new goog.storage.mechanism.HTML5SessionStorage();
    +  if (sessionStorage.isAvailable()) {
    +    mechanism = sessionStorage;
    +    // There should be at least 2 MiB.
    +    minimumQuota = 2 * 1024 * 1024;
    +    mechanism_shared = new goog.storage.mechanism.HTML5SessionStorage();
    +  }
    +}
    +
    +function tearDown() {
    +  if (!!mechanism) {
    +    mechanism.clear();
    +    mechanism = null;
    +  }
    +  if (!!mechanism_shared) {
    +    mechanism_shared.clear();
    +    mechanism_shared = null;
    +  }
    +}
    +
    +function testAvailability() {
    +  if (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('532.5') ||
    +      goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1') &&
    +      window.location.protocol != 'file:' ||
    +      goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8')) {
    +    assertNotNull(mechanism);
    +    assertTrue(mechanism.isAvailable());
    +    assertNotNull(mechanism_shared);
    +    assertTrue(mechanism_shared.isAvailable());
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage.js
    new file mode 100644
    index 0000000000..348ba986b4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage.js
    @@ -0,0 +1,171 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class that implements functionality common
    + * across both session and local web storage mechanisms.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.HTML5WebStorage');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.storage.mechanism.ErrorCode');
    +goog.require('goog.storage.mechanism.IterableMechanism');
    +
    +
    +
    +/**
    + * Provides a storage mechanism that uses HTML5 Web storage.
    + *
    + * @param {Storage} storage The Web storage object.
    + * @constructor
    + * @extends {goog.storage.mechanism.IterableMechanism}
    + */
    +goog.storage.mechanism.HTML5WebStorage = function(storage) {
    +  goog.storage.mechanism.HTML5WebStorage.base(this, 'constructor');
    +
    +  /**
    +   * The web storage object (window.localStorage or window.sessionStorage).
    +   * @private {Storage}
    +   */
    +  this.storage_ = storage;
    +};
    +goog.inherits(goog.storage.mechanism.HTML5WebStorage,
    +              goog.storage.mechanism.IterableMechanism);
    +
    +
    +/**
    + * The key used to check if the storage instance is available.
    + * @private {string}
    + * @const
    + */
    +goog.storage.mechanism.HTML5WebStorage.STORAGE_AVAILABLE_KEY_ = '__sak';
    +
    +
    +/**
    + * Determines whether or not the mechanism is available.
    + * It works only if the provided web storage object exists and is enabled.
    + *
    + * @return {boolean} True if the mechanism is available.
    + */
    +goog.storage.mechanism.HTML5WebStorage.prototype.isAvailable = function() {
    +  if (!this.storage_) {
    +    return false;
    +  }
    +  /** @preserveTry */
    +  try {
    +    // setItem will throw an exception if we cannot access WebStorage (e.g.,
    +    // Safari in private mode).
    +    this.storage_.setItem(
    +        goog.storage.mechanism.HTML5WebStorage.STORAGE_AVAILABLE_KEY_, '1');
    +    this.storage_.removeItem(
    +        goog.storage.mechanism.HTML5WebStorage.STORAGE_AVAILABLE_KEY_);
    +    return true;
    +  } catch (e) {
    +    return false;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.set = function(key, value) {
    +  /** @preserveTry */
    +  try {
    +    // May throw an exception if storage quota is exceeded.
    +    this.storage_.setItem(key, value);
    +  } catch (e) {
    +    // In Safari Private mode, conforming to the W3C spec, invoking
    +    // Storage.prototype.setItem will allways throw a QUOTA_EXCEEDED_ERR
    +    // exception.  Since it's impossible to verify if we're in private browsing
    +    // mode, we throw a different exception if the storage is empty.
    +    if (this.storage_.length == 0) {
    +      throw goog.storage.mechanism.ErrorCode.STORAGE_DISABLED;
    +    } else {
    +      throw goog.storage.mechanism.ErrorCode.QUOTA_EXCEEDED;
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.get = function(key) {
    +  // According to W3C specs, values can be of any type. Since we only save
    +  // strings, any other type is a storage error. If we returned nulls for
    +  // such keys, i.e., treated them as non-existent, this would lead to a
    +  // paradox where a key exists, but it does not when it is retrieved.
    +  // http://www.w3.org/TR/2009/WD-webstorage-20091029/#the-storage-interface
    +  var value = this.storage_.getItem(key);
    +  if (!goog.isString(value) && !goog.isNull(value)) {
    +    throw goog.storage.mechanism.ErrorCode.INVALID_VALUE;
    +  }
    +  return value;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.remove = function(key) {
    +  this.storage_.removeItem(key);
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.getCount = function() {
    +  return this.storage_.length;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.__iterator__ = function(
    +    opt_keys) {
    +  var i = 0;
    +  var storage = this.storage_;
    +  var newIter = new goog.iter.Iterator();
    +  newIter.next = function() {
    +    if (i >= storage.length) {
    +      throw goog.iter.StopIteration;
    +    }
    +    var key = goog.asserts.assertString(storage.key(i++));
    +    if (opt_keys) {
    +      return key;
    +    }
    +    var value = storage.getItem(key);
    +    // The value must exist and be a string, otherwise it is a storage error.
    +    if (!goog.isString(value)) {
    +      throw goog.storage.mechanism.ErrorCode.INVALID_VALUE;
    +    }
    +    return value;
    +  };
    +  return newIter;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.HTML5WebStorage.prototype.clear = function() {
    +  this.storage_.clear();
    +};
    +
    +
    +/**
    + * Gets the key for a given key index. If an index outside of
    + * [0..this.getCount()) is specified, this function returns null.
    + * @param {number} index A key index.
    + * @return {?string} A storage key, or null if the specified index is out of
    + *     range.
    + */
    +goog.storage.mechanism.HTML5WebStorage.prototype.key = function(index) {
    +  return this.storage_.key(index);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.html
    new file mode 100644
    index 0000000000..3de28e1ea7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author:  ruilopes@google.com (Rui do Nascimento Dias Lopes)
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.HTML5WebStorage
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.HTML5WebStorageTest');
    +  </script>
    +  <body>
    +  </body>
    + </head>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.js
    new file mode 100644
    index 0000000000..239478facb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/html5webstorage_test.js
    @@ -0,0 +1,120 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.setTestOnly('goog.storage.mechanism.HTML5WebStorageTest');
    +goog.provide('goog.storage.mechanism.HTML5MockStorage');
    +goog.provide('goog.storage.mechanism.HTML5WebStorageTest');
    +goog.provide('goog.storage.mechanism.MockThrowableStorage');
    +
    +goog.require('goog.storage.mechanism.ErrorCode');
    +goog.require('goog.storage.mechanism.HTML5WebStorage');
    +goog.require('goog.testing.jsunit');
    +
    +
    +
    +/**
    + * A minimal WebStorage implementation that throws exceptions for disabled
    + * storage. Since we cannot have unit tests running in Safari private mode to
    + * test this, we need to mock an exception throwing when trying to set a value.
    + *
    + * @param {boolean=} opt_isStorageDisabled If true, throws exceptions emulating
    + *     Private browsing mode.  If false, storage quota will be marked as
    + *     exceeded.
    + * @constructor
    + */
    +goog.storage.mechanism.MockThrowableStorage = function(opt_isStorageDisabled) {
    +  this.isStorageDisabled_ = !!opt_isStorageDisabled;
    +  this.length = opt_isStorageDisabled ? 0 : 1;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.MockThrowableStorage.prototype.setItem =
    +    function(key, value) {
    +  if (this.isStorageDisabled_) {
    +    throw goog.storage.mechanism.ErrorCode.STORAGE_DISABLED;
    +  } else {
    +    throw goog.storage.mechanism.ErrorCode.QUOTA_EXCEEDED;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.MockThrowableStorage.prototype.removeItem =
    +    function(key) {};
    +
    +
    +/**
    + * A very simple, dummy implementation of key(), merely to verify that calls to
    + * HTML5WebStorage#key are proxied through.
    + * @param {number} index A key index.
    + * @return {string} The key associated with that index.
    + */
    +goog.storage.mechanism.MockThrowableStorage.prototype.key = function(index) {
    +  return 'dummyKey';
    +};
    +
    +
    +
    +/**
    + * Provides an HTML5WebStorage wrapper for MockThrowableStorage.
    + *
    + * @constructor
    + * @extends {goog.storage.mechanism.HTML5WebStorage}
    + */
    +goog.storage.mechanism.HTML5MockStorage = function(opt_isStorageDisabled) {
    +  goog.base(
    +      this,
    +      new goog.storage.mechanism.MockThrowableStorage(opt_isStorageDisabled));
    +};
    +goog.inherits(goog.storage.mechanism.HTML5MockStorage,
    +              goog.storage.mechanism.HTML5WebStorage);
    +
    +
    +function testIsNotAvailableWhenQuotaExceeded() {
    +  var storage = new goog.storage.mechanism.HTML5MockStorage(false);
    +  assertFalse(storage.isAvailable());
    +}
    +
    +function testIsNotAvailableWhenStorageDisabled() {
    +  var storage = new goog.storage.mechanism.HTML5MockStorage(true);
    +  assertFalse(storage.isAvailable());
    +}
    +
    +function testSetThrowsExceptionWhenQuotaExceeded() {
    +  var storage = new goog.storage.mechanism.HTML5MockStorage(false);
    +  var isQuotaExceeded = false;
    +  try {
    +    storage.set('foobar', '1');
    +  } catch (e) {
    +    isQuotaExceeded = e == goog.storage.mechanism.ErrorCode.QUOTA_EXCEEDED;
    +  }
    +  assertTrue(isQuotaExceeded);
    +}
    +
    +function testSetThrowsExceptionWhenStorageDisabled() {
    +  var storage = new goog.storage.mechanism.HTML5MockStorage(true);
    +  var isStorageDisabled = false;
    +  try {
    +    storage.set('foobar', '1');
    +  } catch (e) {
    +    isStorageDisabled = e == goog.storage.mechanism.ErrorCode.STORAGE_DISABLED;
    +  }
    +  assertTrue(isStorageDisabled);
    +}
    +
    +function testKeyIterationWithKeyMethod() {
    +  var storage = new goog.storage.mechanism.HTML5MockStorage(true);
    +  assertEquals('dummyKey', storage.key(1));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata.js
    new file mode 100644
    index 0000000000..3b9875a6ef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata.js
    @@ -0,0 +1,284 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides data persistence using IE userData mechanism.
    + * UserData uses proprietary Element.addBehavior(), Element.load(),
    + * Element.save(), and Element.XMLDocument() methods, see:
    + * http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.IEUserData');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.storage.mechanism.ErrorCode');
    +goog.require('goog.storage.mechanism.IterableMechanism');
    +goog.require('goog.structs.Map');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Provides a storage mechanism using IE userData.
    + *
    + * @param {string} storageKey The key (store name) to store the data under.
    + * @param {string=} opt_storageNodeId The ID of the associated HTML element,
    + *     one will be created if not provided.
    + * @constructor
    + * @extends {goog.storage.mechanism.IterableMechanism}
    + * @final
    + */
    +goog.storage.mechanism.IEUserData = function(storageKey, opt_storageNodeId) {
    +  goog.storage.mechanism.IEUserData.base(this, 'constructor');
    +
    +  // Tested on IE6, IE7 and IE8. It seems that IE9 introduces some security
    +  // features which make persistent (loaded) node attributes invisible from
    +  // JavaScript.
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    if (!goog.storage.mechanism.IEUserData.storageMap_) {
    +      goog.storage.mechanism.IEUserData.storageMap_ = new goog.structs.Map();
    +    }
    +    this.storageNode_ = /** @type {Element} */ (
    +        goog.storage.mechanism.IEUserData.storageMap_.get(storageKey));
    +    if (!this.storageNode_) {
    +      if (opt_storageNodeId) {
    +        this.storageNode_ = document.getElementById(opt_storageNodeId);
    +      } else {
    +        this.storageNode_ = document.createElement('userdata');
    +        // This is a special IE-only method letting us persist data.
    +        this.storageNode_['addBehavior']('#default#userData');
    +        document.body.appendChild(this.storageNode_);
    +      }
    +      goog.storage.mechanism.IEUserData.storageMap_.set(
    +          storageKey, this.storageNode_);
    +    }
    +    this.storageKey_ = storageKey;
    +
    +    /** @preserveTry */
    +    try {
    +      // Availability check.
    +      this.loadNode_();
    +    } catch (e) {
    +      this.storageNode_ = null;
    +    }
    +  }
    +};
    +goog.inherits(goog.storage.mechanism.IEUserData,
    +              goog.storage.mechanism.IterableMechanism);
    +
    +
    +/**
    + * Encoding map for characters which are not encoded by encodeURIComponent().
    + * See encodeKey_ documentation for encoding details.
    + *
    + * @type {!Object}
    + * @const
    + */
    +goog.storage.mechanism.IEUserData.ENCODE_MAP = {
    +  '.': '.2E',
    +  '!': '.21',
    +  '~': '.7E',
    +  '*': '.2A',
    +  '\'': '.27',
    +  '(': '.28',
    +  ')': '.29',
    +  '%': '.'
    +};
    +
    +
    +/**
    + * Global storageKey to storageNode map, so we save on reloading the storage.
    + *
    + * @type {goog.structs.Map}
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.storageMap_ = null;
    +
    +
    +/**
    + * The document element used for storing data.
    + *
    + * @type {Element}
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.prototype.storageNode_ = null;
    +
    +
    +/**
    + * The key to store the data under.
    + *
    + * @type {?string}
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.prototype.storageKey_ = null;
    +
    +
    +/**
    + * Encodes anything other than [-a-zA-Z0-9_] using a dot followed by hex,
    + * and prefixes with underscore to form a valid and safe HTML attribute name.
    + *
    + * We use URI encoding to do the initial heavy lifting, then escape the
    + * remaining characters that we can't use. Since a valid attribute name can't
    + * contain the percent sign (%), we use a dot (.) as an escape character.
    + *
    + * @param {string} key The key to be encoded.
    + * @return {string} The encoded key.
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.encodeKey_ = function(key) {
    +  // encodeURIComponent leaves - _ . ! ~ * ' ( ) unencoded.
    +  return '_' + encodeURIComponent(key).replace(/[.!~*'()%]/g, function(c) {
    +    return goog.storage.mechanism.IEUserData.ENCODE_MAP[c];
    +  });
    +};
    +
    +
    +/**
    + * Decodes a dot-encoded and character-prefixed key.
    + * See encodeKey_ documentation for encoding details.
    + *
    + * @param {string} key The key to be decoded.
    + * @return {string} The decoded key.
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.decodeKey_ = function(key) {
    +  return decodeURIComponent(key.replace(/\./g, '%')).substr(1);
    +};
    +
    +
    +/**
    + * Determines whether or not the mechanism is available.
    + *
    + * @return {boolean} True if the mechanism is available.
    + */
    +goog.storage.mechanism.IEUserData.prototype.isAvailable = function() {
    +  return !!this.storageNode_;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.set = function(key, value) {
    +  this.storageNode_.setAttribute(
    +      goog.storage.mechanism.IEUserData.encodeKey_(key), value);
    +  this.saveNode_();
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.get = function(key) {
    +  // According to Microsoft, values can be strings, numbers or booleans. Since
    +  // we only save strings, any other type is a storage error. If we returned
    +  // nulls for such keys, i.e., treated them as non-existent, this would lead
    +  // to a paradox where a key exists, but it does not when it is retrieved.
    +  // http://msdn.microsoft.com/en-us/library/ms531348(v=vs.85).aspx
    +  var value = this.storageNode_.getAttribute(
    +      goog.storage.mechanism.IEUserData.encodeKey_(key));
    +  if (!goog.isString(value) && !goog.isNull(value)) {
    +    throw goog.storage.mechanism.ErrorCode.INVALID_VALUE;
    +  }
    +  return value;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.remove = function(key) {
    +  this.storageNode_.removeAttribute(
    +      goog.storage.mechanism.IEUserData.encodeKey_(key));
    +  this.saveNode_();
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.getCount = function() {
    +  return this.getNode_().attributes.length;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.__iterator__ = function(opt_keys) {
    +  var i = 0;
    +  var attributes = this.getNode_().attributes;
    +  var newIter = new goog.iter.Iterator();
    +  newIter.next = function() {
    +    if (i >= attributes.length) {
    +      throw goog.iter.StopIteration;
    +    }
    +    var item = goog.asserts.assert(attributes[i++]);
    +    if (opt_keys) {
    +      return goog.storage.mechanism.IEUserData.decodeKey_(item.nodeName);
    +    }
    +    var value = item.nodeValue;
    +    // The value must exist and be a string, otherwise it is a storage error.
    +    if (!goog.isString(value)) {
    +      throw goog.storage.mechanism.ErrorCode.INVALID_VALUE;
    +    }
    +    return value;
    +  };
    +  return newIter;
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.IEUserData.prototype.clear = function() {
    +  var node = this.getNode_();
    +  for (var left = node.attributes.length; left > 0; left--) {
    +    node.removeAttribute(node.attributes[left - 1].nodeName);
    +  }
    +  this.saveNode_();
    +};
    +
    +
    +/**
    + * Loads the underlying storage node to the state we saved it to before.
    + *
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.prototype.loadNode_ = function() {
    +  // This is a special IE-only method on Elements letting us persist data.
    +  this.storageNode_['load'](this.storageKey_);
    +};
    +
    +
    +/**
    + * Saves the underlying storage node.
    + *
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.prototype.saveNode_ = function() {
    +  /** @preserveTry */
    +  try {
    +    // This is a special IE-only method on Elements letting us persist data.
    +    // Do not try to assign this.storageNode_['save'] to a variable, it does
    +    // not work. May throw an exception when the quota is exceeded.
    +    this.storageNode_['save'](this.storageKey_);
    +  } catch (e) {
    +    throw goog.storage.mechanism.ErrorCode.QUOTA_EXCEEDED;
    +  }
    +};
    +
    +
    +/**
    + * Returns the storage node.
    + *
    + * @return {!Element} Storage DOM Element.
    + * @private
    + */
    +goog.storage.mechanism.IEUserData.prototype.getNode_ = function() {
    +  // This is a special IE-only property letting us browse persistent data.
    +  var doc = /** @type {Document} */ (this.storageNode_['XMLDocument']);
    +  return doc.documentElement;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.html
    new file mode 100644
    index 0000000000..85e2469558
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.IEUserData
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.IEUserDataTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.js
    new file mode 100644
    index 0000000000..3393a30603
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/ieuserdata_test.js
    @@ -0,0 +1,75 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.IEUserDataTest');
    +goog.setTestOnly('goog.storage.mechanism.IEUserDataTest');
    +
    +goog.require('goog.storage.mechanism.IEUserData');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSeparationTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSharingTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismTestDefinition');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function setUp() {
    +  var ieUserData = new goog.storage.mechanism.IEUserData('test');
    +  if (ieUserData.isAvailable()) {
    +    mechanism = ieUserData;
    +    // There should be at least 32 KiB.
    +    minimumQuota = 32 * 1024;
    +    mechanism_shared = new goog.storage.mechanism.IEUserData('test');
    +    mechanism_separate = new goog.storage.mechanism.IEUserData('test2');
    +  }
    +}
    +
    +function tearDown() {
    +  if (!!mechanism) {
    +    mechanism.clear();
    +    mechanism = null;
    +  }
    +  if (!!mechanism_shared) {
    +    mechanism_shared.clear();
    +    mechanism_shared = null;
    +  }
    +  if (!!mechanism_separate) {
    +    mechanism_separate.clear();
    +    mechanism_separate = null;
    +  }
    +}
    +
    +function testAvailability() {
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    assertNotNull(mechanism);
    +    assertTrue(mechanism.isAvailable());
    +    assertNotNull(mechanism_shared);
    +    assertTrue(mechanism_shared.isAvailable());
    +    assertNotNull(mechanism_separate);
    +    assertTrue(mechanism_separate.isAvailable());
    +  }
    +}
    +
    +function testEncoding() {
    +  function assertEncodingPair(cleartext, encoded) {
    +    assertEquals(encoded,
    +                 goog.storage.mechanism.IEUserData.encodeKey_(cleartext));
    +    assertEquals(cleartext,
    +                 goog.storage.mechanism.IEUserData.decodeKey_(encoded));
    +  }
    +  assertEncodingPair('simple', '_simple');
    +  assertEncodingPair('aa.bb%cc!\0$\u4e00.',
    +                     '_aa.2Ebb.25cc.21.00.24.E4.B8.80.2E');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanism.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanism.js
    new file mode 100644
    index 0000000000..b5c6ced8ea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanism.js
    @@ -0,0 +1,85 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Interface for storing, retieving and scanning data using some
    + * persistence mechanism.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.IterableMechanism');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.iter');
    +goog.require('goog.storage.mechanism.Mechanism');
    +
    +
    +
    +/**
    + * Interface for all iterable storage mechanisms.
    + *
    + * @constructor
    + * @extends {goog.storage.mechanism.Mechanism}
    + */
    +goog.storage.mechanism.IterableMechanism = function() {
    +  goog.storage.mechanism.IterableMechanism.base(this, 'constructor');
    +};
    +goog.inherits(goog.storage.mechanism.IterableMechanism,
    +              goog.storage.mechanism.Mechanism);
    +
    +
    +/**
    + * Get the number of stored key-value pairs.
    + *
    + * Could be overridden in a subclass, as the default implementation is not very
    + * efficient - it iterates over all keys.
    + *
    + * @return {number} Number of stored elements.
    + */
    +goog.storage.mechanism.IterableMechanism.prototype.getCount = function() {
    +  var count = 0;
    +  goog.iter.forEach(this.__iterator__(true), function(key) {
    +    goog.asserts.assertString(key);
    +    count++;
    +  });
    +  return count;
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the elements in the storage. Will
    + * throw goog.iter.StopIteration after the last element.
    + *
    + * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
    + *     over the values.  The default value is false.
    + * @return {!goog.iter.Iterator} The iterator.
    + */
    +goog.storage.mechanism.IterableMechanism.prototype.__iterator__ =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * Remove all key-value pairs.
    + *
    + * Could be overridden in a subclass, as the default implementation is not very
    + * efficient - it iterates over all keys.
    + */
    +goog.storage.mechanism.IterableMechanism.prototype.clear = function() {
    +  var keys = goog.iter.toArray(this.__iterator__(true));
    +  var selfObj = this;
    +  goog.array.forEach(keys, function(key) {
    +    selfObj.remove(key);
    +  });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanismtester.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanismtester.js
    new file mode 100644
    index 0000000000..7e65b43a4a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/iterablemechanismtester.js
    @@ -0,0 +1,117 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for the iterable storage mechanism interface.
    + *
    + * These tests should be included in tests of any class extending
    + * goog.storage.mechanism.IterableMechanism.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.iterableMechanismTester');
    +
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.storage.mechanism.IterableMechanism');
    +goog.require('goog.testing.asserts');
    +goog.setTestOnly('iterableMechanismTester');
    +
    +
    +var mechanism = null;
    +
    +
    +function testCount() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  assertEquals(0, mechanism.getCount());
    +  mechanism.set('first', 'one');
    +  assertEquals(1, mechanism.getCount());
    +  mechanism.set('second', 'two');
    +  assertEquals(2, mechanism.getCount());
    +  mechanism.set('first', 'three');
    +  assertEquals(2, mechanism.getCount());
    +}
    +
    +
    +function testIteratorBasics() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  assertEquals('first', mechanism.__iterator__(true).next());
    +  assertEquals('one', mechanism.__iterator__(false).next());
    +  var iterator = mechanism.__iterator__();
    +  assertEquals('one', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    +
    +
    +function testIteratorWithTwoValues() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('second', 'two');
    +  assertSameElements(['one', 'two'], goog.iter.toArray(mechanism));
    +  assertSameElements(['first', 'second'],
    +                     goog.iter.toArray(mechanism.__iterator__(true)));
    +}
    +
    +
    +function testClear() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('second', 'two');
    +  mechanism.clear();
    +  assertNull(mechanism.get('first'));
    +  assertNull(mechanism.get('second'));
    +  assertEquals(0, mechanism.getCount());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(mechanism.__iterator__(true).next));
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(mechanism.__iterator__(false).next));
    +}
    +
    +
    +function testClearClear() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.clear();
    +  mechanism.clear();
    +  assertEquals(0, mechanism.getCount());
    +}
    +
    +
    +function testIteratorWithWeirdKeys() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set(' ', 'space');
    +  mechanism.set('=+!@#$%^&*()-_\\|;:\'",./<>?[]{}~`', 'control');
    +  mechanism.set(
    +      '\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341', 'ten');
    +  assertEquals(3, mechanism.getCount());
    +  assertSameElements([
    +    ' ',
    +    '=+!@#$%^&*()-_\\|;:\'",./<>?[]{}~`',
    +    '\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341'
    +  ], goog.iter.toArray(mechanism.__iterator__(true)));
    +  mechanism.clear();
    +  assertEquals(0, mechanism.getCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanism.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanism.js
    new file mode 100644
    index 0000000000..4a41cd9ab7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanism.js
    @@ -0,0 +1,56 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Abstract interface for storing and retrieving data using
    + * some persistence mechanism.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.Mechanism');
    +
    +
    +
    +/**
    + * Basic interface for all storage mechanisms.
    + *
    + * @constructor
    + */
    +goog.storage.mechanism.Mechanism = function() {};
    +
    +
    +/**
    + * Set a value for a key.
    + *
    + * @param {string} key The key to set.
    + * @param {string} value The string to save.
    + */
    +goog.storage.mechanism.Mechanism.prototype.set = goog.abstractMethod;
    +
    +
    +/**
    + * Get the value stored under a key.
    + *
    + * @param {string} key The key to get.
    + * @return {?string} The corresponding value, null if not found.
    + */
    +goog.storage.mechanism.Mechanism.prototype.get = goog.abstractMethod;
    +
    +
    +/**
    + * Remove a key and its value.
    + *
    + * @param {string} key The key to remove.
    + */
    +goog.storage.mechanism.Mechanism.prototype.remove = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory.js
    new file mode 100644
    index 0000000000..ac1da6796e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory.js
    @@ -0,0 +1,112 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides factory methods for selecting the best storage
    + * mechanism, depending on availability and needs.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.mechanismfactory');
    +
    +goog.require('goog.storage.mechanism.HTML5LocalStorage');
    +goog.require('goog.storage.mechanism.HTML5SessionStorage');
    +goog.require('goog.storage.mechanism.IEUserData');
    +goog.require('goog.storage.mechanism.PrefixedMechanism');
    +
    +
    +/**
    + * The key to shared userData storage.
    + * @type {string}
    + */
    +goog.storage.mechanism.mechanismfactory.USER_DATA_SHARED_KEY =
    +    'UserDataSharedStore';
    +
    +
    +/**
    + * Returns the best local storage mechanism, or null if unavailable.
    + * Local storage means that the database is placed on user's computer.
    + * The key-value database is normally shared between all the code paths
    + * that request it, so using an optional namespace is recommended. This
    + * provides separation and makes key collisions unlikely.
    + *
    + * @param {string=} opt_namespace Restricts the visibility to given namespace.
    + * @return {goog.storage.mechanism.IterableMechanism} Created mechanism or null.
    + */
    +goog.storage.mechanism.mechanismfactory.create = function(opt_namespace) {
    +  return goog.storage.mechanism.mechanismfactory.createHTML5LocalStorage(
    +      opt_namespace) ||
    +      goog.storage.mechanism.mechanismfactory.createIEUserData(opt_namespace);
    +};
    +
    +
    +/**
    + * Returns an HTML5 local storage mechanism, or null if unavailable.
    + * Since the HTML5 local storage does not support namespaces natively,
    + * and the key-value database is shared between all the code paths
    + * that request it, it is recommended that an optional namespace is
    + * used to provide key separation employing a prefix.
    + *
    + * @param {string=} opt_namespace Restricts the visibility to given namespace.
    + * @return {goog.storage.mechanism.IterableMechanism} Created mechanism or null.
    + */
    +goog.storage.mechanism.mechanismfactory.createHTML5LocalStorage = function(
    +    opt_namespace) {
    +  var storage = new goog.storage.mechanism.HTML5LocalStorage();
    +  if (storage.isAvailable()) {
    +    return opt_namespace ? new goog.storage.mechanism.PrefixedMechanism(
    +        storage, opt_namespace) : storage;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns an HTML5 session storage mechanism, or null if unavailable.
    + * Since the HTML5 session storage does not support namespaces natively,
    + * and the key-value database is shared between all the code paths
    + * that request it, it is recommended that an optional namespace is
    + * used to provide key separation employing a prefix.
    + *
    + * @param {string=} opt_namespace Restricts the visibility to given namespace.
    + * @return {goog.storage.mechanism.IterableMechanism} Created mechanism or null.
    + */
    +goog.storage.mechanism.mechanismfactory.createHTML5SessionStorage = function(
    +    opt_namespace) {
    +  var storage = new goog.storage.mechanism.HTML5SessionStorage();
    +  if (storage.isAvailable()) {
    +    return opt_namespace ? new goog.storage.mechanism.PrefixedMechanism(
    +        storage, opt_namespace) : storage;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns an IE userData local storage mechanism, or null if unavailable.
    + * Using an optional namespace is recommended to provide separation and
    + * avoid key collisions.
    + *
    + * @param {string=} opt_namespace Restricts the visibility to given namespace.
    + * @return {goog.storage.mechanism.IterableMechanism} Created mechanism or null.
    + */
    +goog.storage.mechanism.mechanismfactory.createIEUserData = function(
    +    opt_namespace) {
    +  var storage = new goog.storage.mechanism.IEUserData(opt_namespace ||
    +      goog.storage.mechanism.mechanismfactory.USER_DATA_SHARED_KEY);
    +  if (storage.isAvailable()) {
    +    return storage;
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.html
    new file mode 100644
    index 0000000000..59a8e36968
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.mechanismfactory
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.mechanismfactoryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.js
    new file mode 100644
    index 0000000000..a07e289cb4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismfactory_test.js
    @@ -0,0 +1,49 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.mechanismfactoryTest');
    +goog.setTestOnly('goog.storage.mechanism.mechanismfactoryTest');
    +
    +goog.require('goog.storage.mechanism.mechanismfactory');
    +goog.require('goog.testing.jsunit');
    +
    +function setUp() {
    +  mechanism = goog.storage.mechanism.mechanismfactory.create('test');
    +  mechanism_shared = goog.storage.mechanism.mechanismfactory.create('test');
    +  mechanism_separate = goog.storage.mechanism.mechanismfactory.create('test2');
    +}
    +
    +function tearDown() {
    +  if (!!mechanism) {
    +    mechanism.clear();
    +    mechanism = null;
    +  }
    +  if (!!mechanism_shared) {
    +    mechanism_shared.clear();
    +    mechanism_shared = null;
    +  }
    +  if (!!mechanism_separate) {
    +    mechanism_separate.clear();
    +    mechanism_separate = null;
    +  }
    +}
    +
    +function testAvailability() {
    +  var probe = goog.storage.mechanism.mechanismfactory.create();
    +  if (!!probe) {
    +    assertNotNull(mechanism);
    +    assertNotNull(mechanism_shared);
    +    assertNotNull(mechanism_separate);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismseparationtester.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismseparationtester.js
    new file mode 100644
    index 0000000000..a2b3264b85
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismseparationtester.js
    @@ -0,0 +1,89 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for storage mechanism separation.
    + *
    + * These tests should be included by tests of any mechanism which natively
    + * implements namespaces. There is no need to include those tests for mechanisms
    + * extending goog.storage.mechanism.PrefixedMechanism. Make sure a different
    + * namespace is used for each object.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.mechanismSeparationTester');
    +
    +goog.require('goog.iter.StopIteration');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismTestDefinition');
    +goog.require('goog.testing.asserts');
    +
    +goog.setTestOnly('goog.storage.mechanism.mechanismSeparationTester');
    +
    +
    +function testSeparateSet() {
    +  if (!mechanism || !mechanism_separate) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  assertNull(mechanism_separate.get('first'));
    +  assertEquals(0, mechanism_separate.getCount());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(mechanism_separate.__iterator__().next));
    +}
    +
    +
    +function testSeparateSetInverse() {
    +  if (!mechanism || !mechanism_separate) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism_separate.set('first', 'two');
    +  assertEquals('one', mechanism.get('first'));
    +  assertEquals(1, mechanism.getCount());
    +  var iterator = mechanism.__iterator__();
    +  assertEquals('one', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    +
    +
    +function testSeparateRemove() {
    +  if (!mechanism || !mechanism_separate) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism_separate.remove('first');
    +  assertEquals('one', mechanism.get('first'));
    +  assertEquals(1, mechanism.getCount());
    +  var iterator = mechanism.__iterator__();
    +  assertEquals('one', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    +
    +
    +function testSeparateClean() {
    +  if (!mechanism || !mechanism_separate) {
    +    return;
    +  }
    +  mechanism_separate.set('first', 'two');
    +  mechanism.clear();
    +  assertEquals('two', mechanism_separate.get('first'));
    +  assertEquals(1, mechanism_separate.getCount());
    +  var iterator = mechanism_separate.__iterator__();
    +  assertEquals('two', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismsharingtester.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismsharingtester.js
    new file mode 100644
    index 0000000000..c40972e52e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismsharingtester.js
    @@ -0,0 +1,85 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for storage mechanism sharing.
    + *
    + * These tests should be included in tests of any storage mechanism in which
    + * separate mechanism instances share the same underlying storage. Most (if
    + * not all) storage mechanisms should have this property. If the mechanism
    + * employs namespaces, make sure the same namespace is used for both objects.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.mechanismSharingTester');
    +
    +goog.require('goog.iter.StopIteration');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismTestDefinition');
    +goog.require('goog.testing.asserts');
    +
    +
    +goog.setTestOnly('goog.storage.mechanism.mechanismSharingTester');
    +
    +function testSharedSet() {
    +  if (!mechanism || !mechanism_shared) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  assertEquals('one', mechanism_shared.get('first'));
    +  assertEquals(1, mechanism_shared.getCount());
    +  var iterator = mechanism_shared.__iterator__();
    +  assertEquals('one', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    +
    +
    +function testSharedSetInverse() {
    +  if (!mechanism || !mechanism_shared) {
    +    return;
    +  }
    +  mechanism_shared.set('first', 'two');
    +  assertEquals('two', mechanism.get('first'));
    +  assertEquals(1, mechanism.getCount());
    +  var iterator = mechanism.__iterator__();
    +  assertEquals('two', iterator.next());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(iterator.next));
    +}
    +
    +
    +function testSharedRemove() {
    +  if (!mechanism || !mechanism_shared) {
    +    return;
    +  }
    +  mechanism_shared.set('first', 'three');
    +  mechanism.remove('first');
    +  assertNull(mechanism_shared.get('first'));
    +  assertEquals(0, mechanism_shared.getCount());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(mechanism_shared.__iterator__().next));
    +}
    +
    +
    +function testSharedClean() {
    +  if (!mechanism || !mechanism_shared) {
    +    return;
    +  }
    +  mechanism.set('first', 'four');
    +  mechanism_shared.clear();
    +  assertEquals(0, mechanism.getCount());
    +  assertEquals(goog.iter.StopIteration,
    +               assertThrows(mechanism.__iterator__().next));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtestdefinition.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtestdefinition.js
    new file mode 100644
    index 0000000000..0e91e71642
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtestdefinition.js
    @@ -0,0 +1,29 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview This shim namespace defines the shared
    + * mechanism variables used in mechanismSeparationTester
    + * and mechanismSelectionTester. This exists to allow test compilation
    + * to work correctly for these legacy tests.
    + * @visibility {//visibility:private}
    + */
    +
    +goog.provide('goog.storage.mechanism.mechanismTestDefinition');
    +goog.setTestOnly('goog.storage.mechanism.mechanismTestDefinition');
    +
    +var mechanism;
    +var mechanism_shared;
    +var mechanism_separate;
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtester.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtester.js
    new file mode 100644
    index 0000000000..0c37eb9c6b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/mechanismtester.js
    @@ -0,0 +1,199 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for the abstract storage mechanism interface.
    + *
    + * These tests should be included in tests of any class extending
    + * goog.storage.mechanism.Mechanism.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.mechanismTester');
    +
    +goog.require('goog.storage.mechanism.ErrorCode');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +goog.setTestOnly('goog.storage.mechanism.mechanismTester');
    +
    +
    +var mechanism = null;
    +var minimumQuota = 0;
    +
    +
    +function testSetGet() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  assertEquals('one', mechanism.get('first'));
    +}
    +
    +
    +function testChange() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('first', 'two');
    +  assertEquals('two', mechanism.get('first'));
    +}
    +
    +
    +function testRemove() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.remove('first');
    +  assertNull(mechanism.get('first'));
    +}
    +
    +
    +function testSetRemoveSet() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.remove('first');
    +  mechanism.set('first', 'one');
    +  assertEquals('one', mechanism.get('first'));
    +}
    +
    +
    +function testRemoveRemove() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.remove('first');
    +  mechanism.remove('first');
    +  assertNull(mechanism.get('first'));
    +}
    +
    +
    +function testSetTwo() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('second', 'two');
    +  assertEquals('one', mechanism.get('first'));
    +  assertEquals('two', mechanism.get('second'));
    +}
    +
    +
    +function testChangeTwo() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('second', 'two');
    +  mechanism.set('second', 'three');
    +  mechanism.set('first', 'four');
    +  assertEquals('four', mechanism.get('first'));
    +  assertEquals('three', mechanism.get('second'));
    +}
    +
    +
    +function testSetRemoveThree() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('first', 'one');
    +  mechanism.set('second', 'two');
    +  mechanism.set('third', 'three');
    +  mechanism.remove('second');
    +  assertNull(mechanism.get('second'));
    +  assertEquals('one', mechanism.get('first'));
    +  assertEquals('three', mechanism.get('third'));
    +  mechanism.remove('first');
    +  assertNull(mechanism.get('first'));
    +  assertEquals('three', mechanism.get('third'));
    +  mechanism.remove('third');
    +  assertNull(mechanism.get('third'));
    +}
    +
    +
    +function testEmptyValue() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  mechanism.set('third', '');
    +  assertEquals('', mechanism.get('third'));
    +}
    +
    +
    +function testWeirdKeys() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  // Some weird keys. We leave out some tests for some browsers where they
    +  // trigger browser bugs, and where the keys are too obscure to prepare a
    +  // workaround.
    +  mechanism.set(' ', 'space');
    +  mechanism.set('=+!@#$%^&*()-_\\|;:\'",./<>?[]{}~`', 'control');
    +  mechanism.set(
    +      '\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341', 'ten');
    +  mechanism.set('\0', 'null');
    +  mechanism.set('\0\0', 'double null');
    +  mechanism.set('\0A', 'null A');
    +  mechanism.set('', 'zero');
    +  assertEquals('space', mechanism.get(' '));
    +  assertEquals('control', mechanism.get('=+!@#$%^&*()-_\\|;:\'",./<>?[]{}~`'));
    +  assertEquals('ten', mechanism.get(
    +      '\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341'));
    +  if (!goog.userAgent.IE) {
    +    // IE does not properly handle nulls in HTML5 localStorage keys (IE8, IE9).
    +    // https://connect.microsoft.com/IE/feedback/details/667799/
    +    assertEquals('null', mechanism.get('\0'));
    +    assertEquals('double null', mechanism.get('\0\0'));
    +    assertEquals('null A', mechanism.get('\0A'));
    +  }
    +  if (!goog.userAgent.GECKO) {
    +    // Firefox does not properly handle the empty key (FF 3.5, 3.6, 4.0).
    +    // https://bugzilla.mozilla.org/show_bug.cgi?id=510849
    +    assertEquals('zero', mechanism.get(''));
    +  }
    +}
    +
    +
    +function testQuota() {
    +  if (!mechanism) {
    +    return;
    +  }
    +  // This test might crash Safari 4, so it is disabled for this version.
    +  // It works fine on Safari 3 and Safari 5.
    +  if (goog.userAgent.product.SAFARI &&
    +      goog.userAgent.product.isVersion(4) &&
    +      !goog.userAgent.product.isVersion(5)) {
    +    return;
    +  }
    +  var buffer = '\u03ff'; // 2 bytes
    +  var savedBytes = 0;
    +  try {
    +    while (buffer.length < minimumQuota) {
    +      buffer = buffer + buffer;
    +      mechanism.set('foo', buffer);
    +      savedBytes = buffer.length;
    +    }
    +  } catch (ex) {
    +    if (ex != goog.storage.mechanism.ErrorCode.QUOTA_EXCEEDED) {
    +      throw ex;
    +    }
    +  }
    +  mechanism.remove('foo');
    +  assertTrue(savedBytes >= minimumQuota);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism.js
    new file mode 100644
    index 0000000000..61414c6136
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism.js
    @@ -0,0 +1,98 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wraps an iterable storage mechanism and creates artificial
    + * namespaces using a prefix in the global namespace.
    + *
    + */
    +
    +goog.provide('goog.storage.mechanism.PrefixedMechanism');
    +
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.storage.mechanism.IterableMechanism');
    +
    +
    +
    +/**
    + * Wraps an iterable storage mechanism and creates artificial namespaces.
    + *
    + * @param {!goog.storage.mechanism.IterableMechanism} mechanism Underlying
    + *     iterable storage mechanism.
    + * @param {string} prefix Prefix for creating an artificial namespace.
    + * @constructor
    + * @extends {goog.storage.mechanism.IterableMechanism}
    + * @final
    + */
    +goog.storage.mechanism.PrefixedMechanism = function(mechanism, prefix) {
    +  goog.storage.mechanism.PrefixedMechanism.base(this, 'constructor');
    +  this.mechanism_ = mechanism;
    +  this.prefix_ = prefix + '::';
    +};
    +goog.inherits(goog.storage.mechanism.PrefixedMechanism,
    +              goog.storage.mechanism.IterableMechanism);
    +
    +
    +/**
    + * The mechanism to be prefixed.
    + *
    + * @type {goog.storage.mechanism.IterableMechanism}
    + * @private
    + */
    +goog.storage.mechanism.PrefixedMechanism.prototype.mechanism_ = null;
    +
    +
    +/**
    + * The prefix for creating artificial namespaces.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.storage.mechanism.PrefixedMechanism.prototype.prefix_ = '';
    +
    +
    +/** @override */
    +goog.storage.mechanism.PrefixedMechanism.prototype.set = function(key, value) {
    +  this.mechanism_.set(this.prefix_ + key, value);
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.PrefixedMechanism.prototype.get = function(key) {
    +  return this.mechanism_.get(this.prefix_ + key);
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.PrefixedMechanism.prototype.remove = function(key) {
    +  this.mechanism_.remove(this.prefix_ + key);
    +};
    +
    +
    +/** @override */
    +goog.storage.mechanism.PrefixedMechanism.prototype.__iterator__ = function(
    +    opt_keys) {
    +  var subIter = this.mechanism_.__iterator__(true);
    +  var selfObj = this;
    +  var newIter = new goog.iter.Iterator();
    +  newIter.next = function() {
    +    var key = /** @type {string} */ (subIter.next());
    +    while (key.substr(0, selfObj.prefix_.length) != selfObj.prefix_) {
    +      key = /** @type {string} */ (subIter.next());
    +    }
    +    return opt_keys ? key.substr(selfObj.prefix_.length) :
    +                      selfObj.mechanism_.get(key);
    +  };
    +  return newIter;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.html b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.html
    new file mode 100644
    index 0000000000..71cf66e1e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.mechanism.PrefixedMechanism
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.mechanism.PrefixedMechanismTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.js b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.js
    new file mode 100644
    index 0000000000..963fec4df9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/mechanism/prefixedmechanism_test.js
    @@ -0,0 +1,61 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.mechanism.PrefixedMechanismTest');
    +goog.setTestOnly('goog.storage.mechanism.PrefixedMechanismTest');
    +
    +goog.require('goog.storage.mechanism.HTML5LocalStorage');
    +goog.require('goog.storage.mechanism.PrefixedMechanism');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSeparationTester');
    +/** @suppress {extraRequire} */
    +goog.require('goog.storage.mechanism.mechanismSharingTester');
    +goog.require('goog.testing.jsunit');
    +
    +var submechanism = null;
    +
    +function setUp() {
    +  submechanism = new goog.storage.mechanism.HTML5LocalStorage();
    +  if (submechanism.isAvailable()) {
    +    mechanism = new goog.storage.mechanism.PrefixedMechanism(
    +        submechanism, 'test');
    +    mechanism_shared = new goog.storage.mechanism.PrefixedMechanism(
    +        submechanism, 'test');
    +    mechanism_separate = new goog.storage.mechanism.PrefixedMechanism(
    +        submechanism, 'test2');
    +  }
    +}
    +
    +function tearDown() {
    +  if (!!mechanism) {
    +    mechanism.clear();
    +    mechanism = null;
    +  }
    +  if (!!mechanism_shared) {
    +    mechanism_shared.clear();
    +    mechanism_shared = null;
    +  }
    +  if (!!mechanism_separate) {
    +    mechanism_separate.clear();
    +    mechanism_separate = null;
    +  }
    +}
    +
    +function testAvailability() {
    +  if (submechanism.isAvailable()) {
    +    assertNotNull(mechanism);
    +    assertNotNull(mechanism_shared);
    +    assertNotNull(mechanism_separate);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/richstorage.js b/mapbox-gl/closure-library/closure/goog/storage/richstorage.js
    new file mode 100644
    index 0000000000..7a0e5d0768
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/richstorage.js
    @@ -0,0 +1,149 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data with attached metadata
    + * persistence. You probably don't want to use this class directly as it
    + * does not save any metadata by itself. It only provides the necessary
    + * infrastructure for subclasses that need to save metadata along with
    + * values stored.
    + *
    + */
    +
    +goog.provide('goog.storage.RichStorage');
    +goog.provide('goog.storage.RichStorage.Wrapper');
    +
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.Storage');
    +
    +
    +
    +/**
    + * Provides a storage for data with attached metadata.
    + *
    + * @param {!goog.storage.mechanism.Mechanism} mechanism The underlying
    + *     storage mechanism.
    + * @constructor
    + * @extends {goog.storage.Storage}
    + */
    +goog.storage.RichStorage = function(mechanism) {
    +  goog.storage.RichStorage.base(this, 'constructor', mechanism);
    +};
    +goog.inherits(goog.storage.RichStorage, goog.storage.Storage);
    +
    +
    +/**
    + * Metadata key under which the actual data is stored.
    + *
    + * @type {string}
    + * @protected
    + */
    +goog.storage.RichStorage.DATA_KEY = 'data';
    +
    +
    +
    +/**
    + * Wraps a value so metadata can be associated with it. You probably want
    + * to use goog.storage.RichStorage.Wrapper.wrapIfNecessary to avoid multiple
    + * embeddings.
    + *
    + * @param {*} value The value to wrap.
    + * @constructor
    + * @final
    + */
    +goog.storage.RichStorage.Wrapper = function(value) {
    +  this[goog.storage.RichStorage.DATA_KEY] = value;
    +};
    +
    +
    +/**
    + * Convenience method for wrapping a value so metadata can be associated with
    + * it. No-op if the value is already wrapped or is undefined.
    + *
    + * @param {*} value The value to wrap.
    + * @return {(!goog.storage.RichStorage.Wrapper|undefined)} The wrapper.
    + */
    +goog.storage.RichStorage.Wrapper.wrapIfNecessary = function(value) {
    +  if (!goog.isDef(value) || value instanceof goog.storage.RichStorage.Wrapper) {
    +    return /** @type {(!goog.storage.RichStorage.Wrapper|undefined)} */ (value);
    +  }
    +  return new goog.storage.RichStorage.Wrapper(value);
    +};
    +
    +
    +/**
    + * Unwraps a value, any metadata is discarded (not returned). You might want to
    + * use goog.storage.RichStorage.Wrapper.unwrapIfPossible to handle cases where
    + * the wrapper is missing.
    + *
    + * @param {!Object} wrapper The wrapper.
    + * @return {*} The wrapped value.
    + */
    +goog.storage.RichStorage.Wrapper.unwrap = function(wrapper) {
    +  var value = wrapper[goog.storage.RichStorage.DATA_KEY];
    +  if (!goog.isDef(value)) {
    +    throw goog.storage.ErrorCode.INVALID_VALUE;
    +  }
    +  return value;
    +};
    +
    +
    +/**
    + * Convenience method for unwrapping a value. Returns undefined if the
    + * wrapper is missing.
    + *
    + * @param {(!Object|undefined)} wrapper The wrapper.
    + * @return {*} The wrapped value or undefined.
    + */
    +goog.storage.RichStorage.Wrapper.unwrapIfPossible = function(wrapper) {
    +  if (!wrapper) {
    +    return undefined;
    +  }
    +  return goog.storage.RichStorage.Wrapper.unwrap(wrapper);
    +};
    +
    +
    +/** @override */
    +goog.storage.RichStorage.prototype.set = function(key, value) {
    +  goog.storage.RichStorage.base(this, 'set', key,
    +      goog.storage.RichStorage.Wrapper.wrapIfNecessary(value));
    +};
    +
    +
    +/**
    + * Get an item wrapper (the item and its metadata) from the storage.
    + *
    + * WARNING: This returns an Object, which once used to be
    + * goog.storage.RichStorage.Wrapper. This is due to the fact
    + * that deserialized objects lose type information and it
    + * is hard to do proper typecasting in JavaScript. Be sure
    + * you know what you are doing when using the returned value.
    + *
    + * @param {string} key The key to get.
    + * @return {(!Object|undefined)} The wrapper, or undefined if not found.
    + */
    +goog.storage.RichStorage.prototype.getWrapper = function(key) {
    +  var wrapper = goog.storage.RichStorage.superClass_.get.call(this, key);
    +  if (!goog.isDef(wrapper) || wrapper instanceof Object) {
    +    return /** @type {(!Object|undefined)} */ (wrapper);
    +  }
    +  throw goog.storage.ErrorCode.INVALID_VALUE;
    +};
    +
    +
    +/** @override */
    +goog.storage.RichStorage.prototype.get = function(key) {
    +  return goog.storage.RichStorage.Wrapper.unwrapIfPossible(
    +      this.getWrapper(key));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.html b/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.html
    new file mode 100644
    index 0000000000..bc8a66cc29
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.storage.RichStorage
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.storage.RichStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.js b/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.js
    new file mode 100644
    index 0000000000..7d3edb2693
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/richstorage_test.js
    @@ -0,0 +1,82 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.storage.RichStorageTest');
    +goog.setTestOnly('goog.storage.RichStorageTest');
    +
    +goog.require('goog.storage.ErrorCode');
    +goog.require('goog.storage.RichStorage');
    +goog.require('goog.storage.storage_test');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.storage.FakeMechanism');
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.RichStorage(mechanism);
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +function testWrapping() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.RichStorage(mechanism);
    +
    +  // Some metadata.
    +  var object = {'a': 97, 'b': 98};
    +  var wrapper = new goog.storage.RichStorage.Wrapper(object);
    +  wrapper['meta'] = 'info';
    +  storage.set('first', wrapper);
    +  assertObjectEquals(object, storage.get('first'));
    +  assertObjectEquals(wrapper, storage.getWrapper('first'));
    +  assertEquals('info', storage.getWrapper('first')['meta']);
    +
    +  // Multiple wrappings.
    +  var wrapper1 = goog.storage.RichStorage.Wrapper.wrapIfNecessary(object);
    +  wrapper1['some'] = 'meta';
    +  var wrapper2 = goog.storage.RichStorage.Wrapper.wrapIfNecessary(wrapper1);
    +  wrapper2['more'] = 'stuff';
    +  storage.set('second', wrapper2);
    +  assertObjectEquals(object, storage.get('second'));
    +  assertObjectEquals(wrapper2, storage.getWrapper('second'));
    +  assertEquals('meta', storage.getWrapper('second')['some']);
    +  assertEquals('stuff', storage.getWrapper('second')['more']);
    +
    +  // Invalid wrappings.
    +  mechanism.set('third', 'null');
    +  assertEquals(goog.storage.ErrorCode.INVALID_VALUE,
    +               assertThrows(function() {storage.get('third')}));
    +  mechanism.set('third', '{"meta": "data"}');
    +  assertEquals(goog.storage.ErrorCode.INVALID_VALUE,
    +               assertThrows(function() {storage.get('third')}));
    +
    +  // Weird values.
    +  var wrapperA = new goog.storage.RichStorage.Wrapper.wrapIfNecessary(null);
    +  wrapperA['one'] = 1;
    +  storage.set('first', wrapperA);
    +  assertObjectEquals(wrapperA, storage.getWrapper('first'));
    +  var wrapperB = new goog.storage.RichStorage.Wrapper.wrapIfNecessary('');
    +  wrapperA['two'] = [];
    +  storage.set('second', wrapperB);
    +  assertObjectEquals(wrapperB, storage.getWrapper('second'));
    +
    +  // Clean up.
    +  storage.remove('first');
    +  storage.remove('second');
    +  storage.remove('third');
    +  assertUndefined(storage.get('first'));
    +  assertUndefined(storage.get('second'));
    +  assertUndefined(storage.get('third'));
    +  assertNull(mechanism.get('first'));
    +  assertNull(mechanism.get('second'));
    +  assertNull(mechanism.get('third'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/storage.js b/mapbox-gl/closure-library/closure/goog/storage/storage.js
    new file mode 100644
    index 0000000000..e35c722ca9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/storage.js
    @@ -0,0 +1,96 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a convenient API for data persistence using a selected
    + * data storage mechanism.
    + *
    + */
    +
    +goog.provide('goog.storage.Storage');
    +
    +goog.require('goog.json');
    +goog.require('goog.storage.ErrorCode');
    +
    +
    +
    +/**
    + * The base implementation for all storage APIs.
    + *
    + * @param {!goog.storage.mechanism.Mechanism} mechanism The underlying
    + *     storage mechanism.
    + * @constructor
    + */
    +goog.storage.Storage = function(mechanism) {
    +  /**
    +   * The mechanism used to persist key-value pairs.
    +   *
    +   * @protected {goog.storage.mechanism.Mechanism}
    +   */
    +  this.mechanism = mechanism;
    +};
    +
    +
    +/**
    + * Sets an item in the data storage.
    + *
    + * @param {string} key The key to set.
    + * @param {*} value The value to serialize to a string and save.
    + */
    +goog.storage.Storage.prototype.set = function(key, value) {
    +  if (!goog.isDef(value)) {
    +    this.mechanism.remove(key);
    +    return;
    +  }
    +  this.mechanism.set(key, goog.json.serialize(value));
    +};
    +
    +
    +/**
    + * Gets an item from the data storage.
    + *
    + * @param {string} key The key to get.
    + * @return {*} Deserialized value or undefined if not found.
    + */
    +goog.storage.Storage.prototype.get = function(key) {
    +  var json;
    +  try {
    +    json = this.mechanism.get(key);
    +  } catch (e) {
    +    // If, for any reason, the value returned by a mechanism's get method is not
    +    // a string, an exception is thrown.  In this case, we must fail gracefully
    +    // instead of propagating the exception to clients.  See b/8095488 for
    +    // details.
    +    return undefined;
    +  }
    +  if (goog.isNull(json)) {
    +    return undefined;
    +  }
    +  /** @preserveTry */
    +  try {
    +    return goog.json.parse(json);
    +  } catch (e) {
    +    throw goog.storage.ErrorCode.INVALID_VALUE;
    +  }
    +};
    +
    +
    +/**
    + * Removes an item from the data storage.
    + *
    + * @param {string} key The key to remove.
    + */
    +goog.storage.Storage.prototype.remove = function(key) {
    +  this.mechanism.remove(key);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/storage_test.html b/mapbox-gl/closure-library/closure/goog/storage/storage_test.html
    new file mode 100644
    index 0000000000..46802ff66d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/storage_test.html
    @@ -0,0 +1,64 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.storage.Storage</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.functions');
    +  goog.require('goog.storage.ErrorCode');
    +  goog.require('goog.storage.Storage');
    +  goog.require('goog.storage.mechanism.mechanismfactory');
    +  goog.require('goog.storage.storage_test');
    +  goog.require('goog.testing.jsunit');
    +  goog.require('goog.testing.storage.FakeMechanism');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +function testBasicOperations() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.Storage(mechanism);
    +  goog.storage.storage_test.runBasicTests(storage);
    +}
    +
    +function testMechanismCommunication() {
    +  var mechanism = new goog.testing.storage.FakeMechanism();
    +  var storage = new goog.storage.Storage(mechanism);
    +
    +  // Invalid JSON.
    +  mechanism.set('first', '');
    +  assertEquals(goog.storage.ErrorCode.INVALID_VALUE,
    +               assertThrows(function() {storage.get('first')}));
    +  mechanism.set('second', '(');
    +  assertEquals(goog.storage.ErrorCode.INVALID_VALUE,
    +               assertThrows(function() {storage.get('second')}));
    +
    +  // Cleaning up.
    +  storage.remove('first');
    +  storage.remove('second');
    +  assertUndefined(storage.get('first'));
    +  assertUndefined(storage.get('second'));
    +  assertNull(mechanism.get('first'));
    +  assertNull(mechanism.get('second'));
    +}
    +
    +function testMechanismFailsGracefullyOnInvalidValue() {
    +  var mechanism = {
    +    get: goog.functions.error('Invalid value')
    +  };
    +  var storage = new goog.storage.Storage(mechanism);
    +  assertUndefined(storage.get('foobar'));
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/storage/storage_test.js b/mapbox-gl/closure-library/closure/goog/storage/storage_test.js
    new file mode 100644
    index 0000000000..b8f4a51ec5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/storage/storage_test.js
    @@ -0,0 +1,59 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for the storage interface.
    + *
    + */
    +
    +goog.provide('goog.storage.storage_test');
    +
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.asserts');
    +goog.setTestOnly('storage_test');
    +
    +
    +goog.storage.storage_test.runBasicTests = function(storage) {
    +  // Simple Objects.
    +  storage.set('first', 'Hello world!');
    +  storage.set('second', ['one', 'two', 'three']);
    +  storage.set('third', {'a': 97, 'b': 98});
    +  assertEquals('Hello world!', storage.get('first'));
    +  assertObjectEquals(['one', 'two', 'three'], storage.get('second'));
    +  assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
    +
    +  // Some more complex fun with a Map.
    +  var map = new goog.structs.Map();
    +  map.set('Alice', 'Hello world!');
    +  map.set('Bob', ['one', 'two', 'three']);
    +  map.set('Cecile', {'a': 97, 'b': 98});
    +  storage.set('first', map.toObject());
    +  assertObjectEquals(map.toObject(), storage.get('first'));
    +
    +  // Setting weird values.
    +  storage.set('second', null);
    +  assertEquals(null, storage.get('second'));
    +  storage.set('second', undefined);
    +  assertEquals(undefined, storage.get('second'));
    +  storage.set('second', '');
    +  assertEquals('', storage.get('second'));
    +
    +  // Clean up.
    +  storage.remove('first');
    +  storage.remove('second');
    +  storage.remove('third');
    +  assertUndefined(storage.get('first'));
    +  assertUndefined(storage.get('second'));
    +  assertUndefined(storage.get('third'));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/string/const.js b/mapbox-gl/closure-library/closure/goog/string/const.js
    new file mode 100644
    index 0000000000..76cac4fd4f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/const.js
    @@ -0,0 +1,182 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.Const');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string.TypedString');
    +
    +
    +
    +/**
    + * Wrapper for compile-time-constant strings.
    + *
    + * Const is a wrapper for strings that can only be created from program
    + * constants (i.e., string literals).  This property relies on a custom Closure
    + * compiler check that {@code goog.string.Const.from} is only invoked on
    + * compile-time-constant expressions.
    + *
    + * Const is useful in APIs whose correct and secure use requires that certain
    + * arguments are not attacker controlled: Compile-time constants are inherently
    + * under the control of the application and not under control of external
    + * attackers, and hence are safe to use in such contexts.
    + *
    + * Instances of this type must be created via its factory method
    + * {@code goog.string.Const.from} and not by invoking its constructor.  The
    + * constructor intentionally takes no parameters and the type is immutable;
    + * hence only a default instance corresponding to the empty string can be
    + * obtained via constructor invocation.
    + *
    + * @see goog.string.Const#from
    + * @constructor
    + * @final
    + * @struct
    + * @implements {goog.string.TypedString}
    + */
    +goog.string.Const = function() {
    +  /**
    +   * The wrapped value of this Const object.  The field has a purposely ugly
    +   * name to make (non-compiled) code that attempts to directly access this
    +   * field stand out.
    +   * @private {string}
    +   */
    +  this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = '';
    +
    +  /**
    +   * A type marker used to implement additional run-time type checking.
    +   * @see goog.string.Const#unwrap
    +   * @const
    +   * @private
    +   */
    +  this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
    +      goog.string.Const.TYPE_MARKER_;
    +};
    +
    +
    +/**
    + * @override
    + * @const
    + */
    +goog.string.Const.prototype.implementsGoogStringTypedString = true;
    +
    +
    +/**
    + * Returns this Const's value a string.
    + *
    + * IMPORTANT: In code where it is security-relevant that an object's type is
    + * indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap}
    + * instead of this method.
    + *
    + * @see goog.string.Const#unwrap
    + * @override
    + */
    +goog.string.Const.prototype.getTypedStringValue = function() {
    +  return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
    +};
    +
    +
    +/**
    + * Returns a debug-string representation of this value.
    + *
    + * To obtain the actual string value wrapped inside an object of this type,
    + * use {@code goog.string.Const.unwrap}.
    + *
    + * @see goog.string.Const#unwrap
    + * @override
    + */
    +goog.string.Const.prototype.toString = function() {
    +  return 'Const{' +
    +         this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
    +         '}';
    +};
    +
    +
    +/**
    + * Performs a runtime check that the provided object is indeed an instance
    + * of {@code goog.string.Const}, and returns its value.
    + * @param {!goog.string.Const} stringConst The object to extract from.
    + * @return {string} The Const object's contained string, unless the run-time
    + *     type check fails. In that case, {@code unwrap} returns an innocuous
    + *     string, or, if assertions are enabled, throws
    + *     {@code goog.asserts.AssertionError}.
    + */
    +goog.string.Const.unwrap = function(stringConst) {
    +  // Perform additional run-time type-checking to ensure that stringConst is
    +  // indeed an instance of the expected type.  This provides some additional
    +  // protection against security bugs due to application code that disables type
    +  // checks.
    +  if (stringConst instanceof goog.string.Const &&
    +      stringConst.constructor === goog.string.Const &&
    +      stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
    +          goog.string.Const.TYPE_MARKER_) {
    +    return stringConst.
    +        stringConstValueWithSecurityContract__googStringSecurityPrivate_;
    +  } else {
    +    goog.asserts.fail('expected object of type Const, got \'' +
    +                      stringConst + '\'');
    +    return 'type_error:Const';
    +  }
    +};
    +
    +
    +/**
    + * Creates a Const object from a compile-time constant string.
    + *
    + * It is illegal to invoke this function on an expression whose
    + * compile-time-contant value cannot be determined by the Closure compiler.
    + *
    + * Correct invocations include,
    + * <pre>
    + *   var s = goog.string.Const.from('hello');
    + *   var t = goog.string.Const.from('hello' + 'world');
    + * </pre>
    + *
    + * In contrast, the following are illegal:
    + * <pre>
    + *   var s = goog.string.Const.from(getHello());
    + *   var t = goog.string.Const.from('hello' + world);
    + * </pre>
    + *
    + * TODO(user): Compile-time checks that this function is only called
    + * with compile-time constant expressions.
    + *
    + * @param {string} s A constant string from which to create a Const.
    + * @return {!goog.string.Const} A Const object initialized to stringConst.
    + */
    +goog.string.Const.from = function(s) {
    +  return goog.string.Const.create__googStringSecurityPrivate_(s);
    +};
    +
    +
    +/**
    + * Type marker for the Const type, used to implement additional run-time
    + * type checking.
    + * @const
    + * @private
    + */
    +goog.string.Const.TYPE_MARKER_ = {};
    +
    +
    +/**
    + * Utility method to create Const instances.
    + * @param {string} s The string to initialize the Const object with.
    + * @return {!goog.string.Const} The initialized Const object.
    + * @private
    + */
    +goog.string.Const.create__googStringSecurityPrivate_ = function(s) {
    +  var stringConst = new goog.string.Const();
    +  stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
    +      s;
    +  return stringConst;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/string/const_test.html b/mapbox-gl/closure-library/closure/goog/string/const_test.html
    new file mode 100644
    index 0000000000..98a33fa7b4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/const_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.string</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.string.constTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/const_test.js b/mapbox-gl/closure-library/closure/goog/string/const_test.js
    new file mode 100644
    index 0000000000..3da24c8e4f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/const_test.js
    @@ -0,0 +1,51 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.string.Const.
    + */
    +
    +goog.provide('goog.string.constTest');
    +
    +goog.require('goog.string.Const');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.string.constTest');
    +
    +
    +
    +function testConst() {
    +  var constString = goog.string.Const.from('blah');
    +  var extracted = goog.string.Const.unwrap(constString);
    +  assertEquals('blah', extracted);
    +  assertEquals('blah', constString.getTypedStringValue());
    +  assertEquals('Const{blah}', String(constString));
    +
    +  // Interface marker is present.
    +  assertTrue(constString.implementsGoogStringTypedString);
    +}
    +
    +
    +/** @suppress {checkTypes} */
    +function testUnwrap() {
    +  var evil = {};
    +  evil.constStringValueWithSecurityContract__googStringSecurityPrivate_ =
    +      'evil';
    +  evil.CONST_STRING_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ = {};
    +
    +  var exception = assertThrows(function() {
    +    goog.string.Const.unwrap(evil);
    +  });
    +  assertTrue(exception.message.indexOf('expected object of type Const') > 0);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/linkify.js b/mapbox-gl/closure-library/closure/goog/string/linkify.js
    new file mode 100644
    index 0000000000..6f7a7112cb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/linkify.js
    @@ -0,0 +1,252 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility function for linkifying text.
    + * @author bolinfest@google.com (Michael Bolin)
    + */
    +
    +goog.provide('goog.string.linkify');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * Takes a string of plain text and linkifies URLs and email addresses. For a
    + * URL (unless opt_attributes is specified), the target of the link will be
    + * _blank and it will have a rel=nofollow attribute applied to it so that links
    + * created by linkify will not be of interest to search engines.
    + * @param {string} text Plain text.
    + * @param {Object<string, string>=} opt_attributes Attributes to add to all
    + *      links created. Default are rel=nofollow and target=_blank. To clear
    + *      those default attributes set rel='' and target=''.
    + * @return {string} HTML Linkified HTML text. Any text that is not part of a
    + *      link will be HTML-escaped.
    + */
    +goog.string.linkify.linkifyPlainText = function(text, opt_attributes) {
    +  // This shortcut makes linkifyPlainText ~10x faster if text doesn't contain
    +  // URLs or email addresses and adds insignificant performance penalty if it
    +  // does.
    +  if (text.indexOf('@') == -1 &&
    +      text.indexOf('://') == -1 &&
    +      text.indexOf('www.') == -1 &&
    +      text.indexOf('Www.') == -1 &&
    +      text.indexOf('WWW.') == -1) {
    +    return goog.string.htmlEscape(text);
    +  }
    +
    +  var attributesMap = opt_attributes || {};
    +  // Set default options.
    +  if (!('rel' in attributesMap)) {
    +    attributesMap['rel'] = 'nofollow';
    +  }
    +  if (!('target' in attributesMap)) {
    +    attributesMap['target'] = '_blank';
    +  }
    +  // Creates attributes string from options.
    +  var attributesArray = [];
    +  for (var key in attributesMap) {
    +    if (attributesMap.hasOwnProperty(key) && attributesMap[key]) {
    +      attributesArray.push(
    +          goog.string.htmlEscape(key), '="',
    +          goog.string.htmlEscape(attributesMap[key]), '" ');
    +    }
    +  }
    +  var attributes = attributesArray.join('');
    +
    +  return text.replace(
    +      goog.string.linkify.FIND_LINKS_RE_,
    +      function(part, before, original, email, protocol) {
    +        var output = [goog.string.htmlEscape(before)];
    +        if (!original) {
    +          return output[0];
    +        }
    +        output.push('<a ', attributes, 'href="');
    +        /** @type {string} */
    +        var linkText;
    +        /** @type {string} */
    +        var afterLink;
    +        if (email) {
    +          output.push('mailto:');
    +          linkText = email;
    +          afterLink = '';
    +        } else {
    +          // This is a full url link.
    +          if (!protocol) {
    +            output.push('http://');
    +          }
    +          var splitEndingPunctuation =
    +              original.match(goog.string.linkify.ENDS_WITH_PUNCTUATION_RE_);
    +          // An open paren in the link will often be matched with a close paren
    +          // at the end, so skip cutting off ending punctuation if there's an
    +          // open paren. For example:
    +          // http://en.wikipedia.org/wiki/Titanic_(1997_film)
    +          if (splitEndingPunctuation && !goog.string.contains(original, '(')) {
    +            linkText = splitEndingPunctuation[1];
    +            afterLink = splitEndingPunctuation[2];
    +          } else {
    +            linkText = original;
    +            afterLink = '';
    +          }
    +        }
    +        linkText = goog.string.htmlEscape(linkText);
    +        afterLink = goog.string.htmlEscape(afterLink);
    +        output.push(linkText, '">', linkText, '</a>', afterLink);
    +        return output.join('');
    +      });
    +};
    +
    +
    +/**
    + * Gets the first URI in text.
    + * @param {string} text Plain text.
    + * @return {string} The first URL, or an empty string if not found.
    + */
    +goog.string.linkify.findFirstUrl = function(text) {
    +  var link = text.match(goog.string.linkify.URL_);
    +  return link != null ? link[0] : '';
    +};
    +
    +
    +/**
    + * Gets the first email address in text.
    + * @param {string} text Plain text.
    + * @return {string} The first email address, or an empty string if not found.
    + */
    +goog.string.linkify.findFirstEmail = function(text) {
    +  var email = text.match(goog.string.linkify.EMAIL_);
    +  return email != null ? email[0] : '';
    +};
    +
    +
    +/**
    + * If a series of these characters is at the end of a url, it will be considered
    + * punctuation and not part of the url.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.ENDING_PUNCTUATION_CHARS_ = ':;,\\.?>\\]\\)!';
    +
    +
    +/**
    + * @type {!RegExp}
    + * @const
    + * @private
    + */
    +goog.string.linkify.ENDS_WITH_PUNCTUATION_RE_ = new RegExp(
    +    '^(.*?)([' + goog.string.linkify.ENDING_PUNCTUATION_CHARS_ + ']+)$');
    +
    +
    +/**
    + * Set of characters to be put into a regex character set ("[...]"), used to
    + * match against a url hostname and everything after it. It includes
    + * "#-@", which represents the characters "#$%&'()*+,-./0123456789:;<=>?@".
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.ACCEPTABLE_URL_CHARS_ = '\\w~#-@!\\[\\]';
    +
    +
    +/**
    + * List of all protocols patterns recognized in urls (mailto is handled in email
    + * matching).
    + * @type {!Array<string>}
    + * @const
    + * @private
    + */
    +goog.string.linkify.RECOGNIZED_PROTOCOLS_ = ['https?', 'ftp'];
    +
    +
    +/**
    + * Regular expression pattern that matches the beginning of an url.
    + * Contains a catching group to capture the scheme.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.PROTOCOL_START_ =
    +    '(' + goog.string.linkify.RECOGNIZED_PROTOCOLS_.join('|') + ')://';
    +
    +
    +/**
    + * Regular expression pattern that matches the beginning of a typical
    + * http url without the http:// scheme.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.WWW_START_ = 'www\\.';
    +
    +
    +/**
    + * Regular expression pattern that matches an url.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.URL_ =
    +    '(?:' + goog.string.linkify.PROTOCOL_START_ + '|' +
    +    goog.string.linkify.WWW_START_ + ')\\w[' +
    +    goog.string.linkify.ACCEPTABLE_URL_CHARS_ + ']*';
    +
    +
    +/**
    + * Regular expression pattern that matches a top level domain.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.TOP_LEVEL_DOMAIN_ =
    +    '(?:com|org|net|edu|gov' +
    +    // from http://www.iana.org/gtld/gtld.htm
    +    '|aero|biz|cat|coop|info|int|jobs|mobi|museum|name|pro|travel' +
    +    '|arpa|asia|xxx' +
    +    // a two letter country code
    +    '|[a-z][a-z])\\b';
    +
    +
    +/**
    + * Regular expression pattern that matches an email.
    + * Contains a catching group to capture the email without the optional "mailto:"
    + * prefix.
    + * @type {string}
    + * @const
    + * @private
    + */
    +goog.string.linkify.EMAIL_ =
    +    '(?:mailto:)?([\\w.+-]+@[A-Za-z0-9.-]+\\.' +
    +    goog.string.linkify.TOP_LEVEL_DOMAIN_ + ')';
    +
    +
    +/**
    + * Regular expression to match all the links (url or email) in a string.
    + * First match is text before first link, might be empty string.
    + * Second match is the original text that should be replaced by a link.
    + * Third match is the email address in the case of an email.
    + * Fourth match is the scheme of the url if specified.
    + * @type {!RegExp}
    + * @const
    + * @private
    + */
    +goog.string.linkify.FIND_LINKS_RE_ = new RegExp(
    +    // Match everything including newlines.
    +    '([\\S\\s]*?)(' +
    +    // Match email after a word break.
    +    '\\b' + goog.string.linkify.EMAIL_ + '|' +
    +    // Match url after a workd break.
    +    '\\b' + goog.string.linkify.URL_ + '|$)',
    +    'gi');
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/string/linkify_test.html b/mapbox-gl/closure-library/closure/goog/string/linkify_test.html
    new file mode 100644
    index 0000000000..d7785575bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/linkify_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.string.linkify
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.string.linkifyTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/linkify_test.js b/mapbox-gl/closure-library/closure/goog/string/linkify_test.js
    new file mode 100644
    index 0000000000..b788b172f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/linkify_test.js
    @@ -0,0 +1,455 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.linkifyTest');
    +goog.setTestOnly('goog.string.linkifyTest');
    +
    +goog.require('goog.string');
    +goog.require('goog.string.linkify');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.jsunit');
    +
    +var div = document.createElement('div');
    +
    +function assertLinkify(comment, input, expected) {
    +  assertEquals(
    +      comment, expected,
    +      goog.string.linkify.linkifyPlainText(input, {rel: '', target: ''}));
    +}
    +
    +function testContainsNoLink() {
    +  assertLinkify(
    +      'Text does not contain any links',
    +      'Text with no links in it.',
    +      'Text with no links in it.');
    +}
    +
    +function testContainsALink() {
    +  assertLinkify(
    +      'Text only contains a link',
    +      'http://www.google.com/',
    +      '<a href="http://www.google.com/">http://www.google.com/<\/a>');
    +}
    +
    +function testStartsWithALink() {
    +  assertLinkify(
    +      'Text starts with a link',
    +      'http://www.google.com/ is a well known search engine',
    +      '<a href="http://www.google.com/">http://www.google.com/<\/a>' +
    +          ' is a well known search engine');
    +}
    +
    +function testEndsWithALink() {
    +  assertLinkify(
    +      'Text ends with a link',
    +      'Look at this search engine: http://www.google.com/',
    +      'Look at this search engine: ' +
    +          '<a href="http://www.google.com/">http://www.google.com/<\/a>');
    +}
    +
    +function testContainsOnlyEmail() {
    +  assertLinkify(
    +      'Text only contains an email address',
    +      'bolinfest@google.com',
    +      '<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>');
    +}
    +
    +function testStartsWithAnEmail() {
    +  assertLinkify(
    +      'Text starts with an email address',
    +      'bolinfest@google.com wrote this test.',
    +      '<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>' +
    +          ' wrote this test.');
    +}
    +
    +function testEndsWithAnEmail() {
    +  assertLinkify(
    +      'Text ends with an email address',
    +      'This test was written by bolinfest@google.com.',
    +      'This test was written by ' +
    +          '<a href="mailto:bolinfest@google.com">bolinfest@google.com<\/a>.');
    +}
    +
    +function testUrlWithPortNumber() {
    +  assertLinkify(
    +      'URL with a port number',
    +      'http://www.google.com:80/',
    +      '<a href="http://www.google.com:80/">http://www.google.com:80/<\/a>');
    +}
    +
    +function testUrlWithUserPasswordAndPortNumber() {
    +  assertLinkify(
    +      'URL with a user, a password and a port number',
    +      'http://lascap:p4ssw0rd@google.com:80/s?q=a&hl=en',
    +      '<a href="http://lascap:p4ssw0rd@google.com:80/s?q=a&amp;hl=en">' +
    +          'http://lascap:p4ssw0rd@google.com:80/s?q=a&amp;hl=en<\/a>');
    +}
    +
    +function testUrlWithUnderscore() {
    +  assertLinkify(
    +      'URL with an underscore',
    +      'http://www_foo.google.com/',
    +      '<a href="http://www_foo.google.com/">http://www_foo.google.com/<\/a>');
    +}
    +
    +function testInternalUrlWithoutDomain() {
    +  assertLinkify(
    +      'Internal URL without a proper domain',
    +      'http://tracker/1068594',
    +      '<a href="http://tracker/1068594">http://tracker/1068594<\/a>');
    +}
    +
    +function testInternalUrlOneChar() {
    +  assertLinkify(
    +      'Internal URL with a one char domain',
    +      'http://b',
    +      '<a href="http://b">http://b<\/a>');
    +}
    +
    +function testSecureInternalUrlWithoutDomain() {
    +  assertLinkify(
    +      'Secure Internal URL without a proper domain',
    +      'https://review/6594805',
    +      '<a href="https://review/6594805">https://review/6594805<\/a>');
    +}
    +
    +function testTwoUrls() {
    +  assertLinkify(
    +      'Text with two URLs in it',
    +      'I use both http://www.google.com and http://yahoo.com, don\'t you?',
    +      'I use both <a href="http://www.google.com">http://www.google.com<\/a> ' +
    +          'and <a href="http://yahoo.com">http://yahoo.com<\/a>, ' +
    +          goog.string.htmlEscape('don\'t you?'));
    +}
    +
    +function testGetParams() {
    +  assertLinkify(
    +      'URL with GET params',
    +      'http://google.com/?a=b&c=d&e=f',
    +      '<a href="http://google.com/?a=b&amp;c=d&amp;e=f">' +
    +          'http://google.com/?a=b&amp;c=d&amp;e=f<\/a>');
    +}
    +
    +function testGoogleCache() {
    +  assertLinkify(
    +      'Google search result from cache',
    +      'http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
    +          'www.google.com/intl/en/help/features.html+google+cache&hl=en',
    +      '<a href="http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
    +          'www.google.com/intl/en/help/features.html+google+cache&amp;hl=en">' +
    +          'http://66.102.7.104/search?q=cache:I4LoMT6euUUJ:' +
    +          'www.google.com/intl/en/help/features.html+google+cache&amp;hl=en' +
    +          '<\/a>');
    +}
    +
    +function testUrlWithoutHttp() {
    +  assertLinkify(
    +      'URL without http protocol',
    +      'It\'s faster to type www.google.com without the http:// in front.',
    +      goog.string.htmlEscape('It\'s faster to type ') +
    +          '<a href="http://www.google.com">www.google.com' +
    +          '<\/a> without the http:// in front.');
    +}
    +
    +function testUrlWithCapitalsWithoutHttp() {
    +  assertLinkify(
    +      'URL with capital letters without http protocol',
    +      'It\'s faster to type Www.google.com without the http:// in front.',
    +      goog.string.htmlEscape('It\'s faster to type ') +
    +          '<a href="http://Www.google.com">Www.google.com' +
    +          '<\/a> without the http:// in front.');
    +}
    +
    +function testUrlHashBang() {
    +  assertLinkify(
    +      'URL with #!',
    +      'Another test URL: ' +
    +      'https://www.google.com/testurls/#!/page',
    +      'Another test URL: ' +
    +      '<a href="https://www.google.com/testurls/#!/page">' +
    +      'https://www.google.com/testurls/#!/page<\/a>');
    +}
    +
    +function testTextLooksLikeUrlWithoutHttp() {
    +  assertLinkify(
    +      'Text looks like an url but is not',
    +      'This showww.is just great: www.is',
    +      'This showww.is just great: <a href="http://www.is">www.is<\/a>');
    +}
    +
    +function testEmailWithSubdomain() {
    +  assertLinkify(
    +      'Email with a subdomain',
    +      'Send mail to bolinfest@groups.google.com.',
    +      'Send mail to <a href="mailto:bolinfest@groups.google.com">' +
    +          'bolinfest@groups.google.com<\/a>.');
    +}
    +
    +function testEmailWithHyphen() {
    +  assertLinkify(
    +      'Email with a hyphen in the domain name',
    +      'Send mail to bolinfest@google-groups.com.',
    +      'Send mail to <a href="mailto:bolinfest@google-groups.com">' +
    +          'bolinfest@google-groups.com<\/a>.');
    +}
    +
    +function testEmailUsernameWithSpecialChars() {
    +  assertLinkify(
    +      'Email with a hyphen, period, and + in the user name',
    +      'Send mail to bolin-fest+for.um@google.com',
    +      'Send mail to <a href="mailto:bolin-fest+for.um@google.com">' +
    +          'bolin-fest+for.um@google.com<\/a>');
    +}
    +
    +function testEmailWithUnderscoreInvalid() {
    +  assertLinkify(
    +      'Email with an underscore in the domain name, which is invalid',
    +      'Do not email bolinfest@google_groups.com.',
    +      'Do not email bolinfest@google_groups.com.');
    +}
    +
    +function testUrlNotHttp() {
    +  assertLinkify(
    +      'Url using unusual scheme',
    +      'Looking for some goodies: ftp://ftp.google.com/goodstuff/',
    +      'Looking for some goodies: ' +
    +      '<a href="ftp://ftp.google.com/goodstuff/">' +
    +          'ftp://ftp.google.com/goodstuff/<\/a>');
    +}
    +
    +function testJsInjection() {
    +  assertLinkify(
    +      'Text includes some javascript',
    +      'Welcome in hell <script>alert(\'this is hell\')<\/script>',
    +      goog.string.htmlEscape(
    +          'Welcome in hell <script>alert(\'this is hell\')<\/script>'));
    +}
    +
    +function testJsInjectionDotIsBlind() {
    +  assertLinkify(
    +      'Javascript injection using regex . blindness to newline chars',
    +      '<script>malicious_code()<\/script>\nVery nice url: www.google.com',
    +      '&lt;script&gt;malicious_code()&lt;/script&gt;\nVery nice url: ' +
    +          '<a href="http://www.google.com">www.google.com<\/a>');
    +}
    +
    +function testJsInjectionWithUnicodeLineReturn() {
    +  assertLinkify(
    +      'Javascript injection using regex . blindness to newline chars with a ' +
    +          'unicode newline character.',
    +      '<script>malicious_code()<\/script>\u2029Vanilla text',
    +      '&lt;script&gt;malicious_code()&lt;/script&gt;\u2029Vanilla text');
    +}
    +
    +function testJsInjectionWithIgnorableNonTagChar() {
    +  assertLinkify(
    +      'Angle brackets are normalized even when followed by an ignorable ' +
    +          'non-tag character.',
    +      '<\u0000img onerror=alert(1337) src=\n>',
    +      '&lt;&#0;img onerror=alert(1337) src=\n&gt;');
    +}
    +
    +function testJsInjectionWithTextarea() {
    +  assertLinkify(
    +      'Putting the result in a textarea can\'t cause other textarea text to ' +
    +          'be treated as tag content.',
    +      '</textarea',
    +      '&lt;/textarea');
    +}
    +
    +function testJsInjectionWithNewlineConversion() {
    +  assertLinkify(
    +      'Any newline conversion and whitespace normalization won\'t cause tag ' +
    +          'parts to be recombined.',
    +      '<<br>script<br>>alert(1337)<<br>/<br>script<br>>',
    +      '&lt;&lt;br&gt;script&lt;br&gt;&gt;alert(1337)&lt;&lt;br&gt;/&lt;' +
    +          'br&gt;script&lt;br&gt;&gt;');
    +}
    +
    +function testNoProtocolBlacklisting() {
    +  assertLinkify(
    +      'No protocol blacklisting.',
    +      'Click: jscript:alert%281337%29\nClick: JSscript:alert%281337%29\n' +
    +          'Click: VBscript:alert%281337%29\nClick: Script:alert%281337%29\n' +
    +          'Click: flavascript:alert%281337%29',
    +      'Click: jscript:alert%281337%29\nClick: JSscript:alert%281337%29\n' +
    +          'Click: VBscript:alert%281337%29\nClick: Script:alert%281337%29\n' +
    +          'Click: flavascript:alert%281337%29');
    +}
    +
    +function testProtocolWhitelistingEffective() {
    +  assertLinkify(
    +      'Protocol whitelisting is effective.',
    +      'Click httpscript:alert%281337%29\nClick mailtoscript:alert%281337%29\n' +
    +          'Click j\u00A0avascript:alert%281337%29\n' +
    +          'Click \u00A0javascript:alert%281337%29',
    +      'Click httpscript:alert%281337%29\nClick mailtoscript:alert%281337%29\n' +
    +          'Click j\u00A0avascript:alert%281337%29\n' +
    +          'Click \u00A0javascript:alert%281337%29');
    +}
    +
    +function testLinkifyNoOptions() {
    +  div.innerHTML = goog.string.linkify.linkifyPlainText('http://www.google.com');
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<a href="http://www.google.com" target="_blank" rel="nofollow">' +
    +      'http://www.google.com<\/a>',
    +      div, true /* opt_strictAttributes */);
    +}
    +
    +function testLinkifyOptionsNoAttributes() {
    +  div.innerHTML = goog.string.linkify.linkifyPlainText(
    +      'The link for www.google.com is located somewhere in ' +
    +      'https://www.google.fr/?hl=en, you should find it easily.',
    +      {rel: '', target: ''});
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      'The link for <a href="http://www.google.com">www.google.com<\/a> is ' +
    +      'located somewhere in ' +
    +      '<a href="https://www.google.fr/?hl=en">https://www.google.fr/?hl=en' +
    +      '<\/a>, you should find it easily.',
    +      div, true /* opt_strictAttributes */);
    +}
    +
    +function testLinkifyOptionsClassName() {
    +  div.innerHTML = goog.string.linkify.linkifyPlainText(
    +      'Attribute with <class> name www.w3c.org.',
    +      {'class': 'link-added'});
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      'Attribute with &lt;class&gt; name <a href="http://www.w3c.org" ' +
    +      'target="_blank" rel="nofollow" class="link-added">www.w3c.org<\/a>.',
    +      div, true /* opt_strictAttributes */);
    +}
    +
    +function testFindFirstUrlNoScheme() {
    +  assertEquals('www.google.com', goog.string.linkify.findFirstUrl(
    +      'www.google.com'));
    +}
    +
    +function testFindFirstUrlNoSchemeWithText() {
    +  assertEquals('www.google.com', goog.string.linkify.findFirstUrl(
    +      'prefix www.google.com something'));
    +}
    +
    +function testFindFirstUrlScheme() {
    +  assertEquals('http://www.google.com', goog.string.linkify.findFirstUrl(
    +      'http://www.google.com'));
    +}
    +
    +function testFindFirstUrlSchemeWithText() {
    +  assertEquals('http://www.google.com', goog.string.linkify.findFirstUrl(
    +      'prefix http://www.google.com something'));
    +}
    +
    +function testFindFirstUrlNoUrl() {
    +  assertEquals('', goog.string.linkify.findFirstUrl(
    +      'ygvtfr676 5v68fk uygbt85F^&%^&I%FVvc .'));
    +}
    +
    +function testFindFirstEmailNoScheme() {
    +  assertEquals('fake@google.com', goog.string.linkify.findFirstEmail(
    +      'fake@google.com'));
    +}
    +
    +function testFindFirstEmailNoSchemeWithText() {
    +  assertEquals('fake@google.com', goog.string.linkify.findFirstEmail(
    +      'prefix fake@google.com something'));
    +}
    +
    +function testFindFirstEmailScheme() {
    +  assertEquals('mailto:fake@google.com', goog.string.linkify.findFirstEmail(
    +      'mailto:fake@google.com'));
    +}
    +
    +function testFindFirstEmailSchemeWithText() {
    +  assertEquals('mailto:fake@google.com', goog.string.linkify.findFirstEmail(
    +      'prefix mailto:fake@google.com something'));
    +}
    +
    +function testFindFirstEmailNoUrl() {
    +  assertEquals('', goog.string.linkify.findFirstEmail(
    +      'ygvtfr676 5v68fk uygbt85F^&%^&I%FVvc .'));
    +}
    +
    +function testContainsPunctuation_parens() {
    +  assertLinkify(
    +      'Link contains parens, but does not end with them',
    +      'www.google.com/abc(v1).html',
    +      '<a href="http://www.google.com/abc(v1).html">' +
    +          'www.google.com/abc(v1).html<\/a>');
    +}
    +
    +function testEndsWithPunctuation() {
    +  assertLinkify(
    +      'Link ends with punctuation',
    +      'Have you seen www.google.com? It\'s awesome.',
    +      'Have you seen <a href="http://www.google.com">www.google.com<\/a>?' +
    +      goog.string.htmlEscape(' It\'s awesome.'));
    +}
    +
    +function testEndsWithPunctuation_closeParen() {
    +  assertLinkify(
    +      'Link inside parentheses',
    +      '(For more info see www.googl.com)',
    +      '(For more info see <a href="http://www.googl.com">www.googl.com<\/a>)');
    +  assertLinkify(
    +      'Parentheses inside link',
    +      'http://en.wikipedia.org/wiki/Titanic_(1997_film)',
    +      '<a href="http://en.wikipedia.org/wiki/Titanic_(1997_film)">' +
    +          'http://en.wikipedia.org/wiki/Titanic_(1997_film)<\/a>');
    +}
    +
    +function testEndsWithPunctuation_openParen() {
    +  assertLinkify(
    +      'Link followed by open parenthesis',
    +      'www.google.com(',
    +      '<a href="http://www.google.com(">www.google.com(<\/a>');
    +}
    +
    +function testEndsWithPunctuation_angles() {
    +  assertLinkify(
    +      'Link inside angled brackets',
    +      'Here is a bibliography entry <http://www.google.com/>',
    +      'Here is a bibliography entry &lt;<a href="http://www.google.com/">' +
    +          'http://www.google.com/<\/a>&gt;');
    +}
    +
    +function testEndsWithPunctuation_closingPairThenSingle() {
    +  assertLinkify(
    +      'Link followed by closing punctuation pair then singular punctuation',
    +      'Here is a bibliography entry <http://www.google.com/>, PTAL.',
    +      'Here is a bibliography entry &lt;<a href="http://www.google.com/">' +
    +          'http://www.google.com/<\/a>&gt;, PTAL.');
    +}
    +
    +function testEndsWithPunctuation_ellipses() {
    +  assertLinkify(
    +      'Link followed by three dots',
    +      'just look it up on www.google.com...',
    +      'just look it up on <a href="http://www.google.com">www.google.com' +
    +          '<\/a>...');
    +}
    +
    +function testBracketsInUrl() {
    +  assertLinkify(
    +      'Link containing brackets',
    +      'before http://google.com/details?answer[0]=42 after',
    +      'before <a href="http://google.com/details?answer[0]=42">' +
    +          'http://google.com/details?answer[0]=42<\/a> after');
    +}
    +
    +function testUrlWithExclamation() {
    +  assertLinkify(
    +      'URL with exclamation points',
    +      'This is awesome www.google.com!',
    +      'This is awesome <a href="http://www.google.com">www.google.com<\/a>!');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/newlines.js b/mapbox-gl/closure-library/closure/goog/string/newlines.js
    new file mode 100644
    index 0000000000..14b6f03e26
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/newlines.js
    @@ -0,0 +1,154 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for string newlines.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +
    +/**
    + * Namespace for string utilities
    + */
    +goog.provide('goog.string.newlines');
    +goog.provide('goog.string.newlines.Line');
    +
    +goog.require('goog.array');
    +
    +
    +/**
    + * Splits a string into lines, properly handling universal newlines.
    + * @param {string} str String to split.
    + * @param {boolean=} opt_keepNewlines Whether to keep the newlines in the
    + *     resulting strings. Defaults to false.
    + * @return {!Array<string>} String split into lines.
    + */
    +goog.string.newlines.splitLines = function(str, opt_keepNewlines) {
    +  var lines = goog.string.newlines.getLines(str);
    +  return goog.array.map(lines, function(line) {
    +    return opt_keepNewlines ? line.getFullLine() : line.getContent();
    +  });
    +};
    +
    +
    +
    +/**
    + * Line metadata class that records the start/end indicies of lines
    + * in a string.  Can be used to implement common newline use cases such as
    + * splitLines() or determining line/column of an index in a string.
    + * Also implements methods to get line contents.
    + *
    + * Indexes are expressed as string indicies into string.substring(), inclusive
    + * at the start, exclusive at the end.
    + *
    + * Create an array of these with goog.string.newlines.getLines().
    + * @param {string} string The original string.
    + * @param {number} startLineIndex The index of the start of the line.
    + * @param {number} endContentIndex The index of the end of the line, excluding
    + *     newlines.
    + * @param {number} endLineIndex The index of the end of the line, index
    + *     newlines.
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.string.newlines.Line = function(string, startLineIndex,
    +                                     endContentIndex, endLineIndex) {
    +  /**
    +   * The original string.
    +   * @type {string}
    +   */
    +  this.string = string;
    +
    +  /**
    +   * Index of the start of the line.
    +   * @type {number}
    +   */
    +  this.startLineIndex = startLineIndex;
    +
    +  /**
    +   * Index of the end of the line, excluding any newline characters.
    +   * Index is the first character after the line, suitable for
    +   * String.substring().
    +   * @type {number}
    +   */
    +  this.endContentIndex = endContentIndex;
    +
    +  /**
    +   * Index of the end of the line, excluding any newline characters.
    +   * Index is the first character after the line, suitable for
    +   * String.substring().
    +   * @type {number}
    +   */
    +
    +  this.endLineIndex = endLineIndex;
    +};
    +
    +
    +/**
    + * @return {string} The content of the line, excluding any newline characters.
    + */
    +goog.string.newlines.Line.prototype.getContent = function() {
    +  return this.string.substring(this.startLineIndex, this.endContentIndex);
    +};
    +
    +
    +/**
    + * @return {string} The full line, including any newline characters.
    + */
    +goog.string.newlines.Line.prototype.getFullLine = function() {
    +  return this.string.substring(this.startLineIndex, this.endLineIndex);
    +};
    +
    +
    +/**
    + * @return {string} The newline characters, if any ('\n', \r', '\r\n', '', etc).
    + */
    +goog.string.newlines.Line.prototype.getNewline = function() {
    +  return this.string.substring(this.endContentIndex, this.endLineIndex);
    +};
    +
    +
    +/**
    + * Splits a string into an array of line metadata.
    + * @param {string} str String to split.
    + * @return {!Array<!goog.string.newlines.Line>} Array of line metadata.
    + */
    +goog.string.newlines.getLines = function(str) {
    +  // We use the constructor because literals are evaluated only once in
    +  // < ES 3.1.
    +  // See http://www.mail-archive.com/es-discuss@mozilla.org/msg01796.html
    +  var re = RegExp('\r\n|\r|\n', 'g');
    +  var sliceIndex = 0;
    +  var result;
    +  var lines = [];
    +
    +  while (result = re.exec(str)) {
    +    var line = new goog.string.newlines.Line(
    +        str, sliceIndex, result.index, result.index + result[0].length);
    +    lines.push(line);
    +
    +    // remember where to start the slice from
    +    sliceIndex = re.lastIndex;
    +  }
    +
    +  // If the string does not end with a newline, add the last line.
    +  if (sliceIndex < str.length) {
    +    var line = new goog.string.newlines.Line(
    +        str, sliceIndex, str.length, str.length);
    +    lines.push(line);
    +  }
    +
    +  return lines;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/string/newlines_test.html b/mapbox-gl/closure-library/closure/goog/string/newlines_test.html
    new file mode 100644
    index 0000000000..dc9854bd46
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/newlines_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.string</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.string.newlinesTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/newlines_test.js b/mapbox-gl/closure-library/closure/goog/string/newlines_test.js
    new file mode 100644
    index 0000000000..6464a18c9e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/newlines_test.js
    @@ -0,0 +1,87 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.string.
    + */
    +
    +goog.provide('goog.string.newlinesTest');
    +
    +goog.require('goog.string.newlines');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.string.newlinesTest');
    +
    +// test for goog.string.splitLines
    +function testSplitLines() {
    +
    +  /**
    +   * @param {!Array<string>} expected
    +   * @param {string} string
    +   * @param {boolean=} opt_keepNewlines
    +   */
    +  function assertSplitLines(expected, string, opt_keepNewlines) {
    +    var keepNewlines = opt_keepNewlines || false;
    +    var lines = goog.string.newlines.splitLines(string, keepNewlines);
    +    assertElementsEquals(expected, lines);
    +  }
    +
    +  // Test values borrowed from Python's splitlines. http://goo.gl/iwawx
    +  assertSplitLines(['abc', 'def', '', 'ghi'], 'abc\ndef\n\rghi');
    +  assertSplitLines(['abc', 'def', '', 'ghi'], 'abc\ndef\n\r\nghi');
    +  assertSplitLines(['abc', 'def', 'ghi'], 'abc\ndef\r\nghi');
    +  assertSplitLines(['abc', 'def', 'ghi'], 'abc\ndef\r\nghi\n');
    +  assertSplitLines(['abc', 'def', 'ghi', ''], 'abc\ndef\r\nghi\n\r');
    +  assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
    +  assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
    +  assertSplitLines(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
    +                   '\nabc\ndef\r\nghi\n\r', true);
    +  assertSplitLines(['', 'abc', 'def', 'ghi', ''], '\nabc\ndef\r\nghi\n\r');
    +  assertSplitLines(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
    +                   '\nabc\ndef\r\nghi\n\r', true);
    +}
    +
    +function testGetLines() {
    +  var lines = goog.string.newlines.getLines('abc\ndef\n\rghi');
    +
    +  assertEquals(4, lines.length);
    +
    +  assertEquals(0, lines[0].startLineIndex);
    +  assertEquals(3, lines[0].endContentIndex);
    +  assertEquals(4, lines[0].endLineIndex);
    +  assertEquals('abc', lines[0].getContent());
    +  assertEquals('abc\n', lines[0].getFullLine());
    +  assertEquals('\n', lines[0].getNewline());
    +
    +  assertEquals(4, lines[1].startLineIndex);
    +  assertEquals(7, lines[1].endContentIndex);
    +  assertEquals(8, lines[1].endLineIndex);
    +  assertEquals('def', lines[1].getContent());
    +  assertEquals('def\n', lines[1].getFullLine());
    +  assertEquals('\n', lines[1].getNewline());
    +
    +  assertEquals(8, lines[2].startLineIndex);
    +  assertEquals(8, lines[2].endContentIndex);
    +  assertEquals(9, lines[2].endLineIndex);
    +  assertEquals('', lines[2].getContent());
    +  assertEquals('\r', lines[2].getFullLine());
    +  assertEquals('\r', lines[2].getNewline());
    +
    +  assertEquals(9, lines[3].startLineIndex);
    +  assertEquals(12, lines[3].endContentIndex);
    +  assertEquals(12, lines[3].endLineIndex);
    +  assertEquals('ghi', lines[3].getContent());
    +  assertEquals('ghi', lines[3].getFullLine());
    +  assertEquals('', lines[3].getNewline());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/parser.js b/mapbox-gl/closure-library/closure/goog/string/parser.js
    new file mode 100644
    index 0000000000..05c6eb8dfb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/parser.js
    @@ -0,0 +1,38 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Defines an interface for parsing strings into objects.
    + */
    +
    +goog.provide('goog.string.Parser');
    +
    +
    +
    +/**
    + * An interface for parsing strings into objects.
    + * @interface
    + */
    +goog.string.Parser = function() {};
    +
    +
    +/**
    + * Parses a string into an object and returns the result.
    + * Agnostic to the format of string and object.
    + *
    + * @param {string} s The string to parse.
    + * @return {*} The object generated from the string.
    + */
    +goog.string.Parser.prototype.parse;
    diff --git a/mapbox-gl/closure-library/closure/goog/string/path.js b/mapbox-gl/closure-library/closure/goog/string/path.js
    new file mode 100644
    index 0000000000..7a9dfff8b5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/path.js
    @@ -0,0 +1,169 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for dealing with POSIX path strings. Based on
    + * Python's os.path and posixpath.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.string.path');
    +
    +goog.require('goog.array');
    +goog.require('goog.string');
    +
    +
    +/**
    + * Returns the final component of a pathname.
    + * See http://docs.python.org/library/os.path.html#os.path.basename
    + * @param {string} path A pathname.
    + * @return {string} path The final component of a pathname, i.e. everything
    + *     after the final slash.
    + */
    +goog.string.path.baseName = function(path) {
    +  var i = path.lastIndexOf('/') + 1;
    +  return path.slice(i);
    +};
    +
    +
    +/**
    + * Alias to goog.string.path.baseName.
    + * @param {string} path A pathname.
    + * @return {string} path The final component of a pathname.
    + * @deprecated Use goog.string.path.baseName.
    + */
    +goog.string.path.basename = goog.string.path.baseName;
    +
    +
    +/**
    + * Returns the directory component of a pathname.
    + * See http://docs.python.org/library/os.path.html#os.path.dirname
    + * @param {string} path A pathname.
    + * @return {string} The directory component of a pathname, i.e. everything
    + *     leading up to the final slash.
    + */
    +goog.string.path.dirname = function(path) {
    +  var i = path.lastIndexOf('/') + 1;
    +  var head = path.slice(0, i);
    +  // If the path isn't all forward slashes, trim the trailing slashes.
    +  if (!/^\/+$/.test(head)) {
    +    head = head.replace(/\/+$/, '');
    +  }
    +  return head;
    +};
    +
    +
    +/**
    + * Extracts the extension part of a pathname.
    + * @param {string} path The path name to process.
    + * @return {string} The extension if any, otherwise the empty string.
    + */
    +goog.string.path.extension = function(path) {
    +  var separator = '.';
    +  // Combining all adjacent periods in the basename to a single period.
    +  var baseName = goog.string.path.baseName(path).replace(/\.+/g, separator);
    +  var separatorIndex = baseName.lastIndexOf(separator);
    +  return separatorIndex <= 0 ? '' : baseName.substr(separatorIndex + 1);
    +};
    +
    +
    +/**
    + * Joins one or more path components (e.g. 'foo/' and 'bar' make 'foo/bar').
    + * An absolute component will discard all previous component.
    + * See http://docs.python.org/library/os.path.html#os.path.join
    + * @param {...string} var_args One of more path components.
    + * @return {string} The path components joined.
    + */
    +goog.string.path.join = function(var_args) {
    +  var path = arguments[0];
    +
    +  for (var i = 1; i < arguments.length; i++) {
    +    var arg = arguments[i];
    +    if (goog.string.startsWith(arg, '/')) {
    +      path = arg;
    +    } else if (path == '' || goog.string.endsWith(path, '/')) {
    +      path += arg;
    +    } else {
    +      path += '/' + arg;
    +    }
    +  }
    +
    +  return path;
    +};
    +
    +
    +/**
    + * Normalizes a pathname by collapsing duplicate separators, parent directory
    + * references ('..'), and current directory references ('.').
    + * See http://docs.python.org/library/os.path.html#os.path.normpath
    + * @param {string} path One or more path components.
    + * @return {string} The path after normalization.
    + */
    +goog.string.path.normalizePath = function(path) {
    +  if (path == '') {
    +    return '.';
    +  }
    +
    +  var initialSlashes = '';
    +  // POSIX will keep two slashes, but three or more will be collapsed to one.
    +  if (goog.string.startsWith(path, '/')) {
    +    initialSlashes = '/';
    +    if (goog.string.startsWith(path, '//') &&
    +        !goog.string.startsWith(path, '///')) {
    +      initialSlashes = '//';
    +    }
    +  }
    +
    +  var parts = path.split('/');
    +  var newParts = [];
    +
    +  for (var i = 0; i < parts.length; i++) {
    +    var part = parts[i];
    +
    +    // '' and '.' don't change the directory, ignore.
    +    if (part == '' || part == '.') {
    +      continue;
    +    }
    +
    +    // A '..' should pop a directory unless this is not an absolute path and
    +    // we're at the root, or we've travelled upwards relatively in the last
    +    // iteration.
    +    if (part != '..' ||
    +        (!initialSlashes && !newParts.length) ||
    +        goog.array.peek(newParts) == '..') {
    +      newParts.push(part);
    +    } else {
    +      newParts.pop();
    +    }
    +  }
    +
    +  var returnPath = initialSlashes + newParts.join('/');
    +  return returnPath || '.';
    +};
    +
    +
    +/**
    + * Splits a pathname into "dirname" and "baseName" components, where "baseName"
    + * is everything after the final slash. Either part may return an empty string.
    + * See http://docs.python.org/library/os.path.html#os.path.split
    + * @param {string} path A pathname.
    + * @return {!Array<string>} An array of [dirname, basename].
    + */
    +goog.string.path.split = function(path) {
    +  var head = goog.string.path.dirname(path);
    +  var tail = goog.string.path.baseName(path);
    +  return [head, tail];
    +};
    +
    +// TODO(nnaze): Implement other useful functions from os.path
    diff --git a/mapbox-gl/closure-library/closure/goog/string/path_test.html b/mapbox-gl/closure-library/closure/goog/string/path_test.html
    new file mode 100644
    index 0000000000..7caad288f9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/path_test.html
    @@ -0,0 +1,21 @@
    +<!doctype html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.string.path
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.string.pathTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/path_test.js b/mapbox-gl/closure-library/closure/goog/string/path_test.js
    new file mode 100644
    index 0000000000..79f0081705
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/path_test.js
    @@ -0,0 +1,108 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.pathTest');
    +goog.setTestOnly('goog.string.pathTest');
    +
    +goog.require('goog.string.path');
    +goog.require('goog.testing.jsunit');
    +
    +// Some test data comes from Python's posixpath tests.
    +// See http://svn.python.org/view/python/trunk/Lib/test/test_posixpath.py
    +
    +function testBasename() {
    +  assertEquals('bar', goog.string.path.baseName('/foo/bar'));
    +  assertEquals('', goog.string.path.baseName('/'));
    +  assertEquals('foo', goog.string.path.baseName('foo'));
    +  assertEquals('foo', goog.string.path.baseName('////foo'));
    +  assertEquals('bar', goog.string.path.baseName('//foo//bar'));
    +}
    +
    +function testDirname() {
    +  assertEquals('/foo', goog.string.path.dirname('/foo/bar'));
    +  assertEquals('/', goog.string.path.dirname('/'));
    +  assertEquals('', goog.string.path.dirname('foo'));
    +  assertEquals('////', goog.string.path.dirname('////foo'));
    +  assertEquals('//foo', goog.string.path.dirname('//foo//bar'));
    +}
    +
    +function testJoin() {
    +  assertEquals('/bar/baz',
    +      goog.string.path.join('/foo', 'bar', '/bar', 'baz'));
    +  assertEquals('/foo/bar/baz',
    +      goog.string.path.join('/foo', 'bar', 'baz'));
    +  assertEquals('/foo/bar/baz',
    +      goog.string.path.join('/foo/', 'bar', 'baz'));
    +  assertEquals('/foo/bar/baz/',
    +      goog.string.path.join('/foo/', 'bar/', 'baz/'));
    +}
    +
    +function testNormalizePath() {
    +  assertEquals('.', goog.string.path.normalizePath(''));
    +  assertEquals('.', goog.string.path.normalizePath('./'));
    +  assertEquals('/', goog.string.path.normalizePath('/'));
    +  assertEquals('//', goog.string.path.normalizePath('//'));
    +  assertEquals('/', goog.string.path.normalizePath('///'));
    +  assertEquals('/foo/bar',
    +               goog.string.path.normalizePath('///foo/.//bar//'));
    +  assertEquals('/foo/baz',
    +               goog.string.path.normalizePath('///foo/.//bar//.//..//.//baz'));
    +  assertEquals('/foo/bar',
    +               goog.string.path.normalizePath('///..//./foo/.//bar'));
    +  assertEquals('../../cat/dog',
    +               goog.string.path.normalizePath('../../cat/dog/'));
    +  assertEquals('../dog',
    +               goog.string.path.normalizePath('../cat/../dog/'));
    +  assertEquals('/cat/dog',
    +               goog.string.path.normalizePath('/../cat/dog/'));
    +  assertEquals('/dog',
    +               goog.string.path.normalizePath('/../cat/../dog'));
    +  assertEquals('/dog',
    +               goog.string.path.normalizePath('/../../../dog'));
    +}
    +
    +function testSplit() {
    +  assertArrayEquals(['/foo', 'bar'], goog.string.path.split('/foo/bar'));
    +  assertArrayEquals(['/', ''], goog.string.path.split('/'));
    +  assertArrayEquals(['', 'foo'], goog.string.path.split('foo'));
    +  assertArrayEquals(['////', 'foo'], goog.string.path.split('////foo'));
    +  assertArrayEquals(['//foo', 'bar'], goog.string.path.split('//foo//bar'));
    +}
    +
    +function testExtension() {
    +  assertEquals('jpg', goog.string.path.extension('././foo/bar/baz.jpg'));
    +  assertEquals('jpg', goog.string.path.extension('././foo bar/baz.jpg'));
    +  assertEquals('jpg', goog.string.path.extension(
    +      'foo/bar/baz/blah blah.jpg'));
    +  assertEquals('', goog.string.path.extension('../../foo/bar/baz baz'));
    +  assertEquals('', goog.string.path.extension('../../foo bar/baz baz'));
    +  assertEquals('', goog.string.path.extension('foo/bar/.'));
    +  assertEquals('', goog.string.path.extension('  '));
    +  assertEquals('', goog.string.path.extension(''));
    +  assertEquals('', goog.string.path.extension('/home/username/.bashrc'));
    +
    +  // Tests cases taken from python os.path.splitext().
    +  assertEquals('bar', goog.string.path.extension('foo.bar'));
    +  assertEquals('bar', goog.string.path.extension('foo.boo.bar'));
    +  assertEquals('bar', goog.string.path.extension('foo.boo.biff.bar'));
    +  assertEquals('rc', goog.string.path.extension('.csh.rc'));
    +  assertEquals('', goog.string.path.extension('nodots'));
    +  assertEquals('', goog.string.path.extension('.cshrc'));
    +  assertEquals('', goog.string.path.extension('...manydots'));
    +  assertEquals('ext', goog.string.path.extension('...manydots.ext'));
    +  assertEquals('', goog.string.path.extension('.'));
    +  assertEquals('', goog.string.path.extension('..'));
    +  assertEquals('', goog.string.path.extension('........'));
    +  assertEquals('', goog.string.path.extension(''));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/string.js b/mapbox-gl/closure-library/closure/goog/string/string.js
    new file mode 100644
    index 0000000000..2ac328223f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/string.js
    @@ -0,0 +1,1565 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for string manipulation.
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +/**
    + * Namespace for string utilities
    + */
    +goog.provide('goog.string');
    +goog.provide('goog.string.Unicode');
    +
    +
    +/**
    + * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
    + * with detection of double-escaping as this letter is frequently used.
    + */
    +goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);
    +
    +
    +/**
    + * @define {boolean} Whether to force non-dom html unescaping.
    + */
    +goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);
    +
    +
    +/**
    + * Common Unicode string characters.
    + * @enum {string}
    + */
    +goog.string.Unicode = {
    +  NBSP: '\xa0'
    +};
    +
    +
    +/**
    + * Fast prefix-checker.
    + * @param {string} str The string to check.
    + * @param {string} prefix A string to look for at the start of {@code str}.
    + * @return {boolean} True if {@code str} begins with {@code prefix}.
    + */
    +goog.string.startsWith = function(str, prefix) {
    +  return str.lastIndexOf(prefix, 0) == 0;
    +};
    +
    +
    +/**
    + * Fast suffix-checker.
    + * @param {string} str The string to check.
    + * @param {string} suffix A string to look for at the end of {@code str}.
    + * @return {boolean} True if {@code str} ends with {@code suffix}.
    + */
    +goog.string.endsWith = function(str, suffix) {
    +  var l = str.length - suffix.length;
    +  return l >= 0 && str.indexOf(suffix, l) == l;
    +};
    +
    +
    +/**
    + * Case-insensitive prefix-checker.
    + * @param {string} str The string to check.
    + * @param {string} prefix  A string to look for at the end of {@code str}.
    + * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
    + *     case).
    + */
    +goog.string.caseInsensitiveStartsWith = function(str, prefix) {
    +  return goog.string.caseInsensitiveCompare(
    +      prefix, str.substr(0, prefix.length)) == 0;
    +};
    +
    +
    +/**
    + * Case-insensitive suffix-checker.
    + * @param {string} str The string to check.
    + * @param {string} suffix A string to look for at the end of {@code str}.
    + * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
    + *     case).
    + */
    +goog.string.caseInsensitiveEndsWith = function(str, suffix) {
    +  return goog.string.caseInsensitiveCompare(
    +      suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
    +};
    +
    +
    +/**
    + * Case-insensitive equality checker.
    + * @param {string} str1 First string to check.
    + * @param {string} str2 Second string to check.
    + * @return {boolean} True if {@code str1} and {@code str2} are the same string,
    + *     ignoring case.
    + */
    +goog.string.caseInsensitiveEquals = function(str1, str2) {
    +  return str1.toLowerCase() == str2.toLowerCase();
    +};
    +
    +
    +/**
    + * Does simple python-style string substitution.
    + * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
    + * @param {string} str The string containing the pattern.
    + * @param {...*} var_args The items to substitute into the pattern.
    + * @return {string} A copy of {@code str} in which each occurrence of
    + *     {@code %s} has been replaced an argument from {@code var_args}.
    + */
    +goog.string.subs = function(str, var_args) {
    +  var splitParts = str.split('%s');
    +  var returnString = '';
    +
    +  var subsArguments = Array.prototype.slice.call(arguments, 1);
    +  while (subsArguments.length &&
    +         // Replace up to the last split part. We are inserting in the
    +         // positions between split parts.
    +         splitParts.length > 1) {
    +    returnString += splitParts.shift() + subsArguments.shift();
    +  }
    +
    +  return returnString + splitParts.join('%s'); // Join unused '%s'
    +};
    +
    +
    +/**
    + * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
    + * and tabs) to a single space, and strips leading and trailing whitespace.
    + * @param {string} str Input string.
    + * @return {string} A copy of {@code str} with collapsed whitespace.
    + */
    +goog.string.collapseWhitespace = function(str) {
    +  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    +  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    +  // include it in the regexp to enforce consistent cross-browser behavior.
    +  return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
    +};
    +
    +
    +/**
    + * Checks if a string is empty or contains only whitespaces.
    + * @param {string} str The string to check.
    + * @return {boolean} Whether {@code str} is empty or whitespace only.
    + */
    +goog.string.isEmptyOrWhitespace = function(str) {
    +  // testing length == 0 first is actually slower in all browsers (about the
    +  // same in Opera).
    +  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    +  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    +  // include it in the regexp to enforce consistent cross-browser behavior.
    +  return /^[\s\xa0]*$/.test(str);
    +};
    +
    +
    +/**
    + * Checks if a string is empty.
    + * @param {string} str The string to check.
    + * @return {boolean} Whether {@code str} is empty.
    + */
    +goog.string.isEmptyString = function(str) {
    +  return str.length == 0;
    +};
    +
    +
    +/**
    + * Checks if a string is empty or contains only whitespaces.
    + *
    + * TODO(user): Deprecate this when clients have been switched over to
    + * goog.string.isEmptyOrWhitespace.
    + *
    + * @param {string} str The string to check.
    + * @return {boolean} Whether {@code str} is empty or whitespace only.
    + */
    +goog.string.isEmpty = goog.string.isEmptyOrWhitespace;
    +
    +
    +/**
    + * Checks if a string is null, undefined, empty or contains only whitespaces.
    + * @param {*} str The string to check.
    + * @return {boolean} Whether {@code str} is null, undefined, empty, or
    + *     whitespace only.
    + * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))
    + *     instead.
    + */
    +goog.string.isEmptyOrWhitespaceSafe = function(str) {
    +  return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));
    +};
    +
    +
    +/**
    + * Checks if a string is null, undefined, empty or contains only whitespaces.
    + *
    + * TODO(user): Deprecate this when clients have been switched over to
    + * goog.string.isEmptyOrWhitespaceSafe.
    + *
    + * @param {*} str The string to check.
    + * @return {boolean} Whether {@code str} is null, undefined, empty, or
    + *     whitespace only.
    + */
    +goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;
    +
    +
    +/**
    + * Checks if a string is all breaking whitespace.
    + * @param {string} str The string to check.
    + * @return {boolean} Whether the string is all breaking whitespace.
    + */
    +goog.string.isBreakingWhitespace = function(str) {
    +  return !/[^\t\n\r ]/.test(str);
    +};
    +
    +
    +/**
    + * Checks if a string contains all letters.
    + * @param {string} str string to check.
    + * @return {boolean} True if {@code str} consists entirely of letters.
    + */
    +goog.string.isAlpha = function(str) {
    +  return !/[^a-zA-Z]/.test(str);
    +};
    +
    +
    +/**
    + * Checks if a string contains only numbers.
    + * @param {*} str string to check. If not a string, it will be
    + *     casted to one.
    + * @return {boolean} True if {@code str} is numeric.
    + */
    +goog.string.isNumeric = function(str) {
    +  return !/[^0-9]/.test(str);
    +};
    +
    +
    +/**
    + * Checks if a string contains only numbers or letters.
    + * @param {string} str string to check.
    + * @return {boolean} True if {@code str} is alphanumeric.
    + */
    +goog.string.isAlphaNumeric = function(str) {
    +  return !/[^a-zA-Z0-9]/.test(str);
    +};
    +
    +
    +/**
    + * Checks if a character is a space character.
    + * @param {string} ch Character to check.
    + * @return {boolean} True if {code ch} is a space.
    + */
    +goog.string.isSpace = function(ch) {
    +  return ch == ' ';
    +};
    +
    +
    +/**
    + * Checks if a character is a valid unicode character.
    + * @param {string} ch Character to check.
    + * @return {boolean} True if {code ch} is a valid unicode character.
    + */
    +goog.string.isUnicodeChar = function(ch) {
    +  return ch.length == 1 && ch >= ' ' && ch <= '~' ||
    +         ch >= '\u0080' && ch <= '\uFFFD';
    +};
    +
    +
    +/**
    + * Takes a string and replaces newlines with a space. Multiple lines are
    + * replaced with a single space.
    + * @param {string} str The string from which to strip newlines.
    + * @return {string} A copy of {@code str} stripped of newlines.
    + */
    +goog.string.stripNewlines = function(str) {
    +  return str.replace(/(\r\n|\r|\n)+/g, ' ');
    +};
    +
    +
    +/**
    + * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
    + * @param {string} str The string to in which to canonicalize newlines.
    + * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
    + */
    +goog.string.canonicalizeNewlines = function(str) {
    +  return str.replace(/(\r\n|\r|\n)/g, '\n');
    +};
    +
    +
    +/**
    + * Normalizes whitespace in a string, replacing all whitespace chars with
    + * a space.
    + * @param {string} str The string in which to normalize whitespace.
    + * @return {string} A copy of {@code str} with all whitespace normalized.
    + */
    +goog.string.normalizeWhitespace = function(str) {
    +  return str.replace(/\xa0|\s/g, ' ');
    +};
    +
    +
    +/**
    + * Normalizes spaces in a string, replacing all consecutive spaces and tabs
    + * with a single space. Replaces non-breaking space with a space.
    + * @param {string} str The string in which to normalize spaces.
    + * @return {string} A copy of {@code str} with all consecutive spaces and tabs
    + *    replaced with a single space.
    + */
    +goog.string.normalizeSpaces = function(str) {
    +  return str.replace(/\xa0|[ \t]+/g, ' ');
    +};
    +
    +
    +/**
    + * Removes the breaking spaces from the left and right of the string and
    + * collapses the sequences of breaking spaces in the middle into single spaces.
    + * The original and the result strings render the same way in HTML.
    + * @param {string} str A string in which to collapse spaces.
    + * @return {string} Copy of the string with normalized breaking spaces.
    + */
    +goog.string.collapseBreakingSpaces = function(str) {
    +  return str.replace(/[\t\r\n ]+/g, ' ').replace(
    +      /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
    +};
    +
    +
    +/**
    + * Trims white spaces to the left and right of a string.
    + * @param {string} str The string to trim.
    + * @return {string} A trimmed copy of {@code str}.
    + */
    +goog.string.trim = (goog.TRUSTED_SITE && String.prototype.trim) ?
    +    function(str) {
    +      return str.trim();
    +    } :
    +    function(str) {
    +      // Since IE doesn't include non-breaking-space (0xa0) in their \s
    +      // character class (as required by section 7.2 of the ECMAScript spec),
    +      // we explicitly include it in the regexp to enforce consistent
    +      // cross-browser behavior.
    +      return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
    +    };
    +
    +
    +/**
    + * Trims whitespaces at the left end of a string.
    + * @param {string} str The string to left trim.
    + * @return {string} A trimmed copy of {@code str}.
    + */
    +goog.string.trimLeft = function(str) {
    +  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    +  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    +  // include it in the regexp to enforce consistent cross-browser behavior.
    +  return str.replace(/^[\s\xa0]+/, '');
    +};
    +
    +
    +/**
    + * Trims whitespaces at the right end of a string.
    + * @param {string} str The string to right trim.
    + * @return {string} A trimmed copy of {@code str}.
    + */
    +goog.string.trimRight = function(str) {
    +  // Since IE doesn't include non-breaking-space (0xa0) in their \s character
    +  // class (as required by section 7.2 of the ECMAScript spec), we explicitly
    +  // include it in the regexp to enforce consistent cross-browser behavior.
    +  return str.replace(/[\s\xa0]+$/, '');
    +};
    +
    +
    +/**
    + * A string comparator that ignores case.
    + * -1 = str1 less than str2
    + *  0 = str1 equals str2
    + *  1 = str1 greater than str2
    + *
    + * @param {string} str1 The string to compare.
    + * @param {string} str2 The string to compare {@code str1} to.
    + * @return {number} The comparator result, as described above.
    + */
    +goog.string.caseInsensitiveCompare = function(str1, str2) {
    +  var test1 = String(str1).toLowerCase();
    +  var test2 = String(str2).toLowerCase();
    +
    +  if (test1 < test2) {
    +    return -1;
    +  } else if (test1 == test2) {
    +    return 0;
    +  } else {
    +    return 1;
    +  }
    +};
    +
    +
    +/**
    + * Regular expression used for splitting a string into substrings of fractional
    + * numbers, integers, and non-numeric characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
    +
    +
    +/**
    + * String comparison function that handles numbers in a way humans might expect.
    + * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
    + * comparison is mostly case-insensitive, though strings that are identical
    + * except for case are sorted with the upper-case strings before lower-case.
    + *
    + * This comparison function is significantly slower (about 500x) than either
    + * the default or the case-insensitive compare. It should not be used in
    + * time-critical code, but should be fast enough to sort several hundred short
    + * strings (like filenames) with a reasonable delay.
    + *
    + * @param {string} str1 The string to compare in a numerically sensitive way.
    + * @param {string} str2 The string to compare {@code str1} to.
    + * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
    + *     0 if str1 > str2.
    + */
    +goog.string.numerateCompare = function(str1, str2) {
    +  if (str1 == str2) {
    +    return 0;
    +  }
    +  if (!str1) {
    +    return -1;
    +  }
    +  if (!str2) {
    +    return 1;
    +  }
    +
    +  // Using match to split the entire string ahead of time turns out to be faster
    +  // for most inputs than using RegExp.exec or iterating over each character.
    +  var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
    +  var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
    +
    +  var count = Math.min(tokens1.length, tokens2.length);
    +
    +  for (var i = 0; i < count; i++) {
    +    var a = tokens1[i];
    +    var b = tokens2[i];
    +
    +    // Compare pairs of tokens, returning if one token sorts before the other.
    +    if (a != b) {
    +
    +      // Only if both tokens are integers is a special comparison required.
    +      // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
    +      var num1 = parseInt(a, 10);
    +      if (!isNaN(num1)) {
    +        var num2 = parseInt(b, 10);
    +        if (!isNaN(num2) && num1 - num2) {
    +          return num1 - num2;
    +        }
    +      }
    +      return a < b ? -1 : 1;
    +    }
    +  }
    +
    +  // If one string is a substring of the other, the shorter string sorts first.
    +  if (tokens1.length != tokens2.length) {
    +    return tokens1.length - tokens2.length;
    +  }
    +
    +  // The two strings must be equivalent except for case (perfect equality is
    +  // tested at the head of the function.) Revert to default ASCII-betical string
    +  // comparison to stablize the sort.
    +  return str1 < str2 ? -1 : 1;
    +};
    +
    +
    +/**
    + * URL-encodes a string
    + * @param {*} str The string to url-encode.
    + * @return {string} An encoded copy of {@code str} that is safe for urls.
    + *     Note that '#', ':', and other characters used to delimit portions
    + *     of URLs *will* be encoded.
    + */
    +goog.string.urlEncode = function(str) {
    +  return encodeURIComponent(String(str));
    +};
    +
    +
    +/**
    + * URL-decodes the string. We need to specially handle '+'s because
    + * the javascript library doesn't convert them to spaces.
    + * @param {string} str The string to url decode.
    + * @return {string} The decoded {@code str}.
    + */
    +goog.string.urlDecode = function(str) {
    +  return decodeURIComponent(str.replace(/\+/g, ' '));
    +};
    +
    +
    +/**
    + * Converts \n to <br>s or <br />s.
    + * @param {string} str The string in which to convert newlines.
    + * @param {boolean=} opt_xml Whether to use XML compatible tags.
    + * @return {string} A copy of {@code str} with converted newlines.
    + */
    +goog.string.newLineToBr = function(str, opt_xml) {
    +  return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
    +};
    +
    +
    +/**
    + * Escapes double quote '"' and single quote '\'' characters in addition to
    + * '&', '<', and '>' so that a string can be included in an HTML tag attribute
    + * value within double or single quotes.
    + *
    + * It should be noted that > doesn't need to be escaped for the HTML or XML to
    + * be valid, but it has been decided to escape it for consistency with other
    + * implementations.
    + *
    + * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
    + * lowercase letter "e".
    + *
    + * NOTE(user):
    + * HtmlEscape is often called during the generation of large blocks of HTML.
    + * Using statics for the regular expressions and strings is an optimization
    + * that can more than half the amount of time IE spends in this function for
    + * large apps, since strings and regexes both contribute to GC allocations.
    + *
    + * Testing for the presence of a character before escaping increases the number
    + * of function calls, but actually provides a speed increase for the average
    + * case -- since the average case often doesn't require the escaping of all 4
    + * characters and indexOf() is much cheaper than replace().
    + * The worst case does suffer slightly from the additional calls, therefore the
    + * opt_isLikelyToContainHtmlChars option has been included for situations
    + * where all 4 HTML entities are very likely to be present and need escaping.
    + *
    + * Some benchmarks (times tended to fluctuate +-0.05ms):
    + *                                     FireFox                     IE6
    + * (no chars / average (mix of cases) / all 4 chars)
    + * no checks                     0.13 / 0.22 / 0.22         0.23 / 0.53 / 0.80
    + * indexOf                       0.08 / 0.17 / 0.26         0.22 / 0.54 / 0.84
    + * indexOf + re test             0.07 / 0.17 / 0.28         0.19 / 0.50 / 0.85
    + *
    + * An additional advantage of checking if replace actually needs to be called
    + * is a reduction in the number of object allocations, so as the size of the
    + * application grows the difference between the various methods would increase.
    + *
    + * @param {string} str string to be escaped.
    + * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
    + *     if the character needs replacing - use this option if you expect each of
    + *     the characters to appear often. Leave false if you expect few html
    + *     characters to occur in your strings, such as if you are escaping HTML.
    + * @return {string} An escaped copy of {@code str}.
    + */
    +goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
    +
    +  if (opt_isLikelyToContainHtmlChars) {
    +    str = str.replace(goog.string.AMP_RE_, '&amp;')
    +          .replace(goog.string.LT_RE_, '&lt;')
    +          .replace(goog.string.GT_RE_, '&gt;')
    +          .replace(goog.string.QUOT_RE_, '&quot;')
    +          .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
    +          .replace(goog.string.NULL_RE_, '&#0;');
    +    if (goog.string.DETECT_DOUBLE_ESCAPING) {
    +      str = str.replace(goog.string.E_RE_, '&#101;');
    +    }
    +    return str;
    +
    +  } else {
    +    // quick test helps in the case when there are no chars to replace, in
    +    // worst case this makes barely a difference to the time taken
    +    if (!goog.string.ALL_RE_.test(str)) return str;
    +
    +    // str.indexOf is faster than regex.test in this case
    +    if (str.indexOf('&') != -1) {
    +      str = str.replace(goog.string.AMP_RE_, '&amp;');
    +    }
    +    if (str.indexOf('<') != -1) {
    +      str = str.replace(goog.string.LT_RE_, '&lt;');
    +    }
    +    if (str.indexOf('>') != -1) {
    +      str = str.replace(goog.string.GT_RE_, '&gt;');
    +    }
    +    if (str.indexOf('"') != -1) {
    +      str = str.replace(goog.string.QUOT_RE_, '&quot;');
    +    }
    +    if (str.indexOf('\'') != -1) {
    +      str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
    +    }
    +    if (str.indexOf('\x00') != -1) {
    +      str = str.replace(goog.string.NULL_RE_, '&#0;');
    +    }
    +    if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
    +      str = str.replace(goog.string.E_RE_, '&#101;');
    +    }
    +    return str;
    +  }
    +};
    +
    +
    +/**
    + * Regular expression that matches an ampersand, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.AMP_RE_ = /&/g;
    +
    +
    +/**
    + * Regular expression that matches a less than sign, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.LT_RE_ = /</g;
    +
    +
    +/**
    + * Regular expression that matches a greater than sign, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.GT_RE_ = />/g;
    +
    +
    +/**
    + * Regular expression that matches a double quote, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.QUOT_RE_ = /"/g;
    +
    +
    +/**
    + * Regular expression that matches a single quote, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.SINGLE_QUOTE_RE_ = /'/g;
    +
    +
    +/**
    + * Regular expression that matches null character, for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.NULL_RE_ = /\x00/g;
    +
    +
    +/**
    + * Regular expression that matches a lowercase letter "e", for use in escaping.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.E_RE_ = /e/g;
    +
    +
    +/**
    + * Regular expression that matches any character that needs to be escaped.
    + * @const {!RegExp}
    + * @private
    + */
    +goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ?
    +    /[\x00&<>"'e]/ :
    +    /[\x00&<>"']/);
    +
    +
    +/**
    + * Unescapes an HTML string.
    + *
    + * @param {string} str The string to unescape.
    + * @return {string} An unescaped copy of {@code str}.
    + */
    +goog.string.unescapeEntities = function(str) {
    +  if (goog.string.contains(str, '&')) {
    +    // We are careful not to use a DOM if we do not have one or we explicitly
    +    // requested non-DOM html unescaping.
    +    if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&
    +        'document' in goog.global) {
    +      return goog.string.unescapeEntitiesUsingDom_(str);
    +    } else {
    +      // Fall back on pure XML entities
    +      return goog.string.unescapePureXmlEntities_(str);
    +    }
    +  }
    +  return str;
    +};
    +
    +
    +/**
    + * Unescapes a HTML string using the provided document.
    + *
    + * @param {string} str The string to unescape.
    + * @param {!Document} document A document to use in escaping the string.
    + * @return {string} An unescaped copy of {@code str}.
    + */
    +goog.string.unescapeEntitiesWithDocument = function(str, document) {
    +  if (goog.string.contains(str, '&')) {
    +    return goog.string.unescapeEntitiesUsingDom_(str, document);
    +  }
    +  return str;
    +};
    +
    +
    +/**
    + * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
    + * entities. This function is XSS-safe and whitespace-preserving.
    + * @private
    + * @param {string} str The string to unescape.
    + * @param {Document=} opt_document An optional document to use for creating
    + *     elements. If this is not specified then the default window.document
    + *     will be used.
    + * @return {string} The unescaped {@code str} string.
    + */
    +goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
    +  /** @type {!Object<string, string>} */
    +  var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
    +  var div;
    +  if (opt_document) {
    +    div = opt_document.createElement('div');
    +  } else {
    +    div = goog.global.document.createElement('div');
    +  }
    +  // Match as many valid entity characters as possible. If the actual entity
    +  // happens to be shorter, it will still work as innerHTML will return the
    +  // trailing characters unchanged. Since the entity characters do not include
    +  // open angle bracket, there is no chance of XSS from the innerHTML use.
    +  // Since no whitespace is passed to innerHTML, whitespace is preserved.
    +  return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
    +    // Check for cached entity.
    +    var value = seen[s];
    +    if (value) {
    +      return value;
    +    }
    +    // Check for numeric entity.
    +    if (entity.charAt(0) == '#') {
    +      // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
    +      var n = Number('0' + entity.substr(1));
    +      if (!isNaN(n)) {
    +        value = String.fromCharCode(n);
    +      }
    +    }
    +    // Fall back to innerHTML otherwise.
    +    if (!value) {
    +      // Append a non-entity character to avoid a bug in Webkit that parses
    +      // an invalid entity at the end of innerHTML text as the empty string.
    +      div.innerHTML = s + ' ';
    +      // Then remove the trailing character from the result.
    +      value = div.firstChild.nodeValue.slice(0, -1);
    +    }
    +    // Cache and return.
    +    return seen[s] = value;
    +  });
    +};
    +
    +
    +/**
    + * Unescapes XML entities.
    + * @private
    + * @param {string} str The string to unescape.
    + * @return {string} An unescaped copy of {@code str}.
    + */
    +goog.string.unescapePureXmlEntities_ = function(str) {
    +  return str.replace(/&([^;]+);/g, function(s, entity) {
    +    switch (entity) {
    +      case 'amp':
    +        return '&';
    +      case 'lt':
    +        return '<';
    +      case 'gt':
    +        return '>';
    +      case 'quot':
    +        return '"';
    +      default:
    +        if (entity.charAt(0) == '#') {
    +          // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
    +          var n = Number('0' + entity.substr(1));
    +          if (!isNaN(n)) {
    +            return String.fromCharCode(n);
    +          }
    +        }
    +        // For invalid entities we just return the entity
    +        return s;
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Regular expression that matches an HTML entity.
    + * See also HTML5: Tokenization / Tokenizing character references.
    + * @private
    + * @type {!RegExp}
    + */
    +goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
    +
    +
    +/**
    + * Do escaping of whitespace to preserve spatial formatting. We use character
    + * entity #160 to make it safer for xml.
    + * @param {string} str The string in which to escape whitespace.
    + * @param {boolean=} opt_xml Whether to use XML compatible tags.
    + * @return {string} An escaped copy of {@code str}.
    + */
    +goog.string.whitespaceEscape = function(str, opt_xml) {
    +  // This doesn't use goog.string.preserveSpaces for backwards compatibility.
    +  return goog.string.newLineToBr(str.replace(/  /g, ' &#160;'), opt_xml);
    +};
    +
    +
    +/**
    + * Preserve spaces that would be otherwise collapsed in HTML by replacing them
    + * with non-breaking space Unicode characters.
    + * @param {string} str The string in which to preserve whitespace.
    + * @return {string} A copy of {@code str} with preserved whitespace.
    + */
    +goog.string.preserveSpaces = function(str) {
    +  return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
    +};
    +
    +
    +/**
    + * Strip quote characters around a string.  The second argument is a string of
    + * characters to treat as quotes.  This can be a single character or a string of
    + * multiple character and in that case each of those are treated as possible
    + * quote characters. For example:
    + *
    + * <pre>
    + * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
    + * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
    + * </pre>
    + *
    + * @param {string} str The string to strip.
    + * @param {string} quoteChars The quote characters to strip.
    + * @return {string} A copy of {@code str} without the quotes.
    + */
    +goog.string.stripQuotes = function(str, quoteChars) {
    +  var length = quoteChars.length;
    +  for (var i = 0; i < length; i++) {
    +    var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
    +    if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
    +      return str.substring(1, str.length - 1);
    +    }
    +  }
    +  return str;
    +};
    +
    +
    +/**
    + * Truncates a string to a certain length and adds '...' if necessary.  The
    + * length also accounts for the ellipsis, so a maximum length of 10 and a string
    + * 'Hello World!' produces 'Hello W...'.
    + * @param {string} str The string to truncate.
    + * @param {number} chars Max number of characters.
    + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    + *     characters from being cut off in the middle.
    + * @return {string} The truncated {@code str} string.
    + */
    +goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
    +  if (opt_protectEscapedCharacters) {
    +    str = goog.string.unescapeEntities(str);
    +  }
    +
    +  if (str.length > chars) {
    +    str = str.substring(0, chars - 3) + '...';
    +  }
    +
    +  if (opt_protectEscapedCharacters) {
    +    str = goog.string.htmlEscape(str);
    +  }
    +
    +  return str;
    +};
    +
    +
    +/**
    + * Truncate a string in the middle, adding "..." if necessary,
    + * and favoring the beginning of the string.
    + * @param {string} str The string to truncate the middle of.
    + * @param {number} chars Max number of characters.
    + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
    + *     characters from being cutoff in the middle.
    + * @param {number=} opt_trailingChars Optional number of trailing characters to
    + *     leave at the end of the string, instead of truncating as close to the
    + *     middle as possible.
    + * @return {string} A truncated copy of {@code str}.
    + */
    +goog.string.truncateMiddle = function(str, chars,
    +    opt_protectEscapedCharacters, opt_trailingChars) {
    +  if (opt_protectEscapedCharacters) {
    +    str = goog.string.unescapeEntities(str);
    +  }
    +
    +  if (opt_trailingChars && str.length > chars) {
    +    if (opt_trailingChars > chars) {
    +      opt_trailingChars = chars;
    +    }
    +    var endPoint = str.length - opt_trailingChars;
    +    var startPoint = chars - opt_trailingChars;
    +    str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
    +  } else if (str.length > chars) {
    +    // Favor the beginning of the string:
    +    var half = Math.floor(chars / 2);
    +    var endPos = str.length - half;
    +    half += chars % 2;
    +    str = str.substring(0, half) + '...' + str.substring(endPos);
    +  }
    +
    +  if (opt_protectEscapedCharacters) {
    +    str = goog.string.htmlEscape(str);
    +  }
    +
    +  return str;
    +};
    +
    +
    +/**
    + * Special chars that need to be escaped for goog.string.quote.
    + * @private {!Object<string, string>}
    + */
    +goog.string.specialEscapeChars_ = {
    +  '\0': '\\0',
    +  '\b': '\\b',
    +  '\f': '\\f',
    +  '\n': '\\n',
    +  '\r': '\\r',
    +  '\t': '\\t',
    +  '\x0B': '\\x0B', // '\v' is not supported in JScript
    +  '"': '\\"',
    +  '\\': '\\\\'
    +};
    +
    +
    +/**
    + * Character mappings used internally for goog.string.escapeChar.
    + * @private {!Object<string, string>}
    + */
    +goog.string.jsEscapeCache_ = {
    +  '\'': '\\\''
    +};
    +
    +
    +/**
    + * Encloses a string in double quotes and escapes characters so that the
    + * string is a valid JS string.
    + * @param {string} s The string to quote.
    + * @return {string} A copy of {@code s} surrounded by double quotes.
    + */
    +goog.string.quote = function(s) {
    +  s = String(s);
    +  if (s.quote) {
    +    return s.quote();
    +  } else {
    +    var sb = ['"'];
    +    for (var i = 0; i < s.length; i++) {
    +      var ch = s.charAt(i);
    +      var cc = ch.charCodeAt(0);
    +      sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
    +          ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
    +    }
    +    sb.push('"');
    +    return sb.join('');
    +  }
    +};
    +
    +
    +/**
    + * Takes a string and returns the escaped string for that character.
    + * @param {string} str The string to escape.
    + * @return {string} An escaped string representing {@code str}.
    + */
    +goog.string.escapeString = function(str) {
    +  var sb = [];
    +  for (var i = 0; i < str.length; i++) {
    +    sb[i] = goog.string.escapeChar(str.charAt(i));
    +  }
    +  return sb.join('');
    +};
    +
    +
    +/**
    + * Takes a character and returns the escaped string for that character. For
    + * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
    + * @param {string} c The character to escape.
    + * @return {string} An escaped string representing {@code c}.
    + */
    +goog.string.escapeChar = function(c) {
    +  if (c in goog.string.jsEscapeCache_) {
    +    return goog.string.jsEscapeCache_[c];
    +  }
    +
    +  if (c in goog.string.specialEscapeChars_) {
    +    return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
    +  }
    +
    +  var rv = c;
    +  var cc = c.charCodeAt(0);
    +  if (cc > 31 && cc < 127) {
    +    rv = c;
    +  } else {
    +    // tab is 9 but handled above
    +    if (cc < 256) {
    +      rv = '\\x';
    +      if (cc < 16 || cc > 256) {
    +        rv += '0';
    +      }
    +    } else {
    +      rv = '\\u';
    +      if (cc < 4096) { // \u1000
    +        rv += '0';
    +      }
    +    }
    +    rv += cc.toString(16).toUpperCase();
    +  }
    +
    +  return goog.string.jsEscapeCache_[c] = rv;
    +};
    +
    +
    +/**
    + * Determines whether a string contains a substring.
    + * @param {string} str The string to search.
    + * @param {string} subString The substring to search for.
    + * @return {boolean} Whether {@code str} contains {@code subString}.
    + */
    +goog.string.contains = function(str, subString) {
    +  return str.indexOf(subString) != -1;
    +};
    +
    +
    +/**
    + * Determines whether a string contains a substring, ignoring case.
    + * @param {string} str The string to search.
    + * @param {string} subString The substring to search for.
    + * @return {boolean} Whether {@code str} contains {@code subString}.
    + */
    +goog.string.caseInsensitiveContains = function(str, subString) {
    +  return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
    +};
    +
    +
    +/**
    + * Returns the non-overlapping occurrences of ss in s.
    + * If either s or ss evalutes to false, then returns zero.
    + * @param {string} s The string to look in.
    + * @param {string} ss The string to look for.
    + * @return {number} Number of occurrences of ss in s.
    + */
    +goog.string.countOf = function(s, ss) {
    +  return s && ss ? s.split(ss).length - 1 : 0;
    +};
    +
    +
    +/**
    + * Removes a substring of a specified length at a specific
    + * index in a string.
    + * @param {string} s The base string from which to remove.
    + * @param {number} index The index at which to remove the substring.
    + * @param {number} stringLength The length of the substring to remove.
    + * @return {string} A copy of {@code s} with the substring removed or the full
    + *     string if nothing is removed or the input is invalid.
    + */
    +goog.string.removeAt = function(s, index, stringLength) {
    +  var resultStr = s;
    +  // If the index is greater or equal to 0 then remove substring
    +  if (index >= 0 && index < s.length && stringLength > 0) {
    +    resultStr = s.substr(0, index) +
    +        s.substr(index + stringLength, s.length - index - stringLength);
    +  }
    +  return resultStr;
    +};
    +
    +
    +/**
    + *  Removes the first occurrence of a substring from a string.
    + *  @param {string} s The base string from which to remove.
    + *  @param {string} ss The string to remove.
    + *  @return {string} A copy of {@code s} with {@code ss} removed or the full
    + *      string if nothing is removed.
    + */
    +goog.string.remove = function(s, ss) {
    +  var re = new RegExp(goog.string.regExpEscape(ss), '');
    +  return s.replace(re, '');
    +};
    +
    +
    +/**
    + *  Removes all occurrences of a substring from a string.
    + *  @param {string} s The base string from which to remove.
    + *  @param {string} ss The string to remove.
    + *  @return {string} A copy of {@code s} with {@code ss} removed or the full
    + *      string if nothing is removed.
    + */
    +goog.string.removeAll = function(s, ss) {
    +  var re = new RegExp(goog.string.regExpEscape(ss), 'g');
    +  return s.replace(re, '');
    +};
    +
    +
    +/**
    + * Escapes characters in the string that are not safe to use in a RegExp.
    + * @param {*} s The string to escape. If not a string, it will be casted
    + *     to one.
    + * @return {string} A RegExp safe, escaped copy of {@code s}.
    + */
    +goog.string.regExpEscape = function(s) {
    +  return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
    +      replace(/\x08/g, '\\x08');
    +};
    +
    +
    +/**
    + * Repeats a string n times.
    + * @param {string} string The string to repeat.
    + * @param {number} length The number of times to repeat.
    + * @return {string} A string containing {@code length} repetitions of
    + *     {@code string}.
    + */
    +goog.string.repeat = function(string, length) {
    +  return new Array(length + 1).join(string);
    +};
    +
    +
    +/**
    + * Pads number to given length and optionally rounds it to a given precision.
    + * For example:
    + * <pre>padNumber(1.25, 2, 3) -> '01.250'
    + * padNumber(1.25, 2) -> '01.25'
    + * padNumber(1.25, 2, 1) -> '01.3'
    + * padNumber(1.25, 0) -> '1.25'</pre>
    + *
    + * @param {number} num The number to pad.
    + * @param {number} length The desired length.
    + * @param {number=} opt_precision The desired precision.
    + * @return {string} {@code num} as a string with the given options.
    + */
    +goog.string.padNumber = function(num, length, opt_precision) {
    +  var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
    +  var index = s.indexOf('.');
    +  if (index == -1) {
    +    index = s.length;
    +  }
    +  return goog.string.repeat('0', Math.max(0, length - index)) + s;
    +};
    +
    +
    +/**
    + * Returns a string representation of the given object, with
    + * null and undefined being returned as the empty string.
    + *
    + * @param {*} obj The object to convert.
    + * @return {string} A string representation of the {@code obj}.
    + */
    +goog.string.makeSafe = function(obj) {
    +  return obj == null ? '' : String(obj);
    +};
    +
    +
    +/**
    + * Concatenates string expressions. This is useful
    + * since some browsers are very inefficient when it comes to using plus to
    + * concat strings. Be careful when using null and undefined here since
    + * these will not be included in the result. If you need to represent these
    + * be sure to cast the argument to a String first.
    + * For example:
    + * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
    + * buildString(null, undefined) -> ''
    + * </pre>
    + * @param {...*} var_args A list of strings to concatenate. If not a string,
    + *     it will be casted to one.
    + * @return {string} The concatenation of {@code var_args}.
    + */
    +goog.string.buildString = function(var_args) {
    +  return Array.prototype.join.call(arguments, '');
    +};
    +
    +
    +/**
    + * Returns a string with at least 64-bits of randomness.
    + *
    + * Doesn't trust Javascript's random function entirely. Uses a combination of
    + * random and current timestamp, and then encodes the string in base-36 to
    + * make it shorter.
    + *
    + * @return {string} A random string, e.g. sn1s7vb4gcic.
    + */
    +goog.string.getRandomString = function() {
    +  var x = 2147483648;
    +  return Math.floor(Math.random() * x).toString(36) +
    +         Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
    +};
    +
    +
    +/**
    + * Compares two version numbers.
    + *
    + * @param {string|number} version1 Version of first item.
    + * @param {string|number} version2 Version of second item.
    + *
    + * @return {number}  1 if {@code version1} is higher.
    + *                   0 if arguments are equal.
    + *                  -1 if {@code version2} is higher.
    + */
    +goog.string.compareVersions = function(version1, version2) {
    +  var order = 0;
    +  // Trim leading and trailing whitespace and split the versions into
    +  // subversions.
    +  var v1Subs = goog.string.trim(String(version1)).split('.');
    +  var v2Subs = goog.string.trim(String(version2)).split('.');
    +  var subCount = Math.max(v1Subs.length, v2Subs.length);
    +
    +  // Iterate over the subversions, as long as they appear to be equivalent.
    +  for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
    +    var v1Sub = v1Subs[subIdx] || '';
    +    var v2Sub = v2Subs[subIdx] || '';
    +
    +    // Split the subversions into pairs of numbers and qualifiers (like 'b').
    +    // Two different RegExp objects are needed because they are both using
    +    // the 'g' flag.
    +    var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    +    var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
    +    do {
    +      var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
    +      var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
    +      // Break if there are no more matches.
    +      if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
    +        break;
    +      }
    +
    +      // Parse the numeric part of the subversion. A missing number is
    +      // equivalent to 0.
    +      var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
    +      var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
    +
    +      // Compare the subversion components. The number has the highest
    +      // precedence. Next, if the numbers are equal, a subversion without any
    +      // qualifier is always higher than a subversion with any qualifier. Next,
    +      // the qualifiers are compared as strings.
    +      order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
    +          goog.string.compareElements_(v1Comp[2].length == 0,
    +              v2Comp[2].length == 0) ||
    +          goog.string.compareElements_(v1Comp[2], v2Comp[2]);
    +      // Stop as soon as an inequality is discovered.
    +    } while (order == 0);
    +  }
    +
    +  return order;
    +};
    +
    +
    +/**
    + * Compares elements of a version number.
    + *
    + * @param {string|number|boolean} left An element from a version number.
    + * @param {string|number|boolean} right An element from a version number.
    + *
    + * @return {number}  1 if {@code left} is higher.
    + *                   0 if arguments are equal.
    + *                  -1 if {@code right} is higher.
    + * @private
    + */
    +goog.string.compareElements_ = function(left, right) {
    +  if (left < right) {
    +    return -1;
    +  } else if (left > right) {
    +    return 1;
    +  }
    +  return 0;
    +};
    +
    +
    +/**
    + * Maximum value of #goog.string.hashCode, exclusive. 2^32.
    + * @type {number}
    + * @private
    + */
    +goog.string.HASHCODE_MAX_ = 0x100000000;
    +
    +
    +/**
    + * String hash function similar to java.lang.String.hashCode().
    + * The hash code for a string is computed as
    + * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
    + * where s[i] is the ith character of the string and n is the length of
    + * the string. We mod the result to make it between 0 (inclusive) and 2^32
    + * (exclusive).
    + * @param {string} str A string.
    + * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
    + *  (exclusive). The empty string returns 0.
    + */
    +goog.string.hashCode = function(str) {
    +  var result = 0;
    +  for (var i = 0; i < str.length; ++i) {
    +    result = 31 * result + str.charCodeAt(i);
    +    // Normalize to 4 byte range, 0 ... 2^32.
    +    result %= goog.string.HASHCODE_MAX_;
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * The most recent unique ID. |0 is equivalent to Math.floor in this case.
    + * @type {number}
    + * @private
    + */
    +goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
    +
    +
    +/**
    + * Generates and returns a string which is unique in the current document.
    + * This is useful, for example, to create unique IDs for DOM elements.
    + * @return {string} A unique id.
    + */
    +goog.string.createUniqueString = function() {
    +  return 'goog_' + goog.string.uniqueStringCounter_++;
    +};
    +
    +
    +/**
    + * Converts the supplied string to a number, which may be Infinity or NaN.
    + * This function strips whitespace: (toNumber(' 123') === 123)
    + * This function accepts scientific notation: (toNumber('1e1') === 10)
    + *
    + * This is better than Javascript's built-in conversions because, sadly:
    + *     (Number(' ') === 0) and (parseFloat('123a') === 123)
    + *
    + * @param {string} str The string to convert.
    + * @return {number} The number the supplied string represents, or NaN.
    + */
    +goog.string.toNumber = function(str) {
    +  var num = Number(str);
    +  if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {
    +    return NaN;
    +  }
    +  return num;
    +};
    +
    +
    +/**
    + * Returns whether the given string is lower camel case (e.g. "isFooBar").
    + *
    + * Note that this assumes the string is entirely letters.
    + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    + *
    + * @param {string} str String to test.
    + * @return {boolean} Whether the string is lower camel case.
    + */
    +goog.string.isLowerCamelCase = function(str) {
    +  return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
    +};
    +
    +
    +/**
    + * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
    + *
    + * Note that this assumes the string is entirely letters.
    + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
    + *
    + * @param {string} str String to test.
    + * @return {boolean} Whether the string is upper camel case.
    + */
    +goog.string.isUpperCamelCase = function(str) {
    +  return /^([A-Z][a-z]*)+$/.test(str);
    +};
    +
    +
    +/**
    + * Converts a string from selector-case to camelCase (e.g. from
    + * "multi-part-string" to "multiPartString"), useful for converting
    + * CSS selectors and HTML dataset keys to their equivalent JS properties.
    + * @param {string} str The string in selector-case form.
    + * @return {string} The string in camelCase form.
    + */
    +goog.string.toCamelCase = function(str) {
    +  return String(str).replace(/\-([a-z])/g, function(all, match) {
    +    return match.toUpperCase();
    +  });
    +};
    +
    +
    +/**
    + * Converts a string from camelCase to selector-case (e.g. from
    + * "multiPartString" to "multi-part-string"), useful for converting JS
    + * style and dataset properties to equivalent CSS selectors and HTML keys.
    + * @param {string} str The string in camelCase form.
    + * @return {string} The string in selector-case form.
    + */
    +goog.string.toSelectorCase = function(str) {
    +  return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
    +};
    +
    +
    +/**
    + * Converts a string into TitleCase. First character of the string is always
    + * capitalized in addition to the first letter of every subsequent word.
    + * Words are delimited by one or more whitespaces by default. Custom delimiters
    + * can optionally be specified to replace the default, which doesn't preserve
    + * whitespace delimiters and instead must be explicitly included if needed.
    + *
    + * Default delimiter => " ":
    + *    goog.string.toTitleCase('oneTwoThree')    => 'OneTwoThree'
    + *    goog.string.toTitleCase('one two three')  => 'One Two Three'
    + *    goog.string.toTitleCase('  one   two   ') => '  One   Two   '
    + *    goog.string.toTitleCase('one_two_three')  => 'One_two_three'
    + *    goog.string.toTitleCase('one-two-three')  => 'One-two-three'
    + *
    + * Custom delimiter => "_-.":
    + *    goog.string.toTitleCase('oneTwoThree', '_-.')       => 'OneTwoThree'
    + *    goog.string.toTitleCase('one two three', '_-.')     => 'One two three'
    + *    goog.string.toTitleCase('  one   two   ', '_-.')    => '  one   two   '
    + *    goog.string.toTitleCase('one_two_three', '_-.')     => 'One_Two_Three'
    + *    goog.string.toTitleCase('one-two-three', '_-.')     => 'One-Two-Three'
    + *    goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
    + *    goog.string.toTitleCase('one. two. three', '_-.')   => 'One. two. three'
    + *    goog.string.toTitleCase('one-two.three', '_-.')     => 'One-Two.Three'
    + *
    + * @param {string} str String value in camelCase form.
    + * @param {string=} opt_delimiters Custom delimiter character set used to
    + *      distinguish words in the string value. Each character represents a
    + *      single delimiter. When provided, default whitespace delimiter is
    + *      overridden and must be explicitly included if needed.
    + * @return {string} String value in TitleCase form.
    + */
    +goog.string.toTitleCase = function(str, opt_delimiters) {
    +  var delimiters = goog.isString(opt_delimiters) ?
    +      goog.string.regExpEscape(opt_delimiters) : '\\s';
    +
    +  // For IE8, we need to prevent using an empty character set. Otherwise,
    +  // incorrect matching will occur.
    +  delimiters = delimiters ? '|[' + delimiters + ']+' : '';
    +
    +  var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
    +  return str.replace(regexp, function(all, p1, p2) {
    +    return p1 + p2.toUpperCase();
    +  });
    +};
    +
    +
    +/**
    + * Capitalizes a string, i.e. converts the first letter to uppercase
    + * and all other letters to lowercase, e.g.:
    + *
    + * goog.string.capitalize('one')     => 'One'
    + * goog.string.capitalize('ONE')     => 'One'
    + * goog.string.capitalize('one two') => 'One two'
    + *
    + * Note that this function does not trim initial whitespace.
    + *
    + * @param {string} str String value to capitalize.
    + * @return {string} String value with first letter in uppercase.
    + */
    +goog.string.capitalize = function(str) {
    +  return String(str.charAt(0)).toUpperCase() +
    +      String(str.substr(1)).toLowerCase();
    +};
    +
    +
    +/**
    + * Parse a string in decimal or hexidecimal ('0xFFFF') form.
    + *
    + * To parse a particular radix, please use parseInt(string, radix) directly. See
    + * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
    + *
    + * This is a wrapper for the built-in parseInt function that will only parse
    + * numbers as base 10 or base 16.  Some JS implementations assume strings
    + * starting with "0" are intended to be octal. ES3 allowed but discouraged
    + * this behavior. ES5 forbids it.  This function emulates the ES5 behavior.
    + *
    + * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
    + *
    + * @param {string|number|null|undefined} value The value to be parsed.
    + * @return {number} The number, parsed. If the string failed to parse, this
    + *     will be NaN.
    + */
    +goog.string.parseInt = function(value) {
    +  // Force finite numbers to strings.
    +  if (isFinite(value)) {
    +    value = String(value);
    +  }
    +
    +  if (goog.isString(value)) {
    +    // If the string starts with '0x' or '-0x', parse as hex.
    +    return /^\s*-?0x/i.test(value) ?
    +        parseInt(value, 16) : parseInt(value, 10);
    +  }
    +
    +  return NaN;
    +};
    +
    +
    +/**
    + * Splits a string on a separator a limited number of times.
    + *
    + * This implementation is more similar to Python or Java, where the limit
    + * parameter specifies the maximum number of splits rather than truncating
    + * the number of results.
    + *
    + * See http://docs.python.org/2/library/stdtypes.html#str.split
    + * See JavaDoc: http://goo.gl/F2AsY
    + * See Mozilla reference: http://goo.gl/dZdZs
    + *
    + * @param {string} str String to split.
    + * @param {string} separator The separator.
    + * @param {number} limit The limit to the number of splits. The resulting array
    + *     will have a maximum length of limit+1.  Negative numbers are the same
    + *     as zero.
    + * @return {!Array<string>} The string, split.
    + */
    +
    +goog.string.splitLimit = function(str, separator, limit) {
    +  var parts = str.split(separator);
    +  var returnVal = [];
    +
    +  // Only continue doing this while we haven't hit the limit and we have
    +  // parts left.
    +  while (limit > 0 && parts.length) {
    +    returnVal.push(parts.shift());
    +    limit--;
    +  }
    +
    +  // If there are remaining parts, append them to the end.
    +  if (parts.length) {
    +    returnVal.push(parts.join(separator));
    +  }
    +
    +  return returnVal;
    +};
    +
    +
    +/**
    + * Computes the Levenshtein edit distance between two strings.
    + * @param {string} a
    + * @param {string} b
    + * @return {number} The edit distance between the two strings.
    + */
    +goog.string.editDistance = function(a, b) {
    +  var v0 = [];
    +  var v1 = [];
    +
    +  if (a == b) {
    +    return 0;
    +  }
    +
    +  if (!a.length || !b.length) {
    +    return Math.max(a.length, b.length);
    +  }
    +
    +  for (var i = 0; i < b.length + 1; i++) {
    +    v0[i] = i;
    +  }
    +
    +  for (var i = 0; i < a.length; i++) {
    +    v1[0] = i + 1;
    +
    +    for (var j = 0; j < b.length; j++) {
    +      var cost = a[i] != b[j];
    +      // Cost for the substring is the minimum of adding one character, removing
    +      // one character, or a swap.
    +      v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
    +    }
    +
    +    for (var j = 0; j < v0.length; j++) {
    +      v0[j] = v1[j];
    +    }
    +  }
    +
    +  return v1[b.length];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/string/string_test.html b/mapbox-gl/closure-library/closure/goog/string/string_test.html
    new file mode 100644
    index 0000000000..11821a5bc7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/string_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.string</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.stringTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/string_test.js b/mapbox-gl/closure-library/closure/goog/string/string_test.js
    new file mode 100644
    index 0000000000..5ac3dc0a47
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/string_test.js
    @@ -0,0 +1,1327 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.string.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.stringTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.stringTest');
    +
    +var stubs;
    +var mockControl;
    +
    +function setUp() {
    +  stubs = new goog.testing.PropertyReplacer();
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +  mockControl.$tearDown();
    +}
    +
    +
    +//=== tests for goog.string.collapseWhitespace ===
    +
    +function testCollapseWhiteSpace() {
    +  var f = goog.string.collapseWhitespace;
    +
    +  assertEquals('Leading spaces not stripped', f('  abc'), 'abc');
    +  assertEquals('Trailing spaces not stripped', f('abc  '), 'abc');
    +  assertEquals('Wrapping spaces not stripped', f('  abc  '), 'abc');
    +
    +  assertEquals('All white space chars not stripped',
    +               f('\xa0\n\t abc\xa0\n\t '), 'abc');
    +
    +  assertEquals('Spaces not collapsed', f('a   b    c'), 'a b c');
    +
    +  assertEquals('Tabs not collapsed', f('a\t\t\tb\tc'), 'a b c');
    +
    +  assertEquals('All check failed', f(' \ta \t \t\tb\t\n\xa0  c  \t\n'),
    +               'a b c');
    +}
    +
    +
    +function testIsEmpty() {
    +  assertTrue(goog.string.isEmpty(''));
    +  assertTrue(goog.string.isEmpty(' '));
    +  assertTrue(goog.string.isEmpty('    '));
    +  assertTrue(goog.string.isEmpty(' \t\t\n\xa0   '));
    +
    +  assertFalse(goog.string.isEmpty(' abc \t\xa0'));
    +  assertFalse(goog.string.isEmpty(' a b c \t'));
    +  assertFalse(goog.string.isEmpty(';'));
    +
    +  assertFalse(goog.string.isEmpty(undefined));
    +  assertFalse(goog.string.isEmpty(null));
    +  assertFalse(goog.string.isEmpty({a: 1, b: 2}));
    +}
    +
    +
    +function testIsEmptyOrWhitespace() {
    +  assertTrue(goog.string.isEmptyOrWhitespace(''));
    +  assertTrue(goog.string.isEmptyOrWhitespace(' '));
    +  assertTrue(goog.string.isEmptyOrWhitespace('    '));
    +  assertTrue(goog.string.isEmptyOrWhitespace(' \t\t\n\xa0   '));
    +
    +  assertFalse(goog.string.isEmptyOrWhitespace(' abc \t\xa0'));
    +  assertFalse(goog.string.isEmptyOrWhitespace(' a b c \t'));
    +  assertFalse(goog.string.isEmptyOrWhitespace(';'));
    +
    +  assertFalse(goog.string.isEmptyOrWhitespace(undefined));
    +  assertFalse(goog.string.isEmptyOrWhitespace(null));
    +  assertFalse(goog.string.isEmptyOrWhitespace({a: 1, b: 2}));
    +}
    +
    +
    +function testIsEmptyString() {
    +  assertTrue(goog.string.isEmptyString(''));
    +
    +  assertFalse(goog.string.isEmptyString(' '));
    +  assertFalse(goog.string.isEmptyString('    '));
    +  assertFalse(goog.string.isEmptyString(' \t\t\n\xa0   '));
    +  assertFalse(goog.string.isEmptyString(' abc \t\xa0'));
    +  assertFalse(goog.string.isEmptyString(' a b c \t'));
    +  assertFalse(goog.string.isEmptyString(';'));
    +
    +  assertFalse(goog.string.isEmptyString({a: 1, b: 2}));
    +}
    +
    +
    +function testIsEmptySafe() {
    +  assertTrue(goog.string.isEmptySafe(''));
    +  assertTrue(goog.string.isEmptySafe(' '));
    +  assertTrue(goog.string.isEmptySafe('    '));
    +  assertTrue(goog.string.isEmptySafe(' \t\t\n\xa0   '));
    +
    +  assertFalse(goog.string.isEmptySafe(' abc \t\xa0'));
    +  assertFalse(goog.string.isEmptySafe(' a b c \t'));
    +  assertFalse(goog.string.isEmptySafe(';'));
    +
    +  assertTrue(goog.string.isEmptySafe(undefined));
    +  assertTrue(goog.string.isEmptySafe(null));
    +  assertFalse(goog.string.isEmptySafe({a: 1, b: 2}));
    +}
    +
    +
    +function testIsEmptyOrWhitespaceSafe() {
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe(''));
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe(' '));
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe('    '));
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe(' \t\t\n\xa0   '));
    +
    +  assertFalse(goog.string.isEmptyOrWhitespaceSafe(' abc \t\xa0'));
    +  assertFalse(goog.string.isEmptyOrWhitespaceSafe(' a b c \t'));
    +  assertFalse(goog.string.isEmptyOrWhitespaceSafe(';'));
    +
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe(undefined));
    +  assertTrue(goog.string.isEmptyOrWhitespaceSafe(null));
    +  assertFalse(goog.string.isEmptyOrWhitespaceSafe({a: 1, b: 2}));
    +}
    +
    +
    +//=== tests for goog.string.isAlpha ===
    +function testIsAlpha() {
    +  assertTrue('"a" should be alpha', goog.string.isAlpha('a'));
    +  assertTrue('"n" should be alpha', goog.string.isAlpha('n'));
    +  assertTrue('"z" should be alpha', goog.string.isAlpha('z'));
    +  assertTrue('"A" should be alpha', goog.string.isAlpha('A'));
    +  assertTrue('"N" should be alpha', goog.string.isAlpha('N'));
    +  assertTrue('"Z" should be alpha', goog.string.isAlpha('Z'));
    +  assertTrue('"aa" should be alpha', goog.string.isAlpha('aa'));
    +  assertTrue('null is alpha', goog.string.isAlpha(null));
    +  assertTrue('undefined is alpha', goog.string.isAlpha(undefined));
    +
    +  assertFalse('"aa!" is not alpha', goog.string.isAlpha('aa!s'));
    +  assertFalse('"!" is not alpha', goog.string.isAlpha('!'));
    +  assertFalse('"0" is not alpha', goog.string.isAlpha('0'));
    +  assertFalse('"5" is not alpha', goog.string.isAlpha('5'));
    +}
    +
    +
    +
    +//=== tests for goog.string.isNumeric ===
    +function testIsNumeric() {
    +  assertTrue('"8" is a numeric string', goog.string.isNumeric('8'));
    +  assertTrue('"5" is a numeric string', goog.string.isNumeric('5'));
    +  assertTrue('"34" is a numeric string', goog.string.isNumeric('34'));
    +  assertTrue('34 is a number', goog.string.isNumeric(34));
    +
    +  assertFalse('"3.14" has a period', goog.string.isNumeric('3.14'));
    +  assertFalse('"A" is a letter', goog.string.isNumeric('A'));
    +  assertFalse('"!" is punctuation', goog.string.isNumeric('!'));
    +  assertFalse('null is not numeric', goog.string.isNumeric(null));
    +  assertFalse('undefined is not numeric', goog.string.isNumeric(undefined));
    +}
    +
    +
    +//=== tests for tests for goog.string.isAlphaNumeric ===
    +function testIsAlphaNumeric() {
    +  assertTrue('"ABCabc" should be alphanumeric',
    +             goog.string.isAlphaNumeric('ABCabc'));
    +  assertTrue('"123" should be alphanumeric', goog.string.isAlphaNumeric('123'));
    +  assertTrue('"ABCabc123" should be alphanumeric',
    +             goog.string.isAlphaNumeric('ABCabc123'));
    +  assertTrue('null is alphanumeric', goog.string.isAlphaNumeric(null));
    +  assertTrue('undefined is alphanumeric',
    +             goog.string.isAlphaNumeric(undefined));
    +
    +  assertFalse('"123!" should not be alphanumeric',
    +              goog.string.isAlphaNumeric('123!'));
    +  assertFalse('"  " should not be alphanumeric',
    +              goog.string.isAlphaNumeric('  '));
    +}
    +
    +
    +//== tests for goog.string.isBreakingWhitespace ===
    +
    +function testIsBreakingWhitespace() {
    +  assertTrue('" " is breaking', goog.string.isBreakingWhitespace(' '));
    +  assertTrue('"\\n" is breaking', goog.string.isBreakingWhitespace('\n'));
    +  assertTrue('"\\t" is breaking', goog.string.isBreakingWhitespace('\t'));
    +  assertTrue('"\\r" is breaking', goog.string.isBreakingWhitespace('\r'));
    +  assertTrue('"\\r\\n\\t " is breaking',
    +      goog.string.isBreakingWhitespace('\r\n\t '));
    +
    +  assertFalse('nbsp is non-breaking', goog.string.isBreakingWhitespace('\xa0'));
    +  assertFalse('"a" is non-breaking', goog.string.isBreakingWhitespace('a'));
    +  assertFalse('"a\\r" is non-breaking',
    +      goog.string.isBreakingWhitespace('a\r'));
    +}
    +
    +
    +//=== tests for goog.string.isSpace ===
    +function testIsSpace() {
    +  assertTrue('" " is a space', goog.string.isSpace(' '));
    +
    +  assertFalse('"\\n" is not a space', goog.string.isSpace('\n'));
    +  assertFalse('"\\t" is not a space', goog.string.isSpace('\t'));
    +  assertFalse('"  " is not a space, it\'s two spaces',
    +              goog.string.isSpace('  '));
    +  assertFalse('"a" is not a space', goog.string.isSpace('a'));
    +  assertFalse('"3" is not a space', goog.string.isSpace('3'));
    +  assertFalse('"#" is not a space', goog.string.isSpace('#'));
    +  assertFalse('null is not a space', goog.string.isSpace(null));
    +  assertFalse('nbsp is not a space', goog.string.isSpace('\xa0'));
    +}
    +
    +
    +// === tests for goog.string.stripNewlines ===
    +function testStripNewLines() {
    +  assertEquals('Should replace new lines with spaces',
    +               goog.string.stripNewlines('some\nlines\rthat\r\nare\n\nsplit'),
    +               'some lines that are split');
    +}
    +
    +
    +// === tests for goog.string.canonicalizeNewlines ===
    +function testCanonicalizeNewlines() {
    +  assertEquals('Should replace all types of new line with \\n',
    +               goog.string.canonicalizeNewlines(
    +      'some\nlines\rthat\r\nare\n\nsplit'),
    +               'some\nlines\nthat\nare\n\nsplit');
    +}
    +
    +
    +// === tests for goog.string.normalizeWhitespace ===
    +function testNormalizeWhitespace() {
    +  assertEquals('All whitespace chars should be replaced with a normal space',
    +               goog.string.normalizeWhitespace('\xa0 \n\t \xa0 \n\t'),
    +               '         ');
    +}
    +
    +
    +// === tests for goog.string.normalizeSpaces ===
    +function testNormalizeSpaces() {
    +  assertEquals('All whitespace chars should be replaced with a normal space',
    +               goog.string.normalizeSpaces('\xa0 \t \xa0 \t'),
    +               '    ');
    +}
    +
    +function testCollapseBreakingSpaces() {
    +  assertEquals('breaking spaces are collapsed', 'a b',
    +      goog.string.collapseBreakingSpaces(' \t\r\n a \t\r\n b \t\r\n '));
    +  assertEquals('non-breaking spaces are kept', 'a \u00a0\u2000 b',
    +      goog.string.collapseBreakingSpaces('a \u00a0\u2000 b'));
    +}
    +
    +/// === tests for goog.string.trim ===
    +function testTrim() {
    +  assertEquals('Should be the same', goog.string.trim('nothing 2 trim'),
    +               'nothing 2 trim');
    +  assertEquals('Remove spaces', goog.string.trim('   hello  goodbye   '),
    +               'hello  goodbye');
    +  assertEquals('Trim other stuff', goog.string.trim('\n\r\xa0 hi \r\n\xa0'),
    +               'hi');
    +}
    +
    +
    +/// === tests for goog.string.trimLeft ===
    +function testTrimLeft() {
    +  var f = goog.string.trimLeft;
    +  assertEquals('Should be the same', f('nothing to trim'), 'nothing to trim');
    +  assertEquals('Remove spaces', f('   hello  goodbye   '), 'hello  goodbye   ');
    +  assertEquals('Trim other stuff', f('\xa0\n\r hi \r\n\xa0'), 'hi \r\n\xa0');
    +}
    +
    +
    +/// === tests for goog.string.trimRight ===
    +function testTrimRight() {
    +  var f = goog.string.trimRight;
    +  assertEquals('Should be the same', f('nothing to trim'), 'nothing to trim');
    +  assertEquals('Remove spaces', f('   hello  goodbye   '), '   hello  goodbye');
    +  assertEquals('Trim other stuff', f('\n\r\xa0 hi \r\n\xa0'), '\n\r\xa0 hi');
    +}
    +
    +
    +// === tests for goog.string.startsWith ===
    +function testStartsWith() {
    +  assertTrue('Should start with \'\'', goog.string.startsWith('abcd', ''));
    +  assertTrue('Should start with \'ab\'', goog.string.startsWith('abcd', 'ab'));
    +  assertTrue('Should start with \'abcd\'',
    +             goog.string.startsWith('abcd', 'abcd'));
    +  assertFalse('Should not start with \'bcd\'',
    +              goog.string.startsWith('abcd', 'bcd'));
    +}
    +
    +function testEndsWith() {
    +  assertTrue('Should end with \'\'', goog.string.endsWith('abcd', ''));
    +  assertTrue('Should end with \'ab\'', goog.string.endsWith('abcd', 'cd'));
    +  assertTrue('Should end with \'abcd\'', goog.string.endsWith('abcd', 'abcd'));
    +  assertFalse('Should not end \'abc\'', goog.string.endsWith('abcd', 'abc'));
    +  assertFalse('Should not end \'abcde\'',
    +              goog.string.endsWith('abcd', 'abcde'));
    +}
    +
    +
    +// === tests for goog.string.caseInsensitiveStartsWith ===
    +function testCaseInsensitiveStartsWith() {
    +  assertTrue('Should start with \'\'',
    +             goog.string.caseInsensitiveStartsWith('abcd', ''));
    +  assertTrue('Should start with \'ab\'',
    +             goog.string.caseInsensitiveStartsWith('abcd', 'Ab'));
    +  assertTrue('Should start with \'abcd\'',
    +             goog.string.caseInsensitiveStartsWith('AbCd', 'abCd'));
    +  assertFalse('Should not start with \'bcd\'',
    +              goog.string.caseInsensitiveStartsWith('ABCD', 'bcd'));
    +}
    +
    +// === tests for goog.string.caseInsensitiveEndsWith ===
    +function testCaseInsensitiveEndsWith() {
    +  assertTrue('Should end with \'\'',
    +             goog.string.caseInsensitiveEndsWith('abcd', ''));
    +  assertTrue('Should end with \'cd\'',
    +             goog.string.caseInsensitiveEndsWith('abCD', 'cd'));
    +  assertTrue('Should end with \'abcd\'',
    +             goog.string.caseInsensitiveEndsWith('abcd', 'abCd'));
    +  assertFalse('Should not end \'abc\'',
    +              goog.string.caseInsensitiveEndsWith('aBCd', 'ABc'));
    +  assertFalse('Should not end \'abcde\'',
    +              goog.string.caseInsensitiveEndsWith('ABCD', 'abcde'));
    +}
    +
    +// === tests for goog.string.caseInsensitiveEquals ===
    +function testCaseInsensitiveEquals() {
    +
    +  function assertCaseInsensitiveEquals(str1, str2) {
    +    assertTrue(goog.string.caseInsensitiveEquals(str1, str2));
    +  }
    +
    +  function assertCaseInsensitiveNotEquals(str1, str2) {
    +    assertFalse(goog.string.caseInsensitiveEquals(str1, str2));
    +  }
    +
    +  assertCaseInsensitiveEquals('abc', 'abc');
    +  assertCaseInsensitiveEquals('abc', 'abC');
    +  assertCaseInsensitiveEquals('d,e,F,G', 'd,e,F,G');
    +  assertCaseInsensitiveEquals('ABCD EFGH 1234', 'abcd efgh 1234');
    +  assertCaseInsensitiveEquals('FooBarBaz', 'fOObARbAZ');
    +
    +  assertCaseInsensitiveNotEquals('ABCD EFGH', 'abcd efg');
    +  assertCaseInsensitiveNotEquals('ABC DEFGH', 'ABCD EFGH');
    +  assertCaseInsensitiveNotEquals('FooBarBaz', 'fOObARbAZ ');
    +}
    +
    +
    +// === tests for goog.string.subs ===
    +function testSubs() {
    +  assertEquals('Should be the same',
    +               'nothing to subs',
    +               goog.string.subs('nothing to subs'));
    +  assertEquals('Should be the same',
    +               '1',
    +               goog.string.subs('%s', '1'));
    +  assertEquals('Should be the same',
    +               '12true',
    +               goog.string.subs('%s%s%s', '1', 2, true));
    +  function f() {
    +    fail('This should not be called');
    +  }
    +  f.toString = function() { return 'f'; };
    +  assertEquals('Should not call function', 'f', goog.string.subs('%s', f));
    +
    +  // If the string that is to be substituted in contains $& then it will be
    +  // usually be replaced with %s, we need to check goog.string.subs, handles
    +  // this case.
    +  assertEquals('$& should not be substituted with %s', 'Foo Bar $&',
    +      goog.string.subs('Foo %s', 'Bar $&'));
    +
    +  assertEquals('$$ should not be substituted', '_$$_',
    +               goog.string.subs('%s', '_$$_'));
    +  assertEquals('$` should not be substituted', '_$`_',
    +               goog.string.subs('%s', '_$`_'));
    +  assertEquals('$\' should not be substituted', '_$\'_',
    +               goog.string.subs('%s', '_$\'_'));
    +  for (var i = 0; i < 99; i += 9) {
    +    assertEquals('$' + i + ' should not be substituted',
    +        '_$' + i + '_', goog.string.subs('%s', '_$' + i + '_'));
    +  }
    +
    +  assertEquals(
    +      'Only the first three "%s" strings should be replaced.',
    +      'test foo test bar test baz test %s test %s test',
    +      goog.string.subs(
    +          'test %s test %s test %s test %s test %s test',
    +          'foo', 'bar', 'baz'));
    +}
    +
    +
    +/**
    + * Verifies that if too many arguments are given, they are ignored.
    + * Logic test for bug documented here: http://go/eusxz
    + */
    +function testSubsTooManyArguments() {
    +  assertEquals('one', goog.string.subs('one', 'two', 'three'));
    +  assertEquals('onetwo', goog.string.subs('one%s', 'two', 'three'));
    +}
    +
    +
    +// === tests for goog.string.caseInsensitiveCompare ===
    +function testCaseInsensitiveCompare() {
    +  var f = goog.string.caseInsensitiveCompare;
    +
    +  assert('"ABC" should be less than "def"', f('ABC', 'def') == -1);
    +  assert('"abc" should be less than "DEF"', f('abc', 'DEF') == -1);
    +
    +  assert('"XYZ" should equal "xyz"', f('XYZ', 'xyz') == 0);
    +
    +  assert('"XYZ" should be greater than "UVW"', f('xyz', 'UVW') == 1);
    +  assert('"XYZ" should be greater than "uvw"', f('XYZ', 'uvw') == 1);
    +}
    +
    +
    +// === tests for goog.string.numerateCompare ===
    +function testNumerateCompare() {
    +  var f = goog.string.numerateCompare;
    +
    +  // Each comparison in this list is tested to assure that t[0] < t[1],
    +  // t[1] > t[0], and identity tests t[0] == t[0] and t[1] == t[1].
    +  var comparisons = [
    +    ['', '0'],
    +    ['2', '10'],
    +    ['05', '9'],
    +    ['3.14', '3.2'],
    +    ['sub', 'substring'],
    +    ['Photo 7', 'photo 8'], // Case insensitive for most sorts.
    +    ['Mango', 'mango'], // Case sensitive if strings are otherwise identical.
    +    ['album 2 photo 20', 'album 10 photo 20'],
    +    ['album 7 photo 20', 'album 7 photo 100']];
    +
    +  for (var i = 0; i < comparisons.length; i++) {
    +    var t = comparisons[i];
    +    assert(t[0] + ' should be less than ' + t[1],
    +           f(t[0], t[1]) < 0);
    +    assert(t[1] + ' should be greater than ' + t[0],
    +           f(t[1], t[0]) > 0);
    +    assert(t[0] + ' should be equal to ' + t[0],
    +           f(t[0], t[0]) == 0);
    +    assert(t[1] + ' should be equal to ' + t[1],
    +           f(t[1], t[1]) == 0);
    +  }
    +}
    +
    +// === tests for goog.string.urlEncode && .urlDecode ===
    +// NOTE: When test was written it was simply an alias for the built in
    +// 'encodeURICompoent', therefore this test is simply used to make sure that in
    +// the future it doesn't get broken.
    +function testUrlEncodeAndDecode() {
    +  var input = '<p>"hello there," she said, "what is going on here?</p>';
    +  var output = '%3Cp%3E%22hello%20there%2C%22%20she%20said%2C%20%22what%20is' +
    +               '%20going%20on%20here%3F%3C%2Fp%3E';
    +
    +  assertEquals('urlEncode vs encodeURIComponent',
    +               encodeURIComponent(input),
    +               goog.string.urlEncode(input));
    +
    +  assertEquals('urlEncode vs model', goog.string.urlEncode(input), output);
    +
    +  assertEquals('urlDecode vs model', goog.string.urlDecode(output), input);
    +
    +  assertEquals('urlDecode vs urlEncode',
    +               goog.string.urlDecode(goog.string.urlEncode(input)), input);
    +
    +  assertEquals('urlDecode with +s instead of %20s',
    +               goog.string.urlDecode(output.replace(/%20/g, '+')),
    +               input);
    +}
    +
    +
    +// === tests for goog.string.newLineToBr ===
    +function testNewLineToBr() {
    +  var str = 'some\nlines\rthat\r\nare\n\nsplit';
    +  var html = 'some<br>lines<br>that<br>are<br><br>split';
    +  var xhtml = 'some<br />lines<br />that<br />are<br /><br />split';
    +
    +  assertEquals('Should be html', goog.string.newLineToBr(str), html);
    +  assertEquals('Should be html', goog.string.newLineToBr(str, false), html);
    +  assertEquals('Should be xhtml', goog.string.newLineToBr(str, true), xhtml);
    +
    +}
    +
    +
    +// === tests for goog.string.htmlEscape and .unescapeEntities ===
    +function testHtmlEscapeAndUnescapeEntities() {
    +  var text = '\'"x1 < x2 && y2 > y1"\'';
    +  var html = '&#39;&quot;x1 &lt; x2 &amp;&amp; y2 &gt; y1&quot;&#39;';
    +
    +  assertEquals('Testing htmlEscape', html, goog.string.htmlEscape(text));
    +  assertEquals('Testing htmlEscape', html, goog.string.htmlEscape(text, false));
    +  assertEquals('Testing htmlEscape', html, goog.string.htmlEscape(text, true));
    +  assertEquals('Testing unescapeEntities', text,
    +               goog.string.unescapeEntities(html));
    +
    +  assertEquals('escape -> unescape', text,
    +               goog.string.unescapeEntities(goog.string.htmlEscape(text)));
    +  assertEquals('unescape -> escape', html,
    +               goog.string.htmlEscape(goog.string.unescapeEntities(html)));
    +}
    +
    +function testHtmlUnescapeEntitiesWithDocument() {
    +  var documentMock = {
    +    createElement: mockControl.createFunctionMock('createElement')
    +  };
    +  var divMock = document.createElement('div');
    +  documentMock.createElement('div').$returns(divMock);
    +  mockControl.$replayAll();
    +
    +  var html = '&lt;a&b&gt;';
    +  var text = '<a&b>';
    +
    +  assertEquals('wrong unescaped value',
    +      text, goog.string.unescapeEntitiesWithDocument(html, documentMock));
    +  assertNotEquals('divMock.innerHTML should have been used', '',
    +      divMock.innerHTML);
    +  mockControl.$verifyAll();
    +}
    +
    +function testHtmlEscapeAndUnescapeEntitiesUsingDom() {
    +  var text = '"x1 < x2 && y2 > y1"';
    +  var html = '&quot;x1 &lt; x2 &amp;&amp; y2 &gt; y1&quot;';
    +
    +  assertEquals('Testing unescapeEntities',
    +               goog.string.unescapeEntitiesUsingDom_(html), text);
    +  assertEquals('escape -> unescape',
    +               goog.string.unescapeEntitiesUsingDom_(
    +                   goog.string.htmlEscape(text)), text);
    +  assertEquals('unescape -> escape',
    +               goog.string.htmlEscape(
    +                   goog.string.unescapeEntitiesUsingDom_(html)), html);
    +}
    +
    +function testHtmlUnescapeEntitiesUsingDom_withAmpersands() {
    +  var html = '&lt;a&b&gt;';
    +  var text = '<a&b>';
    +
    +  assertEquals('wrong unescaped value',
    +      text, goog.string.unescapeEntitiesUsingDom_(html));
    +}
    +
    +function testHtmlEscapeAndUnescapePureXmlEntities_() {
    +  var text = '"x1 < x2 && y2 > y1"';
    +  var html = '&quot;x1 &lt; x2 &amp;&amp; y2 &gt; y1&quot;';
    +
    +  assertEquals('Testing unescapePureXmlEntities_',
    +               goog.string.unescapePureXmlEntities_(html), text);
    +  assertEquals('escape -> unescape',
    +               goog.string.unescapePureXmlEntities_(
    +                   goog.string.htmlEscape(text)), text);
    +  assertEquals('unescape -> escape',
    +               goog.string.htmlEscape(
    +                   goog.string.unescapePureXmlEntities_(html)), html);
    +}
    +
    +
    +function testForceNonDomHtmlUnescaping() {
    +  stubs.set(goog.string, 'FORCE_NON_DOM_HTML_UNESCAPING', true);
    +  // Set document.createElement to empty object so that the call to
    +  // unescapeEntities will blow up if html unescaping is carried out with DOM.
    +  // Notice that we can't directly set document to empty object since IE8 won't
    +  // let us do so.
    +  stubs.set(goog.global.document, 'createElement', {});
    +  goog.string.unescapeEntities('&quot;x1 &lt; x2 &amp;&amp; y2 &gt; y1&quot;');
    +}
    +
    +
    +function testHtmlEscapeDetectDoubleEscaping() {
    +  stubs.set(goog.string, 'DETECT_DOUBLE_ESCAPING', true);
    +  assertEquals('&#101; &lt; pi', goog.string.htmlEscape('e < pi'));
    +  assertEquals('&#101; &lt; pi', goog.string.htmlEscape('e < pi', true));
    +}
    +
    +function testHtmlEscapeNullByte() {
    +  assertEquals('&#0;', goog.string.htmlEscape('\x00'));
    +  assertEquals('&#0;', goog.string.htmlEscape('\x00', true));
    +  assertEquals('\\x00', goog.string.htmlEscape('\\x00'));
    +  assertEquals('\\x00', goog.string.htmlEscape('\\x00', true));
    +}
    +
    +var globalXssVar = 0;
    +
    +function testXssUnescapeEntities() {
    +  // This tests that we don't have any XSS exploits in unescapeEntities
    +  var test = '&amp;<script defer>globalXssVar=1;</' + 'script>';
    +  var expected = '&<script defer>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapeEntities', expected,
    +               goog.string.unescapeEntities(test));
    +  assertEquals('unescapeEntities is vulnarable to XSS', 0, globalXssVar);
    +
    +  test = '&amp;<script>globalXssVar=1;</' + 'script>';
    +  expected = '&<script>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapeEntities', expected,
    +               goog.string.unescapeEntities(test));
    +  assertEquals('unescapeEntities is vulnarable to XSS', 0, globalXssVar);
    +}
    +
    +
    +function testXssUnescapeEntitiesUsingDom() {
    +  // This tests that we don't have any XSS exploits in unescapeEntitiesUsingDom
    +  var test = '&amp;<script defer>globalXssVar=1;</' + 'script>';
    +  var expected = '&<script defer>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapeEntitiesUsingDom_', expected,
    +               goog.string.unescapeEntitiesUsingDom_(test));
    +  assertEquals('unescapeEntitiesUsingDom_ is vulnerable to XSS', 0,
    +               globalXssVar);
    +
    +  test = '&amp;<script>globalXssVar=1;</' + 'script>';
    +  expected = '&<script>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapeEntitiesUsingDom_', expected,
    +               goog.string.unescapeEntitiesUsingDom_(test));
    +  assertEquals('unescapeEntitiesUsingDom_ is vulnerable to XSS', 0,
    +               globalXssVar);
    +}
    +
    +
    +function testXssUnescapePureXmlEntities() {
    +  // This tests that we don't have any XSS exploits in unescapePureXmlEntities
    +  var test = '&amp;<script defer>globalXssVar=1;</' + 'script>';
    +  var expected = '&<script defer>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapePureXmlEntities_', expected,
    +               goog.string.unescapePureXmlEntities_(test));
    +  assertEquals('unescapePureXmlEntities_ is vulnarable to XSS', 0,
    +               globalXssVar);
    +
    +  test = '&amp;<script>globalXssVar=1;</' + 'script>';
    +  expected = '&<script>globalXssVar=1;</' + 'script>';
    +
    +  assertEquals('Testing unescapePureXmlEntities_', expected,
    +               goog.string.unescapePureXmlEntities_(test));
    +  assertEquals('unescapePureXmlEntities_ is vulnarable to XSS', 0,
    +               globalXssVar);
    +}
    +
    +
    +function testUnescapeEntitiesPreservesWhitespace() {
    +  // This tests that whitespace is preserved (primarily for IE)
    +  // Also make sure leading and trailing whitespace are preserved.
    +  var test = '\nTesting\n\twhitespace\n    preservation\n';
    +  var expected = test;
    +
    +  assertEquals('Testing unescapeEntities', expected,
    +               goog.string.unescapeEntities(test));
    +
    +  // Now with entities
    +  test += ' &amp;&nbsp;\n';
    +  expected += ' &\u00A0\n';
    +  assertEquals('Testing unescapeEntities', expected,
    +               goog.string.unescapeEntities(test));
    +}
    +
    +
    +// === tests for goog.string.whitespaceEscape ===
    +function testWhitespaceEscape() {
    +  assertEquals('Should be the same',
    +      goog.string.whitespaceEscape('one two  three   four    five     '),
    +      'one two &#160;three &#160; four &#160; &#160;five &#160; &#160; ');
    +}
    +
    +
    +// === tests for goog.string.preserveSpaces ===
    +function testPreserveSpaces() {
    +  var nbsp = goog.string.Unicode.NBSP;
    +  assertEquals('', goog.string.preserveSpaces(''));
    +  assertEquals(nbsp + 'a', goog.string.preserveSpaces(' a'));
    +  assertEquals(nbsp + ' a', goog.string.preserveSpaces('  a'));
    +  assertEquals(nbsp + ' ' + nbsp + 'a', goog.string.preserveSpaces('   a'));
    +  assertEquals('a ' + nbsp + 'b', goog.string.preserveSpaces('a  b'));
    +  assertEquals('a\n' + nbsp + 'b', goog.string.preserveSpaces('a\n b'));
    +
    +  // We don't care about trailing spaces.
    +  assertEquals('a ', goog.string.preserveSpaces('a '));
    +  assertEquals('a \n' + nbsp + 'b', goog.string.preserveSpaces('a \n b'));
    +}
    +
    +
    +// === tests for goog.string.stripQuotes ===
    +function testStripQuotes() {
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('"hello"', '"'),
    +               'hello');
    +
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('\'hello\'', '\''),
    +               'hello');
    +
    +  assertEquals('Quotes should not be stripped',
    +               goog.string.stripQuotes('-"hello"', '"'),
    +               '-"hello"');
    +}
    +
    +function testStripQuotesMultiple() {
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('"hello"', '"\''),
    +               'hello');
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('\'hello\'', '"\''),
    +               'hello');
    +
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('\'hello\'', ''),
    +               '\'hello\'');
    +}
    +
    +function testStripQuotesMultiple2() {
    +  // Makes sure we do not strip twice
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('"\'hello\'"', '"\''),
    +               '\'hello\'');
    +  assertEquals('Quotes should be stripped',
    +               goog.string.stripQuotes('"\'hello\'"', '\'"'),
    +               '\'hello\'');
    +
    +}
    +
    +// === tests for goog.string.truncate ===
    +function testTruncate() {
    +  var str = 'abcdefghijklmnopqrstuvwxyz';
    +  assertEquals('Should be equal', goog.string.truncate(str, 8), 'abcde...');
    +  assertEquals('Should be equal', goog.string.truncate(str, 11), 'abcdefgh...');
    +
    +  var html = 'true &amp;&amp; false == false';
    +  assertEquals('Should clip html char', goog.string.truncate(html, 11),
    +               'true &am...');
    +  assertEquals('Should not clip html char',
    +               goog.string.truncate(html, 12, true),
    +               'true &amp;&amp; f...');
    +}
    +
    +
    +// === tests for goog.string.truncateMiddle ===
    +function testTruncateMiddle() {
    +  var str = 'abcdefghijklmnopqrstuvwxyz';
    +  assertEquals('abc...xyz', goog.string.truncateMiddle(str, 6));
    +  assertEquals('abc...yz', goog.string.truncateMiddle(str, 5));
    +  assertEquals(str, goog.string.truncateMiddle(str, str.length));
    +
    +  var html = 'true &amp;&amp; false == false';
    +  assertEquals('Should clip html char', 'true &a...= false',
    +      goog.string.truncateMiddle(html, 14));
    +  assertEquals('Should not clip html char',
    +      'true &amp;&amp;...= false',
    +      goog.string.truncateMiddle(html, 14, true));
    +
    +  assertEquals('ab...xyz', goog.string.truncateMiddle(str, 5, null, 3));
    +  assertEquals('abcdefg...xyz', goog.string.truncateMiddle(str, 10, null, 3));
    +  assertEquals('abcdef...wxyz', goog.string.truncateMiddle(str, 10, null, 4));
    +  assertEquals('...yz', goog.string.truncateMiddle(str, 2, null, 3));
    +  assertEquals(str, goog.string.truncateMiddle(str, 50, null, 3));
    +
    +  assertEquals('Should clip html char', 'true &amp;&...lse',
    +      goog.string.truncateMiddle(html, 14, null, 3));
    +  assertEquals('Should not clip html char',
    +      'true &amp;&amp; fal...lse',
    +      goog.string.truncateMiddle(html, 14, true, 3));
    +}
    +
    +
    +// === goog.string.quote ===
    +function testQuote() {
    +  var str = allChars();
    +  assertEquals(str, eval(goog.string.quote(str)));
    +
    +  // empty string
    +  assertEquals('', eval(goog.string.quote('')));
    +
    +  // unicode
    +  str = allChars(0, 10000);
    +  assertEquals(str, eval(goog.string.quote(str)));
    +}
    +
    +function testQuoteSpecialChars() {
    +  assertEquals('"\\""', goog.string.quote('"'));
    +  assertEquals('"\'"', goog.string.quote("'"));
    +  assertEquals('"\\\\"', goog.string.quote('\\'));
    +
    +  var zeroQuoted = goog.string.quote('\0');
    +  assertTrue(
    +      'goog.string.quote mangles the 0 char: ',
    +      '"\\0"' == zeroQuoted || '"\\x00"' == zeroQuoted);
    +}
    +
    +function testCrossBrowserQuote() {
    +  // The vertical space char has weird semantics on jscript, so we don't test
    +  // that one.
    +  var vertChar = '\x0B'.charCodeAt(0);
    +
    +  // The zero char has two alternate encodings (\0 and \x00) both are ok,
    +  // and tested above.
    +  var zeroChar = 0;
    +
    +  var str = allChars(zeroChar + 1, vertChar) + allChars(vertChar + 1, 10000);
    +  var nativeQuote = goog.string.quote(str);
    +
    +  stubs.set(String.prototype, 'quote', null);
    +  assertNull(''.quote);
    +
    +  assertEquals(nativeQuote, goog.string.quote(str));
    +}
    +
    +function allChars(opt_start, opt_end) {
    +  opt_start = opt_start || 0;
    +  opt_end = opt_end || 256;
    +  var rv = '';
    +  for (var i = opt_start; i < opt_end; i++) {
    +    rv += String.fromCharCode(i);
    +  }
    +  return rv;
    +}
    +
    +function testEscapeString() {
    +  var expected = allChars(0, 10000);
    +  try {
    +    var actual =
    +        eval('"' + goog.string.escapeString(expected) + '"');
    +  } catch (e) {
    +    fail('Quote failed: err ' + e.message);
    +  }
    +  assertEquals(expected, actual);
    +}
    +
    +function testCountOf() {
    +  assertEquals(goog.string.countOf('REDSOXROX', undefined), 0);
    +  assertEquals(goog.string.countOf('REDSOXROX', null), 0);
    +  assertEquals(goog.string.countOf('REDSOXROX', ''), 0);
    +  assertEquals(goog.string.countOf('', undefined), 0);
    +  assertEquals(goog.string.countOf('', null), 0);
    +  assertEquals(goog.string.countOf('', ''), 0);
    +  assertEquals(goog.string.countOf('', 'REDSOXROX'), 0);
    +  assertEquals(goog.string.countOf(undefined, 'R'), 0);
    +  assertEquals(goog.string.countOf(null, 'R'), 0);
    +  assertEquals(goog.string.countOf(undefined, undefined), 0);
    +  assertEquals(goog.string.countOf(null, null), 0);
    +
    +  assertEquals(goog.string.countOf('REDSOXROX', 'R'), 2);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'E'), 1);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'X'), 2);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'RED'), 1);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'ROX'), 1);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'OX'), 2);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'Z'), 0);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'REDSOXROX'), 1);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'YANKEES'), 0);
    +  assertEquals(goog.string.countOf('REDSOXROX', 'EVIL_EMPIRE'), 0);
    +
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'R'), 9);
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'RR'), 4);
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'RRR'), 3);
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'RRRR'), 2);
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'RRRRR'), 1);
    +  assertEquals(goog.string.countOf('RRRRRRRRR', 'RRRRRR'), 1);
    +}
    +
    +function testRemoveAt() {
    +  var str = 'barfoobarbazbar';
    +  str = goog.string.removeAt(str, 0, 3);
    +  assertEquals('Remove first bar', 'foobarbazbar', str);
    +  str = goog.string.removeAt(str, 3, 3);
    +  assertEquals('Remove middle bar', 'foobazbar', str);
    +  str = goog.string.removeAt(str, 6, 3);
    +  assertEquals('Remove last bar', 'foobaz', str);
    +  assertEquals('Invalid negative index', 'foobaz',
    +      goog.string.removeAt(str, -1, 0));
    +  assertEquals('Invalid overflow index', 'foobaz',
    +      goog.string.removeAt(str, 9, 0));
    +  assertEquals('Invalid negative stringLength', 'foobaz',
    +      goog.string.removeAt(str, 0, -1));
    +  assertEquals('Invalid overflow stringLength', '',
    +      goog.string.removeAt(str, 0, 9));
    +  assertEquals('Invalid overflow index and stringLength', 'foobaz',
    +      goog.string.removeAt(str, 9, 9));
    +  assertEquals('Invalid zero stringLength', 'foobaz',
    +      goog.string.removeAt(str, 0, 0));
    +}
    +
    +function testRemove() {
    +  var str = 'barfoobarbazbar';
    +  str = goog.string.remove(str, 'bar');
    +  assertEquals('Remove first bar', 'foobarbazbar', str);
    +  str = goog.string.remove(str, 'bar');
    +  assertEquals('Remove middle bar', 'foobazbar', str);
    +  str = goog.string.remove(str, 'bar');
    +  assertEquals('Remove last bar', 'foobaz', str);
    +  str = goog.string.remove(str, 'bar');
    +  assertEquals('Original string', 'foobaz', str);
    +}
    +
    +function testRemoveAll() {
    +  var str = 'foobarbazbarfoobazfoo';
    +  str = goog.string.removeAll(str, 'foo');
    +  assertEquals('Remove all occurrences of foo', 'barbazbarbaz', str);
    +  str = goog.string.removeAll(str, 'foo');
    +  assertEquals('Original string', 'barbazbarbaz', str);
    +}
    +
    +function testRegExpEscape() {
    +  var spec = '()[]{}+-?*.$^|,:#<!\\';
    +  var escapedSpec = '\\' + spec.split('').join('\\');
    +  assertEquals('special chars', escapedSpec, goog.string.regExpEscape(spec));
    +  assertEquals('backslash b', '\\x08', goog.string.regExpEscape('\b'));
    +
    +  var s = allChars();
    +  var re = new RegExp('^' + goog.string.regExpEscape(s) + '$');
    +  assertTrue('All ASCII', re.test(s));
    +  s = '';
    +  var re = new RegExp('^' + goog.string.regExpEscape(s) + '$');
    +  assertTrue('empty string', re.test(s));
    +  s = allChars(0, 10000);
    +  var re = new RegExp('^' + goog.string.regExpEscape(s) + '$');
    +  assertTrue('Unicode', re.test(s));
    +}
    +
    +function testPadNumber() {
    +  assertEquals('01.250', goog.string.padNumber(1.25, 2, 3));
    +  assertEquals('01.25', goog.string.padNumber(1.25, 2));
    +  assertEquals('01.3', goog.string.padNumber(1.25, 2, 1));
    +  assertEquals('1.25', goog.string.padNumber(1.25, 0));
    +  assertEquals('10', goog.string.padNumber(9.9, 2, 0));
    +  assertEquals('7', goog.string.padNumber(7, 0));
    +  assertEquals('7', goog.string.padNumber(7, 1));
    +  assertEquals('07', goog.string.padNumber(7, 2));
    +}
    +
    +function testAsString() {
    +  assertEquals('', goog.string.makeSafe(null));
    +  assertEquals('', goog.string.makeSafe(undefined));
    +  assertEquals('', goog.string.makeSafe(''));
    +
    +  assertEquals('abc', goog.string.makeSafe('abc'));
    +  assertEquals('123', goog.string.makeSafe(123));
    +  assertEquals('0', goog.string.makeSafe(0));
    +
    +  assertEquals('true', goog.string.makeSafe(true));
    +  assertEquals('false', goog.string.makeSafe(false));
    +
    +  var funky = function() {};
    +  funky.toString = function() { return 'funky-thing' };
    +  assertEquals('funky-thing', goog.string.makeSafe(funky));
    +}
    +
    +function testStringRepeat() {
    +  assertEquals('', goog.string.repeat('*', 0));
    +  assertEquals('*', goog.string.repeat('*', 1));
    +  assertEquals('     ', goog.string.repeat(' ', 5));
    +  assertEquals('__________', goog.string.repeat('_', 10));
    +  assertEquals('aaa', goog.string.repeat('a', 3));
    +  assertEquals('foofoofoofoofoofoo', goog.string.repeat('foo', 6));
    +}
    +
    +function testBuildString() {
    +  assertEquals('', goog.string.buildString());
    +  assertEquals('a', goog.string.buildString('a'));
    +  assertEquals('ab', goog.string.buildString('ab'));
    +  assertEquals('ab', goog.string.buildString('a', 'b'));
    +  assertEquals('abcd', goog.string.buildString('a', 'b', 'c', 'd'));
    +  assertEquals('0', goog.string.buildString(0));
    +  assertEquals('0123', goog.string.buildString(0, 1, 2, 3));
    +  assertEquals('ab01', goog.string.buildString('a', 'b', 0, 1));
    +  assertEquals('', goog.string.buildString(null, undefined));
    +}
    +
    +function testCompareVersions() {
    +  var f = goog.string.compareVersions;
    +  assertTrue('numeric equality broken', f(1, 1) == 0);
    +  assertTrue('numeric less than broken', f(1.0, 1.1) < 0);
    +  assertTrue('numeric greater than broken', f(2.0, 1.1) > 0);
    +
    +  assertTrue('exact equality broken', f('1.0', '1.0') == 0);
    +  assertTrue('mutlidot equality broken', f('1.0.0.0', '1.0') == 0);
    +  assertTrue('mutlidigit equality broken', f('1.000', '1.0') == 0);
    +  assertTrue('less than broken', f('1.0.2.1', '1.1') < 0);
    +  assertTrue('greater than broken', f('1.1', '1.0.2.1') > 0);
    +
    +  assertTrue('substring less than broken', f('1', '1.1') < 0);
    +  assertTrue('substring greater than broken', f('2.2', '2') > 0);
    +
    +  assertTrue('b greater than broken', f('1.1', '1.1b') > 0);
    +  assertTrue('b less than broken', f('1.1b', '1.1') < 0);
    +  assertTrue('b equality broken', f('1.1b', '1.1b') == 0);
    +
    +  assertTrue('b > a broken', f('1.1b', '1.1a') > 0);
    +  assertTrue('a < b broken', f('1.1a', '1.1b') < 0);
    +
    +  assertTrue('9.5 < 9.10 broken', f('9.5', '9.10') < 0);
    +  assertTrue('9.5 < 9.11 broken', f('9.5', '9.11') < 0);
    +  assertTrue('9.11 > 9.10 broken', f('9.11', '9.10') > 0);
    +  assertTrue('9.1 < 9.10 broken', f('9.1', '9.10') < 0);
    +  assertTrue('9.1.1 < 9.10 broken', f('9.1.1', '9.10') < 0);
    +  assertTrue('9.1.1 < 9.11 broken', f('9.1.1', '9.11') < 0);
    +
    +  assertTrue('10a > 9b broken', f('1.10a', '1.9b') > 0);
    +  assertTrue('b < b2 broken', f('1.1b', '1.1b2') < 0);
    +  assertTrue('b10 > b9 broken', f('1.1b10', '1.1b9') > 0);
    +
    +  assertTrue('7 > 6 broken with leading whitespace', f(' 7', '6') > 0);
    +  assertTrue('7 > 6 broken with trailing whitespace', f('7 ', '6') > 0);
    +}
    +
    +function testIsUnicodeChar() {
    +  assertFalse('empty string broken', goog.string.isUnicodeChar(''));
    +  assertFalse('non-single char string broken',
    +              goog.string.isUnicodeChar('abc'));
    +  assertTrue('space broken', goog.string.isUnicodeChar(' '));
    +  assertTrue('single char broken', goog.string.isUnicodeChar('a'));
    +  assertTrue('upper case broken', goog.string.isUnicodeChar('A'));
    +  assertTrue('unicode char broken', goog.string.isUnicodeChar('\u0C07'));
    +}
    +
    +function assertHashcodeEquals(expectedHashCode, str) {
    +  assertEquals('wrong hashCode for ' + str.substring(0, 32),
    +      expectedHashCode, goog.string.hashCode(str));
    +}
    +
    +
    +/**
    + * Verify we get random-ish looking values for hash of Strings.
    + */
    +function testHashCode() {
    +  try {
    +    goog.string.hashCode(null);
    +    fail('should throw exception for null');
    +  } catch (ex) {
    +    // success
    +  }
    +  assertHashcodeEquals(0, '');
    +  assertHashcodeEquals(101574, 'foo');
    +  assertHashcodeEquals(1301670364, '\uAAAAfoo');
    +  assertHashcodeEquals(92567585, goog.string.repeat('a', 5));
    +  assertHashcodeEquals(2869595232, goog.string.repeat('a', 6));
    +  assertHashcodeEquals(3058106369, goog.string.repeat('a', 7));
    +  assertHashcodeEquals(312017024, goog.string.repeat('a', 8));
    +  assertHashcodeEquals(2929737728, goog.string.repeat('a', 1024));
    +}
    +
    +function testUniqueString() {
    +  var TEST_COUNT = 20;
    +
    +  var obj = {};
    +  for (var i = 0; i < TEST_COUNT; i++) {
    +    obj[goog.string.createUniqueString()] = true;
    +  }
    +
    +  assertEquals('All strings should be unique.', TEST_COUNT,
    +      goog.object.getCount(obj));
    +}
    +
    +function testToNumber() {
    +  // First, test the cases goog.string.toNumber() was primarily written for,
    +  // because JS built-ins are dumb.
    +  assertNaN(goog.string.toNumber('123a'));
    +  assertNaN(goog.string.toNumber('123.456.78'));
    +  assertNaN(goog.string.toNumber(''));
    +  assertNaN(goog.string.toNumber(' '));
    +
    +  // Now, sanity-check.
    +  assertEquals(123, goog.string.toNumber(' 123 '));
    +  assertEquals(321.123, goog.string.toNumber('321.123'));
    +  assertEquals(1.00001, goog.string.toNumber('1.00001'));
    +  assertEquals(1, goog.string.toNumber('1.00000'));
    +  assertEquals(0.2, goog.string.toNumber('0.20'));
    +  assertEquals(0, goog.string.toNumber('0'));
    +  assertEquals(0, goog.string.toNumber('0.0'));
    +  assertEquals(-1, goog.string.toNumber('-1'));
    +  assertEquals(-0.3, goog.string.toNumber('-.3'));
    +  assertEquals(-12.345, goog.string.toNumber('-12.345'));
    +  assertEquals(100, goog.string.toNumber('1e2'));
    +  assertEquals(0.123, goog.string.toNumber('12.3e-2'));
    +  assertNaN(goog.string.toNumber('abc'));
    +}
    +
    +function testGetRandomString() {
    +  stubs.set(goog, 'now', goog.functions.constant(1295726605874));
    +  stubs.set(Math, 'random', goog.functions.constant(0.6679361383522245));
    +  assertTrue('String must be alphanumeric',
    +             goog.string.isAlphaNumeric(goog.string.getRandomString()));
    +}
    +
    +function testToCamelCase() {
    +  assertEquals('OneTwoThree', goog.string.toCamelCase('-one-two-three'));
    +  assertEquals('oneTwoThree', goog.string.toCamelCase('one-two-three'));
    +  assertEquals('oneTwo', goog.string.toCamelCase('one-two'));
    +  assertEquals('one', goog.string.toCamelCase('one'));
    +  assertEquals('oneTwo', goog.string.toCamelCase('oneTwo'));
    +  assertEquals('String value matching a native function name.',
    +      'toString', goog.string.toCamelCase('toString'));
    +}
    +
    +function testToSelectorCase() {
    +  assertEquals('-one-two-three', goog.string.toSelectorCase('OneTwoThree'));
    +  assertEquals('one-two-three', goog.string.toSelectorCase('oneTwoThree'));
    +  assertEquals('one-two', goog.string.toSelectorCase('oneTwo'));
    +  assertEquals('one', goog.string.toSelectorCase('one'));
    +  assertEquals('one-two', goog.string.toSelectorCase('one-two'));
    +  assertEquals('String value matching a native function name.',
    +      'to-string', goog.string.toSelectorCase('toString'));
    +}
    +
    +function testToTitleCase() {
    +  assertEquals('One', goog.string.toTitleCase('one'));
    +  assertEquals('CamelCase', goog.string.toTitleCase('camelCase'));
    +  assertEquals('Onelongword', goog.string.toTitleCase('onelongword'));
    +  assertEquals('One Two Three', goog.string.toTitleCase('one two three'));
    +  assertEquals('One        Two    Three',
    +      goog.string.toTitleCase('one        two    three'));
    +  assertEquals('   Longword  ', goog.string.toTitleCase('   longword  '));
    +  assertEquals('One-two-three', goog.string.toTitleCase('one-two-three'));
    +  assertEquals('One_two_three', goog.string.toTitleCase('one_two_three'));
    +  assertEquals('String value matching a native function name.',
    +      'ToString', goog.string.toTitleCase('toString'));
    +
    +  // Verify results with no delimiter.
    +  assertEquals('One two three', goog.string.toTitleCase('one two three', ''));
    +  assertEquals('One-two-three', goog.string.toTitleCase('one-two-three', ''));
    +  assertEquals(' onetwothree', goog.string.toTitleCase(' onetwothree', ''));
    +
    +  // Verify results with one delimiter.
    +  assertEquals('One two', goog.string.toTitleCase('one two', '.'));
    +  assertEquals(' one two', goog.string.toTitleCase(' one two', '.'));
    +  assertEquals(' one.Two', goog.string.toTitleCase(' one.two', '.'));
    +  assertEquals('One.Two', goog.string.toTitleCase('one.two', '.'));
    +  assertEquals('One...Two...', goog.string.toTitleCase('one...two...', '.'));
    +
    +  // Verify results with multiple delimiters.
    +  var delimiters = '_-.';
    +  assertEquals('One two three',
    +      goog.string.toTitleCase('one two three', delimiters));
    +  assertEquals('  one two three',
    +      goog.string.toTitleCase('  one two three', delimiters));
    +  assertEquals('One-Two-Three',
    +      goog.string.toTitleCase('one-two-three', delimiters));
    +  assertEquals('One_Two_Three',
    +      goog.string.toTitleCase('one_two_three', delimiters));
    +  assertEquals('One...Two...Three',
    +      goog.string.toTitleCase('one...two...three', delimiters));
    +  assertEquals('One.  two.  three',
    +      goog.string.toTitleCase('one.  two.  three', delimiters));
    +}
    +
    +function testCapitalize() {
    +  assertEquals('Reptar', goog.string.capitalize('reptar'));
    +  assertEquals('Reptar reptar', goog.string.capitalize('reptar reptar'));
    +  assertEquals('Reptar', goog.string.capitalize('REPTAR'));
    +  assertEquals('Reptar', goog.string.capitalize('Reptar'));
    +  assertEquals('1234', goog.string.capitalize('1234'));
    +  assertEquals('$#@!', goog.string.capitalize('$#@!'));
    +  assertEquals('', goog.string.capitalize(''));
    +  assertEquals('R', goog.string.capitalize('r'));
    +  assertEquals('R', goog.string.capitalize('R'));
    +}
    +
    +function testParseInt() {
    +  // Many example values borrowed from
    +  // http://trac.webkit.org/browser/trunk/LayoutTests/fast/js/kde/
    +  // GlobalObject-expected.txt
    +
    +  // Check non-numbers and strings
    +  assertTrue(isNaN(goog.string.parseInt(undefined)));
    +  assertTrue(isNaN(goog.string.parseInt(null)));
    +  assertTrue(isNaN(goog.string.parseInt({})));
    +
    +  assertTrue(isNaN(goog.string.parseInt('')));
    +  assertTrue(isNaN(goog.string.parseInt(' ')));
    +  assertTrue(isNaN(goog.string.parseInt('a')));
    +  assertTrue(isNaN(goog.string.parseInt('FFAA')));
    +  assertEquals(1, goog.string.parseInt(1));
    +  assertEquals(1234567890123456, goog.string.parseInt(1234567890123456));
    +  assertEquals(2, goog.string.parseInt(' 2.3'));
    +  assertEquals(16, goog.string.parseInt('0x10'));
    +  assertEquals(11, goog.string.parseInt('11'));
    +  assertEquals(15, goog.string.parseInt('0xF'));
    +  assertEquals(15, goog.string.parseInt('0XF'));
    +  assertEquals(3735928559, goog.string.parseInt('0XDEADBEEF'));
    +  assertEquals(3, goog.string.parseInt('3x'));
    +  assertEquals(3, goog.string.parseInt('3 x'));
    +  assertFalse(isFinite(goog.string.parseInt('Infinity')));
    +  assertEquals(15, goog.string.parseInt('15'));
    +  assertEquals(15, goog.string.parseInt('015'));
    +  assertEquals(15, goog.string.parseInt('0xf'));
    +  assertEquals(15, goog.string.parseInt('15'));
    +  assertEquals(15, goog.string.parseInt('0xF'));
    +  assertEquals(15, goog.string.parseInt('15.99'));
    +  assertTrue(isNaN(goog.string.parseInt('FXX123')));
    +  assertEquals(15, goog.string.parseInt('15*3'));
    +  assertEquals(7, goog.string.parseInt('0x7'));
    +  assertEquals(1, goog.string.parseInt('1x7'));
    +
    +  // Strings have no special meaning
    +  assertTrue(isNaN(goog.string.parseInt('Infinity')));
    +  assertTrue(isNaN(goog.string.parseInt('NaN')));
    +
    +  // Test numbers and values
    +  assertEquals(3, goog.string.parseInt(3.3));
    +  assertEquals(-3, goog.string.parseInt(-3.3));
    +  assertEquals(0, goog.string.parseInt(-0));
    +  assertTrue(isNaN(goog.string.parseInt(Infinity)));
    +  assertTrue(isNaN(goog.string.parseInt(NaN)));
    +  assertTrue(isNaN(goog.string.parseInt(Number.POSITIVE_INFINITY)));
    +  assertTrue(isNaN(goog.string.parseInt(Number.NEGATIVE_INFINITY)));
    +
    +  // In Chrome (at least), parseInt(Number.MIN_VALUE) is 5 (5e-324) and
    +  // parseInt(Number.MAX_VALUE) is 1 (1.79...e+308) as they are converted
    +  // to strings.  We do not attempt to correct this behavior.
    +
    +  // Additional values for negatives.
    +  assertEquals(-3, goog.string.parseInt('-3'));
    +  assertEquals(-32, goog.string.parseInt('-32    '));
    +  assertEquals(-32, goog.string.parseInt(' -32 '));
    +  assertEquals(-3, goog.string.parseInt('-0x3'));
    +  assertEquals(-50, goog.string.parseInt('-0x32    '));
    +  assertEquals(-243, goog.string.parseInt('   -0xF3    '));
    +  assertTrue(isNaN(goog.string.parseInt(' - 0x32 ')));
    +}
    +
    +function testIsLowerCamelCase() {
    +  assertTrue(goog.string.isLowerCamelCase('foo'));
    +  assertTrue(goog.string.isLowerCamelCase('fooBar'));
    +  assertTrue(goog.string.isLowerCamelCase('fooBarBaz'));
    +  assertTrue(goog.string.isLowerCamelCase('innerHTML'));
    +
    +  assertFalse(goog.string.isLowerCamelCase(''));
    +  assertFalse(goog.string.isLowerCamelCase('a3a'));
    +  assertFalse(goog.string.isLowerCamelCase('goog.dom'));
    +  assertFalse(goog.string.isLowerCamelCase('Foo'));
    +  assertFalse(goog.string.isLowerCamelCase('FooBar'));
    +  assertFalse(goog.string.isLowerCamelCase('ABCBBD'));
    +}
    +
    +function testIsUpperCamelCase() {
    +  assertFalse(goog.string.isUpperCamelCase(''));
    +  assertFalse(goog.string.isUpperCamelCase('foo'));
    +  assertFalse(goog.string.isUpperCamelCase('fooBar'));
    +  assertFalse(goog.string.isUpperCamelCase('fooBarBaz'));
    +  assertFalse(goog.string.isUpperCamelCase('innerHTML'));
    +  assertFalse(goog.string.isUpperCamelCase('a3a'));
    +  assertFalse(goog.string.isUpperCamelCase('goog.dom'));
    +  assertFalse(goog.string.isUpperCamelCase('Boyz2Men'));
    +
    +  assertTrue(goog.string.isUpperCamelCase('ABCBBD'));
    +  assertTrue(goog.string.isUpperCamelCase('Foo'));
    +  assertTrue(goog.string.isUpperCamelCase('FooBar'));
    +  assertTrue(goog.string.isUpperCamelCase('FooBarBaz'));
    +}
    +
    +function testSplitLimit() {
    +  assertArrayEquals(['a*a*a*a'], goog.string.splitLimit('a*a*a*a', '*', -1));
    +  assertArrayEquals(['a*a*a*a'], goog.string.splitLimit('a*a*a*a', '*', 0));
    +  assertArrayEquals(['a', 'a*a*a'], goog.string.splitLimit('a*a*a*a', '*', 1));
    +  assertArrayEquals(['a', 'a', 'a*a'],
    +                    goog.string.splitLimit('a*a*a*a', '*', 2));
    +  assertArrayEquals(['a', 'a', 'a', 'a'],
    +                    goog.string.splitLimit('a*a*a*a', '*', 3));
    +  assertArrayEquals(['a', 'a', 'a', 'a'],
    +                    goog.string.splitLimit('a*a*a*a', '*', 4));
    +
    +  assertArrayEquals(['bbbbbbbbbbbb'],
    +                    goog.string.splitLimit('bbbbbbbbbbbb', 'a', 10));
    +  assertArrayEquals(['babab', 'bab', 'abb'],
    +                    goog.string.splitLimit('bababaababaaabb', 'aa', 10));
    +  assertArrayEquals(['babab', 'babaaabb'],
    +                    goog.string.splitLimit('bababaababaaabb', 'aa', 1));
    +  assertArrayEquals(
    +      ['b', 'a', 'b', 'a', 'b', 'a', 'a', 'b', 'a', 'b', 'aaabb'],
    +      goog.string.splitLimit('bababaababaaabb', '', 10));
    +}
    +
    +function testContains() {
    +  assertTrue(goog.string.contains('moot', 'moo'));
    +  assertFalse(goog.string.contains('moo', 'moot'));
    +  assertFalse(goog.string.contains('Moot', 'moo'));
    +  assertTrue(goog.string.contains('moo', 'moo'));
    +}
    +
    +function testCaseInsensitiveContains() {
    +  assertTrue(goog.string.caseInsensitiveContains('moot', 'moo'));
    +  assertFalse(goog.string.caseInsensitiveContains('moo', 'moot'));
    +  assertTrue(goog.string.caseInsensitiveContains('Moot', 'moo'));
    +  assertTrue(goog.string.caseInsensitiveContains('moo', 'moo'));
    +}
    +
    +function testEditDistance() {
    +  assertEquals('Empty string should match to length of other string', 4,
    +      goog.string.editDistance('goat', ''));
    +  assertEquals('Empty string should match to length of other string', 4,
    +      goog.string.editDistance('', 'moat'));
    +
    +  assertEquals('Equal strings should have zero edit distance', 0,
    +      goog.string.editDistance('abcd', 'abcd'));
    +  assertEquals('Equal strings should have zero edit distance', 0,
    +      goog.string.editDistance('', ''));
    +
    +  assertEquals('Edit distance for adding characters incorrect', 4,
    +      goog.string.editDistance('bdf', 'abcdefg'));
    +  assertEquals('Edit distance for removing characters incorrect', 4,
    +      goog.string.editDistance('abcdefg', 'bdf'));
    +
    +  assertEquals('Edit distance for substituting characters incorrect', 4,
    +      goog.string.editDistance('adef', 'ghij'));
    +  assertEquals('Edit distance for substituting characters incorrect', 1,
    +      goog.string.editDistance('goat', 'boat'));
    +
    +  assertEquals('Substitution should be preferred over insert/delete', 4,
    +      goog.string.editDistance('abcd', 'defg'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringbuffer.js b/mapbox-gl/closure-library/closure/goog/string/stringbuffer.js
    new file mode 100644
    index 0000000000..f4b3a871e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringbuffer.js
    @@ -0,0 +1,103 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility for fast string concatenation.
    + */
    +
    +goog.provide('goog.string.StringBuffer');
    +
    +
    +
    +/**
    + * Utility class to facilitate string concatenation.
    + *
    + * @param {*=} opt_a1 Optional first initial item to append.
    + * @param {...*} var_args Other initial items to
    + *     append, e.g., new goog.string.StringBuffer('foo', 'bar').
    + * @constructor
    + */
    +goog.string.StringBuffer = function(opt_a1, var_args) {
    +  if (opt_a1 != null) {
    +    this.append.apply(this, arguments);
    +  }
    +};
    +
    +
    +/**
    + * Internal buffer for the string to be concatenated.
    + * @type {string}
    + * @private
    + */
    +goog.string.StringBuffer.prototype.buffer_ = '';
    +
    +
    +/**
    + * Sets the contents of the string buffer object, replacing what's currently
    + * there.
    + *
    + * @param {*} s String to set.
    + */
    +goog.string.StringBuffer.prototype.set = function(s) {
    +  this.buffer_ = '' + s;
    +};
    +
    +
    +/**
    + * Appends one or more items to the buffer.
    + *
    + * Calling this with null, undefined, or empty arguments is an error.
    + *
    + * @param {*} a1 Required first string.
    + * @param {*=} opt_a2 Optional second string.
    + * @param {...*} var_args Other items to append,
    + *     e.g., sb.append('foo', 'bar', 'baz').
    + * @return {!goog.string.StringBuffer} This same StringBuffer object.
    + * @suppress {duplicate}
    + */
    +goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
    +  // Use a1 directly to avoid arguments instantiation for single-arg case.
    +  this.buffer_ += a1;
    +  if (opt_a2 != null) { // second argument is undefined (null == undefined)
    +    for (var i = 1; i < arguments.length; i++) {
    +      this.buffer_ += arguments[i];
    +    }
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Clears the internal buffer.
    + */
    +goog.string.StringBuffer.prototype.clear = function() {
    +  this.buffer_ = '';
    +};
    +
    +
    +/**
    + * @return {number} the length of the current contents of the buffer.
    + */
    +goog.string.StringBuffer.prototype.getLength = function() {
    +  return this.buffer_.length;
    +};
    +
    +
    +/**
    + * @return {string} The concatenated string.
    + * @override
    + */
    +goog.string.StringBuffer.prototype.toString = function() {
    +  return this.buffer_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.html b/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.html
    new file mode 100644
    index 0000000000..01ff24a75b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.string.StringBuffer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.string.StringBufferTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.js b/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.js
    new file mode 100644
    index 0000000000..73cb440a98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringbuffer_test.js
    @@ -0,0 +1,97 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.StringBufferTest');
    +goog.setTestOnly('goog.string.StringBufferTest');
    +
    +goog.require('goog.string.StringBuffer');
    +goog.require('goog.testing.jsunit');
    +
    +function testStringBuffer() {
    +  var sb = new goog.string.StringBuffer();
    +  sb.append('Four score');
    +  sb.append(' ');
    +  sb.append('and seven years ago.');
    +  assertEquals('Test 1', 'Four score and seven years ago.', sb.toString());
    +
    +  sb.clear();
    +  assertEquals('Test 2', '', sb.toString());
    +
    +  sb = new goog.string.StringBuffer('Four score ');
    +  sb.append('and seven years ago.');
    +  assertEquals('Test 3', 'Four score and seven years ago.', sb.toString());
    +
    +  // can pass in non-Strings?
    +  sb = new goog.string.StringBuffer(1);
    +  sb.append(2);
    +  assertEquals('Test 4', '12', sb.toString());
    +}
    +
    +
    +function testStringBufferSet() {
    +  var sb = new goog.string.StringBuffer('foo');
    +  sb.set('bar');
    +  assertEquals('Test 1', 'bar', sb.toString());
    +}
    +
    +
    +function testStringBufferMultiAppend() {
    +  var sb = new goog.string.StringBuffer('hey', 'foo');
    +  sb.append('bar', 'baz');
    +  assertEquals('Test 1', 'heyfoobarbaz', sb.toString());
    +
    +  sb = new goog.string.StringBuffer();
    +  sb.append(1, 2);
    +  // should not add up to '3'
    +  assertEquals('Test 2', '12', sb.toString());
    +}
    +
    +
    +function testStringBufferToString() {
    +  var sb = new goog.string.StringBuffer('foo', 'bar');
    +  assertEquals('Test 1', 'foobar', sb.toString());
    +}
    +
    +
    +function testStringBufferWithFalseFirstArgument() {
    +  var sb = new goog.string.StringBuffer(0, 'more');
    +  assertEquals('Test 1', '0more', sb.toString());
    +
    +  sb = new goog.string.StringBuffer(false, 'more');
    +  assertEquals('Test 2', 'falsemore', sb.toString());
    +
    +  sb = new goog.string.StringBuffer('', 'more');
    +  assertEquals('Test 3', 'more', sb.toString());
    +
    +  sb = new goog.string.StringBuffer(null, 'more');
    +  assertEquals('Test 4', '', sb.toString());
    +
    +  sb = new goog.string.StringBuffer(undefined, 'more');
    +  assertEquals('Test 5', '', sb.toString());
    +}
    +
    +
    +function testStringBufferGetLength() {
    +  var sb = new goog.string.StringBuffer();
    +  assertEquals(0, sb.getLength());
    +
    +  sb.append('foo');
    +  assertEquals(3, sb.getLength());
    +
    +  sb.append('baroo');
    +  assertEquals(8, sb.getLength());
    +
    +  sb.clear();
    +  assertEquals(0, sb.getLength());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringformat.js b/mapbox-gl/closure-library/closure/goog/string/stringformat.js
    new file mode 100644
    index 0000000000..37a5386978
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringformat.js
    @@ -0,0 +1,251 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of sprintf-like, python-%-operator-like,
    + * .NET-String.Format-like functionality. Uses JS string's replace method to
    + * extract format specifiers and sends those specifiers to a handler function,
    + * which then, based on conversion type part of the specifier, calls the
    + * appropriate function to handle the specific conversion.
    + * For specific functionality implemented, look at formatRe below, or look
    + * at the tests.
    + */
    +
    +goog.provide('goog.string.format');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * Performs sprintf-like conversion, ie. puts the values in a template.
    + * DO NOT use it instead of built-in conversions in simple cases such as
    + * 'Cost: %.2f' as it would introduce unneccessary latency oposed to
    + * 'Cost: ' + cost.toFixed(2).
    + * @param {string} formatString Template string containing % specifiers.
    + * @param {...string|number} var_args Values formatString is to be filled with.
    + * @return {string} Formatted string.
    + */
    +goog.string.format = function(formatString, var_args) {
    +
    +  // Convert the arguments to an array (MDC recommended way).
    +  var args = Array.prototype.slice.call(arguments);
    +
    +  // Try to get the template.
    +  var template = args.shift();
    +  if (typeof template == 'undefined') {
    +    throw Error('[goog.string.format] Template required');
    +  }
    +
    +  // This re is used for matching, it also defines what is supported.
    +  var formatRe = /%([0\-\ \+]*)(\d+)?(\.(\d+))?([%sfdiu])/g;
    +
    +  /**
    +   * Chooses which conversion function to call based on type conversion
    +   * specifier.
    +   * @param {string} match Contains the re matched string.
    +   * @param {string} flags Formatting flags.
    +   * @param {string} width Replacement string minimum width.
    +   * @param {string} dotp Matched precision including a dot.
    +   * @param {string} precision Specifies floating point precision.
    +   * @param {string} type Type conversion specifier.
    +   * @param {string} offset Matching location in the original string.
    +   * @param {string} wholeString Has the actualString being searched.
    +   * @return {string} Formatted parameter.
    +   */
    +  function replacerDemuxer(match,
    +                           flags,
    +                           width,
    +                           dotp,
    +                           precision,
    +                           type,
    +                           offset,
    +                           wholeString) {
    +
    +    // The % is too simple and doesn't take an argument.
    +    if (type == '%') {
    +      return '%';
    +    }
    +
    +    // Try to get the actual value from parent function.
    +    var value = args.shift();
    +
    +    // If we didn't get any arguments, fail.
    +    if (typeof value == 'undefined') {
    +      throw Error('[goog.string.format] Not enough arguments');
    +    }
    +
    +    // Patch the value argument to the beginning of our type specific call.
    +    arguments[0] = value;
    +
    +    return goog.string.format.demuxes_[type].apply(null, arguments);
    +
    +  }
    +
    +  return template.replace(formatRe, replacerDemuxer);
    +};
    +
    +
    +/**
    + * Contains various conversion functions (to be filled in later on).
    + * @type {Object}
    + * @private
    + */
    +goog.string.format.demuxes_ = {};
    +
    +
    +/**
    + * Processes %s conversion specifier.
    + * @param {string} value Contains the formatRe matched string.
    + * @param {string} flags Formatting flags.
    + * @param {string} width Replacement string minimum width.
    + * @param {string} dotp Matched precision including a dot.
    + * @param {string} precision Specifies floating point precision.
    + * @param {string} type Type conversion specifier.
    + * @param {string} offset Matching location in the original string.
    + * @param {string} wholeString Has the actualString being searched.
    + * @return {string} Replacement string.
    + */
    +goog.string.format.demuxes_['s'] = function(value,
    +                                            flags,
    +                                            width,
    +                                            dotp,
    +                                            precision,
    +                                            type,
    +                                            offset,
    +                                            wholeString) {
    +  var replacement = value;
    +  // If no padding is necessary we're done.
    +  // The check for '' is necessary because Firefox incorrectly provides the
    +  // empty string instead of undefined for non-participating capture groups,
    +  // and isNaN('') == false.
    +  if (isNaN(width) || width == '' || replacement.length >= width) {
    +    return replacement;
    +  }
    +
    +  // Otherwise we should find out where to put spaces.
    +  if (flags.indexOf('-', 0) > -1) {
    +    replacement =
    +        replacement + goog.string.repeat(' ', width - replacement.length);
    +  } else {
    +    replacement =
    +        goog.string.repeat(' ', width - replacement.length) + replacement;
    +  }
    +  return replacement;
    +};
    +
    +
    +/**
    + * Processes %f conversion specifier.
    + * @param {number} value Contains the formatRe matched string.
    + * @param {string} flags Formatting flags.
    + * @param {string} width Replacement string minimum width.
    + * @param {string} dotp Matched precision including a dot.
    + * @param {string} precision Specifies floating point precision.
    + * @param {string} type Type conversion specifier.
    + * @param {string} offset Matching location in the original string.
    + * @param {string} wholeString Has the actualString being searched.
    + * @return {string} Replacement string.
    + */
    +goog.string.format.demuxes_['f'] = function(value,
    +                                            flags,
    +                                            width,
    +                                            dotp,
    +                                            precision,
    +                                            type,
    +                                            offset,
    +                                            wholeString) {
    +
    +  var replacement = value.toString();
    +
    +  // The check for '' is necessary because Firefox incorrectly provides the
    +  // empty string instead of undefined for non-participating capture groups,
    +  // and isNaN('') == false.
    +  if (!(isNaN(precision) || precision == '')) {
    +    replacement = value.toFixed(precision);
    +  }
    +
    +  // Generates sign string that will be attached to the replacement.
    +  var sign;
    +  if (value < 0) {
    +    sign = '-';
    +  } else if (flags.indexOf('+') >= 0) {
    +    sign = '+';
    +  } else if (flags.indexOf(' ') >= 0) {
    +    sign = ' ';
    +  } else {
    +    sign = '';
    +  }
    +
    +  if (value >= 0) {
    +    replacement = sign + replacement;
    +  }
    +
    +  // If no padding is neccessary we're done.
    +  if (isNaN(width) || replacement.length >= width) {
    +    return replacement;
    +  }
    +
    +  // We need a clean signless replacement to start with
    +  replacement = isNaN(precision) ?
    +      Math.abs(value).toString() :
    +      Math.abs(value).toFixed(precision);
    +
    +  var padCount = width - replacement.length - sign.length;
    +
    +  // Find out which side to pad, and if it's left side, then which character to
    +  // pad, and set the sign on the left and padding in the middle.
    +  if (flags.indexOf('-', 0) >= 0) {
    +    replacement = sign + replacement + goog.string.repeat(' ', padCount);
    +  } else {
    +    // Decides which character to pad.
    +    var paddingChar = (flags.indexOf('0', 0) >= 0) ? '0' : ' ';
    +    replacement =
    +        sign + goog.string.repeat(paddingChar, padCount) + replacement;
    +  }
    +
    +  return replacement;
    +};
    +
    +
    +/**
    + * Processes %d conversion specifier.
    + * @param {string} value Contains the formatRe matched string.
    + * @param {string} flags Formatting flags.
    + * @param {string} width Replacement string minimum width.
    + * @param {string} dotp Matched precision including a dot.
    + * @param {string} precision Specifies floating point precision.
    + * @param {string} type Type conversion specifier.
    + * @param {string} offset Matching location in the original string.
    + * @param {string} wholeString Has the actualString being searched.
    + * @return {string} Replacement string.
    + */
    +goog.string.format.demuxes_['d'] = function(value,
    +                                            flags,
    +                                            width,
    +                                            dotp,
    +                                            precision,
    +                                            type,
    +                                            offset,
    +                                            wholeString) {
    +  return goog.string.format.demuxes_['f'](
    +      parseInt(value, 10) /* value */,
    +      flags, width, dotp, 0 /* precision */,
    +      type, offset, wholeString);
    +};
    +
    +
    +// These are additional aliases, for integer conversion.
    +goog.string.format.demuxes_['i'] = goog.string.format.demuxes_['d'];
    +goog.string.format.demuxes_['u'] = goog.string.format.demuxes_['d'];
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringformat_test.html b/mapbox-gl/closure-library/closure/goog/string/stringformat_test.html
    new file mode 100644
    index 0000000000..50ff9a62bc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringformat_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.string.format
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.string.formatTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringformat_test.js b/mapbox-gl/closure-library/closure/goog/string/stringformat_test.js
    new file mode 100644
    index 0000000000..5de8884d2f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringformat_test.js
    @@ -0,0 +1,218 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.formatTest');
    +goog.setTestOnly('goog.string.formatTest');
    +
    +goog.require('goog.string.format');
    +goog.require('goog.testing.jsunit');
    +
    +// The discussion on naming this functionality is going on.
    +var f = goog.string.format;
    +
    +function testImmediateFormatSpecifier() {
    +  assertEquals('Empty String', '', f(''));
    +  assertEquals('Immediate Value', 'Immediate Value', f('Immediate Value'));
    +}
    +
    +function testPercentSign() {
    +  assertEquals('%', '%', f('%'));
    +  assertEquals('%%', '%', f('%%'));
    +  assertEquals('%%%', '%%', f('%%%'));
    +  assertEquals('%%%%', '%%', f('%%%%'));
    +
    +  assertEquals('width of the percent sign ???', '%%', f('%345%%-67.987%'));
    +}
    +
    +function testStringConversionSpecifier() {
    +  assertEquals('%s', 'abc', f('%s', 'abc'));
    +  assertEquals('%2s', 'abc', f('%2s', 'abc'));
    +  assertEquals('%6s', '   abc', f('%6s', 'abc'));
    +  assertEquals('%-6s', 'abc   ', f('%-6s', 'abc'));
    +}
    +
    +function testFloatConversionSpecifier() {
    +  assertEquals('%f', '123', f('%f', 123));
    +  assertEquals('%f', '0.1', f('%f', 0.1));
    +  assertEquals('%f', '123.456', f('%f', 123.456));
    +
    +  // Precisions, paddings and other flags are handled on a flag to flag basis.
    +
    +}
    +
    +function testAliasedConversionSpecifiers() {
    +  assertEquals('%i vs. %d', f('%i', 123), f('%d', 123));
    +  assertEquals('%u vs. %d', f('%u', 123), f('%d', 123));
    +}
    +
    +function testIntegerConversion() {
    +  assertEquals('%d', '0', f('%d', 0));
    +
    +  assertEquals('%d', '123', f('%d', 123));
    +  assertEquals('%d', '0', f('%d', 0.1));
    +  assertEquals('%d', '0', f('%d', 0.9));
    +  assertEquals('%d', '123', f('%d', 123.456));
    +
    +  assertEquals('%d', '-1', f('%d', -1));
    +  assertEquals('%d', '0', f('%d', -0.1));
    +  assertEquals('%d', '0', f('%d', -0.9));
    +  assertEquals('%d', '-123', f('%d', -123.456));
    +
    +  // Precisions, paddings and other flags are handled on a flag to flag basis.
    +
    +}
    +
    +function testSpaceFlag() {
    +  assertEquals('zero %+d ', ' 0', f('% d', 0));
    +
    +  assertEquals('positive % d ', ' 123', f('% d', 123));
    +  assertEquals('negative % d ', '-123', f('% d', -123));
    +
    +  assertEquals('positive % 3d', ' 123', f('% 3d', 123));
    +  assertEquals('negative % 3d', '-123', f('% 3d', -123));
    +
    +  assertEquals('positive % 4d', ' 123', f('% 4d', 123));
    +  assertEquals('negative % 4d', '-123', f('% 4d', -123));
    +
    +  assertEquals('positive % 6d', '   123', f('% 6d', 123));
    +  assertEquals('negative % 6d', '-  123', f('% 6d', -123));
    +
    +  assertEquals('positive % f ', ' 123.456', f('% f', 123.456));
    +  assertEquals('negative % f ', '-123.456', f('% f', -123.456));
    +
    +  assertEquals('positive % .2f ', ' 123.46', f('% .2f', 123.456));
    +  assertEquals('negative % .2f ', '-123.46', f('% .2f', -123.456));
    +
    +  assertEquals('positive % 6.2f', ' 123.46', f('% 6.2f', 123.456));
    +  assertEquals('negative % 6.2f', '-123.46', f('% 6.2f', -123.456));
    +
    +  assertEquals('positive % 7.2f', ' 123.46', f('% 7.2f', 123.456));
    +  assertEquals('negative % 7.2f', '-123.46', f('% 7.2f', -123.456));
    +
    +  assertEquals('positive % 10.2f', '    123.46', f('% 10.2f', 123.456));
    +  assertEquals('negative % 10.2f', '-   123.46', f('% 10.2f', -123.456));
    +
    +  assertEquals('string % s ', 'abc', f('% s', 'abc'));
    +  assertEquals('string % 3s', 'abc', f('% 3s', 'abc'));
    +  assertEquals('string % 4s', ' abc', f('% 4s', 'abc'));
    +  assertEquals('string % 6s', '   abc', f('% 6s', 'abc'));
    +}
    +
    +function testPlusFlag() {
    +  assertEquals('zero %+d ', '+0', f('%+d', 0));
    +
    +  assertEquals('positive %+d ', '+123', f('%+d', 123));
    +  assertEquals('negative %+d ', '-123', f('%+d', -123));
    +
    +  assertEquals('positive %+3d', '+123', f('%+3d', 123));
    +  assertEquals('negative %+3d', '-123', f('%+3d', -123));
    +
    +  assertEquals('positive %+4d', '+123', f('%+4d', 123));
    +  assertEquals('negative %+4d', '-123', f('%+4d', -123));
    +
    +  assertEquals('positive %+6d', '+  123', f('%+6d', 123));
    +  assertEquals('negative %+6d', '-  123', f('%+6d', -123));
    +
    +  assertEquals('positive %+f ', '+123.456', f('%+f', 123.456));
    +  assertEquals('negative %+f ', '-123.456', f('%+f', -123.456));
    +
    +  assertEquals('positive %+.2f ', '+123.46', f('%+.2f', 123.456));
    +  assertEquals('negative %+.2f ', '-123.46', f('%+.2f', -123.456));
    +
    +  assertEquals('positive %+6.2f', '+123.46', f('%+6.2f', 123.456));
    +  assertEquals('negative %+6.2f', '-123.46', f('%+6.2f', -123.456));
    +
    +  assertEquals('positive %+7.2f', '+123.46', f('%+7.2f', 123.456));
    +  assertEquals('negative %+7.2f', '-123.46', f('%+7.2f', -123.456));
    +
    +  assertEquals('positive %+10.2f', '+   123.46', f('%+10.2f', 123.456));
    +  assertEquals('negative %+10.2f', '-   123.46', f('%+10.2f', -123.456));
    +
    +  assertEquals('string %+s ', 'abc', f('%+s', 'abc'));
    +  assertEquals('string %+3s', 'abc', f('%+3s', 'abc'));
    +  assertEquals('string %+4s', ' abc', f('%+4s', 'abc'));
    +  assertEquals('string %+6s', '   abc', f('%+6s', 'abc'));
    +}
    +
    +function testPrecision() {
    +  assertEquals('%.5d', '0', f('%.5d', 0));
    +
    +  assertEquals('%d', '123', f('%d', 123.456));
    +  assertEquals('%.2d', '123', f('%.2d', 123.456));
    +
    +  assertEquals('%f', '123.456', f('%f', 123.456));
    +  assertEquals('%.2f', '123.46', f('%.2f', 123.456));
    +
    +  assertEquals('%.3f', '123.456', f('%.3f', 123.456));
    +  assertEquals('%.6f', '123.456000', f('%.6f', 123.456));
    +  assertEquals('%1.2f', '123.46', f('%1.2f', 123.456));
    +  assertEquals('%7.2f', ' 123.46', f('%7.2f', 123.456));
    +
    +  assertEquals('%5.6f', '123.456000', f('%5.6f', 123.456));
    +  assertEquals('%11.6f', ' 123.456000', f('%11.6f', 123.456));
    +
    +  assertEquals('%07.2f', '0123.46', f('%07.2f', 123.456));
    +  assertEquals('%+7.2f', '+123.46', f('%+7.2f', 123.456));
    +}
    +
    +function testZeroFlag() {
    +  assertEquals('%0s', 'abc', f('%0s', 'abc'));
    +  assertEquals('%02s', 'abc', f('%02s', 'abc'));
    +  assertEquals('%06s', '   abc', f('%06s', 'abc'));
    +
    +  assertEquals('%0d', '123', f('%0d', 123));
    +  assertEquals('%0d', '-123', f('%0d', -123));
    +  assertEquals('%06d', '000123', f('%06d', 123));
    +  assertEquals('%06d', '-00123', f('%06d', -123));
    +  assertEquals('%010d', '0000000123', f('%010d', 123));
    +  assertEquals('%010d', '-000000123', f('%010d', -123));
    +}
    +
    +function testFlagMinus() {
    +  assertEquals('%-s', 'abc', f('%-s', 'abc'));
    +  assertEquals('%-2s', 'abc', f('%-2s', 'abc'));
    +  assertEquals('%-6s', 'abc   ', f('%-6s', 'abc'));
    +
    +  assertEquals('%-d', '123', f('%-d', 123));
    +  assertEquals('%-d', '-123', f('%-d', -123));
    +  assertEquals('%-2d', '123', f('%-2d', 123));
    +  assertEquals('%-2d', '-123', f('%-2d', -123));
    +  assertEquals('%-4d', '123 ', f('%-4d', 123));
    +  assertEquals('%-4d', '-123', f('%-4d', -123));
    +
    +  assertEquals('%-d', '123', f('%-0d', 123));
    +  assertEquals('%-d', '-123', f('%-0d', -123));
    +  assertEquals('%-4d', '123 ', f('%-04d', 123));
    +  assertEquals('%-4d', '-123', f('%-04d', -123));
    +}
    +
    +function testExceptions() {
    +  var e = assertThrows(goog.partial(f, '%f%f', 123.456));
    +  assertEquals('[goog.string.format] Not enough arguments', e.message);
    +
    +  e = assertThrows(f);
    +  assertEquals('[goog.string.format] Template required', e.message);
    +}
    +
    +function testNonParticipatingGroupHandling() {
    +  // Firefox supplies empty string instead of undefined for non-participating
    +  // capture groups. This can trigger bad behavior if a demuxer only checks
    +  // isNaN(val) and not also val == ''. Check for regressions.
    +  var format = '%s %d %i %u';
    +  var expected = '1 2 3 4';
    +  // Good types
    +  assertEquals(expected, goog.string.format(format, 1, '2', '3', '4'));
    +  // Bad types
    +  assertEquals(expected, goog.string.format(format, '1', 2, 3, 4));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/string/stringifier.js b/mapbox-gl/closure-library/closure/goog/string/stringifier.js
    new file mode 100644
    index 0000000000..8e66e92466
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/stringifier.js
    @@ -0,0 +1,38 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Defines an interface for serializing objects into strings.
    + */
    +
    +goog.provide('goog.string.Stringifier');
    +
    +
    +
    +/**
    + * An interface for serializing objects into strings.
    + * @interface
    + */
    +goog.string.Stringifier = function() {};
    +
    +
    +/**
    + * Serializes an object or a value to a string.
    + * Agnostic to the particular format of object and string.
    + *
    + * @param {*} object The object to stringify.
    + * @return {string} A string representation of the input.
    + */
    +goog.string.Stringifier.prototype.stringify;
    diff --git a/mapbox-gl/closure-library/closure/goog/string/typedstring.js b/mapbox-gl/closure-library/closure/goog/string/typedstring.js
    new file mode 100644
    index 0000000000..075115f1c8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/string/typedstring.js
    @@ -0,0 +1,48 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.string.TypedString');
    +
    +
    +
    +/**
    + * Wrapper for strings that conform to a data type or language.
    + *
    + * Implementations of this interface are wrappers for strings, and typically
    + * associate a type contract with the wrapped string.  Concrete implementations
    + * of this interface may choose to implement additional run-time type checking,
    + * see for example {@code goog.html.SafeHtml}. If available, client code that
    + * needs to ensure type membership of an object should use the type's function
    + * to assert type membership, such as {@code goog.html.SafeHtml.unwrap}.
    + * @interface
    + */
    +goog.string.TypedString = function() {};
    +
    +
    +/**
    + * Interface marker of the TypedString interface.
    + *
    + * This property can be used to determine at runtime whether or not an object
    + * implements this interface.  All implementations of this interface set this
    + * property to {@code true}.
    + * @type {boolean}
    + */
    +goog.string.TypedString.prototype.implementsGoogStringTypedString;
    +
    +
    +/**
    + * Retrieves this wrapped string's value.
    + * @return {!string} The wrapped string's value.
    + */
    +goog.string.TypedString.prototype.getTypedStringValue;
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/avltree.js b/mapbox-gl/closure-library/closure/goog/structs/avltree.js
    new file mode 100644
    index 0000000000..d45db1e9c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/avltree.js
    @@ -0,0 +1,899 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: AvlTree.
    + *
    + *
    + * This file provides the implementation of an AVL-Tree datastructure. The tree
    + * maintains a set of unique values in a sorted order. The values can be
    + * accessed efficiently in their sorted order since the tree enforces an O(logn)
    + * maximum height. See http://en.wikipedia.org/wiki/Avl_tree for more detail.
    + *
    + * The big-O notation for all operations are below:
    + * <pre>
    + *   Method                 big-O
    + * ----------------------------------------------------------------------------
    + * - add                    O(logn)
    + * - remove                 O(logn)
    + * - clear                  O(1)
    + * - contains               O(logn)
    + * - indexOf                O(logn)
    + * - getCount               O(1)
    + * - getMinimum             O(1), or O(logn) when optional root is specified
    + * - getMaximum             O(1), or O(logn) when optional root is specified
    + * - getHeight              O(1)
    + * - getValues              O(n)
    + * - inOrderTraverse        O(logn + k), where k is number of traversed nodes
    + * - reverseOrderTraverse   O(logn + k), where k is number of traversed nodes
    + * </pre>
    + */
    +
    +
    +goog.provide('goog.structs.AvlTree');
    +goog.provide('goog.structs.AvlTree.Node');
    +
    +goog.require('goog.structs.Collection');
    +
    +
    +
    +/**
    + * Constructs an AVL-Tree, which uses the specified comparator to order its
    + * values. The values can be accessed efficiently in their sorted order since
    + * the tree enforces a O(logn) maximum height.
    + *
    + * @param {Function=} opt_comparator Function used to order the tree's nodes.
    + * @constructor
    + * @implements {goog.structs.Collection<T>}
    + * @final
    + * @template T
    + */
    +goog.structs.AvlTree = function(opt_comparator) {
    +  this.comparator_ = opt_comparator ||
    +                     goog.structs.AvlTree.DEFAULT_COMPARATOR_;
    +};
    +
    +
    +/**
    + * String comparison function used to compare values in the tree. This function
    + * is used by default if no comparator is specified in the tree's constructor.
    + *
    + * @param {T} a The first value.
    + * @param {T} b The second value.
    + * @return {number} -1 if a < b, 1 if a > b, 0 if a = b.
    + * @template T
    + * @private
    + */
    +goog.structs.AvlTree.DEFAULT_COMPARATOR_ = function(a, b) {
    +  if (String(a) < String(b)) {
    +    return -1;
    +  } else if (String(a) > String(b)) {
    +    return 1;
    +  }
    +  return 0;
    +};
    +
    +
    +/**
    + * Pointer to the root node of the tree.
    + *
    + * @private {goog.structs.AvlTree.Node<T>}
    + */
    +goog.structs.AvlTree.prototype.root_ = null;
    +
    +
    +/**
    + * Comparison function used to compare values in the tree. This function should
    + * take two values, a and b, and return x where:
    + * <pre>
    + *  x < 0 if a < b,
    + *  x > 0 if a > b,
    + *  x = 0 otherwise
    + * </pre>
    + *
    + * @type {Function}
    + * @private
    + */
    +goog.structs.AvlTree.prototype.comparator_ = null;
    +
    +
    +/**
    + * Pointer to the node with the smallest value in the tree.
    + *
    + * @type {goog.structs.AvlTree.Node<T>}
    + * @private
    + */
    +goog.structs.AvlTree.prototype.minNode_ = null;
    +
    +
    +/**
    + * Pointer to the node with the largest value in the tree.
    + *
    + * @type {goog.structs.AvlTree.Node<T>}
    + * @private
    + */
    +goog.structs.AvlTree.prototype.maxNode_ = null;
    +
    +
    +/**
    + * Inserts a node into the tree with the specified value if the tree does
    + * not already contain a node with the specified value. If the value is
    + * inserted, the tree is balanced to enforce the AVL-Tree height property.
    + *
    + * @param {T} value Value to insert into the tree.
    + * @return {boolean} Whether value was inserted into the tree.
    + * @override
    + */
    +goog.structs.AvlTree.prototype.add = function(value) {
    +  // If the tree is empty, create a root node with the specified value
    +  if (this.root_ == null) {
    +    this.root_ = new goog.structs.AvlTree.Node(value);
    +    this.minNode_ = this.root_;
    +    this.maxNode_ = this.root_;
    +    return true;
    +  }
    +
    +  // This will be set to the new node if a new node is added.
    +  var newNode = null;
    +
    +  // Depth traverse the tree and insert the value if we reach a null node
    +  this.traverse_(function(node) {
    +    var retNode = null;
    +    var comparison = this.comparator_(node.value, value);
    +    if (comparison > 0) {
    +      retNode = node.left;
    +      if (node.left == null) {
    +        newNode = new goog.structs.AvlTree.Node(value, node);
    +        node.left = newNode;
    +        if (node == this.minNode_) {
    +          this.minNode_ = newNode;
    +        }
    +      }
    +    } else if (comparison < 0) {
    +      retNode = node.right;
    +      if (node.right == null) {
    +        newNode = new goog.structs.AvlTree.Node(value, node);
    +        node.right = newNode;
    +        if (node == this.maxNode_) {
    +          this.maxNode_ = newNode;
    +        }
    +      }
    +    }
    +    return retNode; // If null, we'll stop traversing the tree
    +  });
    +
    +  // If a node was added, increment counts and balance tree.
    +  if (newNode) {
    +    this.traverse_(
    +        function(node) {
    +          node.count++;
    +          return node.parent;
    +        },
    +        newNode.parent);
    +    this.balance_(newNode.parent); // Maintain the AVL-tree balance
    +  }
    +
    +  // Return true if a node was added, false otherwise
    +  return !!newNode;
    +};
    +
    +
    +/**
    + * Removes a node from the tree with the specified value if the tree contains a
    + * node with this value. If a node is removed the tree is balanced to enforce
    + * the AVL-Tree height property. The value of the removed node is returned.
    + *
    + * @param {T} value Value to find and remove from the tree.
    + * @return {T} The value of the removed node or null if the value was not in
    + *     the tree.
    + * @override
    + */
    +goog.structs.AvlTree.prototype.remove = function(value) {
    +  // Assume the value is not removed and set the value when it is removed
    +  var retValue = null;
    +
    +  // Depth traverse the tree and remove the value if we find it
    +  this.traverse_(function(node) {
    +    var retNode = null;
    +    var comparison = this.comparator_(node.value, value);
    +    if (comparison > 0) {
    +      retNode = node.left;
    +    } else if (comparison < 0) {
    +      retNode = node.right;
    +    } else {
    +      retValue = node.value;
    +      this.removeNode_(node);
    +    }
    +    return retNode; // If null, we'll stop traversing the tree
    +  });
    +
    +  // Return the value that was removed, null if the value was not in the tree
    +  return retValue;
    +};
    +
    +
    +/**
    + * Removes all nodes from the tree.
    + */
    +goog.structs.AvlTree.prototype.clear = function() {
    +  this.root_ = null;
    +  this.minNode_ = null;
    +  this.maxNode_ = null;
    +};
    +
    +
    +/**
    + * Returns true if the tree contains a node with the specified value, false
    + * otherwise.
    + *
    + * @param {T} value Value to find in the tree.
    + * @return {boolean} Whether the tree contains a node with the specified value.
    + * @override
    + */
    +goog.structs.AvlTree.prototype.contains = function(value) {
    +  // Assume the value is not in the tree and set this value if it is found
    +  var isContained = false;
    +
    +  // Depth traverse the tree and set isContained if we find the node
    +  this.traverse_(function(node) {
    +    var retNode = null;
    +    var comparison = this.comparator_(node.value, value);
    +    if (comparison > 0) {
    +      retNode = node.left;
    +    } else if (comparison < 0) {
    +      retNode = node.right;
    +    } else {
    +      isContained = true;
    +    }
    +    return retNode; // If null, we'll stop traversing the tree
    +  });
    +
    +  // Return true if the value is contained in the tree, false otherwise
    +  return isContained;
    +};
    +
    +
    +/**
    + * Returns the index (in an in-order traversal) of the node in the tree with
    + * the specified value. For example, the minimum value in the tree will
    + * return an index of 0 and the maximum will return an index of n - 1 (where
    + * n is the number of nodes in the tree).  If the value is not found then -1
    + * is returned.
    + *
    + * @param {T} value Value in the tree whose in-order index is returned.
    + * @return {!number} The in-order index of the given value in the
    + *     tree or -1 if the value is not found.
    + */
    +goog.structs.AvlTree.prototype.indexOf = function(value) {
    +  // Assume the value is not in the tree and set this value if it is found
    +  var retIndex = -1;
    +  var currIndex = 0;
    +
    +  // Depth traverse the tree and set retIndex if we find the node
    +  this.traverse_(function(node) {
    +    var comparison = this.comparator_(node.value, value);
    +    if (comparison > 0) {
    +      // The value is less than this node, so recurse into the left subtree.
    +      return node.left;
    +    }
    +
    +    if (node.left) {
    +      // The value is greater than all of the nodes in the left subtree.
    +      currIndex += node.left.count;
    +    }
    +
    +    if (comparison < 0) {
    +      // The value is also greater than this node.
    +      currIndex++;
    +      // Recurse into the right subtree.
    +      return node.right;
    +    }
    +    // We found the node, so stop traversing the tree.
    +    retIndex = currIndex;
    +    return null;
    +  });
    +
    +  // Return index if the value is contained in the tree, -1 otherwise
    +  return retIndex;
    +};
    +
    +
    +/**
    + * Returns the number of values stored in the tree.
    + *
    + * @return {number} The number of values stored in the tree.
    + * @override
    + */
    +goog.structs.AvlTree.prototype.getCount = function() {
    +  return this.root_ ? this.root_.count : 0;
    +};
    +
    +
    +/**
    + * Returns a k-th smallest value, based on the comparator, where 0 <= k <
    + * this.getCount().
    + * @param {number} k The number k.
    + * @return {T} The k-th smallest value.
    + */
    +goog.structs.AvlTree.prototype.getKthValue = function(k) {
    +  if (k < 0 || k >= this.getCount()) {
    +    return null;
    +  }
    +  return this.getKthNode_(k).value;
    +};
    +
    +
    +/**
    + * Returns the value u, such that u is contained in the tree and u < v, for all
    + * values v in the tree where v != u.
    + *
    + * @return {T} The minimum value contained in the tree.
    + */
    +goog.structs.AvlTree.prototype.getMinimum = function() {
    +  return this.getMinNode_().value;
    +};
    +
    +
    +/**
    + * Returns the value u, such that u is contained in the tree and u > v, for all
    + * values v in the tree where v != u.
    + *
    + * @return {T} The maximum value contained in the tree.
    + */
    +goog.structs.AvlTree.prototype.getMaximum = function() {
    +  return this.getMaxNode_().value;
    +};
    +
    +
    +/**
    + * Returns the height of the tree (the maximum depth). This height should
    + * always be <= 1.4405*(Math.log(n+2)/Math.log(2))-1.3277, where n is the
    + * number of nodes in the tree.
    + *
    + * @return {number} The height of the tree.
    + */
    +goog.structs.AvlTree.prototype.getHeight = function() {
    +  return this.root_ ? this.root_.height : 0;
    +};
    +
    +
    +/**
    + * Inserts the values stored in the tree into a new Array and returns the Array.
    + *
    + * @return {!Array<T>} An array containing all of the trees values in sorted
    + *     order.
    + */
    +goog.structs.AvlTree.prototype.getValues = function() {
    +  var ret = [];
    +  this.inOrderTraverse(function(value) {
    +    ret.push(value);
    +  });
    +  return ret;
    +};
    +
    +
    +/**
    + * Performs an in-order traversal of the tree and calls {@code func} with each
    + * traversed node, optionally starting from the smallest node with a value >= to
    + * the specified start value. The traversal ends after traversing the tree's
    + * maximum node or when {@code func} returns a value that evaluates to true.
    + *
    + * @param {Function} func Function to call on each traversed node.
    + * @param {Object=} opt_startValue If specified, traversal will begin on the
    + *    node with the smallest value >= opt_startValue.
    + */
    +goog.structs.AvlTree.prototype.inOrderTraverse =
    +    function(func, opt_startValue) {
    +  // If our tree is empty, return immediately
    +  if (!this.root_) {
    +    return;
    +  }
    +
    +  // Depth traverse the tree to find node to begin in-order traversal from
    +  var startNode;
    +  if (opt_startValue) {
    +    this.traverse_(function(node) {
    +      var retNode = null;
    +      var comparison = this.comparator_(node.value, opt_startValue);
    +      if (comparison > 0) {
    +        retNode = node.left;
    +        startNode = node;
    +      } else if (comparison < 0) {
    +        retNode = node.right;
    +      } else {
    +        startNode = node;
    +      }
    +      return retNode; // If null, we'll stop traversing the tree
    +    });
    +    if (!startNode) {
    +      return;
    +    }
    +  } else {
    +    startNode = this.getMinNode_();
    +  }
    +
    +  // Traverse the tree and call func on each traversed node's value
    +  var node = startNode, prev = startNode.left ? startNode.left : startNode;
    +  while (node != null) {
    +    if (node.left != null && node.left != prev && node.right != prev) {
    +      node = node.left;
    +    } else {
    +      if (node.right != prev) {
    +        if (func(node.value)) {
    +          return;
    +        }
    +      }
    +      var temp = node;
    +      node = node.right != null && node.right != prev ?
    +             node.right :
    +             node.parent;
    +      prev = temp;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Performs a reverse-order traversal of the tree and calls {@code func} with
    + * each traversed node, optionally starting from the largest node with a value
    + * <= to the specified start value. The traversal ends after traversing the
    + * tree's minimum node or when func returns a value that evaluates to true.
    + *
    + * @param {function(T):?} func Function to call on each traversed node.
    + * @param {Object=} opt_startValue If specified, traversal will begin on the
    + *    node with the largest value <= opt_startValue.
    + */
    +goog.structs.AvlTree.prototype.reverseOrderTraverse =
    +    function(func, opt_startValue) {
    +  // If our tree is empty, return immediately
    +  if (!this.root_) {
    +    return;
    +  }
    +
    +  // Depth traverse the tree to find node to begin reverse-order traversal from
    +  var startNode;
    +  if (opt_startValue) {
    +    this.traverse_(goog.bind(function(node) {
    +      var retNode = null;
    +      var comparison = this.comparator_(node.value, opt_startValue);
    +      if (comparison > 0) {
    +        retNode = node.left;
    +      } else if (comparison < 0) {
    +        retNode = node.right;
    +        startNode = node;
    +      } else {
    +        startNode = node;
    +      }
    +      return retNode; // If null, we'll stop traversing the tree
    +    }, this));
    +    if (!startNode) {
    +      return;
    +    }
    +  } else {
    +    startNode = this.getMaxNode_();
    +  }
    +
    +  // Traverse the tree and call func on each traversed node's value
    +  var node = startNode, prev = startNode.right ? startNode.right : startNode;
    +  while (node != null) {
    +    if (node.right != null && node.right != prev && node.left != prev) {
    +      node = node.right;
    +    } else {
    +      if (node.left != prev) {
    +        if (func(node.value)) {
    +          return;
    +        }
    +      }
    +      var temp = node;
    +      node = node.left != null && node.left != prev ?
    +             node.left :
    +             node.parent;
    +      prev = temp;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Performs a traversal defined by the supplied {@code traversalFunc}. The first
    + * call to {@code traversalFunc} is passed the root or the optionally specified
    + * startNode. After that, calls {@code traversalFunc} with the node returned
    + * by the previous call to {@code traversalFunc} until {@code traversalFunc}
    + * returns null or the optionally specified endNode. The first call to
    + * traversalFunc is passed the root or the optionally specified startNode.
    + *
    + * @param {function(
    + *     this:goog.structs.AvlTree<T>,
    + *     !goog.structs.AvlTree.Node):?goog.structs.AvlTree.Node} traversalFunc
    + * Function used to traverse the tree.
    + * @param {goog.structs.AvlTree.Node<T>=} opt_startNode The node at which the
    + *     traversal begins.
    + * @param {goog.structs.AvlTree.Node<T>=} opt_endNode The node at which the
    + *     traversal ends.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.traverse_ =
    +    function(traversalFunc, opt_startNode, opt_endNode) {
    +  var node = opt_startNode ? opt_startNode : this.root_;
    +  var endNode = opt_endNode ? opt_endNode : null;
    +  while (node && node != endNode) {
    +    node = traversalFunc.call(this, node);
    +  }
    +};
    +
    +
    +/**
    + * Ensures that the specified node and all its ancestors are balanced. If they
    + * are not, performs left and right tree rotations to achieve a balanced
    + * tree. This method assumes that at most 2 rotations are necessary to balance
    + * the tree (which is true for AVL-trees that are balanced after each node is
    + * added or removed).
    + *
    + * @param {goog.structs.AvlTree.Node<T>} node Node to begin balance from.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.balance_ = function(node) {
    +
    +  this.traverse_(function(node) {
    +    // Calculate the left and right node's heights
    +    var lh = node.left ? node.left.height : 0;
    +    var rh = node.right ? node.right.height : 0;
    +
    +    // Rotate tree rooted at this node if it is not AVL-tree balanced
    +    if (lh - rh > 1) {
    +      if (node.left.right && (!node.left.left ||
    +          node.left.left.height < node.left.right.height)) {
    +        this.leftRotate_(node.left);
    +      }
    +      this.rightRotate_(node);
    +    } else if (rh - lh > 1) {
    +      if (node.right.left && (!node.right.right ||
    +          node.right.right.height < node.right.left.height)) {
    +        this.rightRotate_(node.right);
    +      }
    +      this.leftRotate_(node);
    +    }
    +
    +    // Recalculate the left and right node's heights
    +    lh = node.left ? node.left.height : 0;
    +    rh = node.right ? node.right.height : 0;
    +
    +    // Set this node's height
    +    node.height = Math.max(lh, rh) + 1;
    +
    +    // Traverse up tree and balance parent
    +    return node.parent;
    +  }, node);
    +
    +};
    +
    +
    +/**
    + * Performs a left tree rotation on the specified node.
    + *
    + * @param {goog.structs.AvlTree.Node<T>} node Pivot node to rotate from.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.leftRotate_ = function(node) {
    +  // Re-assign parent-child references for the parent of the node being removed
    +  if (node.isLeftChild()) {
    +    node.parent.left = node.right;
    +    node.right.parent = node.parent;
    +  } else if (node.isRightChild()) {
    +    node.parent.right = node.right;
    +    node.right.parent = node.parent;
    +  } else {
    +    this.root_ = node.right;
    +    this.root_.parent = null;
    +  }
    +
    +  // Re-assign parent-child references for the child of the node being removed
    +  var temp = node.right;
    +  node.right = node.right.left;
    +  if (node.right != null) node.right.parent = node;
    +  temp.left = node;
    +  node.parent = temp;
    +
    +  // Update counts.
    +  temp.count = node.count;
    +  node.count -= (temp.right ? temp.right.count : 0) + 1;
    +};
    +
    +
    +/**
    + * Performs a right tree rotation on the specified node.
    + *
    + * @param {goog.structs.AvlTree.Node<T>} node Pivot node to rotate from.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.rightRotate_ = function(node) {
    +  // Re-assign parent-child references for the parent of the node being removed
    +  if (node.isLeftChild()) {
    +    node.parent.left = node.left;
    +    node.left.parent = node.parent;
    +  } else if (node.isRightChild()) {
    +    node.parent.right = node.left;
    +    node.left.parent = node.parent;
    +  } else {
    +    this.root_ = node.left;
    +    this.root_.parent = null;
    +  }
    +
    +  // Re-assign parent-child references for the child of the node being removed
    +  var temp = node.left;
    +  node.left = node.left.right;
    +  if (node.left != null) node.left.parent = node;
    +  temp.right = node;
    +  node.parent = temp;
    +
    +  // Update counts.
    +  temp.count = node.count;
    +  node.count -= (temp.left ? temp.left.count : 0) + 1;
    +};
    +
    +
    +/**
    + * Removes the specified node from the tree and ensures the tree still
    + * maintains the AVL-tree balance.
    + *
    + * @param {goog.structs.AvlTree.Node<T>} node The node to be removed.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.removeNode_ = function(node) {
    +  // Perform normal binary tree node removal, but balance the tree, starting
    +  // from where we removed the node
    +  if (node.left != null || node.right != null) {
    +    var b = null; // Node to begin balance from
    +    var r;        // Node to replace the node being removed
    +    if (node.left != null) {
    +      r = this.getMaxNode_(node.left);
    +
    +      // Update counts.
    +      this.traverse_(function(node) {
    +        node.count--;
    +        return node.parent;
    +      }, r);
    +
    +      if (r != node.left) {
    +        r.parent.right = r.left;
    +        if (r.left) r.left.parent = r.parent;
    +        r.left = node.left;
    +        r.left.parent = r;
    +        b = r.parent;
    +      }
    +      r.parent = node.parent;
    +      r.right = node.right;
    +      if (r.right) r.right.parent = r;
    +      if (node == this.maxNode_) this.maxNode_ = r;
    +      r.count = node.count;
    +    } else {
    +      r = this.getMinNode_(node.right);
    +
    +      // Update counts.
    +      this.traverse_(function(node) {
    +        node.count--;
    +        return node.parent;
    +      }, r);
    +
    +      if (r != node.right) {
    +        r.parent.left = r.right;
    +        if (r.right) r.right.parent = r.parent;
    +        r.right = node.right;
    +        r.right.parent = r;
    +        b = r.parent;
    +      }
    +      r.parent = node.parent;
    +      r.left = node.left;
    +      if (r.left) r.left.parent = r;
    +      if (node == this.minNode_) this.minNode_ = r;
    +      r.count = node.count;
    +    }
    +
    +    // Update the parent of the node being removed to point to its replace
    +    if (node.isLeftChild()) {
    +      node.parent.left = r;
    +    } else if (node.isRightChild()) {
    +      node.parent.right = r;
    +    } else {
    +      this.root_ = r;
    +    }
    +
    +    // Balance the tree
    +    this.balance_(b ? b : r);
    +  } else {
    +    // Update counts.
    +    this.traverse_(function(node) {
    +      node.count--;
    +      return node.parent;
    +    }, node.parent);
    +
    +    // If the node is a leaf, remove it and balance starting from its parent
    +    if (node.isLeftChild()) {
    +      this.special = 1;
    +      node.parent.left = null;
    +      if (node == this.minNode_) this.minNode_ = node.parent;
    +      this.balance_(node.parent);
    +    } else if (node.isRightChild()) {
    +      node.parent.right = null;
    +      if (node == this.maxNode_) this.maxNode_ = node.parent;
    +      this.balance_(node.parent);
    +    } else {
    +      this.clear();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the node in the tree that has k nodes before it in an in-order
    + * traversal, optionally rooted at {@code opt_rootNode}.
    + *
    + * @param {number} k The number of nodes before the node to be returned in an
    + *     in-order traversal, where 0 <= k < root.count.
    + * @param {goog.structs.AvlTree.Node<T>=} opt_rootNode Optional root node.
    + * @return {goog.structs.AvlTree.Node<T>} The node at the specified index.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.getKthNode_ = function(k, opt_rootNode) {
    +  var root = opt_rootNode || this.root_;
    +  var numNodesInLeftSubtree = root.left ? root.left.count : 0;
    +
    +  if (k < numNodesInLeftSubtree) {
    +    return this.getKthNode_(k, root.left);
    +  } else if (k == numNodesInLeftSubtree) {
    +    return root;
    +  } else {
    +    return this.getKthNode_(k - numNodesInLeftSubtree - 1, root.right);
    +  }
    +};
    +
    +
    +/**
    + * Returns the node with the smallest value in tree, optionally rooted at
    + * {@code opt_rootNode}.
    + *
    + * @param {goog.structs.AvlTree.Node<T>=} opt_rootNode Optional root node.
    + * @return {goog.structs.AvlTree.Node<T>} The node with the smallest value in
    + *     the tree.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.getMinNode_ = function(opt_rootNode) {
    +  if (!opt_rootNode) {
    +    return this.minNode_;
    +  }
    +
    +  var minNode = opt_rootNode;
    +  this.traverse_(function(node) {
    +    var retNode = null;
    +    if (node.left) {
    +      minNode = node.left;
    +      retNode = node.left;
    +    }
    +    return retNode; // If null, we'll stop traversing the tree
    +  }, opt_rootNode);
    +
    +  return minNode;
    +};
    +
    +
    +/**
    + * Returns the node with the largest value in tree, optionally rooted at
    + * opt_rootNode.
    + *
    + * @param {goog.structs.AvlTree.Node<T>=} opt_rootNode Optional root node.
    + * @return {goog.structs.AvlTree.Node<T>} The node with the largest value in
    + *     the tree.
    + * @private
    + */
    +goog.structs.AvlTree.prototype.getMaxNode_ = function(opt_rootNode) {
    +  if (!opt_rootNode) {
    +    return this.maxNode_;
    +  }
    +
    +  var maxNode = opt_rootNode;
    +  this.traverse_(function(node) {
    +    var retNode = null;
    +    if (node.right) {
    +      maxNode = node.right;
    +      retNode = node.right;
    +    }
    +    return retNode; // If null, we'll stop traversing the tree
    +  }, opt_rootNode);
    +
    +  return maxNode;
    +};
    +
    +
    +
    +/**
    + * Constructs an AVL-Tree node with the specified value. If no parent is
    + * specified, the node's parent is assumed to be null. The node's height
    + * defaults to 1 and its children default to null.
    + *
    + * @param {T} value Value to store in the node.
    + * @param {goog.structs.AvlTree.Node<T>=} opt_parent Optional parent node.
    + * @constructor
    + * @final
    + * @template T
    + */
    +goog.structs.AvlTree.Node = function(value, opt_parent) {
    +  /**
    +   * The value stored by the node.
    +   *
    +   * @type {T}
    +   */
    +  this.value = value;
    +
    +  /**
    +   * The node's parent. Null if the node is the root.
    +   *
    +   * @type {goog.structs.AvlTree.Node<T>}
    +   */
    +  this.parent = opt_parent ? opt_parent : null;
    +
    +  /**
    +   * The number of nodes in the subtree rooted at this node.
    +   *
    +   * @type {number}
    +   */
    +  this.count = 1;
    +};
    +
    +
    +/**
    + * The node's left child. Null if the node does not have a left child.
    + *
    + * @type {?goog.structs.AvlTree.Node<T>}
    + */
    +goog.structs.AvlTree.Node.prototype.left = null;
    +
    +
    +/**
    + * The node's right child. Null if the node does not have a right child.
    + *
    + * @type {?goog.structs.AvlTree.Node<T>}
    + */
    +goog.structs.AvlTree.Node.prototype.right = null;
    +
    +
    +/**
    + * The height of the tree rooted at this node.
    + *
    + * @type {number}
    + */
    +goog.structs.AvlTree.Node.prototype.height = 1;
    +
    +
    +/**
    + * Returns true iff the specified node has a parent and is the right child of
    + * its parent.
    + *
    + * @return {boolean} Whether the specified node has a parent and is the right
    + *    child of its parent.
    + */
    +goog.structs.AvlTree.Node.prototype.isRightChild = function() {
    +  return !!this.parent && this.parent.right == this;
    +};
    +
    +
    +/**
    + * Returns true iff the specified node has a parent and is the left child of
    + * its parent.
    + *
    + * @return {boolean} Whether the specified node has a parent and is the left
    + *    child of its parent.
    + */
    +goog.structs.AvlTree.Node.prototype.isLeftChild = function() {
    +  return !!this.parent && this.parent.left == this;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/avltree_test.html b/mapbox-gl/closure-library/closure/goog/structs/avltree_test.html
    new file mode 100644
    index 0000000000..aff7dd2f6c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/avltree_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.AvlTree
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.structs.AvlTreeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/avltree_test.js b/mapbox-gl/closure-library/closure/goog/structs/avltree_test.js
    new file mode 100644
    index 0000000000..a24972e094
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/avltree_test.js
    @@ -0,0 +1,363 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.AvlTreeTest');
    +goog.setTestOnly('goog.structs.AvlTreeTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.structs.AvlTree');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * This test verifies that we can insert strings into the AvlTree and have
    + * them be stored and sorted correctly by the default comparator.
    + */
    +function testInsertsWithDefaultComparator() {
    +  var tree = new goog.structs.AvlTree();
    +  var values = ['bill', 'blake', 'elliot', 'jacob', 'john', 'myles', 'ted'];
    +
    +  // Insert strings into tree out of order
    +  tree.add(values[4]);
    +  tree.add(values[3]);
    +  tree.add(values[0]);
    +  tree.add(values[6]);
    +  tree.add(values[5]);
    +  tree.add(values[1]);
    +  tree.add(values[2]);
    +
    +  // Verify strings are stored in sorted order
    +  var i = 0;
    +  tree.inOrderTraverse(function(value) {
    +    assertEquals(values[i], value);
    +    i += 1;
    +  });
    +  assertEquals(i, values.length);
    +
    +  // Verify that no nodes are visited if the start value is larger than all
    +  // values
    +  tree.inOrderTraverse(function(value) {
    +    fail();
    +  }, 'zed');
    +
    +  // Verify strings are stored in sorted order
    +  i = values.length;
    +  tree.reverseOrderTraverse(function(value) {
    +    i--;
    +    assertEquals(values[i], value);
    +  });
    +  assertEquals(i, 0);
    +
    +  // Verify that no nodes are visited if the start value is smaller than all
    +  // values
    +  tree.reverseOrderTraverse(function(value) {
    +    fail();
    +  }, 'aardvark');
    +}
    +
    +
    +/**
    + * This test verifies that we can insert strings into and remove strings from
    + * the AvlTree and have the only the non-removed values be stored and sorted
    + * correctly by the default comparator.
    + */
    +function testRemovesWithDefaultComparator() {
    +  var tree = new goog.structs.AvlTree();
    +  var values = ['bill', 'blake', 'elliot', 'jacob', 'john', 'myles', 'ted'];
    +
    +  // Insert strings into tree out of order
    +  tree.add('frodo');
    +  tree.add(values[4]);
    +  tree.add(values[3]);
    +  tree.add(values[0]);
    +  tree.add(values[6]);
    +  tree.add('samwise');
    +  tree.add(values[5]);
    +  tree.add(values[1]);
    +  tree.add(values[2]);
    +  tree.add('pippin');
    +
    +  // Remove strings from tree
    +  assertEquals(tree.remove('samwise'), 'samwise');
    +  assertEquals(tree.remove('pippin'), 'pippin');
    +  assertEquals(tree.remove('frodo'), 'frodo');
    +  assertEquals(tree.remove('merry'), null);
    +
    +
    +  // Verify strings are stored in sorted order
    +  var i = 0;
    +  tree.inOrderTraverse(function(value) {
    +    assertEquals(values[i], value);
    +    i += 1;
    +  });
    +  assertEquals(i, values.length);
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and have them be stored and sorted correctly by a custom
    + * comparator.
    + */
    +function testInsertsAndRemovesWithCustomComparator() {
    +  var tree = new goog.structs.AvlTree(function(a, b) {
    +    return a - b;
    +  });
    +
    +  var NUM_TO_INSERT = 37;
    +  var valuesToRemove = [1, 0, 6, 7, 36];
    +
    +  // Insert ints into tree out of order
    +  var values = [];
    +  for (var i = 0; i < NUM_TO_INSERT; i += 1) {
    +    tree.add(i);
    +    values.push(i);
    +  }
    +
    +  for (var i = 0; i < valuesToRemove.length; i += 1) {
    +    assertEquals(tree.remove(valuesToRemove[i]), valuesToRemove[i]);
    +    goog.array.remove(values, valuesToRemove[i]);
    +  }
    +  assertEquals(tree.remove(-1), null);
    +  assertEquals(tree.remove(37), null);
    +
    +  // Verify strings are stored in sorted order
    +  var i = 0;
    +  tree.inOrderTraverse(function(value) {
    +    assertEquals(values[i], value);
    +    i += 1;
    +  });
    +  assertEquals(i, values.length);
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and have it maintain the AVL-Tree upperbound on its height.
    + */
    +function testAvlTreeHeight() {
    +  var tree = new goog.structs.AvlTree(function(a, b) {
    +    return a - b;
    +  });
    +
    +  var NUM_TO_INSERT = 2000;
    +  var NUM_TO_REMOVE = 500;
    +
    +  // Insert ints into tree out of order
    +  for (var i = 0; i < NUM_TO_INSERT; i += 1) {
    +    tree.add(i);
    +  }
    +
    +  // Remove valuse
    +  for (var i = 0; i < NUM_TO_REMOVE; i += 1) {
    +    tree.remove(i);
    +  }
    +
    +  assertTrue(tree.getHeight() <= 1.4405 *
    +      (Math.log(NUM_TO_INSERT - NUM_TO_REMOVE + 2) / Math.log(2)) - 1.3277);
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and have its contains method correctly determine the values it
    + * is contains.
    + */
    +function testAvlTreeContains() {
    +  var tree = new goog.structs.AvlTree();
    +  var values = ['bill', 'blake', 'elliot', 'jacob', 'john', 'myles', 'ted'];
    +
    +  // Insert strings into tree out of order
    +  tree.add('frodo');
    +  tree.add(values[4]);
    +  tree.add(values[3]);
    +  tree.add(values[0]);
    +  tree.add(values[6]);
    +  tree.add('samwise');
    +  tree.add(values[5]);
    +  tree.add(values[1]);
    +  tree.add(values[2]);
    +  tree.add('pippin');
    +
    +  // Remove strings from tree
    +  assertEquals(tree.remove('samwise'), 'samwise');
    +  assertEquals(tree.remove('pippin'), 'pippin');
    +  assertEquals(tree.remove('frodo'), 'frodo');
    +
    +  for (var i = 0; i < values.length; i += 1) {
    +    assertTrue(tree.contains(values[i]));
    +  }
    +  assertFalse(tree.contains('samwise'));
    +  assertFalse(tree.contains('pippin'));
    +  assertFalse(tree.contains('frodo'));
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and have its indexOf method correctly determine the in-order
    + * index of the values it contains.
    + */
    +function testAvlTreeIndexOf() {
    +  var tree = new goog.structs.AvlTree();
    +  var values = ['bill', 'blake', 'elliot', 'jacob', 'john', 'myles', 'ted'];
    +
    +  // Insert strings into tree out of order
    +  tree.add('frodo');
    +  tree.add(values[4]);
    +  tree.add(values[3]);
    +  tree.add(values[0]);
    +  tree.add(values[6]);
    +  tree.add('samwise');
    +  tree.add(values[5]);
    +  tree.add(values[1]);
    +  tree.add(values[2]);
    +  tree.add('pippin');
    +
    +  // Remove strings from tree
    +  assertEquals('samwise', tree.remove('samwise'));
    +  assertEquals('pippin', tree.remove('pippin'));
    +  assertEquals('frodo', tree.remove('frodo'));
    +
    +  for (var i = 0; i < values.length; i += 1) {
    +    assertEquals(i, tree.indexOf(values[i]));
    +  }
    +  assertEquals(-1, tree.indexOf('samwise'));
    +  assertEquals(-1, tree.indexOf('pippin'));
    +  assertEquals(-1, tree.indexOf('frodo'));
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and have its minValue and maxValue routines return the correct
    + * min and max values contained by the tree.
    + */
    +function testMinAndMaxValues() {
    +  var tree = new goog.structs.AvlTree(function(a, b) {
    +    return a - b;
    +  });
    +
    +  var NUM_TO_INSERT = 2000;
    +  var NUM_TO_REMOVE = 500;
    +
    +  // Insert ints into tree out of order
    +  for (var i = 0; i < NUM_TO_INSERT; i += 1) {
    +    tree.add(i);
    +  }
    +
    +  // Remove valuse
    +  for (var i = 0; i < NUM_TO_REMOVE; i += 1) {
    +    tree.remove(i);
    +  }
    +
    +  assertEquals(tree.getMinimum(), NUM_TO_REMOVE);
    +  assertEquals(tree.getMaximum(), NUM_TO_INSERT - 1);
    +}
    +
    +
    +/**
    + * This test verifies that we can insert values into and remove values from
    + * the AvlTree and traverse the tree in reverse order using the
    + * reverseOrderTraverse routine.
    + */
    +function testReverseOrderTraverse() {
    +  var tree = new goog.structs.AvlTree(function(a, b) {
    +    return a - b;
    +  });
    +
    +  var NUM_TO_INSERT = 2000;
    +  var NUM_TO_REMOVE = 500;
    +
    +  // Insert ints into tree out of order
    +  for (var i = 0; i < NUM_TO_INSERT; i += 1) {
    +    tree.add(i);
    +  }
    +
    +  // Remove valuse
    +  for (var i = 0; i < NUM_TO_REMOVE; i += 1) {
    +    tree.remove(i);
    +  }
    +
    +  var i = NUM_TO_INSERT - 1;
    +  tree.reverseOrderTraverse(function(value) {
    +    assertEquals(value, i);
    +    i -= 1;
    +  });
    +  assertEquals(i, NUM_TO_REMOVE - 1);
    +}
    +
    +
    +/**
    + * Verifies correct behavior of getCount(). See http://b/4347755
    + */
    +function testGetCountBehavior() {
    +  var tree = new goog.structs.AvlTree();
    +  tree.add(1);
    +  tree.remove(1);
    +  assertEquals(0, tree.getCount());
    +
    +  var values = ['bill', 'blake', 'elliot', 'jacob', 'john', 'myles', 'ted'];
    +
    +  // Insert strings into tree out of order
    +  tree.add('frodo');
    +  tree.add(values[4]);
    +  tree.add(values[3]);
    +  tree.add(values[0]);
    +  tree.add(values[6]);
    +  tree.add('samwise');
    +  tree.add(values[5]);
    +  tree.add(values[1]);
    +  tree.add(values[2]);
    +  tree.add('pippin');
    +  assertEquals(10, tree.getCount());
    +  assertEquals(
    +      tree.root_.left.count + tree.root_.right.count + 1, tree.getCount());
    +
    +  // Remove strings from tree
    +  assertEquals('samwise', tree.remove('samwise'));
    +  assertEquals('pippin', tree.remove('pippin'));
    +  assertEquals('frodo', tree.remove('frodo'));
    +  assertEquals(null, tree.remove('merry'));
    +  assertEquals(7, tree.getCount());
    +
    +  assertEquals(
    +      tree.root_.left.count + tree.root_.right.count + 1, tree.getCount());
    +}
    +
    +
    +/**
    + * This test verifies that getKthOrder gets the correct value.
    + */
    +function testGetKthOrder() {
    +  var tree = new goog.structs.AvlTree(function(a, b) {
    +    return a - b;
    +  });
    +
    +  var NUM_TO_INSERT = 2000;
    +  var NUM_TO_REMOVE = 500;
    +
    +  // Insert ints into tree out of order
    +  for (var i = 0; i < NUM_TO_INSERT; i += 1) {
    +    tree.add(i);
    +  }
    +
    +  // Remove values.
    +  for (var i = 0; i < NUM_TO_REMOVE; i += 1) {
    +    tree.remove(i);
    +  }
    +  for (var k = 0; k < tree.getCount(); ++k) {
    +    assertEquals(NUM_TO_REMOVE + k, tree.getKthValue(k));
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/circularbuffer.js b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer.js
    new file mode 100644
    index 0000000000..e50971c491
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer.js
    @@ -0,0 +1,216 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Datastructure: Circular Buffer.
    + *
    + * Implements a buffer with a maximum size. New entries override the oldest
    + * entries when the maximum size has been reached.
    + *
    + */
    +
    +
    +goog.provide('goog.structs.CircularBuffer');
    +
    +
    +
    +/**
    + * Class for CircularBuffer.
    + * @param {number=} opt_maxSize The maximum size of the buffer.
    + * @constructor
    + * @template T
    + */
    +goog.structs.CircularBuffer = function(opt_maxSize) {
    +  /**
    +   * Index of the next element in the circular array structure.
    +   * @private {number}
    +   */
    +  this.nextPtr_ = 0;
    +
    +  /**
    +   * Maximum size of the the circular array structure.
    +   * @private {number}
    +   */
    +  this.maxSize_ = opt_maxSize || 100;
    +
    +  /**
    +   * Underlying array for the CircularBuffer.
    +   * @private {!Array<T>}
    +   */
    +  this.buff_ = [];
    +};
    +
    +
    +/**
    + * Adds an item to the buffer. May remove the oldest item if the buffer is at
    + * max size.
    + * @param {T} item The item to add.
    + * @return {T|undefined} The removed old item, if the buffer is at max size.
    + *     Return undefined, otherwise.
    + */
    +goog.structs.CircularBuffer.prototype.add = function(item) {
    +  var previousItem = this.buff_[this.nextPtr_];
    +  this.buff_[this.nextPtr_] = item;
    +  this.nextPtr_ = (this.nextPtr_ + 1) % this.maxSize_;
    +  return previousItem;
    +};
    +
    +
    +/**
    + * Returns the item at the specified index.
    + * @param {number} index The index of the item. The index of an item can change
    + *     after calls to {@code add()} if the buffer is at maximum size.
    + * @return {T} The item at the specified index.
    + */
    +goog.structs.CircularBuffer.prototype.get = function(index) {
    +  index = this.normalizeIndex_(index);
    +  return this.buff_[index];
    +};
    +
    +
    +/**
    + * Sets the item at the specified index.
    + * @param {number} index The index of the item. The index of an item can change
    + *     after calls to {@code add()} if the buffer is at maximum size.
    + * @param {T} item The item to add.
    + */
    +goog.structs.CircularBuffer.prototype.set = function(index, item) {
    +  index = this.normalizeIndex_(index);
    +  this.buff_[index] = item;
    +};
    +
    +
    +/**
    + * Returns the current number of items in the buffer.
    + * @return {number} The current number of items in the buffer.
    + */
    +goog.structs.CircularBuffer.prototype.getCount = function() {
    +  return this.buff_.length;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the buffer is empty.
    + */
    +goog.structs.CircularBuffer.prototype.isEmpty = function() {
    +  return this.buff_.length == 0;
    +};
    +
    +
    +/**
    + * Empties the current buffer.
    + */
    +goog.structs.CircularBuffer.prototype.clear = function() {
    +  this.buff_.length = 0;
    +  this.nextPtr_ = 0;
    +};
    +
    +
    +/** @return {!Array<T>} The values in the buffer. */
    +goog.structs.CircularBuffer.prototype.getValues = function() {
    +  // getNewestValues returns all the values if the maxCount parameter is the
    +  // count
    +  return this.getNewestValues(this.getCount());
    +};
    +
    +
    +/**
    + * Returns the newest values in the buffer up to {@code count}.
    + * @param {number} maxCount The maximum number of values to get. Should be a
    + *     positive number.
    + * @return {!Array<T>} The newest values in the buffer up to {@code count}.
    + */
    +goog.structs.CircularBuffer.prototype.getNewestValues = function(maxCount) {
    +  var l = this.getCount();
    +  var start = this.getCount() - maxCount;
    +  var rv = [];
    +  for (var i = start; i < l; i++) {
    +    rv.push(this.get(i));
    +  }
    +  return rv;
    +};
    +
    +
    +/** @return {!Array<number>} The indexes in the buffer. */
    +goog.structs.CircularBuffer.prototype.getKeys = function() {
    +  var rv = [];
    +  var l = this.getCount();
    +  for (var i = 0; i < l; i++) {
    +    rv[i] = i;
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Whether the buffer contains the key/index.
    + * @param {number} key The key/index to check for.
    + * @return {boolean} Whether the buffer contains the key/index.
    + */
    +goog.structs.CircularBuffer.prototype.containsKey = function(key) {
    +  return key < this.getCount();
    +};
    +
    +
    +/**
    + * Whether the buffer contains the given value.
    + * @param {T} value The value to check for.
    + * @return {boolean} Whether the buffer contains the given value.
    + */
    +goog.structs.CircularBuffer.prototype.containsValue = function(value) {
    +  var l = this.getCount();
    +  for (var i = 0; i < l; i++) {
    +    if (this.get(i) == value) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns the last item inserted into the buffer.
    + * @return {T|null} The last item inserted into the buffer,
    + *     or null if the buffer is empty.
    + */
    +goog.structs.CircularBuffer.prototype.getLast = function() {
    +  if (this.getCount() == 0) {
    +    return null;
    +  }
    +  return this.get(this.getCount() - 1);
    +};
    +
    +
    +/**
    + * Helper function to convert an index in the number space of oldest to
    + * newest items in the array to the position that the element will be at in the
    + * underlying array.
    + * @param {number} index The index of the item in a list ordered from oldest to
    + *     newest.
    + * @return {number} The index of the item in the CircularBuffer's underlying
    + *     array.
    + * @private
    + */
    +goog.structs.CircularBuffer.prototype.normalizeIndex_ = function(index) {
    +  if (index >= this.buff_.length) {
    +    throw Error('Out of bounds exception');
    +  }
    +
    +  if (this.buff_.length < this.maxSize_) {
    +    return index;
    +  }
    +
    +  return (this.nextPtr_ + Number(index)) % this.maxSize_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.html b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.html
    new file mode 100644
    index 0000000000..e022530763
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.structs.CircularBufferTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.js b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.js
    new file mode 100644
    index 0000000000..d3e81f217c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/circularbuffer_test.js
    @@ -0,0 +1,101 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.CircularBufferTest');
    +goog.setTestOnly('goog.structs.CircularBufferTest');
    +
    +goog.require('goog.structs.CircularBuffer');
    +goog.require('goog.testing.jsunit');
    +
    +function testCircularBuffer() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertUndefined(buff.add('first'));
    +  assertEquals(1, buff.getCount());
    +  assertEquals('first', buff.get(0));
    +  assertEquals('first', buff.getLast());
    +  assertUndefined(buff.add('second'));
    +  assertEquals(2, buff.getCount());
    +  assertEquals('first', buff.get(0));
    +  assertEquals('second', buff.get(1));
    +  assertEquals('second', buff.getLast());
    +  assertEquals('first', buff.add('third'));
    +  assertEquals(2, buff.getCount());
    +  assertEquals('second', buff.get(0));
    +  assertEquals('third', buff.get(1));
    +  assertEquals('third', buff.getLast());
    +}
    +
    +function testIsEmpty() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertTrue('initially empty', buff.isEmpty());
    +  assertUndefined(buff.add('first'));
    +  assertFalse('not empty after add empty', buff.isEmpty());
    +}
    +
    +function testClear() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertUndefined(buff.add('first'));
    +  buff.clear();
    +  assertTrue('should be empty after clear', buff.isEmpty());
    +
    +}
    +
    +function testGetValues() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertUndefined(buff.add('first'));
    +  assertUndefined(buff.add('second'));
    +  assertArrayEquals(['first', 'second'], buff.getValues());
    +}
    +
    +function testGetNewestValues() {
    +  var buff = new goog.structs.CircularBuffer(5);
    +  assertUndefined(buff.add('first'));
    +  assertUndefined(buff.add('second'));
    +  assertUndefined(buff.add('third'));
    +  assertUndefined(buff.add('fourth'));
    +  assertUndefined(buff.add('fifth'));
    +  assertArrayEquals(['fourth', 'fifth'], buff.getNewestValues(2));
    +}
    +
    +
    +function testGetKeys() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertUndefined(buff.add('first'));
    +  assertUndefined(buff.add('second'));
    +  assertArrayEquals([0, 1], buff.getKeys());
    +}
    +
    +function testContainsValue() {
    +  var buff = new goog.structs.CircularBuffer(2);
    +  assertUndefined(buff.add('first'));
    +  assertUndefined(buff.add('second'));
    +  assertTrue(buff.containsValue('first'));
    +  assertTrue(buff.containsValue('second'));
    +  assertFalse(buff.containsValue('third'));
    +}
    +
    +function testContainsKey() {
    +  var buff = new goog.structs.CircularBuffer(3);
    +  assertUndefined(buff.add('first'));
    +  assertUndefined(buff.add('second'));
    +  assertUndefined(buff.add('third'));
    +  assertTrue(buff.containsKey(0));
    +  assertTrue(buff.containsKey('0'));
    +  assertTrue(buff.containsKey(1));
    +  assertTrue(buff.containsKey('1'));
    +  assertTrue(buff.containsKey(2));
    +  assertTrue(buff.containsKey('2'));
    +  assertFalse(buff.containsKey(3));
    +  assertFalse(buff.containsKey('3'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/collection.js b/mapbox-gl/closure-library/closure/goog/structs/collection.js
    new file mode 100644
    index 0000000000..86d008db5c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/collection.js
    @@ -0,0 +1,56 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines the collection interface.
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.structs.Collection');
    +
    +
    +
    +/**
    + * An interface for a collection of values.
    + * @interface
    + * @template T
    + */
    +goog.structs.Collection = function() {};
    +
    +
    +/**
    + * @param {T} value Value to add to the collection.
    + */
    +goog.structs.Collection.prototype.add;
    +
    +
    +/**
    + * @param {T} value Value to remove from the collection.
    + */
    +goog.structs.Collection.prototype.remove;
    +
    +
    +/**
    + * @param {T} value Value to find in the collection.
    + * @return {boolean} Whether the collection contains the specified value.
    + */
    +goog.structs.Collection.prototype.contains;
    +
    +
    +/**
    + * @return {number} The number of values stored in the collection.
    + */
    +goog.structs.Collection.prototype.getCount;
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/collection_test.html b/mapbox-gl/closure-library/closure/goog/structs/collection_test.html
    new file mode 100644
    index 0000000000..240db38cdc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/collection_test.html
    @@ -0,0 +1,21 @@
    +<!doctype html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.structs.Collection
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.CollectionTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/collection_test.js b/mapbox-gl/closure-library/closure/goog/structs/collection_test.js
    new file mode 100644
    index 0000000000..3545b82a16
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/collection_test.js
    @@ -0,0 +1,53 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.CollectionTest');
    +goog.setTestOnly('goog.structs.CollectionTest');
    +
    +goog.require('goog.structs.AvlTree');
    +goog.require('goog.structs.Set');
    +goog.require('goog.testing.jsunit');
    +
    +function testSet() {
    +  var set = new goog.structs.Set();
    +  exerciseCollection(set);
    +}
    +
    +function testAvlTree() {
    +  var tree = new goog.structs.AvlTree();
    +  exerciseCollection(tree);
    +}
    +
    +// Simple exercise of a collection object.
    +function exerciseCollection(collection) {
    +  assertEquals(0, collection.getCount());
    +
    +  for (var i = 1; i <= 10; i++) {
    +    assertFalse(collection.contains(i));
    +    collection.add(i);
    +    assertTrue(collection.contains(i));
    +    assertEquals(i, collection.getCount());
    +  }
    +
    +  assertEquals(10, collection.getCount());
    +
    +  for (var i = 10; i > 0; i--) {
    +    assertTrue(collection.contains(i));
    +    collection.remove(i);
    +    assertFalse(collection.contains(i));
    +    assertEquals(i - 1, collection.getCount());
    +  }
    +
    +  assertEquals(0, collection.getCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/heap.js b/mapbox-gl/closure-library/closure/goog/structs/heap.js
    new file mode 100644
    index 0000000000..c2b09b4d89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/heap.js
    @@ -0,0 +1,334 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Heap.
    + *
    + *
    + * This file provides the implementation of a Heap datastructure. Smaller keys
    + * rise to the top.
    + *
    + * The big-O notation for all operations are below:
    + * <pre>
    + *  Method          big-O
    + * ----------------------------------------------------------------------------
    + * - insert         O(logn)
    + * - remove         O(logn)
    + * - peek           O(1)
    + * - contains       O(n)
    + * </pre>
    + */
    +// TODO(user): Should this rely on natural ordering via some Comparable
    +//     interface?
    +
    +
    +goog.provide('goog.structs.Heap');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('goog.structs.Node');
    +
    +
    +
    +/**
    + * Class for a Heap datastructure.
    + *
    + * @param {goog.structs.Heap|Object=} opt_heap Optional goog.structs.Heap or
    + *     Object to initialize heap with.
    + * @constructor
    + * @template K, V
    + */
    +goog.structs.Heap = function(opt_heap) {
    +  /**
    +   * The nodes of the heap.
    +   * @private
    +   * @type {Array<goog.structs.Node>}
    +   */
    +  this.nodes_ = [];
    +
    +  if (opt_heap) {
    +    this.insertAll(opt_heap);
    +  }
    +};
    +
    +
    +/**
    + * Insert the given value into the heap with the given key.
    + * @param {K} key The key.
    + * @param {V} value The value.
    + */
    +goog.structs.Heap.prototype.insert = function(key, value) {
    +  var node = new goog.structs.Node(key, value);
    +  var nodes = this.nodes_;
    +  nodes.push(node);
    +  this.moveUp_(nodes.length - 1);
    +};
    +
    +
    +/**
    + * Adds multiple key-value pairs from another goog.structs.Heap or Object
    + * @param {goog.structs.Heap|Object} heap Object containing the data to add.
    + */
    +goog.structs.Heap.prototype.insertAll = function(heap) {
    +  var keys, values;
    +  if (heap instanceof goog.structs.Heap) {
    +    keys = heap.getKeys();
    +    values = heap.getValues();
    +
    +    // If it is a heap and the current heap is empty, I can rely on the fact
    +    // that the keys/values are in the correct order to put in the underlying
    +    // structure.
    +    if (heap.getCount() <= 0) {
    +      var nodes = this.nodes_;
    +      for (var i = 0; i < keys.length; i++) {
    +        nodes.push(new goog.structs.Node(keys[i], values[i]));
    +      }
    +      return;
    +    }
    +  } else {
    +    keys = goog.object.getKeys(heap);
    +    values = goog.object.getValues(heap);
    +  }
    +
    +  for (var i = 0; i < keys.length; i++) {
    +    this.insert(keys[i], values[i]);
    +  }
    +};
    +
    +
    +/**
    + * Retrieves and removes the root value of this heap.
    + * @return {V} The value removed from the root of the heap.  Returns
    + *     undefined if the heap is empty.
    + */
    +goog.structs.Heap.prototype.remove = function() {
    +  var nodes = this.nodes_;
    +  var count = nodes.length;
    +  var rootNode = nodes[0];
    +  if (count <= 0) {
    +    return undefined;
    +  } else if (count == 1) {
    +    goog.array.clear(nodes);
    +  } else {
    +    nodes[0] = nodes.pop();
    +    this.moveDown_(0);
    +  }
    +  return rootNode.getValue();
    +};
    +
    +
    +/**
    + * Retrieves but does not remove the root value of this heap.
    + * @return {V} The value at the root of the heap. Returns
    + *     undefined if the heap is empty.
    + */
    +goog.structs.Heap.prototype.peek = function() {
    +  var nodes = this.nodes_;
    +  if (nodes.length == 0) {
    +    return undefined;
    +  }
    +  return nodes[0].getValue();
    +};
    +
    +
    +/**
    + * Retrieves but does not remove the key of the root node of this heap.
    + * @return {K} The key at the root of the heap. Returns undefined if the
    + *     heap is empty.
    + */
    +goog.structs.Heap.prototype.peekKey = function() {
    +  return this.nodes_[0] && this.nodes_[0].getKey();
    +};
    +
    +
    +/**
    + * Moves the node at the given index down to its proper place in the heap.
    + * @param {number} index The index of the node to move down.
    + * @private
    + */
    +goog.structs.Heap.prototype.moveDown_ = function(index) {
    +  var nodes = this.nodes_;
    +  var count = nodes.length;
    +
    +  // Save the node being moved down.
    +  var node = nodes[index];
    +  // While the current node has a child.
    +  while (index < (count >> 1)) {
    +    var leftChildIndex = this.getLeftChildIndex_(index);
    +    var rightChildIndex = this.getRightChildIndex_(index);
    +
    +    // Determine the index of the smaller child.
    +    var smallerChildIndex = rightChildIndex < count &&
    +        nodes[rightChildIndex].getKey() < nodes[leftChildIndex].getKey() ?
    +        rightChildIndex : leftChildIndex;
    +
    +    // If the node being moved down is smaller than its children, the node
    +    // has found the correct index it should be at.
    +    if (nodes[smallerChildIndex].getKey() > node.getKey()) {
    +      break;
    +    }
    +
    +    // If not, then take the smaller child as the current node.
    +    nodes[index] = nodes[smallerChildIndex];
    +    index = smallerChildIndex;
    +  }
    +  nodes[index] = node;
    +};
    +
    +
    +/**
    + * Moves the node at the given index up to its proper place in the heap.
    + * @param {number} index The index of the node to move up.
    + * @private
    + */
    +goog.structs.Heap.prototype.moveUp_ = function(index) {
    +  var nodes = this.nodes_;
    +  var node = nodes[index];
    +
    +  // While the node being moved up is not at the root.
    +  while (index > 0) {
    +    // If the parent is less than the node being moved up, move the parent down.
    +    var parentIndex = this.getParentIndex_(index);
    +    if (nodes[parentIndex].getKey() > node.getKey()) {
    +      nodes[index] = nodes[parentIndex];
    +      index = parentIndex;
    +    } else {
    +      break;
    +    }
    +  }
    +  nodes[index] = node;
    +};
    +
    +
    +/**
    + * Gets the index of the left child of the node at the given index.
    + * @param {number} index The index of the node to get the left child for.
    + * @return {number} The index of the left child.
    + * @private
    + */
    +goog.structs.Heap.prototype.getLeftChildIndex_ = function(index) {
    +  return index * 2 + 1;
    +};
    +
    +
    +/**
    + * Gets the index of the right child of the node at the given index.
    + * @param {number} index The index of the node to get the right child for.
    + * @return {number} The index of the right child.
    + * @private
    + */
    +goog.structs.Heap.prototype.getRightChildIndex_ = function(index) {
    +  return index * 2 + 2;
    +};
    +
    +
    +/**
    + * Gets the index of the parent of the node at the given index.
    + * @param {number} index The index of the node to get the parent for.
    + * @return {number} The index of the parent.
    + * @private
    + */
    +goog.structs.Heap.prototype.getParentIndex_ = function(index) {
    +  return (index - 1) >> 1;
    +};
    +
    +
    +/**
    + * Gets the values of the heap.
    + * @return {!Array<V>} The values in the heap.
    + */
    +goog.structs.Heap.prototype.getValues = function() {
    +  var nodes = this.nodes_;
    +  var rv = [];
    +  var l = nodes.length;
    +  for (var i = 0; i < l; i++) {
    +    rv.push(nodes[i].getValue());
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Gets the keys of the heap.
    + * @return {!Array<K>} The keys in the heap.
    + */
    +goog.structs.Heap.prototype.getKeys = function() {
    +  var nodes = this.nodes_;
    +  var rv = [];
    +  var l = nodes.length;
    +  for (var i = 0; i < l; i++) {
    +    rv.push(nodes[i].getKey());
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Whether the heap contains the given value.
    + * @param {V} val The value to check for.
    + * @return {boolean} Whether the heap contains the value.
    + */
    +goog.structs.Heap.prototype.containsValue = function(val) {
    +  return goog.array.some(this.nodes_, function(node) {
    +    return node.getValue() == val;
    +  });
    +};
    +
    +
    +/**
    + * Whether the heap contains the given key.
    + * @param {K} key The key to check for.
    + * @return {boolean} Whether the heap contains the key.
    + */
    +goog.structs.Heap.prototype.containsKey = function(key) {
    +  return goog.array.some(this.nodes_, function(node) {
    +    return node.getKey() == key;
    +  });
    +};
    +
    +
    +/**
    + * Clones a heap and returns a new heap
    + * @return {!goog.structs.Heap} A new goog.structs.Heap with the same key-value
    + *     pairs.
    + */
    +goog.structs.Heap.prototype.clone = function() {
    +  return new goog.structs.Heap(this);
    +};
    +
    +
    +/**
    + * The number of key-value pairs in the map
    + * @return {number} The number of pairs.
    + */
    +goog.structs.Heap.prototype.getCount = function() {
    +  return this.nodes_.length;
    +};
    +
    +
    +/**
    + * Returns true if this heap contains no elements.
    + * @return {boolean} Whether this heap contains no elements.
    + */
    +goog.structs.Heap.prototype.isEmpty = function() {
    +  return goog.array.isEmpty(this.nodes_);
    +};
    +
    +
    +/**
    + * Removes all elements from the heap.
    + */
    +goog.structs.Heap.prototype.clear = function() {
    +  goog.array.clear(this.nodes_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/heap_test.html b/mapbox-gl/closure-library/closure/goog/structs/heap_test.html
    new file mode 100644
    index 0000000000..a771036f49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/heap_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Heap
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.HeapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/heap_test.js b/mapbox-gl/closure-library/closure/goog/structs/heap_test.js
    new file mode 100644
    index 0000000000..d40a80e166
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/heap_test.js
    @@ -0,0 +1,215 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.HeapTest');
    +goog.setTestOnly('goog.structs.HeapTest');
    +
    +goog.require('goog.structs');
    +goog.require('goog.structs.Heap');
    +goog.require('goog.testing.jsunit');
    +
    +function getHeap() {
    +  var h = new goog.structs.Heap();
    +  h.insert(0, 'a');
    +  h.insert(1, 'b');
    +  h.insert(2, 'c');
    +  h.insert(3, 'd');
    +  return h;
    +}
    +
    +
    +function getHeap2() {
    +  var h = new goog.structs.Heap();
    +  h.insert(1, 'b');
    +  h.insert(3, 'd');
    +  h.insert(0, 'a');
    +  h.insert(2, 'c');
    +  return h;
    +}
    +
    +
    +function testGetCount1() {
    +  var h = getHeap();
    +  assertEquals('count, should be 4', h.getCount(), 4);
    +  h.remove();
    +  assertEquals('count, should be 3', h.getCount(), 3);
    +}
    +
    +function testGetCount2() {
    +  var h = getHeap();
    +  h.remove();
    +  h.remove();
    +  h.remove();
    +  h.remove();
    +  assertEquals('count, should be 0', h.getCount(), 0);
    +}
    +
    +
    +function testKeys() {
    +  var h = getHeap();
    +  var keys = h.getKeys();
    +  for (var i = 0; i < 4; i++) {
    +    assertTrue('getKeys, key ' + i + ' found', goog.structs.contains(keys, i));
    +  }
    +  assertEquals('getKeys, Should be 4 keys', goog.structs.getCount(keys), 4);
    +}
    +
    +
    +function testValues() {
    +  var h = getHeap();
    +  var values = h.getValues();
    +
    +  assertTrue('getKeys, value "a" found', goog.structs.contains(values, 'a'));
    +  assertTrue('getKeys, value "b" found', goog.structs.contains(values, 'b'));
    +  assertTrue('getKeys, value "c" found', goog.structs.contains(values, 'c'));
    +  assertTrue('getKeys, value "d" found', goog.structs.contains(values, 'd'));
    +  assertEquals('getKeys, Should be 4 keys', goog.structs.getCount(values), 4);
    +}
    +
    +
    +function testContainsKey() {
    +  var h = getHeap();
    +
    +  for (var i = 0; i < 4; i++) {
    +    assertTrue('containsKey, key ' + i + ' found', h.containsKey(i));
    +  }
    +  assertFalse('containsKey, value 4 not found', h.containsKey(4));
    +}
    +
    +
    +function testContainsValue() {
    +  var h = getHeap();
    +
    +  assertTrue('containsValue, value "a" found', h.containsValue('a'));
    +  assertTrue('containsValue, value "b" found', h.containsValue('b'));
    +  assertTrue('containsValue, value "c" found', h.containsValue('c'));
    +  assertTrue('containsValue, value "d" found', h.containsValue('d'));
    +  assertFalse('containsValue, value "e" not found', h.containsValue('e'));
    +}
    +
    +
    +function testClone() {
    +  var h = getHeap();
    +  var h2 = h.clone();
    +  assertTrue('clone so it should not be empty', !h2.isEmpty());
    +  assertTrue('clone so it should contain key 0', h2.containsKey(0));
    +  assertTrue('clone so it should contain value "a"', h2.containsValue('a'));
    +}
    +
    +
    +function testClear() {
    +  var h = getHeap();
    +  h.clear();
    +  assertTrue('cleared so it should be empty', h.isEmpty());
    +}
    +
    +
    +function testIsEmpty() {
    +  var h = getHeap();
    +  assertFalse('4 values so should not be empty', h.isEmpty());
    +
    +  h.remove();
    +  h.remove();
    +  h.remove();
    +  assertFalse('1 values so should not be empty', h.isEmpty());
    +
    +  h.remove();
    +  assertTrue('0 values so should be empty', h.isEmpty());
    +}
    +
    +
    +function testPeek1() {
    +  var h = getHeap();
    +  assertEquals('peek, Should be "a"', h.peek(), 'a');
    +}
    +
    +
    +function testPeek2() {
    +  var h = getHeap2();
    +  assertEquals('peek, Should be "a"', h.peek(), 'a');
    +}
    +
    +
    +function testPeek3() {
    +  var h = getHeap();
    +  h.clear();
    +  assertEquals('peek, Should be "undefined"', h.peek(), undefined);
    +}
    +
    +
    +function testPeekKey1() {
    +  var h = getHeap();
    +  assertEquals('peekKey, Should be "0"', h.peekKey(), 0);
    +}
    +
    +
    +function testPeekKey2() {
    +  var h = getHeap2();
    +  assertEquals('peekKey, Should be "0"', h.peekKey(), 0);
    +}
    +
    +
    +function testPeekKey3() {
    +  var h = getHeap();
    +  h.clear();
    +  assertEquals('peekKey, Should be "undefined"', h.peekKey(), undefined);
    +}
    +
    +
    +function testRemove1() {
    +  var h = getHeap();
    +
    +  assertEquals('remove, Should be "a"', h.remove(), 'a');
    +  assertEquals('remove, Should be "b"', h.remove(), 'b');
    +  assertEquals('remove, Should be "c"', h.remove(), 'c');
    +  assertEquals('remove, Should be "d"', h.remove(), 'd');
    +}
    +
    +
    +function testRemove2() {
    +  var h = getHeap2();
    +
    +  assertEquals('remove, Should be "a"', h.remove(), 'a');
    +  assertEquals('remove, Should be "b"', h.remove(), 'b');
    +  assertEquals('remove, Should be "c"', h.remove(), 'c');
    +  assertEquals('remove, Should be "d"', h.remove(), 'd');
    +}
    +
    +
    +function testInsertPeek1() {
    +  var h = new goog.structs.Heap();
    +
    +  h.insert(3, 'd');
    +  assertEquals('peek, Should be "d"', h.peek(), 'd');
    +  h.insert(2, 'c');
    +  assertEquals('peek, Should be "c"', h.peek(), 'c');
    +  h.insert(1, 'b');
    +  assertEquals('peek, Should be "b"', h.peek(), 'b');
    +  h.insert(0, 'a');
    +  assertEquals('peek, Should be "a"', h.peek(), 'a');
    +}
    +
    +
    +function testInsertPeek2() {
    +  var h = new goog.structs.Heap();
    +
    +  h.insert(1, 'b');
    +  assertEquals('peak, Should be "b"', h.peek(), 'b');
    +  h.insert(3, 'd');
    +  assertEquals('peak, Should be "b"', h.peek(), 'b');
    +  h.insert(0, 'a');
    +  assertEquals('peak, Should be "a"', h.peek(), 'a');
    +  h.insert(2, 'c');
    +  assertEquals('peak, Should be "a"', h.peek(), 'a');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/inversionmap.js b/mapbox-gl/closure-library/closure/goog/structs/inversionmap.js
    new file mode 100644
    index 0000000000..cbc69e57d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/inversionmap.js
    @@ -0,0 +1,155 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides inversion and inversion map functionality for storing
    + * integer ranges and corresponding values.
    + *
    + */
    +
    +goog.provide('goog.structs.InversionMap');
    +
    +goog.require('goog.array');
    +
    +
    +
    +/**
    + * Maps ranges to values.
    + * @param {Array<number>} rangeArray An array of monotonically
    + *     increasing integer values, with at least one instance.
    + * @param {Array<T>} valueArray An array of corresponding values.
    + *     Length must be the same as rangeArray.
    + * @param {boolean=} opt_delta If true, saves only delta from previous value.
    + * @constructor
    + * @template T
    + */
    +goog.structs.InversionMap = function(rangeArray, valueArray, opt_delta) {
    +  /**
    +   * @protected {Array<number>}
    +   */
    +  this.rangeArray = null;
    +
    +  if (rangeArray.length != valueArray.length) {
    +    // rangeArray and valueArray has to match in number of entries.
    +    return null;
    +  }
    +  this.storeInversion_(rangeArray, opt_delta);
    +
    +  /** @protected {Array<T>} */
    +  this.values = valueArray;
    +};
    +
    +
    +/**
    + * Stores the integers as ranges (half-open).
    + * If delta is true, the integers are delta from the previous value and
    + * will be restored to the absolute value.
    + * When used as a set, even indices are IN, and odd are OUT.
    + * @param {Array<number?>} rangeArray An array of monotonically
    + *     increasing integer values, with at least one instance.
    + * @param {boolean=} opt_delta If true, saves only delta from previous value.
    + * @private
    + */
    +goog.structs.InversionMap.prototype.storeInversion_ = function(rangeArray,
    +    opt_delta) {
    +  this.rangeArray = rangeArray;
    +
    +  for (var i = 1; i < rangeArray.length; i++) {
    +    if (rangeArray[i] == null) {
    +      rangeArray[i] = rangeArray[i - 1] + 1;
    +    } else if (opt_delta) {
    +      rangeArray[i] += rangeArray[i - 1];
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Splices a range -> value map into this inversion map.
    + * @param {Array<number>} rangeArray An array of monotonically
    + *     increasing integer values, with at least one instance.
    + * @param {Array<T>} valueArray An array of corresponding values.
    + *     Length must be the same as rangeArray.
    + * @param {boolean=} opt_delta If true, saves only delta from previous value.
    + */
    +goog.structs.InversionMap.prototype.spliceInversion = function(
    +    rangeArray, valueArray, opt_delta) {
    +  // By building another inversion map, we build the arrays that we need
    +  // to splice in.
    +  var otherMap = new goog.structs.InversionMap(
    +      rangeArray, valueArray, opt_delta);
    +
    +  // Figure out where to splice those arrays.
    +  var startRange = otherMap.rangeArray[0];
    +  var endRange =
    +      /** @type {number} */ (goog.array.peek(otherMap.rangeArray));
    +  var startSplice = this.getLeast(startRange);
    +  var endSplice = this.getLeast(endRange);
    +
    +  // The inversion map works by storing the start points of ranges...
    +  if (startRange != this.rangeArray[startSplice]) {
    +    // ...if we're splicing in a start point that isn't already here,
    +    // then we need to insert it after the insertion point.
    +    startSplice++;
    +  } // otherwise we overwrite the insertion point.
    +
    +  var spliceLength = endSplice - startSplice + 1;
    +  goog.partial(goog.array.splice, this.rangeArray, startSplice,
    +      spliceLength).apply(null, otherMap.rangeArray);
    +  goog.partial(goog.array.splice, this.values, startSplice,
    +      spliceLength).apply(null, otherMap.values);
    +};
    +
    +
    +/**
    + * Gets the value corresponding to a number from the inversion map.
    + * @param {number} intKey The number for which value needs to be retrieved
    + *     from inversion map.
    + * @return {T|null} Value retrieved from inversion map; null if not found.
    + */
    +goog.structs.InversionMap.prototype.at = function(intKey) {
    +  var index = this.getLeast(intKey);
    +  if (index < 0) {
    +    return null;
    +  }
    +  return this.values[index];
    +};
    +
    +
    +/**
    + * Gets the largest index such that rangeArray[index] <= intKey from the
    + * inversion map.
    + * @param {number} intKey The probe for which rangeArray is searched.
    + * @return {number} Largest index such that rangeArray[index] <= intKey.
    + * @protected
    + */
    +goog.structs.InversionMap.prototype.getLeast = function(intKey) {
    +  var arr = this.rangeArray;
    +  var low = 0;
    +  var high = arr.length;
    +  while (high - low > 8) {
    +    var mid = (high + low) >> 1;
    +    if (arr[mid] <= intKey) {
    +      low = mid;
    +    } else {
    +      high = mid;
    +    }
    +  }
    +  for (; low < high; ++low) {
    +    if (intKey < arr[low]) {
    +      break;
    +    }
    +  }
    +  return low - 1;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.html b/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.html
    new file mode 100644
    index 0000000000..9ee0fe0c33
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.InversionMap
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.structs.InversionMapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.js b/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.js
    new file mode 100644
    index 0000000000..32560f5b56
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/inversionmap_test.js
    @@ -0,0 +1,154 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.InversionMapTest');
    +goog.setTestOnly('goog.structs.InversionMapTest');
    +
    +goog.require('goog.structs.InversionMap');
    +goog.require('goog.testing.jsunit');
    +
    +function testInversionWithDelta() {
    +  var alphabetNames = new goog.structs.InversionMap(
    +      [0, 97, 1, 1, 1, 20, 1, 1, 1],
    +      [null,
    +        'LATIN SMALL LETTER A',
    +        'LATIN SMALL LETTER B',
    +        'LATIN SMALL LETTER C',
    +        null,
    +        'LATIN SMALL LETTER X',
    +        'LATIN SMALL LETTER Y',
    +        'LATIN SMALL LETTER Z',
    +        null],
    +      true);
    +
    +  assertEquals('LATIN SMALL LETTER A', alphabetNames.at(97));
    +  assertEquals('LATIN SMALL LETTER Y', alphabetNames.at(121));
    +  assertEquals(null, alphabetNames.at(140));
    +  assertEquals(null, alphabetNames.at(0));
    +}
    +
    +function testInversionWithoutDelta() {
    +  var alphabetNames = new goog.structs.InversionMap(
    +      [0, 97, 98, 99, 100, 120, 121, 122, 123],
    +      [null,
    +        'LATIN SMALL LETTER A',
    +        'LATIN SMALL LETTER B',
    +        'LATIN SMALL LETTER C',
    +        null,
    +        'LATIN SMALL LETTER X',
    +        'LATIN SMALL LETTER Y',
    +        'LATIN SMALL LETTER Z',
    +        null],
    +      false);
    +
    +  assertEquals('LATIN SMALL LETTER A', alphabetNames.at(97));
    +  assertEquals('LATIN SMALL LETTER Y', alphabetNames.at(121));
    +  assertEquals(null, alphabetNames.at(140));
    +  assertEquals(null, alphabetNames.at(0));
    +}
    +
    +function testInversionWithoutDeltaNoOpt() {
    +  var alphabetNames = new goog.structs.InversionMap(
    +      [0, 97, 98, 99, 100, 120, 121, 122, 123],
    +      [null,
    +        'LATIN SMALL LETTER A',
    +        'LATIN SMALL LETTER B',
    +        'LATIN SMALL LETTER C',
    +        null,
    +        'LATIN SMALL LETTER X',
    +        'LATIN SMALL LETTER Y',
    +        'LATIN SMALL LETTER Z',
    +        null]);
    +
    +  assertEquals('LATIN SMALL LETTER A', alphabetNames.at(97));
    +  assertEquals('LATIN SMALL LETTER Y', alphabetNames.at(121));
    +  assertEquals(null, alphabetNames.at(140));
    +  assertEquals(null, alphabetNames.at(0));
    +}
    +
    +function testInversionMapSplice1() {
    +  var alphabetNames = newAsciiMap();
    +  alphabetNames.spliceInversion(
    +      [99, 105, 114], ['XXX', 'YYY', 'ZZZ']);
    +  assertEquals('LATIN SMALL LETTER B', alphabetNames.at(98));
    +  assertEquals('XXX', alphabetNames.at(100));
    +  assertEquals('ZZZ', alphabetNames.at(114));
    +  assertEquals('ZZZ', alphabetNames.at(119));
    +  assertEquals('LATIN SMALL LETTER X', alphabetNames.at(120));
    +}
    +
    +function testInversionMapSplice2() {
    +  var alphabetNames = newAsciiMap();
    +  alphabetNames.spliceInversion(
    +      [105, 114, 121], ['XXX', 'YYY', 'ZZZ']);
    +  assertEquals(null, alphabetNames.at(104));
    +  assertEquals('XXX', alphabetNames.at(105));
    +  assertEquals('YYY', alphabetNames.at(120));
    +  assertEquals('ZZZ', alphabetNames.at(121));
    +  assertEquals('LATIN SMALL LETTER Z', alphabetNames.at(122));
    +}
    +
    +function testInversionMapSplice3() {
    +  var alphabetNames = newAsciiMap();
    +  alphabetNames.spliceInversion(
    +      [98, 99], ['CHANGED LETTER B', 'CHANGED LETTER C']);
    +  assertEquals('LATIN SMALL LETTER A', alphabetNames.at(97));
    +  assertEquals('CHANGED LETTER B', alphabetNames.at(98));
    +  assertEquals('CHANGED LETTER C', alphabetNames.at(99));
    +  assertEquals('LATIN SMALL LETTER D', alphabetNames.at(100));
    +  assertEquals(null, alphabetNames.at(101));
    +}
    +
    +function testInversionMapSplice4() {
    +  var alphabetNames = newAsciiMap();
    +  alphabetNames.spliceInversion(
    +      [98, 1], ['CHANGED LETTER B', 'CHANGED LETTER C'],
    +      true /* delta mode */);
    +  assertEquals('LATIN SMALL LETTER A', alphabetNames.at(97));
    +  assertEquals('CHANGED LETTER B', alphabetNames.at(98));
    +  assertEquals('CHANGED LETTER C', alphabetNames.at(99));
    +  assertEquals('LATIN SMALL LETTER D', alphabetNames.at(100));
    +  assertEquals(null, alphabetNames.at(101));
    +}
    +
    +function testInversionMapSplice5() {
    +  var map = new goog.structs.InversionMap(
    +      [0, 97, 98, 99],
    +      [null,
    +       'LATIN SMALL LETTER A',
    +       'LATIN SMALL LETTER B',
    +       'LATIN SMALL LETTER C']);
    +  map.spliceInversion(
    +      [98], ['CHANGED LETTER B']);
    +  assertEquals('LATIN SMALL LETTER A', map.at(97));
    +  assertEquals('CHANGED LETTER B', map.at(98));
    +  assertEquals('LATIN SMALL LETTER C', map.at(99));
    +
    +  assertArrayEquals([0, 97, 98, 99], map.rangeArray);
    +}
    +
    +function newAsciiMap() {
    +  return new goog.structs.InversionMap(
    +      [0, 97, 98, 99, 100, 101, 120, 121, 122, 123],
    +      [null,
    +        'LATIN SMALL LETTER A',
    +        'LATIN SMALL LETTER B',
    +        'LATIN SMALL LETTER C',
    +        'LATIN SMALL LETTER D',
    +        null,
    +        'LATIN SMALL LETTER X',
    +        'LATIN SMALL LETTER Y',
    +        'LATIN SMALL LETTER Z',
    +        null]);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/linkedmap.js b/mapbox-gl/closure-library/closure/goog/structs/linkedmap.js
    new file mode 100644
    index 0000000000..b064586ace
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/linkedmap.js
    @@ -0,0 +1,488 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A LinkedMap data structure that is accessed using key/value
    + * pairs like an ordinary Map, but which guarantees a consistent iteration
    + * order over its entries. The iteration order is either insertion order (the
    + * default) or ordered from most recent to least recent use. By setting a fixed
    + * size, the LRU version of the LinkedMap makes an effective object cache. This
    + * data structure is similar to Java's LinkedHashMap.
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +
    +goog.provide('goog.structs.LinkedMap');
    +
    +goog.require('goog.structs.Map');
    +
    +
    +
    +/**
    + * Class for a LinkedMap datastructure, which combines O(1) map access for
    + * key/value pairs with a linked list for a consistent iteration order. Sample
    + * usage:
    + *
    + * <pre>
    + * var m = new LinkedMap();
    + * m.set('param1', 'A');
    + * m.set('param2', 'B');
    + * m.set('param3', 'C');
    + * alert(m.getKeys()); // param1, param2, param3
    + *
    + * var c = new LinkedMap(5, true);
    + * for (var i = 0; i < 10; i++) {
    + *   c.set('entry' + i, false);
    + * }
    + * alert(c.getKeys()); // entry9, entry8, entry7, entry6, entry5
    + *
    + * c.set('entry5', true);
    + * c.set('entry1', false);
    + * alert(c.getKeys()); // entry1, entry5, entry9, entry8, entry7
    + * </pre>
    + *
    + * @param {number=} opt_maxCount The maximum number of objects to store in the
    + *     LinkedMap. If unspecified or 0, there is no maximum.
    + * @param {boolean=} opt_cache When set, the LinkedMap stores items in order
    + *     from most recently used to least recently used, instead of insertion
    + *     order.
    + * @constructor
    + * @template KEY, VALUE
    + */
    +goog.structs.LinkedMap = function(opt_maxCount, opt_cache) {
    +  /**
    +   * The maximum number of entries to allow, or null if there is no limit.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.maxCount_ = opt_maxCount || null;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.cache_ = !!opt_cache;
    +
    +  /**
    +   * @private {!goog.structs.Map<string,
    +   *     goog.structs.LinkedMap.Node_.<string, VALUE>>}
    +   */
    +  this.map_ = new goog.structs.Map();
    +
    +  this.head_ = new goog.structs.LinkedMap.Node_('', undefined);
    +  this.head_.next = this.head_.prev = this.head_;
    +};
    +
    +
    +/**
    + * Finds a node and updates it to be the most recently used.
    + * @param {string} key The key of the node.
    + * @return {goog.structs.LinkedMap.Node_} The node or null if not found.
    + * @private
    + */
    +goog.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) {
    +  var node = this.map_.get(key);
    +  if (node) {
    +    if (this.cache_) {
    +      node.remove();
    +      this.insert_(node);
    +    }
    +  }
    +  return node;
    +};
    +
    +
    +/**
    + * Retrieves the value for a given key. If this is a caching LinkedMap, the
    + * entry will become the most recently used.
    + * @param {string} key The key to retrieve the value for.
    + * @param {VALUE=} opt_val A default value that will be returned if the key is
    + *     not found, defaults to undefined.
    + * @return {VALUE} The retrieved value.
    + */
    +goog.structs.LinkedMap.prototype.get = function(key, opt_val) {
    +  var node = this.findAndMoveToTop_(key);
    +  return node ? node.value : opt_val;
    +};
    +
    +
    +/**
    + * Retrieves the value for a given key without updating the entry to be the
    + * most recently used.
    + * @param {string} key The key to retrieve the value for.
    + * @param {VALUE=} opt_val A default value that will be returned if the key is
    + *     not found.
    + * @return {VALUE} The retrieved value.
    + */
    +goog.structs.LinkedMap.prototype.peekValue = function(key, opt_val) {
    +  var node = this.map_.get(key);
    +  return node ? node.value : opt_val;
    +};
    +
    +
    +/**
    + * Sets a value for a given key. If this is a caching LinkedMap, this entry
    + * will become the most recently used.
    + * @param {string} key Key with which the specified value is to be associated.
    + * @param {VALUE} value Value to be associated with the specified key.
    + */
    +goog.structs.LinkedMap.prototype.set = function(key, value) {
    +  var node = this.findAndMoveToTop_(key);
    +  if (node) {
    +    node.value = value;
    +  } else {
    +    node = new goog.structs.LinkedMap.Node_(key, value);
    +    this.map_.set(key, node);
    +    this.insert_(node);
    +  }
    +};
    +
    +
    +/**
    + * Returns the value of the first node without making any modifications.
    + * @return {VALUE} The value of the first node or undefined if the map is empty.
    + */
    +goog.structs.LinkedMap.prototype.peek = function() {
    +  return this.head_.next.value;
    +};
    +
    +
    +/**
    + * Returns the value of the last node without making any modifications.
    + * @return {VALUE} The value of the last node or undefined if the map is empty.
    + */
    +goog.structs.LinkedMap.prototype.peekLast = function() {
    +  return this.head_.prev.value;
    +};
    +
    +
    +/**
    + * Removes the first node from the list and returns its value.
    + * @return {VALUE} The value of the popped node, or undefined if the map was
    + *     empty.
    + */
    +goog.structs.LinkedMap.prototype.shift = function() {
    +  return this.popNode_(this.head_.next);
    +};
    +
    +
    +/**
    + * Removes the last node from the list and returns its value.
    + * @return {VALUE} The value of the popped node, or undefined if the map was
    + *     empty.
    + */
    +goog.structs.LinkedMap.prototype.pop = function() {
    +  return this.popNode_(this.head_.prev);
    +};
    +
    +
    +/**
    + * Removes a value from the LinkedMap based on its key.
    + * @param {string} key The key to remove.
    + * @return {boolean} True if the entry was removed, false if the key was not
    + *     found.
    + */
    +goog.structs.LinkedMap.prototype.remove = function(key) {
    +  var node = this.map_.get(key);
    +  if (node) {
    +    this.removeNode(node);
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Removes a node from the {@code LinkedMap}. It can be overridden to do
    + * further cleanup such as disposing of the node value.
    + * @param {!goog.structs.LinkedMap.Node_} node The node to remove.
    + * @protected
    + */
    +goog.structs.LinkedMap.prototype.removeNode = function(node) {
    +  node.remove();
    +  this.map_.remove(node.key);
    +};
    +
    +
    +/**
    + * @return {number} The number of items currently in the LinkedMap.
    + */
    +goog.structs.LinkedMap.prototype.getCount = function() {
    +  return this.map_.getCount();
    +};
    +
    +
    +/**
    + * @return {boolean} True if the cache is empty, false if it contains any items.
    + */
    +goog.structs.LinkedMap.prototype.isEmpty = function() {
    +  return this.map_.isEmpty();
    +};
    +
    +
    +/**
    + * Sets the maximum number of entries allowed in this object, truncating any
    + * excess objects if necessary.
    + * @param {number} maxCount The new maximum number of entries to allow.
    + */
    +goog.structs.LinkedMap.prototype.setMaxCount = function(maxCount) {
    +  this.maxCount_ = maxCount || null;
    +  if (this.maxCount_ != null) {
    +    this.truncate_(this.maxCount_);
    +  }
    +};
    +
    +
    +/**
    + * @return {!Array<string>} The list of the keys in the appropriate order for
    + *     this LinkedMap.
    + */
    +goog.structs.LinkedMap.prototype.getKeys = function() {
    +  return this.map(function(val, key) {
    +    return key;
    +  });
    +};
    +
    +
    +/**
    + * @return {!Array<VALUE>} The list of the values in the appropriate order for
    + *     this LinkedMap.
    + */
    +goog.structs.LinkedMap.prototype.getValues = function() {
    +  return this.map(function(val, key) {
    +    return val;
    +  });
    +};
    +
    +
    +/**
    + * Tests whether a provided value is currently in the LinkedMap. This does not
    + * affect item ordering in cache-style LinkedMaps.
    + * @param {VALUE} value The value to check for.
    + * @return {boolean} Whether the value is in the LinkedMap.
    + */
    +goog.structs.LinkedMap.prototype.contains = function(value) {
    +  return this.some(function(el) {
    +    return el == value;
    +  });
    +};
    +
    +
    +/**
    + * Tests whether a provided key is currently in the LinkedMap. This does not
    + * affect item ordering in cache-style LinkedMaps.
    + * @param {string} key The key to check for.
    + * @return {boolean} Whether the key is in the LinkedMap.
    + */
    +goog.structs.LinkedMap.prototype.containsKey = function(key) {
    +  return this.map_.containsKey(key);
    +};
    +
    +
    +/**
    + * Removes all entries in this object.
    + */
    +goog.structs.LinkedMap.prototype.clear = function() {
    +  this.truncate_(0);
    +};
    +
    +
    +/**
    + * Calls a function on each item in the LinkedMap.
    + *
    + * @see goog.structs.forEach
    + * @param {function(this:T, VALUE, KEY, goog.structs.LinkedMap<KEY,VALUE>)} f
    + * @param {T=} opt_obj The value of "this" inside f.
    + * @template T
    + */
    +goog.structs.LinkedMap.prototype.forEach = function(f, opt_obj) {
    +  for (var n = this.head_.next; n != this.head_; n = n.next) {
    +    f.call(opt_obj, n.value, n.key, this);
    +  }
    +};
    +
    +
    +/**
    + * Calls a function on each item in the LinkedMap and returns the results of
    + * those calls in an array.
    + *
    + * @see goog.structs.map
    + * @param {function(this:T, VALUE, KEY,
    + *         goog.structs.LinkedMap<KEY,VALUE>): RESULT} f
    + *     The function to call for each item. The function takes
    + *     three arguments: the value, the key, and the LinkedMap.
    + * @param {T=} opt_obj The object context to use as "this" for the
    + *     function.
    + * @return {!Array<RESULT>} The results of the function calls for each item in
    + *     the LinkedMap.
    + * @template T,RESULT
    + */
    +goog.structs.LinkedMap.prototype.map = function(f, opt_obj) {
    +  var rv = [];
    +  for (var n = this.head_.next; n != this.head_; n = n.next) {
    +    rv.push(f.call(opt_obj, n.value, n.key, this));
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Calls a function on each item in the LinkedMap and returns true if any of
    + * those function calls returns a true-like value.
    + *
    + * @see goog.structs.some
    + * @param {function(this:T, VALUE, KEY,
    + *         goog.structs.LinkedMap<KEY,VALUE>):boolean} f
    + *     The function to call for each item. The function takes
    + *     three arguments: the value, the key, and the LinkedMap, and returns a
    + *     boolean.
    + * @param {T=} opt_obj The object context to use as "this" for the
    + *     function.
    + * @return {boolean} Whether f evaluates to true for at least one item in the
    + *     LinkedMap.
    + * @template T
    + */
    +goog.structs.LinkedMap.prototype.some = function(f, opt_obj) {
    +  for (var n = this.head_.next; n != this.head_; n = n.next) {
    +    if (f.call(opt_obj, n.value, n.key, this)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Calls a function on each item in the LinkedMap and returns true only if every
    + * function call returns a true-like value.
    + *
    + * @see goog.structs.some
    + * @param {function(this:T, VALUE, KEY,
    + *         goog.structs.LinkedMap<KEY,VALUE>):boolean} f
    + *     The function to call for each item. The function takes
    + *     three arguments: the value, the key, and the Cache, and returns a
    + *     boolean.
    + * @param {T=} opt_obj The object context to use as "this" for the
    + *     function.
    + * @return {boolean} Whether f evaluates to true for every item in the Cache.
    + * @template T
    + */
    +goog.structs.LinkedMap.prototype.every = function(f, opt_obj) {
    +  for (var n = this.head_.next; n != this.head_; n = n.next) {
    +    if (!f.call(opt_obj, n.value, n.key, this)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Appends a node to the list. LinkedMap in cache mode adds new nodes to
    + * the head of the list, otherwise they are appended to the tail. If there is a
    + * maximum size, the list will be truncated if necessary.
    + *
    + * @param {goog.structs.LinkedMap.Node_} node The item to insert.
    + * @private
    + */
    +goog.structs.LinkedMap.prototype.insert_ = function(node) {
    +  if (this.cache_) {
    +    node.next = this.head_.next;
    +    node.prev = this.head_;
    +
    +    this.head_.next = node;
    +    node.next.prev = node;
    +  } else {
    +    node.prev = this.head_.prev;
    +    node.next = this.head_;
    +
    +    this.head_.prev = node;
    +    node.prev.next = node;
    +  }
    +
    +  if (this.maxCount_ != null) {
    +    this.truncate_(this.maxCount_);
    +  }
    +};
    +
    +
    +/**
    + * Removes elements from the LinkedMap if the given count has been exceeded.
    + * In cache mode removes nodes from the tail of the list. Otherwise removes
    + * nodes from the head.
    + * @param {number} count Number of elements to keep.
    + * @private
    + */
    +goog.structs.LinkedMap.prototype.truncate_ = function(count) {
    +  for (var i = this.map_.getCount(); i > count; i--) {
    +    this.removeNode(this.cache_ ? this.head_.prev : this.head_.next);
    +  }
    +};
    +
    +
    +/**
    + * Removes the node from the LinkedMap if it is not the head, and returns
    + * the node's value.
    + * @param {!goog.structs.LinkedMap.Node_} node The item to remove.
    + * @return {VALUE} The value of the popped node.
    + * @private
    + */
    +goog.structs.LinkedMap.prototype.popNode_ = function(node) {
    +  if (this.head_ != node) {
    +    this.removeNode(node);
    +  }
    +  return node.value;
    +};
    +
    +
    +
    +/**
    + * Internal class for a doubly-linked list node containing a key/value pair.
    + * @param {KEY} key The key.
    + * @param {VALUE} value The value.
    + * @constructor
    + * @template KEY, VALUE
    + * @private
    + */
    +goog.structs.LinkedMap.Node_ = function(key, value) {
    +  this.key = key;
    +  this.value = value;
    +};
    +
    +
    +/**
    + * The next node in the list.
    + * @type {!goog.structs.LinkedMap.Node_}
    + */
    +goog.structs.LinkedMap.Node_.prototype.next;
    +
    +
    +/**
    + * The previous node in the list.
    + * @type {!goog.structs.LinkedMap.Node_}
    + */
    +goog.structs.LinkedMap.Node_.prototype.prev;
    +
    +
    +/**
    + * Causes this node to remove itself from the list.
    + */
    +goog.structs.LinkedMap.Node_.prototype.remove = function() {
    +  this.prev.next = this.next;
    +  this.next.prev = this.prev;
    +
    +  delete this.prev;
    +  delete this.next;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.html b/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.html
    new file mode 100644
    index 0000000000..32332581dd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.LinkedMap
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.LinkedMapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.js b/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.js
    new file mode 100644
    index 0000000000..29c7ce964e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/linkedmap_test.js
    @@ -0,0 +1,296 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.LinkedMapTest');
    +goog.setTestOnly('goog.structs.LinkedMapTest');
    +
    +goog.require('goog.structs.LinkedMap');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +function fillLinkedMap(m) {
    +  m.set('a', 0);
    +  m.set('b', 1);
    +  m.set('c', 2);
    +  m.set('d', 3);
    +}
    +
    +var someObj = {};
    +
    +function testLinkedMap() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  assertArrayEquals(['a', 'b', 'c', 'd'], m.getKeys());
    +  assertArrayEquals([0, 1, 2, 3], m.getValues());
    +}
    +
    +function testMaxSizeLinkedMap() {
    +  var m = new goog.structs.LinkedMap(3);
    +  fillLinkedMap(m);
    +
    +  assertArrayEquals(['b', 'c', 'd'], m.getKeys());
    +  assertArrayEquals([1, 2, 3], m.getValues());
    +}
    +
    +function testLruLinkedMap() {
    +  var m = new goog.structs.LinkedMap(undefined, true);
    +  fillLinkedMap(m);
    +
    +  assertArrayEquals(['d', 'c', 'b', 'a'], m.getKeys());
    +  assertArrayEquals([3, 2, 1, 0], m.getValues());
    +
    +  m.get('a');
    +  assertArrayEquals(['a', 'd', 'c', 'b'], m.getKeys());
    +  assertArrayEquals([0, 3, 2, 1], m.getValues());
    +
    +  m.set('b', 4);
    +  assertArrayEquals(['b', 'a', 'd', 'c'], m.getKeys());
    +  assertArrayEquals([4, 0, 3, 2], m.getValues());
    +}
    +
    +function testMaxSizeLruLinkedMap() {
    +  var m = new goog.structs.LinkedMap(3, true);
    +  fillLinkedMap(m);
    +
    +  assertArrayEquals(['d', 'c', 'b'], m.getKeys());
    +  assertArrayEquals([3, 2, 1], m.getValues());
    +
    +  m.get('c');
    +  assertArrayEquals(['c', 'd', 'b'], m.getKeys());
    +  assertArrayEquals([2, 3, 1], m.getValues());
    +
    +  m.set('d', 4);
    +  assertArrayEquals(['d', 'c', 'b'], m.getKeys());
    +  assertArrayEquals([4, 2, 1], m.getValues());
    +}
    +
    +function testGetCount() {
    +  var m = new goog.structs.LinkedMap();
    +  assertEquals(0, m.getCount());
    +  m.set('a', 0);
    +  assertEquals(1, m.getCount());
    +  m.set('a', 1);
    +  assertEquals(1, m.getCount());
    +  m.set('b', 2);
    +  assertEquals(2, m.getCount());
    +  m.remove('a');
    +  assertEquals(1, m.getCount());
    +}
    +
    +function testIsEmpty() {
    +  var m = new goog.structs.LinkedMap();
    +  assertTrue(m.isEmpty());
    +  m.set('a', 0);
    +  assertFalse(m.isEmpty());
    +  m.remove('a');
    +  assertTrue(m.isEmpty());
    +}
    +
    +function testSetMaxCount() {
    +  var m = new goog.structs.LinkedMap(3);
    +  fillLinkedMap(m);
    +  assertEquals(3, m.getCount());
    +
    +  m.setMaxCount(5);
    +  m.set('e', 5);
    +  m.set('f', 6);
    +  m.set('g', 7);
    +  assertEquals(5, m.getCount());
    +
    +  m.setMaxCount(4);
    +  assertEquals(4, m.getCount());
    +
    +  m.setMaxCount(0);
    +  m.set('h', 8);
    +  m.set('i', 9);
    +  m.set('j', 10);
    +  assertEquals(7, m.getCount());
    +}
    +
    +function testClear() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +  m.clear();
    +  assertTrue(m.isEmpty());
    +}
    +
    +function testForEach() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  m.forEach(function(val, key, linkedMap) {
    +    linkedMap.set(key, val * 2);
    +    assertEquals('forEach should run in provided context.', someObj, this);
    +  }, someObj);
    +
    +  assertArrayEquals(['a', 'b', 'c', 'd'], m.getKeys());
    +  assertArrayEquals([0, 2, 4, 6], m.getValues());
    +}
    +
    +function testMap() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  var result = m.map(function(val, key, linkedMap) {
    +    assertEquals('The LinkedMap object should get passed in', m, linkedMap);
    +    assertEquals('map should run in provided context', someObj, this);
    +    return key + val;
    +  }, someObj);
    +
    +  assertArrayEquals(['a0', 'b1', 'c2', 'd3'], result);
    +}
    +
    +function testSome() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  var result = m.some(function(val, key, linkedMap) {
    +    assertEquals('The LinkedMap object should get passed in', m, linkedMap);
    +    assertEquals('map should run in provided context', someObj, this);
    +    return val > 2;
    +  }, someObj);
    +
    +  assertTrue(result);
    +  assertFalse(m.some(function(val) {return val > 3}));
    +
    +  assertTrue(m.some(function(val, key) {return key == 'c';}));
    +  assertFalse(m.some(function(val, key) {return key == 'e';}));
    +}
    +
    +function testEvery() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  var result = m.every(function(val, key, linkedMap) {
    +    assertEquals('The LinkedMap object should get passed in', m, linkedMap);
    +    assertEquals('map should run in provided context', someObj, this);
    +    return val < 5;
    +  }, someObj);
    +
    +  assertTrue(result);
    +  assertFalse(m.every(function(val) {return val < 2}));
    +
    +  assertTrue(m.every(function(val, key) {return key.length == 1;}));
    +  assertFalse(m.every(function(val, key) {return key == 'b';}));
    +}
    +
    +function testPeek() {
    +  var m = new goog.structs.LinkedMap();
    +  assertEquals(undefined, m.peek());
    +  assertEquals(undefined, m.peekLast());
    +
    +  fillLinkedMap(m);
    +  assertEquals(0, m.peek());
    +
    +  m.remove('a');
    +  assertEquals(1, m.peek());
    +
    +  assertEquals(3, m.peekLast());
    +
    +  assertEquals(3, m.peekValue('d'));
    +  assertEquals(1, m.peek());
    +
    +  m.remove('d');
    +  assertEquals(2, m.peekLast());
    +}
    +
    +function testPop() {
    +  var m = new goog.structs.LinkedMap();
    +  assertEquals(undefined, m.shift());
    +  assertEquals(undefined, m.pop());
    +
    +  fillLinkedMap(m);
    +  assertEquals(4, m.getCount());
    +
    +  assertEquals(0, m.shift());
    +  assertEquals(1, m.peek());
    +
    +  assertEquals(3, m.pop());
    +  assertEquals(2, m.peekLast());
    +
    +  assertEquals(2, m.getCount());
    +}
    +
    +function testContains() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  assertTrue(m.contains(2));
    +  assertFalse(m.contains(4));
    +}
    +
    +function testContainsKey() {
    +  var m = new goog.structs.LinkedMap();
    +  fillLinkedMap(m);
    +
    +  assertTrue(m.containsKey('b'));
    +  assertFalse(m.containsKey('elephant'));
    +  assertFalse(m.containsKey('undefined'));
    +}
    +
    +function testRemoveNodeCalls() {
    +  var m = new goog.structs.LinkedMap(1);
    +  m.removeNode = goog.testing.recordFunction(m.removeNode);
    +
    +  m.set('1', 1);
    +  assertEquals('removeNode not called after adding an element', 0,
    +      m.removeNode.getCallCount());
    +  m.set('1', 2);
    +  assertEquals('removeNode not called after updating an element', 0,
    +      m.removeNode.getCallCount());
    +  m.set('2', 2);
    +  assertEquals('removeNode called after adding an overflowing element', 1,
    +      m.removeNode.getCallCount());
    +
    +  m.remove('3');
    +  assertEquals('removeNode not called after removing a non-existing element', 1,
    +      m.removeNode.getCallCount());
    +  m.remove('2');
    +  assertEquals('removeNode called after removing an existing element', 2,
    +      m.removeNode.getCallCount());
    +
    +  m.set('1', 1);
    +  m.clear();
    +  assertEquals('removeNode called after clearing the map', 3,
    +      m.removeNode.getCallCount());
    +  m.clear();
    +  assertEquals('removeNode not called after clearing an empty map', 3,
    +      m.removeNode.getCallCount());
    +
    +  m.set('1', 1);
    +  m.pop();
    +  assertEquals('removeNode called after calling pop', 4,
    +      m.removeNode.getCallCount());
    +  m.pop();
    +  assertEquals('removeNode not called after calling pop on an empty map', 4,
    +      m.removeNode.getCallCount());
    +
    +  m.set('1', 1);
    +  m.shift();
    +  assertEquals('removeNode called after calling shift', 5,
    +      m.removeNode.getCallCount());
    +  m.shift();
    +  assertEquals('removeNode not called after calling shift on an empty map', 5,
    +      m.removeNode.getCallCount());
    +
    +  m.setMaxCount(2);
    +  m.set('1', 1);
    +  m.set('2', 2);
    +  assertEquals('removeNode not called after increasing the maximum map size', 5,
    +      m.removeNode.getCallCount());
    +  m.setMaxCount(1);
    +  assertEquals('removeNode called after decreasing the maximum map size', 6,
    +      m.removeNode.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/map.js b/mapbox-gl/closure-library/closure/goog/structs/map.js
    new file mode 100644
    index 0000000000..330bb7d130
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/map.js
    @@ -0,0 +1,460 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Hash Map.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + *
    + * This file contains an implementation of a Map structure. It implements a lot
    + * of the methods used in goog.structs so those functions work on hashes. This
    + * is best suited for complex key types. For simple keys such as numbers and
    + * strings, and where special names like __proto__ are not a concern, consider
    + * using the lighter-weight utilities in goog.object.
    + */
    +
    +
    +goog.provide('goog.structs.Map');
    +
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Class for Hash Map datastructure.
    + * @param {*=} opt_map Map or Object to initialize the map with.
    + * @param {...*} var_args If 2 or more arguments are present then they
    + *     will be used as key-value pairs.
    + * @constructor
    + * @template K, V
    + */
    +goog.structs.Map = function(opt_map, var_args) {
    +
    +  /**
    +   * Underlying JS object used to implement the map.
    +   * @private {!Object}
    +   */
    +  this.map_ = {};
    +
    +  /**
    +   * An array of keys. This is necessary for two reasons:
    +   *   1. Iterating the keys using for (var key in this.map_) allocates an
    +   *      object for every key in IE which is really bad for IE6 GC perf.
    +   *   2. Without a side data structure, we would need to escape all the keys
    +   *      as that would be the only way we could tell during iteration if the
    +   *      key was an internal key or a property of the object.
    +   *
    +   * This array can contain deleted keys so it's necessary to check the map
    +   * as well to see if the key is still in the map (this doesn't require a
    +   * memory allocation in IE).
    +   * @private {!Array<string>}
    +   */
    +  this.keys_ = [];
    +
    +  /**
    +   * The number of key value pairs in the map.
    +   * @private {number}
    +   */
    +  this.count_ = 0;
    +
    +  /**
    +   * Version used to detect changes while iterating.
    +   * @private {number}
    +   */
    +  this.version_ = 0;
    +
    +  var argLength = arguments.length;
    +
    +  if (argLength > 1) {
    +    if (argLength % 2) {
    +      throw Error('Uneven number of arguments');
    +    }
    +    for (var i = 0; i < argLength; i += 2) {
    +      this.set(arguments[i], arguments[i + 1]);
    +    }
    +  } else if (opt_map) {
    +    this.addAll(/** @type {Object} */ (opt_map));
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The number of key-value pairs in the map.
    + */
    +goog.structs.Map.prototype.getCount = function() {
    +  return this.count_;
    +};
    +
    +
    +/**
    + * Returns the values of the map.
    + * @return {!Array<V>} The values in the map.
    + */
    +goog.structs.Map.prototype.getValues = function() {
    +  this.cleanupKeysArray_();
    +
    +  var rv = [];
    +  for (var i = 0; i < this.keys_.length; i++) {
    +    var key = this.keys_[i];
    +    rv.push(this.map_[key]);
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Returns the keys of the map.
    + * @return {!Array<string>} Array of string values.
    + */
    +goog.structs.Map.prototype.getKeys = function() {
    +  this.cleanupKeysArray_();
    +  return /** @type {!Array<string>} */ (this.keys_.concat());
    +};
    +
    +
    +/**
    + * Whether the map contains the given key.
    + * @param {*} key The key to check for.
    + * @return {boolean} Whether the map contains the key.
    + */
    +goog.structs.Map.prototype.containsKey = function(key) {
    +  return goog.structs.Map.hasKey_(this.map_, key);
    +};
    +
    +
    +/**
    + * Whether the map contains the given value. This is O(n).
    + * @param {V} val The value to check for.
    + * @return {boolean} Whether the map contains the value.
    + */
    +goog.structs.Map.prototype.containsValue = function(val) {
    +  for (var i = 0; i < this.keys_.length; i++) {
    +    var key = this.keys_[i];
    +    if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Whether this map is equal to the argument map.
    + * @param {goog.structs.Map} otherMap The map against which to test equality.
    + * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
    + *     to test equality of values. If not specified, this will test whether
    + *     the values contained in each map are identical objects.
    + * @return {boolean} Whether the maps are equal.
    + */
    +goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
    +  if (this === otherMap) {
    +    return true;
    +  }
    +
    +  if (this.count_ != otherMap.getCount()) {
    +    return false;
    +  }
    +
    +  var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
    +
    +  this.cleanupKeysArray_();
    +  for (var key, i = 0; key = this.keys_[i]; i++) {
    +    if (!equalityFn(this.get(key), otherMap.get(key))) {
    +      return false;
    +    }
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Default equality test for values.
    + * @param {*} a The first value.
    + * @param {*} b The second value.
    + * @return {boolean} Whether a and b reference the same object.
    + */
    +goog.structs.Map.defaultEquals = function(a, b) {
    +  return a === b;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the map is empty.
    + */
    +goog.structs.Map.prototype.isEmpty = function() {
    +  return this.count_ == 0;
    +};
    +
    +
    +/**
    + * Removes all key-value pairs from the map.
    + */
    +goog.structs.Map.prototype.clear = function() {
    +  this.map_ = {};
    +  this.keys_.length = 0;
    +  this.count_ = 0;
    +  this.version_ = 0;
    +};
    +
    +
    +/**
    + * Removes a key-value pair based on the key. This is O(logN) amortized due to
    + * updating the keys array whenever the count becomes half the size of the keys
    + * in the keys array.
    + * @param {*} key  The key to remove.
    + * @return {boolean} Whether object was removed.
    + */
    +goog.structs.Map.prototype.remove = function(key) {
    +  if (goog.structs.Map.hasKey_(this.map_, key)) {
    +    delete this.map_[key];
    +    this.count_--;
    +    this.version_++;
    +
    +    // clean up the keys array if the threshhold is hit
    +    if (this.keys_.length > 2 * this.count_) {
    +      this.cleanupKeysArray_();
    +    }
    +
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Cleans up the temp keys array by removing entries that are no longer in the
    + * map.
    + * @private
    + */
    +goog.structs.Map.prototype.cleanupKeysArray_ = function() {
    +  if (this.count_ != this.keys_.length) {
    +    // First remove keys that are no longer in the map.
    +    var srcIndex = 0;
    +    var destIndex = 0;
    +    while (srcIndex < this.keys_.length) {
    +      var key = this.keys_[srcIndex];
    +      if (goog.structs.Map.hasKey_(this.map_, key)) {
    +        this.keys_[destIndex++] = key;
    +      }
    +      srcIndex++;
    +    }
    +    this.keys_.length = destIndex;
    +  }
    +
    +  if (this.count_ != this.keys_.length) {
    +    // If the count still isn't correct, that means we have duplicates. This can
    +    // happen when the same key is added and removed multiple times. Now we have
    +    // to allocate one extra Object to remove the duplicates. This could have
    +    // been done in the first pass, but in the common case, we can avoid
    +    // allocating an extra object by only doing this when necessary.
    +    var seen = {};
    +    var srcIndex = 0;
    +    var destIndex = 0;
    +    while (srcIndex < this.keys_.length) {
    +      var key = this.keys_[srcIndex];
    +      if (!(goog.structs.Map.hasKey_(seen, key))) {
    +        this.keys_[destIndex++] = key;
    +        seen[key] = 1;
    +      }
    +      srcIndex++;
    +    }
    +    this.keys_.length = destIndex;
    +  }
    +};
    +
    +
    +/**
    + * Returns the value for the given key.  If the key is not found and the default
    + * value is not given this will return {@code undefined}.
    + * @param {*} key The key to get the value for.
    + * @param {DEFAULT=} opt_val The value to return if no item is found for the
    + *     given key, defaults to undefined.
    + * @return {V|DEFAULT} The value for the given key.
    + * @template DEFAULT
    + */
    +goog.structs.Map.prototype.get = function(key, opt_val) {
    +  if (goog.structs.Map.hasKey_(this.map_, key)) {
    +    return this.map_[key];
    +  }
    +  return opt_val;
    +};
    +
    +
    +/**
    + * Adds a key-value pair to the map.
    + * @param {*} key The key.
    + * @param {V} value The value to add.
    + * @return {*} Some subclasses return a value.
    + */
    +goog.structs.Map.prototype.set = function(key, value) {
    +  if (!(goog.structs.Map.hasKey_(this.map_, key))) {
    +    this.count_++;
    +    this.keys_.push(key);
    +    // Only change the version if we add a new key.
    +    this.version_++;
    +  }
    +  this.map_[key] = value;
    +};
    +
    +
    +/**
    + * Adds multiple key-value pairs from another goog.structs.Map or Object.
    + * @param {Object} map  Object containing the data to add.
    + */
    +goog.structs.Map.prototype.addAll = function(map) {
    +  var keys, values;
    +  if (map instanceof goog.structs.Map) {
    +    keys = map.getKeys();
    +    values = map.getValues();
    +  } else {
    +    keys = goog.object.getKeys(map);
    +    values = goog.object.getValues(map);
    +  }
    +  // we could use goog.array.forEach here but I don't want to introduce that
    +  // dependency just for this.
    +  for (var i = 0; i < keys.length; i++) {
    +    this.set(keys[i], values[i]);
    +  }
    +};
    +
    +
    +/**
    + * Calls the given function on each entry in the map.
    + * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
    + * @param {T=} opt_obj The value of "this" inside f.
    + * @template T
    + */
    +goog.structs.Map.prototype.forEach = function(f, opt_obj) {
    +  var keys = this.getKeys();
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = keys[i];
    +    var value = this.get(key);
    +    f.call(opt_obj, value, key, this);
    +  }
    +};
    +
    +
    +/**
    + * Clones a map and returns a new map.
    + * @return {!goog.structs.Map} A new map with the same key-value pairs.
    + */
    +goog.structs.Map.prototype.clone = function() {
    +  return new goog.structs.Map(this);
    +};
    +
    +
    +/**
    + * Returns a new map in which all the keys and values are interchanged
    + * (keys become values and values become keys). If multiple keys map to the
    + * same value, the chosen transposed value is implementation-dependent.
    + *
    + * It acts very similarly to {goog.object.transpose(Object)}.
    + *
    + * @return {!goog.structs.Map} The transposed map.
    + */
    +goog.structs.Map.prototype.transpose = function() {
    +  var transposed = new goog.structs.Map();
    +  for (var i = 0; i < this.keys_.length; i++) {
    +    var key = this.keys_[i];
    +    var value = this.map_[key];
    +    transposed.set(value, key);
    +  }
    +
    +  return transposed;
    +};
    +
    +
    +/**
    + * @return {!Object} Object representation of the map.
    + */
    +goog.structs.Map.prototype.toObject = function() {
    +  this.cleanupKeysArray_();
    +  var obj = {};
    +  for (var i = 0; i < this.keys_.length; i++) {
    +    var key = this.keys_[i];
    +    obj[key] = this.map_[key];
    +  }
    +  return obj;
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the keys in the map.  Removal of keys
    + * while iterating might have undesired side effects.
    + * @return {!goog.iter.Iterator} An iterator over the keys in the map.
    + */
    +goog.structs.Map.prototype.getKeyIterator = function() {
    +  return this.__iterator__(true);
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the values in the map.  Removal of
    + * keys while iterating might have undesired side effects.
    + * @return {!goog.iter.Iterator} An iterator over the values in the map.
    + */
    +goog.structs.Map.prototype.getValueIterator = function() {
    +  return this.__iterator__(false);
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the values or the keys in the map.
    + * This throws an exception if the map was mutated since the iterator was
    + * created.
    + * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
    + *     over the values.  The default value is false.
    + * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
    + */
    +goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
    +  // Clean up keys to minimize the risk of iterating over dead keys.
    +  this.cleanupKeysArray_();
    +
    +  var i = 0;
    +  var keys = this.keys_;
    +  var map = this.map_;
    +  var version = this.version_;
    +  var selfObj = this;
    +
    +  var newIter = new goog.iter.Iterator;
    +  newIter.next = function() {
    +    while (true) {
    +      if (version != selfObj.version_) {
    +        throw Error('The map has changed since the iterator was created');
    +      }
    +      if (i >= keys.length) {
    +        throw goog.iter.StopIteration;
    +      }
    +      var key = keys[i++];
    +      return opt_keys ? key : map[key];
    +    }
    +  };
    +  return newIter;
    +};
    +
    +
    +/**
    + * Safe way to test for hasOwnProperty.  It even allows testing for
    + * 'hasOwnProperty'.
    + * @param {Object} obj The object to test for presence of the given key.
    + * @param {*} key The key to check for.
    + * @return {boolean} Whether the object has the key.
    + * @private
    + */
    +goog.structs.Map.hasKey_ = function(obj, key) {
    +  return Object.prototype.hasOwnProperty.call(obj, key);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/map_test.html b/mapbox-gl/closure-library/closure/goog/structs/map_test.html
    new file mode 100644
    index 0000000000..c60db95b46
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/map_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Map
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.MapTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/map_test.js b/mapbox-gl/closure-library/closure/goog/structs/map_test.js
    new file mode 100644
    index 0000000000..daa8850cd7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/map_test.js
    @@ -0,0 +1,426 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.MapTest');
    +goog.setTestOnly('goog.structs.MapTest');
    +
    +goog.require('goog.iter');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.jsunit');
    +
    +function stringifyMap(m) {
    +  var keys = goog.structs.getKeys(m);
    +  var s = '';
    +  for (var i = 0; i < keys.length; i++) {
    +    s += keys[i] + m[keys[i]];
    +  }
    +  return s;
    +}
    +
    +function getMap() {
    +  var m = new goog.structs.Map;
    +  m.set('a', 0);
    +  m.set('b', 1);
    +  m.set('c', 2);
    +  m.set('d', 3);
    +  return m;
    +}
    +
    +function testGetCount() {
    +  var m = getMap();
    +  assertEquals('count, should be 4', m.getCount(), 4);
    +  m.remove('d');
    +  assertEquals('count, should be 3', m.getCount(), 3);
    +}
    +
    +function testKeys() {
    +  var m = getMap();
    +  assertEquals('getKeys, The keys should be a,b,c', m.getKeys().join(','),
    +      'a,b,c,d');
    +}
    +
    +function testValues() {
    +  var m = getMap();
    +  assertEquals('getValues, The values should be 0,1,2',
    +      m.getValues().join(','), '0,1,2,3');
    +}
    +
    +function testContainsKey() {
    +  var m = getMap();
    +  assertTrue("containsKey, Should contain the 'a' key", m.containsKey('a'));
    +  assertFalse("containsKey, Should not contain the 'e' key",
    +      m.containsKey('e'));
    +}
    +
    +function testClear() {
    +  var m = getMap();
    +  m.clear();
    +  assertTrue('cleared so it should be empty', m.isEmpty());
    +  assertTrue("cleared so it should not contain 'a' key", !m.containsKey('a'));
    +}
    +
    +function testAddAll() {
    +  var m = new goog.structs.Map;
    +  m.addAll({a: 0, b: 1, c: 2, d: 3});
    +  assertTrue('addAll so it should not be empty', !m.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", m.containsKey('c'));
    +
    +  var m2 = new goog.structs.Map;
    +  m2.addAll(m);
    +  assertTrue('addAll so it should not be empty', !m2.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", m2.containsKey('c'));
    +}
    +
    +function testConstructor() {
    +  var m = getMap();
    +  var m2 = new goog.structs.Map(m);
    +  assertTrue('constr with Map so it should not be empty', !m2.isEmpty());
    +  assertTrue("constr with Map so it should contain 'c' key",
    +      m2.containsKey('c'));
    +}
    +
    +
    +function testConstructorWithVarArgs() {
    +  var m = new goog.structs.Map('a', 1);
    +  assertTrue('constr with var_args so it should not be empty', !m.isEmpty());
    +  assertEquals('constr with var_args', 1, m.get('a'));
    +
    +  m = new goog.structs.Map('a', 1, 'b', 2);
    +  assertTrue('constr with var_args so it should not be empty', !m.isEmpty());
    +  assertEquals('constr with var_args', 1, m.get('a'));
    +  assertEquals('constr with var_args', 2, m.get('b'));
    +
    +  assertThrows('Odd number of arguments is not allowed', function() {
    +    var m = new goog.structs.Map('a', 1, 'b');
    +  });
    +}
    +
    +function testClone() {
    +  var m = getMap();
    +  var m2 = m.clone();
    +  assertTrue('clone so it should not be empty', !m2.isEmpty());
    +  assertTrue("clone so it should contain 'c' key", m2.containsKey('c'));
    +}
    +
    +
    +function testRemove() {
    +  var m = new goog.structs.Map();
    +  for (var i = 0; i < 1000; i++) {
    +    m.set(i, 'foo');
    +  }
    +
    +  for (var i = 0; i < 1000; i++) {
    +    assertTrue(m.keys_.length <= 2 * m.getCount());
    +    m.remove(i);
    +  }
    +  assertTrue(m.isEmpty());
    +  assertEquals('', m.getKeys().join(''));
    +}
    +
    +
    +function testForEach() {
    +  var m = getMap();
    +  var s = '';
    +  goog.structs.forEach(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    s += key + val;
    +  });
    +  assertEquals(s, 'a0b1c2d3');
    +}
    +
    +function testFilter() {
    +  var m = getMap();
    +
    +  var m2 = goog.structs.filter(m, function(val, key, m3) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m3);
    +    return val > 1;
    +  });
    +  assertEquals(stringifyMap(m2), 'c2d3');
    +}
    +
    +
    +function testMap() {
    +  var m = getMap();
    +  var m2 = goog.structs.map(m, function(val, key, m3) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m3);
    +    return val * val;
    +  });
    +  assertEquals(stringifyMap(m2), 'a0b1c4d9');
    +}
    +
    +function testSome() {
    +  var m = getMap();
    +  var b = goog.structs.some(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 1;
    +  });
    +  assertTrue(b);
    +  var b = goog.structs.some(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 100;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testEvery() {
    +  var m = getMap();
    +  var b = goog.structs.every(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val >= 0;
    +  });
    +  assertTrue(b);
    +  b = goog.structs.every(m, function(val, key, m2) {
    +    assertNotUndefined(key);
    +    assertEquals(m, m2);
    +    return val > 1;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testContainsValue() {
    +  var m = getMap();
    +  assertTrue(m.containsValue(3));
    +  assertFalse(m.containsValue(4));
    +}
    +
    +function testObjectProperties() {
    +  var m = new goog.structs.Map;
    +
    +  assertEquals(m.get('toString'), undefined);
    +  assertEquals(m.get('valueOf'), undefined);
    +  assertEquals(m.get('eval'), undefined);
    +  assertEquals(m.get('toSource'), undefined);
    +  assertEquals(m.get('prototype'), undefined);
    +  assertEquals(m.get(':foo'), undefined);
    +
    +  m.set('toString', 'once');
    +  m.set('valueOf', 'upon');
    +  m.set('eval', 'a');
    +  m.set('toSource', 'midnight');
    +  m.set('prototype', 'dreary');
    +  m.set('hasOwnProperty', 'dark');
    +  m.set(':foo', 'happy');
    +
    +  assertEquals(m.get('toString'), 'once');
    +  assertEquals(m.get('valueOf'), 'upon');
    +  assertEquals(m.get('eval'), 'a');
    +  assertEquals(m.get('toSource'), 'midnight');
    +  assertEquals(m.get('prototype'), 'dreary');
    +  assertEquals(m.get('hasOwnProperty'), 'dark');
    +  assertEquals(m.get(':foo'), 'happy');
    +
    +  var keys = m.getKeys().join(',');
    +  assertEquals(keys,
    +      'toString,valueOf,eval,toSource,prototype,hasOwnProperty,:foo');
    +
    +  var values = m.getValues().join(',');
    +  assertEquals(values, 'once,upon,a,midnight,dreary,dark,happy');
    +}
    +
    +function testDuplicateKeys() {
    +  var m = new goog.structs.Map;
    +
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +  m.set('e', 5);
    +  m.set('f', 6);
    +  assertEquals(6, m.getKeys().length);
    +  m.set('foo', 1);
    +  assertEquals(7, m.getKeys().length);
    +  m.remove('foo');
    +  assertEquals(6, m.getKeys().length);
    +  m.set('foo', 2);
    +  assertEquals(7, m.getKeys().length);
    +  m.remove('foo');
    +  m.set('foo', 3);
    +  m.remove('foo');
    +  m.set('foo', 4);
    +  assertEquals(7, m.getKeys().length);
    +}
    +
    +function testGetKeyIterator() {
    +  var m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +  m.set('e', 5);
    +
    +  var iter = m.getKeyIterator();
    +  assertEquals('Should contain the keys', 'abcde', goog.iter.join(iter, ''));
    +
    +  m.remove('b');
    +  m.remove('d');
    +  iter = m.getKeyIterator();
    +  assertEquals('Should not contain the removed keys',
    +               'ace', goog.iter.join(iter, ''));
    +}
    +
    +function testGetValueIterator() {
    +  var m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +  m.set('e', 5);
    +
    +  var iter = m.getValueIterator();
    +  assertEquals('Should contain the values', '12345', goog.iter.join(iter, ''));
    +
    +  m.remove('b');
    +  m.remove('d');
    +  iter = m.getValueIterator();
    +  assertEquals('Should not contain the removed keys',
    +               '135', goog.iter.join(iter, ''));
    +}
    +
    +function testDefaultIterator() {
    +  // The default iterator should behave like the value iterator
    +
    +  var m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +  m.set('e', 5);
    +
    +  assertEquals('Should contain the values', '12345', goog.iter.join(m, ''));
    +
    +  m.remove('b');
    +  m.remove('d');
    +  assertEquals('Should not contain the removed keys',
    +               '135', goog.iter.join(m, ''));
    +}
    +
    +function testMutatedIterator() {
    +  var message = 'The map has changed since the iterator was created';
    +
    +  var m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +
    +  var iter = m.getValueIterator();
    +  m.set('e', 5);
    +  var ex = assertThrows('Expected an exception since the map has changed',
    +      function() {
    +        iter.next();
    +      });
    +  assertEquals(message, ex.message);
    +
    +  m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +
    +  iter = m.getValueIterator();
    +  m.remove('d');
    +  var ex = assertThrows('Expected an exception since the map has changed',
    +      function() {
    +        iter.next();
    +      });
    +  assertEquals(message, ex.message);
    +
    +  m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +
    +  iter = m.getValueIterator();
    +  m.set('d', 5);
    +  iter.next();
    +  // Changing an existing value is OK.
    +  iter.next();
    +}
    +
    +function testTranspose() {
    +  var m = new goog.structs.Map;
    +  m.set('a', 1);
    +  m.set('b', 2);
    +  m.set('c', 3);
    +  m.set('d', 4);
    +  m.set('e', 5);
    +
    +  var transposed = m.transpose();
    +  assertEquals('Should contain the keys', 'abcde',
    +      goog.iter.join(transposed, ''));
    +}
    +
    +function testToObject() {
    +  Object.prototype.b = 0;
    +  try {
    +    var map = new goog.structs.Map();
    +    map.set('a', 0);
    +    var obj = map.toObject();
    +    assertTrue('object representation has key "a"', obj.hasOwnProperty('a'));
    +    assertFalse('object representation does not have key "b"',
    +        obj.hasOwnProperty('b'));
    +    assertEquals('value for key "a"', 0, obj['a']);
    +  } finally {
    +    delete Object.prototype.b;
    +  }
    +}
    +
    +function testEqualsWithSameObject() {
    +  var map1 = getMap();
    +  assertTrue('maps are the same object', map1.equals(map1));
    +}
    +
    +function testEqualsWithDifferentSizeMaps() {
    +  var map1 = getMap();
    +  var map2 = new goog.structs.Map();
    +
    +  assertFalse('maps are different sizes', map1.equals(map2));
    +}
    +
    +function testEqualsWithDefaultEqualityFn() {
    +  var map1 = new goog.structs.Map();
    +  var map2 = new goog.structs.Map();
    +
    +  assertTrue('maps are both empty', map1.equals(map2));
    +
    +  map1 = getMap();
    +  map2 = getMap();
    +  assertTrue('maps are the same', map1.equals(map2));
    +
    +  map2.set('d', '3');
    +  assertFalse('maps have 3 and \'3\'', map1.equals(map2));
    +}
    +
    +function testEqualsWithCustomEqualityFn() {
    +  var map1 = new goog.structs.Map();
    +  var map2 = new goog.structs.Map();
    +
    +  map1.set('a', 0);
    +  map1.set('b', 1);
    +
    +  map2.set('a', '0');
    +  map2.set('b', '1');
    +
    +  var equalsFn = function(a, b) { return a == b };
    +
    +  assertTrue('maps are equal with ==', map1.equals(map2, equalsFn));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/node.js b/mapbox-gl/closure-library/closure/goog/structs/node.js
    new file mode 100644
    index 0000000000..3e1d1acfc8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/node.js
    @@ -0,0 +1,73 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generic immutable node object to be used in collections.
    + *
    + */
    +
    +
    +goog.provide('goog.structs.Node');
    +
    +
    +
    +/**
    + * A generic immutable node. This can be used in various collections that
    + * require a node object for its item (such as a heap).
    + * @param {K} key Key.
    + * @param {V} value Value.
    + * @constructor
    + * @template K, V
    + */
    +goog.structs.Node = function(key, value) {
    +  /**
    +   * The key.
    +   * @private {K}
    +   */
    +  this.key_ = key;
    +
    +  /**
    +   * The value.
    +   * @private {V}
    +   */
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Gets the key.
    + * @return {K} The key.
    + */
    +goog.structs.Node.prototype.getKey = function() {
    +  return this.key_;
    +};
    +
    +
    +/**
    + * Gets the value.
    + * @return {V} The value.
    + */
    +goog.structs.Node.prototype.getValue = function() {
    +  return this.value_;
    +};
    +
    +
    +/**
    + * Clones a node and returns a new node.
    + * @return {!goog.structs.Node<K, V>} A new goog.structs.Node with the same
    + *     key value pair.
    + */
    +goog.structs.Node.prototype.clone = function() {
    +  return new goog.structs.Node(this.key_, this.value_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/pool.js b/mapbox-gl/closure-library/closure/goog/structs/pool.js
    new file mode 100644
    index 0000000000..5b6e4ccdc2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/pool.js
    @@ -0,0 +1,376 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Pool.
    + *
    + *
    + * A generic class for handling pools of objects.
    + * When an object is released, it is attempted to be reused.
    + */
    +
    +
    +goog.provide('goog.structs.Pool');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.structs.Queue');
    +goog.require('goog.structs.Set');
    +
    +
    +
    +/**
    + * A generic pool class. If min is greater than max, an error is thrown.
    + * @param {number=} opt_minCount Min. number of objects (Default: 1).
    + * @param {number=} opt_maxCount Max. number of objects (Default: 10).
    + * @constructor
    + * @extends {goog.Disposable}
    + * @template T
    + */
    +goog.structs.Pool = function(opt_minCount, opt_maxCount) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Minimum number of objects allowed
    +   * @private {number}
    +   */
    +  this.minCount_ = opt_minCount || 0;
    +
    +  /**
    +   * Maximum number of objects allowed
    +   * @private {number}
    +   */
    +  this.maxCount_ = opt_maxCount || 10;
    +
    +  // Make sure that the max and min constraints are valid.
    +  if (this.minCount_ > this.maxCount_) {
    +    throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
    +  }
    +
    +  /**
    +   * Set used to store objects that are currently in the pool and available
    +   * to be used.
    +   * @private {goog.structs.Queue<T>}
    +   */
    +  this.freeQueue_ = new goog.structs.Queue();
    +
    +  /**
    +   * Set used to store objects that are currently in the pool and in use.
    +   * @private {goog.structs.Set<T>}
    +   */
    +  this.inUseSet_ = new goog.structs.Set();
    +
    +  /**
    +   * The minimum delay between objects being made available, in milliseconds. If
    +   * this is 0, no minimum delay is enforced.
    +   * @protected {number}
    +   */
    +  this.delay = 0;
    +
    +  /**
    +   * The time of the last object being made available, in milliseconds since the
    +   * epoch (i.e., the result of Date#toTime). If this is null, no access has
    +   * occurred yet.
    +   * @protected {number?}
    +   */
    +  this.lastAccess = null;
    +
    +  // Make sure that the minCount constraint is satisfied.
    +  this.adjustForMinMax();
    +
    +
    +  // TODO(user): Remove once JSCompiler's undefined properties warnings
    +  // don't error for guarded properties.
    +  var magicProps = {canBeReused: 0};
    +};
    +goog.inherits(goog.structs.Pool, goog.Disposable);
    +
    +
    +/**
    + * Error to throw when the max/min constraint is attempted to be invalidated.
    + * I.e., when it is attempted for maxCount to be less than minCount.
    + * @type {string}
    + * @private
    + */
    +goog.structs.Pool.ERROR_MIN_MAX_ =
    +    '[goog.structs.Pool] Min can not be greater than max';
    +
    +
    +/**
    + * Error to throw when the Pool is attempted to be disposed and it is asked to
    + * make sure that there are no objects that are in use (i.e., haven't been
    + * released).
    + * @type {string}
    + * @private
    + */
    +goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_ =
    +    '[goog.structs.Pool] Objects not released';
    +
    +
    +/**
    + * Sets the minimum count of the pool.
    + * If min is greater than the max count of the pool, an error is thrown.
    + * @param {number} min The minimum count of the pool.
    + */
    +goog.structs.Pool.prototype.setMinimumCount = function(min) {
    +  // Check count constraints.
    +  if (min > this.maxCount_) {
    +    throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
    +  }
    +  this.minCount_ = min;
    +
    +  // Adjust the objects in the pool as needed.
    +  this.adjustForMinMax();
    +};
    +
    +
    +/**
    + * Sets the maximum count of the pool.
    + * If max is less than the max count of the pool, an error is thrown.
    + * @param {number} max The maximum count of the pool.
    + */
    +goog.structs.Pool.prototype.setMaximumCount = function(max) {
    +  // Check count constraints.
    +  if (max < this.minCount_) {
    +    throw Error(goog.structs.Pool.ERROR_MIN_MAX_);
    +  }
    +  this.maxCount_ = max;
    +
    +  // Adjust the objects in the pool as needed.
    +  this.adjustForMinMax();
    +};
    +
    +
    +/**
    + * Sets the minimum delay between objects being returned by getObject, in
    + * milliseconds. This defaults to zero, meaning that no minimum delay is
    + * enforced and objects may be used as soon as they're available.
    + * @param {number} delay The minimum delay, in milliseconds.
    + */
    +goog.structs.Pool.prototype.setDelay = function(delay) {
    +  this.delay = delay;
    +};
    +
    +
    +/**
    + * @return {T|undefined} A new object from the pool if there is one available,
    + *     otherwise undefined.
    + */
    +goog.structs.Pool.prototype.getObject = function() {
    +  var time = goog.now();
    +  if (goog.isDefAndNotNull(this.lastAccess) &&
    +      time - this.lastAccess < this.delay) {
    +    return undefined;
    +  }
    +
    +  var obj = this.removeFreeObject_();
    +  if (obj) {
    +    this.lastAccess = time;
    +    this.inUseSet_.add(obj);
    +  }
    +
    +  return obj;
    +};
    +
    +
    +/**
    + * Returns an object to the pool of available objects so that it can be reused.
    + * @param {T} obj The object to return to the pool of free objects.
    + * @return {boolean} Whether the object was found in the Pool's set of in-use
    + *     objects (in other words, whether any action was taken).
    + */
    +goog.structs.Pool.prototype.releaseObject = function(obj) {
    +  if (this.inUseSet_.remove(obj)) {
    +    this.addFreeObject(obj);
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Removes a free object from the collection of objects that are free so that it
    + * can be used.
    + *
    + * NOTE: This method does not mark the returned object as in use.
    + *
    + * @return {T|undefined} The object removed from the free collection, if there
    + *     is one available. Otherwise, undefined.
    + * @private
    + */
    +goog.structs.Pool.prototype.removeFreeObject_ = function() {
    +  var obj;
    +  while (this.getFreeCount() > 0) {
    +    obj = this.freeQueue_.dequeue();
    +
    +    if (!this.objectCanBeReused(obj)) {
    +      this.adjustForMinMax();
    +    } else {
    +      break;
    +    }
    +  }
    +
    +  if (!obj && this.getCount() < this.maxCount_) {
    +    obj = this.createObject();
    +  }
    +
    +  return obj;
    +};
    +
    +
    +/**
    + * Adds an object to the collection of objects that are free. If the object can
    + * not be added, then it is disposed.
    + *
    + * @param {T} obj The object to add to collection of free objects.
    + */
    +goog.structs.Pool.prototype.addFreeObject = function(obj) {
    +  this.inUseSet_.remove(obj);
    +  if (this.objectCanBeReused(obj) && this.getCount() < this.maxCount_) {
    +    this.freeQueue_.enqueue(obj);
    +  } else {
    +    this.disposeObject(obj);
    +  }
    +};
    +
    +
    +/**
    + * Adjusts the objects held in the pool to be within the min/max constraints.
    + *
    + * NOTE: It is possible that the number of objects in the pool will still be
    + * greater than the maximum count of objects allowed. This will be the case
    + * if no more free objects can be disposed of to get below the minimum count
    + * (i.e., all objects are in use).
    + */
    +goog.structs.Pool.prototype.adjustForMinMax = function() {
    +  var freeQueue = this.freeQueue_;
    +
    +  // Make sure the at least the minimum number of objects are created.
    +  while (this.getCount() < this.minCount_) {
    +    freeQueue.enqueue(this.createObject());
    +  }
    +
    +  // Make sure no more than the maximum number of objects are created.
    +  while (this.getCount() > this.maxCount_ && this.getFreeCount() > 0) {
    +    this.disposeObject(freeQueue.dequeue());
    +  }
    +};
    +
    +
    +/**
    + * Should be overridden by sub-classes to return an instance of the object type
    + * that is expected in the pool.
    + * @return {T} The created object.
    + */
    +goog.structs.Pool.prototype.createObject = function() {
    +  return {};
    +};
    +
    +
    +/**
    + * Should be overridden to dispose of an object. Default implementation is to
    + * remove all its members, which should render it useless. Calls the object's
    + * {@code dispose()} method, if available.
    + * @param {T} obj The object to dispose.
    + */
    +goog.structs.Pool.prototype.disposeObject = function(obj) {
    +  if (typeof obj.dispose == 'function') {
    +    obj.dispose();
    +  } else {
    +    for (var i in obj) {
    +      obj[i] = null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Should be overridden to determine whether an object has become unusable and
    + * should not be returned by getObject(). Calls the object's
    + * {@code canBeReused()}  method, if available.
    + * @param {T} obj The object to test.
    + * @return {boolean} Whether the object can be reused.
    + */
    +goog.structs.Pool.prototype.objectCanBeReused = function(obj) {
    +  if (typeof obj.canBeReused == 'function') {
    +    return obj.canBeReused();
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the given object is in the pool.
    + * @param {T} obj The object to check the pool for.
    + * @return {boolean} Whether the pool contains the object.
    + */
    +goog.structs.Pool.prototype.contains = function(obj) {
    +  return this.freeQueue_.contains(obj) || this.inUseSet_.contains(obj);
    +};
    +
    +
    +/**
    + * Returns the number of objects currently in the pool.
    + * @return {number} Number of objects currently in the pool.
    + */
    +goog.structs.Pool.prototype.getCount = function() {
    +  return this.freeQueue_.getCount() + this.inUseSet_.getCount();
    +};
    +
    +
    +/**
    + * Returns the number of objects currently in use in the pool.
    + * @return {number} Number of objects currently in use in the pool.
    + */
    +goog.structs.Pool.prototype.getInUseCount = function() {
    +  return this.inUseSet_.getCount();
    +};
    +
    +
    +/**
    + * Returns the number of objects currently free in the pool.
    + * @return {number} Number of objects currently free in the pool.
    + */
    +goog.structs.Pool.prototype.getFreeCount = function() {
    +  return this.freeQueue_.getCount();
    +};
    +
    +
    +/**
    + * Determines if the pool contains no objects.
    + * @return {boolean} Whether the pool contains no objects.
    + */
    +goog.structs.Pool.prototype.isEmpty = function() {
    +  return this.freeQueue_.isEmpty() && this.inUseSet_.isEmpty();
    +};
    +
    +
    +/**
    + * Disposes of the pool and all objects currently held in the pool.
    + * @override
    + * @protected
    + */
    +goog.structs.Pool.prototype.disposeInternal = function() {
    +  goog.structs.Pool.superClass_.disposeInternal.call(this);
    +  if (this.getInUseCount() > 0) {
    +    throw Error(goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_);
    +  }
    +  delete this.inUseSet_;
    +
    +  // Call disposeObject on each object held by the pool.
    +  var freeQueue = this.freeQueue_;
    +  while (!freeQueue.isEmpty()) {
    +    this.disposeObject(freeQueue.dequeue());
    +  }
    +  delete this.freeQueue_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/pool_test.html b/mapbox-gl/closure-library/closure/goog/structs/pool_test.html
    new file mode 100644
    index 0000000000..439845870b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/pool_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Pool
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.PoolTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/pool_test.js b/mapbox-gl/closure-library/closure/goog/structs/pool_test.js
    new file mode 100644
    index 0000000000..c1b055800a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/pool_test.js
    @@ -0,0 +1,285 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.PoolTest');
    +goog.setTestOnly('goog.structs.PoolTest');
    +
    +goog.require('goog.structs.Pool');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +// Implementation of the Pool class with isObjectDead() always returning TRUE,
    +// so that the the Pool will not reuse any objects.
    +function NoObjectReusePool(opt_min, opt_max) {
    +  goog.structs.Pool.call(this, opt_min, opt_max);
    +}
    +goog.inherits(NoObjectReusePool, goog.structs.Pool);
    +
    +NoObjectReusePool.prototype.objectCanBeReused = function(obj) {
    +  return false;
    +};
    +
    +
    +function testExceedMax1() {
    +  var p = new goog.structs.Pool(0, 3);
    +  var obj1 = p.getObject();
    +  var obj2 = p.getObject();
    +  var obj3 = p.getObject();
    +  var obj4 = p.getObject();
    +  var obj5 = p.getObject();
    +
    +  assertNotUndefined(obj1);
    +  assertNotUndefined(obj2);
    +  assertNotUndefined(obj3);
    +  assertUndefined(obj4);
    +  assertUndefined(obj5);
    +}
    +
    +
    +function testExceedMax2() {
    +  var p = new goog.structs.Pool(0, 1);
    +  var obj1 = p.getObject();
    +  var obj2 = p.getObject();
    +  var obj3 = p.getObject();
    +  var obj4 = p.getObject();
    +  var obj5 = p.getObject();
    +
    +  assertNotUndefined(obj1);
    +  assertUndefined(obj2);
    +  assertUndefined(obj3);
    +  assertUndefined(obj4);
    +  assertUndefined(obj5);
    +}
    +
    +
    +function testExceedMax3() {
    +  var p = new goog.structs.Pool(); // default: 10
    +  var objs = [];
    +
    +  for (var i = 0; i < 12; i++) {
    +    objs[i] = p.getObject();
    +  }
    +
    +  for (var i = 0; i < 10; i++) {
    +    assertNotNull('First 10 should be not null', objs[i]);
    +  }
    +
    +  assertUndefined(objs[10]);
    +  assertUndefined(objs[11]);
    +}
    +
    +
    +function testReleaseAndGet1() {
    +  var p = new goog.structs.Pool(0, 10);
    +  var o = p.getObject();
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o));
    +  assertEquals(1, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(1, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet2() {
    +  var p = new NoObjectReusePool(0, 10);
    +  var o = p.getObject();
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o));
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet3() {
    +  var p = new goog.structs.Pool(0, 10);
    +  var o1 = p.getObject();
    +  var o2 = p.getObject();
    +  var o3 = p.getObject();
    +  var o4 = {};
    +  assertEquals(3, p.getCount());
    +  assertEquals(3, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o1));
    +  assertTrue('Result should be true', p.releaseObject(o2));
    +  assertFalse('Result should be false', p.releaseObject(o4));
    +  assertEquals(3, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(2, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet4() {
    +  var p = new NoObjectReusePool(0, 10);
    +  var o1 = p.getObject();
    +  var o2 = p.getObject();
    +  var o3 = p.getObject();
    +  var o4 = {};
    +  assertEquals(3, p.getCount());
    +  assertEquals(3, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o1));
    +  assertTrue('Result should be true', p.releaseObject(o2));
    +  assertFalse('Result should be false', p.releaseObject(o4));
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testIsInPool1() {
    +  var p = new goog.structs.Pool();
    +  var o1 = p.getObject();
    +  var o2 = p.getObject();
    +  var o3 = p.getObject();
    +  var o4 = {};
    +  var o5 = {};
    +  var o6 = o1;
    +
    +  assertTrue(p.contains(o1));
    +  assertTrue(p.contains(o2));
    +  assertTrue(p.contains(o3));
    +  assertFalse(p.contains(o4));
    +  assertFalse(p.contains(o5));
    +  assertTrue(p.contains(o6));
    +}
    +
    +
    +function testSetMin1() {
    +  var p = new goog.structs.Pool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  p.setMinimumCount(10);
    +
    +  assertEquals(10, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(10, p.getFreeCount());
    +}
    +
    +
    +function testSetMin2() {
    +  var p = new goog.structs.Pool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  var o1 = p.getObject();
    +
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  p.setMinimumCount(10);
    +
    +  assertEquals(10, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(9, p.getFreeCount());
    +}
    +
    +
    +function testSetMax1() {
    +  var p = new goog.structs.Pool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  var o1 = p.getObject();
    +  var o2 = p.getObject();
    +  var o3 = p.getObject();
    +  var o4 = p.getObject();
    +  var o5 = p.getObject();
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(5, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  assertTrue('Result should be true', p.releaseObject(o5));
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(4, p.getInUseCount());
    +  assertEquals(1, p.getFreeCount());
    +
    +  p.setMaximumCount(4);
    +
    +  assertEquals(4, p.getCount());
    +  assertEquals(4, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testInvalidMinMax1() {
    +  var p = new goog.structs.Pool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  assertThrows(function() {
    +    p.setMinimumCount(11);
    +  });
    +}
    +
    +
    +function testInvalidMinMax2() {
    +  var p = new goog.structs.Pool(5, 10);
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(5, p.getFreeCount());
    +
    +  assertThrows(function() {
    +    p.setMaximumCount(4);
    +  });
    +}
    +
    +
    +function testInvalidMinMax3() {
    +  assertThrows(function() {
    +    new goog.structs.Pool(10, 1);
    +  });
    +}
    +
    +
    +function testRateLimiting() {
    +  var clock = new goog.testing.MockClock();
    +  clock.install();
    +
    +  var p = new goog.structs.Pool(0, 3);
    +  p.setDelay(100);
    +
    +  assertNotUndefined(p.getObject());
    +  assertUndefined(p.getObject());
    +
    +  clock.tick(100);
    +  assertNotUndefined(p.getObject());
    +  assertUndefined(p.getObject());
    +
    +  clock.tick(100);
    +  assertNotUndefined(p.getObject());
    +  assertUndefined(p.getObject());
    +
    +  clock.tick(100);
    +  assertUndefined(p.getObject());
    +
    +  goog.dispose(clock);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/prioritypool.js b/mapbox-gl/closure-library/closure/goog/structs/prioritypool.js
    new file mode 100644
    index 0000000000..7f4ade297c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/prioritypool.js
    @@ -0,0 +1,182 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Priority Pool.
    + *
    + *
    + * An extending of Pool that handles queueing and prioritization.
    + */
    +
    +
    +goog.provide('goog.structs.PriorityPool');
    +
    +goog.require('goog.structs.Pool');
    +goog.require('goog.structs.PriorityQueue');
    +
    +
    +
    +/**
    + * A generic pool class. If max is greater than min, an error is thrown.
    + * @param {number=} opt_minCount Min. number of objects (Default: 1).
    + * @param {number=} opt_maxCount Max. number of objects (Default: 10).
    + * @constructor
    + * @extends {goog.structs.Pool<VALUE>}
    + * @template VALUE
    + */
    +goog.structs.PriorityPool = function(opt_minCount, opt_maxCount) {
    +  /**
    +   * The key for the most recent timeout created.
    +   * @private {number|undefined}
    +   */
    +  this.delayTimeout_ = undefined;
    +
    +  /**
    +   * Queue of requests for pool objects.
    +   * @private {goog.structs.PriorityQueue<VALUE>}
    +   */
    +  this.requestQueue_ = new goog.structs.PriorityQueue();
    +
    +  // Must break convention of putting the super-class's constructor first. This
    +  // is because the super-class constructor calls adjustForMinMax, which this
    +  // class overrides. In this class's implementation, it assumes that there
    +  // is a requestQueue_, and will error if not present.
    +  goog.structs.Pool.call(this, opt_minCount, opt_maxCount);
    +};
    +goog.inherits(goog.structs.PriorityPool, goog.structs.Pool);
    +
    +
    +/**
    + * Default priority for pool objects requests.
    + * @type {number}
    + * @private
    + */
    +goog.structs.PriorityPool.DEFAULT_PRIORITY_ = 100;
    +
    +
    +/** @override */
    +goog.structs.PriorityPool.prototype.setDelay = function(delay) {
    +  goog.structs.PriorityPool.base(this, 'setDelay', delay);
    +
    +  // If the pool hasn't been accessed yet, no need to do anything.
    +  if (!goog.isDefAndNotNull(this.lastAccess)) {
    +    return;
    +  }
    +
    +  goog.global.clearTimeout(this.delayTimeout_);
    +  this.delayTimeout_ = goog.global.setTimeout(
    +      goog.bind(this.handleQueueRequests_, this),
    +      this.delay + this.lastAccess - goog.now());
    +
    +  // Handle all requests.
    +  this.handleQueueRequests_();
    +};
    +
    +
    +/**
    + * Get a new object from the the pool, if there is one available, otherwise
    + * return undefined.
    + * @param {Function=} opt_callback The function to callback when an object is
    + *     available. This could be immediately. If this is not present, then an
    + *     object is immediately returned if available, or undefined if not.
    + * @param {number=} opt_priority The priority of the request. A smaller value
    + *     means a higher priority.
    + * @return {VALUE|undefined} The new object from the pool if there is one
    + *     available and a callback is not given. Otherwise, undefined.
    + * @override
    + */
    +goog.structs.PriorityPool.prototype.getObject = function(opt_callback,
    +                                                         opt_priority) {
    +  if (!opt_callback) {
    +    var result = goog.structs.PriorityPool.base(this, 'getObject');
    +    if (result && this.delay) {
    +      this.delayTimeout_ = goog.global.setTimeout(
    +          goog.bind(this.handleQueueRequests_, this),
    +          this.delay);
    +    }
    +    return result;
    +  }
    +
    +  var priority = goog.isDef(opt_priority) ? opt_priority :
    +      goog.structs.PriorityPool.DEFAULT_PRIORITY_;
    +  this.requestQueue_.enqueue(priority, opt_callback);
    +
    +  // Handle all requests.
    +  this.handleQueueRequests_();
    +
    +  return undefined;
    +};
    +
    +
    +/**
    + * Handles the request queue. Tries to fires off as many queued requests as
    + * possible.
    + * @private
    + */
    +goog.structs.PriorityPool.prototype.handleQueueRequests_ = function() {
    +  var requestQueue = this.requestQueue_;
    +  while (requestQueue.getCount() > 0) {
    +    var obj = this.getObject();
    +
    +    if (!obj) {
    +      return;
    +    } else {
    +      var requestCallback = requestQueue.dequeue();
    +      requestCallback.apply(this, [obj]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adds an object to the collection of objects that are free. If the object can
    + * not be added, then it is disposed.
    + *
    + * NOTE: This method does not remove the object from the in use collection.
    + *
    + * @param {VALUE} obj The object to add to the collection of free objects.
    + * @override
    + */
    +goog.structs.PriorityPool.prototype.addFreeObject = function(obj) {
    +  goog.structs.PriorityPool.superClass_.addFreeObject.call(this, obj);
    +
    +  // Handle all requests.
    +  this.handleQueueRequests_();
    +};
    +
    +
    +/**
    + * Adjusts the objects held in the pool to be within the min/max constraints.
    + *
    + * NOTE: It is possible that the number of objects in the pool will still be
    + * greater than the maximum count of objects allowed. This will be the case
    + * if no more free objects can be disposed of to get below the minimum count
    + * (i.e., all objects are in use).
    + * @override
    + */
    +goog.structs.PriorityPool.prototype.adjustForMinMax = function() {
    +  goog.structs.PriorityPool.superClass_.adjustForMinMax.call(this);
    +
    +  // Handle all requests.
    +  this.handleQueueRequests_();
    +};
    +
    +
    +/** @override */
    +goog.structs.PriorityPool.prototype.disposeInternal = function() {
    +  goog.structs.PriorityPool.superClass_.disposeInternal.call(this);
    +  goog.global.clearTimeout(this.delayTimeout_);
    +  this.requestQueue_.clear();
    +  this.requestQueue_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.html b/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.html
    new file mode 100644
    index 0000000000..8a41fd9007
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.PriorityPool
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.PriorityPoolTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.js b/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.js
    new file mode 100644
    index 0000000000..e4a2e9ce31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/prioritypool_test.js
    @@ -0,0 +1,582 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.PriorityPoolTest');
    +goog.setTestOnly('goog.structs.PriorityPoolTest');
    +
    +goog.require('goog.structs.PriorityPool');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +// Implementation of the Pool class with isObjectDead() always returning TRUE,
    +// so that the the Pool will not reuse any objects.
    +function NoObjectReusePriorityPool(opt_min, opt_max) {
    +  goog.structs.PriorityPool.call(this, opt_min, opt_max);
    +}
    +goog.inherits(NoObjectReusePriorityPool, goog.structs.PriorityPool);
    +
    +NoObjectReusePriorityPool.prototype.objectCanBeReused = function(obj) {
    +  return false;
    +};
    +
    +
    +function testExceedMax1() {
    +  var p = new goog.structs.PriorityPool(0, 3);
    +
    +  var getCount1 = 0;
    +  var callback1 = function(obj) {
    +    assertNotNull(obj);
    +    getCount1++;
    +  };
    +
    +  var getCount2 = 0;
    +  var callback2 = function(obj) {
    +    getCount2++;
    +  };
    +
    +  p.getObject(callback1);
    +  p.getObject(callback1);
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +
    +  assertEquals('getCount for allocated, Should be 3', getCount1, 3);
    +  assertEquals('getCount for unallocated, Should be 0', getCount2, 0);
    +}
    +
    +
    +function testExceedMax2() {
    +  var p = new goog.structs.PriorityPool(0, 1);
    +
    +  var getCount1 = 0;
    +  var callback1 = function(obj) {
    +    assertNotNull(obj);
    +    getCount1++;
    +  };
    +
    +  var getCount2 = 0;
    +  var callback2 = function(obj) {
    +    getCount2++;
    +  };
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +  p.getObject(callback2);
    +
    +  assertEquals('getCount for allocated, Should be 1', getCount1, 1);
    +  assertEquals('getCount for unallocated, Should be 0', getCount2, 0);
    +}
    +
    +function testExceedMax3() {
    +  var p = new goog.structs.PriorityPool(0, 2);
    +
    +  var obj1 = null;
    +  var callback1 = function(obj) {
    +    obj1 = obj;
    +  };
    +
    +  var obj2 = null;
    +  var callback2 = function(obj) {
    +    obj2 = obj;
    +  };
    +
    +  var obj3 = null;
    +  var callback3 = function(obj) {
    +    obj3 = obj;
    +  };
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +
    +  assertNotNull(obj1);
    +  assertNotNull(obj2);
    +  assertNull(obj3);
    +}
    +
    +function testExceedMax4() {
    +  var p = new goog.structs.PriorityPool(); // default: 10
    +  var objs = [];
    +
    +  var getCount1 = 0;
    +  var callback1 = function(obj) {
    +    assertNotNull(obj);
    +    getCount1++;
    +  };
    +
    +  var getCount2 = 0;
    +  var callback2 = function(obj) {
    +    getCount2++;
    +  };
    +
    +  for (var i = 0; i < 12; i++) {
    +    p.getObject(i < 10 ? callback1 : callback2);
    +  }
    +
    +  assertEquals('getCount for allocated, Should be 10', getCount1, 10);
    +  assertEquals('getCount for unallocated, Should be 0', getCount2, 0);
    +}
    +
    +
    +function testReleaseAndGet1() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +
    +  var o = null;
    +  var callback = function(obj) {
    +    o = obj;
    +  };
    +
    +  p.getObject(callback);
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o));
    +  assertEquals(1, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(1, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet2() {
    +  var p = new NoObjectReusePriorityPool(0, 10);
    +
    +  var o = null;
    +  var callback = function(obj) {
    +    o = obj;
    +  };
    +
    +  p.getObject(callback);
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o));
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet3() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  var o4 = {};
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +
    +  assertEquals(3, p.getCount());
    +  assertEquals(3, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o1));
    +  assertTrue('Result should be true', p.releaseObject(o2));
    +  assertFalse('Result should be false', p.releaseObject(o4));
    +  assertEquals(3, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(2, p.getFreeCount());
    +}
    +
    +
    +function testReleaseAndGet4() {
    +  var p = new NoObjectReusePriorityPool(0, 10);
    +
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  var o4 = {};
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +  assertEquals(3, p.getCount());
    +  assertEquals(3, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +  assertTrue('Result should be true', p.releaseObject(o1));
    +  assertTrue('Result should be true', p.releaseObject(o2));
    +  assertFalse('Result should be false', p.releaseObject(o4));
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testIsInPool1() {
    +  var p = new goog.structs.PriorityPool();
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  var o4 = {};
    +  var o5 = {};
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +  var o6 = o1;
    +
    +  assertTrue(p.contains(o1));
    +  assertTrue(p.contains(o2));
    +  assertTrue(p.contains(o3));
    +  assertFalse(p.contains(o4));
    +  assertFalse(p.contains(o5));
    +  assertTrue(p.contains(o6));
    +}
    +
    +
    +function testSetMin1() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  p.setMinimumCount(10);
    +
    +  assertEquals(10, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(10, p.getFreeCount());
    +}
    +
    +
    +function testSetMin2() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +  p.getObject(callback1);
    +
    +  assertEquals(1, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  p.setMinimumCount(10);
    +
    +  assertEquals(10, p.getCount());
    +  assertEquals(1, p.getInUseCount());
    +  assertEquals(9, p.getFreeCount());
    +}
    +
    +
    +function testSetMax1() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  var o4 = null;
    +  var callback4 = function(obj) {
    +    o4 = obj;
    +  };
    +
    +  var o5 = null;
    +  var callback5 = function(obj) {
    +    o5 = obj;
    +  };
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +  p.getObject(callback4);
    +  p.getObject(callback5);
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(5, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  assertTrue('Result should be true', p.releaseObject(o5));
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(4, p.getInUseCount());
    +  assertEquals(1, p.getFreeCount());
    +
    +  p.setMaximumCount(4);
    +
    +  assertEquals(4, p.getCount());
    +  assertEquals(4, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +}
    +
    +
    +function testInvalidMinMax1() {
    +  var p = new goog.structs.PriorityPool(0, 10);
    +
    +  assertEquals(0, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(0, p.getFreeCount());
    +
    +  assertThrows(function() {
    +    p.setMinimumCount(11);
    +  });
    +}
    +
    +
    +function testInvalidMinMax2() {
    +  var p = new goog.structs.PriorityPool(5, 10);
    +
    +  assertEquals(5, p.getCount());
    +  assertEquals(0, p.getInUseCount());
    +  assertEquals(5, p.getFreeCount());
    +
    +  assertThrows(function() {
    +    p.setMaximumCount(4);
    +  });
    +}
    +
    +
    +function testInvalidMinMax3() {
    +  assertThrows(function() {
    +    new goog.structs.PriorityPool(10, 1);
    +  });
    +}
    +
    +function testQueue1() {
    +  var p = new goog.structs.PriorityPool(0, 2);
    +
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  p.getObject(callback1);
    +  p.getObject(callback2);
    +  p.getObject(callback3);
    +
    +  assertNotNull(o1);
    +  assertNotNull(o2);
    +  assertNull(o3);
    +
    +  p.releaseObject(o1);
    +  assertNotNull(o3);
    +}
    +
    +
    +function testPriority1() {
    +  var p = new goog.structs.PriorityPool(0, 2);
    +
    +  var o1 = null;
    +  var callback1 = function(obj) {
    +    o1 = obj;
    +  };
    +
    +  var o2 = null;
    +  var callback2 = function(obj) {
    +    o2 = obj;
    +  };
    +
    +  var o3 = null;
    +  var callback3 = function(obj) {
    +    o3 = obj;
    +  };
    +
    +  var o4 = null;
    +  var callback4 = function(obj) {
    +    o4 = obj;
    +  };
    +
    +  var o5 = null;
    +  var callback5 = function(obj) {
    +    o5 = obj;
    +  };
    +
    +  var o6 = null;
    +  var callback6 = function(obj) {
    +    o6 = obj;
    +  };
    +
    +  p.getObject(callback1);       // Initially seeded requests.
    +  p.getObject(callback2);
    +
    +  p.getObject(callback3, 101);  // Lowest priority.
    +  p.getObject(callback4);       // Second lowest priority (default is 100).
    +  p.getObject(callback5, 99);   // Second highest priority.
    +  p.getObject(callback6, 0);    // Highest priority.
    +
    +  assertNotNull(o1);
    +  assertNotNull(o2);
    +  assertNull(o3);
    +  assertNull(o4);
    +  assertNull(o5);
    +  assertNull(o6);
    +
    +  p.releaseObject(o1);  // Release the first initially seeded request (o1).
    +  assertNotNull(o6);    // Make sure the highest priority request (o6) started.
    +  assertNull(o3);
    +  assertNull(o4);
    +  assertNull(o5);
    +
    +  p.releaseObject(o2);  // Release the second, initially seeded request (o2).
    +  assertNotNull(o5);    // The second highest priority request starts (o5).
    +  assertNull(o3);
    +  assertNull(o4);
    +
    +  p.releaseObject(o6);
    +  assertNotNull(o4);
    +  assertNull(o3);
    +}
    +
    +
    +function testRateLimiting() {
    +  var clock = new goog.testing.MockClock();
    +  clock.install();
    +
    +  var p = new goog.structs.PriorityPool(0, 4);
    +  p.setDelay(100);
    +
    +  var getCount = 0;
    +  var callback = function(obj) {
    +    assertNotNull(obj);
    +    getCount++;
    +  };
    +
    +  p.getObject(callback);
    +  assertEquals(1, getCount);
    +
    +  p.getObject(callback);
    +  assertEquals(1, getCount);
    +
    +  clock.tick(100);
    +  assertEquals(2, getCount);
    +
    +  p.getObject(callback);
    +  p.getObject(callback);
    +  assertEquals(2, getCount);
    +
    +  clock.tick(100);
    +  assertEquals(3, getCount);
    +
    +  clock.tick(100);
    +  assertEquals(4, getCount);
    +
    +  p.getObject(callback);
    +  assertEquals(4, getCount);
    +
    +  clock.tick(100);
    +  assertEquals(4, getCount);
    +
    +  goog.dispose(clock);
    +}
    +
    +
    +function testRateLimitingWithChangingDelay() {
    +  var clock = new goog.testing.MockClock();
    +  clock.install();
    +
    +  var p = new goog.structs.PriorityPool(0, 3);
    +  p.setDelay(100);
    +
    +  var getCount = 0;
    +  var callback = function(obj) {
    +    assertNotNull(obj);
    +    getCount++;
    +  };
    +
    +  p.getObject(callback);
    +  assertEquals(1, getCount);
    +
    +  p.getObject(callback);
    +  assertEquals(1, getCount);
    +
    +  clock.tick(50);
    +  assertEquals(1, getCount);
    +
    +  p.setDelay(50);
    +  assertEquals(2, getCount);
    +
    +  p.getObject(callback);
    +  assertEquals(2, getCount);
    +
    +  clock.tick(20);
    +  assertEquals(2, getCount);
    +
    +  p.setDelay(40);
    +  assertEquals(2, getCount);
    +
    +  clock.tick(20);
    +  assertEquals(3, getCount);
    +
    +  goog.dispose(clock);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/priorityqueue.js b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue.js
    new file mode 100644
    index 0000000000..c89f912b27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue.js
    @@ -0,0 +1,66 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Priority Queue.
    + *
    + *
    + * This file provides the implementation of a Priority Queue. Smaller priorities
    + * move to the front of the queue. If two values have the same priority,
    + * it is arbitrary which value will come to the front of the queue first.
    + */
    +
    +// TODO(user): Should this rely on natural ordering via some Comparable
    +//     interface?
    +
    +
    +goog.provide('goog.structs.PriorityQueue');
    +
    +goog.require('goog.structs.Heap');
    +
    +
    +
    +/**
    + * Class for Priority Queue datastructure.
    + *
    + * @constructor
    + * @extends {goog.structs.Heap<number, VALUE>}
    + * @template VALUE
    + * @final
    + */
    +goog.structs.PriorityQueue = function() {
    +  goog.structs.Heap.call(this);
    +};
    +goog.inherits(goog.structs.PriorityQueue, goog.structs.Heap);
    +
    +
    +/**
    + * Puts the specified value in the queue.
    + * @param {number} priority The priority of the value. A smaller value here
    + *     means a higher priority.
    + * @param {VALUE} value The value.
    + */
    +goog.structs.PriorityQueue.prototype.enqueue = function(priority, value) {
    +  this.insert(priority, value);
    +};
    +
    +
    +/**
    + * Retrieves and removes the head of this queue.
    + * @return {VALUE} The element at the head of this queue. Returns undefined if
    + *     the queue is empty.
    + */
    +goog.structs.PriorityQueue.prototype.dequeue = function() {
    +  return this.remove();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.html b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.html
    new file mode 100644
    index 0000000000..0538f20d59
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.PriorityQueue
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.PriorityQueueTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.js b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.js
    new file mode 100644
    index 0000000000..f0ccdbde44
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/priorityqueue_test.js
    @@ -0,0 +1,175 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.PriorityQueueTest');
    +goog.setTestOnly('goog.structs.PriorityQueueTest');
    +
    +goog.require('goog.structs');
    +goog.require('goog.structs.PriorityQueue');
    +goog.require('goog.testing.jsunit');
    +
    +function getPriorityQueue() {
    +  var p = new goog.structs.PriorityQueue();
    +  p.enqueue(0, 'a');
    +  p.enqueue(1, 'b');
    +  p.enqueue(2, 'c');
    +  p.enqueue(3, 'd');
    +  return p;
    +}
    +
    +
    +function getPriorityQueue2() {
    +  var p = new goog.structs.PriorityQueue();
    +  p.insert(1, 'b');
    +  p.insert(3, 'd');
    +  p.insert(0, 'a');
    +  p.insert(2, 'c');
    +  return p;
    +}
    +
    +
    +function testGetCount1() {
    +  var p = getPriorityQueue();
    +  assertEquals('count, should be 4', p.getCount(), 4);
    +  p.dequeue();
    +  assertEquals('count, should be 3', p.getCount(), 3);
    +}
    +
    +
    +function testGetCount2() {
    +  var p = getPriorityQueue();
    +  assertEquals('count, should be 4', p.getCount(), 4);
    +  p.dequeue();
    +  assertEquals('count, should be 3', p.getCount(), 3);
    +}
    +
    +
    +function testGetCount3() {
    +  var p = getPriorityQueue();
    +  p.dequeue();
    +  p.dequeue();
    +  p.dequeue();
    +  p.dequeue();
    +  assertEquals('count, should be 0', p.getCount(), 0);
    +}
    +
    +
    +function testKeys() {
    +  var p = getPriorityQueue();
    +  var keys = p.getKeys();
    +  for (var i = 0; i < 4; i++) {
    +    assertTrue('getKeys, key ' + i + ' found', goog.structs.contains(keys, i));
    +  }
    +  assertEquals('getKeys, Should be 4 keys', goog.structs.getCount(keys), 4);
    +}
    +
    +
    +function testValues() {
    +  var p = getPriorityQueue();
    +  var values = p.getValues();
    +
    +  assertTrue('getKeys, value "a" found', goog.structs.contains(values, 'a'));
    +  assertTrue('getKeys, value "b" found', goog.structs.contains(values, 'b'));
    +  assertTrue('getKeys, value "c" found', goog.structs.contains(values, 'c'));
    +  assertTrue('getKeys, value "d" found', goog.structs.contains(values, 'd'));
    +  assertEquals('getKeys, Should be 4 keys', goog.structs.getCount(values), 4);
    +}
    +
    +
    +function testClear() {
    +  var p = getPriorityQueue();
    +  p.clear();
    +  assertTrue('cleared so it should be empty', p.isEmpty());
    +}
    +
    +
    +function testIsEmpty() {
    +  var p = getPriorityQueue();
    +  assertFalse('4 values so should not be empty', p.isEmpty());
    +
    +  p.dequeue();
    +  p.dequeue();
    +  p.dequeue();
    +  assertFalse('1 values so should not be empty', p.isEmpty());
    +
    +  p.dequeue();
    +  assertTrue('0 values so should be empty', p.isEmpty());
    +}
    +
    +
    +function testPeek1() {
    +  var p = getPriorityQueue();
    +  assertEquals('peek, Should be "a"', p.peek(), 'a');
    +}
    +
    +
    +function testPeek2() {
    +  var p = getPriorityQueue2();
    +  assertEquals('peek, Should be "a"', p.peek(), 'a');
    +}
    +
    +
    +function testPeek3() {
    +  var p = getPriorityQueue();
    +  p.clear();
    +  assertEquals('peek, Should be "a"', p.peek(), undefined);
    +}
    +
    +
    +function testDequeue1() {
    +  var p = getPriorityQueue();
    +
    +  assertEquals('dequeue, Should be "a"', p.dequeue(), 'a');
    +  assertEquals('dequeue, Should be "b"', p.dequeue(), 'b');
    +  assertEquals('dequeue, Should be "c"', p.dequeue(), 'c');
    +  assertEquals('dequeue, Should be "d"', p.dequeue(), 'd');
    +}
    +
    +
    +function testDequeue2() {
    +  var p = getPriorityQueue2();
    +
    +  assertEquals('dequeue, Should be "a"', p.dequeue(), 'a');
    +  assertEquals('dequeue, Should be "b"', p.dequeue(), 'b');
    +  assertEquals('dequeue, Should be "c"', p.dequeue(), 'c');
    +  assertEquals('dequeue, Should be "d"', p.dequeue(), 'd');
    +}
    +
    +
    +function testEnqueuePeek1() {
    +  var p = new goog.structs.PriorityQueue();
    +
    +  p.enqueue(3, 'd');
    +  assertEquals('peak, Should be "d"', p.peek(), 'd');
    +  p.enqueue(2, 'c');
    +  assertEquals('peak, Should be "c"', p.peek(), 'c');
    +  p.enqueue(1, 'b');
    +  assertEquals('peak, Should be "b"', p.peek(), 'b');
    +  p.enqueue(0, 'a');
    +  assertEquals('peak, Should be "a"', p.peek(), 'a');
    +}
    +
    +
    +function testEnqueuePeek2() {
    +  var p = new goog.structs.PriorityQueue();
    +
    +  p.enqueue(1, 'b');
    +  assertEquals('peak, Should be "b"', p.peek(), 'b');
    +  p.enqueue(3, 'd');
    +  assertEquals('peak, Should be "b"', p.peek(), 'b');
    +  p.enqueue(0, 'a');
    +  assertEquals('peak, Should be "a"', p.peek(), 'a');
    +  p.enqueue(2, 'c');
    +  assertEquals('peak, Should be "a"', p.peek(), 'a');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/quadtree.js b/mapbox-gl/closure-library/closure/goog/structs/quadtree.js
    new file mode 100644
    index 0000000000..cd645dd525
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/quadtree.js
    @@ -0,0 +1,570 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: A point Quad Tree for representing 2D data. Each
    + * region has the same ratio as the bounds for the tree.
    + *
    + * The implementation currently requires pre-determined bounds for data as it
    + * can not rebalance itself to that degree.
    + *
    + * @see ../demos/quadtree.html
    + */
    +
    +
    +goog.provide('goog.structs.QuadTree');
    +goog.provide('goog.structs.QuadTree.Node');
    +goog.provide('goog.structs.QuadTree.Point');
    +
    +goog.require('goog.math.Coordinate');
    +
    +
    +
    +/**
    + * Constructs a new quad tree.
    + * @param {number} minX Minimum x-value that can be held in tree.
    + * @param {number} minY Minimum y-value that can be held in tree.
    + * @param {number} maxX Maximum x-value that can be held in tree.
    + * @param {number} maxY Maximum y-value that can be held in tree.
    + * @constructor
    + * @final
    + */
    +goog.structs.QuadTree = function(minX, minY, maxX, maxY) {
    +  /**
    +   * Count of the number of items in the tree.
    +   * @private {number}
    +   */
    +  this.count_ = 0;
    +
    +  /**
    +   * The root node for the quad tree.
    +   * @private {goog.structs.QuadTree.Node}
    +   */
    +  this.root_ = new goog.structs.QuadTree.Node(
    +      minX, minY, maxX - minX, maxY - minY);
    +};
    +
    +
    +/**
    + * Returns a reference to the tree's root node.  Callers shouldn't modify nodes,
    + * directly.  This is a convenience for visualization and debugging purposes.
    + * @return {goog.structs.QuadTree.Node} The root node.
    + */
    +goog.structs.QuadTree.prototype.getRootNode = function() {
    +  return this.root_;
    +};
    +
    +
    +/**
    + * Sets the value of an (x, y) point within the quad-tree.
    + * @param {number} x The x-coordinate.
    + * @param {number} y The y-coordinate.
    + * @param {*} value The value associated with the point.
    + */
    +goog.structs.QuadTree.prototype.set = function(x, y, value) {
    +  var root = this.root_;
    +  if (x < root.x || y < root.y || x > root.x + root.w || y > root.y + root.h) {
    +    throw Error('Out of bounds : (' + x + ', ' + y + ')');
    +  }
    +  if (this.insert_(root, new goog.structs.QuadTree.Point(x, y, value))) {
    +    this.count_++;
    +  }
    +};
    +
    +
    +/**
    + * Gets the value of the point at (x, y) or null if the point is empty.
    + * @param {number} x The x-coordinate.
    + * @param {number} y The y-coordinate.
    + * @param {*=} opt_default The default value to return if the node doesn't
    + *     exist.
    + * @return {*} The value of the node, the default value if the node
    + *     doesn't exist, or undefined if the node doesn't exist and no default
    + *     has been provided.
    + */
    +goog.structs.QuadTree.prototype.get = function(x, y, opt_default) {
    +  var node = this.find_(this.root_, x, y);
    +  return node ? node.point.value : opt_default;
    +};
    +
    +
    +/**
    + * Removes a point from (x, y) if it exists.
    + * @param {number} x The x-coordinate.
    + * @param {number} y The y-coordinate.
    + * @return {*} The value of the node that was removed, or null if the
    + *     node doesn't exist.
    + */
    +goog.structs.QuadTree.prototype.remove = function(x, y) {
    +  var node = this.find_(this.root_, x, y);
    +  if (node) {
    +    var value = node.point.value;
    +    node.point = null;
    +    node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
    +    this.balance_(node);
    +    this.count_--;
    +    return value;
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the point at (x, y) exists in the tree.
    + * @param {number} x The x-coordinate.
    + * @param {number} y The y-coordinate.
    + * @return {boolean} Whether the tree contains a point at (x, y).
    + */
    +goog.structs.QuadTree.prototype.contains = function(x, y) {
    +  return this.get(x, y) != null;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the tree is empty.
    + */
    +goog.structs.QuadTree.prototype.isEmpty = function() {
    +  return this.root_.nodeType == goog.structs.QuadTree.NodeType.EMPTY;
    +};
    +
    +
    +/**
    + * @return {number} The number of items in the tree.
    + */
    +goog.structs.QuadTree.prototype.getCount = function() {
    +  return this.count_;
    +};
    +
    +
    +/**
    + * Removes all items from the tree.
    + */
    +goog.structs.QuadTree.prototype.clear = function() {
    +  this.root_.nw = this.root_.ne = this.root_.sw = this.root_.se = null;
    +  this.root_.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
    +  this.root_.point = null;
    +  this.count_ = 0;
    +};
    +
    +
    +/**
    + * Returns an array containing the coordinates of each point stored in the tree.
    + * @return {!Array<goog.math.Coordinate?>} Array of coordinates.
    + */
    +goog.structs.QuadTree.prototype.getKeys = function() {
    +  var arr = [];
    +  this.traverse_(this.root_, function(node) {
    +    arr.push(new goog.math.Coordinate(node.point.x, node.point.y));
    +  });
    +  return arr;
    +};
    +
    +
    +/**
    + * Returns an array containing all values stored within the tree.
    + * @return {!Array<Object>} The values stored within the tree.
    + */
    +goog.structs.QuadTree.prototype.getValues = function() {
    +  var arr = [];
    +  this.traverse_(this.root_, function(node) {
    +    // Must have a point because it's a leaf.
    +    arr.push(node.point.value);
    +  });
    +  return arr;
    +};
    +
    +
    +/**
    + * Clones the quad-tree and returns the new instance.
    + * @return {!goog.structs.QuadTree} A clone of the tree.
    + */
    +goog.structs.QuadTree.prototype.clone = function() {
    +  var x1 = this.root_.x;
    +  var y1 = this.root_.y;
    +  var x2 = x1 + this.root_.w;
    +  var y2 = y1 + this.root_.h;
    +  var clone = new goog.structs.QuadTree(x1, y1, x2, y2);
    +  // This is inefficient as the clone needs to recalculate the structure of the
    +  // tree, even though we know it already.  But this is easier and can be
    +  // optimized when/if needed.
    +  this.traverse_(this.root_, function(node) {
    +    clone.set(node.point.x, node.point.y, node.point.value);
    +  });
    +  return clone;
    +};
    +
    +
    +/**
    + * Traverses the tree and calls a function on each node.
    + * @param {function(?, goog.math.Coordinate, goog.structs.QuadTree)} fn
    + *     The function to call for every value. This function takes 3 arguments
    + *     (the value, the coordinate, and the tree itself) and the return value is
    + *     irrelevant.
    + * @param {Object=} opt_obj The object to be used as the value of 'this'
    + *     within {@ code fn}.
    + */
    +goog.structs.QuadTree.prototype.forEach = function(fn, opt_obj) {
    +  this.traverse_(this.root_, function(node) {
    +    var coord = new goog.math.Coordinate(node.point.x, node.point.y);
    +    fn.call(opt_obj, node.point.value, coord, this);
    +  });
    +};
    +
    +
    +/**
    + * Traverses the tree depth-first, with quadrants being traversed in clockwise
    + * order (NE, SE, SW, NW).  The provided function will be called for each
    + * leaf node that is encountered.
    + * @param {goog.structs.QuadTree.Node} node The current node.
    + * @param {function(goog.structs.QuadTree.Node)} fn The function to call
    + *     for each leaf node. This function takes the node as an argument, and its
    + *     return value is irrelevant.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.traverse_ = function(node, fn) {
    +  switch (node.nodeType) {
    +    case goog.structs.QuadTree.NodeType.LEAF:
    +      fn.call(this, node);
    +      break;
    +
    +    case goog.structs.QuadTree.NodeType.POINTER:
    +      this.traverse_(node.ne, fn);
    +      this.traverse_(node.se, fn);
    +      this.traverse_(node.sw, fn);
    +      this.traverse_(node.nw, fn);
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Finds a leaf node with the same (x, y) coordinates as the target point, or
    + * null if no point exists.
    + * @param {goog.structs.QuadTree.Node} node The node to search in.
    + * @param {number} x The x-coordinate of the point to search for.
    + * @param {number} y The y-coordinate of the point to search for.
    + * @return {goog.structs.QuadTree.Node} The leaf node that matches the target,
    + *     or null if it doesn't exist.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.find_ = function(node, x, y) {
    +  switch (node.nodeType) {
    +    case goog.structs.QuadTree.NodeType.EMPTY:
    +      return null;
    +
    +    case goog.structs.QuadTree.NodeType.LEAF:
    +      return node.point.x == x && node.point.y == y ? node : null;
    +
    +    case goog.structs.QuadTree.NodeType.POINTER:
    +      return this.find_(this.getQuadrantForPoint_(node, x, y), x, y);
    +
    +    default:
    +      throw Error('Invalid nodeType');
    +  }
    +};
    +
    +
    +/**
    + * Inserts a point into the tree, updating the tree's structure if necessary.
    + * @param {goog.structs.QuadTree.Node} parent The parent to insert the point
    + *     into.
    + * @param {goog.structs.QuadTree.Point} point The point to insert.
    + * @return {boolean} True if a new node was added to the tree; False if a node
    + *     already existed with the correpsonding coordinates and had its value
    + *     reset.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.insert_ = function(parent, point) {
    +  switch (parent.nodeType) {
    +    case goog.structs.QuadTree.NodeType.EMPTY:
    +      this.setPointForNode_(parent, point);
    +      return true;
    +
    +    case goog.structs.QuadTree.NodeType.LEAF:
    +      if (parent.point.x == point.x && parent.point.y == point.y) {
    +        this.setPointForNode_(parent, point);
    +        return false;
    +      } else {
    +        this.split_(parent);
    +        return this.insert_(parent, point);
    +      }
    +
    +    case goog.structs.QuadTree.NodeType.POINTER:
    +      return this.insert_(
    +          this.getQuadrantForPoint_(parent, point.x, point.y), point);
    +
    +    default:
    +      throw Error('Invalid nodeType in parent');
    +  }
    +};
    +
    +
    +/**
    + * Converts a leaf node to a pointer node and reinserts the node's point into
    + * the correct child.
    + * @param {goog.structs.QuadTree.Node} node The node to split.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.split_ = function(node) {
    +  var oldPoint = node.point;
    +  node.point = null;
    +
    +  node.nodeType = goog.structs.QuadTree.NodeType.POINTER;
    +
    +  var x = node.x;
    +  var y = node.y;
    +  var hw = node.w / 2;
    +  var hh = node.h / 2;
    +
    +  node.nw = new goog.structs.QuadTree.Node(x, y, hw, hh, node);
    +  node.ne = new goog.structs.QuadTree.Node(x + hw, y, hw, hh, node);
    +  node.sw = new goog.structs.QuadTree.Node(x, y + hh, hw, hh, node);
    +  node.se = new goog.structs.QuadTree.Node(x + hw, y + hh, hw, hh, node);
    +
    +  this.insert_(node, oldPoint);
    +};
    +
    +
    +/**
    + * Attempts to balance a node. A node will need balancing if all its children
    + * are empty or it contains just one leaf.
    + * @param {goog.structs.QuadTree.Node} node The node to balance.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.balance_ = function(node) {
    +  switch (node.nodeType) {
    +    case goog.structs.QuadTree.NodeType.EMPTY:
    +    case goog.structs.QuadTree.NodeType.LEAF:
    +      if (node.parent) {
    +        this.balance_(node.parent);
    +      }
    +      break;
    +
    +    case goog.structs.QuadTree.NodeType.POINTER:
    +      var nw = node.nw, ne = node.ne, sw = node.sw, se = node.se;
    +      var firstLeaf = null;
    +
    +      // Look for the first non-empty child, if there is more than one then we
    +      // break as this node can't be balanced.
    +      if (nw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
    +        firstLeaf = nw;
    +      }
    +      if (ne.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
    +        if (firstLeaf) {
    +          break;
    +        }
    +        firstLeaf = ne;
    +      }
    +      if (sw.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
    +        if (firstLeaf) {
    +          break;
    +        }
    +        firstLeaf = sw;
    +      }
    +      if (se.nodeType != goog.structs.QuadTree.NodeType.EMPTY) {
    +        if (firstLeaf) {
    +          break;
    +        }
    +        firstLeaf = se;
    +      }
    +
    +      if (!firstLeaf) {
    +        // All child nodes are empty: so make this node empty.
    +        node.nodeType = goog.structs.QuadTree.NodeType.EMPTY;
    +        node.nw = node.ne = node.sw = node.se = null;
    +
    +      } else if (firstLeaf.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
    +        // Only child was a pointer, therefore we can't rebalance.
    +        break;
    +
    +      } else {
    +        // Only child was a leaf: so update node's point and make it a leaf.
    +        node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
    +        node.nw = node.ne = node.sw = node.se = null;
    +        node.point = firstLeaf.point;
    +      }
    +
    +      // Try and balance the parent as well.
    +      if (node.parent) {
    +        this.balance_(node.parent);
    +      }
    +
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Returns the child quadrant within a node that contains the given (x, y)
    + * coordinate.
    + * @param {goog.structs.QuadTree.Node} parent The node.
    + * @param {number} x The x-coordinate to look for.
    + * @param {number} y The y-coordinate to look for.
    + * @return {goog.structs.QuadTree.Node} The child quadrant that contains the
    + *     point.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.getQuadrantForPoint_ = function(parent, x, y) {
    +  var mx = parent.x + parent.w / 2;
    +  var my = parent.y + parent.h / 2;
    +  if (x < mx) {
    +    return y < my ? parent.nw : parent.sw;
    +  } else {
    +    return y < my ? parent.ne : parent.se;
    +  }
    +};
    +
    +
    +/**
    + * Sets the point for a node, as long as the node is a leaf or empty.
    + * @param {goog.structs.QuadTree.Node} node The node to set the point for.
    + * @param {goog.structs.QuadTree.Point} point The point to set.
    + * @private
    + */
    +goog.structs.QuadTree.prototype.setPointForNode_ = function(node, point) {
    +  if (node.nodeType == goog.structs.QuadTree.NodeType.POINTER) {
    +    throw Error('Can not set point for node of type POINTER');
    +  }
    +  node.nodeType = goog.structs.QuadTree.NodeType.LEAF;
    +  node.point = point;
    +};
    +
    +
    +/**
    + * Enumeration of node types.
    + * @enum {number}
    + */
    +goog.structs.QuadTree.NodeType = {
    +  EMPTY: 0,
    +  LEAF: 1,
    +  POINTER: 2
    +};
    +
    +
    +
    +/**
    + * Constructs a new quad tree node.
    + * @param {number} x X-coordiate of node.
    + * @param {number} y Y-coordinate of node.
    + * @param {number} w Width of node.
    + * @param {number} h Height of node.
    + * @param {goog.structs.QuadTree.Node=} opt_parent Optional parent node.
    + * @constructor
    + * @final
    + */
    +goog.structs.QuadTree.Node = function(x, y, w, h, opt_parent) {
    +  /**
    +   * The x-coordinate of the node.
    +   * @type {number}
    +   */
    +  this.x = x;
    +
    +  /**
    +   * The y-coordinate of the node.
    +   * @type {number}
    +   */
    +  this.y = y;
    +
    +  /**
    +   * The width of the node.
    +   * @type {number}
    +   */
    +  this.w = w;
    +
    +  /**
    +   * The height of the node.
    +   * @type {number}
    +   */
    +  this.h = h;
    +
    +  /**
    +   * The parent node.
    +   * @type {goog.structs.QuadTree.Node?}
    +   */
    +  this.parent = opt_parent || null;
    +};
    +
    +
    +/**
    + * The node's type.
    + * @type {goog.structs.QuadTree.NodeType}
    + */
    +goog.structs.QuadTree.Node.prototype.nodeType =
    +    goog.structs.QuadTree.NodeType.EMPTY;
    +
    +
    +/**
    + * The child node in the North-West quadrant.
    + * @type {goog.structs.QuadTree.Node?}
    + */
    +goog.structs.QuadTree.Node.prototype.nw = null;
    +
    +
    +/**
    + * The child node in the North-East quadrant.
    + * @type {goog.structs.QuadTree.Node?}
    + */
    +goog.structs.QuadTree.Node.prototype.ne = null;
    +
    +
    +/**
    + * The child node in the South-West quadrant.
    + * @type {goog.structs.QuadTree.Node?}
    + */
    +goog.structs.QuadTree.Node.prototype.sw = null;
    +
    +
    +/**
    + * The child node in the South-East quadrant.
    + * @type {goog.structs.QuadTree.Node?}
    + */
    +goog.structs.QuadTree.Node.prototype.se = null;
    +
    +
    +/**
    + * The point for the node, if it is a leaf node.
    + * @type {goog.structs.QuadTree.Point?}
    + */
    +goog.structs.QuadTree.Node.prototype.point = null;
    +
    +
    +
    +/**
    + * Creates a new point object.
    + * @param {number} x The x-coordinate of the point.
    + * @param {number} y The y-coordinate of the point.
    + * @param {*=} opt_value Optional value associated with the point.
    + * @constructor
    + * @final
    + */
    +goog.structs.QuadTree.Point = function(x, y, opt_value) {
    +  /**
    +   * The x-coordinate for the point.
    +   * @type {number}
    +   */
    +  this.x = x;
    +
    +  /**
    +   * The y-coordinate for the point.
    +   * @type {number}
    +   */
    +  this.y = y;
    +
    +  /**
    +   * Optional value associated with the point.
    +   * @type {*}
    +   */
    +  this.value = goog.isDef(opt_value) ? opt_value : null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.html b/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.html
    new file mode 100644
    index 0000000000..18456f088d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.QuadTree
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.QuadTreeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.js b/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.js
    new file mode 100644
    index 0000000000..2b30040343
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/quadtree_test.js
    @@ -0,0 +1,174 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.QuadTreeTest');
    +goog.setTestOnly('goog.structs.QuadTreeTest');
    +
    +goog.require('goog.structs');
    +goog.require('goog.structs.QuadTree');
    +goog.require('goog.testing.jsunit');
    +
    +function getTree() {
    +  var qt = new goog.structs.QuadTree(0, 0, 100, 100);
    +  qt.set(5, 20, 'Foo');
    +  qt.set(50, 32, 'Bar');
    +  qt.set(47, 96, 'Baz');
    +  qt.set(50, 50, 'Bing');
    +  qt.set(12, 0, 'Bong');
    +  return qt;
    +}
    +
    +function testGetCount() {
    +  var qt = getTree();
    +  assertEquals('Count should be 5', 5, qt.getCount());
    +  qt.remove(50, 32);
    +  assertEquals('Count should be 4', 4, qt.getCount());
    +}
    +
    +function testGetKeys() {
    +  var keys = getTree().getKeys();
    +  var keyString = keys.sort().join(' ');
    +  var expected = '(12, 0) (47, 96) (5, 20) (50, 32) (50, 50)';
    +  assertEquals('Sorted keys should be ' + expected, expected, keyString);
    +}
    +
    +function testGetValues() {
    +  var values = getTree().getValues();
    +  var valueString = values.sort().join(',');
    +  assertEquals('Sorted values should be Bar,Baz,Bing,Bong,Foo',
    +      'Bar,Baz,Bing,Bong,Foo', valueString);
    +}
    +
    +function testContains() {
    +  var qt = getTree();
    +  assertTrue('Should contain (5, 20)', qt.contains(5, 20));
    +  assertFalse('Should not contain (13, 13)', qt.contains(13, 13));
    +}
    +
    +function testClear() {
    +  var qt = getTree();
    +  qt.clear();
    +  assertTrue('Tree should be empty', qt.isEmpty());
    +  assertFalse('Tree should not contain (5, 20)', qt.contains(5, 20));
    +}
    +
    +function testConstructor() {
    +  var qt = new goog.structs.QuadTree(-10, -5, 6, 12);
    +  var root = qt.getRootNode();
    +  assertEquals('X of root should be -10', -10, root.x);
    +  assertEquals('Y of root should be -5', -5, root.y);
    +  assertEquals('Width of root should be 16', 16, root.w);
    +  assertEquals('Height of root should be 17', 17, root.h);
    +  assertTrue('Tree should be empty', qt.isEmpty());
    +}
    +
    +function testClone() {
    +  var qt = getTree().clone();
    +  assertFalse('Clone should not be empty', qt.isEmpty());
    +  assertTrue('Should contain (47, 96)', qt.contains(47, 96));
    +}
    +
    +function testRemove() {
    +  var qt = getTree();
    +  assertEquals('(5, 20) should be removed', 'Foo', qt.remove(5, 20));
    +  assertEquals('(5, 20) should be removed', 'Bar', qt.remove(50, 32));
    +  assertEquals('(5, 20) should be removed', 'Baz', qt.remove(47, 96));
    +  assertEquals('(5, 20) should be removed', 'Bing', qt.remove(50, 50));
    +  assertEquals('(5, 20) should be removed', 'Bong', qt.remove(12, 0));
    +  assertNull('(6, 6) wasn\'t there to remove', qt.remove(6, 6));
    +  assertTrue('Tree should be empty', qt.isEmpty());
    +  assertTreesChildrenAreNull(qt);
    +}
    +
    +function testIsEmpty() {
    +  var qt = getTree();
    +  qt.clear();
    +  assertTrue(qt.isEmpty());
    +  assertEquals('Root should  be empty node',
    +      goog.structs.QuadTree.NodeType.EMPTY, qt.getRootNode().nodeType);
    +  assertTreesChildrenAreNull(qt);
    +}
    +
    +function testForEach() {
    +  var qt = getTree();
    +  var s = '';
    +  goog.structs.forEach(qt, function(val, key, qt2) {
    +    assertNotUndefined(key);
    +    assertEquals(qt, qt2);
    +    s += key.x + ',' + key.y + '=' + val + ' ';
    +  });
    +  assertEquals('50,32=Bar 50,50=Bing 47,96=Baz 5,20=Foo 12,0=Bong ', s);
    +}
    +
    +function testBalancing() {
    +  var qt = new goog.structs.QuadTree(0, 0, 100, 100);
    +  var root = qt.getRootNode();
    +
    +  // Add a point to the NW quadrant.
    +  qt.set(25, 25, 'first');
    +
    +  assertEquals('Root should be a leaf node.',
    +      goog.structs.QuadTree.NodeType.LEAF, root.nodeType);
    +  assertTreesChildrenAreNull(qt);
    +
    +  assertEquals('first', root.point.value);
    +
    +  // Add another point in the NW quadrant
    +  qt.set(25, 30, 'second');
    +
    +  assertEquals('Root should now be a pointer.',
    +      goog.structs.QuadTree.NodeType.POINTER, root.nodeType);
    +  assertNotNull('NE should be not be null', root.ne);
    +  assertNotNull('NW should be not be null', root.nw);
    +  assertNotNull('SE should be not be null', root.se);
    +  assertNotNull('SW should be not be null', root.sw);
    +  assertNull(root.point);
    +
    +  // Delete the second point.
    +  qt.remove(25, 30);
    +
    +  assertEquals('Root should have been rebalanced and be a leaf node.',
    +      goog.structs.QuadTree.NodeType.LEAF, root.nodeType);
    +  assertTreesChildrenAreNull(qt);
    +  assertEquals('first', root.point.value);
    +}
    +
    +function testTreeBounds() {
    +  var qt = getTree();
    +  assertFails(qt, qt.set, [-10, -10, 1]);
    +  assertFails(qt, qt.set, [-10, 10, 2]);
    +  assertFails(qt, qt.set, [10, -10, 3]);
    +  assertFails(qt, qt.set, [-10, 110, 4]);
    +  assertFails(qt, qt.set, [10, 130, 5]);
    +  assertFails(qt, qt.set, [110, -10, 6]);
    +  assertFails(qt, qt.set, [150, 14, 7]);
    +}
    +
    +// Helper functions
    +
    +function assertFails(context, fn, args) {
    +  assertThrows('Exception expected from ' + fn.toString() +
    +      ' with arguments ' + args,
    +      function() {
    +        fn.apply(context, args);
    +      });
    +}
    +
    +function assertTreesChildrenAreNull(qt) {
    +  var root = qt.getRootNode();
    +  assertNull('NE should be null', root.ne);
    +  assertNull('NW should be null', root.nw);
    +  assertNull('SE should be null', root.se);
    +  assertNull('SW should be null', root.sw);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/queue.js b/mapbox-gl/closure-library/closure/goog/structs/queue.js
    new file mode 100644
    index 0000000000..8637945f2a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/queue.js
    @@ -0,0 +1,187 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Queue.
    + *
    + *
    + * This file provides the implementation of a FIFO Queue structure.
    + * API is similar to that of com.google.common.collect.IntQueue
    + *
    + * The implementation is a classic 2-stack queue.
    + * There's a "front" stack and a "back" stack.
    + * Items are pushed onto "back" and popped from "front".
    + * When "front" is empty, we replace "front" with reverse(back).
    + *
    + * Example:
    + * front                         back            op
    + * []                            []              enqueue 1
    + * []                            [1]             enqueue 2
    + * []                            [1,2]           enqueue 3
    + * []                            [1,2,3]         dequeue -> ...
    + * [3,2,1]                       []              ... -> 1
    + * [3,2]                         []              enqueue 4
    + * [3,2]                         [4]             dequeue -> 2
    + * [3]                           [4]
    + *
    + * Front and back are simple javascript arrays. We rely on
    + * Array.push and Array.pop being O(1) amortized.
    + *
    + * Note: In V8, queues, up to a certain size, can be implemented
    + * just fine using Array.push and Array.shift, but other JavaScript
    + * engines do not have the optimization of Array.shift.
    + *
    + */
    +
    +goog.provide('goog.structs.Queue');
    +
    +goog.require('goog.array');
    +
    +
    +
    +/**
    + * Class for FIFO Queue data structure.
    + *
    + * @constructor
    + * @template T
    + */
    +goog.structs.Queue = function() {
    +  /**
    +   * @private {!Array<T>} Front stack. Items are pop()'ed from here.
    +   */
    +  this.front_ = [];
    +  /**
    +   * @private {!Array<T>} Back stack. Items are push()'ed here.
    +   */
    +  this.back_ = [];
    +};
    +
    +
    +/**
    + * Flips the back stack onto the front stack if front is empty,
    + * to prepare for peek() or dequeue().
    + *
    + * @private
    + */
    +goog.structs.Queue.prototype.maybeFlip_ = function() {
    +  if (goog.array.isEmpty(this.front_)) {
    +    this.front_ = this.back_;
    +    this.front_.reverse();
    +    this.back_ = [];
    +  }
    +};
    +
    +
    +/**
    + * Puts the specified element on this queue.
    + * @param {T} element The element to be added to the queue.
    + */
    +goog.structs.Queue.prototype.enqueue = function(element) {
    +  this.back_.push(element);
    +};
    +
    +
    +/**
    + * Retrieves and removes the head of this queue.
    + * @return {T} The element at the head of this queue. Returns undefined if the
    + *     queue is empty.
    + */
    +goog.structs.Queue.prototype.dequeue = function() {
    +  this.maybeFlip_();
    +  return this.front_.pop();
    +};
    +
    +
    +/**
    + * Retrieves but does not remove the head of this queue.
    + * @return {T} The element at the head of this queue. Returns undefined if the
    + *     queue is empty.
    + */
    +goog.structs.Queue.prototype.peek = function() {
    +  this.maybeFlip_();
    +  return goog.array.peek(this.front_);
    +};
    +
    +
    +/**
    + * Returns the number of elements in this queue.
    + * @return {number} The number of elements in this queue.
    + */
    +goog.structs.Queue.prototype.getCount = function() {
    +  return this.front_.length + this.back_.length;
    +};
    +
    +
    +/**
    + * Returns true if this queue contains no elements.
    + * @return {boolean} true if this queue contains no elements.
    + */
    +goog.structs.Queue.prototype.isEmpty = function() {
    +  return goog.array.isEmpty(this.front_) &&
    +         goog.array.isEmpty(this.back_);
    +};
    +
    +
    +/**
    + * Removes all elements from the queue.
    + */
    +goog.structs.Queue.prototype.clear = function() {
    +  this.front_ = [];
    +  this.back_ = [];
    +};
    +
    +
    +/**
    + * Returns true if the given value is in the queue.
    + * @param {T} obj The value to look for.
    + * @return {boolean} Whether the object is in the queue.
    + */
    +goog.structs.Queue.prototype.contains = function(obj) {
    +  return goog.array.contains(this.front_, obj) ||
    +         goog.array.contains(this.back_, obj);
    +};
    +
    +
    +/**
    + * Removes the first occurrence of a particular value from the queue.
    + * @param {T} obj Object to remove.
    + * @return {boolean} True if an element was removed.
    + */
    +goog.structs.Queue.prototype.remove = function(obj) {
    +  // TODO(user): Implement goog.array.removeLast() and use it here.
    +  var index = goog.array.lastIndexOf(this.front_, obj);
    +  if (index < 0) {
    +    return goog.array.remove(this.back_, obj);
    +  }
    +  goog.array.removeAt(this.front_, index);
    +  return true;
    +};
    +
    +
    +/**
    + * Returns all the values in the queue.
    + * @return {!Array<T>} An array of the values in the queue.
    + */
    +goog.structs.Queue.prototype.getValues = function() {
    +  var res = [];
    +  // Add the front array in reverse, then the back array.
    +  for (var i = this.front_.length - 1; i >= 0; --i) {
    +    res.push(this.front_[i]);
    +  }
    +  var len = this.back_.length;
    +  for (var i = 0; i < len; ++i) {
    +    res.push(this.back_[i]);
    +  }
    +  return res;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/queue_perf.html b/mapbox-gl/closure-library/closure/goog/structs/queue_perf.html
    new file mode 100644
    index 0000000000..de83db3eb5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/queue_perf.html
    @@ -0,0 +1,126 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.structs.Queue</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css"/>
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.functions');
    +    goog.require('goog.structs.Queue');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.jsunit');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.structs.Queue Performance Tests</h1>
    +  <p>
    +    <strong>User-agent:</strong>
    +    <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +
    +  <script>
    +    var table = new goog.testing.PerformanceTable(
    +        goog.dom.getElement('perfTable'));
    +
    +    // Number of operations to measure in each table line.
    +    var OPS_COUNT = 10000;
    +
    +    var smallSizes = [1, 2, 5, 10, 20, 50, 100, 1000, 10000];
    +    var largeSizes = [100000];
    +
    +    function populateQueue(size) {
    +      var q = new goog.structs.Queue();
    +      for (var i = 0; i < size; ++i) {
    +        q.enqueue(i);
    +      }
    +      return q;
    +    }
    +
    +    function enqueueDequeueTest(size) {
    +      var q = populateQueue(size);
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; ++i) {
    +          q.dequeue();
    +          q.enqueue(i);
    +        }
    +      }, 'Enqueue and dequeue, size ' + size);
    +    }
    +
    +    function containsTest(size) {
    +      var q = populateQueue(size);
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; ++i) {
    +          q.contains(i);
    +        }
    +      }, 'Contains every element, size ' + size);
    +    }
    +
    +    function removeTest(size) {
    +      var q = populateQueue(size);
    +      if (size == 1) {
    +        return;
    +      }
    +      table.run(function() {
    +        var offset = Math.round(size / 2);
    +        for (var i = 0; i < OPS_COUNT; ++i) {
    +          q.remove(i + offset);
    +          q.enqueue(i + size);
    +        }
    +      }, 'Remove element from the middle, size ' + size);
    +    }
    +
    +    function getValuesTest(size) {
    +      var q = populateQueue(size);
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; ++i) {
    +          q.getValues()[size - 1];
    +        }
    +      }, 'Get values, size ' + size);
    +    }
    +
    +    function testEnqueueDequeueSmall() {
    +      goog.array.forEach(smallSizes, enqueueDequeueTest);
    +    }
    +
    +    function testEnqueueDequeueLarge() {
    +      goog.array.forEach(largeSizes, enqueueDequeueTest);
    +    }
    +
    +    function testContainsSmall() {
    +      goog.array.forEach(smallSizes, containsTest);
    +    }
    +
    +    function testContainsLarge() {
    +      goog.array.forEach(largeSizes, containsTest);
    +    }
    +
    +    function testRemoveSmall() {
    +      goog.array.forEach(smallSizes, removeTest);
    +    }
    +
    +    function testRemoveLarge() {
    +      goog.array.forEach(largeSizes, removeTest);
    +    }
    +
    +    function testGetValuesSmall() {
    +      goog.array.forEach(smallSizes, getValuesTest);
    +    }
    +
    +    function testGetValuesLarge() {
    +      goog.array.forEach(largeSizes, getValuesTest);
    +    }
    +
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/queue_test.html b/mapbox-gl/closure-library/closure/goog/structs/queue_test.html
    new file mode 100644
    index 0000000000..84adad0322
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/queue_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Queue
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.QueueTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/queue_test.js b/mapbox-gl/closure-library/closure/goog/structs/queue_test.js
    new file mode 100644
    index 0000000000..ea02c70a4c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/queue_test.js
    @@ -0,0 +1,161 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.QueueTest');
    +goog.setTestOnly('goog.structs.QueueTest');
    +
    +goog.require('goog.structs.Queue');
    +goog.require('goog.testing.jsunit');
    +
    +function stringifyQueue(q) {
    +  var values = q.getValues();
    +  var s = '';
    +  for (var i = 0; i < values.length; i++) {
    +    s += values[i];
    +  }
    +  return s;
    +}
    +
    +function createQueue() {
    +  var q = new goog.structs.Queue();
    +  q.enqueue('a');
    +  q.enqueue('b');
    +  q.enqueue('c');
    +  q.enqueue('a');
    +  q.dequeue();
    +  q.enqueue('b');
    +  q.enqueue('c');
    +  // q is now: bcabc
    +  return q;
    +}
    +
    +function testConstructor() {
    +  var q = new goog.structs.Queue();
    +  assertTrue('testConstructor(), queue should be empty initially', q.isEmpty());
    +  assertEquals('testConstructor(), count should be 0', q.getCount(), 0);
    +  assertEquals('testConstructor(), head element should be undefined', q.peek(),
    +      undefined);
    +}
    +
    +function testCount() {
    +  var q = createQueue();
    +  assertEquals('testCount(), count should be 5', q.getCount(), 5);
    +  q.enqueue('d');
    +  assertEquals('testCount(), count should be 6', q.getCount(), 6);
    +  q.dequeue();
    +  assertEquals('testCount(), count should be 5', q.getCount(), 5);
    +  q.clear();
    +  assertEquals('testCount(), count should be 0', q.getCount(), 0);
    +}
    +
    +function testEnqueue() {
    +  var q = new goog.structs.Queue();
    +  q.enqueue('a');
    +  assertEquals('testEnqueue(), count should be 1', q.getCount(), 1);
    +  q.enqueue('b');
    +  assertEquals('testEnqueue(), count should be 2', q.getCount(), 2);
    +  assertEquals('testEnqueue(), head element should be a', q.peek(), 'a');
    +  q.dequeue();
    +  assertEquals('testEnqueue(), count should be 1', q.getCount(), 1);
    +  assertEquals('testEnqueue(), head element should be b', q.peek(), 'b');
    +}
    +
    +function testDequeue() {
    +  var q = createQueue();
    +  assertEquals('testDequeue(), should return b', q.dequeue(), 'b');
    +  assertEquals('testDequeue(), should return b', q.dequeue(), 'c');
    +  assertEquals('testDequeue(), should return b', q.dequeue(), 'a');
    +  assertEquals('testDequeue(), should return b', q.dequeue(), 'b');
    +  assertEquals('testDequeue(), should return b', q.dequeue(), 'c');
    +  assertTrue('testDequeue(), queue should be empty', q.isEmpty());
    +  assertEquals('testDequeue(), should return undefined for empty queue',
    +      q.dequeue(), undefined);
    +}
    +
    +function testPeek() {
    +  var q = createQueue();
    +  assertEquals('testPeek(), should return b', q.peek(), 'b');
    +  assertEquals('testPeek(), dequeue should return peek() result',
    +      q.dequeue(), 'b');
    +  assertEquals('testPeek(), should return b', q.peek(), 'c');
    +  q.clear();
    +  assertEquals('testPeek(), should return undefined for empty queue',
    +      q.peek(), undefined);
    +}
    +
    +function testClear() {
    +  var q = createQueue();
    +  q.clear();
    +  assertTrue('testClear(), queue should be empty', q.isEmpty());
    +}
    +
    +function testQueue() {
    +  var q = createQueue();
    +  assertEquals('testQueue(), contents must be bcabc',
    +      stringifyQueue(q), 'bcabc');
    +}
    +
    +function testRemove() {
    +  var q = createQueue();
    +  assertEquals('testRemove(), contents must be bcabc',
    +      stringifyQueue(q), 'bcabc');
    +
    +  q.dequeue();
    +  assertEquals('testRemove(), contents must be cabc',
    +      stringifyQueue(q), 'cabc');
    +
    +  q.enqueue('a');
    +  assertEquals('testRemove(), contents must be cabca',
    +      stringifyQueue(q), 'cabca');
    +
    +  assertTrue('testRemove(), remove should have returned true', q.remove('c'));
    +  assertEquals('testRemove(), contents must be abca',
    +      stringifyQueue(q), 'abca');
    +
    +  assertTrue('testRemove(), remove should have returned true', q.remove('b'));
    +  assertEquals('testRemove(), contents must be aca', stringifyQueue(q), 'aca');
    +
    +  assertFalse('testRemove(), remove should have returned false', q.remove('b'));
    +  assertEquals('testRemove(), contents must be aca', stringifyQueue(q), 'aca');
    +
    +  assertTrue('testRemove(), remove should have returned true', q.remove('a'));
    +  assertEquals('testRemove(), contents must be ca', stringifyQueue(q), 'ca');
    +
    +  assertTrue('testRemove(), remove should have returned true', q.remove('a'));
    +  assertEquals('testRemove(), contents must be c', stringifyQueue(q), 'c');
    +
    +  assertTrue('testRemove(), remove should have returned true', q.remove('c'));
    +  assertEquals('testRemove(), contents must be empty', stringifyQueue(q), '');
    +
    +  q.enqueue('a');
    +  q.enqueue('b');
    +  q.enqueue('c');
    +  q.enqueue('a');
    +  q.dequeue();
    +  q.enqueue('b');
    +  q.enqueue('c');
    +  assertEquals('testRemove(), contents must be bcabc',
    +      stringifyQueue(q), 'bcabc');
    +  assertTrue('testRemove(), remove should have returned true', q.remove('c'));
    +  assertEquals('testRemove(), contents must be babc',
    +      stringifyQueue(q), 'babc');
    +}
    +
    +function testContains() {
    +  var q = createQueue();
    +  assertTrue('testContains(), contains should have returned true',
    +      q.contains('a'));
    +  assertFalse('testContains(), contains should have returned false',
    +      q.contains('foobar'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/set.js b/mapbox-gl/closure-library/closure/goog/structs/set.js
    new file mode 100644
    index 0000000000..334ecae7a8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/set.js
    @@ -0,0 +1,279 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Set.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + *
    + * This class implements a set data structure. Adding and removing is O(1). It
    + * supports both object and primitive values. Be careful because you can add
    + * both 1 and new Number(1), because these are not the same. You can even add
    + * multiple new Number(1) because these are not equal.
    + */
    +
    +
    +goog.provide('goog.structs.Set');
    +
    +goog.require('goog.structs');
    +goog.require('goog.structs.Collection');
    +goog.require('goog.structs.Map');
    +
    +
    +
    +/**
    + * A set that can contain both primitives and objects.  Adding and removing
    + * elements is O(1).  Primitives are treated as identical if they have the same
    + * type and convert to the same string.  Objects are treated as identical only
    + * if they are references to the same object.  WARNING: A goog.structs.Set can
    + * contain both 1 and (new Number(1)), because they are not the same.  WARNING:
    + * Adding (new Number(1)) twice will yield two distinct elements, because they
    + * are two different objects.  WARNING: Any object that is added to a
    + * goog.structs.Set will be modified!  Because goog.getUid() is used to
    + * identify objects, every object in the set will be mutated.
    + * @param {Array<T>|Object<?,T>=} opt_values Initial values to start with.
    + * @constructor
    + * @implements {goog.structs.Collection<T>}
    + * @final
    + * @template T
    + */
    +goog.structs.Set = function(opt_values) {
    +  this.map_ = new goog.structs.Map;
    +  if (opt_values) {
    +    this.addAll(opt_values);
    +  }
    +};
    +
    +
    +/**
    + * Obtains a unique key for an element of the set.  Primitives will yield the
    + * same key if they have the same type and convert to the same string.  Object
    + * references will yield the same key only if they refer to the same object.
    + * @param {*} val Object or primitive value to get a key for.
    + * @return {string} A unique key for this value/object.
    + * @private
    + */
    +goog.structs.Set.getKey_ = function(val) {
    +  var type = typeof val;
    +  if (type == 'object' && val || type == 'function') {
    +    return 'o' + goog.getUid(/** @type {Object} */ (val));
    +  } else {
    +    return type.substr(0, 1) + val;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The number of elements in the set.
    + * @override
    + */
    +goog.structs.Set.prototype.getCount = function() {
    +  return this.map_.getCount();
    +};
    +
    +
    +/**
    + * Add a primitive or an object to the set.
    + * @param {T} element The primitive or object to add.
    + * @override
    + */
    +goog.structs.Set.prototype.add = function(element) {
    +  this.map_.set(goog.structs.Set.getKey_(element), element);
    +};
    +
    +
    +/**
    + * Adds all the values in the given collection to this set.
    + * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
    + *     containing the elements to add.
    + */
    +goog.structs.Set.prototype.addAll = function(col) {
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  for (var i = 0; i < l; i++) {
    +    this.add(values[i]);
    +  }
    +};
    +
    +
    +/**
    + * Removes all values in the given collection from this set.
    + * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
    + *     containing the elements to remove.
    + */
    +goog.structs.Set.prototype.removeAll = function(col) {
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  for (var i = 0; i < l; i++) {
    +    this.remove(values[i]);
    +  }
    +};
    +
    +
    +/**
    + * Removes the given element from this set.
    + * @param {T} element The primitive or object to remove.
    + * @return {boolean} Whether the element was found and removed.
    + * @override
    + */
    +goog.structs.Set.prototype.remove = function(element) {
    +  return this.map_.remove(goog.structs.Set.getKey_(element));
    +};
    +
    +
    +/**
    + * Removes all elements from this set.
    + */
    +goog.structs.Set.prototype.clear = function() {
    +  this.map_.clear();
    +};
    +
    +
    +/**
    + * Tests whether this set is empty.
    + * @return {boolean} True if there are no elements in this set.
    + */
    +goog.structs.Set.prototype.isEmpty = function() {
    +  return this.map_.isEmpty();
    +};
    +
    +
    +/**
    + * Tests whether this set contains the given element.
    + * @param {T} element The primitive or object to test for.
    + * @return {boolean} True if this set contains the given element.
    + * @override
    + */
    +goog.structs.Set.prototype.contains = function(element) {
    +  return this.map_.containsKey(goog.structs.Set.getKey_(element));
    +};
    +
    +
    +/**
    + * Tests whether this set contains all the values in a given collection.
    + * Repeated elements in the collection are ignored, e.g.  (new
    + * goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
    + * @param {goog.structs.Collection<T>|Object} col A collection-like object.
    + * @return {boolean} True if the set contains all elements.
    + */
    +goog.structs.Set.prototype.containsAll = function(col) {
    +  return goog.structs.every(col, this.contains, this);
    +};
    +
    +
    +/**
    + * Finds all values that are present in both this set and the given collection.
    + * @param {Array<S>|Object<?,S>} col A collection.
    + * @return {!goog.structs.Set<T|S>} A new set containing all the values
    + *     (primitives or objects) present in both this set and the given
    + *     collection.
    + * @template S
    + */
    +goog.structs.Set.prototype.intersection = function(col) {
    +  var result = new goog.structs.Set();
    +
    +  var values = goog.structs.getValues(col);
    +  for (var i = 0; i < values.length; i++) {
    +    var value = values[i];
    +    if (this.contains(value)) {
    +      result.add(value);
    +    }
    +  }
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Finds all values that are present in this set and not in the given
    + * collection.
    + * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection.
    + * @return {!goog.structs.Set} A new set containing all the values
    + *     (primitives or objects) present in this set but not in the given
    + *     collection.
    + */
    +goog.structs.Set.prototype.difference = function(col) {
    +  var result = this.clone();
    +  result.removeAll(col);
    +  return result;
    +};
    +
    +
    +/**
    + * Returns an array containing all the elements in this set.
    + * @return {!Array<T>} An array containing all the elements in this set.
    + */
    +goog.structs.Set.prototype.getValues = function() {
    +  return this.map_.getValues();
    +};
    +
    +
    +/**
    + * Creates a shallow clone of this set.
    + * @return {!goog.structs.Set<T>} A new set containing all the same elements as
    + *     this set.
    + */
    +goog.structs.Set.prototype.clone = function() {
    +  return new goog.structs.Set(this);
    +};
    +
    +
    +/**
    + * Tests whether the given collection consists of the same elements as this set,
    + * regardless of order, without repetition.  Primitives are treated as equal if
    + * they have the same type and convert to the same string; objects are treated
    + * as equal if they are references to the same object.  This operation is O(n).
    + * @param {goog.structs.Collection<T>|Object} col A collection.
    + * @return {boolean} True if the given collection consists of the same elements
    + *     as this set, regardless of order, without repetition.
    + */
    +goog.structs.Set.prototype.equals = function(col) {
    +  return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
    +};
    +
    +
    +/**
    + * Tests whether the given collection contains all the elements in this set.
    + * Primitives are treated as equal if they have the same type and convert to the
    + * same string; objects are treated as equal if they are references to the same
    + * object.  This operation is O(n).
    + * @param {goog.structs.Collection<T>|Object} col A collection.
    + * @return {boolean} True if this set is a subset of the given collection.
    + */
    +goog.structs.Set.prototype.isSubsetOf = function(col) {
    +  var colCount = goog.structs.getCount(col);
    +  if (this.getCount() > colCount) {
    +    return false;
    +  }
    +  // TODO(user) Find the minimal collection size where the conversion makes
    +  // the contains() method faster.
    +  if (!(col instanceof goog.structs.Set) && colCount > 5) {
    +    // Convert to a goog.structs.Set so that goog.structs.contains runs in
    +    // O(1) time instead of O(n) time.
    +    col = new goog.structs.Set(col);
    +  }
    +  return goog.structs.every(this, function(value) {
    +    return goog.structs.contains(col, value);
    +  });
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the elements in this set.
    + * @param {boolean=} opt_keys This argument is ignored.
    + * @return {!goog.iter.Iterator} An iterator over the elements in this set.
    + */
    +goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
    +  return this.map_.__iterator__(false);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/set_perf.html b/mapbox-gl/closure-library/closure/goog/structs/set_perf.html
    new file mode 100644
    index 0000000000..76d3d87470
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/set_perf.html
    @@ -0,0 +1,267 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.ui.Set vs goog.ui.StringSet</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css"/>
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.functions');
    +    goog.require('goog.string');
    +    goog.require('goog.structs.Set');
    +    goog.require('goog.structs.StringSet');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.PropertyReplacer');
    +    goog.require('goog.testing.jsunit');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.ui.Set and goog.ui.StringSet Performance Tests</h1>
    +  <p>
    +    <strong>User-agent:</strong>
    +    <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +
    +  <script>
    +    var table = new goog.testing.PerformanceTable(
    +        goog.dom.getElement('perfTable'));
    +
    +    // Number of operations to measure in each table line.
    +    var OPS_COUNT = 100000;
    +
    +    var stubs = new goog.testing.PropertyReplacer();
    +
    +    function tearDown() {
    +      stubs.reset();
    +    }
    +
    +    function testCreateSetFromArrayWithoutRepetition() {
    +      var values = []
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        values.push(i);
    +      }
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet(values);
    +      }, 'Create string set from number array without repetition');
    +
    +      values = []
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        values.push(String(i));
    +      }
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet(values);
    +      }, 'Create string set from string array without repetition');
    +    }
    +
    +    function testCreateSetWithoutRepetition() {
    +      table.run(function() {
    +        var s = new goog.structs.Set();
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          s.add(i);
    +        }
    +      }, 'Add elements to set without repetition');
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet();
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          s.add(i);
    +        }
    +      }, 'Add elements to string set without repetition');
    +
    +      stubs.replace(goog.structs.StringSet, 'encode_', goog.functions.identity);
    +      stubs.replace(goog.structs.StringSet, 'decode_', goog.functions.identity);
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet();
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          s.add(i);
    +        }
    +      }, 'Add elements to string set without repetition and escaping');
    +    }
    +
    +    function testCreateSetWithRepetition() {
    +      table.run(function() {
    +        var s = new goog.structs.Set();
    +        for (var n = 0; n < 10 ; n++) {
    +          for (var i = 0; i < OPS_COUNT / 10; i++) {
    +            s.add(i);
    +          }
    +        }
    +      }, 'Add elements to set with repetition');
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet();
    +        for (var n = 0; n < 10; n++) {
    +          for (var i = 0; i < OPS_COUNT / 10; i++) {
    +            s.add(i);
    +          }
    +        }
    +      }, 'Add elements to string set with repetition');
    +    }
    +
    +    function testGetCount() {
    +      var bigSet = new goog.structs.Set;
    +      var bigStringSet = new goog.structs.StringSet;
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        bigSet.add(i);
    +        bigStringSet.add(i);
    +      }
    +
    +      table.run(function() {
    +        bigSet.getCount();
    +      }, 'Count the number of elements in a set');
    +
    +      table.run(function() {
    +        bigStringSet.getCount();
    +      }, 'Count the number of elements in a string set');
    +    }
    +
    +    function testGetValues() {
    +      var bigSet = new goog.structs.Set;
    +      var bigStringSet = new goog.structs.StringSet;
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        bigSet.add(i);
    +        bigStringSet.add(i);
    +      }
    +
    +      table.run(function() {
    +        bigSet.getValues();
    +      }, 'Convert a set to array');
    +
    +      table.run(function() {
    +        bigStringSet.getValues();
    +      }, 'Convert a string set to array');
    +    }
    +
    +    function testForEach() {
    +      var bigSet = new goog.structs.Set;
    +      var bigStringSet = new goog.structs.StringSet;
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        bigSet.add(i);
    +        bigStringSet.add(i);
    +      }
    +
    +      table.run(function() {
    +        goog.structs.forEach(bigSet, goog.nullFunction);
    +      }, 'Iterate over set with forEach');
    +
    +      table.run(function() {
    +        goog.structs.forEach(bigStringSet, goog.nullFunction);
    +      }, 'Iterate over string set with forEach');
    +    }
    +
    +    function testForEachWithLargeKeys() {
    +      var bigSet = new goog.structs.Set;
    +      var bigStringSet = new goog.structs.StringSet;
    +      for (var i = 0; i < OPS_COUNT / 100; i++) {
    +        bigSet.add(goog.string.repeat(String(i), 1000));
    +        bigStringSet.add(goog.string.repeat(String(i), 1000));
    +      }
    +
    +      table.run(function() {
    +        for (var i = 0; i < 100; i++) {
    +          goog.structs.forEach(bigSet, goog.nullFunction);
    +        }
    +      }, 'Iterate over set of large strings with forEach');
    +
    +      table.run(function() {
    +        for (var i = 0; i < 100; i++) {
    +          goog.structs.forEach(bigStringSet, goog.nullFunction);
    +        }
    +      }, 'Iterate over string set of large strings with forEach');
    +    }
    +
    +    function testAddRemove() {
    +      table.run(function() {
    +        var s = new goog.structs.Set();
    +        for (var i = 0; i < OPS_COUNT / 2; i++) {
    +          s.add(i);
    +        }
    +        for (var i = 0; i < OPS_COUNT / 2; i++) {
    +          s.remove(i);
    +        }
    +      }, 'Add then remove elements from set');
    +
    +      table.run(function() {
    +        var s = new goog.structs.StringSet();
    +        for (var i = 0; i < OPS_COUNT / 2; i++) {
    +          s.add(i);
    +        }
    +        for (var i = 0; i < OPS_COUNT / 2; i++) {
    +          s.remove(i);
    +        }
    +      }, 'Add then remove elements from string set');
    +    }
    +
    +    function testContains() {
    +      var bigSet = new goog.structs.Set;
    +      var bigStringSet = new goog.structs.StringSet;
    +      var arr = [];
    +      for (var i = 0; i < OPS_COUNT; i++) {
    +        bigSet.add(i);
    +        bigStringSet.add(i);
    +        arr.push(i);
    +      }
    +
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          bigSet.contains(i);
    +        }
    +      }, 'Membership check for each element of set');
    +
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          bigStringSet.contains(i);
    +        }
    +      }, 'Membership check for each element of string set with contains');
    +
    +      table.run(function() {
    +        bigStringSet.containsArray(arr);
    +      }, 'Membership check for each element of string set with containsArray');
    +
    +      stubs.replace(goog.structs.StringSet, 'encode_', goog.functions.identity);
    +      stubs.replace(goog.structs.StringSet, 'decode_', goog.functions.identity);
    +
    +      table.run(function() {
    +        for (var i = 0; i < OPS_COUNT; i++) {
    +          bigStringSet.contains(i);
    +        }
    +      }, 'Membership check for each element of string set without escaping');
    +    }
    +
    +    function testEquals() {
    +      table.run(function() {
    +        var s1 = new goog.structs.Set();
    +        var s2 = new goog.structs.Set();
    +        for (var i = 0; i < OPS_COUNT / 4; i++) {
    +          s1.add(i);
    +          s2.add(i);
    +        }
    +        s1.equals(s2);
    +      }, 'Create then compare two sets');
    +
    +      table.run(function() {
    +        var s1 = new goog.structs.StringSet();
    +        var s2 = new goog.structs.StringSet();
    +        for (var i = 0; i < OPS_COUNT / 4; i++) {
    +          s1.add(i);
    +          s2.add(i);
    +        }
    +        s1.equals(s2);
    +      }, 'Create then compare two string sets');
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/set_test.html b/mapbox-gl/closure-library/closure/goog/structs/set_test.html
    new file mode 100644
    index 0000000000..cf5fe850c4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/set_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Set
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.SetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/set_test.js b/mapbox-gl/closure-library/closure/goog/structs/set_test.js
    new file mode 100644
    index 0000000000..e21e89dba2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/set_test.js
    @@ -0,0 +1,579 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.SetTest');
    +goog.setTestOnly('goog.structs.SetTest');
    +
    +goog.require('goog.iter');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Set');
    +goog.require('goog.testing.jsunit');
    +
    +var Set = goog.structs.Set;
    +
    +function stringifySet(s) {
    +  return goog.structs.getValues(s).join('');
    +}
    +
    +function testGetCount() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  assertEquals('count, should be 3', s.getCount(), 3);
    +  var d = new String('d'); s.add(d);
    +  assertEquals('count, should be 4', s.getCount(), 4);
    +  s.remove(d);
    +  assertEquals('count, should be 3', s.getCount(), 3);
    +
    +  s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  assertEquals('count, should be 3', s.getCount(), 3);
    +  s.add('d');
    +  assertEquals('count, should be 4', s.getCount(), 4);
    +  s.remove('d');
    +  assertEquals('count, should be 3', s.getCount(), 3);
    +}
    +
    +function testGetValues() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +  assertEquals(s.getValues().join(''), 'abcd');
    +
    +  var s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +  assertEquals(s.getValues().join(''), 'abcd');
    +}
    +
    +function testContains() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +  var e = new String('e');
    +
    +  assertTrue("contains, Should contain 'a'", s.contains(a));
    +  assertFalse("contains, Should not contain 'e'", s.contains(e));
    +
    +  s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +
    +  assertTrue("contains, Should contain 'a'", s.contains('a'));
    +  assertFalse("contains, Should not contain 'e'", s.contains('e'));
    +}
    +
    +function testContainsFunctionValue() {
    +  var s = new Set;
    +
    +  var fn1 = function() {};
    +
    +  assertFalse(s.contains(fn1));
    +  s.add(fn1);
    +  assertTrue(s.contains(fn1));
    +
    +  var fn2 = function() {};
    +
    +  assertFalse(s.contains(fn2));
    +  s.add(fn2);
    +  assertTrue(s.contains(fn2));
    +
    +  assertEquals(s.getCount(), 2);
    +}
    +
    +function testContainsAll() {
    +  var set = new Set([1, 2, 3]);
    +
    +  assertTrue('{1, 2, 3} contains []', set.containsAll([]));
    +  assertTrue('{1, 2, 3} contains [1]', set.containsAll([1]));
    +  assertTrue('{1, 2, 3} contains [1, 1]', set.containsAll([1, 1]));
    +  assertTrue('{1, 2, 3} contains [3, 2, 1]', set.containsAll([3, 2, 1]));
    +  assertFalse("{1, 2, 3} doesn't contain [4]", set.containsAll([4]));
    +  assertFalse("{1, 2, 3} doesn't contain [1, 4]", set.containsAll([1, 4]));
    +
    +  assertTrue('{1, 2, 3} contains {a: 1}', set.containsAll({a: 1}));
    +  assertFalse("{1, 2, 3} doesn't contain {a: 4}", set.containsAll({a: 4}));
    +
    +  assertTrue('{1, 2, 3} contains {1}', set.containsAll(new Set([1])));
    +  assertFalse("{1, 2, 3} doesn't contain {4}", set.containsAll(new Set([4])));
    +}
    +
    +function testIntersection() {
    +  var emptySet = new Set;
    +
    +  assertTrue('intersection of empty set and [] should be empty',
    +      emptySet.intersection([]).isEmpty());
    +  assertIntersection('intersection of 2 empty sets should be empty',
    +      emptySet, new Set(), new Set());
    +
    +  var abcdSet = new Set();
    +  abcdSet.add('a');
    +  abcdSet.add('b');
    +  abcdSet.add('c');
    +  abcdSet.add('d');
    +
    +  assertTrue('intersection of populated set and [] should be empty',
    +      abcdSet.intersection([]).isEmpty());
    +  assertIntersection(
    +      'intersection of populated set and empty set should be empty',
    +      abcdSet, new Set(), new Set());
    +
    +  var bcSet = new Set(['b', 'c']);
    +  assertIntersection('intersection of [a,b,c,d] and [b,c]',
    +      abcdSet, bcSet, bcSet);
    +
    +  var bceSet = new Set(['b', 'c', 'e']);
    +  assertIntersection('intersection of [a,b,c,d] and [b,c,e]',
    +      abcdSet, bceSet, bcSet);
    +}
    +
    +function testDifference() {
    +  var emptySet = new Set;
    +
    +  assertTrue('difference of empty set and [] should be empty',
    +      emptySet.difference([]).isEmpty());
    +  assertTrue('difference of 2 empty sets should be empty',
    +      emptySet.difference(new Set()).isEmpty());
    +
    +  var abcdSet = new Set(['a', 'b', 'c', 'd']);
    +
    +  assertTrue('difference of populated set and [] should be the populated set',
    +      abcdSet.difference([]).equals(abcdSet));
    +  assertTrue(
    +      'difference of populated set and empty set should be the populated set',
    +      abcdSet.difference(new Set()).equals(abcdSet));
    +  assertTrue('difference of two identical sets should be the empty set',
    +      abcdSet.difference(abcdSet).equals(new Set()));
    +
    +  var bcSet = new Set(['b', 'c']);
    +  assertTrue('difference of [a,b,c,d] and [b,c] shoule be [a,d]',
    +      abcdSet.difference(bcSet).equals(new Set(['a', 'd'])));
    +  assertTrue('difference of [b,c] and [a,b,c,d] should be the empty set',
    +      bcSet.difference(abcdSet).equals(new Set()));
    +
    +  var xyzSet = new Set(['x', 'y', 'z']);
    +  assertTrue('difference of [a,b,c,d] and [x,y,z] should be the [a,b,c,d]',
    +      abcdSet.difference(xyzSet).equals(abcdSet));
    +}
    +
    +
    +/**
    + * Helper function to assert intersection is commutative.
    + */
    +function assertIntersection(msg, set1, set2, expectedIntersection) {
    +  assertTrue(msg + ': set1->set2',
    +      set1.intersection(set2).equals(expectedIntersection));
    +  assertTrue(msg + ': set2->set1',
    +      set2.intersection(set1).equals(expectedIntersection));
    +}
    +
    +function testRemoveAll() {
    +  assertRemoveAll('removeAll of empty set from empty set', [], [], []);
    +  assertRemoveAll('removeAll of empty set from populated set',
    +      ['a', 'b', 'c', 'd'], [], ['a', 'b', 'c', 'd']);
    +  assertRemoveAll('removeAll of [a,d] from [a,b,c,d]',
    +      ['a', 'b', 'c', 'd'], ['a', 'd'], ['b', 'c']);
    +  assertRemoveAll('removeAll of [b,c] from [a,b,c,d]',
    +      ['a', 'b', 'c', 'd'], ['b', 'c'], ['a', 'd']);
    +  assertRemoveAll('removeAll of [b,c,e] from [a,b,c,d]',
    +      ['a', 'b', 'c', 'd'], ['b', 'c', 'e'], ['a', 'd']);
    +  assertRemoveAll('removeAll of [a,b,c,d] from [a,d]',
    +      ['a', 'd'], ['a', 'b', 'c', 'd'], []);
    +  assertRemoveAll('removeAll of [a,b,c,d] from [b,c]',
    +      ['b', 'c'], ['a', 'b', 'c', 'd'], []);
    +  assertRemoveAll('removeAll of [a,b,c,d] from [b,c,e]',
    +      ['b', 'c', 'e'], ['a', 'b', 'c', 'd'], ['e']);
    +}
    +
    +
    +/**
    + * Helper function to test removeAll.
    + */
    +function assertRemoveAll(msg, elements1, elements2, expectedResult) {
    +  var set1 = new Set(elements1);
    +  var set2 = new Set(elements2);
    +  set1.removeAll(set2);
    +
    +  assertTrue(msg + ': set1 count increased after removeAll',
    +      elements1.length >= set1.getCount());
    +  assertEquals(msg + ': set2 count changed after removeAll',
    +      elements2.length, set2.getCount());
    +  assertTrue(msg + ': wrong set1 after removeAll', set1.equals(expectedResult));
    +  assertIntersection(msg + ': non-empty intersection after removeAll',
    +      set1, set2, []);
    +}
    +
    +function testAdd() {
    +  var s = new Set;
    +  var a = new String('a');
    +  var b = new String('b');
    +  s.add(a);
    +  assertTrue(s.contains(a));
    +  s.add(b);
    +  assertTrue(s.contains(b));
    +
    +  s = new Set;
    +  s.add('a');
    +  assertTrue(s.contains('a'));
    +  s.add('b');
    +  assertTrue(s.contains('b'));
    +  s.add(null);
    +  assertTrue('contains null', s.contains(null));
    +  assertFalse('does not contain "null"', s.contains('null'));
    +}
    +
    +
    +function testClear() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +  s.clear();
    +  assertTrue('cleared so it should be empty', s.isEmpty());
    +  assertTrue("cleared so it should not contain 'a' key", !s.contains(a));
    +
    +  s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +  s.clear();
    +  assertTrue('cleared so it should be empty', s.isEmpty());
    +  assertTrue("cleared so it should not contain 'a' key", !s.contains('a'));
    +}
    +
    +
    +function testAddAll() {
    +  var s = new Set;
    +  var a = new String('a');
    +  var b = new String('b');
    +  var c = new String('c');
    +  var d = new String('d');
    +  s.addAll([a, b, c, d]);
    +  assertTrue('addAll so it should not be empty', !s.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", s.contains(c));
    +
    +  var s2 = new Set;
    +  s2.addAll(s);
    +  assertTrue('addAll so it should not be empty', !s2.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", s2.contains(c));
    +
    +
    +  s = new Set;
    +  s.addAll(['a', 'b', 'c', 'd']);
    +  assertTrue('addAll so it should not be empty', !s.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", s.contains('c'));
    +
    +  s2 = new Set;
    +  s2.addAll(s);
    +  assertTrue('addAll so it should not be empty', !s2.isEmpty());
    +  assertTrue("addAll so it should contain 'c' key", s2.contains('c'));
    +}
    +
    +
    +function testConstructor() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +  var s2 = new Set(s);
    +  assertFalse('constr with Set so it should not be empty', s2.isEmpty());
    +  assertTrue('constr with Set so it should contain c', s2.contains(c));
    +
    +  s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +  s2 = new Set(s);
    +  assertFalse('constr with Set so it should not be empty', s2.isEmpty());
    +  assertTrue('constr with Set so it should contain c', s2.contains('c'));
    +}
    +
    +
    +function testClone() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +
    +  var s2 = s.clone();
    +  assertFalse('clone so it should not be empty', s2.isEmpty());
    +  assertTrue("clone so it should contain 'c' key", s2.contains(c));
    +
    +  var s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +
    +  s2 = s.clone();
    +  assertFalse('clone so it should not be empty', s2.isEmpty());
    +  assertTrue("clone so it should contain 'c' key", s2.contains('c'));
    +}
    +
    +
    +/**
    + * Helper method for testEquals().
    + * @param {Object} a First element to use in the tests.
    + * @param {Object} b Second element to use in the tests.
    + * @param {Object} c Third element to use in the tests.
    + * @param {Object} d Fourth element to use in the tests.
    + */
    +function helperForTestEquals(a, b, c, d) {
    +  var s = new Set([a, b, c]);
    +
    +  assertTrue('set == itself', s.equals(s));
    +  assertTrue('set == same set', s.equals(new Set([a, b, c])));
    +  assertTrue('set == its clone', s.equals(s.clone()));
    +  assertTrue('set == array of same elements', s.equals([a, b, c]));
    +  assertTrue('set == array of same elements in different order',
    +             s.equals([c, b, a]));
    +
    +  assertFalse('set != empty set', s.equals(new Set));
    +  assertFalse('set != its subset', s.equals(new Set([a, c])));
    +  assertFalse('set != its superset',
    +              s.equals(new Set([a, b, c, d])));
    +  assertFalse('set != different set',
    +              s.equals(new Set([b, c, d])));
    +  assertFalse('set != its subset as array', s.equals([a, c]));
    +  assertFalse('set != its superset as array', s.equals([a, b, c, d]));
    +  assertFalse('set != different set as array', s.equals([b, c, d]));
    +  assertFalse('set != [a, b, c, c]', s.equals([a, b, c, c]));
    +  assertFalse('set != [a, b, b]', s.equals([a, b, b]));
    +  assertFalse('set != [a, a]', s.equals([a, a]));
    +}
    +
    +
    +function testEquals() {
    +  helperForTestEquals(1, 2, 3, 4);
    +  helperForTestEquals('a', 'b', 'c', 'd');
    +  helperForTestEquals(new String('a'), new String('b'),
    +                      new String('c'), new String('d'));
    +}
    +
    +
    +/**
    + * Helper method for testIsSubsetOf().
    + * @param {Object} a First element to use in the tests.
    + * @param {Object} b Second element to use in the tests.
    + * @param {Object} c Third element to use in the tests.
    + * @param {Object} d Fourth element to use in the tests.
    + */
    +function helperForTestIsSubsetOf(a, b, c, d) {
    +  var s = new Set([a, b, c]);
    +
    +  assertTrue('set <= itself', s.isSubsetOf(s));
    +  assertTrue('set <= same set',
    +             s.isSubsetOf(new Set([a, b, c])));
    +  assertTrue('set <= its clone', s.isSubsetOf(s.clone()));
    +  assertTrue('set <= array of same elements', s.isSubsetOf([a, b, c]));
    +  assertTrue('set <= array of same elements in different order',
    +             s.equals([c, b, a]));
    +
    +  assertTrue('set <= Set([a, b, c, d])',
    +             s.isSubsetOf(new Set([a, b, c, d])));
    +  assertTrue('set <= [a, b, c, d]', s.isSubsetOf([a, b, c, d]));
    +  assertTrue('set <= [a, b, c, c]', s.isSubsetOf([a, b, c, c]));
    +
    +  assertFalse('set !<= Set([a, b])',
    +              s.isSubsetOf(new Set([a, b])));
    +  assertFalse('set !<= [a, b]', s.isSubsetOf([a, b]));
    +  assertFalse('set !<= Set([c, d])',
    +              s.isSubsetOf(new Set([c, d])));
    +  assertFalse('set !<= [c, d]', s.isSubsetOf([c, d]));
    +  assertFalse('set !<= Set([a, c, d])',
    +              s.isSubsetOf(new Set([a, c, d])));
    +  assertFalse('set !<= [a, c, d]', s.isSubsetOf([a, c, d]));
    +  assertFalse('set !<= [a, a, b]', s.isSubsetOf([a, a, b]));
    +  assertFalse('set !<= [a, a, b, b]', s.isSubsetOf([a, a, b, b]));
    +}
    +
    +
    +function testIsSubsetOf() {
    +  helperForTestIsSubsetOf(1, 2, 3, 4);
    +  helperForTestIsSubsetOf('a', 'b', 'c', 'd');
    +  helperForTestIsSubsetOf(new String('a'), new String('b'),
    +                          new String('c'), new String('d'));
    +}
    +
    +
    +function testForEach() {
    +  var s = new Set;
    +  var a = new String('a'); s.add(a);
    +  var b = new String('b'); s.add(b);
    +  var c = new String('c'); s.add(c);
    +  var d = new String('d'); s.add(d);
    +  var str = '';
    +  goog.structs.forEach(s, function(val, key, set) {
    +    assertUndefined(key);
    +    assertEquals(s, set);
    +    str += val;
    +  });
    +  assertEquals(str, 'abcd');
    +
    +  s = new Set;
    +  s.add('a');
    +  s.add('b');
    +  s.add('c');
    +  s.add('d');
    +  str = '';
    +  goog.structs.forEach(s, function(val, key, set) {
    +    assertUndefined(key);
    +    assertEquals(s, set);
    +    str += val;
    +  });
    +  assertEquals(str, 'abcd');
    +}
    +
    +
    +function testFilter() {
    +  var s = new Set;
    +  var a = new Number(0); s.add(a);
    +  var b = new Number(1); s.add(b);
    +  var c = new Number(2); s.add(c);
    +  var d = new Number(3); s.add(d);
    +
    +  var s2 = goog.structs.filter(s, function(val, key, set) {
    +    assertUndefined(key);
    +    assertEquals(s, set);
    +    return val > 1;
    +  });
    +  assertEquals(stringifySet(s2), '23');
    +
    +  s = new Set;
    +  s.add(0);
    +  s.add(1);
    +  s.add(2);
    +  s.add(3);
    +
    +  s2 = goog.structs.filter(s, function(val, key, set) {
    +    assertUndefined(key);
    +    assertEquals(s, set);
    +    return val > 1;
    +  });
    +  assertEquals(stringifySet(s2), '23');
    +}
    +
    +
    +function testSome() {
    +  var s = new Set;
    +  var a = new Number(0); s.add(a);
    +  var b = new Number(1); s.add(b);
    +  var c = new Number(2); s.add(c);
    +  var d = new Number(3); s.add(d);
    +
    +  var b = goog.structs.some(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 1;
    +  });
    +  assertTrue(b);
    +  var b = goog.structs.some(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 100;
    +  });
    +  assertFalse(b);
    +
    +  s = new Set;
    +  s.add(0);
    +  s.add(1);
    +  s.add(2);
    +  s.add(3);
    +
    +  b = goog.structs.some(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 1;
    +  });
    +  assertTrue(b);
    +  b = goog.structs.some(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 100;
    +  });
    +  assertFalse(b);
    +}
    +
    +
    +function testEvery() {
    +  var s = new Set;
    +  var a = new Number(0); s.add(a);
    +  var b = new Number(1); s.add(b);
    +  var c = new Number(2); s.add(c);
    +  var d = new Number(3); s.add(d);
    +
    +  var b = goog.structs.every(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val >= 0;
    +  });
    +  assertTrue(b);
    +  b = goog.structs.every(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 1;
    +  });
    +  assertFalse(b);
    +
    +  s = new Set;
    +  s.add(0);
    +  s.add(1);
    +  s.add(2);
    +  s.add(3);
    +
    +  b = goog.structs.every(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val >= 0;
    +  });
    +  assertTrue(b);
    +  b = goog.structs.every(s, function(val, key, s2) {
    +    assertUndefined(key);
    +    assertEquals(s, s2);
    +    return val > 1;
    +  });
    +  assertFalse(b);
    +}
    +
    +function testIterator() {
    +  var s = new Set;
    +  s.add(0);
    +  s.add(1);
    +  s.add(2);
    +  s.add(3);
    +  s.add(4);
    +
    +  assertEquals('01234', goog.iter.join(s, ''));
    +
    +  s.remove(1);
    +  s.remove(3);
    +
    +  assertEquals('024', goog.iter.join(s, ''));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/simplepool.js b/mapbox-gl/closure-library/closure/goog/structs/simplepool.js
    new file mode 100644
    index 0000000000..ff400fccbc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/simplepool.js
    @@ -0,0 +1,200 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Pool.
    + *
    + *
    + * A generic class for handling pools of objects that is more efficient than
    + * goog.structs.Pool because it doesn't maintain a list of objects that are in
    + * use. See constructor comment.
    + */
    +
    +
    +goog.provide('goog.structs.SimplePool');
    +
    +goog.require('goog.Disposable');
    +
    +
    +
    +/**
    + * A generic pool class. Simpler and more efficient than goog.structs.Pool
    + * because it doesn't maintain a list of objects that are in use. This class
    + * has constant overhead and doesn't create any additional objects as part of
    + * the pool management after construction time.
    + *
    + * IMPORTANT: If the objects being pooled are arrays or maps that can have
    + * unlimited number of properties, they need to be cleaned before being
    + * returned to the pool.
    + *
    + * Also note that {@see goog.object.clean} actually allocates an array to clean
    + * the object passed to it, so simply using this function would defy the
    + * purpose of using the pool.
    + *
    + * @param {number} initialCount Initial number of objects to populate the free
    + *     pool at construction time.
    + * @param {number} maxCount Maximum number of objects to keep in the free pool.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @template T
    + */
    +goog.structs.SimplePool = function(initialCount, maxCount) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Function for overriding createObject. The avoids a common case requiring
    +   * subclassing this class.
    +   * @private {Function}
    +   */
    +  this.createObjectFn_ = null;
    +
    +  /**
    +   * Function for overriding disposeObject. The avoids a common case requiring
    +   * subclassing this class.
    +   * @private {Function}
    +   */
    +  this.disposeObjectFn_ = null;
    +
    +  /**
    +   * Maximum number of objects allowed
    +   * @private {number}
    +   */
    +  this.maxCount_ = maxCount;
    +
    +  /**
    +   * Queue used to store objects that are currently in the pool and available
    +   * to be used.
    +   * @private {Array<T>}
    +   */
    +  this.freeQueue_ = [];
    +
    +  this.createInitial_(initialCount);
    +};
    +goog.inherits(goog.structs.SimplePool, goog.Disposable);
    +
    +
    +/**
    + * Sets the {@code createObject} function which is used for creating a new
    + * object in the pool.
    + * @param {Function} createObjectFn Create object function which returns the
    + *     newly created object.
    + */
    +goog.structs.SimplePool.prototype.setCreateObjectFn = function(createObjectFn) {
    +  this.createObjectFn_ = createObjectFn;
    +};
    +
    +
    +/**
    + * Sets the {@code disposeObject} function which is used for disposing of an
    + * object in the pool.
    + * @param {Function} disposeObjectFn Dispose object function which takes the
    + *     object to dispose as a parameter.
    + */
    +goog.structs.SimplePool.prototype.setDisposeObjectFn = function(
    +    disposeObjectFn) {
    +  this.disposeObjectFn_ = disposeObjectFn;
    +};
    +
    +
    +/**
    + * Gets an unused object from the the pool, if there is one available,
    + * otherwise creates a new one.
    + * @return {T} An object from the pool or a new one if necessary.
    + */
    +goog.structs.SimplePool.prototype.getObject = function() {
    +  if (this.freeQueue_.length) {
    +    return this.freeQueue_.pop();
    +  }
    +  return this.createObject();
    +};
    +
    +
    +/**
    + * Returns an object to the pool so that it can be reused. If the pool is
    + * already full, the object is disposed instead.
    + * @param {T} obj The object to release.
    + */
    +goog.structs.SimplePool.prototype.releaseObject = function(obj) {
    +  if (this.freeQueue_.length < this.maxCount_) {
    +    this.freeQueue_.push(obj);
    +  } else {
    +    this.disposeObject(obj);
    +  }
    +};
    +
    +
    +/**
    + * Populates the pool with initialCount objects.
    + * @param {number} initialCount The number of objects to add to the pool.
    + * @private
    + */
    +goog.structs.SimplePool.prototype.createInitial_ = function(initialCount) {
    +  if (initialCount > this.maxCount_) {
    +    throw Error('[goog.structs.SimplePool] Initial cannot be greater than max');
    +  }
    +  for (var i = 0; i < initialCount; i++) {
    +    this.freeQueue_.push(this.createObject());
    +  }
    +};
    +
    +
    +/**
    + * Should be overridden by sub-classes to return an instance of the object type
    + * that is expected in the pool.
    + * @return {T} The created object.
    + */
    +goog.structs.SimplePool.prototype.createObject = function() {
    +  if (this.createObjectFn_) {
    +    return this.createObjectFn_();
    +  } else {
    +    return {};
    +  }
    +};
    +
    +
    +/**
    + * Should be overrideen to dispose of an object. Default implementation is to
    + * remove all of the object's members, which should render it useless. Calls the
    + *  object's dispose method, if available.
    + * @param {T} obj The object to dispose.
    + */
    +goog.structs.SimplePool.prototype.disposeObject = function(obj) {
    +  if (this.disposeObjectFn_) {
    +    this.disposeObjectFn_(obj);
    +  } else if (goog.isObject(obj)) {
    +    if (goog.isFunction(obj.dispose)) {
    +      obj.dispose();
    +    } else {
    +      for (var i in obj) {
    +        delete obj[i];
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Disposes of the pool and all objects currently held in the pool.
    + * @override
    + * @protected
    + */
    +goog.structs.SimplePool.prototype.disposeInternal = function() {
    +  goog.structs.SimplePool.superClass_.disposeInternal.call(this);
    +  // Call disposeObject on each object held by the pool.
    +  var freeQueue = this.freeQueue_;
    +  while (freeQueue.length) {
    +    this.disposeObject(freeQueue.pop());
    +  }
    +  delete this.freeQueue_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/stringset.js b/mapbox-gl/closure-library/closure/goog/structs/stringset.js
    new file mode 100644
    index 0000000000..6c6cf3bc62
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/stringset.js
    @@ -0,0 +1,405 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Data structure for set of strings.
    + *
    + *
    + * This class implements a set data structure for strings. Adding and removing
    + * is O(1). It doesn't contain any bloat from {@link goog.structs.Set}, i.e.
    + * it isn't optimized for IE6 garbage collector (see the description of
    + * {@link goog.structs.Map#keys_} for details), and it distinguishes its
    + * elements by their string value not by hash code.
    + * The implementation assumes that no new keys are added to Object.prototype.
    + */
    +
    +goog.provide('goog.structs.StringSet');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.iter');
    +
    +
    +
    +/**
    + * Creates a set of strings.
    + * @param {!Array<?>=} opt_elements Elements to add to the set. The non-string
    + *     items will be converted to strings, so 15 and '15' will mean the same.
    + * @constructor
    + * @final
    + */
    +goog.structs.StringSet = function(opt_elements) {
    +  /**
    +   * An object storing the escaped elements of the set in its keys.
    +   * @type {!Object}
    +   * @private
    +   */
    +  this.elements_ = {};
    +
    +  if (opt_elements) {
    +    for (var i = 0; i < opt_elements.length; i++) {
    +      this.elements_[goog.structs.StringSet.encode_(opt_elements[i])] = null;
    +    }
    +  }
    +
    +  goog.asserts.assertObjectPrototypeIsIntact();
    +};
    +
    +
    +/**
    + * Empty object. Referring to it is faster than creating a new empty object in
    + * {@code goog.structs.StringSet.encode_}.
    + * @const {!Object}
    + * @private
    + */
    +goog.structs.StringSet.EMPTY_OBJECT_ = {};
    +
    +
    +/**
    + * The '__proto__' and the '__count__' keys aren't enumerable in Firefox, and
    + * 'toString', 'valueOf', 'constructor', etc. aren't enumerable in IE so they
    + * have to be escaped before they are added to the internal object.
    + * NOTE: When a new set is created, 50-80% of the CPU time is spent in encode.
    + * @param {*} element The element to escape.
    + * @return {*} The escaped element or the element itself if it doesn't have to
    + *     be escaped.
    + * @private
    + */
    +goog.structs.StringSet.encode_ = function(element) {
    +  return element in goog.structs.StringSet.EMPTY_OBJECT_ ||
    +      String(element).charCodeAt(0) == 32 ? ' ' + element : element;
    +};
    +
    +
    +/**
    + * Inverse function of {@code goog.structs.StringSet.encode_}.
    + * NOTE: forEach would be 30% faster in FF if the compiler inlined decode.
    + * @param {string} key The escaped element used as the key of the internal
    + *     object.
    + * @return {string} The unescaped element.
    + * @private
    + */
    +goog.structs.StringSet.decode_ = function(key) {
    +  return key.charCodeAt(0) == 32 ? key.substr(1) : key;
    +};
    +
    +
    +/**
    + * Adds a single element to the set.
    + * @param {*} element The element to add. It will be converted to string.
    + */
    +goog.structs.StringSet.prototype.add = function(element) {
    +  this.elements_[goog.structs.StringSet.encode_(element)] = null;
    +};
    +
    +
    +/**
    + * Adds a the elements of an array to this set.
    + * @param {!Array<?>} arr The array to add the elements of.
    + */
    +goog.structs.StringSet.prototype.addArray = function(arr) {
    +  for (var i = 0; i < arr.length; i++) {
    +    this.elements_[goog.structs.StringSet.encode_(arr[i])] = null;
    +  }
    +};
    +
    +
    +/**
    + * Adds the elements which are in {@code set1} but not in {@code set2} to this
    + * set.
    + * @param {!goog.structs.StringSet} set1 First set.
    + * @param {!goog.structs.StringSet} set2 Second set.
    + * @private
    + */
    +goog.structs.StringSet.prototype.addDifference_ = function(set1, set2) {
    +  for (var key in set1.elements_) {
    +    if (!(key in set2.elements_)) {
    +      this.elements_[key] = null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adds a the elements of a set to this set.
    + * @param {!goog.structs.StringSet} stringSet The set to add the elements of.
    + */
    +goog.structs.StringSet.prototype.addSet = function(stringSet) {
    +  for (var key in stringSet.elements_) {
    +    this.elements_[key] = null;
    +  }
    +};
    +
    +
    +/**
    + * Removes all elements of the set.
    + */
    +goog.structs.StringSet.prototype.clear = function() {
    +  this.elements_ = {};
    +};
    +
    +
    +/**
    + * @return {!goog.structs.StringSet} Clone of the set.
    + */
    +goog.structs.StringSet.prototype.clone = function() {
    +  var ret = new goog.structs.StringSet;
    +  ret.addSet(this);
    +  return ret;
    +};
    +
    +
    +/**
    + * Tells if the set contains the given element.
    + * @param {*} element The element to check.
    + * @return {boolean} Whether it is in the set.
    + */
    +goog.structs.StringSet.prototype.contains = function(element) {
    +  return goog.structs.StringSet.encode_(element) in this.elements_;
    +};
    +
    +
    +/**
    + * Tells if the set contains all elements of the array.
    + * @param {!Array<?>} arr The elements to check.
    + * @return {boolean} Whether they are in the set.
    + */
    +goog.structs.StringSet.prototype.containsArray = function(arr) {
    +  for (var i = 0; i < arr.length; i++) {
    +    if (!(goog.structs.StringSet.encode_(arr[i]) in this.elements_)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Tells if this set has the same elements as the given set.
    + * @param {!goog.structs.StringSet} stringSet The other set.
    + * @return {boolean} Whether they have the same elements.
    + */
    +goog.structs.StringSet.prototype.equals = function(stringSet) {
    +  return this.isSubsetOf(stringSet) && stringSet.isSubsetOf(this);
    +};
    +
    +
    +/**
    + * Calls a function for each element in the set.
    + * @param {function(string, undefined, !goog.structs.StringSet)} f The function
    + *     to call for every element. It takes the element, undefined (because sets
    + *     have no notion of keys), and the set.
    + * @param {Object=} opt_obj The object to be used as the value of 'this'
    + *     within {@code f}.
    + */
    +goog.structs.StringSet.prototype.forEach = function(f, opt_obj) {
    +  for (var key in this.elements_) {
    +    f.call(opt_obj, goog.structs.StringSet.decode_(key), undefined, this);
    +  }
    +};
    +
    +
    +/**
    + * Counts the number of elements in the set in linear time.
    + * NOTE: getCount is always called at most once per set instance in google3.
    + * If this usage pattern won't change, the linear getCount implementation is
    + * better, because
    + * <li>populating a set and getting the number of elements in it takes the same
    + * amount of time as keeping a count_ member up to date and getting its value;
    + * <li>if getCount is not called, adding and removing elements have no overhead.
    + * @return {number} The number of elements in the set.
    + */
    +goog.structs.StringSet.prototype.getCount = Object.keys ?
    +    function() {
    +      return Object.keys(this.elements_).length;
    +    } :
    +    function() {
    +      var count = 0;
    +      for (var key in this.elements_) {
    +        count++;
    +      }
    +      return count;
    +    };
    +
    +
    +/**
    + * Calculates the difference of two sets.
    + * @param {!goog.structs.StringSet} stringSet The set to subtract from this set.
    + * @return {!goog.structs.StringSet} {@code this} minus {@code stringSet}.
    + */
    +goog.structs.StringSet.prototype.getDifference = function(stringSet) {
    +  var ret = new goog.structs.StringSet;
    +  ret.addDifference_(this, stringSet);
    +  return ret;
    +};
    +
    +
    +/**
    + * Calculates the intersection of this set with another set.
    + * @param {!goog.structs.StringSet} stringSet The set to take the intersection
    + *     with.
    + * @return {!goog.structs.StringSet} A new set with the common elements.
    + */
    +goog.structs.StringSet.prototype.getIntersection = function(stringSet) {
    +  var ret = new goog.structs.StringSet;
    +  for (var key in this.elements_) {
    +    if (key in stringSet.elements_) {
    +      ret.elements_[key] = null;
    +    }
    +  }
    +  return ret;
    +};
    +
    +
    +/**
    + * Calculates the symmetric difference of two sets.
    + * @param {!goog.structs.StringSet} stringSet The other set.
    + * @return {!goog.structs.StringSet} A new set with the elements in exactly one
    + *     of {@code this} and {@code stringSet}.
    + */
    +goog.structs.StringSet.prototype.getSymmetricDifference = function(stringSet) {
    +  var ret = new goog.structs.StringSet;
    +  ret.addDifference_(this, stringSet);
    +  ret.addDifference_(stringSet, this);
    +  return ret;
    +};
    +
    +
    +/**
    + * Calculates the union of this set and another set.
    + * @param {!goog.structs.StringSet} stringSet The set to take the union with.
    + * @return {!goog.structs.StringSet} A new set with the union of elements.
    + */
    +goog.structs.StringSet.prototype.getUnion = function(stringSet) {
    +  var ret = this.clone();
    +  ret.addSet(stringSet);
    +  return ret;
    +};
    +
    +
    +/**
    + * @return {!Array<string>} The elements of the set.
    + */
    +goog.structs.StringSet.prototype.getValues = Object.keys ?
    +    function() {
    +      // Object.keys was introduced in JavaScript 1.8.5, Array#map in 1.6.
    +      return Object.keys(this.elements_).map(
    +          goog.structs.StringSet.decode_, this);
    +    } :
    +    function() {
    +      var ret = [];
    +      for (var key in this.elements_) {
    +        ret.push(goog.structs.StringSet.decode_(key));
    +      }
    +      return ret;
    +    };
    +
    +
    +/**
    + * Tells if this set and the given set are disjoint.
    + * @param {!goog.structs.StringSet} stringSet The other set.
    + * @return {boolean} True iff they don't have common elements.
    + */
    +goog.structs.StringSet.prototype.isDisjoint = function(stringSet) {
    +  for (var key in this.elements_) {
    +    if (key in stringSet.elements_) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the set is empty.
    + */
    +goog.structs.StringSet.prototype.isEmpty = function() {
    +  for (var key in this.elements_) {
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Tells if this set is the subset of the given set.
    + * @param {!goog.structs.StringSet} stringSet The other set.
    + * @return {boolean} Whether this set if the subset of that.
    + */
    +goog.structs.StringSet.prototype.isSubsetOf = function(stringSet) {
    +  for (var key in this.elements_) {
    +    if (!(key in stringSet.elements_)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Tells if this set is the superset of the given set.
    + * @param {!goog.structs.StringSet} stringSet The other set.
    + * @return {boolean} Whether this set if the superset of that.
    + */
    +goog.structs.StringSet.prototype.isSupersetOf = function(stringSet) {
    +  return stringSet.isSubsetOf(this);
    +};
    +
    +
    +/**
    + * Removes a single element from the set.
    + * @param {*} element The element to remove.
    + * @return {boolean} Whether the element was in the set.
    + */
    +goog.structs.StringSet.prototype.remove = function(element) {
    +  var key = goog.structs.StringSet.encode_(element);
    +  if (key in this.elements_) {
    +    delete this.elements_[key];
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Removes all elements of the given array from this set.
    + * @param {!Array<?>} arr The elements to remove.
    + */
    +goog.structs.StringSet.prototype.removeArray = function(arr) {
    +  for (var i = 0; i < arr.length; i++) {
    +    delete this.elements_[goog.structs.StringSet.encode_(arr[i])];
    +  }
    +};
    +
    +
    +/**
    + * Removes all elements of the given set from this set.
    + * @param {!goog.structs.StringSet} stringSet The set of elements to remove.
    + */
    +goog.structs.StringSet.prototype.removeSet = function(stringSet) {
    +  for (var key in stringSet.elements_) {
    +    delete this.elements_[key];
    +  }
    +};
    +
    +
    +/**
    + * Returns an iterator that iterates over the elements in the set.
    + * NOTE: creating the iterator copies the whole set so use {@link #forEach} when
    + * possible.
    + * @param {boolean=} opt_keys Ignored for sets.
    + * @return {!goog.iter.Iterator} An iterator over the elements in the set.
    + */
    +goog.structs.StringSet.prototype.__iterator__ = function(opt_keys) {
    +  return goog.iter.toIterator(this.getValues());
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/stringset_test.html b/mapbox-gl/closure-library/closure/goog/structs/stringset_test.html
    new file mode 100644
    index 0000000000..131202c761
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/stringset_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.StringSet
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.StringSetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/stringset_test.js b/mapbox-gl/closure-library/closure/goog/structs/stringset_test.js
    new file mode 100644
    index 0000000000..d7499e02dd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/stringset_test.js
    @@ -0,0 +1,259 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.StringSetTest');
    +goog.setTestOnly('goog.structs.StringSetTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.iter');
    +goog.require('goog.structs.StringSet');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_VALUES = [
    +  '', ' ', '  ', 'true', 'null', 'undefined', '0', 'new', 'constructor',
    +  'prototype', '__proto__', 'set', 'hasOwnProperty', 'toString', 'valueOf'
    +];
    +
    +var TEST_VALUES_WITH_DUPLICATES = [
    +  '', '', ' ', '  ', 'true', true, 'null', null, 'undefined', undefined, '0', 0,
    +  'new', 'constructor', 'prototype', '__proto__', 'set', 'hasOwnProperty',
    +  'toString', 'valueOf'
    +];
    +
    +function testConstructor() {
    +  var empty = new goog.structs.StringSet;
    +  assertSameElements('elements in empty set', [], empty.getValues());
    +
    +  var s = new goog.structs.StringSet(TEST_VALUES_WITH_DUPLICATES);
    +  assertSameElements('duplicates are filtered out by their string value',
    +      TEST_VALUES, s.getValues());
    +}
    +
    +function testConstructorAssertsThatObjectPrototypeHasNoEnumerableKeys() {
    +  assertNotThrows(goog.structs.StringSet);
    +  Object.prototype.foo = 0;
    +  try {
    +    assertThrows(goog.structs.StringSet);
    +  } finally {
    +    delete Object.prototype.foo;
    +  }
    +  assertNotThrows(goog.structs.StringSet);
    +}
    +
    +function testOverridingObjectPrototypeToStringIsSafe() {
    +  var originalToString = Object.prototype.toString;
    +  Object.prototype.toString = function() {
    +    return 'object';
    +  };
    +  try {
    +    assertEquals(0, new goog.structs.StringSet().getCount());
    +    assertFalse(new goog.structs.StringSet().contains('toString'));
    +  } finally {
    +    Object.prototype.toString = originalToString;
    +  }
    +}
    +
    +function testAdd() {
    +  var s = new goog.structs.StringSet;
    +  goog.array.forEach(TEST_VALUES_WITH_DUPLICATES, s.add, s);
    +  assertSameElements(TEST_VALUES, s.getValues());
    +}
    +
    +function testAddArray() {
    +  var s = new goog.structs.StringSet;
    +  s.addArray(TEST_VALUES_WITH_DUPLICATES);
    +  assertSameElements('added elements from array', TEST_VALUES, s.getValues());
    +}
    +
    +function testAddSet() {
    +  var s = new goog.structs.StringSet;
    +  s.addSet(new goog.structs.StringSet([1, 2]));
    +  assertSameElements('empty set + {1, 2}', ['1', '2'], s.getValues());
    +  s.addSet(new goog.structs.StringSet([2, 3]));
    +  assertSameElements('{1, 2} + {2, 3}', ['1', '2', '3'], s.getValues());
    +}
    +
    +function testClear() {
    +  var s = new goog.structs.StringSet([1, 2]);
    +  s.clear();
    +  assertSameElements('cleared set', [], s.getValues());
    +}
    +
    +function testClone() {
    +  var s = new goog.structs.StringSet([1, 2]);
    +  var c = s.clone();
    +  assertSameElements('elements in clone', ['1', '2'], c.getValues());
    +  s.add(3);
    +  assertSameElements('changing the original set does not affect the clone',
    +      ['1', '2'], c.getValues());
    +}
    +
    +function testContains() {
    +  var e = new goog.structs.StringSet;
    +  goog.array.forEach(TEST_VALUES, function(element) {
    +    assertFalse('empty set does not contain ' + element, e.contains(element));
    +  });
    +
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  goog.array.forEach(TEST_VALUES_WITH_DUPLICATES, function(element) {
    +    assertTrue('s contains ' + element, s.contains(element));
    +  });
    +  assertFalse('s does not contain 42', s.contains(42));
    +}
    +
    +function testContainsArray() {
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  assertTrue('set contains empty array', s.containsArray([]));
    +  assertTrue('set contains all elements of itself with some duplication',
    +      s.containsArray(TEST_VALUES_WITH_DUPLICATES));
    +  assertFalse('set does not contain 42', s.containsArray([42]));
    +}
    +
    +function testEquals() {
    +  var s = new goog.structs.StringSet([1, 2]);
    +  assertTrue('set equals to itself', s.equals(s));
    +  assertTrue('set equals to its clone', s.equals(s.clone()));
    +  assertFalse('set does not equal to its subset',
    +      s.equals(new goog.structs.StringSet([1])));
    +  assertFalse('set does not equal to its superset',
    +      s.equals(new goog.structs.StringSet([1, 2, 3])));
    +}
    +
    +function testForEach() {
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  var values = [];
    +  var context = {};
    +  s.forEach(function(value, key, stringSet) {
    +    assertEquals('context of forEach callback', context, this);
    +    values.push(value);
    +    assertUndefined('key argument of forEach callback', key);
    +    assertEquals('set argument of forEach callback', s, stringSet);
    +  }, context);
    +  assertSameElements('values passed to forEach callback', TEST_VALUES, values);
    +}
    +
    +function testGetCount() {
    +  var empty = new goog.structs.StringSet;
    +  assertEquals('count(empty set)', 0, empty.getCount());
    +
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  assertEquals('count(non-empty set)', TEST_VALUES.length, s.getCount());
    +}
    +
    +function testGetDifference() {
    +  var s1 = new goog.structs.StringSet([1, 2]);
    +  var s2 = new goog.structs.StringSet([2, 3]);
    +  assertSameElements('{1, 2} - {2, 3}', ['1'],
    +      s1.getDifference(s2).getValues());
    +}
    +
    +function testGetIntersection() {
    +  var s1 = new goog.structs.StringSet([1, 2]);
    +  var s2 = new goog.structs.StringSet([2, 3]);
    +  assertSameElements('{1, 2} * {2, 3}', ['2'],
    +      s1.getIntersection(s2).getValues());
    +}
    +
    +function testGetSymmetricDifference() {
    +  var s1 = new goog.structs.StringSet([1, 2]);
    +  var s2 = new goog.structs.StringSet([2, 3]);
    +  assertSameElements('{1, 2} sym.diff. {2, 3}', ['1', '3'],
    +      s1.getSymmetricDifference(s2).getValues());
    +}
    +
    +function testGetUnion() {
    +  var s1 = new goog.structs.StringSet([1, 2]);
    +  var s2 = new goog.structs.StringSet([2, 3]);
    +  assertSameElements('{1, 2} + {2, 3}', ['1', '2', '3'],
    +      s1.getUnion(s2).getValues());
    +}
    +
    +function testIsDisjoint() {
    +  var s = new goog.structs.StringSet;
    +  var s12 = new goog.structs.StringSet([1, 2]);
    +  var s23 = new goog.structs.StringSet([2, 3]);
    +  var s34 = new goog.structs.StringSet([3, 4]);
    +
    +  assertTrue('{} and {1, 2} are disjoint', s.isDisjoint(s12));
    +  assertTrue('{1, 2} and {3, 4} are disjoint', s12.isDisjoint(s34));
    +  assertFalse('{1, 2} and {2, 3} are not disjoint', s12.isDisjoint(s23));
    +}
    +
    +function testIsEmpty() {
    +  assertTrue('empty set', new goog.structs.StringSet().isEmpty());
    +  assertFalse('non-empty set', new goog.structs.StringSet(['']).isEmpty());
    +}
    +
    +function testIsSubsetOf() {
    +  var s1 = new goog.structs.StringSet([1]);
    +  var s12 = new goog.structs.StringSet([1, 2]);
    +  var s123 = new goog.structs.StringSet([1, 2, 3]);
    +  var s23 = new goog.structs.StringSet([2, 3]);
    +
    +  assertTrue('{1, 2} is subset of {1, 2}', s12.isSubsetOf(s12));
    +  assertTrue('{1, 2} is subset of {1, 2, 3}', s12.isSubsetOf(s123));
    +  assertFalse('{1, 2} is not subset of {1}', s12.isSubsetOf(s1));
    +  assertFalse('{1, 2} is not subset of {2, 3}', s12.isSubsetOf(s23));
    +}
    +
    +function testIsSupersetOf() {
    +  var s1 = new goog.structs.StringSet([1]);
    +  var s12 = new goog.structs.StringSet([1, 2]);
    +  var s123 = new goog.structs.StringSet([1, 2, 3]);
    +  var s23 = new goog.structs.StringSet([2, 3]);
    +
    +  assertTrue('{1, 2} is superset of {1}', s12.isSupersetOf(s1));
    +  assertTrue('{1, 2} is superset of {1, 2}', s12.isSupersetOf(s12));
    +  assertFalse('{1, 2} is not superset of {1, 2, 3}', s12.isSupersetOf(s123));
    +  assertFalse('{1, 2} is not superset of {2, 3}', s12.isSupersetOf(s23));
    +}
    +
    +function testRemove() {
    +  var n = new goog.structs.StringSet([1, 2]);
    +  assertFalse('3 not removed from {1, 2}', n.remove(3));
    +  assertSameElements('set == {1, 2}', ['1', '2'], n.getValues());
    +  assertTrue('2 removed from {1, 2}', n.remove(2));
    +  assertSameElements('set == {1}', ['1'], n.getValues());
    +  assertTrue('"1" removed from {1}', n.remove('1'));
    +  assertSameElements('set == {}', [], n.getValues());
    +
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  goog.array.forEach(TEST_VALUES, s.remove, s);
    +  assertSameElements('all special values have been removed', [], s.getValues());
    +}
    +
    +function testRemoveArray() {
    +  var s = new goog.structs.StringSet(TEST_VALUES);
    +  s.removeArray(TEST_VALUES.slice(0, TEST_VALUES.length - 2));
    +  assertSameElements('removed elements from array',
    +      TEST_VALUES.slice(TEST_VALUES.length - 2), s.getValues());
    +}
    +
    +function testRemoveSet() {
    +  var s1 = new goog.structs.StringSet([1, 2]);
    +  var s2 = new goog.structs.StringSet([2, 3]);
    +  s1.removeSet(s2);
    +  assertSameElements('{1, 2} - {2, 3}', ['1'], s1.getValues());
    +}
    +
    +function testIterator() {
    +  var s = new goog.structs.StringSet(TEST_VALUES_WITH_DUPLICATES);
    +  var values = [];
    +  goog.iter.forEach(s, function(value) {
    +    values.push(value);
    +  });
    +  assertSameElements('__iterator__ takes all elements once',
    +      TEST_VALUES, values);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/structs.js b/mapbox-gl/closure-library/closure/goog/structs/structs.js
    new file mode 100644
    index 0000000000..2744f6f5e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/structs.js
    @@ -0,0 +1,354 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generics method for collection-like classes and objects.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + *
    + * This file contains functions to work with collections. It supports using
    + * Map, Set, Array and Object and other classes that implement collection-like
    + * methods.
    + */
    +
    +
    +goog.provide('goog.structs');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +
    +
    +// We treat an object as a dictionary if it has getKeys or it is an object that
    +// isn't arrayLike.
    +
    +
    +/**
    + * Returns the number of values in the collection-like object.
    + * @param {Object} col The collection-like object.
    + * @return {number} The number of values in the collection-like object.
    + */
    +goog.structs.getCount = function(col) {
    +  if (typeof col.getCount == 'function') {
    +    return col.getCount();
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return col.length;
    +  }
    +  return goog.object.getCount(col);
    +};
    +
    +
    +/**
    + * Returns the values of the collection-like object.
    + * @param {Object} col The collection-like object.
    + * @return {!Array<?>} The values in the collection-like object.
    + */
    +goog.structs.getValues = function(col) {
    +  if (typeof col.getValues == 'function') {
    +    return col.getValues();
    +  }
    +  if (goog.isString(col)) {
    +    return col.split('');
    +  }
    +  if (goog.isArrayLike(col)) {
    +    var rv = [];
    +    var l = col.length;
    +    for (var i = 0; i < l; i++) {
    +      rv.push(col[i]);
    +    }
    +    return rv;
    +  }
    +  return goog.object.getValues(col);
    +};
    +
    +
    +/**
    + * Returns the keys of the collection. Some collections have no notion of
    + * keys/indexes and this function will return undefined in those cases.
    + * @param {Object} col The collection-like object.
    + * @return {!Array|undefined} The keys in the collection.
    + */
    +goog.structs.getKeys = function(col) {
    +  if (typeof col.getKeys == 'function') {
    +    return col.getKeys();
    +  }
    +  // if we have getValues but no getKeys we know this is a key-less collection
    +  if (typeof col.getValues == 'function') {
    +    return undefined;
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    var rv = [];
    +    var l = col.length;
    +    for (var i = 0; i < l; i++) {
    +      rv.push(i);
    +    }
    +    return rv;
    +  }
    +
    +  return goog.object.getKeys(col);
    +};
    +
    +
    +/**
    + * Whether the collection contains the given value. This is O(n) and uses
    + * equals (==) to test the existence.
    + * @param {Object} col The collection-like object.
    + * @param {*} val The value to check for.
    + * @return {boolean} True if the map contains the value.
    + */
    +goog.structs.contains = function(col, val) {
    +  if (typeof col.contains == 'function') {
    +    return col.contains(val);
    +  }
    +  if (typeof col.containsValue == 'function') {
    +    return col.containsValue(val);
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.contains(/** @type {!Array<?>} */ (col), val);
    +  }
    +  return goog.object.containsValue(col, val);
    +};
    +
    +
    +/**
    + * Whether the collection is empty.
    + * @param {Object} col The collection-like object.
    + * @return {boolean} True if empty.
    + */
    +goog.structs.isEmpty = function(col) {
    +  if (typeof col.isEmpty == 'function') {
    +    return col.isEmpty();
    +  }
    +
    +  // We do not use goog.string.isEmptyOrWhitespace because here we treat the string as
    +  // collection and as such even whitespace matters
    +
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.isEmpty(/** @type {!Array<?>} */ (col));
    +  }
    +  return goog.object.isEmpty(col);
    +};
    +
    +
    +/**
    + * Removes all the elements from the collection.
    + * @param {Object} col The collection-like object.
    + */
    +goog.structs.clear = function(col) {
    +  // NOTE(arv): This should not contain strings because strings are immutable
    +  if (typeof col.clear == 'function') {
    +    col.clear();
    +  } else if (goog.isArrayLike(col)) {
    +    goog.array.clear(/** @type {goog.array.ArrayLike} */ (col));
    +  } else {
    +    goog.object.clear(col);
    +  }
    +};
    +
    +
    +/**
    + * Calls a function for each value in a collection. The function takes
    + * three arguments; the value, the key and the collection.
    + *
    + * NOTE: This will be deprecated soon! Please use a more specific method if
    + * possible, e.g. goog.array.forEach, goog.object.forEach, etc.
    + *
    + * @param {S} col The collection-like object.
    + * @param {function(this:T,?,?,S):?} f The function to call for every value.
    + *     This function takes
    + *     3 arguments (the value, the key or undefined if the collection has no
    + *     notion of keys, and the collection) and the return value is irrelevant.
    + * @param {T=} opt_obj The object to be used as the value of 'this'
    + *     within {@code f}.
    + * @template T,S
    + */
    +goog.structs.forEach = function(col, f, opt_obj) {
    +  if (typeof col.forEach == 'function') {
    +    col.forEach(f, opt_obj);
    +  } else if (goog.isArrayLike(col) || goog.isString(col)) {
    +    goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj);
    +  } else {
    +    var keys = goog.structs.getKeys(col);
    +    var values = goog.structs.getValues(col);
    +    var l = values.length;
    +    for (var i = 0; i < l; i++) {
    +      f.call(opt_obj, values[i], keys && keys[i], col);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calls a function for every value in the collection. When a call returns true,
    + * adds the value to a new collection (Array is returned by default).
    + *
    + * @param {S} col The collection-like object.
    + * @param {function(this:T,?,?,S):boolean} f The function to call for every
    + *     value. This function takes
    + *     3 arguments (the value, the key or undefined if the collection has no
    + *     notion of keys, and the collection) and should return a Boolean. If the
    + *     return value is true the value is added to the result collection. If it
    + *     is false the value is not included.
    + * @param {T=} opt_obj The object to be used as the value of 'this'
    + *     within {@code f}.
    + * @return {!Object|!Array<?>} A new collection where the passed values are
    + *     present. If col is a key-less collection an array is returned.  If col
    + *     has keys and values a plain old JS object is returned.
    + * @template T,S
    + */
    +goog.structs.filter = function(col, f, opt_obj) {
    +  if (typeof col.filter == 'function') {
    +    return col.filter(f, opt_obj);
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj);
    +  }
    +
    +  var rv;
    +  var keys = goog.structs.getKeys(col);
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  if (keys) {
    +    rv = {};
    +    for (var i = 0; i < l; i++) {
    +      if (f.call(opt_obj, values[i], keys[i], col)) {
    +        rv[keys[i]] = values[i];
    +      }
    +    }
    +  } else {
    +    // We should not use goog.array.filter here since we want to make sure that
    +    // the index is undefined as well as make sure that col is passed to the
    +    // function.
    +    rv = [];
    +    for (var i = 0; i < l; i++) {
    +      if (f.call(opt_obj, values[i], undefined, col)) {
    +        rv.push(values[i]);
    +      }
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Calls a function for every value in the collection and adds the result into a
    + * new collection (defaults to creating a new Array).
    + *
    + * @param {S} col The collection-like object.
    + * @param {function(this:T,?,?,S):V} f The function to call for every value.
    + *     This function takes 3 arguments (the value, the key or undefined if the
    + *     collection has no notion of keys, and the collection) and should return
    + *     something. The result will be used as the value in the new collection.
    + * @param {T=} opt_obj  The object to be used as the value of 'this'
    + *     within {@code f}.
    + * @return {!Object<V>|!Array<V>} A new collection with the new values.  If
    + *     col is a key-less collection an array is returned.  If col has keys and
    + *     values a plain old JS object is returned.
    + * @template T,S,V
    + */
    +goog.structs.map = function(col, f, opt_obj) {
    +  if (typeof col.map == 'function') {
    +    return col.map(f, opt_obj);
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj);
    +  }
    +
    +  var rv;
    +  var keys = goog.structs.getKeys(col);
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  if (keys) {
    +    rv = {};
    +    for (var i = 0; i < l; i++) {
    +      rv[keys[i]] = f.call(opt_obj, values[i], keys[i], col);
    +    }
    +  } else {
    +    // We should not use goog.array.map here since we want to make sure that
    +    // the index is undefined as well as make sure that col is passed to the
    +    // function.
    +    rv = [];
    +    for (var i = 0; i < l; i++) {
    +      rv[i] = f.call(opt_obj, values[i], undefined, col);
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Calls f for each value in a collection. If any call returns true this returns
    + * true (without checking the rest). If all returns false this returns false.
    + *
    + * @param {S} col The collection-like object.
    + * @param {function(this:T,?,?,S):boolean} f The function to call for every
    + *     value. This function takes 3 arguments (the value, the key or undefined
    + *     if the collection has no notion of keys, and the collection) and should
    + *     return a boolean.
    + * @param {T=} opt_obj  The object to be used as the value of 'this'
    + *     within {@code f}.
    + * @return {boolean} True if any value passes the test.
    + * @template T,S
    + */
    +goog.structs.some = function(col, f, opt_obj) {
    +  if (typeof col.some == 'function') {
    +    return col.some(f, opt_obj);
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj);
    +  }
    +  var keys = goog.structs.getKeys(col);
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  for (var i = 0; i < l; i++) {
    +    if (f.call(opt_obj, values[i], keys && keys[i], col)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Calls f for each value in a collection. If all calls return true this return
    + * true this returns true. If any returns false this returns false at this point
    + *  and does not continue to check the remaining values.
    + *
    + * @param {S} col The collection-like object.
    + * @param {function(this:T,?,?,S):boolean} f The function to call for every
    + *     value. This function takes 3 arguments (the value, the key or
    + *     undefined if the collection has no notion of keys, and the collection)
    + *     and should return a boolean.
    + * @param {T=} opt_obj  The object to be used as the value of 'this'
    + *     within {@code f}.
    + * @return {boolean} True if all key-value pairs pass the test.
    + * @template T,S
    + */
    +goog.structs.every = function(col, f, opt_obj) {
    +  if (typeof col.every == 'function') {
    +    return col.every(f, opt_obj);
    +  }
    +  if (goog.isArrayLike(col) || goog.isString(col)) {
    +    return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj);
    +  }
    +  var keys = goog.structs.getKeys(col);
    +  var values = goog.structs.getValues(col);
    +  var l = values.length;
    +  for (var i = 0; i < l; i++) {
    +    if (!f.call(opt_obj, values[i], keys && keys[i], col)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/structs_test.html b/mapbox-gl/closure-library/closure/goog/structs/structs_test.html
    new file mode 100644
    index 0000000000..6084aafe51
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/structs_test.html
    @@ -0,0 +1,53 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structsTest');
    +  </script>
    + </head>
    + <body>
    +  <!-- test container with 10 elements inside, 1 hr and 1 h1 with id h1 -->
    +  <div id="test">
    +   <hr />
    +   <p>
    +    Paragraph 0
    +   </p>
    +   <p>
    +    Paragraph 1
    +   </p>
    +   <p>
    +    Paragraph 2
    +   </p>
    +   <p>
    +    Paragraph 3
    +   </p>
    +   <p>
    +    Paragraph 4
    +   </p>
    +   <p>
    +    Paragraph 5
    +   </p>
    +   <p>
    +    Paragraph 6
    +   </p>
    +   <p>
    +    Paragraph 7
    +   </p>
    +   <h1 id="h1">
    +    Header
    +   </h1>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/structs_test.js b/mapbox-gl/closure-library/closure/goog/structs/structs_test.js
    new file mode 100644
    index 0000000000..96b2dab3f9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/structs_test.js
    @@ -0,0 +1,1040 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structsTest');
    +goog.setTestOnly('goog.structsTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Map');
    +goog.require('goog.structs.Set');  // needed for filter
    +goog.require('goog.testing.jsunit');
    +
    +/*
    +
    + This one does not test Map or Set
    + It tests Array, Object, String and a NodeList
    +
    +*/
    +
    +
    +
    +function stringifyObject(obj) {
    +  var sb = [];
    +  for (var key in obj) {
    +    sb.push(key + obj[key]);
    +  }
    +  return sb.join('');
    +}
    +
    +
    +function getTestElement() {
    +  return document.getElementById('test');
    +}
    +
    +
    +function getAll() {
    +  return getTestElement().getElementsByTagName('*');
    +}
    +
    +
    +var node;
    +
    +
    +function addNode() {
    +  node = document.createElement('span');
    +  getTestElement().appendChild(node);
    +}
    +
    +
    +function removeNode() {
    +  getTestElement().removeChild(node);
    +}
    +
    +
    +function nodeNames(nl) {
    +  var sb = [];
    +  for (var i = 0, n; n = nl[i]; i++) {
    +    sb.push(n.nodeName.toLowerCase());
    +  }
    +  return sb.join(',');
    +}
    +
    +
    +var allTagNames1 = 'hr,p,p,p,p,p,p,p,p,h1';
    +var allTagNames2 = allTagNames1 + ',span';
    +
    +
    +function testGetCount() {
    +  var arr = ['a', 'b', 'c'];
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(arr));
    +  arr.push('d');
    +  assertEquals('count, should be 4', 4, goog.structs.getCount(arr));
    +  goog.array.remove(arr, 'd');
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(arr));
    +
    +  var obj = {a: 0, b: 1, c: 2};
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(obj));
    +  obj.d = 3;
    +  assertEquals('count, should be 4', 4, goog.structs.getCount(obj));
    +  delete obj.d;
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(obj));
    +
    +  var s = 'abc';
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(s));
    +  s += 'd';
    +  assertEquals('count, should be 4', 4, goog.structs.getCount(s));
    +
    +  var all = getAll();
    +  assertEquals('count, should be 10', 10, goog.structs.getCount(all));
    +  addNode();
    +  assertEquals('count, should be 11', 11, goog.structs.getCount(all));
    +  removeNode();
    +  assertEquals('count, should be 10', 10, goog.structs.getCount(all));
    +
    +  var aMap = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(aMap));
    +  aMap.set('d', 3);
    +  assertEquals('count, should be 4', 4, goog.structs.getCount(aMap));
    +  aMap.remove('a');
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(aMap));
    +
    +  var aSet = new goog.structs.Set('abc');
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(aSet));
    +  aSet.add('d');
    +  assertEquals('count, should be 4', 4, goog.structs.getCount(aSet));
    +  aSet.remove('a');
    +  assertEquals('count, should be 3', 3, goog.structs.getCount(aSet));
    +}
    +
    +
    +function testGetValues() {
    +  var arr = ['a', 'b', 'c', 'd'];
    +  assertEquals('abcd', goog.structs.getValues(arr).join(''));
    +
    +  var obj = {a: 0, b: 1, c: 2, d: 3};
    +  assertEquals('0123', goog.structs.getValues(obj).join(''));
    +
    +  var s = 'abc';
    +  assertEquals('abc', goog.structs.getValues(s).join(''));
    +  s += 'd';
    +  assertEquals('abcd', goog.structs.getValues(s).join(''));
    +
    +  var all = getAll();
    +  assertEquals(allTagNames1, nodeNames(goog.structs.getValues(all)));
    +  addNode();
    +  assertEquals(allTagNames2, nodeNames(goog.structs.getValues(all)));
    +  removeNode();
    +  assertEquals(allTagNames1, nodeNames(goog.structs.getValues(all)));
    +
    +  var aMap = new goog.structs.Map({a: 1, b: 2, c: 3});
    +  assertEquals('123', goog.structs.getValues(aMap).join(''));
    +
    +  var aSet = new goog.structs.Set([1, 2, 3]);
    +  assertEquals('123', goog.structs.getValues(aMap).join(''));
    +}
    +
    +
    +function testGetKeys() {
    +  var arr = ['a', 'b', 'c', 'd'];
    +  assertEquals('0123', goog.structs.getKeys(arr).join(''));
    +
    +  var obj = {a: 0, b: 1, c: 2, d: 3};
    +  assertEquals('abcd', goog.structs.getKeys(obj).join(''));
    +
    +  var s = 'abc';
    +  assertEquals('012', goog.structs.getKeys(s).join(''));
    +  s += 'd';
    +  assertEquals('0123', goog.structs.getKeys(s).join(''));
    +
    +  var all = getAll();
    +  assertEquals('0123456789', goog.structs.getKeys(all).join(''));
    +  addNode();
    +  assertEquals('012345678910', goog.structs.getKeys(all).join(''));
    +  removeNode();
    +  assertEquals('0123456789', goog.structs.getKeys(all).join(''));
    +
    +  var aMap = new goog.structs.Map({a: 1, b: 2, c: 3});
    +  assertEquals('abc', goog.structs.getKeys(aMap).join(''));
    +
    +  var aSet = new goog.structs.Set([1, 2, 3]);
    +  assertUndefined(goog.structs.getKeys(aSet));
    +}
    +
    +function testContains() {
    +  var arr = ['a', 'b', 'c', 'd'];
    +  assertTrue("contains, Should contain 'a'", goog.structs.contains(arr, 'a'));
    +  assertFalse(
    +      "contains, Should not contain 'e'", goog.structs.contains(arr, 'e'));
    +
    +  var obj = {a: 0, b: 1, c: 2, d: 3};
    +  assertTrue("contains, Should contain '0'", goog.structs.contains(obj, 0));
    +  assertFalse(
    +      "contains, Should not contain '4'", goog.structs.contains(obj, 4));
    +
    +  var s = 'abc';
    +  assertTrue("contains, Should contain 'a'", goog.structs.contains(s, 'a'));
    +  assertFalse(
    +      "contains, Should not contain 'd'", goog.structs.contains(s, 'd'));
    +
    +  var all = getAll();
    +  assertTrue("contains, Should contain 'h1'",
    +             goog.structs.contains(all, document.getElementById('h1')));
    +  assertFalse("contains, Should not contain 'document.body'",
    +              goog.structs.contains(all, document.body));
    +
    +  var aMap = new goog.structs.Map({a: 1, b: 2, c: 3});
    +  assertTrue("contains, Should contain '1'", goog.structs.contains(aMap, 1));
    +  assertFalse(
    +      "contains, Should not contain '4'", goog.structs.contains(aMap, 4));
    +
    +  var aSet = new goog.structs.Set([1, 2, 3]);
    +  assertTrue("contains, Should contain '1'", goog.structs.contains(aSet, 1));
    +  assertFalse(
    +      "contains, Should not contain '4'", goog.structs.contains(aSet, 4));
    +}
    +
    +
    +function testClear() {
    +  var arr = ['a', 'b', 'c', 'd'];
    +  goog.structs.clear(arr);
    +  assertTrue('cleared so it should be empty', goog.structs.isEmpty(arr));
    +  assertFalse(
    +      "cleared so it should not contain 'a'", goog.structs.contains(arr, 'a'));
    +
    +  var obj = {a: 0, b: 1, c: 2, d: 3};
    +  goog.structs.clear(obj);
    +  assertTrue('cleared so it should be empty', goog.structs.isEmpty(obj));
    +  assertFalse(
    +      "cleared so it should not contain 'a' key",
    +      goog.structs.contains(obj, 0));
    +
    +  var aMap = new goog.structs.Map({a: 1, b: 2, c: 3});
    +  goog.structs.clear(aMap);
    +  assertTrue('cleared map so it should be empty', goog.structs.isEmpty(aMap));
    +  assertFalse("cleared map so it should not contain '1' value",
    +      goog.structs.contains(aMap, 1));
    +
    +  var aSet = new goog.structs.Set([1, 2, 3]);
    +  goog.structs.clear(aSet);
    +  assertTrue('cleared set so it should be empty', goog.structs.isEmpty(aSet));
    +  assertFalse(
    +      "cleared set so it should not contain '1'",
    +      goog.structs.contains(aSet, 1));
    +
    +  // cannot clear a string or a NodeList
    +}
    +
    +
    +
    +// Map
    +
    +function testMap() {
    +  var RV = {};
    +  var obj = {
    +    map: function(g) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.map(obj, f));
    +}
    +
    +function testMap2() {
    +  var THIS_OBJ = {};
    +  var RV = {};
    +  var obj = {
    +    map: function(g, obj2) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      assertEquals(THIS_OBJ, obj2);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.map(obj, f, THIS_OBJ));
    +}
    +
    +function testMapArrayLike() {
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v * v;
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f));
    +}
    +
    +function testMapArrayLike2() {
    +  var THIS_OBJ = {};
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v * v;
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f, THIS_OBJ));
    +}
    +
    +function testMapString() {
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // Teh SpiderMonkey Array.map for strings turns the string into a String
    +    // so we cannot use assertEquals because it uses ===.
    +    assertTrue(col == col2);
    +    assertEquals('number', typeof i);
    +    return Number(v) * Number(v);
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f));
    +}
    +
    +function testMapString2() {
    +  var THIS_OBJ = {};
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return Number(v) * Number(v);
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f, THIS_OBJ));
    +}
    +
    +function testMapMap() {
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    return v * v;
    +  }
    +  assertObjectEquals({a: 0, b: 1, c: 4}, goog.structs.map(col, f));
    +}
    +
    +function testMapMap2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v * v;
    +  }
    +  assertObjectEquals({a: 0, b: 1, c: 4}, goog.structs.map(col, f, THIS_OBJ));
    +}
    +
    +function testMapSet() {
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    return v * v;
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f));
    +}
    +
    +function testMapSet2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v * v;
    +  }
    +  assertArrayEquals([0, 1, 4], goog.structs.map(col, f, THIS_OBJ));
    +}
    +
    +function testMapNodeList() {
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v.tagName;
    +  }
    +  assertEquals('HRPPPPPPPPH1', goog.structs.map(col, f).join(''));
    +}
    +
    +function testMapNodeList2() {
    +  var THIS_OBJ = {};
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v.tagName;
    +  }
    +  assertEquals('HRPPPPPPPPH1', goog.structs.map(col, f, THIS_OBJ).join(''));
    +}
    +
    +// Filter
    +
    +function testFilter() {
    +  var RV = {};
    +  var obj = {
    +    filter: function(g) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.filter(obj, f));
    +}
    +
    +function testFilter2() {
    +  var THIS_OBJ = {};
    +  var RV = {};
    +  var obj = {
    +    filter: function(g, obj2) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      assertEquals(THIS_OBJ, obj2);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.filter(obj, f, THIS_OBJ));
    +}
    +
    +function testFilterArrayLike() {
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v > 0;
    +  }
    +  assertArrayEquals([1, 2], goog.structs.filter(col, f));
    +}
    +
    +function testFilterArrayLike2() {
    +  var THIS_OBJ = {};
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v > 0;
    +  }
    +  assertArrayEquals([1, 2], goog.structs.filter(col, f, THIS_OBJ));
    +}
    +
    +function testFilterString() {
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    return Number(v) > 0;
    +  }
    +  assertArrayEquals(['1', '2'], goog.structs.filter(col, f));
    +}
    +
    +function testFilterString2() {
    +  var THIS_OBJ = {};
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return Number(v) > 0;
    +  }
    +  assertArrayEquals(['1', '2'], goog.structs.filter(col, f, THIS_OBJ));
    +}
    +
    +function testFilterMap() {
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    return v > 0;
    +  }
    +  assertObjectEquals({b: 1, c: 2}, goog.structs.filter(col, f));
    +}
    +
    +function testFilterMap2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > 0;
    +  }
    +  assertObjectEquals({b: 1, c: 2}, goog.structs.filter(col, f, THIS_OBJ));
    +}
    +
    +function testFilterSet() {
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    return v > 0;
    +  }
    +  assertArrayEquals([1, 2], goog.structs.filter(col, f));
    +}
    +
    +function testFilterSet2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > 0;
    +  }
    +  assertArrayEquals([1, 2], goog.structs.filter(col, f, THIS_OBJ));
    +}
    +
    +function testFilterNodeList() {
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v.tagName == 'P';
    +  }
    +  assertEquals('p,p,p,p,p,p,p,p',
    +               nodeNames(goog.structs.filter(col, f)));
    +}
    +
    +function testFilterNodeList2() {
    +  var THIS_OBJ = {};
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v.tagName == 'P';
    +  }
    +  assertEquals('p,p,p,p,p,p,p,p',
    +               nodeNames(goog.structs.filter(col, f, THIS_OBJ)));
    +}
    +
    +// Some
    +
    +function testSome() {
    +  var RV = {};
    +  var obj = {
    +    some: function(g) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.some(obj, f));
    +}
    +
    +function testSome2() {
    +  var THIS_OBJ = {};
    +  var RV = {};
    +  var obj = {
    +    some: function(g, obj2) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      assertEquals(THIS_OBJ, obj2);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.some(obj, f, THIS_OBJ));
    +}
    +
    +function testSomeArrayLike() {
    +  var limit = 0;
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f));
    +}
    +
    +function testSomeArrayLike2() {
    +  var THIS_OBJ = {};
    +  var limit = 0;
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f, THIS_OBJ));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f, THIS_OBJ));
    +}
    +
    +function testSomeString() {
    +  var limit = 0;
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    return Number(v) > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f));
    +}
    +
    +function testSomeString2() {
    +  var THIS_OBJ = {};
    +  var limit = 0;
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return Number(v) > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f, THIS_OBJ));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f, THIS_OBJ));
    +}
    +
    +function testSomeMap() {
    +  var limit = 0;
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    return v > limit;
    +  }
    +  assertObjectEquals(true, goog.structs.some(col, f));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f));
    +}
    +
    +function testSomeMap2() {
    +  var THIS_OBJ = {};
    +  var limit = 0;
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertObjectEquals(true, goog.structs.some(col, f, THIS_OBJ));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f, THIS_OBJ));
    +}
    +
    +function testSomeSet() {
    +  var limit = 0;
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f));
    +}
    +
    +function testSomeSet2() {
    +  var THIS_OBJ = {};
    +  var limit = 0;
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.some(col, f, THIS_OBJ));
    +  limit = 2;
    +  assertFalse(goog.structs.some(col, f, THIS_OBJ));
    +}
    +
    +function testSomeNodeList() {
    +  var tagName = 'P';
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v.tagName == tagName;
    +  }
    +  assertTrue(goog.structs.some(col, f));
    +  tagName = 'MARQUEE';
    +  assertFalse(goog.structs.some(col, f));
    +}
    +
    +function testSomeNodeList2() {
    +  var THIS_OBJ = {};
    +  var tagName = 'P';
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v.tagName == tagName;
    +  }
    +  assertTrue(goog.structs.some(col, f, THIS_OBJ));
    +  tagName = 'MARQUEE';
    +  assertFalse(goog.structs.some(col, f, THIS_OBJ));
    +}
    +
    +// Every
    +
    +function testEvery() {
    +  var RV = {};
    +  var obj = {
    +    every: function(g) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.every(obj, f));
    +}
    +
    +function testEvery2() {
    +  var THIS_OBJ = {};
    +  var RV = {};
    +  var obj = {
    +    every: function(g, obj2) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      assertEquals(THIS_OBJ, obj2);
    +      return RV;
    +    }
    +  };
    +  function f() {}
    +  assertEquals(RV, goog.structs.every(obj, f, THIS_OBJ));
    +}
    +
    +function testEveryArrayLike() {
    +  var limit = -1;
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f));
    +}
    +
    +function testEveryArrayLike2() {
    +  var THIS_OBJ = {};
    +  var limit = -1;
    +  var col = [0, 1, 2];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f, THIS_OBJ));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f, THIS_OBJ));
    +}
    +
    +function testEveryString() {
    +  var limit = -1;
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    return Number(v) > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f));
    +}
    +
    +function testEveryString2() {
    +  var THIS_OBJ = {};
    +  var limit = -1;
    +  var col = '012';
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return Number(v) > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f, THIS_OBJ));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f, THIS_OBJ));
    +}
    +
    +function testEveryMap() {
    +  var limit = -1;
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    return v > limit;
    +  }
    +  assertObjectEquals(true, goog.structs.every(col, f));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f));
    +}
    +
    +function testEveryMap2() {
    +  var THIS_OBJ = {};
    +  var limit = -1;
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertObjectEquals(true, goog.structs.every(col, f, THIS_OBJ));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f, THIS_OBJ));
    +}
    +
    +function testEverySet() {
    +  var limit = -1;
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f));
    +}
    +
    +function testEverySet2() {
    +  var THIS_OBJ = {};
    +  var limit = -1;
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    return v > limit;
    +  }
    +  assertTrue(goog.structs.every(col, f, THIS_OBJ));
    +  limit = 1;
    +  assertFalse(goog.structs.every(col, f, THIS_OBJ));
    +}
    +
    +function testEveryNodeList() {
    +  var nodeType = 1; // ELEMENT
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    return v.nodeType == nodeType;
    +  }
    +  assertTrue(goog.structs.every(col, f));
    +  nodeType = 3; // TEXT
    +  assertFalse(goog.structs.every(col, f));
    +}
    +
    +function testEveryNodeList2() {
    +  var THIS_OBJ = {};
    +  var nodeType = 1; // ELEMENT
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    return v.nodeType == nodeType;
    +  }
    +  assertTrue(goog.structs.every(col, f, THIS_OBJ));
    +  nodeType = 3; // TEXT
    +  assertFalse(goog.structs.every(col, f, THIS_OBJ));
    +}
    +
    +// For each
    +
    +function testForEach() {
    +  var called = false;
    +  var obj = {
    +    forEach: function(g) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      called = true;
    +    }
    +  };
    +  function f() {}
    +  goog.structs.forEach(obj, f);
    +  assertTrue(called);
    +}
    +
    +function testForEach2() {
    +  var called = false;
    +  var THIS_OBJ = {};
    +  var obj = {
    +    forEach: function(g, obj2) {
    +      assertEquals(f, g);
    +      assertEquals(this, obj);
    +      assertEquals(THIS_OBJ, obj2);
    +      called = true;
    +    }
    +  };
    +  function f() {}
    +  goog.structs.forEach(obj, f, THIS_OBJ);
    +  assertTrue(called);
    +}
    +
    +function testForEachArrayLike() {
    +  var col = [0, 1, 2];
    +  var values = [];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    values.push(v * v);
    +  }
    +  goog.structs.forEach(col, f);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachArrayLike2() {
    +  var THIS_OBJ = {};
    +  var col = [0, 1, 2];
    +  var values = [];
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    values.push(v * v);
    +  }
    +  goog.structs.forEach(col, f, THIS_OBJ);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachString() {
    +  var col = '012';
    +  var values = [];
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    values.push(Number(v) * Number(v));
    +  }
    +  goog.structs.forEach(col, f);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachString2() {
    +  var THIS_OBJ = {};
    +  var col = '012';
    +  var values = [];
    +  function f(v, i, col2) {
    +    // for some reason the strings are equal but identical???
    +    assertEquals(String(col), String(col2));
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    values.push(Number(v) * Number(v));
    +  }
    +  goog.structs.forEach(col, f, THIS_OBJ);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachMap() {
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  var values = [];
    +  var keys = [];
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    values.push(v * v);
    +    keys.push(key);
    +  }
    +  goog.structs.forEach(col, f);
    +  assertArrayEquals([0, 1, 4], values);
    +  assertArrayEquals(['a', 'b', 'c'], keys);
    +}
    +
    +function testForEachMap2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Map({a: 0, b: 1, c: 2});
    +  var values = [];
    +  var keys = [];
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('string', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    values.push(v * v);
    +    keys.push(key);
    +  }
    +  goog.structs.forEach(col, f, THIS_OBJ);
    +  assertArrayEquals([0, 1, 4], values);
    +  assertArrayEquals(['a', 'b', 'c'], keys);
    +}
    +
    +function testForEachSet() {
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  var values = [];
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    values.push(v * v);
    +  }
    +  goog.structs.forEach(col, f);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachSet2() {
    +  var THIS_OBJ = {};
    +  var col = new goog.structs.Set([0, 1, 2]);
    +  var values = [];
    +  function f(v, key, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('undefined', typeof key);
    +    assertEquals(THIS_OBJ, this);
    +    values.push(v * v);
    +  }
    +  goog.structs.forEach(col, f, THIS_OBJ);
    +  assertArrayEquals([0, 1, 4], values);
    +}
    +
    +function testForEachNodeList() {
    +  var values = [];
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    values.push(v.tagName);
    +  }
    +  goog.structs.forEach(col, f);
    +  assertEquals('HRPPPPPPPPH1', values.join(''));
    +}
    +
    +function testForEachNodeList2() {
    +  var THIS_OBJ = {};
    +  var values = [];
    +  var col = getAll();
    +  function f(v, i, col2) {
    +    assertEquals(col, col2);
    +    assertEquals('number', typeof i);
    +    assertEquals(THIS_OBJ, this);
    +    values.push(v.tagName);
    +  }
    +  goog.structs.forEach(col, f, THIS_OBJ);
    +  assertEquals('HRPPPPPPPPH1', values.join(''));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/treenode.js b/mapbox-gl/closure-library/closure/goog/structs/treenode.js
    new file mode 100644
    index 0000000000..ebe898280b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/treenode.js
    @@ -0,0 +1,455 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generic tree node data structure with arbitrary number of child
    + * nodes.
    + *
    + */
    +
    +goog.provide('goog.structs.TreeNode');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.structs.Node');
    +
    +
    +
    +/**
    + * Generic tree node data structure with arbitrary number of child nodes.
    + * It is possible to create a dynamic tree structure by overriding
    + * {@link #getParent} and {@link #getChildren} in a subclass. All other getters
    + * will automatically work.
    + *
    + * @param {KEY} key Key.
    + * @param {VALUE} value Value.
    + * @constructor
    + * @extends {goog.structs.Node<KEY, VALUE>}
    + * @template KEY, VALUE
    + */
    +goog.structs.TreeNode = function(key, value) {
    +  goog.structs.Node.call(this, key, value);
    +
    +  /**
    +   * Reference to the parent node or null if it has no parent.
    +   * @private {goog.structs.TreeNode<KEY, VALUE>}
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * Child nodes or null in case of leaf node.
    +   * @private {Array<!goog.structs.TreeNode<KEY, VALUE>>}
    +   */
    +  this.children_ = null;
    +};
    +goog.inherits(goog.structs.TreeNode, goog.structs.Node);
    +
    +
    +/**
    + * Constant for empty array to avoid unnecessary allocations.
    + * @private
    + */
    +goog.structs.TreeNode.EMPTY_ARRAY_ = [];
    +
    +
    +/**
    + * @return {!goog.structs.TreeNode} Clone of the tree node without its parent
    + *     and child nodes. The key and the value are copied by reference.
    + * @override
    + */
    +goog.structs.TreeNode.prototype.clone = function() {
    +  return new goog.structs.TreeNode(this.getKey(), this.getValue());
    +};
    +
    +
    +/**
    + * @return {!goog.structs.TreeNode} Clone of the subtree with this node as root.
    + */
    +goog.structs.TreeNode.prototype.deepClone = function() {
    +  var clone = this.clone();
    +  this.forEachChild(function(child) {
    +    clone.addChild(child.deepClone());
    +  });
    +  return clone;
    +};
    +
    +
    +/**
    + * @return {goog.structs.TreeNode<KEY, VALUE>} Parent node or null if it has no
    + *     parent.
    + */
    +goog.structs.TreeNode.prototype.getParent = function() {
    +  return this.parent_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the node is a leaf node.
    + */
    +goog.structs.TreeNode.prototype.isLeaf = function() {
    +  return !this.getChildCount();
    +};
    +
    +
    +/**
    + * Tells if the node is the last child of its parent. This method helps how to
    + * connect the tree nodes with lines: L shapes should be used before the last
    + * children and |- shapes before the rest. Schematic tree visualization:
    + *
    + * <pre>
    + * Node1
    + * |-Node2
    + * | L-Node3
    + * |   |-Node4
    + * |   L-Node5
    + * L-Node6
    + * </pre>
    + *
    + * @return {boolean} Whether the node has parent and is the last child of it.
    + */
    +goog.structs.TreeNode.prototype.isLastChild = function() {
    +  var parent = this.getParent();
    +  return Boolean(parent && this == goog.array.peek(parent.getChildren()));
    +};
    +
    +
    +/**
    + * @return {!Array<!goog.structs.TreeNode<KEY, VALUE>>} Immutable child nodes.
    + */
    +goog.structs.TreeNode.prototype.getChildren = function() {
    +  return this.children_ || goog.structs.TreeNode.EMPTY_ARRAY_;
    +};
    +
    +
    +/**
    + * Gets the child node of this node at the given index.
    + * @param {number} index Child index.
    + * @return {goog.structs.TreeNode<KEY, VALUE>} The node at the given index or
    + *     null if not found.
    + */
    +goog.structs.TreeNode.prototype.getChildAt = function(index) {
    +  return this.getChildren()[index] || null;
    +};
    +
    +
    +/**
    + * @return {number} The number of children.
    + */
    +goog.structs.TreeNode.prototype.getChildCount = function() {
    +  return this.getChildren().length;
    +};
    +
    +
    +/**
    + * @return {number} The number of ancestors of the node.
    + */
    +goog.structs.TreeNode.prototype.getDepth = function() {
    +  var depth = 0;
    +  var node = this;
    +  while (node.getParent()) {
    +    depth++;
    +    node = node.getParent();
    +  }
    +  return depth;
    +};
    +
    +
    +/**
    + * @return {!Array<!goog.structs.TreeNode<KEY, VALUE>>} All ancestor nodes in
    + *     bottom-up order.
    + */
    +goog.structs.TreeNode.prototype.getAncestors = function() {
    +  var ancestors = [];
    +  var node = this.getParent();
    +  while (node) {
    +    ancestors.push(node);
    +    node = node.getParent();
    +  }
    +  return ancestors;
    +};
    +
    +
    +/**
    + * @return {!goog.structs.TreeNode<KEY, VALUE>} The root of the tree structure,
    + *     i.e. the farthest ancestor of the node or the node itself if it has no
    + *     parents.
    + */
    +goog.structs.TreeNode.prototype.getRoot = function() {
    +  var root = this;
    +  while (root.getParent()) {
    +    root = root.getParent();
    +  }
    +  return root;
    +};
    +
    +
    +/**
    + * Builds a nested array structure from the node keys in this node's subtree to
    + * facilitate testing tree operations that change the hierarchy.
    + * @return {!Array<KEY>} The structure of this node's descendants as nested
    + *     array of node keys. The number of unclosed opening brackets up to a
    + *     particular node is proportional to the indentation of that node in the
    + *     graphical representation of the tree. Example:
    + *     <pre>
    + *       this
    + *       |- child1
    + *       |  L- grandchild
    + *       L- child2
    + *     </pre>
    + *     is represented as ['child1', ['grandchild'], 'child2'].
    + */
    +goog.structs.TreeNode.prototype.getSubtreeKeys = function() {
    +  var ret = [];
    +  this.forEachChild(function(child) {
    +    ret.push(child.getKey());
    +    if (!child.isLeaf()) {
    +      ret.push(child.getSubtreeKeys());
    +    }
    +  });
    +  return ret;
    +};
    +
    +
    +/**
    + * Tells whether this node is the ancestor of the given node.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} node A node.
    + * @return {boolean} Whether this node is the ancestor of {@code node}.
    + */
    +goog.structs.TreeNode.prototype.contains = function(node) {
    +  var current = node;
    +  do {
    +    current = current.getParent();
    +  } while (current && current != this);
    +  return Boolean(current);
    +};
    +
    +
    +/**
    + * Finds the deepest common ancestor of the given nodes. The concept of
    + * ancestor is not strict in this case, it includes the node itself.
    + * @param {...!goog.structs.TreeNode<KEY, VALUE>} var_args The nodes.
    + * @return {goog.structs.TreeNode<KEY, VALUE>} The common ancestor of the nodes
    + *     or null if they are from different trees.
    + * @template KEY, VALUE
    + */
    +goog.structs.TreeNode.findCommonAncestor = function(var_args) {
    +  /** @type {goog.structs.TreeNode} */
    +  var ret = arguments[0];
    +  if (!ret) {
    +    return null;
    +  }
    +
    +  var retDepth = ret.getDepth();
    +  for (var i = 1; i < arguments.length; i++) {
    +    /** @type {goog.structs.TreeNode} */
    +    var node = arguments[i];
    +    var depth = node.getDepth();
    +    while (node != ret) {
    +      if (depth <= retDepth) {
    +        ret = ret.getParent();
    +        retDepth--;
    +      }
    +      if (depth > retDepth) {
    +        node = node.getParent();
    +        depth--;
    +      }
    +    }
    +  }
    +
    +  return ret;
    +};
    +
    +
    +/**
    + * Returns a node whose key matches the given one in the hierarchy rooted at
    + * this node. The hierarchy is searched using an in-order traversal.
    + * @param {KEY} key The key to search for.
    + * @return {goog.structs.TreeNode<KEY, VALUE>} The node with the given key, or
    + *     null if no node with the given key exists in the hierarchy.
    + */
    +goog.structs.TreeNode.prototype.getNodeByKey = function(key) {
    +  if (this.getKey() == key) {
    +    return this;
    +  }
    +  var children = this.getChildren();
    +  for (var i = 0; i < children.length; i++) {
    +    var descendant = children[i].getNodeByKey(key);
    +    if (descendant) {
    +      return descendant;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Traverses all child nodes.
    + * @param {function(this:THIS, !goog.structs.TreeNode<KEY, VALUE>, number,
    + *     !Array<!goog.structs.TreeNode<KEY, VALUE>>)} f Callback function. It
    + *     takes the node, its index and the array of all child nodes as arguments.
    + * @param {THIS=} opt_this The object to be used as the value of {@code this}
    + *     within {@code f}.
    + * @template THIS
    + */
    +goog.structs.TreeNode.prototype.forEachChild = function(f, opt_this) {
    +  goog.array.forEach(this.getChildren(), f, opt_this);
    +};
    +
    +
    +/**
    + * Traverses all child nodes recursively in preorder.
    + * @param {function(!goog.structs.TreeNode<KEY, VALUE>)} f Callback function.
    + *     It takes the node as argument.
    + * @param {Object=} opt_this The object to be used as the value of {@code this}
    + *     within {@code f}.
    + */
    +goog.structs.TreeNode.prototype.forEachDescendant = function(f, opt_this) {
    +  goog.array.forEach(this.getChildren(), function(child) {
    +    f.call(opt_this, child);
    +    child.forEachDescendant(f, opt_this);
    +  });
    +};
    +
    +
    +/**
    + * Traverses the subtree with the possibility to skip branches. Starts with
    + * this node, and visits the descendant nodes depth-first, in preorder.
    + * @param {function(this:THIS, !goog.structs.TreeNode<KEY, VALUE>):
    + *     (boolean|undefined)} f Callback function. It takes the node as argument.
    + *     The children of this node will be visited if the callback returns true or
    + *     undefined, and will be skipped if the callback returns false.
    + * @param {THIS=} opt_this The object to be used as the value of {@code this}
    + *     within {@code f}.
    + * @template THIS
    + */
    +goog.structs.TreeNode.prototype.traverse = function(f, opt_this) {
    +  if (f.call(opt_this, this) !== false) {
    +    goog.array.forEach(this.getChildren(), function(child) {
    +      child.traverse(f, opt_this);
    +    });
    +  }
    +};
    +
    +
    +/**
    + * Sets the parent node of this node. The callers must ensure that the parent
    + * node and only that has this node among its children.
    + * @param {goog.structs.TreeNode<KEY, VALUE>} parent The parent to set. If
    + *     null, the node will be detached from the tree.
    + * @protected
    + */
    +goog.structs.TreeNode.prototype.setParent = function(parent) {
    +  this.parent_ = parent;
    +};
    +
    +
    +/**
    + * Appends a child node to this node.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} child Orphan child node.
    + */
    +goog.structs.TreeNode.prototype.addChild = function(child) {
    +  this.addChildAt(child, this.children_ ? this.children_.length : 0);
    +};
    +
    +
    +/**
    + * Inserts a child node at the given index.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} child Orphan child node.
    + * @param {number} index The position to insert at.
    + */
    +goog.structs.TreeNode.prototype.addChildAt = function(child, index) {
    +  goog.asserts.assert(!child.getParent());
    +  child.setParent(this);
    +  this.children_ = this.children_ || [];
    +  goog.asserts.assert(index >= 0 && index <= this.children_.length);
    +  goog.array.insertAt(this.children_, child, index);
    +};
    +
    +
    +/**
    + * Replaces a child node at the given index.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} newChild Child node to set. It
    + *     must not have parent node.
    + * @param {number} index Valid index of the old child to replace.
    + * @return {!goog.structs.TreeNode<KEY, VALUE>} The original child node,
    + *     detached from its parent.
    + */
    +goog.structs.TreeNode.prototype.replaceChildAt = function(newChild, index) {
    +  goog.asserts.assert(!newChild.getParent(),
    +      'newChild must not have parent node');
    +  var children = this.getChildren();
    +  var oldChild = children[index];
    +  goog.asserts.assert(oldChild, 'Invalid child or child index is given.');
    +  oldChild.setParent(null);
    +  children[index] = newChild;
    +  newChild.setParent(this);
    +  return oldChild;
    +};
    +
    +
    +/**
    + * Replaces the given child node.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} newChild New node to replace
    + *     {@code oldChild}. It must not have parent node.
    + * @param {!goog.structs.TreeNode<KEY, VALUE>} oldChild Existing child node to
    + *     be replaced.
    + * @return {!goog.structs.TreeNode<KEY, VALUE>} The replaced child node
    + *     detached from its parent.
    + */
    +goog.structs.TreeNode.prototype.replaceChild = function(newChild, oldChild) {
    +  return this.replaceChildAt(newChild,
    +      goog.array.indexOf(this.getChildren(), oldChild));
    +};
    +
    +
    +/**
    + * Removes the child node at the given index.
    + * @param {number} index The position to remove from.
    + * @return {goog.structs.TreeNode<KEY, VALUE>} The removed node if any.
    + */
    +goog.structs.TreeNode.prototype.removeChildAt = function(index) {
    +  var child = this.children_ && this.children_[index];
    +  if (child) {
    +    child.setParent(null);
    +    goog.array.removeAt(this.children_, index);
    +    if (this.children_.length == 0) {
    +      delete this.children_;
    +    }
    +    return child;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Removes the given child node of this node.
    + * @param {goog.structs.TreeNode<KEY, VALUE>} child The node to remove.
    + * @return {goog.structs.TreeNode<KEY, VALUE>} The removed node if any.
    + */
    +goog.structs.TreeNode.prototype.removeChild = function(child) {
    +  return this.removeChildAt(goog.array.indexOf(this.getChildren(), child));
    +};
    +
    +
    +/**
    + * Removes all child nodes of this node.
    + */
    +goog.structs.TreeNode.prototype.removeChildren = function() {
    +  if (this.children_) {
    +    goog.array.forEach(this.children_, function(child) {
    +      child.setParent(null);
    +    });
    +  }
    +  delete this.children_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/treenode_test.html b/mapbox-gl/closure-library/closure/goog/structs/treenode_test.html
    new file mode 100644
    index 0000000000..73312f4993
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/treenode_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.TreeNode
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.TreeNodeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/treenode_test.js b/mapbox-gl/closure-library/closure/goog/structs/treenode_test.js
    new file mode 100644
    index 0000000000..86a1f0550e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/treenode_test.js
    @@ -0,0 +1,384 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.TreeNodeTest');
    +goog.setTestOnly('goog.structs.TreeNodeTest');
    +
    +goog.require('goog.structs.TreeNode');
    +goog.require('goog.testing.jsunit');
    +
    +function testConstructor() {
    +  var node = new goog.structs.TreeNode('key', 'value');
    +  assertEquals('key', 'key', node.getKey());
    +  assertEquals('value', 'value', node.getValue());
    +  assertNull('parent', node.getParent());
    +  assertArrayEquals('children', [], node.getChildren());
    +  assertTrue('leaf', node.isLeaf());
    +}
    +
    +function testClone() {
    +  var node1 = new goog.structs.TreeNode(1, '1');
    +  var node2 = new goog.structs.TreeNode(2, '2');
    +  var node3 = new goog.structs.TreeNode(3, '3');
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +
    +  var clone = node2.clone();
    +  assertEquals('key', 2, clone.getKey());
    +  assertEquals('value', '2', clone.getValue());
    +  assertNull('parent', clone.getParent());
    +  assertArrayEquals('children', [], clone.getChildren());
    +}
    +
    +function testDeepClone() {
    +  var node1 = new goog.structs.TreeNode(1, '1');
    +  var node2 = new goog.structs.TreeNode(2, '2');
    +  var node3 = new goog.structs.TreeNode(3, '3');
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +
    +  var clone = node2.deepClone();
    +  assertEquals('key', 2, clone.getKey());
    +  assertEquals('value', '2', clone.getValue());
    +  assertNull('parent', clone.getParent());
    +  assertEquals('number of children', 1, clone.getChildren().length);
    +  assertEquals('first child key', 3, clone.getChildAt(0).getKey());
    +  assertNotEquals('first child has been cloned', node3, clone.getChildAt(0));
    +}
    +
    +function testGetParent() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertEquals('parent', node1, node2.getParent());
    +  assertNull('orphan', node1.getParent());
    +}
    +
    +function testIsLeaf() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertFalse('not leaf', node1.isLeaf());
    +  assertTrue('leaf', node2.isLeaf());
    +}
    +
    +function testIsLastChild() {
    +  var node1 = new goog.structs.TreeNode(1, '1');
    +  var node2 = new goog.structs.TreeNode(2, '2');
    +  var node3 = new goog.structs.TreeNode(3, '3');
    +  node1.addChild(node2);
    +  node1.addChild(node3);
    +  assertFalse('root', node1.isLastChild());
    +  assertFalse('first child out of the two', node2.isLastChild());
    +  assertTrue('second child out of the two', node3.isLastChild());
    +}
    +
    +function testGetChildren() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertArrayEquals('1 child', [node2], node1.getChildren());
    +  assertArrayEquals('no children', [], node2.getChildren());
    +}
    +
    +function testGetChildAt() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertNull('index too low', node1.getChildAt(-1));
    +  assertEquals('first child', node2, node1.getChildAt(0));
    +  assertNull('index too high', node1.getChildAt(1));
    +  assertNull('no children', node2.getChildAt(0));
    +}
    +
    +function testGetChildCount() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertEquals('child count of root node', 1, node1.getChildCount());
    +  assertEquals('child count of leaf node', 0, node2.getChildCount());
    +}
    +
    +function testGetDepth() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  assertEquals('no parent', 0, node1.getDepth());
    +  assertEquals('1 ancestor', 1, node2.getDepth());
    +  assertEquals('2 ancestors', 2, node3.getDepth());
    +}
    +
    +function testGetAncestors() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  assertArrayEquals('no ancestors', [], node1.getAncestors());
    +  assertArrayEquals('2 ancestors', [node2, node1], node3.getAncestors());
    +}
    +
    +function testGetRoot() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  node1.addChild(node2);
    +  assertEquals('no ancestors', node1, node1.getRoot());
    +  assertEquals('has ancestors', node1, node2.getRoot());
    +}
    +
    +function testGetSubtreeKeys() {
    +  var root = new goog.structs.TreeNode('root', null);
    +  var child1 = new goog.structs.TreeNode('child1', null);
    +  var child2 = new goog.structs.TreeNode('child2', null);
    +  var grandchild = new goog.structs.TreeNode('grandchild', null);
    +  root.addChild(child1);
    +  root.addChild(child2);
    +  child1.addChild(grandchild);
    +  assertArrayEquals('node hierarchy', ['child1', ['grandchild'], 'child2'],
    +      root.getSubtreeKeys());
    +}
    +
    +function testContains() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  node2.addChild(node4);
    +
    +  assertTrue('parent', node1.contains(node2));
    +  assertTrue('grandparent', node1.contains(node3));
    +  assertFalse('child', node2.contains(node1));
    +  assertFalse('grandchild', node3.contains(node1));
    +  assertFalse('sibling', node3.contains(node4));
    +}
    +
    +function testFindCommonAncestor() {
    +  var findCommonAncestor = goog.structs.TreeNode.findCommonAncestor;
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  var node5 = new goog.structs.TreeNode(5, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  node1.addChild(node4);
    +
    +  assertNull('0 nodes', findCommonAncestor());
    +  assertEquals('1 node', node2, findCommonAncestor(node2));
    +  assertEquals('same nodes', node3, findCommonAncestor(node3, node3));
    +  assertEquals('node and child node', node2, findCommonAncestor(node2, node3));
    +  assertEquals('node and parent node', node1, findCommonAncestor(node2, node1));
    +  assertEquals('siblings', node1, findCommonAncestor(node2, node4));
    +  assertEquals('all nodes', node1,
    +      findCommonAncestor(node2, node3, node4, node1));
    +  assertNull('disconnected nodes', findCommonAncestor(node3, node5));
    +}
    +
    +function testGetNodeByKey() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  var node5 = new goog.structs.TreeNode(2, null);
    +  var node6 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  node1.addChild(node4);
    +  node4.addChild(node5);
    +  node1.addChild(node6);
    +
    +  assertEquals('root', node1, node1.getNodeByKey(1));
    +  assertEquals('child with unique key', node5, node4.getNodeByKey(2));
    +  assertEquals('child with duplicate keys', node2, node1.getNodeByKey(2));
    +  assertEquals('grandchild with duplicate keys', node3, node1.getNodeByKey(3));
    +  assertNull('disconnected', node2.getNodeByKey(4));
    +  assertNull('missing', node1.getNodeByKey(5));
    +}
    +
    +function testForEachChild() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node1.addChild(node3);
    +
    +  var thisContext = {};
    +  var visitedNodes = [];
    +  var indices = [];
    +  node1.forEachChild(function(node, index, children) {
    +    assertEquals('value of this', thisContext, this);
    +    visitedNodes.push(node);
    +    indices.push(index);
    +    assertArrayEquals('children argument', [node2, node3], children);
    +  }, thisContext);
    +  assertArrayEquals('visited nodes', [node2, node3], visitedNodes);
    +  assertArrayEquals('index of visited nodes', [0, 1], indices);
    +}
    +
    +function testForEachDescendant() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  node2.addChild(node4);
    +
    +  var thisContext = {};
    +  var visitedNodes = [];
    +  node1.forEachDescendant(function(node) {
    +    assertEquals('value of this', thisContext, this);
    +    visitedNodes.push(node);
    +  }, thisContext);
    +  assertArrayEquals('visited nodes', [node2, node3, node4], visitedNodes);
    +}
    +
    +function testTraverse() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  node1.addChild(node2);
    +  node2.addChild(node3);
    +  node2.addChild(node4);
    +
    +  var thisContext = {};
    +  var visitedNodes = [];
    +  node1.traverse(function(node) {
    +    assertEquals('value of this', thisContext, this);
    +    visitedNodes.push(node);
    +  }, thisContext);
    +  assertArrayEquals('callback returns nothing => all nodes are visited',
    +      [node1, node2, node3, node4], visitedNodes);
    +
    +  visitedNodes = [];
    +  node1.traverse(function(node) {
    +    visitedNodes.push(node);
    +    return node != node2;  // Cut off at node2.
    +  });
    +  assertArrayEquals('children of node2 are skipped',
    +      [node1, node2], visitedNodes);
    +}
    +
    +function testAddChild() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  assertArrayEquals('0 children', [], node1.getChildren());
    +  node1.addChild(node2);
    +  assertArrayEquals('1 child', [node2], node1.getChildren());
    +  assertEquals('parent is set', node1, node2.getParent());
    +  node1.addChild(node3);
    +  assertArrayEquals('2 children', [node2, node3], node1.getChildren());
    +}
    +
    +function testAddChildAt() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  var node4 = new goog.structs.TreeNode(4, null);
    +  var node5 = new goog.structs.TreeNode(5, null);
    +  node1.addChildAt(node2, 0);
    +  assertArrayEquals('add first child', [node2], node1.getChildren());
    +  assertEquals('parent is set', node1, node2.getParent());
    +  node1.addChildAt(node3, 0);
    +  assertArrayEquals('add to the front', [node3, node2],
    +      node1.getChildren());
    +  node1.addChildAt(node4, 1);
    +  assertArrayEquals('add to the middle', [node3, node4, node2],
    +      node1.getChildren());
    +  node1.addChildAt(node5, 3);
    +  assertArrayEquals('add to the end', [node3, node4, node2, node5],
    +      node1.getChildren());
    +}
    +
    +function testReplaceChildAt() {
    +  var root = new goog.structs.TreeNode(0, null);
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  root.addChild(node1);
    +
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  assertEquals('original node', node1, root.replaceChildAt(node2, 0));
    +  assertEquals('parent is set', root, node2.getParent());
    +  assertArrayEquals('children are updated', [node2], root.getChildren());
    +  assertNull('old child node is detached', node1.getParent());
    +}
    +
    +function testReplaceChild() {
    +  var root = new goog.structs.TreeNode(0, null);
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  root.addChild(node1);
    +
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  assertEquals('original node', node1, root.replaceChild(node2, node1));
    +  assertEquals('parent is set', root, node2.getParent());
    +  assertArrayEquals('children are updated', [node2], root.getChildren());
    +  assertNull('old child node is detached', node1.getParent());
    +}
    +
    +function testRemoveChildAt() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node1.addChild(node3);
    +
    +  assertNull('index too low', node1.removeChildAt(-1));
    +  assertNull('index too high', node1.removeChildAt(2));
    +  assertArrayEquals('node1 is intact', [node2, node3], node1.getChildren());
    +
    +  assertEquals('remove first child', node2, node1.removeChildAt(0));
    +  assertArrayEquals('remove from the front', [node3], node1.getChildren());
    +  assertNull('parent is unset', node2.getParent());
    +
    +  assertEquals('remove last child', node3, node1.removeChildAt(0));
    +  assertArrayEquals('remove last child', [], node1.getChildren());
    +  assertTrue('node1 became leaf', node1.isLeaf());
    +}
    +
    +function testRemoveChild() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node1.addChild(node3);
    +
    +  assertNull('remove null', node1.removeChild(null));
    +  assertNull('remove non-child', node1.removeChild(node1));
    +  assertArrayEquals('node1 is intact', [node2, node3], node1.getChildren());
    +
    +  assertEquals('remove node3, return value', node3, node1.removeChild(node3));
    +  assertArrayEquals('node is removed', [node2], node1.getChildren());
    +}
    +
    +function testRemoveChildren() {
    +  var node1 = new goog.structs.TreeNode(1, null);
    +  var node2 = new goog.structs.TreeNode(2, null);
    +  var node3 = new goog.structs.TreeNode(3, null);
    +  node1.addChild(node2);
    +  node1.addChild(node3);
    +
    +  node2.removeChildren();
    +  assertArrayEquals('removing a leaf node\'s children has no effect', [],
    +      node2.getChildren());
    +  assertEquals('node still has parent', node1, node2.getParent());
    +
    +  node1.removeChildren();
    +  assertArrayEquals('children have been removed', [], node1.getChildren());
    +  assertNull('children\'s parents have been unset', node2.getParent());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/trie.js b/mapbox-gl/closure-library/closure/goog/structs/trie.js
    new file mode 100644
    index 0000000000..e583290865
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/trie.js
    @@ -0,0 +1,395 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Datastructure: Trie.
    + *
    + *
    + * This file provides the implementation of a trie data structure.  A trie is a
    + * data structure that stores key/value pairs in a prefix tree.  See:
    + *     http://en.wikipedia.org/wiki/Trie
    + */
    +
    +
    +goog.provide('goog.structs.Trie');
    +
    +goog.require('goog.object');
    +goog.require('goog.structs');
    +
    +
    +
    +/**
    + * Class for a Trie datastructure.  Trie data structures are made out of trees
    + * of Trie classes.
    + *
    + * @param {goog.structs.Trie<VALUE>|Object<string, VALUE>=} opt_trie Optional
    + *     goog.structs.Trie or Object to initialize trie with.
    + * @constructor
    + * @template VALUE
    + */
    +goog.structs.Trie = function(opt_trie) {
    +  /**
    +   * This trie's value.  For the base trie, this will be the value of the
    +   * empty key, if defined.
    +   * @private {VALUE}
    +   */
    +  this.value_ = undefined;
    +
    +  /**
    +   * This trie's child nodes.
    +   * @private {!Object<!goog.structs.Trie<VALUE>>}
    +   */
    +  this.childNodes_ = {};
    +
    +  if (opt_trie) {
    +    this.setAll(opt_trie);
    +  }
    +};
    +
    +
    +/**
    + * Sets the given key/value pair in the trie.  O(L), where L is the length
    + * of the key.
    + * @param {string} key The key.
    + * @param {VALUE} value The value.
    + */
    +goog.structs.Trie.prototype.set = function(key, value) {
    +  this.setOrAdd_(key, value, false);
    +};
    +
    +
    +/**
    + * Adds the given key/value pair in the trie.  Throw an exception if the key
    + * already exists in the trie.  O(L), where L is the length of the key.
    + * @param {string} key The key.
    + * @param {VALUE} value The value.
    + */
    +goog.structs.Trie.prototype.add = function(key, value) {
    +  this.setOrAdd_(key, value, true);
    +};
    +
    +
    +/**
    + * Helper function for set and add.  Adds the given key/value pair to
    + * the trie, or, if the key already exists, sets the value of the key. If
    + * opt_add is true, then throws an exception if the key already has a value in
    + * the trie.  O(L), where L is the length of the key.
    + * @param {string} key The key.
    + * @param {VALUE} value The value.
    + * @param {boolean=} opt_add Throw exception if key is already in the trie.
    + * @private
    + */
    +goog.structs.Trie.prototype.setOrAdd_ = function(key, value, opt_add) {
    +  var node = this;
    +  for (var characterPosition = 0; characterPosition < key.length;
    +       characterPosition++) {
    +    var currentCharacter = key.charAt(characterPosition);
    +    if (!node.childNodes_[currentCharacter]) {
    +      node.childNodes_[currentCharacter] = new goog.structs.Trie();
    +    }
    +    node = node.childNodes_[currentCharacter];
    +  }
    +  if (opt_add && node.value_ !== undefined) {
    +    throw Error('The collection already contains the key "' + key + '"');
    +  } else {
    +    node.value_ = value;
    +  }
    +};
    +
    +
    +/**
    + * Adds multiple key/value pairs from another goog.structs.Trie or Object.
    + * O(N) where N is the number of nodes in the trie.
    + * @param {!Object<string, VALUE>|!goog.structs.Trie<VALUE>} trie Object
    + *     containing the data to add.
    + */
    +goog.structs.Trie.prototype.setAll = function(trie) {
    +  var keys = goog.structs.getKeys(trie);
    +  var values = goog.structs.getValues(trie);
    +
    +  for (var i = 0; i < keys.length; i++) {
    +    this.set(keys[i], values[i]);
    +  }
    +};
    +
    +
    +/**
    + * Traverse along the given path, returns the child node at ending.
    + * Returns undefined if node for the path doesn't exist.
    + * @param {string} path The path to traverse.
    + * @return {!goog.structs.Trie<VALUE>|undefined}
    + * @private
    + */
    +goog.structs.Trie.prototype.getChildNode_ = function(path) {
    +  var node = this;
    +  for (var characterPosition = 0; characterPosition < path.length;
    +      characterPosition++) {
    +    var currentCharacter = path.charAt(characterPosition);
    +    node = node.childNodes_[currentCharacter];
    +    if (!node) {
    +      return undefined;
    +    }
    +  }
    +  return node;
    +};
    +
    +
    +/**
    + * Retrieves a value from the trie given a key.  O(L), where L is the length of
    + * the key.
    + * @param {string} key The key to retrieve from the trie.
    + * @return {VALUE|undefined} The value of the key in the trie, or undefined if
    + *     the trie does not contain this key.
    + */
    +goog.structs.Trie.prototype.get = function(key) {
    +  var node = this.getChildNode_(key);
    +  return node ? node.value_ : undefined;
    +};
    +
    +
    +/**
    + * Retrieves all values from the trie that correspond to prefixes of the given
    + * input key. O(L), where L is the length of the key.
    + *
    + * @param {string} key The key to use for lookup. The given key as well as all
    + *     prefixes of the key are retrieved.
    + * @param {?number=} opt_keyStartIndex Optional position in key to start lookup
    + *     from. Defaults to 0 if not specified.
    + * @return {!Object<string, VALUE>} Map of end index of matching prefixes and
    + *     corresponding values. Empty if no match found.
    + */
    +goog.structs.Trie.prototype.getKeyAndPrefixes = function(key,
    +                                                         opt_keyStartIndex) {
    +  var node = this;
    +  var matches = {};
    +  var characterPosition = opt_keyStartIndex || 0;
    +
    +  if (node.value_ !== undefined) {
    +    matches[characterPosition] = node.value_;
    +  }
    +
    +  for (; characterPosition < key.length; characterPosition++) {
    +    var currentCharacter = key.charAt(characterPosition);
    +    if (!(currentCharacter in node.childNodes_)) {
    +      break;
    +    }
    +    node = node.childNodes_[currentCharacter];
    +    if (node.value_ !== undefined) {
    +      matches[characterPosition] = node.value_;
    +    }
    +  }
    +
    +  return matches;
    +};
    +
    +
    +/**
    + * Gets the values of the trie.  Not returned in any reliable order.  O(N) where
    + * N is the number of nodes in the trie.  Calls getValuesInternal_.
    + * @return {!Array<VALUE>} The values in the trie.
    + */
    +goog.structs.Trie.prototype.getValues = function() {
    +  var allValues = [];
    +  this.getValuesInternal_(allValues);
    +  return allValues;
    +};
    +
    +
    +/**
    + * Gets the values of the trie.  Not returned in any reliable order.  O(N) where
    + * N is the number of nodes in the trie.  Builds the values as it goes.
    + * @param {!Array<VALUE>} allValues Array to place values into.
    + * @private
    + */
    +goog.structs.Trie.prototype.getValuesInternal_ = function(allValues) {
    +  if (this.value_ !== undefined) {
    +    allValues.push(this.value_);
    +  }
    +  for (var childNode in this.childNodes_) {
    +    this.childNodes_[childNode].getValuesInternal_(allValues);
    +  }
    +};
    +
    +
    +/**
    + * Gets the keys of the trie.  Not returned in any reliable order.  O(N) where
    + * N is the number of nodes in the trie (or prefix subtree).
    + * @param {string=} opt_prefix Find only keys with this optional prefix.
    + * @return {!Array<string>} The keys in the trie.
    + */
    +goog.structs.Trie.prototype.getKeys = function(opt_prefix) {
    +  var allKeys = [];
    +  if (opt_prefix) {
    +    // Traverse to the given prefix, then call getKeysInternal_ to dump the
    +    // keys below that point.
    +    var node = this;
    +    for (var characterPosition = 0; characterPosition < opt_prefix.length;
    +        characterPosition++) {
    +      var currentCharacter = opt_prefix.charAt(characterPosition);
    +      if (!node.childNodes_[currentCharacter]) {
    +        return [];
    +      }
    +      node = node.childNodes_[currentCharacter];
    +    }
    +    node.getKeysInternal_(opt_prefix, allKeys);
    +  } else {
    +    this.getKeysInternal_('', allKeys);
    +  }
    +  return allKeys;
    +};
    +
    +
    +/**
    + * Private method to get keys from the trie.  Builds the keys as it goes.
    + * @param {string} keySoFar The partial key (prefix) traversed so far.
    + * @param {!Array<string>} allKeys The partially built array of keys seen so
    + *     far.
    + * @private
    + */
    +goog.structs.Trie.prototype.getKeysInternal_ = function(keySoFar, allKeys) {
    +  if (this.value_ !== undefined) {
    +    allKeys.push(keySoFar);
    +  }
    +  for (var childNode in this.childNodes_) {
    +    this.childNodes_[childNode].getKeysInternal_(keySoFar + childNode, allKeys);
    +  }
    +};
    +
    +
    +/**
    + * Checks to see if a certain key is in the trie.  O(L), where L is the length
    + * of the key.
    + * @param {string} key A key that may be in the trie.
    + * @return {boolean} Whether the trie contains key.
    + */
    +goog.structs.Trie.prototype.containsKey = function(key) {
    +  return this.get(key) !== undefined;
    +};
    +
    +
    +/**
    + * Checks to see if a certain prefix is in the trie. O(L), where L is the length
    + * of the prefix.
    + * @param {string} prefix A prefix that may be in the trie.
    + * @return {boolean} Whether any key of the trie has the prefix.
    + */
    +goog.structs.Trie.prototype.containsPrefix = function(prefix) {
    +  // Empty string is any key's prefix.
    +  if (prefix.length == 0) {
    +    return !this.isEmpty();
    +  }
    +  return !!this.getChildNode_(prefix);
    +};
    +
    +
    +/**
    + * Checks to see if a certain value is in the trie.  Worst case is O(N) where
    + * N is the number of nodes in the trie.
    + * @param {VALUE} value A value that may be in the trie.
    + * @return {boolean} Whether the trie contains the value.
    + */
    +goog.structs.Trie.prototype.containsValue = function(value) {
    +  if (this.value_ === value) {
    +    return true;
    +  }
    +  for (var childNode in this.childNodes_) {
    +    if (this.childNodes_[childNode].containsValue(value)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Completely empties a trie of all keys and values.  ~O(1)
    + */
    +goog.structs.Trie.prototype.clear = function() {
    +  this.childNodes_ = {};
    +  this.value_ = undefined;
    +};
    +
    +
    +/**
    + * Removes a key from the trie or throws an exception if the key is not in the
    + * trie.  O(L), where L is the length of the key.
    + * @param {string} key A key that should be removed from the trie.
    + * @return {VALUE} The value whose key was removed.
    + */
    +goog.structs.Trie.prototype.remove = function(key) {
    +  var node = this;
    +  var parents = [];
    +  for (var characterPosition = 0; characterPosition < key.length;
    +       characterPosition++) {
    +    var currentCharacter = key.charAt(characterPosition);
    +    if (!node.childNodes_[currentCharacter]) {
    +      throw Error('The collection does not have the key "' + key + '"');
    +    }
    +
    +    // Archive the current parent and child name (key in childNodes_) so that
    +    // we may remove the following node and its parents if they are empty.
    +    parents.push([node, currentCharacter]);
    +
    +    node = node.childNodes_[currentCharacter];
    +  }
    +  var oldValue = node.value_;
    +  delete node.value_;
    +
    +  while (parents.length > 0) {
    +    var currentParentAndCharacter = parents.pop();
    +    var currentParent = currentParentAndCharacter[0];
    +    var currentCharacter = currentParentAndCharacter[1];
    +    if (currentParent.childNodes_[currentCharacter].isEmpty()) {
    +      // If the child is empty, then remove it.
    +      delete currentParent.childNodes_[currentCharacter];
    +    } else {
    +      // No point of traversing back any further, since we can't remove this
    +      // path.
    +      break;
    +    }
    +  }
    +  return oldValue;
    +};
    +
    +
    +/**
    + * Clones a trie and returns a new trie.  O(N), where N is the number of nodes
    + * in the trie.
    + * @return {!goog.structs.Trie<VALUE>} A new goog.structs.Trie with the same
    + *     key value pairs.
    + */
    +goog.structs.Trie.prototype.clone = function() {
    +  return new goog.structs.Trie(this);
    +};
    +
    +
    +/**
    + * Returns the number of key value pairs in the trie.  O(N), where N is the
    + * number of nodes in the trie.
    + * TODO: This could be optimized by storing a weight (count below) in every
    + * node.
    + * @return {number} The number of pairs.
    + */
    +goog.structs.Trie.prototype.getCount = function() {
    +  return goog.structs.getCount(this.getValues());
    +};
    +
    +
    +/**
    + * Returns true if this trie contains no elements.  ~O(1).
    + * @return {boolean} True iff this trie contains no elements.
    + */
    +goog.structs.Trie.prototype.isEmpty = function() {
    +  return this.value_ === undefined && goog.object.isEmpty(this.childNodes_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/trie_test.html b/mapbox-gl/closure-library/closure/goog/structs/trie_test.html
    new file mode 100644
    index 0000000000..1921955903
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/trie_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.structs.Trie
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.structs.TrieTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/trie_test.js b/mapbox-gl/closure-library/closure/goog/structs/trie_test.js
    new file mode 100644
    index 0000000000..57935cf35e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/trie_test.js
    @@ -0,0 +1,454 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.structs.TrieTest');
    +goog.setTestOnly('goog.structs.TrieTest');
    +
    +goog.require('goog.object');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Trie');
    +goog.require('goog.testing.jsunit');
    +
    +function makeTrie() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('hello', 1);
    +  trie.add('hi', 'howdy');
    +  trie.add('', 'an empty string key');
    +  trie.add('empty value', '');
    +  trie.add('zero', 0);
    +  trie.add('object', {});
    +  trie.add('null', null);
    +  trie.add('hello, world', 2);
    +  trie.add('world', {});
    +  return trie;
    +}
    +
    +function checkTrie(trie) {
    +  assertEquals('get, should be 1', trie.get('hello'), 1);
    +  assertEquals('get, should be "howdy"', trie.get('hi'), 'howdy');
    +  assertEquals('get, should be "an empty string key"', trie.get(''),
    +      'an empty string key');
    +  assertEquals('get, should be ""', trie.get('empty value'), '');
    +  assertEquals('get, should be ""', typeof trie.get('empty value'), 'string');
    +  assertEquals('get, should be an object', typeof trie.get('object'), 'object');
    +  assertEquals('get, should be 0', trie.get('zero'), 0);
    +  assertEquals('get "null", should be null', trie.get('null'), null);
    +  assertEquals('get, should be 2', trie.get('hello, world'), 2);
    +  assertEquals('get, should be an object', typeof trie.get('world'), 'object');
    +}
    +
    +function testTrieFormation() {
    +  var t = makeTrie();
    +  checkTrie(t);
    +}
    +
    +function testFailureOfMultipleAdds() {
    +  var t = new goog.structs.Trie();
    +  t.add('hello', 'testing');
    +  assertThrows('Error should be thrown when same key added twice.', function() {
    +    t.add('hello', 'test');
    +  });
    +
    +  t = new goog.structs.Trie();
    +  t.add('null', null);
    +  assertThrows('Error should be thrown when same key added twice.', function() {
    +    t.add('null', 'hi!');
    +  });
    +
    +  t = new goog.structs.Trie();
    +  t.add('null', 'blah');
    +  assertThrows('Error should be thrown when same key added twice.', function() {
    +    t.add('null', null);
    +  });
    +}
    +
    +function testTrieClone() {
    +  var trieOne = makeTrie();
    +  var trieTwo = new goog.structs.Trie(trieOne);
    +  checkTrie(trieTwo);
    +}
    +
    +function testTrieFromObject() {
    +  var someObject = {'hello' : 1,
    +    'hi' : 'howdy',
    +    '' : 'an empty string key',
    +    'empty value' : '',
    +    'object' : {},
    +    'zero' : 0,
    +    'null' : null,
    +    'hello, world' : 2,
    +    'world' : {}};
    +  var trie = new goog.structs.Trie(someObject);
    +  checkTrie(trie);
    +}
    +
    +function testTrieGetValues() {
    +  var trie = makeTrie();
    +  var values = trie.getValues();
    +  assertTrue('getValues, should contain "howdy"',
    +      goog.object.contains(values, 'howdy'));
    +  assertTrue('getValues, should contain 1', goog.object.contains(values, 1));
    +  assertTrue('getValues, should contain 0', goog.object.contains(values, 0));
    +  assertTrue('getValues, should contain ""', goog.object.contains(values, ''));
    +  assertTrue('getValues, should contain null',
    +      goog.object.contains(values, null));
    +  assertEquals('goog.structs.getCount(getValues()) should be 9',
    +      goog.structs.getCount(values), 9);
    +}
    +
    +function testTrieGetKeys() {
    +  var trie = makeTrie();
    +  var keys = trie.getKeys();
    +  assertTrue('getKeys, should contain "hello"',
    +      goog.object.contains(keys, 'hello'));
    +  assertTrue('getKeys, should contain "empty value"',
    +      goog.object.contains(keys, 'empty value'));
    +  assertTrue('getKeys, should contain ""', goog.object.contains(keys, ''));
    +  assertTrue('getKeys, should contain "zero"',
    +      goog.object.contains(keys, 'zero'));
    +  assertEquals('goog.structs.getCount(getKeys()) should be 9',
    +      goog.structs.getCount(keys), 9);
    +}
    +
    +
    +function testTrieCount() {
    +  var trieOne = makeTrie();
    +  var trieTwo = new goog.structs.Trie();
    +  assertEquals('count, should be 9', trieOne.getCount(), 9);
    +  assertEquals('count, should be 0', trieTwo.getCount(), 0);
    +}
    +
    +function testRemoveKeyFromTrie() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('key1', 'value1');
    +  trie.add('key2', 'value2');
    +  trie.add('ke', 'value3');
    +  trie.add('zero', 0);
    +  trie.remove('key2');
    +  assertEquals('get "key1", should be "value1"', trie.get('key1'), 'value1');
    +  assertUndefined('get "key2", should be undefined', trie.get('key2'));
    +  trie.remove('zero');
    +  assertUndefined('get "zero", should be undefined', trie.get('zero'));
    +  trie.remove('ke');
    +  assertUndefined('get "ke", should be undefined', trie.get('ke'));
    +  assertEquals('get "key1", should be "value1"', trie.get('key1'), 'value1');
    +  trie.add('a', 'value4');
    +  assertTrue('testing internal structure, a should be a child',
    +      'a' in trie.childNodes_);
    +  trie.remove('a');
    +  assertFalse('testing internal structure, a should no longer be a child',
    +      'a' in trie.childNodes_);
    +
    +  trie.add('xyza', 'value');
    +  trie.remove('xyza', 'value');
    +  assertFalse('Should not have "x"', 'x' in trie.childNodes_);
    +
    +  trie.add('xyza', null);
    +  assertTrue('Should have "x"', 'x' in trie.childNodes_);
    +  trie.remove('xyza');
    +  assertFalse('Should not have "x"', 'x' in trie.childNodes_);
    +
    +  trie.add('xyza', 'value');
    +  trie.add('xb', 'value');
    +  trie.remove('xyza');
    +  assertTrue('get "x" should be defined', 'x' in trie.childNodes_);
    +  assertFalse('get "y" should be undefined',
    +      'y' in trie.childNodes_['x'].childNodes_);
    +
    +  trie.add('akey', 'value1');
    +  trie.add('akey1', 'value2');
    +  trie.remove('akey1');
    +  assertEquals('get "akey", should be "value1"', 'value1', trie.get('akey'));
    +  assertUndefined('get "akey1", should be undefined', trie.get('akey1'));
    +}
    +
    +function testRemoveKeyFromTrieWithNulls() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('key1', null);
    +  trie.add('key2', 'value2');
    +  trie.add('ke', 'value3');
    +  trie.add('zero', 0);
    +  trie.remove('key2');
    +  assertEquals('get "key1", should be null', trie.get('key1'), null);
    +  assertUndefined('get "key2", should be undefined', trie.get('key2'));
    +  trie.remove('zero');
    +  assertUndefined('get "zero", should be undefined', trie.get('zero'));
    +  trie.remove('ke');
    +  assertUndefined('get "ke", should be undefined', trie.get('ke'));
    +  assertEquals('get "key1", should be null', trie.get('key1'), null);
    +  trie.add('a', 'value4');
    +  assertTrue('testing internal structure, a should be a child',
    +      'a' in trie.childNodes_);
    +  trie.remove('a');
    +  assertFalse('testing internal structure, a should no longer be a child',
    +      'a' in trie.childNodes_);
    +
    +  trie.add('xyza', null);
    +  trie.add('xb', 'value');
    +  trie.remove('xyza');
    +  assertTrue('Should have "x"', 'x' in trie.childNodes_);
    +  assertFalse('Should not have "y"',
    +      'y' in trie.childNodes_['x'].childNodes_);
    +}
    +
    +function testRemoveKeyException() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('abcdefg', 'value');
    +  trie.add('abcz', 'value');
    +  trie.add('abc', 'value');
    +
    +  assertThrows('Remove should throw an error on removal of non-existent key',
    +      function() {
    +        trie.remove('abcdefge');
    +      });
    +}
    +
    +function testTrieIsEmpty() {
    +  var trieOne = new goog.structs.Trie();
    +  var trieTwo = makeTrie();
    +  assertTrue('isEmpty, should be empty', trieOne.isEmpty());
    +  assertFalse('isEmpty, should not be empty', trieTwo.isEmpty());
    +  trieOne.add('', 1);
    +  assertFalse('isEmpty, should not be empty', trieTwo.isEmpty());
    +  trieOne.remove('');
    +  assertTrue('isEmpty, should be empty', trieOne.isEmpty());
    +  trieOne.add('', 1);
    +  trieOne.add('a', 1);
    +  trieOne.remove('a');
    +  assertFalse('isEmpty, should not be empty', trieOne.isEmpty());
    +  trieOne.remove('');
    +  assertTrue('isEmpty, should be empty', trieOne.isEmpty());
    +  trieOne.add('', 1);
    +  trieOne.add('a', 1);
    +  trieOne.remove('');
    +  assertFalse('isEmpty, should not be empty', trieOne.isEmpty());
    +  trieOne.remove('a');
    +  assertTrue('isEmpty, should be empty', trieOne.isEmpty());
    +}
    +
    +function testTrieClear() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('key1', 'value1');
    +  trie.add('key2', 'value2');
    +  trie.add('key3', null);
    +  trie.clear();
    +  assertUndefined('get key1, should be undefined', trie.get('key1'));
    +  assertUndefined('get key2, should be undefined', trie.get('key2'));
    +  assertUndefined('get key3, should be undefined', trie.get('key3'));
    +}
    +
    +function testTrieContainsKey() {
    +  var trie = makeTrie();
    +  assertTrue('containsKey, should contain "hello"', trie.containsKey('hello'));
    +  assertTrue('containsKey, should contain "hi"', trie.containsKey('hi'));
    +  assertTrue('containsKey, should contain ""', trie.containsKey(''));
    +  assertTrue('containsKey, should contain "empty value"',
    +      trie.containsKey('empty value'));
    +  assertTrue('containsKey, should contain "object"',
    +      trie.containsKey('object'));
    +  assertTrue('containsKey, should contain "zero"', trie.containsKey('zero'));
    +  assertTrue('containsKey, should contain "null"', trie.containsKey('null'));
    +  assertFalse('containsKey, should not contain "blah"',
    +      trie.containsKey('blah'));
    +  trie.remove('');
    +  trie.remove('hi');
    +  trie.remove('zero');
    +  trie.remove('null');
    +  assertFalse('containsKey, should not contain "zero"',
    +      trie.containsKey('zero'));
    +  assertFalse('containsKey, should not contain ""', trie.containsKey(''));
    +  assertFalse('containsKey, should not contain "hi"', trie.containsKey('hi'));
    +  assertFalse('containsKey, should not contain "null"',
    +      trie.containsKey('null'));
    +}
    +
    +function testTrieContainsPrefix() {
    +
    +  // Empty trie.
    +  var trie = new goog.structs.Trie();
    +  assertFalse('containsPrefix, should not contain ""', trie.containsPrefix(''));
    +  assertFalse('containsPrefix, should not contain "any"',
    +      trie.containsPrefix('any'));
    +  trie.add('key', 'value');
    +  assertTrue('containsPrefix, should contain ""', trie.containsPrefix(''));
    +
    +  // Non-empty trie.
    +  trie = makeTrie();
    +  assertTrue('containsPrefix, should contain ""', trie.containsPrefix(''));
    +  assertFalse('containsPrefix, should not contain "blah"',
    +      trie.containsPrefix('blah'));
    +  assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h'));
    +  assertTrue('containsPrefix, should contain "hello"',
    +      trie.containsPrefix('hello'));
    +  assertTrue('containsPrefix, should contain "hello, world"',
    +      trie.containsPrefix('hello, world'));
    +  assertFalse('containsPrefix, should not contain "hello, world!"',
    +      trie.containsPrefix('hello, world!'));
    +  assertTrue('containsPrefix, should contain "nu"',
    +      trie.containsPrefix('nu'));
    +  assertTrue('containsPrefix, should contain "null"',
    +      trie.containsPrefix('null'));
    +  assertTrue('containsPrefix, should contain "empty value"',
    +      trie.containsPrefix('empty value'));
    +
    +  // Remove nodes.
    +  trie.remove('');
    +  assertTrue('containsPrefix, should contain ""', trie.containsPrefix(''));
    +  trie.remove('hi');
    +  assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h'));
    +  assertFalse('containsPrefix, should not contain "hi"',
    +      trie.containsPrefix('hi'));
    +  trie.remove('hello');
    +  trie.remove('hello, world');
    +  assertFalse('containsPrefix, should not contain "h"',
    +      trie.containsPrefix('h'));
    +  assertFalse('containsPrefix, should not contain "hello"',
    +      trie.containsPrefix('hello'));
    +
    +  // Remove all nodes.
    +  trie.remove('empty value');
    +  trie.remove('zero');
    +  trie.remove('object');
    +  trie.remove('null');
    +  trie.remove('world');
    +  assertFalse('containsPrefix, should not contain ""',
    +      trie.containsPrefix(''));
    +  assertFalse('containsPrefix, should not contain "h"',
    +      trie.containsPrefix('h'));
    +  assertFalse('containsPrefix, should not contain "hi"',
    +      trie.containsPrefix('hi'));
    +
    +  // Add some new nodes.
    +  trie.add('hi', 'value');
    +  trie.add('null', 'value');
    +  assertTrue('containsPrefix, should contain ""', trie.containsPrefix(''));
    +  assertTrue('containsPrefix, should contain "h"', trie.containsPrefix('h'));
    +  assertTrue('containsPrefix, should contain "hi"', trie.containsPrefix('hi'));
    +  assertFalse('containsPrefix, should not contain "hello"',
    +      trie.containsPrefix('hello'));
    +  assertFalse('containsPrefix, should not contain "zero"',
    +      trie.containsPrefix('zero'));
    +
    +  // Clear the trie.
    +  trie.clear();
    +  assertFalse('containsPrefix, should not contain ""',
    +      trie.containsPrefix(''));
    +  assertFalse('containsPrefix, should not contain "h"',
    +      trie.containsPrefix('h'));
    +  assertFalse('containsPrefix, should not contain "hi"',
    +      trie.containsPrefix('hi'));
    +}
    +
    +function testTrieContainsValue() {
    +  var trie = makeTrie();
    +  assertTrue('containsValue, should be true, should contain 1',
    +      trie.containsValue(1));
    +  assertTrue('containsValue, should be true, should contain "howdy"',
    +      trie.containsValue('howdy'));
    +  assertTrue('containsValue, should be true, should contain ""',
    +      trie.containsValue(''));
    +  assertTrue('containsValue, should be true, should contain 0',
    +      trie.containsValue(0));
    +  assertTrue('containsValue, should be true, should contain null',
    +      trie.containsValue(null));
    +  assertTrue('containsValue, should be true, should ' +
    +      'contain "an empty string key"',
    +      trie.containsValue('an empty string key'));
    +  assertFalse('containsValue, should be false, should not contain "blah"',
    +      trie.containsValue('blah'));
    +  trie.remove('empty value');
    +  trie.remove('zero');
    +  assertFalse('containsValue, should be false, should not contain 0',
    +      trie.containsValue(0));
    +  assertFalse('containsValue, should be false, should not contain ""',
    +      trie.containsValue(''));
    +}
    +
    +function testTrieHandlingOfEmptyStrings() {
    +  var trie = new goog.structs.Trie();
    +  assertEquals('get, should be undefined', trie.get(''), undefined);
    +  assertFalse('containsValue, should be false', trie.containsValue(''));
    +  assertFalse('containsKey, should be false', trie.containsKey(''));
    +  trie.add('', 'test');
    +  trie.add('test2', '');
    +  assertTrue('containsValue, should be true', trie.containsValue(''));
    +  assertTrue('containsKey, should be true', trie.containsKey(''));
    +  assertEquals('get, should be "test"', trie.get(''), 'test');
    +  assertEquals('get, should be ""', trie.get('test2'), '');
    +  trie.remove('');
    +  trie.remove('test2');
    +  assertEquals('get, should be undefined', trie.get(''), undefined);
    +  assertFalse('containsValue, should be false', trie.containsValue(''));
    +  assertFalse('containsKey, should be false', trie.containsKey(''));
    +}
    +
    +function testPrefixOptionOnGetKeys() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('abcdefg', 'one');
    +  trie.add('abcdefghijk', 'two');
    +  trie.add('abcde', 'three');
    +  trie.add('abcq', null);
    +  trie.add('abc', 'four');
    +  trie.add('xyz', 'five');
    +  assertEquals('getKeys, should be 1', trie.getKeys('xy').length, 1);
    +  assertEquals('getKeys, should be 1', trie.getKeys('xyz').length, 1);
    +  assertEquals('getKeys, should be 1', trie.getKeys('x').length, 1);
    +  assertEquals('getKeys, should be 4', trie.getKeys('abc').length, 5);
    +  assertEquals('getKeys, should be 2', trie.getKeys('abcdef').length, 2);
    +  assertEquals('getKeys, should be 0', trie.getKeys('abcdefgi').length, 0);
    +}
    +
    +function testGetKeyAndPrefixes() {
    +  var trie = makeTrie();
    +  // Note: trie has one of its keys as ''
    +  assertEquals('getKeyAndPrefixes, should be 2',
    +               2,
    +               goog.object.getCount(trie.getKeyAndPrefixes('world')));
    +  assertEquals('getKeyAndPrefixes, should be 2',
    +               2,
    +               goog.object.getCount(trie.getKeyAndPrefixes('hello')));
    +  assertEquals('getKeyAndPrefixes, should be 2',
    +               2,
    +               goog.object.getCount(trie.getKeyAndPrefixes('hello,')));
    +  assertEquals('getKeyAndPrefixes, should be 3',
    +               3,
    +               goog.object.getCount(trie.getKeyAndPrefixes('hello, world')));
    +  assertEquals('getKeyAndPrefixes, should be 1',
    +               1,
    +               goog.object.getCount(trie.getKeyAndPrefixes('hell')));
    +}
    +
    +function testGetKeyAndPrefixesStartIndex() {
    +  var trie = new goog.structs.Trie();
    +  trie.add('abcdefg', 'one');
    +  trie.add('bcdefg', 'two');
    +  trie.add('abcdefghijk', 'three');
    +  trie.add('abcde', 'four');
    +  trie.add('abcq', null);
    +  trie.add('q', null);
    +  trie.add('abc', 'five');
    +  trie.add('xyz', 'six');
    +  assertEquals('getKeyAndPrefixes, should be 3',
    +               3,
    +               goog.object.getCount(trie.getKeyAndPrefixes('abcdefg', 0)));
    +  assertEquals('getKeyAndPrefixes, should be 1',
    +               1,
    +               goog.object.getCount(trie.getKeyAndPrefixes('abcdefg', 1)));
    +  assertEquals('getKeyAndPrefixes, should be 1',
    +               1,
    +               goog.object.getCount(trie.getKeyAndPrefixes('abcq', 3)));
    +  assertEquals('getKeyAndPrefixes, should be 0',
    +               0,
    +               goog.object.getCount(trie.getKeyAndPrefixes('abcd', 3)));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/weak/weak.js b/mapbox-gl/closure-library/closure/goog/structs/weak/weak.js
    new file mode 100644
    index 0000000000..bfc3292fce
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/weak/weak.js
    @@ -0,0 +1,159 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Common code for weak collections.
    + *
    + * The helpers in this file are used for the shim implementations of
    + * {@code goog.structs.weak.Map} and {@code goog.structs.weak.Set}, for browsers
    + * that do not support ECMAScript 6 native WeakMap and WeakSet.
    + *
    + * IMPORTANT CAVEAT: On browsers that do not provide native WeakMap and WeakSet
    + * implementations, these data structure are only partially weak, and CAN LEAK
    + * MEMORY. Specifically, if a key is no longer held, the key-value pair (in the
    + * case of Map) and some internal metadata (in the case of Set), can be garbage
    + * collected; however, if a key is still held when a Map or Set is no longer
    + * held, the value and metadata will not be collected.
    + *
    + * RECOMMENDATIONS: If the lifetime of the weak collection is expected to be
    + * shorter than that of its keys, the keys should be explicitly removed from the
    + * collection when they are disposed. If this is not possible, this library may
    + * be inappopriate for the application.
    + *
    + * BROWSER COMPATIBILITY: This library is compatible with browsers with a
    + * correct implementation of Object.defineProperty (IE9+, FF4+, SF5.1+, CH5+,
    + * OP12+, etc).
    + * @see goog.structs.weak.SUPPORTED_BROWSER
    + * @see http://kangax.github.io/compat-table/es5/#Object.defineProperty
    + *
    + * @package
    + */
    +
    +
    +goog.provide('goog.structs.weak');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Whether this browser supports weak collections, using either the native or
    + * shim implementation.
    + * @const
    + */
    +// Only test for shim, since ES6 native WeakMap/Set imply ES5 shim dependencies
    +goog.structs.weak.SUPPORTED_BROWSER = Object.defineProperty &&
    +    // IE<9 and Safari<5.1 cannot defineProperty on some objects
    +    !(goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) &&
    +    !(goog.userAgent.SAFARI && !goog.userAgent.isVersionOrHigher('534.48.3'));
    +
    +
    +/**
    + * Whether to use the browser's native WeakMap.
    + * @const
    + */
    +goog.structs.weak.USE_NATIVE_WEAKMAP = 'WeakMap' in goog.global &&
    +    // Firefox<24 WeakMap disallows some objects as keys
    +    // See https://github.com/Polymer/WeakMap/issues/3
    +    !(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('24'));
    +
    +
    +/**
    + * Whether to use the browser's native WeakSet.
    + * @const
    + */
    +goog.structs.weak.USE_NATIVE_WEAKSET = 'WeakSet' in goog.global;
    +
    +
    +/** @const */
    +goog.structs.weak.WEAKREFS_PROPERTY_NAME = '__shimWeakrefs__';
    +
    +
    +/**
    + * Counter used to generate unique ID for shim.
    + * @private
    + */
    +goog.structs.weak.counter_ = 0;
    +
    +
    +/**
    + * Generate a unique ID for shim.
    + * @return {string}
    + */
    +goog.structs.weak.generateId = function() {
    +  return (Math.random() * 1e9 >>> 0) + '' +
    +      (goog.structs.weak.counter_++ % 1e9);
    +};
    +
    +
    +/**
    + * Checks that the key is an extensible object, otherwise throws an Error.
    + * @param {*} key The key.
    + */
    +goog.structs.weak.checkKeyType = function(key) {
    +  if (!goog.isObject(key)) {
    +    throw TypeError('Invalid value used in weak collection');
    +  }
    +  if (Object.isExtensible && !Object.isExtensible(key)) {
    +    throw TypeError('Unsupported non-extensible object used as weak map key');
    +  }
    +};
    +
    +
    +/**
    + * Adds a key-value pair to the collection with the given ID. Helper for shim
    + * implementations of Map#set and Set#add.
    + * @param {string} id The unique ID of the shim weak collection.
    + * @param {*} key The key.
    + * @param {*} value value to add.
    + */
    +goog.structs.weak.set = function(id, key, value) {
    +  goog.structs.weak.checkKeyType(key);
    +  if (!key.hasOwnProperty(goog.structs.weak.WEAKREFS_PROPERTY_NAME)) {
    +    // Use defineProperty to make property non-enumerable
    +    Object.defineProperty(/** @type {!Object} */(key),
    +        goog.structs.weak.WEAKREFS_PROPERTY_NAME, {value: {}});
    +  }
    +  key[goog.structs.weak.WEAKREFS_PROPERTY_NAME][id] = value;
    +};
    +
    +
    +/**
    + * Returns whether the collection with the given ID contains the given
    + * key. Helper for shim implementations of Map#containsKey and Set#contains.
    + * @param {string} id The unique ID of the shim weak collection.
    + * @param {*} key The key to check for.
    + * @return {boolean}
    + */
    +goog.structs.weak.has = function(id, key) {
    +  goog.structs.weak.checkKeyType(key);
    +  return key.hasOwnProperty(goog.structs.weak.WEAKREFS_PROPERTY_NAME) ?
    +      id in key[goog.structs.weak.WEAKREFS_PROPERTY_NAME] :
    +      false;
    +};
    +
    +
    +/**
    + * Removes a key-value pair based on the key. Helper for shim implementations of
    + * Map#remove and Set#remove.
    + * @param {string} id The unique ID of the shim weak collection.
    + * @param {*} key The key to remove.
    + * @return {boolean} Whether object was removed.
    + */
    +goog.structs.weak.remove = function(id, key) {
    +  if (goog.structs.weak.has(id, key)) {
    +    delete key[goog.structs.weak.WEAKREFS_PROPERTY_NAME][id];
    +    return true;
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/structs/weak/weak_test.js b/mapbox-gl/closure-library/closure/goog/structs/weak/weak_test.js
    new file mode 100644
    index 0000000000..e799e94b16
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/structs/weak/weak_test.js
    @@ -0,0 +1,75 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tests for {@code goog.structs.weak}. set, has, and remove are
    + * exercised in {@code goog.structs.weak.MapTest} and
    + * {@code goog.structs.weak.SetTest}.
    + *
    + */
    +
    +
    +goog.provide('goog.structs.weakTest');
    +goog.setTestOnly('goog.structs.weakTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.structs.weak');
    +goog.require('goog.testing.jsunit');
    +
    +
    +function shouldRunTests() {
    +  return goog.structs.weak.SUPPORTED_BROWSER;
    +}
    +
    +
    +function testGenerateId() {
    +  assertNotEquals(goog.structs.weak.generateId(),
    +                  goog.structs.weak.generateId());
    +}
    +
    +
    +function testCheckKeyTypeValidObject() {
    +  var validKeys = [{}, [], document.body, /RegExp/, goog.nullFunction];
    +  goog.array.forEach(validKeys, function(key) {
    +    // no error
    +    goog.structs.weak.checkKeyType(key);
    +  });
    +}
    +
    +
    +function testCheckKeyTypePrimitive() {
    +  var primitiveKeys = ['test', 1, true, null, undefined];
    +  goog.array.forEach(primitiveKeys, function(key) {
    +    assertThrows(function() {
    +      goog.structs.weak.checkKeyType(key);
    +    });
    +  });
    +}
    +
    +
    +function testCheckKeyTypeNonExtensibleObject() {
    +  var sealedObj = {}, frozenObj = {}, preventExtensionsObj = {};
    +  Object.seal(sealedObj);
    +  Object.freeze(frozenObj);
    +  Object.preventExtensions(preventExtensionsObj);
    +  assertThrows(function() {
    +    goog.structs.weak.checkKeyType(sealedObj);
    +  });
    +  assertThrows(function() {
    +    goog.structs.weak.checkKeyType(frozenObj);
    +  });
    +  assertThrows(function() {
    +    goog.structs.weak.checkKeyType(preventExtensionsObj);
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/bidi.js b/mapbox-gl/closure-library/closure/goog/style/bidi.js
    new file mode 100644
    index 0000000000..2d5c7c586d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/bidi.js
    @@ -0,0 +1,184 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Bidi utility functions.
    + *
    + */
    +
    +goog.provide('goog.style.bidi');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Returns the normalized scrollLeft position for a scrolled element.
    + * @param {Element} element The scrolled element.
    + * @return {number} The number of pixels the element is scrolled. 0 indicates
    + *     that the element is not scrolled at all (which, in general, is the
    + *     left-most position in ltr and the right-most position in rtl).
    + */
    +goog.style.bidi.getScrollLeft = function(element) {
    +  var isRtl = goog.style.isRightToLeft(element);
    +  if (isRtl && goog.userAgent.GECKO) {
    +    // ScrollLeft starts at 0 and then goes negative as the element is scrolled
    +    // towards the left.
    +    return -element.scrollLeft;
    +  } else if (isRtl &&
    +             !(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8'))) {
    +    // ScrollLeft starts at the maximum positive value and decreases towards
    +    // 0 as the element is scrolled towards the left. However, for overflow
    +    // visible, there is no scrollLeft and the value always stays correctly at 0
    +    var overflowX = goog.style.getComputedOverflowX(element);
    +    if (overflowX == 'visible') {
    +      return element.scrollLeft;
    +    } else {
    +      return element.scrollWidth - element.clientWidth - element.scrollLeft;
    +    }
    +  }
    +  // ScrollLeft behavior is identical in rtl and ltr, it starts at 0 and
    +  // increases as the element is scrolled away from the start.
    +  return element.scrollLeft;
    +};
    +
    +
    +/**
    + * Returns the "offsetStart" of an element, analagous to offsetLeft but
    + * normalized for right-to-left environments and various browser
    + * inconsistencies. This value returned can always be passed to setScrollOffset
    + * to scroll to an element's left edge in a left-to-right offsetParent or
    + * right edge in a right-to-left offsetParent.
    + *
    + * For example, here offsetStart is 10px in an LTR environment and 5px in RTL:
    + *
    + * <pre>
    + * |          xxxxxxxxxx     |
    + *  ^^^^^^^^^^   ^^^^   ^^^^^
    + *     10px      elem    5px
    + * </pre>
    + *
    + * If an element is positioned before the start of its offsetParent, the
    + * startOffset may be negative.  This can be used with setScrollOffset to
    + * reliably scroll to an element:
    + *
    + * <pre>
    + * var scrollOffset = goog.style.bidi.getOffsetStart(element);
    + * goog.style.bidi.setScrollOffset(element.offsetParent, scrollOffset);
    + * </pre>
    + *
    + * @see setScrollOffset
    + *
    + * @param {Element} element The element for which we need to determine the
    + *     offsetStart position.
    + * @return {number} The offsetStart for that element.
    + */
    +goog.style.bidi.getOffsetStart = function(element) {
    +  var offsetLeftForReal = element.offsetLeft;
    +
    +  // The element might not have an offsetParent.
    +  // For example, the node might not be attached to the DOM tree,
    +  // and position:fixed children do not have an offset parent.
    +  // Just try to do the best we can with what we have.
    +  var bestParent = element.offsetParent;
    +
    +  if (!bestParent && goog.style.getComputedPosition(element) == 'fixed') {
    +    bestParent = goog.dom.getOwnerDocument(element).documentElement;
    +  }
    +
    +  // Just give up in this case.
    +  if (!bestParent) {
    +    return offsetLeftForReal;
    +  }
    +
    +  if (goog.userAgent.GECKO) {
    +    // When calculating an element's offsetLeft, Firefox erroneously subtracts
    +    // the border width from the actual distance.  So we need to add it back.
    +    var borderWidths = goog.style.getBorderBox(bestParent);
    +    offsetLeftForReal += borderWidths.left;
    +  } else if (goog.userAgent.isDocumentModeOrHigher(8) &&
    +             !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    // When calculating an element's offsetLeft, IE8/9-Standards Mode
    +    // erroneously adds the border width to the actual distance.  So we need to
    +    // subtract it.
    +    var borderWidths = goog.style.getBorderBox(bestParent);
    +    offsetLeftForReal -= borderWidths.left;
    +  }
    +
    +  if (goog.style.isRightToLeft(bestParent)) {
    +    // Right edge of the element relative to the left edge of its parent.
    +    var elementRightOffset = offsetLeftForReal + element.offsetWidth;
    +
    +    // Distance from the parent's right edge to the element's right edge.
    +    return bestParent.clientWidth - elementRightOffset;
    +  }
    +
    +  return offsetLeftForReal;
    +};
    +
    +
    +/**
    + * Sets the element's scrollLeft attribute so it is correctly scrolled by
    + * offsetStart pixels.  This takes into account whether the element is RTL and
    + * the nuances of different browsers.  To scroll to the "beginning" of an
    + * element use getOffsetStart to obtain the element's offsetStart value and then
    + * pass the value to setScrollOffset.
    + * @see getOffsetStart
    + * @param {Element} element The element to set scrollLeft on.
    + * @param {number} offsetStart The number of pixels to scroll the element.
    + *     If this value is < 0, 0 is used.
    + */
    +goog.style.bidi.setScrollOffset = function(element, offsetStart) {
    +  offsetStart = Math.max(offsetStart, 0);
    +  // In LTR and in "mirrored" browser RTL (such as IE), we set scrollLeft to
    +  // the number of pixels to scroll.
    +  // Otherwise, in RTL, we need to account for different browser behavior.
    +  if (!goog.style.isRightToLeft(element)) {
    +    element.scrollLeft = offsetStart;
    +  } else if (goog.userAgent.GECKO) {
    +    // Negative scroll-left positions in RTL.
    +    element.scrollLeft = -offsetStart;
    +  } else if (!(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8'))) {
    +    // Take the current scrollLeft value and move to the right by the
    +    // offsetStart to get to the left edge of the element, and then by
    +    // the clientWidth of the element to get to the right edge.
    +    element.scrollLeft =
    +        element.scrollWidth - offsetStart - element.clientWidth;
    +  } else {
    +    element.scrollLeft = offsetStart;
    +  }
    +};
    +
    +
    +/**
    + * Sets the element's left style attribute in LTR or right style attribute in
    + * RTL.  Also clears the left attribute in RTL and the right attribute in LTR.
    + * @param {Element} elem The element to position.
    + * @param {number} left The left position in LTR; will be set as right in RTL.
    + * @param {?number} top The top position.  If null only the left/right is set.
    + * @param {boolean} isRtl Whether we are in RTL mode.
    + */
    +goog.style.bidi.setPosition = function(elem, left, top, isRtl) {
    +  if (!goog.isNull(top)) {
    +    elem.style.top = top + 'px';
    +  }
    +  if (isRtl) {
    +    elem.style.right = left + 'px';
    +    elem.style.left = '';
    +  } else {
    +    elem.style.left = left + 'px';
    +    elem.style.right = '';
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/style/bidi_test.html b/mapbox-gl/closure-library/closure/goog/style/bidi_test.html
    new file mode 100644
    index 0000000000..05c0e26b64
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/bidi_test.html
    @@ -0,0 +1,139 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.style.bidi
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.style.bidiTest');
    +  </script>
    +  <style>
    +   /* Using borders, padding, and margins of various prime values. */
    +    .scrollDiv {
    +      left:50px; width:250px; height: 100px;
    +      position:absolute; overflow:auto; border-left: 3px solid green;
    +      border-right: 17px solid green; margin: 7px; padding: 13px;
    +    }
    +  </style>
    + </head>
    + <body>
    +  <span id="bodyChild" style="position:fixed;left:60px">
    +   bodyChild
    +  </span>
    +  <div>
    +   <span>
    +    LTR
    +   </span>
    +   <div dir="ltr" onscroll="updateInfo()" id="scrollDivLtr" class="scrollDiv">
    +    <div style="width: 1000px; height: 2000px;background-color: blue">
    +    </div>
    +    <div id="scrolledElementLtr" style="background:yellow; top: 25px; left:85px;
    +             width: 100px; position:absolute">
    +     elm
    +    </div>
    +   </div>
    +   <div style="left:400px; position:absolute;">
    +    <div>
    +     elm.offsetParent.scrollLeft:
    +     <span id="elementScrollLeftLtr">
    +     </span>
    +    </div>
    +    <div>
    +     bidi.getScrollLeft(...):
    +     <span id="bidiScrollLeftLtr">
    +     </span>
    +    </div>
    +    <div>
    +     bidi.getOffsetStart(...):
    +     <span id="bidiOffsetStartLtr">
    +     </span>
    +    </div>
    +    <form name="formLtr" action="bidi_test.html#">
    +     goog.style.bidi.setScrollOffset:
    +     <input name="pixelsLtr" type="text" />
    +     <a href="bidi_test.html#" onclick="goog.style.bidi.setScrollOffset(
    +          document.getElementById('scrollDivLtr'),
    +          parseInt(formLtr.elements['pixelsLtr'].value));">
    +      set
    +     </a>
    +    </form>
    +   </div>
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +  </div>
    +  <hr />
    +  <div>
    +   <span>
    +    RTL
    +   </span>
    +   <div dir="rtl" onscroll="updateInfo();" id="scrollDivRtl" class="scrollDiv">
    +    <div style="width:1000px; height:70px;background-color:blue">
    +    </div>
    +    <div id="scrolledElementRtl" style="background:yellow; top: 25px; right:85px;
    +             width: 100px; position:absolute">
    +     elm
    +    </div>
    +   </div>
    +   <div style="left:400px; position:absolute;">
    +    <div>
    +     elm.offsetParent.scrollLeft:
    +     <span id="elementScrollLeftRtl">
    +     </span>
    +    </div>
    +    <div>
    +     bidi.getScrollLeft(...):
    +     <span id="bidiScrollLeftRtl">
    +     </span>
    +    </div>
    +    <div>
    +     bidi.getOffsetStart(...):
    +     <span id="bidiOffsetStartRtl">
    +     </span>
    +    </div>
    +    <form name="formRtl" action="bidi_test.html#">
    +     goog.style.setScrollOffset:
    +     <input name="pixelsRtl" type="text" />
    +     <a href="bidi_test.html#" onclick="goog.style.bidi.setScrollOffset(
    +          document.getElementById('scrollDivRtl'),
    +          parseInt(formRtl.elements['pixelsRtl'].value));">
    +      set
    +     </a>
    +    </form>
    +   </div>
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +   <br />
    +  </div>
    +  <div dir="rtl" id="scrollLeftRtl" style="position: relative; width: 100px; height: 100px;
    +      background-color: blue">
    +   <div style="position:absolute; width: 200px; height: 20px; background-color:
    +    green">
    +    INNER
    +   </div>
    +  </div>
    +  <hr />
    +  <br />
    +  <br />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/bidi_test.js b/mapbox-gl/closure-library/closure/goog/style/bidi_test.js
    new file mode 100644
    index 0000000000..dd75fe8013
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/bidi_test.js
    @@ -0,0 +1,135 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.bidiTest');
    +goog.setTestOnly('goog.style.bidiTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +// Updates the calculated metrics.
    +function updateInfo() {
    +  var element = document.getElementById('scrolledElementRtl');
    +  document.getElementById('elementScrollLeftRtl').innerHTML =
    +      element.offsetParent.scrollLeft;
    +  document.getElementById('bidiOffsetStartRtl').innerHTML =
    +      goog.style.bidi.getOffsetStart(element);
    +  document.getElementById('bidiScrollLeftRtl').innerHTML =
    +      goog.style.bidi.getScrollLeft(element.offsetParent);
    +
    +  element = document.getElementById('scrolledElementLtr');
    +  document.getElementById('elementScrollLeftLtr').innerHTML =
    +      element.offsetParent.scrollLeft;
    +  document.getElementById('bidiOffsetStartLtr').innerHTML =
    +      goog.style.bidi.getOffsetStart(element);
    +  document.getElementById('bidiScrollLeftLtr').innerHTML =
    +      goog.style.bidi.getScrollLeft(element.offsetParent);
    +}
    +
    +function setUpPage() {
    +  updateInfo();
    +}
    +
    +function tearDown() {
    +  document.documentElement.dir = 'ltr';
    +  document.body.dir = 'ltr';
    +}
    +
    +function testGetOffsetStart() {
    +  var elm = document.getElementById('scrolledElementRtl');
    +  assertEquals(elm.style['right'], goog.style.bidi.getOffsetStart(elm) + 'px');
    +  elm = document.getElementById('scrolledElementLtr');
    +  assertEquals(elm.style['left'], goog.style.bidi.getOffsetStart(elm) + 'px');
    +}
    +
    +function testSetScrollOffsetRtl() {
    +  var scrollElm = document.getElementById('scrollDivRtl');
    +  var scrolledElm = document.getElementById('scrolledElementRtl');
    +  var originalDistance =
    +      goog.style.getRelativePosition(scrolledElm, document.body).x;
    +  var scrollAndAssert = function(pixels) {
    +    goog.style.bidi.setScrollOffset(scrollElm, pixels);
    +    assertEquals(originalDistance + pixels,
    +        goog.style.getRelativePosition(scrolledElm, document.body).x);
    +  };
    +  scrollAndAssert(0);
    +  scrollAndAssert(50);
    +  scrollAndAssert(100);
    +  scrollAndAssert(150);
    +  scrollAndAssert(155);
    +  scrollAndAssert(0);
    +}
    +
    +function testSetScrollOffsetLtr() {
    +  var scrollElm = document.getElementById('scrollDivLtr');
    +  var scrolledElm = document.getElementById('scrolledElementLtr');
    +  var originalDistance =
    +      goog.style.getRelativePosition(scrolledElm, document.body).x;
    +  var scrollAndAssert = function(pixels) {
    +    goog.style.bidi.setScrollOffset(scrollElm, pixels);
    +    assertEquals(originalDistance - pixels,
    +        goog.style.getRelativePosition(scrolledElm, document.body).x);
    +  };
    +  scrollAndAssert(0);
    +  scrollAndAssert(50);
    +  scrollAndAssert(100);
    +  scrollAndAssert(150);
    +  scrollAndAssert(155);
    +  scrollAndAssert(0);
    +}
    +
    +function testFixedBodyChildLtr() {
    +  var bodyChild = document.getElementById('bodyChild');
    +  assertEquals(goog.userAgent.GECKO ? document.body : null,
    +      bodyChild.offsetParent);
    +  assertEquals(60, goog.style.bidi.getOffsetStart(bodyChild));
    +}
    +
    +function testFixedBodyChildRtl() {
    +  document.documentElement.dir = 'rtl';
    +  document.body.dir = 'rtl';
    +
    +  var bodyChild = document.getElementById('bodyChild');
    +  assertEquals(goog.userAgent.GECKO ? document.body : null,
    +      bodyChild.offsetParent);
    +
    +  var expectedOffsetStart =
    +      goog.dom.getViewportSize().width - 60 - bodyChild.offsetWidth;
    +
    +  // Gecko seems to also add in the marginbox for the body.
    +  // It's not really clear to me if this is true in the general case,
    +  // or just under certain conditions.
    +  if (goog.userAgent.GECKO) {
    +    var marginBox = goog.style.getMarginBox(document.body);
    +    expectedOffsetStart -= (marginBox.left + marginBox.right);
    +  }
    +
    +  assertEquals(expectedOffsetStart,
    +      goog.style.bidi.getOffsetStart(bodyChild));
    +}
    +
    +function testGetScrollLeftRTL() {
    +  var scrollLeftDiv = document.getElementById('scrollLeftRtl');
    +  scrollLeftDiv.style.overflow = 'visible';
    +  assertEquals(0, goog.style.bidi.getScrollLeft(scrollLeftDiv));
    +  scrollLeftDiv.style.overflow = 'hidden';
    +  assertEquals(0, goog.style.bidi.getScrollLeft(scrollLeftDiv));
    +  scrollLeftDiv.style.overflow = 'scroll';
    +  assertEquals(0, goog.style.bidi.getScrollLeft(scrollLeftDiv));
    +  scrollLeftDiv.style.overflow = 'auto';
    +  assertEquals(0, goog.style.bidi.getScrollLeft(scrollLeftDiv));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/cursor.js b/mapbox-gl/closure-library/closure/goog/style/cursor.js
    new file mode 100644
    index 0000000000..7c54ef2d44
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/cursor.js
    @@ -0,0 +1,116 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions to create special cursor styles, like "draggable"
    + * (open hand) or "dragging" (closed hand).
    + *
    + * @author dgajda@google.com (Damian Gajda) Ported to closure.
    + */
    +
    +goog.provide('goog.style.cursor');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * The file name for the open-hand (draggable) cursor.
    + * @type {string}
    + */
    +goog.style.cursor.OPENHAND_FILE = 'openhand.cur';
    +
    +
    +/**
    + * The file name for the close-hand (dragging) cursor.
    + * @type {string}
    + */
    +goog.style.cursor.CLOSEDHAND_FILE = 'closedhand.cur';
    +
    +
    +/**
    + * Create the style for the draggable cursor based on browser and OS.
    + * The value can be extended to be '!important' if needed.
    + *
    + * @param {string} absoluteDotCurFilePath The absolute base path of
    + *     'openhand.cur' file to be used if the browser supports it.
    + * @param {boolean=} opt_obsolete Just for compiler backward compatibility.
    + * @return {string} The "draggable" mouse cursor style value.
    + */
    +goog.style.cursor.getDraggableCursorStyle = function(
    +    absoluteDotCurFilePath, opt_obsolete) {
    +  return goog.style.cursor.getCursorStyle_(
    +      '-moz-grab',
    +      absoluteDotCurFilePath + goog.style.cursor.OPENHAND_FILE,
    +      'default');
    +};
    +
    +
    +/**
    + * Create the style for the dragging cursor based on browser and OS.
    + * The value can be extended to be '!important' if needed.
    + *
    + * @param {string} absoluteDotCurFilePath The absolute base path of
    + *     'closedhand.cur' file to be used if the browser supports it.
    + * @param {boolean=} opt_obsolete Just for compiler backward compatibility.
    + * @return {string} The "dragging" mouse cursor style value.
    + */
    +goog.style.cursor.getDraggingCursorStyle = function(
    +    absoluteDotCurFilePath, opt_obsolete) {
    +  return goog.style.cursor.getCursorStyle_(
    +      '-moz-grabbing',
    +      absoluteDotCurFilePath + goog.style.cursor.CLOSEDHAND_FILE,
    +      'move');
    +};
    +
    +
    +/**
    + * Create the style for the cursor based on browser and OS.
    + *
    + * @param {string} geckoNonWinBuiltInStyleValue The Gecko on non-Windows OS,
    + *     built in cursor style.
    + * @param {string} absoluteDotCurFilePath The .cur file absolute file to be
    + *     used if the browser supports it.
    + * @param {string} defaultStyle The default fallback cursor style.
    + * @return {string} The computed mouse cursor style value.
    + * @private
    + */
    +goog.style.cursor.getCursorStyle_ = function(geckoNonWinBuiltInStyleValue,
    +    absoluteDotCurFilePath, defaultStyle) {
    +  // Use built in cursors for Gecko on non Windows OS.
    +  // We prefer our custom cursor, but Firefox Mac and Firefox Linux
    +  // cannot do custom cursors. They do have a built-in hand, so use it:
    +  if (goog.userAgent.GECKO && !goog.userAgent.WINDOWS) {
    +    return geckoNonWinBuiltInStyleValue;
    +  }
    +
    +  // Use the custom cursor file.
    +  var cursorStyleValue = 'url("' + absoluteDotCurFilePath + '")';
    +  // Change hot-spot for Safari.
    +  if (goog.userAgent.WEBKIT) {
    +    // Safari seems to ignore the hotspot specified in the .cur file (it uses
    +    // 0,0 instead).  This causes the cursor to jump as it transitions between
    +    // openhand and pointer which is especially annoying when trying to hover
    +    // over the route for draggable routes.  We specify the hotspot here as 7,5
    +    // in the css - unfortunately ie6 can't understand this and falls back to
    +    // the builtin cursors so we just do this for safari (but ie DOES correctly
    +    // use the hotspot specified in the file so this is ok).  The appropriate
    +    // coordinates were determined by looking at a hex dump and the format
    +    // description from wikipedia.
    +    cursorStyleValue += ' 7 5';
    +  }
    +  // Add default cursor fallback.
    +  cursorStyleValue += ', ' + defaultStyle;
    +  return cursorStyleValue;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/style/cursor_test.html b/mapbox-gl/closure-library/closure/goog/style/cursor_test.html
    new file mode 100644
    index 0000000000..6c65a8518a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/cursor_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: dgajda@google.com (Damian Gajda)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.style.cursor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.style.cursorTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/cursor_test.js b/mapbox-gl/closure-library/closure/goog/style/cursor_test.js
    new file mode 100644
    index 0000000000..dc0a7e476e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/cursor_test.js
    @@ -0,0 +1,125 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.cursorTest');
    +goog.setTestOnly('goog.style.cursorTest');
    +
    +goog.require('goog.style.cursor');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var baseCursorUrl = '/images/2/';
    +var origWindowsUserAgentValue;
    +var origGeckoUserAgentValue;
    +var origWebkitUserAgentValue;
    +
    +
    +function setUp() {
    +  origWindowsUserAgentValue = goog.userAgent.WINDOWS;
    +  origGeckoUserAgentValue = goog.userAgent.GECKO;
    +  origWebkitUserAgentValue = goog.userAgent.WEBKIT;
    +}
    +
    +
    +function tearDown() {
    +  goog.userAgent.WINDOWS = origWindowsUserAgentValue;
    +  goog.userAgent.GECKO = origGeckoUserAgentValue;
    +  goog.userAgent.WEBKIT = origWebkitUserAgentValue;
    +}
    +
    +
    +function testGetCursorStylesWebkit() {
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.WEBKIT = true;
    +
    +  assertEquals('Webkit should get a cursor style with moved hot-spot.',
    +      'url("/images/2/openhand.cur") 7 5, default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl));
    +  assertEquals('Webkit should get a cursor style with moved hot-spot.',
    +      'url("/images/2/openhand.cur") 7 5, default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl, true));
    +
    +  assertEquals('Webkit should get a cursor style with moved hot-spot.',
    +      'url("/images/2/closedhand.cur") 7 5, move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl));
    +  assertEquals('Webkit should get a cursor style with moved hot-spot.',
    +      'url("/images/2/closedhand.cur") 7 5, move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl, true));
    +}
    +
    +
    +function testGetCursorStylesFireFoxNonWin() {
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.WINDOWS = false;
    +
    +  assertEquals('FireFox on non Windows should get a custom cursor style.',
    +      '-moz-grab',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl));
    +  assertEquals('FireFox on non Windows should get a custom cursor style and ' +
    +      'no !important modifier.',
    +      '-moz-grab',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl, true));
    +
    +  assertEquals('FireFox on non Windows should get a custom cursor style.',
    +      '-moz-grabbing',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl));
    +  assertEquals('FireFox on non Windows should get a custom cursor style and ' +
    +          'no !important modifier.',
    +      '-moz-grabbing',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl, true));
    +}
    +
    +
    +function testGetCursorStylesFireFoxWin() {
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.WEBKIT = false;
    +  goog.userAgent.WINDOWS = true;
    +
    +  assertEquals('FireFox should get a cursor style with URL.',
    +      'url("/images/2/openhand.cur"), default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl));
    +  assertEquals('FireFox should get a cursor style with URL and no !important' +
    +          ' modifier.',
    +      'url("/images/2/openhand.cur"), default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl, true));
    +
    +  assertEquals('FireFox should get a cursor style with URL.',
    +      'url("/images/2/closedhand.cur"), move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl));
    +  assertEquals('FireFox should get a cursor style with URL and no !important' +
    +          ' modifier.',
    +      'url("/images/2/closedhand.cur"), move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl, true));
    +}
    +
    +
    +function testGetCursorStylesOther() {
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.WEBKIT = false;
    +
    +  assertEquals('Other browsers (IE) should get a cursor style with URL.',
    +      'url("/images/2/openhand.cur"), default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl));
    +  assertEquals('Other browsers (IE) should get a cursor style with URL.',
    +      'url("/images/2/openhand.cur"), default',
    +      goog.style.cursor.getDraggableCursorStyle(baseCursorUrl, true));
    +
    +  assertEquals('Other browsers (IE) should get a cursor style with URL.',
    +      'url("/images/2/closedhand.cur"), move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl));
    +  assertEquals('Other browsers (IE) should get a cursor style with URL.',
    +      'url("/images/2/closedhand.cur"), move',
    +      goog.style.cursor.getDraggingCursorStyle(baseCursorUrl, true));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style.js b/mapbox-gl/closure-library/closure/goog/style/style.js
    new file mode 100644
    index 0000000000..32f2e953a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style.js
    @@ -0,0 +1,2027 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for element styles.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/inline_block_quirks.html
    + * @see ../demos/inline_block_standards.html
    + * @see ../demos/style_viewport.html
    + */
    +
    +goog.provide('goog.style');
    +
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.vendor');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Rect');
    +goog.require('goog.math.Size');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Sets a style value on an element.
    + *
    + * This function is not indended to patch issues in the browser's style
    + * handling, but to allow easy programmatic access to setting dash-separated
    + * style properties.  An example is setting a batch of properties from a data
    + * object without overwriting old styles.  When possible, use native APIs:
    + * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
    + * elem.style.cssText = 'property1: value1; property2: value2'.
    + *
    + * @param {Element} element The element to change.
    + * @param {string|Object} style If a string, a style name. If an object, a hash
    + *     of style names to style values.
    + * @param {string|number|boolean=} opt_value If style was a string, then this
    + *     should be the value.
    + */
    +goog.style.setStyle = function(element, style, opt_value) {
    +  if (goog.isString(style)) {
    +    goog.style.setStyle_(element, opt_value, style);
    +  } else {
    +    for (var key in style) {
    +      goog.style.setStyle_(element, style[key], key);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets a style value on an element, with parameters swapped to work with
    + * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when
    + * necessary.
    + * @param {Element} element The element to change.
    + * @param {string|number|boolean|undefined} value Style value.
    + * @param {string} style Style name.
    + * @private
    + */
    +goog.style.setStyle_ = function(element, value, style) {
    +  var propertyName = goog.style.getVendorJsStyleName_(element, style);
    +
    +  if (propertyName) {
    +    element.style[propertyName] = value;
    +  }
    +};
    +
    +
    +/**
    + * Style name cache that stores previous property name lookups.
    + *
    + * This is used by setStyle to speed up property lookups, entries look like:
    + *   { StyleName: ActualPropertyName }
    + *
    + * @private {!Object<string, string>}
    + */
    +goog.style.styleNameCache_ = {};
    +
    +
    +/**
    + * Returns the style property name in camel-case. If it does not exist and a
    + * vendor-specific version of the property does exist, then return the vendor-
    + * specific property name instead.
    + * @param {Element} element The element to change.
    + * @param {string} style Style name.
    + * @return {string} Vendor-specific style.
    + * @private
    + */
    +goog.style.getVendorJsStyleName_ = function(element, style) {
    +  var propertyName = goog.style.styleNameCache_[style];
    +  if (!propertyName) {
    +    var camelStyle = goog.string.toCamelCase(style);
    +    propertyName = camelStyle;
    +
    +    if (element.style[camelStyle] === undefined) {
    +      var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    +          goog.string.toTitleCase(camelStyle);
    +
    +      if (element.style[prefixedStyle] !== undefined) {
    +        propertyName = prefixedStyle;
    +      }
    +    }
    +    goog.style.styleNameCache_[style] = propertyName;
    +  }
    +
    +  return propertyName;
    +};
    +
    +
    +/**
    + * Returns the style property name in CSS notation. If it does not exist and a
    + * vendor-specific version of the property does exist, then return the vendor-
    + * specific property name instead.
    + * @param {Element} element The element to change.
    + * @param {string} style Style name.
    + * @return {string} Vendor-specific style.
    + * @private
    + */
    +goog.style.getVendorStyleName_ = function(element, style) {
    +  var camelStyle = goog.string.toCamelCase(style);
    +
    +  if (element.style[camelStyle] === undefined) {
    +    var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
    +        goog.string.toTitleCase(camelStyle);
    +
    +    if (element.style[prefixedStyle] !== undefined) {
    +      return goog.dom.vendor.getVendorPrefix() + '-' + style;
    +    }
    +  }
    +
    +  return style;
    +};
    +
    +
    +/**
    + * Retrieves an explicitly-set style value of a node. This returns '' if there
    + * isn't a style attribute on the element or if this style property has not been
    + * explicitly set in script.
    + *
    + * @param {Element} element Element to get style of.
    + * @param {string} property Property to get, css-style (if you have a camel-case
    + * property, use element.style[style]).
    + * @return {string} Style value.
    + */
    +goog.style.getStyle = function(element, property) {
    +  // element.style is '' for well-known properties which are unset.
    +  // For for browser specific styles as 'filter' is undefined
    +  // so we need to return '' explicitly to make it consistent across
    +  // browsers.
    +  var styleValue = element.style[goog.string.toCamelCase(property)];
    +
    +  // Using typeof here because of a bug in Safari 5.1, where this value
    +  // was undefined, but === undefined returned false.
    +  if (typeof(styleValue) !== 'undefined') {
    +    return styleValue;
    +  }
    +
    +  return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
    +      '';
    +};
    +
    +
    +/**
    + * Retrieves a computed style value of a node. It returns empty string if the
    + * value cannot be computed (which will be the case in Internet Explorer) or
    + * "none" if the property requested is an SVG one and it has not been
    + * explicitly set (firefox and webkit).
    + *
    + * @param {Element} element Element to get style of.
    + * @param {string} property Property to get (camel-case).
    + * @return {string} Style value.
    + */
    +goog.style.getComputedStyle = function(element, property) {
    +  var doc = goog.dom.getOwnerDocument(element);
    +  if (doc.defaultView && doc.defaultView.getComputedStyle) {
    +    var styles = doc.defaultView.getComputedStyle(element, null);
    +    if (styles) {
    +      // element.style[..] is undefined for browser specific styles
    +      // as 'filter'.
    +      return styles[property] || styles.getPropertyValue(property) || '';
    +    }
    +  }
    +
    +  return '';
    +};
    +
    +
    +/**
    + * Gets the cascaded style value of a node, or null if the value cannot be
    + * computed (only Internet Explorer can do this).
    + *
    + * @param {Element} element Element to get style of.
    + * @param {string} style Property to get (camel-case).
    + * @return {string} Style value.
    + */
    +goog.style.getCascadedStyle = function(element, style) {
    +  // TODO(nicksantos): This should be documented to return null. #fixTypes
    +  return element.currentStyle ? element.currentStyle[style] : null;
    +};
    +
    +
    +/**
    + * Cross-browser pseudo get computed style. It returns the computed style where
    + * available. If not available it tries the cascaded style value (IE
    + * currentStyle) and in worst case the inline style value.  It shouldn't be
    + * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
    + * discussion.
    + *
    + * @param {Element} element Element to get style of.
    + * @param {string} style Property to get (must be camelCase, not css-style.).
    + * @return {string} Style value.
    + * @private
    + */
    +goog.style.getStyle_ = function(element, style) {
    +  return goog.style.getComputedStyle(element, style) ||
    +         goog.style.getCascadedStyle(element, style) ||
    +         (element.style && element.style[style]);
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the box-sizing CSS attribute.
    + * Browser support: http://caniuse.com/css3-boxsizing.
    + * @param {!Element} element The element whose box-sizing to get.
    + * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
    + *     box-sizing is not supported (IE7 and below).
    + */
    +goog.style.getComputedBoxSizing = function(element) {
    +  return goog.style.getStyle_(element, 'boxSizing') ||
    +      goog.style.getStyle_(element, 'MozBoxSizing') ||
    +      goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the position CSS attribute.
    + * @param {Element} element The element to get the position of.
    + * @return {string} Position value.
    + */
    +goog.style.getComputedPosition = function(element) {
    +  return goog.style.getStyle_(element, 'position');
    +};
    +
    +
    +/**
    + * Retrieves the computed background color string for a given element. The
    + * string returned is suitable for assigning to another element's
    + * background-color, but is not guaranteed to be in any particular string
    + * format. Accessing the color in a numeric form may not be possible in all
    + * browsers or with all input.
    + *
    + * If the background color for the element is defined as a hexadecimal value,
    + * the resulting string can be parsed by goog.color.parse in all supported
    + * browsers.
    + *
    + * Whether named colors like "red" or "lightblue" get translated into a
    + * format which can be parsed is browser dependent. Calling this function on
    + * transparent elements will return "transparent" in most browsers or
    + * "rgba(0, 0, 0, 0)" in WebKit.
    + * @param {Element} element The element to get the background color of.
    + * @return {string} The computed string value of the background color.
    + */
    +goog.style.getBackgroundColor = function(element) {
    +  return goog.style.getStyle_(element, 'backgroundColor');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the overflow-x CSS attribute.
    + * @param {Element} element The element to get the overflow-x of.
    + * @return {string} The computed string value of the overflow-x attribute.
    + */
    +goog.style.getComputedOverflowX = function(element) {
    +  return goog.style.getStyle_(element, 'overflowX');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the overflow-y CSS attribute.
    + * @param {Element} element The element to get the overflow-y of.
    + * @return {string} The computed string value of the overflow-y attribute.
    + */
    +goog.style.getComputedOverflowY = function(element) {
    +  return goog.style.getStyle_(element, 'overflowY');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the z-index CSS attribute.
    + * @param {Element} element The element to get the z-index of.
    + * @return {string|number} The computed value of the z-index attribute.
    + */
    +goog.style.getComputedZIndex = function(element) {
    +  return goog.style.getStyle_(element, 'zIndex');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the text-align CSS attribute.
    + * @param {Element} element The element to get the text-align of.
    + * @return {string} The computed string value of the text-align attribute.
    + */
    +goog.style.getComputedTextAlign = function(element) {
    +  return goog.style.getStyle_(element, 'textAlign');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the cursor CSS attribute.
    + * @param {Element} element The element to get the cursor of.
    + * @return {string} The computed string value of the cursor attribute.
    + */
    +goog.style.getComputedCursor = function(element) {
    +  return goog.style.getStyle_(element, 'cursor');
    +};
    +
    +
    +/**
    + * Retrieves the computed value of the CSS transform attribute.
    + * @param {Element} element The element to get the transform of.
    + * @return {string} The computed string representation of the transform matrix.
    + */
    +goog.style.getComputedTransform = function(element) {
    +  var property = goog.style.getVendorStyleName_(element, 'transform');
    +  return goog.style.getStyle_(element, property) ||
    +      goog.style.getStyle_(element, 'transform');
    +};
    +
    +
    +/**
    + * Sets the top/left values of an element.  If no unit is specified in the
    + * argument then it will add px. The second argument is required if the first
    + * argument is a string or number and is ignored if the first argument
    + * is a coordinate.
    + * @param {Element} el Element to move.
    + * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {string|number=} opt_arg2 Top position.
    + */
    +goog.style.setPosition = function(el, arg1, opt_arg2) {
    +  var x, y;
    +
    +  if (arg1 instanceof goog.math.Coordinate) {
    +    x = arg1.x;
    +    y = arg1.y;
    +  } else {
    +    x = arg1;
    +    y = opt_arg2;
    +  }
    +
    +  el.style.left = goog.style.getPixelStyleValue_(
    +      /** @type {number|string} */ (x), false);
    +  el.style.top = goog.style.getPixelStyleValue_(
    +      /** @type {number|string} */ (y), false);
    +};
    +
    +
    +/**
    + * Gets the offsetLeft and offsetTop properties of an element and returns them
    + * in a Coordinate object
    + * @param {Element} element Element.
    + * @return {!goog.math.Coordinate} The position.
    + */
    +goog.style.getPosition = function(element) {
    +  return new goog.math.Coordinate(element.offsetLeft, element.offsetTop);
    +};
    +
    +
    +/**
    + * Returns the viewport element for a particular document
    + * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
    + *     of.
    + * @return {Element} document.documentElement or document.body.
    + */
    +goog.style.getClientViewportElement = function(opt_node) {
    +  var doc;
    +  if (opt_node) {
    +    doc = goog.dom.getOwnerDocument(opt_node);
    +  } else {
    +    doc = goog.dom.getDocument();
    +  }
    +
    +  // In old IE versions the document.body represented the viewport
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    +      !goog.dom.getDomHelper(doc).isCss1CompatMode()) {
    +    return doc.body;
    +  }
    +  return doc.documentElement;
    +};
    +
    +
    +/**
    + * Calculates the viewport coordinates relative to the page/document
    + * containing the node. The viewport may be the browser viewport for
    + * non-iframe document, or the iframe container for iframe'd document.
    + * @param {!Document} doc The document to use as the reference point.
    + * @return {!goog.math.Coordinate} The page offset of the viewport.
    + */
    +goog.style.getViewportPageOffset = function(doc) {
    +  var body = doc.body;
    +  var documentElement = doc.documentElement;
    +  var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
    +  var scrollTop = body.scrollTop || documentElement.scrollTop;
    +  return new goog.math.Coordinate(scrollLeft, scrollTop);
    +};
    +
    +
    +/**
    + * Gets the client rectangle of the DOM element.
    + *
    + * getBoundingClientRect is part of a new CSS object model draft (with a
    + * long-time presence in IE), replacing the error-prone parent offset
    + * computation and the now-deprecated Gecko getBoxObjectFor.
    + *
    + * This utility patches common browser bugs in getBoundingClientRect. It
    + * will fail if getBoundingClientRect is unsupported.
    + *
    + * If the element is not in the DOM, the result is undefined, and an error may
    + * be thrown depending on user agent.
    + *
    + * @param {!Element} el The element whose bounding rectangle is being queried.
    + * @return {Object} A native bounding rectangle with numerical left, top,
    + *     right, and bottom.  Reported by Firefox to be of object type ClientRect.
    + * @private
    + */
    +goog.style.getBoundingClientRect_ = function(el) {
    +  var rect;
    +  try {
    +    rect = el.getBoundingClientRect();
    +  } catch (e) {
    +    // In IE < 9, calling getBoundingClientRect on an orphan element raises an
    +    // "Unspecified Error". All other browsers return zeros.
    +    return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
    +  }
    +
    +  // Patch the result in IE only, so that this function can be inlined if
    +  // compiled for non-IE.
    +  if (goog.userAgent.IE && el.ownerDocument.body) {
    +
    +    // In IE, most of the time, 2 extra pixels are added to the top and left
    +    // due to the implicit 2-pixel inset border.  In IE6/7 quirks mode and
    +    // IE6 standards mode, this border can be overridden by setting the
    +    // document element's border to zero -- thus, we cannot rely on the
    +    // offset always being 2 pixels.
    +
    +    // In quirks mode, the offset can be determined by querying the body's
    +    // clientLeft/clientTop, but in standards mode, it is found by querying
    +    // the document element's clientLeft/clientTop.  Since we already called
    +    // getBoundingClientRect we have already forced a reflow, so it is not
    +    // too expensive just to query them all.
    +
    +    // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx
    +    var doc = el.ownerDocument;
    +    rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
    +    rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
    +  }
    +  return /** @type {Object} */ (rect);
    +};
    +
    +
    +/**
    + * Returns the first parent that could affect the position of a given element.
    + * @param {Element} element The element to get the offset parent for.
    + * @return {Element} The first offset parent or null if one cannot be found.
    + */
    +goog.style.getOffsetParent = function(element) {
    +  // element.offsetParent does the right thing in IE7 and below.  In other
    +  // browsers it only includes elements with position absolute, relative or
    +  // fixed, not elements with overflow set to auto or scroll.
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
    +    return element.offsetParent;
    +  }
    +
    +  var doc = goog.dom.getOwnerDocument(element);
    +  var positionStyle = goog.style.getStyle_(element, 'position');
    +  var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
    +  for (var parent = element.parentNode; parent && parent != doc;
    +       parent = parent.parentNode) {
    +    positionStyle =
    +        goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
    +    skipStatic = skipStatic && positionStyle == 'static' &&
    +                 parent != doc.documentElement && parent != doc.body;
    +    if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||
    +                        parent.scrollHeight > parent.clientHeight ||
    +                        positionStyle == 'fixed' ||
    +                        positionStyle == 'absolute' ||
    +                        positionStyle == 'relative')) {
    +      return /** @type {!Element} */ (parent);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Calculates and returns the visible rectangle for a given element. Returns a
    + * box describing the visible portion of the nearest scrollable offset ancestor.
    + * Coordinates are given relative to the document.
    + *
    + * @param {Element} element Element to get the visible rect for.
    + * @return {goog.math.Box} Bounding elementBox describing the visible rect or
    + *     null if scrollable ancestor isn't inside the visible viewport.
    + */
    +goog.style.getVisibleRectForElement = function(element) {
    +  var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
    +  var dom = goog.dom.getDomHelper(element);
    +  var body = dom.getDocument().body;
    +  var documentElement = dom.getDocument().documentElement;
    +  var scrollEl = dom.getDocumentScrollElement();
    +
    +  // Determine the size of the visible rect by climbing the dom accounting for
    +  // all scrollable containers.
    +  for (var el = element; el = goog.style.getOffsetParent(el); ) {
    +    // clientWidth is zero for inline block elements in IE.
    +    // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
    +    if ((!goog.userAgent.IE || el.clientWidth != 0) &&
    +        (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
    +        // body may have overflow set on it, yet we still get the entire
    +        // viewport. In some browsers, el.offsetParent may be
    +        // document.documentElement, so check for that too.
    +        (el != body && el != documentElement &&
    +            goog.style.getStyle_(el, 'overflow') != 'visible')) {
    +      var pos = goog.style.getPageOffset(el);
    +      var client = goog.style.getClientLeftTop(el);
    +      pos.x += client.x;
    +      pos.y += client.y;
    +
    +      visibleRect.top = Math.max(visibleRect.top, pos.y);
    +      visibleRect.right = Math.min(visibleRect.right,
    +                                   pos.x + el.clientWidth);
    +      visibleRect.bottom = Math.min(visibleRect.bottom,
    +                                    pos.y + el.clientHeight);
    +      visibleRect.left = Math.max(visibleRect.left, pos.x);
    +    }
    +  }
    +
    +  // Clip by window's viewport.
    +  var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
    +  visibleRect.left = Math.max(visibleRect.left, scrollX);
    +  visibleRect.top = Math.max(visibleRect.top, scrollY);
    +  var winSize = dom.getViewportSize();
    +  visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
    +  visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
    +  return visibleRect.top >= 0 && visibleRect.left >= 0 &&
    +         visibleRect.bottom > visibleRect.top &&
    +         visibleRect.right > visibleRect.left ?
    +         visibleRect : null;
    +};
    +
    +
    +/**
    + * Calculate the scroll position of {@code container} with the minimum amount so
    + * that the content and the borders of the given {@code element} become visible.
    + * If the element is bigger than the container, its top left corner will be
    + * aligned as close to the container's top left corner as possible.
    + *
    + * @param {Element} element The element to make visible.
    + * @param {Element} container The container to scroll.
    + * @param {boolean=} opt_center Whether to center the element in the container.
    + *     Defaults to false.
    + * @return {!goog.math.Coordinate} The new scroll position of the container,
    + *     in form of goog.math.Coordinate(scrollLeft, scrollTop).
    + */
    +goog.style.getContainerOffsetToScrollInto =
    +    function(element, container, opt_center) {
    +  // Absolute position of the element's border's top left corner.
    +  var elementPos = goog.style.getPageOffset(element);
    +  // Absolute position of the container's border's top left corner.
    +  var containerPos = goog.style.getPageOffset(container);
    +  var containerBorder = goog.style.getBorderBox(container);
    +  // Relative pos. of the element's border box to the container's content box.
    +  var relX = elementPos.x - containerPos.x - containerBorder.left;
    +  var relY = elementPos.y - containerPos.y - containerBorder.top;
    +  // How much the element can move in the container, i.e. the difference between
    +  // the element's bottom-right-most and top-left-most position where it's
    +  // fully visible.
    +  var spaceX = container.clientWidth - element.offsetWidth;
    +  var spaceY = container.clientHeight - element.offsetHeight;
    +
    +  var scrollLeft = container.scrollLeft;
    +  var scrollTop = container.scrollTop;
    +  if (container == goog.dom.getDocument().body ||
    +      container == goog.dom.getDocument().documentElement) {
    +    // If the container is the document scroll element (usually <body>),
    +    // getPageOffset(element) is already relative to it and there is no need to
    +    // consider the current scroll.
    +    scrollLeft = containerPos.x + containerBorder.left;
    +    scrollTop = containerPos.y + containerBorder.top;
    +
    +    if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +      // In older versions of IE getPageOffset(element) does not include the
    +      // continaer border so it has to be added to accomodate.
    +      scrollLeft += containerBorder.left;
    +      scrollTop += containerBorder.top;
    +    }
    +  }
    +  if (opt_center) {
    +    // All browsers round non-integer scroll positions down.
    +    scrollLeft += relX - spaceX / 2;
    +    scrollTop += relY - spaceY / 2;
    +  } else {
    +    // This formula was designed to give the correct scroll values in the
    +    // following cases:
    +    // - element is higher than container (spaceY < 0) => scroll down by relY
    +    // - element is not higher that container (spaceY >= 0):
    +    //   - it is above container (relY < 0) => scroll up by abs(relY)
    +    //   - it is below container (relY > spaceY) => scroll down by relY - spaceY
    +    //   - it is in the container => don't scroll
    +    scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
    +    scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
    +  }
    +  return new goog.math.Coordinate(scrollLeft, scrollTop);
    +};
    +
    +
    +/**
    + * Changes the scroll position of {@code container} with the minimum amount so
    + * that the content and the borders of the given {@code element} become visible.
    + * If the element is bigger than the container, its top left corner will be
    + * aligned as close to the container's top left corner as possible.
    + *
    + * @param {Element} element The element to make visible.
    + * @param {Element} container The container to scroll.
    + * @param {boolean=} opt_center Whether to center the element in the container.
    + *     Defaults to false.
    + */
    +goog.style.scrollIntoContainerView = function(element, container, opt_center) {
    +  var offset =
    +      goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
    +  container.scrollLeft = offset.x;
    +  container.scrollTop = offset.y;
    +};
    +
    +
    +/**
    + * Returns clientLeft (width of the left border and, if the directionality is
    + * right to left, the vertical scrollbar) and clientTop as a coordinate object.
    + *
    + * @param {Element} el Element to get clientLeft for.
    + * @return {!goog.math.Coordinate} Client left and top.
    + */
    +goog.style.getClientLeftTop = function(el) {
    +  return new goog.math.Coordinate(el.clientLeft, el.clientTop);
    +};
    +
    +
    +/**
    + * Returns a Coordinate object relative to the top-left of the HTML document.
    + * Implemented as a single function to save having to do two recursive loops in
    + * opera and safari just to get both coordinates.  If you just want one value do
    + * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
    + * note if you call both those methods the tree will be analysed twice.
    + *
    + * @param {Element} el Element to get the page offset for.
    + * @return {!goog.math.Coordinate} The page offset.
    + */
    +goog.style.getPageOffset = function(el) {
    +  var doc = goog.dom.getOwnerDocument(el);
    +  var positionStyle = goog.style.getStyle_(el, 'position');
    +  // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
    +  goog.asserts.assertObject(el, 'Parameter is required');
    +
    +  // NOTE(arv): If element is hidden (display none or disconnected or any the
    +  // ancestors are hidden) we get (0,0) by default but we still do the
    +  // accumulation of scroll position.
    +
    +  // TODO(arv): Should we check if the node is disconnected and in that case
    +  //            return (0,0)?
    +
    +  var pos = new goog.math.Coordinate(0, 0);
    +  var viewportElement = goog.style.getClientViewportElement(doc);
    +  if (el == viewportElement) {
    +    // viewport is always at 0,0 as that defined the coordinate system for this
    +    // function - this avoids special case checks in the code below
    +    return pos;
    +  }
    +
    +  var box = goog.style.getBoundingClientRect_(el);
    +  // Must add the scroll coordinates in to get the absolute page offset
    +  // of element since getBoundingClientRect returns relative coordinates to
    +  // the viewport.
    +  var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
    +  pos.x = box.left + scrollCoord.x;
    +  pos.y = box.top + scrollCoord.y;
    +
    +  return pos;
    +};
    +
    +
    +/**
    + * Returns the left coordinate of an element relative to the HTML document
    + * @param {Element} el Elements.
    + * @return {number} The left coordinate.
    + */
    +goog.style.getPageOffsetLeft = function(el) {
    +  return goog.style.getPageOffset(el).x;
    +};
    +
    +
    +/**
    + * Returns the top coordinate of an element relative to the HTML document
    + * @param {Element} el Elements.
    + * @return {number} The top coordinate.
    + */
    +goog.style.getPageOffsetTop = function(el) {
    +  return goog.style.getPageOffset(el).y;
    +};
    +
    +
    +/**
    + * Returns a Coordinate object relative to the top-left of an HTML document
    + * in an ancestor frame of this element. Used for measuring the position of
    + * an element inside a frame relative to a containing frame.
    + *
    + * @param {Element} el Element to get the page offset for.
    + * @param {Window} relativeWin The window to measure relative to. If relativeWin
    + *     is not in the ancestor frame chain of the element, we measure relative to
    + *     the top-most window.
    + * @return {!goog.math.Coordinate} The page offset.
    + */
    +goog.style.getFramedPageOffset = function(el, relativeWin) {
    +  var position = new goog.math.Coordinate(0, 0);
    +
    +  // Iterate up the ancestor frame chain, keeping track of the current window
    +  // and the current element in that window.
    +  var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
    +  var currentEl = el;
    +  do {
    +    // if we're at the top window, we want to get the page offset.
    +    // if we're at an inner frame, we only want to get the window position
    +    // so that we can determine the actual page offset in the context of
    +    // the outer window.
    +    var offset = currentWin == relativeWin ?
    +        goog.style.getPageOffset(currentEl) :
    +        goog.style.getClientPositionForElement_(
    +            goog.asserts.assert(currentEl));
    +
    +    position.x += offset.x;
    +    position.y += offset.y;
    +  } while (currentWin && currentWin != relativeWin &&
    +      currentWin != currentWin.parent &&
    +      (currentEl = currentWin.frameElement) &&
    +      (currentWin = currentWin.parent));
    +
    +  return position;
    +};
    +
    +
    +/**
    + * Translates the specified rect relative to origBase page, for newBase page.
    + * If origBase and newBase are the same, this function does nothing.
    + *
    + * @param {goog.math.Rect} rect The source rectangle relative to origBase page,
    + *     and it will have the translated result.
    + * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
    + * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
    + *     coordinate.  This must be a DOM for an ancestor frame of origBase
    + *     or the same as origBase.
    + */
    +goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
    +  if (origBase.getDocument() != newBase.getDocument()) {
    +    var body = origBase.getDocument().body;
    +    var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
    +
    +    // Adjust Body's margin.
    +    pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
    +
    +    if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
    +        !origBase.isCss1CompatMode()) {
    +      pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
    +    }
    +
    +    rect.left += pos.x;
    +    rect.top += pos.y;
    +  }
    +};
    +
    +
    +/**
    + * Returns the position of an element relative to another element in the
    + * document.  A relative to B
    + * @param {Element|Event|goog.events.Event} a Element or mouse event whose
    + *     position we're calculating.
    + * @param {Element|Event|goog.events.Event} b Element or mouse event position
    + *     is relative to.
    + * @return {!goog.math.Coordinate} The relative position.
    + */
    +goog.style.getRelativePosition = function(a, b) {
    +  var ap = goog.style.getClientPosition(a);
    +  var bp = goog.style.getClientPosition(b);
    +  return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
    +};
    +
    +
    +/**
    + * Returns the position of the event or the element's border box relative to
    + * the client viewport.
    + * @param {!Element} el Element whose position to get.
    + * @return {!goog.math.Coordinate} The position.
    + * @private
    + */
    +goog.style.getClientPositionForElement_ = function(el) {
    +  var box = goog.style.getBoundingClientRect_(el);
    +  return new goog.math.Coordinate(box.left, box.top);
    +};
    +
    +
    +/**
    + * Returns the position of the event or the element's border box relative to
    + * the client viewport.
    + * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
    + * @return {!goog.math.Coordinate} The position.
    + */
    +goog.style.getClientPosition = function(el) {
    +  goog.asserts.assert(el);
    +  if (el.nodeType == goog.dom.NodeType.ELEMENT) {
    +    return goog.style.getClientPositionForElement_(
    +        /** @type {!Element} */ (el));
    +  } else {
    +    var isAbstractedEvent = goog.isFunction(el.getBrowserEvent);
    +    var be = /** @type {!goog.events.BrowserEvent} */ (el);
    +    var targetEvent = el;
    +
    +    if (el.targetTouches && el.targetTouches.length) {
    +      targetEvent = el.targetTouches[0];
    +    } else if (isAbstractedEvent && be.getBrowserEvent().targetTouches &&
    +        be.getBrowserEvent().targetTouches.length) {
    +      targetEvent = be.getBrowserEvent().targetTouches[0];
    +    }
    +
    +    return new goog.math.Coordinate(
    +        targetEvent.clientX,
    +        targetEvent.clientY);
    +  }
    +};
    +
    +
    +/**
    + * Moves an element to the given coordinates relative to the client viewport.
    + * @param {Element} el Absolutely positioned element to set page offset for.
    + *     It must be in the document.
    + * @param {number|goog.math.Coordinate} x Left position of the element's margin
    + *     box or a coordinate object.
    + * @param {number=} opt_y Top position of the element's margin box.
    + */
    +goog.style.setPageOffset = function(el, x, opt_y) {
    +  // Get current pageoffset
    +  var cur = goog.style.getPageOffset(el);
    +
    +  if (x instanceof goog.math.Coordinate) {
    +    opt_y = x.y;
    +    x = x.x;
    +  }
    +
    +  // NOTE(arv): We cannot allow strings for x and y. We could but that would
    +  // require us to manually transform between different units
    +
    +  // Work out deltas
    +  var dx = x - cur.x;
    +  var dy = opt_y - cur.y;
    +
    +  // Set position to current left/top + delta
    +  goog.style.setPosition(el, el.offsetLeft + dx, el.offsetTop + dy);
    +};
    +
    +
    +/**
    + * Sets the width/height values of an element.  If an argument is numeric,
    + * or a goog.math.Size is passed, it is assumed to be pixels and will add
    + * 'px' after converting it to an integer in string form. (This just sets the
    + * CSS width and height properties so it might set content-box or border-box
    + * size depending on the box model the browser is using.)
    + *
    + * @param {Element} element Element to set the size of.
    + * @param {string|number|goog.math.Size} w Width of the element, or a
    + *     size object.
    + * @param {string|number=} opt_h Height of the element. Required if w is not a
    + *     size object.
    + */
    +goog.style.setSize = function(element, w, opt_h) {
    +  var h;
    +  if (w instanceof goog.math.Size) {
    +    h = w.height;
    +    w = w.width;
    +  } else {
    +    if (opt_h == undefined) {
    +      throw Error('missing height argument');
    +    }
    +    h = opt_h;
    +  }
    +
    +  goog.style.setWidth(element, /** @type {string|number} */ (w));
    +  goog.style.setHeight(element, /** @type {string|number} */ (h));
    +};
    +
    +
    +/**
    + * Helper function to create a string to be set into a pixel-value style
    + * property of an element. Can round to the nearest integer value.
    + *
    + * @param {string|number} value The style value to be used. If a number,
    + *     'px' will be appended, otherwise the value will be applied directly.
    + * @param {boolean} round Whether to round the nearest integer (if property
    + *     is a number).
    + * @return {string} The string value for the property.
    + * @private
    + */
    +goog.style.getPixelStyleValue_ = function(value, round) {
    +  if (typeof value == 'number') {
    +    value = (round ? Math.round(value) : value) + 'px';
    +  }
    +
    +  return value;
    +};
    +
    +
    +/**
    + * Set the height of an element.  Sets the element's style property.
    + * @param {Element} element Element to set the height of.
    + * @param {string|number} height The height value to set.  If a number, 'px'
    + *     will be appended, otherwise the value will be applied directly.
    + */
    +goog.style.setHeight = function(element, height) {
    +  element.style.height = goog.style.getPixelStyleValue_(height, true);
    +};
    +
    +
    +/**
    + * Set the width of an element.  Sets the element's style property.
    + * @param {Element} element Element to set the width of.
    + * @param {string|number} width The width value to set.  If a number, 'px'
    + *     will be appended, otherwise the value will be applied directly.
    + */
    +goog.style.setWidth = function(element, width) {
    +  element.style.width = goog.style.getPixelStyleValue_(width, true);
    +};
    +
    +
    +/**
    + * Gets the height and width of an element, even if its display is none.
    + *
    + * Specifically, this returns the height and width of the border box,
    + * irrespective of the box model in effect.
    + *
    + * Note that this function does not take CSS transforms into account. Please see
    + * {@code goog.style.getTransformedSize}.
    + * @param {Element} element Element to get size of.
    + * @return {!goog.math.Size} Object with width/height properties.
    + */
    +goog.style.getSize = function(element) {
    +  return goog.style.evaluateWithTemporaryDisplay_(
    +      goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
    +};
    +
    +
    +/**
    + * Call {@code fn} on {@code element} such that {@code element}'s dimensions are
    + * accurate when it's passed to {@code fn}.
    + * @param {function(!Element): T} fn Function to call with {@code element} as
    + *     an argument after temporarily changing {@code element}'s display such
    + *     that its dimensions are accurate.
    + * @param {!Element} element Element (which may have display none) to use as
    + *     argument to {@code fn}.
    + * @return {T} Value returned by calling {@code fn} with {@code element}.
    + * @template T
    + * @private
    + */
    +goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
    +  if (goog.style.getStyle_(element, 'display') != 'none') {
    +    return fn(element);
    +  }
    +
    +  var style = element.style;
    +  var originalDisplay = style.display;
    +  var originalVisibility = style.visibility;
    +  var originalPosition = style.position;
    +
    +  style.visibility = 'hidden';
    +  style.position = 'absolute';
    +  style.display = 'inline';
    +
    +  var retVal = fn(element);
    +
    +  style.display = originalDisplay;
    +  style.position = originalPosition;
    +  style.visibility = originalVisibility;
    +
    +  return retVal;
    +};
    +
    +
    +/**
    + * Gets the height and width of an element when the display is not none.
    + * @param {Element} element Element to get size of.
    + * @return {!goog.math.Size} Object with width/height properties.
    + * @private
    + */
    +goog.style.getSizeWithDisplay_ = function(element) {
    +  var offsetWidth = element.offsetWidth;
    +  var offsetHeight = element.offsetHeight;
    +  var webkitOffsetsZero =
    +      goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
    +  if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&
    +      element.getBoundingClientRect) {
    +    // Fall back to calling getBoundingClientRect when offsetWidth or
    +    // offsetHeight are not defined, or when they are zero in WebKit browsers.
    +    // This makes sure that we return for the correct size for SVG elements, but
    +    // will still return 0 on Webkit prior to 534.8, see
    +    // http://trac.webkit.org/changeset/67252.
    +    var clientRect = goog.style.getBoundingClientRect_(element);
    +    return new goog.math.Size(clientRect.right - clientRect.left,
    +        clientRect.bottom - clientRect.top);
    +  }
    +  return new goog.math.Size(offsetWidth, offsetHeight);
    +};
    +
    +
    +/**
    + * Gets the height and width of an element, post transform, even if its display
    + * is none.
    + *
    + * This is like {@code goog.style.getSize}, except:
    + * <ol>
    + * <li>Takes webkitTransforms such as rotate and scale into account.
    + * <li>Will return null if {@code element} doesn't respond to
    + *     {@code getBoundingClientRect}.
    + * <li>Currently doesn't make sense on non-WebKit browsers which don't support
    + *    webkitTransforms.
    + * </ol>
    + * @param {!Element} element Element to get size of.
    + * @return {goog.math.Size} Object with width/height properties.
    + */
    +goog.style.getTransformedSize = function(element) {
    +  if (!element.getBoundingClientRect) {
    +    return null;
    +  }
    +
    +  var clientRect = goog.style.evaluateWithTemporaryDisplay_(
    +      goog.style.getBoundingClientRect_, element);
    +  return new goog.math.Size(clientRect.right - clientRect.left,
    +      clientRect.bottom - clientRect.top);
    +};
    +
    +
    +/**
    + * Returns a bounding rectangle for a given element in page space.
    + * @param {Element} element Element to get bounds of. Must not be display none.
    + * @return {!goog.math.Rect} Bounding rectangle for the element.
    + */
    +goog.style.getBounds = function(element) {
    +  var o = goog.style.getPageOffset(element);
    +  var s = goog.style.getSize(element);
    +  return new goog.math.Rect(o.x, o.y, s.width, s.height);
    +};
    +
    +
    +/**
    + * Converts a CSS selector in the form style-property to styleProperty.
    + * @param {*} selector CSS Selector.
    + * @return {string} Camel case selector.
    + * @deprecated Use goog.string.toCamelCase instead.
    + */
    +goog.style.toCamelCase = function(selector) {
    +  return goog.string.toCamelCase(String(selector));
    +};
    +
    +
    +/**
    + * Converts a CSS selector in the form styleProperty to style-property.
    + * @param {string} selector Camel case selector.
    + * @return {string} Selector cased.
    + * @deprecated Use goog.string.toSelectorCase instead.
    + */
    +goog.style.toSelectorCase = function(selector) {
    +  return goog.string.toSelectorCase(selector);
    +};
    +
    +
    +/**
    + * Gets the opacity of a node (x-browser). This gets the inline style opacity
    + * of the node, and does not take into account the cascaded or the computed
    + * style for this node.
    + * @param {Element} el Element whose opacity has to be found.
    + * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
    + *     if the opacity is not set.
    + */
    +goog.style.getOpacity = function(el) {
    +  var style = el.style;
    +  var result = '';
    +  if ('opacity' in style) {
    +    result = style.opacity;
    +  } else if ('MozOpacity' in style) {
    +    result = style.MozOpacity;
    +  } else if ('filter' in style) {
    +    var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
    +    if (match) {
    +      result = String(match[1] / 100);
    +    }
    +  }
    +  return result == '' ? result : Number(result);
    +};
    +
    +
    +/**
    + * Sets the opacity of a node (x-browser).
    + * @param {Element} el Elements whose opacity has to be set.
    + * @param {number|string} alpha Opacity between 0 and 1 or an empty string
    + *     {@code ''} to clear the opacity.
    + */
    +goog.style.setOpacity = function(el, alpha) {
    +  var style = el.style;
    +  if ('opacity' in style) {
    +    style.opacity = alpha;
    +  } else if ('MozOpacity' in style) {
    +    style.MozOpacity = alpha;
    +  } else if ('filter' in style) {
    +    // TODO(arv): Overwriting the filter might have undesired side effects.
    +    if (alpha === '') {
    +      style.filter = '';
    +    } else {
    +      style.filter = 'alpha(opacity=' + alpha * 100 + ')';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the background of an element to a transparent image in a browser-
    + * independent manner.
    + *
    + * This function does not support repeating backgrounds or alternate background
    + * positions to match the behavior of Internet Explorer. It also does not
    + * support sizingMethods other than crop since they cannot be replicated in
    + * browsers other than Internet Explorer.
    + *
    + * @param {Element} el The element to set background on.
    + * @param {string} src The image source URL.
    + */
    +goog.style.setTransparentBackgroundImage = function(el, src) {
    +  var style = el.style;
    +  // It is safe to use the style.filter in IE only. In Safari 'filter' is in
    +  // style object but access to style.filter causes it to throw an exception.
    +  // Note: IE8 supports images with an alpha channel.
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    +    // See TODO in setOpacity.
    +    style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
    +        'src="' + src + '", sizingMethod="crop")';
    +  } else {
    +    // Set style properties individually instead of using background shorthand
    +    // to prevent overwriting a pre-existing background color.
    +    style.backgroundImage = 'url(' + src + ')';
    +    style.backgroundPosition = 'top left';
    +    style.backgroundRepeat = 'no-repeat';
    +  }
    +};
    +
    +
    +/**
    + * Clears the background image of an element in a browser independent manner.
    + * @param {Element} el The element to clear background image for.
    + */
    +goog.style.clearTransparentBackgroundImage = function(el) {
    +  var style = el.style;
    +  if ('filter' in style) {
    +    // See TODO in setOpacity.
    +    style.filter = '';
    +  } else {
    +    // Set style properties individually instead of using background shorthand
    +    // to prevent overwriting a pre-existing background color.
    +    style.backgroundImage = 'none';
    +  }
    +};
    +
    +
    +/**
    + * Shows or hides an element from the page. Hiding the element is done by
    + * setting the display property to "none", removing the element from the
    + * rendering hierarchy so it takes up no space. To show the element, the default
    + * inherited display property is restored (defined either in stylesheets or by
    + * the browser's default style rules.)
    + *
    + * Caveat 1: if the inherited display property for the element is set to "none"
    + * by the stylesheets, that is the property that will be restored by a call to
    + * showElement(), effectively toggling the display between "none" and "none".
    + *
    + * Caveat 2: if the element display style is set inline (by setting either
    + * element.style.display or a style attribute in the HTML), a call to
    + * showElement will clear that setting and defer to the inherited style in the
    + * stylesheet.
    + * @param {Element} el Element to show or hide.
    + * @param {*} display True to render the element in its default style,
    + *     false to disable rendering the element.
    + * @deprecated Use goog.style.setElementShown instead.
    + */
    +goog.style.showElement = function(el, display) {
    +  goog.style.setElementShown(el, display);
    +};
    +
    +
    +/**
    + * Shows or hides an element from the page. Hiding the element is done by
    + * setting the display property to "none", removing the element from the
    + * rendering hierarchy so it takes up no space. To show the element, the default
    + * inherited display property is restored (defined either in stylesheets or by
    + * the browser's default style rules).
    + *
    + * Caveat 1: if the inherited display property for the element is set to "none"
    + * by the stylesheets, that is the property that will be restored by a call to
    + * setElementShown(), effectively toggling the display between "none" and
    + * "none".
    + *
    + * Caveat 2: if the element display style is set inline (by setting either
    + * element.style.display or a style attribute in the HTML), a call to
    + * setElementShown will clear that setting and defer to the inherited style in
    + * the stylesheet.
    + * @param {Element} el Element to show or hide.
    + * @param {*} isShown True to render the element in its default style,
    + *     false to disable rendering the element.
    + */
    +goog.style.setElementShown = function(el, isShown) {
    +  el.style.display = isShown ? '' : 'none';
    +};
    +
    +
    +/**
    + * Test whether the given element has been shown or hidden via a call to
    + * {@link #setElementShown}.
    + *
    + * Note this is strictly a companion method for a call
    + * to {@link #setElementShown} and the same caveats apply; in particular, this
    + * method does not guarantee that the return value will be consistent with
    + * whether or not the element is actually visible.
    + *
    + * @param {Element} el The element to test.
    + * @return {boolean} Whether the element has been shown.
    + * @see #setElementShown
    + */
    +goog.style.isElementShown = function(el) {
    +  return el.style.display != 'none';
    +};
    +
    +
    +/**
    + * Installs the styles string into the window that contains opt_element.  If
    + * opt_element is null, the main window is used.
    + * @param {string} stylesString The style string to install.
    + * @param {Node=} opt_node Node whose parent document should have the
    + *     styles installed.
    + * @return {Element|StyleSheet} The style element created.
    + */
    +goog.style.installStyles = function(stylesString, opt_node) {
    +  var dh = goog.dom.getDomHelper(opt_node);
    +  var styleSheet = null;
    +
    +  // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
    +  // undefined as of IE 11.
    +  var doc = dh.getDocument();
    +  if (goog.userAgent.IE && doc.createStyleSheet) {
    +    styleSheet = doc.createStyleSheet();
    +    goog.style.setStyles(styleSheet, stylesString);
    +  } else {
    +    var head = dh.getElementsByTagNameAndClass('head')[0];
    +
    +    // In opera documents are not guaranteed to have a head element, thus we
    +    // have to make sure one exists before using it.
    +    if (!head) {
    +      var body = dh.getElementsByTagNameAndClass('body')[0];
    +      head = dh.createDom('head');
    +      body.parentNode.insertBefore(head, body);
    +    }
    +    styleSheet = dh.createDom('style');
    +    // NOTE(user): Setting styles after the style element has been appended
    +    // to the head results in a nasty Webkit bug in certain scenarios. Please
    +    // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
    +    // details.
    +    goog.style.setStyles(styleSheet, stylesString);
    +    dh.appendChild(head, styleSheet);
    +  }
    +  return styleSheet;
    +};
    +
    +
    +/**
    + * Removes the styles added by {@link #installStyles}.
    + * @param {Element|StyleSheet} styleSheet The value returned by
    + *     {@link #installStyles}.
    + */
    +goog.style.uninstallStyles = function(styleSheet) {
    +  var node = styleSheet.ownerNode || styleSheet.owningElement ||
    +      /** @type {Element} */ (styleSheet);
    +  goog.dom.removeNode(node);
    +};
    +
    +
    +/**
    + * Sets the content of a style element.  The style element can be any valid
    + * style element.  This element will have its content completely replaced by
    + * the new stylesString.
    + * @param {Element|StyleSheet} element A stylesheet element as returned by
    + *     installStyles.
    + * @param {string} stylesString The new content of the stylesheet.
    + */
    +goog.style.setStyles = function(element, stylesString) {
    +  if (goog.userAgent.IE && goog.isDef(element.cssText)) {
    +    // Adding the selectors individually caused the browser to hang if the
    +    // selector was invalid or there were CSS comments.  Setting the cssText of
    +    // the style node works fine and ignores CSS that IE doesn't understand.
    +    // However IE >= 11 doesn't support cssText any more, so we make sure that
    +    // cssText is a defined property and otherwise fall back to innerHTML.
    +    element.cssText = stylesString;
    +  } else {
    +    element.innerHTML = stylesString;
    +  }
    +};
    +
    +
    +/**
    + * Sets 'white-space: pre-wrap' for a node (x-browser).
    + *
    + * There are as many ways of specifying pre-wrap as there are browsers.
    + *
    + * CSS3/IE8: white-space: pre-wrap;
    + * Mozilla:  white-space: -moz-pre-wrap;
    + * Opera:    white-space: -o-pre-wrap;
    + * IE6/7:    white-space: pre; word-wrap: break-word;
    + *
    + * @param {Element} el Element to enable pre-wrap for.
    + */
    +goog.style.setPreWrap = function(el) {
    +  var style = el.style;
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    +    style.whiteSpace = 'pre';
    +    style.wordWrap = 'break-word';
    +  } else if (goog.userAgent.GECKO) {
    +    style.whiteSpace = '-moz-pre-wrap';
    +  } else {
    +    style.whiteSpace = 'pre-wrap';
    +  }
    +};
    +
    +
    +/**
    + * Sets 'display: inline-block' for an element (cross-browser).
    + * @param {Element} el Element to which the inline-block display style is to be
    + *    applied.
    + * @see ../demos/inline_block_quirks.html
    + * @see ../demos/inline_block_standards.html
    + */
    +goog.style.setInlineBlock = function(el) {
    +  var style = el.style;
    +  // Without position:relative, weirdness ensues.  Just accept it and move on.
    +  style.position = 'relative';
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    +    // IE8 supports inline-block so fall through to the else
    +    // Zoom:1 forces hasLayout, display:inline gives inline behavior.
    +    style.zoom = '1';
    +    style.display = 'inline';
    +  } else {
    +    // Opera, Webkit, and Safari seem to do OK with the standard inline-block
    +    // style.
    +    style.display = 'inline-block';
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the element is using right to left (rtl) direction.
    + * @param {Element} el  The element to test.
    + * @return {boolean} True for right to left, false for left to right.
    + */
    +goog.style.isRightToLeft = function(el) {
    +  return 'rtl' == goog.style.getStyle_(el, 'direction');
    +};
    +
    +
    +/**
    + * The CSS style property corresponding to an element being
    + * unselectable on the current browser platform (null if none).
    + * Opera and IE instead use a DOM attribute 'unselectable'.
    + * @type {?string}
    + * @private
    + */
    +goog.style.unselectableStyle_ =
    +    goog.userAgent.GECKO ? 'MozUserSelect' :
    +    goog.userAgent.WEBKIT ? 'WebkitUserSelect' :
    +    null;
    +
    +
    +/**
    + * Returns true if the element is set to be unselectable, false otherwise.
    + * Note that on some platforms (e.g. Mozilla), even if an element isn't set
    + * to be unselectable, it will behave as such if any of its ancestors is
    + * unselectable.
    + * @param {Element} el  Element to check.
    + * @return {boolean}  Whether the element is set to be unselectable.
    + */
    +goog.style.isUnselectable = function(el) {
    +  if (goog.style.unselectableStyle_) {
    +    return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
    +  } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    +    return el.getAttribute('unselectable') == 'on';
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Makes the element and its descendants selectable or unselectable.  Note
    + * that on some platforms (e.g. Mozilla), even if an element isn't set to
    + * be unselectable, it will behave as such if any of its ancestors is
    + * unselectable.
    + * @param {Element} el  The element to alter.
    + * @param {boolean} unselectable  Whether the element and its descendants
    + *     should be made unselectable.
    + * @param {boolean=} opt_noRecurse  Whether to only alter the element's own
    + *     selectable state, and leave its descendants alone; defaults to false.
    + */
    +goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
    +  // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
    +  var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
    +  var name = goog.style.unselectableStyle_;
    +  if (name) {
    +    // Add/remove the appropriate CSS style to/from the element and its
    +    // descendants.
    +    var value = unselectable ? 'none' : '';
    +    el.style[name] = value;
    +    if (descendants) {
    +      for (var i = 0, descendant; descendant = descendants[i]; i++) {
    +        descendant.style[name] = value;
    +      }
    +    }
    +  } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
    +    // Toggle the 'unselectable' attribute on the element and its descendants.
    +    var value = unselectable ? 'on' : '';
    +    el.setAttribute('unselectable', value);
    +    if (descendants) {
    +      for (var i = 0, descendant; descendant = descendants[i]; i++) {
    +        descendant.setAttribute('unselectable', value);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the border box size for an element.
    + * @param {Element} element  The element to get the size for.
    + * @return {!goog.math.Size} The border box size.
    + */
    +goog.style.getBorderBoxSize = function(element) {
    +  return new goog.math.Size(element.offsetWidth, element.offsetHeight);
    +};
    +
    +
    +/**
    + * Sets the border box size of an element. This is potentially expensive in IE
    + * if the document is CSS1Compat mode
    + * @param {Element} element  The element to set the size on.
    + * @param {goog.math.Size} size  The new size.
    + */
    +goog.style.setBorderBoxSize = function(element, size) {
    +  var doc = goog.dom.getOwnerDocument(element);
    +  var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    +
    +  if (goog.userAgent.IE &&
    +      !goog.userAgent.isVersionOrHigher('10') &&
    +      (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    +    var style = element.style;
    +    if (isCss1CompatMode) {
    +      var paddingBox = goog.style.getPaddingBox(element);
    +      var borderBox = goog.style.getBorderBox(element);
    +      style.pixelWidth = size.width - borderBox.left - paddingBox.left -
    +                         paddingBox.right - borderBox.right;
    +      style.pixelHeight = size.height - borderBox.top - paddingBox.top -
    +                          paddingBox.bottom - borderBox.bottom;
    +    } else {
    +      style.pixelWidth = size.width;
    +      style.pixelHeight = size.height;
    +    }
    +  } else {
    +    goog.style.setBoxSizingSize_(element, size, 'border-box');
    +  }
    +};
    +
    +
    +/**
    + * Gets the content box size for an element.  This is potentially expensive in
    + * all browsers.
    + * @param {Element} element  The element to get the size for.
    + * @return {!goog.math.Size} The content box size.
    + */
    +goog.style.getContentBoxSize = function(element) {
    +  var doc = goog.dom.getOwnerDocument(element);
    +  var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
    +  if (ieCurrentStyle &&
    +      goog.dom.getDomHelper(doc).isCss1CompatMode() &&
    +      ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
    +      !ieCurrentStyle.boxSizing) {
    +    // If IE in CSS1Compat mode than just use the width and height.
    +    // If we have a boxSizing then fall back on measuring the borders etc.
    +    var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width,
    +                                            'width', 'pixelWidth');
    +    var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height,
    +                                             'height', 'pixelHeight');
    +    return new goog.math.Size(width, height);
    +  } else {
    +    var borderBoxSize = goog.style.getBorderBoxSize(element);
    +    var paddingBox = goog.style.getPaddingBox(element);
    +    var borderBox = goog.style.getBorderBox(element);
    +    return new goog.math.Size(borderBoxSize.width -
    +                              borderBox.left - paddingBox.left -
    +                              paddingBox.right - borderBox.right,
    +                              borderBoxSize.height -
    +                              borderBox.top - paddingBox.top -
    +                              paddingBox.bottom - borderBox.bottom);
    +  }
    +};
    +
    +
    +/**
    + * Sets the content box size of an element. This is potentially expensive in IE
    + * if the document is BackCompat mode.
    + * @param {Element} element  The element to set the size on.
    + * @param {goog.math.Size} size  The new size.
    + */
    +goog.style.setContentBoxSize = function(element, size) {
    +  var doc = goog.dom.getOwnerDocument(element);
    +  var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
    +  if (goog.userAgent.IE &&
    +      !goog.userAgent.isVersionOrHigher('10') &&
    +      (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
    +    var style = element.style;
    +    if (isCss1CompatMode) {
    +      style.pixelWidth = size.width;
    +      style.pixelHeight = size.height;
    +    } else {
    +      var paddingBox = goog.style.getPaddingBox(element);
    +      var borderBox = goog.style.getBorderBox(element);
    +      style.pixelWidth = size.width + borderBox.left + paddingBox.left +
    +                         paddingBox.right + borderBox.right;
    +      style.pixelHeight = size.height + borderBox.top + paddingBox.top +
    +                          paddingBox.bottom + borderBox.bottom;
    +    }
    +  } else {
    +    goog.style.setBoxSizingSize_(element, size, 'content-box');
    +  }
    +};
    +
    +
    +/**
    + * Helper function that sets the box sizing as well as the width and height
    + * @param {Element} element  The element to set the size on.
    + * @param {goog.math.Size} size  The new size to set.
    + * @param {string} boxSizing  The box-sizing value.
    + * @private
    + */
    +goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
    +  var style = element.style;
    +  if (goog.userAgent.GECKO) {
    +    style.MozBoxSizing = boxSizing;
    +  } else if (goog.userAgent.WEBKIT) {
    +    style.WebkitBoxSizing = boxSizing;
    +  } else {
    +    // Includes IE8 and Opera 9.50+
    +    style.boxSizing = boxSizing;
    +  }
    +
    +  // Setting this to a negative value will throw an exception on IE
    +  // (and doesn't do anything different than setting it to 0).
    +  style.width = Math.max(size.width, 0) + 'px';
    +  style.height = Math.max(size.height, 0) + 'px';
    +};
    +
    +
    +/**
    + * IE specific function that converts a non pixel unit to pixels.
    + * @param {Element} element  The element to convert the value for.
    + * @param {string} value  The current value as a string. The value must not be
    + *     ''.
    + * @param {string} name  The CSS property name to use for the converstion. This
    + *     should be 'left', 'top', 'width' or 'height'.
    + * @param {string} pixelName  The CSS pixel property name to use to get the
    + *     value in pixels.
    + * @return {number} The value in pixels.
    + * @private
    + */
    +goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
    +  // Try if we already have a pixel value. IE does not do half pixels so we
    +  // only check if it matches a number followed by 'px'.
    +  if (/^\d+px?$/.test(value)) {
    +    return parseInt(value, 10);
    +  } else {
    +    var oldStyleValue = element.style[name];
    +    var oldRuntimeValue = element.runtimeStyle[name];
    +    // set runtime style to prevent changes
    +    element.runtimeStyle[name] = element.currentStyle[name];
    +    element.style[name] = value;
    +    var pixelValue = element.style[pixelName];
    +    // restore
    +    element.style[name] = oldStyleValue;
    +    element.runtimeStyle[name] = oldRuntimeValue;
    +    return pixelValue;
    +  }
    +};
    +
    +
    +/**
    + * Helper function for getting the pixel padding or margin for IE.
    + * @param {Element} element  The element to get the padding for.
    + * @param {string} propName  The property name.
    + * @return {number} The pixel padding.
    + * @private
    + */
    +goog.style.getIePixelDistance_ = function(element, propName) {
    +  var value = goog.style.getCascadedStyle(element, propName);
    +  return value ?
    +      goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0;
    +};
    +
    +
    +/**
    + * Gets the computed paddings or margins (on all sides) in pixels.
    + * @param {Element} element  The element to get the padding for.
    + * @param {string} stylePrefix  Pass 'padding' to retrieve the padding box,
    + *     or 'margin' to retrieve the margin box.
    + * @return {!goog.math.Box} The computed paddings or margins.
    + * @private
    + */
    +goog.style.getBox_ = function(element, stylePrefix) {
    +  if (goog.userAgent.IE) {
    +    var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
    +    var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
    +    var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
    +    var bottom = goog.style.getIePixelDistance_(
    +        element, stylePrefix + 'Bottom');
    +    return new goog.math.Box(top, right, bottom, left);
    +  } else {
    +    // On non-IE browsers, getComputedStyle is always non-null.
    +    var left = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, stylePrefix + 'Left'));
    +    var right = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, stylePrefix + 'Right'));
    +    var top = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, stylePrefix + 'Top'));
    +    var bottom = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, stylePrefix + 'Bottom'));
    +
    +    // NOTE(arv): Gecko can return floating point numbers for the computed
    +    // style values.
    +    return new goog.math.Box(parseFloat(top),
    +                             parseFloat(right),
    +                             parseFloat(bottom),
    +                             parseFloat(left));
    +  }
    +};
    +
    +
    +/**
    + * Gets the computed paddings (on all sides) in pixels.
    + * @param {Element} element  The element to get the padding for.
    + * @return {!goog.math.Box} The computed paddings.
    + */
    +goog.style.getPaddingBox = function(element) {
    +  return goog.style.getBox_(element, 'padding');
    +};
    +
    +
    +/**
    + * Gets the computed margins (on all sides) in pixels.
    + * @param {Element} element  The element to get the margins for.
    + * @return {!goog.math.Box} The computed margins.
    + */
    +goog.style.getMarginBox = function(element) {
    +  return goog.style.getBox_(element, 'margin');
    +};
    +
    +
    +/**
    + * A map used to map the border width keywords to a pixel width.
    + * @type {Object}
    + * @private
    + */
    +goog.style.ieBorderWidthKeywords_ = {
    +  'thin': 2,
    +  'medium': 4,
    +  'thick': 6
    +};
    +
    +
    +/**
    + * Helper function for IE to get the pixel border.
    + * @param {Element} element  The element to get the pixel border for.
    + * @param {string} prop  The part of the property name.
    + * @return {number} The value in pixels.
    + * @private
    + */
    +goog.style.getIePixelBorder_ = function(element, prop) {
    +  if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
    +    return 0;
    +  }
    +  var width = goog.style.getCascadedStyle(element, prop + 'Width');
    +  if (width in goog.style.ieBorderWidthKeywords_) {
    +    return goog.style.ieBorderWidthKeywords_[width];
    +  }
    +  return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
    +};
    +
    +
    +/**
    + * Gets the computed border widths (on all sides) in pixels
    + * @param {Element} element  The element to get the border widths for.
    + * @return {!goog.math.Box} The computed border widths.
    + */
    +goog.style.getBorderBox = function(element) {
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    var left = goog.style.getIePixelBorder_(element, 'borderLeft');
    +    var right = goog.style.getIePixelBorder_(element, 'borderRight');
    +    var top = goog.style.getIePixelBorder_(element, 'borderTop');
    +    var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
    +    return new goog.math.Box(top, right, bottom, left);
    +  } else {
    +    // On non-IE browsers, getComputedStyle is always non-null.
    +    var left = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, 'borderLeftWidth'));
    +    var right = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, 'borderRightWidth'));
    +    var top = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, 'borderTopWidth'));
    +    var bottom = /** @type {string} */ (
    +        goog.style.getComputedStyle(element, 'borderBottomWidth'));
    +
    +    return new goog.math.Box(parseFloat(top),
    +                             parseFloat(right),
    +                             parseFloat(bottom),
    +                             parseFloat(left));
    +  }
    +};
    +
    +
    +/**
    + * Returns the font face applied to a given node. Opera and IE should return
    + * the font actually displayed. Firefox returns the author's most-preferred
    + * font (whether the browser is capable of displaying it or not.)
    + * @param {Element} el  The element whose font family is returned.
    + * @return {string} The font family applied to el.
    + */
    +goog.style.getFontFamily = function(el) {
    +  var doc = goog.dom.getOwnerDocument(el);
    +  var font = '';
    +  // The moveToElementText method from the TextRange only works if the element
    +  // is attached to the owner document.
    +  if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
    +    var range = doc.body.createTextRange();
    +    range.moveToElementText(el);
    +    /** @preserveTry */
    +    try {
    +      font = range.queryCommandValue('FontName');
    +    } catch (e) {
    +      // This is a workaround for a awkward exception.
    +      // On some IE, there is an exception coming from it.
    +      // The error description from this exception is:
    +      // This window has already been registered as a drop target
    +      // This is bogus description, likely due to a bug in ie.
    +      font = '';
    +    }
    +  }
    +  if (!font) {
    +    // Note if for some reason IE can't derive FontName with a TextRange, we
    +    // fallback to using currentStyle
    +    font = goog.style.getStyle_(el, 'fontFamily');
    +  }
    +
    +  // Firefox returns the applied font-family string (author's list of
    +  // preferred fonts.) We want to return the most-preferred font, in lieu of
    +  // the *actually* applied font.
    +  var fontsArray = font.split(',');
    +  if (fontsArray.length > 1) font = fontsArray[0];
    +
    +  // Sanitize for x-browser consistency:
    +  // Strip quotes because browsers aren't consistent with how they're
    +  // applied; Opera always encloses, Firefox sometimes, and IE never.
    +  return goog.string.stripQuotes(font, '"\'');
    +};
    +
    +
    +/**
    + * Regular expression used for getLengthUnits.
    + * @type {RegExp}
    + * @private
    + */
    +goog.style.lengthUnitRegex_ = /[^\d]+$/;
    +
    +
    +/**
    + * Returns the units used for a CSS length measurement.
    + * @param {string} value  A CSS length quantity.
    + * @return {?string} The units of measurement.
    + */
    +goog.style.getLengthUnits = function(value) {
    +  var units = value.match(goog.style.lengthUnitRegex_);
    +  return units && units[0] || null;
    +};
    +
    +
    +/**
    + * Map of absolute CSS length units
    + * @type {Object}
    + * @private
    + */
    +goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
    +  'cm' : 1,
    +  'in' : 1,
    +  'mm' : 1,
    +  'pc' : 1,
    +  'pt' : 1
    +};
    +
    +
    +/**
    + * Map of relative CSS length units that can be accurately converted to px
    + * font-size values using getIePixelValue_. Only units that are defined in
    + * relation to a font size are convertible (%, small, etc. are not).
    + * @type {Object}
    + * @private
    + */
    +goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
    +  'em' : 1,
    +  'ex' : 1
    +};
    +
    +
    +/**
    + * Returns the font size, in pixels, of text in an element.
    + * @param {Element} el  The element whose font size is returned.
    + * @return {number} The font size (in pixels).
    + */
    +goog.style.getFontSize = function(el) {
    +  var fontSize = goog.style.getStyle_(el, 'fontSize');
    +  var sizeUnits = goog.style.getLengthUnits(fontSize);
    +  if (fontSize && 'px' == sizeUnits) {
    +    // NOTE(user): This could be parseFloat instead, but IE doesn't return
    +    // decimal fractions in getStyle_ and Firefox reports the fractions, but
    +    // ignores them when rendering. Interestingly enough, when we force the
    +    // issue and size something to e.g., 50% of 25px, the browsers round in
    +    // opposite directions with Firefox reporting 12px and IE 13px. I punt.
    +    return parseInt(fontSize, 10);
    +  }
    +
    +  // In IE, we can convert absolute length units to a px value using
    +  // goog.style.getIePixelValue_. Units defined in relation to a font size
    +  // (em, ex) are applied relative to the element's parentNode and can also
    +  // be converted.
    +  if (goog.userAgent.IE) {
    +    if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
    +      return goog.style.getIePixelValue_(el,
    +                                         fontSize,
    +                                         'left',
    +                                         'pixelLeft');
    +    } else if (el.parentNode &&
    +               el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
    +               sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
    +      // Check the parent size - if it is the same it means the relative size
    +      // value is inherited and we therefore don't want to count it twice.  If
    +      // it is different, this element either has explicit style or has a CSS
    +      // rule applying to it.
    +      var parentElement = /** @type {!Element} */ (el.parentNode);
    +      var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
    +      return goog.style.getIePixelValue_(parentElement,
    +                                         fontSize == parentSize ?
    +                                             '1em' : fontSize,
    +                                         'left',
    +                                         'pixelLeft');
    +    }
    +  }
    +
    +  // Sometimes we can't cleanly find the font size (some units relative to a
    +  // node's parent's font size are difficult: %, smaller et al), so we create
    +  // an invisible, absolutely-positioned span sized to be the height of an 'M'
    +  // rendered in its parent's (i.e., our target element's) font size. This is
    +  // the definition of CSS's font size attribute.
    +  var sizeElement = goog.dom.createDom(
    +      'span',
    +      {'style': 'visibility:hidden;position:absolute;' +
    +            'line-height:0;padding:0;margin:0;border:0;height:1em;'});
    +  goog.dom.appendChild(el, sizeElement);
    +  fontSize = sizeElement.offsetHeight;
    +  goog.dom.removeNode(sizeElement);
    +
    +  return fontSize;
    +};
    +
    +
    +/**
    + * Parses a style attribute value.  Converts CSS property names to camel case.
    + * @param {string} value The style attribute value.
    + * @return {!Object} Map of CSS properties to string values.
    + */
    +goog.style.parseStyleAttribute = function(value) {
    +  var result = {};
    +  goog.array.forEach(value.split(/\s*;\s*/), function(pair) {
    +    var keyValue = pair.split(/\s*:\s*/);
    +    if (keyValue.length == 2) {
    +      result[goog.string.toCamelCase(keyValue[0].toLowerCase())] = keyValue[1];
    +    }
    +  });
    +  return result;
    +};
    +
    +
    +/**
    + * Reverse of parseStyleAttribute; that is, takes a style object and returns the
    + * corresponding attribute value.  Converts camel case property names to proper
    + * CSS selector names.
    + * @param {Object} obj Map of CSS properties to values.
    + * @return {string} The style attribute value.
    + */
    +goog.style.toStyleAttribute = function(obj) {
    +  var buffer = [];
    +  goog.object.forEach(obj, function(value, key) {
    +    buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
    +  });
    +  return buffer.join('');
    +};
    +
    +
    +/**
    + * Sets CSS float property on an element.
    + * @param {Element} el The element to set float property on.
    + * @param {string} value The value of float CSS property to set on this element.
    + */
    +goog.style.setFloat = function(el, value) {
    +  el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;
    +};
    +
    +
    +/**
    + * Gets value of explicitly-set float CSS property on an element.
    + * @param {Element} el The element to get float property of.
    + * @return {string} The value of explicitly-set float CSS property on this
    + *     element.
    + */
    +goog.style.getFloat = function(el) {
    +  return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';
    +};
    +
    +
    +/**
    + * Returns the scroll bar width (represents the width of both horizontal
    + * and vertical scroll).
    + *
    + * @param {string=} opt_className An optional class name (or names) to apply
    + *     to the invisible div created to measure the scrollbar. This is necessary
    + *     if some scrollbars are styled differently than others.
    + * @return {number} The scroll bar width in px.
    + */
    +goog.style.getScrollbarWidth = function(opt_className) {
    +  // Add two hidden divs.  The child div is larger than the parent and
    +  // forces scrollbars to appear on it.
    +  // Using overflow:scroll does not work consistently with scrollbars that
    +  // are styled with ::-webkit-scrollbar.
    +  var outerDiv = goog.dom.createElement('div');
    +  if (opt_className) {
    +    outerDiv.className = opt_className;
    +  }
    +  outerDiv.style.cssText = 'overflow:auto;' +
    +      'position:absolute;top:0;width:100px;height:100px';
    +  var innerDiv = goog.dom.createElement('div');
    +  goog.style.setSize(innerDiv, '200px', '200px');
    +  outerDiv.appendChild(innerDiv);
    +  goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
    +  var width = outerDiv.offsetWidth - outerDiv.clientWidth;
    +  goog.dom.removeNode(outerDiv);
    +  return width;
    +};
    +
    +
    +/**
    + * Regular expression to extract x and y translation components from a CSS
    + * transform Matrix representation.
    + *
    + * @type {!RegExp}
    + * @const
    + * @private
    + */
    +goog.style.MATRIX_TRANSLATION_REGEX_ =
    +    new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    +               '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
    +               '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
    +
    +
    +/**
    + * Returns the x,y translation component of any CSS transforms applied to the
    + * element, in pixels.
    + *
    + * @param {!Element} element The element to get the translation of.
    + * @return {!goog.math.Coordinate} The CSS translation of the element in px.
    + */
    +goog.style.getCssTranslation = function(element) {
    +  var transform = goog.style.getComputedTransform(element);
    +  if (!transform) {
    +    return new goog.math.Coordinate(0, 0);
    +  }
    +  var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
    +  if (!matches) {
    +    return new goog.math.Coordinate(0, 0);
    +  }
    +  return new goog.math.Coordinate(parseFloat(matches[1]),
    +                                  parseFloat(matches[2]));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.html b/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.html
    new file mode 100644
    index 0000000000..9bd1a44344
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.html
    @@ -0,0 +1,45 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.style</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.style.style_document_scroll_test');
    +</script>
    +<style>
    +  body, html {
    +    height: 100%;
    +    margin: 0;
    +    padding: 0;
    +    position: absolute;
    +    white-space: nowrap;
    +    width: 800px;
    +  }
    +
    +  #testEl1 {
    +    background: lightblue;
    +    height: 110%;
    +    margin-top: 200px;
    +  }
    +
    +  #testEl2 {
    +    background: lightblue;
    +    display: inline-block;
    +    margin-left: 300px;
    +    width: 5000px;
    +  }
    +</style>
    +</head>
    +<body>
    +  <div id="testEl1">
    +    Test Element2
    +  </div>
    +  <div id="testEl2">Test Element4</div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.js b/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.js
    new file mode 100644
    index 0000000000..abd29fe63a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_document_scroll_test.js
    @@ -0,0 +1,188 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.style_document_scroll_test');
    +goog.setTestOnly('goog.style.style_document_scroll_test');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +
    +
    +var EPSILON = 2;
    +var body;
    +var html;
    +
    +function setUp() {
    +  body = document.body;
    +  setUpElement(body);
    +  html = document.documentElement;
    +  setUpElement(html);
    +}
    +
    +
    +function setUpElement(element) {
    +  element.scrollTop = 100;
    +  element.scrollLeft = 100;
    +}
    +
    +
    +function tearDown() {
    +  tearDownElement(body);
    +  tearDownElement(html);
    +}
    +
    +
    +function tearDownElement(element) {
    +  element.style.border = '';
    +  element.style.padding = '';
    +  element.style.margin = '';
    +  element.scrollTop = 0;
    +  element.scrollLeft = 0;
    +}
    +
    +
    +
    +function testDocumentScrollWithZeroedBodyProperties() {
    +  assertRoughlyEquals(200,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), body).y,
    +      EPSILON);
    +  assertRoughlyEquals(300,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), body).x,
    +      EPSILON);
    +}
    +
    +
    +function testHtmlScrollWithZeroedBodyProperties() {
    +  assertRoughlyEquals(200,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), html).y,
    +      EPSILON);
    +  assertRoughlyEquals(300,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), html).x,
    +      EPSILON);
    +}
    +
    +
    +function testDocumentScrollWithMargin() {
    +  body.style.margin = '20px 0 0 30px';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), body).y,
    +      EPSILON);
    +  assertRoughlyEquals(330,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), body).x,
    +      EPSILON);
    +}
    +
    +
    +function testHtmlScrollWithMargin() {
    +  html.style.margin = '20px 0 0 30px';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), html).y,
    +      EPSILON);
    +  assertRoughlyEquals(330,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), html).x,
    +      EPSILON);
    +}
    +
    +
    +
    +function testDocumentScrollWithPadding() {
    +  body.style.padding = '20px 0 0 30px';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), body).y,
    +      EPSILON);
    +  assertRoughlyEquals(330,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), body).x,
    +      EPSILON);
    +}
    +
    +
    +function testHtmlScrollWithPadding() {
    +  html.style.padding = '20px 0 0 30px';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), html).y,
    +      EPSILON);
    +  assertRoughlyEquals(330,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), html).x,
    +      EPSILON);
    +}
    +
    +
    +function testDocumentScrollWithBorder() {
    +  body.style.border = '20px solid green';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), body).y,
    +      EPSILON);
    +  assertRoughlyEquals(320,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), body).x,
    +      EPSILON);
    +}
    +
    +
    +function testHtmlScrollWithBorder() {
    +  html.style.border = '20px solid green';
    +  assertRoughlyEquals(220,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), html).y,
    +      EPSILON);
    +  assertRoughlyEquals(320,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), html).x,
    +      EPSILON);
    +}
    +
    +
    +function testDocumentScrollWithAllProperties() {
    +  body.style.margin = '20px 0 0 30px';
    +  body.style.padding = '40px 0 0 50px';
    +  body.style.border = '10px solid green';
    +  assertRoughlyEquals(270,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), body).y,
    +      EPSILON);
    +  assertRoughlyEquals(390,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), body).x,
    +      EPSILON);
    +}
    +
    +
    +function testHtmlScrollWithAllProperties() {
    +  html.style.margin = '20px 0 0 30px';
    +  html.style.padding = '40px 0 0 50px';
    +  html.style.border = '10px solid green';
    +  assertRoughlyEquals(270,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl1'), html).y,
    +      EPSILON);
    +  assertRoughlyEquals(390,
    +      goog.style.getContainerOffsetToScrollInto(
    +          goog.dom.getElement('testEl2'), html).x,
    +      EPSILON);
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_quirks_test.html b/mapbox-gl/closure-library/closure/goog/style/style_quirks_test.html
    new file mode 100644
    index 0000000000..e23ebccb4c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_quirks_test.html
    @@ -0,0 +1,538 @@
    +<!-- BackCompat -->
    +<!--
    +
    +  This is a copy of style_test.html but without a doctype. Make sure these two
    +  are in sync.
    +
    +-->
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta name="viewport" content="width=device-width, initial-scale=1.0,
    +    maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">
    +<title>Closure Unit Tests - goog.dom.style</title>
    +<script src="../base.js"></script>
    +<script>goog.require('goog.userAgent');</script>
    +<style>
    +
    +* {
    +  box-sizing: border-box;
    +  -moz-box-sizing: border-box;
    +  -webkit-box-sizing: border-box;
    +}
    +
    +</style>
    +<style>
    +
    +i {
    +  font-family: Times, sans-serif;
    +  font-size: 5em;
    +}
    +
    +#testEl5 {
    +  display: none;
    +}
    +
    +#styleTest1 {
    +  width: 120px;
    +  text-decoration: underline;
    +}
    +
    +#bgcolorTest0 {
    +  background-color: #f00;
    +}
    +
    +#bgcolorTest1 {
    +  background-color: #ff0000;
    +}
    +
    +#bgcolorTest2 {
    +  background-color: rgb(255, 0, 0);
    +}
    +
    +#bgcolorTest3 {
    +  background-color: rgb(100%, 0%, 0%);
    +}
    +
    +#bgcolorTest5 {
    +  background-color: lightblue;
    +}
    +
    +#bgcolorTest6 {
    +  background-color: inherit;
    +}
    +
    +#bgcolorTest7 {
    +  background-color: transparent;
    +}
    +
    +.rtl {
    +  direction: rtl;
    +}
    +
    +.ltr {
    +  direction: ltr;
    +}
    +
    +#pos-scroll-abs {
    +  position: absolute;
    +  top: 200px;
    +  left: 100px;
    +}
    +
    +#pos-scroll-abs-1 {
    +  overflow: scroll;
    +  width: 100px;
    +  height: 100px;
    +}
    +
    +#pos-scroll-abs-2 {
    +  position: absolute;
    +  top: 100px;
    +  left: 100px;
    +  width: 500px;
    +  background-color: pink;
    +}
    +
    +#abs-upper-left {
    +  position: absolute;
    +  top: 0px;
    +  left: 0px;
    +}
    +
    +#no-text-font-styles {
    +  font-family: "Helvetica", Times, serif;
    +  font-size: 30px;
    +}
    +
    +.century {
    +  font-family: "Comic Sans MS", "Century Schoolbook L", serif;
    +}
    +
    +#size-a,
    +#size-e {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  padding: 0;
    +  border-style: solid;
    +  border-color: black;
    +  border-width: 0;
    +}
    +
    +#size-b {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  border: 10px solid black;
    +}
    +
    +#size-c {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  border: 10px solid black;
    +  padding: 10px;
    +  overflow: auto;
    +}
    +
    +#size-d {
    +  width: 10em;
    +  height: 2cm;
    +  background: red;
    +  border: thick solid black;
    +  padding: 2mm;
    +}
    +
    +#size-f {
    +  border-width: 0;
    +  margin: 0;
    +  padding: 0;
    +}
    +
    +#css-position-absolute {
    +  position: absolute;
    +}
    +
    +#css-overflow-hidden {
    +  overflow: hidden;
    +}
    +
    +#css-z-index-200 {
    +  position:relative;
    +  z-index: 200;
    +}
    +
    +#css-text-align-center {
    +  text-align: center;
    +}
    +
    +#css-cursor-pointer {
    +  cursor: pointer;
    +}
    +
    +#test-opacity {
    +  opacity: 0.5;
    +  -moz-opacity: 0.5;
    +  filter: alpha(opacity=50);
    +}
    +
    +#test-frame-offset {
    +  display: block;
    +  position: absolute;
    +  top: 50px;
    +  left: 50px;
    +  width: 50px;
    +  height: 50px;
    +}
    +
    +#test-visible {
    +  background: yellow;
    +  position: absolute;
    +  overflow: hidden;
    +}
    +
    +#test-visible2 {
    +  background: #ebebeb;
    +  position: absolute;
    +  overflow: hidden;
    +}
    +
    +.scrollable-container {
    +  border: 8px solid blue;
    +  padding: 16px;
    +  margin: 32px;
    +  width: 100px;
    +  height: 100px;
    +  overflow: auto;
    +  position: absolute;
    +  left: 400px;
    +  top: 0;
    +}
    +
    +.scrollable-container-item {
    +  margin: 1px;
    +  border: 2px solid gray;
    +  padding: 4px;
    +  width: auto;
    +  /* The overflow is different from style_test so that we have consistent
    +     scroll positions in all browsers. */
    +  overflow: hidden;
    +  height: 20px;
    +}
    +
    +#translation {
    +  position: absolute;
    +  z-index: 10;
    +  left: 10px;
    +  top: 10px;
    +  width: 10px;
    +  height: 10px;
    +  background-color: blue;
    +  -webkit-transform: translate(20px, 30px);
    +  -ms-transform: translate(20px, 30px);
    +  -o-transform: translate(20px, 30px);
    +  -moz-transform: translate(20px, 30px);
    +  transform: translate(20px, 30px);
    +}
    +
    +</style>
    +</head>
    +<body>
    +  <div id="testEl">
    +    <span>Test Element</span>
    +  </div>
    +
    +  <div id="testEl5">
    +    <span>Test Element 5</span>
    +  </div>
    +
    +  <table id="table1">
    +    <tr>
    +      <td id="td1">td1</td>
    +    </tr>
    +  </table>
    +
    +  <span id="span0">span0</span>
    +
    +  <ul>
    +    <li id="li1">li1</li>
    +  </ul>
    +
    +  <span id="span1" class="test1"></span>
    +  <span id="span2" class="test1"></span>
    +  <span id="span3" class="test2"></span>
    +  <span id="span4" class="test3"></span>
    +  <span id="span5" class="test1"></span>
    +  <span id="span6" class="test1"></span>
    +
    +  <p id="p1"></p>
    +
    +  <div id="styleTest1"></div>
    +  <div id="styleTest2" style="width:100px;text-decoration:underline"></div>
    +  <div id="styleTest3"></div>
    +
    +  <!-- Paragraph to test element child and sibling -->
    +  <p id="p2">
    +    <!-- Comment -->
    +    a
    +    <b id="b1">c</b>
    +    d
    +    <!-- Comment -->
    +    e
    +    <b id="b2">f</b>
    +    g
    +    <!-- Comment -->
    +  </p>
    +
    +  <p style="background-color: #eee">
    +    <span id="bgcolorTest0">1</span>
    +    <span id="bgcolorTest1">1</span>
    +    <span id="bgcolorTest2">2</span>
    +    <span id="bgcolorTest3">3</span>
    +    <span id="bgcolorTest4" style="background-color:#ff0000">4</span>
    +    <span id="bgcolorTest5">5</span>
    +    <span id="bgcolorTest6">6</span>
    +    <span id="bgcolorTest7">7</span>
    +    <span id="bgcolorDest">Dest</span>
    +    <span id="installTest0">Styled 0</span>
    +    <span id="installTest1">Styled 1</span>
    +  </p>
    +
    +  <div class='rtl-test' dir='ltr' id='rtl1'>
    +    <div dir='rtl' id='rtl2'>right to left</div>
    +    <div dir='ltr' id='rtl3'>left to right</div>
    +    <div id='rtl4'>left to right (inherited)</div>
    +    <div id='rtl5' style="direction: rtl">right to left (style)</div>
    +    <div id='rtl6' style="direction: ltr">left to right (style)</div>
    +    <div id='rtl7' class=rtl>right to left (css)</div>
    +    <div id='rtl8' class=ltr>left to right (css)</div>
    +    <div class=rtl>
    +      <div id='rtl9'>right to left (css)</div>
    +    </div>
    +    <div class=ltr>
    +      <div id='rtl10'>left to right (css)</div>
    +    </div>
    +  </div>
    +
    +  <div id="pos-scroll-abs">
    +
    +    <p>Some text some text some text some text some text some text some text
    +    some text some text some text. Some text some text some text some text some
    +    text some text some text some text some text some text. Some text some text
    +    some text some text some text some text some text some text some text some
    +    text. Some text some text some text some text some text some text some text
    +    some text some text some text.
    +
    +    <p>Some text some text some text some text some text some text some text
    +    some text some text some text. Some text some text some text some text some
    +    text some text some text some text some text some text. Some text some text
    +    some text some text some text some text some text some text some text some
    +    text. Some text some text some text some text some text some text some text
    +    some text some text some text.
    +
    +    <div id="pos-scroll-abs-1">
    +      <p>Some text some text some text some text some text some text some text
    +      some text some text some text. Some text some text some text some text
    +      some text some text some text some text some text some text. Some text
    +      some text some text some text some text some text some text some text some
    +      text some text. Some text some text some text some text some text some
    +      text some text some text some text some text.
    +
    +      <p>Some text some text some text some text some text some text some text
    +      some text some text some text. Some text some text some text some text
    +      some text some text some text some text some text some text. Some text
    +      some text some text some text some text some text some text some text some
    +      text some text. Some text some text some text some text some text some
    +      text some text some text some text some text.
    +
    +      <div id="pos-scroll-abs-2">
    +
    +        <p>Some text some text some text some text some text some text some text
    +        some text some text some text. Some text some text some text some text
    +        some text some text some text some text some text some text. Some text
    +        some text some text some text some text some text some text some text
    +        some text some text. Some text some text some text some text some text
    +        some text some text some text some text some text.
    +
    +      </div>
    +
    +    </div>
    +
    +  </div>
    +
    +  <div id="abs-upper-left">
    +  foo
    +  </div>
    +
    +  <div id="no-text-font-styles">
    +    <font size="+1" face="Times,serif" id="font-tag">Times</font>
    +    <pre id="pre-font">pre text</pre>
    +    <span style="font:inherit" id="inherit-font">inherited</span>
    +    <span style="font-family:Times,sans-serif; font-size:3in"
    +          id="times-font-family">Times</span>
    +    <b id="bold-font">Bolded</b>
    +    <i id="css-html-tag-redefinition">Times</i>
    +    <span id="small-text" class="century" style="font-size:small">eensy</span>
    +    <span id="x-small-text" style="font-size:x-small">weensy</span>
    +    <span style="font:50% badFont" id="font-style-badfont">
    +      badFont
    +      <span style="font:inherit" id="inherit-50pct-font">
    +        same size as badFont
    +      </span>
    +    </span>
    +    <span id="icon-font" style="font:icon">Icon Font</span>
    +  </div>
    +  <span id="no-font-style">plain</span>
    +  <span style="font-family:Arial" id="nested-font">Arial<span style="font-family:Times">Times nested inside Arial</span></span>
    +  <img id="img-font-test" src=""/>
    +
    +  <span style="font-size:25px">
    +    <span style="font-size:12.5px" id="font-size-12-point-5-px">12.5PX</span>
    +    <span style="font-size:0.5em" id="font-size-50-pct-of-25-px">12.5PX</span>
    +  </span>
    +
    +<div id="size-a"></div>
    +
    +<div id="size-b"></div>
    +
    +<div id="size-c">xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxxxxxxxxxx</div>
    +
    +<div id="size-d">xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx x</div>
    +
    +<div id="size-e"></div>
    +
    +<div id="size-f">hello</div>
    +
    +<div style="font-size: 1px">
    +  <div style="font-size: 2em"><span id="em-font-size"></span></div>
    +</div>
    +
    +<div id="no-float"></div>
    +
    +<div id="float-none" style="float:none"></div>
    +
    +<div id="float-left" style="float:left"></div>
    +
    +<div id="float-test"></div>
    +
    +<div id="position-unset"></div>
    +<div id="style-position-relative" style="position:relative"></div>
    +<div id="style-position-fixed" style="position:fixed"></div>
    +<div id="css-position-absolute"></div>
    +
    +<div id="box-sizing-unset"></div>
    +<div id="box-sizing-border-box" style="box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;"></div>
    +
    +<div id="style-overflow-scroll" style="overflow:scroll"></div>
    +<div id="css-overflow-hidden"></div>
    +
    +<!-- Getting the computed z-index of an unpositioned element is unspecified. -->
    +<div id="style-z-index-200" style="position:relative;z-index:200"></div>
    +<div id="css-z-index-200"></div>
    +
    +<div id="style-text-align-right" style="text-align:right">
    +  <div id="style-text-align-right-inner">foo</div>
    +</div>
    +<div id="css-text-align-center"></div>
    +
    +<div id="style-cursor-move" style="cursor:move">
    +  <span id="style-cursor-move-inner">foo</span>
    +</div>
    +<div id="css-cursor-pointer"></div>
    +
    +<div id="height-test" style="display:inline-block;position:relative">
    +  <div id="height-test-inner" style="display:inline-block">
    +    foo
    +  </div>
    +</div>
    +
    +<div id="test-opacity"></div>
    +
    +<iframe id="test-frame-offset"></iframe>
    +
    +<iframe id="test-translate-frame-standard" src="style_test_standard.html"
    + style="overflow:auto;position:absolute;left:100px;top:150px;width:200px;height:200px;border:0px;">
    +</iframe>
    +<iframe id="test-translate-frame-quirk" src="style_test_quirk.html"
    + style="overflow:auto;position:absolute;left:100px;top:350px;width:200px;height:200px;border:0px;margin:0px;">
    +</iframe>
    +
    +<iframe
    +    id="test-visible-frame"
    +    src="style_test_iframe_standard.html"
    +    style="width: 200px; height: 200px; border: 0px;">
    +</iframe>
    +
    +<div id="test-scrollbarwidth" style="background-color: orange; width: 100px; height: 100px; overflow: auto;">
    +  <div style='width: 200px; height: 200px; background-color: red'>Test Scroll bar width with scroll</div>
    +</div>
    +
    +<div id="scrollable-container" class="scrollable-container">
    +  <!--
    +    Workaround for overlapping top padding of the container and top margin of
    +    the first item in Internet Explorer 6 and 7.
    +    See http://www.quirksmode.org/bugreports/archives/2005/01/IE_nested_boxes_padding_topmargin_top.html#c11285
    +  -->
    +  <div style="height: 0"><!--  --></div>
    +  <div id="item1" class="scrollable-container-item">1</div>
    +  <div id="item2" class="scrollable-container-item">2</div>
    +  <div id="item3" class="scrollable-container-item">3</div>
    +  <div id="item4" class="scrollable-container-item">4</div>
    +  <div id="item5" class="scrollable-container-item">5</div>
    +  <div id="item6" class="scrollable-container-item">6</div>
    +  <div id="item7" class="scrollable-container-item">7</div>
    +  <div id="item8" class="scrollable-container-item">8</div>
    +</div>
    +
    +<div id="test-visible">
    +Test-visible
    +<div id="test-visible-el" style="height:200px;">Test-visible</div>
    +Test-visible
    +</div>
    +
    +<div id="test-visible2"></div>
    +
    +<div id="msFilter" style="-ms-filter:'alpha(opacity=0)'">
    +  A div</div>
    +<div id="filter" style="filter:alpha(opacity=0)">
    +  Another div</div>
    +
    +<div id="offset-parent" style="position:relative">
    +  <div id="offset-child">child</div>
    +</div>
    +
    +<div id="offset-parent-overflow"
    +     style="overflow: scroll; width: 50px; height: 50px;">
    +  <a id="offset-child-overflow">
    +    scrollscrollscrollscrollscrollscrollscrollscroll
    +  </a>
    +</div>
    +
    +<div id="test-viewport"></div>
    +<div id="translation"></div>
    +
    +<div id="rotated"></div>
    +<div id="scaled"></div>
    +
    +<script>
    +if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9)) {
    +  document.write(
    +      '<iframe id="svg-frame" src="style_test_rect.svg"></' + 'iframe>');
    +}
    +goog.require('goog.style_test');
    +</script>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test.html b/mapbox-gl/closure-library/closure/goog/style/style_test.html
    new file mode 100644
    index 0000000000..c44322810e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test.html
    @@ -0,0 +1,526 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  When changing this, make sure that style_quirks_test.html is kept in sync.
    +
    +-->
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<meta name="viewport" content="width=device-width, initial-scale=1.0,
    +    maximum-scale=1.0, minimum-scale=1.0, user-scalable=0">
    +<title>Closure Unit Tests - goog.dom.style</title>
    +<script src="../base.js"></script>
    +<script>goog.require('goog.userAgent');</script>
    +<style>
    +
    +i {
    +  font-family: Times, sans-serif;
    +  font-size: 5em;
    +}
    +
    +#testEl5 {
    +  display: none;
    +}
    +
    +#styleTest1 {
    +  width: 120px;
    +  text-decoration: underline;
    +}
    +
    +#bgcolorTest0 {
    +  background-color: #f00;
    +}
    +
    +#bgcolorTest1 {
    +  background-color: #ff0000;
    +}
    +
    +#bgcolorTest2 {
    +  background-color: rgb(255, 0, 0);
    +}
    +
    +#bgcolorTest3 {
    +  background-color: rgb(100%, 0%, 0%);
    +}
    +
    +#bgcolorTest5 {
    +  background-color: lightblue;
    +}
    +
    +#bgcolorTest6 {
    +  background-color: inherit;
    +}
    +
    +#bgcolorTest7 {
    +  background-color: transparent;
    +}
    +
    +.rtl {
    +  direction: rtl;
    +}
    +
    +.ltr {
    +  direction: ltr;
    +}
    +
    +#pos-scroll-abs {
    +  position: absolute;
    +  top: 200px;
    +  left: 100px;
    +}
    +
    +#pos-scroll-abs-1 {
    +  overflow: scroll;
    +  width: 100px;
    +  height: 100px;
    +}
    +
    +#pos-scroll-abs-2 {
    +  position: absolute;
    +  top: 100px;
    +  left: 100px;
    +  width: 500px;
    +  background-color: pink;
    +}
    +
    +#abs-upper-left {
    +  position: absolute;
    +  top: 0px;
    +  left: 0px;
    +}
    +
    +#no-text-font-styles {
    +  font-family: "Helvetica", Times, serif;
    +  font-size: 30px;
    +}
    +
    +.century {
    +  font-family: "Comic Sans MS", "Century Schoolbook L", serif;
    +}
    +
    +#size-a,
    +#size-e {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  padding: 0;
    +  border-style: solid;
    +  border-color: black;
    +  border-width: 0;
    +}
    +
    +#size-b {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  border: 10px solid black;
    +}
    +
    +#size-c {
    +  width: 100px;
    +  height: 100px;
    +  background: red;
    +  border: 10px solid black;
    +  padding: 10px;
    +  overflow: auto;
    +}
    +
    +#size-d {
    +  width: 10em;
    +  height: 2cm;
    +  background: red;
    +  border: thick solid black;
    +  padding: 2mm;
    +}
    +
    +#size-f {
    +  border-width: 0;
    +  margin: 0;
    +  padding: 0;
    +}
    +
    +#css-position-absolute {
    +  position: absolute;
    +}
    +
    +#css-overflow-hidden {
    +  overflow: hidden;
    +}
    +
    +#css-z-index-200 {
    +  position:relative;
    +  z-index: 200;
    +}
    +
    +#css-text-align-center {
    +  text-align: center;
    +}
    +
    +#css-cursor-pointer {
    +  cursor: pointer;
    +}
    +
    +#test-opacity {
    +  opacity: 0.5;
    +  -moz-opacity: 0.5;
    +  filter: alpha(opacity=50);
    +}
    +
    +#test-frame-offset {
    +  display: block;
    +  position: absolute;
    +  top: 50px;
    +  left: 50px;
    +  width: 50px;
    +  height: 50px;
    +}
    +
    +#test-visible {
    +  background: yellow;
    +  position: absolute;
    +  overflow: hidden;
    +}
    +
    +#test-visible2 {
    +  background: #ebebeb;
    +  position: absolute;
    +  overflow: hidden;
    +}
    +
    +.scrollable-container {
    +  border: 8px solid blue;
    +  padding: 16px;
    +  margin: 32px;
    +  width: 100px;
    +  height: 100px;
    +  overflow: auto;
    +  position: absolute;
    +  left: 400px;
    +  top: 0;
    +}
    +
    +.scrollable-container-item {
    +  margin: 1px;
    +  border: 2px solid gray;
    +  padding: 4px;
    +  width: auto;
    +  height: 20px;
    +}
    +
    +#translation {
    +  position: absolute;
    +  z-index: 10;
    +  left: 10px;
    +  top: 10px;
    +  width: 10px;
    +  height: 10px;
    +  background-color: blue;
    +  -webkit-transform: translate(20px, 30px);
    +  -ms-transform: translate(20px, 30px);
    +  -o-transform: translate(20px, 30px);
    +  -moz-transform: translate(20px, 30px);
    +  transform: translate(20px, 30px);
    +}
    +
    +</style>
    +</head>
    +<body>
    +  <div id="testEl">
    +    <span>Test Element</span>
    +  </div>
    +
    +  <div id="testEl5">
    +    <span>Test Element 5</span>
    +  </div>
    +
    +  <table id="table1">
    +    <tr>
    +      <td id="td1">td1</td>
    +    </tr>
    +  </table>
    +
    +  <span id="span0">span0</span>
    +
    +  <ul>
    +    <li id="li1">li1</li>
    +  </ul>
    +
    +  <span id="span1" class="test1"></span>
    +  <span id="span2" class="test1"></span>
    +  <span id="span3" class="test2"></span>
    +  <span id="span4" class="test3"></span>
    +  <span id="span5" class="test1"></span>
    +  <span id="span6" class="test1"></span>
    +
    +  <p id="p1"></p>
    +
    +  <div id="styleTest1"></div>
    +  <div id="styleTest2" style="width:100px;text-decoration:underline"></div>
    +  <div id="styleTest3"></div>
    +
    +  <!-- Paragraph to test element child and sibling -->
    +  <p id="p2">
    +    <!-- Comment -->
    +    a
    +    <b id="b1">c</b>
    +    d
    +    <!-- Comment -->
    +    e
    +    <b id="b2">f</b>
    +    g
    +    <!-- Comment -->
    +  </p>
    +
    +  <p style="background-color: #eee">
    +    <span id="bgcolorTest0">1</span>
    +    <span id="bgcolorTest1">1</span>
    +    <span id="bgcolorTest2">2</span>
    +    <span id="bgcolorTest3">3</span>
    +    <span id="bgcolorTest4" style="background-color:#ff0000">4</span>
    +    <span id="bgcolorTest5">5</span>
    +    <span id="bgcolorTest6">6</span>
    +    <span id="bgcolorTest7">7</span>
    +    <span id="bgcolorDest">Dest</span>
    +    <span id="installTest0">Styled 0</span>
    +    <span id="installTest1">Styled 1</span>
    +  </p>
    +
    +  <div class='rtl-test' dir='ltr' id='rtl1'>
    +    <div dir='rtl' id='rtl2'>right to left</div>
    +    <div dir='ltr' id='rtl3'>left to right</div>
    +    <div id='rtl4'>left to right (inherited)</div>
    +    <div id='rtl5' style="direction: rtl">right to left (style)</div>
    +    <div id='rtl6' style="direction: ltr">left to right (style)</div>
    +    <div id='rtl7' class=rtl>right to left (css)</div>
    +    <div id='rtl8' class=ltr>left to right (css)</div>
    +    <div class=rtl>
    +      <div id='rtl9'>right to left (css)</div>
    +    </div>
    +    <div class=ltr>
    +      <div id='rtl10'>left to right (css)</div>
    +    </div>
    +  </div>
    +
    +  <div id="pos-scroll-abs">
    +
    +    <p>Some text some text some text some text some text some text some text
    +    some text some text some text. Some text some text some text some text some
    +    text some text some text some text some text some text. Some text some text
    +    some text some text some text some text some text some text some text some
    +    text. Some text some text some text some text some text some text some text
    +    some text some text some text.
    +
    +    <p>Some text some text some text some text some text some text some text
    +    some text some text some text. Some text some text some text some text some
    +    text some text some text some text some text some text. Some text some text
    +    some text some text some text some text some text some text some text some
    +    text. Some text some text some text some text some text some text some text
    +    some text some text some text.
    +
    +    <div id="pos-scroll-abs-1">
    +      <p>Some text some text some text some text some text some text some text
    +      some text some text some text. Some text some text some text some text
    +      some text some text some text some text some text some text. Some text
    +      some text some text some text some text some text some text some text some
    +      text some text. Some text some text some text some text some text some
    +      text some text some text some text some text.
    +
    +      <p>Some text some text some text some text some text some text some text
    +      some text some text some text. Some text some text some text some text
    +      some text some text some text some text some text some text. Some text
    +      some text some text some text some text some text some text some text some
    +      text some text. Some text some text some text some text some text some
    +      text some text some text some text some text.
    +
    +      <div id="pos-scroll-abs-2">
    +
    +        <p>Some text some text some text some text some text some text some text
    +        some text some text some text. Some text some text some text some text
    +        some text some text some text some text some text some text. Some text
    +        some text some text some text some text some text some text some text
    +        some text some text. Some text some text some text some text some text
    +        some text some text some text some text some text.
    +
    +      </div>
    +
    +    </div>
    +
    +  </div>
    +
    +  <div id="abs-upper-left">
    +  foo
    +  </div>
    +
    +  <div id="no-text-font-styles">
    +    <font size="+1" face="Times,serif" id="font-tag">Times</font>
    +    <pre id="pre-font">pre text</pre>
    +    <span style="font:inherit" id="inherit-font">inherited</span>
    +    <span style="font-family:Times,sans-serif; font-size:3in"
    +          id="times-font-family">Times</span>
    +    <b id="bold-font">Bolded</b>
    +    <i id="css-html-tag-redefinition">Times</i>
    +    <span id="small-text" class="century" style="font-size:small">eensy</span>
    +    <span id="x-small-text" style="font-size:x-small">weensy</span>
    +    <span style="font:50% badFont" id="font-style-badfont">
    +      badFont
    +      <span style="font:inherit" id="inherit-50pct-font">
    +        same size as badFont
    +      </span>
    +    </span>
    +    <span id="icon-font" style="font:icon">Icon Font</span>
    +  </div>
    +  <span id="no-font-style">plain</span>
    +  <span style="font-family:Arial" id="nested-font">Arial<span style="font-family:Times">Times nested inside Arial</span></span>
    +  <img id="img-font-test" src=""/>
    +
    +  <span style="font-size:25px">
    +    <span style="font-size:12.5px" id="font-size-12-point-5-px">12.5PX</span>
    +    <span style="font-size:0.5em" id="font-size-50-pct-of-25-px">12.5PX</span>
    +  </span>
    +
    +<div id="size-a"></div>
    +
    +<div id="size-b"></div>
    +
    +<div id="size-c">xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxxxxxxxxxx</div>
    +
    +<div id="size-d">xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxx
    +xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    +xxxxxxxx x</div>
    +
    +<div id="size-e"></div>
    +
    +<div id="size-f">hello</div>
    +
    +<div style="font-size: 1px">
    +  <div style="font-size: 2em"><span id="em-font-size"></span></div>
    +</div>
    +
    +<div id="no-float"></div>
    +
    +<div id="float-none" style="float:none"></div>
    +
    +<div id="float-left" style="float:left"></div>
    +
    +<div id="float-test"></div>
    +
    +<div id="position-unset"></div>
    +<div id="style-position-relative" style="position:relative"></div>
    +<div id="style-position-fixed" style="position:fixed"></div>
    +<div id="css-position-absolute"></div>
    +
    +<div id="box-sizing-unset"></div>
    +<div id="box-sizing-border-box" style="box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;"></div>
    +
    +<div id="style-overflow-scroll" style="overflow:scroll"></div>
    +<div id="css-overflow-hidden"></div>
    +
    +<!-- Getting the computed z-index of an unpositioned element is unspecified. -->
    +<div id="style-z-index-200" style="position:relative;z-index:200"></div>
    +<div id="css-z-index-200"></div>
    +
    +<div id="style-text-align-right" style="text-align:right">
    +  <div id="style-text-align-right-inner">foo</div>
    +</div>
    +<div id="css-text-align-center"></div>
    +
    +<div id="style-cursor-move" style="cursor:move">
    +  <span id="style-cursor-move-inner">foo</span>
    +</div>
    +<div id="css-cursor-pointer"></div>
    +
    +<div id="height-test" style="display:inline-block;position:relative">
    +  <div id="height-test-inner" style="display:inline-block">
    +    foo
    +  </div>
    +</div>
    +
    +<div id="test-opacity"></div>
    +
    +<iframe id="test-frame-offset"></iframe>
    +
    +<iframe id="test-translate-frame-standard" src="style_test_standard.html"
    + style="overflow:auto;position:absolute;left:100px;top:150px;width:200px;height:200px;border:0px;">
    +</iframe>
    +<iframe id="test-translate-frame-quirk" src="style_test_quirk.html"
    + style="overflow:auto;position:absolute;left:100px;top:350px;width:200px;height:200px;border:0px;margin:0px;">
    +</iframe>
    +
    +<iframe
    +    id="test-visible-frame"
    +    src="style_test_iframe_standard.html"
    +    style="width: 200px; height: 200px; border: 0px;">
    +</iframe>
    +
    +<div id="test-scrollbarwidth" style="background-color: orange; width: 100px; height: 100px; overflow: auto;">
    +  <div style='width: 200px; height: 200px; background-color: red'>Test Scroll bar width with scroll</div>
    +</div>
    +
    +<div id="scrollable-container" class="scrollable-container">
    +  <!--
    +    Workaround for overlapping top padding of the container and top margin of
    +    the first item in Internet Explorer 6 and 7.
    +    See http://www.quirksmode.org/bugreports/archives/2005/01/IE_nested_boxes_padding_topmargin_top.html#c11285
    +  -->
    +  <div style="height: 0"><!--  --></div>
    +  <div id="item1" class="scrollable-container-item">1</div>
    +  <div id="item2" class="scrollable-container-item">2</div>
    +  <div id="item3" class="scrollable-container-item">3</div>
    +  <div id="item4" class="scrollable-container-item">4</div>
    +  <div id="item5" class="scrollable-container-item">5</div>
    +  <div id="item6" class="scrollable-container-item">6</div>
    +  <div id="item7" class="scrollable-container-item">7</div>
    +  <div id="item8" class="scrollable-container-item">8</div>
    +</div>
    +
    +<div id="test-visible">
    +Test-visible
    +<div id="test-visible-el" style="height:200px;">Test-visible</div>
    +Test-visible
    +</div>
    +
    +<div id="test-visible2"></div>
    +
    +<div id="msFilter" style="-ms-filter:'alpha(opacity=0)'">
    +  A div</div>
    +<div id="filter" style="filter:alpha(opacity=0)">
    +  Another div</div>
    +
    +<div id="offset-parent" style="position:relative">
    +  <div id="offset-child">child</div>
    +</div>
    +
    +<div id="offset-parent-overflow"
    +     style="overflow: scroll; width: 50px; height: 50px;">
    +  <a id="offset-child-overflow">
    +    scrollscrollscrollscrollscrollscrollscrollscroll
    +  </a>
    +</div>
    +
    +<div id="test-viewport"></div>
    +<div id="translation"></div>
    +
    +<div id="rotated"></div>
    +<div id="scaled"></div>
    +
    +<script>
    +if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9)) {
    +  document.write(
    +      '<iframe id="svg-frame" src="style_test_rect.svg"></' + 'iframe>');
    +}
    +goog.require('goog.style_test');
    +</script>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test.js b/mapbox-gl/closure-library/closure/goog/style/style_test.js
    new file mode 100644
    index 0000000000..d51f83ae4e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test.js
    @@ -0,0 +1,2640 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the Licensegg at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Shared unit tests for styles.
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.style_test');
    +
    +goog.require('goog.array');
    +goog.require('goog.color');
    +goog.require('goog.dom');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Rect');
    +goog.require('goog.math.Size');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgentTestUtil');
    +goog.require('goog.userAgentTestUtil.UserAgents');
    +
    +goog.setTestOnly('goog.style_test');
    +
    +// IE before version 6 will always be border box in compat mode.
    +var isBorderBox = goog.dom.isCss1CompatMode() ?
    +    (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('6')) :
    +    true;
    +var EPSILON = 2;
    +var expectedFailures = new goog.testing.ExpectedFailures();
    +var $ = goog.dom.getElement;
    +var mockUserAgent;
    +
    +function setUpPage() {
    +  var viewportSize = goog.dom.getViewportSize();
    +  // When the window is too short or not wide enough, some tests, especially
    +  // those for off-screen elements, fail.  Oddly, the most reliable
    +  // indicator is a width of zero (which is of course erroneous), since
    +  // height sometimes includes a scroll bar.  We can make no assumptions on
    +  // window size on the Selenium farm.
    +  if (goog.userAgent.IE && viewportSize.width < 300) {
    +    // Move to origin, since IE won't resize outside the screen.
    +    window.moveTo(0, 0);
    +    window.resizeTo(640, 480);
    +  }
    +}
    +
    +function setUp() {
    +  window.scrollTo(0, 0);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  mockUserAgent = new goog.testing.MockUserAgent();
    +  mockUserAgent.install();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  var testVisibleDiv2 = goog.dom.getElement('test-visible2');
    +  testVisibleDiv2.setAttribute('style', '');
    +  testVisibleDiv2.innerHTML = '';
    +  var testViewport = goog.dom.getElement('test-viewport');
    +  testViewport.setAttribute('style', '');
    +  testViewport.innerHTML = '';
    +  goog.dispose(mockUserAgent);
    +
    +  // Prevent multiple vendor prefixed mock elements from poisoning the cache.
    +  goog.style.styleNameCache_ = {};
    +}
    +
    +function testSetStyle() {
    +  var el = $('span1');
    +  goog.style.setStyle(el, 'textDecoration', 'underline');
    +  assertEquals('Should be underline', 'underline', el.style.textDecoration);
    +}
    +
    +function testSetStyleMap() {
    +  var el = $('span6');
    +
    +  var styles = {
    +    'background-color': 'blue',
    +    'font-size': '100px',
    +    textAlign: 'center'
    +  };
    +
    +  goog.style.setStyle(el, styles);
    +
    +  var answers = {
    +    backgroundColor: 'blue',
    +    fontSize: '100px',
    +    textAlign: 'center'
    +  };
    +
    +  goog.object.forEach(answers, function(value, style) {
    +    assertEquals('Should be ' + value, value, el.style[style]);
    +  });
    +}
    +
    +function testSetStyleWithNonCamelizedString() {
    +  var el = $('span5');
    +  goog.style.setStyle(el, 'text-decoration', 'underline');
    +  assertEquals('Should be underline', 'underline', el.style.textDecoration);
    +}
    +
    +function testGetStyle() {
    +  var el = goog.dom.getElement('styleTest3');
    +  goog.style.setStyle(el, 'width', '80px');
    +  goog.style.setStyle(el, 'textDecoration', 'underline');
    +
    +  assertEquals('80px', goog.style.getStyle(el, 'width'));
    +  assertEquals('underline', goog.style.getStyle(el, 'textDecoration'));
    +  assertEquals('underline', goog.style.getStyle(el, 'text-decoration'));
    +  // Non set properties are always empty strings.
    +  assertEquals('', goog.style.getStyle(el, 'border'));
    +}
    +
    +function testGetStyleMsFilter() {
    +  // Element with -ms-filter style set.
    +  var e = goog.dom.getElement('msFilter');
    +
    +  if (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(8) &&
    +      !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    // Only IE8/9 supports -ms-filter and returns it as value for the "filter"
    +    // property. When in compatibility mode, -ms-filter is not supported
    +    // and IE8 behaves as IE7 so the other case will apply.
    +    assertEquals('alpha(opacity=0)', goog.style.getStyle(e, 'filter'));
    +  } else {
    +    // Any other browser does not support ms-filter so it returns empty string.
    +    assertEquals('', goog.style.getStyle(e, 'filter'));
    +  }
    +}
    +
    +function testGetStyleFilter() {
    +  // Element with filter style set.
    +  var e = goog.dom.getElement('filter');
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    // Filter supported.
    +    assertEquals('alpha(opacity=0)', goog.style.getStyle(e, 'filter'));
    +  } else {
    +    assertEquals('', goog.style.getStyle(e, 'filter'));
    +  }
    +}
    +
    +function testGetComputedStyleMsFilter() {
    +  // Element with -ms-filter style set.
    +  var e = goog.dom.getElement('msFilter');
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    if (goog.userAgent.isDocumentModeOrHigher(9)) {
    +      // IE 9 returns the value.
    +      assertEquals('alpha(opacity=0)',
    +          goog.style.getComputedStyle(e, 'filter'));
    +    } else {
    +      // Older IE always returns empty string for computed styles.
    +      assertEquals('', goog.style.getComputedStyle(e, 'filter'));
    +    }
    +  } else {
    +    // Non IE returns 'none' for filter as it is an SVG property
    +    assertEquals('none', goog.style.getComputedStyle(e, 'filter'));
    +  }
    +}
    +
    +function testGetComputedStyleFilter() {
    +  // Element with filter style set.
    +  var e = goog.dom.getElement('filter');
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    if (goog.userAgent.isDocumentModeOrHigher(9)) {
    +      // IE 9 returns the value.
    +      assertEquals('alpha(opacity=0)',
    +          goog.style.getComputedStyle(e, 'filter'));
    +    } else {
    +      // Older IE always returns empty string for computed styles.
    +      assertEquals('', goog.style.getComputedStyle(e, 'filter'));
    +    }
    +  } else {
    +    // Non IE returns 'none' for filter as it is an SVG property
    +    assertEquals('none', goog.style.getComputedStyle(e, 'filter'));
    +  }
    +}
    +
    +function testGetComputedBoxSizing() {
    +  if (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher(8)) {
    +    var defaultBoxSizing = goog.dom.isCss1CompatMode() ?
    +        'content-box' : 'border-box';
    +    var el = goog.dom.getElement('box-sizing-unset');
    +    assertEquals(defaultBoxSizing, goog.style.getComputedBoxSizing(el));
    +
    +    el = goog.dom.getElement('box-sizing-border-box');
    +    assertEquals('border-box', goog.style.getComputedBoxSizing(el));
    +  } else {
    +    // IE7 and below don't support box-sizing.
    +    assertNull(goog.style.getComputedBoxSizing(
    +        goog.dom.getElement('box-sizing-border-box')));
    +  }
    +}
    +
    +function testGetComputedPosition() {
    +  assertEquals('position not set', 'static',
    +               goog.style.getComputedPosition($('position-unset')));
    +  assertEquals('position:relative in style attribute', 'relative',
    +               goog.style.getComputedPosition($('style-position-relative')));
    +  if (goog.userAgent.IE && !goog.dom.isCss1CompatMode() &&
    +      !goog.userAgent.isVersionOrHigher(10)) {
    +    assertEquals('position:fixed in style attribute', 'static',
    +        goog.style.getComputedPosition($('style-position-fixed')));
    +  } else {
    +    assertEquals('position:fixed in style attribute', 'fixed',
    +        goog.style.getComputedPosition($('style-position-fixed')));
    +  }
    +  assertEquals('position:absolute in css', 'absolute',
    +               goog.style.getComputedPosition($('css-position-absolute')));
    +}
    +
    +function testGetComputedOverflowXAndY() {
    +  assertEquals('overflow-x:scroll in style attribute', 'scroll',
    +               goog.style.getComputedOverflowX($('style-overflow-scroll')));
    +  assertEquals('overflow-y:scroll in style attribute', 'scroll',
    +               goog.style.getComputedOverflowY($('style-overflow-scroll')));
    +  assertEquals('overflow-x:hidden in css', 'hidden',
    +               goog.style.getComputedOverflowX($('css-overflow-hidden')));
    +  assertEquals('overflow-y:hidden in css', 'hidden',
    +               goog.style.getComputedOverflowY($('css-overflow-hidden')));
    +}
    +
    +function testGetComputedZIndex() {
    +  assertEquals('z-index:200 in style attribute', '200',
    +               '' + goog.style.getComputedZIndex($('style-z-index-200')));
    +  assertEquals('z-index:200 in css', '200',
    +               '' + goog.style.getComputedZIndex($('css-z-index-200')));
    +}
    +
    +function testGetComputedTextAlign() {
    +  assertEquals('text-align:right in style attribute', 'right',
    +               goog.style.getComputedTextAlign($('style-text-align-right')));
    +  assertEquals(
    +      'text-align:right inherited from parent', 'right',
    +      goog.style.getComputedTextAlign($('style-text-align-right-inner')));
    +  assertEquals('text-align:center in css', 'center',
    +               goog.style.getComputedTextAlign($('css-text-align-center')));
    +}
    +
    +function testGetComputedCursor() {
    +  assertEquals('cursor:move in style attribute', 'move',
    +               goog.style.getComputedCursor($('style-cursor-move')));
    +  assertEquals('cursor:move inherited from parent', 'move',
    +               goog.style.getComputedCursor($('style-cursor-move-inner')));
    +  assertEquals('cursor:poiner in css', 'pointer',
    +               goog.style.getComputedCursor($('css-cursor-pointer')));
    +}
    +
    +function testGetBackgroundColor() {
    +  var dest = $('bgcolorDest');
    +
    +  for (var i = 0; $('bgcolorTest' + i); i++) {
    +    var src = $('bgcolorTest' + i);
    +    var bgColor = goog.style.getBackgroundColor(src);
    +
    +    dest.style.backgroundColor = bgColor;
    +    assertEquals('Background colors should be equal',
    +                 goog.style.getBackgroundColor(src),
    +                 goog.style.getBackgroundColor(dest));
    +
    +    try {
    +      // goog.color.parse throws a generic exception if handed input it
    +      // doesn't understand.
    +      var c = goog.color.parse(bgColor);
    +      assertEquals('rgb(255,0,0)', goog.color.hexToRgbStyle(c.hex));
    +    } catch (e) {
    +      // Internet Explorer is unable to parse colors correctly after test 4.
    +      // Other browsers may vary, but all should be able to handle straight
    +      // hex input.
    +      assertFalse('Should be able to parse color "' + bgColor + '"', i < 5);
    +    }
    +  }
    +}
    +
    +function testSetPosition() {
    +  var el = $('testEl');
    +
    +  goog.style.setPosition(el, 100, 100);
    +  assertEquals('100px', el.style.left);
    +  assertEquals('100px', el.style.top);
    +
    +  goog.style.setPosition(el, '50px', '25px');
    +  assertEquals('50px', el.style.left);
    +  assertEquals('25px', el.style.top);
    +
    +  goog.style.setPosition(el, '10ex', '25px');
    +  assertEquals('10ex', el.style.left);
    +  assertEquals('25px', el.style.top);
    +
    +  goog.style.setPosition(el, '10%', '25%');
    +  assertEquals('10%', el.style.left);
    +  assertEquals('25%', el.style.top);
    +
    +  // ignores stupid units
    +  goog.style.setPosition(el, 0, 0);
    +  // TODO(user): IE errors if you set these values.  Should we make setStyle
    +  // catch these?  Or leave it up to the app.  Fixing the tests for now.
    +  //goog.style.setPosition(el, '10rainbows', '25rainbows');
    +  assertEquals('0px', el.style.left);
    +  assertEquals('0px', el.style.top);
    +
    +
    +  goog.style.setPosition(el, new goog.math.Coordinate(20, 40));
    +  assertEquals('20px', el.style.left);
    +  assertEquals('40px', el.style.top);
    +}
    +
    +function testGetClientPositionAbsPositionElement() {
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '100px';
    +  div.style.top = '200px';
    +  document.body.appendChild(div);
    +  var pos = goog.style.getClientPosition(div);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetClientPositionNestedElements() {
    +  var innerDiv = goog.dom.createDom('DIV');
    +  innerDiv.style.position = 'relative';
    +  innerDiv.style.left = '-10px';
    +  innerDiv.style.top = '-10px';
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '150px';
    +  div.style.top = '250px';
    +  div.appendChild(innerDiv);
    +  document.body.appendChild(div);
    +  var pos = goog.style.getClientPosition(innerDiv);
    +  assertEquals(140, pos.x);
    +  assertEquals(240, pos.y);
    +}
    +
    +function testGetClientPositionOfOffscreenElement() {
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '2000px';
    +  div.style.top = '2000px';
    +  div.style.width = '10px';
    +  div.style.height = '10px';
    +  document.body.appendChild(div);
    +
    +  try {
    +    window.scroll(0, 0);
    +    var pos = goog.style.getClientPosition(div);
    +    assertEquals(2000, pos.x);
    +    assertEquals(2000, pos.y);
    +
    +    // The following tests do not work in Gecko 1.8 and below, due to an
    +    // obscure off-by-one bug in goog.style.getPageOffset.  Same for IE.
    +    if (!goog.userAgent.IE &&
    +        !(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
    +      window.scroll(1, 1);
    +      var pos = goog.style.getClientPosition(div);
    +      assertEquals(1999, pos.x);
    +      assertRoughlyEquals(1999, pos.y, 0.5);
    +
    +      window.scroll(2, 2);
    +      pos = goog.style.getClientPosition(div);
    +      assertEquals(1998, pos.x);
    +      assertRoughlyEquals(1998, pos.y, 0.5);
    +
    +      window.scroll(100, 100);
    +      pos = goog.style.getClientPosition(div);
    +      assertEquals(1900, pos.x);
    +      assertRoughlyEquals(1900, pos.y, 0.5);
    +    }
    +  }
    +  finally {
    +    window.scroll(0, 0);
    +    document.body.removeChild(div);
    +  }
    +}
    +
    +function testGetClientPositionOfOrphanElement() {
    +  var orphanElem = document.createElement('DIV');
    +  var pos = goog.style.getClientPosition(orphanElem);
    +  assertEquals(0, pos.x);
    +  assertEquals(0, pos.y);
    +}
    +
    +function testGetClientPositionEvent() {
    +  var mockEvent = {};
    +  mockEvent.clientX = 100;
    +  mockEvent.clientY = 200;
    +  var pos = goog.style.getClientPosition(mockEvent);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetClientPositionTouchEvent() {
    +  var mockTouchEvent = {};
    +
    +  mockTouchEvent.targetTouches = [{}];
    +  mockTouchEvent.targetTouches[0].clientX = 100;
    +  mockTouchEvent.targetTouches[0].clientY = 200;
    +
    +  mockTouchEvent.touches = [{}];
    +  mockTouchEvent.touches[0].clientX = 100;
    +  mockTouchEvent.touches[0].clientY = 200;
    +
    +  var pos = goog.style.getClientPosition(mockTouchEvent);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetClientPositionEmptyTouchList() {
    +  var mockTouchEvent = {};
    +
    +  mockTouchEvent.clientX = 100;
    +  mockTouchEvent.clientY = 200;
    +
    +  mockTouchEvent.targetTouches = [];
    +  mockTouchEvent.touches = [];
    +
    +  var pos = goog.style.getClientPosition(mockTouchEvent);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetClientPositionAbstractedTouchEvent() {
    +  var e = new goog.events.BrowserEvent();
    +  e.event_ = {};
    +  e.event_.touches = [{}];
    +  e.event_.touches[0].clientX = 100;
    +  e.event_.touches[0].clientY = 200;
    +  e.event_.targetTouches = [{}];
    +  e.event_.targetTouches[0].clientX = 100;
    +  e.event_.targetTouches[0].clientY = 200;
    +  var pos = goog.style.getClientPosition(e);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetPageOffsetAbsPositionedElement() {
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '100px';
    +  div.style.top = '200px';
    +  document.body.appendChild(div);
    +  var pos = goog.style.getPageOffset(div);
    +  assertEquals(100, pos.x);
    +  assertEquals(200, pos.y);
    +}
    +
    +function testGetPageOffsetNestedElements() {
    +  var innerDiv = goog.dom.createDom('DIV');
    +  innerDiv.style.position = 'relative';
    +  innerDiv.style.left = '-10px';
    +  innerDiv.style.top = '-10px';
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '150px';
    +  div.style.top = '250px';
    +  div.appendChild(innerDiv);
    +  document.body.appendChild(div);
    +  var pos = goog.style.getPageOffset(innerDiv);
    +  assertRoughlyEquals(140, pos.x, 0.1);
    +  assertRoughlyEquals(240, pos.y, 0.1);
    +}
    +
    +function testGetPageOffsetWithBodyPadding() {
    +  document.body.style.margin = '40px';
    +  document.body.style.padding = '60px';
    +  document.body.style.borderWidth = '70px';
    +  try {
    +    var div = goog.dom.createDom('DIV');
    +    div.style.position = 'absolute';
    +    div.style.left = '100px';
    +    div.style.top = '200px';
    +    // Margin will affect position, but padding and borders should not.
    +    div.style.margin = '1px';
    +    div.style.padding = '2px';
    +    div.style.borderWidth = '3px';
    +    document.body.appendChild(div);
    +    var pos = goog.style.getPageOffset(div);
    +    assertRoughlyEquals(101, pos.x, 0.1);
    +    assertRoughlyEquals(201, pos.y, 0.1);
    +  }
    +  finally {
    +    document.body.removeChild(div);
    +    document.body.style.margin = '';
    +    document.body.style.padding = '';
    +    document.body.style.borderWidth = '';
    +  }
    +}
    +
    +function testGetPageOffsetWithDocumentElementPadding() {
    +  document.documentElement.style.margin = '40px';
    +  document.documentElement.style.padding = '60px';
    +  document.documentElement.style.borderWidth = '70px';
    +  try {
    +    var div = goog.dom.createDom('DIV');
    +    div.style.position = 'absolute';
    +    div.style.left = '100px';
    +    div.style.top = '200px';
    +    // Margin will affect position, but padding and borders should not.
    +    div.style.margin = '1px';
    +    div.style.padding = '2px';
    +    div.style.borderWidth = '3px';
    +    document.body.appendChild(div);
    +    var pos = goog.style.getPageOffset(div);
    +    // FF3 (but not beyond) gets confused by document margins.
    +    if (goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9') &&
    +        !goog.userAgent.isVersionOrHigher('1.9.1')) {
    +      assertEquals(141, pos.x);
    +      assertEquals(241, pos.y);
    +    } else {
    +      assertRoughlyEquals(101, pos.x, 0.1);
    +      assertRoughlyEquals(201, pos.y, 0.1);
    +    }
    +  }
    +  finally {
    +    document.body.removeChild(div);
    +    document.documentElement.style.margin = '';
    +    document.documentElement.style.padding = '';
    +    document.documentElement.style.borderWidth = '';
    +  }
    +}
    +
    +function testGetPageOffsetElementOffscreen() {
    +  var div = goog.dom.createDom('DIV');
    +  div.style.position = 'absolute';
    +  div.style.left = '10000px';
    +  div.style.top = '20000px';
    +  document.body.appendChild(div);
    +  window.scroll(0, 0);
    +  try {
    +    var pos = goog.style.getPageOffset(div);
    +    assertEquals(10000, pos.x);
    +    assertEquals(20000, pos.y);
    +
    +    // The following tests do not work in Gecko 1.8 and below, due to an
    +    // obscure off-by-one bug in goog.style.getPageOffset.  Same for IE.
    +    if (!(goog.userAgent.IE) &&
    +        !(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
    +      window.scroll(1, 1);
    +      pos = goog.style.getPageOffset(div);
    +      assertEquals(10000, pos.x);
    +      assertRoughlyEquals(20000, pos.y, 0.5);
    +
    +      window.scroll(1000, 2000);
    +      pos = goog.style.getPageOffset(div);
    +      assertEquals(10000, pos.x);
    +      assertRoughlyEquals(20000, pos.y, 1);
    +
    +      window.scroll(10000, 20000);
    +      pos = goog.style.getPageOffset(div);
    +      assertEquals(10000, pos.x);
    +      assertRoughlyEquals(20000, pos.y, 1);
    +    }
    +  }
    +  // Undo changes.
    +  finally {
    +    document.body.removeChild(div);
    +    window.scroll(0, 0);
    +  }
    +}
    +
    +function testGetPageOffsetFixedPositionElements() {
    +  // Skip these tests in certain browsers.
    +  // position:fixed is not supported in IE before version 7
    +  if (!goog.userAgent.IE || !goog.userAgent.isVersionOrHigher('6')) {
    +    // Test with a position fixed element
    +    var div = goog.dom.createDom('DIV');
    +    div.style.position = 'fixed';
    +    div.style.top = '10px';
    +    div.style.left = '10px';
    +    document.body.appendChild(div);
    +    var pos = goog.style.getPageOffset(div);
    +    assertEquals(10, pos.x);
    +    assertEquals(10, pos.y);
    +
    +    // Test with a position fixed element as parent
    +    var innerDiv = goog.dom.createDom('DIV');
    +    div = goog.dom.createDom('DIV');
    +    div.style.position = 'fixed';
    +    div.style.top = '10px';
    +    div.style.left = '10px';
    +    div.style.padding = '5px';
    +    div.appendChild(innerDiv);
    +    document.body.appendChild(div);
    +    pos = goog.style.getPageOffset(innerDiv);
    +    assertEquals(15, pos.x);
    +    assertEquals(15, pos.y);
    +  }
    +}
    +
    +function testGetPositionTolerantToNoDocumentElementBorder() {
    +  // In IE, removing the border on the document element undoes the normal
    +  // 2-pixel offset.  Ensure that we're correctly compensating for both cases.
    +  try {
    +    document.documentElement.style.borderWidth = '0';
    +    var div = goog.dom.createDom('DIV');
    +    div.style.position = 'absolute';
    +    div.style.left = '100px';
    +    div.style.top = '200px';
    +    document.body.appendChild(div);
    +
    +    // Test all major positioning methods.
    +    // Disabled for IE8 and below - IE8 returns dimensions multiplied by 100.
    +    expectedFailures.expectFailureFor(
    +        goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9));
    +    try {
    +      // Test all major positioning methods.
    +      var pos = goog.style.getClientPosition(div);
    +      assertEquals(100, pos.x);
    +      assertRoughlyEquals(200, pos.y, .1);
    +      var offset = goog.style.getPageOffset(div);
    +      assertEquals(100, offset.x);
    +      assertRoughlyEquals(200, offset.y, .1);
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  } finally {
    +    document.documentElement.style.borderWidth = '';
    +  }
    +}
    +
    +function testSetSize() {
    +  var el = $('testEl');
    +
    +  goog.style.setSize(el, 100, 100);
    +  assertEquals('100px', el.style.width);
    +  assertEquals('100px', el.style.height);
    +
    +  goog.style.setSize(el, '50px', '25px');
    +  assertEquals('should be "50px"', '50px', el.style.width);
    +  assertEquals('should be "25px"', '25px', el.style.height);
    +
    +  goog.style.setSize(el, '10ex', '25px');
    +  assertEquals('10ex', el.style.width);
    +  assertEquals('25px', el.style.height);
    +
    +  goog.style.setSize(el, '10%', '25%');
    +  assertEquals('10%', el.style.width);
    +  assertEquals('25%', el.style.height);
    +
    +  // ignores stupid units
    +  goog.style.setSize(el, 0, 0);
    +  // TODO(user): IE errors if you set these values.  Should we make setStyle
    +  // catch these?  Or leave it up to the app.  Fixing the tests for now.
    +  //goog.style.setSize(el, '10rainbows', '25rainbows');
    +  assertEquals('0px', el.style.width);
    +  assertEquals('0px', el.style.height);
    +
    +  goog.style.setSize(el, new goog.math.Size(20, 40));
    +  assertEquals('20px', el.style.width);
    +  assertEquals('40px', el.style.height);
    +}
    +
    +function testSetWidthAndHeight() {
    +  var el = $('testEl');
    +
    +  // Replicate all of the setSize tests above.
    +
    +  goog.style.setWidth(el, 100);
    +  goog.style.setHeight(el, 100);
    +  assertEquals('100px', el.style.width);
    +  assertEquals('100px', el.style.height);
    +
    +  goog.style.setWidth(el, '50px');
    +  goog.style.setHeight(el, '25px');
    +  assertEquals('should be "50px"', '50px', el.style.width);
    +  assertEquals('should be "25px"', '25px', el.style.height);
    +
    +  goog.style.setWidth(el, '10ex');
    +  goog.style.setHeight(el, '25px');
    +  assertEquals('10ex', el.style.width);
    +  assertEquals('25px', el.style.height);
    +
    +  goog.style.setWidth(el, '10%');
    +  goog.style.setHeight(el, '25%');
    +  assertEquals('10%', el.style.width);
    +  assertEquals('25%', el.style.height);
    +
    +  goog.style.setWidth(el, 0);
    +  goog.style.setHeight(el, 0);
    +  assertEquals('0px', el.style.width);
    +  assertEquals('0px', el.style.height);
    +
    +  goog.style.setWidth(el, 20);
    +  goog.style.setHeight(el, 40);
    +  assertEquals('20px', el.style.width);
    +  assertEquals('40px', el.style.height);
    +
    +  // Additional tests testing each separately.
    +  goog.style.setWidth(el, '');
    +  goog.style.setHeight(el, '');
    +  assertEquals('', el.style.width);
    +  assertEquals('', el.style.height);
    +
    +  goog.style.setHeight(el, 20);
    +  assertEquals('', el.style.width);
    +  assertEquals('20px', el.style.height);
    +
    +  goog.style.setWidth(el, 40);
    +  assertEquals('40px', el.style.width);
    +  assertEquals('20px', el.style.height);
    +}
    +
    +function testGetSize() {
    +  var el = $('testEl');
    +  goog.style.setSize(el, 100, 100);
    +
    +  var dims = goog.style.getSize(el);
    +  assertEquals(100, dims.width);
    +  assertEquals(100, dims.height);
    +
    +  goog.style.setStyle(el, 'display', 'none');
    +  dims = goog.style.getSize(el);
    +  assertEquals(100, dims.width);
    +  assertEquals(100, dims.height);
    +
    +  el = $('testEl5');
    +  goog.style.setSize(el, 100, 100);
    +  dims = goog.style.getSize(el);
    +  assertEquals(100, dims.width);
    +  assertEquals(100, dims.height);
    +
    +  el = $('span0');
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +
    +  el = $('table1');
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +
    +  el = $('td1');
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +
    +  el = $('li1');
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +
    +  el = goog.dom.getElementsByTagNameAndClass('html')[0];
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +
    +  el = goog.dom.getElementsByTagNameAndClass('body')[0];
    +  dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.width);
    +  assertNotEquals(0, dims.height);
    +}
    +
    +function testGetSizeSvgElements() {
    +  var svgEl = document.createElementNS &&
    +      document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    +  if (!svgEl || svgEl.getAttribute('transform') == '' ||
    +      (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher(534.8))) {
    +    // SVG not supported, or getBoundingClientRect not supported on SVG
    +    // elements.
    +    return;
    +  }
    +
    +  document.body.appendChild(svgEl);
    +  var el = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
    +  el.setAttribute('x', 10);
    +  el.setAttribute('y', 10);
    +  el.setAttribute('width', 32);
    +  el.setAttribute('height', 21);
    +  el.setAttribute('fill', '#000');
    +
    +  svgEl.appendChild(el);
    +
    +  // The bounding size in 1 larger than the SVG element in IE.
    +  var expectedWidth = (goog.userAgent.IE) ? 33 : 32;
    +  var expectedHeight = (goog.userAgent.IE) ? 22 : 21;
    +
    +  var dims = goog.style.getSize(el);
    +  assertEquals(expectedWidth, dims.width);
    +  assertRoughlyEquals(expectedHeight, dims.height, 0.01);
    +
    +  dims = goog.style.getSize(svgEl);
    +  // The size of the <svg> will be the viewport size on all browsers. This used
    +  // to not be true for Firefox, but they fixed the glitch in Firefox 33.
    +  // https://bugzilla.mozilla.org/show_bug.cgi?id=530985
    +  assertTrue(dims.width >= expectedWidth);
    +  assertTrue(dims.height >= expectedHeight);
    +
    +  el.style.visibility = 'none';
    +
    +  dims = goog.style.getSize(el);
    +  assertEquals(expectedWidth, dims.width);
    +  assertRoughlyEquals(expectedHeight, dims.height, 0.01);
    +
    +  dims = goog.style.getSize(svgEl);
    +  assertTrue(dims.width >= expectedWidth);
    +  assertTrue(dims.height >= expectedHeight);
    +}
    +
    +function testGetSizeSvgDocument() {
    +  var svgEl = document.createElementNS &&
    +      document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    +  if (!svgEl || svgEl.getAttribute('transform') == '' ||
    +      (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher(534.8))) {
    +    // SVG not supported, or getBoundingClientRect not supported on SVG
    +    // elements.
    +    return;
    +  }
    +
    +  var frame = goog.dom.getElement('svg-frame');
    +  var doc = goog.dom.getFrameContentDocument(frame);
    +  var rect = doc.getElementById('rect');
    +  var dims = goog.style.getSize(rect);
    +  if (!goog.userAgent.IE) {
    +    assertEquals(50, dims.width);
    +    assertEquals(50, dims.height);
    +  } else {
    +    assertEquals(51, dims.width);
    +    assertEquals(51, dims.height);
    +  }
    +}
    +
    +function testGetSizeInlineBlock() {
    +  var el = $('height-test-inner');
    +  var dims = goog.style.getSize(el);
    +  assertNotEquals(0, dims.height);
    +}
    +
    +function testGetSizeTransformedRotated() {
    +  if (!hasWebkitTransform()) return;
    +
    +  var el = $('rotated');
    +  goog.style.setSize(el, 300, 200);
    +
    +  var noRotateDims = goog.style.getTransformedSize(el);
    +  assertEquals(300, noRotateDims.width);
    +  assertEquals(200, noRotateDims.height);
    +
    +  el.style.webkitTransform = 'rotate(180deg)';
    +  var rotate180Dims = goog.style.getTransformedSize(el);
    +  assertEquals(300, rotate180Dims.width);
    +  assertEquals(200, rotate180Dims.height);
    +
    +  el.style.webkitTransform = 'rotate(90deg)';
    +  var rotate90ClockwiseDims = goog.style.getTransformedSize(el);
    +  assertEquals(200, rotate90ClockwiseDims.width);
    +  assertEquals(300, rotate90ClockwiseDims.height);
    +
    +  el.style.webkitTransform = 'rotate(-90deg)';
    +  var rotate90CounterClockwiseDims = goog.style.getTransformedSize(el);
    +  assertEquals(200, rotate90CounterClockwiseDims.width);
    +  assertEquals(300, rotate90CounterClockwiseDims.height);
    +}
    +
    +function testGetSizeTransformedScaled() {
    +  if (!hasWebkitTransform()) return;
    +
    +  var el = $('scaled');
    +  goog.style.setSize(el, 300, 200);
    +
    +  var noScaleDims = goog.style.getTransformedSize(el);
    +  assertEquals(300, noScaleDims.width);
    +  assertEquals(200, noScaleDims.height);
    +
    +  el.style.webkitTransform = 'scale(2, 0.5)';
    +  var scaledDims = goog.style.getTransformedSize(el);
    +  assertEquals(600, scaledDims.width);
    +  assertEquals(100, scaledDims.height);
    +}
    +
    +function hasWebkitTransform() {
    +  return 'webkitTransform' in document.body.style;
    +}
    +
    +function testGetSizeOfOrphanElement() {
    +  var orphanElem = document.createElement('DIV');
    +  var size = goog.style.getSize(orphanElem);
    +  assertEquals(0, size.width);
    +  assertEquals(0, size.height);
    +}
    +
    +function testGetBounds() {
    +  var el = $('testEl');
    +
    +  var dims = goog.style.getSize(el);
    +  var pos = goog.style.getPageOffset(el);
    +
    +  var rect = goog.style.getBounds(el);
    +
    +  // Relies on getSize and getPageOffset being correct.
    +  assertEquals(dims.width, rect.width);
    +  assertEquals(dims.height, rect.height);
    +  assertEquals(pos.x, rect.left);
    +  assertEquals(pos.y, rect.top);
    +}
    +
    +function testInstallStyles() {
    +  var el = $('installTest0');
    +  var originalBackground = goog.style.getBackgroundColor(el);
    +
    +  // Uses background-color because it's easy to get the computed value
    +  var result = goog.style.installStyles(
    +      '#installTest0 { background-color: rgb(255, 192, 203); }');
    +
    +  // For some odd reason, the change in computed style does not register on
    +  // Chrome 19 unless the style property is touched.  The behavior goes
    +  // away again in Chrome 20.
    +  // TODO(nnaze): Remove special caseing once we switch the testing image
    +  // to Chrome 20 or higher.
    +  if (isChrome19()) {
    +    el.style.display = '';
    +  }
    +
    +  assertColorRgbEquals('rgb(255,192,203)', goog.style.getBackgroundColor(el));
    +
    +  goog.style.uninstallStyles(result);
    +  assertEquals(originalBackground, goog.style.getBackgroundColor(el));
    +}
    +
    +function testSetStyles() {
    +  var el = $('installTest1');
    +
    +  // Change to pink
    +  var ss = goog.style.installStyles(
    +      '#installTest1 { background-color: rgb(255, 192, 203); }');
    +
    +  // For some odd reason, the change in computed style does not register on
    +  // Chrome 19 unless the style property is touched.  The behavior goes
    +  // away again in Chrome 20.
    +  // TODO(nnaze): Remove special caseing once we switch the testing image
    +  // to Chrome 20 or higher.
    +  if (isChrome19()) {
    +    el.style.display = '';
    +  }
    +
    +  assertColorRgbEquals('rgb(255,192,203)', goog.style.getBackgroundColor(el));
    +
    +  // Now change to orange
    +  goog.style.setStyles(ss,
    +      '#installTest1 { background-color: rgb(255, 255, 0); }');
    +  assertColorRgbEquals('rgb(255,255,0)', goog.style.getBackgroundColor(el));
    +}
    +
    +function assertColorRgbEquals(expected, actual) {
    +  assertEquals(expected,
    +      goog.color.hexToRgbStyle(goog.color.parse(actual).hex));
    +}
    +
    +function isChrome19() {
    +  return goog.userAgent.product.CHROME &&
    +         goog.string.startsWith(goog.userAgent.product.VERSION, '19.');
    +}
    +
    +function testIsRightToLeft() {
    +  assertFalse(goog.style.isRightToLeft($('rtl1')));
    +  assertTrue(goog.style.isRightToLeft($('rtl2')));
    +  assertFalse(goog.style.isRightToLeft($('rtl3')));
    +  assertFalse(goog.style.isRightToLeft($('rtl4')));
    +  assertTrue(goog.style.isRightToLeft($('rtl5')));
    +  assertFalse(goog.style.isRightToLeft($('rtl6')));
    +  assertTrue(goog.style.isRightToLeft($('rtl7')));
    +  assertFalse(goog.style.isRightToLeft($('rtl8')));
    +  assertTrue(goog.style.isRightToLeft($('rtl9')));
    +  assertFalse(goog.style.isRightToLeft($('rtl10')));
    +}
    +
    +function testPosWithAbsoluteAndScroll() {
    +  var el = $('pos-scroll-abs');
    +  var el1 = $('pos-scroll-abs-1');
    +  var el2 = $('pos-scroll-abs-2');
    +
    +  el1.scrollTop = 200;
    +  var pos = goog.style.getPageOffset(el2);
    +
    +  assertEquals(200, pos.x);
    +  // Don't bother with IE in quirks mode
    +  if (!goog.userAgent.IE || document.compatMode == 'CSS1Compat') {
    +    assertRoughlyEquals(300, pos.y, .1);
    +  }
    +}
    +
    +function testPosWithAbsoluteAndWindowScroll() {
    +  window.scrollBy(0, 200);
    +  var el = $('abs-upper-left');
    +  var pos = goog.style.getPageOffset(el);
    +  assertRoughlyEquals('Top should be about 0', 0, pos.y, 0.1);
    +}
    +
    +function testGetBorderBoxSize() {
    +  // Strict mode
    +  var getBorderBoxSize = goog.style.getBorderBoxSize;
    +
    +  var el = $('size-a');
    +  var rect = getBorderBoxSize(el);
    +  assertEquals('width:100px', 100, rect.width);
    +  assertEquals('height:100px', 100, rect.height);
    +
    +  // with border: 10px
    +  el = $('size-b');
    +  rect = getBorderBoxSize(el);
    +  assertEquals('width:100px;border:10px', isBorderBox ? 100 : 120, rect.width);
    +  assertEquals('height:100px;border:10px', isBorderBox ? 100 : 120,
    +               rect.height);
    +
    +  // with border: 10px; padding: 10px
    +  el = $('size-c');
    +  rect = getBorderBoxSize(el);
    +  assertEquals('width:100px;border:10px;padding:10px',
    +               isBorderBox ? 100 : 140, rect.width);
    +  assertEquals('height:100px;border:10px;padding:10px',
    +               isBorderBox ? 100 : 140, rect.height);
    +
    +  // size, padding and borders are all in non pixel units
    +  // all we test here is that we get a number out
    +  el = $('size-d');
    +  rect = getBorderBoxSize(el);
    +  assertEquals('number', typeof rect.width);
    +  assertEquals('number', typeof rect.height);
    +  assertFalse(isNaN(rect.width));
    +  assertFalse(isNaN(rect.height));
    +}
    +
    +function testGetContentBoxSize() {
    +  // Strict mode
    +  var getContentBoxSize = goog.style.getContentBoxSize;
    +
    +  var el = $('size-a');
    +  var rect = getContentBoxSize(el);
    +  assertEquals('width:100px', 100, rect.width);
    +  assertEquals('height:100px', 100, rect.height);
    +
    +  // with border: 10px
    +  el = $('size-b');
    +  rect = getContentBoxSize(el);
    +  assertEquals('width:100px;border:10px',
    +               isBorderBox ? 80 : 100, rect.width);
    +  assertEquals('height:100px;border:10px',
    +               isBorderBox ? 80 : 100, rect.height);
    +
    +  // with border: 10px; padding: 10px
    +  el = $('size-c');
    +  rect = getContentBoxSize(el);
    +  assertEquals('width:100px;border:10px;padding:10px',
    +               isBorderBox ? 60 : 100, rect.width);
    +  assertEquals('height:100px;border:10px;padding:10px',
    +               isBorderBox ? 60 : 100, rect.height);
    +
    +  // size, padding and borders are all in non pixel units
    +  // all we test here is that we get a number out
    +  el = $('size-d');
    +  rect = getContentBoxSize(el);
    +  assertEquals('number', typeof rect.width);
    +  assertEquals('number', typeof rect.height);
    +  assertFalse(isNaN(rect.width));
    +  assertFalse(isNaN(rect.height));
    +
    +  // test whether getContentBoxSize works when width and height
    +  // aren't explicitly set, but the default of 'auto'.
    +  // 'size-f' has no margin, border, or padding, so offsetWidth/Height
    +  // should match the content box size
    +  el = $('size-f');
    +  rect = getContentBoxSize(el);
    +  assertEquals(el.offsetWidth, rect.width);
    +  assertEquals(el.offsetHeight, rect.height);
    +}
    +
    +function testSetBorderBoxSize() {
    +  // Strict mode
    +  var el = $('size-e');
    +  var Size = goog.math.Size;
    +  var setBorderBoxSize = goog.style.setBorderBoxSize;
    +
    +  // Clean up
    +  // style element has 100x100, no border and no padding
    +  el.style.padding = '';
    +  el.style.margin = '';
    +  el.style.borderWidth = '';
    +  el.style.width = '';
    +  el.style.height = '';
    +
    +  setBorderBoxSize(el, new Size(100, 100));
    +
    +  assertEquals(100, el.offsetWidth);
    +  assertEquals(100, el.offsetHeight);
    +
    +  el.style.borderWidth = '10px';
    +  setBorderBoxSize(el, new Size(100, 100));
    +
    +  assertEquals('width:100px;border:10px', 100, el.offsetWidth);
    +  assertEquals('height:100px;border:10px', 100, el.offsetHeight);
    +
    +  el.style.padding = '10px';
    +  setBorderBoxSize(el, new Size(100, 100));
    +  assertEquals(100, el.offsetWidth);
    +  assertEquals(100, el.offsetHeight);
    +
    +  el.style.borderWidth = '0';
    +  setBorderBoxSize(el, new Size(100, 100));
    +  assertEquals(100, el.offsetWidth);
    +  assertEquals(100, el.offsetHeight);
    +
    +  if (goog.userAgent.GECKO) {
    +    assertEquals('border-box', el.style.MozBoxSizing);
    +  } else if (goog.userAgent.WEBKIT) {
    +    assertEquals('border-box', el.style.WebkitBoxSizing);
    +  } else if (goog.userAgent.OPERA ||
    +      goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(8)) {
    +    assertEquals('border-box', el.style.boxSizing);
    +  }
    +
    +  // Try a negative width/height.
    +  setBorderBoxSize(el, new Size(-10, -10));
    +
    +  // Setting the border box smaller than the borders will just give you
    +  // a content box of size 0.
    +  // NOTE(nicksantos): I'm not really sure why IE7 is special here.
    +  var isIeLt8Quirks = goog.userAgent.IE &&
    +      !goog.userAgent.isDocumentModeOrHigher(8) &&
    +      !goog.dom.isCss1CompatMode();
    +  assertEquals(20, el.offsetWidth);
    +  assertEquals(isIeLt8Quirks ? 39 : 20, el.offsetHeight);
    +}
    +
    +function testSetContentBoxSize() {
    +  // Strict mode
    +  var el = $('size-e');
    +  var Size = goog.math.Size;
    +  var setContentBoxSize = goog.style.setContentBoxSize;
    +
    +  // Clean up
    +  // style element has 100x100, no border and no padding
    +  el.style.padding = '';
    +  el.style.margin = '';
    +  el.style.borderWidth = '';
    +  el.style.width = '';
    +  el.style.height = '';
    +
    +  setContentBoxSize(el, new Size(100, 100));
    +
    +  assertEquals(100, el.offsetWidth);
    +  assertEquals(100, el.offsetHeight);
    +
    +  el.style.borderWidth = '10px';
    +  setContentBoxSize(el, new Size(100, 100));
    +  assertEquals('width:100px;border-width:10px', 120, el.offsetWidth);
    +  assertEquals('height:100px;border-width:10px', 120, el.offsetHeight);
    +
    +  el.style.padding = '10px';
    +  setContentBoxSize(el, new Size(100, 100));
    +  assertEquals('width:100px;border-width:10px;padding:10px',
    +               140, el.offsetWidth);
    +  assertEquals('height:100px;border-width:10px;padding:10px',
    +               140, el.offsetHeight);
    +
    +  el.style.borderWidth = '0';
    +  setContentBoxSize(el, new Size(100, 100));
    +  assertEquals('width:100px;padding:10px', 120, el.offsetWidth);
    +  assertEquals('height:100px;padding:10px', 120, el.offsetHeight);
    +
    +  if (goog.userAgent.GECKO) {
    +    assertEquals('content-box', el.style.MozBoxSizing);
    +  } else if (goog.userAgent.WEBKIT) {
    +    assertEquals('content-box', el.style.WebkitBoxSizing);
    +  } else if (goog.userAgent.OPERA ||
    +      goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(8)) {
    +    assertEquals('content-box', el.style.boxSizing);
    +  }
    +
    +  // Try a negative width/height.
    +  setContentBoxSize(el, new Size(-10, -10));
    +
    +  // NOTE(nicksantos): I'm not really sure why IE7 is special here.
    +  var isIeLt8Quirks = goog.userAgent.IE &&
    +      !goog.userAgent.isDocumentModeOrHigher('8') &&
    +      !goog.dom.isCss1CompatMode();
    +  assertEquals(20, el.offsetWidth);
    +  assertEquals(isIeLt8Quirks ? 39 : 20, el.offsetHeight);
    +}
    +
    +function testGetPaddingBox() {
    +  // Strict mode
    +  var el = $('size-e');
    +  var Size = goog.math.Size;
    +  var getPaddingBox = goog.style.getPaddingBox;
    +
    +  // Clean up
    +  // style element has 100x100, no border and no padding
    +  el.style.padding = '';
    +  el.style.margin = '';
    +  el.style.borderWidth = '';
    +  el.style.width = '';
    +  el.style.height = '';
    +
    +  el.style.padding = '10px';
    +  var rect = getPaddingBox(el);
    +  assertEquals(10, rect.left);
    +  assertEquals(10, rect.right);
    +  assertEquals(10, rect.top);
    +  assertEquals(10, rect.bottom);
    +
    +  el.style.padding = '0';
    +  rect = getPaddingBox(el);
    +  assertEquals(0, rect.left);
    +  assertEquals(0, rect.right);
    +  assertEquals(0, rect.top);
    +  assertEquals(0, rect.bottom);
    +
    +  el.style.padding = '1px 2px 3px 4px';
    +  rect = getPaddingBox(el);
    +  assertEquals(1, rect.top);
    +  assertEquals(2, rect.right);
    +  assertEquals(3, rect.bottom);
    +  assertEquals(4, rect.left);
    +
    +  el.style.padding = '1mm 2em 3ex 4%';
    +  rect = getPaddingBox(el);
    +  assertFalse(isNaN(rect.top));
    +  assertFalse(isNaN(rect.right));
    +  assertFalse(isNaN(rect.bottom));
    +  assertFalse(isNaN(rect.left));
    +  assertTrue(rect.top >= 0);
    +  assertTrue(rect.right >= 0);
    +  assertTrue(rect.bottom >= 0);
    +  assertTrue(rect.left >= 0);
    +}
    +
    +function testGetPaddingBoxUnattached() {
    +  var el = document.createElement('div');
    +  var box = goog.style.getPaddingBox(el);
    +  if (goog.userAgent.WEBKIT) {
    +    assertTrue(isNaN(box.top));
    +    assertTrue(isNaN(box.right));
    +    assertTrue(isNaN(box.bottom));
    +    assertTrue(isNaN(box.left));
    +  } else {
    +    assertObjectEquals(new goog.math.Box(0, 0, 0, 0), box);
    +  }
    +}
    +
    +function testGetMarginBox() {
    +  // Strict mode
    +  var el = $('size-e');
    +  var Size = goog.math.Size;
    +  var getMarginBox = goog.style.getMarginBox;
    +
    +  // Clean up
    +  // style element has 100x100, no border and no padding
    +  el.style.padding = '';
    +  el.style.margin = '';
    +  el.style.borderWidth = '';
    +  el.style.width = '';
    +  el.style.height = '';
    +
    +  el.style.margin = '10px';
    +  var rect = getMarginBox(el);
    +  assertEquals(10, rect.left);
    +  // In webkit the right margin is the calculated distance from right edge and
    +  // not the computed right margin so it is not reliable.
    +  // See https://bugs.webkit.org/show_bug.cgi?id=19828
    +  if (!goog.userAgent.WEBKIT) {
    +    assertEquals(10, rect.right);
    +  }
    +  assertEquals(10, rect.top);
    +  assertEquals(10, rect.bottom);
    +
    +  el.style.margin = '0';
    +  rect = getMarginBox(el);
    +  assertEquals(0, rect.left);
    +  // In webkit the right margin is the calculated distance from right edge and
    +  // not the computed right margin so it is not reliable.
    +  // See https://bugs.webkit.org/show_bug.cgi?id=19828
    +  if (!goog.userAgent.WEBKIT) {
    +    assertEquals(0, rect.right);
    +  }
    +  assertEquals(0, rect.top);
    +  assertEquals(0, rect.bottom);
    +
    +  el.style.margin = '1px 2px 3px 4px';
    +  rect = getMarginBox(el);
    +  assertEquals(1, rect.top);
    +  // In webkit the right margin is the calculated distance from right edge and
    +  // not the computed right margin so it is not reliable.
    +  // See https://bugs.webkit.org/show_bug.cgi?id=19828
    +  if (!goog.userAgent.WEBKIT) {
    +    assertEquals(2, rect.right);
    +  }
    +  assertEquals(3, rect.bottom);
    +  assertEquals(4, rect.left);
    +
    +  el.style.margin = '1mm 2em 3ex 4%';
    +  rect = getMarginBox(el);
    +  assertFalse(isNaN(rect.top));
    +  assertFalse(isNaN(rect.right));
    +  assertFalse(isNaN(rect.bottom));
    +  assertFalse(isNaN(rect.left));
    +  assertTrue(rect.top >= 0);
    +  // In webkit the right margin is the calculated distance from right edge and
    +  // not the computed right margin so it is not reliable.
    +  // See https://bugs.webkit.org/show_bug.cgi?id=19828
    +  if (!goog.userAgent.WEBKIT) {
    +    assertTrue(rect.right >= 0);
    +  }
    +  assertTrue(rect.bottom >= 0);
    +  assertTrue(rect.left >= 0);
    +}
    +
    +function testGetBorderBox() {
    +  // Strict mode
    +  var el = $('size-e');
    +  var Size = goog.math.Size;
    +  var getBorderBox = goog.style.getBorderBox;
    +
    +  // Clean up
    +  // style element has 100x100, no border and no padding
    +  el.style.padding = '';
    +  el.style.margin = '';
    +  el.style.borderWidth = '';
    +  el.style.width = '';
    +  el.style.height = '';
    +
    +  el.style.borderWidth = '10px';
    +  var rect = getBorderBox(el);
    +  assertEquals(10, rect.left);
    +  assertEquals(10, rect.right);
    +  assertEquals(10, rect.top);
    +  assertEquals(10, rect.bottom);
    +
    +  el.style.borderWidth = '0';
    +  rect = getBorderBox(el);
    +  assertEquals(0, rect.left);
    +  assertEquals(0, rect.right);
    +  assertEquals(0, rect.top);
    +  assertEquals(0, rect.bottom);
    +
    +  el.style.borderWidth = '1px 2px 3px 4px';
    +  rect = getBorderBox(el);
    +  assertEquals(1, rect.top);
    +  assertEquals(2, rect.right);
    +  assertEquals(3, rect.bottom);
    +  assertEquals(4, rect.left);
    +
    +  // % does not work for border widths in IE
    +  el.style.borderWidth = '1mm 2em 3ex 4pt';
    +  rect = getBorderBox(el);
    +  assertFalse(isNaN(rect.top));
    +  assertFalse(isNaN(rect.right));
    +  assertFalse(isNaN(rect.bottom));
    +  assertFalse(isNaN(rect.left));
    +  assertTrue(rect.top >= 0);
    +  assertTrue(rect.right >= 0);
    +  assertTrue(rect.bottom >= 0);
    +  assertTrue(rect.left >= 0);
    +
    +  el.style.borderWidth = 'thin medium thick 1px';
    +  rect = getBorderBox(el);
    +  assertFalse(isNaN(rect.top));
    +  assertFalse(isNaN(rect.right));
    +  assertFalse(isNaN(rect.bottom));
    +  assertFalse(isNaN(rect.left));
    +  assertTrue(rect.top >= 0);
    +  assertTrue(rect.right >= 0);
    +  assertTrue(rect.bottom >= 0);
    +  assertTrue(rect.left >= 0);
    +}
    +
    +function testGetFontFamily() {
    +  // I tried to use common fonts for these tests. It's possible the test fails
    +  // because the testing platform doesn't have one of these fonts installed:
    +  //   Comic Sans MS or Century Schoolbook L
    +  //   Times
    +  //   Helvetica
    +
    +  var tmpFont = goog.style.getFontFamily($('font-tag'));
    +  assertTrue('FontFamily should be detectable when set via <font face>',
    +             'Times' == tmpFont || 'Times New Roman' == tmpFont);
    +  tmpFont = goog.style.getFontFamily($('small-text'));
    +  assertTrue('Multiword fonts should be reported with quotes stripped.',
    +             'Comic Sans MS' == tmpFont ||
    +                 'Century Schoolbook L' == tmpFont);
    +  // Firefox fails this test & retuns a generic 'monospace' instead of the
    +  // actually displayed font (e.g., "Times New").
    +  //tmpFont = goog.style.getFontFamily($('pre-font'));
    +  //assertEquals('<pre> tags should use a fixed-width font',
    +  //             'Times New',
    +  //             tmpFont);
    +  tmpFont = goog.style.getFontFamily($('inherit-font'));
    +  assertEquals('Explicitly inherited fonts should be detectable',
    +               'Helvetica',
    +               tmpFont);
    +  tmpFont = goog.style.getFontFamily($('times-font-family'));
    +  assertEquals('Font-family set via style attribute should be detected',
    +               'Times',
    +               tmpFont);
    +  tmpFont = goog.style.getFontFamily($('bold-font'));
    +  assertEquals('Implicitly inherited font should be detected',
    +               'Helvetica',
    +               tmpFont);
    +  tmpFont = goog.style.getFontFamily($('css-html-tag-redefinition'));
    +  assertEquals('HTML tag CSS rewrites should be detected',
    +               'Times',
    +               tmpFont);
    +  tmpFont = goog.style.getFontFamily($('no-text-font-styles'));
    +  assertEquals('Font family should exist even with no text',
    +               'Helvetica',
    +               tmpFont);
    +  tmpFont = goog.style.getFontFamily($('icon-font'));
    +  assertNotEquals('icon is a special font-family value',
    +                  'icon',
    +                  tmpFont.toLowerCase());
    +  tmpFont = goog.style.getFontFamily($('font-style-badfont'));
    +  // Firefox fails this test and reports the specified "badFont", which is
    +  // obviously not displayed.
    +  //assertEquals('Invalid fonts should not be returned',
    +  //             'Helvetica',
    +  //             tmpFont);
    +  tmpFont = goog.style.getFontFamily($('img-font-test'));
    +  assertTrue('Even img tags should inherit the document body\'s font',
    +             tmpFont != '');
    +  tmpFont = goog.style.getFontFamily($('nested-font'));
    +  assertEquals('An element with nested content should be unaffected.',
    +               'Arial',
    +               tmpFont);
    +  // IE raises an 'Invalid Argument' error when using the moveToElementText
    +  // method from the TextRange object with an element that is not attached to
    +  // a document.
    +  var element = goog.dom.createDom('span',
    +      {style: 'font-family:Times,sans-serif;'}, 'some text');
    +  tmpFont = goog.style.getFontFamily(element);
    +  assertEquals('Font should be correctly retrieved for element not attached' +
    +               ' to a document',
    +               'Times',
    +               tmpFont);
    +}
    +
    +function testGetFontSize() {
    +  assertEquals('Font size should be determined even without any text',
    +               30,
    +               goog.style.getFontSize($('no-text-font-styles')));
    +  assertEquals('A 5em font should be 5x larger than its parent.',
    +               150,
    +               goog.style.getFontSize($('css-html-tag-redefinition')));
    +  assertTrue('Setting font size=-1 should result in a positive font size.',
    +             goog.style.getFontSize($('font-tag')) > 0);
    +  assertEquals('Inheriting a 50% font-size should have no additional effect',
    +               goog.style.getFontSize($('font-style-badfont')),
    +               goog.style.getFontSize($('inherit-50pct-font')));
    +  assertTrue('In pretty much any display, 3in should be > 8px',
    +             goog.style.getFontSize($('times-font-family')) >
    +                 goog.style.getFontSize($('no-text-font-styles')));
    +  assertTrue('With no applied styles, font-size should still be defined.',
    +             goog.style.getFontSize($('no-font-style')) > 0);
    +  assertEquals('50% of 30px is 15',
    +               15,
    +               goog.style.getFontSize($('font-style-badfont')));
    +  assertTrue('x-small text should be smaller than small text',
    +             goog.style.getFontSize($('x-small-text')) <
    +                 goog.style.getFontSize($('small-text')));
    +  // IE fails this test, the decimal portion of px lengths isn't reported
    +  // by getCascadedStyle. Firefox passes, but only because it ignores the
    +  // decimals altogether.
    +  //assertEquals('12.5px should be the same as 0.5em nested in a 25px node.',
    +  //             goog.style.getFontSize($('font-size-12-point-5-px')),
    +  //             goog.style.getFontSize($('font-size-50-pct-of-25-px')));
    +
    +  assertEquals('Font size should not doubly count em values',
    +      2, goog.style.getFontSize($('em-font-size')));
    +}
    +
    +function testGetLengthUnits() {
    +  assertEquals('px', goog.style.getLengthUnits('15px'));
    +  assertEquals('%', goog.style.getLengthUnits('99%'));
    +  assertNull(goog.style.getLengthUnits(''));
    +}
    +
    +function testParseStyleAttribute() {
    +  var css = 'left: 0px; text-align: center';
    +  var expected = {'left': '0px', 'textAlign': 'center'};
    +
    +  assertObjectEquals(expected, goog.style.parseStyleAttribute(css));
    +}
    +
    +function testToStyleAttribute() {
    +  var object = {'left': '0px', 'textAlign': 'center'};
    +  var expected = 'left:0px;text-align:center;';
    +
    +  assertEquals(expected, goog.style.toStyleAttribute(object));
    +}
    +
    +function testStyleAttributePassthrough() {
    +  var object = {'left': '0px', 'textAlign': 'center'};
    +
    +  assertObjectEquals(object,
    +      goog.style.parseStyleAttribute(goog.style.toStyleAttribute(object)));
    +}
    +
    +function testGetFloat() {
    +  assertEquals('', goog.style.getFloat($('no-float')));
    +  assertEquals('none', goog.style.getFloat($('float-none')));
    +  assertEquals('left', goog.style.getFloat($('float-left')));
    +}
    +
    +function testSetFloat() {
    +  var el = $('float-test');
    +
    +  goog.style.setFloat(el, 'left');
    +  assertEquals('left', goog.style.getFloat(el));
    +
    +  goog.style.setFloat(el, 'right');
    +  assertEquals('right', goog.style.getFloat(el));
    +
    +  goog.style.setFloat(el, 'none');
    +  assertEquals('none', goog.style.getFloat(el));
    +
    +  goog.style.setFloat(el, '');
    +  assertEquals('', goog.style.getFloat(el));
    +}
    +
    +function testIsElementShown() {
    +  var el = $('testEl');
    +
    +  goog.style.setElementShown(el, false);
    +  assertFalse(goog.style.isElementShown(el));
    +
    +  goog.style.setElementShown(el, true);
    +  assertTrue(goog.style.isElementShown(el));
    +}
    +
    +function testGetOpacity() {
    +  var el1 = {
    +    style: {
    +      opacity: '0.3'
    +    }
    +  };
    +
    +  var el2 = {
    +    style: {
    +      MozOpacity: '0.1'
    +    }
    +  };
    +
    +  var el3 = {
    +    style: {
    +      filter: 'some:other,filter;alpha(opacity=25.5);alpha(more=100);'
    +    }
    +  };
    +
    +  assertEquals(0.3, goog.style.getOpacity(el1));
    +  assertEquals(0.1, goog.style.getOpacity(el2));
    +  assertEquals(0.255, goog.style.getOpacity(el3));
    +
    +  el1.style.opacity = '0';
    +  el2.style.MozOpacity = '0';
    +  el3.style.filter = 'some:other,filter;alpha(opacity=0);alpha(more=100);';
    +
    +  assertEquals(0, goog.style.getOpacity(el1));
    +  assertEquals(0, goog.style.getOpacity(el2));
    +  assertEquals(0, goog.style.getOpacity(el3));
    +
    +  el1.style.opacity = '';
    +  el2.style.MozOpacity = '';
    +  el3.style.filter = '';
    +
    +  assertEquals('', goog.style.getOpacity(el1));
    +  assertEquals('', goog.style.getOpacity(el2));
    +  assertEquals('', goog.style.getOpacity(el3));
    +
    +  var el4 = {
    +    style: {}
    +  };
    +
    +  assertEquals('', goog.style.getOpacity(el4));
    +  assertEquals('', goog.style.getOpacity($('test-opacity')));
    +}
    +
    +function testSetOpacity() {
    +  var el1 = {
    +    style: {
    +      opacity: '0.3'
    +    }
    +  };
    +  goog.style.setOpacity(el1, 0.8);
    +
    +  var el2 = {
    +    style: {
    +      MozOpacity: '0.1'
    +    }
    +  };
    +  goog.style.setOpacity(el2, 0.5);
    +
    +  var el3 = {
    +    style: {
    +      filter: 'alpha(opacity=25)'
    +    }
    +  };
    +  goog.style.setOpacity(el3, 0.1);
    +
    +  assertEquals(0.8, Number(el1.style.opacity));
    +  assertEquals(0.5, Number(el2.style.MozOpacity));
    +  assertEquals('alpha(opacity=10)', el3.style.filter);
    +
    +  goog.style.setOpacity(el1, 0);
    +  goog.style.setOpacity(el2, 0);
    +  goog.style.setOpacity(el3, 0);
    +
    +  assertEquals(0, Number(el1.style.opacity));
    +  assertEquals(0, Number(el2.style.MozOpacity));
    +  assertEquals('alpha(opacity=0)', el3.style.filter);
    +
    +  goog.style.setOpacity(el1, '');
    +  goog.style.setOpacity(el2, '');
    +  goog.style.setOpacity(el3, '');
    +
    +  assertEquals('', el1.style.opacity);
    +  assertEquals('', el2.style.MozOpacity);
    +  assertEquals('', el3.style.filter);
    +}
    +
    +function testFramedPageOffset() {
    +  // Set up a complicated iframe ancestor chain.
    +  var iframe = goog.dom.getElement('test-frame-offset');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  var iframeWindow = goog.dom.getWindow(iframeDoc);
    +
    +  var iframePos = 'style="display:block;position:absolute;' +
    +      'top:50px;left:50px;width:50px;height:50px;"';
    +  iframeDoc.write('<iframe id="test-frame-offset-2" ' +
    +      iframePos + '></iframe>' +
    +      '<div id="test-element-2" ' +
    +      ' style="position:absolute;left:300px;top:300px">hi mom!</div>');
    +  iframeDoc.close();
    +  var iframe2 = iframeDoc.getElementById('test-frame-offset-2');
    +  var testElement2 = iframeDoc.getElementById('test-element-2');
    +  var iframeDoc2 = goog.dom.getFrameContentDocument(iframe2);
    +  var iframeWindow2 = goog.dom.getWindow(iframeDoc2);
    +
    +  iframeDoc2.write(
    +      '<div id="test-element-3" ' +
    +      ' style="position:absolute;left:500px;top:500px">hi mom!</div>');
    +  iframeDoc2.close();
    +  var testElement3 = iframeDoc2.getElementById('test-element-3');
    +
    +  assertCoordinateApprox(300, 300, 0,
    +      goog.style.getPageOffset(testElement2));
    +  assertCoordinateApprox(500, 500, 0,
    +      goog.style.getPageOffset(testElement3));
    +
    +  assertCoordinateApprox(350, 350, 0,
    +      goog.style.getFramedPageOffset(testElement2, window));
    +  assertCoordinateApprox(300, 300, 0,
    +      goog.style.getFramedPageOffset(testElement2, iframeWindow));
    +
    +  assertCoordinateApprox(600, 600, 0,
    +      goog.style.getFramedPageOffset(testElement3, window));
    +  assertCoordinateApprox(550, 550, 0,
    +      goog.style.getFramedPageOffset(testElement3, iframeWindow));
    +  assertCoordinateApprox(500, 500, 0,
    +      goog.style.getFramedPageOffset(testElement3, iframeWindow2));
    +
    +  // Scroll the iframes a bit.
    +  window.scrollBy(0, 5);
    +  iframeWindow.scrollBy(0, 11);
    +  iframeWindow2.scrollBy(0, 18);
    +
    +  // On Firefox 2, scrolling inner iframes causes off by one errors
    +  // in the page position, because we're using screen coords to compute them.
    +  assertCoordinateApprox(300, 300, 2,
    +      goog.style.getPageOffset(testElement2));
    +  assertCoordinateApprox(500, 500, 2,
    +      goog.style.getPageOffset(testElement3));
    +
    +  assertCoordinateApprox(350, 350 - 11, 2,
    +      goog.style.getFramedPageOffset(testElement2, window));
    +  assertCoordinateApprox(300, 300, 2,
    +      goog.style.getFramedPageOffset(testElement2, iframeWindow));
    +
    +  assertCoordinateApprox(600, 600 - 18 - 11, 2,
    +      goog.style.getFramedPageOffset(testElement3, window));
    +  assertCoordinateApprox(550, 550 - 18, 2,
    +      goog.style.getFramedPageOffset(testElement3, iframeWindow));
    +  assertCoordinateApprox(500, 500, 2,
    +      goog.style.getFramedPageOffset(testElement3, iframeWindow2));
    +
    +  // In IE, if the element is in a frame that's been removed from the DOM and
    +  // relativeWin is not that frame's contentWindow, the contentWindow's parent
    +  // reference points to itself. We want to guarantee that we don't fall into
    +  // an infinite loop.
    +  var iframeParent = iframe.parentElement;
    +  iframeParent.removeChild(iframe);
    +  // We don't check the value returned as it differs by browser. 0,0 for Chrome
    +  // and FF. IE returns 30000 or 30198 for x in IE8-9 and 300 in IE10-11
    +  goog.style.getFramedPageOffset(testElement2, window);
    +}
    +
    +
    +/**
    + * Asserts that the coordinate is approximately equal to the given
    + * x and y coordinates, give or take delta.
    + */
    +function assertCoordinateApprox(x, y, delta, coord) {
    +  assertTrue('Expected x: ' + x + ', actual x: ' + coord.x,
    +      coord.x >= x - delta && coord.x <= x + delta);
    +  assertTrue('Expected y: ' + y + ', actual y: ' + coord.y,
    +      coord.y >= y - delta && coord.y <= y + delta);
    +}
    +
    +function testTranslateRectForAnotherFrame() {
    +  var rect = new goog.math.Rect(1, 2, 3, 4);
    +  var thisDom = goog.dom.getDomHelper();
    +  goog.style.translateRectForAnotherFrame(rect, thisDom, thisDom);
    +  assertEquals(1, rect.left);
    +  assertEquals(2, rect.top);
    +  assertEquals(3, rect.width);
    +  assertEquals(4, rect.height);
    +
    +  var iframe = $('test-translate-frame-standard');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  var iframeDom = goog.dom.getDomHelper(iframeDoc);
    +  // Cannot rely on iframe starting at origin.
    +  iframeDom.getWindow().scrollTo(0, 0);
    +  // iframe is at (100, 150) and its body is not scrolled.
    +  rect = new goog.math.Rect(1, 2, 3, 4);
    +  goog.style.translateRectForAnotherFrame(rect, iframeDom, thisDom);
    +  assertEquals(1 + 100, rect.left);
    +  assertRoughlyEquals(2 + 150, rect.top, .1);
    +  assertEquals(3, rect.width);
    +  assertEquals(4, rect.height);
    +
    +  iframeDom.getWindow().scrollTo(11, 13);
    +  rect = new goog.math.Rect(1, 2, 3, 4);
    +  goog.style.translateRectForAnotherFrame(rect, iframeDom, thisDom);
    +  assertEquals(1 + 100 - 11, rect.left);
    +  assertRoughlyEquals(2 + 150 - 13, rect.top, .1);
    +  assertEquals(3, rect.width);
    +  assertEquals(4, rect.height);
    +
    +  iframe = $('test-translate-frame-quirk');
    +  iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  iframeDom = goog.dom.getDomHelper(iframeDoc);
    +  // Cannot rely on iframe starting at origin.
    +  iframeDom.getWindow().scrollTo(0, 0);
    +  // iframe is at (100, 350) and its body is not scrolled.
    +  rect = new goog.math.Rect(1, 2, 3, 4);
    +  goog.style.translateRectForAnotherFrame(rect, iframeDom, thisDom);
    +  assertEquals(1 + 100, rect.left);
    +  assertRoughlyEquals(2 + 350, rect.top, .1);
    +  assertEquals(3, rect.width);
    +  assertEquals(4, rect.height);
    +
    +  iframeDom.getWindow().scrollTo(11, 13);
    +  rect = new goog.math.Rect(1, 2, 3, 4);
    +  goog.style.translateRectForAnotherFrame(rect, iframeDom, thisDom);
    +  assertEquals(1 + 100 - 11, rect.left);
    +  assertRoughlyEquals(2 + 350 - 13, rect.top, .1);
    +  assertEquals(3, rect.width);
    +  assertEquals(4, rect.height);
    +}
    +
    +function testGetVisibleRectForElement() {
    +  var container = goog.dom.getElement('test-visible');
    +  var el = goog.dom.getElement('test-visible-el');
    +  var dom = goog.dom.getDomHelper(el);
    +  var winScroll = dom.getDocumentScroll();
    +  var winSize = dom.getViewportSize();
    +
    +  // Skip this test if the window size is small.  Firefox3/Linux in Selenium
    +  // sometimes fails without this check.
    +  if (winSize.width < 20 || winSize.height < 20) {
    +    return;
    +  }
    +
    +  // Move the container element to the window's viewport.
    +  var h = winSize.height < 100 ? winSize.height / 2 : 100;
    +  goog.style.setSize(container, winSize.width / 2, h);
    +  goog.style.setPosition(container, 8, winScroll.y + winSize.height - h);
    +  var visible = goog.style.getVisibleRectForElement(el);
    +  var bounds = goog.style.getBounds(container);
    +  // VisibleRect == Bounds rect of the offsetParent
    +  assertNotNull(visible);
    +  assertEquals(bounds.left, visible.left);
    +  assertEquals(bounds.top, visible.top);
    +  assertEquals(bounds.left + bounds.width, visible.right);
    +  assertEquals(bounds.top + bounds.height, visible.bottom);
    +
    +  // Move a part of the container element to outside of the viewpoert.
    +  goog.style.setPosition(container, 8, winScroll.y + winSize.height - h / 2);
    +  visible = goog.style.getVisibleRectForElement(el);
    +  bounds = goog.style.getBounds(container);
    +  // Confirm VisibleRect == Intersection of the bounds rect of the
    +  // offsetParent and the viewport.
    +  assertNotNull(visible);
    +  assertEquals(bounds.left, visible.left);
    +  assertEquals(bounds.top, visible.top);
    +  assertEquals(bounds.left + bounds.width, visible.right);
    +  assertEquals(winScroll.y + winSize.height, visible.bottom);
    +
    +  // Move the container element to outside of the viewpoert.
    +  goog.style.setPosition(container, 8, winScroll.y + winSize.height * 2);
    +  visible = goog.style.getVisibleRectForElement(el);
    +  assertNull(visible);
    +
    +  // Test the case with body element of height 0
    +  var iframe = goog.dom.getElement('test-visible-frame');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  el = iframeDoc.getElementById('test-visible');
    +  visible = goog.style.getVisibleRectForElement(el);
    +
    +  var iframeViewportSize = goog.dom.getDomHelper(el).getViewportSize();
    +  // NOTE(chrishenry): For iframe, the clipping viewport is always the iframe
    +  // viewport, and not the actual browser viewport.
    +  assertNotNull(visible);
    +  assertEquals(0, visible.top);
    +  assertEquals(iframeViewportSize.height, visible.bottom);
    +  assertEquals(0, visible.left);
    +  assertEquals(iframeViewportSize.width, visible.right);
    +}
    +
    +function testGetVisibleRectForElementWithBodyScrolled() {
    +  var container = goog.dom.getElement('test-visible2');
    +  var dom = goog.dom.getDomHelper(container);
    +  var el = dom.createDom('div', undefined, 'Test');
    +  el.style.position = 'absolute';
    +  dom.append(container, el);
    +
    +  container.style.position = 'absolute';
    +  goog.style.setPosition(container, 20, 500);
    +  goog.style.setSize(container, 100, 150);
    +
    +  // Scroll body container such that container is exactly at top.
    +  window.scrollTo(0, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +
    +  // Top 100px is clipped by window viewport.
    +  window.scrollTo(0, 600);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(600, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +
    +  var winSize = dom.getViewportSize();
    +
    +  // Left 50px is clipped by window viewport.
    +  // Right part is clipped by window viewport.
    +  goog.style.setSize(container, 10000, 150);
    +  window.scrollTo(70, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(70, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(70 + winSize.width, visibleRect.right, EPSILON);
    +
    +  // Bottom part is clipped by window viewport.
    +  goog.style.setSize(container, 100, 2000);
    +  window.scrollTo(0, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +  assertRoughlyEquals(500 + winSize.height, visibleRect.bottom, EPSILON);
    +
    +  goog.style.setPosition(container, 10000, 10000);
    +  assertNull(goog.style.getVisibleRectForElement(el));
    +}
    +
    +function testGetVisibleRectForElementWithNestedAreaAndNonOffsetAncestor() {
    +  // IE7 quirks mode somehow consider container2 below as offset parent
    +  // of the element, which is incorrect.
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8) &&
    +      !goog.dom.isCss1CompatMode()) {
    +    return;
    +  }
    +
    +  var container = goog.dom.getElement('test-visible2');
    +  var dom = goog.dom.getDomHelper(container);
    +  var container2 = dom.createDom('div');
    +  var el = dom.createDom('div', undefined, 'Test');
    +  el.style.position = 'absolute';
    +  dom.append(container, container2);
    +  dom.append(container2, el);
    +
    +  container.style.position = 'absolute';
    +  goog.style.setPosition(container, 20, 500);
    +  goog.style.setSize(container, 100, 150);
    +
    +  // container2 is a scrollable container but is not an offsetParent of
    +  // the element. It is ignored in the computation.
    +  container2.style.overflow = 'hidden';
    +  container2.style.marginTop = '50px';
    +  container2.style.marginLeft = '100px';
    +  goog.style.setSize(container2, 150, 100);
    +
    +  // Scroll body container such that container is exactly at top.
    +  window.scrollTo(0, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +
    +  // Top 100px is clipped by window viewport.
    +  window.scrollTo(0, 600);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(600, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +
    +  var winSize = dom.getViewportSize();
    +
    +  // Left 50px is clipped by window viewport.
    +  // Right part is clipped by window viewport.
    +  goog.style.setSize(container, 10000, 150);
    +  window.scrollTo(70, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(70, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(70 + winSize.width, visibleRect.right, EPSILON);
    +
    +  // Bottom part is clipped by window viewport.
    +  goog.style.setSize(container, 100, 2000);
    +  window.scrollTo(0, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(20, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(120, visibleRect.right, EPSILON);
    +  assertRoughlyEquals(500 + winSize.height, visibleRect.bottom, EPSILON);
    +
    +  goog.style.setPosition(container, 10000, 10000);
    +  assertNull(goog.style.getVisibleRectForElement(el));
    +}
    +
    +function testGetVisibleRectForElementInsideNestedScrollableArea() {
    +  var container = goog.dom.getElement('test-visible2');
    +  var dom = goog.dom.getDomHelper(container);
    +  var container2 = dom.createDom('div');
    +  var el = dom.createDom('div', undefined, 'Test');
    +  el.style.position = 'absolute';
    +  dom.append(container, container2);
    +  dom.append(container2, el);
    +
    +  container.style.position = 'absolute';
    +  goog.style.setPosition(container, 100 /* left */, 500 /* top */);
    +  goog.style.setSize(container, 300 /* width */, 300 /* height */);
    +
    +  container2.style.overflow = 'hidden';
    +  container2.style.position = 'relative';
    +  goog.style.setPosition(container2, 100, 50);
    +  goog.style.setSize(container2, 150, 100);
    +
    +  // Scroll body container such that container is exactly at top.
    +  window.scrollTo(0, 500);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(550, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(200, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(350, visibleRect.right, EPSILON);
    +
    +  // Left 50px is clipped by container.
    +  goog.style.setPosition(container2, -50, 50);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(550, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(100, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(200, visibleRect.right, EPSILON);
    +
    +  // Right part is clipped by container.
    +  goog.style.setPosition(container2, 100, 50);
    +  goog.style.setWidth(container2, 1000, 100);
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(550, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(200, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(650, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(400, visibleRect.right, EPSILON);
    +
    +  // Top 50px is clipped by container.
    +  goog.style.setStyle(container2, 'width', '150px');
    +  goog.style.setStyle(container2, 'top', '-50px');
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(500, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(200, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(550, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(350, visibleRect.right, EPSILON);
    +
    +  // Bottom part is clipped by container.
    +  goog.style.setStyle(container2, 'top', '50px');
    +  goog.style.setStyle(container2, 'height', '1000px');
    +  var visibleRect = goog.style.getVisibleRectForElement(el);
    +  assertNotNull(visibleRect);
    +  assertRoughlyEquals(550, visibleRect.top, EPSILON);
    +  assertRoughlyEquals(200, visibleRect.left, EPSILON);
    +  assertRoughlyEquals(800, visibleRect.bottom, EPSILON);
    +  assertRoughlyEquals(350, visibleRect.right, EPSILON);
    +
    +  // Outside viewport.
    +  goog.style.setStyle(container2, 'top', '10000px');
    +  goog.style.setStyle(container2, 'left', '10000px');
    +  assertNull(goog.style.getVisibleRectForElement(el));
    +}
    +
    +function testScrollIntoContainerViewQuirks() {
    +  if (goog.dom.isCss1CompatMode()) return;
    +
    +  var container = goog.dom.getElement('scrollable-container');
    +
    +  // Scroll the minimum amount to make the elements visible.
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item7'), container);
    +  assertEquals('scroll to item7', 79, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item8'), container);
    +  assertEquals('scroll to item8', 100, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item7'), container);
    +  assertEquals('item7 still visible', 100, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item1'), container);
    +  assertEquals('scroll to item1', 17, container.scrollTop);
    +
    +  // Center the element in the first argument.
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item1'), container,
    +                                     true);
    +  assertEquals('center item1', 0, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item4'), container,
    +                                     true);
    +  assertEquals('center item4', 48, container.scrollTop);
    +
    +  // The element is higher than the container.
    +  goog.dom.getElement('item3').style.height = '140px';
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item3'), container);
    +  assertEquals('show item3 with increased height', 59, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item3'), container,
    +      true);
    +  assertEquals('center item3 with increased height', 87, container.scrollTop);
    +  goog.dom.getElement('item3').style.height = '';
    +
    +  // Scroll to non-integer position.
    +  goog.dom.getElement('item4').style.height = '21px';
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item4'), container,
    +                                     true);
    +  assertEquals('scroll position is rounded down', 48, container.scrollTop);
    +  goog.dom.getElement('item4').style.height = '';
    +}
    +
    +function testScrollIntoContainerViewStandard() {
    +  if (!goog.dom.isCss1CompatMode()) return;
    +
    +  var container = goog.dom.getElement('scrollable-container');
    +
    +  // Scroll the minimum amount to make the elements visible.
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item7'), container);
    +  assertEquals('scroll to item7', 115, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item8'), container);
    +  assertEquals('scroll to item8', 148, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item7'), container);
    +  assertEquals('item7 still visible', 148, container.scrollTop);
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item1'), container);
    +  assertEquals('scroll to item1', 17, container.scrollTop);
    +
    +  // Center the element in the first argument.
    +  goog.style.scrollIntoContainerView(
    +      goog.dom.getElement('item1'), container, true);
    +  assertEquals('center item1', 0, container.scrollTop);
    +  goog.style.scrollIntoContainerView(
    +      goog.dom.getElement('item4'), container, true);
    +  assertEquals('center item4', 66, container.scrollTop);
    +
    +  // The element is higher than the container.
    +  goog.dom.getElement('item3').style.height = '140px';
    +  goog.style.scrollIntoContainerView(goog.dom.getElement('item3'), container);
    +  assertEquals('show item3 with increased height', 83, container.scrollTop);
    +  goog.style.scrollIntoContainerView(
    +      goog.dom.getElement('item3'), container, true);
    +  assertEquals('center item3 with increased height', 93, container.scrollTop);
    +  goog.dom.getElement('item3').style.height = '';
    +
    +  // Scroll to non-integer position.
    +  goog.dom.getElement('item4').style.height = '21px';
    +  goog.style.scrollIntoContainerView(
    +      goog.dom.getElement('item4'), container, true);
    +  assertEquals('scroll position is rounded down', 66, container.scrollTop);
    +  goog.dom.getElement('item4').style.height = '';
    +}
    +
    +function testOffsetParent() {
    +  var parent = goog.dom.getElement('offset-parent');
    +  var child = goog.dom.getElement('offset-child');
    +  assertEquals(parent, goog.style.getOffsetParent(child));
    +}
    +
    +function testOverflowOffsetParent() {
    +  var parent = goog.dom.getElement('offset-parent-overflow');
    +  var child = goog.dom.getElement('offset-child-overflow');
    +  assertEquals(parent, goog.style.getOffsetParent(child));
    +}
    +
    +function testGetViewportPageOffset() {
    +  expectedFailures.expectFailureFor(
    +      goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(9),
    +      'Test has been flaky for ie8-winxp image. Disabling.');
    +
    +  var testViewport = goog.dom.getElement('test-viewport');
    +  testViewport.style.height = '5000px';
    +  testViewport.style.width = '5000px';
    +  var offset = goog.style.getViewportPageOffset(document);
    +  assertEquals(0, offset.x);
    +  assertEquals(0, offset.y);
    +
    +  window.scrollTo(0, 100);
    +  offset = goog.style.getViewportPageOffset(document);
    +  assertEquals(0, offset.x);
    +  assertEquals(100, offset.y);
    +
    +  window.scrollTo(100, 0);
    +  offset = goog.style.getViewportPageOffset(document);
    +  assertEquals(100, offset.x);
    +  assertEquals(0, offset.y);
    +}
    +
    +function testGetsTranslation() {
    +  var element = document.getElementById('translation');
    +
    +  if (goog.userAgent.IE) {
    +    if (!goog.userAgent.isDocumentModeOrHigher(9) ||
    +        (!goog.dom.isCss1CompatMode() &&
    +            !goog.userAgent.isDocumentModeOrHigher(10))) {
    +      // 'CSS transforms were introduced in IE9, but only in standards mode
    +      // later browsers support the translations in quirks mode.
    +      return;
    +    }
    +  }
    +
    +  // First check the element is actually translated, and we haven't missed
    +  // one of the vendor-specific transform properties
    +  var position = goog.style.getClientPosition(element);
    +  var translation = goog.style.getCssTranslation(element);
    +  var expectedTranslation = new goog.math.Coordinate(20, 30);
    +
    +  assertEquals(30, position.x);
    +  assertRoughlyEquals(40, position.y, .1);
    +  assertObjectEquals(expectedTranslation, translation);
    +}
    +
    +
    +/**
    + * Test browser detection for a user agent configuration.
    + * @param {Array<number>} expectedAgents Array of expected userAgents.
    + * @param {string} uaString User agent string.
    + * @param {string=} opt_product Navigator product string.
    + * @param {string=} opt_vendor Navigator vendor string.
    + */
    +function assertUserAgent(expectedAgents, uaString, opt_product, opt_vendor) {
    +
    +  var mockNavigator = {
    +    'userAgent': uaString,
    +    'product': opt_product,
    +    'vendor': opt_vendor
    +  };
    +
    +  mockUserAgent.setNavigator(mockNavigator);
    +  mockUserAgent.setUserAgentString(uaString);
    +
    +  // Force User-Agent lib to reread the global userAgent.
    +  goog.labs.userAgent.util.setUserAgent(null);
    +
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  for (var ua in goog.userAgentTestUtil.UserAgents) {
    +    var isExpected = goog.array.contains(
    +        expectedAgents,
    +        goog.userAgentTestUtil.UserAgents[ua]);
    +    assertEquals(isExpected,
    +                 goog.userAgentTestUtil.getUserAgentDetected(
    +                     goog.userAgentTestUtil.UserAgents[ua]));
    +  }
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Webkit.
    + */
    +function testGetVendorStyleNameWebkit() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  assertEquals('-webkit-transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Webkit.
    + */
    +function testGetVendorStyleNameWebkitNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  assertEquals(
    +      'transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Gecko.
    + */
    +function testGetVendorStyleNameGecko() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  assertEquals('-moz-transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Gecko.
    + */
    +function testGetVendorStyleNameGeckoNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  assertEquals(
    +      'transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for IE.
    + */
    +function testGetVendorStyleNameIE() {
    +  var mockElement = {
    +    'style': {
    +      'msTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  assertEquals('-ms-transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for IE.
    + */
    +function testGetVendorStyleNameIENoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'msTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  assertEquals(
    +      'transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Opera.
    + */
    +function testGetVendorStyleNameOpera() {
    +  var mockElement = {
    +    'style': {
    +      'OTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  assertEquals('-o-transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Opera.
    + */
    +function testGetVendorStyleNameOperaNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'OTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  assertEquals(
    +      'transform-origin',
    +      goog.style.getVendorStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Webkit.
    + */
    +function testGetVendorJsStyleNameWebkit() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  assertEquals('WebkitTransformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Webkit.
    + */
    +function testGetVendorJsStyleNameWebkitNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  assertEquals(
    +      'transformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Gecko.
    + */
    +function testGetVendorJsStyleNameGecko() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  assertEquals('MozTransformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Gecko.
    + */
    +function testGetVendorJsStyleNameGeckoNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  assertEquals(
    +      'transformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for IE.
    + */
    +function testGetVendorJsStyleNameIE() {
    +  var mockElement = {
    +    'style': {
    +      'msTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  assertEquals('msTransformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for IE.
    + */
    +function testGetVendorJsStyleNameIENoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'msTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  assertEquals(
    +      'transformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * with a vendor prefix for Opera.
    + */
    +function testGetVendorJsStyleNameOpera() {
    +  var mockElement = {
    +    'style': {
    +      'OTransformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  assertEquals('OTransformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the proper vendor style name for a CSS property
    + * when it exists without a vendor prefix for Opera.
    + */
    +function testGetVendorJsStyleNameOperaNoPrefix() {
    +  var mockElement = {
    +    'style': {
    +      'OTransformOrigin': '',
    +      'transformOrigin': ''
    +    }
    +  };
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  assertEquals(
    +      'transformOrigin',
    +      goog.style.getVendorJsStyleName_(mockElement, 'transform-origin'));
    +}
    +
    +
    +/**
    + * Test for the setting a style name for a CSS property
    + * with a vendor prefix for Webkit.
    + */
    +function testSetVendorStyleWebkit() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, mockElement.style.WebkitTransform);
    +}
    +
    +
    +/**
    + * Test for the setting a style name for a CSS property
    + * with a vendor prefix for Mozilla.
    + */
    +function testSetVendorStyleGecko() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, mockElement.style.MozTransform);
    +}
    +
    +
    +/**
    + * Test for the setting a style name for a CSS property
    + * with a vendor prefix for IE.
    + */
    +function testSetVendorStyleIE() {
    +  var mockElement = {
    +    'style': {
    +      'msTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, mockElement.style.msTransform);
    +}
    +
    +
    +/**
    + * Test for the setting a style name for a CSS property
    + * with a vendor prefix for Opera.
    + */
    +function testSetVendorStyleOpera() {
    +  var mockElement = {
    +    'style': {
    +      'OTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, mockElement.style.OTransform);
    +}
    +
    +
    +/**
    + * Test for the getting a style name for a CSS property
    + * with a vendor prefix for Webkit.
    + */
    +function testGetVendorStyleWebkit() {
    +  var mockElement = {
    +    'style': {
    +      'WebkitTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.WEBKIT], 'WebKit');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, goog.style.getStyle(mockElement, 'transform'));
    +}
    +
    +
    +/**
    + * Test for the getting a style name for a CSS property
    + * with a vendor prefix for Mozilla.
    + */
    +function testGetVendorStyleGecko() {
    +  var mockElement = {
    +    'style': {
    +      'MozTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.GECKO], 'Gecko', 'Gecko');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, goog.style.getStyle(mockElement, 'transform'));
    +}
    +
    +
    +/**
    + * Test for the getting a style name for a CSS property
    + * with a vendor prefix for IE.
    + */
    +function testGetVendorStyleIE() {
    +  var mockElement = {
    +    'style': {
    +      'msTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.IE], 'MSIE');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, goog.style.getStyle(mockElement, 'transform'));
    +}
    +
    +
    +/**
    + * Test for the getting a style name for a CSS property
    + * with a vendor prefix for Opera.
    + */
    +function testGetVendorStyleOpera() {
    +  var mockElement = {
    +    'style': {
    +      'OTransform': ''
    +    }
    +  };
    +  var styleValue = 'translate3d(0,0,0)';
    +
    +  assertUserAgent([goog.userAgentTestUtil.UserAgents.OPERA], 'Opera');
    +  goog.style.setStyle(mockElement, 'transform', styleValue);
    +  assertEquals(styleValue, goog.style.getStyle(mockElement, 'transform'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_quirk.html b/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_quirk.html
    new file mode 100644
    index 0000000000..2c195581a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_quirk.html
    @@ -0,0 +1,27 @@
    +<!--
    +
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +  <style>
    +    #test-visible {
    +      position: absolute;
    +      background: blue;
    +    }
    +
    +    body {
    +      overflow: hidden;
    +      margin: 0px;
    +    }
    +  </style>
    +</head>
    +<body>
    +  <div id="test-visible">Test</div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_standard.html b/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_standard.html
    new file mode 100644
    index 0000000000..cf6fe4bcf4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test_iframe_standard.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<!--
    +
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <style>
    +    #test-visible {
    +      position: absolute;
    +      background: blue;
    +    }
    +
    +    body {
    +      overflow: hidden;
    +      margin: 0px;
    +    }
    +  </style>
    +</head>
    +<body>
    +  <div id="test-visible">Test</div>
    +</body>
    +</html>
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test_quirk.html b/mapbox-gl/closure-library/closure/goog/style/style_test_quirk.html
    new file mode 100644
    index 0000000000..ba3c46f76d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test_quirk.html
    @@ -0,0 +1,9 @@
    +<!--
    +-->
    +<html><body style="border:0px;margin:0px;"><div style="width:400px;height:400px;background-color:yellow;"></div></body></html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test_rect.svg b/mapbox-gl/closure-library/closure/goog/style/style_test_rect.svg
    new file mode 100644
    index 0000000000..2d0f26c692
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test_rect.svg
    @@ -0,0 +1,11 @@
    +<?xml version="1.0" encoding="utf-8"?>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full"
    +    width="100px" height="100px" viewBox="0 0 100 100">
    +  <rect id="rect" width="50" height="50" fill="blue"/>
    +</svg>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_test_standard.html b/mapbox-gl/closure-library/closure/goog/style/style_test_standard.html
    new file mode 100644
    index 0000000000..46c94dac2c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_test_standard.html
    @@ -0,0 +1,11 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    +            "http://www.w3.org/TR/html4/loose.dtd">
    +<!--
    +-->
    +<html><body style="border:0px;width:400px;height:400px;background-color:blue;"></body></html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.html b/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.html
    new file mode 100644
    index 0000000000..1ea42cd5c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.html
    @@ -0,0 +1,42 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.dom.style
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.style.webkitScrollbarsTest');
    +  </script>
    +  <style>
    +   /*
    + * Note that we have to apply these styles when the page is loaded or the
    + * scrollbars might not pick them up.
    + */
    +::-webkit-scrollbar {
    +  width: 16px;
    +  height: 16px;
    +}
    +
    +.otherScrollBar::-webkit-scrollbar {
    +  width: 10px;
    +  height: 10px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="test-scrollbarwidth" style="background-color: orange; width: 100px; height: 100px; overflow: auto;">
    +   <div style="width: 200px; height: 200px; background-color: red">
    +    Test Scroll bar width with scroll
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.js b/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.js
    new file mode 100644
    index 0000000000..7ce686e6a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/style_webkit_scrollbars_test.js
    @@ -0,0 +1,59 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.webkitScrollbarsTest');
    +goog.setTestOnly('goog.style.webkitScrollbarsTest');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.style');
    +/** @suppress {extraRequire} */
    +goog.require('goog.styleScrollbarTester');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var expectedFailures;
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +
    +  // Assert that the test loaded.
    +  goog.asserts.assert(testScrollbarWidth);
    +}
    +
    +function testScrollBarWidth_webkitScrollbar() {
    +  expectedFailures.expectFailureFor(!goog.userAgent.WEBKIT);
    +
    +  try {
    +    var width = goog.style.getScrollbarWidth();
    +    assertEquals('Scrollbar width should be 16', 16, width);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testScrollBarWidth_webkitScrollbarWithCustomClass() {
    +  expectedFailures.expectFailureFor(!goog.userAgent.WEBKIT);
    +
    +  try {
    +    var customWidth = goog.style.getScrollbarWidth('otherScrollBar');
    +    assertEquals('Custom width should be 10', 10, customWidth);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/stylescrollbartester.js b/mapbox-gl/closure-library/closure/goog/style/stylescrollbartester.js
    new file mode 100644
    index 0000000000..cb9ec204e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/stylescrollbartester.js
    @@ -0,0 +1,78 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Shared unit tests for scrollbar measurement.
    + *
    + * @author flan@google.com (Ian Flanigan)
    + */
    +
    +goog.provide('goog.styleScrollbarTester');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.testing.asserts');
    +goog.setTestOnly('goog.styleScrollbarTester');
    +
    +
    +/**
    + * Tests the scrollbar width calculation. Assumes that there is an element with
    + * id 'test-scrollbarwidth' in the page.
    + */
    +function testScrollbarWidth() {
    +  var width = goog.style.getScrollbarWidth();
    +  assertTrue(width > 0);
    +
    +  var outer = goog.dom.getElement('test-scrollbarwidth');
    +  var inner = goog.dom.getElementsByTagNameAndClass('div', null, outer)[0];
    +  assertTrue('should have a scroll bar',
    +      hasVerticalScroll(outer));
    +  assertTrue('should have a scroll bar',
    +      hasHorizontalScroll(outer));
    +
    +  // Get the inner div absolute width
    +  goog.style.setStyle(outer, 'width', '100%');
    +  assertTrue('should have a scroll bar',
    +      hasVerticalScroll(outer));
    +  assertFalse('should not have a scroll bar',
    +      hasHorizontalScroll(outer));
    +  var innerAbsoluteWidth = inner.offsetWidth;
    +
    +  // Leave the vertical scroll and remove the horizontal by using the scroll
    +  // bar width calculation.
    +  goog.style.setStyle(outer, 'width',
    +      (innerAbsoluteWidth + width) + 'px');
    +  assertTrue('should have a scroll bar',
    +      hasVerticalScroll(outer));
    +  assertFalse('should not have a scroll bar',
    +      hasHorizontalScroll(outer));
    +
    +  // verify by adding 1 more pixel (brings back the vertical scroll bar).
    +  goog.style.setStyle(outer, 'width',
    +      (innerAbsoluteWidth + width - 1) + 'px');
    +  assertTrue('should have a scroll bar',
    +      hasVerticalScroll(outer));
    +  assertTrue('should have a scroll bar',
    +      hasHorizontalScroll(outer));
    +}
    +
    +
    +function hasVerticalScroll(el) {
    +  return el.clientWidth != 0 && el.offsetWidth - el.clientWidth > 0;
    +}
    +
    +
    +function hasHorizontalScroll(el) {
    +  return el.clientHeight != 0 && el.offsetHeight - el.clientHeight > 0;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/transform.js b/mapbox-gl/closure-library/closure/goog/style/transform.js
    new file mode 100644
    index 0000000000..4ceb7411c1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/transform.js
    @@ -0,0 +1,169 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility methods to deal with CSS3 transforms programmatically.
    + */
    +
    +goog.provide('goog.style.transform');
    +
    +goog.require('goog.functions');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.math.Coordinate3');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +
    +/**
    + * Whether CSS3 transform translate() is supported. IE 9 supports 2D transforms
    + * and IE 10 supports 3D transforms. IE 8 supports neither.
    + * @return {boolean} Whether the current environment supports CSS3 transforms.
    + */
    +goog.style.transform.isSupported = goog.functions.cacheReturnValue(function() {
    +  return !goog.userAgent.IE || goog.userAgent.product.isVersion(9);
    +});
    +
    +
    +/**
    + * Whether CSS3 transform translate3d() is supported. If the current browser
    + * supports this transform strategy.
    + * @return {boolean} Whether the current environment supports CSS3 transforms.
    + */
    +goog.style.transform.is3dSupported =
    +    goog.functions.cacheReturnValue(function() {
    +  return goog.userAgent.WEBKIT ||
    +      (goog.userAgent.GECKO && goog.userAgent.product.isVersion(10)) ||
    +      (goog.userAgent.IE && goog.userAgent.product.isVersion(10));
    +});
    +
    +
    +/**
    + * Returns the x,y translation component of any CSS transforms applied to the
    + * element, in pixels.
    + *
    + * @param {!Element} element The element to get the translation of.
    + * @return {!goog.math.Coordinate} The CSS translation of the element in px.
    + */
    +goog.style.transform.getTranslation = function(element) {
    +  var transform = goog.style.getComputedTransform(element);
    +  var matrixConstructor = goog.style.transform.matrixConstructor_();
    +  if (transform && matrixConstructor) {
    +    var matrix = new matrixConstructor(transform);
    +    if (matrix) {
    +      return new goog.math.Coordinate(matrix.m41, matrix.m42);
    +    }
    +  }
    +  return new goog.math.Coordinate(0, 0);
    +};
    +
    +
    +/**
    + * Translates an element's position using the CSS3 transform property.
    + * NOTE: This replaces all other transforms already defined on the element.
    + * @param {Element} element The element to translate.
    + * @param {number} x The horizontal translation.
    + * @param {number} y The vertical translation.
    + * @return {boolean} Whether the CSS translation was set.
    + */
    +goog.style.transform.setTranslation = function(element, x, y) {
    +  if (!goog.style.transform.isSupported()) {
    +    return false;
    +  }
    +  // TODO(user): After http://crbug.com/324107 is fixed, it will be faster to
    +  // use something like: translation = new CSSMatrix().translate(x, y, 0);
    +  var translation = goog.style.transform.is3dSupported() ?
    +      'translate3d(' + x + 'px,' + y + 'px,' + '0px)' :
    +      'translate(' + x + 'px,' + y + 'px)';
    +  goog.style.setStyle(element,
    +      goog.style.transform.getTransformProperty_(), translation);
    +  return true;
    +};
    +
    +
    +/**
    + * Returns the scale of the x, y and z dimensions of CSS transforms applied to
    + * the element.
    + *
    + * @param {!Element} element The element to get the scale of.
    + * @return {!goog.math.Coordinate3} The scale of the element.
    + */
    +goog.style.transform.getScale = function(element) {
    +  var transform = goog.style.getComputedTransform(element);
    +  var matrixConstructor = goog.style.transform.matrixConstructor_();
    +  if (transform && matrixConstructor) {
    +    var matrix = new matrixConstructor(transform);
    +    if (matrix) {
    +      return new goog.math.Coordinate3(matrix.m11, matrix.m22, matrix.m33);
    +    }
    +  }
    +  return new goog.math.Coordinate3(0, 0, 0);
    +};
    +
    +
    +/**
    + * Scales an element using the CSS3 transform property.
    + * NOTE: This replaces all other transforms already defined on the element.
    + * @param {!Element} element The element to scale.
    + * @param {number} x The horizontal scale.
    + * @param {number} y The vertical scale.
    + * @param {number} z The depth scale.
    + * @return {boolean} Whether the CSS scale was set.
    + */
    +goog.style.transform.setScale = function(element, x, y, z) {
    +  if (!goog.style.transform.isSupported()) {
    +    return false;
    +  }
    +  var scale = goog.style.transform.is3dSupported() ?
    +      'scale3d(' + x + ',' + y + ',' + z + ')' :
    +      'scale(' + x + ',' + y + ')';
    +  goog.style.setStyle(element,
    +      goog.style.transform.getTransformProperty_(), scale);
    +  return true;
    +};
    +
    +
    +/**
    + * A cached value of the transform property depending on whether the useragent
    + * is IE9.
    + * @return {string} The transform property depending on whether the useragent
    + *     is IE9.
    + * @private
    + */
    +goog.style.transform.getTransformProperty_ =
    +    goog.functions.cacheReturnValue(function() {
    +  return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE == 9 ?
    +      '-ms-transform' : 'transform';
    +});
    +
    +
    +/**
    + * Gets the constructor for a CSSMatrix object.
    + * @return {function(new:CSSMatrix, string)?} A constructor for a CSSMatrix
    + *     object (or null).
    + * @private
    + */
    +goog.style.transform.matrixConstructor_ =
    +    goog.functions.cacheReturnValue(function() {
    +  if (goog.isDef(goog.global['WebKitCSSMatrix'])) {
    +    return goog.global['WebKitCSSMatrix'];
    +  }
    +  if (goog.isDef(goog.global['MSCSSMatrix'])) {
    +    return goog.global['MSCSSMatrix'];
    +  }
    +  if (goog.isDef(goog.global['CSSMatrix'])) {
    +    return goog.global['CSSMatrix'];
    +  }
    +  return null;
    +});
    diff --git a/mapbox-gl/closure-library/closure/goog/style/transform_test.js b/mapbox-gl/closure-library/closure/goog/style/transform_test.js
    new file mode 100644
    index 0000000000..b5d1339dd7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/transform_test.js
    @@ -0,0 +1,136 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.transformTest');
    +goog.setTestOnly('goog.style.transformTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style.transform');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +
    +/**
    + * Element being transformed.
    + * @type {Element}
    + */
    +var element;
    +
    +
    +/**
    + * Sets a transform translation and asserts the translation was applied.
    + * @param {number} x The horizontal translation
    + * @param {number} y The vertical translation
    + */
    +var setAndAssertTranslation = function(x, y) {
    +  if (goog.userAgent.GECKO ||
    +      goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    // Mozilla and <IE10 do not support CSSMatrix.
    +    return;
    +  }
    +  var success = goog.style.transform.setTranslation(element, x, y);
    +  if (!goog.style.transform.isSupported()) {
    +    assertFalse(success);
    +  } else {
    +    assertTrue(success);
    +    var translation = goog.style.transform.getTranslation(element);
    +    assertEquals(x, translation.x);
    +    assertEquals(y, translation.y);
    +  }
    +};
    +
    +
    +/**
    + * Sets a transform translation and asserts the translation was applied.
    + * @param {number} x The horizontal scale
    + * @param {number} y The vertical scale
    + * @param {number} z The depth scale
    + */
    +var setAndAssertScale = function(x, y, z) {
    +  if (goog.userAgent.GECKO ||
    +      goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
    +    // Mozilla and <IE10 do not support CSSMatrix.
    +    return;
    +  }
    +  var success = goog.style.transform.setScale(element, x, y, z);
    +  if (!goog.style.transform.isSupported()) {
    +    assertFalse(success);
    +  } else {
    +    assertTrue(success);
    +    var scale = goog.style.transform.getScale(element);
    +    assertEquals(x, scale.x);
    +    assertEquals(y, scale.y);
    +    if (goog.style.transform.is3dSupported()) {
    +      assertEquals(z, scale.z);
    +    }
    +  }
    +};
    +
    +
    +function setUp() {
    +  element = goog.dom.createElement('div');
    +  goog.dom.appendChild(goog.dom.getDocument().body, element);
    +}
    +
    +function tearDown() {
    +  goog.dom.removeNode(element);
    +}
    +
    +
    +function testIsSupported() {
    +  if (goog.userAgent.IE && !goog.userAgent.product.isVersion(9)) {
    +    assertFalse(goog.style.transform.isSupported());
    +  } else {
    +    assertTrue(goog.style.transform.isSupported());
    +  }
    +}
    +
    +
    +function testIs3dSupported() {
    +  if (goog.userAgent.GECKO && !goog.userAgent.product.isVersion(10) ||
    +      (goog.userAgent.IE && !goog.userAgent.product.isVersion(10))) {
    +    assertFalse(goog.style.transform.is3dSupported());
    +  } else {
    +    assertTrue(goog.style.transform.is3dSupported());
    +  }
    +}
    +
    +function testTranslateX() {
    +  setAndAssertTranslation(10, 0);
    +}
    +
    +function testTranslateY() {
    +  setAndAssertTranslation(0, 10);
    +}
    +
    +function testTranslateXY() {
    +  setAndAssertTranslation(10, 20);
    +}
    +
    +function testScaleX() {
    +  setAndAssertScale(5, 1, 1);
    +}
    +
    +function testScaleY() {
    +  setAndAssertScale(1, 3, 1);
    +}
    +
    +function testScaleZ() {
    +  setAndAssertScale(1, 1, 8);
    +}
    +
    +function testScale() {
    +  setAndAssertScale(2, 2, 2);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/style/transition.js b/mapbox-gl/closure-library/closure/goog/style/transition.js
    new file mode 100644
    index 0000000000..60d8d48790
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/transition.js
    @@ -0,0 +1,132 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility methods to deal with CSS3 transitions
    + * programmatically.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.style.transition');
    +goog.provide('goog.style.transition.Css3Property');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.safe');
    +goog.require('goog.dom.vendor');
    +goog.require('goog.functions');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * A typedef to represent a CSS3 transition property. Duration and delay
    + * are both in seconds. Timing is CSS3 timing function string, such as
    + * 'easein', 'linear'.
    + *
    + * Alternatively, specifying string in the form of '[property] [duration]
    + * [timing] [delay]' as specified in CSS3 transition is fine too.
    + *
    + * @typedef { {
    + *   property: string,
    + *   duration: number,
    + *   timing: string,
    + *   delay: number
    + * } | string }
    + */
    +goog.style.transition.Css3Property;
    +
    +
    +/**
    + * Sets the element CSS3 transition to properties.
    + * @param {Element} element The element to set transition on.
    + * @param {goog.style.transition.Css3Property|
    + *     Array<goog.style.transition.Css3Property>} properties A single CSS3
    + *     transition property or array of properties.
    + */
    +goog.style.transition.set = function(element, properties) {
    +  if (!goog.isArray(properties)) {
    +    properties = [properties];
    +  }
    +  goog.asserts.assert(
    +      properties.length > 0, 'At least one Css3Property should be specified.');
    +
    +  var values = goog.array.map(
    +      properties, function(p) {
    +        if (goog.isString(p)) {
    +          return p;
    +        } else {
    +          goog.asserts.assertObject(p,
    +              'Expected css3 property to be an object.');
    +          var propString = p.property + ' ' + p.duration + 's ' + p.timing +
    +              ' ' + p.delay + 's';
    +          goog.asserts.assert(p.property && goog.isNumber(p.duration) &&
    +              p.timing && goog.isNumber(p.delay),
    +              'Unexpected css3 property value: %s', propString);
    +          return propString;
    +        }
    +      });
    +  goog.style.transition.setPropertyValue_(element, values.join(','));
    +};
    +
    +
    +/**
    + * Removes any programmatically-added CSS3 transition in the given element.
    + * @param {Element} element The element to remove transition from.
    + */
    +goog.style.transition.removeAll = function(element) {
    +  goog.style.transition.setPropertyValue_(element, '');
    +};
    +
    +
    +/**
    + * @return {boolean} Whether CSS3 transition is supported.
    + */
    +goog.style.transition.isSupported = goog.functions.cacheReturnValue(function() {
    +  // Since IE would allow any attribute, we need to explicitly check the
    +  // browser version here instead.
    +  if (goog.userAgent.IE) {
    +    return goog.userAgent.isVersionOrHigher('10.0');
    +  }
    +
    +  // We create a test element with style=-vendor-transition
    +  // We then detect whether those style properties are recognized and
    +  // available from js.
    +  var el = document.createElement('div');
    +  var transition = 'opacity 1s linear';
    +  var vendorPrefix = goog.dom.vendor.getVendorPrefix();
    +  var style = {'transition': transition};
    +  if (vendorPrefix) {
    +    style[vendorPrefix + '-transition'] = transition;
    +  }
    +  goog.dom.safe.setInnerHtml(el,
    +      goog.html.SafeHtml.create('div', {'style': style}));
    +
    +  var testElement = /** @type {Element} */ (el.firstChild);
    +  goog.asserts.assert(testElement.nodeType == Node.ELEMENT_NODE);
    +
    +  return goog.style.getStyle(testElement, 'transition') != '';
    +});
    +
    +
    +/**
    + * Sets CSS3 transition property value to the given value.
    + * @param {Element} element The element to set transition on.
    + * @param {string} transitionValue The CSS3 transition property value.
    + * @private
    + */
    +goog.style.transition.setPropertyValue_ = function(element, transitionValue) {
    +  goog.style.setStyle(element, 'transition', transitionValue);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/style/transition_test.html b/mapbox-gl/closure-library/closure/goog/style/transition_test.html
    new file mode 100644
    index 0000000000..c27b9bae6e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/transition_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.style.transition
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.style.transitionTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="test">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/style/transition_test.js b/mapbox-gl/closure-library/closure/goog/style/transition_test.js
    new file mode 100644
    index 0000000000..04dccfaa73
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/style/transition_test.js
    @@ -0,0 +1,119 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.style.transitionTest');
    +goog.setTestOnly('goog.style.transitionTest');
    +
    +goog.require('goog.style');
    +goog.require('goog.style.transition');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +
    +/** Fake element. */
    +var element;
    +
    +
    +function setUp() {
    +  element = {'style': {}};
    +}
    +
    +function getTransitionStyle(element) {
    +  return element.style['transition'] ||
    +      goog.style.getStyle(element, 'transition');
    +}
    +
    +
    +function testSetWithNoProperty() {
    +  try {
    +    goog.style.transition.set(element, []);
    +  } catch (e) {
    +    return;
    +  }
    +  fail('Should fail when no property is given.');
    +}
    +
    +
    +function testSetWithString() {
    +  goog.style.transition.set(element, 'opacity 1s ease-in 0.125s');
    +  assertEquals('opacity 1s ease-in 0.125s', getTransitionStyle(element));
    +}
    +
    +
    +function testSetWithSingleProperty() {
    +  goog.style.transition.set(element,
    +      {property: 'opacity', duration: 1, timing: 'ease-in', delay: 0.125});
    +  assertEquals('opacity 1s ease-in 0.125s', getTransitionStyle(element));
    +}
    +
    +
    +function testSetWithMultipleStrings() {
    +  goog.style.transition.set(element, [
    +    'width 1s ease-in',
    +    'height 0.5s linear 1s'
    +  ]);
    +  assertEquals('width 1s ease-in,height 0.5s linear 1s',
    +               getTransitionStyle(element));
    +}
    +
    +
    +function testSetWithMultipleProperty() {
    +  goog.style.transition.set(element, [
    +    {property: 'width', duration: 1, timing: 'ease-in', delay: 0},
    +    {property: 'height', duration: 0.5, timing: 'linear', delay: 1}
    +  ]);
    +  assertEquals('width 1s ease-in 0s,height 0.5s linear 1s',
    +      getTransitionStyle(element));
    +}
    +
    +
    +function testRemoveAll() {
    +  goog.style.setStyle(element, 'transition', 'opacity 1s ease-in');
    +  goog.style.transition.removeAll(element);
    +  assertEquals('', getTransitionStyle(element));
    +}
    +
    +
    +function testAddAndRemoveOnRealElement() {
    +  if (!goog.style.transition.isSupported()) {
    +    return;
    +  }
    +
    +  var div = document.getElementById('test');
    +  goog.style.transition.set(div, 'opacity 1s ease-in 125ms');
    +  assertEquals('opacity 1s ease-in 125ms', getTransitionStyle(div));
    +  goog.style.transition.removeAll(div);
    +  assertEquals('', getTransitionStyle(div));
    +}
    +
    +
    +function testSanityDetectionOfCss3Transition() {
    +  var support = goog.style.transition.isSupported();
    +
    +  // IE support starts at IE10.
    +  if (goog.userAgent.IE) {
    +    assertEquals(goog.userAgent.isVersionOrHigher('10.0'), support);
    +  }
    +
    +  // FF support start at FF4 (Gecko 2.0)
    +  if (goog.userAgent.GECKO) {
    +    assertEquals(goog.userAgent.isVersionOrHigher('2.0'), support);
    +  }
    +
    +  // Webkit support has existed for a long time, we assume support on
    +  // most webkit version in used today.
    +  if (goog.userAgent.WEBKIT) {
    +    assertTrue(support);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/test_module.js b/mapbox-gl/closure-library/closure/goog/test_module.js
    new file mode 100644
    index 0000000000..b265fcefc5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/test_module.js
    @@ -0,0 +1,27 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A test file for testing goog.module.
    + */
    +
    +goog.module('goog.test_module');
    +goog.setTestOnly('goog.test_module');
    +goog.module.declareLegacyNamespace();
    +
    +var dep = goog.require('goog.test_module_dep');
    +
    +/** @constructor */
    +exports = function() {};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/test_module_dep.js b/mapbox-gl/closure-library/closure/goog/test_module_dep.js
    new file mode 100644
    index 0000000000..b65af6f3f7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/test_module_dep.js
    @@ -0,0 +1,26 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A test file for testing goog.module.
    + */
    +
    +goog.module('goog.test_module_dep');
    +goog.setTestOnly('goog.test_module');
    +
    +/** @type {number} */
    +exports.someValue = 1;
    +
    +/** @type {function()} */
    +exports.someFunction = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asserts.js b/mapbox-gl/closure-library/closure/goog/testing/asserts.js
    new file mode 100644
    index 0000000000..047897f740
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asserts.js
    @@ -0,0 +1,1263 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +goog.provide('goog.testing.JsUnitException');
    +goog.provide('goog.testing.asserts');
    +goog.provide('goog.testing.asserts.ArrayLike');
    +
    +goog.require('goog.testing.stacktrace');
    +
    +// TODO(user): Copied from JsUnit with some small modifications, we should
    +// reimplement the asserters.
    +
    +
    +/**
    + * @typedef {Array|NodeList|Arguments|{length: number}}
    + */
    +goog.testing.asserts.ArrayLike;
    +
    +var DOUBLE_EQUALITY_PREDICATE = function(var1, var2) {
    +  return var1 == var2;
    +};
    +var JSUNIT_UNDEFINED_VALUE;
    +var TO_STRING_EQUALITY_PREDICATE = function(var1, var2) {
    +  return var1.toString() === var2.toString();
    +};
    +
    +var PRIMITIVE_EQUALITY_PREDICATES = {
    +  'String': DOUBLE_EQUALITY_PREDICATE,
    +  'Number': DOUBLE_EQUALITY_PREDICATE,
    +  'Boolean': DOUBLE_EQUALITY_PREDICATE,
    +  'Date': function(date1, date2) {
    +    return date1.getTime() == date2.getTime();
    +  },
    +  'RegExp': TO_STRING_EQUALITY_PREDICATE,
    +  'Function': TO_STRING_EQUALITY_PREDICATE
    +};
    +
    +
    +/**
    + * Compares equality of two numbers, allowing them to differ up to a given
    + * tolerance.
    + * @param {number} var1 A number.
    + * @param {number} var2 A number.
    + * @param {number} tolerance the maximum allowed difference.
    + * @return {boolean} Whether the two variables are sufficiently close.
    + * @private
    + */
    +goog.testing.asserts.numberRoughEqualityPredicate_ = function(
    +    var1, var2, tolerance) {
    +  return Math.abs(var1 - var2) <= tolerance;
    +};
    +
    +
    +/**
    + * @type {Object<string, function(*, *, number): boolean>}
    + * @private
    + */
    +goog.testing.asserts.primitiveRoughEqualityPredicates_ = {
    +  'Number': goog.testing.asserts.numberRoughEqualityPredicate_
    +};
    +
    +
    +var _trueTypeOf = function(something) {
    +  var result = typeof something;
    +  try {
    +    switch (result) {
    +      case 'string':
    +        break;
    +      case 'boolean':
    +        break;
    +      case 'number':
    +        break;
    +      case 'object':
    +        if (something == null) {
    +          result = 'null';
    +          break;
    +        }
    +      case 'function':
    +        switch (something.constructor) {
    +          case new String('').constructor:
    +            result = 'String';
    +            break;
    +          case new Boolean(true).constructor:
    +            result = 'Boolean';
    +            break;
    +          case new Number(0).constructor:
    +            result = 'Number';
    +            break;
    +          case new Array().constructor:
    +            result = 'Array';
    +            break;
    +          case new RegExp().constructor:
    +            result = 'RegExp';
    +            break;
    +          case new Date().constructor:
    +            result = 'Date';
    +            break;
    +          case Function:
    +            result = 'Function';
    +            break;
    +          default:
    +            var m = something.constructor.toString().match(
    +                /function\s*([^( ]+)\(/);
    +            if (m) {
    +              result = m[1];
    +            } else {
    +              break;
    +            }
    +        }
    +        break;
    +    }
    +  } catch (e) {
    +
    +  } finally {
    +    result = result.substr(0, 1).toUpperCase() + result.substr(1);
    +  }
    +  return result;
    +};
    +
    +var _displayStringForValue = function(aVar) {
    +  var result;
    +  try {
    +    result = '<' + String(aVar) + '>';
    +  } catch (ex) {
    +    result = '<toString failed: ' + ex.message + '>';
    +    // toString does not work on this object :-(
    +  }
    +  if (!(aVar === null || aVar === JSUNIT_UNDEFINED_VALUE)) {
    +    result += ' (' + _trueTypeOf(aVar) + ')';
    +  }
    +  return result;
    +};
    +
    +var fail = function(failureMessage) {
    +  goog.testing.asserts.raiseException('Call to fail()', failureMessage);
    +};
    +
    +var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) {
    +  return args.length == expectedNumberOfNonCommentArgs + 1;
    +};
    +
    +var commentArg = function(expectedNumberOfNonCommentArgs, args) {
    +  if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) {
    +    return args[0];
    +  }
    +
    +  return null;
    +};
    +
    +var nonCommentArg = function(desiredNonCommentArgIndex,
    +    expectedNumberOfNonCommentArgs, args) {
    +  return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ?
    +      args[desiredNonCommentArgIndex] :
    +      args[desiredNonCommentArgIndex - 1];
    +};
    +
    +var _validateArguments = function(expectedNumberOfNonCommentArgs, args) {
    +  var valid = args.length == expectedNumberOfNonCommentArgs ||
    +      args.length == expectedNumberOfNonCommentArgs + 1 &&
    +      goog.isString(args[0]);
    +  _assert(null, valid, 'Incorrect arguments passed to assert function');
    +};
    +
    +var _assert = function(comment, booleanValue, failureMessage) {
    +  if (!booleanValue) {
    +    goog.testing.asserts.raiseException(comment, failureMessage);
    +  }
    +};
    +
    +
    +/**
    + * @param {*} expected The expected value.
    + * @param {*} actual The actual value.
    + * @return {string} A failure message of the values don't match.
    + * @private
    + */
    +goog.testing.asserts.getDefaultErrorMsg_ = function(expected, actual) {
    +  var msg = 'Expected ' + _displayStringForValue(expected) + ' but was ' +
    +      _displayStringForValue(actual);
    +  if ((typeof expected == 'string') && (typeof actual == 'string')) {
    +    // Try to find a human-readable difference.
    +    var limit = Math.min(expected.length, actual.length);
    +    var commonPrefix = 0;
    +    while (commonPrefix < limit &&
    +        expected.charAt(commonPrefix) == actual.charAt(commonPrefix)) {
    +      commonPrefix++;
    +    }
    +
    +    var commonSuffix = 0;
    +    while (commonSuffix < limit &&
    +        expected.charAt(expected.length - commonSuffix - 1) ==
    +            actual.charAt(actual.length - commonSuffix - 1)) {
    +      commonSuffix++;
    +    }
    +
    +    if (commonPrefix + commonSuffix > limit) {
    +      commonSuffix = 0;
    +    }
    +
    +    if (commonPrefix > 2 || commonSuffix > 2) {
    +      var printString = function(str) {
    +        var startIndex = Math.max(0, commonPrefix - 2);
    +        var endIndex = Math.min(str.length, str.length - (commonSuffix - 2));
    +        return (startIndex > 0 ? '...' : '') +
    +            str.substring(startIndex, endIndex) +
    +            (endIndex < str.length ? '...' : '');
    +      };
    +
    +      msg += '\nDifference was at position ' + commonPrefix +
    +          '. Expected [' + printString(expected) +
    +          '] vs. actual [' + printString(actual) + ']';
    +    }
    +  }
    +  return msg;
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assert = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var comment = commentArg(1, arguments);
    +  var booleanValue = nonCommentArg(1, 1, arguments);
    +
    +  _assert(comment, goog.isBoolean(booleanValue),
    +      'Bad argument to assert(boolean)');
    +  _assert(comment, booleanValue, 'Call to assert(boolean) with false');
    +};
    +
    +
    +/**
    + * Asserts that the function throws an error.
    + *
    + * @param {!(string|Function)} a The assertion comment or the function to call.
    + * @param {!Function=} opt_b The function to call (if the first argument of
    + *     {@code assertThrows} was the comment).
    + * @return {*} The error thrown by the function.
    + * @throws {goog.testing.JsUnitException} If the assertion failed.
    + */
    +var assertThrows = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var func = nonCommentArg(1, 1, arguments);
    +  var comment = commentArg(1, arguments);
    +  _assert(comment, typeof func == 'function',
    +      'Argument passed to assertThrows is not a function');
    +
    +  try {
    +    func();
    +  } catch (e) {
    +    if (e && goog.isString(e['stacktrace']) && goog.isString(e['message'])) {
    +      // Remove the stack trace appended to the error message by Opera 10.0
    +      var startIndex = e['message'].length - e['stacktrace'].length;
    +      if (e['message'].indexOf(e['stacktrace'], startIndex) == startIndex) {
    +        e['message'] = e['message'].substr(0, startIndex - 14);
    +      }
    +    }
    +    return e;
    +  }
    +  goog.testing.asserts.raiseException(comment,
    +      'No exception thrown from function passed to assertThrows');
    +};
    +
    +
    +/**
    + * Asserts that the function does not throw an error.
    + *
    + * @param {!(string|Function)} a The assertion comment or the function to call.
    + * @param {!Function=} opt_b The function to call (if the first argument of
    + *     {@code assertNotThrows} was the comment).
    + * @return {*} The return value of the function.
    + * @throws {goog.testing.JsUnitException} If the assertion failed.
    + */
    +var assertNotThrows = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var comment = commentArg(1, arguments);
    +  var func = nonCommentArg(1, 1, arguments);
    +  _assert(comment, typeof func == 'function',
    +      'Argument passed to assertNotThrows is not a function');
    +
    +  try {
    +    return func();
    +  } catch (e) {
    +    comment = comment ? (comment + '\n') : '';
    +    comment += 'A non expected exception was thrown from function passed to ' +
    +               'assertNotThrows';
    +    // Some browsers don't have a stack trace so at least have the error
    +    // description.
    +    var stackTrace = e['stack'] || e['stacktrace'] || e.toString();
    +    goog.testing.asserts.raiseException(comment, stackTrace);
    +  }
    +};
    +
    +
    +/**
    + * Asserts that the given callback function results in a JsUnitException when
    + * called, and that the resulting failure message matches the given expected
    + * message.
    + * @param {function() : void} callback Function to be run expected to result
    + *     in a JsUnitException (usually contains a call to an assert).
    + * @param {string=} opt_expectedMessage Failure message expected to be given
    + *     with the exception.
    + */
    +var assertThrowsJsUnitException = function(callback, opt_expectedMessage) {
    +  var failed = false;
    +  try {
    +    goog.testing.asserts.callWithoutLogging(callback);
    +  } catch (ex) {
    +    if (!ex.isJsUnitException) {
    +      fail('Expected a JsUnitException');
    +    }
    +    if (typeof opt_expectedMessage != 'undefined' &&
    +        ex.message != opt_expectedMessage) {
    +      fail('Expected message [' + opt_expectedMessage + '] but got [' +
    +          ex.message + ']');
    +    }
    +    failed = true;
    +  }
    +  if (!failed) {
    +    fail('Expected a failure: ' + opt_expectedMessage);
    +  }
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertTrue = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var comment = commentArg(1, arguments);
    +  var booleanValue = nonCommentArg(1, 1, arguments);
    +
    +  _assert(comment, goog.isBoolean(booleanValue),
    +      'Bad argument to assertTrue(boolean)');
    +  _assert(comment, booleanValue, 'Call to assertTrue(boolean) with false');
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertFalse = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var comment = commentArg(1, arguments);
    +  var booleanValue = nonCommentArg(1, 1, arguments);
    +
    +  _assert(comment, goog.isBoolean(booleanValue),
    +      'Bad argument to assertFalse(boolean)');
    +  _assert(comment, !booleanValue, 'Call to assertFalse(boolean) with true');
    +};
    +
    +
    +/**
    + * @param {*} a The expected value (2 args) or the debug message (3 args).
    + * @param {*} b The actual value (2 args) or the expected value (3 args).
    + * @param {*=} opt_c The actual value (3 args only).
    + */
    +var assertEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var var1 = nonCommentArg(1, 2, arguments);
    +  var var2 = nonCommentArg(2, 2, arguments);
    +  _assert(commentArg(2, arguments), var1 === var2,
    +          goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    +};
    +
    +
    +/**
    + * @param {*} a The expected value (2 args) or the debug message (3 args).
    + * @param {*} b The actual value (2 args) or the expected value (3 args).
    + * @param {*=} opt_c The actual value (3 args only).
    + */
    +var assertNotEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var var1 = nonCommentArg(1, 2, arguments);
    +  var var2 = nonCommentArg(2, 2, arguments);
    +  _assert(commentArg(2, arguments), var1 !== var2,
    +      'Expected not to be ' + _displayStringForValue(var2));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNull = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), aVar === null,
    +      goog.testing.asserts.getDefaultErrorMsg_(null, aVar));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNotNull = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), aVar !== null,
    +      'Expected not to be ' + _displayStringForValue(null));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertUndefined = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), aVar === JSUNIT_UNDEFINED_VALUE,
    +      goog.testing.asserts.getDefaultErrorMsg_(JSUNIT_UNDEFINED_VALUE, aVar));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNotUndefined = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE,
    +      'Expected not to be ' + _displayStringForValue(JSUNIT_UNDEFINED_VALUE));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNotNullNorUndefined = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  assertNotNull.apply(null, arguments);
    +  assertNotUndefined.apply(null, arguments);
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNonEmptyString = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments),
    +      aVar !== JSUNIT_UNDEFINED_VALUE && aVar !== null &&
    +      typeof aVar == 'string' && aVar !== '',
    +      'Expected non-empty string but was ' + _displayStringForValue(aVar));
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNaN = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN');
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertNotNaN = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var aVar = nonCommentArg(1, 1, arguments);
    +  _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN');
    +};
    +
    +
    +/**
    + * Runs a function in an environment where test failures are not logged. This is
    + * useful for testing test code, where failures can be a normal part of a test.
    + * @param {function() : void} fn Function to run without logging failures.
    + */
    +goog.testing.asserts.callWithoutLogging = function(fn) {
    +  var testRunner = goog.global['G_testRunner'];
    +  var oldLogTestFailure = testRunner['logTestFailure'];
    +  try {
    +    // Any failures in the callback shouldn't be recorded.
    +    testRunner['logTestFailure'] = undefined;
    +    fn();
    +  } finally {
    +    testRunner['logTestFailure'] = oldLogTestFailure;
    +  }
    +};
    +
    +
    +/**
    + * The return value of the equality predicate passed to findDifferences below,
    + * in cases where the predicate can't test the input variables for equality.
    + * @type {?string}
    + */
    +goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS = null;
    +
    +
    +/**
    + * The return value of the equality predicate passed to findDifferences below,
    + * in cases where the input vriables are equal.
    + * @type {?string}
    + */
    +goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL = '';
    +
    +
    +/**
    + * Determines if two items of any type match, and formulates an error message
    + * if not.
    + * @param {*} expected Expected argument to match.
    + * @param {*} actual Argument as a result of performing the test.
    + * @param {(function(string, *, *): ?string)=} opt_equalityPredicate An optional
    + *     function that can be used to check equality of variables. It accepts 3
    + *     arguments: type-of-variables, var1, var2 (in that order) and returns an
    + *     error message if the variables are not equal,
    + *     goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables
    + *     are equal, or
    + *     goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate
    + *     couldn't check the input variables. The function will be called only if
    + *     the types of var1 and var2 are identical.
    + * @return {?string} Null on success, error message on failure.
    + */
    +goog.testing.asserts.findDifferences = function(expected, actual,
    +    opt_equalityPredicate) {
    +  var failures = [];
    +  var seen1 = [];
    +  var seen2 = [];
    +
    +  // To avoid infinite recursion when the two parameters are self-referential
    +  // along the same path of properties, keep track of the object pairs already
    +  // seen in this call subtree, and abort when a cycle is detected.
    +  function innerAssertWithCycleCheck(var1, var2, path) {
    +    // This is used for testing, so we can afford to be slow (but more
    +    // accurate). So we just check whether var1 is in seen1. If we
    +    // found var1 in index i, we simply need to check whether var2 is
    +    // in seen2[i]. If it is, we do not recurse to check var1/var2. If
    +    // it isn't, we know that the structures of the two objects must be
    +    // different.
    +    //
    +    // This is based on the fact that values at index i in seen1 and
    +    // seen2 will be checked for equality eventually (when
    +    // innerAssertImplementation(seen1[i], seen2[i], path) finishes).
    +    for (var i = 0; i < seen1.length; ++i) {
    +      var match1 = seen1[i] === var1;
    +      var match2 = seen2[i] === var2;
    +      if (match1 || match2) {
    +        if (!match1 || !match2) {
    +          // Asymmetric cycles, so the objects have different structure.
    +          failures.push('Asymmetric cycle detected at ' + path);
    +        }
    +        return;
    +      }
    +    }
    +
    +    seen1.push(var1);
    +    seen2.push(var2);
    +    innerAssertImplementation(var1, var2, path);
    +    seen1.pop();
    +    seen2.pop();
    +  }
    +
    +  var equalityPredicate = opt_equalityPredicate || function(type, var1, var2) {
    +    var typedPredicate = PRIMITIVE_EQUALITY_PREDICATES[type];
    +    if (!typedPredicate) {
    +      return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    +    }
    +    var equal = typedPredicate(var1, var2);
    +    return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    +        goog.testing.asserts.getDefaultErrorMsg_(var1, var2);
    +  };
    +
    +  /**
    +   * @param {*} var1 An item in the expected object.
    +   * @param {*} var2 The corresponding item in the actual object.
    +   * @param {string} path Their path in the objects.
    +   * @suppress {missingProperties} The map_ property is unknown to the compiler
    +   *     unless goog.structs.Map is loaded.
    +   */
    +  function innerAssertImplementation(var1, var2, path) {
    +    if (var1 === var2) {
    +      return;
    +    }
    +
    +    var typeOfVar1 = _trueTypeOf(var1);
    +    var typeOfVar2 = _trueTypeOf(var2);
    +
    +    if (typeOfVar1 == typeOfVar2) {
    +      var isArray = typeOfVar1 == 'Array';
    +      var errorMessage = equalityPredicate(typeOfVar1, var1, var2);
    +      if (errorMessage !=
    +          goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS) {
    +        if (errorMessage !=
    +            goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL) {
    +          failures.push(path + ': ' + errorMessage);
    +        }
    +      } else if (isArray && var1.length != var2.length) {
    +        failures.push(path + ': Expected ' + var1.length + '-element array ' +
    +                      'but got a ' + var2.length + '-element array');
    +      } else {
    +        var childPath = path + (isArray ? '[%s]' : (path ? '.%s' : '%s'));
    +
    +        // if an object has an __iterator__ property, we have no way of
    +        // actually inspecting its raw properties, and JS 1.7 doesn't
    +        // overload [] to make it possible for someone to generically
    +        // use what the iterator returns to compare the object-managed
    +        // properties. This gets us into deep poo with things like
    +        // goog.structs.Map, at least on systems that support iteration.
    +        if (!var1['__iterator__']) {
    +          for (var prop in var1) {
    +            if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    +              // Skip array indices for now. We'll handle them later.
    +              continue;
    +            }
    +
    +            if (prop in var2) {
    +              innerAssertWithCycleCheck(var1[prop], var2[prop],
    +                  childPath.replace('%s', prop));
    +            } else {
    +              failures.push('property ' + prop +
    +                            ' not present in actual ' + (path || typeOfVar2));
    +            }
    +          }
    +          // make sure there aren't properties in var2 that are missing
    +          // from var1. if there are, then by definition they don't
    +          // match.
    +          for (var prop in var2) {
    +            if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
    +              // Skip array indices for now. We'll handle them later.
    +              continue;
    +            }
    +
    +            if (!(prop in var1)) {
    +              failures.push('property ' + prop +
    +                            ' not present in expected ' +
    +                            (path || typeOfVar1));
    +            }
    +          }
    +
    +          // Handle array indices by iterating from 0 to arr.length.
    +          //
    +          // Although all browsers allow holes in arrays, browsers
    +          // are inconsistent in what they consider a hole. For example,
    +          // "[0,undefined,2]" has a hole on IE but not on Firefox.
    +          //
    +          // Because our style guide bans for...in iteration over arrays,
    +          // we assume that most users don't care about holes in arrays,
    +          // and that it is ok to say that a hole is equivalent to a slot
    +          // populated with 'undefined'.
    +          if (isArray) {
    +            for (prop = 0; prop < var1.length; prop++) {
    +              innerAssertWithCycleCheck(var1[prop], var2[prop],
    +                  childPath.replace('%s', String(prop)));
    +            }
    +          }
    +        } else {
    +          // special-case for closure objects that have iterators
    +          if (goog.isFunction(var1.equals)) {
    +            // use the object's own equals function, assuming it accepts an
    +            // object and returns a boolean
    +            if (!var1.equals(var2)) {
    +              failures.push('equals() returned false for ' +
    +                            (path || typeOfVar1));
    +            }
    +          } else if (var1.map_) {
    +            // assume goog.structs.Map or goog.structs.Set, where comparing
    +            // their private map_ field is sufficient
    +            innerAssertWithCycleCheck(var1.map_, var2.map_,
    +                childPath.replace('%s', 'map_'));
    +          } else {
    +            // else die, so user knows we can't do anything
    +            failures.push('unable to check ' + (path || typeOfVar1) +
    +                          ' for equality: it has an iterator we do not ' +
    +                          'know how to handle. please add an equals method');
    +          }
    +        }
    +      }
    +    } else {
    +      failures.push(path + ' ' +
    +          goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
    +    }
    +  }
    +
    +  innerAssertWithCycleCheck(expected, actual, '');
    +  return failures.length == 0 ? null :
    +      goog.testing.asserts.getDefaultErrorMsg_(expected, actual) +
    +          '\n   ' + failures.join('\n   ');
    +};
    +
    +
    +/**
    + * Notes:
    + * Object equality has some nasty browser quirks, and this implementation is
    + * not 100% correct. For example,
    + *
    + * <code>
    + * var a = [0, 1, 2];
    + * var b = [0, 1, 2];
    + * delete a[1];
    + * b[1] = undefined;
    + * assertObjectEquals(a, b); // should fail, but currently passes
    + * </code>
    + *
    + * See asserts_test.html for more interesting edge cases.
    + *
    + * The first comparison object provided is the expected value, the second is
    + * the actual.
    + *
    + * @param {*} a Assertion message or comparison object.
    + * @param {*} b Comparison object.
    + * @param {*=} opt_c Comparison object, if an assertion message was provided.
    + */
    +var assertObjectEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var v1 = nonCommentArg(1, 2, arguments);
    +  var v2 = nonCommentArg(2, 2, arguments);
    +  var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    +  var differences = goog.testing.asserts.findDifferences(v1, v2);
    +
    +  _assert(failureMessage, !differences, differences);
    +};
    +
    +
    +/**
    + * Similar to assertObjectEquals above, but accepts a tolerance margin.
    + *
    + * @param {*} a Assertion message or comparison object.
    + * @param {*} b Comparison object.
    + * @param {*} c Comparison object or tolerance.
    + * @param {*=} opt_d Tolerance, if an assertion message was provided.
    + */
    +var assertObjectRoughlyEquals = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +  var v1 = nonCommentArg(1, 3, arguments);
    +  var v2 = nonCommentArg(2, 3, arguments);
    +  var tolerance = nonCommentArg(3, 3, arguments);
    +  var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    +  var equalityPredicate = function(type, var1, var2) {
    +    var typedPredicate =
    +        goog.testing.asserts.primitiveRoughEqualityPredicates_[type];
    +    if (!typedPredicate) {
    +      return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
    +    }
    +    var equal = typedPredicate(var1, var2, tolerance);
    +    return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
    +        goog.testing.asserts.getDefaultErrorMsg_(var1, var2) +
    +        ' which was more than ' + tolerance + ' away';
    +  };
    +  var differences = goog.testing.asserts.findDifferences(
    +      v1, v2, equalityPredicate);
    +
    +  _assert(failureMessage, !differences, differences);
    +};
    +
    +
    +/**
    + * Compares two arbitrary objects for non-equalness.
    + *
    + * All the same caveats as for assertObjectEquals apply here:
    + * Undefined values may be confused for missing values, or vice versa.
    + *
    + * @param {*} a Assertion message or comparison object.
    + * @param {*} b Comparison object.
    + * @param {*=} opt_c Comparison object, if an assertion message was provided.
    + */
    +var assertObjectNotEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var v1 = nonCommentArg(1, 2, arguments);
    +  var v2 = nonCommentArg(2, 2, arguments);
    +  var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    +  var differences = goog.testing.asserts.findDifferences(v1, v2);
    +
    +  _assert(failureMessage, differences, 'Objects should not be equal');
    +};
    +
    +
    +/**
    + * Compares two arrays ignoring negative indexes and extra properties on the
    + * array objects. Use case: Internet Explorer adds the index, lastIndex and
    + * input enumerable fields to the result of string.match(/regexp/g), which makes
    + * assertObjectEquals fail.
    + * @param {*} a The expected array (2 args) or the debug message (3 args).
    + * @param {*} b The actual array (2 args) or the expected array (3 args).
    + * @param {*=} opt_c The actual array (3 args only).
    + */
    +var assertArrayEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var v1 = nonCommentArg(1, 2, arguments);
    +  var v2 = nonCommentArg(2, 2, arguments);
    +  var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    +
    +  var typeOfVar1 = _trueTypeOf(v1);
    +  _assert(failureMessage,
    +          typeOfVar1 == 'Array',
    +          'Expected an array for assertArrayEquals but found a ' + typeOfVar1);
    +
    +  var typeOfVar2 = _trueTypeOf(v2);
    +  _assert(failureMessage,
    +          typeOfVar2 == 'Array',
    +          'Expected an array for assertArrayEquals but found a ' + typeOfVar2);
    +
    +  assertObjectEquals(failureMessage,
    +      Array.prototype.concat.call(v1), Array.prototype.concat.call(v2));
    +};
    +
    +
    +/**
    + * Compares two objects that can be accessed like an array and assert that
    + * each element is equal.
    + * @param {string|Object} a Failure message (3 arguments)
    + *     or object #1 (2 arguments).
    + * @param {Object} b Object #1 (2 arguments) or object #2 (3 arguments).
    + * @param {Object=} opt_c Object #2 (3 arguments).
    + */
    +var assertElementsEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +
    +  var v1 = nonCommentArg(1, 2, arguments);
    +  var v2 = nonCommentArg(2, 2, arguments);
    +  var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
    +
    +  if (!v1) {
    +    assert(failureMessage, !v2);
    +  } else {
    +    assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    +    for (var i = 0; i < v1.length; ++i) {
    +      assertEquals(
    +          'mismatch at index ' + i + ': ' + failureMessage, v1[i], v2[i]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compares two objects that can be accessed like an array and assert that
    + * each element is roughly equal.
    + * @param {string|Object} a Failure message (4 arguments)
    + *     or object #1 (3 arguments).
    + * @param {Object} b Object #1 (4 arguments) or object #2 (3 arguments).
    + * @param {Object|number} c Object #2 (4 arguments) or tolerance (3 arguments).
    + * @param {number=} opt_d tolerance (4 arguments).
    + */
    +var assertElementsRoughlyEqual = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +
    +  var v1 = nonCommentArg(1, 3, arguments);
    +  var v2 = nonCommentArg(2, 3, arguments);
    +  var tolerance = nonCommentArg(3, 3, arguments);
    +  var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
    +
    +  if (!v1) {
    +    assert(failureMessage, !v2);
    +  } else {
    +    assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
    +    for (var i = 0; i < v1.length; ++i) {
    +      assertRoughlyEquals(failureMessage, v1[i], v2[i], tolerance);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Compares two array-like objects without taking their order into account.
    + * @param {string|goog.testing.asserts.ArrayLike} a Assertion message or the
    + *     expected elements.
    + * @param {goog.testing.asserts.ArrayLike} b Expected elements or the actual
    + *     elements.
    + * @param {goog.testing.asserts.ArrayLike=} opt_c Actual elements.
    + */
    +var assertSameElements = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var expected = nonCommentArg(1, 2, arguments);
    +  var actual = nonCommentArg(2, 2, arguments);
    +  var message = commentArg(2, arguments);
    +
    +  assertTrue('Bad arguments to assertSameElements(opt_message, expected: ' +
    +      'ArrayLike, actual: ArrayLike)',
    +      goog.isArrayLike(expected) && goog.isArrayLike(actual));
    +
    +  // Clones expected and actual and converts them to real arrays.
    +  expected = goog.testing.asserts.toArray_(expected);
    +  actual = goog.testing.asserts.toArray_(actual);
    +  // TODO(user): It would be great to show only the difference
    +  // between the expected and actual elements.
    +  _assert(message, expected.length == actual.length,
    +      'Expected ' + expected.length + ' elements: [' + expected + '], ' +
    +      'got ' + actual.length + ' elements: [' + actual + ']');
    +
    +  var toFind = goog.testing.asserts.toArray_(expected);
    +  for (var i = 0; i < actual.length; i++) {
    +    var index = goog.testing.asserts.indexOf_(toFind, actual[i]);
    +    _assert(message, index != -1, 'Expected [' + expected + '], got [' +
    +        actual + ']');
    +    toFind.splice(index, 1);
    +  }
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertEvaluatesToTrue = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var value = nonCommentArg(1, 1, arguments);
    +  if (!value) {
    +    _assert(commentArg(1, arguments), false, 'Expected to evaluate to true');
    +  }
    +};
    +
    +
    +/**
    + * @param {*} a The value to assert (1 arg) or debug message (2 args).
    + * @param {*=} opt_b The value to assert (2 args only).
    + */
    +var assertEvaluatesToFalse = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var value = nonCommentArg(1, 1, arguments);
    +  if (value) {
    +    _assert(commentArg(1, arguments), false, 'Expected to evaluate to false');
    +  }
    +};
    +
    +
    +/**
    + * Compares two HTML snippets.
    + *
    + * Take extra care if attributes are involved. {@code assertHTMLEquals}'s
    + * implementation isn't prepared for complex cases. For example, the following
    + * comparisons erroneously fail:
    + * <pre>
    + * assertHTMLEquals('<a href="x" target="y">', '<a target="y" href="x">');
    + * assertHTMLEquals('<div classname="a b">', '<div classname="b a">');
    + * assertHTMLEquals('<input disabled>', '<input disabled="disabled">');
    + * </pre>
    + *
    + * When in doubt, use {@code goog.testing.dom.assertHtmlMatches}.
    + *
    + * @param {*} a The expected value (2 args) or the debug message (3 args).
    + * @param {*} b The actual value (2 args) or the expected value (3 args).
    + * @param {*=} opt_c The actual value (3 args only).
    + */
    +var assertHTMLEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var var1 = nonCommentArg(1, 2, arguments);
    +  var var2 = nonCommentArg(2, 2, arguments);
    +  var var1Standardized = standardizeHTML(var1);
    +  var var2Standardized = standardizeHTML(var2);
    +
    +  _assert(commentArg(2, arguments), var1Standardized === var2Standardized,
    +          goog.testing.asserts.getDefaultErrorMsg_(
    +              var1Standardized, var2Standardized));
    +};
    +
    +
    +/**
    + * Compares two CSS property values to make sure that they represent the same
    + * things. This will normalize values in the browser. For example, in Firefox,
    + * this assertion will consider "rgb(0, 0, 255)" and "#0000ff" to be identical
    + * values for the "color" property. This function won't normalize everything --
    + * for example, in most browsers, "blue" will not match "#0000ff". It is
    + * intended only to compensate for unexpected normalizations performed by
    + * the browser that should also affect your expected value.
    + * @param {string} a Assertion message, or the CSS property name.
    + * @param {string} b CSS property name, or the expected value.
    + * @param {string} c The expected value, or the actual value.
    + * @param {string=} opt_d The actual value.
    + */
    +var assertCSSValueEquals = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +  var propertyName = nonCommentArg(1, 3, arguments);
    +  var expectedValue = nonCommentArg(2, 3, arguments);
    +  var actualValue = nonCommentArg(3, 3, arguments);
    +  var expectedValueStandardized =
    +      standardizeCSSValue(propertyName, expectedValue);
    +  var actualValueStandardized =
    +      standardizeCSSValue(propertyName, actualValue);
    +
    +  _assert(commentArg(3, arguments),
    +          expectedValueStandardized == actualValueStandardized,
    +          goog.testing.asserts.getDefaultErrorMsg_(
    +              expectedValueStandardized, actualValueStandardized));
    +};
    +
    +
    +/**
    + * @param {*} a The expected value (2 args) or the debug message (3 args).
    + * @param {*} b The actual value (2 args) or the expected value (3 args).
    + * @param {*=} opt_c The actual value (3 args only).
    + */
    +var assertHashEquals = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var var1 = nonCommentArg(1, 2, arguments);
    +  var var2 = nonCommentArg(2, 2, arguments);
    +  var message = commentArg(2, arguments);
    +  for (var key in var1) {
    +    _assert(message,
    +        key in var2, 'Expected hash had key ' + key + ' that was not found');
    +    _assert(message, var1[key] == var2[key], 'Value for key ' + key +
    +        ' mismatch - expected = ' + var1[key] + ', actual = ' + var2[key]);
    +  }
    +
    +  for (var key in var2) {
    +    _assert(message, key in var1, 'Actual hash had key ' + key +
    +        ' that was not expected');
    +  }
    +};
    +
    +
    +/**
    + * @param {*} a The expected value (3 args) or the debug message (4 args).
    + * @param {*} b The actual value (3 args) or the expected value (4 args).
    + * @param {*} c The tolerance (3 args) or the actual value (4 args).
    + * @param {*=} opt_d The tolerance (4 args only).
    + */
    +var assertRoughlyEquals = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +  var expected = nonCommentArg(1, 3, arguments);
    +  var actual = nonCommentArg(2, 3, arguments);
    +  var tolerance = nonCommentArg(3, 3, arguments);
    +  _assert(commentArg(3, arguments),
    +      goog.testing.asserts.numberRoughEqualityPredicate_(
    +          expected, actual, tolerance),
    +      'Expected ' + expected + ', but got ' + actual +
    +      ' which was more than ' + tolerance + ' away');
    +};
    +
    +
    +/**
    + * Checks if the given element is the member of the given container.
    + * @param {*} a Failure message (3 arguments) or the contained element
    + *     (2 arguments).
    + * @param {*} b The contained element (3 arguments) or the container
    + *     (2 arguments).
    + * @param {*=} opt_c The container.
    + */
    +var assertContains = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var contained = nonCommentArg(1, 2, arguments);
    +  var container = nonCommentArg(2, 2, arguments);
    +  _assert(commentArg(2, arguments),
    +      goog.testing.asserts.contains_(container, contained),
    +      'Expected \'' + container + '\' to contain \'' + contained + '\'');
    +};
    +
    +
    +/**
    + * Checks if the given element is not the member of the given container.
    + * @param {*} a Failure message (3 arguments) or the contained element
    + *     (2 arguments).
    + * @param {*} b The contained element (3 arguments) or the container
    + *     (2 arguments).
    + * @param {*=} opt_c The container.
    + */
    +var assertNotContains = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var contained = nonCommentArg(1, 2, arguments);
    +  var container = nonCommentArg(2, 2, arguments);
    +  _assert(commentArg(2, arguments),
    +      !goog.testing.asserts.contains_(container, contained),
    +      'Expected \'' + container + '\' not to contain \'' + contained + '\'');
    +};
    +
    +
    +/**
    + * Checks if the given string matches the given regular expression.
    + * @param {*} a Failure message (3 arguments) or the expected regular
    + *     expression as a string or RegExp (2 arguments).
    + * @param {*} b The regular expression (3 arguments) or the string to test
    + *     (2 arguments).
    + * @param {*=} opt_c The string to test.
    + */
    +var assertRegExp = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var regexp = nonCommentArg(1, 2, arguments);
    +  var string = nonCommentArg(2, 2, arguments);
    +  if (typeof(regexp) == 'string') {
    +    regexp = new RegExp(regexp);
    +  }
    +  _assert(commentArg(2, arguments),
    +      regexp.test(string),
    +      'Expected \'' + string + '\' to match RegExp ' + regexp.toString());
    +};
    +
    +
    +/**
    + * Converts an array like object to array or clones it if it's already array.
    + * @param {goog.testing.asserts.ArrayLike} arrayLike The collection.
    + * @return {!Array<?>} Copy of the collection as array.
    + * @private
    + */
    +goog.testing.asserts.toArray_ = function(arrayLike) {
    +  var ret = [];
    +  for (var i = 0; i < arrayLike.length; i++) {
    +    ret[i] = arrayLike[i];
    +  }
    +  return ret;
    +};
    +
    +
    +/**
    + * Finds the position of the first occurrence of an element in a container.
    + * @param {goog.testing.asserts.ArrayLike} container
    + *     The array to find the element in.
    + * @param {*} contained Element to find.
    + * @return {number} Index of the first occurrence or -1 if not found.
    + * @private
    + */
    +goog.testing.asserts.indexOf_ = function(container, contained) {
    +  if (container.indexOf) {
    +    return container.indexOf(contained);
    +  } else {
    +    // IE6/7 do not have indexOf so do a search.
    +    for (var i = 0; i < container.length; i++) {
    +      if (container[i] === contained) {
    +        return i;
    +      }
    +    }
    +    return -1;
    +  }
    +};
    +
    +
    +/**
    + * Tells whether the array contains the given element.
    + * @param {goog.testing.asserts.ArrayLike} container The array to
    + *     find the element in.
    + * @param {*} contained Element to find.
    + * @return {boolean} Whether the element is in the array.
    + * @private
    + */
    +goog.testing.asserts.contains_ = function(container, contained) {
    +  // TODO(user): Can we check for container.contains as well?
    +  // That would give us support for most goog.structs (though weird results
    +  // with anything else with a contains method, like goog.math.Range). Falling
    +  // back with container.some would catch all iterables, too.
    +  return goog.testing.asserts.indexOf_(container, contained) != -1;
    +};
    +
    +var standardizeHTML = function(html) {
    +  var translator = document.createElement('DIV');
    +  translator.innerHTML = html;
    +
    +  // Trim whitespace from result (without relying on goog.string)
    +  return translator.innerHTML.replace(/^\s+|\s+$/g, '');
    +};
    +
    +
    +/**
    + * Standardizes a CSS value for a given property by applying it to an element
    + * and then reading it back.
    + * @param {string} propertyName CSS property name.
    + * @param {string} value CSS value.
    + * @return {string} Normalized CSS value.
    + */
    +var standardizeCSSValue = function(propertyName, value) {
    +  var styleDeclaration = document.createElement('DIV').style;
    +  styleDeclaration[propertyName] = value;
    +  return styleDeclaration[propertyName];
    +};
    +
    +
    +/**
    + * Raises a JsUnit exception with the given comment.
    + * @param {string} comment A summary for the exception.
    + * @param {string=} opt_message A description of the exception.
    + */
    +goog.testing.asserts.raiseException = function(comment, opt_message) {
    +  throw new goog.testing.JsUnitException(comment, opt_message);
    +};
    +
    +
    +/**
    + * Helper function for assertObjectEquals.
    + * @param {string} prop A property name.
    + * @return {boolean} If the property name is an array index.
    + * @private
    + */
    +goog.testing.asserts.isArrayIndexProp_ = function(prop) {
    +  return (prop | 0) == prop;
    +};
    +
    +
    +
    +/**
    + * @param {string} comment A summary for the exception.
    + * @param {?string=} opt_message A description of the exception.
    + * @constructor
    + * @extends {Error}
    + * @final
    + */
    +goog.testing.JsUnitException = function(comment, opt_message) {
    +  this.isJsUnitException = true;
    +  this.message = (comment ? comment : '') +
    +                 (comment && opt_message ? '\n' : '') +
    +                 (opt_message ? opt_message : '');
    +  this.stackTrace = goog.testing.stacktrace.get();
    +  // These fields are for compatibility with jsUnitTestManager.
    +  this.comment = comment || null;
    +  this.jsUnitMessage = opt_message || '';
    +
    +  // Ensure there is a stack trace.
    +  if (Error.captureStackTrace) {
    +    Error.captureStackTrace(this, goog.testing.JsUnitException);
    +  } else {
    +    this.stack = new Error().stack || '';
    +  }
    +};
    +goog.inherits(goog.testing.JsUnitException, Error);
    +
    +
    +/** @override */
    +goog.testing.JsUnitException.prototype.toString = function() {
    +  return this.message;
    +};
    +
    +
    +goog.exportSymbol('fail', fail);
    +goog.exportSymbol('assert', assert);
    +goog.exportSymbol('assertThrows', assertThrows);
    +goog.exportSymbol('assertNotThrows', assertNotThrows);
    +goog.exportSymbol('assertTrue', assertTrue);
    +goog.exportSymbol('assertFalse', assertFalse);
    +goog.exportSymbol('assertEquals', assertEquals);
    +goog.exportSymbol('assertNotEquals', assertNotEquals);
    +goog.exportSymbol('assertNull', assertNull);
    +goog.exportSymbol('assertNotNull', assertNotNull);
    +goog.exportSymbol('assertUndefined', assertUndefined);
    +goog.exportSymbol('assertNotUndefined', assertNotUndefined);
    +goog.exportSymbol('assertNotNullNorUndefined', assertNotNullNorUndefined);
    +goog.exportSymbol('assertNonEmptyString', assertNonEmptyString);
    +goog.exportSymbol('assertNaN', assertNaN);
    +goog.exportSymbol('assertNotNaN', assertNotNaN);
    +goog.exportSymbol('assertObjectEquals', assertObjectEquals);
    +goog.exportSymbol('assertObjectRoughlyEquals', assertObjectRoughlyEquals);
    +goog.exportSymbol('assertObjectNotEquals', assertObjectNotEquals);
    +goog.exportSymbol('assertArrayEquals', assertArrayEquals);
    +goog.exportSymbol('assertElementsEquals', assertElementsEquals);
    +goog.exportSymbol('assertElementsRoughlyEqual', assertElementsRoughlyEqual);
    +goog.exportSymbol('assertSameElements', assertSameElements);
    +goog.exportSymbol('assertEvaluatesToTrue', assertEvaluatesToTrue);
    +goog.exportSymbol('assertEvaluatesToFalse', assertEvaluatesToFalse);
    +goog.exportSymbol('assertHTMLEquals', assertHTMLEquals);
    +goog.exportSymbol('assertHashEquals', assertHashEquals);
    +goog.exportSymbol('assertRoughlyEquals', assertRoughlyEquals);
    +goog.exportSymbol('assertContains', assertContains);
    +goog.exportSymbol('assertNotContains', assertNotContains);
    +goog.exportSymbol('assertRegExp', assertRegExp);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asserts_test.html b/mapbox-gl/closure-library/closure/goog/testing/asserts_test.html
    new file mode 100644
    index 0000000000..adb266db6a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asserts_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.asserts
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.assertsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asserts_test.js b/mapbox-gl/closure-library/closure/goog/testing/asserts_test.js
    new file mode 100644
    index 0000000000..bcf29a19c3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asserts_test.js
    @@ -0,0 +1,1181 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.assertsTest');
    +goog.setTestOnly('goog.testing.assertsTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.iter.Iterator');
    +goog.require('goog.iter.StopIteration');
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.string');
    +goog.require('goog.structs.Map');
    +goog.require('goog.structs.Set');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function testAssertTrue() {
    +  assertTrue(true);
    +  assertTrue('Good assertion', true);
    +  assertThrowsJsUnitException(
    +      function() { assertTrue(false); },
    +      'Call to assertTrue(boolean) with false');
    +  assertThrowsJsUnitException(
    +      function() { assertTrue('Should be true', false); },
    +      'Should be true\nCall to assertTrue(boolean) with false');
    +  assertThrowsJsUnitException(
    +      function() { assertTrue(null); },
    +      'Bad argument to assertTrue(boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertTrue(undefined); },
    +      'Bad argument to assertTrue(boolean)');
    +}
    +
    +function testAssertFalse() {
    +  assertFalse(false);
    +  assertFalse('Good assertion', false);
    +  assertThrowsJsUnitException(
    +      function() { assertFalse(true); },
    +      'Call to assertFalse(boolean) with true');
    +  assertThrowsJsUnitException(
    +      function() { assertFalse('Should be false', true); },
    +      'Should be false\nCall to assertFalse(boolean) with true');
    +  assertThrowsJsUnitException(
    +      function() { assertFalse(null); },
    +      'Bad argument to assertFalse(boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertFalse(undefined); },
    +      'Bad argument to assertFalse(boolean)');
    +}
    +
    +function testAssertEqualsWithString() {
    +  assertEquals('a', 'a');
    +  assertEquals('Good assertion', 'a', 'a');
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('a', 'b'); },
    +      'Expected <a> (String) but was <b> (String)');
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('Bad assertion', 'a', 'b'); },
    +      'Bad assertion\nExpected <a> (String) but was <b> (String)');
    +}
    +
    +function testAssertEqualsWithInteger() {
    +  assertEquals(1, 1);
    +  assertEquals('Good assertion', 1, 1);
    +  assertThrowsJsUnitException(
    +      function() { assertEquals(1, 2); },
    +      'Expected <1> (Number) but was <2> (Number)');
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('Bad assertion', 1, 2); },
    +      'Bad assertion\nExpected <1> (Number) but was <2> (Number)');
    +}
    +
    +function testAssertNotEquals() {
    +  assertNotEquals('a', 'b');
    +  assertNotEquals('a', 'a', 'b');
    +  assertThrowsJsUnitException(
    +      function() { assertNotEquals('a', 'a'); },
    +      'Expected not to be <a> (String)');
    +  assertThrowsJsUnitException(
    +      function() { assertNotEquals('a', 'a', 'a'); },
    +      'a\nExpected not to be <a> (String)');
    +}
    +
    +function testAssertNull() {
    +  assertNull(null);
    +  assertNull('Good assertion', null);
    +  assertThrowsJsUnitException(
    +      function() { assertNull(true); },
    +      'Expected <null> but was <true> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertNull('Should be null', false); },
    +      'Should be null\nExpected <null> but was <false> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertNull(undefined); },
    +      'Expected <null> but was <undefined>');
    +  assertThrowsJsUnitException(
    +      function() { assertNull(1); },
    +      'Expected <null> but was <1> (Number)');
    +}
    +
    +function testAssertNotNull() {
    +  assertNotNull(true);
    +  assertNotNull('Good assertion', true);
    +  assertNotNull(false);
    +  assertNotNull(undefined);
    +  assertNotNull(1);
    +  assertNotNull('a');
    +  assertThrowsJsUnitException(
    +      function() { assertNotNull(null); },
    +      'Expected not to be <null>');
    +  assertThrowsJsUnitException(
    +      function() { assertNotNull('Should not be null', null); },
    +      'Should not be null\nExpected not to be <null>');
    +}
    +
    +function testAssertUndefined() {
    +  assertUndefined(undefined);
    +  assertUndefined('Good assertion', undefined);
    +  assertThrowsJsUnitException(
    +      function() { assertUndefined(true); },
    +      'Expected <undefined> but was <true> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertUndefined('Should be undefined', false); },
    +      'Should be undefined\nExpected <undefined> but was <false> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertUndefined(null); },
    +      'Expected <undefined> but was <null>');
    +  assertThrowsJsUnitException(
    +      function() { assertUndefined(1); },
    +      'Expected <undefined> but was <1> (Number)');
    +}
    +
    +function testAssertNotUndefined() {
    +  assertNotUndefined(true);
    +  assertNotUndefined('Good assertion', true);
    +  assertNotUndefined(false);
    +  assertNotUndefined(null);
    +  assertNotUndefined(1);
    +  assertNotUndefined('a');
    +  assertThrowsJsUnitException(
    +      function() { assertNotUndefined(undefined); },
    +      'Expected not to be <undefined>');
    +  assertThrowsJsUnitException(
    +      function() { assertNotUndefined('Should not be undefined', undefined); },
    +      'Should not be undefined\nExpected not to be <undefined>');
    +}
    +
    +function testAssertNotNullNorUndefined() {
    +  assertNotNullNorUndefined(true);
    +  assertNotNullNorUndefined('Good assertion', true);
    +  assertNotNullNorUndefined(false);
    +  assertNotNullNorUndefined(1);
    +  assertNotNullNorUndefined(0);
    +  assertNotNullNorUndefined('a');
    +  assertThrowsJsUnitException(function() {
    +    assertNotNullNorUndefined(undefined);
    +  }, 'Expected not to be <undefined>');
    +  assertThrowsJsUnitException(function() {
    +    assertNotNullNorUndefined('Should not be undefined', undefined);
    +  }, 'Should not be undefined\nExpected not to be <undefined>');
    +  assertThrowsJsUnitException(function() {
    +    assertNotNullNorUndefined(null);
    +  }, 'Expected not to be <null>');
    +  assertThrowsJsUnitException(function() {
    +    assertNotNullNorUndefined('Should not be null', null);
    +  }, 'Should not be null\nExpected not to be <null>');
    +}
    +
    +function testAssertNonEmptyString() {
    +  assertNonEmptyString('hello');
    +  assertNonEmptyString('Good assertion', 'hello');
    +  assertNonEmptyString('true');
    +  assertNonEmptyString('false');
    +  assertNonEmptyString('1');
    +  assertNonEmptyString('null');
    +  assertNonEmptyString('undefined');
    +  assertNonEmptyString('\n');
    +  assertNonEmptyString(' ');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(''); },
    +      'Expected non-empty string but was <> (String)');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString('Should be non-empty string', ''); },
    +      'Should be non-empty string\n' +
    +      'Expected non-empty string but was <> (String)');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(true); },
    +      'Expected non-empty string but was <true> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(false); },
    +      'Expected non-empty string but was <false> (Boolean)');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(1); },
    +      'Expected non-empty string but was <1> (Number)');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(null); },
    +      'Expected non-empty string but was <null>');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(undefined); },
    +      'Expected non-empty string but was <undefined>');
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(['hello']); },
    +      'Expected non-empty string but was <hello> (Array)');
    +  // Different browsers return different values/types in the failure message
    +  // so don't bother checking if the message is exactly as expected.
    +  assertThrowsJsUnitException(
    +      function() { assertNonEmptyString(goog.dom.createTextNode('hello')); });
    +}
    +
    +function testAssertNaN() {
    +  assertNaN(NaN);
    +  assertNaN('Good assertion', NaN);
    +  assertThrowsJsUnitException(
    +      function() { assertNaN(1); }, 'Expected NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNaN('Should be NaN', 1); },
    +      'Should be NaN\nExpected NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNaN(true); }, 'Expected NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNaN(false); }, 'Expected NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNaN(null); }, 'Expected NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNaN(''); }, 'Expected NaN');
    +
    +  // TODO(user): These assertions fail. We should decide on the
    +  // semantics of assertNaN
    +  //assertThrowsJsUnitException(function() { assertNaN(undefined); },
    +  //    'Expected NaN');
    +  //assertThrowsJsUnitException(function() { assertNaN('a'); },
    +  //    'Expected NaN');
    +}
    +
    +function testAssertNotNaN() {
    +  assertNotNaN(1);
    +  assertNotNaN('Good assertion', 1);
    +  assertNotNaN(true);
    +  assertNotNaN(false);
    +  assertNotNaN('');
    +  assertNotNaN(null);
    +
    +  // TODO(user): These assertions fail. We should decide on the
    +  // semantics of assertNotNaN
    +  //assertNotNaN(undefined);
    +  //assertNotNaN('a');
    +
    +  assertThrowsJsUnitException(
    +      function() { assertNotNaN(Number.NaN); },
    +      'Expected not NaN');
    +  assertThrowsJsUnitException(
    +      function() { assertNotNaN('Should not be NaN', Number.NaN); },
    +      'Should not be NaN\nExpected not NaN');
    +}
    +
    +function testAssertObjectEquals() {
    +  var obj1 = [{'a': 'hello', 'b': 'world'}];
    +  var obj2 = [{'a': 'hello', 'c': 'dear', 'b' : 'world'}];
    +
    +  // Check with obj1 and obj2 as first and second arguments respectively.
    +  assertThrows('Objects should not be equal', function() {
    +    assertObjectEquals(obj1, obj2);
    +  });
    +
    +  // Check with obj1 and obj2 as second and first arguments respectively.
    +  assertThrows('Objects should not be equal', function() {
    +    assertObjectEquals(obj2, obj1);
    +  });
    +
    +  // Test if equal objects are considered equal.
    +  var obj3 = [{'b': 'world', 'a': 'hello'}];
    +  assertObjectEquals(obj1, obj3);
    +  assertObjectEquals(obj3, obj1);
    +
    +  // Test with a case where one of the members has an undefined value.
    +  var obj4 = [{'a': 'hello', 'b': undefined}];
    +  var obj5 = [{'a': 'hello'}];
    +
    +  // Check with obj4 and obj5 as first and second arguments respectively.
    +  assertThrows('Objects should not be equal', function() {
    +    assertObjectEquals(obj4, obj5);
    +  });
    +
    +  // Check with obj5 and obj4 as first and second arguments respectively.
    +  assertThrows('Objects should not be equal', function() {
    +    assertObjectEquals(obj5, obj4);
    +  });
    +}
    +
    +function testAssertObjectNotEquals() {
    +  var obj1 = [{'a': 'hello', 'b': 'world'}];
    +  var obj2 = [{'a': 'hello', 'c': 'dear', 'b' : 'world'}];
    +
    +  // Check with obj1 and obj2 as first and second arguments respectively.
    +  assertObjectNotEquals(obj1, obj2);
    +
    +  // Check with obj1 and obj2 as second and first arguments respectively.
    +  assertObjectNotEquals(obj2, obj1);
    +
    +  // Test if equal objects are considered equal.
    +  var obj3 = [{'b': 'world', 'a': 'hello'}];
    +  var error = assertThrows('Objects should be equal', function() {
    +    assertObjectNotEquals(obj1, obj3);
    +  });
    +  assertContains('Objects should not be equal', error.message);
    +  error = assertThrows('Objects should be equal', function() {
    +    assertObjectNotEquals(obj3, obj1);
    +  });
    +  assertContains('Objects should not be equal', error.message);
    +
    +  // Test with a case where one of the members has an undefined value.
    +  var obj4 = [{'a': 'hello', 'b': undefined}];
    +  var obj5 = [{'a': 'hello'}];
    +
    +  // Check with obj4 and obj5 as first and second arguments respectively.
    +  assertObjectNotEquals(obj4, obj5);
    +
    +  // Check with obj5 and obj4 as first and second arguments respectively.
    +  assertObjectNotEquals(obj5, obj4);
    +}
    +
    +function testAssertObjectEquals2() {
    +  // NOTE: (0 in [undefined]) is true on FF but false on IE.
    +  // (0 in {'0': undefined}) is true on both.
    +  // grrr.
    +  assertObjectEquals('arrays should be equal', [undefined], [undefined]);
    +  assertThrows('arrays should not be equal', function() {
    +    assertObjectEquals([undefined, undefined], [undefined]);
    +  });
    +  assertThrows('arrays should not be equal', function() {
    +    assertObjectEquals([undefined], [undefined, undefined]);
    +  });
    +}
    +
    +function testAssertObjectEquals3() {
    +  // Check that objects that contain identical Map objects compare
    +  // as equals. We can't do a negative test because on browsers that
    +  // implement __iterator__ we can't check the values of the iterated
    +  // properties.
    +  var obj1 = [{'a': 'hi',
    +               'b': new goog.structs.Map('hola', 'amigo',
    +                                         'como', 'estas?')},
    +              14,
    +              'yes',
    +              true];
    +  var obj2 = [{'a': 'hi',
    +               'b': new goog.structs.Map('hola', 'amigo',
    +                                         'como', 'estas?')},
    +              14,
    +              'yes',
    +              true];
    +  assertObjectEquals('Objects should be equal', obj1, obj2);
    +
    +  var obj3 = {'a': [1, 2]};
    +  var obj4 = {'a': [1, 2, 3]};
    +  assertThrows('inner arrays should not be equal', function() {
    +    assertObjectEquals(obj3, obj4);
    +  });
    +  assertThrows('inner arrays should not be equal', function() {
    +    assertObjectEquals(obj4, obj3);
    +  });
    +}
    +
    +function testAssertObjectEqualsSet() {
    +  // verify that Sets compare equal, when run in an environment that
    +  // supports iterators
    +  var set1 = new goog.structs.Set();
    +  var set2 = new goog.structs.Set();
    +
    +  set1.add('a');
    +  set1.add('b');
    +  set1.add(13);
    +
    +  set2.add('a');
    +  set2.add('b');
    +  set2.add(13);
    +
    +  assertObjectEquals('sets should be equal', set1, set2);
    +
    +  set2.add('hey');
    +  assertThrows('sets should not be equal', function() {
    +    assertObjectEquals(set1, set2);
    +  });
    +}
    +
    +function testAssertObjectEqualsIterNoEquals() {
    +  // an object with an iterator but no equals() and no map_ cannot
    +  // be compared
    +  function Thing() {
    +    this.what = [];
    +  }
    +  Thing.prototype.add = function(n, v) {
    +    this.what.push(n + '@' + v);
    +  };
    +  Thing.prototype.get = function(n) {
    +    var m = new RegExp('^' + n + '@(.*)$', '');
    +    for (var i = 0; i < this.what.length; ++i) {
    +      var match = this.what[i].match(m);
    +      if (match) {
    +        return match[1];
    +      }
    +    }
    +    return null;
    +  };
    +  Thing.prototype.__iterator__ = function() {
    +    var iter = new goog.iter.Iterator;
    +    iter.index = 0;
    +    iter.thing = this;
    +    iter.next = function() {
    +      if (this.index < this.thing.what.length) {
    +        return this.thing.what[this.index++].split('@')[0];
    +      } else {
    +        throw goog.iter.StopIteration;
    +      }
    +    };
    +    return iter;
    +  };
    +
    +  var thing1 = new Thing(); thing1.name = 'thing1';
    +  var thing2 = new Thing(); thing2.name = 'thing2';
    +  thing1.add('red', 'fish');
    +  thing1.add('blue', 'fish');
    +
    +  thing2.add('red', 'fish');
    +  thing2.add('blue', 'fish');
    +
    +  assertThrows('things should not be equal', function() {
    +    assertObjectEquals(thing1, thing2);
    +  });
    +}
    +
    +function testAssertObjectEqualsWithDates() {
    +  var date = new Date(2010, 0, 1);
    +  var dateWithMilliseconds = new Date(2010, 0, 1, 0, 0, 0, 1);
    +  assertObjectEquals(new Date(2010, 0, 1), date);
    +  assertThrows(goog.partial(assertObjectEquals, date, dateWithMilliseconds));
    +}
    +
    +function testAssertObjectEqualsSparseArrays() {
    +  var arr1 = [, 2,, 4];
    +  var arr2 = [1, 2, 3, 4, 5];
    +
    +  assertThrows('Sparse arrays should not be equal', function() {
    +    assertObjectEquals(arr1, arr2);
    +  });
    +
    +  assertThrows('Sparse arrays should not be equal', function() {
    +    assertObjectEquals(arr2, arr1);
    +  });
    +}
    +
    +function testAssertObjectEqualsSparseArrays2() {
    +  // On IE6-8, the expression "1 in [4,undefined]" evaluates to false,
    +  // but true on other browsers. FML. This test verifies a regression
    +  // where IE reported that arr4 was not equal to arr1 or arr2.
    +  var arr1 = [1,, 3];
    +  var arr2 = [1, undefined, 3];
    +  var arr3 = goog.array.clone(arr1);
    +  var arr4 = [];
    +  arr4.push(1, undefined, 3);
    +
    +  // Assert that all those arrays are equivalent pairwise.
    +  var arrays = [arr1, arr2, arr3, arr4];
    +  for (var i = 0; i < arrays.length; i++) {
    +    for (var j = 0; j < arrays.length; j++) {
    +      assertArrayEquals(arrays[i], arrays[j]);
    +    }
    +  }
    +}
    +
    +function testAssertObjectEqualsArraysWithExtraProps() {
    +  var arr1 = [1];
    +  var arr2 = [1];
    +  arr2.foo = 3;
    +
    +  assertThrows('Aarrays should not be equal', function() {
    +    assertObjectEquals(arr1, arr2);
    +  });
    +
    +  assertThrows('Arrays should not be equal', function() {
    +    assertObjectEquals(arr2, arr1);
    +  });
    +}
    +
    +function testAssertSameElementsOnArray() {
    +  assertSameElements([1, 2], [2, 1]);
    +  assertSameElements('Good assertion', [1, 2], [2, 1]);
    +  assertSameElements('Good assertion with duplicates', [1, 1, 2], [2, 1, 1]);
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements([1, 2], [1]);
    +      },
    +      'Expected 2 elements: [1,2], got 1 elements: [1]');
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements('Should match', [1, 2], [1]);
    +      },
    +      'Should match\nExpected 2 elements: [1,2], got 1 elements: [1]');
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements([1, 2], [1, 3]);
    +      },
    +      'Expected [1,2], got [1,3]');
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements('Should match', [1, 2], [1, 3]);
    +      },
    +      'Should match\nExpected [1,2], got [1,3]');
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements([1, 1, 2], [2, 2, 1]);
    +      },
    +      'Expected [1,1,2], got [2,2,1]');
    +}
    +
    +function testAssertSameElementsOnArrayLike() {
    +  assertSameElements({0: 0, 1: 1, length: 2}, {length: 2, 1: 1, 0: 0});
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements({0: 0, 1: 1, length: 2}, {0: 0, length: 1});
    +      },
    +      'Expected 2 elements: [0,1], got 1 elements: [0]');
    +}
    +
    +function testAssertSameElementsWithBadArguments() {
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertSameElements([], new goog.structs.Set());
    +      },
    +      'Bad arguments to assertSameElements(opt_message, expected: ' +
    +      'ArrayLike, actual: ArrayLike)\n' +
    +      'Call to assertTrue(boolean) with false');
    +}
    +
    +var implicitlyTrue = [
    +  true,
    +  1,
    +  -1,
    +  ' ',
    +  'string',
    +  Infinity,
    +  new Object()
    +];
    +
    +var implicitlyFalse = [
    +  false,
    +  0,
    +  '',
    +  null,
    +  undefined,
    +  NaN
    +];
    +
    +function testAssertEvaluatesToTrue() {
    +  assertEvaluatesToTrue(true);
    +  assertEvaluatesToTrue('', true);
    +  assertEvaluatesToTrue('Good assertion', true);
    +  assertThrowsJsUnitException(
    +      function() { assertEvaluatesToTrue(false); },
    +      'Expected to evaluate to true');
    +  assertThrowsJsUnitException(
    +      function() {assertEvaluatesToTrue('Should be true', false); },
    +      'Should be true\nExpected to evaluate to true');
    +  for (var i = 0; i < implicitlyTrue.length; i++) {
    +    assertEvaluatesToTrue(String('Test ' + implicitlyTrue[i] +
    +        ' [' + i + ']'), implicitlyTrue[i]);
    +  }
    +  for (var i = 0; i < implicitlyFalse.length; i++) {
    +    assertThrowsJsUnitException(
    +        function() { assertEvaluatesToTrue(implicitlyFalse[i]); },
    +        'Expected to evaluate to true');
    +  }
    +}
    +
    +function testAssertEvaluatesToFalse() {
    +  assertEvaluatesToFalse(false);
    +  assertEvaluatesToFalse('Good assertion', false);
    +  assertThrowsJsUnitException(
    +      function() { assertEvaluatesToFalse(true); },
    +      'Expected to evaluate to false');
    +  assertThrowsJsUnitException(
    +      function() { assertEvaluatesToFalse('Should be false', true); },
    +      'Should be false\nExpected to evaluate to false');
    +  for (var i = 0; i < implicitlyFalse.length; i++) {
    +    assertEvaluatesToFalse(String('Test ' + implicitlyFalse[i] +
    +        ' [' + i + ']'), implicitlyFalse[i]);
    +  }
    +  for (var i = 0; i < implicitlyTrue.length; i++) {
    +    assertThrowsJsUnitException(
    +        function() { assertEvaluatesToFalse(implicitlyTrue[i]); },
    +        'Expected to evaluate to false');
    +  }
    +}
    +
    +function testAssertHTMLEquals() {
    +  // TODO
    +}
    +
    +function testAssertHashEquals() {
    +  assertHashEquals({a: 1, b: 2}, {b: 2, a: 1});
    +  assertHashEquals('Good assertion', {a: 1, b: 2}, {b: 2, a: 1});
    +  assertHashEquals({a: undefined}, {a: undefined});
    +  // Missing key.
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals({a: 1, b: 2}, {a: 1}); },
    +      'Expected hash had key b that was not found');
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals('Should match', {a: 1, b: 2}, {a: 1}); },
    +      'Should match\nExpected hash had key b that was not found');
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals({a: undefined}, {}); },
    +      'Expected hash had key a that was not found');
    +  // Not equal key.
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals({a: 1}, {a: 5}); },
    +      'Value for key a mismatch - expected = 1, actual = 5');
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals('Should match', {a: 1}, {a: 5}); },
    +      'Should match\nValue for key a mismatch - expected = 1, actual = 5');
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals({a: undefined}, {a: 1})},
    +      'Value for key a mismatch - expected = undefined, actual = 1');
    +  // Extra key.
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals({a: 1}, {a: 1, b: 1}); },
    +      'Actual hash had key b that was not expected');
    +  assertThrowsJsUnitException(
    +      function() { assertHashEquals('Should match', {a: 1}, {a: 1, b: 1}); },
    +      'Should match\nActual hash had key b that was not expected');
    +}
    +
    +function testAssertRoughlyEquals() {
    +  assertRoughlyEquals(1, 1, 0);
    +  assertRoughlyEquals('Good assertion', 1, 1, 0);
    +  assertRoughlyEquals(1, 1.1, 0.11);
    +  assertRoughlyEquals(1.1, 1, 0.11);
    +  assertThrowsJsUnitException(
    +      function() { assertRoughlyEquals(1, 1.1, 0.05); },
    +      'Expected 1, but got 1.1 which was more than 0.05 away');
    +  assertThrowsJsUnitException(
    +      function() { assertRoughlyEquals('Close enough', 1, 1.1, 0.05); },
    +      'Close enough\nExpected 1, but got 1.1 which was more than 0.05 away');
    +}
    +
    +function testAssertContains() {
    +  var a = [1, 2, 3];
    +  assertContains(1, [1, 2, 3]);
    +  assertContains('Should contain', 1, [1, 2, 3]);
    +  assertThrowsJsUnitException(
    +      function() { assertContains(4, [1, 2, 3]); },
    +      'Expected \'1,2,3\' to contain \'4\'');
    +  assertThrowsJsUnitException(
    +      function() { assertContains('Should contain', 4, [1, 2, 3]); },
    +      'Should contain\nExpected \'1,2,3\' to contain \'4\'');
    +  // assertContains uses ===.
    +  var o = new Object();
    +  assertContains(o, [o, 2, 3]);
    +  assertThrowsJsUnitException(
    +      function() { assertContains(o, [1, 2, 3]); },
    +      'Expected \'1,2,3\' to contain \'[object Object]\'');
    +}
    +
    +function testAssertNotContains() {
    +  var a = [1, 2, 3];
    +  assertNotContains(4, [1, 2, 3]);
    +  assertNotContains('Should not contain', 4, [1, 2, 3]);
    +  assertThrowsJsUnitException(
    +      function() { assertNotContains(1, [1, 2, 3]); },
    +      'Expected \'1,2,3\' not to contain \'1\'');
    +  assertThrowsJsUnitException(
    +      function() { assertNotContains('Should not contain', 1, [1, 2, 3]); },
    +      "Should not contain\nExpected '1,2,3' not to contain '1'");
    +  // assertNotContains uses ===.
    +  var o = new Object();
    +  assertNotContains({}, [o, 2, 3]);
    +  assertThrowsJsUnitException(
    +      function() { assertNotContains(o, [o, 2, 3]); },
    +      'Expected \'[object Object],2,3\' not to contain \'[object Object]\'');
    +}
    +
    +function testAssertRegExp() {
    +  var a = 'I like turtles';
    +  assertRegExp(/turtles$/, a);
    +  assertRegExp('turtles$', a);
    +  assertRegExp('Expected subject to be about turtles', /turtles$/, a);
    +  assertRegExp('Expected subject to be about turtles', 'turtles$', a);
    +
    +  var b = 'Hello';
    +  assertThrowsJsUnitException(
    +      function() { assertRegExp(/turtles$/, b); },
    +      'Expected \'Hello\' to match RegExp /turtles$/');
    +  assertThrowsJsUnitException(
    +      function() { assertRegExp('turtles$', b); },
    +      'Expected \'Hello\' to match RegExp /turtles$/');
    +}
    +
    +function testAssertThrows() {
    +  var failed = false;
    +  try {
    +    assertThrows('assertThrows should not pass with null param', null);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertThrows(
    +        'assertThrows should not pass with undefined param',
    +        undefined);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertThrows('assertThrows should not pass with number param', 1);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertThrows('assertThrows should not pass with string param', 'string');
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertThrows('assertThrows should not pass with object param', {});
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    var error = assertThrows('valid function throws Error', function() {
    +      throw new Error('test');
    +    });
    +  } catch (e) {
    +    fail('assertThrows incorrectly doesn\'t detect a thrown exception');
    +  }
    +  assertEquals('error message', 'test', error.message);
    +
    +  try {
    +    var stringError = assertThrows('valid function throws string error',
    +        function() {
    +          throw 'string error test';
    +        });
    +  } catch (e) {
    +    fail('assertThrows doesn\'t detect a thrown string exception');
    +  }
    +  assertEquals('string error', 'string error test', stringError);
    +}
    +
    +function testAssertNotThrows() {
    +  var failed = false;
    +  try {
    +    assertNotThrows('assertNotThrows should not pass with null param', null);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertNotThrows(
    +        'assertNotThrows should not pass with undefined param', undefined);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertNotThrows('assertNotThrows should not pass with number param', 1);
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +  try {
    +    assertNotThrows('assertNotThrows should not pass with string param',
    +        'string');
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +
    +  try {
    +    assertNotThrows('assertNotThrows should not pass with object param', {});
    +    failed = true;
    +  } catch (e) {
    +  }
    +  assertFalse('Fail should not get set to true', failed);
    +
    +
    +  var result;
    +  try {
    +    result = assertNotThrows('valid function',
    +        function() {
    +          return 'some value';
    +        });
    +  } catch (e) {
    +    // Shouldn't be here: throw exception.
    +    fail('assertNotThrows returned failure on a valid function');
    +  }
    +  assertEquals('assertNotThrows should return the result of the function.',
    +      'some value', result);
    +
    +  var errorDescription = 'a test error exception';
    +  try {
    +    assertNotThrows('non valid error throwing function',
    +        function() {
    +          throw new Error(errorDescription);
    +        });
    +    failed = true;
    +  } catch (e) {
    +    // Some browsers don't have a stack trace so expect to at least have the
    +    // error description. For Gecko and IE7 not even that is included.
    +    if (!goog.userAgent.GECKO &&
    +        (!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('8'))) {
    +      assertContains(errorDescription, e.message);
    +    }
    +  }
    +  assertFalse('assertNotThrows did not fail on a thrown exception', failed);
    +}
    +
    +function testAssertArrayEquals() {
    +  var a1 = [0, 1, 2];
    +  var a2 = [0, 1, 2];
    +  assertArrayEquals('Arrays should be equal', a1, a2);
    +
    +  assertThrows('Should have thrown because args are not arrays', function() {
    +    assertArrayEquals(true, true);
    +  });
    +
    +  a1 = [0, undefined, 2];
    +  a2 = [0, , 2];
    +  // The following test fails unexpectedly. The bug is tracked at
    +  // http://code.google.com/p/closure-library/issues/detail?id=419
    +  // assertThrows(
    +  //     'assertArrayEquals distinguishes undefined items from sparse arrays',
    +  //     function() {
    +  //       assertArrayEquals(a1, a2);
    +  //     });
    +
    +  // For the record. This behavior will probably change in the future.
    +  assertArrayEquals(
    +      'Bug: sparse arrays and undefined items are not distinguished',
    +      [0, undefined, 2], [0, , 2]);
    +
    +  assertThrows('The array elements should be compared with ===', function() {
    +    assertArrayEquals([0], ['0']);
    +  });
    +
    +  assertThrows('Arrays with different length should be different',
    +      function() {
    +        assertArrayEquals([0, undefined], [0]);
    +      });
    +
    +  a1 = [0];
    +  a2 = [0];
    +  a2[-1] = -1;
    +  assertArrayEquals('Negative indexes are ignored', a1, a2);
    +
    +  a1 = [0];
    +  a2 = [0];
    +  a2['extra'] = 1;
    +  assertArrayEquals(
    +      'Extra properties are ignored. Use assertObjectEquals to compare them.',
    +      a1, a2);
    +
    +  assertArrayEquals('An example where assertObjectEquals would fail in IE.',
    +      ['x'], 'x'.match(/x/g));
    +}
    +
    +function testAssertObjectsEqualsDifferentArrays() {
    +  assertThrows('Should have thrown because args are different', function() {
    +    var a1 = ['className1'];
    +    var a2 = ['className2'];
    +    assertObjectEquals(a1, a2);
    +  });
    +}
    +
    +function testAssertObjectsEqualsNegativeArrayIndexes() {
    +  var a1 = [0];
    +  var a2 = [0];
    +  a2[-1] = -1;
    +  // The following test fails unexpectedly. The bug is tracked at
    +  // http://code.google.com/p/closure-library/issues/detail?id=418
    +  // assertThrows('assertObjectEquals compares negative indexes', function() {
    +  //   assertObjectEquals(a1, a2);
    +  // });
    +}
    +
    +function testAssertObjectsEqualsDifferentTypeSameToString() {
    +  assertThrows('Should have thrown because args are different', function() {
    +    var a1 = 'className1';
    +    var a2 = ['className1'];
    +    assertObjectEquals(a1, a2);
    +  });
    +
    +  assertThrows('Should have thrown because args are different', function() {
    +    var a1 = ['className1'];
    +    var a2 = {'0': 'className1'};
    +    assertObjectEquals(a1, a2);
    +  });
    +
    +  assertThrows('Should have thrown because args are different', function() {
    +    var a1 = ['className1'];
    +    var a2 = [['className1']];
    +    assertObjectEquals(a1, a2);
    +  });
    +}
    +
    +function testAssertObjectsRoughlyEquals() {
    +  assertObjectRoughlyEquals({'a': 1}, {'a': 1.2}, 0.3);
    +  assertThrowsJsUnitException(
    +      function() { assertObjectRoughlyEquals({'a': 1}, {'a': 1.2}, 0.1); },
    +      'Expected <[object Object]> (Object) but was <[object Object]> ' +
    +      '(Object)\n   a: Expected <1> (Number) but was <1.2> (Number) which ' +
    +      'was more than 0.1 away');
    +}
    +
    +function testFindDifferences_equal() {
    +  assertNull(goog.testing.asserts.findDifferences(true, true));
    +  assertNull(goog.testing.asserts.findDifferences(null, null));
    +  assertNull(goog.testing.asserts.findDifferences(undefined, undefined));
    +  assertNull(goog.testing.asserts.findDifferences(1, 1));
    +  assertNull(goog.testing.asserts.findDifferences([1, 'a'], [1, 'a']));
    +  assertNull(goog.testing.asserts.findDifferences(
    +      [[1, 2], [3, 4]], [[1, 2], [3, 4]]));
    +  assertNull(goog.testing.asserts.findDifferences(
    +      [{a: 1, b: 2}], [{b: 2, a: 1}]));
    +  assertNull(goog.testing.asserts.findDifferences(null, null));
    +  assertNull(goog.testing.asserts.findDifferences(undefined, undefined));
    +}
    +
    +function testFindDifferences_unequal() {
    +  assertNotNull(goog.testing.asserts.findDifferences(true, false));
    +  assertNotNull(goog.testing.asserts.findDifferences(
    +      [{a: 1, b: 2}], [{a: 2, b: 1}]));
    +  assertNotNull(goog.testing.asserts.findDifferences(
    +      [{a: 1}], [{a: 1, b: [2]}]));
    +  assertNotNull(goog.testing.asserts.findDifferences(
    +      [{a: 1, b: [2]}], [{a: 1}]));
    +}
    +
    +function testFindDifferences_objectsAndNull() {
    +  assertNotNull(goog.testing.asserts.findDifferences({a: 1}, null));
    +  assertNotNull(goog.testing.asserts.findDifferences(null, {a: 1}));
    +  assertNotNull(goog.testing.asserts.findDifferences(null, []));
    +  assertNotNull(goog.testing.asserts.findDifferences([], null));
    +  assertNotNull(goog.testing.asserts.findDifferences([], undefined));
    +}
    +
    +function testFindDifferences_basicCycle() {
    +  var a = {};
    +  var b = {};
    +  a.self = a;
    +  b.self = b;
    +  assertNull(goog.testing.asserts.findDifferences(a, b));
    +
    +  a.unique = 1;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, b));
    +}
    +
    +function testFindDifferences_crossedCycle() {
    +  var a = {};
    +  var b = {};
    +  a.self = b;
    +  b.self = a;
    +  assertNull(goog.testing.asserts.findDifferences(a, b));
    +
    +  a.unique = 1;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, b));
    +}
    +
    +function testFindDifferences_asymmetricCycle() {
    +  var a = {};
    +  var b = {};
    +  var c = {};
    +  var d = {};
    +  var e = {};
    +  a.self = b;
    +  b.self = a;
    +  c.self = d;
    +  d.self = e;
    +  e.self = c;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, c));
    +}
    +
    +function testFindDifferences_basicCycleArray() {
    +  var a = [];
    +  var b = [];
    +  a[0] = a;
    +  b[0] = b;
    +  assertNull(goog.testing.asserts.findDifferences(a, b));
    +
    +  a[1] = 1;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, b));
    +}
    +
    +function testFindDifferences_crossedCycleArray() {
    +  var a = [];
    +  var b = [];
    +  a[0] = b;
    +  b[0] = a;
    +  assertNull(goog.testing.asserts.findDifferences(a, b));
    +
    +  a[1] = 1;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, b));
    +}
    +
    +function testFindDifferences_asymmetricCycleArray() {
    +  var a = [];
    +  var b = [];
    +  var c = [];
    +  var d = [];
    +  var e = [];
    +  a[0] = b;
    +  b[0] = a;
    +  c[0] = d;
    +  d[0] = e;
    +  e[0] = c;
    +  assertNotNull(goog.testing.asserts.findDifferences(a, c));
    +}
    +
    +function testFindDifferences_multiCycles() {
    +  var a = {};
    +  a.cycle1 = a;
    +  a.test = {
    +    cycle2: a
    +  };
    +
    +  var b = {};
    +  b.cycle1 = b;
    +  b.test = {
    +    cycle2: b
    +  };
    +  assertNull(goog.testing.asserts.findDifferences(a, b));
    +}
    +
    +function testFindDifferences_binaryTree() {
    +  function createBinTree(depth, root) {
    +    if (depth == 0) {
    +      return {root: root};
    +    } else {
    +      var node = {};
    +      node.left = createBinTree(depth - 1, root || node);
    +      node.right = createBinTree(depth - 1, root || node);
    +      return node;
    +    }
    +  }
    +
    +  // TODO(gboyer,user): This test does not terminate with the current
    +  // algorithm. Can be enabled when (if) the algorithm is improved.
    +  //assertNull(goog.testing.asserts.findDifferences(
    +  //    createBinTree(5, null), createBinTree(5, null)));
    +  assertNotNull(goog.testing.asserts.findDifferences(
    +      createBinTree(4, null), createBinTree(5, null)));
    +}
    +
    +function testStringForWindowIE() {
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
    +    // NOTE(user): This test sees of we are being affected by a JScript bug
    +    // in try/finally handling. This bug only affects the lowest try/finally
    +    // block in the stack. Calling this function via VBScript allows
    +    // us to run the test synchronously in an empty JS stack.
    +    window.execScript('stringForWindowIEHelper()', 'vbscript');
    +    assertEquals('<[object]> (Object)', window.stringForWindowIEResult);
    +  }
    +}
    +
    +function testStringSamePrefix() {
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('abcdefghi', 'abcdefghx'); },
    +      'Expected <abcdefghi> (String) but was <abcdefghx> (String)\n' +
    +      'Difference was at position 8. Expected [...ghi] vs. actual [...ghx]');
    +}
    +
    +function testStringSameSuffix() {
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('xbcdefghi', 'abcdefghi'); },
    +      'Expected <xbcdefghi> (String) but was <abcdefghi> (String)\n' +
    +      'Difference was at position 0. Expected [xbc...] vs. actual [abc...]');
    +}
    +
    +function testStringDissimilarShort() {
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('x', 'y'); },
    +      'Expected <x> (String) but was <y> (String)');
    +}
    +
    +function testStringDissimilarLong() {
    +  assertThrowsJsUnitException(
    +      function() { assertEquals('xxxxxxxxxx', 'yyyyyyyyyy'); },
    +      'Expected <xxxxxxxxxx> (String) but was <yyyyyyyyyy> (String)');
    +}
    +
    +function testAssertElementsEquals() {
    +  assertElementsEquals([1, 2], [1, 2]);
    +  assertElementsEquals([1, 2], {0: 1, 1: 2, length: 2});
    +  assertElementsEquals('Good assertion', [1, 2], [1, 2]);
    +  assertThrowsJsUnitException(
    +      function() {
    +        assertElementsEquals('Message', [1, 2], [1]);
    +      },
    +      'length mismatch: Message\n' +
    +      'Expected <2> (Number) but was <1> (Number)');
    +}
    +
    +function testStackTrace() {
    +  try {
    +    assertTrue(false);
    +  } catch (e) {
    +    if (Error.captureStackTrace) {
    +      assertNotUndefined(e.stack);
    +    }
    +    if (e.stack) {
    +      var stack = e.stack;
    +      var stackTraceContainsTestName = goog.string.contains(
    +          stack, 'testStackTrace');
    +      if (!stackTraceContainsTestName &&
    +          goog.labs.userAgent.browser.isChrome()) {
    +        // Occasionally Chrome does not give us a full stack trace, making for
    +        // a flaky test. If we don't have the full stack trace, at least
    +        // check that we got the error string.
    +        // Filed a bug on Chromium here:
    +        // https://code.google.com/p/chromium/issues/detail?id=403029
    +        var expected = 'Call to assertTrue(boolean) with false';
    +        assertContains(expected, stack);
    +        return;
    +      }
    +
    +      assertTrue('Expected the stack trace to contain string "testStackTrace"',
    +                 stackTraceContainsTestName);
    +    }
    +  }
    +}
    +
    +function stringForWindowIEHelper() {
    +  window.stringForWindowIEResult = _displayStringForValue(window);
    +}
    +
    +function testDisplayStringForValue() {
    +  assertEquals('<hello> (String)', _displayStringForValue('hello'));
    +  assertEquals('<1> (Number)', _displayStringForValue(1));
    +  assertEquals('<null>', _displayStringForValue(null));
    +  assertEquals('<undefined>', _displayStringForValue(undefined));
    +  assertEquals('<hello,,,,1> (Array)', _displayStringForValue(
    +      ['hello', /* array hole */, undefined, null, 1]));
    +}
    +
    +function testDisplayStringForValue_exception() {
    +  assertEquals('<toString failed: foo message> (Object)',
    +      _displayStringForValue({toString: function() {
    +        throw Error('foo message');
    +      }}));
    +}
    +
    +function testDisplayStringForValue_cycle() {
    +  var cycle = ['cycle'];
    +  cycle.push(cycle);
    +  assertTrue(
    +      'Computing string should terminate and result in a reasonable length',
    +      _displayStringForValue(cycle).length < 1000);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol.js b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol.js
    new file mode 100644
    index 0000000000..d85cfac08b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol.js
    @@ -0,0 +1,175 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A wrapper for MockControl that provides mocks and assertions
    + * for testing asynchronous code. All assertions will only be verified when
    + * $verifyAll is called on the wrapped MockControl.
    + *
    + * This class is meant primarily for testing code that exposes asynchronous APIs
    + * without being truly asynchronous (using asynchronous primitives like browser
    + * events or timeouts). This is often the case when true asynchronous
    + * depedencies have been mocked out. This means that it doesn't rely on
    + * AsyncTestCase or DeferredTestCase, although it can be used with those as
    + * well.
    + *
    + * Example usage:
    + *
    + * <pre>
    + * var mockControl = new goog.testing.MockControl();
    + * var asyncMockControl = new goog.testing.async.MockControl(mockControl);
    + *
    + * myAsyncObject.onSuccess(asyncMockControl.asyncAssertEquals(
    + *     'callback should run and pass the correct value',
    + *     'http://someurl.com');
    + * asyncMockControl.assertDeferredEquals(
    + *     'deferred object should be resolved with the correct value',
    + *     'http://someurl.com',
    + *     myAsyncObject.getDeferredUrl());
    + * asyncMockControl.run();
    + * mockControl.$verifyAll();
    + * </pre>
    + *
    + */
    +
    +
    +goog.provide('goog.testing.async.MockControl');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.debug');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.mockmatchers.IgnoreArgument');
    +
    +
    +
    +/**
    + * Provides asynchronous mocks and assertions controlled by a parent
    + * MockControl.
    + *
    + * @param {goog.testing.MockControl} mockControl The parent MockControl.
    + * @constructor
    + * @final
    + */
    +goog.testing.async.MockControl = function(mockControl) {
    +  /**
    +   * The parent MockControl.
    +   * @type {goog.testing.MockControl}
    +   * @private
    +   */
    +  this.mockControl_ = mockControl;
    +};
    +
    +
    +/**
    + * Returns a function that will assert that it will be called, and run the given
    + * callback when it is.
    + *
    + * @param {string} name The name of the callback mock.
    + * @param {function(...*) : *} callback The wrapped callback. This will be
    + *     called when the returned function is called.
    + * @param {Object=} opt_selfObj The object which this should point to when the
    + *     callback is run.
    + * @return {!Function} The mock callback.
    + * @suppress {missingProperties} Mocks do not fit in the type system well.
    + */
    +goog.testing.async.MockControl.prototype.createCallbackMock = function(
    +    name, callback, opt_selfObj) {
    +  goog.asserts.assert(
    +      goog.isString(name),
    +      'name parameter ' + goog.debug.deepExpose(name) + ' should be a string');
    +
    +  var ignored = new goog.testing.mockmatchers.IgnoreArgument();
    +
    +  // Use everyone's favorite "double-cast" trick to subvert the type system.
    +  var obj = /** @type {Object} */ (this.mockControl_.createFunctionMock(name));
    +  var fn = /** @type {Function} */ (obj);
    +
    +  fn(ignored).$does(function(args) {
    +    if (opt_selfObj) {
    +      callback = goog.bind(callback, opt_selfObj);
    +    }
    +    return callback.apply(this, args);
    +  });
    +  fn.$replay();
    +  return function() { return fn(arguments); };
    +};
    +
    +
    +/**
    + * Returns a function that will assert that its arguments are equal to the
    + * arguments given to asyncAssertEquals. In addition, the function also asserts
    + * that it will be called.
    + *
    + * @param {string} message A message to print if the arguments are wrong.
    + * @param {...*} var_args The arguments to assert.
    + * @return {function(...*) : void} The mock callback.
    + */
    +goog.testing.async.MockControl.prototype.asyncAssertEquals = function(
    +    message, var_args) {
    +  var expectedArgs = Array.prototype.slice.call(arguments, 1);
    +  return this.createCallbackMock('asyncAssertEquals', function() {
    +    assertObjectEquals(
    +        message, expectedArgs, Array.prototype.slice.call(arguments));
    +  });
    +};
    +
    +
    +/**
    + * Asserts that a deferred object will have an error and call its errback
    + * function.
    + * @param {goog.async.Deferred} deferred The deferred object.
    + * @param {function() : void} fn A function wrapping the code in which the error
    + *     will occur.
    + */
    +goog.testing.async.MockControl.prototype.assertDeferredError = function(
    +    deferred, fn) {
    +  deferred.addErrback(this.createCallbackMock(
    +      'assertDeferredError', function() {}));
    +  goog.testing.asserts.callWithoutLogging(fn);
    +};
    +
    +
    +/**
    + * Asserts that a deferred object will call its callback with the given value.
    + *
    + * @param {string} message A message to print if the arguments are wrong.
    + * @param {goog.async.Deferred|*} expected The expected value. If this is a
    + *     deferred object, then the expected value is the deferred value.
    + * @param {goog.async.Deferred|*} actual The actual value. If this is a deferred
    + *     object, then the actual value is the deferred value. Either this or
    + *     'expected' must be deferred.
    + */
    +goog.testing.async.MockControl.prototype.assertDeferredEquals = function(
    +    message, expected, actual) {
    +  if (expected instanceof goog.async.Deferred &&
    +      actual instanceof goog.async.Deferred) {
    +    // Assert that the first deferred is resolved.
    +    expected.addCallback(this.createCallbackMock(
    +        'assertDeferredEquals', function(exp) {
    +          // Assert that the second deferred is resolved, and that the value is
    +          // as expected.
    +          actual.addCallback(this.asyncAssertEquals(message, exp));
    +        }, this));
    +  } else if (expected instanceof goog.async.Deferred) {
    +    expected.addCallback(this.createCallbackMock(
    +        'assertDeferredEquals', function(exp) {
    +          assertObjectEquals(message, exp, actual);
    +        }));
    +  } else if (actual instanceof goog.async.Deferred) {
    +    actual.addCallback(this.asyncAssertEquals(message, expected));
    +  } else {
    +    throw Error('Either expected or actual must be deferred');
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.html b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.html
    new file mode 100644
    index 0000000000..6467dc063c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.async.MockControl;
    +  </title>
    +  <script type="text/javascript" src="../../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.testing.async.MockControlTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.js b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.js
    new file mode 100644
    index 0000000000..bb496005e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/async/mockcontrol_test.js
    @@ -0,0 +1,217 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.async.MockControlTest');
    +goog.setTestOnly('goog.testing.async.MockControlTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.async.MockControl');
    +goog.require('goog.testing.jsunit');
    +
    +var mockControl;
    +var asyncMockControl;
    +
    +var mockControl2;
    +var asyncMockControl2;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  asyncMockControl = new goog.testing.async.MockControl(mockControl);
    +
    +  // We need two of these for the tests where we need to verify our meta-test
    +  // assertions without verifying our tested assertions.
    +  mockControl2 = new goog.testing.MockControl();
    +  asyncMockControl2 = new goog.testing.async.MockControl(mockControl2);
    +}
    +
    +function assertVerifyFails() {
    +  assertThrowsJsUnitException(function() { mockControl.$verifyAll(); });
    +}
    +
    +function testCreateCallbackMockFailure() {
    +  asyncMockControl.createCallbackMock('failingCallbackMock', function() {});
    +  assertVerifyFails();
    +}
    +
    +function testCreateCallbackMockSuccess() {
    +  var callback = asyncMockControl.createCallbackMock(
    +      'succeedingCallbackMock', function() {});
    +  callback();
    +  mockControl.$verifyAll();
    +}
    +
    +function testCreateCallbackMockSuccessWithArg() {
    +  var callback = asyncMockControl.createCallbackMock(
    +      'succeedingCallbackMockWithArg',
    +      asyncMockControl.createCallbackMock(
    +          'metaCallbackMock',
    +          function(val) { assertEquals(10, val); }));
    +  callback(10);
    +  mockControl.$verifyAll();
    +}
    +
    +function testCreateCallbackMockSuccessWithArgs() {
    +  var callback = asyncMockControl.createCallbackMock(
    +      'succeedingCallbackMockWithArgs',
    +      asyncMockControl.createCallbackMock(
    +          'metaCallbackMock', function(val1, val2, val3) {
    +            assertEquals(10, val1);
    +            assertEquals('foo', val2);
    +            assertObjectEquals({foo: 'bar'}, val3);
    +          }));
    +  callback(10, 'foo', {foo: 'bar'});
    +  mockControl.$verifyAll();
    +}
    +
    +function testAsyncAssertEqualsFailureNeverCalled() {
    +  asyncMockControl.asyncAssertEquals('never called', 12);
    +  assertVerifyFails();
    +}
    +
    +function testAsyncAssertEqualsFailureNumberOfArgs() {
    +  assertThrowsJsUnitException(function() {
    +    asyncMockControl.asyncAssertEquals('wrong number of args', 12)();
    +  });
    +}
    +
    +function testAsyncAssertEqualsFailureOneArg() {
    +  assertThrowsJsUnitException(function() {
    +    asyncMockControl.asyncAssertEquals('wrong arg value', 12)(13);
    +  });
    +}
    +
    +function testAsyncAssertEqualsFailureThreeArgs() {
    +  assertThrowsJsUnitException(function() {
    +    asyncMockControl.asyncAssertEquals('wrong arg values', 1, 2, 15)(2, 2, 15);
    +  });
    +}
    +
    +function testAsyncAssertEqualsSuccessNoArgs() {
    +  asyncMockControl.asyncAssertEquals('should be called')();
    +  mockControl.$verifyAll();
    +}
    +
    +function testAsyncAssertEqualsSuccessThreeArgs() {
    +  asyncMockControl.asyncAssertEquals('should have args', 1, 2, 3)(1, 2, 3);
    +  mockControl.$verifyAll();
    +}
    +
    +function testAssertDeferredErrorFailureNoError() {
    +  var deferred = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredError(deferred, function() {});
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredErrorSuccess() {
    +  var deferred = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredError(deferred, function() {
    +    deferred.errback(new Error('FAIL'));
    +  });
    +  mockControl.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsFailureActualDeferredNeverResolves() {
    +  var actual = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('doesn\'t resolve', 12, actual);
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredEqualsFailureActualDeferredNeverResolvesBoth() {
    +  var actualDeferred = new goog.async.Deferred();
    +  var expectedDeferred = new goog.async.Deferred();
    +  expectedDeferred.callback(12);
    +  asyncMockControl.assertDeferredEquals(
    +      'doesn\'t resolve', expectedDeferred, actualDeferred);
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredEqualsFailureExpectedDeferredNeverResolves() {
    +  var expected = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('doesn\'t resolve', expected, 12);
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredEqualsFailureExpectedDeferredNeverResolvesBoth() {
    +  var actualDeferred = new goog.async.Deferred();
    +  var expectedDeferred = new goog.async.Deferred();
    +  actualDeferred.callback(12);
    +  asyncMockControl.assertDeferredEquals(
    +      'doesn\'t resolve', expectedDeferred, actualDeferred);
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredEqualsFailureWrongValueActualDeferred() {
    +  var actual = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('doesn\'t resolve', 12, actual);
    +  asyncMockControl2.assertDeferredError(actual, function() {
    +    actual.callback(13);
    +  });
    +  mockControl2.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsFailureWrongValueExpectedDeferred() {
    +  var expected = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('doesn\'t resolve', expected, 12);
    +  asyncMockControl2.assertDeferredError(expected, function() {
    +    expected.callback(13);
    +  });
    +  mockControl2.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsFailureWongValueBothDeferred() {
    +  var actualDeferred = new goog.async.Deferred();
    +  var expectedDeferred = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals(
    +      'different values', expectedDeferred, actualDeferred);
    +  expectedDeferred.callback(12);
    +  asyncMockControl2.assertDeferredError(actualDeferred, function() {
    +    actualDeferred.callback(13);
    +  });
    +  assertVerifyFails();
    +  mockControl2.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsFailureNeitherDeferredEverResolves() {
    +  var actualDeferred = new goog.async.Deferred();
    +  var expectedDeferred = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals(
    +      'doesn\'t resolve', expectedDeferred, actualDeferred);
    +  assertVerifyFails();
    +}
    +
    +function testAssertDeferredEqualsSuccessActualDeferred() {
    +  var actual = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('should succeed', 12, actual);
    +  actual.callback(12);
    +  mockControl.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsSuccessExpectedDeferred() {
    +  var expected = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals('should succeed', expected, 12);
    +  expected.callback(12);
    +  mockControl.$verifyAll();
    +}
    +
    +function testAssertDeferredEqualsSuccessBothDeferred() {
    +  var actualDeferred = new goog.async.Deferred();
    +  var expectedDeferred = new goog.async.Deferred();
    +  asyncMockControl.assertDeferredEquals(
    +      'should succeed', expectedDeferred, actualDeferred);
    +  expectedDeferred.callback(12);
    +  actualDeferred.callback(12);
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase.js b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase.js
    new file mode 100644
    index 0000000000..b915eddd59
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase.js
    @@ -0,0 +1,900 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved.
    +
    +/**
    + * @fileoverview A class representing a set of test functions that use
    + * asynchronous functions that cannot be meaningfully mocked.
    + *
    + * To create a Google-compatable JsUnit test using this test case, put the
    + * following snippet in your test:
    + *
    + *   var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    + *
    + * To make the test runner wait for your asynchronous behaviour, use:
    + *
    + *   asyncTestCase.waitForAsync('Waiting for xhr to respond');
    + *
    + * The next test will not start until the following call is made, or a
    + * timeout occurs:
    + *
    + *   asyncTestCase.continueTesting();
    + *
    + * There does NOT need to be a 1:1 mapping of waitForAsync calls and
    + * continueTesting calls. The next test will be run after a single call to
    + * continueTesting is made, as long as there is no subsequent call to
    + * waitForAsync in the same thread.
    + *
    + * Example:
    + *   // Returning here would cause the next test to be run.
    + *   asyncTestCase.waitForAsync('description 1');
    + *   // Returning here would *not* cause the next test to be run.
    + *   // Only effect of additional waitForAsync() calls is an updated
    + *   // description in the case of a timeout.
    + *   asyncTestCase.waitForAsync('updated description');
    + *   asyncTestCase.continueTesting();
    + *   // Returning here would cause the next test to be run.
    + *   asyncTestCase.waitForAsync('just kidding, still running.');
    + *   // Returning here would *not* cause the next test to be run.
    + *
    + * The test runner can also be made to wait for more than one asynchronous
    + * event with:
    + *
    + *   asyncTestCase.waitForSignals(n);
    + *
    + * The next test will not start until asyncTestCase.signal() is called n times,
    + * or the test step timeout is exceeded.
    + *
    + * This class supports asynchronous behaviour in all test functions except for
    + * tearDownPage. If such support is needed, it can be added.
    + *
    + * Example Usage:
    + *
    + *   var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    + *   // Optionally, set a longer-than-normal step timeout.
    + *   asyncTestCase.stepTimeout = 30 * 1000;
    + *
    + *   function testSetTimeout() {
    + *     var step = 0;
    + *     function stepCallback() {
    + *       step++;
    + *       switch (step) {
    + *         case 1:
    + *           var startTime = goog.now();
    + *           asyncTestCase.waitForAsync('step 1');
    + *           window.setTimeout(stepCallback, 100);
    + *           break;
    + *         case 2:
    + *           assertTrue('Timeout fired too soon',
    + *               goog.now() - startTime >= 100);
    + *           asyncTestCase.waitForAsync('step 2');
    + *           window.setTimeout(stepCallback, 100);
    + *           break;
    + *         case 3:
    + *           assertTrue('Timeout fired too soon',
    + *               goog.now() - startTime >= 200);
    + *           asyncTestCase.continueTesting();
    + *           break;
    + *         default:
    + *           fail('Unexpected call to stepCallback');
    + *       }
    + *     }
    + *     stepCallback();
    + *   }
    + *
    + * Known Issues:
    + *   IE7 Exceptions:
    + *     As the failingtest.html will show, it appears as though ie7 does not
    + *     propagate an exception past a function called using the func.call()
    + *     syntax. This causes case 3 of the failing tests (exceptions) to show up
    + *     as timeouts in IE.
    + *   window.onerror:
    + *     This seems to catch errors only in ff2/ff3. It does not work in Safari or
    + *     IE7. The consequence of this is that exceptions that would have been
    + *     caught by window.onerror show up as timeouts.
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + */
    +
    +goog.provide('goog.testing.AsyncTestCase');
    +goog.provide('goog.testing.AsyncTestCase.ControlBreakingException');
    +
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.asserts');
    +
    +
    +
    +/**
    + * A test case that is capable of running tests the contain asynchronous logic.
    + * @param {string=} opt_name A descriptive name for the test case.
    + * @extends {goog.testing.TestCase}
    + * @constructor
    + */
    +goog.testing.AsyncTestCase = function(opt_name) {
    +  goog.testing.TestCase.call(this, opt_name);
    +};
    +goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase);
    +
    +
    +/**
    + * Represents result of top stack function call.
    + * @typedef {{controlBreakingExceptionThrown: boolean, message: string}}
    + * @private
    + */
    +goog.testing.AsyncTestCase.TopStackFuncResult_;
    +
    +
    +
    +/**
    + * An exception class used solely for control flow.
    + * @param {string=} opt_message Error message.
    + * @constructor
    + * @extends {Error}
    + * @final
    + */
    +goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) {
    +  goog.testing.AsyncTestCase.ControlBreakingException.base(
    +      this, 'constructor', opt_message);
    +
    +  /**
    +   * The exception message.
    +   * @type {string}
    +   */
    +  this.message = opt_message || '';
    +};
    +goog.inherits(goog.testing.AsyncTestCase.ControlBreakingException, Error);
    +
    +
    +/**
    + * Return value for .toString().
    + * @type {string}
    + */
    +goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING =
    +    '[AsyncTestCase.ControlBreakingException]';
    +
    +
    +/**
    + * Marks this object as a ControlBreakingException
    + * @type {boolean}
    + */
    +goog.testing.AsyncTestCase.ControlBreakingException.prototype.
    +    isControlBreakingException = true;
    +
    +
    +/** @override */
    +goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString =
    +    function() {
    +  // This shows up in the console when the exception is not caught.
    +  return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
    +};
    +
    +
    +/**
    + * How long to wait for a single step of a test to complete in milliseconds.
    + * A step starts when a call to waitForAsync() is made.
    + * @type {number}
    + */
    +goog.testing.AsyncTestCase.prototype.stepTimeout = 1000;
    +
    +
    +/**
    + * How long to wait after a failed test before moving onto the next one.
    + * The purpose of this is to allow any pending async callbacks from the failing
    + * test to finish up and not cause the next test to fail.
    + * @type {number}
    + */
    +goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500;
    +
    +
    +/**
    + * Turn on extra logging to help debug failing async. tests.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false;
    +
    +
    +/**
    + * A reference to the original asserts.js assert_() function.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.origAssert_;
    +
    +
    +/**
    + * A reference to the original asserts.js fail() function.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.origFail_;
    +
    +
    +/**
    + * A reference to the original window.onerror function.
    + * @type {Function|undefined}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.origOnError_;
    +
    +
    +/**
    + * The stage of the test we are currently on.
    + * @type {Function|undefined}}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.curStepFunc_;
    +
    +
    +/**
    + * The name of the stage of the test we are currently on.
    + * @type {string}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.curStepName_ = '';
    +
    +
    +/**
    + * The stage of the test we should run next.
    + * @type {Function|undefined}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.nextStepFunc;
    +
    +
    +/**
    + * The name of the stage of the test we should run next.
    + * @type {string}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.nextStepName_ = '';
    +
    +
    +/**
    + * The handle to the current setTimeout timer.
    + * @type {number}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0;
    +
    +
    +/**
    + * Marks if the cleanUp() function has been called for the currently running
    + * test.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.cleanedUp_ = false;
    +
    +
    +/**
    + * The currently active test.
    + * @type {goog.testing.TestCase.Test|undefined}
    + * @protected
    + */
    +goog.testing.AsyncTestCase.prototype.activeTest;
    +
    +
    +/**
    + * A flag to prevent recursive exception handling.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.inException_ = false;
    +
    +
    +/**
    + * Flag used to determine if we can move to the next step in the testing loop.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.isReady_ = true;
    +
    +
    +/**
    + * Number of signals to wait for before continuing testing when waitForSignals
    + * is used.
    + * @type {number}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.expectedSignalCount_ = 0;
    +
    +
    +/**
    + * Number of signals received.
    + * @type {number}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.receivedSignalCount_ = 0;
    +
    +
    +/**
    + * Flag that tells us if there is a function in the call stack that will make
    + * a call to pump_().
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.returnWillPump_ = false;
    +
    +
    +/**
    + * The number of times we have thrown a ControlBreakingException so that we
    + * know not to complain in our window.onerror handler. In Webkit, window.onerror
    + * is not supported, and so this counter will keep going up but we won't care
    + * about it.
    + * @type {number}
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0;
    +
    +
    +/**
    + * The current step name.
    + * @return {!string} Step name.
    + * @protected
    + */
    +goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() {
    +  return this.curStepName_;
    +};
    +
    +
    +/**
    + * Preferred way of creating an AsyncTestCase. Creates one and initializes it
    + * with the G_testRunner.
    + * @param {string=} opt_name A descriptive name for the test case.
    + * @return {!goog.testing.AsyncTestCase} The created AsyncTestCase.
    + */
    +goog.testing.AsyncTestCase.createAndInstall = function(opt_name) {
    +  var asyncTestCase = new goog.testing.AsyncTestCase(opt_name);
    +  goog.testing.TestCase.initializeTestRunner(asyncTestCase);
    +  return asyncTestCase;
    +};
    +
    +
    +/**
    + * Informs the testcase not to continue to the next step in the test cycle
    + * until continueTesting is called.
    + * @param {string=} opt_name A description of what we are waiting for.
    + */
    +goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) {
    +  this.isReady_ = false;
    +  this.curStepName_ = opt_name || this.curStepName_;
    +
    +  // Reset the timer that tracks if the async test takes too long.
    +  this.stopTimeoutTimer_();
    +  this.startTimeoutTimer_();
    +};
    +
    +
    +/**
    + * Continue with the next step in the test cycle.
    + */
    +goog.testing.AsyncTestCase.prototype.continueTesting = function() {
    +  if (this.receivedSignalCount_ < this.expectedSignalCount_) {
    +    var remaining = this.expectedSignalCount_ - this.receivedSignalCount_;
    +    throw Error('Still waiting for ' + remaining + ' signals.');
    +  }
    +  this.endCurrentStep_();
    +};
    +
    +
    +/**
    + * Ends the current test step and queues the next test step to run.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.endCurrentStep_ = function() {
    +  if (!this.isReady_) {
    +    // We are a potential entry point, so we pump.
    +    this.isReady_ = true;
    +    this.stopTimeoutTimer_();
    +    // Run this in a setTimeout so that the caller has a chance to call
    +    // waitForAsync() again before we continue.
    +    this.timeout(goog.bind(this.pump_, this, null), 0);
    +  }
    +};
    +
    +
    +/**
    + * Informs the testcase not to continue to the next step in the test cycle
    + * until signal is called the specified number of times. Within a test, this
    + * function behaves additively if called multiple times; the number of signals
    + * to wait for will be the sum of all expected number of signals this function
    + * was called with.
    + * @param {number} times The number of signals to receive before
    + *    continuing testing.
    + * @param {string=} opt_name A description of what we are waiting for.
    + */
    +goog.testing.AsyncTestCase.prototype.waitForSignals =
    +    function(times, opt_name) {
    +  this.expectedSignalCount_ += times;
    +  if (this.receivedSignalCount_ < this.expectedSignalCount_) {
    +    this.waitForAsync(opt_name);
    +  }
    +};
    +
    +
    +/**
    + * Signals once to continue with the test. If this is the last signal that the
    + * test was waiting on, call continueTesting.
    + */
    +goog.testing.AsyncTestCase.prototype.signal = function() {
    +  if (++this.receivedSignalCount_ === this.expectedSignalCount_ &&
    +      this.expectedSignalCount_ > 0) {
    +    this.endCurrentStep_();
    +  }
    +};
    +
    +
    +/**
    + * Handles an exception thrown by a test.
    + * @param {*=} opt_e The exception object associated with the failure
    + *     or a string.
    + * @throws Always throws a ControlBreakingException.
    + */
    +goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) {
    +  // If we've caught an exception that we threw, then just pass it along. This
    +  // can happen if doAsyncError() was called from a call to assert and then
    +  // again by pump_().
    +  if (opt_e && opt_e.isControlBreakingException) {
    +    throw opt_e;
    +  }
    +
    +  // Prevent another timeout error from triggering for this test step.
    +  this.stopTimeoutTimer_();
    +
    +  // doError() uses test.name. Here, we create a dummy test and give it a more
    +  // helpful name based on the step we're currently on.
    +  var fakeTestObj = new goog.testing.TestCase.Test(this.curStepName_,
    +                                                   goog.nullFunction);
    +  if (this.activeTest) {
    +    fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']';
    +  }
    +
    +  if (this.activeTest) {
    +    // Note: if the test has an error, and then tearDown has an error, they will
    +    // both be reported.
    +    this.doError(fakeTestObj, opt_e);
    +  } else {
    +    this.exceptionBeforeTest = opt_e;
    +  }
    +
    +  // This is a potential entry point, so we pump. We also add in a bit of a
    +  // delay to try and prevent any async behavior from the failed test from
    +  // causing the next test to fail.
    +  this.timeout(goog.bind(this.pump_, this, this.doAsyncErrorTearDown_),
    +      this.timeToSleepAfterFailure);
    +
    +  // We just caught an exception, so we do not want the code above us on the
    +  // stack to continue executing. If pump_ is in our call-stack, then it will
    +  // batch together multiple errors, so we only increment the count if pump_ is
    +  // not in the stack and let pump_ increment the count when it batches them.
    +  if (!this.returnWillPump_) {
    +    this.numControlExceptionsExpected_ += 1;
    +    this.dbgLog_('doAsynError: numControlExceptionsExpected_ = ' +
    +        this.numControlExceptionsExpected_ + ' and throwing exception.');
    +  }
    +
    +  // Copy the error message to ControlBreakingException.
    +  var message = '';
    +  if (typeof opt_e == 'string') {
    +    message = opt_e;
    +  } else if (opt_e && opt_e.message) {
    +    message = opt_e.message;
    +  }
    +  throw new goog.testing.AsyncTestCase.ControlBreakingException(message);
    +};
    +
    +
    +/**
    + * Sets up the test page and then waits until the test case has been marked
    + * as ready before executing the tests.
    + * @override
    + */
    +goog.testing.AsyncTestCase.prototype.runTests = function() {
    +  this.hookAssert_();
    +  this.hookOnError_();
    +
    +  this.setNextStep_(this.doSetUpPage_, 'setUpPage');
    +  // We are an entry point, so we pump.
    +  this.pump_();
    +};
    +
    +
    +/**
    + * Starts the tests.
    + * @override
    + */
    +goog.testing.AsyncTestCase.prototype.cycleTests = function() {
    +  // We are an entry point, so we pump.
    +  this.saveMessage('Start');
    +  this.setNextStep_(this.doIteration_, 'doIteration');
    +  this.pump_();
    +};
    +
    +
    +/**
    + * Finalizes the test case, called when the tests have finished executing.
    + * @override
    + */
    +goog.testing.AsyncTestCase.prototype.finalize = function() {
    +  this.unhookAll_();
    +  this.setNextStep_(null, 'finalized');
    +  goog.testing.AsyncTestCase.superClass_.finalize.call(this);
    +};
    +
    +
    +/**
    + * Enables verbose logging of what is happening inside of the AsyncTestCase.
    + */
    +goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() {
    +  this.enableDebugLogs_ = true;
    +};
    +
    +
    +/**
    + * Logs the given debug message to the console (when enabled).
    + * @param {string} message The message to log.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) {
    +  if (this.enableDebugLogs_) {
    +    this.log('AsyncTestCase - ' + message);
    +  }
    +};
    +
    +
    +/**
    + * Wraps doAsyncError() for when we are sure that the test runner has no user
    + * code above it in the stack.
    + * @param {string|Error=} opt_e The exception object associated with the
    + *     failure or a string.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ =
    +    function(opt_e) {
    +  /** @preserveTry */
    +  try {
    +    this.doAsyncError(opt_e);
    +  } catch (e) {
    +    // We know that we are on the top of the stack, so there is no need to
    +    // throw this exception in this case.
    +    if (e.isControlBreakingException) {
    +      this.numControlExceptionsExpected_ -= 1;
    +      this.dbgLog_('doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' +
    +          this.numControlExceptionsExpected_ + ' and catching exception.');
    +    } else {
    +      throw e;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calls the tearDown function, catching any errors, and then moves on to
    + * the next step in the testing cycle.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() {
    +  if (this.inException_) {
    +    // We get here if tearDown is throwing the error.
    +    // Upon calling continueTesting, the inline function 'doAsyncError' (set
    +    // below) is run.
    +    this.endCurrentStep_();
    +  } else {
    +    this.inException_ = true;
    +    this.isReady_ = true;
    +
    +    // The continue point is different depending on if the error happened in
    +    // setUpPage() or in setUp()/test*()/tearDown().
    +    var stepFuncAfterError = this.nextStepFunc_;
    +    var stepNameAfterError = 'TestCase.execute (after error)';
    +    if (this.activeTest) {
    +      stepFuncAfterError = this.doIteration_;
    +      stepNameAfterError = 'doIteration (after error)';
    +    }
    +
    +    // We must set the next step before calling tearDown.
    +    this.setNextStep_(function() {
    +      this.inException_ = false;
    +      // This is null when an error happens in setUpPage.
    +      this.setNextStep_(stepFuncAfterError, stepNameAfterError);
    +    }, 'doAsyncError');
    +
    +    // Call the test's tearDown().
    +    if (!this.cleanedUp_) {
    +      this.cleanedUp_ = true;
    +      this.tearDown();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Replaces the asserts.js assert_() and fail() functions with a wrappers to
    + * catch the exceptions.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.hookAssert_ = function() {
    +  if (!this.origAssert_) {
    +    this.origAssert_ = _assert;
    +    this.origFail_ = fail;
    +    var self = this;
    +    _assert = function() {
    +      /** @preserveTry */
    +      try {
    +        self.origAssert_.apply(this, arguments);
    +      } catch (e) {
    +        self.dbgLog_('Wrapping failed assert()');
    +        self.doAsyncError(e);
    +      }
    +    };
    +    fail = function() {
    +      /** @preserveTry */
    +      try {
    +        self.origFail_.apply(this, arguments);
    +      } catch (e) {
    +        self.dbgLog_('Wrapping fail()');
    +        self.doAsyncError(e);
    +      }
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Sets a window.onerror handler for catching exceptions that happen in async
    + * callbacks. Note that as of Safari 3.1, Safari does not support this.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.hookOnError_ = function() {
    +  if (!this.origOnError_) {
    +    this.origOnError_ = window.onerror;
    +    var self = this;
    +    window.onerror = function(error, url, line) {
    +      // Ignore exceptions that we threw on purpose.
    +      var cbe =
    +          goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
    +      if (String(error).indexOf(cbe) != -1 &&
    +          self.numControlExceptionsExpected_) {
    +        self.numControlExceptionsExpected_ -= 1;
    +        self.dbgLog_('window.onerror: numControlExceptionsExpected_ = ' +
    +            self.numControlExceptionsExpected_ + ' and ignoring exception. ' +
    +            error);
    +        // Tell the browser not to compain about the error.
    +        return true;
    +      } else {
    +        self.dbgLog_('window.onerror caught exception.');
    +        var message = error + '\nURL: ' + url + '\nLine: ' + line;
    +        self.doTopOfStackAsyncError_(message);
    +        // Tell the browser to complain about the error.
    +        return false;
    +      }
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Unhooks window.onerror and _assert.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.unhookAll_ = function() {
    +  if (this.origOnError_) {
    +    window.onerror = this.origOnError_;
    +    this.origOnError_ = null;
    +    _assert = this.origAssert_;
    +    this.origAssert_ = null;
    +    fail = this.origFail_;
    +    this.origFail_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Enables the timeout timer. This timer fires unless continueTesting is
    + * called.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() {
    +  if (!this.timeoutHandle_ && this.stepTimeout > 0) {
    +    this.timeoutHandle_ = this.timeout(goog.bind(function() {
    +      this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_);
    +      this.timeoutHandle_ = 0;
    +
    +      this.doTopOfStackAsyncError_('Timed out while waiting for ' +
    +          'continueTesting() to be called.');
    +    }, this, null), this.stepTimeout);
    +    this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_);
    +  }
    +};
    +
    +
    +/**
    + * Disables the timeout timer.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() {
    +  if (this.timeoutHandle_) {
    +    this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_);
    +    this.clearTimeout(this.timeoutHandle_);
    +    this.timeoutHandle_ = 0;
    +  }
    +};
    +
    +
    +/**
    + * Sets the next function to call in our sequence of async callbacks.
    + * @param {Function} func The function that executes the next step.
    + * @param {string} name A description of the next step.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) {
    +  this.nextStepFunc_ = func && goog.bind(func, this);
    +  this.nextStepName_ = name;
    +};
    +
    +
    +/**
    + * Calls the given function, redirecting any exceptions to doAsyncError.
    + * @param {Function} func The function to call.
    + * @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a
    + * TopStackFuncResult_.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) {
    +  /** @preserveTry */
    +  try {
    +    func.call(this);
    +    return {controlBreakingExceptionThrown: false, message: ''};
    +  } catch (e) {
    +    this.dbgLog_('Caught exception in callTopOfStackFunc_');
    +    /** @preserveTry */
    +    try {
    +      this.doAsyncError(e);
    +      return {controlBreakingExceptionThrown: false, message: ''};
    +    } catch (e2) {
    +      if (!e2.isControlBreakingException) {
    +        throw e2;
    +      }
    +      return {controlBreakingExceptionThrown: true, message: e2.message};
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calls the next callback when the isReady_ flag is true.
    + * @param {Function=} opt_doFirst A function to call before pumping.
    + * @private
    + * @throws Throws a ControlBreakingException if there were any failing steps.
    + */
    +goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) {
    +  // If this function is already above us in the call-stack, then we should
    +  // return rather than pumping in order to minimize call-stack depth.
    +  if (!this.returnWillPump_) {
    +    this.setBatchTime(this.now());
    +    this.returnWillPump_ = true;
    +    var topFuncResult = {};
    +
    +    if (opt_doFirst) {
    +      topFuncResult = this.callTopOfStackFunc_(opt_doFirst);
    +    }
    +    // Note: we don't check for this.running here because it is not set to true
    +    // while executing setUpPage and tearDownPage.
    +    // Also, if isReady_ is false, then one of two things will happen:
    +    // 1. Our timeout callback will be called.
    +    // 2. The tests will call continueTesting(), which will call pump_() again.
    +    while (this.isReady_ && this.nextStepFunc_ &&
    +        !topFuncResult.controlBreakingExceptionThrown) {
    +      this.curStepFunc_ = this.nextStepFunc_;
    +      this.curStepName_ = this.nextStepName_;
    +      this.nextStepFunc_ = null;
    +      this.nextStepName_ = '';
    +
    +      this.dbgLog_('Performing step: ' + this.curStepName_);
    +      topFuncResult =
    +          this.callTopOfStackFunc_(/** @type {Function} */(this.curStepFunc_));
    +
    +      // If the max run time is exceeded call this function again async so as
    +      // not to block the browser.
    +      var delta = this.now() - this.getBatchTime();
    +      if (delta > goog.testing.TestCase.maxRunTime &&
    +          !topFuncResult.controlBreakingExceptionThrown) {
    +        this.saveMessage('Breaking async');
    +        var self = this;
    +        this.timeout(function() { self.pump_(); }, 100);
    +        break;
    +      }
    +    }
    +    this.returnWillPump_ = false;
    +  } else if (opt_doFirst) {
    +    opt_doFirst.call(this);
    +  }
    +};
    +
    +
    +/**
    + * Sets up the test page and then waits untill the test case has been marked
    + * as ready before executing the tests.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() {
    +  this.setNextStep_(this.execute, 'TestCase.execute');
    +  this.setUpPage();
    +};
    +
    +
    +/**
    + * Step 1: Move to the next test.
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doIteration_ = function() {
    +  this.expectedSignalCount_ = 0;
    +  this.receivedSignalCount_ = 0;
    +  this.activeTest = this.next();
    +  if (this.activeTest && this.running) {
    +    this.result_.runCount++;
    +    // If this test should be marked as having failed, doIteration will go
    +    // straight to the next test.
    +    if (this.maybeFailTestEarly(this.activeTest)) {
    +      this.setNextStep_(this.doIteration_, 'doIteration');
    +    } else {
    +      this.setNextStep_(this.doSetUp_, 'setUp');
    +    }
    +  } else {
    +    // All tests done.
    +    this.finalize();
    +  }
    +};
    +
    +
    +/**
    + * Step 2: Call setUp().
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doSetUp_ = function() {
    +  this.log('Running test: ' + this.activeTest.name);
    +  this.cleanedUp_ = false;
    +  this.setNextStep_(this.doExecute_, this.activeTest.name);
    +  this.setUp();
    +};
    +
    +
    +/**
    + * Step 3: Call test.execute().
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doExecute_ = function() {
    +  this.setNextStep_(this.doTearDown_, 'tearDown');
    +  this.activeTest.execute();
    +};
    +
    +
    +/**
    + * Step 4: Call tearDown().
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doTearDown_ = function() {
    +  this.cleanedUp_ = true;
    +  this.setNextStep_(this.doNext_, 'doNext');
    +  this.tearDown();
    +};
    +
    +
    +/**
    + * Step 5: Call doSuccess()
    + * @private
    + */
    +goog.testing.AsyncTestCase.prototype.doNext_ = function() {
    +  this.setNextStep_(this.doIteration_, 'doIteration');
    +  this.doSuccess(/** @type {goog.testing.TestCase.Test} */(this.activeTest));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.html b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.html
    new file mode 100644
    index 0000000000..8bfe23f006
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author: agrieve@google.com (Andrew Grieve)
    +
    +This tests that the AsyncTestCase can handle asynchronous behaviour in:
    +  setUpPage(),
    +  setUp(),
    +  test*(),
    +  tearDown()
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.AsyncTestCase Asyncronous Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.testing.AsyncTestCaseAsyncTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.js b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.js
    new file mode 100644
    index 0000000000..af62836bec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_async_test.js
    @@ -0,0 +1,136 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.AsyncTestCaseAsyncTest');
    +goog.setTestOnly('goog.testing.AsyncTestCaseAsyncTest');
    +
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +// Has the setUp() function been called.
    +var setUpCalled = false;
    +// Has the current test function completed. This helps us to ensure that
    +// the next test is not started before the previous completed.
    +var curTestIsDone = true;
    +// Use an asynchronous test runner for our tests.
    +var asyncTestCase =
    +    goog.testing.AsyncTestCase.createAndInstall(document.title);
    +
    +
    +/**
    + * Uses window.setTimeout() to perform asynchronous behaviour and uses
    + * asyncTestCase.waitForAsync() and asyncTestCase.continueTesting() to mark
    + * the beginning and end of it.
    + * @param {number} numAsyncCalls The number of asynchronous calls to make.
    + * @param {string} name The name of the current step.
    + */
    +function doAsyncStuff(numAsyncCalls, name) {
    +  if (numAsyncCalls > 0) {
    +    curTestIsDone = false;
    +    asyncTestCase.waitForAsync(
    +        'doAsyncStuff-' + name + '(' + numAsyncCalls + ')');
    +    window.setTimeout(function() {
    +      doAsyncStuff(numAsyncCalls - 1, name);
    +    }, 0);
    +  } else {
    +    curTestIsDone = true;
    +    asyncTestCase.continueTesting();
    +  }
    +}
    +
    +function setUpPage() {
    +  debug('setUpPage was called.');
    +  doAsyncStuff(3, 'setUpPage');
    +}
    +function setUp() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(3, 'setUp');
    +}
    +function tearDown() {
    +  assertTrue(curTestIsDone);
    +}
    +function test1() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(1, 'test1');
    +}
    +function test2_asyncContinueThenWait() {
    +  var activeTest = asyncTestCase.activeTest_;
    +  function async1() {
    +    asyncTestCase.continueTesting();
    +    asyncTestCase.waitForAsync('2');
    +    window.setTimeout(async2, 0);
    +  }
    +  function async2() {
    +    asyncTestCase.continueTesting();
    +    assertEquals('Did not wait for inner waitForAsync',
    +                 activeTest,
    +                 asyncTestCase.activeTest_);
    +  }
    +  asyncTestCase.waitForAsync('1');
    +  window.setTimeout(async1, 0);
    +}
    +function test3() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(2, 'test3');
    +}
    +
    +function tearDownPage() {
    +  debug('tearDownPage was called.');
    +  assertTrue(curTestIsDone);
    +}
    +
    +
    +var callback = function() {
    +  curTestIsDone = true;
    +  asyncTestCase.signal();
    +};
    +var doAsyncSignals = function() {
    +  curTestIsDone = false;
    +  window.setTimeout(callback, 0);
    +};
    +
    +function testSignalsReturn() {
    +  doAsyncSignals();
    +  doAsyncSignals();
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(3);
    +}
    +
    +function testSignalsMixedSyncAndAsync() {
    +  asyncTestCase.signal();
    +  doAsyncSignals();
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(3);
    +}
    +
    +function testSignalsMixedSyncAndAsyncMultipleWaits() {
    +  asyncTestCase.signal();
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(1);
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(2);
    +}
    +
    +function testSignalsCallContinueTestingBeforeFinishing() {
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(2);
    +
    +  window.setTimeout(function() {
    +    var thrown = assertThrows(function() {
    +      asyncTestCase.continueTesting();
    +    });
    +    assertEquals('Still waiting for 1 signals.', thrown.message);
    +  }, 0);
    +  doAsyncSignals(); // To not timeout.
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.html b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.html
    new file mode 100644
    index 0000000000..856b2f3f88
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author: agrieve@google.com (Andrew Grieve)
    +
    +This tests that the AsyncTestCase can handle synchronous behaviour in:
    +  setUpPage(),
    +  setUp(),
    +  test*(),
    +  tearDown()
    +It is the same test as asynctestcase_async_test.html, except that it uses a mock
    +version of window.setTimeout() to eliminate all asynchronous calls.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.AsyncTestCase Synchronous Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.testing.AsyncTestCaseSyncTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.js b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.js
    new file mode 100644
    index 0000000000..e2b3022257
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_noasync_test.js
    @@ -0,0 +1,111 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.AsyncTestCaseSyncTest');
    +goog.setTestOnly('goog.testing.AsyncTestCaseSyncTest');
    +
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +
    +// Has the setUp() function been called.
    +var setUpCalled = false;
    +// Has the current test function completed. This helps us to ensure that the
    +// next test is not started before the previous completed.
    +var curTestIsDone = true;
    +// For restoring it later.
    +var oldTimeout = window.setTimeout;
    +// Use an asynchronous test runner for our tests.
    +var asyncTestCase =
    +    goog.testing.AsyncTestCase.createAndInstall(document.title);
    +
    +
    +/**
    + * Uses window.setTimeout() to perform asynchronous behaviour and uses
    + * asyncTestCase.waitForAsync() and asyncTestCase.continueTesting() to mark
    + * the beginning and end of it.
    + * @param {number} numAsyncCalls The number of asynchronous calls to make.
    + * @param {string} name The name of the current step.
    + */
    +function doAsyncStuff(numAsyncCalls, name) {
    +  if (numAsyncCalls > 0) {
    +    curTestIsDone = false;
    +    asyncTestCase.waitForAsync(
    +        'doAsyncStuff-' + name + '(' + numAsyncCalls + ')');
    +    window.setTimeout(function() {
    +      doAsyncStuff(numAsyncCalls - 1, name);
    +    }, 0);
    +  } else {
    +    curTestIsDone = true;
    +    asyncTestCase.continueTesting();
    +  }
    +}
    +
    +function setUpPage() {
    +  debug('setUpPage was called.');
    +  // Don't do anything asynchronously.
    +  window.setTimeout = function(callback, time) {
    +    callback();
    +  };
    +  doAsyncStuff(3, 'setUpPage');
    +}
    +function setUp() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(3, 'setUp');
    +}
    +function tearDown() {
    +  assertTrue(curTestIsDone);
    +}
    +function test1() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(1, 'test1');
    +}
    +function test2() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(2, 'test2');
    +}
    +function test3() {
    +  assertTrue(curTestIsDone);
    +  doAsyncStuff(5, 'test3');
    +}
    +var callback = function() {
    +  curTestIsDone = true;
    +  asyncTestCase.signal();
    +};
    +var doAsyncSignals = function() {
    +  curTestIsDone = false;
    +  window.setTimeout(callback, 0);
    +};
    +function testSignalsReturn() {
    +  doAsyncSignals();
    +  doAsyncSignals();
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(3);
    +}
    +function testSignalsCallContinueTestingBeforeFinishing() {
    +  doAsyncSignals();
    +  asyncTestCase.waitForSignals(2);
    +
    +  window.setTimeout(function() {
    +    var thrown = assertThrows(function() {
    +      asyncTestCase.continueTesting();
    +    });
    +    assertEquals('Still waiting for 1 signals.', thrown.message);
    +  }, 0);
    +  doAsyncSignals(); // To not timeout.
    +}
    +function tearDownPage() {
    +  debug('tearDownPage was called.');
    +  assertTrue(curTestIsDone);
    +  window.setTimeout = oldTimeout;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.html b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.html
    new file mode 100644
    index 0000000000..38efeed929
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.asynctestcase
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.AsyncTestCaseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.js b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.js
    new file mode 100644
    index 0000000000..7d3b52c266
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/asynctestcase_test.js
    @@ -0,0 +1,52 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.AsyncTestCaseTest');
    +goog.setTestOnly('goog.testing.AsyncTestCaseTest');
    +
    +goog.require('goog.debug.Error');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +function testControlBreakingExceptionThrown() {
    +  var asyncTestCase = new goog.testing.AsyncTestCase();
    +
    +  // doAsyncError with no message.
    +  try {
    +    asyncTestCase.doAsyncError();
    +  } catch (e) {
    +    assertTrue(e.isControlBreakingException);
    +    assertEquals('', e.message);
    +  }
    +
    +  // doAsyncError with string.
    +  var errorMessage1 = 'Error message 1';
    +  try {
    +    asyncTestCase.doAsyncError(errorMessage1);
    +  } catch (e) {
    +    assertTrue(e.isControlBreakingException);
    +    assertEquals(errorMessage1, e.message);
    +  }
    +
    +  // doAsyncError with error.
    +  var errorMessage2 = 'Error message 2';
    +  try {
    +    var error = new goog.debug.Error(errorMessage2);
    +    asyncTestCase.doAsyncError(error);
    +  } catch (e) {
    +    assertTrue(e.isControlBreakingException);
    +    assertEquals(errorMessage2, e.message);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/benchmark.js b/mapbox-gl/closure-library/closure/goog/testing/benchmark.js
    new file mode 100644
    index 0000000000..41673e0c88
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/benchmark.js
    @@ -0,0 +1,96 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.benchmark');
    +goog.setTestOnly('goog.testing.benchmark');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.testing.PerformanceTable');
    +goog.require('goog.testing.PerformanceTimer');
    +goog.require('goog.testing.TestCase');
    +
    +
    +/**
    + * Run the benchmarks.
    + * @private
    + */
    +goog.testing.benchmark.run_ = function() {
    +  // Parse the 'times' query parameter if it's set.
    +  var times = 200;
    +  var search = window.location.search;
    +  var timesMatch = search.match(/(?:\?|&)times=([^?&]+)/i);
    +  if (timesMatch) {
    +    times = Number(timesMatch[1]);
    +  }
    +
    +  var prefix = 'benchmark';
    +
    +  // First, get the functions.
    +  var testSources = goog.testing.TestCase.getGlobals();
    +
    +  var benchmarks = {};
    +  var names = [];
    +
    +  for (var i = 0; i < testSources.length; i++) {
    +    var testSource = testSources[i];
    +    for (var name in testSource) {
    +      if ((new RegExp('^' + prefix)).test(name)) {
    +        var ref;
    +        try {
    +          ref = testSource[name];
    +        } catch (ex) {
    +          // NOTE(brenneman): When running tests from a file:// URL on Firefox
    +          // 3.5 for Windows, any reference to window.sessionStorage raises
    +          // an "Operation is not supported" exception. Ignore any exceptions
    +          // raised by simply accessing global properties.
    +          ref = undefined;
    +        }
    +
    +        if (goog.isFunction(ref)) {
    +          benchmarks[name] = ref;
    +          names.push(name);
    +        }
    +      }
    +    }
    +  }
    +
    +  document.body.appendChild(
    +      goog.dom.createTextNode(
    +          'Running ' + names.length + ' benchmarks ' + times + ' times each.'));
    +  document.body.appendChild(goog.dom.createElement(goog.dom.TagName.BR));
    +
    +  names.sort();
    +
    +  // Build a table and timer.
    +  var performanceTimer = new goog.testing.PerformanceTimer(times);
    +  performanceTimer.setDiscardOutliers(true);
    +
    +  var performanceTable = new goog.testing.PerformanceTable(document.body,
    +      performanceTimer, 2);
    +
    +  // Next, run the benchmarks.
    +  for (var i = 0; i < names.length; i++) {
    +    performanceTable.run(benchmarks[names[i]], names[i]);
    +  }
    +};
    +
    +
    +/**
    + * Onload handler that runs the benchmarks.
    + * @param {Event} e The event object.
    + */
    +window.onload = function(e) {
    +  goog.testing.benchmark.run_();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase.js b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase.js
    new file mode 100644
    index 0000000000..952b034b20
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase.js
    @@ -0,0 +1,691 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines test classes for tests that can wait for conditions.
    + *
    + * Normal unit tests must complete their test logic within a single function
    + * execution. This is ideal for most tests, but makes it difficult to test
    + * routines that require real time to complete. The tests and TestCase in this
    + * file allow for tests that can wait until a condition is true before
    + * continuing execution.
    + *
    + * Each test has the typical three phases of execution: setUp, the test itself,
    + * and tearDown. During each phase, the test function may add wait conditions,
    + * which result in new test steps being added for that phase. All steps in a
    + * given phase must complete before moving on to the next phase. An error in
    + * any phase will stop that test and report the error to the test runner.
    + *
    + * This class should not be used where adequate mocks exist. Time-based routines
    + * should use the MockClock, which runs much faster and provides equivalent
    + * results. Continuation tests should be used for testing code that depends on
    + * browser behaviors that are difficult to mock. For example, testing code that
    + * relies on Iframe load events, event or layout code that requires a setTimeout
    + * to become valid, and other browser-dependent native object interactions for
    + * which mocks are insufficient.
    + *
    + * Sample usage:
    + *
    + * <pre>
    + * var testCase = new goog.testing.ContinuationTestCase();
    + * testCase.autoDiscoverTests();
    + *
    + * if (typeof G_testRunner != 'undefined') {
    + *   G_testRunner.initialize(testCase);
    + * }
    + *
    + * function testWaiting() {
    + *   var someVar = true;
    + *   waitForTimeout(function() {
    + *     assertTrue(someVar)
    + *   }, 500);
    + * }
    + *
    + * function testWaitForEvent() {
    + *   var et = goog.events.EventTarget();
    + *   waitForEvent(et, 'test', function() {
    + *     // Test step runs after the event fires.
    + *   })
    + *   et.dispatchEvent(et, 'test');
    + * }
    + *
    + * function testWaitForCondition() {
    + *   var counter = 0;
    + *
    + *   waitForCondition(function() {
    + *     // This function is evaluated periodically until it returns true, or it
    + *     // times out.
    + *     return ++counter >= 3;
    + *   }, function() {
    + *     // This test step is run once the condition becomes true.
    + *     assertEquals(3, counter);
    + *   });
    + * }
    + * </pre>
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +
    +goog.provide('goog.testing.ContinuationTestCase');
    +goog.provide('goog.testing.ContinuationTestCase.Step');
    +goog.provide('goog.testing.ContinuationTestCase.Test');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.asserts');
    +
    +
    +
    +/**
    + * Constructs a test case that supports tests with continuations. Test functions
    + * may issue "wait" commands that suspend the test temporarily and continue once
    + * the wait condition is met.
    + *
    + * @param {string=} opt_name Optional name for the test case.
    + * @constructor
    + * @extends {goog.testing.TestCase}
    + * @final
    + */
    +goog.testing.ContinuationTestCase = function(opt_name) {
    +  goog.testing.TestCase.call(this, opt_name);
    +
    +  /**
    +   * An event handler for waiting on Closure or browser events during tests.
    +   * @type {goog.events.EventHandler<!goog.testing.ContinuationTestCase>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +};
    +goog.inherits(goog.testing.ContinuationTestCase, goog.testing.TestCase);
    +
    +
    +/**
    + * The default maximum time to wait for a single test step in milliseconds.
    + * @type {number}
    + */
    +goog.testing.ContinuationTestCase.MAX_TIMEOUT = 1000;
    +
    +
    +/**
    + * Lock used to prevent multiple test steps from running recursively.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.locked_ = false;
    +
    +
    +/**
    + * The current test being run.
    + * @type {goog.testing.ContinuationTestCase.Test}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.currentTest_ = null;
    +
    +
    +/**
    + * Enables or disables the wait functions in the global scope.
    + * @param {boolean} enable Whether the wait functions should be exported.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.enableWaitFunctions_ =
    +    function(enable) {
    +  if (enable) {
    +    goog.exportSymbol('waitForCondition',
    +                      goog.bind(this.waitForCondition, this));
    +    goog.exportSymbol('waitForEvent', goog.bind(this.waitForEvent, this));
    +    goog.exportSymbol('waitForTimeout', goog.bind(this.waitForTimeout, this));
    +  } else {
    +    // Internet Explorer doesn't allow deletion of properties on the window.
    +    goog.global['waitForCondition'] = undefined;
    +    goog.global['waitForEvent'] = undefined;
    +    goog.global['waitForTimeout'] = undefined;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.testing.ContinuationTestCase.prototype.runTests = function() {
    +  this.enableWaitFunctions_(true);
    +  goog.testing.ContinuationTestCase.superClass_.runTests.call(this);
    +};
    +
    +
    +/** @override */
    +goog.testing.ContinuationTestCase.prototype.finalize = function() {
    +  this.enableWaitFunctions_(false);
    +  goog.testing.ContinuationTestCase.superClass_.finalize.call(this);
    +};
    +
    +
    +/** @override */
    +goog.testing.ContinuationTestCase.prototype.cycleTests = function() {
    +  // Get the next test in the queue.
    +  if (!this.currentTest_) {
    +    this.currentTest_ = this.createNextTest_();
    +  }
    +
    +  // Run the next step of the current test, or exit if all tests are complete.
    +  if (this.currentTest_) {
    +    this.runNextStep_();
    +  } else {
    +    this.finalize();
    +  }
    +};
    +
    +
    +/**
    + * Creates the next test in the queue.
    + * @return {goog.testing.ContinuationTestCase.Test} The next test to execute, or
    + *     null if no pending tests remain.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.createNextTest_ = function() {
    +  var test = this.next();
    +  if (!test) {
    +    return null;
    +  }
    +
    +
    +  var name = test.name;
    +  goog.testing.TestCase.currentTestName = name;
    +  this.result_.runCount++;
    +  this.log('Running test: ' + name);
    +
    +  return new goog.testing.ContinuationTestCase.Test(
    +      new goog.testing.TestCase.Test(name, this.setUp, this),
    +      test,
    +      new goog.testing.TestCase.Test(name, this.tearDown, this));
    +};
    +
    +
    +/**
    + * Cleans up a finished test and cycles to the next test.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.finishTest_ = function() {
    +  var err = this.currentTest_.getError();
    +  if (err) {
    +    this.doError(this.currentTest_, err);
    +  } else {
    +    this.doSuccess(this.currentTest_);
    +  }
    +
    +  goog.testing.TestCase.currentTestName = null;
    +  this.currentTest_ = null;
    +  this.locked_ = false;
    +  this.handler_.removeAll();
    +
    +  this.timeout(goog.bind(this.cycleTests, this), 0);
    +};
    +
    +
    +/**
    + * Executes the next step in the current phase, advancing through each phase as
    + * all steps are completed.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.runNextStep_ = function() {
    +  if (this.locked_) {
    +    // Attempting to run a step before the previous step has finished. Try again
    +    // after that step has released the lock.
    +    return;
    +  }
    +
    +  var phase = this.currentTest_.getCurrentPhase();
    +
    +  if (!phase || !phase.length) {
    +    // No more steps for this test.
    +    this.finishTest_();
    +    return;
    +  }
    +
    +  // Find the next step that is not in a wait state.
    +  var stepIndex = goog.array.findIndex(phase, function(step) {
    +    return !step.waiting;
    +  });
    +
    +  if (stepIndex < 0) {
    +    // All active steps are currently waiting. Return until one wakes up.
    +    return;
    +  }
    +
    +  this.locked_ = true;
    +  var step = phase[stepIndex];
    +
    +  try {
    +    step.execute();
    +    // Remove the successfully completed step. If an error is thrown, all steps
    +    // will be removed for this phase.
    +    goog.array.removeAt(phase, stepIndex);
    +
    +  } catch (e) {
    +    this.currentTest_.setError(e);
    +
    +    // An assertion has failed, or an exception was raised. Clear the current
    +    // phase, whether it is setUp, test, or tearDown.
    +    this.currentTest_.cancelCurrentPhase();
    +
    +    // Cancel the setUp and test phase no matter where the error occurred. The
    +    // tearDown phase will still run if it has pending steps.
    +    this.currentTest_.cancelTestPhase();
    +  }
    +
    +  this.locked_ = false;
    +  this.runNextStep_();
    +};
    +
    +
    +/**
    + * Creates a new test step that will run after a user-specified
    + * timeout.  No guarantee is made on the execution order of the
    + * continuation, except for those provided by each browser's
    + * window.setTimeout. In particular, if two continuations are
    + * registered at the same time with very small delta for their
    + * durations, this class can not guarantee that the continuation with
    + * the smaller duration will be executed first.
    + * @param {Function} continuation The test function to invoke after the timeout.
    + * @param {number=} opt_duration The length of the timeout in milliseconds.
    + */
    +goog.testing.ContinuationTestCase.prototype.waitForTimeout =
    +    function(continuation, opt_duration) {
    +  var step = this.addStep_(continuation);
    +  step.setTimeout(goog.bind(this.handleComplete_, this, step),
    +                  opt_duration || 0);
    +};
    +
    +
    +/**
    + * Creates a new test step that will run after an event has fired. If the event
    + * does not fire within a reasonable timeout, the test will fail.
    + * @param {goog.events.EventTarget|EventTarget} eventTarget The target that will
    + *     fire the event.
    + * @param {string} eventType The type of event to listen for.
    + * @param {Function} continuation The test function to invoke after the event
    + *     fires.
    + */
    +goog.testing.ContinuationTestCase.prototype.waitForEvent = function(
    +    eventTarget,
    +    eventType,
    +    continuation) {
    +
    +  var step = this.addStep_(continuation);
    +
    +  var duration = goog.testing.ContinuationTestCase.MAX_TIMEOUT;
    +  step.setTimeout(goog.bind(this.handleTimeout_, this, step, duration),
    +                  duration);
    +
    +  this.handler_.listenOnce(eventTarget,
    +                           eventType,
    +                           goog.bind(this.handleComplete_, this, step));
    +};
    +
    +
    +/**
    + * Creates a new test step which will run once a condition becomes true. The
    + * condition will be polled at a user-specified interval until it becomes true,
    + * or until a maximum timeout is reached.
    + * @param {Function} condition The condition to poll.
    + * @param {Function} continuation The test code to evaluate once the condition
    + *     becomes true.
    + * @param {number=} opt_interval The polling interval in milliseconds.
    + * @param {number=} opt_maxTimeout The maximum amount of time to wait for the
    + *     condition in milliseconds (defaults to 1000).
    + */
    +goog.testing.ContinuationTestCase.prototype.waitForCondition = function(
    +    condition,
    +    continuation,
    +    opt_interval,
    +    opt_maxTimeout) {
    +
    +  var interval = opt_interval || 100;
    +  var timeout = opt_maxTimeout || goog.testing.ContinuationTestCase.MAX_TIMEOUT;
    +
    +  var step = this.addStep_(continuation);
    +  this.testCondition_(step, condition, goog.now(), interval, timeout);
    +};
    +
    +
    +/**
    + * Creates a new asynchronous test step which will be added to the current test
    + * phase.
    + * @param {Function} func The test function that will be executed for this step.
    + * @return {!goog.testing.ContinuationTestCase.Step} A new test step.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.addStep_ = function(func) {
    +  if (!this.currentTest_) {
    +    throw Error('Cannot add test steps outside of a running test.');
    +  }
    +
    +  var step = new goog.testing.ContinuationTestCase.Step(
    +      this.currentTest_.name,
    +      func,
    +      this.currentTest_.scope);
    +  this.currentTest_.addStep(step);
    +  return step;
    +};
    +
    +
    +/**
    + * Handles completion of a step's wait condition. Advances the test, allowing
    + * the step's test method to run.
    + * @param {goog.testing.ContinuationTestCase.Step} step The step that has
    + *     finished waiting.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.handleComplete_ = function(step) {
    +  step.clearTimeout();
    +  step.waiting = false;
    +  this.runNextStep_();
    +};
    +
    +
    +/**
    + * Handles the timeout event for a step that has exceeded the maximum time. This
    + * causes the current test to fail.
    + * @param {goog.testing.ContinuationTestCase.Step} step The timed-out step.
    + * @param {number} duration The length of the timeout in milliseconds.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.handleTimeout_ =
    +    function(step, duration) {
    +  step.ref = function() {
    +    fail('Continuation timed out after ' + duration + 'ms.');
    +  };
    +
    +  // Since the test is failing, cancel any other pending event listeners.
    +  this.handler_.removeAll();
    +  this.handleComplete_(step);
    +};
    +
    +
    +/**
    + * Tests a wait condition and executes the associated test step once the
    + * condition is true.
    + *
    + * If the condition does not become true before the maximum duration, the
    + * interval will stop and the test step will fail in the kill timer.
    + *
    + * @param {goog.testing.ContinuationTestCase.Step} step The waiting test step.
    + * @param {Function} condition The test condition.
    + * @param {number} startTime Time when the test step began waiting.
    + * @param {number} interval The duration in milliseconds to wait between tests.
    + * @param {number} timeout The maximum amount of time to wait for the condition
    + *     to become true. Measured from the startTime in milliseconds.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.prototype.testCondition_ = function(
    +    step,
    +    condition,
    +    startTime,
    +    interval,
    +    timeout) {
    +
    +  var duration = goog.now() - startTime;
    +
    +  if (condition()) {
    +    this.handleComplete_(step);
    +  } else if (duration < timeout) {
    +    step.setTimeout(goog.bind(this.testCondition_,
    +                              this,
    +                              step,
    +                              condition,
    +                              startTime,
    +                              interval,
    +                              timeout),
    +                    interval);
    +  } else {
    +    this.handleTimeout_(step, duration);
    +  }
    +};
    +
    +
    +
    +/**
    + * Creates a continuation test case, which consists of multiple test steps that
    + * occur in several phases.
    + *
    + * The steps are distributed between setUp, test, and tearDown phases. During
    + * the execution of each step, 0 or more steps may be added to the current
    + * phase. Once all steps in a phase have completed, the next phase will be
    + * executed.
    + *
    + * If any errors occur (such as an assertion failure), the setUp and Test phases
    + * will be cancelled immediately. The tearDown phase will always start, but may
    + * be cancelled as well if it raises an error.
    + *
    + * @param {goog.testing.TestCase.Test} setUp A setUp test method to run before
    + *     the main test phase.
    + * @param {goog.testing.TestCase.Test} test A test method to run.
    + * @param {goog.testing.TestCase.Test} tearDown A tearDown test method to run
    + *     after the test method completes or fails.
    + * @constructor
    + * @extends {goog.testing.TestCase.Test}
    + * @final
    + */
    +goog.testing.ContinuationTestCase.Test = function(setUp, test, tearDown) {
    +  // This test container has a name, but no evaluation function or scope.
    +  goog.testing.TestCase.Test.call(this, test.name, null, null);
    +
    +  /**
    +   * The list of test steps to run during setUp.
    +   * @type {Array<goog.testing.TestCase.Test>}
    +   * @private
    +   */
    +  this.setUp_ = [setUp];
    +
    +  /**
    +   * The list of test steps to run for the actual test.
    +   * @type {Array<goog.testing.TestCase.Test>}
    +   * @private
    +   */
    +  this.test_ = [test];
    +
    +  /**
    +   * The list of test steps to run during the tearDown phase.
    +   * @type {Array<goog.testing.TestCase.Test>}
    +   * @private
    +   */
    +  this.tearDown_ = [tearDown];
    +};
    +goog.inherits(goog.testing.ContinuationTestCase.Test,
    +              goog.testing.TestCase.Test);
    +
    +
    +/**
    + * The first error encountered during the test run, if any.
    + * @type {Error}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.error_ = null;
    +
    +
    +/**
    + * @return {Error} The first error to be raised during the test run or null if
    + *     no errors occurred.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.getError = function() {
    +  return this.error_;
    +};
    +
    +
    +/**
    + * Sets an error for the test so it can be reported. Only the first error set
    + * during a test will be reported. Additional errors that occur in later test
    + * phases will be discarded.
    + * @param {Error} e An error.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.setError = function(e) {
    +  this.error_ = this.error_ || e;
    +};
    +
    +
    +/**
    + * @return {Array<goog.testing.TestCase.Test>} The current phase of steps
    + *    being processed. Returns null if all steps have been completed.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.getCurrentPhase = function() {
    +  if (this.setUp_.length) {
    +    return this.setUp_;
    +  }
    +
    +  if (this.test_.length) {
    +    return this.test_;
    +  }
    +
    +  if (this.tearDown_.length) {
    +    return this.tearDown_;
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Adds a new test step to the end of the current phase. The new step will wait
    + * for a condition to be met before running, or will fail after a timeout.
    + * @param {goog.testing.ContinuationTestCase.Step} step The test step to add.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.addStep = function(step) {
    +  var phase = this.getCurrentPhase();
    +  if (phase) {
    +    phase.push(step);
    +  } else {
    +    throw Error('Attempted to add a step to a completed test.');
    +  }
    +};
    +
    +
    +/**
    + * Cancels all remaining steps in the current phase. Called after an error in
    + * any phase occurs.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.cancelCurrentPhase =
    +    function() {
    +  this.cancelPhase_(this.getCurrentPhase());
    +};
    +
    +
    +/**
    + * Skips the rest of the setUp and test phases, but leaves the tearDown phase to
    + * clean up.
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.cancelTestPhase = function() {
    +  this.cancelPhase_(this.setUp_);
    +  this.cancelPhase_(this.test_);
    +};
    +
    +
    +/**
    + * Clears a test phase and cancels any pending steps found.
    + * @param {Array<goog.testing.TestCase.Test>} phase A list of test steps.
    + * @private
    + */
    +goog.testing.ContinuationTestCase.Test.prototype.cancelPhase_ =
    +    function(phase) {
    +  while (phase && phase.length) {
    +    var step = phase.pop();
    +    if (step instanceof goog.testing.ContinuationTestCase.Step) {
    +      step.clearTimeout();
    +    }
    +  }
    +};
    +
    +
    +
    +/**
    + * Constructs a single step in a larger continuation test. Each step is similar
    + * to a typical TestCase test, except it may wait for an event or timeout to
    + * occur before running the test function.
    + *
    + * @param {string} name The test name.
    + * @param {Function} ref The test function to run.
    + * @param {Object=} opt_scope The object context to run the test in.
    + * @constructor
    + * @extends {goog.testing.TestCase.Test}
    + * @final
    + */
    +goog.testing.ContinuationTestCase.Step = function(name, ref, opt_scope) {
    +  goog.testing.TestCase.Test.call(this, name, ref, opt_scope);
    +};
    +goog.inherits(goog.testing.ContinuationTestCase.Step,
    +              goog.testing.TestCase.Test);
    +
    +
    +/**
    + * Whether the step is currently waiting for a condition to continue. All new
    + * steps begin in wait state.
    + * @type {boolean}
    + */
    +goog.testing.ContinuationTestCase.Step.prototype.waiting = true;
    +
    +
    +/**
    + * A saved reference to window.clearTimeout so that MockClock or other overrides
    + * don't affect continuation timeouts.
    + * @type {Function}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.Step.protectedClearTimeout_ =
    +    window.clearTimeout;
    +
    +
    +/**
    + * A saved reference to window.setTimeout so that MockClock or other overrides
    + * don't affect continuation timeouts.
    + * @type {Function}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.Step.protectedSetTimeout_ = window.setTimeout;
    +
    +
    +/**
    + * Key to this step's timeout. If the step is waiting for an event, the timeout
    + * will be used as a kill timer. If the step is waiting
    + * @type {number}
    + * @private
    + */
    +goog.testing.ContinuationTestCase.Step.prototype.timeout_;
    +
    +
    +/**
    + * Starts a timeout for this step. Each step may have only one timeout active at
    + * a time.
    + * @param {Function} func The function to call after the timeout.
    + * @param {number} duration The number of milliseconds to wait before invoking
    + *     the function.
    + */
    +goog.testing.ContinuationTestCase.Step.prototype.setTimeout =
    +    function(func, duration) {
    +
    +  this.clearTimeout();
    +
    +  var setTimeout = goog.testing.ContinuationTestCase.Step.protectedSetTimeout_;
    +  this.timeout_ = setTimeout(func, duration);
    +};
    +
    +
    +/**
    + * Clears the current timeout if it is active.
    + */
    +goog.testing.ContinuationTestCase.Step.prototype.clearTimeout = function() {
    +  if (this.timeout_) {
    +    var clear = goog.testing.ContinuationTestCase.Step.protectedClearTimeout_;
    +
    +    clear(this.timeout_);
    +    delete this.timeout_;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.html b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.html
    new file mode 100644
    index 0000000000..ae11bfbe3a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author: brenneman@google.com (Shawn Brenneman)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.ContinuationTestCase
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.ContinuationTestCaseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.js b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.js
    new file mode 100644
    index 0000000000..e97a3e269b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/continuationtestcase_test.js
    @@ -0,0 +1,346 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.ContinuationTestCaseTest');
    +goog.setTestOnly('goog.testing.ContinuationTestCaseTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.ContinuationTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.jsunit');
    +
    +/**
    + * @fileoverview This test file uses the ContinuationTestCase to test itself,
    + * which is a little confusing. It's also difficult to write a truly effective
    + * test, since testing a failure causes an actual failure in the test runner.
    + * All tests have been manually verified using a sophisticated combination of
    + * alerts and false assertions.
    + */
    +
    +var testCase = new goog.testing.ContinuationTestCase('Continuation Test Case');
    +testCase.autoDiscoverTests();
    +
    +// Standalone Closure Test Runner.
    +if (typeof G_testRunner != 'undefined') {
    +  G_testRunner.initialize(testCase);
    +}
    +
    +
    +var clock = new goog.testing.MockClock();
    +var count = 0;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +function setUpPage() {
    +  count = testCase.getCount();
    +}
    +
    +
    +/**
    + * Resets the mock clock. Includes a wait step to verify that setUp routines
    + * can contain continuations.
    + */
    +function setUp() {
    +  waitForTimeout(function() {
    +    // Pointless assertion to verify that setUp methods can contain waits.
    +    assertEquals(count, testCase.getCount());
    +  }, 0);
    +
    +  clock.reset();
    +}
    +
    +
    +/**
    + * Uninstalls the mock clock if it was installed, and restores the Step timeout
    + * functions to the default window implementations.
    + */
    +function tearDown() {
    +  clock.uninstall();
    +  stubs.reset();
    +
    +  waitForTimeout(function() {
    +    // Pointless assertion to verify that tearDown methods can contain waits.
    +    assertTrue(testCase.now() >= testCase.startTime_);
    +  }, 0);
    +}
    +
    +
    +/**
    + * Installs the Mock Clock and replaces the Step timeouts with the mock
    + * implementations.
    + */
    +function installMockClock() {
    +  clock.install();
    +
    +  // Overwrite the "protected" setTimeout and clearTimeout with the versions
    +  // replaced by MockClock. Normal tests should never do this, but we need to
    +  // test the ContinuationTest itself.
    +  stubs.set(goog.testing.ContinuationTestCase.Step, 'protectedClearTimeout_',
    +            window.clearTimeout);
    +  stubs.set(goog.testing.ContinuationTestCase.Step, 'protectedSetTimeout_',
    +            window.setTimeout);
    +}
    +
    +
    +/**
    + * @return {goog.testing.ContinuationTestCase.Step} A generic step in a
    + *     continuation test.
    + */
    +function getSampleStep() {
    +  return new goog.testing.ContinuationTestCase.Step('test', function() {});
    +}
    +
    +
    +/**
    + * @return {goog.testing.ContinuationTestCase.Test} A simple continuation test
    + *     with generic setUp, test, and tearDown functions.
    + */
    +function getSampleTest() {
    +  var setupStep = new goog.testing.TestCase.Test('setup', function() {});
    +  var testStep = new goog.testing.TestCase.Test('test', function() {});
    +  var teardownStep = new goog.testing.TestCase.Test('teardown', function() {});
    +
    +  return new goog.testing.ContinuationTestCase.Test(setupStep,
    +                                                    testStep,
    +                                                    teardownStep);
    +}
    +
    +
    +function testStepWaiting() {
    +  var step = getSampleStep();
    +  assertTrue(step.waiting);
    +}
    +
    +
    +function testStepSetTimeout() {
    +  installMockClock();
    +  var step = getSampleStep();
    +
    +  var timeoutReached = false;
    +  step.setTimeout(function() {timeoutReached = true}, 100);
    +
    +  clock.tick(50);
    +  assertFalse(timeoutReached);
    +  clock.tick(50);
    +  assertTrue(timeoutReached);
    +}
    +
    +
    +function testStepClearTimeout() {
    +  var step = new goog.testing.ContinuationTestCase.Step('test', function() {});
    +
    +  var timeoutReached = false;
    +  step.setTimeout(function() {timeoutReached = true}, 100);
    +
    +  clock.tick(50);
    +  assertFalse(timeoutReached);
    +  step.clearTimeout();
    +  clock.tick(50);
    +  assertFalse(timeoutReached);
    +}
    +
    +
    +function testTestPhases() {
    +  var test = getSampleTest();
    +
    +  assertEquals('setup', test.getCurrentPhase()[0].name);
    +  test.cancelCurrentPhase();
    +
    +  assertEquals('test', test.getCurrentPhase()[0].name);
    +  test.cancelCurrentPhase();
    +
    +  assertEquals('teardown', test.getCurrentPhase()[0].name);
    +  test.cancelCurrentPhase();
    +
    +  assertNull(test.getCurrentPhase());
    +}
    +
    +
    +function testTestSetError() {
    +  var test = getSampleTest();
    +
    +  var error1 = new Error('Oh noes!');
    +  var error2 = new Error('B0rken.');
    +
    +  assertNull(test.getError());
    +  test.setError(error1);
    +  assertEquals(error1, test.getError());
    +  test.setError(error2);
    +  assertEquals('Once an error has been set, it should not be overwritten.',
    +               error1, test.getError());
    +}
    +
    +
    +function testAddStep() {
    +  var test = getSampleTest();
    +  var step = getSampleStep();
    +
    +  // Try adding a step to each phase and then cancelling the phase.
    +  for (var i = 0; i < 3; i++) {
    +    assertEquals(1, test.getCurrentPhase().length);
    +    test.addStep(step);
    +
    +    assertEquals(2, test.getCurrentPhase().length);
    +    assertEquals(step, test.getCurrentPhase()[1]);
    +    test.cancelCurrentPhase();
    +  }
    +
    +  assertNull(test.getCurrentPhase());
    +}
    +
    +
    +function testCancelTestPhase() {
    +  var test = getSampleTest();
    +
    +  test.cancelTestPhase();
    +  assertEquals('teardown', test.getCurrentPhase()[0].name);
    +
    +  test = getSampleTest();
    +  test.cancelCurrentPhase();
    +  test.cancelTestPhase();
    +  assertEquals('teardown', test.getCurrentPhase()[0].name);
    +
    +  test = getSampleTest();
    +  test.cancelTestPhase();
    +  test.cancelTestPhase();
    +  assertEquals('teardown', test.getCurrentPhase()[0].name);
    +}
    +
    +
    +function testWaitForTimeout() {
    +  var reachedA = false;
    +  var reachedB = false;
    +  var reachedC = false;
    +
    +  waitForTimeout(function a() {
    +    reachedA = true;
    +
    +    assertTrue('A must be true at callback a.', reachedA);
    +    assertFalse('B must be false at callback a.', reachedB);
    +    assertFalse('C must be false at callback a.', reachedC);
    +  }, 10);
    +
    +  waitForTimeout(function b() {
    +    reachedB = true;
    +
    +    assertTrue('A must be true at callback b.', reachedA);
    +    assertTrue('B must be true at callback b.', reachedB);
    +    assertFalse('C must be false at callback b.', reachedC);
    +  }, 20);
    +
    +  waitForTimeout(function c() {
    +    reachedC = true;
    +
    +    assertTrue('A must be true at callback c.', reachedA);
    +    assertTrue('B must be true at callback c.', reachedB);
    +    assertTrue('C must be true at callback c.', reachedC);
    +  }, 20);
    +
    +  assertFalse('a', reachedA);
    +  assertFalse('b', reachedB);
    +  assertFalse('c', reachedC);
    +}
    +
    +
    +function testWaitForEvent() {
    +  var et = new goog.events.EventTarget();
    +
    +  var eventFired = false;
    +  goog.events.listen(et, 'testPrefire', function() {
    +    eventFired = true;
    +    et.dispatchEvent('test');
    +  });
    +
    +  waitForEvent(et, 'test', function() {
    +    assertTrue(eventFired);
    +  });
    +
    +  et.dispatchEvent('testPrefire');
    +}
    +
    +
    +function testWaitForCondition() {
    +  var counter = 0;
    +
    +  waitForCondition(function() {
    +    return ++counter >= 2;
    +  }, function() {
    +    assertEquals(2, counter);
    +  }, 10, 200);
    +}
    +
    +
    +function testOutOfOrderWaits() {
    +  var counter = 0;
    +
    +  // Note that if the delta between the timeout is too small, two
    +  // continuation may be invoked at the same timer tick, using the
    +  // registration order.
    +  waitForTimeout(function() {assertEquals(3, ++counter);}, 200);
    +  waitForTimeout(function() {assertEquals(1, ++counter);}, 0);
    +  waitForTimeout(function() {assertEquals(2, ++counter);}, 100);
    +}
    +
    +
    +/*
    + * Any of the test functions below (except the condition check passed into
    + * waitForCondition) can raise an assertion successfully. Any level of nested
    + * test steps should be possible, in any configuration.
    + */
    +
    +var testObj;
    +
    +
    +function testCrazyNestedWaitFunction() {
    +  testObj = {
    +    lock: true,
    +    et: new goog.events.EventTarget(),
    +    steps: 0
    +  };
    +
    +  waitForTimeout(handleTimeout, 10);
    +  waitForEvent(testObj.et, 'test', handleEvent);
    +  waitForCondition(condition, handleCondition, 1);
    +}
    +
    +function handleTimeout() {
    +  testObj.steps++;
    +  assertEquals('handleTimeout should be called first.', 1, testObj.steps);
    +  waitForTimeout(fireEvent, 10);
    +}
    +
    +function fireEvent() {
    +  testObj.steps++;
    +  assertEquals('fireEvent should be called second.', 2, testObj.steps);
    +  testObj.et.dispatchEvent('test');
    +}
    +
    +function handleEvent() {
    +  testObj.steps++;
    +  assertEquals('handleEvent should be called third.', 3, testObj.steps);
    +  testObj.lock = false;
    +}
    +
    +function condition() {
    +  return !testObj.lock;
    +}
    +
    +function handleCondition() {
    +  assertFalse(testObj.lock);
    +  testObj.steps++;
    +  assertEquals('handleCondition should be called last.', 4, testObj.steps);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase.js b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase.js
    new file mode 100644
    index 0000000000..e903efdc18
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase.js
    @@ -0,0 +1,154 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Defines DeferredTestCase class. By calling waitForDeferred(),
    + * tests in DeferredTestCase can wait for a Deferred object to complete its
    + * callbacks before continuing to the next test.
    + *
    + * Example Usage:
    + *
    + *   var deferredTestCase = goog.testing.DeferredTestCase.createAndInstall();
    + *   // Optionally, set a longer-than-usual step timeout.
    + *   deferredTestCase.stepTimeout = 15 * 1000; // 15 seconds
    + *
    + *   function testDeferredCallbacks() {
    + *     var callbackTime = goog.now();
    + *     var callbacks = new goog.async.Deferred();
    + *     deferredTestCase.addWaitForAsync('Waiting for 1st callback', callbacks);
    + *     callbacks.addCallback(
    + *         function() {
    + *           assertTrue(
    + *               'We\'re going back in time!', goog.now() >= callbackTime);
    + *           callbackTime = goog.now();
    + *         });
    + *     deferredTestCase.addWaitForAsync('Waiting for 2nd callback', callbacks);
    + *     callbacks.addCallback(
    + *         function() {
    + *           assertTrue(
    + *               'We\'re going back in time!', goog.now() >= callbackTime);
    + *           callbackTime = goog.now();
    + *         });
    + *     deferredTestCase.addWaitForAsync('Waiting for last callback', callbacks);
    + *     callbacks.addCallback(
    + *         function() {
    + *           assertTrue(
    + *               'We\'re going back in time!', goog.now() >= callbackTime);
    + *           callbackTime = goog.now();
    + *         });
    + *
    + *     deferredTestCase.waitForDeferred(callbacks);
    + *   }
    + *
    + * Note that DeferredTestCase still preserves the functionality of
    + * AsyncTestCase.
    + *
    + * @see.goog.async.Deferred
    + * @see goog.testing.AsyncTestCase
    + */
    +
    +goog.provide('goog.testing.DeferredTestCase');
    +
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.TestCase');
    +
    +
    +
    +/**
    + * A test case that can asynchronously wait on a Deferred object.
    + * @param {string=} opt_name A descriptive name for the test case.
    + * @constructor
    + * @extends {goog.testing.AsyncTestCase}
    + */
    +goog.testing.DeferredTestCase = function(opt_name) {
    +  goog.testing.AsyncTestCase.call(this, opt_name);
    +};
    +goog.inherits(goog.testing.DeferredTestCase, goog.testing.AsyncTestCase);
    +
    +
    +/**
    + * Preferred way of creating a DeferredTestCase. Creates one and initializes it
    + * with the G_testRunner.
    + * @param {string=} opt_name A descriptive name for the test case.
    + * @return {!goog.testing.DeferredTestCase} The created DeferredTestCase.
    + */
    +goog.testing.DeferredTestCase.createAndInstall = function(opt_name) {
    +  var deferredTestCase = new goog.testing.DeferredTestCase(opt_name);
    +  goog.testing.TestCase.initializeTestRunner(deferredTestCase);
    +  return deferredTestCase;
    +};
    +
    +
    +/**
    + * Handler for when the test produces an error.
    + * @param {Error|string} err The error object.
    + * @protected
    + * @throws Always throws a ControlBreakingException.
    + */
    +goog.testing.DeferredTestCase.prototype.onError = function(err) {
    +  this.doAsyncError(err);
    +};
    +
    +
    +/**
    + * Handler for when the test succeeds.
    + * @protected
    + */
    +goog.testing.DeferredTestCase.prototype.onSuccess = function() {
    +  this.continueTesting();
    +};
    +
    +
    +/**
    + * Adds a callback to update the wait message of this async test case. Using
    + * this method generously also helps to document the test flow.
    + * @param {string} msg The update wait status message.
    + * @param {goog.async.Deferred} d The deferred object to add the waitForAsync
    + *     callback to.
    + * @see goog.testing.AsyncTestCase#waitForAsync
    + */
    +goog.testing.DeferredTestCase.prototype.addWaitForAsync = function(msg, d) {
    +  d.addCallback(goog.bind(this.waitForAsync, this, msg));
    +};
    +
    +
    +/**
    + * Wires up given Deferred object to the test case, then starts the
    + * goog.async.Deferred object's callback.
    + * @param {!string|goog.async.Deferred} a The wait status message or the
    + *     deferred object to wait for.
    + * @param {goog.async.Deferred=} opt_b The deferred object to wait for.
    + */
    +goog.testing.DeferredTestCase.prototype.waitForDeferred = function(a, opt_b) {
    +  var waitMsg;
    +  var deferred;
    +  switch (arguments.length) {
    +    case 1:
    +      deferred = a;
    +      waitMsg = null;
    +      break;
    +    case 2:
    +      deferred = opt_b;
    +      waitMsg = a;
    +      break;
    +    default: // Shouldn't be here in compiled mode
    +      throw Error('Invalid number of arguments');
    +  }
    +  deferred.addCallbacks(this.onSuccess, this.onError, this);
    +  if (!waitMsg) {
    +    waitMsg = 'Waiting for deferred in ' + this.getCurrentStepName();
    +  }
    +  this.waitForAsync( /** @type {!string} */ (waitMsg));
    +  deferred.callback(true);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.html b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.html
    new file mode 100644
    index 0000000000..d5fbd69062
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.DeferredTestCase Asyncronous Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.DeferredTestCaseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.js b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.js
    new file mode 100644
    index 0000000000..d0fd47200e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/deferredtestcase_test.js
    @@ -0,0 +1,137 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.DeferredTestCaseTest');
    +goog.setTestOnly('goog.testing.DeferredTestCaseTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.testing.DeferredTestCase');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.TestRunner');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var deferredTestCase =
    +    goog.testing.DeferredTestCase.createAndInstall(document.title);
    +var testTestCase;
    +var runner;
    +
    +// Optionally, set a longer-than-usual step timeout.
    +deferredTestCase.stepTimeout = 15 * 1000; // 15 seconds
    +
    +// This is the sample code in deferredtestcase.js
    +function testDeferredCallbacks() {
    +  var callbackTime = goog.now();
    +  var callbacks = new goog.async.Deferred();
    +  deferredTestCase.addWaitForAsync('Waiting for 1st callback', callbacks);
    +  callbacks.addCallback(
    +      function() {
    +        assertTrue(
    +            'We\'re going back in time!', goog.now() >= callbackTime);
    +        callbackTime = goog.now();
    +      });
    +  deferredTestCase.addWaitForAsync('Waiting for 2nd callback', callbacks);
    +  callbacks.addCallback(
    +      function() {
    +        assertTrue(
    +            'We\'re going back in time!', goog.now() >= callbackTime);
    +        callbackTime = goog.now();
    +      });
    +  deferredTestCase.addWaitForAsync('Waiting for last callback', callbacks);
    +  callbacks.addCallback(
    +      function() {
    +        assertTrue(
    +            'We\'re going back in time!', goog.now() >= callbackTime);
    +        callbackTime = goog.now();
    +      });
    +
    +  deferredTestCase.waitForDeferred(callbacks);
    +}
    +
    +function createDeferredTestCase(d) {
    +  testTestCase = new goog.testing.DeferredTestCase('Foobar TestCase');
    +  testTestCase.add(new goog.testing.TestCase.Test(
    +      'Foobar Test',
    +      function() {
    +        this.waitForDeferred(d);
    +      },
    +      testTestCase));
    +
    +  var testCompleteCallback = new goog.async.Deferred();
    +  testTestCase.setCompletedCallback(
    +      function() {
    +        testCompleteCallback.callback(true);
    +      });
    +
    +  // We're not going to use the runner to run the test, but we attach one
    +  // here anyway because without a runner TestCase throws an exception in
    +  // finalize().
    +  var runner = new goog.testing.TestRunner();
    +  runner.initialize(testTestCase);
    +
    +  return testCompleteCallback;
    +}
    +
    +function testDeferredWait() {
    +  var d = new goog.async.Deferred();
    +  deferredTestCase.addWaitForAsync('Foobar', d);
    +  d.addCallback(function() {
    +    return goog.async.Deferred.succeed(true);
    +  });
    +  deferredTestCase.waitForDeferred(d);
    +}
    +
    +function testNonAsync() {
    +  assertTrue(true);
    +}
    +
    +function testPassWithTestRunner() {
    +  var d = new goog.async.Deferred();
    +  d.addCallback(function() {
    +    return goog.async.Deferred.succeed(true);
    +  });
    +
    +  var testCompleteDeferred = createDeferredTestCase(d);
    +  testTestCase.execute();
    +
    +  var deferredCallbackOnPass = new goog.async.Deferred();
    +  deferredCallbackOnPass.addCallback(function() {
    +    return testCompleteDeferred;
    +  });
    +  deferredCallbackOnPass.addCallback(function() {
    +    assertTrue('Test case should have succeded.', testTestCase.isSuccess());
    +  });
    +
    +  deferredTestCase.waitForDeferred(deferredCallbackOnPass);
    +}
    +
    +function testFailWithTestRunner() {
    +  var d = new goog.async.Deferred();
    +  d.addCallback(function() {
    +    return goog.async.Deferred.fail(true);
    +  });
    +
    +  var testCompleteDeferred = createDeferredTestCase(d);
    +
    +  // Mock doAsyncError to instead let the test completes successfully,
    +  // but record the failure. The test works as is because the failing
    +  // deferred is not actually asynchronous.
    +  var mockDoAsyncError = goog.testing.recordFunction(function() {
    +    testTestCase.continueTesting();
    +  });
    +  testTestCase.doAsyncError = mockDoAsyncError;
    +
    +  testTestCase.execute();
    +  assertEquals(1, mockDoAsyncError.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/dom.js b/mapbox-gl/closure-library/closure/goog/testing/dom.js
    new file mode 100644
    index 0000000000..a9df88037d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/dom.js
    @@ -0,0 +1,624 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Testing utilities for DOM related tests.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.testing.dom');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeIterator');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagIterator');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.iter');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * @return {!Node} A DIV node with a unique ID identifying the
    + *     {@code END_TAG_MARKER_}.
    + * @private
    + */
    +goog.testing.dom.createEndTagMarker_ = function() {
    +  var marker = goog.dom.createElement(goog.dom.TagName.DIV);
    +  marker.id = goog.getUid(marker);
    +  return marker;
    +};
    +
    +
    +/**
    + * A unique object to use as an end tag marker.
    + * @private {!Node}
    + * @const
    + */
    +goog.testing.dom.END_TAG_MARKER_ = goog.testing.dom.createEndTagMarker_();
    +
    +
    +/**
    + * Tests if the given iterator over nodes matches the given Array of node
    + * descriptors.  Throws an error if any match fails.
    + * @param {goog.iter.Iterator} it  An iterator over nodes.
    + * @param {Array<Node|number|string>} array Array of node descriptors to match
    + *     against.  Node descriptors can be any of the following:
    + *         Node: Test if the two nodes are equal.
    + *         number: Test node.nodeType == number.
    + *         string starting with '#': Match the node's id with the text
    + *             after "#".
    + *         other string: Match the text node's contents.
    + */
    +goog.testing.dom.assertNodesMatch = function(it, array) {
    +  var i = 0;
    +  goog.iter.forEach(it, function(node) {
    +    if (array.length <= i) {
    +      fail('Got more nodes than expected: ' + goog.testing.dom.describeNode_(
    +          node));
    +    }
    +    var expected = array[i];
    +
    +    if (goog.dom.isNodeLike(expected)) {
    +      assertEquals('Nodes should match at position ' + i, expected, node);
    +    } else if (goog.isNumber(expected)) {
    +      assertEquals('Node types should match at position ' + i, expected,
    +          node.nodeType);
    +    } else if (expected.charAt(0) == '#') {
    +      assertEquals('Expected element at position ' + i,
    +          goog.dom.NodeType.ELEMENT, node.nodeType);
    +      var expectedId = expected.substr(1);
    +      assertEquals('IDs should match at position ' + i,
    +          expectedId, node.id);
    +
    +    } else {
    +      assertEquals('Expected text node at position ' + i,
    +          goog.dom.NodeType.TEXT, node.nodeType);
    +      assertEquals('Node contents should match at position ' + i,
    +          expected, node.nodeValue);
    +    }
    +
    +    i++;
    +  });
    +
    +  assertEquals('Used entire match array', array.length, i);
    +};
    +
    +
    +/**
    + * Exposes a node as a string.
    + * @param {Node} node A node.
    + * @return {string} A string representation of the node.
    + */
    +goog.testing.dom.exposeNode = function(node) {
    +  return (node.tagName || node.nodeValue) + (node.id ? '#' + node.id : '') +
    +      ':"' + (node.innerHTML || '') + '"';
    +};
    +
    +
    +/**
    + * Exposes the nodes of a range wrapper as a string.
    + * @param {goog.dom.AbstractRange} range A range.
    + * @return {string} A string representation of the range.
    + */
    +goog.testing.dom.exposeRange = function(range) {
    +  // This is deliberately not implemented as
    +  // goog.dom.AbstractRange.prototype.toString, because it is non-authoritative.
    +  // Two equivalent ranges may have very different exposeRange values, and
    +  // two different ranges may have equal exposeRange values.
    +  // (The mapping of ranges to DOM nodes/offsets is a many-to-many mapping).
    +  if (!range) {
    +    return 'null';
    +  }
    +  return goog.testing.dom.exposeNode(range.getStartNode()) + ':' +
    +         range.getStartOffset() + ' to ' +
    +         goog.testing.dom.exposeNode(range.getEndNode()) + ':' +
    +         range.getEndOffset();
    +};
    +
    +
    +/**
    + * Determines if the current user agent matches the specified string.  Returns
    + * false if the string does specify at least one user agent but does not match
    + * the running agent.
    + * @param {string} userAgents Space delimited string of user agents.
    + * @return {boolean} Whether the user agent was matched.  Also true if no user
    + *     agent was listed in the expectation string.
    + * @private
    + */
    +goog.testing.dom.checkUserAgents_ = function(userAgents) {
    +  if (goog.string.startsWith(userAgents, '!')) {
    +    if (goog.string.contains(userAgents, ' ')) {
    +      throw new Error('Only a single negative user agent may be specified');
    +    }
    +    return !goog.userAgent[userAgents.substr(1)];
    +  }
    +
    +  var agents = userAgents.split(' ');
    +  var hasUserAgent = false;
    +  for (var i = 0, len = agents.length; i < len; i++) {
    +    var cls = agents[i];
    +    if (cls in goog.userAgent) {
    +      hasUserAgent = true;
    +      if (goog.userAgent[cls]) {
    +        return true;
    +      }
    +    }
    +  }
    +  // If we got here, there was a user agent listed but we didn't match it.
    +  return !hasUserAgent;
    +};
    +
    +
    +/**
    + * Map function that converts end tags to a specific object.
    + * @param {Node} node The node to map.
    + * @param {undefined} ignore Always undefined.
    + * @param {!goog.iter.Iterator<Node>} iterator The iterator.
    + * @return {Node} The resulting iteration item.
    + * @private
    + */
    +goog.testing.dom.endTagMap_ = function(node, ignore, iterator) {
    +  return iterator.isEndTag() ? goog.testing.dom.END_TAG_MARKER_ : node;
    +};
    +
    +
    +/**
    + * Check if the given node is important.  A node is important if it is a
    + * non-empty text node, a non-annotated element, or an element annotated to
    + * match on this user agent.
    + * @param {Node} node The node to test.
    + * @return {boolean} Whether this node should be included for iteration.
    + * @private
    + */
    +goog.testing.dom.nodeFilter_ = function(node) {
    +  if (node.nodeType == goog.dom.NodeType.TEXT) {
    +    // If a node is part of a string of text nodes and it has spaces in it,
    +    // we allow it since it's going to affect the merging of nodes done below.
    +    if (goog.string.isBreakingWhitespace(node.nodeValue) &&
    +        (!node.previousSibling ||
    +             node.previousSibling.nodeType != goog.dom.NodeType.TEXT) &&
    +        (!node.nextSibling ||
    +             node.nextSibling.nodeType != goog.dom.NodeType.TEXT)) {
    +      return false;
    +    }
    +    // Allow optional text to be specified as [[BROWSER1 BROWSER2]]Text
    +    var match = node.nodeValue.match(/^\[\[(.+)\]\]/);
    +    if (match) {
    +      return goog.testing.dom.checkUserAgents_(match[1]);
    +    }
    +  } else if (node.className) {
    +    return goog.testing.dom.checkUserAgents_(node.className);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Determines the text to match from the given node, removing browser
    + * specification strings.
    + * @param {Node} node The node expected to match.
    + * @return {string} The text, stripped of browser specification strings.
    + * @private
    + */
    +goog.testing.dom.getExpectedText_ = function(node) {
    +  // Strip off the browser specifications.
    +  return node.nodeValue.match(/^(\[\[.+\]\])?(.*)/)[2];
    +};
    +
    +
    +/**
    + * Describes the given node.
    + * @param {Node} node The node to describe.
    + * @return {string} A description of the node.
    + * @private
    + */
    +goog.testing.dom.describeNode_ = function(node) {
    +  if (node.nodeType == goog.dom.NodeType.TEXT) {
    +    return '[Text: ' + node.nodeValue + ']';
    +  } else {
    +    return '<' + node.tagName + (node.id ? ' #' + node.id : '') + ' .../>';
    +  }
    +};
    +
    +
    +/**
    + * Assert that the html in {@code actual} is substantially similar to
    + * htmlPattern.  This method tests for the same set of styles, for the same
    + * order of nodes, and the presence of attributes.  Breaking whitespace nodes
    + * are ignored.  Elements can be
    + * annotated with classnames corresponding to keys in goog.userAgent and will be
    + * expected to show up in that user agent and expected not to show up in
    + * others.
    + * @param {string} htmlPattern The pattern to match.
    + * @param {!Node} actual The element to check: its contents are matched
    + *     against the HTML pattern.
    + * @param {boolean=} opt_strictAttributes If false, attributes that appear in
    + *     htmlPattern must be in actual, but actual can have attributes not
    + *     present in htmlPattern.  If true, htmlPattern and actual must have the
    + *     same set of attributes.  Default is false.
    + */
    +goog.testing.dom.assertHtmlContentsMatch = function(htmlPattern, actual,
    +    opt_strictAttributes) {
    +  var div = goog.dom.createDom(goog.dom.TagName.DIV);
    +  div.innerHTML = htmlPattern;
    +
    +  var errorSuffix = '\nExpected\n' + htmlPattern + '\nActual\n' +
    +      actual.innerHTML;
    +
    +  var actualIt = goog.iter.filter(
    +      goog.iter.map(new goog.dom.TagIterator(actual),
    +          goog.testing.dom.endTagMap_),
    +      goog.testing.dom.nodeFilter_);
    +
    +  var expectedIt = goog.iter.filter(new goog.dom.NodeIterator(div),
    +      goog.testing.dom.nodeFilter_);
    +
    +  var actualNode;
    +  var preIterated = false;
    +  var advanceActualNode = function() {
    +    // If the iterator has already been advanced, don't advance it again.
    +    if (!preIterated) {
    +      actualNode = /** @type {Node} */ (goog.iter.nextOrValue(actualIt, null));
    +    }
    +    preIterated = false;
    +
    +    // Advance the iterator so long as it is return end tags.
    +    while (actualNode == goog.testing.dom.END_TAG_MARKER_) {
    +      actualNode = /** @type {Node} */ (goog.iter.nextOrValue(actualIt, null));
    +    }
    +  };
    +
    +  // HACK(brenneman): IE has unique ideas about whitespace handling when setting
    +  // innerHTML. This results in elision of leading whitespace in the expected
    +  // nodes where doing so doesn't affect visible rendering. As a workaround, we
    +  // remove the leading whitespace in the actual nodes where necessary.
    +  //
    +  // The collapsible variable tracks whether we should collapse the whitespace
    +  // in the next Text node we encounter.
    +  var IE_TEXT_COLLAPSE =
    +      goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
    +
    +  var collapsible = true;
    +
    +  var number = 0;
    +  goog.iter.forEach(expectedIt, function(expectedNode) {
    +    expectedNode = /** @type {Node} */ (expectedNode);
    +
    +    advanceActualNode();
    +    assertNotNull('Finished actual HTML before finishing expected HTML at ' +
    +                  'node number ' + number + ': ' +
    +                  goog.testing.dom.describeNode_(expectedNode) + errorSuffix,
    +                  actualNode);
    +
    +    // Do no processing for expectedNode == div.
    +    if (expectedNode == div) {
    +      return;
    +    }
    +
    +    assertEquals('Should have the same node type, got ' +
    +        goog.testing.dom.describeNode_(actualNode) + ' but expected ' +
    +        goog.testing.dom.describeNode_(expectedNode) + '.' + errorSuffix,
    +        expectedNode.nodeType, actualNode.nodeType);
    +
    +    if (expectedNode.nodeType == goog.dom.NodeType.ELEMENT) {
    +      var expectedElem = goog.asserts.assertElement(expectedNode);
    +      var actualElem = goog.asserts.assertElement(actualNode);
    +
    +      assertEquals('Tag names should match' + errorSuffix,
    +          expectedElem.tagName, actualElem.tagName);
    +      assertObjectEquals('Should have same styles' + errorSuffix,
    +          goog.style.parseStyleAttribute(expectedElem.style.cssText),
    +          goog.style.parseStyleAttribute(actualElem.style.cssText));
    +      goog.testing.dom.assertAttributesEqual_(errorSuffix, expectedElem,
    +          actualElem, !!opt_strictAttributes);
    +
    +      if (IE_TEXT_COLLAPSE &&
    +          goog.style.getCascadedStyle(actualElem, 'display') != 'inline') {
    +        // Text may be collapsed after any non-inline element.
    +        collapsible = true;
    +      }
    +    } else {
    +      // Concatenate text nodes until we reach a non text node.
    +      var actualText = actualNode.nodeValue;
    +      preIterated = true;
    +      while ((actualNode = /** @type {Node} */
    +              (goog.iter.nextOrValue(actualIt, null))) &&
    +          actualNode.nodeType == goog.dom.NodeType.TEXT) {
    +        actualText += actualNode.nodeValue;
    +      }
    +
    +      if (IE_TEXT_COLLAPSE) {
    +        // Collapse the leading whitespace, unless the string consists entirely
    +        // of whitespace.
    +        if (collapsible && !goog.string.isEmptyOrWhitespace(actualText)) {
    +          actualText = goog.string.trimLeft(actualText);
    +        }
    +        // Prepare to collapse whitespace in the next Text node if this one does
    +        // not end in a whitespace character.
    +        collapsible = /\s$/.test(actualText);
    +      }
    +
    +      var expectedText = goog.testing.dom.getExpectedText_(expectedNode);
    +      if ((actualText && !goog.string.isBreakingWhitespace(actualText)) ||
    +          (expectedText && !goog.string.isBreakingWhitespace(expectedText))) {
    +        var normalizedActual = actualText.replace(/\s+/g, ' ');
    +        var normalizedExpected = expectedText.replace(/\s+/g, ' ');
    +
    +        assertEquals('Text should match' + errorSuffix, normalizedExpected,
    +            normalizedActual);
    +      }
    +    }
    +
    +    number++;
    +  });
    +
    +  advanceActualNode();
    +  assertNull('Finished expected HTML before finishing actual HTML' +
    +      errorSuffix, goog.iter.nextOrValue(actualIt, null));
    +};
    +
    +
    +/**
    + * Assert that the html in {@code actual} is substantially similar to
    + * htmlPattern.  This method tests for the same set of styles, and for the same
    + * order of nodes.  Breaking whitespace nodes are ignored.  Elements can be
    + * annotated with classnames corresponding to keys in goog.userAgent and will be
    + * expected to show up in that user agent and expected not to show up in
    + * others.
    + * @param {string} htmlPattern The pattern to match.
    + * @param {string} actual The html to check.
    + */
    +goog.testing.dom.assertHtmlMatches = function(htmlPattern, actual) {
    +  var div = goog.dom.createDom(goog.dom.TagName.DIV);
    +  div.innerHTML = actual;
    +
    +  goog.testing.dom.assertHtmlContentsMatch(htmlPattern, div);
    +};
    +
    +
    +/**
    + * Finds the first text node descendant of root with the given content.  Note
    + * that this operates on a text node level, so if text nodes get split this
    + * may not match the user visible text.  Using normalize() may help here.
    + * @param {string|RegExp} textOrRegexp The text to find, or a regular
    + *     expression to find a match of.
    + * @param {Element} root The element to search in.
    + * @return {Node} The first text node that matches, or null if none is found.
    + */
    +goog.testing.dom.findTextNode = function(textOrRegexp, root) {
    +  var it = new goog.dom.NodeIterator(root);
    +  var ret = goog.iter.nextOrValue(goog.iter.filter(it, function(node) {
    +    if (node.nodeType == goog.dom.NodeType.TEXT) {
    +      if (goog.isString(textOrRegexp)) {
    +        return node.nodeValue == textOrRegexp;
    +      } else {
    +        return !!node.nodeValue.match(textOrRegexp);
    +      }
    +    } else {
    +      return false;
    +    }
    +  }), null);
    +  return /** @type {Node} */ (ret);
    +};
    +
    +
    +/**
    + * Assert the end points of a range.
    + *
    + * Notice that "Are two ranges visually identical?" and "Do two ranges have
    + * the same endpoint?" are independent questions. Two visually identical ranges
    + * may have different endpoints. And two ranges with the same endpoints may
    + * be visually different.
    + *
    + * @param {Node} start The expected start node.
    + * @param {number} startOffset The expected start offset.
    + * @param {Node} end The expected end node.
    + * @param {number} endOffset The expected end offset.
    + * @param {goog.dom.AbstractRange} range The actual range.
    + */
    +goog.testing.dom.assertRangeEquals = function(start, startOffset, end,
    +    endOffset, range) {
    +  assertEquals('Unexpected start node', start, range.getStartNode());
    +  assertEquals('Unexpected end node', end, range.getEndNode());
    +  assertEquals('Unexpected start offset', startOffset, range.getStartOffset());
    +  assertEquals('Unexpected end offset', endOffset, range.getEndOffset());
    +};
    +
    +
    +/**
    + * Gets the value of a DOM attribute in deterministic way.
    + * @param {!Node} node A node.
    + * @param {string} name Attribute name.
    + * @return {*} Attribute value.
    + * @private
    + */
    +goog.testing.dom.getAttributeValue_ = function(node, name) {
    +  // These hacks avoid nondetermistic results in the following cases:
    +  // IE7: document.createElement('input').height returns a random number.
    +  // FF3: getAttribute('disabled') returns different value for <div disabled="">
    +  //      and <div disabled="disabled">
    +  // WebKit: Two radio buttons with the same name can't be checked at the same
    +  //      time, even if only one of them is in the document.
    +  if (goog.userAgent.WEBKIT && node.tagName == 'INPUT' &&
    +      node['type'] == 'radio' && name == 'checked') {
    +    return false;
    +  }
    +  return goog.isDef(node[name]) &&
    +      typeof node.getAttribute(name) != typeof node[name] ?
    +      node[name] : node.getAttribute(name);
    +};
    +
    +
    +/**
    + * Assert that the attributes of two Nodes are the same (ignoring any
    + * instances of the style attribute).
    + * @param {string} errorSuffix String to add to end of error messages.
    + * @param {!Element} expectedElem The element whose attributes we are expecting.
    + * @param {!Element} actualElem The element with the actual attributes.
    + * @param {boolean} strictAttributes If false, attributes that appear in
    + *     expectedNode must also be in actualNode, but actualNode can have
    + *     attributes not present in expectedNode.  If true, expectedNode and
    + *     actualNode must have the same set of attributes.
    + * @private
    + */
    +goog.testing.dom.assertAttributesEqual_ = function(errorSuffix,
    +    expectedElem, actualElem, strictAttributes) {
    +  if (strictAttributes) {
    +    goog.testing.dom.compareClassAttribute_(expectedElem, actualElem);
    +  }
    +
    +  var expectedAttributes = expectedElem.attributes;
    +  var actualAttributes = actualElem.attributes;
    +
    +  for (var i = 0, len = expectedAttributes.length; i < len; i++) {
    +    var expectedName = expectedAttributes[i].name;
    +    var expectedValue = goog.testing.dom.getAttributeValue_(expectedElem,
    +        expectedName);
    +
    +    var actualAttribute = actualAttributes[expectedName];
    +    var actualValue = goog.testing.dom.getAttributeValue_(actualElem,
    +        expectedName);
    +
    +    // IE enumerates attribute names in the expected node that are not present,
    +    // causing an undefined actualAttribute.
    +    if (!expectedValue && !actualValue) {
    +      continue;
    +    }
    +
    +    if (expectedName == 'id' && goog.userAgent.IE) {
    +      goog.testing.dom.compareIdAttributeForIe_(
    +          /** @type {string} */ (expectedValue), actualAttribute,
    +          strictAttributes, errorSuffix);
    +      continue;
    +    }
    +
    +    if (goog.testing.dom.ignoreAttribute_(expectedName)) {
    +      continue;
    +    }
    +
    +    assertNotUndefined('Expected to find attribute with name ' +
    +        expectedName + ', in element ' +
    +        goog.testing.dom.describeNode_(actualElem) + errorSuffix,
    +        actualAttribute);
    +    assertEquals('Expected attribute ' + expectedName +
    +        ' has a different value ' + errorSuffix,
    +        expectedValue,
    +        goog.testing.dom.getAttributeValue_(actualElem, actualAttribute.name));
    +  }
    +
    +  if (strictAttributes) {
    +    for (i = 0; i < actualAttributes.length; i++) {
    +      var actualName = actualAttributes[i].name;
    +      var actualAttribute = actualAttributes.getNamedItem(actualName);
    +
    +      if (!actualAttribute || goog.testing.dom.ignoreAttribute_(actualName)) {
    +        continue;
    +      }
    +
    +      assertNotUndefined('Unexpected attribute with name ' +
    +          actualName + ' in element ' +
    +          goog.testing.dom.describeNode_(actualElem) + errorSuffix,
    +          expectedAttributes[actualName]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Assert the class attribute of actualElem is the same as the one in
    + * expectedElem, ignoring classes that are useragents.
    + * @param {!Element} expectedElem The DOM element whose class we expect.
    + * @param {!Element} actualElem The DOM element with the actual class.
    + * @private
    + */
    +goog.testing.dom.compareClassAttribute_ = function(expectedElem,
    +    actualElem) {
    +  var classes = goog.dom.classlist.get(expectedElem);
    +
    +  var expectedClasses = [];
    +  for (var i = 0, len = classes.length; i < len; i++) {
    +    if (!(classes[i] in goog.userAgent)) {
    +      expectedClasses.push(classes[i]);
    +    }
    +  }
    +  expectedClasses.sort();
    +
    +  var actualClasses = goog.array.toArray(goog.dom.classlist.get(actualElem));
    +  actualClasses.sort();
    +
    +  assertArrayEquals(
    +      'Expected class was: ' + expectedClasses.join(' ') +
    +      ', but actual class was: ' + actualElem.className,
    +      expectedClasses, actualClasses);
    +};
    +
    +
    +/**
    + * Set of attributes IE adds to elements randomly.
    + * @type {Object}
    + * @private
    + */
    +goog.testing.dom.BAD_IE_ATTRIBUTES_ = goog.object.createSet(
    +    'methods', 'CHECKED', 'dataFld', 'dataFormatAs', 'dataSrc');
    +
    +
    +/**
    + * Whether to ignore the attribute.
    + * @param {string} name Name of the attribute.
    + * @return {boolean} True if the attribute should be ignored.
    + * @private
    + */
    +goog.testing.dom.ignoreAttribute_ = function(name) {
    +  if (name == 'style' || name == 'class') {
    +    return true;
    +  }
    +  return goog.userAgent.IE && goog.testing.dom.BAD_IE_ATTRIBUTES_[name];
    +};
    +
    +
    +/**
    + * Compare id attributes for IE.  In IE, if an element lacks an id attribute
    + * in the original HTML, the element object will still have such an attribute,
    + * but its value will be the empty string.
    + * @param {string} expectedValue The expected value of the id attribute.
    + * @param {Attr} actualAttribute The actual id attribute.
    + * @param {boolean} strictAttributes Whether strict attribute checking should be
    + *     done.
    + * @param {string} errorSuffix String to append to error messages.
    + * @private
    + */
    +goog.testing.dom.compareIdAttributeForIe_ = function(expectedValue,
    +    actualAttribute, strictAttributes, errorSuffix) {
    +  if (expectedValue === '') {
    +    if (strictAttributes) {
    +      assertTrue('Unexpected attribute with name id in element ' +
    +          errorSuffix, actualAttribute.value == '');
    +    }
    +  } else {
    +    assertNotUndefined('Expected to find attribute with name id, in element ' +
    +        errorSuffix, actualAttribute);
    +    assertNotEquals('Expected to find attribute with name id, in element ' +
    +        errorSuffix, '', actualAttribute.value);
    +    assertEquals('Expected attribute has a different value ' + errorSuffix,
    +        expectedValue, actualAttribute.value);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/dom_test.html b/mapbox-gl/closure-library/closure/goog/testing/dom_test.html
    new file mode 100644
    index 0000000000..92bed453c1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/dom_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html lang="en" dir="ltr">
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <!--
    +This test has not yet been updated to run on IE8. See http://b/hotlist?id=36311
    +-->
    +  <!--meta http-equiv="X-UA-Compatible" content="IE=edge"-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    +  <title>
    +   Closure Unit Tests - goog.testing.dom
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.domTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/dom_test.js b/mapbox-gl/closure-library/closure/goog/testing/dom_test.js
    new file mode 100644
    index 0000000000..5d075a8801
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/dom_test.js
    @@ -0,0 +1,434 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.domTest');
    +goog.setTestOnly('goog.testing.domTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var root;
    +function setUpPage() {
    +  root = goog.dom.getElement('root');
    +}
    +
    +function setUp() {
    +  root.innerHTML = '';
    +}
    +
    +function testFindNode() {
    +  // Test the easiest case.
    +  root.innerHTML = 'a<br>b';
    +  assertEquals(goog.testing.dom.findTextNode('a', root), root.firstChild);
    +  assertEquals(goog.testing.dom.findTextNode('b', root), root.lastChild);
    +  assertNull(goog.testing.dom.findTextNode('c', root));
    +}
    +
    +function testFindNodeDuplicate() {
    +  // Test duplicate.
    +  root.innerHTML = 'c<br>c';
    +  assertEquals('Should return first duplicate',
    +      goog.testing.dom.findTextNode('c', root), root.firstChild);
    +}
    +
    +function findNodeWithHierarchy() {
    +  // Test a more complicated hierarchy.
    +  root.innerHTML = '<div>a<p>b<span>c</span>d</p>e</div>';
    +  assertEquals(goog.dom.TagName.DIV,
    +      goog.testing.dom.findTextNode('a', root).parentNode.tagName);
    +  assertEquals(goog.dom.TagName.P,
    +      goog.testing.dom.findTextNode('b', root).parentNode.tagName);
    +  assertEquals(goog.dom.TagName.SPAN,
    +      goog.testing.dom.findTextNode('c', root).parentNode.tagName);
    +  assertEquals(goog.dom.TagName.P,
    +      goog.testing.dom.findTextNode('d', root).parentNode.tagName);
    +  assertEquals(goog.dom.TagName.DIV,
    +      goog.testing.dom.findTextNode('e', root).parentNode.tagName);
    +}
    +
    +function setUpAssertHtmlMatches() {
    +  var tag1, tag2;
    +  if (goog.userAgent.IE) {
    +    tag1 = goog.dom.TagName.DIV;
    +  } else if (goog.userAgent.WEBKIT) {
    +    tag1 = goog.dom.TagName.P;
    +    tag2 = goog.dom.TagName.BR;
    +  } else if (goog.userAgent.GECKO) {
    +    tag1 = goog.dom.TagName.SPAN;
    +    tag2 = goog.dom.TagName.BR;
    +  }
    +
    +  var parent = goog.dom.createDom(goog.dom.TagName.DIV);
    +  root.appendChild(parent);
    +  parent.style.fontSize = '2em';
    +  parent.style.display = 'none';
    +  if (!goog.userAgent.WEBKIT) {
    +    parent.appendChild(goog.dom.createTextNode('NonWebKitText'));
    +  }
    +
    +  if (tag1) {
    +    var e1 = goog.dom.createDom(tag1);
    +    parent.appendChild(e1);
    +    parent = e1;
    +  }
    +  if (tag2) {
    +    parent.appendChild(goog.dom.createDom(tag2));
    +  }
    +  parent.appendChild(goog.dom.createTextNode('Text'));
    +  if (goog.userAgent.WEBKIT) {
    +    root.firstChild.appendChild(goog.dom.createTextNode('WebKitText'));
    +  }
    +}
    +
    +function testAssertHtmlContentsMatch() {
    +  setUpAssertHtmlMatches();
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div style="display: none; font-size: 2em">' +
    +      '[[!WEBKIT]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +      '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +      '</div>[[WEBKIT]]WebKitText',
    +      root);
    +}
    +
    +function testAssertHtmlMismatchText() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched text', function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Bad</span></p></div>' +
    +        '</div>[[WEBKIT]]Extra',
    +        root);
    +  });
    +  assertContains('Text should match', e.message);
    +}
    +
    +function testAssertHtmlMismatchTag() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched tag', function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<span style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</span>[[WEBKIT]]Extra',
    +        root);
    +  });
    +  assertContains('Tag names should match', e.message);
    +}
    +
    +function testAssertHtmlMismatchStyle() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched style', function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div style="display: none; font-size: 3em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</div>[[WEBKIT]]Extra',
    +        root);
    +  });
    +  assertContains('Should have same styles', e.message);
    +}
    +
    +function testAssertHtmlMismatchOptionalText() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched text', function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]Bad<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</div>[[WEBKIT]]Bad',
    +        root);
    +  });
    +  assertContains('Text should match', e.message);
    +}
    +
    +function testAssertHtmlMismatchExtraActualAfterText() {
    +  root.innerHTML = '<div>abc</div>def';
    +
    +  var e = assertThrows('Should fail due to extra actual nodes', function() {
    +    goog.testing.dom.assertHtmlContentsMatch('<div>abc</div>', root);
    +  });
    +  assertContains('Finished expected HTML before', e.message);
    +}
    +
    +function testAssertHtmlMismatchExtraActualAfterElement() {
    +  root.innerHTML = '<br>def';
    +
    +  var e = assertThrows('Should fail due to extra actual nodes', function() {
    +    goog.testing.dom.assertHtmlContentsMatch('<br>', root);
    +  });
    +  assertContains('Finished expected HTML before', e.message);
    +}
    +
    +function testAssertHtmlMatchesWithSplitTextNodes() {
    +  root.appendChild(goog.dom.createTextNode('1'));
    +  root.appendChild(goog.dom.createTextNode('2'));
    +  root.appendChild(goog.dom.createTextNode('3'));
    +  goog.testing.dom.assertHtmlContentsMatch('123', root);
    +}
    +
    +function testAssertHtmlMatchesWithDifferentlyOrderedAttributes() {
    +  root.innerHTML = '<div foo="a" bar="b" class="className"></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div bar="b" class="className" foo="a"></div>', root, true);
    +}
    +
    +function testAssertHtmlMismatchWithDifferentNumberOfAttributes() {
    +  root.innerHTML = '<div foo="a" bar="b"></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div foo="a"></div>', root, true);
    +  });
    +  assertContains('Unexpected attribute with name bar in element', e.message);
    +}
    +
    +function testAssertHtmlMismatchWithDifferentAttributeNames() {
    +  root.innerHTML = '<div foo="a" bar="b"></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div foo="a" baz="b"></div>', root, true);
    +  });
    +  assertContains('Expected to find attribute with name baz', e.message);
    +}
    +
    +function testAssertHtmlMismatchWithDifferentClassNames() {
    +  root.innerHTML = '<div class="className1"></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div class="className2"></div>', root, true);
    +  });
    +  assertContains(
    +      'Expected class was: className2, but actual class was: className1',
    +      e.message);
    +}
    +
    +function testAssertHtmlMatchesWithClassNameAndUserAgentSpecified() {
    +  root.innerHTML =
    +      '<div>' + (goog.userAgent.GECKO ? '<div class="foo"></div>' : '') +
    +      '</div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div><div class="foo GECKO"></div></div>',
    +      root, true);
    +}
    +
    +function testAssertHtmlMatchesWithClassesInDifferentOrder() {
    +  root.innerHTML = '<div class="class1 class2"></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div class="class2 class1"></div>', root, true);
    +}
    +
    +function testAssertHtmlMismatchWithDifferentAttributeValues() {
    +  root.innerHTML = '<div foo="b" bar="a"></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch(
    +        '<div foo="a" bar="a"></div>', root, true);
    +  });
    +  assertContains('Expected attribute foo has a different value', e.message);
    +}
    +
    +function testAssertHtmlMatchesWhenStrictAttributesIsFalse() {
    +  root.innerHTML = '<div foo="a" bar="b"></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div foo="a"></div>', root);
    +}
    +
    +function testAssertHtmlMatchesForMethodsAttribute() {
    +  root.innerHTML = '<a methods="get"></a>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<a></a>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<a methods="get"></a>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<a methods="get"></a>', root,
    +      true);
    +}
    +
    +function testAssertHtmlMatchesForMethodsAttribute() {
    +  root.innerHTML = '<input></input>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<input></input>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<input></input>', root, true);
    +}
    +
    +function testAssertHtmlMatchesForIdAttribute() {
    +  root.innerHTML = '<div id="foo"></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div></div>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<div id="foo"></div>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<div id="foo"></div>', root,
    +      true);
    +}
    +
    +function testAssertHtmlMatchesWhenIdIsNotSpecified() {
    +  root.innerHTML = '<div id="someId"></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div></div>', root);
    +}
    +
    +function testAssertHtmlMismatchWhenIdIsNotSpecified() {
    +  root.innerHTML = '<div id="someId"></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch('<div></div>', root, true);
    +  });
    +  assertContains('Unexpected attribute with name id in element', e.message);
    +}
    +
    +function testAssertHtmlMismatchWhenIdIsSpecified() {
    +  root.innerHTML = '<div></div>';
    +
    +  var e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch('<div id="someId"></div>', root);
    +  });
    +  assertContains('Expected to find attribute with name id, in element',
    +      e.message);
    +
    +  e = assertThrows(function() {
    +    goog.testing.dom.assertHtmlContentsMatch('<div id="someId"></div>', root,
    +        true);
    +  });
    +  assertContains('Expected to find attribute with name id, in element',
    +      e.message);
    +}
    +
    +function testAssertHtmlMatchesWhenIdIsEmpty() {
    +  root.innerHTML = '<div></div>';
    +
    +  goog.testing.dom.assertHtmlContentsMatch('<div></div>', root);
    +  goog.testing.dom.assertHtmlContentsMatch('<div></div>', root, true);
    +}
    +
    +function testAssertHtmlMatchesWithDisabledAttribute() {
    +  var disabledShortest = '<input disabled="disabled">';
    +  var disabledShort = '<input disabled="">';
    +  var disabledLong = '<input disabled="disabled">';
    +  var enabled = '<input>';
    +
    +  root.innerHTML = disabledLong;
    +  goog.testing.dom.assertHtmlContentsMatch(disabledShortest, root, true);
    +  goog.testing.dom.assertHtmlContentsMatch(disabledShort, root, true);
    +  goog.testing.dom.assertHtmlContentsMatch(disabledLong, root, true);
    +
    +
    +  var e = assertThrows('Should fail due to mismatched text', function() {
    +    goog.testing.dom.assertHtmlContentsMatch(enabled, root, true);
    +  });
    +  // Attribute value mismatch in IE.
    +  // Unexpected attribute error in other browsers.
    +  assertContains('disabled', e.message);
    +}
    +
    +function testAssertHtmlMatchesWithCheckedAttribute() {
    +  var checkedShortest = '<input type="radio" name="x" checked="checked">';
    +  var checkedShort = '<input type="radio" name="x" checked="">';
    +  var checkedLong = '<input type="radio" name="x" checked="checked">';
    +  var unchecked = '<input type="radio" name="x">';
    +
    +  root.innerHTML = checkedLong;
    +  goog.testing.dom.assertHtmlContentsMatch(checkedShortest, root, true);
    +  goog.testing.dom.assertHtmlContentsMatch(checkedShort, root, true);
    +  goog.testing.dom.assertHtmlContentsMatch(checkedLong, root, true);
    +  if (!goog.userAgent.IE) {
    +    // CHECKED attribute is ignored because it's among BAD_IE_ATTRIBUTES_.
    +    var e = assertThrows('Should fail due to mismatched text', function() {
    +      goog.testing.dom.assertHtmlContentsMatch(unchecked, root, true);
    +    });
    +    assertContains('Unexpected attribute with name checked', e.message);
    +  }
    +}
    +
    +function testAssertHtmlMatchesWithWhitespace() {
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createTextNode('  A  '));
    +  goog.testing.dom.assertHtmlContentsMatch('  A  ', root);
    +
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createTextNode('  A  '));
    +  root.appendChild(goog.dom.createDom('span', null, '  B  '));
    +  root.appendChild(goog.dom.createTextNode('  C  '));
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '  A  <span>  B  </span>  C  ', root);
    +
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createTextNode('  A'));
    +  root.appendChild(goog.dom.createDom('span', null, '  B'));
    +  root.appendChild(goog.dom.createTextNode('  C'));
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '  A<span>  B</span>  C', root);
    +}
    +
    +function testAssertHtmlMatchesWithWhitespaceAndNesting() {
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createDom('div', null,
    +      goog.dom.createDom('b', null, '  A  '),
    +      goog.dom.createDom('b', null, '  B  ')));
    +  root.appendChild(goog.dom.createDom('div', null,
    +      goog.dom.createDom('b', null, '  C  '),
    +      goog.dom.createDom('b', null, '  D  ')));
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div><b>  A  </b><b>  B  </b></div>' +
    +      '<div><b>  C  </b><b>  D  </b></div>', root);
    +
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createDom('b', null,
    +      goog.dom.createDom('b', null,
    +          goog.dom.createDom('b', null, '  A  '))));
    +  root.appendChild(goog.dom.createDom('b', null, '  B  '));
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<b><b><b>  A  </b></b></b><b>  B  </b>', root);
    +
    +  root.innerHTML = '';
    +  root.appendChild(goog.dom.createDom('div', null,
    +      goog.dom.createDom('b', null,
    +          goog.dom.createDom('b', null, '  A  '))));
    +  root.appendChild(goog.dom.createDom('b', null, '  B  '));
    +
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '<div><b><b>  A  </b></b></div><b>  B  </b>', root);
    +
    +  root.innerHTML = '&nbsp;';
    +  goog.testing.dom.assertHtmlContentsMatch(
    +      '&nbsp;', root);
    +}
    +
    +function testAssertHtmlMatches() {
    +  // Since assertHtmlMatches is based on assertHtmlContentsMatch, we leave the
    +  // majority of edge case testing to the above.  Here we just do a sanity
    +  // check.
    +  goog.testing.dom.assertHtmlMatches('<div>abc</div>', '<div>abc</div>');
    +  goog.testing.dom.assertHtmlMatches('<div>abc</div>', '<div>abc</div> ');
    +  goog.testing.dom.assertHtmlMatches(
    +      '<div style="font-size: 1px; color: red">abc</div>',
    +      '<div style="color: red;  font-size: 1px;;">abc</div>');
    +
    +  var e = assertThrows('Should fail due to mismatched text', function() {
    +    goog.testing.dom.assertHtmlMatches('<div>abc</div>', '<div>abd</div>');
    +  });
    +  assertContains('Text should match', e.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/dom.js b/mapbox-gl/closure-library/closure/goog/testing/editor/dom.js
    new file mode 100644
    index 0000000000..d30b711c63
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/dom.js
    @@ -0,0 +1,293 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Testing utilities for editor specific DOM related tests.
    + *
    + */
    +
    +goog.provide('goog.testing.editor.dom');
    +
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagIterator');
    +goog.require('goog.dom.TagWalkType');
    +goog.require('goog.iter');
    +goog.require('goog.string');
    +goog.require('goog.testing.asserts');
    +
    +
    +/**
    + * Returns the previous (in document order) node from the given node that is a
    + * non-empty text node, or null if none is found or opt_stopAt is not an
    + * ancestor of node. Note that if the given node has children, the search will
    + * start from the end tag of the node, meaning all its descendants will be
    + * included in the search, unless opt_skipDescendants is true.
    + * @param {Node} node Node to start searching from.
    + * @param {Node=} opt_stopAt Node to stop searching at (search will be
    + *     restricted to this node's subtree), defaults to the body of the document
    + *     containing node.
    + * @param {boolean=} opt_skipDescendants Whether to skip searching the given
    + *     node's descentants.
    + * @return {Text} The previous (in document order) node from the given node
    + *     that is a non-empty text node, or null if none is found.
    + */
    +goog.testing.editor.dom.getPreviousNonEmptyTextNode = function(
    +    node, opt_stopAt, opt_skipDescendants) {
    +  return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_(
    +      node, opt_stopAt, opt_skipDescendants, true);
    +};
    +
    +
    +/**
    + * Returns the next (in document order) node from the given node that is a
    + * non-empty text node, or null if none is found or opt_stopAt is not an
    + * ancestor of node. Note that if the given node has children, the search will
    + * start from the start tag of the node, meaning all its descendants will be
    + * included in the search, unless opt_skipDescendants is true.
    + * @param {Node} node Node to start searching from.
    + * @param {Node=} opt_stopAt Node to stop searching at (search will be
    + *     restricted to this node's subtree), defaults to the body of the document
    + *     containing node.
    + * @param {boolean=} opt_skipDescendants Whether to skip searching the given
    + *     node's descentants.
    + * @return {Text} The next (in document order) node from the given node that
    + *     is a non-empty text node, or null if none is found or opt_stopAt is not
    + *     an ancestor of node.
    + */
    +goog.testing.editor.dom.getNextNonEmptyTextNode = function(
    +    node, opt_stopAt, opt_skipDescendants) {
    +  return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_(
    +      node, opt_stopAt, opt_skipDescendants, false);
    +};
    +
    +
    +/**
    + * Helper that returns the previous or next (in document order) node from the
    + * given node that is a non-empty text node, or null if none is found or
    + * opt_stopAt is not an ancestor of node. Note that if the given node has
    + * children, the search will start from the end or start tag of the node
    + * (depending on whether it's searching for the previous or next node), meaning
    + * all its descendants will be included in the search, unless
    + * opt_skipDescendants is true.
    + * @param {Node} node Node to start searching from.
    + * @param {Node=} opt_stopAt Node to stop searching at (search will be
    + *     restricted to this node's subtree), defaults to the body of the document
    + *     containing node.
    + * @param {boolean=} opt_skipDescendants Whether to skip searching the given
    + *   node's descentants.
    + * @param {boolean=} opt_isPrevious Whether to search for the previous non-empty
    + *     text node instead of the next one.
    + * @return {Text} The next (in document order) node from the given node that
    + *     is a non-empty text node, or null if none is found or opt_stopAt is not
    + *     an ancestor of node.
    + * @private
    + */
    +goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_ = function(
    +    node, opt_stopAt, opt_skipDescendants, opt_isPrevious) {
    +  opt_stopAt = opt_stopAt || node.ownerDocument.body;
    +  // Initializing the iterator to iterate over the children of opt_stopAt
    +  // makes it stop only when it finishes iterating through all of that
    +  // node's children, even though we will start at a different node and exit
    +  // that starting node's subtree in the process.
    +  var iter = new goog.dom.TagIterator(opt_stopAt, opt_isPrevious);
    +
    +  // TODO(user): Move this logic to a new method in TagIterator such as
    +  // skipToNode().
    +  // Then we set the iterator to start at the given start node, not opt_stopAt.
    +  var walkType; // Let TagIterator set the initial walk type by default.
    +  var depth = goog.testing.editor.dom.getRelativeDepth_(node, opt_stopAt);
    +  if (depth == -1) {
    +    return null; // Fail because opt_stopAt is not an ancestor of node.
    +  }
    +  if (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +    if (opt_skipDescendants) {
    +      // Specifically set the initial walk type so that we skip the descendant
    +      // subtree by starting at the start if going backwards or at the end if
    +      // going forwards.
    +      walkType = opt_isPrevious ? goog.dom.TagWalkType.START_TAG :
    +                                  goog.dom.TagWalkType.END_TAG;
    +    } else {
    +      // We're starting "inside" an element node so the depth needs to be one
    +      // deeper than the node's actual depth. That's how TagIterator works!
    +      depth++;
    +    }
    +  }
    +  iter.setPosition(node, walkType, depth);
    +
    +  // Advance the iterator so it skips the start node.
    +  try {
    +    iter.next();
    +  } catch (e) {
    +    return null; // It could have been a leaf node.
    +  }
    +  // Now just get the first non-empty text node the iterator finds.
    +  var filter = goog.iter.filter(iter,
    +                                goog.testing.editor.dom.isNonEmptyTextNode_);
    +  try {
    +    return /** @type {Text} */ (filter.next());
    +  } catch (e) { // No next item is available so return null.
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether the given node is a non-empty text node.
    + * @param {Node} node Node to be checked.
    + * @return {boolean} Whether the given node is a non-empty text node.
    + * @private
    + */
    +goog.testing.editor.dom.isNonEmptyTextNode_ = function(node) {
    +  return !!node && node.nodeType == goog.dom.NodeType.TEXT && node.length > 0;
    +};
    +
    +
    +/**
    + * Returns the depth of the given node relative to the given parent node, or -1
    + * if the given node is not a descendant of the given parent node. E.g. if
    + * node == parentNode returns 0, if node.parentNode == parentNode returns 1,
    + * etc.
    + * @param {Node} node Node whose depth to get.
    + * @param {Node} parentNode Node relative to which the depth should be
    + *     calculated.
    + * @return {number} The depth of the given node relative to the given parent
    + *     node, or -1 if the given node is not a descendant of the given parent
    + *     node.
    + * @private
    + */
    +goog.testing.editor.dom.getRelativeDepth_ = function(node, parentNode) {
    +  var depth = 0;
    +  while (node) {
    +    if (node == parentNode) {
    +      return depth;
    +    }
    +    node = node.parentNode;
    +    depth++;
    +  }
    +  return -1;
    +};
    +
    +
    +/**
    + * Assert that the range is surrounded by the given strings. This is useful
    + * because different browsers can place the range endpoints inside different
    + * nodes even when visually the range looks the same. Also, there may be empty
    + * text nodes in the way (again depending on the browser) making it difficult to
    + * use assertRangeEquals.
    + * @param {string} before String that should occur immediately before the start
    + *     point of the range. If this is the empty string, assert will only succeed
    + *     if there is no text before the start point of the range.
    + * @param {string} after String that should occur immediately after the end
    + *     point of the range. If this is the empty string, assert will only succeed
    + *     if there is no text after the end point of the range.
    + * @param {goog.dom.AbstractRange} range The range to be tested.
    + * @param {Node=} opt_stopAt Node to stop searching at (search will be
    + *     restricted to this node's subtree).
    + */
    +goog.testing.editor.dom.assertRangeBetweenText = function(before,
    +                                                          after,
    +                                                          range,
    +                                                          opt_stopAt) {
    +  var previousText =
    +      goog.testing.editor.dom.getTextFollowingRange_(range, true, opt_stopAt);
    +  if (before == '') {
    +    assertNull('Expected nothing before range but found <' + previousText + '>',
    +               previousText);
    +  } else {
    +    assertNotNull('Expected <' + before + '> before range but found nothing',
    +                  previousText);
    +    assertTrue('Expected <' + before + '> before range but found <' +
    +               previousText + '>',
    +               goog.string.endsWith(
    +                   /** @type {string} */ (previousText), before));
    +  }
    +  var nextText =
    +      goog.testing.editor.dom.getTextFollowingRange_(range, false, opt_stopAt);
    +  if (after == '') {
    +    assertNull('Expected nothing after range but found <' + nextText + '>',
    +               nextText);
    +  } else {
    +    assertNotNull('Expected <' + after + '> after range but found nothing',
    +                  nextText);
    +    assertTrue('Expected <' + after + '> after range but found <' +
    +               nextText + '>',
    +               goog.string.startsWith(
    +                   /** @type {string} */ (nextText), after));
    +  }
    +};
    +
    +
    +/**
    + * Returns the text that follows the given range, where the term "follows" means
    + * "comes immediately before the start of the range" if isBefore is true, and
    + * "comes immediately after the end of the range" if isBefore is false, or null
    + * if no non-empty text node is found.
    + * @param {goog.dom.AbstractRange} range The range to search from.
    + * @param {boolean} isBefore Whether to search before the range instead of
    + *     after it.
    + * @param {Node=} opt_stopAt Node to stop searching at (search will be
    + *     restricted to this node's subtree).
    + * @return {?string} The text that follows the given range, or null if no
    + *     non-empty text node is found.
    + * @private
    + */
    +goog.testing.editor.dom.getTextFollowingRange_ = function(range,
    +                                                          isBefore,
    +                                                          opt_stopAt) {
    +  var followingTextNode;
    +  var endpointNode = isBefore ? range.getStartNode() : range.getEndNode();
    +  var endpointOffset = isBefore ? range.getStartOffset() : range.getEndOffset();
    +  var getFollowingTextNode =
    +      isBefore ? goog.testing.editor.dom.getPreviousNonEmptyTextNode :
    +                 goog.testing.editor.dom.getNextNonEmptyTextNode;
    +
    +  if (endpointNode.nodeType == goog.dom.NodeType.TEXT) {
    +    // Range endpoint is in a text node.
    +    var endText = endpointNode.nodeValue;
    +    if (isBefore ? endpointOffset > 0 : endpointOffset < endText.length) {
    +      // There is text in this node following the endpoint so return the portion
    +      // that follows the endpoint.
    +      return isBefore ? endText.substr(0, endpointOffset) :
    +                        endText.substr(endpointOffset);
    +    } else {
    +      // There is no text following the endpoint so look for the follwing text
    +      // node.
    +      followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt);
    +      return followingTextNode && followingTextNode.nodeValue;
    +    }
    +  } else {
    +    // Range endpoint is in an element node.
    +    var numChildren = endpointNode.childNodes.length;
    +    if (isBefore ? endpointOffset > 0 : endpointOffset < numChildren) {
    +      // There is at least one child following the endpoint.
    +      var followingChild =
    +          endpointNode.childNodes[isBefore ? endpointOffset - 1 :
    +                                             endpointOffset];
    +      if (goog.testing.editor.dom.isNonEmptyTextNode_(followingChild)) {
    +        // The following child has text so return that.
    +        return followingChild.nodeValue;
    +      } else {
    +        // The following child has no text so look for the following text node.
    +        followingTextNode = getFollowingTextNode(followingChild, opt_stopAt);
    +        return followingTextNode && followingTextNode.nodeValue;
    +      }
    +    } else {
    +      // There is no child following the endpoint, so search from the endpoint
    +      // node, but don't search its children because they are not following the
    +      // endpoint!
    +      followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt, true);
    +      return followingTextNode && followingTextNode.nodeValue;
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.html b/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.html
    new file mode 100644
    index 0000000000..0a13be3440
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html lang="en" dir="ltr">
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <!--
    +
    +   @author marcosalmeida@google.com (Marcos Almeida)
    +  -->
    +  <title>
    +   Closure Unit Tests - goog.testing.editor.dom
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.editor.domTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +  </div>
    + </body>
    +</html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.js b/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.js
    new file mode 100644
    index 0000000000..74e8b8b504
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/dom_test.js
    @@ -0,0 +1,290 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.editor.domTest');
    +goog.setTestOnly('goog.testing.editor.domTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.functions');
    +goog.require('goog.testing.editor.dom');
    +goog.require('goog.testing.jsunit');
    +
    +var root;
    +var parentNode, childNode1, childNode2, childNode3;
    +var first, middle, last;
    +
    +function setUpPage() {
    +  root = goog.dom.getElement('root');
    +}
    +
    +function tearDown() {
    +  root.innerHTML = '';
    +}
    +
    +function setUpNonEmptyTests() {
    +  childNode1 = goog.dom.createElement(goog.dom.TagName.DIV);
    +  childNode2 = goog.dom.createElement(goog.dom.TagName.DIV);
    +  childNode3 = goog.dom.createElement(goog.dom.TagName.DIV);
    +  parentNode = goog.dom.createDom(goog.dom.TagName.DIV,
    +      null,
    +      childNode1,
    +      childNode2,
    +      childNode3);
    +  goog.dom.appendChild(root, parentNode);
    +
    +  childNode1.appendChild(goog.dom.createTextNode('One'));
    +  childNode1.appendChild(goog.dom.createTextNode(''));
    +
    +  childNode2.appendChild(goog.dom.createElement(goog.dom.TagName.BR));
    +  childNode2.appendChild(goog.dom.createTextNode('TwoA'));
    +  childNode2.appendChild(goog.dom.createTextNode('TwoB'));
    +  childNode2.appendChild(goog.dom.createElement(goog.dom.TagName.BR));
    +
    +  childNode3.appendChild(goog.dom.createTextNode(''));
    +  childNode3.appendChild(goog.dom.createTextNode('Three'));
    +}
    +
    +function testGetNextNonEmptyTextNode() {
    +  setUpNonEmptyTests();
    +
    +  var nodeOne =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(parentNode);
    +  assertEquals('Should have found the next non-empty text node',
    +               'One',
    +               nodeOne.nodeValue);
    +  var nodeTwoA =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeOne);
    +  assertEquals('Should have found the next non-empty text node',
    +               'TwoA',
    +               nodeTwoA.nodeValue);
    +  var nodeTwoB =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeTwoA);
    +  assertEquals('Should have found the next non-empty text node',
    +               'TwoB',
    +               nodeTwoB.nodeValue);
    +  var nodeThree =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeTwoB);
    +  assertEquals('Should have found the next non-empty text node',
    +               'Three',
    +               nodeThree.nodeValue);
    +  var nodeNull =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeThree, parentNode);
    +  assertNull('Should not have found any non-empty text node', nodeNull);
    +
    +  var nodeStop =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeOne, childNode1);
    +  assertNull('Should have stopped before finding a node', nodeStop);
    +
    +  var nodeBeforeStop =
    +      goog.testing.editor.dom.getNextNonEmptyTextNode(nodeTwoA, childNode2);
    +  assertEquals('Should have found the next non-empty text node',
    +               'TwoB',
    +               nodeBeforeStop.nodeValue);
    +}
    +
    +function testGetPreviousNonEmptyTextNode() {
    +  setUpNonEmptyTests();
    +
    +  var nodeThree =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(parentNode);
    +  assertEquals('Should have found the previous non-empty text node',
    +               'Three',
    +               nodeThree.nodeValue);
    +  var nodeTwoB =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeThree);
    +  assertEquals('Should have found the previous non-empty text node',
    +               'TwoB',
    +               nodeTwoB.nodeValue);
    +  var nodeTwoA =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeTwoB);
    +  assertEquals('Should have found the previous non-empty text node',
    +               'TwoA',
    +               nodeTwoA.nodeValue);
    +  var nodeOne =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeTwoA);
    +  assertEquals('Should have found the previous non-empty text node',
    +               'One',
    +               nodeOne.nodeValue);
    +  var nodeNull =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeOne,
    +                                                          parentNode);
    +  assertNull('Should not have found any non-empty text node', nodeNull);
    +
    +  var nodeStop =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeThree,
    +                                                          childNode3);
    +  assertNull('Should have stopped before finding a node', nodeStop);
    +
    +  var nodeBeforeStop =
    +      goog.testing.editor.dom.getPreviousNonEmptyTextNode(nodeTwoB,
    +                                                          childNode2);
    +  assertEquals('Should have found the previous non-empty text node',
    +               'TwoA',
    +               nodeBeforeStop.nodeValue);
    +}
    +
    +
    +function setUpAssertRangeBetweenText() {
    +  // Create the following structure: <[01]><[]><[23]>
    +  // Where <> delimits spans, [] delimits text nodes, 01 and 23 are text.
    +  // We will test all 10 positions in between 0 and 2. All should pass.
    +  first = goog.dom.createDom(goog.dom.TagName.SPAN, null, '01');
    +  middle = goog.dom.createElement(goog.dom.TagName.SPAN);
    +  var emptyTextNode = goog.dom.createTextNode('');
    +  goog.dom.appendChild(middle, emptyTextNode);
    +  last = goog.dom.createDom(goog.dom.TagName.SPAN, null, '23');
    +  goog.dom.appendChild(root, first);
    +  goog.dom.appendChild(root, middle);
    +  goog.dom.appendChild(root, last);
    +}
    +
    +function createFakeRange(startNode, startOffset, opt_endNode, opt_endOffset) {
    +  opt_endNode = opt_endNode || startNode;
    +  opt_endOffset = opt_endOffset || startOffset;
    +  return {
    +    getStartNode: goog.functions.constant(startNode),
    +    getStartOffset: goog.functions.constant(startOffset),
    +    getEndNode: goog.functions.constant(opt_endNode),
    +    getEndOffset: goog.functions.constant(opt_endOffset)
    +  };
    +}
    +
    +function testAssertRangeBetweenText0() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('0', '1',
    +      createFakeRange(first.firstChild, 1));
    +}
    +
    +function testAssertRangeBetweenText1() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(first.firstChild, 2));
    +}
    +
    +function testAssertRangeBetweenText2() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(first, 1));
    +}
    +
    +function testAssertRangeBetweenText3() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(root, 1));
    +}
    +
    +function testAssertRangeBetweenText4() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(middle, 0));
    +}
    +
    +function testAssertRangeBetweenText5() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(middle.firstChild, 0));
    +}
    +
    +function testAssertRangeBetweenText6() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(middle, 1));
    +}
    +
    +function testAssertRangeBetweenText7() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(root, 2));
    +}
    +
    +function testAssertRangeBetweenText8() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(last, 0));
    +}
    +
    +function testAssertRangeBetweenText9() {
    +  setUpAssertRangeBetweenText();
    +  goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +      createFakeRange(last.firstChild, 0));
    +}
    +
    +
    +function testAssertRangeBetweenTextBefore() {
    +  setUpAssertRangeBetweenText();
    +  // Test that it works when the cursor is at the beginning of all text.
    +  goog.testing.editor.dom.assertRangeBetweenText('', '0',
    +      createFakeRange(first.firstChild, 0),
    +      root); // Restrict to root div so it won't find /n's and script.
    +}
    +
    +function testAssertRangeBetweenTextAfter() {
    +  setUpAssertRangeBetweenText();
    +  // Test that it works when the cursor is at the end of all text.
    +  goog.testing.editor.dom.assertRangeBetweenText('3', '',
    +      createFakeRange(last.firstChild, 2),
    +      root); // Restrict to root div so it won't find /n's and script.
    +}
    +
    +
    +function testAssertRangeBetweenTextFail1() {
    +  setUpAssertRangeBetweenText();
    +  var e = assertThrows('assertRangeBetweenText should have failed',
    +      function() {
    +        goog.testing.editor.dom.assertRangeBetweenText('1', '3',
    +            createFakeRange(first.firstChild, 2));
    +      });
    +  assertContains('Assert reason incorrect',
    +      'Expected <3> after range but found <23>', e.message);
    +}
    +
    +function testAssertRangeBetweenTextFail2() {
    +  setUpAssertRangeBetweenText();
    +  var e = assertThrows('assertRangeBetweenText should have failed',
    +      function() {
    +        goog.testing.editor.dom.assertRangeBetweenText('1', '2',
    +            createFakeRange(first.firstChild, 2, last.firstChild, 1));
    +      });
    +  assertContains('Assert reason incorrect',
    +      'Expected <2> after range but found <3>', e.message);
    +}
    +
    +function testAssertRangeBetweenTextBeforeFail() {
    +  setUpAssertRangeBetweenText();
    +  // Test that it gives the right message when the cursor is at the beginning
    +  // of all text but you're expecting something before it.
    +  var e = assertThrows('assertRangeBetweenText should have failed',
    +      function() {
    +        goog.testing.editor.dom.assertRangeBetweenText('-1', '0',
    +            createFakeRange(first.firstChild, 0),
    +            root); // Restrict to root div so it won't find /n's and script.
    +      });
    +  assertContains('Assert reason incorrect',
    +      'Expected <-1> before range but found nothing', e.message);
    +}
    +
    +function testAssertRangeBetweenTextAfterFail() {
    +  setUpAssertRangeBetweenText();
    +  // Test that it gives the right message when the cursor is at the end
    +  // of all text but you're expecting something after it.
    +  var e = assertThrows('assertRangeBetweenText should have failed',
    +      function() {
    +        goog.testing.editor.dom.assertRangeBetweenText('3', '4',
    +            createFakeRange(last.firstChild, 2),
    +            root); // Restrict to root div so it won't find /n's and script.
    +      });
    +  assertContains('Assert reason incorrect',
    +      'Expected <4> after range but found nothing', e.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/fieldmock.js b/mapbox-gl/closure-library/closure/goog/testing/editor/fieldmock.js
    new file mode 100644
    index 0000000000..3628c97bfd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/fieldmock.js
    @@ -0,0 +1,116 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock of goog.editor.field.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.testing.editor.FieldMock');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.Field');
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.mockmatchers');
    +
    +
    +
    +/**
    + * Mock of goog.editor.Field.
    + * @param {Window=} opt_window Window the field would edit.  Defaults to
    + *     {@code window}.
    + * @param {Window=} opt_appWindow "AppWindow" of the field, which can be
    + *     different from {@code opt_window} when mocking a field that uses an
    + *     iframe. Defaults to {@code opt_window}.
    + * @param {goog.dom.AbstractRange=} opt_range An object (mock or real) to be
    + *     returned by getRange(). If ommitted, a new goog.dom.Range is created
    + *     from the window every time getRange() is called.
    + * @constructor
    + * @extends {goog.testing.LooseMock}
    + * @suppress {missingProperties} Mocks do not fit in the type system well.
    + * @final
    + */
    +goog.testing.editor.FieldMock =
    +    function(opt_window, opt_appWindow, opt_range) {
    +  goog.testing.LooseMock.call(this, goog.editor.Field);
    +  opt_window = opt_window || window;
    +  opt_appWindow = opt_appWindow || opt_window;
    +
    +  this.getAppWindow();
    +  this.$anyTimes();
    +  this.$returns(opt_appWindow);
    +
    +  this.getRange();
    +  this.$anyTimes();
    +  this.$does(function() {
    +    return opt_range || goog.dom.Range.createFromWindow(opt_window);
    +  });
    +
    +  this.getEditableDomHelper();
    +  this.$anyTimes();
    +  this.$returns(goog.dom.getDomHelper(opt_window.document));
    +
    +  this.usesIframe();
    +  this.$anyTimes();
    +
    +  this.getBaseZindex();
    +  this.$anyTimes();
    +  this.$returns(0);
    +
    +  this.restoreSavedRange(goog.testing.mockmatchers.ignoreArgument);
    +  this.$anyTimes();
    +  this.$does(function(range) {
    +    if (range) {
    +      range.restore();
    +    }
    +    this.focus();
    +  });
    +
    +  // These methods cannot be set on the prototype, because the prototype
    +  // gets stepped on by the mock framework.
    +  var inModalMode = false;
    +
    +  /**
    +   * @return {boolean} Whether we're in modal interaction mode.
    +   */
    +  this.inModalMode = function() {
    +    return inModalMode;
    +  };
    +
    +  /**
    +   * @param {boolean} mode Sets whether we're in modal interaction mode.
    +   */
    +  this.setModalMode = function(mode) {
    +    inModalMode = mode;
    +  };
    +
    +  var uneditable = false;
    +
    +  /**
    +   * @return {boolean} Whether the field is uneditable.
    +   */
    +  this.isUneditable = function() {
    +    return uneditable;
    +  };
    +
    +  /**
    +   * @param {boolean} isUneditable Whether the field is uneditable.
    +   */
    +  this.setUneditable = function(isUneditable) {
    +    uneditable = isUneditable;
    +  };
    +};
    +goog.inherits(goog.testing.editor.FieldMock, goog.testing.LooseMock);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper.js b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper.js
    new file mode 100644
    index 0000000000..763e3da532
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper.js
    @@ -0,0 +1,180 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class that allows for simple text editing tests.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.testing.editor.TestHelper');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.dom');
    +goog.require('goog.dom.Range');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.node');
    +goog.require('goog.editor.plugins.AbstractBubblePlugin');
    +goog.require('goog.testing.dom');
    +
    +
    +
    +/**
    + * Create a new test controller.
    + * @param {Element} root The root editable element.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.testing.editor.TestHelper = function(root) {
    +  if (!root) {
    +    throw Error('Null root');
    +  }
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Convenience variable for root DOM element.
    +   * @type {!Element}
    +   * @private
    +   */
    +  this.root_ = root;
    +
    +  /**
    +   * The starting HTML of the editable element.
    +   * @type {string}
    +   * @private
    +   */
    +  this.savedHtml_ = '';
    +};
    +goog.inherits(goog.testing.editor.TestHelper, goog.Disposable);
    +
    +
    +/**
    + * Selects a new root element.
    + * @param {Element} root The root editable element.
    + */
    +goog.testing.editor.TestHelper.prototype.setRoot = function(root) {
    +  if (!root) {
    +    throw Error('Null root');
    +  }
    +  this.root_ = root;
    +};
    +
    +
    +/**
    + * Make the root element editable.  Alse saves its HTML to be restored
    + * in tearDown.
    + */
    +goog.testing.editor.TestHelper.prototype.setUpEditableElement = function() {
    +  this.savedHtml_ = this.root_.innerHTML;
    +  if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    this.root_.contentEditable = true;
    +  } else {
    +    this.root_.ownerDocument.designMode = 'on';
    +  }
    +  this.root_.setAttribute('g_editable', 'true');
    +};
    +
    +
    +/**
    + * Reset the element previously initialized, restoring its HTML and making it
    + * non editable.
    + * @suppress {accessControls} Private state of
    + *     {@link goog.editor.plugins.AbstractBubblePlugin} is accessed for test
    + *     purposes.
    + */
    +goog.testing.editor.TestHelper.prototype.tearDownEditableElement = function() {
    +  if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    +    this.root_.contentEditable = false;
    +  } else {
    +    this.root_.ownerDocument.designMode = 'off';
    +  }
    +  goog.dom.removeChildren(this.root_);
    +  this.root_.innerHTML = this.savedHtml_;
    +  this.root_.removeAttribute('g_editable');
    +
    +  if (goog.editor.plugins && goog.editor.plugins.AbstractBubblePlugin) {
    +    // Remove old bubbles.
    +    for (var key in goog.editor.plugins.AbstractBubblePlugin.bubbleMap_) {
    +      goog.editor.plugins.AbstractBubblePlugin.bubbleMap_[key].dispose();
    +    }
    +    // Ensure we get a new bubble for each test.
    +    goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {};
    +  }
    +};
    +
    +
    +/**
    + * Assert that the html in 'root' is substantially similar to htmlPattern.
    + * This method tests for the same set of styles, and for the same order of
    + * nodes.  Breaking whitespace nodes are ignored.  Elements can be annotated
    + * with classnames corresponding to keys in goog.userAgent and will be
    + * expected to show up in that user agent and expected not to show up in
    + * others.
    + * @param {string} htmlPattern The pattern to match.
    + */
    +goog.testing.editor.TestHelper.prototype.assertHtmlMatches = function(
    +    htmlPattern) {
    +  goog.testing.dom.assertHtmlContentsMatch(htmlPattern, this.root_);
    +};
    +
    +
    +/**
    + * Finds the first text node descendant of root with the given content.
    + * @param {string|RegExp} textOrRegexp The text to find, or a regular
    + *     expression to find a match of.
    + * @return {Node} The first text node that matches, or null if none is found.
    + */
    +goog.testing.editor.TestHelper.prototype.findTextNode = function(textOrRegexp) {
    +  return goog.testing.dom.findTextNode(textOrRegexp, this.root_);
    +};
    +
    +
    +/**
    + * Select from the given from offset in the given from node to the given
    + * to offset in the optionally given to node. If nodes are passed in, uses them,
    + * otherwise uses findTextNode to find the nodes to select. Selects a caret
    + * if opt_to and opt_toOffset are not given.
    + * @param {Node|string} from Node or text of the node to start the selection at.
    + * @param {number} fromOffset Offset within the above node to start the
    + *     selection at.
    + * @param {Node|string=} opt_to Node or text of the node to end the selection
    + *     at.
    + * @param {number=} opt_toOffset Offset within the above node to end the
    + *     selection at.
    + */
    +goog.testing.editor.TestHelper.prototype.select = function(from, fromOffset,
    +    opt_to, opt_toOffset) {
    +  var end;
    +  var start = end = goog.isString(from) ? this.findTextNode(from) : from;
    +  var endOffset;
    +  var startOffset = endOffset = fromOffset;
    +
    +  if (opt_to && goog.isNumber(opt_toOffset)) {
    +    end = goog.isString(opt_to) ? this.findTextNode(opt_to) : opt_to;
    +    endOffset = opt_toOffset;
    +  }
    +
    +  goog.dom.Range.createFromNodes(start, startOffset, end, endOffset).select();
    +};
    +
    +
    +/** @override */
    +goog.testing.editor.TestHelper.prototype.disposeInternal = function() {
    +  if (goog.editor.node.isEditableContainer(this.root_)) {
    +    this.tearDownEditableElement();
    +  }
    +  delete this.root_;
    +  goog.testing.editor.TestHelper.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.html b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.html
    new file mode 100644
    index 0000000000..8f540e2f04
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html lang="en" dir="ltr">
    + <head>
    +  <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.editor.TestHelper
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.editor.TestHelperTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +  </div>
    +  <div id="root2">Root 2</div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.js b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.js
    new file mode 100644
    index 0000000000..195645979f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/editor/testhelper_test.js
    @@ -0,0 +1,175 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.editor.TestHelperTest');
    +goog.setTestOnly('goog.testing.editor.TestHelperTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.node');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +var root;
    +var helper;
    +
    +function setUp() {
    +  root = goog.dom.getElement('root');
    +  goog.dom.removeChildren(root);
    +  helper = new goog.testing.editor.TestHelper(root);
    +}
    +
    +function tearDown() {
    +  helper.dispose();
    +}
    +
    +function testSetRoot() {
    +  helper.setRoot(goog.dom.getElement('root2'));
    +  helper.assertHtmlMatches('Root 2');
    +}
    +
    +function testSetupEditableElement() {
    +  helper.setUpEditableElement();
    +  assertTrue(goog.editor.node.isEditableContainer(root));
    +}
    +
    +function testTearDownEditableElement() {
    +  helper.setUpEditableElement();
    +  assertTrue(goog.editor.node.isEditableContainer(root));
    +
    +  helper.tearDownEditableElement();
    +  assertFalse(goog.editor.node.isEditableContainer(root));
    +}
    +
    +function testFindNode() {
    +  // Test the easiest case.
    +  root.innerHTML = 'a<br>b';
    +  assertEquals(helper.findTextNode('a'), root.firstChild);
    +  assertEquals(helper.findTextNode('b'), root.lastChild);
    +  assertNull(helper.findTextNode('c'));
    +}
    +
    +function testFindNodeDuplicate() {
    +  // Test duplicate.
    +  root.innerHTML = 'c<br>c';
    +  assertEquals('Should return first duplicate', helper.findTextNode('c'),
    +      root.firstChild);
    +}
    +
    +function findNodeWithHierarchy() {
    +  // Test a more complicated hierarchy.
    +  root.innerHTML = '<div>a<p>b<span>c</span>d</p>e</div>';
    +  assertEquals(goog.dom.TagName.DIV,
    +      helper.findTextNode('a').parentNode.tagName);
    +  assertEquals(goog.dom.TagName.P,
    +      helper.findTextNode('b').parentNode.tagName);
    +  assertEquals(goog.dom.TagName.SPAN,
    +      helper.findTextNode('c').parentNode.tagName);
    +  assertEquals(goog.dom.TagName.P,
    +      helper.findTextNode('d').parentNode.tagName);
    +  assertEquals(goog.dom.TagName.DIV,
    +      helper.findTextNode('e').parentNode.tagName);
    +}
    +
    +function setUpAssertHtmlMatches() {
    +  var tag1, tag2;
    +  if (goog.userAgent.IE) {
    +    tag1 = goog.dom.TagName.DIV;
    +  } else if (goog.userAgent.WEBKIT) {
    +    tag1 = goog.dom.TagName.P;
    +    tag2 = goog.dom.TagName.BR;
    +  } else if (goog.userAgent.GECKO) {
    +    tag1 = goog.dom.TagName.SPAN;
    +    tag2 = goog.dom.TagName.BR;
    +  }
    +
    +  var parent = goog.dom.createDom(goog.dom.TagName.DIV);
    +  root.appendChild(parent);
    +  parent.style.fontSize = '2em';
    +  parent.style.display = 'none';
    +  if (goog.userAgent.IE || goog.userAgent.GECKO) {
    +    parent.appendChild(goog.dom.createTextNode('NonWebKitText'));
    +  }
    +
    +  if (tag1) {
    +    var e1 = goog.dom.createDom(tag1);
    +    parent.appendChild(e1);
    +    parent = e1;
    +  }
    +  if (tag2) {
    +    parent.appendChild(goog.dom.createDom(tag2));
    +  }
    +  parent.appendChild(goog.dom.createTextNode('Text'));
    +  if (goog.userAgent.WEBKIT) {
    +    root.firstChild.appendChild(goog.dom.createTextNode('WebKitText'));
    +  }
    +}
    +
    +function testAssertHtmlMatches() {
    +  setUpAssertHtmlMatches();
    +
    +  helper.assertHtmlMatches('<div style="display: none; font-size: 2em">' +
    +      '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +      '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +      '</div>[[WEBKIT]]WebKitText');
    +}
    +
    +function testAssertHtmlMismatchText() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched text', function() {
    +    helper.assertHtmlMatches('<div style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Bad</span></p></div>' +
    +        '</div>[[WEBKIT]]Extra');
    +  });
    +  assertContains('Text should match', e.message);
    +}
    +
    +function testAssertHtmlMismatchTag() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched tag', function() {
    +    helper.assertHtmlMatches('<span style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</span>[[WEBKIT]]Extra');
    +  });
    +  assertContains('Tag names should match', e.message);
    +}
    +
    +function testAssertHtmlMismatchStyle() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched style', function() {
    +    helper.assertHtmlMatches('<div style="display: none; font-size: 3em">' +
    +        '[[IE GECKO]]NonWebKitText<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</div>[[WEBKIT]]Extra');
    +  });
    +  assertContains('Should have same styles', e.message);
    +}
    +
    +function testAssertHtmlMismatchOptionalText() {
    +  setUpAssertHtmlMatches();
    +
    +  var e = assertThrows('Should fail due to mismatched style', function() {
    +    helper.assertHtmlMatches('<div style="display: none; font-size: 2em">' +
    +        '[[IE GECKO]]Bad<div class="IE"><p class="WEBKIT">' +
    +        '<span class="GECKO"><br class="GECKO WEBKIT">Text</span></p></div>' +
    +        '</div>[[WEBKIT]]Bad');
    +  });
    +  assertContains('Text should match', e.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver.js b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver.js
    new file mode 100644
    index 0000000000..77ae69d3b2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver.js
    @@ -0,0 +1,87 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Event observer.
    + *
    + * Provides an event observer that holds onto events that it handles.  This
    + * can be used in unit testing to verify an event target's events --
    + * that the order count, types, etc. are correct.
    + *
    + * Example usage:
    + * <pre>
    + * var observer = new goog.testing.events.EventObserver();
    + * var widget = new foo.Widget();
    + * goog.events.listen(widget, ['select', 'submit'], observer);
    + * // Simulate user action of 3 select events and 2 submit events.
    + * assertEquals(3, observer.getEvents('select').length);
    + * assertEquals(2, observer.getEvents('submit').length);
    + * </pre>
    + *
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +goog.provide('goog.testing.events.EventObserver');
    +
    +goog.require('goog.array');
    +
    +
    +
    +/**
    + * Event observer.  Implements a handleEvent interface so it may be used as
    + * a listener in listening functions and methods.
    + * @see goog.events.listen
    + * @see goog.events.EventHandler
    + * @constructor
    + * @final
    + */
    +goog.testing.events.EventObserver = function() {
    +
    +  /**
    +   * A list of events handled by the observer in order of handling, oldest to
    +   * newest.
    +   * @type {!Array<!goog.events.Event>}
    +   * @private
    +   */
    +  this.events_ = [];
    +};
    +
    +
    +/**
    + * Handles an event and remembers it.  Event listening functions and methods
    + * will call this method when this observer is used as a listener.
    + * @see goog.events.listen
    + * @see goog.events.EventHandler
    + * @param {!goog.events.Event} e Event to handle.
    + */
    +goog.testing.events.EventObserver.prototype.handleEvent = function(e) {
    +  this.events_.push(e);
    +};
    +
    +
    +/**
    + * @param {string=} opt_type If given, only return events of this type.
    + * @return {!Array<!goog.events.Event>} The events handled, oldest to newest.
    + */
    +goog.testing.events.EventObserver.prototype.getEvents = function(opt_type) {
    +  var events = goog.array.clone(this.events_);
    +
    +  if (opt_type) {
    +    events = goog.array.filter(events, function(event) {
    +      return event.type == opt_type;
    +    });
    +  }
    +
    +  return events;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.html b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.html
    new file mode 100644
    index 0000000000..772ed8061b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.events.EventObserver
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.events.EventObserverTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.js b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.js
    new file mode 100644
    index 0000000000..efe9702dec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/eventobserver_test.js
    @@ -0,0 +1,67 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.events.EventObserverTest');
    +goog.setTestOnly('goog.testing.events.EventObserverTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.events.EventObserver');
    +goog.require('goog.testing.jsunit');
    +
    +// Return an event's type
    +function getEventType(e) {
    +  return e.type;
    +}
    +
    +function testGetEvents() {
    +  var observer = new goog.testing.events.EventObserver();
    +  var target = new goog.events.EventTarget();
    +  goog.events.listen(target, ['foo', 'bar', 'baz'], observer);
    +
    +  var eventTypes = [
    +    'bar', 'baz', 'foo', 'qux', 'quux', 'corge', 'foo', 'baz'];
    +  goog.array.forEach(eventTypes, goog.bind(target.dispatchEvent, target));
    +
    +  var replayEvents = observer.getEvents();
    +
    +  assertArrayEquals('Only the listened-for event types should be remembered',
    +      ['bar', 'baz', 'foo', 'foo', 'baz'],
    +      goog.array.map(observer.getEvents(), getEventType));
    +
    +  assertArrayEquals(['bar'],
    +      goog.array.map(observer.getEvents('bar'), getEventType));
    +  assertArrayEquals(['baz', 'baz'],
    +      goog.array.map(observer.getEvents('baz'), getEventType));
    +  assertArrayEquals(['foo', 'foo'],
    +      goog.array.map(observer.getEvents('foo'), getEventType));
    +}
    +
    +function testHandleEvent() {
    +  var events = [
    +    new goog.events.Event('foo'),
    +    new goog.events.Event('bar'),
    +    new goog.events.Event('baz')
    +  ];
    +
    +  var observer = new goog.testing.events.EventObserver();
    +  goog.array.forEach(events, goog.bind(observer.handleEvent, observer));
    +
    +  assertArrayEquals(events, observer.getEvents());
    +  assertArrayEquals([events[0]], observer.getEvents('foo'));
    +  assertArrayEquals([events[1]], observer.getEvents('bar'));
    +  assertArrayEquals([events[2]], observer.getEvents('baz'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/events.js b/mapbox-gl/closure-library/closure/goog/testing/events/events.js
    new file mode 100644
    index 0000000000..e2070ab6d0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/events.js
    @@ -0,0 +1,727 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Event Simulation.
    + *
    + * Utility functions for simulating events at the Closure level. All functions
    + * in this package generate events by calling goog.events.fireListeners,
    + * rather than interfacing with the browser directly. This is intended for
    + * testing purposes, and should not be used in production code.
    + *
    + * The decision to use Closure events and dispatchers instead of the browser's
    + * native events and dispatchers was conscious and deliberate. Native event
    + * dispatchers have their own set of quirks and edge cases. Pure JS dispatchers
    + * are more robust and transparent.
    + *
    + * If you think you need a testing mechanism that uses native Event objects,
    + * please, please email closure-tech first to explain your use case before you
    + * sink time into this.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.testing.events');
    +goog.provide('goog.testing.events.Event');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.BrowserFeature');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
    + *
    + * This clones a lot of the functionality of goog.events.Event. This used to
    + * use a mixin, but the mixin results in confusing the two types when compiled.
    + *
    + * @param {string} type Event Type.
    + * @param {Object=} opt_target Reference to the object that is the target of
    + *     this event.
    + * @constructor
    + * @extends {Event}
    + */
    +goog.testing.events.Event = function(type, opt_target) {
    +  this.type = type;
    +
    +  this.target = /** @type {EventTarget} */ (opt_target || null);
    +
    +  this.currentTarget = this.target;
    +};
    +
    +
    +/**
    + * Whether to cancel the event in internal capture/bubble processing for IE.
    + * @type {boolean}
    + * @public
    + * @suppress {underscore|visibility} Technically public, but referencing this
    + *     outside this package is strongly discouraged.
    + */
    +goog.testing.events.Event.prototype.propagationStopped_ = false;
    +
    +
    +/** @override */
    +goog.testing.events.Event.prototype.defaultPrevented = false;
    +
    +
    +/**
    + * Return value for in internal capture/bubble processing for IE.
    + * @type {boolean}
    + * @public
    + * @suppress {underscore|visibility} Technically public, but referencing this
    + *     outside this package is strongly discouraged.
    + */
    +goog.testing.events.Event.prototype.returnValue_ = true;
    +
    +
    +/** @override */
    +goog.testing.events.Event.prototype.stopPropagation = function() {
    +  this.propagationStopped_ = true;
    +};
    +
    +
    +/** @override */
    +goog.testing.events.Event.prototype.preventDefault = function() {
    +  this.defaultPrevented = true;
    +  this.returnValue_ = false;
    +};
    +
    +
    +/**
    + * Asserts an event target exists.  This will fail if target is not defined.
    + *
    + * TODO(nnaze): Gradually add this to the methods in this file, and eventually
    + *     update the method signatures to not take nullables.  See http://b/8961907
    + *
    + * @param {EventTarget} target A target to assert.
    + * @return {!EventTarget} The target, guaranteed to exist.
    + * @private
    + */
    +goog.testing.events.assertEventTarget_ = function(target) {
    +  return goog.asserts.assert(target, 'EventTarget should be defined.');
    +};
    +
    +
    +/**
    + * A static helper function that sets the mouse position to the event.
    + * @param {Event} event A simulated native event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @private
    + */
    +goog.testing.events.setEventClientXY_ = function(event, opt_coords) {
    +  if (!opt_coords && event.target &&
    +      event.target.nodeType == goog.dom.NodeType.ELEMENT) {
    +    try {
    +      opt_coords =
    +          goog.style.getClientPosition(/** @type {!Element} **/ (event.target));
    +    } catch (ex) {
    +      // IE sometimes throws if it can't get the position.
    +    }
    +  }
    +  event.clientX = opt_coords ? opt_coords.x : 0;
    +  event.clientY = opt_coords ? opt_coords.y : 0;
    +
    +  // Pretend the browser window is at (0, 0).
    +  event.screenX = event.clientX;
    +  event.screenY = event.clientY;
    +};
    +
    +
    +/**
    + * Simulates a mousedown, mouseup, and then click on the given event target,
    + * with the left mouse button.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    + *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireClickSequence =
    +    function(target, opt_button, opt_coords, opt_eventProperties) {
    +  // Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3.
    +  return !!(goog.testing.events.fireMouseDownEvent(
    +      target, opt_button, opt_coords, opt_eventProperties) &
    +            goog.testing.events.fireMouseUpEvent(
    +                target, opt_button, opt_coords, opt_eventProperties) &
    +            goog.testing.events.fireClickEvent(
    +                target, opt_button, opt_coords, opt_eventProperties));
    +};
    +
    +
    +/**
    + * Simulates the sequence of events fired by the browser when the user double-
    + * clicks the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireDoubleClickSequence = function(
    +    target, opt_coords, opt_eventProperties) {
    +  // Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick.
    +  // Then return the bitwise AND of the 7.
    +  var btn = goog.events.BrowserEvent.MouseButton.LEFT;
    +  return !!(goog.testing.events.fireMouseDownEvent(
    +      target, btn, opt_coords, opt_eventProperties) &
    +            goog.testing.events.fireMouseUpEvent(
    +                target, btn, opt_coords, opt_eventProperties) &
    +            goog.testing.events.fireClickEvent(
    +                target, btn, opt_coords, opt_eventProperties) &
    +            // IE fires a selectstart instead of the second mousedown in a
    +            // dblclick, but we don't care about selectstart.
    +            (goog.userAgent.IE ||
    +            goog.testing.events.fireMouseDownEvent(
    +                target, btn, opt_coords, opt_eventProperties)) &
    +            goog.testing.events.fireMouseUpEvent(
    +                target, btn, opt_coords, opt_eventProperties) &
    +            // IE doesn't fire the second click in a dblclick.
    +            (goog.userAgent.IE ||
    +            goog.testing.events.fireClickEvent(
    +                target, btn, opt_coords, opt_eventProperties)) &
    +            goog.testing.events.fireDoubleClickEvent(
    +                target, opt_coords, opt_eventProperties));
    +};
    +
    +
    +/**
    + * Simulates a complete keystroke (keydown, keypress, and keyup). Note that
    + * if preventDefault is called on the keydown, the keypress will not fire.
    + *
    + * @param {EventTarget} target The target for the event.
    + * @param {number} keyCode The keycode of the key pressed.
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireKeySequence = function(
    +    target, keyCode, opt_eventProperties) {
    +  return goog.testing.events.fireNonAsciiKeySequence(target, keyCode, keyCode,
    +                                                     opt_eventProperties);
    +};
    +
    +
    +/**
    + * Simulates a complete keystroke (keydown, keypress, and keyup) when typing
    + * a non-ASCII character. Same as fireKeySequence, the keypress will not fire
    + * if preventDefault is called on the keydown.
    + *
    + * @param {EventTarget} target The target for the event.
    + * @param {number} keyCode The keycode of the keydown and keyup events.
    + * @param {number} keyPressKeyCode The keycode of the keypress event.
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireNonAsciiKeySequence = function(
    +    target, keyCode, keyPressKeyCode, opt_eventProperties) {
    +  var keydown =
    +      new goog.testing.events.Event(goog.events.EventType.KEYDOWN, target);
    +  var keyup =
    +      new goog.testing.events.Event(goog.events.EventType.KEYUP, target);
    +  var keypress =
    +      new goog.testing.events.Event(goog.events.EventType.KEYPRESS, target);
    +  keydown.keyCode = keyup.keyCode = keyCode;
    +  keypress.keyCode = keyPressKeyCode;
    +
    +  if (opt_eventProperties) {
    +    goog.object.extend(keydown, opt_eventProperties);
    +    goog.object.extend(keyup, opt_eventProperties);
    +    goog.object.extend(keypress, opt_eventProperties);
    +  }
    +
    +  // Fire keydown, keypress, and keyup. Note that if the keydown is
    +  // prevent-defaulted, then the keypress will not fire.
    +  var result = true;
    +  if (!goog.testing.events.isBrokenGeckoMacActionKey_(keydown)) {
    +    result = goog.testing.events.fireBrowserEvent(keydown);
    +  }
    +  if (goog.events.KeyCodes.firesKeyPressEvent(
    +      keyCode, undefined, keydown.shiftKey, keydown.ctrlKey,
    +      keydown.altKey) && result) {
    +    result &= goog.testing.events.fireBrowserEvent(keypress);
    +  }
    +  return !!(result & goog.testing.events.fireBrowserEvent(keyup));
    +};
    +
    +
    +/**
    + * @param {goog.testing.events.Event} e The event.
    + * @return {boolean} Whether this is the Gecko/Mac's Meta-C/V/X, which
    + *     is broken and requires special handling.
    + * @private
    + */
    +goog.testing.events.isBrokenGeckoMacActionKey_ = function(e) {
    +  return goog.userAgent.MAC && goog.userAgent.GECKO &&
    +      (e.keyCode == goog.events.KeyCodes.C ||
    +       e.keyCode == goog.events.KeyCodes.X ||
    +       e.keyCode == goog.events.KeyCodes.V) && e.metaKey;
    +};
    +
    +
    +/**
    + * Simulates a mouseover event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {EventTarget} relatedTarget The related target for the event (e.g.,
    + *     the node that the mouse is being moved out of).
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireMouseOverEvent = function(target, relatedTarget,
    +    opt_coords) {
    +  var mouseover =
    +      new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
    +  mouseover.relatedTarget = relatedTarget;
    +  goog.testing.events.setEventClientXY_(mouseover, opt_coords);
    +  return goog.testing.events.fireBrowserEvent(mouseover);
    +};
    +
    +
    +/**
    + * Simulates a mousemove event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
    +  var mousemove =
    +      new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
    +
    +  goog.testing.events.setEventClientXY_(mousemove, opt_coords);
    +  return goog.testing.events.fireBrowserEvent(mousemove);
    +};
    +
    +
    +/**
    + * Simulates a mouseout event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {EventTarget} relatedTarget The related target for the event (e.g.,
    + *     the node that the mouse is being moved into).
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireMouseOutEvent = function(target, relatedTarget,
    +    opt_coords) {
    +  var mouseout =
    +      new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
    +  mouseout.relatedTarget = relatedTarget;
    +  goog.testing.events.setEventClientXY_(mouseout, opt_coords);
    +  return goog.testing.events.fireBrowserEvent(mouseout);
    +};
    +
    +
    +/**
    + * Simulates a mousedown event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    + *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireMouseDownEvent =
    +    function(target, opt_button, opt_coords, opt_eventProperties) {
    +
    +  var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    +  button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    +      goog.events.BrowserEvent.IEButtonMap[button] : button;
    +  return goog.testing.events.fireMouseButtonEvent_(
    +      goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
    +      opt_eventProperties);
    +};
    +
    +
    +/**
    + * Simulates a mouseup event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    + *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireMouseUpEvent =
    +    function(target, opt_button, opt_coords, opt_eventProperties) {
    +  var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    +  button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    +      goog.events.BrowserEvent.IEButtonMap[button] : button;
    +  return goog.testing.events.fireMouseButtonEvent_(
    +      goog.events.EventType.MOUSEUP, target, button, opt_coords,
    +      opt_eventProperties);
    +};
    +
    +
    +/**
    + * Simulates a click event on the given target. IE only supports click with
    + * the left mouse button.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    + *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireClickEvent =
    +    function(target, opt_button, opt_coords, opt_eventProperties) {
    +  return goog.testing.events.fireMouseButtonEvent_(goog.events.EventType.CLICK,
    +      target, opt_button, opt_coords, opt_eventProperties);
    +};
    +
    +
    +/**
    + * Simulates a double-click event on the given target. Always double-clicks
    + * with the left mouse button since no browser supports double-clicking with
    + * any other buttons.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireDoubleClickEvent =
    +    function(target, opt_coords, opt_eventProperties) {
    +  return goog.testing.events.fireMouseButtonEvent_(
    +      goog.events.EventType.DBLCLICK, target,
    +      goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
    +      opt_eventProperties);
    +};
    +
    +
    +/**
    + * Helper function to fire a mouse event.
    + * with the left mouse button since no browser supports double-clicking with
    + * any other buttons.
    + * @param {string} type The event type.
    + * @param {EventTarget} target The target for the event.
    + * @param {number=} opt_button Mouse button; defaults to
    + *     {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + * @private
    + */
    +goog.testing.events.fireMouseButtonEvent_ =
    +    function(type, target, opt_button, opt_coords, opt_eventProperties) {
    +  var e =
    +      new goog.testing.events.Event(type, target);
    +  e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
    +  goog.testing.events.setEventClientXY_(e, opt_coords);
    +  if (opt_eventProperties) {
    +    goog.object.extend(e, opt_eventProperties);
    +  }
    +  return goog.testing.events.fireBrowserEvent(e);
    +};
    +
    +
    +/**
    + * Simulates a contextmenu event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
    +  var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    +      goog.events.BrowserEvent.MouseButton.LEFT :
    +      goog.events.BrowserEvent.MouseButton.RIGHT;
    +  var contextmenu =
    +      new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
    +  contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
    +      goog.events.BrowserEvent.IEButtonMap[button] : button;
    +  contextmenu.ctrlKey = goog.userAgent.MAC;
    +  goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
    +  return goog.testing.events.fireBrowserEvent(contextmenu);
    +};
    +
    +
    +/**
    + * Simulates a mousedown, contextmenu, and the mouseup on the given event
    + * target, with the right mouse button.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
    + * target's position (if available), otherwise (0, 0).
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
    +  var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
    +  var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
    +      goog.events.BrowserEvent.MouseButton.LEFT :
    +      goog.events.BrowserEvent.MouseButton.RIGHT;
    +
    +  var result = goog.testing.events.fireMouseDownEvent(target,
    +      button, opt_coords, props);
    +  if (goog.userAgent.WINDOWS) {
    +    // All browsers are consistent on Windows.
    +    result &= goog.testing.events.fireMouseUpEvent(target,
    +        button, opt_coords) &
    +              goog.testing.events.fireContextMenuEvent(target, opt_coords);
    +  } else {
    +    result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
    +
    +    // GECKO on Mac and Linux always fires the mouseup after the contextmenu.
    +
    +    // WEBKIT is really weird.
    +    //
    +    // On Linux, it sometimes fires mouseup, but most of the time doesn't.
    +    // It's really hard to reproduce consistently. I think there's some
    +    // internal race condition. If contextmenu is preventDefaulted, then
    +    // mouseup always fires.
    +    //
    +    // On Mac, it always fires mouseup and then fires a click.
    +    result &= goog.testing.events.fireMouseUpEvent(target,
    +        button, opt_coords, props);
    +
    +    if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
    +      result &= goog.testing.events.fireClickEvent(
    +          target, button, opt_coords, props);
    +    }
    +  }
    +  return !!result;
    +};
    +
    +
    +/**
    + * Simulates a popstate event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {Object} state History state object.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.firePopStateEvent = function(target, state) {
    +  var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
    +  e.state = state;
    +  return goog.testing.events.fireBrowserEvent(e);
    +};
    +
    +
    +/**
    + * Simulate a blur event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @return {boolean} The value returned by firing the blur browser event,
    + *      which returns false iff 'preventDefault' was invoked.
    + */
    +goog.testing.events.fireBlurEvent = function(target) {
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, target);
    +  return goog.testing.events.fireBrowserEvent(e);
    +};
    +
    +
    +/**
    + * Simulate a focus event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @return {boolean} The value returned by firing the focus browser event,
    + *     which returns false iff 'preventDefault' was invoked.
    + */
    +goog.testing.events.fireFocusEvent = function(target) {
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.FOCUS, target);
    +  return goog.testing.events.fireBrowserEvent(e);
    +};
    +
    +
    +/**
    + * Simulates an event's capturing and bubbling phases.
    + * @param {Event} event A simulated native event. It will be wrapped in a
    + *     normalized BrowserEvent and dispatched to Closure listeners on all
    + *     ancestors of its target (inclusive).
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireBrowserEvent = function(event) {
    +  event.returnValue_ = true;
    +
    +  // generate a list of ancestors
    +  var ancestors = [];
    +  for (var current = event.target; current; current = current.parentNode) {
    +    ancestors.push(current);
    +  }
    +
    +  // dispatch capturing listeners
    +  for (var j = ancestors.length - 1;
    +       j >= 0 && !event.propagationStopped_;
    +       j--) {
    +    goog.events.fireListeners(ancestors[j], event.type, true,
    +        new goog.events.BrowserEvent(event, ancestors[j]));
    +  }
    +
    +  // dispatch bubbling listeners
    +  for (var j = 0;
    +       j < ancestors.length && !event.propagationStopped_;
    +       j++) {
    +    goog.events.fireListeners(ancestors[j], event.type, false,
    +        new goog.events.BrowserEvent(event, ancestors[j]));
    +  }
    +
    +  return event.returnValue_;
    +};
    +
    +
    +/**
    + * Simulates a touchstart event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    + *     target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireTouchStartEvent = function(
    +    target, opt_coords, opt_eventProperties) {
    +  // TODO: Support multi-touch events with array of coordinates.
    +  var touchstart =
    +      new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
    +  goog.testing.events.setEventClientXY_(touchstart, opt_coords);
    +  if (opt_eventProperties) {
    +    goog.object.extend(touchstart, opt_eventProperties);
    +  }
    +  return goog.testing.events.fireBrowserEvent(touchstart);
    +};
    +
    +
    +/**
    + * Simulates a touchmove event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    + *     target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireTouchMoveEvent = function(
    +    target, opt_coords, opt_eventProperties) {
    +  // TODO: Support multi-touch events with array of coordinates.
    +  var touchmove =
    +      new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
    +  goog.testing.events.setEventClientXY_(touchmove, opt_coords);
    +  if (opt_eventProperties) {
    +    goog.object.extend(touchmove, opt_eventProperties);
    +  }
    +  return goog.testing.events.fireBrowserEvent(touchmove);
    +};
    +
    +
    +/**
    + * Simulates a touchend event on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
    + *     target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the event: false if preventDefault() was
    + *     called on it, true otherwise.
    + */
    +goog.testing.events.fireTouchEndEvent = function(
    +    target, opt_coords, opt_eventProperties) {
    +  // TODO: Support multi-touch events with array of coordinates.
    +  var touchend =
    +      new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
    +  goog.testing.events.setEventClientXY_(touchend, opt_coords);
    +  if (opt_eventProperties) {
    +    goog.object.extend(touchend, opt_eventProperties);
    +  }
    +  return goog.testing.events.fireBrowserEvent(touchend);
    +};
    +
    +
    +/**
    + * Simulates a simple touch sequence on the given target.
    + * @param {EventTarget} target The target for the event.
    + * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
    + *     target's position (if available), otherwise (0, 0).
    + * @param {Object=} opt_eventProperties Event properties to be mixed into the
    + *     BrowserEvent.
    + * @return {boolean} The returnValue of the sequence: false if preventDefault()
    + *     was called on any of the events, true otherwise.
    + */
    +goog.testing.events.fireTouchSequence = function(
    +    target, opt_coords, opt_eventProperties) {
    +  // TODO: Support multi-touch events with array of coordinates.
    +  // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
    +  return !!(goog.testing.events.fireTouchStartEvent(
    +      target, opt_coords, opt_eventProperties) &
    +            goog.testing.events.fireTouchEndEvent(
    +                target, opt_coords, opt_eventProperties));
    +};
    +
    +
    +/**
    + * Mixins a listenable into the given object. This turns the object
    + * into a goog.events.Listenable. This is useful, for example, when
    + * you need to mock a implementation of listenable and still want it
    + * to work with goog.events.
    + * @param {!Object} obj The object to mixin into.
    + */
    +goog.testing.events.mixinListenable = function(obj) {
    +  var listenable = new goog.events.EventTarget();
    +
    +  listenable.setTargetForTesting(obj);
    +
    +  var listenablePrototype = goog.events.EventTarget.prototype;
    +  var disposablePrototype = goog.Disposable.prototype;
    +  for (var key in listenablePrototype) {
    +    if (listenablePrototype.hasOwnProperty(key) ||
    +        disposablePrototype.hasOwnProperty(key)) {
    +      var member = listenablePrototype[key];
    +      if (goog.isFunction(member)) {
    +        obj[key] = goog.bind(member, listenable);
    +      } else {
    +        obj[key] = member;
    +      }
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/events_test.html b/mapbox-gl/closure-library/closure/goog/testing/events/events_test.html
    new file mode 100644
    index 0000000000..910b0f2a01
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/events_test.html
    @@ -0,0 +1,41 @@
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!DOCTYPE html>
    +<html lang="en" dir="ltr">
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <!--
    +
    +  Author: nicksantos@google.com (Nick Santos)
    +  -->
    +  <title>
    +   Closure Unit Tests - goog.testing.events
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.eventsTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +  </div>
    +  <input id="testButton" type="input" value="Click Me" />
    +  <div id="input">
    +   Prevent Default these events:
    +   <br />
    +  </div>
    +  <div id="log" style="position:absolute;right:0;top:0">
    +   Logged events:
    +  </div>
    +  <div id="parentEl">
    +   <div id="childEl">
    +    hello!
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/events_test.js b/mapbox-gl/closure-library/closure/goog/testing/events/events_test.js
    new file mode 100644
    index 0000000000..8c642ec517
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/events_test.js
    @@ -0,0 +1,624 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.eventsTest');
    +goog.setTestOnly('goog.testing.eventsTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.userAgent');
    +
    +var firedEventTypes;
    +var firedEventCoordinates;
    +var firedScreenCoordinates;
    +var firedShiftKeys;
    +var firedKeyCodes;
    +var root;
    +var log;
    +var input;
    +var testButton;
    +var parentEl;
    +var childEl;
    +var coordinate = new goog.math.Coordinate(123, 456);
    +var stubs = new goog.testing.PropertyReplacer();
    +var eventCount;
    +
    +function setUpPage() {
    +  root = goog.dom.getElement('root');
    +  log = goog.dom.getElement('log');
    +  input = goog.dom.getElement('input');
    +  testButton = goog.dom.getElement('testButton');
    +  parentEl = goog.dom.getElement('parentEl');
    +  childEl = goog.dom.getElement('childEl');
    +}
    +
    +function setUp() {
    +  stubs.reset();
    +  goog.events.removeAll(root);
    +  goog.events.removeAll(log);
    +  goog.events.removeAll(input);
    +  goog.events.removeAll(testButton);
    +  goog.events.removeAll(parentEl);
    +  goog.events.removeAll(childEl);
    +
    +  root.innerHTML = '';
    +  firedEventTypes = [];
    +  firedEventCoordinates = [];
    +  firedScreenCoordinates = [];
    +  firedShiftKeys = [];
    +  firedKeyCodes = [];
    +
    +  for (var key in goog.events.EventType) {
    +    goog.events.listen(root, goog.events.EventType[key], function(e) {
    +      firedEventTypes.push(e.type);
    +      var coord = new goog.math.Coordinate(e.clientX, e.clientY);
    +      firedEventCoordinates.push(coord);
    +
    +      firedScreenCoordinates.push(
    +          new goog.math.Coordinate(e.screenX, e.screenY));
    +
    +      firedShiftKeys.push(!!e.shiftKey);
    +      firedKeyCodes.push(e.keyCode);
    +    });
    +  }
    +
    +  eventCount = {
    +    parentBubble: 0,
    +    parentCapture: 0,
    +    childCapture: 0,
    +    childBubble: 0
    +  };
    +  // Event listeners for the capture/bubble test.
    +  goog.events.listen(parentEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        eventCount.parentCapture++;
    +        assertEquals(parentEl, e.currentTarget);
    +        assertEquals(childEl, e.target);
    +      }, true);
    +  goog.events.listen(childEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        eventCount.childCapture++;
    +        assertEquals(childEl, e.currentTarget);
    +        assertEquals(childEl, e.target);
    +      }, true);
    +  goog.events.listen(childEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        eventCount.childBubble++;
    +        assertEquals(childEl, e.currentTarget);
    +        assertEquals(childEl, e.target);
    +      });
    +  goog.events.listen(parentEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        eventCount.parentBubble++;
    +        assertEquals(parentEl, e.currentTarget);
    +        assertEquals(childEl, e.target);
    +      });
    +}
    +
    +function tearDownPage() {
    +  for (var key in goog.events.EventType) {
    +    var type = goog.events.EventType[key];
    +    if (type == 'mousemove' || type == 'mouseout' || type == 'mouseover') {
    +      continue;
    +    }
    +    goog.dom.appendChild(input,
    +        goog.dom.createDom('label', null,
    +            goog.dom.createDom('input',
    +                {'id': type, 'type': 'checkbox'}),
    +            type,
    +            goog.dom.createDom('br')));
    +    goog.events.listen(testButton, type, function(e) {
    +      if (goog.dom.getElement(e.type).checked) {
    +        e.preventDefault();
    +      }
    +
    +      log.innerHTML += goog.string.subs('<br />%s (%s, %s)',
    +          e.type, e.clientX, e.clientY);
    +    });
    +  }
    +}
    +
    +function testMouseOver() {
    +  goog.testing.events.fireMouseOverEvent(root, null);
    +  goog.testing.events.fireMouseOverEvent(root, null, coordinate);
    +  assertEventTypes(['mouseover', 'mouseover']);
    +  assertCoordinates([goog.style.getClientPosition(root), coordinate]);
    +}
    +
    +function testMouseOut() {
    +  goog.testing.events.fireMouseOutEvent(root, null);
    +  goog.testing.events.fireMouseOutEvent(root, null, coordinate);
    +  assertEventTypes(['mouseout', 'mouseout']);
    +  assertCoordinates([goog.style.getClientPosition(root), coordinate]);
    +}
    +
    +function testFocus() {
    +  goog.testing.events.fireFocusEvent(root);
    +  assertEventTypes(['focus']);
    +}
    +
    +function testBlur() {
    +  goog.testing.events.fireBlurEvent(root);
    +  assertEventTypes(['blur']);
    +}
    +
    +function testClickSequence() {
    +  assertTrue(goog.testing.events.fireClickSequence(root));
    +  assertEventTypes(['mousedown', 'mouseup', 'click']);
    +  var rootPosition = goog.style.getClientPosition(root);
    +  assertCoordinates([rootPosition, rootPosition, rootPosition]);
    +}
    +
    +function testClickSequenceWithCoordinate() {
    +  assertTrue(goog.testing.events.fireClickSequence(root, null, coordinate));
    +  assertCoordinates([coordinate, coordinate, coordinate]);
    +  assertArrayEquals([false, false, false], firedShiftKeys);
    +}
    +
    +function testTouchStart() {
    +  goog.testing.events.fireTouchStartEvent(root);
    +  goog.testing.events.fireTouchStartEvent(root, coordinate);
    +  assertEventTypes(['touchstart', 'touchstart']);
    +  assertCoordinates([goog.style.getClientPosition(root), coordinate]);
    +}
    +
    +function testTouchMove() {
    +  goog.testing.events.fireTouchMoveEvent(root);
    +  goog.testing.events.fireTouchMoveEvent(root, coordinate, {touches: []});
    +  assertEventTypes(['touchmove', 'touchmove']);
    +  assertCoordinates([goog.style.getClientPosition(root), coordinate]);
    +}
    +
    +function testTouchEnd() {
    +  goog.testing.events.fireTouchEndEvent(root);
    +  goog.testing.events.fireTouchEndEvent(root, coordinate);
    +  assertEventTypes(['touchend', 'touchend']);
    +  assertCoordinates([goog.style.getClientPosition(root), coordinate]);
    +}
    +
    +function testTouchSequence() {
    +  assertTrue(goog.testing.events.fireTouchSequence(root));
    +  assertEventTypes(['touchstart', 'touchend']);
    +  var rootPosition = goog.style.getClientPosition(root);
    +  assertCoordinates([rootPosition, rootPosition]);
    +}
    +
    +function testTouchSequenceWithCoordinate() {
    +  assertTrue(goog.testing.events.fireTouchSequence(root, coordinate));
    +  assertCoordinates([coordinate, coordinate]);
    +}
    +
    +function testClickSequenceWithEventProperty() {
    +  assertTrue(goog.testing.events.fireClickSequence(
    +      root, null, undefined, { shiftKey: true }));
    +  assertArrayEquals([true, true, true], firedShiftKeys);
    +}
    +
    +function testClickSequenceCancellingMousedown() {
    +  preventDefaultEventType('mousedown');
    +  assertFalse(goog.testing.events.fireClickSequence(root));
    +  assertEventTypes(['mousedown', 'mouseup', 'click']);
    +}
    +
    +function testClickSequenceCancellingMousedownWithCoordinate() {
    +  preventDefaultEventType('mousedown');
    +  assertFalse(goog.testing.events.fireClickSequence(root, null, coordinate));
    +  assertCoordinates([coordinate, coordinate, coordinate]);
    +}
    +
    +function testClickSequenceCancellingMouseup() {
    +  preventDefaultEventType('mouseup');
    +  assertFalse(goog.testing.events.fireClickSequence(root));
    +  assertEventTypes(['mousedown', 'mouseup', 'click']);
    +}
    +
    +function testClickSequenceCancellingMouseupWithCoordinate() {
    +  preventDefaultEventType('mouseup');
    +  assertFalse(goog.testing.events.fireClickSequence(root, null, coordinate));
    +  assertCoordinates([coordinate, coordinate, coordinate]);
    +}
    +
    +function testClickSequenceCancellingClick() {
    +  preventDefaultEventType('click');
    +  assertFalse(goog.testing.events.fireClickSequence(root));
    +  assertEventTypes(['mousedown', 'mouseup', 'click']);
    +}
    +
    +function testClickSequenceCancellingClickWithCoordinate() {
    +  preventDefaultEventType('click');
    +  assertFalse(goog.testing.events.fireClickSequence(root, null, coordinate));
    +  assertCoordinates([coordinate, coordinate, coordinate]);
    +}
    +
    +// For a double click, IE fires selectstart instead of the second mousedown,
    +// but we don't simulate selectstart. Also, IE doesn't fire the second click.
    +var DBLCLICK_SEQ = (goog.userAgent.IE ?
    +                    ['mousedown',
    +                     'mouseup',
    +                     'click',
    +                     'mouseup',
    +                     'dblclick'] :
    +                    ['mousedown',
    +                     'mouseup',
    +                     'click',
    +                     'mousedown',
    +                     'mouseup',
    +                     'click',
    +                     'dblclick']);
    +
    +
    +var DBLCLICK_SEQ_COORDS = goog.array.repeat(coordinate, DBLCLICK_SEQ.length);
    +
    +function testDoubleClickSequence() {
    +  assertTrue(goog.testing.events.fireDoubleClickSequence(root));
    +  assertEventTypes(DBLCLICK_SEQ);
    +}
    +
    +function testDoubleClickSequenceWithCoordinate() {
    +  assertTrue(goog.testing.events.fireDoubleClickSequence(root, coordinate));
    +  assertCoordinates(DBLCLICK_SEQ_COORDS);
    +}
    +
    +function testDoubleClickSequenceCancellingMousedown() {
    +  preventDefaultEventType('mousedown');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root));
    +  assertEventTypes(DBLCLICK_SEQ);
    +}
    +
    +function testDoubleClickSequenceCancellingMousedownWithCoordinate() {
    +  preventDefaultEventType('mousedown');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root, coordinate));
    +  assertCoordinates(DBLCLICK_SEQ_COORDS);
    +}
    +
    +function testDoubleClickSequenceCancellingMouseup() {
    +  preventDefaultEventType('mouseup');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root));
    +  assertEventTypes(DBLCLICK_SEQ);
    +}
    +
    +function testDoubleClickSequenceCancellingMouseupWithCoordinate() {
    +  preventDefaultEventType('mouseup');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root, coordinate));
    +  assertCoordinates(DBLCLICK_SEQ_COORDS);
    +}
    +
    +function testDoubleClickSequenceCancellingClick() {
    +  preventDefaultEventType('click');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root));
    +  assertEventTypes(DBLCLICK_SEQ);
    +}
    +
    +function testDoubleClickSequenceCancellingClickWithCoordinate() {
    +  preventDefaultEventType('click');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root, coordinate));
    +  assertCoordinates(DBLCLICK_SEQ_COORDS);
    +}
    +
    +function testDoubleClickSequenceCancellingDoubleClick() {
    +  preventDefaultEventType('dblclick');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root));
    +  assertEventTypes(DBLCLICK_SEQ);
    +}
    +
    +function testDoubleClickSequenceCancellingDoubleClickWithCoordinate() {
    +  preventDefaultEventType('dblclick');
    +  assertFalse(goog.testing.events.fireDoubleClickSequence(root, coordinate));
    +  assertCoordinates(DBLCLICK_SEQ_COORDS);
    +}
    +
    +function testKeySequence() {
    +  assertTrue(goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.ZERO));
    +  assertEventTypes(['keydown', 'keypress', 'keyup']);
    +}
    +
    +function testKeySequenceCancellingKeydown() {
    +  preventDefaultEventType('keydown');
    +  assertFalse(goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.ZERO));
    +  assertEventTypes(['keydown', 'keyup']);
    +}
    +
    +function testKeySequenceCancellingKeypress() {
    +  preventDefaultEventType('keypress');
    +  assertFalse(goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.ZERO));
    +  assertEventTypes(['keydown', 'keypress', 'keyup']);
    +}
    +
    +function testKeySequenceCancellingKeyup() {
    +  preventDefaultEventType('keyup');
    +  assertFalse(goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.ZERO));
    +  assertEventTypes(['keydown', 'keypress', 'keyup']);
    +}
    +
    +function testKeySequenceWithEscapeKey() {
    +  assertTrue(goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.ESC));
    +  if (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525')) {
    +    assertEventTypes(['keydown', 'keyup']);
    +  } else {
    +    assertEventTypes(['keydown', 'keypress', 'keyup']);
    +  }
    +}
    +
    +function testKeySequenceForMacActionKeysNegative() {
    +  stubs.set(goog.userAgent, 'GECKO', false);
    +  goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.C, {'metaKey': true});
    +  assertEventTypes(['keydown', 'keypress', 'keyup']);
    +}
    +
    +function testKeySequenceForMacActionKeysPositive() {
    +  stubs.set(goog.userAgent, 'GECKO', true);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +  goog.testing.events.fireKeySequence(
    +      root, goog.events.KeyCodes.C, {'metaKey': true});
    +  assertEventTypes(['keypress', 'keyup']);
    +}
    +
    +function testKeySequenceForOptionKeysOnMac() {
    +  // Mac uses an option (or alt) key to type non-ASCII characters. This test
    +  // verifies we can emulate key events sent when typing such non-ASCII
    +  // characters.
    +  stubs.set(goog.userAgent, 'WEBKIT', true);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +
    +  var optionKeyCodes = [
    +    [0xc0, 0x00e6],  // option+'
    +    [0xbc, 0x2264],  // option+,
    +    [0xbd, 0x2013],  // option+-
    +    [0xbe, 0x2265],  // option+.
    +    [0xbf, 0x00f7],  // option+/
    +    [0x30, 0x00ba],  // option+0
    +    [0x31, 0x00a1],  // option+1
    +    [0x32, 0x2122],  // option+2
    +    [0x33, 0x00a3],  // option+3
    +    [0x34, 0x00a2],  // option+4
    +    [0x35, 0x221e],  // option+5
    +    [0x36, 0x00a7],  // option+6
    +    [0x37, 0x00b6],  // option+7
    +    [0x38, 0x2022],  // option+8
    +    [0x39, 0x00aa],  // option+9
    +    [0xba, 0x2026],  // option+;
    +    [0xbb, 0x2260],  // option+=
    +    [0xdb, 0x201c],  // option+[
    +    [0xdc, 0x00ab],  // option+\
    +    [0xdd, 0x2018],  // option+]
    +    [0x41, 0x00e5],  // option+a
    +    [0x42, 0x222b],  // option+b
    +    [0x43, 0x00e7],  // option+c
    +    [0x44, 0x2202],  // option+d
    +    [0x45, 0x00b4],  // option+e
    +    [0x46, 0x0192],  // option+f
    +    [0x47, 0x00a9],  // option+g
    +    [0x48, 0x02d9],  // option+h
    +    [0x49, 0x02c6],  // option+i
    +    [0x4a, 0x2206],  // option+j
    +    [0x4b, 0x02da],  // option+k
    +    [0x4c, 0x00ac],  // option+l
    +    [0x4d, 0x00b5],  // option+m
    +    [0x4e, 0x02dc],  // option+n
    +    [0x4f, 0x00f8],  // option+o
    +    [0x50, 0x03c0],  // option+p
    +    [0x51, 0x0153],  // option+q
    +    [0x52, 0x00ae],  // option+r
    +    [0x53, 0x00df],  // option+s
    +    [0x54, 0x2020],  // option+t
    +    [0x56, 0x221a],  // option+v
    +    [0x57, 0x2211],  // option+w
    +    [0x58, 0x2248],  // option+x
    +    [0x59, 0x00a5],  // option+y
    +    [0x5a, 0x03a9]   // option+z
    +  ];
    +
    +  for (var i = 0; i < optionKeyCodes.length; ++i) {
    +    firedEventTypes = [];
    +    firedKeyCodes = [];
    +    var keyCode = optionKeyCodes[i][0];
    +    var keyPressKeyCode = optionKeyCodes[i][1];
    +    goog.testing.events.fireNonAsciiKeySequence(
    +        root, keyCode, keyPressKeyCode, {'altKey': true});
    +    assertEventTypes(['keydown', 'keypress', 'keyup']);
    +    assertArrayEquals([keyCode, keyPressKeyCode, keyCode], firedKeyCodes);
    +  }
    +}
    +
    +var CONTEXTMENU_SEQ =
    +    goog.userAgent.WINDOWS ? ['mousedown', 'mouseup', 'contextmenu'] :
    +    goog.userAgent.GECKO ? ['mousedown', 'contextmenu', 'mouseup'] :
    +    goog.userAgent.WEBKIT && goog.userAgent.MAC ?
    +        ['mousedown', 'contextmenu', 'mouseup', 'click'] :
    +    ['mousedown', 'contextmenu', 'mouseup'];
    +
    +function testContextMenuSequence() {
    +  assertTrue(goog.testing.events.fireContextMenuSequence(root));
    +  assertEventTypes(CONTEXTMENU_SEQ);
    +}
    +
    +function testContextMenuSequenceWithCoordinate() {
    +  assertTrue(goog.testing.events.fireContextMenuSequence(root, coordinate));
    +  assertEventTypes(CONTEXTMENU_SEQ);
    +  assertCoordinates(goog.array.repeat(coordinate, CONTEXTMENU_SEQ.length));
    +}
    +
    +function testContextMenuSequenceCancellingMousedown() {
    +  preventDefaultEventType('mousedown');
    +  assertFalse(goog.testing.events.fireContextMenuSequence(root));
    +  assertEventTypes(CONTEXTMENU_SEQ);
    +}
    +
    +function testContextMenuSequenceCancellingMouseup() {
    +  preventDefaultEventType('mouseup');
    +  assertFalse(goog.testing.events.fireContextMenuSequence(root));
    +  assertEventTypes(CONTEXTMENU_SEQ);
    +}
    +
    +function testContextMenuSequenceCancellingContextMenu() {
    +  preventDefaultEventType('contextmenu');
    +  assertFalse(goog.testing.events.fireContextMenuSequence(root));
    +  assertEventTypes(CONTEXTMENU_SEQ);
    +}
    +
    +function testContextMenuSequenceFakeMacWebkit() {
    +  stubs.set(goog.userAgent, 'WINDOWS', false);
    +  stubs.set(goog.userAgent, 'MAC', true);
    +  stubs.set(goog.userAgent, 'WEBKIT', true);
    +  assertTrue(goog.testing.events.fireContextMenuSequence(root));
    +  assertEventTypes(['mousedown', 'contextmenu', 'mouseup', 'click']);
    +}
    +
    +function testCaptureBubble_simple() {
    +  assertTrue(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 1,
    +    childBubble: 1,
    +    parentBubble: 1
    +  }, eventCount);
    +}
    +
    +function testCaptureBubble_preventDefault() {
    +  goog.events.listen(childEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  assertFalse(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 1,
    +    childBubble: 1,
    +    parentBubble: 1
    +  }, eventCount);
    +}
    +
    +function testCaptureBubble_stopPropagationParentCapture() {
    +  goog.events.listen(parentEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        e.stopPropagation();
    +      }, true /* capture */);
    +  assertTrue(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 0,
    +    childBubble: 0,
    +    parentBubble: 0
    +  }, eventCount);
    +}
    +
    +function testCaptureBubble_stopPropagationChildCapture() {
    +  goog.events.listen(childEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        e.stopPropagation();
    +      }, true /* capture */);
    +  assertTrue(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 1,
    +    childBubble: 0,
    +    parentBubble: 0
    +  }, eventCount);
    +}
    +
    +function testCaptureBubble_stopPropagationChildBubble() {
    +  goog.events.listen(childEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        e.stopPropagation();
    +      });
    +  assertTrue(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 1,
    +    childBubble: 1,
    +    parentBubble: 0
    +  }, eventCount);
    +}
    +
    +function testCaptureBubble_stopPropagationParentBubble() {
    +  goog.events.listen(parentEl, goog.events.EventType.CLICK,
    +      function(e) {
    +        e.stopPropagation();
    +      });
    +  assertTrue(goog.testing.events.fireClickEvent(childEl));
    +  assertObjectEquals({
    +    parentCapture: 1,
    +    childCapture: 1,
    +    childBubble: 1,
    +    parentBubble: 1
    +  }, eventCount);
    +}
    +
    +function testMixinListenable() {
    +  var obj = {};
    +  obj.doFoo = goog.testing.recordFunction();
    +
    +  goog.testing.events.mixinListenable(obj);
    +
    +  obj.doFoo();
    +  assertEquals(1, obj.doFoo.getCallCount());
    +
    +  var handler = goog.testing.recordFunction();
    +  goog.events.listen(obj, 'test', handler);
    +  obj.dispatchEvent('test');
    +  assertEquals(1, handler.getCallCount());
    +  assertEquals(obj, handler.getLastCall().getArgument(0).target);
    +
    +  goog.events.unlisten(obj, 'test', handler);
    +  obj.dispatchEvent('test');
    +  assertEquals(1, handler.getCallCount());
    +
    +  goog.events.listen(obj, 'test', handler);
    +  obj.dispose();
    +  obj.dispatchEvent('test');
    +  assertEquals(1, handler.getCallCount());
    +}
    +
    +
    +/**
    + * Assert that the list of events given was fired, in that order.
    + */
    +function assertEventTypes(list) {
    +  assertArrayEquals(list, firedEventTypes);
    +}
    +
    +
    +/**
    + * Assert that the list of event coordinates given was caught, in that order.
    + */
    +function assertCoordinates(list) {
    +  assertArrayEquals(list, firedEventCoordinates);
    +  assertArrayEquals(list, firedScreenCoordinates);
    +}
    +
    +
    +/** Prevent default the event of the given type on the root element. */
    +function preventDefaultEventType(type) {
    +  goog.events.listen(root, type, preventDefault);
    +}
    +
    +function preventDefault(e) {
    +  e.preventDefault();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/matchers.js b/mapbox-gl/closure-library/closure/goog/testing/events/matchers.js
    new file mode 100644
    index 0000000000..7223b08455
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/matchers.js
    @@ -0,0 +1,42 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock matchers for event related arguments.
    + */
    +
    +goog.provide('goog.testing.events.EventMatcher');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.testing.mockmatchers.ArgumentMatcher');
    +
    +
    +
    +/**
    + * A matcher that verifies that an argument is a {@code goog.events.Event} of a
    + * particular type.
    + * @param {string} type The single type the event argument must be of.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.events.EventMatcher = function(type) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function(obj) {
    +        return obj instanceof goog.events.Event &&
    +            obj.type == type;
    +      }, 'isEventOfType(' + type + ')');
    +};
    +goog.inherits(goog.testing.events.EventMatcher,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.html b/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.html
    new file mode 100644
    index 0000000000..1d49ee2e14
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <!--
    +
    +  -->
    +  <title>
    +   Closure Unit Tests - goog.testing.events.EventMatcher
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.testing.events.EventMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.js b/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.js
    new file mode 100644
    index 0000000000..12826b039a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/matchers_test.js
    @@ -0,0 +1,35 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.events.EventMatcherTest');
    +goog.setTestOnly('goog.testing.events.EventMatcherTest');
    +
    +goog.require('goog.events.Event');
    +goog.require('goog.testing.events.EventMatcher');
    +goog.require('goog.testing.jsunit');
    +
    +function testEventMatcher() {
    +  var matcher = new goog.testing.events.EventMatcher('foo');
    +  assertFalse(matcher.matches(undefined));
    +  assertFalse(matcher.matches(null));
    +  assertFalse(matcher.matches({type: 'foo'}));
    +  assertFalse(matcher.matches(new goog.events.Event('bar')));
    +
    +  assertTrue(matcher.matches(new goog.events.Event('foo')));
    +  var FooEvent = function() {
    +    goog.events.Event.call(this, 'foo');
    +  };
    +  goog.inherits(FooEvent, goog.events.Event);
    +  assertTrue(matcher.matches(new FooEvent()));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler.js b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler.js
    new file mode 100644
    index 0000000000..31529bff12
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler.js
    @@ -0,0 +1,65 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview NetworkStatusMonitor test double.
    + * @author dbk@google.com (David Barrett-Kahn)
    + */
    +
    +goog.provide('goog.testing.events.OnlineHandler');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.net.NetworkStatusMonitor');
    +
    +
    +
    +/**
    + * NetworkStatusMonitor test double.
    + * @param {boolean} initialState The initial online state of the mock.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @implements {goog.net.NetworkStatusMonitor}
    + * @final
    + */
    +goog.testing.events.OnlineHandler = function(initialState) {
    +  goog.testing.events.OnlineHandler.base(this, 'constructor');
    +
    +  /**
    +   * Whether the mock is online.
    +   * @private {boolean}
    +   */
    +  this.online_ = initialState;
    +};
    +goog.inherits(goog.testing.events.OnlineHandler, goog.events.EventTarget);
    +
    +
    +/** @override */
    +goog.testing.events.OnlineHandler.prototype.isOnline = function() {
    +  return this.online_;
    +};
    +
    +
    +/**
    + * Sets the online state.
    + * @param {boolean} newOnlineState The new online state.
    + */
    +goog.testing.events.OnlineHandler.prototype.setOnline =
    +    function(newOnlineState) {
    +  if (newOnlineState != this.online_) {
    +    this.online_ = newOnlineState;
    +    this.dispatchEvent(newOnlineState ?
    +        goog.net.NetworkStatusMonitor.EventType.ONLINE :
    +        goog.net.NetworkStatusMonitor.EventType.OFFLINE);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.html b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.html
    new file mode 100644
    index 0000000000..59034b204e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<html>
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <!--
    +
    +  Author: dbk@google.com (David Barrett-Kahn)
    +  -->
    +  <title>
    +   Closure Unit Tests - goog.testing.events
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.events.OnlineHandlerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.js b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.js
    new file mode 100644
    index 0000000000..00bb3c3803
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/events/onlinehandler_test.js
    @@ -0,0 +1,84 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.events.OnlineHandlerTest');
    +goog.setTestOnly('goog.testing.events.OnlineHandlerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.net.NetworkStatusMonitor');
    +goog.require('goog.testing.events.EventObserver');
    +goog.require('goog.testing.events.OnlineHandler');
    +goog.require('goog.testing.jsunit');
    +
    +var handler;
    +
    +var observer;
    +
    +function tearDown() {
    +  handler = null;
    +  observer = null;
    +}
    +
    +function testInitialValue() {
    +  createHandler(true);
    +  assertEquals(true, handler.isOnline());
    +  createHandler(false);
    +  assertEquals(false, handler.isOnline());
    +}
    +
    +function testStateChange() {
    +  createHandler(true);
    +  assertEventCounts(0 /* expectedOnlineEvents */,
    +      0 /* expectedOfflineEvents */);
    +
    +  // Expect no events.
    +  handler.setOnline(true);
    +  assertEquals(true, handler.isOnline());
    +  assertEventCounts(0 /* expectedOnlineEvents */,
    +      0 /* expectedOfflineEvents */);
    +
    +  // Expect one offline event.
    +  handler.setOnline(false);
    +  assertEquals(false, handler.isOnline());
    +  assertEventCounts(0 /* expectedOnlineEvents */,
    +      1 /* expectedOfflineEvents */);
    +
    +  // Expect no events.
    +  handler.setOnline(false);
    +  assertEquals(false, handler.isOnline());
    +  assertEventCounts(0 /* expectedOnlineEvents */,
    +      1 /* expectedOfflineEvents */);
    +
    +  // Expect one online event.
    +  handler.setOnline(true);
    +  assertEquals(true, handler.isOnline());
    +  assertEventCounts(1 /* expectedOnlineEvents */,
    +      1 /* expectedOfflineEvents */);
    +}
    +
    +function createHandler(initialValue) {
    +  handler = new goog.testing.events.OnlineHandler(initialValue);
    +  observer = new goog.testing.events.EventObserver();
    +  goog.events.listen(handler,
    +      [goog.net.NetworkStatusMonitor.EventType.ONLINE,
    +       goog.net.NetworkStatusMonitor.EventType.OFFLINE],
    +      observer);
    +}
    +
    +function assertEventCounts(expectedOnlineEvents, expectedOfflineEvents) {
    +  assertEquals(expectedOnlineEvents, observer.getEvents(
    +      goog.net.NetworkStatusMonitor.EventType.ONLINE).length);
    +  assertEquals(expectedOfflineEvents, observer.getEvents(
    +      goog.net.NetworkStatusMonitor.EventType.OFFLINE).length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/expectedfailures.js b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures.js
    new file mode 100644
    index 0000000000..1df3a1c1d7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures.js
    @@ -0,0 +1,237 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Helper class to allow for expected unit test failures.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.testing.ExpectedFailures');
    +
    +goog.require('goog.debug.DivConsole');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.log');
    +goog.require('goog.style');
    +goog.require('goog.testing.JsUnitException');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.asserts');
    +
    +
    +
    +/**
    + * Helper class for allowing some unit tests to fail, particularly designed to
    + * mark tests that should be fixed on a given browser.
    + *
    + * <pre>
    + * var expectedFailures = new goog.testing.ExpectedFailures();
    + *
    + * function tearDown() {
    + *   expectedFailures.handleTearDown();
    + * }
    + *
    + * function testSomethingThatBreaksInWebKit() {
    + *   expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
    + *
    + *   try {
    + *     ...
    + *     assert(somethingThatFailsInWebKit);
    + *     ...
    + *   } catch (e) {
    + *     expectedFailures.handleException(e);
    + *   }
    + * }
    + * </pre>
    + *
    + * @constructor
    + * @final
    + */
    +goog.testing.ExpectedFailures = function() {
    +  goog.testing.ExpectedFailures.setUpConsole_();
    +  this.reset_();
    +};
    +
    +
    +/**
    + * The lazily created debugging console.
    + * @type {goog.debug.DivConsole?}
    + * @private
    + */
    +goog.testing.ExpectedFailures.console_ = null;
    +
    +
    +/**
    + * Logger for the expected failures.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.logger_ =
    +    goog.log.getLogger('goog.testing.ExpectedFailures');
    +
    +
    +/**
    + * Whether or not we are expecting failure.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.expectingFailure_;
    +
    +
    +/**
    + * The string to emit upon an expected failure.
    + * @type {string}
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.failureMessage_;
    +
    +
    +/**
    + * An array of suppressed failures.
    + * @type {Array<!Error>}
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.suppressedFailures_;
    +
    +
    +/**
    + * Sets up the debug console, if it isn't already set up.
    + * @private
    + */
    +goog.testing.ExpectedFailures.setUpConsole_ = function() {
    +  if (!goog.testing.ExpectedFailures.console_) {
    +    var xButton = goog.dom.createDom(goog.dom.TagName.DIV, {
    +      'style': 'position: absolute; border-left:1px solid #333;' +
    +          'border-bottom:1px solid #333; right: 0; top: 0; width: 1em;' +
    +          'height: 1em; cursor: pointer; background-color: #cde;' +
    +          'text-align: center; color: black'
    +    }, 'X');
    +    var div = goog.dom.createDom(goog.dom.TagName.DIV, {
    +      'style': 'position: absolute; border: 1px solid #333; right: 10px;' +
    +          'top : 10px; width: 400px; display: none'
    +    }, xButton);
    +    document.body.appendChild(div);
    +    goog.events.listen(xButton, goog.events.EventType.CLICK, function() {
    +      goog.style.setElementShown(div, false);
    +    });
    +
    +    goog.testing.ExpectedFailures.console_ = new goog.debug.DivConsole(div);
    +    goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_,
    +        goog.bind(goog.style.setElementShown, null, div, true));
    +    goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_,
    +        goog.bind(goog.testing.ExpectedFailures.console_.addLogRecord,
    +            goog.testing.ExpectedFailures.console_));
    +  }
    +};
    +
    +
    +/**
    + * Register to expect failure for the given condition.  Multiple calls to this
    + * function act as a boolean OR.  The first applicable message will be used.
    + * @param {boolean} condition Whether to expect failure.
    + * @param {string=} opt_message Descriptive message of this expected failure.
    + */
    +goog.testing.ExpectedFailures.prototype.expectFailureFor = function(
    +    condition, opt_message) {
    +  this.expectingFailure_ = this.expectingFailure_ || condition;
    +  if (condition) {
    +    this.failureMessage_ = this.failureMessage_ || opt_message || '';
    +  }
    +};
    +
    +
    +/**
    + * Determines if the given exception was expected.
    + * @param {Object} ex The exception to check.
    + * @return {boolean} Whether the exception was expected.
    + */
    +goog.testing.ExpectedFailures.prototype.isExceptionExpected = function(ex) {
    +  return this.expectingFailure_ && ex instanceof goog.testing.JsUnitException;
    +};
    +
    +
    +/**
    + * Handle an exception, suppressing it if it is a unit test failure that we
    + * expected.
    + * @param {Error} ex The exception to handle.
    + */
    +goog.testing.ExpectedFailures.prototype.handleException = function(ex) {
    +  if (this.isExceptionExpected(ex)) {
    +    goog.log.info(this.logger_, 'Suppressing test failure in ' +
    +        goog.testing.TestCase.currentTestName + ':' +
    +        (this.failureMessage_ ? '\n(' + this.failureMessage_ + ')' : ''),
    +        ex);
    +    this.suppressedFailures_.push(ex);
    +    return;
    +  }
    +
    +  // Rethrow the exception if we weren't expecting it or if it is a normal
    +  // exception.
    +  throw ex;
    +};
    +
    +
    +/**
    + * Run the given function, catching any expected failures.
    + * @param {Function} func The function to run.
    + * @param {boolean=} opt_lenient Whether to ignore if the expected failures
    + *     didn't occur.  In this case a warning will be logged in handleTearDown.
    + */
    +goog.testing.ExpectedFailures.prototype.run = function(func, opt_lenient) {
    +  try {
    +    func();
    +  } catch (ex) {
    +    this.handleException(ex);
    +  }
    +
    +  if (!opt_lenient && this.expectingFailure_ &&
    +      !this.suppressedFailures_.length) {
    +    fail(this.getExpectationMessage_());
    +  }
    +};
    +
    +
    +/**
    + * @return {string} A warning describing an expected failure that didn't occur.
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.getExpectationMessage_ = function() {
    +  return 'Expected a test failure in \'' +
    +         goog.testing.TestCase.currentTestName + '\' but the test passed.';
    +};
    +
    +
    +/**
    + * Handle the tearDown phase of a test, alerting the user if an expected test
    + * was not suppressed.
    + */
    +goog.testing.ExpectedFailures.prototype.handleTearDown = function() {
    +  if (this.expectingFailure_ && !this.suppressedFailures_.length) {
    +    goog.log.warning(this.logger_, this.getExpectationMessage_());
    +  }
    +  this.reset_();
    +};
    +
    +
    +/**
    + * Reset internal state.
    + * @private
    + */
    +goog.testing.ExpectedFailures.prototype.reset_ = function() {
    +  this.expectingFailure_ = false;
    +  this.failureMessage_ = '';
    +  this.suppressedFailures_ = [];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.html b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.html
    new file mode 100644
    index 0000000000..1ae17be86a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.ExpectedFailures
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.ExpectedFailuresTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.js b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.js
    new file mode 100644
    index 0000000000..8a287b0664
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/expectedfailures_test.js
    @@ -0,0 +1,121 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.ExpectedFailuresTest');
    +goog.setTestOnly('goog.testing.ExpectedFailuresTest');
    +
    +goog.require('goog.debug.Logger');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.JsUnitException');
    +goog.require('goog.testing.jsunit');
    +
    +var count, expectedFailures, lastLevel, lastMessage;
    +
    +// Stub out the logger.
    +goog.testing.ExpectedFailures.prototype.logger_.log = function(level,
    +    message) {
    +  lastLevel = level;
    +  lastMessage = message;
    +  count++;
    +};
    +
    +function setUp() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +  count = 0;
    +  lastLevel = lastMessage = '';
    +}
    +
    +// Individual test methods.
    +
    +function testNoExpectedFailure() {
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testPreventExpectedFailure() {
    +  expectedFailures.expectFailureFor(true);
    +
    +  expectedFailures.handleException(new goog.testing.JsUnitException('', ''));
    +  assertEquals('Should have logged a message', 1, count);
    +  assertEquals('Should have logged an info message',
    +      goog.debug.Logger.Level.INFO, lastLevel);
    +  assertContains('Should log a suppression message',
    +      'Suppressing test failure', lastMessage);
    +
    +  expectedFailures.handleTearDown();
    +  assertEquals('Should not have logged another message', 1, count);
    +}
    +
    +function testDoNotPreventException() {
    +  var ex = 'exception';
    +  expectedFailures.expectFailureFor(false);
    +  var e = assertThrows('Should have rethrown exception', function() {
    +    expectedFailures.handleException(ex);
    +  });
    +  assertEquals('Should rethrow same exception', ex, e);
    +}
    +
    +function testExpectedFailureDidNotOccur() {
    +  expectedFailures.expectFailureFor(true);
    +
    +  expectedFailures.handleTearDown();
    +  assertEquals('Should have logged a message', 1, count);
    +  assertEquals('Should have logged a warning',
    +      goog.debug.Logger.Level.WARNING, lastLevel);
    +  assertContains('Should log a suppression message',
    +      'Expected a test failure', lastMessage);
    +}
    +
    +function testRun() {
    +  expectedFailures.expectFailureFor(true);
    +
    +  expectedFailures.run(function() {
    +    fail('Expected failure');
    +  });
    +
    +  assertEquals('Should have logged a message', 1, count);
    +  assertEquals('Should have logged an info message',
    +      goog.debug.Logger.Level.INFO, lastLevel);
    +  assertContains('Should log a suppression message',
    +      'Suppressing test failure', lastMessage);
    +
    +  expectedFailures.handleTearDown();
    +  assertEquals('Should not have logged another message', 1, count);
    +}
    +
    +function testRunStrict() {
    +  expectedFailures.expectFailureFor(true);
    +
    +  var ex = assertThrows(function() {
    +    expectedFailures.run(function() {
    +      // Doesn't fail!
    +    });
    +  });
    +  assertContains(
    +      "Expected a test failure in 'testRunStrict' but the test passed.",
    +      ex.message);
    +}
    +
    +function testRunLenient() {
    +  expectedFailures.expectFailureFor(true);
    +
    +  expectedFailures.run(function() {
    +    // Doesn't fail!
    +  }, true);
    +  expectedFailures.handleTearDown();
    +  assertEquals('Should have logged a message', 1, count);
    +  assertEquals('Should have logged a warning',
    +      goog.debug.Logger.Level.WARNING, lastLevel);
    +  assertContains('Should log a suppression message',
    +      'Expected a test failure', lastMessage);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/blob.js b/mapbox-gl/closure-library/closure/goog/testing/fs/blob.js
    new file mode 100644
    index 0000000000..d1da648eaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/blob.js
    @@ -0,0 +1,135 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock blob object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.Blob');
    +
    +goog.require('goog.crypt.base64');
    +
    +
    +
    +/**
    + * A mock Blob object. The data is stored as a string.
    + *
    + * @param {string=} opt_data The string data encapsulated by the blob.
    + * @param {string=} opt_type The mime type of the blob.
    + * @constructor
    + */
    +goog.testing.fs.Blob = function(opt_data, opt_type) {
    +  /**
    +   * @see http://www.w3.org/TR/FileAPI/#dfn-type
    +   * @type {string}
    +   */
    +  this.type = opt_type || '';
    +
    +  this.setDataInternal(opt_data || '');
    +};
    +
    +
    +/**
    + * The string data encapsulated by the blob.
    + * @type {string}
    + * @private
    + */
    +goog.testing.fs.Blob.prototype.data_;
    +
    +
    +/**
    + * @see http://www.w3.org/TR/FileAPI/#dfn-size
    + * @type {number}
    + */
    +goog.testing.fs.Blob.prototype.size;
    +
    +
    +/**
    + * Creates a blob with bytes of a blob ranging from the optional start
    + * parameter up to but not including the optional end parameter, and with a type
    + * attribute that is the value of the optional contentType parameter.
    + * @see http://www.w3.org/TR/FileAPI/#dfn-slice
    + * @param {number=} opt_start The start byte offset.
    + * @param {number=} opt_end The end point of a slice.
    + * @param {string=} opt_contentType The type of the resulting Blob.
    + * @return {!goog.testing.fs.Blob} The result blob of the slice operation.
    + */
    +goog.testing.fs.Blob.prototype.slice = function(
    +    opt_start, opt_end, opt_contentType) {
    +  var relativeStart;
    +  if (goog.isNumber(opt_start)) {
    +    relativeStart = (opt_start < 0) ?
    +        Math.max(this.data_.length + opt_start, 0) :
    +        Math.min(opt_start, this.data_.length);
    +  } else {
    +    relativeStart = 0;
    +  }
    +  var relativeEnd;
    +  if (goog.isNumber(opt_end)) {
    +    relativeEnd = (opt_end < 0) ?
    +        Math.max(this.data_.length + opt_end, 0) :
    +        Math.min(opt_end, this.data_.length);
    +  } else {
    +    relativeEnd = this.data_.length;
    +  }
    +  var span = Math.max(relativeEnd - relativeStart, 0);
    +  return new goog.testing.fs.Blob(
    +      this.data_.substr(relativeStart, span),
    +      opt_contentType);
    +};
    +
    +
    +/**
    + * @return {string} The string data encapsulated by the blob.
    + * @override
    + */
    +goog.testing.fs.Blob.prototype.toString = function() {
    +  return this.data_;
    +};
    +
    +
    +/**
    + * @return {!ArrayBuffer} The string data encapsulated by the blob as an
    + *     ArrayBuffer.
    + */
    +goog.testing.fs.Blob.prototype.toArrayBuffer = function() {
    +  var buf = new ArrayBuffer(this.data_.length * 2);
    +  var arr = new Uint16Array(buf);
    +  for (var i = 0; i < this.data_.length; i++) {
    +    arr[i] = this.data_.charCodeAt(i);
    +  }
    +  return buf;
    +};
    +
    +
    +/**
    + * @return {string} The string data encapsulated by the blob as a data: URI.
    + */
    +goog.testing.fs.Blob.prototype.toDataUrl = function() {
    +  return 'data:' + this.type + ';base64,' +
    +      goog.crypt.base64.encodeString(this.data_);
    +};
    +
    +
    +/**
    + * Sets the internal contents of the blob. This should only be called by other
    + * functions inside the {@code goog.testing.fs} namespace.
    + *
    + * @param {string} data The data for this Blob.
    + */
    +goog.testing.fs.Blob.prototype.setDataInternal = function(data) {
    +  this.data_ = data;
    +  this.size = data.length;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.html
    new file mode 100644
    index 0000000000..50a46ff9bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.Blob
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.BlobTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.js
    new file mode 100644
    index 0000000000..8b57ee968f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/blob_test.js
    @@ -0,0 +1,65 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.BlobTest');
    +goog.setTestOnly('goog.testing.fs.BlobTest');
    +
    +goog.require('goog.testing.fs.Blob');
    +goog.require('goog.testing.jsunit');
    +
    +function testAttributes() {
    +  var blob = new goog.testing.fs.Blob();
    +  assertEquals(0, blob.size);
    +  assertEquals('', blob.type);
    +
    +  blob = new goog.testing.fs.Blob('foo bar baz');
    +  assertEquals(11, blob.size);
    +  assertEquals('', blob.type);
    +
    +  blob = new goog.testing.fs.Blob('foo bar baz', 'text/plain');
    +  assertEquals(11, blob.size);
    +  assertEquals('text/plain', blob.type);
    +}
    +
    +function testToString() {
    +  assertEquals('', new goog.testing.fs.Blob().toString());
    +  assertEquals('foo bar', new goog.testing.fs.Blob('foo bar').toString());
    +}
    +
    +function testSlice() {
    +  var blob = new goog.testing.fs.Blob('abcdef');
    +  assertEquals('bc', blob.slice(1, 3).toString());
    +  assertEquals('def', blob.slice(3, 10).toString());
    +  assertEquals('abcd', blob.slice(0, -2).toString());
    +  assertEquals('', blob.slice(10, 1).toString());
    +  assertEquals('b', blob.slice(-5, 2).toString());
    +
    +  assertEquals('abcdef', blob.slice().toString());
    +  assertEquals('abc', blob.slice(/* opt_start */ undefined, 3).toString());
    +  assertEquals('def', blob.slice(3).toString());
    +
    +  assertEquals('text/plain', blob.slice(1, 2, 'text/plain').type);
    +}
    +
    +function testSetDataInternal() {
    +  var blob = new goog.testing.fs.Blob();
    +
    +  blob.setDataInternal('asdf');
    +  assertEquals('asdf', blob.toString());
    +  assertEquals(4, blob.size);
    +
    +  blob.setDataInternal('');
    +  assertEquals('', blob.toString());
    +  assertEquals(0, blob.size);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.html
    new file mode 100644
    index 0000000000..732b6f31b9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.DirectoryEntry
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.DirectoryEntryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.js
    new file mode 100644
    index 0000000000..976190f458
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/directoryentry_test.js
    @@ -0,0 +1,319 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.DirectoryEntryTest');
    +goog.setTestOnly('goog.testing.fs.DirectoryEntryTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var fs, dir, mockClock;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  fs = new goog.testing.fs.FileSystem();
    +  dir = fs.getRoot().createDirectorySync('foo');
    +  dir.createDirectorySync('subdir').createFileSync('subfile');
    +  dir.createFileSync('file');
    +}
    +
    +function tearDown() {
    +  mockClock.uninstall();
    +}
    +
    +function testIsFile() {
    +  assertFalse(dir.isFile());
    +}
    +
    +function testIsDirectory() {
    +  assertTrue(dir.isDirectory());
    +}
    +
    +function testRemoveWithChildren() {
    +  dir.getFileSync('bar', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  expectError(dir.remove(), goog.fs.Error.ErrorCode.INVALID_MODIFICATION);
    +}
    +
    +function testRemoveWithoutChildren() {
    +  var emptyDir = dir.getDirectorySync(
    +      'empty', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  emptyDir.remove().
    +      addCallback(function() {
    +        assertTrue(emptyDir.deleted);
    +        assertFalse(fs.getRoot().hasChild('empty'));
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file removal');
    +}
    +
    +function testRemoveRootRecursively() {
    +  var root = fs.getRoot();
    +  root.removeRecursively().addCallback(function() {
    +    assertTrue(dir.deleted);
    +    assertFalse(fs.getRoot().deleted);
    +  })
    +  .addBoth(continueTesting);
    +  waitForAsync('waiting for testRemoveRoot');
    +}
    +
    +function testGetFile() {
    +  // Advance the clock by an arbitrary but known amount.
    +  mockClock.tick(41);
    +  dir.getFile('file').
    +      addCallback(function(file) {
    +        assertEquals(dir.getFileSync('file'), file);
    +        assertEquals('file', file.getName());
    +        assertEquals('/foo/file', file.getFullPath());
    +        assertTrue(file.isFile());
    +
    +        return dir.getLastModified();
    +      }).
    +      addCallback(function(date) {
    +        assertEquals('Reading a file should not update the modification date.',
    +            0, date.getTime());
    +        return dir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals('Reading a file should not update the metadata.',
    +            0, metadata.modificationTime.getTime());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file');
    +}
    +
    +function testGetFileFromSubdir() {
    +  dir.getFile('subdir/subfile').addCallback(function(file) {
    +    assertEquals(dir.getDirectorySync('subdir').getFileSync('subfile'), file);
    +    assertEquals('subfile', file.getName());
    +    assertEquals('/foo/subdir/subfile', file.getFullPath());
    +    assertTrue(file.isFile());
    +  }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file');
    +}
    +
    +function testGetAbsolutePaths() {
    +  fs.getRoot().getFile('/foo/subdir/subfile').
    +      addCallback(function(subfile) {
    +        assertEquals('/foo/subdir/subfile', subfile.getFullPath());
    +        return fs.getRoot().getDirectory('//foo////');
    +      }).
    +      addCallback(function(foo) {
    +        assertEquals('/foo', foo.getFullPath());
    +        return foo.getDirectory('/');
    +      }).
    +      addCallback(function(root) {
    +        assertEquals('/', root.getFullPath());
    +        return root.getDirectory('/////');
    +      }).
    +      addCallback(function(root) {
    +        assertEquals('/', root.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testGetAbsolutePaths');
    +}
    +
    +function testCreateFile() {
    +  mockClock.tick(43);
    +  dir.getLastModified().
    +      addCallback(function(date) { assertEquals(0, date.getTime()); }).
    +      addCallback(function() {
    +        return dir.getFile('bar', goog.fs.DirectoryEntry.Behavior.CREATE);
    +      }).
    +      addCallback(function(file) {
    +        mockClock.tick();
    +        assertEquals('bar', file.getName());
    +        assertEquals('/foo/bar', file.getFullPath());
    +        assertEquals(dir, file.parent);
    +        assertTrue(file.isFile());
    +
    +        return dir.getLastModified();
    +      }).
    +      addCallback(function(date) {
    +        assertEquals(43, date.getTime());
    +        return dir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals(43, metadata.modificationTime.getTime());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file creation');
    +}
    +
    +function testCreateFileThatAlreadyExists() {
    +  mockClock.tick(47);
    +  var existingFile = dir.getFileSync('file');
    +  dir.getFile('file', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(file) {
    +        mockClock.tick();
    +        assertEquals('file', file.getName());
    +        assertEquals('/foo/file', file.getFullPath());
    +        assertEquals(dir, file.parent);
    +        assertEquals(existingFile, file);
    +        assertTrue(file.isFile());
    +
    +        return dir.getLastModified();
    +      }).
    +      addCallback(function(date) {
    +        assertEquals(47, date.getTime());
    +        return dir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals(47, metadata.modificationTime.getTime());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file creation');
    +}
    +
    +function testCreateFileInSubdir() {
    +  dir.getFile('subdir/bar', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(function(file) {
    +        assertEquals('bar', file.getName());
    +        assertEquals('/foo/subdir/bar', file.getFullPath());
    +        assertEquals(dir.getDirectorySync('subdir'), file.parent);
    +        assertTrue(file.isFile());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file creation');
    +}
    +
    +function testCreateFileExclusive() {
    +  dir.getFile('bar', goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE).
    +      addCallback(function(file) {
    +        assertEquals('bar', file.getName());
    +        assertEquals('/foo/bar', file.getFullPath());
    +        assertEquals(dir, file.parent);
    +        assertTrue(file.isFile());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('waiting for file creation');
    +}
    +
    +function testGetNonExistentFile() {
    +  expectError(dir.getFile('bar'), goog.fs.Error.ErrorCode.NOT_FOUND);
    +}
    +
    +function testGetNonExistentFileInSubdir() {
    +  expectError(dir.getFile('subdir/bar'), goog.fs.Error.ErrorCode.NOT_FOUND);
    +}
    +
    +function testGetFileInNonExistentSubdir() {
    +  expectError(dir.getFile('bar/subfile'), goog.fs.Error.ErrorCode.NOT_FOUND);
    +}
    +
    +function testGetFileThatsActuallyADirectory() {
    +  expectError(dir.getFile('subdir'), goog.fs.Error.ErrorCode.TYPE_MISMATCH);
    +}
    +
    +function testCreateFileInNonExistentSubdir() {
    +  expectError(
    +      dir.getFile('bar/newfile', goog.fs.DirectoryEntry.Behavior.CREATE),
    +      goog.fs.Error.ErrorCode.NOT_FOUND);
    +}
    +
    +function testCreateFileThatsActuallyADirectory() {
    +  expectError(
    +      dir.getFile('subdir', goog.fs.DirectoryEntry.Behavior.CREATE),
    +      goog.fs.Error.ErrorCode.TYPE_MISMATCH);
    +}
    +
    +function testCreateExclusiveExistingFile() {
    +  expectError(
    +      dir.getFile('file', goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE),
    +      goog.fs.Error.ErrorCode.INVALID_MODIFICATION);
    +}
    +
    +function testListEmptyDirectory() {
    +  var emptyDir = fs.getRoot().
    +      getDirectorySync('empty', goog.fs.DirectoryEntry.Behavior.CREATE);
    +
    +  emptyDir.listDirectory().
    +      addCallback(function(entryList) {
    +        assertSameElements([], entryList);
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testListEmptyDirectory');
    +}
    +
    +function testListDirectory() {
    +  var root = fs.getRoot();
    +  root.getDirectorySync('dir1', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  root.getDirectorySync('dir2', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  root.getFileSync('file1', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  root.getFileSync('file2', goog.fs.DirectoryEntry.Behavior.CREATE);
    +
    +  fs.getRoot().listDirectory().
    +      addCallback(function(entryList) {
    +        assertSameElements([
    +          'dir1',
    +          'dir2',
    +          'file1',
    +          'file2',
    +          'foo'
    +        ],
    +        goog.array.map(entryList, function(entry) {
    +          return entry.getName();
    +        }));
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testListDirectory');
    +}
    +
    +function testCreatePath() {
    +  dir.createPath('baz/bat').
    +      addCallback(function(batDir) {
    +        assertEquals('/foo/baz/bat', batDir.getFullPath());
    +        return batDir.createPath('../zazzle');
    +      }).
    +      addCallback(function(zazzleDir) {
    +        assertEquals('/foo/baz/zazzle', zazzleDir.getFullPath());
    +        return zazzleDir.createPath('/elements/actinides/neptunium/');
    +      }).
    +      addCallback(function(elDir) {
    +        assertEquals('/elements/actinides/neptunium', elDir.getFullPath());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testCreatePath');
    +}
    +
    +
    +function continueTesting(result) {
    +  asyncTestCase.continueTesting();
    +  if (result instanceof Error) {
    +    throw result;
    +  }
    +  mockClock.tick();
    +}
    +
    +function expectError(deferred, code) {
    +  deferred.
    +      addCallback(function() { fail('Expected an error'); }).
    +      addErrback(function(err) {
    +        assertEquals(code, err.code);
    +        asyncTestCase.continueTesting();
    +      });
    +  waitForAsync('waiting for error');
    +}
    +
    +function waitForAsync(msg) {
    +  asyncTestCase.waitForAsync(msg);
    +  mockClock.tick();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/entry.js b/mapbox-gl/closure-library/closure/goog/testing/fs/entry.js
    new file mode 100644
    index 0000000000..c8aba75535
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/entry.js
    @@ -0,0 +1,637 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock filesystem objects. These are all in the same file to
    + * avoid circular dependency issues.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.DirectoryEntry');
    +goog.provide('goog.testing.fs.Entry');
    +goog.provide('goog.testing.fs.FileEntry');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.DirectoryEntryImpl');
    +goog.require('goog.fs.Entry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileEntry');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.testing.fs.File');
    +goog.require('goog.testing.fs.FileWriter');
    +
    +
    +
    +/**
    + * A mock filesystem entry object.
    + *
    + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
    + * @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly
    + *     containing this entry.
    + * @param {string} name The name of this entry.
    + * @constructor
    + * @implements {goog.fs.Entry}
    + */
    +goog.testing.fs.Entry = function(fs, parent, name) {
    +  /**
    +   * This entry's filesystem.
    +   * @type {!goog.testing.fs.FileSystem}
    +   * @private
    +   */
    +  this.fs_ = fs;
    +
    +  /**
    +   * The name of this entry.
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = name;
    +
    +  /**
    +   * The parent of this entry.
    +   * @type {!goog.testing.fs.DirectoryEntry}
    +   */
    +  this.parent = parent;
    +};
    +
    +
    +/**
    + * Whether or not this entry has been deleted.
    + * @type {boolean}
    + */
    +goog.testing.fs.Entry.prototype.deleted = false;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.isFile = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.isDirectory = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.getFullPath = function() {
    +  if (this.getName() == '' || this.parent.getName() == '') {
    +    // The root directory has an empty name
    +    return '/' + this.name_;
    +  } else {
    +    return this.parent.getFullPath() + '/' + this.name_;
    +  }
    +};
    +
    +
    +/**
    + * @return {!goog.testing.fs.FileSystem}
    + * @override
    + */
    +goog.testing.fs.Entry.prototype.getFileSystem = function() {
    +  return this.fs_;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.getLastModified = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.getMetadata = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.moveTo = function(parent, opt_newName) {
    +  var msg = 'moving ' + this.getFullPath() + ' into ' + parent.getFullPath() +
    +      (opt_newName ? ', renaming to ' + opt_newName : '');
    +  var newFile;
    +  return this.checkNotDeleted(msg).
    +      addCallback(function() { return this.copyTo(parent, opt_newName); }).
    +      addCallback(function(file) {
    +        newFile = file;
    +        return this.remove();
    +      }).addCallback(function() { return newFile; });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.copyTo = function(parent, opt_newName) {
    +  goog.asserts.assert(parent instanceof goog.testing.fs.DirectoryEntry);
    +  var msg = 'copying ' + this.getFullPath() + ' into ' + parent.getFullPath() +
    +      (opt_newName ? ', renaming to ' + opt_newName : '');
    +  var self = this;
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    var name = opt_newName || self.getName();
    +    var entry = self.clone();
    +    parent.children[name] = entry;
    +    parent.lastModifiedTimestamp_ = goog.now();
    +    entry.name_ = name;
    +    entry.parent = /** @type {!goog.testing.fs.DirectoryEntry} */ (parent);
    +    return entry;
    +  });
    +};
    +
    +
    +/**
    + * @return {!goog.testing.fs.Entry} A shallow copy of this entry object.
    + */
    +goog.testing.fs.Entry.prototype.clone = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.toUrl = function(opt_mimetype) {
    +  return 'fakefilesystem:' + this.getFullPath();
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.toUri = goog.testing.fs.Entry.prototype.toUrl;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.wrapEntry = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.remove = function() {
    +  var msg = 'removing ' + this.getFullPath();
    +  var self = this;
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    delete this.parent.children[self.getName()];
    +    self.parent.lastModifiedTimestamp_ = goog.now();
    +    self.deleted = true;
    +    return;
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.Entry.prototype.getParent = function() {
    +  var msg = 'getting parent of ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).
    +      addCallback(function() { return this.parent; });
    +};
    +
    +
    +/**
    + * Return a deferred that will call its errback if this entry has been deleted.
    + * In addition, the deferred will only run after a timeout of 0, and all its
    + * callbacks will run with the entry as "this".
    + *
    + * @param {string} action The name of the action being performed. For error
    + *     reporting.
    + * @return {!goog.async.Deferred} The deferred that will be called after a
    + *     timeout of 0.
    + * @protected
    + */
    +goog.testing.fs.Entry.prototype.checkNotDeleted = function(action) {
    +  var d = new goog.async.Deferred(undefined, this);
    +  goog.Timer.callOnce(function() {
    +    if (this.deleted) {
    +      var err = new goog.fs.Error(
    +          /** @type {!FileError} */ ({'name': 'NotFoundError'}),
    +          action);
    +      d.errback(err);
    +    } else {
    +      d.callback();
    +    }
    +  }, 0, this);
    +  return d;
    +};
    +
    +
    +
    +/**
    + * A mock directory entry object.
    + *
    + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
    + * @param {goog.testing.fs.DirectoryEntry} parent The directory entry directly
    + *     containing this entry. If this is null, that means this is the root
    + *     directory and so is its own parent.
    + * @param {string} name The name of this entry.
    + * @param {!Object<!goog.testing.fs.Entry>} children The map of child names to
    + *     entry objects.
    + * @constructor
    + * @extends {goog.testing.fs.Entry}
    + * @implements {goog.fs.DirectoryEntry}
    + * @final
    + */
    +goog.testing.fs.DirectoryEntry = function(fs, parent, name, children) {
    +  goog.testing.fs.DirectoryEntry.base(
    +      this, 'constructor', fs, parent || this, name);
    +
    +  /**
    +   * The map of child names to entry objects.
    +   * @type {!Object<!goog.testing.fs.Entry>}
    +   */
    +  this.children = children;
    +
    +  /**
    +   * The modification time of the directory. Measured using goog.now, which may
    +   * be overridden with mock time providers.
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastModifiedTimestamp_ = goog.now();
    +};
    +goog.inherits(goog.testing.fs.DirectoryEntry, goog.testing.fs.Entry);
    +
    +
    +/**
    + * Constructs and returns the metadata object for this entry.
    + * @return {{modificationTime: Date}} The metadata object.
    + * @private
    + */
    +goog.testing.fs.DirectoryEntry.prototype.getMetadata_ = function() {
    +  return {
    +    'modificationTime': new Date(this.lastModifiedTimestamp_)
    +  };
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.isFile = function() {
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.isDirectory = function() {
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.getLastModified = function() {
    +  var msg = 'reading last modified date for ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).
    +      addCallback(function() {return new Date(this.lastModifiedTimestamp_)});
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.getMetadata = function() {
    +  var msg = 'reading metadata for ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).
    +      addCallback(function() {return this.getMetadata_()});
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.clone = function() {
    +  return new goog.testing.fs.DirectoryEntry(
    +      this.getFileSystem(), this.parent, this.getName(), this.children);
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.remove = function() {
    +  if (!goog.object.isEmpty(this.children)) {
    +    var d = new goog.async.Deferred();
    +    goog.Timer.callOnce(function() {
    +      d.errback(new goog.fs.Error(
    +          /** @type {!FileError} */ ({'name': 'InvalidModificationError'}),
    +          'removing ' + this.getFullPath()));
    +    }, 0, this);
    +    return d;
    +  } else if (this != this.getFileSystem().getRoot()) {
    +    return goog.testing.fs.DirectoryEntry.base(this, 'remove');
    +  } else {
    +    // Root directory, do nothing.
    +    return goog.async.Deferred.succeed();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.getFile = function(
    +    path, opt_behavior) {
    +  var msg = 'loading file ' + path + ' from ' + this.getFullPath();
    +  opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    try {
    +      return goog.async.Deferred.succeed(this.getFileSync(path, opt_behavior));
    +    } catch (e) {
    +      return goog.async.Deferred.fail(e);
    +    }
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.getDirectory = function(
    +    path, opt_behavior) {
    +  var msg = 'loading directory ' + path + ' from ' + this.getFullPath();
    +  opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    try {
    +      return goog.async.Deferred.succeed(
    +          this.getDirectorySync(path, opt_behavior));
    +    } catch (e) {
    +      return goog.async.Deferred.fail(e);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Get a file entry synchronously, without waiting for a Deferred to resolve.
    + *
    + * @param {string} path The path to the file, relative to this directory.
    + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
    + *     loading the file.
    + * @param {string=} opt_data The string data encapsulated by the blob.
    + * @param {string=} opt_type The mime type of the blob.
    + * @return {!goog.testing.fs.FileEntry} The loaded file.
    + */
    +goog.testing.fs.DirectoryEntry.prototype.getFileSync = function(
    +    path, opt_behavior, opt_data, opt_type) {
    +  opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
    +  return (/** @type {!goog.testing.fs.FileEntry} */ (this.getEntry_(
    +      path, opt_behavior, true /* isFile */,
    +      goog.bind(function(parent, name) {
    +        return new goog.testing.fs.FileEntry(
    +            this.getFileSystem(), parent, name,
    +            goog.isDef(opt_data) ? opt_data : '', opt_type);
    +      }, this))));
    +};
    +
    +
    +/**
    + * Creates a file synchronously. This is a shorthand for getFileSync, useful for
    + * setting up tests.
    + *
    + * @param {string} path The path to the file, relative to this directory.
    + * @return {!goog.testing.fs.FileEntry} The created file.
    + */
    +goog.testing.fs.DirectoryEntry.prototype.createFileSync = function(path) {
    +  return this.getFileSync(path, goog.fs.DirectoryEntry.Behavior.CREATE);
    +};
    +
    +
    +/**
    + * Get a directory synchronously, without waiting for a Deferred to resolve.
    + *
    + * @param {string} path The path to the directory, relative to this one.
    + * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
    + *     loading the directory.
    + * @return {!goog.testing.fs.DirectoryEntry} The loaded directory.
    + */
    +goog.testing.fs.DirectoryEntry.prototype.getDirectorySync = function(
    +    path, opt_behavior) {
    +  opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
    +  return (/** @type {!goog.testing.fs.DirectoryEntry} */ (this.getEntry_(
    +      path, opt_behavior, false /* isFile */,
    +      goog.bind(function(parent, name) {
    +        return new goog.testing.fs.DirectoryEntry(
    +            this.getFileSystem(), parent, name, {});
    +      }, this))));
    +};
    +
    +
    +/**
    + * Creates a directory synchronously. This is a shorthand for getFileSync,
    + * useful for setting up tests.
    + *
    + * @param {string} path The path to the directory, relative to this directory.
    + * @return {!goog.testing.fs.DirectoryEntry} The created directory.
    + */
    +goog.testing.fs.DirectoryEntry.prototype.createDirectorySync = function(path) {
    +  return this.getDirectorySync(path, goog.fs.DirectoryEntry.Behavior.CREATE);
    +};
    +
    +
    +/**
    + * Get a file or directory entry from a path. This handles parsing the path for
    + * subdirectories and throwing appropriate errors should something go wrong.
    + *
    + * @param {string} path The path to the entry, relative to this directory.
    + * @param {goog.fs.DirectoryEntry.Behavior} behavior The behavior for loading
    + *     the entry.
    + * @param {boolean} isFile Whether a file or directory is being loaded.
    + * @param {function(!goog.testing.fs.DirectoryEntry, string) :
    + *             !goog.testing.fs.Entry} createFn
    + *     The function for creating the entry if it doesn't yet exist. This is
    + *     passed the parent entry and the name of the new entry.
    + * @return {!goog.testing.fs.Entry} The loaded entry.
    + * @private
    + */
    +goog.testing.fs.DirectoryEntry.prototype.getEntry_ = function(
    +    path, behavior, isFile, createFn) {
    +  // Filter out leading, trailing, and duplicate slashes.
    +  var components = goog.array.filter(path.split('/'), goog.functions.identity);
    +
    +  var basename = /** @type {string} */ (goog.array.peek(components)) || '';
    +  var dir = goog.string.startsWith(path, '/') ?
    +      this.getFileSystem().getRoot() : this;
    +
    +  goog.array.forEach(components.slice(0, -1), function(p) {
    +    var subdir = dir.children[p];
    +    if (!subdir) {
    +      throw new goog.fs.Error(
    +          /** @type {!FileError} */ ({'name': 'NotFoundError'}),
    +          'loading ' + path + ' from ' + this.getFullPath() + ' (directory ' +
    +          dir.getFullPath() + '/' + p + ')');
    +    }
    +    dir = subdir;
    +  }, this);
    +
    +  // If there is no basename, the path must resolve to the root directory.
    +  var entry = basename ? dir.children[basename] : dir;
    +
    +  if (!entry) {
    +    if (behavior == goog.fs.DirectoryEntry.Behavior.DEFAULT) {
    +      throw new goog.fs.Error(
    +          /** @type {!FileError} */ ({'name': 'NotFoundError'}),
    +          'loading ' + path + ' from ' + this.getFullPath());
    +    } else {
    +      goog.asserts.assert(
    +          behavior == goog.fs.DirectoryEntry.Behavior.CREATE ||
    +          behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
    +      entry = createFn(dir, basename);
    +      dir.children[basename] = entry;
    +      this.lastModifiedTimestamp_ = goog.now();
    +      return entry;
    +    }
    +  } else if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) {
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidModificationError'}),
    +        'loading ' + path + ' from ' + this.getFullPath());
    +  } else if (entry.isFile() != isFile) {
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'TypeMismatchError'}),
    +        'loading ' + path + ' from ' + this.getFullPath());
    +  } else {
    +    if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE) {
    +      this.lastModifiedTimestamp_ = goog.now();
    +    }
    +    return entry;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether this directory has a child with the given name.
    + *
    + * @param {string} name The name of the entry to check for.
    + * @return {boolean} Whether or not this has a child with the given name.
    + */
    +goog.testing.fs.DirectoryEntry.prototype.hasChild = function(name) {
    +  return name in this.children;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.removeRecursively = function() {
    +  var msg = 'removing ' + this.getFullPath() + ' recursively';
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    var d = goog.async.Deferred.succeed(null);
    +    goog.object.forEach(this.children, function(child) {
    +      d.awaitDeferred(
    +          child.isDirectory() ? child.removeRecursively() : child.remove());
    +    });
    +    d.addCallback(function() { return this.remove(); }, this);
    +    return d;
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.listDirectory = function() {
    +  var msg = 'listing ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    return goog.object.getValues(this.children);
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.DirectoryEntry.prototype.createPath =
    +    // This isn't really type-safe.
    +    /** @type {!Function} */ (goog.fs.DirectoryEntryImpl.prototype.createPath);
    +
    +
    +
    +/**
    + * A mock file entry object.
    + *
    + * @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
    + * @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly
    + *     containing this entry.
    + * @param {string} name The name of this entry.
    + * @param {string} data The data initially contained in the file.
    + * @param {string=} opt_type The mime type of the blob.
    + * @constructor
    + * @extends {goog.testing.fs.Entry}
    + * @implements {goog.fs.FileEntry}
    + * @final
    + */
    +goog.testing.fs.FileEntry = function(fs, parent, name, data, opt_type) {
    +  goog.testing.fs.FileEntry.base(this, 'constructor', fs, parent, name);
    +
    +  /**
    +   * The internal file blob referenced by this file entry.
    +   * @type {!goog.testing.fs.File}
    +   * @private
    +   */
    +  this.file_ =
    +      new goog.testing.fs.File(name, new Date(goog.now()), data, opt_type);
    +
    +  /**
    +   * The metadata for file.
    +   * @type {{modificationTime: Date}}
    +   * @private
    +   */
    +  this.metadata_ = {
    +    'modificationTime': this.file_.lastModifiedDate
    +  };
    +};
    +goog.inherits(goog.testing.fs.FileEntry, goog.testing.fs.Entry);
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.isFile = function() {
    +  return true;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.isDirectory = function() {
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.clone = function() {
    +  return new goog.testing.fs.FileEntry(
    +      this.getFileSystem(), this.parent,
    +      this.getName(), this.fileSync().toString());
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.getLastModified = function() {
    +  return this.file().addCallback(function(file) {
    +    return file.lastModifiedDate;
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.getMetadata = function() {
    +  var msg = 'getting metadata for ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    return this.metadata_;
    +  });
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.createWriter = function() {
    +  var d = new goog.async.Deferred();
    +  goog.Timer.callOnce(
    +      goog.bind(d.callback, d, new goog.testing.fs.FileWriter(this)));
    +  return d;
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileEntry.prototype.file = function() {
    +  var msg = 'getting file for ' + this.getFullPath();
    +  return this.checkNotDeleted(msg).addCallback(function() {
    +    return this.fileSync();
    +  });
    +};
    +
    +
    +/**
    + * Get the internal file representation synchronously, without waiting for a
    + * Deferred to resolve.
    + *
    + * @return {!goog.testing.fs.File} The internal file blob referenced by this
    + *     FileEntry.
    + */
    +goog.testing.fs.FileEntry.prototype.fileSync = function() {
    +  return this.file_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.html
    new file mode 100644
    index 0000000000..734cf1f4e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.Entry
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.EntryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.js
    new file mode 100644
    index 0000000000..3f3d3eff93
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/entry_test.js
    @@ -0,0 +1,222 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.EntryTest');
    +goog.setTestOnly('goog.testing.fs.EntryTest');
    +
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var fs, file, mockClock;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  fs = new goog.testing.fs.FileSystem();
    +  file = fs.getRoot().
    +      getDirectorySync('foo', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      getFileSync('bar', goog.fs.DirectoryEntry.Behavior.CREATE);
    +}
    +
    +function tearDown() {
    +  mockClock.uninstall();
    +}
    +
    +function testGetName() {
    +  assertEquals('bar', file.getName());
    +}
    +
    +function testGetFullPath() {
    +  assertEquals('/foo/bar', file.getFullPath());
    +  assertEquals('/', fs.getRoot().getFullPath());
    +}
    +
    +function testGetFileSystem() {
    +  assertEquals(fs, file.getFileSystem());
    +}
    +
    +function testMoveTo() {
    +  file.moveTo(fs.getRoot()).addCallback(function(newFile) {
    +    assertTrue(file.deleted);
    +    assertFalse(newFile.deleted);
    +    assertEquals('/bar', newFile.getFullPath());
    +    assertEquals(fs.getRoot(), newFile.parent);
    +    assertEquals(newFile, fs.getRoot().getFileSync('bar'));
    +    assertFalse(fs.getRoot().getDirectorySync('foo').hasChild('bar'));
    +
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('waiting for file move');
    +}
    +
    +function testMoveToNewName() {
    +  // Advance the clock to an arbitrary, known time.
    +  mockClock.tick(71);
    +  file.moveTo(fs.getRoot(), 'baz').
    +      addCallback(function(newFile) {
    +        mockClock.tick();
    +        assertTrue(file.deleted);
    +        assertFalse(newFile.deleted);
    +        assertEquals('/baz', newFile.getFullPath());
    +        assertEquals(fs.getRoot(), newFile.parent);
    +        assertEquals(newFile, fs.getRoot().getFileSync('baz'));
    +
    +        var oldParentDir = fs.getRoot().getDirectorySync('foo');
    +        assertFalse(oldParentDir.hasChild('bar'));
    +        assertFalse(oldParentDir.hasChild('baz'));
    +
    +        return oldParentDir.getLastModified();
    +      }).
    +      addCallback(function(lastModifiedDate) {
    +        assertEquals(71, lastModifiedDate.getTime());
    +        var oldParentDir = fs.getRoot().getDirectorySync('foo');
    +        return oldParentDir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals(71, metadata.modificationTime.getTime());
    +        return fs.getRoot().getLastModified();
    +      }).
    +      addCallback(function(rootLastModifiedDate) {
    +        assertEquals(71, rootLastModifiedDate.getTime());
    +        return fs.getRoot().getMetadata();
    +      }).
    +      addCallback(function(rootMetadata) {
    +        assertEquals(71, rootMetadata.modificationTime.getTime());
    +        asyncTestCase.continueTesting();
    +      });
    +  waitForAsync('waiting for file move');
    +}
    +
    +function testMoveDeletedFile() {
    +  assertFailsWhenDeleted(function() { return file.moveTo(fs.getRoot()); });
    +}
    +
    +function testCopyTo() {
    +  mockClock.tick(61);
    +  file.copyTo(fs.getRoot()).
    +      addCallback(function(newFile) {
    +        assertFalse(file.deleted);
    +        assertFalse(newFile.deleted);
    +        assertEquals('/bar', newFile.getFullPath());
    +        assertEquals(fs.getRoot(), newFile.parent);
    +        assertEquals(newFile, fs.getRoot().getFileSync('bar'));
    +
    +        var oldParentDir = fs.getRoot().getDirectorySync('foo');
    +        assertEquals(file, oldParentDir.getFileSync('bar'));
    +        return oldParentDir.getLastModified();
    +      }).
    +      addCallback(function(lastModifiedDate) {
    +        assertEquals('The original parent directory was not modified.',
    +                     0, lastModifiedDate.getTime());
    +        var oldParentDir = fs.getRoot().getDirectorySync('foo');
    +        return oldParentDir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals('The original parent directory was not modified.',
    +                     0, metadata.modificationTime.getTime());
    +        return fs.getRoot().getLastModified();
    +      }).
    +      addCallback(function(rootLastModifiedDate) {
    +        assertEquals(61, rootLastModifiedDate.getTime());
    +        return fs.getRoot().getMetadata();
    +      }).
    +      addCallback(function(rootMetadata) {
    +        assertEquals(61, rootMetadata.modificationTime.getTime());
    +        asyncTestCase.continueTesting();
    +      });
    +  waitForAsync('waiting for file copy');
    +}
    +
    +function testCopyToNewName() {
    +  file.copyTo(fs.getRoot(), 'baz').addCallback(function(newFile) {
    +    assertFalse(file.deleted);
    +    assertFalse(newFile.deleted);
    +    assertEquals('/baz', newFile.getFullPath());
    +    assertEquals(fs.getRoot(), newFile.parent);
    +    assertEquals(newFile, fs.getRoot().getFileSync('baz'));
    +    assertEquals(file, fs.getRoot().getDirectorySync('foo').getFileSync('bar'));
    +    assertFalse(fs.getRoot().getDirectorySync('foo').hasChild('baz'));
    +
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('waiting for file copy');
    +}
    +
    +function testCopyDeletedFile() {
    +  assertFailsWhenDeleted(function() { return file.copyTo(fs.getRoot()); });
    +}
    +
    +function testRemove() {
    +  mockClock.tick(57);
    +  file.remove().
    +      addCallback(function() {
    +        mockClock.tick();
    +        var parentDir = fs.getRoot().getDirectorySync('foo');
    +
    +        assertTrue(file.deleted);
    +        assertFalse(parentDir.hasChild('bar'));
    +
    +        return parentDir.getLastModified();
    +      }).
    +      addCallback(function(date) {
    +        assertEquals(57, date.getTime());
    +        var parentDir = fs.getRoot().getDirectorySync('foo');
    +        return parentDir.getMetadata();
    +      }).
    +      addCallback(function(metadata) {
    +        assertEquals(57, metadata.modificationTime.getTime());
    +        asyncTestCase.continueTesting();
    +      });
    +  waitForAsync('waiting for file removal');
    +}
    +
    +function testRemoveDeletedFile() {
    +  assertFailsWhenDeleted(function() { return file.remove(); });
    +}
    +
    +function testGetParent() {
    +  file.getParent().addCallback(function(p) {
    +    assertEquals(file.parent, p);
    +    assertEquals(fs.getRoot().getDirectorySync('foo'), p);
    +    assertEquals('/foo', p.getFullPath());
    +
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('waiting for file parent');
    +}
    +
    +function testGetDeletedFileParent() {
    +  assertFailsWhenDeleted(function() { return file.getParent(); });
    +}
    +
    +
    +function assertFailsWhenDeleted(fn) {
    +  file.remove().addCallback(fn).
    +      addCallback(function() { fail('Expected an error'); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.NOT_FOUND, err.code);
    +        asyncTestCase.continueTesting();
    +      });
    +  waitForAsync('waiting for file operation');
    +}
    +
    +function waitForAsync(msg) {
    +  asyncTestCase.waitForAsync(msg);
    +  mockClock.tick();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/file.js b/mapbox-gl/closure-library/closure/goog/testing/fs/file.js
    new file mode 100644
    index 0000000000..aa72e66caa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/file.js
    @@ -0,0 +1,53 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock file object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.File');
    +
    +goog.require('goog.testing.fs.Blob');
    +
    +
    +
    +/**
    + * A mock file object.
    + *
    + * @param {string} name The name of the file.
    + * @param {Date=} opt_lastModified The last modified date for this file. May be
    + *     null if file modification dates are not supported.
    + * @param {string=} opt_data The string data encapsulated by the blob.
    + * @param {string=} opt_type The mime type of the blob.
    + * @constructor
    + * @extends {goog.testing.fs.Blob}
    + * @final
    + */
    +goog.testing.fs.File = function(name, opt_lastModified, opt_data, opt_type) {
    +  goog.testing.fs.File.base(this, 'constructor', opt_data, opt_type);
    +
    +  /**
    +   * @see http://www.w3.org/TR/FileAPI/#dfn-name
    +   * @type {string}
    +   */
    +  this.name = name;
    +
    +  /**
    +   * @see http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate
    +   * @type {Date}
    +   */
    +  this.lastModifiedDate = opt_lastModified || null;
    +};
    +goog.inherits(goog.testing.fs.File, goog.testing.fs.Blob);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.html
    new file mode 100644
    index 0000000000..574ef466a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.FileEntry
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.FileEntryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.js
    new file mode 100644
    index 0000000000..9e450f1e12
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/fileentry_test.js
    @@ -0,0 +1,88 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.FileEntryTest');
    +goog.setTestOnly('goog.testing.fs.FileEntryTest');
    +
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.fs.FileEntry');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var fs, file, fileEntry, mockClock, currentTime;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  fs = new goog.testing.fs.FileSystem();
    +  fileEntry = fs.getRoot().createDirectorySync('foo').createFileSync('bar');
    +}
    +
    +function tearDown() {
    +  mockClock.uninstall();
    +}
    +
    +function testIsFile() {
    +  assertTrue(fileEntry.isFile());
    +}
    +
    +function testIsDirectory() {
    +  assertFalse(fileEntry.isDirectory());
    +}
    +
    +function testFile() {
    +  var testFile = new goog.testing.fs.FileEntry(fs, fs.getRoot(),
    +                                               'test', 'hello world');
    +  testFile.file().addCallback(function(f) {
    +    assertEquals('test', f.name);
    +    assertEquals('hello world', f.toString());
    +
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('testFile');
    +}
    +
    +function testGetLastModified() {
    +  // Advance the clock to a known time.
    +  mockClock.tick(53);
    +  var testFile = new goog.testing.fs.FileEntry(fs, fs.getRoot(),
    +                                               'timeTest', 'hello world');
    +  mockClock.tick();
    +  testFile.getLastModified().addCallback(function(date) {
    +    assertEquals(53, date.getTime());
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('testGetLastModified');
    +}
    +
    +function testGetMetadata() {
    +  // Advance the clock to a known time.
    +  mockClock.tick(54);
    +  var testFile = new goog.testing.fs.FileEntry(fs, fs.getRoot(),
    +                                               'timeTest', 'hello world');
    +  mockClock.tick();
    +  testFile.getMetadata().addCallback(function(metadata) {
    +    assertEquals(54, metadata.modificationTime.getTime());
    +    asyncTestCase.continueTesting();
    +  });
    +  waitForAsync('testGetMetadata');
    +}
    +
    +
    +function waitForAsync(msg) {
    +  asyncTestCase.waitForAsync(msg);
    +  mockClock.tick();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filereader.js b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader.js
    new file mode 100644
    index 0000000000..4fb965d4f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader.js
    @@ -0,0 +1,275 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock FileReader object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.FileReader');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileReader');
    +goog.require('goog.testing.fs.ProgressEvent');
    +
    +
    +
    +/**
    + * A mock FileReader object. This emits the same events as
    + * {@link goog.fs.FileReader}.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.testing.fs.FileReader = function() {
    +  goog.testing.fs.FileReader.base(this, 'constructor');
    +
    +  /**
    +   * The current state of the reader.
    +   * @type {goog.fs.FileReader.ReadyState}
    +   * @private
    +   */
    +  this.readyState_ = goog.fs.FileReader.ReadyState.INIT;
    +};
    +goog.inherits(goog.testing.fs.FileReader, goog.events.EventTarget);
    +
    +
    +/**
    + * The most recent error experienced by this reader.
    + * @type {goog.fs.Error}
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.error_;
    +
    +
    +/**
    + * Whether the current operation has been aborted.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.aborted_ = false;
    +
    +
    +/**
    + * The blob this reader is reading from.
    + * @type {goog.testing.fs.Blob}
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.blob_;
    +
    +
    +/**
    + * The possible return types.
    + * @enum {number}
    + */
    +goog.testing.fs.FileReader.ReturnType = {
    +  /**
    +   * Used when reading as text.
    +   */
    +  TEXT: 1,
    +
    +  /**
    +   * Used when reading as binary string.
    +   */
    +  BINARY_STRING: 2,
    +
    +  /**
    +   * Used when reading as array buffer.
    +   */
    +  ARRAY_BUFFER: 3,
    +
    +  /**
    +   * Used when reading as data URL.
    +   */
    +  DATA_URL: 4
    +};
    +
    +
    +/**
    + * The return type we're reading.
    + * @type {goog.testing.fs.FileReader.ReturnType}
    + * @private
    + */
    +goog.testing.fs.FileReader.returnType_;
    +
    +
    +/**
    + * @see {goog.fs.FileReader#getReadyState}
    + * @return {goog.fs.FileReader.ReadyState} The current ready state.
    + */
    +goog.testing.fs.FileReader.prototype.getReadyState = function() {
    +  return this.readyState_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#getError}
    + * @return {goog.fs.Error} The current error.
    + */
    +goog.testing.fs.FileReader.prototype.getError = function() {
    +  return this.error_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#abort}
    + */
    +goog.testing.fs.FileReader.prototype.abort = function() {
    +  if (this.readyState_ != goog.fs.FileReader.ReadyState.LOADING) {
    +    var msg = 'aborting read';
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  this.aborted_ = true;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#getResult}
    + * @return {*} The result of the file read.
    + */
    +goog.testing.fs.FileReader.prototype.getResult = function() {
    +  if (this.readyState_ != goog.fs.FileReader.ReadyState.DONE) {
    +    return undefined;
    +  }
    +  if (this.error_) {
    +    return undefined;
    +  }
    +  if (this.returnType_ == goog.testing.fs.FileReader.ReturnType.TEXT) {
    +    return this.blob_.toString();
    +  } else if (this.returnType_ ==
    +      goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER) {
    +    return this.blob_.toArrayBuffer();
    +  } else if (this.returnType_ ==
    +      goog.testing.fs.FileReader.ReturnType.BINARY_STRING) {
    +    return this.blob_.toString();
    +  } else if (this.returnType_ ==
    +      goog.testing.fs.FileReader.ReturnType.DATA_URL) {
    +    return this.blob_.toDataUrl();
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * Fires the read events.
    + * @param {!goog.testing.fs.Blob} blob The blob to read from.
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.read_ = function(blob) {
    +  this.blob_ = blob;
    +  if (this.readyState_ == goog.fs.FileReader.ReadyState.LOADING) {
    +    var msg = 'reading file';
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  this.readyState_ = goog.fs.FileReader.ReadyState.LOADING;
    +  goog.Timer.callOnce(function() {
    +    if (this.aborted_) {
    +      this.abort_(blob.size);
    +      return;
    +    }
    +
    +    this.progressEvent_(goog.fs.FileReader.EventType.LOAD_START, 0, blob.size);
    +    this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size / 2,
    +        blob.size);
    +    this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size,
    +        blob.size);
    +    this.readyState_ = goog.fs.FileReader.ReadyState.DONE;
    +    this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size,
    +        blob.size);
    +    this.progressEvent_(goog.fs.FileReader.EventType.LOAD_END, blob.size,
    +        blob.size);
    +  }, 0, this);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#readAsBinaryString}
    + * @param {!goog.testing.fs.Blob} blob The blob to read.
    + */
    +goog.testing.fs.FileReader.prototype.readAsBinaryString = function(blob) {
    +  this.returnType_ = goog.testing.fs.FileReader.ReturnType.BINARY_STRING;
    +  this.read_(blob);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#readAsArrayBuffer}
    + * @param {!goog.testing.fs.Blob} blob The blob to read.
    + */
    +goog.testing.fs.FileReader.prototype.readAsArrayBuffer = function(blob) {
    +  this.returnType_ = goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER;
    +  this.read_(blob);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#readAsText}
    + * @param {!goog.testing.fs.Blob} blob The blob to read.
    + * @param {string=} opt_encoding The name of the encoding to use.
    + */
    +goog.testing.fs.FileReader.prototype.readAsText = function(blob, opt_encoding) {
    +  this.returnType_ = goog.testing.fs.FileReader.ReturnType.TEXT;
    +  this.read_(blob);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileReader#readAsDataUrl}
    + * @param {!goog.testing.fs.Blob} blob The blob to read.
    + */
    +goog.testing.fs.FileReader.prototype.readAsDataUrl = function(blob) {
    +  this.returnType_ = goog.testing.fs.FileReader.ReturnType.DATA_URL;
    +  this.read_(blob);
    +};
    +
    +
    +/**
    + * Abort the current action and emit appropriate events.
    + *
    + * @param {number} total The total data that was to be processed, in bytes.
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.abort_ = function(total) {
    +  this.error_ = new goog.fs.Error(
    +      /** @type {!FileError} */ ({'name': 'AbortError'}),
    +      'reading file');
    +  this.progressEvent_(goog.fs.FileReader.EventType.ERROR, 0, total);
    +  this.progressEvent_(goog.fs.FileReader.EventType.ABORT, 0, total);
    +  this.readyState_ = goog.fs.FileReader.ReadyState.DONE;
    +  this.progressEvent_(goog.fs.FileReader.EventType.LOAD_END, 0, total);
    +  this.aborted_ = false;
    +};
    +
    +
    +/**
    + * Dispatch a progress event.
    + *
    + * @param {goog.fs.FileReader.EventType} type The event type.
    + * @param {number} loaded The number of bytes processed.
    + * @param {number} total The total data that was to be processed, in bytes.
    + * @private
    + */
    +goog.testing.fs.FileReader.prototype.progressEvent_ = function(type, loaded,
    +    total) {
    +  this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.html
    new file mode 100644
    index 0000000000..18dfb21d99
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.FileReader
    +  </title>
    +  <script type="text/javascript" src="../../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.testing.fs.FileReaderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.js
    new file mode 100644
    index 0000000000..b2e056b971
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filereader_test.js
    @@ -0,0 +1,234 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.FileReaderTest');
    +goog.setTestOnly('goog.testing.fs.FileReaderTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.events');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileReader');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.fs.FileReader');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var file, deferredReader;
    +var hasArrayBuffer = goog.isDef(goog.global.ArrayBuffer);
    +
    +function setUp() {
    +  var fs = new goog.testing.fs.FileSystem();
    +  var fileEntry = fs.getRoot().createDirectorySync('foo').createFileSync('bar');
    +  file = fileEntry.fileSync();
    +  file.setDataInternal('test content');
    +
    +  deferredReader = new goog.async.Deferred();
    +  goog.Timer.callOnce(
    +      goog.bind(deferredReader.callback, deferredReader,
    +          new goog.testing.fs.FileReader()));
    +}
    +
    +function testRead() {
    +  deferredReader.
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.INIT)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(readAsText)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_START)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_END)).
    +      addCallback(goog.partial(checkResult, file.toString())).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.DONE)).
    +      addBoth(continueTesting);
    +  waitForAsync('testRead');
    +}
    +
    +function testReadAsArrayBuffer() {
    +  if (!hasArrayBuffer) {
    +    // Skip if array buffer is not supported
    +    return;
    +  }
    +  deferredReader.
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.INIT)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(readAsArrayBuffer)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_START)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_END)).
    +      addCallback(goog.partial(checkResult, file.toArrayBuffer())).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.DONE)).
    +      addBoth(continueTesting);
    +  waitForAsync('testReadAsArrayBuffer');
    +}
    +
    +function testReadAsDataUrl() {
    +  deferredReader.
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.INIT)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(readAsDataUrl)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_START)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_END)).
    +      addCallback(goog.partial(checkResult, file.toDataUrl())).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.DONE)).
    +      addBoth(continueTesting);
    +  waitForAsync('testReadAsDataUrl');
    +}
    +
    +function testAbort() {
    +  deferredReader.
    +      addCallback(goog.partial(readAsText)).
    +      addCallback(function(reader) { reader.abort(); }).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(waitForError, goog.fs.Error.ErrorCode.ABORT)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.ABORT)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.LOADING)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileReader.EventType.LOAD_END)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileReader.ReadyState.DONE)).
    +      addCallback(goog.partial(checkResult, undefined)).
    +      addBoth(continueTesting);
    +  waitForAsync('testAbort');
    +}
    +
    +function testAbortBeforeRead() {
    +  deferredReader.
    +      addCallback(function(reader) { reader.abort(); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(function(calledErrback) {
    +        assertTrue(calledErrback);
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testAbortBeforeRead');
    +}
    +
    +function testReadDuringRead() {
    +  deferredReader.
    +      addCallback(goog.partial(readAsText)).
    +      addCallback(goog.partial(readAsText)).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(assertTrue).
    +      addBoth(continueTesting);
    +  waitForAsync('testReadDuringRead');
    +}
    +
    +function continueTesting(result) {
    +  asyncTestCase.continueTesting();
    +  if (result instanceof Error) {
    +    throw result;
    +  }
    +}
    +
    +function waitForAsync(msg) {
    +  asyncTestCase.waitForAsync(msg);
    +}
    +
    +function waitForEvent(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(target, type, goog.bind(d.callback, d, target));
    +  return d;
    +}
    +
    +function waitForError(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(
    +      target, goog.fs.FileReader.EventType.ERROR, function(e) {
    +        assertEquals(type, target.getError().code);
    +        d.callback(target);
    +      });
    +  return d;
    +}
    +
    +function readAsText(reader) {
    +  reader.readAsText(file);
    +}
    +
    +function readAsArrayBuffer(reader) {
    +  reader.readAsArrayBuffer(file);
    +}
    +
    +function readAsDataUrl(reader) {
    +  reader.readAsDataUrl(file);
    +}
    +
    +function readAndWait(reader) {
    +  readAsText(reader);
    +  return waitForEvent(goog.fs.FileSaver.EventType.LOAD_END, reader);
    +}
    +
    +function checkResult(expectedResult, reader) {
    +  checkEquals(expectedResult, reader.getResult());
    +}
    +
    +function checkEquals(a, b) {
    +  if (hasArrayBuffer &&
    +      a instanceof ArrayBuffer && b instanceof ArrayBuffer) {
    +    assertEquals(a.byteLength, b.byteLength);
    +    var viewA = new Uint8Array(a);
    +    var viewB = new Uint8Array(b);
    +    for (var i = 0; i < a.byteLength; i++) {
    +      assertEquals(viewA[i], viewB[i]);
    +    }
    +  } else {
    +    assertEquals(a, b);
    +  }
    +}
    +
    +function checkReadyState(expectedState, reader) {
    +  assertEquals(expectedState, reader.getReadyState());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filesystem.js b/mapbox-gl/closure-library/closure/goog/testing/fs/filesystem.js
    new file mode 100644
    index 0000000000..f094cc1306
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filesystem.js
    @@ -0,0 +1,64 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock filesystem object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.FileSystem');
    +
    +goog.require('goog.fs.FileSystem');
    +goog.require('goog.testing.fs.DirectoryEntry');
    +
    +
    +
    +/**
    + * A mock filesystem object.
    + *
    + * @param {string=} opt_name The name of the filesystem.
    + * @constructor
    + * @implements {goog.fs.FileSystem}
    + * @final
    + */
    +goog.testing.fs.FileSystem = function(opt_name) {
    +  /**
    +   * The name of the filesystem.
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = opt_name || 'goog.testing.fs.FileSystem';
    +
    +  /**
    +   * The root entry of the filesystem.
    +   * @type {!goog.testing.fs.DirectoryEntry}
    +   * @private
    +   */
    +  this.root_ = new goog.testing.fs.DirectoryEntry(this, null, '', {});
    +};
    +
    +
    +/** @override */
    +goog.testing.fs.FileSystem.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * @override
    + * @return {!goog.testing.fs.DirectoryEntry}
    + */
    +goog.testing.fs.FileSystem.prototype.getRoot = function() {
    +  return this.root_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter.js b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter.js
    new file mode 100644
    index 0000000000..aad8a62e89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter.js
    @@ -0,0 +1,268 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock FileWriter object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.FileWriter');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.string');
    +goog.require('goog.testing.fs.ProgressEvent');
    +
    +
    +
    +/**
    + * A mock FileWriter object. This emits the same events as
    + * {@link goog.fs.FileSaver} and {@link goog.fs.FileWriter}.
    + *
    + * @param {!goog.testing.fs.FileEntry} fileEntry The file entry to write to.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.testing.fs.FileWriter = function(fileEntry) {
    +  goog.testing.fs.FileWriter.base(this, 'constructor');
    +
    +  /**
    +   * The file entry to which to write.
    +   * @type {!goog.testing.fs.FileEntry}
    +   * @private
    +   */
    +  this.fileEntry_ = fileEntry;
    +
    +  /**
    +   * The file blob to write to.
    +   * @type {!goog.testing.fs.File}
    +   * @private
    +   */
    +  this.file_ = fileEntry.fileSync();
    +
    +  /**
    +   * The current state of the writer.
    +   * @type {goog.fs.FileSaver.ReadyState}
    +   * @private
    +   */
    +  this.readyState_ = goog.fs.FileSaver.ReadyState.INIT;
    +};
    +goog.inherits(goog.testing.fs.FileWriter, goog.events.EventTarget);
    +
    +
    +/**
    + * The most recent error experienced by this writer.
    + * @type {goog.fs.Error}
    + * @private
    + */
    +goog.testing.fs.FileWriter.prototype.error_;
    +
    +
    +/**
    + * Whether the current operation has been aborted.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.fs.FileWriter.prototype.aborted_ = false;
    +
    +
    +/**
    + * The current position in the file.
    + * @type {number}
    + * @private
    + */
    +goog.testing.fs.FileWriter.prototype.position_ = 0;
    +
    +
    +/**
    + * @see {goog.fs.FileSaver#getReadyState}
    + * @return {goog.fs.FileSaver.ReadyState} The ready state.
    + */
    +goog.testing.fs.FileWriter.prototype.getReadyState = function() {
    +  return this.readyState_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileSaver#getError}
    + * @return {goog.fs.Error} The error.
    + */
    +goog.testing.fs.FileWriter.prototype.getError = function() {
    +  return this.error_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileWriter#getPosition}
    + * @return {number} The position.
    + */
    +goog.testing.fs.FileWriter.prototype.getPosition = function() {
    +  return this.position_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileWriter#getLength}
    + * @return {number} The length.
    + */
    +goog.testing.fs.FileWriter.prototype.getLength = function() {
    +  return this.file_.size;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileSaver#abort}
    + */
    +goog.testing.fs.FileWriter.prototype.abort = function() {
    +  if (this.readyState_ != goog.fs.FileSaver.ReadyState.WRITING) {
    +    var msg = 'aborting save of ' + this.fileEntry_.getFullPath();
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  this.aborted_ = true;
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileWriter#write}
    + * @param {!goog.testing.fs.Blob} blob The blob to write.
    + */
    +goog.testing.fs.FileWriter.prototype.write = function(blob) {
    +  if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
    +    var msg = 'writing to ' + this.fileEntry_.getFullPath();
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING;
    +  goog.Timer.callOnce(function() {
    +    if (this.aborted_) {
    +      this.abort_(blob.size);
    +      return;
    +    }
    +
    +    this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, blob.size);
    +    var fileString = this.file_.toString();
    +    this.file_.setDataInternal(
    +        fileString.substring(0, this.position_) + blob.toString() +
    +        fileString.substring(this.position_ + blob.size, fileString.length));
    +    this.position_ += blob.size;
    +
    +    this.progressEvent_(
    +        goog.fs.FileSaver.EventType.WRITE, blob.size, blob.size);
    +    this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
    +    this.progressEvent_(
    +        goog.fs.FileSaver.EventType.WRITE_END, blob.size, blob.size);
    +  }, 0, this);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileWriter#truncate}
    + * @param {number} size The size to truncate to.
    + */
    +goog.testing.fs.FileWriter.prototype.truncate = function(size) {
    +  if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
    +    var msg = 'truncating ' + this.fileEntry_.getFullPath();
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({'name': 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING;
    +  goog.Timer.callOnce(function() {
    +    if (this.aborted_) {
    +      this.abort_(size);
    +      return;
    +    }
    +
    +    this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, size);
    +
    +    var fileString = this.file_.toString();
    +    if (size > fileString.length) {
    +      this.file_.setDataInternal(
    +          fileString + goog.string.repeat('\0', size - fileString.length));
    +    } else {
    +      this.file_.setDataInternal(fileString.substring(0, size));
    +    }
    +    this.position_ = Math.min(this.position_, size);
    +
    +    this.progressEvent_(goog.fs.FileSaver.EventType.WRITE, size, size);
    +    this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
    +    this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, size, size);
    +  }, 0, this);
    +};
    +
    +
    +/**
    + * @see {goog.fs.FileWriter#seek}
    + * @param {number} offset The offset to seek to.
    + */
    +goog.testing.fs.FileWriter.prototype.seek = function(offset) {
    +  if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
    +    var msg = 'truncating ' + this.fileEntry_.getFullPath();
    +    throw new goog.fs.Error(
    +        /** @type {!FileError} */ ({name: 'InvalidStateError'}),
    +        msg);
    +  }
    +
    +  if (offset < 0) {
    +    this.position_ = Math.max(0, this.file_.size + offset);
    +  } else {
    +    this.position_ = Math.min(offset, this.file_.size);
    +  }
    +};
    +
    +
    +/**
    + * Abort the current action and emit appropriate events.
    + *
    + * @param {number} total The total data that was to be processed, in bytes.
    + * @private
    + */
    +goog.testing.fs.FileWriter.prototype.abort_ = function(total) {
    +  this.error_ = new goog.fs.Error(
    +      /** @type {!FileError} */ ({'name': 'AbortError'}),
    +      'saving ' + this.fileEntry_.getFullPath());
    +  this.progressEvent_(goog.fs.FileSaver.EventType.ERROR, 0, total);
    +  this.progressEvent_(goog.fs.FileSaver.EventType.ABORT, 0, total);
    +  this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
    +  this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, 0, total);
    +  this.aborted_ = false;
    +};
    +
    +
    +/**
    + * Dispatch a progress event.
    + *
    + * @param {goog.fs.FileSaver.EventType} type The type of the event.
    + * @param {number} loaded The number of bytes processed.
    + * @param {number} total The total data that was to be processed, in bytes.
    + * @private
    + */
    +goog.testing.fs.FileWriter.prototype.progressEvent_ = function(
    +    type, loaded, total) {
    +  // On write, update the last modified date to the current (real or mock) time.
    +  if (type == goog.fs.FileSaver.EventType.WRITE) {
    +    this.file_.lastModifiedDate = new Date(goog.now());
    +  }
    +
    +  this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.html
    new file mode 100644
    index 0000000000..6aaf582a8c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs.FileWriter
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.FileWriterTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.js
    new file mode 100644
    index 0000000000..143fac94fc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/filewriter_test.js
    @@ -0,0 +1,322 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.FileWriterTest');
    +goog.setTestOnly('goog.testing.fs.FileWriterTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.events');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.fs.Blob');
    +goog.require('goog.testing.fs.FileSystem');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var file, deferredWriter, mockClock;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  var fs = new goog.testing.fs.FileSystem();
    +  var fileEntry = fs.getRoot().createDirectorySync('foo').createFileSync('bar');
    +
    +  deferredWriter = fileEntry.createWriter();
    +  file = fileEntry.fileSync();
    +  file.setDataInternal('');
    +}
    +
    +function tearDown() {
    +  mockClock.uninstall();
    +}
    +
    +function testWrite() {
    +  deferredWriter.
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(goog.partial(checkPositionAndLength, 0, 0)).
    +      addCallback(goog.partial(checkLastModified, 0)).
    +      addCallback(goog.partial(tick, 3)).
    +      addCallback(goog.partial(writeString, 'hello')).
    +      addCallback(goog.partial(checkPositionAndLength, 0, 0)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(checkLastModified, 0)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_START)).
    +      addCallback(goog.partial(checkLastModified, 0)).
    +      addCallback(goog.partial(checkPositionAndLength, 0, 0)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE)).
    +      addCallback(function() { assertEquals('hello', file.toString()); }).
    +      addCallback(goog.partial(checkPositionAndLength, 5, 5)).
    +      addCallback(goog.partial(checkLastModified, 3)).
    +      addCallback(tick).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(checkLastModified, 3)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_END)).
    +      addCallback(goog.partial(checkLastModified, 3)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.DONE)).
    +      addCallback(goog.partial(checkLastModified, 3)).
    +      addCallback(goog.partial(writeString, ' world')).
    +      addCallback(goog.partial(checkPositionAndLength, 5, 5)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE)).
    +      addCallback(function() { assertEquals('hello world', file.toString()); }).
    +      addCallback(goog.partial(checkPositionAndLength, 11, 11)).
    +      addCallback(goog.partial(checkLastModified, 4)).
    +      addBoth(continueTesting);
    +  waitForAsync('testWrite');
    +}
    +
    +function testSeek() {
    +  deferredWriter.
    +      addCallback(goog.partial(tick, 17)).
    +      addCallback(goog.partial(writeAndWait, 'hello world')).
    +      addCallback(tick).
    +      addCallback(goog.partial(checkPositionAndLength, 11, 11)).
    +
    +      addCallback(function(writer) { writer.seek(6); }).
    +      addCallback(goog.partial(checkPositionAndLength, 6, 11)).
    +      addCallback(goog.partial(checkLastModified, 17)).
    +      addCallback(goog.partial(writeAndWait, 'universe')).
    +      addCallback(tick).
    +      addCallback(function() {
    +        assertEquals('hello universe', file.toString());
    +      }).
    +      addCallback(goog.partial(checkPositionAndLength, 14, 14)).
    +
    +      addCallback(function(writer) { writer.seek(500); }).
    +      addCallback(goog.partial(checkPositionAndLength, 14, 14)).
    +      addCallback(goog.partial(writeAndWait, '!')).
    +      addCallback(tick).
    +      addCallback(function() {
    +        assertEquals('hello universe!', file.toString());
    +      }).
    +      addCallback(goog.partial(checkPositionAndLength, 15, 15)).
    +
    +      addCallback(function(writer) { writer.seek(-9); }).
    +      addCallback(goog.partial(checkPositionAndLength, 6, 15)).
    +      addCallback(goog.partial(writeAndWait, 'foo')).
    +      addCallback(tick).
    +      addCallback(function() {
    +        assertEquals('hello fooverse!', file.toString());
    +      }).
    +      addCallback(goog.partial(checkPositionAndLength, 9, 15)).
    +
    +      addCallback(function(writer) { writer.seek(-500); }).
    +      addCallback(goog.partial(checkPositionAndLength, 0, 15)).
    +      addCallback(goog.partial(writeAndWait, 'bye-o')).
    +      addCallback(tick).
    +      addCallback(function() {
    +        assertEquals('bye-o fooverse!', file.toString());
    +      }).
    +      addCallback(goog.partial(checkPositionAndLength, 5, 15)).
    +      addCallback(goog.partial(checkLastModified, 21)).
    +      addBoth(continueTesting);
    +  waitForAsync('testSeek');
    +}
    +
    +function testAbort() {
    +  deferredWriter.
    +      addCallback(goog.partial(tick, 13)).
    +      addCallback(goog.partial(writeString, 'hello world')).
    +      addCallback(function(writer) { writer.abort(); }).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(waitForError, goog.fs.Error.ErrorCode.ABORT)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.ABORT)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_END)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.DONE)).
    +      addCallback(goog.partial(checkPositionAndLength, 0, 0)).
    +      addCallback(goog.partial(checkLastModified, 0)).
    +      addCallback(function() { assertEquals('', file.toString()); }).
    +      addBoth(continueTesting);
    +  waitForAsync('testAbort');
    +}
    +
    +function testTruncate() {
    +  deferredWriter.
    +      addCallback(goog.partial(writeAndWait, 'hello world')).
    +      addCallback(goog.partial(checkPositionAndLength, 11, 11)).
    +      addCallback(function(writer) { writer.truncate(5); }).
    +      addCallback(goog.partial(checkPositionAndLength, 11, 11)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_START)).
    +      addCallback(goog.partial(tick, 7)).
    +      addCallback(goog.partial(checkPositionAndLength, 11, 11)).
    +      addCallback(goog.partial(checkLastModified, 0)).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE)).
    +      addCallback(goog.partial(checkLastModified, 7)).
    +      addCallback(tick).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(goog.partial(checkPositionAndLength, 5, 5)).
    +      addCallback(function() { assertEquals('hello', file.toString()); }).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_END)).
    +      addCallback(goog.partial(checkReadyState,
    +                               goog.fs.FileSaver.ReadyState.DONE)).
    +
    +      addCallback(function(writer) { writer.truncate(10); }).
    +      addCallback(goog.partial(waitForEvent,
    +                               goog.fs.FileSaver.EventType.WRITE_END)).
    +      addCallback(goog.partial(checkPositionAndLength, 5, 10)).
    +      addCallback(goog.partial(checkLastModified, 8)).
    +      addCallback(function() {
    +        assertEquals('hello\0\0\0\0\0', file.toString());
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testTruncate');
    +}
    +
    +function testAbortBeforeWrite() {
    +  deferredWriter.
    +      addCallback(function(writer) { writer.abort(); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(function(calledErrback) {
    +        assertTrue(calledErrback);
    +      }).
    +      addBoth(continueTesting);
    +  waitForAsync('testAbortBeforeWrite');
    +}
    +
    +function testAbortAfterWrite() {
    +  deferredWriter.
    +      addCallback(goog.partial(writeAndWait, 'hello world')).
    +      addCallback(function(writer) { writer.abort(); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(assertTrue).
    +      addBoth(continueTesting);
    +  waitForAsync('testAbortAfterWrite');
    +}
    +
    +function testWriteDuringWrite() {
    +  deferredWriter.
    +      addCallback(goog.partial(writeString, 'hello world')).
    +      addCallback(goog.partial(writeString, 'hello world')).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(assertTrue).
    +      addBoth(continueTesting);
    +  waitForAsync('testWriteDuringWrite');
    +}
    +
    +function testSeekDuringWrite() {
    +  deferredWriter.
    +      addCallback(goog.partial(writeString, 'hello world')).
    +      addCallback(function(writer) { writer.seek(5); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(assertTrue).
    +      addBoth(continueTesting);
    +  waitForAsync('testSeekDuringWrite');
    +}
    +
    +function testTruncateDuringWrite() {
    +  deferredWriter.
    +      addCallback(goog.partial(writeString, 'hello world')).
    +      addCallback(function(writer) { writer.truncate(5); }).
    +      addErrback(function(err) {
    +        assertEquals(goog.fs.Error.ErrorCode.INVALID_STATE, err.code);
    +        return true;
    +      }).
    +      addCallback(assertTrue).
    +      addBoth(continueTesting);
    +  waitForAsync('testTruncateDuringWrite');
    +}
    +
    +
    +function tick(opt_tickCount) {
    +  mockClock.tick(opt_tickCount);
    +}
    +
    +function continueTesting(result) {
    +  asyncTestCase.continueTesting();
    +  if (result instanceof Error) {
    +    throw result;
    +  }
    +  mockClock.tick();
    +}
    +
    +function waitForAsync(msg) {
    +  asyncTestCase.waitForAsync(msg);
    +
    +  // The mock clock must be advanced far enough that all timeouts added during
    +  // callbacks will be triggered. 1000ms is much more than enough.
    +  mockClock.tick(1000);
    +}
    +
    +function waitForEvent(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(target, type, goog.bind(d.callback, d, target));
    +  return d;
    +}
    +
    +function waitForError(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(
    +      target, goog.fs.FileSaver.EventType.ERROR, function(e) {
    +        assertEquals(type, target.getError().code);
    +        d.callback(target);
    +      });
    +  return d;
    +}
    +
    +function checkReadyState(expectedState, writer) {
    +  assertEquals(expectedState, writer.getReadyState());
    +}
    +
    +function checkPositionAndLength(expectedPosition, expectedLength, writer) {
    +  assertEquals(expectedPosition, writer.getPosition());
    +  assertEquals(expectedLength, writer.getLength());
    +}
    +
    +function checkLastModified(expectedTime) {
    +  assertEquals(expectedTime, file.lastModifiedDate.getTime());
    +}
    +
    +function writeString(str, writer) {
    +  writer.write(new goog.testing.fs.Blob(str));
    +}
    +
    +function writeAndWait(str, writer) {
    +  writeString(str, writer);
    +  return waitForEvent(goog.fs.FileSaver.EventType.WRITE_END, writer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/fs.js b/mapbox-gl/closure-library/closure/goog/testing/fs/fs.js
    new file mode 100644
    index 0000000000..73121afe3a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/fs.js
    @@ -0,0 +1,169 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock implementations of the Closure HTML5 FileSystem wrapper
    + * classes. These implementations are designed to be usable in any browser, so
    + * they use none of the native FileSystem-related objects.
    + *
    + */
    +
    +goog.provide('goog.testing.fs');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +/** @suppress {extraRequire} */
    +goog.require('goog.fs');
    +goog.require('goog.testing.fs.Blob');
    +goog.require('goog.testing.fs.FileSystem');
    +
    +
    +/**
    + * Get a filesystem object. Since these are mocks, there's no difference between
    + * temporary and persistent filesystems.
    + *
    + * @param {number} size Ignored.
    + * @return {!goog.async.Deferred} The deferred
    + *     {@link goog.testing.fs.FileSystem}.
    + */
    +goog.testing.fs.getTemporary = function(size) {
    +  var d = new goog.async.Deferred();
    +  goog.Timer.callOnce(
    +      goog.bind(d.callback, d, new goog.testing.fs.FileSystem()));
    +  return d;
    +};
    +
    +
    +/**
    + * Get a filesystem object. Since these are mocks, there's no difference between
    + * temporary and persistent filesystems.
    + *
    + * @param {number} size Ignored.
    + * @return {!goog.async.Deferred} The deferred
    + *     {@link goog.testing.fs.FileSystem}.
    + */
    +goog.testing.fs.getPersistent = function(size) {
    +  return goog.testing.fs.getTemporary(size);
    +};
    +
    +
    +/**
    + * Which object URLs have been granted for fake blobs.
    + * @type {!Object<boolean>}
    + * @private
    + */
    +goog.testing.fs.objectUrls_ = {};
    +
    +
    +/**
    + * Create a fake object URL for a given fake blob. This can be used as a real
    + * URL, and it can be created and revoked normally.
    + *
    + * @param {!goog.testing.fs.Blob} blob The blob for which to create the URL.
    + * @return {string} The URL.
    + */
    +goog.testing.fs.createObjectUrl = function(blob) {
    +  var url = blob.toDataUrl();
    +  goog.testing.fs.objectUrls_[url] = true;
    +  return url;
    +};
    +
    +
    +/**
    + * Remove a URL that was created for a fake blob.
    + *
    + * @param {string} url The URL to revoke.
    + */
    +goog.testing.fs.revokeObjectUrl = function(url) {
    +  delete goog.testing.fs.objectUrls_[url];
    +};
    +
    +
    +/**
    + * Return whether or not a URL has been granted for the given blob.
    + *
    + * @param {!goog.testing.fs.Blob} blob The blob to check.
    + * @return {boolean} Whether a URL has been granted.
    + */
    +goog.testing.fs.isObjectUrlGranted = function(blob) {
    +  return (blob.toDataUrl()) in goog.testing.fs.objectUrls_;
    +};
    +
    +
    +/**
    + * Concatenates one or more values together and converts them to a fake blob.
    + *
    + * @param {...(string|!goog.testing.fs.Blob)} var_args The values that will make
    + *     up the resulting blob.
    + * @return {!goog.testing.fs.Blob} The blob.
    + */
    +goog.testing.fs.getBlob = function(var_args) {
    +  return new goog.testing.fs.Blob(goog.array.map(arguments, String).join(''));
    +};
    +
    +
    +/**
    + * Creates a blob with the given properties.
    + * See https://developer.mozilla.org/en-US/docs/Web/API/Blob for more details.
    + *
    + * @param {Array<string|!goog.testing.fs.Blob>} parts
    + *     The values that will make up the resulting blob.
    + * @param {string=} opt_type The MIME type of the Blob.
    + * @param {string=} opt_endings Specifies how strings containing newlines are to
    + *     be written out.
    + * @return {!goog.testing.fs.Blob} The blob.
    + */
    +goog.testing.fs.getBlobWithProperties = function(parts, opt_type, opt_endings) {
    +  return new goog.testing.fs.Blob(goog.array.map(parts, String).join(''),
    +      opt_type);
    +};
    +
    +
    +/**
    + * Returns the string value of a fake blob.
    + *
    + * @param {!goog.testing.fs.Blob} blob The blob to convert to a string.
    + * @param {string=} opt_encoding Ignored.
    + * @return {!goog.async.Deferred} The deferred string value of the blob.
    + */
    +goog.testing.fs.blobToString = function(blob, opt_encoding) {
    +  var d = new goog.async.Deferred();
    +  goog.Timer.callOnce(goog.bind(d.callback, d, blob.toString()));
    +  return d;
    +};
    +
    +
    +/**
    + * Installs goog.testing.fs in place of the standard goog.fs. After calling
    + * this, code that uses goog.fs should work without issue using goog.testing.fs.
    + *
    + * @param {!goog.testing.PropertyReplacer} stubs The property replacer for
    + *     stubbing out the original goog.fs functions.
    + */
    +goog.testing.fs.install = function(stubs) {
    +  // Prevent warnings that goog.fs may get optimized away. It's true this is
    +  // unsafe in compiled code, but it's only meant for tests.
    +  var fs = goog.getObjectByName('goog.fs');
    +  stubs.replace(fs, 'getTemporary', goog.testing.fs.getTemporary);
    +  stubs.replace(fs, 'getPersistent', goog.testing.fs.getPersistent);
    +  stubs.replace(fs, 'createObjectUrl', goog.testing.fs.createObjectUrl);
    +  stubs.replace(fs, 'revokeObjectUrl', goog.testing.fs.revokeObjectUrl);
    +  stubs.replace(fs, 'getBlob', goog.testing.fs.getBlob);
    +  stubs.replace(fs, 'getBlobWithProperties',
    +      goog.testing.fs.getBlobWithProperties);
    +  stubs.replace(fs, 'blobToString', goog.testing.fs.blobToString);
    +  stubs.replace(fs, 'browserSupportsObjectUrls',
    +      function() { return true; });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.html
    new file mode 100644
    index 0000000000..8143c2e33b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.testing.fs
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.js
    new file mode 100644
    index 0000000000..ef496f01a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/fs_test.js
    @@ -0,0 +1,55 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fsTest');
    +goog.setTestOnly('goog.testing.fsTest');
    +
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.fs');
    +goog.require('goog.testing.fs.Blob');
    +goog.require('goog.testing.jsunit');
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +function testObjectUrls() {
    +  var blob = goog.testing.fs.getBlob('foo');
    +  var url = goog.testing.fs.createObjectUrl(blob);
    +  assertTrue(goog.testing.fs.isObjectUrlGranted(blob));
    +  goog.testing.fs.revokeObjectUrl(url);
    +  assertFalse(goog.testing.fs.isObjectUrlGranted(blob));
    +}
    +
    +function testGetBlob() {
    +  assertEquals(
    +      new goog.testing.fs.Blob('foobarbaz').toString(),
    +      goog.testing.fs.getBlob('foo', 'bar', 'baz').toString());
    +  assertEquals(
    +      new goog.testing.fs.Blob('foobarbaz').toString(),
    +      goog.testing.fs.getBlob('foo', new goog.testing.fs.Blob('bar'), 'baz').
    +      toString());
    +}
    +
    +function testBlobToString() {
    +  goog.testing.fs.blobToString(new goog.testing.fs.Blob('foobarbaz')).
    +      addCallback(goog.partial(assertEquals, 'foobarbaz')).
    +      addCallback(goog.bind(asyncTestCase.continueTesting, asyncTestCase));
    +  asyncTestCase.waitForAsync('testBlobToString');
    +}
    +
    +function testGetBlobWithProperties() {
    +  assertEquals(
    +      'data:spam/eggs;base64,Zm9vYmFy',
    +      new goog.testing.fs.getBlobWithProperties(
    +          ['foo', new goog.testing.fs.Blob('bar')], 'spam/eggs').toDataUrl());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.html b/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.html
    new file mode 100644
    index 0000000000..81cb8b1e24
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <title>
    +   Closure Integration Tests - goog.testing.fs
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.fs.integrationTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="closureTestRunnerLog">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.js b/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.js
    new file mode 100644
    index 0000000000..a64566f804
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/integration_test.js
    @@ -0,0 +1,221 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.fs.integrationTest');
    +goog.setTestOnly('goog.testing.fs.integrationTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.async.DeferredList');
    +goog.require('goog.events');
    +goog.require('goog.fs');
    +goog.require('goog.fs.DirectoryEntry');
    +goog.require('goog.fs.Error');
    +goog.require('goog.fs.FileSaver');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.fs');
    +goog.require('goog.testing.jsunit');
    +
    +var TEST_DIR = 'goog-fs-test-dir';
    +
    +var deferredFs = goog.testing.fs.getTemporary();
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +
    +function setUpPage() {
    +  goog.testing.fs.install(new goog.testing.PropertyReplacer());
    +}
    +
    +function tearDown() {
    +  loadTestDir().
    +      addCallback(function(dir) { return dir.removeRecursively(); }).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('removing filesystem');
    +}
    +
    +function testWriteFile() {
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testWriteFile');
    +}
    +
    +function testRemoveFile() {
    +  loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(fileEntry) { return fileEntry.remove(); }).
    +      addCallback(goog.partial(checkFileRemoved, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testRemoveFile');
    +}
    +
    +function testMoveFile() {
    +  var deferredSubdir = loadDirectory(
    +      'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredWrittenFile =
    +      loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE).
    +      addCallback(goog.partial(writeToFile, 'test content'));
    +  goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
    +      addCallback(splitArgs(function(dir, fileEntry) {
    +        return fileEntry.moveTo(dir);
    +      })).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addCallback(goog.partial(checkFileRemoved, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testMoveFile');
    +}
    +
    +function testCopyFile() {
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredSubdir = loadDirectory(
    +      'subdir', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  var deferredWrittenFile = deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content'));
    +  goog.async.DeferredList.gatherResults([deferredSubdir, deferredWrittenFile]).
    +      addCallback(splitArgs(function(dir, fileEntry) {
    +        return fileEntry.copyTo(dir);
    +      })).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test content')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testCopyFile');
    +}
    +
    +function testAbortWrite() {
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(startWrite, 'test content')).
    +      addCallback(function(writer) { writer.abort(); }).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.ABORT)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, '')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testAbortWrite');
    +}
    +
    +function testSeek() {
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(fileEntry) { return fileEntry.createWriter(); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) {
    +        writer.seek(5);
    +        writer.write(goog.fs.getBlob('stuff and things'));
    +      }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test stuff and things')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testSeek');
    +}
    +
    +function testTruncate() {
    +  var deferredFile = loadFile('test', goog.fs.DirectoryEntry.Behavior.CREATE);
    +  deferredFile.branch().
    +      addCallback(goog.partial(writeToFile, 'test content')).
    +      addCallback(function(fileEntry) { return fileEntry.createWriter(); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) { writer.truncate(4); }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING)).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      awaitDeferred(deferredFile).
    +      addCallback(goog.partial(checkFileContent, 'test')).
    +      addBoth(continueTesting);
    +  asyncTestCase.waitForAsync('testTruncate');
    +}
    +
    +
    +function continueTesting(result) {
    +  asyncTestCase.continueTesting();
    +  if (result instanceof Error) {
    +    throw result;
    +  }
    +}
    +
    +function loadTestDir() {
    +  return deferredFs.branch().addCallback(function(fs) {
    +    return fs.getRoot().getDirectory(
    +        TEST_DIR, goog.fs.DirectoryEntry.Behavior.CREATE);
    +  });
    +}
    +
    +function loadFile(filename, behavior) {
    +  return loadTestDir().addCallback(function(dir) {
    +    return dir.getFile(filename, behavior);
    +  });
    +}
    +
    +function loadDirectory(filename, behavior) {
    +  return loadTestDir().addCallback(function(dir) {
    +    return dir.getDirectory(filename, behavior);
    +  });
    +}
    +
    +function startWrite(content, fileEntry) {
    +  return fileEntry.createWriter().
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.INIT)).
    +      addCallback(function(writer) {
    +        writer.write(goog.fs.getBlob(content));
    +        return writer;
    +      }).
    +      addCallback(
    +          goog.partial(checkReadyState, goog.fs.FileSaver.ReadyState.WRITING));
    +}
    +
    +function waitForEvent(type, target) {
    +  var d = new goog.async.Deferred();
    +  goog.events.listenOnce(target, type, d.callback, false, d);
    +  return d;
    +}
    +
    +function writeToFile(content, fileEntry) {
    +  return startWrite(content, fileEntry).
    +      addCallback(
    +          goog.partial(waitForEvent, goog.fs.FileSaver.EventType.WRITE)).
    +      addCallback(function() { return fileEntry; });
    +}
    +
    +function checkFileContent(content, fileEntry) {
    +  return fileEntry.file().
    +      addCallback(function(blob) { return goog.fs.blobToString(blob); }).
    +      addCallback(goog.partial(assertEquals, content));
    +}
    +
    +function checkFileRemoved(filename) {
    +  return loadFile(filename).
    +      addCallback(goog.partial(fail, 'expected file to be removed')).
    +      addErrback(function(err) {
    +        assertEquals(err.code, goog.fs.Error.ErrorCode.NOT_FOUND);
    +        return true; // Go back to callback path
    +      });
    +}
    +
    +function checkReadyState(expectedState, writer) {
    +  assertEquals(expectedState, writer.getReadyState());
    +}
    +
    +function splitArgs(fn) {
    +  return function(args) { return fn(args[0], args[1]); };
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/fs/progressevent.js b/mapbox-gl/closure-library/closure/goog/testing/fs/progressevent.js
    new file mode 100644
    index 0000000000..6ffe16e018
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/fs/progressevent.js
    @@ -0,0 +1,82 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock ProgressEvent object.
    + *
    + */
    +
    +goog.provide('goog.testing.fs.ProgressEvent');
    +
    +goog.require('goog.events.Event');
    +
    +
    +
    +/**
    + * A mock progress event.
    + *
    + * @param {!goog.fs.FileSaver.EventType|!goog.fs.FileReader.EventType} type
    + *     Event type.
    + * @param {number} loaded The number of bytes processed.
    + * @param {number} total The total data that was to be processed, in bytes.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.testing.fs.ProgressEvent = function(type, loaded, total) {
    +  goog.testing.fs.ProgressEvent.base(this, 'constructor', type);
    +
    +  /**
    +   * The number of bytes processed.
    +   * @type {number}
    +   * @private
    +   */
    +  this.loaded_ = loaded;
    +
    +
    +  /**
    +   * The total data that was to be procesed, in bytes.
    +   * @type {number}
    +   * @private
    +   */
    +  this.total_ = total;
    +};
    +goog.inherits(goog.testing.fs.ProgressEvent, goog.events.Event);
    +
    +
    +/**
    + * @see {goog.fs.ProgressEvent#isLengthComputable}
    + * @return {boolean} True if the length is known.
    + */
    +goog.testing.fs.ProgressEvent.prototype.isLengthComputable = function() {
    +  return true;
    +};
    +
    +
    +/**
    + * @see {goog.fs.ProgressEvent#getLoaded}
    + * @return {number} The number of bytes loaded or written.
    + */
    +goog.testing.fs.ProgressEvent.prototype.getLoaded = function() {
    +  return this.loaded_;
    +};
    +
    +
    +/**
    + * @see {goog.fs.ProgressEvent#getTotal}
    + * @return {number} The total bytes to load or write.
    + */
    +goog.testing.fs.ProgressEvent.prototype.getTotal = function() {
    +  return this.total_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/functionmock.js b/mapbox-gl/closure-library/closure/goog/testing/functionmock.js
    new file mode 100644
    index 0000000000..4de9f2021a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/functionmock.js
    @@ -0,0 +1,176 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Enable mocking of functions not attached to objects
    + * whether they be global / top-level or anonymous methods / closures.
    + *
    + * See the unit tests for usage.
    + *
    + */
    +
    +goog.provide('goog.testing');
    +goog.provide('goog.testing.FunctionMock');
    +goog.provide('goog.testing.GlobalFunctionMock');
    +goog.provide('goog.testing.MethodMock');
    +
    +goog.require('goog.object');
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.Mock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.StrictMock');
    +
    +
    +/**
    + * Class used to mock a function. Useful for mocking closures and anonymous
    + * callbacks etc. Creates a function object that extends goog.testing.Mock.
    + * @param {string=} opt_functionName The optional name of the function to mock.
    + *     Set to '[anonymous mocked function]' if not passed in.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked function.
    + * @suppress {missingProperties} Mocks do not fit in the type system well.
    + */
    +goog.testing.FunctionMock = function(opt_functionName, opt_strictness) {
    +  var fn = function() {
    +    var args = Array.prototype.slice.call(arguments);
    +    args.splice(0, 0, opt_functionName || '[anonymous mocked function]');
    +    return fn.$mockMethod.apply(fn, args);
    +  };
    +  var base = opt_strictness === goog.testing.Mock.LOOSE ?
    +      goog.testing.LooseMock : goog.testing.StrictMock;
    +  goog.object.extend(fn, new base({}));
    +
    +  return /** @type {!goog.testing.MockInterface} */ (fn);
    +};
    +
    +
    +/**
    + * Mocks an existing function. Creates a goog.testing.FunctionMock
    + * and registers it in the given scope with the name specified by functionName.
    + * @param {Object} scope The scope of the method to be mocked out.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked method.
    + */
    +goog.testing.MethodMock = function(scope, functionName, opt_strictness) {
    +  if (!(functionName in scope)) {
    +    throw Error(functionName + ' is not a property of the given scope.');
    +  }
    +
    +  var fn = goog.testing.FunctionMock(functionName, opt_strictness);
    +
    +  fn.$propertyReplacer_ = new goog.testing.PropertyReplacer();
    +  fn.$propertyReplacer_.set(scope, functionName, fn);
    +  fn.$tearDown = goog.testing.MethodMock.$tearDown;
    +
    +  return fn;
    +};
    +
    +
    +/**
    + * Resets the global function that we mocked back to its original state.
    + * @this {goog.testing.MockInterface}
    + */
    +goog.testing.MethodMock.$tearDown = function() {
    +  this.$propertyReplacer_.reset();
    +};
    +
    +
    +/**
    + * Mocks a global / top-level function. Creates a goog.testing.MethodMock
    + * in the global scope with the name specified by functionName.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked global function.
    + */
    +goog.testing.GlobalFunctionMock = function(functionName, opt_strictness) {
    +  return goog.testing.MethodMock(goog.global, functionName, opt_strictness);
    +};
    +
    +
    +/**
    + * Convenience method for creating a mock for a function.
    + * @param {string=} opt_functionName The optional name of the function to mock
    + *     set to '[anonymous mocked function]' if not passed in.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {goog.testing.MockInterface} The mocked function.
    + */
    +goog.testing.createFunctionMock = function(opt_functionName, opt_strictness) {
    +  return goog.testing.FunctionMock(opt_functionName, opt_strictness);
    +};
    +
    +
    +/**
    + * Convenience method for creating a mock for a method.
    + * @param {Object} scope The scope of the method to be mocked out.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked global function.
    + */
    +goog.testing.createMethodMock = function(scope, functionName, opt_strictness) {
    +  return goog.testing.MethodMock(scope, functionName, opt_strictness);
    +};
    +
    +
    +/**
    + * Convenience method for creating a mock for a constructor. Copies class
    + * members to the mock.
    + *
    + * <p>When mocking a constructor to return a mocked instance, remember to create
    + * the instance mock before mocking the constructor. If you mock the constructor
    + * first, then the mock framework will be unable to examine the prototype chain
    + * when creating the mock instance.
    + * @param {Object} scope The scope of the constructor to be mocked out.
    + * @param {string} constructorName The name of the constructor we're going to
    + *     mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked constructor.
    + */
    +goog.testing.createConstructorMock = function(scope, constructorName,
    +                                              opt_strictness) {
    +  var realConstructor = scope[constructorName];
    +  var constructorMock = goog.testing.MethodMock(scope, constructorName,
    +                                                opt_strictness);
    +
    +  // Copy class members from the real constructor to the mock. Do not copy
    +  // the closure superClass_ property (see goog.inherits), the built-in
    +  // prototype property, or properties added to Function.prototype
    +  // (see goog.MODIFY_FUNCTION_PROTOTYPES in closure/base.js).
    +  for (var property in realConstructor) {
    +    if (property != 'superClass_' &&
    +        property != 'prototype' &&
    +        realConstructor.hasOwnProperty(property)) {
    +      constructorMock[property] = realConstructor[property];
    +    }
    +  }
    +  return constructorMock;
    +};
    +
    +
    +/**
    + * Convenience method for creating a mocks for a global / top-level function.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked global function.
    + */
    +goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) {
    +  return goog.testing.GlobalFunctionMock(functionName, opt_strictness);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.html b/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.html
    new file mode 100644
    index 0000000000..20fcb1a71f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  Test mocking global / top-level functions
    +
    +-->
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Global Mock Unit Test
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.FunctionMockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.js b/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.js
    new file mode 100644
    index 0000000000..9d495c2f09
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/functionmock_test.js
    @@ -0,0 +1,503 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.FunctionMockTest');
    +goog.setTestOnly('goog.testing.FunctionMockTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.string');
    +goog.require('goog.testing');
    +goog.require('goog.testing.FunctionMock');
    +goog.require('goog.testing.Mock');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +
    +// Global scope so we can tear it down safely
    +var mockGlobal;
    +
    +function tearDown() {
    +  if (mockGlobal) {
    +    mockGlobal.$tearDown();
    +  }
    +}
    +
    +
    +//----- Tests for goog.testing.FunctionMock
    +
    +function testMockFunctionCallOrdering() {
    +  var doOneTest = function(mockFunction, success, expected_args, actual_args) {
    +    goog.array.forEach(expected_args, function(arg) { mockFunction(arg); });
    +    mockFunction.$replay();
    +    var callFunction = function() {
    +      goog.array.forEach(actual_args, function(arg) { mockFunction(arg); });
    +      mockFunction.$verify();
    +    };
    +    if (success) {
    +      callFunction();
    +    } else {
    +      assertThrows(callFunction);
    +    }
    +  };
    +
    +  var doTest = function(strict_ok, loose_ok, expected_args, actual_args) {
    +    doOneTest(goog.testing.createFunctionMock(), strict_ok,
    +              expected_args, actual_args);
    +    doOneTest(goog.testing.createFunctionMock('name'), strict_ok,
    +              expected_args, actual_args);
    +    doOneTest(goog.testing.createFunctionMock('name', goog.testing.Mock.STRICT),
    +              strict_ok, expected_args, actual_args);
    +    doOneTest(goog.testing.createFunctionMock('name', goog.testing.Mock.LOOSE),
    +              loose_ok, expected_args, actual_args);
    +  };
    +
    +  doTest(true, true, [1, 2], [1, 2]);
    +  doTest(false, true, [1, 2], [2, 1]);
    +  doTest(false, false, [1, 2], [2, 2]);
    +  doTest(false, false, [1, 2], [1]);
    +  doTest(false, false, [1, 2], [1, 1]);
    +  doTest(false, false, [1, 2], [1]);
    +}
    +
    +function testMocksFunctionWithNoArgs() {
    +  var mockFoo = goog.testing.createFunctionMock();
    +  mockFoo();
    +  mockFoo.$replay();
    +  mockFoo();
    +  mockFoo.$verify();
    +}
    +
    +function testMocksFunctionWithOneArg() {
    +  var mockFoo = goog.testing.createFunctionMock();
    +  mockFoo('x');
    +  mockFoo.$replay();
    +  mockFoo('x');
    +  mockFoo.$verify();
    +}
    +
    +function testMocksFunctionWithMultipleArgs() {
    +  var mockFoo = goog.testing.createFunctionMock();
    +  mockFoo('x', 'y');
    +  mockFoo.$replay();
    +  mockFoo('x', 'y');
    +  mockFoo.$verify();
    +}
    +
    +function testFailsIfCalledWithIncorrectArgs() {
    +  var mockFoo = goog.testing.createFunctionMock();
    +
    +  mockFoo();
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo('x');});
    +  mockFoo.$reset();
    +
    +  mockFoo('x');
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo();});
    +  mockFoo.$reset();
    +
    +  mockFoo('x');
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo('x', 'y');});
    +  mockFoo.$reset();
    +
    +  mockFoo('x', 'y');
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo('x');});
    +  mockFoo.$reset();
    +
    +  mockFoo('correct');
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo('wrong');});
    +  mockFoo.$reset();
    +
    +  mockFoo('correct', 'args');
    +  mockFoo.$replay();
    +  assertThrows(function() {mockFoo('wrong', 'args');});
    +  mockFoo.$reset();
    +}
    +
    +function testMocksFunctionWithReturnValue() {
    +  var mockFoo = goog.testing.createFunctionMock();
    +  mockFoo().$returns('bar');
    +  mockFoo.$replay();
    +  assertEquals('bar', mockFoo());
    +  mockFoo.$verify();
    +}
    +
    +function testFunctionMockWorksWhenPassedAsACallback() {
    +  var invoker = {
    +    register: function(callback) {
    +      this.callback = callback;
    +    },
    +
    +    invoke: function(args) {
    +      return this.callback(args);
    +    }
    +  };
    +
    +  var mockFunction = goog.testing.createFunctionMock();
    +  mockFunction('bar').$returns('baz');
    +
    +  mockFunction.$replay();
    +  invoker.register(mockFunction);
    +  assertEquals('baz', invoker.invoke('bar'));
    +  mockFunction.$verify();
    +}
    +
    +function testFunctionMockQuacksLikeAStrictMock() {
    +  var mockFunction = goog.testing.createFunctionMock();
    +  assertQuacksLike(mockFunction, goog.testing.StrictMock);
    +}
    +
    +
    +//----- Global functions for goog.testing.GlobalFunctionMock to mock
    +
    +function globalFoo() {
    +  return 'I am Spartacus!';
    +}
    +
    +function globalBar(who, what) {
    +  return [who, 'is', what].join(' ');
    +}
    +
    +
    +//----- Tests for goog.testing.createGlobalFunctionMock
    +
    +function testMocksGlobalFunctionWithNoArgs() {
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo');
    +  mockGlobal().$returns('No, I am Spartacus!');
    +
    +  mockGlobal.$replay();
    +  assertEquals('No, I am Spartacus!', globalFoo());
    +  mockGlobal.$verify();
    +}
    +
    +function testMocksGlobalFunctionUsingGlobalName() {
    +  goog.testing.createGlobalFunctionMock('globalFoo');
    +  globalFoo().$returns('No, I am Spartacus!');
    +
    +  globalFoo.$replay();
    +  assertEquals('No, I am Spartacus!', globalFoo());
    +  globalFoo.$verify();
    +  globalFoo.$tearDown();
    +}
    +
    +function testMocksGlobalFunctionWithArgs() {
    +  var mockReturnValue = 'Noam is Chomsky!';
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalBar');
    +  mockGlobal('Noam', 'Spartacus').$returns(mockReturnValue);
    +
    +  mockGlobal.$replay();
    +  assertEquals(mockReturnValue, globalBar('Noam', 'Spartacus'));
    +  mockGlobal.$verify();
    +}
    +
    +function testGlobalFunctionMockFailsWithIncorrectArgs() {
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalBar');
    +  mockGlobal('a', 'b');
    +
    +  mockGlobal.$replay();
    +
    +  assertThrows('Mock should have failed because of incorrect arguments',
    +      function() {globalBar('b', 'a')});
    +}
    +
    +function testGlobalFunctionMockQuacksLikeAFunctionMock() {
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo');
    +  assertQuacksLike(mockGlobal, goog.testing.FunctionMock);
    +}
    +
    +function testMockedFunctionsAvailableInGlobalAndGoogGlobalAndWindowScope() {
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo');
    +
    +  // we expect this call 3 times through global, goog.global and window scope
    +  mockGlobal().$times(3);
    +
    +  mockGlobal.$replay();
    +  goog.global.globalFoo();
    +  window.globalFoo();
    +  globalFoo();
    +  mockGlobal.$verify();
    +}
    +
    +function testTearDownRestoresOriginalGlobalFunction() {
    +  mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo');
    +  mockGlobal().$returns('No, I am Spartacus!');
    +
    +  mockGlobal.$replay();
    +  assertEquals('No, I am Spartacus!', globalFoo());
    +  mockGlobal.$tearDown();
    +  assertEquals('I am Spartacus!', globalFoo());
    +  mockGlobal.$verify();
    +}
    +
    +function testTearDownHandlesMultipleMocking() {
    +  var mock1 = goog.testing.createGlobalFunctionMock('globalFoo');
    +  var mock2 = goog.testing.createGlobalFunctionMock('globalFoo');
    +  var mock3 = goog.testing.createGlobalFunctionMock('globalFoo');
    +  mock1().$returns('No, I am Spartacus 1!');
    +  mock2().$returns('No, I am Spartacus 2!');
    +  mock3().$returns('No, I am Spartacus 3!');
    +
    +  mock1.$replay();
    +  mock2.$replay();
    +  mock3.$replay();
    +  assertEquals('No, I am Spartacus 3!', globalFoo());
    +  mock3.$tearDown();
    +  assertEquals('No, I am Spartacus 2!', globalFoo());
    +  mock2.$tearDown();
    +  assertEquals('No, I am Spartacus 1!', globalFoo());
    +  mock1.$tearDown();
    +  assertEquals('I am Spartacus!', globalFoo());
    +}
    +
    +function testGlobalFunctionMockCallOrdering() {
    +  var mock = goog.testing.createGlobalFunctionMock('globalFoo');
    +  mock(1);
    +  mock(2);
    +  mock.$replay();
    +  assertThrows(function() {globalFoo(2);});
    +  mock.$tearDown();
    +
    +  mock = goog.testing.createGlobalFunctionMock('globalFoo',
    +                                               goog.testing.Mock.STRICT);
    +  mock(1);
    +  mock(2);
    +  mock.$replay();
    +  globalFoo(1);
    +  globalFoo(2);
    +  mock.$verify();
    +  mock.$tearDown();
    +
    +  mock = goog.testing.createGlobalFunctionMock('globalFoo',
    +                                               goog.testing.Mock.STRICT);
    +  mock(1);
    +  mock(2);
    +  mock.$replay();
    +  assertThrows(function() {globalFoo(2);});
    +  mock.$tearDown();
    +
    +  mock = goog.testing.createGlobalFunctionMock('globalFoo',
    +                                               goog.testing.Mock.LOOSE);
    +  mock(1);
    +  mock(2);
    +  mock.$replay();
    +  globalFoo(2);
    +  globalFoo(1);
    +  mock.$verify();
    +  mock.$tearDown();
    +}
    +
    +//----- Functions for goog.testing.MethodMock to mock
    +
    +var mynamespace = {};
    +
    +mynamespace.myMethod = function() {
    +  return 'I should be mocked.';
    +};
    +
    +function testMocksMethod() {
    +  mockMethod = goog.testing.createMethodMock(mynamespace, 'myMethod');
    +  mockMethod().$returns('I have been mocked!');
    +
    +  mockMethod.$replay();
    +  assertEquals('I have been mocked!', mockMethod());
    +  mockMethod.$verify();
    +}
    +
    +function testMocksMethodInNamespace() {
    +  goog.testing.createMethodMock(mynamespace, 'myMethod');
    +  mynamespace.myMethod().$returns('I have been mocked!');
    +
    +  mynamespace.myMethod.$replay();
    +  assertEquals('I have been mocked!', mynamespace.myMethod());
    +  mynamespace.myMethod.$verify();
    +  mynamespace.myMethod.$tearDown();
    +}
    +
    +function testMethodMockCanOnlyMockExistingMethods() {
    +  assertThrows(function() {
    +    goog.testing.createMethodMock(mynamespace, 'doesNotExist');
    +  });
    +}
    +
    +function testMethodMockCallOrdering() {
    +  goog.testing.createMethodMock(mynamespace, 'myMethod');
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod.$replay();
    +  assertThrows(function() {mynamespace.myMethod(2);});
    +  mynamespace.myMethod.$tearDown();
    +
    +  goog.testing.createMethodMock(mynamespace, 'myMethod',
    +                                goog.testing.Mock.STRICT);
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod.$replay();
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod.$verify();
    +  mynamespace.myMethod.$tearDown();
    +
    +  goog.testing.createMethodMock(mynamespace, 'myMethod',
    +                                goog.testing.Mock.STRICT);
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod.$replay();
    +  assertThrows(function() {mynamespace.myMethod(2);});
    +  mynamespace.myMethod.$tearDown();
    +
    +  goog.testing.createMethodMock(mynamespace, 'myMethod',
    +                                goog.testing.Mock.LOOSE);
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod.$replay();
    +  mynamespace.myMethod(2);
    +  mynamespace.myMethod(1);
    +  mynamespace.myMethod.$verify();
    +  mynamespace.myMethod.$tearDown();
    +}
    +
    +//----- Functions for goog.testing.createConstructorMock to mock
    +
    +var constructornamespace = {};
    +
    +constructornamespace.MyConstructor = function() {
    +};
    +
    +constructornamespace.MyConstructor.prototype.myMethod = function() {
    +  return 'I should be mocked.';
    +};
    +
    +constructornamespace.MyConstructorWithArgument = function(argument) {
    +  this.argument_ = argument;
    +};
    +
    +constructornamespace.MyConstructorWithArgument.prototype.myMethod = function() {
    +  return this.argument_;
    +};
    +
    +constructornamespace.MyConstructorWithClassMembers = function() {
    +};
    +
    +constructornamespace.MyConstructorWithClassMembers.CONSTANT = 42;
    +
    +constructornamespace.MyConstructorWithClassMembers.classMethod = function() {
    +  return 'class method return value';
    +};
    +
    +function testConstructorMock() {
    +  var mockObject =
    +      new goog.testing.StrictMock(constructornamespace.MyConstructor);
    +  var mockConstructor = goog.testing.createConstructorMock(
    +      constructornamespace, 'MyConstructor');
    +  mockConstructor().$returns(mockObject);
    +  mockObject.myMethod().$returns('I have been mocked!');
    +
    +  mockConstructor.$replay();
    +  mockObject.$replay();
    +  assertEquals('I have been mocked!',
    +      new constructornamespace.MyConstructor().myMethod());
    +  mockConstructor.$verify();
    +  mockObject.$verify();
    +  mockConstructor.$tearDown();
    +}
    +
    +function testConstructorMockWithArgument() {
    +  var mockObject = new goog.testing.StrictMock(
    +      constructornamespace.MyConstructorWithArgument);
    +  var mockConstructor = goog.testing.createConstructorMock(
    +      constructornamespace, 'MyConstructorWithArgument');
    +  mockConstructor(goog.testing.mockmatchers.isString).$returns(mockObject);
    +  mockObject.myMethod().$returns('I have been mocked!');
    +
    +  mockConstructor.$replay();
    +  mockObject.$replay();
    +  assertEquals('I have been mocked!',
    +      new constructornamespace.MyConstructorWithArgument('I should be mocked.')
    +          .myMethod());
    +  mockConstructor.$verify();
    +  mockObject.$verify();
    +  mockConstructor.$tearDown();
    +}
    +
    +
    +/**
    + * Test that class members are copied to the mock constructor.
    + */
    +function testConstructorMockWithClassMembers() {
    +  var mockConstructor = goog.testing.createConstructorMock(
    +      constructornamespace, 'MyConstructorWithClassMembers');
    +  assertEquals(42, constructornamespace.MyConstructorWithClassMembers.CONSTANT);
    +  assertEquals('class method return value',
    +      constructornamespace.MyConstructorWithClassMembers.classMethod());
    +  mockConstructor.$tearDown();
    +}
    +
    +function testConstructorMockCallOrdering() {
    +  var instance = {};
    +
    +  goog.testing.createConstructorMock(constructornamespace,
    +                                     'MyConstructorWithArgument');
    +  constructornamespace.MyConstructorWithArgument(1).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument(2).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument.$replay();
    +  assertThrows(
    +      function() {new constructornamespace.MyConstructorWithArgument(2);});
    +  constructornamespace.MyConstructorWithArgument.$tearDown();
    +
    +  goog.testing.createConstructorMock(constructornamespace,
    +                                     'MyConstructorWithArgument',
    +                                     goog.testing.Mock.STRICT);
    +  constructornamespace.MyConstructorWithArgument(1).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument(2).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument.$replay();
    +  new constructornamespace.MyConstructorWithArgument(1);
    +  new constructornamespace.MyConstructorWithArgument(2);
    +  constructornamespace.MyConstructorWithArgument.$verify();
    +  constructornamespace.MyConstructorWithArgument.$tearDown();
    +
    +  goog.testing.createConstructorMock(constructornamespace,
    +                                     'MyConstructorWithArgument',
    +                                     goog.testing.Mock.STRICT);
    +  constructornamespace.MyConstructorWithArgument(1).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument(2).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument.$replay();
    +  assertThrows(
    +      function() {new constructornamespace.MyConstructorWithArgument(2);});
    +  constructornamespace.MyConstructorWithArgument.$tearDown();
    +
    +  goog.testing.createConstructorMock(constructornamespace,
    +                                     'MyConstructorWithArgument',
    +                                     goog.testing.Mock.LOOSE);
    +  constructornamespace.MyConstructorWithArgument(1).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument(2).$returns(instance);
    +  constructornamespace.MyConstructorWithArgument.$replay();
    +  new constructornamespace.MyConstructorWithArgument(2);
    +  new constructornamespace.MyConstructorWithArgument(1);
    +  constructornamespace.MyConstructorWithArgument.$verify();
    +  constructornamespace.MyConstructorWithArgument.$tearDown();
    +}
    +
    +//----- Helper assertions
    +
    +function assertQuacksLike(obj, target) {
    +  for (meth in target.prototype) {
    +    if (!goog.string.endsWith(meth, '_')) {
    +      assertNotUndefined('Should have implemented ' + meth + '()', obj[meth]);
    +    }
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/graphics.js b/mapbox-gl/closure-library/closure/goog/testing/graphics.js
    new file mode 100644
    index 0000000000..be342e457c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/graphics.js
    @@ -0,0 +1,64 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Testing utilities for DOM related tests.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.testing.graphics');
    +
    +goog.require('goog.graphics.Path');
    +goog.require('goog.testing.asserts');
    +
    +
    +/**
    + * Array mapping numeric segment constant to a descriptive character.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.testing.graphics.SEGMENT_NAMES_ = function() {
    +  var arr = [];
    +  arr[goog.graphics.Path.Segment.MOVETO] = 'M';
    +  arr[goog.graphics.Path.Segment.LINETO] = 'L';
    +  arr[goog.graphics.Path.Segment.CURVETO] = 'C';
    +  arr[goog.graphics.Path.Segment.ARCTO] = 'A';
    +  arr[goog.graphics.Path.Segment.CLOSE] = 'X';
    +  return arr;
    +}();
    +
    +
    +/**
    + * Test if the given path matches the expected array of commands and parameters.
    + * @param {Array<string|number>} expected The expected array of commands and
    + *     parameters.
    + * @param {goog.graphics.Path} path The path to test against.
    + */
    +goog.testing.graphics.assertPathEquals = function(expected, path) {
    +  var actual = [];
    +  path.forEachSegment(function(seg, args) {
    +    actual.push(goog.testing.graphics.SEGMENT_NAMES_[seg]);
    +    Array.prototype.push.apply(actual, args);
    +  });
    +  assertEquals(expected.length, actual.length);
    +  for (var i = 0; i < expected.length; i++) {
    +    if (goog.isNumber(expected[i])) {
    +      assertTrue(goog.isNumber(actual[i]));
    +      assertRoughlyEquals(expected[i], actual[i], 0.01);
    +    } else {
    +      assertEquals(expected[i], actual[i]);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts.js b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts.js
    new file mode 100644
    index 0000000000..ab3033606f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts.js
    @@ -0,0 +1,77 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Assert functions that account for locale data changes.
    + *
    + * The locale data gets updated from CLDR (http://cldr.unicode.org/),
    + * and CLDR gets an update about twice per year.
    + * So the locale data are expected to change.
    + * This can make unit tests quite fragile:
    + *   assertEquals("Dec 31, 2013, 1:23pm", format);
    + * Now imagine that the decision is made to add a dot after abbreviations,
    + * and a comma between date and time.
    + * The previous assert will fail, because the string is now
    + *   "Dec. 31 2013, 1:23pm"
    + *
    + * One option is to not unit test the results of the formatters client side,
    + * and just trust that CLDR and closure/i18n takes care of that.
    + * The other option is to be a more flexible when testing.
    + * This is the role of assertI18nEquals, to centralize all the small
    + * differences between hard-coded values in unit tests and the current result.
    + * It allows some decupling, so that the closure/i18n can be updated without
    + * breaking all the clients using it.
    + * For the example above, this will succeed:
    + *   assertI18nEquals("Dec 31, 2013, 1:23pm", "Dec. 31, 2013 1:23pm");
    + * It does this by white-listing, no "guessing" involved.
    + *
    + * But I would say that the best practice is the first option: trust the
    + * library, stop unit-testing it.
    + */
    +
    +goog.provide('goog.testing.i18n.asserts');
    +goog.setTestOnly('goog.testing.i18n.asserts');
    +
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * A map of known tests where locale data changed, but the old values are
    + * still tested for by various clients.
    + * @const {!Object<string, string>}
    + * @private
    + */
    +goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_ = {
    +  // Data to test the assert itself, old string as key, new string as value
    +};
    +
    +
    +/**
    + * Asserts that the two values are "almost equal" from i18n perspective
    + * (based on a manually maintained and validated whitelist).
    + * @param {string} expected The expected value.
    + * @param {string} actual The actual value.
    + */
    +goog.testing.i18n.asserts.assertI18nEquals = function(expected, actual) {
    +  if (expected == actual) {
    +    return;
    +  }
    +
    +  var newExpected = goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_[expected];
    +  if (newExpected == actual) {
    +    return;
    +  }
    +
    +  assertEquals(expected, actual);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.html b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.html
    new file mode 100644
    index 0000000000..77afbf9804
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.testing.i18n.asserts</title>
    +<script src="../../base.js"></script>
    +</head>
    +<body>
    +<script>
    +  goog.require('goog.testing.i18n.assertsTest');
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.js b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.js
    new file mode 100644
    index 0000000000..163ca04c59
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/i18n/asserts_test.js
    @@ -0,0 +1,67 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.testing.i18n.asserts.
    + */
    +
    +goog.provide('goog.testing.i18n.assertsTest');
    +goog.setTestOnly('goog.testing.i18n.assertsTest');
    +
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.i18n.asserts');
    +
    +
    +// Add this mapping for testing only
    +goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_['mappedValue'] = 'newValue';
    +
    +var expectedFailures = new goog.testing.ExpectedFailures();
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testEdgeCases() {
    +  // Pass
    +  goog.testing.i18n.asserts.assertI18nEquals(null, null);
    +  goog.testing.i18n.asserts.assertI18nEquals('', '');
    +
    +  // Fail
    +  expectedFailures.expectFailureFor(true);
    +  try {
    +    goog.testing.i18n.asserts.assertI18nEquals(null, '');
    +    goog.testing.i18n.asserts.assertI18nEquals(null, 'test');
    +    goog.testing.i18n.asserts.assertI18nEquals('', null);
    +    goog.testing.i18n.asserts.assertI18nEquals('', 'test');
    +    goog.testing.i18n.asserts.assertI18nEquals('test', null);
    +    goog.testing.i18n.asserts.assertI18nEquals('test', '');
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testMappingWorks() {
    +  // Real equality
    +  goog.testing.i18n.asserts.assertI18nEquals('test', 'test');
    +  // i18n mapped equality
    +  goog.testing.i18n.asserts.assertI18nEquals('mappedValue', 'newValue');
    +
    +  // Negative testing
    +  expectedFailures.expectFailureFor(true);
    +  try {
    +    goog.testing.i18n.asserts.assertI18nEquals('unmappedValue', 'newValue');
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/jsunit.js b/mapbox-gl/closure-library/closure/goog/testing/jsunit.js
    new file mode 100644
    index 0000000000..c2c9c1e210
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/jsunit.js
    @@ -0,0 +1,157 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for working with JsUnit.  Writes out the JsUnit file
    + * that needs to be included in every unit test.
    + *
    + * Testing code should not have dependencies outside of goog.testing so as to
    + * reduce the chance of masking missing dependencies.
    + *
    + */
    +
    +goog.provide('goog.testing.jsunit');
    +
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.TestRunner');
    +
    +
    +/**
    + * Base path for JsUnit app files, relative to Closure's base path.
    + * @type {string}
    + */
    +goog.testing.jsunit.BASE_PATH =
    +    '../../third_party/java/jsunit/core/app/';
    +
    +
    +/**
    + * Filename for the core JS Unit script.
    + * @type {string}
    + */
    +goog.testing.jsunit.CORE_SCRIPT =
    +    goog.testing.jsunit.BASE_PATH + 'jsUnitCore.js';
    +
    +
    +/**
    + * @define {boolean} If this code is being parsed by JsTestC, we let it disable
    + * the onload handler to avoid running the test in JsTestC.
    + */
    +goog.define('goog.testing.jsunit.AUTO_RUN_ONLOAD', true);
    +
    +
    +/**
    + * @define {number} Sets a delay in milliseconds after the window onload event
    + * and running the tests. Used to prevent interference with Selenium and give
    + * tests with asynchronous operations time to finish loading.
    + */
    +goog.define('goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS', 500);
    +
    +
    +(function() {
    +  // Increases the maximum number of stack frames in Google Chrome from the
    +  // default 10 to 50 to get more useful stack traces.
    +  Error.stackTraceLimit = 50;
    +
    +  // Store a reference to the window's timeout so that it can't be overridden
    +  // by tests.
    +  /** @type {!Function} */
    +  var realTimeout = window.setTimeout;
    +
    +  // Check for JsUnit's test runner (need to check for >2.2 and <=2.2)
    +  if (top['JsUnitTestManager'] || top['jsUnitTestManager']) {
    +    // Running inside JsUnit so add support code.
    +    var path = goog.basePath + goog.testing.jsunit.CORE_SCRIPT;
    +    document.write('<script type="text/javascript" src="' +
    +                   path + '"></' + 'script>');
    +
    +  } else {
    +
    +    // Create a test runner.
    +    var tr = new goog.testing.TestRunner();
    +
    +    // Export it so that it can be queried by Selenium and tests that use a
    +    // compiled test runner.
    +    goog.exportSymbol('G_testRunner', tr);
    +    goog.exportSymbol('G_testRunner.initialize', tr.initialize);
    +    goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized);
    +    goog.exportSymbol('G_testRunner.isFinished', tr.isFinished);
    +    goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess);
    +    goog.exportSymbol('G_testRunner.getReport', tr.getReport);
    +    goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime);
    +    goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded);
    +    goog.exportSymbol('G_testRunner.setStrict', tr.setStrict);
    +    goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure);
    +    goog.exportSymbol('G_testRunner.getTestResults', tr.getTestResults);
    +
    +    // Export debug as a global function for JSUnit compatibility.  This just
    +    // calls log on the current test case.
    +    if (!goog.global['debug']) {
    +      goog.exportSymbol('debug', goog.bind(tr.log, tr));
    +    }
    +
    +    // If the application has defined a global error filter, set it now.  This
    +    // allows users who use a base test include to set the error filter before
    +    // the testing code is loaded.
    +    if (goog.global['G_errorFilter']) {
    +      tr.setErrorFilter(goog.global['G_errorFilter']);
    +    }
    +
    +    // Add an error handler to report errors that may occur during
    +    // initialization of the page.
    +    var onerror = window.onerror;
    +    window.onerror = function(error, url, line) {
    +      // Call any existing onerror handlers.
    +      if (onerror) {
    +        onerror(error, url, line);
    +      }
    +      if (typeof error == 'object') {
    +        // Webkit started passing an event object as the only argument to
    +        // window.onerror.  It doesn't contain an error message, url or line
    +        // number.  We therefore log as much info as we can.
    +        if (error.target && error.target.tagName == 'SCRIPT') {
    +          tr.logError('UNKNOWN ERROR: Script ' + error.target.src);
    +        } else {
    +          tr.logError('UNKNOWN ERROR: No error information available.');
    +        }
    +      } else {
    +        tr.logError('JS ERROR: ' + error + '\nURL: ' + url + '\nLine: ' + line);
    +      }
    +    };
    +
    +    // Create an onload handler, if the test runner hasn't been initialized then
    +    // no test has been registered with the test runner by the test file.  We
    +    // then create a new test case and auto discover any tests in the global
    +    // scope. If this code is being parsed by JsTestC, we let it disable the
    +    // onload handler to avoid running the test in JsTestC.
    +    if (goog.testing.jsunit.AUTO_RUN_ONLOAD) {
    +      var onload = window.onload;
    +      window.onload = function(e) {
    +        // Call any existing onload handlers.
    +        if (onload) {
    +          onload(e);
    +        }
    +        // Wait so that we don't interfere with WebDriver.
    +        realTimeout(function() {
    +          if (!tr.initialized) {
    +            var test = new goog.testing.TestCase(document.title);
    +            test.autoDiscoverTests();
    +            tr.initialize(test);
    +          }
    +          tr.execute();
    +        }, goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS);
    +        window.onload = null;
    +      };
    +    }
    +  }
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/loosemock.js b/mapbox-gl/closure-library/closure/goog/testing/loosemock.js
    new file mode 100644
    index 0000000000..cef72d77a6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/loosemock.js
    @@ -0,0 +1,242 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file defines a loose mock implementation.
    + */
    +
    +goog.provide('goog.testing.LooseExpectationCollection');
    +goog.provide('goog.testing.LooseMock');
    +
    +goog.require('goog.array');
    +goog.require('goog.structs.Map');
    +goog.require('goog.testing.Mock');
    +
    +
    +
    +/**
    + * This class is an ordered collection of expectations for one method. Since
    + * the loose mock does most of its verification at the time of $verify, this
    + * class is necessary to manage the return/throw behavior when the mock is
    + * being called.
    + * @constructor
    + * @final
    + */
    +goog.testing.LooseExpectationCollection = function() {
    +  /**
    +   * The list of expectations. All of these should have the same name.
    +   * @type {Array<goog.testing.MockExpectation>}
    +   * @private
    +   */
    +  this.expectations_ = [];
    +};
    +
    +
    +/**
    + * Adds an expectation to this collection.
    + * @param {goog.testing.MockExpectation} expectation The expectation to add.
    + */
    +goog.testing.LooseExpectationCollection.prototype.addExpectation =
    +    function(expectation) {
    +  this.expectations_.push(expectation);
    +};
    +
    +
    +/**
    + * Gets the list of expectations in this collection.
    + * @return {Array<goog.testing.MockExpectation>} The array of expectations.
    + */
    +goog.testing.LooseExpectationCollection.prototype.getExpectations = function() {
    +  return this.expectations_;
    +};
    +
    +
    +
    +/**
    + * This is a mock that does not care about the order of method calls. As a
    + * result, it won't throw exceptions until verify() is called. The only
    + * exception is that if a method is called that has no expectations, then an
    + * exception will be thrown.
    + * @param {Object|Function} objectToMock The object that should be mocked, or
    + *    the constructor of an object to mock.
    + * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
    + *     calls.
    + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
    + *     a mock should be constructed from the static functions of a class.
    + * @param {boolean=} opt_createProxy An optional argument denoting that
    + *     a proxy for the target mock should be created.
    + * @constructor
    + * @extends {goog.testing.Mock}
    + */
    +goog.testing.LooseMock = function(objectToMock, opt_ignoreUnexpectedCalls,
    +    opt_mockStaticMethods, opt_createProxy) {
    +  goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
    +      opt_createProxy);
    +
    +  /**
    +   * A map of method names to a LooseExpectationCollection for that method.
    +   * @type {goog.structs.Map}
    +   * @private
    +   */
    +  this.$expectations_ = new goog.structs.Map();
    +
    +  /**
    +   * The calls that have been made; we cache them to verify at the end. Each
    +   * element is an array where the first element is the name, and the second
    +   * element is the arguments.
    +   * @type {Array<Array<*>>}
    +   * @private
    +   */
    +  this.$calls_ = [];
    +
    +  /**
    +   * Whether to ignore unexpected calls.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.$ignoreUnexpectedCalls_ = !!opt_ignoreUnexpectedCalls;
    +};
    +goog.inherits(goog.testing.LooseMock, goog.testing.Mock);
    +
    +
    +/**
    + * A setter for the ignoreUnexpectedCalls field.
    + * @param {boolean} ignoreUnexpectedCalls Whether to ignore unexpected calls.
    + * @return {!goog.testing.LooseMock} This mock object.
    + */
    +goog.testing.LooseMock.prototype.$setIgnoreUnexpectedCalls = function(
    +    ignoreUnexpectedCalls) {
    +  this.$ignoreUnexpectedCalls_ = ignoreUnexpectedCalls;
    +  return this;
    +};
    +
    +
    +/** @override */
    +goog.testing.LooseMock.prototype.$recordExpectation = function() {
    +  if (!this.$expectations_.containsKey(this.$pendingExpectation.name)) {
    +    this.$expectations_.set(this.$pendingExpectation.name,
    +        new goog.testing.LooseExpectationCollection());
    +  }
    +
    +  var collection = this.$expectations_.get(this.$pendingExpectation.name);
    +  collection.addExpectation(this.$pendingExpectation);
    +};
    +
    +
    +/** @override */
    +goog.testing.LooseMock.prototype.$recordCall = function(name, args) {
    +  if (!this.$expectations_.containsKey(name)) {
    +    if (this.$ignoreUnexpectedCalls_) {
    +      return;
    +    }
    +    this.$throwCallException(name, args);
    +  }
    +
    +  // Start from the beginning of the expectations for this name,
    +  // and iterate over them until we find an expectation that matches
    +  // and also has calls remaining.
    +  var collection = this.$expectations_.get(name);
    +  var matchingExpectation = null;
    +  var expectations = collection.getExpectations();
    +  for (var i = 0; i < expectations.length; i++) {
    +    var expectation = expectations[i];
    +    if (this.$verifyCall(expectation, name, args)) {
    +      matchingExpectation = expectation;
    +      if (expectation.actualCalls < expectation.maxCalls) {
    +        break;
    +      } // else continue and see if we can find something that does match
    +    }
    +  }
    +  if (matchingExpectation == null) {
    +    this.$throwCallException(name, args, expectation);
    +  }
    +
    +  matchingExpectation.actualCalls++;
    +  if (matchingExpectation.actualCalls > matchingExpectation.maxCalls) {
    +    this.$throwException('Too many calls to ' + matchingExpectation.name +
    +            '\nExpected: ' + matchingExpectation.maxCalls + ' but was: ' +
    +            matchingExpectation.actualCalls);
    +  }
    +
    +  this.$calls_.push([name, args]);
    +  return this.$do(matchingExpectation, args);
    +};
    +
    +
    +/** @override */
    +goog.testing.LooseMock.prototype.$reset = function() {
    +  goog.testing.LooseMock.superClass_.$reset.call(this);
    +
    +  this.$expectations_ = new goog.structs.Map();
    +  this.$calls_ = [];
    +};
    +
    +
    +/** @override */
    +goog.testing.LooseMock.prototype.$replay = function() {
    +  goog.testing.LooseMock.superClass_.$replay.call(this);
    +
    +  // Verify that there are no expectations that can never be reached.
    +  // This can't catch every situation, but it is a decent sanity check
    +  // and it's similar to the behavior of EasyMock in java.
    +  var collections = this.$expectations_.getValues();
    +  for (var i = 0; i < collections.length; i++) {
    +    var expectations = collections[i].getExpectations();
    +    for (var j = 0; j < expectations.length; j++) {
    +      var expectation = expectations[j];
    +      // If this expectation can be called infinite times, then
    +      // check if any subsequent expectation has the exact same
    +      // argument list.
    +      if (!isFinite(expectation.maxCalls)) {
    +        for (var k = j + 1; k < expectations.length; k++) {
    +          var laterExpectation = expectations[k];
    +          if (laterExpectation.minCalls > 0 &&
    +              goog.array.equals(expectation.argumentList,
    +                  laterExpectation.argumentList)) {
    +            var name = expectation.name;
    +            var argsString = this.$argumentsAsString(expectation.argumentList);
    +            this.$throwException([
    +              'Expected call to ', name, ' with arguments ', argsString,
    +              ' has an infinite max number of calls; can\'t expect an',
    +              ' identical call later with a positive min number of calls'
    +            ].join(''));
    +          }
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.testing.LooseMock.prototype.$verify = function() {
    +  goog.testing.LooseMock.superClass_.$verify.call(this);
    +  var collections = this.$expectations_.getValues();
    +
    +  for (var i = 0; i < collections.length; i++) {
    +    var expectations = collections[i].getExpectations();
    +    for (var j = 0; j < expectations.length; j++) {
    +      var expectation = expectations[j];
    +      if (expectation.actualCalls > expectation.maxCalls) {
    +        this.$throwException('Too many calls to ' + expectation.name +
    +            '\nExpected: ' + expectation.maxCalls + ' but was: ' +
    +            expectation.actualCalls);
    +      } else if (expectation.actualCalls < expectation.minCalls) {
    +        this.$throwException('Not enough calls to ' + expectation.name +
    +            '\nExpected: ' + expectation.minCalls + ' but was: ' +
    +            expectation.actualCalls);
    +      }
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.html b/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.html
    new file mode 100644
    index 0000000000..3950b5bcba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.LooseMock
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.LooseMockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.js b/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.js
    new file mode 100644
    index 0000000000..a3d4483cda
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/loosemock_test.js
    @@ -0,0 +1,342 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.LooseMockTest');
    +goog.setTestOnly('goog.testing.LooseMockTest');
    +
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +
    +// The object that we will be mocking
    +var RealObject = function() {
    +};
    +
    +RealObject.prototype.a = function() {
    +  fail('real object should never be called');
    +};
    +
    +RealObject.prototype.b = function() {
    +  fail('real object should never be called');
    +};
    +
    +var mock;
    +
    +var stubs;
    +
    +function setUp() {
    +  var obj = new RealObject();
    +  mock = new goog.testing.LooseMock(obj);
    +  stubs = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +/*
    + * Calling this method evades the logTestFailure in
    + * goog.testing.Mock.prototype.$recordAndThrow, so it doesn't look like the
    + * test failed.
    + */
    +function silenceFailureLogging() {
    +  if (goog.global['G_testRunner']) {
    +    stubs.set(goog.global['G_testRunner'],
    +        'logTestFailure', goog.nullFunction);
    +  }
    +}
    +
    +function unsilenceFailureLogging() {
    +  stubs.reset();
    +}
    +
    +
    +/**
    + * Version of assertThrows that doesn't log the exception generated by the
    + * mocks under test.
    + * TODO: would be nice to check that a particular substring is in the thrown
    + * message so we know it's not something dumb like a syntax error
    + */
    +function assertThrowsQuiet(var_args) {
    +  silenceFailureLogging();
    +  assertThrows.apply(null, arguments);
    +  unsilenceFailureLogging();
    +}
    +
    +// Most of the basic functionality is tested in strictmock_test; these tests
    +// cover the cases where loose mocks are different from strict mocks
    +function testSimpleExpectations() {
    +  mock.a(5);
    +  mock.b();
    +  mock.$replay();
    +  mock.a(5);
    +  mock.b();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a();
    +  mock.b();
    +  mock.$replay();
    +  mock.b();
    +  mock.a();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a(5).$times(2);
    +  mock.a(5);
    +  mock.a(2);
    +  mock.$replay();
    +  mock.a(5);
    +  mock.a(5);
    +  mock.a(5);
    +  mock.a(2);
    +  mock.$verify();
    +}
    +
    +
    +function testMultipleExpectations() {
    +  mock.a().$returns(1);
    +  mock.a().$returns(2);
    +  mock.$replay();
    +  assertEquals(1, mock.a());
    +  assertEquals(2, mock.a());
    +  mock.$verify();
    +}
    +
    +
    +function testMultipleExpectationArgs() {
    +  mock.a('asdf').$anyTimes();
    +  mock.a('qwer').$anyTimes();
    +  mock.b().$times(3);
    +  mock.$replay();
    +  mock.a('asdf');
    +  mock.b();
    +  mock.a('asdf');
    +  mock.a('qwer');
    +  mock.b();
    +  mock.a('qwer');
    +  mock.b();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a('asdf').$anyTimes();
    +  mock.a('qwer').$anyTimes();
    +  mock.$replay();
    +  mock.a('asdf');
    +  mock.a('qwer');
    +  goog.bind(mock.a, mock, 'asdf');
    +  goog.bind(mock.$verify, mock);
    +}
    +
    +function testSameMethodOutOfOrder() {
    +  mock.a('foo').$returns(1);
    +  mock.a('bar').$returns(2);
    +  mock.$replay();
    +  assertEquals(2, mock.a('bar'));
    +  assertEquals(1, mock.a('foo'));
    +}
    +
    +function testSameMethodDifferentReturnValues() {
    +  mock.a('foo').$returns(1).$times(2);
    +  mock.a('foo').$returns(3);
    +  mock.a('bar').$returns(2);
    +  mock.$replay();
    +  assertEquals(1, mock.a('foo'));
    +  assertEquals(2, mock.a('bar'));
    +  assertEquals(1, mock.a('foo'));
    +  assertEquals(3, mock.a('foo'));
    +  assertThrowsQuiet(function() {
    +    mock.a('foo');
    +    mock.$verify();
    +  });
    +}
    +
    +function testSameMethodBrokenExpectations() {
    +  // This is a weird corner case.
    +  // No way to ever make this verify no matter what you call after replaying,
    +  // because the second expectation of mock.a('foo') will be masked by
    +  // the first expectation that can be called any number of times, and so we
    +  // can never satisfy that second expectation.
    +  mock.a('foo').$returns(1).$anyTimes();
    +  mock.a('bar').$returns(2);
    +  mock.a('foo').$returns(3);
    +
    +  // LooseMock can detect this case and fail on $replay.
    +  assertThrowsQuiet(goog.bind(mock.$replay, mock));
    +  mock.$reset();
    +
    +  // This is a variant of the corner case above, but it's harder to determine
    +  // that the expectation to mock.a('bar') can never be satisfied. So we don't
    +  // fail on $replay, but we do fail on $verify.
    +  mock.a(goog.testing.mockmatchers.isString).$returns(1).$anyTimes();
    +  mock.a('bar').$returns(2);
    +  mock.$replay();
    +
    +  assertEquals(1, mock.a('foo'));
    +  assertEquals(1, mock.a('bar'));
    +  assertThrowsQuiet(goog.bind(mock.$verify, mock));
    +}
    +
    +function testSameMethodMultipleAnyTimes() {
    +  mock.a('foo').$returns(1).$anyTimes();
    +  mock.a('foo').$returns(2).$anyTimes();
    +  mock.$replay();
    +  assertEquals(1, mock.a('foo'));
    +  assertEquals(1, mock.a('foo'));
    +  assertEquals(1, mock.a('foo'));
    +  // Note we'll never return 2 but that's ok.
    +  mock.$verify();
    +}
    +
    +function testFailingFast() {
    +  mock.a().$anyTimes();
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  assertThrowsQuiet(goog.bind(mock.b, mock));
    +  mock.$reset();
    +
    +  // too many
    +  mock.a();
    +  mock.b();
    +  mock.$replay();
    +  mock.a();
    +  mock.b();
    +
    +  var message;
    +  silenceFailureLogging();
    +  try {
    +    mock.a();
    +  } catch (e) {
    +    message = e.message;
    +  }
    +  unsilenceFailureLogging();
    +
    +  assertTrue('No exception thrown on unexpected call', goog.isDef(message));
    +  assertContains('Too many calls to a', message);
    +}
    +
    +function testTimes() {
    +  mock.a().$times(3);
    +  mock.b().$times(2);
    +  mock.$replay();
    +  mock.a();
    +  mock.b();
    +  mock.b();
    +  mock.a();
    +  mock.a();
    +  mock.$verify();
    +}
    +
    +
    +function testFailingSlow() {
    +  // not enough
    +  mock.a().$times(3);
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  assertThrowsQuiet(goog.bind(mock.$verify, mock));
    +
    +  mock.$reset();
    +
    +  // not enough, interleaved order
    +  mock.a().$times(3);
    +  mock.b().$times(3);
    +  mock.$replay();
    +  mock.a();
    +  mock.b();
    +  mock.a();
    +  mock.b();
    +  assertThrowsQuiet(goog.bind(mock.$verify, mock));
    +
    +  mock.$reset();
    +  // bad args
    +  mock.a('asdf').$anyTimes();
    +  mock.$replay();
    +  mock.a('asdf');
    +  assertThrowsQuiet(goog.bind(mock.a, mock, 'qwert'));
    +  assertThrowsQuiet(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testArgsAndReturns() {
    +  mock.a('asdf').$atLeastOnce().$returns(5);
    +  mock.b('qwer').$times(2).$returns(3);
    +  mock.$replay();
    +  assertEquals(5, mock.a('asdf'));
    +  assertEquals(3, mock.b('qwer'));
    +  assertEquals(5, mock.a('asdf'));
    +  assertEquals(5, mock.a('asdf'));
    +  assertEquals(3, mock.b('qwer'));
    +  mock.$verify();
    +}
    +
    +
    +function testThrows() {
    +  mock.a().$throws('exception!');
    +  mock.$replay();
    +  assertThrowsQuiet(goog.bind(mock.a, mock));
    +  mock.$verify();
    +}
    +
    +
    +function testDoes() {
    +  mock.a(1, 2).$does(function(a, b) {return a + b;});
    +  mock.$replay();
    +  assertEquals('Mock should call the function', 3, mock.a(1, 2));
    +  mock.$verify();
    +}
    +
    +function testIgnoresExtraCalls() {
    +  mock = new goog.testing.LooseMock(RealObject, true);
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  mock.b(); // doesn't throw
    +  mock.$verify();
    +}
    +
    +function testSkipAnyTimes() {
    +  mock = new goog.testing.LooseMock(RealObject);
    +  mock.a(1).$anyTimes();
    +  mock.a(2).$anyTimes();
    +  mock.a(3).$anyTimes();
    +  mock.$replay();
    +  mock.a(1);
    +  mock.a(3);
    +  mock.$verify();
    +}
    +
    +function testErrorMessageForBadArgs() {
    +  mock.a();
    +  mock.$anyTimes();
    +
    +  mock.$replay();
    +
    +  var message;
    +  silenceFailureLogging();
    +  try {
    +    mock.a('a');
    +  } catch (e) {
    +    message = e.message;
    +  }
    +  unsilenceFailureLogging();
    +
    +  assertTrue('No exception thrown on verify', goog.isDef(message));
    +  assertContains('Bad arguments to a()', message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessagechannel.js b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessagechannel.js
    new file mode 100644
    index 0000000000..085587a794
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessagechannel.js
    @@ -0,0 +1,80 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock MessageChannel implementation that can receive fake
    + * messages and test that the right messages are sent.
    + *
    + */
    +
    +
    +goog.provide('goog.testing.messaging.MockMessageChannel');
    +
    +goog.require('goog.messaging.AbstractChannel');
    +goog.require('goog.testing.asserts');
    +
    +
    +
    +/**
    + * Class for unit-testing code that communicates over a MessageChannel.
    + * @param {goog.testing.MockControl} mockControl The mock control used to create
    + *   the method mock for #send.
    + * @extends {goog.messaging.AbstractChannel}
    + * @constructor
    + * @final
    + */
    +goog.testing.messaging.MockMessageChannel = function(mockControl) {
    +  goog.testing.messaging.MockMessageChannel.base(this, 'constructor');
    +
    +  /**
    +   * Whether the channel has been disposed.
    +   * @type {boolean}
    +   */
    +  this.disposed = false;
    +
    +  mockControl.createMethodMock(this, 'send');
    +};
    +goog.inherits(goog.testing.messaging.MockMessageChannel,
    +              goog.messaging.AbstractChannel);
    +
    +
    +/**
    + * A mock send function. Actually an instance of
    + * {@link goog.testing.FunctionMock}.
    + * @param {string} serviceName The name of the remote service to run.
    + * @param {string|!Object} payload The payload to send to the remote page.
    + * @override
    + */
    +goog.testing.messaging.MockMessageChannel.prototype.send = function(
    +    serviceName, payload) {};
    +
    +
    +/**
    + * Sets a flag indicating that this is disposed.
    + * @override
    + */
    +goog.testing.messaging.MockMessageChannel.prototype.dispose = function() {
    +  this.disposed = true;
    +};
    +
    +
    +/**
    + * Mocks the receipt of a message. Passes the payload the appropriate service.
    + * @param {string} serviceName The service to run.
    + * @param {string|!Object} payload The argument to pass to the service.
    + */
    +goog.testing.messaging.MockMessageChannel.prototype.receive = function(
    +    serviceName, payload) {
    +  this.deliver(serviceName, payload);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageevent.js b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageevent.js
    new file mode 100644
    index 0000000000..474bcd1f66
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageevent.js
    @@ -0,0 +1,102 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A simple mock class for imitating HTML5 MessageEvents.
    + *
    + */
    +
    +goog.provide('goog.testing.messaging.MockMessageEvent');
    +
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventType');
    +goog.require('goog.testing.events.Event');
    +
    +
    +
    +/**
    + * Creates a new fake MessageEvent.
    + *
    + * @param {*} data The data of the message.
    + * @param {string=} opt_origin The origin of the message, for server-sent and
    + *     cross-document events.
    + * @param {string=} opt_lastEventId The last event ID, for server-sent events.
    + * @param {Window=} opt_source The proxy for the source window, for
    + *     cross-document events.
    + * @param {Array<MessagePort>=} opt_ports The Array of ports sent with the
    + *     message, for cross-document and channel events.
    + * @extends {goog.testing.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.testing.messaging.MockMessageEvent = function(
    +    data, opt_origin, opt_lastEventId, opt_source, opt_ports) {
    +  goog.testing.messaging.MockMessageEvent.base(
    +      this, 'constructor', goog.events.EventType.MESSAGE);
    +
    +  /**
    +   * The data of the message.
    +   * @type {*}
    +   */
    +  this.data = data;
    +
    +  /**
    +   * The origin of the message, for server-sent and cross-document events.
    +   * @type {?string}
    +   */
    +  this.origin = opt_origin || null;
    +
    +  /**
    +   * The last event ID, for server-sent events.
    +   * @type {?string}
    +   */
    +  this.lastEventId = opt_lastEventId || null;
    +
    +  /**
    +   * The proxy for the source window, for cross-document events.
    +   * @type {Window}
    +   */
    +  this.source = opt_source || null;
    +
    +  /**
    +   * The Array of ports sent with the message, for cross-document and channel
    +   * events.
    +   * @type {Array<!MessagePort>}
    +   */
    +  this.ports = opt_ports || null;
    +};
    +goog.inherits(
    +    goog.testing.messaging.MockMessageEvent, goog.testing.events.Event);
    +
    +
    +/**
    + * Wraps a new fake MessageEvent in a BrowserEvent, like how a real MessageEvent
    + * would be wrapped.
    + *
    + * @param {*} data The data of the message.
    + * @param {string=} opt_origin The origin of the message, for server-sent and
    + *     cross-document events.
    + * @param {string=} opt_lastEventId The last event ID, for server-sent events.
    + * @param {Window=} opt_source The proxy for the source window, for
    + *     cross-document events.
    + * @param {Array<MessagePort>=} opt_ports The Array of ports sent with the
    + *     message, for cross-document and channel events.
    + * @return {!goog.events.BrowserEvent} The wrapping event.
    + */
    +goog.testing.messaging.MockMessageEvent.wrap = function(
    +    data, opt_origin, opt_lastEventId, opt_source, opt_ports) {
    +  return new goog.events.BrowserEvent(
    +      new goog.testing.messaging.MockMessageEvent(
    +          data, opt_origin, opt_lastEventId, opt_source, opt_ports));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageport.js b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageport.js
    new file mode 100644
    index 0000000000..b99ea0da48
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockmessageport.js
    @@ -0,0 +1,86 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A simple dummy class for representing message ports in tests.
    + *
    + */
    +
    +goog.provide('goog.testing.messaging.MockMessagePort');
    +
    +goog.require('goog.events.EventTarget');
    +
    +
    +
    +/**
    + * Class for unit-testing code that uses MessagePorts.
    + * @param {*} id An opaque identifier, used because message ports otherwise have
    + *     no distinguishing characteristics.
    + * @param {goog.testing.MockControl} mockControl The mock control used to create
    + *     the method mock for #postMessage.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.testing.messaging.MockMessagePort = function(id, mockControl) {
    +  goog.testing.messaging.MockMessagePort.base(this, 'constructor');
    +
    +  /**
    +   * An opaque identifier, used because message ports otherwise have no
    +   * distinguishing characteristics.
    +   * @type {*}
    +   */
    +  this.id = id;
    +
    +  /**
    +   * Whether or not the port has been started.
    +   * @type {boolean}
    +   */
    +  this.started = false;
    +
    +  /**
    +   * Whether or not the port has been closed.
    +   * @type {boolean}
    +   */
    +  this.closed = false;
    +
    +  mockControl.createMethodMock(this, 'postMessage');
    +};
    +goog.inherits(goog.testing.messaging.MockMessagePort, goog.events.EventTarget);
    +
    +
    +/**
    + * A mock postMessage funciton. Actually an instance of
    + * {@link goog.testing.FunctionMock}.
    + * @param {*} message The message to send.
    + * @param {Array<MessagePort>=} opt_ports Ports to send with the message.
    + */
    +goog.testing.messaging.MockMessagePort.prototype.postMessage = function(
    +    message, opt_ports) {};
    +
    +
    +/**
    + * Starts the port.
    + */
    +goog.testing.messaging.MockMessagePort.prototype.start = function() {
    +  this.started = true;
    +};
    +
    +
    +/**
    + * Closes the port.
    + */
    +goog.testing.messaging.MockMessagePort.prototype.close = function() {
    +  this.closed = true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/messaging/mockportnetwork.js b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockportnetwork.js
    new file mode 100644
    index 0000000000..3ef9062cda
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/messaging/mockportnetwork.js
    @@ -0,0 +1,66 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A fake PortNetwork implementation that simply produces
    + * MockMessageChannels for all ports.
    + *
    + */
    +
    +goog.provide('goog.testing.messaging.MockPortNetwork');
    +
    +goog.require('goog.messaging.PortNetwork'); // interface
    +goog.require('goog.testing.messaging.MockMessageChannel');
    +
    +
    +
    +/**
    + * The fake PortNetwork.
    + *
    + * @param {!goog.testing.MockControl} mockControl The mock control for creating
    + *     the mock message channels.
    + * @constructor
    + * @implements {goog.messaging.PortNetwork}
    + * @final
    + */
    +goog.testing.messaging.MockPortNetwork = function(mockControl) {
    +  /**
    +   * The mock control for creating mock message channels.
    +   * @type {!goog.testing.MockControl}
    +   * @private
    +   */
    +  this.mockControl_ = mockControl;
    +
    +  /**
    +   * The mock ports that have been created.
    +   * @type {!Object<!goog.testing.messaging.MockMessageChannel>}
    +   * @private
    +   */
    +  this.ports_ = {};
    +};
    +
    +
    +/**
    + * Get the mock port with the given name.
    + * @param {string} name The name of the port to get.
    + * @return {!goog.testing.messaging.MockMessageChannel} The mock port.
    + * @override
    + */
    +goog.testing.messaging.MockPortNetwork.prototype.dial = function(name) {
    +  if (!(name in this.ports_)) {
    +    this.ports_[name] =
    +        new goog.testing.messaging.MockMessageChannel(this.mockControl_);
    +  }
    +  return this.ports_[name];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mock.js b/mapbox-gl/closure-library/closure/goog/testing/mock.js
    new file mode 100644
    index 0000000000..c832ac6649
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mock.js
    @@ -0,0 +1,645 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file defines base classes used for creating mocks in
    + * JavaScript. The API was inspired by EasyMock.
    + *
    + * The basic API is:
    + * <ul>
    + *   <li>Create an object to be mocked
    + *   <li>Create a mock object, passing in the above object to the constructor
    + *   <li>Set expectations by calling methods on the mock object
    + *   <li>Call $replay() on the mock object
    + *   <li>Pass the mock to code that will make real calls on it
    + *   <li>Call $verify() to make sure that expectations were met
    + * </ul>
    + *
    + * For examples, please see the unit tests for LooseMock and StrictMock.
    + *
    + * Still TODO
    + *   implement better (and pluggable) argument matching
    + *   Have the exceptions for LooseMock show the number of expected/actual calls
    + *   loose and strict mocks share a lot of code - move it to the base class
    + *
    + */
    +
    +goog.provide('goog.testing.Mock');
    +goog.provide('goog.testing.MockExpectation');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('goog.testing.JsUnitException');
    +goog.require('goog.testing.MockInterface');
    +goog.require('goog.testing.mockmatchers');
    +
    +
    +
    +/**
    + * This is a class that represents an expectation.
    + * @param {string} name The name of the method for this expectation.
    + * @constructor
    + * @final
    + */
    +goog.testing.MockExpectation = function(name) {
    +  /**
    +   * The name of the method that is expected to be called.
    +   * @type {string}
    +   */
    +  this.name = name;
    +
    +  /**
    +   * An array of error messages for expectations not met.
    +   * @type {Array<string>}
    +   */
    +  this.errorMessages = [];
    +};
    +
    +
    +/**
    + * The minimum number of times this method should be called.
    + * @type {number}
    + */
    +goog.testing.MockExpectation.prototype.minCalls = 1;
    +
    +
    +/**
    +  * The maximum number of times this method should be called.
    +  * @type {number}
    +  */
    +goog.testing.MockExpectation.prototype.maxCalls = 1;
    +
    +
    +/**
    + * The value that this method should return.
    + * @type {*}
    + */
    +goog.testing.MockExpectation.prototype.returnValue;
    +
    +
    +/**
    + * The value that will be thrown when the method is called
    + * @type {*}
    + */
    +goog.testing.MockExpectation.prototype.exceptionToThrow;
    +
    +
    +/**
    + * The arguments that are expected to be passed to this function
    + * @type {Array<*>}
    + */
    +goog.testing.MockExpectation.prototype.argumentList;
    +
    +
    +/**
    + * The number of times this method is called by real code.
    + * @type {number}
    + */
    +goog.testing.MockExpectation.prototype.actualCalls = 0;
    +
    +
    +/**
    + * The number of times this method is called during the verification phase.
    + * @type {number}
    + */
    +goog.testing.MockExpectation.prototype.verificationCalls = 0;
    +
    +
    +/**
    + * The function which will be executed when this method is called.
    + * Method arguments will be passed to this function, and return value
    + * of this function will be returned by the method.
    + * @type {Function}
    + */
    +goog.testing.MockExpectation.prototype.toDo;
    +
    +
    +/**
    + * Allow expectation failures to include messages.
    + * @param {string} message The failure message.
    + */
    +goog.testing.MockExpectation.prototype.addErrorMessage = function(message) {
    +  this.errorMessages.push(message);
    +};
    +
    +
    +/**
    + * Get the error messages seen so far.
    + * @return {string} Error messages separated by \n.
    + */
    +goog.testing.MockExpectation.prototype.getErrorMessage = function() {
    +  return this.errorMessages.join('\n');
    +};
    +
    +
    +/**
    + * Get how many error messages have been seen so far.
    + * @return {number} Count of error messages.
    + */
    +goog.testing.MockExpectation.prototype.getErrorMessageCount = function() {
    +  return this.errorMessages.length;
    +};
    +
    +
    +
    +/**
    + * The base class for a mock object.
    + * @param {Object|Function} objectToMock The object that should be mocked, or
    + *    the constructor of an object to mock.
    + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
    + *     a mock should be constructed from the static functions of a class.
    + * @param {boolean=} opt_createProxy An optional argument denoting that
    + *     a proxy for the target mock should be created.
    + * @constructor
    + * @implements {goog.testing.MockInterface}
    + */
    +goog.testing.Mock = function(objectToMock, opt_mockStaticMethods,
    +    opt_createProxy) {
    +  if (!goog.isObject(objectToMock) && !goog.isFunction(objectToMock)) {
    +    throw new Error('objectToMock must be an object or constructor.');
    +  }
    +  if (opt_createProxy && !opt_mockStaticMethods &&
    +      goog.isFunction(objectToMock)) {
    +    /**
    + * @constructor
    + * @final
    + */
    +    var tempCtor = function() {};
    +    goog.inherits(tempCtor, objectToMock);
    +    this.$proxy = new tempCtor();
    +  } else if (opt_createProxy && opt_mockStaticMethods &&
    +      goog.isFunction(objectToMock)) {
    +    throw Error('Cannot create a proxy when opt_mockStaticMethods is true');
    +  } else if (opt_createProxy && !goog.isFunction(objectToMock)) {
    +    throw Error('Must have a constructor to create a proxy');
    +  }
    +
    +  if (goog.isFunction(objectToMock) && !opt_mockStaticMethods) {
    +    this.$initializeFunctions_(objectToMock.prototype);
    +  } else {
    +    this.$initializeFunctions_(objectToMock);
    +  }
    +  this.$argumentListVerifiers_ = {};
    +};
    +
    +
    +/**
    + * Option that may be passed when constructing function, method, and
    + * constructor mocks. Indicates that the expected calls should be accepted in
    + * any order.
    + * @const
    + * @type {number}
    + */
    +goog.testing.Mock.LOOSE = 1;
    +
    +
    +/**
    + * Option that may be passed when constructing function, method, and
    + * constructor mocks. Indicates that the expected calls should be accepted in
    + * the recorded order only.
    + * @const
    + * @type {number}
    + */
    +goog.testing.Mock.STRICT = 0;
    +
    +
    +/**
    + * This array contains the name of the functions that are part of the base
    + * Object prototype.
    + * Basically a copy of goog.object.PROTOTYPE_FIELDS_.
    + * @const
    + * @type {!Array<string>}
    + * @private
    + */
    +goog.testing.Mock.PROTOTYPE_FIELDS_ = [
    +  'constructor',
    +  'hasOwnProperty',
    +  'isPrototypeOf',
    +  'propertyIsEnumerable',
    +  'toLocaleString',
    +  'toString',
    +  'valueOf'
    +];
    +
    +
    +/**
    + * A proxy for the mock.  This can be used for dependency injection in lieu of
    + * the mock if the test requires a strict instanceof check.
    + * @type {Object}
    + */
    +goog.testing.Mock.prototype.$proxy = null;
    +
    +
    +/**
    + * Map of argument name to optional argument list verifier function.
    + * @type {Object}
    + */
    +goog.testing.Mock.prototype.$argumentListVerifiers_;
    +
    +
    +/**
    + * Whether or not we are in recording mode.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.Mock.prototype.$recording_ = true;
    +
    +
    +/**
    + * The expectation currently being created. All methods that modify the
    + * current expectation return the Mock object for easy chaining, so this is
    + * where we keep track of the expectation that's currently being modified.
    + * @type {goog.testing.MockExpectation}
    + * @protected
    + */
    +goog.testing.Mock.prototype.$pendingExpectation;
    +
    +
    +/**
    + * First exception thrown by this mock; used in $verify.
    + * @type {Object}
    + * @private
    + */
    +goog.testing.Mock.prototype.$threwException_ = null;
    +
    +
    +/**
    + * Initializes the functions on the mock object.
    + * @param {Object} objectToMock The object being mocked.
    + * @private
    + */
    +goog.testing.Mock.prototype.$initializeFunctions_ = function(objectToMock) {
    +  // Gets the object properties.
    +  var enumerableProperties = goog.object.getKeys(objectToMock);
    +
    +  // The non enumerable properties are added if they override the ones in the
    +  // Object prototype. This is due to the fact that IE8 does not enumerate any
    +  // of the prototype Object functions even when overriden and mocking these is
    +  // sometimes needed.
    +  for (var i = 0; i < goog.testing.Mock.PROTOTYPE_FIELDS_.length; i++) {
    +    var prop = goog.testing.Mock.PROTOTYPE_FIELDS_[i];
    +    // Look at b/6758711 if you're considering adding ALL properties to ALL
    +    // mocks.
    +    if (objectToMock[prop] !== Object.prototype[prop]) {
    +      enumerableProperties.push(prop);
    +    }
    +  }
    +
    +  // Adds the properties to the mock.
    +  for (var i = 0; i < enumerableProperties.length; i++) {
    +    var prop = enumerableProperties[i];
    +    if (typeof objectToMock[prop] == 'function') {
    +      this[prop] = goog.bind(this.$mockMethod, this, prop);
    +      if (this.$proxy) {
    +        this.$proxy[prop] = goog.bind(this.$mockMethod, this, prop);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Registers a verfifier function to use when verifying method argument lists.
    + * @param {string} methodName The name of the method for which the verifierFn
    + *     should be used.
    + * @param {Function} fn Argument list verifier function.  Should take 2 argument
    + *     arrays as arguments, and return true if they are considered equivalent.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$registerArgumentListVerifier = function(methodName,
    +                                                                     fn) {
    +  this.$argumentListVerifiers_[methodName] = fn;
    +  return this;
    +};
    +
    +
    +/**
    + * The function that replaces all methods on the mock object.
    + * @param {string} name The name of the method being mocked.
    + * @return {*} In record mode, returns the mock object. In replay mode, returns
    + *    whatever the creator of the mock set as the return value.
    + */
    +goog.testing.Mock.prototype.$mockMethod = function(name) {
    +  try {
    +    // Shift off the name argument so that args contains the arguments to
    +    // the mocked method.
    +    var args = goog.array.slice(arguments, 1);
    +    if (this.$recording_) {
    +      this.$pendingExpectation = new goog.testing.MockExpectation(name);
    +      this.$pendingExpectation.argumentList = args;
    +      this.$recordExpectation();
    +      return this;
    +    } else {
    +      return this.$recordCall(name, args);
    +    }
    +  } catch (ex) {
    +    this.$recordAndThrow(ex);
    +  }
    +};
    +
    +
    +/**
    + * Records the currently pending expectation, intended to be overridden by a
    + * subclass.
    + * @protected
    + */
    +goog.testing.Mock.prototype.$recordExpectation = function() {};
    +
    +
    +/**
    + * Records an actual method call, intended to be overridden by a
    + * subclass. The subclass must find the pending expectation and return the
    + * correct value.
    + * @param {string} name The name of the method being called.
    + * @param {Array<?>} args The arguments to the method.
    + * @return {*} The return expected by the mock.
    + * @protected
    + */
    +goog.testing.Mock.prototype.$recordCall = function(name, args) {
    +  return undefined;
    +};
    +
    +
    +/**
    + * If the expectation expects to throw, this method will throw.
    + * @param {goog.testing.MockExpectation} expectation The expectation.
    + */
    +goog.testing.Mock.prototype.$maybeThrow = function(expectation) {
    +  if (typeof expectation.exceptionToThrow != 'undefined') {
    +    throw expectation.exceptionToThrow;
    +  }
    +};
    +
    +
    +/**
    + * If this expectation defines a function to be called,
    + * it will be called and its result will be returned.
    + * Otherwise, if the expectation expects to throw, it will throw.
    + * Otherwise, this method will return defined value.
    + * @param {goog.testing.MockExpectation} expectation The expectation.
    + * @param {Array<?>} args The arguments to the method.
    + * @return {*} The return value expected by the mock.
    + */
    +goog.testing.Mock.prototype.$do = function(expectation, args) {
    +  if (typeof expectation.toDo == 'undefined') {
    +    this.$maybeThrow(expectation);
    +    return expectation.returnValue;
    +  } else {
    +    return expectation.toDo.apply(this, args);
    +  }
    +};
    +
    +
    +/**
    + * Specifies a return value for the currently pending expectation.
    + * @param {*} val The return value.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$returns = function(val) {
    +  this.$pendingExpectation.returnValue = val;
    +  return this;
    +};
    +
    +
    +/**
    + * Specifies a value for the currently pending expectation to throw.
    + * @param {*} val The value to throw.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$throws = function(val) {
    +  this.$pendingExpectation.exceptionToThrow = val;
    +  return this;
    +};
    +
    +
    +/**
    + * Specifies a function to call for currently pending expectation.
    + * Note, that using this method overrides declarations made
    + * using $returns() and $throws() methods.
    + * @param {Function} func The function to call.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$does = function(func) {
    +  this.$pendingExpectation.toDo = func;
    +  return this;
    +};
    +
    +
    +/**
    + * Allows the expectation to be called 0 or 1 times.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$atMostOnce = function() {
    +  this.$pendingExpectation.minCalls = 0;
    +  this.$pendingExpectation.maxCalls = 1;
    +  return this;
    +};
    +
    +
    +/**
    + * Allows the expectation to be called any number of times, as long as it's
    + * called once.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$atLeastOnce = function() {
    +  this.$pendingExpectation.maxCalls = Infinity;
    +  return this;
    +};
    +
    +
    +/**
    + * Allows the expectation to be called exactly once.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$once = function() {
    +  this.$pendingExpectation.minCalls = 1;
    +  this.$pendingExpectation.maxCalls = 1;
    +  return this;
    +};
    +
    +
    +/**
    + * Disallows the expectation from being called.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$never = function() {
    +  this.$pendingExpectation.minCalls = 0;
    +  this.$pendingExpectation.maxCalls = 0;
    +  return this;
    +};
    +
    +
    +/**
    + * Allows the expectation to be called any number of times.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$anyTimes = function() {
    +  this.$pendingExpectation.minCalls = 0;
    +  this.$pendingExpectation.maxCalls = Infinity;
    +  return this;
    +};
    +
    +
    +/**
    + * Specifies the number of times the expectation should be called.
    + * @param {number} times The number of times this method will be called.
    + * @return {!goog.testing.Mock} This mock object.
    + */
    +goog.testing.Mock.prototype.$times = function(times) {
    +  this.$pendingExpectation.minCalls = times;
    +  this.$pendingExpectation.maxCalls = times;
    +  return this;
    +};
    +
    +
    +/**
    + * Switches from recording to replay mode.
    + * @override
    + */
    +goog.testing.Mock.prototype.$replay = function() {
    +  this.$recording_ = false;
    +};
    +
    +
    +/**
    + * Resets the state of this mock object. This clears all pending expectations
    + * without verifying, and puts the mock in recording mode.
    + * @override
    + */
    +goog.testing.Mock.prototype.$reset = function() {
    +  this.$recording_ = true;
    +  this.$threwException_ = null;
    +  delete this.$pendingExpectation;
    +};
    +
    +
    +/**
    + * Throws an exception and records that an exception was thrown.
    + * @param {string} comment A short comment about the exception.
    + * @param {?string=} opt_message A longer message about the exception.
    + * @throws {Object} JsUnitException object.
    + * @protected
    + */
    +goog.testing.Mock.prototype.$throwException = function(comment, opt_message) {
    +  this.$recordAndThrow(new goog.testing.JsUnitException(comment, opt_message));
    +};
    +
    +
    +/**
    + * Throws an exception and records that an exception was thrown.
    + * @param {Object} ex Exception.
    + * @throws {Object} #ex.
    + * @protected
    + */
    +goog.testing.Mock.prototype.$recordAndThrow = function(ex) {
    +  // If it's an assert exception, record it.
    +  if (ex['isJsUnitException']) {
    +    var testRunner = goog.global['G_testRunner'];
    +    if (testRunner) {
    +      var logTestFailureFunction = testRunner['logTestFailure'];
    +      if (logTestFailureFunction) {
    +        logTestFailureFunction.call(testRunner, ex);
    +      }
    +    }
    +
    +    if (!this.$threwException_) {
    +      // Only remember first exception thrown.
    +      this.$threwException_ = ex;
    +    }
    +  }
    +  throw ex;
    +};
    +
    +
    +/**
    + * Verify that all of the expectations were met. Should be overridden by
    + * subclasses.
    + * @override
    + */
    +goog.testing.Mock.prototype.$verify = function() {
    +  if (this.$threwException_) {
    +    throw this.$threwException_;
    +  }
    +};
    +
    +
    +/**
    + * Verifies that a method call matches an expectation.
    + * @param {goog.testing.MockExpectation} expectation The expectation to check.
    + * @param {string} name The name of the called method.
    + * @param {Array<*>?} args The arguments passed to the mock.
    + * @return {boolean} Whether the call matches the expectation.
    + */
    +goog.testing.Mock.prototype.$verifyCall = function(expectation, name, args) {
    +  if (expectation.name != name) {
    +    return false;
    +  }
    +  var verifierFn =
    +      this.$argumentListVerifiers_.hasOwnProperty(expectation.name) ?
    +      this.$argumentListVerifiers_[expectation.name] :
    +      goog.testing.mockmatchers.flexibleArrayMatcher;
    +
    +  return verifierFn(expectation.argumentList, args, expectation);
    +};
    +
    +
    +/**
    + * Render the provided argument array to a string to help
    + * clients with debugging tests.
    + * @param {Array<*>?} args The arguments passed to the mock.
    + * @return {string} Human-readable string.
    + */
    +goog.testing.Mock.prototype.$argumentsAsString = function(args) {
    +  var retVal = [];
    +  for (var i = 0; i < args.length; i++) {
    +    try {
    +      retVal.push(goog.typeOf(args[i]));
    +    } catch (e) {
    +      retVal.push('[unknown]');
    +    }
    +  }
    +  return '(' + retVal.join(', ') + ')';
    +};
    +
    +
    +/**
    + * Throw an exception based on an incorrect method call.
    + * @param {string} name Name of method called.
    + * @param {Array<*>?} args Arguments passed to the mock.
    + * @param {goog.testing.MockExpectation=} opt_expectation Expected next call,
    + *     if any.
    + */
    +goog.testing.Mock.prototype.$throwCallException = function(name, args,
    +                                                           opt_expectation) {
    +  var errorStringBuffer = [];
    +  var actualArgsString = this.$argumentsAsString(args);
    +  var expectedArgsString = opt_expectation ?
    +      this.$argumentsAsString(opt_expectation.argumentList) : '';
    +
    +  if (opt_expectation && opt_expectation.name == name) {
    +    errorStringBuffer.push('Bad arguments to ', name, '().\n',
    +                           'Actual: ', actualArgsString, '\n',
    +                           'Expected: ', expectedArgsString, '\n',
    +                           opt_expectation.getErrorMessage());
    +  } else {
    +    errorStringBuffer.push('Unexpected call to ', name,
    +                           actualArgsString, '.');
    +    if (opt_expectation) {
    +      errorStringBuffer.push('\nNext expected call was to ',
    +                             opt_expectation.name,
    +                             expectedArgsString);
    +    }
    +  }
    +  this.$throwException(errorStringBuffer.join(''));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mock_test.html b/mapbox-gl/closure-library/closure/goog/testing/mock_test.html
    new file mode 100644
    index 0000000000..fd2f0a41db
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mock_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.Mock
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mock_test.js b/mapbox-gl/closure-library/closure/goog/testing/mock_test.js
    new file mode 100644
    index 0000000000..3b8d23aba8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mock_test.js
    @@ -0,0 +1,260 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockTest');
    +goog.setTestOnly('goog.testing.MockTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.testing');
    +goog.require('goog.testing.Mock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.MockExpectation');
    +goog.require('goog.testing.jsunit');
    +
    +// The object that we will be mocking
    +var RealObject = function() {
    +};
    +
    +RealObject.prototype.a = function() {
    +  fail('real object should never be called');
    +};
    +
    +RealObject.prototype.b = function() {
    +  fail('real object should never be called');
    +};
    +
    +var matchers = goog.testing.mockmatchers;
    +var mock;
    +
    +function setUp() {
    +  var obj = new RealObject();
    +  mock = new goog.testing.Mock(obj);
    +}
    +
    +function testMockErrorMessage() {
    +  var expectation = new goog.testing.MockExpectation('a');
    +  assertEquals(0, expectation.getErrorMessageCount());
    +  assertEquals('', expectation.getErrorMessage());
    +
    +  expectation.addErrorMessage('foo failed');
    +  assertEquals(1, expectation.getErrorMessageCount());
    +  assertEquals('foo failed', expectation.getErrorMessage());
    +
    +  expectation.addErrorMessage('bar failed');
    +  assertEquals(2, expectation.getErrorMessageCount());
    +  assertEquals('foo failed\nbar failed', expectation.getErrorMessage());
    +}
    +
    +function testVerifyArgumentList() {
    +  var expectation = new goog.testing.MockExpectation('a');
    +  assertEquals('', expectation.getErrorMessage());
    +
    +  // test single string arg
    +  expectation.argumentList = ['foo'];
    +  assertTrue(mock.$verifyCall(expectation, 'a', ['foo']));
    +
    +  // single numeric arg
    +  expectation.argumentList = [2];
    +  assertTrue(mock.$verifyCall(expectation, 'a', [2]));
    +
    +  // single object arg (using standard === comparison)
    +  var obj = {prop1: 'prop1', prop2: 2};
    +  expectation.argumentList = [obj];
    +  assertTrue(mock.$verifyCall(expectation, 'a', [obj]));
    +
    +  // make sure comparison succeeds if args are similar, but not ===
    +  var obj2 = {prop1: 'prop1', prop2: 2};
    +  expectation.argumentList = [obj];
    +  assertTrue(mock.$verifyCall(expectation, 'a', [obj2]));
    +  assertEquals('', expectation.getErrorMessage());
    +
    +  // multiple args
    +  expectation.argumentList = ['foo', 2, obj, obj2];
    +  assertTrue(mock.$verifyCall(expectation, 'a', ['foo', 2, obj, obj2]));
    +
    +  // test flexible arg matching.
    +  expectation.argumentList = ['foo', matchers.isNumber];
    +  assertTrue(mock.$verifyCall(expectation, 'a', ['foo', 1]));
    +
    +  expectation.argumentList = [new matchers.InstanceOf(RealObject)];
    +  assertTrue(mock.$verifyCall(expectation, 'a', [new RealObject()]));
    +}
    +
    +function testVerifyArgumentListForObjectMethods() {
    +  var expectation = new goog.testing.MockExpectation('toString');
    +  expectation.argumentList = [];
    +  assertTrue(mock.$verifyCall(expectation, 'toString', []));
    +}
    +
    +function testRegisterArgumentListVerifier() {
    +  var expectationA = new goog.testing.MockExpectation('a');
    +  var expectationB = new goog.testing.MockExpectation('b');
    +
    +  // Simple matcher that return true if all args are === equivalent.
    +  mock.$registerArgumentListVerifier('a', function(expectedArgs, args) {
    +    return goog.array.equals(expectedArgs, args, function(a, b) {
    +      return (a === b);
    +    });
    +  });
    +
    +  // test single string arg
    +  expectationA.argumentList = ['foo'];
    +  assertTrue(mock.$verifyCall(expectationA, 'a', ['foo']));
    +
    +  // single numeric arg
    +  expectationA.argumentList = [2];
    +  assertTrue(mock.$verifyCall(expectationA, 'a', [2]));
    +
    +  // single object arg (using standard === comparison)
    +  var obj = {prop1: 'prop1', prop2: 2};
    +  expectationA.argumentList = [obj];
    +  expectationB.argumentList = [obj];
    +  assertTrue(mock.$verifyCall(expectationA, 'a', [obj]));
    +  assertTrue(mock.$verifyCall(expectationB, 'b', [obj]));
    +
    +  // if args are similar, but not ===, then comparison should succeed
    +  // for method with registered object matcher, and fail for method without
    +  var obj2 = {prop1: 'prop1', prop2: 2};
    +  expectationA.argumentList = [obj];
    +  expectationB.argumentList = [obj];
    +  assertFalse(mock.$verifyCall(expectationA, 'a', [obj2]));
    +  assertTrue(mock.$verifyCall(expectationB, 'b', [obj2]));
    +
    +
    +  // multiple args, should fail for method with registered arg matcher,
    +  // and succeed for method without.
    +  expectationA.argumentList = ['foo', 2, obj, obj2];
    +  expectationB.argumentList = ['foo', 2, obj, obj2];
    +  assertFalse(mock.$verifyCall(expectationA, 'a', ['foo', 2, obj2, obj]));
    +  assertTrue(mock.$verifyCall(expectationB, 'b', ['foo', 2, obj2, obj]));
    +}
    +
    +
    +function testCreateProxy() {
    +  mock = new goog.testing.Mock(RealObject, false, true);
    +  assertTrue(mock.$proxy instanceof RealObject);
    +  assertThrows(function() {
    +    new goog.testing.Mock(RealObject, true, true);
    +  });
    +  assertThrows(function() {
    +    new goog.testing.Mock(1, false, true);
    +  });
    +}
    +
    +
    +function testValidConstructorArgument() {
    +  var someNamespace = { };
    +  assertThrows(function() {
    +    new goog.testing.Mock(someNamespace.RealObjectWithTypo);
    +  });
    +}
    +
    +
    +function testArgumentsAsString() {
    +  assertEquals('()', mock.$argumentsAsString([]));
    +  assertEquals('(string, number, object, null)',
    +               mock.$argumentsAsString(['red', 1, {}, null]));
    +}
    +
    +
    +function testThrowCallExceptionBadArgs() {
    +  var msg;
    +  mock.$throwException = function(m) {
    +    msg = m;
    +  };
    +
    +  mock.$throwCallException(
    +      'fn1', ['b'],
    +      { name: 'fn1',
    +        argumentList: ['c'],
    +        getErrorMessage: function() { return ''; } });
    +  assertContains(
    +      'Bad arguments to fn1().\nActual: (string)\nExpected: (string)', msg);
    +}
    +
    +function testThrowCallExceptionUnexpected() {
    +  var msg;
    +  mock.$throwException = function(m) {
    +    msg = m;
    +  };
    +
    +  mock.$throwCallException('fn1', ['b']);
    +  assertEquals('Unexpected call to fn1(string).', msg);
    +}
    +
    +function testThrowCallExceptionUnexpectedWithNext() {
    +  var msg;
    +  mock.$throwException = function(m) {
    +    msg = m;
    +  };
    +
    +  mock.$throwCallException(
    +      'fn1', ['b'],
    +      { name: 'fn2',
    +        argumentList: [3],
    +        getErrorMessage: function() { return ''; } });
    +  assertEquals(
    +      'Unexpected call to fn1(string).\n' +
    +      'Next expected call was to fn2(number)', msg);
    +}
    +
    +// This tests that base Object functions which are not enumerable in IE can
    +// be mocked correctly.
    +function testBindNonEnumerableFunctions() {
    +  // Create Foo and override non enumerable functions.
    +  var Foo = function() {};
    +  Foo.prototype.constructor = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.hasOwnProperty = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.isPrototypeOf = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.propertyIsEnumerable = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.toLocaleString = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.toString = function() {
    +    fail('real object should never be called');
    +  };
    +  Foo.prototype.valueOf = function() {
    +    fail('real object should never be called');
    +  };
    +
    +  // Create Mock and set $returns for toString.
    +  var mockControl = new goog.testing.MockControl();
    +  var mock = mockControl.createLooseMock(Foo);
    +  mock.constructor().$returns('constructor');
    +  mock.hasOwnProperty().$returns('hasOwnProperty');
    +  mock.isPrototypeOf().$returns('isPrototypeOf');
    +  mock.propertyIsEnumerable().$returns('propertyIsEnumerable');
    +  mock.toLocaleString().$returns('toLocaleString');
    +  mock.toString().$returns('toString');
    +  mock.valueOf().$returns('valueOf');
    +
    +  // Execute and assert that the Mock is working correctly.
    +  mockControl.$replayAll();
    +  assertEquals('constructor', mock.constructor());
    +  assertEquals('hasOwnProperty', mock.hasOwnProperty());
    +  assertEquals('isPrototypeOf', mock.isPrototypeOf());
    +  assertEquals('propertyIsEnumerable', mock.propertyIsEnumerable());
    +  assertEquals('toLocaleString', mock.toLocaleString());
    +  assertEquals('toString', mock.toString());
    +  assertEquals('valueOf', mock.valueOf());
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory.js b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory.js
    new file mode 100644
    index 0000000000..e4adc10383
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory.js
    @@ -0,0 +1,585 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file defines a factory that can be used to mock and
    + * replace an entire class.  This allows for mocks to be used effectively with
    + * "new" instead of having to inject all instances.  Essentially, a given class
    + * is replaced with a proxy to either a loose or strict mock.  Proxies locate
    + * the appropriate mock based on constructor arguments.
    + *
    + * The usage is:
    + * <ul>
    + *   <li>Create a mock with one of the provided methods with a specifc set of
    + *       constructor arguments
    + *   <li>Set expectations by calling methods on the mock object
    + *   <li>Call $replay() on the mock object
    + *   <li>Instantiate the object as normal
    + *   <li>Call $verify() to make sure that expectations were met
    + *   <li>Call reset on the factory to revert all classes back to their original
    + *       state
    + * </ul>
    + *
    + * For examples, please see the unit test.
    + *
    + */
    +
    +
    +goog.provide('goog.testing.MockClassFactory');
    +goog.provide('goog.testing.MockClassRecord');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.mockmatchers');
    +
    +
    +
    +/**
    + * A record that represents all the data associated with a mock replacement of
    + * a given class.
    + * @param {Object} namespace The namespace in which the mocked class resides.
    + * @param {string} className The name of the class within the namespace.
    + * @param {Function} originalClass The original class implementation before it
    + *     was replaced by a proxy.
    + * @param {Function} proxy The proxy that replaced the original class.
    + * @constructor
    + * @final
    + */
    +goog.testing.MockClassRecord = function(namespace, className, originalClass,
    +    proxy) {
    +  /**
    +   * A standard closure namespace (e.g. goog.foo.bar) that contains the mock
    +   * class referenced by this MockClassRecord.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.namespace_ = namespace;
    +
    +  /**
    +   * The name of the class within the provided namespace.
    +   * @type {string}
    +   * @private
    +   */
    +  this.className_ = className;
    +
    +  /**
    +   * The original class implementation.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.originalClass_ = originalClass;
    +
    +  /**
    +   * The proxy being used as a replacement for the original class.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.proxy_ = proxy;
    +
    +  /**
    +   * A mocks that will be constructed by their argument list.  The entries are
    +   * objects with the format {'args': args, 'mock': mock}.
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.instancesByArgs_ = [];
    +};
    +
    +
    +/**
    + * A mock associated with the static functions for a given class.
    + * @type {goog.testing.StrictMock|goog.testing.LooseMock|null}
    + * @private
    + */
    +goog.testing.MockClassRecord.prototype.staticMock_ = null;
    +
    +
    +/**
    + * A getter for this record's namespace.
    + * @return {Object} The namespace.
    + */
    +goog.testing.MockClassRecord.prototype.getNamespace = function() {
    +  return this.namespace_;
    +};
    +
    +
    +/**
    + * A getter for this record's class name.
    + * @return {string} The name of the class referenced by this record.
    + */
    +goog.testing.MockClassRecord.prototype.getClassName = function() {
    +  return this.className_;
    +};
    +
    +
    +/**
    + * A getter for the original class.
    + * @return {Function} The original class implementation before mocking.
    + */
    +goog.testing.MockClassRecord.prototype.getOriginalClass = function() {
    +  return this.originalClass_;
    +};
    +
    +
    +/**
    + * A getter for the proxy being used as a replacement for the original class.
    + * @return {Function} The proxy.
    + */
    +goog.testing.MockClassRecord.prototype.getProxy = function() {
    +  return this.proxy_;
    +};
    +
    +
    +/**
    + * A getter for the static mock.
    + * @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The static
    + *     mock associated with this record.
    + */
    +goog.testing.MockClassRecord.prototype.getStaticMock = function() {
    +  return this.staticMock_;
    +};
    +
    +
    +/**
    + * A setter for the static mock.
    + * @param {goog.testing.StrictMock|goog.testing.LooseMock} staticMock A mock to
    + *     associate with the static functions for the referenced class.
    + */
    +goog.testing.MockClassRecord.prototype.setStaticMock = function(staticMock) {
    +  this.staticMock_ = staticMock;
    +};
    +
    +
    +/**
    + * Adds a new mock instance mapping.  The mapping connects a set of function
    + * arguments to a specific mock instance.
    + * @param {Array<?>} args An array of function arguments.
    + * @param {goog.testing.StrictMock|goog.testing.LooseMock} mock A mock
    + *     associated with the supplied arguments.
    + */
    +goog.testing.MockClassRecord.prototype.addMockInstance = function(args, mock) {
    +  this.instancesByArgs_.push({args: args, mock: mock});
    +};
    +
    +
    +/**
    + * Finds the mock corresponding to a given argument set.  Throws an error if
    + * there is no appropriate match found.
    + * @param {Array<?>} args An array of function arguments.
    + * @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The mock
    + *     corresponding to a given argument set.
    + */
    +goog.testing.MockClassRecord.prototype.findMockInstance = function(args) {
    +  for (var i = 0; i < this.instancesByArgs_.length; i++) {
    +    var instanceArgs = this.instancesByArgs_[i].args;
    +    if (goog.testing.mockmatchers.flexibleArrayMatcher(instanceArgs, args)) {
    +      return this.instancesByArgs_[i].mock;
    +    }
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Resets this record by reverting all the mocked classes back to the original
    + * implementation and clearing out the mock instance list.
    + */
    +goog.testing.MockClassRecord.prototype.reset = function() {
    +  this.namespace_[this.className_] = this.originalClass_;
    +  this.instancesByArgs_ = [];
    +};
    +
    +
    +
    +/**
    + * A factory used to create new mock class instances.  It is able to generate
    + * both static and loose mocks.  The MockClassFactory is a singleton since it
    + * tracks the classes that have been mocked internally.
    + * @constructor
    + * @final
    + */
    +goog.testing.MockClassFactory = function() {
    +  if (goog.testing.MockClassFactory.instance_) {
    +    return goog.testing.MockClassFactory.instance_;
    +  }
    +
    +  /**
    +   * A map from class name -> goog.testing.MockClassRecord.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.mockClassRecords_ = {};
    +
    +  goog.testing.MockClassFactory.instance_ = this;
    +};
    +
    +
    +/**
    + * A singleton instance of the MockClassFactory.
    + * @type {goog.testing.MockClassFactory?}
    + * @private
    + */
    +goog.testing.MockClassFactory.instance_ = null;
    +
    +
    +/**
    + * The names of the fields that are defined on Object.prototype.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.testing.MockClassFactory.PROTOTYPE_FIELDS_ = [
    +  'constructor',
    +  'hasOwnProperty',
    +  'isPrototypeOf',
    +  'propertyIsEnumerable',
    +  'toLocaleString',
    +  'toString',
    +  'valueOf'
    +];
    +
    +
    +/**
    + * Iterates through a namespace to find the name of a given class.  This is done
    + * solely to support compilation since string identifiers would break down.
    + * Tests usually aren't compiled, but the functionality is supported.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class whose name should be returned.
    + * @return {string} The name of the class.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.getClassName_ = function(namespace,
    +    classToMock) {
    +  var namespaces;
    +  if (namespace === goog.global) {
    +    namespaces = goog.testing.TestCase.getGlobals();
    +  } else {
    +    namespaces = [namespace];
    +  }
    +  for (var i = 0; i < namespaces.length; i++) {
    +    for (var prop in namespaces[i]) {
    +      if (namespaces[i][prop] === classToMock) {
    +        return prop;
    +      }
    +    }
    +  }
    +
    +  throw Error('Class is not a part of the given namespace');
    +};
    +
    +
    +/**
    + * Returns whether or not a given class has been mocked.
    + * @param {string} className The name of the class.
    + * @return {boolean} Whether or not the given class name has a MockClassRecord.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.classHasMock_ = function(className) {
    +  return !!this.mockClassRecords_[className];
    +};
    +
    +
    +/**
    + * Returns a proxy constructor closure.  Since this is a constructor, "this"
    + * refers to the local scope of the constructed object thus bind cannot be
    + * used.
    + * @param {string} className The name of the class.
    + * @param {Function} mockFinder A bound function that returns the mock
    + *     associated with a class given the constructor's argument list.
    + * @return {!Function} A proxy constructor.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.getProxyCtor_ = function(className,
    +    mockFinder) {
    +  return function() {
    +    this.$mock_ = mockFinder(className, arguments);
    +    if (!this.$mock_) {
    +      // The "arguments" variable is not a proper Array so it must be converted.
    +      var args = Array.prototype.slice.call(arguments, 0);
    +      throw Error('No mock found for ' + className + ' with arguments ' +
    +          args.join(', '));
    +    }
    +  };
    +};
    +
    +
    +/**
    + * Returns a proxy function for a mock class instance.  This function cannot
    + * be used with bind since "this" must refer to the scope of the proxy
    + * constructor.
    + * @param {string} fnName The name of the function that should be proxied.
    + * @return {!Function} A proxy function.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.getProxyFunction_ = function(fnName) {
    +  return function() {
    +    return this.$mock_[fnName].apply(this.$mock_, arguments);
    +  };
    +};
    +
    +
    +/**
    + * Find a mock instance for a given class name and argument list.
    + * @param {string} className The name of the class.
    + * @param {Array<?>} args The argument list to match.
    + * @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock found for
    + *     the given argument list.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.findMockInstance_ = function(className,
    +    args) {
    +  return this.mockClassRecords_[className].findMockInstance(args);
    +};
    +
    +
    +/**
    + * Create a proxy class.  A proxy will pass functions to the mock for a class.
    + * The proxy class only covers prototype methods.  A static mock is not build
    + * simultaneously since it might be strict or loose.  The proxy class inherits
    + * from the target class in order to preserve instanceof checks.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class that will be proxied.
    + * @param {string} className The name of the class.
    + * @return {!Function} The proxy for provided class.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.createProxy_ = function(namespace,
    +    classToMock, className) {
    +  var proxy = this.getProxyCtor_(className,
    +      goog.bind(this.findMockInstance_, this));
    +  var protoToProxy = classToMock.prototype;
    +  goog.inherits(proxy, classToMock);
    +
    +  for (var prop in protoToProxy) {
    +    if (goog.isFunction(protoToProxy[prop])) {
    +      proxy.prototype[prop] = this.getProxyFunction_(prop);
    +    }
    +  }
    +
    +  // For IE the for-in-loop does not contain any properties that are not
    +  // enumerable on the prototype object (for example isPrototypeOf from
    +  // Object.prototype) and it will also not include 'replace' on objects that
    +  // extend String and change 'replace' (not that it is common for anyone to
    +  // extend anything except Object).
    +  // TODO (arv): Implement goog.object.getIterator and replace this loop.
    +
    +  goog.array.forEach(goog.testing.MockClassFactory.PROTOTYPE_FIELDS_,
    +      function(field) {
    +        if (Object.prototype.hasOwnProperty.call(protoToProxy, field)) {
    +          proxy.prototype[field] = this.getProxyFunction_(field);
    +        }
    +      }, this);
    +
    +  this.mockClassRecords_[className] = new goog.testing.MockClassRecord(
    +      namespace, className, classToMock, proxy);
    +  namespace[className] = proxy;
    +  return proxy;
    +};
    +
    +
    +/**
    + * Gets either a loose or strict mock for a given class based on a set of
    + * arguments.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class that will be mocked.
    + * @param {boolean} isStrict Whether or not the mock should be strict.
    + * @param {goog.array.ArrayLike} ctorArgs The arguments associated with this
    + *     instance's constructor.
    + * @return {!goog.testing.StrictMock|!goog.testing.LooseMock} The mock created
    + *     for the provided class.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.getMockClass_ =
    +    function(namespace, classToMock, isStrict, ctorArgs) {
    +  var className = this.getClassName_(namespace, classToMock);
    +
    +  // The namespace and classToMock variables should be removed from the
    +  // passed in argument stack.
    +  ctorArgs = goog.array.slice(ctorArgs, 2);
    +
    +  if (goog.isFunction(classToMock)) {
    +    var mock = isStrict ? new goog.testing.StrictMock(classToMock) :
    +        new goog.testing.LooseMock(classToMock);
    +
    +    if (!this.classHasMock_(className)) {
    +      this.createProxy_(namespace, classToMock, className);
    +    } else {
    +      var instance = this.findMockInstance_(className, ctorArgs);
    +      if (instance) {
    +        throw Error('Mock instance already created for ' + className +
    +            ' with arguments ' + ctorArgs.join(', '));
    +      }
    +    }
    +    this.mockClassRecords_[className].addMockInstance(ctorArgs, mock);
    +
    +    return mock;
    +  } else {
    +    throw Error('Cannot create a mock class for ' + className +
    +        ' of type ' + typeof classToMock);
    +  }
    +};
    +
    +
    +/**
    + * Gets a strict mock for a given class.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class that will be mocked.
    + * @param {...*} var_args The arguments associated with this instance's
    + *     constructor.
    + * @return {!goog.testing.StrictMock} The mock created for the provided class.
    + */
    +goog.testing.MockClassFactory.prototype.getStrictMockClass =
    +    function(namespace, classToMock, var_args) {
    +  return /** @type {!goog.testing.StrictMock} */ (this.getMockClass_(namespace,
    +      classToMock, true, arguments));
    +};
    +
    +
    +/**
    + * Gets a loose mock for a given class.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class that will be mocked.
    + * @param {...*} var_args The arguments associated with this instance's
    + *     constructor.
    + * @return {goog.testing.LooseMock} The mock created for the provided class.
    + */
    +goog.testing.MockClassFactory.prototype.getLooseMockClass =
    +    function(namespace, classToMock, var_args) {
    +  return /** @type {goog.testing.LooseMock} */ (this.getMockClass_(namespace,
    +      classToMock, false, arguments));
    +};
    +
    +
    +/**
    + * Creates either a loose or strict mock for the static functions of a given
    + * class.
    + * @param {Function} classToMock The class whose static functions will be
    + *     mocked.  This should be the original class and not the proxy.
    + * @param {string} className The name of the class.
    + * @param {Function} proxy The proxy that will replace the original class.
    + * @param {boolean} isStrict Whether or not the mock should be strict.
    + * @return {!goog.testing.StrictMock|!goog.testing.LooseMock} The mock created
    + *     for the static functions of the provided class.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.createStaticMock_ =
    +    function(classToMock, className, proxy, isStrict) {
    +  var mock = isStrict ? new goog.testing.StrictMock(classToMock, true) :
    +      new goog.testing.LooseMock(classToMock, false, true);
    +
    +  for (var prop in classToMock) {
    +    if (goog.isFunction(classToMock[prop])) {
    +      proxy[prop] = goog.bind(mock.$mockMethod, mock, prop);
    +    } else if (classToMock[prop] !== classToMock.prototype) {
    +      proxy[prop] = classToMock[prop];
    +    }
    +  }
    +
    +  this.mockClassRecords_[className].setStaticMock(mock);
    +  return mock;
    +};
    +
    +
    +/**
    + * Gets either a loose or strict mock for the static functions of a given class.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class whose static functions will be
    + *     mocked.  This should be the original class and not the proxy.
    + * @param {boolean} isStrict Whether or not the mock should be strict.
    + * @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock created
    + *     for the static functions of the provided class.
    + * @private
    + */
    +goog.testing.MockClassFactory.prototype.getStaticMock_ = function(namespace,
    +    classToMock, isStrict) {
    +  var className = this.getClassName_(namespace, classToMock);
    +
    +  if (goog.isFunction(classToMock)) {
    +    if (!this.classHasMock_(className)) {
    +      var proxy = this.createProxy_(namespace, classToMock, className);
    +      var mock = this.createStaticMock_(classToMock, className, proxy,
    +          isStrict);
    +      return mock;
    +    }
    +
    +    if (!this.mockClassRecords_[className].getStaticMock()) {
    +      var proxy = this.mockClassRecords_[className].getProxy();
    +      var originalClass = this.mockClassRecords_[className].getOriginalClass();
    +      var mock = this.createStaticMock_(originalClass, className, proxy,
    +          isStrict);
    +      return mock;
    +    } else {
    +      var mock = this.mockClassRecords_[className].getStaticMock();
    +      var mockIsStrict = mock instanceof goog.testing.StrictMock;
    +
    +      if (mockIsStrict != isStrict) {
    +        var mockType = mock instanceof goog.testing.StrictMock ? 'strict' :
    +            'loose';
    +        var requestedType = isStrict ? 'strict' : 'loose';
    +        throw Error('Requested a ' + requestedType + ' static mock, but a ' +
    +            mockType + ' mock already exists.');
    +      }
    +
    +      return mock;
    +    }
    +  } else {
    +    throw Error('Cannot create a mock for the static functions of ' +
    +        className + ' of type ' + typeof classToMock);
    +  }
    +};
    +
    +
    +/**
    + * Gets a strict mock for the static functions of a given class.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class whose static functions will be
    + *     mocked.  This should be the original class and not the proxy.
    + * @return {goog.testing.StrictMock} The mock created for the static functions
    + *     of the provided class.
    + */
    +goog.testing.MockClassFactory.prototype.getStrictStaticMock =
    +    function(namespace, classToMock) {
    +  return /** @type {goog.testing.StrictMock} */ (this.getStaticMock_(namespace,
    +      classToMock, true));
    +};
    +
    +
    +/**
    + * Gets a loose mock for the static functions of a given class.
    + * @param {Object} namespace A javascript namespace (e.g. goog.testing).
    + * @param {Function} classToMock The class whose static functions will be
    + *     mocked.  This should be the original class and not the proxy.
    + * @return {goog.testing.LooseMock} The mock created for the static functions
    + *     of the provided class.
    + */
    +goog.testing.MockClassFactory.prototype.getLooseStaticMock =
    +    function(namespace, classToMock) {
    +  return /** @type {goog.testing.LooseMock} */ (this.getStaticMock_(namespace,
    +      classToMock, false));
    +};
    +
    +
    +/**
    + * Resests the factory by reverting all mocked classes to their original
    + * implementations and removing all MockClassRecords.
    + */
    +goog.testing.MockClassFactory.prototype.reset = function() {
    +  goog.object.forEach(this.mockClassRecords_, function(record) {
    +    record.reset();
    +  });
    +  this.mockClassRecords_ = {};
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.html
    new file mode 100644
    index 0000000000..49e96da47a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockClassFactory
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockClassFactoryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.js
    new file mode 100644
    index 0000000000..5ecec30224
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclassfactory_test.js
    @@ -0,0 +1,238 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.setTestOnly('goog.testing.MockClassFactoryTest');
    +goog.require('goog.testing');
    +goog.require('goog.testing.MockClassFactory');
    +goog.require('goog.testing.jsunit');
    +goog.provide('fake.BaseClass');
    +goog.provide('fake.ChildClass');
    +goog.provide('goog.testing.MockClassFactoryTest');
    +
    +// Classes that will be mocked.  A base class and child class are used to
    +// test inheritance.
    +fake.BaseClass = function(a) {
    +  fail('real object should never be called');
    +};
    +
    +fake.BaseClass.prototype.foo = function() {
    +  fail('real object should never be called');
    +};
    +
    +fake.BaseClass.prototype.toString = function() {return 'foo';};
    +
    +fake.BaseClass.prototype.toLocaleString = function() {return 'bar';};
    +
    +fake.ChildClass = function(a) {
    +  fail('real object should never be called');
    +};
    +goog.inherits(fake.ChildClass, fake.BaseClass);
    +
    +fake.ChildClass.staticFoo = function() {
    +  fail('real object should never be called');
    +};
    +
    +fake.ChildClass.prototype.bar = function() {
    +  fail('real object should never be called');
    +};
    +
    +fake.ChildClass.staticProperty = 'staticPropertyOnClass';
    +
    +function TopLevelBaseClass() {
    +}
    +
    +var mockClassFactory = new goog.testing.MockClassFactory();
    +var matchers = goog.testing.mockmatchers;
    +
    +function tearDown() {
    +  mockClassFactory.reset();
    +}
    +
    +function testGetStrictMockClass() {
    +  var mock1 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 1);
    +  mock1.foo();
    +  mock1.$replay();
    +
    +  var mock2 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 2);
    +  mock2.foo();
    +  mock2.$replay();
    +
    +  var mock3 = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 3);
    +  mock3.foo();
    +  mock3.bar();
    +  mock3.$replay();
    +
    +  var instance1 = new fake.BaseClass(1);
    +  instance1.foo();
    +  mock1.$verify();
    +
    +  var instance2 = new fake.BaseClass(2);
    +  instance2.foo();
    +  mock2.$verify();
    +
    +  var instance3 = new fake.ChildClass(3);
    +  instance3.foo();
    +  instance3.bar();
    +  mock3.$verify();
    +
    +  assertThrows(function() {new fake.BaseClass(-1)});
    +  assertTrue(instance1 instanceof fake.BaseClass);
    +  assertTrue(instance2 instanceof fake.BaseClass);
    +  assertTrue(instance3 instanceof fake.ChildClass);
    +}
    +
    +function testGetStrictMockClassCreatesAllProxies() {
    +  var mock1 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 1);
    +  // toString(), toLocaleString() and others are treaded specially in
    +  // createProxy_().
    +  mock1.toString();
    +  mock1.toLocaleString();
    +  mock1.$replay();
    +
    +  var instance1 = new fake.BaseClass(1);
    +  instance1.toString();
    +  instance1.toLocaleString();
    +  mock1.$verify();
    +}
    +
    +function testGetLooseMockClass() {
    +  var mock1 = mockClassFactory.getLooseMockClass(fake, fake.BaseClass, 1);
    +  mock1.foo().$anyTimes().$returns(3);
    +  mock1.$replay();
    +
    +  var mock2 = mockClassFactory.getLooseMockClass(fake, fake.BaseClass, 2);
    +  mock2.foo().$times(3);
    +  mock2.$replay();
    +
    +  var mock3 = mockClassFactory.getLooseMockClass(fake, fake.ChildClass, 3);
    +  mock3.foo().$atLeastOnce().$returns(5);
    +  mock3.bar().$atLeastOnce();
    +  mock3.$replay();
    +
    +  var instance1 = new fake.BaseClass(1);
    +  assertEquals(3, instance1.foo());
    +  assertEquals(3, instance1.foo());
    +  assertEquals(3, instance1.foo());
    +  assertEquals(3, instance1.foo());
    +  assertEquals(3, instance1.foo());
    +  mock1.$verify();
    +
    +  var instance2 = new fake.BaseClass(2);
    +  instance2.foo();
    +  instance2.foo();
    +  instance2.foo();
    +  mock2.$verify();
    +
    +  var instance3 = new fake.ChildClass(3);
    +  assertEquals(5, instance3.foo());
    +  assertEquals(5, instance3.foo());
    +  instance3.bar();
    +  mock3.$verify();
    +
    +  assertThrows(function() {new fake.BaseClass(-1)});
    +  assertTrue(instance1 instanceof fake.BaseClass);
    +  assertTrue(instance2 instanceof fake.BaseClass);
    +  assertTrue(instance3 instanceof fake.ChildClass);
    +}
    +
    +function testGetStrictStaticMock() {
    +  var staticMock = mockClassFactory.getStrictStaticMock(fake,
    +      fake.ChildClass);
    +  var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 1);
    +
    +  mock.foo();
    +  mock.bar();
    +  staticMock.staticFoo();
    +  mock.$replay();
    +  staticMock.$replay();
    +
    +  var instance = new fake.ChildClass(1);
    +  instance.foo();
    +  instance.bar();
    +  fake.ChildClass.staticFoo();
    +  mock.$verify();
    +  staticMock.$verify();
    +
    +  assertTrue(instance instanceof fake.BaseClass);
    +  assertTrue(instance instanceof fake.ChildClass);
    +  assertThrows(function() {
    +    mockClassFactory.getLooseStaticMock(fake, fake.ChildClass);
    +  });
    +}
    +
    +function testGetStrictStaticMockKeepsStaticProperties() {
    +  var OriginalChildClass = fake.ChildClass;
    +  var staticMock = mockClassFactory.getStrictStaticMock(fake,
    +      fake.ChildClass);
    +  assertEquals(OriginalChildClass.staticProperty,
    +      fake.ChildClass.staticProperty);
    +}
    +
    +function testGetLooseStaticMockKeepsStaticProperties() {
    +  var OriginalChildClass = fake.ChildClass;
    +  var staticMock = mockClassFactory.getLooseStaticMock(fake,
    +      fake.ChildClass);
    +  assertEquals(OriginalChildClass.staticProperty,
    +      fake.ChildClass.staticProperty);
    +}
    +
    +function testGetLooseStaticMock() {
    +  var staticMock = mockClassFactory.getLooseStaticMock(fake,
    +      fake.ChildClass);
    +  var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 1);
    +
    +  mock.foo();
    +  mock.bar();
    +  staticMock.staticFoo().$atLeastOnce();
    +  mock.$replay();
    +  staticMock.$replay();
    +
    +  var instance = new fake.ChildClass(1);
    +  instance.foo();
    +  instance.bar();
    +  fake.ChildClass.staticFoo();
    +  fake.ChildClass.staticFoo();
    +  mock.$verify();
    +  staticMock.$verify();
    +
    +  assertTrue(instance instanceof fake.BaseClass);
    +  assertTrue(instance instanceof fake.ChildClass);
    +  assertThrows(function() {
    +    mockClassFactory.getStrictStaticMock(fake, fake.ChildClass);
    +  });
    +}
    +
    +function testFlexibleClassMockInstantiation() {
    +  // This mock should be returned for all instances created with a number
    +  // as the first argument.
    +  var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass,
    +      matchers.isNumber);
    +  mock.foo(); // Will be called by the first mock instance.
    +  mock.foo(); // Will be called by the second mock instance.
    +  mock.$replay();
    +
    +  var instance1 = new fake.ChildClass(1);
    +  var instance2 = new fake.ChildClass(2);
    +  instance1.foo();
    +  instance2.foo();
    +  assertThrows(function() {
    +    new fake.ChildClass('foo');
    +  });
    +  mock.$verify();
    +}
    +
    +function testMockTopLevelClass() {
    +  var mock = mockClassFactory.getStrictMockClass(goog.global,
    +      goog.global.TopLevelBaseClass);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclock.js b/mapbox-gl/closure-library/closure/goog/testing/mockclock.js
    new file mode 100644
    index 0000000000..b1d6b6f59a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclock.js
    @@ -0,0 +1,591 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock Clock implementation for working with setTimeout,
    + * setInterval, clearTimeout and clearInterval within unit tests.
    + *
    + * Derived from jsUnitMockTimeout.js, contributed to JsUnit by
    + * Pivotal Computer Systems, www.pivotalsf.com
    + *
    + */
    +
    +goog.provide('goog.testing.MockClock');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.async.run');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.watchers');
    +
    +
    +
    +/**
    + * Class for unit testing code that uses setTimeout and clearTimeout.
    + *
    + * NOTE: If you are using MockClock to test code that makes use of
    + *       goog.fx.Animation, then you must either:
    + *
    + * 1. Install and dispose of the MockClock in setUpPage() and tearDownPage()
    + *    respectively (rather than setUp()/tearDown()).
    + *
    + * or
    + *
    + * 2. Ensure that every test clears the animation queue by calling
    + *    mockClock.tick(x) at the end of each test function (where `x` is large
    + *    enough to complete all animations).
    + *
    + * Otherwise, if any animation is left pending at the time that
    + * MockClock.dispose() is called, that will permanently prevent any future
    + * animations from playing on the page.
    + *
    + * @param {boolean=} opt_autoInstall Install the MockClock at construction time.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.testing.MockClock = function(opt_autoInstall) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Reverse-order queue of timers to fire.
    +   *
    +   * The last item of the queue is popped off.  Insertion happens from the
    +   * right.  For example, the expiration times for each element of the queue
    +   * might be in the order 300, 200, 200.
    +   *
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.queue_ = [];
    +
    +  /**
    +   * Set of timeouts that should be treated as cancelled.
    +   *
    +   * Rather than removing cancelled timers directly from the queue, this set
    +   * simply marks them as deleted so that they can be ignored when their
    +   * turn comes up.  The keys are the timeout keys that are cancelled, each
    +   * mapping to true.
    +   *
    +   * @type {Object}
    +   * @private
    +   */
    +  this.deletedKeys_ = {};
    +
    +  if (opt_autoInstall) {
    +    this.install();
    +  }
    +};
    +goog.inherits(goog.testing.MockClock, goog.Disposable);
    +
    +
    +/**
    + * Default wait timeout for mocking requestAnimationFrame (in milliseconds).
    + *
    + * @type {number}
    + * @const
    + */
    +goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20;
    +
    +
    +/**
    + * Count of the number of timeouts made.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MockClock.prototype.timeoutsMade_ = 0;
    +
    +
    +/**
    + * PropertyReplacer instance which overwrites and resets setTimeout,
    + * setInterval, etc. or null if the MockClock is not installed.
    + * @type {goog.testing.PropertyReplacer}
    + * @private
    + */
    +goog.testing.MockClock.prototype.replacer_ = null;
    +
    +
    +/**
    + * Map of deleted keys.  These keys represents keys that were deleted in a
    + * clearInterval, timeoutid -> object.
    + * @type {Object}
    + * @private
    + */
    +goog.testing.MockClock.prototype.deletedKeys_ = null;
    +
    +
    +/**
    + * The current simulated time in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MockClock.prototype.nowMillis_ = 0;
    +
    +
    +/**
    + * Additional delay between the time a timeout was set to fire, and the time
    + * it actually fires.  Useful for testing workarounds for this Firefox 2 bug:
    + * https://bugzilla.mozilla.org/show_bug.cgi?id=291386
    + * May be negative.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MockClock.prototype.timeoutDelay_ = 0;
    +
    +
    +/**
    + * Installs the MockClock by overriding the global object's implementation of
    + * setTimeout, setInterval, clearTimeout and clearInterval.
    + */
    +goog.testing.MockClock.prototype.install = function() {
    +  if (!this.replacer_) {
    +    var r = this.replacer_ = new goog.testing.PropertyReplacer();
    +    r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this));
    +    r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this));
    +    r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this));
    +    r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this));
    +    r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this));
    +    // goog.Promise uses goog.async.run. In order to be able to test
    +    // Promise-based code, we need to make sure that goog.async.run uses
    +    // nextTick instead of native browser Promises. This means that it will
    +    // default to setImmediate, which is replaced above. Note that we test for
    +    // the presence of goog.async.run.forceNextTick to be resilient to the case
    +    // where tests replace goog.async.run directly.
    +    goog.async.run.forceNextTick && goog.async.run.forceNextTick();
    +
    +    // Replace the requestAnimationFrame functions.
    +    this.replaceRequestAnimationFrame_();
    +
    +    // PropertyReplacer#set can't be called with renameable functions.
    +    this.oldGoogNow_ = goog.now;
    +    goog.now = goog.bind(this.getCurrentTime, this);
    +  }
    +};
    +
    +
    +/**
    + * Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.
    + * @private
    + */
    +goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() {
    +  var r = this.replacer_;
    +  var requestFuncs = ['requestAnimationFrame',
    +                      'webkitRequestAnimationFrame',
    +                      'mozRequestAnimationFrame',
    +                      'oRequestAnimationFrame',
    +                      'msRequestAnimationFrame'];
    +
    +  var cancelFuncs = ['cancelAnimationFrame',
    +                     'cancelRequestAnimationFrame',
    +                     'webkitCancelRequestAnimationFrame',
    +                     'mozCancelRequestAnimationFrame',
    +                     'oCancelRequestAnimationFrame',
    +                     'msCancelRequestAnimationFrame'];
    +
    +  for (var i = 0; i < requestFuncs.length; ++i) {
    +    if (goog.global && goog.global[requestFuncs[i]]) {
    +      r.set(goog.global, requestFuncs[i],
    +          goog.bind(this.requestAnimationFrame_, this));
    +    }
    +  }
    +
    +  for (var i = 0; i < cancelFuncs.length; ++i) {
    +    if (goog.global && goog.global[cancelFuncs[i]]) {
    +      r.set(goog.global, cancelFuncs[i],
    +          goog.bind(this.cancelRequestAnimationFrame_, this));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes the MockClock's hooks into the global object's functions and revert
    + * to their original values.
    + */
    +goog.testing.MockClock.prototype.uninstall = function() {
    +  if (this.replacer_) {
    +    this.replacer_.reset();
    +    this.replacer_ = null;
    +    goog.now = this.oldGoogNow_;
    +  }
    +
    +  this.fireResetEvent();
    +};
    +
    +
    +/** @override */
    +goog.testing.MockClock.prototype.disposeInternal = function() {
    +  this.uninstall();
    +  this.queue_ = null;
    +  this.deletedKeys_ = null;
    +  goog.testing.MockClock.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Resets the MockClock, removing all timeouts that are scheduled and resets
    + * the fake timer count.
    + */
    +goog.testing.MockClock.prototype.reset = function() {
    +  this.queue_ = [];
    +  this.deletedKeys_ = {};
    +  this.nowMillis_ = 0;
    +  this.timeoutsMade_ = 0;
    +  this.timeoutDelay_ = 0;
    +
    +  this.fireResetEvent();
    +};
    +
    +
    +/**
    + * Signals that the mock clock has been reset, allowing objects that
    + * maintain their own internal state to reset.
    + */
    +goog.testing.MockClock.prototype.fireResetEvent = function() {
    +  goog.testing.watchers.signalClockReset();
    +};
    +
    +
    +/**
    + * Sets the amount of time between when a timeout is scheduled to fire and when
    + * it actually fires.
    + * @param {number} delay The delay in milliseconds.  May be negative.
    + */
    +goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) {
    +  this.timeoutDelay_ = delay;
    +};
    +
    +
    +/**
    + * @return {number} delay The amount of time between when a timeout is
    + *     scheduled to fire and when it actually fires, in milliseconds.  May
    + *     be negative.
    + */
    +goog.testing.MockClock.prototype.getTimeoutDelay = function() {
    +  return this.timeoutDelay_;
    +};
    +
    +
    +/**
    + * Increments the MockClock's time by a given number of milliseconds, running
    + * any functions that are now overdue.
    + * @param {number=} opt_millis Number of milliseconds to increment the counter.
    + *     If not specified, clock ticks 1 millisecond.
    + * @return {number} Current mock time in milliseconds.
    + */
    +goog.testing.MockClock.prototype.tick = function(opt_millis) {
    +  if (typeof opt_millis != 'number') {
    +    opt_millis = 1;
    +  }
    +  var endTime = this.nowMillis_ + opt_millis;
    +  this.runFunctionsWithinRange_(endTime);
    +  this.nowMillis_ = endTime;
    +  return endTime;
    +};
    +
    +
    +/**
    + * Takes a promise and then ticks the mock clock. If the promise successfully
    + * resolves, returns the value produced by the promise. If the promise is
    + * rejected, it throws the rejection as an exception. If the promise is not
    + * resolved at all, throws an exception.
    + * Also ticks the general clock by the specified amount.
    + *
    + * @param {!goog.Thenable<T>} promise A promise that should be resolved after
    + *     the mockClock is ticked for the given opt_millis.
    + * @param {number=} opt_millis Number of milliseconds to increment the counter.
    + *     If not specified, clock ticks 1 millisecond.
    + * @return {T}
    + * @template T
    + */
    +goog.testing.MockClock.prototype.tickPromise = function(promise, opt_millis) {
    +  var value;
    +  var error;
    +  var resolved = false;
    +  promise.then(function(v) {
    +    value = v;
    +    resolved = true;
    +  }, function(e) {
    +    error = e;
    +    resolved = true;
    +  });
    +  this.tick(opt_millis);
    +  if (!resolved) {
    +    throw new Error(
    +        'Promise was expected to be resolved after mock clock tick.');
    +  }
    +  if (error) {
    +    throw error;
    +  }
    +  return value;
    +};
    +
    +
    +/**
    + * @return {number} The number of timeouts that have been scheduled.
    + */
    +goog.testing.MockClock.prototype.getTimeoutsMade = function() {
    +  return this.timeoutsMade_;
    +};
    +
    +
    +/**
    + * @return {number} The MockClock's current time in milliseconds.
    + */
    +goog.testing.MockClock.prototype.getCurrentTime = function() {
    +  return this.nowMillis_;
    +};
    +
    +
    +/**
    + * @param {number} timeoutKey The timeout key.
    + * @return {boolean} Whether the timer has been set and not cleared,
    + *     independent of the timeout's expiration.  In other words, the timeout
    + *     could have passed or could be scheduled for the future.  Either way,
    + *     this function returns true or false depending only on whether the
    + *     provided timeoutKey represents a timeout that has been set and not
    + *     cleared.
    + */
    +goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) {
    +  return timeoutKey <= this.timeoutsMade_ && !this.deletedKeys_[timeoutKey];
    +};
    +
    +
    +/**
    + * Runs any function that is scheduled before a certain time.  Timeouts can
    + * be made to fire early or late if timeoutDelay_ is non-0.
    + * @param {number} endTime The latest time in the range, in milliseconds.
    + * @private
    + */
    +goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(
    +    endTime) {
    +  var adjustedEndTime = endTime - this.timeoutDelay_;
    +
    +  // Repeatedly pop off the last item since the queue is always sorted.
    +  while (this.queue_ && this.queue_.length &&
    +      this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) {
    +    var timeout = this.queue_.pop();
    +
    +    if (!(timeout.timeoutKey in this.deletedKeys_)) {
    +      // Only move time forwards.
    +      this.nowMillis_ = Math.max(this.nowMillis_,
    +          timeout.runAtMillis + this.timeoutDelay_);
    +      // Call timeout in global scope and pass the timeout key as the argument.
    +      timeout.funcToCall.call(goog.global, timeout.timeoutKey);
    +      // In case the interval was cleared in the funcToCall
    +      if (timeout.recurring) {
    +        this.scheduleFunction_(
    +            timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Schedules a function to be run at a certain time.
    + * @param {number} timeoutKey The timeout key.
    + * @param {Function} funcToCall The function to call.
    + * @param {number} millis The number of milliseconds to call it in.
    + * @param {boolean} recurring Whether to function call should recur.
    + * @private
    + */
    +goog.testing.MockClock.prototype.scheduleFunction_ = function(
    +    timeoutKey, funcToCall, millis, recurring) {
    +  if (!goog.isFunction(funcToCall)) {
    +    // Early error for debuggability rather than dying in the next .tick()
    +    throw new TypeError('The provided callback must be a function, not a ' +
    +        typeof funcToCall);
    +  }
    +
    +  var timeout = {
    +    runAtMillis: this.nowMillis_ + millis,
    +    funcToCall: funcToCall,
    +    recurring: recurring,
    +    timeoutKey: timeoutKey,
    +    millis: millis
    +  };
    +
    +  goog.testing.MockClock.insert_(timeout, this.queue_);
    +};
    +
    +
    +/**
    + * Inserts a timer descriptor into a descending-order queue.
    + *
    + * Later-inserted duplicates appear at lower indices.  For example, the
    + * asterisk in (5,4,*,3,2,1) would be the insertion point for 3.
    + *
    + * @param {Object} timeout The timeout to insert, with numerical runAtMillis
    + *     property.
    + * @param {Array<Object>} queue The queue to insert into, with each element
    + *     having a numerical runAtMillis property.
    + * @private
    + */
    +goog.testing.MockClock.insert_ = function(timeout, queue) {
    +  // Although insertion of N items is quadratic, requiring goog.structs.Heap
    +  // from a unit test will make tests more prone to breakage.  Since unit
    +  // tests are normally small, scalability is not a primary issue.
    +
    +  // Find an insertion point.  Since the queue is in reverse order (so we
    +  // can pop rather than unshift), and later timers with the same time stamp
    +  // should be executed later, we look for the element strictly greater than
    +  // the one we are inserting.
    +
    +  for (var i = queue.length; i != 0; i--) {
    +    if (queue[i - 1].runAtMillis > timeout.runAtMillis) {
    +      break;
    +    }
    +    queue[i] = queue[i - 1];
    +  }
    +
    +  queue[i] = timeout;
    +};
    +
    +
    +/**
    + * Maximum 32-bit signed integer.
    + *
    + * Timeouts over this time return immediately in many browsers, due to integer
    + * overflow.  Such known browsers include Firefox, Chrome, and Safari, but not
    + * IE.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.testing.MockClock.MAX_INT_ = 2147483647;
    +
    +
    +/**
    + * Schedules a function to be called after {@code millis} milliseconds.
    + * Mock implementation for setTimeout.
    + * @param {Function} funcToCall The function to call.
    + * @param {number=} opt_millis The number of milliseconds to call it after.
    + * @return {number} The number of timeouts created.
    + * @private
    + */
    +goog.testing.MockClock.prototype.setTimeout_ = function(
    +    funcToCall, opt_millis) {
    +  var millis = opt_millis || 0;
    +  if (millis > goog.testing.MockClock.MAX_INT_) {
    +    throw Error(
    +        'Bad timeout value: ' + millis + '.  Timeouts over MAX_INT ' +
    +        '(24.8 days) cause timeouts to be fired ' +
    +        'immediately in most browsers, except for IE.');
    +  }
    +  this.timeoutsMade_ = this.timeoutsMade_ + 1;
    +  this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, false);
    +  return this.timeoutsMade_;
    +};
    +
    +
    +/**
    + * Schedules a function to be called every {@code millis} milliseconds.
    + * Mock implementation for setInterval.
    + * @param {Function} funcToCall The function to call.
    + * @param {number=} opt_millis The number of milliseconds between calls.
    + * @return {number} The number of timeouts created.
    + * @private
    + */
    +goog.testing.MockClock.prototype.setInterval_ =
    +    function(funcToCall, opt_millis) {
    +  var millis = opt_millis || 0;
    +  this.timeoutsMade_ = this.timeoutsMade_ + 1;
    +  this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, true);
    +  return this.timeoutsMade_;
    +};
    +
    +
    +/**
    + * Schedules a function to be called when an animation frame is triggered.
    + * Mock implementation for requestAnimationFrame.
    + * @param {Function} funcToCall The function to call.
    + * @return {number} The number of timeouts created.
    + * @private
    + */
    +goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) {
    +  return this.setTimeout_(goog.bind(function() {
    +    if (funcToCall) {
    +      funcToCall(this.getCurrentTime());
    +    } else if (goog.global.mozRequestAnimationFrame) {
    +      var event = new goog.testing.events.Event('MozBeforePaint', goog.global);
    +      event['timeStamp'] = this.getCurrentTime();
    +      goog.testing.events.fireBrowserEvent(event);
    +    }
    +  }, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
    +};
    +
    +
    +/**
    + * Schedules a function to be called immediately after the current JS
    + * execution.
    + * Mock implementation for setImmediate.
    + * @param {Function} funcToCall The function to call.
    + * @return {number} The number of timeouts created.
    + * @private
    + */
    +goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) {
    +  return this.setTimeout_(funcToCall, 0);
    +};
    +
    +
    +/**
    + * Clears a timeout.
    + * Mock implementation for clearTimeout.
    + * @param {number} timeoutKey The timeout key to clear.
    + * @private
    + */
    +goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) {
    +  // Some common libraries register static state with timers.
    +  // This is bad. It leads to all sorts of crazy test problems where
    +  // 1) Test A sets up a new mock clock and a static timer.
    +  // 2) Test B sets up a new mock clock, but re-uses the static timer
    +  //    from Test A.
    +  // 3) A timeout key from test A gets cleared, breaking a timeout in
    +  //    Test B.
    +  //
    +  // For now, we just hackily fail silently if someone tries to clear a timeout
    +  // key before we've allocated it.
    +  // Ideally, we should throw an exception if we see this happening.
    +  //
    +  // TODO(chrishenry): We might also try allocating timeout ids from a global
    +  // pool rather than a local pool.
    +  if (this.isTimeoutSet(timeoutKey)) {
    +    this.deletedKeys_[timeoutKey] = true;
    +  }
    +};
    +
    +
    +/**
    + * Clears an interval.
    + * Mock implementation for clearInterval.
    + * @param {number} timeoutKey The interval key to clear.
    + * @private
    + */
    +goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) {
    +  this.clearTimeout_(timeoutKey);
    +};
    +
    +
    +/**
    + * Clears a requestAnimationFrame.
    + * Mock implementation for cancelRequestAnimationFrame.
    + * @param {number} timeoutKey The requestAnimationFrame key to clear.
    + * @private
    + */
    +goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ =
    +    function(timeoutKey) {
    +  this.clearTimeout_(timeoutKey);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.html
    new file mode 100644
    index 0000000000..a729a15912
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockClock
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockClockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.js
    new file mode 100644
    index 0000000000..8a71ca1269
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockclock_test.js
    @@ -0,0 +1,610 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockClockTest');
    +goog.setTestOnly('goog.testing.MockClockTest');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Timer');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testMockClockWasInstalled() {
    +  var clock = new goog.testing.MockClock();
    +  var originalTimeout = window.setTimeout;
    +  clock.install();
    +  assertNotEquals(window.setTimeout, originalTimeout);
    +  setTimeout(function() {}, 100);
    +  assertEquals(1, clock.getTimeoutsMade());
    +  setInterval(function() {}, 200);
    +  assertEquals(2, clock.getTimeoutsMade());
    +  clock.uninstall();
    +  assertEquals(window.setTimeout, originalTimeout);
    +  assertNull(clock.replacer_);
    +}
    +
    +
    +function testSetTimeoutAndTick() {
    +  var clock = new goog.testing.MockClock(true);
    +  var m5 = false, m10 = false, m15 = false, m20 = false;
    +  setTimeout(function() { m5 = true; }, 5);
    +  setTimeout(function() { m10 = true; }, 10);
    +  setTimeout(function() { m15 = true; }, 15);
    +  setTimeout(function() { m20 = true; }, 20);
    +  assertEquals(4, clock.getTimeoutsMade());
    +
    +  assertEquals(4, clock.tick(4));
    +  assertEquals(4, clock.getCurrentTime());
    +
    +  assertFalse(m5);
    +  assertFalse(m10);
    +  assertFalse(m15);
    +  assertFalse(m20);
    +
    +  assertEquals(5, clock.tick(1));
    +  assertEquals(5, clock.getCurrentTime());
    +
    +  assertTrue('m5 should now be true', m5);
    +  assertFalse(m10);
    +  assertFalse(m15);
    +  assertFalse(m20);
    +
    +  assertEquals(10, clock.tick(5));
    +  assertEquals(10, clock.getCurrentTime());
    +
    +  assertTrue('m5 should be true', m5);
    +  assertTrue('m10 should now be true', m10);
    +  assertFalse(m15);
    +  assertFalse(m20);
    +
    +  assertEquals(15, clock.tick(5));
    +  assertEquals(15, clock.getCurrentTime());
    +
    +  assertTrue('m5 should be true', m5);
    +  assertTrue('m10 should be true', m10);
    +  assertTrue('m15 should now be true', m15);
    +  assertFalse(m20);
    +
    +  assertEquals(20, clock.tick(5));
    +  assertEquals(20, clock.getCurrentTime());
    +
    +  assertTrue('m5 should be true', m5);
    +  assertTrue('m10 should be true', m10);
    +  assertTrue('m15 should be true', m15);
    +  assertTrue('m20 should now be true', m20);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testSetImmediateAndTick() {
    +  var clock = new goog.testing.MockClock(true);
    +  var tick0 = false;
    +  var tick1 = false;
    +  setImmediate(function() { tick0 = true; });
    +  setImmediate(function() { tick1 = true; });
    +  assertEquals(2, clock.getTimeoutsMade());
    +
    +  clock.tick(0);
    +  assertTrue(tick0);
    +  assertTrue(tick1);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testSetInterval() {
    +  var clock = new goog.testing.MockClock(true);
    +  var times = 0;
    +  setInterval(function() { times++; }, 100);
    +
    +  clock.tick(500);
    +  assertEquals(5, times);
    +  clock.tick(100);
    +  assertEquals(6, times);
    +  clock.tick(100);
    +  assertEquals(7, times);
    +  clock.tick(50);
    +  assertEquals(7, times);
    +  clock.tick(50);
    +  assertEquals(8, times);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testRequestAnimationFrame() {
    +  goog.global.requestAnimationFrame = function() {
    +  };
    +  var clock = new goog.testing.MockClock(true);
    +  var times = [];
    +  var recFunc = goog.testing.recordFunction(function(now) {
    +    times.push(now);
    +  });
    +  goog.global.requestAnimationFrame(recFunc);
    +  clock.tick(50);
    +  assertEquals(1, recFunc.getCallCount());
    +  assertEquals(20, times[0]);
    +
    +  goog.global.requestAnimationFrame(recFunc);
    +  clock.tick(100);
    +  assertEquals(2, recFunc.getCallCount());
    +  assertEquals(70, times[1]);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testClearTimeout() {
    +  var clock = new goog.testing.MockClock(true);
    +  var ran = false;
    +  var c = setTimeout(function() { ran = true; }, 100);
    +  clock.tick(50);
    +  assertFalse(ran);
    +  clearTimeout(c);
    +  clock.tick(100);
    +  assertFalse(ran);
    +  clock.uninstall();
    +}
    +
    +
    +function testClearInterval() {
    +  var clock = new goog.testing.MockClock(true);
    +  var times = 0;
    +  var c = setInterval(function() { times++; }, 100);
    +
    +  clock.tick(500);
    +  assertEquals(5, times);
    +  clock.tick(100);
    +  assertEquals(6, times);
    +  clock.tick(100);
    +  clearInterval(c);
    +  assertEquals(7, times);
    +  clock.tick(50);
    +  assertEquals(7, times);
    +  clock.tick(50);
    +  assertEquals(7, times);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testClearInterval2() {
    +  // Tests that we can clear the interval from inside the function
    +  var clock = new goog.testing.MockClock(true);
    +  var times = 0;
    +  var c = setInterval(function() {
    +    times++;
    +    if (times == 6) {
    +      clearInterval(c);
    +    }
    +  }, 100);
    +
    +  clock.tick(500);
    +  assertEquals(5, times);
    +  clock.tick(100);
    +  assertEquals(6, times);
    +  clock.tick(100);
    +  assertEquals(6, times);
    +  clock.tick(50);
    +  assertEquals(6, times);
    +  clock.tick(50);
    +  assertEquals(6, times);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testCancelRequestAnimationFrame() {
    +  goog.global.requestAnimationFrame = function() {
    +  };
    +  goog.global.cancelRequestAnimationFrame = function() {
    +  };
    +  var clock = new goog.testing.MockClock(true);
    +  var ran = false;
    +  var c = goog.global.requestAnimationFrame(function() { ran = true; });
    +  clock.tick(10);
    +  assertFalse(ran);
    +  goog.global.cancelRequestAnimationFrame(c);
    +  clock.tick(20);
    +  assertFalse(ran);
    +  clock.uninstall();
    +}
    +
    +
    +function testMockGoogNow() {
    +  assertNotEquals(0, goog.now());
    +  var clock = new goog.testing.MockClock(true);
    +  assertEquals(0, goog.now());
    +  clock.tick(50);
    +  assertEquals(50, goog.now());
    +  clock.uninstall();
    +  assertNotEquals(50, goog.now());
    +}
    +
    +
    +function testTimeoutDelay() {
    +  var clock = new goog.testing.MockClock(true);
    +  var m5 = false, m10 = false, m20 = false;
    +  setTimeout(function() { m5 = true; }, 5);
    +  setTimeout(function() { m10 = true; }, 10);
    +  setTimeout(function() { m20 = true; }, 20);
    +
    +  // Fire 3ms early, so m5 fires at t=2
    +  clock.setTimeoutDelay(-3);
    +  clock.tick(1);
    +  assertFalse(m5);
    +  assertFalse(m10);
    +  clock.tick(1);
    +  assertTrue(m5);
    +  assertFalse(m10);
    +
    +  // Fire 3ms late, so m10 fires at t=13
    +  clock.setTimeoutDelay(3);
    +  assertEquals(12, clock.tick(10));
    +  assertEquals(12, clock.getCurrentTime());
    +  assertFalse(m10);
    +  clock.tick(1);
    +  assertTrue(m10);
    +  assertFalse(m20);
    +
    +  // Fire 10ms early, so m20 fires now, since it's after t=10
    +  clock.setTimeoutDelay(-10);
    +  assertFalse(m20);
    +  assertEquals(14, clock.tick(1));
    +  assertEquals(14, clock.getCurrentTime());
    +  assertTrue(m20);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testTimerCallbackCanCreateIntermediateTimer() {
    +  var clock = new goog.testing.MockClock(true);
    +  var sequence = [];
    +
    +  // Create 3 timers: 1, 2, and 3.  Timer 1 should fire at T=1, timer 2 at
    +  // T=2, and timer 3 at T=3.  The catch: Timer 2 is created by the
    +  // callback within timer 0.
    +
    +  // Testing method: Create a simple string sequencing each timer and at
    +  // what time it fired.
    +
    +  setTimeout(function() {
    +    sequence.push('timer1 at T=' + goog.now());
    +    setTimeout(function() {
    +      sequence.push('timer2 at T=' + goog.now());
    +    }, 1);
    +  }, 1);
    +
    +  setTimeout(function() {
    +    sequence.push('timer3 at T=' + goog.now());
    +  }, 3);
    +
    +  clock.tick(4);
    +
    +  assertEquals(
    +      'Each timer should fire in sequence at the correct time.',
    +      'timer1 at T=1, timer2 at T=2, timer3 at T=3',
    +      sequence.join(', '));
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testCorrectArgumentsPassedToCallback() {
    +  var clock = new goog.testing.MockClock(true);
    +  var timeoutId;
    +  var timeoutExecuted = false;
    +
    +  timeoutId = setTimeout(function(arg) {
    +    assertEquals('"this" must be goog.global',
    +        goog.global, this);
    +    assertEquals('The timeout ID must be the first parameter',
    +        timeoutId, arg);
    +    assertEquals('Exactly one argument must be passed',
    +        1, arguments.length);
    +    timeoutExecuted = true;
    +  }, 1);
    +
    +  clock.tick(4);
    +
    +  assertTrue('The timeout was not executed', timeoutExecuted);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testTickZero() {
    +  var clock = new goog.testing.MockClock(true);
    +  var calls = 0;
    +
    +  setTimeout(function() {
    +    assertEquals('I need to be first', 0, calls);
    +    calls++;
    +  }, 0);
    +
    +  setTimeout(function() {
    +    assertEquals('I need to be second', 1, calls);
    +    calls++;
    +  }, 0);
    +
    +  clock.tick(0);
    +  assertEquals(2, calls);
    +
    +  setTimeout(function() {
    +    assertEquals('I need to be third', 2, calls);
    +    calls++;
    +  }, 0);
    +
    +  clock.tick(0);
    +  assertEquals(3, calls);
    +
    +  assertEquals('Time should still be zero', 0, goog.now());
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testReset() {
    +  var clock = new goog.testing.MockClock(true);
    +
    +  setTimeout(function() {
    +    fail('Timeouts should be cleared after a reset');
    +  }, 0);
    +
    +  clock.reset();
    +  clock.tick(999999);
    +  clock.uninstall();
    +}
    +
    +
    +function testQueueInsertionHelper() {
    +  var queue = [];
    +
    +  function queueToString() {
    +    var buffer = [];
    +    for (var i = 0; i < queue.length; i++) {
    +      buffer.push(queue[i].runAtMillis);
    +    }
    +    return buffer.join(',');
    +  }
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 2}, queue);
    +  assertEquals('Only item',
    +      '2', queueToString());
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 4}, queue);
    +  assertEquals('Biggest item',
    +      '4,2', queueToString());
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 5}, queue);
    +  assertEquals('An even bigger item',
    +      '5,4,2', queueToString());
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 1}, queue);
    +  assertEquals('Smallest item',
    +      '5,4,2,1', queueToString());
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 1, dup: true}, queue);
    +  assertEquals('Duplicate smallest item',
    +      '5,4,2,1,1', queueToString());
    +  assertTrue('Duplicate item comes at a smaller index', queue[3].dup);
    +
    +  goog.testing.MockClock.insert_({runAtMillis: 3}, queue);
    +  goog.testing.MockClock.insert_({runAtMillis: 3, dup: true}, queue);
    +  assertEquals('Duplicate a middle item',
    +      '5,4,3,3,2,1,1', queueToString());
    +  assertTrue('Duplicate item comes at a smaller index', queue[2].dup);
    +}
    +
    +
    +function testIsTimeoutSet() {
    +  var clock = new goog.testing.MockClock(true);
    +  var timeoutKey = setTimeout(function() {}, 1);
    +  assertTrue('Timeout ' + timeoutKey + ' should be set',
    +      clock.isTimeoutSet(timeoutKey));
    +  var nextTimeoutKey = timeoutKey + 1;
    +  assertFalse('Timeout ' + nextTimeoutKey + ' should not be set',
    +      clock.isTimeoutSet(nextTimeoutKey));
    +  clearTimeout(timeoutKey);
    +  assertFalse('Timeout ' + timeoutKey + ' should no longer be set',
    +      clock.isTimeoutSet(timeoutKey));
    +  var newTimeoutKey = setTimeout(function() {}, 1);
    +  clock.tick(5);
    +  assertFalse('Timeout ' + timeoutKey + ' should not be set',
    +      clock.isTimeoutSet(timeoutKey));
    +  assertTrue('Timeout ' + newTimeoutKey + ' should be set',
    +      clock.isTimeoutSet(newTimeoutKey));
    +  clock.uninstall();
    +}
    +
    +
    +function testBalksOnTimeoutsGreaterThanMaxInt() {
    +  // Browsers have trouble with timeout greater than max int, so we
    +  // want Mock Clock to fail if this happens.
    +  var clock = new goog.testing.MockClock(true);
    +  // Functions on window don't seem to be able to throw exceptions in
    +  // IE6.  Explicitly reading the property makes it work.
    +  var setTimeout = window.setTimeout;
    +  assertThrows('Timeouts > MAX_INT should fail',
    +      function() {
    +        setTimeout(goog.nullFunction, 2147483648);
    +      });
    +  assertThrows('Timeouts much greater than MAX_INT should fail',
    +      function() {
    +        setTimeout(goog.nullFunction, 2147483648 * 10);
    +      });
    +  clock.uninstall();
    +}
    +
    +
    +function testCorrectSetTimeoutIsRestored() {
    +  var safe = goog.functions.error('should not have been called');
    +  stubs.set(window, 'setTimeout', safe);
    +
    +  var clock = new goog.testing.MockClock(true);
    +  assertNotEquals('setTimeout is replaced', safe, window.setTimeout);
    +  clock.uninstall();
    +  // NOTE: If this assertion proves to be flaky in IE, the string value of
    +  // the two functions have to be compared as described in
    +  // goog.testing.TestCase#finalize.
    +  assertEquals('setTimeout is restored', safe, window.setTimeout);
    +}
    +
    +
    +function testMozRequestAnimationFrame() {
    +  // Setting this function will indirectly tell the mock clock to mock it out.
    +  stubs.set(window, 'mozRequestAnimationFrame', goog.nullFunction);
    +
    +  var clock = new goog.testing.MockClock(true);
    +
    +  var mozBeforePaint = goog.testing.recordFunction();
    +  goog.events.listen(window, 'MozBeforePaint', mozBeforePaint);
    +
    +  window.mozRequestAnimationFrame(null);
    +  assertEquals(0, mozBeforePaint.getCallCount());
    +
    +  clock.tick(goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
    +  assertEquals(1, mozBeforePaint.getCallCount());
    +  clock.dispose();
    +}
    +
    +
    +function testClearBeforeSet() {
    +  var clock = new goog.testing.MockClock(true);
    +  var expectedId = 1;
    +  window.clearTimeout(expectedId);
    +
    +  var fn = goog.testing.recordFunction();
    +  var actualId = window.setTimeout(fn, 0);
    +  assertEquals(
    +      'In order for this test to work, we have to guess the ids in advance',
    +      expectedId, actualId);
    +  clock.tick(1);
    +  assertEquals(1, fn.getCallCount());
    +  clock.dispose();
    +}
    +
    +
    +function testNonFunctionArguments() {
    +  var clock = new goog.testing.MockClock(true);
    +
    +  // Unlike normal setTimeout and friends, we only accept functions (not
    +  // strings, not undefined, etc). Make sure that if we get a non-function, we
    +  // fail early rather than on the next .tick() operation.
    +
    +  assertThrows('setTimeout with a non-function value should fail',
    +      function() {
    +        window.setTimeout(undefined, 0);
    +      });
    +  clock.tick(1);
    +
    +  assertThrows('setTimeout with a string should fail',
    +      function() {
    +        window.setTimeout('throw new Error("setTimeout string eval!");', 0);
    +      });
    +  clock.tick(1);
    +
    +  clock.dispose();
    +}
    +
    +
    +function testUnspecifiedTimeout() {
    +  var clock = new goog.testing.MockClock(true);
    +  var m0a = false, m0b = false, m10 = false;
    +  setTimeout(function() { m0a = true; });
    +  setTimeout(function() { m10 = true; }, 10);
    +  assertEquals(2, clock.getTimeoutsMade());
    +
    +  assertFalse(m0a);
    +  assertFalse(m0b);
    +  assertFalse(m10);
    +
    +  assertEquals(0, clock.tick(0));
    +  assertEquals(0, clock.getCurrentTime());
    +
    +  assertTrue(m0a);
    +  assertFalse(m0b);
    +  assertFalse(m10);
    +
    +  setTimeout(function() { m0b = true; });
    +  assertEquals(3, clock.getTimeoutsMade());
    +
    +  assertEquals(0, clock.tick(0));
    +  assertEquals(0, clock.getCurrentTime());
    +
    +  assertTrue(m0a);
    +  assertTrue(m0b);
    +  assertFalse(m10);
    +
    +  assertEquals(10, clock.tick(10));
    +  assertEquals(10, clock.getCurrentTime());
    +
    +  assertTrue(m0a);
    +  assertTrue(m0b);
    +  assertTrue(m10);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testUnspecifiedInterval() {
    +  var clock = new goog.testing.MockClock(true);
    +  var times = 0;
    +  var handle = setInterval(function() {
    +    if (++times >= 5) {
    +      clearInterval(handle);
    +    }
    +  });
    +
    +  clock.tick(0);
    +  assertEquals(5, times);
    +
    +  clock.uninstall();
    +}
    +
    +
    +function testTickPromise() {
    +  var clock = new goog.testing.MockClock(true);
    +
    +  var p = goog.Promise.resolve('foo');
    +  assertEquals('foo', clock.tickPromise(p));
    +
    +  var rejected = goog.Promise.reject(new Error('failed'));
    +  var e = assertThrows(function() {
    +    clock.tickPromise(rejected);
    +  });
    +  assertEquals('failed', e.message);
    +
    +  var delayed = goog.Timer.promise(500, 'delayed');
    +  e = assertThrows(function() {
    +    clock.tickPromise(delayed);
    +  });
    +  assertEquals('Promise was expected to be resolved after mock clock tick.',
    +      e.message);
    +  assertEquals('delayed', clock.tickPromise(delayed, 500));
    +
    +  clock.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockcontrol.js b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol.js
    new file mode 100644
    index 0000000000..0e2e3bc73a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol.js
    @@ -0,0 +1,220 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A MockControl holds a set of mocks for a particular test.
    + * It consolidates calls to $replay, $verify, and $tearDown, which simplifies
    + * the test and helps avoid omissions.
    + *
    + * You can create and control a mock:
    + *   var mockFoo = mockControl.addMock(new MyMock(Foo));
    + *
    + * MockControl also exposes some convenience functions that create
    + * controlled mocks for common mocks: StrictMock, LooseMock,
    + * FunctionMock, MethodMock, and GlobalFunctionMock.
    + *
    + */
    +
    +
    +goog.provide('goog.testing.MockControl');
    +
    +goog.require('goog.array');
    +goog.require('goog.testing');
    +goog.require('goog.testing.LooseMock');
    +goog.require('goog.testing.StrictMock');
    +
    +
    +
    +/**
    + * Controls a set of mocks.  Controlled mocks are replayed, verified, and
    + * cleaned-up at the same time.
    + * @constructor
    + */
    +goog.testing.MockControl = function() {
    +  /**
    +   * The list of mocks being controlled.
    +   * @type {Array<goog.testing.MockInterface>}
    +   * @private
    +   */
    +  this.mocks_ = [];
    +};
    +
    +
    +/**
    + * Takes control of this mock.
    + * @param {goog.testing.MockInterface} mock Mock to be controlled.
    + * @return {goog.testing.MockInterface} The same mock passed in,
    + *     for convenience.
    + */
    +goog.testing.MockControl.prototype.addMock = function(mock) {
    +  this.mocks_.push(mock);
    +  return mock;
    +};
    +
    +
    +/**
    + * Calls replay on each controlled mock.
    + */
    +goog.testing.MockControl.prototype.$replayAll = function() {
    +  goog.array.forEach(this.mocks_, function(m) {
    +    m.$replay();
    +  });
    +};
    +
    +
    +/**
    + * Calls reset on each controlled mock.
    + */
    +goog.testing.MockControl.prototype.$resetAll = function() {
    +  goog.array.forEach(this.mocks_, function(m) {
    +    m.$reset();
    +  });
    +};
    +
    +
    +/**
    + * Calls verify on each controlled mock.
    + */
    +goog.testing.MockControl.prototype.$verifyAll = function() {
    +  goog.array.forEach(this.mocks_, function(m) {
    +    m.$verify();
    +  });
    +};
    +
    +
    +/**
    + * Calls tearDown on each controlled mock, if necesssary.
    + */
    +goog.testing.MockControl.prototype.$tearDown = function() {
    +  goog.array.forEach(this.mocks_, function(m) {
    +    // $tearDown if defined.
    +    if (m.$tearDown) {
    +      m.$tearDown();
    +    }
    +    // TODO(user): Somehow determine if verifyAll should have been called
    +    // but was not.
    +  });
    +};
    +
    +
    +/**
    + * Creates a controlled StrictMock.  Passes its arguments through to the
    + * StrictMock constructor.
    + * @param {Object|Function} objectToMock The object that should be mocked, or
    + *    the constructor of an object to mock.
    + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
    + *     a mock should be constructed from the static functions of a class.
    + * @param {boolean=} opt_createProxy An optional argument denoting that
    + *     a proxy for the target mock should be created.
    + * @return {!goog.testing.StrictMock} The mock object.
    + */
    +goog.testing.MockControl.prototype.createStrictMock = function(
    +    objectToMock, opt_mockStaticMethods, opt_createProxy) {
    +  var m = new goog.testing.StrictMock(objectToMock, opt_mockStaticMethods,
    +                                      opt_createProxy);
    +  this.addMock(m);
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a controlled LooseMock.  Passes its arguments through to the
    + * LooseMock constructor.
    + * @param {Object|Function} objectToMock The object that should be mocked, or
    + *    the constructor of an object to mock.
    + * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
    + *     calls.
    + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
    + *     a mock should be constructed from the static functions of a class.
    + * @param {boolean=} opt_createProxy An optional argument denoting that
    + *     a proxy for the target mock should be created.
    + * @return {!goog.testing.LooseMock} The mock object.
    + */
    +goog.testing.MockControl.prototype.createLooseMock = function(
    +    objectToMock, opt_ignoreUnexpectedCalls,
    +    opt_mockStaticMethods, opt_createProxy) {
    +  var m = new goog.testing.LooseMock(objectToMock, opt_ignoreUnexpectedCalls,
    +                                     opt_mockStaticMethods, opt_createProxy);
    +  this.addMock(m);
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a controlled FunctionMock.  Passes its arguments through to the
    + * FunctionMock constructor.
    + * @param {string=} opt_functionName The optional name of the function to mock
    + *     set to '[anonymous mocked function]' if not passed in.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {goog.testing.MockInterface} The mocked function.
    + */
    +goog.testing.MockControl.prototype.createFunctionMock = function(
    +    opt_functionName, opt_strictness) {
    +  var m = goog.testing.createFunctionMock(opt_functionName, opt_strictness);
    +  this.addMock(m);
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a controlled MethodMock.  Passes its arguments through to the
    + * MethodMock constructor.
    + * @param {Object} scope The scope of the method to be mocked out.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked method.
    + */
    +goog.testing.MockControl.prototype.createMethodMock = function(
    +    scope, functionName, opt_strictness) {
    +  var m = goog.testing.createMethodMock(scope, functionName, opt_strictness);
    +  this.addMock(m);
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a controlled MethodMock for a constructor.  Passes its arguments
    + * through to the MethodMock constructor. See
    + * {@link goog.testing.createConstructorMock} for details.
    + * @param {Object} scope The scope of the constructor to be mocked out.
    + * @param {string} constructorName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {!goog.testing.MockInterface} The mocked method.
    + */
    +goog.testing.MockControl.prototype.createConstructorMock = function(
    +    scope, constructorName, opt_strictness) {
    +  var m = goog.testing.createConstructorMock(scope, constructorName,
    +                                             opt_strictness);
    +  this.addMock(m);
    +  return m;
    +};
    +
    +
    +/**
    + * Creates a controlled GlobalFunctionMock.  Passes its arguments through to the
    + * GlobalFunctionMock constructor.
    + * @param {string} functionName The name of the function we're going to mock.
    + * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
    + *     goog.testing.Mock.STRICT. The default is STRICT.
    + * @return {goog.testing.MockInterface} The mocked function.
    + */
    +goog.testing.MockControl.prototype.createGlobalFunctionMock = function(
    +    functionName, opt_strictness) {
    +  var m = goog.testing.createGlobalFunctionMock(functionName, opt_strictness);
    +  this.addMock(m);
    +  return m;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.html
    new file mode 100644
    index 0000000000..193f6283b9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockControl
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockControlTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.js
    new file mode 100644
    index 0000000000..a73e538b4a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockcontrol_test.js
    @@ -0,0 +1,121 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockControlTest');
    +goog.setTestOnly('goog.testing.MockControlTest');
    +
    +goog.require('goog.testing.Mock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +
    +// Emulate the behavior of a mock.
    +function MockMock() {
    +  this.replayCalled = false;
    +  this.resetCalled = false;
    +  this.verifyCalled = false;
    +  this.tearDownCalled = false;
    +}
    +
    +MockMock.prototype.$replay = function() {
    +  this.replayCalled = true;
    +};
    +
    +MockMock.prototype.$reset = function() {
    +  this.resetCalled = true;
    +};
    +
    +MockMock.prototype.$verify = function() {
    +  this.verifyCalled = true;
    +};
    +
    +function setUp() {
    +  var mock = new goog.testing.Mock(MockMock);
    +}
    +
    +function testAdd() {
    +  var mockMock = new MockMock();
    +
    +  var control = new goog.testing.MockControl();
    +  assertEquals(mockMock, control.addMock(mockMock));
    +}
    +
    +function testReplayAll() {
    +  var mockMock1 = new MockMock();
    +  var mockMock2 = new MockMock();
    +  var mockMockExcluded = new MockMock();
    +
    +  var control = new goog.testing.MockControl();
    +  control.addMock(mockMock1);
    +  control.addMock(mockMock2);
    +
    +  control.$replayAll();
    +  assertTrue(mockMock1.replayCalled);
    +  assertTrue(mockMock2.replayCalled);
    +  assertFalse(mockMockExcluded.replayCalled);
    +}
    +
    +function testResetAll() {
    +  var mockMock1 = new MockMock();
    +  var mockMock2 = new MockMock();
    +  var mockMockExcluded = new MockMock();
    +
    +  var control = new goog.testing.MockControl();
    +  control.addMock(mockMock1);
    +  control.addMock(mockMock2);
    +
    +  control.$resetAll();
    +  assertTrue(mockMock1.resetCalled);
    +  assertTrue(mockMock2.resetCalled);
    +  assertFalse(mockMockExcluded.resetCalled);
    +}
    +
    +function testVerifyAll() {
    +  var mockMock1 = new MockMock();
    +  var mockMock2 = new MockMock();
    +  var mockMockExcluded = new MockMock();
    +
    +  var control = new goog.testing.MockControl();
    +  control.addMock(mockMock1);
    +  control.addMock(mockMock2);
    +
    +  control.$verifyAll();
    +  assertTrue(mockMock1.verifyCalled);
    +  assertTrue(mockMock2.verifyCalled);
    +  assertFalse(mockMockExcluded.verifyCalled);
    +}
    +
    +function testTearDownAll() {
    +  var mockMock1 = new MockMock();
    +  var mockMock2 = new MockMock();
    +  var mockMockExcluded = new MockMock();
    +
    +  // $tearDown is optional.
    +  mockMock2.$tearDown = function() {
    +    this.tearDownCalled = true;
    +  };
    +  mockMockExcluded.$tearDown = function() {
    +    this.tearDownCalled = true;
    +  };
    +
    +  var control = new goog.testing.MockControl();
    +  control.addMock(mockMock1);
    +  control.addMock(mockMock2);
    +
    +  control.$tearDown();
    +
    +  // mockMock2 has a tearDown method and is in the control.
    +  assertTrue(mockMock2.tearDownCalled);
    +  assertFalse(mockMock1.tearDownCalled);
    +  assertFalse(mockMockExcluded.tearDownCalled);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockinterface.js b/mapbox-gl/closure-library/closure/goog/testing/mockinterface.js
    new file mode 100644
    index 0000000000..1a53470267
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockinterface.js
    @@ -0,0 +1,45 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An interface that all mocks should share.
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.testing.MockInterface');
    +
    +
    +
    +/** @interface */
    +goog.testing.MockInterface = function() {};
    +
    +
    +/**
    + * Write down all the expected functions that have been called on the
    + * mock so far. From here on out, future function calls will be
    + * compared against this list.
    + */
    +goog.testing.MockInterface.prototype.$replay = function() {};
    +
    +
    +/**
    + * Reset the mock.
    + */
    +goog.testing.MockInterface.prototype.$reset = function() {};
    +
    +
    +/**
    + * Assert that the expected function calls match the actual calls.
    + */
    +goog.testing.MockInterface.prototype.$verify = function() {};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockmatchers.js b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers.js
    new file mode 100644
    index 0000000000..a0545f73ab
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers.js
    @@ -0,0 +1,400 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Matchers to be used with the mock utilities.  They allow for
    + * flexible matching by type.  Custom matchers can be created by passing a
    + * matcher function into an ArgumentMatcher instance.
    + *
    + * For examples, please see the unit test.
    + *
    + */
    +
    +
    +goog.provide('goog.testing.mockmatchers');
    +goog.provide('goog.testing.mockmatchers.ArgumentMatcher');
    +goog.provide('goog.testing.mockmatchers.IgnoreArgument');
    +goog.provide('goog.testing.mockmatchers.InstanceOf');
    +goog.provide('goog.testing.mockmatchers.ObjectEquals');
    +goog.provide('goog.testing.mockmatchers.RegexpMatch');
    +goog.provide('goog.testing.mockmatchers.SaveArgument');
    +goog.provide('goog.testing.mockmatchers.TypeOf');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.testing.asserts');
    +
    +
    +
    +/**
    + * A simple interface for executing argument matching.  A match in this case is
    + * testing to see if a supplied object fits a given criteria.  True is returned
    + * if the given criteria is met.
    + * @param {Function=} opt_matchFn A function that evaluates a given argument
    + *     and returns true if it meets a given criteria.
    + * @param {?string=} opt_matchName The name expressing intent as part of
    + *      an error message for when a match fails.
    + * @constructor
    + */
    +goog.testing.mockmatchers.ArgumentMatcher =
    +    function(opt_matchFn, opt_matchName) {
    +  /**
    +   * A function that evaluates a given argument and returns true if it meets a
    +   * given criteria.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.matchFn_ = opt_matchFn || null;
    +
    +  /**
    +   * A string indicating the match intent (e.g. isBoolean or isString).
    +   * @type {?string}
    +   * @private
    +   */
    +  this.matchName_ = opt_matchName || null;
    +};
    +
    +
    +/**
    + * A function that takes a match argument and an optional MockExpectation
    + * which (if provided) will get error information and returns whether or
    + * not it matches.
    + * @param {*} toVerify The argument that should be verified.
    + * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
    + *     for this match.
    + * @return {boolean} Whether or not a given argument passes verification.
    + */
    +goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =
    +    function(toVerify, opt_expectation) {
    +  if (this.matchFn_) {
    +    var isamatch = this.matchFn_(toVerify);
    +    if (!isamatch && opt_expectation) {
    +      if (this.matchName_) {
    +        opt_expectation.addErrorMessage('Expected: ' +
    +            this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));
    +      } else {
    +        opt_expectation.addErrorMessage('Expected: missing mockmatcher' +
    +            ' description but was: ' +
    +            _displayStringForValue(toVerify));
    +      }
    +    }
    +    return isamatch;
    +  } else {
    +    throw Error('No match function defined for this mock matcher');
    +  }
    +};
    +
    +
    +
    +/**
    + * A matcher that verifies that an argument is an instance of a given class.
    + * @param {Function} ctor The class that will be used for verification.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.mockmatchers.InstanceOf = function(ctor) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function(obj) {
    +        return obj instanceof ctor;
    +        // NOTE: Browser differences on ctor.toString() output
    +        // make using that here problematic. So for now, just let
    +        // people know the instanceOf() failed without providing
    +        // browser specific details...
    +      }, 'instanceOf()');
    +};
    +goog.inherits(goog.testing.mockmatchers.InstanceOf,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +
    +/**
    + * A matcher that verifies that an argument is of a given type (e.g. "object").
    + * @param {string} type The type that a given argument must have.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.mockmatchers.TypeOf = function(type) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function(obj) {
    +        return goog.typeOf(obj) == type;
    +      }, 'typeOf(' + type + ')');
    +};
    +goog.inherits(goog.testing.mockmatchers.TypeOf,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +
    +/**
    + * A matcher that verifies that an argument matches a given RegExp.
    + * @param {RegExp} regexp The regular expression that the argument must match.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.mockmatchers.RegexpMatch = function(regexp) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function(str) {
    +        return regexp.test(str);
    +      }, 'match(' + regexp + ')');
    +};
    +goog.inherits(goog.testing.mockmatchers.RegexpMatch,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +
    +/**
    + * A matcher that always returns true. It is useful when the user does not care
    + * for some arguments.
    + * For example: mockFunction('username', 'password', IgnoreArgument);
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.mockmatchers.IgnoreArgument = function() {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function() {
    +        return true;
    +      }, 'true');
    +};
    +goog.inherits(goog.testing.mockmatchers.IgnoreArgument,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +
    +/**
    + * A matcher that verifies that the argument is an object that equals the given
    + * expected object, using a deep comparison.
    + * @param {Object} expectedObject An object to match against when
    + *     verifying the argument.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(this,
    +      function(matchObject) {
    +        assertObjectEquals('Expected equal objects', expectedObject,
    +            matchObject);
    +        return true;
    +      }, 'objectEquals(' + expectedObject + ')');
    +};
    +goog.inherits(goog.testing.mockmatchers.ObjectEquals,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +/** @override */
    +goog.testing.mockmatchers.ObjectEquals.prototype.matches =
    +    function(toVerify, opt_expectation) {
    +  // Override the default matches implementation to capture the exception thrown
    +  // by assertObjectEquals (if any) and add that message to the expectation.
    +  try {
    +    return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(
    +        this, toVerify, opt_expectation);
    +  } catch (e) {
    +    if (opt_expectation) {
    +      opt_expectation.addErrorMessage(e.message);
    +    }
    +    return false;
    +  }
    +};
    +
    +
    +
    +/**
    + * A matcher that saves the argument that it is verifying so that your unit test
    + * can perform extra tests with this argument later.  For example, if the
    + * argument is a callback method, the unit test can then later call this
    + * callback to test the asynchronous portion of the call.
    + * @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher
    + *     Argument matcher or matching function that will be used to validate the
    + *     argument.  By default, argument will always be valid.
    + * @param {?string=} opt_matchName The name expressing intent as part of
    + *      an error message for when a match fails.
    + * @constructor
    + * @extends {goog.testing.mockmatchers.ArgumentMatcher}
    + * @final
    + */
    +goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {
    +  goog.testing.mockmatchers.ArgumentMatcher.call(
    +      this, /** @type {Function} */ (opt_matcher), opt_matchName);
    +
    +  if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {
    +    /**
    +     * Delegate match requests to this matcher.
    +     * @type {goog.testing.mockmatchers.ArgumentMatcher}
    +     * @private
    +     */
    +    this.delegateMatcher_ = opt_matcher;
    +  } else if (!opt_matcher) {
    +    this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;
    +  }
    +};
    +goog.inherits(goog.testing.mockmatchers.SaveArgument,
    +    goog.testing.mockmatchers.ArgumentMatcher);
    +
    +
    +/** @override */
    +goog.testing.mockmatchers.SaveArgument.prototype.matches = function(
    +    toVerify, opt_expectation) {
    +  this.arg = toVerify;
    +  if (this.delegateMatcher_) {
    +    return this.delegateMatcher_.matches(toVerify, opt_expectation);
    +  }
    +  return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(
    +      this, toVerify, opt_expectation);
    +};
    +
    +
    +/**
    + * Saved argument that was verified.
    + * @type {*}
    + */
    +goog.testing.mockmatchers.SaveArgument.prototype.arg;
    +
    +
    +/**
    + * An instance of the IgnoreArgument matcher. Returns true for all matches.
    + * @type {goog.testing.mockmatchers.IgnoreArgument}
    + */
    +goog.testing.mockmatchers.ignoreArgument =
    +    new goog.testing.mockmatchers.IgnoreArgument();
    +
    +
    +/**
    + * A matcher that verifies that an argument is an array.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isArray =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,
    +        'isArray');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a array-like.  A NodeList is an
    + * example of a collection that is very close to an array.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isArrayLike =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,
    +        'isArrayLike');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a date-like.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isDateLike =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,
    +        'isDateLike');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a string.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isString =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,
    +        'isString');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a boolean.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isBoolean =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,
    +        'isBoolean');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a number.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isNumber =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,
    +        'isNumber');
    +
    +
    +/**
    + * A matcher that verifies that an argument is a function.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isFunction =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,
    +        'isFunction');
    +
    +
    +/**
    + * A matcher that verifies that an argument is an object.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isObject =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,
    +        'isObject');
    +
    +
    +/**
    + * A matcher that verifies that an argument is like a DOM node.
    + * @type {goog.testing.mockmatchers.ArgumentMatcher}
    + */
    +goog.testing.mockmatchers.isNodeLike =
    +    new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,
    +        'isNodeLike');
    +
    +
    +/**
    + * A function that checks to see if an array matches a given set of
    + * expectations.  The expectations array can be a mix of ArgumentMatcher
    + * implementations and values.  True will be returned if values are identical or
    + * if a matcher returns a positive result.
    + * @param {Array<?>} expectedArr An array of expectations which can be either
    + *     values to check for equality or ArgumentMatchers.
    + * @param {Array<?>} arr The array to match.
    + * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
    + *     for this match.
    + * @return {boolean} Whether or not the given array matches the expectations.
    + */
    +goog.testing.mockmatchers.flexibleArrayMatcher =
    +    function(expectedArr, arr, opt_expectation) {
    +  return goog.array.equals(expectedArr, arr, function(a, b) {
    +    var errCount = 0;
    +    if (opt_expectation) {
    +      errCount = opt_expectation.getErrorMessageCount();
    +    }
    +    var isamatch = a === b ||
    +        a instanceof goog.testing.mockmatchers.ArgumentMatcher &&
    +        a.matches(b, opt_expectation);
    +    var failureMessage = null;
    +    if (!isamatch) {
    +      failureMessage = goog.testing.asserts.findDifferences(a, b);
    +      isamatch = !failureMessage;
    +    }
    +    if (!isamatch && opt_expectation) {
    +      // If the error count changed, the match sent out an error
    +      // message. If the error count has not changed, then
    +      // we need to send out an error message...
    +      if (errCount == opt_expectation.getErrorMessageCount()) {
    +        // Use the _displayStringForValue() from assert.js
    +        // for consistency...
    +        if (!failureMessage) {
    +          failureMessage = 'Expected: ' + _displayStringForValue(a) +
    +              ' but was: ' + _displayStringForValue(b);
    +        }
    +        opt_expectation.addErrorMessage(failureMessage);
    +      }
    +    }
    +    return isamatch;
    +  });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.html
    new file mode 100644
    index 0000000000..f56b54f47b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  @author earmbrust@google.com (Erick Armbrust)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.mockmatchers
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.mockmatchersTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="someDiv">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.js
    new file mode 100644
    index 0000000000..68d8e25245
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockmatchers_test.js
    @@ -0,0 +1,378 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.mockmatchersTest');
    +goog.setTestOnly('goog.testing.mockmatchersTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.testing.mockmatchers.ArgumentMatcher');
    +
    +// A local reference to the mockmatchers namespace.
    +var matchers = goog.testing.mockmatchers;
    +
    +// Simple classes to test the InstanceOf matcher.
    +var foo = function() {};
    +var bar = function() {};
    +
    +// Simple class to test adding error messages to
    +// MockExpectation objects
    +function MockMock() {
    +  this.errorMessages = [];
    +}
    +
    +var mockExpect = null;
    +
    +MockMock.prototype.addErrorMessage = function(msg) {
    +  this.errorMessages.push(msg);
    +};
    +
    +
    +MockMock.prototype.getErrorMessageCount = function() {
    +  return this.errorMessages.length;
    +};
    +
    +
    +function setUp() {
    +  mockExpect = new MockMock();
    +}
    +
    +
    +function testNoMatchName() {
    +  // A matcher that does not fill in the match name
    +  var matcher = new goog.testing.mockmatchers.ArgumentMatcher(goog.isString);
    +
    +  // Make sure the lack of match name doesn't affect the ability
    +  // to return True/False
    +  assertTrue(matcher.matches('hello'));
    +  assertFalse(matcher.matches(123));
    +
    +  // Make sure we handle the lack of a match name
    +  assertFalse(matcher.matches(456, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: missing mockmatcher description ' +
    +      'but was: <456> (Number)', mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testInstanceOf() {
    +  var matcher = new matchers.InstanceOf(foo);
    +  assertTrue(matcher.matches(new foo()));
    +  assertFalse(matcher.matches(new bar()));
    +
    +  assertFalse(matcher.matches(new bar(), mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: instanceOf() ' +
    +      'but was: <[object Object]> (Object)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testTypeOf() {
    +  var matcher = new matchers.TypeOf('number');
    +  assertTrue(matcher.matches(1));
    +  assertTrue(matcher.matches(2));
    +  assertFalse(matcher.matches('test'));
    +
    +  assertFalse(matcher.matches(true, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: typeOf(number) but was: <true> (Boolean)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testRegexpMatch() {
    +  var matcher = new matchers.RegexpMatch(/^cho[dtp]/);
    +  assertTrue(matcher.matches('chodhop'));
    +  assertTrue(matcher.matches('chopper'));
    +  assertFalse(matcher.matches('chocolate'));
    +  assertFalse(matcher.matches(null));
    +
    +  assertFalse(matcher.matches('an anger', mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: match(/^cho[dtp]/) but was: <an anger> (String)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testObjectEquals() {
    +  // Test a simple match.
    +  var simpleMatcher = new matchers.ObjectEquals({name: 'Bob', age: 42});
    +  assertTrue(simpleMatcher.matches({name: 'Bob', age: 42}, mockExpect));
    +  assertEquals(0, mockExpect.getErrorMessageCount());
    +  expectObjectEqualsFailure(simpleMatcher, {name: 'Bill', age: 42},
    +      'name: Expected <Bob> (String) but was <Bill> (String)');
    +  expectObjectEqualsFailure(simpleMatcher, {name: 'Bob', age: 21},
    +      'age: Expected <42> (Number) but was <21> (Number)');
    +  expectObjectEqualsFailure(simpleMatcher, {name: 'Bob'},
    +      'property age not present in actual Object');
    +  expectObjectEqualsFailure(simpleMatcher,
    +      {name: 'Bob', age: 42, country: 'USA'},
    +      'property country not present in expected Object');
    +
    +  // Multiple mismatches should include multiple messages.
    +  expectObjectEqualsFailure(simpleMatcher, {name: 'Jim', age: 36},
    +      'name: Expected <Bob> (String) but was <Jim> (String)\n' +
    +      '   age: Expected <42> (Number) but was <36> (Number)');
    +}
    +
    +function testComplexObjectEquals() {
    +  var complexMatcher = new matchers.ObjectEquals(
    +      {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'baz', sub2: -1}});
    +  assertTrue(complexMatcher.matches(
    +      {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'baz', sub2: -1}}));
    +  expectObjectEqualsFailure(complexMatcher,
    +      {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'zap', sub2: -1}},
    +      'sub1: Expected <baz> (String) but was <zap> (String)');
    +  expectObjectEqualsFailure(complexMatcher,
    +      {a: 'foo', b: 2, c: ['bar', 6], d: {sub1: 'baz', sub2: -1}},
    +      'c[1]: Expected <3> (Number) but was <6> (Number)');
    +}
    +
    +
    +function testSaveArgument() {
    +  var saveMatcher = new matchers.SaveArgument();
    +  assertTrue(saveMatcher.matches(42));
    +  assertEquals(42, saveMatcher.arg);
    +
    +  saveMatcher = new matchers.SaveArgument(goog.isString);
    +  assertTrue(saveMatcher.matches('test'));
    +  assertEquals('test', saveMatcher.arg);
    +  assertFalse(saveMatcher.matches(17));
    +  assertEquals(17, saveMatcher.arg);
    +
    +  saveMatcher = new matchers.SaveArgument(new matchers.ObjectEquals({
    +    value: 'value'
    +  }));
    +  assertTrue(saveMatcher.matches({ value: 'value' }));
    +  assertEquals('value', saveMatcher.arg.value);
    +  assertFalse(saveMatcher.matches('test'));
    +  assertEquals('test', saveMatcher.arg);
    +}
    +
    +
    +function testIsArray() {
    +  assertTrue(matchers.isArray.matches([]));
    +  assertTrue(matchers.isArray.matches(new Array()));
    +  assertFalse(matchers.isArray.matches('test'));
    +
    +  assertFalse(matchers.isArray.matches({}, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isArray but was: <[object Object]> (Object)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsArrayLike() {
    +  var nodeList = (function() {
    +    var div = document.createElement('div');
    +    div.appendChild(document.createElement('p'));
    +    div.appendChild(document.createElement('p'));
    +    return div.getElementsByTagName('div');
    +  })();
    +
    +  assertTrue(matchers.isArrayLike.matches([]));
    +  assertTrue(matchers.isArrayLike.matches(new Array()));
    +  assertTrue(matchers.isArrayLike.matches(nodeList));
    +  assertFalse(matchers.isArrayLike.matches('test'));
    +
    +  assertFalse(matchers.isArrayLike.matches(3, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isArrayLike but was: <3> (Number)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsDateLike() {
    +  assertTrue(matchers.isDateLike.matches(new Date()));
    +  assertFalse(matchers.isDateLike.matches('test'));
    +
    +  assertFalse(matchers.isDateLike.matches('test', mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isDateLike but was: <test> (String)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsString() {
    +  assertTrue(matchers.isString.matches('a'));
    +  assertTrue(matchers.isString.matches('b'));
    +  assertFalse(matchers.isString.matches(null));
    +
    +  assertFalse(matchers.isString.matches(null, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isString but was: <null>',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsBoolean() {
    +  assertTrue(matchers.isBoolean.matches(true));
    +  assertTrue(matchers.isBoolean.matches(false));
    +  assertFalse(matchers.isBoolean.matches(null));
    +
    +  assertFalse(matchers.isBoolean.matches([], mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isBoolean but was: <> (Array)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsNumber() {
    +  assertTrue(matchers.isNumber.matches(-1));
    +  assertTrue(matchers.isNumber.matches(1));
    +  assertTrue(matchers.isNumber.matches(1.25));
    +  assertFalse(matchers.isNumber.matches(null));
    +
    +  assertFalse(matchers.isNumber.matches('hello', mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isNumber but was: <hello> (String)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsFunction() {
    +  assertTrue(matchers.isFunction.matches(function() {}));
    +  assertFalse(matchers.isFunction.matches('test'));
    +
    +  assertFalse(matchers.isFunction.matches({}, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isFunction but was: <[object Object]> (Object)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsObject() {
    +  assertTrue(matchers.isObject.matches({}));
    +  assertTrue(matchers.isObject.matches(new Object()));
    +  assertTrue(matchers.isObject.matches(new function() {}));
    +  assertTrue(matchers.isObject.matches([]));
    +  assertTrue(matchers.isObject.matches(new Array()));
    +  assertTrue(matchers.isObject.matches(function() {}));
    +  assertFalse(matchers.isObject.matches(null));
    +
    +  assertFalse(matchers.isObject.matches(1234, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isObject but was: <1234> (Number)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIsNodeLike() {
    +  assertFalse(matchers.isNodeLike.matches({}));
    +  assertFalse(matchers.isNodeLike.matches(1));
    +  assertFalse(matchers.isNodeLike.matches(function() {}));
    +  assertFalse(matchers.isNodeLike.matches(false));
    +  assertTrue(matchers.isNodeLike.matches(document.body));
    +  assertTrue(matchers.isNodeLike.matches(goog.dom.getElement('someDiv')));
    +
    +  assertFalse(matchers.isNodeLike.matches('test', mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals('Expected: isNodeLike but was: <test> (String)',
    +      mockExpect.errorMessages[0]);
    +}
    +
    +
    +function testIgnoreArgumentsMatcher() {
    +  // ignoreArgument always returns true:
    +  assertTrue(matchers.ignoreArgument.matches());
    +  assertTrue(matchers.ignoreArgument.matches(356));
    +  assertTrue(matchers.ignoreArgument.matches('str'));
    +  assertTrue(matchers.ignoreArgument.matches(['array', 123, false]));
    +  assertTrue(matchers.ignoreArgument.matches({'map': 1, key2: 'value2'}));
    +}
    +
    +
    +function testFlexibleArrayMatcher() {
    +  // Test that basic lists are verified properly.
    +  var a1 = [1, 'test'];
    +  var a2 = [1, 'test'];
    +  var a3 = [1, 'test', 'extra'];
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    +  assertFalse(matchers.flexibleArrayMatcher(a1, a3));
    +
    +  // Test that basic lists with basic class instances are verified properly.
    +  var instance = new foo();
    +  a1 = [1, 'test', instance];
    +  a2 = [1, 'test', instance];
    +  a3 = [1, 'test', new foo()];
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a3));
    +
    +  // Create an argument verifier that returns a consistent value.
    +  var verifyValue = true;
    +  var argVerifier = function() {};
    +  goog.inherits(argVerifier, matchers.ArgumentMatcher);
    +  argVerifier.prototype.matches = function(arg) {
    +    return verifyValue;
    +  };
    +
    +  // Test that the arguments are always verified when the verifier returns
    +  // true.
    +  a1 = [1, 'test', new argVerifier()];
    +  a2 = [1, 'test', 'anything'];
    +  a3 = [1, 'test', 12345];
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a3));
    +
    +  // Now test the case when then verifier returns false.
    +  verifyValue = false;
    +  assertFalse(matchers.flexibleArrayMatcher(a1, a2));
    +  assertFalse(matchers.flexibleArrayMatcher(a1, a3));
    +
    +  // And test we report errors back up via the opt_expectation
    +  assertFalse(matchers.flexibleArrayMatcher(a2, a3, mockExpect));
    +  assertEquals(1, mockExpect.errorMessages.length);
    +  assertEquals(
    +      'Expected <anything> (String) but was <12345> (Number)\n' +
    +      '    Expected <anything> (String) but was <12345> (Number)',
    +      mockExpect.errorMessages[0]);
    +
    +  // And test we report errors found via the matcher
    +  a1 = [1, goog.testing.mockmatchers.isString];
    +  a2 = [1, 'test string'];
    +  a3 = [1, null];
    +  assertTrue(matchers.flexibleArrayMatcher(a1, a2, mockExpect));
    +  assertFalse(matchers.flexibleArrayMatcher(a1, a3, mockExpect));
    +  // Old error is still there
    +  assertEquals(2, mockExpect.errorMessages.length);
    +  assertEquals(
    +      'Expected <anything> (String) but was <12345> (Number)\n' +
    +      '    Expected <anything> (String) but was <12345> (Number)',
    +      mockExpect.errorMessages[0]);
    +  // plus the new error...
    +  assertEquals('Expected: isString but was: <null>',
    +      mockExpect.errorMessages[1]);
    +}
    +
    +
    +/**
    + * Utility method for checking for an ObjectEquals match failure.  Checks that
    + * the expected error message was included in the error messages appended to
    + * the expectation object.
    + * @param {goog.testing.mockmatchers.ArgumentMatcher.ObjectEquals} matcher
    + *     The matcher to test against.
    + * @param {Object} matchObject The object to compare.
    + * @param {string=} opt_errorMsg The deep object comparison failure message
    + *     to check for.
    + */
    +function expectObjectEqualsFailure(matcher, matchObject, opt_errorMsg) {
    +  mockExpect.errorMessages = [];
    +  assertFalse(matcher.matches(matchObject, mockExpect));
    +  assertNotEquals(0, mockExpect.getErrorMessageCount());
    +  if (opt_errorMsg) {
    +    assertContains(opt_errorMsg, mockExpect.errorMessages[0]);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrandom.js b/mapbox-gl/closure-library/closure/goog/testing/mockrandom.js
    new file mode 100644
    index 0000000000..5c6d77c516
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrandom.js
    @@ -0,0 +1,153 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview MockRandom provides a mechanism for specifying a stream of
    + * numbers to expect from calls to Math.random().
    + *
    + */
    +
    +goog.provide('goog.testing.MockRandom');
    +
    +goog.require('goog.Disposable');
    +
    +
    +
    +/**
    + * Class for unit testing code that uses Math.random.
    + *
    + * @param {Array<number>} sequence The sequence of numbers to return.
    + * @param {boolean=} opt_install Whether to install the MockRandom at
    + *     construction time.
    + * @extends {goog.Disposable}
    + * @constructor
    + * @final
    + */
    +goog.testing.MockRandom = function(sequence, opt_install) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The sequence of numbers to be returned by calls to random()
    +   * @type {Array<number>}
    +   * @private
    +   */
    +  this.sequence_ = sequence || [];
    +
    +  /**
    +   * The original Math.random function.
    +   * @type {function(): number}
    +   * @private
    +   */
    +  this.mathRandom_ = Math.random;
    +
    +  /**
    +   * Whether to throw an exception when Math.random() is called when there is
    +   * nothing left in the sequence.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.strictlyFromSequence_ = false;
    +
    +  if (opt_install) {
    +    this.install();
    +  }
    +};
    +goog.inherits(goog.testing.MockRandom, goog.Disposable);
    +
    +
    +/**
    + * Whether this MockRandom has been installed.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MockRandom.prototype.installed_;
    +
    +
    +/**
    + * Installs this MockRandom as the system number generator.
    + */
    +goog.testing.MockRandom.prototype.install = function() {
    +  if (!this.installed_) {
    +    Math.random = goog.bind(this.random, this);
    +    this.installed_ = true;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The next number in the sequence. If there are no more values
    + *     left, this will return a random number, unless
    + *     {@code this.strictlyFromSequence_} is true, in which case an error will
    + *     be thrown.
    + */
    +goog.testing.MockRandom.prototype.random = function() {
    +  if (this.hasMoreValues()) {
    +    return this.sequence_.shift();
    +  }
    +  if (this.strictlyFromSequence_) {
    +    throw new Error('No numbers left in sequence.');
    +  }
    +  return this.mathRandom_();
    +};
    +
    +
    +/**
    + * @return {boolean} Whether there are more numbers left in the sequence.
    + */
    +goog.testing.MockRandom.prototype.hasMoreValues = function() {
    +  return this.sequence_.length > 0;
    +};
    +
    +
    +/**
    + * Injects new numbers into the beginning of the sequence.
    + * @param {Array<number>|number} values Number or array of numbers to inject.
    + */
    +goog.testing.MockRandom.prototype.inject = function(values) {
    +  if (goog.isArray(values)) {
    +    this.sequence_ = values.concat(this.sequence_);
    +  } else {
    +    this.sequence_.splice(0, 0, values);
    +  }
    +};
    +
    +
    +/**
    + * Uninstalls the MockRandom.
    + */
    +goog.testing.MockRandom.prototype.uninstall = function() {
    +  if (this.installed_) {
    +    Math.random = this.mathRandom_;
    +    this.installed_ = false;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.testing.MockRandom.prototype.disposeInternal = function() {
    +  this.uninstall();
    +  delete this.sequence_;
    +  delete this.mathRandom_;
    +  goog.testing.MockRandom.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * @param {boolean} strictlyFromSequence Whether to throw an exception when
    + *     Math.random() is called when there is nothing left in the sequence.
    + */
    +goog.testing.MockRandom.prototype.setStrictlyFromSequence =
    +    function(strictlyFromSequence) {
    +  this.strictlyFromSequence_ = strictlyFromSequence;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.html
    new file mode 100644
    index 0000000000..5094264a07
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockRandom
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockRandomTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.js
    new file mode 100644
    index 0000000000..73f4896778
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrandom_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockRandomTest');
    +goog.setTestOnly('goog.testing.MockRandomTest');
    +
    +goog.require('goog.testing.MockRandom');
    +goog.require('goog.testing.jsunit');
    +
    +function testMockRandomInstall() {
    +  var random = new goog.testing.MockRandom([]);
    +  var originalRandom = Math.random;
    +
    +  assertFalse(!!random.installed_);
    +
    +  random.install();
    +  assertTrue(random.installed_);
    +  assertNotEquals(Math.random, originalRandom);
    +
    +  random.uninstall();
    +  assertFalse(random.installed_);
    +  assertEquals(originalRandom, Math.random);
    +}
    +
    +function testMockRandomRandom() {
    +  var random = new goog.testing.MockRandom([], true);
    +
    +  assertFalse(random.hasMoreValues());
    +
    +  random.inject(2);
    +  assertTrue(random.hasMoreValues());
    +  assertEquals(2, Math.random());
    +
    +  random.inject([1, 2, 3]);
    +  assertTrue(random.hasMoreValues());
    +  assertEquals(1, Math.random());
    +  assertEquals(2, Math.random());
    +  assertEquals(3, Math.random());
    +  assertFalse(random.hasMoreValues());
    +  assertNotUndefined(Math.random());
    +}
    +
    +function testRandomStrictlyFromSequence() {
    +  var random = new goog.testing.MockRandom([], /* install */ true);
    +  random.setStrictlyFromSequence(true);
    +  assertFalse(random.hasMoreValues());
    +  assertThrows(function() {
    +    Math.random();
    +  });
    +
    +  random.inject(3);
    +  assertTrue(random.hasMoreValues());
    +  assertNotThrows(function() {
    +    Math.random();
    +  });
    +
    +  random.setStrictlyFromSequence(false);
    +  assertFalse(random.hasMoreValues());
    +  assertNotThrows(function() {
    +    Math.random();
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrange.js b/mapbox-gl/closure-library/closure/goog/testing/mockrange.js
    new file mode 100644
    index 0000000000..0e41892b18
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrange.js
    @@ -0,0 +1,67 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview LooseMock of goog.dom.AbstractRange.
    + *
    + */
    +
    +goog.provide('goog.testing.MockRange');
    +
    +goog.require('goog.dom.AbstractRange');
    +goog.require('goog.testing.LooseMock');
    +
    +
    +
    +/**
    + * LooseMock of goog.dom.AbstractRange. Useful because the mock framework cannot
    + * simply create a mock out of an abstract class, and cannot create a mock out
    + * of classes that implements __iterator__ because it relies on the default
    + * behavior of iterating through all of an object's properties.
    + * @constructor
    + * @extends {goog.testing.LooseMock}
    + * @final
    + */
    +goog.testing.MockRange = function() {
    +  goog.testing.LooseMock.call(this, goog.testing.MockRange.ConcreteRange_);
    +};
    +goog.inherits(goog.testing.MockRange, goog.testing.LooseMock);
    +
    +
    +// *** Private helper class ************************************************* //
    +
    +
    +
    +/**
    + * Concrete subclass of goog.dom.AbstractRange that simply sets the abstract
    + * method __iterator__ to undefined so that javascript defaults to iterating
    + * through all of the object's properties.
    + * @constructor
    + * @extends {goog.dom.AbstractRange}
    + * @private
    + */
    +goog.testing.MockRange.ConcreteRange_ = function() {
    +  goog.dom.AbstractRange.call(this);
    +};
    +goog.inherits(goog.testing.MockRange.ConcreteRange_, goog.dom.AbstractRange);
    +
    +
    +/**
    + * Undefine the iterator so the mock framework can loop through this class'
    + * properties.
    + * @override
    + */
    +goog.testing.MockRange.ConcreteRange_.prototype.__iterator__ =
    +    // This isn't really type-safe.
    +    /** @type {?} */ (undefined);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.html
    new file mode 100644
    index 0000000000..172b378a40
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockRange
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockRangeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.js
    new file mode 100644
    index 0000000000..c6c9db530f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockrange_test.js
    @@ -0,0 +1,35 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockRangeTest');
    +goog.setTestOnly('goog.testing.MockRangeTest');
    +
    +goog.require('goog.testing.MockRange');
    +goog.require('goog.testing.jsunit');
    +
    +
    +/**
    + * Tests that a MockRange can be created successfully, a call to a mock
    + * method can be recorded, and the correct behavior replayed and verified.
    + */
    +function testMockMethod() {
    +  var mockRange = new goog.testing.MockRange();
    +  mockRange.getStartOffset().$returns(42);
    +  mockRange.$replay();
    +
    +  assertEquals('Mock method should return recorded value',
    +               42,
    +               mockRange.getStartOffset());
    +  mockRange.$verify();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockstorage.js b/mapbox-gl/closure-library/closure/goog/testing/mockstorage.js
    new file mode 100644
    index 0000000000..e97d4bd9e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockstorage.js
    @@ -0,0 +1,108 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a JS storage class implementing the HTML5 Storage
    + * interface.
    + */
    +
    +
    +goog.require('goog.structs.Map');
    +
    +
    +goog.provide('goog.testing.MockStorage');
    +
    +
    +
    +/**
    + * A JS storage instance, implementing the HMTL5 Storage interface.
    + * See http://www.w3.org/TR/webstorage/ for details.
    + *
    + * @constructor
    + * @implements {Storage}
    + * @final
    + */
    +goog.testing.MockStorage = function() {
    +  /**
    +   * The underlying storage object.
    +   * @type {goog.structs.Map}
    +   * @private
    +   */
    +  this.store_ = new goog.structs.Map();
    +
    +  /**
    +   * The number of elements in the storage.
    +   * @type {number}
    +   */
    +  this.length = 0;
    +};
    +
    +
    +/**
    + * Sets an item to the storage.
    + * @param {string} key Storage key.
    + * @param {*} value Storage value. Must be convertible to string.
    + * @override
    + */
    +goog.testing.MockStorage.prototype.setItem = function(key, value) {
    +  this.store_.set(key, String(value));
    +  this.length = this.store_.getCount();
    +};
    +
    +
    +/**
    + * Gets an item from the storage.  The item returned is the "structured clone"
    + * of the value from setItem.  In practice this means it's the value cast to a
    + * string.
    + * @param {string} key Storage key.
    + * @return {?string} Storage value for key; null if does not exist.
    + * @override
    + */
    +goog.testing.MockStorage.prototype.getItem = function(key) {
    +  var val = this.store_.get(key);
    +  // Enforce that getItem returns string values.
    +  return (val != null) ? /** @type {string} */ (val) : null;
    +};
    +
    +
    +/**
    + * Removes and item from the storage.
    + * @param {string} key Storage key.
    + * @override
    + */
    +goog.testing.MockStorage.prototype.removeItem = function(key) {
    +  this.store_.remove(key);
    +  this.length = this.store_.getCount();
    +};
    +
    +
    +/**
    + * Clears the storage.
    + * @override
    + */
    +goog.testing.MockStorage.prototype.clear = function() {
    +  this.store_.clear();
    +  this.length = 0;
    +};
    +
    +
    +/**
    + * Returns the key at the given index.
    + * @param {number} index The index for the key.
    + * @return {?string} Key at the given index, null if not found.
    + * @override
    + */
    +goog.testing.MockStorage.prototype.key = function(index) {
    +  return this.store_.getKeys()[index] || null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.html
    new file mode 100644
    index 0000000000..ccc87fef75
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.MockStorage
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.MockStorageTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.js
    new file mode 100644
    index 0000000000..a4f686e17b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockstorage_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockStorageTest');
    +goog.setTestOnly('goog.testing.MockStorageTest');
    +
    +goog.require('goog.testing.MockStorage');
    +goog.require('goog.testing.jsunit');
    +
    +var instance;
    +
    +function setUp() {
    +  instance = new goog.testing.MockStorage();
    +}
    +
    +
    +/**
    + * Tests the MockStorage interface.
    + */
    +function testMockStorage() {
    +  assertEquals(0, instance.length);
    +
    +  instance.setItem('foo', 'bar');
    +  assertEquals(1, instance.length);
    +  assertEquals('bar', instance.getItem('foo'));
    +  assertEquals('foo', instance.key(0));
    +
    +  instance.setItem('foo', 'baz');
    +  assertEquals('baz', instance.getItem('foo'));
    +
    +  instance.setItem('goo', 'gl');
    +  assertEquals(2, instance.length);
    +  assertEquals('gl', instance.getItem('goo'));
    +  assertEquals('goo', instance.key(1));
    +
    +  assertNull(instance.getItem('poogle'));
    +
    +  instance.removeItem('foo');
    +  assertEquals(1, instance.length);
    +  assertEquals('goo', instance.key(0));
    +
    +  instance.setItem('a', 12);
    +  assertEquals('12', instance.getItem('a'));
    +  instance.setItem('b', false);
    +  assertEquals('false', instance.getItem('b'));
    +  instance.setItem('c', {a: 1, b: 12});
    +  assertEquals('[object Object]', instance.getItem('c'));
    +
    +  instance.clear();
    +  assertEquals(0, instance.length);
    +
    +  // Test some special cases.
    +  instance.setItem('emptyString', '');
    +  assertEquals('', instance.getItem('emptyString'));
    +  instance.setItem('isNull', null);
    +  assertEquals('null', instance.getItem('isNull'));
    +  instance.setItem('isUndefined', undefined);
    +  assertEquals('undefined', instance.getItem('isUndefined'));
    +  instance.setItem('', 'empty key');
    +  assertEquals('empty key', instance.getItem(''));
    +  assertEquals(4, instance.length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockuseragent.js b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent.js
    new file mode 100644
    index 0000000000..49db5e61ac
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent.js
    @@ -0,0 +1,143 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview MockUserAgent overrides goog.userAgent.getUserAgentString()
    + *     depending on a specified configuration.
    + *
    + */
    +
    +goog.provide('goog.testing.MockUserAgent');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Class for unit testing code that uses goog.userAgent.
    + *
    + * @extends {goog.Disposable}
    + * @constructor
    + * @final
    + */
    +goog.testing.MockUserAgent = function() {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * Property replacer used to mock out User-Agent functions.
    +   * @type {!goog.testing.PropertyReplacer}
    +   * @private
    +   */
    +  this.propertyReplacer_ = new goog.testing.PropertyReplacer();
    +
    +  /**
    +   * The userAgent string used by goog.userAgent.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.userAgent_ = goog.userAgent.getUserAgentString();
    +
    +  /**
    +   * The navigator object used by goog.userAgent
    +   * @type {Object}
    +   * @private
    +   */
    +  this.navigator_ = goog.userAgent.getNavigator();
    +};
    +goog.inherits(goog.testing.MockUserAgent, goog.Disposable);
    +
    +
    +/**
    + * Whether this MockUserAgent has been installed.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MockUserAgent.prototype.installed_;
    +
    +
    +/**
    + * Installs this MockUserAgent.
    + */
    +goog.testing.MockUserAgent.prototype.install = function() {
    +  if (!this.installed_) {
    +    // Stub out user agent functions.
    +    this.propertyReplacer_.set(goog.userAgent, 'getUserAgentString',
    +                               goog.bind(this.getUserAgentString, this));
    +
    +    this.propertyReplacer_.set(goog.labs.userAgent.util, 'getUserAgent',
    +                               goog.bind(this.getUserAgentString, this));
    +
    +    // Stub out navigator functions.
    +    this.propertyReplacer_.set(goog.userAgent, 'getNavigator',
    +                               goog.bind(this.getNavigator, this));
    +
    +    this.installed_ = true;
    +  }
    +};
    +
    +
    +/**
    + * @return {?string} The userAgent set in this class.
    + */
    +goog.testing.MockUserAgent.prototype.getUserAgentString = function() {
    +  return this.userAgent_;
    +};
    +
    +
    +/**
    + * @param {string} userAgent The desired userAgent string to use.
    + */
    +goog.testing.MockUserAgent.prototype.setUserAgentString = function(userAgent) {
    +  this.userAgent_ = userAgent;
    +};
    +
    +
    +/**
    + * @return {Object} The Navigator set in this class.
    + */
    +goog.testing.MockUserAgent.prototype.getNavigator = function() {
    +  return this.navigator_;
    +};
    +
    +
    +/**
    + * @param {Object} navigator The desired Navigator object to use.
    + */
    +goog.testing.MockUserAgent.prototype.setNavigator = function(navigator) {
    +  this.navigator_ = navigator;
    +};
    +
    +
    +/**
    + * Uninstalls the MockUserAgent.
    + */
    +goog.testing.MockUserAgent.prototype.uninstall = function() {
    +  if (this.installed_) {
    +    this.propertyReplacer_.reset();
    +    this.installed_ = false;
    +  }
    +
    +};
    +
    +
    +/** @override */
    +goog.testing.MockUserAgent.prototype.disposeInternal = function() {
    +  this.uninstall();
    +  delete this.propertyReplacer_;
    +  delete this.navigator_;
    +  goog.testing.MockUserAgent.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.html b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.html
    new file mode 100644
    index 0000000000..331994c04b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  @author andybons@google.com (Andrew Bonventre)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.testing.MockUserAgent</title>
    +<script src="../base.js"></script>
    +<body>
    +<script>
    +goog.require('goog.testing.MockUserAgentTest');
    +</script>
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.js b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.js
    new file mode 100644
    index 0000000000..913b3c25ed
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/mockuseragent_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.MockUserAgentTest');
    +
    +goog.require('goog.dispose');
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +goog.setTestOnly('goog.testing.MockUserAgentTest');
    +
    +var mockUserAgent;
    +
    +function setUp() {
    +  mockUserAgent = new goog.testing.MockUserAgent();
    +}
    +
    +function tearDown() {
    +  goog.dispose(mockUserAgent);
    +  assertFalse(mockUserAgent.installed_);
    +}
    +
    +function testMockUserAgentInstall() {
    +  var originalUserAgentFunction = goog.userAgent.getUserAgentString;
    +
    +  assertFalse(!!mockUserAgent.installed_);
    +
    +  mockUserAgent.install();
    +  assertTrue(mockUserAgent.installed_);
    +  assertNotEquals(goog.userAgent.getUserAgentString,
    +      originalUserAgentFunction);
    +
    +  mockUserAgent.uninstall();
    +  assertFalse(mockUserAgent.installed_);
    +  assertEquals(originalUserAgentFunction, goog.userAgent.getUserAgentString);
    +}
    +
    +function testMockUserAgentGetAgent() {
    +  var uaString = 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) ' +
    +      'AppleWebKit/525.13 (KHTML, like Gecko) ' +
    +      'Chrome/0.2.149.27 Safari/525.13';
    +
    +  mockUserAgent = new goog.testing.MockUserAgent();
    +  mockUserAgent.setUserAgentString(uaString);
    +  mockUserAgent.install();
    +
    +  assertTrue(mockUserAgent.installed_);
    +  assertEquals(uaString, goog.userAgent.getUserAgentString());
    +}
    +
    +function testMockUserAgentNavigator() {
    +  var fakeNavigator = {};
    +
    +  mockUserAgent = new goog.testing.MockUserAgent();
    +  mockUserAgent.setNavigator(fakeNavigator);
    +  mockUserAgent.install();
    +
    +  assertTrue(mockUserAgent.installed_);
    +  assertEquals(fakeNavigator, goog.userAgent.getNavigator());
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/multitestrunner.js b/mapbox-gl/closure-library/closure/goog/testing/multitestrunner.js
    new file mode 100644
    index 0000000000..c7c7246f35
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/multitestrunner.js
    @@ -0,0 +1,1450 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility for running multiple test files that utilize the same
    + * interface as goog.testing.TestRunner.  Each test is run in series and their
    + * results aggregated.  The main usecase for the MultiTestRunner is to allow
    + * the testing of all tests in a project locally.
    + *
    + */
    +
    +goog.provide('goog.testing.MultiTestRunner');
    +goog.provide('goog.testing.MultiTestRunner.TestFrame');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ServerChart');
    +goog.require('goog.ui.TableSorter');
    +
    +
    +
    +/**
    + * A component for running multiple tests within the browser.
    + * @param {goog.dom.DomHelper=} opt_domHelper A DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + * @final
    + */
    +goog.testing.MultiTestRunner = function(opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Array of tests to execute, when combined with the base path this should be
    +   * a relative path to the test from the page containing the multi testrunner.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.allTests_ = [];
    +
    +  /**
    +   * Tests that match the filter function.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.activeTests_ = [];
    +
    +  /**
    +   * An event handler for handling events.
    +   * @type {goog.events.EventHandler<!goog.testing.MultiTestRunner>}
    +   * @private
    +   */
    +  this.eh_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * A table sorter for the stats.
    +   * @type {goog.ui.TableSorter}
    +   * @private
    +   */
    +  this.tableSorter_ = new goog.ui.TableSorter(this.dom_);
    +};
    +goog.inherits(goog.testing.MultiTestRunner, goog.ui.Component);
    +
    +
    +/**
    + * Default maximimum amount of time to spend at each stage of the test.
    + * @type {number}
    + */
    +goog.testing.MultiTestRunner.DEFAULT_TIMEOUT_MS = 45 * 1000;
    +
    +
    +/**
    + * Messages corresponding to the numeric states.
    + * @type {Array<string>}
    + */
    +goog.testing.MultiTestRunner.STATES = [
    +  'waiting for test runner',
    +  'initializing tests',
    +  'waiting for tests to finish'
    +];
    +
    +
    +/**
    + * The test suite's name.
    + * @type {string} name
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.name_ = '';
    +
    +
    +/**
    + * The base path used to resolve files within the allTests_ array.
    + * @type {string}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.basePath_ = '';
    +
    +
    +/**
    + * A set of tests that have finished.  All extant keys map to true.
    + * @type {Object<boolean>}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.finished_ = null;
    +
    +
    +/**
    + * Whether the report should contain verbose information about the passes.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.verbosePasses_ = false;
    +
    +
    +/**
    + * Whether to hide passing tests completely in the report, makes verbosePasses_
    + * obsolete.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.hidePasses_ = false;
    +
    +
    +/**
    + * Flag used to tell the test runner to stop after the current test.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.stopped_ = false;
    +
    +
    +/**
    + * Flag indicating whether the test runner is active.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.active_ = false;
    +
    +
    +/**
    + * Index of the next test to run.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.startedCount_ = 0;
    +
    +
    +/**
    + * Count of the results received so far.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.resultCount_ = 0;
    +
    +
    +/**
    + * Number of passes so far.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.passes_ = 0;
    +
    +
    +/**
    + * Timestamp for the current start time.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.startTime_ = 0;
    +
    +
    +/**
    + * Only tests whose paths patch this filter function will be
    + * executed.
    + * @type {function(string): boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.filterFn_ = goog.functions.TRUE;
    +
    +
    +/**
    + * Number of milliseconds to wait for loading and initialization steps.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.timeoutMs_ =
    +    goog.testing.MultiTestRunner.DEFAULT_TIMEOUT_MS;
    +
    +
    +/**
    + * An array of objects containing stats about the tests.
    + * @type {Array<Object>?}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.stats_ = null;
    +
    +
    +/**
    + * Reference to the start button element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.startButtonEl_ = null;
    +
    +
    +/**
    + * Reference to the stop button element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.stopButtonEl_ = null;
    +
    +
    +/**
    + * Reference to the log element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.logEl_ = null;
    +
    +
    +/**
    + * Reference to the report element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.reportEl_ = null;
    +
    +
    +/**
    + * Reference to the stats element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.statsEl_ = null;
    +
    +
    +/**
    + * Reference to the progress bar's element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.progressEl_ = null;
    +
    +
    +/**
    + * Reference to the progress bar's inner row element.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.progressRow_ = null;
    +
    +
    +/**
    + * Reference to the log tab.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.logTabEl_ = null;
    +
    +
    +/**
    + * Reference to the report tab.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.reportTabEl_ = null;
    +
    +
    +/**
    + * Reference to the stats tab.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.statsTabEl_ = null;
    +
    +
    +/**
    + * The number of tests to run at a time.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.poolSize_ = 1;
    +
    +
    +/**
    + * The size of the stats bucket for the number of files loaded histogram.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.numFilesStatsBucketSize_ = 20;
    +
    +
    +/**
    + * The size of the stats bucket in ms for the run time histogram.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.runTimeStatsBucketSize_ = 500;
    +
    +
    +/**
    + * Sets the name for the test suite.
    + * @param {string} name The suite's name.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setName = function(name) {
    +  this.name_ = name;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the name for the test suite.
    + * @return {string} The name for the test suite.
    + */
    +goog.testing.MultiTestRunner.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * Sets the basepath that tests added using addTests are resolved with.
    + * @param {string} path The relative basepath.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setBasePath = function(path) {
    +  this.basePath_ = path;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the basepath that tests added using addTests are resolved with.
    + * @return {string} The basepath that tests added using addTests are resolved
    + *     with.
    + */
    +goog.testing.MultiTestRunner.prototype.getBasePath = function() {
    +  return this.basePath_;
    +};
    +
    +
    +/**
    + * Sets whether the report should contain verbose information for tests that
    + * pass.
    + * @param {boolean} verbose Whether report should be verbose.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setVerbosePasses = function(verbose) {
    +  this.verbosePasses_ = verbose;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns whether the report should contain verbose information for tests that
    + * pass.
    + * @return {boolean} Whether the report should contain verbose information for
    + *     tests that pass.
    + */
    +goog.testing.MultiTestRunner.prototype.getVerbosePasses = function() {
    +  return this.verbosePasses_;
    +};
    +
    +
    +/**
    + * Sets whether the report should contain passing tests at all, makes
    + * setVerbosePasses obsolete.
    + * @param {boolean} hide Whether report should not contain passing tests.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setHidePasses = function(hide) {
    +  this.hidePasses_ = hide;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns whether the report should contain passing tests at all, makes
    + * setVerbosePasses obsolete.
    + * @return {boolean} Whether the report should contain passing tests at all,
    + *     makes setVerbosePasses obsolete.
    + */
    +goog.testing.MultiTestRunner.prototype.getHidePasses = function() {
    +  return this.hidePasses_;
    +};
    +
    +
    +/**
    + * Sets the bucket sizes for the histograms.
    + * @param {number} f Bucket size for num files loaded histogram.
    + * @param {number} t Bucket size for run time histogram.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setStatsBucketSizes = function(f, t) {
    +  this.numFilesStatsBucketSize_ = f;
    +  this.runTimeStatsBucketSize_ = t;
    +  return this;
    +};
    +
    +
    +/**
    + * Sets the number of milliseconds to wait for the page to load, initialize and
    + * run the tests.
    + * @param {number} timeout Time in milliseconds.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setTimeout = function(timeout) {
    +  this.timeoutMs_ = timeout;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the number of milliseconds to wait for the page to load, initialize
    + * and run the tests.
    + * @return {number} The number of milliseconds to wait for the page to load,
    + *     initialize and run the tests.
    + */
    +goog.testing.MultiTestRunner.prototype.getTimeout = function() {
    +  return this.timeoutMs_;
    +};
    +
    +
    +/**
    + * Sets the number of tests that can be run at the same time. This only improves
    + * performance due to the amount of time spent loading the tests.
    + * @param {number} size The number of tests to run at a time.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setPoolSize = function(size) {
    +  this.poolSize_ = size;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the number of tests that can be run at the same time. This only
    + * improves performance due to the amount of time spent loading the tests.
    + * @return {number} The number of tests that can be run at the same time. This
    + *     only improves performance due to the amount of time spent loading the
    + *     tests.
    + */
    +goog.testing.MultiTestRunner.prototype.getPoolSize = function() {
    +  return this.poolSize_;
    +};
    +
    +
    +/**
    + * Sets a filter function. Only test paths that match the filter function
    + * will be executed.
    + * @param {function(string): boolean} filterFn Filters test paths.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.setFilterFunction = function(filterFn) {
    +  this.filterFn_ = filterFn;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns a filter function. Only test paths that match the filter function
    + * will be executed.
    + * @return {function(string): boolean} A filter function. Only test paths that
    + *     match the filter function will be executed.
    +
    + */
    +goog.testing.MultiTestRunner.prototype.getFilterFunction = function() {
    +  return this.filterFn_;
    +};
    +
    +
    +/**
    + * Adds an array of tests to the tests that the test runner should execute.
    + * @param {Array<string>} tests Adds tests to the test runner.
    + * @return {!goog.testing.MultiTestRunner} Instance for chaining.
    + */
    +goog.testing.MultiTestRunner.prototype.addTests = function(tests) {
    +  goog.array.extend(this.allTests_, tests);
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the list of all tests added to the runner.
    + * @return {Array<string>} The list of all tests added to the runner.
    + */
    +goog.testing.MultiTestRunner.prototype.getAllTests = function() {
    +  return this.allTests_;
    +};
    +
    +
    +/**
    + * Returns the list of tests that will be run when start() is called.
    + * @return {!Array<string>} The list of tests that will be run when start() is
    + *     called.
    + */
    +goog.testing.MultiTestRunner.prototype.getTestsToRun = function() {
    +  return goog.array.filter(this.allTests_, this.filterFn_);
    +};
    +
    +
    +/**
    + * Returns a list of tests from runner that have been marked as failed.
    + * @return {!Array<string>} A list of tests from runner that have been marked
    + *     as failed.
    + */
    +goog.testing.MultiTestRunner.prototype.getTestsThatFailed = function() {
    +  var stats = this.stats_;
    +  var failedTests = [];
    +  if (stats) {
    +    for (var i = 0, stat; stat = stats[i]; i++) {
    +      if (!stat.success) {
    +        failedTests.push(stat.testFile);
    +      }
    +    }
    +  }
    +  return failedTests;
    +};
    +
    +
    +/**
    + * Deletes and re-creates the progress table inside the progess element.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.resetProgressDom_ = function() {
    +  goog.dom.removeChildren(this.progressEl_);
    +  var progressTable = this.dom_.createDom('table');
    +  var progressTBody = this.dom_.createDom('tbody');
    +  this.progressRow_ = this.dom_.createDom('tr');
    +  for (var i = 0; i < this.activeTests_.length; i++) {
    +    var progressCell = this.dom_.createDom('td');
    +    this.progressRow_.appendChild(progressCell);
    +  }
    +  progressTBody.appendChild(this.progressRow_);
    +  progressTable.appendChild(progressTBody);
    +  this.progressEl_.appendChild(progressTable);
    +};
    +
    +
    +/** @override */
    +goog.testing.MultiTestRunner.prototype.createDom = function() {
    +  goog.testing.MultiTestRunner.superClass_.createDom.call(this);
    +  var el = this.getElement();
    +  el.className = goog.getCssName('goog-testrunner');
    +
    +  this.progressEl_ = this.dom_.createDom('div');
    +  this.progressEl_.className = goog.getCssName('goog-testrunner-progress');
    +  el.appendChild(this.progressEl_);
    +
    +  var buttons = this.dom_.createDom('div');
    +  buttons.className = goog.getCssName('goog-testrunner-buttons');
    +  this.startButtonEl_ = this.dom_.createDom('button', null, 'Start');
    +  this.stopButtonEl_ =
    +      this.dom_.createDom('button', {'disabled': true}, 'Stop');
    +  buttons.appendChild(this.startButtonEl_);
    +  buttons.appendChild(this.stopButtonEl_);
    +  el.appendChild(buttons);
    +
    +  this.eh_.listen(this.startButtonEl_, 'click',
    +      this.onStartClicked_);
    +  this.eh_.listen(this.stopButtonEl_, 'click',
    +      this.onStopClicked_);
    +
    +  this.logEl_ = this.dom_.createElement('div');
    +  this.logEl_.className = goog.getCssName('goog-testrunner-log');
    +  el.appendChild(this.logEl_);
    +
    +  this.reportEl_ = this.dom_.createElement('div');
    +  this.reportEl_.className = goog.getCssName('goog-testrunner-report');
    +  this.reportEl_.style.display = 'none';
    +  el.appendChild(this.reportEl_);
    +
    +  this.statsEl_ = this.dom_.createElement('div');
    +  this.statsEl_.className = goog.getCssName('goog-testrunner-stats');
    +  this.statsEl_.style.display = 'none';
    +  el.appendChild(this.statsEl_);
    +
    +  this.logTabEl_ = this.dom_.createDom('div', null, 'Log');
    +  this.logTabEl_.className = goog.getCssName('goog-testrunner-logtab') + ' ' +
    +      goog.getCssName('goog-testrunner-activetab');
    +  el.appendChild(this.logTabEl_);
    +
    +  this.reportTabEl_ = this.dom_.createDom('div', null, 'Report');
    +  this.reportTabEl_.className = goog.getCssName('goog-testrunner-reporttab');
    +  el.appendChild(this.reportTabEl_);
    +
    +  this.statsTabEl_ = this.dom_.createDom('div', null, 'Stats');
    +  this.statsTabEl_.className = goog.getCssName('goog-testrunner-statstab');
    +  el.appendChild(this.statsTabEl_);
    +
    +  this.eh_.listen(this.logTabEl_, 'click', this.onLogTabClicked_);
    +  this.eh_.listen(this.reportTabEl_, 'click', this.onReportTabClicked_);
    +  this.eh_.listen(this.statsTabEl_, 'click', this.onStatsTabClicked_);
    +
    +};
    +
    +
    +/** @override */
    +goog.testing.MultiTestRunner.prototype.disposeInternal = function() {
    +  goog.testing.MultiTestRunner.superClass_.disposeInternal.call(this);
    +  this.tableSorter_.dispose();
    +  this.eh_.dispose();
    +  this.startButtonEl_ = null;
    +  this.stopButtonEl_ = null;
    +  this.logEl_ = null;
    +  this.reportEl_ = null;
    +  this.progressEl_ = null;
    +  this.logTabEl_ = null;
    +  this.reportTabEl_ = null;
    +  this.statsTabEl_ = null;
    +  this.statsEl_ = null;
    +};
    +
    +
    +/**
    + * Starts executing the tests.
    + */
    +goog.testing.MultiTestRunner.prototype.start = function() {
    +  this.startButtonEl_.disabled = true;
    +  this.stopButtonEl_.disabled = false;
    +  this.stopped_ = false;
    +  this.active_ = true;
    +  this.finished_ = {};
    +  this.activeTests_ = this.getTestsToRun();
    +  this.startedCount_ = 0;
    +  this.resultCount_ = 0;
    +  this.passes_ = 0;
    +  this.stats_ = [];
    +  this.startTime_ = goog.now();
    +
    +  this.resetProgressDom_();
    +  goog.dom.removeChildren(this.logEl_);
    +
    +  this.resetReport_();
    +  this.clearStats_();
    +  this.showTab_(0);
    +
    +  // Ensure the pool isn't too big.
    +  while (this.getChildCount() > this.poolSize_) {
    +    this.removeChildAt(0, true).dispose();
    +  }
    +
    +  // Start a test in each runner.
    +  for (var i = 0; i < this.poolSize_; i++) {
    +    if (i >= this.getChildCount()) {
    +      var testFrame = new goog.testing.MultiTestRunner.TestFrame(
    +          this.basePath_, this.timeoutMs_, this.verbosePasses_, this.dom_);
    +      this.addChild(testFrame, true);
    +    }
    +    this.runNextTest_(
    +        /** @type {goog.testing.MultiTestRunner.TestFrame} */
    +        (this.getChildAt(i)));
    +  }
    +};
    +
    +
    +/**
    + * Logs a message to the log window.
    + * @param {string} msg A message to log.
    + */
    +goog.testing.MultiTestRunner.prototype.log = function(msg) {
    +  if (msg != '.') {
    +    msg = this.getTimeStamp_() + ' : ' + msg;
    +  }
    +
    +  this.logEl_.appendChild(this.dom_.createDom('div', null, msg));
    +
    +  // Autoscroll if we're near the bottom.
    +  var top = this.logEl_.scrollTop;
    +  var height = this.logEl_.scrollHeight - this.logEl_.offsetHeight;
    +  if (top == 0 || top > height - 50) {
    +    this.logEl_.scrollTop = height;
    +  }
    +};
    +
    +
    +/**
    + * Processes a result returned from a TestFrame.  If there are tests remaining
    + * it will trigger the next one to be run, otherwise if there are no tests and
    + * all results have been recieved then it will call finish.
    + * @param {goog.testing.MultiTestRunner.TestFrame} frame The frame that just
    + *     finished.
    + */
    +goog.testing.MultiTestRunner.prototype.processResult = function(frame) {
    +  var success = frame.isSuccess();
    +  var report = frame.getReport();
    +  var test = frame.getTestFile();
    +
    +  this.stats_.push(frame.getStats());
    +  this.finished_[test] = true;
    +
    +  var prefix = success ? '' : '*** FAILURE *** ';
    +  this.log(prefix +
    +      this.trimFileName_(test) + ' : ' + (success ? 'Passed' : 'Failed'));
    +
    +  this.resultCount_++;
    +
    +  if (success) {
    +    this.passes_++;
    +  }
    +
    +  this.drawProgressSegment_(test, success);
    +  this.writeCurrentSummary_();
    +  if (!(success && this.hidePasses_)) {
    +    this.drawTestResult_(test, success, report);
    +  }
    +
    +  if (!this.stopped_ && this.startedCount_ < this.activeTests_.length) {
    +    this.runNextTest_(frame);
    +  } else if (this.resultCount_ == this.activeTests_.length) {
    +    this.finish_();
    +  }
    +};
    +
    +
    +/**
    + * Runs the next available test, if there are any left.
    + * @param {goog.testing.MultiTestRunner.TestFrame} frame Where to run the test.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.runNextTest_ = function(frame) {
    +  if (this.startedCount_ < this.activeTests_.length) {
    +    var nextTest = this.activeTests_[this.startedCount_++];
    +    this.log(this.trimFileName_(nextTest) + ' : Loading');
    +    frame.runTest(nextTest);
    +  }
    +};
    +
    +
    +/**
    + * Handles the test finishing, processing the results and rendering the report.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.finish_ = function() {
    +  if (this.stopped_) {
    +    this.log('Stopped');
    +  } else {
    +    this.log('Finished');
    +  }
    +
    +  this.startButtonEl_.disabled = false;
    +  this.stopButtonEl_.disabled = true;
    +  this.active_ = false;
    +
    +  this.showTab_(1);
    +  this.drawStats_();
    +
    +  // Remove all the test frames
    +  while (this.getChildCount() > 0) {
    +    this.removeChildAt(0, true).dispose();
    +  }
    +
    +  // Compute tests that did not finish before the stop button was hit.
    +  var unfinished = [];
    +  for (var i = 0; i < this.activeTests_.length; i++) {
    +    var test = this.activeTests_[i];
    +    if (!this.finished_[test]) {
    +      unfinished.push(test);
    +    }
    +  }
    +
    +  if (unfinished.length) {
    +    this.reportEl_.appendChild(goog.dom.createDom('pre', undefined,
    +        'Theses tests did not finish:\n' + unfinished.join('\n')));
    +  }
    +};
    +
    +
    +/**
    + * Resets the report, clearing out all children and drawing the initial summary.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.resetReport_ = function() {
    +  goog.dom.removeChildren(this.reportEl_);
    +  var summary = this.dom_.createDom('div');
    +  summary.className = goog.getCssName('goog-testrunner-progress-summary');
    +  this.reportEl_.appendChild(summary);
    +  this.writeCurrentSummary_();
    +};
    +
    +
    +/**
    + * Draws the stats for the test run.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawStats_ = function() {
    +  this.drawFilesHistogram_();
    +
    +  // Only show time stats if pool size is 1, otherwise times are wrong.
    +  if (this.poolSize_ == 1) {
    +    this.drawRunTimePie_();
    +    this.drawTimeHistogram_();
    +  }
    +
    +  this.drawWorstTestsTable_();
    +};
    +
    +
    +/**
    + * Draws the histogram showing number of files loaded.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawFilesHistogram_ = function() {
    +  this.drawStatsHistogram_(
    +      'numFilesLoaded',
    +      this.numFilesStatsBucketSize_,
    +      goog.functions.identity,
    +      500,
    +      'Histogram showing distribution of\nnumber of files loaded per test');
    +};
    +
    +
    +/**
    + * Draws the histogram showing how long each test took to complete.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawTimeHistogram_ = function() {
    +  this.drawStatsHistogram_(
    +      'totalTime',
    +      this.runTimeStatsBucketSize_,
    +      function(x) { return x / 1000; },
    +      500,
    +      'Histogram showing distribution of\ntime spent running tests in s');
    +};
    +
    +
    +/**
    + * Draws a stats histogram.
    + * @param {string} statsField Field of the stats object to graph.
    + * @param {number} bucketSize The size for the histogram's buckets.
    + * @param {function(number, ...*): *} valueTransformFn Function for
    + *     transforming the x-labels value for display.
    + * @param {number} width The width in pixels of the graph.
    + * @param {string} title The graph's title.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawStatsHistogram_ = function(
    +    statsField, bucketSize, valueTransformFn, width, title) {
    +
    +  var hist = {}, data = [], xlabels = [], ylabels = [];
    +  var max = 0;
    +  for (var i = 0; i < this.stats_.length; i++) {
    +    var num = this.stats_[i][statsField];
    +    var bucket = Math.floor(num / bucketSize) * bucketSize;
    +    if (bucket > max) {
    +      max = bucket;
    +    }
    +    if (!hist[bucket]) {
    +      hist[bucket] = 1;
    +    } else {
    +      hist[bucket]++;
    +    }
    +  }
    +  var maxBucketSize = 0;
    +  for (var i = 0; i <= max; i += bucketSize) {
    +    xlabels.push(valueTransformFn(i));
    +    var count = hist[i] || 0;
    +    if (count > maxBucketSize) {
    +      maxBucketSize = count;
    +    }
    +    data.push(count);
    +  }
    +  var diff = Math.max(1, Math.ceil(maxBucketSize / 10));
    +  for (var i = 0; i <= maxBucketSize; i += diff) {
    +    ylabels.push(i);
    +  }
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR, width, 250, null,
    +      goog.ui.ServerChart.CHART_SERVER_HTTPS_URI);
    +  chart.setTitle(title);
    +  chart.addDataSet(data, 'ff9900');
    +  chart.setLeftLabels(ylabels);
    +  chart.setGridY(ylabels.length - 1);
    +  chart.setXLabels(xlabels);
    +  chart.render(this.statsEl_);
    +};
    +
    +
    +/**
    + * Draws a pie chart showing the percentage of time spent running the tests
    + * compared to loading them etc.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawRunTimePie_ = function() {
    +  var totalTime = 0, runTime = 0;
    +  for (var i = 0; i < this.stats_.length; i++) {
    +    var stat = this.stats_[i];
    +    totalTime += stat.totalTime;
    +    runTime += stat.runTime;
    +  }
    +  var loadTime = totalTime - runTime;
    +  var pie = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.PIE, 500, 250, null,
    +      goog.ui.ServerChart.CHART_SERVER_HTTPS_URI);
    +  pie.setMinValue(0);
    +  pie.setMaxValue(totalTime);
    +  pie.addDataSet([runTime, loadTime], 'ff9900');
    +  pie.setXLabels([
    +    'Test execution (' + runTime + 'ms)',
    +    'Loading (' + loadTime + 'ms)']);
    +  pie.render(this.statsEl_);
    +};
    +
    +
    +/**
    + * Draws a pie chart showing the percentage of time spent running the tests
    + * compared to loading them etc.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawWorstTestsTable_ = function() {
    +  this.stats_.sort(function(a, b) {
    +    return b['numFilesLoaded'] - a['numFilesLoaded'];
    +  });
    +
    +  var tbody = goog.bind(this.dom_.createDom, this.dom_, 'tbody');
    +  var thead = goog.bind(this.dom_.createDom, this.dom_, 'thead');
    +  var tr = goog.bind(this.dom_.createDom, this.dom_, 'tr');
    +  var th = goog.bind(this.dom_.createDom, this.dom_, 'th');
    +  var td = goog.bind(this.dom_.createDom, this.dom_, 'td');
    +  var a = goog.bind(this.dom_.createDom, this.dom_, 'a');
    +
    +  var head = thead({'style': 'cursor: pointer'},
    +      tr(null,
    +          th(null, ' '),
    +          th(null, 'Test file'),
    +          th('center', 'Num files loaded'),
    +          th('center', 'Run time (ms)'),
    +          th('center', 'Total time (ms)')));
    +  var body = tbody();
    +  var table = this.dom_.createDom('table', null, head, body);
    +
    +  for (var i = 0; i < this.stats_.length; i++) {
    +    var stat = this.stats_[i];
    +    body.appendChild(tr(null,
    +        td('center', String(i + 1)),
    +        td(null, a(
    +            {'href': this.basePath_ + stat['testFile'], 'target': '_blank'},
    +            stat['testFile'])),
    +        td('center', String(stat['numFilesLoaded'])),
    +        td('center', String(stat['runTime'])),
    +        td('center', String(stat['totalTime']))));
    +  }
    +
    +  this.statsEl_.appendChild(table);
    +
    +  this.tableSorter_.setDefaultSortFunction(goog.ui.TableSorter.numericSort);
    +  this.tableSorter_.setSortFunction(
    +      1 /* test file name */, goog.ui.TableSorter.alphaSort);
    +  this.tableSorter_.decorate(table);
    +};
    +
    +
    +/**
    + * Clears the stats page.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.clearStats_ = function() {
    +  goog.dom.removeChildren(this.statsEl_);
    +  this.tableSorter_.exitDocument();
    +};
    +
    +
    +/**
    + * Updates the report's summary.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.writeCurrentSummary_ = function() {
    +  var total = this.activeTests_.length;
    +  var executed = this.resultCount_;
    +  var passes = this.passes_;
    +  var duration = Math.round((goog.now() - this.startTime_) / 1000);
    +  var text = executed + ' of ' + total + ' tests executed.<br>' +
    +      passes + ' passed, ' + (executed - passes) + ' failed.<br>' +
    +      'Duration: ' + duration + 's.';
    +  this.reportEl_.firstChild.innerHTML = text;
    +};
    +
    +
    +/**
    + * Adds a segment to the progress bar.
    + * @param {string} title Title for the segment.
    + * @param {*} success Whether the segment should indicate a success.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawProgressSegment_ =
    +    function(title, success) {
    +  var part = this.progressRow_.cells[this.resultCount_ - 1];
    +  part.title = title + ' : ' + (success ? 'SUCCESS' : 'FAILURE');
    +  part.style.backgroundColor = success ? '#090' : '#900';
    +};
    +
    +
    +/**
    + * Draws a test result in the report pane.
    + * @param {string} test Test name.
    + * @param {*} success Whether the test succeeded.
    + * @param {string} report The report.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.drawTestResult_ = function(
    +    test, success, report) {
    +  var text = goog.string.isEmptyOrWhitespace(report) ?
    +      'No report for ' + test + '\n' : report;
    +  var el = this.dom_.createDom('div');
    +  text = goog.string.htmlEscape(text).replace(/\n/g, '<br>');
    +  if (success) {
    +    el.className = goog.getCssName('goog-testrunner-report-success');
    +  } else {
    +    text += '<a href="' + this.basePath_ + test +
    +        '">Run individually &raquo;</a><br>&nbsp;';
    +    el.className = goog.getCssName('goog-testrunner-report-failure');
    +  }
    +  el.innerHTML = text;
    +  this.reportEl_.appendChild(el);
    +};
    +
    +
    +/**
    + * Returns the current timestamp.
    + * @return {string} HH:MM:SS.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.getTimeStamp_ = function() {
    +  var d = new Date;
    +  return goog.string.padNumber(d.getHours(), 2) + ':' +
    +      goog.string.padNumber(d.getMinutes(), 2) + ':' +
    +      goog.string.padNumber(d.getSeconds(), 2);
    +};
    +
    +
    +/**
    + * Trims a filename to be less than 35-characters, ensuring that we do not break
    + * a path part.
    + * @param {string} name The file name.
    + * @return {string} The shortened name.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.trimFileName_ = function(name) {
    +  if (name.length < 35) {
    +    return name;
    +  }
    +  var parts = name.split('/');
    +  var result = '';
    +  while (result.length < 35 && parts.length > 0) {
    +    result = '/' + parts.pop() + result;
    +  }
    +  return '...' + result;
    +};
    +
    +
    +/**
    + * Shows the report and hides the log if the argument is true.
    + * @param {number} tab Which tab to show.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.showTab_ = function(tab) {
    +  var activeTabCssClass = goog.getCssName('goog-testrunner-activetab');
    +
    +  var logTabElement = goog.asserts.assert(this.logTabEl_);
    +  var reportTabElement = goog.asserts.assert(this.reportTabEl_);
    +  var statsTabElement = goog.asserts.assert(this.statsTabEl_);
    +
    +  if (tab == 0) {
    +    this.logEl_.style.display = '';
    +    goog.dom.classlist.add(logTabElement, activeTabCssClass);
    +  } else {
    +    this.logEl_.style.display = 'none';
    +    goog.dom.classlist.remove(logTabElement, activeTabCssClass);
    +  }
    +
    +  if (tab == 1) {
    +    this.reportEl_.style.display = '';
    +    goog.dom.classlist.add(reportTabElement, activeTabCssClass);
    +  } else {
    +    this.reportEl_.style.display = 'none';
    +    goog.dom.classlist.remove(reportTabElement, activeTabCssClass);
    +  }
    +
    +  if (tab == 2) {
    +    this.statsEl_.style.display = '';
    +    goog.dom.classlist.add(statsTabElement, activeTabCssClass);
    +  } else {
    +    this.statsEl_.style.display = 'none';
    +    goog.dom.classlist.remove(statsTabElement, activeTabCssClass);
    +  }
    +};
    +
    +
    +/**
    + * Handles the start button being clicked.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.onStartClicked_ = function(e) {
    +  this.start();
    +};
    +
    +
    +/**
    + * Handles the stop button being clicked.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.onStopClicked_ = function(e) {
    +  this.stopped_ = true;
    +  this.finish_();
    +};
    +
    +
    +/**
    + * Handles the log tab being clicked.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.onLogTabClicked_ = function(e) {
    +  this.showTab_(0);
    +};
    +
    +
    +/**
    + * Handles the log tab being clicked.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.onReportTabClicked_ = function(e) {
    +  this.showTab_(1);
    +};
    +
    +
    +/**
    + * Handles the stats tab being clicked.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.prototype.onStatsTabClicked_ = function(e) {
    +  this.showTab_(2);
    +};
    +
    +
    +
    +/**
    + * Class used to manage the interaction with a single iframe.
    + * @param {string} basePath The base path for tests.
    + * @param {number} timeoutMs The time to wait for the test to load and run.
    + * @param {boolean} verbosePasses Whether to show results for passes.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.testing.MultiTestRunner.TestFrame = function(
    +    basePath, timeoutMs, verbosePasses, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Base path where tests should be resolved from.
    +   * @type {string}
    +   * @private
    +   */
    +  this.basePath_ = basePath;
    +
    +  /**
    +   * The timeout for the test.
    +   * @type {number}
    +   * @private
    +   */
    +  this.timeoutMs_ = timeoutMs;
    +
    +  /**
    +   * Whether to show a summary for passing tests.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.verbosePasses_ = verbosePasses;
    +
    +  /**
    +   * An event handler for handling events.
    +   * @type {goog.events.EventHandler<!goog.testing.MultiTestRunner.TestFrame>}
    +   * @private
    +   */
    +  this.eh_ = new goog.events.EventHandler(this);
    +
    +};
    +goog.inherits(goog.testing.MultiTestRunner.TestFrame, goog.ui.Component);
    +
    +
    +/**
    + * Reference to the iframe.
    + * @type {HTMLIFrameElement}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.iframeEl_ = null;
    +
    +
    +/**
    + * Whether the iframe for the current test has loaded.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.iframeLoaded_ = false;
    +
    +
    +/**
    + * The test file being run.
    + * @type {string}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.testFile_ = '';
    +
    +
    +/**
    + * The report returned from the test.
    + * @type {string}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.report_ = '';
    +
    +
    +/**
    + * The total time loading and running the test in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.totalTime_ = 0;
    +
    +
    +/**
    + * The actual runtime of the test in milliseconds.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.runTime_ = 0;
    +
    +
    +/**
    + * The number of files loaded by the test.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.numFilesLoaded_ = 0;
    +
    +
    +/**
    + * Whether the test was successful, null if no result has been returned yet.
    + * @type {?boolean}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.isSuccess_ = null;
    +
    +
    +/**
    + * Timestamp for the when the test was started.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.startTime_ = 0;
    +
    +
    +/**
    + * Timestamp for the last state, used to determine timeouts.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.lastStateTime_ = 0;
    +
    +
    +/**
    + * The state of the active test.
    + * @type {number}
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.currentState_ = 0;
    +
    +
    +/** @override */
    +goog.testing.MultiTestRunner.TestFrame.prototype.disposeInternal = function() {
    +  goog.testing.MultiTestRunner.TestFrame.superClass_.disposeInternal.call(this);
    +  this.dom_.removeNode(this.iframeEl_);
    +  this.eh_.dispose();
    +  this.iframeEl_ = null;
    +};
    +
    +
    +/**
    + * Runs a test file in this test frame.
    + * @param {string} testFile The test to run.
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.runTest = function(testFile) {
    +  this.lastStateTime_ = this.startTime_ = goog.now();
    +
    +  if (!this.iframeEl_) {
    +    this.createIframe_();
    +  }
    +
    +  this.iframeLoaded_ = false;
    +  this.currentState_ = 0;
    +  this.isSuccess_ = null;
    +  this.report_ = '';
    +  this.testFile_ = testFile;
    +
    +  try {
    +    this.iframeEl_.src = this.basePath_ + testFile;
    +  } catch (e) {
    +    // Failures will trigger a JS exception on the local file system.
    +    this.report_ = this.testFile_ + ' failed to load : ' + e.message;
    +    this.isSuccess_ = false;
    +    this.finish_();
    +    return;
    +  }
    +
    +  this.checkForCompletion_();
    +};
    +
    +
    +/**
    + * @return {string} The test file the TestFrame is running.
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.getTestFile = function() {
    +  return this.testFile_;
    +};
    +
    +
    +/**
    + * @return {!Object} Stats about the test run.
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.getStats = function() {
    +  return {
    +    'testFile': this.testFile_,
    +    'success': this.isSuccess_,
    +    'runTime': this.runTime_,
    +    'totalTime': this.totalTime_,
    +    'numFilesLoaded': this.numFilesLoaded_
    +  };
    +};
    +
    +
    +/**
    + * @return {string} The report for the test run.
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.getReport = function() {
    +  return this.report_;
    +};
    +
    +
    +/**
    + * @return {?boolean} Whether the test frame had a success.
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.isSuccess = function() {
    +  return this.isSuccess_;
    +};
    +
    +
    +/**
    + * Handles the TestFrame finishing a single test.
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.finish_ = function() {
    +  this.totalTime_ = goog.now() - this.startTime_;
    +  // TODO(user): Fire an event instead?
    +  if (this.getParent() && this.getParent().processResult) {
    +    this.getParent().processResult(this);
    +  }
    +};
    +
    +
    +/**
    + * Creates an iframe to run the tests in.  For overriding in unit tests.
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.createIframe_ = function() {
    +  this.iframeEl_ =
    +      /** @type {!HTMLIFrameElement} */ (this.dom_.createDom('iframe'));
    +  this.getElement().appendChild(this.iframeEl_);
    +  this.eh_.listen(this.iframeEl_, 'load', this.onIframeLoaded_);
    +};
    +
    +
    +/**
    + * Handles the iframe loading.
    + * @param {goog.events.BrowserEvent} e The load event.
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.onIframeLoaded_ = function(e) {
    +  this.iframeLoaded_ = true;
    +};
    +
    +
    +/**
    + * Checks the active test for completion, keeping track of the tests' various
    + * execution stages.
    + * @private
    + */
    +goog.testing.MultiTestRunner.TestFrame.prototype.checkForCompletion_ =
    +    function() {
    +  var js = goog.dom.getFrameContentWindow(this.iframeEl_);
    +  switch (this.currentState_) {
    +    case 0:
    +      if (this.iframeLoaded_ && js['G_testRunner']) {
    +        this.lastStateTime_ = goog.now();
    +        this.currentState_++;
    +      }
    +      break;
    +    case 1:
    +      if (js['G_testRunner']['isInitialized']()) {
    +        this.lastStateTime_ = goog.now();
    +        this.currentState_++;
    +      }
    +      break;
    +    case 2:
    +      if (js['G_testRunner']['isFinished']()) {
    +        var tr = js['G_testRunner'];
    +        this.isSuccess_ = tr['isSuccess']();
    +        this.report_ = tr['getReport'](this.verbosePasses_);
    +        this.runTime_ = tr['getRunTime']();
    +        this.numFilesLoaded_ = tr['getNumFilesLoaded']();
    +        this.finish_();
    +        return;
    +      }
    +  }
    +
    +  // Check to see if the test has timed out.
    +  if (goog.now() - this.lastStateTime_ > this.timeoutMs_) {
    +    this.report_ = this.testFile_ + ' timed out  ' +
    +        goog.testing.MultiTestRunner.STATES[this.currentState_];
    +    this.isSuccess_ = false;
    +    this.finish_();
    +    return;
    +  }
    +
    +  // Check again in 100ms.
    +  goog.Timer.callOnce(this.checkForCompletion_, 100, this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/net/xhrio.js b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio.js
    new file mode 100644
    index 0000000000..7604a527f9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio.js
    @@ -0,0 +1,751 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Mock of XhrIo for unit testing.
    + */
    +
    +goog.provide('goog.testing.net.XhrIo');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom.xml');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.json');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.HttpStatus');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.structs.Map');
    +
    +
    +
    +/**
    + * Mock implementation of goog.net.XhrIo. This doesn't provide a mock
    + * implementation for all cases, but it's not too hard to add them as needed.
    + * @param {goog.testing.TestQueue=} opt_testQueue Test queue for inserting test
    + *     events.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.testing.net.XhrIo = function(opt_testQueue) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Map of default headers to add to every request, use:
    +   * XhrIo.headers.set(name, value)
    +   * @type {goog.structs.Map}
    +   */
    +  this.headers = new goog.structs.Map();
    +
    +  /**
    +   * Queue of events write to.
    +   * @type {goog.testing.TestQueue?}
    +   * @private
    +   */
    +  this.testQueue_ = opt_testQueue || null;
    +};
    +goog.inherits(goog.testing.net.XhrIo, goog.events.EventTarget);
    +
    +
    +/**
    + * Alias this enum here to make mocking of goog.net.XhrIo easier.
    + * @enum {string}
    + */
    +goog.testing.net.XhrIo.ResponseType = goog.net.XhrIo.ResponseType;
    +
    +
    +/**
    + * All non-disposed instances of goog.testing.net.XhrIo created
    + * by {@link goog.testing.net.XhrIo.send} are in this Array.
    + * @see goog.testing.net.XhrIo.cleanup
    + * @type {!Array<!goog.testing.net.XhrIo>}
    + * @private
    + */
    +goog.testing.net.XhrIo.sendInstances_ = [];
    +
    +
    +/**
    + * Returns an Array containing all non-disposed instances of
    + * goog.testing.net.XhrIo created by {@link goog.testing.net.XhrIo.send}.
    + * @return {!Array<!goog.testing.net.XhrIo>} Array of goog.testing.net.XhrIo
    + *     instances.
    + */
    +goog.testing.net.XhrIo.getSendInstances = function() {
    +  return goog.testing.net.XhrIo.sendInstances_;
    +};
    +
    +
    +/**
    + * Disposes all non-disposed instances of goog.testing.net.XhrIo created by
    + * {@link goog.testing.net.XhrIo.send}.
    + * @see goog.net.XhrIo.cleanup
    + */
    +goog.testing.net.XhrIo.cleanup = function() {
    +  var instances = goog.testing.net.XhrIo.sendInstances_;
    +  while (instances.length) {
    +    instances.pop().dispose();
    +  }
    +};
    +
    +
    +/**
    + * Simulates the static XhrIo send method.
    + * @param {string} url Uri to make request to.
    + * @param {Function=} opt_callback Callback function for when request is
    + *     complete.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {string=} opt_content Post data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + * @param {number=} opt_timeoutInterval Number of milliseconds after which an
    + *     incomplete request will be aborted; 0 means no timeout is set.
    + * @param {boolean=} opt_withCredentials Whether to send credentials with the
    + *     request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
    + * @return {!goog.testing.net.XhrIo} The mocked sent XhrIo.
    + */
    +goog.testing.net.XhrIo.send = function(url, opt_callback, opt_method,
    +                                       opt_content, opt_headers,
    +                                       opt_timeoutInterval,
    +                                       opt_withCredentials) {
    +  var x = new goog.testing.net.XhrIo();
    +  goog.testing.net.XhrIo.sendInstances_.push(x);
    +  if (opt_callback) {
    +    goog.events.listen(x, goog.net.EventType.COMPLETE, opt_callback);
    +  }
    +  goog.events.listen(x,
    +                     goog.net.EventType.READY,
    +                     goog.partial(goog.testing.net.XhrIo.cleanupSend_, x));
    +  if (opt_timeoutInterval) {
    +    x.setTimeoutInterval(opt_timeoutInterval);
    +  }
    +  x.setWithCredentials(Boolean(opt_withCredentials));
    +  x.send(url, opt_method, opt_content, opt_headers);
    +
    +  return x;
    +};
    +
    +
    +/**
    + * Disposes of the specified goog.testing.net.XhrIo created by
    + * {@link goog.testing.net.XhrIo.send} and removes it from
    + * {@link goog.testing.net.XhrIo.pendingStaticSendInstances_}.
    + * @param {!goog.testing.net.XhrIo} XhrIo An XhrIo created by
    + *     {@link goog.testing.net.XhrIo.send}.
    + * @private
    + */
    +goog.testing.net.XhrIo.cleanupSend_ = function(XhrIo) {
    +  XhrIo.dispose();
    +  goog.array.remove(goog.testing.net.XhrIo.sendInstances_, XhrIo);
    +};
    +
    +
    +/**
    + * Stores the simulated response headers for the requests which are sent through
    + * this XhrIo.
    + * @type {Object}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.responseHeaders_;
    +
    +
    +/**
    + * Whether MockXhrIo is active.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.active_ = false;
    +
    +
    +/**
    + * Last URI that was requested.
    + * @type {string}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastUri_ = '';
    +
    +
    +/**
    + * Last HTTP method that was requested.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastMethod_;
    +
    +
    +/**
    + * Last POST content that was requested.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastContent_;
    +
    +
    +/**
    + * Additional headers that were requested in the last query.
    + * @type {Object|goog.structs.Map|undefined}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastHeaders_;
    +
    +
    +/**
    + * Last error code.
    + * @type {goog.net.ErrorCode}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastErrorCode_ =
    +    goog.net.ErrorCode.NO_ERROR;
    +
    +
    +/**
    + * Last error message.
    + * @type {string}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.lastError_ = '';
    +
    +
    +/**
    + * The response object.
    + * @type {string|Document|ArrayBuffer}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.response_ = '';
    +
    +
    +/**
    + * Mock ready state.
    + * @type {number}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.readyState_ =
    +    goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +
    +
    +/**
    + * Number of milliseconds after which an incomplete request will be aborted and
    + * a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set.
    + * @type {number}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.timeoutInterval_ = 0;
    +
    +
    +/**
    + * Window timeout ID used to cancel the timeout event handler if the request
    + * completes successfully.
    + * @type {Object}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.timeoutId_ = null;
    +
    +
    +/**
    + * The requested type for the response. The empty string means use the default
    + * XHR behavior.
    + * @type {goog.net.XhrIo.ResponseType}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.responseType_ =
    +    goog.net.XhrIo.ResponseType.DEFAULT;
    +
    +
    +/**
    + * Whether a "credentialed" request is to be sent (one that is aware of cookies
    + * and authentication) . This is applicable only for cross-domain requests and
    + * more recent browsers that support this part of the HTTP Access Control
    + * standard.
    + *
    + * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.withCredentials_ = false;
    +
    +
    +/**
    + * Whether there's currently an underlying XHR object.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.net.XhrIo.prototype.xhr_ = false;
    +
    +
    +/**
    + * Returns the number of milliseconds after which an incomplete request will be
    + * aborted, or 0 if no timeout is set.
    + * @return {number} Timeout interval in milliseconds.
    + */
    +goog.testing.net.XhrIo.prototype.getTimeoutInterval = function() {
    +  return this.timeoutInterval_;
    +};
    +
    +
    +/**
    + * Sets the number of milliseconds after which an incomplete request will be
    + * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
    + * timeout is set.
    + * @param {number} ms Timeout interval in milliseconds; 0 means none.
    + */
    +goog.testing.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
    +  this.timeoutInterval_ = Math.max(0, ms);
    +};
    +
    +
    +/**
    + * Causes timeout events to be fired.
    + */
    +goog.testing.net.XhrIo.prototype.simulateTimeout = function() {
    +  this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
    +  this.dispatchEvent(goog.net.EventType.TIMEOUT);
    +  this.abort(goog.net.ErrorCode.TIMEOUT);
    +};
    +
    +
    +/**
    + * Sets the desired type for the response. At time of writing, this is only
    + * supported in very recent versions of WebKit (10.0.612.1 dev and later).
    + *
    + * If this is used, the response may only be accessed via {@link #getResponse}.
    + *
    + * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
    + */
    +goog.testing.net.XhrIo.prototype.setResponseType = function(type) {
    +  this.responseType_ = type;
    +};
    +
    +
    +/**
    + * Gets the desired type for the response.
    + * @return {goog.net.XhrIo.ResponseType} The desired type for the response.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseType = function() {
    +  return this.responseType_;
    +};
    +
    +
    +/**
    + * Sets whether a "credentialed" request that is aware of cookie and
    + * authentication information should be made. This option is only supported by
    + * browsers that support HTTP Access Control. As of this writing, this option
    + * is not supported in IE.
    + *
    + * @param {boolean} withCredentials Whether this should be a "credentialed"
    + *     request.
    + */
    +goog.testing.net.XhrIo.prototype.setWithCredentials =
    +    function(withCredentials) {
    +  this.withCredentials_ = withCredentials;
    +};
    +
    +
    +/**
    + * Gets whether a "credentialed" request is to be sent.
    + * @return {boolean} The desired type for the response.
    + */
    +goog.testing.net.XhrIo.prototype.getWithCredentials = function() {
    +  return this.withCredentials_;
    +};
    +
    +
    +/**
    + * Abort the current XMLHttpRequest
    + * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
    + *     defaults to ABORT.
    + */
    +goog.testing.net.XhrIo.prototype.abort = function(opt_failureCode) {
    +  if (this.active_) {
    +    try {
    +      this.active_ = false;
    +      this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
    +      this.dispatchEvent(goog.net.EventType.COMPLETE);
    +      this.dispatchEvent(goog.net.EventType.ABORT);
    +    } finally {
    +      this.simulateReady();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Simulates the XhrIo send.
    + * @param {string} url Uri to make request too.
    + * @param {string=} opt_method Send method, default: GET.
    + * @param {string=} opt_content Post data.
    + * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
    + *     request.
    + */
    +goog.testing.net.XhrIo.prototype.send = function(url, opt_method, opt_content,
    +                                                 opt_headers) {
    +  if (this.xhr_) {
    +    throw Error('[goog.net.XhrIo] Object is active with another request');
    +  }
    +
    +  this.lastUri_ = url;
    +  this.lastMethod_ = opt_method || 'GET';
    +  this.lastContent_ = opt_content;
    +  this.lastHeaders_ = opt_headers;
    +
    +  if (this.testQueue_) {
    +    this.testQueue_.enqueue(['s', url, opt_method, opt_content, opt_headers]);
    +  }
    +  this.xhr_ = true;
    +  this.active_ = true;
    +  this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +  this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.LOADING);
    +};
    +
    +
    +/**
    + * Creates a new XHR object.
    + * @return {goog.net.XhrLike.OrNative} The newly created XHR
    + *     object.
    + * @protected
    + */
    +goog.testing.net.XhrIo.prototype.createXhr = function() {
    +  return goog.net.XmlHttp();
    +};
    +
    +
    +/**
    + * Simulates changing to the new ready state.
    + * @param {number} readyState Ready state to change to.
    + */
    +goog.testing.net.XhrIo.prototype.simulateReadyStateChange =
    +    function(readyState) {
    +  if (readyState < this.readyState_) {
    +    throw Error('Readystate cannot go backwards');
    +  }
    +
    +  // INTERACTIVE can be dispatched repeatedly as more data is reported.
    +  if (readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
    +      readyState == this.readyState_) {
    +    this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
    +    return;
    +  }
    +
    +  while (this.readyState_ < readyState) {
    +    this.readyState_++;
    +    this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
    +
    +    if (this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE) {
    +      this.active_ = false;
    +      this.dispatchEvent(goog.net.EventType.COMPLETE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Simulate receiving some bytes but the request not fully completing, and
    + * the XHR entering the 'INTERACTIVE' state.
    + * @param {string} partialResponse A string to append to the response text.
    + * @param {Object=} opt_headers Simulated response headers.
    + */
    +goog.testing.net.XhrIo.prototype.simulatePartialResponse =
    +    function(partialResponse, opt_headers) {
    +  this.response_ += partialResponse;
    +  this.responseHeaders_ = opt_headers || {};
    +  this.statusCode_ = 200;
    +  this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.INTERACTIVE);
    +};
    +
    +
    +/**
    + * Simulates receiving a response.
    + * @param {number} statusCode Simulated status code.
    + * @param {string|Document|ArrayBuffer|null} response Simulated response.
    + * @param {Object=} opt_headers Simulated response headers.
    + */
    +goog.testing.net.XhrIo.prototype.simulateResponse = function(statusCode,
    +    response, opt_headers) {
    +  this.statusCode_ = statusCode;
    +  this.response_ = response || '';
    +  this.responseHeaders_ = opt_headers || {};
    +
    +  try {
    +    if (this.isSuccess()) {
    +      this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
    +      this.dispatchEvent(goog.net.EventType.SUCCESS);
    +    } else {
    +      this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
    +      this.lastError_ = this.getStatusText() + ' [' + this.getStatus() + ']';
    +      this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
    +      this.dispatchEvent(goog.net.EventType.ERROR);
    +    }
    +  } finally {
    +    this.simulateReady();
    +  }
    +};
    +
    +
    +/**
    + * Simulates the Xhr is ready for the next request.
    + */
    +goog.testing.net.XhrIo.prototype.simulateReady = function() {
    +  this.active_ = false;
    +  this.xhr_ = false;
    +  this.dispatchEvent(goog.net.EventType.READY);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether there is an active request.
    + */
    +goog.testing.net.XhrIo.prototype.isActive = function() {
    +  return !!this.xhr_;
    +};
    +
    +
    +/**
    + * Has the request completed.
    + * @return {boolean} Whether the request has completed.
    + */
    +goog.testing.net.XhrIo.prototype.isComplete = function() {
    +  return this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE;
    +};
    +
    +
    +/**
    + * Has the request compeleted with a success.
    + * @return {boolean} Whether the request compeleted successfully.
    + */
    +goog.testing.net.XhrIo.prototype.isSuccess = function() {
    +  switch (this.getStatus()) {
    +    case goog.net.HttpStatus.OK:
    +    case goog.net.HttpStatus.NO_CONTENT:
    +    case goog.net.HttpStatus.NOT_MODIFIED:
    +      return true;
    +
    +    default:
    +      return false;
    +  }
    +};
    +
    +
    +/**
    + * Returns the readystate.
    + * @return {number} goog.net.XmlHttp.ReadyState.*.
    + */
    +goog.testing.net.XhrIo.prototype.getReadyState = function() {
    +  return this.readyState_;
    +};
    +
    +
    +/**
    + * Get the status from the Xhr object.  Will only return correct result when
    + * called from the context of a callback.
    + * @return {number} Http status.
    + */
    +goog.testing.net.XhrIo.prototype.getStatus = function() {
    +  return this.statusCode_;
    +};
    +
    +
    +/**
    + * Get the status text from the Xhr object.  Will only return correct result
    + * when called from the context of a callback.
    + * @return {string} Status text.
    + */
    +goog.testing.net.XhrIo.prototype.getStatusText = function() {
    +  return '';
    +};
    +
    +
    +/**
    + * Gets the last error message.
    + * @return {goog.net.ErrorCode} Last error code.
    + */
    +goog.testing.net.XhrIo.prototype.getLastErrorCode = function() {
    +  return this.lastErrorCode_;
    +};
    +
    +
    +/**
    + * Gets the last error message.
    + * @return {string} Last error message.
    + */
    +goog.testing.net.XhrIo.prototype.getLastError = function() {
    +  return this.lastError_;
    +};
    +
    +
    +/**
    + * Gets the last URI that was requested.
    + * @return {string} Last URI.
    + */
    +goog.testing.net.XhrIo.prototype.getLastUri = function() {
    +  return this.lastUri_;
    +};
    +
    +
    +/**
    + * Gets the last HTTP method that was requested.
    + * @return {string|undefined} Last HTTP method used by send.
    + */
    +goog.testing.net.XhrIo.prototype.getLastMethod = function() {
    +  return this.lastMethod_;
    +};
    +
    +
    +/**
    + * Gets the last POST content that was requested.
    + * @return {string|undefined} Last POST content or undefined if last request was
    + *      a GET.
    + */
    +goog.testing.net.XhrIo.prototype.getLastContent = function() {
    +  return this.lastContent_;
    +};
    +
    +
    +/**
    + * Gets the headers of the last request.
    + * @return {Object|goog.structs.Map|undefined} Last headers manually set in send
    + *      call or undefined if no additional headers were specified.
    + */
    +goog.testing.net.XhrIo.prototype.getLastRequestHeaders = function() {
    +  return this.lastHeaders_;
    +};
    +
    +
    +/**
    + * Gets the response text from the Xhr object.  Will only return correct result
    + * when called from the context of a callback.
    + * @return {string} Result from the server.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseText = function() {
    +  if (goog.isString(this.response_)) {
    +    return this.response_;
    +  } else if (goog.global['ArrayBuffer'] &&
    +      this.response_ instanceof ArrayBuffer) {
    +    return '';
    +  } else {
    +    return goog.dom.xml.serialize(/** @type {Document} */ (this.response_));
    +  }
    +};
    +
    +
    +/**
    + * Gets the response body from the Xhr object. Will only return correct result
    + * when called from the context of a callback.
    + * @return {Object} Binary result from the server or null.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseBody = function() {
    +  return null;
    +};
    +
    +
    +/**
    + * Gets the response and evaluates it as JSON from the Xhr object.  Will only
    + * return correct result when called from the context of a callback.
    + * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
    + *     stripping of the response before parsing. This needs to be set only if
    + *     your backend server prepends the same prefix string to the JSON response.
    + * @return {Object} JavaScript object.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
    +  var responseText = this.getResponseText();
    +  if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
    +    responseText = responseText.substring(opt_xssiPrefix.length);
    +  }
    +
    +  return goog.json.parse(responseText);
    +};
    +
    +
    +/**
    + * Gets the response XML from the Xhr object.  Will only return correct result
    + * when called from the context of a callback.
    + * @return {Document} Result from the server if it was XML.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseXml = function() {
    +  // NOTE(user): I haven't found out how to check in Internet Explorer
    +  // whether the response is XML document, so I do it the other way around.
    +  return goog.isString(this.response_) ||
    +      (goog.global['ArrayBuffer'] && this.response_ instanceof ArrayBuffer) ?
    +      null : /** @type {Document} */ (this.response_);
    +};
    +
    +
    +/**
    + * Get the response as the type specificed by {@link #setResponseType}. At time
    + * of writing, this is only supported in very recent versions of WebKit
    + * (10.0.612.1 dev and later).
    + *
    + * @return {*} The response.
    + */
    +goog.testing.net.XhrIo.prototype.getResponse = function() {
    +  return this.response_;
    +};
    +
    +
    +/**
    + * Get the value of the response-header with the given name from the Xhr object
    + * Will only return correct result when called from the context of a callback
    + * and the request has completed
    + * @param {string} key The name of the response-header to retrieve.
    + * @return {string|undefined} The value of the response-header named key.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseHeader = function(key) {
    +  return this.isComplete() ? this.responseHeaders_[key] : undefined;
    +};
    +
    +
    +/**
    + * Gets the text of all the headers in the response.
    + * Will only return correct result when called from the context of a callback
    + * and the request has completed
    + * @return {string} The string containing all the response headers.
    + */
    +goog.testing.net.XhrIo.prototype.getAllResponseHeaders = function() {
    +  if (!this.isComplete()) {
    +    return '';
    +  }
    +
    +  var headers = [];
    +  goog.object.forEach(this.responseHeaders_, function(value, name) {
    +    headers.push(name + ': ' + value);
    +  });
    +
    +  return headers.join('\r\n');
    +};
    +
    +
    +/**
    + * Returns all response headers as a key-value map.
    + * Multiple values for the same header key can be combined into one,
    + * separated by a comma and a space.
    + * Note that the native getResponseHeader method for retrieving a single header
    + * does a case insensitive match on the header name. This method does not
    + * include any case normalization logic, it will just return a key-value
    + * representation of the headers.
    + * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
    + * @return {!Object<string, string>} An object with the header keys as keys
    + *     and header values as values.
    + */
    +goog.testing.net.XhrIo.prototype.getResponseHeaders = function() {
    +  var headersObject = {};
    +  goog.object.forEach(this.responseHeaders_, function(value, key) {
    +    if (headersObject[key]) {
    +      headersObject[key] += ', ' + value;
    +    } else {
    +      headersObject[key] = value;
    +    }
    +  });
    +  return headersObject;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.html b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.html
    new file mode 100644
    index 0000000000..4dfe7fc0de
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.net.XhrIo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.net.XhrIoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.js b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.js
    new file mode 100644
    index 0000000000..c72005c710
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/net/xhrio_test.js
    @@ -0,0 +1,423 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.net.XhrIoTest');
    +goog.setTestOnly('goog.testing.net.XhrIoTest');
    +
    +goog.require('goog.dom.xml');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.net.ErrorCode');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XmlHttp');
    +goog.require('goog.object');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers.InstanceOf');
    +goog.require('goog.testing.net.XhrIo');
    +
    +var mockControl;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function testStaticSend() {
    +  sendInstances = goog.testing.net.XhrIo.getSendInstances();
    +  var returnedXhr = goog.testing.net.XhrIo.send('url');
    +  assertEquals('sendInstances_ after send',
    +               1, sendInstances.length);
    +  xhr = sendInstances[sendInstances.length - 1];
    +  assertTrue('isActive after request', xhr.isActive());
    +  assertEquals(returnedXhr, xhr);
    +  assertEquals('readyState after request',
    +               goog.net.XmlHttp.ReadyState.LOADING,
    +               xhr.getReadyState());
    +
    +  xhr.simulateResponse(200, '');
    +  assertFalse('isActive after response', xhr.isActive());
    +  assertEquals('readyState after response',
    +               goog.net.XmlHttp.ReadyState.COMPLETE,
    +               xhr.getReadyState());
    +
    +  xhr.simulateReady();
    +  assertEquals('sendInstances_ after READY',
    +               0, sendInstances.length);
    +}
    +
    +function testStaticSendWithException() {
    +  goog.testing.net.XhrIo.send('url', function() {
    +    if (!this.isSuccess()) {
    +      throw Error('The xhr did not complete successfully!');
    +    }
    +  });
    +  var sendInstances = goog.testing.net.XhrIo.getSendInstances();
    +  var xhr = sendInstances[sendInstances.length - 1];
    +  try {
    +    xhr.simulateResponse(400, '');
    +  } catch (e) {
    +    // Do nothing with the exception; we just want to make sure
    +    // the class cleans itself up properly when an exception is
    +    // thrown.
    +  }
    +  assertEquals('Send instance array not cleaned up properly!',
    +               0, sendInstances.length);
    +}
    +
    +function testMultipleSend() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  assertFalse('isActive before first request', xhr.isActive());
    +  assertEquals('readyState before first request',
    +               goog.net.XmlHttp.ReadyState.UNINITIALIZED,
    +               xhr.getReadyState());
    +
    +  xhr.send('url');
    +  assertTrue('isActive after first request', xhr.isActive());
    +  assertEquals('readyState after first request',
    +               goog.net.XmlHttp.ReadyState.LOADING,
    +               xhr.getReadyState());
    +
    +  xhr.simulateResponse(200, '');
    +  assertFalse('isActive after first response', xhr.isActive());
    +  assertEquals('readyState after first response',
    +               goog.net.XmlHttp.ReadyState.COMPLETE,
    +               xhr.getReadyState());
    +
    +  xhr.send('url');
    +  assertTrue('isActive after second request', xhr.isActive());
    +  assertEquals('readyState after second request',
    +               goog.net.XmlHttp.ReadyState.LOADING,
    +               xhr.getReadyState());
    +}
    +
    +function testGetLastUri() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  assertEquals('nothing sent yet, empty URI', '', xhr.getLastUri());
    +
    +  var requestUrl = 'http://www.example.com/';
    +  xhr.send(requestUrl);
    +  assertEquals('message sent, URI saved', requestUrl, xhr.getLastUri());
    +}
    +
    +function testGetLastMethod() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  assertUndefined('nothing sent yet, empty method', xhr.getLastMethod());
    +
    +  var method = 'POKE';
    +  xhr.send('http://www.example.com/', method);
    +  assertEquals('message sent, method saved', method, xhr.getLastMethod());
    +  xhr.simulateResponse(200, '');
    +
    +  xhr.send('http://www.example.com/');
    +  assertEquals('message sent, method saved', 'GET', xhr.getLastMethod());
    +}
    +
    +function testGetLastContent() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  assertUndefined('nothing sent yet, empty content', xhr.getLastContent());
    +
    +  var postContent = 'var=value&var2=value2';
    +  xhr.send('http://www.example.com/', undefined, postContent);
    +  assertEquals('POST message sent, content saved',
    +               postContent, xhr.getLastContent());
    +  xhr.simulateResponse(200, '');
    +
    +  xhr.send('http://www.example.com/');
    +  assertUndefined('GET message sent, content cleaned', xhr.getLastContent());
    +}
    +
    +function testGetLastRequestHeaders() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  assertUndefined('nothing sent yet, empty headers',
    +                  xhr.getLastRequestHeaders());
    +
    +  xhr.send('http://www.example.com/', undefined, undefined,
    +      {'From': 'page@google.com'});
    +  assertObjectEquals('Request sent with extra headers, headers saved',
    +      {'From': 'page@google.com'},
    +      xhr.getLastRequestHeaders());
    +  xhr.simulateResponse(200, '');
    +
    +  xhr.send('http://www.example.com');
    +  assertUndefined('New request sent without extra headers',
    +      xhr.getLastRequestHeaders());
    +}
    +
    +function testGetResponseText() {
    +  // Text response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertEquals('text', e.target.getResponseText());
    +  });
    +  xhr.simulateResponse(200, 'text');
    +  assertTrue(called);
    +
    +  // XML response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  var xml = goog.dom.xml.createDocument();
    +  xml.appendChild(xml.createElement('root'));
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    var text = e.target.getResponseText();
    +    assertTrue(/<root ?\/>/.test(text));
    +  });
    +  xhr.simulateResponse(200, xml);
    +  assertTrue(called);
    +}
    +
    +function testGetResponseJson() {
    +  // Valid JSON response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertArrayEquals([0, 1], e.target.getResponseJson());
    +  });
    +  xhr.simulateResponse(200, '[0, 1]');
    +  assertTrue(called);
    +
    +  // Valid JSON response with XSSI prefix encoded came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertArrayEquals([0, 1], e.target.getResponseJson(')]}\', \n'));
    +  });
    +  xhr.simulateResponse(200, ')]}\', \n[0, 1]');
    +  assertTrue(called);
    +
    +  // Invalid JSON response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertThrows(e.target.getResponseJson);
    +  });
    +  xhr.simulateResponse(200, '[0, 1');
    +  assertTrue(called);
    +
    +  // XML response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  var xml = goog.dom.xml.createDocument();
    +  xml.appendChild(xml.createElement('root'));
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertThrows(e.target.getResponseJson);
    +  });
    +  xhr.simulateResponse(200, xml);
    +  assertTrue(called);
    +}
    +
    +function testGetResponseXml() {
    +  // Text response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertNull(e.target.getResponseXml());
    +  });
    +  xhr.simulateResponse(200, 'text');
    +  assertTrue(called);
    +
    +  // XML response came.
    +  var called = false;
    +  var xhr = new goog.testing.net.XhrIo();
    +  var xml = goog.dom.xml.createDocument();
    +  xml.appendChild(xml.createElement('root'));
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, function(e) {
    +    called = true;
    +    assertEquals(xml, e.target.getResponseXml());
    +  });
    +  xhr.simulateResponse(200, xml);
    +  assertTrue(called);
    +}
    +
    +function testGetResponseHeaders_noHeadersPresent() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +      .$does(function(e) {
    +        assertTrue(e.type == goog.net.EventType.SUCCESS);
    +        assertUndefined(e.target.getResponseHeader('XHR'));
    +      });
    +  mockControl.$replayAll();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +  xhr.simulateResponse(200, '');
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testGetResponseHeaders_headersPresent() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +      .$does(function(e) {
    +        assertTrue(e.type == goog.net.EventType.SUCCESS);
    +        assertUndefined(e.target.getResponseHeader('XHR'));
    +        assertEquals(e.target.getResponseHeader('Pragma'), 'no-cache');
    +      });
    +  mockControl.$replayAll();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +  xhr.simulateResponse(200, '', {'Pragma': 'no-cache'});
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testAbort_WhenNoPendingSentRequests() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var eventListener = mockControl.createFunctionMock();
    +  mockControl.$replayAll();
    +
    +  goog.events.listen(xhr, goog.net.EventType.COMPLETE, eventListener);
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, eventListener);
    +  goog.events.listen(xhr, goog.net.EventType.ABORT, eventListener);
    +  goog.events.listen(xhr, goog.net.EventType.ERROR, eventListener);
    +  goog.events.listen(xhr, goog.net.EventType.READY, eventListener);
    +
    +  xhr.abort();
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testAbort_PendingSentRequest() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertTrue(e.type == goog.net.EventType.COMPLETE);
    +        assertObjectEquals(e.target, xhr);
    +        assertEquals(e.target.getLastErrorCode(), goog.net.ErrorCode.ABORT);
    +        assertTrue(e.target.isActive());
    +      });
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertTrue(e.type == goog.net.EventType.ABORT);
    +        assertObjectEquals(e.target, xhr);
    +        assertTrue(e.target.isActive());
    +      });
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertTrue(e.type == goog.net.EventType.READY);
    +        assertObjectEquals(e.target, xhr);
    +        assertFalse(e.target.isActive());
    +      });
    +  mockControl.$replayAll();
    +
    +  goog.events.listen(xhr, goog.net.EventType.COMPLETE, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.ABORT, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.ERROR, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.READY, mockListener);
    +  xhr.send('dummyurl');
    +  xhr.abort();
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testEvents_Success() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +
    +  var readyState = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
    +  function readyStateListener(e) {
    +    assertEquals(e.type, goog.net.EventType.READY_STATE_CHANGE);
    +    assertObjectEquals(e.target, xhr);
    +    readyState++;
    +    assertEquals(e.target.getReadyState(), readyState);
    +    assertTrue(e.target.isActive());
    +  }
    +
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertEquals(e.type, goog.net.EventType.COMPLETE);
    +        assertObjectEquals(e.target, xhr);
    +        assertEquals(e.target.getLastErrorCode(), goog.net.ErrorCode.NO_ERROR);
    +        assertTrue(e.target.isActive());
    +      });
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertEquals(e.type, goog.net.EventType.SUCCESS);
    +        assertObjectEquals(e.target, xhr);
    +        assertTrue(e.target.isActive());
    +      });
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +  .$does(function(e) {
    +        assertEquals(e.type, goog.net.EventType.READY);
    +        assertObjectEquals(e.target, xhr);
    +        assertFalse(e.target.isActive());
    +      });
    +  mockControl.$replayAll();
    +
    +  goog.events.listen(xhr, goog.net.EventType.READY_STATE_CHANGE,
    +      readyStateListener);
    +  goog.events.listen(xhr, goog.net.EventType.COMPLETE, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.ABORT, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.ERROR, mockListener);
    +  goog.events.listen(xhr, goog.net.EventType.READY, mockListener);
    +  xhr.send('dummyurl');
    +  xhr.simulateResponse(200, null);
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testGetResponseHeaders() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +      .$does(function(e) {
    +        var headers = e.target.getResponseHeaders();
    +        assertEquals(2, goog.object.getCount(headers));
    +        assertEquals('foo', headers['test1']);
    +        assertEquals('bar', headers['test2']);
    +      });
    +  mockControl.$replayAll();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +
    +  // Simulate an XHR with 2 headers.
    +  xhr.simulateResponse(200, '', {'test1': 'foo', 'test2': 'bar'});
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testGetResponseHeadersWithColonInValue() {
    +  var xhr = new goog.testing.net.XhrIo();
    +  var mockListener = mockControl.createFunctionMock();
    +  mockListener(new goog.testing.mockmatchers.InstanceOf(goog.events.Event))
    +      .$does(function(e) {
    +        var headers = e.target.getResponseHeaders();
    +        assertEquals(1, goog.object.getCount(headers));
    +        assertEquals('f:o:o', headers['test1']);
    +      });
    +  mockControl.$replayAll();
    +  goog.events.listen(xhr, goog.net.EventType.SUCCESS, mockListener);
    +
    +  // Simulate an XHR with a colon in the http header value.
    +  xhr.simulateResponse(200, '', {'test1': 'f:o:o'});
    +
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/net/xhriopool.js b/mapbox-gl/closure-library/closure/goog/testing/net/xhriopool.js
    new file mode 100644
    index 0000000000..a2baa01199
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/net/xhriopool.js
    @@ -0,0 +1,65 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An XhrIo pool that uses a single mock XHR object for testing.
    + *
    + */
    +
    +goog.provide('goog.testing.net.XhrIoPool');
    +
    +goog.require('goog.net.XhrIoPool');
    +goog.require('goog.testing.net.XhrIo');
    +
    +
    +
    +/**
    + * A pool containing a single mock XhrIo object.
    + *
    + * @param {goog.testing.net.XhrIo=} opt_xhr The mock XhrIo object.
    + * @constructor
    + * @extends {goog.net.XhrIoPool}
    + * @final
    + */
    +goog.testing.net.XhrIoPool = function(opt_xhr) {
    +  /**
    +   * The mock XhrIo object.
    +   * @type {!goog.testing.net.XhrIo}
    +   * @private
    +   */
    +  this.xhr_ = opt_xhr || new goog.testing.net.XhrIo();
    +
    +  // Run this after setting xhr_ because xhr_ is used to initialize the pool.
    +  goog.testing.net.XhrIoPool.base(this, 'constructor', undefined, 1, 1);
    +};
    +goog.inherits(goog.testing.net.XhrIoPool, goog.net.XhrIoPool);
    +
    +
    +/**
    + * @override
    + * @suppress {invalidCasts}
    + */
    +goog.testing.net.XhrIoPool.prototype.createObject = function() {
    +  return (/** @type {!goog.net.XhrIo} */ (this.xhr_));
    +};
    +
    +
    +/**
    + * Get the mock XhrIo used by this pool.
    + *
    + * @return {!goog.testing.net.XhrIo} The mock XhrIo.
    + */
    +goog.testing.net.XhrIoPool.prototype.getXhr = function() {
    +  return this.xhr_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/objectpropertystring.js b/mapbox-gl/closure-library/closure/goog/testing/objectpropertystring.js
    new file mode 100644
    index 0000000000..18c93d96aa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/objectpropertystring.js
    @@ -0,0 +1,68 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Helper for passing property names as string literals in
    + * compiled test code.
    + *
    + */
    +
    +goog.provide('goog.testing.ObjectPropertyString');
    +
    +
    +
    +/**
    + * Object to pass a property name as a string literal and its containing object
    + * when the JSCompiler is rewriting these names. This should only be used in
    + * test code.
    + *
    + * @param {Object} object The containing object.
    + * @param {Object|string} propertyString Property name as a string literal.
    + * @constructor
    + * @final
    + */
    +goog.testing.ObjectPropertyString = function(object, propertyString) {
    +  this.object_ = object;
    +  this.propertyString_ = /** @type {string} */ (propertyString);
    +};
    +
    +
    +/**
    + * @type {Object}
    + * @private
    + */
    +goog.testing.ObjectPropertyString.prototype.object_;
    +
    +
    +/**
    + * @type {string}
    + * @private
    + */
    +goog.testing.ObjectPropertyString.prototype.propertyString_;
    +
    +
    +/**
    + * @return {Object} The object.
    + */
    +goog.testing.ObjectPropertyString.prototype.getObject = function() {
    +  return this.object_;
    +};
    +
    +
    +/**
    + * @return {string} The property string.
    + */
    +goog.testing.ObjectPropertyString.prototype.getPropertyString = function() {
    +  return this.propertyString_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/performancetable.css b/mapbox-gl/closure-library/closure/goog/testing/performancetable.css
    new file mode 100644
    index 0000000000..28056d0264
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/performancetable.css
    @@ -0,0 +1,46 @@
    +/*
    + * Copyright 2009 The Closure Library Authors. All Rights Reserved.
    + *
    + * Use of this source code is governed by the Apache License, Version 2.0.
    + * See the COPYING file for details.
    + */
    +
    +/* Author: attila@google.com (Attila Bodis) */
    +/* Author: nicksantos@google.com (Nick Santos) */
    +
    +table.test-results {
    +  font: 12px "Courier New", Courier, monospace;
    +}
    +
    +table.test-results thead th {
    +  padding: 2px;
    +  color: #fff;
    +  background-color: #369;
    +  text-align: center;
    +}
    +
    +table.test-results tbody td {
    +  padding: 2px;
    +  color: #333;
    +  background-color: #ffc;
    +  text-align: right;
    +}
    +
    +table.test-results tbody td.test-description {
    +  text-align: left;
    +}
    +
    +table.test-results tbody td.test-average {
    +  color: #000;
    +  font-weight: bold;
    +}
    +
    +table.test-results tbody tr.test-suspicious td.test-standard-deviation {
    +  color: #800;
    +}
    +
    +table.test-results tbody td.test-error {
    +  color: #800;
    +  font-weight: bold;
    +  text-align: center;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/performancetable.js b/mapbox-gl/closure-library/closure/goog/testing/performancetable.js
    new file mode 100644
    index 0000000000..c89c2b68bb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/performancetable.js
    @@ -0,0 +1,191 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A table for showing the results of performance testing.
    + *
    + * {@see goog.testing.benchmark} for an easy way to use this functionality.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.testing.PerformanceTable');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.testing.PerformanceTimer');
    +
    +
    +
    +/**
    + * A UI widget that runs performance tests and displays the results.
    + * @param {Element} root The element where the table should be attached.
    + * @param {goog.testing.PerformanceTimer=} opt_timer A timer to use for
    + *     executing functions and profiling them.
    + * @param {number=} opt_precision Number of digits of precision to include in
    + *     results.  Defaults to 0.
    + * @param {number=} opt_numSamples The number of samples to take. Defaults to 5.
    + * @constructor
    + * @final
    + */
    +goog.testing.PerformanceTable = function(
    +    root, opt_timer, opt_precision, opt_numSamples) {
    +  /**
    +   * Where the table should be attached.
    +   * @private {Element}
    +   */
    +  this.root_ = root;
    +
    +  /**
    +   * Number of digits of precision to include in results.
    +   * Defaults to 0.
    +   * @private {number}
    +   */
    +  this.precision_ = opt_precision || 0;
    +
    +  var timer = opt_timer;
    +  if (!timer) {
    +    timer = new goog.testing.PerformanceTimer();
    +    timer.setNumSamples(opt_numSamples || 5);
    +    timer.setDiscardOutliers(true);
    +  }
    +
    +  /**
    +   * A timer for running the tests.
    +   * @private {goog.testing.PerformanceTimer}
    +   */
    +  this.timer_ = timer;
    +
    +  this.initRoot_();
    +};
    +
    +
    +/**
    + * @return {goog.testing.PerformanceTimer} The timer being used.
    + */
    +goog.testing.PerformanceTable.prototype.getTimer = function() {
    +  return this.timer_;
    +};
    +
    +
    +/**
    + * Render the initial table.
    + * @private
    + */
    +goog.testing.PerformanceTable.prototype.initRoot_ = function() {
    +  this.root_.innerHTML =
    +      '<table class="test-results" cellspacing="1">' +
    +      '  <thead>' +
    +      '    <tr>' +
    +      '      <th rowspan="2">Test Description</th>' +
    +      '      <th rowspan="2">Runs</th>' +
    +      '      <th colspan="4">Results (ms)</th>' +
    +      '    </tr>' +
    +      '    <tr>' +
    +      '      <th>Average</th>' +
    +      '      <th>Std Dev</th>' +
    +      '      <th>Minimum</th>' +
    +      '      <th>Maximum</th>' +
    +      '    </tr>' +
    +      '  </thead>' +
    +      '  <tbody>' +
    +      '  </tbody>' +
    +      '</table>';
    +};
    +
    +
    +/**
    + * @return {Element} The body of the table.
    + * @private
    + */
    +goog.testing.PerformanceTable.prototype.getTableBody_ = function() {
    +  return this.root_.getElementsByTagName(goog.dom.TagName.TBODY)[0];
    +};
    +
    +
    +/**
    + * Round to the specified precision.
    + * @param {number} num The number to round.
    + * @return {string} The rounded number, as a string.
    + * @private
    + */
    +goog.testing.PerformanceTable.prototype.round_ = function(num) {
    +  var factor = Math.pow(10, this.precision_);
    +  return String(Math.round(num * factor) / factor);
    +};
    +
    +
    +/**
    + * Run the given function with the performance timer, and show the results.
    + * @param {Function} fn The function to run.
    + * @param {string=} opt_desc A description to associate with this run.
    + */
    +goog.testing.PerformanceTable.prototype.run = function(fn, opt_desc) {
    +  this.runTask(
    +      new goog.testing.PerformanceTimer.Task(/** @type {function()} */ (fn)),
    +      opt_desc);
    +};
    +
    +
    +/**
    + * Run the given task with the performance timer, and show the results.
    + * @param {goog.testing.PerformanceTimer.Task} task The performance timer task
    + *     to run.
    + * @param {string=} opt_desc A description to associate with this run.
    + */
    +goog.testing.PerformanceTable.prototype.runTask = function(task, opt_desc) {
    +  var results = this.timer_.runTask(task);
    +  this.recordResults(results, opt_desc);
    +};
    +
    +
    +/**
    + * Record a performance timer results object to the performance table. See
    + * {@code goog.testing.PerformanceTimer} for details of the format of this
    + * object.
    + * @param {Object} results The performance timer results object.
    + * @param {string=} opt_desc A description to associate with these results.
    + */
    +goog.testing.PerformanceTable.prototype.recordResults = function(
    +    results, opt_desc) {
    +  var average = results['average'];
    +  var standardDeviation = results['standardDeviation'];
    +  var isSuspicious = average < 0 || standardDeviation > average * .5;
    +  var resultsRow = goog.dom.createDom('tr', null,
    +      goog.dom.createDom('td', 'test-description',
    +          opt_desc || 'No description'),
    +      goog.dom.createDom('td', 'test-count', String(results['count'])),
    +      goog.dom.createDom('td', 'test-average', this.round_(average)),
    +      goog.dom.createDom('td', 'test-standard-deviation',
    +          this.round_(standardDeviation)),
    +      goog.dom.createDom('td', 'test-minimum', String(results['minimum'])),
    +      goog.dom.createDom('td', 'test-maximum', String(results['maximum'])));
    +  if (isSuspicious) {
    +    resultsRow.className = 'test-suspicious';
    +  }
    +  this.getTableBody_().appendChild(resultsRow);
    +};
    +
    +
    +/**
    + * Report an error in the table.
    + * @param {*} reason The reason for the error.
    + */
    +goog.testing.PerformanceTable.prototype.reportError = function(reason) {
    +  this.getTableBody_().appendChild(
    +      goog.dom.createDom('tr', null,
    +          goog.dom.createDom('td', {'class': 'test-error', 'colSpan': 5},
    +              String(reason))));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/performancetimer.js b/mapbox-gl/closure-library/closure/goog/testing/performancetimer.js
    new file mode 100644
    index 0000000000..f477105b90
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/performancetimer.js
    @@ -0,0 +1,418 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Performance timer.
    + *
    + * {@see goog.testing.benchmark} for an easy way to use this functionality.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.testing.PerformanceTimer');
    +goog.provide('goog.testing.PerformanceTimer.Task');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * Creates a performance timer that runs test functions a number of times to
    + * generate timing samples, and provides performance statistics (minimum,
    + * maximum, average, and standard deviation).
    + * @param {number=} opt_numSamples Number of times to run the test function;
    + *     defaults to 10.
    + * @param {number=} opt_timeoutInterval Number of milliseconds after which the
    + *     test is to be aborted; defaults to 5 seconds (5,000ms).
    + * @constructor
    + */
    +goog.testing.PerformanceTimer = function(opt_numSamples, opt_timeoutInterval) {
    +  /**
    +   * Number of times the test function is to be run; defaults to 10.
    +   * @private {number}
    +   */
    +  this.numSamples_ = opt_numSamples || 10;
    +
    +  /**
    +   * Number of milliseconds after which the test is to be aborted; defaults to
    +   * 5,000ms.
    +   * @private {number}
    +   */
    +  this.timeoutInterval_ = opt_timeoutInterval || 5000;
    +
    +  /**
    +   * Whether to discard outliers (i.e. the smallest and the largest values)
    +   * from the sample set before computing statistics.  Defaults to false.
    +   * @private {boolean}
    +   */
    +  this.discardOutliers_ = false;
    +};
    +
    +
    +/**
    + * A function whose subsequent calls differ in milliseconds. Used to calculate
    + * the start and stop checkpoint times for runs. Note that high performance
    + * timers do not necessarily return the current time in milliseconds.
    + * @return {number}
    + * @private
    + */
    +goog.testing.PerformanceTimer.now_ = function() {
    +  // goog.now is used in DEBUG mode to make the class easier to test.
    +  return !goog.DEBUG && window.performance && window.performance.now ?
    +      window.performance.now() :
    +      goog.now();
    +};
    +
    +
    +/**
    + * @return {number} The number of times the test function will be run.
    + */
    +goog.testing.PerformanceTimer.prototype.getNumSamples = function() {
    +  return this.numSamples_;
    +};
    +
    +
    +/**
    + * Sets the number of times the test function will be run.
    + * @param {number} numSamples Number of times to run the test function.
    + */
    +goog.testing.PerformanceTimer.prototype.setNumSamples = function(numSamples) {
    +  this.numSamples_ = numSamples;
    +};
    +
    +
    +/**
    + * @return {number} The number of milliseconds after which the test times out.
    + */
    +goog.testing.PerformanceTimer.prototype.getTimeoutInterval = function() {
    +  return this.timeoutInterval_;
    +};
    +
    +
    +/**
    + * Sets the number of milliseconds after which the test times out.
    + * @param {number} timeoutInterval Timeout interval in ms.
    + */
    +goog.testing.PerformanceTimer.prototype.setTimeoutInterval = function(
    +    timeoutInterval) {
    +  this.timeoutInterval_ = timeoutInterval;
    +};
    +
    +
    +/**
    + * Sets whether to ignore the smallest and the largest values when computing
    + * stats.
    + * @param {boolean} discard Whether to discard outlier values.
    + */
    +goog.testing.PerformanceTimer.prototype.setDiscardOutliers = function(discard) {
    +  this.discardOutliers_ = discard;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether outlier values are discarded prior to computing
    + *     stats.
    + */
    +goog.testing.PerformanceTimer.prototype.isDiscardOutliers = function() {
    +  return this.discardOutliers_;
    +};
    +
    +
    +/**
    + * Executes the test function the required number of times (or until the
    + * test run exceeds the timeout interval, whichever comes first).  Returns
    + * an object containing the following:
    + * <pre>
    + *   {
    + *     'average': average execution time (ms)
    + *     'count': number of executions (may be fewer than expected due to timeout)
    + *     'maximum': longest execution time (ms)
    + *     'minimum': shortest execution time (ms)
    + *     'standardDeviation': sample standard deviation (ms)
    + *     'total': total execution time (ms)
    + *   }
    + * </pre>
    + *
    + * @param {Function} testFn Test function whose performance is to
    + *     be measured.
    + * @return {!Object} Object containing performance stats.
    + */
    +goog.testing.PerformanceTimer.prototype.run = function(testFn) {
    +  return this.runTask(new goog.testing.PerformanceTimer.Task(
    +      /** @type {goog.testing.PerformanceTimer.TestFunction} */ (testFn)));
    +};
    +
    +
    +/**
    + * Executes the test function of the specified task as described in
    + * {@code run}. In addition, if specified, the set up and tear down functions of
    + * the task are invoked before and after each invocation of the test function.
    + * @see goog.testing.PerformanceTimer#run
    + * @param {goog.testing.PerformanceTimer.Task} task A task describing the test
    + *     function to invoke.
    + * @return {!Object} Object containing performance stats.
    + */
    +goog.testing.PerformanceTimer.prototype.runTask = function(task) {
    +  var samples = [];
    +  var testStart = goog.testing.PerformanceTimer.now_();
    +  var totalRunTime = 0;
    +
    +  var testFn = task.getTest();
    +  var setUpFn = task.getSetUp();
    +  var tearDownFn = task.getTearDown();
    +
    +  for (var i = 0; i < this.numSamples_ && totalRunTime <= this.timeoutInterval_;
    +       i++) {
    +    setUpFn();
    +    var sampleStart = goog.testing.PerformanceTimer.now_();
    +    testFn();
    +    var sampleEnd = goog.testing.PerformanceTimer.now_();
    +    tearDownFn();
    +    samples[i] = sampleEnd - sampleStart;
    +    totalRunTime = sampleEnd - testStart;
    +  }
    +
    +  return this.finishTask_(samples);
    +};
    +
    +
    +/**
    + * Finishes the run of a task by creating a result object from samples, in the
    + * format described in {@code run}.
    + * @see goog.testing.PerformanceTimer#run
    + * @return {!Object} Object containing performance stats.
    + * @private
    + */
    +goog.testing.PerformanceTimer.prototype.finishTask_ = function(samples) {
    +  if (this.discardOutliers_ && samples.length > 2) {
    +    goog.array.remove(samples, Math.min.apply(null, samples));
    +    goog.array.remove(samples, Math.max.apply(null, samples));
    +  }
    +
    +  return goog.testing.PerformanceTimer.createResults(samples);
    +};
    +
    +
    +/**
    + * Executes the test function of the specified task asynchronously. The test
    + * function is expected to take a callback as input and has to call it to signal
    + * that it's done. In addition, if specified, the setUp and tearDown functions
    + * of the task are invoked before and after each invocation of the test
    + * function. Note that setUp/tearDown functions take a callback as input and
    + * must call this callback when they are done.
    + * @see goog.testing.PerformanceTimer#run
    + * @param {goog.testing.PerformanceTimer.Task} task A task describing the test
    + *     function to invoke.
    + * @return {!goog.async.Deferred} The deferred result, eventually an object
    + *     containing performance stats.
    + */
    +goog.testing.PerformanceTimer.prototype.runAsyncTask = function(task) {
    +  var samples = [];
    +  var testStart = goog.testing.PerformanceTimer.now_();
    +
    +  var testFn = task.getTest();
    +  var setUpFn = task.getSetUp();
    +  var tearDownFn = task.getTearDown();
    +
    +  // Note that this uses a separate code path from runTask() because
    +  // implementing runTask() in terms of runAsyncTask() could easily cause
    +  // a stack overflow if there are many iterations.
    +  var result = new goog.async.Deferred();
    +  this.runAsyncTaskSample_(testFn, setUpFn, tearDownFn, result, samples,
    +      testStart);
    +  return result;
    +};
    +
    +
    +/**
    + * Runs a task once, waits for the test function to complete asynchronously
    + * and starts another run if not enough samples have been collected. Otherwise
    + * finishes this task.
    + * @param {goog.testing.PerformanceTimer.TestFunction} testFn The test function.
    + * @param {goog.testing.PerformanceTimer.TestFunction} setUpFn The set up
    + *     function that will be called once before the test function is run.
    + * @param {goog.testing.PerformanceTimer.TestFunction} tearDownFn The set up
    + *     function that will be called once after the test function completed.
    + * @param {!goog.async.Deferred} result The deferred result, eventually an
    + *     object containing performance stats.
    + * @param {!Array<number>} samples The time samples from all runs of the test
    + *     function so far.
    + * @param {number} testStart The timestamp when the first sample was started.
    + * @private
    + */
    +goog.testing.PerformanceTimer.prototype.runAsyncTaskSample_ = function(testFn,
    +    setUpFn, tearDownFn, result, samples, testStart) {
    +  var timer = this;
    +  timer.handleOptionalDeferred_(setUpFn, function() {
    +    var sampleStart = goog.testing.PerformanceTimer.now_();
    +    timer.handleOptionalDeferred_(testFn, function() {
    +      var sampleEnd = goog.testing.PerformanceTimer.now_();
    +      timer.handleOptionalDeferred_(tearDownFn, function() {
    +        samples.push(sampleEnd - sampleStart);
    +        var totalRunTime = sampleEnd - testStart;
    +        if (samples.length < timer.numSamples_ &&
    +            totalRunTime <= timer.timeoutInterval_) {
    +          timer.runAsyncTaskSample_(testFn, setUpFn, tearDownFn, result,
    +              samples, testStart);
    +        } else {
    +          result.callback(timer.finishTask_(samples));
    +        }
    +      });
    +    });
    +  });
    +};
    +
    +
    +/**
    + * Execute a function that optionally returns a deferred object and continue
    + * with the given continuation function only once the deferred object has a
    + * result.
    + * @param {goog.testing.PerformanceTimer.TestFunction} deferredFactory The
    + *     function that optionally returns a deferred object.
    + * @param {function()} continuationFunction The function that should be called
    + *     after the optional deferred has a result.
    + * @private
    + */
    +goog.testing.PerformanceTimer.prototype.handleOptionalDeferred_ = function(
    +    deferredFactory, continuationFunction) {
    +  var deferred = deferredFactory();
    +  if (deferred) {
    +    deferred.addCallback(continuationFunction);
    +  } else {
    +    continuationFunction();
    +  }
    +};
    +
    +
    +/**
    + * Creates a performance timer results object by analyzing a given array of
    + * sample timings.
    + * @param {Array<number>} samples The samples to analyze.
    + * @return {!Object} Object containing performance stats.
    + */
    +goog.testing.PerformanceTimer.createResults = function(samples) {
    +  return {
    +    'average': goog.math.average.apply(null, samples),
    +    'count': samples.length,
    +    'maximum': Math.max.apply(null, samples),
    +    'minimum': Math.min.apply(null, samples),
    +    'standardDeviation': goog.math.standardDeviation.apply(null, samples),
    +    'total': goog.math.sum.apply(null, samples)
    +  };
    +};
    +
    +
    +/**
    + * A test function whose performance should be measured or a setUp/tearDown
    + * function. It may optionally return a deferred object. If it does so, the
    + * test harness will assume the function is asynchronous and it must signal
    + * that it's done by setting an (empty) result on the deferred object. If the
    + * function doesn't return anything, the test harness will assume it's
    + * synchronous.
    + * @typedef {function():(goog.async.Deferred|undefined)}
    + */
    +goog.testing.PerformanceTimer.TestFunction;
    +
    +
    +
    +/**
    + * A task for the performance timer to measure. Callers can specify optional
    + * setUp and tearDown methods to control state before and after each run of the
    + * test function.
    + * @param {goog.testing.PerformanceTimer.TestFunction} test Test function whose
    + *     performance is to be measured.
    + * @constructor
    + * @final
    + */
    +goog.testing.PerformanceTimer.Task = function(test) {
    +  /**
    +   * The test function to time.
    +   * @type {goog.testing.PerformanceTimer.TestFunction}
    +   * @private
    +   */
    +  this.test_ = test;
    +};
    +
    +
    +/**
    + * An optional set up function to run before each invocation of the test
    + * function.
    + * @type {goog.testing.PerformanceTimer.TestFunction}
    + * @private
    + */
    +goog.testing.PerformanceTimer.Task.prototype.setUp_ = goog.nullFunction;
    +
    +
    +/**
    + * An optional tear down function to run after each invocation of the test
    + * function.
    + * @type {goog.testing.PerformanceTimer.TestFunction}
    + * @private
    + */
    +goog.testing.PerformanceTimer.Task.prototype.tearDown_ = goog.nullFunction;
    +
    +
    +/**
    + * @return {goog.testing.PerformanceTimer.TestFunction} The test function to
    + *     time.
    + */
    +goog.testing.PerformanceTimer.Task.prototype.getTest = function() {
    +  return this.test_;
    +};
    +
    +
    +/**
    + * Specifies a set up function to be invoked before each invocation of the test
    + * function.
    + * @param {goog.testing.PerformanceTimer.TestFunction} setUp The set up
    + *     function.
    + * @return {!goog.testing.PerformanceTimer.Task} This task.
    + */
    +goog.testing.PerformanceTimer.Task.prototype.withSetUp = function(setUp) {
    +  this.setUp_ = setUp;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {goog.testing.PerformanceTimer.TestFunction} The set up function or
    + *     the default no-op function if none was specified.
    + */
    +goog.testing.PerformanceTimer.Task.prototype.getSetUp = function() {
    +  return this.setUp_;
    +};
    +
    +
    +/**
    + * Specifies a tear down function to be invoked after each invocation of the
    + * test function.
    + * @param {goog.testing.PerformanceTimer.TestFunction} tearDown The tear down
    + *     function.
    + * @return {!goog.testing.PerformanceTimer.Task} This task.
    + */
    +goog.testing.PerformanceTimer.Task.prototype.withTearDown = function(tearDown) {
    +  this.tearDown_ = tearDown;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {goog.testing.PerformanceTimer.TestFunction} The tear down function
    + *     or the default no-op function if none was specified.
    + */
    +goog.testing.PerformanceTimer.Task.prototype.getTearDown = function() {
    +  return this.tearDown_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.html b/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.html
    new file mode 100644
    index 0000000000..232aa736da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.PerformanceTimer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.PerformanceTimerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.js b/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.js
    new file mode 100644
    index 0000000000..fe23bf5f5a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/performancetimer_test.js
    @@ -0,0 +1,198 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.PerformanceTimerTest');
    +goog.setTestOnly('goog.testing.PerformanceTimerTest');
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.dom');
    +goog.require('goog.math');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PerformanceTimer');
    +goog.require('goog.testing.jsunit');
    +
    +var mockClock;
    +var sandbox;
    +var timer;
    +function setUpPage() {
    +  sandbox = document.getElementById('sandbox');
    +}
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +  timer = new goog.testing.PerformanceTimer();
    +}
    +
    +function tearDown() {
    +  mockClock.dispose();
    +  timer = null;
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertTrue('Timer must be an instance of goog.testing.PerformanceTimer',
    +      timer instanceof goog.testing.PerformanceTimer);
    +  assertEquals('Timer must collect the default number of samples', 10,
    +      timer.getNumSamples());
    +  assertEquals('Timer must have the default timeout interval', 5000,
    +      timer.getTimeoutInterval());
    +}
    +
    +function testRun_noSetUpOrTearDown() {
    +  runAndAssert(false, false, false);
    +}
    +
    +function testRun_withSetup() {
    +  runAndAssert(true, false, false);
    +}
    +
    +function testRun_withTearDown() {
    +  runAndAssert(false, true, false);
    +}
    +
    +function testRun_withSetUpAndTearDown() {
    +  runAndAssert(true, true, false);
    +}
    +
    +function testRunAsync_noSetUpOrTearDown() {
    +  runAndAssert(false, false, true);
    +}
    +
    +function testRunAsync_withSetup() {
    +  runAndAssert(true, false, true);
    +}
    +
    +function testRunAsync_withTearDown() {
    +  runAndAssert(false, true, true);
    +}
    +
    +function testRunAsync_withSetUpAndTearDown() {
    +  runAndAssert(true, true, true);
    +}
    +
    +function runAndAssert(useSetUp, useTearDown, runAsync) {
    +  var fakeExecutionTime = [100, 95, 98, 104, 130, 101, 96, 98, 90, 103];
    +  var count = 0;
    +  var testFunction = function() {
    +    mockClock.tick(fakeExecutionTime[count++]);
    +    if (runAsync) {
    +      var deferred = new goog.async.Deferred();
    +      deferred.callback();
    +      return deferred;
    +    }
    +  };
    +
    +  var setUpCount = 0;
    +  var setUpFunction = function() {
    +    // Should have no effect on total time.
    +    mockClock.tick(7);
    +    setUpCount++;
    +    if (runAsync) {
    +      var deferred = new goog.async.Deferred();
    +      deferred.callback();
    +      return deferred;
    +    }
    +  };
    +
    +  var tearDownCount = 0;
    +  var tearDownFunction = function() {
    +    // Should have no effect on total time.
    +    mockClock.tick(11);
    +    tearDownCount++;
    +    if (runAsync) {
    +      var deferred = new goog.async.Deferred();
    +      deferred.callback();
    +      return deferred;
    +    }
    +  };
    +
    +  // Fast test function should complete successfully in under 5 seconds...
    +  var task = new goog.testing.PerformanceTimer.Task(testFunction);
    +  if (useSetUp) {
    +    task.withSetUp(setUpFunction);
    +  }
    +  if (useTearDown) {
    +    task.withTearDown(tearDownFunction);
    +  }
    +  if (runAsync) {
    +    var assertsRan = false;
    +    var deferred = timer.runAsyncTask(task);
    +    deferred.addCallback(
    +        function(results) {
    +          assertsRan = assertResults(results, useSetUp, useTearDown,
    +              setUpCount, tearDownCount, fakeExecutionTime);
    +        });
    +    assertTrue(assertsRan);
    +  } else {
    +    var results = timer.runTask(task);
    +    assertResults(results, useSetUp, useTearDown, setUpCount, tearDownCount,
    +        fakeExecutionTime);
    +  }
    +}
    +
    +function assertResults(results, useSetUp, useTearDown, setUpCount,
    +    tearDownCount, fakeExecutionTime) {
    +  assertNotNull('Results must be available.', results);
    +
    +  assertEquals('Average is wrong.',
    +      goog.math.average.apply(null, fakeExecutionTime), results['average']);
    +  assertEquals('Standard deviation is wrong.',
    +      goog.math.standardDeviation.apply(null, fakeExecutionTime),
    +      results['standardDeviation']);
    +
    +  assertEquals('Count must be as expected.', 10, results['count']);
    +  assertEquals('Maximum is wrong.', 130, results['maximum']);
    +  assertEquals('Mimimum is wrong.', 90, results['minimum']);
    +  assertEquals('Total must be a nonnegative number.',
    +      goog.math.sum.apply(null, fakeExecutionTime), results['total']);
    +
    +  assertEquals('Set up count must be as expected.',
    +      useSetUp ? 10 : 0, setUpCount);
    +  assertEquals('Tear down count must be as expected.',
    +      useTearDown ? 10 : 0, tearDownCount);
    +
    +  return true;
    +}
    +
    +function testTimeout() {
    +  var count = 0;
    +  var testFunction = function() {
    +    mockClock.tick(100);
    +    ++count;
    +  };
    +
    +  timer.setNumSamples(200);
    +  timer.setTimeoutInterval(2500);
    +  var results = timer.run(testFunction);
    +
    +  assertNotNull('Results must be available', results);
    +  assertEquals('Count is wrong', count, results['count']);
    +  assertTrue('Count must less than expected',
    +      results['count'] < timer.getNumSamples());
    +}
    +
    +function testCreateResults() {
    +  var samples = [53, 0, 103];
    +  var expectedResults = {
    +    'average': 52,
    +    'count': 3,
    +    'maximum': 103,
    +    'minimum': 0,
    +    'standardDeviation': goog.math.standardDeviation.apply(null, samples),
    +    'total': 156
    +  };
    +  assertObjectEquals(
    +      expectedResults,
    +      goog.testing.PerformanceTimer.createResults(samples));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer.js b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer.js
    new file mode 100644
    index 0000000000..3a8b882d8e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer.js
    @@ -0,0 +1,245 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Helper class for creating stubs for testing.
    + *
    + */
    +
    +goog.provide('goog.testing.PropertyReplacer');
    +
    +/** @suppress {extraRequire} Needed for some tests to compile. */
    +goog.require('goog.testing.ObjectPropertyString');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Helper class for stubbing out variables and object properties for unit tests.
    + * This class can change the value of some variables before running the test
    + * cases, and to reset them in the tearDown phase.
    + * See googletest.StubOutForTesting as an analogy in Python:
    + * http://protobuf.googlecode.com/svn/trunk/python/stubout.py
    + *
    + * Example usage:
    + * <pre>var stubs = new goog.testing.PropertyReplacer();
    + *
    + * function setUp() {
    + *   // Mock functions used in all test cases.
    + *   stubs.set(Math, 'random', function() {
    + *     return 4;  // Chosen by fair dice roll. Guaranteed to be random.
    + *   });
    + * }
    + *
    + * function tearDown() {
    + *   stubs.reset();
    + * }
    + *
    + * function testThreeDice() {
    + *   // Mock a constant used only in this test case.
    + *   stubs.set(goog.global, 'DICE_COUNT', 3);
    + *   assertEquals(12, rollAllDice());
    + * }</pre>
    + *
    + * Constraints on altered objects:
    + * <ul>
    + *   <li>DOM subclasses aren't supported.
    + *   <li>The value of the objects' constructor property must either be equal to
    + *       the real constructor or kept untouched.
    + * </ul>
    + *
    + * @constructor
    + * @final
    + */
    +goog.testing.PropertyReplacer = function() {
    +  /**
    +   * Stores the values changed by the set() method in chronological order.
    +   * Its items are objects with 3 fields: 'object', 'key', 'value'. The
    +   * original value for the given key in the given object is stored under the
    +   * 'value' key.
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.original_ = [];
    +};
    +
    +
    +/**
    + * Indicates that a key didn't exist before having been set by the set() method.
    + * @private @const
    + */
    +goog.testing.PropertyReplacer.NO_SUCH_KEY_ = {};
    +
    +
    +/**
    + * Tells if the given key exists in the object. Ignores inherited fields.
    + * @param {Object|Function} obj The JavaScript or native object or function
    + *     whose key is to be checked.
    + * @param {string} key The key to check.
    + * @return {boolean} Whether the object has the key as own key.
    + * @private
    + */
    +goog.testing.PropertyReplacer.hasKey_ = function(obj, key) {
    +  if (!(key in obj)) {
    +    return false;
    +  }
    +  // hasOwnProperty is only reliable with JavaScript objects. It returns false
    +  // for built-in DOM attributes.
    +  if (Object.prototype.hasOwnProperty.call(obj, key)) {
    +    return true;
    +  }
    +  // In all browsers except Opera obj.constructor never equals to Object if
    +  // obj is an instance of a native class. In Opera we have to fall back on
    +  // examining obj.toString().
    +  if (obj.constructor == Object &&
    +      (!goog.userAgent.OPERA ||
    +          Object.prototype.toString.call(obj) == '[object Object]')) {
    +    return false;
    +  }
    +  try {
    +    // Firefox hack to consider "className" part of the HTML elements or
    +    // "body" part of document. Although they are defined in the prototype of
    +    // HTMLElement or Document, accessing them this way throws an exception.
    +    // <pre>
    +    //   var dummy = document.body.constructor.prototype.className
    +    //   [Exception... "Cannot modify properties of a WrappedNative"]
    +    // </pre>
    +    var dummy = obj.constructor.prototype[key];
    +  } catch (e) {
    +    return true;
    +  }
    +  return !(key in obj.constructor.prototype);
    +};
    +
    +
    +/**
    + * Deletes a key from an object. Sets it to undefined or empty string if the
    + * delete failed.
    + * @param {Object|Function} obj The object or function to delete a key from.
    + * @param {string} key The key to delete.
    + * @private
    + */
    +goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) {
    +  try {
    +    delete obj[key];
    +    // Delete has no effect for built-in properties of DOM nodes in FF.
    +    if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    +      return;
    +    }
    +  } catch (e) {
    +    // IE throws TypeError when trying to delete properties of native objects
    +    // (e.g. DOM nodes or window), even if they have been added by JavaScript.
    +  }
    +
    +  obj[key] = undefined;
    +  if (obj[key] == 'undefined') {
    +    // Some properties such as className in IE are always evaluated as string
    +    // so undefined will become 'undefined'.
    +    obj[key] = '';
    +  }
    +};
    +
    +
    +/**
    + * Adds or changes a value in an object while saving its original state.
    + * @param {Object|Function} obj The JavaScript or native object or function to
    + *     alter. See the constraints in the class description.
    + * @param {string} key The key to change the value for.
    + * @param {*} value The new value to set.
    + */
    +goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) {
    +  var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? obj[key] :
    +                  goog.testing.PropertyReplacer.NO_SUCH_KEY_;
    +  this.original_.push({object: obj, key: key, value: origValue});
    +  obj[key] = value;
    +};
    +
    +
    +/**
    + * Changes an existing value in an object to another one of the same type while
    + * saving its original state. The advantage of {@code replace} over {@link #set}
    + * is that {@code replace} protects against typos and erroneously passing tests
    + * after some members have been renamed during a refactoring.
    + * @param {Object|Function} obj The JavaScript or native object or function to
    + *     alter. See the constraints in the class description.
    + * @param {string} key The key to change the value for. It has to be present
    + *     either in {@code obj} or in its prototype chain.
    + * @param {*} value The new value to set. It has to have the same type as the
    + *     original value. The types are compared with {@link goog.typeOf}.
    + * @throws {Error} In case of missing key or type mismatch.
    + */
    +goog.testing.PropertyReplacer.prototype.replace = function(obj, key, value) {
    +  if (!(key in obj)) {
    +    throw Error('Cannot replace missing property "' + key + '" in ' + obj);
    +  }
    +  if (goog.typeOf(obj[key]) != goog.typeOf(value)) {
    +    throw Error('Cannot replace property "' + key + '" in ' + obj +
    +        ' with a value of different type');
    +  }
    +  this.set(obj, key, value);
    +};
    +
    +
    +/**
    + * Builds an object structure for the provided namespace path.  Doesn't
    + * overwrite those prefixes of the path that are already objects or functions.
    + * @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'.
    + * @param {*} value The value to set.
    + */
    +goog.testing.PropertyReplacer.prototype.setPath = function(path, value) {
    +  var parts = path.split('.');
    +  var obj = goog.global;
    +  for (var i = 0; i < parts.length - 1; i++) {
    +    var part = parts[i];
    +    if (part == 'prototype' && !obj[part]) {
    +      throw Error('Cannot set the prototype of ' + parts.slice(0, i).join('.'));
    +    }
    +    if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) {
    +      this.set(obj, part, {});
    +    }
    +    obj = obj[part];
    +  }
    +  this.set(obj, parts[parts.length - 1], value);
    +};
    +
    +
    +/**
    + * Deletes the key from the object while saving its original value.
    + * @param {Object|Function} obj The JavaScript or native object or function to
    + *     alter. See the constraints in the class description.
    + * @param {string} key The key to delete.
    + */
    +goog.testing.PropertyReplacer.prototype.remove = function(obj, key) {
    +  if (goog.testing.PropertyReplacer.hasKey_(obj, key)) {
    +    this.original_.push({object: obj, key: key, value: obj[key]});
    +    goog.testing.PropertyReplacer.deleteKey_(obj, key);
    +  }
    +};
    +
    +
    +/**
    + * Resets all changes made by goog.testing.PropertyReplacer.prototype.set.
    + */
    +goog.testing.PropertyReplacer.prototype.reset = function() {
    +  for (var i = this.original_.length - 1; i >= 0; i--) {
    +    var original = this.original_[i];
    +    if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) {
    +      goog.testing.PropertyReplacer.deleteKey_(original.object, original.key);
    +    } else {
    +      original.object[original.key] = original.value;
    +    }
    +    delete this.original_[i];
    +  }
    +  this.original_.length = 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.html b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.html
    new file mode 100644
    index 0000000000..6455c37904
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.PropertyReplacer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.PropertyReplacerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.js b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.js
    new file mode 100644
    index 0000000000..e5712d099f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/propertyreplacer_test.js
    @@ -0,0 +1,364 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.PropertyReplacerTest');
    +goog.setTestOnly('goog.testing.PropertyReplacerTest');
    +
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +// Test PropertyReplacer with JavaScript objects.
    +function testSetJsProperties() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +  var x = {a: 1, b: undefined};
    +
    +  // Setting simple values.
    +  stubs.set(x, 'num', 1);
    +  assertEquals('x.num = 1', 1, x.num);
    +  stubs.set(x, 'undef', undefined);
    +  assertTrue('x.undef = undefined', 'undef' in x && x.undef === undefined);
    +  stubs.set(x, 'null', null);
    +  assertTrue('x["null"] = null', x['null'] === null);
    +
    +  // Setting a simple value that existed originally.
    +  stubs.set(x, 'b', null);
    +  assertTrue('x.b = null', x.b === null);
    +
    +  // Setting a complex value.
    +  stubs.set(x, 'obj', {});
    +  assertEquals('x.obj = {}', 'object', typeof x.obj);
    +  stubs.set(x.obj, 'num', 2);
    +  assertEquals('x.obj.num = 2', 2, x.obj.num);
    +
    +  // Overwriting a leaf.
    +  stubs.set(x.obj, 'num', 3);
    +  assertEquals('x.obj.num = 3', 3, x.obj.num);
    +
    +  // Overwriting a non-leaf.
    +  stubs.set(x, 'obj', {});
    +  assertFalse('x.obj = {} again', 'num' in x.obj);
    +
    +  // Setting a function.
    +  stubs.set(x, 'func', function(n) { return n + 1; });
    +  assertEquals('x.func = lambda n: n+1', 11, x.func(10));
    +
    +  // Setting a constructor and a prototype method.
    +  stubs.set(x, 'Class', function(num) { this.num = num; });
    +  stubs.set(x.Class.prototype, 'triple', function() { return this.num * 3; });
    +  assertEquals('prototype method', 12, (new x.Class(4)).triple());
    +
    +  // Cleaning up with UnsetAll() twice. The second run should have no effect.
    +  for (var i = 0; i < 2; i++) {
    +    stubs.reset();
    +    assertEquals('x.a preserved', 1, x.a);
    +    assertTrue('x.b reset', 'b' in x && x.b === undefined);
    +    assertFalse('x.num removed', 'num' in x);
    +    assertFalse('x.undef removed', 'undef' in x);
    +    assertFalse('x["null"] removed', 'null' in x);
    +    assertFalse('x.obj removed', 'obj' in x);
    +    assertFalse('x.func removed', 'func' in x);
    +    assertFalse('x.Class removed', 'Class' in x);
    +  }
    +}
    +
    +// Test removing JavaScript object properties.
    +function testRemoveJsProperties() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +  var orig = {'a': 1, 'b': undefined};
    +  var x = {'a': 1, 'b': undefined};
    +
    +  stubs.remove(x, 'a');
    +  assertFalse('x.a removed', 'a' in x);
    +  assertTrue('x.b not removed', 'b' in x);
    +  stubs.reset();
    +  assertObjectEquals('x.a reset', x, orig);
    +
    +  stubs.remove(x, 'b');
    +  assertFalse('x.b removed', 'b' in x);
    +  stubs.reset();
    +  assertObjectEquals('x.b reset', x, orig);
    +
    +  stubs.set(x, 'c', 2);
    +  stubs.remove(x, 'c');
    +  assertObjectEquals('x.c added then removed', x, orig);
    +  stubs.reset();
    +  assertObjectEquals('x.c reset', x, orig);
    +
    +  stubs.remove(x, 'b');
    +  stubs.set(x, 'b', undefined);
    +  assertObjectEquals('x.b removed then added', x, orig);
    +  stubs.reset();
    +  assertObjectEquals('x.b reset', x, orig);
    +
    +  stubs.remove(x, 'd');
    +  assertObjectEquals('removing non-existing key', x, orig);
    +  stubs.reset();
    +  assertObjectEquals('reset removing non-existing key', x, orig);
    +}
    +
    +// Test PropertyReplacer with prototype chain.
    +function testPrototype() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +
    +  // Simple inheritance.
    +  var a = {a: 0};
    +  function B() {};
    +  B.prototype = a;
    +  var b = new B();
    +
    +  stubs.set(a, 0, 1);
    +  stubs.set(b, 0, 2);
    +  stubs.reset();
    +  assertEquals('a.a == 0', 0, a['a']);
    +  assertEquals('b.a == 0', 0, b['a']);
    +
    +  // Inheritance with goog.inherits.
    +  var c = {a: 0};
    +  function C() {};
    +  C.prototype = c;
    +  function D() {};
    +  goog.inherits(D, C);
    +  var d = new D();
    +
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(c, 'a', 1);
    +  stubs.set(d, 'a', 2);
    +  stubs.reset();
    +  assertEquals('c.a == 0', 0, c['a']);
    +  assertEquals('d.a == 0', 0, d['a']);
    +
    +  // Setting prototype fields.
    +  stubs.set(B.prototype, 'c', 'z');
    +  assertEquals('b.c=="z"', 'z', b.c);
    +  stubs.reset();
    +  assertFalse('b.c deleted', 'c' in b);
    +
    +  // Setting Object.prototype's fields.
    +  stubs.set(Object.prototype, 'one', 1);
    +  assertEquals('b.one==1', 1, b.one);
    +  stubs.reset();
    +  assertFalse('b.one deleted', 'one' in b);
    +
    +  stubs.set(Object.prototype, 'two', 2);
    +  stubs.remove(b, 'two');
    +  assertEquals('b.two==2', 2, b.two);
    +  stubs.remove(Object.prototype, 'two');
    +  assertFalse('b.two deleted', 'two' in b);
    +  stubs.reset();
    +  assertFalse('Object prototype reset', 'two' in b);
    +}
    +
    +// Test replacing function properties.
    +function testFunctionProperties() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(Array, 'x', 1);
    +  assertEquals('Array.x==1', 1, Array.x);
    +  stubs.reset();
    +  assertFalse('Array.x deleted', 'x' in Array);
    +
    +  stubs.set(Math.random, 'x', 1);
    +  assertEquals('Math.random.x==1', 1, Math.random.x);
    +  stubs.reset();
    +  assertFalse('Math.random.x deleted', 'x' in Math.random);
    +}
    +
    +// Test the hasKey_ private method.
    +function testHasKey() {
    +  f = goog.testing.PropertyReplacer.hasKey_;
    +
    +  assertFalse('{}.a', f({}, 'a'));
    +  assertTrue('{a:0}.a', f({a: 0}, 'a'));
    +
    +  function C() {};
    +  C.prototype.a = 0;
    +  assertFalse('C.prototype.a set, is C.a own?', f(C, 'a'));
    +  assertTrue('C.prototype.a', f(C.prototype, 'a'));
    +  assertFalse('C.a not set', f(C, 'a'));
    +  C.a = 0;
    +  assertTrue('C.a set', f(C, 'a'));
    +
    +  var c = new C();
    +  assertFalse('C().a, inherited', f(c, 'a'));
    +  c.a = 0;
    +  assertTrue('C().a, own', f(c, 'a'));
    +
    +  assertFalse('window, invalid key', f(window, 'no such key'));
    +  assertTrue('window, global variable', f(window, 'goog'));
    +  assertTrue('window, build-in key', f(window, 'location'));
    +
    +  assertFalse('document, invalid key', f(document, 'no such key'));
    +  assertTrue('document.body', f(document, 'body'));
    +
    +  var div = document.createElement('div');
    +  assertFalse('div, invalid key', f(div, 'no such key'));
    +  assertTrue('div.className', f(div, 'className'));
    +  div['a'] = 0;
    +  assertTrue('div, key added by JS', f(div, 'a'));
    +
    +  assertFalse('Date().getTime', f(new Date(), 'getTime'));
    +  assertTrue('Date.prototype.getTime', f(Date.prototype, 'getTime'));
    +
    +  assertFalse('Math, invalid key', f(Math, 'no such key'));
    +  assertTrue('Math.random', f(Math, 'random'));
    +
    +  function Parent() {};
    +  Parent.prototype.a = 0;
    +  function Child() {};
    +  goog.inherits(Child, Parent);
    +  assertFalse('goog.inherits, parent prototype', f(new Child, 'a'));
    +  Child.prototype.a = 1;
    +  assertFalse('goog.inherits, child prototype', f(new Child, 'a'));
    +
    +  function OverwrittenProto() {};
    +  OverwrittenProto.prototype = {a: 0};
    +  assertFalse(f(new OverwrittenProto, 'a'));
    +}
    +
    +// Test PropertyReplacer with DOM objects' built in attributes.
    +function testDomBuiltInAttributes() {
    +  var div = document.createElement('div');
    +  div.id = 'old-id';
    +
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(div, 'id', 'new-id');
    +  stubs.set(div, 'className', 'test-class');
    +  assertEquals('div.id == "new-id"', 'new-id', div.id);
    +  assertEquals('div.className == "test-class"', 'test-class', div.className);
    +
    +  stubs.remove(div, 'className');
    +  // '' in Firefox, undefined in Chrome.
    +  assertEvaluatesToFalse('div.className is empty', div.className);
    +
    +  stubs.reset();
    +  assertEquals('div.id == "old-id"', 'old-id', div.id);
    +  assertEquals('div.name == ""', '', div.className);
    +}
    +
    +// Test PropertyReplacer with DOM objects' custom attributes.
    +function testDomCustomAttributes() {
    +  var div = document.createElement('div');
    +  div.attr1 = 'old';
    +
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(div, 'attr1', 'new');
    +  stubs.set(div, 'attr2', 'new');
    +  assertEquals('div.attr1 == "new"', 'new', div.attr1);
    +  assertEquals('div.attr2 == "new"', 'new', div.attr2);
    +
    +  stubs.set(div, 'attr3', 'new');
    +  stubs.remove(div, 'attr3');
    +  assertEquals('div.attr3 == undefined', undefined, div.attr3);
    +
    +  stubs.reset();
    +  assertEquals('div.attr1 == "old"', 'old', div.attr1);
    +  assertEquals('div.attr2 == undefined', undefined, div.attr2);
    +}
    +
    +// Test PropertyReplacer overriding Math.random.
    +function testMathRandom() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +  stubs.set(Math, 'random', function() { return -1; });
    +  assertEquals('mocked Math.random', -1, Math.random());
    +
    +  stubs.reset();
    +  assertNotEquals('original Math.random', -1, Math.random());
    +}
    +
    +// Tests the replace method of PropertyReplacer.
    +function testReplace() {
    +  var stubs = new goog.testing.PropertyReplacer();
    +  function C() {
    +    this.a = 1;
    +  };
    +  C.prototype.b = 1;
    +  C.prototype.toString = function() {
    +    return 'obj';
    +  };
    +  var obj = new C();
    +
    +  stubs.replace(obj, 'a', 2);
    +  assertEquals('successfully replaced the own property of an object', 2, obj.a);
    +
    +  stubs.replace(obj, 'b', 2);
    +  assertEquals('successfully replaced the property in the prototype', 2, obj.b);
    +
    +  var error = assertThrows('cannot replace missing key',
    +      goog.bind(stubs.replace, stubs, obj, 'unknown', 1));
    +  // Using assertContains instead of assertEquals because Opera 10.0 adds
    +  // the stack trace to the error message.
    +  assertEquals('error message for missing key',
    +      'Cannot replace missing property "unknown" in obj', error.message);
    +  assertFalse('object not touched', 'unknown' in obj);
    +
    +  var error = assertThrows('cannot change value type',
    +      goog.bind(stubs.replace, stubs, obj, 'a', '3'));
    +  assertContains('error message for type mismatch',
    +      'Cannot replace property "a" in obj with a value of different type',
    +      error.message);
    +}
    +
    +// Tests altering complete namespace paths.
    +function testSetPath() {
    +  goog.global.a = {b: {}};
    +  var stubs = new goog.testing.PropertyReplacer();
    +
    +  stubs.setPath('a.b.c.d', 1);
    +  assertObjectEquals('a.b.c.d=1', {b: {c: {d: 1}}}, goog.global.a);
    +  stubs.setPath('a.b.e', 2);
    +  assertObjectEquals('a.b.e=2', {b: {c: {d: 1}, e: 2}}, goog.global.a);
    +  stubs.setPath('a.f', 3);
    +  assertObjectEquals('a.f=3', {b: {c: {d: 1}, e: 2}, f: 3}, goog.global.a);
    +  stubs.setPath('a.f.g', 4);
    +  assertObjectEquals('a.f.g=4', {b: {c: {d: 1}, e: 2}, f: {g: 4}},
    +                     goog.global.a);
    +  stubs.setPath('a', 5);
    +  assertEquals('a=5', 5, goog.global.a);
    +
    +  stubs.setPath('x.y.z', 5);
    +  assertObjectEquals('x.y.z=5', {y: {z: 5}}, goog.global.x);
    +
    +  stubs.reset();
    +  assertObjectEquals('a.* reset', {b: {}}, goog.global.a);
    +  // NOTE: it's impossible to delete global variables in Internet Explorer,
    +  // so ('x' in goog.global) would be true.
    +  assertUndefined('x.* reset', goog.global.x);
    +}
    +
    +// Tests altering paths with functions in them.
    +function testSetPathWithFunction() {
    +  var f = function() {};
    +  goog.global.a = {b: f};
    +  var stubs = new goog.testing.PropertyReplacer();
    +
    +  stubs.setPath('a.b.c', 1);
    +  assertEquals('a.b.c=1, f kept', f, goog.global.a.b);
    +  assertEquals('a.b.c=1, c set', 1, goog.global.a.b.c);
    +
    +  stubs.setPath('a.b.prototype.d', 2);
    +  assertEquals('a.b.prototype.d=2, b kept', f, goog.global.a.b);
    +  assertEquals('a.b.prototype.d=2, c kept', 1, goog.global.a.b.c);
    +  assertFalse('a.b.prototype.d=2, a.b.d not set', 'd' in goog.global.a.b);
    +  assertTrue('a.b.prototype.d=2, proto set', 'd' in goog.global.a.b.prototype);
    +  assertEquals('a.b.prototype.d=2, d set', 2, new goog.global.a.b().d);
    +
    +  var invalidSetPath = function() {
    +    stubs.setPath('a.prototype.e', 3);
    +  };
    +  assertThrows('setting the prototype of a non-function', invalidSetPath);
    +
    +  stubs.reset();
    +  assertObjectEquals('a.b.c reset', {b: f}, goog.global.a);
    +  assertObjectEquals('a.b.prototype reset', {}, goog.global.a.b.prototype);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2.js b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2.js
    new file mode 100644
    index 0000000000..fbf232c994
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2.js
    @@ -0,0 +1,145 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Test helpers to compare goog.proto2.Messages.
    + *
    + */
    +
    +goog.provide('goog.testing.proto2');
    +
    +goog.require('goog.proto2.Message');
    +goog.require('goog.proto2.ObjectSerializer');
    +goog.require('goog.testing.asserts');
    +
    +
    +/**
    + * Compares two goog.proto2.Message instances of the same type.
    + * @param {!goog.proto2.Message} expected First message.
    + * @param {!goog.proto2.Message} actual Second message.
    + * @param {string} path Path to the messages.
    + * @return {string} A string describing where they differ. Empty string if they
    + *     are equal.
    + * @private
    + */
    +goog.testing.proto2.findDifferences_ = function(expected, actual, path) {
    +  var fields = expected.getDescriptor().getFields();
    +  for (var i = 0; i < fields.length; i++) {
    +    var field = fields[i];
    +    var newPath = (path ? path + '/' : '') + field.getName();
    +
    +    if (expected.has(field) && !actual.has(field)) {
    +      return newPath + ' should be present';
    +    }
    +    if (!expected.has(field) && actual.has(field)) {
    +      return newPath + ' should not be present';
    +    }
    +
    +    if (expected.has(field)) {
    +      var isComposite = field.isCompositeType();
    +
    +      if (field.isRepeated()) {
    +        var expectedCount = expected.countOf(field);
    +        var actualCount = actual.countOf(field);
    +        if (expectedCount != actualCount) {
    +          return newPath + ' should have ' + expectedCount + ' items, ' +
    +              'but has ' + actualCount;
    +        }
    +
    +        for (var j = 0; j < expectedCount; j++) {
    +          var expectedItem = expected.get(field, j);
    +          var actualItem = actual.get(field, j);
    +          if (isComposite) {
    +            var itemDiff = goog.testing.proto2.findDifferences_(
    +                /** @type {!goog.proto2.Message} */ (expectedItem),
    +                /** @type {!goog.proto2.Message} */ (actualItem),
    +                newPath + '[' + j + ']');
    +            if (itemDiff) {
    +              return itemDiff;
    +            }
    +          } else {
    +            if (expectedItem != actualItem) {
    +              return newPath + '[' + j + '] should be ' + expectedItem +
    +                  ', but was ' + actualItem;
    +            }
    +          }
    +        }
    +      } else {
    +        var expectedValue = expected.get(field);
    +        var actualValue = actual.get(field);
    +        if (isComposite) {
    +          var diff = goog.testing.proto2.findDifferences_(
    +              /** @type {!goog.proto2.Message} */ (expectedValue),
    +              /** @type {!goog.proto2.Message} */ (actualValue),
    +              newPath);
    +          if (diff) {
    +            return diff;
    +          }
    +        } else {
    +          if (expectedValue != actualValue) {
    +            return newPath + ' should be ' + expectedValue + ', but was ' +
    +                actualValue;
    +          }
    +        }
    +      }
    +    }
    +  }
    +
    +  return '';
    +};
    +
    +
    +/**
    + * Compares two goog.proto2.Message objects. Gives more readable output than
    + * assertObjectEquals on mismatch.
    + * @param {!goog.proto2.Message} expected Expected proto2 message.
    + * @param {!goog.proto2.Message} actual Actual proto2 message.
    + * @param {string=} opt_failureMessage Failure message when the values don't
    + *     match.
    + */
    +goog.testing.proto2.assertEquals = function(expected, actual,
    +    opt_failureMessage) {
    +  var failureSummary = opt_failureMessage || '';
    +  if (!(expected instanceof goog.proto2.Message) ||
    +      !(actual instanceof goog.proto2.Message)) {
    +    goog.testing.asserts.raiseException(failureSummary,
    +        'Bad arguments were passed to goog.testing.proto2.assertEquals');
    +  }
    +  if (expected.constructor != actual.constructor) {
    +    goog.testing.asserts.raiseException(failureSummary,
    +        'Message type mismatch: ' + expected.getDescriptor().getFullName() +
    +        ' != ' + actual.getDescriptor().getFullName());
    +  }
    +  var diff = goog.testing.proto2.findDifferences_(expected, actual, '');
    +  if (diff) {
    +    goog.testing.asserts.raiseException(failureSummary, diff);
    +  }
    +};
    +
    +
    +/**
    + * Helper function to quickly build protocol buffer messages from JSON objects.
    + * @param {function(new:MessageType)} messageCtor A constructor that
    + *     creates a {@code goog.proto2.Message} subclass instance.
    + * @param {!Object} json JSON object which uses field names as keys.
    + * @return {MessageType} The deserialized protocol buffer.
    + * @template MessageType
    + */
    +goog.testing.proto2.fromObject = function(messageCtor, json) {
    +  var serializer = new goog.proto2.ObjectSerializer(
    +      goog.proto2.ObjectSerializer.KeyOption.NAME);
    +  var message = new messageCtor;
    +  serializer.deserializeTo(message, json);
    +  return message;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.html b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.html
    new file mode 100644
    index 0000000000..33444189e6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.proto2
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.proto2Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.js b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.js
    new file mode 100644
    index 0000000000..2b1678d5d9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/proto2/proto2_test.js
    @@ -0,0 +1,137 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.proto2Test');
    +goog.setTestOnly('goog.testing.proto2Test');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.proto2');
    +goog.require('proto2.TestAllTypes');
    +
    +function testAssertEquals() {
    +  var assertProto2Equals = goog.testing.proto2.assertEquals;
    +  assertProto2Equals(new proto2.TestAllTypes, new proto2.TestAllTypes);
    +  assertProto2Equals(new proto2.TestAllTypes, new proto2.TestAllTypes, 'oops');
    +
    +  var ex = assertThrows(goog.partial(assertProto2Equals,
    +      new proto2.TestAllTypes, new proto2.TestAllTypes.NestedMessage));
    +  assertEquals(
    +      'Message type mismatch: TestAllTypes != TestAllTypes.NestedMessage',
    +      ex.message);
    +
    +  var message = new proto2.TestAllTypes;
    +  message.setOptionalInt32(1);
    +  ex = assertThrows(goog.partial(assertProto2Equals,
    +      new proto2.TestAllTypes, message));
    +  assertEquals('optional_int32 should not be present', ex.message);
    +
    +  ex = assertThrows(goog.partial(assertProto2Equals,
    +      new proto2.TestAllTypes, message, 'oops'));
    +  assertEquals('oops\noptional_int32 should not be present', ex.message);
    +}
    +
    +function testFindDifferences_EmptyMessages() {
    +  assertEquals('', goog.testing.proto2.findDifferences_(
    +      new proto2.TestAllTypes, new proto2.TestAllTypes, ''));
    +}
    +
    +function testFindDifferences_FieldNotPresent() {
    +  var message = new proto2.TestAllTypes;
    +  message.setOptionalInt32(0);
    +  var empty = new proto2.TestAllTypes;
    +  assertEquals('optional_int32 should not be present',
    +      goog.testing.proto2.findDifferences_(empty, message, ''));
    +  assertEquals('optional_int32 should be present',
    +      goog.testing.proto2.findDifferences_(message, empty, ''));
    +  assertEquals('path/optional_int32 should be present',
    +      goog.testing.proto2.findDifferences_(message, empty, 'path'));
    +}
    +
    +function testFindDifferences_IntFieldDiffers() {
    +  var message1 = new proto2.TestAllTypes;
    +  message1.setOptionalInt32(1);
    +  var message2 = new proto2.TestAllTypes;
    +  message2.setOptionalInt32(2);
    +  assertEquals('optional_int32 should be 1, but was 2',
    +      goog.testing.proto2.findDifferences_(message1, message2, ''));
    +}
    +
    +function testFindDifferences_NestedIntFieldDiffers() {
    +  var message1 = new proto2.TestAllTypes;
    +  var nested1 = new proto2.TestAllTypes.NestedMessage();
    +  nested1.setB(1);
    +  message1.setOptionalNestedMessage(nested1);
    +  var message2 = new proto2.TestAllTypes;
    +  var nested2 = new proto2.TestAllTypes.NestedMessage();
    +  nested2.setB(2);
    +  message2.setOptionalNestedMessage(nested2);
    +  assertEquals('optional_nested_message/b should be 1, but was 2',
    +      goog.testing.proto2.findDifferences_(message1, message2, ''));
    +}
    +
    +function testFindDifferences_RepeatedFieldLengthDiffers() {
    +  var message1 = new proto2.TestAllTypes;
    +  message1.addRepeatedInt32(1);
    +  var message2 = new proto2.TestAllTypes;
    +  message2.addRepeatedInt32(1);
    +  message2.addRepeatedInt32(2);
    +  assertEquals('repeated_int32 should have 1 items, but has 2',
    +      goog.testing.proto2.findDifferences_(message1, message2, ''));
    +}
    +
    +function testFindDifferences_RepeatedFieldItemDiffers() {
    +  var message1 = new proto2.TestAllTypes;
    +  message1.addRepeatedInt32(1);
    +  var message2 = new proto2.TestAllTypes;
    +  message2.addRepeatedInt32(2);
    +  assertEquals('repeated_int32[0] should be 1, but was 2',
    +      goog.testing.proto2.findDifferences_(message1, message2, ''));
    +}
    +
    +function testFindDifferences_RepeatedNestedMessageDiffers() {
    +  var message1 = new proto2.TestAllTypes;
    +  var nested1 = new proto2.TestAllTypes.NestedMessage();
    +  nested1.setB(1);
    +  message1.addRepeatedNestedMessage(nested1);
    +  var message2 = new proto2.TestAllTypes;
    +  var nested2 = new proto2.TestAllTypes.NestedMessage();
    +  nested2.setB(2);
    +  message2.addRepeatedNestedMessage(nested2);
    +  assertEquals('repeated_nested_message[0]/b should be 1, but was 2',
    +      goog.testing.proto2.findDifferences_(message1, message2, ''));
    +}
    +
    +function testFromObject() {
    +  var nested = new proto2.TestAllTypes.NestedMessage();
    +  nested.setB(1);
    +  var message = new proto2.TestAllTypes;
    +  message.addRepeatedNestedMessage(nested);
    +  message.setOptionalInt32(2);
    +  // Successfully deserializes simple as well as message fields.
    +  assertObjectEquals(
    +      message,
    +      goog.testing.proto2.fromObject(proto2.TestAllTypes, {
    +        'optional_int32': 2,
    +        'repeated_nested_message': [{'b': 1}]
    +      }));
    +  // Fails if the field name is not recognized.
    +  assertThrows(function() {
    +    goog.testing.proto2.fromObject(proto2.TestAllTypes, {'unknown': 1});
    +  });
    +  // Fails if the value type is wrong in the JSON object.
    +  assertThrows(function() {
    +    goog.testing.proto2.fromObject(proto2.TestAllTypes,
    +        {'optional_int32': '1'});
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/pseudorandom.js b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom.js
    new file mode 100644
    index 0000000000..fb57a3ac2f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom.js
    @@ -0,0 +1,180 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview PseudoRandom provides a mechanism for generating deterministic
    + * psuedo random numbers based on a seed. Based on the Park-Miller algorithm.
    + * See http://dx.doi.org/10.1145%2F63039.63042 for details.
    + *
    + */
    +
    +goog.provide('goog.testing.PseudoRandom');
    +
    +goog.require('goog.Disposable');
    +
    +
    +
    +/**
    + * Class for unit testing code that uses Math.random. Generates deterministic
    + * random numbers.
    + *
    + * @param {number=} opt_seed The seed to use.
    + * @param {boolean=} opt_install Whether to install the PseudoRandom at
    + *     construction time.
    + * @extends {goog.Disposable}
    + * @constructor
    + * @final
    + */
    +goog.testing.PseudoRandom = function(opt_seed, opt_install) {
    +  goog.Disposable.call(this);
    +
    +  if (!goog.isDef(opt_seed)) {
    +    opt_seed = goog.testing.PseudoRandom.seedUniquifier_++ + goog.now();
    +  }
    +  this.seed(opt_seed);
    +
    +  if (opt_install) {
    +    this.install();
    +  }
    +};
    +goog.inherits(goog.testing.PseudoRandom, goog.Disposable);
    +
    +
    +/**
    + * Helps create a unique seed.
    + * @type {number}
    + * @private
    + */
    +goog.testing.PseudoRandom.seedUniquifier_ = 0;
    +
    +
    +/**
    + * Constant used as part of the algorithm.
    + * @type {number}
    + */
    +goog.testing.PseudoRandom.A = 48271;
    +
    +
    +/**
    + * Constant used as part of the algorithm. 2^31 - 1.
    + * @type {number}
    + */
    +goog.testing.PseudoRandom.M = 2147483647;
    +
    +
    +/**
    + * Constant used as part of the algorithm. It is equal to M / A.
    + * @type {number}
    + */
    +goog.testing.PseudoRandom.Q = 44488;
    +
    +
    +/**
    + * Constant used as part of the algorithm. It is equal to M % A.
    + * @type {number}
    + */
    +goog.testing.PseudoRandom.R = 3399;
    +
    +
    +/**
    + * Constant used as part of the algorithm to get values from range [0, 1).
    + * @type {number}
    + */
    +goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE =
    +    1.0 / (goog.testing.PseudoRandom.M - 1);
    +
    +
    +/**
    + * The seed of the random sequence and also the next returned value (before
    + * normalization). Must be between 1 and M - 1 (inclusive).
    + * @type {number}
    + * @private
    + */
    +goog.testing.PseudoRandom.prototype.seed_ = 1;
    +
    +
    +/**
    + * Whether this PseudoRandom has been installed.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.PseudoRandom.prototype.installed_;
    +
    +
    +/**
    + * The original Math.random function.
    + * @type {function(): number}
    + * @private
    + */
    +goog.testing.PseudoRandom.prototype.mathRandom_;
    +
    +
    +/**
    + * Installs this PseudoRandom as the system number generator.
    + */
    +goog.testing.PseudoRandom.prototype.install = function() {
    +  if (!this.installed_) {
    +    this.mathRandom_ = Math.random;
    +    Math.random = goog.bind(this.random, this);
    +    this.installed_ = true;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.testing.PseudoRandom.prototype.disposeInternal = function() {
    +  goog.testing.PseudoRandom.superClass_.disposeInternal.call(this);
    +  this.uninstall();
    +};
    +
    +
    +/**
    + * Uninstalls the PseudoRandom.
    + */
    +goog.testing.PseudoRandom.prototype.uninstall = function() {
    +  if (this.installed_) {
    +    Math.random = this.mathRandom_;
    +    this.installed_ = false;
    +  }
    +};
    +
    +
    +/**
    + * Seed the generator.
    + *
    + * @param {number=} seed The seed to use.
    + */
    +goog.testing.PseudoRandom.prototype.seed = function(seed) {
    +  this.seed_ = seed % (goog.testing.PseudoRandom.M - 1);
    +  if (this.seed_ <= 0) {
    +    this.seed_ += goog.testing.PseudoRandom.M - 1;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The next number in the sequence.
    + */
    +goog.testing.PseudoRandom.prototype.random = function() {
    +  var hi = Math.floor(this.seed_ / goog.testing.PseudoRandom.Q);
    +  var lo = this.seed_ % goog.testing.PseudoRandom.Q;
    +  var test = goog.testing.PseudoRandom.A * lo -
    +             goog.testing.PseudoRandom.R * hi;
    +  if (test > 0) {
    +    this.seed_ = test;
    +  } else {
    +    this.seed_ = test + goog.testing.PseudoRandom.M;
    +  }
    +  return (this.seed_ - 1) * goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.html b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.html
    new file mode 100644
    index 0000000000..8fff6ca6db
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.PseudoRandom
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.PseudoRandomTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.js b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.js
    new file mode 100644
    index 0000000000..1d918f1fdf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/pseudorandom_test.js
    @@ -0,0 +1,94 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.PseudoRandomTest');
    +goog.setTestOnly('goog.testing.PseudoRandomTest');
    +
    +goog.require('goog.testing.PseudoRandom');
    +goog.require('goog.testing.jsunit');
    +
    +function runFairnessTest(sides, rolls, chiSquareLimit) {
    +  // Initialize the count table for dice rolls.
    +  var counts = [];
    +  for (var i = 0; i < sides; ++i) {
    +    counts[i] = 0;
    +  }
    +  // Roll the dice many times and count the results.
    +  for (var i = 0; i < rolls; ++i) {
    +    ++counts[Math.floor(Math.random() * sides)];
    +  }
    +  // If the dice is fair, we expect a uniform distribution.
    +  var expected = rolls / sides;
    +  // Pearson's chi-square test for a distribution fit.
    +  var chiSquare = 0;
    +  for (var i = 0; i < sides; ++i) {
    +    chiSquare += (counts[i] - expected) * (counts[i] - expected) / expected;
    +  }
    +  assert('Chi-square test for a distribution fit failed',
    +      chiSquare < chiSquareLimit);
    +}
    +
    +function testInstall() {
    +  var random = new goog.testing.PseudoRandom();
    +  var originalRandom = Math.random;
    +
    +  assertFalse(!!random.installed_);
    +
    +  random.install();
    +  assertTrue(random.installed_);
    +  assertNotEquals(Math.random, originalRandom);
    +
    +  random.uninstall();
    +  assertFalse(random.installed_);
    +  assertEquals(originalRandom, Math.random);
    +}
    +
    +function testBounds() {
    +  var random = new goog.testing.PseudoRandom();
    +  random.install();
    +
    +  for (var i = 0; i < 100000; ++i) {
    +    var value = Math.random();
    +    assert('Random value out of bounds', value >= 0 && value < 1);
    +  }
    +
    +  random.uninstall();
    +}
    +
    +function testFairness() {
    +  var random = new goog.testing.PseudoRandom(0, true);
    +
    +  // Chi-square statistics: p-value = 0.05, df = 5, limit = 11.07.
    +  runFairnessTest(6, 100000, 11.07);
    +  // Chi-square statistics: p-value = 0.05, df = 100, limit = 124.34.
    +  runFairnessTest(101, 100000, 124.34);
    +
    +  random.uninstall();
    +}
    +
    +function testReseed() {
    +  var random = new goog.testing.PseudoRandom(100, true);
    +
    +  var sequence = [];
    +  for (var i = 0; i < 64000; ++i) {
    +    sequence.push(Math.random());
    +  }
    +
    +  random.seed(100);
    +  for (var i = 0; i < sequence.length; ++i) {
    +    assertEquals(sequence[i], Math.random());
    +  }
    +
    +  random.uninstall();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/recordfunction.js b/mapbox-gl/closure-library/closure/goog/testing/recordfunction.js
    new file mode 100644
    index 0000000000..fc81c1d8db
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/recordfunction.js
    @@ -0,0 +1,215 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Helper class for recording the calls of a function.
    + *
    + * Example:
    + * <pre>
    + * var stubs = new goog.testing.PropertyReplacer();
    + *
    + * function tearDown() {
    + *   stubs.reset();
    + * }
    + *
    + * function testShuffle() {
    + *   stubs.set(Math, 'random', goog.testing.recordFunction(Math.random));
    + *   var arr = shuffle([1, 2, 3, 4, 5]);
    + *   assertSameElements([1, 2, 3, 4, 5], arr);
    + *   assertEquals(4, Math.random.getCallCount());
    + * }
    + *
    + * function testOpenDialog() {
    + *   stubs.set(goog.ui, 'Dialog',
    + *       goog.testing.recordConstructor(goog.ui.Dialog));
    + *   openConfirmDialog();
    + *   var lastDialogInstance = goog.ui.Dialog.getLastCall().getThis();
    + *   assertEquals('confirm', lastDialogInstance.getTitle());
    + * }
    + * </pre>
    + *
    + */
    +
    +goog.provide('goog.testing.FunctionCall');
    +goog.provide('goog.testing.recordConstructor');
    +goog.provide('goog.testing.recordFunction');
    +
    +goog.require('goog.testing.asserts');
    +
    +
    +/**
    + * Wraps the function into another one which calls the inner function and
    + * records its calls. The recorded function will have 3 static methods:
    + * {@code getCallCount}, {@code getCalls} and {@code getLastCall} but won't
    + * inherit the original function's prototype and static fields.
    + *
    + * @param {!Function=} opt_f The function to wrap and record. Defaults to
    + *     {@link goog.nullFunction}.
    + * @return {!Function} The wrapped function.
    + */
    +goog.testing.recordFunction = function(opt_f) {
    +  var f = opt_f || goog.nullFunction;
    +  var calls = [];
    +
    +  function recordedFunction() {
    +    try {
    +      var ret = f.apply(this, arguments);
    +      calls.push(new goog.testing.FunctionCall(f, this, arguments, ret, null));
    +      return ret;
    +    } catch (err) {
    +      calls.push(new goog.testing.FunctionCall(f, this, arguments, undefined,
    +          err));
    +      throw err;
    +    }
    +  }
    +
    +  /**
    +   * @return {number} Total number of calls.
    +   */
    +  recordedFunction.getCallCount = function() {
    +    return calls.length;
    +  };
    +
    +  /**
    +   * Asserts that the function was called {@code expected} times.
    +   * @param {number} expected The expected number of calls.
    +   */
    +  recordedFunction.assertCallCount = function(expected) {
    +    var actual = calls.length;
    +    assertEquals(
    +        'Expected ' + expected + ' call(s), but was ' + actual + '.',
    +        expected, actual);
    +  };
    +
    +  /**
    +   * @return {!Array<!goog.testing.FunctionCall>} All calls of the recorded
    +   *     function.
    +   */
    +  recordedFunction.getCalls = function() {
    +    return calls;
    +  };
    +
    +
    +  /**
    +   * @return {goog.testing.FunctionCall} Last call of the recorded function or
    +   *     null if it hasn't been called.
    +   */
    +  recordedFunction.getLastCall = function() {
    +    return calls[calls.length - 1] || null;
    +  };
    +
    +  /**
    +   * Returns and removes the last call of the recorded function.
    +   * @return {goog.testing.FunctionCall} Last call of the recorded function or
    +   *     null if it hasn't been called.
    +   */
    +  recordedFunction.popLastCall = function() {
    +    return calls.pop() || null;
    +  };
    +
    +  /**
    +   * Resets the recorded function and removes all calls.
    +   */
    +  recordedFunction.reset = function() {
    +    calls.length = 0;
    +  };
    +
    +  return recordedFunction;
    +};
    +
    +
    +/**
    + * Same as {@link goog.testing.recordFunction} but the recorded function will
    + * have the same prototype and static fields as the original one. It can be
    + * used with constructors.
    + *
    + * @param {!Function} ctor The function to wrap and record.
    + * @return {!Function} The wrapped function.
    + */
    +goog.testing.recordConstructor = function(ctor) {
    +  var recordedConstructor = goog.testing.recordFunction(ctor);
    +  recordedConstructor.prototype = ctor.prototype;
    +  goog.mixin(recordedConstructor, ctor);
    +  return recordedConstructor;
    +};
    +
    +
    +
    +/**
    + * Struct for a single function call.
    + * @param {!Function} func The called function.
    + * @param {!Object} thisContext {@code this} context of called function.
    + * @param {!Arguments} args Arguments of the called function.
    + * @param {*} ret Return value of the function or undefined in case of error.
    + * @param {*} error The error thrown by the function or null if none.
    + * @constructor
    + */
    +goog.testing.FunctionCall = function(func, thisContext, args, ret, error) {
    +  this.function_ = func;
    +  this.thisContext_ = thisContext;
    +  this.arguments_ = Array.prototype.slice.call(args);
    +  this.returnValue_ = ret;
    +  this.error_ = error;
    +};
    +
    +
    +/**
    + * @return {!Function} The called function.
    + */
    +goog.testing.FunctionCall.prototype.getFunction = function() {
    +  return this.function_;
    +};
    +
    +
    +/**
    + * @return {!Object} {@code this} context of called function. It is the same as
    + *     the created object if the function is a constructor.
    + */
    +goog.testing.FunctionCall.prototype.getThis = function() {
    +  return this.thisContext_;
    +};
    +
    +
    +/**
    + * @return {!Array<?>} Arguments of the called function.
    + */
    +goog.testing.FunctionCall.prototype.getArguments = function() {
    +  return this.arguments_;
    +};
    +
    +
    +/**
    + * Returns the nth argument of the called function.
    + * @param {number} index 0-based index of the argument.
    + * @return {*} The argument value or undefined if there is no such argument.
    + */
    +goog.testing.FunctionCall.prototype.getArgument = function(index) {
    +  return this.arguments_[index];
    +};
    +
    +
    +/**
    + * @return {*} Return value of the function or undefined in case of error.
    + */
    +goog.testing.FunctionCall.prototype.getReturnValue = function() {
    +  return this.returnValue_;
    +};
    +
    +
    +/**
    + * @return {*} The error thrown by the function or null if none.
    + */
    +goog.testing.FunctionCall.prototype.getError = function() {
    +  return this.error_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.html b/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.html
    new file mode 100644
    index 0000000000..83aaee8c2b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.recordFunction
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.recordFunctionTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.js b/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.js
    new file mode 100644
    index 0000000000..59ed731810
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/recordfunction_test.js
    @@ -0,0 +1,175 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.recordFunctionTest');
    +goog.setTestOnly('goog.testing.recordFunctionTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordConstructor');
    +goog.require('goog.testing.recordFunction');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testNoCalls() {
    +  var f = goog.testing.recordFunction(goog.functions.identity);
    +  assertEquals('call count', 0, f.getCallCount());
    +  assertNull('last call', f.getLastCall());
    +  assertArrayEquals('all calls', [], f.getCalls());
    +}
    +
    +function testWithoutArguments() {
    +  var f = goog.testing.recordFunction();
    +  assertUndefined('f(1)', f(1));
    +  assertEquals('call count', 1, f.getCallCount());
    +  var lastCall = f.getLastCall();
    +  assertEquals('original function', goog.nullFunction, lastCall.getFunction());
    +  assertEquals('this context', window, lastCall.getThis());
    +  assertArrayEquals('arguments', [1], lastCall.getArguments());
    +  assertEquals('arguments[0]', 1, lastCall.getArgument(0));
    +  assertUndefined('arguments[1]', lastCall.getArgument(1));
    +  assertUndefined('return value', lastCall.getReturnValue());
    +}
    +
    +function testWithIdentityFunction() {
    +  var f = goog.testing.recordFunction(goog.functions.identity);
    +  var dummyThis = {};
    +  assertEquals('f(1)', 1, f(1));
    +  assertEquals('f.call(dummyThis, 2)', 2, f.call(dummyThis, 2));
    +
    +  var calls = f.getCalls();
    +  var firstCall = calls[0];
    +  var lastCall = f.getLastCall();
    +  assertEquals('call count', 2, f.getCallCount());
    +  assertEquals('last call', calls[1], lastCall);
    +  assertEquals('original function', goog.functions.identity,
    +      lastCall.getFunction());
    +  assertEquals('this context of first call', window, firstCall.getThis());
    +  assertEquals('this context of last call', dummyThis, lastCall.getThis());
    +  assertArrayEquals('arguments of the first call', [1],
    +      firstCall.getArguments());
    +  assertArrayEquals('arguments of the last call', [2], lastCall.getArguments());
    +  assertEquals('return value of the first call', 1, firstCall.getReturnValue());
    +  assertEquals('return value of the last call', 2, lastCall.getReturnValue());
    +  assertNull('error thrown by the first call', firstCall.getError());
    +  assertNull('error thrown by the last call', lastCall.getError());
    +}
    +
    +function testWithErrorFunction() {
    +  var f = goog.testing.recordFunction(goog.functions.error('error'));
    +
    +  var error = assertThrows('f(1) should throw an error', function() {
    +    f(1);
    +  });
    +  assertEquals('error message', 'error', error.message);
    +  assertEquals('call count', 1, f.getCallCount());
    +  var lastCall = f.getLastCall();
    +  assertEquals('this context', window, lastCall.getThis());
    +  assertArrayEquals('arguments', [1], lastCall.getArguments());
    +  assertUndefined('return value', lastCall.getReturnValue());
    +  assertEquals('recorded error message', 'error', lastCall.getError().message);
    +}
    +
    +function testWithClass() {
    +  var ns = {};
    +  /** @constructor */
    +  ns.TestClass = function(num) {
    +    this.setX(ns.TestClass.identity(1) + num);
    +  };
    +  ns.TestClass.prototype.setX = function(x) {
    +    this.x = x;
    +  };
    +  ns.TestClass.identity = function(x) {
    +    return x;
    +  };
    +  var originalNsTestClass = ns.TestClass;
    +
    +  stubs.set(ns, 'TestClass', goog.testing.recordConstructor(ns.TestClass));
    +  stubs.set(ns.TestClass.prototype, 'setX',
    +      goog.testing.recordFunction(ns.TestClass.prototype.setX));
    +  stubs.set(ns.TestClass, 'identity',
    +      goog.testing.recordFunction(ns.TestClass.identity));
    +
    +  var obj = new ns.TestClass(2);
    +  assertEquals('constructor is called once', 1, ns.TestClass.getCallCount());
    +  var lastConstructorCall = ns.TestClass.getLastCall();
    +  assertArrayEquals('... with argument 2', [2],
    +      lastConstructorCall.getArguments());
    +  assertEquals('the created object', obj, lastConstructorCall.getThis());
    +  assertEquals('type of the created object', originalNsTestClass,
    +      obj.constructor);
    +
    +  assertEquals('setX is called once', 1, obj.setX.getCallCount());
    +  assertArrayEquals('... with argument 3', [3],
    +      obj.setX.getLastCall().getArguments());
    +  assertEquals('The x field is properly set', 3, obj.x);
    +
    +  assertEquals('identity is called once', 1,
    +      ns.TestClass.identity.getCallCount());
    +  assertArrayEquals('... with argument 1', [1],
    +      ns.TestClass.identity.getLastCall().getArguments());
    +}
    +
    +function testPopLastCall() {
    +  var f = goog.testing.recordFunction();
    +  f(0);
    +  f(1);
    +
    +  var firstCall = f.getCalls()[0];
    +  var lastCall = f.getCalls()[1];
    +  assertEquals('return value of popLastCall', lastCall, f.popLastCall());
    +  assertArrayEquals('1 call remains', [firstCall], f.getCalls());
    +  assertEquals('next return value of popLastCall', firstCall, f.popLastCall());
    +  assertArrayEquals('0 calls remain', [], f.getCalls());
    +  assertNull('no more calls to pop', f.popLastCall());
    +}
    +
    +function testReset() {
    +  var f = goog.testing.recordFunction();
    +  f(0);
    +  f(1);
    +
    +  assertEquals('Should be two calls.', 2, f.getCallCount());
    +
    +  f.reset();
    +
    +  assertEquals('Call count should reset.', 0, f.getCallCount());
    +}
    +
    +function testAssertCallCount() {
    +  var f = goog.testing.recordFunction(goog.functions.identity);
    +
    +  f.assertCallCount(0);
    +
    +  f('Poodles');
    +  f.assertCallCount(1);
    +
    +  f('Hopscotch');
    +  f.assertCallCount(2);
    +
    +  f.reset();
    +  f.assertCallCount(0);
    +
    +  f('Bedazzler');
    +  f.assertCallCount(1);
    +
    +  assertThrows(function() {
    +    f.assertCallCount(11);
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase.js b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase.js
    new file mode 100644
    index 0000000000..63980dac1d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase.js
    @@ -0,0 +1,124 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility for sharding tests.
    + *
    + * Usage instructions:
    + * <ol>
    + *   <li>Instead of writing your large test in foo_test.html, write it in
    + * foo_test_template.html</li>
    + *   <li>Add a call to {@code goog.testing.ShardingTestCase.shardByFileName()}
    + * near the top of your test, before any test cases or setup methods.</li>
    + *   <li>Symlink foo_test_template.html into different sharded test files
    + * named foo_1of4_test.html, foo_2of4_test.html, etc, using `ln -s`.</li>
    + *   <li>Add the symlinks as foo_1of4_test.html.
    + *       In perforce, run the command `g4 add foo_1of4_test.html` followed
    + * by `g4 reopen -t symlink foo_1of4_test.html` so that perforce treats the file
    + * as a symlink
    + *   </li>
    + * </ol>
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.testing.ShardingTestCase');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.testing.TestCase');
    +
    +
    +
    +/**
    + * A test case that runs tests in per-file shards.
    + * @param {number} shardIndex Shard index for this page,
    + *     <strong>1-indexed</strong>.
    + * @param {number} numShards Number of shards to split up test cases into.
    + * @extends {goog.testing.TestCase}
    + * @constructor
    + * @final
    + */
    +goog.testing.ShardingTestCase = function(shardIndex, numShards, opt_name) {
    +  goog.testing.ShardingTestCase.base(this, 'constructor', opt_name);
    +
    +  goog.asserts.assert(shardIndex > 0, 'Shard index should be positive');
    +  goog.asserts.assert(numShards > 0, 'Number of shards should be positive');
    +  goog.asserts.assert(shardIndex <= numShards,
    +      'Shard index out of bounds');
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.shardIndex_ = shardIndex;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.numShards_ = numShards;
    +};
    +goog.inherits(goog.testing.ShardingTestCase, goog.testing.TestCase);
    +
    +
    +/**
    + * Whether we've actually partitioned the tests yet. We may execute twice
    + * ('Run again without reloading') without failing.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.ShardingTestCase.prototype.sharded_ = false;
    +
    +
    +/**
    + * Installs a runTests global function that goog.testing.JsUnit will use to
    + * run tests, which will run a single shard of the tests present on the page.
    + * @override
    + */
    +goog.testing.ShardingTestCase.prototype.runTests = function() {
    +  if (!this.sharded_) {
    +    var numTests = this.getCount();
    +    goog.asserts.assert(numTests >= this.numShards_,
    +        'Must have at least as many tests as shards!');
    +    var shardSize = Math.ceil(numTests / this.numShards_);
    +    var startIndex = (this.shardIndex_ - 1) * shardSize;
    +    var endIndex = startIndex + shardSize;
    +    goog.asserts.assert(this.order == goog.testing.TestCase.Order.SORTED,
    +        'Only SORTED order is allowed for sharded tests');
    +    this.setTests(this.getTests().slice(startIndex, endIndex));
    +    this.sharded_ = true;
    +  }
    +
    +  // Call original runTests method to execute the tests.
    +  goog.testing.ShardingTestCase.base(this, 'runTests');
    +};
    +
    +
    +/**
    + * Shards tests based on the test filename. Assumes that the filename is
    + * formatted like 'foo_1of5_test.html'.
    + * @param {string=} opt_name A descriptive name for the test case.
    + */
    +goog.testing.ShardingTestCase.shardByFileName = function(opt_name) {
    +  var path = window.location.pathname;
    +  var shardMatch = path.match(/_(\d+)of(\d+)_test\.html/);
    +  goog.asserts.assert(shardMatch,
    +      'Filename must be of the form "foo_1of5_test.html"');
    +  var shardIndex = parseInt(shardMatch[1], 10);
    +  var numShards = parseInt(shardMatch[2], 10);
    +
    +  var testCase = new goog.testing.ShardingTestCase(
    +      shardIndex, numShards, opt_name);
    +  goog.testing.TestCase.initializeTestRunner(testCase);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.html b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.html
    new file mode 100644
    index 0000000000..63e60e08e0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.asserts
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.ShardingTestCaseTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.js b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.js
    new file mode 100644
    index 0000000000..cbe3c70856
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/shardingtestcase_test.js
    @@ -0,0 +1,31 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.ShardingTestCaseTest');
    +goog.setTestOnly('goog.testing.ShardingTestCaseTest');
    +
    +goog.require('goog.testing.ShardingTestCase');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +
    +goog.testing.TestCase.initializeTestRunner(
    +    new goog.testing.ShardingTestCase(1, 2));
    +
    +function testA() {
    +}
    +
    +function testB() {
    +  fail('testB should not be in this shard');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/singleton.js b/mapbox-gl/closure-library/closure/goog/testing/singleton.js
    new file mode 100644
    index 0000000000..03f104355d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/singleton.js
    @@ -0,0 +1,46 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This module simplifies testing code which uses stateful
    + * singletons. {@code goog.testing.singleton.reset} resets all instances, so
    + * next time when {@code getInstance} is called, a new instance is created.
    + * It's recommended to reset the singletons in {@code tearDown} to prevent
    + * interference between subsequent tests.
    + *
    + * The {@code goog.testing.singleton} functions expect that the goog.DEBUG flag
    + * is enabled, and the tests are either uncompiled or compiled without renaming.
    + *
    + */
    +
    +goog.provide('goog.testing.singleton');
    +
    +
    +/**
    + * Deletes all singleton instances, so {@code getInstance} will return a new
    + * instance on next call.
    + */
    +goog.testing.singleton.reset = function() {
    +  var singletons = goog.getObjectByName('goog.instantiatedSingletons_');
    +  var ctor;
    +  while (ctor = singletons.pop()) {
    +    delete ctor.instance_;
    +  }
    +};
    +
    +
    +/**
    + * @deprecated Please use {@code goog.addSingletonGetter}.
    + */
    +goog.testing.singleton.addSingletonGetter = goog.addSingletonGetter;
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/singleton_test.html b/mapbox-gl/closure-library/closure/goog/testing/singleton_test.html
    new file mode 100644
    index 0000000000..ca9422670a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/singleton_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.singleton
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.singletonTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/singleton_test.js b/mapbox-gl/closure-library/closure/goog/testing/singleton_test.js
    new file mode 100644
    index 0000000000..5ec19b2143
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/singleton_test.js
    @@ -0,0 +1,33 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.singletonTest');
    +goog.setTestOnly('goog.testing.singletonTest');
    +
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.singleton');
    +
    +function testGetInstance() {
    +  function SingletonClass() {}
    +  goog.addSingletonGetter(SingletonClass);
    +
    +  var s1 = SingletonClass.getInstance();
    +  var s2 = SingletonClass.getInstance();
    +  assertEquals('second getInstance call returns the same instance', s1, s2);
    +
    +  goog.testing.singleton.reset();
    +  var s3 = SingletonClass.getInstance();
    +  assertNotEquals('getInstance returns a new instance after reset', s1, s3);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/stacktrace.js b/mapbox-gl/closure-library/closure/goog/testing/stacktrace.js
    new file mode 100644
    index 0000000000..9ece019c0d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/stacktrace.js
    @@ -0,0 +1,594 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tools for parsing and pretty printing error stack traces.
    + *
    + */
    +
    +goog.provide('goog.testing.stacktrace');
    +goog.provide('goog.testing.stacktrace.Frame');
    +
    +
    +
    +/**
    + * Class representing one stack frame.
    + * @param {string} context Context object, empty in case of global functions or
    + *     if the browser doesn't provide this information.
    + * @param {string} name Function name, empty in case of anonymous functions.
    + * @param {string} alias Alias of the function if available. For example the
    + *     function name will be 'c' and the alias will be 'b' if the function is
    + *     defined as <code>a.b = function c() {};</code>.
    + * @param {string} args Arguments of the function in parentheses if available.
    + * @param {string} path File path or URL including line number and optionally
    + *     column number separated by colons.
    + * @constructor
    + * @final
    + */
    +goog.testing.stacktrace.Frame = function(context, name, alias, args, path) {
    +  this.context_ = context;
    +  this.name_ = name;
    +  this.alias_ = alias;
    +  this.args_ = args;
    +  this.path_ = path;
    +};
    +
    +
    +/**
    + * @return {string} The function name or empty string if the function is
    + *     anonymous and the object field which it's assigned to is unknown.
    + */
    +goog.testing.stacktrace.Frame.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the stack frame contains an anonymous function.
    + */
    +goog.testing.stacktrace.Frame.prototype.isAnonymous = function() {
    +  return !this.name_ || this.context_ == '[object Object]';
    +};
    +
    +
    +/**
    + * Brings one frame of the stack trace into a common format across browsers.
    + * @return {string} Pretty printed stack frame.
    + */
    +goog.testing.stacktrace.Frame.prototype.toCanonicalString = function() {
    +  var htmlEscape = goog.testing.stacktrace.htmlEscape_;
    +  var deobfuscate = goog.testing.stacktrace.maybeDeobfuscateFunctionName_;
    +
    +  var canonical = [
    +    this.context_ ? htmlEscape(this.context_) + '.' : '',
    +    this.name_ ? htmlEscape(deobfuscate(this.name_)) : 'anonymous',
    +    htmlEscape(this.args_),
    +    this.alias_ ? ' [as ' + htmlEscape(deobfuscate(this.alias_)) + ']' : ''
    +  ];
    +
    +  if (this.path_) {
    +    canonical.push(' at ');
    +    canonical.push(htmlEscape(this.path_));
    +  }
    +  return canonical.join('');
    +};
    +
    +
    +/**
    + * Maximum number of steps while the call chain is followed.
    + * @private {number}
    + * @const
    + */
    +goog.testing.stacktrace.MAX_DEPTH_ = 20;
    +
    +
    +/**
    + * Maximum length of a string that can be matched with a RegExp on
    + * Firefox 3x. Exceeding this approximate length will cause string.match
    + * to exceed Firefox's stack quota. This situation can be encountered
    + * when goog.globalEval is invoked with a long argument; such as
    + * when loading a module.
    + * @private {number}
    + * @const
    + */
    +goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_ = 500000;
    +
    +
    +/**
    + * RegExp pattern for JavaScript identifiers. We don't support Unicode
    + * identifiers defined in ECMAScript v3.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.IDENTIFIER_PATTERN_ = '[a-zA-Z_$][\\w$]*';
    +
    +
    +/**
    + * RegExp pattern for function name alias in the V8 stack trace.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.V8_ALIAS_PATTERN_ =
    +    '(?: \\[as (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')\\])?';
    +
    +
    +/**
    + * RegExp pattern for the context of a function call in a V8 stack trace.
    + * Creates an optional submatch for the namespace identifier including the
    + * "new" keyword for constructor calls (e.g. "new foo.Bar").
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.V8_CONTEXT_PATTERN_ =
    +    '(?:((?:new )?(?:\\[object Object\\]|' +
    +    goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
    +    '(?:\\.' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*))\\.)?';
    +
    +
    +/**
    + * RegExp pattern for function names and constructor calls in the V8 stack
    + * trace.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ =
    +    '(?:new )?(?:' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
    +    '|<anonymous>)';
    +
    +
    +/**
    + * RegExp pattern for function call in the V8 stack trace. Creates 3 submatches
    + * with context object (optional), function name and function alias (optional).
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ =
    +    ' ' + goog.testing.stacktrace.V8_CONTEXT_PATTERN_ +
    +    '(' + goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ + ')' +
    +    goog.testing.stacktrace.V8_ALIAS_PATTERN_;
    +
    +
    +/**
    + * RegExp pattern for an URL + position inside the file.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.URL_PATTERN_ =
    +    '((?:http|https|file)://[^\\s)]+|javascript:.*)';
    +
    +
    +/**
    + * RegExp pattern for an URL + line number + column number in V8.
    + * The URL is either in submatch 1 or submatch 2.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.CHROME_URL_PATTERN_ = ' (?:' +
    +    '\\(unknown source\\)' + '|' +
    +    '\\(native\\)' + '|' +
    +    '\\((.+)\\)|(.+))';
    +
    +
    +/**
    + * Regular expression for parsing one stack frame in V8. For more information
    + * on V8 stack frame formats, see
    + * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi.
    + * @private {!RegExp}
    + * @const
    + */
    +goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^    at' +
    +    '(?:' + goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' +
    +    goog.testing.stacktrace.CHROME_URL_PATTERN_ + '$');
    +
    +
    +/**
    + * RegExp pattern for function call in the Firefox stack trace.
    + * Creates 2 submatches with function name (optional) and arguments.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ =
    +    '(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')?' +
    +    '(\\(.*\\))?@';
    +
    +
    +/**
    + * Regular expression for parsing one stack frame in Firefox.
    + * @private {!RegExp}
    + * @const
    + */
    +goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' +
    +    goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ +
    +    '(?::0|' + goog.testing.stacktrace.URL_PATTERN_ + ')$');
    +
    +
    +/**
    + * RegExp pattern for an anonymous function call in an Opera stack frame.
    + * Creates 2 (optional) submatches: the context object and function name.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ =
    +    '<anonymous function(?:\\: ' +
    +    '(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
    +    '(?:\\.' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' +
    +    '(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '))?>';
    +
    +
    +/**
    + * RegExp pattern for a function call in an Opera stack frame.
    + * Creates 4 (optional) submatches: the function name (if not anonymous),
    + * the aliased context object and function name (if anonymous), and the
    + * function call arguments.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ =
    +    '(?:(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')|' +
    +    goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ +
    +    ')(\\(.*\\)))?@';
    +
    +
    +/**
    + * Regular expression for parsing on stack frame in Opera 11.68 - 12.17.
    + * Newer versions of Opera use V8 and stack frames should match against
    + * goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_.
    + * @private {!RegExp}
    + * @const
    + */
    +goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp('^' +
    +    goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ +
    +    goog.testing.stacktrace.URL_PATTERN_ + '?$');
    +
    +
    +/**
    + * Regular expression for finding the function name in its source.
    + * @private {!RegExp}
    + * @const
    + */
    +goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_ = new RegExp(
    +    '^function (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')');
    +
    +
    +/**
    + * RegExp pattern for function call in a IE stack trace. This expression allows
    + * for identifiers like 'Anonymous function', 'eval code', and 'Global code'.
    + * @private {string}
    + * @const
    + */
    +goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ = '(' +
    +    goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)';
    +
    +
    +/**
    + * Regular expression for parsing a stack frame in IE.
    + * @private {!RegExp}
    + * @const
    + */
    +goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_ = new RegExp('^   at ' +
    +    goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ +
    +    '\\s*\\((eval code:[^)]*|' + goog.testing.stacktrace.URL_PATTERN_ +
    +    ')\\)?$');
    +
    +
    +/**
    + * Creates a stack trace by following the call chain. Based on
    + * {@link goog.debug.getStacktrace}.
    + * @return {!Array<!goog.testing.stacktrace.Frame>} Stack frames.
    + * @private
    + * @suppress {es5Strict}
    + */
    +goog.testing.stacktrace.followCallChain_ = function() {
    +  var frames = [];
    +  var fn = arguments.callee.caller;
    +  var depth = 0;
    +
    +  while (fn && depth < goog.testing.stacktrace.MAX_DEPTH_) {
    +    var fnString = Function.prototype.toString.call(fn);
    +    var match = fnString.match(goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_);
    +    var functionName = match ? match[1] : '';
    +
    +    var argsBuilder = ['('];
    +    if (fn.arguments) {
    +      for (var i = 0; i < fn.arguments.length; i++) {
    +        var arg = fn.arguments[i];
    +        if (i > 0) {
    +          argsBuilder.push(', ');
    +        }
    +        if (goog.isString(arg)) {
    +          argsBuilder.push('"', arg, '"');
    +        } else {
    +          // Some args are mocks, and we don't want to fail from them not having
    +          // expected a call to toString, so instead insert a static string.
    +          if (arg && arg['$replay']) {
    +            argsBuilder.push('goog.testing.Mock');
    +          } else {
    +            argsBuilder.push(String(arg));
    +          }
    +        }
    +      }
    +    } else {
    +      // Opera 10 doesn't know the arguments of native functions.
    +      argsBuilder.push('unknown');
    +    }
    +    argsBuilder.push(')');
    +    var args = argsBuilder.join('');
    +
    +    frames.push(new goog.testing.stacktrace.Frame('', functionName, '', args,
    +        ''));
    +
    +    /** @preserveTry */
    +    try {
    +      fn = fn.caller;
    +    } catch (e) {
    +      break;
    +    }
    +    depth++;
    +  }
    +
    +  return frames;
    +};
    +
    +
    +/**
    + * Parses one stack frame.
    + * @param {string} frameStr The stack frame as string.
    + * @return {goog.testing.stacktrace.Frame} Stack frame object or null if the
    + *     parsing failed.
    + * @private
    + */
    +goog.testing.stacktrace.parseStackFrame_ = function(frameStr) {
    +  // This match includes newer versions of Opera (15+).
    +  var m = frameStr.match(goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_);
    +  if (m) {
    +    return new goog.testing.stacktrace.Frame(m[1] || '', m[2] || '', m[3] || '',
    +        '', m[4] || m[5] || m[6] || '');
    +  }
    +
    +  if (frameStr.length >
    +      goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) {
    +    return goog.testing.stacktrace.parseLongFirefoxFrame_(frameStr);
    +  }
    +
    +  m = frameStr.match(goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_);
    +  if (m) {
    +    return new goog.testing.stacktrace.Frame('', m[1] || '', '', m[2] || '',
    +        m[3] || '');
    +  }
    +
    +  // Match against Presto Opera 11.68 - 12.17.
    +  m = frameStr.match(goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_);
    +  if (m) {
    +    return new goog.testing.stacktrace.Frame(m[2] || '', m[1] || m[3] || '',
    +        '', m[4] || '', m[5] || '');
    +  }
    +
    +  m = frameStr.match(goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_);
    +  if (m) {
    +    return new goog.testing.stacktrace.Frame('', m[1] || '', '', '',
    +        m[2] || '');
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Parses a long firefox stack frame.
    + * @param {string} frameStr The stack frame as string.
    + * @return {!goog.testing.stacktrace.Frame} Stack frame object.
    + * @private
    + */
    +goog.testing.stacktrace.parseLongFirefoxFrame_ = function(frameStr) {
    +  var firstParen = frameStr.indexOf('(');
    +  var lastAmpersand = frameStr.lastIndexOf('@');
    +  var lastColon = frameStr.lastIndexOf(':');
    +  var functionName = '';
    +  if ((firstParen >= 0) && (firstParen < lastAmpersand)) {
    +    functionName = frameStr.substring(0, firstParen);
    +  }
    +  var loc = '';
    +  if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) {
    +    loc = frameStr.substring(lastAmpersand + 1);
    +  }
    +  var args = '';
    +  if ((firstParen >= 0 && lastAmpersand > 0) &&
    +      (firstParen < lastAmpersand)) {
    +    args = frameStr.substring(firstParen, lastAmpersand);
    +  }
    +  return new goog.testing.stacktrace.Frame('', functionName, '', args, loc);
    +};
    +
    +
    +/**
    + * Function to deobfuscate function names.
    + * @type {function(string): string}
    + * @private
    + */
    +goog.testing.stacktrace.deobfuscateFunctionName_;
    +
    +
    +/**
    + * Sets function to deobfuscate function names.
    + * @param {function(string): string} fn function to deobfuscate function names.
    + */
    +goog.testing.stacktrace.setDeobfuscateFunctionName = function(fn) {
    +  goog.testing.stacktrace.deobfuscateFunctionName_ = fn;
    +};
    +
    +
    +/**
    + * Deobfuscates a compiled function name with the function passed to
    + * {@link #setDeobfuscateFunctionName}. Returns the original function name if
    + * the deobfuscator hasn't been set.
    + * @param {string} name The function name to deobfuscate.
    + * @return {string} The deobfuscated function name.
    + * @private
    + */
    +goog.testing.stacktrace.maybeDeobfuscateFunctionName_ = function(name) {
    +  return goog.testing.stacktrace.deobfuscateFunctionName_ ?
    +      goog.testing.stacktrace.deobfuscateFunctionName_(name) : name;
    +};
    +
    +
    +/**
    + * Escapes the special character in HTML.
    + * @param {string} text Plain text.
    + * @return {string} Escaped text.
    + * @private
    + */
    +goog.testing.stacktrace.htmlEscape_ = function(text) {
    +  return text.replace(/&/g, '&amp;').
    +             replace(/</g, '&lt;').
    +             replace(/>/g, '&gt;').
    +             replace(/"/g, '&quot;');
    +};
    +
    +
    +/**
    + * Converts the stack frames into canonical format. Chops the beginning and the
    + * end of it which come from the testing environment, not from the test itself.
    + * @param {!Array<goog.testing.stacktrace.Frame>} frames The frames.
    + * @return {string} Canonical, pretty printed stack trace.
    + * @private
    + */
    +goog.testing.stacktrace.framesToString_ = function(frames) {
    +  // Removes the anonymous calls from the end of the stack trace (they come
    +  // from testrunner.js, testcase.js and asserts.js), so the stack trace will
    +  // end with the test... method.
    +  var lastIndex = frames.length - 1;
    +  while (frames[lastIndex] && frames[lastIndex].isAnonymous()) {
    +    lastIndex--;
    +  }
    +
    +  // Removes the beginning of the stack trace until the call of the private
    +  // _assert function (inclusive), so the stack trace will begin with a public
    +  // asserter. Does nothing if _assert is not present in the stack trace.
    +  var privateAssertIndex = -1;
    +  for (var i = 0; i < frames.length; i++) {
    +    if (frames[i] && frames[i].getName() == '_assert') {
    +      privateAssertIndex = i;
    +      break;
    +    }
    +  }
    +
    +  var canonical = [];
    +  for (var i = privateAssertIndex + 1; i <= lastIndex; i++) {
    +    canonical.push('> ');
    +    if (frames[i]) {
    +      canonical.push(frames[i].toCanonicalString());
    +    } else {
    +      canonical.push('(unknown)');
    +    }
    +    canonical.push('\n');
    +  }
    +  return canonical.join('');
    +};
    +
    +
    +/**
    + * Parses the browser's native stack trace.
    + * @param {string} stack Stack trace.
    + * @return {!Array<goog.testing.stacktrace.Frame>} Stack frames. The
    + *     unrecognized frames will be nulled out.
    + * @private
    + */
    +goog.testing.stacktrace.parse_ = function(stack) {
    +  var lines = stack.replace(/\s*$/, '').split('\n');
    +  var frames = [];
    +  for (var i = 0; i < lines.length; i++) {
    +    frames.push(goog.testing.stacktrace.parseStackFrame_(lines[i]));
    +  }
    +  return frames;
    +};
    +
    +
    +/**
    + * Brings the stack trace into a common format across browsers.
    + * @param {string} stack Browser-specific stack trace.
    + * @return {string} Same stack trace in common format.
    + */
    +goog.testing.stacktrace.canonicalize = function(stack) {
    +  var frames = goog.testing.stacktrace.parse_(stack);
    +  return goog.testing.stacktrace.framesToString_(frames);
    +};
    +
    +
    +/**
    + * Returns the native stack trace.
    + * @return {string|!Array<!CallSite>}
    + * @private
    + */
    +goog.testing.stacktrace.getNativeStack_ = function() {
    +  var tmpError = new Error();
    +  if (tmpError.stack) {
    +    return tmpError.stack;
    +  }
    +
    +  // IE10 will only create a stack trace when the Error is thrown.
    +  // We use null.x() to throw an exception because the closure compiler may
    +  // replace "throw" with a function call in an attempt to minimize the binary
    +  // size, which in turn has the side effect of adding an unwanted stack frame.
    +  try {
    +    null.x();
    +  } catch (e) {
    +    return e.stack;
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Gets the native stack trace if available otherwise follows the call chain.
    + * @return {string} The stack trace in canonical format.
    + */
    +goog.testing.stacktrace.get = function() {
    +  var stack = goog.testing.stacktrace.getNativeStack_();
    +  var frames;
    +  if (!stack) {
    +    frames = goog.testing.stacktrace.followCallChain_();
    +  } else if (goog.isArray(stack)) {
    +    frames = goog.testing.stacktrace.callSitesToFrames_(stack);
    +  } else {
    +    frames = goog.testing.stacktrace.parse_(stack);
    +  }
    +  return goog.testing.stacktrace.framesToString_(frames);
    +};
    +
    +
    +/**
    + * Converts an array of CallSite (elements of a stack trace in V8) to an array
    + * of Frames.
    + * @param {!Array<!CallSite>} stack The stack as an array of CallSites.
    + * @return {!Array<!goog.testing.stacktrace.Frame>} The stack as an array of
    + *     Frames.
    + * @private
    + */
    +goog.testing.stacktrace.callSitesToFrames_ = function(stack) {
    +  var frames = [];
    +  for (var i = 0; i < stack.length; i++) {
    +    var callSite = stack[i];
    +    var functionName = callSite.getFunctionName() || 'unknown';
    +    var fileName = callSite.getFileName();
    +    var path = fileName ? fileName + ':' + callSite.getLineNumber() + ':' +
    +        callSite.getColumnNumber() : 'unknown';
    +    frames.push(
    +        new goog.testing.stacktrace.Frame('', functionName, '', '', path));
    +  }
    +  return frames;
    +};
    +
    +
    +goog.exportSymbol('setDeobfuscateFunctionName',
    +    goog.testing.stacktrace.setDeobfuscateFunctionName);
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.html b/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.html
    new file mode 100644
    index 0000000000..e50c94b6ab
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.stacktrace
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.stacktraceTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.js b/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.js
    new file mode 100644
    index 0000000000..1bb945c7a1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/stacktrace_test.js
    @@ -0,0 +1,375 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.stacktraceTest');
    +goog.setTestOnly('goog.testing.stacktraceTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.stacktrace');
    +goog.require('goog.testing.stacktrace.Frame');
    +goog.require('goog.userAgent');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +var expectedFailures;
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  stubs.set(goog.testing.stacktrace, 'isClosureInspectorActive_', function() {
    +    return false;
    +  });
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testParseStackFrameInV8() {
    +  var frameString = '    at Error (unknown source)';
    +  var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  var expected = new goog.testing.stacktrace.Frame('', 'Error', '', '', '');
    +  assertObjectEquals('exception name only', expected, frame);
    +
    +  frameString = '    at Object.assert (file:///.../asserts.js:29:10)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('Object', 'assert', '', '',
    +      'file:///.../asserts.js:29:10');
    +  assertObjectEquals('context object + function name + url', expected, frame);
    +
    +  frameString = '    at Object.x.y.z (/Users/bob/file.js:564:9)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('Object.x.y', 'z', '', '',
    +      '/Users/bob/file.js:564:9');
    +  assertObjectEquals('nested context object + function name + url',
    +      expected, frame);
    +
    +  frameString = '    at http://www.example.com/jsunit.js:117:13';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '', '',
    +      'http://www.example.com/jsunit.js:117:13');
    +  assertObjectEquals('url only', expected, frame);
    +
    +  frameString = '    at [object Object].exec [as execute] (file:///foo)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('[object Object]', 'exec',
    +      'execute', '', 'file:///foo');
    +  assertObjectEquals('function alias', expected, frame);
    +
    +  frameString = '    at new Class (file:///foo)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'new Class', '', '',
    +      'file:///foo');
    +  assertObjectEquals('constructor call', expected, frame);
    +
    +  frameString = '    at new <anonymous> (file:///foo)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'new <anonymous>', '', '',
    +      'file:///foo');
    +  assertObjectEquals('anonymous constructor call', expected, frame);
    +
    +  frameString = '    at Array.forEach (native)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('Array', 'forEach', '', '', '');
    +  assertObjectEquals('native function call', expected, frame);
    +
    +  frameString = '    at foo (eval at file://bar)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'foo', '', '',
    +      'eval at file://bar');
    +  assertObjectEquals('eval', expected, frame);
    +
    +  frameString = '    at foo.bar (closure/goog/foo.js:11:99)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('foo', 'bar', '', '',
    +      'closure/goog/foo.js:11:99');
    +  assertObjectEquals('Path without schema', expected, frame);
    +
    +  // In the Chrome console, execute: console.log(eval('Error().stack')).
    +  frameString =
    +      '    at eval (eval at <anonymous> (unknown source), <anonymous>:1:1)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'eval', '', '',
    +      'eval at <anonymous> (unknown source), <anonymous>:1:1');
    +  assertObjectEquals('nested eval', expected, frame);
    +}
    +
    +function testParseStackFrameInOpera() {
    +  var frameString = '@';
    +  var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  var expected = new goog.testing.stacktrace.Frame('', '', '', '', '');
    +  assertObjectEquals('empty frame', expected, frame);
    +
    +  frameString = '@javascript:console.log(Error().stack):1';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '', '',
    +      'javascript:console.log(Error().stack):1');
    +  assertObjectEquals('javascript path only', expected, frame);
    +
    +  frameString = '@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '', '',
    +      'file:///foo:42');
    +  assertObjectEquals('path only', expected, frame);
    +
    +  // (function go() { throw Error() })()
    +  // var c = go; c()
    +  frameString = 'go([arguments not available])@';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'go', '',
    +      '([arguments not available])', '');
    +  assertObjectEquals('name and empty path', expected, frame);
    +
    +  frameString = 'go([arguments not available])@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'go', '',
    +      '([arguments not available])', 'file:///foo:42');
    +  assertObjectEquals('name and path', expected, frame);
    +
    +  // (function() { throw Error() })()
    +  frameString =
    +      '<anonymous function>([arguments not available])@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '',
    +      '([arguments not available])', 'file:///foo:42');
    +  assertObjectEquals('anonymous function', expected, frame);
    +
    +  // var b = {foo: function() { throw Error() }}
    +  frameString = '<anonymous function: foo>()@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame(
    +      '', 'foo', '', '()', 'file:///foo:42');
    +  assertObjectEquals('object literal function', expected, frame);
    +
    +  // var c = {}; c.foo = function() { throw Error() }
    +  frameString = '<anonymous function: c.foo>()@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame(
    +      'c', 'foo', '', '()', 'file:///foo:42');
    +  assertObjectEquals('named object literal function', expected, frame);
    +
    +  frameString = '<anonymous function: Foo.prototype.bar>()@';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame(
    +      'Foo.prototype', 'bar', '', '()', '');
    +  assertObjectEquals('prototype function', expected, frame);
    +
    +  frameString = '<anonymous function: goog.Foo.prototype.bar>()@';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame(
    +      'goog.Foo.prototype', 'bar', '', '()', '');
    +  assertObjectEquals('namespaced prototype function', expected, frame);
    +}
    +
    +// All test strings are parsed with the conventional and long
    +// frame algorithms.
    +function testParseStackFrameInFirefox() {
    +  var frameString = 'Error("Assertion failed")@:0';
    +  var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  var expected = new goog.testing.stacktrace.Frame('', 'Error', '',
    +      '("Assertion failed")', '');
    +  assertObjectEquals('function name + arguments', expected, frame);
    +
    +  frame = goog.testing.stacktrace.parseLongFirefoxFrame_(frameString);
    +  assertObjectEquals('function name + arguments', expected, frame);
    +
    +  frameString = '()@file:///foo:42';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '', '()',
    +      'file:///foo:42');
    +  assertObjectEquals('anonymous function', expected, frame);
    +
    +  frame = goog.testing.stacktrace.parseLongFirefoxFrame_(frameString);
    +  assertObjectEquals('anonymous function', expected, frame);
    +
    +  frameString = '@javascript:alert(0)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', '', '', '',
    +      'javascript:alert(0)');
    +  assertObjectEquals('anonymous function', expected, frame);
    +
    +  frame = goog.testing.stacktrace.parseLongFirefoxFrame_(frameString);
    +  assertObjectEquals('anonymous function', expected, frame);
    +}
    +
    +function testCanonicalizeFrame() {
    +  var frame = new goog.testing.stacktrace.Frame('<window>', 'foo', 'bar',
    +      '("<a>\'&amp;")', 'http://x?a=1&b=2:1');
    +  assertEquals('canonical stack frame, everything is escaped',
    +      '&lt;window&gt;.foo(&quot;&lt;a&gt;\'&amp;amp;&quot;) ' +
    +      '[as bar] at http://x?a=1&amp;b=2:1', frame.toCanonicalString());
    +}
    +
    +function testDeobfuscateFunctionName() {
    +  goog.testing.stacktrace.setDeobfuscateFunctionName(function(name) {
    +    return name.replace(/\$/g, '.');
    +  });
    +
    +  var frame = new goog.testing.stacktrace.Frame('', 'a$b$c', 'd$e', '', '');
    +  assertEquals('deobfuscated function name', 'a.b.c [as d.e]',
    +      frame.toCanonicalString());
    +}
    +
    +function testFramesToString() {
    +  var normalFrame = new goog.testing.stacktrace.Frame('', 'foo', '', '', '');
    +  var anonFrame = new goog.testing.stacktrace.Frame('', '', '', '', '');
    +  var frames = [normalFrame, anonFrame, null, anonFrame];
    +  var stack = goog.testing.stacktrace.framesToString_(frames);
    +  assertEquals('framesToString', '> foo\n> anonymous\n> (unknown)\n', stack);
    +}
    +
    +function testFollowCallChain() {
    +  var func = function(var_args) {
    +    return goog.testing.stacktrace.followCallChain_();
    +  };
    +
    +  // Created a fake type with a toString method.
    +  function LocalType() {};
    +  LocalType.prototype.toString = function() {
    +    return 'sg';
    +  };
    +
    +  // Create a mock with no expectations.
    +  var mock = new goog.testing.StrictMock(LocalType);
    +
    +  mock.$replay();
    +
    +  var frames = func(undefined, null, false, 0, '', {}, goog.nullFunction,
    +      mock, new LocalType);
    +
    +  // Opera before version 10 doesn't support the caller attribute. In that
    +  // browser followCallChain_() returns empty stack trace.
    +  expectedFailures.expectFailureFor(goog.userAgent.OPERA &&
    +      !goog.userAgent.isVersionOrHigher('10'));
    +  try {
    +    assertTrue('The stack trace consists of >=2 frames', frames.length >= 2);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +
    +  if (frames.length >= 2) {
    +    assertEquals('innermost function is anonymous', '', frames[0].getName());
    +    // There are white space differences how browsers convert functions to
    +    // strings.
    +    var expected = '(undefined,null,false,0,"",[objectObject],function(){},' +
    +        'goog.testing.Mock,sg)';
    +    assertEquals('arguments of the innermost function (ignoring whitespaces)',
    +        expected, frames[0].args_.replace(/\s/g, ''));
    +    assertEquals('test function name', 'testFollowCallChain',
    +        frames[1].getName());
    +  }
    +
    +  mock.$verify();
    +}
    +
    +// Create a stack trace string with one modest record and one long record,
    +// Verify that all frames are parsed. The length of the long arg is set
    +// to blow Firefox 3x's stack if put through a RegExp.
    +function testParsingLongStackTrace() {
    +  var longArg = goog.string.buildString(
    +      '(', goog.string.repeat('x', 1000000), ')');
    +  var stackTrace = goog.string.buildString(
    +      'shortFrame()@:0\n',
    +      'longFrame',
    +      longArg,
    +      '@http://google.com/somescript:0\n');
    +  var frames = goog.testing.stacktrace.parse_(stackTrace);
    +  assertEquals('number of returned frames', 2, frames.length);
    +  var expected = new goog.testing.stacktrace.Frame(
    +      '', 'shortFrame', '', '()', '');
    +  assertObjectEquals('short frame', expected, frames[0]);
    +
    +  expected = new goog.testing.stacktrace.Frame(
    +      '', 'longFrame', '', longArg, 'http://google.com/somescript:0');
    +  assertObjectEquals('exception name only', expected, frames[1]);
    +}
    +
    +function testParseStackFrameInIE10() {
    +  var frameString = '   at foo (http://bar:4000/bar.js:150:3)';
    +  var frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  var expected = new goog.testing.stacktrace.Frame('', 'foo', '', '',
    +      'http://bar:4000/bar.js:150:3');
    +  assertObjectEquals('name and path', expected, frame);
    +
    +  frameString = '   at Anonymous function (http://bar:4000/bar.js:150:3)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'Anonymous function', '', '',
    +      'http://bar:4000/bar.js:150:3');
    +  assertObjectEquals('Anonymous function', expected, frame);
    +
    +  frameString = '   at Global code (http://bar:4000/bar.js:150:3)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'Global code', '', '',
    +      'http://bar:4000/bar.js:150:3');
    +  assertObjectEquals('Global code', expected, frame);
    +
    +  frameString = '   at foo (eval code:150:3)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'foo', '', '',
    +      'eval code:150:3');
    +  assertObjectEquals('eval code', expected, frame);
    +
    +  frameString = '   at eval code (eval code:150:3)';
    +  frame = goog.testing.stacktrace.parseStackFrame_(frameString);
    +  expected = new goog.testing.stacktrace.Frame('', 'eval code', '', '',
    +      'eval code:150:3');
    +  assertObjectEquals('nested eval', expected, frame);
    +}
    +
    +// Verifies that retrieving the stack trace works when the 'stack' field of an
    +// exception contains an array of CallSites instead of a string. This is the
    +// case when running in a lightweight V8 runtime (for instance, in gjstests),
    +// as opposed to a browser environment.
    +function testGetStackFrameWithV8CallSites() {
    +  // A function to create V8 CallSites. Note that CallSite is an extern and thus
    +  // cannot be mocked with closure mocks.
    +  function createCallSite(functionName, fileName, lineNumber, colNumber) {
    +    return {
    +      getFunctionName: goog.functions.constant(functionName),
    +      getFileName: goog.functions.constant(fileName),
    +      getLineNumber: goog.functions.constant(lineNumber),
    +      getColumnNumber: goog.functions.constant(colNumber)
    +    };
    +  }
    +
    +  // Mock the goog.testing.stacktrace.getStack_ function, which normally
    +  // triggers an exception for the purpose of reading and returning its stack
    +  // trace. Here, pretend that V8 provided an array of CallSites instead of the
    +  // string that browsers provide.
    +  stubs.set(goog.testing.stacktrace, 'getNativeStack_', function() {
    +    return [
    +      createCallSite('fn1', 'file1', 1, 2),
    +      createCallSite('fn2', 'file2', 3, 4),
    +      createCallSite('fn3', 'file3', 5, 6)
    +    ];
    +  });
    +
    +  // Retrieve the stacktrace. This should translate the array of CallSites into
    +  // a single multi-line string.
    +  var stackTrace = goog.testing.stacktrace.get();
    +
    +  // Make sure the stack trace was translated correctly.
    +  var frames = stackTrace.split('\n');
    +  assertEquals(frames[0], '> fn1 at file1:1:2');
    +  assertEquals(frames[1], '> fn2 at file2:3:4');
    +  assertEquals(frames[2], '> fn3 at file3:5:6');
    +}
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/storage/fakemechanism.js b/mapbox-gl/closure-library/closure/goog/testing/storage/fakemechanism.js
    new file mode 100644
    index 0000000000..1094757167
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/storage/fakemechanism.js
    @@ -0,0 +1,68 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a fake storage mechanism for testing.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.testing.storage.FakeMechanism');
    +goog.setTestOnly('goog.testing.storage.FakeMechanism');
    +
    +goog.require('goog.storage.mechanism.IterableMechanism');
    +goog.require('goog.structs.Map');
    +
    +
    +
    +/**
    + * Creates a fake iterable mechanism.
    + *
    + * @constructor
    + * @extends {goog.storage.mechanism.IterableMechanism}
    + * @final
    + */
    +goog.testing.storage.FakeMechanism = function() {
    +  /**
    +   * @type {goog.structs.Map}
    +   * @private
    +   */
    +  this.storage_ = new goog.structs.Map();
    +};
    +goog.inherits(goog.testing.storage.FakeMechanism,
    +    goog.storage.mechanism.IterableMechanism);
    +
    +
    +/** @override */
    +goog.testing.storage.FakeMechanism.prototype.set = function(key, value) {
    +  this.storage_.set(key, value);
    +};
    +
    +
    +/** @override */
    +goog.testing.storage.FakeMechanism.prototype.get = function(key) {
    +  return /** @type {?string} */ (
    +      this.storage_.get(key, null /* default value */));
    +};
    +
    +
    +/** @override */
    +goog.testing.storage.FakeMechanism.prototype.remove = function(key) {
    +  this.storage_.remove(key);
    +};
    +
    +
    +/** @override */
    +goog.testing.storage.FakeMechanism.prototype.__iterator__ = function(opt_keys) {
    +  return this.storage_.__iterator__(opt_keys);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/strictmock.js b/mapbox-gl/closure-library/closure/goog/testing/strictmock.js
    new file mode 100644
    index 0000000000..c3303d6955
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/strictmock.js
    @@ -0,0 +1,130 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This file defines a strict mock implementation.
    + */
    +
    +goog.provide('goog.testing.StrictMock');
    +
    +goog.require('goog.array');
    +goog.require('goog.testing.Mock');
    +
    +
    +
    +/**
    + * This is a mock that verifies that methods are called in the order that they
    + * are specified during the recording phase. Since it verifies order, it
    + * follows 'fail fast' semantics. If it detects a deviation from the
    + * expectations, it will throw an exception and not wait for verify to be
    + * called.
    + * @param {Object|Function} objectToMock The object that should be mocked, or
    + *    the constructor of an object to mock.
    + * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
    + *     a mock should be constructed from the static functions of a class.
    + * @param {boolean=} opt_createProxy An optional argument denoting that
    + *     a proxy for the target mock should be created.
    + * @constructor
    + * @extends {goog.testing.Mock}
    + * @final
    + */
    +goog.testing.StrictMock = function(objectToMock, opt_mockStaticMethods,
    +    opt_createProxy) {
    +  goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
    +      opt_createProxy);
    +
    +  /**
    +   * An array of MockExpectations.
    +   * @type {Array<goog.testing.MockExpectation>}
    +   * @private
    +   */
    +  this.$expectations_ = [];
    +};
    +goog.inherits(goog.testing.StrictMock, goog.testing.Mock);
    +
    +
    +/** @override */
    +goog.testing.StrictMock.prototype.$recordExpectation = function() {
    +  this.$expectations_.push(this.$pendingExpectation);
    +};
    +
    +
    +/** @override */
    +goog.testing.StrictMock.prototype.$recordCall = function(name, args) {
    +  if (this.$expectations_.length == 0) {
    +    this.$throwCallException(name, args);
    +  }
    +
    +  // If the current expectation has a different name, make sure it was called
    +  // enough and then discard it. We're through with it.
    +  var currentExpectation = this.$expectations_[0];
    +  while (!this.$verifyCall(currentExpectation, name, args)) {
    +
    +    // This might be an item which has passed its min, and we can now
    +    // look past it, or it might be below its min and generate an error.
    +    if (currentExpectation.actualCalls < currentExpectation.minCalls) {
    +      this.$throwCallException(name, args, currentExpectation);
    +    }
    +
    +    this.$expectations_.shift();
    +    if (this.$expectations_.length < 1) {
    +      // Nothing left, but this may be a failed attempt to call the previous
    +      // item on the list, which may have been between its min and max.
    +      this.$throwCallException(name, args, currentExpectation);
    +    }
    +    currentExpectation = this.$expectations_[0];
    +  }
    +
    +  if (currentExpectation.maxCalls == 0) {
    +    this.$throwCallException(name, args);
    +  }
    +
    +  currentExpectation.actualCalls++;
    +  // If we hit the max number of calls for this expectation, we're finished
    +  // with it.
    +  if (currentExpectation.actualCalls == currentExpectation.maxCalls) {
    +    this.$expectations_.shift();
    +  }
    +
    +  return this.$do(currentExpectation, args);
    +};
    +
    +
    +/** @override */
    +goog.testing.StrictMock.prototype.$reset = function() {
    +  goog.testing.StrictMock.superClass_.$reset.call(this);
    +
    +  goog.array.clear(this.$expectations_);
    +};
    +
    +
    +/** @override */
    +goog.testing.StrictMock.prototype.$verify = function() {
    +  goog.testing.StrictMock.superClass_.$verify.call(this);
    +
    +  while (this.$expectations_.length > 0) {
    +    var expectation = this.$expectations_[0];
    +    if (expectation.actualCalls < expectation.minCalls) {
    +      this.$throwException('Missing a call to ' + expectation.name +
    +          '\nExpected: ' + expectation.minCalls + ' but was: ' +
    +          expectation.actualCalls);
    +
    +    } else {
    +      // Don't need to check max, that's handled when the call is made
    +      this.$expectations_.shift();
    +    }
    +  }
    +};
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.html b/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.html
    new file mode 100644
    index 0000000000..161f80c1e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.StrictMock
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.StrictMockTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.js b/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.js
    new file mode 100644
    index 0000000000..26efd8cf0f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/strictmock_test.js
    @@ -0,0 +1,423 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.StrictMockTest');
    +goog.setTestOnly('goog.testing.StrictMockTest');
    +
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.jsunit');
    +
    +// The object that we will be mocking
    +var RealObject = function() {
    +};
    +
    +RealObject.prototype.a = function() {
    +  fail('real object should never be called');
    +};
    +
    +RealObject.prototype.b = function() {
    +  fail('real object should never be called');
    +};
    +
    +RealObject.prototype.c = function() {
    +  fail('real object should never be called');
    +};
    +
    +var mock;
    +
    +function setUp() {
    +  var obj = new RealObject();
    +  mock = new goog.testing.StrictMock(obj);
    +}
    +
    +
    +function testMockFunction() {
    +  var mock = new goog.testing.StrictMock(RealObject);
    +  mock.a();
    +  mock.b();
    +  mock.c();
    +  mock.$replay();
    +  mock.a();
    +  mock.b();
    +  mock.c();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  assertThrows(function() {mock.x()});
    +}
    +
    +
    +function testSimpleExpectations() {
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a();
    +  mock.b();
    +  mock.a();
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  mock.b();
    +  mock.a();
    +  mock.a();
    +  mock.$verify();
    +}
    +
    +
    +function testFailToSetExpectation() {
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.a, mock));
    +
    +  mock.$reset();
    +
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.b, mock));
    +}
    +
    +
    +function testUnexpectedCall() {
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  assertThrows(goog.bind(mock.a, mock));
    +
    +  mock.$reset();
    +
    +  mock.a();
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.b, mock));
    +}
    +
    +
    +function testNotEnoughCalls() {
    +  mock.a();
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.$verify, mock));
    +
    +  mock.$reset();
    +
    +  mock.a();
    +  mock.b();
    +  mock.$replay();
    +  mock.a();
    +  assertThrows(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testOutOfOrderCalls() {
    +  mock.a();
    +  mock.b();
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.b, mock));
    +}
    +
    +
    +function testVerify() {
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a();
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testArgumentMatching() {
    +  mock.a('foo');
    +  mock.b('bar');
    +  mock.$replay();
    +  mock.a('foo');
    +  assertThrows(function() {mock.b('foo')});
    +
    +  mock.$reset();
    +  mock.a('foo');
    +  mock.a('bar');
    +  mock.$replay();
    +  mock.a('foo');
    +  mock.a('bar');
    +  mock.$verify();
    +
    +  mock.$reset();
    +  mock.a('foo');
    +  mock.a('bar');
    +  mock.$replay();
    +  assertThrows(function() {mock.a('bar')});
    +}
    +
    +
    +function testReturnValue() {
    +  mock.a().$returns(5);
    +  mock.$replay();
    +
    +  assertEquals('Mock should return the right value', 5, mock.a());
    +
    +  mock.$verify();
    +}
    +
    +function testMultipleReturnValues() {
    +  mock.a().$returns(3);
    +  mock.a().$returns(2);
    +  mock.a().$returns(1);
    +
    +  mock.$replay();
    +
    +  assertArrayEquals('Mock should return the right value sequence',
    +      [3, 2, 1],
    +      [mock.a(), mock.a(), mock.a()]);
    +
    +  mock.$verify();
    +}
    +
    +
    +function testAtMostOnce() {
    +  // Zero times SUCCESS.
    +  mock.a().$atMostOnce();
    +  mock.$replay();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  // One time SUCCESS.
    +  mock.a().$atMostOnce();
    +  mock.$replay();
    +  mock.a();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  // Many times FAIL.
    +  mock.a().$atMostOnce();
    +  mock.$replay();
    +  mock.a();
    +  assertThrows(goog.bind(mock.a, mock));
    +
    +  mock.$reset();
    +
    +  // atMostOnce only lasts until a new method is called.
    +  mock.a().$atMostOnce();
    +  mock.b();
    +  mock.a();
    +  mock.$replay();
    +  mock.b();
    +  assertThrows(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testAtLeastOnce() {
    +  // atLeastOnce does not mean zero times
    +  mock.a().$atLeastOnce();
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.$verify, mock));
    +
    +  mock.$reset();
    +
    +  // atLeastOnce does mean three times
    +  mock.a().$atLeastOnce();
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  mock.a();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  // atLeastOnce only lasts until a new method is called
    +  mock.a().$atLeastOnce();
    +  mock.b();
    +  mock.a();
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  mock.b();
    +  mock.a();
    +  assertThrows(goog.bind(mock.a, mock));
    +}
    +
    +
    +function testAtLeastOnceWithArgs() {
    +  mock.a('asdf').$atLeastOnce();
    +  mock.a('qwert');
    +  mock.$replay();
    +  mock.a('asdf');
    +  mock.a('asdf');
    +  mock.a('qwert');
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a('asdf').$atLeastOnce();
    +  mock.a('qwert');
    +  mock.$replay();
    +  mock.a('asdf');
    +  mock.a('asdf');
    +  assertThrows(function() {mock.a('zxcv')});
    +  assertThrows(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testAnyTimes() {
    +  mock.a().$anyTimes();
    +  mock.$replay();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a().$anyTimes();
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  mock.a();
    +  mock.a();
    +  mock.a();
    +  mock.$verify();
    +}
    +
    +
    +function testAnyTimesWithArguments() {
    +  mock.a('foo').$anyTimes();
    +  mock.$replay();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a('foo').$anyTimes();
    +  mock.a('bar').$anyTimes();
    +  mock.$replay();
    +  mock.a('foo');
    +  mock.a('foo');
    +  mock.a('foo');
    +  mock.a('bar');
    +  mock.a('bar');
    +  mock.$verify();
    +}
    +
    +
    +function testZeroTimes() {
    +  mock.a().$times(0);
    +  mock.$replay();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a().$times(0);
    +  mock.$replay();
    +  assertThrows(function() {mock.a()});
    +}
    +
    +
    +function testZeroTimesWithArguments() {
    +  mock.a('foo').$times(0);
    +  mock.$replay();
    +  mock.$verify();
    +
    +  mock.$reset();
    +
    +  mock.a('foo').$times(0);
    +  mock.$replay();
    +  assertThrows(function() {mock.a('foo')});
    +}
    +
    +
    +function testTooManyCalls() {
    +  mock.a().$times(2);
    +  mock.$replay();
    +  mock.a();
    +  mock.a();
    +  assertThrows(function() {mock.a()});
    +}
    +
    +
    +function testTooManyCallsWithArguments() {
    +  mock.a('foo').$times(2);
    +  mock.$replay();
    +  mock.a('foo');
    +  mock.a('foo');
    +  assertThrows(function() {mock.a('foo')});
    +}
    +
    +
    +function testMultipleSkippedAnyTimes() {
    +  mock.a().$anyTimes();
    +  mock.b().$anyTimes();
    +  mock.c().$anyTimes();
    +  mock.$replay();
    +  mock.c();
    +  mock.$verify();
    +}
    +
    +
    +function testMultipleSkippedAnyTimesWithArguments() {
    +  mock.a('foo').$anyTimes();
    +  mock.a('bar').$anyTimes();
    +  mock.a('baz').$anyTimes();
    +  mock.$replay();
    +  mock.a('baz');
    +  mock.$verify();
    +}
    +
    +
    +function testVerifyThrows() {
    +  mock.a(1);
    +  mock.$replay();
    +  mock.a(1);
    +  try {
    +    mock.a(2);
    +    fail('bad mock, should fail');
    +  } catch (ex) {
    +    // this could be an event handler, for example
    +  }
    +  assertThrows(goog.bind(mock.$verify, mock));
    +}
    +
    +
    +function testThrows() {
    +  mock.a().$throws('exception!');
    +  mock.$replay();
    +  assertThrows(goog.bind(mock.a, mock));
    +  mock.$verify();
    +}
    +
    +
    +function testDoes() {
    +  mock.a(1, 2).$does(function(a, b) {return a + b;});
    +  mock.$replay();
    +  assertEquals('Mock should call the function', 3, mock.a(1, 2));
    +  mock.$verify();
    +}
    +
    +function testErrorMessageForBadArgs() {
    +  mock.a();
    +  mock.$anyTimes();
    +
    +  mock.$replay();
    +
    +  var message;
    +  try {
    +    mock.a('a');
    +  } catch (e) {
    +    message = e.message;
    +  }
    +
    +  assertTrue('No exception thrown on verify', goog.isDef(message));
    +  assertContains('Bad arguments to a()', message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts.js b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts.js
    new file mode 100644
    index 0000000000..2f9cb34dd0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts.js
    @@ -0,0 +1,310 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A utility class for making layout assertions. This is a port
    + * of http://go/layoutbot.java
    + * See {@link http://go/layouttesting}.
    + */
    +
    +goog.provide('goog.testing.style.layoutasserts');
    +
    +goog.require('goog.style');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.style');
    +
    +
    +/**
    + * Asserts that an element has:
    + *   1 - a CSS rendering the makes the element visible.
    + *   2 - a non-zero width and height.
    + * @param {Element|string} a The element or optionally the comment string.
    + * @param {Element=} opt_b The element when a comment string is present.
    + */
    +var assertIsVisible = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var element = nonCommentArg(1, 1, arguments);
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.isVisible(element) &&
    +      goog.testing.style.hasVisibleDimensions(element),
    +      'Specified element should be visible.');
    +};
    +
    +
    +/**
    + * The counter assertion of assertIsVisible().
    + * @param {Element|string} a The element or optionally the comment string.
    + * @param {Element=} opt_b The element when a comment string is present.
    + */
    +var assertNotVisible = function(a, opt_b) {
    +  _validateArguments(1, arguments);
    +  var element = nonCommentArg(1, 1, arguments);
    +  if (!element) {
    +    return;
    +  }
    +
    +  _assert(commentArg(1, arguments),
    +      !goog.testing.style.isVisible(element) ||
    +      !goog.testing.style.hasVisibleDimensions(element),
    +      'Specified element should not be visible.');
    +};
    +
    +
    +/**
    + * Asserts that the two specified elements intersect.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertIntersect = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.intersects(element, otherElement),
    +      'Elements should intersect.');
    +};
    +
    +
    +/**
    + * Asserts that the two specified elements do not intersect.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertNoIntersect = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +
    +  _assert(commentArg(1, arguments),
    +      !goog.testing.style.intersects(element, otherElement),
    +      'Elements should not intersect.');
    +};
    +
    +
    +/**
    + * Asserts that the element must have the specified width.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertWidth = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var width = nonCommentArg(2, 2, arguments);
    +  var size = goog.style.getSize(element);
    +  var elementWidth = size.width;
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.layoutasserts.isWithinThreshold_(
    +          width, elementWidth, 0 /* tolerance */),
    +      'Element should have width ' + width + ' but was ' + elementWidth + '.');
    +};
    +
    +
    +/**
    + * Asserts that the element must have the specified width within the specified
    + * tolerance.
    + * @param {Element|string} a The element or optionally the comment string.
    + * @param {number|Element} b The height or the element if comment string is
    + *     present.
    + * @param {number} c The tolerance or the height if comment string is
    + *     present.
    + * @param {number=} opt_d The tolerance if comment string is present.
    + */
    +var assertWidthWithinTolerance = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +  var element = nonCommentArg(1, 3, arguments);
    +  var width = nonCommentArg(2, 3, arguments);
    +  var tolerance = nonCommentArg(3, 3, arguments);
    +  var size = goog.style.getSize(element);
    +  var elementWidth = size.width;
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.layoutasserts.isWithinThreshold_(
    +          width, elementWidth, tolerance),
    +      'Element width(' + elementWidth + ') should be within given width(' +
    +      width + ') with tolerance value of ' + tolerance + '.');
    +};
    +
    +
    +/**
    + * Asserts that the element must have the specified height.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertHeight = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var height = nonCommentArg(2, 2, arguments);
    +  var size = goog.style.getSize(element);
    +  var elementHeight = size.height;
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.layoutasserts.isWithinThreshold_(
    +          height, elementHeight, 0 /* tolerance */),
    +      'Element should have height ' + height + '.');
    +};
    +
    +
    +/**
    + * Asserts that the element must have the specified height within the specified
    + * tolerance.
    + * @param {Element|string} a The element or optionally the comment string.
    + * @param {number|Element} b The height or the element if comment string is
    + *     present.
    + * @param {number} c The tolerance or the height if comment string is
    + *     present.
    + * @param {number=} opt_d The tolerance if comment string is present.
    + */
    +var assertHeightWithinTolerance = function(a, b, c, opt_d) {
    +  _validateArguments(3, arguments);
    +  var element = nonCommentArg(1, 3, arguments);
    +  var height = nonCommentArg(2, 3, arguments);
    +  var tolerance = nonCommentArg(3, 3, arguments);
    +  var size = goog.style.getSize(element);
    +  var elementHeight = size.height;
    +
    +  _assert(commentArg(1, arguments),
    +      goog.testing.style.layoutasserts.isWithinThreshold_(
    +          height, elementHeight, tolerance),
    +      'Element width(' + elementHeight + ') should be within given width(' +
    +      height + ') with tolerance value of ' + tolerance + '.');
    +};
    +
    +
    +/**
    + * Asserts that the first element is to the left of the second element.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertIsLeftOf = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +
    +  _assert(commentArg(1, arguments),
    +      elementRect.left < otherElementRect.left,
    +      'Elements should be left to right.');
    +};
    +
    +
    +/**
    + * Asserts that the first element is strictly left of the second element.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertIsStrictlyLeftOf = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +
    +  _assert(commentArg(1, arguments),
    +      elementRect.left + elementRect.width < otherElementRect.left,
    +      'Elements should be strictly left to right.');
    +};
    +
    +
    +/**
    + * Asserts that the first element is higher than the second element.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertIsAbove = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +
    +  _assert(commentArg(1, arguments),
    +      elementRect.top < otherElementRect.top,
    +      'Elements should be top to bottom.');
    +};
    +
    +
    +/**
    + * Asserts that the first element is strictly higher than the second element.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertIsStrictlyAbove = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +
    +  _assert(commentArg(1, arguments),
    +      elementRect.top + elementRect.height < otherElementRect.top,
    +      'Elements should be strictly top to bottom.');
    +};
    +
    +
    +/**
    + * Asserts that the first element's bounds contain the bounds of the second
    + * element.
    + * @param {Element|string} a The first element or optionally the comment string.
    + * @param {Element} b The second element or the first element if comment string
    + *     is present.
    + * @param {Element=} opt_c The second element if comment string is present.
    + */
    +var assertContained = function(a, b, opt_c) {
    +  _validateArguments(2, arguments);
    +  var element = nonCommentArg(1, 2, arguments);
    +  var otherElement = nonCommentArg(2, 2, arguments);
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +
    +  _assert(commentArg(1, arguments),
    +      elementRect.contains(otherElementRect),
    +      'Element should be contained within the other element.');
    +};
    +
    +
    +/**
    + * Returns true if the difference between val1 and val2 is less than or equal to
    + * the threashold.
    + * @param {number} val1 The first value.
    + * @param {number} val2 The second value.
    + * @param {number} threshold The threshold value.
    + * @return {boolean} Whether or not the the values are within the threshold.
    + * @private
    + */
    +goog.testing.style.layoutasserts.isWithinThreshold_ = function(
    +    val1, val2, threshold) {
    +  return Math.abs(val1 - val2) <= threshold;
    +};
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.html b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.html
    new file mode 100644
    index 0000000000..ff5a8341e3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.testing.style.layoutasserts Tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.style.layoutassertsTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="element1">
    +  </div>
    +  <div id="element2">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.js b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.js
    new file mode 100644
    index 0000000000..90529b4279
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/layoutasserts_test.js
    @@ -0,0 +1,257 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.style.layoutassertsTest');
    +goog.setTestOnly('goog.testing.style.layoutassertsTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +/** @suppress {extraRequire} */
    +goog.require('goog.testing.style.layoutasserts');
    +
    +var div1;
    +var div2;
    +var DEFAULT_WIDTH = 200;
    +var DEFAULT_HEIGHT = 100;
    +
    +function setUp() {
    +  div1 = goog.dom.createDom(
    +      'div',
    +      {
    +        id: 'test',
    +        className: 'test',
    +        style: 'position:absolute;top:0;left:0;' +
    +            'width:' + DEFAULT_WIDTH + 'px;' +
    +            'height:' + DEFAULT_HEIGHT + 'px;' +
    +                'background-color:#EEE',
    +        innerHTML: 'abc'
    +      });
    +  div2 = goog.dom.createDom('div',
    +      {
    +        id: 'test2',
    +        className: 'test2',
    +        style: 'position:absolute;' +
    +            'top:0;left:0;' +
    +            'width:' + DEFAULT_WIDTH + 'px;' +
    +            'height:' + DEFAULT_HEIGHT + 'px;' +
    +            'background-color:#F00',
    +        innerHTML: 'abc'
    +      });
    +
    +}
    +
    +
    +function tearDown() {
    +  div1 = null;
    +  div2 = null;
    +}
    +
    +
    +/**
    + * Tests assertIsVisible.
    + */
    +function testAssertIsVisible() {
    +  assertThrows('Exception should be thrown when asserting visibility.',
    +      goog.bind(assertIsVisible, null, null)); // assertIsVisible(null)
    +
    +  // Attach it to BODY tag and assert that it is visible.
    +  document.body.appendChild(div1);
    +  assertIsVisible('Div should be visible.', div1);
    +
    +  // Tests with hidden element
    +  failed = false;
    +  goog.style.setElementShown(div1, false /* display */);
    +  assertThrows('Exception should be thrown when asserting visibility.',
    +      goog.bind(assertIsVisible, null, div1));
    +
    +  // Clean up.
    +  document.body.removeChild(div1);
    +}
    +
    +
    +/**
    + * Tests assertNotVisible.
    + */
    +function testAssertNotVisible() {
    +  // Tests null as a parameter.
    +  var element = null;
    +  assertNotVisible(element);
    +
    +  // Attach the element to BODY element, assert should fail.
    +  document.body.appendChild(div1);
    +  assertThrows('Exception should be thrown when asserting non-visibility.',
    +      goog.bind(assertNotVisible, null, div1));
    +
    +  // Clean up.
    +  document.body.removeChild(div1);
    +}
    +
    +
    +/**
    + * Tests assertIsIntersect.
    + */
    +function testAssertIntersect() {
    +  document.body.appendChild(div1);
    +  document.body.appendChild(div2);
    +
    +  // No intersection
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 500, 500);
    +  assertThrows('Exception should be thrown when asserting intersection.',
    +      goog.bind(assertIntersect, null, div1, div2));
    +  assertNoIntersect(div1, div2);
    +
    +  // Some intersection
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 50, 50);
    +  assertThrows('Exception should be thrown when asserting no intersection.',
    +      goog.bind(assertNoIntersect, null, div1, div2));
    +  assertIntersect(div1, div2);
    +
    +  // Completely superimposed.
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 0, 0);
    +  assertThrows('Exception should be thrown when asserting no intersection.',
    +      goog.bind(assertNoIntersect, null, div1, div2));
    +  assertIntersect(div1, div2);
    +}
    +
    +
    +/**
    + * Tests assertWidth.
    + */
    +function testAssertWidth() {
    +  document.body.appendChild(div1);
    +
    +  // Test correct width
    +  assertWidth(div1, DEFAULT_WIDTH);
    +
    +  // Test wrong width
    +  assertThrows('Exception should be thrown when elements has wrong width',
    +      goog.bind(assertWidth, null, div1, 400));
    +
    +  // Test a valid tolerance value
    +  assertWidthWithinTolerance(div1, 180, 20);
    +
    +  // Test exceeding tolerance value
    +  assertThrows(
    +      'Exception should be thrown when element\'s width exceeds tolerance',
    +      goog.bind(assertWidthWithinTolerance, null, div1, 100, 0.1));
    +}
    +
    +
    +/**
    + * Tests assertHeight.
    + */
    +function testAssertHeight() {
    +  document.body.appendChild(div1);
    +
    +  // Test correct height
    +  assertHeight(div1, DEFAULT_HEIGHT);
    +
    +  // Test wrong height
    +  assertThrows('Exception should be thrown when element has wrong height.',
    +      goog.bind(assertHeightWithinTolerance, null, div1, 300));
    +
    +  // Test a valid tolerance value
    +  assertHeightWithinTolerance(div1, 90, 10);
    +
    +  // Test exceeding tolerance value
    +  assertThrows(
    +      'Exception should be thrown when element\'s height exceeds tolerance',
    +      goog.bind(assertHeight, null, div1, 50, 0.2));
    +}
    +
    +
    +/**
    + * Tests assertIsLeftOf.
    + */
    +function testAssertIsLeftOf() {
    +  document.body.appendChild(div1);
    +  document.body.appendChild(div2);
    +
    +  // Test elements of same size & location
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(assertIsLeftOf, null, div1, div2));
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(
    +          assertIsStrictlyLeftOf, null, div1, div2));
    +
    +  // Test elements that are not left to right
    +  goog.style.setPosition(div1, 100, 0);
    +  goog.style.setPosition(div2, 0, 0);
    +  assertThrows(
    +      'Exception should be thrown when elements are not left to right.',
    +      goog.bind(assertIsLeftOf, null, div1, div2));
    +  assertThrows(
    +      'Exception should be thrown when elements are not left to right.',
    +      goog.bind(
    +          assertIsStrictlyLeftOf, null, div1, div2));
    +
    +  // Test elements that intersect, but is left to right
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 100, 0);
    +  assertIsLeftOf(div1, div2);
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(
    +          assertIsStrictlyLeftOf, null, div1, div2));
    +
    +  // Test elements that are strictly left to right
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 999, 0);
    +  assertIsLeftOf(div1, div2);
    +  assertIsStrictlyLeftOf(div1, div2);
    +}
    +
    +
    +/**
    + * Tests assertIsAbove.
    + */
    +function testAssertIsAbove() {
    +  document.body.appendChild(div1);
    +  document.body.appendChild(div2);
    +
    +  // Test elements of same size & location
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(assertIsAbove, null, div1, div2));
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(
    +      assertIsStrictlyAbove, null, div1, div2));
    +
    +  // Test elements that are not top to bottom
    +  goog.style.setPosition(div1, 0, 999);
    +  goog.style.setPosition(div2, 0, 0);
    +  assertThrows(
    +      'Exception should be thrown when elements are not top to bottom.',
    +      goog.bind(assertIsAbove, null, div1, div2));
    +  assertThrows(
    +      'Exception should be thrown when elements are not top to bottom.',
    +      goog.bind(
    +      assertIsStrictlyAbove, null, div1, div2));
    +
    +  // Test elements that intersect, but is top to bottom
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 0, 50);
    +  assertIsAbove(div1, div2);
    +  assertThrows('Exception should be thrown when elements intersect.',
    +      goog.bind(
    +      assertIsStrictlyAbove, null, div1, div2));
    +
    +  // Test elements that are top to bottom
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 0, 999);
    +  assertIsAbove(div1, div2);
    +  assertIsStrictlyAbove(div1, div2);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/style.js b/mapbox-gl/closure-library/closure/goog/testing/style/style.js
    new file mode 100644
    index 0000000000..e180cf4af9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/style.js
    @@ -0,0 +1,101 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for inspecting page layout. This is a port of
    + *     http://go/layoutbot.java
    + *     See {@link http://go/layouttesting}.
    + */
    +
    +goog.provide('goog.testing.style');
    +
    +goog.require('goog.dom');
    +goog.require('goog.math.Rect');
    +goog.require('goog.style');
    +
    +
    +/**
    + * Determines whether the bounding rectangles of the given elements intersect.
    + * @param {Element} element The first element.
    + * @param {Element} otherElement The second element.
    + * @return {boolean} Whether the bounding rectangles of the given elements
    + *     intersect.
    + */
    +goog.testing.style.intersects = function(element, otherElement) {
    +  var elementRect = goog.style.getBounds(element);
    +  var otherElementRect = goog.style.getBounds(otherElement);
    +  return goog.math.Rect.intersects(elementRect, otherElementRect);
    +};
    +
    +
    +/**
    + * Determines whether the element has visible dimensions, i.e. x > 0 && y > 0.
    + * @param {Element} element The element to check.
    + * @return {boolean} Whether the element has visible dimensions.
    + */
    +goog.testing.style.hasVisibleDimensions = function(element) {
    +  var elSize = goog.style.getSize(element);
    +  var shortest = elSize.getShortest();
    +  if (shortest <= 0) {
    +    return false;
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Determines whether the CSS style of the element renders it visible.
    + * @param {!Element} element The element to check.
    + * @return {boolean} Whether the CSS style of the element renders it visible.
    + */
    +goog.testing.style.isVisible = function(element) {
    +  var visibilityStyle =
    +      goog.testing.style.getAvailableStyle_(element, 'visibility');
    +  var displayStyle =
    +      goog.testing.style.getAvailableStyle_(element, 'display');
    +
    +  return (visibilityStyle != 'hidden' && displayStyle != 'none');
    +};
    +
    +
    +/**
    + * Test whether the given element is on screen.
    + * @param {!Element} el The element to test.
    + * @return {boolean} Whether the element is on the screen.
    + */
    +goog.testing.style.isOnScreen = function(el) {
    +  var doc = goog.dom.getDomHelper(el).getDocument();
    +  var viewport = goog.style.getVisibleRectForElement(doc.body);
    +  var viewportRect = goog.math.Rect.createFromBox(viewport);
    +  return goog.dom.contains(doc, el) &&
    +      goog.style.getBounds(el).intersects(viewportRect);
    +};
    +
    +
    +/**
    + * This is essentially goog.style.getStyle_. goog.style.getStyle_ is private
    + * and is not a recommended way for general purpose style extractor. For the
    + * purposes of layout testing, we only use this function for retrieving
    + * 'visiblity' and 'display' style.
    + * @param {!Element} element The element to retrieve the style from.
    + * @param {string} style Style property name.
    + * @return {string} Style value.
    + * @private
    + */
    +goog.testing.style.getAvailableStyle_ = function(element, style) {
    +  return goog.style.getComputedStyle(element, style) ||
    +      goog.style.getCascadedStyle(element, style) ||
    +      goog.style.getStyle(element, style);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/style_test.html b/mapbox-gl/closure-library/closure/goog/testing/style/style_test.html
    new file mode 100644
    index 0000000000..dad03e1b4d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/style_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.testing.style Tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.styleTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/style/style_test.js b/mapbox-gl/closure-library/closure/goog/testing/style/style_test.js
    new file mode 100644
    index 0000000000..adb665ee18
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/style/style_test.js
    @@ -0,0 +1,157 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.styleTest');
    +goog.setTestOnly('goog.testing.styleTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.style');
    +
    +var div1;
    +var div2;
    +
    +function setUp() {
    +  var createDiv = function(color) {
    +    var div = goog.dom.createDom(
    +        'div',
    +        {
    +          style: 'position:absolute;top:0;left:0;' +
    +              'width:200px;height:100px;' +
    +              'background-color:' + color,
    +          innerHTML: 'abc'
    +        });
    +    document.body.appendChild(div);
    +    return div;
    +  };
    +
    +  div1 = createDiv('#EEE');
    +  div2 = createDiv('#F00');
    +}
    +
    +
    +function tearDown() {
    +  if (div1.parentNode)
    +    div1.parentNode.removeChild(div1);
    +  if (div2.parentNode)
    +    div2.parentNode.removeChild(div2);
    +
    +  div1 = null;
    +  div2 = null;
    +}
    +
    +
    +function testIsVisible() {
    +  assertTrue('The div should be detected as visible.',
    +             goog.testing.style.isVisible(div1));
    +
    +  // Tests with hidden element
    +  goog.style.setElementShown(div1, false /* display */);
    +  assertFalse('The div should be detected as not visible.',
    +              goog.testing.style.isVisible(div1));
    +}
    +
    +function testIsOnScreen() {
    +  var el = document.createElement('div');
    +  document.body.appendChild(el);
    +
    +  var dom = goog.dom.getDomHelper(el);
    +  var winScroll = dom.getDocumentScroll();
    +  var winSize = dom.getViewportSize();
    +
    +  el.style.position = 'absolute';
    +  goog.style.setSize(el, 100, 100);
    +
    +  goog.style.setPosition(el, winScroll.x, winScroll.y);
    +  assertTrue('An element fully on the screen is on screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x - 10, winScroll.y - 10);
    +  assertTrue(
    +      'An element partially off the top-left of the screen is on screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x - 150, winScroll.y - 10);
    +  assertFalse(
    +      'An element completely off the top of the screen is off screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x - 10, winScroll.y - 150);
    +  assertFalse(
    +      'An element completely off the left of the screen is off screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x + winSize.width + 1, winScroll.y);
    +  assertFalse(
    +      'An element completely off the right of the screen is off screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x, winScroll.y + winSize.height + 1);
    +  assertFalse(
    +      'An element completely off the bottom of the screen is off screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x + winSize.width - 10, winScroll.y);
    +  assertTrue(
    +      'An element partially off the right of the screen is on screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  goog.style.setPosition(el, winScroll.x, winScroll.y + winSize.height - 10);
    +  assertTrue(
    +      'An element partially off the bottom of the screen is on screen.',
    +      goog.testing.style.isOnScreen(el));
    +
    +  var el2 = document.createElement('div');
    +  el2.style.position = 'absolute';
    +  goog.style.setSize(el2, 100, 100);
    +  goog.style.setPosition(el2, winScroll.x, winScroll.y);
    +  assertFalse('An element not in the DOM is not on screen.',
    +              goog.testing.style.isOnScreen(el2));
    +}
    +
    +function testHasVisibleDimensions() {
    +  goog.style.setSize(div1, 0, 0);
    +  assertFalse('0x0 should not be considered visible dimensions.',
    +              goog.testing.style.hasVisibleDimensions(div1));
    +  goog.style.setSize(div1, 10, 0);
    +  assertFalse('10x0 should not be considered visible dimensions.',
    +              goog.testing.style.hasVisibleDimensions(div1));
    +  goog.style.setSize(div1, 10, 10);
    +  assertTrue('10x10 should be considered visible dimensions.',
    +             goog.testing.style.hasVisibleDimensions(div1));
    +  goog.style.setSize(div1, 0, 10);
    +  assertFalse('0x10 should not be considered visible dimensions.',
    +              goog.testing.style.hasVisibleDimensions(div1));
    +}
    +
    +function testIntersects() {
    +  // No intersection
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 500, 500);
    +  assertFalse('The divs should not be determined to itersect.',
    +              goog.testing.style.intersects(div1, div2));
    +
    +  // Some intersection
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 50, 50);
    +  assertTrue('The divs should be determined to itersect.',
    +             goog.testing.style.intersects(div1, div2));
    +
    +  // Completely superimposed.
    +  goog.style.setPosition(div1, 0, 0);
    +  goog.style.setPosition(div2, 0, 0);
    +  assertTrue('The divs should be determined to itersect.',
    +             goog.testing.style.intersects(div1, div2));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/testcase.js b/mapbox-gl/closure-library/closure/goog/testing/testcase.js
    new file mode 100644
    index 0000000000..2200e2f304
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/testcase.js
    @@ -0,0 +1,1476 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class representing a set of test functions to be run.
    + *
    + * Testing code should not have dependencies outside of goog.testing so as to
    + * reduce the chance of masking missing dependencies.
    + *
    + * This file does not compile correctly with --collapse_properties. Use
    + * --property_renaming=ALL_UNQUOTED instead.
    + *
    + */
    +
    +goog.provide('goog.testing.TestCase');
    +goog.provide('goog.testing.TestCase.Error');
    +goog.provide('goog.testing.TestCase.Order');
    +goog.provide('goog.testing.TestCase.Result');
    +goog.provide('goog.testing.TestCase.Test');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.object');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.stacktrace');
    +
    +
    +
    +/**
    + * A class representing a JsUnit test case.  A TestCase is made up of a number
    + * of test functions which can be run.  Individual test cases can override the
    + * following functions to set up their test environment:
    + *   - runTests - completely override the test's runner
    + *   - setUpPage - called before any of the test functions are run
    + *   - tearDownPage - called after all tests are finished
    + *   - setUp - called before each of the test functions
    + *   - tearDown - called after each of the test functions
    + *   - shouldRunTests - called before a test run, all tests are skipped if it
    + *                      returns false.  Can be used to disable tests on browsers
    + *                      where they aren't expected to pass.
    + *
    + * Use {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}
    + *
    + * @param {string=} opt_name The name of the test case, defaults to
    + *     'Untitled Test Case'.
    + * @constructor
    + */
    +goog.testing.TestCase = function(opt_name) {
    +  /**
    +   * A name for the test case.
    +   * @type {string}
    +   * @private
    +   */
    +  this.name_ = opt_name || 'Untitled Test Case';
    +
    +  /**
    +   * Array of test functions that can be executed.
    +   * @type {!Array<!goog.testing.TestCase.Test>}
    +   * @private
    +   */
    +  this.tests_ = [];
    +
    +  /**
    +   * Set of test names and/or indices to execute, or null if all tests should
    +   * be executed.
    +   *
    +   * Indices are included to allow automation tools to run a subset of the
    +   * tests without knowing the exact contents of the test file.
    +   *
    +   * Indices should only be used with SORTED ordering.
    +   *
    +   * Example valid values:
    +   * <ul>
    +   * <li>[testName]
    +   * <li>[testName1, testName2]
    +   * <li>[2] - will run the 3rd test in the order specified
    +   * <li>[1,3,5]
    +   * <li>[testName1, testName2, 3, 5] - will work
    +   * <ul>
    +   * @type {Object}
    +   * @private
    +   */
    +  this.testsToRun_ = null;
    +
    +  /** @private {function(!goog.testing.TestCase.Result)} */
    +  this.runNextTestCallback_ = goog.nullFunction;
    +
    +  /**
    +   * The number of {@link runNextTest_} frames currently on the stack.
    +   * When this exceeds {@link MAX_STACK_DEPTH_}, test execution is rescheduled
    +   * for a later tick of the event loop.
    +   * @see {finishTestInvocation_}
    +   * @private {number}
    +   */
    +  this.depth_ = 0;
    +
    +  /** @private {goog.testing.TestCase.Test} */
    +  this.curTest_ = null;
    +
    +  var search = '';
    +  if (goog.global.location) {
    +    search = goog.global.location.search;
    +  }
    +
    +  // Parse the 'runTests' query parameter into a set of test names and/or
    +  // test indices.
    +  var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
    +  if (runTestsMatch) {
    +    this.testsToRun_ = {};
    +    var arr = runTestsMatch[1].split(',');
    +    for (var i = 0, len = arr.length; i < len; i++) {
    +      this.testsToRun_[arr[i]] = 1;
    +    }
    +  }
    +
    +  // Checks the URL for a valid order param.
    +  var orderMatch = search.match(/(?:\?|&)order=(natural|random|sorted)/i);
    +  if (orderMatch) {
    +    this.order = orderMatch[1];
    +  }
    +
    +  /**
    +   * Object used to encapsulate the test results.
    +   * @type {!goog.testing.TestCase.Result}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.result_ = new goog.testing.TestCase.Result(this);
    +};
    +
    +
    +/**
    + * The order to run the auto-discovered tests.
    + * @enum {string}
    + */
    +goog.testing.TestCase.Order = {
    +  /**
    +   * This is browser dependent and known to be different in FF and Safari
    +   * compared to others.
    +   */
    +  NATURAL: 'natural',
    +
    +  /** Random order. */
    +  RANDOM: 'random',
    +
    +  /** Sorted based on the name. */
    +  SORTED: 'sorted'
    +};
    +
    +
    +/**
    + * @return {string} The name of the test.
    + */
    +goog.testing.TestCase.prototype.getName = function() {
    +  return this.name_;
    +};
    +
    +
    +/**
    + * The maximum amount of time in milliseconds that the test case can take
    + * before it is forced to yield and reschedule. This prevents the test runner
    + * from blocking the browser and potentially hurting the test harness.
    + * @type {number}
    + */
    +goog.testing.TestCase.maxRunTime = 200;
    +
    +
    +/**
    + * The maximum number of {@link runNextTest_} frames that can be on the stack
    + * before the test case is forced to yield and reschedule. Although modern
    + * browsers can handle thousands of stack frames, this is set conservatively
    + * because maximum stack depth has never been standardized, and engine-specific
    + * techniques like tail cail optimization can affect the exact depth.
    + * @private @const
    + */
    +goog.testing.TestCase.MAX_STACK_DEPTH_ = 100;
    +
    +
    +/**
    + * The order to run the auto-discovered tests in.
    + * @type {string}
    + */
    +goog.testing.TestCase.prototype.order = goog.testing.TestCase.Order.SORTED;
    +
    +
    +/**
    + * Save a reference to {@code window.setTimeout}, so any code that overrides the
    + * default behavior (the MockClock, for example) doesn't affect our runner.
    + * @type {function((Function|string), number=, *=): number}
    + * @private
    + */
    +goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
    +
    +
    +/**
    + * Save a reference to {@code window.clearTimeout}, so any code that overrides
    + * the default behavior (e.g. MockClock) doesn't affect our runner.
    + * @type {function((null|number|undefined)): void}
    + * @private
    + */
    +goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
    +
    +
    +/**
    + * Save a reference to {@code window.Date}, so any code that overrides
    + * the default behavior doesn't affect our runner.
    + * @type {function(new: Date)}
    + * @private
    + */
    +goog.testing.TestCase.protectedDate_ = Date;
    +
    +
    +/**
    + * Saved string referencing goog.global.setTimeout's string serialization.  IE
    + * sometimes fails to uphold equality for setTimeout, but the string version
    + * stays the same.
    + * @type {string}
    + * @private
    + */
    +goog.testing.TestCase.setTimeoutAsString_ = String(goog.global.setTimeout);
    +
    +
    +/**
    + * TODO(user) replace this with prototype.currentTest.
    + * Name of the current test that is running, or null if none is running.
    + * @type {?string}
    + */
    +goog.testing.TestCase.currentTestName = null;
    +
    +
    +/**
    + * Avoid a dependency on goog.userAgent and keep our own reference of whether
    + * the browser is IE.
    + * @type {boolean}
    + */
    +goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
    +    !!goog.global.navigator &&
    +    goog.global.navigator.userAgent.indexOf('MSIE') != -1;
    +
    +
    +/**
    + * Exception object that was detected before a test runs.
    + * @type {*}
    + * @protected
    + */
    +goog.testing.TestCase.prototype.exceptionBeforeTest;
    +
    +
    +/**
    + * Whether the test case has ever tried to execute.
    + * @type {boolean}
    + */
    +goog.testing.TestCase.prototype.started = false;
    +
    +
    +/**
    + * Whether the test case is running.
    + * @type {boolean}
    + */
    +goog.testing.TestCase.prototype.running = false;
    +
    +
    +/**
    + * Timestamp for when the test was started.
    + * @type {number}
    + * @private
    + */
    +goog.testing.TestCase.prototype.startTime_ = 0;
    +
    +
    +/**
    + * Time since the last batch of tests was started, if batchTime exceeds
    + * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
    + * browser and a new batch will be started.
    + * @type {number}
    + * @private
    + */
    +goog.testing.TestCase.prototype.batchTime_ = 0;
    +
    +
    +/**
    + * Pointer to the current test.
    + * @type {number}
    + * @private
    + */
    +goog.testing.TestCase.prototype.currentTestPointer_ = 0;
    +
    +
    +/**
    + * Optional callback that will be executed when the test has finalized.
    + * @type {Function}
    + * @private
    + */
    +goog.testing.TestCase.prototype.onCompleteCallback_ = null;
    +
    +
    +/**
    + * Adds a new test to the test case.
    + * @param {goog.testing.TestCase.Test} test The test to add.
    + */
    +goog.testing.TestCase.prototype.add = function(test) {
    +  if (this.started) {
    +    throw Error('Tests cannot be added after execute() has been called. ' +
    +                'Test: ' + test.name);
    +  }
    +
    +  this.tests_.push(test);
    +};
    +
    +
    +/**
    + * Creates and adds a new test.
    + *
    + * Convenience function to make syntax less awkward when not using automatic
    + * test discovery.
    + *
    + * @param {string} name The test name.
    + * @param {!Function} ref Reference to the test function.
    + * @param {!Object=} opt_scope Optional scope that the test function should be
    + *     called in.
    + */
    +goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
    +  var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
    +  this.add(test);
    +};
    +
    +
    +/**
    + * Sets the tests.
    + * @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.setTests = function(tests) {
    +  this.tests_ = tests;
    +};
    +
    +
    +/**
    + * Gets the tests.
    + * @return {!Array<goog.testing.TestCase.Test>} The test array.
    + */
    +goog.testing.TestCase.prototype.getTests = function() {
    +  return this.tests_;
    +};
    +
    +
    +/**
    + * Returns the number of tests contained in the test case.
    + * @return {number} The number of tests.
    + */
    +goog.testing.TestCase.prototype.getCount = function() {
    +  return this.tests_.length;
    +};
    +
    +
    +/**
    + * Returns the number of tests actually run in the test case, i.e. subtracting
    + * any which are skipped.
    + * @return {number} The number of un-ignored tests.
    + */
    +goog.testing.TestCase.prototype.getActuallyRunCount = function() {
    +  return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
    +};
    +
    +
    +/**
    + * Returns the current test and increments the pointer.
    + * @return {goog.testing.TestCase.Test} The current test case.
    + */
    +goog.testing.TestCase.prototype.next = function() {
    +  var test;
    +  while ((test = this.tests_[this.currentTestPointer_++])) {
    +    if (!this.testsToRun_ || this.testsToRun_[test.name] ||
    +        this.testsToRun_[this.currentTestPointer_ - 1]) {
    +      return test;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Resets the test case pointer, so that next returns the first test.
    + */
    +goog.testing.TestCase.prototype.reset = function() {
    +  this.currentTestPointer_ = 0;
    +  this.result_ = new goog.testing.TestCase.Result(this);
    +};
    +
    +
    +/**
    + * Sets the callback function that should be executed when the tests have
    + * completed.
    + * @param {Function} fn The callback function.
    + */
    +goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
    +  this.onCompleteCallback_ = fn;
    +};
    +
    +
    +/**
    + * Can be overridden in test classes to indicate whether the tests in a case
    + * should be run in that particular situation.  For example, this could be used
    + * to stop tests running in a particular browser, where browser support for
    + * the class under test was absent.
    + * @return {boolean} Whether any of the tests in the case should be run.
    + */
    +goog.testing.TestCase.prototype.shouldRunTests = function() {
    +  return true;
    +};
    +
    +
    +/**
    + * Executes the tests, yielding asynchronously if execution time exceeds
    + * {@link maxRunTime}. There is no guarantee that the test case has finished
    + * once this method has returned. To be notified when the test case
    + * has finished, use {@link #setCompletedCallback} or
    + * {@link #runTestsReturningPromise}.
    + */
    +goog.testing.TestCase.prototype.execute = function() {
    +  if (!this.prepareForRun_()) {
    +    return;
    +  }
    +  this.log('Starting tests: ' + this.name_);
    +  this.cycleTests();
    +};
    +
    +
    +/**
    + * Sets up the internal state of the test case for a run.
    + * @return {boolean} If false, preparation failed because the test case
    + *     is not supposed to run in the present environment.
    + * @private
    + */
    +goog.testing.TestCase.prototype.prepareForRun_ = function() {
    +  this.started = true;
    +  this.reset();
    +  this.startTime_ = this.now();
    +  this.running = true;
    +  this.result_.totalCount = this.getCount();
    +  if (!this.shouldRunTests()) {
    +    this.log('shouldRunTests() returned false, skipping these tests.');
    +    this.result_.testSuppressed = true;
    +    this.finalize();
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Finalizes the test case, called when the tests have finished executing.
    + */
    +goog.testing.TestCase.prototype.finalize = function() {
    +  this.saveMessage('Done');
    +
    +  this.tearDownPage();
    +
    +  var restoredSetTimeout =
    +      goog.testing.TestCase.protectedSetTimeout_ == goog.global.setTimeout &&
    +      goog.testing.TestCase.protectedClearTimeout_ == goog.global.clearTimeout;
    +  if (!restoredSetTimeout && goog.testing.TestCase.IS_IE &&
    +      String(goog.global.setTimeout) ==
    +          goog.testing.TestCase.setTimeoutAsString_) {
    +    // In strange cases, IE's value of setTimeout *appears* to change, but
    +    // the string representation stays stable.
    +    restoredSetTimeout = true;
    +  }
    +
    +  if (!restoredSetTimeout) {
    +    var message = 'ERROR: Test did not restore setTimeout and clearTimeout';
    +    this.saveMessage(message);
    +    var err = new goog.testing.TestCase.Error(this.name_, message);
    +    this.result_.errors.push(err);
    +  }
    +  goog.global.clearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    +  goog.global.setTimeout = goog.testing.TestCase.protectedSetTimeout_;
    +  this.endTime_ = this.now();
    +  this.running = false;
    +  this.result_.runTime = this.endTime_ - this.startTime_;
    +  this.result_.numFilesLoaded = this.countNumFilesLoaded_();
    +  this.result_.complete = true;
    +
    +  this.log(this.result_.getSummary());
    +  if (this.result_.isSuccess()) {
    +    this.log('Tests complete');
    +  } else {
    +    this.log('Tests Failed');
    +  }
    +  if (this.onCompleteCallback_) {
    +    var fn = this.onCompleteCallback_;
    +    // Execute's the completed callback in the context of the global object.
    +    fn();
    +    this.onCompleteCallback_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Saves a message to the result set.
    + * @param {string} message The message to save.
    + */
    +goog.testing.TestCase.prototype.saveMessage = function(message) {
    +  this.result_.messages.push(this.getTimeStamp_() + '  ' + message);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the test case is running inside the multi test
    + *     runner.
    + */
    +goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
    +  var top = goog.global['top'];
    +  return top && typeof top['_allTests'] != 'undefined';
    +};
    +
    +
    +/**
    + * Logs an object to the console, if available.
    + * @param {*} val The value to log. Will be ToString'd.
    + */
    +goog.testing.TestCase.prototype.log = function(val) {
    +  if (!this.isInsideMultiTestRunner() && goog.global.console) {
    +    if (typeof val == 'string') {
    +      val = this.getTimeStamp_() + ' : ' + val;
    +    }
    +    if (val instanceof Error && val.stack) {
    +      // Chrome does console.log asynchronously in a different process
    +      // (http://code.google.com/p/chromium/issues/detail?id=50316).
    +      // This is an acute problem for Errors, which almost never survive.
    +      // Grab references to the immutable strings so they survive.
    +      goog.global.console.log(val, val.message, val.stack);
    +      // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
    +      // there are no circular references.
    +    } else {
    +      goog.global.console.log(val);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the test was a success.
    + */
    +goog.testing.TestCase.prototype.isSuccess = function() {
    +  return !!this.result_ && this.result_.isSuccess();
    +};
    +
    +
    +/**
    + * Returns a string detailing the results from the test.
    + * @param {boolean=} opt_verbose If true results will include data about all
    + *     tests, not just what failed.
    + * @return {string} The results from the test.
    + */
    +goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
    +  var rv = [];
    +
    +  if (this.running) {
    +    rv.push(this.name_ + ' [RUNNING]');
    +  } else {
    +    var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
    +    rv.push(this.name_ + ' [' + label + ']');
    +  }
    +
    +  if (goog.global.location) {
    +    rv.push(this.trimPath_(goog.global.location.href));
    +  }
    +
    +  rv.push(this.result_.getSummary());
    +
    +  if (opt_verbose) {
    +    rv.push('.', this.result_.messages.join('\n'));
    +  } else if (!this.result_.isSuccess()) {
    +    rv.push(this.result_.errors.join('\n'));
    +  }
    +
    +  rv.push(' ');
    +
    +  return rv.join('\n');
    +};
    +
    +
    +/**
    + * Returns the test results.
    + * @return {!goog.testing.TestCase.Result}
    + * @package
    + */
    +goog.testing.TestCase.prototype.getResult = function() {
    +  return this.result_;
    +};
    +
    +
    +/**
    + * Returns the amount of time it took for the test to run.
    + * @return {number} The run time, in milliseconds.
    + */
    +goog.testing.TestCase.prototype.getRunTime = function() {
    +  return this.result_.runTime;
    +};
    +
    +
    +/**
    + * Returns the number of script files that were loaded in order to run the test.
    + * @return {number} The number of script files.
    + */
    +goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
    +  return this.result_.numFilesLoaded;
    +};
    +
    +
    +/**
    + * Returns the test results object: a map from test names to a list of test
    + * failures (if any exist).
    + * @return {!Object<string, !Array<string>>} Tests results object.
    + */
    +goog.testing.TestCase.prototype.getTestResults = function() {
    +  return this.result_.resultsByName;
    +};
    +
    +
    +/**
    + * Executes each of the tests, yielding asynchronously if execution time
    + * exceeds {@link #maxRunTime}. There is no guarantee that the test case
    + * has finished execution once this method has returned.
    + * To be notified when the test case has finished execution, use
    + * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
    + *
    + * Overridable by the individual test case.  This allows test cases to defer
    + * when the test is actually started.  If overridden, finalize must be called
    + * by the test to indicate it has finished.
    + */
    +goog.testing.TestCase.prototype.runTests = function() {
    +  try {
    +    this.setUpPage();
    +  } catch (e) {
    +    this.exceptionBeforeTest = e;
    +  }
    +  this.execute();
    +};
    +
    +
    +/**
    + * Executes each of the tests, returning a promise that resolves with the
    + * test results once they are done running.
    + * @return {!IThenable.<!goog.testing.TestCase.Result>}
    + * @final
    + * @package
    + */
    +goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
    +  try {
    +    this.setUpPage();
    +  } catch (e) {
    +    this.exceptionBeforeTest = e;
    +  }
    +  if (!this.prepareForRun_()) {
    +    return goog.Promise.resolve(this.result_);
    +  }
    +  this.log('Starting tests: ' + this.name_);
    +  this.saveMessage('Start');
    +  this.batchTime_ = this.now();
    +  return new goog.Promise(function(resolve) {
    +    this.runNextTestCallback_ = resolve;
    +    this.runNextTest_();
    +  }, this);
    +};
    +
    +
    +/**
    + * Executes the next test method synchronously or with promises, depending on
    + * the test method's return value.
    + *
    + * If the test method returns a promise, the next test method will run once
    + * the promise is resolved or rejected. If the test method does not
    + * return a promise, it is assumed to be synchronous, and execution proceeds
    + * immediately to the next test method. This means that test cases can run
    + * partially synchronously and partially asynchronously, depending on
    + * the return values of their test methods. In particular, a test case
    + * executes synchronously until the first promise is returned from a
    + * test method (or until a resource limit is reached; see
    + * {@link finishTestInvocation_}).
    + * @private
    + */
    +goog.testing.TestCase.prototype.runNextTest_ = function() {
    +  this.curTest_ = this.next();
    +  if (!this.curTest_ || !this.running) {
    +    this.finalize();
    +    this.runNextTestCallback_(this.result_);
    +    return;
    +  }
    +  this.result_.runCount++;
    +  this.log('Running test: ' + this.curTest_.name);
    +  if (this.maybeFailTestEarly(this.curTest_)) {
    +    this.finishTestInvocation_();
    +    return;
    +  }
    +  goog.testing.TestCase.currentTestName = this.curTest_.name;
    +  this.invokeTestFunction_(
    +      this.setUp, this.safeRunTest_, this.safeTearDown_);
    +};
    +
    +
    +/**
    + * Calls the given test function, handling errors appropriately.
    + * @private
    + */
    +goog.testing.TestCase.prototype.safeRunTest_ = function() {
    +  this.invokeTestFunction_(
    +      goog.bind(this.curTest_.ref, this.curTest_.scope),
    +      this.safeTearDown_,
    +      this.safeTearDown_);
    +};
    +
    +
    +/**
    + * Calls {@link tearDown}, handling errors appropriately.
    + * @param {*=} opt_error Error associated with the test, if any.
    + * @private
    + */
    +goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
    +  if (arguments.length == 1) {
    +    this.doError(this.curTest_, opt_error);
    +  }
    +  this.invokeTestFunction_(
    +      this.tearDown, this.finishTestInvocation_, this.finishTestInvocation_);
    +};
    +
    +
    +/**
    + * Calls the given {@code fn}, then calls either {@code onSuccess} or
    + * {@code onFailure}, either synchronously or using promises, depending on
    + * {@code fn}'s return value.
    + *
    + * If {@code fn} throws an exception, {@code onFailure} is called immediately
    + * with the exception.
    + *
    + * If {@code fn} returns a promise, and the promise is eventually resolved,
    + * {@code onSuccess} is called with no arguments. If the promise is eventually
    + * rejected, {@code onFailure} is called with the rejection reason.
    + *
    + * Otherwise, if {@code fn} neither returns a promise nor throws an exception,
    + * {@code onSuccess} is called immediately with no arguments.
    + *
    + * {@code fn}, {@code onSuccess}, and {@code onFailure} are all called with
    + * the TestCase instance as the method receiver.
    + *
    + * @param {function()} fn The function to call.
    + * @param {function()} onSuccess Success callback.
    + * @param {function(*)} onFailure Failure callback.
    + * @private
    + */
    +goog.testing.TestCase.prototype.invokeTestFunction_ = function(
    +    fn, onSuccess, onFailure) {
    +  try {
    +    var retval = fn.call(this);
    +    if (goog.Thenable.isImplementedBy(retval) ||
    +        goog.isFunction(retval && retval['then'])) {
    +      var self = this;
    +      retval.then(
    +          function() {
    +            self.resetBatchTimeAfterPromise_();
    +            onSuccess.call(self);
    +          },
    +          function(e) {
    +            self.resetBatchTimeAfterPromise_();
    +            onFailure.call(self, e);
    +          });
    +    } else {
    +      onSuccess.call(this);
    +    }
    +  } catch (e) {
    +    onFailure.call(this, e);
    +  }
    +};
    +
    +
    +/**
    + * Resets the batch run timer. This should only be called after resolving a
    + * promise since Promise.then() has an implicit yield.
    + * @private
    + */
    +goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() {
    +  this.batchTime_ = this.now();
    +};
    +
    +
    +/**
    + * Finishes up bookkeeping for the current test function, and schedules
    + * the next test function to run, either immediately or asychronously.
    + * @param {*=} opt_error Optional error resulting from the test invocation.
    + * @private
    + */
    +goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
    +  if (arguments.length == 1) {
    +    this.doError(this.curTest_, opt_error);
    +  }
    +
    +  // If no errors have been recorded for the test, it is a success.
    +  if (!(this.curTest_.name in this.result_.resultsByName) ||
    +      !this.result_.resultsByName[this.curTest_.name].length) {
    +    this.doSuccess(this.curTest_);
    +  }
    +
    +  goog.testing.TestCase.currentTestName = null;
    +
    +  // If the test case has consumed too much time or stack space,
    +  // yield to avoid blocking the browser. Otherwise, proceed to the next test.
    +  if (this.depth_ > goog.testing.TestCase.MAX_STACK_DEPTH_ ||
    +      this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
    +    this.saveMessage('Breaking async');
    +    this.batchTime_ = this.now();
    +    this.depth_ = 0;
    +    this.timeout(goog.bind(this.runNextTest_, this), 0);
    +  } else {
    +    ++this.depth_;
    +    this.runNextTest_();
    +  }
    +};
    +
    +
    +/**
    + * Reorders the tests depending on the {@code order} field.
    + * @param {Array<goog.testing.TestCase.Test>} tests An array of tests to
    + *     reorder.
    + * @private
    + */
    +goog.testing.TestCase.prototype.orderTests_ = function(tests) {
    +  switch (this.order) {
    +    case goog.testing.TestCase.Order.RANDOM:
    +      // Fisher-Yates shuffle
    +      var i = tests.length;
    +      while (i > 1) {
    +        // goog.math.randomInt is inlined to reduce dependencies.
    +        var j = Math.floor(Math.random() * i); // exclusive
    +        i--;
    +        var tmp = tests[i];
    +        tests[i] = tests[j];
    +        tests[j] = tmp;
    +      }
    +      break;
    +
    +    case goog.testing.TestCase.Order.SORTED:
    +      tests.sort(function(t1, t2) {
    +        if (t1.name == t2.name) {
    +          return 0;
    +        }
    +        return t1.name < t2.name ? -1 : 1;
    +      });
    +      break;
    +
    +      // Do nothing for NATURAL.
    +  }
    +};
    +
    +
    +/**
    + * Gets list of objects that potentially contain test cases. For IE 8 and below,
    + * this is the global "this" (for properties set directly on the global this or
    + * window) and the RuntimeObject (for global variables and functions). For all
    + * other browsers, the array simply contains the global this.
    + *
    + * @param {string=} opt_prefix An optional prefix. If specified, only get things
    + *     under this prefix. Note that the prefix is only honored in IE, since it
    + *     supports the RuntimeObject:
    + *     http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    + *     TODO: Remove this option.
    + * @return {!Array<!Object>} A list of objects that should be inspected.
    + */
    +goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
    +  return goog.testing.TestCase.getGlobals(opt_prefix);
    +};
    +
    +
    +/**
    + * Gets list of objects that potentially contain test cases. For IE 8 and below,
    + * this is the global "this" (for properties set directly on the global this or
    + * window) and the RuntimeObject (for global variables and functions). For all
    + * other browsers, the array simply contains the global this.
    + *
    + * @param {string=} opt_prefix An optional prefix. If specified, only get things
    + *     under this prefix. Note that the prefix is only honored in IE, since it
    + *     supports the RuntimeObject:
    + *     http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
    + *     TODO: Remove this option.
    + * @return {!Array<!Object>} A list of objects that should be inspected.
    + */
    +goog.testing.TestCase.getGlobals = function(opt_prefix) {
    +  // Look in the global scope for most browsers, on IE we use the little known
    +  // RuntimeObject which holds references to all globals. We reference this
    +  // via goog.global so that there isn't an aliasing that throws an exception
    +  // in Firefox.
    +  return typeof goog.global['RuntimeObject'] != 'undefined' ?
    +      [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
    +      [goog.global];
    +};
    +
    +
    +/**
    + * Gets called before any tests are executed.  Can be overridden to set up the
    + * environment for the whole test case.
    + */
    +goog.testing.TestCase.prototype.setUpPage = function() {};
    +
    +
    +/**
    + * Gets called after all tests have been executed.  Can be overridden to tear
    + * down the entire test case.
    + */
    +goog.testing.TestCase.prototype.tearDownPage = function() {};
    +
    +
    +/**
    + * Gets called before every goog.testing.TestCase.Test is been executed. Can be
    + * overridden to add set up functionality to each test.
    + */
    +goog.testing.TestCase.prototype.setUp = function() {};
    +
    +
    +/**
    + * Gets called after every goog.testing.TestCase.Test has been executed. Can be
    + * overriden to add tear down functionality to each test.
    + */
    +goog.testing.TestCase.prototype.tearDown = function() {};
    +
    +
    +/**
    + * @return {string} The function name prefix used to auto-discover tests.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
    +  return 'test';
    +};
    +
    +
    +/**
    + * @return {number} Time since the last batch of tests was started.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.getBatchTime = function() {
    +  return this.batchTime_;
    +};
    +
    +
    +/**
    + * @param {number} batchTime Time since the last batch of tests was started.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
    +  this.batchTime_ = batchTime;
    +};
    +
    +
    +/**
    + * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
    + *     function.
    + * @param {string} name The name of the function.
    + * @param {function() : void} ref The auto-discovered function.
    + * @return {!goog.testing.TestCase.Test} The newly created test.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction =
    +    function(name, ref) {
    +  return new goog.testing.TestCase.Test(name, ref, goog.global);
    +};
    +
    +
    +/**
    + * Adds any functions defined in the global scope that correspond to
    + * lifecycle events for the test case. Overrides setUp, tearDown, setUpPage,
    + * tearDownPage and runTests if they are defined.
    + */
    +goog.testing.TestCase.prototype.autoDiscoverLifecycle = function() {
    +  if (goog.global['setUp']) {
    +    this.setUp = goog.bind(goog.global['setUp'], goog.global);
    +  }
    +  if (goog.global['tearDown']) {
    +    this.tearDown = goog.bind(goog.global['tearDown'], goog.global);
    +  }
    +  if (goog.global['setUpPage']) {
    +    this.setUpPage = goog.bind(goog.global['setUpPage'], goog.global);
    +  }
    +  if (goog.global['tearDownPage']) {
    +    this.tearDownPage = goog.bind(goog.global['tearDownPage'], goog.global);
    +  }
    +  if (goog.global['runTests']) {
    +    this.runTests = goog.bind(goog.global['runTests'], goog.global);
    +  }
    +  if (goog.global['shouldRunTests']) {
    +    this.shouldRunTests = goog.bind(goog.global['shouldRunTests'], goog.global);
    +  }
    +};
    +
    +
    +/**
    + * Adds any functions defined in the global scope that are prefixed with "test"
    + * to the test case.
    + */
    +goog.testing.TestCase.prototype.autoDiscoverTests = function() {
    +  var prefix = this.getAutoDiscoveryPrefix();
    +  var testSources = this.getGlobals(prefix);
    +
    +  var foundTests = [];
    +
    +  for (var i = 0; i < testSources.length; i++) {
    +    var testSource = testSources[i];
    +    for (var name in testSource) {
    +      if ((new RegExp('^' + prefix)).test(name)) {
    +        var ref;
    +        try {
    +          ref = testSource[name];
    +        } catch (ex) {
    +          // NOTE(brenneman): When running tests from a file:// URL on Firefox
    +          // 3.5 for Windows, any reference to goog.global.sessionStorage raises
    +          // an "Operation is not supported" exception. Ignore any exceptions
    +          // raised by simply accessing global properties.
    +          ref = undefined;
    +        }
    +
    +        if (goog.isFunction(ref)) {
    +          foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
    +        }
    +      }
    +    }
    +  }
    +
    +  this.orderTests_(foundTests);
    +
    +  for (var i = 0; i < foundTests.length; i++) {
    +    this.add(foundTests[i]);
    +  }
    +
    +  this.log(this.getCount() + ' tests auto-discovered');
    +
    +  // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
    +  // currently override autoDiscoverTests and expect lifecycle events to be
    +  // registered as a part of this call.
    +  this.autoDiscoverLifecycle();
    +};
    +
    +
    +/**
    + * Checks to see if the test should be marked as failed before it is run.
    + *
    + * If there was an error in setUpPage, we treat that as a failure for all tests
    + * and mark them all as having failed.
    + *
    + * @param {goog.testing.TestCase.Test} testCase The current test case.
    + * @return {boolean} Whether the test was marked as failed.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
    +  if (this.exceptionBeforeTest) {
    +    // We just use the first error to report an error on a failed test.
    +    this.curTest_.name = 'setUpPage for ' + this.curTest_.name;
    +    this.doError(this.curTest_, this.exceptionBeforeTest);
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Cycles through the tests, yielding asynchronously if the execution time
    + * execeeds {@link #maxRunTime}. In particular, there is no guarantee that
    + * the test case has finished execution once this method has returned.
    + * To be notified when the test case has finished execution, use
    + * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
    + */
    +goog.testing.TestCase.prototype.cycleTests = function() {
    +  this.saveMessage('Start');
    +  this.batchTime_ = this.now();
    +  if (this.running) {
    +    this.runNextTestCallback_ = goog.nullFunction;
    +    // Kick off the tests. runNextTest_ will schedule all of the tests,
    +    // using a mixture of synchronous and asynchronous strategies.
    +    this.runNextTest_();
    +  }
    +};
    +
    +
    +/**
    + * Counts the number of files that were loaded for dependencies that are
    + * required to run the test.
    + * @return {number} The number of files loaded.
    + * @private
    + */
    +goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
    +  var scripts = document.getElementsByTagName('script');
    +  var count = 0;
    +  for (var i = 0, n = scripts.length; i < n; i++) {
    +    if (scripts[i].src) {
    +      count++;
    +    }
    +  }
    +  return count;
    +};
    +
    +
    +/**
    + * Calls a function after a delay, using the protected timeout.
    + * @param {Function} fn The function to call.
    + * @param {number} time Delay in milliseconds.
    + * @return {number} The timeout id.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.timeout = function(fn, time) {
    +  // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
    +  // would result in an Illegal Invocation error. The method must be executed
    +  // with the global context.
    +  var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
    +  return protectedSetTimeout(fn, time);
    +};
    +
    +
    +/**
    + * Clears a timeout created by {@code this.timeout()}.
    + * @param {number} id A timeout id.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.clearTimeout = function(id) {
    +  // NOTE: see execution note for protectedSetTimeout above.
    +  var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
    +  protectedClearTimeout(id);
    +};
    +
    +
    +/**
    + * @return {number} The current time in milliseconds, don't use goog.now as some
    + *     tests override it.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.now = function() {
    +  // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    +  var protectedDate = goog.testing.TestCase.protectedDate_;
    +  return new protectedDate().getTime();
    +};
    +
    +
    +/**
    + * Returns the current time.
    + * @return {string} HH:MM:SS.
    + * @private
    + */
    +goog.testing.TestCase.prototype.getTimeStamp_ = function() {
    +  // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
    +  var protectedDate = goog.testing.TestCase.protectedDate_;
    +  var d = new protectedDate();
    +
    +  // Ensure millis are always 3-digits
    +  var millis = '00' + d.getMilliseconds();
    +  millis = millis.substr(millis.length - 3);
    +
    +  return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
    +         this.pad_(d.getSeconds()) + '.' + millis;
    +};
    +
    +
    +/**
    + * Pads a number to make it have a leading zero if it's less than 10.
    + * @param {number} number The number to pad.
    + * @return {string} The resulting string.
    + * @private
    + */
    +goog.testing.TestCase.prototype.pad_ = function(number) {
    +  return number < 10 ? '0' + number : String(number);
    +};
    +
    +
    +/**
    + * Trims a path to be only that after google3.
    + * @param {string} path The path to trim.
    + * @return {string} The resulting string.
    + * @private
    + */
    +goog.testing.TestCase.prototype.trimPath_ = function(path) {
    +  return path.substring(path.indexOf('google3') + 8);
    +};
    +
    +
    +/**
    + * Handles a test that passed.
    + * @param {goog.testing.TestCase.Test} test The test that passed.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.doSuccess = function(test) {
    +  this.result_.successCount++;
    +  // An empty list of error messages indicates that the test passed.
    +  // If we already have a failure for this test, do not set to empty list.
    +  if (!(test.name in this.result_.resultsByName)) {
    +    this.result_.resultsByName[test.name] = [];
    +  }
    +  var message = test.name + ' : PASSED';
    +  this.saveMessage(message);
    +  this.log(message);
    +};
    +
    +
    +/**
    + * Handles a test that failed.
    + * @param {goog.testing.TestCase.Test} test The test that failed.
    + * @param {*=} opt_e The exception object associated with the
    + *     failure or a string.
    + * @protected
    + */
    +goog.testing.TestCase.prototype.doError = function(test, opt_e) {
    +  var message = test.name + ' : FAILED';
    +  this.log(message);
    +  this.saveMessage(message);
    +  var err = this.logError(test.name, opt_e);
    +  this.result_.errors.push(err);
    +  if (test.name in this.result_.resultsByName) {
    +    this.result_.resultsByName[test.name].push(err.toString());
    +  } else {
    +    this.result_.resultsByName[test.name] = [err.toString()];
    +  }
    +};
    +
    +
    +/**
    + * @param {string} name Failed test name.
    + * @param {*=} opt_e The exception object associated with the
    + *     failure or a string.
    + * @return {!goog.testing.TestCase.Error} Error object.
    + */
    +goog.testing.TestCase.prototype.logError = function(name, opt_e) {
    +  var errMsg = null;
    +  var stack = null;
    +  if (opt_e) {
    +    this.log(opt_e);
    +    if (goog.isString(opt_e)) {
    +      errMsg = opt_e;
    +    } else {
    +      errMsg = opt_e.message || opt_e.description || opt_e.toString();
    +      stack = opt_e.stack ? goog.testing.stacktrace.canonicalize(opt_e.stack) :
    +          opt_e['stackTrace'];
    +    }
    +  } else {
    +    errMsg = 'An unknown error occurred';
    +  }
    +  var err = new goog.testing.TestCase.Error(name, errMsg, stack);
    +
    +  // Avoid double logging.
    +  if (!opt_e || !opt_e['isJsUnitException'] ||
    +      !opt_e['loggedJsUnitException']) {
    +    this.saveMessage(err.toString());
    +  }
    +  if (opt_e && opt_e['isJsUnitException']) {
    +    opt_e['loggedJsUnitException'] = true;
    +  }
    +
    +  return err;
    +};
    +
    +
    +
    +/**
    + * A class representing a single test function.
    + * @param {string} name The test name.
    + * @param {Function} ref Reference to the test function.
    + * @param {Object=} opt_scope Optional scope that the test function should be
    + *     called in.
    + * @constructor
    + */
    +goog.testing.TestCase.Test = function(name, ref, opt_scope) {
    +  /**
    +   * The name of the test.
    +   * @type {string}
    +   */
    +  this.name = name;
    +
    +  /**
    +   * Reference to the test function.
    +   * @type {Function}
    +   */
    +  this.ref = ref;
    +
    +  /**
    +   * Scope that the test function should be called in.
    +   * @type {Object}
    +   */
    +  this.scope = opt_scope || null;
    +};
    +
    +
    +/**
    + * Executes the test function.
    + * @package
    + */
    +goog.testing.TestCase.Test.prototype.execute = function() {
    +  this.ref.call(this.scope);
    +};
    +
    +
    +
    +/**
    + * A class for representing test results.  A bag of public properties.
    + * @param {goog.testing.TestCase} testCase The test case that owns this result.
    + * @constructor
    + * @final
    + */
    +goog.testing.TestCase.Result = function(testCase) {
    +  /**
    +   * The test case that owns this result.
    +   * @type {goog.testing.TestCase}
    +   * @private
    +   */
    +  this.testCase_ = testCase;
    +
    +  /**
    +   * Total number of tests that should have been run.
    +   * @type {number}
    +   */
    +  this.totalCount = 0;
    +
    +  /**
    +   * Total number of tests that were actually run.
    +   * @type {number}
    +   */
    +  this.runCount = 0;
    +
    +  /**
    +   * Number of successful tests.
    +   * @type {number}
    +   */
    +  this.successCount = 0;
    +
    +  /**
    +   * The amount of time the tests took to run.
    +   * @type {number}
    +   */
    +  this.runTime = 0;
    +
    +  /**
    +   * The number of files loaded to run this test.
    +   * @type {number}
    +   */
    +  this.numFilesLoaded = 0;
    +
    +  /**
    +   * Whether this test case was suppressed by shouldRunTests() returning false.
    +   * @type {boolean}
    +   */
    +  this.testSuppressed = false;
    +
    +  /**
    +   * Test results for each test that was run. The test name is always added
    +   * as the key in the map, and the array of strings is an optional list
    +   * of failure messages. If the array is empty, the test passed. Otherwise,
    +   * the test failed.
    +   * @type {!Object<string, !Array<string>>}
    +   */
    +  this.resultsByName = {};
    +
    +  /**
    +   * Errors encountered while running the test.
    +   * @type {!Array<goog.testing.TestCase.Error>}
    +   */
    +  this.errors = [];
    +
    +  /**
    +   * Messages to show the user after running the test.
    +   * @type {!Array<string>}
    +   */
    +  this.messages = [];
    +
    +  /**
    +   * Whether the tests have completed.
    +   * @type {boolean}
    +   */
    +  this.complete = false;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the test was successful.
    + */
    +goog.testing.TestCase.Result.prototype.isSuccess = function() {
    +  return this.complete && this.errors.length == 0;
    +};
    +
    +
    +/**
    + * @return {string} A summary of the tests, including total number of tests that
    + *     passed, failed, and the time taken.
    + */
    +goog.testing.TestCase.Result.prototype.getSummary = function() {
    +  var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
    +      this.runTime + 'ms.\n';
    +  if (this.testSuppressed) {
    +    summary += 'Tests not run because shouldRunTests() returned false.';
    +  } else {
    +    var failures = this.totalCount - this.successCount;
    +    var suppressionMessage = '';
    +
    +    var countOfRunTests = this.testCase_.getActuallyRunCount();
    +    if (countOfRunTests) {
    +      failures = countOfRunTests - this.successCount;
    +      suppressionMessage = ', ' +
    +          (this.totalCount - countOfRunTests) + ' suppressed by querystring';
    +    }
    +    summary += this.successCount + ' passed, ' +
    +        failures + ' failed' + suppressionMessage + '.\n' +
    +        Math.round(this.runTime / this.runCount) + ' ms/test. ' +
    +        this.numFilesLoaded + ' files loaded.';
    +  }
    +
    +  return summary;
    +};
    +
    +
    +/**
    + * Initializes the given test case with the global test runner 'G_testRunner'.
    + * @param {goog.testing.TestCase} testCase The test case to install.
    + */
    +goog.testing.TestCase.initializeTestRunner = function(testCase) {
    +  testCase.autoDiscoverTests();
    +  var gTestRunner = goog.global['G_testRunner'];
    +  if (gTestRunner) {
    +    gTestRunner['initialize'](testCase);
    +  } else {
    +    throw Error('G_testRunner is undefined. Please ensure goog.testing.jsunit' +
    +        ' is included.');
    +  }
    +};
    +
    +
    +
    +/**
    + * A class representing an error thrown by the test
    + * @param {string} source The name of the test which threw the error.
    + * @param {string} message The error message.
    + * @param {string=} opt_stack A string showing the execution stack.
    + * @constructor
    + * @final
    + */
    +goog.testing.TestCase.Error = function(source, message, opt_stack) {
    +  /**
    +   * The name of the test which threw the error.
    +   * @type {string}
    +   */
    +  this.source = source;
    +
    +  /**
    +   * Reference to the test function.
    +   * @type {string}
    +   */
    +  this.message = message;
    +
    +  /**
    +   * Scope that the test function should be called in.
    +   * @type {?string}
    +   */
    +  this.stack = opt_stack || null;
    +};
    +
    +
    +/**
    + * Returns a string representing the error object.
    + * @return {string} A string representation of the error.
    + * @override
    + */
    +goog.testing.TestCase.Error.prototype.toString = function() {
    +  return 'ERROR in ' + this.source + '\n' +
    +      this.message + (this.stack ? '\n' + this.stack : '');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/testcase_test.js b/mapbox-gl/closure-library/closure/goog/testing/testcase_test.js
    new file mode 100644
    index 0000000000..ff70bd3b1f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/testcase_test.js
    @@ -0,0 +1,276 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.TestCaseTest');
    +goog.setTestOnly('goog.testing.TestCaseTest');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.testing.TestCase');
    +goog.require('goog.testing.jsunit');
    +
    +
    +// Dual of fail().
    +var ok = function() { assertTrue(true); };
    +
    +// Native Promise-based equivalent of ok().
    +var okPromise = function() { return Promise.resolve(null); };
    +
    +// Native Promise-based equivalent of fail().
    +var failPromise = function() { return Promise.reject(null); };
    +
    +// goog.Promise-based equivalent of ok().
    +var okGoogPromise = function() { return goog.Promise.resolve(null); };
    +
    +// goog.Promise-based equivalent of fail().
    +var failGoogPromise = function() { return goog.Promise.reject(null); };
    +
    +function testEmptyTestCase() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.runTests();
    +  assertTrue(testCase.isSuccess());
    +  var result = testCase.getResult();
    +  assertTrue(result.complete);
    +  assertEquals(0, result.totalCount);
    +  assertEquals(0, result.runCount);
    +  assertEquals(0, result.successCount);
    +  assertEquals(0, result.errors.length);
    +}
    +
    +function testEmptyTestCaseReturningPromise() {
    +  return new goog.testing.TestCase().runTestsReturningPromise().
    +      then(function(result) {
    +        assertTrue(result.complete);
    +        assertEquals(0, result.totalCount);
    +        assertEquals(0, result.runCount);
    +        assertEquals(0, result.successCount);
    +        assertEquals(0, result.errors.length);
    +      });
    +}
    +
    +function testTestCase_SyncSuccess() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', ok);
    +  testCase.runTests();
    +  assertTrue(testCase.isSuccess());
    +  var result = testCase.getResult();
    +  assertTrue(result.complete);
    +  assertEquals(1, result.totalCount);
    +  assertEquals(1, result.runCount);
    +  assertEquals(1, result.successCount);
    +  assertEquals(0, result.errors.length);
    +}
    +
    +function testTestCaseReturningPromise_SyncSuccess() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', ok);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(0, result.errors.length);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_GoogPromiseResolve() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okGoogPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(0, result.errors.length);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_PromiseResolve() {
    +  if (!('Promise' in goog.global)) {
    +    return;
    +  }
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(0, result.errors.length);
    +  });
    +}
    +
    +function testTestCase_SyncFailure() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', fail);
    +  testCase.runTests();
    +  assertFalse(testCase.isSuccess());
    +  var result = testCase.getResult();
    +  assertTrue(result.complete);
    +  assertEquals(1, result.totalCount);
    +  assertEquals(1, result.runCount);
    +  assertEquals(0, result.successCount);
    +  assertEquals(1, result.errors.length);
    +  assertEquals('foo', result.errors[0].source);
    +}
    +
    +function testTestCaseReturningPromise_SyncFailure() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', fail);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertFalse(testCase.isSuccess());
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(0, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('foo', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_GoogPromiseReject() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', failGoogPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertFalse(testCase.isSuccess());
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(0, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('foo', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_PromiseReject() {
    +  if (!('Promise' in goog.global)) {
    +    return;
    +  }
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', failPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertFalse(testCase.isSuccess());
    +    assertTrue(result.complete);
    +    assertEquals(1, result.totalCount);
    +    assertEquals(1, result.runCount);
    +    assertEquals(0, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('foo', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCase_SyncSuccess_SyncFailure() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', ok);
    +  testCase.addNewTest('bar', fail);
    +  testCase.runTests();
    +  assertFalse(testCase.isSuccess());
    +  var result = testCase.getResult();
    +  assertTrue(result.complete);
    +  assertEquals(2, result.totalCount);
    +  assertEquals(2, result.runCount);
    +  assertEquals(1, result.successCount);
    +  assertEquals(1, result.errors.length);
    +  assertEquals('bar', result.errors[0].source);
    +}
    +
    +function testTestCaseReturningPromise_SyncSuccess_SyncFailure() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', ok);
    +  testCase.addNewTest('bar', fail);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(2, result.totalCount);
    +    assertEquals(2, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('bar', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_GoogPromiseResolve_GoogPromiseReject() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okGoogPromise);
    +  testCase.addNewTest('bar', failGoogPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(2, result.totalCount);
    +    assertEquals(2, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('bar', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_PromiseResolve_PromiseReject() {
    +  if (!('Promise' in goog.global)) {
    +    return;
    +  }
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okPromise);
    +  testCase.addNewTest('bar', failPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(2, result.totalCount);
    +    assertEquals(2, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('bar', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_PromiseResolve_GoogPromiseReject() {
    +  if (!('Promise' in goog.global)) {
    +    return;
    +  }
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okPromise);
    +  testCase.addNewTest('bar', failGoogPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(2, result.totalCount);
    +    assertEquals(2, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('bar', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseReturningPromise_GoogPromiseResolve_PromiseReject() {
    +  if (!('Promise' in goog.global)) {
    +    return;
    +  }
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', okGoogPromise);
    +  testCase.addNewTest('bar', failPromise);
    +  return testCase.runTestsReturningPromise().then(function(result) {
    +    assertTrue(result.complete);
    +    assertEquals(2, result.totalCount);
    +    assertEquals(2, result.runCount);
    +    assertEquals(1, result.successCount);
    +    assertEquals(1, result.errors.length);
    +    assertEquals('bar', result.errors[0].source);
    +  });
    +}
    +
    +function testTestCaseNeverRun() {
    +  var testCase = new goog.testing.TestCase();
    +  testCase.addNewTest('foo', fail);
    +  // Missing testCase.runTests()
    +  var result = testCase.getResult();
    +  assertFalse(result.complete);
    +  assertEquals(0, result.totalCount);
    +  assertEquals(0, result.runCount);
    +  assertEquals(0, result.successCount);
    +  assertEquals(0, result.errors.length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/testqueue.js b/mapbox-gl/closure-library/closure/goog/testing/testqueue.js
    new file mode 100644
    index 0000000000..4ead784601
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/testqueue.js
    @@ -0,0 +1,66 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generic queue for writing unit tests.
    + */
    +
    +goog.provide('goog.testing.TestQueue');
    +
    +
    +
    +/**
    + * Generic queue for writing unit tests
    + * @constructor
    + */
    +goog.testing.TestQueue = function() {
    +  /**
    +   * Events that have accumulated
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.events_ = [];
    +};
    +
    +
    +/**
    + * Adds a new event onto the queue.
    + * @param {Object} event The event to queue.
    + */
    +goog.testing.TestQueue.prototype.enqueue = function(event) {
    +  this.events_.push(event);
    +};
    +
    +
    +/**
    + * Returns whether the queue is empty.
    + * @return {boolean} Whether the queue is empty.
    + */
    +goog.testing.TestQueue.prototype.isEmpty = function() {
    +  return this.events_.length == 0;
    +};
    +
    +
    +/**
    + * Gets the next event from the queue. Throws an exception if the queue is
    + * empty.
    + * @param {string=} opt_comment Comment if the queue is empty.
    + * @return {Object} The next event from the queue.
    + */
    +goog.testing.TestQueue.prototype.dequeue = function(opt_comment) {
    +  if (this.isEmpty()) {
    +    throw Error('Handler is empty: ' + opt_comment);
    +  }
    +  return this.events_.shift();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/testrunner.js b/mapbox-gl/closure-library/closure/goog/testing/testrunner.js
    new file mode 100644
    index 0000000000..a05041f4e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/testrunner.js
    @@ -0,0 +1,439 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The test runner is a singleton object that is used to execute
    + * a goog.testing.TestCases, display the results, and expose the results to
    + * Selenium for automation.  If a TestCase hasn't been registered with the
    + * runner by the time window.onload occurs, the testRunner will try to auto-
    + * discover JsUnit style test pages.
    + *
    + * The hooks for selenium are (see http://go/selenium-hook-setup):-
    + *  - Boolean G_testRunner.isFinished()
    + *  - Boolean G_testRunner.isSuccess()
    + *  - String G_testRunner.getReport()
    + *  - number G_testRunner.getRunTime()
    + *  - Object<string, Array<string>> G_testRunner.getTestResults()
    + *
    + * Testing code should not have dependencies outside of goog.testing so as to
    + * reduce the chance of masking missing dependencies.
    + *
    + */
    +
    +goog.provide('goog.testing.TestRunner');
    +
    +goog.require('goog.testing.TestCase');
    +
    +
    +
    +/**
    + * Construct a test runner.
    + *
    + * NOTE(user): This is currently pretty weird, I'm essentially trying to
    + * create a wrapper that the Selenium test can hook into to query the state of
    + * the running test case, while making goog.testing.TestCase general.
    + *
    + * @constructor
    + */
    +goog.testing.TestRunner = function() {
    +  /**
    +   * Errors that occurred in the window.
    +   * @type {Array<string>}
    +   */
    +  this.errors = [];
    +};
    +
    +
    +/**
    + * Reference to the active test case.
    + * @type {goog.testing.TestCase?}
    + */
    +goog.testing.TestRunner.prototype.testCase = null;
    +
    +
    +/**
    + * Whether the test runner has been initialized yet.
    + * @type {boolean}
    + */
    +goog.testing.TestRunner.prototype.initialized = false;
    +
    +
    +/**
    + * Element created in the document to add test results to.
    + * @type {Element}
    + * @private
    + */
    +goog.testing.TestRunner.prototype.logEl_ = null;
    +
    +
    +/**
    + * Function to use when filtering errors.
    + * @type {(function(string))?}
    + * @private
    + */
    +goog.testing.TestRunner.prototype.errorFilter_ = null;
    +
    +
    +/**
    + * Whether an empty test case counts as an error.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.TestRunner.prototype.strict_ = true;
    +
    +
    +/**
    + * Initializes the test runner.
    + * @param {goog.testing.TestCase} testCase The test case to initialize with.
    + */
    +goog.testing.TestRunner.prototype.initialize = function(testCase) {
    +  if (this.testCase && this.testCase.running) {
    +    throw Error('The test runner is already waiting for a test to complete');
    +  }
    +  this.testCase = testCase;
    +  this.initialized = true;
    +};
    +
    +
    +/**
    + * By default, the test runner is strict, and fails if it runs an empty
    + * test case.
    + * @param {boolean} strict Whether the test runner should fail on an empty
    + *     test case.
    + */
    +goog.testing.TestRunner.prototype.setStrict = function(strict) {
    +  this.strict_ = strict;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the test runner should fail on an empty
    + *     test case.
    + */
    +goog.testing.TestRunner.prototype.isStrict = function() {
    +  return this.strict_;
    +};
    +
    +
    +/**
    + * Returns true if the test runner is initialized.
    + * Used by Selenium Hooks.
    + * @return {boolean} Whether the test runner is active.
    + */
    +goog.testing.TestRunner.prototype.isInitialized = function() {
    +  return this.initialized;
    +};
    +
    +
    +/**
    + * Returns true if the test runner is finished.
    + * Used by Selenium Hooks.
    + * @return {boolean} Whether the test runner is active.
    + */
    +goog.testing.TestRunner.prototype.isFinished = function() {
    +  return this.errors.length > 0 ||
    +      this.initialized && !!this.testCase && this.testCase.started &&
    +      !this.testCase.running;
    +};
    +
    +
    +/**
    + * Returns true if the test case didn't fail.
    + * Used by Selenium Hooks.
    + * @return {boolean} Whether the current test returned successfully.
    + */
    +goog.testing.TestRunner.prototype.isSuccess = function() {
    +  return !this.hasErrors() && !!this.testCase && this.testCase.isSuccess();
    +};
    +
    +
    +/**
    + * Returns true if the test case runner has errors that were caught outside of
    + * the test case.
    + * @return {boolean} Whether there were JS errors.
    + */
    +goog.testing.TestRunner.prototype.hasErrors = function() {
    +  return this.errors.length > 0;
    +};
    +
    +
    +/**
    + * Logs an error that occurred.  Used in the case of environment setting up
    + * an onerror handler.
    + * @param {string} msg Error message.
    + */
    +goog.testing.TestRunner.prototype.logError = function(msg) {
    +  if (!this.errorFilter_ || this.errorFilter_.call(null, msg)) {
    +    this.errors.push(msg);
    +  }
    +};
    +
    +
    +/**
    + * Log failure in current running test.
    + * @param {Error} ex Exception.
    + */
    +goog.testing.TestRunner.prototype.logTestFailure = function(ex) {
    +  var testName = /** @type {string} */ (goog.testing.TestCase.currentTestName);
    +  if (this.testCase) {
    +    this.testCase.logError(testName, ex);
    +  } else {
    +    // NOTE: Do not forget to log the original exception raised.
    +    throw new Error('Test runner not initialized with a test case. Original ' +
    +                    'exception: ' + ex.message);
    +  }
    +};
    +
    +
    +/**
    + * Sets a function to use as a filter for errors.
    + * @param {function(string)} fn Filter function.
    + */
    +goog.testing.TestRunner.prototype.setErrorFilter = function(fn) {
    +  this.errorFilter_ = fn;
    +};
    +
    +
    +/**
    + * Returns a report of the test case that ran.
    + * Used by Selenium Hooks.
    + * @param {boolean=} opt_verbose If true results will include data about all
    + *     tests, not just what failed.
    + * @return {string} A report summary of the test.
    + */
    +goog.testing.TestRunner.prototype.getReport = function(opt_verbose) {
    +  var report = [];
    +  if (this.testCase) {
    +    report.push(this.testCase.getReport(opt_verbose));
    +  }
    +  if (this.errors.length > 0) {
    +    report.push('JavaScript errors detected by test runner:');
    +    report.push.apply(report, this.errors);
    +    report.push('\n');
    +  }
    +  return report.join('\n');
    +};
    +
    +
    +/**
    + * Returns the amount of time it took for the test to run.
    + * Used by Selenium Hooks.
    + * @return {number} The run time, in milliseconds.
    + */
    +goog.testing.TestRunner.prototype.getRunTime = function() {
    +  return this.testCase ? this.testCase.getRunTime() : 0;
    +};
    +
    +
    +/**
    + * Returns the number of script files that were loaded in order to run the test.
    + * @return {number} The number of script files.
    + */
    +goog.testing.TestRunner.prototype.getNumFilesLoaded = function() {
    +  return this.testCase ? this.testCase.getNumFilesLoaded() : 0;
    +};
    +
    +
    +/**
    + * Executes a test case and prints the results to the window.
    + */
    +goog.testing.TestRunner.prototype.execute = function() {
    +  if (!this.testCase) {
    +    throw Error('The test runner must be initialized with a test case ' +
    +                'before execute can be called.');
    +  }
    +
    +  if (this.strict_ && this.testCase.getCount() == 0) {
    +    throw Error(
    +        'No tests found in given test case: ' +
    +        this.testCase.getName() + ' ' +
    +        'By default, the test runner fails if a test case has no tests. ' +
    +        'To modify this behavior, see goog.testing.TestRunner\'s ' +
    +        'setStrict() method, or G_testRunner.setStrict()');
    +  }
    +
    +  this.testCase.setCompletedCallback(goog.bind(this.onComplete_, this));
    +  if (goog.testing.TestRunner.shouldUsePromises_(this.testCase)) {
    +    this.testCase.runTestsReturningPromise();
    +  } else {
    +    this.testCase.runTests();
    +  }
    +};
    +
    +
    +/**
    + * @param {!goog.testing.TestCase} testCase
    + * @return {boolean}
    + * @private
    + */
    +goog.testing.TestRunner.shouldUsePromises_ = function(testCase) {
    +  return testCase.constructor === goog.testing.TestCase;
    +};
    +
    +
    +/**
    + * Writes the results to the document when the test case completes.
    + * @private
    + */
    +goog.testing.TestRunner.prototype.onComplete_ = function() {
    +  var log = this.testCase.getReport(true);
    +  if (this.errors.length > 0) {
    +    log += '\n' + this.errors.join('\n');
    +  }
    +
    +  if (!this.logEl_) {
    +    var el = document.getElementById('closureTestRunnerLog');
    +    if (el == null) {
    +      el = document.createElement('div');
    +      document.body.appendChild(el);
    +    }
    +    this.logEl_ = el;
    +  }
    +
    +  // Highlight the page to indicate the overall outcome.
    +  this.writeLog(log);
    +
    +  // TODO(chrishenry): Make this work with multiple test cases (b/8603638).
    +  var runAgainLink = document.createElement('a');
    +  runAgainLink.style.display = 'inline-block';
    +  runAgainLink.style.fontSize = 'small';
    +  runAgainLink.style.marginBottom = '16px';
    +  runAgainLink.href = '';
    +  runAgainLink.onclick = goog.bind(function() {
    +    this.execute();
    +    return false;
    +  }, this);
    +  runAgainLink.innerHTML = 'Run again without reloading';
    +  this.logEl_.appendChild(runAgainLink);
    +};
    +
    +
    +/**
    + * Writes a nicely formatted log out to the document.
    + * @param {string} log The string to write.
    + */
    +goog.testing.TestRunner.prototype.writeLog = function(log) {
    +  var lines = log.split('\n');
    +  for (var i = 0; i < lines.length; i++) {
    +    var line = lines[i];
    +    var color;
    +    var isFailOrError = /FAILED/.test(line) || /ERROR/.test(line);
    +    if (/PASSED/.test(line)) {
    +      color = 'darkgreen';
    +    } else if (isFailOrError) {
    +      color = 'darkred';
    +    } else {
    +      color = '#333';
    +    }
    +    var div = document.createElement('div');
    +    if (line.substr(0, 2) == '> ') {
    +      // The stack trace may contain links so it has to be interpreted as HTML.
    +      div.innerHTML = line;
    +    } else {
    +      div.appendChild(document.createTextNode(line));
    +    }
    +
    +    var testNameMatch =
    +        /(\S+) (\[[^\]]*] )?: (FAILED|ERROR|PASSED)/.exec(line);
    +    if (testNameMatch) {
    +      // Build a URL to run the test individually.  If this test was already
    +      // part of another subset test, we need to overwrite the old runTests
    +      // query parameter.  We also need to do this without bringing in any
    +      // extra dependencies, otherwise we could mask missing dependency bugs.
    +      var newSearch = 'runTests=' + testNameMatch[1];
    +      var search = window.location.search;
    +      if (search) {
    +        var oldTests = /runTests=([^&]*)/.exec(search);
    +        if (oldTests) {
    +          newSearch = search.substr(0, oldTests.index) +
    +                      newSearch +
    +                      search.substr(oldTests.index + oldTests[0].length);
    +        } else {
    +          newSearch = search + '&' + newSearch;
    +        }
    +      } else {
    +        newSearch = '?' + newSearch;
    +      }
    +      var href = window.location.href;
    +      var hash = window.location.hash;
    +      if (hash && hash.charAt(0) != '#') {
    +        hash = '#' + hash;
    +      }
    +      href = href.split('#')[0].split('?')[0] + newSearch + hash;
    +
    +      // Add the link.
    +      var a = document.createElement('A');
    +      a.innerHTML = '(run individually)';
    +      a.style.fontSize = '0.8em';
    +      a.style.color = '#888';
    +      a.href = href;
    +      div.appendChild(document.createTextNode(' '));
    +      div.appendChild(a);
    +    }
    +
    +    div.style.color = color;
    +    div.style.font = 'normal 100% monospace';
    +    div.style.wordWrap = 'break-word';
    +    if (i == 0) {
    +      // Highlight the first line as a header that indicates the test outcome.
    +      div.style.padding = '20px';
    +      div.style.marginBottom = '10px';
    +      if (isFailOrError) {
    +        div.style.border = '5px solid ' + color;
    +        div.style.backgroundColor = '#ffeeee';
    +      } else {
    +        div.style.border = '1px solid black';
    +        div.style.backgroundColor = '#eeffee';
    +      }
    +    }
    +
    +    try {
    +      div.style.whiteSpace = 'pre-wrap';
    +    } catch (e) {
    +      // NOTE(brenneman): IE raises an exception when assigning to pre-wrap.
    +      // Thankfully, it doesn't collapse whitespace when using monospace fonts,
    +      // so it will display correctly if we ignore the exception.
    +    }
    +
    +    if (i < 2) {
    +      div.style.fontWeight = 'bold';
    +    }
    +    this.logEl_.appendChild(div);
    +  }
    +};
    +
    +
    +/**
    + * Logs a message to the current test case.
    + * @param {string} s The text to output to the log.
    + */
    +goog.testing.TestRunner.prototype.log = function(s) {
    +  if (this.testCase) {
    +    this.testCase.log(s);
    +  }
    +};
    +
    +
    +// TODO(nnaze): Properly handle serving test results when multiple test cases
    +// are run.
    +/**
    + * @return {Object<string, !Array<string>>} A map of test names to a list of
    + * test failures (if any) to provide formatted data for the test runner.
    + */
    +goog.testing.TestRunner.prototype.getTestResults = function() {
    +  if (this.testCase) {
    +    return this.testCase.getTestResults();
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts.js b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts.js
    new file mode 100644
    index 0000000000..33b5b92477
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts.js
    @@ -0,0 +1,58 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Additional asserts for testing ControlRenderers.
    + *
    + * @author mkretzschmar@google.com (Martin Kretzschmar)
    + */
    +
    +goog.provide('goog.testing.ui.rendererasserts');
    +
    +goog.require('goog.testing.asserts');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +/**
    + * Assert that a control renderer constructor doesn't call getCssClass.
    + *
    + * @param {?function(new:goog.ui.ControlRenderer)} rendererClassUnderTest The
    + *     renderer constructor to test.
    + */
    +goog.testing.ui.rendererasserts.assertNoGetCssClassCallsInConstructor =
    +    function(rendererClassUnderTest) {
    +  var getCssClassCalls = 0;
    +
    +  /**
    +   * @constructor
    +   * @extends {goog.ui.ControlRenderer}
    +   * @final
    +   */
    +  function TestControlRenderer() {
    +    rendererClassUnderTest.call(this);
    +  }
    +  goog.inherits(TestControlRenderer, rendererClassUnderTest);
    +
    +  /** @override */
    +  TestControlRenderer.prototype.getCssClass = function() {
    +    getCssClassCalls++;
    +    return TestControlRenderer.superClass_.getCssClass.call(this);
    +  };
    +
    +  var testControlRenderer = new TestControlRenderer();
    +
    +  assertEquals('Constructors should not call getCssClass, ' +
    +      'getCustomRenderer must be able to override it post construction.',
    +      0, getCssClassCalls);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.html b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.html
    new file mode 100644
    index 0000000000..287b6382e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author: mkretzschmar@google.com (Martin Kretzschmar)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.ui.rendererasserts
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.ui.rendererassertsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.js b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.js
    new file mode 100644
    index 0000000000..b144c70e75
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererasserts_test.js
    @@ -0,0 +1,46 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.ui.rendererassertsTest');
    +goog.setTestOnly('goog.testing.ui.rendererassertsTest');
    +
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.ControlRenderer');
    +
    +function testSuccess() {
    +  function GoodRenderer() {}
    +
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(GoodRenderer);
    +}
    +
    +function testFailure() {
    +  function BadRenderer() {
    +    goog.ui.ControlRenderer.call(this);
    +    this.myClass = this.getCssClass();
    +  }
    +  goog.inherits(BadRenderer, goog.ui.ControlRenderer);
    +
    +  var ex = assertThrows(
    +      'Expected assertNoGetCssClassCallsInConstructor to fail.',
    +      function() {
    +        goog.testing.ui.rendererasserts.
    +            assertNoGetCssClassCallsInConstructor(BadRenderer);
    +      });
    +  assertTrue('Expected assertNoGetCssClassCallsInConstructor to throw a' +
    +      ' jsunit exception', ex.isJsUnitException);
    +  assertContains('getCssClass', ex.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/rendererharness.js b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererharness.js
    new file mode 100644
    index 0000000000..43d955cf7f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/rendererharness.js
    @@ -0,0 +1,177 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +// All Rights Reserved
    +
    +/**
    + * @fileoverview A driver for testing renderers.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.testing.ui.RendererHarness');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.dom');
    +
    +
    +
    +/**
    + * A driver for testing renderers.
    + *
    + * @param {goog.ui.ControlRenderer} renderer A renderer to test.
    + * @param {Element} renderParent The parent of the element where controls will
    + *     be rendered.
    + * @param {Element} decorateParent The parent of the element where controls will
    + *     be decorated.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.testing.ui.RendererHarness = function(renderer, renderParent,
    +    decorateParent) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The renderer under test.
    +   * @type {goog.ui.ControlRenderer}
    +   * @private
    +   */
    +  this.renderer_ = renderer;
    +
    +  /**
    +   * The parent of the element where controls will be rendered.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.renderParent_ = renderParent;
    +
    +  /**
    +   * The original HTML of the render element.
    +   * @type {string}
    +   * @private
    +   */
    +  this.renderHtml_ = renderParent.innerHTML;
    +
    +  /**
    +   * Teh parent of the element where controls will be decorated.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.decorateParent_ = decorateParent;
    +
    +  /**
    +   * The original HTML of the decorated element.
    +   * @type {string}
    +   * @private
    +   */
    +  this.decorateHtml_ = decorateParent.innerHTML;
    +};
    +goog.inherits(goog.testing.ui.RendererHarness, goog.Disposable);
    +
    +
    +/**
    + * A control to create by decoration.
    + * @type {goog.ui.Control}
    + * @private
    + */
    +goog.testing.ui.RendererHarness.prototype.decorateControl_;
    +
    +
    +/**
    + * A control to create by rendering.
    + * @type {goog.ui.Control}
    + * @private
    + */
    +goog.testing.ui.RendererHarness.prototype.renderControl_;
    +
    +
    +/**
    + * Whether all the necessary assert methods have been called.
    + * @type {boolean}
    + * @private
    + */
    +goog.testing.ui.RendererHarness.prototype.verified_ = false;
    +
    +
    +/**
    + * Attach a control and render its DOM.
    + * @param {goog.ui.Control} control A control.
    + * @return {Element} The element created.
    + */
    +goog.testing.ui.RendererHarness.prototype.attachControlAndRender =
    +    function(control) {
    +  this.renderControl_ = control;
    +
    +  control.setRenderer(this.renderer_);
    +  control.render(this.renderParent_);
    +  return control.getElement();
    +};
    +
    +
    +/**
    + * Attach a control and decorate the element given in the constructor.
    + * @param {goog.ui.Control} control A control.
    + * @return {Element} The element created.
    + */
    +goog.testing.ui.RendererHarness.prototype.attachControlAndDecorate =
    +    function(control) {
    +  this.decorateControl_ = control;
    +
    +  control.setRenderer(this.renderer_);
    +
    +  var child = this.decorateParent_.firstChild;
    +  assertEquals('The decorated node must be an element',
    +               goog.dom.NodeType.ELEMENT, child.nodeType);
    +  control.decorate(/** @type {!Element} */ (child));
    +  return control.getElement();
    +};
    +
    +
    +/**
    + * Assert that the rendered element and the decorated element match.
    + */
    +goog.testing.ui.RendererHarness.prototype.assertDomMatches = function() {
    +  assert('Both elements were not generated',
    +         !!(this.renderControl_ && this.decorateControl_));
    +  goog.testing.dom.assertHtmlMatches(
    +      this.renderControl_.getElement().innerHTML,
    +      this.decorateControl_.getElement().innerHTML);
    +  this.verified_ = true;
    +};
    +
    +
    +/**
    + * Destroy the harness, verifying that all assertions had been checked.
    + * @override
    + * @protected
    + */
    +goog.testing.ui.RendererHarness.prototype.disposeInternal = function() {
    +  // If the harness was not verified appropriately, throw an exception.
    +  assert('Expected assertDomMatches to be called',
    +         this.verified_ || !this.renderControl_ || !this.decorateControl_);
    +
    +  if (this.decorateControl_) {
    +    this.decorateControl_.dispose();
    +  }
    +  if (this.renderControl_) {
    +    this.renderControl_.dispose();
    +  }
    +
    +  this.renderParent_.innerHTML = this.renderHtml_;
    +  this.decorateParent_.innerHTML = this.decorateHtml_;
    +
    +  goog.testing.ui.RendererHarness.superClass_.disposeInternal.call(this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/style.js b/mapbox-gl/closure-library/closure/goog/testing/ui/style.js
    new file mode 100644
    index 0000000000..2b162c4fd5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/style.js
    @@ -0,0 +1,140 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tools for testing Closure renderers against static markup
    + * spec pages.
    + *
    + */
    +
    +goog.provide('goog.testing.ui.style');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.asserts');
    +
    +
    +/**
    + * Uses document.write to add an iFrame to the page with the reference path in
    + * the src attribute. Used for loading an html file containing reference
    + * structures to test against into the page. Should be called within the body of
    + * the jsunit test page.
    + * @param {string} referencePath A path to a reference HTML file.
    + */
    +goog.testing.ui.style.writeReferenceFrame = function(referencePath) {
    +  document.write('<iframe id="reference" name="reference" ' +
    +      'src="' + referencePath + '"></iframe>');
    +};
    +
    +
    +/**
    + * Returns a reference to the first element child of a node with the given id
    + * from the page loaded into the reference iFrame. Used to retrieve a particular
    + * reference DOM structure to test against.
    + * @param {string} referenceId The id of a container element for a reference
    + *   structure in the reference page.
    + * @return {Node} The root element of the reference structure.
    + */
    +goog.testing.ui.style.getReferenceNode = function(referenceId) {
    +  return goog.dom.getFirstElementChild(
    +      window.frames['reference'].document.getElementById(referenceId));
    +};
    +
    +
    +/**
    + * Returns an array of all element children of a given node.
    + * @param {Node} element The node to get element children of.
    + * @return {!Array<!Node>} An array of all the element children.
    + */
    +goog.testing.ui.style.getElementChildren = function(element) {
    +  var first = goog.dom.getFirstElementChild(element);
    +  if (!first) {
    +    return [];
    +  }
    +  var children = [first], next;
    +  while (next = goog.dom.getNextElementSibling(children[children.length - 1])) {
    +    children.push(next);
    +  }
    +  return children;
    +};
    +
    +
    +/**
    + * Tests whether a given node is a "content" node of a reference structure,
    + * which means it is allowed to have arbitrary children.
    + * @param {Node} element The node to test.
    + * @return {boolean} Whether the given node is a content node or not.
    + */
    +goog.testing.ui.style.isContentNode = function(element) {
    +  return element.className.indexOf('content') != -1;
    +};
    +
    +
    +/**
    + * Tests that the structure, node names, and classes of the given element are
    + * the same as the reference structure with the given id. Throws an error if the
    + * element doesn't have the same nodes at each level of the DOM with the same
    + * classes on each. The test ignores all DOM structure within content nodes.
    + * @param {Node} element The root node of the DOM structure to test.
    + * @param {string} referenceId The id of the container for the reference
    + *   structure to test against.
    + */
    +goog.testing.ui.style.assertStructureMatchesReference = function(element,
    +    referenceId) {
    +  goog.testing.ui.style.assertStructureMatchesReferenceInner_(element,
    +      goog.testing.ui.style.getReferenceNode(referenceId));
    +};
    +
    +
    +/**
    + * A recursive function for comparing structure, node names, and classes between
    + * a test and reference DOM structure. Throws an error if one of these things
    + * doesn't match. Used internally by
    + * {@link goog.testing.ui.style.assertStructureMatchesReference}.
    + * @param {Node} element DOM element to test.
    + * @param {Node} reference DOM element to use as a reference (test against).
    + * @private
    + */
    +goog.testing.ui.style.assertStructureMatchesReferenceInner_ = function(element,
    +    reference) {
    +  if (!element && !reference) {
    +    return;
    +  }
    +  assertTrue('Expected two elements.', !!element && !!reference);
    +  assertEquals('Expected nodes to have the same nodeName.',
    +      element.nodeName, reference.nodeName);
    +  var testElem = goog.asserts.assertElement(element);
    +  var refElem = goog.asserts.assertElement(reference);
    +  var elementClasses = goog.dom.classlist.get(testElem);
    +  goog.array.forEach(goog.dom.classlist.get(refElem), function(referenceClass) {
    +    assertContains('Expected test node to have all reference classes.',
    +        referenceClass, elementClasses);
    +  });
    +  // Call assertStructureMatchesReferenceInner_ on all element children
    +  // unless this is a content node
    +  var elChildren = goog.testing.ui.style.getElementChildren(element),
    +      refChildren = goog.testing.ui.style.getElementChildren(reference);
    +  if (!goog.testing.ui.style.isContentNode(reference)) {
    +    if (elChildren.length != refChildren.length) {
    +      assertEquals('Expected same number of children for a non-content node.',
    +          elChildren.length, refChildren.length);
    +    }
    +    for (var i = 0; i < elChildren.length; i++) {
    +      goog.testing.ui.style.assertStructureMatchesReferenceInner_(elChildren[i],
    +          refChildren[i]);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/style_reference.html b/mapbox-gl/closure-library/closure/goog/testing/ui/style_reference.html
    new file mode 100644
    index 0000000000..c60a51ffd8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/style_reference.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Unit Tests - goog.testing.ui.style Reference HTML</title>
    +</head>
    +<body>
    +
    +  <div id="reference">
    +    <div class="one two three">
    +      <div class="four five"></div>
    +      <div class="six seven content">
    +        Content
    +      </div>
    +    </div>
    +  </div>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.html b/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.html
    new file mode 100644
    index 0000000000..a657efd5e0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.html
    @@ -0,0 +1,75 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.testing.ui.style
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.testing.ui.styleTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="correct">
    +    <div class="one two three">
    +      <div class="four five"></div>
    +      <div class="six seven content">
    +        <div></div>
    +        Blah
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div id="missing-class">
    +    <div class="one two three">
    +      <div class="five"></div>
    +      <div class="six seven content">
    +        <div></div>
    +        Blah
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div id="extra-class">
    +    <div class="one two three">
    +      <div class="four five five-point-five"></div>
    +      <div class="six seven content">
    +        <div></div>
    +        Blah
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div id="missing-child">
    +    <div class="one two three">
    +      <div class="six seven content">
    +        <div></div>
    +        Blah
    +      </div>
    +    </div>
    +  </div>
    +
    +  <div id="extra-child">
    +    <div class="one two three">
    +      <div class="four five">
    +        <div class="five-point-five"></div>
    +      </div>
    +      <div class="six seven content">
    +        <div></div>
    +        Blah
    +      </div>
    +    </div>
    +  </div>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.js b/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.js
    new file mode 100644
    index 0000000000..1d1b3aaa0f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/ui/style_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.testing.ui.styleTest');
    +goog.setTestOnly('goog.testing.ui.styleTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.style');
    +
    +// Write iFrame tag to load reference FastUI markup. Then, our tests will
    +// compare the generated markup to the reference markup.
    +var refPath = 'style_reference.html';
    +goog.testing.ui.style.writeReferenceFrame(refPath);
    +
    +// assertStructureMatchesReference should succeed if the structure, node
    +// names, and classes match.
    +function testCorrect() {
    +  var el = goog.dom.getFirstElementChild(goog.dom.getElement('correct'));
    +  goog.testing.ui.style.assertStructureMatchesReference(el, 'reference');
    +}
    +
    +// assertStructureMatchesReference should fail if one of the nodes is
    +// missing a class.
    +function testMissingClass() {
    +  var el = goog.dom.getFirstElementChild(
    +      goog.dom.getElement('missing-class'));
    +  var e = assertThrows(function() {
    +    goog.testing.ui.style.assertStructureMatchesReference(el, 'reference');
    +  });
    +  assertContains('all reference classes', e.message);
    +}
    +
    +// assertStructureMatchesReference should NOT fail if one of the nodes has
    +// an additional class.
    +function testExtraClass() {
    +  var el = goog.dom.getFirstElementChild(
    +      goog.dom.getElement('extra-class'));
    +  goog.testing.ui.style.assertStructureMatchesReference(el, 'reference');
    +}
    +
    +// assertStructureMatchesReference should fail if there is a missing child
    +// node somewhere in the DOM structure.
    +function testMissingChild() {
    +  var el = goog.dom.getFirstElementChild(
    +      goog.dom.getElement('missing-child'));
    +  var e = assertThrows(function() {
    +    goog.testing.ui.style.assertStructureMatchesReference(el, 'reference');
    +  });
    +  assertContains('same number of children', e.message);
    +}
    +
    +// assertStructureMatchesReference should fail if there is an extra child
    +// node somewhere in the DOM structure.
    +function testExtraChild() {
    +  var el = goog.dom.getFirstElementChild(
    +      goog.dom.getElement('extra-child'));
    +  var e = assertThrows(function() {
    +    goog.testing.ui.style.assertStructureMatchesReference(el, 'reference');
    +  });
    +  assertContains('same number of children', e.message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/testing/watchers.js b/mapbox-gl/closure-library/closure/goog/testing/watchers.js
    new file mode 100644
    index 0000000000..a242a57e30
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/testing/watchers.js
    @@ -0,0 +1,46 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Simple notifiers for the Closure testing framework.
    + *
    + * @author johnlenz@google.com (John Lenz)
    + */
    +
    +goog.provide('goog.testing.watchers');
    +
    +
    +/** @private {!Array<function()>} */
    +goog.testing.watchers.resetWatchers_ = [];
    +
    +
    +/**
    + * Fires clock reset watching functions.
    + */
    +goog.testing.watchers.signalClockReset = function() {
    +  var watchers = goog.testing.watchers.resetWatchers_;
    +  for (var i = 0; i < watchers.length; i++) {
    +    goog.testing.watchers.resetWatchers_[i]();
    +  }
    +};
    +
    +
    +/**
    + * Enqueues a function to be called when the clock used for setTimeout is reset.
    + * @param {function()} fn
    + */
    +goog.testing.watchers.watchClockReset = function(fn) {
    +  goog.testing.watchers.resetWatchers_.push(fn);
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/timer/timer.js b/mapbox-gl/closure-library/closure/goog/timer/timer.js
    new file mode 100644
    index 0000000000..19a26b6807
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/timer/timer.js
    @@ -0,0 +1,329 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A timer class to which other classes and objects can
    + * listen on.  This is only an abstraction above setInterval.
    + *
    + * @see ../demos/timers.html
    + */
    +
    +goog.provide('goog.Timer');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.events.EventTarget');
    +
    +
    +
    +/**
    + * Class for handling timing events.
    + *
    + * @param {number=} opt_interval Number of ms between ticks (Default: 1ms).
    + * @param {Object=} opt_timerObject  An object that has setTimeout, setInterval,
    + *     clearTimeout and clearInterval (eg Window).
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.Timer = function(opt_interval, opt_timerObject) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Number of ms between ticks
    +   * @type {number}
    +   * @private
    +   */
    +  this.interval_ = opt_interval || 1;
    +
    +  /**
    +   * An object that implements setTimeout, setInterval, clearTimeout and
    +   * clearInterval. We default to the window object. Changing this on
    +   * goog.Timer.prototype changes the object for all timer instances which can
    +   * be useful if your environment has some other implementation of timers than
    +   * the window object.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.timerObject_ = opt_timerObject || goog.Timer.defaultTimerObject;
    +
    +  /**
    +   * Cached tick_ bound to the object for later use in the timer.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.boundTick_ = goog.bind(this.tick_, this);
    +
    +  /**
    +   * Firefox browser often fires the timer event sooner
    +   * (sometimes MUCH sooner) than the requested timeout. So we
    +   * compare the time to when the event was last fired, and
    +   * reschedule if appropriate. See also goog.Timer.intervalScale
    +   * @type {number}
    +   * @private
    +   */
    +  this.last_ = goog.now();
    +};
    +goog.inherits(goog.Timer, goog.events.EventTarget);
    +
    +
    +/**
    + * Maximum timeout value.
    + *
    + * Timeout values too big to fit into a signed 32-bit integer may cause
    + * overflow in FF, Safari, and Chrome, resulting in the timeout being
    + * scheduled immediately.  It makes more sense simply not to schedule these
    + * timeouts, since 24.8 days is beyond a reasonable expectation for the
    + * browser to stay open.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.Timer.MAX_TIMEOUT_ = 2147483647;
    +
    +
    +/**
    + * A timer ID that cannot be returned by any known implmentation of
    + * Window.setTimeout.  Passing this value to window.clearTimeout should
    + * therefore be a no-op.
    + *
    + * @const {number}
    + * @private
    + */
    +goog.Timer.INVALID_TIMEOUT_ID_ = -1;
    +
    +
    +/**
    + * Whether this timer is enabled
    + * @type {boolean}
    + */
    +goog.Timer.prototype.enabled = false;
    +
    +
    +/**
    + * An object that implements setTimout, setInterval, clearTimeout and
    + * clearInterval. We default to the global object. Changing
    + * goog.Timer.defaultTimerObject changes the object for all timer instances
    + * which can be useful if your environment has some other implementation of
    + * timers you'd like to use.
    + * @type {Object}
    + */
    +goog.Timer.defaultTimerObject = goog.global;
    +
    +
    +/**
    + * A variable that controls the timer error correction. If the
    + * timer is called before the requested interval times
    + * intervalScale, which often happens on mozilla, the timer is
    + * rescheduled. See also this.last_
    + * @type {number}
    + */
    +goog.Timer.intervalScale = 0.8;
    +
    +
    +/**
    + * Variable for storing the result of setInterval
    + * @type {?number}
    + * @private
    + */
    +goog.Timer.prototype.timer_ = null;
    +
    +
    +/**
    + * Gets the interval of the timer.
    + * @return {number} interval Number of ms between ticks.
    + */
    +goog.Timer.prototype.getInterval = function() {
    +  return this.interval_;
    +};
    +
    +
    +/**
    + * Sets the interval of the timer.
    + * @param {number} interval Number of ms between ticks.
    + */
    +goog.Timer.prototype.setInterval = function(interval) {
    +  this.interval_ = interval;
    +  if (this.timer_ && this.enabled) {
    +    // Stop and then start the timer to reset the interval.
    +    this.stop();
    +    this.start();
    +  } else if (this.timer_) {
    +    this.stop();
    +  }
    +};
    +
    +
    +/**
    + * Callback for the setTimeout used by the timer
    + * @private
    + */
    +goog.Timer.prototype.tick_ = function() {
    +  if (this.enabled) {
    +    var elapsed = goog.now() - this.last_;
    +    if (elapsed > 0 &&
    +        elapsed < this.interval_ * goog.Timer.intervalScale) {
    +      this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
    +          this.interval_ - elapsed);
    +      return;
    +    }
    +
    +    // Prevents setInterval from registering a duplicate timeout when called
    +    // in the timer event handler.
    +    if (this.timer_) {
    +      this.timerObject_.clearTimeout(this.timer_);
    +      this.timer_ = null;
    +    }
    +
    +    this.dispatchTick();
    +    // The timer could be stopped in the timer event handler.
    +    if (this.enabled) {
    +      this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
    +          this.interval_);
    +      this.last_ = goog.now();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Dispatches the TICK event. This is its own method so subclasses can override.
    + */
    +goog.Timer.prototype.dispatchTick = function() {
    +  this.dispatchEvent(goog.Timer.TICK);
    +};
    +
    +
    +/**
    + * Starts the timer.
    + */
    +goog.Timer.prototype.start = function() {
    +  this.enabled = true;
    +
    +  // If there is no interval already registered, start it now
    +  if (!this.timer_) {
    +    // IMPORTANT!
    +    // window.setInterval in FireFox has a bug - it fires based on
    +    // absolute time, rather than on relative time. What this means
    +    // is that if a computer is sleeping/hibernating for 24 hours
    +    // and the timer interval was configured to fire every 1000ms,
    +    // then after the PC wakes up the timer will fire, in rapid
    +    // succession, 3600*24 times.
    +    // This bug is described here and is already fixed, but it will
    +    // take time to propagate, so for now I am switching this over
    +    // to setTimeout logic.
    +    //     https://bugzilla.mozilla.org/show_bug.cgi?id=376643
    +    //
    +    this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
    +        this.interval_);
    +    this.last_ = goog.now();
    +  }
    +};
    +
    +
    +/**
    + * Stops the timer.
    + */
    +goog.Timer.prototype.stop = function() {
    +  this.enabled = false;
    +  if (this.timer_) {
    +    this.timerObject_.clearTimeout(this.timer_);
    +    this.timer_ = null;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.Timer.prototype.disposeInternal = function() {
    +  goog.Timer.superClass_.disposeInternal.call(this);
    +  this.stop();
    +  delete this.timerObject_;
    +};
    +
    +
    +/**
    + * Constant for the timer's event type
    + * @type {string}
    + */
    +goog.Timer.TICK = 'tick';
    +
    +
    +/**
    + * Calls the given function once, after the optional pause.
    + *
    + * The function is always called asynchronously, even if the delay is 0. This
    + * is a common trick to schedule a function to run after a batch of browser
    + * event processing.
    + *
    + * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function
    + *     or object that has a handleEvent method.
    + * @param {number=} opt_delay Milliseconds to wait; default is 0.
    + * @param {SCOPE=} opt_handler Object in whose scope to call the listener.
    + * @return {number} A handle to the timer ID.
    + * @template SCOPE
    + */
    +goog.Timer.callOnce = function(listener, opt_delay, opt_handler) {
    +  if (goog.isFunction(listener)) {
    +    if (opt_handler) {
    +      listener = goog.bind(listener, opt_handler);
    +    }
    +  } else if (listener && typeof listener.handleEvent == 'function') {
    +    // using typeof to prevent strict js warning
    +    listener = goog.bind(listener.handleEvent, listener);
    +  } else {
    +    throw Error('Invalid listener argument');
    +  }
    +
    +  if (opt_delay > goog.Timer.MAX_TIMEOUT_) {
    +    // Timeouts greater than MAX_INT return immediately due to integer
    +    // overflow in many browsers.  Since MAX_INT is 24.8 days, just don't
    +    // schedule anything at all.
    +    return goog.Timer.INVALID_TIMEOUT_ID_;
    +  } else {
    +    return goog.Timer.defaultTimerObject.setTimeout(
    +        listener, opt_delay || 0);
    +  }
    +};
    +
    +
    +/**
    + * Clears a timeout initiated by callOnce
    + * @param {?number} timerId a timer ID.
    + */
    +goog.Timer.clear = function(timerId) {
    +  goog.Timer.defaultTimerObject.clearTimeout(timerId);
    +};
    +
    +
    +/**
    + * @param {number} delay Milliseconds to wait.
    + * @param {(RESULT|goog.Thenable<RESULT>|Thenable)=} opt_result The value
    + *     with which the promise will be resolved.
    + * @return {!goog.Promise<RESULT>} A promise that will be resolved after
    + *     the specified delay, unless it is canceled first.
    + * @template RESULT
    + */
    +goog.Timer.promise = function(delay, opt_result) {
    +  var timerKey = null;
    +  return new goog.Promise(function(resolve, reject) {
    +    timerKey = goog.Timer.callOnce(function() {
    +      resolve(opt_result);
    +    }, delay);
    +    if (timerKey == goog.Timer.INVALID_TIMEOUT_ID_) {
    +      reject(new Error('Failed to schedule timer.'));
    +    }
    +  }).thenCatch(function(error) {
    +    // Clear the timer. The most likely reason is "cancel" signal.
    +    goog.Timer.clear(timerKey);
    +    throw error;
    +  });
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/timer/timer_test.html b/mapbox-gl/closure-library/closure/goog/timer/timer_test.html
    new file mode 100644
    index 0000000000..94513d3a68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/timer/timer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.Timer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.TimerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/timer/timer_test.js b/mapbox-gl/closure-library/closure/goog/timer/timer_test.js
    new file mode 100644
    index 0000000000..2a03e54961
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/timer/timer_test.js
    @@ -0,0 +1,149 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.TimerTest');
    +goog.setTestOnly('goog.TimerTest');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Timer');
    +goog.require('goog.events');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +var intervalIds = {};
    +var intervalIdCounter = 0;
    +var mockClock;
    +var maxDuration = 60 * 1000; // 60s
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true /* install */);
    +}
    +
    +function tearDown() {
    +  mockClock.dispose();
    +}
    +
    +// Run a test for 60s and see how many counts we get
    +function runTest(string, ticks, number) {
    +  var t = new goog.Timer(ticks);
    +  var count = 0;
    +  goog.events.listen(t, 'tick', function(evt) {
    +    count++;
    +  });
    +  t.start();
    +  mockClock.tick(maxDuration);
    +  assertEquals(string, number, count);
    +  t.stop();
    +  goog.events.removeAll(t);
    +}
    +
    +
    +function test100msTicks() {
    +  // Desc, interval in ms, expected ticks in 60s
    +  runTest('10 ticks per second for 60 seconds', 100, 600);
    +}
    +
    +function test500msTicks() {
    +  runTest('2 ticks per second for 60 seconds', 500, 120);
    +}
    +
    +function test1sTicks() {
    +  runTest('1 tick per second for 60 seconds', 1000, 60);
    +}
    +
    +function test2sTicks() {
    +  runTest('1 tick every 2 seconds for 60 seconds', 2000, 30);
    +}
    +
    +function test5sTicks() {
    +  runTest('1 tick every 5 seconds for 60 seconds', 5000, 12);
    +}
    +
    +function test10sTicks() {
    +  runTest('1 tick every 10 seconds for 60 seconds', 10000, 6);
    +}
    +
    +function test30sTicks() {
    +  runTest('1 tick every 30 seconds for 60 seconds', 30000, 2);
    +}
    +
    +function test60sTicks() {
    +  runTest('1 tick every 60 seconds', 60000, 1);
    +}
    +
    +function testCallOnce() {
    +  var c = 0;
    +  var actualTimeoutId = goog.Timer.callOnce(
    +      function() {
    +        if (c > 0) {
    +          assertTrue('callOnce should only be called once', false);
    +        }
    +        c++;
    +      });
    +  assertEquals('callOnce should return the timeout ID',
    +      mockClock.getTimeoutsMade(), actualTimeoutId);
    +
    +  var obj = {c: 0};
    +  goog.Timer.callOnce(function() {
    +    if (this.c > 0) {
    +      assertTrue('callOnce should only be called once', false);
    +    }
    +    assertEquals(obj, this);
    +    this.c++;
    +  }, 1, obj);
    +  mockClock.tick(maxDuration);
    +}
    +
    +function testCallOnceIgnoresTimeoutsTooLarge() {
    +  var failCallback = goog.partial(fail, 'Timeout should never be called');
    +  assertEquals('Timeouts slightly too large should yield a timer ID of -1',
    +      -1, goog.Timer.callOnce(failCallback, 2147483648));
    +  assertEquals('Infinite timeouts should yield a timer ID of -1',
    +      -1, goog.Timer.callOnce(failCallback, Infinity));
    +}
    +
    +function testPromise() {
    +  var c = 0;
    +  goog.Timer.promise(1, 'A').then(function(result) {
    +    c++;
    +    assertEquals('promise should return resolved value', 'A', result);
    +  });
    +  mockClock.tick(10);
    +  assertEquals('promise must be yielded once and only once', 1, c);
    +}
    +
    +function testPromise_cancel() {
    +  var c = 0;
    +  goog.Timer.promise(1, 'A').then(function(result) {
    +    fail('promise must not be resolved');
    +  }, function(reason) {
    +    c++;
    +    assertTrue('promise must fail due to cancel signal',
    +        reason instanceof goog.Promise.CancellationError);
    +  }).cancel();
    +  mockClock.tick(10);
    +  assertEquals('promise must be canceled once and only once', 1, c);
    +}
    +
    +function testPromise_timeoutTooLarge() {
    +  var c = 0;
    +  goog.Timer.promise(2147483648, 'A').then(function(result) {
    +    fail('promise must not be resolved');
    +  }, function(reason) {
    +    c++;
    +    assertTrue('promise must be rejected', reason instanceof Error);
    +  });
    +  mockClock.tick(10);
    +  assertEquals('promise must be rejected once and only once', 1, c);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/entries.js b/mapbox-gl/closure-library/closure/goog/tweak/entries.js
    new file mode 100644
    index 0000000000..6ee7175aa6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/entries.js
    @@ -0,0 +1,1002 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definitions for all tweak entries.
    + * The class hierarchy is as follows (abstract entries are denoted with a *):
    + * BaseEntry(id, description) *
    + *   -> ButtonAction(buttons in the UI)
    + *   -> BaseSetting(query parameter) *
    + *     -> BooleanGroup(child booleans)
    + *     -> BasePrimitiveSetting(value, defaultValue) *
    + *       -> BooleanSetting
    + *       -> StringSetting
    + *       -> NumericSetting
    + *       -> BooleanInGroupSetting(token)
    + * Most clients should not use these classes directly, but instead use the API
    + * defined in tweak.js. One possible use case for directly using them is to
    + * register tweaks that are not known at compile time.
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + */
    +
    +goog.provide('goog.tweak.BaseEntry');
    +goog.provide('goog.tweak.BasePrimitiveSetting');
    +goog.provide('goog.tweak.BaseSetting');
    +goog.provide('goog.tweak.BooleanGroup');
    +goog.provide('goog.tweak.BooleanInGroupSetting');
    +goog.provide('goog.tweak.BooleanSetting');
    +goog.provide('goog.tweak.ButtonAction');
    +goog.provide('goog.tweak.NumericSetting');
    +goog.provide('goog.tweak.StringSetting');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.log');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Base class for all Registry entries.
    + * @param {string} id The ID for the entry. Must contain only letters,
    + *     numbers, underscores and periods.
    + * @param {string} description A description of what the entry does.
    + * @constructor
    + */
    +goog.tweak.BaseEntry = function(id, description) {
    +  /**
    +   * An ID to uniquely identify the entry.
    +   * @type {string}
    +   * @private
    +   */
    +  this.id_ = id;
    +
    +  /**
    +   * A descriptive label for the entry.
    +   * @type {string}
    +   */
    +  this.label = id;
    +
    +  /**
    +   * A description of what this entry does.
    +   * @type {string}
    +   */
    +  this.description = description;
    +
    +  /**
    +   * Functions to be called whenever a setting is changed or a button is
    +   * clicked.
    +   * @type {!Array<!Function>}
    +   * @private
    +   */
    +  this.callbacks_ = [];
    +};
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + */
    +goog.tweak.BaseEntry.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BaseEntry');
    +
    +
    +/**
    + * Whether a restart is required for changes to the setting to take effect.
    + * @type {boolean}
    + * @private
    + */
    +goog.tweak.BaseEntry.prototype.restartRequired_ = true;
    +
    +
    +/**
    + * @return {string} Returns the entry's ID.
    + */
    +goog.tweak.BaseEntry.prototype.getId = function() {
    +  return this.id_;
    +};
    +
    +
    +/**
    + * Returns whether a restart is required for changes to the setting to take
    + * effect.
    + * @return {boolean} The value.
    + */
    +goog.tweak.BaseEntry.prototype.isRestartRequired = function() {
    +  return this.restartRequired_;
    +};
    +
    +
    +/**
    + * Sets whether a restart is required for changes to the setting to take
    + * effect.
    + * @param {boolean} value The new value.
    + */
    +goog.tweak.BaseEntry.prototype.setRestartRequired = function(value) {
    +  this.restartRequired_ = value;
    +};
    +
    +
    +/**
    + * Adds a callback that should be called when the setting has changed (or when
    + * an action has been clicked).
    + * @param {!Function} callback The callback to add.
    + */
    +goog.tweak.BaseEntry.prototype.addCallback = function(callback) {
    +  this.callbacks_.push(callback);
    +};
    +
    +
    +/**
    + * Removes a callback that was added by addCallback.
    + * @param {!Function} callback The callback to add.
    + */
    +goog.tweak.BaseEntry.prototype.removeCallback = function(callback) {
    +  goog.array.remove(this.callbacks_, callback);
    +};
    +
    +
    +/**
    + * Calls all registered callbacks.
    + */
    +goog.tweak.BaseEntry.prototype.fireCallbacks = function() {
    +  for (var i = 0, callback; callback = this.callbacks_[i]; ++i) {
    +    callback(this);
    +  }
    +};
    +
    +
    +
    +/**
    + * Base class for all tweak entries that are settings. Settings are entries
    + * that are associated with a query parameter.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @constructor
    + * @extends {goog.tweak.BaseEntry}
    + */
    +goog.tweak.BaseSetting = function(id, description) {
    +  goog.tweak.BaseEntry.call(this, id, description);
    +  // Apply this restriction for settings since they turn in to query
    +  // parameters. For buttons, it's not really important.
    +  goog.asserts.assert(!/[^A-Za-z0-9._]/.test(id),
    +      'Tweak id contains illegal characters: ', id);
    +
    +  /**
    +   * The value of this setting's query parameter.
    +   * @type {string|undefined}
    +   * @protected
    +   */
    +  this.initialQueryParamValue;
    +
    +  /**
    +   * The query parameter that controls this setting.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.paramName_ = this.getId().toLowerCase();
    +};
    +goog.inherits(goog.tweak.BaseSetting, goog.tweak.BaseEntry);
    +
    +
    +/**
    + * States of initialization. Entries are initialized lazily in order to allow
    + * their initialization to happen in multiple statements.
    + * @enum {number}
    + * @private
    + */
    +goog.tweak.BaseSetting.InitializeState_ = {
    +  // The start state for all settings.
    +  NOT_INITIALIZED: 0,
    +  // This is used to allow concrete classes to call assertNotInitialized()
    +  // during their initialize() function.
    +  INITIALIZING: 1,
    +  // One a setting is initialized, it may no longer change its configuration
    +  // settings (associated query parameter, token, etc).
    +  INITIALIZED: 2
    +};
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.BaseSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BaseSetting');
    +
    +
    +/**
    + * Whether initialize() has been called (or is in the middle of being called).
    + * @type {goog.tweak.BaseSetting.InitializeState_}
    + * @private
    + */
    +goog.tweak.BaseSetting.prototype.initializeState_ =
    +    goog.tweak.BaseSetting.InitializeState_.NOT_INITIALIZED;
    +
    +
    +/**
    + * Sets the value of the entry based on the value of the query parameter. Once
    + * this is called, configuration settings (associated query parameter, token,
    + * etc) may not be changed.
    + * @param {?string} value The part of the query param for this setting after
    + *     the '='. Null if it is not present.
    + * @protected
    + */
    +goog.tweak.BaseSetting.prototype.initialize = goog.abstractMethod;
    +
    +
    +/**
    + * Returns the value to be used in the query parameter for this tweak.
    + * @return {?string} The encoded value. Null if the value is set to its
    + *     default.
    + */
    +goog.tweak.BaseSetting.prototype.getNewValueEncoded = goog.abstractMethod;
    +
    +
    +/**
    + * Asserts that this tweak has not been initialized yet.
    + * @param {string} funcName Function name to use in the assertion message.
    + * @protected
    + */
    +goog.tweak.BaseSetting.prototype.assertNotInitialized = function(funcName) {
    +  goog.asserts.assert(this.initializeState_ !=
    +      goog.tweak.BaseSetting.InitializeState_.INITIALIZED,
    +      'Cannot call ' + funcName + ' after the tweak as been initialized.');
    +};
    +
    +
    +/**
    + * Returns whether the setting is currently being initialized.
    + * @return {boolean} Whether the setting is currently being initialized.
    + * @protected
    + */
    +goog.tweak.BaseSetting.prototype.isInitializing = function() {
    +  return this.initializeState_ ==
    +      goog.tweak.BaseSetting.InitializeState_.INITIALIZING;
    +};
    +
    +
    +/**
    + * Sets the initial query parameter value for this setting. May not be called
    + * after the setting has been initialized.
    + * @param {string} value The inital query parameter value for this setting.
    + */
    +goog.tweak.BaseSetting.prototype.setInitialQueryParamValue = function(value) {
    +  this.assertNotInitialized('setInitialQueryParamValue');
    +  this.initialQueryParamValue = value;
    +};
    +
    +
    +/**
    + * Returns the name of the query parameter used for this setting.
    + * @return {?string} The param name. Null if no query parameter is directly
    + *     associated with the setting.
    + */
    +goog.tweak.BaseSetting.prototype.getParamName = function() {
    +  return this.paramName_;
    +};
    +
    +
    +/**
    + * Sets the name of the query parameter used for this setting. If null is
    + * passed the the setting will not appear in the top-level query string.
    + * @param {?string} value The new value.
    + */
    +goog.tweak.BaseSetting.prototype.setParamName = function(value) {
    +  this.assertNotInitialized('setParamName');
    +  this.paramName_ = value;
    +};
    +
    +
    +/**
    + * Applies the default value or query param value if this is the first time
    + * that the function has been called.
    + * @protected
    + */
    +goog.tweak.BaseSetting.prototype.ensureInitialized = function() {
    +  if (this.initializeState_ ==
    +      goog.tweak.BaseSetting.InitializeState_.NOT_INITIALIZED) {
    +    // Instead of having only initialized / not initialized, there is a
    +    // separate in-between state so that functions that call
    +    // assertNotInitialized() will not fail when called inside of the
    +    // initialize().
    +    this.initializeState_ =
    +        goog.tweak.BaseSetting.InitializeState_.INITIALIZING;
    +    var value = this.initialQueryParamValue == undefined ? null :
    +        this.initialQueryParamValue;
    +    this.initialize(value);
    +    this.initializeState_ =
    +        goog.tweak.BaseSetting.InitializeState_.INITIALIZED;
    +  }
    +};
    +
    +
    +
    +/**
    + * Base class for all settings that wrap primitive values.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {*} defaultValue The default value for this setting.
    + * @constructor
    + * @extends {goog.tweak.BaseSetting}
    + */
    +goog.tweak.BasePrimitiveSetting = function(id, description, defaultValue) {
    +  goog.tweak.BaseSetting.call(this, id, description);
    +  /**
    +   * The default value of the setting.
    +   * @type {*}
    +   * @private
    +   */
    +  this.defaultValue_ = defaultValue;
    +
    +  /**
    +   * The value of the tweak.
    +   * @type {*}
    +   * @private
    +   */
    +  this.value_;
    +
    +  /**
    +   * The value of the tweak once "Apply Tweaks" is pressed.
    +   * @type {*}
    +   * @private
    +   */
    +  this.newValue_;
    +};
    +goog.inherits(goog.tweak.BasePrimitiveSetting, goog.tweak.BaseSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BasePrimitiveSetting');
    +
    +
    +/**
    + * Returns the query param encoded representation of the setting's value.
    + * @return {string} The encoded value.
    + * @protected
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.encodeNewValue =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * If the setting has the restartRequired option, then returns its inital
    + * value. Otherwise, returns its current value.
    + * @return {*} The value.
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.getValue = function() {
    +  this.ensureInitialized();
    +  return this.value_;
    +};
    +
    +
    +/**
    + * Returns the value of the setting to use once "Apply Tweaks" is clicked.
    + * @return {*} The value.
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.getNewValue = function() {
    +  this.ensureInitialized();
    +  return this.newValue_;
    +};
    +
    +
    +/**
    + * Sets the value of the setting. If the setting has the restartRequired
    + * option, then the value will not be changed until the "Apply Tweaks" button
    + * is clicked. If it does not have the option, the value will be update
    + * immediately and all registered callbacks will be called.
    + * @param {*} value The value.
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.setValue = function(value) {
    +  this.ensureInitialized();
    +  var changed = this.newValue_ != value;
    +  this.newValue_ = value;
    +  // Don't fire callbacks if we are currently in the initialize() method.
    +  if (this.isInitializing()) {
    +    this.value_ = value;
    +  } else {
    +    if (!this.isRestartRequired()) {
    +      // Update the current value only if the tweak has been marked as not
    +      // needing a restart.
    +      this.value_ = value;
    +    }
    +    if (changed) {
    +      this.fireCallbacks();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the default value for this setting.
    + * @return {*} The default value.
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.getDefaultValue = function() {
    +  return this.defaultValue_;
    +};
    +
    +
    +/**
    + * Sets the default value for the tweak.
    + * @param {*} value The new value.
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.setDefaultValue =
    +    function(value) {
    +  this.assertNotInitialized('setDefaultValue');
    +  this.defaultValue_ = value;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BasePrimitiveSetting.prototype.getNewValueEncoded = function() {
    +  this.ensureInitialized();
    +  return this.newValue_ == this.defaultValue_ ? null : this.encodeNewValue();
    +};
    +
    +
    +
    +/**
    + * A registry setting for string values.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @constructor
    + * @extends {goog.tweak.BasePrimitiveSetting}
    + * @final
    + */
    +goog.tweak.StringSetting = function(id, description) {
    +  goog.tweak.BasePrimitiveSetting.call(this, id, description, '');
    +  /**
    +   * Valid values for the setting.
    +   * @type {Array<string>|undefined}
    +   */
    +  this.validValues_;
    +};
    +goog.inherits(goog.tweak.StringSetting, goog.tweak.BasePrimitiveSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.StringSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.StringSetting');
    +
    +
    +/**
    + * @override
    + * @return {string} The tweaks's value.
    + */
    +goog.tweak.StringSetting.prototype.getValue;
    +
    +
    +/**
    + * @override
    + * @return {string} The tweaks's new value.
    + */
    +goog.tweak.StringSetting.prototype.getNewValue;
    +
    +
    +/**
    + * @override
    + * @param {string} value The tweaks's value.
    + */
    +goog.tweak.StringSetting.prototype.setValue;
    +
    +
    +/**
    + * @override
    + * @param {string} value The default value.
    + */
    +goog.tweak.StringSetting.prototype.setDefaultValue;
    +
    +
    +/**
    + * @override
    + * @return {string} The default value.
    + */
    +goog.tweak.StringSetting.prototype.getDefaultValue;
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.StringSetting.prototype.encodeNewValue = function() {
    +  return this.getNewValue();
    +};
    +
    +
    +/**
    + * Sets the valid values for the setting.
    + * @param {Array<string>|undefined} values Valid values.
    + */
    +goog.tweak.StringSetting.prototype.setValidValues = function(values) {
    +  this.assertNotInitialized('setValidValues');
    +  this.validValues_ = values;
    +  // Set the default value to the first value in the list if the current
    +  // default value is not within it.
    +  if (values && !goog.array.contains(values, this.getDefaultValue())) {
    +    this.setDefaultValue(values[0]);
    +  }
    +};
    +
    +
    +/**
    + * Returns the valid values for the setting.
    + * @return {Array<string>|undefined} Valid values.
    + */
    +goog.tweak.StringSetting.prototype.getValidValues = function() {
    +  return this.validValues_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.StringSetting.prototype.initialize = function(value) {
    +  if (value == null) {
    +    this.setValue(this.getDefaultValue());
    +  } else {
    +    var validValues = this.validValues_;
    +    if (validValues) {
    +      // Make the query parameter values case-insensitive since users might
    +      // type them by hand. Make the capitalization that is actual used come
    +      // from the list of valid values.
    +      value = value.toLowerCase();
    +      for (var i = 0, il = validValues.length; i < il; ++i) {
    +        if (value == validValues[i].toLowerCase()) {
    +          this.setValue(validValues[i]);
    +          return;
    +        }
    +      }
    +      // Warn if the value is not in the list of allowed values.
    +      goog.log.warning(this.logger, 'Tweak ' + this.getId() +
    +          ' has value outside of expected range:' + value);
    +    }
    +    this.setValue(value);
    +  }
    +};
    +
    +
    +
    +/**
    + * A registry setting for numeric values.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @constructor
    + * @extends {goog.tweak.BasePrimitiveSetting}
    + * @final
    + */
    +goog.tweak.NumericSetting = function(id, description) {
    +  goog.tweak.BasePrimitiveSetting.call(this, id, description, 0);
    +  /**
    +   * Valid values for the setting.
    +   * @type {Array<number>|undefined}
    +   */
    +  this.validValues_;
    +};
    +goog.inherits(goog.tweak.NumericSetting, goog.tweak.BasePrimitiveSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.NumericSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.NumericSetting');
    +
    +
    +/**
    + * @override
    + * @return {number} The tweaks's value.
    + */
    +goog.tweak.NumericSetting.prototype.getValue;
    +
    +
    +/**
    + * @override
    + * @return {number} The tweaks's new value.
    + */
    +goog.tweak.NumericSetting.prototype.getNewValue;
    +
    +
    +/**
    + * @override
    + * @param {number} value The tweaks's value.
    + */
    +goog.tweak.NumericSetting.prototype.setValue;
    +
    +
    +/**
    + * @override
    + * @param {number} value The default value.
    + */
    +goog.tweak.NumericSetting.prototype.setDefaultValue;
    +
    +
    +/**
    + * @override
    + * @return {number} The default value.
    + */
    +goog.tweak.NumericSetting.prototype.getDefaultValue;
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.NumericSetting.prototype.encodeNewValue = function() {
    +  return '' + this.getNewValue();
    +};
    +
    +
    +/**
    + * Sets the valid values for the setting.
    + * @param {Array<number>|undefined} values Valid values.
    + */
    +goog.tweak.NumericSetting.prototype.setValidValues =
    +    function(values) {
    +  this.assertNotInitialized('setValidValues');
    +  this.validValues_ = values;
    +  // Set the default value to the first value in the list if the current
    +  // default value is not within it.
    +  if (values && !goog.array.contains(values, this.getDefaultValue())) {
    +    this.setDefaultValue(values[0]);
    +  }
    +};
    +
    +
    +/**
    + * Returns the valid values for the setting.
    + * @return {Array<number>|undefined} Valid values.
    + */
    +goog.tweak.NumericSetting.prototype.getValidValues = function() {
    +  return this.validValues_;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.NumericSetting.prototype.initialize = function(value) {
    +  if (value == null) {
    +    this.setValue(this.getDefaultValue());
    +  } else {
    +    var coercedValue = +value;
    +    // Warn if the value is not in the list of allowed values.
    +    if (this.validValues_ &&
    +        !goog.array.contains(this.validValues_, coercedValue)) {
    +      goog.log.warning(this.logger, 'Tweak ' + this.getId() +
    +          ' has value outside of expected range: ' + value);
    +    }
    +
    +    if (isNaN(coercedValue)) {
    +      goog.log.warning(this.logger, 'Tweak ' + this.getId() +
    +          ' has value of NaN, resetting to ' + this.getDefaultValue());
    +      this.setValue(this.getDefaultValue());
    +    } else {
    +      this.setValue(coercedValue);
    +    }
    +  }
    +};
    +
    +
    +
    +/**
    + * A registry setting that can be either true of false.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @constructor
    + * @extends {goog.tweak.BasePrimitiveSetting}
    + */
    +goog.tweak.BooleanSetting = function(id, description) {
    +  goog.tweak.BasePrimitiveSetting.call(this, id, description, false);
    +};
    +goog.inherits(goog.tweak.BooleanSetting, goog.tweak.BasePrimitiveSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.BooleanSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BooleanSetting');
    +
    +
    +/**
    + * @override
    + * @return {boolean} The tweaks's value.
    + */
    +goog.tweak.BooleanSetting.prototype.getValue;
    +
    +
    +/**
    + * @override
    + * @return {boolean} The tweaks's new value.
    + */
    +goog.tweak.BooleanSetting.prototype.getNewValue;
    +
    +
    +/**
    + * @override
    + * @param {boolean} value The tweaks's value.
    + */
    +goog.tweak.BooleanSetting.prototype.setValue;
    +
    +
    +/**
    + * @override
    + * @param {boolean} value The default value.
    + */
    +goog.tweak.BooleanSetting.prototype.setDefaultValue;
    +
    +
    +/**
    + * @override
    + * @return {boolean} The default value.
    + */
    +goog.tweak.BooleanSetting.prototype.getDefaultValue;
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BooleanSetting.prototype.encodeNewValue = function() {
    +  return this.getNewValue() ? '1' : '0';
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BooleanSetting.prototype.initialize = function(value) {
    +  if (value == null) {
    +    this.setValue(this.getDefaultValue());
    +  } else {
    +    value = value.toLowerCase();
    +    this.setValue(value == 'true' || value == '1');
    +  }
    +};
    +
    +
    +
    +/**
    + * An entry in a BooleanGroup.
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {!goog.tweak.BooleanGroup} group The group that this entry belongs
    + *     to.
    + * @constructor
    + * @extends {goog.tweak.BooleanSetting}
    + * @final
    + */
    +goog.tweak.BooleanInGroupSetting = function(id, description, group) {
    +  goog.tweak.BooleanSetting.call(this, id, description);
    +
    +  /**
    +   * The token to use in the query parameter.
    +   * @type {string}
    +   * @private
    +   */
    +  this.token_ = this.getId().toLowerCase();
    +
    +  /**
    +   * The BooleanGroup that this setting belongs to.
    +   * @type {!goog.tweak.BooleanGroup}
    +   * @private
    +   */
    +  this.group_ = group;
    +
    +  // Take setting out of top-level query parameter list.
    +  goog.tweak.BooleanInGroupSetting.superClass_.setParamName.call(this,
    +      null);
    +};
    +goog.inherits(goog.tweak.BooleanInGroupSetting, goog.tweak.BooleanSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.BooleanInGroupSetting.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BooleanInGroupSetting');
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BooleanInGroupSetting.prototype.setParamName = function(value) {
    +  goog.asserts.fail('Use setToken() for BooleanInGroupSetting.');
    +};
    +
    +
    +/**
    + * Sets the token to use in the query parameter.
    + * @param {string} value The value.
    + */
    +goog.tweak.BooleanInGroupSetting.prototype.setToken = function(value) {
    +  this.token_ = value;
    +};
    +
    +
    +/**
    + * Returns the token to use in the query parameter.
    + * @return {string} The value.
    + */
    +goog.tweak.BooleanInGroupSetting.prototype.getToken = function() {
    +  return this.token_;
    +};
    +
    +
    +/**
    + * Returns the BooleanGroup that this setting belongs to.
    + * @return {!goog.tweak.BooleanGroup} The BooleanGroup that this setting
    + *     belongs to.
    + */
    +goog.tweak.BooleanInGroupSetting.prototype.getGroup = function() {
    +  return this.group_;
    +};
    +
    +
    +
    +/**
    + * A registry setting that contains a group of boolean subfield, where all
    + * entries modify the same query parameter. For example:
    + *     ?foo=setting1,-setting2
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @constructor
    + * @extends {goog.tweak.BaseSetting}
    + * @final
    + */
    +goog.tweak.BooleanGroup = function(id, description) {
    +  goog.tweak.BaseSetting.call(this, id, description);
    +
    +  /**
    +   * A map of token->child entry.
    +   * @type {!Object<!goog.tweak.BooleanSetting>}
    +   * @private
    +   */
    +  this.entriesByToken_ = {};
    +
    +
    +  /**
    +   * A map of token->true/false for all tokens that appeared in the query
    +   * parameter.
    +   * @type {!Object<boolean>}
    +   * @private
    +   */
    +  this.queryParamValues_ = {};
    +
    +};
    +goog.inherits(goog.tweak.BooleanGroup, goog.tweak.BaseSetting);
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @protected
    + * @override
    + */
    +goog.tweak.BooleanGroup.prototype.logger =
    +    goog.log.getLogger('goog.tweak.BooleanGroup');
    +
    +
    +/**
    + * Returns the map of token->boolean settings.
    + * @return {!Object<!goog.tweak.BooleanSetting>} The child settings.
    + */
    +goog.tweak.BooleanGroup.prototype.getChildEntries = function() {
    +  return this.entriesByToken_;
    +};
    +
    +
    +/**
    + * Adds the given BooleanSetting to the group.
    + * @param {goog.tweak.BooleanInGroupSetting} boolEntry The entry.
    + */
    +goog.tweak.BooleanGroup.prototype.addChild = function(boolEntry) {
    +  this.ensureInitialized();
    +
    +  var token = boolEntry.getToken();
    +  var lcToken = token.toLowerCase();
    +  goog.asserts.assert(!this.entriesByToken_[lcToken],
    +      'Multiple bools registered with token "%s" in group: %s', token,
    +      this.getId());
    +  this.entriesByToken_[lcToken] = boolEntry;
    +
    +  // Initialize from query param.
    +  var value = this.queryParamValues_[lcToken];
    +  if (value != undefined) {
    +    boolEntry.initialQueryParamValue = value ? '1' : '0';
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BooleanGroup.prototype.initialize = function(value) {
    +  var queryParamValues = {};
    +
    +  if (value) {
    +    var tokens = value.split(/\s*,\s*/);
    +    for (var i = 0; i < tokens.length; ++i) {
    +      var token = tokens[i].toLowerCase();
    +      var negative = token.charAt(0) == '-';
    +      if (negative) {
    +        token = token.substr(1);
    +      }
    +      queryParamValues[token] = !negative;
    +    }
    +  }
    +  this.queryParamValues_ = queryParamValues;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.tweak.BooleanGroup.prototype.getNewValueEncoded = function() {
    +  this.ensureInitialized();
    +  var nonDefaultValues = [];
    +  // Sort the keys so that the generate value is stable.
    +  var keys = goog.object.getKeys(this.entriesByToken_);
    +  keys.sort();
    +  for (var i = 0, entry; entry = this.entriesByToken_[keys[i]]; ++i) {
    +    var encodedValue = entry.getNewValueEncoded();
    +    if (encodedValue != null) {
    +      nonDefaultValues.push((entry.getNewValue() ? '' : '-') +
    +          entry.getToken());
    +    }
    +  }
    +  return nonDefaultValues.length ? nonDefaultValues.join(',') : null;
    +};
    +
    +
    +
    +/**
    + * A registry action (a button).
    + * @param {string} id The ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {!Function} callback Function to call when the button is clicked.
    + * @constructor
    + * @extends {goog.tweak.BaseEntry}
    + * @final
    + */
    +goog.tweak.ButtonAction = function(id, description, callback) {
    +  goog.tweak.BaseEntry.call(this, id, description);
    +  this.addCallback(callback);
    +  this.setRestartRequired(false);
    +};
    +goog.inherits(goog.tweak.ButtonAction, goog.tweak.BaseEntry);
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/entries_test.html b/mapbox-gl/closure-library/closure/goog/tweak/entries_test.html
    new file mode 100644
    index 0000000000..21919c69ed
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/entries_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<!--
    +Author: agrieve@google.com (Andrew Grieve)
    +-->
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.tweak.BaseEntry
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.tweak.BaseEntryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/entries_test.js b/mapbox-gl/closure-library/closure/goog/tweak/entries_test.js
    new file mode 100644
    index 0000000000..4726291e49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/entries_test.js
    @@ -0,0 +1,85 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.tweak.BaseEntryTest');
    +goog.setTestOnly('goog.tweak.BaseEntryTest');
    +
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +/** @suppress {extraRequire} needed for createRegistryEntries. */
    +goog.require('goog.tweak.testhelpers');
    +
    +var mockControl;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function tearDown() {
    +  goog.tweak.registry_ = null;
    +  mockControl.$verifyAll();
    +}
    +
    +function testGetValue_defaultValues() {
    +  createRegistryEntries('');
    +  assertFalse('wrong initial value for bool', boolEntry.getValue());
    +  assertEquals('wrong initial value for enum', 'A', strEnumEntry.getValue());
    +  assertEquals('wrong initial value for str', '', strEntry.getValue());
    +
    +  assertEquals('wrong initial value for bool2', true, boolEntry2.getValue());
    +  assertEquals('wrong initial value for enum2', 1, numEnumEntry.getValue());
    +  assertEquals('wrong initial value for str2', 'foo', strEntry2.getValue());
    +
    +  assertFalse('wrong initial value for BoolOne', boolOneEntry.getValue());
    +  assertTrue('wrong initial value for BoolTwo', boolTwoEntry.getValue());
    +}
    +
    +function testGetValue_nonDefaultValues() {
    +  createRegistryEntries('?bool=1&enum=C');
    +  // These have the restartRequired option set.
    +  boolEntry.setValue(false);
    +  strEntry.setValue('foo');
    +  numEntry.setValue(5);
    +  assertTrue('wrong value for boolean', boolEntry.getValue());
    +  assertEquals('wrong value for string', strEntry.getDefaultValue(),
    +      strEntry.getValue());
    +  assertEquals('wrong value for num', numEntry.getDefaultValue(),
    +      numEntry.getValue());
    +
    +  // These do not have the restartRequired option set.
    +  strEnumEntry.setValue('B');
    +  boolOneEntry.setValue(true);
    +  assertEquals('wrong value for strEnum', 'B', strEnumEntry.getValue());
    +  assertEquals('wrong value for boolOne', true, boolOneEntry.getValue());
    +}
    +
    +function testCallbacks() {
    +  createRegistryEntries('');
    +  var mockCallback = mockControl.createFunctionMock();
    +  boolEntry.addCallback(mockCallback);
    +  boolOneEntry.addCallback(mockCallback);
    +  strEnumEntry.addCallback(mockCallback);
    +  numEnumEntry.addCallback(mockCallback);
    +
    +  mockCallback(boolEntry);
    +  mockCallback(boolOneEntry);
    +  mockCallback(strEnumEntry);
    +  mockCallback(numEnumEntry);
    +  mockControl.$replayAll();
    +
    +  boolEntry.setValue(true);
    +  boolOneEntry.setValue(true);
    +  strEnumEntry.setValue('C');
    +  numEnumEntry.setValue(3);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/registry.js b/mapbox-gl/closure-library/closure/goog/tweak/registry.js
    new file mode 100644
    index 0000000000..8866e87765
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/registry.js
    @@ -0,0 +1,315 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition for goog.tweak.Registry.
    + * Most clients should not use this class directly, but instead use the API
    + * defined in tweak.js. One possible use case for directly using TweakRegistry
    + * is to register tweaks that are not known at compile time.
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + */
    +
    +goog.provide('goog.tweak.Registry');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.log');
    +goog.require('goog.string');
    +goog.require('goog.tweak.BasePrimitiveSetting');
    +goog.require('goog.tweak.BaseSetting');
    +goog.require('goog.tweak.BooleanSetting');
    +goog.require('goog.tweak.NumericSetting');
    +goog.require('goog.tweak.StringSetting');
    +goog.require('goog.uri.utils');
    +
    +
    +
    +/**
    + * Singleton that manages all tweaks. This should be instantiated only from
    + * goog.tweak.getRegistry().
    + * @param {string} queryParams Value of window.location.search.
    + * @param {!Object<string|number|boolean>} compilerOverrides Default value
    + *     overrides set by the compiler.
    + * @constructor
    + * @final
    + */
    +goog.tweak.Registry = function(queryParams, compilerOverrides) {
    +  /**
    +   * A map of entry id -> entry object
    +   * @type {!Object<!goog.tweak.BaseEntry>}
    +   * @private
    +   */
    +  this.entryMap_ = {};
    +
    +  /**
    +   * The map of query params to use when initializing entry settings.
    +   * @type {!Object<string>}
    +   * @private
    +   */
    +  this.parsedQueryParams_ = goog.tweak.Registry.parseQueryParams(queryParams);
    +
    +  /**
    +   * List of callbacks to call when a new entry is registered.
    +   * @type {!Array<!Function>}
    +   * @private
    +   */
    +  this.onRegisterListeners_ = [];
    +
    +  /**
    +   * A map of entry ID -> default value override for overrides set by the
    +   * compiler.
    +   * @type {!Object<string|number|boolean>}
    +   * @private
    +   */
    +  this.compilerDefaultValueOverrides_ = compilerOverrides;
    +
    +  /**
    +   * A map of entry ID -> default value override for overrides set by
    +   * goog.tweak.overrideDefaultValue().
    +   * @type {!Object<string|number|boolean>}
    +   * @private
    +   */
    +  this.defaultValueOverrides_ = {};
    +};
    +
    +
    +/**
    + * The logger for this class.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.tweak.Registry.prototype.logger_ =
    +    goog.log.getLogger('goog.tweak.Registry');
    +
    +
    +/**
    + * Simple parser for query params. Makes all keys lower-case.
    + * @param {string} queryParams The part of the url between the ? and the #.
    + * @return {!Object<string>} map of key->value.
    + */
    +goog.tweak.Registry.parseQueryParams = function(queryParams) {
    +  // Strip off the leading ? and split on &.
    +  var parts = queryParams.substr(1).split('&');
    +  var ret = {};
    +
    +  for (var i = 0, il = parts.length; i < il; ++i) {
    +    var entry = parts[i].split('=');
    +    if (entry[0]) {
    +      ret[goog.string.urlDecode(entry[0]).toLowerCase()] =
    +          goog.string.urlDecode(entry[1] || '');
    +    }
    +  }
    +  return ret;
    +};
    +
    +
    +/**
    + * Registers the given tweak setting/action.
    + * @param {goog.tweak.BaseEntry} entry The entry.
    + */
    +goog.tweak.Registry.prototype.register = function(entry) {
    +  var id = entry.getId();
    +  var oldBaseEntry = this.entryMap_[id];
    +  if (oldBaseEntry) {
    +    if (oldBaseEntry == entry) {
    +      goog.log.warning(this.logger_, 'Tweak entry registered twice: ' + id);
    +      return;
    +    }
    +    goog.asserts.fail(
    +        'Tweak entry registered twice and with different types: ' + id);
    +  }
    +
    +  // Check for a default value override, either from compiler flags or from a
    +  // call to overrideDefaultValue().
    +  var defaultValueOverride = (id in this.compilerDefaultValueOverrides_) ?
    +      this.compilerDefaultValueOverrides_[id] : this.defaultValueOverrides_[id];
    +  if (goog.isDef(defaultValueOverride)) {
    +    goog.asserts.assertInstanceof(entry, goog.tweak.BasePrimitiveSetting,
    +        'Cannot set the default value of non-primitive setting %s',
    +        entry.label);
    +    entry.setDefaultValue(defaultValueOverride);
    +  }
    +
    +  // Set its value from the query params.
    +  if (entry instanceof goog.tweak.BaseSetting) {
    +    if (entry.getParamName()) {
    +      entry.setInitialQueryParamValue(
    +          this.parsedQueryParams_[entry.getParamName()]);
    +    }
    +  }
    +
    +  this.entryMap_[id] = entry;
    +  // Call all listeners.
    +  for (var i = 0, callback; callback = this.onRegisterListeners_[i]; ++i) {
    +    callback(entry);
    +  }
    +};
    +
    +
    +/**
    + * Adds a callback to be called whenever a new tweak is added.
    + * @param {!Function} func The callback.
    + */
    +goog.tweak.Registry.prototype.addOnRegisterListener = function(func) {
    +  this.onRegisterListeners_.push(func);
    +};
    +
    +
    +/**
    + * @param {string} id The unique string that identifies this entry.
    + * @return {boolean} Whether a tweak with the given ID is registered.
    + */
    +goog.tweak.Registry.prototype.hasEntry = function(id) {
    +  return id in this.entryMap_;
    +};
    +
    +
    +/**
    + * Returns the BaseEntry with the given ID. Asserts if it does not exists.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {!goog.tweak.BaseEntry} The entry.
    + */
    +goog.tweak.Registry.prototype.getEntry = function(id) {
    +  var ret = this.entryMap_[id];
    +  goog.asserts.assert(ret, 'Tweak not registered: %s', id);
    +  return ret;
    +};
    +
    +
    +/**
    + * Returns the boolean setting with the given ID. Asserts if the ID does not
    + * refer to a registered entry or if it refers to one of the wrong type.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {!goog.tweak.BooleanSetting} The entry.
    + */
    +goog.tweak.Registry.prototype.getBooleanSetting = function(id) {
    +  var entry = this.getEntry(id);
    +  goog.asserts.assertInstanceof(entry, goog.tweak.BooleanSetting,
    +      'getBooleanSetting called on wrong type of BaseSetting');
    +  return /** @type {!goog.tweak.BooleanSetting} */ (entry);
    +};
    +
    +
    +/**
    + * Returns the string setting with the given ID. Asserts if the ID does not
    + * refer to a registered entry or if it refers to one of the wrong type.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {!goog.tweak.StringSetting} The entry.
    + */
    +goog.tweak.Registry.prototype.getStringSetting = function(id) {
    +  var entry = this.getEntry(id);
    +  goog.asserts.assertInstanceof(entry, goog.tweak.StringSetting,
    +      'getStringSetting called on wrong type of BaseSetting');
    +  return /** @type {!goog.tweak.StringSetting} */ (entry);
    +};
    +
    +
    +/**
    + * Returns the numeric setting with the given ID. Asserts if the ID does not
    + * refer to a registered entry or if it refers to one of the wrong type.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {!goog.tweak.NumericSetting} The entry.
    + */
    +goog.tweak.Registry.prototype.getNumericSetting = function(id) {
    +  var entry = this.getEntry(id);
    +  goog.asserts.assertInstanceof(entry, goog.tweak.NumericSetting,
    +      'getNumericSetting called on wrong type of BaseSetting');
    +  return /** @type {!goog.tweak.NumericSetting} */ (entry);
    +};
    +
    +
    +/**
    + * Creates and returns an array of all BaseSetting objects with an associted
    + * query parameter.
    + * @param {boolean} excludeChildEntries Exclude BooleanInGroupSettings.
    + * @param {boolean} excludeNonSettings Exclude entries that are not subclasses
    + *     of BaseSetting.
    + * @return {!Array<!goog.tweak.BaseSetting>} The settings.
    + */
    +goog.tweak.Registry.prototype.extractEntries =
    +    function(excludeChildEntries, excludeNonSettings) {
    +  var entries = [];
    +  for (var id in this.entryMap_) {
    +    var entry = this.entryMap_[id];
    +    if (entry instanceof goog.tweak.BaseSetting) {
    +      if (excludeChildEntries && !entry.getParamName()) {
    +        continue;
    +      }
    +    } else if (excludeNonSettings) {
    +      continue;
    +    }
    +    entries.push(entry);
    +  }
    +  return entries;
    +};
    +
    +
    +/**
    + * Returns the query part of the URL that will apply all set tweaks.
    + * @param {string=} opt_existingSearchStr The part of the url between the ? and
    + *     the #. Uses window.location.search if not given.
    + * @return {string} The query string.
    + */
    +goog.tweak.Registry.prototype.makeUrlQuery =
    +    function(opt_existingSearchStr) {
    +  var existingParams = opt_existingSearchStr == undefined ?
    +      window.location.search : opt_existingSearchStr;
    +
    +  var sortedEntries = this.extractEntries(true /* excludeChildEntries */,
    +                                          true /* excludeNonSettings */);
    +  // Sort the params so that the urlQuery has stable ordering.
    +  sortedEntries.sort(function(a, b) {
    +    return goog.array.defaultCompare(a.getParamName(), b.getParamName());
    +  });
    +
    +  // Add all values that are not set to their defaults.
    +  var keysAndValues = [];
    +  for (var i = 0, entry; entry = sortedEntries[i]; ++i) {
    +    var encodedValue = entry.getNewValueEncoded();
    +    if (encodedValue != null) {
    +      keysAndValues.push(entry.getParamName(), encodedValue);
    +    }
    +    // Strip all tweak query params from the existing query string. This will
    +    // make the final query string contain only the tweak settings that are set
    +    // to their non-default values and also maintain non-tweak related query
    +    // parameters.
    +    existingParams = goog.uri.utils.removeParam(existingParams,
    +        encodeURIComponent(/** @type {string} */ (entry.getParamName())));
    +  }
    +
    +  var tweakParams = goog.uri.utils.buildQueryData(keysAndValues);
    +  // Decode spaces and commas in order to make the URL more readable.
    +  tweakParams = tweakParams.replace(/%2C/g, ',').replace(/%20/g, '+');
    +  return !tweakParams ? existingParams :
    +      existingParams ? existingParams + '&' + tweakParams :
    +      '?' + tweakParams;
    +};
    +
    +
    +/**
    + * Sets a default value to use for the given tweak instead of the one passed
    + * to the register* function. This function must be called before the tweak is
    + * registered.
    + * @param {string} id The unique string that identifies the entry.
    + * @param {string|number|boolean} value The replacement value to be used as the
    + *     default value for the setting.
    + */
    +goog.tweak.Registry.prototype.overrideDefaultValue = function(id, value) {
    +  goog.asserts.assert(!this.hasEntry(id),
    +      'goog.tweak.overrideDefaultValue must be called before the tweak is ' +
    +      'registered. Tweak: %s', id);
    +  this.defaultValueOverrides_[id] = value;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/registry_test.html b/mapbox-gl/closure-library/closure/goog/tweak/registry_test.html
    new file mode 100644
    index 0000000000..9e7cfae884
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/registry_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<!--
    +Author: agrieve@google.com (Andrew Grieve)
    +-->
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.tweak.Registry
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.tweak.RegistryTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/registry_test.js b/mapbox-gl/closure-library/closure/goog/tweak/registry_test.js
    new file mode 100644
    index 0000000000..fda18d7196
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/registry_test.js
    @@ -0,0 +1,137 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.tweak.RegistryTest');
    +goog.setTestOnly('goog.tweak.RegistryTest');
    +
    +goog.require('goog.asserts.AssertionError');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.tweak');
    +/** @suppress {extraRequire} needed for createRegistryEntries. */
    +goog.require('goog.tweak.testhelpers');
    +
    +var registry;
    +
    +function setUp() {
    +  createRegistryEntries('');
    +  registry = goog.tweak.getRegistry();
    +}
    +
    +function tearDown() {
    +  goog.tweak.registry_ = null;
    +}
    +
    +function testGetBaseEntry() {
    +  // Initial values
    +  assertEquals('wrong bool1 object', boolEntry,
    +      registry.getBooleanSetting('Bool'));
    +  assertEquals('wrong string object', strEntry,
    +      registry.getStringSetting('Str'));
    +  assertEquals('wrong numeric object', numEntry,
    +      registry.getNumericSetting('Num'));
    +  assertEquals('wrong button object', buttonEntry,
    +      registry.getEntry('Button'));
    +  assertEquals('wrong button object', boolGroup,
    +      registry.getEntry('BoolGroup'));
    +}
    +
    +function testInitializeFromQueryParams() {
    +  var testCase = 0;
    +  function assertQuery(queryStr, boolValue, enumValue, strValue, subBoolValue,
    +      subBoolValue2) {
    +    createRegistryEntries(queryStr);
    +    assertEquals('Wrong bool value for query: ' + queryStr, boolValue,
    +        boolEntry.getValue());
    +    assertEquals('Wrong enum value for query: ' + queryStr, enumValue,
    +        strEnumEntry.getValue());
    +    assertEquals('Wrong str value for query: ' + queryStr, strValue,
    +        strEntry.getValue());
    +    assertEquals('Wrong BoolOne value for query: ' + queryStr, subBoolValue,
    +        boolOneEntry.getValue());
    +    assertEquals('Wrong BoolTwo value for query: ' + queryStr, subBoolValue2,
    +        boolTwoEntry.getValue());
    +  }
    +  assertQuery('?dummy=1&bool=&enum=&s=', false, '', '', false, true);
    +  assertQuery('?bool=0&enum=A&s=a', false, 'A', 'a', false, true);
    +  assertQuery('?bool=1&enum=A', true, 'A', '', false, true);
    +  assertQuery('?bool=true&enum=B&s=as+df', true, 'B', 'as df', false, true);
    +  assertQuery('?enum=C', false, 'C', '', false, true);
    +  assertQuery('?enum=C&boolgroup=-booltwo', false, 'C', '', false, false);
    +  assertQuery('?enum=C&boolgroup=b1,-booltwo', false, 'C', '', true, false);
    +  assertQuery('?enum=C&boolgroup=b1', false, 'C', '', true, true);
    +  assertQuery('?s=a+b%20c%26', false, 'A', 'a b c&', false, true);
    +}
    +
    +function testMakeUrlQuery() {
    +  assertEquals('All values are default.',
    +      '', registry.makeUrlQuery(''));
    +  assertEquals('All values are default - with existing params.',
    +      '?super=pudu', registry.makeUrlQuery('?super=pudu'));
    +
    +  boolEntry.setValue(true);
    +  numEnumEntry.setValue(2);
    +  strEntry.setValue('f o&o');
    +  assertEquals('Wrong query string 1.',
    +      '?bool=1&enum2=2&s=f+o%26o',
    +      registry.makeUrlQuery('?bool=1'));
    +  assertEquals('Wrong query string 1 - with existing params.',
    +      '?super=pudu&bool=1&enum2=2&s=f+o%26o',
    +      registry.makeUrlQuery('?bool=0&s=g&super=pudu'));
    +
    +  boolOneEntry.setValue(true);
    +  assertEquals('Wrong query string 2.',
    +      '?bool=1&boolgroup=B1&enum2=2&s=f+o%26o',
    +      registry.makeUrlQuery(''));
    +
    +  boolTwoEntry.setValue(false);
    +  assertEquals('Wrong query string 3.',
    +      '?bool=1&boolgroup=B1,-booltwo&enum2=2&s=f+o%26o',
    +      registry.makeUrlQuery(''));
    +}
    +
    +function testOverrideDefaultValue_calledBefore() {
    +  registry.overrideDefaultValue('b', false);
    +  registry.overrideDefaultValue('b', true);
    +  goog.tweak.registerBoolean('b', 'b desc');
    +  var bEntry = registry.getEntry('b');
    +  assertTrue('Default value should be true.', bEntry.getDefaultValue());
    +  assertTrue('Value should be true.', bEntry.getValue());
    +}
    +
    +function testOverrideDefaultValue_calledAfter() {
    +  var exception = assertThrows('Should assert.', function() {
    +    registry.overrideDefaultValue('Bool2', false);
    +  });
    +  assertTrue('Wrong exception',
    +      exception instanceof goog.asserts.AssertionError);
    +}
    +
    +function testCompilerOverrideDefaultValue() {
    +  createRegistryEntries('', { 'b': true });
    +  registry = goog.tweak.getRegistry();
    +  goog.tweak.registerBoolean('b', 'b desc');
    +  var bEntry = registry.getEntry('b');
    +  assertTrue('Default value should be true.', bEntry.getDefaultValue());
    +  assertTrue('Value should be true.', bEntry.getValue());
    +}
    +
    +function testCompilerAndJsOverrideDefaultValue() {
    +  createRegistryEntries('', { 'b': false });
    +  registry = goog.tweak.getRegistry();
    +  registry.overrideDefaultValue('b', true);
    +  goog.tweak.registerBoolean('b', 'b desc', true);
    +  var bEntry = registry.getEntry('b');
    +  assertFalse('Default value should be false.', bEntry.getDefaultValue());
    +  assertFalse('Value should be false.', bEntry.getValue());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/testhelpers.js b/mapbox-gl/closure-library/closure/goog/tweak/testhelpers.js
    new file mode 100644
    index 0000000000..b1427d0a30
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/testhelpers.js
    @@ -0,0 +1,123 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Common test functions for tweak unit tests.
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + * @package
    + */
    +
    +goog.provide('goog.tweak.testhelpers');
    +
    +goog.setTestOnly();
    +
    +goog.require('goog.tweak');
    +goog.require('goog.tweak.BooleanGroup');
    +goog.require('goog.tweak.BooleanInGroupSetting');
    +goog.require('goog.tweak.BooleanSetting');
    +goog.require('goog.tweak.ButtonAction');
    +goog.require('goog.tweak.NumericSetting');
    +goog.require('goog.tweak.Registry');
    +goog.require('goog.tweak.StringSetting');
    +
    +
    +var boolEntry;
    +var boolEntry2;
    +var strEntry;
    +var strEntry2;
    +var strEnumEntry;
    +var numEntry;
    +var numEnumEntry;
    +var boolGroup;
    +var boolOneEntry;
    +var boolTwoEntry;
    +var buttonEntry;
    +
    +
    +/**
    + * Creates a registry with some entries in it.
    + * @param {string} queryParams The query parameter string to use for the
    + *     registry.
    + * @param {!Object<string|number|boolean>=} opt_compilerOverrides Compiler
    + *     overrides.
    + * @suppress {accessControls} Private state is accessed for test purposes.
    + */
    +function createRegistryEntries(queryParams, opt_compilerOverrides) {
    +  // Initialize the registry with the given query string.
    +  var registry =
    +      new goog.tweak.Registry(queryParams, opt_compilerOverrides || {});
    +  goog.tweak.registry_ = registry;
    +
    +  boolEntry = new goog.tweak.BooleanSetting('Bool', 'The bool1');
    +  registry.register(boolEntry);
    +
    +  boolEntry2 = new goog.tweak.BooleanSetting('Bool2', 'The bool2');
    +  boolEntry2.setDefaultValue(true);
    +  registry.register(boolEntry2);
    +
    +  strEntry = new goog.tweak.StringSetting('Str', 'The str1');
    +  strEntry.setParamName('s');
    +  registry.register(strEntry);
    +
    +  strEntry2 = new goog.tweak.StringSetting('Str2', 'The str2');
    +  strEntry2.setDefaultValue('foo');
    +  registry.register(strEntry2);
    +
    +  strEnumEntry = new goog.tweak.StringSetting('Enum', 'The enum');
    +  strEnumEntry.setValidValues(['A', 'B', 'C']);
    +  strEnumEntry.setRestartRequired(false);
    +  registry.register(strEnumEntry);
    +
    +  numEntry = new goog.tweak.NumericSetting('Num', 'The num');
    +  numEntry.setDefaultValue(99);
    +  registry.register(numEntry);
    +
    +  numEnumEntry = new goog.tweak.NumericSetting('Enum2', 'The 2nd enum');
    +  numEnumEntry.setValidValues([1, 2, 3]);
    +  numEnumEntry.setRestartRequired(false);
    +  numEnumEntry.label = 'Enum the second&';
    +  registry.register(numEnumEntry);
    +
    +  boolGroup = new goog.tweak.BooleanGroup('BoolGroup', 'The bool group');
    +  registry.register(boolGroup);
    +
    +  boolOneEntry = new goog.tweak.BooleanInGroupSetting('BoolOne', 'Desc for 1',
    +      boolGroup);
    +  boolOneEntry.setToken('B1');
    +  boolOneEntry.setRestartRequired(false);
    +  boolGroup.addChild(boolOneEntry);
    +  registry.register(boolOneEntry);
    +
    +  boolTwoEntry = new goog.tweak.BooleanInGroupSetting('BoolTwo', 'Desc for 2',
    +      boolGroup);
    +  boolTwoEntry.setDefaultValue(true);
    +  boolGroup.addChild(boolTwoEntry);
    +  registry.register(boolTwoEntry);
    +
    +  buttonEntry = new goog.tweak.ButtonAction('Button', 'The Btn',
    +      goog.nullFunction);
    +  buttonEntry.label = '<btn>';
    +  registry.register(buttonEntry);
    +
    +  var nsBoolGroup = new goog.tweak.BooleanGroup('foo.bar.BoolGroup',
    +      'Namespaced Bool Group');
    +  registry.register(nsBoolGroup);
    +  var nsBool = new goog.tweak.BooleanInGroupSetting('foo.bar.BoolOne',
    +      'Desc for Namespaced 1', nsBoolGroup);
    +  nsBoolGroup.addChild(nsBool);
    +  registry.register(nsBool);
    +}
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/tweak.js b/mapbox-gl/closure-library/closure/goog/tweak/tweak.js
    new file mode 100644
    index 0000000000..1ba9f07e2d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/tweak.js
    @@ -0,0 +1,301 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides facilities for creating and querying tweaks.
    + * @see http://code.google.com/p/closure-library/wiki/UsingTweaks
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + */
    +
    +goog.provide('goog.tweak');
    +goog.provide('goog.tweak.ConfigParams');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.tweak.BaseSetting');
    +goog.require('goog.tweak.BooleanGroup');
    +goog.require('goog.tweak.BooleanInGroupSetting');
    +goog.require('goog.tweak.BooleanSetting');
    +goog.require('goog.tweak.ButtonAction');
    +goog.require('goog.tweak.NumericSetting');
    +goog.require('goog.tweak.Registry');
    +goog.require('goog.tweak.StringSetting');
    +
    +
    +/**
    + * Calls to this function are overridden by the compiler by the processTweaks
    + * pass. It returns the overrides to default values for tweaks set by compiler
    + * options.
    + * @return {!Object<number|string|boolean>} A map of tweakId -> defaultValue.
    + * @private
    + */
    +goog.tweak.getCompilerOverrides_ = function() {
    +  return {};
    +};
    +
    +
    +/**
    + * The global reference to the registry, if it exists.
    + * @type {goog.tweak.Registry}
    + * @private
    + */
    +goog.tweak.registry_ = null;
    +
    +
    +/**
    + * The boolean group set by beginBooleanGroup and cleared by endBooleanGroup.
    + * @type {goog.tweak.BooleanGroup}
    + * @private
    + */
    +goog.tweak.activeBooleanGroup_ = null;
    +
    +
    +/**
    + * Returns/creates the registry singleton.
    + * @return {!goog.tweak.Registry} The tweak registry.
    + */
    +goog.tweak.getRegistry = function() {
    +  if (!goog.tweak.registry_) {
    +    var queryString = window.location.search;
    +    var overrides = goog.tweak.getCompilerOverrides_();
    +    goog.tweak.registry_ = new goog.tweak.Registry(queryString, overrides);
    +  }
    +  return goog.tweak.registry_;
    +};
    +
    +
    +/**
    + * Type for configParams.
    + * TODO(agrieve): Remove |Object when optional fields in struct types are
    + *     implemented.
    + * @typedef {{
    + *     label:(string|undefined),
    + *     validValues:(!Array<string>|!Array<number>|undefined),
    + *     paramName:(string|undefined),
    + *     restartRequired:(boolean|undefined),
    + *     callback:(Function|undefined),
    + *     token:(string|undefined)
    + *     }|!Object}
    + */
    +goog.tweak.ConfigParams;
    +
    +
    +/**
    + * Applies all extra configuration parameters in configParams.
    + * @param {!goog.tweak.BaseEntry} entry The entry to apply them to.
    + * @param {!goog.tweak.ConfigParams} configParams Extra configuration
    + *     parameters.
    + * @private
    + */
    +goog.tweak.applyConfigParams_ = function(entry, configParams) {
    +  if (configParams.label) {
    +    entry.label = configParams.label;
    +    delete configParams.label;
    +  }
    +  if (configParams.validValues) {
    +    goog.asserts.assert(entry instanceof goog.tweak.StringSetting ||
    +        entry instanceof goog.tweak.NumericSetting,
    +        'Cannot set validValues on tweak: %s', entry.getId());
    +    entry.setValidValues(configParams.validValues);
    +    delete configParams.validValues;
    +  }
    +  if (goog.isDef(configParams.paramName)) {
    +    goog.asserts.assertInstanceof(entry, goog.tweak.BaseSetting,
    +        'Cannot set paramName on tweak: %s', entry.getId());
    +    entry.setParamName(configParams.paramName);
    +    delete configParams.paramName;
    +  }
    +  if (goog.isDef(configParams.restartRequired)) {
    +    entry.setRestartRequired(configParams.restartRequired);
    +    delete configParams.restartRequired;
    +  }
    +  if (configParams.callback) {
    +    entry.addCallback(configParams.callback);
    +    delete configParams.callback;
    +    goog.asserts.assert(
    +        !entry.isRestartRequired() || (configParams.restartRequired == false),
    +        'Tweak %s should set restartRequired: false, when adding a callback.',
    +        entry.getId());
    +  }
    +  if (configParams.token) {
    +    goog.asserts.assertInstanceof(entry, goog.tweak.BooleanInGroupSetting,
    +        'Cannot set token on tweak: %s', entry.getId());
    +    entry.setToken(configParams.token);
    +    delete configParams.token;
    +  }
    +  for (var key in configParams) {
    +    goog.asserts.fail('Unknown config options (' + key + '=' +
    +        configParams[key] + ') for tweak ' + entry.getId());
    +  }
    +};
    +
    +
    +/**
    + * Registers a tweak using the given factoryFunc.
    + * @param {!goog.tweak.BaseEntry} entry The entry to register.
    + * @param {boolean|string|number=} opt_defaultValue Default value.
    + * @param {goog.tweak.ConfigParams=} opt_configParams Extra
    + *     configuration parameters.
    + * @private
    + */
    +goog.tweak.doRegister_ = function(entry, opt_defaultValue, opt_configParams) {
    +  if (opt_configParams) {
    +    goog.tweak.applyConfigParams_(entry, opt_configParams);
    +  }
    +  if (opt_defaultValue != undefined) {
    +    entry.setDefaultValue(opt_defaultValue);
    +  }
    +  if (goog.tweak.activeBooleanGroup_) {
    +    goog.asserts.assertInstanceof(entry, goog.tweak.BooleanInGroupSetting,
    +        'Forgot to end Boolean Group: %s',
    +        goog.tweak.activeBooleanGroup_.getId());
    +    goog.tweak.activeBooleanGroup_.addChild(
    +        /** @type {!goog.tweak.BooleanInGroupSetting} */ (entry));
    +  }
    +  goog.tweak.getRegistry().register(entry);
    +};
    +
    +
    +/**
    + * Creates and registers a group of BooleanSettings that are all set by a
    + * single query parameter. A call to goog.tweak.endBooleanGroup() must be used
    + * to close this group. Only goog.tweak.registerBoolean() calls are allowed with
    + * the beginBooleanGroup()/endBooleanGroup().
    + * @param {string} id The unique ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration
    + *     parameters.
    + */
    +goog.tweak.beginBooleanGroup = function(id, description, opt_configParams) {
    +  var entry = new goog.tweak.BooleanGroup(id, description);
    +  goog.tweak.doRegister_(entry, undefined, opt_configParams);
    +  goog.tweak.activeBooleanGroup_ = entry;
    +};
    +
    +
    +/**
    + * Stops adding boolean entries to the active boolean group.
    + */
    +goog.tweak.endBooleanGroup = function() {
    +  goog.tweak.activeBooleanGroup_ = null;
    +};
    +
    +
    +/**
    + * Creates and registers a BooleanSetting.
    + * @param {string} id The unique ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {boolean=} opt_defaultValue The default value for the setting.
    + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration
    + *     parameters.
    + */
    +goog.tweak.registerBoolean =
    +    function(id, description, opt_defaultValue, opt_configParams) {
    +  // TODO(agrieve): There is a bug in the compiler that causes these calls not
    +  //     to be stripped without this outer if. Might be Issue #90.
    +  if (goog.tweak.activeBooleanGroup_) {
    +    var entry = new goog.tweak.BooleanInGroupSetting(id, description,
    +        goog.tweak.activeBooleanGroup_);
    +  } else {
    +    entry = new goog.tweak.BooleanSetting(id, description);
    +  }
    +  goog.tweak.doRegister_(entry, opt_defaultValue, opt_configParams);
    +};
    +
    +
    +/**
    + * Creates and registers a StringSetting.
    + * @param {string} id The unique ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {string=} opt_defaultValue The default value for the setting.
    + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration
    + *     parameters.
    + */
    +goog.tweak.registerString =
    +    function(id, description, opt_defaultValue, opt_configParams) {
    +  goog.tweak.doRegister_(new goog.tweak.StringSetting(id, description),
    +                         opt_defaultValue, opt_configParams);
    +};
    +
    +
    +/**
    + * Creates and registers a NumericSetting.
    + * @param {string} id The unique ID for the setting.
    + * @param {string} description A description of what the setting does.
    + * @param {number=} opt_defaultValue The default value for the setting.
    + * @param {goog.tweak.ConfigParams=} opt_configParams Extra configuration
    + *     parameters.
    + */
    +goog.tweak.registerNumber =
    +    function(id, description, opt_defaultValue, opt_configParams) {
    +  goog.tweak.doRegister_(new goog.tweak.NumericSetting(id, description),
    +                         opt_defaultValue, opt_configParams);
    +};
    +
    +
    +/**
    + * Creates and registers a ButtonAction.
    + * @param {string} id The unique ID for the setting.
    + * @param {string} description A description of what the action does.
    + * @param {!Function} callback Function to call when the button is clicked.
    + * @param {string=} opt_label The button text (instead of the ID).
    + */
    +goog.tweak.registerButton = function(id, description, callback, opt_label) {
    +  var tweak = new goog.tweak.ButtonAction(id, description, callback);
    +  tweak.label = opt_label || tweak.label;
    +  goog.tweak.doRegister_(tweak);
    +};
    +
    +
    +/**
    + * Sets a default value to use for the given tweak instead of the one passed
    + * to the register* function. This function must be called before the tweak is
    + * registered.
    + * @param {string} id The unique string that identifies the entry.
    + * @param {string|number|boolean} value The new default value for the tweak.
    + */
    +goog.tweak.overrideDefaultValue = function(id, value) {
    +  goog.tweak.getRegistry().overrideDefaultValue(id, value);
    +};
    +
    +
    +/**
    + * Returns the value of the boolean setting with the given ID.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {boolean} The value of the tweak.
    + */
    +goog.tweak.getBoolean = function(id) {
    +  return goog.tweak.getRegistry().getBooleanSetting(id).getValue();
    +};
    +
    +
    +/**
    + * Returns the value of the string setting with the given ID,
    + * @param {string} id The unique string that identifies this entry.
    + * @return {string} The value of the tweak.
    + */
    +goog.tweak.getString = function(id) {
    +  return goog.tweak.getRegistry().getStringSetting(id).getValue();
    +};
    +
    +
    +/**
    + * Returns the value of the numeric setting with the given ID.
    + * @param {string} id The unique string that identifies this entry.
    + * @return {number} The value of the tweak.
    + */
    +goog.tweak.getNumber = function(id) {
    +  return goog.tweak.getRegistry().getNumericSetting(id).getValue();
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/tweakui.js b/mapbox-gl/closure-library/closure/goog/tweak/tweakui.js
    new file mode 100644
    index 0000000000..e77f99e879
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/tweakui.js
    @@ -0,0 +1,826 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A UI for editing tweak settings / clicking tweak actions.
    + *
    + * @author agrieve@google.com (Andrew Grieve)
    + */
    +
    +goog.provide('goog.tweak.EntriesPanel');
    +goog.provide('goog.tweak.TweakUi');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.tweak');
    +goog.require('goog.tweak.BaseEntry');
    +goog.require('goog.tweak.BooleanGroup');
    +goog.require('goog.tweak.BooleanInGroupSetting');
    +goog.require('goog.tweak.BooleanSetting');
    +goog.require('goog.tweak.ButtonAction');
    +goog.require('goog.tweak.NumericSetting');
    +goog.require('goog.tweak.StringSetting');
    +goog.require('goog.ui.Zippy');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A UI for editing tweak settings / clicking tweak actions.
    + * @param {!goog.tweak.Registry} registry The registry to render.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with.
    + * @constructor
    + * @final
    + */
    +goog.tweak.TweakUi = function(registry, opt_domHelper) {
    +  /**
    +   * The registry to create a UI from.
    +   * @type {!goog.tweak.Registry}
    +   * @private
    +   */
    +  this.registry_ = registry;
    +
    +  /**
    +   * The element to display when the UI is visible.
    +   * @type {goog.tweak.EntriesPanel|undefined}
    +   * @private
    +   */
    +  this.entriesPanel_;
    +
    +  /**
    +   * The DomHelper to render with.
    +   * @type {!goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  // Listen for newly registered entries (happens with lazy-loaded modules).
    +  registry.addOnRegisterListener(goog.bind(this.onNewRegisteredEntry_, this));
    +};
    +
    +
    +/**
    + * The CSS class name unique to the root tweak panel div.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.ROOT_PANEL_CLASS_ = goog.getCssName('goog-tweak-root');
    +
    +
    +/**
    + * The CSS class name unique to the tweak entry div.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.ENTRY_CSS_CLASS_ = goog.getCssName('goog-tweak-entry');
    +
    +
    +/**
    + * The CSS classes for each tweak entry div.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.ENTRY_CSS_CLASSES_ = goog.tweak.TweakUi.ENTRY_CSS_CLASS_ +
    +    ' ' + goog.getCssName('goog-inline-block');
    +
    +
    +/**
    + * The CSS classes for each namespace tweak entry div.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ =
    +    goog.tweak.TweakUi.ENTRY_CSS_CLASS_;
    +
    +
    +/**
    + * Marker that the style sheet has already been installed.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ =
    +    '__closure_tweak_installed_';
    +
    +
    +/**
    + * CSS used by TweakUI.
    + * @type {string}
    + * @private
    + */
    +goog.tweak.TweakUi.CSS_STYLES_ = (function() {
    +  var MOBILE = goog.userAgent.MOBILE;
    +  var IE = goog.userAgent.IE;
    +  var ENTRY_CLASS = '.' + goog.tweak.TweakUi.ENTRY_CSS_CLASS_;
    +  var ROOT_PANEL_CLASS = '.' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_;
    +  var GOOG_INLINE_BLOCK_CLASS = '.' + goog.getCssName('goog-inline-block');
    +  var ret = ROOT_PANEL_CLASS + '{background:#ffc; padding:0 4px}';
    +  // Make this work even if the user hasn't included common.css.
    +  if (!IE) {
    +    ret += GOOG_INLINE_BLOCK_CLASS + '{display:inline-block}';
    +  }
    +  // Space things out vertically for touch UIs.
    +  if (MOBILE) {
    +    ret += ROOT_PANEL_CLASS + ',' + ROOT_PANEL_CLASS + ' fieldset{' +
    +        'line-height:2em;' + '}';
    +  }
    +  return ret;
    +})();
    +
    +
    +/**
    + * Creates a TweakUi if tweaks are enabled.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with.
    + * @return {!Element|undefined} The root UI element or undefined if tweaks are
    + *     not enabled.
    + */
    +goog.tweak.TweakUi.create = function(opt_domHelper) {
    +  var registry = goog.tweak.getRegistry();
    +  if (registry) {
    +    var ui = new goog.tweak.TweakUi(registry, opt_domHelper);
    +    ui.render();
    +    return ui.getRootElement();
    +  }
    +};
    +
    +
    +/**
    + * Creates a TweakUi inside of a show/hide link.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with.
    + * @return {!Element|undefined} The root UI element or undefined if tweaks are
    + *     not enabled.
    + */
    +goog.tweak.TweakUi.createCollapsible = function(opt_domHelper) {
    +  var registry = goog.tweak.getRegistry();
    +  if (registry) {
    +    var dh = opt_domHelper || goog.dom.getDomHelper();
    +
    +    // The following strings are for internal debugging only.  No translation
    +    // necessary.  Do NOT wrap goog.getMsg() around these strings.
    +    var showLink = dh.createDom('a', {href: 'javascript:;'}, 'Show Tweaks');
    +    var hideLink = dh.createDom('a', {href: 'javascript:;'}, 'Hide Tweaks');
    +    var ret = dh.createDom('div', null, showLink);
    +
    +    var lazyCreate = function() {
    +      // Lazily render the UI.
    +      var ui = new goog.tweak.TweakUi(
    +          /** @type {!goog.tweak.Registry} */ (registry), dh);
    +      ui.render();
    +      // Put the hide link on the same line as the "Show Descriptions" link.
    +      // Set the style lazily because we can.
    +      hideLink.style.marginRight = '10px';
    +      var tweakElem = ui.getRootElement();
    +      tweakElem.insertBefore(hideLink, tweakElem.firstChild);
    +      ret.appendChild(tweakElem);
    +      return tweakElem;
    +    };
    +    new goog.ui.Zippy(showLink, lazyCreate, false /* expanded */, hideLink);
    +    return ret;
    +  }
    +};
    +
    +
    +/**
    + * Compares the given entries. Orders alphabetically and groups buttons and
    + * expandable groups.
    + * @param {goog.tweak.BaseEntry} a The first entry to compare.
    + * @param {goog.tweak.BaseEntry} b The second entry to compare.
    + * @return {number} Refer to goog.array.defaultCompare.
    + * @private
    + */
    +goog.tweak.TweakUi.entryCompare_ = function(a, b) {
    +  return (
    +      goog.array.defaultCompare(a instanceof goog.tweak.NamespaceEntry_,
    +          b instanceof goog.tweak.NamespaceEntry_) ||
    +      goog.array.defaultCompare(a instanceof goog.tweak.BooleanGroup,
    +          b instanceof goog.tweak.BooleanGroup) ||
    +      goog.array.defaultCompare(a instanceof goog.tweak.ButtonAction,
    +          b instanceof goog.tweak.ButtonAction) ||
    +      goog.array.defaultCompare(a.label, b.label) ||
    +      goog.array.defaultCompare(a.getId(), b.getId()));
    +};
    +
    +
    +/**
    + * @param {!goog.tweak.BaseEntry} entry The entry.
    + * @return {boolean} Returns whether the given entry contains sub-entries.
    + * @private
    + */
    +goog.tweak.TweakUi.isGroupEntry_ = function(entry) {
    +  return entry instanceof goog.tweak.NamespaceEntry_ ||
    +      entry instanceof goog.tweak.BooleanGroup;
    +};
    +
    +
    +/**
    + * Returns the list of entries from the given boolean group.
    + * @param {!goog.tweak.BooleanGroup} group The group to get the entries from.
    + * @return {!Array<!goog.tweak.BaseEntry>} The sorted entries.
    + * @private
    + */
    +goog.tweak.TweakUi.extractBooleanGroupEntries_ = function(group) {
    +  var ret = goog.object.getValues(group.getChildEntries());
    +  ret.sort(goog.tweak.TweakUi.entryCompare_);
    +  return ret;
    +};
    +
    +
    +/**
    + * @param {!goog.tweak.BaseEntry} entry The entry.
    + * @return {string} Returns the namespace for the entry, or '' if it is not
    + *     namespaced.
    + * @private
    + */
    +goog.tweak.TweakUi.extractNamespace_ = function(entry) {
    +  var namespaceMatch = /.+(?=\.)/.exec(entry.getId());
    +  return namespaceMatch ? namespaceMatch[0] : '';
    +};
    +
    +
    +/**
    + * @param {!goog.tweak.BaseEntry} entry The entry.
    + * @return {string} Returns the part of the label after the last period, unless
    + *     the label has been explicly set (it is different from the ID).
    + * @private
    + */
    +goog.tweak.TweakUi.getNamespacedLabel_ = function(entry) {
    +  var label = entry.label;
    +  if (label == entry.getId()) {
    +    label = label.substr(label.lastIndexOf('.') + 1);
    +  }
    +  return label;
    +};
    +
    +
    +/**
    + * @return {!Element} The root element. Must not be called before render().
    + */
    +goog.tweak.TweakUi.prototype.getRootElement = function() {
    +  goog.asserts.assert(this.entriesPanel_,
    +      'TweakUi.getRootElement called before render().');
    +  return this.entriesPanel_.getRootElement();
    +};
    +
    +
    +/**
    + * Reloads the page with query parameters set by the UI.
    + * @private
    + */
    +goog.tweak.TweakUi.prototype.restartWithAppliedTweaks_ = function() {
    +  var queryString = this.registry_.makeUrlQuery();
    +  var wnd = this.domHelper_.getWindow();
    +  if (queryString != wnd.location.search) {
    +    wnd.location.search = queryString;
    +  } else {
    +    wnd.location.reload();
    +  }
    +};
    +
    +
    +/**
    + * Installs the required CSS styles.
    + * @private
    + */
    +goog.tweak.TweakUi.prototype.installStyles_ = function() {
    +  // Use an marker to install the styles only once per document.
    +  // Styles are injected via JS instead of in a separate style sheet so that
    +  // they are automatically excluded when tweaks are stripped out.
    +  var doc = this.domHelper_.getDocument();
    +  if (!(goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ in doc)) {
    +    goog.style.installStyles(
    +        goog.tweak.TweakUi.CSS_STYLES_, doc);
    +    doc[goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_] = true;
    +  }
    +};
    +
    +
    +/**
    + * Creates the element to display when the UI is visible.
    + * @return {!Element} The root element.
    + */
    +goog.tweak.TweakUi.prototype.render = function() {
    +  this.installStyles_();
    +  var dh = this.domHelper_;
    +  // The submit button
    +  var submitButton = dh.createDom('button', {style: 'font-weight:bold'},
    +      'Apply Tweaks');
    +  submitButton.onclick = goog.bind(this.restartWithAppliedTweaks_, this);
    +
    +  var rootPanel = new goog.tweak.EntriesPanel([], dh);
    +  var rootPanelDiv = rootPanel.render(submitButton);
    +  rootPanelDiv.className += ' ' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_;
    +  this.entriesPanel_ = rootPanel;
    +
    +  var entries = this.registry_.extractEntries(true /* excludeChildEntries */,
    +      false /* excludeNonSettings */);
    +  for (var i = 0, entry; entry = entries[i]; i++) {
    +    this.insertEntry_(entry);
    +  }
    +
    +  return rootPanelDiv;
    +};
    +
    +
    +/**
    + * Updates the UI with the given entry.
    + * @param {!goog.tweak.BaseEntry} entry The newly registered entry.
    + * @private
    + */
    +goog.tweak.TweakUi.prototype.onNewRegisteredEntry_ = function(entry) {
    +  if (this.entriesPanel_) {
    +    this.insertEntry_(entry);
    +  }
    +};
    +
    +
    +/**
    + * Updates the UI with the given entry.
    + * @param {!goog.tweak.BaseEntry} entry The newly registered entry.
    + * @private
    + */
    +goog.tweak.TweakUi.prototype.insertEntry_ = function(entry) {
    +  var panel = this.entriesPanel_;
    +  var namespace = goog.tweak.TweakUi.extractNamespace_(entry);
    +
    +  if (namespace) {
    +    // Find the NamespaceEntry that the entry belongs to.
    +    var namespaceEntryId = goog.tweak.NamespaceEntry_.ID_PREFIX + namespace;
    +    var nsPanel = panel.childPanels[namespaceEntryId];
    +    if (nsPanel) {
    +      panel = nsPanel;
    +    } else {
    +      entry = new goog.tweak.NamespaceEntry_(namespace, [entry]);
    +    }
    +  }
    +  if (entry instanceof goog.tweak.BooleanInGroupSetting) {
    +    var group = entry.getGroup();
    +    // BooleanGroup entries are always registered before their
    +    // BooleanInGroupSettings.
    +    panel = panel.childPanels[group.getId()];
    +  }
    +  goog.asserts.assert(panel, 'Missing panel for entry %s', entry.getId());
    +  panel.insertEntry(entry);
    +};
    +
    +
    +
    +/**
    + * The body of the tweaks UI and also used for BooleanGroup.
    + * @param {!Array<!goog.tweak.BaseEntry>} entries The entries to show in the
    + *     panel.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with.
    + * @constructor
    + * @final
    + */
    +goog.tweak.EntriesPanel = function(entries, opt_domHelper) {
    +  /**
    +   * The entries to show in the panel.
    +   * @type {!Array<!goog.tweak.BaseEntry>} entries
    +   * @private
    +   */
    +  this.entries_ = entries;
    +
    +  var self = this;
    +  /**
    +   * The bound onclick handler for the help question marks.
    +   * @this {Element}
    +   * @private
    +   */
    +  this.boundHelpOnClickHandler_ = function() {
    +    self.onHelpClick_(this.parentNode);
    +  };
    +
    +  /**
    +   * The element that contains the UI.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.rootElem_;
    +
    +  /**
    +   * The element that contains all of the settings and the endElement.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.mainPanel_;
    +
    +  /**
    +   * Flips between true/false each time the "Toggle Descriptions" link is
    +   * clicked.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.showAllDescriptionsState_;
    +
    +  /**
    +   * The DomHelper to render with.
    +   * @type {!goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Map of tweak ID -> EntriesPanel for child panels (BooleanGroups).
    +   * @type {!Object<!goog.tweak.EntriesPanel>}
    +   */
    +  this.childPanels = {};
    +};
    +
    +
    +/**
    + * @return {!Element} Returns the expanded element. Must not be called before
    + *     render().
    + */
    +goog.tweak.EntriesPanel.prototype.getRootElement = function() {
    +  goog.asserts.assert(this.rootElem_,
    +      'EntriesPanel.getRootElement called before render().');
    +  return /** @type {!Element} */ (this.rootElem_);
    +};
    +
    +
    +/**
    + * Creates and returns the expanded element.
    + * The markup looks like:
    + * <div>
    + *   <a>Show Descriptions</a>
    + *   <div>
    + *      ...
    + *      {endElement}
    + *   </div>
    + * </div>
    + * @param {Element|DocumentFragment=} opt_endElement Element to insert after all
    + *     tweak entries.
    + * @return {!Element} The root element for the panel.
    + */
    +goog.tweak.EntriesPanel.prototype.render = function(opt_endElement) {
    +  var dh = this.domHelper_;
    +  var entries = this.entries_;
    +  var ret = dh.createDom('div');
    +
    +  var showAllDescriptionsLink = dh.createDom('a', {
    +    href: 'javascript:;',
    +    onclick: goog.bind(this.toggleAllDescriptions, this)
    +  }, 'Toggle all Descriptions');
    +  ret.appendChild(showAllDescriptionsLink);
    +
    +  // Add all of the entries.
    +  var mainPanel = dh.createElement('div');
    +  this.mainPanel_ = mainPanel;
    +  for (var i = 0, entry; entry = entries[i]; i++) {
    +    mainPanel.appendChild(this.createEntryElem_(entry));
    +  }
    +
    +  if (opt_endElement) {
    +    mainPanel.appendChild(opt_endElement);
    +  }
    +  ret.appendChild(mainPanel);
    +  this.rootElem_ = ret;
    +  return /** @type {!Element} */ (ret);
    +};
    +
    +
    +/**
    + * Inserts the given entry into the panel.
    + * @param {!goog.tweak.BaseEntry} entry The entry to insert.
    + */
    +goog.tweak.EntriesPanel.prototype.insertEntry = function(entry) {
    +  var insertIndex = -goog.array.binarySearch(this.entries_, entry,
    +      goog.tweak.TweakUi.entryCompare_) - 1;
    +  goog.asserts.assert(insertIndex >= 0, 'insertEntry failed for %s',
    +      entry.getId());
    +  goog.array.insertAt(this.entries_, entry, insertIndex);
    +  this.mainPanel_.insertBefore(
    +      this.createEntryElem_(entry),
    +      // IE doesn't like 'undefined' here.
    +      this.mainPanel_.childNodes[insertIndex] || null);
    +};
    +
    +
    +/**
    + * Creates and returns a form element for the given entry.
    + * @param {!goog.tweak.BaseEntry} entry The entry.
    + * @return {!Element} The root DOM element for the entry.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createEntryElem_ = function(entry) {
    +  var dh = this.domHelper_;
    +  var isGroupEntry = goog.tweak.TweakUi.isGroupEntry_(entry);
    +  var classes = isGroupEntry ? goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ :
    +      goog.tweak.TweakUi.ENTRY_CSS_CLASSES_;
    +  // Containers should not use label tags or else all descendent inputs will be
    +  // connected on desktop browsers.
    +  var containerNodeName = isGroupEntry ? 'span' : 'label';
    +  var ret = dh.createDom('div', classes,
    +      dh.createDom(containerNodeName, {
    +        // Make the hover text the description.
    +        title: entry.description,
    +        style: 'color:' + (entry.isRestartRequired() ? '' : 'blue')
    +      }, this.createTweakEntryDom_(entry)),
    +      // Add the expandable help question mark.
    +      this.createHelpElem_(entry));
    +  return ret;
    +};
    +
    +
    +/**
    + * Click handler for the help link.
    + * @param {Node} entryDiv The div that contains the tweak.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.onHelpClick_ = function(entryDiv) {
    +  this.showDescription_(entryDiv, !entryDiv.style.display);
    +};
    +
    +
    +/**
    + * Twiddle the DOM so that the entry within the given span is shown/hidden.
    + * @param {Node} entryDiv The div that contains the tweak.
    + * @param {boolean} show True to show, false to hide.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.showDescription_ =
    +    function(entryDiv, show) {
    +  var descriptionElem = entryDiv.lastChild.lastChild;
    +  goog.style.setElementShown(/** @type {Element} */ (descriptionElem), show);
    +  entryDiv.style.display = show ? 'block' : '';
    +};
    +
    +
    +/**
    + * Creates and returns a help element for the given entry.
    + * @param {goog.tweak.BaseEntry} entry The entry.
    + * @return {!Element} The root element of the created DOM.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createHelpElem_ = function(entry) {
    +  // The markup looks like:
    +  // <span onclick=...><b>?</b><span>{description}</span></span>
    +  var ret = this.domHelper_.createElement('span');
    +  ret.innerHTML = '<b style="padding:0 1em 0 .5em">?</b>' +
    +      '<span style="display:none;color:#666"></span>';
    +  ret.onclick = this.boundHelpOnClickHandler_;
    +  var descriptionElem = ret.lastChild;
    +  goog.dom.setTextContent(/** @type {Element} */ (descriptionElem),
    +      entry.description);
    +  if (!entry.isRestartRequired()) {
    +    descriptionElem.innerHTML +=
    +        ' <span style="color:blue">(no restart required)</span>';
    +  }
    +  return ret;
    +};
    +
    +
    +/**
    + * Show all entry descriptions (has the same effect as clicking on all ?'s).
    + */
    +goog.tweak.EntriesPanel.prototype.toggleAllDescriptions = function() {
    +  var show = !this.showAllDescriptionsState_;
    +  this.showAllDescriptionsState_ = show;
    +  var entryDivs = this.domHelper_.getElementsByTagNameAndClass('div',
    +      goog.tweak.TweakUi.ENTRY_CSS_CLASS_, this.rootElem_);
    +  for (var i = 0, div; div = entryDivs[i]; i++) {
    +    this.showDescription_(div, show);
    +  }
    +};
    +
    +
    +/**
    + * Creates the DOM element to control the given enum setting.
    + * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The
    + *     setting.
    + * @param {string} label The label for the entry.
    + * @param {!Function} onchangeFunc onchange event handler.
    + * @return {!DocumentFragment} The DOM element.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createComboBoxDom_ =
    +    function(tweak, label, onchangeFunc) {
    +  // The markup looks like:
    +  // Label: <select><option></option></select>
    +  var dh = this.domHelper_;
    +  var ret = dh.getDocument().createDocumentFragment();
    +  ret.appendChild(dh.createTextNode(label + ': '));
    +  var selectElem = dh.createElement('select');
    +  var values = tweak.getValidValues();
    +  for (var i = 0, il = values.length; i < il; ++i) {
    +    var optionElem = dh.createElement('option');
    +    optionElem.text = String(values[i]);
    +    // Setting the option tag's value is required for selectElem.value to work
    +    // properly.
    +    optionElem.value = String(values[i]);
    +    selectElem.appendChild(optionElem);
    +  }
    +  ret.appendChild(selectElem);
    +
    +  // Set the value and add a callback.
    +  selectElem.value = tweak.getNewValue();
    +  selectElem.onchange = onchangeFunc;
    +  tweak.addCallback(function() {
    +    selectElem.value = tweak.getNewValue();
    +  });
    +  return ret;
    +};
    +
    +
    +/**
    + * Creates the DOM element to control the given boolean setting.
    + * @param {!goog.tweak.BooleanSetting} tweak The setting.
    + * @param {string} label The label for the entry.
    + * @return {!DocumentFragment} The DOM elements.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createBooleanSettingDom_ =
    +    function(tweak, label) {
    +  var dh = this.domHelper_;
    +  var ret = dh.getDocument().createDocumentFragment();
    +  var checkbox = dh.createDom('input', {type: 'checkbox'});
    +  ret.appendChild(checkbox);
    +  ret.appendChild(dh.createTextNode(label));
    +
    +  // Needed on IE6 to ensure the textbox doesn't get cleared
    +  // when added to the DOM.
    +  checkbox.defaultChecked = tweak.getNewValue();
    +
    +  checkbox.checked = tweak.getNewValue();
    +  checkbox.onchange = function() {
    +    tweak.setValue(checkbox.checked);
    +  };
    +  tweak.addCallback(function() {
    +    checkbox.checked = tweak.getNewValue();
    +  });
    +  return ret;
    +};
    +
    +
    +/**
    + * Creates the DOM for a BooleanGroup or NamespaceEntry.
    + * @param {!goog.tweak.BooleanGroup|!goog.tweak.NamespaceEntry_} entry The
    + *     entry.
    + * @param {string} label The label for the entry.
    + * @param {!Array<goog.tweak.BaseEntry>} childEntries The child entries.
    + * @return {!DocumentFragment} The DOM element.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createSubPanelDom_ = function(entry, label,
    +    childEntries) {
    +  var dh = this.domHelper_;
    +  var toggleLink = dh.createDom('a', {href: 'javascript:;'},
    +      label + ' \xBB');
    +  var toggleLink2 = dh.createDom('a', {href: 'javascript:;'},
    +      '\xAB ' + label);
    +  toggleLink2.style.marginRight = '10px';
    +
    +  var innerUi = new goog.tweak.EntriesPanel(childEntries, dh);
    +  this.childPanels[entry.getId()] = innerUi;
    +
    +  var elem = innerUi.render();
    +  // Move the toggle descriptions link into the legend.
    +  var descriptionsLink = elem.firstChild;
    +  var childrenElem = dh.createDom('fieldset',
    +      goog.getCssName('goog-inline-block'),
    +      dh.createDom('legend', null, toggleLink2, descriptionsLink),
    +      elem);
    +
    +  new goog.ui.Zippy(toggleLink, childrenElem, false /* expanded */,
    +      toggleLink2);
    +
    +  var ret = dh.getDocument().createDocumentFragment();
    +  ret.appendChild(toggleLink);
    +  ret.appendChild(childrenElem);
    +  return ret;
    +};
    +
    +
    +/**
    + * Creates the DOM element to control the given string setting.
    + * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The
    + *     setting.
    + * @param {string} label The label for the entry.
    + * @param {!Function} onchangeFunc onchange event handler.
    + * @return {!DocumentFragment} The DOM element.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createTextBoxDom_ =
    +    function(tweak, label, onchangeFunc) {
    +  var dh = this.domHelper_;
    +  var ret = dh.getDocument().createDocumentFragment();
    +  ret.appendChild(dh.createTextNode(label + ': '));
    +  var textBox = dh.createDom('input', {
    +    value: tweak.getNewValue(),
    +    // TODO(agrieve): Make size configurable or autogrow.
    +    size: 5,
    +    onblur: onchangeFunc
    +  });
    +  ret.appendChild(textBox);
    +  tweak.addCallback(function() {
    +    textBox.value = tweak.getNewValue();
    +  });
    +  return ret;
    +};
    +
    +
    +/**
    + * Creates the DOM element to control the given button action.
    + * @param {!goog.tweak.ButtonAction} tweak The action.
    + * @param {string} label The label for the entry.
    + * @return {!Element} The DOM element.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createButtonActionDom_ =
    +    function(tweak, label) {
    +  return this.domHelper_.createDom('button', {
    +    onclick: goog.bind(tweak.fireCallbacks, tweak)
    +  }, label);
    +};
    +
    +
    +/**
    + * Creates the DOM element to control the given entry.
    + * @param {!goog.tweak.BaseEntry} entry The entry.
    + * @return {!Element|!DocumentFragment} The DOM element.
    + * @private
    + */
    +goog.tweak.EntriesPanel.prototype.createTweakEntryDom_ = function(entry) {
    +  var label = goog.tweak.TweakUi.getNamespacedLabel_(entry);
    +  if (entry instanceof goog.tweak.BooleanSetting) {
    +    return this.createBooleanSettingDom_(entry, label);
    +  } else if (entry instanceof goog.tweak.BooleanGroup) {
    +    var childEntries = goog.tweak.TweakUi.extractBooleanGroupEntries_(entry);
    +    return this.createSubPanelDom_(entry, label, childEntries);
    +  } else if (entry instanceof goog.tweak.StringSetting) {
    +    /** @this {Element} */
    +    var setValueFunc = function() {
    +      entry.setValue(this.value);
    +    };
    +    return entry.getValidValues() ?
    +        this.createComboBoxDom_(entry, label, setValueFunc) :
    +        this.createTextBoxDom_(entry, label, setValueFunc);
    +  } else if (entry instanceof goog.tweak.NumericSetting) {
    +    setValueFunc = function() {
    +      // Reset the value if it's not a number.
    +      if (isNaN(this.value)) {
    +        this.value = entry.getNewValue();
    +      } else {
    +        entry.setValue(+this.value);
    +      }
    +    };
    +    return entry.getValidValues() ?
    +        this.createComboBoxDom_(entry, label, setValueFunc) :
    +        this.createTextBoxDom_(entry, label, setValueFunc);
    +  } else if (entry instanceof goog.tweak.NamespaceEntry_) {
    +    return this.createSubPanelDom_(entry, entry.label, entry.entries);
    +  }
    +  goog.asserts.assertInstanceof(entry, goog.tweak.ButtonAction,
    +      'invalid entry: %s', entry);
    +  return this.createButtonActionDom_(
    +      /** @type {!goog.tweak.ButtonAction} */ (entry), label);
    +};
    +
    +
    +
    +/**
    + * Entries used to represent the collapsible namespace links. These entries are
    + * never registered with the TweakRegistry, but are contained within the
    + * collection of entries within TweakPanels.
    + * @param {string} namespace The namespace for the entry.
    + * @param {!Array<!goog.tweak.BaseEntry>} entries Entries within the namespace.
    + * @constructor
    + * @extends {goog.tweak.BaseEntry}
    + * @private
    + */
    +goog.tweak.NamespaceEntry_ = function(namespace, entries) {
    +  goog.tweak.BaseEntry.call(this,
    +      goog.tweak.NamespaceEntry_.ID_PREFIX + namespace,
    +      'Tweaks within the ' + namespace + ' namespace.');
    +
    +  /**
    +   * Entries within this namespace.
    +   * @type {!Array<!goog.tweak.BaseEntry>}
    +   */
    +  this.entries = entries;
    +
    +  this.label = namespace;
    +};
    +goog.inherits(goog.tweak.NamespaceEntry_, goog.tweak.BaseEntry);
    +
    +
    +/**
    + * Prefix for the IDs of namespace entries used to ensure that they do not
    + * conflict with regular entries.
    + * @type {string}
    + */
    +goog.tweak.NamespaceEntry_.ID_PREFIX = '!';
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.html b/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.html
    new file mode 100644
    index 0000000000..201fe277d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: agrieve@google.com (Andrew Grieve)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.tweak.TweakUi
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.tweak.TweakUiTest');
    +  </script>
    +  <link rel="stylesheet" type="text/css" href="../css/common.css" />
    + </head>
    + <body>
    +  <div id="root">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.js b/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.js
    new file mode 100644
    index 0000000000..e0f11fc53a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/tweak/tweakui_test.js
    @@ -0,0 +1,275 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.tweak.TweakUiTest');
    +goog.setTestOnly('goog.tweak.TweakUiTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.string');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.tweak');
    +goog.require('goog.tweak.TweakUi');
    +/** @suppress {extraRequire} needed for createRegistryEntries. */
    +goog.require('goog.tweak.testhelpers');
    +
    +var root;
    +var registry;
    +var EXPECTED_ENTRIES_COUNT = 14;
    +
    +function setUp() {
    +  root = document.getElementById('root');
    +  // Make both test cases use the same entries in order to be able to test that
    +  // having two UIs on the same page does not cause trouble.
    +  createRegistryEntries('');
    +  registry = goog.tweak.getRegistry();
    +}
    +
    +function tearDown() {
    +  goog.tweak.activeBooleanGroup_ = null;
    +  // When debugging a single test, don't clear out the DOM.
    +  if (window.location.search.indexOf('runTests') == -1) {
    +    root.innerHTML = '';
    +  }
    +}
    +
    +function tearDownPage() {
    +  // When debugging a single test, don't clear out the DOM.
    +  if (window.location.search.indexOf('runTests') != -1) {
    +    return;
    +  }
    +  // Create both registries for interactive testing.
    +  createRegistryEntries('');
    +  registry = goog.tweak.getRegistry();
    +  // Add an extra tweak for testing the creation of tweaks after the UI has
    +  // already been rendered.
    +  var entryCounter = 0;
    +  goog.tweak.registerButton('CreateNewTweak', 'Creates a new tweak. Meant ' +
    +      'to simulate a tweak being registered in a lazy-loaded module.',
    +      function() {
    +        goog.tweak.registerBoolean(
    +            'Lazy' + ++entryCounter, 'Lazy-loaded tweak.');
    +      });
    +  goog.tweak.registerButton('CreateNewTweakInNamespace1',
    +      'Creates a new tweak within a namespace. Meant to simulate a tweak ' +
    +      'being registered in a lazy-loaded module.', function() {
    +        goog.tweak.registerString(
    +            'foo.bar.Lazy' + ++entryCounter, 'Lazy-loaded tweak.');
    +      });
    +  goog.tweak.registerButton('CreateNewTweakInNamespace2',
    +      'Creates a new tweak within a namespace. Meant to simulate a tweak ' +
    +      'being registered in a lazy-loaded module.', function() {
    +        goog.tweak.registerNumber(
    +            'foo.bar.baz.Lazy' + ++entryCounter, 'Lazy combo', 3,
    +            {validValues: [1, 2, 3], label: 'Lazy!'});
    +      });
    +
    +  var label = document.createElement('h3');
    +  label.innerHTML = 'TweakUi:';
    +  root.appendChild(label);
    +  createUi(false);
    +
    +  label = document.createElement('h3');
    +  label.innerHTML = 'Collapsible:';
    +  root.appendChild(label);
    +  createUi(true);
    +}
    +
    +function createUi(collapsible) {
    +  var tweakUiElem = collapsible ? goog.tweak.TweakUi.createCollapsible() :
    +      goog.tweak.TweakUi.create();
    +  root.appendChild(tweakUiElem);
    +}
    +
    +function getAllEntryDivs() {
    +  return goog.dom.getElementsByTagNameAndClass('div',
    +      goog.tweak.TweakUi.ENTRY_CSS_CLASS_);
    +}
    +
    +function getEntryDiv(entry) {
    +  var label = goog.tweak.TweakUi.getNamespacedLabel_(entry);
    +  var allDivs = getAllEntryDivs();
    +  var ret;
    +  for (var i = 0, div; div = allDivs[i]; i++) {
    +    var divText = goog.dom.getTextContent(div);
    +    if (goog.string.startsWith(divText, label) &&
    +        goog.string.contains(divText, entry.description)) {
    +      assertFalse('Found multiple divs matching entry ' + entry.getId(), !!ret);
    +      ret = div;
    +    }
    +  }
    +  assertTrue('getEntryDiv failed for ' + entry.getId(), !!ret);
    +  return ret;
    +}
    +
    +function getEntryInput(entry) {
    +  var div = getEntryDiv(entry);
    +  return div.getElementsByTagName('input')[0] ||
    +      div.getElementsByTagName('select')[0];
    +}
    +
    +function testCreate() {
    +  createUi(false);
    +  assertEquals('Wrong number of entry divs.', EXPECTED_ENTRIES_COUNT,
    +      getAllEntryDivs().length);
    +
    +  assertFalse('checkbox should not be checked 1',
    +      getEntryInput(boolEntry).checked);
    +  assertTrue('checkbox should be checked 2',
    +      getEntryInput(boolEntry2).checked);
    +  // Enusre custom labels are being used.
    +  var html = document.getElementsByTagName('button')[0].innerHTML;
    +  assertTrue('Button label is wrong', html.indexOf('&lt;btn&gt;') > -1);
    +  html = getEntryDiv(numEnumEntry).innerHTML;
    +  assertTrue('Enum2 label is wrong', html.indexOf('second&amp;') > -1);
    +}
    +
    +function testToggleBooleanSetting() {
    +  boolEntry.setValue(true);
    +  createUi(false);
    +
    +  assertTrue('checkbox should be checked',
    +      getEntryInput(boolEntry).checked);
    +
    +  boolEntry.setValue(false);
    +  assertFalse('checkbox should not be checked 1',
    +      getEntryInput(boolEntry).checked);
    +}
    +
    +function testToggleStringSetting() {
    +  strEntry.setValue('val1');
    +  createUi(false);
    +
    +  assertEquals('Textbox has wrong value 1',
    +      'val1', getEntryInput(strEntry).value);
    +
    +  strEntry.setValue('val2');
    +  assertEquals('Textbox has wrong value 2',
    +      'val2', getEntryInput(strEntry).value);
    +}
    +
    +function testToggleStringEnumSetting() {
    +  strEnumEntry.setValue('B');
    +  createUi(false);
    +
    +  assertEquals('wrong value 1',
    +      'B', getEntryInput(strEnumEntry).value);
    +
    +  strEnumEntry.setValue('C');
    +  assertEquals('wrong value 2',
    +      'C', getEntryInput(strEnumEntry).value);
    +}
    +
    +
    +function testToggleNumericSetting() {
    +  numEntry.setValue(3);
    +  createUi(false);
    +
    +  assertEquals('wrong value 1',
    +      '3', getEntryInput(numEntry).value);
    +
    +  numEntry.setValue(4);
    +  assertEquals('wrong value 2',
    +      '4', getEntryInput(numEntry).value);
    +}
    +
    +function testToggleNumericEnumSetting() {
    +  numEnumEntry.setValue(2);
    +  createUi(false);
    +
    +  assertEquals('wrong value 1',
    +      '2', getEntryInput(numEnumEntry).value);
    +
    +  numEnumEntry.setValue(3);
    +  assertEquals('wrong value 2',
    +      '3', getEntryInput(numEnumEntry).value);
    +}
    +
    +function testClickBooleanSetting() {
    +  createUi(false);
    +
    +  var input = getEntryInput(boolEntry);
    +  input.checked = true;
    +  input.onchange();
    +  assertTrue('setting should be true', boolEntry.getNewValue());
    +  input.checked = false;
    +  input.onchange();
    +  assertFalse('setting should be false', boolEntry.getNewValue());
    +}
    +
    +function testToggleDescriptions() {
    +  createUi(false);
    +  var toggleLink = root.getElementsByTagName('a')[0];
    +  var heightBefore = root.offsetHeight;
    +  toggleLink.onclick();
    +  assertTrue('Expected div height to grow from toggle descriptions.',
    +      root.offsetHeight > heightBefore);
    +  toggleLink.onclick();
    +  assertEquals('Expected div height to revert from toggle descriptions.',
    +      heightBefore, root.offsetHeight);
    +}
    +
    +function assertEntryOrder(entryId1, entryId2) {
    +  var entry1 = registry.getEntry(entryId1);
    +  var entry2 = registry.getEntry(entryId2);
    +  var div1 = getEntryDiv(entry1);
    +  var div2 = getEntryDiv(entry2);
    +  var order = goog.dom.compareNodeOrder(div1, div2);
    +  assertTrue(entry1.getId() + ' should be before ' + entry2.getId(), order < 0);
    +}
    +
    +function testAddEntry() {
    +  createUi(false);
    +  goog.tweak.registerBoolean('Lazy1', 'Lazy-loaded tweak.');
    +  goog.tweak.registerBoolean('Lazy2', 'Lazy-loaded tweak.',
    +      /* defaultValue */ false, { restartRequired: false });
    +  goog.tweak.beginBooleanGroup('LazyGroup', 'Lazy-loaded tweak.');
    +  goog.tweak.registerBoolean('Lazy3', 'Lazy-loaded tweak.');
    +  goog.tweak.endBooleanGroup();
    +
    +  assertEquals('Wrong number of entry divs.', EXPECTED_ENTRIES_COUNT + 4,
    +      getAllEntryDivs().length);
    +  assertEntryOrder('Enum2', 'Lazy1');
    +  assertEntryOrder('Lazy1', 'Lazy2');
    +  assertEntryOrder('Lazy2', 'Num');
    +  assertEntryOrder('BoolGroup', 'Lazy3');
    +}
    +
    +function testAddNamespacedEntries() {
    +  createUi(false);
    +  goog.tweak.beginBooleanGroup('NS.LazyGroup', 'Lazy-loaded tweak.');
    +  goog.tweak.registerBoolean('NS.InGroup', 'Lazy-loaded tweak.');
    +  goog.tweak.endBooleanGroup();
    +  goog.tweak.registerBoolean('NS.Banana', 'Lazy-loaded tweak.');
    +  goog.tweak.registerBoolean('NS.Apple', 'Lazy-loaded tweak.');
    +
    +  assertEquals('Wrong number of entry divs.', EXPECTED_ENTRIES_COUNT + 5,
    +      getAllEntryDivs().length);
    +  assertEntryOrder('Enum2', 'NS.Apple');
    +  assertEntryOrder('NS.Apple', 'NS.Banana');
    +  assertEntryOrder('NS.Banana', 'NS.InGroup');
    +}
    +
    +function testCollapsibleIsLazy() {
    +  if (document.createEvent) {
    +    createUi(true);
    +    assertEquals('Expected no entry divs.', 0, getAllEntryDivs().length);
    +    var showLink = root.getElementsByTagName('a')[0];
    +    var event = document.createEvent('MouseEvents');
    +    event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false,
    +        false, false, false, 0, null);
    +    showLink.dispatchEvent(event);
    +    assertEquals('Wrong number of entry divs.', EXPECTED_ENTRIES_COUNT,
    +        getAllEntryDivs().length);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/abstractspellchecker.js b/mapbox-gl/closure-library/closure/goog/ui/abstractspellchecker.js
    new file mode 100644
    index 0000000000..d085a57073
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/abstractspellchecker.js
    @@ -0,0 +1,1229 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Abstract base class for spell checker implementations.
    + *
    + * The spell checker supports two modes - synchronous and asynchronous.
    + *
    + * In synchronous mode subclass calls processText_ which processes all the text
    + * given to it before it returns. If the text string is very long, it could
    + * cause warnings from the browser that considers the script to be
    + * busy-looping.
    + *
    + * Asynchronous mode allows breaking processing large text segments without
    + * encountering stop script warnings by rescheduling remaining parts of the
    + * text processing to another stack.
    + *
    + * In asynchronous mode abstract spell checker keeps track of a number of text
    + * chunks that have been processed after the very beginning, and returns every
    + * so often so that the calling function could reschedule its execution on a
    + * different stack (for example by calling setInterval(0)).
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.AbstractSpellChecker');
    +goog.provide('goog.ui.AbstractSpellChecker.AsyncResult');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.dom.selection');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.structs.Set');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuSeparator');
    +goog.require('goog.ui.PopupMenu');
    +
    +
    +
    +/**
    + * Abstract base class for spell checker editor implementations. Provides basic
    + * functionality such as word lookup and caching.
    + *
    + * @param {goog.spell.SpellCheck} spellCheck Instance of the SpellCheck
    + *     support object to use. A single instance can be shared by multiple editor
    + *     components.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.AbstractSpellChecker = function(spellCheck, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Handler to use for caching and lookups.
    +   * @type {goog.spell.SpellCheck}
    +   * @protected
    +   */
    +  this.spellCheck = spellCheck;
    +
    +  /**
    +   * Word to element references. Used by replace/ignore.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.wordElements_ = {};
    +
    +  /**
    +   * List of all 'edit word' input elements.
    +   * @type {Array<Element>}
    +   * @private
    +   */
    +  this.inputElements_ = [];
    +
    +  /**
    +   * Global regular expression for splitting a string into individual words and
    +   * blocks of separators. Matches zero or one word followed by zero or more
    +   * separators.
    +   * @type {RegExp}
    +   * @private
    +   */
    +  this.splitRegex_ = new RegExp(
    +      '([^' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)' +
    +      '([' + goog.spell.SpellCheck.WORD_BOUNDARY_CHARS + ']*)', 'g');
    +
    +  goog.events.listen(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.WORD_CHANGED, this.onWordChanged_,
    +      false, this);
    +};
    +goog.inherits(goog.ui.AbstractSpellChecker, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.AbstractSpellChecker);
    +
    +
    +/**
    + * The prefix to mark keys with.
    + * @type {string}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.KEY_PREFIX_ = ':';
    +
    +
    +/**
    + * The attribute name for original element contents (to offer subsequent
    + * correction menu).
    + * @type {string}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.ORIGINAL_ = 'g-spell-original';
    +
    +
    +/**
    + * Suggestions menu.
    + *
    + * @type {goog.ui.PopupMenu|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.menu_;
    +
    +
    +/**
    + * Separator between suggestions and ignore in suggestions menu.
    + *
    + * @type {goog.ui.MenuSeparator|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.menuSeparator_;
    +
    +
    +/**
    + * Menu item for ignore option.
    + *
    + * @type {goog.ui.MenuItem|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.menuIgnore_;
    +
    +
    +/**
    + * Menu item for edit word option.
    + *
    + * @type {goog.ui.MenuItem|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.menuEdit_;
    +
    +
    +/**
    + * Whether the correction UI is visible.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.isVisible_ = false;
    +
    +
    +/**
    + * Cache for corrected words. All corrected words are reverted to their original
    + * status on resume. Therefore that status is never written to the cache and is
    + * instead indicated by this set.
    + *
    + * @type {goog.structs.Set|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.correctedWords_;
    +
    +
    +/**
    + * Class name for suggestions menu.
    + *
    + * @type {string}
    + */
    +goog.ui.AbstractSpellChecker.prototype.suggestionsMenuClassName =
    +    goog.getCssName('goog-menu');
    +
    +
    +/**
    + * Whether corrected words should be highlighted.
    + *
    + * @type {boolean}
    + */
    +goog.ui.AbstractSpellChecker.prototype.markCorrected = false;
    +
    +
    +/**
    + * Word the correction menu is displayed for.
    + *
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.activeWord_;
    +
    +
    +/**
    + * Element the correction menu is displayed for.
    + *
    + * @type {Element|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.activeElement_;
    +
    +
    +/**
    + * Indicator that the spell checker is running in the asynchronous mode.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.asyncMode_ = false;
    +
    +
    +/**
    + * Maximum number of words to process on a single stack in asynchronous mode.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.asyncWordsPerBatch_ = 1000;
    +
    +
    +/**
    + * Current text to process when running in the asynchronous mode.
    + *
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.asyncText_;
    +
    +
    +/**
    + * Current start index of the range that spell-checked correctly.
    + *
    + * @type {number|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.asyncRangeStart_;
    +
    +
    +/**
    + * Current node with which the asynchronous text is associated.
    + *
    + * @type {Node|undefined}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.asyncNode_;
    +
    +
    +/**
    + * Number of elements processed in the asyncronous mode since last yield.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.processedElementsCount_ = 0;
    +
    +
    +/**
    + * Markers for the text that does not need to be included in the processing.
    + *
    + * For rich text editor this is a list of strings formatted as
    + * tagName.className or className. If both are specified, the element will be
    + * excluded if BOTH are matched. If only a className is specified, then we will
    + * exclude regions with the className. If only one marker is needed, it may be
    + * passed as a string.
    + * For plain text editor this is a RegExp that matches the excluded text.
    + *
    + * Used exclusively by the derived classes
    + *
    + * @type {Array<string>|string|RegExp|undefined}
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.excludeMarker;
    +
    +
    +/**
    + * Numeric Id of the element that has focus. 0 when not set.
    + *
    + * @private {number}
    + */
    +goog.ui.AbstractSpellChecker.prototype.focusedElementIndex_ = 0;
    +
    +
    +/**
    + * Index for the most recently added misspelled word.
    + *
    + * @private {number}
    + */
    +goog.ui.AbstractSpellChecker.prototype.lastIndex_ = 0;
    +
    +
    +/**
    + * @return {goog.spell.SpellCheck} The handler used for caching and lookups.
    + */
    +goog.ui.AbstractSpellChecker.prototype.getSpellCheck = function() {
    +  return this.spellCheck;
    +};
    +
    +
    +/**
    + * @return {goog.spell.SpellCheck} The handler used for caching and lookups.
    + * @override
    + * @suppress {checkTypes} This method makes no sense. It overrides
    + *     Component's getHandler with something different.
    + * @deprecated Use #getSpellCheck instead.
    + */
    +goog.ui.AbstractSpellChecker.prototype.getHandler = function() {
    +  return this.getSpellCheck();
    +};
    +
    +
    +/**
    + * Sets the spell checker used for caching and lookups.
    + * @param {goog.spell.SpellCheck} spellCheck The handler used for caching and
    + *     lookups.
    + */
    +goog.ui.AbstractSpellChecker.prototype.setSpellCheck = function(spellCheck) {
    +  this.spellCheck = spellCheck;
    +};
    +
    +
    +/**
    + * Sets the handler used for caching and lookups.
    + * @param {goog.spell.SpellCheck} handler The handler used for caching and
    + *     lookups.
    + * @deprecated Use #setSpellCheck instead.
    + */
    +goog.ui.AbstractSpellChecker.prototype.setHandler = function(handler) {
    +  this.setSpellCheck(handler);
    +};
    +
    +
    +/**
    + * @return {goog.ui.PopupMenu|undefined} The suggestions menu.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getMenu = function() {
    +  return this.menu_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.MenuItem|undefined} The menu item for edit word option.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getMenuEdit = function() {
    +  return this.menuEdit_;
    +};
    +
    +
    +/**
    + * @return {number} The index of the latest misspelled word to be added.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getLastIndex = function() {
    +  return this.lastIndex_;
    +};
    +
    +
    +/**
    + * @return {number} Increments and returns the index for the next misspelled
    + *     word to be added.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getNextIndex = function() {
    +  return ++this.lastIndex_;
    +};
    +
    +
    +/**
    + * Sets the marker for the excluded text.
    + *
    + * {@see goog.ui.AbstractSpellChecker.prototype.excludeMarker}
    + *
    + * @param {Array<string>|string|RegExp|null} marker A RegExp for plain text
    + *        or class names for the rich text spell checker for the elements to
    + *        exclude from checking.
    + */
    +goog.ui.AbstractSpellChecker.prototype.setExcludeMarker = function(marker) {
    +  this.excludeMarker = marker || undefined;
    +};
    +
    +
    +/**
    + * Checks spelling for all text.
    + * Should be overridden by implementation.
    + */
    +goog.ui.AbstractSpellChecker.prototype.check = function() {
    +  this.isVisible_ = true;
    +  if (this.markCorrected) {
    +    this.correctedWords_ = new goog.structs.Set();
    +  }
    +};
    +
    +
    +/**
    + * Hides correction UI.
    + * Should be overridden by implementation.
    + */
    +goog.ui.AbstractSpellChecker.prototype.resume = function() {
    +  this.isVisible_ = false;
    +  this.clearWordElements();
    +  this.lastIndex_ = 0;
    +  this.setFocusedElementIndex(0);
    +
    +  var input;
    +  while (input = this.inputElements_.pop()) {
    +    input.parentNode.replaceChild(
    +        this.getDomHelper().createTextNode(input.value), input);
    +  }
    +
    +  if (this.correctedWords_) {
    +    this.correctedWords_.clear();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the correction ui is visible.
    + */
    +goog.ui.AbstractSpellChecker.prototype.isVisible = function() {
    +  return this.isVisible_;
    +};
    +
    +
    +/**
    + * Clears the word to element references map used by replace/ignore.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.clearWordElements = function() {
    +  this.wordElements_ = {};
    +};
    +
    +
    +/**
    + * Ignores spelling of word.
    + *
    + * @param {string} word Word to add.
    + */
    +goog.ui.AbstractSpellChecker.prototype.ignoreWord = function(word) {
    +  this.spellCheck.setWordStatus(word,
    +      goog.spell.SpellCheck.WordStatus.IGNORED);
    +};
    +
    +
    +/**
    + * Edits a word.
    + *
    + * @param {Element} el An element wrapping the word that should be edited.
    + * @param {string} old Word to edit.
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.editWord_ = function(el, old) {
    +  var input = this.getDomHelper().createDom(
    +      'input', {'type': 'text', 'value': old});
    +  var w = goog.style.getSize(el).width;
    +
    +  // Minimum width to ensure there's always enough room to type.
    +  if (w < 50) {
    +    w = 50;
    +  }
    +  input.style.width = w + 'px';
    +  el.parentNode.replaceChild(input, el);
    +  try {
    +    input.focus();
    +    goog.dom.selection.setCursorPosition(input, old.length);
    +  } catch (o) { }
    +
    +  this.inputElements_.push(input);
    +};
    +
    +
    +/**
    + * Replaces word.
    + *
    + * @param {Element} el An element wrapping the word that should be replaced.
    + * @param {string} old Word that was replaced.
    + * @param {string} word Word to replace with.
    + */
    +goog.ui.AbstractSpellChecker.prototype.replaceWord = function(el, old, word) {
    +  if (old != word) {
    +    if (!el.getAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_)) {
    +      el.setAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_, old);
    +    }
    +    goog.dom.setTextContent(el, word);
    +
    +    var status = this.spellCheck.checkWord(word);
    +
    +    // Indicate that the word is corrected unless the status is 'INVALID'.
    +    // (if markCorrected is enabled).
    +    if (this.markCorrected && this.correctedWords_ &&
    +        status != goog.spell.SpellCheck.WordStatus.INVALID) {
    +      this.correctedWords_.add(word);
    +      status = goog.spell.SpellCheck.WordStatus.CORRECTED;
    +    }
    +
    +    // Avoid potential collision with the built-in object namespace. For
    +    // example, 'watch' is a reserved name in FireFox.
    +    var oldIndex = goog.ui.AbstractSpellChecker.toInternalKey_(old);
    +    var newIndex = goog.ui.AbstractSpellChecker.toInternalKey_(word);
    +
    +    // Remove reference between old word and element
    +    var elements = this.wordElements_[oldIndex];
    +    goog.array.remove(elements, el);
    +
    +    if (status != goog.spell.SpellCheck.WordStatus.VALID) {
    +      // Create reference between new word and element
    +      if (this.wordElements_[newIndex]) {
    +        this.wordElements_[newIndex].push(el);
    +      } else {
    +        this.wordElements_[newIndex] = [el];
    +      }
    +    }
    +
    +    // Update element based on status.
    +    this.updateElement(el, word, status);
    +
    +    this.dispatchEvent(goog.events.EventType.CHANGE);
    +  }
    +};
    +
    +
    +/**
    + * Retrieves the array of suggested spelling choices.
    + *
    + * @return {Array<string>} Suggested spelling choices.
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.getSuggestions_ = function() {
    +  // Add new suggestion entries.
    +  var suggestions = this.spellCheck.getSuggestions(
    +      /** @type {string} */ (this.activeWord_));
    +  if (!suggestions[0]) {
    +    var originalWord = this.activeElement_.getAttribute(
    +        goog.ui.AbstractSpellChecker.ORIGINAL_);
    +    if (originalWord && originalWord != this.activeWord_) {
    +      suggestions = this.spellCheck.getSuggestions(originalWord);
    +    }
    +  }
    +  return suggestions;
    +};
    +
    +
    +/**
    + * Displays suggestions menu.
    + *
    + * @param {Element} el Element to display menu for.
    + * @param {goog.events.BrowserEvent|goog.math.Coordinate=} opt_pos Position to
    + *     display menu at relative to the viewport (in client coordinates), or a
    + *     mouse event.
    + */
    +goog.ui.AbstractSpellChecker.prototype.showSuggestionsMenu = function(el,
    +                                                                      opt_pos) {
    +  this.activeWord_ = goog.dom.getTextContent(el);
    +  this.activeElement_ = el;
    +
    +  // Remove suggestion entries from menu, if any.
    +  while (this.menu_.getChildAt(0) != this.menuSeparator_) {
    +    this.menu_.removeChildAt(0, true).dispose();
    +  }
    +
    +  // Add new suggestion entries.
    +  var suggestions = this.getSuggestions_();
    +  for (var suggestion, i = 0; suggestion = suggestions[i]; i++) {
    +    this.menu_.addChildAt(new goog.ui.MenuItem(
    +        suggestion, suggestion, this.getDomHelper()), i, true);
    +  }
    +
    +  if (!suggestions[0]) {
    +    /** @desc Item shown in menu when no suggestions are available. */
    +    var MSG_SPELL_NO_SUGGESTIONS = goog.getMsg('No Suggestions');
    +    var item = new goog.ui.MenuItem(
    +        MSG_SPELL_NO_SUGGESTIONS, '', this.getDomHelper());
    +    item.setEnabled(false);
    +    this.menu_.addChildAt(item, 0, true);
    +  }
    +
    +  // Show 'Edit word' option if {@link markCorrected} is enabled and don't show
    +  // 'Ignore' option for corrected words.
    +  if (this.markCorrected) {
    +    var corrected = this.correctedWords_ &&
    +                    this.correctedWords_.contains(this.activeWord_);
    +    this.menuIgnore_.setVisible(!corrected);
    +    this.menuEdit_.setVisible(true);
    +  } else {
    +    this.menuIgnore_.setVisible(true);
    +    this.menuEdit_.setVisible(false);
    +  }
    +
    +  if (opt_pos) {
    +    if (!(opt_pos instanceof goog.math.Coordinate)) { // it's an event
    +      var posX = opt_pos.clientX;
    +      var posY = opt_pos.clientY;
    +      // Certain implementations which derive from AbstractSpellChecker
    +      // use an iframe in which case the coordinates are relative to
    +      // that iframe's view port.
    +      if (this.getElement().contentDocument ||
    +          this.getElement().contentWindow) {
    +        var offset = goog.style.getClientPosition(this.getElement());
    +        posX += offset.x;
    +        posY += offset.y;
    +      }
    +      opt_pos = new goog.math.Coordinate(posX, posY);
    +    }
    +    this.menu_.showAt(opt_pos.x, opt_pos.y);
    +  } else {
    +    this.menu_.setVisible(true);
    +  }
    +};
    +
    +
    +/**
    + * Initializes suggestions menu. Populates menu with separator and ignore option
    + * that are always valid. Suggestions are later added above the separator.
    + *
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.initSuggestionsMenu = function() {
    +  this.menu_ = new goog.ui.PopupMenu(this.getDomHelper());
    +  this.menuSeparator_ = new goog.ui.MenuSeparator(this.getDomHelper());
    +
    +  // Leave alone setAllowAutoFocus at default (true). This allows menu to get
    +  // keyboard focus and thus allowing non-mouse users to get to the menu.
    +
    +  /** @desc Ignore entry in suggestions menu. */
    +  var MSG_SPELL_IGNORE = goog.getMsg('Ignore');
    +
    +  /** @desc Edit word entry in suggestions menu. */
    +  var MSG_SPELL_EDIT_WORD = goog.getMsg('Edit Word');
    +
    +  this.menu_.addChild(this.menuSeparator_, true);
    +  this.menuIgnore_ =
    +      new goog.ui.MenuItem(MSG_SPELL_IGNORE, '', this.getDomHelper());
    +  this.menu_.addChild(this.menuIgnore_, true);
    +  this.menuEdit_ =
    +      new goog.ui.MenuItem(MSG_SPELL_EDIT_WORD, '', this.getDomHelper());
    +  this.menuEdit_.setVisible(false);
    +  this.menu_.addChild(this.menuEdit_, true);
    +  this.menu_.setParent(this);
    +  this.menu_.render();
    +
    +  var menuElement = this.menu_.getElement();
    +  goog.asserts.assert(menuElement);
    +  goog.dom.classlist.add(menuElement,
    +      this.suggestionsMenuClassName);
    +
    +  goog.events.listen(this.menu_, goog.ui.Component.EventType.ACTION,
    +      this.onCorrectionAction, false, this);
    +};
    +
    +
    +/**
    + * Handles correction menu actions.
    + *
    + * @param {goog.events.Event} event Action event.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.onCorrectionAction = function(event) {
    +  var word = /** @type {string} */ (this.activeWord_);
    +  var el = /** @type {Element} */ (this.activeElement_);
    +  if (event.target == this.menuIgnore_) {
    +    this.ignoreWord(word);
    +  } else if (event.target == this.menuEdit_) {
    +    this.editWord_(el, word);
    +  } else {
    +    this.replaceWord(el, word, event.target.getModel());
    +    this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +  }
    +
    +  delete this.activeWord_;
    +  delete this.activeElement_;
    +};
    +
    +
    +/**
    + * Removes spell-checker markup and restore the node to text.
    + *
    + * @param {Element} el Word element. MUST have a text node child.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.removeMarkup = function(el) {
    +  var firstChild = el.firstChild;
    +  var text = firstChild.nodeValue;
    +
    +  if (el.nextSibling &&
    +      el.nextSibling.nodeType == goog.dom.NodeType.TEXT) {
    +    if (el.previousSibling &&
    +        el.previousSibling.nodeType == goog.dom.NodeType.TEXT) {
    +      el.previousSibling.nodeValue = el.previousSibling.nodeValue + text +
    +          el.nextSibling.nodeValue;
    +      this.getDomHelper().removeNode(el.nextSibling);
    +    } else {
    +      el.nextSibling.nodeValue = text + el.nextSibling.nodeValue;
    +    }
    +  } else if (el.previousSibling &&
    +      el.previousSibling.nodeType == goog.dom.NodeType.TEXT) {
    +    el.previousSibling.nodeValue += text;
    +  } else {
    +    el.parentNode.insertBefore(firstChild, el);
    +  }
    +
    +  this.getDomHelper().removeNode(el);
    +};
    +
    +
    +/**
    + * Updates element based on word status. Either converts it to a text node, or
    + * merges it with the previous or next text node if the status of the world is
    + * VALID, in which case the element itself is eliminated.
    + *
    + * @param {Element} el Word element.
    + * @param {string} word Word to update status for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.updateElement =
    +    function(el, word, status) {
    +  if (this.markCorrected && this.correctedWords_ &&
    +      this.correctedWords_.contains(word)) {
    +    status = goog.spell.SpellCheck.WordStatus.CORRECTED;
    +  }
    +  if (status == goog.spell.SpellCheck.WordStatus.VALID) {
    +    this.removeMarkup(el);
    +  } else {
    +    goog.dom.setProperties(el, this.getElementProperties(status));
    +  }
    +};
    +
    +
    +/**
    + * Generates unique Ids for spell checker elements.
    + * @param {number=} opt_id Id to suffix with.
    + * @return {string} Unique element id.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.makeElementId = function(opt_id) {
    +  return this.getId() + '.' + (opt_id ? opt_id : this.getNextIndex());
    +};
    +
    +
    +/**
    + * Returns the span element that matches the given number index.
    + * @param {number} index Number index that is used in the element id.
    + * @return {Element} The matching span element or null if no span matches.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getElementByIndex = function(index) {
    +  return this.getDomHelper().getElement(this.makeElementId(index));
    +};
    +
    +
    +/**
    + * Creates an element for a specified word and stores a reference to it.
    + *
    + * @param {string} word Word to create element for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @return {!HTMLSpanElement} The created element.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.createWordElement = function(
    +    word, status) {
    +  var parameters = this.getElementProperties(status);
    +
    +  // Add id & tabindex as necessary.
    +  if (!parameters['id']) {
    +    parameters['id'] = this.makeElementId();
    +  }
    +  if (!parameters['tabIndex']) {
    +    parameters['tabIndex'] = -1;
    +  }
    +
    +  var el = /** @type {!HTMLSpanElement} */
    +      (this.getDomHelper().createDom('span', parameters, word));
    +  goog.a11y.aria.setRole(el, 'menuitem');
    +  goog.a11y.aria.setState(el, 'haspopup', true);
    +  this.registerWordElement(word, el);
    +
    +  return el;
    +};
    +
    +
    +/**
    + * Stores a reference to word element.
    + *
    + * @param {string} word The word to store.
    + * @param {HTMLSpanElement} el The element associated with it.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.registerWordElement = function(
    +    word, el) {
    +  // Avoid potential collision with the built-in object namespace. For
    +  // example, 'watch' is a reserved name in FireFox.
    +  var index = goog.ui.AbstractSpellChecker.toInternalKey_(word);
    +  if (this.wordElements_[index]) {
    +    this.wordElements_[index].push(el);
    +  } else {
    +    this.wordElements_[index] = [el];
    +  }
    +};
    +
    +
    +/**
    + * Returns desired element properties for the specified status.
    + * Should be overridden by implementation.
    + *
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @return {Object} Properties to apply to the element.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getElementProperties =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * Handles word change events and updates the word elements accordingly.
    + *
    + * @param {goog.spell.SpellCheck.WordChangedEvent} event The event object.
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.prototype.onWordChanged_ = function(event) {
    +  // Avoid potential collision with the built-in object namespace. For
    +  // example, 'watch' is a reserved name in FireFox.
    +  var index = goog.ui.AbstractSpellChecker.toInternalKey_(event.word);
    +  var elements = this.wordElements_[index];
    +  if (elements) {
    +    for (var el, i = 0; el = elements[i]; i++) {
    +      this.updateElement(el, event.word, event.status);
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.AbstractSpellChecker.prototype.disposeInternal = function() {
    +  if (this.isVisible_) {
    +    // Clears wordElements_
    +    this.resume();
    +  }
    +
    +  goog.events.unlisten(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.WORD_CHANGED, this.onWordChanged_,
    +      false, this);
    +
    +  if (this.menu_) {
    +    this.menu_.dispose();
    +    delete this.menu_;
    +    delete this.menuIgnore_;
    +    delete this.menuSeparator_;
    +  }
    +  delete this.spellCheck;
    +  delete this.wordElements_;
    +
    +  goog.ui.AbstractSpellChecker.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Precharges local dictionary cache. This is optional, but greatly reduces
    + * amount of subsequent churn in the DOM tree because most of the words become
    + * known from the very beginning.
    + *
    + * @param {string} text Text to process.
    + * @param {number} words Max number of words to scan.
    + * @return {number} number of words actually scanned.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.populateDictionary = function(text,
    +                                                                     words) {
    +  this.splitRegex_.lastIndex = 0;
    +  var result;
    +  var numScanned = 0;
    +  while (result = this.splitRegex_.exec(text)) {
    +    if (result[0].length == 0) {
    +      break;
    +    }
    +    var word = result[1];
    +    if (word) {
    +      this.spellCheck.checkWord(word);
    +      ++numScanned;
    +      if (numScanned >= words) {
    +        break;
    +      }
    +    }
    +  }
    +  this.spellCheck.processPending();
    +  return numScanned;
    +};
    +
    +
    +/**
    + * Processes word.
    + * Should be overridden by implementation.
    + *
    + * @param {Node} node Node containing word.
    + * @param {string} text Word to process.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of the word.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.processWord = function(
    +    node, text, status) {
    +  throw Error('Need to override processWord_ in derivative class');
    +};
    +
    +
    +/**
    + * Processes range of text that checks out (contains no unrecognized words).
    + * Should be overridden by implementation. May contain words and separators.
    + *
    + * @param {Node} node Node containing text range.
    + * @param {string} text text to process.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.processRange = function(node, text) {
    +  throw Error('Need to override processRange_ in derivative class');
    +};
    +
    +
    +/**
    + * Starts asynchronous processing mode.
    + *
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.initializeAsyncMode = function() {
    +  if (this.asyncMode_ || this.processedElementsCount_ ||
    +      this.asyncText_ != null || this.asyncNode_) {
    +    throw Error('Async mode already in progress.');
    +  }
    +  this.asyncMode_ = true;
    +  this.processedElementsCount_ = 0;
    +  delete this.asyncText_;
    +  this.asyncRangeStart_ = 0;
    +  delete this.asyncNode_;
    +
    +  this.blockReadyEvents();
    +};
    +
    +
    +/**
    + * Finalizes asynchronous processing mode. Should be called after there is no
    + * more text to process and processTextAsync and/or continueAsyncProcessing
    + * returned FINISHED.
    + *
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.finishAsyncProcessing = function() {
    +  if (!this.asyncMode_ || this.asyncText_ != null || this.asyncNode_) {
    +    throw Error('Async mode not started or there is still text to process.');
    +  }
    +  this.asyncMode_ = false;
    +  this.processedElementsCount_ = 0;
    +
    +  this.unblockReadyEvents();
    +  this.spellCheck.processPending();
    +};
    +
    +
    +/**
    + * Blocks processing of spell checker READY events. This is used in dictionary
    + * recharge and async mode so that completion is not signaled prematurely.
    + *
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.blockReadyEvents = function() {
    +  goog.events.listen(this.spellCheck, goog.spell.SpellCheck.EventType.READY,
    +      goog.events.Event.stopPropagation, true);
    +};
    +
    +
    +/**
    + * Unblocks processing of spell checker READY events. This is used in
    + * dictionary recharge and async mode so that completion is not signaled
    + * prematurely.
    + *
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.unblockReadyEvents = function() {
    +  goog.events.unlisten(this.spellCheck, goog.spell.SpellCheck.EventType.READY,
    +      goog.events.Event.stopPropagation, true);
    +};
    +
    +
    +/**
    + * Splits text into individual words and blocks of separators. Calls virtual
    + * processWord_ and processRange_ methods.
    + *
    + * @param {Node} node Node containing text.
    + * @param {string} text Text to process.
    + * @return {goog.ui.AbstractSpellChecker.AsyncResult} operation result.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.processTextAsync = function(
    +    node, text) {
    +  if (!this.asyncMode_ || this.asyncText_ != null || this.asyncNode_) {
    +    throw Error('Not in async mode or previous text has not been processed.');
    +  }
    +
    +  this.splitRegex_.lastIndex = 0;
    +  var stringSegmentStart = 0;
    +
    +  var result;
    +  while (result = this.splitRegex_.exec(text)) {
    +    if (result[0].length == 0) {
    +      break;
    +    }
    +    var word = result[1];
    +    if (word) {
    +      var status = this.spellCheck.checkWord(word);
    +      if (status != goog.spell.SpellCheck.WordStatus.VALID) {
    +        var preceedingText = text.substr(stringSegmentStart, result.index -
    +            stringSegmentStart);
    +        if (preceedingText) {
    +          this.processRange(node, preceedingText);
    +        }
    +        stringSegmentStart = result.index + word.length;
    +        this.processWord(node, word, status);
    +      }
    +    }
    +    this.processedElementsCount_++;
    +    if (this.processedElementsCount_ > this.asyncWordsPerBatch_) {
    +      this.asyncText_ = text;
    +      this.asyncRangeStart_ = stringSegmentStart;
    +      this.asyncNode_ = node;
    +      this.processedElementsCount_ = 0;
    +      return goog.ui.AbstractSpellChecker.AsyncResult.PENDING;
    +    }
    +  }
    +
    +  var leftoverText = text.substr(stringSegmentStart);
    +  if (leftoverText) {
    +    this.processRange(node, leftoverText);
    +  }
    +
    +  return goog.ui.AbstractSpellChecker.AsyncResult.DONE;
    +};
    +
    +
    +/**
    + * Continues processing started by processTextAsync. Calls virtual
    + * processWord_ and processRange_ methods.
    + *
    + * @return {goog.ui.AbstractSpellChecker.AsyncResult} operation result.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.continueAsyncProcessing = function() {
    +  if (!this.asyncMode_ || this.asyncText_ == null || !this.asyncNode_) {
    +    throw Error('Not in async mode or processing not started.');
    +  }
    +  var node = /** @type {Node} */ (this.asyncNode_);
    +  var stringSegmentStart = this.asyncRangeStart_;
    +  goog.asserts.assertNumber(stringSegmentStart);
    +  var text = this.asyncText_;
    +
    +  var result;
    +  while (result = this.splitRegex_.exec(text)) {
    +    if (result[0].length == 0) {
    +      break;
    +    }
    +    var word = result[1];
    +    if (word) {
    +      var status = this.spellCheck.checkWord(word);
    +      if (status != goog.spell.SpellCheck.WordStatus.VALID) {
    +        var preceedingText = text.substr(stringSegmentStart, result.index -
    +            stringSegmentStart);
    +        if (preceedingText) {
    +          this.processRange(node, preceedingText);
    +        }
    +        stringSegmentStart = result.index + word.length;
    +        this.processWord(node, word, status);
    +      }
    +    }
    +    this.processedElementsCount_++;
    +    if (this.processedElementsCount_ > this.asyncWordsPerBatch_) {
    +      this.processedElementsCount_ = 0;
    +      this.asyncRangeStart_ = stringSegmentStart;
    +      return goog.ui.AbstractSpellChecker.AsyncResult.PENDING;
    +    }
    +  }
    +  delete this.asyncText_;
    +  this.asyncRangeStart_ = 0;
    +  delete this.asyncNode_;
    +
    +  var leftoverText = text.substr(stringSegmentStart);
    +  if (leftoverText) {
    +    this.processRange(node, leftoverText);
    +  }
    +
    +  return goog.ui.AbstractSpellChecker.AsyncResult.DONE;
    +};
    +
    +
    +/**
    + * Converts a word to an internal key representation. This is necessary to
    + * avoid collisions with object's internal namespace. Only words that are
    + * reserved need to be escaped.
    + *
    + * @param {string} word The word to map.
    + * @return {string} The index.
    + * @private
    + */
    +goog.ui.AbstractSpellChecker.toInternalKey_ = function(word) {
    +  if (word in Object.prototype) {
    +    return goog.ui.AbstractSpellChecker.KEY_PREFIX_ + word;
    +  }
    +  return word;
    +};
    +
    +
    +/**
    + * Navigate keyboard focus in the given direction.
    + *
    + * @param {goog.ui.AbstractSpellChecker.Direction} direction The direction to
    + *     navigate in.
    + * @return {boolean} Whether the action is handled here.  If not handled
    + *     here, the initiating event may be propagated.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.navigate = function(direction) {
    +  var handled = false;
    +  var isMovingToNextWord =
    +      direction == goog.ui.AbstractSpellChecker.Direction.NEXT;
    +  var focusedIndex = this.getFocusedElementIndex();
    +
    +  var el;
    +  do {
    +    // Determine new index based on given direction.
    +    focusedIndex += isMovingToNextWord ? 1 : -1;
    +
    +    if (focusedIndex < 1 || focusedIndex > this.getLastIndex()) {
    +      // Exit the loop, because this focusedIndex cannot have an element.
    +      handled = true;
    +      break;
    +    }
    +
    +    // Word elements are removed during the correction action. If no element is
    +    // found for the new focusedIndex, then try again with the next value.
    +  } while (!(el = this.getElementByIndex(focusedIndex)));
    +
    +  if (el) {
    +    this.setFocusedElementIndex(focusedIndex);
    +    this.focusOnElement(el);
    +    handled = true;
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Returns the index of the currently focussed invalid word element. This index
    + * starts at one instead of zero.
    + *
    + * @return {number} the index of the currently focussed element
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.getFocusedElementIndex = function() {
    +  return this.focusedElementIndex_;
    +};
    +
    +
    +/**
    + * Sets the index of the currently focussed invalid word element. This index
    + * should start at one instead of zero.
    + *
    + * @param {number} focusElementIndex the index of the currently focussed element
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.setFocusedElementIndex =
    +    function(focusElementIndex) {
    +  this.focusedElementIndex_ = focusElementIndex;
    +};
    +
    +
    +/**
    + * Sets the focus on the provided word element.
    + *
    + * @param {Element} element The word element that should receive focus.
    + * @protected
    + */
    +goog.ui.AbstractSpellChecker.prototype.focusOnElement = function(element) {
    +  element.focus();
    +};
    +
    +
    +/**
    + * Constants for representing the direction while navigating.
    + *
    + * @enum {number}
    + */
    +goog.ui.AbstractSpellChecker.Direction = {
    +  PREVIOUS: 0,
    +  NEXT: 1
    +};
    +
    +
    +/**
    + * Constants for the result of asynchronous processing.
    + * @enum {number}
    + */
    +goog.ui.AbstractSpellChecker.AsyncResult = {
    +  /**
    +   * Caller must reschedule operation and call continueAsyncProcessing on the
    +   * new stack frame.
    +   */
    +  PENDING: 1,
    +  /**
    +   * Current element has been fully processed. Caller can call
    +   * processTextAsync or finishAsyncProcessing.
    +   */
    +  DONE: 2
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/ac.js b/mapbox-gl/closure-library/closure/goog/ui/ac/ac.js
    new file mode 100644
    index 0000000000..910289510c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/ac.js
    @@ -0,0 +1,50 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility methods supporting the autocomplete package.
    + *
    + * @see ../../demos/autocomplete-basic.html
    + */
    +
    +goog.provide('goog.ui.ac');
    +
    +goog.require('goog.ui.ac.ArrayMatcher');
    +goog.require('goog.ui.ac.AutoComplete');
    +goog.require('goog.ui.ac.InputHandler');
    +goog.require('goog.ui.ac.Renderer');
    +
    +
    +/**
    + * Factory function for building a basic autocomplete widget that autocompletes
    + * an inputbox or text area from a data array.
    + * @param {Array<?>} data Data array.
    + * @param {Element} input Input element or text area.
    + * @param {boolean=} opt_multi Whether to allow multiple entries separated with
    + *     semi-colons or commas.
    + * @param {boolean=} opt_useSimilar use similar matches. e.g. "gost" => "ghost".
    + * @return {!goog.ui.ac.AutoComplete} A new autocomplete object.
    + */
    +goog.ui.ac.createSimpleAutoComplete =
    +    function(data, input, opt_multi, opt_useSimilar) {
    +  var matcher = new goog.ui.ac.ArrayMatcher(data, !opt_useSimilar);
    +  var renderer = new goog.ui.ac.Renderer();
    +  var inputHandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi);
    +
    +  var autoComplete = new goog.ui.ac.AutoComplete(
    +      matcher, renderer, inputHandler);
    +  inputHandler.attachAutoComplete(autoComplete);
    +  inputHandler.attachInputs(input);
    +  return autoComplete;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.html
    new file mode 100644
    index 0000000000..9a1de80b0f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  Integration tests for the entire autocomplete package.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.AutoComplete
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.acTest');
    +  </script>
    + </head>
    + <body id="body">
    +  <input type="text" id="input" />
    +  <input type="text" id="user" value="For manual testing" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.js
    new file mode 100644
    index 0000000000..a6e1967e28
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/ac_test.js
    @@ -0,0 +1,209 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.acTest');
    +goog.setTestOnly('goog.ui.acTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.dom.selection');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ac');
    +goog.require('goog.userAgent');
    +var autocomplete;
    +var data = ['ab', 'aab', 'aaab'];
    +var input;
    +var mockClock;
    +
    +function setUpPage() {
    +  goog.ui.ac.createSimpleAutoComplete(data, goog.dom.getElement('user'), true,
    +      false);
    +}
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +  input = goog.dom.getElement('input');
    +  input.value = '';
    +  autocomplete = goog.ui.ac.createSimpleAutoComplete(data, input, true, false);
    +}
    +
    +function tearDown() {
    +  autocomplete.dispose();
    +  mockClock.dispose();
    +}
    +
    +//=========================================================================
    +// Utility methods
    +
    +
    +/**
    + * Fire listeners of a given type that are listening to the event's
    + * currentTarget.
    + *
    + * @param {goog.events.BrowserEvent} event
    + */
    +function simulateEvent(event) {
    +  goog.events.fireListeners(
    +      event.currentTarget, event.type, true, event);
    +  goog.events.fireListeners(
    +      event.currentTarget, event.type, false, event);
    +}
    +
    +
    +/**
    + * Fire all key event listeners that are listening to the input element.
    + *
    + * @param {number} keyCode The key code.
    + */
    +function simulateAllKeyEventsOnInput(keyCode) {
    +  var eventTypes = [
    +    goog.events.EventType.KEYDOWN,
    +    goog.events.EventType.KEYPRESS,
    +    goog.events.EventType.KEYUP
    +  ];
    +
    +  goog.array.forEach(eventTypes,
    +      function(type) {
    +        var event = new goog.events.Event(type, input);
    +        event.keyCode = keyCode;
    +        simulateEvent(new goog.events.BrowserEvent(event, input));
    +      });
    +}
    +
    +
    +/**
    + * @param {string} text
    + * @return {Node} Node whose inner text maches the given text.
    + */
    +function findNodeByInnerText(text) {
    +  return goog.dom.findNode(document.body, function(node) {
    +    try {
    +      var display = goog.userAgent.IE ?
    +          goog.style.getCascadedStyle(node, 'display') :
    +          goog.style.getComputedStyle(node, 'display');
    +
    +      return goog.dom.getRawTextContent(node) == text &&
    +          'none' != display && node.nodeType == goog.dom.NodeType.ELEMENT;
    +    } catch (e) {
    +      return false;
    +    }
    +  });
    +}
    +
    +//=========================================================================
    +// Tests
    +
    +
    +/**
    + * Ensure that the display of the autocompleter works.
    + */
    +function testBasicDisplay() {
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.DOWN);
    +
    +  input.value = 'a';
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.A);
    +  mockClock.tick(500);
    +
    +  var nodes = [
    +    findNodeByInnerText(data[0]),
    +    findNodeByInnerText(data[1]),
    +    findNodeByInnerText(data[2])
    +  ];
    +  assert(!!nodes[0]);
    +  assert(!!nodes[1]);
    +  assert(!!nodes[2]);
    +  assert(goog.style.isUnselectable(nodes[0]));
    +  assert(goog.style.isUnselectable(nodes[1]));
    +  assert(goog.style.isUnselectable(nodes[2]));
    +
    +  input.value = 'aa';
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.A);
    +  mockClock.tick(500);
    +
    +  assertFalse(!!findNodeByInnerText(data[0]));
    +  assert(!!findNodeByInnerText(data[1]));
    +  assert(!!findNodeByInnerText(data[2]));
    +}
    +
    +
    +/**
    + * Ensure that key navigation with multiple inputs work
    + */
    +function testKeyNavigation() {
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.DOWN);
    +
    +  input.value = 'c, a';
    +  goog.dom.selection.setCursorPosition(input, 'c, a'.length);
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.A);
    +  mockClock.tick(500);
    +
    +  assert(document.body.innerHTML, !!findNodeByInnerText(data[1]));
    +  assert(!!findNodeByInnerText(data[2]));
    +
    +  var selected = goog.asserts.assertElement(findNodeByInnerText(data[0]));
    +  assertTrue('Should have new standard active class',
    +      goog.dom.classlist.contains(selected, 'ac-active'));
    +  assertTrue('Should have legacy active class',
    +      goog.dom.classlist.contains(selected, 'active'));
    +
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.DOWN);
    +  assertFalse(goog.dom.classlist.contains(
    +      goog.asserts.assertElement(findNodeByInnerText(data[0])), 'ac-active'));
    +  assert(goog.dom.classlist.contains(
    +      goog.asserts.assertElement(findNodeByInnerText(data[1])), 'ac-active'));
    +
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.ENTER);
    +  assertEquals('c, aab, ', input.value);
    +}
    +
    +
    +/**
    + * Ensure that mouse navigation with multiple inputs works.
    + */
    +function testMouseNavigation() {
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.DOWN);
    +
    +  input.value = 'c, a';
    +  goog.dom.selection.setCursorPosition(input, 'c, a'.length);
    +  simulateAllKeyEventsOnInput(goog.events.KeyCodes.A);
    +  mockClock.tick(500);
    +
    +  var secondOption = goog.asserts.assertElement(findNodeByInnerText(data[1]));
    +  var parent = secondOption.parentNode;
    +  assertFalse(goog.dom.classlist.contains(secondOption, 'ac-active'));
    +
    +  var mouseOver = new goog.events.Event(
    +      goog.events.EventType.MOUSEOVER, secondOption);
    +  simulateEvent(new goog.events.BrowserEvent(mouseOver, parent));
    +  assert(goog.dom.classlist.contains(secondOption, 'ac-active'));
    +
    +  var mouseDown = new goog.events.Event(
    +      goog.events.EventType.MOUSEDOWN, secondOption);
    +  simulateEvent(new goog.events.BrowserEvent(mouseDown, parent));
    +  var mouseClick = new goog.events.Event(
    +      goog.events.EventType.CLICK, secondOption);
    +  simulateEvent(new goog.events.BrowserEvent(mouseClick, parent));
    +
    +  assertEquals('c, aab, ', input.value);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher.js b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher.js
    new file mode 100644
    index 0000000000..6d41584e4c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher.js
    @@ -0,0 +1,216 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Basic class for matching words in an array.
    + *
    + */
    +
    +
    +goog.provide('goog.ui.ac.ArrayMatcher');
    +
    +goog.require('goog.string');
    +
    +
    +
    +/**
    + * Basic class for matching words in an array
    + * @constructor
    + * @param {Array<?>} rows Dictionary of items to match.  Can be objects if they
    + *     have a toString method that returns the value to match against.
    + * @param {boolean=} opt_noSimilar if true, do not do similarity matches for the
    + *     input token against the dictionary.
    + */
    +goog.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
    +  this.rows_ = rows || [];
    +  this.useSimilar_ = !opt_noSimilar;
    +};
    +
    +
    +/**
    + * Replaces the rows that this object searches over.
    + * @param {Array<?>} rows Dictionary of items to match.
    + */
    +goog.ui.ac.ArrayMatcher.prototype.setRows = function(rows) {
    +  this.rows_ = rows || [];
    +};
    +
    +
    +/**
    + * Function used to pass matches to the autocomplete
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @param {Function} matchHandler callback to execute after matching.
    + * @param {string=} opt_fullString The full string from the input box.
    + */
    +goog.ui.ac.ArrayMatcher.prototype.requestMatchingRows =
    +    function(token, maxMatches, matchHandler, opt_fullString) {
    +
    +  var matches = this.useSimilar_ ?
    +      goog.ui.ac.ArrayMatcher.getMatchesForRows(token, maxMatches, this.rows_) :
    +      this.getPrefixMatches(token, maxMatches);
    +
    +  matchHandler(token, matches);
    +};
    +
    +
    +/**
    + * Matches the token against the specified rows, first looking for prefix
    + * matches and if that fails, then looking for similar matches.
    + *
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @param {!Array<?>} rows Rows to search for matches. Can be objects if they
    + *     have a toString method that returns the value to match against.
    + * @return {!Array<?>} Rows that match.
    + */
    +goog.ui.ac.ArrayMatcher.getMatchesForRows =
    +    function(token, maxMatches, rows) {
    +  var matches =
    +      goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows(token, maxMatches, rows);
    +
    +  if (matches.length == 0) {
    +    matches = goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows(token,
    +        maxMatches, rows);
    +  }
    +  return matches;
    +};
    +
    +
    +/**
    + * Matches the token against the start of words in the row.
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @return {!Array<?>} Rows that match.
    + */
    +goog.ui.ac.ArrayMatcher.prototype.getPrefixMatches =
    +    function(token, maxMatches) {
    +  return goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows(token, maxMatches,
    +      this.rows_);
    +};
    +
    +
    +/**
    + * Matches the token against the start of words in the row.
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @param {!Array<?>} rows Rows to search for matches. Can be objects if they have
    + *     a toString method that returns the value to match against.
    + * @return {!Array<?>} Rows that match.
    + */
    +goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows =
    +    function(token, maxMatches, rows) {
    +  var matches = [];
    +
    +  if (token != '') {
    +    var escapedToken = goog.string.regExpEscape(token);
    +    var matcher = new RegExp('(^|\\W+)' + escapedToken, 'i');
    +
    +    for (var i = 0; i < rows.length && matches.length < maxMatches; i++) {
    +      var row = rows[i];
    +      if (String(row).match(matcher)) {
    +        matches.push(row);
    +      }
    +    }
    +  }
    +  return matches;
    +};
    +
    +
    +/**
    + * Matches the token against similar rows, by calculating "distance" between the
    + * terms.
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @return {!Array<?>} The best maxMatches rows.
    + */
    +goog.ui.ac.ArrayMatcher.prototype.getSimilarRows = function(token, maxMatches) {
    +  return goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows(token, maxMatches,
    +      this.rows_);
    +};
    +
    +
    +/**
    + * Matches the token against similar rows, by calculating "distance" between the
    + * terms.
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @param {!Array<?>} rows Rows to search for matches. Can be objects
    + *     if they have a toString method that returns the value to
    + *     match against.
    + * @return {!Array<?>} The best maxMatches rows.
    + */
    +goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows =
    +    function(token, maxMatches, rows) {
    +  var results = [];
    +
    +  for (var index = 0; index < rows.length; index++) {
    +    var row = rows[index];
    +    var str = token.toLowerCase();
    +    var txt = String(row).toLowerCase();
    +    var score = 0;
    +
    +    if (txt.indexOf(str) != -1) {
    +      score = parseInt((txt.indexOf(str) / 4).toString(), 10);
    +
    +    } else {
    +      var arr = str.split('');
    +
    +      var lastPos = -1;
    +      var penalty = 10;
    +
    +      for (var i = 0, c; c = arr[i]; i++) {
    +        var pos = txt.indexOf(c);
    +
    +        if (pos > lastPos) {
    +          var diff = pos - lastPos - 1;
    +
    +          if (diff > penalty - 5) {
    +            diff = penalty - 5;
    +          }
    +
    +          score += diff;
    +
    +          lastPos = pos;
    +        } else {
    +          score += penalty;
    +          penalty += 5;
    +        }
    +      }
    +    }
    +
    +    if (score < str.length * 6) {
    +      results.push({
    +        str: row,
    +        score: score,
    +        index: index
    +      });
    +    }
    +  }
    +
    +  results.sort(function(a, b) {
    +    var diff = a.score - b.score;
    +    if (diff != 0) {
    +      return diff;
    +    }
    +    return a.index - b.index;
    +  });
    +
    +  var matches = [];
    +  for (var i = 0; i < maxMatches && i < results.length; i++) {
    +    matches.push(results[i].str);
    +  }
    +
    +  return matches;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.html
    new file mode 100644
    index 0000000000..a6f044c709
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.ArrayMatcher
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.ac.ArrayMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.js
    new file mode 100644
    index 0000000000..c778a87dd4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/arraymatcher_test.js
    @@ -0,0 +1,133 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.ArrayMatcherTest');
    +goog.setTestOnly('goog.ui.ac.ArrayMatcherTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ac.ArrayMatcher');
    +// TODO(arv): Add more useful tests for the similarity matching.
    +
    +var ArrayMatcher = goog.ui.ac.ArrayMatcher;
    +
    +function testRequestingRows() {
    +  var items = ['a', 'Ab', 'abc', 'ba', 'ca'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res;
    +  function matcher(token, matches) {
    +    assertEquals('a', token);
    +    res = matches;
    +    assertEquals('Should have three matches', 3, matches.length);
    +    assertEquals('a', matches[0]);
    +    assertEquals('Ab', matches[1]);
    +    assertEquals('abc', matches[2]);
    +  }
    +
    +  am.requestMatchingRows('a', 10, matcher);
    +  var res2 = goog.ui.ac.ArrayMatcher.getMatchesForRows('a', 10, items);
    +  assertArrayEquals(res, res2);
    +}
    +
    +function testRequestingRowsMaxMatches() {
    +  var items = ['a', 'Ab', 'abc', 'ba', 'ca'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  function matcher(token, matches) {
    +    assertEquals('a', token);
    +    assertEquals('Should have two matches', 2, matches.length);
    +    assertEquals('a', matches[0]);
    +    assertEquals('Ab', matches[1]);
    +  }
    +
    +  am.requestMatchingRows('a', 2, matcher);
    +}
    +
    +function testRequestingRowsSimilarMatches() {
    +  // No prefix matches so use similar
    +  var items = ['b', 'c', 'ba', 'ca'];
    +  var am = new ArrayMatcher(items, false);
    +
    +  function matcher(token, matches) {
    +    assertEquals('a', token);
    +    assertEquals('Should have two matches', 2, matches.length);
    +    assertEquals('ba', matches[0]);
    +    assertEquals('ca', matches[1]);
    +  }
    +
    +  am.requestMatchingRows('a', 10, matcher);
    +}
    +
    +function testRequestingRowsSimilarMatchesMaxMatches() {
    +  // No prefix matches so use similar
    +  var items = ['b', 'c', 'ba', 'ca'];
    +  var am = new ArrayMatcher(items, false);
    +
    +  function matcher(token, matches) {
    +    assertEquals('a', token);
    +    assertEquals('Should have one match', 1, matches.length);
    +    assertEquals('ba', matches[0]);
    +  }
    +
    +  am.requestMatchingRows('a', 1, matcher);
    +}
    +
    +function testGetPrefixMatches() {
    +  var items = ['a', 'b', 'c'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res = am.getPrefixMatches('a', 10);
    +  assertEquals('Should have one match', 1, res.length);
    +  assertEquals('Should return \'a\'', 'a', res[0]);
    +  var res2 = goog.ui.ac.ArrayMatcher.getPrefixMatchesForRows('a', 10, items);
    +  assertArrayEquals(res, res2);
    +}
    +
    +function testGetPrefixMatchesMaxMatches() {
    +  var items = ['a', 'Ab', 'abc', 'ba', 'ca'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res = am.getPrefixMatches('a', 2);
    +  assertEquals('Should have two matches', 2, res.length);
    +  assertEquals('a', res[0]);
    +}
    +
    +function testGetPrefixMatchesEmptyToken() {
    +  var items = ['a', 'b', 'c'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res = am.getPrefixMatches('', 10);
    +  assertEquals('Should have no matches', 0, res.length);
    +}
    +
    +function testGetSimilarRows() {
    +  var items = ['xa', 'xb', 'xc'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res = am.getSimilarRows('a', 10);
    +  assertEquals('Should have one match', 1, res.length);
    +  assertEquals('xa', res[0]);
    +  var res2 = goog.ui.ac.ArrayMatcher.getSimilarMatchesForRows('a', 10, items);
    +  assertArrayEquals(res, res2);
    +}
    +
    +function testGetSimilarRowsMaxMatches() {
    +  var items = ['xa', 'xAa', 'xaAa'];
    +  var am = new ArrayMatcher(items, true);
    +
    +  var res = am.getSimilarRows('a', 2);
    +  assertEquals('Should have two matches', 2, res.length);
    +  assertEquals('xa', res[0]);
    +  assertEquals('xAa', res[1]);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete.js b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete.js
    new file mode 100644
    index 0000000000..4d710fdca1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete.js
    @@ -0,0 +1,921 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Gmail-like AutoComplete logic.
    + *
    + * @see ../../demos/autocomplete-basic.html
    + */
    +
    +goog.provide('goog.ui.ac.AutoComplete');
    +goog.provide('goog.ui.ac.AutoComplete.EventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * This is the central manager class for an AutoComplete instance. The matcher
    + * can specify disabled rows that should not be hilited or selected by
    + * implementing <code>isRowDisabled(row):boolean</code> for each autocomplete
    + * row. No row will be considered disabled if this method is not implemented.
    + *
    + * @param {Object} matcher A data source and row matcher, implements
    + *        <code>requestMatchingRows(token, maxMatches, matchCallback)</code>.
    + * @param {goog.events.EventTarget} renderer An object that implements
    + *        <code>
    + *          isVisible():boolean<br>
    + *          renderRows(rows:Array, token:string, target:Element);<br>
    + *          hiliteId(row-id:number);<br>
    + *          dismiss();<br>
    + *          dispose():
    + *        </code>.
    + * @param {Object} selectionHandler An object that implements
    + *        <code>
    + *          selectRow(row);<br>
    + *          update(opt_force);
    + *        </code>.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.ac.AutoComplete = function(matcher, renderer, selectionHandler) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * A data-source which provides autocomplete suggestions.
    +   *
    +   * TODO(chrishenry): Tighten the type to !goog.ui.ac.AutoComplete.Matcher.
    +   *
    +   * @type {Object}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.matcher_ = matcher;
    +
    +  /**
    +   * A handler which interacts with the input DOM element (textfield, textarea,
    +   * or richedit).
    +   *
    +   * TODO(chrishenry): Tighten the type to !Object.
    +   *
    +   * @type {Object}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.selectionHandler_ = selectionHandler;
    +
    +  /**
    +   * A renderer to render/show/highlight/hide the autocomplete menu.
    +   * @type {goog.events.EventTarget}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.renderer_ = renderer;
    +  goog.events.listen(
    +      renderer,
    +      [
    +        goog.ui.ac.AutoComplete.EventType.HILITE,
    +        goog.ui.ac.AutoComplete.EventType.SELECT,
    +        goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS,
    +        goog.ui.ac.AutoComplete.EventType.DISMISS
    +      ],
    +      this.handleEvent, false, this);
    +
    +  /**
    +   * Currently typed token which will be used for completion.
    +   * @type {?string}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.token_ = null;
    +
    +  /**
    +   * Autocomplete suggestion items.
    +   * @type {Array<?>}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.rows_ = [];
    +
    +  /**
    +   * Id of the currently highlighted row.
    +   * @type {number}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.hiliteId_ = -1;
    +
    +  /**
    +   * Id of the first row in autocomplete menu. Note that new ids are assigned
    +   * everytime new suggestions are fetched.
    +   *
    +   * TODO(chrishenry): Figure out what subclass does with this value
    +   * and whether we should expose a more proper API.
    +   *
    +   * @type {number}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.firstRowId_ = 0;
    +
    +  /**
    +   * The target HTML node for displaying.
    +   * @type {Element}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.target_ = null;
    +
    +  /**
    +   * The timer id for dismissing autocomplete menu with a delay.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.dismissTimer_ = null;
    +
    +  /**
    +   * Mapping from text input element to the anchor element. If the
    +   * mapping does not exist, the input element will act as the anchor
    +   * element.
    +   * @type {Object<Element>}
    +   * @private
    +   */
    +  this.inputToAnchorMap_ = {};
    +};
    +goog.inherits(goog.ui.ac.AutoComplete, goog.events.EventTarget);
    +
    +
    +/**
    + * The maximum number of matches that should be returned
    + * @type {number}
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.maxMatches_ = 10;
    +
    +
    +/**
    + * True iff the first row should automatically be highlighted
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.autoHilite_ = true;
    +
    +
    +/**
    + * True iff the user can unhilight all rows by pressing the up arrow.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.allowFreeSelect_ = false;
    +
    +
    +/**
    + * True iff item selection should wrap around from last to first. If
    + *     allowFreeSelect_ is on in conjunction, there is a step of free selection
    + *     before wrapping.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.wrap_ = false;
    +
    +
    +/**
    + * Whether completion from suggestion triggers fetching new suggestion.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.triggerSuggestionsOnUpdate_ = false;
    +
    +
    +/**
    + * Events associated with the autocomplete
    + * @enum {string}
    + */
    +goog.ui.ac.AutoComplete.EventType = {
    +
    +  /** A row has been highlighted by the renderer */
    +  ROW_HILITE: 'rowhilite',
    +
    +  // Note: The events below are used for internal autocomplete events only and
    +  // should not be used in non-autocomplete code.
    +
    +  /** A row has been mouseovered and should be highlighted by the renderer. */
    +  HILITE: 'hilite',
    +
    +  /** A row has been selected by the renderer */
    +  SELECT: 'select',
    +
    +  /** A dismiss event has occurred */
    +  DISMISS: 'dismiss',
    +
    +  /** Event that cancels a dismiss event */
    +  CANCEL_DISMISS: 'canceldismiss',
    +
    +  /**
    +   * Field value was updated.  A row field is included and is non-null when a
    +   * row has been selected.  The value of the row typically includes fields:
    +   * contactData and formattedValue as well as a toString function (though none
    +   * of these fields are guaranteed to exist).  The row field may be used to
    +   * return custom-type row data.
    +   */
    +  UPDATE: 'update',
    +
    +  /**
    +   * The list of suggestions has been updated, usually because either the list
    +   * has opened, or because the user has typed another character and the
    +   * suggestions have been updated, or the user has dismissed the autocomplete.
    +   */
    +  SUGGESTIONS_UPDATE: 'suggestionsupdate'
    +};
    +
    +
    +/**
    + * @typedef {{
    + *   requestMatchingRows:(!Function|undefined),
    + *   isRowDisabled:(!Function|undefined)
    + * }}
    + */
    +goog.ui.ac.AutoComplete.Matcher;
    +
    +
    +/**
    + * @return {!Object} The data source providing the `autocomplete
    + *     suggestions.
    + */
    +goog.ui.ac.AutoComplete.prototype.getMatcher = function() {
    +  return goog.asserts.assert(this.matcher_);
    +};
    +
    +
    +/**
    + * Sets the data source providing the autocomplete suggestions.
    + *
    + * See constructor documentation for the interface.
    + *
    + * @param {!Object} matcher The matcher.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.setMatcher = function(matcher) {
    +  this.matcher_ = matcher;
    +};
    +
    +
    +/**
    + * @return {!Object} The handler used to interact with the input DOM
    + *     element (textfield, textarea, or richedit), e.g. to update the
    + *     input DOM element with selected value.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.getSelectionHandler = function() {
    +  return goog.asserts.assert(this.selectionHandler_);
    +};
    +
    +
    +/**
    + * @return {goog.events.EventTarget} The renderer that
    + *     renders/shows/highlights/hides the autocomplete menu.
    + *     See constructor documentation for the expected renderer API.
    + */
    +goog.ui.ac.AutoComplete.prototype.getRenderer = function() {
    +  return this.renderer_;
    +};
    +
    +
    +/**
    + * Sets the renderer that renders/shows/highlights/hides the autocomplete
    + * menu.
    + *
    + * See constructor documentation for the expected renderer API.
    + *
    + * @param {goog.events.EventTarget} renderer The renderer.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.setRenderer = function(renderer) {
    +  this.renderer_ = renderer;
    +};
    +
    +
    +/**
    + * @return {?string} The currently typed token used for completion.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.getToken = function() {
    +  return this.token_;
    +};
    +
    +
    +/**
    + * Sets the current token (without changing the rendered autocompletion).
    + *
    + * NOTE(chrishenry): This method will likely go away when we figure
    + * out a better API.
    + *
    + * @param {?string} token The new token.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.setTokenInternal = function(token) {
    +  this.token_ = token;
    +};
    +
    +
    +/**
    + * @param {number} index The suggestion index, must be within the
    + *     interval [0, this.getSuggestionCount()).
    + * @return {Object} The currently suggested item at the given index
    + *     (or null if there is none).
    + */
    +goog.ui.ac.AutoComplete.prototype.getSuggestion = function(index) {
    +  return this.rows_[index];
    +};
    +
    +
    +/**
    + * @return {!Array<?>} The current autocomplete suggestion items.
    + */
    +goog.ui.ac.AutoComplete.prototype.getAllSuggestions = function() {
    +  return goog.asserts.assert(this.rows_);
    +};
    +
    +
    +/**
    + * @return {number} The number of currently suggested items.
    + */
    +goog.ui.ac.AutoComplete.prototype.getSuggestionCount = function() {
    +  return this.rows_.length;
    +};
    +
    +
    +/**
    + * @return {number} The id (not index!) of the currently highlighted row.
    + */
    +goog.ui.ac.AutoComplete.prototype.getHighlightedId = function() {
    +  return this.hiliteId_;
    +};
    +
    +
    +/**
    + * Generic event handler that handles any events this object is listening to.
    + * @param {goog.events.Event} e Event Object.
    + */
    +goog.ui.ac.AutoComplete.prototype.handleEvent = function(e) {
    +  var matcher = /** @type {?goog.ui.ac.AutoComplete.Matcher} */ (this.matcher_);
    +
    +  if (e.target == this.renderer_) {
    +    switch (e.type) {
    +      case goog.ui.ac.AutoComplete.EventType.HILITE:
    +        this.hiliteId(/** @type {number} */ (e.row));
    +        break;
    +
    +      case goog.ui.ac.AutoComplete.EventType.SELECT:
    +        var rowDisabled = false;
    +
    +        // e.row can be either a valid row id or empty.
    +        if (goog.isNumber(e.row)) {
    +          var rowId = e.row;
    +          var index = this.getIndexOfId(rowId);
    +          var row = this.rows_[index];
    +
    +          // Make sure the row selected is not a disabled row.
    +          rowDisabled = !!row && matcher.isRowDisabled &&
    +              matcher.isRowDisabled(row);
    +          if (row && !rowDisabled && this.hiliteId_ != rowId) {
    +            // Event target row not currently highlighted - fix the mismatch.
    +            this.hiliteId(rowId);
    +          }
    +        }
    +        if (!rowDisabled) {
    +          // Note that rowDisabled can be false even if e.row does not
    +          // contain a valid row ID; at least one client depends on us
    +          // proceeding anyway.
    +          this.selectHilited();
    +        }
    +        break;
    +
    +      case goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS:
    +        this.cancelDelayedDismiss();
    +        break;
    +
    +      case goog.ui.ac.AutoComplete.EventType.DISMISS:
    +        this.dismissOnDelay();
    +        break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the max number of matches to fetch from the Matcher.
    + *
    + * @param {number} max Max number of matches.
    + */
    +goog.ui.ac.AutoComplete.prototype.setMaxMatches = function(max) {
    +  this.maxMatches_ = max;
    +};
    +
    +
    +/**
    + * Sets whether or not the first row should be highlighted by default.
    + *
    + * @param {boolean} autoHilite true iff the first row should be
    + *      highlighted by default.
    + */
    +goog.ui.ac.AutoComplete.prototype.setAutoHilite = function(autoHilite) {
    +  this.autoHilite_ = autoHilite;
    +};
    +
    +
    +/**
    + * Sets whether or not the up/down arrow can unhilite all rows.
    + *
    + * @param {boolean} allowFreeSelect true iff the up arrow can unhilite all rows.
    + */
    +goog.ui.ac.AutoComplete.prototype.setAllowFreeSelect =
    +    function(allowFreeSelect) {
    +  this.allowFreeSelect_ = allowFreeSelect;
    +};
    +
    +
    +/**
    + * Sets whether or not selections can wrap around the edges.
    + *
    + * @param {boolean} wrap true iff sections should wrap around the edges.
    + */
    +goog.ui.ac.AutoComplete.prototype.setWrap = function(wrap) {
    +  this.wrap_ = wrap;
    +};
    +
    +
    +/**
    + * Sets whether or not to request new suggestions immediately after completion
    + * of a suggestion.
    + *
    + * @param {boolean} triggerSuggestionsOnUpdate true iff completion should fetch
    + *     new suggestions.
    + */
    +goog.ui.ac.AutoComplete.prototype.setTriggerSuggestionsOnUpdate = function(
    +    triggerSuggestionsOnUpdate) {
    +  this.triggerSuggestionsOnUpdate_ = triggerSuggestionsOnUpdate;
    +};
    +
    +
    +/**
    + * Sets the token to match against.  This triggers calls to the Matcher to
    + * fetch the matches (up to maxMatches), and then it triggers a call to
    + * <code>renderer.renderRows()</code>.
    + *
    + * @param {string} token The string for which to search in the Matcher.
    + * @param {string=} opt_fullString Optionally, the full string in the input
    + *     field.
    + */
    +goog.ui.ac.AutoComplete.prototype.setToken = function(token, opt_fullString) {
    +  if (this.token_ == token) {
    +    return;
    +  }
    +  this.token_ = token;
    +  this.matcher_.requestMatchingRows(this.token_,
    +      this.maxMatches_, goog.bind(this.matchListener_, this), opt_fullString);
    +  this.cancelDelayedDismiss();
    +};
    +
    +
    +/**
    + * Gets the current target HTML node for displaying autocomplete UI.
    + * @return {Element} The current target HTML node for displaying autocomplete
    + *     UI.
    + */
    +goog.ui.ac.AutoComplete.prototype.getTarget = function() {
    +  return this.target_;
    +};
    +
    +
    +/**
    + * Sets the current target HTML node for displaying autocomplete UI.
    + * Can be an implementation specific definition of how to display UI in relation
    + * to the target node.
    + * This target will be passed into  <code>renderer.renderRows()</code>
    + *
    + * @param {Element} target The current target HTML node for displaying
    + *     autocomplete UI.
    + */
    +goog.ui.ac.AutoComplete.prototype.setTarget = function(target) {
    +  this.target_ = target;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the autocomplete's renderer is open.
    + */
    +goog.ui.ac.AutoComplete.prototype.isOpen = function() {
    +  return this.renderer_.isVisible();
    +};
    +
    +
    +/**
    + * @return {number} Number of rows in the autocomplete.
    + * @deprecated Use this.getSuggestionCount().
    + */
    +goog.ui.ac.AutoComplete.prototype.getRowCount = function() {
    +  return this.getSuggestionCount();
    +};
    +
    +
    +/**
    + * Moves the hilite to the next non-disabled row.
    + * Calls renderer.hiliteId() when there's something to do.
    + * @return {boolean} Returns true on a successful hilite.
    + */
    +goog.ui.ac.AutoComplete.prototype.hiliteNext = function() {
    +  var lastId = this.firstRowId_ + this.rows_.length - 1;
    +  var toHilite = this.hiliteId_;
    +  // Hilite the next row, skipping any disabled rows.
    +  for (var i = 0; i < this.rows_.length; i++) {
    +    // Increment to the next row.
    +    if (toHilite >= this.firstRowId_ && toHilite < lastId) {
    +      toHilite++;
    +    } else if (toHilite == -1) {
    +      toHilite = this.firstRowId_;
    +    } else if (this.allowFreeSelect_ && toHilite == lastId) {
    +      this.hiliteId(-1);
    +      return false;
    +    } else if (this.wrap_ && toHilite == lastId) {
    +      toHilite = this.firstRowId_;
    +    } else {
    +      return false;
    +    }
    +
    +    if (this.hiliteId(toHilite)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Moves the hilite to the previous non-disabled row.  Calls
    + * renderer.hiliteId() when there's something to do.
    + * @return {boolean} Returns true on a successful hilite.
    + */
    +goog.ui.ac.AutoComplete.prototype.hilitePrev = function() {
    +  var lastId = this.firstRowId_ + this.rows_.length - 1;
    +  var toHilite = this.hiliteId_;
    +  // Hilite the previous row, skipping any disabled rows.
    +  for (var i = 0; i < this.rows_.length; i++) {
    +    // Decrement to the previous row.
    +    if (toHilite > this.firstRowId_) {
    +      toHilite--;
    +    } else if (this.allowFreeSelect_ && toHilite == this.firstRowId_) {
    +      this.hiliteId(-1);
    +      return false;
    +    } else if (this.wrap_ && (toHilite == -1 || toHilite == this.firstRowId_)) {
    +      toHilite = lastId;
    +    } else {
    +      return false;
    +    }
    +
    +    if (this.hiliteId(toHilite)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Hilites the id if it's valid and the row is not disabled, otherwise does
    + * nothing.
    + * @param {number} id A row id (not index).
    + * @return {boolean} Whether the id was hilited. Returns false if the row is
    + *     disabled.
    + */
    +goog.ui.ac.AutoComplete.prototype.hiliteId = function(id) {
    +  var index = this.getIndexOfId(id);
    +  var row = this.rows_[index];
    +  var rowDisabled = !!row && this.matcher_.isRowDisabled &&
    +      this.matcher_.isRowDisabled(row);
    +  if (!rowDisabled) {
    +    this.hiliteId_ = id;
    +    this.renderer_.hiliteId(id);
    +    return index != -1;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Hilites the index, if it's valid and the row is not disabled, otherwise does
    + * nothing.
    + * @param {number} index The row's index.
    + * @return {boolean} Whether the index was hilited.
    + */
    +goog.ui.ac.AutoComplete.prototype.hiliteIndex = function(index) {
    +  return this.hiliteId(this.getIdOfIndex_(index));
    +};
    +
    +
    +/**
    + * If there are any current matches, this passes the hilited row data to
    + * <code>selectionHandler.selectRow()</code>
    + * @return {boolean} Whether there are any current matches.
    + */
    +goog.ui.ac.AutoComplete.prototype.selectHilited = function() {
    +  var index = this.getIndexOfId(this.hiliteId_);
    +  if (index != -1) {
    +    var selectedRow = this.rows_[index];
    +    var suppressUpdate = this.selectionHandler_.selectRow(selectedRow);
    +    if (this.triggerSuggestionsOnUpdate_) {
    +      this.token_ = null;
    +      this.dismissOnDelay();
    +    } else {
    +      this.dismiss();
    +    }
    +    if (!suppressUpdate) {
    +      this.dispatchEvent({
    +        type: goog.ui.ac.AutoComplete.EventType.UPDATE,
    +        row: selectedRow,
    +        index: index
    +      });
    +      if (this.triggerSuggestionsOnUpdate_) {
    +        this.selectionHandler_.update(true);
    +      }
    +    }
    +    return true;
    +  } else {
    +    this.dismiss();
    +    this.dispatchEvent(
    +        {
    +          type: goog.ui.ac.AutoComplete.EventType.UPDATE,
    +          row: null,
    +          index: null
    +        });
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether or not the autocomplete is open and has a highlighted row.
    + * @return {boolean} Whether an autocomplete row is highlighted.
    + */
    +goog.ui.ac.AutoComplete.prototype.hasHighlight = function() {
    +  return this.isOpen() && this.getIndexOfId(this.hiliteId_) != -1;
    +};
    +
    +
    +/**
    + * Clears out the token, rows, and hilite, and calls
    + * <code>renderer.dismiss()</code>
    + */
    +goog.ui.ac.AutoComplete.prototype.dismiss = function() {
    +  this.hiliteId_ = -1;
    +  this.token_ = null;
    +  this.firstRowId_ += this.rows_.length;
    +  this.rows_ = [];
    +  window.clearTimeout(this.dismissTimer_);
    +  this.dismissTimer_ = null;
    +  this.renderer_.dismiss();
    +  this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE);
    +  this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.DISMISS);
    +};
    +
    +
    +/**
    + * Call a dismiss after a delay, if there's already a dismiss active, ignore.
    + */
    +goog.ui.ac.AutoComplete.prototype.dismissOnDelay = function() {
    +  if (!this.dismissTimer_) {
    +    this.dismissTimer_ = window.setTimeout(goog.bind(this.dismiss, this), 100);
    +  }
    +};
    +
    +
    +/**
    + * Cancels any delayed dismiss events immediately.
    + * @return {boolean} Whether a delayed dismiss was cancelled.
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.immediatelyCancelDelayedDismiss_ =
    +    function() {
    +  if (this.dismissTimer_) {
    +    window.clearTimeout(this.dismissTimer_);
    +    this.dismissTimer_ = null;
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Cancel the active delayed dismiss if there is one.
    + */
    +goog.ui.ac.AutoComplete.prototype.cancelDelayedDismiss = function() {
    +  // Under certain circumstances a cancel event occurs immediately prior to a
    +  // delayedDismiss event that it should be cancelling. To handle this situation
    +  // properly, a timer is used to stop that event.
    +  // Using only the timer creates undesirable behavior when the cancel occurs
    +  // less than 10ms before the delayed dismiss timout ends. If that happens the
    +  // clearTimeout() will occur too late and have no effect.
    +  if (!this.immediatelyCancelDelayedDismiss_()) {
    +    window.setTimeout(goog.bind(this.immediatelyCancelDelayedDismiss_, this),
    +        10);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.ac.AutoComplete.prototype.disposeInternal = function() {
    +  goog.ui.ac.AutoComplete.superClass_.disposeInternal.call(this);
    +  delete this.inputToAnchorMap_;
    +  this.renderer_.dispose();
    +  this.selectionHandler_.dispose();
    +  this.matcher_ = null;
    +};
    +
    +
    +/**
    + * Callback passed to Matcher when requesting matches for a token.
    + * This might be called synchronously, or asynchronously, or both, for
    + * any implementation of a Matcher.
    + * If the Matcher calls this back, with the same token this AutoComplete
    + * has set currently, then this will package the matching rows in object
    + * of the form
    + * <pre>
    + * {
    + *   id: an integer ID unique to this result set and AutoComplete instance,
    + *   data: the raw row data from Matcher
    + * }
    + * </pre>
    + *
    + * @param {string} matchedToken Token that corresponds with the rows.
    + * @param {!Array<?>} rows Set of data that match the given token.
    + * @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true,
    + *     keeps the currently hilited (by index) element hilited. If false not.
    + *     Otherwise a RenderOptions object.
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.matchListener_ =
    +    function(matchedToken, rows, opt_options) {
    +  if (this.token_ != matchedToken) {
    +    // Matcher's response token doesn't match current token.
    +    // This is probably an async response that came in after
    +    // the token was changed, so don't do anything.
    +    return;
    +  }
    +
    +  this.renderRows(rows, opt_options);
    +};
    +
    +
    +/**
    + * Renders the rows and adds highlighting.
    + * @param {!Array<?>} rows Set of data that match the given token.
    + * @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true,
    + *     keeps the currently hilited (by index) element hilited. If false not.
    + *     Otherwise a RenderOptions object.
    + */
    +goog.ui.ac.AutoComplete.prototype.renderRows = function(rows, opt_options) {
    +  // The optional argument should be a RenderOptions object.  It can be a
    +  // boolean for backwards compatibility, defaulting to false.
    +  var optionsObj = goog.typeOf(opt_options) == 'object' && opt_options;
    +
    +  var preserveHilited =
    +      optionsObj ? optionsObj.getPreserveHilited() : opt_options;
    +  var indexToHilite = preserveHilited ? this.getIndexOfId(this.hiliteId_) : -1;
    +
    +  // Current token matches the matcher's response token.
    +  this.firstRowId_ += this.rows_.length;
    +  this.rows_ = rows;
    +  var rendRows = [];
    +  for (var i = 0; i < rows.length; ++i) {
    +    rendRows.push({
    +      id: this.getIdOfIndex_(i),
    +      data: rows[i]
    +    });
    +  }
    +
    +  var anchor = null;
    +  if (this.target_) {
    +    anchor = this.inputToAnchorMap_[goog.getUid(this.target_)] || this.target_;
    +  }
    +  this.renderer_.setAnchorElement(anchor);
    +  this.renderer_.renderRows(rendRows, this.token_, this.target_);
    +
    +  var autoHilite = this.autoHilite_;
    +  if (optionsObj && optionsObj.getAutoHilite() !== undefined) {
    +    autoHilite = optionsObj.getAutoHilite();
    +  }
    +  this.hiliteId_ = -1;
    +  if ((autoHilite || indexToHilite >= 0) &&
    +      rendRows.length != 0 &&
    +      this.token_) {
    +    if (indexToHilite >= 0) {
    +      this.hiliteId(this.getIdOfIndex_(indexToHilite));
    +    } else {
    +      // Hilite the first non-disabled row.
    +      this.hiliteNext();
    +    }
    +  }
    +  this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE);
    +};
    +
    +
    +/**
    + * Gets the index corresponding to a particular id.
    + * @param {number} id A unique id for the row.
    + * @return {number} A valid index into rows_, or -1 if the id is invalid.
    + * @protected
    + */
    +goog.ui.ac.AutoComplete.prototype.getIndexOfId = function(id) {
    +  var index = id - this.firstRowId_;
    +  if (index < 0 || index >= this.rows_.length) {
    +    return -1;
    +  }
    +  return index;
    +};
    +
    +
    +/**
    + * Gets the id corresponding to a particular index.  (Does no checking.)
    + * @param {number} index The index of a row in the result set.
    + * @return {number} The id that currently corresponds to that index.
    + * @private
    + */
    +goog.ui.ac.AutoComplete.prototype.getIdOfIndex_ = function(index) {
    +  return this.firstRowId_ + index;
    +};
    +
    +
    +/**
    + * Attach text areas or input boxes to the autocomplete by DOM reference.  After
    + * elements are attached to the autocomplete, when a user types they will see
    + * the autocomplete drop down.
    + * @param {...Element} var_args Variable args: Input or text area elements to
    + *     attach the autocomplete too.
    + */
    +goog.ui.ac.AutoComplete.prototype.attachInputs = function(var_args) {
    +  // Delegate to the input handler
    +  var inputHandler = /** @type {goog.ui.ac.InputHandler} */
    +      (this.selectionHandler_);
    +  inputHandler.attachInputs.apply(inputHandler, arguments);
    +};
    +
    +
    +/**
    + * Detach text areas or input boxes to the autocomplete by DOM reference.
    + * @param {...Element} var_args Variable args: Input or text area elements to
    + *     detach from the autocomplete.
    + */
    +goog.ui.ac.AutoComplete.prototype.detachInputs = function(var_args) {
    +  // Delegate to the input handler
    +  var inputHandler = /** @type {goog.ui.ac.InputHandler} */
    +      (this.selectionHandler_);
    +  inputHandler.detachInputs.apply(inputHandler, arguments);
    +
    +  // Remove mapping from input to anchor if one exists.
    +  goog.array.forEach(arguments, function(input) {
    +    goog.object.remove(this.inputToAnchorMap_, goog.getUid(input));
    +  }, this);
    +};
    +
    +
    +/**
    + * Attaches the autocompleter to a text area or text input element
    + * with an anchor element. The anchor element is the element the
    + * autocomplete box will be positioned against.
    + * @param {Element} inputElement The input element. May be 'textarea',
    + *     text 'input' element, or any other element that exposes similar
    + *     interface.
    + * @param {Element} anchorElement The anchor element.
    + */
    +goog.ui.ac.AutoComplete.prototype.attachInputWithAnchor = function(
    +    inputElement, anchorElement) {
    +  this.inputToAnchorMap_[goog.getUid(inputElement)] = anchorElement;
    +  this.attachInputs(inputElement);
    +};
    +
    +
    +/**
    + * Forces an update of the display.
    + * @param {boolean=} opt_force Whether to force an update.
    + */
    +goog.ui.ac.AutoComplete.prototype.update = function(opt_force) {
    +  var inputHandler = /** @type {goog.ui.ac.InputHandler} */
    +      (this.selectionHandler_);
    +  inputHandler.update(opt_force);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.html
    new file mode 100644
    index 0000000000..e35cc7eba6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.AutoComplete
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ac.AutoCompleteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="test-area">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.js
    new file mode 100644
    index 0000000000..359a255d00
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/autocomplete_test.js
    @@ -0,0 +1,1678 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.AutoCompleteTest');
    +goog.setTestOnly('goog.ui.ac.AutoCompleteTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.string');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.ui.ac.AutoComplete');
    +goog.require('goog.ui.ac.InputHandler');
    +goog.require('goog.ui.ac.RenderOptions');
    +goog.require('goog.ui.ac.Renderer');
    +
    +
    +
    +/**
    + * Mock DataStore
    + * @constructor
    + */
    +function MockDS(opt_autoHilite) {
    +  this.autoHilite_ = opt_autoHilite;
    +  var disabledRow = {
    +    match: function(str) {return this.text.match(str);},
    +    rowDisabled: true,
    +    text: 'hello@u.edu'
    +  };
    +  this.rows_ = [
    +    '"Slartibartfast Theadore" <fjordmaster@magrathea.com>',
    +    '"Zaphod Beeblebrox" <theprez@universe.gov>',
    +    '"Ford Prefect" <ford@theguide.com>',
    +    '"Arthur Dent" <has.no.tea@gmail.com>',
    +    '"Marvin The Paranoid Android" <marv@googlemail.com>',
    +    'the.mice@magrathea.com',
    +    'the.mice@myotherdomain.com',
    +    'hello@a.com',
    +    disabledRow,
    +    'row@u.edu',
    +    'person@a.edu'
    +  ];
    +  this.isRowDisabled = function(row) {
    +    return !!row.rowDisabled;
    +  };
    +}
    +
    +MockDS.prototype.requestMatchingRows = function(token, maxMatches,
    +                                                matchHandler) {
    +  var escapedToken = goog.string.regExpEscape(token);
    +  var matcher = new RegExp('(^|\\W+)' + escapedToken);
    +  var matches = [];
    +  for (var i = 0; i < this.rows_.length && matches.length < maxMatches; ++i) {
    +    var row = this.rows_[i];
    +    if (row.match(matcher)) {
    +      matches.push(row);
    +    }
    +  }
    +  if (this.autoHilite_ === undefined) {
    +    matchHandler(token, matches);
    +  } else {
    +    var options = new goog.ui.ac.RenderOptions();
    +    options.setAutoHilite(this.autoHilite_);
    +    matchHandler(token, matches, options);
    +  }
    +};
    +
    +
    +/**
    + * Mock Selection Handler
    + */
    +
    +function MockSelect() {
    +}
    +goog.inherits(MockSelect, goog.events.EventTarget);
    +
    +MockSelect.prototype.selectRow = function(row) {
    +  this.selectedRow = row;
    +};
    +
    +
    +
    +/**
    + * Renderer subclass that exposes additional private members for testing.
    + * @constructor
    + */
    +function TestRend() {
    +  goog.ui.ac.Renderer.call(this, goog.dom.getElement('test-area'));
    +}
    +goog.inherits(TestRend, goog.ui.ac.Renderer);
    +
    +TestRend.prototype.getRenderedRows = function() {
    +  return this.rows_;
    +};
    +
    +TestRend.prototype.getHilitedRowIndex = function() {
    +  return this.hilitedRow_;
    +};
    +
    +TestRend.prototype.getHilitedRowDiv = function() {
    +  return this.rowDivs_[this.hilitedRow_];
    +};
    +
    +TestRend.prototype.getRowDiv = function(index) {
    +  return this.rowDivs_[index];
    +};
    +
    +var handler;
    +var inputElement;
    +var mockControl;
    +
    +function setUp() {
    +  inputElement = goog.dom.createDom('input', {type: 'text'});
    +  handler = new goog.events.EventHandler();
    +  mockControl = new goog.testing.MockControl();
    +}
    +
    +function tearDown() {
    +  handler.dispose();
    +  mockControl.$tearDown();
    +  goog.dom.removeChildren(goog.dom.getElement('test-area'));
    +}
    +
    +
    +/**
    + * Make sure results are truncated (or not) by setMaxMatches.
    + */
    +function testMaxMatches() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  ac.setMaxMatches(2);
    +  ac.setToken('the');
    +  assertEquals(2, rend.getRenderedRows().length);
    +  ac.setToken('');
    +
    +  ac.setMaxMatches(3);
    +  ac.setToken('the');
    +  assertEquals(3, rend.getRenderedRows().length);
    +  ac.setToken('');
    +
    +  ac.setMaxMatches(1000);
    +  ac.setToken('the');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  ac.setToken('');
    +}
    +
    +function testHiliteViaMouse() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var updates = 0;
    +  var row = null;
    +  var rowNode = null;
    +  handler.listen(rend,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function(evt) {
    +        updates++;
    +        rowNode = evt.rowNode;
    +      });
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setMaxMatches(4);
    +  ac.setToken('the');
    +  // Need to set the startRenderingRows_ time to something long ago, otherwise
    +  // the mouse event will not be fired.  (The autocomplete logic waits for some
    +  // time to pass after rendering before firing mouseover events.)
    +  rend.startRenderingRows_ = -1;
    +  var hilitedRowDiv = rend.getRowDiv(3);
    +  goog.testing.events.fireMouseOverEvent(hilitedRowDiv);
    +  assertEquals(2, updates);
    +  assertTrue(goog.string.contains(rowNode.innerHTML, 'mice@myotherdomain.com'));
    +}
    +
    +function testMouseClickBeforeHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setMaxMatches(4);
    +  ac.setToken('the');
    +  // Need to set the startRenderingRows_ time to something long ago, otherwise
    +  // the mouse event will not be fired.  (The autocomplete logic waits for some
    +  // time to pass after rendering before firing mouseover events.)
    +  rend.startRenderingRows_ = -1;
    +
    +  // hilite row 3...
    +  var hilitedRowDiv = rend.getRowDiv(3);
    +  goog.testing.events.fireMouseOverEvent(hilitedRowDiv);
    +
    +  // but click row 2, to simulate mouse getting ahead of focus.
    +  var targetRowDiv = rend.getRowDiv(2);
    +  goog.testing.events.fireClickEvent(targetRowDiv);
    +
    +  assertEquals('the.mice@magrathea.com', select.selectedRow);
    +}
    +
    +function testMouseClickOnFirstRowBeforeHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAutoHilite(false);
    +  ac.setMaxMatches(4);
    +  ac.setToken('the');
    +
    +  // Click the first row before highlighting it, to simulate mouse getting ahead
    +  // of focus.
    +  var targetRowDiv = rend.getRowDiv(0);
    +  goog.testing.events.fireClickEvent(targetRowDiv);
    +
    +  assertEquals(
    +      '"Zaphod Beeblebrox" <theprez@universe.gov>', select.selectedRow);
    +}
    +
    +function testMouseClickOnRowAfterBlur() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var ih = new goog.ui.ac.InputHandler();
    +  ih.attachInput(inputElement);
    +
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, ih);
    +  goog.testing.events.fireFocusEvent(inputElement);
    +  ac.setToken('the');
    +  var targetRowDiv = rend.getRowDiv(0);
    +
    +  // Simulate the user clicking on an autocomplete row in the short time between
    +  // blur and autocomplete dismissal.
    +  goog.testing.events.fireBlurEvent(inputElement);
    +  assertNotThrows(function() {
    +    goog.testing.events.fireClickEvent(targetRowDiv);
    +  });
    +}
    +
    +
    +/*
    + * Send AutoComplete a SELECT event with empty string for the row. We can't
    + * simulate with a simple mouse click, so we dispatch the event directly.
    + */
    +function testSelectEventEmptyRow() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setMaxMatches(4);
    +  ac.setToken('the');
    +  rend.startRenderingRows_ = -1;
    +
    +  // hilight row 2 ('the.mice@...')
    +  var hilitedRowDiv = rend.getRowDiv(2);
    +  goog.testing.events.fireMouseOverEvent(hilitedRowDiv);
    +  assertUndefined(select.selectedRow);
    +
    +  // Dispatch an event that does not specify a row.
    +  rend.dispatchEvent({
    +    type: goog.ui.ac.AutoComplete.EventType.SELECT,
    +    row: ''
    +  });
    +
    +  assertEquals('the.mice@magrathea.com', select.selectedRow);
    +}
    +
    +function testSuggestionsUpdateEvent() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  var updates = 0;
    +  handler.listen(ac,
    +      goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE,
    +      function() {
    +        updates++;
    +      });
    +
    +  ac.setToken('the');
    +  assertEquals(1, updates);
    +
    +  ac.setToken('beeb');
    +  assertEquals(2, updates);
    +
    +  ac.setToken('ford');
    +  assertEquals(3, updates);
    +
    +  ac.dismiss();
    +  assertEquals(4, updates);
    +
    +  ac.setToken('dent');
    +  assertEquals(5, updates);
    +}
    +
    +function checkHilitedIndex(renderer, index) {
    +  assertEquals(index, renderer.getHilitedRowIndex());
    +}
    +
    +function testGetRowCount() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  assertEquals(0, ac.getRowCount());
    +
    +  ac.setToken('Zaphod');
    +  assertEquals(1, ac.getRowCount());
    +
    +  ac.setMaxMatches(2);
    +  ac.setToken('the');
    +  assertEquals(2, ac.getRowCount());
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with default behavior of
    + * allowFreeSelect_ and wrap_.
    + */
    +function testHiliteNextPrev_default() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  var updates = 0;
    +  handler.listen(rend,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function() {
    +        updates++;
    +      });
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('the');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // check to see if we can select the last of the 4 items
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +    // try going over the edge
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +
    +    // go back down
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +  }
    +  // 21 changes in the loop above (3 * 7)
    +  assertEquals(21, updates);
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with default behavior of
    + * allowFreeSelect_ and wrap_ and with a disabled first row.
    + */
    +function testHiliteNextPrevWithDisabledFirstRow_default() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  var updates = 0;
    +  handler.listen(rend,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function() {
    +        updates++;
    +      });
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(3);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled first row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('edu');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // The first row is disabled, second should be highlighted.
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back down
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    // First row is disabled, make sure we don't highlight it.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +  }
    +  // 9 changes in the loop above (3 * 3)
    +  assertEquals(9, updates);
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with default behavior of
    + * allowFreeSelect_ and wrap_ and with a disabled middle row.
    + */
    +function testHiliteNextPrevWithDisabledMiddleRow_default() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  var updates = 0;
    +  handler.listen(rend,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function() {
    +        updates++;
    +      });
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(3);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled middle row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('u');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled and should be skipped.
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back down
    +    ac.hilitePrev();
    +    // Second row is disabled, make sure we don't highlight it.
    +    checkHilitedIndex(rend, 0);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +  }
    +  // 9 changes in the loop above (3 * 3)
    +  assertEquals(9, updates);
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with default behavior of
    + * allowFreeSelect_ and wrap_ and with a disabled last row.
    + */
    +function testHiliteNextPrevWithDisabledLastRow_default() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  var updates = 0;
    +  handler.listen(rend,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function() {
    +        updates++;
    +      });
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(3);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled last row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('h');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    // try going over the edge since last row is disabled
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back down
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +  }
    +  // 9 changes in the loop above (3 * 3)
    +  assertEquals(9, updates);
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ off and
    + * allowFreeSelect_ on.
    + */
    +function testHiliteNextPrev_allowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('the');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // check to see if we can select the last of the 4 items
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +    // try going over the edge. Since allowFreeSelect is on, this will
    +    // deselect the last row.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, deselects first.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ off and
    + * allowFreeSelect_ on, and a disabled first row.
    + */
    +function testHiliteNextPrevWithDisabledFirstRow_allowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled first row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('edu');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // The first row is disabled, second should be highlighted.
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    // Try going over the edge. Since allowFreeSelect is on, this will
    +    // deselect the last row.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list, first row is disabled
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    // first is disabled, so deselect the second.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ off and
    + * allowFreeSelect_ on, and a disabled middle row.
    + */
    +function testHiliteNextPrevWithDisabledMiddleRow_allowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled middle row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('u');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled and should be skipped.
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since allowFreeSelect is on, this will
    +    // deselect the last row.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, deselects first.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ off and
    + * allowFreeSelect_ on, and a disabled last row.
    + */
    +function testHiliteNextPrevWithDisabledLastRow_allowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled last row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('h');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    // try going over the edge since last row is disabled. Since allowFreeSelect
    +    // is on, this will deselect the last row.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, deselects first.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ off.
    + */
    +function testHiliteNextPrev_wrap() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('the');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // check to see if we can select the last of the 4 items
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +    // try going over the edge. Since wrap is on, this will go back to 0.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, selects last.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 3);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ off and a disabled first row.
    + */
    +function testHiliteNextPrevWithDisabledFirstRow_wrap() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled first row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('edu');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // The first row is disabled, second should be highlighted.
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since wrap is on and first row is disabled,
    +    // this will go back to 1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    // first is disabled, so wrap and select the last.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ off and a disabled middle row.
    + */
    +function testHiliteNextPrevWithDisabledMiddleRow_wrap() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled middle row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('u');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled and should be skipped.
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since wrap is on, this will go back to 0.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, selects last.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ off and a disabled last row.
    + */
    +function testHiliteNextPrevWithDisabledLastRow_wrap() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled last row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('h');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    // try going over the edge since last row is disabled. Since wrap is on,
    +    // this will go back to 0.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, since wrap is on and last row is disabled, this
    +    // will select the second last.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on.
    + */
    +function testHiliteNextPrev_wrapAndAllowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('the');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // check to see if we can select the last of the 4 items
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +    // try going over the edge. Since free select is on, this should go
    +    // to -1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 3);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on and a disabled first row.
    + */
    +function testHiliteNextPrevWithDisabledFirstRow_wrapAndAllowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled first row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('edu');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // The first row is disabled, second should be highlighted.
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since free select is on, this should go to -1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list, fist row is disabled
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on and a disabled middle row.
    + */
    +function testHiliteNextPrevWithDisabledMiddleRow_wrapAndAllowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled middle row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('u');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled and should be skipped.
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since free select is on, this should go to -1
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on and a disabled last row.
    + */
    +function testHiliteNextPrevWithDisabledLastRow_wrapAndAllowFreeSelect() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled last row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('h');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    // try going over the edge since last row is disabled. Since free select is
    +    // on, this should go to -1
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to the second last, since last is disabled.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on AND turn autoHilite_ off.
    + */
    +function testHiliteNextPrev_wrapAndAllowFreeSelectNoAutoHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(false);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('the');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // check to see if we can select the last of the 4 items.
    +    // Initially nothing should be selected since autoHilite_ is off.
    +    checkHilitedIndex(rend, -1);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 3);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 3);
    +    // try going over the edge. Since free select is on, this should go
    +    // to -1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 3);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on AND turn autoHilite_ off, and a disabled first row.
    + */
    +function testHiliteNextPrevWithDisabledFirstRow_wrapAndAllowFreeSelectNoAutoHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(false);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled first row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('edu');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // Initially nothing should be selected since autoHilite_ is off.
    +    checkHilitedIndex(rend, -1);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +    ac.hiliteNext();
    +    // First row is disabled.
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since free select is on, this should go to -1
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list, first row is disabled
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on AND turn autoHilite_ off, and a disabled middle row.
    + */
    +function testHiliteNextPrevWithDisabledMiddleRow_wrapAndAllowFreeSelectNoAutoHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(false);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled middle row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('u');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // Initially nothing should be selected since autoHilite_ is off.
    +    checkHilitedIndex(rend, -1);
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled
    +    checkHilitedIndex(rend, 2);
    +    // try going over the edge. Since free select is on, this should go to -1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    // Second row is disabled.
    +    checkHilitedIndex(rend, 2);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 2);
    +  }
    +}
    +
    +
    +/**
    + * Try using next and prev to navigate past the ends with wrap_ on
    + * allowFreeSelect_ on AND turn autoHilite_ off, and a disabled last row.
    + */
    +function testHiliteNextPrevWithDisabledLastRow_wrapAndAllowFreeSelectNoAutoHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(false);
    +
    +  // make sure 'next' and 'prev' don't explode before any token is set
    +  ac.hiliteNext();
    +  ac.hilitePrev();
    +  ac.setMaxMatches(4);
    +  assertEquals(0, rend.getRenderedRows().length);
    +
    +  // check a few times with disabled last row
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('');
    +    ac.setToken('h');
    +    assertEquals(3, rend.getRenderedRows().length);
    +    // Initially nothing should be selected since autoHilite_ is off.
    +    checkHilitedIndex(rend, -1);
    +    ac.hilitePrev();
    +    // Last row is disabled
    +    checkHilitedIndex(rend, 1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +    // try going over the edge. Since free select is on, this should go to -1.
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, -1);
    +
    +    // go back down the list
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 0);
    +    ac.hiliteNext();
    +    checkHilitedIndex(rend, 1);
    +
    +    // go back up the list.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 0);
    +    // go back above the first, free select.
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, -1);
    +    // wrap to last
    +    ac.hilitePrev();
    +    checkHilitedIndex(rend, 1);
    +  }
    +}
    +
    +function testHiliteWithChangingNumberOfRows() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setAutoHilite(true);
    +  ac.setMaxMatches(4);
    +
    +  ac.setToken('m');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  checkHilitedIndex(rend, 0);
    +
    +  ac.setToken('ma');
    +  assertEquals(3, rend.getRenderedRows().length);
    +  checkHilitedIndex(rend, 0);
    +
    +  // Hilite the second element
    +  var id = rend.getRenderedRows()[1].id;
    +  ac.hiliteId(id);
    +
    +  ac.setToken('mar');
    +  assertEquals(1, rend.getRenderedRows().length);
    +  checkHilitedIndex(rend, 0);
    +
    +  ac.setToken('ma');
    +  assertEquals(3, rend.getRenderedRows().length);
    +  checkHilitedIndex(rend, 0);
    +
    +  // Hilite the second element
    +  var id = rend.getRenderedRows()[1].id;
    +  ac.hiliteId(id);
    +
    +  ac.setToken('m');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  checkHilitedIndex(rend, 0);
    +}
    +
    +
    +/**
    + * Checks that autohilite is disabled when there is no token; this allows the
    + * user to tab out of an empty autocomplete.
    + */
    +function testNoAutoHiliteWhenTokenIsEmpty() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(true);
    +  ac.setMaxMatches(4);
    +
    +  ac.setToken('');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  // No token; nothing should be hilited.
    +  checkHilitedIndex(rend, -1);
    +
    +  ac.setToken('the');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  // Now there is a token, so the first row should be highlighted.
    +  checkHilitedIndex(rend, 0);
    +}
    +
    +
    +/**
    + * Checks that opt_preserveHilited works.
    + */
    +function testPreserveHilitedWithoutAutoHilite() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setMaxMatches(4);
    +  ac.setAutoHilite(false);
    +
    +  ac.setToken('m');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  // No token; nothing should be hilited.
    +  checkHilitedIndex(rend, -1);
    +
    +  // Hilite the second element
    +  var id = rend.getRenderedRows()[1].id;
    +  ac.hiliteId(id);
    +
    +  checkHilitedIndex(rend, 1);
    +
    +  // Re-render and check if the second element is still hilited
    +  ac.renderRows(rend.getRenderedRows(), true /* preserve hilite */);
    +
    +  checkHilitedIndex(rend, 1);
    +
    +  // Re-render without preservation
    +  ac.renderRows(rend.getRenderedRows());
    +
    +  checkHilitedIndex(rend, -1);
    +}
    +
    +
    +/**
    + * Checks that the autohilite argument "true" of the matcher is used.
    + */
    +function testAutoHiliteFromMatcherTrue() {
    +  var ds = new MockDS(true);
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(false);  // Will be overruled.
    +  ac.setMaxMatches(4);
    +
    +  ac.setToken('the');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  // The first row should be highlighted.
    +  checkHilitedIndex(rend, 0);
    +}
    +
    +
    +/**
    + * Checks that the autohilite argument "false" of the matcher is used.
    + */
    +function testAutoHiliteFromMatcherFalse() {
    +  var ds = new MockDS(false);
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setWrap(true);
    +  ac.setAllowFreeSelect(true);
    +  ac.setAutoHilite(true);  // Will be overruled.
    +  ac.setMaxMatches(4);
    +
    +  ac.setToken('the');
    +  assertEquals(4, rend.getRenderedRows().length);
    +  // The first row should not be highlighted.
    +  checkHilitedIndex(rend, -1);
    +}
    +
    +
    +/**
    + * Hilite using ids, the way mouse-based hiliting would work.
    + */
    +function testHiliteId() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  // check a few times
    +  for (var i = 0; i < 3; ++i) {
    +    ac.setToken('m');
    +    assertEquals(4, rend.getRenderedRows().length);
    +    // try hiliting all 3
    +    for (var x = 0; x < 4; ++x) {
    +      var id = rend.getRenderedRows()[x].id;
    +      ac.hiliteId(id);
    +      assertEquals(ac.getIdOfIndex_(x), id);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Test selecting the hilited row
    + */
    +function testSelection() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac;
    +
    +  // try with default selection
    +  ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setToken('m');
    +  ac.selectHilited();
    +  assertEquals('"Slartibartfast Theadore" <fjordmaster@magrathea.com>',
    +               select.selectedRow);
    +
    +  // try second item
    +  ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setToken('the');
    +  ac.hiliteNext();
    +  ac.selectHilited();
    +  assertEquals('"Ford Prefect" <ford@theguide.com>',
    +               select.selectedRow);
    +}
    +
    +
    +/**
    + * Dismiss when empty and non-empty
    + */
    +function testDismiss() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +
    +  // dismiss empty
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  var dismissed = 0;
    +  handler.listen(ac,
    +      goog.ui.ac.AutoComplete.EventType.DISMISS,
    +      function() {
    +        dismissed++;
    +      });
    +  ac.dismiss();
    +  assertEquals(1, dismissed);
    +
    +  ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setToken('sir not seen in this picture');
    +  ac.dismiss();
    +
    +  // dismiss with contents
    +  ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setToken('t');
    +  ac.dismiss();
    +}
    +
    +function testTriggerSuggestionsOnUpdate() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +
    +  var dismissCalled = 0;
    +  rend.dismiss = function() {
    +    dismissCalled++;
    +  };
    +
    +  var updateCalled = 0;
    +  select.update = function(opt_force) {
    +    updateCalled++;
    +  };
    +
    +  // Normally, menu is dismissed after selecting row (without updating).
    +  ac.setToken('the');
    +  ac.selectHilited();
    +  assertEquals(1, dismissCalled);
    +  assertEquals(0, updateCalled);
    +
    +  // But not if we re-trigger on update.
    +  ac.setTriggerSuggestionsOnUpdate(true);
    +  ac.setToken('the');
    +  ac.selectHilited();
    +  assertEquals(1, dismissCalled);
    +  assertEquals(1, updateCalled);
    +}
    +
    +function testDispose() {
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setToken('the');
    +  ac.dispose();
    +}
    +
    +
    +/**
    + * Ensure that activedescendant is updated properly.
    + */
    +function testRolesAndStates() {
    +  function checkActiveDescendant(activeDescendant) {
    +    assertNotNull(inputElement);
    +    assertEquals(
    +        goog.a11y.aria.getActiveDescendant(inputElement),
    +        activeDescendant);
    +  }
    +  function checkRole(el, role) {
    +    assertNotNull(el);
    +    assertEquals(goog.a11y.aria.getRole(el), role);
    +  }
    +  var ds = new MockDS();
    +  var rend = new TestRend();
    +  var select = new MockSelect();
    +  var ac = new goog.ui.ac.AutoComplete(ds, rend, select);
    +  ac.setTarget(inputElement);
    +
    +  // initially activedescendant is not set
    +  checkActiveDescendant(null);
    +
    +  // highlight the matching row and check that activedescendant updates
    +  ac.setToken('');
    +  ac.setToken('the');
    +  ac.hiliteNext();
    +  checkActiveDescendant(rend.getHilitedRowDiv());
    +
    +  // highligted row should have a role of 'option'
    +  checkRole(rend.getHilitedRowDiv(), goog.a11y.aria.Role.OPTION);
    +
    +  // closing the autocomplete should clear activedescendant
    +  ac.dismiss();
    +  checkActiveDescendant(null);
    +}
    +
    +function testAttachInputWithAnchor() {
    +  var anchorElement = goog.dom.createDom('div', null, inputElement);
    +
    +  var mockRenderer = mockControl.createLooseMock(
    +      goog.ui.ac.Renderer, true);
    +  mockRenderer.setAnchorElement(anchorElement);
    +  var ignore = goog.testing.mockmatchers.ignoreArgument;
    +  mockRenderer.renderRows(ignore, ignore, inputElement);
    +
    +  var mockInputHandler = mockControl.createLooseMock(
    +      goog.ui.ac.InputHandler, true);
    +  mockInputHandler.attachInputs(inputElement);
    +
    +  mockControl.$replayAll();
    +  var autoComplete = new goog.ui.ac.AutoComplete(
    +      null, mockRenderer, mockInputHandler);
    +  autoComplete.attachInputWithAnchor(inputElement, anchorElement);
    +  autoComplete.setTarget(inputElement);
    +
    +  autoComplete.renderRows(['abc', 'def']);
    +  mockControl.$verifyAll();
    +}
    +
    +function testDetachInputWithAnchor() {
    +  var mockRenderer = mockControl.createLooseMock(
    +      goog.ui.ac.Renderer, true);
    +  var mockInputHandler = mockControl.createLooseMock(
    +      goog.ui.ac.InputHandler, true);
    +  var anchorElement = goog.dom.createDom('div', null, inputElement);
    +  var inputElement2 = goog.dom.createDom('input', {type: 'text'});
    +  var anchorElement2 = goog.dom.createDom('div', null, inputElement2);
    +
    +  mockControl.$replayAll();
    +  var autoComplete = new goog.ui.ac.AutoComplete(
    +      null, mockRenderer, mockInputHandler);
    +
    +  autoComplete.attachInputWithAnchor(inputElement, anchorElement);
    +  autoComplete.attachInputWithAnchor(inputElement2, anchorElement2);
    +  autoComplete.detachInputs(inputElement, inputElement2);
    +
    +  assertFalse(goog.getUid(inputElement) in autoComplete.inputToAnchorMap_);
    +  assertFalse(goog.getUid(inputElement2) in autoComplete.inputToAnchorMap_);
    +  mockControl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher.js b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher.js
    new file mode 100644
    index 0000000000..74ea252976
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher.js
    @@ -0,0 +1,273 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Matcher which maintains a client-side cache on top of some
    + * other matcher.
    + * @author reinerp@google.com (Reiner Pope)
    + */
    +
    +
    +goog.provide('goog.ui.ac.CachingMatcher');
    +
    +goog.require('goog.array');
    +goog.require('goog.async.Throttle');
    +goog.require('goog.ui.ac.ArrayMatcher');
    +goog.require('goog.ui.ac.RenderOptions');
    +
    +
    +
    +/**
    + * A matcher which wraps another (typically slow) matcher and
    + * keeps a client-side cache of the results. For instance, you can use this to
    + * wrap a RemoteArrayMatcher to hide the latency of the underlying matcher
    + * having to make ajax request.
    + *
    + * Objects in the cache are deduped on their stringified forms.
    + *
    + * Note - when the user types a character, they will instantly get a set of
    + * local results, and then some time later, the results from the server will
    + * show up.
    + *
    + * @constructor
    + * @param {!Object} baseMatcher The underlying matcher to use. Must implement
    + *     requestMatchingRows.
    + * @final
    + */
    +goog.ui.ac.CachingMatcher = function(baseMatcher) {
    +  /** @private {!Array<!Object>}} The cache. */
    +  this.rows_ = [];
    +
    +  /**
    +   * Set of stringified rows, for fast deduping. Each element of this.rows_
    +   * is stored in rowStrings_ as (' ' + row) to ensure we avoid builtin
    +   * properties like 'toString'.
    +   * @private {Object<string, boolean>}
    +   */
    +  this.rowStrings_ = {};
    +
    +  /**
    +   * Maximum number of rows in the cache. If the cache grows larger than this,
    +   * the entire cache will be emptied.
    +   * @private {number}
    +   */
    +  this.maxCacheSize_ = 1000;
    +
    +  /** @private {!Object} The underlying matcher to use. */
    +  this.baseMatcher_ = baseMatcher;
    +
    +  /**
    +   * Local matching function.
    +   * @private {function(string, number, !Array<!Object>): !Array<!Object>}
    +   */
    +  this.getMatchesForRows_ = goog.ui.ac.ArrayMatcher.getMatchesForRows;
    +
    +  /** @private {number} Number of matches to request from the base matcher. */
    +  this.baseMatcherMaxMatches_ = 100;
    +
    +  /** @private {goog.async.Throttle} */
    +  this.throttledTriggerBaseMatch_ =
    +      new goog.async.Throttle(this.triggerBaseMatch_, 150, this);
    +
    +  /** @private {string} */
    +  this.mostRecentToken_ = '';
    +
    +  /** @private {Function} */
    +  this.mostRecentMatchHandler_ = null;
    +
    +  /** @private {number} */
    +  this.mostRecentMaxMatches_ = 10;
    +
    +  /**
    +   * The set of rows which we last displayed.
    +   *
    +   * NOTE(reinerp): The need for this is subtle. When a server result comes
    +   * back, we don't want to suddenly change the list of results without the user
    +   * doing anything. So we make sure to add the new server results to the end of
    +   * the currently displayed list.
    +   *
    +   * We need to keep track of the last rows we displayed, because the "similar
    +   * matcher" we use locally might otherwise reorder results.
    +   *
    +   * @private {Array<!Object>}
    +   */
    +  this.mostRecentMatches_ = [];
    +};
    +
    +
    +/**
    + * Sets the number of milliseconds with which to throttle the match requests
    + * on the underlying matcher.
    + *
    + * Default value: 150.
    + *
    + * @param {number} throttleTime .
    + */
    +goog.ui.ac.CachingMatcher.prototype.setThrottleTime = function(throttleTime) {
    +  this.throttledTriggerBaseMatch_ =
    +      new goog.async.Throttle(this.triggerBaseMatch_, throttleTime, this);
    +};
    +
    +
    +/**
    + * Sets the maxMatches to use for the base matcher. If the base matcher makes
    + * AJAX requests, it may help to make this a large number so that the local
    + * cache gets populated quickly.
    + *
    + * Default value: 100.
    + *
    + * @param {number} maxMatches The value to set.
    + */
    +goog.ui.ac.CachingMatcher.prototype.setBaseMatcherMaxMatches =
    +    function(maxMatches) {
    +  this.baseMatcherMaxMatches_ = maxMatches;
    +};
    +
    +
    +/**
    + * Sets the maximum size of the local cache. If the local cache grows larger
    + * than this size, it will be emptied.
    + *
    + * Default value: 1000.
    + *
    + * @param {number} maxCacheSize .
    + */
    +goog.ui.ac.CachingMatcher.prototype.setMaxCacheSize = function(maxCacheSize) {
    +  this.maxCacheSize_ = maxCacheSize;
    +};
    +
    +
    +/**
    + * Sets the local matcher to use.
    + *
    + * The local matcher should be a function with the same signature as
    + * {@link goog.ui.ac.ArrayMatcher.getMatchesForRows}, i.e. its arguments are
    + * searchToken, maxMatches, rowsToSearch; and it returns a list of matching
    + * rows.
    + *
    + * Default value: {@link goog.ui.ac.ArrayMatcher.getMatchesForRows}.
    + *
    + * @param {function(string, number, !Array<!Object>): !Array<!Object>}
    + *     localMatcher
    + */
    +goog.ui.ac.CachingMatcher.prototype.setLocalMatcher = function(localMatcher) {
    +  this.getMatchesForRows_ = localMatcher;
    +};
    +
    +
    +/**
    + * Function used to pass matches to the autocomplete.
    + * @param {string} token Token to match.
    + * @param {number} maxMatches Max number of matches to return.
    + * @param {Function} matchHandler callback to execute after matching.
    + */
    +goog.ui.ac.CachingMatcher.prototype.requestMatchingRows =
    +    function(token, maxMatches, matchHandler) {
    +  this.mostRecentMaxMatches_ = maxMatches;
    +  this.mostRecentToken_ = token;
    +  this.mostRecentMatchHandler_ = matchHandler;
    +  this.throttledTriggerBaseMatch_.fire();
    +
    +  var matches = this.getMatchesForRows_(token, maxMatches, this.rows_);
    +  matchHandler(token, matches);
    +  this.mostRecentMatches_ = matches;
    +};
    +
    +
    +/**
    + * Adds the specified rows to the cache.
    + * @param {!Array<!Object>} rows .
    + * @private
    + */
    +goog.ui.ac.CachingMatcher.prototype.addRows_ = function(rows) {
    +  goog.array.forEach(rows, function(row) {
    +    // The ' ' prefix is to avoid colliding with builtins like toString.
    +    if (!this.rowStrings_[' ' + row]) {
    +      this.rows_.push(row);
    +      this.rowStrings_[' ' + row] = true;
    +    }
    +  }, this);
    +};
    +
    +
    +/**
    + * Checks if the cache is larger than the maximum cache size. If so clears it.
    + * @private
    + */
    +goog.ui.ac.CachingMatcher.prototype.clearCacheIfTooLarge_ = function() {
    +  if (this.rows_.length > this.maxCacheSize_) {
    +    this.rows_ = [];
    +    this.rowStrings_ = {};
    +  }
    +};
    +
    +
    +/**
    + * Triggers a match request against the base matcher. This function is
    + * unthrottled, so don't call it directly; instead use
    + * this.throttledTriggerBaseMatch_.
    + * @private
    + */
    +goog.ui.ac.CachingMatcher.prototype.triggerBaseMatch_ = function() {
    +  this.baseMatcher_.requestMatchingRows(this.mostRecentToken_,
    +      this.baseMatcherMaxMatches_, goog.bind(this.onBaseMatch_, this));
    +};
    +
    +
    +/**
    + * Handles a match response from the base matcher.
    + * @param {string} token The token against which the base match was called.
    + * @param {!Array<!Object>} matches The matches returned by the base matcher.
    + * @private
    + */
    +goog.ui.ac.CachingMatcher.prototype.onBaseMatch_ = function(token, matches) {
    +  // NOTE(reinerp): The user might have typed some more characters since the
    +  // base matcher request was sent out, which manifests in that token might be
    +  // older than this.mostRecentToken_. We make sure to do our local matches
    +  // using this.mostRecentToken_ rather than token so that we display results
    +  // relevant to what the user is seeing right now.
    +
    +  // NOTE(reinerp): We compute a diff between the currently displayed results
    +  // and the new results we would get now that the server results have come
    +  // back. Using this diff, we make sure the new results are only added to the
    +  // end of the list of results. See the documentation on
    +  // this.mostRecentMatches_ for details
    +
    +  this.addRows_(matches);
    +
    +  var oldMatchesSet = {};
    +  goog.array.forEach(this.mostRecentMatches_, function(match) {
    +    // The ' ' prefix is to avoid colliding with builtins like toString.
    +    oldMatchesSet[' ' + match] = true;
    +  });
    +  var newMatches = this.getMatchesForRows_(this.mostRecentToken_,
    +      this.mostRecentMaxMatches_, this.rows_);
    +  newMatches = goog.array.filter(newMatches, function(match) {
    +    return !(oldMatchesSet[' ' + match]);
    +  });
    +  newMatches = this.mostRecentMatches_.concat(newMatches)
    +      .slice(0, this.mostRecentMaxMatches_);
    +
    +  this.mostRecentMatches_ = newMatches;
    +
    +  // We've gone to the effort of keeping the existing rows as before, so let's
    +  // make sure to keep them highlighted.
    +  var options = new goog.ui.ac.RenderOptions();
    +  options.setPreserveHilited(true);
    +  this.mostRecentMatchHandler_(this.mostRecentToken_, newMatches, options);
    +
    +  // We clear the cache *after* running the local match, so we don't
    +  // suddenly remove results just because the remote match came back.
    +  this.clearCacheIfTooLarge_();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.html
    new file mode 100644
    index 0000000000..1f78421f06
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.ArrayMatcher
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ac.CachingMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.js
    new file mode 100644
    index 0000000000..9d4344ae21
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/cachingmatcher_test.js
    @@ -0,0 +1,214 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.CachingMatcherTest');
    +goog.setTestOnly('goog.ui.ac.CachingMatcherTest');
    +
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.ui.ac.CachingMatcher');
    +ignoreArgument = goog.testing.mockmatchers.ignoreArgument;
    +
    +
    +
    +/**
    + * Fake version of Throttle which only fires when we call permitOne().
    + * @constructor
    + * @suppress {missingProvide}
    + */
    +goog.async.Throttle = function(fn, time, self) {
    +  this.fn = fn;
    +  this.time = time;
    +  this.self = self;
    +  this.numFires = 0;
    +};
    +
    +
    +/** @suppress {missingProvide} */
    +goog.async.Throttle.prototype.fire = function() {
    +  this.numFires++;
    +};
    +
    +
    +/** @suppress {missingProvide} */
    +goog.async.Throttle.prototype.permitOne = function() {
    +  if (this.numFires == 0) {
    +    return;
    +  }
    +  this.fn.call(this.self);
    +  this.numFires = 0;
    +};
    +
    +// Actual tests.
    +var mockControl;
    +var mockMatcher;
    +var mockHandler;
    +var matcher;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  mockMatcher = {
    +    requestMatchingRows: mockControl.createFunctionMock('requestMatchingRows')
    +  };
    +  mockHandler = mockControl.createFunctionMock('matchHandler');
    +  matcher = new goog.ui.ac.CachingMatcher(mockMatcher);
    +}
    +
    +function tearDown() {
    +  mockControl.$tearDown();
    +}
    +
    +function testLocalThenRemoteMatch() {
    +  // We immediately get the local match.
    +  mockHandler('foo', []);
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('foo', 12, mockHandler);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // Now we run the remote match.
    +  mockHandler('foo', ['foo1', 'foo2'], ignoreArgument);
    +  mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('foo', ['foo1', 'foo2', 'bar3']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testCacheSize() {
    +  matcher.setMaxCacheSize(4);
    +
    +  // First we populate, but not overflow the cache.
    +  mockHandler('foo', []);
    +  mockHandler('foo', ['foo111', 'foo222'], ignoreArgument);
    +  mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('foo', ['foo111', 'foo222', 'bar333']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('foo', 12, mockHandler);
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // Now we verify the cache is populated.
    +  mockHandler('foo1', ['foo111']);
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('foo1', 12, mockHandler);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // Now we overflow the cache. Check that the remote results show the first
    +  // time we get them back, even though they overflow the cache.
    +  mockHandler('foo11', ['foo111']);
    +  mockHandler('foo11', ['foo111', 'foo112', 'foo113', 'foo114'],
    +              ignoreArgument);
    +  mockMatcher.requestMatchingRows('foo11', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('foo11', ['foo111', 'foo112', 'foo113', 'foo114']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('foo11', 12, mockHandler);
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // Now check that the cache is empty.
    +  mockHandler('foo11', []);
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('foo11', 12, mockHandler);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testSimilarMatchingDoesntReorderResults() {
    +  // Populate the cache. We get two prefix matches.
    +  mockHandler('ba', []);
    +  mockHandler('ba', ['bar', 'baz', 'bam'], ignoreArgument);
    +  mockMatcher.requestMatchingRows('ba', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('ba', ['bar', 'baz', 'bam']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('ba', 12, mockHandler);
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // The user types another character. The local match gives us two similar
    +  // matches, but no prefix matches. The remote match returns a prefix match,
    +  // which would normally be ranked above the similar matches, but gets ranked
    +  // below the similar matches because the user hasn't typed any more
    +  // characters.
    +  mockHandler('bad', ['bar', 'baz', 'bam']);
    +  mockHandler('bad', ['bar', 'baz', 'bam', 'bad', 'badder', 'baddest'],
    +              ignoreArgument);
    +  mockMatcher.requestMatchingRows('bad', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('bad', ['bad', 'badder', 'baddest']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('bad', 12, mockHandler);
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +
    +  // The user types yet another character, which allows the prefix matches to
    +  // jump to the top of the list of suggestions.
    +  mockHandler('badd', ['badder', 'baddest']);
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows('badd', 12, mockHandler);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testSetThrottleTime() {
    +  assertEquals(150, matcher.throttledTriggerBaseMatch_.time);
    +  matcher.setThrottleTime(234);
    +  assertEquals(234, matcher.throttledTriggerBaseMatch_.time);
    +}
    +
    +function testSetBaseMatcherMaxMatches() {
    +  mockHandler('foo', []); // Local match
    +  mockMatcher.requestMatchingRows('foo', 789, ignoreArgument);
    +  mockControl.$replayAll();
    +  matcher.setBaseMatcherMaxMatches();
    +  matcher.requestMatchingRows('foo', 12, mockHandler);
    +}
    +
    +function testSetLocalMatcher() {
    +  // Use a local matcher which just sorts all the rows alphabetically.
    +  function sillyMatcher(token, maxMatches, rows) {
    +    rows = rows.concat([]);
    +    rows.sort();
    +    return rows;
    +  }
    +
    +  mockHandler('foo', []);
    +  mockHandler('foo', ['a', 'b', 'c'], ignoreArgument);
    +  mockMatcher.requestMatchingRows('foo', 100, ignoreArgument)
    +      .$does(function(token, maxResults, matchHandler) {
    +        matchHandler('foo', ['b', 'a', 'c']);
    +      });
    +  mockControl.$replayAll();
    +  matcher.setLocalMatcher(sillyMatcher);
    +  matcher.requestMatchingRows('foo', 12, mockHandler);
    +  matcher.throttledTriggerBaseMatch_.permitOne();
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler.js b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler.js
    new file mode 100644
    index 0000000000..bedc799a32
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler.js
    @@ -0,0 +1,1327 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for managing the interactions between an
    + * auto-complete object and a text-input or textarea.
    + *
    + * IME note:
    + *
    + * We used to suspend autocomplete while there are IME preedit characters, but
    + * now for parity with Search we do not. We still detect the beginning and end
    + * of IME entry because we need to listen to more events while an IME commit is
    + * happening, but we update continuously as the user types.
    + *
    + * IMEs vary across operating systems, browsers, and even input languages. This
    + * class tries to handle IME for:
    + * - Windows x {FF3, IE7, Chrome} x MS IME 2002 (Japanese)
    + * - Mac     x {FF3, Safari3}     x Kotoeri (Japanese)
    + * - Linux   x {FF3}              x UIM + Anthy (Japanese)
    + *
    + * TODO(user): We cannot handle {Mac, Linux} x FF3 correctly.
    + * TODO(user): We need to support Windows x Google IME.
    + *
    + * This class was tested with hiragana input. The event sequence when inputting
    + * 'ai<enter>' with IME on (which commits two characters) is as follows:
    + *
    + * Notation: [key down code, key press, key up code]
    + *           key code or +: event fired
    + *           -: event not fired
    + *
    + * - Win/FF3: [WIN_IME, +, A], [-, -, ENTER]
    + *            Note: No events are fired for 'i'.
    + *
    + * - Win/IE7: [WIN_IME, -, A], [WIN_IME, -, I], [WIN_IME, -, ENTER]
    + *
    + * - Win/Chrome: Same as Win/IE7
    + *
    + * - Mac/FF3: [A, -, A], [I, -, I], [ENTER, -, ENTER]
    + *
    + * - Mac/Safari3: Same as Win/IE7
    + *
    + * - Linux/FF3: No events are generated.
    + *
    + * With IME off,
    + *
    + * - ALL: [A, +, A], [I, +, I], [ENTER, +, ENTER]
    + *        Note: Key code of key press event varies across configuration.
    + *
    + * With Microsoft Pinyin IME 3.0 (Simplified Chinese),
    + *
    + * - Win/IE7: Same as Win/IE7 with MS IME 2002 (Japanese)
    + *
    + *   The issue with this IME is that the key sequence that ends preedit is not
    + *   a single ENTER key up.
    + *   - ENTER key up following either ENTER or SPACE ends preedit.
    + *   - SPACE key up following even number of LEFT, RIGHT, or SPACE (any
    + *     combination) ends preedit.
    + *   TODO(user): We only support SPACE-then-ENTER sequence.
    + *   TODO(mpd): With the change to autocomplete during IME, this might not be an
    + *   issue. Remove this comment once tested.
    + *
    + * With Microsoft Korean IME 2002,
    + *
    + * - Win/IE7: Same as Win/IE7 with MS IME 2002 (Japanese), but there is no
    + *   sequence that ends the preedit.
    + *
    + * The following is the algorithm we use to detect IME preedit:
    + *
    + * - WIN_IME key down starts predit.
    + * - (1) ENTER key up or (2) CTRL-M key up ends preedit.
    + * - Any key press not immediately following WIN_IME key down signifies that
    + *   preedit has ended.
    + *
    + * If you need to change this algorithm, please note the OS, browser, language,
    + * and behavior above so that we can avoid regressions. Contact mpd or yuzo
    + * if you have questions or concerns.
    + *
    + */
    +
    +
    +goog.provide('goog.ui.ac.InputHandler');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.dom');
    +goog.require('goog.dom.selection');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +
    +
    +/**
    + * Class for managing the interaction between an auto-complete object and a
    + * text-input or textarea.
    + *
    + * @param {?string=} opt_separators Separators to split multiple entries.
    + *     If none passed, uses ',' and ';'.
    + * @param {?string=} opt_literals Characters used to delimit text literals.
    + * @param {?boolean=} opt_multi Whether to allow multiple entries
    + *     (Default: true).
    + * @param {?number=} opt_throttleTime Number of milliseconds to throttle
    + *     keyevents with (Default: 150). Use -1 to disable updates on typing. Note
    + *     that typing the separator will update autocomplete suggestions.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.ui.ac.InputHandler = function(opt_separators, opt_literals,
    +    opt_multi, opt_throttleTime) {
    +  goog.Disposable.call(this);
    +  var throttleTime = opt_throttleTime || 150;
    +
    +  /**
    +   * Whether this input accepts multiple values
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.multi_ = opt_multi != null ? opt_multi : true;
    +
    +  // Set separators depends on this.multi_ being set correctly
    +  this.setSeparators(opt_separators ||
    +      goog.ui.ac.InputHandler.STANDARD_LIST_SEPARATORS);
    +
    +  /**
    +   * Characters that are used to delimit literal text. Separarator characters
    +   * found within literal text are not processed as separators
    +   * @type {string}
    +   * @private
    +   */
    +  this.literals_ = opt_literals || '';
    +
    +  /**
    +   * Whether to prevent highlighted item selection when tab is pressed.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.preventSelectionOnTab_ = false;
    +
    +  /**
    +   * Whether to prevent the default behavior (moving focus to another element)
    +   * when tab is pressed.  This occurs by default only for multi-value mode.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.preventDefaultOnTab_ = this.multi_;
    +
    +  /**
    +   * A timer object used to monitor for changes when an element is active.
    +   *
    +   * TODO(user): Consider tuning the throttle time, so that it takes into
    +   * account the length of the token.  When the token is short it is likely to
    +   * match lots of rows, therefore we want to check less frequently.  Even
    +   * something as simple as <3-chars = 150ms, then 100ms otherwise.
    +   *
    +   * @type {goog.Timer}
    +   * @private
    +   */
    +  this.timer_ = throttleTime > 0 ? new goog.Timer(throttleTime) : null;
    +
    +  /**
    +   * Event handler used by the input handler to manage events.
    +   * @type {goog.events.EventHandler<!goog.ui.ac.InputHandler>}
    +   * @private
    +   */
    +  this.eh_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Event handler to help us find an input element that already has the focus.
    +   * @type {goog.events.EventHandler<!goog.ui.ac.InputHandler>}
    +   * @private
    +   */
    +  this.activateHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The keyhandler used for listening on most key events.  This takes care of
    +   * abstracting away some of the browser differences.
    +   * @type {goog.events.KeyHandler}
    +   * @private
    +   */
    +  this.keyHandler_ = new goog.events.KeyHandler();
    +
    +  /**
    +   * The last key down key code.
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastKeyCode_ = -1;  // Initialize to a non-existent value.
    +};
    +goog.inherits(goog.ui.ac.InputHandler, goog.Disposable);
    +
    +
    +/**
    + * Whether or not we need to pause the execution of the blur handler in order
    + * to allow the execution of the selection handler to run first. This is
    + * currently true when running on IOS version prior to 4.2, since we need
    + * some special logic for these devices to handle bug 4484488.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.REQUIRES_ASYNC_BLUR_ =
    +    (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) &&
    +        // Check the webkit version against the version for iOS 4.2.1.
    +        !goog.userAgent.isVersionOrHigher('533.17.9');
    +
    +
    +/**
    + * Standard list separators.
    + * @type {string}
    + * @const
    + */
    +goog.ui.ac.InputHandler.STANDARD_LIST_SEPARATORS = ',;';
    +
    +
    +/**
    + * Literals for quotes.
    + * @type {string}
    + * @const
    + */
    +goog.ui.ac.InputHandler.QUOTE_LITERALS = '"';
    +
    +
    +/**
    + * The AutoComplete instance this inputhandler is associated with.
    + * @type {goog.ui.ac.AutoComplete}
    + */
    +goog.ui.ac.InputHandler.prototype.ac_;
    +
    +
    +/**
    + * Characters that can be used to split multiple entries in an input string
    + * @type {string}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.separators_;
    +
    +
    +/**
    + * The separator we use to reconstruct the string
    + * @type {string}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.defaultSeparator_;
    +
    +
    +/**
    + * Regular expression used from trimming tokens or null for no trimming.
    + * @type {RegExp}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.trimmer_;
    +
    +
    +/**
    + * Regular expression to test whether a separator exists
    + * @type {RegExp}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.separatorCheck_;
    +
    +
    +/**
    + * Should auto-completed tokens be wrapped in whitespace?  Used in selectRow.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.whitespaceWrapEntries_ = true;
    +
    +
    +/**
    + * Should the occurrence of a literal indicate a token boundary?
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.generateNewTokenOnLiteral_ = true;
    +
    +
    +/**
    + * Whether to flip the orientation of up & down for hiliting next
    + * and previous autocomplete entries.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.upsideDown_ = false;
    +
    +
    +/**
    + * If we're in 'multi' mode, does typing a separator force the updating of
    + * suggestions?
    + * For example, if somebody finishes typing "obama, hillary,", should the last
    + * comma trigger updating suggestions in a guaranteed manner? Especially useful
    + * when the suggestions depend on complete keywords. Note that "obama, hill"
    + * (a leading sub-string of "obama, hillary" will lead to different and possibly
    + * irrelevant suggestions.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.separatorUpdates_ = true;
    +
    +
    +/**
    + * If we're in 'multi' mode, does typing a separator force the current term to
    + * autocomplete?
    + * For example, if 'tomato' is a suggested completion and the user has typed
    + * 'to,', do we autocomplete to turn that into 'tomato,'?
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.separatorSelects_ = true;
    +
    +
    +/**
    + * The id of the currently active timeout, so it can be cleared if required.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.activeTimeoutId_ = null;
    +
    +
    +/**
    + * The element that is currently active.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.activeElement_ = null;
    +
    +
    +/**
    + * The previous value of the active element.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.lastValue_ = '';
    +
    +
    +/**
    + * Flag used to indicate that the IME key has been seen and we need to wait for
    + * the up event.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.waitingForIme_ = false;
    +
    +
    +/**
    + * Flag used to indicate that the user just selected a row and we should
    + * therefore ignore the change of the input value.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.rowJustSelected_ = false;
    +
    +
    +/**
    + * Flag indicating whether the result list should be updated continuously
    + * during typing or only after a short pause.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.updateDuringTyping_ = true;
    +
    +
    +/**
    + * Attach an instance of an AutoComplete
    + * @param {goog.ui.ac.AutoComplete} ac Autocomplete object.
    + */
    +goog.ui.ac.InputHandler.prototype.attachAutoComplete = function(ac) {
    +  this.ac_ = ac;
    +};
    +
    +
    +/**
    + * Returns the associated autocomplete instance.
    + * @return {goog.ui.ac.AutoComplete} The associated autocomplete instance.
    + */
    +goog.ui.ac.InputHandler.prototype.getAutoComplete = function() {
    +  return this.ac_;
    +};
    +
    +
    +/**
    + * Returns the current active element.
    + * @return {Element} The currently active element.
    + */
    +goog.ui.ac.InputHandler.prototype.getActiveElement = function() {
    +  return this.activeElement_;
    +};
    +
    +
    +/**
    + * Returns the value of the current active element.
    + * @return {string} The value of the current active element.
    + */
    +goog.ui.ac.InputHandler.prototype.getValue = function() {
    +  return this.activeElement_.value;
    +};
    +
    +
    +/**
    + * Sets the value of the current active element.
    + * @param {string} value The new value.
    + */
    +goog.ui.ac.InputHandler.prototype.setValue = function(value) {
    +  this.activeElement_.value = value;
    +};
    +
    +
    +/**
    + * Returns the current cursor position.
    + * @return {number} The index of the cursor position.
    + */
    +goog.ui.ac.InputHandler.prototype.getCursorPosition = function() {
    +  return goog.dom.selection.getStart(this.activeElement_);
    +};
    +
    +
    +/**
    + * Sets the cursor at the given position.
    + * @param {number} pos The index of the cursor position.
    + */
    +goog.ui.ac.InputHandler.prototype.setCursorPosition = function(pos) {
    +  goog.dom.selection.setStart(this.activeElement_, pos);
    +  goog.dom.selection.setEnd(this.activeElement_, pos);
    +};
    +
    +
    +/**
    + * Attaches the input handler to a target element. The target element
    + * should be a textarea, input box, or other focusable element with the
    + * same interface.
    + * @param {Element|goog.events.EventTarget} target An element to attach the
    + *     input handler too.
    + */
    +goog.ui.ac.InputHandler.prototype.attachInput = function(target) {
    +  if (goog.dom.isElement(target)) {
    +    var el = /** @type {!Element} */ (target);
    +    goog.a11y.aria.setState(el, 'haspopup', true);
    +  }
    +
    +  this.eh_.listen(target, goog.events.EventType.FOCUS, this.handleFocus);
    +  this.eh_.listen(target, goog.events.EventType.BLUR, this.handleBlur);
    +
    +  if (!this.activeElement_) {
    +    this.activateHandler_.listen(
    +        target, goog.events.EventType.KEYDOWN,
    +        this.onKeyDownOnInactiveElement_);
    +
    +    // Don't wait for a focus event if the element already has focus.
    +    if (goog.dom.isElement(target)) {
    +      var ownerDocument = goog.dom.getOwnerDocument(
    +          /** @type {Element} */ (target));
    +      if (goog.dom.getActiveElement(ownerDocument) == target) {
    +        this.processFocus(/** @type {!Element} */ (target));
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Detaches the input handler from the provided element.
    + * @param {Element|goog.events.EventTarget} target An element to detach the
    + *     input handler from.
    + */
    +goog.ui.ac.InputHandler.prototype.detachInput = function(target) {
    +  if (target == this.activeElement_) {
    +    this.handleBlur();
    +  }
    +  this.eh_.unlisten(target, goog.events.EventType.FOCUS, this.handleFocus);
    +  this.eh_.unlisten(target, goog.events.EventType.BLUR, this.handleBlur);
    +
    +  if (!this.activeElement_) {
    +    this.activateHandler_.unlisten(
    +        target, goog.events.EventType.KEYDOWN,
    +        this.onKeyDownOnInactiveElement_);
    +  }
    +};
    +
    +
    +/**
    + * Attaches the input handler to multiple elements.
    + * @param {...Element} var_args Elements to attach the input handler too.
    + */
    +goog.ui.ac.InputHandler.prototype.attachInputs = function(var_args) {
    +  for (var i = 0; i < arguments.length; i++) {
    +    this.attachInput(arguments[i]);
    +  }
    +};
    +
    +
    +/**
    + * Detaches the input handler from multuple elements.
    + * @param {...Element} var_args Variable arguments for elements to unbind from.
    + */
    +goog.ui.ac.InputHandler.prototype.detachInputs = function(var_args) {
    +  for (var i = 0; i < arguments.length; i++) {
    +    this.detachInput(arguments[i]);
    +  }
    +};
    +
    +
    +/**
    + * Selects the given row.  Implements the SelectionHandler interface.
    + * @param {Object} row The row to select.
    + * @param {boolean=} opt_multi Should this be treated as a single or multi-token
    + *     auto-complete?  Overrides previous setting of opt_multi on constructor.
    + * @return {boolean} Whether to suppress the update event.
    + */
    +goog.ui.ac.InputHandler.prototype.selectRow = function(row, opt_multi) {
    +  if (this.activeElement_) {
    +    this.setTokenText(row.toString(), opt_multi);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Sets the text of the current token without updating the autocomplete
    + * choices.
    + * @param {string} tokenText The text for the current token.
    + * @param {boolean=} opt_multi Should this be treated as a single or multi-token
    + *     auto-complete?  Overrides previous setting of opt_multi on constructor.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.setTokenText =
    +    function(tokenText, opt_multi) {
    +  if (goog.isDef(opt_multi) ? opt_multi : this.multi_) {
    +    var index = this.getTokenIndex_(this.getValue(), this.getCursorPosition());
    +
    +    // Break up the current input string.
    +    var entries = this.splitInput_(this.getValue());
    +
    +    // Get the new value, ignoring whitespace associated with the entry.
    +    var replaceValue = tokenText;
    +
    +    // Only add punctuation if there isn't already a separator available.
    +    if (!this.separatorCheck_.test(replaceValue)) {
    +      replaceValue = goog.string.trimRight(replaceValue) +
    +                     this.defaultSeparator_;
    +    }
    +
    +    // Ensure there's whitespace wrapping the entries, if whitespaceWrapEntries_
    +    // has been set to true.
    +    if (this.whitespaceWrapEntries_) {
    +      if (index != 0 && !goog.string.isEmptyOrWhitespace(entries[index - 1])) {
    +        replaceValue = ' ' + replaceValue;
    +      }
    +      // Add a space only if it's the last token; otherwise, we assume the
    +      // next token already has the proper spacing.
    +      if (index == entries.length - 1) {
    +        replaceValue = replaceValue + ' ';
    +      }
    +    }
    +
    +    // If the token needs changing, then update the input box and move the
    +    // cursor to the correct position.
    +    if (replaceValue != entries[index]) {
    +
    +      // Replace the value in the array.
    +      entries[index] = replaceValue;
    +
    +      var el = this.activeElement_;
    +      // If there is an uncommitted IME in Firefox or IE 9, setting the value
    +      // fails and results in actually clearing the value that's already in the
    +      // input.
    +      // The FF bug is http://bugzilla.mozilla.org/show_bug.cgi?id=549674
    +      // Blurring before setting the value works around this problem. We'd like
    +      // to do this only if there is an uncommitted IME, but this isn't possible
    +      // to detect. Since text editing is finicky we restrict this
    +      // workaround to Firefox and IE 9 where it's necessary.
    +      if (goog.userAgent.GECKO ||
    +          (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9'))) {
    +        el.blur();
    +      }
    +      // Join the array and replace the contents of the input.
    +      el.value = entries.join('');
    +
    +      // Calculate which position to put the cursor at.
    +      var pos = 0;
    +      for (var i = 0; i <= index; i++) {
    +        pos += entries[i].length;
    +      }
    +
    +      // Set the cursor.
    +      el.focus();
    +      this.setCursorPosition(pos);
    +    }
    +  } else {
    +    this.setValue(tokenText);
    +  }
    +
    +  // Avoid triggering an autocomplete just because the value changed.
    +  this.rowJustSelected_ = true;
    +};
    +
    +
    +/** @override */
    +goog.ui.ac.InputHandler.prototype.disposeInternal = function() {
    +  goog.ui.ac.InputHandler.superClass_.disposeInternal.call(this);
    +  if (this.activeTimeoutId_ != null) {
    +    // Need to check against null explicitly because 0 is a valid value.
    +    window.clearTimeout(this.activeTimeoutId_);
    +  }
    +  this.eh_.dispose();
    +  delete this.eh_;
    +  this.activateHandler_.dispose();
    +  this.keyHandler_.dispose();
    +  goog.dispose(this.timer_);
    +};
    +
    +
    +/**
    + * Sets the entry separator characters.
    + *
    + * @param {string} separators The separator characters to set.
    + * @param {string=} opt_defaultSeparators The defaultSeparator character to set.
    + */
    +goog.ui.ac.InputHandler.prototype.setSeparators =
    +    function(separators, opt_defaultSeparators) {
    +  this.separators_ = separators;
    +  this.defaultSeparator_ =
    +      goog.isDefAndNotNull(opt_defaultSeparators) ?
    +      opt_defaultSeparators : this.separators_.substring(0, 1);
    +
    +  var wspaceExp = this.multi_ ? '[\\s' + this.separators_ + ']+' : '[\\s]+';
    +
    +  this.trimmer_ = new RegExp('^' + wspaceExp + '|' + wspaceExp + '$', 'g');
    +  this.separatorCheck_ = new RegExp('\\s*[' + this.separators_ + ']$');
    +};
    +
    +
    +/**
    + * Sets whether to flip the orientation of up & down for hiliting next
    + * and previous autocomplete entries.
    + * @param {boolean} upsideDown Whether the orientation is upside down.
    + */
    +goog.ui.ac.InputHandler.prototype.setUpsideDown = function(upsideDown) {
    +  this.upsideDown_ = upsideDown;
    +};
    +
    +
    +/**
    + * Sets whether auto-completed tokens should be wrapped with whitespace.
    + * @param {boolean} newValue boolean value indicating whether or not
    + *     auto-completed tokens should be wrapped with whitespace.
    + */
    +goog.ui.ac.InputHandler.prototype.setWhitespaceWrapEntries =
    +    function(newValue) {
    +  this.whitespaceWrapEntries_ = newValue;
    +};
    +
    +
    +/**
    + * Sets whether new tokens should be generated from literals.  That is, should
    + * hello'world be two tokens, assuming ' is a literal?
    + * @param {boolean} newValue boolean value indicating whether or not
    + * new tokens should be generated from literals.
    + */
    +goog.ui.ac.InputHandler.prototype.setGenerateNewTokenOnLiteral =
    +    function(newValue) {
    +  this.generateNewTokenOnLiteral_ = newValue;
    +};
    +
    +
    +/**
    + * Sets the regular expression used to trim the tokens before passing them to
    + * the matcher:  every substring that matches the given regular expression will
    + * be removed.  This can also be set to null to disable trimming.
    + * @param {RegExp} trimmer Regexp to use for trimming or null to disable it.
    + */
    +goog.ui.ac.InputHandler.prototype.setTrimmingRegExp = function(trimmer) {
    +  this.trimmer_ = trimmer;
    +};
    +
    +
    +/**
    + * Sets whether we will prevent the default input behavior (moving focus to the
    + * next focusable  element) on TAB.
    + * @param {boolean} newValue Whether to preventDefault on TAB.
    + */
    +goog.ui.ac.InputHandler.prototype.setPreventDefaultOnTab = function(newValue) {
    +  this.preventDefaultOnTab_ = newValue;
    +};
    +
    +
    +/**
    + * Sets whether we will prevent highlighted item selection on TAB.
    + * @param {boolean} newValue Whether to prevent selection on TAB.
    + */
    +goog.ui.ac.InputHandler.prototype.setPreventSelectionOnTab =
    +    function(newValue) {
    +  this.preventSelectionOnTab_ = newValue;
    +};
    +
    +
    +/**
    + * Sets whether separators perform autocomplete.
    + * @param {boolean} newValue Whether to autocomplete on separators.
    + */
    +goog.ui.ac.InputHandler.prototype.setSeparatorCompletes = function(newValue) {
    +  this.separatorUpdates_ = newValue;
    +  this.separatorSelects_ = newValue;
    +};
    +
    +
    +/**
    + * Sets whether separators perform autocomplete.
    + * @param {boolean} newValue Whether to autocomplete on separators.
    + */
    +goog.ui.ac.InputHandler.prototype.setSeparatorSelects = function(newValue) {
    +  this.separatorSelects_ = newValue;
    +};
    +
    +
    +/**
    + * Gets the time to wait before updating the results. If the update during
    + * typing flag is switched on, this delay counts from the last update,
    + * otherwise from the last keypress.
    + * @return {number} Throttle time in milliseconds.
    + */
    +goog.ui.ac.InputHandler.prototype.getThrottleTime = function() {
    +  return this.timer_ ? this.timer_.getInterval() : -1;
    +};
    +
    +
    +/**
    + * Sets whether a row has just been selected.
    + * @param {boolean} justSelected Whether or not the row has just been selected.
    + */
    +goog.ui.ac.InputHandler.prototype.setRowJustSelected = function(justSelected) {
    +  this.rowJustSelected_ = justSelected;
    +};
    +
    +
    +/**
    + * Sets the time to wait before updating the results.
    + * @param {number} time New throttle time in milliseconds.
    + */
    +goog.ui.ac.InputHandler.prototype.setThrottleTime = function(time) {
    +  if (time < 0) {
    +    this.timer_.dispose();
    +    this.timer_ = null;
    +    return;
    +  }
    +  if (this.timer_) {
    +    this.timer_.setInterval(time);
    +  } else {
    +    this.timer_ = new goog.Timer(time);
    +  }
    +};
    +
    +
    +/**
    + * Gets whether the result list is updated during typing.
    + * @return {boolean} Value of the flag.
    + */
    +goog.ui.ac.InputHandler.prototype.getUpdateDuringTyping = function() {
    +  return this.updateDuringTyping_;
    +};
    +
    +
    +/**
    + * Sets whether the result list should be updated during typing.
    + * @param {boolean} value New value of the flag.
    + */
    +goog.ui.ac.InputHandler.prototype.setUpdateDuringTyping = function(value) {
    +  this.updateDuringTyping_ = value;
    +};
    +
    +
    +/**
    + * Handles a key event.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @return {boolean} True if the key event was handled.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.handleKeyEvent = function(e) {
    +  switch (e.keyCode) {
    +
    +    // If the menu is open and 'down' caused a change then prevent the default
    +    // action and prevent scrolling.  If the box isn't a multi autocomplete
    +    // and the menu isn't open, we force it open now.
    +    case goog.events.KeyCodes.DOWN:
    +      if (this.ac_.isOpen()) {
    +        this.moveDown_();
    +        e.preventDefault();
    +        return true;
    +
    +      } else if (!this.multi_) {
    +        this.update(true);
    +        e.preventDefault();
    +        return true;
    +      }
    +      break;
    +
    +    // If the menu is open and 'up' caused a change then prevent the default
    +    // action and prevent scrolling.
    +    case goog.events.KeyCodes.UP:
    +      if (this.ac_.isOpen()) {
    +        this.moveUp_();
    +        e.preventDefault();
    +        return true;
    +      }
    +      break;
    +
    +    // If tab key is pressed, select the current highlighted item.  The default
    +    // action is also prevented if the input is a multi input, to prevent the
    +    // user tabbing out of the field.
    +    case goog.events.KeyCodes.TAB:
    +      if (this.ac_.isOpen() && !e.shiftKey && !this.preventSelectionOnTab_) {
    +        // Ensure the menu is up to date before completing.
    +        this.update();
    +        if (this.ac_.selectHilited() && this.preventDefaultOnTab_) {
    +          e.preventDefault();
    +          return true;
    +        }
    +      } else {
    +        this.ac_.dismiss();
    +      }
    +      break;
    +
    +    // On enter, just select the highlighted row.
    +    case goog.events.KeyCodes.ENTER:
    +      if (this.ac_.isOpen()) {
    +        // Ensure the menu is up to date before completing.
    +        this.update();
    +        if (this.ac_.selectHilited()) {
    +          e.preventDefault();
    +          e.stopPropagation();
    +          return true;
    +        }
    +      } else {
    +        this.ac_.dismiss();
    +      }
    +      break;
    +
    +    // On escape tell the autocomplete to dismiss.
    +    case goog.events.KeyCodes.ESC:
    +      if (this.ac_.isOpen()) {
    +        this.ac_.dismiss();
    +        e.preventDefault();
    +        e.stopPropagation();
    +        return true;
    +      }
    +      break;
    +
    +    // The IME keycode indicates an IME sequence has started, we ignore all
    +    // changes until we get an enter key-up.
    +    case goog.events.KeyCodes.WIN_IME:
    +      if (!this.waitingForIme_) {
    +        this.startWaitingForIme_();
    +        return true;
    +      }
    +      break;
    +
    +    default:
    +      if (this.timer_ && !this.updateDuringTyping_) {
    +        // Waits throttle time before sending the request again.
    +        this.timer_.stop();
    +        this.timer_.start();
    +      }
    +  }
    +
    +  return this.handleSeparator_(e);
    +};
    +
    +
    +/**
    + * Handles a key event for a separator key.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @return {boolean} True if the key event was handled.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.handleSeparator_ = function(e) {
    +  var isSeparatorKey = this.multi_ && e.charCode &&
    +      this.separators_.indexOf(String.fromCharCode(e.charCode)) != -1;
    +  if (this.separatorUpdates_ && isSeparatorKey) {
    +    this.update();
    +  }
    +  if (this.separatorSelects_ && isSeparatorKey) {
    +    if (this.ac_.selectHilited()) {
    +      e.preventDefault();
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this inputhandler need to listen on key-up.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.needKeyUpListener = function() {
    +  return false;
    +};
    +
    +
    +/**
    + * Handles the key up event. Registered only if needKeyUpListener returns true.
    + * @param {goog.events.Event} e The keyup event.
    + * @return {boolean} Whether an action was taken or not.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.handleKeyUp = function(e) {
    +  return false;
    +};
    +
    +
    +/**
    + * Adds the necessary input event handlers.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.addEventHandlers_ = function() {
    +  this.keyHandler_.attach(this.activeElement_);
    +  this.eh_.listen(
    +      this.keyHandler_, goog.events.KeyHandler.EventType.KEY, this.onKey_);
    +  if (this.needKeyUpListener()) {
    +    this.eh_.listen(this.activeElement_,
    +        goog.events.EventType.KEYUP, this.handleKeyUp);
    +  }
    +  this.eh_.listen(this.activeElement_,
    +      goog.events.EventType.MOUSEDOWN, this.onMouseDown_);
    +
    +  // IE also needs a keypress to check if the user typed a separator
    +  if (goog.userAgent.IE) {
    +    this.eh_.listen(this.activeElement_,
    +        goog.events.EventType.KEYPRESS, this.onIeKeyPress_);
    +  }
    +};
    +
    +
    +/**
    + * Removes the necessary input event handlers.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.removeEventHandlers_ = function() {
    +  this.eh_.unlisten(
    +      this.keyHandler_, goog.events.KeyHandler.EventType.KEY, this.onKey_);
    +  this.keyHandler_.detach();
    +  this.eh_.unlisten(this.activeElement_,
    +      goog.events.EventType.KEYUP, this.handleKeyUp);
    +  this.eh_.unlisten(this.activeElement_,
    +      goog.events.EventType.MOUSEDOWN, this.onMouseDown_);
    +
    +  if (goog.userAgent.IE) {
    +    this.eh_.unlisten(this.activeElement_,
    +        goog.events.EventType.KEYPRESS, this.onIeKeyPress_);
    +  }
    +
    +  if (this.waitingForIme_) {
    +    this.stopWaitingForIme_();
    +  }
    +};
    +
    +
    +/**
    + * Handles an element getting focus.
    + * @param {goog.events.Event} e Browser event object.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.handleFocus = function(e) {
    +  this.processFocus(/** @type {Element} */ (e.target || null));
    +};
    +
    +
    +/**
    + * Registers handlers for the active element when it receives focus.
    + * @param {Element} target The element to focus.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.processFocus = function(target) {
    +  this.activateHandler_.removeAll();
    +
    +  if (this.ac_) {
    +    this.ac_.cancelDelayedDismiss();
    +  }
    +
    +  // Double-check whether the active element has actually changed.
    +  // This is a fix for Safari 3, which fires spurious focus events.
    +  if (target != this.activeElement_) {
    +    this.activeElement_ = target;
    +    if (this.timer_) {
    +      this.timer_.start();
    +      this.eh_.listen(this.timer_, goog.Timer.TICK, this.onTick_);
    +    }
    +    this.lastValue_ = this.getValue();
    +    this.addEventHandlers_();
    +  }
    +};
    +
    +
    +/**
    + * Handles an element blurring.
    + * @param {goog.events.Event=} opt_e Browser event object.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.handleBlur = function(opt_e) {
    +  // Phones running iOS prior to version 4.2.
    +  if (goog.ui.ac.InputHandler.REQUIRES_ASYNC_BLUR_) {
    +    // @bug 4484488 This is required so that the menu works correctly on
    +    // iOS prior to version 4.2. Otherwise, the blur action closes the menu
    +    // before the menu button click can be processed.
    +    // In order to fix the bug, we set a timeout to process the blur event, so
    +    // that any pending selection event can be processed first.
    +    this.activeTimeoutId_ =
    +        window.setTimeout(goog.bind(this.processBlur, this), 0);
    +    return;
    +  } else {
    +    this.processBlur();
    +  }
    +};
    +
    +
    +/**
    + * Helper function that does the logic to handle an element blurring.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.processBlur = function() {
    +  // it's possible that a blur event could fire when there's no active element,
    +  // in the case where attachInput was called on an input that already had
    +  // the focus
    +  if (this.activeElement_) {
    +    this.removeEventHandlers_();
    +    this.activeElement_ = null;
    +
    +    if (this.timer_) {
    +      this.timer_.stop();
    +      this.eh_.unlisten(this.timer_, goog.Timer.TICK, this.onTick_);
    +    }
    +
    +    if (this.ac_) {
    +      // Pause dismissal slightly to take into account any other events that
    +      // might fire on the renderer (e.g. a click will lose the focus).
    +      this.ac_.dismissOnDelay();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles the timer's tick event.  Calculates the current token, and reports
    + * any update to the autocomplete.
    + * @param {goog.events.Event} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onTick_ = function(e) {
    +  this.update();
    +};
    +
    +
    +/**
    + * Handles typing in an inactive input element. Activate it.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onKeyDownOnInactiveElement_ = function(e) {
    +  this.handleFocus(e);
    +};
    +
    +
    +/**
    + * Handles typing in the active input element.  Checks if the key is a special
    + * key and does the relevent action as appropriate.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onKey_ = function(e) {
    +  this.lastKeyCode_ = e.keyCode;
    +  if (this.ac_) {
    +    this.handleKeyEvent(e);
    +  }
    +};
    +
    +
    +/**
    + * Handles a KEYPRESS event generated by typing in the active input element.
    + * Checks if IME input is ended.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onKeyPress_ = function(e) {
    +  if (this.waitingForIme_ &&
    +      this.lastKeyCode_ != goog.events.KeyCodes.WIN_IME) {
    +    this.stopWaitingForIme_();
    +  }
    +};
    +
    +
    +/**
    + * Handles the key-up event.  This is only ever used by Mac FF or when we are in
    + * an IME entry scenario.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onKeyUp_ = function(e) {
    +  if (this.waitingForIme_ &&
    +      (e.keyCode == goog.events.KeyCodes.ENTER ||
    +       (e.keyCode == goog.events.KeyCodes.M && e.ctrlKey))) {
    +    this.stopWaitingForIme_();
    +  }
    +};
    +
    +
    +/**
    + * Handles mouse-down event.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onMouseDown_ = function(e) {
    +  if (this.ac_) {
    +    this.handleMouseDown(e);
    +  }
    +};
    +
    +
    +/**
    + * For subclasses to override to handle the mouse-down event.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.handleMouseDown = function(e) {
    +};
    +
    +
    +/**
    + * Starts waiting for IME.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.startWaitingForIme_ = function() {
    +  if (this.waitingForIme_) {
    +    return;
    +  }
    +  this.eh_.listen(
    +      this.activeElement_, goog.events.EventType.KEYUP, this.onKeyUp_);
    +  this.eh_.listen(
    +      this.activeElement_, goog.events.EventType.KEYPRESS, this.onKeyPress_);
    +  this.waitingForIme_ = true;
    +};
    +
    +
    +/**
    + * Stops waiting for IME.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.stopWaitingForIme_ = function() {
    +  if (!this.waitingForIme_) {
    +    return;
    +  }
    +  this.waitingForIme_ = false;
    +  this.eh_.unlisten(
    +      this.activeElement_, goog.events.EventType.KEYPRESS, this.onKeyPress_);
    +  this.eh_.unlisten(
    +      this.activeElement_, goog.events.EventType.KEYUP, this.onKeyUp_);
    +};
    +
    +
    +/**
    + * Handles the key-press event for IE, checking to see if the user typed a
    + * separator character.
    + * @param {goog.events.BrowserEvent} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.onIeKeyPress_ = function(e) {
    +  this.handleSeparator_(e);
    +};
    +
    +
    +/**
    + * Checks if an update has occurred and notified the autocomplete of the new
    + * token.
    + * @param {boolean=} opt_force If true the menu will be forced to update.
    + */
    +goog.ui.ac.InputHandler.prototype.update = function(opt_force) {
    +  if (this.activeElement_ &&
    +      (opt_force || this.getValue() != this.lastValue_)) {
    +    if (opt_force || !this.rowJustSelected_) {
    +      var token = this.parseToken();
    +
    +      if (this.ac_) {
    +        this.ac_.setTarget(this.activeElement_);
    +        this.ac_.setToken(token, this.getValue());
    +      }
    +    }
    +    this.lastValue_ = this.getValue();
    +  }
    +  this.rowJustSelected_ = false;
    +};
    +
    +
    +/**
    + * Parses a text area or input box for the currently highlighted token.
    + * @return {string} Token to complete.
    + * @protected
    + */
    +goog.ui.ac.InputHandler.prototype.parseToken = function() {
    +  return this.parseToken_();
    +};
    +
    +
    +/**
    + * Moves hilite up.  May hilite next or previous depending on orientation.
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.moveUp_ = function() {
    +  return this.upsideDown_ ? this.ac_.hiliteNext() : this.ac_.hilitePrev();
    +};
    +
    +
    +/**
    + * Moves hilite down.  May hilite next or previous depending on orientation.
    + * @return {boolean} True if successful.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.moveDown_ = function() {
    +  return this.upsideDown_ ? this.ac_.hilitePrev() : this.ac_.hiliteNext();
    +};
    +
    +
    +/**
    + * Parses a text area or input box for the currently highlighted token.
    + * @return {string} Token to complete.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.parseToken_ = function() {
    +  var caret = this.getCursorPosition();
    +  var text = this.getValue();
    +  return this.trim_(this.splitInput_(text)[this.getTokenIndex_(text, caret)]);
    +};
    +
    +
    +/**
    + * Trims a token of characters that we want to ignore
    + * @param {string} text string to trim.
    + * @return {string} Trimmed string.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.trim_ = function(text) {
    +  return this.trimmer_ ? String(text).replace(this.trimmer_, '') : text;
    +};
    +
    +
    +/**
    + * Gets the index of the currently highlighted token
    + * @param {string} text string to parse.
    + * @param {number} caret Position of cursor in string.
    + * @return {number} Index of token.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.getTokenIndex_ = function(text, caret) {
    +  // Split up the input string into multiple entries
    +  var entries = this.splitInput_(text);
    +
    +  // Short-circuit to select the last entry
    +  if (caret == text.length) return entries.length - 1;
    +
    +  // Calculate which of the entries the cursor is currently in
    +  var current = 0;
    +  for (var i = 0, pos = 0; i < entries.length && pos <= caret; i++) {
    +    pos += entries[i].length;
    +    current = i;
    +  }
    +
    +  // Get the token for the current item
    +  return current;
    +};
    +
    +
    +/**
    + * Splits an input string of text at the occurance of a character in
    + * {@link goog.ui.ac.InputHandler.prototype.separators_} and creates
    + * an array of tokens.  Each token may contain additional whitespace and
    + * formatting marks.  If necessary use
    + * {@link goog.ui.ac.InputHandler.prototype.trim_} to clean up the
    + * entries.
    + *
    + * @param {string} text Input text.
    + * @return {!Array<string>} Parsed array.
    + * @private
    + */
    +goog.ui.ac.InputHandler.prototype.splitInput_ = function(text) {
    +  if (!this.multi_) {
    +    return [text];
    +  }
    +
    +  var arr = String(text).split('');
    +  var parts = [];
    +  var cache = [];
    +
    +  for (var i = 0, inLiteral = false; i < arr.length; i++) {
    +    if (this.literals_ && this.literals_.indexOf(arr[i]) != -1) {
    +      if (this.generateNewTokenOnLiteral_ && !inLiteral) {
    +        parts.push(cache.join(''));
    +        cache.length = 0;
    +      }
    +      cache.push(arr[i]);
    +      inLiteral = !inLiteral;
    +
    +    } else if (!inLiteral && this.separators_.indexOf(arr[i]) != -1) {
    +      cache.push(arr[i]);
    +      parts.push(cache.join(''));
    +      cache.length = 0;
    +
    +    } else {
    +      cache.push(arr[i]);
    +    }
    +  }
    +  parts.push(cache.join(''));
    +
    +  return parts;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.html
    new file mode 100644
    index 0000000000..583348dbea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.InputHandler
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ac.InputHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <input type="text" id="textInput" style="display:none" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.js
    new file mode 100644
    index 0000000000..a2f1192d61
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/inputhandler_test.js
    @@ -0,0 +1,716 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.InputHandlerTest');
    +goog.setTestOnly('goog.ui.ac.InputHandlerTest');
    +
    +goog.require('goog.dom.selection');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ac.InputHandler');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Mock out the input element.
    + * @constructor
    + */
    +function MockElement() {
    +  goog.events.EventTarget.call(this);
    +  this.setAttributeNS = function() {};
    +  this.setAttribute = function(key, value) { this[key] = value; };
    +  this.focus = function() {};
    +  this.blur = function() {};
    +  this.ownerDocument = document;
    +  this.selectionStart = 0;
    +}
    +goog.inherits(MockElement, goog.events.EventTarget);
    +
    +
    +
    +/**
    + * @constructor
    + */
    +function MockAutoCompleter() {
    +  this.setToken = null;
    +  this.setTokenWasCalled = false;
    +  this.selectHilitedWasCalled = false;
    +  this.dismissWasCalled = false;
    +  this.getTarget = function() { return mockElement };
    +  this.setTarget = function() { };
    +  this.setToken = function(token) {
    +    this.setTokenWasCalled = true;
    +    this.setToken = token;
    +  };
    +  this.selectHilited = function() {
    +    this.selectHilitedWasCalled = true;
    +    return true; // Success.
    +  };
    +  this.cancelDelayedDismiss = function() { };
    +  this.dismissOnDelay = function() {};
    +  this.dismiss = function() { this.dismissWasCalled = true; };
    +  this.isOpen = goog.functions.TRUE;
    +}
    +
    +
    +
    +/**
    + * MockInputHandler simulates key events for testing the IME behavior of
    + * InputHandler.
    + * @constructor
    + */
    +function MockInputHandler() {
    +  goog.ui.ac.InputHandler.call(this);
    +
    +  this.ac_ = new MockAutoCompleter();
    +  this.cursorPosition_ = 0;
    +
    +  this.attachInput(mockElement);
    +}
    +goog.inherits(MockInputHandler, goog.ui.ac.InputHandler);
    +
    +
    +/** Checks for updates to the text area, should not happen during IME. */
    +MockInputHandler.prototype.update = function() {
    +  this.updates++;
    +};
    +
    +
    +/** Simulates key events. */
    +MockInputHandler.prototype.fireKeyEvents = function(
    +    keyCode, down, press, up, opt_properties) {
    +  if (down) this.fireEvent('keydown', keyCode, opt_properties);
    +  if (press) this.fireEvent('keypress', keyCode, opt_properties);
    +  if (up) this.fireEvent('keyup', keyCode, opt_properties);
    +};
    +
    +
    +/** Simulates an event. */
    +MockInputHandler.prototype.fireEvent = function(
    +    type, keyCode, opt_properties) {
    +  var e = {};
    +  e.type = type;
    +  e.keyCode = keyCode;
    +  e.preventDefault = function() {};
    +  if (!goog.userAgent.IE) {
    +    e.which = type == 'keydown' ? keyCode : 0;
    +  }
    +  if (opt_properties) {
    +    goog.object.extend(e, opt_properties);
    +  }
    +  e = new goog.events.BrowserEvent(e);
    +  mockElement.dispatchEvent(e);
    +};
    +
    +MockInputHandler.prototype.setCursorPosition = function(cursorPosition) {
    +  this.cursorPosition_ = cursorPosition;
    +};
    +
    +MockInputHandler.prototype.getCursorPosition = function() {
    +  return this.cursorPosition_;
    +};
    +
    +// Variables used by all test
    +var mh = null;
    +var oldMac, oldWin, oldLinux, oldIe, oldFf, oldWebkit, oldVersion;
    +var oldUsesKeyDown;
    +var mockElement;
    +var mockClock;
    +
    +function setUp() {
    +  oldMac = goog.userAgent.MAC;
    +  oldWin = goog.userAgent.WINDOWS;
    +  oldLinux = goog.userAgent.LINUX;
    +  oldIe = goog.userAgent.IE;
    +  oldFf = goog.userAgent.GECKO;
    +  oldWebkit = goog.userAgent.WEBKIT;
    +  oldVersion = goog.userAgent.VERSION;
    +  oldUsesKeyDown = goog.events.KeyHandler.USES_KEYDOWN_;
    +  mockClock = new goog.testing.MockClock(true);
    +  mockElement = new MockElement;
    +  mh = new MockInputHandler;
    +}
    +
    +function tearDown() {
    +  goog.userAgent.MAC = oldMac;
    +  goog.userAgent.WINDOWS = oldWin;
    +  goog.userAgent.LINUX = oldLinux;
    +  goog.userAgent.IE = oldIe;
    +  goog.userAgent.GECKO = oldFf;
    +  goog.userAgent.WEBKIT = oldWebkit;
    +  goog.userAgent.VERSION = oldVersion;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = oldUsesKeyDown;
    +  mockClock.dispose();
    +  mockElement.dispose();
    +}
    +
    +
    +/** Used to simulate behavior of Windows/Firefox3 */
    +function simulateWinFirefox3() {
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.WEBKIT = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = false;
    +}
    +
    +
    +/** Used to simulate behavior of Windows/InternetExplorer7 */
    +function simulateWinIe7() {
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.IE = true;
    +  goog.userAgent.DOCUMENT_MODE = 7;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.WEBKIT = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +}
    +
    +
    +/** Used to simulate behavior of Windows/Chrome */
    +function simulateWinChrome() {
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = true;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.WEBKIT = true;
    +  goog.userAgent.VERSION = '525';
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +}
    +
    +
    +/** Used to simulate behavior of Mac/Firefox3 */
    +function simulateMacFirefox3() {
    +  goog.userAgent.MAC = true;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.WEBKIT = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +}
    +
    +
    +/** Used to simulate behavior of Mac/Safari3 */
    +function simulateMacSafari3() {
    +  goog.userAgent.MAC = true;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = false;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = false;
    +  goog.userAgent.WEBKIT = true;
    +  goog.userAgent.VERSION = '525';
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +}
    +
    +
    +/** Used to simulate behavior of Linux/Firefox3 */
    +function simulateLinuxFirefox3() {
    +  goog.userAgent.MAC = false;
    +  goog.userAgent.WINDOWS = false;
    +  goog.userAgent.LINUX = true;
    +  goog.userAgent.IE = false;
    +  goog.userAgent.GECKO = true;
    +  goog.userAgent.WEBKIT = false;
    +  goog.events.KeyHandler.USES_KEYDOWN_ = true;
    +}
    +
    +
    +/** Test the normal, non-IME case */
    +function testRegularKey() {
    +  // Each key fires down, press, and up in that order, and each should
    +  // trigger an autocomplete update
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireKeyEvents(goog.events.KeyCodes.K, true, true, true);
    +  assertFalse('IME should not be triggered by K', mh.waitingForIme_);
    +
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, true, true, true);
    +  assertFalse('IME should not be triggered by A', mh.waitingForIme_);
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Windows/Firefox3.
    + */
    +function testImeWinFirefox3() {
    +  simulateWinFirefox3();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +  // Event is not generated for key code a.
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, false, false, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +  // Event is not generated for key code i.
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Windows/InternetExplorer7.
    + */
    +function testImeWinIe7() {
    +  simulateWinIe7();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, false, false, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.I, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Windows/Chrome.
    + */
    +function testImeWinChrome() {
    +  simulateWinChrome();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, false, false, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.I, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Mac/Firefox3.
    + */
    +function testImeMacFirefox3() {
    +  // TODO(user): Currently our code cannot distinguish preedit characters
    +  // from normal ones for Mac/Firefox3.
    +  // Enable this test after we fix it.
    +
    +  simulateMacFirefox3();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, true, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.I, true, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Mac/Safari3.
    + */
    +function testImeMacSafari3() {
    +  simulateMacSafari3();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, false, false, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, false, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.I, false, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * This test simulates the key inputs generated by pressing
    + * '<ime_on>a<enter>i<ime_off>u' using the Japanese IME
    + * on Linux/Firefox3.
    + */
    +function testImeLinuxFirefox3() {
    +  // TODO(user): Currently our code cannot distinguish preedit characters
    +  // from normal ones for Linux/Firefox3.
    +  // Enable this test after we fix it.
    +
    +
    +  simulateLinuxFirefox3();
    +  mh.fireEvent('focus', '');
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // ime_on
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +
    +  // a
    +  mh.fireKeyEvents(goog.events.KeyCodes.A, true, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // enter
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  // i
    +  mh.fireKeyEvents(goog.events.KeyCodes.WIN_IME, true, true, false);
    +  mh.fireKeyEvents(goog.events.KeyCodes.I, true, false, true);
    +  assertTrue('IME should be triggered', mh.waitingForIme_);
    +
    +  // ime_off
    +
    +  // u
    +  mh.fireKeyEvents(goog.events.KeyCodes.U, true, true, true);
    +  assertFalse('IME should not be triggered', mh.waitingForIme_);
    +
    +  mh.fireEvent('blur', '');
    +}
    +
    +
    +/**
    + * Check attaching to an EventTarget instead of an element.
    + */
    +function testAttachEventTarget() {
    +  var target = new goog.events.EventTarget();
    +
    +  assertNull(mh.activeElement_);
    +  mh.attachInput(target);
    +  assertNull(mh.activeElement_);
    +
    +  mockElement.dispatchEvent(new goog.events.Event('focus', mockElement));
    +  assertEquals(mockElement, mh.activeElement_);
    +
    +  mh.detachInput(target);
    +}
    +
    +
    +/**
    + * Make sure that the active element handling works.
    + */
    +function testActiveElement() {
    +  assertNull(mh.activeElement_);
    +
    +  mockElement.dispatchEvent('keydown');
    +  assertEquals(mockElement, mh.activeElement_);
    +
    +  mockElement.dispatchEvent('blur');
    +  assertNull(mh.activeElement_);
    +
    +  mockElement.dispatchEvent('focus');
    +  assertEquals(mockElement, mh.activeElement_);
    +
    +  mh.detachInput(mockElement);
    +  assertNull(mh.activeElement_);
    +}
    +
    +
    +/**
    + * We can attach an EventTarget that isn't an element.
    + */
    +function testAttachEventTarget() {
    +  var target = new goog.events.EventTarget();
    +
    +  assertNull(mh.activeElement_);
    +  mh.attachInput(target);
    +  assertNull(mh.activeElement_);
    +
    +  target.dispatchEvent(new goog.events.Event('focus', mockElement));
    +  assertEquals(mockElement, mh.activeElement_);
    +
    +  mh.detachInput(target);
    +}
    +
    +
    +/**
    + * Make sure an already-focused element becomes active immediately.
    + */
    +function testActiveElementAlreadyFocused() {
    +  var element = document.getElementById('textInput');
    +  element.style.display = '';
    +  element.focus();
    +
    +  assertNull(mh.activeElement_);
    +
    +  mh.attachInput(element);
    +  assertEquals(element, mh.activeElement_);
    +
    +  mh.detachInput(element);
    +  element.style.display = 'none';
    +}
    +
    +function testUpdateDoesNotTriggerSetTokenForSelectRow() {
    +
    +  var ih = new goog.ui.ac.InputHandler();
    +
    +  // Set up our input handler with the necessary mocks
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  ih.ac_ = mockAutoCompleter;
    +  ih.activeElement_ = mockElement;
    +
    +  var row = {};
    +  ih.selectRow(row, false);
    +
    +  ih.update();
    +  assertFalse('update should not call setToken on selectRow',
    +              mockAutoCompleter.setTokenWasCalled);
    +
    +  ih.update();
    +  assertFalse('update should not call setToken on selectRow',
    +              mockAutoCompleter.setTokenWasCalled);
    +}
    +
    +function testSetTokenText() {
    +
    +  var ih = new MockInputHandler();
    +
    +  // Set up our input handler with the necessary mocks
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  ih.ac_ = mockAutoCompleter;
    +  ih.activeElement_ = mockElement;
    +  mockElement.value = 'bob, wal, joey';
    +  ih.setCursorPosition(8);
    +
    +  ih.setTokenText('waldo', true /* multi-row */);
    +
    +  assertEquals('bob, waldo, joey', mockElement.value);
    +}
    +
    +function testSetTokenTextLeftHandSideOfToken() {
    +
    +  var ih = new MockInputHandler();
    +  ih.setSeparators(' ');
    +  ih.setWhitespaceWrapEntries(false);
    +
    +  // Set up our input handler with the necessary mocks
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  ih.ac_ = mockAutoCompleter;
    +  ih.activeElement_ = mockElement;
    +  mockElement.value = 'foo bar';
    +  // Sets cursor position right before 'bar'
    +  ih.setCursorPosition(4);
    +
    +  ih.setTokenText('bar', true /* multi-row */);
    +
    +  assertEquals('foo bar ', mockElement.value);
    +}
    +
    +function testEmptyTokenWithSeparator() {
    +  var ih = new goog.ui.ac.InputHandler();
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  ih.ac_ = mockAutoCompleter;
    +  ih.activeElement_ = mockElement;
    +  mockElement.value = ', ,';
    +  // Sets cursor position before the second comma
    +  goog.dom.selection.setStart(mockElement, 2);
    +
    +  ih.update();
    +  assertTrue('update should call setToken on selectRow',
    +      mockAutoCompleter.setTokenWasCalled);
    +  assertEquals('update should be called with empty string',
    +      '', mockAutoCompleter.setToken);
    +}
    +
    +function testNonEmptyTokenWithSeparator() {
    +  var ih = new goog.ui.ac.InputHandler();
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  ih.ac_ = mockAutoCompleter;
    +  ih.activeElement_ = mockElement;
    +  mockElement.value = ', joe ,';
    +  // Sets cursor position before the second comma
    +  goog.dom.selection.setStart(mockElement, 5);
    +
    +  ih.update();
    +  assertTrue('update should call setToken on selectRow',
    +      mockAutoCompleter.setTokenWasCalled);
    +  assertEquals('update should be called with expected string',
    +      'joe', mockAutoCompleter.setToken);
    +}
    +
    +function testGetThrottleTime() {
    +  var ih = new goog.ui.ac.InputHandler();
    +  ih.setThrottleTime(999);
    +  assertEquals('throttle time set+get', 999, ih.getThrottleTime());
    +}
    +
    +function testGetUpdateDuringTyping() {
    +  var ih = new goog.ui.ac.InputHandler();
    +  ih.setUpdateDuringTyping(false);
    +  assertFalse('update during typing set+get', ih.getUpdateDuringTyping());
    +}
    +
    +function testEnterToSelect() {
    +  mh.fireEvent('focus', '');
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, true, true, true);
    +  assertTrue('Should hilite', mh.ac_.selectHilitedWasCalled);
    +  assertFalse('Should NOT be dismissed', mh.ac_.dismissWasCalled);
    +}
    +
    +function testEnterDoesNotSelectWhenClosed() {
    +  mh.fireEvent('focus', '');
    +  mh.ac_.isOpen = goog.functions.FALSE;
    +  mh.fireKeyEvents(goog.events.KeyCodes.ENTER, true, true, true);
    +  assertFalse('Should NOT hilite', mh.ac_.selectHilitedWasCalled);
    +  assertTrue('Should be dismissed', mh.ac_.dismissWasCalled);
    +}
    +
    +function testTabToSelect() {
    +  mh.fireEvent('focus', '');
    +  mh.fireKeyEvents(goog.events.KeyCodes.TAB, true, true, true);
    +  assertTrue('Should hilite', mh.ac_.selectHilitedWasCalled);
    +  assertFalse('Should NOT be dismissed', mh.ac_.dismissWasCalled);
    +}
    +
    +function testTabDoesNotSelectWhenClosed() {
    +  mh.fireEvent('focus', '');
    +  mh.ac_.isOpen = goog.functions.FALSE;
    +  mh.fireKeyEvents(goog.events.KeyCodes.TAB, true, true, true);
    +  assertFalse('Should NOT hilite', mh.ac_.selectHilitedWasCalled);
    +  assertTrue('Should be dismissed', mh.ac_.dismissWasCalled);
    +}
    +
    +function testShiftTabDoesNotSelect() {
    +  mh.fireEvent('focus', '');
    +  mh.ac_.isOpen = goog.functions.TRUE;
    +  mh.fireKeyEvents(goog.events.KeyCodes.TAB, true, true, true,
    +      {shiftKey: true});
    +  assertFalse('Should NOT hilite', mh.ac_.selectHilitedWasCalled);
    +  assertTrue('Should be dismissed', mh.ac_.dismissWasCalled);
    +}
    +
    +function testEmptySeparatorUsesDefaults() {
    +  var inputHandler = new goog.ui.ac.InputHandler('');
    +  assertFalse(inputHandler.separatorCheck_.test(''));
    +  assertFalse(inputHandler.separatorCheck_.test('x'));
    +  assertTrue(inputHandler.separatorCheck_.test(','));
    +}
    +
    +function testMultipleSeparatorUsesEmptyDefaults() {
    +  var inputHandler = new goog.ui.ac.InputHandler(',\n', null, true);
    +  inputHandler.setWhitespaceWrapEntries(false);
    +  inputHandler.setSeparators(',\n', '');
    +
    +  // Set up our input handler with the necessary mocks
    +  var mockAutoCompleter = new MockAutoCompleter();
    +  inputHandler.ac_ = mockAutoCompleter;
    +  inputHandler.activeElement_ = mockElement;
    +  mockElement.value = 'bob,wal';
    +  inputHandler.setCursorPosition(8);
    +
    +  inputHandler.setTokenText('waldo', true /* multi-row */);
    +
    +  assertEquals('bob,waldo', mockElement.value);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/remote.js b/mapbox-gl/closure-library/closure/goog/ui/ac/remote.js
    new file mode 100644
    index 0000000000..121b98d938
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/remote.js
    @@ -0,0 +1,114 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Factory class to create a simple autocomplete that will match
    + * from an array of data provided via ajax.
    + *
    + * @see ../../demos/autocompleteremote.html
    + */
    +
    +goog.provide('goog.ui.ac.Remote');
    +
    +goog.require('goog.ui.ac.AutoComplete');
    +goog.require('goog.ui.ac.InputHandler');
    +goog.require('goog.ui.ac.RemoteArrayMatcher');
    +goog.require('goog.ui.ac.Renderer');
    +
    +
    +
    +/**
    + * Factory class for building a remote autocomplete widget that autocompletes
    + * an inputbox or text area from a data array provided via ajax.
    + * @param {string} url The Uri which generates the auto complete matches.
    + * @param {Element} input Input element or text area.
    + * @param {boolean=} opt_multi Whether to allow multiple entries; defaults
    + *     to false.
    + * @param {boolean=} opt_useSimilar Whether to use similar matches; e.g.
    + *     "gost" => "ghost".
    + * @constructor
    + * @extends {goog.ui.ac.AutoComplete}
    + */
    +goog.ui.ac.Remote = function(url, input, opt_multi, opt_useSimilar) {
    +  var matcher = new goog.ui.ac.RemoteArrayMatcher(url, !opt_useSimilar);
    +  this.matcher_ = matcher;
    +
    +  var renderer = new goog.ui.ac.Renderer();
    +
    +  var inputhandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi, 300);
    +
    +  goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
    +
    +  inputhandler.attachAutoComplete(this);
    +  inputhandler.attachInputs(input);
    +};
    +goog.inherits(goog.ui.ac.Remote, goog.ui.ac.AutoComplete);
    +
    +
    +/**
    + * Set whether or not standard highlighting should be used when rendering rows.
    + * @param {boolean} useStandardHighlighting true if standard highlighting used.
    + */
    +goog.ui.ac.Remote.prototype.setUseStandardHighlighting =
    +    function(useStandardHighlighting) {
    +  this.renderer_.setUseStandardHighlighting(useStandardHighlighting);
    +};
    +
    +
    +/**
    + * Gets the attached InputHandler object.
    + * @return {goog.ui.ac.InputHandler} The input handler.
    + */
    +goog.ui.ac.Remote.prototype.getInputHandler = function() {
    +  return /** @type {goog.ui.ac.InputHandler} */ (
    +      this.selectionHandler_);
    +};
    +
    +
    +/**
    + * Set the send method ("GET", "POST") for the matcher.
    + * @param {string} method The send method; default: GET.
    + */
    +goog.ui.ac.Remote.prototype.setMethod = function(method) {
    +  this.matcher_.setMethod(method);
    +};
    +
    +
    +/**
    + * Set the post data for the matcher.
    + * @param {string} content Post data.
    + */
    +goog.ui.ac.Remote.prototype.setContent = function(content) {
    +  this.matcher_.setContent(content);
    +};
    +
    +
    +/**
    + * Set the HTTP headers for the matcher.
    + * @param {Object|goog.structs.Map} headers Map of headers to add to the
    + *     request.
    + */
    +goog.ui.ac.Remote.prototype.setHeaders = function(headers) {
    +  this.matcher_.setHeaders(headers);
    +};
    +
    +
    +/**
    + * Set the timeout interval for the matcher.
    + * @param {number} interval Number of milliseconds after which an
    + *     incomplete request will be aborted; 0 means no timeout is set.
    + */
    +goog.ui.ac.Remote.prototype.setTimeoutInterval = function(interval) {
    +  this.matcher_.setTimeoutInterval(interval);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher.js b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher.js
    new file mode 100644
    index 0000000000..d2b6c235a7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher.js
    @@ -0,0 +1,274 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class that retrieves autocomplete matches via an ajax call.
    + *
    + */
    +
    +goog.provide('goog.ui.ac.RemoteArrayMatcher');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Uri');
    +goog.require('goog.events');
    +goog.require('goog.json');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +
    +
    +
    +/**
    + * An array matcher that requests matches via ajax.
    + * @param {string} url The Uri which generates the auto complete matches.  The
    + *     search term is passed to the server as the 'token' query param.
    + * @param {boolean=} opt_noSimilar If true, request that the server does not do
    + *     similarity matches for the input token against the dictionary.
    + *     The value is sent to the server as the 'use_similar' query param which is
    + *     either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true).
    + * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Specify the
    + *     XmlHttpFactory used to retrieve the matches.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.ui.ac.RemoteArrayMatcher =
    +    function(url, opt_noSimilar, opt_xmlHttpFactory) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The base URL for the ajax call.  The token and max_matches are added as
    +   * query params.
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * Whether similar matches should be found as well.  This is sent as a hint
    +   * to the server only.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useSimilar_ = !opt_noSimilar;
    +
    +  /**
    +   * The XhrIo object used for making remote requests.  When a new request
    +   * is made, the current one is aborted and the new one sent.
    +   * @type {goog.net.XhrIo}
    +   * @private
    +   */
    +  this.xhr_ = new goog.net.XhrIo(opt_xmlHttpFactory);
    +};
    +goog.inherits(goog.ui.ac.RemoteArrayMatcher, goog.Disposable);
    +
    +
    +/**
    + * The HTTP send method (GET, POST) to use when making the ajax call.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.method_ = 'GET';
    +
    +
    +/**
    + * Data to submit during a POST.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.content_ = undefined;
    +
    +
    +/**
    + * Headers to send with every HTTP request.
    + * @type {Object|goog.structs.Map}
    + * @private
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.headers_ = null;
    +
    +
    +/**
    + * Key to the listener on XHR. Used to clear previous listeners.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.lastListenerKey_ = null;
    +
    +
    +/**
    + * Set the send method ("GET", "POST").
    + * @param {string} method The send method; default: GET.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.setMethod = function(method) {
    +  this.method_ = method;
    +};
    +
    +
    +/**
    + * Set the post data.
    + * @param {string} content Post data.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.setContent = function(content) {
    +  this.content_ = content;
    +};
    +
    +
    +/**
    + * Set the HTTP headers.
    + * @param {Object|goog.structs.Map} headers Map of headers to add to the
    + *     request.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.setHeaders = function(headers) {
    +  this.headers_ = headers;
    +};
    +
    +
    +/**
    + * Set the timeout interval.
    + * @param {number} interval Number of milliseconds after which an
    + *     incomplete request will be aborted; 0 means no timeout is set.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.setTimeoutInterval =
    +    function(interval) {
    +  this.xhr_.setTimeoutInterval(interval);
    +};
    +
    +
    +/**
    + * Builds a complete GET-style URL, given the base URI and autocomplete related
    + * parameter values.
    + * <b>Override this to build any customized lookup URLs.</b>
    + * <b>Can be used to change request method and any post content as well.</b>
    + * @param {string} uri The base URI of the request target.
    + * @param {string} token Current token in autocomplete.
    + * @param {number} maxMatches Maximum number of matches required.
    + * @param {boolean} useSimilar A hint to the server.
    + * @param {string=} opt_fullString Complete text in the input element.
    + * @return {?string} The complete url. Return null if no request should be sent.
    + * @protected
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.buildUrl = function(uri,
    +    token, maxMatches, useSimilar, opt_fullString) {
    +  var url = new goog.Uri(uri);
    +  url.setParameterValue('token', token);
    +  url.setParameterValue('max_matches', String(maxMatches));
    +  url.setParameterValue('use_similar', String(Number(useSimilar)));
    +  return url.toString();
    +};
    +
    +
    +/**
    + * Returns whether the suggestions should be updated?
    + * <b>Override this to prevent updates eg - when token is empty.</b>
    + * @param {string} uri The base URI of the request target.
    + * @param {string} token Current token in autocomplete.
    + * @param {number} maxMatches Maximum number of matches required.
    + * @param {boolean} useSimilar A hint to the server.
    + * @param {string=} opt_fullString Complete text in the input element.
    + * @return {boolean} Whether new matches be requested.
    + * @protected
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.shouldRequestMatches =
    +    function(uri, token, maxMatches, useSimilar, opt_fullString) {
    +  return true;
    +};
    +
    +
    +/**
    + * Parses and retrieves the array of suggestions from XHR response.
    + * <b>Override this if the response is not a simple JSON array.</b>
    + * @param {string} responseText The XHR response text.
    + * @return {Array<string>} The array of suggestions.
    + * @protected
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.parseResponseText = function(
    +    responseText) {
    +
    +  var matches = [];
    +  // If there is no response text, unsafeParse will throw a syntax error.
    +  if (responseText) {
    +    /** @preserveTry */
    +    try {
    +      matches = goog.json.unsafeParse(responseText);
    +    } catch (exception) {
    +    }
    +  }
    +  return /** @type {Array<string>} */ (matches);
    +};
    +
    +
    +/**
    + * Handles the XHR response.
    + * @param {string} token The XHR autocomplete token.
    + * @param {Function} matchHandler The AutoComplete match handler.
    + * @param {goog.events.Event} event The XHR success event.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.xhrCallback = function(token,
    +    matchHandler, event) {
    +  var text = event.target.getResponseText();
    +  matchHandler(token, this.parseResponseText(text));
    +};
    +
    +
    +/**
    + * Retrieve a set of matching rows from the server via ajax.
    + * @param {string} token The text that should be matched; passed to the server
    + *     as the 'token' query param.
    + * @param {number} maxMatches The maximum number of matches requested from the
    + *     server; passed as the 'max_matches' query param.  The server is
    + *     responsible for limiting the number of matches that are returned.
    + * @param {Function} matchHandler Callback to execute on the result after
    + *     matching.
    + * @param {string=} opt_fullString The full string from the input box.
    + */
    +goog.ui.ac.RemoteArrayMatcher.prototype.requestMatchingRows =
    +    function(token, maxMatches, matchHandler, opt_fullString) {
    +
    +  if (!this.shouldRequestMatches(this.url_, token, maxMatches, this.useSimilar_,
    +      opt_fullString)) {
    +    return;
    +  }
    +  // Set the query params on the URL.
    +  var url = this.buildUrl(this.url_, token, maxMatches, this.useSimilar_,
    +      opt_fullString);
    +  if (!url) {
    +    // Do nothing if there is no URL.
    +    return;
    +  }
    +
    +  // The callback evals the server response and calls the match handler on
    +  // the array of matches.
    +  var callback = goog.bind(this.xhrCallback, this, token, matchHandler);
    +
    +  // Abort the current request and issue the new one; prevent requests from
    +  // being queued up by the browser with a slow server
    +  if (this.xhr_.isActive()) {
    +    this.xhr_.abort();
    +  }
    +  // This ensures if previous XHR is aborted or ends with error, the
    +  // corresponding success-callbacks are cleared.
    +  if (this.lastListenerKey_) {
    +    goog.events.unlistenByKey(this.lastListenerKey_);
    +  }
    +  // Listen once ensures successful callback gets cleared by itself.
    +  this.lastListenerKey_ = goog.events.listenOnce(this.xhr_,
    +      goog.net.EventType.SUCCESS, callback);
    +  this.xhr_.send(url, this.method_, this.content_, this.headers_);
    +};
    +
    +
    +/** @override */
    +goog.ui.ac.RemoteArrayMatcher.prototype.disposeInternal = function() {
    +  this.xhr_.dispose();
    +  goog.ui.ac.RemoteArrayMatcher.superClass_.disposeInternal.call(
    +      this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.html
    new file mode 100644
    index 0000000000..f880407588
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.RemoteArrayMatcher
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ac.RemoteArrayMatcherTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.js
    new file mode 100644
    index 0000000000..65080ce316
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/remotearraymatcher_test.js
    @@ -0,0 +1,67 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.RemoteArrayMatcherTest');
    +goog.setTestOnly('goog.ui.ac.RemoteArrayMatcherTest');
    +
    +goog.require('goog.json');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.net.XhrIo');
    +goog.require('goog.ui.ac.RemoteArrayMatcher');
    +var url = 'http://www.google.com';
    +var token = 'goog';
    +var maxMatches = 5;
    +var fullToken = 'google';
    +
    +var responseJsonText = "['eric', 'larry', 'sergey', 'marissa', 'pupius']";
    +var responseJson = goog.json.unsafeParse(responseJsonText);
    +
    +var mockControl;
    +var mockMatchHandler;
    +
    +
    +function setUp() {
    +  goog.net.XhrIo = goog.testing.net.XhrIo;
    +  mockControl = new goog.testing.MockControl();
    +  mockMatchHandler = mockControl.createFunctionMock();
    +}
    +
    +function testRequestMatchingRows_noSimilarTrue() {
    +  var matcher = new goog.ui.ac.RemoteArrayMatcher(url);
    +  mockMatchHandler(token, responseJson);
    +  mockControl.$replayAll();
    +  matcher.requestMatchingRows(token, maxMatches, mockMatchHandler, fullToken);
    +  matcher.xhr_.simulateResponse(200, responseJsonText);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    +
    +function testRequestMatchingRows_twoCalls() {
    +  var matcher = new goog.ui.ac.RemoteArrayMatcher(url);
    +
    +  var dummyMatchHandler = mockControl.createFunctionMock();
    +
    +  mockMatchHandler(token, responseJson);
    +  mockControl.$replayAll();
    +
    +  matcher.requestMatchingRows(token, maxMatches, dummyMatchHandler,
    +      fullToken);
    +
    +  matcher.requestMatchingRows(token, maxMatches, mockMatchHandler, fullToken);
    +  matcher.xhr_.simulateResponse(200, responseJsonText);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/renderer.js b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer.js
    new file mode 100644
    index 0000000000..49b494f8f2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer.js
    @@ -0,0 +1,1100 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for rendering the results of an auto complete and
    + * allow the user to select an row.
    + *
    + */
    +
    +goog.provide('goog.ui.ac.Renderer');
    +goog.provide('goog.ui.ac.Renderer.CustomRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.dom.FadeInAndShow');
    +goog.require('goog.fx.dom.FadeOutAndHide');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.IdGenerator');
    +goog.require('goog.ui.ac.AutoComplete');
    +
    +
    +
    +/**
    + * Class for rendering the results of an auto-complete in a drop down list.
    + *
    + * @constructor
    + * @param {Element=} opt_parentNode optional reference to the parent element
    + *     that will hold the autocomplete elements. goog.dom.getDocument().body
    + *     will be used if this is null.
    + * @param {?({renderRow}|{render})=} opt_customRenderer Custom full renderer to
    + *     render each row. Should be something with a renderRow or render method.
    + * @param {boolean=} opt_rightAlign Determines if the autocomplete will always
    + *     be right aligned. False by default.
    + * @param {boolean=} opt_useStandardHighlighting Determines if standard
    + *     highlighting should be applied to each row of data. Standard highlighting
    + *     bolds every matching substring for a given token in each row. True by
    + *     default.
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.ac.Renderer = function(opt_parentNode, opt_customRenderer,
    +    opt_rightAlign, opt_useStandardHighlighting) {
    +  goog.ui.ac.Renderer.base(this, 'constructor');
    +
    +  /**
    +   * Reference to the parent element that will hold the autocomplete elements
    +   * @type {Element}
    +   * @private
    +   */
    +  this.parent_ = opt_parentNode || goog.dom.getDocument().body;
    +
    +  /**
    +   * Dom helper for the parent element's document.
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = goog.dom.getDomHelper(this.parent_);
    +
    +  /**
    +   * Whether to reposition the autocomplete UI below the target node
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.reposition_ = !opt_parentNode;
    +
    +  /**
    +   * Reference to the main element that controls the rendered autocomplete
    +   * @type {Element}
    +   * @private
    +   */
    +  this.element_ = null;
    +
    +  /**
    +   * The current token that has been entered
    +   * @type {string}
    +   * @private
    +   */
    +  this.token_ = '';
    +
    +  /**
    +   * Array used to store the current set of rows being displayed
    +   * @type {Array<!Object>}
    +   * @private
    +   */
    +  this.rows_ = [];
    +
    +  /**
    +   * Array of the node divs that hold each result that is being displayed.
    +   * @type {Array<Element>}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.rowDivs_ = [];
    +
    +  /**
    +   * The index of the currently highlighted row
    +   * @type {number}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.hilitedRow_ = -1;
    +
    +  /**
    +   * The time that the rendering of the menu rows started
    +   * @type {number}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.startRenderingRows_ = -1;
    +
    +  /**
    +   * Store the current state for the renderer
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.visible_ = false;
    +
    +  /**
    +   * Classname for the main element.  This must be a single valid class name.
    +   * @type {string}
    +   */
    +  this.className = goog.getCssName('ac-renderer');
    +
    +  /**
    +   * Classname for row divs.  This must be a single valid class name.
    +   * @type {string}
    +   */
    +  this.rowClassName = goog.getCssName('ac-row');
    +
    +  // TODO(gboyer): Remove this as soon as we remove references and ensure that
    +  // no groups are pushing javascript using this.
    +  /**
    +   * The old class name for active row.  This name is deprecated because its
    +   * name is generic enough that a typical implementation would require a
    +   * descendant selector.
    +   * Active row will have rowClassName & activeClassName &
    +   * legacyActiveClassName.
    +   * @type {string}
    +   * @private
    +   */
    +  this.legacyActiveClassName_ = goog.getCssName('active');
    +
    +  /**
    +   * Class name for active row div.  This must be a single valid class name.
    +   * Active row will have rowClassName & activeClassName &
    +   * legacyActiveClassName.
    +   * @type {string}
    +   */
    +  this.activeClassName = goog.getCssName('ac-active');
    +
    +  /**
    +   * Class name for the bold tag highlighting the matched part of the text.
    +   * @type {string}
    +   */
    +  this.highlightedClassName = goog.getCssName('ac-highlighted');
    +
    +  /**
    +   * Custom full renderer
    +   * @type {?({renderRow}|{render})}
    +   * @private
    +   */
    +  this.customRenderer_ = opt_customRenderer || null;
    +
    +  /**
    +   * Flag to indicate whether standard highlighting should be applied.
    +   * this is set to true if left unspecified to retain existing
    +   * behaviour for autocomplete clients
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useStandardHighlighting_ = opt_useStandardHighlighting != null ?
    +      opt_useStandardHighlighting : true;
    +
    +  /**
    +   * Flag to indicate whether matches should be done on whole words instead
    +   * of any string.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.matchWordBoundary_ = true;
    +
    +  /**
    +   * Flag to set all tokens as highlighted in the autocomplete row.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.highlightAllTokens_ = false;
    +
    +  /**
    +   * Determines if the autocomplete will always be right aligned
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.rightAlign_ = !!opt_rightAlign;
    +
    +  /**
    +   * Whether to align with top of target field
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.topAlign_ = false;
    +
    +  /**
    +   * Duration (in msec) of fade animation when menu is shown/hidden.
    +   * Setting to 0 (default) disables animation entirely.
    +   * @type {number}
    +   * @private
    +   */
    +  this.menuFadeDuration_ = 0;
    +
    +  /**
    +   * Whether we should limit the dropdown from extending past the bottom of the
    +   * screen and instead show a scrollbar on the dropdown.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.showScrollbarsIfTooLarge_ = false;
    +
    +  /**
    +   * Animation in progress, if any.
    +   * @type {goog.fx.Animation|undefined}
    +   */
    +  this.animation_;
    +};
    +goog.inherits(goog.ui.ac.Renderer, goog.events.EventTarget);
    +
    +
    +/**
    + * The anchor element to position the rendered autocompleter against.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.anchorElement_;
    +
    +
    +/**
    + * The element on which to base the width of the autocomplete.
    + * @type {Node}
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.widthProvider_;
    +
    +
    +/**
    + * A flag used to make sure we highlight only one match in the rendered row.
    + * @private {boolean}
    + */
    +goog.ui.ac.Renderer.prototype.wasHighlightedAtLeastOnce_;
    +
    +
    +/**
    + * The delay before mouseover events are registered, in milliseconds
    + * @type {number}
    + * @const
    + */
    +goog.ui.ac.Renderer.DELAY_BEFORE_MOUSEOVER = 300;
    +
    +
    +/**
    + * Gets the renderer's element.
    + * @return {Element} The  main element that controls the rendered autocomplete.
    + */
    +goog.ui.ac.Renderer.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Sets the width provider element. The provider is only used on redraw and as
    + * such will not automatically update on resize.
    + * @param {Node} widthProvider The element whose width should be mirrored.
    + */
    +goog.ui.ac.Renderer.prototype.setWidthProvider = function(widthProvider) {
    +  this.widthProvider_ = widthProvider;
    +};
    +
    +
    +/**
    + * Set whether to align autocomplete to top of target element
    + * @param {boolean} align If true, align to top.
    + */
    +goog.ui.ac.Renderer.prototype.setTopAlign = function(align) {
    +  this.topAlign_ = align;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we should be aligning to the top of
    + *     the target element.
    + */
    +goog.ui.ac.Renderer.prototype.getTopAlign = function() {
    +  return this.topAlign_;
    +};
    +
    +
    +/**
    + * Set whether to align autocomplete to the right of the target element.
    + * @param {boolean} align If true, align to right.
    + */
    +goog.ui.ac.Renderer.prototype.setRightAlign = function(align) {
    +  this.rightAlign_ = align;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the autocomplete menu should be right aligned.
    + */
    +goog.ui.ac.Renderer.prototype.getRightAlign = function() {
    +  return this.rightAlign_;
    +};
    +
    +
    +/**
    + * @param {boolean} show Whether we should limit the dropdown from extending
    + *     past the bottom of the screen and instead show a scrollbar on the
    + *     dropdown.
    + */
    +goog.ui.ac.Renderer.prototype.setShowScrollbarsIfTooLarge = function(show) {
    +  this.showScrollbarsIfTooLarge_ = show;
    +};
    +
    +
    +/**
    + * Set whether or not standard highlighting should be used when rendering rows.
    + * @param {boolean} useStandardHighlighting true if standard highlighting used.
    + */
    +goog.ui.ac.Renderer.prototype.setUseStandardHighlighting =
    +    function(useStandardHighlighting) {
    +  this.useStandardHighlighting_ = useStandardHighlighting;
    +};
    +
    +
    +/**
    + * @param {boolean} matchWordBoundary Determines whether matches should be
    + *     higlighted only when the token matches text at a whole-word boundary.
    + *     True by default.
    + */
    +goog.ui.ac.Renderer.prototype.setMatchWordBoundary =
    +    function(matchWordBoundary) {
    +  this.matchWordBoundary_ = matchWordBoundary;
    +};
    +
    +
    +/**
    + * Set whether or not to highlight all matching tokens rather than just the
    + * first.
    + * @param {boolean} highlightAllTokens Whether to highlight all matching tokens
    + *     rather than just the first.
    + */
    +goog.ui.ac.Renderer.prototype.setHighlightAllTokens =
    +    function(highlightAllTokens) {
    +  this.highlightAllTokens_ = highlightAllTokens;
    +};
    +
    +
    +/**
    + * Sets the duration (in msec) of the fade animation when menu is shown/hidden.
    + * Setting to 0 (default) disables animation entirely.
    + * @param {number} duration Duration (in msec) of the fade animation (or 0 for
    + *     no animation).
    + */
    +goog.ui.ac.Renderer.prototype.setMenuFadeDuration = function(duration) {
    +  this.menuFadeDuration_ = duration;
    +};
    +
    +
    +/**
    + * Sets the anchor element for the subsequent call to renderRows.
    + * @param {Element} anchor The anchor element.
    + */
    +goog.ui.ac.Renderer.prototype.setAnchorElement = function(anchor) {
    +  this.anchorElement_ = anchor;
    +};
    +
    +
    +/**
    + * @return {Element} The anchor element.
    + * @protected
    + */
    +goog.ui.ac.Renderer.prototype.getAnchorElement = function() {
    +  return this.anchorElement_;
    +};
    +
    +
    +/**
    + * Render the autocomplete UI
    + *
    + * @param {Array<!Object>} rows Matching UI rows.
    + * @param {string} token Token we are currently matching against.
    + * @param {Element=} opt_target Current HTML node, will position popup beneath
    + *     this node.
    + */
    +goog.ui.ac.Renderer.prototype.renderRows = function(rows, token, opt_target) {
    +  this.token_ = token;
    +  this.rows_ = rows;
    +  this.hilitedRow_ = -1;
    +  this.startRenderingRows_ = goog.now();
    +  this.target_ = opt_target;
    +  this.rowDivs_ = [];
    +  this.redraw();
    +};
    +
    +
    +/**
    + * Hide the object.
    + */
    +goog.ui.ac.Renderer.prototype.dismiss = function() {
    +  if (this.target_) {
    +    goog.a11y.aria.setActiveDescendant(this.target_, null);
    +  }
    +  if (this.visible_) {
    +    this.visible_ = false;
    +
    +    // Clear ARIA popup role for the target input box.
    +    if (this.target_) {
    +      goog.a11y.aria.setState(this.target_,
    +          goog.a11y.aria.State.HASPOPUP,
    +          false);
    +    }
    +
    +    if (this.menuFadeDuration_ > 0) {
    +      goog.dispose(this.animation_);
    +      this.animation_ = new goog.fx.dom.FadeOutAndHide(this.element_,
    +          this.menuFadeDuration_);
    +      this.animation_.play();
    +    } else {
    +      goog.style.setElementShown(this.element_, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Show the object.
    + */
    +goog.ui.ac.Renderer.prototype.show = function() {
    +  if (!this.visible_) {
    +    this.visible_ = true;
    +
    +    // Set ARIA roles and states for the target input box.
    +    if (this.target_) {
    +      goog.a11y.aria.setRole(this.target_,
    +          goog.a11y.aria.Role.COMBOBOX);
    +      goog.a11y.aria.setState(this.target_,
    +          goog.a11y.aria.State.AUTOCOMPLETE,
    +          'list');
    +      goog.a11y.aria.setState(this.target_,
    +          goog.a11y.aria.State.HASPOPUP,
    +          true);
    +    }
    +
    +    if (this.menuFadeDuration_ > 0) {
    +      goog.dispose(this.animation_);
    +      this.animation_ = new goog.fx.dom.FadeInAndShow(this.element_,
    +          this.menuFadeDuration_);
    +      this.animation_.play();
    +    } else {
    +      goog.style.setElementShown(this.element_, true);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} True if the object is visible.
    + */
    +goog.ui.ac.Renderer.prototype.isVisible = function() {
    +  return this.visible_;
    +};
    +
    +
    +/**
    + * Sets the 'active' class of the nth item.
    + * @param {number} index Index of the item to highlight.
    + */
    +goog.ui.ac.Renderer.prototype.hiliteRow = function(index) {
    +  var row = index >= 0 && index < this.rows_.length ?
    +      this.rows_[index] : undefined;
    +  var rowDiv = index >= 0 && index < this.rowDivs_.length ?
    +      this.rowDivs_[index] : undefined;
    +
    +  var evtObj = /** @lends {goog.events.Event.prototype} */ ({
    +    type: goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +    rowNode: rowDiv,
    +    row: row ? row.data : null
    +  });
    +  if (this.dispatchEvent(evtObj)) {
    +    this.hiliteNone();
    +    this.hilitedRow_ = index;
    +    if (rowDiv) {
    +      goog.dom.classlist.addAll(rowDiv, [this.activeClassName,
    +        this.legacyActiveClassName_]);
    +      if (this.target_) {
    +        goog.a11y.aria.setActiveDescendant(this.target_, rowDiv);
    +      }
    +      goog.style.scrollIntoContainerView(rowDiv, this.element_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes the 'active' class from the currently selected row.
    + */
    +goog.ui.ac.Renderer.prototype.hiliteNone = function() {
    +  if (this.hilitedRow_ >= 0) {
    +    goog.dom.classlist.removeAll(
    +        goog.asserts.assert(this.rowDivs_[this.hilitedRow_]),
    +        [this.activeClassName, this.legacyActiveClassName_]);
    +  }
    +};
    +
    +
    +/**
    + * Sets the 'active' class of the item with a given id.
    + * @param {number} id Id of the row to hilight. If id is -1 then no rows get
    + *     hilited.
    + */
    +goog.ui.ac.Renderer.prototype.hiliteId = function(id) {
    +  if (id == -1) {
    +    this.hiliteRow(-1);
    +  } else {
    +    for (var i = 0; i < this.rows_.length; i++) {
    +      if (this.rows_[i].id == id) {
    +        this.hiliteRow(i);
    +        return;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets CSS classes on autocomplete conatainer element.
    + *
    + * @param {Element} elem The container element.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.setMenuClasses_ = function(elem) {
    +  goog.asserts.assert(elem);
    +  // Legacy clients may set the renderer's className to a space-separated list
    +  // or even have a trailing space.
    +  goog.dom.classlist.addAll(elem, goog.string.trim(this.className).split(' '));
    +};
    +
    +
    +/**
    + * If the main HTML element hasn't been made yet, creates it and appends it
    + * to the parent.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.maybeCreateElement_ = function() {
    +  if (!this.element_) {
    +    // Make element and add it to the parent
    +    var el = this.dom_.createDom('div', {style: 'display:none'});
    +    if (this.showScrollbarsIfTooLarge_) {
    +      // Make sure that the dropdown will get scrollbars if it isn't large
    +      // enough to show all rows.
    +      el.style.overflowY = 'auto';
    +    }
    +    this.element_ = el;
    +    this.setMenuClasses_(el);
    +    goog.a11y.aria.setRole(el, goog.a11y.aria.Role.LISTBOX);
    +
    +    el.id = goog.ui.IdGenerator.getInstance().getNextUniqueId();
    +
    +    this.dom_.appendChild(this.parent_, el);
    +
    +    // Add this object as an event handler
    +    goog.events.listen(el, goog.events.EventType.CLICK,
    +                       this.handleClick_, false, this);
    +    goog.events.listen(el, goog.events.EventType.MOUSEDOWN,
    +                       this.handleMouseDown_, false, this);
    +    goog.events.listen(el, goog.events.EventType.MOUSEOVER,
    +                       this.handleMouseOver_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Redraw (or draw if this is the first call) the rendered auto-complete drop
    + * down.
    + */
    +goog.ui.ac.Renderer.prototype.redraw = function() {
    +  // Create the element if it doesn't yet exist
    +  this.maybeCreateElement_();
    +
    +  // For top aligned with target (= bottom aligned element),
    +  // we need to hide and then add elements while hidden to prevent
    +  // visible repositioning
    +  if (this.topAlign_) {
    +    this.element_.style.visibility = 'hidden';
    +  }
    +
    +  if (this.widthProvider_) {
    +    var width = this.widthProvider_.clientWidth + 'px';
    +    this.element_.style.minWidth = width;
    +  }
    +
    +  // Remove the current child nodes
    +  this.rowDivs_.length = 0;
    +  this.dom_.removeChildren(this.element_);
    +
    +  // Generate the new rows (use forEach so we can change rows_ from an
    +  // array to a different datastructure if required)
    +  if (this.customRenderer_ && this.customRenderer_.render) {
    +    this.customRenderer_.render(this, this.element_, this.rows_, this.token_);
    +  } else {
    +    var curRow = null;
    +    goog.array.forEach(this.rows_, function(row) {
    +      row = this.renderRowHtml(row, this.token_);
    +      if (this.topAlign_) {
    +        // Aligned with top of target = best match at bottom
    +        this.element_.insertBefore(row, curRow);
    +      } else {
    +        this.dom_.appendChild(this.element_, row);
    +      }
    +      curRow = row;
    +    }, this);
    +  }
    +
    +  // Don't show empty result sets
    +  if (this.rows_.length == 0) {
    +    this.dismiss();
    +    return;
    +  } else {
    +    this.show();
    +  }
    +
    +  this.reposition();
    +
    +  // Make the autocompleter unselectable, so that it
    +  // doesn't steal focus from the input field when clicked.
    +  goog.style.setUnselectable(this.element_, true);
    +};
    +
    +
    +/**
    + * @return {goog.positioning.Corner} The anchor corner to position the popup at.
    + * @protected
    + */
    +goog.ui.ac.Renderer.prototype.getAnchorCorner = function() {
    +  var anchorCorner = this.rightAlign_ ?
    +      goog.positioning.Corner.BOTTOM_RIGHT :
    +      goog.positioning.Corner.BOTTOM_LEFT;
    +  if (this.topAlign_) {
    +    anchorCorner = goog.positioning.flipCornerVertical(anchorCorner);
    +  }
    +  return anchorCorner;
    +};
    +
    +
    +/**
    + * Repositions the auto complete popup relative to the location node, if it
    + * exists and the auto position has been set.
    + */
    +goog.ui.ac.Renderer.prototype.reposition = function() {
    +  if (this.target_ && this.reposition_) {
    +    var anchorElement = this.anchorElement_ || this.target_;
    +    var anchorCorner = this.getAnchorCorner();
    +
    +    var overflowMode = goog.positioning.Overflow.ADJUST_X_EXCEPT_OFFSCREEN;
    +    if (this.showScrollbarsIfTooLarge_) {
    +      // positionAtAnchor will set the height of this.element_ when it runs
    +      // (because of RESIZE_HEIGHT), and it will never increase it relative to
    +      // its current value when it runs again. But if the user scrolls their
    +      // page, then we might actually want a bigger height when the dropdown is
    +      // displayed next time. So we clear the height before calling
    +      // positionAtAnchor, so it is free to set the height as large as it
    +      // chooses.
    +      this.element_.style.height = '';
    +      overflowMode |= goog.positioning.Overflow.RESIZE_HEIGHT;
    +    }
    +
    +    goog.positioning.positionAtAnchor(
    +        anchorElement, anchorCorner,
    +        this.element_, goog.positioning.flipCornerVertical(anchorCorner),
    +        null, null, overflowMode);
    +
    +    if (this.topAlign_) {
    +      // This flickers, but is better than the alternative of positioning
    +      // in the wrong place and then moving.
    +      this.element_.style.visibility = 'visible';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the renderer should try to determine where to position the
    + * drop down.
    + * @param {boolean} auto Whether to autoposition the drop down.
    + */
    +goog.ui.ac.Renderer.prototype.setAutoPosition = function(auto) {
    +  this.reposition_ = auto;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the drop down will be autopositioned.
    + * @protected
    + */
    +goog.ui.ac.Renderer.prototype.getAutoPosition = function() {
    +  return this.reposition_;
    +};
    +
    +
    +/**
    + * @return {Element} The target element.
    + * @protected
    + */
    +goog.ui.ac.Renderer.prototype.getTarget = function() {
    +  return this.target_;
    +};
    +
    +
    +/**
    + * Disposes of the renderer and its associated HTML.
    + * @override
    + * @protected
    + */
    +goog.ui.ac.Renderer.prototype.disposeInternal = function() {
    +  if (this.element_) {
    +    goog.events.unlisten(this.element_, goog.events.EventType.CLICK,
    +        this.handleClick_, false, this);
    +    goog.events.unlisten(this.element_, goog.events.EventType.MOUSEDOWN,
    +        this.handleMouseDown_, false, this);
    +    goog.events.unlisten(this.element_, goog.events.EventType.MOUSEOVER,
    +        this.handleMouseOver_, false, this);
    +    this.dom_.removeNode(this.element_);
    +    this.element_ = null;
    +    this.visible_ = false;
    +  }
    +
    +  goog.dispose(this.animation_);
    +  this.parent_ = null;
    +
    +  goog.ui.ac.Renderer.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Generic function that takes a row and renders a DOM structure for that row.
    + *
    + * Normally this will only be matching a maximum of 20 or so items.  Even with
    + * 40 rows, DOM this building is fine.
    + *
    + * @param {Object} row Object representing row.
    + * @param {string} token Token to highlight.
    + * @param {Node} node The node to render into.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.renderRowContents_ =
    +    function(row, token, node) {
    +  goog.dom.setTextContent(node, row.data.toString());
    +};
    +
    +
    +/**
    + * Goes through a node and all of its child nodes, replacing HTML text that
    + * matches a token with <b>token</b>.
    + * The replacement will happen on the first match or all matches depending on
    + * this.highlightAllTokens_ value.
    + *
    + * @param {Node} node Node to match.
    + * @param {string|Array<string>} tokenOrArray Token to match or array of tokens
    + *     to match.  By default, only the first match will be highlighted.  If
    + *     highlightAllTokens is set, then all tokens appearing at the start of a
    + *     word, in whatever order and however many times, will be highlighted.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.startHiliteMatchingText_ =
    +    function(node, tokenOrArray) {
    +  this.wasHighlightedAtLeastOnce_ = false;
    +  this.hiliteMatchingText_(node, tokenOrArray);
    +};
    +
    +
    +/**
    + * @param {Node} node Node to match.
    + * @param {string|Array<string>} tokenOrArray Token to match or array of tokens
    + *     to match.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.hiliteMatchingText_ =
    +    function(node, tokenOrArray) {
    +  if (!this.highlightAllTokens_ && this.wasHighlightedAtLeastOnce_) {
    +    return;
    +  }
    +
    +  if (node.nodeType == goog.dom.NodeType.TEXT) {
    +    var rest = null;
    +    if (goog.isArray(tokenOrArray) &&
    +        tokenOrArray.length > 1 &&
    +        !this.highlightAllTokens_) {
    +      rest = goog.array.slice(tokenOrArray, 1);
    +    }
    +
    +    var token = this.getTokenRegExp_(tokenOrArray);
    +    if (token.length == 0) return;
    +
    +    var text = node.nodeValue;
    +
    +    // Create a regular expression to match a token at the beginning of a line
    +    // or preceeded by non-alpha-numeric characters. Note: token could have |
    +    // operators in it, so we need to parenthesise it before adding \b to it.
    +    // or preceeded by non-alpha-numeric characters
    +    //
    +    // NOTE(user): When using word matches, this used to have
    +    // a (^|\\W+) clause where it now has \\b but it caused various
    +    // browsers to hang on really long strings. The (^|\\W+) matcher was also
    +    // unnecessary, because \b already checks that the character before the
    +    // is a non-word character, and ^ matches the start of the line or following
    +    // a line terminator character, which is also \W. The regexp also used to
    +    // have a capturing match before the \\b, which would capture the
    +    // non-highlighted content, but that caused the regexp matching to run much
    +    // slower than the current version.
    +    var re = this.matchWordBoundary_ ?
    +        new RegExp('\\b(?:' + token + ')', 'gi') :
    +        new RegExp(token, 'gi');
    +    var textNodes = [];
    +    var lastIndex = 0;
    +
    +    // Find all matches
    +    // Note: text.split(re) has inconsistencies between IE and FF, so
    +    // manually recreated the logic
    +    var match = re.exec(text);
    +    var numMatches = 0;
    +    while (match) {
    +      numMatches++;
    +      textNodes.push(text.substring(lastIndex, match.index));
    +      textNodes.push(text.substring(match.index, re.lastIndex));
    +      lastIndex = re.lastIndex;
    +      match = re.exec(text);
    +    }
    +    textNodes.push(text.substring(lastIndex));
    +
    +    // Replace the tokens with bolded text.  Each pair of textNodes
    +    // (starting at index idx) includes a node of text before the bolded
    +    // token, and a node (at idx + 1) consisting of what should be
    +    // enclosed in bold tags.
    +    if (textNodes.length > 1) {
    +      var maxNumToBold = !this.highlightAllTokens_ ? 1 : numMatches;
    +      for (var i = 0; i < maxNumToBold; i++) {
    +        var idx = 2 * i;
    +
    +        node.nodeValue = textNodes[idx];
    +        var boldTag = this.dom_.createElement('b');
    +        boldTag.className = this.highlightedClassName;
    +        this.dom_.appendChild(boldTag,
    +            this.dom_.createTextNode(textNodes[idx + 1]));
    +        boldTag = node.parentNode.insertBefore(boldTag, node.nextSibling);
    +        node.parentNode.insertBefore(this.dom_.createTextNode(''),
    +            boldTag.nextSibling);
    +        node = boldTag.nextSibling;
    +      }
    +
    +      // Append the remaining text nodes to the end.
    +      var remainingTextNodes = goog.array.slice(textNodes, maxNumToBold * 2);
    +      node.nodeValue = remainingTextNodes.join('');
    +
    +      this.wasHighlightedAtLeastOnce_ = true;
    +    } else if (rest) {
    +      this.hiliteMatchingText_(node, rest);
    +    }
    +  } else {
    +    var child = node.firstChild;
    +    while (child) {
    +      var nextChild = child.nextSibling;
    +      this.hiliteMatchingText_(child, tokenOrArray);
    +      child = nextChild;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Transforms a token into a string ready to be put into the regular expression
    + * in hiliteMatchingText_.
    + * @param {string|Array<string>} tokenOrArray The token or array to get the
    + *     regex string from.
    + * @return {string} The regex-ready token.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.getTokenRegExp_ = function(tokenOrArray) {
    +  var token = '';
    +
    +  if (!tokenOrArray) {
    +    return token;
    +  }
    +
    +  if (goog.isArray(tokenOrArray)) {
    +    // Remove invalid tokens from the array, which may leave us with nothing.
    +    tokenOrArray = goog.array.filter(tokenOrArray, function(str) {
    +      return !goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));
    +    });
    +  }
    +
    +  // If highlighting all tokens, join them with '|' so the regular expression
    +  // will match on any of them.
    +  if (this.highlightAllTokens_) {
    +    if (goog.isArray(tokenOrArray)) {
    +      var tokenArray = goog.array.map(tokenOrArray, goog.string.regExpEscape);
    +      token = tokenArray.join('|');
    +    } else {
    +      // Remove excess whitespace from the string so bars will separate valid
    +      // tokens in the regular expression.
    +      token = goog.string.collapseWhitespace(tokenOrArray);
    +
    +      token = goog.string.regExpEscape(token);
    +      token = token.replace(/ /g, '|');
    +    }
    +  } else {
    +    // Not highlighting all matching tokens.  If tokenOrArray is a string, use
    +    // that as the token.  If it is an array, use the first element in the
    +    // array.
    +    // TODO(user): why is this this way?. We should match against all
    +    // tokens in the array, but only accept the first match.
    +    if (goog.isArray(tokenOrArray)) {
    +      token = tokenOrArray.length > 0 ?
    +          goog.string.regExpEscape(tokenOrArray[0]) : '';
    +    } else {
    +      // For the single-match string token, we refuse to match anything if
    +      // the string begins with a non-word character, as matches by definition
    +      // can only occur at the start of a word. (This also handles the
    +      // goog.string.isEmptyOrWhitespace(goog.string.makeSafe(tokenOrArray)) case.)
    +      if (!/^\W/.test(tokenOrArray)) {
    +        token = goog.string.regExpEscape(tokenOrArray);
    +      }
    +    }
    +  }
    +
    +  return token;
    +};
    +
    +
    +/**
    + * Render a row by creating a div and then calling row rendering callback or
    + * default row handler
    + *
    + * @param {Object} row Object representing row.
    + * @param {string} token Token to highlight.
    + * @return {!Element} An element with the rendered HTML.
    + */
    +goog.ui.ac.Renderer.prototype.renderRowHtml = function(row, token) {
    +  // Create and return the element.
    +  var elem = this.dom_.createDom('div', {
    +    className: this.rowClassName,
    +    id: goog.ui.IdGenerator.getInstance().getNextUniqueId()
    +  });
    +  goog.a11y.aria.setRole(elem, goog.a11y.aria.Role.OPTION);
    +  if (this.customRenderer_ && this.customRenderer_.renderRow) {
    +    this.customRenderer_.renderRow(row, token, elem);
    +  } else {
    +    this.renderRowContents_(row, token, elem);
    +  }
    +
    +  if (token && this.useStandardHighlighting_) {
    +    this.startHiliteMatchingText_(elem, token);
    +  }
    +
    +  goog.dom.classlist.add(elem, this.rowClassName);
    +  this.rowDivs_.push(elem);
    +  return elem;
    +};
    +
    +
    +/**
    + * Given an event target looks up through the parents till it finds a div.  Once
    + * found it will then look to see if that is one of the childnodes, if it is
    + * then the index is returned, otherwise -1 is returned.
    + * @param {Element} et HtmlElement.
    + * @return {number} Index corresponding to event target.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.getRowFromEventTarget_ = function(et) {
    +  while (et && et != this.element_ &&
    +      !goog.dom.classlist.contains(et, this.rowClassName)) {
    +    et = /** @type {Element} */ (et.parentNode);
    +  }
    +  return et ? goog.array.indexOf(this.rowDivs_, et) : -1;
    +};
    +
    +
    +/**
    + * Handle the click events.  These are redirected to the AutoComplete object
    + * which then makes a callback to select the correct row.
    + * @param {goog.events.Event} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.handleClick_ = function(e) {
    +  var index = this.getRowFromEventTarget_(/** @type {Element} */ (e.target));
    +  if (index >= 0) {
    +    this.dispatchEvent(/** @lends {goog.events.Event.prototype} */ ({
    +      type: goog.ui.ac.AutoComplete.EventType.SELECT,
    +      row: this.rows_[index].id
    +    }));
    +  }
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * Handle the mousedown event and prevent the AC from losing focus.
    + * @param {goog.events.Event} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.handleMouseDown_ = function(e) {
    +  e.stopPropagation();
    +  e.preventDefault();
    +};
    +
    +
    +/**
    + * Handle the mousing events.  These are redirected to the AutoComplete object
    + * which then makes a callback to set the correctly highlighted row.  This is
    + * because the AutoComplete can move the focus as well, and there is no sense
    + * duplicating the code
    + * @param {goog.events.Event} e Browser event object.
    + * @private
    + */
    +goog.ui.ac.Renderer.prototype.handleMouseOver_ = function(e) {
    +  var index = this.getRowFromEventTarget_(/** @type {Element} */ (e.target));
    +  if (index >= 0) {
    +    if ((goog.now() - this.startRenderingRows_) <
    +        goog.ui.ac.Renderer.DELAY_BEFORE_MOUSEOVER) {
    +      return;
    +    }
    +
    +    this.dispatchEvent({
    +      type: goog.ui.ac.AutoComplete.EventType.HILITE,
    +      row: this.rows_[index].id
    +    });
    +  }
    +};
    +
    +
    +
    +/**
    + * Class allowing different implementations to custom render the autocomplete.
    + * Extending classes should override the render function.
    + * @constructor
    + */
    +goog.ui.ac.Renderer.CustomRenderer = function() {
    +};
    +
    +
    +/**
    + * Renders the autocomplete box. May be set to null.
    + *
    + * Because of the type, this function cannot be documented with param JSDoc.
    + *
    + * The function expects the following parameters:
    + *
    + * renderer, goog.ui.ac.Renderer: The autocomplete renderer.
    + * element, Element: The main element that controls the rendered autocomplete.
    + * rows, Array: The current set of rows being displayed.
    + * token, string: The current token that has been entered. *
    + *
    + * @type {function(goog.ui.ac.Renderer, Element, Array, string)|
    + *        null|undefined}
    + */
    +goog.ui.ac.Renderer.CustomRenderer.prototype.render = function(
    +    renderer, element, rows, token) {
    +};
    +
    +
    +/**
    + * Generic function that takes a row and renders a DOM structure for that row.
    + * @param {Object} row Object representing row.
    + * @param {string} token Token to highlight.
    + * @param {Node} node The node to render into.
    + */
    +goog.ui.ac.Renderer.CustomRenderer.prototype.renderRow =
    +    function(row, token, node) {
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.html
    new file mode 100644
    index 0000000000..39149c08f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.html
    @@ -0,0 +1,38 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ac.Renderer
    +  </title>
    +  <style type="text/css">
    +   #viewport {
    +  width: 400px;
    +  height: 200px;
    +  overflow: hidden; /* Suppress scroll bars to get consistent cross-browser resizing */
    +  position: relative;
    +}
    +  </style>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ac.RendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="someElement">Click target</div>
    +  <div id="target">Target</div>
    +  <div id="viewport">
    +    Parent (viewport) element for some tests
    +    <div id="viewportTarget">Target for viewport tests</div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.js
    new file mode 100644
    index 0000000000..03a40aac4a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/renderer_test.js
    @@ -0,0 +1,730 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ac.RendererTest');
    +goog.setTestOnly('goog.ui.ac.RendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.fx.dom.FadeInAndShow');
    +goog.require('goog.fx.dom.FadeOutAndHide');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ac.AutoComplete');
    +goog.require('goog.ui.ac.Renderer');
    +var renderer;
    +var rendRows = [];
    +var someElement;
    +var target;
    +var viewport;
    +var viewportTarget;
    +var propertyReplacer;
    +
    +function setUpPage() {
    +  someElement = goog.dom.getElement('someElement');
    +  target = goog.dom.getElement('target');
    +  viewport = goog.dom.getElement('viewport');
    +  viewportTarget = goog.dom.getElement('viewportTarget');
    +  propertyReplacer = new goog.testing.PropertyReplacer();
    +}
    +
    +
    +// One-time set up of rows formatted for the renderer.
    +var rows = [
    +  'Amanda Annie Anderson',
    +  'Frankie Manning',
    +  'Louis D Armstrong',
    +  // NOTE(user): sorry about this test input, but it has caused problems
    +  // in the past, so I want to make sure to test against it.
    +  'Foo Bar................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................',
    +  '<div><div>test</div></div>',
    +  '<div><div>test1</div><div>test2</div></div>',
    +  '<div>random test string<div>test1</div><div><div>test2</div><div>test3</div></div></div>'
    +];
    +
    +for (var i = 0; i < rows.length; i++) {
    +  rendRows.push({
    +    id: i,
    +    data: rows[i]
    +  });
    +}
    +
    +function setUp() {
    +  renderer = new goog.ui.ac.Renderer();
    +  renderer.rowDivs_ = [];
    +  renderer.target_ = target;
    +}
    +
    +function tearDown() {
    +  renderer.dispose();
    +  propertyReplacer.reset();
    +}
    +
    +function testBasicMatchingWithHtmlRow() {
    +  // '<div><div>test</div></div>'
    +  var row = rendRows[4];
    +  var token = 'te';
    +  enableHtmlRendering(renderer);
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +}
    +
    +function testShouldMatchOnlyOncePerDefaultWithComplexHtmlStrings() {
    +  // '<div><div>test1</div><div>test2</div></div>'
    +  var row = rendRows[5];
    +  var token = 'te';
    +  enableHtmlRendering(renderer);
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +
    +  // It should match and render highlighting for the first 'test1' and
    +  // stop here. This is the default behavior of the renderer.
    +  assertNumBoldTags(boldTagElArray, 1);
    +}
    +
    +function testShouldMatchMultipleTimesWithComplexHtmlStrings() {
    +  renderer.setHighlightAllTokens(true);
    +
    +  // '<div><div>test1</div><div>test2</div></div>'
    +  var row = rendRows[5];
    +  var token = 'te';
    +  enableHtmlRendering(renderer);
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +
    +  // It should match and render highlighting for both 'test1' and 'test2'.
    +  assertNumBoldTags(boldTagElArray, 2);
    +
    +  // Try again with a more complex HTML string.
    +  // '<div>random test string<div>test1</div><div><div>test2</div><div>test3</div></div></div>'
    +  row = rendRows[6];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  // It should match 'test', 'test1', 'test2' and 'test3' wherever they are in the
    +  // DOM tree.
    +  assertNumBoldTags(boldTagElArray, 4);
    +}
    +
    +function testBasicStringTokenHighlightingUsingUniversalMatching() {
    +  var row = rendRows[0];  // 'Amanda Annie Anderson'
    +  renderer.setMatchWordBoundary(false);
    +
    +  // Should highlight first match only.
    +  var token = 'A';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'A');
    +  assertLastNodeText(node, 'manda Annie Anderson');
    +
    +  // Match should be case insensitive, and should match tokens in the
    +  // middle of words if useWordMatching is turned off ("an" in Amanda).
    +  var token = 'an';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Am');
    +  assertHighlightedText(boldTagElArray[0], 'an');
    +  assertLastNodeText(node, 'da Annie Anderson');
    +
    +  // Should only match on non-empty strings.
    +  token = '';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Should not match leading whitespace.
    +  token = ' an';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +}
    +
    +function testBasicStringTokenHighlighting() {
    +  var row = rendRows[0];  // 'Amanda Annie Anderson'
    +
    +  // Should highlight first match only.
    +  var token = 'A';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'A');
    +  assertLastNodeText(node, 'manda Annie Anderson');
    +
    +  // Should only match on non-empty strings.
    +  token = '';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Match should be case insensitive, and should not match tokens in the
    +  // middle of words ("an" in Amanda).
    +  token = 'an';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    +  assertHighlightedText(boldTagElArray[0], 'An');
    +  assertLastNodeText(node, 'nie Anderson');
    +
    +  // Should not match whitespace.
    +  token = ' ';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Should not match leading whitespace since all matches are at the start of
    +  // word boundaries.
    +  token = ' an';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Should match trailing whitespace.
    +  token = 'annie ';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    +  assertHighlightedText(boldTagElArray[0], 'Annie ');
    +  assertLastNodeText(node, 'Anderson');
    +
    +  // Should match across whitespace.
    +  row = rendRows[2]; // 'Louis D Armstrong'
    +  token = 'd a';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Louis ');
    +  assertHighlightedText(boldTagElArray[0], 'D A');
    +  assertLastNodeText(node, 'rmstrong');
    +
    +  // Should match the last token.
    +  token = 'aRmStRoNg';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Louis D ');
    +  assertHighlightedText(boldTagElArray[0], 'Armstrong');
    +  assertLastNodeText(node, '');
    +}
    +
    +// The name of this function is fortuitous, in that it gets tested
    +// last on FF. The lazy regexp on FF is particularly slow, and causes
    +// the test to take a long time, and sometimes time out when run on forge
    +// because it triggers the test runner to go back to the event loop...
    +function testPathologicalInput() {
    +  // Should not hang on bizarrely long strings
    +  var row = rendRows[3]; // pathological row
    +  var token = 'foo';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertHighlightedText(boldTagElArray[0], 'Foo');
    +  assert(goog.string.startsWith(
    +      boldTagElArray[0].nextSibling.nodeValue, ' Bar...'));
    +}
    +
    +function testBasicArrayTokenHighlighting() {
    +  var row = rendRows[1];  // 'Frankie Manning'
    +
    +  // Only the first match in the array should be highlighted.
    +  var token = ['f', 'm'];
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'F');
    +  assertLastNodeText(node, 'rankie Manning');
    +
    +  // Only the first match in the array should be highlighted.
    +  token = ['m', 'f'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Frankie ');
    +  assertHighlightedText(boldTagElArray[0], 'M');
    +  assertLastNodeText(node, 'anning');
    +
    +  // Skip tokens that do not match.
    +  token = ['asdf', 'f'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'F');
    +  assertLastNodeText(node, 'rankie Manning');
    +
    +  // Highlight nothing if no tokens match.
    +  token = ['Foo', 'bar', 'baz'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Frankie Manning');
    +
    +  // Empty array should not match.
    +  token = [];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Frankie Manning');
    +
    +  // Empty string in array should not match.
    +  token = [''];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Frankie Manning');
    +
    +  // Whitespace in array should not match.
    +  token = [' '];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Frankie Manning');
    +
    +  // Whitespace entries in array should not match.
    +  token = [' ', 'man'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Frankie ');
    +  assertHighlightedText(boldTagElArray[0], 'Man');
    +  assertLastNodeText(node, 'ning');
    +
    +  // Whitespace in array entry should match as a whole token.
    +  row = rendRows[2]; // 'Louis D Armstrong'
    +  token = ['d arm', 'lou'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Louis ');
    +  assertHighlightedText(boldTagElArray[0], 'D Arm');
    +  assertLastNodeText(node, 'strong');
    +}
    +
    +function testHighlightAllTokensSingleTokenHighlighting() {
    +  renderer.setHighlightAllTokens(true);
    +  var row = rendRows[0];  // 'Amanda Annie Anderson'
    +
    +  // All matches at the start of the word should be highlighted when
    +  // highlightAllTokens is set.
    +  var token = 'a';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 3);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'A');
    +  assertPreviousNodeText(boldTagElArray[1], 'manda ');
    +  assertHighlightedText(boldTagElArray[1], 'A');
    +  assertPreviousNodeText(boldTagElArray[2], 'nnie ');
    +  assertHighlightedText(boldTagElArray[2], 'A');
    +  assertLastNodeText(node, 'nderson');
    +
    +  // Should not match on empty string.
    +  token = '';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Match should be case insensitive.
    +  token = 'AN';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 2);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    +  assertHighlightedText(boldTagElArray[0], 'An');
    +  assertPreviousNodeText(boldTagElArray[1], 'nie ');
    +  assertHighlightedText(boldTagElArray[1], 'An');
    +  assertLastNodeText(node, 'derson');
    +
    +  // Should not match on whitespace.
    +  token = ' ';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // When highlighting all tokens, should match despite leading whitespace.
    +  token = '  am';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'Am');
    +  assertLastNodeText(node, 'anda Annie Anderson');
    +
    +  // Should match with trailing whitepsace.
    +  token = 'ann   ';
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    +  assertHighlightedText(boldTagElArray[0], 'Ann');
    +  assertLastNodeText(node, 'ie Anderson');
    +}
    +
    +function testHighlightAllTokensMultipleStringTokenHighlighting() {
    +  renderer.setHighlightAllTokens(true);
    +  var row = rendRows[1];  // 'Frankie Manning'
    +
    +  // Each individual space-separated token should match.
    +  var token = 'm F';
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 2);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'F');
    +  assertPreviousNodeText(boldTagElArray[1], 'rankie ');
    +  assertHighlightedText(boldTagElArray[1], 'M');
    +  assertLastNodeText(node, 'anning');
    +}
    +
    +function testHighlightAllTokensArrayTokenHighlighting() {
    +  renderer.setHighlightAllTokens(true);
    +  var row = rendRows[0];  // 'Amanda Annie Anderson'
    +
    +  // All tokens in the array should match.
    +  var token = ['AM', 'AN'];
    +  var node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 3);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'Am');
    +  assertPreviousNodeText(boldTagElArray[1], 'anda ');
    +  assertHighlightedText(boldTagElArray[1], 'An');
    +  assertPreviousNodeText(boldTagElArray[2], 'nie ');
    +  assertHighlightedText(boldTagElArray[2], 'An');
    +  assertLastNodeText(node, 'derson');
    +
    +  // Empty array should not match.
    +  token = [];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Empty string in array should not match.
    +  token = [''];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Whitespace in array should not match.
    +  token = [' '];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 0);
    +  assertLastNodeText(node, 'Amanda Annie Anderson');
    +
    +  // Empty string entries in array should not match.
    +  token = ['', 'Ann'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    +  assertHighlightedText(boldTagElArray[0], 'Ann');
    +  assertLastNodeText(node, 'ie Anderson');
    +
    +  // Whitespace entries in array should not match.
    +  token = [' ', 'And'];
    +  node = renderer.renderRowHtml(row, token);
    +  boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 1);
    +  assertPreviousNodeText(boldTagElArray[0], 'Amanda Annie ');
    +  assertHighlightedText(boldTagElArray[0], 'And');
    +  assertLastNodeText(node, 'erson');
    +
    +  // Whitespace in array entry should match as a whole token.
    +  token = ['annie a', 'Am'];
    +  node = renderer.renderRowHtml(row, token);
    +  var boldTagElArray = node.getElementsByTagName('b');
    +  assertNumBoldTags(boldTagElArray, 2);
    +  assertPreviousNodeText(boldTagElArray[0], '');
    +  assertHighlightedText(boldTagElArray[0], 'Am');
    +  assertPreviousNodeText(boldTagElArray[1], 'anda ');
    +  assertHighlightedText(boldTagElArray[1], 'Annie A');
    +  assertLastNodeText(node, 'nderson');
    +}
    +
    +function testMenuFadeDuration() {
    +  var hideCalled = false;
    +  var hideAnimCalled = false;
    +  var showCalled = false;
    +  var showAnimCalled = false;
    +
    +  propertyReplacer.set(goog.style, 'setElementShown', function(el, state) {
    +    if (state) {
    +      showCalled = true;
    +    } else {
    +      hideCalled = true;
    +    }
    +  });
    +
    +  propertyReplacer.set(goog.fx.dom.FadeInAndShow.prototype, 'play',
    +      function() {
    +        showAnimCalled = true;
    +      });
    +
    +  propertyReplacer.set(goog.fx.dom.FadeOutAndHide.prototype, 'play',
    +      function() {
    +        hideAnimCalled = true;
    +      });
    +
    +  // Default behavior does show/hide but not animations.
    +
    +  renderer.show();
    +  assertTrue(showCalled);
    +  assertFalse(showAnimCalled);
    +
    +  renderer.dismiss();
    +  assertTrue(hideCalled);
    +  assertFalse(hideAnimCalled);
    +
    +  // But animations can be turned on.
    +
    +  showCalled = false;
    +  hideCalled = false;
    +  renderer.setMenuFadeDuration(100);
    +
    +  renderer.show();
    +  assertFalse(showCalled);
    +  assertTrue(showAnimCalled);
    +
    +  renderer.dismiss();
    +  assertFalse(hideCalled);
    +  assertTrue(hideAnimCalled);
    +}
    +
    +function testAriaTags() {
    +  renderer.maybeCreateElement_();
    +
    +  assertNotNull(target);
    +  assertEvaluatesToFalse('The role should be empty.',
    +      goog.a11y.aria.getRole(target));
    +  assertEquals('',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.AUTOCOMPLETE));
    +  assertEquals('',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.HASPOPUP));
    +
    +  renderer.show();
    +  assertEquals(goog.a11y.aria.Role.COMBOBOX, goog.a11y.aria.getRole(
    +      target));
    +  assertEquals('list',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.AUTOCOMPLETE));
    +  assertEquals('true',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.HASPOPUP));
    +
    +  renderer.dismiss();
    +  assertEquals(goog.a11y.aria.Role.COMBOBOX, goog.a11y.aria.getRole(
    +      target));
    +  assertEquals('list',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.AUTOCOMPLETE));
    +  assertEquals('false',
    +      goog.a11y.aria.getState(target,
    +          goog.a11y.aria.State.HASPOPUP));
    +}
    +
    +function testHiliteRowWithDefaultRenderer() {
    +  renderer.renderRows(rendRows, '');
    +  renderer.hiliteRow(2);
    +  assertEquals(2, renderer.hilitedRow_);
    +  assertTrue(goog.dom.classlist.contains(
    +      renderer.rowDivs_[2], renderer.activeClassName));
    +}
    +
    +function testHiliteRowWithCustomRenderer() {
    +  goog.dispose(renderer);
    +
    +  // Use a custom renderer that doesn't put the result divs as direct children
    +  // of this.element_.
    +  var customRenderer = {
    +    render: function(renderer, element, rows, token) {
    +      // Put all of the results into a results holder div that is a child of
    +      // this.element_.
    +      var resultsHolder = goog.dom.createDom('div');
    +      goog.dom.appendChild(element, resultsHolder);
    +      for (var i = 0, row; row = rows[i]; ++i) {
    +        var node = renderer.renderRowHtml(row, token);
    +        goog.dom.appendChild(resultsHolder, node);
    +      }
    +    }
    +  };
    +  renderer = new goog.ui.ac.Renderer(null, customRenderer);
    +
    +  // Make sure we can still highlight the row at position 2 even though
    +  // this.element_.childNodes contains only a single child.
    +  renderer.renderRows(rendRows, '');
    +  renderer.hiliteRow(2);
    +  assertEquals(2, renderer.hilitedRow_);
    +  assertTrue(goog.dom.classlist.contains(
    +      renderer.rowDivs_[2], renderer.activeClassName));
    +}
    +
    +function testReposition() {
    +  renderer.renderRows(rendRows, '', target);
    +  var el = renderer.getElement();
    +  el.style.position = 'absolute';
    +  el.style.width = '100px';
    +
    +  renderer.setAutoPosition(true);
    +  renderer.redraw();
    +
    +  var rendererOffset = goog.style.getPageOffset(renderer.getElement());
    +  var rendererSize = goog.style.getSize(renderer.getElement());
    +  var targetOffset = goog.style.getPageOffset(target);
    +  var targetSize = goog.style.getSize(target);
    +
    +  assertEquals(0 + targetOffset.x, rendererOffset.x);
    +  assertRoughlyEquals(
    +      targetOffset.y + targetSize.height, rendererOffset.y, 1);
    +}
    +
    +function testRepositionWithRightAlign() {
    +  renderer.renderRows(rendRows, '', target);
    +  var el = renderer.getElement();
    +  el.style.position = 'absolute';
    +  el.style.width = '150px';
    +
    +  renderer.setAutoPosition(true);
    +  renderer.setRightAlign(true);
    +  renderer.redraw();
    +
    +  var rendererOffset = goog.style.getPageOffset(renderer.getElement());
    +  var rendererSize = goog.style.getSize(renderer.getElement());
    +  var targetOffset = goog.style.getPageOffset(target);
    +  var targetSize = goog.style.getSize(target);
    +
    +  assertRoughlyEquals(
    +      targetOffset.x + targetSize.width,
    +      rendererOffset.x + rendererSize.width,
    +      1);
    +  assertRoughlyEquals(
    +      targetOffset.y + targetSize.height, rendererOffset.y, 1);
    +}
    +
    +function testRepositionResizeHeight() {
    +  renderer = new goog.ui.ac.Renderer(viewport);
    +  // Render the first 4 rows from test set.
    +  renderer.renderRows(rendRows.slice(0, 4), '', viewportTarget);
    +  renderer.setAutoPosition(true);
    +  renderer.setShowScrollbarsIfTooLarge(true);
    +
    +  // Stick a huge row in the dropdown element, to make sure it won't fit in the viewport.
    +  var hugeRow = goog.dom.createDom('div', {style: 'height:1000px'});
    +  goog.dom.appendChild(renderer.getElement(), hugeRow);
    +
    +  renderer.reposition();
    +
    +  var rendererOffset = goog.style.getPageOffset(renderer.getElement());
    +  var rendererSize = goog.style.getSize(renderer.getElement());
    +  var viewportOffset = goog.style.getPageOffset(viewport);
    +  var viewportSize = goog.style.getSize(viewport);
    +
    +  assertRoughlyEquals(
    +      viewportOffset.y + viewportSize.height,
    +      rendererSize.height + rendererOffset.y,
    +      1);
    +
    +  // Remove the huge row, and make sure that the dropdown element gets shrunk.
    +  renderer.getElement().removeChild(hugeRow);
    +  renderer.reposition();
    +
    +  rendererOffset = goog.style.getPageOffset(renderer.getElement());
    +  rendererSize = goog.style.getSize(renderer.getElement());
    +  viewportOffset = goog.style.getPageOffset(viewport);
    +  viewportSize = goog.style.getSize(viewport);
    +
    +  assertTrue((rendererSize.height + rendererOffset.y) < (viewportOffset.y + viewportSize.height));
    +}
    +
    +function testHiliteEvent() {
    +  renderer.renderRows(rendRows, '');
    +
    +  var hiliteEventFired = false;
    +  goog.events.listenOnce(renderer,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function(e) {
    +        hiliteEventFired = true;
    +        assertEquals(e.row, rendRows[1].data);
    +      });
    +  renderer.hiliteRow(1);
    +  assertTrue(hiliteEventFired);
    +
    +  hiliteEventFired = false;
    +  goog.events.listenOnce(renderer,
    +      goog.ui.ac.AutoComplete.EventType.ROW_HILITE,
    +      function(e) {
    +        hiliteEventFired = true;
    +        assertNull(e.row);
    +      });
    +  renderer.hiliteRow(rendRows.length); // i.e. out of bounds.
    +  assertTrue(hiliteEventFired);
    +}
    +
    +// ------- Helper functions -------
    +
    +// The default rowRenderer will escape any HTML in the row content.
    +// Activating HTML rendering will allow HTML strings to be rendered to DOM
    +// instead of being escaped.
    +function enableHtmlRendering(renderer) {
    +  renderer.customRenderer_ = {
    +    renderRow: function(row, token, node) {
    +      node.innerHTML = row.data.toString();
    +    }
    +  };
    +}
    +
    +function assertNumBoldTags(boldTagElArray, expectedNum) {
    +  assertEquals('Incorrect number of bold tags', expectedNum,
    +      boldTagElArray.length);
    +}
    +
    +function assertPreviousNodeText(boldTag, expectedText) {
    +  var prevNode = boldTag.previousSibling;
    +  assertEquals('Expected text before the token does not match', expectedText,
    +      prevNode.nodeValue);
    +}
    +
    +function assertHighlightedText(boldTag, expectedHighlightedText) {
    +  assertEquals('Incorrect text bolded', expectedHighlightedText,
    +      boldTag.innerHTML);
    +}
    +
    +function assertLastNodeText(node, expectedText) {
    +  var lastNode = node.lastChild;
    +  assertEquals('Incorrect text in the last node', expectedText,
    +      lastNode.nodeValue);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/renderoptions.js b/mapbox-gl/closure-library/closure/goog/ui/ac/renderoptions.js
    new file mode 100644
    index 0000000000..2f99b2b16f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/renderoptions.js
    @@ -0,0 +1,80 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Options for rendering matches.
    + *
    + */
    +
    +goog.provide('goog.ui.ac.RenderOptions');
    +
    +
    +
    +/**
    + * A simple class that contains options for rendering a set of autocomplete
    + * matches.  Used as an optional argument in the callback from the matcher.
    + * @constructor
    + */
    +goog.ui.ac.RenderOptions = function() {
    +};
    +
    +
    +/**
    + * Whether the current highlighting is to be preserved when displaying the new
    + * set of matches.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ac.RenderOptions.prototype.preserveHilited_ = false;
    +
    +
    +/**
    + * Whether the first match is to be highlighted.  When undefined the autoHilite
    + * flag of the autocomplete is used.
    + * @type {boolean|undefined}
    + * @private
    + */
    +goog.ui.ac.RenderOptions.prototype.autoHilite_;
    +
    +
    +/**
    + * @param {boolean} flag The new value for the preserveHilited_ flag.
    + */
    +goog.ui.ac.RenderOptions.prototype.setPreserveHilited = function(flag) {
    +  this.preserveHilited_ = flag;
    +};
    +
    +
    +/**
    + * @return {boolean} The value of the preserveHilited_ flag.
    + */
    +goog.ui.ac.RenderOptions.prototype.getPreserveHilited = function() {
    +  return this.preserveHilited_;
    +};
    +
    +
    +/**
    + * @param {boolean} flag The new value for the autoHilite_ flag.
    + */
    +goog.ui.ac.RenderOptions.prototype.setAutoHilite = function(flag) {
    +  this.autoHilite_ = flag;
    +};
    +
    +
    +/**
    + * @return {boolean|undefined} The value of the autoHilite_ flag.
    + */
    +goog.ui.ac.RenderOptions.prototype.getAutoHilite = function() {
    +  return this.autoHilite_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/richinputhandler.js b/mapbox-gl/closure-library/closure/goog/ui/ac/richinputhandler.js
    new file mode 100644
    index 0000000000..4e8faee634
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/richinputhandler.js
    @@ -0,0 +1,58 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for managing the interactions between a rich autocomplete
    + * object and a text-input or textarea.
    + *
    + */
    +
    +goog.provide('goog.ui.ac.RichInputHandler');
    +
    +goog.require('goog.ui.ac.InputHandler');
    +
    +
    +
    +/**
    + * Class for managing the interaction between an autocomplete object and a
    + * text-input or textarea.
    + * @param {?string=} opt_separators Seperators to split multiple entries.
    + * @param {?string=} opt_literals Characters used to delimit text literals.
    + * @param {?boolean=} opt_multi Whether to allow multiple entries
    + *     (Default: true).
    + * @param {?number=} opt_throttleTime Number of milliseconds to throttle
    + *     keyevents with (Default: 150).
    + * @constructor
    + * @extends {goog.ui.ac.InputHandler}
    + */
    +goog.ui.ac.RichInputHandler = function(opt_separators, opt_literals,
    +    opt_multi, opt_throttleTime) {
    +  goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals,
    +      opt_multi, opt_throttleTime);
    +};
    +goog.inherits(goog.ui.ac.RichInputHandler, goog.ui.ac.InputHandler);
    +
    +
    +/**
    + * Selects the given rich row.  The row's select(target) method is called.
    + * @param {Object} row The row to select.
    + * @return {boolean} Whether to suppress the update event.
    + * @override
    + */
    +goog.ui.ac.RichInputHandler.prototype.selectRow = function(row) {
    +  var suppressUpdate = goog.ui.ac.RichInputHandler.superClass_
    +      .selectRow.call(this, row);
    +  row.select(this.ac_.getTarget());
    +  return suppressUpdate;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/richremote.js b/mapbox-gl/closure-library/closure/goog/ui/ac/richremote.js
    new file mode 100644
    index 0000000000..5109a1df69
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/richremote.js
    @@ -0,0 +1,107 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Factory class to create a rich autocomplete that will match
    + * from an array of data provided via ajax.  The server returns a complex data
    + * structure that is used with client-side javascript functions to render the
    + * results.
    + *
    + * The server sends a list of the form:
    + *   [["type1", {...}, {...}, ...], ["type2", {...}, {...}, ...], ...]
    + * The first element of each sublist is a string designating the type of the
    + * hashes in the sublist, each of which represents one match.  The type string
    + * must be the name of a function(item) which converts the hash into a rich
    + * row that contains both a render(node, token) and a select(target) method.
    + * The render method is called by the renderer when rendering the rich row,
    + * and the select method is called by the RichInputHandler when the rich row is
    + * selected.
    + *
    + * @see ../../demos/autocompleterichremote.html
    + */
    +
    +goog.provide('goog.ui.ac.RichRemote');
    +
    +goog.require('goog.ui.ac.AutoComplete');
    +goog.require('goog.ui.ac.Remote');
    +goog.require('goog.ui.ac.Renderer');
    +goog.require('goog.ui.ac.RichInputHandler');
    +goog.require('goog.ui.ac.RichRemoteArrayMatcher');
    +
    +
    +
    +/**
    + * Factory class to create a rich autocomplete widget that autocompletes an
    + * inputbox or textarea from data provided via ajax.  The server returns a
    + * complex data structure that is used with client-side javascript functions to
    + * render the results.
    + * @param {string} url The Uri which generates the auto complete matches.
    + * @param {Element} input Input element or text area.
    + * @param {boolean=} opt_multi Whether to allow multiple entries; defaults
    + *     to false.
    + * @param {boolean=} opt_useSimilar Whether to use similar matches; e.g.
    + *     "gost" => "ghost".
    + * @constructor
    + * @extends {goog.ui.ac.Remote}
    + */
    +goog.ui.ac.RichRemote = function(url, input, opt_multi, opt_useSimilar) {
    +  // Create a custom renderer that renders rich rows.  The renderer calls
    +  // row.render(node, token) for each row.
    +  var customRenderer = {};
    +  customRenderer.renderRow = function(row, token, node) {
    +    return row.data.render(node, token);
    +  };
    +
    +  /**
    +   * A standard renderer that uses a custom row renderer to display the
    +   * rich rows generated by this autocomplete widget.
    +   * @type {goog.ui.ac.Renderer}
    +   * @private
    +   */
    +  var renderer = new goog.ui.ac.Renderer(null, customRenderer);
    +  this.renderer_ = renderer;
    +
    +  /**
    +   * A remote matcher that parses rich results returned by the server.
    +   * @type {goog.ui.ac.RichRemoteArrayMatcher}
    +   * @private
    +   */
    +  var matcher = new goog.ui.ac.RichRemoteArrayMatcher(url,
    +      !opt_useSimilar);
    +  this.matcher_ = matcher;
    +
    +  /**
    +   * An input handler that calls select on a row when it is selected.
    +   * @type {goog.ui.ac.RichInputHandler}
    +   * @private
    +   */
    +  var inputhandler = new goog.ui.ac.RichInputHandler(null, null,
    +      !!opt_multi, 300);
    +
    +  // Create the widget and connect it to the input handler.
    +  goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
    +  inputhandler.attachAutoComplete(this);
    +  inputhandler.attachInputs(input);
    +};
    +goog.inherits(goog.ui.ac.RichRemote, goog.ui.ac.Remote);
    +
    +
    +/**
    + * Set the filter that is called before the array matches are returned.
    + * @param {Function} rowFilter A function(rows) that returns an array of rows as
    + *     a subset of the rows input array.
    + */
    +goog.ui.ac.RichRemote.prototype.setRowFilter = function(rowFilter) {
    +  this.matcher_.setRowFilter(rowFilter);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ac/richremotearraymatcher.js b/mapbox-gl/closure-library/closure/goog/ui/ac/richremotearraymatcher.js
    new file mode 100644
    index 0000000000..f7befb4e0a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ac/richremotearraymatcher.js
    @@ -0,0 +1,125 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class that retrieves rich autocomplete matches, represented as
    + * a structured list of lists, via an ajax call.  The first element of each
    + * sublist is the name of a client-side javascript function that converts the
    + * remaining sublist elements into rich rows.
    + *
    + */
    +
    +goog.provide('goog.ui.ac.RichRemoteArrayMatcher');
    +
    +goog.require('goog.json');
    +goog.require('goog.ui.ac.RemoteArrayMatcher');
    +
    +
    +
    +/**
    + * An array matcher that requests rich matches via ajax and converts them into
    + * rich rows.
    + * @param {string} url The Uri which generates the auto complete matches.  The
    + *     search term is passed to the server as the 'token' query param.
    + * @param {boolean=} opt_noSimilar If true, request that the server does not do
    + *     similarity matches for the input token against the dictionary.
    + *     The value is sent to the server as the 'use_similar' query param which is
    + *     either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true).
    + * @constructor
    + * @extends {goog.ui.ac.RemoteArrayMatcher}
    + */
    +goog.ui.ac.RichRemoteArrayMatcher = function(url, opt_noSimilar) {
    +  goog.ui.ac.RemoteArrayMatcher.call(this, url, opt_noSimilar);
    +
    +  /**
    +   * A function(rows) that is called before the array matches are returned.
    +   * It runs client-side and filters the results given by the server before
    +   * being rendered by the client.
    +   * @type {Function}
    +   * @private
    +   */
    +  this.rowFilter_ = null;
    +
    +};
    +goog.inherits(goog.ui.ac.RichRemoteArrayMatcher, goog.ui.ac.RemoteArrayMatcher);
    +
    +
    +/**
    + * Set the filter that is called before the array matches are returned.
    + * @param {Function} rowFilter A function(rows) that returns an array of rows as
    + *     a subset of the rows input array.
    + */
    +goog.ui.ac.RichRemoteArrayMatcher.prototype.setRowFilter = function(rowFilter) {
    +  this.rowFilter_ = rowFilter;
    +};
    +
    +
    +/**
    + * Retrieve a set of matching rows from the server via ajax and convert them
    + * into rich rows.
    + * @param {string} token The text that should be matched; passed to the server
    + *     as the 'token' query param.
    + * @param {number} maxMatches The maximum number of matches requested from the
    + *     server; passed as the 'max_matches' query param. The server is
    + *     responsible for limiting the number of matches that are returned.
    + * @param {Function} matchHandler Callback to execute on the result after
    + *     matching.
    + * @override
    + */
    +goog.ui.ac.RichRemoteArrayMatcher.prototype.requestMatchingRows =
    +    function(token, maxMatches, matchHandler) {
    +  // The RichRemoteArrayMatcher must map over the results and filter them
    +  // before calling the request matchHandler.  This is done by passing
    +  // myMatchHandler to RemoteArrayMatcher.requestMatchingRows which maps,
    +  // filters, and then calls matchHandler.
    +  var myMatchHandler = goog.bind(function(token, matches) {
    +    /** @preserveTry */
    +    try {
    +      var rows = [];
    +      for (var i = 0; i < matches.length; i++) {
    +        var func =  /** @type {!Function} */
    +            (goog.json.unsafeParse(matches[i][0]));
    +        for (var j = 1; j < matches[i].length; j++) {
    +          var richRow = func(matches[i][j]);
    +          rows.push(richRow);
    +
    +          // If no render function was provided, set the node's innerHTML.
    +          if (typeof richRow.render == 'undefined') {
    +            richRow.render = function(node, token) {
    +              node.innerHTML = richRow.toString();
    +            };
    +          }
    +
    +          // If no select function was provided, set the text of the input.
    +          if (typeof richRow.select == 'undefined') {
    +            richRow.select = function(target) {
    +              target.value = richRow.toString();
    +            };
    +          }
    +        }
    +      }
    +      if (this.rowFilter_) {
    +        rows = this.rowFilter_(rows);
    +      }
    +      matchHandler(token, rows);
    +    } catch (exception) {
    +      // TODO(user): Is this what we want?
    +      matchHandler(token, []);
    +    }
    +  }, this);
    +
    +  // Call the super's requestMatchingRows with myMatchHandler
    +  goog.ui.ac.RichRemoteArrayMatcher.superClass_
    +      .requestMatchingRows.call(this, token, maxMatches, myMatchHandler);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/activitymonitor.js b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor.js
    new file mode 100644
    index 0000000000..51f91dec13
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor.js
    @@ -0,0 +1,348 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Activity Monitor.
    + *
    + * Fires throttled events when a user interacts with the specified document.
    + * This class also exposes the amount of time since the last user event.
    + *
    + * If you would prefer to get BECOME_ACTIVE and BECOME_IDLE events when the
    + * user changes states, then you should use the IdleTimer class instead.
    + *
    + */
    +
    +goog.provide('goog.ui.ActivityMonitor');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +
    +
    +
    +/**
    + * Once initialized with a document, the activity monitor can be queried for
    + * the current idle time.
    + *
    + * @param {goog.dom.DomHelper|Array<goog.dom.DomHelper>=} opt_domHelper
    + *     DomHelper which contains the document(s) to listen to.  If null, the
    + *     default document is usedinstead.
    + * @param {boolean=} opt_useBubble Whether to use the bubble phase to listen for
    + *     events. By default listens on the capture phase so that it won't miss
    + *     events that get stopPropagation/cancelBubble'd. However, this can cause
    + *     problems in IE8 if the page loads multiple scripts that include the
    + *     closure event handling code.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.ActivityMonitor = function(opt_domHelper, opt_useBubble) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Array of documents that are being listened to.
    +   * @type {Array<Document>}
    +   * @private
    +   */
    +  this.documents_ = [];
    +
    +  /**
    +   * Whether to use the bubble phase to listen for events.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useBubble_ = !!opt_useBubble;
    +
    +  /**
    +   * The event handler.
    +   * @type {goog.events.EventHandler<!goog.ui.ActivityMonitor>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Whether the current window is an iframe.
    +   * TODO(user): Move to goog.dom.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isIframe_ = window.parent != window;
    +
    +  if (!opt_domHelper) {
    +    this.addDocument(goog.dom.getDomHelper().getDocument());
    +  } else if (goog.isArray(opt_domHelper)) {
    +    for (var i = 0; i < opt_domHelper.length; i++) {
    +      this.addDocument(opt_domHelper[i].getDocument());
    +    }
    +  } else {
    +    this.addDocument(opt_domHelper.getDocument());
    +  }
    +
    +  /**
    +   * The time (in milliseconds) of the last user event.
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastEventTime_ = goog.now();
    +
    +};
    +goog.inherits(goog.ui.ActivityMonitor, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.ActivityMonitor);
    +
    +
    +/**
    + * The last event type that was detected.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ActivityMonitor.prototype.lastEventType_ = '';
    +
    +
    +/**
    + * The mouse x-position after the last user event.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ActivityMonitor.prototype.lastMouseX_;
    +
    +
    +/**
    + * The mouse y-position after the last user event.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ActivityMonitor.prototype.lastMouseY_;
    +
    +
    +/**
    + * The earliest time that another throttled ACTIVITY event will be dispatched
    + * @type {number}
    + * @private
    + */
    +goog.ui.ActivityMonitor.prototype.minEventTime_ = 0;
    +
    +
    +/**
    + * Minimum amount of time in ms between throttled ACTIVITY events
    + * @type {number}
    + */
    +goog.ui.ActivityMonitor.MIN_EVENT_SPACING = 3 * 1000;
    +
    +
    +/**
    + * If a user executes one of these events, s/he is considered not idle.
    + * @type {Array<goog.events.EventType>}
    + * @private
    + */
    +goog.ui.ActivityMonitor.userEventTypesBody_ = [
    +  goog.events.EventType.CLICK,
    +  goog.events.EventType.DBLCLICK,
    +  goog.events.EventType.MOUSEDOWN,
    +  goog.events.EventType.MOUSEMOVE,
    +  goog.events.EventType.MOUSEUP
    +];
    +
    +
    +/**
    + * If a user executes one of these events, s/he is considered not idle.
    + * Note: monitoring touch events within iframe cause problems in iOS.
    + * @type {Array<goog.events.EventType>}
    + * @private
    + */
    +goog.ui.ActivityMonitor.userTouchEventTypesBody_ = [
    +  goog.events.EventType.TOUCHEND,
    +  goog.events.EventType.TOUCHMOVE,
    +  goog.events.EventType.TOUCHSTART
    +];
    +
    +
    +/**
    + * If a user executes one of these events, s/he is considered not idle.
    + * @type {Array<goog.events.EventType>}
    + * @private
    + */
    +goog.ui.ActivityMonitor.userEventTypesDocuments_ =
    +    [goog.events.EventType.KEYDOWN, goog.events.EventType.KEYUP];
    +
    +
    +/**
    + * Event constants for the activity monitor.
    + * @enum {string}
    + */
    +goog.ui.ActivityMonitor.Event = {
    +  /** Event fired when the user does something interactive */
    +  ACTIVITY: 'activity'
    +};
    +
    +
    +/** @override */
    +goog.ui.ActivityMonitor.prototype.disposeInternal = function() {
    +  goog.ui.ActivityMonitor.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +  delete this.documents_;
    +};
    +
    +
    +/**
    + * Adds a document to those being monitored by this class.
    + *
    + * @param {Document} doc Document to monitor.
    + */
    +goog.ui.ActivityMonitor.prototype.addDocument = function(doc) {
    +  if (goog.array.contains(this.documents_, doc)) {
    +    return;
    +  }
    +  this.documents_.push(doc);
    +  var useCapture = !this.useBubble_;
    +
    +  var eventsToListenTo = goog.array.concat(
    +      goog.ui.ActivityMonitor.userEventTypesDocuments_,
    +      goog.ui.ActivityMonitor.userEventTypesBody_);
    +
    +  if (!this.isIframe_) {
    +    // Monitoring touch events in iframe causes problems interacting with text
    +    // fields in iOS (input text, textarea, contenteditable, select/copy/paste),
    +    // so just ignore these events. This shouldn't matter much given that a
    +    // touchstart event followed by touchend event produces a click event,
    +    // which is being monitored correctly.
    +    goog.array.extend(eventsToListenTo,
    +        goog.ui.ActivityMonitor.userTouchEventTypesBody_);
    +  }
    +
    +  this.eventHandler_.listen(doc, eventsToListenTo, this.handleEvent_,
    +      useCapture);
    +};
    +
    +
    +/**
    + * Removes a document from those being monitored by this class.
    + *
    + * @param {Document} doc Document to monitor.
    + */
    +goog.ui.ActivityMonitor.prototype.removeDocument = function(doc) {
    +  if (this.isDisposed()) {
    +    return;
    +  }
    +  goog.array.remove(this.documents_, doc);
    +  var useCapture = !this.useBubble_;
    +
    +  var eventsToUnlistenTo = goog.array.concat(
    +      goog.ui.ActivityMonitor.userEventTypesDocuments_,
    +      goog.ui.ActivityMonitor.userEventTypesBody_);
    +
    +  if (!this.isIframe_) {
    +    // See note above about monitoring touch events in iframe.
    +    goog.array.extend(eventsToUnlistenTo,
    +        goog.ui.ActivityMonitor.userTouchEventTypesBody_);
    +  }
    +
    +  this.eventHandler_.unlisten(doc, eventsToUnlistenTo, this.handleEvent_,
    +      useCapture);
    +};
    +
    +
    +/**
    + * Updates the last event time when a user action occurs.
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @private
    + */
    +goog.ui.ActivityMonitor.prototype.handleEvent_ = function(e) {
    +  var update = false;
    +  switch (e.type) {
    +    case goog.events.EventType.MOUSEMOVE:
    +      // In FF 1.5, we get spurious mouseover and mouseout events when the UI
    +      // redraws. We only want to update the idle time if the mouse has moved.
    +      if (typeof this.lastMouseX_ == 'number' &&
    +          this.lastMouseX_ != e.clientX ||
    +          typeof this.lastMouseY_ == 'number' &&
    +          this.lastMouseY_ != e.clientY) {
    +        update = true;
    +      }
    +      this.lastMouseX_ = e.clientX;
    +      this.lastMouseY_ = e.clientY;
    +      break;
    +    default:
    +      update = true;
    +  }
    +
    +  if (update) {
    +    var type = goog.asserts.assertString(e.type);
    +    this.updateIdleTime(goog.now(), type);
    +  }
    +};
    +
    +
    +/**
    + * Updates the last event time to be the present time, useful for non-DOM
    + * events that should update idle time.
    + */
    +goog.ui.ActivityMonitor.prototype.resetTimer = function() {
    +  this.updateIdleTime(goog.now(), 'manual');
    +};
    +
    +
    +/**
    + * Updates the idle time and fires an event if time has elapsed since
    + * the last update.
    + * @param {number} eventTime Time (in MS) of the event that cleared the idle
    + *     timer.
    + * @param {string} eventType Type of the event, used only for debugging.
    + * @protected
    + */
    +goog.ui.ActivityMonitor.prototype.updateIdleTime = function(
    +    eventTime, eventType) {
    +  // update internal state noting whether the user was idle
    +  this.lastEventTime_ = eventTime;
    +  this.lastEventType_ = eventType;
    +
    +  // dispatch event
    +  if (eventTime > this.minEventTime_) {
    +    this.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY);
    +    this.minEventTime_ = eventTime + goog.ui.ActivityMonitor.MIN_EVENT_SPACING;
    +  }
    +};
    +
    +
    +/**
    + * Returns the amount of time the user has been idle.
    + * @param {number=} opt_now The current time can optionally be passed in for the
    + *     computation to avoid an extra Date allocation.
    + * @return {number} The amount of time in ms that the user has been idle.
    + */
    +goog.ui.ActivityMonitor.prototype.getIdleTime = function(opt_now) {
    +  var now = opt_now || goog.now();
    +  return now - this.lastEventTime_;
    +};
    +
    +
    +/**
    + * Returns the type of the last user event.
    + * @return {string} event type.
    + */
    +goog.ui.ActivityMonitor.prototype.getLastEventType = function() {
    +  return this.lastEventType_;
    +};
    +
    +
    +/**
    + * Returns the time of the last event
    + * @return {number} last event time.
    + */
    +goog.ui.ActivityMonitor.prototype.getLastEventTime = function() {
    +  return this.lastEventTime_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.html b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.html
    new file mode 100644
    index 0000000000..582d6aed98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ActivityMonitor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ActivityMonitorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="mydiv">
    +   foo
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.js b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.js
    new file mode 100644
    index 0000000000..a3b8f9aaca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/activitymonitor_test.js
    @@ -0,0 +1,147 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ActivityMonitorTest');
    +goog.setTestOnly('goog.ui.ActivityMonitorTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.ActivityMonitor');
    +
    +var mockClock;
    +var stubs = new goog.testing.PropertyReplacer();
    +var mydiv;
    +
    +function setUp() {
    +  mydiv = document.getElementById('mydiv');
    +  mockClock = new goog.testing.MockClock(true);
    +  // ActivityMonitor initializes a private to 0 which it compares to now(),
    +  // so tests fail unless we start the mock clock with something besides 0.
    +  mockClock.tick(1);
    +}
    +
    +function tearDown() {
    +  mockClock.dispose();
    +  stubs.reset();
    +}
    +
    +function testIdle() {
    +  var activityMonitor = new goog.ui.ActivityMonitor();
    +  assertEquals('Upon creation, last event time should be creation time',
    +      mockClock.getCurrentTime(), activityMonitor.getLastEventTime());
    +
    +  mockClock.tick(1000);
    +  activityMonitor.resetTimer();
    +  var resetTime = mockClock.getCurrentTime();
    +  assertEquals('Upon reset, last event time should be reset time',
    +      resetTime, activityMonitor.getLastEventTime());
    +  assertEquals('Upon reset, idle time should be zero',
    +      0, activityMonitor.getIdleTime());
    +
    +  mockClock.tick(1000);
    +  assertEquals('1s after reset, last event time should be reset time',
    +      resetTime, activityMonitor.getLastEventTime());
    +  assertEquals('1s after reset, idle time should be 1s',
    +      1000, activityMonitor.getIdleTime());
    +}
    +
    +function testEventFired() {
    +  var activityMonitor = new goog.ui.ActivityMonitor();
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listen(activityMonitor, goog.ui.ActivityMonitor.Event.ACTIVITY,
    +                     listener);
    +
    +  mockClock.tick(1000);
    +  goog.testing.events.fireClickEvent(mydiv);
    +  assertEquals('Activity event should fire when click happens after creation',
    +               1, listener.getCallCount());
    +
    +  mockClock.tick(3000);
    +  goog.testing.events.fireClickEvent(mydiv);
    +  assertEquals('Activity event should not fire when click happens 3s or ' +
    +               'less since the last activity', 1, listener.getCallCount());
    +
    +  mockClock.tick(1);
    +  goog.testing.events.fireClickEvent(mydiv);
    +  assertEquals('Activity event should fire when click happens more than ' +
    +               '3s since the last activity', 2, listener.getCallCount());
    +}
    +
    +function testEventFiredWhenPropagationStopped() {
    +  var activityMonitor = new goog.ui.ActivityMonitor();
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listen(activityMonitor, goog.ui.ActivityMonitor.Event.ACTIVITY,
    +                     listener);
    +
    +  goog.events.listenOnce(mydiv, goog.events.EventType.CLICK,
    +                         goog.events.Event.stopPropagation);
    +  goog.testing.events.fireClickEvent(mydiv);
    +  assertEquals('Activity event should fire despite click propagation ' +
    +      'stopped because listening on capture', 1, listener.getCallCount());
    +}
    +
    +function testEventNotFiredWhenPropagationStopped() {
    +  var activityMonitor = new goog.ui.ActivityMonitor(undefined, true);
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listen(activityMonitor, goog.ui.ActivityMonitor.Event.ACTIVITY,
    +                     listener);
    +
    +  goog.events.listenOnce(mydiv, goog.events.EventType.CLICK,
    +                         goog.events.Event.stopPropagation);
    +  goog.testing.events.fireClickEvent(mydiv);
    +  assertEquals('Activity event should not fire since click propagation ' +
    +      'stopped and listening on bubble', 0, listener.getCallCount());
    +}
    +
    +function testTouchSequenceFired() {
    +  var activityMonitor = new goog.ui.ActivityMonitor();
    +  var listener = goog.testing.recordFunction();
    +  goog.events.listen(activityMonitor, goog.ui.ActivityMonitor.Event.ACTIVITY,
    +                     listener);
    +
    +  mockClock.tick(1000);
    +  goog.testing.events.fireTouchSequence(mydiv);
    +  assertEquals('Activity event should fire when touch happens after creation',
    +               1, listener.getCallCount());
    +
    +  mockClock.tick(3000);
    +  goog.testing.events.fireTouchSequence(mydiv);
    +  assertEquals('Activity event should not fire when touch happens 3s or ' +
    +               'less since the last activity', 1, listener.getCallCount());
    +
    +  mockClock.tick(1);
    +  goog.testing.events.fireTouchSequence(mydiv);
    +  assertEquals('Activity event should fire when touch happens more than ' +
    +               '3s since the last activity', 2, listener.getCallCount());
    +}
    +
    +function testAddDocument_duplicate() {
    +  var defaultDoc = goog.dom.getDomHelper().getDocument();
    +  var activityMonitor = new goog.ui.ActivityMonitor();
    +  assertEquals(1, activityMonitor.documents_.length);
    +  assertEquals(defaultDoc, activityMonitor.documents_[0]);
    +  var listenerCount = activityMonitor.eventHandler_.getListenerCount();
    +
    +  activityMonitor.addDocument(defaultDoc);
    +  assertEquals(1, activityMonitor.documents_.length);
    +  assertEquals(defaultDoc, activityMonitor.documents_[0]);
    +  assertEquals(listenerCount, activityMonitor.eventHandler_.getListenerCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip.js b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip.js
    new file mode 100644
    index 0000000000..d7b9e9d93a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip.js
    @@ -0,0 +1,367 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Advanced tooltip widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/advancedtooltip.html
    + */
    +
    +goog.provide('goog.ui.AdvancedTooltip');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.ui.Tooltip');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Advanced tooltip widget with cursor tracking abilities. Works like a regular
    + * tooltip but can track the cursor position and direction to determine if the
    + * tooltip should be dismissed or remain open.
    + *
    + * @param {Element|string=} opt_el Element to display tooltip for, either
    + *     element reference or string id.
    + * @param {?string=} opt_str Text message to display in tooltip.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Tooltip}
    + */
    +goog.ui.AdvancedTooltip = function(opt_el, opt_str, opt_domHelper) {
    +  goog.ui.Tooltip.call(this, opt_el, opt_str, opt_domHelper);
    +};
    +goog.inherits(goog.ui.AdvancedTooltip, goog.ui.Tooltip);
    +goog.tagUnsealableClass(goog.ui.AdvancedTooltip);
    +
    +
    +/**
    + * Whether to track the cursor and thereby close the tooltip if it moves away
    + * from the tooltip and keep it open if it moves towards it.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.cursorTracking_ = false;
    +
    +
    +/**
    + * Delay in milliseconds before tooltips are hidden if cursor tracking is
    + * enabled and the cursor is moving away from the tooltip.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.cursorTrackingHideDelayMs_ = 100;
    +
    +
    +/**
    + * Box object representing a margin around the tooltip where the cursor is
    + * allowed without dismissing the tooltip.
    + *
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.hotSpotPadding_;
    +
    +
    +/**
    + * Bounding box.
    + *
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.boundingBox_;
    +
    +
    +/**
    + * Anchor bounding box.
    + *
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.anchorBox_;
    +
    +
    +/**
    + * Whether the cursor tracking is active.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.tracking_ = false;
    +
    +
    +/**
    + * Sets margin around the tooltip where the cursor is allowed without dismissing
    + * the tooltip.
    + *
    + * @param {goog.math.Box=} opt_box The margin around the tooltip.
    + */
    +goog.ui.AdvancedTooltip.prototype.setHotSpotPadding = function(opt_box) {
    +  this.hotSpotPadding_ = opt_box || null;
    +};
    +
    +
    +/**
    + * @return {goog.math.Box} box The margin around the tooltip where the cursor is
    + *     allowed without dismissing the tooltip.
    + */
    +goog.ui.AdvancedTooltip.prototype.getHotSpotPadding = function() {
    +  return this.hotSpotPadding_;
    +};
    +
    +
    +/**
    + * Sets whether to track the cursor and thereby close the tooltip if it moves
    + * away from the tooltip and keep it open if it moves towards it.
    + *
    + * @param {boolean} b Whether to track the cursor.
    + */
    +goog.ui.AdvancedTooltip.prototype.setCursorTracking = function(b) {
    +  this.cursorTracking_ = b;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to track the cursor and thereby close the tooltip
    + *     if it moves away from the tooltip and keep it open if it moves towards
    + *     it.
    + */
    +goog.ui.AdvancedTooltip.prototype.getCursorTracking = function() {
    +  return this.cursorTracking_;
    +};
    +
    +
    +/**
    + * Sets delay in milliseconds before tooltips are hidden if cursor tracking is
    + * enabled and the cursor is moving away from the tooltip.
    + *
    + * @param {number} delay The delay in milliseconds.
    + */
    +goog.ui.AdvancedTooltip.prototype.setCursorTrackingHideDelayMs =
    +    function(delay) {
    +  this.cursorTrackingHideDelayMs_ = delay;
    +};
    +
    +
    +/**
    + * @return {number} The delay in milliseconds before tooltips are hidden if
    + *     cursor tracking is enabled and the cursor is moving away from the
    + *     tooltip.
    + */
    +goog.ui.AdvancedTooltip.prototype.getCursorTrackingHideDelayMs = function() {
    +  return this.cursorTrackingHideDelayMs_;
    +};
    +
    +
    +/**
    + * Called after the popup is shown.
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.onShow_ = function() {
    +  goog.ui.AdvancedTooltip.superClass_.onShow_.call(this);
    +
    +  this.boundingBox_ = goog.style.getBounds(this.getElement()).toBox();
    +  if (this.anchor) {
    +    this.anchorBox_ = goog.style.getBounds(this.anchor).toBox();
    +  }
    +
    +  this.tracking_ = this.cursorTracking_;
    +  goog.events.listen(this.getDomHelper().getDocument(),
    +                     goog.events.EventType.MOUSEMOVE,
    +                     this.handleMouseMove, false, this);
    +};
    +
    +
    +/**
    + * Called after the popup is hidden.
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.onHide_ = function() {
    +  goog.events.unlisten(this.getDomHelper().getDocument(),
    +                       goog.events.EventType.MOUSEMOVE,
    +                       this.handleMouseMove, false, this);
    +
    +  this.boundingBox_ = null;
    +  this.anchorBox_ = null;
    +  this.tracking_ = false;
    +
    +  goog.ui.AdvancedTooltip.superClass_.onHide_.call(this);
    +};
    +
    +
    +/**
    + * Returns true if the mouse is in the tooltip.
    + * @return {boolean} True if the mouse is in the tooltip.
    + */
    +goog.ui.AdvancedTooltip.prototype.isMouseInTooltip = function() {
    +  return this.isCoordinateInTooltip(this.cursorPosition);
    +};
    +
    +
    +/**
    + * Checks whether the supplied coordinate is inside the tooltip, including
    + * padding if any.
    + * @param {goog.math.Coordinate} coord Coordinate being tested.
    + * @return {boolean} Whether the coord is in the tooltip.
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.isCoordinateInTooltip = function(coord) {
    +  // Check if coord is inside the bounding box of the tooltip
    +  if (this.hotSpotPadding_) {
    +    var offset = goog.style.getPageOffset(this.getElement());
    +    var size = goog.style.getSize(this.getElement());
    +    return offset.x - this.hotSpotPadding_.left <= coord.x &&
    +        coord.x <= offset.x + size.width + this.hotSpotPadding_.right &&
    +        offset.y - this.hotSpotPadding_.top <= coord.y &&
    +        coord.y <= offset.y + size.height + this.hotSpotPadding_.bottom;
    +  }
    +
    +  return goog.ui.AdvancedTooltip.superClass_.isCoordinateInTooltip.call(this,
    +                                                                        coord);
    +};
    +
    +
    +/**
    + * Checks if supplied coordinate is in the tooltip, its triggering anchor, or
    + * a tooltip that has been triggered by a child of this tooltip.
    + * Called from handleMouseMove to determine if hide timer should be started,
    + * and from maybeHide to determine if tooltip should be hidden.
    + * @param {goog.math.Coordinate} coord Coordinate being tested.
    + * @return {boolean} Whether coordinate is in the anchor, the tooltip, or any
    + *     tooltip whose anchor is a child of this tooltip.
    + * @private
    + */
    +goog.ui.AdvancedTooltip.prototype.isCoordinateActive_ = function(coord) {
    +  if ((this.anchorBox_ && this.anchorBox_.contains(coord)) ||
    +      this.isCoordinateInTooltip(coord)) {
    +    return true;
    +  }
    +
    +  // Check if mouse might be in active child element.
    +  var childTooltip = this.getChildTooltip();
    +  return !!childTooltip && childTooltip.isCoordinateInTooltip(coord);
    +};
    +
    +
    +/**
    + * Called by timer from mouse out handler. Hides tooltip if cursor is still
    + * outside element and tooltip.
    + * @param {Element} el Anchor when hide timer was started.
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.maybeHide = function(el) {
    +  this.hideTimer = undefined;
    +  if (el == this.anchor) {
    +    // Check if cursor is inside the bounding box of the tooltip or the element
    +    // that triggered it, or if tooltip is active (possibly due to receiving
    +    // the focus), or if there is a nested tooltip being shown.
    +    if (!this.isCoordinateActive_(this.cursorPosition) &&
    +        !this.getActiveElement() &&
    +        !this.hasActiveChild()) {
    +      // Under certain circumstances gecko fires ghost mouse events with the
    +      // coordinates 0, 0 regardless of the cursors position.
    +      if (goog.userAgent.GECKO && this.cursorPosition.x == 0 &&
    +          this.cursorPosition.y == 0) {
    +        return;
    +      }
    +      this.setVisible(false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for mouse move events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.handleMouseMove = function(event) {
    +  var startTimer = this.isVisible();
    +  if (this.boundingBox_) {
    +    var scroll = this.getDomHelper().getDocumentScroll();
    +    var c = new goog.math.Coordinate(event.clientX + scroll.x,
    +        event.clientY + scroll.y);
    +    if (this.isCoordinateActive_(c)) {
    +      startTimer = false;
    +    } else if (this.tracking_) {
    +      var prevDist = goog.math.Box.distance(this.boundingBox_,
    +          this.cursorPosition);
    +      var currDist = goog.math.Box.distance(this.boundingBox_, c);
    +      startTimer = currDist >= prevDist;
    +    }
    +  }
    +
    +  if (startTimer) {
    +    this.startHideTimer();
    +
    +    // Even though the mouse coordinate is not on the tooltip (or nested child),
    +    // they may have an active element because of a focus event.  Don't let
    +    // that prevent us from taking down the tooltip(s) on this mouse move.
    +    this.setActiveElement(null);
    +    var childTooltip = this.getChildTooltip();
    +    if (childTooltip) {
    +      childTooltip.setActiveElement(null);
    +    }
    +  } else if (this.getState() == goog.ui.Tooltip.State.WAITING_TO_HIDE) {
    +    this.clearHideTimer();
    +  }
    +
    +  goog.ui.AdvancedTooltip.superClass_.handleMouseMove.call(this, event);
    +};
    +
    +
    +/**
    + * Handler for mouse over events for the tooltip element.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.handleTooltipMouseOver = function(event) {
    +  if (this.getActiveElement() != this.getElement()) {
    +    this.tracking_ = false;
    +    this.setActiveElement(this.getElement());
    +  }
    +};
    +
    +
    +/**
    + * Override hide delay with cursor tracking hide delay while tracking.
    + * @return {number} Hide delay to use.
    + * @override
    + */
    +goog.ui.AdvancedTooltip.prototype.getHideDelayMs = function() {
    +  return this.tracking_ ? this.cursorTrackingHideDelayMs_ :
    +      goog.ui.AdvancedTooltip.base(this, 'getHideDelayMs');
    +};
    +
    +
    +/**
    + * Forces the recalculation of the hotspot on the next mouse over event.
    + * @deprecated Not ever necessary to call this function. Hot spot is calculated
    + *     as neccessary.
    + */
    +goog.ui.AdvancedTooltip.prototype.resetHotSpot = goog.nullFunction;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.html b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.html
    new file mode 100644
    index 0000000000..980c58eece
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.AdvancedTooltip
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.AdvancedTooltipTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   <span id="hovertarget">
    +    Hover Here For Popup
    +    <span id="childtarget">
    +     Child of target
    +    </span>
    +   </span>
    +  </p>
    +  <p id="notpopup">
    +   Content
    +  </p>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.js b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.js
    new file mode 100644
    index 0000000000..4e1e78edf4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/advancedtooltip_test.js
    @@ -0,0 +1,287 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.AdvancedTooltipTest');
    +goog.setTestOnly('goog.ui.AdvancedTooltipTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.AdvancedTooltip');
    +goog.require('goog.ui.Tooltip');
    +goog.require('goog.userAgent');
    +
    +var att;
    +var clock;
    +var anchor;
    +var elsewhere;
    +var popup;
    +
    +var SHOWDELAY = 50;
    +var HIDEDELAY = 250;
    +var TRACKINGDELAY = 100;
    +
    +function isWindowTooSmall() {
    +  // Firefox 3 fails if the window is too small.
    +  return goog.userAgent.GECKO &&
    +      (window.innerWidth < 350 || window.innerHeight < 100);
    +}
    +
    +function setUp() {
    +  popup = goog.dom.createDom('span',
    +      {id: 'popup', style: 'position:absolute;top:300;left:300'}, 'Hello');
    +  att = new goog.ui.AdvancedTooltip('hovertarget');
    +  att.setElement(popup);
    +  att.setCursorTracking(true);
    +  att.setHotSpotPadding(new goog.math.Box(10, 10, 10, 10));
    +  att.setShowDelayMs(SHOWDELAY);
    +  att.setHideDelayMs(HIDEDELAY);
    +  att.setCursorTrackingHideDelayMs(TRACKINGDELAY);
    +  att.setMargin(new goog.math.Box(300, 0, 0, 300));
    +
    +  clock = new goog.testing.MockClock(true);
    +
    +  anchor = goog.dom.getElement('hovertarget');
    +  elsewhere = goog.dom.getElement('notpopup');
    +}
    +
    +function tearDown() {
    +  // tooltip needs to be hidden as well as disposed of so that it doesn't
    +  // leave global state hanging around to trip up other tests.
    +  if (att.isVisible()) {
    +    att.onHide_();
    +  }
    +  att.dispose();
    +  clock.uninstall();
    +}
    +
    +function assertVisible(msg, element) {
    +  if (element) {
    +    assertEquals(msg, 'visible', element.style.visibility);
    +  } else {
    +    assertEquals('visible', msg.style.visibility);
    +  }
    +}
    +
    +function assertHidden(msg, element) {
    +  if (element) {
    +    assertEquals(msg, 'hidden', element.style.visibility);
    +  } else {
    +    assertEquals('hidden', msg.style.visibility);
    +  }
    +}
    +
    +
    +/**
    + * Helper function to fire events related to moving a mouse from one element
    + * to another. Fires mouseout, mouseover, and mousemove event.
    + * @param {Element} from Element the mouse is moving from.
    + * @param {Element} to Element the mouse is moving to.
    + */
    +function fireMouseEvents(from, to) {
    +  goog.testing.events.fireMouseOutEvent(from, to);
    +  goog.testing.events.fireMouseOverEvent(to, from);
    +  var bounds = goog.style.getBounds(to);
    +  goog.testing.events.fireMouseMoveEvent(
    +      document, new goog.math.Coordinate(bounds.left + 1, bounds.top + 1));
    +}
    +
    +function testCursorTracking() {
    +  if (isWindowTooSmall()) {
    +    return;
    +  }
    +
    +  var oneThirdOfTheWay, twoThirdsOfTheWay;
    +
    +  oneThirdOfTheWay = new goog.math.Coordinate(100, 100);
    +  twoThirdsOfTheWay = new goog.math.Coordinate(200, 200);
    +
    +  goog.testing.events.fireMouseOverEvent(anchor, elsewhere);
    +  clock.tick(SHOWDELAY);
    +  assertVisible('Mouse over anchor should show popup', popup);
    +
    +  goog.testing.events.fireMouseOutEvent(anchor, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, oneThirdOfTheWay);
    +  clock.tick(HIDEDELAY);
    +  assertVisible("Moving mouse towards popup shouldn't hide it", popup);
    +
    +  goog.testing.events.fireMouseMoveEvent(document, twoThirdsOfTheWay);
    +  goog.testing.events.fireMouseMoveEvent(document, oneThirdOfTheWay);
    +  clock.tick(TRACKINGDELAY);
    +  assertHidden('Moving mouse away from popup should hide it', popup);
    +
    +  goog.testing.events.fireMouseMoveEvent(document, twoThirdsOfTheWay);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, anchor));
    +  clock.tick(SHOWDELAY);
    +  assertVisible('Set focus shows popup', popup);
    +  goog.testing.events.fireMouseMoveEvent(document, oneThirdOfTheWay);
    +  clock.tick(TRACKINGDELAY);
    +  assertHidden('Mouse move after focus should hide popup', popup);
    +}
    +
    +function testPadding() {
    +  if (isWindowTooSmall()) {
    +    return;
    +  }
    +
    +  goog.testing.events.fireMouseOverEvent(anchor, elsewhere);
    +  clock.tick(SHOWDELAY);
    +
    +  var attBounds = goog.style.getBounds(popup);
    +  var inPadding = new goog.math.Coordinate(attBounds.left - 5,
    +                                           attBounds.top - 5);
    +  var outOfPadding = new goog.math.Coordinate(attBounds.left - 15,
    +                                              attBounds.top - 15);
    +
    +  fireMouseEvents(anchor, popup);
    +  goog.testing.events.fireMouseOutEvent(popup, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, inPadding);
    +  clock.tick(HIDEDELAY);
    +  assertVisible("Mouse out of popup but within padding shouldn't hide it",
    +                popup);
    +
    +  goog.testing.events.fireMouseMoveEvent(document, outOfPadding);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Mouse move beyond popup padding should hide it', popup);
    +}
    +
    +
    +function testAnchorWithChild() {
    +  var child = goog.dom.getElement('childtarget');
    +
    +  fireMouseEvents(elsewhere, anchor);
    +  fireMouseEvents(anchor, child);
    +  clock.tick(SHOWDELAY);
    +  assertVisible('Mouse into child of anchor should still show popup', popup);
    +
    +  fireMouseEvents(child, anchor);
    +  clock.tick(HIDEDELAY);
    +  assertVisible('Mouse from child to anchor should still show popup', popup);
    +}
    +
    +function testNestedTooltip() {
    +  if (!isWindowTooSmall()) {
    +    checkNestedTooltips(false);
    +  }
    +}
    +
    +function testNestedAdvancedTooltip() {
    +  if (!isWindowTooSmall()) {
    +    checkNestedTooltips(true);
    +  }
    +}
    +
    +function testResizingTooltipWhileShown() {
    +  fireMouseEvents(elsewhere, anchor);
    +  clock.tick(SHOWDELAY);
    +  popup.style.height = '100px';
    +  var attBounds = goog.style.getBounds(popup);
    +  var inPadding = new goog.math.Coordinate(
    +      attBounds.left + 5, attBounds.top + attBounds.height + 5);
    +  var outOfPadding = new goog.math.Coordinate(
    +      attBounds.left + 5, attBounds.top + attBounds.height + 15);
    +
    +  fireMouseEvents(anchor, popup);
    +  goog.testing.events.fireMouseOutEvent(popup, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, inPadding);
    +  clock.tick(HIDEDELAY);
    +  assertVisible("Mouse out of popup but within padding shouldn't hide it",
    +                popup);
    +
    +  goog.testing.events.fireMouseMoveEvent(document, outOfPadding);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Mouse move beyond popup padding should hide it', popup);
    +}
    +
    +function checkNestedTooltips(useAdvancedTooltip) {
    +  popup.appendChild(goog.dom.createDom(
    +      'span', {id: 'nestedAnchor'}, 'Nested Anchor'));
    +  var nestedAnchor = goog.dom.getElement('nestedAnchor');
    +  var nestedTooltip;
    +  if (useAdvancedTooltip) {
    +    nestedTooltip = new goog.ui.AdvancedTooltip(nestedAnchor, 'popup');
    +  } else {
    +    nestedTooltip = new goog.ui.Tooltip(nestedAnchor, 'popup');
    +  }
    +  var nestedPopup = nestedTooltip.getElement();
    +  nestedTooltip.setShowDelayMs(SHOWDELAY);
    +  nestedTooltip.setHideDelayMs(HIDEDELAY);
    +
    +  fireMouseEvents(elsewhere, anchor);
    +  clock.tick(SHOWDELAY);
    +  fireMouseEvents(anchor, popup);
    +  fireMouseEvents(popup, nestedAnchor);
    +  clock.tick(SHOWDELAY + HIDEDELAY);
    +  assertVisible('Mouse into nested anchor should show popup', nestedPopup);
    +  assertVisible('Mouse into nested anchor should not hide parent', popup);
    +  fireMouseEvents(nestedAnchor, elsewhere);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Mouse out of nested popup should hide it', nestedPopup);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Mouse out of nested popup should eventually hide parent',
    +               popup);
    +
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, anchor));
    +  clock.tick(SHOWDELAY);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, anchor));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, nestedAnchor));
    +  clock.tick(SHOWDELAY + HIDEDELAY);
    +  assertVisible("Moving focus to child anchor doesn't hide parent", popup);
    +  assertVisible('Set focus shows nested popup', nestedPopup);
    +
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, nestedAnchor));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, anchor));
    +  clock.tick(HIDEDELAY + HIDEDELAY);
    +  assertHidden('Lose focus hides nested popup', nestedPopup);
    +  assertVisible(
    +      "Moving focus from nested anchor to parent doesn't hide parent", popup);
    +
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, anchor));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, nestedAnchor));
    +  clock.tick(SHOWDELAY);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, nestedAnchor));
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Lose focus hides nested popup', nestedPopup);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Nested anchor losing focus hides parent', popup);
    +
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, anchor));
    +  clock.tick(SHOWDELAY);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, anchor));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, nestedAnchor));
    +  clock.tick(SHOWDELAY);
    +  var coordElsewhere = new goog.math.Coordinate(1, 1);
    +  goog.testing.events.fireMouseMoveEvent(document, coordElsewhere);
    +  clock.tick(HIDEDELAY);
    +  assertHidden('Mouse move should hide parent with active child', popup);
    +  assertHidden('Mouse move should hide nested popup', nestedPopup);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/animatedzippy.js b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy.js
    new file mode 100644
    index 0000000000..62aec7127e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy.js
    @@ -0,0 +1,200 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Animated zippy widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/zippy.html
    + */
    +
    +goog.provide('goog.ui.AnimatedZippy');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.easing');
    +goog.require('goog.ui.Zippy');
    +goog.require('goog.ui.ZippyEvent');
    +
    +
    +
    +/**
    + * Zippy widget. Expandable/collapsible container, clicking the header toggles
    + * the visibility of the content.
    + *
    + * @param {Element|string|null} header Header element, either element
    + *     reference, string id or null if no header exists.
    + * @param {Element|string} content Content element, either element reference or
    + *     string id.
    + * @param {boolean=} opt_expanded Initial expanded/visibility state. Defaults to
    + *     false.
    + * @param {goog.dom.DomHelper=} opt_domHelper An optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Zippy}
    + */
    +goog.ui.AnimatedZippy = function(header, content, opt_expanded, opt_domHelper) {
    +  var domHelper = opt_domHelper || goog.dom.getDomHelper();
    +
    +  // Create wrapper element and move content into it.
    +  var elWrapper = domHelper.createDom('div', {'style': 'overflow:hidden'});
    +  var elContent = domHelper.getElement(content);
    +  elContent.parentNode.replaceChild(elWrapper, elContent);
    +  elWrapper.appendChild(elContent);
    +
    +  /**
    +   * Content wrapper, used for animation.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elWrapper_ = elWrapper;
    +
    +  /**
    +   * Reference to animation or null if animation is not active.
    +   * @type {goog.fx.Animation}
    +   * @private
    +   */
    +  this.anim_ = null;
    +
    +  // Call constructor of super class.
    +  goog.ui.Zippy.call(this, header, elContent, opt_expanded,
    +      undefined, domHelper);
    +
    +  // Set initial state.
    +  // NOTE: Set the class names as well otherwise animated zippys
    +  // start with empty class names.
    +  var expanded = this.isExpanded();
    +  this.elWrapper_.style.display = expanded ? '' : 'none';
    +  this.updateHeaderClassName(expanded);
    +};
    +goog.inherits(goog.ui.AnimatedZippy, goog.ui.Zippy);
    +goog.tagUnsealableClass(goog.ui.AnimatedZippy);
    +
    +
    +/**
    + * Duration of expand/collapse animation, in milliseconds.
    + * @type {number}
    + */
    +goog.ui.AnimatedZippy.prototype.animationDuration = 500;
    +
    +
    +/**
    + * Acceleration function for expand/collapse animation.
    + * @type {!Function}
    + */
    +goog.ui.AnimatedZippy.prototype.animationAcceleration = goog.fx.easing.easeOut;
    +
    +
    +/**
    + * @return {boolean} Whether the zippy is in the process of being expanded or
    + *     collapsed.
    + */
    +goog.ui.AnimatedZippy.prototype.isBusy = function() {
    +  return this.anim_ != null;
    +};
    +
    +
    +/**
    + * Sets expanded state.
    + *
    + * @param {boolean} expanded Expanded/visibility state.
    + * @override
    + */
    +goog.ui.AnimatedZippy.prototype.setExpanded = function(expanded) {
    +  if (this.isExpanded() == expanded && !this.anim_) {
    +    return;
    +  }
    +
    +  // Reset display property of wrapper to allow content element to be
    +  // measured.
    +  if (this.elWrapper_.style.display == 'none') {
    +    this.elWrapper_.style.display = '';
    +  }
    +
    +  // Measure content element.
    +  var h = this.getContentElement().offsetHeight;
    +
    +  // Stop active animation (if any) and determine starting height.
    +  var startH = 0;
    +  if (this.anim_) {
    +    expanded = this.isExpanded();
    +    goog.events.removeAll(this.anim_);
    +    this.anim_.stop(false);
    +
    +    var marginTop = parseInt(this.getContentElement().style.marginTop, 10);
    +    startH = h - Math.abs(marginTop);
    +  } else {
    +    startH = expanded ? 0 : h;
    +  }
    +
    +  // Updates header class name after the animation has been stopped.
    +  this.updateHeaderClassName(expanded);
    +
    +  // Set up expand/collapse animation.
    +  this.anim_ = new goog.fx.Animation([0, startH],
    +                                     [0, expanded ? h : 0],
    +                                     this.animationDuration,
    +                                     this.animationAcceleration);
    +
    +  var events = [goog.fx.Transition.EventType.BEGIN,
    +                goog.fx.Animation.EventType.ANIMATE,
    +                goog.fx.Transition.EventType.END];
    +  goog.events.listen(this.anim_, events, this.onAnimate_, false, this);
    +  goog.events.listen(this.anim_,
    +                     goog.fx.Transition.EventType.END,
    +                     goog.bind(this.onAnimationCompleted_, this, expanded));
    +
    +  // Start animation.
    +  this.anim_.play(false);
    +};
    +
    +
    +/**
    + * Called during animation
    + *
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.AnimatedZippy.prototype.onAnimate_ = function(e) {
    +  var contentElement = this.getContentElement();
    +  var h = contentElement.offsetHeight;
    +  contentElement.style.marginTop = (e.y - h) + 'px';
    +};
    +
    +
    +/**
    + * Called once the expand/collapse animation has completed.
    + *
    + * @param {boolean} expanded Expanded/visibility state.
    + * @private
    + */
    +goog.ui.AnimatedZippy.prototype.onAnimationCompleted_ = function(expanded) {
    +  // Fix wrong end position if the content has changed during the animation.
    +  if (expanded) {
    +    this.getContentElement().style.marginTop = '0';
    +  }
    +
    +  goog.events.removeAll(/** @type {!goog.fx.Animation} */ (this.anim_));
    +  this.setExpandedInternal(expanded);
    +  this.anim_ = null;
    +
    +  if (!expanded) {
    +    this.elWrapper_.style.display = 'none';
    +  }
    +
    +  // Fire toggle event.
    +  this.dispatchEvent(new goog.ui.ZippyEvent(goog.ui.Zippy.Events.TOGGLE,
    +                                            this, expanded));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.html b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.html
    new file mode 100644
    index 0000000000..320c8f98b1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.html
    @@ -0,0 +1,57 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.AnimatedZippy
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.AnimatedZippyTest');
    +  </script>
    +  <style type="text/css">
    +   .demo {
    +      border: solid 1px red;
    +      margin: 0 0 20px 0;
    +    }
    +
    +    .demo h2 {
    +      background-color: yellow;
    +      border: solid 1px #ccc;
    +      padding: 2px;
    +      margin: 0;
    +      fint-size: 100%;
    +    }
    +
    +    .demo div {
    +      border: solid 1px #ccc;
    +      padding: 2px;
    +    }
    +  </style>
    + </head>
    + <body>
    +  <div class="demo" id="d1">
    +   <h2 id="t1">
    +    handler
    +   </h2>
    +   <div id="c1">
    +    sem. Suspendisse porta felis ac ipsum. Sed tincidunt dui vitae nulla. Ut
    +    blandit. Nunc non neque. Mauris placerat. Vestibulum mollis tellus id dolor.
    +    Phasellus ac dolor molestie nunc euismod aliquam. Mauris tellus ipsum,
    +    fringilla id, tincidunt eu, vestibulum sit amet, metus. Quisque congue
    +    varius
    +    ligula. Quisque ornare mollis enim. Aliquam erat volutpat. Nulla mattis
    +    venenatis magna.
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.js b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.js
    new file mode 100644
    index 0000000000..a024a9d73a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/animatedzippy_test.js
    @@ -0,0 +1,93 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.AnimatedZippyTest');
    +goog.setTestOnly('goog.ui.AnimatedZippyTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.AnimatedZippy');
    +goog.require('goog.ui.Zippy');
    +
    +var animatedZippy;
    +var animatedZippyHeaderEl;
    +var propertyReplacer;
    +
    +function setUp() {
    +  animatedZippyHeaderEl = goog.dom.getElement('t1');
    +  goog.asserts.assert(animatedZippyHeaderEl);
    +  animatedZippy = new goog.ui.AnimatedZippy(animatedZippyHeaderEl,
    +      goog.dom.getElement('c1'));
    +
    +  propertyReplacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +  animatedZippy.dispose();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('must not be null', animatedZippy);
    +}
    +
    +function testExpandCollapse() {
    +  var animationsPlayed = 0;
    +  var toggleEventsFired = 0;
    +
    +  propertyReplacer.replace(goog.fx.Animation.prototype, 'play', function() {
    +    animationsPlayed++;
    +    this.dispatchAnimationEvent(goog.fx.Transition.EventType.END);
    +  });
    +  propertyReplacer.replace(goog.ui.AnimatedZippy.prototype, 'onAnimate_',
    +      goog.functions.NULL);
    +
    +  goog.events.listenOnce(animatedZippy, goog.ui.Zippy.Events.TOGGLE,
    +      function(e) {
    +        toggleEventsFired++;
    +        assertTrue('TOGGLE event must be for expansion', e.expanded);
    +        assertEquals('expanded must be true', true,
    +            animatedZippy.isExpanded());
    +        assertEquals('aria-expanded must be true', 'true',
    +            goog.a11y.aria.getState(animatedZippyHeaderEl,
    +                goog.a11y.aria.State.EXPANDED));
    +      });
    +
    +  animatedZippy.expand();
    +
    +  goog.events.listenOnce(animatedZippy, goog.ui.Zippy.Events.TOGGLE,
    +      function(e) {
    +        toggleEventsFired++;
    +        assertFalse('TOGGLE event must be for collapse', e.expanded);
    +        assertEquals('expanded must be false', false,
    +            animatedZippy.isExpanded());
    +        assertEquals('aria-expanded must be false', 'false',
    +            goog.a11y.aria.getState(animatedZippyHeaderEl,
    +            goog.a11y.aria.State.EXPANDED));
    +      });
    +
    +  animatedZippy.collapse();
    +
    +  assertEquals('animations must play', 2, animationsPlayed);
    +  assertEquals('TOGGLE events must fire', 2, toggleEventsFired);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/attachablemenu.js b/mapbox-gl/closure-library/closure/goog/ui/attachablemenu.js
    new file mode 100644
    index 0000000000..bc13986de8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/attachablemenu.js
    @@ -0,0 +1,476 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the AttachableMenu class.
    + *
    + */
    +
    +goog.provide('goog.ui.AttachableMenu');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.ItemEvent');
    +goog.require('goog.ui.MenuBase');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * An implementation of a menu that can attach itself to DOM element that
    + * are annotated appropriately.
    + *
    + * The following attributes are used by the AttachableMenu
    + *
    + * menu-item - Should be set on DOM elements that function as items in the
    + * menu that can be selected.
    + * classNameSelected - A class that will be added to the element's class names
    + * when the item is selected via keyboard or mouse.
    + *
    + * @param {Element=} opt_element A DOM element for the popup.
    + * @constructor
    + * @extends {goog.ui.MenuBase}
    + * @deprecated Use goog.ui.PopupMenu.
    + * @final
    + */
    +goog.ui.AttachableMenu = function(opt_element) {
    +  goog.ui.MenuBase.call(this, opt_element);
    +};
    +goog.inherits(goog.ui.AttachableMenu, goog.ui.MenuBase);
    +goog.tagUnsealableClass(goog.ui.AttachableMenu);
    +
    +
    +/**
    + * The currently selected element (mouse was moved over it or keyboard arrows)
    + * @type {Element}
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.selectedElement_ = null;
    +
    +
    +/**
    + * Class name to append to a menu item's class when it's selected
    + * @type {string}
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.itemClassName_ = 'menu-item';
    +
    +
    +/**
    + * Class name to append to a menu item's class when it's selected
    + * @type {string}
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.selectedItemClassName_ = 'menu-item-selected';
    +
    +
    +/**
    + * Keep track of when the last key was pressed so that a keydown-scroll doesn't
    + * trigger a mouseover event
    + * @type {number}
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.lastKeyDown_ = goog.now();
    +
    +
    +/** @override */
    +goog.ui.AttachableMenu.prototype.disposeInternal = function() {
    +  goog.ui.AttachableMenu.superClass_.disposeInternal.call(this);
    +  this.selectedElement_ = null;
    +};
    +
    +
    +/**
    + * Sets the class name to use for menu items
    + *
    + * @return {string} The class name to use for items.
    + */
    +goog.ui.AttachableMenu.prototype.getItemClassName = function() {
    +  return this.itemClassName_;
    +};
    +
    +
    +/**
    + * Sets the class name to use for menu items
    + *
    + * @param {string} name The class name to use for items.
    + */
    +goog.ui.AttachableMenu.prototype.setItemClassName = function(name) {
    +  this.itemClassName_ = name;
    +};
    +
    +
    +/**
    + * Sets the class name to use for selected menu items
    + * todo(user) - reevaluate if we can simulate pseudo classes in IE
    + *
    + * @return {string} The class name to use for selected items.
    + */
    +goog.ui.AttachableMenu.prototype.getSelectedItemClassName = function() {
    +  return this.selectedItemClassName_;
    +};
    +
    +
    +/**
    + * Sets the class name to use for selected menu items
    + * todo(user) - reevaluate if we can simulate pseudo classes in IE
    + *
    + * @param {string} name The class name to use for selected items.
    + */
    +goog.ui.AttachableMenu.prototype.setSelectedItemClassName = function(name) {
    +  this.selectedItemClassName_ = name;
    +};
    +
    +
    +/**
    + * Returns the selected item
    + *
    + * @return {Element} The item selected or null if no item is selected.
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.getSelectedItem = function() {
    +  return this.selectedElement_;
    +};
    +
    +
    +/** @override */
    +goog.ui.AttachableMenu.prototype.setSelectedItem = function(obj) {
    +  var elt = /** @type {Element} */ (obj);
    +  if (this.selectedElement_) {
    +    goog.dom.classlist.remove(this.selectedElement_,
    +        this.selectedItemClassName_);
    +  }
    +
    +  this.selectedElement_ = elt;
    +
    +  var el = this.getElement();
    +  goog.asserts.assert(el, 'The attachable menu DOM element cannot be null.');
    +  if (this.selectedElement_) {
    +    goog.dom.classlist.add(this.selectedElement_, this.selectedItemClassName_);
    +
    +    if (elt.id) {
    +      // Update activedescendant to reflect the new selection. ARIA roles for
    +      // menu and menuitem can be set statically (thru Soy templates, for
    +      // example) whereas this needs to be updated as the selection changes.
    +      goog.a11y.aria.setState(el, goog.a11y.aria.State.ACTIVEDESCENDANT,
    +          elt.id);
    +    }
    +
    +    var top = this.selectedElement_.offsetTop;
    +    var height = this.selectedElement_.offsetHeight;
    +    var scrollTop = el.scrollTop;
    +    var scrollHeight = el.offsetHeight;
    +
    +    // If the menu is scrollable this scrolls the selected item into view
    +    // (this has no effect when the menu doesn't scroll)
    +    if (top < scrollTop) {
    +      el.scrollTop = top;
    +    } else if (top + height > scrollTop + scrollHeight) {
    +      el.scrollTop = top + height - scrollHeight;
    +    }
    +  } else {
    +    // Clear off activedescendant to reflect no selection.
    +    goog.a11y.aria.setState(el, goog.a11y.aria.State.ACTIVEDESCENDANT, '');
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.AttachableMenu.prototype.showPopupElement = function() {
    +  // The scroll position cannot be set for hidden (display: none) elements in
    +  // gecko browsers.
    +  var el = /** @type {Element} */ (this.getElement());
    +  goog.style.setElementShown(el, true);
    +  el.scrollTop = 0;
    +  el.style.visibility = 'visible';
    +};
    +
    +
    +/**
    + * Called after the menu is shown.
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onShow_ = function() {
    +  goog.ui.AttachableMenu.superClass_.onShow_.call(this);
    +
    +  // In IE, focusing the menu causes weird scrolling to happen. Focusing the
    +  // first child makes the scroll behavior better, and the key handling still
    +  // works. In FF, focusing the first child causes us to lose key events, so we
    +  // still focus the menu.
    +  var el = this.getElement();
    +  goog.userAgent.IE ? el.firstChild.focus() :
    +      el.focus();
    +};
    +
    +
    +/**
    + * Returns the next or previous item. Used for up/down arrows.
    + *
    + * @param {boolean} prev True to go to the previous element instead of next.
    + * @return {Element} The next or previous element.
    + * @protected
    + */
    +goog.ui.AttachableMenu.prototype.getNextPrevItem = function(prev) {
    +  // first find the index of the next element
    +  var elements = this.getElement().getElementsByTagName('*');
    +  var elementCount = elements.length;
    +  var index;
    +  // if there is a selected element, find its index and then inc/dec by one
    +  if (this.selectedElement_) {
    +    for (var i = 0; i < elementCount; i++) {
    +      if (elements[i] == this.selectedElement_) {
    +        index = prev ? i - 1 : i + 1;
    +        break;
    +      }
    +    }
    +  }
    +
    +  // if no selected element, start from beginning or end
    +  if (!goog.isDef(index)) {
    +    index = prev ? elementCount - 1 : 0;
    +  }
    +
    +  // iterate forward or backwards through the elements finding the next
    +  // menu item
    +  for (var i = 0; i < elementCount; i++) {
    +    var multiplier = prev ? -1 : 1;
    +    var nextIndex = index + (multiplier * i) % elementCount;
    +
    +    // if overflowed/underflowed, wrap around
    +    if (nextIndex < 0) {
    +      nextIndex += elementCount;
    +    } else if (nextIndex >= elementCount) {
    +      nextIndex -= elementCount;
    +    }
    +
    +    if (this.isMenuItem_(elements[nextIndex])) {
    +      return elements[nextIndex];
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Mouse over handler for the menu.
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onMouseOver = function(e) {
    +  var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
    +  if (eltItem == null) {
    +    return;
    +  }
    +
    +  // Stop the keydown triggering a mouseover in FF.
    +  if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) {
    +    this.setSelectedItem(eltItem);
    +  }
    +};
    +
    +
    +/**
    + * Mouse out handler for the menu.
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onMouseOut = function(e) {
    +  var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
    +  if (eltItem == null) {
    +    return;
    +  }
    +
    +  // Stop the keydown triggering a mouseout in FF.
    +  if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) {
    +    this.setSelectedItem(null);
    +  }
    +};
    +
    +
    +/**
    + * Mouse down handler for the menu. Prevents default to avoid text selection.
    + * @param {!goog.events.Event} e The event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onMouseDown = goog.events.Event.preventDefault;
    +
    +
    +/**
    + * Mouse up handler for the menu.
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onMouseUp = function(e) {
    +  var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
    +  if (eltItem == null) {
    +    return;
    +  }
    +  this.setVisible(false);
    +  this.onItemSelected_(eltItem);
    +};
    +
    +
    +/**
    + * Key down handler for the menu.
    + * @param {goog.events.KeyEvent} e The event object.
    + * @protected
    + * @override
    + */
    +goog.ui.AttachableMenu.prototype.onKeyDown = function(e) {
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.DOWN:
    +      this.setSelectedItem(this.getNextPrevItem(false));
    +      this.lastKeyDown_ = goog.now();
    +      break;
    +    case goog.events.KeyCodes.UP:
    +      this.setSelectedItem(this.getNextPrevItem(true));
    +      this.lastKeyDown_ = goog.now();
    +      break;
    +    case goog.events.KeyCodes.ENTER:
    +      if (this.selectedElement_) {
    +        this.onItemSelected_();
    +        this.setVisible(false);
    +      }
    +      break;
    +    case goog.events.KeyCodes.ESC:
    +      this.setVisible(false);
    +      break;
    +    default:
    +      if (e.charCode) {
    +        var charStr = String.fromCharCode(e.charCode);
    +        this.selectByName_(charStr, 1, true);
    +      }
    +      break;
    +  }
    +  // Prevent the browser's default keydown behaviour when the menu is open,
    +  // e.g. keyboard scrolling.
    +  e.preventDefault();
    +
    +  // Stop propagation to prevent application level keyboard shortcuts from
    +  // firing.
    +  e.stopPropagation();
    +
    +  this.dispatchEvent(e);
    +};
    +
    +
    +/**
    + * Find an item that has the given prefix and select it.
    + *
    + * @param {string} prefix The entered prefix, so far.
    + * @param {number=} opt_direction 1 to search forward from the selection
    + *     (default), -1 to search backward (e.g. to go to the previous match).
    + * @param {boolean=} opt_skip True if should skip the current selection,
    + *     unless no other item has the given prefix.
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.selectByName_ =
    +    function(prefix, opt_direction, opt_skip) {
    +  var elements = this.getElement().getElementsByTagName('*');
    +  var elementCount = elements.length;
    +  var index;
    +
    +  if (elementCount == 0) {
    +    return;
    +  }
    +
    +  if (!this.selectedElement_ ||
    +      (index = goog.array.indexOf(elements, this.selectedElement_)) == -1) {
    +    // no selection or selection isn't known => start at the beginning
    +    index = 0;
    +  }
    +
    +  var start = index;
    +  var re = new RegExp('^' + goog.string.regExpEscape(prefix), 'i');
    +  var skip = opt_skip && this.selectedElement_;
    +  var dir = opt_direction || 1;
    +
    +  do {
    +    if (elements[index] != skip && this.isMenuItem_(elements[index])) {
    +      var name = goog.dom.getTextContent(elements[index]);
    +      if (name.match(re)) {
    +        break;
    +      }
    +    }
    +    index += dir;
    +    if (index == elementCount) {
    +      index = 0;
    +    } else if (index < 0) {
    +      index = elementCount - 1;
    +    }
    +  } while (index != start);
    +
    +  if (this.selectedElement_ != elements[index]) {
    +    this.setSelectedItem(elements[index]);
    +  }
    +};
    +
    +
    +/**
    + * Dispatch an ITEM_ACTION event when an item is selected
    + * @param {Object=} opt_item Item selected.
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.onItemSelected_ = function(opt_item) {
    +  this.dispatchEvent(new goog.ui.ItemEvent(goog.ui.MenuBase.Events.ITEM_ACTION,
    +      this, opt_item || this.selectedElement_));
    +};
    +
    +
    +/**
    + * Returns whether the specified element is a menu item.
    + * @param {Element} elt The element to find a menu item ancestor of.
    + * @return {boolean} Whether the specified element is a menu item.
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.isMenuItem_ = function(elt) {
    +  return !!elt && goog.dom.classlist.contains(elt, this.itemClassName_);
    +};
    +
    +
    +/**
    + * Returns the menu-item scoping the specified element, or null if there is
    + * none.
    + * @param {Element|undefined} elt The element to find a menu item ancestor of.
    + * @return {Element} The menu-item scoping the specified element, or null if
    + *     there is none.
    + * @private
    + */
    +goog.ui.AttachableMenu.prototype.getAncestorMenuItem_ = function(elt) {
    +  if (elt) {
    +    var ownerDocumentBody = goog.dom.getOwnerDocument(elt).body;
    +    while (elt != null && elt != ownerDocumentBody) {
    +      if (this.isMenuItem_(elt)) {
    +        return elt;
    +      }
    +      elt = /** @type {Element} */ (elt.parentNode);
    +    }
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/bidiinput.js b/mapbox-gl/closure-library/closure/goog/ui/bidiinput.js
    new file mode 100644
    index 0000000000..602a4343e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/bidiinput.js
    @@ -0,0 +1,180 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Component for an input field with bidi direction automatic
    + * detection. The input element directionality is automatically set according
    + * to the contents (value) of the element.
    + *
    + * @see ../demos/bidiinput.html
    + */
    +
    +
    +goog.provide('goog.ui.BidiInput');
    +
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.i18n.bidi');
    +goog.require('goog.i18n.bidi.Dir');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Default implementation of BidiInput.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper  Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.BidiInput = function(opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +};
    +goog.inherits(goog.ui.BidiInput, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.BidiInput);
    +
    +
    +/**
    + * The input handler that provides the input event.
    + * @type {goog.events.InputHandler?}
    + * @private
    + */
    +goog.ui.BidiInput.prototype.inputHandler_ = null;
    +
    +
    +/**
    + * Decorates the given HTML element as a BidiInput. The HTML element can be an
    + * input element with type='text', a textarea element, or any contenteditable.
    + * Overrides {@link goog.ui.Component#decorateInternal}.  Considered protected.
    + * @param {Element} element  Element to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.BidiInput.prototype.decorateInternal = function(element) {
    +  goog.ui.BidiInput.superClass_.decorateInternal.call(this, element);
    +  this.init_();
    +};
    +
    +
    +/**
    + * Creates the element for the text input.
    + * @protected
    + * @override
    + */
    +goog.ui.BidiInput.prototype.createDom = function() {
    +  this.setElementInternal(
    +      this.getDomHelper().createDom('input', {'type': 'text'}));
    +  this.init_();
    +};
    +
    +
    +/**
    + * Initializes the events and initial text direction.
    + * Called from either decorate or createDom, after the input field has
    + * been created.
    + * @private
    + */
    +goog.ui.BidiInput.prototype.init_ = function() {
    +  // Set initial direction by current text
    +  this.setDirection_();
    +
    +  // Listen to value change events
    +  this.inputHandler_ = new goog.events.InputHandler(this.getElement());
    +  goog.events.listen(this.inputHandler_,
    +      goog.events.InputHandler.EventType.INPUT,
    +      this.setDirection_, false, this);
    +};
    +
    +
    +/**
    + * Set the direction of the input element based on the current value. If the
    + * value does not have any strongly directional characters, remove the dir
    + * attribute so that the direction is inherited instead.
    + * This method is called when the user changes the input element value, or
    + * when a program changes the value using
    + * {@link goog.ui.BidiInput#setValue}
    + * @private
    + */
    +goog.ui.BidiInput.prototype.setDirection_ = function() {
    +  var element = this.getElement();
    +  var text = this.getValue();
    +  switch (goog.i18n.bidi.estimateDirection(text)) {
    +    case (goog.i18n.bidi.Dir.LTR):
    +      element.dir = 'ltr';
    +      break;
    +    case (goog.i18n.bidi.Dir.RTL):
    +      element.dir = 'rtl';
    +      break;
    +    default:
    +      // Default for no direction, inherit from document.
    +      element.removeAttribute('dir');
    +  }
    +};
    +
    +
    +/**
    + * Returns the direction of the input element.
    + * @return {?string} Return 'rtl' for right-to-left text,
    + *     'ltr' for left-to-right text, or null if the value itself is not
    + *     enough to determine directionality (e.g. an empty value), and the
    + *     direction is inherited from a parent element (typically the body
    + *     element).
    + */
    +goog.ui.BidiInput.prototype.getDirection = function() {
    +  var dir = this.getElement().dir;
    +  if (dir == '') {
    +    dir = null;
    +  }
    +  return dir;
    +};
    +
    +
    +/**
    + * Sets the value of the underlying input field, and sets the direction
    + * according to the given value.
    + * @param {string} value  The Value to set in the underlying input field.
    + */
    +goog.ui.BidiInput.prototype.setValue = function(value) {
    +  var element = this.getElement();
    +  if (goog.isDefAndNotNull(element.value)) {
    +    element.value = value;
    +  } else {
    +    goog.dom.setTextContent(element, value);
    +  }
    +  this.setDirection_();
    +};
    +
    +
    +/**
    + * Returns the value of the underlying input field.
    + * @return {string} Value of the underlying input field.
    + */
    +goog.ui.BidiInput.prototype.getValue = function() {
    +  var element = this.getElement();
    +  return goog.isDefAndNotNull(element.value) ? element.value :
    +      goog.dom.getRawTextContent(element);
    +};
    +
    +
    +/** @override */
    +goog.ui.BidiInput.prototype.disposeInternal = function() {
    +  if (this.inputHandler_) {
    +    goog.events.removeAll(this.inputHandler_);
    +    this.inputHandler_.dispose();
    +    this.inputHandler_ = null;
    +    goog.ui.BidiInput.superClass_.disposeInternal.call(this);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.html b/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.html
    new file mode 100644
    index 0000000000..13999f1651
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.BidiInput
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.BidiInputTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="textDiv">
    +   <input type="text" id="emptyText" value="" />
    +   <input type="text" id="bidiText" value="hello, world!" />
    +   <div id="bidiTextDiv" contenteditable="true">
    +    hello,world!
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.js b/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.js
    new file mode 100644
    index 0000000000..ae6d1eecbb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/bidiinput_test.js
    @@ -0,0 +1,122 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.BidiInputTest');
    +goog.setTestOnly('goog.ui.BidiInputTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.BidiInput');
    +
    +function setUp() {
    +  document.body.focus();
    +}
    +
    +function tearDown() {
    +  document.getElementById('emptyText').value = '';
    +  document.getElementById('bidiText').value = 'hello, world!';
    +}
    +
    +function testEmptyInput() {
    +  var bidiInput = new goog.ui.BidiInput();
    +  var emptyText = goog.dom.getElement('emptyText');
    +  bidiInput.decorate(emptyText);
    +  assertEquals('', bidiInput.getValue());
    +  bidiInput.setValue('hello!');
    +  assertEquals('hello!', bidiInput.getValue());
    +}
    +
    +function testSetDirection() {
    +  var shalomInHebrew = '\u05e9\u05dc\u05d5\u05dd';
    +  var isAGoodLanguageInHebrew =
    +      '\u05d4\u05d9\u05d0 \u05e9\u05e4\u05d4 \u05d8\u05d5\u05d1\u05d4';
    +  var learnInHebrew = '\u05dc\u05de\u05d3';
    +
    +  var bidiInput = new goog.ui.BidiInput();
    +  var bidiText = goog.dom.getElement('bidiText');
    +  bidiInput.decorate(bidiText);
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue(shalomInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue('hello!');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue(':) , ? ! ' + shalomInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue(':) , ? ! hello!');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('   ;)   ');
    +  assertEquals(null, bidiInput.getDirection());
    +
    +  bidiInput.setValue(shalomInHebrew + ', how are you today?');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('Hello is ' + shalomInHebrew + ' in Hebrew');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('JavaScript ' + isAGoodLanguageInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue(learnInHebrew + ' JavaScript');
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue('');
    +  assertEquals(null, bidiInput.getDirection());
    +}
    +
    +function testSetDirection_inContenteditableDiv() {
    +  var shalomInHebrew = '\u05e9\u05dc\u05d5\u05dd';
    +  var isAGoodLanguageInHebrew =
    +      '\u05d4\u05d9\u05d0 \u05e9\u05e4\u05d4 \u05d8\u05d5\u05d1\u05d4';
    +  var learnInHebrew = '\u05dc\u05de\u05d3';
    +
    +  var bidiInput = new goog.ui.BidiInput();
    +  var bidiTextDiv = goog.dom.getElement('bidiTextDiv');
    +  bidiInput.decorate(bidiTextDiv);
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue(shalomInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue('hello!');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue(':) , ? ! ' + shalomInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue(':) , ? ! hello!');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('   ;)   ');
    +  assertEquals(null, bidiInput.getDirection());
    +
    +  bidiInput.setValue(shalomInHebrew + ', how are you today?');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('Hello is ' + shalomInHebrew + ' in Hebrew');
    +  assertEquals('ltr', bidiInput.getDirection());
    +
    +  bidiInput.setValue('JavaScript ' + isAGoodLanguageInHebrew);
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue(learnInHebrew + ' JavaScript');
    +  assertEquals('rtl', bidiInput.getDirection());
    +
    +  bidiInput.setValue('');
    +  assertEquals(null, bidiInput.getDirection());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/bubble.js b/mapbox-gl/closure-library/closure/goog/ui/bubble.js
    new file mode 100644
    index 0000000000..303f633810
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/bubble.js
    @@ -0,0 +1,473 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the Bubble class.
    + *
    + *
    + * @see ../demos/bubble.html
    + *
    + * TODO: support decoration and addChild
    + */
    +
    +goog.provide('goog.ui.Bubble');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Box');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AbsolutePosition');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.CornerBit');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Popup');
    +
    +
    +
    +/**
    + * The Bubble provides a general purpose bubble implementation that can be
    + * anchored to a particular element and displayed for a period of time.
    + *
    + * @param {string|Element} message HTML string or an element to display inside
    + *     the bubble.
    + * @param {Object=} opt_config The configuration
    + *     for the bubble. If not specified, the default configuration will be
    + *     used. {@see goog.ui.Bubble.defaultConfig}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.Bubble = function(message, opt_config, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The HTML string or element to display inside the bubble.
    +   *
    +   * @type {string|Element}
    +   * @private
    +   */
    +  this.message_ = message;
    +
    +  /**
    +   * The Popup element used to position and display the bubble.
    +   *
    +   * @type {goog.ui.Popup}
    +   * @private
    +   */
    +  this.popup_ = new goog.ui.Popup();
    +
    +  /**
    +   * Configuration map that contains bubble's UI elements.
    +   *
    +   * @type {Object}
    +   * @private
    +   */
    +  this.config_ = opt_config || goog.ui.Bubble.defaultConfig;
    +
    +  /**
    +   * Id of the close button for this bubble.
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.closeButtonId_ = this.makeId('cb');
    +
    +  /**
    +   * Id of the div for the embedded element.
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.messageId_ = this.makeId('mi');
    +
    +};
    +goog.inherits(goog.ui.Bubble, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.Bubble);
    +
    +
    +/**
    + * In milliseconds, timeout after which the button auto-hides. Null means
    + * infinite.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.Bubble.prototype.timeout_ = null;
    +
    +
    +/**
    + * Key returned by the bubble timer.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.Bubble.prototype.timerId_ = 0;
    +
    +
    +/**
    + * Key returned by the listen function for the close button.
    + * @type {goog.events.Key}
    + * @private
    + */
    +goog.ui.Bubble.prototype.listener_ = null;
    +
    +
    +/**
    + * Key returned by the listen function for the close button.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Bubble.prototype.anchor_ = null;
    +
    +
    +/** @override */
    +goog.ui.Bubble.prototype.createDom = function() {
    +  goog.ui.Bubble.superClass_.createDom.call(this);
    +
    +  var element = this.getElement();
    +  element.style.position = 'absolute';
    +  element.style.visibility = 'hidden';
    +
    +  this.popup_.setElement(element);
    +};
    +
    +
    +/**
    + * Attaches the bubble to an anchor element. Computes the positioning and
    + * orientation of the bubble.
    + *
    + * @param {Element} anchorElement The element to which we are attaching.
    + */
    +goog.ui.Bubble.prototype.attach = function(anchorElement) {
    +  this.setAnchoredPosition_(
    +      anchorElement, this.computePinnedCorner_(anchorElement));
    +};
    +
    +
    +/**
    + * Sets the corner of the bubble to used in the positioning algorithm.
    + *
    + * @param {goog.positioning.Corner} corner The bubble corner used for
    + *     positioning constants.
    + */
    +goog.ui.Bubble.prototype.setPinnedCorner = function(corner) {
    +  this.popup_.setPinnedCorner(corner);
    +};
    +
    +
    +/**
    + * Sets the position of the bubble. Pass null for corner in AnchoredPosition
    + * for corner to be computed automatically.
    + *
    + * @param {goog.positioning.AbstractPosition} position The position of the
    + *     bubble.
    + */
    +goog.ui.Bubble.prototype.setPosition = function(position) {
    +  if (position instanceof goog.positioning.AbsolutePosition) {
    +    this.popup_.setPosition(position);
    +  } else if (position instanceof goog.positioning.AnchoredPosition) {
    +    this.setAnchoredPosition_(position.element, position.corner);
    +  } else {
    +    throw Error('Bubble only supports absolute and anchored positions!');
    +  }
    +};
    +
    +
    +/**
    + * Sets the timeout after which bubble hides itself.
    + *
    + * @param {number} timeout Timeout of the bubble.
    + */
    +goog.ui.Bubble.prototype.setTimeout = function(timeout) {
    +  this.timeout_ = timeout;
    +};
    +
    +
    +/**
    + * Sets whether the bubble should be automatically hidden whenever user clicks
    + * outside the bubble element.
    + *
    + * @param {boolean} autoHide Whether to hide if user clicks outside the bubble.
    + */
    +goog.ui.Bubble.prototype.setAutoHide = function(autoHide) {
    +  this.popup_.setAutoHide(autoHide);
    +};
    +
    +
    +/**
    + * Sets whether the bubble should be visible.
    + *
    + * @param {boolean} visible Desired visibility state.
    + */
    +goog.ui.Bubble.prototype.setVisible = function(visible) {
    +  if (visible && !this.popup_.isVisible()) {
    +    this.configureElement_();
    +  }
    +  this.popup_.setVisible(visible);
    +  if (!this.popup_.isVisible()) {
    +    this.unconfigureElement_();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the bubble is visible.
    + */
    +goog.ui.Bubble.prototype.isVisible = function() {
    +  return this.popup_.isVisible();
    +};
    +
    +
    +/** @override */
    +goog.ui.Bubble.prototype.disposeInternal = function() {
    +  this.unconfigureElement_();
    +  this.popup_.dispose();
    +  this.popup_ = null;
    +  goog.ui.Bubble.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Creates element's contents and configures all timers. This is called on
    + * setVisible(true).
    + * @private
    + */
    +goog.ui.Bubble.prototype.configureElement_ = function() {
    +  if (!this.isInDocument()) {
    +    throw Error('You must render the bubble before showing it!');
    +  }
    +
    +  var element = this.getElement();
    +  var corner = this.popup_.getPinnedCorner();
    +  element.innerHTML = this.computeHtmlForCorner_(corner);
    +
    +  if (typeof this.message_ == 'object') {
    +    var messageDiv = this.getDomHelper().getElement(this.messageId_);
    +    this.getDomHelper().appendChild(messageDiv, this.message_);
    +  }
    +  var closeButton = this.getDomHelper().getElement(this.closeButtonId_);
    +  this.listener_ = goog.events.listen(closeButton,
    +      goog.events.EventType.CLICK, this.hideBubble_, false, this);
    +
    +  if (this.timeout_) {
    +    this.timerId_ = goog.Timer.callOnce(this.hideBubble_, this.timeout_, this);
    +  }
    +};
    +
    +
    +/**
    + * Gets rid of the element's contents and all assoicated timers and listeners.
    + * This is called on dispose as well as on setVisible(false).
    + * @private
    + */
    +goog.ui.Bubble.prototype.unconfigureElement_ = function() {
    +  if (this.listener_) {
    +    goog.events.unlistenByKey(this.listener_);
    +    this.listener_ = null;
    +  }
    +  if (this.timerId_) {
    +    goog.Timer.clear(this.timerId_);
    +    this.timerId_ = null;
    +  }
    +
    +  var element = this.getElement();
    +  if (element) {
    +    this.getDomHelper().removeChildren(element);
    +    element.innerHTML = '';
    +  }
    +};
    +
    +
    +/**
    + * Computes bubble position based on anchored element.
    + *
    + * @param {Element} anchorElement The element to which we are attaching.
    + * @param {goog.positioning.Corner} corner The bubble corner used for
    + *     positioning.
    + * @private
    + */
    +goog.ui.Bubble.prototype.setAnchoredPosition_ = function(anchorElement,
    +    corner) {
    +  this.popup_.setPinnedCorner(corner);
    +  var margin = this.createMarginForCorner_(corner);
    +  this.popup_.setMargin(margin);
    +  var anchorCorner = goog.positioning.flipCorner(corner);
    +  this.popup_.setPosition(new goog.positioning.AnchoredPosition(
    +      anchorElement, anchorCorner));
    +};
    +
    +
    +/**
    + * Hides the bubble. This is called asynchronously by timer of event processor
    + * for the mouse click on the close button.
    + * @private
    + */
    +goog.ui.Bubble.prototype.hideBubble_ = function() {
    +  this.setVisible(false);
    +};
    +
    +
    +/**
    + * Returns an AnchoredPosition that will position the bubble optimally
    + * given the position of the anchor element and the size of the viewport.
    + *
    + * @param {Element} anchorElement The element to which the bubble is attached.
    + * @return {!goog.positioning.AnchoredPosition} The AnchoredPosition
    + *     to give to {@link #setPosition}.
    + */
    +goog.ui.Bubble.prototype.getComputedAnchoredPosition = function(anchorElement) {
    +  return new goog.positioning.AnchoredPosition(
    +      anchorElement, this.computePinnedCorner_(anchorElement));
    +};
    +
    +
    +/**
    + * Computes the pinned corner for the bubble.
    + *
    + * @param {Element} anchorElement The element to which the button is attached.
    + * @return {goog.positioning.Corner} The pinned corner.
    + * @private
    + */
    +goog.ui.Bubble.prototype.computePinnedCorner_ = function(anchorElement) {
    +  var doc = this.getDomHelper().getOwnerDocument(anchorElement);
    +  var viewportElement = goog.style.getClientViewportElement(doc);
    +  var viewportWidth = viewportElement.offsetWidth;
    +  var viewportHeight = viewportElement.offsetHeight;
    +  var anchorElementOffset = goog.style.getPageOffset(anchorElement);
    +  var anchorElementSize = goog.style.getSize(anchorElement);
    +  var anchorType = 0;
    +  // right margin or left?
    +  if (viewportWidth - anchorElementOffset.x - anchorElementSize.width >
    +      anchorElementOffset.x) {
    +    anchorType += 1;
    +  }
    +  // attaches to the top or to the bottom?
    +  if (viewportHeight - anchorElementOffset.y - anchorElementSize.height >
    +      anchorElementOffset.y) {
    +    anchorType += 2;
    +  }
    +  return goog.ui.Bubble.corners_[anchorType];
    +};
    +
    +
    +/**
    + * Computes the right offset for a given bubble corner
    + * and creates a margin element for it. This is done to have the
    + * button anchor element on its frame rather than on the corner.
    + *
    + * @param {goog.positioning.Corner} corner The corner.
    + * @return {!goog.math.Box} the computed margin. Only left or right fields are
    + *     non-zero, but they may be negative.
    + * @private
    + */
    +goog.ui.Bubble.prototype.createMarginForCorner_ = function(corner) {
    +  var margin = new goog.math.Box(0, 0, 0, 0);
    +  if (corner & goog.positioning.CornerBit.RIGHT) {
    +    margin.right -= this.config_.marginShift;
    +  } else {
    +    margin.left -= this.config_.marginShift;
    +  }
    +  return margin;
    +};
    +
    +
    +/**
    + * Computes the HTML string for a given bubble orientation.
    + *
    + * @param {goog.positioning.Corner} corner The corner.
    + * @return {string} The HTML string to place inside the bubble's popup.
    + * @private
    + */
    +goog.ui.Bubble.prototype.computeHtmlForCorner_ = function(corner) {
    +  var bubbleTopClass;
    +  var bubbleBottomClass;
    +  switch (corner) {
    +    case goog.positioning.Corner.TOP_LEFT:
    +      bubbleTopClass = this.config_.cssBubbleTopLeftAnchor;
    +      bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor;
    +      break;
    +    case goog.positioning.Corner.TOP_RIGHT:
    +      bubbleTopClass = this.config_.cssBubbleTopRightAnchor;
    +      bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor;
    +      break;
    +    case goog.positioning.Corner.BOTTOM_LEFT:
    +      bubbleTopClass = this.config_.cssBubbleTopNoAnchor;
    +      bubbleBottomClass = this.config_.cssBubbleBottomLeftAnchor;
    +      break;
    +    case goog.positioning.Corner.BOTTOM_RIGHT:
    +      bubbleTopClass = this.config_.cssBubbleTopNoAnchor;
    +      bubbleBottomClass = this.config_.cssBubbleBottomRightAnchor;
    +      break;
    +    default:
    +      throw Error('This corner type is not supported by bubble!');
    +  }
    +  var message = null;
    +  if (typeof this.message_ == 'object') {
    +    message = '<div id="' + this.messageId_ + '">';
    +  } else {
    +    message = this.message_;
    +  }
    +  var html =
    +      '<table border=0 cellspacing=0 cellpadding=0 style="z-index:1"' +
    +      ' width=' + this.config_.bubbleWidth + '>' +
    +      '<tr><td colspan=4 class="' + bubbleTopClass + '">' +
    +      '<tr>' +
    +      '<td class="' + this.config_.cssBubbleLeft + '">' +
    +      '<td class="' + this.config_.cssBubbleFont + '"' +
    +      ' style="padding:0 4px;background:white">' + message +
    +      '<td id="' + this.closeButtonId_ + '"' +
    +      ' class="' + this.config_.cssCloseButton + '"/>' +
    +      '<td class="' + this.config_.cssBubbleRight + '">' +
    +      '<tr>' +
    +      '<td colspan=4 class="' + bubbleBottomClass + '">' +
    +      '</table>';
    +  return html;
    +};
    +
    +
    +/**
    + * A default configuration for the bubble.
    + *
    + * @type {Object}
    + */
    +goog.ui.Bubble.defaultConfig = {
    +  bubbleWidth: 147,
    +  marginShift: 60,
    +  cssBubbleFont: goog.getCssName('goog-bubble-font'),
    +  cssCloseButton: goog.getCssName('goog-bubble-close-button'),
    +  cssBubbleTopRightAnchor: goog.getCssName('goog-bubble-top-right-anchor'),
    +  cssBubbleTopLeftAnchor: goog.getCssName('goog-bubble-top-left-anchor'),
    +  cssBubbleTopNoAnchor: goog.getCssName('goog-bubble-top-no-anchor'),
    +  cssBubbleBottomRightAnchor:
    +      goog.getCssName('goog-bubble-bottom-right-anchor'),
    +  cssBubbleBottomLeftAnchor: goog.getCssName('goog-bubble-bottom-left-anchor'),
    +  cssBubbleBottomNoAnchor: goog.getCssName('goog-bubble-bottom-no-anchor'),
    +  cssBubbleLeft: goog.getCssName('goog-bubble-left'),
    +  cssBubbleRight: goog.getCssName('goog-bubble-right')
    +};
    +
    +
    +/**
    + * An auxiliary array optimizing the corner computation.
    + *
    + * @type {Array<goog.positioning.Corner>}
    + * @private
    + */
    +goog.ui.Bubble.corners_ = [
    +  goog.positioning.Corner.BOTTOM_RIGHT,
    +  goog.positioning.Corner.BOTTOM_LEFT,
    +  goog.positioning.Corner.TOP_RIGHT,
    +  goog.positioning.Corner.TOP_LEFT
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/button.js b/mapbox-gl/closure-library/closure/goog/ui/button.js
    new file mode 100644
    index 0000000000..08cf69c133
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/button.js
    @@ -0,0 +1,214 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A button control. This implementation extends {@link
    + * goog.ui.Control}.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/button.html
    + */
    +
    +goog.provide('goog.ui.Button');
    +goog.provide('goog.ui.Button.Side');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.ButtonSide');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.NativeButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A button control, rendered as a native browser button by default.
    + *
    + * @param {goog.ui.ControlContent=} opt_content Text caption or existing DOM
    + *     structure to display as the button's caption (if any).
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
    + *     decorate the button; defaults to {@link goog.ui.NativeButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Button = function(opt_content, opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, opt_content, opt_renderer ||
    +      goog.ui.NativeButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.Button, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.Button);
    +
    +
    +/**
    + * Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed}
    + * for details. Aliased from goog.ui.ButtonSide to support legacy users without
    + * creating a circular dependency in {@link goog.ui.ButtonRenderer}.
    + * @enum {number}
    + * @deprecated use {@link goog.ui.ButtonSide} instead.
    + */
    +goog.ui.Button.Side = goog.ui.ButtonSide;
    +
    +
    +/**
    + * Value associated with the button.
    + * @type {*}
    + * @private
    + */
    +goog.ui.Button.prototype.value_;
    +
    +
    +/**
    + * Tooltip text for the button, displayed on hover.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.Button.prototype.tooltip_;
    +
    +
    +// goog.ui.Button API implementation.
    +
    +
    +/**
    + * Returns the value associated with the button.
    + * @return {*} Button value (undefined if none).
    + */
    +goog.ui.Button.prototype.getValue = function() {
    +  return this.value_;
    +};
    +
    +
    +/**
    + * Sets the value associated with the button, and updates its DOM.
    + * @param {*} value New button value.
    + */
    +goog.ui.Button.prototype.setValue = function(value) {
    +  this.value_ = value;
    +  var renderer = /** @type {!goog.ui.ButtonRenderer} */ (this.getRenderer());
    +  renderer.setValue(this.getElement(), /** @type {string} */ (value));
    +};
    +
    +
    +/**
    + * Sets the value associated with the button.  Unlike {@link #setValue},
    + * doesn't update the button's DOM.  Considered protected; to be called only
    + * by renderer code during element decoration.
    + * @param {*} value New button value.
    + * @protected
    + */
    +goog.ui.Button.prototype.setValueInternal = function(value) {
    +  this.value_ = value;
    +};
    +
    +
    +/**
    + * Returns the tooltip for the button.
    + * @return {string|undefined} Tooltip text (undefined if none).
    + */
    +goog.ui.Button.prototype.getTooltip = function() {
    +  return this.tooltip_;
    +};
    +
    +
    +/**
    + * Sets the tooltip for the button, and updates its DOM.
    + * @param {string} tooltip New tooltip text.
    + */
    +goog.ui.Button.prototype.setTooltip = function(tooltip) {
    +  this.tooltip_ = tooltip;
    +  this.getRenderer().setTooltip(this.getElement(), tooltip);
    +};
    +
    +
    +/**
    + * Sets the tooltip for the button.  Unlike {@link #setTooltip}, doesn't update
    + * the button's DOM.  Considered protected; to be called only by renderer code
    + * during element decoration.
    + * @param {string} tooltip New tooltip text.
    + * @protected
    + */
    +goog.ui.Button.prototype.setTooltipInternal = function(tooltip) {
    +  this.tooltip_ = tooltip;
    +};
    +
    +
    +/**
    + * Collapses the border on one or both sides of the button, allowing it to be
    + * combined with the adjancent button(s), forming a single UI componenet with
    + * multiple targets.
    + * @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for
    + *     which borders should be collapsed.
    + */
    +goog.ui.Button.prototype.setCollapsed = function(sides) {
    +  this.getRenderer().setCollapsed(this, sides);
    +};
    +
    +
    +// goog.ui.Control & goog.ui.Component API implementation.
    +
    +
    +/** @override */
    +goog.ui.Button.prototype.disposeInternal = function() {
    +  goog.ui.Button.superClass_.disposeInternal.call(this);
    +  delete this.value_;
    +  delete this.tooltip_;
    +};
    +
    +
    +/** @override */
    +goog.ui.Button.prototype.enterDocument = function() {
    +  goog.ui.Button.superClass_.enterDocument.call(this);
    +  if (this.isSupportedState(goog.ui.Component.State.FOCUSED)) {
    +    var keyTarget = this.getKeyEventTarget();
    +    if (keyTarget) {
    +      this.getHandler().listen(keyTarget, goog.events.EventType.KEYUP,
    +          this.handleKeyEventInternal);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Attempts to handle a keyboard event; returns true if the event was handled,
    + * false otherwise.  If the button is enabled and the Enter/Space key was
    + * pressed, handles the event by dispatching an {@code ACTION} event,
    + * and returns true. Overrides {@link goog.ui.Control#handleKeyEventInternal}.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the key event was handled.
    + * @protected
    + * @override
    + */
    +goog.ui.Button.prototype.handleKeyEventInternal = function(e) {
    +  if (e.keyCode == goog.events.KeyCodes.ENTER &&
    +      e.type == goog.events.KeyHandler.EventType.KEY ||
    +      e.keyCode == goog.events.KeyCodes.SPACE &&
    +      e.type == goog.events.EventType.KEYUP) {
    +    return this.performActionInternal(e);
    +  }
    +  // Return true for space keypress (even though the event is handled on keyup)
    +  // as preventDefault needs to be called up keypress to take effect in IE and
    +  // WebKit.
    +  return e.keyCode == goog.events.KeyCodes.SPACE;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Buttons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.ButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Button(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/button_perf.html b/mapbox-gl/closure-library/closure/goog/ui/button_perf.html
    new file mode 100644
    index 0000000000..859b47de34
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/button_perf.html
    @@ -0,0 +1,176 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.ui.Button</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css" />
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.dom');
    +    goog.require('goog.events');
    +    goog.require('goog.events.EventHandler');
    +    goog.require('goog.events.EventType');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.jsunit');
    +    goog.require('goog.ui.Button');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.ui.Button Performance Tests</h1>
    +  <p>
    +    <b>User-agent:</b> <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +  <div id="renderSandbox"></div>
    +  <div id="decorateSandbox"></div>
    +  <script>
    +    // The sandboxen.
    +    var renderSandbox = goog.dom.getElement('renderSandbox');
    +    var decorateSandbox = goog.dom.getElement('decorateSandbox');
    +
    +    // Arrays of rendered/decorated buttons (so we can dispose of them).
    +    var renderedButtons;
    +    var decoratedButtons;
    +
    +    // 0-based index of the button currently being rendered/decorated.
    +    var renderIndex;
    +    var decorateIndex;
    +
    +    // Element currently being decorated.
    +    var elementToDecorate;
    +
    +    // Number of buttons to create/decorate per test run.
    +    var SAMPLES_PER_RUN = 200;
    +
    +    // The performance table.
    +    var table;
    +
    +    function setUpPage() {
    +      table = new goog.testing.PerformanceTable(
    +          goog.dom.getElement('perfTable'));
    +    }
    +
    +    // Sets up a render test.
    +    function setUpRenderTest() {
    +      renderedButtons = [];
    +      renderIndex = 0;
    +    }
    +
    +    // Cleans up after a render test.
    +    function cleanUpAfterRenderTest() {
    +      for (var i = 0, count = renderedButtons.length; i < count; i++) {
    +        renderedButtons[i].dispose();
    +      }
    +      renderedButtons = null;
    +      goog.dom.removeChildren(renderSandbox);
    +    }
    +
    +    // Sets up a decorate test.
    +    function setUpDecorateTest(opt_count) {
    +      var count = opt_count || 1000;
    +      for (var i = 0; i < count; i++) {
    +        decorateSandbox.appendChild(goog.dom.createDom('button', 'goog-button',
    +            'W00t!'));
    +      }
    +      elementToDecorate = decorateSandbox.firstChild;
    +      decoratedButtons = [];
    +      decorateIndex = 0;
    +    }
    +
    +    // Cleans up after a decorate test.
    +    function cleanUpAfterDecorateTest() {
    +      for (var i = 0, count = decoratedButtons.length; i < count; i++) {
    +        decoratedButtons[i].dispose();
    +      }
    +      decoratedButtons = null;
    +      goog.dom.removeChildren(decorateSandbox);
    +    }
    +
    +    // Renders the given number of buttons.  Since children are appended to
    +    // the same parent element in each performance test run, we keep track of
    +    // the current index via the global renderIndex variable.
    +    function renderButtons(count, autoDetectBiDi) {
    +      for (var i = 0; i < count; i++) {
    +        var button = new goog.ui.Button('W00t!');
    +        if (!autoDetectBiDi) {
    +          button.setRightToLeft(false);
    +        }
    +        button.render(renderSandbox);
    +        renderedButtons[renderIndex++] = button;
    +      }
    +    }
    +
    +    // Decorates "count" buttons.  The decorate sandbox contains enough child
    +    // elements for the whole test, but we only decorate up to "count" elements
    +    // per test run, so we need to keep track of where we are via the global
    +    // decorateIndex and elementToDecorate variables.
    +    function decorateButtons(count, autoDetectBiDi) {
    +      for (var i = 0; i < count; i++) {
    +        var next = elementToDecorate.nextSibling;
    +        var button = new goog.ui.Button();
    +        if (!autoDetectBiDi) {
    +          button.setRightToLeft(false);
    +        }
    +        button.decorate(elementToDecorate);
    +        decoratedButtons[decorateIndex++] = button;
    +        elementToDecorate = next;
    +      }
    +    }
    +
    +    function testRender() {
    +      setUpRenderTest();
    +      table.run(
    +          goog.partial(renderButtons, SAMPLES_PER_RUN, true),
    +          'Render ' + SAMPLES_PER_RUN + ' buttons (default)');
    +      cleanUpAfterRenderTest();
    +      assertEquals('The expected number of buttons must have been rendered',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(), renderIndex);
    +    }
    +
    +    function testDecorate() {
    +      setUpDecorateTest(SAMPLES_PER_RUN * table.getTimer().getNumSamples());
    +      table.run(
    +          goog.partial(decorateButtons, SAMPLES_PER_RUN, true),
    +          'Decorate ' + SAMPLES_PER_RUN + ' buttons (default)');
    +      cleanUpAfterDecorateTest();
    +      assertEquals('The expected number of buttons must have been decorated',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(),
    +          decorateIndex);
    +      assertNull('All buttons must have been decorated', elementToDecorate);
    +    }
    +
    +    function testRenderNoBiDiAutoDetect() {
    +      setUpRenderTest();
    +      table.run(
    +          goog.partial(renderButtons, SAMPLES_PER_RUN, false),
    +          'Render ' + SAMPLES_PER_RUN + ' buttons (no BiDi auto-detect)');
    +      cleanUpAfterRenderTest();
    +      assertEquals('The expected number of buttons must have been rendered',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(),
    +          renderIndex);
    +    }
    +
    +    function testDecorateNoBiDiAutoDetect() {
    +      setUpDecorateTest(SAMPLES_PER_RUN * table.getTimer().getNumSamples());
    +      table.run(
    +          goog.partial(decorateButtons, SAMPLES_PER_RUN, false),
    +          'Decorate ' + SAMPLES_PER_RUN + ' buttons (no BiDi auto-detect)');
    +      cleanUpAfterDecorateTest();
    +      assertEquals('The expected number of buttons must have been decorated',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(),
    +          decorateIndex);
    +      assertNull('All buttons must have been decorated', elementToDecorate);
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/button_test.html b/mapbox-gl/closure-library/closure/goog/ui/button_test.html
    new file mode 100644
    index 0000000000..3940a5e633
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/button_test.html
    @@ -0,0 +1,34 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: nicksantos@google.com (Nick Santos)
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Button
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.ButtonTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   Here's a button defined in markup:
    +  </p>
    +  <button id="demoButton" role="button">
    +   Foo
    +  </button>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/button_test.js b/mapbox-gl/closure-library/closure/goog/ui/button_test.js
    new file mode 100644
    index 0000000000..1878a02fa2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/button_test.js
    @@ -0,0 +1,283 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ButtonTest');
    +goog.setTestOnly('goog.ui.ButtonTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.ButtonSide');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.NativeButtonRenderer');
    +
    +var sandbox;
    +var button;
    +var clonedButtonDom;
    +var demoButtonElement;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  button = new goog.ui.Button();
    +  demoButtonElement = goog.dom.getElement('demoButton');
    +  clonedButtonDom = demoButtonElement.cloneNode(true);
    +}
    +
    +function tearDown() {
    +  button.dispose();
    +  demoButtonElement.parentNode.replaceChild(clonedButtonDom,
    +      demoButtonElement);
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Button must not be null', button);
    +  assertEquals('Renderer must default to expected value',
    +      goog.ui.NativeButtonRenderer.getInstance(), button.getRenderer());
    +
    +  var fakeDomHelper = {};
    +  var testButton = new goog.ui.Button('Hello',
    +      goog.ui.ButtonRenderer.getInstance(), fakeDomHelper);
    +  assertEquals('Content must have expected value', 'Hello',
    +      testButton.getContent());
    +  assertEquals('Renderer must have expected value',
    +      goog.ui.ButtonRenderer.getInstance(), testButton.getRenderer());
    +  assertEquals('DOM helper must have expected value', fakeDomHelper,
    +      testButton.getDomHelper());
    +  testButton.dispose();
    +}
    +
    +function testGetSetValue() {
    +  assertUndefined('Button\'s value must default to undefined',
    +      button.getValue());
    +  button.setValue(17);
    +  assertEquals('Button must have expected value', 17, button.getValue());
    +  button.render(sandbox);
    +  assertEquals('Button element must have expected value', '17',
    +      button.getElement().value);
    +  button.setValue('foo');
    +  assertEquals('Button element must have updated value', 'foo',
    +      button.getElement().value);
    +  button.setValueInternal('bar');
    +  assertEquals('Button must have new internal value', 'bar',
    +      button.getValue());
    +  assertEquals('Button element must be unchanged', 'foo',
    +      button.getElement().value);
    +}
    +
    +function testGetSetTooltip() {
    +  assertUndefined('Button\'s tooltip must default to undefined',
    +      button.getTooltip());
    +  button.setTooltip('Hello');
    +  assertEquals('Button must have expected tooltip', 'Hello',
    +      button.getTooltip());
    +  button.render(sandbox);
    +  assertEquals('Button element must have expected title', 'Hello',
    +      button.getElement().title);
    +  button.setTooltip('Goodbye');
    +  assertEquals('Button element must have updated title', 'Goodbye',
    +      button.getElement().title);
    +  button.setTooltipInternal('World');
    +  assertEquals('Button must have new internal tooltip', 'World',
    +      button.getTooltip());
    +  assertEquals('Button element must be unchanged', 'Goodbye',
    +      button.getElement().title);
    +}
    +
    +function testSetCollapsed() {
    +  assertNull('Button must not have any collapsed styling by default',
    +      button.getExtraClassNames());
    +  button.setCollapsed(goog.ui.ButtonSide.START);
    +  assertSameElements('Button must have the start side collapsed',
    +      ['goog-button-collapse-left'], button.getExtraClassNames());
    +  button.render(sandbox);
    +  assertSameElements('Button element must have the start side collapsed',
    +      ['goog-button', 'goog-button-collapse-left'],
    +      goog.dom.classlist.get(button.getElement()));
    +  button.setCollapsed(goog.ui.ButtonSide.BOTH);
    +  assertSameElements('Button must have both sides collapsed',
    +      ['goog-button-collapse-left', 'goog-button-collapse-right'],
    +      button.getExtraClassNames());
    +  assertSameElements('Button element must have both sides collapsed', [
    +    'goog-button',
    +    'goog-button-collapse-left',
    +    'goog-button-collapse-right'
    +  ], goog.dom.classlist.get(button.getElement()));
    +}
    +
    +function testDispose() {
    +  assertFalse('Button must not have been disposed of', button.isDisposed());
    +  button.render(sandbox);
    +  button.setValue('foo');
    +  button.setTooltip('bar');
    +  button.dispose();
    +  assertTrue('Button must have been disposed of', button.isDisposed());
    +  assertUndefined('Button\'s value must have been deleted',
    +      button.getValue());
    +  assertUndefined('Button\'s tooltip must have been deleted',
    +      button.getTooltip());
    +}
    +
    +function testBasicButtonBehavior() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +  goog.events.listen(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +
    +  button.decorate(demoButtonElement);
    +  goog.testing.events.fireClickSequence(demoButtonElement);
    +  assertEquals('Button must have dispatched ACTION on click', 1,
    +      dispatchedActionCount);
    +
    +  dispatchedActionCount = 0;
    +  var e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY,
    +      button);
    +  e.keyCode = goog.events.KeyCodes.ENTER;
    +  button.handleKeyEvent(e);
    +  assertEquals('Enabled button must have dispatched ACTION on Enter key', 1,
    +      dispatchedActionCount);
    +
    +  dispatchedActionCount = 0;
    +  e = new goog.events.Event(goog.events.EventType.KEYUP, button);
    +  e.keyCode = goog.events.KeyCodes.SPACE;
    +  button.handleKeyEvent(e);
    +  assertEquals('Enabled button must have dispatched ACTION on Space key', 1,
    +      dispatchedActionCount);
    +
    +  goog.events.unlisten(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +}
    +
    +function testDisabledButtonBehavior() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +  goog.events.listen(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +
    +  button.setEnabled(false);
    +
    +  dispatchedActionCount = 0;
    +  button.handleKeyEvent({keyCode: goog.events.KeyCodes.ENTER});
    +  assertEquals('Disabled button must not dispatch ACTION on Enter key',
    +      0, dispatchedActionCount);
    +
    +  dispatchedActionCount = 0;
    +  button.handleKeyEvent({
    +    keyCode: goog.events.KeyCodes.SPACE,
    +    type: goog.events.EventType.KEYUP
    +  });
    +  assertEquals('Disabled button must not have dispatched ACTION on Space',
    +      0, dispatchedActionCount);
    +
    +  goog.events.unlisten(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +}
    +
    +function testSpaceFireActionOnKeyUp() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +  goog.events.listen(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +
    +  dispatchedActionCount = 0;
    +  e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, button);
    +  e.keyCode = goog.events.KeyCodes.SPACE;
    +  button.handleKeyEvent(e);
    +  assertEquals('Button must not have dispatched ACTION on Space keypress',
    +      0, dispatchedActionCount);
    +  assertEquals('The default action (scrolling) must have been prevented ' +
    +               'for Space keypress',
    +               false,
    +               e.returnValue_);
    +
    +
    +  dispatchedActionCount = 0;
    +  e = new goog.events.Event(goog.events.EventType.KEYUP, button);
    +  e.keyCode = goog.events.KeyCodes.SPACE;
    +  button.handleKeyEvent(e);
    +  assertEquals('Button must have dispatched ACTION on Space keyup',
    +      1, dispatchedActionCount);
    +
    +  goog.events.unlisten(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +}
    +
    +function testEnterFireActionOnKeyPress() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +  goog.events.listen(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +
    +  dispatchedActionCount = 0;
    +  e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, button);
    +  e.keyCode = goog.events.KeyCodes.ENTER;
    +  button.handleKeyEvent(e);
    +  assertEquals('Button must have dispatched ACTION on Enter keypress',
    +      1, dispatchedActionCount);
    +
    +  dispatchedActionCount = 0;
    +  e = new goog.events.Event(goog.events.EventType.KEYUP, button);
    +  e.keyCode = goog.events.KeyCodes.ENTER;
    +  button.handleKeyEvent(e);
    +  assertEquals('Button must not have dispatched ACTION on Enter keyup',
    +      0, dispatchedActionCount);
    +
    +  goog.events.unlisten(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Button must not have aria label by default',
    +      button.getAriaLabel());
    +  button.setAriaLabel('Button 1');
    +  button.render();
    +  assertEquals('Button element must have expected aria-label', 'Button 1',
    +      button.getElement().getAttribute('aria-label'));
    +  button.setAriaLabel('Button 2');
    +  assertEquals('Button element must have updated aria-label', 'Button 2',
    +      button.getElement().getAttribute('aria-label'));
    +}
    +
    +function testSetAriaLabel_decorate() {
    +  assertNull('Button must not have aria label by default',
    +      button.getAriaLabel());
    +  button.setAriaLabel('Button 1');
    +  button.decorate(demoButtonElement);
    +  var el = button.getElementStrict();
    +  assertEquals('Button element must have expected aria-label', 'Button 1',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Button element must have expected aria-role', 'button',
    +      el.getAttribute('role'));
    +  button.setAriaLabel('Button 2');
    +  assertEquals('Button element must have updated aria-label', 'Button 2',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Button element must have expected aria-role', 'button',
    +      el.getAttribute('role'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer.js
    new file mode 100644
    index 0000000000..8d8f7c0fc7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer.js
    @@ -0,0 +1,219 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default renderer for {@link goog.ui.Button}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ButtonRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.ui.ButtonSide');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Button}s.  Extends the superclass with
    + * the following button-specific API methods:
    + * <ul>
    + *   <li>{@code getValue} - returns the button element's value
    + *   <li>{@code setValue} - updates the button element to reflect its new value
    + *   <li>{@code getTooltip} - returns the button element's tooltip text
    + *   <li>{@code setTooltip} - updates the button element's tooltip text
    + *   <li>{@code setCollapsed} - removes one or both of the button element's
    + *       borders
    + * </ul>
    + * For alternate renderers, see {@link goog.ui.NativeButtonRenderer},
    + * {@link goog.ui.CustomButtonRenderer}, and {@link goog.ui.FlatButtonRenderer}.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.ButtonRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ButtonRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.ButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ButtonRenderer.CSS_CLASS = goog.getCssName('goog-button');
    +
    +
    +/**
    + * Returns the ARIA role to be applied to buttons.
    + * @return {goog.a11y.aria.Role|undefined} ARIA role.
    + * @override
    + */
    +goog.ui.ButtonRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.BUTTON;
    +};
    +
    +
    +/**
    + * Updates the button's ARIA (accessibility) state if the button is being
    + * treated as a checkbox. Also makes sure that attributes which aren't
    + * supported by buttons aren't being added.
    + * @param {Element} element Element whose ARIA state is to be updated.
    + * @param {goog.ui.Component.State} state Component state being enabled or
    + *     disabled.
    + * @param {boolean} enable Whether the state is being enabled or disabled.
    + * @protected
    + * @override
    + */
    +goog.ui.ButtonRenderer.prototype.updateAriaState = function(element, state,
    +    enable) {
    +  switch (state) {
    +    // If button has CHECKED or SELECTED state, assign aria-pressed
    +    case goog.ui.Component.State.SELECTED:
    +    case goog.ui.Component.State.CHECKED:
    +      goog.asserts.assert(element,
    +          'The button DOM element cannot be null.');
    +      goog.a11y.aria.setState(element, goog.a11y.aria.State.PRESSED, enable);
    +      break;
    +    default:
    +    case goog.ui.Component.State.OPENED:
    +    case goog.ui.Component.State.DISABLED:
    +      goog.ui.ButtonRenderer.base(
    +          this, 'updateAriaState', element, state, enable);
    +      break;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.ButtonRenderer.prototype.createDom = function(button) {
    +  var element = goog.ui.ButtonRenderer.base(this, 'createDom', button);
    +  this.setTooltip(element, button.getTooltip());
    +
    +  var value = button.getValue();
    +  if (value) {
    +    this.setValue(element, value);
    +  }
    +
    +  // If this is a toggle button, set ARIA state
    +  if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
    +    this.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +                         button.isChecked());
    +  }
    +
    +  return element;
    +};
    +
    +
    +/** @override */
    +goog.ui.ButtonRenderer.prototype.decorate = function(button, element) {
    +  // The superclass implementation takes care of common attributes; we only
    +  // need to set the value and the tooltip.
    +  element = goog.ui.ButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +
    +  button.setValueInternal(this.getValue(element));
    +  button.setTooltipInternal(this.getTooltip(element));
    +
    +  // If this is a toggle button, set ARIA state
    +  if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
    +    this.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +                         button.isChecked());
    +  }
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Takes a button's root element, and returns the value associated with it.
    + * No-op in the base class.
    + * @param {Element} element The button's root element.
    + * @return {string|undefined} The button's value (undefined if none).
    + */
    +goog.ui.ButtonRenderer.prototype.getValue = goog.nullFunction;
    +
    +
    +/**
    + * Takes a button's root element and a value, and updates the element to reflect
    + * the new value.  No-op in the base class.
    + * @param {Element} element The button's root element.
    + * @param {string} value New value.
    + */
    +goog.ui.ButtonRenderer.prototype.setValue = goog.nullFunction;
    +
    +
    +/**
    + * Takes a button's root element, and returns its tooltip text.
    + * @param {Element} element The button's root element.
    + * @return {string|undefined} The tooltip text.
    + */
    +goog.ui.ButtonRenderer.prototype.getTooltip = function(element) {
    +  return element.title;
    +};
    +
    +
    +/**
    + * Takes a button's root element and a tooltip string, and updates the element
    + * with the new tooltip.
    + * @param {Element} element The button's root element.
    + * @param {string} tooltip New tooltip text.
    + * @protected
    + */
    +goog.ui.ButtonRenderer.prototype.setTooltip = function(element, tooltip) {
    +  if (element) {
    +    // Don't set a title attribute if there isn't a tooltip. Blank title
    +    // attributes can be interpreted incorrectly by screen readers.
    +    if (tooltip) {
    +      element.title = tooltip;
    +    } else {
    +      element.removeAttribute('title');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Collapses the border on one or both sides of the button, allowing it to be
    + * combined with the adjacent button(s), forming a single UI componenet with
    + * multiple targets.
    + * @param {goog.ui.Button} button Button to update.
    + * @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for
    + *     which borders should be collapsed.
    + * @protected
    + */
    +goog.ui.ButtonRenderer.prototype.setCollapsed = function(button, sides) {
    +  var isRtl = button.isRightToLeft();
    +  var collapseLeftClassName =
    +      goog.getCssName(this.getStructuralCssClass(), 'collapse-left');
    +  var collapseRightClassName =
    +      goog.getCssName(this.getStructuralCssClass(), 'collapse-right');
    +
    +  button.enableClassName(isRtl ? collapseRightClassName : collapseLeftClassName,
    +      !!(sides & goog.ui.ButtonSide.START));
    +  button.enableClassName(isRtl ? collapseLeftClassName : collapseRightClassName,
    +      !!(sides & goog.ui.ButtonSide.END));
    +};
    +
    +
    +/** @override */
    +goog.ui.ButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ButtonRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.html
    new file mode 100644
    index 0000000000..cf9af634b6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ButtonRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.js
    new file mode 100644
    index 0000000000..029f9d6724
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/buttonrenderer_test.js
    @@ -0,0 +1,242 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ButtonRendererTest');
    +goog.setTestOnly('goog.ui.ButtonRendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.ButtonSide');
    +goog.require('goog.ui.Component');
    +
    +var button, buttonRenderer, testRenderer;
    +var sandbox;
    +var expectedFailures;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +
    +
    +/**
    + * A subclass of ButtonRenderer that overrides
    + * {@code getStructuralCssClass} for testing purposes.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +function TestRenderer() {
    +  goog.ui.ButtonRenderer.call(this);
    +}
    +goog.inherits(TestRenderer, goog.ui.ButtonRenderer);
    +goog.addSingletonGetter(TestRenderer);
    +
    +
    +/** @override */
    +TestRenderer.prototype.getStructuralCssClass = function() {
    +  return 'goog-base';
    +};
    +
    +function setUp() {
    +  buttonRenderer = goog.ui.ButtonRenderer.getInstance();
    +  button = new goog.ui.Button('Hello', buttonRenderer);
    +  testRenderer = TestRenderer.getInstance();
    +}
    +
    +function tearDown() {
    +  button.dispose();
    +  goog.dom.removeChildren(sandbox);
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('ButtonRenderer singleton instance must not be null',
    +      buttonRenderer);
    +}
    +
    +function testGetAriaRole() {
    +  assertEquals('ButtonRenderer\'s ARIA role must have expected value',
    +      goog.a11y.aria.Role.BUTTON, buttonRenderer.getAriaRole());
    +}
    +
    +function testCreateDom() {
    +  var element = buttonRenderer.createDom(button);
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Element must be a DIV', 'DIV', element.tagName);
    +  assertHTMLEquals('Element must have expected structure',
    +      '<div class="goog-button">Hello</div>',
    +      goog.dom.getOuterHtml(element));
    +
    +  button.setTooltip('Hello, world!');
    +  button.setValue('foo');
    +  element = buttonRenderer.createDom(button);
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Element must be a DIV', 'DIV', element.tagName);
    +  assertSameElements('Element must have expected class name',
    +      ['goog-button'], goog.dom.classlist.get(element));
    +  assertEquals('Element must have expected title', 'Hello, world!',
    +      element.title);
    +  assertUndefined('Element must have no value', element.value);
    +  assertEquals('Element must have expected contents', 'Hello',
    +      element.innerHTML);
    +
    +  button.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = buttonRenderer.createDom(button);
    +  assertEquals('button\'s aria-pressed attribute must be false', 'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +}
    +
    +function testSetTooltip() {
    +  button.createDom();
    +  button.setTooltip('tooltip');
    +  assertEquals('tooltip', button.getElement().title);
    +  button.setTooltip('');
    +  assertEquals('', button.getElement().title);
    +  // IE7 doesn't support hasAttribute.
    +  if (button.getElement().hasAttribute) {
    +    assertFalse(button.getElement().hasAttribute('title'));
    +  }
    +}
    +
    +function testCreateDomAriaState() {
    +  button.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  button.setChecked(true);
    +  var element = buttonRenderer.createDom(button);
    +
    +  assertEquals('button\'s aria-pressed attribute must be true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +}
    +
    +function testUseAriaPressedForSelected() {
    +  button.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  button.setSelected(true);
    +  button.setRenderer(buttonRenderer);
    +  button.render();
    +  var element = button.getElement();
    +
    +  assertEquals('button\'s aria-pressed attribute must be true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +  assertEquals('button\'s aria-selected attribute must be empty', '',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testAriaDisabled() {
    +  button.setEnabled(false);
    +  button.setRenderer(buttonRenderer);
    +  button.render();
    +  var element = button.getElement();
    +
    +  assertEquals('button\'s aria-disabled attribute must be true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.DISABLED));
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML =
    +      '<div id="foo">Foo</div>\n' +
    +      '<div id="bar" title="Hello, world!">Bar</div>\n' +
    +      '<div id="toggle">Toggle</div>';
    +
    +  var foo = new goog.ui.Button(null, buttonRenderer);
    +  foo.decorate(goog.dom.getElement('foo'));
    +  assertEquals('foo\'s tooltip must be the empty string', '',
    +      foo.getTooltip());
    +  foo.dispose();
    +
    +  var bar = new goog.ui.Button(null, buttonRenderer);
    +  bar.decorate(goog.dom.getElement('bar'));
    +  assertEquals('bar\'s tooltip must be initialized', 'Hello, world!',
    +      bar.getTooltip());
    +  bar.dispose();
    +
    +  var toggle = new goog.ui.Button(null, buttonRenderer);
    +  toggle.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = goog.dom.getElement('toggle');
    +  assertNotNull(element);
    +  toggle.decorate(element);
    +  assertEquals('toggle\'s aria-pressed attribute must be false', 'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +  toggle.dispose();
    +}
    +
    +function testCollapse() {
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.START);
    +  assertSameElements('Button should have class to collapse start',
    +      ['goog-button-collapse-left'], button.getExtraClassNames());
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.END);
    +  assertSameElements('Button should have class to collapse end',
    +      ['goog-button-collapse-right'], button.getExtraClassNames());
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.BOTH);
    +  assertSameElements('Button should have classes to collapse both',
    +      ['goog-button-collapse-left', 'goog-button-collapse-right'],
    +      button.getExtraClassNames());
    +}
    +
    +function testCollapseRtl() {
    +  button.setRightToLeft(true);
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.START);
    +  assertSameElements('Button should have class to collapse start',
    +      ['goog-button-collapse-right'], button.getExtraClassNames());
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.END);
    +  assertSameElements('Button should have class to collapse end',
    +      ['goog-button-collapse-left'], button.getExtraClassNames());
    +  buttonRenderer.setCollapsed(button, goog.ui.ButtonSide.BOTH);
    +  assertSameElements('Button should have classes to collapse both',
    +      ['goog-button-collapse-left', 'goog-button-collapse-right'],
    +      button.getExtraClassNames());
    +}
    +
    +function testCollapseWithStructuralClass() {
    +  testRenderer.setCollapsed(button, goog.ui.ButtonSide.BOTH);
    +  assertSameElements('Should use structural class for collapse classes',
    +      ['goog-base-collapse-left', 'goog-base-collapse-right'],
    +      button.getExtraClassNames());
    +}
    +
    +function testUpdateAriaState() {
    +  var element = buttonRenderer.createDom(button);
    +  buttonRenderer.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +      true);
    +  assertEquals('Button must have pressed ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +
    +  // Test for updating a state other than CHECKED
    +  buttonRenderer.updateAriaState(element, goog.ui.Component.State.DISABLED,
    +      true);
    +  assertEquals('Button must have disabled ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.DISABLED));
    +
    +  buttonRenderer.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +      false);
    +  assertEquals('Control must no longer have pressed ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +  buttonRenderer.updateAriaState(element, goog.ui.Component.State.SELECTED,
    +      true);
    +  assertEquals('Button must have pressed ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.ButtonRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/buttonside.js b/mapbox-gl/closure-library/closure/goog/ui/buttonside.js
    new file mode 100644
    index 0000000000..75a5df3881
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/buttonside.js
    @@ -0,0 +1,41 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Enum for button side constants. In its own file so as to not
    + * cause a circular dependency with {@link goog.ui.ButtonRenderer}.
    + *
    + * @author doughtie@google.com (Gavin Doughtie)
    + */
    +
    +goog.provide('goog.ui.ButtonSide');
    +
    +
    +/**
    + * Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed}
    + * for details.
    + * @enum {number}
    + */
    +goog.ui.ButtonSide = {
    +  /** Neither side. */
    +  NONE: 0,
    +  /** Left for LTR, right for RTL. */
    +  START: 1,
    +  /** Right for LTR, left for RTL. */
    +  END: 2,
    +  /** Both sides. */
    +  BOTH: 3
    +};
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charcounter.js b/mapbox-gl/closure-library/closure/goog/ui/charcounter.js
    new file mode 100644
    index 0000000000..89e659a603
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charcounter.js
    @@ -0,0 +1,199 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Character counter widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/charcounter.html
    + */
    +
    +goog.provide('goog.ui.CharCounter');
    +goog.provide('goog.ui.CharCounter.Display');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.InputHandler');
    +
    +
    +
    +/**
    + * CharCounter widget. Counts the number of characters in a input field or a
    + * text box and displays the number of additional characters that may be
    + * entered before the maximum length is reached.
    + *
    + * @extends {goog.events.EventTarget}
    + * @param {HTMLInputElement|HTMLTextAreaElement} elInput Input or text area
    + *     element to count the number of characters in.
    + * @param {Element} elCount HTML element to display the remaining number of
    + *     characters in. You can pass in null for this if you don't want to expose
    + *     the number of chars remaining.
    + * @param {number} maxLength The maximum length.
    + * @param {goog.ui.CharCounter.Display=} opt_displayMode Display mode for this
    + *     char counter. Defaults to {@link goog.ui.CharCounter.Display.REMAINING}.
    + * @constructor
    + * @final
    + */
    +goog.ui.CharCounter = function(elInput, elCount, maxLength, opt_displayMode) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Input or text area element to count the number of characters in.
    +   * @type {HTMLInputElement|HTMLTextAreaElement}
    +   * @private
    +   */
    +  this.elInput_ = elInput;
    +
    +  /**
    +   * HTML element to display the remaining number of characters in.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elCount_ = elCount;
    +
    +  /**
    +   * The maximum length.
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxLength_ = maxLength;
    +
    +  /**
    +   * The display mode for this char counter.
    +   * @type {!goog.ui.CharCounter.Display}
    +   * @private
    +   */
    +  this.display_ = opt_displayMode || goog.ui.CharCounter.Display.REMAINING;
    +
    +  elInput.removeAttribute('maxlength');
    +
    +  /**
    +   * The input handler that provides the input event.
    +   * @type {goog.events.InputHandler}
    +   * @private
    +   */
    +  this.inputHandler_ = new goog.events.InputHandler(elInput);
    +
    +  goog.events.listen(this.inputHandler_,
    +      goog.events.InputHandler.EventType.INPUT, this.onChange_, false, this);
    +
    +  this.checkLength();
    +};
    +goog.inherits(goog.ui.CharCounter, goog.events.EventTarget);
    +
    +
    +/**
    + * Display mode for the char counter.
    + * @enum {number}
    + */
    +goog.ui.CharCounter.Display = {
    +  /** Widget displays the number of characters remaining (the default). */
    +  REMAINING: 0,
    +  /** Widget displays the number of characters entered. */
    +  INCREMENTAL: 1
    +};
    +
    +
    +/**
    + * Sets the maximum length.
    + *
    + * @param {number} maxLength The maximum length.
    + */
    +goog.ui.CharCounter.prototype.setMaxLength = function(maxLength) {
    +  this.maxLength_ = maxLength;
    +  this.checkLength();
    +};
    +
    +
    +/**
    + * Returns the maximum length.
    + *
    + * @return {number} The maximum length.
    + */
    +goog.ui.CharCounter.prototype.getMaxLength = function() {
    +  return this.maxLength_;
    +};
    +
    +
    +/**
    + * Sets the display mode.
    + *
    + * @param {!goog.ui.CharCounter.Display} displayMode The display mode.
    + */
    +goog.ui.CharCounter.prototype.setDisplayMode = function(displayMode) {
    +  this.display_ = displayMode;
    +  this.checkLength();
    +};
    +
    +
    +/**
    + * Returns the display mode.
    + *
    + * @return {!goog.ui.CharCounter.Display} The display mode.
    + */
    +goog.ui.CharCounter.prototype.getDisplayMode = function() {
    +  return this.display_;
    +};
    +
    +
    +/**
    + * Change event handler for input field.
    + *
    + * @param {goog.events.BrowserEvent} event Change event.
    + * @private
    + */
    +goog.ui.CharCounter.prototype.onChange_ = function(event) {
    +  this.checkLength();
    +};
    +
    +
    +/**
    + * Checks length of text in input field and updates the counter. Truncates text
    + * if the maximum lengths is exceeded.
    + */
    +goog.ui.CharCounter.prototype.checkLength = function() {
    +  var count = this.elInput_.value.length;
    +
    +  // There's no maxlength property for textareas so instead we truncate the
    +  // text if it gets too long. It's also used to truncate the text in a input
    +  // field if the maximum length is changed.
    +  if (count > this.maxLength_) {
    +
    +    var scrollTop = this.elInput_.scrollTop;
    +    var scrollLeft = this.elInput_.scrollLeft;
    +
    +    this.elInput_.value = this.elInput_.value.substring(0, this.maxLength_);
    +    count = this.maxLength_;
    +
    +    this.elInput_.scrollTop = scrollTop;
    +    this.elInput_.scrollLeft = scrollLeft;
    +  }
    +
    +  if (this.elCount_) {
    +    var incremental = this.display_ == goog.ui.CharCounter.Display.INCREMENTAL;
    +    goog.dom.setTextContent(
    +        this.elCount_,
    +        String(incremental ? count : this.maxLength_ - count));
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.CharCounter.prototype.disposeInternal = function() {
    +  goog.ui.CharCounter.superClass_.disposeInternal.call(this);
    +  delete this.elInput_;
    +  this.inputHandler_.dispose();
    +  this.inputHandler_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.html b/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.html
    new file mode 100644
    index 0000000000..ca2f375fd7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.CharCounter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.CharCounterTest');
    +  </script>
    + </head>
    + <body>
    +  <textarea id="test-textarea-id">
    +  </textarea>
    +  <div>
    +   Characters remaining:
    +   <span class="char-count">
    +   </span>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.js b/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.js
    new file mode 100644
    index 0000000000..50b4bd1177
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charcounter_test.js
    @@ -0,0 +1,218 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.CharCounterTest');
    +goog.setTestOnly('goog.ui.CharCounterTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.asserts');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.CharCounter');
    +goog.require('goog.userAgent');
    +
    +var countElement, charCounter, inputElement;
    +var incremental = goog.ui.CharCounter.Display.INCREMENTAL;
    +var remaining = goog.ui.CharCounter.Display.REMAINING;
    +var maxLength = 25;
    +
    +function setUp() {
    +  inputElement = goog.dom.getElement('test-textarea-id');
    +  inputElement.value = '';
    +  countElement = goog.dom.getElementByClass('char-count');
    +  goog.dom.setTextContent(countElement, '');
    +  charCounter =
    +      new goog.ui.CharCounter(inputElement, countElement, maxLength);
    +}
    +
    +function tearDown() {
    +  charCounter.dispose();
    +}
    +
    +function setupCheckLength(content, mode) {
    +  inputElement.value = content;
    +  charCounter.setDisplayMode(mode);
    +  charCounter.checkLength();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Character counter can not be null', charCounter);
    +  assertEquals(maxLength.toString(), goog.dom.getTextContent(countElement));
    +}
    +
    +function testSetMaxLength() {
    +  charCounter.setMaxLength(10);
    +  assertEquals('10', goog.dom.getTextContent(countElement));
    +
    +  var tooLongContent = 'This is too long text content';
    +  inputElement.value = tooLongContent;
    +  charCounter.setMaxLength(10);
    +  assertEquals('0', goog.dom.getTextContent(countElement));
    +  assertEquals('This is to', inputElement.value);
    +}
    +
    +function testGetMaxLength() {
    +  assertEquals(maxLength, charCounter.getMaxLength());
    +}
    +
    +function testSetDisplayMode() {
    +  // Test counter to be in incremental mode
    +  charCounter.setDisplayMode(incremental);
    +  assertEquals('0', goog.dom.getTextContent(countElement));
    +
    +  // Test counter to be in remaining mode
    +  charCounter.setDisplayMode(remaining);
    +  assertEquals(maxLength.toString(), goog.dom.getTextContent(countElement));
    +}
    +
    +function testGetDisplayMode() {
    +  assertEquals(remaining, charCounter.getDisplayMode());
    +
    +  var incrementalCharCounter = new goog.ui.CharCounter(
    +      inputElement, countElement, maxLength, incremental);
    +  assertEquals(incremental, incrementalCharCounter.getDisplayMode());
    +}
    +
    +function testCheckLength() {
    +  // Test the characters remaining in DOM
    +  setupCheckLength('', remaining);
    +  assertEquals(maxLength.toString(), goog.dom.getTextContent(countElement));
    +
    +  // Test the characters incremental in DOM
    +  setupCheckLength('', incremental);
    +  assertEquals('0', goog.dom.getTextContent(countElement));
    +}
    +
    +function testCheckLength_limitedContent() {
    +  var limitedContent = 'Limited text content';
    +  var limitedContentLength = limitedContent.length;
    +  var remainingLimitedContentLength = maxLength - limitedContentLength;
    +
    +  // Set some content and test the characters remaining in DOM
    +  setupCheckLength(limitedContent, remaining);
    +
    +  assertEquals(limitedContent, inputElement.value);
    +  assertEquals(remainingLimitedContentLength.toString(),
    +      goog.dom.getTextContent(countElement));
    +
    +  // Test the characters incremented in DOM with limited content
    +  charCounter.setDisplayMode(incremental);
    +  charCounter.checkLength();
    +
    +  assertEquals(limitedContent, inputElement.value);
    +  assertEquals(limitedContentLength.toString(),
    +      goog.dom.getTextContent(countElement));
    +}
    +
    +function testCheckLength_overflowContent() {
    +  var tooLongContent = 'This is too long text content';
    +  var truncatedContent = 'This is too long text con';
    +
    +  // Set content longer than the maxLength and test the characters remaining
    +  // in DOM with overflowing content
    +  setupCheckLength(tooLongContent, remaining);
    +
    +  assertEquals(truncatedContent, inputElement.value);
    +  assertEquals('0', goog.dom.getTextContent(countElement));
    +
    +  // Set content longer than the maxLength and test the characters
    +  // incremented in DOM with overflowing content
    +  setupCheckLength(tooLongContent, incremental);
    +
    +  assertEquals(truncatedContent, inputElement.value);
    +  assertEquals(maxLength.toString(), goog.dom.getTextContent(countElement));
    +}
    +
    +function testCheckLength_newLineContent() {
    +  var newLineContent = 'New\nline';
    +  var newLineContentLength = newLineContent.length;
    +  var remainingNewLineContentLength = maxLength - newLineContentLength;
    +
    +  var carriageReturnContent = 'New\r\nline';
    +  var carriageReturnContentLength = carriageReturnContent.length;
    +  var remainingCarriageReturnContentLength =
    +      maxLength - carriageReturnContentLength;
    +
    +  // Set some content with new line characters and test the characters
    +  // remaining in DOM
    +  setupCheckLength(newLineContent, remaining);
    +
    +  // Test for IE 7,8 which appends \r to \n
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9.0')) {
    +    assertEquals(carriageReturnContent, inputElement.value);
    +    assertEquals(remainingCarriageReturnContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  } else {
    +    assertEquals(newLineContent, inputElement.value);
    +    assertEquals(remainingNewLineContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  }
    +
    +  // Set some content with new line characters and test the characters
    +  // incremental in DOM
    +  setupCheckLength(newLineContent, incremental);
    +
    +  // Test for IE 7,8 which appends \r to \n
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9.0')) {
    +    assertEquals(carriageReturnContent, inputElement.value);
    +    assertEquals(carriageReturnContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  } else {
    +    assertEquals(newLineContent, inputElement.value);
    +    assertEquals(newLineContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  }
    +}
    +
    +function testCheckLength_carriageReturnContent() {
    +  var newLineContent = 'New\nline';
    +  var newLineContentLength = newLineContent.length;
    +  var remainingNewLineContentLength = maxLength - newLineContentLength;
    +
    +  var carriageReturnContent = 'New\r\nline';
    +  var carriageReturnContentLength = carriageReturnContent.length;
    +  var remainingCarriageReturnContentLength =
    +      maxLength - carriageReturnContentLength;
    +
    +  // Set some content with carriage return characters and test the
    +  // characters remaining in DOM
    +  setupCheckLength(carriageReturnContent, remaining);
    +
    +  // Test for IE 7,8
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9.0')) {
    +    assertEquals(carriageReturnContent, inputElement.value);
    +    assertEquals(remainingCarriageReturnContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  } else {
    +    // Others replace \r\n with \n
    +    assertEquals(newLineContent, inputElement.value);
    +    assertEquals(remainingNewLineContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  }
    +
    +  // Set some content with carriage return characters and test the
    +  // characters incremental in DOM
    +  setupCheckLength(carriageReturnContent, incremental);
    +
    +  // Test for IE 7,8
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9.0')) {
    +    assertEquals(carriageReturnContent, inputElement.value);
    +    assertEquals(carriageReturnContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  } else {
    +    // Others replace \r\n with \n
    +    assertEquals(newLineContent, inputElement.value);
    +    assertEquals(newLineContentLength.toString(),
    +        goog.dom.getTextContent(countElement));
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charpicker.js b/mapbox-gl/closure-library/closure/goog/ui/charpicker.js
    new file mode 100644
    index 0000000000..f485ac2f86
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charpicker.js
    @@ -0,0 +1,922 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Character Picker widget for picking any Unicode character.
    + *
    + * @see ../demos/charpicker.html
    + */
    +
    +goog.provide('goog.ui.CharPicker');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.i18n.CharListDecompressor');
    +goog.require('goog.i18n.uChar');
    +goog.require('goog.structs.Set');
    +goog.require('goog.style');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ContainerScroller');
    +goog.require('goog.ui.FlatButtonRenderer');
    +goog.require('goog.ui.HoverCard');
    +goog.require('goog.ui.LabelInput');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.Tooltip');
    +
    +
    +
    +/**
    + * Character Picker Class. This widget can be used to pick any Unicode
    + * character by traversing a category-subcategory structure or by inputing its
    + * hex value.
    + *
    + * See charpicker.html demo for example usage.
    + * @param {goog.i18n.CharPickerData} charPickerData Category names and charlist.
    + * @param {!goog.i18n.uChar.NameFetcher} charNameFetcher Object which fetches
    + *     the names of the characters that are shown in the widget. These names
    + *     may be stored locally or come from an external source.
    + * @param {Array<string>=} opt_recents List of characters to be displayed in
    + *     resently selected characters area.
    + * @param {number=} opt_initCategory Sequence number of initial category.
    + * @param {number=} opt_initSubcategory Sequence number of initial subcategory.
    + * @param {number=} opt_rowCount Number of rows in the grid.
    + * @param {number=} opt_columnCount Number of columns in the grid.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.ui.CharPicker = function(charPickerData, charNameFetcher, opt_recents,
    +                              opt_initCategory, opt_initSubcategory,
    +                              opt_rowCount, opt_columnCount, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Object used to retrieve character names.
    +   * @type {!goog.i18n.uChar.NameFetcher}
    +   * @private
    +   */
    +  this.charNameFetcher_ = charNameFetcher;
    +
    +  /**
    +   * Object containing character lists and category names.
    +   * @type {goog.i18n.CharPickerData}
    +   * @private
    +   */
    +  this.data_ = charPickerData;
    +
    +  /**
    +   * The category number to be used on widget init.
    +   * @type {number}
    +   * @private
    +   */
    +  this.initCategory_ = opt_initCategory || 0;
    +
    +  /**
    +   * The subcategory number to be used on widget init.
    +   * @type {number}
    +   * @private
    +   */
    +  this.initSubcategory_ = opt_initSubcategory || 0;
    +
    +  /**
    +   * Number of columns in the grid.
    +   * @type {number}
    +   * @private
    +   */
    +  this.columnCount_ = opt_columnCount || 10;
    +
    +  /**
    +   * Number of entries to be added to the grid.
    +   * @type {number}
    +   * @private
    +   */
    +  this.gridsize_ = (opt_rowCount || 10) * this.columnCount_;
    +
    +  /**
    +   * Number of the recently selected characters displayed.
    +   * @type {number}
    +   * @private
    +   */
    +  this.recentwidth_ = this.columnCount_ + 1;
    +
    +  /**
    +   * List of recently used characters.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.recents_ = opt_recents || [];
    +
    +  /**
    +   * Handler for events.
    +   * @type {goog.events.EventHandler<!goog.ui.CharPicker>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Decompressor used to get the list of characters from a base88 encoded
    +   * character list.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.decompressor_ = new goog.i18n.CharListDecompressor();
    +};
    +goog.inherits(goog.ui.CharPicker, goog.ui.Component);
    +
    +
    +/**
    + * The last selected character.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.selectedChar_ = null;
    +
    +
    +/**
    + * Set of formatting characters whose display need to be swapped with nbsp
    + * to prevent layout issues.
    + * @type {goog.structs.Set}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.layoutAlteringChars_ = null;
    +
    +
    +/**
    + * The top category menu.
    + * @type {goog.ui.Menu}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.menu_ = null;
    +
    +
    +/**
    + * The top category menu button.
    + * @type {goog.ui.MenuButton}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.menubutton_ = null;
    +
    +
    +/**
    + * The subcategory menu.
    + * @type {goog.ui.Menu}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.submenu_ = null;
    +
    +
    +/**
    + * The subcategory menu button.
    + * @type {goog.ui.MenuButton}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.submenubutton_ = null;
    +
    +
    +/** @type {number} */
    +goog.ui.CharPicker.prototype.itempos;
    +
    +
    +/** @type {!Array<string>} */
    +goog.ui.CharPicker.prototype.items;
    +
    +
    +/** @private {!goog.events.KeyHandler} */
    +goog.ui.CharPicker.prototype.keyHandler_;
    +
    +
    +/**
    + * Category index used to index the data tables.
    + * @type {number}
    + */
    +goog.ui.CharPicker.prototype.category;
    +
    +
    +/** @private {Element} */
    +goog.ui.CharPicker.prototype.stick_ = null;
    +
    +
    +/**
    + * The element representing the number of rows visible in the grid.
    + * This along with goog.ui.CharPicker.stick_ would help to create a scrollbar
    + * of right size.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.stickwrap_ = null;
    +
    +
    +/**
    + * The component containing all the buttons for each character in display.
    + * @type {goog.ui.Component}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.grid_ = null;
    +
    +
    +/**
    + * The component used for extra information about the character set displayed.
    + * @type {goog.ui.Component}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.notice_ = null;
    +
    +
    +/**
    + * Grid displaying recently selected characters.
    + * @type {goog.ui.Component}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.recentgrid_ = null;
    +
    +
    +/**
    + * Input field for entering the hex value of the character.
    + * @type {goog.ui.Component}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.input_ = null;
    +
    +
    +/**
    + * OK button for entering hex value of the character.
    + * @private {goog.ui.Button}
    + */
    +goog.ui.CharPicker.prototype.okbutton_ = null;
    +
    +
    +/**
    + * Element displaying character name in preview.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.charNameEl_ = null;
    +
    +
    +/**
    + * Element displaying character in preview.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.zoomEl_ = null;
    +
    +
    +/**
    + * Element displaying character number (codepoint) in preview.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.unicodeEl_ = null;
    +
    +
    +/**
    + * Hover card for displaying the preview of a character.
    + * Preview would contain character in large size and its U+ notation. It would
    + * also display the name, if available.
    + * @type {goog.ui.HoverCard}
    + * @private
    + */
    +goog.ui.CharPicker.prototype.hc_ = null;
    +
    +
    +/**
    + * Gets the last selected character.
    + * @return {?string} The last selected character.
    + */
    +goog.ui.CharPicker.prototype.getSelectedChar = function() {
    +  return this.selectedChar_;
    +};
    +
    +
    +/**
    + * Gets the list of characters user selected recently.
    + * @return {Array<string>} The recent character list.
    + */
    +goog.ui.CharPicker.prototype.getRecentChars = function() {
    +  return this.recents_;
    +};
    +
    +
    +/** @override */
    +goog.ui.CharPicker.prototype.createDom = function() {
    +  goog.ui.CharPicker.superClass_.createDom.call(this);
    +
    +  this.decorateInternal(this.getDomHelper().createElement('div'));
    +};
    +
    +
    +/** @override */
    +goog.ui.CharPicker.prototype.disposeInternal = function() {
    +  goog.dispose(this.hc_);
    +  this.hc_ = null;
    +  goog.dispose(this.eventHandler_);
    +  this.eventHandler_ = null;
    +  goog.ui.CharPicker.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/** @override */
    +goog.ui.CharPicker.prototype.decorateInternal = function(element) {
    +  goog.ui.CharPicker.superClass_.decorateInternal.call(this, element);
    +
    +  // The chars below cause layout disruption or too narrow to hover:
    +  // \u0020, \u00AD, \u2000 - \u200f, \u2028 - \u202f, \u3000, \ufeff
    +  var chrs = this.decompressor_.toCharList(':2%C^O80V1H2s2G40Q%s0');
    +  this.layoutAlteringChars_ = new goog.structs.Set(chrs);
    +
    +  this.menu_ = new goog.ui.Menu(this.getDomHelper());
    +
    +  var categories = this.data_.categories;
    +  for (var i = 0; i < this.data_.categories.length; i++) {
    +    this.menu_.addChild(this.createMenuItem_(i, categories[i]), true);
    +  }
    +
    +  this.menubutton_ = new goog.ui.MenuButton('Category Menu', this.menu_,
    +      /* opt_renderer */ undefined, this.getDomHelper());
    +  this.addChild(this.menubutton_, true);
    +
    +  this.submenu_ = new goog.ui.Menu(this.getDomHelper());
    +
    +  this.submenubutton_ = new goog.ui.MenuButton('Subcategory Menu',
    +      this.submenu_, /* opt_renderer */ undefined, this.getDomHelper());
    +  this.addChild(this.submenubutton_, true);
    +
    +  // The containing component for grid component and the scroller.
    +  var gridcontainer = new goog.ui.Component(this.getDomHelper());
    +  this.addChild(gridcontainer, true);
    +
    +  var stickwrap = new goog.ui.Component(this.getDomHelper());
    +  gridcontainer.addChild(stickwrap, true);
    +  this.stickwrap_ = stickwrap.getElement();
    +
    +  var stick = new goog.ui.Component(this.getDomHelper());
    +  stickwrap.addChild(stick, true);
    +  this.stick_ = stick.getElement();
    +
    +  this.grid_ = new goog.ui.Component(this.getDomHelper());
    +  gridcontainer.addChild(this.grid_, true);
    +
    +  this.notice_ = new goog.ui.Component(this.getDomHelper());
    +  this.notice_.setElementInternal(this.getDomHelper().createDom('div'));
    +  this.addChild(this.notice_, true);
    +
    +  // The component used for displaying 'Recent Selections' label.
    +  /**
    +   * @desc The text label above the list of recently selected characters.
    +   */
    +  var MSG_CHAR_PICKER_RECENT_SELECTIONS = goog.getMsg('Recent Selections:');
    +  var recenttext = new goog.ui.Component(this.getDomHelper());
    +  recenttext.setElementInternal(this.getDomHelper().createDom('span', null,
    +      MSG_CHAR_PICKER_RECENT_SELECTIONS));
    +  this.addChild(recenttext, true);
    +
    +  this.recentgrid_ = new goog.ui.Component(this.getDomHelper());
    +  this.addChild(this.recentgrid_, true);
    +
    +  // The component used for displaying 'U+'.
    +  var uplus = new goog.ui.Component(this.getDomHelper());
    +  uplus.setElementInternal(this.getDomHelper().createDom('span', null, 'U+'));
    +  this.addChild(uplus, true);
    +
    +  /**
    +   * @desc The text inside the input box to specify the hex code of a character.
    +   */
    +  var MSG_CHAR_PICKER_HEX_INPUT = goog.getMsg('Hex Input');
    +  this.input_ = new goog.ui.LabelInput(
    +      MSG_CHAR_PICKER_HEX_INPUT, this.getDomHelper());
    +  this.addChild(this.input_, true);
    +
    +  this.okbutton_ = new goog.ui.Button(
    +      'OK', /* opt_renderer */ undefined, this.getDomHelper());
    +  this.addChild(this.okbutton_, true);
    +  this.okbutton_.setEnabled(false);
    +
    +  this.zoomEl_ = this.getDomHelper().createDom('div',
    +      {id: 'zoom', className: goog.getCssName('goog-char-picker-char-zoom')});
    +
    +  this.charNameEl_ = this.getDomHelper().createDom('div',
    +      {id: 'charName', className: goog.getCssName('goog-char-picker-name')});
    +
    +  this.unicodeEl_ = this.getDomHelper().createDom('div',
    +      {id: 'unicode', className: goog.getCssName('goog-char-picker-unicode')});
    +
    +  var card = this.getDomHelper().createDom('div',
    +      {'id': 'preview'},
    +      this.zoomEl_, this.charNameEl_, this.unicodeEl_);
    +  goog.style.setElementShown(card, false);
    +  this.hc_ = new goog.ui.HoverCard({'DIV': 'char'},
    +      /* opt_checkDescendants */ undefined, this.getDomHelper());
    +  this.hc_.setElement(card);
    +  var self = this;
    +
    +  /**
    +   * Function called by hover card just before it is visible to collect data.
    +   */
    +  function onBeforeShow() {
    +    var trigger = self.hc_.getAnchorElement();
    +    var ch = self.getChar_(trigger);
    +    if (ch) {
    +      goog.dom.setTextContent(self.zoomEl_, self.displayChar_(ch));
    +      goog.dom.setTextContent(self.unicodeEl_, goog.i18n.uChar.toHexString(ch));
    +      // Clear the character name since we don't want to show old data because
    +      // it is retrieved asynchronously and the DOM object is re-used
    +      goog.dom.setTextContent(self.charNameEl_, '');
    +      self.charNameFetcher_.getName(ch, function(charName) {
    +        if (charName) {
    +          goog.dom.setTextContent(self.charNameEl_, charName);
    +        }
    +      });
    +    }
    +  }
    +
    +  goog.events.listen(this.hc_, goog.ui.HoverCard.EventType.BEFORE_SHOW,
    +                     onBeforeShow);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.getCssName('goog-char-picker'));
    +  goog.dom.classlist.add(goog.asserts.assert(this.stick_),
    +                         goog.getCssName('goog-stick'));
    +  goog.dom.classlist.add(goog.asserts.assert(this.stickwrap_),
    +                         goog.getCssName('goog-stickwrap'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(gridcontainer.getElement()),
    +      goog.getCssName('goog-char-picker-grid-container'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.grid_.getElement()),
    +      goog.getCssName('goog-char-picker-grid'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.recentgrid_.getElement()),
    +      goog.getCssName('goog-char-picker-grid'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.recentgrid_.getElement()),
    +      goog.getCssName('goog-char-picker-recents'));
    +
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.notice_.getElement()),
    +      goog.getCssName('goog-char-picker-notice'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(uplus.getElement()),
    +      goog.getCssName('goog-char-picker-uplus'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.input_.getElement()),
    +      goog.getCssName('goog-char-picker-input-box'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.okbutton_.getElement()),
    +      goog.getCssName('goog-char-picker-okbutton'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(card),
    +      goog.getCssName('goog-char-picker-hovercard'));
    +
    +  this.hc_.className = goog.getCssName('goog-char-picker-hovercard');
    +
    +  this.grid_.buttoncount = this.gridsize_;
    +  this.recentgrid_.buttoncount = this.recentwidth_;
    +  this.populateGridWithButtons_(this.grid_);
    +  this.populateGridWithButtons_(this.recentgrid_);
    +
    +  this.updateGrid_(this.recentgrid_, this.recents_);
    +  this.setSelectedCategory_(this.initCategory_, this.initSubcategory_);
    +  new goog.ui.ContainerScroller(this.menu_);
    +  new goog.ui.ContainerScroller(this.submenu_);
    +
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.menu_.getElement()),
    +      goog.getCssName('goog-char-picker-menu'));
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.submenu_.getElement()),
    +      goog.getCssName('goog-char-picker-menu'));
    +};
    +
    +
    +/** @override */
    +goog.ui.CharPicker.prototype.enterDocument = function() {
    +  goog.ui.CharPicker.superClass_.enterDocument.call(this);
    +  var inputkh = new goog.events.InputHandler(this.input_.getElement());
    +  this.keyHandler_ = new goog.events.KeyHandler(this.input_.getElement());
    +
    +  // Stop the propagation of ACTION events at menu and submenu buttons.
    +  // If stopped at capture phase, the button will not be set to normal state.
    +  // If not stopped, the user widget will receive the event, which is
    +  // undesired. User widget should receive an event only on the character
    +  // click.
    +  this.eventHandler_.
    +      listen(
    +          this.menubutton_,
    +          goog.ui.Component.EventType.ACTION,
    +          goog.events.Event.stopPropagation).
    +      listen(
    +          this.submenubutton_,
    +          goog.ui.Component.EventType.ACTION,
    +          goog.events.Event.stopPropagation).
    +      listen(
    +          this,
    +          goog.ui.Component.EventType.ACTION,
    +          this.handleSelectedItem_,
    +          true).
    +      listen(
    +          inputkh,
    +          goog.events.InputHandler.EventType.INPUT,
    +          this.handleInput_).
    +      listen(
    +          this.keyHandler_,
    +          goog.events.KeyHandler.EventType.KEY,
    +          this.handleEnter_).
    +      listen(
    +          this.recentgrid_,
    +          goog.ui.Component.EventType.FOCUS,
    +          this.handleFocus_).
    +      listen(
    +          this.grid_,
    +          goog.ui.Component.EventType.FOCUS,
    +          this.handleFocus_);
    +
    +  goog.events.listen(this.okbutton_.getElement(),
    +      goog.events.EventType.MOUSEDOWN, this.handleOkClick_, true, this);
    +
    +  goog.events.listen(this.stickwrap_, goog.events.EventType.SCROLL,
    +      this.handleScroll_, true, this);
    +};
    +
    +
    +/**
    + * Handles the button focus by updating the aria label with the character name
    + * so it becomes possible to get spoken feedback while tabbing through the
    + * visible symbols.
    + * @param {goog.events.Event} e The focus event.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleFocus_ = function(e) {
    +  var button = e.target;
    +  var element = button.getElement();
    +  var ch = this.getChar_(element);
    +
    +  // Clear the aria label to avoid speaking the old value in case the button
    +  // element has no char attribute or the character name cannot be retrieved.
    +  goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, '');
    +
    +  if (ch) {
    +    // This is working with screen readers because the call to getName is
    +    // synchronous once the values have been prefetched by the RemoteNameFetcher
    +    // and because it is always synchronous when using the LocalNameFetcher.
    +    // Also, the special character itself is not used as the label because some
    +    // screen readers, notably ChromeVox, are not able to speak them.
    +    // TODO(user): Consider changing the NameFetcher API to provide a
    +    // method that lets the caller retrieve multiple character names at once
    +    // so that this asynchronous gymnastic can be avoided.
    +    this.charNameFetcher_.getName(ch, function(charName) {
    +      if (charName) {
    +        goog.a11y.aria.setState(
    +            element, goog.a11y.aria.State.LABEL, charName);
    +      }
    +    });
    +  }
    +};
    +
    +
    +/**
    + * On scroll, updates the grid with characters correct to the scroll position.
    + * @param {goog.events.Event} e Scroll event to handle.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleScroll_ = function(e) {
    +  var height = e.target.scrollHeight;
    +  var top = e.target.scrollTop;
    +  var itempos = Math.ceil(top * this.items.length / (this.columnCount_ *
    +      height)) * this.columnCount_;
    +  if (this.itempos != itempos) {
    +    this.itempos = itempos;
    +    this.modifyGridWithItems_(this.grid_, this.items, itempos);
    +  }
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * On a menu click, sets correct character set in the grid; on a grid click
    + * accept the character as the selected one and adds to recent selection, if not
    + * already present.
    + * @param {goog.events.Event} e Event for the click on menus or grid.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleSelectedItem_ = function(e) {
    +  var parent = /** @type {goog.ui.Component} */ (e.target).getParent();
    +  if (parent == this.menu_) {
    +    this.menu_.setVisible(false);
    +    this.setSelectedCategory_(e.target.getValue());
    +  } else if (parent == this.submenu_) {
    +    this.submenu_.setVisible(false);
    +    this.setSelectedSubcategory_(e.target.getValue());
    +  } else if (parent == this.grid_) {
    +    var button = e.target.getElement();
    +    this.selectedChar_ = this.getChar_(button);
    +    this.updateRecents_(this.selectedChar_);
    +  } else if (parent == this.recentgrid_) {
    +    this.selectedChar_ = this.getChar_(e.target.getElement());
    +  }
    +};
    +
    +
    +/**
    + * When user types the characters displays the preview. Enables the OK button,
    + * if the character is valid.
    + * @param {goog.events.Event} e Event for typing in input field.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleInput_ = function(e) {
    +  var ch = this.getInputChar();
    +  if (ch) {
    +    goog.dom.setTextContent(this.zoomEl_, ch);
    +    goog.dom.setTextContent(this.unicodeEl_, goog.i18n.uChar.toHexString(ch));
    +    goog.dom.setTextContent(this.charNameEl_, '');
    +    var coord =
    +        new goog.ui.Tooltip.ElementTooltipPosition(this.input_.getElement());
    +    this.hc_.setPosition(coord);
    +    this.hc_.triggerForElement(this.input_.getElement());
    +    this.okbutton_.setEnabled(true);
    +  } else {
    +    this.hc_.cancelTrigger();
    +    this.hc_.setVisible(false);
    +    this.okbutton_.setEnabled(false);
    +  }
    +};
    +
    +
    +/**
    + * On OK click accepts the character and updates the recent char list.
    + * @param {goog.events.Event=} opt_event Event for click on OK button.
    + * @return {boolean} Indicates whether to propagate event.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleOkClick_ = function(opt_event) {
    +  var ch = this.getInputChar();
    +  if (ch && ch.charCodeAt(0)) {
    +    this.selectedChar_ = ch;
    +    this.updateRecents_(ch);
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Behaves exactly like the OK button on Enter key.
    + * @param {goog.events.KeyEvent} e Event for enter on the input field.
    + * @return {boolean} Indicates whether to propagate event.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.handleEnter_ = function(e) {
    +  if (e.keyCode == goog.events.KeyCodes.ENTER) {
    +    return this.handleOkClick_() ?
    +        this.dispatchEvent(goog.ui.Component.EventType.ACTION) : false;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Gets the character from the event target.
    + * @param {Element} e Event target containing the 'char' attribute.
    + * @return {string} The character specified in the event.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.getChar_ = function(e) {
    +  return e.getAttribute('char');
    +};
    +
    +
    +/**
    + * Creates a menu entry for either the category listing or subcategory listing.
    + * @param {number} id Id to be used for the entry.
    + * @param {string} caption Text displayed for the menu item.
    + * @return {!goog.ui.MenuItem} Menu item to be added to the menu listing.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.createMenuItem_ = function(id, caption) {
    +  var item = new goog.ui.MenuItem(caption, /* model */ id, this.getDomHelper());
    +  item.setVisible(true);
    +  return item;
    +};
    +
    +
    +/**
    + * Sets the category and updates the submenu items and grid accordingly.
    + * @param {number} category Category index used to index the data tables.
    + * @param {number=} opt_subcategory Subcategory index used with category index.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.setSelectedCategory_ = function(category,
    +                                                             opt_subcategory) {
    +  this.category = category;
    +  this.menubutton_.setCaption(this.data_.categories[category]);
    +  while (this.submenu_.hasChildren()) {
    +    this.submenu_.removeChildAt(0, true).dispose();
    +  }
    +
    +  var subcategories = this.data_.subcategories[category];
    +  var charList = this.data_.charList[category];
    +  for (var i = 0; i < subcategories.length; i++) {
    +    var subtitle = charList[i].length == 0;
    +    var item = this.createMenuItem_(i, subcategories[i]);
    +    this.submenu_.addChild(item, true);
    +  }
    +  this.setSelectedSubcategory_(opt_subcategory || 0);
    +};
    +
    +
    +/**
    + * Sets the subcategory and updates the grid accordingly.
    + * @param {number} subcategory Sub-category index used to index the data tables.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.setSelectedSubcategory_ = function(subcategory) {
    +  var subcategories = this.data_.subcategories;
    +  var name = subcategories[this.category][subcategory];
    +  this.submenubutton_.setCaption(name);
    +  this.setSelectedGrid_(this.category, subcategory);
    +};
    +
    +
    +/**
    + * Updates the grid according to a given category and subcategory.
    + * @param {number} category Index to the category table.
    + * @param {number} subcategory Index to the subcategory table.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.setSelectedGrid_ = function(category,
    +    subcategory) {
    +  var charLists = this.data_.charList;
    +  var charListStr = charLists[category][subcategory];
    +  var content = this.decompressor_.toCharList(charListStr);
    +  this.charNameFetcher_.prefetch(charListStr);
    +  this.updateGrid_(this.grid_, content);
    +};
    +
    +
    +/**
    + * Updates the grid with new character list.
    + * @param {goog.ui.Component} grid The grid which is updated with a new set of
    + *     characters.
    + * @param {Array<string>} items Characters to be added to the grid.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.updateGrid_ = function(grid, items) {
    +  if (grid == this.grid_) {
    +    /**
    +     * @desc The message used when there are invisible characters like space
    +     *     or format control characters.
    +     */
    +    var MSG_PLEASE_HOVER =
    +        goog.getMsg('Please hover over each cell for the character name.');
    +
    +    goog.dom.setTextContent(this.notice_.getElement(),
    +        this.charNameFetcher_.isNameAvailable(
    +            items[0]) ? MSG_PLEASE_HOVER : '');
    +    this.items = items;
    +    if (this.stickwrap_.offsetHeight > 0) {
    +      this.stick_.style.height =
    +          this.stickwrap_.offsetHeight * items.length / this.gridsize_ + 'px';
    +    } else {
    +      // This is the last ditch effort if height is not avaialble.
    +      // Maximum of 3em is assumed to the the cell height. Extra space after
    +      // last character in the grid is OK.
    +      this.stick_.style.height = 3 * this.columnCount_ * items.length /
    +          this.gridsize_ + 'em';
    +    }
    +    this.stickwrap_.scrollTop = 0;
    +  }
    +
    +  this.modifyGridWithItems_(grid, items, 0);
    +};
    +
    +
    +/**
    + * Updates the grid with new character list for a given starting point.
    + * @param {goog.ui.Component} grid The grid which is updated with a new set of
    + *     characters.
    + * @param {Array<string>} items Characters to be added to the grid.
    + * @param {number} start The index from which the characters should be
    + *     displayed.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.modifyGridWithItems_ = function(grid, items,
    +    start) {
    +  for (var buttonpos = 0, itempos = start;
    +       buttonpos < grid.buttoncount && itempos < items.length;
    +       buttonpos++, itempos++) {
    +    this.modifyCharNode_(
    +        /** @type {!goog.ui.Button} */ (grid.getChildAt(buttonpos)),
    +        items[itempos]);
    +  }
    +
    +  for (; buttonpos < grid.buttoncount; buttonpos++) {
    +    grid.getChildAt(buttonpos).setVisible(false);
    +  }
    +};
    +
    +
    +/**
    + * Creates the grid for characters to displayed for selection.
    + * @param {goog.ui.Component} grid The grid which is updated with a new set of
    + *     characters.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.populateGridWithButtons_ = function(grid) {
    +  for (var i = 0; i < grid.buttoncount; i++) {
    +    var button = new goog.ui.Button(' ',
    +                                    goog.ui.FlatButtonRenderer.getInstance(),
    +                                    this.getDomHelper());
    +
    +    // Dispatch the focus event so we can update the aria description while
    +    // the user tabs through the cells.
    +    button.setDispatchTransitionEvents(goog.ui.Component.State.FOCUSED, true);
    +
    +    grid.addChild(button, true);
    +    button.setVisible(false);
    +
    +    var buttonEl = button.getElement();
    +    goog.asserts.assert(buttonEl, 'The button DOM element cannot be null.');
    +
    +    // Override the button role so the user doesn't hear "button" each time he
    +    // tabs through the cells.
    +    goog.a11y.aria.removeRole(buttonEl);
    +  }
    +};
    +
    +
    +/**
    + * Updates the grid cell with new character.
    + * @param {goog.ui.Button} button This button is popped up for new character.
    + * @param {string} ch Character to be displayed by the button.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.modifyCharNode_ = function(button, ch) {
    +  var text = this.displayChar_(ch);
    +  var buttonEl = button.getElement();
    +  goog.dom.setTextContent(buttonEl, text);
    +  buttonEl.setAttribute('char', ch);
    +  button.setVisible(true);
    +};
    +
    +
    +/**
    + * Adds a given character to the recent character list.
    + * @param {string} character Character to be added to the recent list.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.updateRecents_ = function(character) {
    +  if (character && character.charCodeAt(0) &&
    +      !goog.array.contains(this.recents_, character)) {
    +    this.recents_.unshift(character);
    +    if (this.recents_.length > this.recentwidth_) {
    +      this.recents_.pop();
    +    }
    +    this.updateGrid_(this.recentgrid_, this.recents_);
    +  }
    +};
    +
    +
    +/**
    + * Gets the user inputed unicode character.
    + * @return {string} Unicode character inputed by user.
    + */
    +goog.ui.CharPicker.prototype.getInputChar = function() {
    +  var text = this.input_.getValue();
    +  var code = parseInt(text, 16);
    +  return /** @type {string} */ (goog.i18n.uChar.fromCharCode(code));
    +};
    +
    +
    +/**
    + * Gets the display character for the given character.
    + * @param {string} ch Character whose display is fetched.
    + * @return {string} The display of the given character.
    + * @private
    + */
    +goog.ui.CharPicker.prototype.displayChar_ = function(ch) {
    +  return this.layoutAlteringChars_.contains(ch) ? '\u00A0' : ch;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.html
    new file mode 100644
    index 0000000000..989b832e71
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.CharPicker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.CharPickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="charpicker">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.js
    new file mode 100644
    index 0000000000..fe0765e6ad
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/charpicker_test.js
    @@ -0,0 +1,87 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.CharPickerTest');
    +goog.setTestOnly('goog.ui.CharPickerTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.i18n.CharPickerData');
    +goog.require('goog.i18n.uChar.NameFetcher');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.ui.CharPicker');
    +goog.require('goog.ui.FlatButtonRenderer');
    +
    +var charPicker, charPickerData, charPickerElement;
    +var mockControl, charNameFetcherMock;
    +
    +function setUp() {
    +  mockControl = new goog.testing.MockControl();
    +  charNameFetcherMock = mockControl.createLooseMock(
    +      goog.i18n.uChar.NameFetcher, true /* opt_ignoreUnexpectedCalls */);
    +
    +  charPickerData = new goog.i18n.CharPickerData();
    +  charPickerElement = goog.dom.getElement('charpicker');
    +
    +  charPicker = new goog.ui.CharPicker(
    +      charPickerData, charNameFetcherMock);
    +}
    +
    +function tearDown() {
    +  goog.dispose(charPicker);
    +  goog.dom.removeChildren(charPickerElement);
    +  mockControl.$tearDown();
    +}
    +
    +function testAriaLabelIsUpdatedOnFocus() {
    +  var character = '←';
    +  var characterName = 'right arrow';
    +
    +  charNameFetcherMock.getName(
    +      character,
    +      goog.testing.mockmatchers.isFunction).
    +      $does(function(c, callback) {
    +        callback(characterName);
    +      });
    +
    +  mockControl.$replayAll();
    +
    +  charPicker.decorate(charPickerElement);
    +
    +  // Get the first button elements within the grid div and override its
    +  // char attribute so the test doesn't depend on the actual grid content.
    +  var gridElement = goog.dom.getElementByClass(
    +      goog.getCssName('goog-char-picker-grid'), charPickerElement);
    +  var buttonElement = goog.dom.getElementsByClass(
    +      goog.ui.FlatButtonRenderer.CSS_CLASS, gridElement)[0];
    +  buttonElement.setAttribute('char', character);
    +
    +  // Trigger a focus event on the button element.
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, buttonElement));
    +
    +  mockControl.$verifyAll();
    +
    +  var ariaLabel = goog.a11y.aria.getState(
    +      buttonElement, goog.a11y.aria.State.LABEL);
    +  assertEquals('The aria label should be updated when the button' +
    +      'gains focus.', characterName, ariaLabel);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/checkbox.js b/mapbox-gl/closure-library/closure/goog/ui/checkbox.js
    new file mode 100644
    index 0000000000..a76d1d78b3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/checkbox.js
    @@ -0,0 +1,272 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tristate checkbox widget.
    + *
    + * @see ../demos/checkbox.html
    + */
    +
    +goog.provide('goog.ui.Checkbox');
    +goog.provide('goog.ui.Checkbox.State');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.string');
    +goog.require('goog.ui.CheckboxRenderer');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * 3-state checkbox widget. Fires CHECK or UNCHECK events before toggled and
    + * CHANGE event after toggled by user.
    + * The checkbox can also be enabled/disabled and get focused and highlighted.
    + *
    + * @param {goog.ui.Checkbox.State=} opt_checked Checked state to set.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @param {goog.ui.CheckboxRenderer=} opt_renderer Renderer used to render or
    + *     decorate the checkbox; defaults to {@link goog.ui.CheckboxRenderer}.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Checkbox = function(opt_checked, opt_domHelper, opt_renderer) {
    +  var renderer = opt_renderer || goog.ui.CheckboxRenderer.getInstance();
    +  goog.ui.Control.call(this, null, renderer, opt_domHelper);
    +  // The checkbox maintains its own tri-state CHECKED state.
    +  // The control class maintains DISABLED, ACTIVE, and FOCUSED (which enable tab
    +  // navigation, and keyHandling with SPACE).
    +
    +  /**
    +   * Checked state of the checkbox.
    +   * @type {goog.ui.Checkbox.State}
    +   * @private
    +   */
    +  this.checked_ = goog.isDef(opt_checked) ?
    +      opt_checked : goog.ui.Checkbox.State.UNCHECKED;
    +};
    +goog.inherits(goog.ui.Checkbox, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.Checkbox);
    +
    +
    +/**
    + * Possible checkbox states.
    + * @enum {?boolean}
    + */
    +goog.ui.Checkbox.State = {
    +  CHECKED: true,
    +  UNCHECKED: false,
    +  UNDETERMINED: null
    +};
    +
    +
    +/**
    + * Label element bound to the checkbox.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Checkbox.prototype.label_ = null;
    +
    +
    +/**
    + * @return {goog.ui.Checkbox.State} Checked state of the checkbox.
    + */
    +goog.ui.Checkbox.prototype.getChecked = function() {
    +  return this.checked_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the checkbox is checked.
    + * @override
    + */
    +goog.ui.Checkbox.prototype.isChecked = function() {
    +  return this.checked_ == goog.ui.Checkbox.State.CHECKED;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the checkbox is not checked.
    + */
    +goog.ui.Checkbox.prototype.isUnchecked = function() {
    +  return this.checked_ == goog.ui.Checkbox.State.UNCHECKED;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the checkbox is in partially checked state.
    + */
    +goog.ui.Checkbox.prototype.isUndetermined = function() {
    +  return this.checked_ == goog.ui.Checkbox.State.UNDETERMINED;
    +};
    +
    +
    +/**
    + * Sets the checked state of the checkbox.
    + * @param {?boolean} checked The checked state to set.
    + * @override
    + */
    +goog.ui.Checkbox.prototype.setChecked = function(checked) {
    +  if (checked != this.checked_) {
    +    this.checked_ = /** @type {goog.ui.Checkbox.State} */ (checked);
    +    this.getRenderer().setCheckboxState(this.getElement(), this.checked_);
    +  }
    +};
    +
    +
    +/**
    + * Sets the checked state for the checkbox.  Unlike {@link #setChecked},
    + * doesn't update the checkbox's DOM.  Considered protected; to be called
    + * only by renderer code during element decoration.
    + * @param {goog.ui.Checkbox.State} checked New checkbox state.
    + */
    +goog.ui.Checkbox.prototype.setCheckedInternal = function(checked) {
    +  this.checked_ = checked;
    +};
    +
    +
    +/**
    + * Binds an HTML element to the checkbox which if clicked toggles the checkbox.
    + * Behaves the same way as the 'label' HTML tag. The label element has to be the
    + * direct or non-direct ancestor of the checkbox element because it will get the
    + * focus when keyboard support is implemented.
    + * Note: Control#enterDocument also sets aria-label on the element but
    + * Checkbox#enterDocument sets aria-labeledby on the same element which
    + * overrides the aria-label in all modern screen readers.
    + *
    + * @param {Element} label The label control to set. If null, only the checkbox
    + *     reacts to clicks.
    + */
    +goog.ui.Checkbox.prototype.setLabel = function(label) {
    +  if (this.isInDocument()) {
    +    this.exitDocument();
    +    this.label_ = label;
    +    this.enterDocument();
    +  } else {
    +    this.label_ = label;
    +  }
    +};
    +
    +
    +/**
    + * Toggles the checkbox. State transitions:
    + * <ul>
    + *   <li>unchecked -> checked
    + *   <li>undetermined -> checked
    + *   <li>checked -> unchecked
    + * </ul>
    + */
    +goog.ui.Checkbox.prototype.toggle = function() {
    +  this.setChecked(this.checked_ ? goog.ui.Checkbox.State.UNCHECKED :
    +      goog.ui.Checkbox.State.CHECKED);
    +};
    +
    +
    +/** @override */
    +goog.ui.Checkbox.prototype.enterDocument = function() {
    +  goog.ui.Checkbox.base(this, 'enterDocument');
    +  if (this.isHandleMouseEvents()) {
    +    var handler = this.getHandler();
    +    // Listen to the label, if it was set.
    +    if (this.label_) {
    +      // Any mouse events that happen to the associated label should have the
    +      // same effect on the checkbox as if they were happening to the checkbox
    +      // itself.
    +      handler.
    +          listen(this.label_, goog.events.EventType.CLICK,
    +              this.handleClickOrSpace_).
    +          listen(this.label_, goog.events.EventType.MOUSEOVER,
    +              this.handleMouseOver).
    +          listen(this.label_, goog.events.EventType.MOUSEOUT,
    +              this.handleMouseOut).
    +          listen(this.label_, goog.events.EventType.MOUSEDOWN,
    +              this.handleMouseDown).
    +          listen(this.label_, goog.events.EventType.MOUSEUP,
    +              this.handleMouseUp);
    +    }
    +    // Checkbox needs to explicitly listen for click event.
    +    handler.listen(this.getElement(),
    +        goog.events.EventType.CLICK, this.handleClickOrSpace_);
    +  }
    +
    +  // Set aria label.
    +  var checkboxElement = this.getElementStrict();
    +  if (this.label_ && checkboxElement != this.label_ &&
    +      goog.string.isEmptyOrWhitespace(goog.a11y.aria.getLabel(checkboxElement))) {
    +    if (!this.label_.id) {
    +      this.label_.id = this.makeId('lbl');
    +    }
    +    goog.a11y.aria.setState(checkboxElement,
    +        goog.a11y.aria.State.LABELLEDBY,
    +        this.label_.id);
    +  }
    +};
    +
    +
    +/**
    + * Fix for tabindex not being updated so that disabled checkbox is not
    + * focusable. In particular this fails in Chrome.
    + * Note: in general tabIndex=-1 will prevent from keyboard focus but enables
    + * mouse focus, however in this case the control class prevents mouse focus.
    + * @override
    + */
    +goog.ui.Checkbox.prototype.setEnabled = function(enabled) {
    +  goog.ui.Checkbox.base(this, 'setEnabled', enabled);
    +  var el = this.getElement();
    +  if (el) {
    +    el.tabIndex = this.isEnabled() ? 0 : -1;
    +  }
    +};
    +
    +
    +/**
    + * Handles the click event.
    + * @param {!goog.events.BrowserEvent} e The event.
    + * @private
    + */
    +goog.ui.Checkbox.prototype.handleClickOrSpace_ = function(e) {
    +  e.stopPropagation();
    +  var eventType = this.checked_ ? goog.ui.Component.EventType.UNCHECK :
    +      goog.ui.Component.EventType.CHECK;
    +  if (this.isEnabled() && !e.target.href && this.dispatchEvent(eventType)) {
    +    e.preventDefault();  // Prevent scrolling in Chrome if SPACE is pressed.
    +    this.toggle();
    +    this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Checkbox.prototype.handleKeyEventInternal = function(e) {
    +  if (e.keyCode == goog.events.KeyCodes.SPACE) {
    +    this.performActionInternal(e);
    +    this.handleClickOrSpace_(e);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Register this control so it can be created from markup.
    + */
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.CheckboxRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Checkbox();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.html b/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.html
    new file mode 100644
    index 0000000000..6cbbd2d61b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.html
    @@ -0,0 +1,38 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Checkbox
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.CheckboxTest');
    +  </script>
    + </head>
    + <body>
    +  <div>
    +   <span id="decorate" class="goog-checkbox">
    +   </span>
    +   <span id="normal" class="goog-checkbox">
    +   </span>
    +   <span id="checked" class="goog-checkbox goog-checkbox-checked">
    +   </span>
    +   <span id="unchecked" class="goog-checkbox goog-checkbox-unchecked">
    +   </span>
    +   <span id="undetermined" class="goog-checkbox goog-checkbox-undetermined">
    +   </span>
    +   <span id="disabled" class="goog-checkbox goog-checkbox-disabled">
    +   </span>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.js b/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.js
    new file mode 100644
    index 0000000000..54558d1cd3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/checkbox_test.js
    @@ -0,0 +1,451 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.CheckboxTest');
    +goog.setTestOnly('goog.ui.CheckboxTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Checkbox');
    +goog.require('goog.ui.CheckboxRenderer');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.ui.decorate');
    +
    +var checkbox;
    +
    +function setUp() {
    +  checkbox = new goog.ui.Checkbox();
    +}
    +
    +function tearDown() {
    +  checkbox.dispose();
    +}
    +
    +function testClassNames() {
    +  checkbox.createDom();
    +
    +  checkbox.setChecked(false);
    +  assertSameElements('classnames of unchecked checkbox',
    +      ['goog-checkbox', 'goog-checkbox-unchecked'],
    +      goog.dom.classlist.get(checkbox.getElement()));
    +
    +  checkbox.setChecked(true);
    +  assertSameElements('classnames of checked checkbox',
    +      ['goog-checkbox', 'goog-checkbox-checked'],
    +      goog.dom.classlist.get(checkbox.getElement()));
    +
    +  checkbox.setChecked(null);
    +  assertSameElements('classnames of partially checked checkbox',
    +      ['goog-checkbox', 'goog-checkbox-undetermined'],
    +      goog.dom.classlist.get(checkbox.getElement()));
    +
    +  checkbox.setEnabled(false);
    +  assertSameElements('classnames of partially checked disabled checkbox',
    +      ['goog-checkbox',
    +       'goog-checkbox-undetermined',
    +       'goog-checkbox-disabled'],
    +      goog.dom.classlist.get(checkbox.getElement()));
    +}
    +
    +function testIsEnabled() {
    +  assertTrue('enabled by default', checkbox.isEnabled());
    +  checkbox.setEnabled(false);
    +  assertFalse('has been disabled', checkbox.isEnabled());
    +}
    +
    +function testCheckedState() {
    +  assertTrue('unchecked by default', !checkbox.isChecked() &&
    +      checkbox.isUnchecked() && !checkbox.isUndetermined());
    +
    +  checkbox.setChecked(true);
    +  assertTrue('set to checked', checkbox.isChecked() &&
    +      !checkbox.isUnchecked() && !checkbox.isUndetermined());
    +
    +  checkbox.setChecked(null);
    +  assertTrue('set to partially checked', !checkbox.isChecked() &&
    +      !checkbox.isUnchecked() && checkbox.isUndetermined());
    +}
    +
    +function testToggle() {
    +  checkbox.setChecked(null);
    +  checkbox.toggle();
    +  assertTrue('undetermined -> checked', checkbox.getChecked());
    +  checkbox.toggle();
    +  assertFalse('checked -> unchecked', checkbox.getChecked());
    +  checkbox.toggle();
    +  assertTrue('unchecked -> checked', checkbox.getChecked());
    +}
    +
    +function testEvents() {
    +  checkbox.render();
    +
    +  var events = [];
    +  goog.events.listen(checkbox,
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK,
    +        goog.ui.Component.EventType.UNCHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      function(e) {
    +        events.push(e.type);
    +      });
    +
    +  checkbox.setEnabled(false);
    +  goog.testing.events.fireClickSequence(checkbox.getElement());
    +  assertArrayEquals('disabled => no events', [], events);
    +  assertFalse('checked state did not change', checkbox.getChecked());
    +  events = [];
    +
    +  checkbox.setEnabled(true);
    +  goog.testing.events.fireClickSequence(checkbox.getElement());
    +  assertArrayEquals('ACTION+CHECK+CHANGE fired',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      events);
    +  assertTrue('checkbox became checked', checkbox.getChecked());
    +  events = [];
    +
    +  goog.testing.events.fireClickSequence(checkbox.getElement());
    +  assertArrayEquals('ACTION+UNCHECK+CHANGE fired',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.UNCHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      events);
    +  assertFalse('checkbox became unchecked', checkbox.getChecked());
    +  events = [];
    +
    +  goog.events.listen(checkbox, goog.ui.Component.EventType.CHECK,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  goog.testing.events.fireClickSequence(checkbox.getElement());
    +  assertArrayEquals('ACTION+CHECK fired',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK
    +      ],
    +      events);
    +  assertFalse('toggling has been prevented', checkbox.getChecked());
    +}
    +
    +function testCheckboxAriaLabelledby() {
    +  var label = goog.dom.createElement('div');
    +  var label2 = goog.dom.createElement('div', {id: checkbox.makeId('foo')});
    +  document.body.appendChild(label);
    +  document.body.appendChild(label2);
    +  try {
    +    checkbox.setChecked(false);
    +    checkbox.setLabel(label);
    +    checkbox.render(label);
    +    assertNotNull(checkbox.getElement());
    +    assertEquals(label.id,
    +        goog.a11y.aria.getState(checkbox.getElement(),
    +            goog.a11y.aria.State.LABELLEDBY));
    +
    +    checkbox.setLabel(label2);
    +    assertEquals(label2.id,
    +        goog.a11y.aria.getState(checkbox.getElement(),
    +            goog.a11y.aria.State.LABELLEDBY));
    +  } finally {
    +    document.body.removeChild(label);
    +    document.body.removeChild(label2);
    +  }
    +}
    +
    +function testLabel() {
    +  var label = goog.dom.createElement('div');
    +  document.body.appendChild(label);
    +  try {
    +    checkbox.setChecked(false);
    +    checkbox.setLabel(label);
    +    checkbox.render(label);
    +
    +    // Clicking on label toggles checkbox.
    +    goog.testing.events.fireClickSequence(label);
    +    assertTrue('checkbox toggled if the label is clicked',
    +        checkbox.getChecked());
    +    goog.testing.events.fireClickSequence(checkbox.getElement());
    +    assertFalse('checkbox toggled if it is clicked', checkbox.getChecked());
    +
    +    // Test that mouse events on the label have the correct effect on the
    +    // checkbox state when it is enabled.
    +    checkbox.setEnabled(true);
    +    goog.testing.events.fireMouseOverEvent(label);
    +    assertTrue(checkbox.hasState(goog.ui.Component.State.HOVER));
    +    assertContains('checkbox gets hover state on mouse over',
    +        'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
    +    goog.testing.events.fireMouseDownEvent(label);
    +    assertTrue(checkbox.hasState(goog.ui.Component.State.ACTIVE));
    +    assertContains('checkbox gets active state on label mousedown',
    +        'goog-checkbox-active',
    +        goog.dom.classlist.get(checkbox.getElement()));
    +    goog.testing.events.fireMouseOutEvent(checkbox.getElement());
    +    assertFalse(checkbox.hasState(goog.ui.Component.State.HOVER));
    +    assertNotContains('checkbox does not have hover state after mouse out',
    +        'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
    +    assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
    +    assertNotContains('checkbox does not have active state after mouse out',
    +        'goog-checkbox-active', goog.dom.classlist.get(checkbox.getElement()));
    +
    +    // Test label mouse events on disabled checkbox.
    +    checkbox.setEnabled(false);
    +    goog.testing.events.fireMouseOverEvent(label);
    +    assertFalse(checkbox.hasState(goog.ui.Component.State.HOVER));
    +    assertNotContains(
    +        'disabled checkbox does not get hover state on mouseover',
    +        'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
    +    goog.testing.events.fireMouseDownEvent(label);
    +    assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
    +    assertNotContains('disabled checkbox does not get active state mousedown',
    +        'goog-checkbox-active',
    +        goog.dom.classlist.get(checkbox.getElement()));
    +    goog.testing.events.fireMouseOutEvent(checkbox.getElement());
    +    assertFalse(checkbox.hasState(goog.ui.Component.State.ACTIVE));
    +    assertNotContains('checkbox does not get stuck in hover state',
    +        'goog-checkbox-hover', goog.dom.classlist.get(checkbox.getElement()));
    +
    +    // Making the label null prevents it from affecting checkbox state.
    +    checkbox.setEnabled(true);
    +    checkbox.setLabel(null);
    +    goog.testing.events.fireClickSequence(label);
    +    assertFalse('label element deactivated', checkbox.getChecked());
    +    goog.testing.events.fireClickSequence(checkbox.getElement());
    +    assertTrue('checkbox still active', checkbox.getChecked());
    +  } finally {
    +    document.body.removeChild(label);
    +  }
    +}
    +
    +function testConstructor() {
    +  assertEquals('state is unchecked', goog.ui.Checkbox.State.UNCHECKED,
    +      checkbox.getChecked());
    +
    +  var testCheckboxWithState = new goog.ui.Checkbox(
    +      goog.ui.Checkbox.State.UNDETERMINED);
    +  assertNotNull('checkbox created with custom state', testCheckboxWithState);
    +  assertEquals('checkbox state is undetermined',
    +      goog.ui.Checkbox.State.UNDETERMINED,
    +      testCheckboxWithState.getChecked());
    +  testCheckboxWithState.dispose();
    +}
    +
    +function testCustomRenderer() {
    +  var cssClass = 'my-custom-checkbox';
    +  var renderer = goog.ui.ControlRenderer.getCustomRenderer(
    +      goog.ui.CheckboxRenderer, cssClass);
    +  var customCheckbox = new goog.ui.Checkbox(
    +      undefined, undefined, renderer);
    +  customCheckbox.createDom();
    +  assertElementsEquals(
    +      ['my-custom-checkbox', 'my-custom-checkbox-unchecked'],
    +      goog.dom.classlist.get(customCheckbox.getElement()));
    +  customCheckbox.setChecked(true);
    +  assertElementsEquals(
    +      ['my-custom-checkbox', 'my-custom-checkbox-checked'],
    +      goog.dom.classlist.get(customCheckbox.getElement()));
    +  customCheckbox.setChecked(null);
    +  assertElementsEquals(
    +      ['my-custom-checkbox', 'my-custom-checkbox-undetermined'],
    +      goog.dom.classlist.get(customCheckbox.getElement()));
    +  customCheckbox.dispose();
    +}
    +
    +function testGetAriaRole() {
    +  checkbox.createDom();
    +  assertNotNull(checkbox.getElement());
    +  assertEquals("Checkbox's ARIA role should be 'checkbox'",
    +      goog.a11y.aria.Role.CHECKBOX,
    +      goog.a11y.aria.getRole(checkbox.getElement()));
    +}
    +
    +function testCreateDomUpdateAriaState() {
    +  checkbox.createDom();
    +  assertNotNull(checkbox.getElement());
    +  assertEquals('Checkbox must have default false ARIA state aria-checked',
    +      'false', goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.CHECKED);
    +  assertEquals('Checkbox must have true ARIA state aria-checked', 'true',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.UNCHECKED);
    +  assertEquals('Checkbox must have false ARIA state aria-checked', 'false',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.UNDETERMINED);
    +  assertEquals('Checkbox must have mixed ARIA state aria-checked', 'mixed',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testDecorateUpdateAriaState() {
    +  var decorateSpan = goog.dom.getElement('decorate');
    +  checkbox.decorate(decorateSpan);
    +
    +  assertEquals('Checkbox must have default false ARIA state aria-checked',
    +      'false', goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.CHECKED);
    +  assertEquals('Checkbox must have true ARIA state aria-checked', 'true',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.UNCHECKED);
    +  assertEquals('Checkbox must have false ARIA state aria-checked', 'false',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +
    +  checkbox.setChecked(goog.ui.Checkbox.State.UNDETERMINED);
    +  assertEquals('Checkbox must have mixed ARIA state aria-checked', 'mixed',
    +      goog.a11y.aria.getState(checkbox.getElement(),
    +          goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSpaceKey() {
    +  var normalSpan = goog.dom.getElement('normal');
    +
    +  checkbox.decorate(normalSpan);
    +  assertEquals('default state is unchecked',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
    +  assertEquals('SPACE toggles checkbox to be checked',
    +      goog.ui.Checkbox.State.CHECKED, checkbox.getChecked());
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
    +  assertEquals('another SPACE toggles checkbox to be unchecked',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +
    +  // Enter for example doesn't work
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.ENTER);
    +  assertEquals('Enter leaves checkbox unchecked',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +}
    +
    +function testSpaceKeyFiresEvents() {
    +  var normalSpan = goog.dom.getElement('normal');
    +
    +  checkbox.decorate(normalSpan);
    +  var events = [];
    +  goog.events.listen(checkbox,
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK,
    +        goog.ui.Component.EventType.UNCHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      function(e) {
    +        events.push(e.type);
    +      });
    +
    +  assertEquals('Unexpected default state.',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
    +  assertArrayEquals('Unexpected events fired when checking with spacebar.',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      events);
    +  assertEquals('Unexpected state after checking.',
    +      goog.ui.Checkbox.State.CHECKED, checkbox.getChecked());
    +
    +  events = [];
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
    +  assertArrayEquals('Unexpected events fired when unchecking with spacebar.',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.UNCHECK,
    +        goog.ui.Component.EventType.CHANGE
    +      ],
    +      events);
    +  assertEquals('Unexpected state after unchecking.',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +
    +  events = [];
    +  goog.events.listenOnce(checkbox, goog.ui.Component.EventType.CHECK,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  goog.testing.events.fireKeySequence(normalSpan, goog.events.KeyCodes.SPACE);
    +  assertArrayEquals('Unexpected events fired when checking with spacebar and ' +
    +      'the check event is cancelled.',
    +      [
    +        goog.ui.Component.EventType.ACTION,
    +        goog.ui.Component.EventType.CHECK
    +      ],
    +      events);
    +  assertEquals('Unexpected state after check event is cancelled.',
    +      goog.ui.Checkbox.State.UNCHECKED, checkbox.getChecked());
    +}
    +
    +function testDecorate() {
    +  var normalSpan = goog.dom.getElement('normal');
    +  var checkedSpan = goog.dom.getElement('checked');
    +  var uncheckedSpan = goog.dom.getElement('unchecked');
    +  var undeterminedSpan = goog.dom.getElement('undetermined');
    +  var disabledSpan = goog.dom.getElement('disabled');
    +
    +  validateCheckBox(normalSpan, goog.ui.Checkbox.State.UNCHECKED);
    +  validateCheckBox(checkedSpan, goog.ui.Checkbox.State.CHECKED);
    +  validateCheckBox(uncheckedSpan, goog.ui.Checkbox.State.UNCHECKED);
    +  validateCheckBox(undeterminedSpan, goog.ui.Checkbox.State.UNDETERMINED);
    +  validateCheckBox(disabledSpan, goog.ui.Checkbox.State.UNCHECKED, true);
    +}
    +
    +function validateCheckBox(span, state, opt_disabled) {
    +  var testCheckbox = goog.ui.decorate(span);
    +  assertNotNull('checkbox created', testCheckbox);
    +  assertEquals('decorate was successful',
    +      goog.ui.Checkbox, testCheckbox.constructor);
    +  assertEquals('checkbox state should be: ' + state, state,
    +      testCheckbox.getChecked());
    +  assertEquals('checkbox is ' + (!opt_disabled ? 'enabled' : 'disabled'),
    +      !opt_disabled, testCheckbox.isEnabled());
    +  testCheckbox.dispose();
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Checkbox must not have aria label by default',
    +      checkbox.getAriaLabel());
    +  checkbox.setAriaLabel('Checkbox 1');
    +  checkbox.render();
    +  var el = checkbox.getElementStrict();
    +  assertEquals('Checkbox element must have expected aria-label', 'Checkbox 1',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Checkbox element must have expected aria-role', 'checkbox',
    +      el.getAttribute('role'));
    +  checkbox.setAriaLabel('Checkbox 2');
    +  assertEquals('Checkbox element must have updated aria-label', 'Checkbox 2',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Checkbox element must have expected aria-role', 'checkbox',
    +      el.getAttribute('role'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/checkboxmenuitem.js b/mapbox-gl/closure-library/closure/goog/ui/checkboxmenuitem.js
    new file mode 100644
    index 0000000000..29127016be
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/checkboxmenuitem.js
    @@ -0,0 +1,53 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A menu item class that supports checkbox semantics.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.CheckBoxMenuItem');
    +
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a checkbox menu item.  This is just a convenience class
    + * that extends {@link goog.ui.MenuItem} by making it checkable.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {*=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + */
    +goog.ui.CheckBoxMenuItem = function(content, opt_model, opt_domHelper) {
    +  goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper);
    +  this.setCheckable(true);
    +};
    +goog.inherits(goog.ui.CheckBoxMenuItem, goog.ui.MenuItem);
    +
    +
    +// Register a decorator factory function for goog.ui.CheckBoxMenuItems.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-checkbox-menuitem'), function() {
    +      // CheckBoxMenuItem defaults to using MenuItemRenderer.
    +      return new goog.ui.CheckBoxMenuItem(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/checkboxrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/checkboxrenderer.js
    new file mode 100644
    index 0000000000..5a423b434c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/checkboxrenderer.js
    @@ -0,0 +1,190 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default renderer for {@link goog.ui.Checkbox}s.
    + *
    + */
    +
    +goog.provide('goog.ui.CheckboxRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.object');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Checkbox}s.  Extends the superclass
    + * to support checkbox states:
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.CheckboxRenderer = function() {
    +  goog.ui.CheckboxRenderer.base(this, 'constructor');
    +};
    +goog.inherits(goog.ui.CheckboxRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.CheckboxRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.CheckboxRenderer.CSS_CLASS = goog.getCssName('goog-checkbox');
    +
    +
    +/** @override */
    +goog.ui.CheckboxRenderer.prototype.createDom = function(checkbox) {
    +  var element = checkbox.getDomHelper().createDom(
    +      'span', this.getClassNames(checkbox).join(' '));
    +
    +  var state = checkbox.getChecked();
    +  this.setCheckboxState(element, state);
    +
    +  return element;
    +};
    +
    +
    +/** @override */
    +goog.ui.CheckboxRenderer.prototype.decorate = function(checkbox, element) {
    +  // The superclass implementation takes care of common attributes; we only
    +  // need to set the checkbox state.
    +  element = goog.ui.CheckboxRenderer.base(this, 'decorate', checkbox, element);
    +  goog.asserts.assert(element);
    +  var classes = goog.dom.classlist.get(element);
    +  // Update the checked state of the element based on its css classNames
    +  // with the following order: undetermined -> checked -> unchecked.
    +  var checked = /** @suppress {missingRequire} */ (
    +      goog.ui.Checkbox.State.UNCHECKED);
    +  if (goog.array.contains(classes,
    +      this.getClassForCheckboxState(
    +          /** @suppress {missingRequire} */
    +          goog.ui.Checkbox.State.UNDETERMINED))) {
    +    checked = (/** @suppress {missingRequire} */
    +        goog.ui.Checkbox.State.UNDETERMINED);
    +  } else if (goog.array.contains(classes,
    +      this.getClassForCheckboxState(
    +          /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED))) {
    +    checked = /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED;
    +  } else if (goog.array.contains(classes,
    +      this.getClassForCheckboxState(/** @suppress {missingRequire} */
    +          goog.ui.Checkbox.State.UNCHECKED))) {
    +    checked = (/** @suppress {missingRequire} */
    +        goog.ui.Checkbox.State.UNCHECKED);
    +  }
    +  checkbox.setCheckedInternal(checked);
    +  goog.asserts.assert(element, 'The element cannot be null.');
    +  goog.a11y.aria.setState(element, goog.a11y.aria.State.CHECKED,
    +      this.ariaStateFromCheckState_(checked));
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the ARIA role to be applied to checkboxes.
    + * @return {goog.a11y.aria.Role} ARIA role.
    + * @override
    + */
    +goog.ui.CheckboxRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.CHECKBOX;
    +};
    +
    +
    +/**
    + * Updates the appearance of the control in response to a checkbox state
    + * change.
    + * @param {Element} element Checkbox element.
    + * @param {goog.ui.Checkbox.State} state Updated checkbox state.
    + */
    +goog.ui.CheckboxRenderer.prototype.setCheckboxState = function(
    +    element, state) {
    +  if (element) {
    +    goog.asserts.assert(element);
    +    var classToAdd = this.getClassForCheckboxState(state);
    +    goog.asserts.assert(classToAdd);
    +    goog.asserts.assert(element);
    +    if (goog.dom.classlist.contains(element, classToAdd)) {
    +      return;
    +    }
    +    goog.object.forEach(
    +        /** @suppress {missingRequire} */ goog.ui.Checkbox.State,
    +        function(state) {
    +          var className = this.getClassForCheckboxState(state);
    +          goog.asserts.assert(element);
    +          goog.dom.classlist.enable(element, className,
    +                                    className == classToAdd);
    +        }, this);
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.CHECKED,
    +        this.ariaStateFromCheckState_(state));
    +  }
    +};
    +
    +
    +/**
    + * Gets the checkbox's ARIA (accessibility) state from its checked state.
    + * @param {goog.ui.Checkbox.State} state Checkbox state.
    + * @return {string} The value of goog.a11y.aria.state.CHECKED. Either 'true',
    + *     'false', or 'mixed'.
    + * @private
    + */
    +goog.ui.CheckboxRenderer.prototype.ariaStateFromCheckState_ = function(state) {
    +  if (state ==
    +      /** @suppress {missingRequire} */ goog.ui.Checkbox.State.UNDETERMINED) {
    +    return 'mixed';
    +  } else if (state ==
    +             /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED) {
    +    return 'true';
    +  } else {
    +    return 'false';
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.CheckboxRenderer.prototype.getCssClass = function() {
    +  return goog.ui.CheckboxRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Takes a single {@link goog.ui.Checkbox.State}, and returns the
    + * corresponding CSS class name.
    + * @param {goog.ui.Checkbox.State} state Checkbox state.
    + * @return {string} CSS class representing the given state.
    + * @protected
    + */
    +goog.ui.CheckboxRenderer.prototype.getClassForCheckboxState = function(state) {
    +  var baseClass = this.getStructuralCssClass();
    +  if (state ==
    +      /** @suppress {missingRequire} */ goog.ui.Checkbox.State.CHECKED) {
    +    return goog.getCssName(baseClass, 'checked');
    +  } else if (state ==
    +             /** @suppress {missingRequire} */
    +             goog.ui.Checkbox.State.UNCHECKED) {
    +    return goog.getCssName(baseClass, 'unchecked');
    +  } else if (state ==
    +             /** @suppress {missingRequire} */
    +             goog.ui.Checkbox.State.UNDETERMINED) {
    +    return goog.getCssName(baseClass, 'undetermined');
    +  }
    +  throw Error('Invalid checkbox state: ' + state);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorbutton.js b/mapbox-gl/closure-library/closure/goog/ui/colorbutton.js
    new file mode 100644
    index 0000000000..fc598789c0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorbutton.js
    @@ -0,0 +1,59 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A color button rendered via
    + * {@link goog.ui.ColorButtonRenderer}.
    + *
    + */
    +
    +goog.provide('goog.ui.ColorButton');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ColorButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A color button control.  Identical to {@link goog.ui.Button}, except it
    + * defaults its renderer to {@link goog.ui.ColorButtonRenderer}.
    + * This button displays a particular color and is clickable.
    + * It is primarily useful with {@link goog.ui.ColorSplitBehavior} to cache the
    + * last selected color.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *    structure to display as the button's caption.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
    + *    render or decorate the button; defaults to
    + *    {@link goog.ui.ColorButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *    document interaction.
    + * @constructor
    + * @extends {goog.ui.Button}
    + * @final
    + */
    +goog.ui.ColorButton = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Button.call(this, content, opt_renderer ||
    +      goog.ui.ColorButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ColorButton, goog.ui.Button);
    +
    +// Register a decorator factory function for goog.ui.ColorButtons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.ColorButtonRenderer.CSS_CLASS,
    +    function() {
    +      // ColorButton defaults to using ColorButtonRenderer.
    +      return new goog.ui.ColorButton(null);
    +    });
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.html b/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.html
    new file mode 100644
    index 0000000000..3828ae3d53
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ColorButton
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.ColorButtonTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="buttonDiv">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.js b/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.js
    new file mode 100644
    index 0000000000..b3f21b6da3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorbutton_test.js
    @@ -0,0 +1,64 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ColorButtonTest');
    +goog.setTestOnly('goog.ui.ColorButtonTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ColorButton');
    +goog.require('goog.ui.decorate');
    +
    +var button, button2;
    +var buttonDiv;
    +
    +function setUp() {
    +  buttonDiv = goog.dom.getElement('buttonDiv');
    +}
    +
    +function tearDown() {
    +  goog.dom.removeChildren(buttonDiv);
    +  goog.dispose(button);
    +  goog.dispose(button2);
    +  button = null;
    +  button2 = null;
    +}
    +
    +function testRender() {
    +  button = new goog.ui.ColorButton('test');
    +  assertNotNull('button should be valid', button);
    +  button.render(buttonDiv);
    +  assertColorButton(button.getElement());
    +}
    +
    +function testDecorate() {
    +  var colorDiv = goog.dom.createDom('div', 'goog-color-button');
    +  goog.dom.appendChild(buttonDiv, colorDiv);
    +  button2 = goog.ui.decorate(colorDiv);
    +  assertNotNull('button should be valid', button2);
    +  assertColorButton(button2.getElement());
    +}
    +
    +function assertColorButton(div) {
    +  assertTrue('button className contains goog-color-button',
    +      goog.array.contains(goog.dom.classlist.get(div), 'goog-color-button'));
    +  var caption = goog.asserts.assertElement(
    +      div.firstChild.firstChild.firstChild);
    +  assertTrue('caption className contains goog-color-button',
    +      goog.array.contains(goog.dom.classlist.get(caption),
    +          'goog-color-button'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/colorbuttonrenderer.js
    new file mode 100644
    index 0000000000..97e6ffffc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorbuttonrenderer.js
    @@ -0,0 +1,75 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.ColorButton}s.
    + *
    + */
    +
    +goog.provide('goog.ui.ColorButtonRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.functions');
    +goog.require('goog.ui.ColorMenuButtonRenderer');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.ColorButton}s.
    + * Uses {@link goog.ui.ColorMenuButton}s but disables the dropdown.
    + *
    + * @constructor
    + * @extends {goog.ui.ColorMenuButtonRenderer}
    + * @final
    + */
    +goog.ui.ColorButtonRenderer = function() {
    +  goog.ui.ColorButtonRenderer.base(this, 'constructor');
    +
    +  /**
    +   * @override
    +   */
    +  // TODO(user): enable disabling the dropdown in goog.ui.ColorMenuButton
    +  this.createDropdown = goog.functions.NULL;
    +
    +};
    +goog.inherits(goog.ui.ColorButtonRenderer, goog.ui.ColorMenuButtonRenderer);
    +goog.addSingletonGetter(goog.ui.ColorButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer. Additionally, applies class to the button's caption.
    + * @type {string}
    + */
    +goog.ui.ColorButtonRenderer.CSS_CLASS = goog.getCssName('goog-color-button');
    +
    +
    +/** @override */
    +goog.ui.ColorButtonRenderer.prototype.createCaption = function(content, dom) {
    +  var caption = goog.ui.ColorButtonRenderer.base(
    +      this, 'createCaption', content, dom);
    +  goog.asserts.assert(caption);
    +  goog.dom.classlist.add(caption, goog.ui.ColorButtonRenderer.CSS_CLASS);
    +  return caption;
    +};
    +
    +
    +/** @override */
    +goog.ui.ColorButtonRenderer.prototype.initializeDom = function(button) {
    +  goog.ui.ColorButtonRenderer.base(this, 'initializeDom', button);
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(button.getElement()),
    +      goog.ui.ColorButtonRenderer.CSS_CLASS);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colormenubutton.js b/mapbox-gl/closure-library/closure/goog/ui/colormenubutton.js
    new file mode 100644
    index 0000000000..8bf833b137
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colormenubutton.js
    @@ -0,0 +1,215 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A color menu button.  Extends {@link goog.ui.MenuButton} by
    + * showing the currently selected color in the button caption.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ColorMenuButton');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('goog.ui.ColorMenuButtonRenderer');
    +goog.require('goog.ui.ColorPalette');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A color menu button control.  Extends {@link goog.ui.MenuButton} by adding
    + * an API for getting and setting the currently selected color from a menu of
    + * color palettes.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked;
    + *     should contain at least one {@link goog.ui.ColorPalette} if present.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to {@link goog.ui.ColorMenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.MenuButton}
    + */
    +goog.ui.ColorMenuButton = function(content, opt_menu, opt_renderer,
    +    opt_domHelper) {
    +  goog.ui.MenuButton.call(this, content, opt_menu, opt_renderer ||
    +      goog.ui.ColorMenuButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ColorMenuButton, goog.ui.MenuButton);
    +
    +
    +/**
    + * Default color palettes.
    + * @type {!Object}
    + */
    +goog.ui.ColorMenuButton.PALETTES = {
    +  /** Default grayscale colors. */
    +  GRAYSCALE: [
    +    '#000', '#444', '#666', '#999', '#ccc', '#eee', '#f3f3f3', '#fff'
    +  ],
    +
    +  /** Default solid colors. */
    +  SOLID: [
    +    '#f00', '#f90', '#ff0', '#0f0', '#0ff', '#00f', '#90f', '#f0f'
    +  ],
    +
    +  /** Default pastel colors. */
    +  PASTEL: [
    +    '#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9',
    +    '#ead1dc',
    +    '#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', '#b4a7d6',
    +    '#d5a6bd',
    +    '#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', '#6fa8dc', '#8e7cc3',
    +    '#c27ba0',
    +    '#cc0000', '#e69138', '#f1c232', '#6aa84f', '#45818e', '#3d85c6', '#674ea7',
    +    '#a64d79',
    +    '#990000', '#b45f06', '#bf9000', '#38761d', '#134f5c', '#0b5394', '#351c75',
    +    '#741b47',
    +    '#660000', '#783f04', '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d',
    +    '#4c1130'
    +  ]
    +};
    +
    +
    +/**
    + * Value for the "no color" menu item object in the color menu (if present).
    + * The {@link goog.ui.ColorMenuButton#handleMenuAction} method interprets
    + * ACTION events dispatched by an item with this value as meaning "clear the
    + * selected color."
    + * @type {string}
    + */
    +goog.ui.ColorMenuButton.NO_COLOR = 'none';
    +
    +
    +/**
    + * Factory method that creates and returns a new {@link goog.ui.Menu} instance
    + * containing default color palettes.
    + * @param {Array<goog.ui.Control>=} opt_extraItems Optional extra menu items to
    + *     add before the color palettes.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.Menu} Color menu.
    + */
    +goog.ui.ColorMenuButton.newColorMenu = function(opt_extraItems, opt_domHelper) {
    +  var menu = new goog.ui.Menu(opt_domHelper);
    +
    +  if (opt_extraItems) {
    +    goog.array.forEach(opt_extraItems, function(item) {
    +      menu.addChild(item, true);
    +    });
    +  }
    +
    +  goog.object.forEach(goog.ui.ColorMenuButton.PALETTES, function(colors) {
    +    var palette = new goog.ui.ColorPalette(colors, null, opt_domHelper);
    +    palette.setSize(8);
    +    menu.addChild(palette, true);
    +  });
    +
    +  return menu;
    +};
    +
    +
    +/**
    + * Returns the currently selected color (null if none).
    + * @return {string} The selected color.
    + */
    +goog.ui.ColorMenuButton.prototype.getSelectedColor = function() {
    +  return /** @type {string} */ (this.getValue());
    +};
    +
    +
    +/**
    + * Sets the selected color, or clears the selected color if the argument is
    + * null or not any of the available color choices.
    + * @param {?string} color New color.
    + */
    +goog.ui.ColorMenuButton.prototype.setSelectedColor = function(color) {
    +  this.setValue(color);
    +};
    +
    +
    +/**
    + * Sets the value associated with the color menu button.  Overrides
    + * {@link goog.ui.Button#setValue} by interpreting the value as a color
    + * spec string.
    + * @param {*} value New button value; should be a color spec string.
    + * @override
    + */
    +goog.ui.ColorMenuButton.prototype.setValue = function(value) {
    +  var color = /** @type {?string} */ (value);
    +  for (var i = 0, item; item = this.getItemAt(i); i++) {
    +    if (typeof item.setSelectedColor == 'function') {
    +      // This menu item looks like a color palette.
    +      item.setSelectedColor(color);
    +    }
    +  }
    +  goog.ui.ColorMenuButton.superClass_.setValue.call(this, color);
    +};
    +
    +
    +/**
    + * Handles {@link goog.ui.Component.EventType.ACTION} events dispatched by
    + * the menu item clicked by the user.  Updates the button, calls the superclass
    + * implementation to hide the menu, stops the propagation of the event, and
    + * dispatches an ACTION event on behalf of the button itself.  Overrides
    + * {@link goog.ui.MenuButton#handleMenuAction}.
    + * @param {goog.events.Event} e Action event to handle.
    + * @override
    + */
    +goog.ui.ColorMenuButton.prototype.handleMenuAction = function(e) {
    +  if (typeof e.target.getSelectedColor == 'function') {
    +    // User clicked something that looks like a color palette.
    +    this.setValue(e.target.getSelectedColor());
    +  } else if (e.target.getValue() == goog.ui.ColorMenuButton.NO_COLOR) {
    +    // User clicked the special "no color" menu item.
    +    this.setValue(null);
    +  }
    +  goog.ui.ColorMenuButton.superClass_.handleMenuAction.call(this, e);
    +  e.stopPropagation();
    +  this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +};
    +
    +
    +/**
    + * Opens or closes the menu.  Overrides {@link goog.ui.MenuButton#setOpen} by
    + * generating a default color menu on the fly if needed.
    + * @param {boolean} open Whether to open or close the menu.
    + * @param {goog.events.Event=} opt_e Mousedown event that caused the menu to
    + *     be opened.
    + * @override
    + */
    +goog.ui.ColorMenuButton.prototype.setOpen = function(open, opt_e) {
    +  if (open && this.getItemCount() == 0) {
    +    this.setMenu(
    +        goog.ui.ColorMenuButton.newColorMenu(null, this.getDomHelper()));
    +    this.setValue(/** @type {?string} */ (this.getValue()));
    +  }
    +  goog.ui.ColorMenuButton.superClass_.setOpen.call(this, open, opt_e);
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.ColorMenuButtons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.ColorMenuButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.ColorMenuButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer.js
    new file mode 100644
    index 0000000000..51cd1be1b8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer.js
    @@ -0,0 +1,147 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.ColorMenuButton}s.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ColorMenuButtonRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.color');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.ColorMenuButton}s.
    + * @constructor
    + * @extends {goog.ui.MenuButtonRenderer}
    + */
    +goog.ui.ColorMenuButtonRenderer = function() {
    +  goog.ui.MenuButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ColorMenuButtonRenderer, goog.ui.MenuButtonRenderer);
    +goog.addSingletonGetter(goog.ui.ColorMenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ColorMenuButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-color-menu-button');
    +
    +
    +/**
    + * Overrides the superclass implementation by wrapping the caption text or DOM
    + * structure in a color indicator element.  Creates the following DOM structure:
    + *   <div class="goog-inline-block goog-menu-button-caption">
    + *     <div class="goog-color-menu-button-indicator">
    + *       Contents...
    + *     </div>
    + *   </div>
    + * The 'goog-color-menu-button-indicator' style should be defined to have a
    + * bottom border of nonzero width and a default color that blends into its
    + * background.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Caption element.
    + * @override
    + */
    +goog.ui.ColorMenuButtonRenderer.prototype.createCaption = function(content,
    +    dom) {
    +  return goog.ui.ColorMenuButtonRenderer.superClass_.createCaption.call(this,
    +      goog.ui.ColorMenuButtonRenderer.wrapCaption(content, dom), dom);
    +};
    +
    +
    +/**
    + * Wrap a caption in a div with the color-menu-button-indicator CSS class.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Caption element.
    + */
    +goog.ui.ColorMenuButtonRenderer.wrapCaption = function(content, dom) {
    +  return dom.createDom('div',
    +      goog.getCssName(goog.ui.ColorMenuButtonRenderer.CSS_CLASS, 'indicator'),
    +      content);
    +};
    +
    +
    +/**
    + * Takes a color menu button control's root element and a value object
    + * (which is assumed to be a color), and updates the button's DOM to reflect
    + * the new color.  Overrides {@link goog.ui.ButtonRenderer#setValue}.
    + * @param {Element} element The button control's root element (if rendered).
    + * @param {*} value New value; assumed to be a color spec string.
    + * @override
    + */
    +goog.ui.ColorMenuButtonRenderer.prototype.setValue = function(element, value) {
    +  if (element) {
    +    goog.ui.ColorMenuButtonRenderer.setCaptionValue(
    +        this.getContentElement(element), value);
    +  }
    +};
    +
    +
    +/**
    + * Takes a control's content element and a value object (which is assumed
    + * to be a color), and updates its DOM to reflect the new color.
    + * @param {Element} caption A content element of a control.
    + * @param {*} value New value; assumed to be a color spec string.
    + */
    +goog.ui.ColorMenuButtonRenderer.setCaptionValue = function(caption, value) {
    +  // Assume that the caption's first child is the indicator.
    +  if (caption && caption.firstChild) {
    +    // Normalize the value to a hex color spec or null (otherwise setting
    +    // borderBottomColor will cause a JS error on IE).
    +    var hexColor;
    +
    +    var strValue = /** @type {string} */ (value);
    +    hexColor = strValue && goog.color.isValidColor(strValue) ?
    +        goog.color.parse(strValue).hex :
    +        null;
    +
    +    // Stupid IE6/7 doesn't do transparent borders.
    +    // TODO(attila): Add user-agent version check when IE8 comes out...
    +    caption.firstChild.style.borderBottomColor = hexColor ||
    +        (goog.userAgent.IE ? '' : 'transparent');
    +  }
    +};
    +
    +
    +/**
    + * Initializes the button's DOM when it enters the document.  Overrides the
    + * superclass implementation by making sure the button's color indicator is
    + * initialized.
    + * @param {goog.ui.Control} button goog.ui.ColorMenuButton whose DOM is to be
    + *     initialized as it enters the document.
    + * @override
    + */
    +goog.ui.ColorMenuButtonRenderer.prototype.initializeDom = function(button) {
    +  var buttonElement = button.getElement();
    +  goog.asserts.assert(buttonElement);
    +  this.setValue(buttonElement, button.getValue());
    +  goog.dom.classlist.add(buttonElement,
    +      goog.ui.ColorMenuButtonRenderer.CSS_CLASS);
    +  goog.ui.ColorMenuButtonRenderer.superClass_.initializeDom.call(this,
    +      button);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.html
    new file mode 100644
    index 0000000000..b5a7be8ee0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for ColorMenuButtonRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ColorMenuButtonTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <!-- A parent to attach rendered buttons to -->
    +   <div id="parent"></div>
    +   <!-- A button to decorate -->
    +   <div id="decoratedButton"><div>Foo</div></div></div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.js
    new file mode 100644
    index 0000000000..5205be3690
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colormenubuttonrenderer_test.js
    @@ -0,0 +1,71 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ColorMenuButtonTest');
    +goog.setTestOnly('goog.ui.ColorMenuButtonTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.RendererHarness');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.ColorMenuButton');
    +goog.require('goog.ui.ColorMenuButtonRenderer');
    +goog.require('goog.userAgent');
    +
    +var harness;
    +
    +function setUp() {
    +  harness = new goog.testing.ui.RendererHarness(
    +      goog.ui.ColorMenuButtonRenderer.getInstance(),
    +      goog.dom.getElement('parent'),
    +      goog.dom.getElement('decoratedButton'));
    +}
    +
    +function tearDown() {
    +  harness.dispose();
    +}
    +
    +function testEquality() {
    +  harness.attachControlAndRender(
    +      new goog.ui.ColorMenuButton('Foo'));
    +  harness.attachControlAndDecorate(
    +      new goog.ui.ColorMenuButton());
    +  harness.assertDomMatches();
    +}
    +
    +function testWrapCaption() {
    +  var caption = goog.dom.createDom('div', null, 'Foo');
    +  var wrappedCaption = goog.ui.ColorMenuButtonRenderer.wrapCaption(caption,
    +      goog.dom.getDomHelper());
    +  assertNotEquals('Caption should have been wrapped', caption, wrappedCaption);
    +  assertEquals('Wrapped caption should have indicator css class',
    +      'goog-color-menu-button-indicator', wrappedCaption.className);
    +}
    +
    +function testSetCaptionValue() {
    +  var caption = goog.dom.createDom('div', null, 'Foo');
    +  var wrappedCaption = goog.ui.ColorMenuButtonRenderer.wrapCaption(caption,
    +      goog.dom.getDomHelper());
    +  goog.ui.ColorMenuButtonRenderer.setCaptionValue(wrappedCaption, 'red');
    +
    +  var expectedColor =
    +      goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) ?
    +      '#ff0000' : 'rgb(255, 0, 0)';
    +  assertEquals(expectedColor, caption.style.borderBottomColor);
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.ColorMenuButtonRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorpalette.js b/mapbox-gl/closure-library/closure/goog/ui/colorpalette.js
    new file mode 100644
    index 0000000000..f1a271e18e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorpalette.js
    @@ -0,0 +1,178 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A control for representing a palette of colors, that the user
    + * can highlight or select via the keyboard or the mouse.
    + *
    + */
    +
    +goog.provide('goog.ui.ColorPalette');
    +
    +goog.require('goog.array');
    +goog.require('goog.color');
    +goog.require('goog.style');
    +goog.require('goog.ui.Palette');
    +goog.require('goog.ui.PaletteRenderer');
    +
    +
    +
    +/**
    + * A color palette is a grid of color swatches that the user can highlight or
    + * select via the keyboard or the mouse.  The selection state of the palette is
    + * controlled by a selection model.  When the user makes a selection, the
    + * component fires an ACTION event.  Event listeners may retrieve the selected
    + * color using the {@link #getSelectedColor} method.
    + *
    + * @param {Array<string>=} opt_colors Array of colors in any valid CSS color
    + *     format.
    + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
    + *     decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Palette}
    + */
    +goog.ui.ColorPalette = function(opt_colors, opt_renderer, opt_domHelper) {
    +  /**
    +   * Array of colors to show in the palette.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.colors_ = opt_colors || [];
    +
    +  goog.ui.Palette.call(this, null,
    +      opt_renderer || goog.ui.PaletteRenderer.getInstance(), opt_domHelper);
    +
    +  // Set the colors separately from the super call since we need the correct
    +  // DomHelper to be initialized for this class.
    +  this.setColors(this.colors_);
    +};
    +goog.inherits(goog.ui.ColorPalette, goog.ui.Palette);
    +goog.tagUnsealableClass(goog.ui.ColorPalette);
    +
    +
    +/**
    + * Array of normalized colors. Initialized lazily as often never needed.
    + * @type {?Array<string>}
    + * @private
    + */
    +goog.ui.ColorPalette.prototype.normalizedColors_ = null;
    +
    +
    +/**
    + * Array of labels for the colors. Will be used for the tooltips and
    + * accessibility.
    + * @type {?Array<string>}
    + * @private
    + */
    +goog.ui.ColorPalette.prototype.labels_ = null;
    +
    +
    +/**
    + * Returns the array of colors represented in the color palette.
    + * @return {Array<string>} Array of colors.
    + */
    +goog.ui.ColorPalette.prototype.getColors = function() {
    +  return this.colors_;
    +};
    +
    +
    +/**
    + * Sets the colors that are contained in the palette.
    + * @param {Array<string>} colors Array of colors in any valid CSS color format.
    + * @param {Array<string>=} opt_labels The array of labels to be used as
    + *        tooltips. When not provided, the color value will be used.
    + */
    +goog.ui.ColorPalette.prototype.setColors = function(colors, opt_labels) {
    +  this.colors_ = colors;
    +  this.labels_ = opt_labels || null;
    +  this.normalizedColors_ = null;
    +  this.setContent(this.createColorNodes());
    +};
    +
    +
    +/**
    + * @return {?string} The current selected color in hex, or null.
    + */
    +goog.ui.ColorPalette.prototype.getSelectedColor = function() {
    +  var selectedItem = /** @type {Element} */ (this.getSelectedItem());
    +  if (selectedItem) {
    +    var color = goog.style.getStyle(selectedItem, 'background-color');
    +    return goog.ui.ColorPalette.parseColor_(color);
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Sets the selected color.  Clears the selection if the argument is null or
    + * can't be parsed as a color.
    + * @param {?string} color The color to set as selected; null clears the
    + *     selection.
    + */
    +goog.ui.ColorPalette.prototype.setSelectedColor = function(color) {
    +  var hexColor = goog.ui.ColorPalette.parseColor_(color);
    +  if (!this.normalizedColors_) {
    +    this.normalizedColors_ = goog.array.map(this.colors_, function(color) {
    +      return goog.ui.ColorPalette.parseColor_(color);
    +    });
    +  }
    +  this.setSelectedIndex(hexColor ?
    +      goog.array.indexOf(this.normalizedColors_, hexColor) : -1);
    +};
    +
    +
    +/**
    + * @return {!Array<!Node>} An array of DOM nodes for each color.
    + * @protected
    + */
    +goog.ui.ColorPalette.prototype.createColorNodes = function() {
    +  return goog.array.map(this.colors_, function(color, index) {
    +    var swatch = this.getDomHelper().createDom('div', {
    +      'class': goog.getCssName(this.getRenderer().getCssClass(),
    +          'colorswatch'),
    +      'style': 'background-color:' + color
    +    });
    +    if (this.labels_ && this.labels_[index]) {
    +      swatch.title = this.labels_[index];
    +    } else {
    +      swatch.title = color.charAt(0) == '#' ?
    +          'RGB (' + goog.color.hexToRgb(color).join(', ') + ')' : color;
    +    }
    +    return swatch;
    +  }, this);
    +};
    +
    +
    +/**
    + * Takes a string, attempts to parse it as a color spec, and returns a
    + * normalized hex color spec if successful (null otherwise).
    + * @param {?string} color String possibly containing a color spec; may be null.
    + * @return {?string} Normalized hex color spec, or null if the argument can't
    + *     be parsed as a color.
    + * @private
    + */
    +goog.ui.ColorPalette.parseColor_ = function(color) {
    +  if (color) {
    +    /** @preserveTry */
    +    try {
    +      return goog.color.parse(color).hex;
    +    } catch (ex) {
    +      // Fall through.
    +    }
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.html b/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.html
    new file mode 100644
    index 0000000000..da4da9c847
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!-- Author:  attila@google.com (Attila Bodis) -->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ColorPalette
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ColorPaletteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.js b/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.js
    new file mode 100644
    index 0000000000..e9628261ed
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorpalette_test.js
    @@ -0,0 +1,169 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ColorPaletteTest');
    +goog.setTestOnly('goog.ui.ColorPaletteTest');
    +
    +goog.require('goog.color');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ColorPalette');
    +
    +var emptyPalette, samplePalette;
    +
    +function setUp() {
    +  emptyPalette = new goog.ui.ColorPalette();
    +  samplePalette = new goog.ui.ColorPalette([
    +    'red', '#00FF00', 'rgb(0, 0, 255)'
    +  ]);
    +  samplePalette.setSelectedColor('blue');
    +}
    +
    +function tearDown() {
    +  emptyPalette.dispose();
    +  samplePalette.dispose();
    +  document.getElementById('sandbox').innerHTML = '';
    +}
    +
    +function testEmptyColorPalette() {
    +  var colors = emptyPalette.getColors();
    +  assertNotNull(colors);
    +  assertEquals(0, colors.length);
    +
    +  var nodes = emptyPalette.getContent();
    +  assertNotNull(nodes);
    +  assertEquals(0, nodes.length);
    +}
    +
    +function testSampleColorPalette() {
    +  var colors = samplePalette.getColors();
    +  assertNotNull(colors);
    +  assertEquals(3, colors.length);
    +  assertEquals('red', colors[0]);
    +  assertEquals('#00FF00', colors[1]);
    +  assertEquals('rgb(0, 0, 255)', colors[2]);
    +
    +  var nodes = samplePalette.getContent();
    +  assertNotNull(nodes);
    +  assertEquals(3, nodes.length);
    +  assertEquals('goog-palette-colorswatch', nodes[0].className);
    +  assertEquals('goog-palette-colorswatch', nodes[1].className);
    +  assertEquals('goog-palette-colorswatch', nodes[2].className);
    +  assertEquals('#ff0000',
    +      goog.color.parse(nodes[0].style.backgroundColor).hex);
    +  assertEquals('#00ff00',
    +      goog.color.parse(nodes[1].style.backgroundColor).hex);
    +  assertEquals('#0000ff',
    +      goog.color.parse(nodes[2].style.backgroundColor).hex);
    +}
    +
    +function testGetColors() {
    +  var emptyColors = emptyPalette.getColors();
    +  assertNotNull(emptyColors);
    +  assertEquals(0, emptyColors.length);
    +
    +  var sampleColors = samplePalette.getColors();
    +  assertNotNull(sampleColors);
    +  assertEquals(3, sampleColors.length);
    +  assertEquals('red', sampleColors[0]);
    +  assertEquals('#00FF00', sampleColors[1]);
    +  assertEquals('rgb(0, 0, 255)', sampleColors[2]);
    +}
    +
    +function testSetColors() {
    +  emptyPalette.setColors(['black', '#FFFFFF']);
    +
    +  var colors = emptyPalette.getColors();
    +  assertNotNull(colors);
    +  assertEquals(2, colors.length);
    +  assertEquals('black', colors[0]);
    +  assertEquals('#FFFFFF', colors[1]);
    +
    +  var nodes = emptyPalette.getContent();
    +  assertNotNull(nodes);
    +  assertEquals(2, nodes.length);
    +  assertEquals('goog-palette-colorswatch', nodes[0].className);
    +  assertEquals('goog-palette-colorswatch', nodes[1].className);
    +  assertEquals('#000000',
    +      goog.color.parse(nodes[0].style.backgroundColor).hex);
    +  assertEquals('#ffffff',
    +      goog.color.parse(nodes[1].style.backgroundColor).hex);
    +  assertEquals('black', nodes[0].title);
    +  assertEquals('RGB (255, 255, 255)', nodes[1].title);
    +
    +  samplePalette.setColors(['#336699', 'cyan']);
    +
    +  var newColors = samplePalette.getColors();
    +  assertNotNull(newColors);
    +  assertEquals(2, newColors.length);
    +  assertEquals('#336699', newColors[0]);
    +  assertEquals('cyan', newColors[1]);
    +
    +  var newNodes = samplePalette.getContent();
    +  assertNotNull(newNodes);
    +  assertEquals(2, newNodes.length);
    +  assertEquals('goog-palette-colorswatch', newNodes[0].className);
    +  assertEquals('goog-palette-colorswatch', newNodes[1].className);
    +  assertEquals('#336699',
    +      goog.color.parse(newNodes[0].style.backgroundColor).hex);
    +  assertEquals('#00ffff',
    +      goog.color.parse(newNodes[1].style.backgroundColor).hex);
    +}
    +
    +function testSetColorsWithLabels() {
    +  emptyPalette.setColors(['#00f', '#FFFFFF', 'black'], ['blue', 'white']);
    +  var nodes = emptyPalette.getContent();
    +  assertEquals('blue', nodes[0].title);
    +  assertEquals('white', nodes[1].title);
    +  assertEquals('black', nodes[2].title);
    +}
    +
    +function testRender() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  assertTrue(samplePalette.isInDocument());
    +
    +  var elem = samplePalette.getElement();
    +  assertNotNull(elem);
    +  assertEquals('DIV', elem.tagName);
    +  assertEquals('goog-palette', elem.className);
    +
    +  var table = elem.firstChild;
    +  assertEquals('TABLE', table.tagName);
    +  assertEquals('goog-palette-table', table.className);
    +}
    +
    +function testGetSelectedColor() {
    +  assertNull(emptyPalette.getSelectedColor());
    +  assertEquals('#0000ff', samplePalette.getSelectedColor());
    +}
    +
    +function testSetSelectedColor() {
    +  emptyPalette.setSelectedColor('red');
    +  assertNull(emptyPalette.getSelectedColor());
    +
    +  samplePalette.setSelectedColor('red');
    +  assertEquals('#ff0000', samplePalette.getSelectedColor());
    +  samplePalette.setSelectedColor(17); // Invalid color spec.
    +  assertNull(samplePalette.getSelectedColor());
    +
    +  samplePalette.setSelectedColor('rgb(0, 255, 0)');
    +  assertEquals('#00ff00', samplePalette.getSelectedColor());
    +  samplePalette.setSelectedColor(false); // Invalid color spec.
    +  assertNull(samplePalette.getSelectedColor());
    +
    +  samplePalette.setSelectedColor('#0000FF');
    +  assertEquals('#0000ff', samplePalette.getSelectedColor());
    +  samplePalette.setSelectedColor(null); // Invalid color spec.
    +  assertNull(samplePalette.getSelectedColor());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorpicker.js b/mapbox-gl/closure-library/closure/goog/ui/colorpicker.js
    new file mode 100644
    index 0000000000..1be8120149
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorpicker.js
    @@ -0,0 +1,345 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A color picker component.  A color picker can compose several
    + * instances of goog.ui.ColorPalette.
    + *
    + * NOTE: The ColorPicker is in a state of transition towards the common
    + * component/control/container interface we are developing.  If the API changes
    + * we will do our best to update your code.  The end result will be that a
    + * color picker will compose multiple color palettes.  In the simple case this
    + * will be one grid, but may consistute 3 distinct grids, a custom color picker
    + * or even a color wheel.
    + *
    + */
    +
    +goog.provide('goog.ui.ColorPicker');
    +goog.provide('goog.ui.ColorPicker.EventType');
    +
    +goog.require('goog.ui.ColorPalette');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Create a new, empty color picker.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.ColorPalette=} opt_colorPalette Optional color palette to
    + *     use for this color picker.
    + * @extends {goog.ui.Component}
    + * @constructor
    + * @final
    + */
    +goog.ui.ColorPicker = function(opt_domHelper, opt_colorPalette) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The color palette used inside the color picker.
    +   * @type {goog.ui.ColorPalette?}
    +   * @private
    +   */
    +  this.colorPalette_ = opt_colorPalette || null;
    +
    +  this.getHandler().listen(
    +      this, goog.ui.Component.EventType.ACTION, this.onColorPaletteAction_);
    +};
    +goog.inherits(goog.ui.ColorPicker, goog.ui.Component);
    +
    +
    +/**
    + * Default number of columns in the color palette. May be overridden by calling
    + * setSize.
    + *
    + * @type {number}
    + */
    +goog.ui.ColorPicker.DEFAULT_NUM_COLS = 5;
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +goog.ui.ColorPicker.EventType = {
    +  CHANGE: 'change'
    +};
    +
    +
    +/**
    + * Whether the component is focusable.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ColorPicker.prototype.focusable_ = true;
    +
    +
    +/**
    + * Gets the array of colors displayed by the color picker.
    + * Modifying this array will lead to unexpected behavior.
    + * @return {Array<string>?} The colors displayed by this widget.
    + */
    +goog.ui.ColorPicker.prototype.getColors = function() {
    +  return this.colorPalette_ ? this.colorPalette_.getColors() : null;
    +};
    +
    +
    +/**
    + * Sets the array of colors to be displayed by the color picker.
    + * @param {Array<string>} colors The array of colors to be added.
    + */
    +goog.ui.ColorPicker.prototype.setColors = function(colors) {
    +  // TODO(user): Don't add colors directly, we should add palettes and the
    +  // picker should support multiple palettes.
    +  if (!this.colorPalette_) {
    +    this.createColorPalette_(colors);
    +  } else {
    +    this.colorPalette_.setColors(colors);
    +  }
    +};
    +
    +
    +/**
    + * Sets the array of colors to be displayed by the color picker.
    + * @param {Array<string>} colors The array of colors to be added.
    + * @deprecated Use setColors.
    + */
    +goog.ui.ColorPicker.prototype.addColors = function(colors) {
    +  this.setColors(colors);
    +};
    +
    +
    +/**
    + * Sets the size of the palette.  Will throw an error after the picker has been
    + * rendered.
    + * @param {goog.math.Size|number} size The size of the grid.
    + */
    +goog.ui.ColorPicker.prototype.setSize = function(size) {
    +  // TODO(user): The color picker should contain multiple palettes which will
    +  // all be resized at this point.
    +  if (!this.colorPalette_) {
    +    this.createColorPalette_([]);
    +  }
    +  this.colorPalette_.setSize(size);
    +};
    +
    +
    +/**
    + * Gets the number of columns displayed.
    + * @return {goog.math.Size?} The size of the grid.
    + */
    +goog.ui.ColorPicker.prototype.getSize = function() {
    +  return this.colorPalette_ ? this.colorPalette_.getSize() : null;
    +};
    +
    +
    +/**
    + * Sets the number of columns.  Will throw an error after the picker has been
    + * rendered.
    + * @param {number} n The number of columns.
    + * @deprecated Use setSize.
    + */
    +goog.ui.ColorPicker.prototype.setColumnCount = function(n) {
    +  this.setSize(n);
    +};
    +
    +
    +/**
    + * @return {number} The index of the color selected.
    + */
    +goog.ui.ColorPicker.prototype.getSelectedIndex = function() {
    +  return this.colorPalette_ ? this.colorPalette_.getSelectedIndex() : -1;
    +};
    +
    +
    +/**
    + * Sets which color is selected. A value that is out-of-range means that no
    + * color is selected.
    + * @param {number} ind The index in this.colors_ of the selected color.
    + */
    +goog.ui.ColorPicker.prototype.setSelectedIndex = function(ind) {
    +  if (this.colorPalette_) {
    +    this.colorPalette_.setSelectedIndex(ind);
    +  }
    +};
    +
    +
    +/**
    + * Gets the color that is currently selected in this color picker.
    + * @return {?string} The hex string of the color selected, or null if no
    + *     color is selected.
    + */
    +goog.ui.ColorPicker.prototype.getSelectedColor = function() {
    +  return this.colorPalette_ ? this.colorPalette_.getSelectedColor() : null;
    +};
    +
    +
    +/**
    + * Sets which color is selected.  Noop if the color palette hasn't been created
    + * yet.
    + * @param {string} color The selected color.
    + */
    +goog.ui.ColorPicker.prototype.setSelectedColor = function(color) {
    +  // TODO(user): This will set the color in the first available palette that
    +  // contains it
    +  if (this.colorPalette_) {
    +    this.colorPalette_.setSelectedColor(color);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is focusable, false otherwise.  The default
    + * is true.  Focusable components always have a tab index and allocate a key
    + * handler to handle keyboard events while focused.
    + * @return {boolean} True iff the component is focusable.
    + */
    +goog.ui.ColorPicker.prototype.isFocusable = function() {
    +  return this.focusable_;
    +};
    +
    +
    +/**
    + * Sets whether the component is focusable.  The default is true.
    + * Focusable components always have a tab index and allocate a key handler to
    + * handle keyboard events while focused.
    + * @param {boolean} focusable True iff the component is focusable.
    + */
    +goog.ui.ColorPicker.prototype.setFocusable = function(focusable) {
    +  this.focusable_ = focusable;
    +  if (this.colorPalette_) {
    +    this.colorPalette_.setSupportedState(goog.ui.Component.State.FOCUSED,
    +        focusable);
    +  }
    +};
    +
    +
    +/**
    + * ColorPickers cannot be used to decorate pre-existing html, since the
    + * structure they build is fairly complicated.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Returns always false.
    + * @override
    + */
    +goog.ui.ColorPicker.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/**
    + * Renders the color picker inside the provided element. This will override the
    + * current content of the element.
    + * @override
    + */
    +goog.ui.ColorPicker.prototype.enterDocument = function() {
    +  goog.ui.ColorPicker.superClass_.enterDocument.call(this);
    +  if (this.colorPalette_) {
    +    this.colorPalette_.render(this.getElement());
    +  }
    +  this.getElement().unselectable = 'on';
    +};
    +
    +
    +/** @override */
    +goog.ui.ColorPicker.prototype.disposeInternal = function() {
    +  goog.ui.ColorPicker.superClass_.disposeInternal.call(this);
    +  if (this.colorPalette_) {
    +    this.colorPalette_.dispose();
    +    this.colorPalette_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Sets the focus to the color picker's palette.
    + */
    +goog.ui.ColorPicker.prototype.focus = function() {
    +  if (this.colorPalette_) {
    +    this.colorPalette_.getElement().focus();
    +  }
    +};
    +
    +
    +/**
    + * Handles actions from the color palette.
    + *
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.ColorPicker.prototype.onColorPaletteAction_ = function(e) {
    +  e.stopPropagation();
    +  this.dispatchEvent(goog.ui.ColorPicker.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * Create a color palette for the color picker.
    + * @param {Array<string>} colors Array of colors.
    + * @private
    + */
    +goog.ui.ColorPicker.prototype.createColorPalette_ = function(colors) {
    +  // TODO(user): The color picker should eventually just contain a number of
    +  // palettes and manage the interactions between them.  This will go away then.
    +  var cp = new goog.ui.ColorPalette(colors, null, this.getDomHelper());
    +  cp.setSize(goog.ui.ColorPicker.DEFAULT_NUM_COLS);
    +  cp.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
    +  // TODO(user): Use addChild(cp, true) and remove calls to render.
    +  this.addChild(cp);
    +  this.colorPalette_ = cp;
    +  if (this.isInDocument()) {
    +    this.colorPalette_.render(this.getElement());
    +  }
    +};
    +
    +
    +/**
    + * Returns an unrendered instance of the color picker.  The colors and layout
    + * are a simple color grid, the same as the old Gmail color picker.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @return {!goog.ui.ColorPicker} The unrendered instance.
    + */
    +goog.ui.ColorPicker.createSimpleColorGrid = function(opt_domHelper) {
    +  var cp = new goog.ui.ColorPicker(opt_domHelper);
    +  cp.setSize(7);
    +  cp.setColors(goog.ui.ColorPicker.SIMPLE_GRID_COLORS);
    +  return cp;
    +};
    +
    +
    +/**
    + * Array of colors for a 7-cell wide simple-grid color picker.
    + * @type {Array<string>}
    + */
    +goog.ui.ColorPicker.SIMPLE_GRID_COLORS = [
    +  // grays
    +  '#ffffff', '#cccccc', '#c0c0c0', '#999999', '#666666', '#333333', '#000000',
    +  // reds
    +  '#ffcccc', '#ff6666', '#ff0000', '#cc0000', '#990000', '#660000', '#330000',
    +  // oranges
    +  '#ffcc99', '#ff9966', '#ff9900', '#ff6600', '#cc6600', '#993300', '#663300',
    +  // yellows
    +  '#ffff99', '#ffff66', '#ffcc66', '#ffcc33', '#cc9933', '#996633', '#663333',
    +  // olives
    +  '#ffffcc', '#ffff33', '#ffff00', '#ffcc00', '#999900', '#666600', '#333300',
    +  // greens
    +  '#99ff99', '#66ff99', '#33ff33', '#33cc00', '#009900', '#006600', '#003300',
    +  // turquoises
    +  '#99ffff', '#33ffff', '#66cccc', '#00cccc', '#339999', '#336666', '#003333',
    +  // blues
    +  '#ccffff', '#66ffff', '#33ccff', '#3366ff', '#3333ff', '#000099', '#000066',
    +  // purples
    +  '#ccccff', '#9999ff', '#6666cc', '#6633ff', '#6600cc', '#333399', '#330099',
    +  // violets
    +  '#ffccff', '#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#663366', '#330033'
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/colorsplitbehavior.js b/mapbox-gl/closure-library/closure/goog/ui/colorsplitbehavior.js
    new file mode 100644
    index 0000000000..3aa7ab0992
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/colorsplitbehavior.js
    @@ -0,0 +1,61 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Behavior for combining a color button and a menu.
    + *
    + * @see ../demos/split.html
    + */
    +
    +goog.provide('goog.ui.ColorSplitBehavior');
    +
    +goog.require('goog.ui.ColorMenuButton');
    +goog.require('goog.ui.SplitBehavior');
    +
    +
    +
    +/**
    + * Constructs a ColorSplitBehavior for combining a color button and a menu.
    + * To use this, provide a goog.ui.ColorButton which will be attached with
    + * a goog.ui.ColorMenuButton (with no caption).
    + * Whenever a color is selected from the ColorMenuButton, it will be placed in
    + * the ColorButton and the user can apply it over and over (by clicking the
    + * ColorButton).
    + * Primary use case - setting the color of text/background in a text editor.
    + *
    + * @param {!goog.ui.Button} colorButton A button to interact with a color menu
    + *     button (preferably a goog.ui.ColorButton).
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @extends {goog.ui.SplitBehavior}
    + * @constructor
    + * @final
    + */
    +goog.ui.ColorSplitBehavior = function(colorButton, opt_domHelper) {
    +  goog.ui.ColorSplitBehavior.base(this, 'constructor', colorButton,
    +      new goog.ui.ColorMenuButton(goog.ui.ColorSplitBehavior.ZERO_WIDTH_SPACE_),
    +      goog.ui.SplitBehavior.DefaultHandlers.VALUE,
    +      undefined,
    +      opt_domHelper);
    +};
    +goog.inherits(goog.ui.ColorSplitBehavior, goog.ui.SplitBehavior);
    +
    +
    +/**
    + * A zero width space character.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ColorSplitBehavior.ZERO_WIDTH_SPACE_ = '\uFEFF';
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/combobox.js b/mapbox-gl/closure-library/closure/goog/ui/combobox.js
    new file mode 100644
    index 0000000000..5f7d9c1ed4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/combobox.js
    @@ -0,0 +1,987 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A combo box control that allows user input with
    + * auto-suggestion from a limited set of options.
    + *
    + * @see ../demos/combobox.html
    + */
    +
    +goog.provide('goog.ui.ComboBox');
    +goog.provide('goog.ui.ComboBoxItem');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.log');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.MenuAnchoredPosition');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ItemEvent');
    +goog.require('goog.ui.LabelInput');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuSeparator');
    +goog.require('goog.ui.registry');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A ComboBox control.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.Menu=} opt_menu Optional menu component.
    + *     This menu is disposed of by this control.
    + * @param {goog.ui.LabelInput=} opt_labelInput Optional label input.
    + *     This label input is disposed of by this control.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.ComboBox = function(opt_domHelper, opt_menu, opt_labelInput) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.labelInput_ = opt_labelInput || new goog.ui.LabelInput();
    +  this.enabled_ = true;
    +
    +  // TODO(user): Allow lazy creation of menus/menu items
    +  this.menu_ = opt_menu || new goog.ui.Menu(this.getDomHelper());
    +  this.setupMenu_();
    +};
    +goog.inherits(goog.ui.ComboBox, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.ComboBox);
    +
    +
    +/**
    + * Number of milliseconds to wait before dismissing combobox after blur.
    + * @type {number}
    + */
    +goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS = 250;
    +
    +
    +/**
    + * A logger to help debugging of combo box behavior.
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.logger_ =
    +    goog.log.getLogger('goog.ui.ComboBox');
    +
    +
    +/**
    + * Whether the combo box is enabled.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.enabled_;
    +
    +
    +/**
    + * Keyboard event handler to manage key events dispatched by the input element.
    + * @type {goog.events.KeyHandler}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.keyHandler_;
    +
    +
    +/**
    + * Input handler to take care of firing events when the user inputs text in
    + * the input.
    + * @type {goog.events.InputHandler?}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.inputHandler_ = null;
    +
    +
    +/**
    + * The last input token.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.lastToken_ = null;
    +
    +
    +/**
    + * A LabelInput control that manages the focus/blur state of the input box.
    + * @type {goog.ui.LabelInput?}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.labelInput_ = null;
    +
    +
    +/**
    + * Drop down menu for the combo box.  Will be created at construction time.
    + * @type {goog.ui.Menu?}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.menu_ = null;
    +
    +
    +/**
    + * The cached visible count.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.visibleCount_ = -1;
    +
    +
    +/**
    + * The input element.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.input_ = null;
    +
    +
    +/**
    + * The match function.  The first argument for the match function will be
    + * a MenuItem's caption and the second will be the token to evaluate.
    + * @type {Function}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.matchFunction_ = goog.string.startsWith;
    +
    +
    +/**
    + * Element used as the combo boxes button.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.button_ = null;
    +
    +
    +/**
    + * Default text content for the input box when it is unchanged and unfocussed.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.defaultText_ = '';
    +
    +
    +/**
    + * Name for the input box created
    + * @type {string}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.fieldName_ = '';
    +
    +
    +/**
    + * Timer identifier for delaying the dismissal of the combo menu.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.ComboBox.prototype.dismissTimer_ = null;
    +
    +
    +/**
    + * True if the unicode inverted triangle should be displayed in the dropdown
    + * button. Defaults to false.
    + * @type {boolean} useDropdownArrow
    + * @private
    + */
    +goog.ui.ComboBox.prototype.useDropdownArrow_ = false;
    +
    +
    +/**
    + * Create the DOM objects needed for the combo box.  A span and text input.
    + * @override
    + */
    +goog.ui.ComboBox.prototype.createDom = function() {
    +  this.input_ = this.getDomHelper().createDom(
    +      'input', {name: this.fieldName_, type: 'text', autocomplete: 'off'});
    +  this.button_ = this.getDomHelper().createDom('span',
    +      goog.getCssName('goog-combobox-button'));
    +  this.setElementInternal(this.getDomHelper().createDom('span',
    +      goog.getCssName('goog-combobox'), this.input_, this.button_));
    +  if (this.useDropdownArrow_) {
    +    goog.dom.setTextContent(this.button_, '\u25BC');
    +    goog.style.setUnselectable(this.button_, true /* unselectable */);
    +  }
    +  this.input_.setAttribute('label', this.defaultText_);
    +  this.labelInput_.decorate(this.input_);
    +  this.menu_.setFocusable(false);
    +  if (!this.menu_.isInDocument()) {
    +    this.addChild(this.menu_, true);
    +  }
    +};
    +
    +
    +/**
    + * Enables/Disables the combo box.
    + * @param {boolean} enabled Whether to enable (true) or disable (false) the
    + *     combo box.
    + */
    +goog.ui.ComboBox.prototype.setEnabled = function(enabled) {
    +  this.enabled_ = enabled;
    +  this.labelInput_.setEnabled(enabled);
    +  goog.dom.classlist.enable(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('goog-combobox-disabled'), !enabled);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the menu item is enabled.
    + */
    +goog.ui.ComboBox.prototype.isEnabled = function() {
    +  return this.enabled_;
    +};
    +
    +
    +/** @override */
    +goog.ui.ComboBox.prototype.enterDocument = function() {
    +  goog.ui.ComboBox.superClass_.enterDocument.call(this);
    +
    +  var handler = this.getHandler();
    +  handler.listen(this.getElement(),
    +      goog.events.EventType.MOUSEDOWN, this.onComboMouseDown_);
    +  handler.listen(this.getDomHelper().getDocument(),
    +      goog.events.EventType.MOUSEDOWN, this.onDocClicked_);
    +
    +  handler.listen(this.input_,
    +      goog.events.EventType.BLUR, this.onInputBlur_);
    +
    +  this.keyHandler_ = new goog.events.KeyHandler(this.input_);
    +  handler.listen(this.keyHandler_,
    +      goog.events.KeyHandler.EventType.KEY, this.handleKeyEvent);
    +
    +  this.inputHandler_ = new goog.events.InputHandler(this.input_);
    +  handler.listen(this.inputHandler_,
    +      goog.events.InputHandler.EventType.INPUT, this.onInputEvent_);
    +
    +  handler.listen(this.menu_,
    +      goog.ui.Component.EventType.ACTION, this.onMenuSelected_);
    +};
    +
    +
    +/** @override */
    +goog.ui.ComboBox.prototype.exitDocument = function() {
    +  this.keyHandler_.dispose();
    +  delete this.keyHandler_;
    +  this.inputHandler_.dispose();
    +  this.inputHandler_ = null;
    +  goog.ui.ComboBox.superClass_.exitDocument.call(this);
    +};
    +
    +
    +/**
    + * Combo box currently can't decorate elements.
    + * @return {boolean} The value false.
    + * @override
    + */
    +goog.ui.ComboBox.prototype.canDecorate = function() {
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.ui.ComboBox.prototype.disposeInternal = function() {
    +  goog.ui.ComboBox.superClass_.disposeInternal.call(this);
    +
    +  this.clearDismissTimer_();
    +
    +  this.labelInput_.dispose();
    +  this.menu_.dispose();
    +
    +  this.labelInput_ = null;
    +  this.menu_ = null;
    +  this.input_ = null;
    +  this.button_ = null;
    +};
    +
    +
    +/**
    + * Dismisses the menu and resets the value of the edit field.
    + */
    +goog.ui.ComboBox.prototype.dismiss = function() {
    +  this.clearDismissTimer_();
    +  this.hideMenu_();
    +  this.menu_.setHighlightedIndex(-1);
    +};
    +
    +
    +/**
    + * Adds a new menu item at the end of the menu.
    + * @param {goog.ui.MenuItem} item Menu item to add to the menu.
    + */
    +goog.ui.ComboBox.prototype.addItem = function(item) {
    +  this.menu_.addChild(item, true);
    +  this.visibleCount_ = -1;
    +};
    +
    +
    +/**
    + * Adds a new menu item at a specific index in the menu.
    + * @param {goog.ui.MenuItem} item Menu item to add to the menu.
    + * @param {number} n Index at which to insert the menu item.
    + */
    +goog.ui.ComboBox.prototype.addItemAt = function(item, n) {
    +  this.menu_.addChildAt(item, n, true);
    +  this.visibleCount_ = -1;
    +};
    +
    +
    +/**
    + * Removes an item from the menu and disposes it.
    + * @param {goog.ui.MenuItem} item The menu item to remove.
    + */
    +goog.ui.ComboBox.prototype.removeItem = function(item) {
    +  var child = this.menu_.removeChild(item, true);
    +  if (child) {
    +    child.dispose();
    +    this.visibleCount_ = -1;
    +  }
    +};
    +
    +
    +/**
    + * Remove all of the items from the ComboBox menu
    + */
    +goog.ui.ComboBox.prototype.removeAllItems = function() {
    +  for (var i = this.getItemCount() - 1; i >= 0; --i) {
    +    this.removeItem(this.getItemAt(i));
    +  }
    +};
    +
    +
    +/**
    + * Removes a menu item at a given index in the menu.
    + * @param {number} n Index of item.
    + */
    +goog.ui.ComboBox.prototype.removeItemAt = function(n) {
    +  var child = this.menu_.removeChildAt(n, true);
    +  if (child) {
    +    child.dispose();
    +    this.visibleCount_ = -1;
    +  }
    +};
    +
    +
    +/**
    + * Returns a reference to the menu item at a given index.
    + * @param {number} n Index of menu item.
    + * @return {goog.ui.MenuItem?} Reference to the menu item.
    + */
    +goog.ui.ComboBox.prototype.getItemAt = function(n) {
    +  return /** @type {goog.ui.MenuItem?} */(this.menu_.getChildAt(n));
    +};
    +
    +
    +/**
    + * Returns the number of items in the list, including non-visible items,
    + * such as separators.
    + * @return {number} Number of items in the menu for this combobox.
    + */
    +goog.ui.ComboBox.prototype.getItemCount = function() {
    +  return this.menu_.getChildCount();
    +};
    +
    +
    +/**
    + * @return {goog.ui.Menu} The menu that pops up.
    + */
    +goog.ui.ComboBox.prototype.getMenu = function() {
    +  return this.menu_;
    +};
    +
    +
    +/**
    + * @return {Element} The input element.
    + */
    +goog.ui.ComboBox.prototype.getInputElement = function() {
    +  return this.input_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.LabelInput} A LabelInput control that manages the
    + *     focus/blur state of the input box.
    + */
    +goog.ui.ComboBox.prototype.getLabelInput = function() {
    +  return this.labelInput_;
    +};
    +
    +
    +/**
    + * @return {number} The number of visible items in the menu.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.getNumberOfVisibleItems_ = function() {
    +  if (this.visibleCount_ == -1) {
    +    var count = 0;
    +    for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
    +      var item = this.menu_.getChildAt(i);
    +      if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) {
    +        count++;
    +      }
    +    }
    +    this.visibleCount_ = count;
    +  }
    +
    +  goog.log.info(this.logger_,
    +      'getNumberOfVisibleItems() - ' + this.visibleCount_);
    +  return this.visibleCount_;
    +};
    +
    +
    +/**
    + * Sets the match function to be used when filtering the combo box menu.
    + * @param {Function} matchFunction The match function to be used when filtering
    + *     the combo box menu.
    + */
    +goog.ui.ComboBox.prototype.setMatchFunction = function(matchFunction) {
    +  this.matchFunction_ = matchFunction;
    +};
    +
    +
    +/**
    + * @return {Function} The match function for the combox box.
    + */
    +goog.ui.ComboBox.prototype.getMatchFunction = function() {
    +  return this.matchFunction_;
    +};
    +
    +
    +/**
    + * Sets the default text for the combo box.
    + * @param {string} text The default text for the combo box.
    + */
    +goog.ui.ComboBox.prototype.setDefaultText = function(text) {
    +  this.defaultText_ = text;
    +  if (this.labelInput_) {
    +    this.labelInput_.setLabel(this.defaultText_);
    +  }
    +};
    +
    +
    +/**
    + * @return {string} text The default text for the combox box.
    + */
    +goog.ui.ComboBox.prototype.getDefaultText = function() {
    +  return this.defaultText_;
    +};
    +
    +
    +/**
    + * Sets the field name for the combo box.
    + * @param {string} fieldName The field name for the combo box.
    + */
    +goog.ui.ComboBox.prototype.setFieldName = function(fieldName) {
    +  this.fieldName_ = fieldName;
    +};
    +
    +
    +/**
    + * @return {string} The field name for the combo box.
    + */
    +goog.ui.ComboBox.prototype.getFieldName = function() {
    +  return this.fieldName_;
    +};
    +
    +
    +/**
    + * Set to true if a unicode inverted triangle should be displayed in the
    + * dropdown button.
    + * This option defaults to false for backwards compatibility.
    + * @param {boolean} useDropdownArrow True to use the dropdown arrow.
    + */
    +goog.ui.ComboBox.prototype.setUseDropdownArrow = function(useDropdownArrow) {
    +  this.useDropdownArrow_ = !!useDropdownArrow;
    +};
    +
    +
    +/**
    + * Sets the current value of the combo box.
    + * @param {string} value The new value.
    + */
    +goog.ui.ComboBox.prototype.setValue = function(value) {
    +  goog.log.info(this.logger_, 'setValue() - ' + value);
    +  if (this.labelInput_.getValue() != value) {
    +    this.labelInput_.setValue(value);
    +    this.handleInputChange_();
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The current value of the combo box.
    + */
    +goog.ui.ComboBox.prototype.getValue = function() {
    +  return this.labelInput_.getValue();
    +};
    +
    +
    +/**
    + * @return {string} HTML escaped token.
    + */
    +goog.ui.ComboBox.prototype.getToken = function() {
    +  // TODO(user): Remove HTML escaping and fix the existing calls.
    +  return goog.string.htmlEscape(this.getTokenText_());
    +};
    +
    +
    +/**
    + * @return {string} The token for the current cursor position in the
    + *     input box, when multi-input is disabled it will be the full input value.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.getTokenText_ = function() {
    +  // TODO(user): Implement multi-input such that getToken returns a substring
    +  // of the whole input delimited by commas.
    +  return goog.string.trim(this.labelInput_.getValue().toLowerCase());
    +};
    +
    +
    +/**
    + * @private
    + */
    +goog.ui.ComboBox.prototype.setupMenu_ = function() {
    +  var sm = this.menu_;
    +  sm.setVisible(false);
    +  sm.setAllowAutoFocus(false);
    +  sm.setAllowHighlightDisabled(true);
    +};
    +
    +
    +/**
    + * Shows the menu if it isn't already showing.  Also positions the menu
    + * correctly, resets the menu item visibilities and highlights the relevent
    + * item.
    + * @param {boolean} showAll Whether to show all items, with the first matching
    + *     item highlighted.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.maybeShowMenu_ = function(showAll) {
    +  var isVisible = this.menu_.isVisible();
    +  var numVisibleItems = this.getNumberOfVisibleItems_();
    +
    +  if (isVisible && numVisibleItems == 0) {
    +    goog.log.fine(this.logger_, 'no matching items, hiding');
    +    this.hideMenu_();
    +
    +  } else if (!isVisible && numVisibleItems > 0) {
    +    if (showAll) {
    +      goog.log.fine(this.logger_, 'showing menu');
    +      this.setItemVisibilityFromToken_('');
    +      this.setItemHighlightFromToken_(this.getTokenText_());
    +    }
    +    // In Safari 2.0, when clicking on the combox box, the blur event is
    +    // received after the click event that invokes this function. Since we want
    +    // to cancel the dismissal after the blur event is processed, we have to
    +    // wait for all event processing to happen.
    +    goog.Timer.callOnce(this.clearDismissTimer_, 1, this);
    +
    +    this.showMenu_();
    +  }
    +
    +  this.positionMenu();
    +};
    +
    +
    +/**
    + * Positions the menu.
    + * @protected
    + */
    +goog.ui.ComboBox.prototype.positionMenu = function() {
    +  if (this.menu_ && this.menu_.isVisible()) {
    +    var position = new goog.positioning.MenuAnchoredPosition(this.getElement(),
    +        goog.positioning.Corner.BOTTOM_START, true);
    +    position.reposition(this.menu_.getElement(),
    +        goog.positioning.Corner.TOP_START);
    +  }
    +};
    +
    +
    +/**
    + * Show the menu and add an active class to the combo box's element.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.showMenu_ = function() {
    +  this.menu_.setVisible(true);
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('goog-combobox-active'));
    +};
    +
    +
    +/**
    + * Hide the menu and remove the active class from the combo box's element.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.hideMenu_ = function() {
    +  this.menu_.setVisible(false);
    +  goog.dom.classlist.remove(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('goog-combobox-active'));
    +};
    +
    +
    +/**
    + * Clears the dismiss timer if it's active.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.clearDismissTimer_ = function() {
    +  if (this.dismissTimer_) {
    +    goog.Timer.clear(this.dismissTimer_);
    +    this.dismissTimer_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Event handler for when the combo box area has been clicked.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.onComboMouseDown_ = function(e) {
    +  // We only want this event on the element itself or the input or the button.
    +  if (this.enabled_ &&
    +      (e.target == this.getElement() || e.target == this.input_ ||
    +       goog.dom.contains(this.button_, /** @type {Node} */ (e.target)))) {
    +    if (this.menu_.isVisible()) {
    +      goog.log.fine(this.logger_, 'Menu is visible, dismissing');
    +      this.dismiss();
    +    } else {
    +      goog.log.fine(this.logger_, 'Opening dropdown');
    +      this.maybeShowMenu_(true);
    +      if (goog.userAgent.OPERA) {
    +        // select() doesn't focus <input> elements in Opera.
    +        this.input_.focus();
    +      }
    +      this.input_.select();
    +      this.menu_.setMouseButtonPressed(true);
    +      // Stop the click event from stealing focus
    +      e.preventDefault();
    +    }
    +  }
    +  // Stop the event from propagating outside of the combo box
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * Event handler for when the document is clicked.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.onDocClicked_ = function(e) {
    +  if (!goog.dom.contains(
    +      this.menu_.getElement(), /** @type {Node} */ (e.target))) {
    +    goog.log.info(this.logger_, 'onDocClicked_() - dismissing immediately');
    +    this.dismiss();
    +  }
    +};
    +
    +
    +/**
    + * Handle the menu's select event.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.onMenuSelected_ = function(e) {
    +  goog.log.info(this.logger_, 'onMenuSelected_()');
    +  var item = /** @type {!goog.ui.MenuItem} */ (e.target);
    +  // Stop propagation of the original event and redispatch to allow the menu
    +  // select to be cancelled at this level. i.e. if a menu item should cause
    +  // some behavior such as a user prompt instead of assigning the caption as
    +  // the value.
    +  if (this.dispatchEvent(new goog.ui.ItemEvent(
    +      goog.ui.Component.EventType.ACTION, this, item))) {
    +    var caption = item.getCaption();
    +    goog.log.fine(this.logger_,
    +        'Menu selection: ' + caption + '. Dismissing menu');
    +    if (this.labelInput_.getValue() != caption) {
    +      this.labelInput_.setValue(caption);
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +    this.dismiss();
    +  }
    +  e.stopPropagation();
    +};
    +
    +
    +/**
    + * Event handler for when the input box looses focus -- hide the menu
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.onInputBlur_ = function(e) {
    +  goog.log.info(this.logger_, 'onInputBlur_() - delayed dismiss');
    +  this.clearDismissTimer_();
    +  this.dismissTimer_ = goog.Timer.callOnce(
    +      this.dismiss, goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS, this);
    +};
    +
    +
    +/**
    + * Handles keyboard events from the input box.  Returns true if the combo box
    + * was able to handle the event, false otherwise.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the event was handled by the combo box.
    + * @protected
    + * @suppress {visibility} performActionInternal
    + */
    +goog.ui.ComboBox.prototype.handleKeyEvent = function(e) {
    +  var isMenuVisible = this.menu_.isVisible();
    +
    +  // Give the menu a chance to handle the event.
    +  if (isMenuVisible && this.menu_.handleKeyEvent(e)) {
    +    return true;
    +  }
    +
    +  // The menu is either hidden or didn't handle the event.
    +  var handled = false;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.ESC:
    +      // If the menu is visible and the user hit Esc, dismiss the menu.
    +      if (isMenuVisible) {
    +        goog.log.fine(this.logger_,
    +            'Dismiss on Esc: ' + this.labelInput_.getValue());
    +        this.dismiss();
    +        handled = true;
    +      }
    +      break;
    +    case goog.events.KeyCodes.TAB:
    +      // If the menu is open and an option is highlighted, activate it.
    +      if (isMenuVisible) {
    +        var highlighted = this.menu_.getHighlighted();
    +        if (highlighted) {
    +          goog.log.fine(this.logger_,
    +              'Select on Tab: ' + this.labelInput_.getValue());
    +          highlighted.performActionInternal(e);
    +          handled = true;
    +        }
    +      }
    +      break;
    +    case goog.events.KeyCodes.UP:
    +    case goog.events.KeyCodes.DOWN:
    +      // If the menu is hidden and the user hit the up/down arrow, show it.
    +      if (!isMenuVisible) {
    +        goog.log.fine(this.logger_, 'Up/Down - maybe show menu');
    +        this.maybeShowMenu_(true);
    +        handled = true;
    +      }
    +      break;
    +  }
    +
    +  if (handled) {
    +    e.preventDefault();
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Handles the content of the input box changing.
    + * @param {goog.events.Event} e The INPUT event to handle.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.onInputEvent_ = function(e) {
    +  // If the key event is text-modifying, update the menu.
    +  goog.log.fine(this.logger_,
    +      'Key is modifying: ' + this.labelInput_.getValue());
    +  this.handleInputChange_();
    +};
    +
    +
    +/**
    + * Handles the content of the input box changing, either because of user
    + * interaction or programmatic changes.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.handleInputChange_ = function() {
    +  var token = this.getTokenText_();
    +  this.setItemVisibilityFromToken_(token);
    +  if (goog.dom.getActiveElement(this.getDomHelper().getDocument()) ==
    +      this.input_) {
    +    // Do not alter menu visibility unless the user focus is currently on the
    +    // combobox (otherwise programmatic changes may cause the menu to become
    +    // visible).
    +    this.maybeShowMenu_(false);
    +  }
    +  var highlighted = this.menu_.getHighlighted();
    +  if (token == '' || !highlighted || !highlighted.isVisible()) {
    +    this.setItemHighlightFromToken_(token);
    +  }
    +  this.lastToken_ = token;
    +  this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * Loops through all menu items setting their visibility according to a token.
    + * @param {string} token The token.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.setItemVisibilityFromToken_ = function(token) {
    +  goog.log.info(this.logger_, 'setItemVisibilityFromToken_() - ' + token);
    +  var isVisibleItem = false;
    +  var count = 0;
    +  var recheckHidden = !this.matchFunction_(token, this.lastToken_);
    +
    +  for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
    +    var item = this.menu_.getChildAt(i);
    +    if (item instanceof goog.ui.MenuSeparator) {
    +      // Ensure that separators are only shown if there is at least one visible
    +      // item before them.
    +      item.setVisible(isVisibleItem);
    +      isVisibleItem = false;
    +    } else if (item instanceof goog.ui.MenuItem) {
    +      if (!item.isVisible() && !recheckHidden) continue;
    +
    +      var caption = item.getCaption();
    +      var visible = this.isItemSticky_(item) ||
    +          caption && this.matchFunction_(caption.toLowerCase(), token);
    +      if (typeof item.setFormatFromToken == 'function') {
    +        item.setFormatFromToken(token);
    +      }
    +      item.setVisible(!!visible);
    +      isVisibleItem = visible || isVisibleItem;
    +
    +    } else {
    +      // Assume all other items are correctly using their visibility.
    +      isVisibleItem = item.isVisible() || isVisibleItem;
    +    }
    +
    +    if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) {
    +      count++;
    +    }
    +  }
    +
    +  this.visibleCount_ = count;
    +};
    +
    +
    +/**
    + * Highlights the first token that matches the given token.
    + * @param {string} token The token.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.setItemHighlightFromToken_ = function(token) {
    +  goog.log.info(this.logger_, 'setItemHighlightFromToken_() - ' + token);
    +
    +  if (token == '') {
    +    this.menu_.setHighlightedIndex(-1);
    +    return;
    +  }
    +
    +  for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
    +    var item = this.menu_.getChildAt(i);
    +    var caption = item.getCaption();
    +    if (caption && this.matchFunction_(caption.toLowerCase(), token)) {
    +      this.menu_.setHighlightedIndex(i);
    +      if (item.setFormatFromToken) {
    +        item.setFormatFromToken(token);
    +      }
    +      return;
    +    }
    +  }
    +  this.menu_.setHighlightedIndex(-1);
    +};
    +
    +
    +/**
    + * Returns true if the item has an isSticky method and the method returns true.
    + * @param {goog.ui.MenuItem} item The item.
    + * @return {boolean} Whether the item has an isSticky method and the method
    + *     returns true.
    + * @private
    + */
    +goog.ui.ComboBox.prototype.isItemSticky_ = function(item) {
    +  return typeof item.isSticky == 'function' && item.isSticky();
    +};
    +
    +
    +
    +/**
    + * Class for combo box items.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {Object=} opt_data Identifying data for the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper used for dom
    + *     interactions.
    + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + */
    +goog.ui.ComboBoxItem = function(content, opt_data, opt_domHelper,
    +    opt_renderer) {
    +  goog.ui.MenuItem.call(this, content, opt_data, opt_domHelper, opt_renderer);
    +};
    +goog.inherits(goog.ui.ComboBoxItem, goog.ui.MenuItem);
    +goog.tagUnsealableClass(goog.ui.ComboBoxItem);
    +
    +
    +// Register a decorator factory function for goog.ui.ComboBoxItems.
    +goog.ui.registry.setDecoratorByClassName(goog.getCssName('goog-combobox-item'),
    +    function() {
    +      // ComboBoxItem defaults to using MenuItemRenderer.
    +      return new goog.ui.ComboBoxItem(null);
    +    });
    +
    +
    +/**
    + * Whether the menu item is sticky, non-sticky items will be hidden as the
    + * user types.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ComboBoxItem.prototype.isSticky_ = false;
    +
    +
    +/**
    + * Sets the menu item to be sticky or not sticky.
    + * @param {boolean} sticky Whether the menu item should be sticky.
    + */
    +goog.ui.ComboBoxItem.prototype.setSticky = function(sticky) {
    +  this.isSticky_ = sticky;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the menu item is sticky.
    + */
    +goog.ui.ComboBoxItem.prototype.isSticky = function() {
    +  return this.isSticky_;
    +};
    +
    +
    +/**
    + * Sets the format for a menu item based on a token, bolding the token.
    + * @param {string} token The token.
    + */
    +goog.ui.ComboBoxItem.prototype.setFormatFromToken = function(token) {
    +  if (this.isEnabled()) {
    +    var caption = this.getCaption();
    +    var index = caption.toLowerCase().indexOf(token);
    +    if (index >= 0) {
    +      var domHelper = this.getDomHelper();
    +      this.setContent([
    +        domHelper.createTextNode(caption.substr(0, index)),
    +        domHelper.createDom('b', null, caption.substr(index, token.length)),
    +        domHelper.createTextNode(caption.substr(index + token.length))
    +      ]);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/combobox_test.html b/mapbox-gl/closure-library/closure/goog/ui/combobox_test.html
    new file mode 100644
    index 0000000000..92c109a36e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/combobox_test.html
    @@ -0,0 +1,48 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author gboyer@google.com (Garrett Boyer)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ComboBox
    +  </title>
    +  <style type="text/css">
    +   .goog-menu {
    +  position: absolute;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ComboBoxTest');
    +  </script>
    + </head>
    + <body>
    +  <h2 style="color:red">
    +   This test is rudimentary.
    +The fact that it passes should not (yet) make you too confident.
    +  </h2>
    +  <div id="combo">
    +  </div>
    +  <div id="menu">
    +   <div class="goog-combobox-item">
    +    Red
    +   </div>
    +   <div class="goog-combobox-item">
    +    Green
    +   </div>
    +   <div class="goog-combobox-item">
    +    Blue
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/combobox_test.js b/mapbox-gl/closure-library/closure/goog/ui/combobox_test.js
    new file mode 100644
    index 0000000000..3a3f654113
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/combobox_test.js
    @@ -0,0 +1,271 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ComboBoxTest');
    +goog.setTestOnly('goog.ui.ComboBoxTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ComboBox');
    +goog.require('goog.ui.ComboBoxItem');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.ui.LabelInput');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +
    +var comboBox;
    +var input;
    +
    +function setUp() {
    +  goog.dom.getElement('combo').innerHTML = '';
    +
    +  comboBox = new goog.ui.ComboBox();
    +  comboBox.setDefaultText('Select a color...');
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Red'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Maroon'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Gre<en'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Blue'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Royal Blue'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Yellow'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Magenta'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Mouve'));
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Grey'));
    +  comboBox.render(goog.dom.getElement('combo'));
    +
    +  input = comboBox.getInputElement();
    +}
    +
    +function tearDown() {
    +  comboBox.dispose();
    +}
    +
    +function testInputElementAttributes() {
    +  var comboBox = new goog.ui.ComboBox();
    +  comboBox.setFieldName('a_form_field');
    +  comboBox.createDom();
    +  var inputElement = comboBox.getInputElement();
    +  assertEquals('text', inputElement.type);
    +  assertEquals('a_form_field', inputElement.name);
    +  assertEquals('off', inputElement.autocomplete);
    +  comboBox.dispose();
    +}
    +
    +function testSetDefaultText() {
    +  assertEquals('Select a color...', comboBox.getDefaultText());
    +  comboBox.setDefaultText('new default text...');
    +  assertEquals('new default text...', comboBox.getDefaultText());
    +  assertEquals('new default text...', comboBox.labelInput_.getLabel());
    +}
    +
    +function testGetMenu() {
    +  assertTrue('Menu should be instance of goog.ui.Menu',
    +      comboBox.getMenu() instanceof goog.ui.Menu);
    +  assertEquals('Menu should have correct number of children',
    +      9, comboBox.getMenu().getChildCount());
    +}
    +
    +function testMenuBeginsInvisible() {
    +  assertFalse('Menu should begin invisible', comboBox.getMenu().isVisible());
    +}
    +
    +function testClickCausesPopup() {
    +  goog.testing.events.fireClickSequence(input);
    +  assertTrue('Menu becomes visible after click',
    +      comboBox.getMenu().isVisible());
    +}
    +
    +function testUpKeyCausesPopup() {
    +  goog.testing.events.fireKeySequence(input, goog.events.KeyCodes.UP);
    +  assertTrue('Menu becomes visible after UP key',
    +      comboBox.getMenu().isVisible());
    +}
    +
    +function testActionSelectsItem() {
    +  comboBox.getMenu().getItemAt(2).dispatchEvent(
    +      goog.ui.Component.EventType.ACTION);
    +  assertEquals('Gre<en', input.value);
    +}
    +
    +function testActionSelectsItemWithModel() {
    +  var itemWithModel = new goog.ui.MenuItem('one', 1);
    +  comboBox.addItem(itemWithModel);
    +  itemWithModel.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals('one', comboBox.getValue());
    +}
    +
    +function testRedisplayMenuAfterBackspace() {
    +  input.value = 'mx';
    +  comboBox.onInputEvent_();
    +  input.value = 'm';
    +  comboBox.onInputEvent_();
    +  assertEquals('Three items should be displayed',
    +      3, comboBox.getNumberOfVisibleItems_());
    +}
    +
    +function testExternallyCreatedMenu() {
    +  var menu = new goog.ui.Menu();
    +  menu.decorate(goog.dom.getElement('menu'));
    +  assertTrue('Menu items should be instances of goog.ui.ComboBoxItem',
    +      menu.getChildAt(0) instanceof goog.ui.ComboBoxItem);
    +
    +  comboBox = new goog.ui.ComboBox(null, menu);
    +  comboBox.render(goog.dom.getElement('combo'));
    +
    +  input = comboBox.getElement().getElementsByTagName(
    +      goog.dom.TagName.INPUT)[0];
    +  menu.getItemAt(2).dispatchEvent(
    +      goog.ui.Component.EventType.ACTION);
    +  assertEquals('Blue', input.value);
    +}
    +
    +function testRecomputeVisibleCountAfterChangingItems() {
    +  input.value = 'Black';
    +  comboBox.onInputEvent_();
    +  assertEquals('No items should be displayed',
    +      0, comboBox.getNumberOfVisibleItems_());
    +  comboBox.addItem(new goog.ui.ComboBoxItem('Black'));
    +  assertEquals('One item should be displayed',
    +      1, comboBox.getNumberOfVisibleItems_());
    +
    +  input.value = 'Red';
    +  comboBox.onInputEvent_();
    +  assertEquals('One item should be displayed',
    +      1, comboBox.getNumberOfVisibleItems_());
    +  comboBox.removeItemAt(0);  // Red
    +  assertEquals('No items should be displayed',
    +      0, comboBox.getNumberOfVisibleItems_());
    +}
    +
    +function testSetEnabled() {
    +  // By default, everything should be enabled.
    +  assertFalse('Text input should initially not be disabled', input.disabled);
    +  assertFalse('Text input should initially not look disabled',
    +      goog.dom.classlist.contains(input,
    +      goog.getCssName(goog.ui.LabelInput.prototype.labelCssClassName,
    +          'disabled')));
    +  assertFalse('Combo box should initially not look disabled',
    +      goog.dom.classlist.contains(comboBox.getElement(),
    +      goog.getCssName('goog-combobox-disabled')));
    +  goog.testing.events.fireClickSequence(comboBox.getElement());
    +  assertTrue('Menu initially becomes visible after click',
    +      comboBox.getMenu().isVisible());
    +  goog.testing.events.fireClickSequence(document);
    +  assertFalse('Menu initially becomes invisible after document click',
    +      comboBox.getMenu().isVisible());
    +
    +  assertTrue(comboBox.isEnabled());
    +  comboBox.setEnabled(false);
    +  assertFalse(comboBox.isEnabled());
    +  assertTrue('Text input should be disabled after being disabled',
    +      input.disabled);
    +  assertTrue('Text input should appear disabled after being disabled',
    +      goog.dom.classlist.contains(input,
    +      goog.getCssName(goog.ui.LabelInput.prototype.labelCssClassName,
    +          'disabled')));
    +  assertTrue('Combo box should appear disabled after being disabled',
    +      goog.dom.classlist.contains(comboBox.getElement(),
    +      goog.getCssName('goog-combobox-disabled')));
    +  goog.testing.events.fireClickSequence(comboBox.getElement());
    +  assertFalse('Menu should not become visible after click if disabled',
    +      comboBox.getMenu().isVisible());
    +
    +  comboBox.setEnabled(true);
    +  assertTrue(comboBox.isEnabled());
    +  assertFalse('Text input should not be disabled after being re-enabled',
    +      input.disabled);
    +  assertFalse('Text input should not appear disabled after being re-enabled',
    +      goog.dom.classlist.contains(input,
    +      goog.getCssName(goog.ui.LabelInput.prototype.labelCssClassName,
    +          'disabled')));
    +  assertFalse('Combo box should not appear disabled after being re-enabled',
    +      goog.dom.classlist.contains(comboBox.getElement(),
    +      goog.getCssName('goog-combobox-disabled')));
    +  goog.testing.events.fireClickSequence(comboBox.getElement());
    +  assertTrue('Menu becomes visible after click when re-enabled',
    +      comboBox.getMenu().isVisible());
    +  goog.testing.events.fireClickSequence(document);
    +  assertFalse('Menu becomes invisible after document click when re-enabled',
    +      comboBox.getMenu().isVisible());
    +}
    +
    +function testSetFormatFromToken() {
    +  var item = new goog.ui.ComboBoxItem('ABc');
    +  item.setFormatFromToken('b');
    +  var div = goog.dom.createDom('div');
    +  new goog.ui.ControlRenderer().setContent(div, item.getContent());
    +  assertTrue(div.innerHTML == 'A<b>B</b>c' || div.innerHTML == 'A<B>B</B>c');
    +}
    +
    +function testSetValue() {
    +  var clock = new goog.testing.MockClock(/* autoInstall */ true);
    +
    +  // Get the input focus. Note that both calls are needed to correctly
    +  // simulate the focus (and setting document.activeElement) across all
    +  // browsers.
    +  input.focus();
    +  goog.testing.events.fireClickSequence(input);
    +
    +  // Simulate text input.
    +  input.value = 'Black';
    +  comboBox.onInputEvent_();
    +  clock.tick();
    +  assertEquals('No items should be displayed',
    +      0, comboBox.getNumberOfVisibleItems_());
    +  assertFalse('Menu should be invisible', comboBox.getMenu().isVisible());
    +
    +  // Programmatic change with the input focus causes the menu visibility to
    +  // change if needed.
    +  comboBox.setValue('Blue');
    +  clock.tick();
    +  assertTrue('Menu should be visible1', comboBox.getMenu().isVisible());
    +  assertEquals('One item should be displayed',
    +      1, comboBox.getNumberOfVisibleItems_());
    +
    +  // Simulate user input to ensure all the items are invisible again, then
    +  // blur away.
    +  input.value = 'Black';
    +  comboBox.onInputEvent_();
    +  clock.tick();
    +  input.blur();
    +  document.body.focus();
    +  clock.tick(goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS);
    +  assertEquals('No items should be displayed',
    +      0, comboBox.getNumberOfVisibleItems_());
    +  assertFalse('Menu should be invisible', comboBox.getMenu().isVisible());
    +
    +  // Programmatic change without the input focus does not pop up the menu,
    +  // but still updates the list of visible items within it.
    +  comboBox.setValue('Blue');
    +  clock.tick();
    +  assertFalse('Menu should be invisible', comboBox.getMenu().isVisible());
    +  assertEquals('Menu should contain one item',
    +      1, comboBox.getNumberOfVisibleItems_());
    +
    +  // Click on the combobox. The entire menu becomes visible, the last item
    +  // (programmatically) set is highlighted.
    +  goog.testing.events.fireClickSequence(comboBox.getElement());
    +  assertTrue('Menu should be visible2', comboBox.getMenu().isVisible());
    +  assertEquals('All items should be displayed',
    +      comboBox.getMenu().getItemCount(), comboBox.getNumberOfVisibleItems_());
    +  assertEquals('The last item set should be highlighted',
    +      /* Blue= */ 3, comboBox.getMenu().getHighlightedIndex());
    +
    +  clock.uninstall();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/component.js b/mapbox-gl/closure-library/closure/goog/ui/component.js
    new file mode 100644
    index 0000000000..b8806fde44
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/component.js
    @@ -0,0 +1,1297 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Abstract class for all UI components. This defines the standard
    + * design pattern that all UI components should follow.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/samplecomponent.html
    + * @see http://code.google.com/p/closure-library/wiki/IntroToComponents
    + */
    +
    +goog.provide('goog.ui.Component');
    +goog.provide('goog.ui.Component.Error');
    +goog.provide('goog.ui.Component.EventType');
    +goog.provide('goog.ui.Component.State');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.ui.IdGenerator');
    +
    +
    +
    +/**
    + * Default implementation of UI component.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.Component = function(opt_domHelper) {
    +  goog.events.EventTarget.call(this);
    +  /**
    +   * DomHelper used to interact with the document, allowing components to be
    +   * created in a different window.
    +   * @protected {!goog.dom.DomHelper}
    +   * @suppress {underscore|visibility}
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Whether the component is rendered right-to-left.  Right-to-left is set
    +   * lazily when {@link #isRightToLeft} is called the first time, unless it has
    +   * been set by calling {@link #setRightToLeft} explicitly.
    +   * @private {?boolean}
    +   */
    +  this.rightToLeft_ = goog.ui.Component.defaultRightToLeft_;
    +
    +  /**
    +   * Unique ID of the component, lazily initialized in {@link
    +   * goog.ui.Component#getId} if needed.  This property is strictly private and
    +   * must not be accessed directly outside of this class!
    +   * @private {?string}
    +   */
    +  this.id_ = null;
    +
    +  /**
    +   * Whether the component is in the document.
    +   * @private {boolean}
    +   */
    +  this.inDocument_ = false;
    +
    +  // TODO(attila): Stop referring to this private field in subclasses.
    +  /**
    +   * The DOM element for the component.
    +   * @private {Element}
    +   */
    +  this.element_ = null;
    +
    +  /**
    +   * Event handler.
    +   * TODO(user): rename it to handler_ after all component subclasses in
    +   * inside Google have been cleaned up.
    +   * Code search: http://go/component_code_search
    +   * @private {goog.events.EventHandler|undefined}
    +   */
    +  this.googUiComponentHandler_ = void 0;
    +
    +  /**
    +   * Arbitrary data object associated with the component.  Such as meta-data.
    +   * @private {*}
    +   */
    +  this.model_ = null;
    +
    +  /**
    +   * Parent component to which events will be propagated.  This property is
    +   * strictly private and must not be accessed directly outside of this class!
    +   * @private {goog.ui.Component?}
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * Array of child components.  Lazily initialized on first use.  Must be kept
    +   * in sync with {@code childIndex_}.  This property is strictly private and
    +   * must not be accessed directly outside of this class!
    +   * @private {Array<goog.ui.Component>?}
    +   */
    +  this.children_ = null;
    +
    +  /**
    +   * Map of child component IDs to child components.  Used for constant-time
    +   * random access to child components by ID.  Lazily initialized on first use.
    +   * Must be kept in sync with {@code children_}.  This property is strictly
    +   * private and must not be accessed directly outside of this class!
    +   *
    +   * We use a plain Object, not a {@link goog.structs.Map}, for simplicity.
    +   * This means components can't have children with IDs such as 'constructor' or
    +   * 'valueOf', but this shouldn't really be an issue in practice, and if it is,
    +   * we can always fix it later without changing the API.
    +   *
    +   * @private {Object}
    +   */
    +  this.childIndex_ = null;
    +
    +  /**
    +   * Flag used to keep track of whether a component decorated an already
    +   * existing element or whether it created the DOM itself.
    +   *
    +   * If an element is decorated, dispose will leave the node in the document.
    +   * It is up to the app to remove the node.
    +   *
    +   * If an element was rendered, dispose will remove the node automatically.
    +   *
    +   * @private {boolean}
    +   */
    +  this.wasDecorated_ = false;
    +};
    +goog.inherits(goog.ui.Component, goog.events.EventTarget);
    +
    +
    +/**
    + * @define {boolean} Whether to support calling decorate with an element that is
    + *     not yet in the document. If true, we check if the element is in the
    + *     document, and avoid calling enterDocument if it isn't. If false, we
    + *     maintain legacy behavior (always call enterDocument from decorate).
    + */
    +goog.define('goog.ui.Component.ALLOW_DETACHED_DECORATION', false);
    +
    +
    +/**
    + * Generator for unique IDs.
    + * @type {goog.ui.IdGenerator}
    + * @private
    + */
    +goog.ui.Component.prototype.idGenerator_ = goog.ui.IdGenerator.getInstance();
    +
    +
    +// TODO(gboyer): See if we can remove this and just check goog.i18n.bidi.IS_RTL.
    +/**
    + * @define {number} Defines the default BIDI directionality.
    + *     0: Unknown.
    + *     1: Left-to-right.
    + *     -1: Right-to-left.
    + */
    +goog.define('goog.ui.Component.DEFAULT_BIDI_DIR', 0);
    +
    +
    +/**
    + * The default right to left value.
    + * @type {?boolean}
    + * @private
    + */
    +goog.ui.Component.defaultRightToLeft_ =
    +    (goog.ui.Component.DEFAULT_BIDI_DIR == 1) ? false :
    +    (goog.ui.Component.DEFAULT_BIDI_DIR == -1) ? true : null;
    +
    +
    +/**
    + * Common events fired by components so that event propagation is useful.  Not
    + * all components are expected to dispatch or listen for all event types.
    + * Events dispatched before a state transition should be cancelable to prevent
    + * the corresponding state change.
    + * @enum {string}
    + */
    +goog.ui.Component.EventType = {
    +  /** Dispatched before the component becomes visible. */
    +  BEFORE_SHOW: 'beforeshow',
    +
    +  /**
    +   * Dispatched after the component becomes visible.
    +   * NOTE(user): For goog.ui.Container, this actually fires before containers
    +   * are shown.  Use goog.ui.Container.EventType.AFTER_SHOW if you want an event
    +   * that fires after a goog.ui.Container is shown.
    +   */
    +  SHOW: 'show',
    +
    +  /** Dispatched before the component becomes hidden. */
    +  HIDE: 'hide',
    +
    +  /** Dispatched before the component becomes disabled. */
    +  DISABLE: 'disable',
    +
    +  /** Dispatched before the component becomes enabled. */
    +  ENABLE: 'enable',
    +
    +  /** Dispatched before the component becomes highlighted. */
    +  HIGHLIGHT: 'highlight',
    +
    +  /** Dispatched before the component becomes un-highlighted. */
    +  UNHIGHLIGHT: 'unhighlight',
    +
    +  /** Dispatched before the component becomes activated. */
    +  ACTIVATE: 'activate',
    +
    +  /** Dispatched before the component becomes deactivated. */
    +  DEACTIVATE: 'deactivate',
    +
    +  /** Dispatched before the component becomes selected. */
    +  SELECT: 'select',
    +
    +  /** Dispatched before the component becomes un-selected. */
    +  UNSELECT: 'unselect',
    +
    +  /** Dispatched before a component becomes checked. */
    +  CHECK: 'check',
    +
    +  /** Dispatched before a component becomes un-checked. */
    +  UNCHECK: 'uncheck',
    +
    +  /** Dispatched before a component becomes focused. */
    +  FOCUS: 'focus',
    +
    +  /** Dispatched before a component becomes blurred. */
    +  BLUR: 'blur',
    +
    +  /** Dispatched before a component is opened (expanded). */
    +  OPEN: 'open',
    +
    +  /** Dispatched before a component is closed (collapsed). */
    +  CLOSE: 'close',
    +
    +  /** Dispatched after a component is moused over. */
    +  ENTER: 'enter',
    +
    +  /** Dispatched after a component is moused out of. */
    +  LEAVE: 'leave',
    +
    +  /** Dispatched after the user activates the component. */
    +  ACTION: 'action',
    +
    +  /** Dispatched after the external-facing state of a component is changed. */
    +  CHANGE: 'change'
    +};
    +
    +
    +/**
    + * Errors thrown by the component.
    + * @enum {string}
    + */
    +goog.ui.Component.Error = {
    +  /**
    +   * Error when a method is not supported.
    +   */
    +  NOT_SUPPORTED: 'Method not supported',
    +
    +  /**
    +   * Error when the given element can not be decorated.
    +   */
    +  DECORATE_INVALID: 'Invalid element to decorate',
    +
    +  /**
    +   * Error when the component is already rendered and another render attempt is
    +   * made.
    +   */
    +  ALREADY_RENDERED: 'Component already rendered',
    +
    +  /**
    +   * Error when an attempt is made to set the parent of a component in a way
    +   * that would result in an inconsistent object graph.
    +   */
    +  PARENT_UNABLE_TO_BE_SET: 'Unable to set parent component',
    +
    +  /**
    +   * Error when an attempt is made to add a child component at an out-of-bounds
    +   * index.  We don't support sparse child arrays.
    +   */
    +  CHILD_INDEX_OUT_OF_BOUNDS: 'Child component index out of bounds',
    +
    +  /**
    +   * Error when an attempt is made to remove a child component from a component
    +   * other than its parent.
    +   */
    +  NOT_OUR_CHILD: 'Child is not in parent component',
    +
    +  /**
    +   * Error when an operation requiring DOM interaction is made when the
    +   * component is not in the document
    +   */
    +  NOT_IN_DOCUMENT: 'Operation not supported while component is not in document',
    +
    +  /**
    +   * Error when an invalid component state is encountered.
    +   */
    +  STATE_INVALID: 'Invalid component state'
    +};
    +
    +
    +/**
    + * Common component states.  Components may have distinct appearance depending
    + * on what state(s) apply to them.  Not all components are expected to support
    + * all states.
    + * @enum {number}
    + */
    +goog.ui.Component.State = {
    +  /**
    +   * Union of all supported component states.
    +   */
    +  ALL: 0xFF,
    +
    +  /**
    +   * Component is disabled.
    +   * @see goog.ui.Component.EventType.DISABLE
    +   * @see goog.ui.Component.EventType.ENABLE
    +   */
    +  DISABLED: 0x01,
    +
    +  /**
    +   * Component is highlighted.
    +   * @see goog.ui.Component.EventType.HIGHLIGHT
    +   * @see goog.ui.Component.EventType.UNHIGHLIGHT
    +   */
    +  HOVER: 0x02,
    +
    +  /**
    +   * Component is active (or "pressed").
    +   * @see goog.ui.Component.EventType.ACTIVATE
    +   * @see goog.ui.Component.EventType.DEACTIVATE
    +   */
    +  ACTIVE: 0x04,
    +
    +  /**
    +   * Component is selected.
    +   * @see goog.ui.Component.EventType.SELECT
    +   * @see goog.ui.Component.EventType.UNSELECT
    +   */
    +  SELECTED: 0x08,
    +
    +  /**
    +   * Component is checked.
    +   * @see goog.ui.Component.EventType.CHECK
    +   * @see goog.ui.Component.EventType.UNCHECK
    +   */
    +  CHECKED: 0x10,
    +
    +  /**
    +   * Component has focus.
    +   * @see goog.ui.Component.EventType.FOCUS
    +   * @see goog.ui.Component.EventType.BLUR
    +   */
    +  FOCUSED: 0x20,
    +
    +  /**
    +   * Component is opened (expanded).  Applies to tree nodes, menu buttons,
    +   * submenus, zippys (zippies?), etc.
    +   * @see goog.ui.Component.EventType.OPEN
    +   * @see goog.ui.Component.EventType.CLOSE
    +   */
    +  OPENED: 0x40
    +};
    +
    +
    +/**
    + * Static helper method; returns the type of event components are expected to
    + * dispatch when transitioning to or from the given state.
    + * @param {goog.ui.Component.State} state State to/from which the component
    + *     is transitioning.
    + * @param {boolean} isEntering Whether the component is entering or leaving the
    + *     state.
    + * @return {goog.ui.Component.EventType} Event type to dispatch.
    + */
    +goog.ui.Component.getStateTransitionEvent = function(state, isEntering) {
    +  switch (state) {
    +    case goog.ui.Component.State.DISABLED:
    +      return isEntering ? goog.ui.Component.EventType.DISABLE :
    +          goog.ui.Component.EventType.ENABLE;
    +    case goog.ui.Component.State.HOVER:
    +      return isEntering ? goog.ui.Component.EventType.HIGHLIGHT :
    +          goog.ui.Component.EventType.UNHIGHLIGHT;
    +    case goog.ui.Component.State.ACTIVE:
    +      return isEntering ? goog.ui.Component.EventType.ACTIVATE :
    +          goog.ui.Component.EventType.DEACTIVATE;
    +    case goog.ui.Component.State.SELECTED:
    +      return isEntering ? goog.ui.Component.EventType.SELECT :
    +          goog.ui.Component.EventType.UNSELECT;
    +    case goog.ui.Component.State.CHECKED:
    +      return isEntering ? goog.ui.Component.EventType.CHECK :
    +          goog.ui.Component.EventType.UNCHECK;
    +    case goog.ui.Component.State.FOCUSED:
    +      return isEntering ? goog.ui.Component.EventType.FOCUS :
    +          goog.ui.Component.EventType.BLUR;
    +    case goog.ui.Component.State.OPENED:
    +      return isEntering ? goog.ui.Component.EventType.OPEN :
    +          goog.ui.Component.EventType.CLOSE;
    +    default:
    +      // Fall through.
    +  }
    +
    +  // Invalid state.
    +  throw Error(goog.ui.Component.Error.STATE_INVALID);
    +};
    +
    +
    +/**
    + * Set the default right-to-left value. This causes all component's created from
    + * this point foward to have the given value. This is useful for cases where
    + * a given page is always in one directionality, avoiding unnecessary
    + * right to left determinations.
    + * @param {?boolean} rightToLeft Whether the components should be rendered
    + *     right-to-left. Null iff components should determine their directionality.
    + */
    +goog.ui.Component.setDefaultRightToLeft = function(rightToLeft) {
    +  goog.ui.Component.defaultRightToLeft_ = rightToLeft;
    +};
    +
    +
    +/**
    + * Gets the unique ID for the instance of this component.  If the instance
    + * doesn't already have an ID, generates one on the fly.
    + * @return {string} Unique component ID.
    + */
    +goog.ui.Component.prototype.getId = function() {
    +  return this.id_ || (this.id_ = this.idGenerator_.getNextUniqueId());
    +};
    +
    +
    +/**
    + * Assigns an ID to this component instance.  It is the caller's responsibility
    + * to guarantee that the ID is unique.  If the component is a child of a parent
    + * component, then the parent component's child index is updated to reflect the
    + * new ID; this may throw an error if the parent already has a child with an ID
    + * that conflicts with the new ID.
    + * @param {string} id Unique component ID.
    + */
    +goog.ui.Component.prototype.setId = function(id) {
    +  if (this.parent_ && this.parent_.childIndex_) {
    +    // Update the parent's child index.
    +    goog.object.remove(this.parent_.childIndex_, this.id_);
    +    goog.object.add(this.parent_.childIndex_, id, this);
    +  }
    +
    +  // Update the component ID.
    +  this.id_ = id;
    +};
    +
    +
    +/**
    + * Gets the component's element.
    + * @return {Element} The element for the component.
    + */
    +goog.ui.Component.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Gets the component's element. This differs from getElement in that
    + * it assumes that the element exists (i.e. the component has been
    + * rendered/decorated) and will cause an assertion error otherwise (if
    + * assertion is enabled).
    + * @return {!Element} The element for the component.
    + */
    +goog.ui.Component.prototype.getElementStrict = function() {
    +  var el = this.element_;
    +  goog.asserts.assert(
    +      el, 'Can not call getElementStrict before rendering/decorating.');
    +  return el;
    +};
    +
    +
    +/**
    + * Sets the component's root element to the given element.  Considered
    + * protected and final.
    + *
    + * This should generally only be called during createDom. Setting the element
    + * does not actually change which element is rendered, only the element that is
    + * associated with this UI component.
    + *
    + * This should only be used by subclasses and its associated renderers.
    + *
    + * @param {Element} element Root element for the component.
    + */
    +goog.ui.Component.prototype.setElementInternal = function(element) {
    +  this.element_ = element;
    +};
    +
    +
    +/**
    + * Returns an array of all the elements in this component's DOM with the
    + * provided className.
    + * @param {string} className The name of the class to look for.
    + * @return {!goog.array.ArrayLike} The items found with the class name provided.
    + */
    +goog.ui.Component.prototype.getElementsByClass = function(className) {
    +  return this.element_ ?
    +      this.dom_.getElementsByClass(className, this.element_) : [];
    +};
    +
    +
    +/**
    + * Returns the first element in this component's DOM with the provided
    + * className.
    + * @param {string} className The name of the class to look for.
    + * @return {Element} The first item with the class name provided.
    + */
    +goog.ui.Component.prototype.getElementByClass = function(className) {
    +  return this.element_ ?
    +      this.dom_.getElementByClass(className, this.element_) : null;
    +};
    +
    +
    +/**
    + * Similar to {@code getElementByClass} except that it expects the
    + * element to be present in the dom thus returning a required value. Otherwise,
    + * will assert.
    + * @param {string} className The name of the class to look for.
    + * @return {!Element} The first item with the class name provided.
    + */
    +goog.ui.Component.prototype.getRequiredElementByClass = function(className) {
    +  var el = this.getElementByClass(className);
    +  goog.asserts.assert(el, 'Expected element in component with class: %s',
    +      className);
    +  return el;
    +};
    +
    +
    +/**
    + * Returns the event handler for this component, lazily created the first time
    + * this method is called.
    + * @return {!goog.events.EventHandler<T>} Event handler for this component.
    + * @protected
    + * @this T
    + * @template T
    + */
    +goog.ui.Component.prototype.getHandler = function() {
    +  // TODO(user): templated "this" values currently result in "this" being
    +  // "unknown" in the body of the function.
    +  var self = /** @type {goog.ui.Component} */ (this);
    +  if (!self.googUiComponentHandler_) {
    +    self.googUiComponentHandler_ = new goog.events.EventHandler(self);
    +  }
    +  return self.googUiComponentHandler_;
    +};
    +
    +
    +/**
    + * Sets the parent of this component to use for event bubbling.  Throws an error
    + * if the component already has a parent or if an attempt is made to add a
    + * component to itself as a child.  Callers must use {@code removeChild}
    + * or {@code removeChildAt} to remove components from their containers before
    + * calling this method.
    + * @see goog.ui.Component#removeChild
    + * @see goog.ui.Component#removeChildAt
    + * @param {goog.ui.Component} parent The parent component.
    + */
    +goog.ui.Component.prototype.setParent = function(parent) {
    +  if (this == parent) {
    +    // Attempting to add a child to itself is an error.
    +    throw Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET);
    +  }
    +
    +  if (parent && this.parent_ && this.id_ && this.parent_.getChild(this.id_) &&
    +      this.parent_ != parent) {
    +    // This component is already the child of some parent, so it should be
    +    // removed using removeChild/removeChildAt first.
    +    throw Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET);
    +  }
    +
    +  this.parent_ = parent;
    +  goog.ui.Component.superClass_.setParentEventTarget.call(this, parent);
    +};
    +
    +
    +/**
    + * Returns the component's parent, if any.
    + * @return {goog.ui.Component?} The parent component.
    + */
    +goog.ui.Component.prototype.getParent = function() {
    +  return this.parent_;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.events.EventTarget#setParentEventTarget} to throw an
    + * error if the parent component is set, and the argument is not the parent.
    + * @override
    + */
    +goog.ui.Component.prototype.setParentEventTarget = function(parent) {
    +  if (this.parent_ && this.parent_ != parent) {
    +    throw Error(goog.ui.Component.Error.NOT_SUPPORTED);
    +  }
    +  goog.ui.Component.superClass_.setParentEventTarget.call(this, parent);
    +};
    +
    +
    +/**
    + * Returns the dom helper that is being used on this component.
    + * @return {!goog.dom.DomHelper} The dom helper used on this component.
    + */
    +goog.ui.Component.prototype.getDomHelper = function() {
    +  return this.dom_;
    +};
    +
    +
    +/**
    + * Determines whether the component has been added to the document.
    + * @return {boolean} TRUE if rendered. Otherwise, FALSE.
    + */
    +goog.ui.Component.prototype.isInDocument = function() {
    +  return this.inDocument_;
    +};
    +
    +
    +/**
    + * Creates the initial DOM representation for the component.  The default
    + * implementation is to set this.element_ = div.
    + */
    +goog.ui.Component.prototype.createDom = function() {
    +  this.element_ = this.dom_.createElement('div');
    +};
    +
    +
    +/**
    + * Renders the component.  If a parent element is supplied, the component's
    + * element will be appended to it.  If there is no optional parent element and
    + * the element doesn't have a parentNode then it will be appended to the
    + * document body.
    + *
    + * If this component has a parent component, and the parent component is
    + * not in the document already, then this will not call {@code enterDocument}
    + * on this component.
    + *
    + * Throws an Error if the component is already rendered.
    + *
    + * @param {Element=} opt_parentElement Optional parent element to render the
    + *    component into.
    + */
    +goog.ui.Component.prototype.render = function(opt_parentElement) {
    +  this.render_(opt_parentElement);
    +};
    +
    +
    +/**
    + * Renders the component before another element. The other element should be in
    + * the document already.
    + *
    + * Throws an Error if the component is already rendered.
    + *
    + * @param {Node} sibling Node to render the component before.
    + */
    +goog.ui.Component.prototype.renderBefore = function(sibling) {
    +  this.render_(/** @type {Element} */ (sibling.parentNode),
    +               sibling);
    +};
    +
    +
    +/**
    + * Renders the component.  If a parent element is supplied, the component's
    + * element will be appended to it.  If there is no optional parent element and
    + * the element doesn't have a parentNode then it will be appended to the
    + * document body.
    + *
    + * If this component has a parent component, and the parent component is
    + * not in the document already, then this will not call {@code enterDocument}
    + * on this component.
    + *
    + * Throws an Error if the component is already rendered.
    + *
    + * @param {Element=} opt_parentElement Optional parent element to render the
    + *    component into.
    + * @param {Node=} opt_beforeNode Node before which the component is to
    + *    be rendered.  If left out the node is appended to the parent element.
    + * @private
    + */
    +goog.ui.Component.prototype.render_ = function(opt_parentElement,
    +                                               opt_beforeNode) {
    +  if (this.inDocument_) {
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  if (!this.element_) {
    +    this.createDom();
    +  }
    +
    +  if (opt_parentElement) {
    +    opt_parentElement.insertBefore(this.element_, opt_beforeNode || null);
    +  } else {
    +    this.dom_.getDocument().body.appendChild(this.element_);
    +  }
    +
    +  // If this component has a parent component that isn't in the document yet,
    +  // we don't call enterDocument() here.  Instead, when the parent component
    +  // enters the document, the enterDocument() call will propagate to its
    +  // children, including this one.  If the component doesn't have a parent
    +  // or if the parent is already in the document, we call enterDocument().
    +  if (!this.parent_ || this.parent_.isInDocument()) {
    +    this.enterDocument();
    +  }
    +};
    +
    +
    +/**
    + * Decorates the element for the UI component. If the element is in the
    + * document, the enterDocument method will be called.
    + *
    + * If goog.ui.Component.ALLOW_DETACHED_DECORATION is false, the caller must
    + * pass an element that is in the document.
    + *
    + * @param {Element} element Element to decorate.
    + */
    +goog.ui.Component.prototype.decorate = function(element) {
    +  if (this.inDocument_) {
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  } else if (element && this.canDecorate(element)) {
    +    this.wasDecorated_ = true;
    +
    +    // Set the DOM helper of the component to match the decorated element.
    +    var doc = goog.dom.getOwnerDocument(element);
    +    if (!this.dom_ || this.dom_.getDocument() != doc) {
    +      this.dom_ = goog.dom.getDomHelper(element);
    +    }
    +
    +    // Call specific component decorate logic.
    +    this.decorateInternal(element);
    +
    +    // If supporting detached decoration, check that element is in doc.
    +    if (!goog.ui.Component.ALLOW_DETACHED_DECORATION ||
    +        goog.dom.contains(doc, element)) {
    +      this.enterDocument();
    +    }
    +  } else {
    +    throw Error(goog.ui.Component.Error.DECORATE_INVALID);
    +  }
    +};
    +
    +
    +/**
    + * Determines if a given element can be decorated by this type of component.
    + * This method should be overridden by inheriting objects.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} True if the element can be decorated, false otherwise.
    + */
    +goog.ui.Component.prototype.canDecorate = function(element) {
    +  return true;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the component was decorated.
    + */
    +goog.ui.Component.prototype.wasDecorated = function() {
    +  return this.wasDecorated_;
    +};
    +
    +
    +/**
    + * Actually decorates the element. Should be overridden by inheriting objects.
    + * This method can assume there are checks to ensure the component has not
    + * already been rendered have occurred and that enter document will be called
    + * afterwards. This method is considered protected.
    + * @param {Element} element Element to decorate.
    + * @protected
    + */
    +goog.ui.Component.prototype.decorateInternal = function(element) {
    +  this.element_ = element;
    +};
    +
    +
    +/**
    + * Called when the component's element is known to be in the document. Anything
    + * using document.getElementById etc. should be done at this stage.
    + *
    + * If the component contains child components, this call is propagated to its
    + * children.
    + */
    +goog.ui.Component.prototype.enterDocument = function() {
    +  this.inDocument_ = true;
    +
    +  // Propagate enterDocument to child components that have a DOM, if any.
    +  // If a child was decorated before entering the document (permitted when
    +  // goog.ui.Component.ALLOW_DETACHED_DECORATION is true), its enterDocument
    +  // will be called here.
    +  this.forEachChild(function(child) {
    +    if (!child.isInDocument() && child.getElement()) {
    +      child.enterDocument();
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Called by dispose to clean up the elements and listeners created by a
    + * component, or by a parent component/application who has removed the
    + * component from the document but wants to reuse it later.
    + *
    + * If the component contains child components, this call is propagated to its
    + * children.
    + *
    + * It should be possible for the component to be rendered again once this method
    + * has been called.
    + */
    +goog.ui.Component.prototype.exitDocument = function() {
    +  // Propagate exitDocument to child components that have been rendered, if any.
    +  this.forEachChild(function(child) {
    +    if (child.isInDocument()) {
    +      child.exitDocument();
    +    }
    +  });
    +
    +  if (this.googUiComponentHandler_) {
    +    this.googUiComponentHandler_.removeAll();
    +  }
    +
    +  this.inDocument_ = false;
    +};
    +
    +
    +/**
    + * Disposes of the component.  Calls {@code exitDocument}, which is expected to
    + * remove event handlers and clean up the component.  Propagates the call to
    + * the component's children, if any. Removes the component's DOM from the
    + * document unless it was decorated.
    + * @override
    + * @protected
    + */
    +goog.ui.Component.prototype.disposeInternal = function() {
    +  if (this.inDocument_) {
    +    this.exitDocument();
    +  }
    +
    +  if (this.googUiComponentHandler_) {
    +    this.googUiComponentHandler_.dispose();
    +    delete this.googUiComponentHandler_;
    +  }
    +
    +  // Disposes of the component's children, if any.
    +  this.forEachChild(function(child) {
    +    child.dispose();
    +  });
    +
    +  // Detach the component's element from the DOM, unless it was decorated.
    +  if (!this.wasDecorated_ && this.element_) {
    +    goog.dom.removeNode(this.element_);
    +  }
    +
    +  this.children_ = null;
    +  this.childIndex_ = null;
    +  this.element_ = null;
    +  this.model_ = null;
    +  this.parent_ = null;
    +
    +  goog.ui.Component.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Helper function for subclasses that gets a unique id for a given fragment,
    + * this can be used by components to generate unique string ids for DOM
    + * elements.
    + * @param {string} idFragment A partial id.
    + * @return {string} Unique element id.
    + */
    +goog.ui.Component.prototype.makeId = function(idFragment) {
    +  return this.getId() + '.' + idFragment;
    +};
    +
    +
    +/**
    + * Makes a collection of ids.  This is a convenience method for makeId.  The
    + * object's values are the id fragments and the new values are the generated
    + * ids.  The key will remain the same.
    + * @param {Object} object The object that will be used to create the ids.
    + * @return {!Object} An object of id keys to generated ids.
    + */
    +goog.ui.Component.prototype.makeIds = function(object) {
    +  var ids = {};
    +  for (var key in object) {
    +    ids[key] = this.makeId(object[key]);
    +  }
    +  return ids;
    +};
    +
    +
    +/**
    + * Returns the model associated with the UI component.
    + * @return {*} The model.
    + */
    +goog.ui.Component.prototype.getModel = function() {
    +  return this.model_;
    +};
    +
    +
    +/**
    + * Sets the model associated with the UI component.
    + * @param {*} obj The model.
    + */
    +goog.ui.Component.prototype.setModel = function(obj) {
    +  this.model_ = obj;
    +};
    +
    +
    +/**
    + * Helper function for returning the fragment portion of an id generated using
    + * makeId().
    + * @param {string} id Id generated with makeId().
    + * @return {string} Fragment.
    + */
    +goog.ui.Component.prototype.getFragmentFromId = function(id) {
    +  return id.substring(this.getId().length + 1);
    +};
    +
    +
    +/**
    + * Helper function for returning an element in the document with a unique id
    + * generated using makeId().
    + * @param {string} idFragment The partial id.
    + * @return {Element} The element with the unique id, or null if it cannot be
    + *     found.
    + */
    +goog.ui.Component.prototype.getElementByFragment = function(idFragment) {
    +  if (!this.inDocument_) {
    +    throw Error(goog.ui.Component.Error.NOT_IN_DOCUMENT);
    +  }
    +  return this.dom_.getElement(this.makeId(idFragment));
    +};
    +
    +
    +/**
    + * Adds the specified component as the last child of this component.  See
    + * {@link goog.ui.Component#addChildAt} for detailed semantics.
    + *
    + * @see goog.ui.Component#addChildAt
    + * @param {goog.ui.Component} child The new child component.
    + * @param {boolean=} opt_render If true, the child component will be rendered
    + *    into the parent.
    + */
    +goog.ui.Component.prototype.addChild = function(child, opt_render) {
    +  // TODO(gboyer): addChildAt(child, this.getChildCount(), false) will
    +  // reposition any already-rendered child to the end.  Instead, perhaps
    +  // addChild(child, false) should never reposition the child; instead, clients
    +  // that need the repositioning will use addChildAt explicitly.  Right now,
    +  // clients can get around this by calling addChild before calling decorate.
    +  this.addChildAt(child, this.getChildCount(), opt_render);
    +};
    +
    +
    +/**
    + * Adds the specified component as a child of this component at the given
    + * 0-based index.
    + *
    + * Both {@code addChild} and {@code addChildAt} assume the following contract
    + * between parent and child components:
    + *  <ul>
    + *    <li>the child component's element must be a descendant of the parent
    + *        component's element, and
    + *    <li>the DOM state of the child component must be consistent with the DOM
    + *        state of the parent component (see {@code isInDocument}) in the
    + *        steady state -- the exception is to addChildAt(child, i, false) and
    + *        then immediately decorate/render the child.
    + *  </ul>
    + *
    + * In particular, {@code parent.addChild(child)} will throw an error if the
    + * child component is already in the document, but the parent isn't.
    + *
    + * Clients of this API may call {@code addChild} and {@code addChildAt} with
    + * {@code opt_render} set to true.  If {@code opt_render} is true, calling these
    + * methods will automatically render the child component's element into the
    + * parent component's element. If the parent does not yet have an element, then
    + * {@code createDom} will automatically be invoked on the parent before
    + * rendering the child.
    + *
    + * Invoking {@code parent.addChild(child, true)} will throw an error if the
    + * child component is already in the document, regardless of the parent's DOM
    + * state.
    + *
    + * If {@code opt_render} is true and the parent component is not already
    + * in the document, {@code enterDocument} will not be called on this component
    + * at this point.
    + *
    + * Finally, this method also throws an error if the new child already has a
    + * different parent, or the given index is out of bounds.
    + *
    + * @see goog.ui.Component#addChild
    + * @param {goog.ui.Component} child The new child component.
    + * @param {number} index 0-based index at which the new child component is to be
    + *    added; must be between 0 and the current child count (inclusive).
    + * @param {boolean=} opt_render If true, the child component will be rendered
    + *    into the parent.
    + * @return {void} Nada.
    + */
    +goog.ui.Component.prototype.addChildAt = function(child, index, opt_render) {
    +  goog.asserts.assert(!!child, 'Provided element must not be null.');
    +
    +  if (child.inDocument_ && (opt_render || !this.inDocument_)) {
    +    // Adding a child that's already in the document is an error, except if the
    +    // parent is also in the document and opt_render is false (e.g. decorate()).
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  if (index < 0 || index > this.getChildCount()) {
    +    // Allowing sparse child arrays would lead to strange behavior, so we don't.
    +    throw Error(goog.ui.Component.Error.CHILD_INDEX_OUT_OF_BOUNDS);
    +  }
    +
    +  // Create the index and the child array on first use.
    +  if (!this.childIndex_ || !this.children_) {
    +    this.childIndex_ = {};
    +    this.children_ = [];
    +  }
    +
    +  // Moving child within component, remove old reference.
    +  if (child.getParent() == this) {
    +    goog.object.set(this.childIndex_, child.getId(), child);
    +    goog.array.remove(this.children_, child);
    +
    +  // Add the child to this component.  goog.object.add() throws an error if
    +  // a child with the same ID already exists.
    +  } else {
    +    goog.object.add(this.childIndex_, child.getId(), child);
    +  }
    +
    +  // Set the parent of the child to this component.  This throws an error if
    +  // the child is already contained by another component.
    +  child.setParent(this);
    +  goog.array.insertAt(this.children_, child, index);
    +
    +  if (child.inDocument_ && this.inDocument_ && child.getParent() == this) {
    +    // Changing the position of an existing child, move the DOM node (if
    +    // necessary).
    +    var contentElement = this.getContentElement();
    +    var insertBeforeElement = contentElement.childNodes[index] || null;
    +    if (insertBeforeElement != child.getElement()) {
    +      contentElement.insertBefore(child.getElement(), insertBeforeElement);
    +    }
    +  } else if (opt_render) {
    +    // If this (parent) component doesn't have a DOM yet, call createDom now
    +    // to make sure we render the child component's element into the correct
    +    // parent element (otherwise render_ with a null first argument would
    +    // render the child into the document body, which is almost certainly not
    +    // what we want).
    +    if (!this.element_) {
    +      this.createDom();
    +    }
    +    // Render the child into the parent at the appropriate location.  Note that
    +    // getChildAt(index + 1) returns undefined if inserting at the end.
    +    // TODO(attila): We should have a renderer with a renderChildAt API.
    +    var sibling = this.getChildAt(index + 1);
    +    // render_() calls enterDocument() if the parent is already in the document.
    +    child.render_(this.getContentElement(), sibling ? sibling.element_ : null);
    +  } else if (this.inDocument_ && !child.inDocument_ && child.element_ &&
    +      child.element_.parentNode &&
    +      // Under some circumstances, IE8 implicitly creates a Document Fragment
    +      // for detached nodes, so ensure the parent is an Element as it should be.
    +      child.element_.parentNode.nodeType == goog.dom.NodeType.ELEMENT) {
    +    // We don't touch the DOM, but if the parent is in the document, and the
    +    // child element is in the document but not marked as such, then we call
    +    // enterDocument on the child.
    +    // TODO(gboyer): It would be nice to move this condition entirely, but
    +    // there's a large risk of breaking existing applications that manually
    +    // append the child to the DOM and then call addChild.
    +    child.enterDocument();
    +  }
    +};
    +
    +
    +/**
    + * Returns the DOM element into which child components are to be rendered,
    + * or null if the component itself hasn't been rendered yet.  This default
    + * implementation returns the component's root element.  Subclasses with
    + * complex DOM structures must override this method.
    + * @return {Element} Element to contain child elements (null if none).
    + */
    +goog.ui.Component.prototype.getContentElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Returns true if the component is rendered right-to-left, false otherwise.
    + * The first time this function is invoked, the right-to-left rendering property
    + * is set if it has not been already.
    + * @return {boolean} Whether the control is rendered right-to-left.
    + */
    +goog.ui.Component.prototype.isRightToLeft = function() {
    +  if (this.rightToLeft_ == null) {
    +    this.rightToLeft_ = goog.style.isRightToLeft(this.inDocument_ ?
    +        this.element_ : this.dom_.getDocument().body);
    +  }
    +  return /** @type {boolean} */(this.rightToLeft_);
    +};
    +
    +
    +/**
    + * Set is right-to-left. This function should be used if the component needs
    + * to know the rendering direction during dom creation (i.e. before
    + * {@link #enterDocument} is called and is right-to-left is set).
    + * @param {boolean} rightToLeft Whether the component is rendered
    + *     right-to-left.
    + */
    +goog.ui.Component.prototype.setRightToLeft = function(rightToLeft) {
    +  if (this.inDocument_) {
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +  this.rightToLeft_ = rightToLeft;
    +};
    +
    +
    +/**
    + * Returns true if the component has children.
    + * @return {boolean} True if the component has children.
    + */
    +goog.ui.Component.prototype.hasChildren = function() {
    +  return !!this.children_ && this.children_.length != 0;
    +};
    +
    +
    +/**
    + * Returns the number of children of this component.
    + * @return {number} The number of children.
    + */
    +goog.ui.Component.prototype.getChildCount = function() {
    +  return this.children_ ? this.children_.length : 0;
    +};
    +
    +
    +/**
    + * Returns an array containing the IDs of the children of this component, or an
    + * empty array if the component has no children.
    + * @return {!Array<string>} Child component IDs.
    + */
    +goog.ui.Component.prototype.getChildIds = function() {
    +  var ids = [];
    +
    +  // We don't use goog.object.getKeys(this.childIndex_) because we want to
    +  // return the IDs in the correct order as determined by this.children_.
    +  this.forEachChild(function(child) {
    +    // addChild()/addChildAt() guarantee that the child array isn't sparse.
    +    ids.push(child.getId());
    +  });
    +
    +  return ids;
    +};
    +
    +
    +/**
    + * Returns the child with the given ID, or null if no such child exists.
    + * @param {string} id Child component ID.
    + * @return {goog.ui.Component?} The child with the given ID; null if none.
    + */
    +goog.ui.Component.prototype.getChild = function(id) {
    +  // Use childIndex_ for O(1) access by ID.
    +  return (this.childIndex_ && id) ? /** @type {goog.ui.Component} */ (
    +      goog.object.get(this.childIndex_, id)) || null : null;
    +};
    +
    +
    +/**
    + * Returns the child at the given index, or null if the index is out of bounds.
    + * @param {number} index 0-based index.
    + * @return {goog.ui.Component?} The child at the given index; null if none.
    + */
    +goog.ui.Component.prototype.getChildAt = function(index) {
    +  // Use children_ for access by index.
    +  return this.children_ ? this.children_[index] || null : null;
    +};
    +
    +
    +/**
    + * Calls the given function on each of this component's children in order.  If
    + * {@code opt_obj} is provided, it will be used as the 'this' object in the
    + * function when called.  The function should take two arguments:  the child
    + * component and its 0-based index.  The return value is ignored.
    + * @param {function(this:T,?,number):?} f The function to call for every
    + * child component; should take 2 arguments (the child and its index).
    + * @param {T=} opt_obj Used as the 'this' object in f when called.
    + * @template T
    + */
    +goog.ui.Component.prototype.forEachChild = function(f, opt_obj) {
    +  if (this.children_) {
    +    goog.array.forEach(this.children_, f, opt_obj);
    +  }
    +};
    +
    +
    +/**
    + * Returns the 0-based index of the given child component, or -1 if no such
    + * child is found.
    + * @param {goog.ui.Component?} child The child component.
    + * @return {number} 0-based index of the child component; -1 if not found.
    + */
    +goog.ui.Component.prototype.indexOfChild = function(child) {
    +  return (this.children_ && child) ? goog.array.indexOf(this.children_, child) :
    +      -1;
    +};
    +
    +
    +/**
    + * Removes the given child from this component, and returns it.  Throws an error
    + * if the argument is invalid or if the specified child isn't found in the
    + * parent component.  The argument can either be a string (interpreted as the
    + * ID of the child component to remove) or the child component itself.
    + *
    + * If {@code opt_unrender} is true, calls {@link goog.ui.component#exitDocument}
    + * on the removed child, and subsequently detaches the child's DOM from the
    + * document.  Otherwise it is the caller's responsibility to clean up the child
    + * component's DOM.
    + *
    + * @see goog.ui.Component#removeChildAt
    + * @param {string|goog.ui.Component|null} child The ID of the child to remove,
    + *    or the child component itself.
    + * @param {boolean=} opt_unrender If true, calls {@code exitDocument} on the
    + *    removed child component, and detaches its DOM from the document.
    + * @return {goog.ui.Component} The removed component, if any.
    + */
    +goog.ui.Component.prototype.removeChild = function(child, opt_unrender) {
    +  if (child) {
    +    // Normalize child to be the object and id to be the ID string.  This also
    +    // ensures that the child is really ours.
    +    var id = goog.isString(child) ? child : child.getId();
    +    child = this.getChild(id);
    +
    +    if (id && child) {
    +      goog.object.remove(this.childIndex_, id);
    +      goog.array.remove(this.children_, child);
    +
    +      if (opt_unrender) {
    +        // Remove the child component's DOM from the document.  We have to call
    +        // exitDocument first (see documentation).
    +        child.exitDocument();
    +        if (child.element_) {
    +          goog.dom.removeNode(child.element_);
    +        }
    +      }
    +
    +      // Child's parent must be set to null after exitDocument is called
    +      // so that the child can unlisten to its parent if required.
    +      child.setParent(null);
    +    }
    +  }
    +
    +  if (!child) {
    +    throw Error(goog.ui.Component.Error.NOT_OUR_CHILD);
    +  }
    +
    +  return /** @type {!goog.ui.Component} */(child);
    +};
    +
    +
    +/**
    + * Removes the child at the given index from this component, and returns it.
    + * Throws an error if the argument is out of bounds, or if the specified child
    + * isn't found in the parent.  See {@link goog.ui.Component#removeChild} for
    + * detailed semantics.
    + *
    + * @see goog.ui.Component#removeChild
    + * @param {number} index 0-based index of the child to remove.
    + * @param {boolean=} opt_unrender If true, calls {@code exitDocument} on the
    + *    removed child component, and detaches its DOM from the document.
    + * @return {goog.ui.Component} The removed component, if any.
    + */
    +goog.ui.Component.prototype.removeChildAt = function(index, opt_unrender) {
    +  // removeChild(null) will throw error.
    +  return this.removeChild(this.getChildAt(index), opt_unrender);
    +};
    +
    +
    +/**
    + * Removes every child component attached to this one and returns them.
    + *
    + * @see goog.ui.Component#removeChild
    + * @param {boolean=} opt_unrender If true, calls {@link #exitDocument} on the
    + *    removed child components, and detaches their DOM from the document.
    + * @return {!Array<goog.ui.Component>} The removed components if any.
    + */
    +goog.ui.Component.prototype.removeChildren = function(opt_unrender) {
    +  var removedChildren = [];
    +  while (this.hasChildren()) {
    +    removedChildren.push(this.removeChildAt(0, opt_unrender));
    +  }
    +  return removedChildren;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/component_test.html b/mapbox-gl/closure-library/closure/goog/ui/component_test.html
    new file mode 100644
    index 0000000000..3c4923a2c9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/component_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Component
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ComponentTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/component_test.js b/mapbox-gl/closure-library/closure/goog/ui/component_test.js
    new file mode 100644
    index 0000000000..405779e642
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/component_test.js
    @@ -0,0 +1,892 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ComponentTest');
    +goog.setTestOnly('goog.ui.ComponentTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +
    +var component;
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +var sandbox;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  component = new goog.ui.Component();
    +}
    +
    +function tearDown() {
    +  component.dispose();
    +  goog.dom.removeChildren(sandbox);
    +  propertyReplacer.reset();
    +}
    +
    +function testConstructor() {
    +  assertTrue('Instance must be non-null and have the expected class',
    +      component instanceof goog.ui.Component);
    +  assertTrue('DOM helper must be non-null and have the expected class',
    +      component.dom_ instanceof goog.dom.DomHelper);
    +
    +  var fakeDom = {};
    +  var otherComponent = new goog.ui.Component(fakeDom);
    +  assertEquals('DOM helper must refer to expected object', fakeDom,
    +      otherComponent.dom_);
    +
    +  otherComponent.dispose();
    +}
    +
    +function testGetId() {
    +  assertNull('Component ID should be initialized to null', component.id_);
    +  var id = component.getId();
    +  assertNotNull('Component ID should be generated on demand', id);
    +  assertEquals('Subsequent calls to getId() must return same value', id,
    +      component.getId());
    +}
    +
    +function testSetId() {
    +  component.setId('myId');
    +  assertEquals('getId() must return explicitly set ID', 'myId',
    +      component.getId());
    +
    +  var child = new goog.ui.Component();
    +  var childId = child.getId();
    +  component.addChild(child);
    +  assertEquals('Parent component must find child by ID', child,
    +      component.getChild(childId));
    +
    +  child.setId('someNewId');
    +  assertEquals('Parent component must find child by new ID', child,
    +      component.getChild('someNewId'));
    +
    +  child.dispose();
    +}
    +
    +function testGetSetElement() {
    +  assertNull('Element must be null by default', component.getElement());
    +  var element = goog.dom.createElement(goog.dom.TagName.DIV);
    +  component.setElementInternal(element);
    +  assertEquals('getElement() must return expected element', element,
    +      component.getElement());
    +}
    +
    +function testGetSetParent() {
    +  assertNull('Parent must be null by default', component.getParent());
    +
    +  var parent = new goog.ui.Component();
    +  component.setParent(parent);
    +  assertEquals('getParent() must return expected component', parent,
    +      component.getParent());
    +
    +  component.setParent(null);
    +  assertNull('Parent must be null', component.getParent());
    +
    +  assertThrows('Setting a component\'s parent to itself must throw error',
    +      function() {
    +        component.setParent(component);
    +      });
    +
    +  parent.addChild(component);
    +  assertEquals('getParent() must return expected component', parent,
    +      component.getParent());
    +  assertThrows('Changing a child component\'s parent must throw error',
    +      function() {
    +        component.setParent(new goog.ui.Component());
    +      });
    +
    +  parent.dispose();
    +}
    +
    +function testGetParentEventTarget() {
    +  assertNull('Parent event target must be null by default',
    +      component.getParentEventTarget());
    +
    +  var parent = new goog.ui.Component();
    +  component.setParent(parent);
    +  assertEquals('Parent event target must be the parent component', parent,
    +      component.getParentEventTarget());
    +  assertThrows('Directly setting the parent event target to other than ' +
    +      'the parent component when the parent component is set must throw ' +
    +      'error',
    +      function() {
    +        component.setParentEventTarget(new goog.ui.Component());
    +      });
    +
    +  parent.dispose();
    +}
    +
    +function testSetParentEventTarget() {
    +  var parentEventTarget = new goog.events.EventTarget();
    +  component.setParentEventTarget(parentEventTarget);
    +  assertEquals('Parent component must be null', null,
    +      component.getParent());
    +
    +  parentEventTarget.dispose();
    +}
    +
    +function testGetDomHelper() {
    +  var domHelper = new goog.dom.DomHelper();
    +  var component = new goog.ui.Component(domHelper);
    +  assertEquals('Component must return the same DomHelper passed', domHelper,
    +      component.getDomHelper());
    +}
    +
    +function testIsInDocument() {
    +  assertFalse('Component must not be in the document by default',
    +      component.isInDocument());
    +  component.enterDocument();
    +  assertTrue('Component must be in the document', component.isInDocument());
    +}
    +
    +function testCreateDom() {
    +  assertNull('Component must not have DOM by default',
    +      component.getElement());
    +  component.createDom();
    +  assertEquals('Component\'s DOM must be an element node',
    +      goog.dom.NodeType.ELEMENT, component.getElement().nodeType);
    +}
    +
    +function testRender() {
    +  assertFalse('Component must not be in the document by default',
    +      component.isInDocument());
    +  assertNull('Component must not have DOM by default',
    +      component.getElement());
    +  assertFalse('wasDecorated() must be false before component is rendered',
    +      component.wasDecorated());
    +
    +  component.render(sandbox);
    +  assertTrue('Rendered component must be in the document',
    +      component.isInDocument());
    +  assertEquals('Component\'s element must be a child of the parent element',
    +      sandbox, component.getElement().parentNode);
    +  assertFalse('wasDecorated() must still be false for rendered component',
    +      component.wasDecorated());
    +
    +  assertThrows('Trying to re-render component must throw error',
    +      function() {
    +        component.render();
    +      });
    +}
    +
    +function testRender_NoParent() {
    +  component.render();
    +  assertTrue('Rendered component must be in the document',
    +      component.isInDocument());
    +  assertEquals('Component\'s element must be a child of the document body',
    +      document.body, component.getElement().parentNode);
    +}
    +
    +function testRender_ParentNotInDocument() {
    +  var parent = new goog.ui.Component();
    +  component.setParent(parent);
    +
    +  assertFalse('Parent component must not be in the document',
    +      parent.isInDocument());
    +  assertFalse('Child component must not be in the document',
    +      component.isInDocument());
    +  assertNull('Child component must not have DOM', component.getElement());
    +
    +  component.render();
    +  assertFalse('Parent component must not be in the document',
    +      parent.isInDocument());
    +  assertFalse('Child component must not be in the document',
    +      component.isInDocument());
    +  assertNotNull('Child component must have DOM', component.getElement());
    +
    +  parent.dispose();
    +}
    +
    +
    +function testRenderBefore() {
    +  var sibling = goog.dom.createElement(goog.dom.TagName.DIV);
    +  sandbox.appendChild(sibling);
    +
    +  component.renderBefore(sibling);
    +  assertTrue('Rendered component must be in the document',
    +      component.isInDocument());
    +  assertEquals('Component\'s element must be a child of the parent element',
    +      sandbox, component.getElement().parentNode);
    +  assertEquals('Component\'s element must have expected nextSibling',
    +      sibling, component.getElement().nextSibling);
    +}
    +
    +
    +function testRenderChild() {
    +  var parent = new goog.ui.Component();
    +
    +  parent.createDom();
    +  assertFalse('Parent must not be in the document', parent.isInDocument());
    +  assertNotNull('Parent must have a DOM', parent.getElement());
    +
    +  parent.addChild(component);
    +  assertFalse('Child must not be in the document',
    +      component.isInDocument());
    +  assertNull('Child must not have a DOM', component.getElement());
    +
    +  component.render(parent.getElement());
    +  assertFalse('Parent must not be in the document', parent.isInDocument());
    +  assertFalse('Child must not be in the document if the parent isn\'t',
    +      component.isInDocument());
    +  assertNotNull('Child must have a DOM', component.getElement());
    +  assertEquals('Child\'s element must be a child of the parent\'s element',
    +      parent.getElement(), component.getElement().parentNode);
    +
    +  parent.render(sandbox);
    +  assertTrue('Parent must be in the document', parent.isInDocument());
    +  assertTrue('Child must be in the document', component.isInDocument());
    +
    +  parent.dispose();
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  assertFalse('wasDecorated() must be false by default',
    +      component.wasDecorated());
    +
    +  component.decorate(foo);
    +  assertTrue('Component must be in the document', component.isInDocument());
    +  assertEquals('Component\'s element must be the decorated element', foo,
    +      component.getElement());
    +  assertTrue('wasDecorated() must be true for decorated component',
    +      component.wasDecorated());
    +
    +  assertThrows('Trying to decorate with a control already in the document' +
    +      ' must throw error',
    +      function() {
    +        component.decorate(foo);
    +      });
    +}
    +
    +function testDecorate_AllowDetached_NotInDocument() {
    +  goog.ui.Component.ALLOW_DETACHED_DECORATION = true;
    +  var element = document.createElement('div');
    +  component.decorate(element);
    +  assertFalse('Component should not call enterDocument when decorated ' +
    +      'with an element that is not in the document.',
    +      component.isInDocument());
    +  goog.ui.Component.ALLOW_DETACHED_DECORATION = false;
    +}
    +
    +function testDecorate_AllowDetached_InDocument() {
    +  goog.ui.Component.ALLOW_DETACHED_DECORATION = true;
    +  var element = document.createElement('div');
    +  sandbox.appendChild(element);
    +  component.decorate(element);
    +  assertTrue('Component should call enterDocument when decorated ' +
    +      'with an element that is in the document.',
    +      component.isInDocument());
    +  goog.ui.Component.ALLOW_DETACHED_DECORATION = false;
    +}
    +
    +function testCannotDecorate() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  // Have canDecorate() return false.
    +  propertyReplacer.set(component, 'canDecorate', function() {
    +    return false;
    +  });
    +
    +  assertThrows('Trying to decorate an element for which canDecorate()' +
    +      ' returns false must throw error',
    +      function() {
    +        component.decorate(foo);
    +      });
    +}
    +
    +function testCanDecorate() {
    +  assertTrue('canDecorate() must return true by default',
    +      component.canDecorate(sandbox));
    +}
    +
    +function testWasDecorated() {
    +  assertFalse('wasDecorated() must return false by default',
    +      component.wasDecorated());
    +}
    +
    +function testDecorateInternal() {
    +  assertNull('Element must be null by default', component.getElement());
    +  var element = goog.dom.createElement(goog.dom.TagName.DIV);
    +  component.decorateInternal(element);
    +  assertEquals('Element must have expected value', element,
    +      component.getElement());
    +}
    +
    +function testGetElementAndGetElementsByClass() {
    +  sandbox.innerHTML =
    +      '<ul id="task-list">' +
    +      '<li class="task">Unclog drain' +
    +      '</ul>' +
    +      '<ul id="completed-tasks">' +
    +      '<li id="groceries" class="task">Buy groceries' +
    +      '<li class="task">Rotate tires' +
    +      '<li class="task">Clean kitchen' +
    +      '</ul>' +
    +      assertNull(
    +      'Should be nothing to return before the component has a DOM',
    +      component.getElementByClass('task'));
    +  assertEquals('Should return an empty list before the component has a DOM',
    +               0,
    +               component.getElementsByClass('task').length);
    +
    +  component.decorate(goog.dom.getElement('completed-tasks'));
    +  assertEquals(
    +      'getElementByClass() should return the first completed task',
    +      'groceries',
    +      component.getElementByClass('task').id);
    +  assertEquals(
    +      'getElementsByClass() should return only the completed tasks',
    +      3,
    +      component.getElementsByClass('task').length);
    +}
    +
    +function testGetRequiredElementByClass() {
    +  sandbox.innerHTML =
    +      '<ul id="task-list">' +
    +      '<li class="task">Unclog drain' +
    +      '</ul>' +
    +      '<ul id="completed-tasks">' +
    +      '<li id="groceries" class="task">Buy groceries' +
    +      '<li class="task">Rotate tires' +
    +      '<li class="task">Clean kitchen' +
    +      '</ul>';
    +  component.decorate(goog.dom.getElement('completed-tasks'));
    +  assertEquals(
    +      'getRequiredElementByClass() should return the first completed task',
    +      'groceries',
    +      component.getRequiredElementByClass('task').id);
    +  assertThrows('Attempting to retrieve a required element that does not' +
    +      'exist should fail', function() {
    +        component.getRequiredElementByClass('undefinedClass');
    +      });
    +}
    +
    +function testEnterExitDocument() {
    +  var c1 = new goog.ui.Component();
    +  var c2 = new goog.ui.Component();
    +
    +  component.addChild(c1);
    +  component.addChild(c2);
    +
    +  component.createDom();
    +  c1.createDom();
    +  c2.createDom();
    +
    +  assertFalse('Parent must not be in the document',
    +      component.isInDocument());
    +  assertFalse('Neither child must be in the document',
    +      c1.isInDocument() || c2.isInDocument());
    +
    +  component.enterDocument();
    +  assertTrue('Parent must be in the document', component.isInDocument());
    +  assertTrue('Both children must be in the document',
    +      c1.isInDocument() && c2.isInDocument());
    +
    +  component.exitDocument();
    +  assertFalse('Parent must not be in the document',
    +      component.isInDocument());
    +  assertFalse('Neither child must be in the document',
    +      c1.isInDocument() || c2.isInDocument());
    +
    +  c1.dispose();
    +  c2.dispose();
    +}
    +
    +function testDispose() {
    +  var c1, c2;
    +
    +  component.createDom();
    +  component.addChild((c1 = new goog.ui.Component()), true);
    +  component.addChild((c2 = new goog.ui.Component()), true);
    +
    +  var element = component.getElement();
    +  var c1Element = c1.getElement();
    +  var c2Element = c2.getElement();
    +
    +  component.render(sandbox);
    +  assertTrue('Parent must be in the document', component.isInDocument());
    +  assertEquals('Parent\'s element must be a child of the sandbox element',
    +      sandbox, element.parentNode);
    +  assertTrue('Both children must be in the document',
    +      c1.isInDocument() && c2.isInDocument());
    +  assertEquals('First child\'s element must be a child of the parent\'s' +
    +      ' element', element, c1Element.parentNode);
    +  assertEquals('Second child\'s element must be a child of the parent\'s' +
    +      ' element', element, c2Element.parentNode);
    +
    +  assertFalse('Parent must not have been disposed of',
    +      component.isDisposed());
    +  assertFalse('Neither child must have been disposed of',
    +      c1.isDisposed() || c2.isDisposed());
    +
    +  component.dispose();
    +  assertTrue('Parent must have been disposed of', component.isDisposed());
    +  assertFalse('Parent must not be in the document',
    +      component.isInDocument());
    +  assertNotEquals('Parent\'s element must no longer be a child of the' +
    +      ' sandbox element', sandbox, element.parentNode);
    +  assertTrue('Both children must have been disposed of',
    +      c1.isDisposed() && c2.isDisposed());
    +  assertFalse('Neither child must be in the document',
    +      c1.isInDocument() || c2.isInDocument());
    +  assertNotEquals('First child\'s element must no longer be a child of' +
    +      ' the parent\'s element', element, c1Element.parentNode);
    +  assertNotEquals('Second child\'s element must no longer be a child of' +
    +      ' the parent\'s element', element, c2Element.parentNode);
    +}
    +
    +function testDispose_Decorated() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  component.decorate(foo);
    +  assertTrue('Component must be in the document', component.isInDocument());
    +  assertFalse('Component must not have been disposed of',
    +      component.isDisposed());
    +  assertEquals('Component\'s element must have expected value', foo,
    +      component.getElement());
    +  assertEquals('Decorated element must be a child of the sandbox', sandbox,
    +      foo.parentNode);
    +
    +  component.dispose();
    +  assertFalse('Component must not be in the document',
    +      component.isInDocument());
    +  assertTrue('Component must have been disposed of',
    +      component.isDisposed());
    +  assertNull('Component\'s element must be null', component.getElement());
    +  assertEquals('Previously decorated element must still be a child of the' +
    +      ' sandbox', sandbox, foo.parentNode);
    +}
    +
    +function testMakeIdAndGetFragmentFromId() {
    +  assertEquals('Unique id must have expected value',
    +      component.getId() + '.foo', component.makeId('foo'));
    +  assertEquals('Fragment must have expected value', 'foo',
    +      component.getFragmentFromId(component.makeId('foo')));
    +}
    +
    +function testMakeIdsWithObject() {
    +  var EnumDef = {
    +    ENUM_1: 'enum 1',
    +    ENUM_2: 'enum 2',
    +    ENUM_3: 'enum 3'
    +  };
    +  var ids = component.makeIds(EnumDef);
    +  assertEquals(component.makeId(EnumDef.ENUM_1), ids.ENUM_1);
    +  assertEquals(component.makeId(EnumDef.ENUM_2), ids.ENUM_2);
    +  assertEquals(component.makeId(EnumDef.ENUM_3), ids.ENUM_3);
    +}
    +
    +function testGetElementByFragment() {
    +  component.render(sandbox);
    +
    +  var element = component.dom_.createDom('DIV', {
    +    id: component.makeId('foo')
    +  }, 'Hello');
    +  sandbox.appendChild(element);
    +
    +  assertEquals('Element must have expected value', element,
    +      component.getElementByFragment('foo'));
    +}
    +
    +function testGetSetModel() {
    +  assertNull('Model must be null by default', component.getModel());
    +
    +  var model = 'someModel';
    +  component.setModel(model);
    +  assertEquals('Model must have expected value', model,
    +      component.getModel());
    +
    +  component.setModel(null);
    +  assertNull('Model must be null', component.getModel());
    +}
    +
    +function testAddChild() {
    +  var child = new goog.ui.Component();
    +  child.setId('child');
    +
    +  assertFalse('Parent must not be in the document',
    +      component.isInDocument());
    +
    +  component.addChild(child);
    +  assertTrue('Parent must have children.', component.hasChildren());
    +  assertEquals('Child must have expected parent', component,
    +      child.getParent());
    +  assertEquals('Parent must find child by ID', child,
    +      component.getChild('child'));
    +}
    +
    +function testAddChild_Render() {
    +  var child = new goog.ui.Component();
    +
    +  component.render(sandbox);
    +  assertTrue('Parent must be in the document', component.isInDocument());
    +  assertEquals('Parent must be in the sandbox', sandbox,
    +      component.getElement().parentNode);
    +
    +  component.addChild(child, true);
    +  assertTrue('Child must be in the document', child.isInDocument());
    +  assertEquals('Child element must be a child of the parent element',
    +      component.getElement(), child.getElement().parentNode);
    +}
    +
    +function testAddChild_DomOnly() {
    +  var child = new goog.ui.Component();
    +
    +  component.createDom();
    +  assertNotNull('Parent must have a DOM', component.getElement());
    +  assertFalse('Parent must not be in the document',
    +      component.isInDocument());
    +
    +  component.addChild(child, true);
    +  assertNotNull('Child must have a DOM', child.getElement());
    +  assertEquals('Child element must be a child of the parent element',
    +      component.getElement(), child.getElement().parentNode);
    +  assertFalse('Child must not be in the document', child.isInDocument());
    +}
    +
    +function testAddChildAt() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +  var c = new goog.ui.Component();
    +  var d = new goog.ui.Component();
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +  d.setId('d');
    +
    +  component.addChildAt(b, 0);
    +  assertEquals('b', component.getChildIds().join(''));
    +  component.addChildAt(d, 1);
    +  assertEquals('bd', component.getChildIds().join(''));
    +  component.addChildAt(a, 0);
    +  assertEquals('abd', component.getChildIds().join(''));
    +  component.addChildAt(c, 2);
    +  assertEquals('abcd', component.getChildIds().join(''));
    +
    +  assertEquals(a, component.getChildAt(0));
    +  assertEquals(b, component.getChildAt(1));
    +  assertEquals(c, component.getChildAt(2));
    +  assertEquals(d, component.getChildAt(3));
    +
    +  assertThrows('Adding child at out-of-bounds index must throw error',
    +      function() {
    +        component.addChildAt(new goog.ui.Component(), 5);
    +      });
    +}
    +
    +function testAddChildAtThrowsIfNull() {
    +  assertThrows('Adding a null child must throw an error',
    +      function() {
    +        component.addChildAt(null, 0);
    +      });
    +}
    +
    +function testHasChildren() {
    +  assertFalse('Component must not have children', component.hasChildren());
    +
    +  component.addChildAt(new goog.ui.Component(), 0);
    +  assertTrue('Component must have children', component.hasChildren());
    +
    +  component.removeChildAt(0);
    +  assertFalse('Component must not have children', component.hasChildren());
    +}
    +
    +function testGetChildCount() {
    +  assertEquals('Component must have 0 children', 0,
    +      component.getChildCount());
    +
    +  component.addChild(new goog.ui.Component());
    +  assertEquals('Component must have 1 child', 1,
    +      component.getChildCount());
    +
    +  component.addChild(new goog.ui.Component());
    +  assertEquals('Component must have 2 children', 2,
    +      component.getChildCount());
    +
    +  component.removeChildAt(1);
    +  assertEquals('Component must have 1 child', 1,
    +      component.getChildCount());
    +
    +  component.removeChildAt(0);
    +  assertEquals('Component must have 0 children', 0,
    +      component.getChildCount());
    +}
    +
    +function testGetChildIds() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +
    +  a.setId('a');
    +  b.setId('b');
    +
    +  component.addChild(a);
    +  assertEquals('a', component.getChildIds().join(''));
    +
    +  component.addChild(b);
    +  assertEquals('ab', component.getChildIds().join(''));
    +
    +  var ids = component.getChildIds();
    +  ids.push('c');
    +  assertEquals('Changes to the array returned by getChildIds() must not' +
    +      ' affect the component', 'ab', component.getChildIds().join(''));
    +}
    +
    +function testGetChild() {
    +  assertNull('Parent must have no children', component.getChild('myId'));
    +
    +  var c = new goog.ui.Component();
    +  c.setId('myId');
    +  component.addChild(c);
    +  assertEquals('Parent must find child by ID', c,
    +      component.getChild('myId'));
    +
    +  c.setId('newId');
    +  assertNull('Parent must not find child by old ID',
    +      component.getChild('myId'));
    +  assertEquals('Parent must find child by new ID', c,
    +      component.getChild('newId'));
    +}
    +
    +function testGetChildAt() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +
    +  a.setId('a');
    +  b.setId('b');
    +
    +  component.addChildAt(a, 0);
    +  assertEquals('Parent must find child by index', a,
    +      component.getChildAt(0));
    +
    +  component.addChildAt(b, 1);
    +  assertEquals('Parent must find child by index', b,
    +      component.getChildAt(1));
    +
    +  assertNull('Parent must return null for out-of-bounds index',
    +      component.getChildAt(3));
    +}
    +
    +function testForEachChild() {
    +  var invoked = false;
    +  component.forEachChild(function(child) {
    +    assertNotNull('Child must never be null', child);
    +    invoked = true;
    +  });
    +  assertFalse('forEachChild must not call its argument if the parent has ' +
    +      'no children', invoked);
    +
    +  component.addChild(new goog.ui.Component());
    +  component.addChild(new goog.ui.Component());
    +  component.addChild(new goog.ui.Component());
    +  var callCount = 0;
    +  component.forEachChild(function(child, index) {
    +    assertEquals(component, this);
    +    callCount++;
    +  }, component);
    +  assertEquals(3, callCount);
    +}
    +
    +function testIndexOfChild() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +  var c = new goog.ui.Component();
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +
    +  component.addChild(a);
    +  assertEquals(0, component.indexOfChild(a));
    +
    +  component.addChild(b);
    +  assertEquals(1, component.indexOfChild(b));
    +
    +  component.addChild(c);
    +  assertEquals(2, component.indexOfChild(c));
    +
    +  assertEquals('indexOfChild must return -1 for nonexistent child', -1,
    +      component.indexOfChild(new goog.ui.Component()));
    +}
    +
    +function testRemoveChild() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +  var c = new goog.ui.Component();
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +
    +  component.addChild(a);
    +  component.addChild(b);
    +  component.addChild(c);
    +
    +  assertEquals('Parent must remove and return child', c,
    +      component.removeChild(c));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('c'));
    +
    +  assertEquals('Parent must remove and return child by ID', b,
    +      component.removeChild('b'));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('b'));
    +
    +  assertEquals('Parent must remove and return child by index', a,
    +      component.removeChildAt(0));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('a'));
    +}
    +
    +function testMovingChildrenUsingAddChildAt() {
    +  component.render(sandbox);
    +
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +  var c = new goog.ui.Component();
    +  var d = new goog.ui.Component();
    +  a.setElementInternal(goog.dom.createElement('a'));
    +  b.setElementInternal(goog.dom.createElement('b'));
    +  c.setElementInternal(goog.dom.createElement('c'));
    +  d.setElementInternal(goog.dom.createElement('d'));
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +  d.setId('d');
    +
    +  component.addChild(a, true);
    +  component.addChild(b, true);
    +  component.addChild(c, true);
    +  component.addChild(d, true);
    +
    +  assertEquals('abcd', component.getChildIds().join(''));
    +  assertEquals(a, component.getChildAt(0));
    +  assertEquals(b, component.getChildAt(1));
    +  assertEquals(c, component.getChildAt(2));
    +  assertEquals(d, component.getChildAt(3));
    +
    +  // Move child d to the top and b to the bottom.
    +  component.addChildAt(d, 0);
    +  component.addChildAt(b, 3);
    +
    +  assertEquals('dacb', component.getChildIds().join(''));
    +  assertEquals(d, component.getChildAt(0));
    +  assertEquals(a, component.getChildAt(1));
    +  assertEquals(c, component.getChildAt(2));
    +  assertEquals(b, component.getChildAt(3));
    +
    +  // Move child a to the top, and check that DOM nodes are in correct order.
    +  component.addChildAt(a, 0);
    +  assertEquals('adcb', component.getChildIds().join(''));
    +  assertEquals(a, component.getChildAt(0));
    +  assertEquals(a.getElement(), component.getElement().childNodes[0]);
    +}
    +
    +function testAddChildAfterDomCreatedDoesNotEnterDocument() {
    +  var parent = new goog.ui.Component();
    +  var child = new goog.ui.Component();
    +
    +  var nestedDiv = goog.dom.createDom('div');
    +  parent.setElementInternal(
    +      goog.dom.createDom('div', undefined, nestedDiv));
    +  parent.render();
    +
    +  // Now add a child, whose DOM already exists. This happens, for example,
    +  // if the child itself performs an addChild(x, true).
    +  child.createDom();
    +  parent.addChild(child, false);
    +  // The parent shouldn't call enterDocument on the child, since the child
    +  // actually isn't in the document yet.
    +  assertFalse(child.isInDocument());
    +
    +  // Now, actually render the child; it should be in the document.
    +  child.render(nestedDiv);
    +  assertTrue(child.isInDocument());
    +  assertEquals('Child should be rendered in the expected div',
    +      nestedDiv, child.getElement().parentNode);
    +}
    +
    +function testAddChildAfterDomManuallyInserted() {
    +  var parent = new goog.ui.Component();
    +  var child = new goog.ui.Component();
    +
    +  var nestedDiv = goog.dom.createDom('div');
    +  parent.setElementInternal(
    +      goog.dom.createDom('div', undefined, nestedDiv));
    +  parent.render();
    +
    +  // This sequence is weird, but some people do it instead of just manually
    +  // doing render.  The addChild will detect that the child is in the DOM
    +  // and call enterDocument.
    +  child.createDom();
    +  nestedDiv.appendChild(child.getElement());
    +  parent.addChild(child, false);
    +
    +  assertTrue(child.isInDocument());
    +  assertEquals('Child should be rendered in the expected div',
    +      nestedDiv, child.getElement().parentNode);
    +}
    +
    +function testRemoveChildren() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +  var c = new goog.ui.Component();
    +
    +  component.addChild(a);
    +  component.addChild(b);
    +  component.addChild(c);
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +
    +  assertArrayEquals('Parent must remove and return children.', [a, b, c],
    +      component.removeChildren());
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('a'));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('b'));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('c'));
    +}
    +
    +function testRemoveChildren_Unrender() {
    +  var a = new goog.ui.Component();
    +  var b = new goog.ui.Component();
    +
    +  component.render(sandbox);
    +  component.addChild(a);
    +  component.addChild(b);
    +
    +  assertArrayEquals('Prent must remove and return children.', [a, b],
    +      component.removeChildren(true));
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('a'));
    +  assertFalse('Child must no longer be in the document.',
    +      a.isInDocument());
    +  assertNull('Parent must no longer contain this child',
    +      component.getChild('b'));
    +  assertFalse('Child must no longer be in the document.',
    +      b.isInDocument());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/container.js b/mapbox-gl/closure-library/closure/goog/ui/container.js
    new file mode 100644
    index 0000000000..3d7579a189
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/container.js
    @@ -0,0 +1,1354 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for containers that host {@link goog.ui.Control}s,
    + * such as menus and toolbars.  Provides default keyboard and mouse event
    + * handling and child management, based on a generalized version of
    + * {@link goog.ui.Menu}.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/container.html
    + */
    +// TODO(attila):  Fix code/logic duplication between this and goog.ui.Control.
    +// TODO(attila):  Maybe pull common stuff all the way up into Component...?
    +
    +goog.provide('goog.ui.Container');
    +goog.provide('goog.ui.Container.EventType');
    +goog.provide('goog.ui.Container.Orientation');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ContainerRenderer');
    +goog.require('goog.ui.Control');
    +
    +
    +
    +/**
    + * Base class for containers.  Extends {@link goog.ui.Component} by adding
    + * the following:
    + *  <ul>
    + *    <li>a {@link goog.events.KeyHandler}, to simplify keyboard handling,
    + *    <li>a pluggable <em>renderer</em> framework, to simplify the creation of
    + *        containers without the need to subclass this class,
    + *    <li>methods to manage child controls hosted in the container,
    + *    <li>default mouse and keyboard event handling methods.
    + *  </ul>
    + * @param {?goog.ui.Container.Orientation=} opt_orientation Container
    + *     orientation; defaults to {@code VERTICAL}.
    + * @param {goog.ui.ContainerRenderer=} opt_renderer Renderer used to render or
    + *     decorate the container; defaults to {@link goog.ui.ContainerRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for document
    + *     interaction.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.Container = function(opt_orientation, opt_renderer, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +  this.renderer_ = opt_renderer || goog.ui.ContainerRenderer.getInstance();
    +  this.orientation_ = opt_orientation || this.renderer_.getDefaultOrientation();
    +};
    +goog.inherits(goog.ui.Container, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.Container);
    +
    +
    +/**
    + * Container-specific events.
    + * @enum {string}
    + */
    +goog.ui.Container.EventType = {
    +  /**
    +   * Dispatched after a goog.ui.Container becomes visible. Non-cancellable.
    +   * NOTE(user): This event really shouldn't exist, because the
    +   * goog.ui.Component.EventType.SHOW event should behave like this one. But the
    +   * SHOW event for containers has been behaving as other components'
    +   * BEFORE_SHOW event for a long time, and too much code relies on that old
    +   * behavior to fix it now.
    +   */
    +  AFTER_SHOW: 'aftershow',
    +
    +  /**
    +   * Dispatched after a goog.ui.Container becomes invisible. Non-cancellable.
    +   */
    +  AFTER_HIDE: 'afterhide'
    +};
    +
    +
    +/**
    + * Container orientation constants.
    + * @enum {string}
    + */
    +goog.ui.Container.Orientation = {
    +  HORIZONTAL: 'horizontal',
    +  VERTICAL: 'vertical'
    +};
    +
    +
    +/**
    + * Allows an alternative element to be set to receive key events, otherwise
    + * defers to the renderer's element choice.
    + * @type {Element|undefined}
    + * @private
    + */
    +goog.ui.Container.prototype.keyEventTarget_ = null;
    +
    +
    +/**
    + * Keyboard event handler.
    + * @type {goog.events.KeyHandler?}
    + * @private
    + */
    +goog.ui.Container.prototype.keyHandler_ = null;
    +
    +
    +/**
    + * Renderer for the container.  Defaults to {@link goog.ui.ContainerRenderer}.
    + * @type {goog.ui.ContainerRenderer?}
    + * @private
    + */
    +goog.ui.Container.prototype.renderer_ = null;
    +
    +
    +/**
    + * Container orientation; determines layout and default keyboard navigation.
    + * @type {?goog.ui.Container.Orientation}
    + * @private
    + */
    +goog.ui.Container.prototype.orientation_ = null;
    +
    +
    +/**
    + * Whether the container is set to be visible.  Defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.visible_ = true;
    +
    +
    +/**
    + * Whether the container is enabled and reacting to keyboard and mouse events.
    + * Defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.enabled_ = true;
    +
    +
    +/**
    + * Whether the container supports keyboard focus.  Defaults to true.  Focusable
    + * containers have a {@code tabIndex} and can be navigated to via the keyboard.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.focusable_ = true;
    +
    +
    +/**
    + * The 0-based index of the currently highlighted control in the container
    + * (-1 if none).
    + * @type {number}
    + * @private
    + */
    +goog.ui.Container.prototype.highlightedIndex_ = -1;
    +
    +
    +/**
    + * The currently open (expanded) control in the container (null if none).
    + * @type {goog.ui.Control?}
    + * @private
    + */
    +goog.ui.Container.prototype.openItem_ = null;
    +
    +
    +/**
    + * Whether the mouse button is held down.  Defaults to false.  This flag is set
    + * when the user mouses down over the container, and remains set until they
    + * release the mouse button.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.mouseButtonPressed_ = false;
    +
    +
    +/**
    + * Whether focus of child components should be allowed.  Only effective if
    + * focusable_ is set to false.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.allowFocusableChildren_ = false;
    +
    +
    +/**
    + * Whether highlighting a child component should also open it.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Container.prototype.openFollowsHighlight_ = true;
    +
    +
    +/**
    + * Map of DOM IDs to child controls.  Each key is the DOM ID of a child
    + * control's root element; each value is a reference to the child control
    + * itself.  Used for looking up the child control corresponding to a DOM
    + * node in O(1) time.
    + * @type {Object}
    + * @private
    + */
    +goog.ui.Container.prototype.childElementIdMap_ = null;
    +
    +
    +// Event handler and renderer management.
    +
    +
    +/**
    + * Returns the DOM element on which the container is listening for keyboard
    + * events (null if none).
    + * @return {Element} Element on which the container is listening for key
    + *     events.
    + */
    +goog.ui.Container.prototype.getKeyEventTarget = function() {
    +  // Delegate to renderer, unless we've set an explicit target.
    +  return this.keyEventTarget_ || this.renderer_.getKeyEventTarget(this);
    +};
    +
    +
    +/**
    + * Attaches an element on which to listen for key events.
    + * @param {Element|undefined} element The element to attach, or null/undefined
    + *     to attach to the default element.
    + */
    +goog.ui.Container.prototype.setKeyEventTarget = function(element) {
    +  if (this.focusable_) {
    +    var oldTarget = this.getKeyEventTarget();
    +    var inDocument = this.isInDocument();
    +
    +    this.keyEventTarget_ = element;
    +    var newTarget = this.getKeyEventTarget();
    +
    +    if (inDocument) {
    +      // Unlisten for events on the old key target.  Requires us to reset
    +      // key target state temporarily.
    +      this.keyEventTarget_ = oldTarget;
    +      this.enableFocusHandling_(false);
    +      this.keyEventTarget_ = element;
    +
    +      // Listen for events on the new key target.
    +      this.getKeyHandler().attach(newTarget);
    +      this.enableFocusHandling_(true);
    +    }
    +  } else {
    +    throw Error('Can\'t set key event target for container ' +
    +        'that doesn\'t support keyboard focus!');
    +  }
    +};
    +
    +
    +/**
    + * Returns the keyboard event handler for this container, lazily created the
    + * first time this method is called.  The keyboard event handler listens for
    + * keyboard events on the container's key event target, as determined by its
    + * renderer.
    + * @return {!goog.events.KeyHandler} Keyboard event handler for this container.
    + */
    +goog.ui.Container.prototype.getKeyHandler = function() {
    +  return this.keyHandler_ ||
    +      (this.keyHandler_ = new goog.events.KeyHandler(this.getKeyEventTarget()));
    +};
    +
    +
    +/**
    + * Returns the renderer used by this container to render itself or to decorate
    + * an existing element.
    + * @return {goog.ui.ContainerRenderer} Renderer used by the container.
    + */
    +goog.ui.Container.prototype.getRenderer = function() {
    +  return this.renderer_;
    +};
    +
    +
    +/**
    + * Registers the given renderer with the container.  Changing renderers after
    + * the container has already been rendered or decorated is an error.
    + * @param {goog.ui.ContainerRenderer} renderer Renderer used by the container.
    + */
    +goog.ui.Container.prototype.setRenderer = function(renderer) {
    +  if (this.getElement()) {
    +    // Too late.
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  this.renderer_ = renderer;
    +};
    +
    +
    +// Standard goog.ui.Component implementation.
    +
    +
    +/**
    + * Creates the container's DOM.
    + * @override
    + */
    +goog.ui.Container.prototype.createDom = function() {
    +  // Delegate to renderer.
    +  this.setElementInternal(this.renderer_.createDom(this));
    +};
    +
    +
    +/**
    + * Returns the DOM element into which child components are to be rendered,
    + * or null if the container itself hasn't been rendered yet.  Overrides
    + * {@link goog.ui.Component#getContentElement} by delegating to the renderer.
    + * @return {Element} Element to contain child elements (null if none).
    + * @override
    + */
    +goog.ui.Container.prototype.getContentElement = function() {
    +  // Delegate to renderer.
    +  return this.renderer_.getContentElement(this.getElement());
    +};
    +
    +
    +/**
    + * Returns true if the given element can be decorated by this container.
    + * Overrides {@link goog.ui.Component#canDecorate}.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} True iff the element can be decorated.
    + * @override
    + */
    +goog.ui.Container.prototype.canDecorate = function(element) {
    +  // Delegate to renderer.
    +  return this.renderer_.canDecorate(element);
    +};
    +
    +
    +/**
    + * Decorates the given element with this container. Overrides {@link
    + * goog.ui.Component#decorateInternal}.  Considered protected.
    + * @param {Element} element Element to decorate.
    + * @override
    + */
    +goog.ui.Container.prototype.decorateInternal = function(element) {
    +  // Delegate to renderer.
    +  this.setElementInternal(this.renderer_.decorate(this, element));
    +  // Check whether the decorated element is explicitly styled to be invisible.
    +  if (element.style.display == 'none') {
    +    this.visible_ = false;
    +  }
    +};
    +
    +
    +/**
    + * Configures the container after its DOM has been rendered, and sets up event
    + * handling.  Overrides {@link goog.ui.Component#enterDocument}.
    + * @override
    + */
    +goog.ui.Container.prototype.enterDocument = function() {
    +  goog.ui.Container.superClass_.enterDocument.call(this);
    +
    +  this.forEachChild(function(child) {
    +    if (child.isInDocument()) {
    +      this.registerChildId_(child);
    +    }
    +  }, this);
    +
    +  var elem = this.getElement();
    +
    +  // Call the renderer's initializeDom method to initialize the container's DOM.
    +  this.renderer_.initializeDom(this);
    +
    +  // Initialize visibility (opt_force = true, so we don't dispatch events).
    +  this.setVisible(this.visible_, true);
    +
    +  // Handle events dispatched by child controls.
    +  this.getHandler().
    +      listen(this, goog.ui.Component.EventType.ENTER,
    +          this.handleEnterItem).
    +      listen(this, goog.ui.Component.EventType.HIGHLIGHT,
    +          this.handleHighlightItem).
    +      listen(this, goog.ui.Component.EventType.UNHIGHLIGHT,
    +          this.handleUnHighlightItem).
    +      listen(this, goog.ui.Component.EventType.OPEN, this.handleOpenItem).
    +      listen(this, goog.ui.Component.EventType.CLOSE, this.handleCloseItem).
    +
    +      // Handle mouse events.
    +      listen(elem, goog.events.EventType.MOUSEDOWN, this.handleMouseDown).
    +      listen(goog.dom.getOwnerDocument(elem), goog.events.EventType.MOUSEUP,
    +          this.handleDocumentMouseUp).
    +
    +      // Handle mouse events on behalf of controls in the container.
    +      listen(elem, [
    +        goog.events.EventType.MOUSEDOWN,
    +        goog.events.EventType.MOUSEUP,
    +        goog.events.EventType.MOUSEOVER,
    +        goog.events.EventType.MOUSEOUT,
    +        goog.events.EventType.CONTEXTMENU
    +      ], this.handleChildMouseEvents);
    +
    +  // If the container is focusable, set up keyboard event handling.
    +  if (this.isFocusable()) {
    +    this.enableFocusHandling_(true);
    +  }
    +};
    +
    +
    +/**
    + * Sets up listening for events applicable to focusable containers.
    + * @param {boolean} enable Whether to enable or disable focus handling.
    + * @private
    + */
    +goog.ui.Container.prototype.enableFocusHandling_ = function(enable) {
    +  var handler = this.getHandler();
    +  var keyTarget = this.getKeyEventTarget();
    +  if (enable) {
    +    handler.
    +        listen(keyTarget, goog.events.EventType.FOCUS, this.handleFocus).
    +        listen(keyTarget, goog.events.EventType.BLUR, this.handleBlur).
    +        listen(this.getKeyHandler(), goog.events.KeyHandler.EventType.KEY,
    +            this.handleKeyEvent);
    +  } else {
    +    handler.
    +        unlisten(keyTarget, goog.events.EventType.FOCUS, this.handleFocus).
    +        unlisten(keyTarget, goog.events.EventType.BLUR, this.handleBlur).
    +        unlisten(this.getKeyHandler(), goog.events.KeyHandler.EventType.KEY,
    +            this.handleKeyEvent);
    +  }
    +};
    +
    +
    +/**
    + * Cleans up the container before its DOM is removed from the document, and
    + * removes event handlers.  Overrides {@link goog.ui.Component#exitDocument}.
    + * @override
    + */
    +goog.ui.Container.prototype.exitDocument = function() {
    +  // {@link #setHighlightedIndex} has to be called before
    +  // {@link goog.ui.Component#exitDocument}, otherwise it has no effect.
    +  this.setHighlightedIndex(-1);
    +
    +  if (this.openItem_) {
    +    this.openItem_.setOpen(false);
    +  }
    +
    +  this.mouseButtonPressed_ = false;
    +
    +  goog.ui.Container.superClass_.exitDocument.call(this);
    +};
    +
    +
    +/** @override */
    +goog.ui.Container.prototype.disposeInternal = function() {
    +  goog.ui.Container.superClass_.disposeInternal.call(this);
    +
    +  if (this.keyHandler_) {
    +    this.keyHandler_.dispose();
    +    this.keyHandler_ = null;
    +  }
    +
    +  this.keyEventTarget_ = null;
    +  this.childElementIdMap_ = null;
    +  this.openItem_ = null;
    +  this.renderer_ = null;
    +};
    +
    +
    +// Default event handlers.
    +
    +
    +/**
    + * Handles ENTER events raised by child controls when they are navigated to.
    + * @param {goog.events.Event} e ENTER event to handle.
    + * @return {boolean} Whether to prevent handleMouseOver from handling
    + *    the event.
    + */
    +goog.ui.Container.prototype.handleEnterItem = function(e) {
    +  // Allow the Control to highlight itself.
    +  return true;
    +};
    +
    +
    +/**
    + * Handles HIGHLIGHT events dispatched by items in the container when
    + * they are highlighted.
    + * @param {goog.events.Event} e Highlight event to handle.
    + */
    +goog.ui.Container.prototype.handleHighlightItem = function(e) {
    +  var index = this.indexOfChild(/** @type {goog.ui.Control} */ (e.target));
    +  if (index > -1 && index != this.highlightedIndex_) {
    +    var item = this.getHighlighted();
    +    if (item) {
    +      // Un-highlight previously highlighted item.
    +      item.setHighlighted(false);
    +    }
    +
    +    this.highlightedIndex_ = index;
    +    item = this.getHighlighted();
    +
    +    if (this.isMouseButtonPressed()) {
    +      // Activate item when mouse button is pressed, to allow MacOS-style
    +      // dragging to choose menu items.  Although this should only truly
    +      // happen if the highlight is due to mouse movements, there is little
    +      // harm in doing it for keyboard or programmatic highlights.
    +      item.setActive(true);
    +    }
    +
    +    // Update open item if open item needs follow highlight.
    +    if (this.openFollowsHighlight_ &&
    +        this.openItem_ && item != this.openItem_) {
    +      if (item.isSupportedState(goog.ui.Component.State.OPENED)) {
    +        item.setOpen(true);
    +      } else {
    +        this.openItem_.setOpen(false);
    +      }
    +    }
    +  }
    +
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The DOM element for the container cannot be null.');
    +  if (e.target.getElement() != null) {
    +    goog.a11y.aria.setState(element,
    +        goog.a11y.aria.State.ACTIVEDESCENDANT,
    +        e.target.getElement().id);
    +  }
    +};
    +
    +
    +/**
    + * Handles UNHIGHLIGHT events dispatched by items in the container when
    + * they are unhighlighted.
    + * @param {goog.events.Event} e Unhighlight event to handle.
    + */
    +goog.ui.Container.prototype.handleUnHighlightItem = function(e) {
    +  if (e.target == this.getHighlighted()) {
    +    this.highlightedIndex_ = -1;
    +  }
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The DOM element for the container cannot be null.');
    +  // Setting certain ARIA attributes to empty strings is problematic.
    +  // Just remove the attribute instead.
    +  goog.a11y.aria.removeState(element, goog.a11y.aria.State.ACTIVEDESCENDANT);
    +};
    +
    +
    +/**
    + * Handles OPEN events dispatched by items in the container when they are
    + * opened.
    + * @param {goog.events.Event} e Open event to handle.
    + */
    +goog.ui.Container.prototype.handleOpenItem = function(e) {
    +  var item = /** @type {goog.ui.Control} */ (e.target);
    +  if (item && item != this.openItem_ && item.getParent() == this) {
    +    if (this.openItem_) {
    +      this.openItem_.setOpen(false);
    +    }
    +    this.openItem_ = item;
    +  }
    +};
    +
    +
    +/**
    + * Handles CLOSE events dispatched by items in the container when they are
    + * closed.
    + * @param {goog.events.Event} e Close event to handle.
    + */
    +goog.ui.Container.prototype.handleCloseItem = function(e) {
    +  if (e.target == this.openItem_) {
    +    this.openItem_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Handles mousedown events over the container.  The default implementation
    + * sets the "mouse button pressed" flag and, if the container is focusable,
    + * grabs keyboard focus.
    + * @param {goog.events.BrowserEvent} e Mousedown event to handle.
    + */
    +goog.ui.Container.prototype.handleMouseDown = function(e) {
    +  if (this.enabled_) {
    +    this.setMouseButtonPressed(true);
    +  }
    +
    +  var keyTarget = this.getKeyEventTarget();
    +  if (keyTarget && goog.dom.isFocusableTabIndex(keyTarget)) {
    +    // The container is configured to receive keyboard focus.
    +    keyTarget.focus();
    +  } else {
    +    // The control isn't configured to receive keyboard focus; prevent it
    +    // from stealing focus or destroying the selection.
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Handles mouseup events over the document.  The default implementation
    + * clears the "mouse button pressed" flag.
    + * @param {goog.events.BrowserEvent} e Mouseup event to handle.
    + */
    +goog.ui.Container.prototype.handleDocumentMouseUp = function(e) {
    +  this.setMouseButtonPressed(false);
    +};
    +
    +
    +/**
    + * Handles mouse events originating from nodes belonging to the controls hosted
    + * in the container.  Locates the child control based on the DOM node that
    + * dispatched the event, and forwards the event to the control for handling.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + */
    +goog.ui.Container.prototype.handleChildMouseEvents = function(e) {
    +  var control = this.getOwnerControl(/** @type {Node} */ (e.target));
    +  if (control) {
    +    // Child control identified; forward the event.
    +    switch (e.type) {
    +      case goog.events.EventType.MOUSEDOWN:
    +        control.handleMouseDown(e);
    +        break;
    +      case goog.events.EventType.MOUSEUP:
    +        control.handleMouseUp(e);
    +        break;
    +      case goog.events.EventType.MOUSEOVER:
    +        control.handleMouseOver(e);
    +        break;
    +      case goog.events.EventType.MOUSEOUT:
    +        control.handleMouseOut(e);
    +        break;
    +      case goog.events.EventType.CONTEXTMENU:
    +        control.handleContextMenu(e);
    +        break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the child control that owns the given DOM node, or null if no such
    + * control is found.
    + * @param {Node} node DOM node whose owner is to be returned.
    + * @return {goog.ui.Control?} Control hosted in the container to which the node
    + *     belongs (if found).
    + * @protected
    + */
    +goog.ui.Container.prototype.getOwnerControl = function(node) {
    +  // Ensure that this container actually has child controls before
    +  // looking up the owner.
    +  if (this.childElementIdMap_) {
    +    var elem = this.getElement();
    +    // See http://b/2964418 . IE9 appears to evaluate '!=' incorrectly, so
    +    // using '!==' instead.
    +    // TODO(user): Possibly revert this change if/when IE9 fixes the issue.
    +    while (node && node !== elem) {
    +      var id = node.id;
    +      if (id in this.childElementIdMap_) {
    +        return this.childElementIdMap_[id];
    +      }
    +      node = node.parentNode;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Handles focus events raised when the container's key event target receives
    + * keyboard focus.
    + * @param {goog.events.BrowserEvent} e Focus event to handle.
    + */
    +goog.ui.Container.prototype.handleFocus = function(e) {
    +  // No-op in the base class.
    +};
    +
    +
    +/**
    + * Handles blur events raised when the container's key event target loses
    + * keyboard focus.  The default implementation clears the highlight index.
    + * @param {goog.events.BrowserEvent} e Blur event to handle.
    + */
    +goog.ui.Container.prototype.handleBlur = function(e) {
    +  this.setHighlightedIndex(-1);
    +  this.setMouseButtonPressed(false);
    +  // If the container loses focus, and one of its children is open, close it.
    +  if (this.openItem_) {
    +    this.openItem_.setOpen(false);
    +  }
    +};
    +
    +
    +/**
    + * Attempts to handle a keyboard event, if the control is enabled, by calling
    + * {@link handleKeyEventInternal}.  Considered protected; should only be used
    + * within this package and by subclasses.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the key event was handled.
    + */
    +goog.ui.Container.prototype.handleKeyEvent = function(e) {
    +  if (this.isEnabled() && this.isVisible() &&
    +      (this.getChildCount() != 0 || this.keyEventTarget_) &&
    +      this.handleKeyEventInternal(e)) {
    +    e.preventDefault();
    +    e.stopPropagation();
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Attempts to handle a keyboard event; returns true if the event was handled,
    + * false otherwise.  If the container is enabled, and a child is highlighted,
    + * calls the child control's {@code handleKeyEvent} method to give the control
    + * a chance to handle the event first.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the event was handled by the container (or one of
    + *     its children).
    + */
    +goog.ui.Container.prototype.handleKeyEventInternal = function(e) {
    +  // Give the highlighted control the chance to handle the key event.
    +  var highlighted = this.getHighlighted();
    +  if (highlighted && typeof highlighted.handleKeyEvent == 'function' &&
    +      highlighted.handleKeyEvent(e)) {
    +    return true;
    +  }
    +
    +  // Give the open control the chance to handle the key event.
    +  if (this.openItem_ && this.openItem_ != highlighted &&
    +      typeof this.openItem_.handleKeyEvent == 'function' &&
    +      this.openItem_.handleKeyEvent(e)) {
    +    return true;
    +  }
    +
    +  // Do not handle the key event if any modifier key is pressed.
    +  if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) {
    +    return false;
    +  }
    +
    +  // Either nothing is highlighted, or the highlighted control didn't handle
    +  // the key event, so attempt to handle it here.
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.ESC:
    +      if (this.isFocusable()) {
    +        this.getKeyEventTarget().blur();
    +      } else {
    +        return false;
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.HOME:
    +      this.highlightFirst();
    +      break;
    +
    +    case goog.events.KeyCodes.END:
    +      this.highlightLast();
    +      break;
    +
    +    case goog.events.KeyCodes.UP:
    +      if (this.orientation_ == goog.ui.Container.Orientation.VERTICAL) {
    +        this.highlightPrevious();
    +      } else {
    +        return false;
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.LEFT:
    +      if (this.orientation_ == goog.ui.Container.Orientation.HORIZONTAL) {
    +        if (this.isRightToLeft()) {
    +          this.highlightNext();
    +        } else {
    +          this.highlightPrevious();
    +        }
    +      } else {
    +        return false;
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.DOWN:
    +      if (this.orientation_ == goog.ui.Container.Orientation.VERTICAL) {
    +        this.highlightNext();
    +      } else {
    +        return false;
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.RIGHT:
    +      if (this.orientation_ == goog.ui.Container.Orientation.HORIZONTAL) {
    +        if (this.isRightToLeft()) {
    +          this.highlightPrevious();
    +        } else {
    +          this.highlightNext();
    +        }
    +      } else {
    +        return false;
    +      }
    +      break;
    +
    +    default:
    +      return false;
    +  }
    +
    +  return true;
    +};
    +
    +
    +// Child component management.
    +
    +
    +/**
    + * Creates a DOM ID for the child control and registers it to an internal
    + * hash table to be able to find it fast by id.
    + * @param {goog.ui.Component} child The child control. Its root element has
    + *     to be created yet.
    + * @private
    + */
    +goog.ui.Container.prototype.registerChildId_ = function(child) {
    +  // Map the DOM ID of the control's root element to the control itself.
    +  var childElem = child.getElement();
    +
    +  // If the control's root element doesn't have a DOM ID assign one.
    +  var id = childElem.id || (childElem.id = child.getId());
    +
    +  // Lazily create the child element ID map on first use.
    +  if (!this.childElementIdMap_) {
    +    this.childElementIdMap_ = {};
    +  }
    +  this.childElementIdMap_[id] = child;
    +};
    +
    +
    +/**
    + * Adds the specified control as the last child of this container.  See
    + * {@link goog.ui.Container#addChildAt} for detailed semantics.
    + * @param {goog.ui.Component} child The new child control.
    + * @param {boolean=} opt_render Whether the new child should be rendered
    + *     immediately after being added (defaults to false).
    + * @override
    + */
    +goog.ui.Container.prototype.addChild = function(child, opt_render) {
    +  goog.asserts.assertInstanceof(child, goog.ui.Control,
    +      'The child of a container must be a control');
    +  goog.ui.Container.superClass_.addChild.call(this, child, opt_render);
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.Container#getChild} to make it clear that it
    + * only returns {@link goog.ui.Control}s.
    + * @param {string} id Child component ID.
    + * @return {goog.ui.Control} The child with the given ID; null if none.
    + * @override
    + */
    +goog.ui.Container.prototype.getChild;
    +
    +
    +/**
    + * Overrides {@link goog.ui.Container#getChildAt} to make it clear that it
    + * only returns {@link goog.ui.Control}s.
    + * @param {number} index 0-based index.
    + * @return {goog.ui.Control} The child with the given ID; null if none.
    + * @override
    + */
    +goog.ui.Container.prototype.getChildAt;
    +
    +
    +/**
    + * Adds the control as a child of this container at the given 0-based index.
    + * Overrides {@link goog.ui.Component#addChildAt} by also updating the
    + * container's highlight index.  Since {@link goog.ui.Component#addChild} uses
    + * {@link #addChildAt} internally, we only need to override this method.
    + * @param {goog.ui.Component} control New child.
    + * @param {number} index Index at which the new child is to be added.
    + * @param {boolean=} opt_render Whether the new child should be rendered
    + *     immediately after being added (defaults to false).
    + * @override
    + */
    +goog.ui.Container.prototype.addChildAt = function(control, index, opt_render) {
    +  goog.asserts.assertInstanceof(control, goog.ui.Control);
    +
    +  // Make sure the child control dispatches HIGHLIGHT, UNHIGHLIGHT, OPEN, and
    +  // CLOSE events, and that it doesn't steal keyboard focus.
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.HOVER, true);
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.OPENED, true);
    +  if (this.isFocusable() || !this.isFocusableChildrenAllowed()) {
    +    control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  }
    +
    +  // Disable mouse event handling by child controls.
    +  control.setHandleMouseEvents(false);
    +
    +  var srcIndex = (control.getParent() == this) ?
    +      this.indexOfChild(control) : -1;
    +
    +  // Let the superclass implementation do the work.
    +  goog.ui.Container.superClass_.addChildAt.call(this, control, index,
    +      opt_render);
    +
    +  if (control.isInDocument() && this.isInDocument()) {
    +    this.registerChildId_(control);
    +  }
    +
    +  this.updateHighlightedIndex_(srcIndex, index);
    +};
    +
    +
    +/**
    + * Updates the highlighted index when children are added or moved.
    + * @param {number} fromIndex Index of the child before it was moved, or -1 if
    + *     the child was added.
    + * @param {number} toIndex Index of the child after it was moved or added.
    + * @private
    + */
    +goog.ui.Container.prototype.updateHighlightedIndex_ = function(
    +    fromIndex, toIndex) {
    +  if (fromIndex == -1) {
    +    fromIndex = this.getChildCount();
    +  }
    +  if (fromIndex == this.highlightedIndex_) {
    +    // The highlighted element itself was moved.
    +    this.highlightedIndex_ = Math.min(this.getChildCount() - 1, toIndex);
    +  } else if (fromIndex > this.highlightedIndex_ &&
    +      toIndex <= this.highlightedIndex_) {
    +    // The control was added or moved behind the highlighted index.
    +    this.highlightedIndex_++;
    +  } else if (fromIndex < this.highlightedIndex_ &&
    +      toIndex > this.highlightedIndex_) {
    +    // The control was moved from before to behind the highlighted index.
    +    this.highlightedIndex_--;
    +  }
    +};
    +
    +
    +/**
    + * Removes a child control.  Overrides {@link goog.ui.Component#removeChild} by
    + * updating the highlight index.  Since {@link goog.ui.Component#removeChildAt}
    + * uses {@link #removeChild} internally, we only need to override this method.
    + * @param {string|goog.ui.Component} control The ID of the child to remove, or
    + *     the control itself.
    + * @param {boolean=} opt_unrender Whether to call {@code exitDocument} on the
    + *     removed control, and detach its DOM from the document (defaults to
    + *     false).
    + * @return {goog.ui.Control} The removed control, if any.
    + * @override
    + */
    +goog.ui.Container.prototype.removeChild = function(control, opt_unrender) {
    +  control = goog.isString(control) ? this.getChild(control) : control;
    +  goog.asserts.assertInstanceof(control, goog.ui.Control);
    +
    +  if (control) {
    +    var index = this.indexOfChild(control);
    +    if (index != -1) {
    +      if (index == this.highlightedIndex_) {
    +        control.setHighlighted(false);
    +        this.highlightedIndex_ = -1;
    +      } else if (index < this.highlightedIndex_) {
    +        this.highlightedIndex_--;
    +      }
    +    }
    +
    +    // Remove the mapping from the child element ID map.
    +    var childElem = control.getElement();
    +    if (childElem && childElem.id && this.childElementIdMap_) {
    +      goog.object.remove(this.childElementIdMap_, childElem.id);
    +    }
    +  }
    +
    +  control = /** @type {!goog.ui.Control} */ (
    +      goog.ui.Container.superClass_.removeChild.call(this, control,
    +          opt_unrender));
    +
    +  // Re-enable mouse event handling (in case the control is reused elsewhere).
    +  control.setHandleMouseEvents(true);
    +
    +  return control;
    +};
    +
    +
    +// Container state management.
    +
    +
    +/**
    + * Returns the container's orientation.
    + * @return {?goog.ui.Container.Orientation} Container orientation.
    + */
    +goog.ui.Container.prototype.getOrientation = function() {
    +  return this.orientation_;
    +};
    +
    +
    +/**
    + * Sets the container's orientation.
    + * @param {goog.ui.Container.Orientation} orientation Container orientation.
    + */
    +// TODO(attila): Do we need to support containers with dynamic orientation?
    +goog.ui.Container.prototype.setOrientation = function(orientation) {
    +  if (this.getElement()) {
    +    // Too late.
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  this.orientation_ = orientation;
    +};
    +
    +
    +/**
    + * Returns true if the container's visibility is set to visible, false if
    + * it is set to hidden.  A container that is set to hidden is guaranteed
    + * to be hidden from the user, but the reverse isn't necessarily true.
    + * A container may be set to visible but can otherwise be obscured by another
    + * element, rendered off-screen, or hidden using direct CSS manipulation.
    + * @return {boolean} Whether the container is set to be visible.
    + */
    +goog.ui.Container.prototype.isVisible = function() {
    +  return this.visible_;
    +};
    +
    +
    +/**
    + * Shows or hides the container.  Does nothing if the container already has
    + * the requested visibility.  Otherwise, dispatches a SHOW or HIDE event as
    + * appropriate, giving listeners a chance to prevent the visibility change.
    + * @param {boolean} visible Whether to show or hide the container.
    + * @param {boolean=} opt_force If true, doesn't check whether the container
    + *     already has the requested visibility, and doesn't dispatch any events.
    + * @return {boolean} Whether the visibility was changed.
    + */
    +goog.ui.Container.prototype.setVisible = function(visible, opt_force) {
    +  if (opt_force || (this.visible_ != visible && this.dispatchEvent(visible ?
    +      goog.ui.Component.EventType.SHOW : goog.ui.Component.EventType.HIDE))) {
    +    this.visible_ = visible;
    +
    +    var elem = this.getElement();
    +    if (elem) {
    +      goog.style.setElementShown(elem, visible);
    +      if (this.isFocusable()) {
    +        // Enable keyboard access only for enabled & visible containers.
    +        this.renderer_.enableTabIndex(this.getKeyEventTarget(),
    +            this.enabled_ && this.visible_);
    +      }
    +      if (!opt_force) {
    +        this.dispatchEvent(this.visible_ ?
    +            goog.ui.Container.EventType.AFTER_SHOW :
    +            goog.ui.Container.EventType.AFTER_HIDE);
    +      }
    +    }
    +
    +    return true;
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Returns true if the container is enabled, false otherwise.
    + * @return {boolean} Whether the container is enabled.
    + */
    +goog.ui.Container.prototype.isEnabled = function() {
    +  return this.enabled_;
    +};
    +
    +
    +/**
    + * Enables/disables the container based on the {@code enable} argument.
    + * Dispatches an {@code ENABLED} or {@code DISABLED} event prior to changing
    + * the container's state, which may be caught and canceled to prevent the
    + * container from changing state.  Also enables/disables child controls.
    + * @param {boolean} enable Whether to enable or disable the container.
    + */
    +goog.ui.Container.prototype.setEnabled = function(enable) {
    +  if (this.enabled_ != enable && this.dispatchEvent(enable ?
    +      goog.ui.Component.EventType.ENABLE :
    +      goog.ui.Component.EventType.DISABLE)) {
    +    if (enable) {
    +      // Flag the container as enabled first, then update children.  This is
    +      // because controls can't be enabled if their parent is disabled.
    +      this.enabled_ = true;
    +      this.forEachChild(function(child) {
    +        // Enable child control unless it is flagged.
    +        if (child.wasDisabled) {
    +          delete child.wasDisabled;
    +        } else {
    +          child.setEnabled(true);
    +        }
    +      });
    +    } else {
    +      // Disable children first, then flag the container as disabled.  This is
    +      // because controls can't be disabled if their parent is already disabled.
    +      this.forEachChild(function(child) {
    +        // Disable child control, or flag it if it's already disabled.
    +        if (child.isEnabled()) {
    +          child.setEnabled(false);
    +        } else {
    +          child.wasDisabled = true;
    +        }
    +      });
    +      this.enabled_ = false;
    +      this.setMouseButtonPressed(false);
    +    }
    +
    +    if (this.isFocusable()) {
    +      // Enable keyboard access only for enabled & visible components.
    +      this.renderer_.enableTabIndex(this.getKeyEventTarget(),
    +          enable && this.visible_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the container is focusable, false otherwise.  The default
    + * is true.  Focusable containers always have a tab index and allocate a key
    + * handler to handle keyboard events while focused.
    + * @return {boolean} Whether the component is focusable.
    + */
    +goog.ui.Container.prototype.isFocusable = function() {
    +  return this.focusable_;
    +};
    +
    +
    +/**
    + * Sets whether the container is focusable.  The default is true.  Focusable
    + * containers always have a tab index and allocate a key handler to handle
    + * keyboard events while focused.
    + * @param {boolean} focusable Whether the component is to be focusable.
    + */
    +goog.ui.Container.prototype.setFocusable = function(focusable) {
    +  if (focusable != this.focusable_ && this.isInDocument()) {
    +    this.enableFocusHandling_(focusable);
    +  }
    +  this.focusable_ = focusable;
    +  if (this.enabled_ && this.visible_) {
    +    this.renderer_.enableTabIndex(this.getKeyEventTarget(), focusable);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the container allows children to be focusable, false
    + * otherwise.  Only effective if the container is not focusable.
    + * @return {boolean} Whether children should be focusable.
    + */
    +goog.ui.Container.prototype.isFocusableChildrenAllowed = function() {
    +  return this.allowFocusableChildren_;
    +};
    +
    +
    +/**
    + * Sets whether the container allows children to be focusable, false
    + * otherwise.  Only effective if the container is not focusable.
    + * @param {boolean} focusable Whether the children should be focusable.
    + */
    +goog.ui.Container.prototype.setFocusableChildrenAllowed = function(focusable) {
    +  this.allowFocusableChildren_ = focusable;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether highlighting a child component should also open it.
    + */
    +goog.ui.Container.prototype.isOpenFollowsHighlight = function() {
    +  return this.openFollowsHighlight_;
    +};
    +
    +
    +/**
    + * Sets whether highlighting a child component should also open it.
    + * @param {boolean} follow Whether highlighting a child component also opens it.
    + */
    +goog.ui.Container.prototype.setOpenFollowsHighlight = function(follow) {
    +  this.openFollowsHighlight_ = follow;
    +};
    +
    +
    +// Highlight management.
    +
    +
    +/**
    + * Returns the index of the currently highlighted item (-1 if none).
    + * @return {number} Index of the currently highlighted item.
    + */
    +goog.ui.Container.prototype.getHighlightedIndex = function() {
    +  return this.highlightedIndex_;
    +};
    +
    +
    +/**
    + * Highlights the item at the given 0-based index (if any).  If another item
    + * was previously highlighted, it is un-highlighted.
    + * @param {number} index Index of item to highlight (-1 removes the current
    + *     highlight).
    + */
    +goog.ui.Container.prototype.setHighlightedIndex = function(index) {
    +  var child = this.getChildAt(index);
    +  if (child) {
    +    child.setHighlighted(true);
    +  } else if (this.highlightedIndex_ > -1) {
    +    this.getHighlighted().setHighlighted(false);
    +  }
    +};
    +
    +
    +/**
    + * Highlights the given item if it exists and is a child of the container;
    + * otherwise un-highlights the currently highlighted item.
    + * @param {goog.ui.Control} item Item to highlight.
    + */
    +goog.ui.Container.prototype.setHighlighted = function(item) {
    +  this.setHighlightedIndex(this.indexOfChild(item));
    +};
    +
    +
    +/**
    + * Returns the currently highlighted item (if any).
    + * @return {goog.ui.Control?} Highlighted item (null if none).
    + */
    +goog.ui.Container.prototype.getHighlighted = function() {
    +  return this.getChildAt(this.highlightedIndex_);
    +};
    +
    +
    +/**
    + * Highlights the first highlightable item in the container
    + */
    +goog.ui.Container.prototype.highlightFirst = function() {
    +  this.highlightHelper(function(index, max) {
    +    return (index + 1) % max;
    +  }, this.getChildCount() - 1);
    +};
    +
    +
    +/**
    + * Highlights the last highlightable item in the container.
    + */
    +goog.ui.Container.prototype.highlightLast = function() {
    +  this.highlightHelper(function(index, max) {
    +    index--;
    +    return index < 0 ? max - 1 : index;
    +  }, 0);
    +};
    +
    +
    +/**
    + * Highlights the next highlightable item (or the first if nothing is currently
    + * highlighted).
    + */
    +goog.ui.Container.prototype.highlightNext = function() {
    +  this.highlightHelper(function(index, max) {
    +    return (index + 1) % max;
    +  }, this.highlightedIndex_);
    +};
    +
    +
    +/**
    + * Highlights the previous highlightable item (or the last if nothing is
    + * currently highlighted).
    + */
    +goog.ui.Container.prototype.highlightPrevious = function() {
    +  this.highlightHelper(function(index, max) {
    +    index--;
    +    return index < 0 ? max - 1 : index;
    +  }, this.highlightedIndex_);
    +};
    +
    +
    +/**
    + * Helper function that manages the details of moving the highlight among
    + * child controls in response to keyboard events.
    + * @param {function(number, number) : number} fn Function that accepts the
    + *     current and maximum indices, and returns the next index to check.
    + * @param {number} startIndex Start index.
    + * @return {boolean} Whether the highlight has changed.
    + * @protected
    + */
    +goog.ui.Container.prototype.highlightHelper = function(fn, startIndex) {
    +  // If the start index is -1 (meaning there's nothing currently highlighted),
    +  // try starting from the currently open item, if any.
    +  var curIndex = startIndex < 0 ?
    +      this.indexOfChild(this.openItem_) : startIndex;
    +  var numItems = this.getChildCount();
    +
    +  curIndex = fn.call(this, curIndex, numItems);
    +  var visited = 0;
    +  while (visited <= numItems) {
    +    var control = this.getChildAt(curIndex);
    +    if (control && this.canHighlightItem(control)) {
    +      this.setHighlightedIndexFromKeyEvent(curIndex);
    +      return true;
    +    }
    +    visited++;
    +    curIndex = fn.call(this, curIndex, numItems);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns whether the given item can be highlighted.
    + * @param {goog.ui.Control} item The item to check.
    + * @return {boolean} Whether the item can be highlighted.
    + * @protected
    + */
    +goog.ui.Container.prototype.canHighlightItem = function(item) {
    +  return item.isVisible() && item.isEnabled() &&
    +      item.isSupportedState(goog.ui.Component.State.HOVER);
    +};
    +
    +
    +/**
    + * Helper method that sets the highlighted index to the given index in response
    + * to a keyboard event.  The base class implementation simply calls the
    + * {@link #setHighlightedIndex} method, but subclasses can override this
    + * behavior as needed.
    + * @param {number} index Index of item to highlight.
    + * @protected
    + */
    +goog.ui.Container.prototype.setHighlightedIndexFromKeyEvent = function(index) {
    +  this.setHighlightedIndex(index);
    +};
    +
    +
    +/**
    + * Returns the currently open (expanded) control in the container (null if
    + * none).
    + * @return {goog.ui.Control?} The currently open control.
    + */
    +goog.ui.Container.prototype.getOpenItem = function() {
    +  return this.openItem_;
    +};
    +
    +
    +/**
    + * Returns true if the mouse button is pressed, false otherwise.
    + * @return {boolean} Whether the mouse button is pressed.
    + */
    +goog.ui.Container.prototype.isMouseButtonPressed = function() {
    +  return this.mouseButtonPressed_;
    +};
    +
    +
    +/**
    + * Sets or clears the "mouse button pressed" flag.
    + * @param {boolean} pressed Whether the mouse button is presed.
    + */
    +goog.ui.Container.prototype.setMouseButtonPressed = function(pressed) {
    +  this.mouseButtonPressed_ = pressed;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/container_perf.html b/mapbox-gl/closure-library/closure/goog/ui/container_perf.html
    new file mode 100644
    index 0000000000..40486afb60
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/container_perf.html
    @@ -0,0 +1,55 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.ui.Container</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css"/>
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.jsunit');
    +    goog.require('goog.ui.Container');
    +    goog.require('goog.ui.Control');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.ui.Container Performance Tests</h1>
    +  <p>
    +    <strong>User-agent:</strong>
    +    <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +
    +  <script>
    +    var table = new goog.testing.PerformanceTable(
    +        goog.dom.getElement('perfTable'));
    +
    +    // Number of controls to add to the container per test run.
    +    var SAMPLES_PER_RUN = 500;
    +
    +    function testContainerAddChild() {
    +      var description = 'Add ' + SAMPLES_PER_RUN +
    +          ' child controls to the container with immediate rendering';
    +      table.run(function() {
    +        var container = new goog.ui.Container();
    +        for (var i = 0; i < SAMPLES_PER_RUN; i++) {
    +          container.addChild(new goog.ui.Control(), true);
    +        }
    +        container.render();
    +        assertEquals('container child count', SAMPLES_PER_RUN,
    +              container.getChildCount());
    +        container.dispose();
    +      }, description);
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/container_test.html b/mapbox-gl/closure-library/closure/goog/ui/container_test.html
    new file mode 100644
    index 0000000000..6e562cb498
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/container_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Container
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ContainerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/container_test.js b/mapbox-gl/closure-library/closure/goog/ui/container_test.js
    new file mode 100644
    index 0000000000..85334b81d2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/container_test.js
    @@ -0,0 +1,612 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ContainerTest');
    +goog.setTestOnly('goog.ui.ContainerTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyEvent');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Control');
    +
    +var sandbox;
    +var containerElement;
    +var container;
    +var keyContainer;
    +var listContainer;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +}
    +
    +function setUp() {
    +  container = new goog.ui.Container();
    +  keyContainer = null;
    +  listContainer = null;
    +
    +  sandbox.innerHTML =
    +      '<div id="containerElement" class="goog-container">\n' +
    +      '  <div class="goog-control" id="hello">Hello</div>\n' +
    +      '  <div class="goog-control" id="world">World</div>\n' +
    +      '</div>';
    +  containerElement = goog.dom.getElement('containerElement');
    +}
    +
    +function tearDown() {
    +  goog.dom.removeChildren(sandbox);
    +  container.dispose();
    +  goog.dispose(keyContainer);
    +  goog.dispose(listContainer);
    +}
    +
    +function testDecorateHidden() {
    +  containerElement.style.display = 'none';
    +
    +  assertTrue('Container must be visible', container.isVisible());
    +  container.decorate(containerElement);
    +  assertFalse('Container must be hidden', container.isVisible());
    +  container.forEachChild(function(control) {
    +    assertTrue('Child control ' + control.getId() + ' must report being ' +
    +        'visible, even if in a hidden container', control.isVisible());
    +  });
    +}
    +
    +function testDecorateDisabled() {
    +  goog.dom.classlist.add(containerElement, 'goog-container-disabled');
    +
    +  assertTrue('Container must be enabled', container.isEnabled());
    +  container.decorate(containerElement);
    +  assertFalse('Container must be disabled', container.isEnabled());
    +  container.forEachChild(function(control) {
    +    assertFalse('Child control ' + control.getId() + ' must be disabled, ' +
    +        'because the host container is disabled', control.isEnabled());
    +  });
    +}
    +
    +function testDecorateFocusableContainer() {
    +  container.decorate(containerElement);
    +  assertTrue('Container must be focusable', container.isFocusable());
    +  container.forEachChild(function(control) {
    +    assertFalse('Child control ' + control.getId() + ' must not be ' +
    +        'focusable',
    +        control.isSupportedState(goog.ui.Component.State.FOCUSED));
    +  });
    +}
    +
    +function testDecorateFocusableChildrenContainer() {
    +  container.setFocusable(false);
    +  container.setFocusableChildrenAllowed(true);
    +  container.decorate(containerElement);
    +  assertFalse('Container must not be focusable', container.isFocusable());
    +  container.forEachChild(function(control) {
    +    assertTrue('Child control ' + control.getId() + ' must be ' +
    +        'focusable',
    +        control.isSupportedState(goog.ui.Component.State.FOCUSED));
    +  });
    +}
    +
    +function testHighlightOnEnter() {
    +  // This interaction test ensures that containers enforce that children
    +  // get highlighted on mouseover, and that one and only one child may
    +  // be highlighted at a time.  Although integration tests aren't the
    +  // best, it's difficult to test these event-based interactions due to
    +  // their disposition toward the "misunderstood contract" problem.
    +
    +  container.decorate(containerElement);
    +  assertFalse('Child 0 should initially not be highlighted',
    +      container.getChildAt(0).isHighlighted());
    +
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(0).getElement(), sandbox);
    +  assertTrue('Child 0 should become highlighted after a mouse over',
    +      container.getChildAt(0).isHighlighted());
    +  assertEquals('Child 0 should be the active descendant',
    +      container.getChildAt(0).getElement(),
    +      goog.a11y.aria.getActiveDescendant(container.getElement()));
    +
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(1).getElement(),
    +      container.getChildAt(0).getElement());
    +  assertFalse('Child 0 should lose highlight when child 1 is moused ' +
    +      'over, even if no mouseout occurs.',
    +      container.getChildAt(0).isHighlighted());
    +  assertTrue('Child 1 should now be highlighted.',
    +      container.getChildAt(1).isHighlighted());
    +  assertEquals('Child 1 should be the active descendant',
    +      container.getChildAt(1).getElement(),
    +      goog.a11y.aria.getActiveDescendant(container.getElement()));
    +}
    +
    +function testHighlightOnEnterPreventable() {
    +  container.decorate(containerElement);
    +  goog.events.listen(container, goog.ui.Component.EventType.ENTER,
    +      function(event) {
    +        event.preventDefault();
    +      });
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(0).getElement(), sandbox);
    +  assertFalse('Child 0 should not be highlighted if preventDefault called',
    +      container.getChildAt(0).isHighlighted());
    +}
    +
    +function testHighlightDisabled() {
    +  // Another interaction test.  Already tested in control_test.
    +  container.decorate(containerElement);
    +  container.getChildAt(0).setEnabled(false);
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(0).getElement(), sandbox);
    +  assertFalse('Disabled children should not be highlighted',
    +      container.getChildAt(0).isHighlighted());
    +}
    +
    +function testGetOwnerControl() {
    +  container.decorate(containerElement);
    +
    +  assertEquals('Must return appropriate control given an element in the ' +
    +      'control.',
    +      container.getChildAt(1),
    +      container.getOwnerControl(container.getChildAt(1).getElement()));
    +
    +  assertNull('Must return null for element not associated with control.',
    +      container.getOwnerControl(document.body));
    +  assertNull('Must return null if given null node',
    +      container.getOwnerControl(null));
    +}
    +
    +function testShowEvent() {
    +  container.decorate(containerElement);
    +  container.setVisible(false);
    +  var eventFired = false;
    +  goog.events.listen(container, goog.ui.Component.EventType.SHOW,
    +      function() {
    +        assertFalse('Container must not be visible when SHOW event is ' +
    +                    'fired',
    +                    container.isVisible());
    +        eventFired = true;
    +      });
    +  container.setVisible(true);
    +  assertTrue('SHOW event expected', eventFired);
    +}
    +
    +function testAfterShowEvent() {
    +  container.decorate(containerElement);
    +  container.setVisible(false);
    +  var eventFired = false;
    +  goog.events.listen(container, goog.ui.Container.EventType.AFTER_SHOW,
    +      function() {
    +        assertTrue('Container must be visible when AFTER_SHOW event is ' +
    +                   'fired',
    +                   container.isVisible());
    +        eventFired = true;
    +      });
    +  container.setVisible(true);
    +  assertTrue('AFTER_SHOW event expected', eventFired);
    +}
    +
    +function testHideEvents() {
    +  var events = [];
    +  container.decorate(containerElement);
    +  container.setVisible(true);
    +  var eventFired = false;
    +  goog.events.listen(container, goog.ui.Component.EventType.HIDE,
    +      function(e) {
    +        assertTrue(
    +            'Container must be visible when HIDE event is fired',
    +            container.isVisible());
    +        events.push(e.type);
    +      });
    +  goog.events.listen(container, goog.ui.Container.EventType.AFTER_HIDE,
    +      function(e) {
    +        assertFalse(
    +            'Container must not be visible when AFTER_HIDE event is fired',
    +            container.isVisible());
    +        events.push(e.type);
    +      });
    +  container.setVisible(false);
    +  assertArrayEquals('HIDE event followed by AFTER_HIDE expected', [
    +    goog.ui.Component.EventType.HIDE,
    +    goog.ui.Container.EventType.AFTER_HIDE
    +  ], events);
    +}
    +
    +
    +
    +/**
    + * Test container to which the elements have to be added with
    + * {@code container.addChild(element, false)}
    + * @constructor
    + * @extends {goog.ui.Container}
    + */
    +function ListContainer() {
    +  goog.ui.Container.call(this);
    +}
    +goog.inherits(ListContainer, goog.ui.Container);
    +
    +
    +/** @override */
    +ListContainer.prototype.createDom = function() {
    +  ListContainer.superClass_.createDom.call(this);
    +  var ul = this.getDomHelper().createDom('ul');
    +  this.forEachChild(function(child) {
    +    child.createDom();
    +    var childEl = child.getElement();
    +    ul.appendChild(this.getDomHelper().createDom('li', {}, childEl));
    +  }, this);
    +  this.getContentElement().appendChild(ul);
    +};
    +
    +function testGetOwnerControlWithNoRenderingInAddChild() {
    +  listContainer = new ListContainer();
    +  var control = new goog.ui.Control('item');
    +  listContainer.addChild(control);
    +  listContainer.render();
    +  var ownerControl = listContainer.getOwnerControl(control.getElement());
    +
    +  assertEquals('Control was added with addChild(control, false)',
    +      control, ownerControl);
    +}
    +
    +
    +
    +/**
    + * Test container for tracking key events being handled.
    + * @constructor
    + * @extends {goog.ui.Container}
    + */
    +function KeyHandlingContainer() {
    +  goog.ui.Container.call(this);
    +  this.keyEventsHandled = 0;
    +}
    +goog.inherits(KeyHandlingContainer, goog.ui.Container);
    +
    +
    +/** @override */
    +KeyHandlingContainer.prototype.handleKeyEventInternal = function() {
    +  this.keyEventsHandled++;
    +  return false;
    +};
    +
    +function testHandleKeyEvent_onlyHandlesWhenVisible() {
    +  keyContainer = new KeyHandlingContainer();
    +  keyContainer.decorate(containerElement);
    +
    +  keyContainer.setVisible(false);
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('No key events should be handled',
    +      0, keyContainer.keyEventsHandled);
    +
    +  keyContainer.setVisible(true);
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('One key event should be handled',
    +      1, keyContainer.keyEventsHandled);
    +}
    +
    +function testHandleKeyEvent_onlyHandlesWhenEnabled() {
    +  keyContainer = new KeyHandlingContainer();
    +  keyContainer.decorate(containerElement);
    +  keyContainer.setVisible(true);
    +
    +  keyContainer.setEnabled(false);
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('No key events should be handled',
    +      0, keyContainer.keyEventsHandled);
    +
    +  keyContainer.setEnabled(true);
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('One key event should be handled',
    +      1, keyContainer.keyEventsHandled);
    +}
    +
    +function testHandleKeyEvent_childlessContainersIgnoreKeyEvents() {
    +  keyContainer = new KeyHandlingContainer();
    +  keyContainer.render();
    +  keyContainer.setVisible(true);
    +
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('No key events should be handled',
    +      0, keyContainer.keyEventsHandled);
    +
    +  keyContainer.addChild(new goog.ui.Control());
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('One key event should be handled',
    +      1, keyContainer.keyEventsHandled);
    +}
    +
    +function testHandleKeyEvent_alwaysHandlesWithKeyEventTarget() {
    +  keyContainer = new KeyHandlingContainer();
    +  keyContainer.render();
    +  keyContainer.setKeyEventTarget(goog.dom.createDom('div'));
    +  keyContainer.setVisible(true);
    +
    +  keyContainer.handleKeyEvent(new goog.events.Event());
    +  assertEquals('One key events should be handled',
    +      1, keyContainer.keyEventsHandled);
    +}
    +
    +function testHandleKeyEventInternal_onlyHandlesUnmodified() {
    +  container.setKeyEventTarget(sandbox);
    +  var event = new goog.events.KeyEvent(
    +      goog.events.KeyCodes.ESC, 0, false, null);
    +
    +  var propertyNames = [
    +    'shiftKey',
    +    'altKey',
    +    'ctrlKey',
    +    'metaKey'
    +  ];
    +
    +  // Verify that the event is not handled whenever a modifier key is true.
    +  for (var i = 0, propertyName; propertyName = propertyNames[i]; i++) {
    +    assertTrue('Event should be handled when modifer key is not pressed.',
    +        container.handleKeyEventInternal(event));
    +    event[propertyName] = true;
    +    assertFalse('Event should not be handled when modifer key is pressed.',
    +        container.handleKeyEventInternal(event));
    +    event[propertyName] = false;
    +  }
    +}
    +
    +function testOpenFollowsHighlight() {
    +  container.decorate(containerElement);
    +  container.setOpenFollowsHighlight(true);
    +  assertTrue('isOpenFollowsHighlight should return true',
    +      container.isOpenFollowsHighlight());
    +
    +  // Make the children openable.
    +  container.forEachChild(function(child) {
    +    child.setSupportedState(goog.ui.Component.State.OPENED, true);
    +  });
    +  // Open child 1 initially.
    +  container.getChildAt(1).setOpen(true);
    +
    +  assertFalse('Child 0 should initially not be highlighted',
    +      container.getChildAt(0).isHighlighted());
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(0).getElement(), sandbox);
    +  assertTrue('Child 0 should become highlighted after a mouse over',
    +      container.getChildAt(0).isHighlighted());
    +  assertTrue('Child 0 should become open after higlighted',
    +      container.getChildAt(0).isOpen());
    +  assertFalse('Child 1 should become closed once 0 is open',
    +      container.getChildAt(1).isOpen());
    +  assertEquals('OpenItem should be child 0',
    +      container.getChildAt(0), container.getOpenItem());
    +}
    +
    +function testOpenNotFollowsHighlight() {
    +  container.decorate(containerElement);
    +  container.setOpenFollowsHighlight(false);
    +  assertFalse('isOpenFollowsHighlight should return false',
    +      container.isOpenFollowsHighlight());
    +
    +  // Make the children openable.
    +  container.forEachChild(function(child) {
    +    child.setSupportedState(goog.ui.Component.State.OPENED, true);
    +  });
    +  // Open child 1 initially.
    +  container.getChildAt(1).setOpen(true);
    +
    +  assertFalse('Child 0 should initially not be highlighted',
    +      container.getChildAt(0).isHighlighted());
    +  goog.testing.events.fireMouseOverEvent(
    +      container.getChildAt(0).getElement(), sandbox);
    +  assertTrue('Child 0 should become highlighted after a mouse over',
    +      container.getChildAt(0).isHighlighted());
    +  assertFalse('Child 0 should remain closed after higlighted',
    +      container.getChildAt(0).isOpen());
    +  assertTrue('Child 1 should remain open',
    +      container.getChildAt(1).isOpen());
    +  assertEquals('OpenItem should be child 1',
    +      container.getChildAt(1), container.getOpenItem());
    +}
    +
    +function testRemoveChild() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  var b = new goog.ui.Control('B');
    +  var c = new goog.ui.Control('C');
    +
    +  a.setId('a');
    +  b.setId('b');
    +  c.setId('c');
    +
    +  container.addChild(a, true);
    +  container.addChild(b, true);
    +  container.addChild(c, true);
    +
    +  container.setHighlightedIndex(2);
    +
    +  assertEquals('Parent must remove and return child by ID', b,
    +      container.removeChild('b'));
    +  assertNull('Parent must no longer contain this child',
    +      container.getChild('b'));
    +  assertEquals('Highlighted index must be decreased', 1,
    +      container.getHighlightedIndex());
    +  assertTrue('The removed control must handle its own mouse events',
    +      b.isHandleMouseEvents());
    +
    +  assertEquals('Parent must remove and return child', c,
    +      container.removeChild(c));
    +  assertNull('Parent must no longer contain this child',
    +      container.getChild('c'));
    +  assertFalse('This child must no longer be highlighted',
    +      c.isHighlighted());
    +  assertTrue('The removed control must handle its own mouse events',
    +      c.isHandleMouseEvents());
    +
    +  assertEquals('Parent must remove and return child by index', a,
    +      container.removeChildAt(0));
    +  assertNull('Parent must no longer contain this child',
    +      container.getChild('a'));
    +  assertTrue('The removed control must handle its own mouse events',
    +      a.isHandleMouseEvents());
    +}
    +
    +function testRemoveHighlightedDisposedChild() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  container.addChild(a, true);
    +
    +  container.setHighlightedIndex(0);
    +  a.dispose();
    +  container.removeChild(a);
    +  container.dispose();
    +}
    +
    +
    +/**
    + * Checks that getHighlighted() returns the expected value and checks
    + * that the child at this index is highlighted and other children are not.
    + * @param {string} explanation Message indicating what is expected.
    + * @param {number} index Expected return value of getHighlightedIndex().
    + */
    +function assertHighlightedIndex(explanation, index) {
    +  assertEquals(explanation, index, container.getHighlightedIndex());
    +  for (var i = 0; i < container.getChildCount(); i++) {
    +    if (i == index) {
    +      assertTrue('Child at highlighted index should be highlighted',
    +          container.getChildAt(i).isHighlighted());
    +    } else {
    +      assertFalse('Only child at highlighted index should be highlighted',
    +          container.getChildAt(i).isHighlighted());
    +    }
    +  }
    +}
    +
    +function testUpdateHighlightedIndex_updatesWhenChildrenAreAdded() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  var b = new goog.ui.Control('B');
    +  var c = new goog.ui.Control('C');
    +
    +  container.addChild(a);
    +  container.setHighlightedIndex(0);
    +  assertHighlightedIndex('Highlighted index should match set value', 0);
    +
    +  // Add child before the highlighted one.
    +  container.addChildAt(b, 0);
    +  assertHighlightedIndex('Highlighted index should be increased', 1);
    +
    +  // Add child after the highlighted one.
    +  container.addChildAt(c, 2);
    +  assertHighlightedIndex('Highlighted index should not change', 1);
    +
    +  container.dispose();
    +}
    +
    +function testUpdateHighlightedIndex_updatesWhenChildrenAreMoved() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  var b = new goog.ui.Control('B');
    +  var c = new goog.ui.Control('C');
    +
    +  container.addChild(a);
    +  container.addChild(b);
    +  container.addChild(c);
    +
    +  // Highlight 'c' and swap 'a' and 'b'
    +  // [a, b, c] -> [a, b, *c] -> [b, a, *c] (* indicates the highlighted child)
    +  container.setHighlightedIndex(2);
    +  container.addChildAt(a, 1, false);
    +  assertHighlightedIndex('Highlighted index should not change', 2);
    +
    +  // Move the highlighted child 'c' from index 2 to index 1.
    +  // [b, a, *c] -> [b, *c, a]
    +  container.addChildAt(c, 1, false);
    +  assertHighlightedIndex('Highlighted index must follow the moved child', 1);
    +
    +  // Take the element in front of the highlighted index and move it behind it.
    +  // [b, *c, a] -> [*c, a, b]
    +  container.addChildAt(b, 2, false);
    +  assertHighlightedIndex('Highlighted index must be decreased', 0);
    +
    +  // And move the element back to the front.
    +  // [*c, a, b] -> [b, *c, a]
    +  container.addChildAt(b, 0, false);
    +  assertHighlightedIndex('Highlighted index must be increased', 1);
    +
    +  container.dispose();
    +}
    +
    +function testUpdateHighlightedIndex_notChangedOnNoOp() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  container.addChild(new goog.ui.Control('A'));
    +  container.addChild(new goog.ui.Control('B'));
    +  container.setHighlightedIndex(1);
    +
    +  // Re-add a child to its current position.
    +  container.addChildAt(container.getChildAt(0), 0, false);
    +  assertHighlightedIndex('Highlighted index must not change', 1);
    +
    +  container.dispose();
    +}
    +
    +function testUpdateHighlightedIndex_notChangedWhenNoChildSelected() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  var b = new goog.ui.Control('B');
    +  var c = new goog.ui.Control('C');
    +  container.addChild(a);
    +  container.addChild(b);
    +  container.addChild(c);
    +
    +  // Move children around.
    +  container.addChildAt(a, 2, false);
    +  container.addChildAt(b, 1, false);
    +  container.addChildAt(c, 2, false);
    +
    +  assertHighlightedIndex('Highlighted index must not change', -1);
    +
    +  container.dispose();
    +}
    +
    +function testUpdateHighlightedIndex_indexStaysInBoundsWhenMovedToMaxIndex() {
    +  goog.dom.removeChildren(containerElement);
    +  container.decorate(containerElement);
    +
    +  var a = new goog.ui.Control('A');
    +  var b = new goog.ui.Control('B');
    +  container.addChild(a);
    +  container.addChild(b);
    +
    +  // Move higlighted child to an index one behind last child.
    +  container.setHighlightedIndex(0);
    +  container.addChildAt(a, 2);
    +
    +  assertEquals('Child should be moved to index 1', a, container.getChildAt(1));
    +  assertEquals('Child count should not change', 2, container.getChildCount());
    +  assertHighlightedIndex('Highlighted index must point to new index', 1);
    +
    +  container.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer.js
    new file mode 100644
    index 0000000000..c6ac213fdd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer.js
    @@ -0,0 +1,374 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for container renderers.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ContainerRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.registry');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Container}.  Can be used as-is, but
    + * subclasses of Container will probably want to use renderers specifically
    + * tailored for them by extending this class.
    + * @param {string=} opt_ariaRole Optional ARIA role used for the element.
    + * @constructor
    + */
    +goog.ui.ContainerRenderer = function(opt_ariaRole) {
    +  // By default, the ARIA role is unspecified.
    +  /** @private {string|undefined} */
    +  this.ariaRole_ = opt_ariaRole;
    +};
    +goog.addSingletonGetter(goog.ui.ContainerRenderer);
    +
    +
    +/**
    + * Constructs a new renderer and sets the CSS class that the renderer will use
    + * as the base CSS class to apply to all elements rendered by that renderer.
    + * An example to use this function using a menu is:
    + *
    + * <pre>
    + * var myCustomRenderer = goog.ui.ContainerRenderer.getCustomRenderer(
    + *     goog.ui.MenuRenderer, 'my-special-menu');
    + * var newMenu = new goog.ui.Menu(opt_domHelper, myCustomRenderer);
    + * </pre>
    + *
    + * Your styles for the menu can now be:
    + * <pre>
    + * .my-special-menu { }
    + * </pre>
    + *
    + * <em>instead</em> of
    + * <pre>
    + * .CSS_MY_SPECIAL_MENU .goog-menu { }
    + * </pre>
    + *
    + * You would want to use this functionality when you want an instance of a
    + * component to have specific styles different than the other components of the
    + * same type in your application.  This avoids using descendant selectors to
    + * apply the specific styles to this component.
    + *
    + * @param {Function} ctor The constructor of the renderer you want to create.
    + * @param {string} cssClassName The name of the CSS class for this renderer.
    + * @return {goog.ui.ContainerRenderer} An instance of the desired renderer with
    + *     its getCssClass() method overridden to return the supplied custom CSS
    + *     class name.
    + */
    +goog.ui.ContainerRenderer.getCustomRenderer = function(ctor, cssClassName) {
    +  var renderer = new ctor();
    +
    +  /**
    +   * Returns the CSS class to be applied to the root element of components
    +   * rendered using this renderer.
    +   * @return {string} Renderer-specific CSS class.
    +   */
    +  renderer.getCssClass = function() {
    +    return cssClassName;
    +  };
    +
    +  return renderer;
    +};
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of containers rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ContainerRenderer.CSS_CLASS = goog.getCssName('goog-container');
    +
    +
    +/**
    + * Returns the ARIA role to be applied to the container.
    + * See http://wiki/Main/ARIA for more info.
    + * @return {undefined|string} ARIA role.
    + */
    +goog.ui.ContainerRenderer.prototype.getAriaRole = function() {
    +  return this.ariaRole_;
    +};
    +
    +
    +/**
    + * Enables or disables the tab index of the element.  Only elements with a
    + * valid tab index can receive focus.
    + * @param {Element} element Element whose tab index is to be changed.
    + * @param {boolean} enable Whether to add or remove the element's tab index.
    + */
    +goog.ui.ContainerRenderer.prototype.enableTabIndex = function(element, enable) {
    +  if (element) {
    +    element.tabIndex = enable ? 0 : -1;
    +  }
    +};
    +
    +
    +/**
    + * Creates and returns the container's root element.  The default
    + * simply creates a DIV and applies the renderer's own CSS class name to it.
    + * To be overridden in subclasses.
    + * @param {goog.ui.Container} container Container to render.
    + * @return {Element} Root element for the container.
    + */
    +goog.ui.ContainerRenderer.prototype.createDom = function(container) {
    +  return container.getDomHelper().createDom('div',
    +      this.getClassNames(container).join(' '));
    +};
    +
    +
    +/**
    + * Returns the DOM element into which child components are to be rendered,
    + * or null if the container hasn't been rendered yet.
    + * @param {Element} element Root element of the container whose content element
    + *     is to be returned.
    + * @return {Element} Element to contain child elements (null if none).
    + */
    +goog.ui.ContainerRenderer.prototype.getContentElement = function(element) {
    +  return element;
    +};
    +
    +
    +/**
    + * Default implementation of {@code canDecorate}; returns true if the element
    + * is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + */
    +goog.ui.ContainerRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == 'DIV';
    +};
    +
    +
    +/**
    + * Default implementation of {@code decorate} for {@link goog.ui.Container}s.
    + * Decorates the element with the container, and attempts to decorate its child
    + * elements.  Returns the decorated element.
    + * @param {goog.ui.Container} container Container to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {!Element} Decorated element.
    + */
    +goog.ui.ContainerRenderer.prototype.decorate = function(container, element) {
    +  // Set the container's ID to the decorated element's DOM ID, if any.
    +  if (element.id) {
    +    container.setId(element.id);
    +  }
    +
    +  // Configure the container's state based on the CSS class names it has.
    +  var baseClass = this.getCssClass();
    +  var hasBaseClass = false;
    +  var classNames = goog.dom.classlist.get(element);
    +  if (classNames) {
    +    goog.array.forEach(classNames, function(className) {
    +      if (className == baseClass) {
    +        hasBaseClass = true;
    +      } else if (className) {
    +        this.setStateFromClassName(container, className, baseClass);
    +      }
    +    }, this);
    +  }
    +
    +  if (!hasBaseClass) {
    +    // Make sure the container's root element has the renderer's own CSS class.
    +    goog.dom.classlist.add(element, baseClass);
    +  }
    +
    +  // Decorate the element's children, if applicable.  This should happen after
    +  // the container's own state has been initialized, since how children are
    +  // decorated may depend on the state of the container.
    +  this.decorateChildren(container, this.getContentElement(element));
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Sets the container's state based on the given CSS class name, encountered
    + * during decoration.  CSS class names that don't represent container states
    + * are ignored.  Considered protected; subclasses should override this method
    + * to support more states and CSS class names.
    + * @param {goog.ui.Container} container Container to update.
    + * @param {string} className CSS class name.
    + * @param {string} baseClass Base class name used as the root of state-specific
    + *     class names (typically the renderer's own class name).
    + * @protected
    + */
    +goog.ui.ContainerRenderer.prototype.setStateFromClassName = function(container,
    +    className, baseClass) {
    +  if (className == goog.getCssName(baseClass, 'disabled')) {
    +    container.setEnabled(false);
    +  } else if (className == goog.getCssName(baseClass, 'horizontal')) {
    +    container.setOrientation(goog.ui.Container.Orientation.HORIZONTAL);
    +  } else if (className == goog.getCssName(baseClass, 'vertical')) {
    +    container.setOrientation(goog.ui.Container.Orientation.VERTICAL);
    +  }
    +};
    +
    +
    +/**
    + * Takes a container and an element that may contain child elements, decorates
    + * the child elements, and adds the corresponding components to the container
    + * as child components.  Any non-element child nodes (e.g. empty text nodes
    + * introduced by line breaks in the HTML source) are removed from the element.
    + * @param {goog.ui.Container} container Container whose children are to be
    + *     discovered.
    + * @param {Element} element Element whose children are to be decorated.
    + * @param {Element=} opt_firstChild the first child to be decorated.
    + */
    +goog.ui.ContainerRenderer.prototype.decorateChildren = function(container,
    +    element, opt_firstChild) {
    +  if (element) {
    +    var node = opt_firstChild || element.firstChild, next;
    +    // Tag soup HTML may result in a DOM where siblings have different parents.
    +    while (node && node.parentNode == element) {
    +      // Get the next sibling here, since the node may be replaced or removed.
    +      next = node.nextSibling;
    +      if (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +        // Decorate element node.
    +        var child = this.getDecoratorForChild(/** @type {!Element} */(node));
    +        if (child) {
    +          // addChild() may need to look at the element.
    +          child.setElementInternal(/** @type {!Element} */(node));
    +          // If the container is disabled, mark the child disabled too.  See
    +          // bug 1263729.  Note that this must precede the call to addChild().
    +          if (!container.isEnabled()) {
    +            child.setEnabled(false);
    +          }
    +          container.addChild(child);
    +          child.decorate(/** @type {!Element} */(node));
    +        }
    +      } else if (!node.nodeValue || goog.string.trim(node.nodeValue) == '') {
    +        // Remove empty text node, otherwise madness ensues (e.g. controls that
    +        // use goog-inline-block will flicker and shift on hover on Gecko).
    +        element.removeChild(node);
    +      }
    +      node = next;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Inspects the element, and creates an instance of {@link goog.ui.Control} or
    + * an appropriate subclass best suited to decorate it.  Returns the control (or
    + * null if no suitable class was found).  This default implementation uses the
    + * element's CSS class to find the appropriate control class to instantiate.
    + * May be overridden in subclasses.
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Control?} A new control suitable to decorate the element
    + *     (null if none).
    + */
    +goog.ui.ContainerRenderer.prototype.getDecoratorForChild = function(element) {
    +  return /** @type {goog.ui.Control} */ (
    +      goog.ui.registry.getDecorator(element));
    +};
    +
    +
    +/**
    + * Initializes the container's DOM when the container enters the document.
    + * Called from {@link goog.ui.Container#enterDocument}.
    + * @param {goog.ui.Container} container Container whose DOM is to be initialized
    + *     as it enters the document.
    + */
    +goog.ui.ContainerRenderer.prototype.initializeDom = function(container) {
    +  var elem = container.getElement();
    +  goog.asserts.assert(elem, 'The container DOM element cannot be null.');
    +  // Make sure the container's element isn't selectable.  On Gecko, recursively
    +  // marking each child element unselectable is expensive and unnecessary, so
    +  // only mark the root element unselectable.
    +  goog.style.setUnselectable(elem, true, goog.userAgent.GECKO);
    +
    +  // IE doesn't support outline:none, so we have to use the hideFocus property.
    +  if (goog.userAgent.IE) {
    +    elem.hideFocus = true;
    +  }
    +
    +  // Set the ARIA role.
    +  var ariaRole = this.getAriaRole();
    +  if (ariaRole) {
    +    goog.a11y.aria.setRole(elem, ariaRole);
    +  }
    +};
    +
    +
    +/**
    + * Returns the element within the container's DOM that should receive keyboard
    + * focus (null if none).  The default implementation returns the container's
    + * root element.
    + * @param {goog.ui.Container} container Container whose key event target is
    + *     to be returned.
    + * @return {Element} Key event target (null if none).
    + */
    +goog.ui.ContainerRenderer.prototype.getKeyEventTarget = function(container) {
    +  return container.getElement();
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of containers
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + */
    +goog.ui.ContainerRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ContainerRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Returns all CSS class names applicable to the given container, based on its
    + * state.  The array of class names returned includes the renderer's own CSS
    + * class, followed by a CSS class indicating the container's orientation,
    + * followed by any state-specific CSS classes.
    + * @param {goog.ui.Container} container Container whose CSS classes are to be
    + *     returned.
    + * @return {!Array<string>} Array of CSS class names applicable to the
    + *     container.
    + */
    +goog.ui.ContainerRenderer.prototype.getClassNames = function(container) {
    +  var baseClass = this.getCssClass();
    +  var isHorizontal =
    +      container.getOrientation() == goog.ui.Container.Orientation.HORIZONTAL;
    +  var classNames = [
    +    baseClass,
    +    (isHorizontal ?
    +        goog.getCssName(baseClass, 'horizontal') :
    +        goog.getCssName(baseClass, 'vertical'))
    +  ];
    +  if (!container.isEnabled()) {
    +    classNames.push(goog.getCssName(baseClass, 'disabled'));
    +  }
    +  return classNames;
    +};
    +
    +
    +/**
    + * Returns the default orientation of containers rendered or decorated by this
    + * renderer.  The base class implementation returns {@code VERTICAL}.
    + * @return {goog.ui.Container.Orientation} Default orientation for containers
    + *     created or decorated by this renderer.
    + */
    +goog.ui.ContainerRenderer.prototype.getDefaultOrientation = function() {
    +  return goog.ui.Container.Orientation.VERTICAL;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.html
    new file mode 100644
    index 0000000000..b1dd9ac647
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ContainerRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ContainerRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.js
    new file mode 100644
    index 0000000000..4eef812f98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerrenderer_test.js
    @@ -0,0 +1,225 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ContainerRendererTest');
    +goog.setTestOnly('goog.ui.ContainerRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.ContainerRenderer');
    +goog.require('goog.userAgent');
    +
    +var renderer;
    +var expectedFailures;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  var sandbox = goog.dom.getElement('sandbox');
    +
    +  sandbox.appendChild(goog.dom.createDom('span', {
    +    id: 'noTabIndex'
    +  }, 'Test'));
    +  sandbox.appendChild(goog.dom.createDom('div', {
    +    id: 'container',
    +    'class': 'goog-container-horizontal'
    +  }, goog.dom.createDom('div', {
    +    id: 'control',
    +    'class': 'goog-control'
    +  }, 'Hello, world!')));
    +
    +  renderer = goog.ui.ContainerRenderer.getInstance();
    +}
    +
    +function tearDown() {
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +  stubs.reset();
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testGetInstance() {
    +  assertTrue('getInstance() must return a ContainerRenderer',
    +      renderer instanceof goog.ui.ContainerRenderer);
    +  assertEquals('getInstance() must return the same object each time',
    +      renderer, goog.ui.ContainerRenderer.getInstance());
    +}
    +
    +function testGetCustomRenderer() {
    +  var cssClass = 'special-css-class';
    +  var containerRenderer = goog.ui.ContainerRenderer.getCustomRenderer(
    +      goog.ui.ContainerRenderer, cssClass);
    +  assertEquals(
    +      'Renderer should have returned the custom CSS class.',
    +      cssClass,
    +      containerRenderer.getCssClass());
    +}
    +
    +function testGetAriaRole() {
    +  assertUndefined('ARIA role must be undefined', renderer.getAriaRole());
    +}
    +
    +function testEnableTabIndex() {
    +  var container = goog.dom.getElement('container');
    +  assertFalse('Container must not have any tab index',
    +      goog.dom.isFocusableTabIndex(container));
    +
    +  // WebKit on Mac doesn't support tabIndex for arbitrary DOM elements
    +  // until version 527 or later.
    +  expectedFailures.expectFailureFor(goog.userAgent.WEBKIT &&
    +      goog.userAgent.MAC && !goog.userAgent.isVersionOrHigher('527'));
    +  try {
    +    renderer.enableTabIndex(container, true);
    +    assertTrue('Container must have a tab index',
    +        goog.dom.isFocusableTabIndex(container));
    +    assertEquals('Container\'s tab index must be 0', 0, container.tabIndex);
    +
    +    renderer.enableTabIndex(container, false);
    +    assertFalse('Container must not have a tab index',
    +        goog.dom.isFocusableTabIndex(container));
    +    assertEquals('Container\'s tab index must be -1', -1,
    +        container.tabIndex);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testCreateDom() {
    +  var horizontal = new goog.ui.Container(
    +      goog.ui.Container.Orientation.HORIZONTAL);
    +  var element1 = renderer.createDom(horizontal);
    +  assertEquals('Element must be a DIV', 'DIV', element1.tagName);
    +  assertEquals('Element must have the expected class name',
    +      'goog-container goog-container-horizontal',
    +      element1.className);
    +
    +  var vertical = new goog.ui.Container(
    +      goog.ui.Container.Orientation.VERTICAL);
    +  var element2 = renderer.createDom(vertical);
    +  assertEquals('Element must be a DIV', 'DIV', element2.tagName);
    +  assertEquals('Element must have the expected class name',
    +      'goog-container goog-container-vertical',
    +      element2.className);
    +}
    +
    +function testGetContentElement() {
    +  assertNull('getContentElement() must return null if element is null',
    +      renderer.getContentElement(null));
    +  var element = goog.dom.getElement('container');
    +  assertEquals('getContentElement() must return its argument',
    +      element, renderer.getContentElement(element));
    +}
    +
    +function testCanDecorate() {
    +  assertFalse('canDecorate() must return false for a SPAN',
    +      renderer.canDecorate(goog.dom.getElement('noTabIndex')));
    +  assertTrue('canDecorate() must return true for a DIV',
    +      renderer.canDecorate(goog.dom.getElement('container')));
    +}
    +
    +function testDecorate() {
    +  var container = new goog.ui.Container();
    +  var element = goog.dom.getElement('container');
    +
    +  assertFalse('Container must not be in the document',
    +      container.isInDocument());
    +  container.decorate(element);
    +  assertTrue('Container must be in the document',
    +      container.isInDocument());
    +
    +  assertEquals('Container\'s ID must match the decorated element\'s ID',
    +      element.id, container.getId());
    +  assertEquals('Element must have the expected class name',
    +      'goog-container-horizontal goog-container', element.className);
    +  assertEquals('Container must have one child', 1,
    +      container.getChildCount());
    +  assertEquals('Child component\'s ID must be as expected', 'control',
    +      container.getChildAt(0).getId());
    +
    +  assertThrows('Redecorating must throw error', function() {
    +    container.decorate(element);
    +  });
    +}
    +
    +function testDecorateWithCustomContainerElement() {
    +  var element = goog.dom.getElement('container');
    +  var alternateContainerElement = goog.dom.createElement('div');
    +  element.appendChild(alternateContainerElement);
    +
    +  var container = new goog.ui.Container();
    +  stubs.set(renderer, 'getContentElement', function() {
    +    return alternateContainerElement;
    +  });
    +
    +  assertFalse('Container must not be in the document',
    +      container.isInDocument());
    +  container.decorate(element);
    +  assertTrue('Container must be in the document',
    +      container.isInDocument());
    +
    +  assertEquals('Container\'s ID must match the decorated element\'s ID',
    +      element.id, container.getId());
    +  assertEquals('Element must have the expected class name',
    +      'goog-container-horizontal goog-container', element.className);
    +  assertEquals('Container must have 0 children', 0,
    +      container.getChildCount());
    +
    +  assertThrows('Redecorating must throw error', function() {
    +    container.decorate(element);
    +  });
    +}
    +
    +function testSetStateFromClassName() {
    +  var container = new goog.ui.Container();
    +
    +  assertEquals('Container must be vertical',
    +      goog.ui.Container.Orientation.VERTICAL, container.getOrientation());
    +  renderer.setStateFromClassName(container, 'goog-container-horizontal',
    +      'goog-container');
    +  assertEquals('Container must be horizontal',
    +      goog.ui.Container.Orientation.HORIZONTAL, container.getOrientation());
    +  renderer.setStateFromClassName(container, 'goog-container-vertical',
    +      'goog-container');
    +  assertEquals('Container must be vertical',
    +      goog.ui.Container.Orientation.VERTICAL, container.getOrientation());
    +
    +  assertTrue('Container must be enabled', container.isEnabled());
    +  renderer.setStateFromClassName(container, 'goog-container-disabled',
    +      'goog-container');
    +  assertFalse('Container must be disabled', container.isEnabled());
    +}
    +
    +function testInitializeDom() {
    +  var container = new goog.ui.Container();
    +  var element = goog.dom.getElement('container');
    +  container.decorate(element);
    +
    +  assertTrue('Container\'s root element must be unselectable',
    +      goog.style.isUnselectable(container.getElement()));
    +
    +  assertEquals('On IE, container\'s root element must have hideFocus=true',
    +      goog.userAgent.IE, !!container.getElement().hideFocus);
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.ContainerRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerscroller.js b/mapbox-gl/closure-library/closure/goog/ui/containerscroller.js
    new file mode 100644
    index 0000000000..c67a94566b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerscroller.js
    @@ -0,0 +1,223 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Scroll behavior that can be added onto a container.
    + * @author gboyer@google.com (Garry Boyer)
    + */
    +
    +goog.provide('goog.ui.ContainerScroller');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Container');
    +
    +
    +
    +/**
    + * Plug-on scrolling behavior for a container.
    + *
    + * Use this to style containers, such as pop-up menus, to be scrolling, and
    + * automatically keep the highlighted element visible.
    + *
    + * To use this, first style your container with the desired overflow
    + * properties and height to achieve vertical scrolling.  Also, the scrolling
    + * div should have no vertical padding, for two reasons: it is difficult to
    + * compensate for, and is generally not what you want due to the strange way
    + * CSS handles padding on the scrolling dimension.
    + *
    + * The container must already be rendered before this may be constructed.
    + *
    + * @param {!goog.ui.Container} container The container to attach behavior to.
    + * @constructor
    + * @extends {goog.Disposable}
    + * @final
    + */
    +goog.ui.ContainerScroller = function(container) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The container that we are bestowing scroll behavior on.
    +   * @type {!goog.ui.Container}
    +   * @private
    +   */
    +  this.container_ = container;
    +
    +  /**
    +   * Event handler for this object.
    +   * @type {!goog.events.EventHandler<!goog.ui.ContainerScroller>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  this.eventHandler_.listen(container, goog.ui.Component.EventType.HIGHLIGHT,
    +      this.onHighlight_);
    +  this.eventHandler_.listen(container, goog.ui.Component.EventType.ENTER,
    +      this.onEnter_);
    +  this.eventHandler_.listen(container, goog.ui.Container.EventType.AFTER_SHOW,
    +      this.onAfterShow_);
    +  this.eventHandler_.listen(container, goog.ui.Component.EventType.HIDE,
    +      this.onHide_);
    +
    +  // TODO(gboyer): Allow a ContainerScroller to be attached with a Container
    +  // before the container is rendered.
    +
    +  this.doScrolling_(true);
    +};
    +goog.inherits(goog.ui.ContainerScroller, goog.Disposable);
    +
    +
    +/**
    + * The last target the user hovered over.
    + *
    + * @see #onEnter_
    + * @type {goog.ui.Component}
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.lastEnterTarget_ = null;
    +
    +
    +/**
    + * The scrollTop of the container before it was hidden.
    + * Used to restore the scroll position when the container is shown again.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.scrollTopBeforeHide_ = null;
    +
    +
    +/**
    + * Whether we are disabling the default handler for hovering.
    + *
    + * @see #onEnter_
    + * @see #temporarilyDisableHover_
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.disableHover_ = false;
    +
    +
    +/**
    + * Handles hover events on the container's children.
    + *
    + * Helps enforce two constraints: scrolling should not cause mouse highlights,
    + * and mouse highlights should not cause scrolling.
    + *
    + * @param {goog.events.Event} e The container's ENTER event.
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.onEnter_ = function(e) {
    +  if (this.disableHover_) {
    +    // The container was scrolled recently.  Since the mouse may be over the
    +    // container, stop the default action of the ENTER event from causing
    +    // highlights.
    +    e.preventDefault();
    +  } else {
    +    // The mouse is moving and causing hover events.  Stop the resulting
    +    // highlight (if it happens) from causing a scroll.
    +    this.lastEnterTarget_ = /** @type {goog.ui.Component} */ (e.target);
    +  }
    +};
    +
    +
    +/**
    + * Handles highlight events on the container's children.
    + * @param {goog.events.Event} e The container's highlight event.
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.onHighlight_ = function(e) {
    +  this.doScrolling_();
    +};
    +
    +
    +/**
    + * Handles AFTER_SHOW events on the container. Makes the container
    + * scroll to the previously scrolled position (if there was one),
    + * then adjust it to make the highlighted element be in view (if there is one).
    + * If there was no previous scroll position, then center the highlighted
    + * element (if there is one).
    + * @param {goog.events.Event} e The container's AFTER_SHOW event.
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.onAfterShow_ = function(e) {
    +  if (this.scrollTopBeforeHide_ != null) {
    +    this.container_.getElement().scrollTop = this.scrollTopBeforeHide_;
    +    // Make sure the highlighted item is still visible, in case the list
    +    // or its hilighted item has changed.
    +    this.doScrolling_(false);
    +  } else {
    +    this.doScrolling_(true);
    +  }
    +};
    +
    +
    +/**
    + * Handles hide events on the container. Clears out the last enter target,
    + * since it is no longer applicable, and remembers the scroll position of
    + * the menu so that it can be restored when the menu is reopened.
    + * @param {goog.events.Event} e The container's hide event.
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.onHide_ = function(e) {
    +  if (e.target == this.container_) {
    +    this.lastEnterTarget_ = null;
    +    this.scrollTopBeforeHide_ = this.container_.getElement().scrollTop;
    +  }
    +};
    +
    +
    +/**
    + * Centers the currently highlighted item, if this is scrollable.
    + * @param {boolean=} opt_center Whether to center the highlighted element
    + *     rather than simply ensure it is in view.  Useful for the first
    + *     render.
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.doScrolling_ = function(opt_center) {
    +  var highlighted = this.container_.getHighlighted();
    +
    +  // Only scroll if we're visible and there is a highlighted item.
    +  if (this.container_.isVisible() && highlighted &&
    +      highlighted != this.lastEnterTarget_) {
    +    var element = this.container_.getElement();
    +    goog.style.scrollIntoContainerView(highlighted.getElement(), element,
    +        opt_center);
    +    this.temporarilyDisableHover_();
    +    this.lastEnterTarget_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Temporarily disables hover events from changing highlight.
    + * @see #onEnter_
    + * @private
    + */
    +goog.ui.ContainerScroller.prototype.temporarilyDisableHover_ = function() {
    +  this.disableHover_ = true;
    +  goog.Timer.callOnce(function() {
    +    this.disableHover_ = false;
    +  }, 0, this);
    +};
    +
    +
    +/** @override */
    +goog.ui.ContainerScroller.prototype.disposeInternal = function() {
    +  goog.ui.ContainerScroller.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.lastEnterTarget_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.html b/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.html
    new file mode 100644
    index 0000000000..b828e0bad2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.html
    @@ -0,0 +1,74 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author:  gboyer@google.com (Garrett Boyer)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ContainerScroller
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ContainerScrollerTest');
    +  </script>
    +  <style type="text/css">
    +   .goog-container {
    +  height: 100px;
    +  overflow-y: auto;
    +  overflow-x: hidden;
    +  position: relative;
    +  /* Give a border and margin to ensure ContainerScroller is tolerant to
    +   * them.  It is, however, not tolerant to padding. */
    +  border: 11px solid #666;
    +  margin: 7px 13px 17px 19px;
    +}
    +.goog-control {
    +  font-size: 10px;
    +  height: 14px;
    +  padding: 3px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="sandbox" class="goog-container">
    +   <div class="goog-control" id="control-0">
    +    0
    +   </div>
    +   <div class="goog-control" id="control-1">
    +    1
    +   </div>
    +   <div class="goog-control" id="control-2">
    +    2
    +   </div>
    +   <div class="goog-control" id="control-3">
    +    3
    +   </div>
    +   <div class="goog-control" id="control-4">
    +    4
    +   </div>
    +   <div class="goog-control" id="control-5">
    +    5
    +   </div>
    +   <div class="goog-control" id="control-6">
    +    6
    +   </div>
    +   <div class="goog-control" id="control-7">
    +    7
    +   </div>
    +   <div class="goog-control" id="control-8">
    +    8
    +   </div>
    +   <div class="goog-control" id="control-9">
    +    9
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.js b/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.js
    new file mode 100644
    index 0000000000..952051666a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/containerscroller_test.js
    @@ -0,0 +1,182 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ContainerScrollerTest');
    +goog.setTestOnly('goog.ui.ContainerScrollerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.ContainerScroller');
    +
    +var sandbox;
    +var sandboxHtml;
    +var container;
    +var mockClock;
    +var scroller;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  sandboxHtml = sandbox.innerHTML;
    +}
    +
    +function setUp() {
    +  container = new goog.ui.Container();
    +  container.decorate(sandbox);
    +  container.getElement().scrollTop = 0;
    +  mockClock = new goog.testing.MockClock(true);
    +  scroller = null;
    +}
    +
    +function tearDown() {
    +  container.dispose();
    +  if (scroller) {
    +    scroller.dispose();
    +  }
    +  // Tick one second to clear all the extra registered events.
    +  mockClock.tick(1000);
    +  mockClock.uninstall();
    +  sandbox.innerHTML = sandboxHtml;
    +}
    +
    +function testHighlightFirstStaysAtTop() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(0).setHighlighted(true);
    +  assertEquals(0, container.getElement().scrollTop);
    +}
    +
    +function testHighlightSecondStaysAtTop() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(1).setHighlighted(true);
    +  assertEquals(0, container.getElement().scrollTop);
    +}
    +
    +function testHighlightSecondLastScrollsNearTheBottom() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(8).setHighlighted(true);
    +  assertEquals('Since scrolling is lazy, when highlighting the second' +
    +      ' last, the item should be the last visible one.',
    +      80, container.getElement().scrollTop);
    +}
    +
    +function testHighlightLastScrollsToBottom() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(9).setHighlighted(true);
    +  assertEquals(100, container.getElement().scrollTop);
    +}
    +
    +function testScrollRestoreIfStillVisible() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(9).setHighlighted(true);
    +  var scrollTop = container.getElement().scrollTop;
    +  container.setVisible(false);
    +  container.setVisible(true);
    +  assertEquals('Scroll position should be the same after restore, if it ' +
    +               'still makes highlighted item visible',
    +               scrollTop, container.getElement().scrollTop);
    +}
    +
    +function testNoScrollRestoreIfNotVisible() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getElement().scrollTop = 100;
    +  container.setVisible(false);
    +  container.getChildAt(0).setHighlighted(true);
    +  container.setVisible(true);
    +  assertNotEquals('Scroll position should not be the same after restore, if ' +
    +                  'the scroll position when the menu was hidden no longer ' +
    +                  'makes the highlighted item visible when the container is ' +
    +                  'shown again',
    +      100, container.getElement().scrollTop);
    +}
    +
    +function testCenterOnHighlightedOnFirstOpen() {
    +  container.setVisible(false);
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(4).setHighlighted(true);
    +  container.setVisible(true);
    +  // #2 should be at the top when 4 is centered, meaning a scroll top
    +  // of 40 pixels.
    +  assertEquals(
    +      'On the very first display of the scroller, the item should be ' +
    +      'centered, rather than just assured in view.',
    +      40, container.getElement().scrollTop);
    +}
    +
    +function testHighlightsAreIgnoredInResponseToScrolling() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(9).setHighlighted(true);
    +  goog.testing.events.fireMouseOverEvent(
    +      goog.dom.getElement('control-5'),
    +      goog.dom.getElement('control-9'));
    +  assertEquals('Mouseovers due to scrolls should be ignored',
    +      9, container.getHighlightedIndex());
    +}
    +
    +function testHighlightsAreNotIgnoredWhenNotScrolling() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getChildAt(5).setHighlighted(true);
    +  mockClock.tick(1000);
    +  goog.testing.events.fireMouseOutEvent(
    +      goog.dom.getElement('control-5'),
    +      goog.dom.getElement('control-6'));
    +  goog.testing.events.fireMouseOverEvent(
    +      goog.dom.getElement('control-6'),
    +      goog.dom.getElement('control-5'));
    +  assertEquals('Mousovers not due to scrolls should not be ignored',
    +      6, container.getHighlightedIndex());
    +}
    +
    +function testFastSynchronousHighlightsNotIgnored() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  // Whereas subsequent highlights from mouseovers due to a scroll, should
    +  // be ignored, they should not ignored if they are made synchronusly
    +  // from the code and not from a mouseover.  Imagine how bad it would be
    +  // if you could only set the highligted index a certain number of
    +  // times in the same execution context.
    +  container.getChildAt(9).setHighlighted(true);
    +  container.getChildAt(1).setHighlighted(true);
    +  assertEquals('Synchronous highlights should NOT be ignored.',
    +      1, container.getHighlightedIndex());
    +  container.getChildAt(8).setHighlighted(true);
    +  assertEquals('Synchronous highlights should NOT be ignored.',
    +      8, container.getHighlightedIndex());
    +}
    +
    +function testInitialItemIsCentered() {
    +  container.getChildAt(4).setHighlighted(true);
    +  scroller = new goog.ui.ContainerScroller(container);
    +  // #2 should be at the top when 4 is centered, meaning a scroll top
    +  // of 40 pixels.
    +  assertEquals(
    +      'On the very first attachment of the scroller, the item should be ' +
    +      'centered, rather than just assured in view.',
    +      40, container.getElement().scrollTop);
    +}
    +
    +function testInitialItemIsCenteredTopItem() {
    +  container.getChildAt(0).setHighlighted(true);
    +  scroller = new goog.ui.ContainerScroller(container);
    +  assertEquals(0, container.getElement().scrollTop);
    +}
    +
    +function testHidingMenuItemsDoesntAffectContainerScroller() {
    +  scroller = new goog.ui.ContainerScroller(container);
    +  container.getElement = function() {
    +    fail('getElement() must not be called when a control in the container is ' +
    +         'being hidden');
    +  };
    +  container.getChildAt(0).setVisible(false);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/control.js b/mapbox-gl/closure-library/closure/goog/ui/control.js
    new file mode 100644
    index 0000000000..477b165090
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/control.js
    @@ -0,0 +1,1423 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for UI controls such as buttons, menus, menu items,
    + * toolbar buttons, etc.  The implementation is based on a generalized version
    + * of {@link goog.ui.MenuItem}.
    + * TODO(attila):  If the renderer framework works well, pull it into Component.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/control.html
    + * @see http://code.google.com/p/closure-library/wiki/IntroToControls
    + */
    +
    +goog.provide('goog.ui.Control');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +/** @suppress {extraRequire} */
    +goog.require('goog.ui.ControlContent');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.ui.decorate');
    +goog.require('goog.ui.registry');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Base class for UI controls.  Extends {@link goog.ui.Component} by adding
    + * the following:
    + *  <ul>
    + *    <li>a {@link goog.events.KeyHandler}, to simplify keyboard handling,
    + *    <li>a pluggable <em>renderer</em> framework, to simplify the creation of
    + *        simple controls without the need to subclass this class,
    + *    <li>the notion of component <em>content</em>, like a text caption or DOM
    + *        structure displayed in the component (e.g. a button label),
    + *    <li>getter and setter for component content, as well as a getter and
    + *        setter specifically for caption text (for convenience),
    + *    <li>support for hiding/showing the component,
    +      <li>fine-grained control over supported states and state transition
    +          events, and
    + *    <li>default mouse and keyboard event handling.
    + *  </ul>
    + * This class has sufficient built-in functionality for most simple UI controls.
    + * All controls dispatch SHOW, HIDE, ENTER, LEAVE, and ACTION events on show,
    + * hide, mouseover, mouseout, and user action, respectively.  Additional states
    + * are also supported.  See closure/demos/control.html
    + * for example usage.
    + * @param {goog.ui.ControlContent=} opt_content Text caption or DOM structure
    + *     to display as the content of the control (if any).
    + * @param {goog.ui.ControlRenderer=} opt_renderer Renderer used to render or
    + *     decorate the component; defaults to {@link goog.ui.ControlRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.Control = function(opt_content, opt_renderer, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +  this.renderer_ = opt_renderer ||
    +      goog.ui.registry.getDefaultRenderer(this.constructor);
    +  this.setContentInternal(goog.isDef(opt_content) ? opt_content : null);
    +
    +  /** @private {?string} The control's aria-label. */
    +  this.ariaLabel_ = null;
    +};
    +goog.inherits(goog.ui.Control, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.Control);
    +
    +
    +// Renderer registry.
    +// TODO(attila): Refactor existing usages inside Google in a follow-up CL.
    +
    +
    +/**
    + * Maps a CSS class name to a function that returns a new instance of
    + * {@link goog.ui.Control} or a subclass thereof, suitable to decorate
    + * an element that has the specified CSS class.  UI components that extend
    + * {@link goog.ui.Control} and want {@link goog.ui.Container}s to be able
    + * to discover and decorate elements using them should register a factory
    + * function via this API.
    + * @param {string} className CSS class name.
    + * @param {Function} decoratorFunction Function that takes no arguments and
    + *     returns a new instance of a control to decorate an element with the
    + *     given class.
    + * @deprecated Use {@link goog.ui.registry.setDecoratorByClassName} instead.
    + */
    +goog.ui.Control.registerDecorator = goog.ui.registry.setDecoratorByClassName;
    +
    +
    +/**
    + * Takes an element and returns a new instance of {@link goog.ui.Control}
    + * or a subclass, suitable to decorate it (based on the element's CSS class).
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Control?} New control instance to decorate the element
    + *     (null if none).
    + * @deprecated Use {@link goog.ui.registry.getDecorator} instead.
    + */
    +goog.ui.Control.getDecorator =
    +    /** @type {function(Element): goog.ui.Control} */ (
    +        goog.ui.registry.getDecorator);
    +
    +
    +/**
    + * Takes an element, and decorates it with a {@link goog.ui.Control} instance
    + * if a suitable decorator is found.
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Control?} New control instance that decorates the element
    + *     (null if none).
    + * @deprecated Use {@link goog.ui.decorate} instead.
    + */
    +goog.ui.Control.decorate = /** @type {function(Element): goog.ui.Control} */ (
    +    goog.ui.decorate);
    +
    +
    +/**
    + * Renderer associated with the component.
    + * @type {goog.ui.ControlRenderer|undefined}
    + * @private
    + */
    +goog.ui.Control.prototype.renderer_;
    +
    +
    +/**
    + * Text caption or DOM structure displayed in the component.
    + * @type {goog.ui.ControlContent}
    + * @private
    + */
    +goog.ui.Control.prototype.content_ = null;
    +
    +
    +/**
    + * Current component state; a bit mask of {@link goog.ui.Component.State}s.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Control.prototype.state_ = 0x00;
    +
    +
    +/**
    + * A bit mask of {@link goog.ui.Component.State}s this component supports.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Control.prototype.supportedStates_ =
    +    goog.ui.Component.State.DISABLED |
    +    goog.ui.Component.State.HOVER |
    +    goog.ui.Component.State.ACTIVE |
    +    goog.ui.Component.State.FOCUSED;
    +
    +
    +/**
    + * A bit mask of {@link goog.ui.Component.State}s for which this component
    + * provides default event handling.  For example, a component that handles
    + * the HOVER state automatically will highlight itself on mouseover, whereas
    + * a component that doesn't handle HOVER automatically will only dispatch
    + * ENTER and LEAVE events but not call {@link setHighlighted} on itself.
    + * By default, components provide default event handling for all states.
    + * Controls hosted in containers (e.g. menu items in a menu, or buttons in a
    + * toolbar) will typically want to have their container manage their highlight
    + * state.  Selectable controls managed by a selection model will also typically
    + * want their selection state to be managed by the model.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Control.prototype.autoStates_ = goog.ui.Component.State.ALL;
    +
    +
    +/**
    + * A bit mask of {@link goog.ui.Component.State}s for which this component
    + * dispatches state transition events.  Because events are expensive, the
    + * default behavior is to not dispatch any state transition events at all.
    + * Use the {@link #setDispatchTransitionEvents} API to request transition
    + * events  as needed.  Subclasses may enable transition events by default.
    + * Controls hosted in containers or managed by a selection model will typically
    + * want to dispatch transition events.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Control.prototype.statesWithTransitionEvents_ = 0x00;
    +
    +
    +/**
    + * Component visibility.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Control.prototype.visible_ = true;
    +
    +
    +/**
    + * Keyboard event handler.
    + * @type {goog.events.KeyHandler}
    + * @private
    + */
    +goog.ui.Control.prototype.keyHandler_;
    +
    +
    +/**
    + * Additional class name(s) to apply to the control's root element, if any.
    + * @type {Array<string>?}
    + * @private
    + */
    +goog.ui.Control.prototype.extraClassNames_ = null;
    +
    +
    +/**
    + * Whether the control should listen for and handle mouse events; defaults to
    + * true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Control.prototype.handleMouseEvents_ = true;
    +
    +
    +/**
    + * Whether the control allows text selection within its DOM.  Defaults to false.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Control.prototype.allowTextSelection_ = false;
    +
    +
    +/**
    + * The control's preferred ARIA role.
    + * @type {?goog.a11y.aria.Role}
    + * @private
    + */
    +goog.ui.Control.prototype.preferredAriaRole_ = null;
    +
    +
    +// Event handler and renderer management.
    +
    +
    +/**
    + * Returns true if the control is configured to handle its own mouse events,
    + * false otherwise.  Controls not hosted in {@link goog.ui.Container}s have
    + * to handle their own mouse events, but controls hosted in containers may
    + * allow their parent to handle mouse events on their behalf.  Considered
    + * protected; should only be used within this package and by subclasses.
    + * @return {boolean} Whether the control handles its own mouse events.
    + */
    +goog.ui.Control.prototype.isHandleMouseEvents = function() {
    +  return this.handleMouseEvents_;
    +};
    +
    +
    +/**
    + * Enables or disables mouse event handling for the control.  Containers may
    + * use this method to disable mouse event handling in their child controls.
    + * Considered protected; should only be used within this package and by
    + * subclasses.
    + * @param {boolean} enable Whether to enable or disable mouse event handling.
    + */
    +goog.ui.Control.prototype.setHandleMouseEvents = function(enable) {
    +  if (this.isInDocument() && enable != this.handleMouseEvents_) {
    +    // Already in the document; need to update event handler.
    +    this.enableMouseEventHandling_(enable);
    +  }
    +  this.handleMouseEvents_ = enable;
    +};
    +
    +
    +/**
    + * Returns the DOM element on which the control is listening for keyboard
    + * events (null if none).
    + * @return {Element} Element on which the control is listening for key
    + *     events.
    + */
    +goog.ui.Control.prototype.getKeyEventTarget = function() {
    +  // Delegate to renderer.
    +  return this.renderer_.getKeyEventTarget(this);
    +};
    +
    +
    +/**
    + * Returns the keyboard event handler for this component, lazily created the
    + * first time this method is called.  Considered protected; should only be
    + * used within this package and by subclasses.
    + * @return {!goog.events.KeyHandler} Keyboard event handler for this component.
    + * @protected
    + */
    +goog.ui.Control.prototype.getKeyHandler = function() {
    +  return this.keyHandler_ || (this.keyHandler_ = new goog.events.KeyHandler());
    +};
    +
    +
    +/**
    + * Returns the renderer used by this component to render itself or to decorate
    + * an existing element.
    + * @return {goog.ui.ControlRenderer|undefined} Renderer used by the component
    + *     (undefined if none).
    + */
    +goog.ui.Control.prototype.getRenderer = function() {
    +  return this.renderer_;
    +};
    +
    +
    +/**
    + * Registers the given renderer with the component.  Changing renderers after
    + * the component has entered the document is an error.
    + * @param {goog.ui.ControlRenderer} renderer Renderer used by the component.
    + * @throws {Error} If the control is already in the document.
    + */
    +goog.ui.Control.prototype.setRenderer = function(renderer) {
    +  if (this.isInDocument()) {
    +    // Too late.
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  if (this.getElement()) {
    +    // The component has already been rendered, but isn't yet in the document.
    +    // Replace the renderer and delete the current DOM, so it can be re-rendered
    +    // using the new renderer the next time someone calls render().
    +    this.setElementInternal(null);
    +  }
    +
    +  this.renderer_ = renderer;
    +};
    +
    +
    +// Support for additional styling.
    +
    +
    +/**
    + * Returns any additional class name(s) to be applied to the component's
    + * root element, or null if no extra class names are needed.
    + * @return {Array<string>?} Additional class names to be applied to
    + *     the component's root element (null if none).
    + */
    +goog.ui.Control.prototype.getExtraClassNames = function() {
    +  return this.extraClassNames_;
    +};
    +
    +
    +/**
    + * Adds the given class name to the list of classes to be applied to the
    + * component's root element.
    + * @param {string} className Additional class name to be applied to the
    + *     component's root element.
    + */
    +goog.ui.Control.prototype.addClassName = function(className) {
    +  if (className) {
    +    if (this.extraClassNames_) {
    +      if (!goog.array.contains(this.extraClassNames_, className)) {
    +        this.extraClassNames_.push(className);
    +      }
    +    } else {
    +      this.extraClassNames_ = [className];
    +    }
    +    this.renderer_.enableExtraClassName(this, className, true);
    +  }
    +};
    +
    +
    +/**
    + * Removes the given class name from the list of classes to be applied to
    + * the component's root element.
    + * @param {string} className Class name to be removed from the component's root
    + *     element.
    + */
    +goog.ui.Control.prototype.removeClassName = function(className) {
    +  if (className && this.extraClassNames_ &&
    +      goog.array.remove(this.extraClassNames_, className)) {
    +    if (this.extraClassNames_.length == 0) {
    +      this.extraClassNames_ = null;
    +    }
    +    this.renderer_.enableExtraClassName(this, className, false);
    +  }
    +};
    +
    +
    +/**
    + * Adds or removes the given class name to/from the list of classes to be
    + * applied to the component's root element.
    + * @param {string} className CSS class name to add or remove.
    + * @param {boolean} enable Whether to add or remove the class name.
    + */
    +goog.ui.Control.prototype.enableClassName = function(className, enable) {
    +  if (enable) {
    +    this.addClassName(className);
    +  } else {
    +    this.removeClassName(className);
    +  }
    +};
    +
    +
    +// Standard goog.ui.Component implementation.
    +
    +
    +/**
    + * Creates the control's DOM.  Overrides {@link goog.ui.Component#createDom} by
    + * delegating DOM manipulation to the control's renderer.
    + * @override
    + */
    +goog.ui.Control.prototype.createDom = function() {
    +  var element = this.renderer_.createDom(this);
    +  this.setElementInternal(element);
    +
    +  // Initialize ARIA role.
    +  this.renderer_.setAriaRole(element, this.getPreferredAriaRole());
    +
    +  // Initialize text selection.
    +  if (!this.isAllowTextSelection()) {
    +    // The renderer is assumed to create selectable elements.  Since making
    +    // elements unselectable is expensive, only do it if needed (bug 1037090).
    +    this.renderer_.setAllowTextSelection(element, false);
    +  }
    +
    +  // Initialize visibility.
    +  if (!this.isVisible()) {
    +    // The renderer is assumed to create visible elements. Since hiding
    +    // elements can be expensive, only do it if needed (bug 1037105).
    +    this.renderer_.setVisible(element, false);
    +  }
    +};
    +
    +
    +/**
    + * Returns the control's preferred ARIA role. This can be used by a control to
    + * override the role that would be assigned by the renderer.  This is useful in
    + * cases where a different ARIA role is appropriate for a control because of the
    + * context in which it's used.  E.g., a {@link goog.ui.MenuButton} added to a
    + * {@link goog.ui.Select} should have an ARIA role of LISTBOX and not MENUITEM.
    + * @return {?goog.a11y.aria.Role} This control's preferred ARIA role or null if
    + *     no preferred ARIA role is set.
    + */
    +goog.ui.Control.prototype.getPreferredAriaRole = function() {
    +  return this.preferredAriaRole_;
    +};
    +
    +
    +/**
    + * Sets the control's preferred ARIA role. This can be used to override the role
    + * that would be assigned by the renderer.  This is useful in cases where a
    + * different ARIA role is appropriate for a control because of the
    + * context in which it's used.  E.g., a {@link goog.ui.MenuButton} added to a
    + * {@link goog.ui.Select} should have an ARIA role of LISTBOX and not MENUITEM.
    + * @param {goog.a11y.aria.Role} role This control's preferred ARIA role.
    + */
    +goog.ui.Control.prototype.setPreferredAriaRole = function(role) {
    +  this.preferredAriaRole_ = role;
    +};
    +
    +
    +/**
    + * Gets the control's aria label.
    + * @return {?string} This control's aria label.
    + */
    +goog.ui.Control.prototype.getAriaLabel = function() {
    +  return this.ariaLabel_;
    +};
    +
    +
    +/**
    + * Sets the control's aria label. This can be used to assign aria label to the
    + * element after it is rendered.
    + * @param {string} label The string to set as the aria label for this control.
    + *     No escaping is done on this value.
    + */
    +goog.ui.Control.prototype.setAriaLabel = function(label) {
    +  this.ariaLabel_ = label;
    +  var element = this.getElement();
    +  if (element) {
    +    this.renderer_.setAriaLabel(element, label);
    +  }
    +};
    +
    +
    +/**
    + * Returns the DOM element into which child components are to be rendered,
    + * or null if the control itself hasn't been rendered yet.  Overrides
    + * {@link goog.ui.Component#getContentElement} by delegating to the renderer.
    + * @return {Element} Element to contain child elements (null if none).
    + * @override
    + */
    +goog.ui.Control.prototype.getContentElement = function() {
    +  // Delegate to renderer.
    +  return this.renderer_.getContentElement(this.getElement());
    +};
    +
    +
    +/**
    + * Returns true if the given element can be decorated by this component.
    + * Overrides {@link goog.ui.Component#canDecorate}.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the element can be decorated by this component.
    + * @override
    + */
    +goog.ui.Control.prototype.canDecorate = function(element) {
    +  // Controls support pluggable renderers; delegate to the renderer.
    +  return this.renderer_.canDecorate(element);
    +};
    +
    +
    +/**
    + * Decorates the given element with this component. Overrides {@link
    + * goog.ui.Component#decorateInternal} by delegating DOM manipulation
    + * to the control's renderer.
    + * @param {Element} element Element to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.Control.prototype.decorateInternal = function(element) {
    +  element = this.renderer_.decorate(this, element);
    +  this.setElementInternal(element);
    +
    +  // Initialize ARIA role.
    +  this.renderer_.setAriaRole(element, this.getPreferredAriaRole());
    +
    +  // Initialize text selection.
    +  if (!this.isAllowTextSelection()) {
    +    // Decorated elements are assumed to be selectable.  Since making elements
    +    // unselectable is expensive, only do it if needed (bug 1037090).
    +    this.renderer_.setAllowTextSelection(element, false);
    +  }
    +
    +  // Initialize visibility based on the decorated element's styling.
    +  this.visible_ = element.style.display != 'none';
    +};
    +
    +
    +/**
    + * Configures the component after its DOM has been rendered, and sets up event
    + * handling.  Overrides {@link goog.ui.Component#enterDocument}.
    + * @override
    + */
    +goog.ui.Control.prototype.enterDocument = function() {
    +  goog.ui.Control.superClass_.enterDocument.call(this);
    +
    +  // Call the renderer's setAriaStates method to set element's aria attributes.
    +  this.renderer_.setAriaStates(this, this.getElementStrict());
    +
    +  // Call the renderer's initializeDom method to configure properties of the
    +  // control's DOM that can only be done once it's in the document.
    +  this.renderer_.initializeDom(this);
    +
    +  // Initialize event handling if at least one state other than DISABLED is
    +  // supported.
    +  if (this.supportedStates_ & ~goog.ui.Component.State.DISABLED) {
    +    // Initialize mouse event handling if the control is configured to handle
    +    // its own mouse events.  (Controls hosted in containers don't need to
    +    // handle their own mouse events.)
    +    if (this.isHandleMouseEvents()) {
    +      this.enableMouseEventHandling_(true);
    +    }
    +
    +    // Initialize keyboard event handling if the control is focusable and has
    +    // a key event target.  (Controls hosted in containers typically aren't
    +    // focusable, allowing their container to handle keyboard events for them.)
    +    if (this.isSupportedState(goog.ui.Component.State.FOCUSED)) {
    +      var keyTarget = this.getKeyEventTarget();
    +      if (keyTarget) {
    +        var keyHandler = this.getKeyHandler();
    +        keyHandler.attach(keyTarget);
    +        this.getHandler().
    +            listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
    +                this.handleKeyEvent).
    +            listen(keyTarget, goog.events.EventType.FOCUS,
    +                this.handleFocus).
    +            listen(keyTarget, goog.events.EventType.BLUR,
    +                this.handleBlur);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Enables or disables mouse event handling on the control.
    + * @param {boolean} enable Whether to enable mouse event handling.
    + * @private
    + */
    +goog.ui.Control.prototype.enableMouseEventHandling_ = function(enable) {
    +  var handler = this.getHandler();
    +  var element = this.getElement();
    +  if (enable) {
    +    handler.
    +        listen(element, goog.events.EventType.MOUSEOVER, this.handleMouseOver).
    +        listen(element, goog.events.EventType.MOUSEDOWN, this.handleMouseDown).
    +        listen(element, goog.events.EventType.MOUSEUP, this.handleMouseUp).
    +        listen(element, goog.events.EventType.MOUSEOUT, this.handleMouseOut);
    +    if (this.handleContextMenu != goog.nullFunction) {
    +      handler.listen(element, goog.events.EventType.CONTEXTMENU,
    +          this.handleContextMenu);
    +    }
    +    if (goog.userAgent.IE) {
    +      handler.listen(element, goog.events.EventType.DBLCLICK,
    +          this.handleDblClick);
    +    }
    +  } else {
    +    handler.
    +        unlisten(element, goog.events.EventType.MOUSEOVER,
    +            this.handleMouseOver).
    +        unlisten(element, goog.events.EventType.MOUSEDOWN,
    +            this.handleMouseDown).
    +        unlisten(element, goog.events.EventType.MOUSEUP, this.handleMouseUp).
    +        unlisten(element, goog.events.EventType.MOUSEOUT, this.handleMouseOut);
    +    if (this.handleContextMenu != goog.nullFunction) {
    +      handler.unlisten(element, goog.events.EventType.CONTEXTMENU,
    +          this.handleContextMenu);
    +    }
    +    if (goog.userAgent.IE) {
    +      handler.unlisten(element, goog.events.EventType.DBLCLICK,
    +          this.handleDblClick);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Cleans up the component before its DOM is removed from the document, and
    + * removes event handlers.  Overrides {@link goog.ui.Component#exitDocument}
    + * by making sure that components that are removed from the document aren't
    + * focusable (i.e. have no tab index).
    + * @override
    + */
    +goog.ui.Control.prototype.exitDocument = function() {
    +  goog.ui.Control.superClass_.exitDocument.call(this);
    +  if (this.keyHandler_) {
    +    this.keyHandler_.detach();
    +  }
    +  if (this.isVisible() && this.isEnabled()) {
    +    this.renderer_.setFocusable(this, false);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Control.prototype.disposeInternal = function() {
    +  goog.ui.Control.superClass_.disposeInternal.call(this);
    +  if (this.keyHandler_) {
    +    this.keyHandler_.dispose();
    +    delete this.keyHandler_;
    +  }
    +  delete this.renderer_;
    +  this.content_ = null;
    +  this.extraClassNames_ = null;
    +};
    +
    +
    +// Component content management.
    +
    +
    +/**
    + * Returns the text caption or DOM structure displayed in the component.
    + * @return {goog.ui.ControlContent} Text caption or DOM structure
    + *     comprising the component's contents.
    + */
    +goog.ui.Control.prototype.getContent = function() {
    +  return this.content_;
    +};
    +
    +
    +/**
    + * Sets the component's content to the given text caption, element, or array of
    + * nodes.  (If the argument is an array of nodes, it must be an actual array,
    + * not an array-like object.)
    + * @param {goog.ui.ControlContent} content Text caption or DOM
    + *     structure to set as the component's contents.
    + */
    +goog.ui.Control.prototype.setContent = function(content) {
    +  // Controls support pluggable renderers; delegate to the renderer.
    +  this.renderer_.setContent(this.getElement(), content);
    +
    +  // setContentInternal needs to be after the renderer, since the implementation
    +  // may depend on the content being in the DOM.
    +  this.setContentInternal(content);
    +};
    +
    +
    +/**
    + * Sets the component's content to the given text caption, element, or array
    + * of nodes.  Unlike {@link #setContent}, doesn't modify the component's DOM.
    + * Called by renderers during element decoration.
    + *
    + * This should only be used by subclasses and its associated renderers.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure
    + *     to set as the component's contents.
    + */
    +goog.ui.Control.prototype.setContentInternal = function(content) {
    +  this.content_ = content;
    +};
    +
    +
    +/**
    + * @return {string} Text caption of the control or empty string if none.
    + */
    +goog.ui.Control.prototype.getCaption = function() {
    +  var content = this.getContent();
    +  if (!content) {
    +    return '';
    +  }
    +  var caption =
    +      goog.isString(content) ? content :
    +      goog.isArray(content) ? goog.array.map(content,
    +          goog.dom.getRawTextContent).join('') :
    +      goog.dom.getTextContent(/** @type {!Node} */ (content));
    +  return goog.string.collapseBreakingSpaces(caption);
    +};
    +
    +
    +/**
    + * Sets the text caption of the component.
    + * @param {string} caption Text caption of the component.
    + */
    +goog.ui.Control.prototype.setCaption = function(caption) {
    +  this.setContent(caption);
    +};
    +
    +
    +// Component state management.
    +
    +
    +/** @override */
    +goog.ui.Control.prototype.setRightToLeft = function(rightToLeft) {
    +  // The superclass implementation ensures the control isn't in the document.
    +  goog.ui.Control.superClass_.setRightToLeft.call(this, rightToLeft);
    +
    +  var element = this.getElement();
    +  if (element) {
    +    this.renderer_.setRightToLeft(element, rightToLeft);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the control allows text selection within its DOM, false
    + * otherwise.  Controls that disallow text selection have the appropriate
    + * unselectable styling applied to their elements.  Note that controls hosted
    + * in containers will report that they allow text selection even if their
    + * container disallows text selection.
    + * @return {boolean} Whether the control allows text selection.
    + */
    +goog.ui.Control.prototype.isAllowTextSelection = function() {
    +  return this.allowTextSelection_;
    +};
    +
    +
    +/**
    + * Allows or disallows text selection within the control's DOM.
    + * @param {boolean} allow Whether the control should allow text selection.
    + */
    +goog.ui.Control.prototype.setAllowTextSelection = function(allow) {
    +  this.allowTextSelection_ = allow;
    +
    +  var element = this.getElement();
    +  if (element) {
    +    this.renderer_.setAllowTextSelection(element, allow);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component's visibility is set to visible, false if
    + * it is set to hidden.  A component that is set to hidden is guaranteed
    + * to be hidden from the user, but the reverse isn't necessarily true.
    + * A component may be set to visible but can otherwise be obscured by another
    + * element, rendered off-screen, or hidden using direct CSS manipulation.
    + * @return {boolean} Whether the component is visible.
    + */
    +goog.ui.Control.prototype.isVisible = function() {
    +  return this.visible_;
    +};
    +
    +
    +/**
    + * Shows or hides the component.  Does nothing if the component already has
    + * the requested visibility.  Otherwise, dispatches a SHOW or HIDE event as
    + * appropriate, giving listeners a chance to prevent the visibility change.
    + * When showing a component that is both enabled and focusable, ensures that
    + * its key target has a tab index.  When hiding a component that is enabled
    + * and focusable, blurs its key target and removes its tab index.
    + * @param {boolean} visible Whether to show or hide the component.
    + * @param {boolean=} opt_force If true, doesn't check whether the component
    + *     already has the requested visibility, and doesn't dispatch any events.
    + * @return {boolean} Whether the visibility was changed.
    + */
    +goog.ui.Control.prototype.setVisible = function(visible, opt_force) {
    +  if (opt_force || (this.visible_ != visible && this.dispatchEvent(visible ?
    +      goog.ui.Component.EventType.SHOW : goog.ui.Component.EventType.HIDE))) {
    +    var element = this.getElement();
    +    if (element) {
    +      this.renderer_.setVisible(element, visible);
    +    }
    +    if (this.isEnabled()) {
    +      this.renderer_.setFocusable(this, visible);
    +    }
    +    this.visible_ = visible;
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns true if the component is enabled, false otherwise.
    + * @return {boolean} Whether the component is enabled.
    + */
    +goog.ui.Control.prototype.isEnabled = function() {
    +  return !this.hasState(goog.ui.Component.State.DISABLED);
    +};
    +
    +
    +/**
    + * Returns true if the control has a parent that is itself disabled, false
    + * otherwise.
    + * @return {boolean} Whether the component is hosted in a disabled container.
    + * @private
    + */
    +goog.ui.Control.prototype.isParentDisabled_ = function() {
    +  var parent = this.getParent();
    +  return !!parent && typeof parent.isEnabled == 'function' &&
    +      !parent.isEnabled();
    +};
    +
    +
    +/**
    + * Enables or disables the component.  Does nothing if this state transition
    + * is disallowed.  If the component is both visible and focusable, updates its
    + * focused state and tab index as needed.  If the component is being disabled,
    + * ensures that it is also deactivated and un-highlighted first.  Note that the
    + * component's enabled/disabled state is "locked" as long as it is hosted in a
    + * {@link goog.ui.Container} that is itself disabled; this is to prevent clients
    + * from accidentally re-enabling a control that is in a disabled container.
    + * @param {boolean} enable Whether to enable or disable the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setEnabled = function(enable) {
    +  if (!this.isParentDisabled_() &&
    +      this.isTransitionAllowed(goog.ui.Component.State.DISABLED, !enable)) {
    +    if (!enable) {
    +      this.setActive(false);
    +      this.setHighlighted(false);
    +    }
    +    if (this.isVisible()) {
    +      this.renderer_.setFocusable(this, enable);
    +    }
    +    this.setState(goog.ui.Component.State.DISABLED, !enable, true);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is currently highlighted, false otherwise.
    + * @return {boolean} Whether the component is highlighted.
    + */
    +goog.ui.Control.prototype.isHighlighted = function() {
    +  return this.hasState(goog.ui.Component.State.HOVER);
    +};
    +
    +
    +/**
    + * Highlights or unhighlights the component.  Does nothing if this state
    + * transition is disallowed.
    + * @param {boolean} highlight Whether to highlight or unhighlight the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setHighlighted = function(highlight) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.HOVER, highlight)) {
    +    this.setState(goog.ui.Component.State.HOVER, highlight);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is active (pressed), false otherwise.
    + * @return {boolean} Whether the component is active.
    + */
    +goog.ui.Control.prototype.isActive = function() {
    +  return this.hasState(goog.ui.Component.State.ACTIVE);
    +};
    +
    +
    +/**
    + * Activates or deactivates the component.  Does nothing if this state
    + * transition is disallowed.
    + * @param {boolean} active Whether to activate or deactivate the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setActive = function(active) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.ACTIVE, active)) {
    +    this.setState(goog.ui.Component.State.ACTIVE, active);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is selected, false otherwise.
    + * @return {boolean} Whether the component is selected.
    + */
    +goog.ui.Control.prototype.isSelected = function() {
    +  return this.hasState(goog.ui.Component.State.SELECTED);
    +};
    +
    +
    +/**
    + * Selects or unselects the component.  Does nothing if this state transition
    + * is disallowed.
    + * @param {boolean} select Whether to select or unselect the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setSelected = function(select) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.SELECTED, select)) {
    +    this.setState(goog.ui.Component.State.SELECTED, select);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is checked, false otherwise.
    + * @return {boolean} Whether the component is checked.
    + */
    +goog.ui.Control.prototype.isChecked = function() {
    +  return this.hasState(goog.ui.Component.State.CHECKED);
    +};
    +
    +
    +/**
    + * Checks or unchecks the component.  Does nothing if this state transition
    + * is disallowed.
    + * @param {boolean} check Whether to check or uncheck the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setChecked = function(check) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.CHECKED, check)) {
    +    this.setState(goog.ui.Component.State.CHECKED, check);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is styled to indicate that it has keyboard
    + * focus, false otherwise.  Note that {@code isFocused()} returning true
    + * doesn't guarantee that the component's key event target has keyborad focus,
    + * only that it is styled as such.
    + * @return {boolean} Whether the component is styled to indicate as having
    + *     keyboard focus.
    + */
    +goog.ui.Control.prototype.isFocused = function() {
    +  return this.hasState(goog.ui.Component.State.FOCUSED);
    +};
    +
    +
    +/**
    + * Applies or removes styling indicating that the component has keyboard focus.
    + * Note that unlike the other "set" methods, this method is called as a result
    + * of the component's element having received or lost keyboard focus, not the
    + * other way around, so calling {@code setFocused(true)} doesn't guarantee that
    + * the component's key event target has keyboard focus, only that it is styled
    + * as such.
    + * @param {boolean} focused Whether to apply or remove styling to indicate that
    + *     the component's element has keyboard focus.
    + */
    +goog.ui.Control.prototype.setFocused = function(focused) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.FOCUSED, focused)) {
    +    this.setState(goog.ui.Component.State.FOCUSED, focused);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the component is open (expanded), false otherwise.
    + * @return {boolean} Whether the component is open.
    + */
    +goog.ui.Control.prototype.isOpen = function() {
    +  return this.hasState(goog.ui.Component.State.OPENED);
    +};
    +
    +
    +/**
    + * Opens (expands) or closes (collapses) the component.  Does nothing if this
    + * state transition is disallowed.
    + * @param {boolean} open Whether to open or close the component.
    + * @see #isTransitionAllowed
    + */
    +goog.ui.Control.prototype.setOpen = function(open) {
    +  if (this.isTransitionAllowed(goog.ui.Component.State.OPENED, open)) {
    +    this.setState(goog.ui.Component.State.OPENED, open);
    +  }
    +};
    +
    +
    +/**
    + * Returns the component's state as a bit mask of {@link
    + * goog.ui.Component.State}s.
    + * @return {number} Bit mask representing component state.
    + */
    +goog.ui.Control.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * Returns true if the component is in the specified state, false otherwise.
    + * @param {goog.ui.Component.State} state State to check.
    + * @return {boolean} Whether the component is in the given state.
    + */
    +goog.ui.Control.prototype.hasState = function(state) {
    +  return !!(this.state_ & state);
    +};
    +
    +
    +/**
    + * Sets or clears the given state on the component, and updates its styling
    + * accordingly.  Does nothing if the component is already in the correct state
    + * or if it doesn't support the specified state.  Doesn't dispatch any state
    + * transition events; use advisedly.
    + * @param {goog.ui.Component.State} state State to set or clear.
    + * @param {boolean} enable Whether to set or clear the state (if supported).
    + * @param {boolean=} opt_calledFrom Prevents looping with setEnabled.
    + */
    +goog.ui.Control.prototype.setState = function(state, enable, opt_calledFrom) {
    +  if (!opt_calledFrom && state == goog.ui.Component.State.DISABLED) {
    +    this.setEnabled(!enable);
    +    return;
    +  }
    +  if (this.isSupportedState(state) && enable != this.hasState(state)) {
    +    // Delegate actual styling to the renderer, since it is DOM-specific.
    +    this.renderer_.setState(this, state, enable);
    +    this.state_ = enable ? this.state_ | state : this.state_ & ~state;
    +  }
    +};
    +
    +
    +/**
    + * Sets the component's state to the state represented by a bit mask of
    + * {@link goog.ui.Component.State}s.  Unlike {@link #setState}, doesn't
    + * update the component's styling, and doesn't reject unsupported states.
    + * Called by renderers during element decoration.  Considered protected;
    + * should only be used within this package and by subclasses.
    + *
    + * This should only be used by subclasses and its associated renderers.
    + *
    + * @param {number} state Bit mask representing component state.
    + */
    +goog.ui.Control.prototype.setStateInternal = function(state) {
    +  this.state_ = state;
    +};
    +
    +
    +/**
    + * Returns true if the component supports the specified state, false otherwise.
    + * @param {goog.ui.Component.State} state State to check.
    + * @return {boolean} Whether the component supports the given state.
    + */
    +goog.ui.Control.prototype.isSupportedState = function(state) {
    +  return !!(this.supportedStates_ & state);
    +};
    +
    +
    +/**
    + * Enables or disables support for the given state. Disabling support
    + * for a state while the component is in that state is an error.
    + * @param {goog.ui.Component.State} state State to support or de-support.
    + * @param {boolean} support Whether the component should support the state.
    + * @throws {Error} If disabling support for a state the control is currently in.
    + */
    +goog.ui.Control.prototype.setSupportedState = function(state, support) {
    +  if (this.isInDocument() && this.hasState(state) && !support) {
    +    // Since we hook up event handlers in enterDocument(), this is an error.
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  if (!support && this.hasState(state)) {
    +    // We are removing support for a state that the component is currently in.
    +    this.setState(state, false);
    +  }
    +
    +  this.supportedStates_ = support ?
    +      this.supportedStates_ | state : this.supportedStates_ & ~state;
    +};
    +
    +
    +/**
    + * Returns true if the component provides default event handling for the state,
    + * false otherwise.
    + * @param {goog.ui.Component.State} state State to check.
    + * @return {boolean} Whether the component provides default event handling for
    + *     the state.
    + */
    +goog.ui.Control.prototype.isAutoState = function(state) {
    +  return !!(this.autoStates_ & state) && this.isSupportedState(state);
    +};
    +
    +
    +/**
    + * Enables or disables automatic event handling for the given state(s).
    + * @param {number} states Bit mask of {@link goog.ui.Component.State}s for which
    + *     default event handling is to be enabled or disabled.
    + * @param {boolean} enable Whether the component should provide default event
    + *     handling for the state(s).
    + */
    +goog.ui.Control.prototype.setAutoStates = function(states, enable) {
    +  this.autoStates_ = enable ?
    +      this.autoStates_ | states : this.autoStates_ & ~states;
    +};
    +
    +
    +/**
    + * Returns true if the component is set to dispatch transition events for the
    + * given state, false otherwise.
    + * @param {goog.ui.Component.State} state State to check.
    + * @return {boolean} Whether the component dispatches transition events for
    + *     the state.
    + */
    +goog.ui.Control.prototype.isDispatchTransitionEvents = function(state) {
    +  return !!(this.statesWithTransitionEvents_ & state) &&
    +      this.isSupportedState(state);
    +};
    +
    +
    +/**
    + * Enables or disables transition events for the given state(s).  Controls
    + * handle state transitions internally by default, and only dispatch state
    + * transition events if explicitly requested to do so by calling this method.
    + * @param {number} states Bit mask of {@link goog.ui.Component.State}s for
    + *     which transition events should be enabled or disabled.
    + * @param {boolean} enable Whether transition events should be enabled.
    + */
    +goog.ui.Control.prototype.setDispatchTransitionEvents = function(states,
    +    enable) {
    +  this.statesWithTransitionEvents_ = enable ?
    +      this.statesWithTransitionEvents_ | states :
    +      this.statesWithTransitionEvents_ & ~states;
    +};
    +
    +
    +/**
    + * Returns true if the transition into or out of the given state is allowed to
    + * proceed, false otherwise.  A state transition is allowed under the following
    + * conditions:
    + * <ul>
    + *   <li>the component supports the state,
    + *   <li>the component isn't already in the target state,
    + *   <li>either the component is configured not to dispatch events for this
    + *       state transition, or a transition event was dispatched and wasn't
    + *       canceled by any event listener, and
    + *   <li>the component hasn't been disposed of
    + * </ul>
    + * Considered protected; should only be used within this package and by
    + * subclasses.
    + * @param {goog.ui.Component.State} state State to/from which the control is
    + *     transitioning.
    + * @param {boolean} enable Whether the control is entering or leaving the state.
    + * @return {boolean} Whether the state transition is allowed to proceed.
    + * @protected
    + */
    +goog.ui.Control.prototype.isTransitionAllowed = function(state, enable) {
    +  return this.isSupportedState(state) &&
    +      this.hasState(state) != enable &&
    +      (!(this.statesWithTransitionEvents_ & state) || this.dispatchEvent(
    +          goog.ui.Component.getStateTransitionEvent(state, enable))) &&
    +      !this.isDisposed();
    +};
    +
    +
    +// Default event handlers, to be overridden in subclasses.
    +
    +
    +/**
    + * Handles mouseover events.  Dispatches an ENTER event; if the event isn't
    + * canceled, the component is enabled, and it supports auto-highlighting,
    + * highlights the component.  Considered protected; should only be used
    + * within this package and by subclasses.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + */
    +goog.ui.Control.prototype.handleMouseOver = function(e) {
    +  // Ignore mouse moves between descendants.
    +  if (!goog.ui.Control.isMouseEventWithinElement_(e, this.getElement()) &&
    +      this.dispatchEvent(goog.ui.Component.EventType.ENTER) &&
    +      this.isEnabled() &&
    +      this.isAutoState(goog.ui.Component.State.HOVER)) {
    +    this.setHighlighted(true);
    +  }
    +};
    +
    +
    +/**
    + * Handles mouseout events.  Dispatches a LEAVE event; if the event isn't
    + * canceled, and the component supports auto-highlighting, deactivates and
    + * un-highlights the component.  Considered protected; should only be used
    + * within this package and by subclasses.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + */
    +goog.ui.Control.prototype.handleMouseOut = function(e) {
    +  if (!goog.ui.Control.isMouseEventWithinElement_(e, this.getElement()) &&
    +      this.dispatchEvent(goog.ui.Component.EventType.LEAVE)) {
    +    if (this.isAutoState(goog.ui.Component.State.ACTIVE)) {
    +      // Deactivate on mouseout; otherwise we lose track of the mouse button.
    +      this.setActive(false);
    +    }
    +    if (this.isAutoState(goog.ui.Component.State.HOVER)) {
    +      this.setHighlighted(false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles contextmenu events.
    + * @param {goog.events.BrowserEvent} e Event to handle.
    + */
    +goog.ui.Control.prototype.handleContextMenu = goog.nullFunction;
    +
    +
    +/**
    + * Checks if a mouse event (mouseover or mouseout) occured below an element.
    + * @param {goog.events.BrowserEvent} e Mouse event (should be mouseover or
    + *     mouseout).
    + * @param {Element} elem The ancestor element.
    + * @return {boolean} Whether the event has a relatedTarget (the element the
    + *     mouse is coming from) and it's a descendent of elem.
    + * @private
    + */
    +goog.ui.Control.isMouseEventWithinElement_ = function(e, elem) {
    +  // If relatedTarget is null, it means there was no previous element (e.g.
    +  // the mouse moved out of the window).  Assume this means that the mouse
    +  // event was not within the element.
    +  return !!e.relatedTarget && goog.dom.contains(elem, e.relatedTarget);
    +};
    +
    +
    +/**
    + * Handles mousedown events.  If the component is enabled, highlights and
    + * activates it.  If the component isn't configured for keyboard access,
    + * prevents it from receiving keyboard focus.  Considered protected; should
    + * only be used within this package and by subclasses.
    + * @param {goog.events.Event} e Mouse event to handle.
    + */
    +goog.ui.Control.prototype.handleMouseDown = function(e) {
    +  if (this.isEnabled()) {
    +    // Highlight enabled control on mousedown, regardless of the mouse button.
    +    if (this.isAutoState(goog.ui.Component.State.HOVER)) {
    +      this.setHighlighted(true);
    +    }
    +
    +    // For the left button only, activate the control, and focus its key event
    +    // target (if supported).
    +    if (e.isMouseActionButton()) {
    +      if (this.isAutoState(goog.ui.Component.State.ACTIVE)) {
    +        this.setActive(true);
    +      }
    +      if (this.renderer_.isFocusable(this)) {
    +        this.getKeyEventTarget().focus();
    +      }
    +    }
    +  }
    +
    +  // Cancel the default action unless the control allows text selection.
    +  if (!this.isAllowTextSelection() && e.isMouseActionButton()) {
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Handles mouseup events.  If the component is enabled, highlights it.  If
    + * the component has previously been activated, performs its associated action
    + * by calling {@link performActionInternal}, then deactivates it.  Considered
    + * protected; should only be used within this package and by subclasses.
    + * @param {goog.events.Event} e Mouse event to handle.
    + */
    +goog.ui.Control.prototype.handleMouseUp = function(e) {
    +  if (this.isEnabled()) {
    +    if (this.isAutoState(goog.ui.Component.State.HOVER)) {
    +      this.setHighlighted(true);
    +    }
    +    if (this.isActive() &&
    +        this.performActionInternal(e) &&
    +        this.isAutoState(goog.ui.Component.State.ACTIVE)) {
    +      this.setActive(false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles dblclick events.  Should only be registered if the user agent is
    + * IE.  If the component is enabled, performs its associated action by calling
    + * {@link performActionInternal}.  This is used to allow more performant
    + * buttons in IE.  In IE, no mousedown event is fired when that mousedown will
    + * trigger a dblclick event.  Because of this, a user clicking quickly will
    + * only cause ACTION events to fire on every other click.  This is a workaround
    + * to generate ACTION events for every click.  Unfortunately, this workaround
    + * won't ever trigger the ACTIVE state.  This is roughly the same behaviour as
    + * if this were a 'button' element with a listener on mouseup.  Considered
    + * protected; should only be used within this package and by subclasses.
    + * @param {goog.events.Event} e Mouse event to handle.
    + */
    +goog.ui.Control.prototype.handleDblClick = function(e) {
    +  if (this.isEnabled()) {
    +    this.performActionInternal(e);
    +  }
    +};
    +
    +
    +/**
    + * Performs the appropriate action when the control is activated by the user.
    + * The default implementation first updates the checked and selected state of
    + * controls that support them, then dispatches an ACTION event.  Considered
    + * protected; should only be used within this package and by subclasses.
    + * @param {goog.events.Event} e Event that triggered the action.
    + * @return {boolean} Whether the action is allowed to proceed.
    + * @protected
    + */
    +goog.ui.Control.prototype.performActionInternal = function(e) {
    +  if (this.isAutoState(goog.ui.Component.State.CHECKED)) {
    +    this.setChecked(!this.isChecked());
    +  }
    +  if (this.isAutoState(goog.ui.Component.State.SELECTED)) {
    +    this.setSelected(true);
    +  }
    +  if (this.isAutoState(goog.ui.Component.State.OPENED)) {
    +    this.setOpen(!this.isOpen());
    +  }
    +
    +  var actionEvent = new goog.events.Event(goog.ui.Component.EventType.ACTION,
    +      this);
    +  if (e) {
    +    actionEvent.altKey = e.altKey;
    +    actionEvent.ctrlKey = e.ctrlKey;
    +    actionEvent.metaKey = e.metaKey;
    +    actionEvent.shiftKey = e.shiftKey;
    +    actionEvent.platformModifierKey = e.platformModifierKey;
    +  }
    +  return this.dispatchEvent(actionEvent);
    +};
    +
    +
    +/**
    + * Handles focus events on the component's key event target element.  If the
    + * component is focusable, updates its state and styling to indicate that it
    + * now has keyboard focus.  Considered protected; should only be used within
    + * this package and by subclasses.  <b>Warning:</b> IE dispatches focus and
    + * blur events asynchronously!
    + * @param {goog.events.Event} e Focus event to handle.
    + */
    +goog.ui.Control.prototype.handleFocus = function(e) {
    +  if (this.isAutoState(goog.ui.Component.State.FOCUSED)) {
    +    this.setFocused(true);
    +  }
    +};
    +
    +
    +/**
    + * Handles blur events on the component's key event target element.  Always
    + * deactivates the component.  In addition, if the component is focusable,
    + * updates its state and styling to indicate that it no longer has keyboard
    + * focus.  Considered protected; should only be used within this package and
    + * by subclasses.  <b>Warning:</b> IE dispatches focus and blur events
    + * asynchronously!
    + * @param {goog.events.Event} e Blur event to handle.
    + */
    +goog.ui.Control.prototype.handleBlur = function(e) {
    +  if (this.isAutoState(goog.ui.Component.State.ACTIVE)) {
    +    this.setActive(false);
    +  }
    +  if (this.isAutoState(goog.ui.Component.State.FOCUSED)) {
    +    this.setFocused(false);
    +  }
    +};
    +
    +
    +/**
    + * Attempts to handle a keyboard event, if the component is enabled and visible,
    + * by calling {@link handleKeyEventInternal}.  Considered protected; should only
    + * be used within this package and by subclasses.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the key event was handled.
    + */
    +goog.ui.Control.prototype.handleKeyEvent = function(e) {
    +  if (this.isVisible() && this.isEnabled() &&
    +      this.handleKeyEventInternal(e)) {
    +    e.preventDefault();
    +    e.stopPropagation();
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Attempts to handle a keyboard event; returns true if the event was handled,
    + * false otherwise.  Considered protected; should only be used within this
    + * package and by subclasses.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} Whether the key event was handled.
    + * @protected
    + */
    +goog.ui.Control.prototype.handleKeyEventInternal = function(e) {
    +  return e.keyCode == goog.events.KeyCodes.ENTER &&
    +      this.performActionInternal(e);
    +};
    +
    +
    +// Register the default renderer for goog.ui.Controls.
    +goog.ui.registry.setDefaultRenderer(goog.ui.Control, goog.ui.ControlRenderer);
    +
    +
    +// Register a decorator factory function for goog.ui.Controls.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.ControlRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Control(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/control_perf.html b/mapbox-gl/closure-library/closure/goog/ui/control_perf.html
    new file mode 100644
    index 0000000000..901195ba1e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/control_perf.html
    @@ -0,0 +1,166 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +Author:  attila@google.com (Attila Bodis)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - goog.ui.Control</title>
    +  <link rel="stylesheet" type="text/css" href="../testing/performancetable.css" />
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.dom');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.jsunit');
    +    goog.require('goog.ui.Control');
    +  </script>
    +</head>
    +<body>
    +  <h1>goog.ui.Control Performance Tests</h1>
    +  <p>
    +    <b>User-agent:</b> <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +  <div id="renderSandbox"></div>
    +  <div id="decorateSandbox"></div>
    +  <script>
    +    // The sandboxen.
    +    var renderSandbox = goog.dom.getElement('renderSandbox');
    +    var decorateSandbox = goog.dom.getElement('decorateSandbox');
    +
    +    // Arrays of rendered/decorated controls (so we can dispose of them).
    +    var renderedControls;
    +    var decoratedControls;
    +
    +    // 0-based index of the control currently being rendered/decorated.
    +    var renderIndex;
    +    var decorateIndex;
    +
    +    // Element currently being decorated.
    +    var elementToDecorate;
    +
    +    // Number of controls to create/decorate per test run.
    +    var SAMPLES_PER_RUN = 100;
    +
    +    // The performance table.
    +    var table;
    +
    +    // Sets up a render test.
    +    function setUpRenderTest() {
    +      renderedControls = [];
    +      renderIndex = 0;
    +    }
    +
    +    // Cleans up after a render test.
    +    function cleanUpAfterRenderTest() {
    +      for (var i = 0, count = renderedControls.length; i < count; i++) {
    +        renderedControls[i].dispose();
    +      }
    +      renderedControls = null;
    +      goog.dom.removeChildren(renderSandbox);
    +    }
    +
    +    // Sets up a decorate test.
    +    function setUpDecorateTest(opt_count) {
    +      var count = opt_count || 1000;
    +      for (var i = 0; i < count; i++) {
    +        decorateSandbox.appendChild(goog.dom.createDom('div', 'goog-control',
    +            'W00t!'));
    +      }
    +      elementToDecorate = decorateSandbox.firstChild;
    +      decoratedControls = [];
    +      decorateIndex = 0;
    +    }
    +
    +    // Cleans up after a decorate test.
    +    function cleanUpAfterDecorateTest() {
    +      for (var i = 0, count = decoratedControls.length; i < count; i++) {
    +        decoratedControls[i].dispose();
    +      }
    +      decoratedControls = null;
    +      goog.dom.removeChildren(decorateSandbox);
    +    }
    +
    +    // Renders the given number of controls.  Since children are appended to
    +    // the same parent element in each performance test run, we keep track of
    +    // the current index via the global renderIndex variable.
    +    function renderControls(count, autoDetectBiDi) {
    +      for (var i = 0; i < count; i++) {
    +        var control = new goog.ui.Control('W00t!');
    +        if (!autoDetectBiDi) {
    +          control.setRightToLeft(false);
    +        }
    +        control.render(renderSandbox);
    +        renderedControls[renderIndex++] = control;
    +      }
    +    }
    +
    +    // Decorates "count" controls.  The decorate sandbox contains enough child
    +    // elements for the whole test, but we only decorate up to "count" elements
    +    // per test run, so we need to keep track of where we are via the global
    +    // decorateIndex and elementToDecorate variables.
    +    function decorateControls(count, autoDetectBiDi) {
    +      for (var i = 0; i < count; i++) {
    +        var next = elementToDecorate.nextSibling;
    +        var control = new goog.ui.Control();
    +        if (!autoDetectBiDi) {
    +          control.setRightToLeft(false);
    +        }
    +        control.decorate(elementToDecorate);
    +        decoratedControls[decorateIndex++] = control;
    +        elementToDecorate = next;
    +      }
    +    }
    +
    +    function setUpPage() {
    +      table = new goog.testing.PerformanceTable(
    +          goog.dom.getElement('perfTable'));
    +    }
    +
    +    function testRender() {
    +      setUpRenderTest();
    +      table.run(goog.partial(renderControls, SAMPLES_PER_RUN, true),
    +          'Render ' + SAMPLES_PER_RUN + ' controls (default)');
    +      cleanUpAfterRenderTest();
    +      assertEquals('The expected number of controls must have been rendered',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(), renderIndex);
    +    }
    +
    +    function testDecorate() {
    +      setUpDecorateTest(SAMPLES_PER_RUN * table.getTimer().getNumSamples());
    +      table.run(goog.partial(decorateControls, SAMPLES_PER_RUN, true),
    +          'Decorate ' + SAMPLES_PER_RUN + ' controls (default)');
    +      cleanUpAfterDecorateTest();
    +      assertEquals('The expected number of controls must have been decorated',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(), decorateIndex);
    +      assertNull('All controls must have been decorated', elementToDecorate);
    +    }
    +
    +    function testRenderNoBiDiAutoDetect() {
    +      setUpRenderTest();
    +      table.run(goog.partial(renderControls, SAMPLES_PER_RUN, false),
    +          'Render ' + SAMPLES_PER_RUN + ' controls (no BiDi auto-detect)');
    +      cleanUpAfterRenderTest();
    +      assertEquals('The expected number of controls must have been rendered',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(), renderIndex);
    +    }
    +
    +    function testDecorateNoBiDiAutoDetect() {
    +      setUpDecorateTest(SAMPLES_PER_RUN * table.getTimer().getNumSamples());
    +      table.run(goog.partial(decorateControls, SAMPLES_PER_RUN, false),
    +          'Decorate ' + SAMPLES_PER_RUN + ' controls (no BiDi auto-detect)');
    +      cleanUpAfterDecorateTest();
    +      assertEquals('The expected number of controls must have been decorated',
    +          SAMPLES_PER_RUN * table.getTimer().getNumSamples(), decorateIndex);
    +      assertNull('All controls must have been decorated', elementToDecorate);
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/control_test.html b/mapbox-gl/closure-library/closure/goog/ui/control_test.html
    new file mode 100644
    index 0000000000..0e045ff57c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/control_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Control
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ControlTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/control_test.js b/mapbox-gl/closure-library/closure/goog/ui/control_test.js
    new file mode 100644
    index 0000000000..75b01fd1be
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/control_test.js
    @@ -0,0 +1,2383 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ControlTest');
    +goog.setTestOnly('goog.ui.ControlTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.ui.registry');
    +goog.require('goog.userAgent');
    +
    +// Disabled due to problems on farm.
    +var testFocus = false;
    +
    +var control;
    +
    +var ALL_EVENTS = goog.object.getValues(goog.ui.Component.EventType);
    +var events = {};
    +var expectedFailures;
    +var sandbox;
    +var aria = goog.a11y.aria;
    +var State = goog.a11y.aria.State;
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +  sandbox = document.getElementById('sandbox');
    +}
    +
    +
    +
    +/**
    + * A dummy renderer, for testing purposes.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +function TestRenderer() {
    +  goog.ui.ControlRenderer.call(this);
    +}
    +goog.inherits(TestRenderer, goog.ui.ControlRenderer);
    +
    +
    +/**
    + * Initializes the testcase prior to execution.
    + */
    +function setUp() {
    +  control = new goog.ui.Control('Hello');
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.ALL, true);
    +  goog.events.listen(control, ALL_EVENTS, countEvent);
    +}
    +
    +
    +/**
    + * Cleans up after executing the testcase.
    + */
    +function tearDown() {
    +  control.dispose();
    +  expectedFailures.handleTearDown();
    +  goog.dom.removeChildren(sandbox);
    +  resetEventCount();
    +}
    +
    +
    +/**
    + * Resets the global counter for events dispatched by test objects.
    + */
    +function resetEventCount() {
    +  goog.object.clear(events);
    +}
    +
    +
    +/**
    + * Increments the global counter for events of this type.
    + * @param {goog.events.Event} e Event to count.
    + */
    +function countEvent(e) {
    +  var type = e.type;
    +  var target = e.target;
    +
    +  if (!events[target]) {
    +    events[target] = {};
    +  }
    +
    +  if (events[target][type]) {
    +    events[target][type]++;
    +  } else {
    +    events[target][type] = 1;
    +  }
    +}
    +
    +
    +/**
    + * Returns the number of times test objects dispatched events of the given
    + * type since the global counter was last reset.
    + * @param {goog.ui.Control} target Event target.
    + * @param {string} type Event type.
    + * @return {number} Number of events of this type.
    + */
    +function getEventCount(target, type) {
    +  return events[target] && events[target][type] || 0;
    +}
    +
    +
    +/**
    + * Returns true if no events were dispatched since the last reset.
    + * @return {boolean} Whether no events have been dispatched since the last
    + *     reset.
    + */
    +function noEventsDispatched() {
    +  return !events || goog.object.isEmpty(events);
    +}
    +
    +
    +/**
    + * Returns the number of event listeners created by the control.
    + * @param {goog.ui.Control} control Control whose event listers are to be
    + *     counted.
    + * @return {number} Number of event listeners.
    + */
    +function getListenerCount(control) {
    +  return control.googUiComponentHandler_ ?
    +      goog.object.getCount(control.getHandler().keys_) : 0;
    +}
    +
    +
    +/**
    + * Simulates a mousedown event on the given element, including focusing it.
    + * @param {Element} element Element on which to simulate mousedown.
    + * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
    + *     defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
    + * @return {boolean} Whether the event was allowed to proceed.
    + */
    +function fireMouseDownAndFocus(element, opt_button) {
    +  var result = goog.testing.events.fireMouseDownEvent(element, opt_button);
    +  if (result) {
    +    // Browsers move focus for all buttons, not just the left button.
    +    element.focus();
    +  }
    +  return result;
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on Mac Safari 3.x.
    + */
    +function isMacSafari3() {
    +  return goog.userAgent.WEBKIT && goog.userAgent.MAC &&
    +      !goog.userAgent.isVersionOrHigher('527');
    +}
    +
    +
    +/**
    + * Tests the {@link goog.ui.Control} constructor.
    + */
    +function testConstructor() {
    +  assertNotNull('Constructed control must not be null', control);
    +  assertEquals('Content must have expected value', 'Hello',
    +      control.getContent());
    +  assertEquals('Renderer must default to the registered renderer',
    +      goog.ui.registry.getDefaultRenderer(goog.ui.Control),
    +      control.getRenderer());
    +
    +  var content = goog.dom.createDom('div', null, 'Hello',
    +      goog.dom.createDom('b', null, 'World'));
    +  var testRenderer = new TestRenderer();
    +  var fakeDomHelper = {};
    +  var foo = new goog.ui.Control(content, testRenderer, fakeDomHelper);
    +  assertNotNull('Constructed object must not be null', foo);
    +  assertEquals('Content must have expected value', content,
    +      foo.getContent());
    +  assertEquals('Renderer must have expected value', testRenderer,
    +      foo.getRenderer());
    +  assertEquals('DOM helper must have expected value', fakeDomHelper,
    +      foo.getDomHelper());
    +  foo.dispose();
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getHandler}.
    + */
    +function testGetHandler() {
    +  assertUndefined('Event handler must be undefined before getHandler() ' +
    +      'is called', control.googUiComponentHandler_);
    +  var handler = control.getHandler();
    +  assertNotNull('Event handler must not be null', handler);
    +  assertEquals('getHandler() must return the same instance if called again',
    +      handler, control.getHandler());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isHandleMouseEvents}.
    + */
    +function testIsHandleMouseEvents() {
    +  assertTrue('Controls must handle their own mouse events by default',
    +      control.isHandleMouseEvents());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setHandleMouseEvents}.
    + */
    +function testSetHandleMouseEvents() {
    +  assertTrue('Control must handle its own mouse events by default',
    +      control.isHandleMouseEvents());
    +  control.setHandleMouseEvents(false);
    +  assertFalse('Control must no longer handle its own mouse events',
    +      control.isHandleMouseEvents());
    +  control.setHandleMouseEvents(true);
    +  assertTrue('Control must once again handle its own mouse events',
    +      control.isHandleMouseEvents());
    +  control.render(sandbox);
    +  assertTrue('Rendered control must handle its own mouse events',
    +      control.isHandleMouseEvents());
    +  control.setHandleMouseEvents(false);
    +  assertFalse('Rendered control must no longer handle its own mouse events',
    +      control.isHandleMouseEvents());
    +  control.setHandleMouseEvents(true);
    +  assertTrue('Rendered control must once again handle its own mouse events',
    +      control.isHandleMouseEvents());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getKeyEventTarget}.
    + */
    +function testGetKeyEventTarget() {
    +  assertNull('Key event target of control without DOM must be null',
    +      control.getKeyEventTarget());
    +  control.createDom();
    +  assertEquals('Key event target of control with DOM must be its element',
    +      control.getElement(), control.getKeyEventTarget());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getKeyHandler}.
    + */
    +function testGetKeyHandler() {
    +  assertUndefined('Key handler must be undefined before getKeyHandler() ' +
    +      'is called', control.keyHandler_);
    +  var keyHandler = control.getKeyHandler();
    +  assertNotNull('Key handler must not be null', keyHandler);
    +  assertEquals('getKeyHandler() must return the same instance if called ' +
    +      'again', keyHandler, control.getKeyHandler());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getRenderer}.
    + */
    +function testGetRenderer() {
    +  assertEquals('Renderer must be the default registered renderer',
    +      goog.ui.registry.getDefaultRenderer(goog.ui.Control),
    +      control.getRenderer());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setRenderer}.
    + */
    +function testSetRenderer() {
    +  control.createDom();
    +  assertNotNull('Control must have a DOM', control.getElement());
    +  assertFalse('Control must not be in the document',
    +      control.isInDocument());
    +  assertEquals('Renderer must be the default registered renderer',
    +      goog.ui.registry.getDefaultRenderer(goog.ui.Control),
    +      control.getRenderer());
    +
    +  var testRenderer = new TestRenderer();
    +  control.setRenderer(testRenderer);
    +  assertNull('Control must not have a DOM after its renderer is reset',
    +      control.getElement());
    +  assertFalse('Control still must not be in the document',
    +      control.isInDocument());
    +  assertEquals('Renderer must have expected value', testRenderer,
    +      control.getRenderer());
    +
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +
    +  assertThrows('Resetting the renderer after the control has entered ' +
    +      'the document must throw error',
    +      function() {
    +        control.setRenderer({});
    +      });
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getExtraClassNames}.
    + */
    +function testGetExtraClassNames() {
    +  assertNull('Control must not have any extra class names by default',
    +      control.getExtraClassNames());
    +}
    +
    +
    +/**
    +  * Tests {@link goog.ui.Control#addExtraClassName} and
    +  * {@link goog.ui.Control#removeExtraClassName}.
    +  */
    +function testAddRemoveClassName() {
    +  assertNull('Control must not have any extra class names by default',
    +      control.getExtraClassNames());
    +  control.addClassName('foo');
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['foo'], control.getExtraClassNames());
    +  assertNull('Control must not have a DOM', control.getElement());
    +
    +  control.createDom();
    +  assertSameElements('Control\'s element must have expected class names',
    +      ['goog-control', 'foo'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.addClassName('bar');
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +  assertSameElements('Control\'s element must have expected class names',
    +      ['goog-control', 'foo', 'bar'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.addClassName('bar');
    +  assertArrayEquals('Adding the same class name again must be a no-op',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +  assertSameElements('Adding the same class name again must be a no-op',
    +      ['goog-control', 'foo', 'bar'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.addClassName(null);
    +  assertArrayEquals('Adding null class name must be a no-op',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +  assertSameElements('Adding null class name must be a no-op',
    +      ['goog-control', 'foo', 'bar'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.removeClassName(null);
    +  assertArrayEquals('Removing null class name must be a no-op',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +  assertSameElements('Removing null class name must be a no-op',
    +      ['goog-control', 'foo', 'bar'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.removeClassName('foo');
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['bar'], control.getExtraClassNames());
    +  assertSameElements('Control\'s element must have expected class names',
    +      ['goog-control', 'bar'],
    +      goog.dom.classlist.get(control.getElement()));
    +
    +  control.removeClassName('bar');
    +  assertNull('Control must not have any extra class names',
    +      control.getExtraClassNames());
    +  assertSameElements('Control\'s element must have expected class names',
    +      ['goog-control'],
    +      goog.dom.classlist.get(control.getElement()));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#enableClassName}.
    + */
    +function testEnableClassName() {
    +  assertNull('Control must not have any extra class names by default',
    +      control.getExtraClassNames());
    +
    +  control.enableClassName('foo', true);
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['foo'], control.getExtraClassNames());
    +
    +  control.enableClassName('bar', true);
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +
    +  control.enableClassName('bar', true);
    +  assertArrayEquals('Enabling the same class name again must be a no-op',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +
    +  control.enableClassName(null);
    +  assertArrayEquals('Enabling null class name must be a no-op',
    +      ['foo', 'bar'], control.getExtraClassNames());
    +
    +  control.enableClassName('foo', false);
    +  assertArrayEquals('Control must have expected extra class names',
    +      ['bar'], control.getExtraClassNames());
    +
    +  control.enableClassName('bar', false);
    +  assertNull('Control must not have any extra class names',
    +      control.getExtraClassNames());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#createDom}.
    + */
    +function testCreateDom() {
    +  assertNull('Control must not have a DOM by default',
    +      control.getElement());
    +  assertFalse('Control must not allow text selection by default',
    +      control.isAllowTextSelection());
    +  assertTrue('Control must be visible by default', control.isVisible());
    +
    +  control.createDom();
    +  assertNotNull('Control must have a DOM', control.getElement());
    +  assertTrue('Control\'s element must be unselectable',
    +      goog.style.isUnselectable(control.getElement()));
    +  assertTrue('Control\'s element must be visible',
    +      control.getElement().style.display != 'none');
    +
    +  control.setAllowTextSelection(true);
    +  control.createDom();
    +  assertFalse('Control\'s element must be selectable',
    +      goog.style.isUnselectable(control.getElement()));
    +
    +  control.setVisible(false);
    +  control.createDom();
    +  assertTrue('Control\'s element must be hidden',
    +      control.getElement().style.display == 'none');
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getContentElement}.
    + */
    +function testGetContentElement() {
    +  assertNull('Unrendered control must not have a content element',
    +      control.getContentElement());
    +  control.createDom();
    +  assertEquals('Control\'s content element must equal its root element',
    +      control.getElement(), control.getContentElement());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#canDecorate}.
    + */
    +function testCanDecorate() {
    +  assertTrue(control.canDecorate(goog.dom.createElement('div')));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#decorateInternal}.
    + */
    +function testDecorateInternal() {
    +  sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.decorate(foo);
    +  assertEquals('Decorated control\'s element must have expected value',
    +      foo, control.getElement());
    +  assertTrue('Element must be unselectable',
    +      goog.style.isUnselectable(control.getElement()));
    +  assertTrue('Element must be visible',
    +      control.getElement().style.display != 'none');
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#decorateInternal} with a control that
    + * allows text selection.
    + */
    +function testDecorateInternalForSelectableControl() {
    +  sandbox.innerHTML = '<div id="foo">Hello, <b>World</b>!</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.setAllowTextSelection(true);
    +  control.decorate(foo);
    +  assertEquals('Decorated control\'s element must have expected value',
    +      foo, control.getElement());
    +  assertFalse('Element must be selectable',
    +      goog.style.isUnselectable(control.getElement()));
    +  assertTrue('Control must be visible', control.isVisible());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#decorateInternal} with a hidden element.
    + */
    +function testDecorateInternalForHiddenElement() {
    +  sandbox.innerHTML = '<div id="foo" style="display:none">Hello!</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.decorate(foo);
    +  assertEquals('Decorated control\'s element must have expected value',
    +      foo, control.getElement());
    +  assertTrue('Element must be unselectable',
    +      goog.style.isUnselectable(control.getElement()));
    +  assertFalse('Control must be hidden', control.isVisible());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#enterDocument}.
    + */
    +function testEnterDocument() {
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +  if (goog.userAgent.IE) {
    +    assertEquals('Control must have 5 mouse & 3 key event listeners on IE',
    +        8, getListenerCount(control));
    +  } else {
    +    assertEquals('Control must have 4 mouse and 3 key event listeners', 7,
    +        getListenerCount(control));
    +  }
    +  assertEquals('Control\'s key event handler must be attached to its ' +
    +      'key event target', control.getKeyEventTarget(),
    +      control.getKeyHandler().element_);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#enterDocument} for a control that doesn't
    + * handle mouse events.
    + */
    +function testEnterDocumentForControlWithoutMouseHandling() {
    +  control.setHandleMouseEvents(false);
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +  assertEquals('Control must have 3 key event listeners', 3,
    +      getListenerCount(control));
    +  assertEquals('Control\'s key event handler must be attached to its ' +
    +      'key event target', control.getKeyEventTarget(),
    +      control.getKeyHandler().element_);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#enterDocument} for a control that isn't
    + * focusable.
    + */
    +function testEnterDocumentForNonFocusableControl() {
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +  if (goog.userAgent.IE) {
    +    assertEquals('Control must have 5 mouse event listeners on IE', 5,
    +        getListenerCount(control));
    +  } else {
    +    assertEquals('Control must have 4 mouse event listeners', 4,
    +        getListenerCount(control));
    +  }
    +  assertUndefined('Control must not have a key event handler',
    +      control.keyHandler_);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#enterDocument} for a control that doesn't
    + * need to do any event handling.
    + */
    +function testEnterDocumentForControlWithoutEventHandlers() {
    +  control.setHandleMouseEvents(false);
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +  assertEquals('Control must have 0 event listeners', 0,
    +      getListenerCount(control));
    +  assertUndefined('Control must not have an event handler',
    +      control.googUiComponentHandler_);
    +  assertUndefined('Control must not have a key event handler',
    +      control.keyHandler_);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#exitDocument}.
    + */
    +function testExitDocument() {
    +  control.render(sandbox);
    +  assertTrue('Control must be in the document', control.isInDocument());
    +  if (goog.userAgent.IE) {
    +    assertEquals('Control must have 5 mouse & 3 key event listeners on IE',
    +        8, getListenerCount(control));
    +  } else {
    +    assertEquals('Control must have 4 mouse and 3 key event listeners', 7,
    +        getListenerCount(control));
    +  }
    +  assertEquals('Control\'s key event handler must be attached to its ' +
    +      'key event target', control.getKeyEventTarget(),
    +      control.getKeyHandler().element_);
    +  // Expected to fail on Mac Safari prior to version 527.
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    assertTrue('Control\'s element must support keyboard focus',
    +        goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +
    +  control.exitDocument();
    +  assertFalse('Control must no longer be in the document',
    +      control.isInDocument());
    +  assertEquals('Control must have no event listeners', 0,
    +      getListenerCount(control));
    +  assertNull('Control\'s key event handler must be unattached',
    +      control.getKeyHandler().element_);
    +  assertFalse('Control\'s element must no longer support keyboard focus',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#dispose}.
    + */
    +function testDispose() {
    +  control.render(sandbox);
    +  var handler = control.getHandler();
    +  var keyHandler = control.getKeyHandler();
    +  control.dispose();
    +  assertFalse('Control must no longer be in the document',
    +      control.isInDocument());
    +  assertTrue('Control must have been disposed of', control.isDisposed());
    +  assertUndefined('Renderer must have been deleted', control.getRenderer());
    +  assertNull('Content must be nulled out', control.getContent());
    +  assertTrue('Event handler must have been disposed of',
    +      handler.isDisposed());
    +  assertUndefined('Event handler must have been deleted',
    +      control.googUiComponentHandler_);
    +  assertTrue('Key handler must have been disposed of',
    +      keyHandler.isDisposed());
    +  assertUndefined('Key handler must have been deleted',
    +      control.keyHandler_);
    +  assertNull('Extra class names must have been nulled out',
    +      control.getExtraClassNames());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getContent}.
    + */
    +function testGetContent() {
    +  assertNull('Empty control must have null content',
    +      (new goog.ui.Control(null)).getContent());
    +  assertEquals('Control must have expected content', 'Hello',
    +      control.getContent());
    +  control.render(sandbox);
    +  assertEquals('Control must have expected content after rendering',
    +      'Hello', control.getContent());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getContent}.
    + */
    +function testGetContentForDecoratedControl() {
    +  sandbox.innerHTML =
    +      '<div id="empty"></div>\n' +
    +      '<div id="text">Hello, world!</div>\n' +
    +      '<div id="element"><span>Foo</span></div>\n' +
    +      '<div id="nodelist">Hello, <b>world</b>!</div>\n';
    +
    +  var empty = new goog.ui.Control(null);
    +  empty.decorate(goog.dom.getElement('empty'));
    +  assertNull('Content of control decorating empty DIV must be null',
    +      empty.getContent());
    +  empty.dispose();
    +
    +  var text = new goog.ui.Control(null);
    +  text.decorate(goog.dom.getElement('text'));
    +  assertEquals('Content of control decorating DIV with text contents ' +
    +      'must be as expected', 'Hello, world!', text.getContent().nodeValue);
    +  text.dispose();
    +
    +  var element = new goog.ui.Control(null);
    +  element.decorate(goog.dom.getElement('element'));
    +  assertEquals('Content of control decorating DIV with element child ' +
    +      'must be as expected', goog.dom.getElement('element').firstChild,
    +      element.getContent());
    +  element.dispose();
    +
    +  var nodelist = new goog.ui.Control(null);
    +  nodelist.decorate(goog.dom.getElement('nodelist'));
    +  assertSameElements('Content of control decorating DIV with mixed ' +
    +      'contents must be as expected',
    +      goog.dom.getElement('nodelist').childNodes, nodelist.getContent());
    +  nodelist.dispose();
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setAriaLabel}.
    + */
    +function testSetAriaLabel_render() {
    +  assertNull('Controls must not have any aria label by default',
    +      control.getAriaLabel());
    +
    +  control.setAriaLabel('label');
    +  assertEquals('Control must have aria label', 'label', control.getAriaLabel());
    +
    +  control.render(sandbox);
    +
    +  var elem = control.getElementStrict();
    +  assertEquals(
    +      'Element must have control\'s aria label after rendering',
    +      'label',
    +      goog.a11y.aria.getLabel(elem));
    +
    +  control.setAriaLabel('new label');
    +  assertEquals('Element must have the new aria label',
    +      'new label',
    +      goog.a11y.aria.getLabel(elem));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setAriaLabel}.
    + */
    +function testSetAriaLabel_decorate() {
    +  assertNull('Controls must not have any aria label by default',
    +      control.getAriaLabel());
    +
    +  control.setAriaLabel('label');
    +  assertEquals('Control must have aria label', 'label', control.getAriaLabel());
    +
    +  sandbox.innerHTML = '<div id="nodelist" role="button">' +
    +      'Hello, <b>world</b>!</div>';
    +  control.decorate(goog.dom.getElement('nodelist'));
    +
    +  var elem = control.getElementStrict();
    +  assertEquals(
    +      'Element must have control\'s aria label after rendering',
    +      'label',
    +      goog.a11y.aria.getLabel(elem));
    +  assertEquals(
    +      'Element must have the correct role',
    +      'button',
    +      elem.getAttribute('role'));
    +
    +  control.setAriaLabel('new label');
    +  assertEquals('Element must have the new aria label',
    +      'new label',
    +      goog.a11y.aria.getLabel(elem));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setContent}.
    + */
    +function testSetContent() {
    +  control.setContent('Bye');
    +  assertEquals('Unrendered control control must have expected contents',
    +      'Bye', control.getContent());
    +  assertNull('No DOM must be created by setContent', control.getElement());
    +
    +  control.createDom();
    +  assertEquals('Rendered control\'s DOM must have expected contents',
    +      'Bye', control.getElement().innerHTML);
    +
    +  control.setContent(null);
    +  assertNull('Rendered control must have expected contents',
    +      control.getContent());
    +  assertEquals('Rendered control\'s DOM must have expected contents',
    +      '', control.getElement().innerHTML);
    +
    +  control.setContent([goog.dom.createDom('div', null,
    +      goog.dom.createDom('span', null, 'Hello')), 'World']);
    +  assertHTMLEquals('Control\'s DOM must be updated',
    +      '<div><span>Hello</span></div>World', control.getElement().innerHTML);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setContentInternal}.
    + */
    +function testSetContentInternal() {
    +  control.render(sandbox);
    +  assertEquals('Control must have expected content after rendering',
    +      'Hello', control.getContent());
    +  control.setContentInternal('Bye');
    +  assertEquals('Control must have expected contents',
    +      'Bye', control.getContent());
    +  assertEquals('Control\'s DOM must be unchanged', 'Hello',
    +      control.getElement().innerHTML);
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getCaption}.
    + */
    +function testGetCaption() {
    +  assertEquals('Empty control\'s caption must be empty string', '',
    +      (new goog.ui.Control(null)).getCaption());
    +
    +  assertEquals('Caption must have expected value', 'Hello',
    +      control.getCaption());
    +
    +  sandbox.innerHTML = '<div id="nodelist">Hello, <b>world</b>!</div>';
    +  control.decorate(goog.dom.getElement('nodelist'));
    +  assertEquals('Caption must have expected value', 'Hello, world!',
    +      control.getCaption());
    +
    +  var arrayContent = goog.array.clone(goog.dom.htmlToDocumentFragment(
    +      ' <b> foo</b><i>  bar</i> ').childNodes);
    +  control.setContent(arrayContent);
    +  assertEquals('whitespaces must be normalized in the caption',
    +      'foo bar', control.getCaption());
    +
    +  control.setContent('\xa0foo');
    +  assertEquals('indenting spaces must be kept', '\xa0foo',
    +      control.getCaption());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setCaption}.
    + */
    +function testSetCaption() {
    +  control.setCaption('Hello, world!');
    +  assertEquals('Control must have a string caption "Hello, world!"',
    +      'Hello, world!', control.getCaption());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setRightToLeft}.
    + */
    +function testSetRightToLeft() {
    +  control.createDom();
    +  assertFalse('Control\'s element must not have right-to-left class',
    +      goog.dom.classlist.contains(control.getElement(),
    +          'goog-control-rtl'));
    +  control.setRightToLeft(true);
    +  assertTrue('Control\'s element must have right-to-left class',
    +      goog.dom.classlist.contains(control.getElement(),
    +          'goog-control-rtl'));
    +  control.render(sandbox);
    +  assertThrows('Changing the render direction of a control already in ' +
    +      'the document is an error',
    +      function() {
    +        control.setRightToLeft(false);
    +      });
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isAllowTextSelection}.
    + */
    +function testIsAllowTextSelection() {
    +  assertFalse('Controls must not allow text selection by default',
    +      control.isAllowTextSelection());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setAllowTextSelection}.
    + */
    +function testSetAllowTextSelection() {
    +  assertFalse('Controls must not allow text selection by default',
    +      control.isAllowTextSelection());
    +
    +  control.setAllowTextSelection(true);
    +  assertTrue('Control must allow text selection',
    +      control.isAllowTextSelection());
    +
    +  control.setAllowTextSelection(false);
    +  assertFalse('Control must no longer allow text selection',
    +      control.isAllowTextSelection());
    +
    +  control.render(sandbox);
    +
    +  assertFalse('Control must not allow text selection even after rendered',
    +      control.isAllowTextSelection());
    +
    +  control.setAllowTextSelection(true);
    +  assertTrue('Control must once again allow text selection',
    +      control.isAllowTextSelection());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isVisible}.
    + */
    +function testIsVisible() {
    +  assertTrue('Controls must be visible by default', control.isVisible());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} before it is rendered.
    + */
    +function testSetVisible() {
    +  assertFalse('setVisible(true) must return false if already visible',
    +      control.setVisible(true));
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +
    +  assertTrue('setVisible(false) must return true if previously visible',
    +      control.setVisible(false));
    +  assertEquals('One HIDE event must have been dispatched',
    +      1, getEventCount(control, goog.ui.Component.EventType.HIDE));
    +  assertFalse('Control must no longer be visible', control.isVisible());
    +
    +  assertTrue('setVisible(true) must return true if previously hidden',
    +      control.setVisible(true));
    +  assertEquals('One SHOW event must have been dispatched',
    +      1, getEventCount(control, goog.ui.Component.EventType.SHOW));
    +  assertTrue('Control must be visible', control.isVisible());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} after it is rendered.
    + */
    +function testSetVisibleForRenderedControl() {
    +  control.render(sandbox);
    +  assertTrue('No events must have been dispatched during rendering',
    +      noEventsDispatched());
    +
    +  assertFalse('setVisible(true) must return false if already visible',
    +      control.setVisible(true));
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +  assertTrue('Control\'s element must be visible',
    +      control.getElement().style.display != 'none');
    +
    +  assertTrue('setVisible(false) must return true if previously visible',
    +      control.setVisible(false));
    +  assertEquals('One HIDE event must have been dispatched',
    +      1, getEventCount(control, goog.ui.Component.EventType.HIDE));
    +  assertFalse('Control must no longer be visible', control.isVisible());
    +  assertTrue('Control\'s element must be hidden',
    +      control.getElement().style.display == 'none');
    +
    +  assertTrue('setVisible(true) must return true if previously hidden',
    +      control.setVisible(true));
    +  assertEquals('One SHOW event must have been dispatched',
    +      1, getEventCount(control, goog.ui.Component.EventType.SHOW));
    +  assertTrue('Control must be visible', control.isVisible());
    +  assertTrue('Control\'s element must be visible',
    +      control.getElement().style.display != 'none');
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} for disabled non-focusable
    + * controls.
    + */
    +function testSetVisibleForDisabledNonFocusableControl() {
    +  // Hidden, disabled, non-focusable control becoming visible.
    +  control.setEnabled(false);
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertTrue('Control must be visible', control.isVisible());
    +  assertFalse('Control must not have a tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +
    +  // Visible, disabled, non-focusable control becoming hidden.
    +  control.getKeyEventTarget().focus();
    +  assertEquals('Control must not have dispatched FOCUS', 0,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +  assertFalse('Control must not have keyboard focus', control.isFocused());
    +  control.setVisible(false);
    +  assertFalse('Control must be hidden', control.isVisible());
    +  assertFalse('Control must not have a tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  assertEquals('Control must have dispatched HIDE', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIDE));
    +  assertEquals('Control must not have dispatched BLUR', 0,
    +      getEventCount(control, goog.ui.Component.EventType.BLUR));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} for disabled focusable controls.
    + */
    +function testSetVisibleForDisabledFocusableControl() {
    +  // Hidden, disabled, focusable control becoming visible.
    +  control.setEnabled(false);
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, true);
    +  control.render(sandbox);
    +  assertTrue('Control must be visible', control.isVisible());
    +  assertFalse('Control must not have a tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +
    +  // Visible, disabled, focusable control becoming hidden.
    +  control.getKeyEventTarget().focus();
    +  assertEquals('Control must not have dispatched FOCUS', 0,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +  assertFalse('Control must not have keyboard focus', control.isFocused());
    +  control.setVisible(false);
    +  assertFalse('Control must be hidden', control.isVisible());
    +  assertFalse('Control must not have a tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  assertEquals('Control must have dispatched HIDE', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIDE));
    +  assertEquals('Control must not have dispatched BLUR', 0,
    +      getEventCount(control, goog.ui.Component.EventType.BLUR));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} for enabled non-focusable
    + * controls.
    + */
    +function testSetVisibleForEnabledNonFocusableControl() {
    +  // Hidden, enabled, non-focusable control becoming visible.
    +  control.setEnabled(true);
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertTrue('Control must be visible', control.isVisible());
    +  assertFalse('Control must not have a tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +
    +  if (testFocus) {
    +    // Visible, enabled, non-focusable control becoming hidden.
    +    control.getKeyEventTarget().focus();
    +    assertEquals('Control must not have dispatched FOCUS', 0,
    +        getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +    assertFalse('Control must not have keyboard focus',
    +        control.isFocused());
    +    control.setVisible(false);
    +    assertFalse('Control must be hidden', control.isVisible());
    +    assertFalse('Control must not have a tab index',
    +        goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +    assertEquals('Control must have dispatched HIDE', 1,
    +        getEventCount(control, goog.ui.Component.EventType.HIDE));
    +    assertEquals('Control must not have dispatched BLUR', 0,
    +        getEventCount(control, goog.ui.Component.EventType.BLUR));
    +  }
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setVisible} for enabled focusable controls.
    + */
    +function testSetVisibleForEnabledFocusableControl() {
    +  // Hidden, enabled, focusable control becoming visible.
    +  control.setEnabled(true);
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, true);
    +  control.render(sandbox);
    +  assertTrue('Control must be visible', control.isVisible());
    +
    +  if (testFocus) {
    +    // Expected to fail on Mac Safari prior to version 527.
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      // Mac Safari currently doesn't support tabIndex on arbitrary
    +      // elements.
    +      assertTrue('Control must have a tab index',
    +          goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +
    +    // Visible, enabled, focusable control becoming hidden.
    +    control.getKeyEventTarget().focus();
    +
    +    // Expected to fail on IE.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    try {
    +      // IE dispatches focus and blur events asynchronously!
    +      assertEquals('Control must have dispatched FOCUS', 1,
    +          getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +      assertTrue('Control must have keyboard focus', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +
    +    control.setVisible(false);
    +    assertFalse('Control must be hidden', control.isVisible());
    +    assertFalse('Control must not have a tab index',
    +        goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +    assertEquals('Control must have dispatched HIDE', 1,
    +        getEventCount(control, goog.ui.Component.EventType.HIDE));
    +
    +    // Expected to fail on IE.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    try {
    +      // IE dispatches focus and blur events asynchronously!
    +      assertEquals('Control must have dispatched BLUR', 1,
    +          getEventCount(control, goog.ui.Component.EventType.BLUR));
    +      assertFalse('Control must no longer have keyboard focus',
    +          control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isEnabled}.
    + */
    +function testIsEnabled() {
    +  assertTrue('Controls must be enabled by default', control.isEnabled());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setEnabled}.
    + */
    +function testSetEnabled() {
    +  control.render(sandbox);
    +  control.setHighlighted(true);
    +  control.setActive(true);
    +  control.getKeyEventTarget().focus();
    +
    +  resetEventCount();
    +
    +  control.setEnabled(true);
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +  assertTrue('Control must be enabled', control.isEnabled());
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertTrue('Control must be active', control.isActive());
    +  var elem = control.getElementStrict();
    +  assertTrue('Control element must not have aria-disabled',
    +      goog.string.isEmptyOrWhitespace(aria.getState(elem, State.DISABLED)));
    +  assertEquals('Control element must have a tabIndex of 0', 0,
    +      goog.string.toNumber(elem.getAttribute('tabIndex') || ''));
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +
    +  resetEventCount();
    +
    +  control.setEnabled(false);
    +  assertEquals('One DISABLE event must have been dispatched', 1,
    +      getEventCount(control, goog.ui.Component.EventType.DISABLE));
    +  assertFalse('Control must be disabled', control.isEnabled());
    +  assertFalse('Control must not be highlighted', control.isHighlighted());
    +  assertFalse('Control must not be active', control.isActive());
    +  assertFalse('Control must not be focused', control.isFocused());
    +  assertEquals('Control element must have aria-disabled true', 'true',
    +      aria.getState(control.getElementStrict(), State.DISABLED));
    +  assertNull('Control element must not have a tabIndex',
    +      control.getElement().getAttribute('tabIndex'));
    +
    +  control.setEnabled(true);
    +  control.exitDocument();
    +  var cssClass = goog.getCssName(goog.ui.ControlRenderer.CSS_CLASS, 'disabled');
    +  var element = goog.dom.createDom('div', {tabIndex: 0});
    +  element.className = cssClass;
    +  goog.dom.appendChild(sandbox, element);
    +  control.decorate(element);
    +  assertEquals('Control element must have aria-disabled true', 'true',
    +      aria.getState(control.getElementStrict(), State.DISABLED));
    +  assertNull('Control element must not have a tabIndex',
    +      control.getElement().getAttribute('tabIndex'));
    +  control.setEnabled(true);
    +  elem = control.getElementStrict();
    +  assertEquals('Control element must have aria-disabled false', 'false',
    +      aria.getState(elem, State.DISABLED));
    +  assertEquals('Control element must have tabIndex 0', 0,
    +      goog.string.toNumber(elem.getAttribute('tabIndex') || ''));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setState} when using
    + * goog.ui.Component.State.DISABLED.
    + */
    +function testSetStateWithDisabled() {
    +  control.render(sandbox);
    +  control.setHighlighted(true);
    +  control.setActive(true);
    +  control.getKeyEventTarget().focus();
    +
    +  resetEventCount();
    +
    +  control.setState(goog.ui.Component.State.DISABLED, false);
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +  assertTrue('Control must be enabled', control.isEnabled());
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertTrue('Control must be active', control.isActive());
    +  assertTrue('Control element must not have aria-disabled', goog.string.isEmptyOrWhitespace(
    +      aria.getState(control.getElementStrict(), State.DISABLED)));
    +  assertEquals('Control element must have a tabIndex of 0', 0,
    +      goog.string.toNumber(
    +          control.getElement().getAttribute('tabIndex') || ''));
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +
    +  resetEventCount();
    +
    +  control.setState(goog.ui.Component.State.DISABLED, true);
    +  assertEquals('One DISABLE event must have been dispatched', 1,
    +      getEventCount(control, goog.ui.Component.EventType.DISABLE));
    +  assertFalse('Control must be disabled', control.isEnabled());
    +  assertFalse('Control must not be highlighted', control.isHighlighted());
    +  assertFalse('Control must not be active', control.isActive());
    +  assertFalse('Control must not be focused', control.isFocused());
    +  assertEquals('Control element must have aria-disabled true', 'true',
    +      aria.getState(control.getElementStrict(), State.DISABLED));
    +  assertNull('Control element must not have a tabIndex',
    +      control.getElement().getAttribute('tabIndex'));
    +
    +  control.setState(goog.ui.Component.State.DISABLED, false);
    +  control.exitDocument();
    +  var cssClass = goog.getCssName(goog.ui.ControlRenderer.CSS_CLASS, 'disabled');
    +  var element = goog.dom.createDom('div', {tabIndex: 0});
    +  element.className = cssClass;
    +  goog.dom.appendChild(sandbox, element);
    +  control.decorate(element);
    +  assertEquals('Control element must have aria-disabled true', 'true',
    +      aria.getState(control.getElementStrict(), State.DISABLED));
    +  assertNull('Control element must not have a tabIndex',
    +      control.getElement().getAttribute('tabIndex'));
    +  control.setState(goog.ui.Component.State.DISABLED, false);
    +  elem = control.getElementStrict();
    +  assertEquals('Control element must have aria-disabled false', 'false',
    +      aria.getState(elem, State.DISABLED));
    +  assertEquals('Control element must have tabIndex 0', 0,
    +      goog.string.toNumber(elem.getAttribute('tabIndex') || ''));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setEnabled} when the control has a parent.
    + */
    +function testSetEnabledWithParent() {
    +  var child = new goog.ui.Control(null);
    +  child.setDispatchTransitionEvents(goog.ui.Component.State.ALL, true);
    +  control.addChild(child, true /* opt_render */);
    +  control.setEnabled(false);
    +
    +  resetEventCount();
    +
    +  assertFalse('Parent must be disabled', control.isEnabled());
    +  assertTrue('Child must be enabled', child.isEnabled());
    +
    +  child.setEnabled(false);
    +  assertTrue('No events must have been dispatched when child is disabled',
    +      noEventsDispatched());
    +  assertTrue('Child must still be enabled', child.isEnabled());
    +
    +  resetEventCount();
    +
    +  control.setEnabled(true);
    +  assertEquals('One ENABLE event must have been dispatched by the parent',
    +      1, getEventCount(control, goog.ui.Component.EventType.ENABLE));
    +  assertTrue('Parent must be enabled', control.isEnabled());
    +  assertTrue('Child must still be enabled', child.isEnabled());
    +
    +  resetEventCount();
    +
    +  child.setEnabled(false);
    +  assertEquals('One DISABLE event must have been dispatched by the child',
    +      1, getEventCount(child, goog.ui.Component.EventType.DISABLE));
    +  assertTrue('Parent must still be enabled', control.isEnabled());
    +  assertFalse('Child must now be disabled', child.isEnabled());
    +
    +  resetEventCount();
    +
    +  control.setEnabled(false);
    +  assertEquals('One DISABLE event must have been dispatched by the parent',
    +      1, getEventCount(control, goog.ui.Component.EventType.DISABLE));
    +  assertFalse('Parent must now be disabled', control.isEnabled());
    +  assertFalse('Child must still be disabled', child.isEnabled());
    +
    +  child.dispose();
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isHighlighted}.
    + */
    +function testIsHighlighted() {
    +  assertFalse('Controls must not be highlighted by default',
    +      control.isHighlighted());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setHighlighted}.
    + */
    +function testSetHighlighted() {
    +  control.setSupportedState(goog.ui.Component.State.HOVER, false);
    +
    +  control.setHighlighted(true);
    +  assertFalse('Control must not be highlighted, because it isn\'t ' +
    +      'highlightable', control.isHighlighted());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.HOVER, true);
    +
    +  control.setHighlighted(true);
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertEquals('Control must have dispatched a HIGHLIGHT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +
    +  control.setHighlighted(true);
    +  assertTrue('Control must still be highlighted', control.isHighlighted());
    +  assertEquals('Control must not dispatch more HIGHLIGHT events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +
    +  control.setHighlighted(false);
    +  assertFalse('Control must not be highlighted', control.isHighlighted());
    +  assertEquals('Control must have dispatched an UNHIGHLIGHT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNHIGHLIGHT));
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +
    +  control.setHighlighted(true);
    +  assertTrue('Control must be highlighted, even when disabled',
    +      control.isHighlighted());
    +  assertEquals('Control must have dispatched another HIGHLIGHT event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isActive}.
    + */
    +function testIsActive() {
    +  assertFalse('Controls must not be active by default', control.isActive());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setActive}.
    + */
    +function testSetActive() {
    +  control.setSupportedState(goog.ui.Component.State.ACTIVE, false);
    +
    +  control.setActive(true);
    +  assertFalse('Control must not be active, because it isn\'t activateable',
    +      control.isActive());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.ACTIVE, true);
    +
    +  control.setActive(true);
    +  assertTrue('Control must be active', control.isActive());
    +  assertEquals('Control must have dispatched an ACTIVATE event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ACTIVATE));
    +
    +  control.setActive(true);
    +  assertTrue('Control must still be active', control.isActive());
    +  assertEquals('Control must not dispatch more ACTIVATE events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ACTIVATE));
    +
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +  assertFalse('Control must not be active', control.isActive());
    +  assertEquals('Control must have dispatched a DEACTIVATE event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.DEACTIVATE));
    +}
    +
    +
    +/**
    + * Tests disposing the control from an action event handler.
    + */
    +function testDisposeOnAction() {
    +  goog.events.listen(control, goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        control.dispose();
    +      });
    +
    +  // Control must not throw an exception if disposed of in an ACTION event
    +  // handler.
    +  control.performActionInternal();
    +  control.setActive(true);
    +  assertTrue('Control should have been disposed of', control.isDisposed());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isSelected}.
    + */
    +function testIsSelected() {
    +  assertFalse('Controls must not be selected by default',
    +      control.isSelected());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setSelected}.
    + */
    +function testSetSelected() {
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, false);
    +
    +  control.setSelected(true);
    +  assertFalse('Control must not be selected, because it isn\'t selectable',
    +      control.isSelected());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +
    +  control.setSelected(true);
    +  assertTrue('Control must be selected', control.isSelected());
    +  assertEquals('Control must have dispatched a SELECT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.SELECT));
    +
    +  control.setSelected(true);
    +  assertTrue('Control must still be selected', control.isSelected());
    +  assertEquals('Control must not dispatch more SELECT events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.SELECT));
    +
    +  control.setSelected(false);
    +  assertFalse('Control must not be selected', control.isSelected());
    +  assertEquals('Control must have dispatched an UNSELECT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNSELECT));
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +
    +  control.setSelected(true);
    +  assertTrue('Control must be selected, even when disabled',
    +      control.isSelected());
    +  assertEquals('Control must have dispatched another SELECT event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.SELECT));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isChecked}.
    + */
    +function testIsChecked() {
    +  assertFalse('Controls must not be checked by default',
    +      control.isChecked());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setChecked}.
    + */
    +function testSetChecked() {
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, false);
    +
    +  control.setChecked(true);
    +  assertFalse('Control must not be checked, because it isn\'t checkable',
    +      control.isChecked());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +
    +  control.setChecked(true);
    +  assertTrue('Control must be checked', control.isChecked());
    +  assertEquals('Control must have dispatched a CHECK event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.CHECK));
    +
    +  control.setChecked(true);
    +  assertTrue('Control must still be checked', control.isChecked());
    +  assertEquals('Control must not dispatch more CHECK events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.CHECK));
    +
    +  control.setChecked(false);
    +  assertFalse('Control must not be checked', control.isChecked());
    +  assertEquals('Control must have dispatched an UNCHECK event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNCHECK));
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +
    +  control.setChecked(true);
    +  assertTrue('Control must be checked, even when disabled',
    +      control.isChecked());
    +  assertEquals('Control must have dispatched another CHECK event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.CHECK));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isFocused}.
    + */
    +function testIsFocused() {
    +  assertFalse('Controls must not be focused by default',
    +      control.isFocused());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setFocused}.
    + */
    +function testSetFocused() {
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +
    +  control.setFocused(true);
    +  assertFalse('Control must not be focused, because it isn\'t focusable',
    +      control.isFocused());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, true);
    +
    +  control.setFocused(true);
    +  assertTrue('Control must be focused', control.isFocused());
    +  assertEquals('Control must have dispatched a FOCUS event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +
    +  control.setFocused(true);
    +  assertTrue('Control must still be focused', control.isFocused());
    +  assertEquals('Control must not dispatch more FOCUS events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +
    +  control.setFocused(false);
    +  assertFalse('Control must not be focused', control.isFocused());
    +  assertEquals('Control must have dispatched an BLUR event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.BLUR));
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +
    +  control.setFocused(true);
    +  assertTrue('Control must be focused, even when disabled',
    +      control.isFocused());
    +  assertEquals('Control must have dispatched another FOCUS event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isOpen}.
    + */
    +function testIsOpen() {
    +  assertFalse('Controls must not be open by default', control.isOpen());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setOpen}.
    + */
    +function testSetOpen() {
    +  control.setSupportedState(goog.ui.Component.State.OPENED, false);
    +
    +  control.setOpen(true);
    +  assertFalse('Control must not be opened, because it isn\'t openable',
    +      control.isOpen());
    +  assertTrue('Control must not have dispatched any events',
    +      noEventsDispatched());
    +
    +  control.setSupportedState(goog.ui.Component.State.OPENED, true);
    +
    +  control.setOpen(true);
    +  assertTrue('Control must be opened', control.isOpen());
    +  assertEquals('Control must have dispatched a OPEN event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.OPEN));
    +
    +  control.setOpen(true);
    +  assertTrue('Control must still be opened', control.isOpen());
    +  assertEquals('Control must not dispatch more OPEN events', 1,
    +      getEventCount(control, goog.ui.Component.EventType.OPEN));
    +
    +  control.setOpen(false);
    +  assertFalse('Control must not be opened', control.isOpen());
    +  assertEquals('Control must have dispatched an CLOSE event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.CLOSE));
    +  control.setEnabled(false);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +
    +  control.setOpen(true);
    +  assertTrue('Control must be opened, even when disabled',
    +      control.isOpen());
    +  assertEquals('Control must have dispatched another OPEN event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.OPEN));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#getState}.
    + */
    +function testGetState() {
    +  assertEquals('Controls must be in the default state', 0x00,
    +      control.getState());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#hasState}.
    + */
    +function testHasState() {
    +  assertFalse('Control must not be disabled',
    +      control.hasState(goog.ui.Component.State.DISABLED));
    +  assertFalse('Control must not be in the HOVER state',
    +      control.hasState(goog.ui.Component.State.HOVER));
    +  assertFalse('Control must not be active',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +  assertFalse('Control must not be selected',
    +      control.hasState(goog.ui.Component.State.SELECTED));
    +  assertFalse('Control must not be checked',
    +      control.hasState(goog.ui.Component.State.CHECKED));
    +  assertFalse('Control must not be focused',
    +      control.hasState(goog.ui.Component.State.FOCUSED));
    +  assertFalse('Control must not be open',
    +      control.hasState(goog.ui.Component.State.OPEN));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setState}.
    + */
    +function testSetState() {
    +  control.createDom();
    +  control.setSupportedState(goog.ui.Component.State.ACTIVE, false);
    +
    +  assertFalse('Control must not be active',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +  control.setState(goog.ui.Component.State.ACTIVE, true);
    +  assertFalse('Control must still be inactive (because it doesn\'t ' +
    +      'support the ACTIVE state)',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +
    +  control.setSupportedState(goog.ui.Component.State.ACTIVE, true);
    +
    +  control.setState(goog.ui.Component.State.ACTIVE, true);
    +  assertTrue('Control must be active',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must have the active CSS style',
    +      goog.dom.classlist.contains(control.getElement(),
    +          'goog-control-active'));
    +
    +  control.setState(goog.ui.Component.State.ACTIVE, true);
    +  assertTrue('Control must still be active',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must still have the active CSS style',
    +      goog.dom.classlist.contains(control.getElement(),
    +          'goog-control-active'));
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setStateInternal}.
    + */
    +function testSetStateInternal() {
    +  control.setStateInternal(0x00);
    +  assertEquals('State should be 0x00', 0x00, control.getState());
    +  control.setStateInternal(0x17);
    +  assertEquals('State should be 0x17', 0x17, control.getState());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isSupportedState}.
    + */
    +function testIsSupportedState() {
    +  assertTrue('Control must support DISABLED',
    +      control.isSupportedState(goog.ui.Component.State.DISABLED));
    +  assertTrue('Control must support HOVER',
    +      control.isSupportedState(goog.ui.Component.State.HOVER));
    +  assertTrue('Control must support ACTIVE',
    +      control.isSupportedState(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must support FOCUSED',
    +      control.isSupportedState(goog.ui.Component.State.FOCUSED));
    +  assertFalse('Control must no support SELECTED',
    +      control.isSupportedState(goog.ui.Component.State.SELECTED));
    +  assertFalse('Control must no support CHECKED',
    +      control.isSupportedState(goog.ui.Component.State.CHECKED));
    +  assertFalse('Control must no support OPENED',
    +      control.isSupportedState(goog.ui.Component.State.OPENED));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setSupportedState}.
    + */
    +function testSetSupportedState() {
    +  control.setSupportedState(goog.ui.Component.State.HOVER, true);
    +  assertTrue('Control must still support HOVER',
    +      control.isSupportedState(goog.ui.Component.State.HOVER));
    +
    +  control.setSupportedState(goog.ui.Component.State.HOVER, false);
    +  assertFalse('Control must no longer support HOVER',
    +      control.isSupportedState(goog.ui.Component.State.HOVER));
    +
    +  control.setState(goog.ui.Component.State.ACTIVE, true);
    +  control.setSupportedState(goog.ui.Component.State.ACTIVE, false);
    +  assertFalse('Control must no longer support ACTIVE',
    +      control.isSupportedState(goog.ui.Component.State.ACTIVE));
    +  assertFalse('Control must no longer be in the ACTIVE state',
    +      control.hasState(goog.ui.Component.State.ACTIVE));
    +
    +  control.render(sandbox);
    +
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, true);
    +  control.setState(goog.ui.Component.State.FOCUSED, true);
    +
    +  assertThrows('Must not be able to disable support for the FOCUSED ' +
    +      "state for a control that's already in the document and focused",
    +      function() {
    +        control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +      });
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isAutoState}.
    + */
    +function testIsAutoState() {
    +  assertTrue('Control must have DISABLED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.DISABLED));
    +  assertTrue('Control must have HOVER as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.HOVER));
    +  assertTrue('Control must have ACTIVE as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must have FOCUSED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.FOCUSED));
    +
    +  assertFalse('Control must not have SELECTED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.SELECTED));
    +  assertFalse('Control must not have CHECKED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.CHECKED));
    +  assertFalse('Control must not have OPENED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.OPENED));
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setAutoStates}.
    + */
    +function testSetAutoStates() {
    +  control.setAutoStates(goog.ui.Component.State.HOVER, false);
    +  assertFalse('Control must not have HOVER as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.HOVER));
    +
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE |
    +      goog.ui.Component.State.FOCUSED, false);
    +  assertFalse('Control must not have ACTIVE as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.ACTIVE));
    +  assertFalse('Control must not have FOCUSED as an auto-state',
    +      control.isAutoState(goog.ui.Component.State.FOCUSED));
    +
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.setAutoStates(goog.ui.Component.State.FOCUSED, true);
    +  assertFalse('Control must not have FOCUSED as an auto-state if it no ' +
    +      'longer supports FOCUSED',
    +      control.isAutoState(goog.ui.Component.State.FOCUSED));
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isDispatchTransitionEvents}.
    + */
    +function testIsDispatchTransitionEvents() {
    +  assertTrue('Control must dispatch DISABLED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.DISABLED));
    +  assertTrue('Control must dispatch HOVER transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.HOVER));
    +  assertTrue('Control must dispatch ACTIVE transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must dispatch FOCUSED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.FOCUSED));
    +
    +  assertFalse('Control must not dispatch SELECTED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.SELECTED));
    +  assertFalse('Control must not dispatch CHECKED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.CHECKED));
    +  assertFalse('Control must not dispatch OPENED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.OPENED));
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#setDispatchTransitionEvents}.
    + */
    +function testSetDispatchTransitionEvents() {
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.HOVER, false);
    +  assertFalse('Control must not dispatch HOVER transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.HOVER));
    +
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.SELECTED,
    +      true);
    +  assertTrue('Control must dispatch SELECTED transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.SELECTED));
    +
    +  assertTrue('No events must have been dispatched', noEventsDispatched());
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#isTransitionAllowed}.
    + */
    +function testIsTransitionAllowed() {
    +  assertTrue('Control must support the HOVER state',
    +      control.isSupportedState(goog.ui.Component.State.HOVER));
    +  assertFalse('Control must not be in the HOVER state',
    +      control.hasState(goog.ui.Component.State.HOVER));
    +  assertTrue('Control must dispatch HOVER transition events',
    +      control.isDispatchTransitionEvents(goog.ui.Component.State.HOVER));
    +
    +  assertTrue('Control must be allowed to transition to the HOVER state',
    +      control.isTransitionAllowed(goog.ui.Component.State.HOVER, true));
    +  assertEquals('Control must have dispatched one HIGHLIGHT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +  assertFalse('Control must not be highlighted',
    +      control.hasState(goog.ui.Component.State.HOVER));
    +
    +  control.setState(goog.ui.Component.State.HOVER, true);
    +  control.setDispatchTransitionEvents(goog.ui.Component.State.HOVER, false);
    +
    +  assertTrue('Control must be allowed to transition from the HOVER state',
    +      control.isTransitionAllowed(goog.ui.Component.State.HOVER, false));
    +  assertEquals('Control must not have dispatched any UNHIGHLIGHT events', 0,
    +      getEventCount(control, goog.ui.Component.EventType.UNHIGHLIGHT));
    +  assertTrue('Control must still be highlighted',
    +      control.hasState(goog.ui.Component.State.HOVER));
    +
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  resetEventCount();
    +
    +  assertFalse('Control doesn\'t support the FOCUSED state',
    +      control.isSupportedState(goog.ui.Component.State.FOCUSED));
    +  assertFalse('Control must not be FOCUSED',
    +      control.hasState(goog.ui.Component.State.FOCUSED));
    +  assertFalse('Control must not be allowed to transition to the FOCUSED ' +
    +      'state',
    +      control.isTransitionAllowed(goog.ui.Component.State.FOCUSED, true));
    +  assertEquals('Control must not have dispatched any FOCUS events', 0,
    +      getEventCount(control, goog.ui.Component.EventType.FOCUS));
    +
    +  control.setEnabled(false);
    +  resetEventCount();
    +
    +  assertTrue('Control must support the DISABLED state',
    +      control.isSupportedState(goog.ui.Component.State.DISABLED));
    +  assertTrue('Control must be DISABLED',
    +      control.hasState(goog.ui.Component.State.DISABLED));
    +  assertFalse('Control must not be allowed to transition to the DISABLED ' +
    +      'state, because it is already there',
    +      control.isTransitionAllowed(goog.ui.Component.State.DISABLED, true));
    +  assertEquals('Control must not have dispatched any ENABLE events', 0,
    +      getEventCount(control, goog.ui.Component.EventType.ENABLE));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#handleKeyEvent}.
    + */
    +function testHandleKeyEvent() {
    +  control.render();
    +  control.isVisible = control.isEnabled = function() {
    +    return true;
    +  };
    +
    +
    +  goog.testing.events.fireKeySequence(
    +      control.getKeyEventTarget(), goog.events.KeyCodes.A);
    +
    +  assertEquals('Control must not have dispatched an ACTION event', 0,
    +      getEventCount(control, goog.ui.Component.EventType.ACTION));
    +
    +  goog.testing.events.fireKeySequence(
    +      control.getKeyEventTarget(), goog.events.KeyCodes.ENTER);
    +  assertEquals('Control must have dispatched an ACTION event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ACTION));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#performActionInternal}.
    + */
    +function testPerformActionInternal() {
    +  assertFalse('Control must not be checked', control.isChecked());
    +  assertFalse('Control must not be selected', control.isSelected());
    +  assertFalse('Control must not be open', control.isOpen());
    +
    +  control.performActionInternal();
    +
    +  assertFalse('Control must not be checked', control.isChecked());
    +  assertFalse('Control must not be selected', control.isSelected());
    +  assertFalse('Control must not be open', control.isOpen());
    +  assertEquals('Control must have dispatched an ACTION event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ACTION));
    +
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  control.setSupportedState(goog.ui.Component.State.OPENED, true);
    +
    +  control.performActionInternal();
    +
    +  assertTrue('Control must be checked', control.isChecked());
    +  assertTrue('Control must be selected', control.isSelected());
    +  assertTrue('Control must be open', control.isOpen());
    +  assertEquals('Control must have dispatched a CHECK event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.CHECK));
    +  assertEquals('Control must have dispatched a SELECT event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.SELECT));
    +  assertEquals('Control must have dispatched a OPEN event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.OPEN));
    +  assertEquals('Control must have dispatched another ACTION event', 2,
    +      getEventCount(control, goog.ui.Component.EventType.ACTION));
    +
    +  control.performActionInternal();
    +
    +  assertFalse('Control must not be checked', control.isChecked());
    +  assertTrue('Control must be selected', control.isSelected());
    +  assertFalse('Control must not be open', control.isOpen());
    +  assertEquals('Control must have dispatched an UNCHECK event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNCHECK));
    +  assertEquals('Control must not have dispatched an UNSELECT event', 0,
    +      getEventCount(control, goog.ui.Component.EventType.UNSELECT));
    +  assertEquals('Control must have dispatched a CLOSE event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.CLOSE));
    +  assertEquals('Control must have dispatched another ACTION event', 3,
    +      getEventCount(control, goog.ui.Component.EventType.ACTION));
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#handleMouseOver}.
    + */
    +function testHandleMouseOver() {
    +  control.setContent(goog.dom.createDom('span', {id: 'caption'}, 'Hello'));
    +  control.render(sandbox);
    +
    +  var element = control.getElement();
    +  var caption = goog.dom.getElement('caption');
    +
    +  // Verify baseline assumptions.
    +  assertTrue('Caption must be contained within the control',
    +      goog.dom.contains(element, caption));
    +  assertTrue('Control must be enabled', control.isEnabled());
    +  assertTrue('HOVER must be an auto-state',
    +      control.isAutoState(goog.ui.Component.State.HOVER));
    +  assertFalse('Control must not start out highlighted',
    +      control.isHighlighted());
    +
    +  // Scenario 1:  relatedTarget is contained within the control's DOM.
    +  goog.testing.events.fireMouseOverEvent(element, caption);
    +  assertTrue('No events must have been dispatched for internal mouse move',
    +      noEventsDispatched());
    +  assertFalse('Control must not be highlighted for internal mouse move',
    +      control.isHighlighted());
    +  resetEventCount();
    +
    +  // Scenario 2:  preventDefault() is called on the ENTER event.
    +  var key = goog.events.listen(control, goog.ui.Component.EventType.ENTER,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  goog.testing.events.fireMouseOverEvent(element, sandbox);
    +  assertEquals('Control must have dispatched 1 ENTER event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ENTER));
    +  assertFalse('Control must not be highlighted if ENTER is canceled',
    +      control.isHighlighted());
    +  goog.events.unlistenByKey(key);
    +  resetEventCount();
    +
    +  // Scenario 3:  Control is disabled.
    +  control.setEnabled(false);
    +  goog.testing.events.fireMouseOverEvent(element, sandbox);
    +  assertEquals('Control must dispatch ENTER event on mouseover even if ' +
    +      'disabled', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ENTER));
    +  assertFalse('Control must not be highlighted if it is disabled',
    +      control.isHighlighted());
    +  control.setEnabled(true);
    +  resetEventCount();
    +
    +  // Scenario 4:  HOVER is not an auto-state.
    +  control.setAutoStates(goog.ui.Component.State.HOVER, false);
    +  goog.testing.events.fireMouseOverEvent(element, sandbox);
    +  assertEquals('Control must dispatch ENTER event on mouseover even if ' +
    +      'HOVER is not an auto-state', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ENTER));
    +  assertFalse('Control must not be highlighted if HOVER isn\'t an auto-' +
    +      'state', control.isHighlighted());
    +  control.setAutoStates(goog.ui.Component.State.HOVER, true);
    +  resetEventCount();
    +
    +  // Scenario 5:  All is well.
    +  goog.testing.events.fireMouseOverEvent(element, sandbox);
    +  assertEquals('Control must dispatch ENTER event on mouseover', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ENTER));
    +  assertEquals('Control must dispatch HIGHLIGHT event on mouseover', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  resetEventCount();
    +
    +  // Scenario 6: relatedTarget is null
    +  control.setHighlighted(false);
    +  goog.testing.events.fireMouseOverEvent(element, null);
    +  assertEquals('Control must dispatch ENTER event on mouseover', 1,
    +      getEventCount(control, goog.ui.Component.EventType.ENTER));
    +  assertEquals('Control must dispatch HIGHLIGHT event on mouseover', 1,
    +      getEventCount(control, goog.ui.Component.EventType.HIGHLIGHT));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  resetEventCount();
    +}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#handleMouseOut}.
    + */
    +function testHandleMouseOut() {
    +  control.setContent(goog.dom.createDom('span', {id: 'caption'}, 'Hello'));
    +  control.setHighlighted(true);
    +  control.setActive(true);
    +
    +  resetEventCount();
    +
    +  control.render(sandbox);
    +
    +  var element = control.getElement();
    +  var caption = goog.dom.getElement('caption');
    +
    +  // Verify baseline assumptions.
    +  assertTrue('Caption must be contained within the control',
    +      goog.dom.contains(element, caption));
    +  assertTrue('Control must be enabled', control.isEnabled());
    +  assertTrue('HOVER must be an auto-state',
    +      control.isAutoState(goog.ui.Component.State.HOVER));
    +  assertTrue('ACTIVE must be an auto-state',
    +      control.isAutoState(goog.ui.Component.State.ACTIVE));
    +  assertTrue('Control must start out highlighted', control.isHighlighted());
    +  assertTrue('Control must start out active', control.isActive());
    +
    +  // Scenario 1:  relatedTarget is contained within the control's DOM.
    +  goog.testing.events.fireMouseOutEvent(element, caption);
    +  assertTrue('No events must have been dispatched for internal mouse move',
    +      noEventsDispatched());
    +  assertTrue('Control must not be un-highlighted for internal mouse move',
    +      control.isHighlighted());
    +  assertTrue('Control must not be deactivated for internal mouse move',
    +      control.isActive());
    +  resetEventCount();
    +
    +  // Scenario 2:  preventDefault() is called on the LEAVE event.
    +  var key = goog.events.listen(control, goog.ui.Component.EventType.LEAVE,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  goog.testing.events.fireMouseOutEvent(element, sandbox);
    +  assertEquals('Control must have dispatched 1 LEAVE event', 1,
    +      getEventCount(control, goog.ui.Component.EventType.LEAVE));
    +  assertTrue('Control must not be un-highlighted if LEAVE is canceled',
    +      control.isHighlighted());
    +  assertTrue('Control must not be deactivated if LEAVE is canceled',
    +      control.isActive());
    +  goog.events.unlistenByKey(key);
    +  resetEventCount();
    +
    +  // Scenario 3:  ACTIVE is not an auto-state.
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE, false);
    +  goog.testing.events.fireMouseOutEvent(element, sandbox);
    +  assertEquals('Control must dispatch LEAVE event on mouseout even if ' +
    +      'ACTIVE is not an auto-state', 1,
    +      getEventCount(control, goog.ui.Component.EventType.LEAVE));
    +  assertTrue('Control must not be deactivated if ACTIVE isn\'t an auto-' +
    +      'state', control.isActive());
    +  assertFalse('Control must be un-highlighted even if ACTIVE isn\'t an ' +
    +      'auto-state', control.isHighlighted());
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE, true);
    +  control.setHighlighted(true);
    +  resetEventCount();
    +
    +  // Scenario 4:  HOVER is not an auto-state.
    +  control.setAutoStates(goog.ui.Component.State.HOVER, false);
    +  goog.testing.events.fireMouseOutEvent(element, sandbox);
    +  assertEquals('Control must dispatch LEAVE event on mouseout even if ' +
    +      'HOVER is not an auto-state', 1,
    +      getEventCount(control, goog.ui.Component.EventType.LEAVE));
    +  assertFalse('Control must be deactivated even if HOVER isn\'t an auto-' +
    +      'state', control.isActive());
    +  assertTrue('Control must not be un-highlighted if HOVER isn\'t an auto-' +
    +      'state', control.isHighlighted());
    +  control.setAutoStates(goog.ui.Component.State.HOVER, true);
    +  control.setActive(true);
    +  resetEventCount();
    +
    +  // Scenario 5:  All is well.
    +  goog.testing.events.fireMouseOutEvent(element, sandbox);
    +  assertEquals('Control must dispatch LEAVE event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.LEAVE));
    +  assertEquals('Control must dispatch DEACTIVATE event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.DEACTIVATE));
    +  assertEquals('Control must dispatch UNHIGHLIGHT event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNHIGHLIGHT));
    +  assertFalse('Control must be deactivated', control.isActive());
    +  assertFalse('Control must be unhighlighted', control.isHighlighted());
    +  resetEventCount();
    +
    +  // Scenario 6: relatedTarget is null
    +  control.setActive(true);
    +  control.setHighlighted(true);
    +  goog.testing.events.fireMouseOutEvent(element, null);
    +  assertEquals('Control must dispatch LEAVE event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.LEAVE));
    +  assertEquals('Control must dispatch DEACTIVATE event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.DEACTIVATE));
    +  assertEquals('Control must dispatch UNHIGHLIGHT event on mouseout', 1,
    +      getEventCount(control, goog.ui.Component.EventType.UNHIGHLIGHT));
    +  assertFalse('Control must be deactivated', control.isActive());
    +  assertFalse('Control must be unhighlighted', control.isHighlighted());
    +  resetEventCount();
    +}
    +
    +function testIsMouseEventWithinElement() {
    +  var child = goog.dom.createElement('div');
    +  var parent = goog.dom.createDom('div', null, child);
    +  var notChild = goog.dom.createElement('div');
    +
    +  var event = new goog.testing.events.Event('mouseout');
    +  event.relatedTarget = child;
    +  assertTrue('Event is within element',
    +             goog.ui.Control.isMouseEventWithinElement_(event, parent));
    +
    +  var event = new goog.testing.events.Event('mouseout');
    +  event.relatedTarget = notChild;
    +  assertFalse('Event is not within element',
    +              goog.ui.Control.isMouseEventWithinElement_(event, parent));
    +}
    +
    +function testHandleMouseDown() {
    +  control.render(sandbox);
    +  assertFalse('preventDefault() must have been called for control that ' +
    +      'doesn\'t support text selection',
    +      fireMouseDownAndFocus(control.getElement()));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertTrue('Control must be active', control.isActive());
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +}
    +
    +function testHandleMouseDownForDisabledControl() {
    +  control.setEnabled(false);
    +  control.render(sandbox);
    +  assertFalse('preventDefault() must have been called for control that ' +
    +      'doesn\'t support text selection',
    +      fireMouseDownAndFocus(control.getElement()));
    +  assertFalse('Control must not be highlighted', control.isHighlighted());
    +  assertFalse('Control must not be active', control.isActive());
    +  if (testFocus) {
    +    assertFalse('Control must not be focused', control.isFocused());
    +  }
    +}
    +
    +function testHandleMouseDownForNoHoverAutoState() {
    +  control.setAutoStates(goog.ui.Component.State.HOVER, false);
    +  control.render(sandbox);
    +  assertFalse('preventDefault() must have been called for control that ' +
    +      'doesn\'t support text selection',
    +      fireMouseDownAndFocus(control.getElement()));
    +  assertFalse('Control must not be highlighted', control.isHighlighted());
    +  assertTrue('Control must be active', control.isActive());
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +}
    +
    +function testHandleMouseDownForRightMouseButton() {
    +  control.render(sandbox);
    +  assertTrue('preventDefault() must not have been called for right ' +
    +      'mouse button', fireMouseDownAndFocus(control.getElement(),
    +      goog.events.BrowserEvent.MouseButton.RIGHT));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertFalse('Control must not be active', control.isActive());
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +}
    +
    +function testHandleMouseDownForNoActiveAutoState() {
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE, false);
    +  control.render(sandbox);
    +  assertFalse('preventDefault() must have been called for control that ' +
    +      'doesn\'t support text selection',
    +      fireMouseDownAndFocus(control.getElement()));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertFalse('Control must not be active', control.isActive());
    +
    +  if (testFocus) {
    +    // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +    // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +    expectedFailures.expectFailureFor(goog.userAgent.IE);
    +    expectedFailures.expectFailureFor(isMacSafari3());
    +    try {
    +      assertTrue('Control must be focused', control.isFocused());
    +    } catch (e) {
    +      expectedFailures.handleException(e);
    +    }
    +  }
    +}
    +
    +function testHandleMouseDownForNonFocusableControl() {
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertFalse('preventDefault() must have been called for control that ' +
    +      'doesn\'t support text selection',
    +      fireMouseDownAndFocus(control.getElement()));
    +  assertTrue('Control must be highlighted', control.isHighlighted());
    +  assertTrue('Control must be active', control.isActive());
    +  assertFalse('Control must not be focused', control.isFocused());
    +}
    +
    +// TODO(attila): Find out why this is flaky on FF2/Linux and FF1.5/Win.
    +//function testHandleMouseDownForSelectableControl() {
    +//  control.setAllowTextSelection(true);
    +//  control.render(sandbox);
    +//  assertTrue('preventDefault() must not have been called for control ' +
    +//      'that supports text selection',
    +//      fireMouseDownAndFocus(control.getElement()));
    +//  assertTrue('Control must be highlighted', control.isHighlighted());
    +//  assertTrue('Control must be active', control.isActive());
    +//  // Expected to fail on IE and Mac Safari 3.  IE calls focus handlers
    +//  // asynchronously, and Mac Safari 3 doesn't support keyboard focus.
    +//  expectedFailures.expectFailureFor(goog.userAgent.IE);
    +//  expectedFailures.expectFailureFor(isMacSafari3());
    +//  try {
    +//    assertTrue('Control must be focused', control.isFocused());
    +//  } catch (e) {
    +//    expectedFailures.handleException(e);
    +//  }
    +//}
    +
    +
    +/**
    + * Tests {@link goog.ui.Control#handleMouseUp}.
    + */
    +function testHandleMouseUp() {
    +  control.setActive(true);
    +
    +  // Override performActionInternal() for testing purposes.
    +  var actionPerformed = false;
    +  control.performActionInternal = function() {
    +    actionPerformed = true;
    +    return true;
    +  };
    +
    +  resetEventCount();
    +
    +  control.render(sandbox);
    +  var element = control.getElement();
    +
    +  // Verify baseline assumptions.
    +  assertTrue('Control must be enabled', control.isEnabled());
    +  assertTrue('HOVER must be an auto-state',
    +      control.isAutoState(goog.ui.Component.State.HOVER));
    +  assertTrue('ACTIVE must be an auto-state',
    +      control.isAutoState(goog.ui.Component.State.ACTIVE));
    +  assertFalse('Control must not start out highlighted',
    +      control.isHighlighted());
    +  assertTrue('Control must start out active', control.isActive());
    +
    +  // Scenario 1:  Control is disabled.
    +  control.setEnabled(false);
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertFalse('Disabled control must not highlight on mouseup',
    +      control.isHighlighted());
    +  assertFalse('No action must have been performed', actionPerformed);
    +  control.setActive(true);
    +  control.setEnabled(true);
    +
    +  // Scenario 2:  HOVER is not an auto-state.
    +  control.setAutoStates(goog.ui.Component.State.HOVER, false);
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertFalse('Control must not highlight on mouseup if HOVER isn\'t an ' +
    +      'auto-state', control.isHighlighted());
    +  assertTrue('Action must have been performed even if HOVER isn\'t an ' +
    +      'auto-state', actionPerformed);
    +  assertFalse('Control must have been deactivated on mouseup even if ' +
    +      'HOVER isn\'t an auto-state', control.isActive());
    +  actionPerformed = false;
    +  control.setActive(true);
    +  control.setAutoStates(goog.ui.Component.State.HOVER, true);
    +
    +  // Scenario 3:  Control is not active.
    +  control.setActive(false);
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertTrue('Control must highlight on mouseup, even if inactive',
    +      control.isHighlighted());
    +  assertFalse('No action must have been performed if control is inactive',
    +      actionPerformed);
    +  assertFalse('Inactive control must remain inactive after mouseup',
    +      control.isActive());
    +  control.setHighlighted(false);
    +  control.setActive(true);
    +
    +  // Scenario 4:  performActionInternal() returns false.
    +  control.performActionInternal = function() {
    +    actionPerformed = true;
    +    return false;
    +  };
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertTrue('Control must highlight on mouseup, even if no action is ' +
    +      'performed', control.isHighlighted());
    +  assertTrue('performActionInternal must have been called',
    +      actionPerformed);
    +  assertTrue('Control must not deactivate if performActionInternal ' +
    +      'returns false', control.isActive());
    +  control.setHighlighted(false);
    +  actionPerformed = false;
    +  control.performActionInternal = function() {
    +    actionPerformed = true;
    +    return true;
    +  };
    +
    +  // Scenario 5:  ACTIVE is not an auto-state.
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE, false);
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertTrue('Control must highlight on mouseup even if ACTIVE isn\'t an ' +
    +      'auto-state', control.isHighlighted());
    +  assertTrue('Action must have been performed even if ACTIVE isn\'t an ' +
    +      'auto-state', actionPerformed);
    +  assertTrue('Control must not have been deactivated on mouseup if ' +
    +      'ACTIVE isn\'t an auto-state', control.isActive());
    +  actionPerformed = false;
    +  control.setHighlighted(false);
    +  control.setAutoStates(goog.ui.Component.State.ACTIVE, true);
    +
    +  // Scenario 6:  All is well.
    +  goog.testing.events.fireMouseUpEvent(element);
    +  assertTrue('Control must highlight on mouseup', control.isHighlighted());
    +  assertTrue('Action must have been performed', actionPerformed);
    +  assertFalse('Control must have been deactivated', control.isActive());
    +}
    +
    +function testDefaultConstructor() {
    +  var control = new goog.ui.Control();
    +  assertNull(control.getContent());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/controlcontent.js b/mapbox-gl/closure-library/closure/goog/ui/controlcontent.js
    new file mode 100644
    index 0000000000..907bec61ed
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/controlcontent.js
    @@ -0,0 +1,28 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Type declaration for control content.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +goog.provide('goog.ui.ControlContent');
    +
    +
    +/**
    + * Type declaration for text caption or DOM structure to be used as the content
    + * of {@link goog.ui.Control}s.
    + * @typedef {string|Node|Array<Node>|NodeList}
    + */
    +goog.ui.ControlContent;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/controlrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer.js
    new file mode 100644
    index 0000000000..52ac0db80d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer.js
    @@ -0,0 +1,947 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Base class for control renderers.
    + * TODO(attila):  If the renderer framework works well, pull it into Component.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ControlRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Control}s.  Can be used as-is, but
    + * subclasses of Control will probably want to use renderers specifically
    + * tailored for them by extending this class.  Controls that use renderers
    + * delegate one or more of the following API methods to the renderer:
    + * <ul>
    + *    <li>{@code createDom} - renders the DOM for the component
    + *    <li>{@code canDecorate} - determines whether an element can be decorated
    + *        by the component
    + *    <li>{@code decorate} - decorates an existing element with the component
    + *    <li>{@code setState} - updates the appearance of the component based on
    + *        its state
    + *    <li>{@code getContent} - returns the component's content
    + *    <li>{@code setContent} - sets the component's content
    + * </ul>
    + * Controls are stateful; renderers, on the other hand, should be stateless and
    + * reusable.
    + * @constructor
    + */
    +goog.ui.ControlRenderer = function() {
    +};
    +goog.addSingletonGetter(goog.ui.ControlRenderer);
    +goog.tagUnsealableClass(goog.ui.ControlRenderer);
    +
    +
    +/**
    + * Constructs a new renderer and sets the CSS class that the renderer will use
    + * as the base CSS class to apply to all elements rendered by that renderer.
    + * An example to use this function using a color palette:
    + *
    + * <pre>
    + * var myCustomRenderer = goog.ui.ControlRenderer.getCustomRenderer(
    + *     goog.ui.PaletteRenderer, 'my-special-palette');
    + * var newColorPalette = new goog.ui.ColorPalette(
    + *     colors, myCustomRenderer, opt_domHelper);
    + * </pre>
    + *
    + * Your CSS can look like this now:
    + * <pre>
    + * .my-special-palette { }
    + * .my-special-palette-table { }
    + * .my-special-palette-cell { }
    + * etc.
    + * </pre>
    + *
    + * <em>instead</em> of
    + * <pre>
    + * .CSS_MY_SPECIAL_PALETTE .goog-palette { }
    + * .CSS_MY_SPECIAL_PALETTE .goog-palette-table { }
    + * .CSS_MY_SPECIAL_PALETTE .goog-palette-cell { }
    + * etc.
    + * </pre>
    + *
    + * You would want to use this functionality when you want an instance of a
    + * component to have specific styles different than the other components of the
    + * same type in your application.  This avoids using descendant selectors to
    + * apply the specific styles to this component.
    + *
    + * @param {Function} ctor The constructor of the renderer you are trying to
    + *     create.
    + * @param {string} cssClassName The name of the CSS class for this renderer.
    + * @return {goog.ui.ControlRenderer} An instance of the desired renderer with
    + *     its getCssClass() method overridden to return the supplied custom CSS
    + *     class name.
    + */
    +goog.ui.ControlRenderer.getCustomRenderer = function(ctor, cssClassName) {
    +  var renderer = new ctor();
    +
    +  /**
    +   * Returns the CSS class to be applied to the root element of components
    +   * rendered using this renderer.
    +   * @return {string} Renderer-specific CSS class.
    +   */
    +  renderer.getCssClass = function() {
    +    return cssClassName;
    +  };
    +
    +  return renderer;
    +};
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ControlRenderer.CSS_CLASS = goog.getCssName('goog-control');
    +
    +
    +/**
    + * Array of arrays of CSS classes that we want composite classes added and
    + * removed for in IE6 and lower as a workaround for lack of multi-class CSS
    + * selector support.
    + *
    + * Subclasses that have accompanying CSS requiring this workaround should define
    + * their own static IE6_CLASS_COMBINATIONS constant and override
    + * getIe6ClassCombinations to return it.
    + *
    + * For example, if your stylesheet uses the selector .button.collapse-left
    + * (and is compiled to .button_collapse-left for the IE6 version of the
    + * stylesheet,) you should include ['button', 'collapse-left'] in this array
    + * and the class button_collapse-left will be applied to the root element
    + * whenever both button and collapse-left are applied individually.
    + *
    + * Members of each class name combination will be joined with underscores in the
    + * order that they're defined in the array. You should alphabetize them (for
    + * compatibility with the CSS compiler) unless you are doing something special.
    + * @type {Array<Array<string>>}
    + */
    +goog.ui.ControlRenderer.IE6_CLASS_COMBINATIONS = [];
    +
    +
    +/**
    + * Map of component states to corresponding ARIA attributes.  Since the mapping
    + * of component states to ARIA attributes is neither component- nor
    + * renderer-specific, this is a static property of the renderer class, and is
    + * initialized on first use.
    + * @type {Object<goog.ui.Component.State, goog.a11y.aria.State>}
    + * @private
    + */
    +goog.ui.ControlRenderer.ariaAttributeMap_;
    +
    +
    +/**
    + * Map of certain ARIA states to ARIA roles that support them. Used for checked
    + * and selected Component states because they are used on Components with ARIA
    + * roles that do not support the corresponding ARIA state.
    + * @private {!Object<goog.a11y.aria.Role, goog.a11y.aria.State>}
    + * @const
    + */
    +goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_ = goog.object.create(
    +    goog.a11y.aria.Role.BUTTON, goog.a11y.aria.State.PRESSED,
    +    goog.a11y.aria.Role.CHECKBOX, goog.a11y.aria.State.CHECKED,
    +    goog.a11y.aria.Role.MENU_ITEM, goog.a11y.aria.State.SELECTED,
    +    goog.a11y.aria.Role.MENU_ITEM_CHECKBOX, goog.a11y.aria.State.CHECKED,
    +    goog.a11y.aria.Role.MENU_ITEM_RADIO, goog.a11y.aria.State.CHECKED,
    +    goog.a11y.aria.Role.RADIO, goog.a11y.aria.State.CHECKED,
    +    goog.a11y.aria.Role.TAB, goog.a11y.aria.State.SELECTED,
    +    goog.a11y.aria.Role.TREEITEM, goog.a11y.aria.State.SELECTED);
    +
    +
    +/**
    + * Returns the ARIA role to be applied to the control.
    + * See http://wiki/Main/ARIA for more info.
    + * @return {goog.a11y.aria.Role|undefined} ARIA role.
    + */
    +goog.ui.ControlRenderer.prototype.getAriaRole = function() {
    +  // By default, the ARIA role is unspecified.
    +  return undefined;
    +};
    +
    +
    +/**
    + * Returns the control's contents wrapped in a DIV, with the renderer's own
    + * CSS class and additional state-specific classes applied to it.
    + * @param {goog.ui.Control} control Control to render.
    + * @return {Element} Root element for the control.
    + */
    +goog.ui.ControlRenderer.prototype.createDom = function(control) {
    +  // Create and return DIV wrapping contents.
    +  var element = control.getDomHelper().createDom(
    +      'div', this.getClassNames(control).join(' '), control.getContent());
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Takes the control's root element and returns the parent element of the
    + * control's contents.  Since by default controls are rendered as a single
    + * DIV, the default implementation returns the element itself.  Subclasses
    + * with more complex DOM structures must override this method as needed.
    + * @param {Element} element Root element of the control whose content element
    + *     is to be returned.
    + * @return {Element} The control's content element.
    + */
    +goog.ui.ControlRenderer.prototype.getContentElement = function(element) {
    +  return element;
    +};
    +
    +
    +/**
    + * Updates the control's DOM by adding or removing the specified class name
    + * to/from its root element. May add additional combined classes as needed in
    + * IE6 and lower. Because of this, subclasses should use this method when
    + * modifying class names on the control's root element.
    + * @param {goog.ui.Control|Element} control Control instance (or root element)
    + *     to be updated.
    + * @param {string} className CSS class name to add or remove.
    + * @param {boolean} enable Whether to add or remove the class name.
    + */
    +goog.ui.ControlRenderer.prototype.enableClassName = function(control,
    +    className, enable) {
    +  var element = /** @type {Element} */ (
    +      control.getElement ? control.getElement() : control);
    +  if (element) {
    +    var classNames = [className];
    +
    +    // For IE6, we need to enable any combined classes involving this class
    +    // as well.
    +    // TODO(user): Remove this as IE6 is no longer in use.
    +    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
    +      classNames = this.getAppliedCombinedClassNames_(
    +          goog.dom.classlist.get(element), className);
    +      classNames.push(className);
    +    }
    +
    +    goog.dom.classlist.enableAll(element, classNames, enable);
    +  }
    +};
    +
    +
    +/**
    + * Updates the control's DOM by adding or removing the specified extra class
    + * name to/from its element.
    + * @param {goog.ui.Control} control Control to be updated.
    + * @param {string} className CSS class name to add or remove.
    + * @param {boolean} enable Whether to add or remove the class name.
    + */
    +goog.ui.ControlRenderer.prototype.enableExtraClassName = function(control,
    +    className, enable) {
    +  // The base class implementation is trivial; subclasses should override as
    +  // needed.
    +  this.enableClassName(control, className, enable);
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element, false otherwise.
    + * The default implementation always returns true.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + */
    +goog.ui.ControlRenderer.prototype.canDecorate = function(element) {
    +  return true;
    +};
    +
    +
    +/**
    + * Default implementation of {@code decorate} for {@link goog.ui.Control}s.
    + * Initializes the control's ID, content, and state based on the ID of the
    + * element, its child nodes, and its CSS classes, respectively.  Returns the
    + * element.
    + * @param {goog.ui.Control} control Control instance to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + */
    +goog.ui.ControlRenderer.prototype.decorate = function(control, element) {
    +  // Set the control's ID to the decorated element's DOM ID, if any.
    +  if (element.id) {
    +    control.setId(element.id);
    +  }
    +
    +  // Set the control's content to the decorated element's content.
    +  var contentElem = this.getContentElement(element);
    +  if (contentElem && contentElem.firstChild) {
    +    control.setContentInternal(contentElem.firstChild.nextSibling ?
    +        goog.array.clone(contentElem.childNodes) : contentElem.firstChild);
    +  } else {
    +    control.setContentInternal(null);
    +  }
    +
    +  // Initialize the control's state based on the decorated element's CSS class.
    +  // This implementation is optimized to minimize object allocations, string
    +  // comparisons, and DOM access.
    +  var state = 0x00;
    +  var rendererClassName = this.getCssClass();
    +  var structuralClassName = this.getStructuralCssClass();
    +  var hasRendererClassName = false;
    +  var hasStructuralClassName = false;
    +  var hasCombinedClassName = false;
    +  var classNames = goog.array.toArray(goog.dom.classlist.get(element));
    +  goog.array.forEach(classNames, function(className) {
    +    if (!hasRendererClassName && className == rendererClassName) {
    +      hasRendererClassName = true;
    +      if (structuralClassName == rendererClassName) {
    +        hasStructuralClassName = true;
    +      }
    +    } else if (!hasStructuralClassName && className == structuralClassName) {
    +      hasStructuralClassName = true;
    +    } else {
    +      state |= this.getStateFromClass(className);
    +    }
    +    if (this.getStateFromClass(className) == goog.ui.Component.State.DISABLED) {
    +      goog.asserts.assertElement(contentElem);
    +      if (goog.dom.isFocusableTabIndex(contentElem)) {
    +        goog.dom.setFocusableTabIndex(contentElem, false);
    +      }
    +    }
    +  }, this);
    +  control.setStateInternal(state);
    +
    +  // Make sure the element has the renderer's CSS classes applied, as well as
    +  // any extra class names set on the control.
    +  if (!hasRendererClassName) {
    +    classNames.push(rendererClassName);
    +    if (structuralClassName == rendererClassName) {
    +      hasStructuralClassName = true;
    +    }
    +  }
    +  if (!hasStructuralClassName) {
    +    classNames.push(structuralClassName);
    +  }
    +  var extraClassNames = control.getExtraClassNames();
    +  if (extraClassNames) {
    +    classNames.push.apply(classNames, extraClassNames);
    +  }
    +
    +  // For IE6, rewrite all classes on the decorated element if any combined
    +  // classes apply.
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
    +    var combinedClasses = this.getAppliedCombinedClassNames_(
    +        classNames);
    +    if (combinedClasses.length > 0) {
    +      classNames.push.apply(classNames, combinedClasses);
    +      hasCombinedClassName = true;
    +    }
    +  }
    +
    +  // Only write to the DOM if new class names had to be added to the element.
    +  if (!hasRendererClassName || !hasStructuralClassName ||
    +      extraClassNames || hasCombinedClassName) {
    +    goog.dom.classlist.set(element, classNames.join(' '));
    +  }
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Initializes the control's DOM by configuring properties that can only be set
    + * after the DOM has entered the document.  This implementation sets up BiDi
    + * and keyboard focus.  Called from {@link goog.ui.Control#enterDocument}.
    + * @param {goog.ui.Control} control Control whose DOM is to be initialized
    + *     as it enters the document.
    + */
    +goog.ui.ControlRenderer.prototype.initializeDom = function(control) {
    +  // Initialize render direction (BiDi).  We optimize the left-to-right render
    +  // direction by assuming that elements are left-to-right by default, and only
    +  // updating their styling if they are explicitly set to right-to-left.
    +  if (control.isRightToLeft()) {
    +    this.setRightToLeft(control.getElement(), true);
    +  }
    +
    +  // Initialize keyboard focusability (tab index).  We assume that components
    +  // aren't focusable by default (i.e have no tab index), and only touch the
    +  // DOM if the component is focusable, enabled, and visible, and therefore
    +  // needs a tab index.
    +  if (control.isEnabled()) {
    +    this.setFocusable(control, control.isVisible());
    +  }
    +};
    +
    +
    +/**
    + * Sets the element's ARIA role.
    + * @param {Element} element Element to update.
    + * @param {?goog.a11y.aria.Role=} opt_preferredRole The preferred ARIA role.
    + */
    +goog.ui.ControlRenderer.prototype.setAriaRole = function(element,
    +    opt_preferredRole) {
    +  var ariaRole = opt_preferredRole || this.getAriaRole();
    +  if (ariaRole) {
    +    goog.asserts.assert(element,
    +        'The element passed as a first parameter cannot be null.');
    +    var currentRole = goog.a11y.aria.getRole(element);
    +    if (ariaRole == currentRole) {
    +      return;
    +    }
    +    goog.a11y.aria.setRole(element, ariaRole);
    +  }
    +};
    +
    +
    +/**
    + * Sets the element's ARIA attributes, including distinguishing between
    + * universally supported ARIA properties and ARIA states that are only
    + * supported by certain ARIA roles. Only attributes which are initialized to be
    + * true will be set.
    + * @param {!goog.ui.Control} control Control whose ARIA state will be updated.
    + * @param {!Element} element Element whose ARIA state is to be updated.
    + */
    +goog.ui.ControlRenderer.prototype.setAriaStates = function(control, element) {
    +  goog.asserts.assert(control);
    +  goog.asserts.assert(element);
    +
    +  var ariaLabel = control.getAriaLabel();
    +  if (goog.isDefAndNotNull(ariaLabel)) {
    +    this.setAriaLabel(element, ariaLabel);
    +  }
    +
    +  if (!control.isVisible()) {
    +    goog.a11y.aria.setState(
    +        element, goog.a11y.aria.State.HIDDEN, !control.isVisible());
    +  }
    +  if (!control.isEnabled()) {
    +    this.updateAriaState(
    +        element, goog.ui.Component.State.DISABLED, !control.isEnabled());
    +  }
    +  if (control.isSupportedState(goog.ui.Component.State.SELECTED)) {
    +    this.updateAriaState(
    +        element, goog.ui.Component.State.SELECTED, control.isSelected());
    +  }
    +  if (control.isSupportedState(goog.ui.Component.State.CHECKED)) {
    +    this.updateAriaState(
    +        element, goog.ui.Component.State.CHECKED, control.isChecked());
    +  }
    +  if (control.isSupportedState(goog.ui.Component.State.OPENED)) {
    +    this.updateAriaState(
    +        element, goog.ui.Component.State.OPENED, control.isOpen());
    +  }
    +};
    +
    +
    +/**
    + * Sets the element's ARIA label. This should be overriden by subclasses that
    + * don't apply the role directly on control.element_.
    + * @param {!Element} element Element whose ARIA label is to be updated.
    + * @param {string} ariaLabel Label to add to the element.
    + */
    +goog.ui.ControlRenderer.prototype.setAriaLabel = function(element, ariaLabel) {
    +  goog.a11y.aria.setLabel(element, ariaLabel);
    +};
    +
    +
    +/**
    + * Allows or disallows text selection within the control's DOM.
    + * @param {Element} element The control's root element.
    + * @param {boolean} allow Whether the element should allow text selection.
    + */
    +goog.ui.ControlRenderer.prototype.setAllowTextSelection = function(element,
    +    allow) {
    +  // On all browsers other than IE and Opera, it isn't necessary to recursively
    +  // apply unselectable styling to the element's children.
    +  goog.style.setUnselectable(element, !allow,
    +      !goog.userAgent.IE && !goog.userAgent.OPERA);
    +};
    +
    +
    +/**
    + * Applies special styling to/from the control's element if it is rendered
    + * right-to-left, and removes it if it is rendered left-to-right.
    + * @param {Element} element The control's root element.
    + * @param {boolean} rightToLeft Whether the component is rendered
    + *     right-to-left.
    + */
    +goog.ui.ControlRenderer.prototype.setRightToLeft = function(element,
    +    rightToLeft) {
    +  this.enableClassName(element,
    +      goog.getCssName(this.getStructuralCssClass(), 'rtl'), rightToLeft);
    +};
    +
    +
    +/**
    + * Returns true if the control's key event target supports keyboard focus
    + * (based on its {@code tabIndex} attribute), false otherwise.
    + * @param {goog.ui.Control} control Control whose key event target is to be
    + *     checked.
    + * @return {boolean} Whether the control's key event target is focusable.
    + */
    +goog.ui.ControlRenderer.prototype.isFocusable = function(control) {
    +  var keyTarget;
    +  if (control.isSupportedState(goog.ui.Component.State.FOCUSED) &&
    +      (keyTarget = control.getKeyEventTarget())) {
    +    return goog.dom.isFocusableTabIndex(keyTarget);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Updates the control's key event target to make it focusable or non-focusable
    + * via its {@code tabIndex} attribute.  Does nothing if the control doesn't
    + * support the {@code FOCUSED} state, or if it has no key event target.
    + * @param {goog.ui.Control} control Control whose key event target is to be
    + *     updated.
    + * @param {boolean} focusable Whether to enable keyboard focus support on the
    + *     control's key event target.
    + */
    +goog.ui.ControlRenderer.prototype.setFocusable = function(control, focusable) {
    +  var keyTarget;
    +  if (control.isSupportedState(goog.ui.Component.State.FOCUSED) &&
    +      (keyTarget = control.getKeyEventTarget())) {
    +    if (!focusable && control.isFocused()) {
    +      // Blur before hiding.  Note that IE calls onblur handlers asynchronously.
    +      try {
    +        keyTarget.blur();
    +      } catch (e) {
    +        // TODO(user|user):  Find out why this fails on IE.
    +      }
    +      // The blur event dispatched by the key event target element when blur()
    +      // was called on it should have been handled by the control's handleBlur()
    +      // method, so at this point the control should no longer be focused.
    +      // However, blur events are unreliable on IE and FF3, so if at this point
    +      // the control is still focused, we trigger its handleBlur() method
    +      // programmatically.
    +      if (control.isFocused()) {
    +        control.handleBlur(null);
    +      }
    +    }
    +    // Don't overwrite existing tab index values unless needed.
    +    if (goog.dom.isFocusableTabIndex(keyTarget) != focusable) {
    +      goog.dom.setFocusableTabIndex(keyTarget, focusable);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Shows or hides the element.
    + * @param {Element} element Element to update.
    + * @param {boolean} visible Whether to show the element.
    + */
    +goog.ui.ControlRenderer.prototype.setVisible = function(element, visible) {
    +  // The base class implementation is trivial; subclasses should override as
    +  // needed.  It should be possible to do animated reveals, for example.
    +  goog.style.setElementShown(element, visible);
    +  if (element) {
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.HIDDEN, !visible);
    +  }
    +};
    +
    +
    +/**
    + * Updates the appearance of the control in response to a state change.
    + * @param {goog.ui.Control} control Control instance to update.
    + * @param {goog.ui.Component.State} state State to enable or disable.
    + * @param {boolean} enable Whether the control is entering or exiting the state.
    + */
    +goog.ui.ControlRenderer.prototype.setState = function(control, state, enable) {
    +  var element = control.getElement();
    +  if (element) {
    +    var className = this.getClassForState(state);
    +    if (className) {
    +      this.enableClassName(control, className, enable);
    +    }
    +    this.updateAriaState(element, state, enable);
    +  }
    +};
    +
    +
    +/**
    + * Updates the element's ARIA (accessibility) attributes , including
    + * distinguishing between universally supported ARIA properties and ARIA states
    + * that are only supported by certain ARIA roles.
    + * @param {Element} element Element whose ARIA state is to be updated.
    + * @param {goog.ui.Component.State} state Component state being enabled or
    + *     disabled.
    + * @param {boolean} enable Whether the state is being enabled or disabled.
    + * @protected
    + */
    +goog.ui.ControlRenderer.prototype.updateAriaState = function(element, state,
    +    enable) {
    +  // Ensure the ARIA attribute map exists.
    +  if (!goog.ui.ControlRenderer.ariaAttributeMap_) {
    +    goog.ui.ControlRenderer.ariaAttributeMap_ = goog.object.create(
    +        goog.ui.Component.State.DISABLED, goog.a11y.aria.State.DISABLED,
    +        goog.ui.Component.State.SELECTED, goog.a11y.aria.State.SELECTED,
    +        goog.ui.Component.State.CHECKED, goog.a11y.aria.State.CHECKED,
    +        goog.ui.Component.State.OPENED, goog.a11y.aria.State.EXPANDED);
    +  }
    +  goog.asserts.assert(element,
    +      'The element passed as a first parameter cannot be null.');
    +  var ariaAttr = goog.ui.ControlRenderer.getAriaStateForAriaRole_(
    +      element, goog.ui.ControlRenderer.ariaAttributeMap_[state]);
    +  if (ariaAttr) {
    +    goog.a11y.aria.setState(element, ariaAttr, enable);
    +  }
    +};
    +
    +
    +/**
    + * Returns the appropriate ARIA attribute based on ARIA role if the ARIA
    + * attribute is an ARIA state.
    + * @param {!Element} element The element from which to get the ARIA role for
    + * matching ARIA state.
    + * @param {goog.a11y.aria.State} attr The ARIA attribute to check to see if it
    + * can be applied to the given ARIA role.
    + * @return {goog.a11y.aria.State} An ARIA attribute that can be applied to the
    + * given ARIA role.
    + * @private
    + */
    +goog.ui.ControlRenderer.getAriaStateForAriaRole_ = function(element, attr) {
    +  var role = goog.a11y.aria.getRole(element);
    +  if (!role) {
    +    return attr;
    +  }
    +  role = /** @type {goog.a11y.aria.Role} */ (role);
    +  var matchAttr = goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_[role] || attr;
    +  return goog.ui.ControlRenderer.isAriaState_(attr) ? matchAttr : attr;
    +};
    +
    +
    +/**
    + * Determines if the given ARIA attribute is an ARIA property or ARIA state.
    + * @param {goog.a11y.aria.State} attr The ARIA attribute to classify.
    + * @return {boolean} If the ARIA attribute is an ARIA state.
    + * @private
    + */
    +goog.ui.ControlRenderer.isAriaState_ = function(attr) {
    +  return attr == goog.a11y.aria.State.CHECKED ||
    +      attr == goog.a11y.aria.State.SELECTED;
    +};
    +
    +
    +/**
    + * Takes a control's root element, and sets its content to the given text
    + * caption or DOM structure.  The default implementation replaces the children
    + * of the given element.  Renderers that create more complex DOM structures
    + * must override this method accordingly.
    + * @param {Element} element The control's root element.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to be
    + *     set as the control's content. The DOM nodes will not be cloned, they
    + *     will only moved under the content element of the control.
    + */
    +goog.ui.ControlRenderer.prototype.setContent = function(element, content) {
    +  var contentElem = this.getContentElement(element);
    +  if (contentElem) {
    +    goog.dom.removeChildren(contentElem);
    +    if (content) {
    +      if (goog.isString(content)) {
    +        goog.dom.setTextContent(contentElem, content);
    +      } else {
    +        var childHandler = function(child) {
    +          if (child) {
    +            var doc = goog.dom.getOwnerDocument(contentElem);
    +            contentElem.appendChild(goog.isString(child) ?
    +                doc.createTextNode(child) : child);
    +          }
    +        };
    +        if (goog.isArray(content)) {
    +          // Array of nodes.
    +          goog.array.forEach(content, childHandler);
    +        } else if (goog.isArrayLike(content) && !('nodeType' in content)) {
    +          // NodeList. The second condition filters out TextNode which also has
    +          // length attribute but is not array like. The nodes have to be cloned
    +          // because childHandler removes them from the list during iteration.
    +          goog.array.forEach(
    +              goog.array.clone(/** @type {!NodeList} */(content)),
    +              childHandler);
    +        } else {
    +          // Node or string.
    +          childHandler(content);
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the element within the component's DOM that should receive keyboard
    + * focus (null if none).  The default implementation returns the control's root
    + * element.
    + * @param {goog.ui.Control} control Control whose key event target is to be
    + *     returned.
    + * @return {Element} The key event target.
    + */
    +goog.ui.ControlRenderer.prototype.getKeyEventTarget = function(control) {
    +  return control.getElement();
    +};
    +
    +
    +// CSS class name management.
    +
    +
    +/**
    + * Returns the CSS class name to be applied to the root element of all
    + * components rendered or decorated using this renderer.  The class name
    + * is expected to uniquely identify the renderer class, i.e. no two
    + * renderer classes are expected to share the same CSS class name.
    + * @return {string} Renderer-specific CSS class name.
    + */
    +goog.ui.ControlRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ControlRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Returns an array of combinations of classes to apply combined class names for
    + * in IE6 and below. See {@link IE6_CLASS_COMBINATIONS} for more detail. This
    + * method doesn't reference {@link IE6_CLASS_COMBINATIONS} so that it can be
    + * compiled out, but subclasses should return their IE6_CLASS_COMBINATIONS
    + * static constant instead.
    + * @return {Array<Array<string>>} Array of class name combinations.
    + */
    +goog.ui.ControlRenderer.prototype.getIe6ClassCombinations = function() {
    +  return [];
    +};
    +
    +
    +/**
    + * Returns the name of a DOM structure-specific CSS class to be applied to the
    + * root element of all components rendered or decorated using this renderer.
    + * Unlike the class name returned by {@link #getCssClass}, the structural class
    + * name may be shared among different renderers that generate similar DOM
    + * structures.  The structural class name also serves as the basis of derived
    + * class names used to identify and style structural elements of the control's
    + * DOM, as well as the basis for state-specific class names.  The default
    + * implementation returns the same class name as {@link #getCssClass}, but
    + * subclasses are expected to override this method as needed.
    + * @return {string} DOM structure-specific CSS class name (same as the renderer-
    + *     specific CSS class name by default).
    + */
    +goog.ui.ControlRenderer.prototype.getStructuralCssClass = function() {
    +  return this.getCssClass();
    +};
    +
    +
    +/**
    + * Returns all CSS class names applicable to the given control, based on its
    + * state.  The return value is an array of strings containing
    + * <ol>
    + *   <li>the renderer-specific CSS class returned by {@link #getCssClass},
    + *       followed by
    + *   <li>the structural CSS class returned by {@link getStructuralCssClass} (if
    + *       different from the renderer-specific CSS class), followed by
    + *   <li>any state-specific classes returned by {@link #getClassNamesForState},
    + *       followed by
    + *   <li>any extra classes returned by the control's {@code getExtraClassNames}
    + *       method and
    + *   <li>for IE6 and lower, additional combined classes from
    + *       {@link getAppliedCombinedClassNames_}.
    + * </ol>
    + * Since all controls have at least one renderer-specific CSS class name, this
    + * method is guaranteed to return an array of at least one element.
    + * @param {goog.ui.Control} control Control whose CSS classes are to be
    + *     returned.
    + * @return {!Array<string>} Array of CSS class names applicable to the control.
    + * @protected
    + */
    +goog.ui.ControlRenderer.prototype.getClassNames = function(control) {
    +  var cssClass = this.getCssClass();
    +
    +  // Start with the renderer-specific class name.
    +  var classNames = [cssClass];
    +
    +  // Add structural class name, if different.
    +  var structuralCssClass = this.getStructuralCssClass();
    +  if (structuralCssClass != cssClass) {
    +    classNames.push(structuralCssClass);
    +  }
    +
    +  // Add state-specific class names, if any.
    +  var classNamesForState = this.getClassNamesForState(control.getState());
    +  classNames.push.apply(classNames, classNamesForState);
    +
    +  // Add extra class names, if any.
    +  var extraClassNames = control.getExtraClassNames();
    +  if (extraClassNames) {
    +    classNames.push.apply(classNames, extraClassNames);
    +  }
    +
    +  // Add composite classes for IE6 support
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
    +    classNames.push.apply(classNames,
    +        this.getAppliedCombinedClassNames_(classNames));
    +  }
    +
    +  return classNames;
    +};
    +
    +
    +/**
    + * Returns an array of all the combined class names that should be applied based
    + * on the given list of classes. Checks the result of
    + * {@link getIe6ClassCombinations} for any combinations that have all
    + * members contained in classes. If a combination matches, the members are
    + * joined with an underscore (in order), and added to the return array.
    + *
    + * If opt_includedClass is provided, return only the combined classes that have
    + * all members contained in classes AND include opt_includedClass as well.
    + * opt_includedClass is added to classes as well.
    + * @param {goog.array.ArrayLike<string>} classes Array-like thing of classes to
    + *     return matching combined classes for.
    + * @param {?string=} opt_includedClass If provided, get only the combined
    + *     classes that include this one.
    + * @return {!Array<string>} Array of combined class names that should be
    + *     applied.
    + * @private
    + */
    +goog.ui.ControlRenderer.prototype.getAppliedCombinedClassNames_ = function(
    +    classes, opt_includedClass) {
    +  var toAdd = [];
    +  if (opt_includedClass) {
    +    classes = classes.concat([opt_includedClass]);
    +  }
    +  goog.array.forEach(this.getIe6ClassCombinations(), function(combo) {
    +    if (goog.array.every(combo, goog.partial(goog.array.contains, classes)) &&
    +        (!opt_includedClass || goog.array.contains(combo, opt_includedClass))) {
    +      toAdd.push(combo.join('_'));
    +    }
    +  });
    +  return toAdd;
    +};
    +
    +
    +/**
    + * Takes a bit mask of {@link goog.ui.Component.State}s, and returns an array
    + * of the appropriate class names representing the given state, suitable to be
    + * applied to the root element of a component rendered using this renderer, or
    + * null if no state-specific classes need to be applied.  This default
    + * implementation uses the renderer's {@link getClassForState} method to
    + * generate each state-specific class.
    + * @param {number} state Bit mask of component states.
    + * @return {!Array<string>} Array of CSS class names representing the given
    + *     state.
    + * @protected
    + */
    +goog.ui.ControlRenderer.prototype.getClassNamesForState = function(state) {
    +  var classNames = [];
    +  while (state) {
    +    // For each enabled state, push the corresponding CSS class name onto
    +    // the classNames array.
    +    var mask = state & -state;  // Least significant bit
    +    classNames.push(this.getClassForState(
    +        /** @type {goog.ui.Component.State} */ (mask)));
    +    state &= ~mask;
    +  }
    +  return classNames;
    +};
    +
    +
    +/**
    + * Takes a single {@link goog.ui.Component.State}, and returns the
    + * corresponding CSS class name (null if none).
    + * @param {goog.ui.Component.State} state Component state.
    + * @return {string|undefined} CSS class representing the given state (undefined
    + *     if none).
    + * @protected
    + */
    +goog.ui.ControlRenderer.prototype.getClassForState = function(state) {
    +  if (!this.classByState_) {
    +    this.createClassByStateMap_();
    +  }
    +  return this.classByState_[state];
    +};
    +
    +
    +/**
    + * Takes a single CSS class name which may represent a component state, and
    + * returns the corresponding component state (0x00 if none).
    + * @param {string} className CSS class name, possibly representing a component
    + *     state.
    + * @return {goog.ui.Component.State} state Component state corresponding
    + *     to the given CSS class (0x00 if none).
    + * @protected
    + */
    +goog.ui.ControlRenderer.prototype.getStateFromClass = function(className) {
    +  if (!this.stateByClass_) {
    +    this.createStateByClassMap_();
    +  }
    +  var state = parseInt(this.stateByClass_[className], 10);
    +  return /** @type {goog.ui.Component.State} */ (isNaN(state) ? 0x00 : state);
    +};
    +
    +
    +/**
    + * Creates the lookup table of states to classes, used during state changes.
    + * @private
    + */
    +goog.ui.ControlRenderer.prototype.createClassByStateMap_ = function() {
    +  var baseClass = this.getStructuralCssClass();
    +
    +  // This ensures space-separated css classnames are not allowed, which some
    +  // ControlRenderers had been doing.  See http://b/13694665.
    +  var isValidClassName = !goog.string.contains(
    +      goog.string.normalizeWhitespace(baseClass), ' ');
    +  goog.asserts.assert(isValidClassName,
    +      'ControlRenderer has an invalid css class: \'' + baseClass + '\'');
    +
    +  /**
    +   * Map of component states to state-specific structural class names,
    +   * used when changing the DOM in response to a state change.  Precomputed
    +   * and cached on first use to minimize object allocations and string
    +   * concatenation.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.classByState_ = goog.object.create(
    +      goog.ui.Component.State.DISABLED, goog.getCssName(baseClass, 'disabled'),
    +      goog.ui.Component.State.HOVER, goog.getCssName(baseClass, 'hover'),
    +      goog.ui.Component.State.ACTIVE, goog.getCssName(baseClass, 'active'),
    +      goog.ui.Component.State.SELECTED, goog.getCssName(baseClass, 'selected'),
    +      goog.ui.Component.State.CHECKED, goog.getCssName(baseClass, 'checked'),
    +      goog.ui.Component.State.FOCUSED, goog.getCssName(baseClass, 'focused'),
    +      goog.ui.Component.State.OPENED, goog.getCssName(baseClass, 'open'));
    +};
    +
    +
    +/**
    + * Creates the lookup table of classes to states, used during decoration.
    + * @private
    + */
    +goog.ui.ControlRenderer.prototype.createStateByClassMap_ = function() {
    +  // We need the classByState_ map so we can transpose it.
    +  if (!this.classByState_) {
    +    this.createClassByStateMap_();
    +  }
    +
    +  /**
    +   * Map of state-specific structural class names to component states,
    +   * used during element decoration.  Precomputed and cached on first use
    +   * to minimize object allocations and string concatenation.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.stateByClass_ = goog.object.transpose(this.classByState_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.html
    new file mode 100644
    index 0000000000..1cfce6a8ba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ControlRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ControlRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.js
    new file mode 100644
    index 0000000000..7709c748bd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/controlrenderer_test.js
    @@ -0,0 +1,1194 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ControlRendererTest');
    +goog.setTestOnly('goog.ui.ControlRendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.userAgent');
    +
    +var control, controlRenderer, testRenderer, propertyReplacer;
    +var sandbox;
    +var expectedFailures;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +
    +
    +/**
    + * A subclass of ControlRenderer that overrides {@code getAriaRole} and
    + * {@code getStructuralCssClass} for testing purposes.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +function TestRenderer() {
    +  goog.ui.ControlRenderer.call(this);
    +}
    +goog.inherits(TestRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(TestRenderer);
    +
    +TestRenderer.CSS_CLASS = 'goog-button';
    +
    +TestRenderer.IE6_CLASS_COMBINATIONS = [
    +  ['combined', 'goog-base-hover', 'goog-button'],
    +  ['combined', 'goog-base-disabled', 'goog-button'],
    +  ['combined', 'combined2', 'goog-base-hover', 'goog-base-rtl',
    +   'goog-button']
    +];
    +
    +
    +/** @override */
    +TestRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.BUTTON;
    +};
    +
    +
    +/** @override */
    +TestRenderer.prototype.getCssClass = function() {
    +  return TestRenderer.CSS_CLASS;
    +};
    +
    +
    +/** @override */
    +TestRenderer.prototype.getStructuralCssClass = function() {
    +  return 'goog-base';
    +};
    +
    +
    +/** @override */
    +TestRenderer.prototype.getIe6ClassCombinations = function() {
    +  return TestRenderer.IE6_CLASS_COMBINATIONS;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we're on Mac Safari 3.x.
    + */
    +function isMacSafari3() {
    +  return goog.userAgent.WEBKIT && goog.userAgent.MAC &&
    +      !goog.userAgent.isVersionOrHigher('527');
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on IE6 or lower.
    + */
    +function isIe6() {
    +  return goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7');
    +}
    +
    +function setUp() {
    +  control = new goog.ui.Control('Hello');
    +  controlRenderer = goog.ui.ControlRenderer.getInstance();
    +  testRenderer = TestRenderer.getInstance();
    +  propertyReplacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  propertyReplacer.reset();
    +  control.dispose();
    +  expectedFailures.handleTearDown();
    +  control = null;
    +  controlRenderer = null;
    +  testRenderer = null;
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('ControlRenderer singleton instance must not be null',
    +      controlRenderer);
    +  assertNotNull('TestRenderer singleton instance must not be null',
    +      testRenderer);
    +}
    +
    +function testGetCustomRenderer() {
    +  var cssClass = 'special-css-class';
    +  var renderer = goog.ui.ControlRenderer.getCustomRenderer(
    +      goog.ui.ControlRenderer, cssClass);
    +  assertEquals(
    +      'Renderer should have returned the custom CSS class.',
    +      cssClass,
    +      renderer.getCssClass());
    +}
    +
    +function testGetAriaRole() {
    +  assertUndefined('ControlRenderer\'s ARIA role must be undefined',
    +      controlRenderer.getAriaRole());
    +  assertEquals('TestRenderer\'s ARIA role must have expected value',
    +      goog.a11y.aria.Role.BUTTON, testRenderer.getAriaRole());
    +}
    +
    +function testCreateDom() {
    +  assertHTMLEquals('ControlRenderer must create correct DOM',
    +      '<div class="goog-control">Hello</div>',
    +      goog.dom.getOuterHtml(controlRenderer.createDom(control)));
    +  assertHTMLEquals('TestRenderer must create correct DOM',
    +      '<div class="goog-button goog-base">Hello</div>',
    +      goog.dom.getOuterHtml(testRenderer.createDom(control)));
    +}
    +
    +function testGetContentElement() {
    +  assertEquals('getContentElement() must return its argument', sandbox,
    +      controlRenderer.getContentElement(sandbox));
    +}
    +
    +function testEnableExtraClassName() {
    +  // enableExtraClassName() must be a no-op if control has no DOM.
    +  controlRenderer.enableExtraClassName(control, 'foo', true);
    +
    +  control.createDom();
    +  var element = control.getElement();
    +
    +  controlRenderer.enableExtraClassName(control, 'foo', true);
    +  assertSameElements('Extra class name must have been added',
    +      ['goog-control', 'foo'], goog.dom.classlist.get(element));
    +
    +  controlRenderer.enableExtraClassName(control, 'foo', true);
    +  assertSameElements('Enabling existing extra class name must be a no-op',
    +      ['goog-control', 'foo'], goog.dom.classlist.get(element));
    +
    +  controlRenderer.enableExtraClassName(control, 'bar', false);
    +  assertSameElements('Disabling nonexistent class name must be a no-op',
    +      ['goog-control', 'foo'], goog.dom.classlist.get(element));
    +
    +  controlRenderer.enableExtraClassName(control, 'foo', false);
    +  assertSameElements('Extra class name must have been removed',
    +      ['goog-control'], goog.dom.classlist.get(element));
    +}
    +
    +function testCanDecorate() {
    +  assertTrue('canDecorate() must return true',
    +      controlRenderer.canDecorate());
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML = '<div id="foo">Hello, world!</div>';
    +  var foo = goog.dom.getElement('foo');
    +  var element = controlRenderer.decorate(control, foo);
    +
    +  assertEquals('decorate() must return its argument', foo, element);
    +  assertEquals('Decorated control\'s ID must be set', 'foo',
    +      control.getId());
    +  assertTrue('Decorated control\'s content must be a text node',
    +      control.getContent().nodeType == goog.dom.NodeType.TEXT);
    +  assertEquals('Decorated control\'s content must have expected value',
    +      'Hello, world!', control.getContent().nodeValue);
    +  assertEquals('Decorated control\'s state must be as expected', 0x00,
    +      control.getState());
    +  assertSameElements('Decorated element\'s classes must be as expected',
    +      ['goog-control'], goog.dom.classlist.get(element));
    +}
    +
    +function testDecorateComplexDom() {
    +  sandbox.innerHTML = '<div id="foo"><i>Hello</i>,<b>world</b>!</div>';
    +  var foo = goog.dom.getElement('foo');
    +  var element = controlRenderer.decorate(control, foo);
    +
    +  assertEquals('decorate() must return its argument', foo, element);
    +  assertEquals('Decorated control\'s ID must be set', 'foo',
    +      control.getId());
    +  assertTrue('Decorated control\'s content must be an array',
    +      goog.isArray(control.getContent()));
    +  assertEquals('Decorated control\'s content must have expected length', 4,
    +      control.getContent().length);
    +  assertEquals('Decorated control\'s state must be as expected', 0x00,
    +      control.getState());
    +  assertSameElements('Decorated element\'s classes must be as expected',
    +      ['goog-control'], goog.dom.classlist.get(element));
    +}
    +
    +function testDecorateWithClasses() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="app goog-base-disabled goog-base-hover"></div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  control.addClassName('extra');
    +  var element = testRenderer.decorate(control, foo);
    +
    +  assertEquals('decorate() must return its argument', foo, element);
    +  assertEquals('Decorated control\'s ID must be set', 'foo',
    +      control.getId());
    +  assertNull('Decorated control\'s content must be null',
    +      control.getContent());
    +  assertEquals('Decorated control\'s state must be as expected',
    +      goog.ui.Component.State.DISABLED | goog.ui.Component.State.HOVER,
    +      control.getState());
    +  assertSameElements('Decorated element\'s classes must be as expected', [
    +    'app',
    +    'extra',
    +    'goog-base',
    +    'goog-base-disabled',
    +    'goog-base-hover',
    +    'goog-button'
    +  ], goog.dom.classlist.get(element));
    +}
    +
    +function testDecorateOptimization() {
    +  // Temporarily replace goog.dom.classlist.set().
    +  propertyReplacer.set(goog.dom.classlist, 'set', function() {
    +    fail('goog.dom.classlist.set() must not be called');
    +  });
    +
    +  // Since foo has all required classes, goog.dom.classlist.set() must not be
    +  // called at all.
    +  sandbox.innerHTML = '<div id="foo" class="goog-control">Foo</div>';
    +  controlRenderer.decorate(control, goog.dom.getElement('foo'));
    +
    +  // Since bar has all required classes, goog.dom.classlist.set() must not be
    +  // called at all.
    +  sandbox.innerHTML = '<div id="bar" class="goog-base goog-button">Bar' +
    +      '</div>';
    +  testRenderer.decorate(control, goog.dom.getElement('bar'));
    +
    +  // Since baz has all required classes, goog.dom.classlist.set() must not be
    +  // called at all.
    +  sandbox.innerHTML = '<div id="baz" class="goog-base goog-button ' +
    +      'goog-button-disabled">Baz</div>';
    +  testRenderer.decorate(control, goog.dom.getElement('baz'));
    +}
    +
    +function testInitializeDom() {
    +  var renderer = new goog.ui.ControlRenderer();
    +
    +  // Replace setRightToLeft().
    +  renderer.setRightToLeft = function() {
    +    fail('setRightToLeft() must not be called');
    +  };
    +
    +  // When a control with default render direction enters the document,
    +  // setRightToLeft() must not be called.
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +
    +  // When a control in the default state (enabled, visible, focusable)
    +  // enters the document, it must get a tab index.
    +  // Expected to fail on Mac Safari 3, because it doesn't support tab index.
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    assertTrue('Enabled, visible, focusable control must have tab index',
    +        goog.dom.isFocusableTabIndex(control.getElement()));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testInitializeDomDecorated() {
    +  var renderer = new goog.ui.ControlRenderer();
    +
    +  // Replace setRightToLeft().
    +  renderer.setRightToLeft = function() {
    +    fail('setRightToLeft() must not be called');
    +  };
    +
    +  sandbox.innerHTML = '<div id="foo" class="goog-control">Foo</div>';
    +
    +  // When a control with default render direction enters the document,
    +  // setRightToLeft() must not be called.
    +  control.setRenderer(renderer);
    +  control.decorate(goog.dom.getElement('foo'));
    +
    +  // When a control in the default state (enabled, visible, focusable)
    +  // enters the document, it must get a tab index.
    +  // Expected to fail on Mac Safari 3, because it doesn't support tab index.
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    assertTrue('Enabled, visible, focusable control must have tab index',
    +        goog.dom.isFocusableTabIndex(control.getElement()));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testInitializeDomDisabledBiDi() {
    +  var renderer = new goog.ui.ControlRenderer();
    +
    +  // Replace setFocusable().
    +  renderer.setFocusable = function() {
    +    fail('setFocusable() must not be called');
    +  };
    +
    +  // When a disabled control enters the document, setFocusable() must not
    +  // be called.
    +  control.setEnabled(false);
    +  control.setRightToLeft(true);
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +
    +  // When a right-to-left control enters the document, special stying must
    +  // be applied.
    +  assertSameElements('BiDi control must have right-to-left class',
    +      ['goog-control', 'goog-control-disabled', 'goog-control-rtl'],
    +      goog.dom.classlist.get(control.getElement()));
    +}
    +
    +function testInitializeDomDisabledBiDiDecorated() {
    +  var renderer = new goog.ui.ControlRenderer();
    +
    +  // Replace setFocusable().
    +  renderer.setFocusable = function() {
    +    fail('setFocusable() must not be called');
    +  };
    +
    +  sandbox.innerHTML =
    +      '<div dir="rtl">\n' +
    +      '  <div id="foo" class="goog-control-disabled">Foo</div>\n' +
    +      '</div>\n';
    +
    +  // When a disabled control enters the document, setFocusable() must not
    +  // be called.
    +  control.setRenderer(renderer);
    +  control.decorate(goog.dom.getElement('foo'));
    +
    +  // When a right-to-left control enters the document, special stying must
    +  // be applied.
    +  assertSameElements('BiDi control must have right-to-left class',
    +      ['goog-control', 'goog-control-disabled', 'goog-control-rtl'],
    +      goog.dom.classlist.get(control.getElement()));
    +}
    +
    +function testSetAriaRole() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +
    +  var foo = goog.dom.getElement('foo');
    +  assertNotNull(foo);
    +  controlRenderer.setAriaRole(foo);
    +  assertEvaluatesToFalse('The role should be empty.',
    +      goog.a11y.aria.getRole(foo));
    +  var bar = goog.dom.getElement('bar');
    +  assertNotNull(bar);
    +  testRenderer.setAriaRole(bar);
    +  assertEquals('Element must have expected ARIA role',
    +      goog.a11y.aria.Role.BUTTON, goog.a11y.aria.getRole(bar));
    +}
    +
    +function testSetAriaStatesHidden() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  control.setVisible(true);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-hidden.', '',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.HIDDEN));
    +
    +  control.setVisible(false);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-hidden.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.HIDDEN));
    +}
    +
    +function testSetAriaStatesDisabled() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  control.setEnabled(true);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-disabled.', '',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.DISABLED));
    +
    +  control.setEnabled(false);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-disabled.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.DISABLED));
    +}
    +
    +function testSetAriaStatesSelected() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +
    +  control.setSelected(true);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-selected.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.SELECTED));
    +
    +  control.setSelected(false);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-selected.', 'false',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testSetAriaStatesChecked() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +
    +  control.setChecked(true);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-checked.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.CHECKED));
    +
    +  control.setChecked(false);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-checked.', 'false',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSetAriaStatesExpanded() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +  var foo = goog.dom.getElement('foo');
    +  control.setSupportedState(goog.ui.Component.State.OPENED, true);
    +
    +  control.setOpen(true);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-expanded.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.EXPANDED));
    +
    +  control.setOpen(false);
    +  controlRenderer.setAriaStates(control, foo);
    +
    +  assertEquals('ControlRenderer did not set aria-expanded.', 'false',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.EXPANDED));
    +}
    +
    +function testSetAllowTextSelection() {
    +  sandbox.innerHTML = '<div id="foo"><span>Foo</span></div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  controlRenderer.setAllowTextSelection(foo, false);
    +  assertTrue('Parent element must be unselectable on all browsers',
    +      goog.style.isUnselectable(foo));
    +  if (goog.userAgent.IE || goog.userAgent.OPERA) {
    +    assertTrue('On IE and Opera, child element must also be unselectable',
    +        goog.style.isUnselectable(foo.firstChild));
    +  } else {
    +    assertFalse('On browsers other than IE and Opera, the child element ' +
    +        'must not be unselectable',
    +        goog.style.isUnselectable(foo.firstChild));
    +  }
    +
    +  controlRenderer.setAllowTextSelection(foo, true);
    +  assertFalse('Parent element must be selectable',
    +      goog.style.isUnselectable(foo));
    +  assertFalse('Child element must be unselectable',
    +      goog.style.isUnselectable(foo.firstChild));
    +}
    +
    +function testSetRightToLeft() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div><div id="bar">Bar</div>';
    +
    +  var foo = goog.dom.getElement('foo');
    +  controlRenderer.setRightToLeft(foo, true);
    +  assertSameElements('Element must have right-to-left class applied',
    +      ['goog-control-rtl'], goog.dom.classlist.get(foo));
    +  controlRenderer.setRightToLeft(foo, false);
    +  assertSameElements('Element must not have right-to-left class applied',
    +      [], goog.dom.classlist.get(foo));
    +
    +  var bar = goog.dom.getElement('bar');
    +  testRenderer.setRightToLeft(bar, true);
    +  assertSameElements('Element must have right-to-left class applied',
    +      ['goog-base-rtl'], goog.dom.classlist.get(bar));
    +  testRenderer.setRightToLeft(bar, false);
    +  assertSameElements('Element must not have right-to-left class applied',
    +      [], goog.dom.classlist.get(bar));
    +}
    +
    +function testIsFocusable() {
    +  control.render(sandbox);
    +  // Expected to fail on Mac Safari 3, because it doesn't support tab index.
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    assertTrue('Control\'s key event target must be focusable',
    +        controlRenderer.isFocusable(control));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testIsFocusableForNonFocusableControl() {
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertFalse('Non-focusable control\'s key event target must not be ' +
    +      'focusable', controlRenderer.isFocusable(control));
    +}
    +
    +function testIsFocusableForControlWithoutKeyEventTarget() {
    +  // Unrendered control has no key event target.
    +  assertNull('Unrendered control must not have key event target',
    +      control.getKeyEventTarget());
    +  assertFalse('isFocusable() must return null if no key event target',
    +      controlRenderer.isFocusable(control));
    +}
    +
    +function testSetFocusable() {
    +  control.render(sandbox);
    +  controlRenderer.setFocusable(control, false);
    +  assertFalse('Control\'s key event target must not have tab index',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  controlRenderer.setFocusable(control, true);
    +  // Expected to fail on Mac Safari 3, because it doesn't support tab index.
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    assertTrue('Control\'s key event target must have focusable tab index',
    +        goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testSetFocusableForNonFocusableControl() {
    +  control.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +  control.render(sandbox);
    +  assertFalse('Non-focusable control\'s key event target must not be ' +
    +      'focusable',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +  controlRenderer.setFocusable(control, true);
    +  assertFalse('Non-focusable control\'s key event target must not be ' +
    +      'focusable, even after calling setFocusable(true)',
    +      goog.dom.isFocusableTabIndex(control.getKeyEventTarget()));
    +}
    +
    +function testSetVisible() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +  assertTrue('Element must be visible', foo.style.display != 'none');
    +  controlRenderer.setVisible(foo, true);
    +  assertEquals('ControlRenderer did not set aria-hidden.', 'false',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.HIDDEN));
    +  assertTrue('Element must still be visible', foo.style.display != 'none');
    +  controlRenderer.setVisible(foo, false);
    +  assertEquals('ControlRenderer did not set aria-hidden.', 'true',
    +      goog.a11y.aria.getState(foo, goog.a11y.aria.State.HIDDEN));
    +  assertTrue('Element must be hidden', foo.style.display == 'none');
    +}
    +
    +function testSetState() {
    +  control.setRenderer(testRenderer);
    +  control.createDom();
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertSameElements('Control must have expected class names',
    +      ['goog-button', 'goog-base'], goog.dom.classlist.get(element));
    +  assertEquals('Control must not have disabled ARIA state', '',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.DISABLED));
    +
    +  testRenderer.setState(control, goog.ui.Component.State.DISABLED, true);
    +  assertSameElements('Control must have disabled class name',
    +      ['goog-button', 'goog-base', 'goog-base-disabled'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Control must have disabled ARIA state', 'true',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.DISABLED));
    +
    +  testRenderer.setState(control, goog.ui.Component.State.DISABLED, false);
    +  assertSameElements('Control must no longer have disabled class name',
    +      ['goog-button', 'goog-base'], goog.dom.classlist.get(element));
    +  assertEquals('Control must not have disabled ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.DISABLED));
    +
    +  testRenderer.setState(control, 0xFFFFFF, true);
    +  assertSameElements('Class names must be unchanged for invalid state',
    +      ['goog-button', 'goog-base'], goog.dom.classlist.get(element));
    +}
    +
    +function testUpdateAriaStateDisabled() {
    +  control.createDom();
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.DISABLED,
    +      true);
    +  assertEquals('Control must have disabled ARIA state', 'true',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.DISABLED));
    +
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.DISABLED,
    +      false);
    +  assertEquals('Control must no longer have disabled ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.DISABLED));
    +}
    +
    +function testSetAriaStatesRender_ariaStateDisabled() {
    +  control.setEnabled(false);
    +  var renderer = new goog.ui.ControlRenderer();
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +  assertEquals('Control must have disabled ARIA state', 'true',
    +      goog.a11y.aria.getState(element,
    +      goog.a11y.aria.State.DISABLED));
    +}
    +
    +function testSetAriaStatesDecorate_ariaStateDisabled() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="app goog-base-disabled"></div>';
    +  var element = goog.dom.getElement('foo');
    +
    +  control.setRenderer(testRenderer);
    +  control.decorate(element);
    +  assertNotNull(element);
    +  assertFalse('Control must be disabled', control.isEnabled());
    +  assertEquals('Control must have disabled ARIA state', 'true',
    +      goog.a11y.aria.getState(element,
    +      goog.a11y.aria.State.DISABLED));
    +}
    +
    +function testUpdateAriaStateSelected() {
    +  control.createDom();
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.SELECTED,
    +      true);
    +  assertEquals('Control must have selected ARIA state', 'true',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.SELECTED));
    +
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.SELECTED,
    +      false);
    +  assertEquals('Control must no longer have selected ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element,
    +          goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testSetAriaStatesRender_ariaStateSelected() {
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  control.setSelected(true);
    +
    +  var renderer = new goog.ui.ControlRenderer();
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertTrue('Control must be selected', control.isSelected());
    +  assertEquals('Control must have selected ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testSetAriaStatesRender_ariaStateNotSelected() {
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +
    +  var renderer = new goog.ui.ControlRenderer();
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertFalse('Control must not be selected', control.isSelected());
    +  assertEquals('Control must have not-selected ARIA state', 'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testSetAriaStatesDecorate_ariaStateSelected() {
    +  control.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +
    +  sandbox.innerHTML =
    +      '<div id="foo" class="app goog-control-selected"></div>';
    +  var element = goog.dom.getElement('foo');
    +
    +  control.setRenderer(controlRenderer);
    +  control.decorate(element);
    +  assertNotNull(element);
    +  assertTrue('Control must be selected', control.isSelected());
    +  assertEquals('Control must have selected ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.SELECTED));
    +}
    +
    +function testUpdateAriaStateChecked() {
    +  control.createDom();
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +      true);
    +  assertEquals('Control must have checked ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.CHECKED,
    +      false);
    +  assertEquals('Control must no longer have checked ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSetAriaStatesRender_ariaStateChecked() {
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  control.setChecked(true);
    +
    +  var renderer = new goog.ui.ControlRenderer();
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertTrue('Control must be checked', control.isChecked());
    +  assertEquals('Control must have checked ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSetAriaStatesDecorate_ariaStateChecked() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="app goog-control-checked"></div>';
    +  var element = goog.dom.getElement('foo');
    +
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  control.decorate(element);
    +  assertNotNull(element);
    +  assertTrue('Control must be checked', control.isChecked());
    +  assertEquals('Control must have checked ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testUpdateAriaStateOpened() {
    +  control.createDom();
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.OPENED,
    +      true);
    +  assertEquals('Control must have expanded ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.EXPANDED));
    +
    +  controlRenderer.updateAriaState(element, goog.ui.Component.State.OPENED,
    +      false);
    +  assertEquals('Control must no longer have expanded ARIA state',
    +      'false',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.EXPANDED));
    +}
    +
    +function testSetAriaStatesRender_ariaStateOpened() {
    +  control.setSupportedState(goog.ui.Component.State.OPENED, true);
    +  control.setOpen(true);
    +
    +  var renderer = new goog.ui.ControlRenderer();
    +  control.setRenderer(renderer);
    +  control.render(sandbox);
    +  var element = control.getElement();
    +  assertNotNull(element);
    +  assertTrue('Control must be opened', control.isOpen());
    +  assertEquals('Control must have expanded ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.EXPANDED));
    +}
    +
    +function testSetAriaStatesDecorate_ariaStateOpened() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="app goog-base-open"></div>';
    +  var element = goog.dom.getElement('foo');
    +
    +  control.setSupportedState(goog.ui.Component.State.OPENED, true);
    +  control.setRenderer(testRenderer);
    +  control.decorate(element);
    +  assertNotNull(element);
    +  assertTrue('Control must be opened', control.isOpen());
    +  assertEquals('Control must have expanded ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.EXPANDED));
    +}
    +
    +function testSetAriaStateRoleNotInMap() {
    +  sandbox.innerHTML = '<div id="foo" role="option">Hello, world!</div>';
    +  control.setRenderer(controlRenderer);
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = goog.dom.getElement('foo');
    +  control.decorate(element);
    +  assertEquals('Element should have ARIA role option.',
    +      goog.a11y.aria.Role.OPTION, goog.a11y.aria.getRole(element));
    +  control.setStateInternal(goog.ui.Component.State.DISABLED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-disabled true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.DISABLED));
    +  control.setStateInternal(goog.ui.Component.State.CHECKED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-checked true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSetAriaStateRoleInMapMatches() {
    +  sandbox.innerHTML = '<div id="foo" role="checkbox">Hello, world!</div>';
    +  control.setRenderer(controlRenderer);
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = goog.dom.getElement('foo');
    +  control.decorate(element);
    +  assertEquals('Element should have ARIA role checkbox.',
    +      goog.a11y.aria.Role.CHECKBOX, goog.a11y.aria.getRole(element));
    +  control.setStateInternal(goog.ui.Component.State.DISABLED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-disabled true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.DISABLED));
    +  control.setStateInternal(goog.ui.Component.State.CHECKED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-checked true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testSetAriaStateRoleInMapNotMatches() {
    +  sandbox.innerHTML = '<div id="foo" role="button">Hello, world!</div>';
    +  control.setRenderer(controlRenderer);
    +  control.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = goog.dom.getElement('foo');
    +  control.decorate(element);
    +  assertEquals('Element should have ARIA role button.',
    +      goog.a11y.aria.Role.BUTTON, goog.a11y.aria.getRole(element));
    +  control.setStateInternal(goog.ui.Component.State.DISABLED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-disabled true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.DISABLED));
    +  control.setStateInternal(goog.ui.Component.State.CHECKED, true);
    +  controlRenderer.setAriaStates(control, element);
    +  assertEquals('Element should have aria-pressed true', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.PRESSED));
    +  assertEquals('Element should not have aria-checked', '',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testToggleAriaStateMap() {
    +  var map = goog.object.create(
    +      goog.a11y.aria.Role.BUTTON, goog.a11y.aria.State.PRESSED,
    +      goog.a11y.aria.Role.CHECKBOX, goog.a11y.aria.State.CHECKED,
    +      goog.a11y.aria.Role.MENU_ITEM, goog.a11y.aria.State.SELECTED,
    +      goog.a11y.aria.Role.MENU_ITEM_CHECKBOX, goog.a11y.aria.State.CHECKED,
    +      goog.a11y.aria.Role.MENU_ITEM_RADIO, goog.a11y.aria.State.CHECKED,
    +      goog.a11y.aria.Role.RADIO, goog.a11y.aria.State.CHECKED,
    +      goog.a11y.aria.Role.TAB, goog.a11y.aria.State.SELECTED,
    +      goog.a11y.aria.Role.TREEITEM, goog.a11y.aria.State.SELECTED);
    +  for (var key in map) {
    +    assertTrue('Toggle ARIA state map incorrect.',
    +        key in goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_);
    +    assertEquals('Toggle ARIA state map incorrect.', map[key],
    +        goog.ui.ControlRenderer.TOGGLE_ARIA_STATE_MAP_[key]);
    +  }
    +}
    +function testSetContent() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox, 'Not so fast!');
    +  assertEquals('Element must contain expected text value', 'Not so fast!',
    +      goog.dom.getTextContent(sandbox));
    +}
    +
    +function testSetContentNull() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox, null);
    +  assertEquals('Element must have no child nodes', 0,
    +      sandbox.childNodes.length);
    +  assertEquals('Element must contain expected text value', '',
    +      goog.dom.getTextContent(sandbox));
    +}
    +
    +function testSetContentEmpty() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox, '');
    +  assertEquals('Element must not have children', 0,
    +      sandbox.childNodes.length);
    +  assertEquals('Element must contain expected text value', '',
    +      goog.dom.getTextContent(sandbox));
    +}
    +
    +function testSetContentWhitespace() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox, ' ');
    +  assertEquals('Element must have one child', 1,
    +      sandbox.childNodes.length);
    +  assertEquals('Child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.firstChild.nodeType);
    +  assertEquals('Element must contain expected text value', ' ',
    +      goog.dom.getTextContent(sandbox));
    +}
    +
    +function testSetContentTextNode() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox, document.createTextNode('Text'));
    +  assertEquals('Element must have one child', 1,
    +      sandbox.childNodes.length);
    +  assertEquals('Child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.firstChild.nodeType);
    +  assertEquals('Element must contain expected text value', 'Text',
    +      goog.dom.getTextContent(sandbox));
    +}
    +
    +function testSetContentElementNode() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox,
    +      goog.dom.createDom('div', {id: 'foo'}, 'Foo'));
    +  assertEquals('Element must have one child', 1,
    +      sandbox.childNodes.length);
    +  assertEquals('Child must be an element node', goog.dom.NodeType.ELEMENT,
    +      sandbox.firstChild.nodeType);
    +  assertHTMLEquals('Element must contain expected HTML',
    +      '<div id="foo">Foo</div>', sandbox.innerHTML);
    +}
    +
    +function testSetContentArray() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  controlRenderer.setContent(sandbox,
    +      ['Hello, ', goog.dom.createDom('b', null, 'world'), '!']);
    +  assertEquals('Element must have three children', 3,
    +      sandbox.childNodes.length);
    +  assertEquals('1st child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.childNodes[0].nodeType);
    +  assertEquals('2nd child must be an element', goog.dom.NodeType.ELEMENT,
    +      sandbox.childNodes[1].nodeType);
    +  assertEquals('3rd child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.childNodes[2].nodeType);
    +  assertHTMLEquals('Element must contain expected HTML',
    +      'Hello, <b>world</b>!', sandbox.innerHTML);
    +}
    +
    +function testSetContentNodeList() {
    +  sandbox.innerHTML = 'Hello, world!';
    +  var div = goog.dom.createDom('div', null, 'Hello, ',
    +      goog.dom.createDom('b', null, 'world'), '!');
    +  controlRenderer.setContent(sandbox, div.childNodes);
    +  assertEquals('Element must have three children', 3,
    +      sandbox.childNodes.length);
    +  assertEquals('1st child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.childNodes[0].nodeType);
    +  assertEquals('2nd child must be an element', goog.dom.NodeType.ELEMENT,
    +      sandbox.childNodes[1].nodeType);
    +  assertEquals('3rd child must be a text node', goog.dom.NodeType.TEXT,
    +      sandbox.childNodes[2].nodeType);
    +  assertHTMLEquals('Element must contain expected HTML',
    +      'Hello, <b>world</b>!', sandbox.innerHTML);
    +}
    +
    +function testGetKeyEventTarget() {
    +  assertNull('Key event target for unrendered control must be null',
    +      controlRenderer.getKeyEventTarget(control));
    +  control.createDom();
    +  assertEquals('Key event target for rendered control must be its element',
    +      control.getElement(), controlRenderer.getKeyEventTarget(control));
    +}
    +
    +function testGetCssClass() {
    +  assertEquals('ControlRenderer\'s CSS class must have expected value',
    +      goog.ui.ControlRenderer.CSS_CLASS, controlRenderer.getCssClass());
    +  assertEquals('TestRenderer\'s CSS class must have expected value',
    +      TestRenderer.CSS_CLASS, testRenderer.getCssClass());
    +}
    +
    +function testGetStructuralCssClass() {
    +  assertEquals('ControlRenderer\'s structural class must be its CSS class',
    +      controlRenderer.getCssClass(),
    +      controlRenderer.getStructuralCssClass());
    +  assertEquals('TestRenderer\'s structural class must have expected value',
    +      'goog-base', testRenderer.getStructuralCssClass());
    +}
    +
    +function testGetClassNames() {
    +  // These tests use assertArrayEquals, because the order is significant.
    +  assertArrayEquals('ControlRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-control'],
    +      controlRenderer.getClassNames(control));
    +  assertArrayEquals('TestRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-button', 'goog-base'],
    +      testRenderer.getClassNames(control));
    +}
    +
    +function testGetClassNamesForControlWithState() {
    +  control.setStateInternal(goog.ui.Component.State.HOVER |
    +      goog.ui.Component.State.ACTIVE);
    +
    +  // These tests use assertArrayEquals, because the order is significant.
    +  assertArrayEquals('ControlRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-control', 'goog-control-hover', 'goog-control-active'],
    +      controlRenderer.getClassNames(control));
    +  assertArrayEquals('TestRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-button', 'goog-base', 'goog-base-hover', 'goog-base-active'],
    +      testRenderer.getClassNames(control));
    +}
    +
    +function testGetClassNamesForControlWithExtraClassNames() {
    +  control.addClassName('foo');
    +  control.addClassName('bar');
    +
    +  // These tests use assertArrayEquals, because the order is significant.
    +  assertArrayEquals('ControlRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-control', 'foo', 'bar'],
    +      controlRenderer.getClassNames(control));
    +  assertArrayEquals('TestRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-button', 'goog-base', 'foo', 'bar'],
    +      testRenderer.getClassNames(control));
    +}
    +
    +function testGetClassNamesForControlWithStateAndExtraClassNames() {
    +  control.setStateInternal(goog.ui.Component.State.HOVER |
    +      goog.ui.Component.State.ACTIVE);
    +  control.addClassName('foo');
    +  control.addClassName('bar');
    +
    +  // These tests use assertArrayEquals, because the order is significant.
    +  assertArrayEquals('ControlRenderer must return expected class names ' +
    +      'in the expected order', [
    +        'goog-control',
    +        'goog-control-hover',
    +        'goog-control-active',
    +        'foo',
    +        'bar'
    +      ], controlRenderer.getClassNames(control));
    +  assertArrayEquals('TestRenderer must return expected class names ' +
    +      'in the expected order', [
    +        'goog-button',
    +        'goog-base',
    +        'goog-base-hover',
    +        'goog-base-active',
    +        'foo',
    +        'bar'
    +      ], testRenderer.getClassNames(control));
    +}
    +
    +function testGetClassNamesForState() {
    +  // These tests use assertArrayEquals, because the order is significant.
    +  assertArrayEquals('ControlRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-control-hover', 'goog-control-checked'],
    +      controlRenderer.getClassNamesForState(goog.ui.Component.State.HOVER |
    +          goog.ui.Component.State.CHECKED));
    +  assertArrayEquals('TestRenderer must return expected class names ' +
    +      'in the expected order',
    +      ['goog-base-hover', 'goog-base-checked'],
    +      testRenderer.getClassNamesForState(goog.ui.Component.State.HOVER |
    +          goog.ui.Component.State.CHECKED));
    +}
    +
    +function testGetClassForState() {
    +  var renderer = new goog.ui.ControlRenderer();
    +  assertUndefined('State-to-class map must not exist until first use',
    +      renderer.classByState_);
    +  assertEquals('Renderer must return expected class name for SELECTED',
    +      'goog-control-selected',
    +      renderer.getClassForState(goog.ui.Component.State.SELECTED));
    +  assertUndefined('Renderer must return undefined for invalid state',
    +      renderer.getClassForState('foo'));
    +}
    +
    +function testGetStateFromClass() {
    +  var renderer = new goog.ui.ControlRenderer();
    +  assertUndefined('Class-to-state map must not exist until first use',
    +      renderer.stateByClass_);
    +  assertEquals('Renderer must return expected state',
    +      goog.ui.Component.State.SELECTED,
    +      renderer.getStateFromClass('goog-control-selected'));
    +  assertEquals('Renderer must return 0x00 for unknown class',
    +      0x00,
    +      renderer.getStateFromClass('goog-control-annoyed'));
    +}
    +
    +function testIe6ClassCombinationsCreateDom() {
    +  control.setRenderer(testRenderer);
    +
    +  control.enableClassName('combined', true);
    +
    +  control.createDom();
    +  var element = control.getElement();
    +
    +  testRenderer.setState(control, goog.ui.Component.State.DISABLED, true);
    +  var expectedClasses = [
    +    'combined',
    +    'goog-base',
    +    'goog-base-disabled',
    +    'goog-button'
    +  ];
    +  if (isIe6()) {
    +    assertSameElements('IE6 and lower should have one combined class',
    +        expectedClasses.concat(['combined_goog-base-disabled_goog-button']),
    +        goog.dom.classlist.get(element));
    +  } else {
    +    assertSameElements('Non IE6 browsers should not have a combined class',
    +        expectedClasses, goog.dom.classlist.get(element));
    +  }
    +
    +  testRenderer.setState(control, goog.ui.Component.State.DISABLED, false);
    +  testRenderer.setState(control, goog.ui.Component.State.HOVER, true);
    +  var expectedClasses = [
    +    'combined',
    +    'goog-base',
    +    'goog-base-hover',
    +    'goog-button'
    +  ];
    +  if (isIe6()) {
    +    assertSameElements('IE6 and lower should have one combined class',
    +        expectedClasses.concat(['combined_goog-base-hover_goog-button']),
    +        goog.dom.classlist.get(element));
    +  } else {
    +    assertSameElements('Non IE6 browsers should not have a combined class',
    +        expectedClasses, goog.dom.classlist.get(element));
    +  }
    +
    +  testRenderer.setRightToLeft(element, true);
    +  testRenderer.enableExtraClassName(control, 'combined2', true);
    +  var expectedClasses = [
    +    'combined',
    +    'combined2',
    +    'goog-base',
    +    'goog-base-hover',
    +    'goog-base-rtl',
    +    'goog-button'
    +  ];
    +  if (isIe6()) {
    +    assertSameElements('IE6 and lower should have two combined class',
    +        expectedClasses.concat([
    +          'combined_goog-base-hover_goog-button',
    +          'combined_combined2_goog-base-hover_goog-base-rtl_goog-button'
    +        ]), goog.dom.classlist.get(element));
    +  } else {
    +    assertSameElements('Non IE6 browsers should not have a combined class',
    +        expectedClasses, goog.dom.classlist.get(element));
    +  }
    +
    +}
    +
    +function testIe6ClassCombinationsDecorate() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="combined goog-base-hover"></div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  var element = testRenderer.decorate(control, foo);
    +
    +  var expectedClasses = [
    +    'combined',
    +    'goog-base',
    +    'goog-base-hover',
    +    'goog-button'
    +  ];
    +  if (isIe6()) {
    +    assertSameElements('IE6 and lower should have one combined class',
    +        expectedClasses.concat(['combined_goog-base-hover_goog-button']),
    +        goog.dom.classlist.get(element));
    +  } else {
    +    assertSameElements('Non IE6 browsers should not have a combined class',
    +        expectedClasses, goog.dom.classlist.get(element));
    +  }
    +
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/cookieeditor.js b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor.js
    new file mode 100644
    index 0000000000..6c7ee7b0d3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor.js
    @@ -0,0 +1,185 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Displays and edits the value of a cookie.
    + * Intended only for debugging.
    + */
    +goog.provide('goog.ui.CookieEditor');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.cookies');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Displays and edits the value of a cookie.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.ui.CookieEditor = function(opt_domHelper) {
    +  goog.ui.CookieEditor.base(this, 'constructor', opt_domHelper);
    +};
    +goog.inherits(goog.ui.CookieEditor, goog.ui.Component);
    +
    +
    +/**
    + * Cookie key.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.cookieKey_;
    +
    +
    +/**
    + * Text area.
    + * @type {HTMLTextAreaElement}
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.textAreaElem_;
    +
    +
    +/**
    + * Clear button.
    + * @type {HTMLButtonElement}
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.clearButtonElem_;
    +
    +
    +/**
    + * Invalid value warning text.
    + * @type {HTMLSpanElement}
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.valueWarningElem_;
    +
    +
    +/**
    + * Update button.
    + * @type {HTMLButtonElement}
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.updateButtonElem_;
    +
    +
    +// TODO(user): add combobox for user to select different cookies
    +/**
    + * Sets the cookie which this component will edit.
    + * @param {string} cookieKey Cookie key.
    + */
    +goog.ui.CookieEditor.prototype.selectCookie = function(cookieKey) {
    +  goog.asserts.assert(goog.net.cookies.isValidName(cookieKey));
    +  this.cookieKey_ = cookieKey;
    +  if (this.textAreaElem_) {
    +    this.textAreaElem_.value = goog.net.cookies.get(cookieKey) || '';
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.CookieEditor.prototype.canDecorate = function() {
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.ui.CookieEditor.prototype.createDom = function() {
    +  // Debug-only, so we don't need i18n.
    +  this.clearButtonElem_ = /** @type {!HTMLButtonElement} */ (goog.dom.createDom(
    +      goog.dom.TagName.BUTTON, /* attributes */ null, 'Clear'));
    +  this.updateButtonElem_ =
    +      /** @type {!HTMLButtonElement} */ (goog.dom.createDom(
    +          goog.dom.TagName.BUTTON, /* attributes */ null, 'Update'));
    +  var value = this.cookieKey_ && goog.net.cookies.get(this.cookieKey_);
    +  this.textAreaElem_ = /** @type {!HTMLTextAreaElement} */ (goog.dom.createDom(
    +      goog.dom.TagName.TEXTAREA, /* attibutes */ null, value || ''));
    +  this.valueWarningElem_ = /** @type {!HTMLSpanElement} */ (goog.dom.createDom(
    +      goog.dom.TagName.SPAN, /* attibutes */ {
    +        'style': 'display:none;color:red'
    +      }, 'Invalid cookie value.'));
    +  this.setElementInternal(goog.dom.createDom(goog.dom.TagName.DIV,
    +      /* attibutes */ null,
    +      this.valueWarningElem_,
    +      goog.dom.createDom(goog.dom.TagName.BR),
    +      this.textAreaElem_,
    +      goog.dom.createDom(goog.dom.TagName.BR),
    +      this.clearButtonElem_,
    +      this.updateButtonElem_));
    +};
    +
    +
    +/** @override */
    +goog.ui.CookieEditor.prototype.enterDocument = function() {
    +  goog.ui.CookieEditor.base(this, 'enterDocument');
    +  this.getHandler().listen(this.clearButtonElem_,
    +      goog.events.EventType.CLICK,
    +      this.handleClear_);
    +  this.getHandler().listen(this.updateButtonElem_,
    +      goog.events.EventType.CLICK,
    +      this.handleUpdate_);
    +};
    +
    +
    +/**
    + * Handles user clicking clear button.
    + * @param {!goog.events.Event} e The click event.
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.handleClear_ = function(e) {
    +  if (this.cookieKey_) {
    +    goog.net.cookies.remove(this.cookieKey_);
    +  }
    +  this.textAreaElem_.value = '';
    +};
    +
    +
    +/**
    + * Handles user clicking update button.
    + * @param {!goog.events.Event} e The click event.
    + * @private
    + */
    +goog.ui.CookieEditor.prototype.handleUpdate_ = function(e) {
    +  if (this.cookieKey_) {
    +    var value = this.textAreaElem_.value;
    +    if (value) {
    +      // Strip line breaks.
    +      value = goog.string.stripNewlines(value);
    +    }
    +    if (goog.net.cookies.isValidValue(value)) {
    +      goog.net.cookies.set(this.cookieKey_, value);
    +      goog.style.setElementShown(this.valueWarningElem_, false);
    +    } else {
    +      goog.style.setElementShown(this.valueWarningElem_, true);
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.CookieEditor.prototype.disposeInternal = function() {
    +  this.clearButtonElem_ = null;
    +  this.cookieKey_ = null;
    +  this.textAreaElem_ = null;
    +  this.updateButtonElem_ = null;
    +  this.valueWarningElem_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.html b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.html
    new file mode 100644
    index 0000000000..0399a99090
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.CookieEditor
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.CookieEditorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="test_container">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.js b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.js
    new file mode 100644
    index 0000000000..f3f9ed17f9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/cookieeditor_test.js
    @@ -0,0 +1,104 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.CookieEditorTest');
    +goog.setTestOnly('goog.ui.CookieEditorTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.cookies');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.CookieEditor');
    +
    +var COOKIE_KEY = 'my_fabulous_cookie';
    +var COOKIE_VALUES;
    +
    +goog.net.cookies.get = function(key) {
    +  return COOKIE_VALUES[key];
    +};
    +
    +goog.net.cookies.set = function(key, value) {
    +  return COOKIE_VALUES[key] = value;
    +};
    +
    +goog.net.cookies.remove = function(key, value) {
    +  delete COOKIE_VALUES[key];
    +};
    +
    +function setUp() {
    +  goog.dom.removeChildren(goog.dom.getElement('test_container'));
    +  COOKIE_VALUES = {};
    +}
    +
    +function newCookieEditor(opt_cookieValue) {
    +  // Set cookie.
    +  if (opt_cookieValue) {
    +    goog.net.cookies.set(COOKIE_KEY, opt_cookieValue);
    +  }
    +
    +  // Render editor.
    +  var editor = new goog.ui.CookieEditor();
    +  editor.selectCookie(COOKIE_KEY);
    +  editor.render(goog.dom.getElement('test_container'));
    +  assertEquals('wrong text area value', opt_cookieValue || '',
    +      editor.textAreaElem_.value || '');
    +
    +  return editor;
    +}
    +
    +function testRender() {
    +  // Render editor.
    +  var editor = newCookieEditor();
    +
    +  // All expected elements created?
    +  var elem = editor.getElement();
    +  assertNotNullNorUndefined('missing element', elem);
    +  assertNotNullNorUndefined('missing clear button', editor.clearButtonElem_);
    +  assertNotNullNorUndefined('missing update button',
    +      editor.updateButtonElem_);
    +  assertNotNullNorUndefined('missing text area', editor.textAreaElem_);
    +}
    +
    +function testEditCookie() {
    +  // Render editor.
    +  var editor = newCookieEditor();
    +
    +  // Invalid value.
    +  var newValue = 'my bad value;';
    +  editor.textAreaElem_.value = newValue;
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.CLICK, editor.updateButtonElem_));
    +  assertTrue('unexpected cookie value', !goog.net.cookies.get(COOKIE_KEY));
    +
    +  // Valid value.
    +  newValue = 'my fabulous value';
    +  editor.textAreaElem_.value = newValue;
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.CLICK, editor.updateButtonElem_));
    +  assertEquals('wrong cookie value', newValue,
    +      goog.net.cookies.get(COOKIE_KEY));
    +}
    +
    +function testClearCookie() {
    +  // Render editor.
    +  var value = 'I will be cleared';
    +  var editor = newCookieEditor(value);
    +
    +  // Clear value.
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.CLICK, editor.clearButtonElem_));
    +  assertTrue('unexpected cookie value', !goog.net.cookies.get(COOKIE_KEY));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/css3buttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/css3buttonrenderer.js
    new file mode 100644
    index 0000000000..e29cb78987
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/css3buttonrenderer.js
    @@ -0,0 +1,154 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An alternative imageless button renderer that uses CSS3 rather
    + * than voodoo to render custom buttons with rounded corners and dimensionality
    + * (via a subtle flat shadow on the bottom half of the button) without the use
    + * of images.
    + *
    + * Based on the Custom Buttons 3.1 visual specification, see
    + * http://go/custombuttons
    + *
    + * Tested and verified to work in Gecko 1.9.2+ and WebKit 528+.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/css3button.html
    + */
    +
    +goog.provide('goog.ui.Css3ButtonRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.Button}s. Css3 buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + *
    + * @constructor
    + * @extends {goog.ui.ButtonRenderer}
    + * @final
    + */
    +goog.ui.Css3ButtonRenderer = function() {
    +  goog.ui.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.Css3ButtonRenderer, goog.ui.ButtonRenderer);
    +
    +
    +/**
    + * The singleton instance of this renderer class.
    + * @type {goog.ui.Css3ButtonRenderer?}
    + * @private
    + */
    +goog.ui.Css3ButtonRenderer.instance_ = null;
    +goog.addSingletonGetter(goog.ui.Css3ButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.Css3ButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button');
    +
    +
    +/** @override */
    +goog.ui.Css3ButtonRenderer.prototype.getContentElement = function(element) {
    +  return /** @type {Element} */ (element);
    +};
    +
    +
    +/**
    + * Returns the button's contents wrapped in the following DOM structure:
    + *    <div class="goog-inline-block goog-css3-button">
    + *      Contents...
    + *    </div>
    + * Overrides {@link goog.ui.ButtonRenderer#createDom}.
    + * @param {goog.ui.Control} control goog.ui.Button to render.
    + * @return {!Element} Root element for the button.
    + * @override
    + */
    +goog.ui.Css3ButtonRenderer.prototype.createDom = function(control) {
    +  var button = /** @type {goog.ui.Button} */ (control);
    +  var classNames = this.getClassNames(button);
    +  var attr = {
    +    'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' '),
    +    'title': button.getTooltip() || ''
    +  };
    +  return button.getDomHelper().createDom('div', attr, button.getContent());
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element.  Overrides
    + * {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
    + * element is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.Css3ButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == goog.dom.TagName.DIV;
    +};
    +
    +
    +/** @override */
    +goog.ui.Css3ButtonRenderer.prototype.decorate = function(button, element) {
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.addAll(element,
    +      [goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass()]);
    +  return goog.ui.Css3ButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.Css3ButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.Css3ButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Css3ButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.Css3ButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Button(null,
    +          goog.ui.Css3ButtonRenderer.getInstance());
    +    });
    +
    +
    +// Register a decorator factory function for toggle buttons using the
    +// goog.ui.Css3ButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-css3-toggle-button'),
    +    function() {
    +      var button = new goog.ui.Button(null,
    +          goog.ui.Css3ButtonRenderer.getInstance());
    +      button.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +      return button;
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/css3menubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/css3menubuttonrenderer.js
    new file mode 100644
    index 0000000000..7e6f24b9e2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/css3menubuttonrenderer.js
    @@ -0,0 +1,146 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An alternative imageless button renderer that uses CSS3 rather
    + * than voodoo to render custom buttons with rounded corners and dimensionality
    + * (via a subtle flat shadow on the bottom half of the button) without the use
    + * of images.
    + *
    + * Based on the Custom Buttons 3.1 visual specification, see
    + * http://go/custombuttons
    + *
    + * Tested and verified to work in Gecko 1.9.2+ and WebKit 528+.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/css3menubutton.html
    + */
    +
    +goog.provide('goog.ui.Css3MenuButtonRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.MenuButton}s. Css3 buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + *
    + * @constructor
    + * @extends {goog.ui.MenuButtonRenderer}
    + * @final
    + */
    +goog.ui.Css3MenuButtonRenderer = function() {
    +  goog.ui.MenuButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.Css3MenuButtonRenderer, goog.ui.MenuButtonRenderer);
    +
    +
    +/**
    + * The singleton instance of this renderer class.
    + * @type {goog.ui.Css3MenuButtonRenderer?}
    + * @private
    + */
    +goog.ui.Css3MenuButtonRenderer.instance_ = null;
    +goog.addSingletonGetter(goog.ui.Css3MenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.Css3MenuButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button');
    +
    +
    +/** @override */
    +goog.ui.Css3MenuButtonRenderer.prototype.getContentElement = function(element) {
    +  if (element) {
    +    var captionElem = goog.dom.getElementsByTagNameAndClass(
    +        '*', goog.getCssName(this.getCssClass(), 'caption'), element)[0];
    +    return captionElem;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element.  Overrides
    + * {@link goog.ui.MenuButtonRenderer#canDecorate} by returning true if the
    + * element is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.Css3MenuButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == goog.dom.TagName.DIV;
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content
    + * wrapped in a pseudo-rounded-corner box.  Creates the following DOM structure:
    + *  <div class="goog-inline-block goog-css3-button goog-css3-menu-button">
    + *    <div class="goog-css3-button-caption">Contents...</div>
    + *    <div class="goog-css3-button-dropdown"></div>
    + *  </div>
    + *
    + * Used by both {@link #createDom} and {@link #decorate}.  To be overridden
    + * by subclasses.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.Css3MenuButtonRenderer.prototype.createButton = function(content, dom) {
    +  var baseClass = this.getCssClass();
    +  var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' ';
    +  return dom.createDom('div', inlineBlock,
    +      dom.createDom('div', [goog.getCssName(baseClass, 'caption'),
    +                            goog.getCssName('goog-inline-block')],
    +                    content),
    +      dom.createDom('div', [goog.getCssName(baseClass, 'dropdown'),
    +                            goog.getCssName('goog-inline-block')]));
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.Css3MenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.Css3MenuButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Css3MenuButtonRenderer.
    +// Since we're using goog-css3-button as the base class in order to get the
    +// same styling as goog.ui.Css3ButtonRenderer, we need to be explicit about
    +// giving goog-css3-menu-button here.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-css3-menu-button'),
    +    function() {
    +      return new goog.ui.MenuButton(null, null,
    +          goog.ui.Css3MenuButtonRenderer.getInstance());
    +    });
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/cssnames.js b/mapbox-gl/closure-library/closure/goog/ui/cssnames.js
    new file mode 100644
    index 0000000000..4ad5419ba5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/cssnames.js
    @@ -0,0 +1,29 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Common CSS class name constants.
    + *
    + * @author mkretzschmar@google.com (Martin Kretzschmar)
    + */
    +
    +goog.provide('goog.ui.INLINE_BLOCK_CLASSNAME');
    +
    +
    +/**
    + * CSS class name for applying the "display: inline-block" property in a
    + * cross-browser way.
    + * @type {string}
    + */
    +goog.ui.INLINE_BLOCK_CLASSNAME = goog.getCssName('goog-inline-block');
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/custombutton.js b/mapbox-gl/closure-library/closure/goog/ui/custombutton.js
    new file mode 100644
    index 0000000000..15f728468d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/custombutton.js
    @@ -0,0 +1,58 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A button rendered via {@link goog.ui.CustomButtonRenderer}.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.CustomButton');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A custom button control.  Identical to {@link goog.ui.Button}, except it
    + * defaults its renderer to {@link goog.ui.CustomButtonRenderer}.  One could
    + * just as easily pass {@code goog.ui.CustomButtonRenderer.getInstance()} to
    + * the {@link goog.ui.Button} constructor and get the same result.  Provided
    + * for convenience.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *    structure to display as the button's caption.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
    + *    render or decorate the button; defaults to
    + *    {@link goog.ui.CustomButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *    document interaction.
    + * @constructor
    + * @extends {goog.ui.Button}
    + */
    +goog.ui.CustomButton = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Button.call(this, content, opt_renderer ||
    +      goog.ui.CustomButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.CustomButton, goog.ui.Button);
    +
    +
    +// Register a decorator factory function for goog.ui.CustomButtons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.CustomButtonRenderer.CSS_CLASS,
    +    function() {
    +      // CustomButton defaults to using CustomButtonRenderer.
    +      return new goog.ui.CustomButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/custombuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/custombuttonrenderer.js
    new file mode 100644
    index 0000000000..40150acd7e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/custombuttonrenderer.js
    @@ -0,0 +1,270 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A custom button renderer that uses CSS voodoo to render a
    + * button-like object with fake rounded corners.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.CustomButtonRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.string');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.Button}s.  Custom buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + *
    + * @constructor
    + * @extends {goog.ui.ButtonRenderer}
    + */
    +goog.ui.CustomButtonRenderer = function() {
    +  goog.ui.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.CustomButtonRenderer, goog.ui.ButtonRenderer);
    +goog.addSingletonGetter(goog.ui.CustomButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.CustomButtonRenderer.CSS_CLASS = goog.getCssName('goog-custom-button');
    +
    +
    +/**
    + * Returns the button's contents wrapped in the following DOM structure:
    + *    <div class="goog-inline-block goog-custom-button">
    + *      <div class="goog-inline-block goog-custom-button-outer-box">
    + *        <div class="goog-inline-block goog-custom-button-inner-box">
    + *          Contents...
    + *        </div>
    + *      </div>
    + *    </div>
    + * Overrides {@link goog.ui.ButtonRenderer#createDom}.
    + * @param {goog.ui.Control} control goog.ui.Button to render.
    + * @return {!Element} Root element for the button.
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.createDom = function(control) {
    +  var button = /** @type {goog.ui.Button} */ (control);
    +  var classNames = this.getClassNames(button);
    +  var attributes = {
    +    'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
    +  };
    +  var buttonElement = button.getDomHelper().createDom('div', attributes,
    +      this.createButton(button.getContent(), button.getDomHelper()));
    +  this.setTooltip(
    +      buttonElement, /** @type {!string}*/ (button.getTooltip()));
    +
    +  return buttonElement;
    +};
    +
    +
    +/**
    + * Returns the ARIA role to be applied to custom buttons.
    + * @return {goog.a11y.aria.Role|undefined} ARIA role.
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.BUTTON;
    +};
    +
    +
    +/**
    + * Takes the button's root element and returns the parent element of the
    + * button's contents.  Overrides the superclass implementation by taking
    + * the nested DIV structure of custom buttons into account.
    + * @param {Element} element Root element of the button whose content
    + *     element is to be returned.
    + * @return {Element} The button's content element (if any).
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.getContentElement = function(element) {
    +  return element && element.firstChild &&
    +      /** @type {Element} */ (element.firstChild.firstChild);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content
    + * wrapped in a pseudo-rounded-corner box.  Creates the following DOM structure:
    + *  <div class="goog-inline-block goog-custom-button-outer-box">
    + *    <div class="goog-inline-block goog-custom-button-inner-box">
    + *      Contents...
    + *    </div>
    + *  </div>
    + * Used by both {@link #createDom} and {@link #decorate}.  To be overridden
    + * by subclasses.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Pseudo-rounded-corner box containing the content.
    + */
    +goog.ui.CustomButtonRenderer.prototype.createButton = function(content, dom) {
    +  return dom.createDom('div',
    +      goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +      goog.getCssName(this.getCssClass(), 'outer-box'),
    +      dom.createDom('div',
    +          goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +          goog.getCssName(this.getCssClass(), 'inner-box'), content));
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element.  Overrides
    + * {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
    + * element is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == 'DIV';
    +};
    +
    +
    +/**
    + * Check if the button's element has a box structure.
    + * @param {goog.ui.Button} button Button instance whose structure is being
    + *     checked.
    + * @param {Element} element Element of the button.
    + * @return {boolean} Whether the element has a box structure.
    + * @protected
    + */
    +goog.ui.CustomButtonRenderer.prototype.hasBoxStructure = function(
    +    button, element) {
    +  var outer = button.getDomHelper().getFirstElementChild(element);
    +  var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box');
    +  if (outer && goog.dom.classlist.contains(outer, outerClassName)) {
    +
    +    var inner = button.getDomHelper().getFirstElementChild(outer);
    +    var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box');
    +    if (inner && goog.dom.classlist.contains(inner, innerClassName)) {
    +      // We have a proper box structure.
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Takes an existing element and decorates it with the custom button control.
    + * Initializes the control's ID, content, tooltip, value, and state based
    + * on the ID of the element, its child nodes, and its CSS classes, respectively.
    + * Returns the element.  Overrides {@link goog.ui.ButtonRenderer#decorate}.
    + * @param {goog.ui.Control} control Button instance to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.decorate = function(control, element) {
    +  goog.asserts.assert(element);
    +
    +  var button = /** @type {goog.ui.Button} */ (control);
    +  // Trim text nodes in the element's child node list; otherwise madness
    +  // ensues (i.e. on Gecko, buttons will flicker and shift when moused over).
    +  goog.ui.CustomButtonRenderer.trimTextNodes_(element, true);
    +  goog.ui.CustomButtonRenderer.trimTextNodes_(element, false);
    +
    +  // Create the buttom dom if it has not been created.
    +  if (!this.hasBoxStructure(button, element)) {
    +    element.appendChild(
    +        this.createButton(element.childNodes, button.getDomHelper()));
    +  }
    +
    +  goog.dom.classlist.addAll(element,
    +      [goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass()]);
    +  return goog.ui.CustomButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.CustomButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.CustomButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Takes an element and removes leading or trailing whitespace from the start
    + * or the end of its list of child nodes.  The Boolean argument determines
    + * whether to trim from the start or the end of the node list.  Empty text
    + * nodes are removed, and the first non-empty text node is trimmed from the
    + * left or the right as appropriate.  For example,
    + *    <div class="goog-inline-block">
    + *      #text ""
    + *      #text "\n    Hello "
    + *      <span>...</span>
    + *      #text " World!    \n"
    + *      #text ""
    + *    </div>
    + * becomes
    + *    <div class="goog-inline-block">
    + *      #text "Hello "
    + *      <span>...</span>
    + *      #text " World!"
    + *    </div>
    + * This is essential for Gecko, where leading/trailing whitespace messes with
    + * the layout of elements with -moz-inline-box (used in goog-inline-block), and
    + * optional but harmless for non-Gecko.
    + *
    + * @param {Element} element Element whose child node list is to be trimmed.
    + * @param {boolean} fromStart Whether to trim from the start or from the end.
    + * @private
    + */
    +goog.ui.CustomButtonRenderer.trimTextNodes_ = function(element, fromStart) {
    +  if (element) {
    +    var node = fromStart ? element.firstChild : element.lastChild, next;
    +    // Tag soup HTML may result in a DOM where siblings have different parents.
    +    while (node && node.parentNode == element) {
    +      // Get the next/previous sibling here, since the node may be removed.
    +      next = fromStart ? node.nextSibling : node.previousSibling;
    +      if (node.nodeType == goog.dom.NodeType.TEXT) {
    +        // Found a text node.
    +        var text = node.nodeValue;
    +        if (goog.string.trim(text) == '') {
    +          // Found an empty text node; remove it.
    +          element.removeChild(node);
    +        } else {
    +          // Found a non-empty text node; trim from the start/end, then exit.
    +          node.nodeValue = fromStart ?
    +              goog.string.trimLeft(text) : goog.string.trimRight(text);
    +          break;
    +        }
    +      } else {
    +        // Found a non-text node; done.
    +        break;
    +      }
    +      node = next;
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette.js b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette.js
    new file mode 100644
    index 0000000000..f66a0de05b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette.js
    @@ -0,0 +1,140 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A color palette with a button for adding additional colors
    + * manually.
    + *
    + */
    +
    +goog.provide('goog.ui.CustomColorPalette');
    +
    +goog.require('goog.color');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.ColorPalette');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * A custom color palette is a grid of color swatches and a button that allows
    + * the user to add additional colors to the palette
    + *
    + * @param {Array<string>} initColors Array of initial colors to populate the
    + *     palette with.
    + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
    + *     decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.ColorPalette}
    + * @final
    + */
    +goog.ui.CustomColorPalette = function(initColors, opt_renderer, opt_domHelper) {
    +  goog.ui.ColorPalette.call(this, initColors, opt_renderer, opt_domHelper);
    +  this.setSupportedState(goog.ui.Component.State.OPENED, true);
    +};
    +goog.inherits(goog.ui.CustomColorPalette, goog.ui.ColorPalette);
    +
    +
    +/**
    + * Returns an array of DOM nodes for each color, and an additional cell with a
    + * '+'.
    + * @return {!Array<Node>} Array of div elements.
    + * @override
    + */
    +goog.ui.CustomColorPalette.prototype.createColorNodes = function() {
    +  /** @desc Hover caption for the button that allows the user to add a color. */
    +  var MSG_CLOSURE_CUSTOM_COLOR_BUTTON = goog.getMsg('Add a color');
    +
    +  var nl = goog.ui.CustomColorPalette.base(this, 'createColorNodes');
    +  nl.push(goog.dom.createDom('div', {
    +    'class': goog.getCssName('goog-palette-customcolor'),
    +    'title': MSG_CLOSURE_CUSTOM_COLOR_BUTTON
    +  }, '+'));
    +  return nl;
    +};
    +
    +
    +/**
    + * @override
    + * @param {goog.events.Event} e Mouse or key event that triggered the action.
    + * @return {boolean} True if the action was allowed to proceed, false otherwise.
    + */
    +goog.ui.CustomColorPalette.prototype.performActionInternal = function(e) {
    +  var item = /** @type {Element} */ (this.getHighlightedItem());
    +  if (item) {
    +    if (goog.dom.classlist.contains(
    +        item, goog.getCssName('goog-palette-customcolor'))) {
    +      // User activated the special "add custom color" swatch.
    +      this.promptForCustomColor();
    +    } else {
    +      // User activated a normal color swatch.
    +      this.setSelectedItem(item);
    +      return this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Prompts the user to enter a custom color.  Currently uses a window.prompt
    + * but could be updated to use a dialog box with a WheelColorPalette.
    + */
    +goog.ui.CustomColorPalette.prototype.promptForCustomColor = function() {
    +  /** @desc Default custom color dialog. */
    +  var MSG_CLOSURE_CUSTOM_COLOR_PROMPT = goog.getMsg(
    +      'Input custom color, i.e. pink, #F00, #D015FF or rgb(100, 50, 25)');
    +
    +  // A CustomColorPalette is considered "open" while the color selection prompt
    +  // is open.  Enabling state transition events for the OPENED state and
    +  // listening for OPEN events allows clients to save the selection before
    +  // it is destroyed (see e.g. bug 1064701).
    +  var response = null;
    +  this.setOpen(true);
    +  if (this.isOpen()) {
    +    // The OPEN event wasn't canceled; prompt for custom color.
    +    response = window.prompt(MSG_CLOSURE_CUSTOM_COLOR_PROMPT, '#FFFFFF');
    +    this.setOpen(false);
    +  }
    +
    +  if (!response) {
    +    // The user hit cancel
    +    return;
    +  }
    +
    +  var color;
    +  /** @preserveTry */
    +  try {
    +    color = goog.color.parse(response).hex;
    +  } catch (er) {
    +    /** @desc Alert message sent when the input string is not a valid color. */
    +    var MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT = goog.getMsg(
    +        'ERROR: "{$color}" is not a valid color.', {'color': response});
    +    alert(MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT);
    +    return;
    +  }
    +
    +  // TODO(user): This is relatively inefficient.  Consider adding
    +  // functionality to palette to add individual items after render time.
    +  var colors = this.getColors();
    +  colors.push(color);
    +  this.setColors(colors);
    +
    +  // Set the selected color to the new color and notify listeners of the action.
    +  this.setSelectedColor(color);
    +  this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.html b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.html
    new file mode 100644
    index 0000000000..1931af0dd6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   CustomColorPalette Unit Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script src="../deps.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.CustomColorPaletteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.js b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.js
    new file mode 100644
    index 0000000000..3881c42cc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/customcolorpalette_test.js
    @@ -0,0 +1,55 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.CustomColorPaletteTest');
    +goog.setTestOnly('goog.ui.CustomColorPaletteTest');
    +
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.CustomColorPalette');
    +
    +var samplePalette;
    +
    +function setUp() {
    +  samplePalette = new goog.ui.CustomColorPalette();
    +}
    +
    +function tearDown() {
    +  samplePalette.dispose();
    +  document.getElementById('sandbox').innerHTML = '';
    +}
    +
    +function testRender() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  assertTrue('Palette must have been rendered',
    +             samplePalette.isInDocument());
    +
    +  var elem = samplePalette.getElement();
    +  assertNotNull('The palette element should not be null', elem);
    +  assertEquals('The palette element should have the right tag name',
    +               goog.dom.TagName.DIV, elem.tagName);
    +
    +  assertTrue('The custom color palette should have the right class name',
    +             goog.dom.classlist.contains(elem, 'goog-palette'));
    +}
    +
    +function testSetColors() {
    +  var colorSet = ['#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af',
    +    '#6fa8dc', '#8e7cc3'];
    +  samplePalette.setColors(colorSet);
    +  assertSameElements('The palette should have the correct set of colors',
    +                     colorSet, samplePalette.getColors());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/datepicker.js b/mapbox-gl/closure-library/closure/goog/ui/datepicker.js
    new file mode 100644
    index 0000000000..a460523263
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/datepicker.js
    @@ -0,0 +1,1549 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Date picker implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/datepicker.html
    + */
    +
    +goog.provide('goog.ui.DatePicker');
    +goog.provide('goog.ui.DatePicker.Events');
    +goog.provide('goog.ui.DatePickerEvent');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.date.Date');
    +goog.require('goog.date.DateRange');
    +goog.require('goog.date.Interval');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.i18n.DateTimeFormat');
    +goog.require('goog.i18n.DateTimePatterns');
    +goog.require('goog.i18n.DateTimeSymbols');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.DefaultDatePickerRenderer');
    +goog.require('goog.ui.IdGenerator');
    +
    +
    +
    +/**
    + * DatePicker widget. Allows a single date to be selected from a calendar like
    + * view.
    + *
    + * @param {goog.date.Date|Date=} opt_date Date to initialize the date picker
    + *     with, defaults to the current date.
    + * @param {Object=} opt_dateTimeSymbols Date and time symbols to use.
    + *     Defaults to goog.i18n.DateTimeSymbols if not set.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.DatePickerRenderer=} opt_renderer Optional Date picker
    + *     renderer.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.DatePicker = function(opt_date, opt_dateTimeSymbols, opt_domHelper,
    +    opt_renderer) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Date and time symbols to use.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.symbols_ = opt_dateTimeSymbols || goog.i18n.DateTimeSymbols;
    +
    +  this.wdayNames_ = this.symbols_.STANDALONESHORTWEEKDAYS;
    +
    +  // The DateTimeFormat object uses the global goog.i18n.DateTimeSymbols
    +  // for initialization. So we save the original value, the global object,
    +  // create the formatters, then restore the original value.
    +  var tempSymbols = goog.i18n.DateTimeSymbols;  // save
    +  goog.i18n.DateTimeSymbols = this.symbols_;
    +
    +  // Formatters for the various areas of the picker
    +  this.i18nDateFormatterDay_ = new goog.i18n.DateTimeFormat('d');
    +  this.i18nDateFormatterDay2_ = new goog.i18n.DateTimeFormat('dd');
    +  this.i18nDateFormatterWeek_ = new goog.i18n.DateTimeFormat('w');
    +
    +  // Previous implementation did not use goog.i18n.DateTimePatterns,
    +  // so it is likely most developers did not set it.
    +  // This is why the fallback to a hard-coded string (just in case).
    +  var patYear = goog.i18n.DateTimePatterns.YEAR_FULL || 'y';
    +  this.i18nDateFormatterYear_ = new goog.i18n.DateTimeFormat(patYear);
    +  var patMMMMy = goog.i18n.DateTimePatterns.YEAR_MONTH_FULL || 'MMMM y';
    +  this.i18nDateFormatterMonthYear_ = new goog.i18n.DateTimeFormat(patMMMMy);
    +
    +  goog.i18n.DateTimeSymbols = tempSymbols;  // restore
    +
    +  /**
    +   * @type {!goog.ui.DatePickerRenderer}
    +   * @private
    +   */
    +  this.renderer_ = opt_renderer || new goog.ui.DefaultDatePickerRenderer(
    +      this.getBaseCssClass(), this.getDomHelper());
    +
    +  /**
    +   * Selected date.
    +   * @type {goog.date.Date}
    +   * @private
    +   */
    +  this.date_ = new goog.date.Date(opt_date);
    +  this.date_.setFirstWeekCutOffDay(this.symbols_.FIRSTWEEKCUTOFFDAY);
    +  this.date_.setFirstDayOfWeek(this.symbols_.FIRSTDAYOFWEEK);
    +
    +  /**
    +   * Active month.
    +   * @type {goog.date.Date}
    +   * @private
    +   */
    +  this.activeMonth_ = this.date_.clone();
    +  this.activeMonth_.setDate(1);
    +
    +  /**
    +   * Class names to apply to the weekday columns.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.wdayStyles_ = ['', '', '', '', '', '', ''];
    +  this.wdayStyles_[this.symbols_.WEEKENDRANGE[0]] =
    +      goog.getCssName(this.getBaseCssClass(), 'wkend-start');
    +  this.wdayStyles_[this.symbols_.WEEKENDRANGE[1]] =
    +      goog.getCssName(this.getBaseCssClass(), 'wkend-end');
    +
    +  /**
    +   * Object that is being used to cache key handlers.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.keyHandlers_ = {};
    +
    +  /**
    +   * Collection of dates that make up the date picker.
    +   * @type {!Array<!Array<!goog.date.Date>>}
    +   * @private
    +   */
    +  this.grid_ = [];
    +
    +  /** @private {Array<!Array<Element>>} */
    +  this.elTable_;
    +
    +  /**
    +   * TODO(tbreisacher): Remove external references to this field,
    +   * and make it private.
    +   * @type {Element}
    +   */
    +  this.tableBody_;
    +
    +  /** @private {Element} */
    +  this.tableFoot_;
    +
    +  /** @private {Element} */
    +  this.elYear_;
    +
    +  /** @private {Element} */
    +  this.elMonth_;
    +
    +  /** @private {Element} */
    +  this.elToday_;
    +
    +  /** @private {Element} */
    +  this.elNone_;
    +
    +  /** @private {Element} */
    +  this.menu_;
    +
    +  /** @private {Element} */
    +  this.menuSelected_;
    +
    +  /** @private {function(Element)} */
    +  this.menuCallback_;
    +};
    +goog.inherits(goog.ui.DatePicker, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.DatePicker);
    +
    +
    +/**
    + * Flag indicating if the number of weeks shown should be fixed.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showFixedNumWeeks_ = true;
    +
    +
    +/**
    + * Flag indicating if days from other months should be shown.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showOtherMonths_ = true;
    +
    +
    +/**
    + * Range of dates which are selectable by the user.
    + * @type {goog.date.DateRange}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.userSelectableDateRange_ =
    +    goog.date.DateRange.allTime();
    +
    +
    +/**
    + * Flag indicating if extra week(s) always should be added at the end. If not
    + * set the extra week is added at the beginning if the number of days shown
    + * from the previous month is less then the number from the next month.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.extraWeekAtEnd_ = true;
    +
    +
    +/**
    + * Flag indicating if week numbers should be shown.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showWeekNum_ = true;
    +
    +
    +/**
    + * Flag indicating if weekday names should be shown.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showWeekdays_ = true;
    +
    +
    +/**
    + * Flag indicating if none is a valid selection. Also controls if the none
    + * button should be shown or not.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.allowNone_ = true;
    +
    +
    +/**
    + * Flag indicating if the today button should be shown.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showToday_ = true;
    +
    +
    +/**
    + * Flag indicating if the picker should use a simple navigation menu that only
    + * contains controls for navigating to the next and previous month. The default
    + * navigation menu contains controls for navigating to the next/previous month,
    + * next/previous year, and menus for jumping to specific months and years.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.simpleNavigation_ = false;
    +
    +
    +/**
    + * Custom decorator function. Takes a goog.date.Date object, returns a String
    + * representing a CSS class or null if no special styling applies
    + * @type {Function}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.decoratorFunction_ = null;
    +
    +
    +/**
    + * Flag indicating if the dates should be printed as a two charater date.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.longDateFormat_ = false;
    +
    +
    +/**
    + * Element for navigation row on a datepicker.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.elNavRow_ = null;
    +
    +
    +/**
    + * Element for the month/year in the navigation row.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.elMonthYear_ = null;
    +
    +
    +/**
    + * Element for footer row on a datepicker.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.elFootRow_ = null;
    +
    +
    +/**
    + * Generator for unique table cell IDs.
    + * @type {goog.ui.IdGenerator}
    + * @private
    + */
    +goog.ui.DatePicker.prototype.cellIdGenerator_ =
    +    goog.ui.IdGenerator.getInstance();
    +
    +
    +/**
    + * Name of base CSS class of datepicker.
    + * @type {string}
    + * @private
    + */
    +goog.ui.DatePicker.BASE_CSS_CLASS_ = goog.getCssName('goog-date-picker');
    +
    +
    +/**
    + * The numbers of years to show before and after the current one in the
    + * year pull-down menu. A total of YEAR_MENU_RANGE * 2 + 1 will be shown.
    + * Example: for range = 2 and year 2013 => [2011, 2012, 2013, 2014, 2015]
    + * @const {number}
    + * @private
    + */
    +goog.ui.DatePicker.YEAR_MENU_RANGE_ = 5;
    +
    +
    +/**
    + * Constants for event names
    + *
    + * @const
    + */
    +goog.ui.DatePicker.Events = {
    +  CHANGE: 'change',
    +  CHANGE_ACTIVE_MONTH: 'changeActiveMonth',
    +  SELECT: 'select'
    +};
    +
    +
    +/**
    + * @deprecated Use isInDocument.
    + */
    +goog.ui.DatePicker.prototype.isCreated =
    +    goog.ui.DatePicker.prototype.isInDocument;
    +
    +
    +/**
    + * @return {number} The first day of week, 0 = Monday, 6 = Sunday.
    + */
    +goog.ui.DatePicker.prototype.getFirstWeekday = function() {
    +  return this.activeMonth_.getFirstDayOfWeek();
    +};
    +
    +
    +/**
    + * Returns the class name associated with specified weekday.
    + * @param {number} wday The week day number to get the class name for.
    + * @return {string} The class name associated with specified weekday.
    + */
    +goog.ui.DatePicker.prototype.getWeekdayClass = function(wday) {
    +  return this.wdayStyles_[wday];
    +};
    +
    +
    +/**
    + * @return {boolean} Whether a fixed number of weeks should be showed. If not
    + *     only weeks for the current month will be shown.
    + */
    +goog.ui.DatePicker.prototype.getShowFixedNumWeeks = function() {
    +  return this.showFixedNumWeeks_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether a days from the previous and/or next month should
    + *     be shown.
    + */
    +goog.ui.DatePicker.prototype.getShowOtherMonths = function() {
    +  return this.showOtherMonths_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether a the extra week(s) added always should be at the
    + *     end. Only applicable if a fixed number of weeks are shown.
    + */
    +goog.ui.DatePicker.prototype.getExtraWeekAtEnd = function() {
    +  return this.extraWeekAtEnd_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether week numbers should be shown.
    + */
    +goog.ui.DatePicker.prototype.getShowWeekNum = function() {
    +  return this.showWeekNum_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether weekday names should be shown.
    + */
    +goog.ui.DatePicker.prototype.getShowWeekdayNames = function() {
    +  return this.showWeekdays_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether none is a valid selection.
    + */
    +goog.ui.DatePicker.prototype.getAllowNone = function() {
    +  return this.allowNone_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the today button should be shown.
    + */
    +goog.ui.DatePicker.prototype.getShowToday = function() {
    +  return this.showToday_;
    +};
    +
    +
    +/**
    + * Returns base CSS class. This getter is used to get base CSS class part.
    + * All CSS class names in component are created as:
    + *   goog.getCssName(this.getBaseCssClass(), 'CLASS_NAME')
    + * @return {string} Base CSS class.
    + */
    +goog.ui.DatePicker.prototype.getBaseCssClass = function() {
    +  return goog.ui.DatePicker.BASE_CSS_CLASS_;
    +};
    +
    +
    +/**
    + * Sets the first day of week
    + *
    + * @param {number} wday Week day, 0 = Monday, 6 = Sunday.
    + */
    +goog.ui.DatePicker.prototype.setFirstWeekday = function(wday) {
    +  this.activeMonth_.setFirstDayOfWeek(wday);
    +  this.updateCalendarGrid_();
    +  this.redrawWeekdays_();
    +};
    +
    +
    +/**
    + * Sets class name associated with specified weekday.
    + *
    + * @param {number} wday Week day, 0 = Monday, 6 = Sunday.
    + * @param {string} className Class name.
    + */
    +goog.ui.DatePicker.prototype.setWeekdayClass = function(wday, className) {
    +  this.wdayStyles_[wday] = className;
    +  this.redrawCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether a fixed number of weeks should be showed. If not only weeks
    + * for the current month will be showed.
    + *
    + * @param {boolean} b Whether a fixed number of weeks should be showed.
    + */
    +goog.ui.DatePicker.prototype.setShowFixedNumWeeks = function(b) {
    +  this.showFixedNumWeeks_ = b;
    +  this.updateCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether a days from the previous and/or next month should be shown.
    + *
    + * @param {boolean} b Whether a days from the previous and/or next month should
    + *     be shown.
    + */
    +goog.ui.DatePicker.prototype.setShowOtherMonths = function(b) {
    +  this.showOtherMonths_ = b;
    +  this.redrawCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets the range of dates which may be selected by the user.
    + *
    + * @param {goog.date.DateRange} dateRange The range of selectable dates.
    + */
    +goog.ui.DatePicker.prototype.setUserSelectableDateRange =
    +    function(dateRange) {
    +  this.userSelectableDateRange_ = dateRange;
    +};
    +
    +
    +/**
    + * Determine if a date may be selected by the user.
    + *
    + * @param {goog.date.Date} date The date to be tested.
    + * @return {boolean} Whether the user may select this date.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.isUserSelectableDate_ = function(date) {
    +  return this.userSelectableDateRange_.contains(date);
    +};
    +
    +
    +/**
    + * Sets whether the picker should use a simple navigation menu that only
    + * contains controls for navigating to the next and previous month. The default
    + * navigation menu contains controls for navigating to the next/previous month,
    + * next/previous year, and menus for jumping to specific months and years.
    + *
    + * @param {boolean} b Whether to use a simple navigation menu.
    + */
    +goog.ui.DatePicker.prototype.setUseSimpleNavigationMenu = function(b) {
    +  this.simpleNavigation_ = b;
    +  this.updateNavigationRow_();
    +  this.updateCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether a the extra week(s) added always should be at the end. Only
    + * applicable if a fixed number of weeks are shown.
    + *
    + * @param {boolean} b Whether a the extra week(s) added always should be at the
    + *     end.
    + */
    +goog.ui.DatePicker.prototype.setExtraWeekAtEnd = function(b) {
    +  this.extraWeekAtEnd_ = b;
    +  this.updateCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether week numbers should be shown.
    + *
    + * @param {boolean} b Whether week numbers should be shown.
    + */
    +goog.ui.DatePicker.prototype.setShowWeekNum = function(b) {
    +  this.showWeekNum_ = b;
    +  // The navigation and footer rows may rely on the number of visible columns,
    +  // so we update them when adding/removing the weeknum column.
    +  this.updateNavigationRow_();
    +  this.updateFooterRow_();
    +  this.updateCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether weekday names should be shown.
    + *
    + * @param {boolean} b Whether weekday names should be shown.
    + */
    +goog.ui.DatePicker.prototype.setShowWeekdayNames = function(b) {
    +  this.showWeekdays_ = b;
    +  this.redrawWeekdays_();
    +  this.redrawCalendarGrid_();
    +};
    +
    +
    +/**
    + * Sets whether the picker uses narrow weekday names ('M', 'T', 'W', ...).
    + *
    + * The default behavior is to use short names ('Mon', 'Tue', 'Wed', ...).
    + *
    + * @param {boolean} b Whether to use narrow weekday names.
    + */
    +goog.ui.DatePicker.prototype.setUseNarrowWeekdayNames = function(b) {
    +  this.wdayNames_ = b ? this.symbols_.STANDALONENARROWWEEKDAYS :
    +      this.symbols_.STANDALONESHORTWEEKDAYS;
    +  this.redrawWeekdays_();
    +};
    +
    +
    +/**
    + * Sets whether none is a valid selection.
    + *
    + * @param {boolean} b Whether none is a valid selection.
    + */
    +goog.ui.DatePicker.prototype.setAllowNone = function(b) {
    +  this.allowNone_ = b;
    +  if (this.elNone_) {
    +    this.updateTodayAndNone_();
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the today button should be shown.
    + *
    + * @param {boolean} b Whether the today button should be shown.
    + */
    +goog.ui.DatePicker.prototype.setShowToday = function(b) {
    +  this.showToday_ = b;
    +  if (this.elToday_) {
    +    this.updateTodayAndNone_();
    +  }
    +};
    +
    +
    +/**
    + * Updates the display style of the None and Today buttons as well as hides the
    + * table foot if both are hidden.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.updateTodayAndNone_ = function() {
    +  goog.style.setElementShown(this.elToday_, this.showToday_);
    +  goog.style.setElementShown(this.elNone_, this.allowNone_);
    +  goog.style.setElementShown(this.tableFoot_,
    +                             this.showToday_ || this.allowNone_);
    +};
    +
    +
    +/**
    + * Sets the decorator function. The function should have the interface of
    + *   {string} f({goog.date.Date});
    + * and return a String representing a CSS class to decorate the cell
    + * corresponding to the date specified.
    + *
    + * @param {Function} f The decorator function.
    + */
    +goog.ui.DatePicker.prototype.setDecorator = function(f) {
    +  this.decoratorFunction_ = f;
    +};
    +
    +
    +/**
    + * Sets whether the date will be printed in long format. In long format, dates
    + * such as '1' will be printed as '01'.
    + *
    + * @param {boolean} b Whethere dates should be printed in long format.
    + */
    +goog.ui.DatePicker.prototype.setLongDateFormat = function(b) {
    +  this.longDateFormat_ = b;
    +  this.redrawCalendarGrid_();
    +};
    +
    +
    +/**
    + * Changes the active month to the previous one.
    + */
    +goog.ui.DatePicker.prototype.previousMonth = function() {
    +  this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.MONTHS, -1));
    +  this.updateCalendarGrid_();
    +  this.fireChangeActiveMonthEvent_();
    +};
    +
    +
    +/**
    + * Changes the active month to the next one.
    + */
    +goog.ui.DatePicker.prototype.nextMonth = function() {
    +  this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.MONTHS, 1));
    +  this.updateCalendarGrid_();
    +  this.fireChangeActiveMonthEvent_();
    +};
    +
    +
    +/**
    + * Changes the active year to the previous one.
    + */
    +goog.ui.DatePicker.prototype.previousYear = function() {
    +  this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.YEARS, -1));
    +  this.updateCalendarGrid_();
    +  this.fireChangeActiveMonthEvent_();
    +};
    +
    +
    +/**
    + * Changes the active year to the next one.
    + */
    +goog.ui.DatePicker.prototype.nextYear = function() {
    +  this.activeMonth_.add(new goog.date.Interval(goog.date.Interval.YEARS, 1));
    +  this.updateCalendarGrid_();
    +  this.fireChangeActiveMonthEvent_();
    +};
    +
    +
    +/**
    + * Selects the current date.
    + */
    +goog.ui.DatePicker.prototype.selectToday = function() {
    +  this.setDate(new goog.date.Date());
    +};
    +
    +
    +/**
    + * Clears the selection.
    + */
    +goog.ui.DatePicker.prototype.selectNone = function() {
    +  if (this.allowNone_) {
    +    this.setDate(null);
    +  }
    +};
    +
    +
    +/**
    + * @return {!goog.date.Date} The active month displayed.
    + */
    +goog.ui.DatePicker.prototype.getActiveMonth = function() {
    +  return this.activeMonth_.clone();
    +};
    +
    +
    +/**
    + * @return {goog.date.Date} The selected date or null if nothing is selected.
    + */
    +goog.ui.DatePicker.prototype.getDate = function() {
    +  return this.date_ && this.date_.clone();
    +};
    +
    +
    +/**
    + * @param {number} row The row in the grid.
    + * @param {number} col The column in the grid.
    + * @return {goog.date.Date} The date in the grid or null if there is none.
    + */
    +goog.ui.DatePicker.prototype.getDateAt = function(row, col) {
    +  return this.grid_[row] ?
    +      this.grid_[row][col] ? this.grid_[row][col].clone() : null : null;
    +};
    +
    +
    +/**
    + * Returns a date element given a row and column. In elTable_, the elements that
    + * represent dates are 1 indexed because of other elements such as headers.
    + * This corrects for the offset and makes the API 0 indexed.
    + *
    + * @param {number} row The row in the element table.
    + * @param {number} col The column in the element table.
    + * @return {Element} The element in the grid or null if there is none.
    + * @protected
    + */
    +goog.ui.DatePicker.prototype.getDateElementAt = function(row, col) {
    +  if (row < 0 || col < 0) {
    +    return null;
    +  }
    +  var adjustedRow = row + 1;
    +  return this.elTable_[adjustedRow] ?
    +      this.elTable_[adjustedRow][col + 1] || null : null;
    +};
    +
    +
    +/**
    + * Sets the selected date.
    + *
    + * @param {goog.date.Date|Date} date Date to select or null to select nothing.
    + */
    +goog.ui.DatePicker.prototype.setDate = function(date) {
    +  // Check if the month has been changed.
    +  var sameMonth = date == this.date_ || date && this.date_ &&
    +      date.getFullYear() == this.date_.getFullYear() &&
    +      date.getMonth() == this.date_.getMonth();
    +
    +  // Check if the date has been changed.
    +  var sameDate = date == this.date_ || sameMonth &&
    +      date.getDate() == this.date_.getDate();
    +
    +  // Set current date to clone of supplied goog.date.Date or Date.
    +  this.date_ = date && new goog.date.Date(date);
    +
    +  // Set current month
    +  if (date) {
    +    this.activeMonth_.set(this.date_);
    +    this.activeMonth_.setDate(1);
    +  }
    +
    +  // Update calendar grid even if the date has not changed as even if today is
    +  // selected another month can be displayed.
    +  this.updateCalendarGrid_();
    +
    +  // TODO(eae): Standardize selection and change events with other components.
    +  // Fire select event.
    +  var selectEvent = new goog.ui.DatePickerEvent(
    +      goog.ui.DatePicker.Events.SELECT, this, this.date_);
    +  this.dispatchEvent(selectEvent);
    +
    +  // Fire change event.
    +  if (!sameDate) {
    +    var changeEvent = new goog.ui.DatePickerEvent(
    +        goog.ui.DatePicker.Events.CHANGE, this, this.date_);
    +    this.dispatchEvent(changeEvent);
    +  }
    +
    +  // Fire change active month event.
    +  if (!sameMonth) {
    +    this.fireChangeActiveMonthEvent_();
    +  }
    +};
    +
    +
    +/**
    + * Updates the navigation row (navigating months and maybe years) in the navRow_
    + * element of a created picker.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.updateNavigationRow_ = function() {
    +  if (!this.elNavRow_) {
    +    return;
    +  }
    +  var row = this.elNavRow_;
    +
    +  // Clear the navigation row.
    +  while (row.firstChild) {
    +    row.removeChild(row.firstChild);
    +  }
    +
    +  var fullDateFormat = this.symbols_.DATEFORMATS[
    +      goog.i18n.DateTimeFormat.Format.FULL_DATE].toLowerCase();
    +  this.renderer_.renderNavigationRow(
    +      row, this.simpleNavigation_, this.showWeekNum_, fullDateFormat);
    +
    +  if (this.simpleNavigation_) {
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'previousMonth'),
    +        this.previousMonth);
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'nextMonth'),
    +        this.nextMonth);
    +
    +    this.elMonthYear_ = goog.dom.getElementByClass(
    +        goog.getCssName(this.getBaseCssClass(), 'monthyear'),
    +        row);
    +  } else {
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'previousMonth'),
    +        this.previousMonth);
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'nextMonth'),
    +        this.nextMonth);
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'month'),
    +        this.showMonthMenu_);
    +
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'previousYear'),
    +        this.previousYear);
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'nextYear'),
    +        this.nextYear);
    +    this.addPreventDefaultClickHandler_(row,
    +        goog.getCssName(this.getBaseCssClass(), 'year'),
    +        this.showYearMenu_);
    +
    +    this.elMonth_ = goog.dom.getElementByClass(
    +        goog.getCssName(this.getBaseCssClass(), 'month'), row);
    +    this.elYear_ = goog.dom.getDomHelper().getElementByClass(
    +        goog.getCssName(this.getBaseCssClass(), 'year'), row);
    +  }
    +};
    +
    +
    +/**
    + * Setup click handler with prevent default.
    + *
    + * @param {!Element} parentElement The parent element of the element. This is
    + *     needed because the element in question might not be in the dom yet.
    + * @param {string} cssName The CSS class name of the element to attach a click
    + *     handler.
    + * @param {Function} handlerFunction The click handler function.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.addPreventDefaultClickHandler_ =
    +    function(parentElement, cssName, handlerFunction) {
    +  var element = goog.dom.getElementByClass(cssName, parentElement);
    +  this.getHandler().listen(element,
    +      goog.events.EventType.CLICK,
    +      function(e) {
    +        e.preventDefault();
    +        handlerFunction.call(this, e);
    +      });
    +};
    +
    +
    +/**
    + * Updates the footer row (with select buttons) in the footRow_ element of a
    + * created picker.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.updateFooterRow_ = function() {
    +  if (!this.elFootRow_) {
    +    return;
    +  }
    +
    +  var row = this.elFootRow_;
    +
    +  // Clear the footer row.
    +  goog.dom.removeChildren(row);
    +
    +  this.renderer_.renderFooterRow(row, this.showWeekNum_);
    +
    +  this.addPreventDefaultClickHandler_(row,
    +      goog.getCssName(this.getBaseCssClass(), 'today-btn'),
    +      this.selectToday);
    +  this.addPreventDefaultClickHandler_(row,
    +      goog.getCssName(this.getBaseCssClass(), 'none-btn'),
    +      this.selectNone);
    +
    +  this.elToday_ = goog.dom.getElementByClass(
    +      goog.getCssName(this.getBaseCssClass(), 'today-btn'), row);
    +  this.elNone_ = goog.dom.getElementByClass(
    +      goog.getCssName(this.getBaseCssClass(), 'none-btn'), row);
    +
    +  this.updateTodayAndNone_();
    +};
    +
    +
    +/** @override */
    +goog.ui.DatePicker.prototype.decorateInternal = function(el) {
    +  goog.ui.DatePicker.superClass_.decorateInternal.call(this, el);
    +  goog.asserts.assert(el);
    +  goog.dom.classlist.add(el, this.getBaseCssClass());
    +
    +  var table = this.dom_.createElement('table');
    +  var thead = this.dom_.createElement('thead');
    +  var tbody = this.dom_.createElement('tbody');
    +  var tfoot = this.dom_.createElement('tfoot');
    +
    +  goog.a11y.aria.setRole(tbody, 'grid');
    +  tbody.tabIndex = '0';
    +
    +  // As per comment in colorpicker: table.tBodies and table.tFoot should not be
    +  // used because of a bug in Safari, hence using an instance variable
    +  this.tableBody_ = tbody;
    +  this.tableFoot_ = tfoot;
    +
    +  var row = this.dom_.createElement('tr');
    +  row.className = goog.getCssName(this.getBaseCssClass(), 'head');
    +  this.elNavRow_ = row;
    +  this.updateNavigationRow_();
    +
    +  thead.appendChild(row);
    +
    +  var cell;
    +  this.elTable_ = [];
    +  for (var i = 0; i < 7; i++) {
    +    row = this.dom_.createElement('tr');
    +    this.elTable_[i] = [];
    +    for (var j = 0; j < 8; j++) {
    +      cell = this.dom_.createElement(j == 0 || i == 0 ? 'th' : 'td');
    +      if ((j == 0 || i == 0) && j != i) {
    +        cell.className = (j == 0) ?
    +            goog.getCssName(this.getBaseCssClass(), 'week') :
    +            goog.getCssName(this.getBaseCssClass(), 'wday');
    +        goog.a11y.aria.setRole(cell, j == 0 ? 'rowheader' : 'columnheader');
    +      }
    +      row.appendChild(cell);
    +      this.elTable_[i][j] = cell;
    +    }
    +    tbody.appendChild(row);
    +  }
    +
    +  row = this.dom_.createElement('tr');
    +  row.className = goog.getCssName(this.getBaseCssClass(), 'foot');
    +  this.elFootRow_ = row;
    +  this.updateFooterRow_();
    +  tfoot.appendChild(row);
    +
    +
    +  table.cellSpacing = '0';
    +  table.cellPadding = '0';
    +  table.appendChild(thead);
    +  table.appendChild(tbody);
    +  table.appendChild(tfoot);
    +  el.appendChild(table);
    +
    +  this.redrawWeekdays_();
    +  this.updateCalendarGrid_();
    +
    +  el.tabIndex = 0;
    +};
    +
    +
    +/** @override */
    +goog.ui.DatePicker.prototype.createDom = function() {
    +  goog.ui.DatePicker.superClass_.createDom.call(this);
    +  this.decorateInternal(this.getElement());
    +};
    +
    +
    +/** @override */
    +goog.ui.DatePicker.prototype.enterDocument = function() {
    +  goog.ui.DatePicker.superClass_.enterDocument.call(this);
    +
    +  var eh = this.getHandler();
    +  eh.listen(this.tableBody_, goog.events.EventType.CLICK,
    +      this.handleGridClick_);
    +  eh.listen(this.getKeyHandlerForElement_(this.getElement()),
    +      goog.events.KeyHandler.EventType.KEY, this.handleGridKeyPress_);
    +};
    +
    +
    +/** @override */
    +goog.ui.DatePicker.prototype.exitDocument = function() {
    +  goog.ui.DatePicker.superClass_.exitDocument.call(this);
    +  this.destroyMenu_();
    +  for (var uid in this.keyHandlers_) {
    +    this.keyHandlers_[uid].dispose();
    +  }
    +  this.keyHandlers_ = {};
    +};
    +
    +
    +/**
    + * @deprecated Use decorate instead.
    + */
    +goog.ui.DatePicker.prototype.create =
    +    goog.ui.DatePicker.prototype.decorate;
    +
    +
    +/** @override */
    +goog.ui.DatePicker.prototype.disposeInternal = function() {
    +  goog.ui.DatePicker.superClass_.disposeInternal.call(this);
    +
    +  this.elTable_ = null;
    +  this.tableBody_ = null;
    +  this.tableFoot_ = null;
    +  this.elNavRow_ = null;
    +  this.elFootRow_ = null;
    +  this.elMonth_ = null;
    +  this.elMonthYear_ = null;
    +  this.elYear_ = null;
    +  this.elToday_ = null;
    +  this.elNone_ = null;
    +};
    +
    +
    +/**
    + * Click handler for date grid.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleGridClick_ = function(event) {
    +  if (event.target.tagName == 'TD') {
    +    // colIndex/rowIndex is broken in Safari, find position by looping
    +    var el, x = -2, y = -2; // first col/row is for weekday/weeknum
    +    for (el = event.target; el; el = el.previousSibling, x++) {}
    +    for (el = event.target.parentNode; el; el = el.previousSibling, y++) {}
    +    var obj = this.grid_[y][x];
    +    if (this.isUserSelectableDate_(obj)) {
    +      this.setDate(obj.clone());
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Keypress handler for date grid.
    + *
    + * @param {goog.events.BrowserEvent} event Keypress event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleGridKeyPress_ = function(event) {
    +  var months, days;
    +  switch (event.keyCode) {
    +    case 33: // Page up
    +      event.preventDefault();
    +      months = -1;
    +      break;
    +    case 34: // Page down
    +      event.preventDefault();
    +      months = 1;
    +      break;
    +    case 37: // Left
    +      event.preventDefault();
    +      days = -1;
    +      break;
    +    case 39: // Right
    +      event.preventDefault();
    +      days = 1;
    +      break;
    +    case 38: // Down
    +      event.preventDefault();
    +      days = -7;
    +      break;
    +    case 40: // Up
    +      event.preventDefault();
    +      days = 7;
    +      break;
    +    case 36: // Home
    +      event.preventDefault();
    +      this.selectToday();
    +    case 46: // Delete
    +      event.preventDefault();
    +      this.selectNone();
    +    default:
    +      return;
    +  }
    +  var date;
    +  if (this.date_) {
    +    date = this.date_.clone();
    +    date.add(new goog.date.Interval(0, months, days));
    +  } else {
    +    date = this.activeMonth_.clone();
    +    date.setDate(1);
    +  }
    +  if (this.isUserSelectableDate_(date)) {
    +    this.setDate(date);
    +  }
    +};
    +
    +
    +/**
    + * Click handler for month button. Opens month selection menu.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showMonthMenu_ = function(event) {
    +  event.stopPropagation();
    +
    +  var list = [];
    +  for (var i = 0; i < 12; i++) {
    +    list.push(this.symbols_.STANDALONEMONTHS[i]);
    +  }
    +  this.createMenu_(this.elMonth_, list, this.handleMonthMenuClick_,
    +      this.symbols_.STANDALONEMONTHS[this.activeMonth_.getMonth()]);
    +};
    +
    +
    +/**
    + * Click handler for year button. Opens year selection menu.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.showYearMenu_ = function(event) {
    +  event.stopPropagation();
    +
    +  var list = [];
    +  var year = this.activeMonth_.getFullYear();
    +  var loopDate = this.activeMonth_.clone();
    +  for (var i = -goog.ui.DatePicker.YEAR_MENU_RANGE_;
    +      i <= goog.ui.DatePicker.YEAR_MENU_RANGE_; i++) {
    +    loopDate.setFullYear(year + i);
    +    list.push(this.i18nDateFormatterYear_.format(loopDate));
    +  }
    +  this.createMenu_(this.elYear_, list, this.handleYearMenuClick_,
    +      this.i18nDateFormatterYear_.format(this.activeMonth_));
    +};
    +
    +
    +/**
    + * Call back function for month menu.
    + *
    + * @param {Element} target Selected item.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleMonthMenuClick_ = function(target) {
    +  var itemIndex = Number(target.getAttribute('itemIndex'));
    +  this.activeMonth_.setMonth(itemIndex);
    +  this.updateCalendarGrid_();
    +
    +  if (this.elMonth_.focus) {
    +    this.elMonth_.focus();
    +  }
    +};
    +
    +
    +/**
    + * Call back function for year menu.
    + *
    + * @param {Element} target Selected item.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleYearMenuClick_ = function(target) {
    +  if (target.firstChild.nodeType == goog.dom.NodeType.TEXT) {
    +    // We use the same technique used for months to get the position of the
    +    // item in the menu, as the year is not necessarily numeric.
    +    var itemIndex = Number(target.getAttribute('itemIndex'));
    +    var year = this.activeMonth_.getFullYear();
    +    this.activeMonth_.setFullYear(year + itemIndex -
    +        goog.ui.DatePicker.YEAR_MENU_RANGE_);
    +    this.updateCalendarGrid_();
    +  }
    +
    +  this.elYear_.focus();
    +};
    +
    +
    +/**
    + * Support function for menu creation.
    + *
    + * @param {Element} srcEl Button to create menu for.
    + * @param {Array<string>} items List of items to populate menu with.
    + * @param {function(Element)} method Call back method.
    + * @param {string} selected Item to mark as selected in menu.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.createMenu_ = function(srcEl, items, method,
    +                                                    selected) {
    +  this.destroyMenu_();
    +
    +  var el = this.dom_.createElement('div');
    +  el.className = goog.getCssName(this.getBaseCssClass(), 'menu');
    +
    +  this.menuSelected_ = null;
    +
    +  var ul = this.dom_.createElement('ul');
    +  for (var i = 0; i < items.length; i++) {
    +    var li = this.dom_.createDom('li', null, items[i]);
    +    li.setAttribute('itemIndex', i);
    +    if (items[i] == selected) {
    +      this.menuSelected_ = li;
    +    }
    +    ul.appendChild(li);
    +  }
    +  el.appendChild(ul);
    +  el.style.left = srcEl.offsetLeft + srcEl.parentNode.offsetLeft + 'px';
    +  el.style.top = srcEl.offsetTop + 'px';
    +  el.style.width = srcEl.clientWidth + 'px';
    +  this.elMonth_.parentNode.appendChild(el);
    +
    +  this.menu_ = el;
    +  if (!this.menuSelected_) {
    +    this.menuSelected_ = /** @type {Element} */ (ul.firstChild);
    +  }
    +  this.menuSelected_.className =
    +      goog.getCssName(this.getBaseCssClass(), 'menu-selected');
    +  this.menuCallback_ = method;
    +
    +  var eh = this.getHandler();
    +  eh.listen(this.menu_, goog.events.EventType.CLICK, this.handleMenuClick_);
    +  eh.listen(this.getKeyHandlerForElement_(this.menu_),
    +      goog.events.KeyHandler.EventType.KEY, this.handleMenuKeyPress_);
    +  eh.listen(this.dom_.getDocument(), goog.events.EventType.CLICK,
    +      this.destroyMenu_);
    +  el.tabIndex = 0;
    +  el.focus();
    +};
    +
    +
    +/**
    + * Click handler for menu.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleMenuClick_ = function(event) {
    +  event.stopPropagation();
    +
    +  this.destroyMenu_();
    +  if (this.menuCallback_) {
    +    this.menuCallback_(/** @type {Element} */ (event.target));
    +  }
    +};
    +
    +
    +/**
    + * Keypress handler for menu.
    + *
    + * @param {goog.events.BrowserEvent} event Keypress event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.handleMenuKeyPress_ = function(event) {
    +  // Prevent the grid keypress handler from catching the keypress event.
    +  event.stopPropagation();
    +
    +  var el;
    +  var menuSelected = this.menuSelected_;
    +  switch (event.keyCode) {
    +    case 35: // End
    +      event.preventDefault();
    +      el = menuSelected.parentNode.lastChild;
    +      break;
    +    case 36: // Home
    +      event.preventDefault();
    +      el = menuSelected.parentNode.firstChild;
    +      break;
    +    case 38: // Up
    +      event.preventDefault();
    +      el = menuSelected.previousSibling;
    +      break;
    +    case 40: // Down
    +      event.preventDefault();
    +      el = menuSelected.nextSibling;
    +      break;
    +    case 13: // Enter
    +    case 9: // Tab
    +    case 0: // Space
    +      event.preventDefault();
    +      this.destroyMenu_();
    +      this.menuCallback_(menuSelected);
    +      break;
    +  }
    +  if (el && el != menuSelected) {
    +    menuSelected.className = '';
    +    el.className = goog.getCssName(this.getBaseCssClass(), 'menu-selected');
    +    this.menuSelected_ = /** @type {!Element} */ (el);
    +  }
    +};
    +
    +
    +/**
    + * Support function for menu destruction.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.destroyMenu_ = function() {
    +  if (this.menu_) {
    +    var eh = this.getHandler();
    +    eh.unlisten(this.menu_, goog.events.EventType.CLICK, this.handleMenuClick_);
    +    eh.unlisten(this.getKeyHandlerForElement_(this.menu_),
    +        goog.events.KeyHandler.EventType.KEY, this.handleMenuKeyPress_);
    +    eh.unlisten(this.dom_.getDocument(), goog.events.EventType.CLICK,
    +        this.destroyMenu_);
    +    goog.dom.removeNode(this.menu_);
    +    delete this.menu_;
    +  }
    +};
    +
    +
    +/**
    + * Determines the dates/weekdays for the current month and builds an in memory
    + * representation of the calendar.
    + *
    + * @private
    + */
    +goog.ui.DatePicker.prototype.updateCalendarGrid_ = function() {
    +  if (!this.getElement()) {
    +    return;
    +  }
    +
    +  var date = this.activeMonth_.clone();
    +  date.setDate(1);
    +
    +  // Show year name of select month
    +  if (this.elMonthYear_) {
    +    goog.dom.setTextContent(this.elMonthYear_,
    +        this.i18nDateFormatterMonthYear_.format(date));
    +  }
    +  if (this.elMonth_) {
    +    goog.dom.setTextContent(this.elMonth_,
    +        this.symbols_.STANDALONEMONTHS[date.getMonth()]);
    +  }
    +  if (this.elYear_) {
    +    goog.dom.setTextContent(this.elYear_,
    +        this.i18nDateFormatterYear_.format(date));
    +  }
    +
    +  var wday = date.getWeekday();
    +  var days = date.getNumberOfDaysInMonth();
    +
    +  // Determine how many days to show for previous month
    +  date.add(new goog.date.Interval(goog.date.Interval.MONTHS, -1));
    +  date.setDate(date.getNumberOfDaysInMonth() - (wday - 1));
    +
    +  if (this.showFixedNumWeeks_ && !this.extraWeekAtEnd_ && days + wday < 33) {
    +    date.add(new goog.date.Interval(goog.date.Interval.DAYS, -7));
    +  }
    +
    +  // Create weekday/day grid
    +  var dayInterval = new goog.date.Interval(goog.date.Interval.DAYS, 1);
    +  this.grid_ = [];
    +  for (var y = 0; y < 6; y++) { // Weeks
    +    this.grid_[y] = [];
    +    for (var x = 0; x < 7; x++) { // Weekdays
    +      this.grid_[y][x] = date.clone();
    +      date.add(dayInterval);
    +    }
    +  }
    +
    +  this.redrawCalendarGrid_();
    +};
    +
    +
    +/**
    + * Draws calendar view from in memory representation and applies class names
    + * depending on the selection, weekday and whatever the day belongs to the
    + * active month or not.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.redrawCalendarGrid_ = function() {
    +  if (!this.getElement()) {
    +    return;
    +  }
    +
    +  var month = this.activeMonth_.getMonth();
    +  var today = new goog.date.Date();
    +  var todayYear = today.getFullYear();
    +  var todayMonth = today.getMonth();
    +  var todayDate = today.getDate();
    +
    +  // Draw calendar week by week, a worst case month has six weeks.
    +  for (var y = 0; y < 6; y++) {
    +
    +    // Draw week number, if enabled
    +    if (this.showWeekNum_) {
    +      goog.dom.setTextContent(this.elTable_[y + 1][0],
    +          this.i18nDateFormatterWeek_.format(this.grid_[y][0]));
    +      goog.dom.classlist.set(this.elTable_[y + 1][0],
    +          goog.getCssName(this.getBaseCssClass(), 'week'));
    +    } else {
    +      goog.dom.setTextContent(this.elTable_[y + 1][0], '');
    +      goog.dom.classlist.set(this.elTable_[y + 1][0], '');
    +    }
    +
    +    for (var x = 0; x < 7; x++) {
    +      var o = this.grid_[y][x];
    +      var el = this.elTable_[y + 1][x + 1];
    +
    +      // Assign a unique element id (required for setting the active descendant
    +      // ARIA role) unless already set.
    +      if (!el.id) {
    +        el.id = this.cellIdGenerator_.getNextUniqueId();
    +      }
    +      goog.asserts.assert(el, 'The table DOM element cannot be null.');
    +      goog.a11y.aria.setRole(el, 'gridcell');
    +      var classes = [goog.getCssName(this.getBaseCssClass(), 'date')];
    +      if (!this.isUserSelectableDate_(o)) {
    +        classes.push(goog.getCssName(this.getBaseCssClass(),
    +            'unavailable-date'));
    +      }
    +      if (this.showOtherMonths_ || o.getMonth() == month) {
    +        // Date belongs to previous or next month
    +        if (o.getMonth() != month) {
    +          classes.push(goog.getCssName(this.getBaseCssClass(), 'other-month'));
    +        }
    +
    +        // Apply styles set by setWeekdayClass
    +        var wday = (x + this.activeMonth_.getFirstDayOfWeek() + 7) % 7;
    +        if (this.wdayStyles_[wday]) {
    +          classes.push(this.wdayStyles_[wday]);
    +        }
    +
    +        // Current date
    +        if (o.getDate() == todayDate && o.getMonth() == todayMonth &&
    +            o.getFullYear() == todayYear) {
    +          classes.push(goog.getCssName(this.getBaseCssClass(), 'today'));
    +        }
    +
    +        // Selected date
    +        if (this.date_ && o.getDate() == this.date_.getDate() &&
    +            o.getMonth() == this.date_.getMonth() &&
    +            o.getFullYear() == this.date_.getFullYear()) {
    +          classes.push(goog.getCssName(this.getBaseCssClass(), 'selected'));
    +          goog.asserts.assert(this.tableBody_,
    +              'The table body DOM element cannot be null');
    +          goog.a11y.aria.setState(this.tableBody_, 'activedescendant', el.id);
    +        }
    +
    +        // Custom decorator
    +        if (this.decoratorFunction_) {
    +          var customClass = this.decoratorFunction_(o);
    +          if (customClass) {
    +            classes.push(customClass);
    +          }
    +        }
    +
    +        // Set cell text to the date and apply classes.
    +        var formatedDate = this.longDateFormat_ ?
    +            this.i18nDateFormatterDay2_.format(o) :
    +            this.i18nDateFormatterDay_.format(o);
    +        goog.dom.setTextContent(el, formatedDate);
    +        // Date belongs to previous or next month and showOtherMonths is false,
    +        // clear text and classes.
    +      } else {
    +        goog.dom.setTextContent(el, '');
    +      }
    +      goog.dom.classlist.set(el, classes.join(' '));
    +    }
    +
    +    // Hide the either the last one or last two weeks if they contain no days
    +    // from the active month and the showFixedNumWeeks is false. The first four
    +    // weeks are always shown as no month has less than 28 days).
    +    if (y >= 4) {
    +      var parentEl = /** @type {Element} */ (
    +          this.elTable_[y + 1][0].parentElement ||
    +          this.elTable_[y + 1][0].parentNode);
    +      goog.style.setElementShown(parentEl,
    +          this.grid_[y][0].getMonth() == month || this.showFixedNumWeeks_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Fires the CHANGE_ACTIVE_MONTH event.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.fireChangeActiveMonthEvent_ = function() {
    +  var changeMonthEvent = new goog.ui.DatePickerEvent(
    +      goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH,
    +      this,
    +      this.getActiveMonth());
    +  this.dispatchEvent(changeMonthEvent);
    +};
    +
    +
    +/**
    + * Draw weekday names, if enabled. Start with whatever day has been set as the
    + * first day of week.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.redrawWeekdays_ = function() {
    +  if (!this.getElement()) {
    +    return;
    +  }
    +  if (this.showWeekdays_) {
    +    for (var x = 0; x < 7; x++) {
    +      var el = this.elTable_[0][x + 1];
    +      var wday = (x + this.activeMonth_.getFirstDayOfWeek() + 7) % 7;
    +      goog.dom.setTextContent(el, this.wdayNames_[(wday + 1) % 7]);
    +    }
    +  }
    +  var parentEl =  /** @type {Element} */ (this.elTable_[0][0].parentElement ||
    +      this.elTable_[0][0].parentNode);
    +  goog.style.setElementShown(parentEl, this.showWeekdays_);
    +};
    +
    +
    +/**
    + * Returns the key handler for an element and caches it so that it can be
    + * retrieved at a later point.
    + * @param {Element} el The element to get the key handler for.
    + * @return {goog.events.KeyHandler} The key handler for the element.
    + * @private
    + */
    +goog.ui.DatePicker.prototype.getKeyHandlerForElement_ = function(el) {
    +  var uid = goog.getUid(el);
    +  if (!(uid in this.keyHandlers_)) {
    +    this.keyHandlers_[uid] = new goog.events.KeyHandler(el);
    +  }
    +  return this.keyHandlers_[uid];
    +};
    +
    +
    +
    +/**
    + * Object representing a date picker event.
    + *
    + * @param {string} type Event type.
    + * @param {goog.ui.DatePicker} target Date picker initiating event.
    + * @param {goog.date.Date} date Selected date.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.DatePickerEvent = function(type, target, date) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * The selected date
    +   * @type {goog.date.Date}
    +   */
    +  this.date = date;
    +};
    +goog.inherits(goog.ui.DatePickerEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.html
    new file mode 100644
    index 0000000000..36eb7f619d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.DatePicker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.DatePickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.js
    new file mode 100644
    index 0000000000..36523032d6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/datepicker_test.js
    @@ -0,0 +1,362 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.DatePickerTest');
    +goog.setTestOnly('goog.ui.DatePickerTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.date.Date');
    +goog.require('goog.date.DateRange');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.i18n.DateTimeSymbols');
    +goog.require('goog.i18n.DateTimeSymbols_en_US');
    +goog.require('goog.i18n.DateTimeSymbols_zh_HK');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.DatePicker');
    +
    +var picker;
    +var $$ = goog.dom.getElementsByTagNameAndClass;
    +var sandbox;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +}
    +
    +function tearDown() {
    +  picker.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testIsMonthOnLeft() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_en_US;
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  var head = $$('tr', 'goog-date-picker-head')[0];
    +  var month = $$('button', 'goog-date-picker-month',
    +      head.firstChild)[0];
    +  assertSameElements(
    +      'Button element must have expected class names',
    +      ['goog-date-picker-btn', 'goog-date-picker-month'],
    +      goog.dom.classlist.get(month)
    +  );
    +}
    +
    +function testIsYearOnLeft() {
    +  goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_zh_HK;
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  var head = $$('tr', 'goog-date-picker-head')[0];
    +  var year = $$('button', 'goog-date-picker-year',
    +      head.firstChild)[0];
    +  assertSameElements(
    +      'Button element must have expected class names',
    +      ['goog-date-picker-btn', 'goog-date-picker-year'],
    +      goog.dom.classlist.get(year)
    +  );
    +}
    +
    +function testHidingOfTableFoot0() {
    +  picker = new goog.ui.DatePicker();
    +  picker.setAllowNone(false);
    +  picker.setShowToday(false);
    +  picker.create(sandbox);
    +  var tFoot = $$('tfoot')[0];
    +  assertFalse(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFoot1() {
    +  picker = new goog.ui.DatePicker();
    +  picker.setAllowNone(false);
    +  picker.setShowToday(true);
    +  picker.create(sandbox);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFoot2() {
    +  picker = new goog.ui.DatePicker();
    +  picker.setAllowNone(true);
    +  picker.setShowToday(false);
    +  picker.create(sandbox);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFoot3() {
    +  picker = new goog.ui.DatePicker();
    +  picker.setAllowNone(true);
    +  picker.setShowToday(true);
    +  picker.create(sandbox);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFootAfterCreate0() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setAllowNone(false);
    +  picker.setShowToday(false);
    +  var tFoot = $$('tfoot')[0];
    +  assertFalse(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFootAfterCreate1() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setAllowNone(false);
    +  picker.setShowToday(true);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFootAfterCreate2() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setAllowNone(true);
    +  picker.setShowToday(false);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testHidingOfTableFootAfterCreate3() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setAllowNone(true);
    +  picker.setShowToday(true);
    +  var tFoot = $$('tfoot')[0];
    +  assertTrue(goog.style.isElementShown(tFoot));
    +}
    +
    +function testLongDateFormat() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setLongDateFormat(true);
    +  var dates = $$('td', 'goog-date-picker-date');
    +  for (var i = 0; i < dates.length; i++) {
    +    assertEquals(2, goog.dom.getTextContent(dates[i]).length);
    +  }
    +}
    +
    +function testGetActiveMonth() {
    +  picker = new goog.ui.DatePicker(new Date(2000, 5, 5));
    +  var month = picker.getActiveMonth();
    +  assertObjectEquals(new goog.date.Date(2000, 5, 1), month);
    +
    +  month.setMonth(10);
    +  assertObjectEquals('modifying the returned object is safe',
    +      new goog.date.Date(2000, 5, 1), picker.getActiveMonth());
    +}
    +
    +function testGetDate() {
    +  picker = new goog.ui.DatePicker(new Date(2000, 0, 1));
    +  var date = picker.getDate();
    +  assertObjectEquals(new goog.date.Date(2000, 0, 1), date);
    +
    +  date.setMonth(1);
    +  assertObjectEquals('modifying the returned date is safe',
    +      new goog.date.Date(2000, 0, 1), picker.getDate());
    +
    +  picker.setDate(null);
    +  assertNull('no date is selected', picker.getDate());
    +}
    +
    +function testGetDateAt() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setDate(new Date(2000, 5, 5));
    +  var date = picker.getDateAt(0, 0);
    +  assertTrue(date.equals(picker.grid_[0][0]));
    +
    +  date.setMonth(1);
    +  assertFalse(date.equals(picker.grid_[0][0]));
    +}
    +
    +function testGetDateAt_NotInGrid() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setDate(new Date(2000, 5, 5));
    +  var date = picker.getDateAt(-1, 0);
    +  assertNull(date);
    +
    +  date = picker.getDateAt(0, -1);
    +  assertNull(date);
    +}
    +
    +function testGetDateElementAt() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setDate(new Date(2000, 5, 5));
    +  var element = picker.getDateElementAt(0, 0);
    +  assertEquals('td', element.tagName.toLowerCase());
    +  assertObjectEquals(element, picker.elTable_[1][1]);
    +}
    +
    +function testGetDateElementAt_NotInTable() {
    +  picker = new goog.ui.DatePicker();
    +  picker.create(sandbox);
    +  picker.setDate(new Date(2000, 5, 5));
    +  var element = picker.getDateElementAt(-1, 0);
    +  assertNull(element);
    +
    +  element = picker.getDateElementAt(0, -1);
    +  assertNull(element);
    +
    +  element = picker.getDateElementAt(picker.elTable_.length - 1, 0);
    +  assertNull(element);
    +
    +  element = picker.getDateElementAt(0, picker.elTable_[0].length - 1);
    +  assertNull(element);
    +}
    +
    +function testSetDate() {
    +  picker = new goog.ui.DatePicker();
    +  picker.createDom();
    +  picker.enterDocument();
    +  var selectEvents = 0;
    +  var changeEvents = 0;
    +  var changeActiveMonthEvents = 0;
    +  goog.events.listen(picker, goog.ui.DatePicker.Events.SELECT,
    +      function() {
    +        selectEvents++;
    +      });
    +  goog.events.listen(picker, goog.ui.DatePicker.Events.CHANGE,
    +      function() {
    +        changeEvents++;
    +      });
    +  goog.events.listen(picker, goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH,
    +      function() {
    +        changeActiveMonthEvents++;
    +      });
    +
    +  // Set date.
    +  picker.setDate(new Date(2010, 1, 26));
    +  assertEquals('no select event dispatched', 1, selectEvents);
    +  assertEquals('no change event dispatched', 1, changeEvents);
    +  assertEquals('no change active month event dispatched',
    +      1, changeActiveMonthEvents);
    +  assertTrue('date is set',
    +      new goog.date.Date(2010, 1, 26).equals(picker.getDate()));
    +
    +  // Set date to same date.
    +  picker.setDate(new Date(2010, 1, 26));
    +  assertEquals('1 select event dispatched', 2, selectEvents);
    +  assertEquals('no change event dispatched', 1, changeEvents);
    +  assertEquals('no change active month event dispatched',
    +      1, changeActiveMonthEvents);
    +
    +  // Set date to different date.
    +  picker.setDate(new Date(2010, 1, 27));
    +  assertEquals('another select event dispatched', 3, selectEvents);
    +  assertEquals('1 change event dispatched', 2, changeEvents);
    +  assertEquals('2 change active month events dispatched',
    +      1, changeActiveMonthEvents);
    +
    +  // Set date to a date in a different month.
    +  picker.setDate(new Date(2010, 2, 27));
    +  assertEquals('another select event dispatched', 4, selectEvents);
    +  assertEquals('another change event dispatched', 3, changeEvents);
    +  assertEquals('3 change active month event dispatched',
    +      2, changeActiveMonthEvents);
    +
    +  // Set date to none.
    +  picker.setDate(null);
    +  assertEquals('another select event dispatched', 5, selectEvents);
    +  assertEquals('another change event dispatched', 4, changeEvents);
    +  assertNull('date cleared', picker.getDate());
    +}
    +
    +function testChangeActiveMonth() {
    +  picker = new goog.ui.DatePicker();
    +  var changeActiveMonthEvents = 0;
    +  goog.events.listen(picker, goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH,
    +      function() {
    +        changeActiveMonthEvents++;
    +      });
    +
    +  // Set date.
    +  picker.setDate(new Date(2010, 1, 26));
    +  assertEquals('no change active month event dispatched',
    +      1, changeActiveMonthEvents);
    +  assertTrue('date is set',
    +      new goog.date.Date(2010, 1, 26).equals(picker.getDate()));
    +
    +  // Change to next month.
    +  picker.nextMonth();
    +  assertEquals('1 change active month event dispatched',
    +      2, changeActiveMonthEvents);
    +  assertTrue('date should still be the same',
    +      new goog.date.Date(2010, 1, 26).equals(picker.getDate()));
    +
    +  // Change to next year.
    +  picker.nextYear();
    +  assertEquals('2 change active month events dispatched',
    +      3, changeActiveMonthEvents);
    +
    +  // Change to previous month.
    +  picker.previousMonth();
    +  assertEquals('3 change active month events dispatched',
    +      4, changeActiveMonthEvents);
    +
    +  // Change to previous year.
    +  picker.previousYear();
    +  assertEquals('4 change active month events dispatched',
    +      5, changeActiveMonthEvents);
    +}
    +
    +function testUserSelectableDates() {
    +  var dateRange = new goog.date.DateRange(
    +      new goog.date.Date(2010, 1, 25), new goog.date.Date(2010, 1, 27));
    +  picker = new goog.ui.DatePicker();
    +  picker.setUserSelectableDateRange(dateRange);
    +  assertFalse('should not be selectable date',
    +              picker.isUserSelectableDate_(new goog.date.Date(2010, 1, 24)));
    +  assertTrue('should be a selectable date',
    +      picker.isUserSelectableDate_(new goog.date.Date(2010, 1, 25)));
    +  assertTrue('should be a selectable date',
    +      picker.isUserSelectableDate_(new goog.date.Date(2010, 1, 26)));
    +  assertTrue('should be a selectable date',
    +      picker.isUserSelectableDate_(new goog.date.Date(2010, 1, 27)));
    +  assertFalse('should not be selectable date',
    +              picker.isUserSelectableDate_(new goog.date.Date(2010, 1, 28)));
    +}
    +
    +function testUniqueCellIds() {
    +  picker = new goog.ui.DatePicker();
    +  picker.render();
    +  var cells = goog.dom.getElementsByTagNameAndClass('td', undefined,
    +      picker.getElement());
    +  var existingIds = {};
    +  var numCells = cells.length;
    +  for (var i = 0; i < numCells; i++) {
    +    assertNotNull(cells[i]);
    +    if (goog.a11y.aria.getRole(cells[i]) == goog.a11y.aria.Role.GRIDCELL) {
    +      assertNonEmptyString('cell id is non empty', cells[i].id);
    +      assertUndefined('cell id is not unique',
    +          existingIds[cells[i].id]);
    +      existingIds[cells[i].id] = 1;
    +    }
    +  }
    +}
    +
    +function testDecoratePreservesClasses() {
    +  picker = new goog.ui.DatePicker();
    +  var div = goog.dom.createDom('div', 'existing-class');
    +  picker.decorate(div);
    +  assertTrue(goog.dom.classlist.contains(div, picker.getBaseCssClass()));
    +  assertTrue(goog.dom.classlist.contains(div, 'existing-class'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/datepickerrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/datepickerrenderer.js
    new file mode 100644
    index 0000000000..7bc5224638
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/datepickerrenderer.js
    @@ -0,0 +1,55 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The renderer interface for {@link goog.ui.DatePicker}.
    + *
    + * @see ../demos/datepicker.html
    + */
    +
    +goog.provide('goog.ui.DatePickerRenderer');
    +
    +
    +
    +/**
    + * The renderer for {@link goog.ui.DatePicker}. Renders the date picker's
    + * navigation header and footer.
    + * @interface
    + */
    +goog.ui.DatePickerRenderer = function() {};
    +
    +
    +/**
    + * Render the navigation row.
    + *
    + * @param {!Element} row The parent element to render the component into.
    + * @param {boolean} simpleNavigation Whether the picker should render a simple
    + *     navigation menu that only contains controls for navigating to the next
    + *     and previous month. The default navigation menu contains controls for
    + *     navigating to the next/previous month, next/previous year, and menus for
    + *     jumping to specific months and years.
    + * @param {boolean} showWeekNum Whether week numbers should be shown.
    + * @param {string} fullDateFormat The full date format.
    + *     {@see goog.i18n.DateTimeSymbols}.
    + */
    +goog.ui.DatePickerRenderer.prototype.renderNavigationRow = goog.abstractMethod;
    +
    +
    +/**
    + * Render the footer row.
    + *
    + * @param {!Element} row The parent element to render the component into.
    + * @param {boolean} showWeekNum Whether week numbers should be shown.
    + */
    +goog.ui.DatePickerRenderer.prototype.renderFooterRow = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/decorate.js b/mapbox-gl/closure-library/closure/goog/ui/decorate.js
    new file mode 100644
    index 0000000000..0cf9c2dff6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/decorate.js
    @@ -0,0 +1,38 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides a function that decorates an element based on its CSS
    + * class name.
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.decorate');
    +
    +goog.require('goog.ui.registry');
    +
    +
    +/**
    + * Decorates the element with a suitable {@link goog.ui.Component} instance, if
    + * a matching decorator is found.
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Component?} New component instance, decorating the element.
    + */
    +goog.ui.decorate = function(element) {
    +  var decorator = goog.ui.registry.getDecorator(element);
    +  if (decorator) {
    +    decorator.decorate(element);
    +  }
    +  return decorator;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/decorate_test.html b/mapbox-gl/closure-library/closure/goog/ui/decorate_test.html
    new file mode 100644
    index 0000000000..7d33336617
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/decorate_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.registry
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.decorateTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="x" class="fake-component-x">
    +  </div>
    +  <div id="y" class="fake-component-y fake-component-x">
    +  </div>
    +  <div id="z" class="fake-component-z">
    +  </div>
    +  <div id="u">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/decorate_test.js b/mapbox-gl/closure-library/closure/goog/ui/decorate_test.js
    new file mode 100644
    index 0000000000..961715a6f1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/decorate_test.js
    @@ -0,0 +1,102 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.decorateTest');
    +goog.setTestOnly('goog.ui.decorateTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.decorate');
    +goog.require('goog.ui.registry');
    +// Fake component and renderer implementations, for testing only.
    +
    +// UnknownComponent has no default renderer or decorator registered.
    +function UnknownComponent() {
    +}
    +
    +// FakeComponentX's default renderer is FakeRenderer.  It also has a
    +// decorator.
    +function FakeComponentX() {
    +  this.element = null;
    +}
    +
    +FakeComponentX.prototype.decorate = function(element) {
    +  this.element = element;
    +};
    +
    +// FakeComponentY doesn't have an explicitly registered default
    +// renderer; it should inherit the default renderer from its superclass.
    +// It does have a decorator registered.
    +function FakeComponentY() {
    +  FakeComponentX.call(this);
    +}
    +goog.inherits(FakeComponentY, FakeComponentX);
    +
    +// FakeComponentZ is just another component.  Its default renderer is
    +// FakeSingletonRenderer, but it has no decorator registered.
    +function FakeComponentZ() {
    +}
    +
    +// FakeRenderer is a stateful renderer.
    +function FakeRenderer() {
    +}
    +
    +// FakeSingletonRenderer is a stateless renderer that can be used as a
    +// singleton.
    +function FakeSingletonRenderer() {
    +}
    +
    +FakeSingletonRenderer.instance_ = new FakeSingletonRenderer();
    +
    +FakeSingletonRenderer.getInstance = function() {
    +  return FakeSingletonRenderer.instance_;
    +};
    +
    +function setUp() {
    +  goog.ui.registry.setDefaultRenderer(FakeComponentX, FakeRenderer);
    +  goog.ui.registry.setDefaultRenderer(FakeComponentZ,
    +      FakeSingletonRenderer);
    +
    +  goog.ui.registry.setDecoratorByClassName('fake-component-x',
    +      function() {
    +        return new FakeComponentX();
    +      });
    +  goog.ui.registry.setDecoratorByClassName('fake-component-y',
    +      function() {
    +        return new FakeComponentY();
    +      });
    +}
    +
    +function tearDown() {
    +  goog.ui.registry.reset();
    +}
    +
    +function testDecorate() {
    +  var dx = goog.ui.decorate(document.getElementById('x'));
    +  assertTrue('Decorator for element with fake-component-x class must be ' +
    +      'a FakeComponentX', dx instanceof FakeComponentX);
    +  assertEquals('Element x must have been decorated',
    +      document.getElementById('x'), dx.element);
    +
    +  var dy = goog.ui.decorate(document.getElementById('y'));
    +  assertTrue('Decorator for element with fake-component-y class must be ' +
    +      'a FakeComponentY', dy instanceof FakeComponentY);
    +  assertEquals('Element y must have been decorated',
    +      document.getElementById('y'), dy.element);
    +
    +  var dz = goog.ui.decorate(document.getElementById('z'));
    +  assertNull('Decorator for element with unknown class must be null', dz);
    +
    +  var du = goog.ui.decorate(document.getElementById('u'));
    +  assertNull('Decorator for element without CSS class must be null', du);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/defaultdatepickerrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/defaultdatepickerrenderer.js
    new file mode 100644
    index 0000000000..d13e7757e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/defaultdatepickerrenderer.js
    @@ -0,0 +1,202 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The default renderer for {@link goog.ui.DatePicker}.
    + *
    + * @see ../demos/datepicker.html
    + */
    +
    +goog.provide('goog.ui.DefaultDatePickerRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +/** @suppress {extraRequire} Interface. */
    +goog.require('goog.ui.DatePickerRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.DatePicker}. Renders the date picker's
    + * navigation header and footer.
    + *
    + * @param {string} baseCssClass Name of base CSS class of the date picker.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper.
    + * @constructor
    + * @implements {goog.ui.DatePickerRenderer}
    + */
    +goog.ui.DefaultDatePickerRenderer = function(baseCssClass, opt_domHelper) {
    +  /**
    +   * Name of base CSS class of datepicker
    +   * @type {string}
    +   * @private
    +   */
    +  this.baseCssClass_ = baseCssClass;
    +
    +  /**
    +   * @type {!goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +};
    +
    +
    +/**
    + * Returns the dom helper that is being used on this component.
    + * @return {!goog.dom.DomHelper} The dom helper used on this component.
    + */
    +goog.ui.DefaultDatePickerRenderer.prototype.getDomHelper = function() {
    +  return this.dom_;
    +};
    +
    +
    +/**
    + * Returns base CSS class. This getter is used to get base CSS class part.
    + * All CSS class names in component are created as:
    + *   goog.getCssName(this.getBaseCssClass(), 'CLASS_NAME')
    + * @return {string} Base CSS class.
    + */
    +goog.ui.DefaultDatePickerRenderer.prototype.getBaseCssClass = function() {
    +  return this.baseCssClass_;
    +};
    +
    +
    +/**
    + * Render the navigation row (navigating months and maybe years).
    + *
    + * @param {!Element} row The parent element to render the component into.
    + * @param {boolean} simpleNavigation Whether the picker should render a simple
    + *     navigation menu that only contains controls for navigating to the next
    + *     and previous month. The default navigation menu contains controls for
    + *     navigating to the next/previous month, next/previous year, and menus for
    + *     jumping to specific months and years.
    + * @param {boolean} showWeekNum Whether week numbers should be shown.
    + * @param {string} fullDateFormat The full date format.
    + *     {@see goog.i18n.DateTimeSymbols}.
    + * @override
    + */
    +goog.ui.DefaultDatePickerRenderer.prototype.renderNavigationRow =
    +    function(row, simpleNavigation, showWeekNum, fullDateFormat) {
    +  // Populate the navigation row according to the configured navigation mode.
    +  var cell, monthCell, yearCell;
    +
    +  if (simpleNavigation) {
    +    cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +    cell.colSpan = showWeekNum ? 1 : 2;
    +    this.createButton_(cell, '\u00AB',
    +        goog.getCssName(this.getBaseCssClass(), 'previousMonth'));  // <<
    +    row.appendChild(cell);
    +
    +    cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +    cell.colSpan = showWeekNum ? 6 : 5;
    +    cell.className = goog.getCssName(this.getBaseCssClass(), 'monthyear');
    +    row.appendChild(cell);
    +
    +    cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +    this.createButton_(cell, '\u00BB',
    +        goog.getCssName(this.getBaseCssClass(), 'nextMonth'));  // >>
    +    row.appendChild(cell);
    +
    +  } else {
    +    monthCell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +    monthCell.colSpan = 5;
    +    this.createButton_(monthCell, '\u00AB',
    +        goog.getCssName(this.getBaseCssClass(), 'previousMonth'));  // <<
    +    this.createButton_(monthCell, '',
    +        goog.getCssName(this.getBaseCssClass(), 'month'));
    +    this.createButton_(monthCell, '\u00BB',
    +        goog.getCssName(this.getBaseCssClass(), 'nextMonth'));  // >>
    +
    +    yearCell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +    yearCell.colSpan = 3;
    +    this.createButton_(yearCell, '\u00AB',
    +        goog.getCssName(this.getBaseCssClass(), 'previousYear'));  // <<
    +    this.createButton_(yearCell, '',
    +        goog.getCssName(this.getBaseCssClass(), 'year'));
    +    this.createButton_(yearCell, '\u00BB',
    +        goog.getCssName(this.getBaseCssClass(), 'nextYear'));  // <<
    +
    +    // If the date format has year ('y') appearing first before month ('m'),
    +    // show the year on the left hand side of the datepicker popup.  Otherwise,
    +    // show the month on the left side.  This check assumes the data to be
    +    // valid, and that all date formats contain month and year.
    +    if (fullDateFormat.indexOf('y') < fullDateFormat.indexOf('m')) {
    +      row.appendChild(yearCell);
    +      row.appendChild(monthCell);
    +    } else {
    +      row.appendChild(monthCell);
    +      row.appendChild(yearCell);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Render the footer row (with select buttons).
    + *
    + * @param {!Element} row The parent element to render the component into.
    + * @param {boolean} showWeekNum Whether week numbers should be shown.
    + * @override
    + */
    +goog.ui.DefaultDatePickerRenderer.prototype.renderFooterRow =
    +    function(row, showWeekNum) {
    +  // Populate the footer row with buttons for Today and None.
    +  var cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +  cell.colSpan = showWeekNum ? 2 : 3;
    +  cell.className = goog.getCssName(this.getBaseCssClass(), 'today-cont');
    +
    +  /** @desc Label for button that selects the current date. */
    +  var MSG_DATEPICKER_TODAY_BUTTON_LABEL = goog.getMsg('Today');
    +  this.createButton_(cell, MSG_DATEPICKER_TODAY_BUTTON_LABEL,
    +      goog.getCssName(this.getBaseCssClass(), 'today-btn'));
    +  row.appendChild(cell);
    +
    +  cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +  cell.colSpan = showWeekNum ? 4 : 3;
    +  row.appendChild(cell);
    +
    +  cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
    +  cell.colSpan = 2;
    +  cell.className = goog.getCssName(this.getBaseCssClass(), 'none-cont');
    +
    +  /** @desc Label for button that clears the selection. */
    +  var MSG_DATEPICKER_NONE = goog.getMsg('None');
    +  this.createButton_(cell, MSG_DATEPICKER_NONE,
    +      goog.getCssName(this.getBaseCssClass(), 'none-btn'));
    +  row.appendChild(cell);
    +};
    +
    +
    +/**
    + * Support function for button creation.
    + *
    + * @param {Element} parentNode Container the button should be added to.
    + * @param {string} label Button label.
    + * @param {string=} opt_className Class name for button, which will be used
    + *    in addition to "goog-date-picker-btn".
    + * @private
    + * @return {!Element} The created button element.
    + */
    +goog.ui.DefaultDatePickerRenderer.prototype.createButton_ =
    +    function(parentNode, label, opt_className) {
    +  var classes = [goog.getCssName(this.getBaseCssClass(), 'btn')];
    +  if (opt_className) {
    +    classes.push(opt_className);
    +  }
    +  var el = this.getDomHelper().createElement(goog.dom.TagName.BUTTON);
    +  el.className = classes.join(' ');
    +  el.appendChild(this.getDomHelper().createTextNode(label));
    +  parentNode.appendChild(el);
    +  return el;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dialog.js b/mapbox-gl/closure-library/closure/goog/ui/dialog.js
    new file mode 100644
    index 0000000000..81c1910d34
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dialog.js
    @@ -0,0 +1,1603 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for showing simple modal dialog boxes.
    + *
    + * TODO(user):
    + *   * Standardize CSS class names with other components
    + *   * Add functionality to "host" other components in content area
    + *   * Abstract out ButtonSet and make it more general
    + * @see ../demos/dialog.html
    + */
    +
    +goog.provide('goog.ui.Dialog');
    +goog.provide('goog.ui.Dialog.ButtonSet');
    +goog.provide('goog.ui.Dialog.ButtonSet.DefaultButtons');
    +goog.provide('goog.ui.Dialog.DefaultButtonCaptions');
    +goog.provide('goog.ui.Dialog.DefaultButtonKeys');
    +goog.provide('goog.ui.Dialog.Event');
    +goog.provide('goog.ui.Dialog.EventType');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.dom.safe');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.math.Rect');
    +goog.require('goog.string');
    +goog.require('goog.structs.Map');
    +goog.require('goog.style');
    +goog.require('goog.ui.ModalPopup');
    +
    +
    +
    +/**
    + * Class for showing simple dialog boxes.
    + * The Html structure of the dialog box is:
    + * <pre>
    + *  Element         Function                Class-name, modal-dialog = default
    + * ----------------------------------------------------------------------------
    + * - iframe         Iframe mask              modal-dialog-bg
    + * - div            Background mask          modal-dialog-bg
    + * - div            Dialog area              modal-dialog
    + *     - div        Title bar                modal-dialog-title
    + *        - span                             modal-dialog-title-text
    + *          - text  Title text               N/A
    + *        - span                             modal-dialog-title-close
    + *          - ??    Close box                N/A
    + *     - div        Content area             modal-dialog-content
    + *        - ??      User specified content   N/A
    + *     - div        Button area              modal-dialog-buttons
    + *        - button                           N/A
    + *        - button
    + *        - ...
    + * </pre>
    + * @constructor
    + * @param {string=} opt_class CSS class name for the dialog element, also used
    + *     as a class name prefix for related elements; defaults to modal-dialog.
    + *     This should be a single, valid CSS class name.
    + * @param {boolean=} opt_useIframeMask Work around windowed controls z-index
    + *     issue by using an iframe instead of a div for bg element.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link
    + *     goog.ui.Component} for semantics.
    + * @extends {goog.ui.ModalPopup}
    + */
    +goog.ui.Dialog = function(opt_class, opt_useIframeMask, opt_domHelper) {
    +  goog.ui.Dialog.base(this, 'constructor', opt_useIframeMask, opt_domHelper);
    +
    +  /**
    +   * CSS class name for the dialog element, also used as a class name prefix for
    +   * related elements.  Defaults to goog.getCssName('modal-dialog').
    +   * @type {string}
    +   * @private
    +   */
    +  this.class_ = opt_class || goog.getCssName('modal-dialog');
    +
    +  this.buttons_ = goog.ui.Dialog.ButtonSet.createOkCancel();
    +};
    +goog.inherits(goog.ui.Dialog, goog.ui.ModalPopup);
    +goog.tagUnsealableClass(goog.ui.Dialog);
    +
    +
    +/**
    + * Button set.  Default to Ok/Cancel.
    + * @type {goog.ui.Dialog.ButtonSet}
    + * @private
    + */
    +goog.ui.Dialog.prototype.buttons_;
    +
    +
    +/**
    + * Whether the escape key closes this dialog.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Dialog.prototype.escapeToCancel_ = true;
    +
    +
    +/**
    + * Whether this dialog should include a title close button.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Dialog.prototype.hasTitleCloseButton_ = true;
    +
    +
    +/**
    + * Whether the dialog is modal. Defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Dialog.prototype.modal_ = true;
    +
    +
    +/**
    + * Whether the dialog is draggable. Defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Dialog.prototype.draggable_ = true;
    +
    +
    +/**
    + * Opacity for background mask.  Defaults to 50%.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Dialog.prototype.backgroundElementOpacity_ = 0.50;
    +
    +
    +/**
    + * Dialog's title.
    + * @type {string}
    + * @private
    + */
    +goog.ui.Dialog.prototype.title_ = '';
    +
    +
    +/**
    + * Dialog's content (HTML).
    + * @type {goog.html.SafeHtml}
    + * @private
    + */
    +goog.ui.Dialog.prototype.content_ = null;
    +
    +
    +/**
    + * Dragger.
    + * @type {goog.fx.Dragger}
    + * @private
    + */
    +goog.ui.Dialog.prototype.dragger_ = null;
    +
    +
    +/**
    + * Whether the dialog should be disposed when it is hidden.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Dialog.prototype.disposeOnHide_ = false;
    +
    +
    +/**
    + * Element for the title bar.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.prototype.titleEl_ = null;
    +
    +
    +/**
    + * Element for the text area of the title bar.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.prototype.titleTextEl_ = null;
    +
    +
    +/**
    + * Id of element for the text area of the title bar.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.Dialog.prototype.titleTextId_ = null;
    +
    +
    +/**
    + * Element for the close box area of the title bar.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.prototype.titleCloseEl_ = null;
    +
    +
    +/**
    + * Element for the content area.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.prototype.contentEl_ = null;
    +
    +
    +/**
    + * Element for the button bar.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.prototype.buttonEl_ = null;
    +
    +
    +/**
    + * The dialog's preferred ARIA role.
    + * @type {goog.a11y.aria.Role}
    + * @private
    + */
    +goog.ui.Dialog.prototype.preferredAriaRole_ = goog.a11y.aria.Role.DIALOG;
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.getCssClass = function() {
    +  return this.class_;
    +};
    +
    +
    +/**
    + * Sets the title.
    + * @param {string} title The title text.
    + */
    +goog.ui.Dialog.prototype.setTitle = function(title) {
    +  this.title_ = title;
    +  if (this.titleTextEl_) {
    +    goog.dom.setTextContent(this.titleTextEl_, title);
    +  }
    +};
    +
    +
    +/**
    + * Gets the title.
    + * @return {string} The title.
    + */
    +goog.ui.Dialog.prototype.getTitle = function() {
    +  return this.title_;
    +};
    +
    +
    +/**
    + * Allows arbitrary HTML to be set in the content element.
    + * TODO(user): Deprecate in favor of setSafeHtmlContent, once developer docs on
    + * using goog.html.SafeHtml are in place.
    + * @param {string} html Content HTML.
    + */
    +goog.ui.Dialog.prototype.setContent = function(html) {
    +  this.setSafeHtmlContent(goog.html.legacyconversions.safeHtmlFromString(html));
    +};
    +
    +
    +/**
    + * Allows arbitrary HTML to be set in the content element.
    + * @param {!goog.html.SafeHtml} html Content HTML.
    + */
    +goog.ui.Dialog.prototype.setSafeHtmlContent = function(html) {
    +  this.content_ = html;
    +  if (this.contentEl_) {
    +    goog.dom.safe.setInnerHtml(this.contentEl_, html);
    +  }
    +};
    +
    +
    +/**
    + * Gets the content HTML of the content element as a plain string.
    + *
    + * Note that this method returns the HTML markup that was previously set via
    + * setContent(). In particular, the HTML returned by this method does not
    + * reflect any changes to the content element's DOM that were made my means
    + * other than setContent().
    + *
    + * @return {string} Content HTML.
    + */
    +goog.ui.Dialog.prototype.getContent = function() {
    +  return this.content_ != null ?
    +      goog.html.SafeHtml.unwrap(this.content_) : '';
    +};
    +
    +
    +/**
    + * Gets the content HTML of the content element.
    + * @return {goog.html.SafeHtml} Content HTML.
    + */
    +goog.ui.Dialog.prototype.getSafeHtmlContent = function() {
    +  return this.content_;
    +};
    +
    +
    +/**
    + * Returns the dialog's preferred ARIA role. This can be used to override the
    + * default dialog role, e.g. with an ARIA role of ALERTDIALOG for a simple
    + * warning or confirmation dialog.
    + * @return {goog.a11y.aria.Role} This dialog's preferred ARIA role.
    + */
    +goog.ui.Dialog.prototype.getPreferredAriaRole = function() {
    +  return this.preferredAriaRole_;
    +};
    +
    +
    +/**
    + * Sets the dialog's preferred ARIA role. This can be used to override the
    + * default dialog role, e.g. with an ARIA role of ALERTDIALOG for a simple
    + * warning or confirmation dialog.
    + * @param {goog.a11y.aria.Role} role This dialog's preferred ARIA role.
    + */
    +goog.ui.Dialog.prototype.setPreferredAriaRole = function(role) {
    +  this.preferredAriaRole_ = role;
    +};
    +
    +
    +/**
    + * Renders if the DOM is not created.
    + * @private
    + */
    +goog.ui.Dialog.prototype.renderIfNoDom_ = function() {
    +  if (!this.getElement()) {
    +    // TODO(gboyer): Ideally we'd only create the DOM, but many applications
    +    // are requiring this behavior.  Eventually, it would be best if the
    +    // element getters could return null if the elements have not been
    +    // created.
    +    this.render();
    +  }
    +};
    +
    +
    +/**
    + * Returns the content element so that more complicated things can be done with
    + * the content area.  Renders if the DOM is not yet created.  Overrides
    + * {@link goog.ui.Component#getContentElement}.
    + * @return {Element} The content element.
    + * @override
    + */
    +goog.ui.Dialog.prototype.getContentElement = function() {
    +  this.renderIfNoDom_();
    +  return this.contentEl_;
    +};
    +
    +
    +/**
    + * Returns the title element so that more complicated things can be done with
    + * the title.  Renders if the DOM is not yet created.
    + * @return {Element} The title element.
    + */
    +goog.ui.Dialog.prototype.getTitleElement = function() {
    +  this.renderIfNoDom_();
    +  return this.titleEl_;
    +};
    +
    +
    +/**
    + * Returns the title text element so that more complicated things can be done
    + * with the text of the title.  Renders if the DOM is not yet created.
    + * @return {Element} The title text element.
    + */
    +goog.ui.Dialog.prototype.getTitleTextElement = function() {
    +  this.renderIfNoDom_();
    +  return this.titleTextEl_;
    +};
    +
    +
    +/**
    + * Returns the title close element so that more complicated things can be done
    + * with the close area of the title.  Renders if the DOM is not yet created.
    + * @return {Element} The close box.
    + */
    +goog.ui.Dialog.prototype.getTitleCloseElement = function() {
    +  this.renderIfNoDom_();
    +  return this.titleCloseEl_;
    +};
    +
    +
    +/**
    + * Returns the button element so that more complicated things can be done with
    + * the button area.  Renders if the DOM is not yet created.
    + * @return {Element} The button container element.
    + */
    +goog.ui.Dialog.prototype.getButtonElement = function() {
    +  this.renderIfNoDom_();
    +  return this.buttonEl_;
    +};
    +
    +
    +/**
    + * Returns the dialog element so that more complicated things can be done with
    + * the dialog box.  Renders if the DOM is not yet created.
    + * @return {Element} The dialog element.
    + */
    +goog.ui.Dialog.prototype.getDialogElement = function() {
    +  this.renderIfNoDom_();
    +  return this.getElement();
    +};
    +
    +
    +/**
    + * Returns the background mask element so that more complicated things can be
    + * done with the background region.  Renders if the DOM is not yet created.
    + * @return {Element} The background mask element.
    + * @override
    + */
    +goog.ui.Dialog.prototype.getBackgroundElement = function() {
    +  this.renderIfNoDom_();
    +  return goog.ui.Dialog.base(this, 'getBackgroundElement');
    +};
    +
    +
    +/**
    + * Gets the opacity of the background mask.
    + * @return {number} Background mask opacity.
    + */
    +goog.ui.Dialog.prototype.getBackgroundElementOpacity = function() {
    +  return this.backgroundElementOpacity_;
    +};
    +
    +
    +/**
    + * Sets the opacity of the background mask.
    + * @param {number} opacity Background mask opacity.
    + */
    +goog.ui.Dialog.prototype.setBackgroundElementOpacity = function(opacity) {
    +  this.backgroundElementOpacity_ = opacity;
    +
    +  if (this.getElement()) {
    +    var bgEl = this.getBackgroundElement();
    +    if (bgEl) {
    +      goog.style.setOpacity(bgEl, this.backgroundElementOpacity_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the modal property of the dialog. In case the dialog is already
    + * inDocument, renders the modal background elements according to the specified
    + * modal parameter.
    + *
    + * Note that non-modal dialogs cannot use an iframe mask.
    + *
    + * @param {boolean} modal Whether the dialog is modal.
    + */
    +goog.ui.Dialog.prototype.setModal = function(modal) {
    +  if (modal != this.modal_) {
    +    this.setModalInternal_(modal);
    +  }
    +};
    +
    +
    +/**
    + * Sets the modal property of the dialog.
    + * @param {boolean} modal Whether the dialog is modal.
    + * @private
    + */
    +goog.ui.Dialog.prototype.setModalInternal_ = function(modal) {
    +  this.modal_ = modal;
    +  if (this.isInDocument()) {
    +    var dom = this.getDomHelper();
    +    var bg = this.getBackgroundElement();
    +    var bgIframe = this.getBackgroundIframe();
    +    if (modal) {
    +      if (bgIframe) {
    +        dom.insertSiblingBefore(bgIframe, this.getElement());
    +      }
    +      dom.insertSiblingBefore(bg, this.getElement());
    +    } else {
    +      dom.removeNode(bgIframe);
    +      dom.removeNode(bg);
    +    }
    +  }
    +  if (this.isVisible()) {
    +    this.setA11YDetectBackground(modal);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} modal Whether the dialog is modal.
    + */
    +goog.ui.Dialog.prototype.getModal = function() {
    +  return this.modal_;
    +};
    +
    +
    +/**
    + * @return {string} The CSS class name for the dialog element.
    + */
    +goog.ui.Dialog.prototype.getClass = function() {
    +  return this.getCssClass();
    +};
    +
    +
    +/**
    + * Sets whether the dialog can be dragged.
    + * @param {boolean} draggable Whether the dialog can be dragged.
    + */
    +goog.ui.Dialog.prototype.setDraggable = function(draggable) {
    +  this.draggable_ = draggable;
    +  this.setDraggingEnabled_(draggable && this.isInDocument());
    +};
    +
    +
    +/**
    + * Returns a dragger for moving the dialog and adds a class for the move cursor.
    + * Defaults to allow dragging of the title only, but can be overridden if
    + * different drag targets or dragging behavior is desired.
    + * @return {!goog.fx.Dragger} The created dragger instance.
    + * @protected
    + */
    +goog.ui.Dialog.prototype.createDragger = function() {
    +  return new goog.fx.Dragger(this.getElement(), this.titleEl_);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the dialog is draggable.
    + */
    +goog.ui.Dialog.prototype.getDraggable = function() {
    +  return this.draggable_;
    +};
    +
    +
    +/**
    + * Enables or disables dragging.
    + * @param {boolean} enabled Whether to enable it.
    + * @private
    + */
    +goog.ui.Dialog.prototype.setDraggingEnabled_ = function(enabled) {
    +  // This isn't ideal, but the quickest and easiest way to append
    +  // title-draggable to the last class in the class_ string, then trim and
    +  // split the string into an array (in case the dialog was set up with
    +  // multiple, space-separated class names).
    +  var classNames = goog.string.trim(goog.getCssName(this.class_,
    +      'title-draggable')).split(' ');
    +
    +  if (this.getElement()) {
    +    if (enabled) {
    +      goog.dom.classlist.addAll(
    +          goog.asserts.assert(this.titleEl_), classNames);
    +    } else {
    +      goog.dom.classlist.removeAll(
    +          goog.asserts.assert(this.titleEl_), classNames);
    +    }
    +  }
    +
    +  if (enabled && !this.dragger_) {
    +    this.dragger_ = this.createDragger();
    +    goog.dom.classlist.addAll(goog.asserts.assert(this.titleEl_), classNames);
    +    goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.START,
    +        this.setDraggerLimits_, false, this);
    +  } else if (!enabled && this.dragger_) {
    +    this.dragger_.dispose();
    +    this.dragger_ = null;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.createDom = function() {
    +  goog.ui.Dialog.base(this, 'createDom');
    +  var element = this.getElement();
    +  goog.asserts.assert(element, 'getElement() returns null');
    +
    +  var dom = this.getDomHelper();
    +  this.titleEl_ = dom.createDom('div', goog.getCssName(this.class_, 'title'),
    +      this.titleTextEl_ = dom.createDom(
    +          'span',
    +          {'className': goog.getCssName(this.class_, 'title-text'),
    +            'id': this.getId()},
    +          this.title_),
    +      this.titleCloseEl_ = dom.createDom(
    +          'span', goog.getCssName(this.class_, 'title-close'))),
    +  goog.dom.append(element, this.titleEl_,
    +      this.contentEl_ = dom.createDom('div',
    +          goog.getCssName(this.class_, 'content')),
    +      this.buttonEl_ = dom.createDom('div',
    +          goog.getCssName(this.class_, 'buttons')));
    +
    +  // Make the title and close button behave correctly with screen readers.
    +  // Note: this is only being added if the dialog is not decorated. Decorators
    +  // are expected to add aria label, role, and tab indexing in their templates.
    +  goog.a11y.aria.setRole(this.titleTextEl_, goog.a11y.aria.Role.HEADING);
    +  goog.a11y.aria.setRole(this.titleCloseEl_, goog.a11y.aria.Role.BUTTON);
    +  goog.dom.setFocusableTabIndex(this.titleCloseEl_, true);
    +  goog.a11y.aria.setLabel(this.titleCloseEl_,
    +      goog.ui.Dialog.MSG_GOOG_UI_DIALOG_CLOSE_);
    +
    +  this.titleTextId_ = this.titleTextEl_.id;
    +  goog.a11y.aria.setRole(element, this.getPreferredAriaRole());
    +  goog.a11y.aria.setState(element, goog.a11y.aria.State.LABELLEDBY,
    +      this.titleTextId_ || '');
    +  // If setContent() was called before createDom(), make sure the inner HTML of
    +  // the content element is initialized.
    +  if (this.content_) {
    +    goog.dom.safe.setInnerHtml(this.contentEl_, this.content_);
    +  }
    +  goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_);
    +
    +  // Render the buttons.
    +  if (this.buttons_) {
    +    this.buttons_.attachToElement(this.buttonEl_);
    +  }
    +  goog.style.setElementShown(this.buttonEl_, !!this.buttons_);
    +  this.setBackgroundElementOpacity(this.backgroundElementOpacity_);
    +};
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.decorateInternal = function(element) {
    +  goog.ui.Dialog.base(this, 'decorateInternal', element);
    +  var dialogElement = this.getElement();
    +  goog.asserts.assert(dialogElement,
    +      'The DOM element for dialog cannot be null.');
    +  // Decorate or create the content element.
    +  var contentClass = goog.getCssName(this.class_, 'content');
    +  this.contentEl_ = goog.dom.getElementsByTagNameAndClass(
    +      null, contentClass, dialogElement)[0];
    +  if (!this.contentEl_) {
    +    this.contentEl_ = this.getDomHelper().createDom('div', contentClass);
    +    if (this.content_) {
    +      goog.dom.safe.setInnerHtml(this.contentEl_, this.content_);
    +    }
    +    dialogElement.appendChild(this.contentEl_);
    +  }
    +
    +  // Decorate or create the title bar element.
    +  var titleClass = goog.getCssName(this.class_, 'title');
    +  var titleTextClass = goog.getCssName(this.class_, 'title-text');
    +  var titleCloseClass = goog.getCssName(this.class_, 'title-close');
    +  this.titleEl_ = goog.dom.getElementsByTagNameAndClass(
    +      null, titleClass, dialogElement)[0];
    +  if (this.titleEl_) {
    +    // Only look for title text & title close elements if a title bar element
    +    // was found.  Otherwise assume that the entire title bar has to be
    +    // created from scratch.
    +    this.titleTextEl_ = goog.dom.getElementsByTagNameAndClass(
    +        null, titleTextClass, this.titleEl_)[0];
    +    this.titleCloseEl_ = goog.dom.getElementsByTagNameAndClass(
    +        null, titleCloseClass, this.titleEl_)[0];
    +  } else {
    +    // Create the title bar element and insert it before the content area.
    +    // This is useful if the element to decorate only includes a content area.
    +    this.titleEl_ = this.getDomHelper().createDom('div', titleClass);
    +    dialogElement.insertBefore(this.titleEl_, this.contentEl_);
    +  }
    +
    +  // Decorate or create the title text element.
    +  if (this.titleTextEl_) {
    +    this.title_ = goog.dom.getTextContent(this.titleTextEl_);
    +    // Give the title text element an id if it doesn't already have one.
    +    if (!this.titleTextEl_.id) {
    +      this.titleTextEl_.id = this.getId();
    +    }
    +  } else {
    +    this.titleTextEl_ = goog.dom.createDom(
    +        'span', {'className': titleTextClass, 'id': this.getId()});
    +    this.titleEl_.appendChild(this.titleTextEl_);
    +  }
    +  this.titleTextId_ = this.titleTextEl_.id;
    +  goog.a11y.aria.setState(dialogElement, goog.a11y.aria.State.LABELLEDBY,
    +      this.titleTextId_ || '');
    +  // Decorate or create the title close element.
    +  if (!this.titleCloseEl_) {
    +    this.titleCloseEl_ = this.getDomHelper().createDom('span', titleCloseClass);
    +    this.titleEl_.appendChild(this.titleCloseEl_);
    +  }
    +  goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_);
    +
    +  // Decorate or create the button container element.
    +  var buttonsClass = goog.getCssName(this.class_, 'buttons');
    +  this.buttonEl_ = goog.dom.getElementsByTagNameAndClass(
    +      null, buttonsClass, dialogElement)[0];
    +  if (this.buttonEl_) {
    +    // Button container element found.  Create empty button set and use it to
    +    // decorate the button container.
    +    this.buttons_ = new goog.ui.Dialog.ButtonSet(this.getDomHelper());
    +    this.buttons_.decorate(this.buttonEl_);
    +  } else {
    +    // Create new button container element, and render a button set into it.
    +    this.buttonEl_ = this.getDomHelper().createDom('div', buttonsClass);
    +    dialogElement.appendChild(this.buttonEl_);
    +    if (this.buttons_) {
    +      this.buttons_.attachToElement(this.buttonEl_);
    +    }
    +    goog.style.setElementShown(this.buttonEl_, !!this.buttons_);
    +  }
    +  this.setBackgroundElementOpacity(this.backgroundElementOpacity_);
    +};
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.enterDocument = function() {
    +  goog.ui.Dialog.base(this, 'enterDocument');
    +
    +  // Listen for keyboard events while the dialog is visible.
    +  this.getHandler().
    +      listen(this.getElement(), goog.events.EventType.KEYDOWN, this.onKey_).
    +      listen(this.getElement(), goog.events.EventType.KEYPRESS, this.onKey_);
    +
    +  // NOTE: see bug 1163154 for an example of an edge case where making the
    +  // dialog visible in response to a KEYDOWN will result in a CLICK event
    +  // firing on the default button (immediately closing the dialog) if the key
    +  // that fired the KEYDOWN is also normally used to activate controls
    +  // (i.e. SPACE/ENTER).
    +  //
    +  // This could be worked around by attaching the onButtonClick_ handler in a
    +  // setTimeout, but that was deemed undesirable.
    +  this.getHandler().listen(this.buttonEl_, goog.events.EventType.CLICK,
    +      this.onButtonClick_);
    +
    +  // Add drag support.
    +  this.setDraggingEnabled_(this.draggable_);
    +
    +  // Add event listeners to the close box and the button container.
    +  this.getHandler().listen(
    +      this.titleCloseEl_, goog.events.EventType.CLICK,
    +      this.onTitleCloseClick_);
    +
    +  var element = this.getElement();
    +  goog.asserts.assert(element, 'The DOM element for dialog cannot be null');
    +  goog.a11y.aria.setRole(element, this.getPreferredAriaRole());
    +  if (this.titleTextEl_.id !== '') {
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.LABELLEDBY,
    +        this.titleTextEl_.id);
    +  }
    +
    +  if (!this.modal_) {
    +    this.setModalInternal_(false);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.exitDocument = function() {
    +  if (this.isVisible()) {
    +    this.setVisible(false);
    +  }
    +
    +  // Remove drag support.
    +  this.setDraggingEnabled_(false);
    +
    +  goog.ui.Dialog.base(this, 'exitDocument');
    +};
    +
    +
    +/**
    + * Sets the visibility of the dialog box and moves focus to the
    + * default button. Lazily renders the component if needed. After this
    + * method returns, isVisible() will always return the new state, even
    + * if there is a transition.
    + * @param {boolean} visible Whether the dialog should be visible.
    + * @override
    + */
    +goog.ui.Dialog.prototype.setVisible = function(visible) {
    +  if (visible == this.isVisible()) {
    +    return;
    +  }
    +
    +  // If the dialog hasn't been rendered yet, render it now.
    +  if (!this.isInDocument()) {
    +    this.render();
    +  }
    +
    +  goog.ui.Dialog.base(this, 'setVisible', visible);
    +};
    +
    +
    +/**
    + * @override
    + * @suppress {deprecated} AFTER_SHOW is deprecated earlier in this file.
    + */
    +goog.ui.Dialog.prototype.onShow = function() {
    +  goog.ui.Dialog.base(this, 'onShow');
    +  this.dispatchEvent(goog.ui.Dialog.EventType.AFTER_SHOW);
    +};
    +
    +
    +/**
    + * @override
    + * @suppress {deprecated} AFTER_HIDE is deprecated earlier in this file.
    + */
    +goog.ui.Dialog.prototype.onHide = function() {
    +  goog.ui.Dialog.base(this, 'onHide');
    +  this.dispatchEvent(goog.ui.Dialog.EventType.AFTER_HIDE);
    +  if (this.disposeOnHide_) {
    +    this.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Sets dragger limits when dragging is started.
    + * @param {!goog.events.Event} e goog.fx.Dragger.EventType.START event.
    + * @private
    + */
    +goog.ui.Dialog.prototype.setDraggerLimits_ = function(e) {
    +  var doc = this.getDomHelper().getDocument();
    +  var win = goog.dom.getWindow(doc) || window;
    +
    +  // Take the max of scroll height and view height for cases in which document
    +  // does not fill screen.
    +  var viewSize = goog.dom.getViewportSize(win);
    +  var w = Math.max(doc.body.scrollWidth, viewSize.width);
    +  var h = Math.max(doc.body.scrollHeight, viewSize.height);
    +
    +  var dialogSize = goog.style.getSize(this.getElement());
    +  if (goog.style.getComputedPosition(this.getElement()) == 'fixed') {
    +    // Ensure position:fixed dialogs can't be dragged beyond the viewport.
    +    this.dragger_.setLimits(new goog.math.Rect(0, 0,
    +        Math.max(0, viewSize.width - dialogSize.width),
    +        Math.max(0, viewSize.height - dialogSize.height)));
    +  } else {
    +    this.dragger_.setLimits(new goog.math.Rect(0, 0,
    +        w - dialogSize.width, h - dialogSize.height));
    +  }
    +};
    +
    +
    +/**
    + * Handles a click on the title close area.
    + * @param {goog.events.BrowserEvent} e Browser's event object.
    + * @private
    + */
    +goog.ui.Dialog.prototype.onTitleCloseClick_ = function(e) {
    +  this.handleTitleClose_();
    +};
    +
    +
    +/**
    + * Performs the action of closing the dialog in response to the title close
    + * button being interacted with. General purpose method to be called by click
    + * and button event handlers.
    + * @private
    + */
    +goog.ui.Dialog.prototype.handleTitleClose_ = function() {
    +  if (!this.hasTitleCloseButton_) {
    +    return;
    +  }
    +
    +  var bs = this.getButtonSet();
    +  var key = bs && bs.getCancel();
    +  // Only if there is a valid cancel button is an event dispatched.
    +  if (key) {
    +    var caption = /** @type {Element|string} */(bs.get(key));
    +    if (this.dispatchEvent(new goog.ui.Dialog.Event(key, caption))) {
    +      this.setVisible(false);
    +    }
    +  } else {
    +    this.setVisible(false);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this dialog has a title close button.
    + */
    +goog.ui.Dialog.prototype.getHasTitleCloseButton = function() {
    +  return this.hasTitleCloseButton_;
    +};
    +
    +
    +/**
    + * Sets whether the dialog should have a close button in the title bar. There
    + * will always be an element for the title close button, but setting this
    + * parameter to false will cause it to be hidden and have no active listener.
    + * @param {boolean} b Whether this dialog should have a title close button.
    + */
    +goog.ui.Dialog.prototype.setHasTitleCloseButton = function(b) {
    +  this.hasTitleCloseButton_ = b;
    +  if (this.titleCloseEl_) {
    +    goog.style.setElementShown(this.titleCloseEl_, this.hasTitleCloseButton_);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the escape key should close this dialog.
    + */
    +goog.ui.Dialog.prototype.isEscapeToCancel = function() {
    +  return this.escapeToCancel_;
    +};
    +
    +
    +/**
    + * @param {boolean} b Whether the escape key should close this dialog.
    + */
    +goog.ui.Dialog.prototype.setEscapeToCancel = function(b) {
    +  this.escapeToCancel_ = b;
    +};
    +
    +
    +/**
    + * Sets whether the dialog should be disposed when it is hidden.  By default
    + * dialogs are not disposed when they are hidden.
    + * @param {boolean} b Whether the dialog should get disposed when it gets
    + *     hidden.
    + */
    +goog.ui.Dialog.prototype.setDisposeOnHide = function(b) {
    +  this.disposeOnHide_ = b;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the dialog should be disposed when it is hidden.
    + */
    +goog.ui.Dialog.prototype.getDisposeOnHide = function() {
    +  return this.disposeOnHide_;
    +};
    +
    +
    +/** @override */
    +goog.ui.Dialog.prototype.disposeInternal = function() {
    +  this.titleCloseEl_ = null;
    +  this.buttonEl_ = null;
    +  goog.ui.Dialog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Sets the button set to use.
    + * Note: Passing in null will cause no button set to be rendered.
    + * @param {goog.ui.Dialog.ButtonSet?} buttons The button set to use.
    + */
    +goog.ui.Dialog.prototype.setButtonSet = function(buttons) {
    +  this.buttons_ = buttons;
    +  if (this.buttonEl_) {
    +    if (this.buttons_) {
    +      this.buttons_.attachToElement(this.buttonEl_);
    +    } else {
    +      goog.dom.safe.setInnerHtml(
    +          this.buttonEl_, goog.html.SafeHtml.EMPTY);
    +    }
    +    goog.style.setElementShown(this.buttonEl_, !!this.buttons_);
    +  }
    +};
    +
    +
    +/**
    + * Returns the button set being used.
    + * @return {goog.ui.Dialog.ButtonSet?} The button set being used.
    + */
    +goog.ui.Dialog.prototype.getButtonSet = function() {
    +  return this.buttons_;
    +};
    +
    +
    +/**
    + * Handles a click on the button container.
    + * @param {goog.events.BrowserEvent} e Browser's event object.
    + * @private
    + */
    +goog.ui.Dialog.prototype.onButtonClick_ = function(e) {
    +  var button = this.findParentButton_(/** @type {Element} */ (e.target));
    +  if (button && !button.disabled) {
    +    var key = button.name;
    +    var caption = /** @type {Element|string} */(
    +        this.getButtonSet().get(key));
    +    if (this.dispatchEvent(new goog.ui.Dialog.Event(key, caption))) {
    +      this.setVisible(false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Finds the parent button of an element (or null if there was no button
    + * parent).
    + * @param {Element} element The element that was clicked on.
    + * @return {Element} Returns the parent button or null if not found.
    + * @private
    + */
    +goog.ui.Dialog.prototype.findParentButton_ = function(element) {
    +  var el = element;
    +  while (el != null && el != this.buttonEl_) {
    +    if (el.tagName == 'BUTTON') {
    +      return /** @type {Element} */(el);
    +    }
    +    el = el.parentNode;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Handles keydown and keypress events, and dismisses the popup if cancel is
    + * pressed.  If there is a cancel action in the ButtonSet, than that will be
    + * fired.  Also prevents tabbing out of the dialog.
    + * @param {goog.events.BrowserEvent} e Browser's event object.
    + * @private
    + */
    +goog.ui.Dialog.prototype.onKey_ = function(e) {
    +  var close = false;
    +  var hasHandler = false;
    +  var buttonSet = this.getButtonSet();
    +  var target = e.target;
    +
    +  if (e.type == goog.events.EventType.KEYDOWN) {
    +    // Escape and tab can only properly be handled in keydown handlers.
    +    if (this.escapeToCancel_ && e.keyCode == goog.events.KeyCodes.ESC) {
    +      // Only if there is a valid cancel button is an event dispatched.
    +      var cancel = buttonSet && buttonSet.getCancel();
    +
    +      // Users may expect to hit escape on a SELECT element.
    +      var isSpecialFormElement =
    +          target.tagName == 'SELECT' && !target.disabled;
    +
    +      if (cancel && !isSpecialFormElement) {
    +        hasHandler = true;
    +
    +        var caption = buttonSet.get(cancel);
    +        close = this.dispatchEvent(
    +            new goog.ui.Dialog.Event(cancel,
    +                /** @type {Element|null|string} */(caption)));
    +      } else if (!isSpecialFormElement) {
    +        close = true;
    +      }
    +    } else if (e.keyCode == goog.events.KeyCodes.TAB && e.shiftKey &&
    +        target == this.getElement()) {
    +      // Prevent the user from shift-tabbing backwards out of the dialog box.
    +      // Instead, set up a wrap in focus backward to the end of the dialog.
    +      this.setupBackwardTabWrap();
    +    }
    +  } else if (e.keyCode == goog.events.KeyCodes.ENTER) {
    +    // Only handle ENTER in keypress events, in case the action opens a
    +    // popup window.
    +    var key;
    +    if (target.tagName == 'BUTTON' && !target.disabled) {
    +      // If the target is a button and it's enabled, we can fire that button's
    +      // handler.
    +      key = target.name;
    +    } else if (target == this.titleCloseEl_) {
    +      // if the title 'close' button is in focus, close the dialog
    +      this.handleTitleClose_();
    +    } else if (buttonSet) {
    +      // Try to fire the default button's handler (if one exists), but only if
    +      // the button is enabled.
    +      var defaultKey = buttonSet.getDefault();
    +      var defaultButton = defaultKey && buttonSet.getButton(defaultKey);
    +
    +      // Users may expect to hit enter on a TEXTAREA, SELECT or an A element.
    +      var isSpecialFormElement =
    +          (target.tagName == 'TEXTAREA' || target.tagName == 'SELECT' ||
    +           target.tagName == 'A') && !target.disabled;
    +
    +      if (defaultButton && !defaultButton.disabled && !isSpecialFormElement) {
    +        key = defaultKey;
    +      }
    +    }
    +    if (key && buttonSet) {
    +      hasHandler = true;
    +      close = this.dispatchEvent(
    +          new goog.ui.Dialog.Event(key, String(buttonSet.get(key))));
    +    }
    +  } else if (target == this.titleCloseEl_ &&
    +      e.keyCode == goog.events.KeyCodes.SPACE) {
    +    // if the title 'close' button is in focus on 'SPACE,' close the dialog
    +    this.handleTitleClose_();
    +  }
    +
    +  if (close || hasHandler) {
    +    e.stopPropagation();
    +    e.preventDefault();
    +  }
    +
    +  if (close) {
    +    this.setVisible(false);
    +  }
    +};
    +
    +
    +
    +/**
    + * Dialog event class.
    + * @param {string} key Key identifier for the button.
    + * @param {string|Element} caption Caption on the button (might be i18nlized).
    + * @constructor
    + * @extends {goog.events.Event}
    + */
    +goog.ui.Dialog.Event = function(key, caption) {
    +  this.type = goog.ui.Dialog.EventType.SELECT;
    +  this.key = key;
    +  this.caption = caption;
    +};
    +goog.inherits(goog.ui.Dialog.Event, goog.events.Event);
    +
    +
    +/**
    + * Event type constant for dialog events.
    + * TODO(attila): Change this to goog.ui.Dialog.EventType.SELECT.
    + * @type {string}
    + * @deprecated Use goog.ui.Dialog.EventType.SELECT.
    + */
    +goog.ui.Dialog.SELECT_EVENT = 'dialogselect';
    +
    +
    +/**
    + * Events dispatched by dialogs.
    + * @enum {string}
    + */
    +goog.ui.Dialog.EventType = {
    +  /**
    +   * Dispatched when the user closes the dialog.
    +   * The dispatched event will always be of type {@link goog.ui.Dialog.Event}.
    +   * Canceling the event will prevent the dialog from closing.
    +   */
    +  SELECT: 'dialogselect',
    +
    +  /**
    +   * Dispatched after the dialog is closed. Not cancelable.
    +   * @deprecated Use goog.ui.PopupBase.EventType.HIDE.
    +   */
    +  AFTER_HIDE: 'afterhide',
    +
    +  /**
    +   * Dispatched after the dialog is shown. Not cancelable.
    +   * @deprecated Use goog.ui.PopupBase.EventType.SHOW.
    +   */
    +  AFTER_SHOW: 'aftershow'
    +};
    +
    +
    +
    +/**
    + * A button set defines the behaviour of a set of buttons that the dialog can
    + * show.  Uses the {@link goog.structs.Map} interface.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link
    + *    goog.ui.Component} for semantics.
    + * @constructor
    + * @extends {goog.structs.Map}
    + */
    +goog.ui.Dialog.ButtonSet = function(opt_domHelper) {
    +  // TODO(attila):  Refactor ButtonSet to extend goog.ui.Component?
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +  goog.structs.Map.call(this);
    +};
    +goog.inherits(goog.ui.Dialog.ButtonSet, goog.structs.Map);
    +goog.tagUnsealableClass(goog.ui.Dialog.ButtonSet);
    +
    +
    +/**
    + * A CSS className for this component.
    + * @type {string}
    + * @private
    + */
    +goog.ui.Dialog.ButtonSet.prototype.class_ = goog.getCssName('goog-buttonset');
    +
    +
    +/**
    + * The button that has default focus (references key in buttons_ map).
    + * @type {?string}
    + * @private
    + */
    +goog.ui.Dialog.ButtonSet.prototype.defaultButton_ = null;
    +
    +
    +/**
    + * Optional container the button set should be rendered into.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Dialog.ButtonSet.prototype.element_ = null;
    +
    +
    +/**
    + * The button whose action is associated with the escape key and the X button
    + * on the dialog.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.Dialog.ButtonSet.prototype.cancelButton_ = null;
    +
    +
    +/**
    + * Adds a button to the button set.  Buttons will be displayed in the order they
    + * are added.
    + *
    + * @param {*} key Key used to identify the button in events.
    + * @param {*} caption A string caption or a DOM node that can be
    + *     appended to a button element.
    + * @param {boolean=} opt_isDefault Whether this button is the default button,
    + *     Dialog will dispatch for this button if enter is pressed.
    + * @param {boolean=} opt_isCancel Whether this button has the same behaviour as
    + *    cancel.  If escape is pressed this button will fire.
    + * @return {!goog.ui.Dialog.ButtonSet} The button set, to make it easy to chain
    + *    "set" calls and build new ButtonSets.
    + * @override
    + */
    +goog.ui.Dialog.ButtonSet.prototype.set = function(key, caption,
    +    opt_isDefault, opt_isCancel) {
    +  goog.structs.Map.prototype.set.call(this, key, caption);
    +
    +  if (opt_isDefault) {
    +    this.defaultButton_ = /** @type {?string} */ (key);
    +  }
    +  if (opt_isCancel) {
    +    this.cancelButton_ = /** @type {?string} */ (key);
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a button (an object with a key and caption) to this button set. Buttons
    + * will be displayed in the order they are added.
    + * @see goog.ui.Dialog.DefaultButtons
    + * @param {{key: string, caption: string}} button The button key and caption.
    + * @param {boolean=} opt_isDefault Whether this button is the default button.
    + *     Dialog will dispatch for this button if enter is pressed.
    + * @param {boolean=} opt_isCancel Whether this button has the same behavior as
    + *     cancel. If escape is pressed this button will fire.
    + * @return {!goog.ui.Dialog.ButtonSet} The button set, to make it easy to chain
    + *     "addButton" calls and build new ButtonSets.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.addButton = function(button, opt_isDefault,
    +    opt_isCancel) {
    +  return this.set(button.key, button.caption, opt_isDefault, opt_isCancel);
    +};
    +
    +
    +/**
    + * Attaches the button set to an element, rendering it inside.
    + * @param {Element} el Container.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.attachToElement = function(el) {
    +  this.element_ = el;
    +  this.render();
    +};
    +
    +
    +/**
    + * Renders the button set inside its container element.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.render = function() {
    +  if (this.element_) {
    +    goog.dom.safe.setInnerHtml(
    +        this.element_, goog.html.SafeHtml.EMPTY);
    +    var domHelper = goog.dom.getDomHelper(this.element_);
    +    this.forEach(function(caption, key) {
    +      var button = domHelper.createDom('button', {'name': key}, caption);
    +      if (key == this.defaultButton_) {
    +        button.className = goog.getCssName(this.class_, 'default');
    +      }
    +      this.element_.appendChild(button);
    +    }, this);
    +  }
    +};
    +
    +
    +/**
    + * Decorates the given element by adding any {@code button} elements found
    + * among its descendants to the button set.  The first button found is assumed
    + * to be the default and will receive focus when the button set is rendered.
    + * If a button with a name of {@link goog.ui.Dialog.DefaultButtonKeys.CANCEL}
    + * is found, it is assumed to have "Cancel" semantics.
    + * TODO(attila):  ButtonSet should be a goog.ui.Component.  Really.
    + * @param {Element} element The element to decorate; should contain buttons.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.decorate = function(element) {
    +  if (!element || element.nodeType != goog.dom.NodeType.ELEMENT) {
    +    return;
    +  }
    +
    +  this.element_ = element;
    +  var buttons = this.element_.getElementsByTagName('button');
    +  for (var i = 0, button, key, caption; button = buttons[i]; i++) {
    +    // Buttons should have a "name" attribute and have their caption defined by
    +    // their innerHTML, but not everyone knows this, and we should play nice.
    +    key = button.name || button.id;
    +    caption = goog.dom.getTextContent(button) || button.value;
    +    if (key) {
    +      var isDefault = i == 0;
    +      var isCancel = button.name == goog.ui.Dialog.DefaultButtonKeys.CANCEL;
    +      this.set(key, caption, isDefault, isCancel);
    +      if (isDefault) {
    +        goog.dom.classlist.add(button, goog.getCssName(this.class_,
    +            'default'));
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the component's element.
    + * @return {Element} The element for the component.
    + * TODO(user): Remove after refactoring to goog.ui.Component.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Returns the dom helper that is being used on this component.
    + * @return {!goog.dom.DomHelper} The dom helper used on this component.
    + * TODO(user): Remove after refactoring to goog.ui.Component.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getDomHelper = function() {
    +  return this.dom_;
    +};
    +
    +
    +/**
    + * Sets the default button.
    + * @param {?string} key The default button.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.setDefault = function(key) {
    +  this.defaultButton_ = key;
    +};
    +
    +
    +/**
    + * Returns the default button.
    + * @return {?string} The default button.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getDefault = function() {
    +  return this.defaultButton_;
    +};
    +
    +
    +/**
    + * Sets the cancel button.
    + * @param {?string} key The cancel button.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.setCancel = function(key) {
    +  this.cancelButton_ = key;
    +};
    +
    +
    +/**
    + * Returns the cancel button.
    + * @return {?string} The cancel button.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getCancel = function() {
    +  return this.cancelButton_;
    +};
    +
    +
    +/**
    + * Returns the HTML Button element.
    + * @param {string} key The button to return.
    + * @return {Element} The button, if found else null.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getButton = function(key) {
    +  var buttons = this.getAllButtons();
    +  for (var i = 0, nextButton; nextButton = buttons[i]; i++) {
    +    if (nextButton.name == key || nextButton.id == key) {
    +      return nextButton;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns all the HTML Button elements in the button set container.
    + * @return {!NodeList} A live NodeList of the buttons.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.getAllButtons = function() {
    +  return this.element_.getElementsByTagName(goog.dom.TagName.BUTTON);
    +};
    +
    +
    +/**
    + * Enables or disables a button in this set by key. If the button is not found,
    + * does nothing.
    + * @param {string} key The button to enable or disable.
    + * @param {boolean} enabled True to enable; false to disable.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.setButtonEnabled = function(key, enabled) {
    +  var button = this.getButton(key);
    +  if (button) {
    +    button.disabled = !enabled;
    +  }
    +};
    +
    +
    +/**
    + * Enables or disables all of the buttons in this set.
    + * @param {boolean} enabled True to enable; false to disable.
    + */
    +goog.ui.Dialog.ButtonSet.prototype.setAllButtonsEnabled = function(enabled) {
    +  var allButtons = this.getAllButtons();
    +  for (var i = 0, button; button = allButtons[i]; i++) {
    +    button.disabled = !enabled;
    +  }
    +};
    +
    +
    +/**
    + * The keys used to identify standard buttons in events.
    + * @enum {string}
    + */
    +goog.ui.Dialog.DefaultButtonKeys = {
    +  OK: 'ok',
    +  CANCEL: 'cancel',
    +  YES: 'yes',
    +  NO: 'no',
    +  SAVE: 'save',
    +  CONTINUE: 'continue'
    +};
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'OK' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_OK_ = goog.getMsg('OK');
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'Cancel' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_CANCEL_ = goog.getMsg('Cancel');
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'Yes' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_YES_ = goog.getMsg('Yes');
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'No' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_NO_ = goog.getMsg('No');
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'Save' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_SAVE_ = goog.getMsg('Save');
    +
    +
    +/**
    + * @desc Standard caption for the dialog 'Continue' button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_DIALOG_CONTINUE_ = goog.getMsg('Continue');
    +
    +
    +/**
    + * @desc Standard label for the dialog 'X' (close) button.
    + * @private
    + */
    +goog.ui.Dialog.MSG_GOOG_UI_DIALOG_CLOSE_ = goog.getMsg('Close');
    +
    +
    +/**
    + * The default captions for the default buttons.
    + * @enum {string}
    + */
    +goog.ui.Dialog.DefaultButtonCaptions = {
    +  OK: goog.ui.Dialog.MSG_DIALOG_OK_,
    +  CANCEL: goog.ui.Dialog.MSG_DIALOG_CANCEL_,
    +  YES: goog.ui.Dialog.MSG_DIALOG_YES_,
    +  NO: goog.ui.Dialog.MSG_DIALOG_NO_,
    +  SAVE: goog.ui.Dialog.MSG_DIALOG_SAVE_,
    +  CONTINUE: goog.ui.Dialog.MSG_DIALOG_CONTINUE_
    +};
    +
    +
    +/**
    + * The standard buttons (keys associated with captions).
    + * @enum {{key: string, caption: string}}
    + */
    +goog.ui.Dialog.ButtonSet.DefaultButtons = {
    +  OK: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.OK,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.OK
    +  },
    +  CANCEL: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.CANCEL,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.CANCEL
    +  },
    +  YES: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.YES,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.YES
    +  },
    +  NO: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.NO,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.NO
    +  },
    +  SAVE: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.SAVE,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.SAVE
    +  },
    +  CONTINUE: {
    +    key: goog.ui.Dialog.DefaultButtonKeys.CONTINUE,
    +    caption: goog.ui.Dialog.DefaultButtonCaptions.CONTINUE
    +  }
    +};
    +
    +
    +/**
    + * Creates a new ButtonSet with a single 'OK' button, which is also set with
    + * cancel button semantics so that pressing escape will close the dialog.
    + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet.
    + */
    +goog.ui.Dialog.ButtonSet.createOk = function() {
    +  return new goog.ui.Dialog.ButtonSet().
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.OK, true, true);
    +};
    +
    +
    +/**
    + * Creates a new ButtonSet with 'OK' (default) and 'Cancel' buttons.
    + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet.
    + */
    +goog.ui.Dialog.ButtonSet.createOkCancel = function() {
    +  return new goog.ui.Dialog.ButtonSet().
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.OK, true).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, false, true);
    +};
    +
    +
    +/**
    + * Creates a new ButtonSet with 'Yes' (default) and 'No' buttons.
    + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet.
    + */
    +goog.ui.Dialog.ButtonSet.createYesNo = function() {
    +  return new goog.ui.Dialog.ButtonSet().
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.YES, true).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.NO, false, true);
    +};
    +
    +
    +/**
    + * Creates a new ButtonSet with 'Yes', 'No' (default), and 'Cancel' buttons.
    + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet.
    + */
    +goog.ui.Dialog.ButtonSet.createYesNoCancel = function() {
    +  return new goog.ui.Dialog.ButtonSet().
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.YES).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.NO, true).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, false, true);
    +};
    +
    +
    +/**
    + * Creates a new ButtonSet with 'Continue', 'Save', and 'Cancel' (default)
    + * buttons.
    + * @return {!goog.ui.Dialog.ButtonSet} The created ButtonSet.
    + */
    +goog.ui.Dialog.ButtonSet.createContinueSaveCancel = function() {
    +  return new goog.ui.Dialog.ButtonSet().
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CONTINUE).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.SAVE).
    +      addButton(goog.ui.Dialog.ButtonSet.DefaultButtons.CANCEL, true, true);
    +};
    +
    +
    +// TODO(user): These shared instances should be phased out.
    +(function() {
    +  if (typeof document != 'undefined') {
    +    /** @deprecated Use goog.ui.Dialog.ButtonSet#createOk. */
    +    goog.ui.Dialog.ButtonSet.OK = goog.ui.Dialog.ButtonSet.createOk();
    +
    +    /** @deprecated Use goog.ui.Dialog.ButtonSet#createOkCancel. */
    +    goog.ui.Dialog.ButtonSet.OK_CANCEL =
    +        goog.ui.Dialog.ButtonSet.createOkCancel();
    +
    +    /** @deprecated Use goog.ui.Dialog.ButtonSet#createYesNo. */
    +    goog.ui.Dialog.ButtonSet.YES_NO = goog.ui.Dialog.ButtonSet.createYesNo();
    +
    +    /** @deprecated Use goog.ui.Dialog.ButtonSet#createYesNoCancel. */
    +    goog.ui.Dialog.ButtonSet.YES_NO_CANCEL =
    +        goog.ui.Dialog.ButtonSet.createYesNoCancel();
    +
    +    /** @deprecated Use goog.ui.Dialog.ButtonSet#createContinueSaveCancel. */
    +    goog.ui.Dialog.ButtonSet.CONTINUE_SAVE_CANCEL =
    +        goog.ui.Dialog.ButtonSet.createContinueSaveCancel();
    +  }
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dialog_test.html b/mapbox-gl/closure-library/closure/goog/ui/dialog_test.html
    new file mode 100644
    index 0000000000..99697c58b5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dialog_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Dialog
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.DialogTest');
    +  </script>
    + </head>
    + <body>
    +  <iframe id="f" src="javascript:'&lt;input&gt;'">
    +   <input />
    +   '">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dialog_test.js b/mapbox-gl/closure-library/closure/goog/ui/dialog_test.js
    new file mode 100644
    index 0000000000..8f9221172f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dialog_test.js
    @@ -0,0 +1,844 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.DialogTest');
    +goog.setTestOnly('goog.ui.DialogTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.fx.css3');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.testing');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Dialog');
    +goog.require('goog.userAgent');
    +var bodyChildElement;
    +var decorateTarget;
    +var dialog;
    +var mockClock;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +  bodyChildElement = document.createElement('div');
    +  document.body.appendChild(bodyChildElement);
    +  dialog = new goog.ui.Dialog();
    +  var buttons = new goog.ui.Dialog.ButtonSet();
    +  buttons.set(goog.ui.Dialog.DefaultButtonKeys.CANCEL,
    +      'Foo!',
    +      false,
    +      true);
    +  buttons.set(goog.ui.Dialog.DefaultButtonKeys.OK,
    +      'OK',
    +      true);
    +  dialog.setButtonSet(buttons);
    +  dialog.setVisible(true);
    +
    +  decorateTarget = goog.dom.createDom('div');
    +  document.body.appendChild(decorateTarget);
    +
    +  // Reset global flags to their defaults.
    +  /** @suppress {missingRequire} */
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', true);
    +}
    +
    +function tearDown() {
    +  dialog.dispose();
    +  goog.dom.removeNode(bodyChildElement);
    +  goog.dom.removeNode(decorateTarget);
    +  mockClock.dispose();
    +}
    +
    +function testCrossFrameFocus() {
    +  // Firefox (3.6, maybe future versions) fails this test when there are too
    +  // many other test files being run concurrently.
    +  if (goog.userAgent.IE || goog.userAgent.GECKO) {
    +    return;
    +  }
    +  dialog.setVisible(false);
    +  var iframeWindow = goog.dom.getElement('f').contentWindow;
    +  var iframeInput = iframeWindow.document.getElementsByTagName('input')[0];
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  var dialogElement = dialog.getElement();
    +  var focusCounter = 0;
    +  goog.events.listen(dialogElement, 'focus', function() {
    +    focusCounter++;
    +  });
    +  iframeInput.focus();
    +  dialog.setVisible(true);
    +  dialog.setVisible(false);
    +  iframeInput.focus();
    +  dialog.setVisible(true);
    +  assertEquals(2, focusCounter);
    +}
    +
    +function testNoDisabledButtonFocus() {
    +  dialog.setVisible(false);
    +  var buttonEl =
    +      dialog.getButtonSet().getButton(goog.ui.Dialog.DefaultButtonKeys.OK);
    +  buttonEl.disabled = true;
    +  var focused = false;
    +  buttonEl.focus = function() {
    +    focused = true;
    +  };
    +  dialog.setVisible(true);
    +  assertFalse('Should not have called focus on disabled button', focused);
    +}
    +
    +function testNoTitleClose() {
    +  assertTrue(goog.style.isElementShown(dialog.getTitleCloseElement()));
    +  dialog.setHasTitleCloseButton(false);
    +  assertFalse(goog.style.isElementShown(dialog.getTitleCloseElement()));
    +}
    +
    +
    +/**
    + * Helper that clicks the first button in the dialog and checks if that
    + * results in a goog.ui.Dialog.EventType.SELECT being dispatched.
    + * @param {boolean} disableButton Whether to disable the button being
    + *     tested.
    + * @return {boolean} Whether a goog.ui.Dialog.EventType.SELECT was dispatched.
    + */
    +function checkSelectDispatchedOnButtonClick(disableButton) {
    +  var aButton = dialog.getButtonElement().getElementsByTagName('BUTTON')[0];
    +  assertNotEquals(aButton, null);
    +  aButton.disabled = disableButton;
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +  goog.testing.events.fireClickSequence(aButton);
    +  return wasCalled;
    +}
    +
    +function testButtonClicksDispatchSelectEvents() {
    +  assertTrue('Select event should be dispatched' +
    +             ' when clicking on an enabled button',
    +      checkSelectDispatchedOnButtonClick(false));
    +}
    +
    +function testDisabledButtonClicksDontDispatchSelectEvents() {
    +  assertFalse('Select event should not be dispatched' +
    +              ' when clicking on a disabled button',
    +      checkSelectDispatchedOnButtonClick(true));
    +}
    +
    +function testEnterKeyDispatchesDefaultSelectEvents() {
    +  var okButton = dialog.getButtonElement().getElementsByTagName('BUTTON')[1];
    +  assertNotEquals(okButton, null);
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +  // Test that event is not dispatched when default button is disabled.
    +  okButton.disabled = true;
    +  goog.testing.events.fireKeySequence(dialog.getElement(),
    +                                      goog.events.KeyCodes.ENTER);
    +  assertFalse(wasCalled);
    +  // Test that event is dispatched when default button is enabled.
    +  okButton.disabled = false;
    +  goog.testing.events.fireKeySequence(dialog.getElement(),
    +                                      goog.events.KeyCodes.ENTER);
    +  assertTrue(wasCalled);
    +}
    +
    +function testEnterKeyOnDisabledDefaultButtonDoesNotDispatchSelectEvents() {
    +  var okButton = dialog.getButtonElement().getElementsByTagName('BUTTON')[1];
    +  okButton.focus();
    +
    +  var callRecorder = goog.testing.recordFunction();
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +
    +  okButton.disabled = true;
    +  goog.testing.events.fireKeySequence(okButton, goog.events.KeyCodes.ENTER);
    +  assertEquals(0, callRecorder.getCallCount());
    +
    +  okButton.disabled = false;
    +  goog.testing.events.fireKeySequence(okButton, goog.events.KeyCodes.ENTER);
    +  assertEquals(1, callRecorder.getCallCount());
    +}
    +
    +function testEnterKeyDoesNothingOnSpecialFormElements() {
    +  checkEnterKeyDoesNothingOnSpecialFormElement(
    +      '<textarea>Hello dialog</textarea>',
    +      'TEXTAREA');
    +
    +  checkEnterKeyDoesNothingOnSpecialFormElement(
    +      '<select>Selection</select>',
    +      'SELECT');
    +
    +  checkEnterKeyDoesNothingOnSpecialFormElement(
    +      '<a href="http://google.com">Hello dialog</a>',
    +      'A');
    +}
    +
    +function checkEnterKeyDoesNothingOnSpecialFormElement(content, tagName) {
    +  // TODO(user): Switch to setSafeHtmlContent here and elsewhere.
    +  dialog.setContent(content);
    +  var formElement = dialog.getContentElement().
    +      getElementsByTagName(tagName)[0];
    +  var wasCalled = false;
    +  var callRecorder = function() {
    +    wasCalled = true;
    +  };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +
    +  // Enter does not fire on the enabled form element.
    +  goog.testing.events.fireKeySequence(formElement,
    +      goog.events.KeyCodes.ENTER);
    +  assertFalse(wasCalled);
    +
    +  // Enter fires on the disabled form element.
    +  formElement.disabled = true;
    +  goog.testing.events.fireKeySequence(formElement,
    +      goog.events.KeyCodes.ENTER);
    +  assertTrue(wasCalled);
    +}
    +
    +function testEscapeKeyDoesNothingOnSpecialFormElements() {
    +  dialog.setContent('<select><option>Hello</option>' +
    +      '<option>dialog</option></select>');
    +  var select = dialog.getContentElement().
    +      getElementsByTagName('SELECT')[0];
    +  var wasCalled = false;
    +  var callRecorder = function() {
    +    wasCalled = true;
    +  };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +
    +  // Escape does not fire on the enabled select box.
    +  goog.testing.events.fireKeySequence(select,
    +      goog.events.KeyCodes.ESC);
    +  assertFalse(wasCalled);
    +
    +  // Escape fires on the disabled select.
    +  select.disabled = true;
    +  goog.testing.events.fireKeySequence(select,
    +      goog.events.KeyCodes.ESC);
    +  assertTrue(wasCalled);
    +}
    +
    +function testEscapeCloses() {
    +  // If escapeCloses is set to false, the dialog should ignore the escape key
    +  assertTrue(dialog.isEscapeToCancel());
    +  dialog.setEscapeToCancel(false);
    +  assertFalse(dialog.isEscapeToCancel());
    +
    +  var buttons = new goog.ui.Dialog.ButtonSet();
    +  buttons.set(goog.ui.Dialog.DefaultButtonKeys.OK, 'OK', true);
    +  dialog.setButtonSet(buttons);
    +  goog.testing.events.fireKeySequence(dialog.getContentElement(),
    +      goog.events.KeyCodes.ESC);
    +  assertTrue(dialog.isVisible());
    +
    +  // Having a cancel button should make no difference, escape should still not
    +  // work.
    +  buttons.set(goog.ui.Dialog.DefaultButtonKeys.CANCEL, 'Foo!', false, true);
    +  dialog.setButtonSet(buttons);
    +  goog.testing.events.fireKeySequence(dialog.getContentElement(),
    +      goog.events.KeyCodes.ESC);
    +  assertTrue(dialog.isVisible());
    +}
    +
    +function testKeydownClosesWithoutButtonSet() {
    +  // Clear button set
    +  dialog.setButtonSet(null);
    +
    +  // Create a custom button.
    +  dialog.setContent('<button id="button" name="ok">OK</button>');
    +  var wasCalled = false;
    +  function called() {
    +    wasCalled = true;
    +  }
    +  var element = goog.dom.getElement('button');
    +  goog.events.listen(element, goog.events.EventType.KEYPRESS, called);
    +  // Listen for 'Enter' on the button.
    +  // This tests using a dialog with no ButtonSet that has been set. Uses
    +  // a custom button.  The callback should be called with no exception thrown.
    +  goog.testing.events.fireKeySequence(element, goog.events.KeyCodes.ENTER);
    +  assertTrue('Should have gotten event on the button.', wasCalled);
    +}
    +
    +function testEnterKeyWithoutDefaultDoesNotPreventPropagation() {
    +  var buttons = new goog.ui.Dialog.ButtonSet();
    +  buttons.set(goog.ui.Dialog.DefaultButtonKeys.CANCEL,
    +      'Foo!',
    +      false);
    +  // Set a button set without a default selected button
    +  dialog.setButtonSet(buttons);
    +  dialog.setContent('<span id="linkel" tabindex="0">Link Span</span>');
    +
    +  var call = false;
    +  function called() {
    +    call = true;
    +  }
    +  var element = document.getElementById('linkel');
    +  goog.events.listen(element, goog.events.EventType.KEYDOWN, called);
    +  goog.testing.events.fireKeySequence(element, goog.events.KeyCodes.ENTER);
    +
    +  assertTrue('Should have gotten event on the link', call);
    +}
    +
    +function testPreventDefaultedSelectCausesStopPropagation() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK_CANCEL);
    +
    +  var callCount = 0;
    +  var keypressCount = 0;
    +  var keydownCount = 0;
    +
    +  var preventDefaulter = function(e) {
    +    e.preventDefault();
    +  };
    +
    +  goog.events.listen(
    +      dialog, goog.ui.Dialog.EventType.SELECT, preventDefaulter);
    +  goog.events.listen(
    +      document.body, goog.events.EventType.KEYPRESS, function() {
    +        keypressCount++;
    +      });
    +  goog.events.listen(
    +      document.body, goog.events.EventType.KEYDOWN, function() {
    +        keydownCount++;
    +      });
    +
    +  // Ensure that if the SELECT event is prevented, all key events
    +  // are still stopped from propagating.
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.ENTER);
    +  assertEquals('The KEYPRESS should be stopped', 0, keypressCount);
    +  assertEquals('The KEYDOWN should not be stopped', 1, keydownCount);
    +
    +  keypressCount = 0;
    +  keydownCount = 0;
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.ESC);
    +  assertEquals('The KEYDOWN should be stopped', 0, keydownCount);
    +  // Note: Some browsers don't yield keypresses on escape, so don't check.
    +
    +  goog.events.unlisten(
    +      dialog, goog.ui.Dialog.EventType.SELECT, preventDefaulter);
    +
    +  keypressCount = 0;
    +  keydownCount = 0;
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.ENTER);
    +  assertEquals('The KEYPRESS should be stopped', 0, keypressCount);
    +  assertEquals('The KEYDOWN should not be stopped', 1, keydownCount);
    +}
    +
    +function testEnterKeyHandledInKeypress() {
    +  var inKeyPress = false;
    +  goog.events.listen(
    +      document.body, goog.events.EventType.KEYPRESS,
    +      function() {
    +        inKeyPress = true;
    +      }, true /* capture */);
    +  goog.events.listen(
    +      document.body, goog.events.EventType.KEYPRESS,
    +      function() {
    +        inKeyPress = false;
    +      }, false /* !capture */);
    +  var selectCalled = false;
    +  goog.events.listen(
    +      dialog, goog.ui.Dialog.EventType.SELECT, function() {
    +        selectCalled = true;
    +        assertTrue(
    +            'Select must be dispatched during keypress to allow popups',
    +            inKeyPress);
    +      });
    +
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.ENTER);
    +  assertTrue(selectCalled);
    +}
    +
    +function testShiftTabAtTopSetsUpWrapAndDoesNotPreventPropagation() {
    +  dialog.setupBackwardTabWrap = goog.testing.recordFunction();
    +  shiftTabRecorder = goog.testing.recordFunction();
    +
    +  goog.events.listen(
    +      dialog.getElement(), goog.events.EventType.KEYDOWN, shiftTabRecorder);
    +  var shiftProperties = { shiftKey: true };
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.TAB, shiftProperties);
    +
    +  assertNotNull('Should have gotten event on Shift+TAB',
    +      shiftTabRecorder.getLastCall());
    +  assertNotNull('Backward tab wrap should have been set up',
    +      dialog.setupBackwardTabWrap.getLastCall());
    +}
    +
    +function testButtonsWithContentsDispatchSelectEvents() {
    +  var aButton = dialog.getButtonElement().getElementsByTagName('BUTTON')[0];
    +  var aSpan = document.createElement('SPAN');
    +  aButton.appendChild(aSpan);
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT, callRecorder);
    +  goog.testing.events.fireClickSequence(aSpan);
    +  assertTrue(wasCalled);
    +}
    +
    +function testAfterHideEvent() {
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.AFTER_HIDE,
    +      callRecorder);
    +  dialog.setVisible(false);
    +  assertTrue(wasCalled);
    +}
    +
    +function testAfterShowEvent() {
    +  dialog.setVisible(false);
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.AFTER_SHOW,
    +      callRecorder);
    +  dialog.setVisible(true);
    +  assertTrue(wasCalled);
    +}
    +
    +function testCannedButtonSets() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.OK]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK_CANCEL);
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.OK,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.YES_NO);
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.YES,
    +                 goog.ui.Dialog.DefaultButtonKeys.NO]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.YES_NO_CANCEL);
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.YES,
    +                 goog.ui.Dialog.DefaultButtonKeys.NO,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.CONTINUE_SAVE_CANCEL);
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.CONTINUE,
    +                 goog.ui.Dialog.DefaultButtonKeys.SAVE,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +}
    +
    +function testFactoryButtonSets() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.createOk());
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.OK]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.createOkCancel());
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.OK,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.createYesNo());
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.YES,
    +                 goog.ui.Dialog.DefaultButtonKeys.NO]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.createYesNoCancel());
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.YES,
    +                 goog.ui.Dialog.DefaultButtonKeys.NO,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.createContinueSaveCancel());
    +  assertButtons([goog.ui.Dialog.DefaultButtonKeys.CONTINUE,
    +                 goog.ui.Dialog.DefaultButtonKeys.SAVE,
    +                 goog.ui.Dialog.DefaultButtonKeys.CANCEL]);
    +}
    +
    +function testDefaultButtonClassName() {
    +  var key = 'someKey';
    +  var msg = 'someMessage';
    +  var isDefault = false;
    +  var buttonSetOne = new goog.ui.Dialog.ButtonSet().set(key, msg, isDefault);
    +  dialog.setButtonSet(buttonSetOne);
    +  var defaultClassName = goog.getCssName(buttonSetOne.class_, 'default');
    +  var buttonOne = buttonSetOne.getButton(key);
    +  assertNotEquals(defaultClassName, buttonOne.className);
    +  var isDefault = true;
    +  var buttonSetTwo = new goog.ui.Dialog.ButtonSet().set(key, msg, isDefault);
    +  dialog.setButtonSet(buttonSetTwo);
    +  var buttonTwo = buttonSetTwo.getButton(key);
    +  assertEquals(defaultClassName, buttonTwo.className);
    +}
    +
    +function testGetButton() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  var buttons = document.getElementsByName(
    +      goog.ui.Dialog.DefaultButtonKeys.OK);
    +  assertEquals(buttons[0], dialog.getButtonSet().getButton(
    +      goog.ui.Dialog.DefaultButtonKeys.OK));
    +}
    +
    +function testGetAllButtons() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.YES_NO_CANCEL);
    +  var buttons = dialog.getElement().getElementsByTagName(
    +      goog.dom.TagName.BUTTON);
    +  for (var i = 0; i < buttons.length; i++) {
    +    assertEquals(buttons[i], dialog.getButtonSet().getAllButtons()[i]);
    +  }
    +}
    +
    +function testSetButtonEnabled() {
    +  var buttonSet = goog.ui.Dialog.ButtonSet.createYesNoCancel();
    +  dialog.setButtonSet(buttonSet);
    +  assertFalse(
    +      buttonSet.getButton(goog.ui.Dialog.DefaultButtonKeys.NO).disabled);
    +  buttonSet.setButtonEnabled(goog.ui.Dialog.DefaultButtonKeys.NO, false);
    +  assertTrue(
    +      buttonSet.getButton(goog.ui.Dialog.DefaultButtonKeys.NO).disabled);
    +  buttonSet.setButtonEnabled(goog.ui.Dialog.DefaultButtonKeys.NO, true);
    +  assertFalse(
    +      buttonSet.getButton(goog.ui.Dialog.DefaultButtonKeys.NO).disabled);
    +}
    +
    +function testSetAllButtonsEnabled() {
    +  var buttonSet = goog.ui.Dialog.ButtonSet.createContinueSaveCancel();
    +  dialog.setButtonSet(buttonSet);
    +  var buttons = buttonSet.getAllButtons();
    +  for (var i = 0; i < buttons.length; i++) {
    +    assertFalse(buttons[i].disabled);
    +  }
    +
    +  buttonSet.setAllButtonsEnabled(false);
    +  for (var i = 0; i < buttons.length; i++) {
    +    assertTrue(buttons[i].disabled);
    +  }
    +
    +  buttonSet.setAllButtonsEnabled(true);
    +  for (var i = 0; i < buttons.length; i++) {
    +    assertFalse(buttons[i].disabled);
    +  }
    +}
    +
    +function testIframeMask() {
    +  var prevNumFrames =
    +      goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.IFRAME).length;
    +  // generate a new dialog
    +  dialog.dispose();
    +  dialog = new goog.ui.Dialog(null, true /* iframe mask */);
    +  dialog.setVisible(true);
    +
    +  // Test that the dialog added one iframe to the document.
    +  // The absolute number of iframes should not be tested because,
    +  // in certain cases, the test runner itself can can add an iframe
    +  // to the document as part of a strategy not to block the UI for too long.
    +  // See goog.async.nextTick.getSetImmediateEmulator_.
    +  var curNumFrames =
    +      goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.IFRAME).length;
    +  assertEquals(
    +      'No iframe mask created', prevNumFrames + 1, curNumFrames);
    +}
    +
    +function testNonModalDialog() {
    +  var prevNumFrames =
    +      goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.IFRAME).length;
    +  // generate a new dialog
    +  dialog.dispose();
    +  dialog = new goog.ui.Dialog(null, true /* iframe mask */);
    +  dialog.setModal(false);
    +  assertAriaHidden(false);
    +  dialog.setVisible(true);
    +  assertAriaHidden(true);
    +
    +  // Test that the dialog did not change the number of iframes in the document.
    +  // The absolute number of iframes should not be tested because,
    +  // in certain cases, the test runner itself can can add an iframe
    +  // to the document as part of a strategy not to block the UI for too long.
    +  // See goog.async.nextTick.getSetImmediateEmulator_.
    +  var curNumFrames =
    +      goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.IFRAME).length;
    +  assertEquals(
    +      'Iframe mask created for modal dialog', prevNumFrames, curNumFrames);
    +}
    +
    +function testSwapModalForOpenDialog() {
    +  dialog.dispose();
    +  dialog = new goog.ui.Dialog(null, true /* iframe mask */);
    +  assertAriaHidden(false);
    +  dialog.setVisible(true);
    +  assertAriaHidden(true);
    +  dialog.setModal(false);
    +  assertAriaHidden(false);
    +  assertFalse('IFrame bg element should not be in dom',
    +      goog.dom.contains(document.body, dialog.getBackgroundIframe()));
    +  assertFalse('bg element should not be in dom',
    +      goog.dom.contains(document.body, dialog.getBackgroundElement()));
    +
    +  dialog.setModal(true);
    +  assertAriaHidden(true);
    +  assertTrue('IFrame bg element should be in dom',
    +      goog.dom.contains(document.body, dialog.getBackgroundIframe()));
    +  assertTrue('bg element should be in dom',
    +      goog.dom.contains(document.body, dialog.getBackgroundElement()));
    +
    +  assertEquals('IFrame bg element is a child of body',
    +      document.body, dialog.getBackgroundIframe().parentNode);
    +  assertEquals('bg element is a child of body',
    +      document.body, dialog.getBackgroundElement().parentNode);
    +
    +  assertTrue('IFrame bg element should visible',
    +      goog.style.isElementShown(dialog.getBackgroundIframe()));
    +  assertTrue('bg element should be visible',
    +      goog.style.isElementShown(dialog.getBackgroundElement()));
    +}
    +
    +
    +/**
    + * Assert that the dialog has buttons with the given keys in the correct
    + * order.
    + * @param {Array<string>} keys An array of button keys.
    + */
    +function assertButtons(keys) {
    +  var buttons = dialog.getElement().getElementsByTagName(
    +      goog.dom.TagName.BUTTON);
    +  var actualKeys = [];
    +  for (var i = 0; i < buttons.length; i++) {
    +    actualKeys[i] = buttons[i].name;
    +  }
    +  assertArrayEquals(keys, actualKeys);
    +}
    +
    +function testButtonSetOkFiresDialogEventOnEscape() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  var wasCalled = false;
    +  var callRecorder = function() { wasCalled = true; };
    +  goog.events.listen(dialog, goog.ui.Dialog.EventType.SELECT,
    +      callRecorder);
    +  goog.testing.events.fireKeySequence(
    +      dialog.getElement(), goog.events.KeyCodes.ESC);
    +  assertTrue(wasCalled);
    +}
    +
    +function testHideButtons_afterRender() {
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  assertTrue(goog.style.isElementShown(dialog.buttonEl_));
    +  dialog.setButtonSet(null);
    +  assertFalse(goog.style.isElementShown(dialog.buttonEl_));
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  assertTrue(goog.style.isElementShown(dialog.buttonEl_));
    +}
    +
    +function testHideButtons_beforeRender() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.setButtonSet(null);
    +  dialog.setVisible(true);
    +  assertFalse(goog.style.isElementShown(dialog.buttonEl_));
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  assertTrue(goog.style.isElementShown(dialog.buttonEl_));
    +}
    +
    +function testHideButtons_beforeDecorate() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.setButtonSet(null);
    +  dialog.decorate(decorateTarget);
    +  dialog.setVisible(true);
    +  assertFalse(goog.style.isElementShown(dialog.buttonEl_));
    +  dialog.setButtonSet(goog.ui.Dialog.ButtonSet.OK);
    +  assertTrue(goog.style.isElementShown(dialog.buttonEl_));
    +}
    +
    +function testAriaLabelledBy_render() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.render();
    +  assertTrue(!!dialog.getTitleTextElement().id);
    +  assertNotNull(dialog.getElement());
    +  assertEquals(dialog.getTitleTextElement().id,
    +      goog.a11y.aria.getState(dialog.getElement(),
    +          'labelledby'));
    +}
    +
    +function testAriaLabelledBy_decorate() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.decorate(decorateTarget);
    +  dialog.setVisible(true);
    +  assertTrue(!!dialog.getTitleTextElement().id);
    +  assertNotNull(dialog.getElement());
    +  assertEquals(dialog.getTitleTextElement().id,
    +      goog.a11y.aria.getState(dialog.getElement(),
    +          'labelledby'));
    +}
    +
    +
    +function testPreferredAriaRole_renderDefault() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.render();
    +  assertNotNull(dialog.getElement());
    +  assertEquals(dialog.getPreferredAriaRole(),
    +      goog.a11y.aria.getRole(dialog.getElement()));
    +}
    +
    +function testPreferredAriaRole_decorateDefault() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.decorate(decorateTarget);
    +  assertNotNull(dialog.getElement());
    +  assertEquals(dialog.getPreferredAriaRole(),
    +      goog.a11y.aria.getRole(dialog.getElement()));
    +}
    +
    +function testPreferredAriaRole_renderOverride() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.setPreferredAriaRole(goog.a11y.aria.Role.ALERTDIALOG);
    +  dialog.render();
    +  assertNotNull(dialog.getElement());
    +  assertEquals(goog.a11y.aria.Role.ALERTDIALOG,
    +      goog.a11y.aria.getRole(dialog.getElement()));
    +}
    +
    +function testPreferredAriaRole_decorateOverride() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.setPreferredAriaRole(goog.a11y.aria.Role.ALERTDIALOG);
    +  dialog.decorate(decorateTarget);
    +  assertNotNull(dialog.getElement());
    +  assertEquals(goog.a11y.aria.Role.ALERTDIALOG,
    +      goog.a11y.aria.getRole(dialog.getElement()));
    +}
    +
    +function testDefaultOpacityIsAppliedOnRender() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.render();
    +  assertEquals(0.5, goog.style.getOpacity(dialog.getBackgroundElement()));
    +}
    +
    +function testDefaultOpacityIsAppliedOnDecorate() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.decorate(decorateTarget);
    +  assertEquals(0.5, goog.style.getOpacity(dialog.getBackgroundElement()));
    +}
    +
    +function testDraggableStyle() {
    +  assertTrue('draggable CSS class is set', goog.dom.classlist.contains(
    +      dialog.titleEl_, 'modal-dialog-title-draggable'));
    +  dialog.setDraggable(false);
    +  assertFalse('draggable CSS class is removed', goog.dom.classlist.contains(
    +      dialog.titleEl_, 'modal-dialog-title-draggable'));
    +}
    +
    +function testDraggingLifecycle() {
    +  dialog.dispose();
    +
    +  dialog = new goog.ui.Dialog();
    +  dialog.setDraggerLimits_ = goog.testing.recordFunction();
    +  dialog.createDom();
    +  assertNull('dragger is not created in createDom', dialog.dragger_);
    +
    +  dialog.setVisible(true);
    +  assertNotNull('dragger is created when the dialog is rendered',
    +      dialog.dragger_);
    +
    +  assertNull('dragging limits are not set just before dragging',
    +      dialog.setDraggerLimits_.getLastCall());
    +  goog.testing.events.fireMouseDownEvent(dialog.titleEl_);
    +  assertNotNull('dragging limits are set',
    +      dialog.setDraggerLimits_.getLastCall());
    +
    +  dialog.exitDocument();
    +  assertNull('dragger is cleaned up in exitDocument', dialog.dragger_);
    +}
    +
    +function testDisposingVisibleDialogWithTransitionsDoesNotThrowException() {
    +  var transition = goog.fx.css3.fadeIn(dialog.getElement(),
    +      0.1 /* duration */);
    +
    +  dialog.setTransition(transition, transition, transition, transition);
    +  dialog.setVisible(true);
    +  dialog.dispose();
    +  // Nothing to assert. We only want to ensure that there is no error.
    +}
    +
    +function testEventsDuringAnimation() {
    +  dialog.dispose();
    +  dialog = new goog.ui.Dialog();
    +  dialog.render();
    +  dialog.setTransition(
    +      goog.fx.css3.fadeIn(dialog.getElement(), 1),
    +      goog.fx.css3.fadeIn(dialog.getBackgroundElement(), 1),
    +      goog.fx.css3.fadeOut(dialog.getElement(), 1),
    +      goog.fx.css3.fadeOut(dialog.getBackgroundElement(), 1));
    +  dialog.setVisible(true);
    +  assertTrue(dialog.isVisible());
    +
    +  var buttonSet = dialog.getButtonSet();
    +  var button = buttonSet.getButton(buttonSet.getDefault());
    +
    +  // The button event fires while the animation is still going.
    +  goog.testing.events.fireClickSequence(button);
    +  mockClock.tick(2000);
    +  assertFalse(dialog.isVisible());
    +}
    +
    +function testHtmlContent() {
    +  dialog.setSafeHtmlContent(goog.html.testing.newSafeHtmlForTest(
    +      '<span class="theSpan">Hello</span>'));
    +  var spanEl =
    +      goog.dom.getElementByClass('theSpan', dialog.getContentElement());
    +  assertEquals('Hello', goog.dom.getTextContent(spanEl));
    +  assertEquals('<span class="theSpan">Hello</span>', dialog.getContent());
    +  assertEquals('<span class="theSpan">Hello</span>',
    +               goog.html.SafeHtml.unwrap(dialog.getSafeHtmlContent()));
    +}
    +
    +function testSetContent_guardedByGlobalFlag() {
    +  /** @suppress {missingRequire} */
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', false);
    +  assertEquals(
    +      'Error: Legacy conversion from string to goog.html types is disabled',
    +      assertThrows(function() {
    +        dialog.setContent('<img src="blag" onerror="evil();">');
    +      }).message);
    +}
    +
    +function testFocus() {
    +  // Focus should go to the dialog element.
    +  document.body.focus();
    +  dialog.focus();
    +  assertEquals(dialog.getElement(), document.activeElement);
    +}
    +
    +// Asserts that a test element which is a child of the document body has the
    +// aria property 'hidden' set on it, or not.
    +function assertAriaHidden(expectedHidden) {
    +  var expectedString = expectedHidden ? 'true' : '';
    +  assertEquals(expectedString,
    +      goog.a11y.aria.getState(bodyChildElement,
    +          goog.a11y.aria.State.HIDDEN));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker.js b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker.js
    new file mode 100644
    index 0000000000..81ff25c41b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker.js
    @@ -0,0 +1,318 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A dimension picker control.  A dimension picker allows the
    + * user to visually select a row and column count.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + * @see ../demos/dimensionpicker.html
    + * @see ../demos/dimensionpicker_rtl.html
    + */
    +
    +goog.provide('goog.ui.DimensionPicker');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Size');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.DimensionPickerRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A dimension picker allows the user to visually select a row and column
    + * count using their mouse and keyboard.
    + *
    + * The currently selected dimension is controlled by an ACTION event.  Event
    + * listeners may retrieve the selected item using the
    + * {@link #getValue} method.
    + *
    + * @param {goog.ui.DimensionPickerRenderer=} opt_renderer Renderer used to
    + *     render or decorate the palette; defaults to
    + *     {@link goog.ui.DimensionPickerRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + * @final
    + */
    +goog.ui.DimensionPicker = function(opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, null,
    +      opt_renderer || goog.ui.DimensionPickerRenderer.getInstance(),
    +      opt_domHelper);
    +
    +  this.size_ = new goog.math.Size(this.minColumns, this.minRows);
    +};
    +goog.inherits(goog.ui.DimensionPicker, goog.ui.Control);
    +
    +
    +/**
    + * Minimum number of columns to show in the grid.
    + * @type {number}
    + */
    +goog.ui.DimensionPicker.prototype.minColumns = 5;
    +
    +
    +/**
    + * Minimum number of rows to show in the grid.
    + * @type {number}
    + */
    +goog.ui.DimensionPicker.prototype.minRows = 5;
    +
    +
    +/**
    + * Maximum number of columns to show in the grid.
    + * @type {number}
    + */
    +goog.ui.DimensionPicker.prototype.maxColumns = 20;
    +
    +
    +/**
    + * Maximum number of rows to show in the grid.
    + * @type {number}
    + */
    +goog.ui.DimensionPicker.prototype.maxRows = 20;
    +
    +
    +/**
    + * Palette dimensions (columns x rows).
    + * @type {goog.math.Size}
    + * @private
    + */
    +goog.ui.DimensionPicker.prototype.size_;
    +
    +
    +/**
    + * Currently highlighted row count.
    + * @type {number}
    + * @private
    + */
    +goog.ui.DimensionPicker.prototype.highlightedRows_ = 1;
    +
    +
    +/**
    + * Currently highlighted column count.
    + * @type {number}
    + * @private
    + */
    +goog.ui.DimensionPicker.prototype.highlightedColumns_ = 1;
    +
    +
    +/** @override */
    +goog.ui.DimensionPicker.prototype.enterDocument = function() {
    +  goog.ui.DimensionPicker.superClass_.enterDocument.call(this);
    +
    +  var handler = this.getHandler();
    +  handler.
    +      listen(this.getRenderer().getMouseMoveElement(this),
    +          goog.events.EventType.MOUSEMOVE, this.handleMouseMove).
    +      listen(this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
    +          this.handleWindowResize);
    +
    +  var parent = this.getParent();
    +  if (parent) {
    +    handler.listen(parent, goog.ui.Component.EventType.SHOW, this.handleShow_);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.DimensionPicker.prototype.exitDocument = function() {
    +  goog.ui.DimensionPicker.superClass_.exitDocument.call(this);
    +
    +  var handler = this.getHandler();
    +  handler.
    +      unlisten(this.getRenderer().getMouseMoveElement(this),
    +          goog.events.EventType.MOUSEMOVE, this.handleMouseMove).
    +      unlisten(this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
    +          this.handleWindowResize);
    +
    +  var parent = this.getParent();
    +  if (parent) {
    +    handler.unlisten(parent, goog.ui.Component.EventType.SHOW,
    +        this.handleShow_);
    +  }
    +};
    +
    +
    +/**
    + * Resets the highlighted size when the picker is shown.
    + * @private
    + */
    +goog.ui.DimensionPicker.prototype.handleShow_ = function() {
    +  if (this.isVisible()) {
    +    this.setValue(1, 1);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.DimensionPicker.prototype.disposeInternal = function() {
    +  goog.ui.DimensionPicker.superClass_.disposeInternal.call(this);
    +  delete this.size_;
    +};
    +
    +
    +// Palette event handling.
    +
    +
    +/**
    + * Handles mousemove events.  Determines which palette size was moused over and
    + * highlights it.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + * @protected
    + */
    +goog.ui.DimensionPicker.prototype.handleMouseMove = function(e) {
    +  var highlightedSizeX = this.getRenderer().getGridOffsetX(this,
    +      this.isRightToLeft() ? e.target.offsetWidth - e.offsetX : e.offsetX);
    +  var highlightedSizeY = this.getRenderer().getGridOffsetY(this, e.offsetY);
    +
    +  this.setValue(highlightedSizeX, highlightedSizeY);
    +};
    +
    +
    +/**
    + * Handles window resize events.  Ensures no scrollbars are introduced by the
    + * renderer's mouse catcher.
    + * @param {goog.events.Event} e Resize event to handle.
    + * @protected
    + */
    +goog.ui.DimensionPicker.prototype.handleWindowResize = function(e) {
    +  this.getRenderer().positionMouseCatcher(this);
    +};
    +
    +
    +/**
    + * Handle key events if supported, so the user can use the keyboard to
    + * manipulate the highlighted rows and columns.
    + * @param {goog.events.KeyEvent} e The key event object.
    + * @return {boolean} Whether the key event was handled.
    + * @override
    + */
    +goog.ui.DimensionPicker.prototype.handleKeyEvent = function(e) {
    +  var rows = this.highlightedRows_;
    +  var columns = this.highlightedColumns_;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.DOWN:
    +      rows++;
    +      break;
    +    case goog.events.KeyCodes.UP:
    +      rows--;
    +      break;
    +    case goog.events.KeyCodes.LEFT:
    +      if (this.isRightToLeft()) {
    +        columns++;
    +      } else {
    +        if (columns == 1) {
    +          // Delegate to parent.
    +          return false;
    +        } else {
    +          columns--;
    +        }
    +      }
    +      break;
    +    case goog.events.KeyCodes.RIGHT:
    +      if (this.isRightToLeft()) {
    +        if (columns == 1) {
    +          // Delegate to parent.
    +          return false;
    +        } else {
    +          columns--;
    +        }
    +      } else {
    +        columns++;
    +      }
    +      break;
    +    default:
    +      return goog.ui.DimensionPicker.superClass_.handleKeyEvent.call(this, e);
    +  }
    +  this.setValue(columns, rows);
    +  return true;
    +};
    +
    +
    +// Palette management.
    +
    +
    +/**
    + * @return {goog.math.Size} Current table size shown (columns x rows).
    + */
    +goog.ui.DimensionPicker.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * @return {!goog.math.Size} size The currently highlighted dimensions.
    + */
    +goog.ui.DimensionPicker.prototype.getValue = function() {
    +  return new goog.math.Size(this.highlightedColumns_, this.highlightedRows_);
    +};
    +
    +
    +/**
    + * Sets the currently highlighted dimensions. If the dimensions are not valid
    + * (not between 1 and the maximum number of columns/rows to show), they will
    + * be changed to the closest valid value.
    + * @param {(number|!goog.math.Size)} columns The number of columns to highlight,
    + *     or a goog.math.Size object containing both.
    + * @param {number=} opt_rows The number of rows to highlight.  Can be
    + *     omitted when columns is a good.math.Size object.
    + */
    +goog.ui.DimensionPicker.prototype.setValue = function(columns,
    +    opt_rows) {
    +  if (!goog.isDef(opt_rows)) {
    +    columns = /** @type {!goog.math.Size} */ (columns);
    +    opt_rows = columns.height;
    +    columns = columns.width;
    +  } else {
    +    columns = /** @type {number} */ (columns);
    +  }
    +
    +  // Ensure that the row and column values are within the minimum value (1) and
    +  // maxmimum values.
    +  columns = Math.max(1, columns);
    +  opt_rows = Math.max(1, opt_rows);
    +  columns = Math.min(this.maxColumns, columns);
    +  opt_rows = Math.min(this.maxRows, opt_rows);
    +
    +  if (this.highlightedColumns_ != columns ||
    +      this.highlightedRows_ != opt_rows) {
    +    var renderer = this.getRenderer();
    +    // Show one more row/column than highlighted so the user understands the
    +    // palette can grow.
    +    this.size_.width = Math.max(
    +        Math.min(columns + 1, this.maxColumns), this.minColumns);
    +    this.size_.height = Math.max(
    +        Math.min(opt_rows + 1, this.maxRows), this.minRows);
    +    renderer.updateSize(this, this.getElement());
    +
    +    this.highlightedColumns_ = columns;
    +    this.highlightedRows_ = opt_rows;
    +    renderer.setHighlightedSize(this, columns, opt_rows);
    +  }
    +};
    +
    +
    +/**
    + * Register this control so it can be created from markup
    + */
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.DimensionPickerRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.DimensionPicker();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.html
    new file mode 100644
    index 0000000000..038626d126
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: robbyw@google.com (Robby Walker)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.DimensionPicker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.DimensionPickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="render">
    +  </div>
    +  <div id="decorate">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.js
    new file mode 100644
    index 0000000000..bb3cbe3d88
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpicker_test.js
    @@ -0,0 +1,249 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.DimensionPickerTest');
    +goog.setTestOnly('goog.ui.DimensionPickerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Size');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.DimensionPicker');
    +goog.require('goog.ui.DimensionPickerRenderer');
    +
    +var picker;
    +var render;
    +var decorate;
    +
    +function setUpPage() {
    +  render = goog.dom.getElement('render');
    +  decorate = goog.dom.getElement('decorate');
    +}
    +
    +function setUp() {
    +  picker = new goog.ui.DimensionPicker();
    +  render.innerHTML = '';
    +  decorate.innerHTML = '';
    +}
    +
    +function tearDown() {
    +  picker.dispose();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Should have successful construction', picker);
    +  assertNull('Should not be in document', picker.getElement());
    +}
    +
    +function testRender() {
    +  picker.render(render);
    +
    +  assertEquals('Should create 1 child', 1, render.childNodes.length);
    +  assertEquals('Should be a div', goog.dom.TagName.DIV,
    +      render.firstChild.tagName);
    +}
    +
    +function testDecorate() {
    +  picker.decorate(decorate);
    +
    +  assertNotEquals('Should add several children', decorate.firstChild,
    +      decorate.lastChild);
    +}
    +
    +function testHighlightedSize() {
    +  picker.render(render);
    +
    +  var size = picker.getValue();
    +  assertEquals('Should have 1 column highlighted initially.',
    +      1, size.width);
    +  assertEquals('Should have 1 row highlighted initially.',
    +      1, size.height);
    +
    +  picker.setValue(1, 2);
    +  size = picker.getValue();
    +  assertEquals('Should have 1 column highlighted.', 1, size.width);
    +  assertEquals('Should have 2 rows highlighted.', 2, size.height);
    +
    +  picker.setValue(new goog.math.Size(3, 4));
    +  size = picker.getValue();
    +  assertEquals('Should have 3 columns highlighted.', 3, size.width);
    +  assertEquals('Should have 4 rows highlighted.', 4, size.height);
    +
    +  picker.setValue(new goog.math.Size(-3, 0));
    +  size = picker.getValue();
    +  assertEquals('Should have 1 column highlighted when passed a negative ' +
    +      'column value.', 1, size.width);
    +  assertEquals('Should have 1 row highlighted when passed 0 as the row ' +
    +      'value.', 1, size.height);
    +
    +  picker.setValue(picker.maxColumns + 10, picker.maxRows + 2);
    +  size = picker.getValue();
    +  assertEquals('Column value should be decreased to match max columns ' +
    +      'if it is too high.', picker.maxColumns, size.width);
    +  assertEquals('Row value should be decreased to match max rows ' +
    +      'if it is too high.', picker.maxRows, size.height);
    +}
    +
    +function testSizeShown() {
    +  picker.render(render);
    +
    +  var size = picker.getSize();
    +  assertEquals('Should have 5 columns visible', 5, size.width);
    +  assertEquals('Should have 5 rows visible', 5, size.height);
    +
    +  picker.setValue(4, 4);
    +  size = picker.getSize();
    +  assertEquals('Should have 5 columns visible', 5, size.width);
    +  assertEquals('Should have 5 rows visible', 5, size.height);
    +
    +  picker.setValue(12, 13);
    +  size = picker.getSize();
    +  assertEquals('Should have 13 columns visible', 13, size.width);
    +  assertEquals('Should have 14 rows visible', 14, size.height);
    +
    +  picker.setValue(20, 20);
    +  size = picker.getSize();
    +  assertEquals('Should have 20 columns visible', 20, size.width);
    +  assertEquals('Should have 20 rows visible', 20, size.height);
    +
    +  picker.setValue(2, 3);
    +  size = picker.getSize();
    +  assertEquals('Should have 5 columns visible', 5, size.width);
    +  assertEquals('Should have 5 rows visible', 5, size.height);
    +}
    +
    +function testHandleMove() {
    +  picker.render(render);
    +  var renderer = picker.getRenderer();
    +  var mouseMoveElem = renderer.getMouseMoveElement(picker);
    +
    +  picker.rightToLeft_ = false;
    +  var e = {
    +    target: mouseMoveElem,
    +    offsetX: 18, // Each grid square currently a magic 18px.
    +    offsetY: 36
    +  };
    +
    +  picker.handleMouseMove(e);
    +  var size = picker.getValue();
    +  assertEquals('Should have 1 column highlighted', 1, size.width);
    +  assertEquals('Should have 2 rows highlighted', 2, size.height);
    +
    +  picker.rightToLeft_ = true;
    +
    +  picker.handleMouseMove(e);
    +  var size = picker.getValue();
    +  // In RTL we pick from the right side of the picker, so an offsetX of 0
    +  // would actually mean select all columns.
    +  assertEquals('Should have columns to the right of the mouse highlighted',
    +      Math.ceil((mouseMoveElem.offsetWidth - e.offsetX) / 18), size.width);
    +  assertEquals('Should have 2 rows highlighted', 2, size.height);
    +}
    +
    +function testHandleKeyboardEvents() {
    +  picker.render(render);
    +
    +  picker.rightToLeft_ = false;
    +
    +  var result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.DOWN});
    +  var size = picker.getValue();
    +  assertEquals('Should have 1 column highlighted', 1, size.width);
    +  assertEquals('Should have 2 rows highlighted', 2, size.height);
    +  assertTrue('Should handle DOWN key event', result);
    +
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.RIGHT});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 2, size.width);
    +  assertEquals('Should have 2 rows highlighted', 2, size.height);
    +  assertTrue('Should handle RIGHT key event', result);
    +
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.UP});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 2, size.width);
    +  assertEquals('Should have 1 rows highlighted', 1, size.height);
    +  assertTrue('Should handle UP key event', result);
    +
    +  // Pressing UP when there is only 1 row should be handled but has no
    +  // effect.
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.UP});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 2, size.width);
    +  assertEquals('Should have 1 rows highlighted', 1, size.height);
    +  assertTrue('Should handle UP key event', result);
    +
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.LEFT});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 1, size.width);
    +  assertEquals('Should have 1 rows highlighted', 1, size.height);
    +  assertTrue('Should handle LEFT key event', result);
    +
    +  // Pressing LEFT when there is only 1 row should not be handled
    +  //allowing SubMenu to close.
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.LEFT});
    +  assertFalse(
    +      'Should not handle LEFT key event when there is only one column',
    +      result);
    +
    +  picker.rightToLeft_ = true;
    +
    +  // In RTL the roles of the LEFT and RIGHT keys are swapped.
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.LEFT});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 2, size.width);
    +  assertEquals('Should have 2 rows highlighted', 1, size.height);
    +  assertTrue('Should handle LEFT key event', result);
    +
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.RIGHT});
    +  size = picker.getValue();
    +  assertEquals('Should have 2 column highlighted', 1, size.width);
    +  assertEquals('Should have 1 rows highlighted', 1, size.height);
    +  assertTrue('Should handle RIGHT key event', result);
    +
    +  result = picker.handleKeyEvent({keyCode: goog.events.KeyCodes.RIGHT});
    +  assertFalse(
    +      'Should not handle RIGHT key event when there is only one column',
    +      result);
    +}
    +
    +function testDispose() {
    +  var element = picker.getElement();
    +  picker.render(render);
    +  picker.dispose();
    +  assertTrue('Picker should have been disposed of', picker.isDisposed());
    +  assertNull('Picker element reference should have been nulled out',
    +      picker.getElement());
    +}
    +
    +function testRendererDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(
    +          goog.ui.DimensionPickerRenderer);
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Picker must not have aria label by default',
    +      picker.getAriaLabel());
    +  picker.setAriaLabel('My picker');
    +  picker.render(render);
    +  var element = picker.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Picker element must have expected aria-label', 'My picker',
    +      element.getAttribute('aria-label'));
    +  assertTrue(goog.dom.isFocusableTabIndex(element));
    +  picker.setAriaLabel('My new picker');
    +  assertEquals('Picker element must have updated aria-label', 'My new picker',
    +      element.getAttribute('aria-label'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer.js
    new file mode 100644
    index 0000000000..b0553dc24c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer.js
    @@ -0,0 +1,420 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview The default renderer for a goog.dom.DimensionPicker.  A
    + * dimension picker allows the user to visually select a row and column count.
    + * It looks like a palette but in order to minimize DOM load it is rendered.
    + * using CSS background tiling instead of as a grid of nodes.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.DimensionPickerRenderer');
    +
    +goog.require('goog.a11y.aria.Announcer');
    +goog.require('goog.a11y.aria.LivePriority');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.i18n.bidi');
    +goog.require('goog.style');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.DimensionPicker}s.  Renders the
    + * palette as two divs, one with the un-highlighted background, and one with the
    + * highlighted background.
    + *
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.DimensionPickerRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +
    +  /** @private {goog.a11y.aria.Announcer} */
    +  this.announcer_ = new goog.a11y.aria.Announcer();
    +};
    +goog.inherits(goog.ui.DimensionPickerRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.DimensionPickerRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.DimensionPickerRenderer.CSS_CLASS =
    +    goog.getCssName('goog-dimension-picker');
    +
    +
    +/**
    + * Return the underlying div for the given outer element.
    + * @param {Element} element The root element.
    + * @return {Element} The underlying div.
    + * @private
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getUnderlyingDiv_ = function(
    +    element) {
    +  return element.firstChild.childNodes[1];
    +};
    +
    +
    +/**
    + * Return the highlight div for the given outer element.
    + * @param {Element} element The root element.
    + * @return {Element} The highlight div.
    + * @private
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getHighlightDiv_ = function(
    +    element) {
    +  return /** @type {Element} */ (element.firstChild.lastChild);
    +};
    +
    +
    +/**
    + * Return the status message div for the given outer element.
    + * @param {Element} element The root element.
    + * @return {Element} The status message div.
    + * @private
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getStatusDiv_ = function(
    +    element) {
    +  return /** @type {Element} */ (element.lastChild);
    +};
    +
    +
    +/**
    + * Return the invisible mouse catching div for the given outer element.
    + * @param {Element} element The root element.
    + * @return {Element} The invisible mouse catching div.
    + * @private
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getMouseCatcher_ = function(
    +    element) {
    +  return /** @type {Element} */ (element.firstChild.firstChild);
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#canDecorate} to allow decorating
    + * empty DIVs only.
    + * @param {Element} element The element to check.
    + * @return {boolean} Whether if the element is an empty div.
    + * @override
    + */
    +goog.ui.DimensionPickerRenderer.prototype.canDecorate = function(
    +    element) {
    +  return element.tagName == goog.dom.TagName.DIV && !element.firstChild;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#decorate} to decorate empty DIVs.
    + * @param {goog.ui.Control} control goog.ui.DimensionPicker to decorate.
    + * @param {Element} element The element to decorate.
    + * @return {Element} The decorated element.
    + * @override
    + */
    +goog.ui.DimensionPickerRenderer.prototype.decorate = function(control,
    +    element) {
    +  var palette = /** @type {goog.ui.DimensionPicker} */ (control);
    +  goog.ui.DimensionPickerRenderer.superClass_.decorate.call(this,
    +      palette, element);
    +
    +  this.addElementContents_(palette, element);
    +  this.updateSize(palette, element);
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Scales various elements in order to update the palette's size.
    + * @param {goog.ui.DimensionPicker} palette The palette object.
    + * @param {Element} element The element to set the style of.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.updateSize =
    +    function(palette, element) {
    +  var size = palette.getSize();
    +
    +  element.style.width = size.width + 'em';
    +
    +  var underlyingDiv = this.getUnderlyingDiv_(element);
    +  underlyingDiv.style.width = size.width + 'em';
    +  underlyingDiv.style.height = size.height + 'em';
    +
    +  if (palette.isRightToLeft()) {
    +    this.adjustParentDirection_(palette, element);
    +  }
    +};
    +
    +
    +/**
    + * Adds the appropriate content elements to the given outer DIV.
    + * @param {goog.ui.DimensionPicker} palette The palette object.
    + * @param {Element} element The element to decorate.
    + * @private
    + */
    +goog.ui.DimensionPickerRenderer.prototype.addElementContents_ = function(
    +    palette, element) {
    +  // First we create a single div containing three stacked divs.  The bottom div
    +  // catches mouse events.  We can't use document level mouse move detection as
    +  // we could lose events to iframes.  This is especially important in Firefox 2
    +  // in which TrogEdit creates iframes. The middle div uses a css tiled
    +  // background image to represent deselected tiles.  The top div uses a
    +  // different css tiled background image to represent selected tiles.
    +  var mouseCatcherDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.getCssClass(), 'mousecatcher'));
    +  var unhighlightedDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
    +      {
    +        'class': goog.getCssName(this.getCssClass(), 'unhighlighted'),
    +        'style': 'width:100%;height:100%'
    +      });
    +  var highlightedDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.getCssClass(), 'highlighted'));
    +  element.appendChild(
    +      palette.getDomHelper().createDom(goog.dom.TagName.DIV,
    +          {'style': 'width:100%;height:100%'},
    +          mouseCatcherDiv, unhighlightedDiv, highlightedDiv));
    +
    +  // Lastly we add a div to store the text version of the current state.
    +  element.appendChild(palette.getDomHelper().createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.getCssClass(), 'status')));
    +};
    +
    +
    +/**
    + * Creates a div and adds the appropriate contents to it.
    + * @param {goog.ui.Control} control Picker to render.
    + * @return {!Element} Root element for the palette.
    + * @override
    + */
    +goog.ui.DimensionPickerRenderer.prototype.createDom = function(control) {
    +  var palette = /** @type {goog.ui.DimensionPicker} */ (control);
    +  var classNames = this.getClassNames(palette);
    +  // Hide the element from screen readers so they don't announce "1 of 1" for
    +  // the perceived number of items in the palette.
    +  var element = palette.getDomHelper().createDom(goog.dom.TagName.DIV, {
    +    'class': classNames ? classNames.join(' ') : '',
    +    'aria-hidden': 'true'
    +  });
    +  this.addElementContents_(palette, element);
    +  this.updateSize(palette, element);
    +  return element;
    +};
    +
    +
    +/**
    + * Initializes the control's DOM when the control enters the document.  Called
    + * from {@link goog.ui.Control#enterDocument}.
    + * @param {goog.ui.Control} control Palette whose DOM is to be
    + *     initialized as it enters the document.
    + * @override
    + */
    +goog.ui.DimensionPickerRenderer.prototype.initializeDom = function(
    +    control) {
    +  var palette = /** @type {goog.ui.DimensionPicker} */ (control);
    +  goog.ui.DimensionPickerRenderer.superClass_.initializeDom.call(this, palette);
    +
    +  // Make the displayed highlighted size match the dimension picker's value.
    +  var highlightedSize = palette.getValue();
    +  this.setHighlightedSize(palette,
    +      highlightedSize.width, highlightedSize.height);
    +
    +  this.positionMouseCatcher(palette);
    +};
    +
    +
    +/**
    + * Get the element to listen for mouse move events on.
    + * @param {goog.ui.DimensionPicker} palette The palette to listen on.
    + * @return {Element} The element to listen for mouse move events on.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getMouseMoveElement = function(
    +    palette) {
    +  return /** @type {Element} */ (palette.getElement().firstChild);
    +};
    +
    +
    +/**
    + * Returns the x offset in to the grid for the given mouse x position.
    + * @param {goog.ui.DimensionPicker} palette The table size palette.
    + * @param {number} x The mouse event x position.
    + * @return {number} The x offset in to the grid.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getGridOffsetX = function(
    +    palette, x) {
    +  // TODO(robbyw): Don't rely on magic 18 - measure each palette's em size.
    +  return Math.min(palette.maxColumns, Math.ceil(x / 18));
    +};
    +
    +
    +/**
    + * Returns the y offset in to the grid for the given mouse y position.
    + * @param {goog.ui.DimensionPicker} palette The table size palette.
    + * @param {number} y The mouse event y position.
    + * @return {number} The y offset in to the grid.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getGridOffsetY = function(
    +    palette, y) {
    +  return Math.min(palette.maxRows, Math.ceil(y / 18));
    +};
    +
    +
    +/**
    + * Sets the highlighted size. Does nothing if the palette hasn't been rendered.
    + * @param {goog.ui.DimensionPicker} palette The table size palette.
    + * @param {number} columns The number of columns to highlight.
    + * @param {number} rows The number of rows to highlight.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.setHighlightedSize = function(
    +    palette, columns, rows) {
    +  var element = palette.getElement();
    +  // Can't update anything if DimensionPicker hasn't been rendered.
    +  if (!element) {
    +    return;
    +  }
    +
    +  // Style the highlight div.
    +  var style = this.getHighlightDiv_(element).style;
    +  style.width = columns + 'em';
    +  style.height = rows + 'em';
    +
    +  // Explicitly set style.right so the element grows to the left when increase
    +  // in width.
    +  if (palette.isRightToLeft()) {
    +    style.right = '0';
    +  }
    +
    +  /**
    +   * @desc The dimension of the columns and rows currently selected in the
    +   * dimension picker, as text that can be spoken by a screen reader.
    +   */
    +  var MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS = goog.getMsg(
    +      '{$numCols} by {$numRows}',
    +      {'numCols': columns, 'numRows': rows});
    +  this.announcer_.say(MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS,
    +      goog.a11y.aria.LivePriority.ASSERTIVE);
    +
    +  // Update the size text.
    +  goog.dom.setTextContent(this.getStatusDiv_(element),
    +      goog.i18n.bidi.enforceLtrInText(columns + ' x ' + rows));
    +};
    +
    +
    +/**
    + * Position the mouse catcher such that it receives mouse events past the
    + * selectedsize up to the maximum size.  Takes care to not introduce scrollbars.
    + * Should be called on enter document and when the window changes size.
    + * @param {goog.ui.DimensionPicker} palette The table size palette.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.positionMouseCatcher = function(
    +    palette) {
    +  var mouseCatcher = this.getMouseCatcher_(palette.getElement());
    +  var doc = goog.dom.getOwnerDocument(mouseCatcher);
    +  var body = doc.body;
    +
    +  var position = goog.style.getRelativePosition(mouseCatcher, body);
    +
    +  // Hide the mouse catcher so it doesn't affect the body's scroll size.
    +  mouseCatcher.style.display = 'none';
    +
    +  // Compute the maximum size the catcher can be without introducing scrolling.
    +  var xAvailableEm = (palette.isRightToLeft() && position.x > 0) ?
    +      Math.floor(position.x / 18) :
    +      Math.floor((body.scrollWidth - position.x) / 18);
    +
    +  // Computing available height is more complicated - we need to check the
    +  // window's inner height.
    +  var height;
    +  if (goog.userAgent.IE) {
    +    // Offset 20px to make up for scrollbar size.
    +    height = goog.style.getClientViewportElement(body).scrollHeight - 20;
    +  } else {
    +    var win = goog.dom.getWindow(doc);
    +    // Offset 20px to make up for scrollbar size.
    +    height = Math.max(win.innerHeight, body.scrollHeight) - 20;
    +  }
    +  var yAvailableEm = Math.floor((height - position.y) / 18);
    +
    +  // Resize and display the mouse catcher.
    +  mouseCatcher.style.width = Math.min(palette.maxColumns, xAvailableEm) + 'em';
    +  mouseCatcher.style.height = Math.min(palette.maxRows, yAvailableEm) + 'em';
    +  mouseCatcher.style.display = '';
    +
    +  // Explicitly set style.right so the mouse catcher is positioned on the left
    +  // side instead of right.
    +  if (palette.isRightToLeft()) {
    +    mouseCatcher.style.right = '0';
    +  }
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.DimensionPickerRenderer.prototype.getCssClass = function() {
    +  return goog.ui.DimensionPickerRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * This function adjusts the positioning from 'left' and 'top' to 'right' and
    + * 'top' as appropriate for RTL control.  This is so when the dimensionpicker
    + * grow in width, the containing element grow to the left instead of right.
    + * This won't be necessary if goog.ui.SubMenu rendering code would position RTL
    + * control with 'right' and 'top'.
    + * @private
    + *
    + * @param {goog.ui.DimensionPicker} palette The palette object.
    + * @param {Element} element The palette's element.
    + */
    +goog.ui.DimensionPickerRenderer.prototype.adjustParentDirection_ =
    +    function(palette, element) {
    +  var parent = palette.getParent();
    +  if (parent) {
    +    var parentElement = parent.getElement();
    +
    +    // Anchors the containing element to the right so it grows to the left
    +    // when it increase in width.
    +    var right = goog.style.getStyle(parentElement, 'right');
    +    if (right == '') {
    +      var parentPos = goog.style.getPosition(parentElement);
    +      var parentSize = goog.style.getSize(parentElement);
    +      if (parentSize.width != 0 && parentPos.x != 0) {
    +        var visibleRect = goog.style.getBounds(
    +            goog.style.getClientViewportElement());
    +        var visibleWidth = visibleRect.width;
    +        right = visibleWidth - parentPos.x - parentSize.width;
    +        goog.style.setStyle(parentElement, 'right', right + 'px');
    +      }
    +    }
    +
    +    // When a table is inserted, the containing elemet's position is
    +    // recalculated the next time it shows, set left back to '' to prevent
    +    // extra white space on the left.
    +    var left = goog.style.getStyle(parentElement, 'left');
    +    if (left != '') {
    +      goog.style.setStyle(parentElement, 'left', '');
    +    }
    +  } else {
    +    goog.style.setStyle(element, 'right', '0px');
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.html
    new file mode 100644
    index 0000000000..8f8f9b6c4b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for DimensionPickerRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.DimensionPickerRendererTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.js
    new file mode 100644
    index 0000000000..7410182d0a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dimensionpickerrenderer_test.js
    @@ -0,0 +1,55 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.DimensionPickerRendererTest');
    +goog.setTestOnly('goog.ui.DimensionPickerRendererTest');
    +
    +goog.require('goog.a11y.aria.LivePriority');
    +goog.require('goog.array');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.DimensionPicker');
    +goog.require('goog.ui.DimensionPickerRenderer');
    +
    +
    +var renderer;
    +var picker;
    +
    +function setUp() {
    +  renderer = new goog.ui.DimensionPickerRenderer();
    +  picker = new goog.ui.DimensionPicker(renderer);
    +}
    +
    +function tearDown() {
    +  picker.dispose();
    +}
    +
    +
    +/**
    + * Tests that the right aria label is added when the highlighted
    + * size changes.
    + */
    +function testSetHighlightedSizeUpdatesLiveRegion() {
    +  picker.render();
    +
    +  var sayFunction = goog.testing.recordFunction();
    +  renderer.announcer_.say = sayFunction;
    +  renderer.setHighlightedSize(picker, 3, 7);
    +
    +  assertEquals(1, sayFunction.getCallCount());
    +
    +  assertTrue(goog.array.equals(
    +      ['3 by 7', goog.a11y.aria.LivePriority.ASSERTIVE],
    +      sayFunction.getLastCall().getArguments()));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/dragdropdetector.js b/mapbox-gl/closure-library/closure/goog/ui/dragdropdetector.js
    new file mode 100644
    index 0000000000..f044546078
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/dragdropdetector.js
    @@ -0,0 +1,647 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detects images dragged and dropped on to the window.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.DragDropDetector');
    +goog.provide('goog.ui.DragDropDetector.EventType');
    +goog.provide('goog.ui.DragDropDetector.ImageDropEvent');
    +goog.provide('goog.ui.DragDropDetector.LinkDropEvent');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Creates a new drag and drop detector.
    + * @param {string=} opt_filePath The URL of the page to use for the detector.
    + *     It should contain the same contents as dragdropdetector_target.html in
    + *     the demos directory.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.ui.DragDropDetector = function(opt_filePath) {
    +  goog.ui.DragDropDetector.base(this, 'constructor');
    +
    +  var iframe = goog.dom.createDom(goog.dom.TagName.IFRAME, {
    +    'frameborder': 0
    +  });
    +  // In Firefox, we do all drop detection with an IFRAME.  In IE, we only use
    +  // the IFRAME to capture copied, non-linked images.  (When we don't need it,
    +  // we put a text INPUT before it and push it off screen.)
    +  iframe.className = goog.userAgent.IE ?
    +      goog.getCssName(
    +          goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-editable-iframe') :
    +      goog.getCssName(
    +          goog.ui.DragDropDetector.BASE_CSS_NAME_, 'w3c-editable-iframe');
    +  iframe.src = opt_filePath || goog.ui.DragDropDetector.DEFAULT_FILE_PATH_;
    +
    +  this.element_ = /** @type {!HTMLIFrameElement} */ (iframe);
    +
    +  this.handler_ = new goog.events.EventHandler(this);
    +  this.handler_.listen(iframe, goog.events.EventType.LOAD, this.initIframe_);
    +
    +  if (goog.userAgent.IE) {
    +    // In IE, we have to bounce between an INPUT for catching links and an
    +    // IFRAME for catching images.
    +    this.textInput_ = goog.dom.createDom(goog.dom.TagName.INPUT, {
    +      'type': 'text',
    +      'className': goog.getCssName(
    +          goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-input')
    +    });
    +
    +    this.root_ = goog.dom.createDom(goog.dom.TagName.DIV,
    +        goog.getCssName(goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-div'),
    +        this.textInput_, iframe);
    +  } else {
    +    this.root_ = iframe;
    +  }
    +
    +  document.body.appendChild(this.root_);
    +};
    +goog.inherits(goog.ui.DragDropDetector, goog.events.EventTarget);
    +
    +
    +/**
    + * Drag and drop event types.
    + * @enum {string}
    + */
    +goog.ui.DragDropDetector.EventType = {
    +  IMAGE_DROPPED: 'onimagedrop',
    +  LINK_DROPPED: 'onlinkdrop'
    +};
    +
    +
    +/**
    + * Browser specific drop event type.
    + * @type {string}
    + * @private
    + */
    +goog.ui.DragDropDetector.DROP_EVENT_TYPE_ = goog.userAgent.IE ?
    +    goog.events.EventType.DROP : 'dragdrop';
    +
    +
    +/**
    + * Initial value for clientX and clientY indicating that the location has
    + * never been updated.
    + */
    +goog.ui.DragDropDetector.INIT_POSITION = -10000;
    +
    +
    +/**
    + * Prefix for all CSS names.
    + * @type {string}
    + * @private
    + */
    +goog.ui.DragDropDetector.BASE_CSS_NAME_ = goog.getCssName('goog-dragdrop');
    +
    +
    +/**
    + * @desc Message shown to users to inform them that they can't drag and drop
    + *     local files.
    + */
    +goog.ui.DragDropDetector.MSG_DRAG_DROP_LOCAL_FILE_ERROR = goog.getMsg(
    +    'It is not possible to drag ' +
    +    'and drop image files at this time.\nPlease drag an image from your web ' +
    +    'browser.');
    +
    +
    +/**
    + * @desc Message shown to users trying to drag and drop protected images from
    + *     Flickr, etc.
    + */
    +goog.ui.DragDropDetector.MSG_DRAG_DROP_PROTECTED_FILE_ERROR = goog.getMsg(
    +    'The image you are ' +
    +    'trying to drag has been blocked by the hosting site.');
    +
    +
    +/**
    + * A map of special case information for URLs that cannot be dropped.  Each
    + * entry is of the form:
    + *     regex: url regex
    + *     message: user visible message about this special case
    + * @type {Array<{regex: RegExp, message: string}>}
    + * @private
    + */
    +goog.ui.DragDropDetector.SPECIAL_CASE_URLS_ = [
    +  {
    +    regex: /^file:\/\/\//,
    +    message: goog.ui.DragDropDetector.MSG_DRAG_DROP_LOCAL_FILE_ERROR
    +  },
    +  {
    +    regex: /flickr(.*)spaceball.gif$/,
    +    message: goog.ui.DragDropDetector.MSG_DRAG_DROP_PROTECTED_FILE_ERROR
    +  }
    +];
    +
    +
    +/**
    + * Regex that matches anything that looks kind of like a URL.  It matches
    + * nonspacechars://nonspacechars
    + * @type {RegExp}
    + * @private
    + */
    +goog.ui.DragDropDetector.URL_LIKE_REGEX_ = /^\S+:\/\/\S*$/;
    +
    +
    +/**
    + * Path to the dragdrop.html file.
    + * @type {string}
    + * @private
    + */
    +goog.ui.DragDropDetector.DEFAULT_FILE_PATH_ = 'dragdropdetector_target.html';
    +
    +
    +/**
    + * Our event handler object.
    + * @type {goog.events.EventHandler}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.handler_;
    +
    +
    +/**
    + * The root element (the IFRAME on most browsers, the DIV on IE).
    + * @type {Element}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.root_;
    +
    +
    +/**
    + * The text INPUT element used to detect link drops on IE.  null on Firefox.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.textInput_;
    +
    +
    +/**
    + * The iframe element.
    + * @type {HTMLIFrameElement}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.element_;
    +
    +
    +/**
    + * The iframe's window, null if the iframe hasn't loaded yet.
    + * @type {Window}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.window_ = null;
    +
    +
    +/**
    + * The iframe's document, null if the iframe hasn't loaded yet.
    + * @type {Document}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.document_ = null;
    +
    +
    +/**
    + * The iframe's body, null if the iframe hasn't loaded yet.
    + * @type {HTMLBodyElement}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.body_ = null;
    +
    +
    +/**
    + * Whether we are in "screen cover" mode in which the iframe or div is
    + * covering the entire screen.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.isCoveringScreen_ = false;
    +
    +
    +/**
    + * The last position of the mouse while dragging.
    + * @type {goog.math.Coordinate}
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.mousePosition_ = null;
    +
    +
    +/**
    + * Initialize the iframe after it has loaded.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.initIframe_ = function() {
    +  // Set up a holder for position data.
    +  this.mousePosition_ = new goog.math.Coordinate(
    +      goog.ui.DragDropDetector.INIT_POSITION,
    +      goog.ui.DragDropDetector.INIT_POSITION);
    +
    +  // Set up pointers to the important parts of the IFrame.
    +  this.window_ = this.element_.contentWindow;
    +  this.document_ = this.window_.document;
    +  this.body_ = this.document_.body;
    +
    +  if (goog.userAgent.GECKO) {
    +    this.document_.designMode = 'on';
    +  } else if (!goog.userAgent.IE) {
    +    // Bug 1667110
    +    // In IE, we only set the IFrame body as content-editable when we bring it
    +    // into view at the top of the page.  Otherwise it may take focus when the
    +    // page is loaded, scrolling the user far offscreen.
    +    // Note that this isn't easily unit-testable, since it depends on a
    +    // browser-specific behavior with content-editable areas.
    +    this.body_.contentEditable = true;
    +  }
    +
    +  this.handler_.listen(document.body, goog.events.EventType.DRAGENTER,
    +      this.coverScreen_);
    +
    +  if (goog.userAgent.IE) {
    +    // IE only events.
    +    // Set up events on the IFrame.
    +    this.handler_.
    +        listen(this.body_,
    +            [goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER],
    +            goog.ui.DragDropDetector.enforceCopyEffect_).
    +        listen(this.body_, goog.events.EventType.MOUSEOUT,
    +            this.switchToInput_).
    +        listen(this.body_, goog.events.EventType.DRAGLEAVE,
    +            this.uncoverScreen_).
    +        listen(this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
    +            function(e) {
    +              this.trackMouse_(e);
    +
    +              // The drop event occurs before the content is added to the
    +              // iframe.  We setTimeout so that handleNodeInserted_ is called
    +              //  after the content is in the document.
    +              goog.global.setTimeout(
    +                  goog.bind(this.handleNodeInserted_, this, e), 0);
    +              return true;
    +            }).
    +
    +        // Set up events on the DIV.
    +        listen(this.root_,
    +            [goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER],
    +            this.handleNewDrag_).
    +        listen(this.root_,
    +            [
    +              goog.events.EventType.MOUSEMOVE,
    +              goog.events.EventType.KEYPRESS
    +            ], this.uncoverScreen_).
    +
    +        // Set up events on the text INPUT.
    +        listen(this.textInput_, goog.events.EventType.DRAGOVER,
    +            goog.events.Event.preventDefault).
    +        listen(this.textInput_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
    +            this.handleInputDrop_);
    +  } else {
    +    // W3C events.
    +    this.handler_.
    +        listen(this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
    +            function(e) {
    +              this.trackMouse_(e);
    +              this.uncoverScreen_();
    +            }).
    +        listen(this.body_,
    +            [goog.events.EventType.MOUSEMOVE, goog.events.EventType.KEYPRESS],
    +            this.uncoverScreen_).
    +
    +        // Detect content insertion.
    +        listen(this.document_, 'DOMNodeInserted',
    +            this.handleNodeInserted_);
    +  }
    +};
    +
    +
    +/**
    + * Enforce that anything dragged over the IFRAME is copied in to it, rather
    + * than making it navigate to a different URL.
    + * @param {goog.events.BrowserEvent} e The event to enforce copying on.
    + * @private
    + */
    +goog.ui.DragDropDetector.enforceCopyEffect_ = function(e) {
    +  var event = e.getBrowserEvent();
    +  // This function is only called on IE.
    +  if (event.dataTransfer.dropEffect.toLowerCase() != 'copy') {
    +    event.dataTransfer.dropEffect = 'copy';
    +  }
    +};
    +
    +
    +/**
    + * Cover the screen with the iframe.
    + * @param {goog.events.BrowserEvent} e The event that caused this function call.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.coverScreen_ = function(e) {
    +  // Don't do anything if the drop effect is 'none' and we are in IE.
    +  // It is set to 'none' in cases like dragging text inside a text area.
    +  if (goog.userAgent.IE &&
    +      e.getBrowserEvent().dataTransfer.dropEffect == 'none') {
    +    return;
    +  }
    +
    +  if (!this.isCoveringScreen_) {
    +    this.isCoveringScreen_ = true;
    +    if (goog.userAgent.IE) {
    +      goog.style.setStyle(this.root_, 'top', '0');
    +      this.body_.contentEditable = true;
    +      this.switchToInput_(e);
    +    } else {
    +      goog.style.setStyle(this.root_, 'height', '5000px');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Uncover the screen.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.uncoverScreen_ = function() {
    +  if (this.isCoveringScreen_) {
    +    this.isCoveringScreen_ = false;
    +    if (goog.userAgent.IE) {
    +      this.body_.contentEditable = false;
    +      goog.style.setStyle(this.root_, 'top', '-5000px');
    +    } else {
    +      goog.style.setStyle(this.root_, 'height', '10px');
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Re-insert the INPUT into the DIV.  Does nothing when the DIV is off screen.
    + * @param {goog.events.BrowserEvent} e The event that caused this function call.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.switchToInput_ = function(e) {
    +  // This is only called on IE.
    +  if (this.isCoveringScreen_) {
    +    goog.style.setElementShown(this.textInput_, true);
    +  }
    +};
    +
    +
    +/**
    + * Remove the text INPUT so the IFRAME is showing.  Does nothing when the DIV is
    + * off screen.
    + * @param {goog.events.BrowserEvent} e The event that caused this function call.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.switchToIframe_ = function(e) {
    +  // This is only called on IE.
    +  if (this.isCoveringScreen_) {
    +    goog.style.setElementShown(this.textInput_, false);
    +  }
    +};
    +
    +
    +/**
    + * Handle a new drag event.
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @return {boolean|undefined} Returns false in IE to cancel the event.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.handleNewDrag_ = function(e) {
    +  var event = e.getBrowserEvent();
    +
    +  // This is only called on IE.
    +  if (event.dataTransfer.dropEffect == 'link') {
    +    this.switchToInput_(e);
    +    e.preventDefault();
    +    return false;
    +  }
    +
    +  // Things that aren't links can be placed in the contentEditable iframe.
    +  this.switchToIframe_(e);
    +
    +  // No need to return true since for events return true is the same as no
    +  // return.
    +};
    +
    +
    +/**
    + * Handle mouse tracking.
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.trackMouse_ = function(e) {
    +  this.mousePosition_.x = e.clientX;
    +  this.mousePosition_.y = e.clientY;
    +
    +  // Check if the event is coming from within the iframe.
    +  if (goog.dom.getOwnerDocument(/** @type {Node} */ (e.target)) != document) {
    +    var iframePosition = goog.style.getClientPosition(this.element_);
    +    this.mousePosition_.x += iframePosition.x;
    +    this.mousePosition_.y += iframePosition.y;
    +  }
    +};
    +
    +
    +/**
    + * Handle a drop on the IE text INPUT.
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.handleInputDrop_ = function(e) {
    +  this.dispatchEvent(
    +      new goog.ui.DragDropDetector.LinkDropEvent(
    +          e.getBrowserEvent().dataTransfer.getData('Text')));
    +  this.uncoverScreen_();
    +  e.preventDefault();
    +};
    +
    +
    +/**
    + * Clear the contents of the iframe.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.clearContents_ = function() {
    +  if (goog.userAgent.WEBKIT) {
    +    // Since this is called on a mutation event for the nodes we are going to
    +    // clear, calling this right away crashes some versions of WebKit.  Wait
    +    // until the events are finished.
    +    goog.global.setTimeout(goog.bind(function() {
    +      goog.dom.setTextContent(this, '');
    +    }, this.body_), 0);
    +  } else {
    +    this.document_.execCommand('selectAll', false, null);
    +    this.document_.execCommand('delete', false, null);
    +    this.document_.execCommand('selectAll', false, null);
    +  }
    +};
    +
    +
    +/**
    + * Event handler called when the content of the iframe changes.
    + * @param {goog.events.BrowserEvent} e The event that caused this function call.
    + * @private
    + */
    +goog.ui.DragDropDetector.prototype.handleNodeInserted_ = function(e) {
    +  var uri;
    +
    +  if (this.body_.innerHTML.indexOf('<') == -1) {
    +    // If the document contains no tags (i.e. is just text), try it out.
    +    uri = goog.string.trim(goog.dom.getTextContent(this.body_));
    +
    +    // See if it looks kind of like a url.
    +    if (!uri.match(goog.ui.DragDropDetector.URL_LIKE_REGEX_)) {
    +      uri = null;
    +    }
    +  }
    +
    +  if (!uri) {
    +    var imgs = this.body_.getElementsByTagName(goog.dom.TagName.IMG);
    +    if (imgs && imgs.length) {
    +      // TODO(robbyw): Grab all the images, instead of just the first.
    +      var img = imgs[0];
    +      uri = img.src;
    +    }
    +  }
    +
    +  if (uri) {
    +    var specialCases = goog.ui.DragDropDetector.SPECIAL_CASE_URLS_;
    +    var len = specialCases.length;
    +    for (var i = 0; i < len; i++) {
    +      var specialCase = specialCases[i];
    +      if (uri.match(specialCase.regex)) {
    +        alert(specialCase.message);
    +        break;
    +      }
    +    }
    +
    +    // If no special cases matched, add the image.
    +    if (i == len) {
    +      this.dispatchEvent(
    +          new goog.ui.DragDropDetector.ImageDropEvent(
    +              uri, this.mousePosition_));
    +      return;
    +    }
    +  }
    +
    +  var links = this.body_.getElementsByTagName(goog.dom.TagName.A);
    +  if (links) {
    +    for (i = 0, len = links.length; i < len; i++) {
    +      this.dispatchEvent(
    +          new goog.ui.DragDropDetector.LinkDropEvent(links[i].href));
    +    }
    +  }
    +
    +  this.clearContents_();
    +  this.uncoverScreen_();
    +};
    +
    +
    +/** @override */
    +goog.ui.DragDropDetector.prototype.disposeInternal = function() {
    +  goog.ui.DragDropDetector.base(this, 'disposeInternal');
    +  this.handler_.dispose();
    +  this.handler_ = null;
    +};
    +
    +
    +
    +/**
    + * Creates a new image drop event object.
    + * @param {string} url The url of the dropped image.
    + * @param {goog.math.Coordinate} position The screen position where the drop
    + *     occurred.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.DragDropDetector.ImageDropEvent = function(url, position) {
    +  goog.ui.DragDropDetector.ImageDropEvent.base(this, 'constructor',
    +      goog.ui.DragDropDetector.EventType.IMAGE_DROPPED);
    +
    +  /**
    +   * The url of the image that was dropped.
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * The screen position where the drop occurred.
    +   * @type {goog.math.Coordinate}
    +   * @private
    +   */
    +  this.position_ = position;
    +};
    +goog.inherits(goog.ui.DragDropDetector.ImageDropEvent,
    +    goog.events.Event);
    +
    +
    +/**
    + * @return {string} The url of the image that was dropped.
    + */
    +goog.ui.DragDropDetector.ImageDropEvent.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * @return {goog.math.Coordinate} The screen position where the drop occurred.
    + *     This may be have x and y of goog.ui.DragDropDetector.INIT_POSITION,
    + *     indicating the drop position is unknown.
    + */
    +goog.ui.DragDropDetector.ImageDropEvent.prototype.getPosition = function() {
    +  return this.position_;
    +};
    +
    +
    +
    +/**
    + * Creates a new link drop event object.
    + * @param {string} url The url of the dropped link.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.DragDropDetector.LinkDropEvent = function(url) {
    +  goog.ui.DragDropDetector.LinkDropEvent.base(this, 'constructor',
    +      goog.ui.DragDropDetector.EventType.LINK_DROPPED);
    +
    +  /**
    +   * The url of the link that was dropped.
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +};
    +goog.inherits(goog.ui.DragDropDetector.LinkDropEvent,
    +    goog.events.Event);
    +
    +
    +/**
    + * @return {string} The url of the link that was dropped.
    + */
    +goog.ui.DragDropDetector.LinkDropEvent.prototype.getUrl = function() {
    +  return this.url_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/drilldownrow.js b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow.js
    new file mode 100644
    index 0000000000..9d987310fe
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow.js
    @@ -0,0 +1,494 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tree-like drilldown components for HTML tables.
    + *
    + * This component supports expanding and collapsing groups of rows in
    + * HTML tables.  The behavior is like typical Tree widgets, but tables
    + * need special support to enable the tree behaviors.
    + *
    + * Any row or rows in an HTML table can be DrilldownRows.  The root
    + * DrilldownRow nodes are always visible in the table, but the rest show
    + * or hide as input events expand and collapse their ancestors.
    + *
    + * Programming them:  Top-level DrilldownRows are made by decorating
    + * a TR element.  Children are made with addChild or addChildAt, and
    + * are entered into the document by the render() method.
    + *
    + * A DrilldownRow can have any number of children.  If it has no children
    + * it can be loaded, not loaded, or with a load in progress.
    + * Top-level DrilldownRows are always displayed (though setting
    + * style.display on a containing DOM node could make one be not
    + * visible to the user).  A DrilldownRow can be expanded, or not.  A
    + * DrilldownRow displays if all of its ancestors are expanded.
    + *
    + * Set up event handlers and style each row for the application in an
    + * enterDocument method.
    + *
    + * Children normally render into the document lazily, at the first
    + * moment when all ancestors are expanded.
    + *
    + * @see ../demos/drilldownrow.html
    + */
    +
    +// TODO(user): Build support for dynamically loading DrilldownRows,
    +// probably using automplete as an example to follow.
    +
    +// TODO(user): Make DrilldownRows accessible through the keyboard.
    +
    +// The render method is redefined in this class because when addChildAt renders
    +// the new child it assumes that the child's DOM node will be a child
    +// of the parent component's DOM node, but all DOM nodes of DrilldownRows
    +// in the same tree of DrilldownRows are siblings to each other.
    +//
    +// Arguments (or lack of arguments) to the render methods in Component
    +// all determine the place of the new DOM node in the DOM tree, but
    +// the place of a new DrilldownRow in the DOM needs to be determined by
    +// its position in the tree of DrilldownRows.
    +
    +goog.provide('goog.ui.DrilldownRow');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Builds a DrilldownRow component, which can overlay a tree
    + * structure onto sections of an HTML table.
    + *
    + * @param {Object=} opt_properties This parameter can contain:
    + *   contents:  if present, user data identifying
    + *     the information loaded into the row and its children.
    + *   loaded: initializes the isLoaded property, defaults to true.
    + *   expanded: DrilldownRow expanded or not, default is true.
    + *   html: String of HTML, relevant and required for DrilldownRows to be
    + *     added as children.  Ignored when decorating an existing table row.
    + *   decorator: Function that accepts one DrilldownRow argument, and
    + *     should customize and style the row.  The default is to call
    + *     goog.ui.DrilldownRow.decorator.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.ui.DrilldownRow = function(opt_properties, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +  var properties = opt_properties || {};
    +
    +  // Initialize instance variables.
    +
    +  /**
    +   * String of HTML to initialize the DOM structure for the table row.
    +   * Should have the form '<tr attr="etc">Row contents here</tr>'.
    +   * @type {string}
    +   * @private
    +   */
    +  this.html_ = properties.html;
    +
    +  /**
    +   * Controls whether this component's children will show when it shows.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.expanded_ = typeof properties.expanded != 'undefined' ?
    +      properties.expanded : true;
    +
    +  /**
    +   * Is this component loaded? States are true, false, and null for
    +   * 'loading in progress'.  For in-memory
    +   * trees of components, this is always true.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.loaded_ = typeof properties.loaded != 'undefined' ?
    +      properties.loaded : true;
    +
    +  /**
    +   * If this component's DOM element is created from a string of
    +   * HTML, this is the function to call when it is entered into the DOM tree.
    +   * @type {Function} args are DrilldownRow and goog.events.EventHandler
    +   *   of the DrilldownRow.
    +   * @private
    +   */
    +  this.decoratorFn_ = properties.decorator || goog.ui.DrilldownRow.decorate;
    +
    +  /**
    +   * Is the DrilldownRow to be displayed?  If it is rendered, this mirrors
    +   * the style.display of the DrilldownRow's row.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.displayed_ = true;
    +};
    +goog.inherits(goog.ui.DrilldownRow, goog.ui.Component);
    +
    +
    +/**
    + * Example object with properties of the form accepted by the class
    + * constructor.  These are educational and show the compiler that
    + * these properties can be set so it doesn't emit warnings.
    + */
    +goog.ui.DrilldownRow.sampleProperties = {
    +  html: '<tr><td>Sample</td><td>Sample</td></tr>',
    +  loaded: true,
    +  decorator: function(selfObj, handler) {
    +    // When the mouse is hovering, add CSS class goog-drilldown-hover.
    +    goog.ui.DrilldownRow.decorate(selfObj);
    +    var row = selfObj.getElement();
    +    handler.listen(row, 'mouseover', function() {
    +      goog.dom.classlist.add(row, goog.getCssName('goog-drilldown-hover'));
    +    });
    +    handler.listen(row, 'mouseout', function() {
    +      goog.dom.classlist.remove(row, goog.getCssName('goog-drilldown-hover'));
    +    });
    +  }
    +};
    +
    +
    +//
    +// Implementations of Component methods.
    +//
    +
    +
    +/**
    + * The base class method calls its superclass method and this
    + * drilldown's 'decorator' method as defined in the constructor.
    + * @override
    + */
    +goog.ui.DrilldownRow.prototype.enterDocument = function() {
    +  goog.ui.DrilldownRow.superClass_.enterDocument.call(this);
    +  this.decoratorFn_(this, this.getHandler());
    +};
    +
    +
    +/** @override */
    +goog.ui.DrilldownRow.prototype.createDom = function() {
    +  this.setElementInternal(goog.ui.DrilldownRow.createRowNode_(
    +      this.html_, this.getDomHelper().getDocument()));
    +};
    +
    +
    +/**
    + * A top-level DrilldownRow decorates a TR element.
    + *
    + * @param {Element} node The element to test for decorability.
    + * @return {boolean} true iff the node is a TR.
    + * @override
    + */
    +goog.ui.DrilldownRow.prototype.canDecorate = function(node) {
    +  return node.tagName == 'TR';
    +};
    +
    +
    +/**
    + * Child drilldowns are rendered when needed.
    + *
    + * @param {goog.ui.Component} child New DrilldownRow child to be added.
    + * @param {number} index position to be occupied by the child.
    + * @param {boolean=} opt_render true to force immediate rendering.
    + * @override
    + */
    +goog.ui.DrilldownRow.prototype.addChildAt = function(child, index, opt_render) {
    +  goog.asserts.assertInstanceof(child, goog.ui.DrilldownRow);
    +  goog.ui.DrilldownRow.superClass_.addChildAt.call(this, child, index, false);
    +  child.setDisplayable_(this.isVisible_() && this.isExpanded());
    +  if (opt_render && !child.isInDocument()) {
    +    child.render();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.DrilldownRow.prototype.removeChild = function(child) {
    +  goog.dom.removeNode(child.getElement());
    +  return goog.ui.DrilldownRow.superClass_.removeChild.call(this, child);
    +};
    +
    +
    +/**
    + * Rendering of DrilldownRow's is on need, do not call this directly
    + * from application code.
    + *
    + * Rendering a DrilldownRow places it according to its position in its
    + * tree of DrilldownRows.  DrilldownRows cannot be placed any other
    + * way so this method does not use any arguments.  This does not call
    + * the base class method and does not modify any of this
    + * DrilldownRow's children.
    + * @override
    + */
    +goog.ui.DrilldownRow.prototype.render = function() {
    +  if (arguments.length) {
    +    throw Error('A DrilldownRow cannot be placed under a specific parent.');
    +  } else {
    +    var parent = this.getParent();
    +    if (!parent.isInDocument()) {
    +      throw Error('Cannot render child of un-rendered parent');
    +    }
    +    // The new child's TR node needs to go just after the last TR
    +    // of the part of the parent's subtree that is to the left
    +    // of this.  The subtree includes the parent.
    +    goog.asserts.assertInstanceof(parent, goog.ui.DrilldownRow);
    +    var previous = parent.previousRenderedChild_(this);
    +    var row;
    +    if (previous) {
    +      goog.asserts.assertInstanceof(previous, goog.ui.DrilldownRow);
    +      row = previous.lastRenderedLeaf_().getElement();
    +    } else {
    +      row = parent.getElement();
    +    }
    +    row = /** @type {Element} */ (row.nextSibling);
    +    // Render the child row component into the document.
    +    if (row) {
    +      this.renderBefore(row);
    +    } else {
    +      // Render at the end of the parent of this DrilldownRow's
    +      // DOM element.
    +      var tbody = /** @type {Element} */ (parent.getElement().parentNode);
    +      goog.ui.DrilldownRow.superClass_.render.call(this, tbody);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Finds the numeric index of this child within its parent Component.
    + * Throws an exception if it has no parent.
    + *
    + * @return {number} index of this within the children of the parent Component.
    + */
    +goog.ui.DrilldownRow.prototype.findIndex = function() {
    +  var parent = this.getParent();
    +  if (!parent) {
    +    throw Error('Component has no parent');
    +  }
    +  return parent.indexOfChild(this);
    +};
    +
    +
    +//
    +// Type-specific operations
    +//
    +
    +
    +/**
    + * Returns the expanded state of the DrilldownRow.
    + *
    + * @return {boolean} true iff this is expanded.
    + */
    +goog.ui.DrilldownRow.prototype.isExpanded = function() {
    +  return this.expanded_;
    +};
    +
    +
    +/**
    + * Sets the expanded state of this DrilldownRow: makes all children
    + * displayable or not displayable corresponding to the expanded state.
    + *
    + * @param {boolean} expanded whether this should be expanded or not.
    + */
    +goog.ui.DrilldownRow.prototype.setExpanded = function(expanded) {
    +  if (expanded != this.expanded_) {
    +    this.expanded_ = expanded;
    +    var elem = this.getElement();
    +    goog.asserts.assert(elem);
    +    goog.dom.classlist.toggle(elem,
    +        goog.getCssName('goog-drilldown-expanded'));
    +    goog.dom.classlist.toggle(elem,
    +        goog.getCssName('goog-drilldown-collapsed'));
    +    if (this.isVisible_()) {
    +      this.forEachChild(function(child) {
    +        child.setDisplayable_(expanded);
    +      });
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns this DrilldownRow's level in the tree.  Top level is 1.
    + *
    + * @return {number} depth of this DrilldownRow in its tree of drilldowns.
    + */
    +goog.ui.DrilldownRow.prototype.getDepth = function() {
    +  for (var component = this, depth = 0;
    +       component instanceof goog.ui.DrilldownRow;
    +       component = component.getParent(), depth++) {}
    +  return depth;
    +};
    +
    +
    +/**
    + * This static function is a default decorator that adds HTML at the
    + * beginning of the first cell to display indentation and an expander
    + * image; sets up a click handler on the toggler; initializes a class
    + * for the row: either goog-drilldown-expanded or
    + * goog-drilldown-collapsed, depending on the initial state of the
    + * DrilldownRow; and sets up a click event handler on the toggler
    + * element.
    + *
    + * This creates a DIV with class=toggle.  Your application can set up
    + * CSS style rules something like this:
    + *
    + * tr.goog-drilldown-expanded .toggle {
    + *   background-image: url('minus.png');
    + * }
    + *
    + * tr.goog-drilldown-collapsed .toggle {
    + *   background-image: url('plus.png');
    + * }
    + *
    + * These background images show whether the DrilldownRow is expanded.
    + *
    + * @param {goog.ui.DrilldownRow} selfObj DrilldownRow to be decorated.
    + */
    +goog.ui.DrilldownRow.decorate = function(selfObj) {
    +  var depth = selfObj.getDepth();
    +  var row = selfObj.getElement();
    +  goog.asserts.assert(row);
    +  if (!row.cells) {
    +    throw Error('No cells');
    +  }
    +  var cell = row.cells[0];
    +  var html = '<div style="float: left; width: ' + depth +
    +      'em;"><div class=toggle style="width: 1em; float: right;">' +
    +      '&nbsp;</div></div>';
    +  var fragment = selfObj.getDomHelper().htmlToDocumentFragment(html);
    +  cell.insertBefore(fragment, cell.firstChild);
    +  goog.dom.classlist.add(row, selfObj.isExpanded() ?
    +      goog.getCssName('goog-drilldown-expanded') :
    +      goog.getCssName('goog-drilldown-collapsed'));
    +  // Default mouse event handling:
    +  var toggler = fragment.getElementsByTagName('div')[0];
    +  var key = selfObj.getHandler().listen(toggler, 'click', function(event) {
    +    selfObj.setExpanded(!selfObj.isExpanded());
    +  });
    +};
    +
    +
    +//
    +// Private methods
    +//
    +
    +
    +/**
    + * Turn display of a DrilldownRow on or off.  If the DrilldownRow has not
    + * yet been rendered, this renders it.  This propagates the effect
    + * of the change recursively as needed -- children displaying iff the
    + * parent is displayed and expanded.
    + *
    + * @param {boolean} display state, true iff display is desired.
    + * @private
    + */
    +goog.ui.DrilldownRow.prototype.setDisplayable_ = function(display) {
    +  if (display && !this.isInDocument()) {
    +    this.render();
    +  }
    +  if (this.displayed_ == display) {
    +    return;
    +  }
    +  this.displayed_ = display;
    +  if (this.isInDocument()) {
    +    this.getElement().style.display = display ? '' : 'none';
    +  }
    +  var selfObj = this;
    +  this.forEachChild(function(child) {
    +    child.setDisplayable_(display && selfObj.expanded_);
    +  });
    +};
    +
    +
    +/**
    + * True iff this and all its DrilldownRow parents are displayable.  The
    + * value is an approximation to actual visibility, since it does not
    + * look at whether DOM nodes containing the top-level component have
    + * display: none, visibility: hidden or are otherwise not displayable.
    + * So this visibility is relative to the top-level component.
    + *
    + * @return {boolean} visibility of this relative to its top-level drilldown.
    + * @private
    + */
    +goog.ui.DrilldownRow.prototype.isVisible_ = function() {
    +  for (var component = this;
    +       component instanceof goog.ui.DrilldownRow;
    +       component = component.getParent()) {
    +    if (!component.displayed_)
    +      return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Create and return a TR element from HTML that looks like
    + * "<tr> ... </tr>".
    + *
    + * @param {string} html for one row.
    + * @param {Document} doc object to hold the Element.
    + * @return {Element} table row node created from the HTML.
    + * @private
    + */
    +goog.ui.DrilldownRow.createRowNode_ = function(html, doc) {
    +  // Note: this may be slow.
    +  var tableHtml = '<table>' + html + '</table>';
    +  var div = doc.createElement('div');
    +  div.innerHTML = tableHtml;
    +  return div.firstChild.rows[0];
    +};
    +
    +
    +/**
    + * Get the recursively rightmost child that is in the document.
    + *
    + * @return {goog.ui.DrilldownRow} rightmost child currently entered in
    + *     the document, potentially this DrilldownRow.  If this is in the
    + *     document, result is non-null.
    + * @private
    + */
    +goog.ui.DrilldownRow.prototype.lastRenderedLeaf_ = function() {
    +  var leaf = null;
    +  for (var node = this;
    +       node && node.isInDocument();
    +       // Node will become undefined if parent has no children.
    +       node = node.getChildAt(node.getChildCount() - 1)) {
    +    leaf = node;
    +  }
    +  return /** @type {goog.ui.DrilldownRow} */ (leaf);
    +};
    +
    +
    +/**
    + * Search this node's direct children for the last one that is in the
    + * document and is before the given child.
    + * @param {goog.ui.DrilldownRow} child The child to stop the search at.
    + * @return {goog.ui.Component?} The last child component before the given child
    + *     that is in the document.
    + * @private
    + */
    +goog.ui.DrilldownRow.prototype.previousRenderedChild_ = function(child) {
    +  for (var i = this.getChildCount() - 1; i >= 0; i--) {
    +    if (this.getChildAt(i) == child) {
    +      for (var j = i - 1; j >= 0; j--) {
    +        var prev = this.getChildAt(j);
    +        if (prev.isInDocument()) {
    +          return prev;
    +        }
    +      }
    +    }
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.html b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.html
    new file mode 100644
    index 0000000000..c193aeaf1d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.html
    @@ -0,0 +1,65 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <title>
    +   DrilldownRow Tests
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.DrilldownRowTest');
    +  </script>
    +  <style type="text/css">
    +   .toggle {
    +  cursor: pointer;
    +  cursor: hand;
    +  background-repeat: none;
    +  background-position: right;
    +}
    +
    +tr.goog-drilldown-expanded .toggle {
    +  background-image: url('../images/minus.png');
    +}
    +
    +tr.goog-drilldown-collapsed .toggle {
    +  background-image: url('../images/plus.png');
    +}
    +
    +tr.goog-drilldown-hover td {
    +  background-color: #CCCCFF;
    +}
    +
    +td {
    +  background-color: white;
    +}
    +  </style>
    + </head>
    + <body>
    +  <table id="table" style="background-color: silver">
    +   <tr>
    +    <th>
    +     Column Head
    +    </th>
    +    <th>
    +     Second Head
    +    </th>
    +   </tr>
    +   <tr id="firstRow">
    +    <td>
    +     First row
    +    </td>
    +    <td>
    +     Second column
    +    </td>
    +   </tr>
    +  </table>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.js b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.js
    new file mode 100644
    index 0000000000..7aa7bf1ac0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/drilldownrow_test.js
    @@ -0,0 +1,47 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.DrilldownRowTest');
    +goog.setTestOnly('goog.ui.DrilldownRowTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.DrilldownRow');
    +
    +function testMakeRows() {
    +  var ff = goog.dom.getElement('firstRow');
    +  var d = new goog.ui.DrilldownRow({});
    +  var d1 = new goog.ui.DrilldownRow(
    +      {html: '<tr><td>Second row</td><td>Second column</td></tr>'}
    +      );
    +  var d2 = new goog.ui.DrilldownRow(
    +      {html: '<tr><td>Third row</td><td>Second column</td></tr>'}
    +      );
    +  var d21 = new goog.ui.DrilldownRow(
    +      {html: '<tr><td>Fourth row</td><td>Second column</td></tr>'}
    +      );
    +  var d22 = new goog.ui.DrilldownRow(goog.ui.DrilldownRow.sampleProperties);
    +  d.decorate(ff);
    +  d.addChild(d1, true);
    +  d.addChild(d2, true);
    +  d2.addChild(d21, true);
    +  d2.addChild(d22, true);
    +
    +  assertThrows(function() {
    +    d.findIndex();
    +  });
    +
    +  assertEquals(0, d1.findIndex());
    +  assertEquals(1, d2.findIndex());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog.js b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog.js
    new file mode 100644
    index 0000000000..0ef5912eeb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog.js
    @@ -0,0 +1,444 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wrapper around {@link goog.ui.Dialog}, to provide
    + * dialogs that are smarter about interacting with a rich text editor.
    + *
    + * @author nicksantos@google.com (Nick Santos)
    + */
    +
    +goog.provide('goog.ui.editor.AbstractDialog');
    +goog.provide('goog.ui.editor.AbstractDialog.Builder');
    +goog.provide('goog.ui.editor.AbstractDialog.EventType');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.string');
    +goog.require('goog.ui.Dialog');
    +goog.require('goog.ui.PopupBase');
    +
    +
    +// *** Public interface ***************************************************** //
    +
    +
    +
    +/**
    + * Creates an object that represents a dialog box.
    + * @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
    + * dialog's dom structure.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.editor.AbstractDialog = function(domHelper) {
    +  goog.events.EventTarget.call(this);
    +  this.dom = domHelper;
    +};
    +goog.inherits(goog.ui.editor.AbstractDialog, goog.events.EventTarget);
    +
    +
    +/**
    + * Causes the dialog box to appear, centered on the screen. Lazily creates the
    + * dialog if needed.
    + */
    +goog.ui.editor.AbstractDialog.prototype.show = function() {
    +  // Lazily create the wrapped dialog to be shown.
    +  if (!this.dialogInternal_) {
    +    this.dialogInternal_ = this.createDialogControl();
    +    this.dialogInternal_.listen(goog.ui.PopupBase.EventType.HIDE,
    +        this.handleAfterHide_, false, this);
    +  }
    +
    +  this.dialogInternal_.setVisible(true);
    +};
    +
    +
    +/**
    + * Hides the dialog, causing AFTER_HIDE to fire.
    + */
    +goog.ui.editor.AbstractDialog.prototype.hide = function() {
    +  if (this.dialogInternal_) {
    +    // This eventually fires the wrapped dialog's AFTER_HIDE event, calling our
    +    // handleAfterHide_().
    +    this.dialogInternal_.setVisible(false);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the dialog is open.
    + */
    +goog.ui.editor.AbstractDialog.prototype.isOpen = function() {
    +  return !!this.dialogInternal_ && this.dialogInternal_.isVisible();
    +};
    +
    +
    +/**
    + * Runs the handler registered on the OK button event and closes the dialog if
    + * that handler succeeds.
    + * This is useful in cases such as double-clicking an item in the dialog is
    + * equivalent to selecting it and clicking the default button.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.processOkAndClose = function() {
    +  // Fake an OK event from the wrapped dialog control.
    +  var evt = new goog.ui.Dialog.Event(goog.ui.Dialog.DefaultButtonKeys.OK, null);
    +  if (this.handleOk(evt)) {
    +    // handleOk calls dispatchEvent, so if any listener calls preventDefault it
    +    // will return false and we won't hide the dialog.
    +    this.hide();
    +  }
    +};
    +
    +
    +// *** Dialog events ******************************************************** //
    +
    +
    +/**
    + * Event type constants for events the dialog fires.
    + * @enum {string}
    + */
    +goog.ui.editor.AbstractDialog.EventType = {
    +  // This event is fired after the dialog is hidden, no matter if it was closed
    +  // via OK or Cancel or is being disposed without being hidden first.
    +  AFTER_HIDE: 'afterhide',
    +  // Either the cancel or OK events can be canceled via preventDefault or by
    +  // returning false from their handlers to stop the dialog from closing.
    +  CANCEL: 'cancel',
    +  OK: 'ok'
    +};
    +
    +
    +// *** Inner helper class *************************************************** //
    +
    +
    +
    +/**
    + * A builder class for the dialog control. All methods except build return this.
    + * @param {goog.ui.editor.AbstractDialog} editorDialog Editor dialog object
    + *     that will wrap the wrapped dialog object this builder will create.
    + * @constructor
    + */
    +goog.ui.editor.AbstractDialog.Builder = function(editorDialog) {
    +  // We require the editor dialog to be passed in so that the builder can set up
    +  // ok/cancel listeners by default, making it easier for most dialogs.
    +  this.editorDialog_ = editorDialog;
    +  this.wrappedDialog_ = new goog.ui.Dialog('', true, this.editorDialog_.dom);
    +  this.buttonSet_ = new goog.ui.Dialog.ButtonSet(this.editorDialog_.dom);
    +  this.buttonHandlers_ = {};
    +  this.addClassName(goog.getCssName('tr-dialog'));
    +};
    +
    +
    +/**
    + * Sets the title of the dialog.
    + * @param {string} title Title HTML (escaped).
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.setTitle = function(title) {
    +  this.wrappedDialog_.setTitle(title);
    +  return this;
    +};
    +
    +
    +/**
    + * Adds an OK button to the dialog. Clicking this button will cause {@link
    + * handleOk} to run, subsequently dispatching an OK event.
    + * @param {string=} opt_label The caption for the button, if not "OK".
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.addOkButton =
    +    function(opt_label) {
    +  var key = goog.ui.Dialog.DefaultButtonKeys.OK;
    +  /** @desc Label for an OK button in an editor dialog. */
    +  var MSG_TR_DIALOG_OK = goog.getMsg('OK');
    +  // True means this is the default/OK button.
    +  this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_OK, true);
    +  this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleOk,
    +                                        this.editorDialog_);
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a Cancel button to the dialog. Clicking this button will cause {@link
    + * handleCancel} to run, subsequently dispatching a CANCEL event.
    + * @param {string=} opt_label The caption for the button, if not "Cancel".
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.addCancelButton =
    +    function(opt_label) {
    +  var key = goog.ui.Dialog.DefaultButtonKeys.CANCEL;
    +  /** @desc Label for a cancel button in an editor dialog. */
    +  var MSG_TR_DIALOG_CANCEL = goog.getMsg('Cancel');
    +  // False means it's not the OK button, true means it's the Cancel button.
    +  this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_CANCEL, false, true);
    +  this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleCancel,
    +                                        this.editorDialog_);
    +  return this;
    +};
    +
    +
    +/**
    + * Adds a custom button to the dialog.
    + * @param {string} label The caption for the button.
    + * @param {function(goog.ui.Dialog.EventType):*} handler Function called when
    + *     the button is clicked. It is recommended that this function be a method
    + *     in the concrete subclass of AbstractDialog using this Builder, and that
    + *     it dispatch an event (see {@link handleOk}).
    + * @param {string=} opt_buttonId Identifier to be used to access the button when
    + *     calling AbstractDialog.getButtonElement().
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.addButton =
    +    function(label, handler, opt_buttonId) {
    +  // We don't care what the key is, just that we can match the button with the
    +  // handler function later.
    +  var key = opt_buttonId || goog.string.createUniqueString();
    +  this.buttonSet_.set(key, label);
    +  this.buttonHandlers_[key] = handler;
    +  return this;
    +};
    +
    +
    +/**
    + * Puts a CSS class on the dialog's main element.
    + * @param {string} className The class to add.
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.addClassName =
    +    function(className) {
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.wrappedDialog_.getDialogElement()), className);
    +  return this;
    +};
    +
    +
    +/**
    + * Sets the content element of the dialog.
    + * @param {Element} contentElem An element for the main body.
    + * @return {!goog.ui.editor.AbstractDialog.Builder} This.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.setContent =
    +    function(contentElem) {
    +  goog.dom.appendChild(this.wrappedDialog_.getContentElement(), contentElem);
    +  return this;
    +};
    +
    +
    +/**
    + * Builds the wrapped dialog control. May only be called once, after which
    + * no more methods may be called on this builder.
    + * @return {!goog.ui.Dialog} The wrapped dialog control.
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.build = function() {
    +  if (this.buttonSet_.isEmpty()) {
    +    // If caller didn't set any buttons, add an OK and Cancel button by default.
    +    this.addOkButton();
    +    this.addCancelButton();
    +  }
    +  this.wrappedDialog_.setButtonSet(this.buttonSet_);
    +
    +  var handlers = this.buttonHandlers_;
    +  this.buttonHandlers_ = null;
    +  this.wrappedDialog_.listen(goog.ui.Dialog.EventType.SELECT,
    +      // Listen for the SELECT event, which means a button was clicked, and
    +      // call the handler associated with that button via the key property.
    +      function(e) {
    +        if (handlers[e.key]) {
    +          return handlers[e.key](e);
    +        }
    +      });
    +
    +  // All editor dialogs are modal.
    +  this.wrappedDialog_.setModal(true);
    +
    +  var dialog = this.wrappedDialog_;
    +  this.wrappedDialog_ = null;
    +  return dialog;
    +};
    +
    +
    +/**
    + * Editor dialog that will wrap the wrapped dialog this builder will create.
    + * @type {goog.ui.editor.AbstractDialog}
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.editorDialog_;
    +
    +
    +/**
    + * wrapped dialog control being built by this builder.
    + * @type {goog.ui.Dialog}
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.wrappedDialog_;
    +
    +
    +/**
    + * Set of buttons to be added to the wrapped dialog control.
    + * @type {goog.ui.Dialog.ButtonSet}
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.buttonSet_;
    +
    +
    +/**
    + * Map from keys that will be returned in the wrapped dialog SELECT events to
    + * handler functions to be called to handle those events.
    + * @type {Object}
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.Builder.prototype.buttonHandlers_;
    +
    +
    +// *** Protected interface ************************************************** //
    +
    +
    +/**
    + * The DOM helper for the parent document.
    + * @type {goog.dom.DomHelper}
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.dom;
    +
    +
    +/**
    + * Creates and returns the goog.ui.Dialog control that is being wrapped
    + * by this object.
    + * @return {!goog.ui.Dialog} Created Dialog control.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.createDialogControl =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * Returns the HTML Button element for the OK button in this dialog.
    + * @return {Element} The button element if found, else null.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.getOkButtonElement = function() {
    +  return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.OK);
    +};
    +
    +
    +/**
    + * Returns the HTML Button element for the Cancel button in this dialog.
    + * @return {Element} The button element if found, else null.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.getCancelButtonElement = function() {
    +  return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.CANCEL);
    +};
    +
    +
    +/**
    + * Returns the HTML Button element for the button added to this dialog with
    + * the given button id.
    + * @param {string} buttonId The id of the button to get.
    + * @return {Element} The button element if found, else null.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.getButtonElement = function(buttonId) {
    +  return this.dialogInternal_.getButtonSet().getButton(buttonId);
    +};
    +
    +
    +/**
    + * Creates and returns the event object to be used when dispatching the OK
    + * event to listeners, or returns null to prevent the dialog from closing.
    + * Subclasses should override this to return their own subclass of
    + * goog.events.Event that includes all data a plugin would need from the dialog.
    + * @param {goog.events.Event} e The event object dispatched by the wrapped
    + *     dialog.
    + * @return {goog.events.Event} The event object to be used when dispatching the
    + *     OK event to listeners.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.createOkEvent = goog.abstractMethod;
    +
    +
    +/**
    + * Handles the event dispatched by the wrapped dialog control when the user
    + * clicks the OK button. Attempts to create the OK event object and dispatches
    + * it if successful.
    + * @param {goog.ui.Dialog.Event} e wrapped dialog OK event object.
    + * @return {boolean} Whether the default action (closing the dialog) should
    + *     still be executed. This will be false if the OK event could not be
    + *     created to be dispatched, or if any listener to that event returs false
    + *     or calls preventDefault.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.handleOk = function(e) {
    +  var eventObj = this.createOkEvent(e);
    +  if (eventObj) {
    +    return this.dispatchEvent(eventObj);
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * Handles the event dispatched by the wrapped dialog control when the user
    + * clicks the Cancel button. Simply dispatches a CANCEL event.
    + * @return {boolean} Returns false if any of the handlers called prefentDefault
    + *     on the event or returned false themselves.
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.handleCancel = function() {
    +  return this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
    +};
    +
    +
    +/**
    + * Disposes of the dialog. If the dialog is open, it will be hidden and
    + * AFTER_HIDE will be dispatched.
    + * @override
    + * @protected
    + */
    +goog.ui.editor.AbstractDialog.prototype.disposeInternal = function() {
    +  if (this.dialogInternal_) {
    +    this.hide();
    +
    +    this.dialogInternal_.dispose();
    +    this.dialogInternal_ = null;
    +  }
    +
    +  goog.ui.editor.AbstractDialog.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +// *** Private implementation *********************************************** //
    +
    +
    +/**
    + * The wrapped dialog widget.
    + * @type {goog.ui.Dialog}
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.prototype.dialogInternal_;
    +
    +
    +/**
    + * Cleans up after the dialog is hidden and fires the AFTER_HIDE event. Should
    + * be a listener for the wrapped dialog's AFTER_HIDE event.
    + * @private
    + */
    +goog.ui.editor.AbstractDialog.prototype.handleAfterHide_ = function() {
    +  this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.html b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.html
    new file mode 100644
    index 0000000000..7109fb4bf3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author nicksantos@google.com (Nick Santos)
    +  @author marcosalmeida@google.com (Marcos Almeida)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.editor.AbstractDialog
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.editor.AbstractDialogTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.js b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.js
    new file mode 100644
    index 0000000000..2ad4d7dc86
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/abstractdialog_test.js
    @@ -0,0 +1,483 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.editor.AbstractDialogTest');
    +goog.setTestOnly('goog.ui.editor.AbstractDialogTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers.ArgumentMatcher');
    +goog.require('goog.ui.editor.AbstractDialog');
    +goog.require('goog.userAgent');
    +
    +function shouldRunTests() {
    +  // Test disabled in IE7 due to flakiness. See b/4269021.
    +  return !(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('7'));
    +}
    +
    +var dialog;
    +var builder;
    +
    +var mockCtrl;
    +var mockAfterHideHandler;
    +var mockOkHandler;
    +var mockCancelHandler;
    +var mockCustomButtonHandler;
    +
    +var CUSTOM_EVENT = 'customEvent';
    +var CUSTOM_BUTTON_ID = 'customButton';
    +
    +
    +function setUp() {
    +  mockCtrl = new goog.testing.MockControl();
    +  mockAfterHideHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
    +  mockOkHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
    +  mockCancelHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
    +  mockCustomButtonHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
    +}
    +
    +function tearDown() {
    +  if (dialog) {
    +    mockAfterHideHandler.$setIgnoreUnexpectedCalls(true);
    +    dialog.dispose();
    +  }
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect an AFTER_HIDE event.
    + */
    +function expectAfterHide() {
    +  mockAfterHideHandler.handleEvent(
    +      new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
    +        return arg.type ==
    +               goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE;
    +      }));
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect an OK event.
    + */
    +function expectOk() {
    +  mockOkHandler.handleEvent(new goog.testing.mockmatchers.ArgumentMatcher(
    +      function(arg) {
    +        return arg.type == goog.ui.editor.AbstractDialog.EventType.OK;
    +      }));
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect an OK event and to call
    + * preventDefault when handling it.
    + */
    +function expectOkPreventDefault() {
    +  expectOk();
    +  mockOkHandler.$does(function(e) {
    +    e.preventDefault();
    +  });
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect an OK event and to return false
    + * when handling it.
    + */
    +function expectOkReturnFalse() {
    +  expectOk();
    +  mockOkHandler.$returns(false);
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect a CANCEL event.
    + */
    +function expectCancel() {
    +  mockCancelHandler.handleEvent(new goog.testing.mockmatchers.ArgumentMatcher(
    +      function(arg) {
    +        return arg.type == goog.ui.editor.AbstractDialog.EventType.CANCEL;
    +      }));
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect a custom button event.
    + */
    +function expectCustomButton() {
    +  mockCustomButtonHandler.handleEvent(
    +      new goog.testing.mockmatchers.ArgumentMatcher(function(arg) {
    +        return arg.type == CUSTOM_EVENT;
    +      }));
    +}
    +
    +
    +/**
    + * Helper to create the dialog being tested in each test. Since NewDialog is
    + * abstract, needs to add a concrete version of any abstract methods. Also
    + * creates up the global builder variable which should be set up after the call
    + * to this method.
    + * @return {goog.ui.editor.AbstractDialog} The dialog.
    + */
    +function createTestDialog() {
    +  var dialog = new goog.ui.editor.AbstractDialog(new goog.dom.DomHelper());
    +  builder = new goog.ui.editor.AbstractDialog.Builder(dialog);
    +  dialog.createDialogControl = function() {
    +    return builder.build();
    +  };
    +  dialog.createOkEvent = function(e) {
    +    return new goog.events.Event(goog.ui.editor.AbstractDialog.EventType.OK);
    +  };
    +  dialog.addEventListener(goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE,
    +                          mockAfterHideHandler);
    +  dialog.addEventListener(goog.ui.editor.AbstractDialog.EventType.OK,
    +                          mockOkHandler);
    +  dialog.addEventListener(goog.ui.editor.AbstractDialog.EventType.CANCEL,
    +                          mockCancelHandler);
    +  dialog.addEventListener(CUSTOM_EVENT, mockCustomButtonHandler);
    +  return dialog;
    +}
    +
    +
    +/**
    + * Asserts that the given dialog is open.
    + * @param {string} msg Message to be printed in case of failure.
    + * @param {goog.ui.editor.AbstractDialog} dialog Dialog to be tested.
    + */
    +function assertOpen(msg, dialog) {
    +  assertTrue(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
    +}
    +
    +
    +/**
    + * Asserts that the given dialog is closed.
    + * @param {string} msg Message to be printed in case of failure.
    + * @param {goog.ui.editor.AbstractDialog} dialog Dialog to be tested.
    + */
    +function assertNotOpen(msg, dialog) {
    +  assertFalse(msg + ' [AbstractDialog.isOpen()]', dialog && dialog.isOpen());
    +}
    +
    +
    +/**
    + * Tests that if you create a dialog and hide it without having shown it, no
    + * errors occur.
    + */
    +function testCreateAndHide() {
    +  dialog = createTestDialog();
    +  mockCtrl.$replayAll();
    +
    +  assertNotOpen('Dialog should not be open after creation', dialog);
    +  dialog.hide();
    +  assertNotOpen('Dialog should not be open after hide()', dialog);
    +
    +  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was not dispatched.
    +}
    +
    +
    +/**
    + * Tests that when you show and hide a dialog the flags indicating open are
    + * correct and the AFTER_HIDE event is dispatched (and no errors happen).
    + */
    +function testShowAndHide() {
    +  dialog = createTestDialog();
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  assertNotOpen('Dialog should not be open before show()', dialog);
    +  dialog.show();
    +  assertOpen('Dialog should be open after show()', dialog);
    +  dialog.hide();
    +  assertNotOpen('Dialog should not be open after hide()', dialog);
    +
    +  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched.
    +}
    +
    +
    +/**
    + * Tests that when you show and dispose a dialog (without hiding it first) the
    + * flags indicating open are correct and the AFTER_HIDE event is dispatched (and
    + * no errors happen).
    + */
    +function testShowAndDispose() {
    +  dialog = createTestDialog();
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  assertNotOpen('Dialog should not be open before show()', dialog);
    +  dialog.show();
    +  assertOpen('Dialog should be open after show()', dialog);
    +  dialog.dispose();
    +  assertNotOpen('Dialog should not be open after dispose()', dialog);
    +
    +  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was dispatched.
    +}
    +
    +
    +/**
    + * Tests that when you dispose a dialog (without ever showing it first) the
    + * flags indicating open are correct and the AFTER_HIDE event is never
    + * dispatched (and no errors happen).
    + */
    +function testDisposeWithoutShow() {
    +  dialog = createTestDialog();
    +  mockCtrl.$replayAll();
    +
    +  assertNotOpen('Dialog should not be open before dispose()', dialog);
    +  dialog.dispose();
    +  assertNotOpen('Dialog should not be open after dispose()', dialog);
    +
    +  mockCtrl.$verifyAll(); // Verifies AFTER_HIDE was NOT dispatched.
    +}
    +
    +
    +/**
    + * Tests that labels set in the builder can be found in the resulting dialog's
    + * HTML.
    + */
    +function testBasicLayout() {
    +  dialog = createTestDialog();
    +  mockCtrl.$replayAll();
    +
    +  // create some dialog content
    +  var content = goog.dom.createDom('div', null, 'The Content');
    +  builder.setTitle('The Title')
    +      .setContent(content)
    +      .addOkButton('The OK Button')
    +      .addCancelButton()
    +      .addButton('The Apply Button', goog.nullFunction)
    +      .addClassName('myClassName');
    +  dialog.show();
    +
    +  var dialogElem = dialog.dialogInternal_.getElement();
    +  var html = dialogElem.innerHTML;
    +  // TODO(user): This is really insufficient. If the title and content
    +  // were swapped this test would still pass!
    +  assertContains('Dialog html should contain title', '>The Title<', html);
    +  assertContains('Dialog html should contain content', '>The Content<', html);
    +  assertContains('Dialog html should contain custom OK button label',
    +                 '>The OK Button<',
    +                 html);
    +  assertContains('Dialog html should contain default Cancel button label',
    +                 '>Cancel<',
    +                 html);
    +  assertContains('Dialog html should contain custom button label',
    +                 '>The Apply Button<',
    +                 html);
    +  assertTrue('Dialog should have default Closure class',
    +             goog.dom.classlist.contains(dialogElem, 'modal-dialog'));
    +  assertTrue('Dialog should have our custom class',
    +             goog.dom.classlist.contains(dialogElem, 'myClassName'));
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking the OK button dispatches the OK event and closes the
    + * dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testOk() {
    +  dialog = createTestDialog();
    +  expectOk(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +  assertNotOpen('Dialog should not be open after clicking OK', dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that hitting the enter key dispatches the OK event and closes the
    + * dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testEnter() {
    +  dialog = createTestDialog();
    +  expectOk(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireKeySequence(dialog.dialogInternal_.getElement(),
    +                                      goog.events.KeyCodes.ENTER);
    +  assertNotOpen('Dialog should not be open after hitting enter', dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking the Cancel button dispatches the CANCEL event and closes
    + * the dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testCancel() {
    +  dialog = createTestDialog();
    +  expectCancel(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  builder.addCancelButton('My Cancel Button');
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(dialog.getCancelButtonElement());
    +  assertNotOpen('Dialog should not be open after clicking Cancel', dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that hitting the escape key dispatches the CANCEL event and closes
    + * the dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testEscape() {
    +  dialog = createTestDialog();
    +  expectCancel(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireKeySequence(dialog.dialogInternal_.getElement(),
    +                                      goog.events.KeyCodes.ESC);
    +  assertNotOpen('Dialog should not be open after hitting escape', dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking the custom button dispatches the custom event and closes
    + * the dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testCustomButton() {
    +  dialog = createTestDialog();
    +  expectCustomButton(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  builder.addButton('My Custom Button',
    +      function() {
    +        dialog.dispatchEvent(CUSTOM_EVENT);
    +      },
    +      CUSTOM_BUTTON_ID);
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(
    +      dialog.getButtonElement(CUSTOM_BUTTON_ID));
    +  assertNotOpen('Dialog should not be open after clicking custom button',
    +                dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that if the OK handler calls preventDefault, the dialog doesn't close.
    + */
    +function testOkPreventDefault() {
    +  dialog = createTestDialog();
    +  expectOkPreventDefault(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +  assertOpen('Dialog should not be closed because preventDefault was called',
    +             dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that if the OK handler returns false, the dialog doesn't close.
    + */
    +function testOkReturnFalse() {
    +  dialog = createTestDialog();
    +  expectOkReturnFalse(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +  assertOpen('Dialog should not be closed because handler returned false',
    +             dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that if creating the OK event fails, no event is dispatched and the
    + * dialog doesn't close.
    + */
    +function testCreateOkEventFail() {
    +  dialog = createTestDialog();
    +  dialog.createOkEvent = function() { // Override our mock createOkEvent.
    +    return null;
    +  };
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +  assertOpen('Dialog should not be closed because OK event creation failed',
    +             dialog);
    +
    +  mockCtrl.$verifyAll(); // Verifies that no event was dispatched.
    +}
    +
    +
    +/**
    + * Tests that processOkAndClose() dispatches the OK event and closes the
    + * dialog (dispatching the AFTER_HIDE event too).
    + */
    +function testProcessOkAndClose() {
    +  dialog = createTestDialog();
    +  expectOk(dialog);
    +  expectAfterHide(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  dialog.processOkAndClose();
    +  assertNotOpen('Dialog should not be open after processOkAndClose()', dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that if the OK handler triggered by processOkAndClose calls
    + * preventDefault, the dialog doesn't close (in the old implementation this
    + * failed due to not great design, so this is sort of a regression test).
    + */
    +function testProcessOkAndClosePreventDefault() {
    +  dialog = createTestDialog();
    +  expectOkPreventDefault(dialog);
    +  mockCtrl.$replayAll();
    +
    +  dialog.show();
    +  dialog.processOkAndClose();
    +  assertOpen('Dialog should not be closed because preventDefault was called',
    +      dialog);
    +
    +  mockCtrl.$verifyAll();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/bubble.js b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble.js
    new file mode 100644
    index 0000000000..2259aaf558
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble.js
    @@ -0,0 +1,559 @@
    +// Copyright 2005 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Bubble component - handles display, hiding, etc. of the
    + * actual bubble UI.
    + *
    + * This is used exclusively by code within the editor package, and should not
    + * be used directly.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.editor.Bubble');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.ViewportSizeMonitor');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.editor.style');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.log');
    +goog.require('goog.math.Box');
    +goog.require('goog.object');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Property bubble UI element.
    + * @param {Element} parent The parent element for this bubble.
    + * @param {number} zIndex The z index to draw the bubble at.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.editor.Bubble = function(parent, zIndex) {
    +  goog.ui.editor.Bubble.base(this, 'constructor');
    +
    +  /**
    +   * Dom helper for the document the bubble should be shown in.
    +   * @type {!goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = goog.dom.getDomHelper(parent);
    +
    +  /**
    +   * Event handler for this bubble.
    +   * @type {goog.events.EventHandler<!goog.ui.editor.Bubble>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * Object that monitors the application window for size changes.
    +   * @type {goog.dom.ViewportSizeMonitor}
    +   * @private
    +   */
    +  this.viewPortSizeMonitor_ = new goog.dom.ViewportSizeMonitor(
    +      this.dom_.getWindow());
    +
    +  /**
    +   * Maps panel ids to panels.
    +   * @type {Object<goog.ui.editor.Bubble.Panel_>}
    +   * @private
    +   */
    +  this.panels_ = {};
    +
    +  /**
    +   * Container element for the entire bubble.  This may contain elements related
    +   * to look and feel or styling of the bubble.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.bubbleContainer_ =
    +      this.dom_.createDom(goog.dom.TagName.DIV,
    +          {'className': goog.ui.editor.Bubble.BUBBLE_CLASSNAME});
    +
    +  goog.style.setElementShown(this.bubbleContainer_, false);
    +  goog.dom.appendChild(parent, this.bubbleContainer_);
    +  goog.style.setStyle(this.bubbleContainer_, 'zIndex', zIndex);
    +
    +  /**
    +   * Container element for the bubble panels - this should be some inner element
    +   * within (or equal to) bubbleContainer.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.bubbleContents_ = this.createBubbleDom(this.dom_, this.bubbleContainer_);
    +
    +  /**
    +   * Element showing the close box.
    +   * @type {!Element}
    +   * @private
    +   */
    +  this.closeBox_ = this.dom_.createDom(goog.dom.TagName.DIV, {
    +    'className': goog.getCssName('tr_bubble_closebox'),
    +    'innerHTML': '&nbsp;'
    +  });
    +  this.bubbleContents_.appendChild(this.closeBox_);
    +
    +  // We make bubbles unselectable so that clicking on them does not steal focus
    +  // or move the cursor away from the element the bubble is attached to.
    +  goog.editor.style.makeUnselectable(this.bubbleContainer_, this.eventHandler_);
    +
    +  /**
    +   * Popup that controls showing and hiding the bubble at the appropriate
    +   * position.
    +   * @type {goog.ui.PopupBase}
    +   * @private
    +   */
    +  this.popup_ = new goog.ui.PopupBase(this.bubbleContainer_);
    +};
    +goog.inherits(goog.ui.editor.Bubble, goog.events.EventTarget);
    +
    +
    +/**
    + * The css class name of the bubble container element.
    + * @type {string}
    + */
    +goog.ui.editor.Bubble.BUBBLE_CLASSNAME = goog.getCssName('tr_bubble');
    +
    +
    +/**
    + * Creates and adds DOM for the bubble UI to the given container.  This default
    + * implementation just returns the container itself.
    + * @param {!goog.dom.DomHelper} dom DOM helper to use.
    + * @param {!Element} container Element to add the new elements to.
    + * @return {!Element} The element where bubble content should be added.
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.createBubbleDom = function(dom, container) {
    +  return container;
    +};
    +
    +
    +/**
    + * A logger for goog.ui.editor.Bubble.
    + * @type {goog.log.Logger}
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.logger =
    +    goog.log.getLogger('goog.ui.editor.Bubble');
    +
    +
    +/** @override */
    +goog.ui.editor.Bubble.prototype.disposeInternal = function() {
    +  goog.ui.editor.Bubble.base(this, 'disposeInternal');
    +
    +  goog.dom.removeNode(this.bubbleContainer_);
    +  this.bubbleContainer_ = null;
    +
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +
    +  this.viewPortSizeMonitor_.dispose();
    +  this.viewPortSizeMonitor_ = null;
    +};
    +
    +
    +/**
    + * @return {Element} The element that where the bubble's contents go.
    + */
    +goog.ui.editor.Bubble.prototype.getContentElement = function() {
    +  return this.bubbleContents_;
    +};
    +
    +
    +/**
    + * @return {Element} The element that contains the bubble.
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.getContainerElement = function() {
    +  return this.bubbleContainer_;
    +};
    +
    +
    +/**
    + * @return {goog.events.EventHandler<T>} The event handler.
    + * @protected
    + * @this T
    + * @template T
    + */
    +goog.ui.editor.Bubble.prototype.getEventHandler = function() {
    +  return this.eventHandler_;
    +};
    +
    +
    +/**
    + * Handles user resizing of window.
    + * @private
    + */
    +goog.ui.editor.Bubble.prototype.handleWindowResize_ = function() {
    +  if (this.isVisible()) {
    +    this.reposition();
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the bubble dismisses itself when the user clicks outside of it.
    + * @param {boolean} autoHide Whether to autohide on an external click.
    + */
    +goog.ui.editor.Bubble.prototype.setAutoHide = function(autoHide) {
    +  this.popup_.setAutoHide(autoHide);
    +};
    +
    +
    +/**
    + * Returns whether there is already a panel of the given type.
    + * @param {string} type Type of panel to check.
    + * @return {boolean} Whether there is already a panel of the given type.
    + */
    +goog.ui.editor.Bubble.prototype.hasPanelOfType = function(type) {
    +  return goog.object.some(this.panels_, function(panel) {
    +    return panel.type == type;
    +  });
    +};
    +
    +
    +/**
    + * Adds a panel to the bubble.
    + * @param {string} type The type of bubble panel this is.  Should usually be
    + *     the same as the tagName of the targetElement.  This ensures multiple
    + *     bubble panels don't appear for the same element.
    + * @param {string} title The title of the panel.
    + * @param {Element} targetElement The target element of the bubble.
    + * @param {function(Element): void} contentFn Function that when called with
    + *     a container element, will add relevant panel content to it.
    + * @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble
    + *     above the element instead of below it.  Defaults to preferring below.
    + *     If any panel prefers the top position, the top position is used.
    + * @return {string} The id of the panel.
    + */
    +goog.ui.editor.Bubble.prototype.addPanel = function(type, title, targetElement,
    +    contentFn, opt_preferTopPosition) {
    +  var id = goog.string.createUniqueString();
    +  var panel = new goog.ui.editor.Bubble.Panel_(this.dom_, id, type, title,
    +      targetElement, !opt_preferTopPosition);
    +  this.panels_[id] = panel;
    +
    +  // Insert the panel in string order of type.  Technically we could use binary
    +  // search here but n is really small (probably 0 - 2) so it's not worth it.
    +  // The last child of bubbleContents_ is the close box so we take care not
    +  // to treat it as a panel element, and we also ensure it stays as the last
    +  // element.  The intention here is not to create any artificial order, but
    +  // just to ensure that it is always consistent.
    +  var nextElement;
    +  for (var i = 0, len = this.bubbleContents_.childNodes.length - 1; i < len;
    +       i++) {
    +    var otherChild = this.bubbleContents_.childNodes[i];
    +    var otherPanel = this.panels_[otherChild.id];
    +    if (otherPanel.type > type) {
    +      nextElement = otherChild;
    +      break;
    +    }
    +  }
    +  goog.dom.insertSiblingBefore(panel.element,
    +      nextElement || this.bubbleContents_.lastChild);
    +
    +  contentFn(panel.getContentElement());
    +  goog.editor.style.makeUnselectable(panel.element, this.eventHandler_);
    +
    +  var numPanels = goog.object.getCount(this.panels_);
    +  if (numPanels == 1) {
    +    this.openBubble_();
    +  } else if (numPanels == 2) {
    +    goog.dom.classlist.add(
    +        goog.asserts.assert(this.bubbleContainer_),
    +        goog.getCssName('tr_multi_bubble'));
    +  }
    +  this.reposition();
    +
    +  return id;
    +};
    +
    +
    +/**
    + * Removes the panel with the given id.
    + * @param {string} id The id of the panel.
    + */
    +goog.ui.editor.Bubble.prototype.removePanel = function(id) {
    +  var panel = this.panels_[id];
    +  goog.dom.removeNode(panel.element);
    +  delete this.panels_[id];
    +
    +  var numPanels = goog.object.getCount(this.panels_);
    +  if (numPanels <= 1) {
    +    goog.dom.classlist.remove(
    +        goog.asserts.assert(this.bubbleContainer_),
    +        goog.getCssName('tr_multi_bubble'));
    +  }
    +
    +  if (numPanels == 0) {
    +    this.closeBubble_();
    +  } else {
    +    this.reposition();
    +  }
    +};
    +
    +
    +/**
    + * Opens the bubble.
    + * @private
    + */
    +goog.ui.editor.Bubble.prototype.openBubble_ = function() {
    +  this.eventHandler_.
    +      listen(this.closeBox_, goog.events.EventType.CLICK,
    +          this.closeBubble_).
    +      listen(this.viewPortSizeMonitor_,
    +          goog.events.EventType.RESIZE, this.handleWindowResize_).
    +      listen(this.popup_, goog.ui.PopupBase.EventType.HIDE,
    +          this.handlePopupHide);
    +
    +  this.popup_.setVisible(true);
    +  this.reposition();
    +};
    +
    +
    +/**
    + * Closes the bubble.
    + * @private
    + */
    +goog.ui.editor.Bubble.prototype.closeBubble_ = function() {
    +  this.popup_.setVisible(false);
    +};
    +
    +
    +/**
    + * Handles the popup's hide event by removing all panels and dispatching a
    + * HIDE event.
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.handlePopupHide = function() {
    +  // Remove the panel elements.
    +  for (var panelId in this.panels_) {
    +    goog.dom.removeNode(this.panels_[panelId].element);
    +  }
    +
    +  // Update the state to reflect no panels.
    +  this.panels_ = {};
    +  goog.dom.classlist.remove(
    +      goog.asserts.assert(this.bubbleContainer_),
    +      goog.getCssName('tr_multi_bubble'));
    +
    +  this.eventHandler_.removeAll();
    +  this.dispatchEvent(goog.ui.Component.EventType.HIDE);
    +};
    +
    +
    +/**
    + * Returns the visibility of the bubble.
    + * @return {boolean} True if visible false if not.
    + */
    +goog.ui.editor.Bubble.prototype.isVisible = function() {
    +  return this.popup_.isVisible();
    +};
    +
    +
    +/**
    + * The vertical clearance in pixels between the bottom of the targetElement
    + * and the edge of the bubble.
    + * @type {number}
    + * @private
    + */
    +goog.ui.editor.Bubble.VERTICAL_CLEARANCE_ = goog.userAgent.IE ? 4 : 2;
    +
    +
    +/**
    + * Bubble's margin box to be passed to goog.positioning.
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.editor.Bubble.MARGIN_BOX_ = new goog.math.Box(
    +    goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0,
    +    goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0);
    +
    +
    +/**
    + * Returns the margin box.
    + * @return {goog.math.Box}
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.getMarginBox = function() {
    +  return goog.ui.editor.Bubble.MARGIN_BOX_;
    +};
    +
    +
    +/**
    + * Positions and displays this bubble below its targetElement. Assumes that
    + * the bubbleContainer is already contained in the document object it applies
    + * to.
    + */
    +goog.ui.editor.Bubble.prototype.reposition = function() {
    +  var targetElement = null;
    +  var preferBottomPosition = true;
    +  for (var panelId in this.panels_) {
    +    var panel = this.panels_[panelId];
    +    // We don't care which targetElement we get, so we just take the last one.
    +    targetElement = panel.targetElement;
    +    preferBottomPosition = preferBottomPosition && panel.preferBottomPosition;
    +  }
    +  var status = goog.positioning.OverflowStatus.FAILED;
    +
    +  // Fix for bug when bubbleContainer and targetElement have
    +  // opposite directionality, the bubble should anchor to the END of
    +  // the targetElement instead of START.
    +  var reverseLayout = (goog.style.isRightToLeft(this.bubbleContainer_) !=
    +      goog.style.isRightToLeft(targetElement));
    +
    +  // Try to put the bubble at the bottom of the target unless the plugin has
    +  // requested otherwise.
    +  if (preferBottomPosition) {
    +    status = this.positionAtAnchor_(reverseLayout ?
    +        goog.positioning.Corner.BOTTOM_END :
    +        goog.positioning.Corner.BOTTOM_START,
    +        goog.positioning.Corner.TOP_START,
    +        goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
    +  }
    +
    +  if (status & goog.positioning.OverflowStatus.FAILED) {
    +    // Try to put it at the top of the target if there is not enough
    +    // space at the bottom.
    +    status = this.positionAtAnchor_(reverseLayout ?
    +        goog.positioning.Corner.TOP_END : goog.positioning.Corner.TOP_START,
    +        goog.positioning.Corner.BOTTOM_START,
    +        goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
    +  }
    +
    +  if (status & goog.positioning.OverflowStatus.FAILED) {
    +    // Put it at the bottom again with adjustment if there is no
    +    // enough space at the top.
    +    status = this.positionAtAnchor_(reverseLayout ?
    +        goog.positioning.Corner.BOTTOM_END :
    +        goog.positioning.Corner.BOTTOM_START,
    +        goog.positioning.Corner.TOP_START,
    +        goog.positioning.Overflow.ADJUST_X |
    +        goog.positioning.Overflow.ADJUST_Y);
    +    if (status & goog.positioning.OverflowStatus.FAILED) {
    +      goog.log.warning(this.logger,
    +          'reposition(): positionAtAnchor() failed with ' + status);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * A helper for reposition() - positions the bubble in regards to the position
    + * of the elements the bubble is attached to.
    + * @param {goog.positioning.Corner} targetCorner The corner of
    + *     the target element.
    + * @param {goog.positioning.Corner} bubbleCorner The corner of the bubble.
    + * @param {number} overflow Overflow handling mode bitmap,
    + *     {@see goog.positioning.Overflow}.
    + * @return {number} Status bitmap, {@see goog.positioning.OverflowStatus}.
    + * @private
    + */
    +goog.ui.editor.Bubble.prototype.positionAtAnchor_ = function(
    +    targetCorner, bubbleCorner, overflow) {
    +  var targetElement = null;
    +  for (var panelId in this.panels_) {
    +    // For now, we use the outermost element.  This assumes the multiple
    +    // elements this panel is showing for contain each other - in the event
    +    // that is not generally the case this may need to be updated to pick
    +    // the lowest or highest element depending on targetCorner.
    +    var candidate = this.panels_[panelId].targetElement;
    +    if (!targetElement || goog.dom.contains(candidate, targetElement)) {
    +      targetElement = this.panels_[panelId].targetElement;
    +    }
    +  }
    +  return goog.positioning.positionAtAnchor(
    +      targetElement, targetCorner, this.bubbleContainer_,
    +      bubbleCorner, null, this.getMarginBox(), overflow,
    +      null, this.getViewportBox());
    +};
    +
    +
    +/**
    + * Returns the viewport box to use when positioning the bubble.
    + * @return {goog.math.Box}
    + * @protected
    + */
    +goog.ui.editor.Bubble.prototype.getViewportBox = goog.functions.NULL;
    +
    +
    +
    +/**
    + * Private class used to describe a bubble panel.
    + * @param {goog.dom.DomHelper} dom DOM helper used to create the panel.
    + * @param {string} id ID of the panel.
    + * @param {string} type Type of the panel.
    + * @param {string} title Title of the panel.
    + * @param {Element} targetElement Element the panel is showing for.
    + * @param {boolean} preferBottomPosition Whether this panel prefers to show
    + *     below the target element.
    + * @constructor
    + * @private
    + */
    +goog.ui.editor.Bubble.Panel_ = function(dom, id, type, title, targetElement,
    +    preferBottomPosition) {
    +  /**
    +   * The type of bubble panel.
    +   * @type {string}
    +   */
    +  this.type = type;
    +
    +  /**
    +   * The target element of this bubble panel.
    +   * @type {Element}
    +   */
    +  this.targetElement = targetElement;
    +
    +  /**
    +   * Whether the panel prefers to be placed below the target element.
    +   * @type {boolean}
    +   */
    +  this.preferBottomPosition = preferBottomPosition;
    +
    +  /**
    +   * The element containing this panel.
    +   */
    +  this.element = dom.createDom(goog.dom.TagName.DIV,
    +      {className: goog.getCssName('tr_bubble_panel'), id: id},
    +      dom.createDom(goog.dom.TagName.DIV,
    +          {className: goog.getCssName('tr_bubble_panel_title')},
    +          title ? title + ':' : ''), // TODO(robbyw): Does this work in bidi?
    +      dom.createDom(goog.dom.TagName.DIV,
    +          {className: goog.getCssName('tr_bubble_panel_content')}));
    +};
    +
    +
    +/**
    + * @return {Element} The element in the panel where content should go.
    + */
    +goog.ui.editor.Bubble.Panel_.prototype.getContentElement = function() {
    +  return /** @type {Element} */ (this.element.lastChild);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.html b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.html
    new file mode 100644
    index 0000000000..c2bfe2a438
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author tildahl@google.com (Michael Tildahl)
    +  @author robbyw@google.com (Robby Walker)
    +-->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.editor.Bubble
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.editor.BubbleTest');
    +  </script>
    +  <link rel="stylesheet" type="text/css" href="../../css/editor/bubble.css" />
    + </head>
    + <body>
    +  <div id="field"></div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.js b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.js
    new file mode 100644
    index 0000000000..dae1e4f5a0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/bubble_test.js
    @@ -0,0 +1,255 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.editor.BubbleTest');
    +goog.setTestOnly('goog.ui.editor.BubbleTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.editor.Bubble');
    +
    +var testHelper;
    +var fieldDiv;
    +var bubble;
    +var link;
    +var link2;
    +var panelId;
    +
    +function setUpPage() {
    +  fieldDiv = goog.dom.getElement('field');
    +  var viewportSize = goog.dom.getViewportSize();
    +  // Some tests depends on enough size of viewport.
    +  if (viewportSize.width < 600 || viewportSize.height < 440) {
    +    window.moveTo(0, 0);
    +    window.resizeTo(640, 480);
    +  }
    +}
    +
    +function setUp() {
    +  testHelper = new goog.testing.editor.TestHelper(fieldDiv);
    +  testHelper.setUpEditableElement();
    +
    +  bubble = new goog.ui.editor.Bubble(document.body, 999);
    +
    +  fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>' +
    +      '<a href="http://www.google.com">Google2</a>';
    +  link = fieldDiv.firstChild;
    +  link2 = fieldDiv.lastChild;
    +
    +  window.scrollTo(0, 0);
    +  goog.style.setStyle(document.body, 'direction', 'ltr');
    +  goog.style.setStyle(document.getElementById('field'), 'position', 'static');
    +}
    +
    +function tearDown() {
    +  if (panelId) {
    +    bubble.removePanel(panelId);
    +  }
    +  testHelper.tearDownEditableElement();
    +}
    +
    +
    +/**
    + * This is a helper function for setting up the target element with a
    + * given direction.
    + *
    + * @param {string} dir The direction of the target element, 'ltr' or 'rtl'.
    + * @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble
    + *     above the element instead of below it.  Defaults to preferring below.
    + */
    +function prepareTargetWithGivenDirection(dir, opt_preferTopPosition) {
    +  goog.style.setStyle(document.body, 'direction', dir);
    +
    +  fieldDiv.style.direction = dir;
    +  fieldDiv.innerHTML = '<a href="http://www.google.com">Google</a>';
    +  link = fieldDiv.firstChild;
    +
    +  panelId = bubble.addPanel('A', 'Link', link, function(el) {
    +    el.innerHTML = '<div style="border:1px solid blue;">B</div>';
    +  }, opt_preferTopPosition);
    +}
    +
    +
    +/**
    + * This is a helper function for getting the expected position of the bubble.
    + * (align to the right or the left of the target element).  Align left by
    + * default and align right if opt_alignRight is true. The expected Y is
    + * unaffected by alignment.
    + *
    + * @param {boolean=} opt_alignRight Sets the expected alignment to be right.
    + */
    +function getExpectedBubblePositionWithGivenAlignment(opt_alignRight) {
    +  var targetPosition = goog.style.getFramedPageOffset(link, window);
    +  var targetWidth = link.offsetWidth;
    +  var bubbleSize = goog.style.getSize(bubble.bubbleContainer_);
    +  var expectedBubbleX = opt_alignRight ?
    +      targetPosition.x + targetWidth - bubbleSize.width : targetPosition.x;
    +  var expectedBubbleY = link.offsetHeight + targetPosition.y +
    +      goog.ui.editor.Bubble.VERTICAL_CLEARANCE_;
    +
    +  return {
    +    x: expectedBubbleX,
    +    y: expectedBubbleY
    +  };
    +}
    +
    +function testCreateBubbleWithLinkPanel() {
    +  var id = goog.string.createUniqueString();
    +  panelId = bubble.addPanel('A', 'Link', link, function(container) {
    +    container.innerHTML = '<span id="' + id + '">Test</span>';
    +  });
    +  assertNotNull('Bubble should be created', bubble.bubbleContents_);
    +  assertNotNull('Added element should be present', goog.dom.getElement(id));
    +  assertTrue('Bubble should be visible', bubble.isVisible());
    +}
    +
    +function testCloseBubble() {
    +  testCreateBubbleWithLinkPanel();
    +
    +  var count = 0;
    +  goog.events.listen(bubble, goog.ui.Component.EventType.HIDE, function() {
    +    count++;
    +  });
    +
    +  bubble.removePanel(panelId);
    +  panelId = null;
    +
    +  assertFalse('Bubble should not be visible', bubble.isVisible());
    +  assertEquals('Hide event should be dispatched', 1, count);
    +}
    +
    +function testCloseBox() {
    +  testCreateBubbleWithLinkPanel();
    +
    +  var count = 0;
    +  goog.events.listen(bubble, goog.ui.Component.EventType.HIDE, function() {
    +    count++;
    +  });
    +
    +  var closeBox = goog.dom.getElementsByTagNameAndClass(
    +      goog.dom.TagName.DIV, 'tr_bubble_closebox', bubble.bubbleContainer_)[0];
    +  goog.testing.events.fireClickSequence(closeBox);
    +  panelId = null;
    +
    +  assertFalse('Bubble should not be visible', bubble.isVisible());
    +  assertEquals('Hide event should be dispatched', 1, count);
    +}
    +
    +function testViewPortSizeMonitorEvent() {
    +  testCreateBubbleWithLinkPanel();
    +
    +  var numCalled = 0;
    +  bubble.reposition = function() {
    +    numCalled++;
    +  };
    +
    +  assertNotUndefined('viewPortSizeMonitor_ should not be undefined',
    +      bubble.viewPortSizeMonitor_);
    +  bubble.viewPortSizeMonitor_.dispatchEvent(goog.events.EventType.RESIZE);
    +
    +  assertEquals('reposition not called', 1, numCalled);
    +}
    +
    +function testBubblePositionPreferTop() {
    +  called = false;
    +  bubble.positionAtAnchor_ = function(targetCorner, bubbleCorner, overflow) {
    +    called = true;
    +
    +    // Assert that the bubble is positioned below the target.
    +    assertEquals(goog.positioning.Corner.TOP_START, targetCorner);
    +    assertEquals(goog.positioning.Corner.BOTTOM_START, bubbleCorner);
    +
    +    return goog.positioning.OverflowStatus.NONE;
    +  };
    +  prepareTargetWithGivenDirection('ltr', true);
    +  assertTrue(called);
    +}
    +
    +function testBubblePosition() {
    +  panelId = bubble.addPanel('A', 'Link', link, goog.nullFunction);
    +  var CLEARANCE = goog.ui.editor.Bubble.VERTICAL_CLEARANCE_;
    +  var bubbleContainer = bubble.bubbleContainer_;
    +
    +  // The field is at a normal place, alomost the top of the viewport, and
    +  // there is enough space at the bottom of the field.
    +  var targetPos = goog.style.getFramedPageOffset(link, window);
    +  var targetSize = goog.style.getSize(link);
    +  var pos = goog.style.getFramedPageOffset(bubbleContainer);
    +  assertEquals(targetPos.y + targetSize.height + CLEARANCE, pos.y);
    +  assertEquals(targetPos.x, pos.x);
    +
    +  // Move the target to the bottom of the viewport.
    +  var field = document.getElementById('field');
    +  var fieldPos = goog.style.getFramedPageOffset(field, window);
    +  fieldPos.y += bubble.dom_.getViewportSize().height -
    +      (targetPos.y + targetSize.height);
    +  goog.style.setStyle(field, 'position', 'absolute');
    +  goog.style.setPosition(field, fieldPos);
    +  bubble.reposition();
    +  var bubbleSize = goog.style.getSize(bubbleContainer);
    +  targetPosition = goog.style.getFramedPageOffset(link, window);
    +  pos = goog.style.getFramedPageOffset(bubbleContainer);
    +  assertEquals(targetPosition.y - CLEARANCE - bubbleSize.height, pos.y);
    +}
    +
    +function testBubblePositionRightAligned() {
    +  prepareTargetWithGivenDirection('rtl');
    +
    +  var expectedPos = getExpectedBubblePositionWithGivenAlignment(true);
    +  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
    +  assertRoughlyEquals(expectedPos.x, pos.x, 0.1);
    +  assertRoughlyEquals(expectedPos.y, pos.y, 0.1);
    +}
    +
    +
    +/**
    + * Test for bug 1955511, the bubble should align to the right side
    + * of the target element when the bubble is RTL, regardless of the
    + * target element's directionality.
    + */
    +function testBubblePositionLeftToRight() {
    +  goog.style.setStyle(bubble.bubbleContainer_, 'direction', 'ltr');
    +  prepareTargetWithGivenDirection('rtl');
    +
    +  var expectedPos = getExpectedBubblePositionWithGivenAlignment();
    +  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
    +  assertRoughlyEquals(expectedPos.x, pos.x, 0.1);
    +  assertRoughlyEquals(expectedPos.y, pos.y, 0.1);
    +}
    +
    +
    +/**
    + * Test for bug 1955511, the bubble should align to the left side
    + * of the target element when the bubble is LTR, regardless of the
    + * target element's directionality.
    + */
    +function testBubblePositionRightToLeft() {
    +  goog.style.setStyle(bubble.bubbleContainer_, 'direction', 'rtl');
    +  prepareTargetWithGivenDirection('ltr');
    +
    +  var expectedPos = getExpectedBubblePositionWithGivenAlignment(true);
    +  var pos = goog.style.getFramedPageOffset(bubble.bubbleContainer_);
    +  assertEquals(expectedPos.x, pos.x);
    +  assertEquals(expectedPos.y, pos.y);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/defaulttoolbar.js b/mapbox-gl/closure-library/closure/goog/ui/editor/defaulttoolbar.js
    new file mode 100644
    index 0000000000..9ddb3716f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/defaulttoolbar.js
    @@ -0,0 +1,1066 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Factory functions for creating a default editing toolbar.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../../demos/editor/editor.html
    + */
    +
    +goog.provide('goog.ui.editor.ButtonDescriptor');
    +goog.provide('goog.ui.editor.DefaultToolbar');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.editor.Command');
    +goog.require('goog.style');
    +goog.require('goog.ui.editor.ToolbarFactory');
    +goog.require('goog.ui.editor.messages');
    +goog.require('goog.userAgent');
    +
    +// Font menu creation.
    +
    +
    +/** @desc Font menu item caption for the default sans-serif font. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL = goog.getMsg('Normal');
    +
    +
    +/** @desc Font menu item caption for the default serif font. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL_SERIF =
    +    goog.getMsg('Normal / serif');
    +
    +
    +/**
    + * Common font descriptors for all locales.  Each descriptor has the following
    + * attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the font menu (e.g. 'Tahoma')
    + *   <li>{@code value} - Value for the corresponding 'font-family' CSS style
    + *       (e.g. 'Tahoma, Arial, sans-serif')
    + * </ul>
    + * @type {!Array<{caption:string, value:string}>}
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.FONTS_ = [
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL,
    +    value: 'arial,sans-serif'
    +  },
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL_SERIF,
    +    value: 'times new roman,serif'
    +  },
    +  {caption: 'Courier New', value: 'courier new,monospace'},
    +  {caption: 'Georgia', value: 'georgia,serif'},
    +  {caption: 'Trebuchet', value: 'trebuchet ms,sans-serif'},
    +  {caption: 'Verdana', value: 'verdana,sans-serif'}
    +];
    +
    +
    +/**
    + * Locale-specific font descriptors.  The object is a map of locale strings to
    + * arrays of font descriptors.
    + * @type {!Object<!Array<{caption:string, value:string}>>}
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.I18N_FONTS_ = {
    +  'ja': [{
    +    caption: '\uff2d\uff33 \uff30\u30b4\u30b7\u30c3\u30af',
    +    value: 'ms pgothic,sans-serif'
    +  }, {
    +    caption: '\uff2d\uff33 \uff30\u660e\u671d',
    +    value: 'ms pmincho,serif'
    +  }, {
    +    caption: '\uff2d\uff33 \u30b4\u30b7\u30c3\u30af',
    +    value: 'ms gothic,monospace'
    +  }],
    +  'ko': [{
    +    caption: '\uad74\ub9bc',
    +    value: 'gulim,sans-serif'
    +  }, {
    +    caption: '\ubc14\ud0d5',
    +    value: 'batang,serif'
    +  }, {
    +    caption: '\uad74\ub9bc\uccb4',
    +    value: 'gulimche,monospace'
    +  }],
    +  'zh-tw': [{
    +    caption: '\u65b0\u7d30\u660e\u9ad4',
    +    value: 'pmingliu,serif'
    +  }, {
    +    caption: '\u7d30\u660e\u9ad4',
    +    value: 'mingliu,serif'
    +  }],
    +  'zh-cn': [{
    +    caption: '\u5b8b\u4f53',
    +    value: 'simsun,serif'
    +  }, {
    +    caption: '\u9ed1\u4f53',
    +    value: 'simhei,sans-serif'
    +  }, {
    +    caption: 'MS Song',
    +    value: 'ms song,monospace'
    +  }]
    +};
    +
    +
    +/**
    + * Default locale for font names.
    + * @type {string}
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.locale_ = 'en-us';
    +
    +
    +/**
    + * Sets the locale for the font names.  If not set, defaults to 'en-us'.
    + * Used only for default creation of font names name.  Must be set
    + * before font name menu is created.
    + * @param {string} locale Locale to use for the toolbar font names.
    + */
    +goog.ui.editor.DefaultToolbar.setLocale = function(locale) {
    +  goog.ui.editor.DefaultToolbar.locale_ = locale;
    +};
    +
    +
    +/**
    + * Initializes the given font menu button by adding default fonts to the menu.
    + * If goog.ui.editor.DefaultToolbar.setLocale was called to specify a locale
    + * for which locale-specific default fonts exist, those are added before
    + * common fonts.
    + * @param {!goog.ui.Select} button Font menu button.
    + */
    +goog.ui.editor.DefaultToolbar.addDefaultFonts = function(button) {
    +  // Normalize locale to lowercase, with a hyphen (see bug 1036165).
    +  var locale =
    +      goog.ui.editor.DefaultToolbar.locale_.replace(/_/, '-').toLowerCase();
    +  // Add locale-specific default fonts, if any.
    +  var fontlist = [];
    +
    +  if (locale in goog.ui.editor.DefaultToolbar.I18N_FONTS_) {
    +    fontlist = goog.ui.editor.DefaultToolbar.I18N_FONTS_[locale];
    +  }
    +  if (fontlist.length) {
    +    goog.ui.editor.ToolbarFactory.addFonts(button, fontlist);
    +  }
    +  // Add locale-independent default fonts.
    +  goog.ui.editor.ToolbarFactory.addFonts(button,
    +      goog.ui.editor.DefaultToolbar.FONTS_);
    +};
    +
    +
    +// Font size menu creation.
    +
    +
    +/** @desc Font size menu item caption for the 'Small' size. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_SMALL = goog.getMsg('Small');
    +
    +
    +/** @desc Font size menu item caption for the 'Normal' size. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL = goog.getMsg('Normal');
    +
    +
    +/** @desc Font size menu item caption for the 'Large' size. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_LARGE = goog.getMsg('Large');
    +
    +
    +/** @desc Font size menu item caption for the 'Huge' size. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_HUGE = goog.getMsg('Huge');
    +
    +
    +/**
    + * Font size descriptors, each with the following attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the font size menu (e.g. 'Huge')
    + *   <li>{@code value} - Value for the corresponding HTML font size (e.g. 6)
    + * </ul>
    + * @type {!Array<{caption:string, value:number}>}
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.FONT_SIZES_ = [
    +  {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_SMALL, value: 1},
    +  {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL, value: 2},
    +  {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_LARGE, value: 4},
    +  {caption: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_HUGE, value: 6}
    +];
    +
    +
    +/**
    + * Initializes the given font size menu button by adding default font sizes to
    + * it.
    + * @param {!goog.ui.Select} button Font size menu button.
    + */
    +goog.ui.editor.DefaultToolbar.addDefaultFontSizes = function(button) {
    +  goog.ui.editor.ToolbarFactory.addFontSizes(button,
    +      goog.ui.editor.DefaultToolbar.FONT_SIZES_);
    +};
    +
    +
    +// Header format menu creation.
    +
    +
    +/** @desc Caption for "Heading" block format option. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_HEADING = goog.getMsg('Heading');
    +
    +
    +/** @desc Caption for "Subheading" block format option. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_SUBHEADING = goog.getMsg('Subheading');
    +
    +
    +/** @desc Caption for "Minor heading" block format option. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_MINOR_HEADING =
    +    goog.getMsg('Minor heading');
    +
    +
    +/** @desc Caption for "Normal" block format option. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL = goog.getMsg('Normal');
    +
    +
    +/**
    + * Format option descriptors, each with the following attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the menu (e.g. 'Minor heading')
    + *   <li>{@code command} - Corresponding {@link goog.dom.TagName} (e.g.
    + *       'H4')
    + * </ul>
    + * @type {!Array<{caption: string, command: !goog.dom.TagName}>}
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.FORMAT_OPTIONS_ = [
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_HEADING,
    +    command: goog.dom.TagName.H2
    +  },
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_SUBHEADING,
    +    command: goog.dom.TagName.H3
    +  },
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_MINOR_HEADING,
    +    command: goog.dom.TagName.H4
    +  },
    +  {
    +    caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL,
    +    command: goog.dom.TagName.P
    +  }
    +];
    +
    +
    +/**
    + * Initializes the given "Format block" menu button by adding default format
    + * options to the menu.
    + * @param {!goog.ui.Select} button "Format block" menu button.
    + */
    +goog.ui.editor.DefaultToolbar.addDefaultFormatOptions = function(button) {
    +  goog.ui.editor.ToolbarFactory.addFormatOptions(button,
    +      goog.ui.editor.DefaultToolbar.FORMAT_OPTIONS_);
    +};
    +
    +
    +/**
    + * Creates a {@link goog.ui.Toolbar} containing a default set of editor
    + * toolbar buttons, and renders it into the given parent element.
    + * @param {!Element} elem Toolbar parent element.
    + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is
    + *     right-to-left; defaults to the directionality of the toolbar parent
    + *     element.
    + * @return {!goog.ui.Toolbar} Default editor toolbar, rendered into the given
    + *     parent element.
    + * @see goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS
    + */
    +goog.ui.editor.DefaultToolbar.makeDefaultToolbar = function(elem,
    +    opt_isRightToLeft) {
    +  var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem);
    +  var buttons = isRightToLeft ?
    +      goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS_RTL :
    +      goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS;
    +  return goog.ui.editor.DefaultToolbar.makeToolbar(buttons, elem,
    +      opt_isRightToLeft);
    +};
    +
    +
    +/**
    + * Creates a {@link goog.ui.Toolbar} containing the specified set of
    + * toolbar buttons, and renders it into the given parent element.  Each
    + * item in the {@code items} array must either be a
    + * {@link goog.editor.Command} (to create a built-in button) or a subclass
    + * of {@link goog.ui.Control} (to create a custom control).
    + * @param {!Array<string|goog.ui.Control>} items Toolbar items; each must
    + *     be a {@link goog.editor.Command} or a {@link goog.ui.Control}.
    + * @param {!Element} elem Toolbar parent element.
    + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is
    + *     right-to-left; defaults to the directionality of the toolbar parent
    + *     element.
    + * @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent
    + *     element.
    + */
    +goog.ui.editor.DefaultToolbar.makeToolbar = function(items, elem,
    +    opt_isRightToLeft) {
    +  var domHelper = goog.dom.getDomHelper(elem);
    +  var controls = [];
    +
    +  for (var i = 0, button; button = items[i]; i++) {
    +    if (goog.isString(button)) {
    +      button = goog.ui.editor.DefaultToolbar.makeBuiltInToolbarButton(button,
    +          domHelper);
    +    }
    +    if (button) {
    +      controls.push(button);
    +    }
    +  }
    +
    +  return goog.ui.editor.ToolbarFactory.makeToolbar(controls, elem,
    +      opt_isRightToLeft);
    +};
    +
    +
    +/**
    + * Creates an instance of a subclass of {@link goog.ui.Button} for the given
    + * {@link goog.editor.Command}, or null if no built-in button exists for the
    + * command.  Note that this function is only intended to create built-in
    + * buttons; please don't try to hack it!
    + * @param {string} command Editor command ID.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {goog.ui.Button} Toolbar button (null if no built-in button exists
    + *     for the command).
    + */
    +goog.ui.editor.DefaultToolbar.makeBuiltInToolbarButton = function(command,
    +    opt_domHelper) {
    +  var button;
    +  var descriptor = goog.ui.editor.DefaultToolbar.buttons_[command];
    +  if (descriptor) {
    +    // Default the factory method to makeToggleButton, since most built-in
    +    // toolbar buttons are toggle buttons. See also
    +    // goog.ui.editor.DefaultToolbar.button_list_.
    +    var factory = descriptor.factory ||
    +        goog.ui.editor.ToolbarFactory.makeToggleButton;
    +    var id = descriptor.command;
    +    var tooltip = descriptor.tooltip;
    +    var caption = descriptor.caption;
    +    var classNames = descriptor.classes;
    +    // Default the DOM helper to the one for the current document.
    +    var domHelper = opt_domHelper || goog.dom.getDomHelper();
    +    // Instantiate the button based on the descriptor.
    +    button = factory(id, tooltip, caption, classNames, null, domHelper);
    +    // If this button's state should be queried when updating the toolbar,
    +    // set the button object's queryable property to true.
    +    if (descriptor.queryable) {
    +      button.queryable = true;
    +    }
    +  }
    +  return button;
    +};
    +
    +
    +/**
    + * A set of built-in buttons to display in the default editor toolbar.
    + * @type {!Array<string>}
    + */
    +goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS = [
    +  goog.editor.Command.IMAGE,
    +  goog.editor.Command.LINK,
    +  goog.editor.Command.BOLD,
    +  goog.editor.Command.ITALIC,
    +  goog.editor.Command.UNORDERED_LIST,
    +  goog.editor.Command.FONT_COLOR,
    +  goog.editor.Command.FONT_FACE,
    +  goog.editor.Command.FONT_SIZE,
    +  goog.editor.Command.JUSTIFY_LEFT,
    +  goog.editor.Command.JUSTIFY_CENTER,
    +  goog.editor.Command.JUSTIFY_RIGHT,
    +  goog.editor.Command.EDIT_HTML
    +];
    +
    +
    +/**
    + * A set of built-in buttons to display in the default editor toolbar when
    + * the editor chrome is right-to-left (BiDi mode only).
    + * @type {!Array<string>}
    + */
    +goog.ui.editor.DefaultToolbar.DEFAULT_BUTTONS_RTL = [
    +  goog.editor.Command.IMAGE,
    +  goog.editor.Command.LINK,
    +  goog.editor.Command.BOLD,
    +  goog.editor.Command.ITALIC,
    +  goog.editor.Command.UNORDERED_LIST,
    +  goog.editor.Command.FONT_COLOR,
    +  goog.editor.Command.FONT_FACE,
    +  goog.editor.Command.FONT_SIZE,
    +  goog.editor.Command.JUSTIFY_RIGHT,
    +  goog.editor.Command.JUSTIFY_CENTER,
    +  goog.editor.Command.JUSTIFY_LEFT,
    +  goog.editor.Command.DIR_RTL,
    +  goog.editor.Command.DIR_LTR,
    +  goog.editor.Command.EDIT_HTML
    +];
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element.  This button
    + * is designed to be used as the RTL button.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.rtlButtonFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeToggleButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  button.updateFromValue = function(value) {
    +    // Enable/disable right-to-left text editing mode in the toolbar.
    +    var isRtl = !!value;
    +    // Enable/disable a marker class on the toolbar's root element; the rest is
    +    // done using CSS scoping in editortoolbar.css.  This changes
    +    // direction-senitive toolbar icons (like indent/outdent)
    +    goog.dom.classlist.enable(
    +        goog.asserts.assert(button.getParent().getElement()),
    +        goog.getCssName('tr-rtl-mode'), isRtl);
    +    button.setChecked(isRtl);
    +  };
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element.  Designed to
    + * be used to create undo and redo buttons.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  button.updateFromValue = function(value) {
    +    button.setEnabled(value);
    +  };
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element.  Used to create
    + * a font face button, filled with default fonts.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; defaults
    + *     to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.fontFaceFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeSelectButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  goog.ui.editor.DefaultToolbar.addDefaultFonts(button);
    +  button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FONT_NORMAL);
    +  // Font options don't have keyboard accelerators.
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(button.getMenu().getContentElement()),
    +      goog.getCssName('goog-menu-noaccel'));
    +
    +  // How to update this button's state.
    +  button.updateFromValue = function(value) {
    +    // Normalize value to null or a non-empty string (sometimes we get
    +    // the empty string, sometimes we get false...), extract the substring
    +    // up to the first comma to get the primary font name, and normalize
    +    // to lowercase.  This allows us to map a font spec like "Arial,
    +    // Helvetica, sans-serif" to a font menu item.
    +    // TODO (attila): Try to make this more robust.
    +    var item = null;
    +    if (value && value.length > 0) {
    +      item = /** @type {goog.ui.MenuItem} */ (button.getMenu().getChild(
    +          goog.ui.editor.ToolbarFactory.getPrimaryFont(value)));
    +    }
    +    var selectedItem = button.getSelectedItem();
    +    if (item != selectedItem) {
    +      button.setSelectedItem(item);
    +    }
    +  };
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element. Use to create a
    + * font size button, filled with default font sizes.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer; defaults
    + *     to {@link goog.ui.ToolbarMebuButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.fontSizeFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeSelectButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  goog.ui.editor.DefaultToolbar.addDefaultFontSizes(button);
    +  button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_NORMAL);
    +  // Font size options don't have keyboard accelerators.
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(button.getMenu().getContentElement()),
    +      goog.getCssName('goog-menu-noaccel'));
    +  // How to update this button's state.
    +  button.updateFromValue = function(value) {
    +    // Webkit pre-534.7 returns a string like '32px' instead of the equivalent
    +    // integer, so normalize that first.
    +    // NOTE(user): Gecko returns "6" so can't just normalize all
    +    // strings, only ones ending in "px".
    +    if (goog.isString(value) &&
    +        goog.style.getLengthUnits(value) == 'px') {
    +      value = goog.ui.editor.ToolbarFactory.getLegacySizeFromPx(
    +          parseInt(value, 10));
    +    }
    +    // Normalize value to null or a positive integer (sometimes we get
    +    // the empty string, sometimes we get false, or -1 if the above
    +    // normalization didn't match to a particular 0-7 size)
    +    value = value > 0 ? value : null;
    +    if (value != button.getValue()) {
    +      button.setValue(value);
    +    }
    +  };
    +  return button;
    +};
    +
    +
    +/**
    + * Function to update the state of a color menu button.
    + * @param {goog.ui.ToolbarColorMenuButton} button The button to which the
    + *     color menu is attached.
    + * @param {number} color Color value to update to.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.colorUpdateFromValue_ = function(button, color) {
    +  var value = color;
    +  /** @preserveTry */
    +  try {
    +    if (goog.userAgent.IE) {
    +      // IE returns a number that, converted to hex, is a BGR color.
    +      // Convert from decimal to BGR to RGB.
    +      var hex = '000000' + value.toString(16);
    +      var bgr = hex.substr(hex.length - 6, 6);
    +      value = '#' + bgr.substring(4, 6) + bgr.substring(2, 4) +
    +          bgr.substring(0, 2);
    +    }
    +    if (value != button.getValue()) {
    +      button.setValue(/** @type {string} */ (value));
    +    }
    +  } catch (ex) {
    +    // TODO(attila): Find out when/why this happens.
    +  }
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element. Use to create
    + * a font color button.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer} if
    + *     unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.fontColorFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeColorMenuButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  // Initialize default foreground color.
    +  button.setSelectedColor('#000');
    +  button.updateFromValue = goog.partial(
    +      goog.ui.editor.DefaultToolbar.colorUpdateFromValue_, button);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element. Use to create
    + * a font background color button.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer} if
    + *     unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.backgroundColorFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeColorMenuButton(id,
    +      tooltip, caption, opt_classNames, opt_renderer, opt_domHelper);
    +  // Initialize default background color.
    +  button.setSelectedColor('#FFF');
    +  button.updateFromValue = goog.partial(
    +      goog.ui.editor.DefaultToolbar.colorUpdateFromValue_, button);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element. Use to create
    + * the format menu, prefilled with default formats.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to
    + *     {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + * @private
    + */
    +goog.ui.editor.DefaultToolbar.formatBlockFactory_ = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeSelectButton(id, tooltip,
    +      caption, opt_classNames, opt_renderer, opt_domHelper);
    +  goog.ui.editor.DefaultToolbar.addDefaultFormatOptions(button);
    +  button.setDefaultCaption(goog.ui.editor.DefaultToolbar.MSG_FORMAT_NORMAL);
    +  // Format options don't have keyboard accelerators.
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(button.getMenu().getContentElement()),
    +      goog.getCssName('goog-menu-noaccel'));
    +  // How to update this button.
    +  button.updateFromValue = function(value) {
    +    // Normalize value to null or a nonempty string (sometimes we get
    +    // the empty string, sometimes we get false...)
    +    value = value && value.length > 0 ? value : null;
    +    if (value != button.getValue()) {
    +      button.setValue(value);
    +    }
    +  };
    +  return button;
    +};
    +
    +
    +// Messages used for tooltips and captions.
    +
    +
    +/** @desc Format menu tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_TITLE = goog.getMsg('Format');
    +
    +
    +/** @desc Format menu caption. */
    +goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_CAPTION = goog.getMsg('Format');
    +
    +
    +/** @desc Undo button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_UNDO_TITLE = goog.getMsg('Undo');
    +
    +
    +/** @desc Redo button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_REDO_TITLE = goog.getMsg('Redo');
    +
    +
    +/** @desc Font menu tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_FACE_TITLE = goog.getMsg('Font');
    +
    +
    +/** @desc Font size menu tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_TITLE = goog.getMsg('Font size');
    +
    +
    +/** @desc Text foreground color menu tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_FONT_COLOR_TITLE = goog.getMsg('Text color');
    +
    +
    +/** @desc Bold button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_BOLD_TITLE = goog.getMsg('Bold');
    +
    +
    +/** @desc Italic button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_ITALIC_TITLE = goog.getMsg('Italic');
    +
    +
    +/** @desc Underline button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_UNDERLINE_TITLE = goog.getMsg('Underline');
    +
    +
    +/** @desc Text background color menu tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_BACKGROUND_COLOR_TITLE =
    +    goog.getMsg('Text background color');
    +
    +
    +/** @desc Link button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_LINK_TITLE =
    +    goog.getMsg('Add or remove link');
    +
    +
    +/** @desc Numbered list button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_ORDERED_LIST_TITLE =
    +    goog.getMsg('Numbered list');
    +
    +
    +/** @desc Bullet list button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_UNORDERED_LIST_TITLE =
    +    goog.getMsg('Bullet list');
    +
    +
    +/** @desc Outdent button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_OUTDENT_TITLE =
    +    goog.getMsg('Decrease indent');
    +
    +
    +/** @desc Indent button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_INDENT_TITLE = goog.getMsg('Increase indent');
    +
    +
    +/** @desc Align left button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_ALIGN_LEFT_TITLE = goog.getMsg('Align left');
    +
    +
    +/** @desc Align center button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_ALIGN_CENTER_TITLE =
    +    goog.getMsg('Align center');
    +
    +
    +/** @desc Align right button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_ALIGN_RIGHT_TITLE =
    +    goog.getMsg('Align right');
    +
    +
    +/** @desc Justify button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_JUSTIFY_TITLE = goog.getMsg('Justify');
    +
    +
    +/** @desc Remove formatting button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_REMOVE_FORMAT_TITLE =
    +    goog.getMsg('Remove formatting');
    +
    +
    +/** @desc Insert image button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_IMAGE_TITLE = goog.getMsg('Insert image');
    +
    +
    +/** @desc Strike through button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_STRIKE_THROUGH_TITLE =
    +    goog.getMsg('Strikethrough');
    +
    +
    +/** @desc Left-to-right button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_DIR_LTR_TITLE = goog.getMsg('Left-to-right');
    +
    +
    +/** @desc Right-to-left button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_DIR_RTL_TITLE = goog.getMsg('Right-to-left');
    +
    +
    +/** @desc Blockquote button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_BLOCKQUOTE_TITLE = goog.getMsg('Quote');
    +
    +
    +/** @desc Edit HTML button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_TITLE =
    +    goog.getMsg('Edit HTML source');
    +
    +
    +/** @desc Subscript button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_SUBSCRIPT = goog.getMsg('Subscript');
    +
    +
    +/** @desc Superscript button tooltip. */
    +goog.ui.editor.DefaultToolbar.MSG_SUPERSCRIPT = goog.getMsg('Superscript');
    +
    +
    +/** @desc Edit HTML button caption. */
    +goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_CAPTION = goog.getMsg('Edit HTML');
    +
    +
    +/**
    + * Map of {@code goog.editor.Command}s to toolbar button descriptor objects,
    + * each of which has the following attributes:
    + * <ul>
    + *   <li>{@code command} - The command corresponding to the
    + *       button (mandatory)
    + *   <li>{@code tooltip} - Tooltip text (optional); if unspecified, the button
    + *       has no hover text
    + *   <li>{@code caption} - Caption to display on the button (optional); if
    + *       unspecified, the button has no text caption
    + *   <li>{@code classes} - CSS class name(s) to be applied to the button's
    + *       element when rendered (optional); if unspecified, defaults to
    + *       'tr-icon'
    + *       plus 'tr-' followed by the command ID, but without any leading '+'
    + *       character (e.g. if the command ID is '+undo', then {@code classes}
    + *       defaults to 'tr-icon tr-undo')
    + *   <li>{@code factory} - factory function used to create the button, which
    + *       must accept {@code id}, {@code tooltip}, {@code caption}, and
    + *       {@code classes} as arguments, and must return an instance of
    + *       {@link goog.ui.Button} or an appropriate subclass (optional); if
    + *       unspecified, defaults to
    + *       {@link goog.ui.editor.DefaultToolbar.makeToggleButton},
    + *       since most built-in toolbar buttons are toggle buttons
    + *   <li>(@code queryable} - Whether the button's state should be queried
    + *       when updating the toolbar (optional).
    + * </ul>
    + * Note that this object is only used for creating toolbar buttons for
    + * built-in editor commands; custom buttons aren't listed here.  Please don't
    + * try to hack this!
    + * @private {!Object<!goog.ui.editor.ButtonDescriptor>}.
    + */
    +goog.ui.editor.DefaultToolbar.buttons_ = {};
    +
    +
    +/**
    + * @typedef {{command: string, tooltip: ?string,
    + *   caption: ?goog.ui.ControlContent, classes: ?string,
    + *   factory: ?function(string, string, goog.ui.ControlContent, ?string,
    + *       goog.ui.ButtonRenderer, goog.dom.DomHelper):goog.ui.Button,
    + *   queryable:?boolean}}
    + */
    +goog.ui.editor.ButtonDescriptor;
    +
    +
    +/**
    + * Built-in toolbar button descriptors.  See
    + * {@link goog.ui.editor.DefaultToolbar.buttons_} for details on button
    + * descriptor objects.  This array is processed at JS parse time; each item is
    + * inserted into {@link goog.ui.editor.DefaultToolbar.buttons_}, and the array
    + * itself is deleted and (hopefully) garbage-collected.
    + * @private {Array<!goog.ui.editor.ButtonDescriptor>}
    + */
    +goog.ui.editor.DefaultToolbar.button_list_ = [{
    +  command: goog.editor.Command.UNDO,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_UNDO_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-undo'),
    +  factory: goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.REDO,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_REDO_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-redo'),
    +  factory: goog.ui.editor.DefaultToolbar.undoRedoButtonFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.FONT_FACE,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_FACE_TITLE,
    +  classes: goog.getCssName('tr-fontName'),
    +  factory: goog.ui.editor.DefaultToolbar.fontFaceFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.FONT_SIZE,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_SIZE_TITLE,
    +  classes: goog.getCssName('tr-fontSize'),
    +  factory: goog.ui.editor.DefaultToolbar.fontSizeFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.BOLD,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_BOLD_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-bold'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.ITALIC,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_ITALIC_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-italic'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.UNDERLINE,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_UNDERLINE_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-underline'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.FONT_COLOR,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_FONT_COLOR_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-foreColor'),
    +  factory: goog.ui.editor.DefaultToolbar.fontColorFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.BACKGROUND_COLOR,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_BACKGROUND_COLOR_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-backColor'),
    +  factory: goog.ui.editor.DefaultToolbar.backgroundColorFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.LINK,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_LINK_TITLE,
    +  caption: goog.ui.editor.messages.MSG_LINK_CAPTION,
    +  classes: goog.getCssName('tr-link'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.ORDERED_LIST,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_ORDERED_LIST_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-insertOrderedList'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.UNORDERED_LIST,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_UNORDERED_LIST_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-insertUnorderedList'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.OUTDENT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_OUTDENT_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-outdent'),
    +  factory: goog.ui.editor.ToolbarFactory.makeButton
    +}, {
    +  command: goog.editor.Command.INDENT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_INDENT_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-indent'),
    +  factory: goog.ui.editor.ToolbarFactory.makeButton
    +}, {
    +  command: goog.editor.Command.JUSTIFY_LEFT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_LEFT_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-justifyLeft'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.JUSTIFY_CENTER,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_CENTER_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-justifyCenter'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.JUSTIFY_RIGHT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_ALIGN_RIGHT_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-justifyRight'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.JUSTIFY_FULL,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_JUSTIFY_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-justifyFull'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.REMOVE_FORMAT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_REMOVE_FORMAT_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-removeFormat'),
    +  factory: goog.ui.editor.ToolbarFactory.makeButton
    +}, {
    +  command: goog.editor.Command.IMAGE,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_IMAGE_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-image'),
    +  factory: goog.ui.editor.ToolbarFactory.makeButton
    +}, {
    +  command: goog.editor.Command.STRIKE_THROUGH,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_STRIKE_THROUGH_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-strikeThrough'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.SUBSCRIPT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_SUBSCRIPT,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-subscript'),
    +  queryable: true
    +} , {
    +  command: goog.editor.Command.SUPERSCRIPT,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_SUPERSCRIPT,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-superscript'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.DIR_LTR,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_DIR_LTR_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-ltr'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.DIR_RTL,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_DIR_RTL_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' + goog.getCssName('tr-rtl'),
    +  factory: goog.ui.editor.DefaultToolbar.rtlButtonFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.BLOCKQUOTE,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_BLOCKQUOTE_TITLE,
    +  classes: goog.getCssName('tr-icon') + ' ' +
    +      goog.getCssName('tr-BLOCKQUOTE'),
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.FORMAT_BLOCK,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_TITLE,
    +  caption: goog.ui.editor.DefaultToolbar.MSG_FORMAT_BLOCK_CAPTION,
    +  classes: goog.getCssName('tr-formatBlock'),
    +  factory: goog.ui.editor.DefaultToolbar.formatBlockFactory_,
    +  queryable: true
    +}, {
    +  command: goog.editor.Command.EDIT_HTML,
    +  tooltip: goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_TITLE,
    +  caption: goog.ui.editor.DefaultToolbar.MSG_EDIT_HTML_CAPTION,
    +  classes: goog.getCssName('tr-editHtml'),
    +  factory: goog.ui.editor.ToolbarFactory.makeButton
    +}];
    +
    +
    +(function() {
    +  // Create the goog.ui.editor.DefaultToolbar.buttons_ map from
    +  // goog.ui.editor.DefaultToolbar.button_list_.
    +  for (var i = 0, button;
    +      button = goog.ui.editor.DefaultToolbar.button_list_[i]; i++) {
    +    goog.ui.editor.DefaultToolbar.buttons_[button.command] = button;
    +  }
    +
    +  // goog.ui.editor.DefaultToolbar.button_list_ is no longer needed
    +  // once the map is ready.
    +  goog.ui.editor.DefaultToolbar.button_list_ = null;
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog.js b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog.js
    new file mode 100644
    index 0000000000..83c63ef150
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog.js
    @@ -0,0 +1,1063 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A dialog for editing/creating a link.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.editor.LinkDialog');
    +goog.provide('goog.ui.editor.LinkDialog.BeforeTestLinkEvent');
    +goog.provide('goog.ui.editor.LinkDialog.EventType');
    +goog.provide('goog.ui.editor.LinkDialog.OkEvent');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.safe');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Link');
    +goog.require('goog.editor.focus');
    +goog.require('goog.editor.node');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.string');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.style');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.LinkButtonRenderer');
    +goog.require('goog.ui.editor.AbstractDialog');
    +goog.require('goog.ui.editor.TabPane');
    +goog.require('goog.ui.editor.messages');
    +goog.require('goog.userAgent');
    +goog.require('goog.window');
    +
    +
    +
    +/**
    + * A type of goog.ui.editor.AbstractDialog for editing/creating a link.
    + * @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
    + *     dialog's dom structure.
    + * @param {goog.editor.Link} link The target link.
    + * @constructor
    + * @extends {goog.ui.editor.AbstractDialog}
    + * @final
    + */
    +goog.ui.editor.LinkDialog = function(domHelper, link) {
    +  goog.ui.editor.LinkDialog.base(this, 'constructor', domHelper);
    +
    +  /**
    +   * The link being modified by this dialog.
    +   * @type {goog.editor.Link}
    +   * @private
    +   */
    +  this.targetLink_ = link;
    +
    +  /**
    +   * The event handler for this dialog.
    +   * @type {goog.events.EventHandler<!goog.ui.editor.LinkDialog>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  /**
    +   * Optional warning to show about email addresses.
    +   * @type {goog.html.SafeHtml}
    +   * @private
    +   */
    +  this.emailWarning_ = null;
    +
    +  /**
    +   * Whether to show a checkbox where the user can choose to have the link open
    +   * in a new window.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.showOpenLinkInNewWindow_ = false;
    +
    +  /**
    +   * Whether the "open link in new window" checkbox should be checked when the
    +   * dialog is shown, and also whether it was checked last time the dialog was
    +   * closed.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isOpenLinkInNewWindowChecked_ = false;
    +
    +  /**
    +   * Whether to show a checkbox where the user can choose to have 'rel=nofollow'
    +   * attribute added to the link.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.showRelNoFollow_ = false;
    +
    +  /**
    +   * InputHandler object to listen for changes in the url input field.
    +   * @type {goog.events.InputHandler}
    +   * @private
    +   */
    +  this.urlInputHandler_ = null;
    +
    +  /**
    +   * InputHandler object to listen for changes in the email input field.
    +   * @type {goog.events.InputHandler}
    +   * @private
    +   */
    +  this.emailInputHandler_ = null;
    +
    +  /**
    +   * InputHandler object to listen for changes in the text to display input
    +   * field.
    +   * @type {goog.events.InputHandler}
    +   * @private
    +   */
    +  this.textInputHandler_ = null;
    +
    +  /**
    +   * The tab bar where the url and email tabs are.
    +   * @type {goog.ui.editor.TabPane}
    +   * @private
    +   */
    +  this.tabPane_ = null;
    +
    +  /**
    +   * The div element holding the link's display text input.
    +   * @type {HTMLDivElement}
    +   * @private
    +   */
    +  this.textToDisplayDiv_ = null;
    +
    +  /**
    +   * The input element holding the link's display text.
    +   * @type {HTMLInputElement}
    +   * @private
    +   */
    +  this.textToDisplayInput_ = null;
    +
    +  /**
    +   * Whether or not the feature of automatically generating the display text is
    +   * enabled.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.autogenFeatureEnabled_ = true;
    +
    +  /**
    +   * Whether or not we should automatically generate the display text.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.autogenerateTextToDisplay_ = false;
    +
    +  /**
    +   * Whether or not automatic generation of the display text is disabled.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.disableAutogen_ = false;
    +
    +  /**
    +   * The input element (checkbox) to indicate that the link should open in a new
    +   * window.
    +   * @type {HTMLInputElement}
    +   * @private
    +   */
    +  this.openInNewWindowCheckbox_ = null;
    +
    +  /**
    +   * The input element (checkbox) to indicate that the link should have
    +   * 'rel=nofollow' attribute.
    +   * @type {HTMLInputElement}
    +   * @private
    +   */
    +  this.relNoFollowCheckbox_ = null;
    +
    +  /**
    +   * Whether to stop leaking the page's url via the referrer header when the
    +   * "test this link" link is clicked.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.stopReferrerLeaks_ = false;
    +};
    +goog.inherits(goog.ui.editor.LinkDialog, goog.ui.editor.AbstractDialog);
    +
    +
    +/**
    + * Events specific to the link dialog.
    + * @enum {string}
    + */
    +goog.ui.editor.LinkDialog.EventType = {
    +  BEFORE_TEST_LINK: 'beforetestlink'
    +};
    +
    +
    +
    +/**
    + * OK event object for the link dialog.
    + * @param {string} linkText Text the user chose to display for the link.
    + * @param {string} linkUrl Url the user chose for the link to point to.
    + * @param {boolean} openInNewWindow Whether the link should open in a new window
    + *     when clicked.
    + * @param {boolean} noFollow Whether the link should have 'rel=nofollow'
    + *     attribute.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.editor.LinkDialog.OkEvent = function(
    +    linkText, linkUrl, openInNewWindow, noFollow) {
    +  goog.ui.editor.LinkDialog.OkEvent.base(this, 'constructor',
    +      goog.ui.editor.AbstractDialog.EventType.OK);
    +
    +  /**
    +   * The text of the link edited in the dialog.
    +   * @type {string}
    +   */
    +  this.linkText = linkText;
    +
    +  /**
    +   * The url of the link edited in the dialog.
    +   * @type {string}
    +   */
    +  this.linkUrl = linkUrl;
    +
    +  /**
    +   * Whether the link should open in a new window when clicked.
    +   * @type {boolean}
    +   */
    +  this.openInNewWindow = openInNewWindow;
    +
    +  /**
    +   * Whether the link should have 'rel=nofollow' attribute.
    +   * @type {boolean}
    +   */
    +  this.noFollow = noFollow;
    +};
    +goog.inherits(goog.ui.editor.LinkDialog.OkEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * Event fired before testing a link by opening it in another window.
    + * Calling preventDefault will stop the link from being opened.
    + * @param {string} url Url of the link being tested.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.editor.LinkDialog.BeforeTestLinkEvent = function(url) {
    +  goog.ui.editor.LinkDialog.BeforeTestLinkEvent.base(this, 'constructor',
    +      goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK);
    +
    +  /**
    +   * The url of the link being tested.
    +   * @type {string}
    +   */
    +  this.url = url;
    +};
    +goog.inherits(goog.ui.editor.LinkDialog.BeforeTestLinkEvent, goog.events.Event);
    +
    +
    +/**
    + * Sets the warning message to show to users about including email addresses on
    + * public web pages.
    + * @param {!goog.html.SafeHtml} emailWarning Warning message to show users about
    + *     including email addresses on the web.
    + */
    +goog.ui.editor.LinkDialog.prototype.setEmailWarning = function(
    +    emailWarning) {
    +  this.emailWarning_ = emailWarning;
    +};
    +
    +
    +/**
    + * Tells the dialog to show a checkbox where the user can choose to have the
    + * link open in a new window.
    + * @param {boolean} startChecked Whether to check the checkbox the first
    + *     time the dialog is shown. Subesquent times the checkbox will remember its
    + *     previous state.
    + */
    +goog.ui.editor.LinkDialog.prototype.showOpenLinkInNewWindow = function(
    +    startChecked) {
    +  this.showOpenLinkInNewWindow_ = true;
    +  this.isOpenLinkInNewWindowChecked_ = startChecked;
    +};
    +
    +
    +/**
    + * Tells the dialog to show a checkbox where the user can choose to add
    + * 'rel=nofollow' attribute to the link.
    + */
    +goog.ui.editor.LinkDialog.prototype.showRelNoFollow = function() {
    +  this.showRelNoFollow_ = true;
    +};
    +
    +
    +/** @override */
    +goog.ui.editor.LinkDialog.prototype.show = function() {
    +  goog.ui.editor.LinkDialog.base(this, 'show');
    +
    +
    +  this.selectAppropriateTab_(this.textToDisplayInput_.value,
    +                             this.getTargetUrl_());
    +  this.syncOkButton_();
    +
    +  if (this.showOpenLinkInNewWindow_) {
    +    if (!this.targetLink_.isNew()) {
    +      // If link is not new, checkbox should reflect current target.
    +      this.isOpenLinkInNewWindowChecked_ =
    +          this.targetLink_.getAnchor().target == '_blank';
    +    }
    +    this.openInNewWindowCheckbox_.checked = this.isOpenLinkInNewWindowChecked_;
    +  }
    +
    +  if (this.showRelNoFollow_) {
    +    this.relNoFollowCheckbox_.checked =
    +        goog.ui.editor.LinkDialog.hasNoFollow(this.targetLink_.getAnchor().rel);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.editor.LinkDialog.prototype.hide = function() {
    +  this.disableAutogenFlag_(false);
    +  goog.ui.editor.LinkDialog.base(this, 'hide');
    +};
    +
    +
    +/**
    + * Tells the dialog whether to show the 'text to display' div.
    + * When the target element of the dialog is an image, there is no link text
    + * to modify. This function can be used for this kind of situations.
    + * @param {boolean} visible Whether to make 'text to display' div visible.
    + */
    +goog.ui.editor.LinkDialog.prototype.setTextToDisplayVisible = function(
    +    visible) {
    +  if (this.textToDisplayDiv_) {
    +    goog.style.setStyle(this.textToDisplayDiv_, 'display',
    +                        visible ? 'block' : 'none');
    +  }
    +};
    +
    +
    +/**
    + * Tells the plugin whether to stop leaking the page's url via the referrer
    + * header when the "test this link" link is clicked.
    + * @param {boolean} stop Whether to stop leaking the referrer.
    + */
    +goog.ui.editor.LinkDialog.prototype.setStopReferrerLeaks = function(stop) {
    +  this.stopReferrerLeaks_ = stop;
    +};
    +
    +
    +/**
    + * Tells the dialog whether the autogeneration of text to display is to be
    + * enabled.
    + * @param {boolean} enable Whether to enable the feature.
    + */
    +goog.ui.editor.LinkDialog.prototype.setAutogenFeatureEnabled = function(
    +    enable) {
    +  this.autogenFeatureEnabled_ = enable;
    +};
    +
    +
    +/**
    + * Checks if {@code str} contains {@code "nofollow"} as a separate word.
    + * @param {string} str String to be tested.  This is usually {@code rel}
    + *     attribute of an {@code HTMLAnchorElement} object.
    + * @return {boolean} {@code true} if {@code str} contains {@code nofollow}.
    + */
    +goog.ui.editor.LinkDialog.hasNoFollow = function(str) {
    +  return goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_.test(str);
    +};
    +
    +
    +/**
    + * Removes {@code "nofollow"} from {@code rel} if it's present as a separate
    + * word.
    + * @param {string} rel Input string.  This is usually {@code rel} attribute of
    + *     an {@code HTMLAnchorElement} object.
    + * @return {string} {@code rel} with any {@code "nofollow"} removed.
    + */
    +goog.ui.editor.LinkDialog.removeNoFollow = function(rel) {
    +  return rel.replace(goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_, '');
    +};
    +
    +
    +// *** Protected interface ************************************************** //
    +
    +
    +/** @override */
    +goog.ui.editor.LinkDialog.prototype.createDialogControl = function() {
    +  var builder = new goog.ui.editor.AbstractDialog.Builder(this);
    +  builder.setTitle(goog.ui.editor.messages.MSG_EDIT_LINK)
    +      .setContent(this.createDialogContent_());
    +  return builder.build();
    +};
    +
    +
    +/**
    + * Creates and returns the event object to be used when dispatching the OK
    + * event to listeners based on which tab is currently selected and the contents
    + * of the input fields of that tab.
    + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when
    + *     dispatching the OK event to listeners.
    + * @protected
    + * @override
    + */
    +goog.ui.editor.LinkDialog.prototype.createOkEvent = function() {
    +  if (this.tabPane_.getCurrentTabId() ==
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB) {
    +    return this.createOkEventFromEmailTab_();
    +  } else {
    +    return this.createOkEventFromWebTab_();
    +  }
    +};
    +
    +
    +// *** Private implementation *********************************************** //
    +
    +
    +/**
    + * Regular expression that matches {@code nofollow} value in an
    + * {@code * HTMLAnchorElement}'s {@code rel} element.
    + * @type {RegExp}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.NO_FOLLOW_REGEX_ = /\bnofollow\b/i;
    +
    +
    +/**
    + * Creates contents of this dialog.
    + * @return {!Element} Contents of the dialog as a DOM element.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.createDialogContent_ = function() {
    +  this.textToDisplayDiv_ = /** @type {!HTMLDivElement} */(
    +      this.buildTextToDisplayDiv_());
    +  var content = this.dom.createDom(goog.dom.TagName.DIV, null,
    +      this.textToDisplayDiv_);
    +
    +  this.tabPane_ = new goog.ui.editor.TabPane(this.dom,
    +      goog.ui.editor.messages.MSG_LINK_TO);
    +  this.registerDisposable(this.tabPane_);
    +  this.tabPane_.addTab(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB,
    +      goog.ui.editor.messages.MSG_ON_THE_WEB,
    +      goog.ui.editor.messages.MSG_ON_THE_WEB_TIP,
    +      goog.ui.editor.LinkDialog.BUTTON_GROUP_,
    +      this.buildTabOnTheWeb_());
    +  this.tabPane_.addTab(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB,
    +      goog.ui.editor.messages.MSG_EMAIL_ADDRESS,
    +      goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP,
    +      goog.ui.editor.LinkDialog.BUTTON_GROUP_,
    +      this.buildTabEmailAddress_());
    +  this.tabPane_.render(content);
    +
    +  this.eventHandler_.listen(this.tabPane_, goog.ui.Component.EventType.SELECT,
    +      this.onChangeTab_);
    +
    +  if (this.showOpenLinkInNewWindow_) {
    +    content.appendChild(this.buildOpenInNewWindowDiv_());
    +  }
    +  if (this.showRelNoFollow_) {
    +    content.appendChild(this.buildRelNoFollowDiv_());
    +  }
    +
    +  return content;
    +};
    +
    +
    +/**
    + * Builds and returns the text to display section of the edit link dialog.
    + * @return {!Element} A div element to be appended into the dialog div.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.buildTextToDisplayDiv_ = function() {
    +  var table = this.dom.createTable(1, 2);
    +  table.cellSpacing = '0';
    +  table.cellPadding = '0';
    +  table.style.fontSize = '10pt';
    +  // Build the text to display input.
    +  var textToDisplayDiv = this.dom.createDom(goog.dom.TagName.DIV);
    +  var html = goog.html.SafeHtml.create('span', {
    +    'style': {
    +      'position': 'relative',
    +      'bottom': '2px',
    +      'padding-right': '1px',
    +      'white-space': 'nowrap'
    +    },
    +    id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY_LABEL
    +  }, [goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY, goog.string.Unicode.NBSP]);
    +  goog.dom.safe.setInnerHtml(table.rows[0].cells[0], html);
    +  this.textToDisplayInput_ = /** @type {!HTMLInputElement} */(
    +      this.dom.createDom(goog.dom.TagName.INPUT,
    +          {id: goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY}));
    +  var textInput = this.textToDisplayInput_;
    +  // 98% prevents scroll bars in standards mode.
    +  // TODO(robbyw): Is this necessary for quirks mode?
    +  goog.style.setStyle(textInput, 'width', '98%');
    +  goog.style.setStyle(table.rows[0].cells[1], 'width', '100%');
    +  goog.dom.appendChild(table.rows[0].cells[1], textInput);
    +
    +  goog.a11y.aria.setState(/** @type {!Element} */ (textInput),
    +      goog.a11y.aria.State.LABELLEDBY,
    +      goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY_LABEL);
    +  textInput.value = this.targetLink_.getCurrentText();
    +
    +  this.textInputHandler_ = new goog.events.InputHandler(textInput);
    +  this.registerDisposable(this.textInputHandler_);
    +  this.eventHandler_.listen(this.textInputHandler_,
    +      goog.events.InputHandler.EventType.INPUT, this.onTextToDisplayEdit_);
    +
    +  goog.dom.appendChild(textToDisplayDiv, table);
    +  return textToDisplayDiv;
    +};
    +
    +
    +/**
    + * Builds and returns the "checkbox to open the link in a new window" section of
    + * the edit link dialog.
    + * @return {!Element} A div element to be appended into the dialog div.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.buildOpenInNewWindowDiv_ = function() {
    +  this.openInNewWindowCheckbox_ = /** @type {!HTMLInputElement} */(
    +      this.dom.createDom(goog.dom.TagName.INPUT, {'type': 'checkbox'}));
    +  return this.dom.createDom(goog.dom.TagName.DIV, null,
    +      this.dom.createDom(goog.dom.TagName.LABEL, null,
    +                         this.openInNewWindowCheckbox_,
    +                         goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW));
    +};
    +
    +
    +/**
    + * Creates a DIV with a checkbox for {@code rel=nofollow} option.
    + * @return {!Element} Newly created DIV element.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.buildRelNoFollowDiv_ = function() {
    +  /** @desc Checkbox text for adding 'rel=nofollow' attribute to a link. */
    +  var MSG_ADD_REL_NOFOLLOW_ATTR = goog.getMsg(
    +      "Add '{$relNoFollow}' attribute ({$linkStart}Learn more{$linkEnd})", {
    +        'relNoFollow': 'rel=nofollow',
    +        'linkStart': '<a href="http://support.google.com/webmasters/bin/' +
    +            'answer.py?hl=en&answer=96569" target="_blank">',
    +        'linkEnd': '</a>'
    +      });
    +
    +  this.relNoFollowCheckbox_ = /** @type {!HTMLInputElement} */(
    +      this.dom.createDom(goog.dom.TagName.INPUT, {'type': 'checkbox'}));
    +  return this.dom.createDom(goog.dom.TagName.DIV, null,
    +      this.dom.createDom(goog.dom.TagName.LABEL, null,
    +          this.relNoFollowCheckbox_,
    +          goog.dom.htmlToDocumentFragment(MSG_ADD_REL_NOFOLLOW_ATTR)));
    +};
    +
    +
    +/**
    +* Builds and returns the div containing the tab "On the web".
    +* @return {!Element} The div element containing the tab.
    +* @private
    +*/
    +goog.ui.editor.LinkDialog.prototype.buildTabOnTheWeb_ = function() {
    +  var onTheWebDiv = this.dom.createElement(goog.dom.TagName.DIV);
    +
    +  var headingDiv = this.dom.createDom(goog.dom.TagName.DIV, {},
    +      this.dom.createDom(goog.dom.TagName.B, {},
    +          goog.ui.editor.messages.MSG_WHAT_URL));
    +  var urlInput = this.dom.createDom(goog.dom.TagName.INPUT,
    +      {
    +        id: goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT,
    +        className: goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_
    +      });
    +  goog.a11y.aria.setState(urlInput,
    +      goog.a11y.aria.State.LABELLEDBY,
    +      goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  // IE throws on unknown values for type.
    +  if (!goog.userAgent.IE) {
    +    // On browsers that support Web Forms 2.0, allow autocompletion of URLs.
    +    // (As of now, this is only supported by Opera 9)
    +    urlInput.type = 'url';
    +  }
    +
    +  if (goog.editor.BrowserFeature.NEEDS_99_WIDTH_IN_STANDARDS_MODE &&
    +      goog.editor.node.isStandardsMode(urlInput)) {
    +    urlInput.style.width = '99%';
    +  }
    +
    +  var inputDiv = this.dom.createDom(goog.dom.TagName.DIV, null, urlInput);
    +
    +  this.urlInputHandler_ = new goog.events.InputHandler(urlInput);
    +  this.registerDisposable(this.urlInputHandler_);
    +  this.eventHandler_.listen(this.urlInputHandler_,
    +      goog.events.InputHandler.EventType.INPUT,
    +      this.onUrlOrEmailInputChange_);
    +
    +  var testLink = new goog.ui.Button(goog.ui.editor.messages.MSG_TEST_THIS_LINK,
    +      goog.ui.LinkButtonRenderer.getInstance(),
    +      this.dom);
    +  testLink.render(inputDiv);
    +  testLink.getElement().style.marginTop = '1em';
    +  this.eventHandler_.listen(testLink,
    +      goog.ui.Component.EventType.ACTION,
    +      this.onWebTestLink_);
    +
    +  // Build the "On the web" explanation text div.
    +  var explanationDiv = this.dom.createDom(goog.dom.TagName.DIV,
    +      goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_);
    +  goog.dom.safe.setInnerHtml(explanationDiv,
    +      goog.ui.editor.messages.getTrLinkExplanationSafeHtml());
    +  onTheWebDiv.appendChild(headingDiv);
    +  onTheWebDiv.appendChild(inputDiv);
    +  onTheWebDiv.appendChild(explanationDiv);
    +
    +  return onTheWebDiv;
    +};
    +
    +
    +/**
    + * Builds and returns the div containing the tab "Email address".
    + * @return {!Element} the div element containing the tab.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.buildTabEmailAddress_ = function() {
    +  var emailTab = this.dom.createDom(goog.dom.TagName.DIV);
    +
    +  var headingDiv = this.dom.createDom(goog.dom.TagName.DIV, {},
    +      this.dom.createDom(goog.dom.TagName.B, {},
    +          goog.ui.editor.messages.MSG_WHAT_EMAIL));
    +  goog.dom.appendChild(emailTab, headingDiv);
    +  var emailInput = this.dom.createDom(goog.dom.TagName.INPUT,
    +      {
    +        id: goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT,
    +        className: goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_
    +      });
    +  goog.a11y.aria.setState(emailInput,
    +      goog.a11y.aria.State.LABELLEDBY,
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB);
    +
    +  if (goog.editor.BrowserFeature.NEEDS_99_WIDTH_IN_STANDARDS_MODE &&
    +      goog.editor.node.isStandardsMode(emailInput)) {
    +    // Standards mode sizes this too large.
    +    emailInput.style.width = '99%';
    +  }
    +
    +  goog.dom.appendChild(emailTab, emailInput);
    +
    +  this.emailInputHandler_ = new goog.events.InputHandler(emailInput);
    +  this.registerDisposable(this.emailInputHandler_);
    +  this.eventHandler_.listen(this.emailInputHandler_,
    +      goog.events.InputHandler.EventType.INPUT,
    +      this.onUrlOrEmailInputChange_);
    +
    +  goog.dom.appendChild(emailTab,
    +      this.dom.createDom(goog.dom.TagName.DIV,
    +          {
    +            id: goog.ui.editor.LinkDialog.Id_.EMAIL_WARNING,
    +            className: goog.ui.editor.LinkDialog.EMAIL_WARNING_CLASSNAME_,
    +            style: 'visibility:hidden'
    +          }, goog.ui.editor.messages.MSG_INVALID_EMAIL));
    +
    +  if (this.emailWarning_) {
    +    var explanationDiv = this.dom.createDom(goog.dom.TagName.DIV,
    +        goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_);
    +    goog.dom.safe.setInnerHtml(explanationDiv, this.emailWarning_);
    +    goog.dom.appendChild(emailTab, explanationDiv);
    +  }
    +  return emailTab;
    +};
    +
    +
    +/**
    + * Returns the url that the target points to.
    + * @return {string} The url that the target points to.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.getTargetUrl_ = function() {
    +  // Get the href-attribute through getAttribute() rather than the href property
    +  // because Google-Toolbar on Firefox with "Send with Gmail" turned on
    +  // modifies the href-property of 'mailto:' links but leaves the attribute
    +  // untouched.
    +  return this.targetLink_.getAnchor().getAttribute('href') || '';
    +};
    +
    +
    +/**
    + * Selects the correct tab based on the URL, and fills in its inputs.
    + * For new links, it suggests a url based on the link text.
    + * @param {string} text The inner text of the link.
    + * @param {string} url The href for the link.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.selectAppropriateTab_ = function(
    +    text, url) {
    +  if (this.isNewLink_()) {
    +    // Newly created non-empty link: try to infer URL from the link text.
    +    this.guessUrlAndSelectTab_(text);
    +  } else if (goog.editor.Link.isMailto(url)) {
    +    // The link is for an email.
    +    this.tabPane_.setSelectedTabId(
    +        goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB);
    +    this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT)
    +        .value = url.substring(url.indexOf(':') + 1);
    +    this.setAutogenFlagFromCurInput_();
    +  } else {
    +    // No specific tab was appropriate, default to on the web tab.
    +    this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +    this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT)
    +        .value = this.isNewLink_() ? 'http://' : url;
    +    this.setAutogenFlagFromCurInput_();
    +  }
    +};
    +
    +
    +/**
    + * Select a url/tab based on the link's text. This function is simply
    + * the isNewLink_() == true case of selectAppropriateTab_().
    + * @param {string} text The inner text of the link.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.guessUrlAndSelectTab_ = function(text) {
    +  if (goog.editor.Link.isLikelyEmailAddress(text)) {
    +    // The text is for an email address.
    +    this.tabPane_.setSelectedTabId(
    +        goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB);
    +    this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT)
    +        .value = text;
    +    this.setAutogenFlag_(true);
    +    // TODO(user): Why disable right after enabling? What bug are we
    +    // working around?
    +    this.disableAutogenFlag_(true);
    +  } else if (goog.editor.Link.isLikelyUrl(text)) {
    +    // The text is for a web URL.
    +    this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +    this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT)
    +        .value = text;
    +    this.setAutogenFlag_(true);
    +    this.disableAutogenFlag_(true);
    +  } else {
    +    // No meaning could be deduced from text, choose a default tab.
    +    if (!this.targetLink_.getCurrentText()) {
    +      this.setAutogenFlag_(true);
    +    }
    +    this.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  }
    +};
    +
    +
    +/**
    + * Called on a change to the url or email input. If either one of those tabs
    + * is active, sets the OK button to enabled/disabled accordingly.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.syncOkButton_ = function() {
    +  var inputValue;
    +  if (this.tabPane_.getCurrentTabId() ==
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB) {
    +    inputValue = this.dom.getElement(
    +        goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT).value;
    +    this.toggleInvalidEmailWarning_(inputValue != '' &&
    +        !goog.editor.Link.isLikelyEmailAddress(inputValue));
    +  } else if (this.tabPane_.getCurrentTabId() ==
    +      goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB) {
    +    inputValue = this.dom.getElement(
    +        goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT).value;
    +  } else {
    +    return;
    +  }
    +  this.getOkButtonElement().disabled =
    +      goog.string.isEmptyOrWhitespace(inputValue);
    +};
    +
    +
    +/**
    + * Show/hide the Invalid Email Address warning.
    + * @param {boolean} on Whether to show the warning.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.toggleInvalidEmailWarning_ = function(on) {
    +  this.dom.getElement(goog.ui.editor.LinkDialog.Id_.EMAIL_WARNING)
    +      .style.visibility = (on ? 'visible' : 'hidden');
    +};
    +
    +
    +/**
    + * Changes the autogenerateTextToDisplay flag so that text to
    + * display stops autogenerating.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.onTextToDisplayEdit_ = function() {
    +  var inputEmpty = this.textToDisplayInput_.value == '';
    +  if (inputEmpty) {
    +    this.setAutogenFlag_(true);
    +  } else {
    +    this.setAutogenFlagFromCurInput_();
    +  }
    +};
    +
    +
    +/**
    + * The function called when hitting OK with the "On the web" tab current.
    + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when
    + *     dispatching the OK event to listeners.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.createOkEventFromWebTab_ = function() {
    +  var input = /** @type {HTMLInputElement} */(
    +      this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT));
    +  var linkURL = input.value;
    +  if (goog.editor.Link.isLikelyEmailAddress(linkURL)) {
    +    // Make sure that if user types in an e-mail address, it becomes "mailto:".
    +    return this.createOkEventFromEmailTab_(
    +        goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT);
    +  } else {
    +    if (linkURL.search(/:/) < 0) {
    +      linkURL = 'http://' + goog.string.trimLeft(linkURL);
    +    }
    +    return this.createOkEventFromUrl_(linkURL);
    +  }
    +};
    +
    +
    +/**
    + * The function called when hitting OK with the "email address" tab current.
    + * @param {string=} opt_inputId Id of an alternate input to check.
    + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when
    + *     dispatching the OK event to listeners.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.createOkEventFromEmailTab_ = function(
    +    opt_inputId) {
    +  var linkURL = this.dom.getElement(
    +      opt_inputId || goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT).value;
    +  linkURL = 'mailto:' + linkURL;
    +  return this.createOkEventFromUrl_(linkURL);
    +};
    +
    +
    +/**
    + * Function to test a link from the on the web tab.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.onWebTestLink_ = function() {
    +  var input = /** @type {HTMLInputElement} */(
    +      this.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT));
    +  var url = input.value;
    +  if (url.search(/:/) < 0) {
    +    url = 'http://' + goog.string.trimLeft(url);
    +  }
    +  if (this.dispatchEvent(
    +      new goog.ui.editor.LinkDialog.BeforeTestLinkEvent(url))) {
    +    var win = this.dom.getWindow();
    +    var size = goog.dom.getViewportSize(win);
    +    var openOptions = {
    +      target: '_blank',
    +      width: Math.max(size.width - 50, 50),
    +      height: Math.max(size.height - 50, 50),
    +      toolbar: true,
    +      scrollbars: true,
    +      location: true,
    +      statusbar: false,
    +      menubar: true,
    +      'resizable': true,
    +      'noreferrer': this.stopReferrerLeaks_
    +    };
    +    goog.window.open(url, openOptions, win);
    +  }
    +};
    +
    +
    +/**
    + * Called whenever the url or email input is edited. If the text to display
    + * matches the text to display, turn on auto. Otherwise if auto is on, update
    + * the text to display based on the url.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.onUrlOrEmailInputChange_ = function() {
    +  if (this.autogenerateTextToDisplay_) {
    +    this.setTextToDisplayFromAuto_();
    +  } else if (this.textToDisplayInput_.value == '') {
    +    this.setAutogenFlagFromCurInput_();
    +  }
    +  this.syncOkButton_();
    +};
    +
    +
    +/**
    + * Called when the currently selected tab changes.
    + * @param {goog.events.Event} e The tab change event.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.onChangeTab_ = function(e) {
    +  var tab = /** @type {goog.ui.Tab} */ (e.target);
    +
    +  // Focus on the input field in the selected tab.
    +  var input = this.dom.getElement(tab.getId() +
    +      goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX);
    +  goog.editor.focus.focusInputField(input);
    +
    +  // For some reason, IE does not fire onpropertychange events when the width
    +  // is specified as a percentage, which breaks the InputHandlers.
    +  input.style.width = '';
    +  input.style.width = input.offsetWidth + 'px';
    +
    +  this.syncOkButton_();
    +  this.setTextToDisplayFromAuto_();
    +};
    +
    +
    +/**
    + * If autogen is turned on, set the value of text to display based on the
    + * current selection or url.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.setTextToDisplayFromAuto_ = function() {
    +  if (this.autogenFeatureEnabled_ && this.autogenerateTextToDisplay_) {
    +    var inputId = this.tabPane_.getCurrentTabId() +
    +        goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX;
    +    this.textToDisplayInput_.value =
    +        /** @type {HTMLInputElement} */(this.dom.getElement(inputId)).value;
    +  }
    +};
    +
    +
    +/**
    + * Turn on the autogenerate text to display flag, and set some sort of indicator
    + * that autogen is on.
    + * @param {boolean} val Boolean value to set autogenerate to.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.setAutogenFlag_ = function(val) {
    +  // TODO(user): This whole autogen thing is very confusing. It needs
    +  // to be refactored and/or explained.
    +  this.autogenerateTextToDisplay_ = val;
    +};
    +
    +
    +/**
    + * Disables autogen so that onUrlOrEmailInputChange_ doesn't act in cases
    + * that are undesirable.
    + * @param {boolean} autogen Boolean value to set disableAutogen to.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.disableAutogenFlag_ = function(autogen) {
    +  this.setAutogenFlag_(!autogen);
    +  this.disableAutogen_ = autogen;
    +};
    +
    +
    +/**
    + * Creates an OK event from the text to display input and the specified link.
    + * If text to display input is empty, then generate the auto value for it.
    + * @return {!goog.ui.editor.LinkDialog.OkEvent} The event object to be used when
    + *     dispatching the OK event to listeners.
    + * @param {string} url Url the target element should point to.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.createOkEventFromUrl_ = function(url) {
    +  // Fill in the text to display input in case it is empty.
    +  this.setTextToDisplayFromAuto_();
    +  if (this.showOpenLinkInNewWindow_) {
    +    // Save checkbox state for next time.
    +    this.isOpenLinkInNewWindowChecked_ = this.openInNewWindowCheckbox_.checked;
    +  }
    +  return new goog.ui.editor.LinkDialog.OkEvent(this.textToDisplayInput_.value,
    +      url, this.showOpenLinkInNewWindow_ && this.isOpenLinkInNewWindowChecked_,
    +      this.showRelNoFollow_ && this.relNoFollowCheckbox_.checked);
    +};
    +
    +
    +/**
    + * If an email or url is being edited, set autogenerate to on if the text to
    + * display matches the url.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.setAutogenFlagFromCurInput_ = function() {
    +  var autogen = false;
    +  if (!this.disableAutogen_) {
    +    var tabInput = this.dom.getElement(this.tabPane_.getCurrentTabId() +
    +        goog.ui.editor.LinkDialog.Id_.TAB_INPUT_SUFFIX);
    +    autogen = (tabInput.value == this.textToDisplayInput_.value);
    +  }
    +  this.setAutogenFlag_(autogen);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the link is new.
    + * @private
    + */
    +goog.ui.editor.LinkDialog.prototype.isNewLink_ = function() {
    +  return this.targetLink_.isNew();
    +};
    +
    +
    +/**
    + * IDs for relevant DOM elements.
    + * @enum {string}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.Id_ = {
    +  TEXT_TO_DISPLAY: 'linkdialog-text',
    +  TEXT_TO_DISPLAY_LABEL: 'linkdialog-text-label',
    +  ON_WEB_TAB: 'linkdialog-onweb',
    +  ON_WEB_INPUT: 'linkdialog-onweb-tab-input',
    +  EMAIL_ADDRESS_TAB: 'linkdialog-email',
    +  EMAIL_ADDRESS_INPUT: 'linkdialog-email-tab-input',
    +  EMAIL_WARNING: 'linkdialog-email-warning',
    +  TAB_INPUT_SUFFIX: '-tab-input'
    +};
    +
    +
    +/**
    + * Base name for the radio buttons group.
    + * @type {string}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.BUTTON_GROUP_ = 'linkdialog-buttons';
    +
    +
    +/**
    + * Class name for the url and email input elements.
    + * @type {string}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.TARGET_INPUT_CLASSNAME_ =
    +    goog.getCssName('tr-link-dialog-target-input');
    +
    +
    +/**
    + * Class name for the email address warning element.
    + * @type {string}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.EMAIL_WARNING_CLASSNAME_ =
    +    goog.getCssName('tr-link-dialog-email-warning');
    +
    +
    +/**
    + * Class name for the explanation text elements.
    + * @type {string}
    + * @private
    + */
    +goog.ui.editor.LinkDialog.EXPLANATION_TEXT_CLASSNAME_ =
    +    goog.getCssName('tr-link-dialog-explanation-text');
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.html b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.html
    new file mode 100644
    index 0000000000..f64c70209a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.ui.editor.LinkDialog Tests
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.editor.LinkDialogTest');
    +  </script>
    +  <link rel="stylesheet" href="../../css/dialog.css" />
    +  <link rel="stylesheet" href="../../css/linkbutton.css" />
    +  <link rel="stylesheet" href="../../css/editor/dialog.css" />
    +  <link rel="stylesheet" href="../../css/editor/linkdialog.css" />
    + </head>
    + <body>
    +  <iframe id="appWindowIframe" src="javascript:''">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.js b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.js
    new file mode 100644
    index 0000000000..f493525c49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/linkdialog_test.js
    @@ -0,0 +1,666 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.editor.LinkDialogTest');
    +goog.setTestOnly('goog.ui.editor.LinkDialogTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.editor.BrowserFeature');
    +goog.require('goog.editor.Link');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.testing.mockmatchers.ArgumentMatcher');
    +goog.require('goog.ui.editor.AbstractDialog');
    +goog.require('goog.ui.editor.LinkDialog');
    +goog.require('goog.ui.editor.messages');
    +goog.require('goog.userAgent');
    +
    +var dialog;
    +var mockCtrl;
    +var mockLink;
    +var mockOkHandler;
    +var mockGetViewportSize;
    +var mockWindowOpen;
    +var isNew;
    +var anchorElem;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +var ANCHOR_TEXT = 'anchor text';
    +var ANCHOR_URL = 'http://www.google.com/';
    +var ANCHOR_EMAIL = 'm@r.cos';
    +var ANCHOR_MAILTO = 'mailto:' + ANCHOR_EMAIL;
    +
    +function setUp() {
    +  anchorElem = goog.dom.createElement(goog.dom.TagName.A);
    +  goog.dom.appendChild(goog.dom.getDocument().body, anchorElem);
    +
    +  mockCtrl = new goog.testing.MockControl();
    +  mockLink = mockCtrl.createLooseMock(goog.editor.Link);
    +  mockOkHandler = mockCtrl.createLooseMock(goog.events.EventHandler);
    +
    +  isNew = false;
    +  mockLink.isNew();
    +  mockLink.$anyTimes();
    +  mockLink.$does(function() {
    +    return isNew;
    +  });
    +  mockLink.getCurrentText();
    +  mockLink.$anyTimes();
    +  mockLink.$does(function() {
    +    return anchorElem.innerHTML;
    +  });
    +  mockLink.setTextAndUrl(goog.testing.mockmatchers.isString,
    +      goog.testing.mockmatchers.isString);
    +  mockLink.$anyTimes();
    +  mockLink.$does(function(text, url) {
    +    anchorElem.innerHTML = text;
    +    anchorElem.href = url;
    +  });
    +  mockLink.$registerArgumentListVerifier('placeCursorRightOf', function() {
    +    return true;
    +  });
    +  mockLink.placeCursorRightOf(goog.testing.mockmatchers.iBoolean);
    +  mockLink.$anyTimes();
    +  mockLink.getAnchor();
    +  mockLink.$anyTimes();
    +  mockLink.$returns(anchorElem);
    +
    +  mockWindowOpen = mockCtrl.createFunctionMock('open');
    +  stubs.set(window, 'open', mockWindowOpen);
    +}
    +
    +function tearDown() {
    +  dialog.dispose();
    +  goog.dom.removeNode(anchorElem);
    +  stubs.reset();
    +}
    +
    +function setUpAnchor(href, text, opt_isNew, opt_target, opt_rel) {
    +  anchorElem.href = href;
    +  anchorElem.innerHTML = text;
    +  isNew = !!opt_isNew;
    +  if (opt_target) {
    +    anchorElem.target = opt_target;
    +  }
    +  if (opt_rel) {
    +    anchorElem.rel = opt_rel;
    +  }
    +}
    +
    +
    +/**
    + * Creates and shows the dialog to be tested.
    + * @param {Document=} opt_document Document to render the dialog into.
    + *     Defaults to the main window's document.
    + * @param {boolean=} opt_openInNewWindow Whether the open in new window
    + *     checkbox should be shown.
    + * @param {boolean=} opt_noFollow Whether rel=nofollow checkbox should be
    + *     shown.
    + */
    +function createAndShow(opt_document, opt_openInNewWindow, opt_noFollow) {
    +  dialog = new goog.ui.editor.LinkDialog(new goog.dom.DomHelper(opt_document),
    +                                         mockLink);
    +  if (opt_openInNewWindow) {
    +    dialog.showOpenLinkInNewWindow(false);
    +  }
    +  if (opt_noFollow) {
    +    dialog.showRelNoFollow();
    +  }
    +  dialog.addEventListener(goog.ui.editor.AbstractDialog.EventType.OK,
    +      mockOkHandler);
    +  dialog.show();
    +}
    +
    +
    +/**
    + * Sets up the mock event handler to expect an OK event with the given text
    + * and url.
    + */
    +function expectOk(linkText, linkUrl, opt_openInNewWindow, opt_noFollow) {
    +  mockOkHandler.handleEvent(new goog.testing.mockmatchers.ArgumentMatcher(
    +      function(arg) {
    +        return arg.type == goog.ui.editor.AbstractDialog.EventType.OK &&
    +               arg.linkText == linkText &&
    +               arg.linkUrl == linkUrl &&
    +               arg.openInNewWindow == !!opt_openInNewWindow &&
    +               arg.noFollow == !!opt_noFollow;
    +      },
    +      '{linkText: ' + linkText + ', linkUrl: ' + linkUrl +
    +          ', openInNewWindow: ' + opt_openInNewWindow +
    +          ', noFollow: ' + opt_noFollow + '}'));
    +}
    +
    +
    +/**
    + * Return true if we should use active element in our tests.
    + * @return {boolean} .
    + */
    +function useActiveElement() {
    +  return goog.editor.BrowserFeature.HAS_ACTIVE_ELEMENT ||
    +      goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher(9);
    +}
    +
    +
    +/**
    + * Tests that when you show the dialog for a new link, you can switch
    + * to the URL view.
    + * @param {Document=} opt_document Document to render the dialog into.
    + *     Defaults to the main window's document.
    + */
    +function testShowNewLinkSwitchToUrl(opt_document) {
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true); // Must be done before creating the dialog.
    +  createAndShow(opt_document);
    +
    +  var webRadio = dialog.dom.getElement(
    +      goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB).firstChild;
    +  var emailRadio = dialog.dom.getElement(
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB).firstChild;
    +  assertTrue('Web Radio Button selected', webRadio.checked);
    +  assertFalse('Email Radio Button selected', emailRadio.checked);
    +  if (useActiveElement()) {
    +    assertEquals('Focus should be on url input',
    +        getUrlInput(),
    +        dialog.dom.getActiveElement());
    +  }
    +
    +  emailRadio.click();
    +  assertFalse('Web Radio Button selected', webRadio.checked);
    +  assertTrue('Email Radio Button selected', emailRadio.checked);
    +  if (useActiveElement()) {
    +    assertEquals('Focus should be on url input',
    +        getEmailInput(),
    +        dialog.dom.getActiveElement());
    +  }
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that when you show the dialog for a new link, the input fields are
    + * empty, the web tab is selected and focus is in the url input field.
    + * @param {Document=} opt_document Document to render the dialog into.
    + *     Defaults to the main window's document.
    + */
    +function testShowForNewLink(opt_document) {
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true); // Must be done before creating the dialog.
    +  createAndShow(opt_document);
    +
    +  assertEquals('Display text input field should be empty',
    +               '',
    +               getDisplayInputText());
    +  assertEquals('Url input field should be empty',
    +               '',
    +               getUrlInputText());
    +  assertEquals('On the web tab should be selected',
    +               goog.ui.editor.LinkDialog.Id_.ON_WEB,
    +               dialog.curTabId_);
    +  if (useActiveElement()) {
    +    assertEquals('Focus should be on url input',
    +                 getUrlInput(),
    +                 dialog.dom.getActiveElement());
    +  }
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Fakes that the mock field is using an iframe and does the same test as
    + * testShowForNewLink().
    + */
    +function testShowForNewLinkWithDiffAppWindow() {
    +  testShowForNewLink(goog.dom.getElement('appWindowIframe').contentDocument);
    +}
    +
    +
    +/**
    + * Tests that when you show the dialog for a url link, the input fields are
    + * filled in, the web tab is selected and focus is in the url input field.
    + */
    +function testShowForUrlLink() {
    +  mockCtrl.$replayAll();
    +  setUpAnchor(ANCHOR_URL, ANCHOR_TEXT);
    +  createAndShow();
    +
    +  assertEquals('Display text input field should be filled in',
    +               ANCHOR_TEXT,
    +               getDisplayInputText());
    +  assertEquals('Url input field should be filled in',
    +               ANCHOR_URL,
    +               getUrlInputText());
    +  assertEquals('On the web tab should be selected',
    +               goog.ui.editor.LinkDialog.Id_.ON_WEB,
    +               dialog.curTabId_);
    +  if (useActiveElement()) {
    +    assertEquals('Focus should be on url input',
    +                 getUrlInput(),
    +                 dialog.dom.getActiveElement());
    +  }
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that when you show the dialog for a mailto link, the input fields are
    + * filled in, the email tab is selected and focus is in the email input field.
    + */
    +function testShowForMailtoLink() {
    +  mockCtrl.$replayAll();
    +  setUpAnchor(ANCHOR_MAILTO, ANCHOR_TEXT);
    +  createAndShow();
    +
    +  assertEquals('Display text input field should be filled in',
    +               ANCHOR_TEXT,
    +               getDisplayInputText());
    +  assertEquals('Email input field should be filled in',
    +               ANCHOR_EMAIL, // The 'mailto:' is not in the input!
    +               getEmailInputText());
    +  assertEquals('Email tab should be selected',
    +               goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS,
    +               dialog.curTabId_);
    +  if (useActiveElement()) {
    +    assertEquals('Focus should be on email input',
    +                 getEmailInput(),
    +                 dialog.dom.getActiveElement());
    +  }
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that the display text is autogenerated from the url input in the
    + * right situations (and not generated when appropriate too).
    + */
    +function testAutogeneration() {
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  // Simulate typing a url when everything is empty, should autogen.
    +  setUrlInputText(ANCHOR_URL);
    +  assertEquals('Display text should have been autogenerated',
    +               ANCHOR_URL,
    +               getDisplayInputText());
    +
    +  // Simulate typing text when url is set, afterwards should not autogen.
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_MAILTO);
    +  assertNotEquals('Display text should not have been autogenerated',
    +                  ANCHOR_MAILTO,
    +                  getDisplayInputText());
    +  assertEquals('Display text should have remained the same',
    +               ANCHOR_TEXT,
    +               getDisplayInputText());
    +
    +  // Simulate typing text equal to existing url, afterwards should autogen.
    +  setDisplayInputText(ANCHOR_MAILTO);
    +  setUrlInputText(ANCHOR_URL);
    +  assertEquals('Display text should have been autogenerated',
    +               ANCHOR_URL,
    +               getDisplayInputText());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that the display text is not autogenerated from the url input in all
    + * situations when the autogeneration feature is turned off.
    + */
    +function testAutogenerationOff() {
    +  mockCtrl.$replayAll();
    +
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  // Disable the autogen feature
    +  dialog.setAutogenFeatureEnabled(false);
    +
    +  // Simulate typing a url when everything is empty, should not autogen.
    +  setUrlInputText(ANCHOR_URL);
    +  assertEquals('Display text should not have been autogenerated',
    +               '',
    +               getDisplayInputText());
    +
    +  // Simulate typing text when url is set, afterwards should not autogen.
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_MAILTO);
    +  assertNotEquals('Display text should not have been autogenerated',
    +                  ANCHOR_MAILTO,
    +                  getDisplayInputText());
    +  assertEquals('Display text should have remained the same',
    +               ANCHOR_TEXT,
    +               getDisplayInputText());
    +
    +  // Simulate typing text equal to existing url, afterwards should not
    +  // autogen.
    +  setDisplayInputText(ANCHOR_MAILTO);
    +  setUrlInputText(ANCHOR_URL);
    +  assertEquals('Display text should not have been autogenerated',
    +               ANCHOR_MAILTO,
    +               getDisplayInputText());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking OK with the url tab selected dispatches an event with
    + * the proper link data.
    + */
    +function testOkForUrl() {
    +  expectOk(ANCHOR_TEXT, ANCHOR_URL);
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking OK with the url tab selected but with an email address
    + * in the url field dispatches an event with the proper link data.
    + */
    +function testOkForUrlWithEmail() {
    +  expectOk(ANCHOR_TEXT, ANCHOR_MAILTO);
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_EMAIL);
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Tests that clicking OK with the email tab selected dispatches an event with
    + * the proper link data.
    + */
    +function testOkForEmail() {
    +  expectOk(ANCHOR_TEXT, ANCHOR_MAILTO);
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  dialog.tabPane_.setSelectedTabId(
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_TAB);
    +
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setEmailInputText(ANCHOR_EMAIL);
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +function testOpenLinkInNewWindowNewLink() {
    +  expectOk(ANCHOR_TEXT, ANCHOR_URL, true);
    +  expectOk(ANCHOR_TEXT, ANCHOR_URL, false);
    +  mockCtrl.$replayAll();
    +
    +  setUpAnchor('', '', true);
    +  createAndShow(undefined, true);
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +
    +  assertFalse('"Open in new window" should start unchecked',
    +      getOpenInNewWindowCheckboxChecked());
    +  setOpenInNewWindowCheckboxChecked(true);
    +  assertTrue('"Open in new window" should have gotten checked',
    +      getOpenInNewWindowCheckboxChecked());
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +
    +  // Reopen same dialog
    +  dialog.show();
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +
    +  assertTrue('"Open in new window" should remember it was checked',
    +      getOpenInNewWindowCheckboxChecked());
    +  setOpenInNewWindowCheckboxChecked(false);
    +  assertFalse('"Open in new window" should have gotten unchecked',
    +      getOpenInNewWindowCheckboxChecked());
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +}
    +
    +function testOpenLinkInNewWindowExistingLink() {
    +  mockCtrl.$replayAll();
    +
    +  // Edit an existing link that already opens in a new window.
    +  setUpAnchor('', '', false, '_blank');
    +  createAndShow(undefined, true);
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +
    +  assertTrue('"Open in new window" should start checked for existing link',
    +      getOpenInNewWindowCheckboxChecked());
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +function testRelNoFollowNewLink() {
    +  expectOk(ANCHOR_TEXT, ANCHOR_URL, null, true);
    +  expectOk(ANCHOR_TEXT, ANCHOR_URL, null, false);
    +  mockCtrl.$replayAll();
    +
    +  setUpAnchor('', '', true, true);
    +  createAndShow(null, null, true);
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +  assertFalse('rel=nofollow should start unchecked',
    +      dialog.relNoFollowCheckbox_.checked);
    +
    +  // Check rel=nofollow and close the dialog.
    +  dialog.relNoFollowCheckbox_.checked = true;
    +  goog.testing.events.fireClickSequence(dialog.getOkButtonElement());
    +
    +  // Reopen the same dialog.
    +  anchorElem.rel = 'foo nofollow bar';
    +  dialog.show();
    +  dialog.tabPane_.setSelectedTabId(goog.ui.editor.LinkDialog.Id_.ON_WEB_TAB);
    +  setDisplayInputText(ANCHOR_TEXT);
    +  setUrlInputText(ANCHOR_URL);
    +  assertTrue('rel=nofollow should start checked when reopening the dialog',
    +      dialog.relNoFollowCheckbox_.checked);
    +}
    +
    +function testRelNoFollowExistingLink() {
    +  mockCtrl.$replayAll();
    +
    +  setUpAnchor('', '', null, null, 'foo nofollow bar');
    +  createAndShow(null, null, true);
    +  assertTrue('rel=nofollow should start checked for existing link',
    +      dialog.relNoFollowCheckbox_.checked);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Test that clicking on the test button opens a new window with the correct
    + * options.
    + */
    +function testWebTestButton() {
    +  if (goog.userAgent.GECKO) {
    +    // TODO(robbyw): Figure out why this is flaky and fix it.
    +    return;
    +  }
    +
    +  var width, height;
    +  mockWindowOpen(ANCHOR_URL, '_blank',
    +      new goog.testing.mockmatchers.ArgumentMatcher(function(str) {
    +        return str == 'width=' + width + ',height=' + height +
    +            ',toolbar=1,scrollbars=1,location=1,statusbar=0,' +
    +            'menubar=1,resizable=1';
    +      }, '3rd arg: (string) window.open() options'));
    +
    +  mockCtrl.$replayAll();
    +  setUpAnchor(ANCHOR_URL, ANCHOR_TEXT);
    +  createAndShow();
    +
    +  // Measure viewport after opening dialog because that might cause scrollbars
    +  // to appear and reduce the viewport size.
    +  var size = goog.dom.getViewportSize(window);
    +  width = Math.max(size.width - 50, 50);
    +  height = Math.max(size.height - 50, 50);
    +
    +  var testLink = goog.testing.dom.findTextNode(
    +      goog.ui.editor.messages.MSG_TEST_THIS_LINK,
    +      dialog.dialogInternal_.getElement());
    +  goog.testing.events.fireClickSequence(testLink.parentNode);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Test that clicking on the test button does not open a new window when
    + * the event is canceled.
    + */
    +function testWebTestButtonPreventDefault() {
    +  mockCtrl.$replayAll();
    +  setUpAnchor(ANCHOR_URL, ANCHOR_TEXT);
    +  createAndShow();
    +
    +  goog.events.listen(dialog,
    +      goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK,
    +      function(e) {
    +        assertEquals(e.url, ANCHOR_URL);
    +        e.preventDefault();
    +      });
    +  var testLink = goog.testing.dom.findTextNode(
    +      goog.ui.editor.messages.MSG_TEST_THIS_LINK,
    +      dialog.dialogInternal_.getElement());
    +  goog.testing.events.fireClickSequence(testLink.parentNode);
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +
    +/**
    + * Test that the setTextToDisplayVisible() correctly works.
    + * options.
    + */
    +function testSetTextToDisplayVisible() {
    +  mockCtrl.$replayAll();
    +  setUpAnchor('', '', true);
    +  createAndShow();
    +
    +  assertNotEquals('none',
    +                  goog.style.getStyle(dialog.textToDisplayDiv_, 'display'));
    +  dialog.setTextToDisplayVisible(false);
    +  assertEquals('none',
    +               goog.style.getStyle(dialog.textToDisplayDiv_, 'display'));
    +  dialog.setTextToDisplayVisible(true);
    +  assertNotEquals('none',
    +                  goog.style.getStyle(dialog.textToDisplayDiv_, 'display'));
    +
    +  mockCtrl.$verifyAll();
    +}
    +
    +function getDisplayInput() {
    +  return dialog.dom.getElement(goog.ui.editor.LinkDialog.Id_.TEXT_TO_DISPLAY);
    +}
    +
    +function getDisplayInputText() {
    +  return getDisplayInput().value;
    +}
    +
    +function setDisplayInputText(text) {
    +  var textInput = getDisplayInput();
    +  textInput.value = text;
    +  // Fire event so that dialog behaves like when user types.
    +  fireInputEvent(textInput, goog.events.KeyCodes.M);
    +}
    +
    +function getUrlInput() {
    +  var elt = dialog.dom.getElement(goog.ui.editor.LinkDialog.Id_.ON_WEB_INPUT);
    +  assertNotNullNorUndefined('UrlInput must be found', elt);
    +  return elt;
    +}
    +
    +function getUrlInputText() {
    +  return getUrlInput().value;
    +}
    +
    +function setUrlInputText(text) {
    +  var urlInput = getUrlInput();
    +  urlInput.value = text;
    +  // Fire event so that dialog behaves like when user types.
    +  fireInputEvent(dialog.urlInputHandler_, goog.events.KeyCodes.M);
    +}
    +
    +function getEmailInput() {
    +  var elt = dialog.dom.getElement(
    +      goog.ui.editor.LinkDialog.Id_.EMAIL_ADDRESS_INPUT);
    +  assertNotNullNorUndefined('EmailInput must be found', elt);
    +  return elt;
    +}
    +
    +function getEmailInputText() {
    +  return getEmailInput().value;
    +}
    +
    +function setEmailInputText(text) {
    +  var emailInput = getEmailInput();
    +  emailInput.value = text;
    +  // Fire event so that dialog behaves like when user types.
    +  fireInputEvent(dialog.emailInputHandler_, goog.events.KeyCodes.M);
    +}
    +
    +function getOpenInNewWindowCheckboxChecked() {
    +  return dialog.openInNewWindowCheckbox_.checked;
    +}
    +
    +function setOpenInNewWindowCheckboxChecked(checked) {
    +  dialog.openInNewWindowCheckbox_.checked = checked;
    +}
    +
    +function fireInputEvent(input, keyCode) {
    +  var inputEvent = new goog.testing.events.Event(goog.events.EventType.INPUT,
    +      input);
    +  inputEvent.keyCode = keyCode;
    +  inputEvent.charCode = keyCode;
    +  goog.testing.events.fireBrowserEvent(inputEvent);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/messages.js b/mapbox-gl/closure-library/closure/goog/ui/editor/messages.js
    new file mode 100644
    index 0000000000..614c97b22d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/messages.js
    @@ -0,0 +1,148 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Messages common to Editor UI components.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.editor.messages');
    +
    +goog.require('goog.html.uncheckedconversions');
    +goog.require('goog.string.Const');
    +
    +
    +/** @desc Link button / bubble caption. */
    +goog.ui.editor.messages.MSG_LINK_CAPTION = goog.getMsg('Link');
    +
    +
    +/** @desc Title for the dialog that edits a link. */
    +goog.ui.editor.messages.MSG_EDIT_LINK = goog.getMsg('Edit Link');
    +
    +
    +/** @desc Prompt the user for the text of the link they've written. */
    +goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY = goog.getMsg('Text to display:');
    +
    +
    +/** @desc Prompt the user for the URL of the link they've created. */
    +goog.ui.editor.messages.MSG_LINK_TO = goog.getMsg('Link to:');
    +
    +
    +/** @desc Prompt the user to type a web address for their link. */
    +goog.ui.editor.messages.MSG_ON_THE_WEB = goog.getMsg('Web address');
    +
    +
    +/** @desc More details on what linking to a web address involves.. */
    +goog.ui.editor.messages.MSG_ON_THE_WEB_TIP = goog.getMsg(
    +    'Link to a page or file somewhere else on the web');
    +
    +
    +/**
    + * @desc Text for a button that allows the user to test the link that
    + *     they created.
    + */
    +goog.ui.editor.messages.MSG_TEST_THIS_LINK = goog.getMsg('Test this link');
    +
    +
    +/**
    + * @desc Explanation for how to create a link with the link-editing dialog.
    + */
    +goog.ui.editor.messages.MSG_TR_LINK_EXPLANATION = goog.getMsg(
    +    '{$startBold}Not sure what to put in the box?{$endBold} ' +
    +    'First, find the page on the web that you want to ' +
    +    'link to. (A {$searchEngineLink}search engine{$endLink} ' +
    +    'might be useful.) Then, copy the web address from ' +
    +    "the box in your browser's address bar, and paste it into " +
    +    'the box above.',
    +    {'startBold': '<b>',
    +      'endBold': '</b>',
    +      'searchEngineLink': "<a href='http://www.google.com/' target='_new'>",
    +      'endLink': '</a>'});
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} SafeHtml version of MSG_TR_LINK_EXPLANATION.
    + */
    +goog.ui.editor.messages.getTrLinkExplanationSafeHtml = function() {
    +  return goog.html.uncheckedconversions.
    +      safeHtmlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from('Parameterless translation'),
    +          goog.ui.editor.messages.MSG_TR_LINK_EXPLANATION);
    +};
    +
    +
    +/** @desc Prompt for the URL of a link that the user is creating. */
    +goog.ui.editor.messages.MSG_WHAT_URL = goog.getMsg(
    +    'To what URL should this link go?');
    +
    +
    +/**
    + * @desc Prompt for an email address, so that the user can create a link
    + *    that sends an email.
    + */
    +goog.ui.editor.messages.MSG_EMAIL_ADDRESS = goog.getMsg('Email address');
    +
    +
    +/**
    + * @desc Explanation of the prompt for an email address in a link.
    + */
    +goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP = goog.getMsg(
    +    'Link to an email address');
    +
    +
    +/** @desc Error message when the user enters an invalid email address. */
    +goog.ui.editor.messages.MSG_INVALID_EMAIL = goog.getMsg(
    +    'Invalid email address');
    +
    +
    +/**
    + * @desc When the user creates a mailto link, asks them what email
    + *     address clicking on this link will send mail to.
    + */
    +goog.ui.editor.messages.MSG_WHAT_EMAIL = goog.getMsg(
    +    'To what email address should this link?');
    +
    +
    +/**
    + * @desc Warning about the dangers of creating links with email
    + *     addresses in them.
    + */
    +goog.ui.editor.messages.MSG_EMAIL_EXPLANATION = goog.getMsg(
    +    '{$preb}Be careful.{$postb} ' +
    +    'Remember that any time you include an email address on a web page, ' +
    +    'nasty spammers can find it too.', {'preb': '<b>', 'postb': '</b>'});
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} SafeHtml version of MSG_EMAIL_EXPLANATION.
    + */
    +goog.ui.editor.messages.getEmailExplanationSafeHtml = function() {
    +  return goog.html.uncheckedconversions.
    +      safeHtmlFromStringKnownToSatisfyTypeContract(
    +          goog.string.Const.from('Parameterless translation'),
    +          goog.ui.editor.messages.MSG_EMAIL_EXPLANATION);
    +};
    +
    +
    +/**
    + * @desc Label for the checkbox that allows the user to specify what when this
    + *     link is clicked, it should be opened in a new window.
    + */
    +goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW = goog.getMsg(
    +    'Open this link in a new window');
    +
    +
    +/** @desc Image bubble caption. */
    +goog.ui.editor.messages.MSG_IMAGE_CAPTION = goog.getMsg('Image');
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/tabpane.js b/mapbox-gl/closure-library/closure/goog/ui/editor/tabpane.js
    new file mode 100644
    index 0000000000..7a5ec92506
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/tabpane.js
    @@ -0,0 +1,201 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tabbed pane with style and functionality specific to
    + * Editor dialogs.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.editor.TabPane');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabBar');
    +
    +
    +
    +/**
    + * Creates a new Editor-style tab pane.
    + * @param {goog.dom.DomHelper} dom The dom helper for the window to create this
    + *     tab pane in.
    + * @param {string=} opt_caption Optional caption of the tab pane.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.ui.editor.TabPane = function(dom, opt_caption) {
    +  goog.ui.editor.TabPane.base(this, 'constructor', dom);
    +
    +  /**
    +   * The event handler used to register events.
    +   * @type {goog.events.EventHandler<!goog.ui.editor.TabPane>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  /**
    +   * The tab bar used to render the tabs.
    +   * @type {goog.ui.TabBar}
    +   * @private
    +   */
    +  this.tabBar_ = new goog.ui.TabBar(goog.ui.TabBar.Location.START,
    +      undefined, this.dom_);
    +  this.tabBar_.setFocusable(false);
    +
    +  /**
    +   * The content element.
    +   * @private
    +   */
    +  this.tabContent_ = this.dom_.createDom(goog.dom.TagName.DIV,
    +      {className: goog.getCssName('goog-tab-content')});
    +
    +  /**
    +   * The currently selected radio button.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.selectedRadio_ = null;
    +
    +  /**
    +   * The currently visible tab content.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.visibleContent_ = null;
    +
    +
    +  // Add the caption as the first element in the tab bar.
    +  if (opt_caption) {
    +    var captionControl = new goog.ui.Control(opt_caption, undefined,
    +        this.dom_);
    +    captionControl.addClassName(goog.getCssName('tr-tabpane-caption'));
    +    captionControl.setEnabled(false);
    +    this.tabBar_.addChild(captionControl, true);
    +  }
    +};
    +goog.inherits(goog.ui.editor.TabPane, goog.ui.Component);
    +
    +
    +/**
    + * @return {string} The ID of the content element for the current tab.
    + */
    +goog.ui.editor.TabPane.prototype.getCurrentTabId = function() {
    +  return this.tabBar_.getSelectedTab().getId();
    +};
    +
    +
    +/**
    + * Selects the tab with the given id.
    + * @param {string} id Id of the tab to select.
    + */
    +goog.ui.editor.TabPane.prototype.setSelectedTabId = function(id) {
    +  this.tabBar_.setSelectedTab(this.tabBar_.getChild(id));
    +};
    +
    +
    +/**
    + * Adds a tab to the tab pane.
    + * @param {string} id The id of the tab to add.
    + * @param {string} caption The caption of the tab.
    + * @param {string} tooltip The tooltip for the tab.
    + * @param {string} groupName for the radio button group.
    + * @param {Element} content The content element to show when this tab is
    + *     selected.
    + */
    +goog.ui.editor.TabPane.prototype.addTab = function(id, caption, tooltip,
    +    groupName, content) {
    +  var radio = this.dom_.createDom(goog.dom.TagName.INPUT,
    +      {
    +        name: groupName,
    +        type: 'radio'
    +      });
    +
    +  var tab = new goog.ui.Tab([radio, this.dom_.createTextNode(caption)],
    +      undefined, this.dom_);
    +  tab.setId(id);
    +  tab.setTooltip(tooltip);
    +  this.tabBar_.addChild(tab, true);
    +
    +  // When you navigate the radio buttons with TAB and then the Arrow keys on
    +  // Chrome and FF, you get a CLICK event on them, and the radio button
    +  // is selected.  You don't get a SELECT at all.  We listen for SELECT
    +  // nonetheless because it's possible that some browser will issue only
    +  // SELECT.
    +  this.eventHandler_.listen(radio,
    +      [goog.events.EventType.SELECT, goog.events.EventType.CLICK],
    +      goog.bind(this.tabBar_.setSelectedTab, this.tabBar_, tab));
    +
    +  content.id = id + '-tab';
    +  this.tabContent_.appendChild(content);
    +  goog.style.setElementShown(content, false);
    +};
    +
    +
    +/** @override */
    +goog.ui.editor.TabPane.prototype.enterDocument = function() {
    +  goog.ui.editor.TabPane.base(this, 'enterDocument');
    +
    +  // Get the root element and add a class name to it.
    +  var root = this.getElement();
    +  goog.asserts.assert(root);
    +  goog.dom.classlist.add(root, goog.getCssName('tr-tabpane'));
    +
    +  // Add the tabs.
    +  this.addChild(this.tabBar_, true);
    +  this.eventHandler_.listen(this.tabBar_, goog.ui.Component.EventType.SELECT,
    +      this.handleTabSelect_);
    +
    +  // Add the tab content.
    +  root.appendChild(this.tabContent_);
    +
    +  // Add an element to clear the tab float.
    +  root.appendChild(
    +      this.dom_.createDom(goog.dom.TagName.DIV,
    +          {className: goog.getCssName('goog-tab-bar-clear')}));
    +};
    +
    +
    +/**
    + * Handles a tab change.
    + * @param {goog.events.Event} e The browser change event.
    + * @private
    + */
    +goog.ui.editor.TabPane.prototype.handleTabSelect_ = function(e) {
    +  var tab = /** @type {goog.ui.Tab} */ (e.target);
    +
    +  // Show the tab content.
    +  if (this.visibleContent_) {
    +    goog.style.setElementShown(this.visibleContent_, false);
    +  }
    +  this.visibleContent_ = this.dom_.getElement(tab.getId() + '-tab');
    +  goog.style.setElementShown(this.visibleContent_, true);
    +
    +  // Select the appropriate radio button (and deselect the current one).
    +  if (this.selectedRadio_) {
    +    this.selectedRadio_.checked = false;
    +  }
    +  this.selectedRadio_ = tab.getElement().getElementsByTagName(
    +      goog.dom.TagName.INPUT)[0];
    +  this.selectedRadio_.checked = true;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarcontroller.js b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarcontroller.js
    new file mode 100644
    index 0000000000..299780ad65
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarcontroller.js
    @@ -0,0 +1,296 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for managing the editor toolbar.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../../demos/editor/editor.html
    + */
    +
    +goog.provide('goog.ui.editor.ToolbarController');
    +
    +goog.require('goog.editor.Field');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * A class for managing the editor toolbar.  Acts as a bridge between
    + * a {@link goog.editor.Field} and a {@link goog.ui.Toolbar}.
    + *
    + * The {@code toolbar} argument must be an instance of {@link goog.ui.Toolbar}
    + * or a subclass.  This class doesn't care how the toolbar was created.  As
    + * long as one or more controls hosted  in the toolbar have IDs that match
    + * built-in {@link goog.editor.Command}s, they will function as expected.  It is
    + * the caller's responsibility to ensure that the toolbar is already rendered
    + * or that it decorates an existing element.
    + *
    + *
    + * @param {!goog.editor.Field} field Editable field to be controlled by the
    + *     toolbar.
    + * @param {!goog.ui.Toolbar} toolbar Toolbar to control the editable field.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.editor.ToolbarController = function(field, toolbar) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Event handler to listen for field events and user actions.
    +   * @type {!goog.events.EventHandler<!goog.ui.editor.ToolbarController>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * The field instance controlled by the toolbar.
    +   * @type {!goog.editor.Field}
    +   * @private
    +   */
    +  this.field_ = field;
    +
    +  /**
    +   * The toolbar that controls the field.
    +   * @type {!goog.ui.Toolbar}
    +   * @private
    +   */
    +  this.toolbar_ = toolbar;
    +
    +  /**
    +   * Editing commands whose state is to be queried when updating the toolbar.
    +   * @type {!Array<string>}
    +   * @private
    +   */
    +  this.queryCommands_ = [];
    +
    +  // Iterate over all buttons, and find those which correspond to
    +  // queryable commands. Add them to the list of commands to query on
    +  // each COMMAND_VALUE_CHANGE event.
    +  this.toolbar_.forEachChild(function(button) {
    +    if (button.queryable) {
    +      this.queryCommands_.push(this.getComponentId(button.getId()));
    +    }
    +  }, this);
    +
    +  // Make sure the toolbar doesn't steal keyboard focus.
    +  this.toolbar_.setFocusable(false);
    +
    +  // Hook up handlers that update the toolbar in response to field events,
    +  // and to execute editor commands in response to toolbar events.
    +  this.handler_.
    +      listen(this.field_, goog.editor.Field.EventType.COMMAND_VALUE_CHANGE,
    +          this.updateToolbar).
    +      listen(this.toolbar_, goog.ui.Component.EventType.ACTION,
    +          this.handleAction);
    +};
    +goog.inherits(goog.ui.editor.ToolbarController, goog.events.EventTarget);
    +
    +
    +/**
    + * Returns the Closure component ID of the control that corresponds to the
    + * given {@link goog.editor.Command} constant.
    + * Subclasses may override this method if they want to use a custom mapping
    + * scheme from commands to controls.
    + * @param {string} command Editor command.
    + * @return {string} Closure component ID of the corresponding toolbar
    + *     control, if any.
    + * @protected
    + */
    +goog.ui.editor.ToolbarController.prototype.getComponentId = function(command) {
    +  // The default implementation assumes that the component ID is the same as
    +  // the command constant.
    +  return command;
    +};
    +
    +
    +/**
    + * Returns the {@link goog.editor.Command} constant
    + * that corresponds to the given Closure component ID.  Subclasses may override
    + * this method if they want to use a custom mapping scheme from controls to
    + * commands.
    + * @param {string} id Closure component ID of a toolbar control.
    + * @return {string} Editor command or dialog constant corresponding to the
    + *     toolbar control, if any.
    + * @protected
    + */
    +goog.ui.editor.ToolbarController.prototype.getCommand = function(id) {
    +  // The default implementation assumes that the component ID is the same as
    +  // the command constant.
    +  return id;
    +};
    +
    +
    +/**
    + * Returns the event handler object for the editor toolbar.  Useful for classes
    + * that extend {@code goog.ui.editor.ToolbarController}.
    + * @return {!goog.events.EventHandler<T>} The event handler object.
    + * @protected
    + * @this T
    + * @template T
    + */
    +goog.ui.editor.ToolbarController.prototype.getHandler = function() {
    +  return this.handler_;
    +};
    +
    +
    +/**
    + * Returns the field instance managed by the toolbar.  Useful for
    + * classes that extend {@code goog.ui.editor.ToolbarController}.
    + * @return {!goog.editor.Field} The field managed by the toolbar.
    + * @protected
    + */
    +goog.ui.editor.ToolbarController.prototype.getField = function() {
    +  return this.field_;
    +};
    +
    +
    +/**
    + * Returns the toolbar UI component that manages the editor.  Useful for
    + * classes that extend {@code goog.ui.editor.ToolbarController}.
    + * @return {!goog.ui.Toolbar} The toolbar UI component.
    + */
    +goog.ui.editor.ToolbarController.prototype.getToolbar = function() {
    +  return this.toolbar_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the toolbar is visible.
    + */
    +goog.ui.editor.ToolbarController.prototype.isVisible = function() {
    +  return this.toolbar_.isVisible();
    +};
    +
    +
    +/**
    + * Shows or hides the toolbar.
    + * @param {boolean} visible Whether to show or hide the toolbar.
    + */
    +goog.ui.editor.ToolbarController.prototype.setVisible = function(visible) {
    +  this.toolbar_.setVisible(visible);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the toolbar is enabled.
    + */
    +goog.ui.editor.ToolbarController.prototype.isEnabled = function() {
    +  return this.toolbar_.isEnabled();
    +};
    +
    +
    +/**
    + * Enables or disables the toolbar.
    + * @param {boolean} enabled Whether to enable or disable the toolbar.
    + */
    +goog.ui.editor.ToolbarController.prototype.setEnabled = function(enabled) {
    +  this.toolbar_.setEnabled(enabled);
    +};
    +
    +
    +/**
    + * Programmatically blurs the editor toolbar, un-highlighting the currently
    + * highlighted item, and closing the currently open menu (if any).
    + */
    +goog.ui.editor.ToolbarController.prototype.blur = function() {
    +  // We can't just call this.toolbar_.getElement().blur(), because the toolbar
    +  // element itself isn't focusable, so goog.ui.Container#handleBlur isn't
    +  // registered to handle blur events.
    +  this.toolbar_.handleBlur(null);
    +};
    +
    +
    +/** @override */
    +goog.ui.editor.ToolbarController.prototype.disposeInternal = function() {
    +  goog.ui.editor.ToolbarController.superClass_.disposeInternal.call(this);
    +  if (this.handler_) {
    +    this.handler_.dispose();
    +    delete this.handler_;
    +  }
    +  if (this.toolbar_) {
    +    this.toolbar_.dispose();
    +    delete this.toolbar_;
    +  }
    +  delete this.field_;
    +  delete this.queryCommands_;
    +};
    +
    +
    +/**
    + * Updates the toolbar in response to editor events.  Specifically, updates
    + * button states based on {@code COMMAND_VALUE_CHANGE} events, reflecting the
    + * effective formatting of the selection.
    + * @param {goog.events.Event} e Editor event to handle.
    + * @protected
    + */
    +goog.ui.editor.ToolbarController.prototype.updateToolbar = function(e) {
    +  if (!this.toolbar_.isEnabled() ||
    +      !this.dispatchEvent(goog.ui.Component.EventType.CHANGE)) {
    +    return;
    +  }
    +
    +  var state;
    +
    +  /** @preserveTry */
    +  try {
    +    /** @type {Array<string>} */
    +    e.commands; // Added by dispatchEvent.
    +
    +    // If the COMMAND_VALUE_CHANGE event specifies which commands changed
    +    // state, then we only need to update those ones, otherwise update all
    +    // commands.
    +    state = /** @type {Object} */ (
    +        this.field_.queryCommandValue(e.commands || this.queryCommands_));
    +  } catch (ex) {
    +    // TODO(attila): Find out when/why this happens.
    +    state = {};
    +  }
    +
    +  this.updateToolbarFromState(state);
    +};
    +
    +
    +/**
    + * Updates the toolbar to reflect a given state.
    + * @param {Object} state Object mapping editor commands to values.
    + */
    +goog.ui.editor.ToolbarController.prototype.updateToolbarFromState =
    +    function(state) {
    +  for (var command in state) {
    +    var button = this.toolbar_.getChild(this.getComponentId(command));
    +    if (button) {
    +      var value = state[command];
    +      if (button.updateFromValue) {
    +        button.updateFromValue(value);
    +      } else {
    +        button.setChecked(!!value);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles {@code ACTION} events dispatched by toolbar buttons in response to
    + * user actions by executing the corresponding field command.
    + * @param {goog.events.Event} e Action event to handle.
    + * @protected
    + */
    +goog.ui.editor.ToolbarController.prototype.handleAction = function(e) {
    +  var command = this.getCommand(e.target.getId());
    +  this.field_.execCommand(command, e.target.getValue());
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory.js b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory.js
    new file mode 100644
    index 0000000000..d179e18404
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory.js
    @@ -0,0 +1,439 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generic factory functions for creating the building blocks for
    + * an editor toolbar.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.editor.ToolbarFactory');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.string');
    +goog.require('goog.string.Unicode');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Option');
    +goog.require('goog.ui.Toolbar');
    +goog.require('goog.ui.ToolbarButton');
    +goog.require('goog.ui.ToolbarColorMenuButton');
    +goog.require('goog.ui.ToolbarMenuButton');
    +goog.require('goog.ui.ToolbarRenderer');
    +goog.require('goog.ui.ToolbarSelect');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Takes a font spec (e.g. "Arial, Helvetica, sans-serif") and returns the
    + * primary font name, normalized to lowercase (e.g. "arial").
    + * @param {string} fontSpec Font specification.
    + * @return {string} The primary font name, in lowercase.
    + */
    +goog.ui.editor.ToolbarFactory.getPrimaryFont = function(fontSpec) {
    +  var i = fontSpec.indexOf(',');
    +  var fontName = (i != -1 ? fontSpec.substring(0, i) : fontSpec).toLowerCase();
    +  // Strip leading/trailing quotes from the font name (bug 1050118).
    +  return goog.string.stripQuotes(fontName, '"\'');
    +};
    +
    +
    +/**
    + * Bulk-adds fonts to the given font menu button.  The argument must be an
    + * array of font descriptor objects, each of which must have the following
    + * attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the font menu (e.g. 'Tahoma')
    + *   <li>{@code value} - Value for the corresponding 'font-family' CSS style
    + *       (e.g. 'Tahoma, Arial, sans-serif')
    + * </ul>
    + * @param {!goog.ui.Select} button Font menu button.
    + * @param {!Array<{caption: string, value: string}>} fonts Array of
    + *     font descriptors.
    + */
    +goog.ui.editor.ToolbarFactory.addFonts = function(button, fonts) {
    +  goog.array.forEach(fonts, function(font) {
    +    goog.ui.editor.ToolbarFactory.addFont(button, font.caption, font.value);
    +  });
    +};
    +
    +
    +/**
    + * Adds a menu item to the given font menu button.  The first font listed in
    + * the {@code value} argument is considered the font ID, so adding two items
    + * whose CSS style starts with the same font may lead to unpredictable results.
    + * @param {!goog.ui.Select} button Font menu button.
    + * @param {string} caption Caption to show for the font menu.
    + * @param {string} value Value for the corresponding 'font-family' CSS style.
    + */
    +goog.ui.editor.ToolbarFactory.addFont = function(button, caption, value) {
    +  // The font ID is the first font listed in the CSS style, normalized to
    +  // lowercase.
    +  var id = goog.ui.editor.ToolbarFactory.getPrimaryFont(value);
    +
    +  // Construct the option, and add it to the button.
    +  var option = new goog.ui.Option(caption, value, button.getDomHelper());
    +  option.setId(id);
    +  button.addItem(option);
    +
    +  // Captions are shown in their own font.
    +  option.getContentElement().style.fontFamily = value;
    +};
    +
    +
    +/**
    + * Bulk-adds font sizes to the given font size menu button.  The argument must
    + * be an array of font size descriptor objects, each of which must have the
    + * following attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the font size menu (e.g. 'Huge')
    + *   <li>{@code value} - Value for the corresponding HTML font size (e.g. 6)
    + * </ul>
    + * @param {!goog.ui.Select} button Font size menu button.
    + * @param {!Array<{caption: string, value:number}>} sizes Array of font
    + *     size descriptors.
    + */
    +goog.ui.editor.ToolbarFactory.addFontSizes = function(button, sizes) {
    +  goog.array.forEach(sizes, function(size) {
    +    goog.ui.editor.ToolbarFactory.addFontSize(button, size.caption, size.value);
    +  });
    +};
    +
    +
    +/**
    + * Adds a menu item to the given font size menu button.  The {@code value}
    + * argument must be a legacy HTML font size in the 0-7 range.
    + * @param {!goog.ui.Select} button Font size menu button.
    + * @param {string} caption Caption to show in the font size menu.
    + * @param {number} value Value for the corresponding HTML font size.
    + */
    +goog.ui.editor.ToolbarFactory.addFontSize = function(button, caption, value) {
    +  // Construct the option, and add it to the button.
    +  var option = new goog.ui.Option(caption, value, button.getDomHelper());
    +  button.addItem(option);
    +
    +  // Adjust the font size of the menu item and the height of the checkbox
    +  // element after they've been rendered by addItem().  Captions are shown in
    +  // the corresponding font size, and lining up the checkbox is tricky.
    +  var content = option.getContentElement();
    +  content.style.fontSize =
    +      goog.ui.editor.ToolbarFactory.getPxFromLegacySize(value) + 'px';
    +  content.firstChild.style.height = '1.1em';
    +};
    +
    +
    +/**
    + * Converts a legacy font size specification into an equivalent pixel size.
    + * For example, {@code &lt;font size="6"&gt;} is {@code font-size: 32px;}, etc.
    + * @param {number} fontSize Legacy font size spec in the 0-7 range.
    + * @return {number} Equivalent pixel size.
    + */
    +goog.ui.editor.ToolbarFactory.getPxFromLegacySize = function(fontSize) {
    +  return goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_[fontSize] || 10;
    +};
    +
    +
    +/**
    + * Converts a pixel font size specification into an equivalent legacy size.
    + * For example, {@code font-size: 32px;} is {@code &lt;font size="6"&gt;}, etc.
    + * If the given pixel size doesn't exactly match one of the legacy sizes, -1 is
    + * returned.
    + * @param {number} px Pixel font size.
    + * @return {number} Equivalent legacy size spec in the 0-7 range, or -1 if none
    + *     exists.
    + */
    +goog.ui.editor.ToolbarFactory.getLegacySizeFromPx = function(px) {
    +  // Use lastIndexOf to get the largest legacy size matching the pixel size
    +  // (most notably returning 1 instead of 0 for 10px).
    +  return goog.array.lastIndexOf(
    +      goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_, px);
    +};
    +
    +
    +/**
    + * Map of legacy font sizes (0-7) to equivalent pixel sizes.
    + * @type {!Array<number>}
    + * @private
    + */
    +goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ =
    +    [10, 10, 13, 16, 18, 24, 32, 48];
    +
    +
    +/**
    + * Bulk-adds format options to the given "Format block" menu button.  The
    + * argument must be an array of format option descriptor objects, each of
    + * which must have the following attributes:
    + * <ul>
    + *   <li>{@code caption} - Caption to show in the menu (e.g. 'Minor heading')
    + *   <li>{@code command} - Corresponding {@link goog.dom.TagName} (e.g.
    + *       'H4')
    + * </ul>
    + * @param {!goog.ui.Select} button "Format block" menu button.
    + * @param {!Array<{caption: string, command: goog.dom.TagName}>} formats Array
    + *     of format option descriptors.
    + */
    +goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) {
    +  goog.array.forEach(formats, function(format) {
    +    goog.ui.editor.ToolbarFactory.addFormatOption(button, format.caption,
    +        format.command);
    +  });
    +};
    +
    +
    +/**
    + * Adds a menu item to the given "Format block" menu button.
    + * @param {!goog.ui.Select} button "Format block" menu button.
    + * @param {string} caption Caption to show in the menu.
    + * @param {goog.dom.TagName} tag Corresponding block format tag.
    + */
    +goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) {
    +  // Construct the option, and add it to the button.
    +  // TODO(attila): Create boring but functional menu item for now...
    +  var buttonDom = button.getDomHelper();
    +  var option = new goog.ui.Option(buttonDom.createDom(goog.dom.TagName.DIV,
    +      null, caption), tag, buttonDom);
    +  option.setId(tag);
    +  button.addItem(option);
    +};
    +
    +
    +/**
    + * Creates a {@link goog.ui.Toolbar} containing the specified set of
    + * toolbar buttons, and renders it into the given parent element.  Each
    + * item in the {@code items} array must a {@link goog.ui.Control}.
    + * @param {!Array<goog.ui.Control>} items Toolbar items; each must
    + *     be a {@link goog.ui.Control}.
    + * @param {!Element} elem Toolbar parent element.
    + * @param {boolean=} opt_isRightToLeft Whether the editor chrome is
    + *     right-to-left; defaults to the directionality of the toolbar parent
    + *     element.
    + * @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent
    + *     element.
    + */
    +goog.ui.editor.ToolbarFactory.makeToolbar = function(items, elem,
    +    opt_isRightToLeft) {
    +  var domHelper = goog.dom.getDomHelper(elem);
    +
    +  // Create an empty horizontal toolbar using the default renderer.
    +  var toolbar = new goog.ui.Toolbar(goog.ui.ToolbarRenderer.getInstance(),
    +      goog.ui.Container.Orientation.HORIZONTAL, domHelper);
    +
    +  // Optimization:  Explicitly test for the directionality of the parent
    +  // element here, so we can set it for both the toolbar and its children,
    +  // saving a lot of expensive calls to goog.style.isRightToLeft() during
    +  // rendering.
    +  var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem);
    +  toolbar.setRightToLeft(isRightToLeft);
    +
    +  // Optimization:  Set the toolbar to non-focusable before it is rendered,
    +  // to avoid creating unnecessary keyboard event handler objects.
    +  toolbar.setFocusable(false);
    +
    +  for (var i = 0, button; button = items[i]; i++) {
    +    // Optimization:  Set the button to non-focusable before it is rendered,
    +    // to avoid creating unnecessary keyboard event handler objects.  Also set
    +    // the directionality of the button explicitly, to avoid expensive calls
    +    // to goog.style.isRightToLeft() during rendering.
    +    button.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +    button.setRightToLeft(isRightToLeft);
    +    toolbar.addChild(button, true);
    +  }
    +
    +  toolbar.render(elem);
    +  return toolbar;
    +};
    +
    +
    +/**
    + * Creates a toolbar button with the given ID, tooltip, and caption.  Applies
    + * any custom CSS class names to the button's caption element.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toolbar button.
    + */
    +goog.ui.editor.ToolbarFactory.makeButton = function(id, tooltip, caption,
    +    opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = new goog.ui.ToolbarButton(
    +      goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
    +          opt_domHelper),
    +      opt_renderer,
    +      opt_domHelper);
    +  button.setId(id);
    +  button.setTooltip(tooltip);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a toggle button with the given ID, tooltip, and caption. Applies
    + * any custom CSS class names to the button's caption element. The button
    + * returned has checkbox-like toggle semantics.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Button} A toggle button.
    + */
    +goog.ui.editor.ToolbarFactory.makeToggleButton = function(id, tooltip, caption,
    +    opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = goog.ui.editor.ToolbarFactory.makeButton(id, tooltip, caption,
    +      opt_classNames, opt_renderer, opt_domHelper);
    +  button.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a menu button with the given ID, tooltip, and caption. Applies
    + * any custom CSS class names to the button's caption element.  The button
    + * returned doesn't have an actual menu attached; use {@link
    + * goog.ui.MenuButton#setMenu} to attach a {@link goog.ui.Menu} to the
    + * button.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
    + *     {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.MenuButton} A menu button.
    + */
    +goog.ui.editor.ToolbarFactory.makeMenuButton = function(id, tooltip, caption,
    +    opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = new goog.ui.ToolbarMenuButton(
    +      goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
    +          opt_domHelper),
    +      null,
    +      opt_renderer,
    +      opt_domHelper);
    +  button.setId(id);
    +  button.setTooltip(tooltip);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a select button with the given ID, tooltip, and caption. Applies
    + * any custom CSS class names to the button's root element.  The button
    + * returned doesn't have an actual menu attached; use {@link
    + * goog.ui.Select#setMenu} to attach a {@link goog.ui.Menu} containing
    + * {@link goog.ui.Option}s to the select button.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in buttons, anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption; used as the
    + *     default caption when nothing is selected.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the button's
    + *     root element.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.Select} A select button.
    + */
    +goog.ui.editor.ToolbarFactory.makeSelectButton = function(id, tooltip, caption,
    +    opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = new goog.ui.ToolbarSelect(null, null,
    +      opt_renderer,
    +      opt_domHelper);
    +  if (opt_classNames) {
    +    // Unlike the other button types, for goog.ui.Select buttons we apply the
    +    // extra class names to the root element, because for select buttons the
    +    // caption isn't stable (as it changes each time the selection changes).
    +    goog.array.forEach(opt_classNames.split(/\s+/), button.addClassName,
    +        button);
    +  }
    +  button.addClassName(goog.getCssName('goog-toolbar-select'));
    +  button.setDefaultCaption(caption);
    +  button.setId(id);
    +  button.setTooltip(tooltip);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a color menu button with the given ID, tooltip, and caption.
    + * Applies any custom CSS class names to the button's caption element.  The
    + * button is created with a default color menu containing standard color
    + * palettes.
    + * @param {string} id Button ID; must equal a {@link goog.editor.Command} for
    + *     built-in toolbar buttons, but can be anything else for custom buttons.
    + * @param {string} tooltip Tooltip to be shown on hover.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the caption
    + *     element.
    + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
    + *     defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer}
    + *     if unspecified.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!goog.ui.ColorMenuButton} A color menu button.
    + */
    +goog.ui.editor.ToolbarFactory.makeColorMenuButton = function(id, tooltip,
    +    caption, opt_classNames, opt_renderer, opt_domHelper) {
    +  var button = new goog.ui.ToolbarColorMenuButton(
    +      goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
    +          opt_domHelper),
    +      null,
    +      opt_renderer,
    +      opt_domHelper);
    +  button.setId(id);
    +  button.setTooltip(tooltip);
    +  return button;
    +};
    +
    +
    +/**
    + * Creates a new DIV that wraps a button caption, optionally applying CSS
    + * class names to it.  Used as a helper function in button factory methods.
    + * @param {goog.ui.ControlContent} caption Button caption.
    + * @param {string=} opt_classNames CSS class name(s) to apply to the DIV that
    + *     wraps the caption (if any).
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
    + *     creation; defaults to the current document if unspecified.
    + * @return {!Element} DIV that wraps the caption.
    + * @private
    + */
    +goog.ui.editor.ToolbarFactory.createContent_ = function(caption, opt_classNames,
    +    opt_domHelper) {
    +  // FF2 doesn't like empty DIVs, especially when rendered right-to-left.
    +  if ((!caption || caption == '') && goog.userAgent.GECKO &&
    +      !goog.userAgent.isVersionOrHigher('1.9a')) {
    +    caption = goog.string.Unicode.NBSP;
    +  }
    +  return (opt_domHelper || goog.dom.getDomHelper()).createDom(
    +      goog.dom.TagName.DIV,
    +      opt_classNames ? {'class' : opt_classNames} : null, caption);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.html b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.html
    new file mode 100644
    index 0000000000..e77e4be7b6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<!--
    +  All Rights Reserved.
    +
    +  @author jparent@google.com (Julie Parent)
    +-->
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.editor.ToolbarFactory
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.editor.ToolbarFactoryTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="myField">foo</div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.js b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.js
    new file mode 100644
    index 0000000000..440c897bc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/editor/toolbarfactory_test.js
    @@ -0,0 +1,73 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.editor.ToolbarFactoryTest');
    +goog.setTestOnly('goog.ui.editor.ToolbarFactoryTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.editor.TestHelper');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.editor.ToolbarFactory');
    +goog.require('goog.userAgent');
    +
    +var helper;
    +var expectedFailures;
    +
    +function setUpPage() {
    +  helper = new goog.testing.editor.TestHelper(goog.dom.getElement('myField'));
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  helper.setUpEditableElement();
    +}
    +
    +function tearDown() {
    +  helper.tearDownEditableElement();
    +  expectedFailures.handleTearDown();
    +}
    +
    +
    +/**
    + * Makes sure we have the correct conversion table in
    + * goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_. Can only be tested in
    + * a browser that takes legacy size values as input to execCommand but returns
    + * pixel size values from queryCommandValue. That's OK because that's the only
    + * situation where this conversion table's precision is critical. (When it's
    + * used to size the labels of the font size menu options it's ok if it's a few
    + * pixels off.)
    + */
    +function testGetLegacySizeFromPx() {
    +  // We will be warned if other browsers start behaving like webkit pre-534.7.
    +  expectedFailures.expectFailureFor(
    +      !goog.userAgent.WEBKIT ||
    +      (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('534.7')));
    +  try {
    +    var fieldElem = goog.dom.getElement('myField');
    +    // Start from 1 because size 0 is bogus (becomes 16px, legacy size 3).
    +    for (var i = 1; i <
    +        goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_.length; i++) {
    +      helper.select(fieldElem, 0, fieldElem, 1);
    +      document.execCommand('fontSize', false, i);
    +      helper.select('foo', 1);
    +      var value = document.queryCommandValue('fontSize');
    +      assertEquals('Px size ' + value + ' should convert to legacy size ' + i,
    +          i, goog.ui.editor.ToolbarFactory.getLegacySizeFromPx(
    +              parseInt(value, 10)));
    +    }
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emoji.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/emoji.js
    new file mode 100644
    index 0000000000..af1b6eb416
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emoji.js
    @@ -0,0 +1,73 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Emoji implementation.
    + *
    + */
    +
    +goog.provide('goog.ui.emoji.Emoji');
    +
    +
    +
    +/**
    + * Creates an emoji.
    + *
    + * A simple wrapper for an emoji.
    + *
    + * @param {string} url URL pointing to the source image for the emoji.
    + * @param {string} id The id of the emoji, e.g., 'std.1'.
    + * @constructor
    + * @final
    + */
    +goog.ui.emoji.Emoji = function(url, id) {
    +  /**
    +   * The URL pointing to the source image for the emoji
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * The id of the emoji
    +   *
    +   * @type {string}
    +   * @private
    +   */
    +  this.id_ = id;
    +};
    +
    +
    +/**
    + * The name of the goomoji attribute, used for emoji image elements.
    + * @type {string}
    + */
    +goog.ui.emoji.Emoji.ATTRIBUTE = 'goomoji';
    +
    +
    +/**
    + * @return {string} The URL for this emoji.
    + */
    +goog.ui.emoji.Emoji.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * @return {string} The id of this emoji.
    + */
    +goog.ui.emoji.Emoji.prototype.getId = function() {
    +  return this.id_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipalette.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipalette.js
    new file mode 100644
    index 0000000000..6f19144ffe
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipalette.js
    @@ -0,0 +1,289 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Emoji Palette implementation. This provides a UI widget for
    + * choosing an emoji from a palette of possible choices. EmojiPalettes are
    + * contained within EmojiPickers.
    + *
    + * See ../demos/popupemojipicker.html for an example of how to instantiate
    + * an emoji picker.
    + *
    + * Based on goog.ui.ColorPicker (colorpicker.js).
    + *
    + */
    +
    +goog.provide('goog.ui.emoji.EmojiPalette');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.ImageLoader');
    +goog.require('goog.ui.Palette');
    +goog.require('goog.ui.emoji.Emoji');
    +goog.require('goog.ui.emoji.EmojiPaletteRenderer');
    +
    +
    +
    +/**
    + * A page of emoji to be displayed in an EmojiPicker.
    + *
    + * @param {Array<Array<?>>} emoji List of emoji for this page.
    +  * @param {?string=} opt_urlPrefix Prefix that should be prepended to all URL.
    + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
    + *     decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Palette}
    + * @constructor
    + * @final
    + */
    +goog.ui.emoji.EmojiPalette = function(emoji,
    +                                      opt_urlPrefix,
    +                                      opt_renderer,
    +                                      opt_domHelper) {
    +  goog.ui.Palette.call(this,
    +                       null,
    +                       opt_renderer ||
    +                       new goog.ui.emoji.EmojiPaletteRenderer(null),
    +                       opt_domHelper);
    +  /**
    +   * All the different emoji that this palette can display. Maps emoji ids
    +   * (string) to the goog.ui.emoji.Emoji for that id.
    +   *
    +   * @type {Object}
    +   * @private
    +   */
    +  this.emojiCells_ = {};
    +
    +  /**
    +   * Map of emoji id to index into this.emojiCells_.
    +   *
    +   * @type {Object}
    +   * @private
    +   */
    +  this.emojiMap_ = {};
    +
    +  /**
    +   * List of the animated emoji in this palette. Each internal array is of type
    +   * [HTMLDivElement, goog.ui.emoji.Emoji], and represents the palette item
    +   * for that animated emoji, and the Emoji object.
    +   *
    +   * @type {Array<Array<(HTMLDivElement|goog.ui.emoji.Emoji)>>}
    +   * @private
    +   */
    +  this.animatedEmoji_ = [];
    +
    +  this.urlPrefix_ = opt_urlPrefix || '';
    +
    +  /**
    +   * Palette items that are displayed on this page of the emoji picker. Each
    +   * item is a div wrapped around a div or an img.
    +   *
    +   * @type {Array<HTMLDivElement>}
    +   * @private
    +   */
    +  this.emoji_ = this.getEmojiArrayFromProperties_(emoji);
    +
    +  this.setContent(this.emoji_);
    +};
    +goog.inherits(goog.ui.emoji.EmojiPalette, goog.ui.Palette);
    +
    +
    +/**
    + * Indicates a prefix that should be prepended to all URLs of images in this
    + * emojipalette. This provides an optimization if the URLs are long, so that
    + * the client does not have to send a long string for each emoji.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.urlPrefix_ = '';
    +
    +
    +/**
    + * Whether the emoji images have been loaded.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.imagesLoaded_ = false;
    +
    +
    +/**
    + * Image loader for loading animated emoji.
    + *
    + * @type {goog.net.ImageLoader}
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.imageLoader_;
    +
    +
    +/**
    + * Helps create an array of emoji palette items from an array of emoji
    + * properties. Each element will be either a div with background-image set to
    + * a sprite, or an img element pointing directly to an emoji, and all elements
    + * are wrapped with an outer div for alignment issues (i.e., this allows
    + * centering the inner div).
    + *
    + * @param {Object} emojiGroup The group of emoji for this page.
    + * @return {!Array<!HTMLDivElement>} The emoji items.
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getEmojiArrayFromProperties_ =
    +    function(emojiGroup) {
    +  var emojiItems = [];
    +
    +  for (var i = 0; i < emojiGroup.length; i++) {
    +    var url = emojiGroup[i][0];
    +    var id = emojiGroup[i][1];
    +    var spriteInfo = emojiGroup[i][2];
    +    var displayUrl = spriteInfo ? spriteInfo.getUrl() :
    +                     this.urlPrefix_ + url;
    +
    +    var item = this.getRenderer().createPaletteItem(
    +        this.getDomHelper(), id, spriteInfo, displayUrl);
    +    emojiItems.push(item);
    +
    +    var emoji = new goog.ui.emoji.Emoji(url, id);
    +    this.emojiCells_[id] = emoji;
    +    this.emojiMap_[id] = i;
    +
    +    // Keep track of sprited emoji that are animated, for later loading.
    +    if (spriteInfo && spriteInfo.isAnimated()) {
    +      this.animatedEmoji_.push([item, emoji]);
    +    }
    +  }
    +
    +  // Create the image loader now so that tests can access it before it has
    +  // started loading images.
    +  if (this.animatedEmoji_.length > 0) {
    +    this.imageLoader_ = new goog.net.ImageLoader();
    +  }
    +
    +  this.imagesLoaded_ = true;
    +  return emojiItems;
    +};
    +
    +
    +/**
    + * Sends off requests for all the animated emoji and replaces their static
    + * sprites when the images are done downloading.
    + */
    +goog.ui.emoji.EmojiPalette.prototype.loadAnimatedEmoji = function() {
    +  if (this.animatedEmoji_.length > 0) {
    +    for (var i = 0; i < this.animatedEmoji_.length; i++) {
    +      var paletteItem = /** @type {!Element} */ (this.animatedEmoji_[i][0]);
    +      var emoji =
    +          /** @type {goog.ui.emoji.Emoji} */ (this.animatedEmoji_[i][1]);
    +      var url = this.urlPrefix_ + emoji.getUrl();
    +
    +      this.imageLoader_.addImage(emoji.getId(), url);
    +    }
    +
    +    this.getHandler().listen(this.imageLoader_, goog.events.EventType.LOAD,
    +        this.handleImageLoad_);
    +    this.imageLoader_.start();
    +  }
    +};
    +
    +
    +/**
    + * Handles image load events from the ImageLoader.
    + *
    + * @param {goog.events.Event} e The event object.
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.handleImageLoad_ = function(e) {
    +  var id = e.target.id;
    +  var url = e.target.src;
    +  // Just to be safe, we check to make sure we have an id and src url from
    +  // the event target, which the ImageLoader sets to an Image object.
    +  if (id && url) {
    +    var item = this.emoji_[this.emojiMap_[id]];
    +    if (item) {
    +      this.getRenderer().updateAnimatedPaletteItem(item, e.target);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns the image loader that this palette uses. Used for testing.
    + *
    + * @return {goog.net.ImageLoader} the image loader.
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getImageLoader = function() {
    +  return this.imageLoader_;
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.EmojiPalette.prototype.disposeInternal = function() {
    +  goog.ui.emoji.EmojiPalette.superClass_.disposeInternal.call(this);
    +
    +  if (this.imageLoader_) {
    +    this.imageLoader_.dispose();
    +    this.imageLoader_ = null;
    +  }
    +  this.animatedEmoji_ = null;
    +  this.emojiCells_ = null;
    +  this.emojiMap_ = null;
    +  this.emoji_ = null;
    +};
    +
    +
    +/**
    + * Returns a goomoji id from an img or the containing td, or null if none
    + * exists for that element.
    + *
    + * @param {Element} el The element to get the Goomoji id from.
    + * @return {?string} A goomoji id from an img or the containing td, or null if
    + *     none exists for that element.
    + * @private
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getGoomojiIdFromElement_ = function(el) {
    +  if (!el) {
    +    return null;
    +  }
    +
    +  var item = this.getRenderer().getContainingItem(this, el);
    +  return item ? item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) : null;
    +};
    +
    +
    +/**
    + * @return {goog.ui.emoji.Emoji} The currently selected emoji from this palette.
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getSelectedEmoji = function() {
    +  var elem = /** @type {Element} */ (this.getSelectedItem());
    +  var goomojiId = this.getGoomojiIdFromElement_(elem);
    +  return this.emojiCells_[goomojiId];
    +};
    +
    +
    +/**
    + * @return {number} The number of emoji managed by this palette.
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getNumberOfEmoji = function() {
    +  return this.emojiCells_.length;
    +};
    +
    +
    +/**
    + * Returns the index of the specified emoji within this palette.
    + *
    + * @param {string} id Id of the emoji to look up.
    + * @return {number} The index of the specified emoji within this palette.
    + */
    +goog.ui.emoji.EmojiPalette.prototype.getEmojiIndex = function(id) {
    +  return this.emojiMap_[id];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js
    new file mode 100644
    index 0000000000..ece47dbf2c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipaletterenderer.js
    @@ -0,0 +1,209 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Emoji Palette renderer implementation.
    + *
    + */
    +
    +goog.provide('goog.ui.emoji.EmojiPaletteRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.style');
    +goog.require('goog.ui.PaletteRenderer');
    +goog.require('goog.ui.emoji.Emoji');
    +
    +
    +
    +/**
    + * Renders an emoji palette.
    + *
    + * @param {?string} defaultImgUrl Url of the img that should be used to fill up
    + *     the cells in the emoji table, to prevent jittering. Will be stretched
    + *     to the emoji cell size. A good image is a transparent dot.
    + * @constructor
    + * @extends {goog.ui.PaletteRenderer}
    + */
    +goog.ui.emoji.EmojiPaletteRenderer = function(defaultImgUrl) {
    +  goog.ui.PaletteRenderer.call(this);
    +
    +  this.defaultImgUrl_ = defaultImgUrl;
    +};
    +goog.inherits(goog.ui.emoji.EmojiPaletteRenderer, goog.ui.PaletteRenderer);
    +
    +
    +/**
    + * Globally unique ID sequence for cells rendered by this renderer class.
    + * @type {number}
    + * @private
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.cellId_ = 0;
    +
    +
    +/**
    + * Url of the img that should be used for cells in the emoji palette that are
    + * not filled with emoji, i.e., after all the emoji have already been placed
    + * on a page.
    + *
    + * @type {?string}
    + * @private
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.defaultImgUrl_ = null;
    +
    +
    +/** @override */
    +goog.ui.emoji.EmojiPaletteRenderer.getCssClass = function() {
    +  return goog.getCssName('goog-ui-emojipalette');
    +};
    +
    +
    +/**
    + * Creates a palette item from the given emoji data.
    + *
    + * @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
    + * @param {string} id Goomoji id for the emoji.
    + * @param {goog.ui.emoji.SpriteInfo} spriteInfo Spriting info for the emoji.
    + * @param {string} displayUrl URL of the image served for this cell, whether
    + *     an individual emoji image or a sprite.
    + * @return {!HTMLDivElement} The palette item for this emoji.
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.createPaletteItem =
    +    function(dom, id, spriteInfo, displayUrl) {
    +  var el;
    +
    +  if (spriteInfo) {
    +    var cssClass = spriteInfo.getCssClass();
    +    if (cssClass) {
    +      el = dom.createDom('div', cssClass);
    +    } else {
    +      el = this.buildElementFromSpriteMetadata(dom, spriteInfo, displayUrl);
    +    }
    +  } else {
    +    el = dom.createDom('img', {'src': displayUrl});
    +  }
    +
    +  var outerdiv =
    +      dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'), el);
    +  outerdiv.setAttribute(goog.ui.emoji.Emoji.ATTRIBUTE, id);
    +  return /** @type {!HTMLDivElement} */ (outerdiv);
    +};
    +
    +
    +/**
    + * Modifies a palette item containing an animated emoji, in response to the
    + * animated emoji being successfully downloaded.
    + *
    + * @param {Element} item The palette item to update.
    + * @param {Image} animatedImg An Image object containing the animated emoji.
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.updateAnimatedPaletteItem =
    +    function(item, animatedImg) {
    +  // An animated emoji is one that had sprite info for a static version and is
    +  // now being updated. See createPaletteItem for the structure of the palette
    +  // items we're modifying.
    +
    +  var inner = /** @type {Element} */ (item.firstChild);
    +  goog.asserts.assert(inner);
    +  // The first case is a palette item with a CSS class representing the sprite,
    +  // and an animated emoji.
    +  var classes = goog.dom.classlist.get(inner);
    +  if (classes && classes.length == 1) {
    +    inner.className = '';
    +  }
    +
    +  goog.style.setStyle(inner, {
    +    'width': animatedImg.width,
    +    'height': animatedImg.height,
    +    'background-image': 'url(' + animatedImg.src + ')',
    +    'background-position': '0 0'
    +  });
    +};
    +
    +
    +/**
    + * Builds the inner contents of a palette item out of sprite metadata.
    + *
    + * @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
    + * @param {goog.ui.emoji.SpriteInfo} spriteInfo The metadata to create the css
    + *     for the sprite.
    + * @param {string} displayUrl The URL of the image for this cell.
    + * @return {HTMLDivElement} The inner element for a palette item.
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.buildElementFromSpriteMetadata =
    +    function(dom, spriteInfo, displayUrl) {
    +  var width = spriteInfo.getWidthCssValue();
    +  var height = spriteInfo.getHeightCssValue();
    +  var x = spriteInfo.getXOffsetCssValue();
    +  var y = spriteInfo.getYOffsetCssValue();
    +
    +  var el = dom.createDom('div');
    +  goog.style.setStyle(el, {
    +    'width': width,
    +    'height': height,
    +    'background-image': 'url(' + displayUrl + ')',
    +    'background-repeat': 'no-repeat',
    +    'background-position': x + ' ' + y
    +  });
    +
    +  return /** @type {!HTMLDivElement} */ (el);
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.createCell = function(node, dom) {
    +  // Create a cell with  the default img if we're out of items, in order to
    +  // prevent jitter in the table. If there's no default img url, just create an
    +  // empty div, to prevent trying to fetch a null url.
    +  if (!node) {
    +    var elem = this.defaultImgUrl_ ?
    +               dom.createDom('img', {'src': this.defaultImgUrl_}) :
    +               dom.createDom('div');
    +    node = dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'),
    +                         elem);
    +  }
    +
    +  var cell = dom.createDom('td', {
    +    'class': goog.getCssName(this.getCssClass(), 'cell'),
    +    // Cells must have an ID, for accessibility, so we generate one here.
    +    'id': this.getCssClass() + '-cell-' +
    +        goog.ui.emoji.EmojiPaletteRenderer.cellId_++
    +  }, node);
    +  goog.a11y.aria.setRole(cell, 'gridcell');
    +  return cell;
    +};
    +
    +
    +/**
    + * Returns the item corresponding to the given node, or null if the node is
    + * neither a palette cell nor part of a palette item.
    + * @param {goog.ui.Palette} palette Palette in which to look for the item.
    + * @param {Node} node Node to look for.
    + * @return {Node} The corresponding palette item (null if not found).
    + * @override
    + */
    +goog.ui.emoji.EmojiPaletteRenderer.prototype.getContainingItem =
    +    function(palette, node) {
    +  var root = palette.getElement();
    +  while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) {
    +    if (node.tagName == 'TD') {
    +      return node.firstChild;
    +    }
    +    node = node.parentNode;
    +  }
    +
    +  return null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker.js
    new file mode 100644
    index 0000000000..22ba08da9e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker.js
    @@ -0,0 +1,812 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Emoji Picker implementation. This provides a UI widget for
    + * choosing an emoji from a grid of possible choices.
    + *
    + * @see ../demos/popupemojipicker.html for an example of how to instantiate
    + * an emoji picker.
    + *
    + * Based on goog.ui.ColorPicker (colorpicker.js).
    + *
    + * @see ../../demos/popupemojipicker.html
    + */
    +
    +goog.provide('goog.ui.emoji.EmojiPicker');
    +
    +goog.require('goog.log');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.TabPane');
    +goog.require('goog.ui.emoji.Emoji');
    +goog.require('goog.ui.emoji.EmojiPalette');
    +goog.require('goog.ui.emoji.EmojiPaletteRenderer');
    +goog.require('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
    +
    +
    +
    +/**
    + * Creates a new, empty emoji picker. An emoji picker is a grid of emoji, each
    + * cell of the grid containing a single emoji. The picker may contain multiple
    + * pages of emoji.
    + *
    + * When a user selects an emoji, by either clicking or pressing enter, the
    + * picker fires a goog.ui.Component.EventType.ACTION event with the id. The
    + * client listens on this event and in the handler can retrieve the id of the
    + * selected emoji and do something with it, for instance, inserting an image
    + * tag into a rich text control. An emoji picker does not maintain state. That
    + * is, once an emoji is selected, the emoji picker does not remember which emoji
    + * was selected.
    + *
    + * The emoji picker is implemented as a tabpane with each tabpage being a table.
    + * Each of the tables are the same size to prevent jittering when switching
    + * between pages.
    + *
    + * @param {string} defaultImgUrl Url of the img that should be used to fill up
    + *     the cells in the emoji table, to prevent jittering. Should be the same
    + *     size as the emoji.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.emoji.EmojiPicker = function(defaultImgUrl, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.defaultImgUrl_ = defaultImgUrl;
    +
    +  /**
    +   * Emoji that this picker displays.
    +   *
    +   * @type {Array<Object>}
    +   * @private
    +   */
    +  this.emoji_ = [];
    +
    +  /**
    +   * Pages of this emoji picker.
    +   *
    +   * @type {Array<goog.ui.emoji.EmojiPalette>}
    +   * @private
    +   */
    +  this.pages_ = [];
    +
    +  /**
    +   * Keeps track of which pages in the picker have been loaded. Used for delayed
    +   * loading of tabs.
    +   *
    +   * @type {Array<boolean>}
    +   * @private
    +   */
    +  this.pageLoadStatus_ = [];
    +
    +  /**
    +   * Tabpane to hold the pages of this emojipicker.
    +   *
    +   * @type {goog.ui.TabPane}
    +   * @private
    +   */
    +  this.tabPane_ = null;
    +
    +  this.getHandler().listen(this, goog.ui.Component.EventType.ACTION,
    +      this.onEmojiPaletteAction_);
    +};
    +goog.inherits(goog.ui.emoji.EmojiPicker, goog.ui.Component);
    +
    +
    +/**
    + * Default number of rows per grid of emoji.
    + *
    + * @type {number}
    + */
    +goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS = 5;
    +
    +
    +/**
    + * Default number of columns per grid of emoji.
    + *
    + * @type {number}
    + */
    +goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS = 10;
    +
    +
    +/**
    + * Default location of the tabs in relation to the emoji grids.
    + *
    + * @type {goog.ui.TabPane.TabLocation}
    + */
    +goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION =
    +    goog.ui.TabPane.TabLocation.TOP;
    +
    +
    +/** @private {goog.ui.emoji.Emoji} */
    +goog.ui.emoji.EmojiPicker.prototype.selectedEmoji_;
    +
    +
    +/** @private {goog.ui.emoji.EmojiPaletteRenderer} */
    +goog.ui.emoji.EmojiPicker.prototype.renderer_;
    +
    +
    +/**
    + * Number of rows per grid of emoji.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.numRows_ =
    +    goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS;
    +
    +
    +/**
    + * Number of columns per grid of emoji.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.numCols_ =
    +    goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS;
    +
    +
    +/**
    + * Whether the number of rows in the picker should be automatically determined
    + * by the specified number of columns so as to minimize/eliminate jitter when
    + * switching between tabs.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.autoSizeByColumnCount_ = true;
    +
    +
    +/**
    + * Location of the tabs for the picker tabpane.
    + *
    + * @type {goog.ui.TabPane.TabLocation}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.tabLocation_ =
    +    goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION;
    +
    +
    +/**
    + * Whether the component is focusable.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.focusable_ = true;
    +
    +
    +/**
    + * Url of the img that should be used for cells in the emoji picker that are
    + * not filled with emoji, i.e., after all the emoji have already been placed
    + * on a page.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.defaultImgUrl_;
    +
    +
    +/**
    + * If present, indicates a prefix that should be prepended to all URLs
    + * of images in this emojipicker. This provides an optimization if the URLs
    + * are long, so that the client does not have to send a long string for each
    + * emoji.
    + *
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.urlPrefix_;
    +
    +
    +/**
    + * If true, delay loading the images for the emojipalettes until after
    + * construction. This gives a better user experience before the images are in
    + * the cache, since other widgets waiting for construction of the emojipalettes
    + * won't have to wait for all the images (which may be a substantial amount) to
    + * load.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.delayedLoad_ = false;
    +
    +
    +/**
    + * Whether to use progressive rendering in the emojipicker's palette, if using
    + * sprited imgs. If true, then uses img tags, which most browsers render
    + * progressively (i.e., as the data comes in). If false, then uses div tags
    + * with the background-image, which some newer browsers render progressively
    + * but older ones do not.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.progressiveRender_ = false;
    +
    +
    +/**
    + * Whether to require the caller to manually specify when to start loading
    + * animated emoji. This is primarily for unittests to be able to test the
    + * structure of the emojipicker palettes before and after the animated emoji
    + * have been loaded.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.manualLoadOfAnimatedEmoji_ = false;
    +
    +
    +/**
    + * Index of the active page in the picker.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.activePage_ = -1;
    +
    +
    +/**
    + * Adds a group of emoji to the picker.
    + *
    + * @param {string|Element} title Title for the group.
    + * @param {Array<Array<string>>} emojiGroup A new group of emoji to be added
    + *    Each internal array contains [emojiUrl, emojiId].
    + */
    +goog.ui.emoji.EmojiPicker.prototype.addEmojiGroup =
    +    function(title, emojiGroup) {
    +  this.emoji_.push({title: title, emoji: emojiGroup});
    +};
    +
    +
    +/**
    + * Gets the number of rows per grid in the emoji picker.
    + *
    + * @return {number} number of rows per grid.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getNumRows = function() {
    +  return this.numRows_;
    +};
    +
    +
    +/**
    + * Gets the number of columns per grid in the emoji picker.
    + *
    + * @return {number} number of columns per grid.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getNumColumns = function() {
    +  return this.numCols_;
    +};
    +
    +
    +/**
    + * Sets the number of rows per grid in the emoji picker. This should only be
    + * called before the picker has been rendered.
    + *
    + * @param {number} numRows Number of rows per grid.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setNumRows = function(numRows) {
    +  this.numRows_ = numRows;
    +};
    +
    +
    +/**
    + * Sets the number of columns per grid in the emoji picker. This should only be
    + * called before the picker has been rendered.
    + *
    + * @param {number} numCols Number of columns per grid.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setNumColumns = function(numCols) {
    +  this.numCols_ = numCols;
    +};
    +
    +
    +/**
    + * Sets whether to automatically size the emojipicker based on the number of
    + * columns and the number of emoji in each group, so as to reduce jitter.
    + *
    + * @param {boolean} autoSize Whether to automatically size the picker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setAutoSizeByColumnCount =
    +    function(autoSize) {
    +  this.autoSizeByColumnCount_ = autoSize;
    +};
    +
    +
    +/**
    + * Sets the location of the tabs in relation to the emoji grids. This should
    + * only be called before the picker has been rendered.
    + *
    + * @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setTabLocation = function(tabLocation) {
    +  this.tabLocation_ = tabLocation;
    +};
    +
    +
    +/**
    + * Sets whether loading of images should be delayed until after dom creation.
    + * Thus, this function must be called before {@link #createDom}. If set to true,
    + * the client must call {@link #loadImages} when they wish the images to be
    + * loaded.
    + *
    + * @param {boolean} shouldDelay Whether to delay loading the images.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setDelayedLoad = function(shouldDelay) {
    +  this.delayedLoad_ = shouldDelay;
    +};
    +
    +
    +/**
    + * Sets whether to require the caller to manually specify when to start loading
    + * animated emoji. This is primarily for unittests to be able to test the
    + * structure of the emojipicker palettes before and after the animated emoji
    + * have been loaded. This only affects sprited emojipickers with sprite data
    + * for animated emoji.
    + *
    + * @param {boolean} manual Whether to load animated emoji manually.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setManualLoadOfAnimatedEmoji =
    +    function(manual) {
    +  this.manualLoadOfAnimatedEmoji_ = manual;
    +};
    +
    +
    +/**
    + * Returns true if the component is focusable, false otherwise.  The default
    + * is true.  Focusable components always have a tab index and allocate a key
    + * handler to handle keyboard events while focused.
    + * @return {boolean} Whether the component is focusable.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.isFocusable = function() {
    +  return this.focusable_;
    +};
    +
    +
    +/**
    + * Sets whether the component is focusable.  The default is true.
    + * Focusable components always have a tab index and allocate a key handler to
    + * handle keyboard events while focused.
    + * @param {boolean} focusable Whether the component is focusable.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setFocusable = function(focusable) {
    +  this.focusable_ = focusable;
    +  for (var i = 0; i < this.pages_.length; i++) {
    +    if (this.pages_[i]) {
    +      this.pages_[i].setSupportedState(goog.ui.Component.State.FOCUSED,
    +                                       focusable);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the URL prefix for the emoji URLs.
    + *
    + * @param {string} urlPrefix Prefix that should be prepended to all URLs.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
    +  this.urlPrefix_ = urlPrefix;
    +};
    +
    +
    +/**
    + * Sets the progressive rendering aspect of this emojipicker. Must be called
    + * before createDom to have an effect.
    + *
    + * @param {boolean} progressive Whether this picker should render progressively.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.setProgressiveRender =
    +    function(progressive) {
    +  this.progressiveRender_ = progressive;
    +};
    +
    +
    +/**
    + * Logger for the emoji picker.
    + *
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.logger_ =
    +    goog.log.getLogger('goog.ui.emoji.EmojiPicker');
    +
    +
    +/**
    + * Adjusts the number of rows to be the maximum row count out of all the emoji
    + * groups, in order to prevent jitter in switching among the tabs.
    + *
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.adjustNumRowsIfNecessary_ = function() {
    +  var currentMax = 0;
    +
    +  for (var i = 0; i < this.emoji_.length; i++) {
    +    var numEmoji = this.emoji_[i].emoji.length;
    +    var rowsNeeded = Math.ceil(numEmoji / this.numCols_);
    +    if (rowsNeeded > currentMax) {
    +      currentMax = rowsNeeded;
    +    }
    +  }
    +
    +  this.setNumRows(currentMax);
    +};
    +
    +
    +/**
    + * Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
    + * No-op if delayed loading is not set.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.loadImages = function() {
    +  if (!this.delayedLoad_) {
    +    return;
    +  }
    +
    +  // Load the first page only
    +  this.loadPage_(0);
    +  this.activePage_ = 0;
    +};
    +
    +
    +/**
    + * @override
    + * @suppress {deprecated} Using deprecated goog.ui.TabPane.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.createDom = function() {
    +  this.setElementInternal(this.getDomHelper().createDom('div'));
    +
    +  if (this.autoSizeByColumnCount_) {
    +    this.adjustNumRowsIfNecessary_();
    +  }
    +
    +  if (this.emoji_.length == 0) {
    +    throw Error('Must add some emoji to the picker');
    +  }
    +
    +  // If there is more than one group of emoji, we construct a tabpane
    +  if (this.emoji_.length > 1) {
    +    // Give the tabpane a div to use as its content element, since tabpane
    +    // overwrites the CSS class of the element it's passed
    +    var div = this.getDomHelper().createDom('div');
    +    this.getElement().appendChild(div);
    +    this.tabPane_ = new goog.ui.TabPane(div,
    +                                        this.tabLocation_,
    +                                        this.getDomHelper(),
    +                                        true  /* use MOUSEDOWN */);
    +  }
    +
    +  this.renderer_ = this.progressiveRender_ ?
    +      new goog.ui.emoji.ProgressiveEmojiPaletteRenderer(this.defaultImgUrl_) :
    +      new goog.ui.emoji.EmojiPaletteRenderer(this.defaultImgUrl_);
    +
    +  for (var i = 0; i < this.emoji_.length; i++) {
    +    var emoji = this.emoji_[i].emoji;
    +    var page = this.delayedLoad_ ?
    +               this.createPlaceholderEmojiPage_(emoji) :
    +               this.createEmojiPage_(emoji, i);
    +    this.pages_.push(page);
    +  }
    +
    +  this.activePage_ = 0;
    +  this.getElement().tabIndex = 0;
    +};
    +
    +
    +/**
    + * Used by unittests to manually load the animated emoji for this picker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.manuallyLoadAnimatedEmoji = function() {
    +  for (var i = 0; i < this.pages_.length; i++) {
    +    this.pages_[i].loadAnimatedEmoji();
    +  }
    +};
    +
    +
    +/**
    + * Creates a page if it has not already been loaded. This has the side effects
    + * of setting the load status of the page to true.
    + *
    + * @param {Array<Array<string>>} emoji Emoji for this page. See
    + *     {@link addEmojiGroup} for more details.
    + * @param {number} index Index of the page in the emojipicker.
    + * @return {goog.ui.emoji.EmojiPalette} the emoji page.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.createEmojiPage_ = function(emoji, index) {
    +  // Safeguard against trying to create the same page twice
    +  if (this.pageLoadStatus_[index]) {
    +    return null;
    +  }
    +
    +  var palette = new goog.ui.emoji.EmojiPalette(emoji,
    +                                               this.urlPrefix_,
    +                                               this.renderer_,
    +                                               this.getDomHelper());
    +  if (!this.manualLoadOfAnimatedEmoji_) {
    +    palette.loadAnimatedEmoji();
    +  }
    +  palette.setSize(this.numCols_, this.numRows_);
    +  palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
    +  palette.createDom();
    +  palette.setParent(this);
    +
    +  this.pageLoadStatus_[index] = true;
    +
    +  return palette;
    +};
    +
    +
    +/**
    + * Returns an array of emoji whose real URLs have been replaced with the
    + * default img URL. Used for delayed loading.
    + *
    + * @param {Array<Array<string>>} emoji Original emoji array.
    + * @return {!Array<!Array<string>>} emoji array with all emoji pointing to the
    + *     default img.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getPlaceholderEmoji_ = function(emoji) {
    +  var placeholderEmoji = [];
    +
    +  for (var i = 0; i < emoji.length; i++) {
    +    placeholderEmoji.push([this.defaultImgUrl_, emoji[i][1]]);
    +  }
    +
    +  return placeholderEmoji;
    +};
    +
    +
    +/**
    + * Creates an emoji page using placeholder emoji pointing to the default
    + * img instead of the real emoji. Used for delayed loading.
    + *
    + * @param {Array<Array<string>>} emoji Emoji for this page. See
    + *     {@link addEmojiGroup} for more details.
    + * @return {!goog.ui.emoji.EmojiPalette} the emoji page.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.createPlaceholderEmojiPage_ =
    +    function(emoji) {
    +  var placeholderEmoji = this.getPlaceholderEmoji_(emoji);
    +
    +  var palette = new goog.ui.emoji.EmojiPalette(placeholderEmoji,
    +                                               null,  // no url prefix
    +                                               this.renderer_,
    +                                               this.getDomHelper());
    +  palette.setSize(this.numCols_, this.numRows_);
    +  palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
    +  palette.createDom();
    +  palette.setParent(this);
    +
    +  return palette;
    +};
    +
    +
    +/**
    + * EmojiPickers cannot be used to decorate pre-existing html, since the
    + * structure they build is fairly complicated.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Returns always false.
    + * @override
    + */
    +goog.ui.emoji.EmojiPicker.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/**
    + * @override
    + * @suppress {deprecated} Using deprecated goog.ui.TabPane.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.enterDocument = function() {
    +  goog.ui.emoji.EmojiPicker.superClass_.enterDocument.call(this);
    +
    +  for (var i = 0; i < this.pages_.length; i++) {
    +    this.pages_[i].enterDocument();
    +    var pageElement = this.pages_[i].getElement();
    +
    +    // Add a new tab to the tabpane if there's more than one group of emoji.
    +    // If there is just one group of emoji, then we simply use the single
    +    // page's element as the content for the picker
    +    if (this.pages_.length > 1) {
    +      // Create a simple default title containg the page number if the title
    +      // was not provided in the emoji group params
    +      var title = this.emoji_[i].title || (i + 1);
    +      this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
    +          pageElement, title, this.getDomHelper()));
    +    } else {
    +      this.getElement().appendChild(pageElement);
    +    }
    +  }
    +
    +  // Initialize listeners. Note that we need to initialize this listener
    +  // after createDom, because addPage causes the goog.ui.TabPane.Events.CHANGE
    +  // event to fire, but we only want the handler (which loads delayed images)
    +  // to run after the picker has been constructed.
    +  if (this.tabPane_) {
    +    this.getHandler().listen(
    +        this.tabPane_, goog.ui.TabPane.Events.CHANGE, this.onPageChanged_);
    +
    +    // Make the tabpane unselectable so that changing tabs doesn't disturb the
    +    // cursor
    +    goog.style.setUnselectable(this.tabPane_.getElement(), true);
    +  }
    +
    +  this.getElement().unselectable = 'on';
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.EmojiPicker.prototype.exitDocument = function() {
    +  goog.ui.emoji.EmojiPicker.superClass_.exitDocument.call(this);
    +  for (var i = 0; i < this.pages_.length; i++) {
    +    this.pages_[i].exitDocument();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.EmojiPicker.prototype.disposeInternal = function() {
    +  goog.ui.emoji.EmojiPicker.superClass_.disposeInternal.call(this);
    +
    +  if (this.tabPane_) {
    +    this.tabPane_.dispose();
    +    this.tabPane_ = null;
    +  }
    +
    +  for (var i = 0; i < this.pages_.length; i++) {
    +    this.pages_[i].dispose();
    +  }
    +  this.pages_.length = 0;
    +};
    +
    +
    +/**
    + * @return {string} CSS class for the root element of EmojiPicker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getCssClass = function() {
    +  return goog.getCssName('goog-ui-emojipicker');
    +};
    +
    +
    +/**
    + * Returns the currently selected emoji from this picker. If the picker is
    + * using the URL prefix optimization, allocates a new emoji object with the
    + * full URL. This method is meant to be used by clients of the emojipicker,
    + * e.g., in a listener on goog.ui.component.EventType.ACTION that wants to use
    + * the just-selected emoji.
    + *
    + * @return {goog.ui.emoji.Emoji} The currently selected emoji from this picker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getSelectedEmoji = function() {
    +  return this.urlPrefix_ ?
    +      new goog.ui.emoji.Emoji(this.urlPrefix_ + this.selectedEmoji_.getId(),
    +                              this.selectedEmoji_.getId()) :
    +      this.selectedEmoji_;
    +};
    +
    +
    +/**
    + * Returns the number of emoji groups in this picker.
    + *
    + * @return {number} The number of emoji groups in this picker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getNumEmojiGroups = function() {
    +  return this.emoji_.length;
    +};
    +
    +
    +/**
    + * Returns a page from the picker. This should be considered protected, and is
    + * ONLY FOR TESTING.
    + *
    + * @param {number} index Index of the page to return.
    + * @return {goog.ui.emoji.EmojiPalette?} the page at the specified index or null
    + *     if none exists.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getPage = function(index) {
    +  return this.pages_[index];
    +};
    +
    +
    +/**
    + * Returns all the pages from the picker. This should be considered protected,
    + * and is ONLY FOR TESTING.
    + *
    + * @return {Array<goog.ui.emoji.EmojiPalette>?} the pages in the picker or
    + *     null if none exist.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getPages = function() {
    +  return this.pages_;
    +};
    +
    +
    +/**
    + * Returns the tabpane if this is a multipage picker. This should be considered
    + * protected, and is ONLY FOR TESTING.
    + *
    + * @return {goog.ui.TabPane} the tabpane if it is a multipage picker,
    + *     or null if it does not exist or is a single page picker.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getTabPane = function() {
    +  return this.tabPane_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.emoji.EmojiPalette} The active page of the emoji picker.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.getActivePage_ = function() {
    +  return this.pages_[this.activePage_];
    +};
    +
    +
    +/**
    + * Handles actions from the EmojiPalettes that this picker contains.
    + *
    + * @param {goog.ui.Component.EventType} e The event object.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.onEmojiPaletteAction_ = function(e) {
    +  this.selectedEmoji_ = this.getActivePage_().getSelectedEmoji();
    +};
    +
    +
    +/**
    + * Handles changes in the active page in the tabpane.
    + *
    + * @param {goog.ui.TabPaneEvent} e The event object.
    + * @private
    + */
    +goog.ui.emoji.EmojiPicker.prototype.onPageChanged_ = function(e) {
    +  var index = /** @type {number} */ (e.page.getIndex());
    +  this.loadPage_(index);
    +  this.activePage_ = index;
    +};
    +
    +
    +/**
    + * Loads a page into the picker if it has not yet been loaded.
    + *
    + * @param {number} index Index of the page to load.
    + * @private
    + * @suppress {deprecated} Using deprecated goog.ui.TabPane.
    + */
    +goog.ui.emoji.EmojiPicker.prototype.loadPage_ = function(index) {
    +  if (index < 0 || index > this.pages_.length) {
    +    throw Error('Index out of bounds');
    +  }
    +
    +  if (!this.pageLoadStatus_[index]) {
    +    var oldPage = this.pages_[index];
    +    this.pages_[index] = this.createEmojiPage_(this.emoji_[index].emoji,
    +                                               index);
    +    this.pages_[index].enterDocument();
    +    var pageElement = this.pages_[index].getElement();
    +    if (this.pages_.length > 1) {
    +      this.tabPane_.removePage(index);
    +      var title = this.emoji_[index].title || (index + 1);
    +      this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
    +          pageElement, title, this.getDomHelper()), index);
    +      this.tabPane_.setSelectedIndex(index);
    +    } else {
    +      var el = this.getElement();
    +      el.appendChild(pageElement);
    +    }
    +    if (oldPage) {
    +      oldPage.dispose();
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.html
    new file mode 100644
    index 0000000000..ab59b7ad17
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.emoji.EmojiPicker
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +  goog.require('goog.ui.emoji.EmojiPickerTest');
    +  </script>
    +  <link rel="stylesheet" href="../../demos/css/emojisprite.css" />
    + </head>
    + <body>
    +  <div id="test1">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.js
    new file mode 100644
    index 0000000000..759c5814d6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/emojipicker_test.js
    @@ -0,0 +1,910 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.emoji.EmojiPickerTest');
    +goog.setTestOnly('goog.ui.emoji.EmojiPickerTest');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.style');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.emoji.Emoji');
    +goog.require('goog.ui.emoji.EmojiPicker');
    +goog.require('goog.ui.emoji.SpriteInfo');
    +var handler;
    +
    +function setUp() {
    +  handler = new goog.events.EventHandler();
    +}
    +
    +function tearDown() {
    +  handler.removeAll();
    +}
    +
    +// 26 emoji
    +var emojiGroup1 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200']
    +  ]];
    +
    +// 20 emoji
    +var emojiGroup2 = [
    +  'Emoji 2',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204']
    +  ]];
    +
    +// 20 emoji
    +var emojiGroup3 = [
    +  'Emoji 3',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204']
    +  ]];
    +
    +var sprite = '../../demos/emoji/sprite.png';
    +var sprite2 = '../../demos/emoji/sprite2.png';
    +
    +
    +/**
    + * Creates a SpriteInfo object with the specified properties. If the image is
    + * sprited via CSS, then only the first parameter needs a value. If the image
    + * is sprited via metadata, then the first parameter should be left null.
    + *
    + * @param {?string} cssClass CSS class to properly display the sprited image.
    + * @param {string=} opt_url Url of the sprite image.
    + * @param {number=} opt_width Width of the image being sprited.
    + * @param {number=} opt_height Height of the image being sprited.
    + * @param {number=} opt_xOffset Positive x offset of the image being sprited
    + *     within the sprite.
    + * @param {number=} opt_yOffset Positive y offset of the image being sprited
    + *     within the sprite.
    + * @param {boolean=} opt_animated Whether the sprite info is for an animated
    + *     emoji.
    + */
    +function si(cssClass, opt_url, opt_width, opt_height, opt_xOffset,
    +            opt_yOffset, opt_animated) {
    +  return new goog.ui.emoji.SpriteInfo(cssClass, opt_url, opt_width,
    +      opt_height, opt_xOffset, opt_yOffset, opt_animated);
    +}
    +
    +// Contains a mix of sprited emoji via css, sprited emoji via metadata, and
    +// non-sprited emoji
    +var spritedEmoji1 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/204.gif', 'std.204', si('SPRITE_204')],
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/2BE.gif', 'std.2BE',
    +    si(null, sprite, 18, 18, 36, 54)],
    +   ['../../demos/emoji/2BF.gif', 'std.2BF',
    +    si(null, sprite, 18, 18, 0, 126)],
    +   ['../../demos/emoji/2C0.gif', 'std.2C0',
    +    si(null, sprite, 18, 18, 18, 305)],
    +   ['../../demos/emoji/2C1.gif', 'std.2C1',
    +    si(null, sprite, 18, 18, 0, 287)],
    +   ['../../demos/emoji/2C2.gif', 'std.2C2',
    +    si(null, sprite, 18, 18, 18, 126)],
    +   ['../../demos/emoji/2C3.gif', 'std.2C3',
    +    si(null, sprite, 18, 18, 36, 234)],
    +   ['../../demos/emoji/2C4.gif', 'std.2C4',
    +    si(null, sprite, 18, 18, 36, 72)],
    +   ['../../demos/emoji/2C5.gif', 'std.2C5',
    +    si(null, sprite, 18, 18, 54, 54)],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203'],
    +   ['../../demos/emoji/204.gif', 'std.204'],
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201'],
    +   ['../../demos/emoji/202.gif', 'std.202'],
    +   ['../../demos/emoji/203.gif', 'std.203']
    +  ]];
    +
    +// This group contains a mix of sprited emoji via css, sprited emoji via
    +// metadata, and non-sprited emoji.
    +var spritedEmoji2 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/204.gif', 'std.204', si('SPRITE_204')],
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/2BE.gif', 'std.2BE',
    +    si(null, sprite, 18, 18, 36, 54)],
    +   ['../../demos/emoji/2BF.gif', 'std.2BF',
    +    si(null, sprite, 18, 18, 0, 126)],
    +   ['../../demos/emoji/2C0.gif', 'std.2C0',
    +    si(null, sprite, 18, 18, 18, 305)],
    +   ['../../demos/emoji/2C1.gif', 'std.2C1',
    +    si(null, sprite, 18, 18, 0, 287)],
    +   ['../../demos/emoji/2C2.gif', 'std.2C2',
    +    si(null, sprite, 18, 18, 18, 126)],
    +   ['../../demos/emoji/2C3.gif', 'std.2C3',
    +    si(null, sprite, 18, 18, 36, 234)],
    +   ['../../demos/emoji/2C4.gif', 'std.2C4',
    +    si(null, sprite, 18, 18, 36, 72)],
    +   ['../../demos/emoji/2C5.gif', 'std.2C5',
    +    si(null, sprite, 18, 18, 54, 54)],
    +   ['../../demos/emoji/2C6.gif', 'std.2C6'],
    +   ['../../demos/emoji/2C7.gif', 'std.2C7'],
    +   ['../../demos/emoji/2C8.gif', 'std.2C8'],
    +   ['../../demos/emoji/2C9.gif', 'std.2C9'],
    +   ['../../demos/emoji/2CA.gif', 'std.2CA',
    +    si(null, sprite2, 18, 20, 36, 72, 1)],
    +   ['../../demos/emoji/2E3.gif', 'std.2E3',
    +    si(null, sprite2, 18, 18, 0, 0, 1)],
    +   ['../../demos/emoji/2EF.gif', 'std.2EF',
    +    si(null, sprite2, 18, 20, 0, 300, 1)],
    +   ['../../demos/emoji/2F1.gif', 'std.2F1',
    +    si(null, sprite2, 18, 18, 0, 320, 1)]
    +  ]];
    +
    +var emojiGroups = [emojiGroup1, emojiGroup2, emojiGroup3];
    +
    +function testConstructAndRenderOnePageEmojiPicker() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +  picker.dispose();
    +}
    +
    +function testConstructAndRenderMultiPageEmojiPicker() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.addEmojiGroup(emojiGroup2[0], emojiGroup2[1]);
    +  picker.addEmojiGroup(emojiGroup3[0], emojiGroup3[1]);
    +  picker.render();
    +  picker.dispose();
    +}
    +
    +function testExitDocumentCleansUpProperlyForSinglePageEmojiPicker() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +  picker.enterDocument();
    +  picker.exitDocument();
    +  picker.dispose();
    +}
    +
    +function testExitDocumentCleansUpProperlyForMultiPageEmojiPicker() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.addEmojiGroup(emojiGroup2[0], emojiGroup2[1]);
    +  picker.render();
    +  picker.enterDocument();
    +  picker.exitDocument();
    +  picker.dispose();
    +}
    +
    +function testNumGroups() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +
    +  for (var i = 0; i < emojiGroups.length; i++) {
    +    picker.addEmojiGroup(emojiGroups[i][0], emojiGroups[i][1]);
    +  }
    +
    +  assertTrue(picker.getNumEmojiGroups() == emojiGroups.length);
    +}
    +
    +function testAdjustNumRowsIfNecessaryIsCorrect() {
    +  var picker = new goog.ui.emoji.EmojiPicker('../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.setAutoSizeByColumnCount(true);
    +  picker.setNumColumns(5);
    +  assertEquals(5, picker.getNumColumns());
    +  assertEquals(goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS, picker.getNumRows());
    +
    +  picker.adjustNumRowsIfNecessary_();
    +
    +  // The emojiGroup has 26 emoji. ceil(26/5) should give 6 rows.
    +  assertEquals(6, picker.getNumRows());
    +
    +  // Change col count to 10, should give 3 rows.
    +  picker.setNumColumns(10);
    +  picker.adjustNumRowsIfNecessary_();
    +  assertEquals(3, picker.getNumRows());
    +
    +  // Add another gruop, with 20 emoji. Deliberately set the number of rows too
    +  // low. It should adjust it to three to accommodate the emoji in the first
    +  // group.
    +  picker.addEmojiGroup(emojiGroup2[0], emojiGroup2[1]);
    +  picker.setNumColumns(10);
    +  picker.setNumRows(2);
    +  picker.adjustNumRowsIfNecessary_();
    +  assertEquals(3, picker.getNumRows());
    +}
    +
    +
    +/**
    + * Helper for testDelayedLoad. Returns true if the two paths end with the same
    + * file.
    + *
    + * E.g., ('../../cool.gif', 'file:///home/usr/somewhere/cool.gif') --> true
    + *
    + * @param {string} path1 First url
    + * @param {string} path2 Second url
    + */
    +function checkPathsEndWithSameFile(path1, path2) {
    +  var pieces1 = path1.split('/');
    +  var file1 = pieces1[pieces1.length - 1];
    +  var pieces2 = path2.split('/');
    +  var file2 = pieces2[pieces2.length - 1];
    +
    +  return file1 == file2;
    +}
    +
    +
    +/**
    + * Gets the emoji URL from a palette element. Palette elements are divs or
    + * imgs wrapped in an outer div. The returns the background-image if it's a div,
    + * or the src attribute if it's an image.
    + *
    + * @param {Element} element Element to get the image url for
    + * @return {string}
    + */
    +function getImageUrl(element) {
    +  element = element.firstChild;  // get the wrapped element
    +  if (element.tagName == 'IMG') {
    +    return element.src;
    +  } else {
    +    var url = goog.style.getStyle(element, 'background-image');
    +    url = url.replace(/url\(/, '');
    +    url = url.replace(/\)/, '');
    +    return url;
    +  }
    +}
    +
    +
    +/**
    + * Checks that the content of an emojipicker page is all images pointing to
    + * the default img.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} page The page of the picker to check
    + * @param {string} defaultImgUrl The url of the default img
    + */
    +function checkContentIsDefaultImg(page, defaultImgUrl) {
    +  var content = page.getContent();
    +
    +  for (var i = 0; i < content.length; i++) {
    +    var url = getImageUrl(content[i]);
    +    assertTrue('img src should be ' + defaultImgUrl + ' but is ' +
    +               url,
    +               checkPathsEndWithSameFile(url, defaultImgUrl));
    +  }
    +}
    +
    +
    +/**
    + * Checks that the content of an emojipicker page is the specified emoji and
    + * the default img after the emoji are all used.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} page The page of the picker to check
    + * @param {Array<Array<string>>} emojiList List of emoji that should be in the
    + *     palette
    + * @param {string} defaultImgUrl The url of the default img
    + */
    +function checkContentIsEmojiImages(page, emojiList, defaultImg) {
    +  var content = page.getContent();
    +
    +  for (var i = 0; i < content.length; i++) {
    +    var url = getImageUrl(content[i]);
    +    if (i < emojiList.length) {
    +      assertTrue('Paths should end with the same file: ' +
    +                 url + ', ' + emojiList[i][0],
    +                 checkPathsEndWithSameFile(url, emojiList[i][0]));
    +    } else {
    +      assertTrue('Paths should end with the same file: ' +
    +                 url + ', ' + defaultImg,
    +                 checkPathsEndWithSameFile(url, defaultImg));
    +    }
    +  }
    +}
    +
    +
    +function testNonDelayedLoadPaletteCreationForSinglePagePicker() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +
    +  var page = picker.getPage(0);
    +  assertTrue('Page should be in the document but is not', page.isInDocument());
    +
    +  // The content should be the actual emoji images now, with the remainder set
    +  // to the default img
    +  checkContentIsEmojiImages(page, emojiGroup1[1], defaultImg);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testNonDelayedLoadPaletteCreationForMultiPagePicker() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +
    +  for (var i = 0; i < emojiGroups.length; i++) {
    +    picker.addEmojiGroup(emojiGroups[i][0], emojiGroups[i][1]);
    +  }
    +
    +  picker.render();
    +
    +  for (var i = 0; i < emojiGroups.length; i++) {
    +    var page = picker.getPage(i);
    +    assertTrue('Page ' + i + ' should be in the document but is not',
    +               page.isInDocument());
    +    checkContentIsEmojiImages(page, emojiGroups[i][1], defaultImg);
    +  }
    +
    +  picker.dispose();
    +}
    +
    +
    +function testDelayedLoadPaletteCreationForSinglePagePicker() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(true);
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +
    +  // At this point the picker should have pages filled with the default img
    +  checkContentIsDefaultImg(picker.getPage(0), defaultImg);
    +
    +  // Now load the images
    +  picker.loadImages();
    +
    +  var page = picker.getPage(0);
    +  assertTrue('Page should be in the document but is not', page.isInDocument());
    +
    +  // The content should be the actual emoji images now, with the remainder set
    +  // to the default img
    +  checkContentIsEmojiImages(page, emojiGroup1[1], defaultImg);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testGetSelectedEmoji() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +
    +  // No emoji should be selected yet
    +  assertUndefined(palette.getSelectedEmoji());
    +
    +  // Artificially select the first emoji
    +  palette.setSelectedIndex(0);
    +
    +  // Now we should get the first emoji back. See emojiGroup1 above.
    +  var emoji = palette.getSelectedEmoji();
    +  assertEquals(emoji.getId(), 'std.200');
    +  assertEquals(emoji.getUrl(), '../../demos/emoji/200.gif');
    +
    +  picker.dispose();
    +}
    +
    +
    +function testGetSelectedEmoji_click() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +  // Artificially select the an emoji
    +  palette.setSelectedIndex(2);
    +  var element = palette.getSelectedItem();
    +  palette.setSelectedIndex(0);  // Select a different emoji.
    +
    +  var eventSent;
    +  handler.listen(picker, goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        eventSent = e;
    +      });
    +  goog.testing.events.fireClickSequence(element, undefined, undefined,
    +      { shiftKey: false });
    +
    +  // Now we should get the first emoji back. See emojiGroup1 above.
    +  var emoji = picker.getSelectedEmoji();
    +  assertEquals(emoji.getId(), 'std.202');
    +  assertEquals(emoji.getUrl(), '../../demos/emoji/202.gif');
    +  assertFalse(eventSent.shiftKey);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testGetSelectedEmoji_shiftClick() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +  // Artificially select the an emoji
    +  palette.setSelectedIndex(3);
    +  var element = palette.getSelectedItem();
    +  palette.setSelectedIndex(0);  // Select a different emoji.
    +
    +  var eventSent;
    +  handler.listen(picker, goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        eventSent = e;
    +      });
    +  goog.testing.events.fireClickSequence(element, undefined, undefined,
    +      { shiftKey: true });
    +
    +  // Now we should get the first emoji back. See emojiGroup1 above.
    +  var emoji = picker.getSelectedEmoji();
    +  assertEquals(emoji.getId(), 'std.203');
    +  assertEquals(emoji.getUrl(), '../../demos/emoji/203.gif');
    +  assertTrue(eventSent.shiftKey);
    +
    +  picker.dispose();
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a non-progressively-rendered
    + * emojipicker.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + */
    +function checkStructureForNonProgressivePicker(palette, emoji) {
    +  // We can hackily check the items by selecting an item and then getting the
    +  // selected item.
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      assertEquals(inner.tagName, 'DIV');
    +      var cssClass = spriteInfo.getCssClass();
    +      if (cssClass) {
    +        assertTrue('Sprite should have its CSS class set',
    +            goog.dom.classlist.contains(inner, cssClass));
    +      } else {
    +        checkPathsEndWithSameFile(
    +            goog.style.getStyle(inner, 'background-image'),
    +            spriteInfo.getUrl());
    +        assertEquals(spriteInfo.getWidthCssValue(),
    +            goog.style.getStyle(inner, 'width'));
    +        assertEquals(spriteInfo.getHeightCssValue(),
    +            goog.style.getStyle(inner, 'height'));
    +        assertEquals((spriteInfo.getXOffsetCssValue() + ' ' +
    +                      spriteInfo.getYOffsetCssValue()).replace(/px/g, '').
    +            replace(/pt/g, ''),
    +            goog.style.getStyle(inner,
    +                'background-position').replace(/px/g, '').
    +                replace(/pt/g, ''));
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a progressively-rendered emojipicker.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + */
    +function checkStructureForProgressivePicker(palette, emoji) {
    +  // We can hackily check the items by selecting an item and then getting the
    +  // selected item.
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      var cssClass = spriteInfo.getCssClass();
    +      if (cssClass) {
    +        assertEquals('DIV', inner.tagName);
    +        assertTrue('Sprite should have its CSS class set',
    +            goog.dom.classlist.contains(inner, cssClass));
    +      } else {
    +        // There's an inner div wrapping an img tag
    +        assertEquals('DIV', inner.tagName);
    +        var img = inner.firstChild;
    +        assertNotNull('Div should be wrapping something', img);
    +        assertEquals('IMG', img.tagName);
    +        checkPathsEndWithSameFile(img.src, spriteInfo.getUrl());
    +        assertEquals(spriteInfo.getWidthCssValue(),
    +                     goog.style.getStyle(inner, 'width'));
    +        assertEquals(spriteInfo.getHeightCssValue(),
    +                     goog.style.getStyle(inner, 'height'));
    +        assertEquals(spriteInfo.getXOffsetCssValue().replace(/px/, '').
    +            replace(/pt/, ''),
    +            goog.style.getStyle(img, 'left').replace(/px/, '').
    +                replace(/pt/, ''));
    +        assertEquals(spriteInfo.getYOffsetCssValue().replace(/px/, '').
    +            replace(/pt/, ''),
    +            goog.style.getStyle(img, 'top').replace(/px/, '').
    +                replace(/pt/, ''));
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a non-progressive fast-loading picker
    + * after the animated emoji have loaded.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + */
    +function checkPostLoadStructureForFastLoadNonProgressivePicker(palette, emoji) {
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var url = emojiInfo[0];   // url of the animated emoji
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      assertEquals(inner.tagName, 'DIV');
    +      if (spriteInfo.isAnimated()) {
    +        var img = new Image();
    +        img.src = url;
    +        checkPathsEndWithSameFile(
    +            goog.style.getStyle(inner, 'background-image'),
    +            url);
    +        assertEquals(String(img.width), goog.style.getStyle(inner, 'width').
    +            replace(/px/g, '').replace(/pt/g, ''));
    +        assertEquals(String(img.height), goog.style.getStyle(inner, 'height').
    +            replace(/px/g, '').replace(/pt/g, ''));
    +        assertEquals('0 0', goog.style.getStyle(inner,
    +            'background-position').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +      } else {
    +        var cssClass = spriteInfo.getCssClass();
    +        if (cssClass) {
    +          assertTrue('Sprite should have its CSS class set',
    +              goog.dom.classlist.contains(inner, cssClass));
    +        } else {
    +          checkPathsEndWithSameFile(
    +              goog.style.getStyle(inner, 'background-image'),
    +              spriteInfo.getUrl());
    +          assertEquals(spriteInfo.getWidthCssValue(),
    +              goog.style.getStyle(inner, 'width'));
    +          assertEquals(spriteInfo.getHeightCssValue(),
    +              goog.style.getStyle(inner, 'height'));
    +          assertEquals((spriteInfo.getXOffsetCssValue() + ' ' +
    +                        spriteInfo.getYOffsetCssValue()).replace(/px/g, '').
    +              replace(/pt/g, ''),
    +              goog.style.getStyle(inner,
    +                  'background-position').replace(/px/g, '').
    +                  replace(/pt/g, ''));
    +        }
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a progressive fast-loading picker
    + * after the animated emoji have loaded.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + */
    +function checkPostLoadStructureForFastLoadProgressivePicker(palette, emoji) {
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var url = emojiInfo[0];  // url of the animated emoji
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      if (spriteInfo.isAnimated()) {
    +        var testImg = new Image();
    +        testImg.src = url;
    +        var img = inner.firstChild;
    +        checkPathsEndWithSameFile(img.src, url);
    +        assertEquals(testImg.width, img.width);
    +        assertEquals(testImg.height, img.height);
    +        assertEquals('0', goog.style.getStyle(img, 'left').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +        assertEquals('0', goog.style.getStyle(img, 'top').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +      } else {
    +        var cssClass = spriteInfo.getCssClass();
    +        if (cssClass) {
    +          assertEquals('DIV', inner.tagName);
    +          assertTrue('Sprite should have its CSS class set',
    +              goog.dom.classlist.contains(inner, cssClass));
    +        } else {
    +          // There's an inner div wrapping an img tag
    +          assertEquals('DIV', inner.tagName);
    +          var img = inner.firstChild;
    +          assertNotNull('Div should be wrapping something', img);
    +          assertEquals('IMG', img.tagName);
    +          checkPathsEndWithSameFile(img.src, spriteInfo.getUrl());
    +          assertEquals(spriteInfo.getWidthCssValue(),
    +              goog.style.getStyle(inner, 'width'));
    +          assertEquals(spriteInfo.getHeightCssValue(),
    +              goog.style.getStyle(inner, 'height'));
    +          assertEquals(spriteInfo.getXOffsetCssValue().replace(/px/, '').
    +              replace(/pt/, ''),
    +              goog.style.getStyle(img, 'left').replace(/px/, '').
    +                  replace(/pt/, ''));
    +          assertEquals(spriteInfo.getYOffsetCssValue().replace(/px/, '').
    +              replace(/pt/, ''),
    +              goog.style.getStyle(img, 'top').replace(/px/, '').
    +                  replace(/pt/, ''));
    +        }
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +
    +function testPreLoadCellConstructionForFastLoadingNonProgressive() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.setManualLoadOfAnimatedEmoji(true);
    +  picker.setProgressiveRender(false);
    +  picker.addEmojiGroup(spritedEmoji2[0], spritedEmoji2[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +
    +  checkStructureForNonProgressivePicker(palette, spritedEmoji2);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testPreLoadCellConstructionForFastLoadingProgressive() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.setManualLoadOfAnimatedEmoji(true);
    +  picker.setProgressiveRender(true);
    +  picker.addEmojiGroup(spritedEmoji2[0], spritedEmoji2[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +
    +  checkStructureForProgressivePicker(palette, spritedEmoji2);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testCellConstructionForNonProgressiveRenderingSpriting() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.addEmojiGroup(spritedEmoji1[0], spritedEmoji1[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +
    +  checkStructureForNonProgressivePicker(palette, spritedEmoji1);
    +  picker.dispose();
    +}
    +
    +
    +function testCellConstructionForProgressiveRenderingSpriting() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(false);
    +  picker.setProgressiveRender(true);
    +  picker.addEmojiGroup(spritedEmoji1[0], spritedEmoji1[1]);
    +  picker.render();
    +
    +  var palette = picker.getPage(0);
    +
    +  checkStructureForProgressivePicker(palette, spritedEmoji1);
    +
    +  picker.dispose();
    +}
    +
    +
    +function testDelayedLoadPaletteCreationForMultiPagePicker() {
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  var picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  picker.setDelayedLoad(true);
    +
    +  for (var i = 0; i < emojiGroups.length; i++) {
    +    picker.addEmojiGroup(emojiGroups[i][0], emojiGroups[i][1]);
    +  }
    +
    +  picker.render();
    +
    +  // At this point the picker should have pages filled with the default img
    +  for (var i = 0; i < emojiGroups.length; i++) {
    +    checkContentIsDefaultImg(picker.getPage(i), defaultImg);
    +  }
    +
    +  // Now load the images
    +  picker.loadImages();
    +
    +  // The first page should be loaded
    +  var page = picker.getPage(0);
    +  assertTrue('Page ' + i + ' should be in the document but is not',
    +             page.isInDocument());
    +  checkContentIsEmojiImages(page, emojiGroups[0][1], defaultImg);
    +
    +  // The other pages should all be filled with the default img since they are
    +  // lazily loaded
    +  for (var i = 1; i < 3; i++) {
    +    checkContentIsDefaultImg(picker.getPage(i), defaultImg);
    +  }
    +
    +  // Activate the other two pages so that their images get loaded, and check
    +  // that they're now loaded correctly
    +  var tabPane = picker.getTabPane();
    +
    +  for (var i = 1; i < 3; i++) {
    +    tabPane.setSelectedIndex(i);
    +    page = picker.getPage(i);
    +    assertTrue(page.isInDocument());
    +    checkContentIsEmojiImages(page, emojiGroups[i][1], defaultImg);
    +  }
    +
    +  picker.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.html
    new file mode 100644
    index 0000000000..f7dd25c2da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.emoji.EmojiPicker
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.emoji.FastNonProgressiveEmojiPickerTest');
    +  </script>
    +  <link rel="stylesheet" href="../../demos/css/emojisprite.css" />
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.js
    new file mode 100644
    index 0000000000..adec7b27a2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_nonprogressive_emojipicker_test.js
    @@ -0,0 +1,248 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.emoji.FastNonProgressiveEmojiPickerTest');
    +goog.setTestOnly('goog.ui.emoji.FastNonProgressiveEmojiPickerTest');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.EventType');
    +goog.require('goog.style');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.emoji.Emoji');
    +goog.require('goog.ui.emoji.EmojiPicker');
    +goog.require('goog.ui.emoji.SpriteInfo');
    +goog.require('goog.userAgent');
    +var sprite = '../../demos/emoji/sprite.png';
    +var sprite2 = '../../demos/emoji/sprite2.png';
    +
    +
    +/**
    + * Creates a SpriteInfo object with the specified properties. If the image is
    + * sprited via CSS, then only the first parameter needs a value. If the image
    + * is sprited via metadata, then the first parameter should be left null.
    + *
    + * @param {?string} cssClass CSS class to properly display the sprited image.
    + * @param {string=} opt_url Url of the sprite image.
    + * @param {number=} opt_width Width of the image being sprited.
    + * @param {number=} opt_height Height of the image being sprited.
    + * @param {number=} opt_xOffset Positive x offset of the image being sprited
    + *     within the sprite.
    + * @param {number=} opt_yOffset Positive y offset of the image being sprited
    + *     within the sprite.
    + * @param {boolean=} opt_animated Whether the sprite info is for an animated
    + *     emoji.
    + */
    +function si(cssClass, opt_url, opt_width, opt_height, opt_xOffset,
    +            opt_yOffset, opt_animated) {
    +  return new goog.ui.emoji.SpriteInfo(cssClass, opt_url, opt_width,
    +      opt_height, opt_xOffset, opt_yOffset, opt_animated);
    +}
    +
    +
    +// This group contains a mix of sprited emoji via css, sprited emoji via
    +// metadata, and non-sprited emoji.
    +var spritedEmoji2 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/204.gif', 'std.204', si('SPRITE_204')],
    +   ['../../demos/emoji/205.gif', 'std.205', si('SPRITE_205')],
    +   ['../../demos/emoji/206.gif', 'std.206', si('SPRITE_206')],
    +   ['../../demos/emoji/2BC.gif', 'std.2BC', si('SPRITE_2BC')],
    +   ['../../demos/emoji/2BD.gif', 'std.2BD', si('SPRITE_2BD')],
    +   ['../../demos/emoji/2BE.gif', 'std.2BE',
    +    si(null, sprite, 18, 18, 36, 54)],
    +   ['../../demos/emoji/2BF.gif', 'std.2BF',
    +    si(null, sprite, 18, 18, 0, 126)],
    +   ['../../demos/emoji/2C0.gif', 'std.2C0',
    +    si(null, sprite, 18, 18, 18, 305)],
    +   ['../../demos/emoji/2C1.gif', 'std.2C1',
    +    si(null, sprite, 18, 18, 0, 287)],
    +   ['../../demos/emoji/2C2.gif', 'std.2C2',
    +    si(null, sprite, 18, 18, 18, 126)],
    +   ['../../demos/emoji/2C3.gif', 'std.2C3',
    +    si(null, sprite, 18, 18, 36, 234)],
    +   ['../../demos/emoji/2C4.gif', 'std.2C4',
    +    si(null, sprite, 18, 18, 36, 72)],
    +   ['../../demos/emoji/2C5.gif', 'std.2C5',
    +    si(null, sprite, 18, 18, 54, 54)],
    +   ['../../demos/emoji/2C6.gif', 'std.2C6'],
    +   ['../../demos/emoji/2C7.gif', 'std.2C7'],
    +   ['../../demos/emoji/2C8.gif', 'std.2C8'],
    +   ['../../demos/emoji/2C9.gif', 'std.2C9'],
    +   ['../../demos/emoji/2CA.gif', 'std.2CA',
    +    si(null, sprite2, 18, 20, 36, 72, 1)],
    +   ['../../demos/emoji/2E3.gif', 'std.2E3',
    +    si(null, sprite2, 18, 18, 0, 0, 1)],
    +   ['../../demos/emoji/2EF.gif', 'std.2EF',
    +    si(null, sprite2, 18, 20, 0, 300, 1)],
    +   ['../../demos/emoji/2F1.gif', 'std.2F1',
    +    si(null, sprite2, 18, 18, 0, 320, 1)]
    +  ]];
    +
    +
    +/**
    + * Returns true if the two paths end with the same file.
    + *
    + * E.g., ('../../cool.gif', 'file:///home/usr/somewhere/cool.gif') --> true
    + *
    + * @param {string} path1 First url
    + * @param {string} path2 Second url
    + */
    +function checkPathsEndWithSameFile(path1, path2) {
    +  var pieces1 = path1.split('/');
    +  var file1 = pieces1[pieces1.length - 1];
    +  var pieces2 = path2.split('/');
    +  var file2 = pieces2[pieces2.length - 1];
    +
    +  return file1 == file2;
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a non-progressive fast-loading picker
    + * after the animated emoji have loaded.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + * @param {Object} images Map of id -> Image for the images loaded in this
    + *     picker.
    + */
    +function checkPostLoadStructureForFastLoadNonProgressivePicker(palette,
    +                                                               emoji,
    +                                                               images) {
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var id = cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE);
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var url = emojiInfo[0];   // url of the animated emoji
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      assertEquals(inner.tagName, 'DIV');
    +      if (spriteInfo.isAnimated()) {
    +        var img = images[id];
    +        checkPathsEndWithSameFile(
    +            goog.style.getStyle(inner, 'background-image'),
    +            url);
    +        assertEquals(String(img.width), goog.style.getStyle(inner, 'width').
    +            replace(/px/g, '').replace(/pt/g, ''));
    +        assertEquals(String(img.height), goog.style.getStyle(inner, 'height').
    +            replace(/px/g, '').replace(/pt/g, ''));
    +        assertEquals('0 0', goog.style.getStyle(inner,
    +            'background-position').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +      } else {
    +        var cssClass = spriteInfo.getCssClass();
    +        if (cssClass) {
    +          assertTrue('Sprite should have its CSS class set',
    +              goog.dom.classlist.contains(inner, cssClass));
    +        } else {
    +          checkPathsEndWithSameFile(
    +              goog.style.getStyle(inner, 'background-image'),
    +              spriteInfo.getUrl());
    +          assertEquals(spriteInfo.getWidthCssValue(),
    +              goog.style.getStyle(inner, 'width'));
    +          assertEquals(spriteInfo.getHeightCssValue(),
    +              goog.style.getStyle(inner, 'height'));
    +          assertEquals((spriteInfo.getXOffsetCssValue() + ' ' +
    +                        spriteInfo.getYOffsetCssValue()).replace(/px/g, '').
    +              replace(/pt/g, ''),
    +              goog.style.getStyle(inner,
    +                  'background-position').replace(/px/g, '').
    +                  replace(/pt/g, ''));
    +        }
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +var testCase = new goog.testing.AsyncTestCase(document.title);
    +testCase.stepTimeout = 4 * 1000;
    +
    +testCase.setUpPage = function() {
    +  this.waitForAsync('setUpPage');
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  this.picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  this.picker.setDelayedLoad(false);
    +  this.picker.setManualLoadOfAnimatedEmoji(true);
    +  this.picker.setProgressiveRender(false);
    +  this.picker.addEmojiGroup(spritedEmoji2[0], spritedEmoji2[1]);
    +  this.picker.render();
    +
    +  this.palette = this.picker.getPage(0);
    +  var imageLoader = this.palette.getImageLoader();
    +  this.images = {};
    +
    +  goog.events.listen(imageLoader, goog.net.EventType.COMPLETE,
    +      this.onImageLoaderComplete, false, this);
    +  goog.events.listen(imageLoader, goog.events.EventType.LOAD,
    +      this.onImageLoaded, false, this);
    +
    +  // Now we load the animated emoji and check the structure again. The animated
    +  // emoji will be different.
    +  this.picker.manuallyLoadAnimatedEmoji();
    +};
    +
    +testCase.onImageLoaded = function(e) {
    +  var image = e.target;
    +  this.log('Image loaded: ' + image.src);
    +  this.images[image.id] = image;
    +};
    +
    +testCase.onImageLoaderComplete = function(e) {
    +  this.log('Image loading complete');
    +  this.continueTesting();
    +};
    +
    +testCase.tearDownPage = function() {
    +  this.picker.dispose();
    +};
    +
    +testCase.addNewTest('testStructure', function() {
    +  // Bug 2280968
    +  if (goog.userAgent.IE && goog.userAgent.VERSION == '6.0') {
    +    this.log('Not testing emojipicker structure');
    +    return;
    +  }
    +
    +  this.log('Testing emojipicker structure');
    +  checkPostLoadStructureForFastLoadNonProgressivePicker(this.palette,
    +      spritedEmoji2, this.images);
    +});
    +
    +
    +// Standalone Closure Test Runner.
    +G_testRunner.initialize(testCase);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.html
    new file mode 100644
    index 0000000000..d91dcbe99f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.emoji.EmojiPicker
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.emoji.FastProgressiveEmojiPickerTest');
    +  </script>
    +  <link rel="stylesheet" href="../../demos/css/emojisprite.css" />
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.js
    new file mode 100644
    index 0000000000..4b5e4ed5e9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/fast_progressive_emojipicker_test.js
    @@ -0,0 +1,248 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.emoji.FastProgressiveEmojiPickerTest');
    +goog.setTestOnly('goog.ui.emoji.FastProgressiveEmojiPickerTest');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.EventType');
    +goog.require('goog.style');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.emoji.Emoji');
    +goog.require('goog.ui.emoji.EmojiPicker');
    +goog.require('goog.ui.emoji.SpriteInfo');
    +var sprite = '../../demos/emoji/sprite.png';
    +var sprite2 = '../../demos/emoji/sprite2.png';
    +
    +
    +/**
    + * Creates a SpriteInfo object with the specified properties. If the image is
    + * sprited via CSS, then only the first parameter needs a value. If the image
    + * is sprited via metadata, then the first parameter should be left null.
    + *
    + * @param {?string} cssClass CSS class to properly display the sprited image.
    + * @param {string=} opt_url Url of the sprite image.
    + * @param {number=} opt_width Width of the image being sprited.
    + * @param {number=} opt_height Height of the image being sprited.
    + * @param {number=} opt_xOffset Positive x offset of the image being sprited
    + *     within the sprite.
    + * @param {number=} opt_yOffset Positive y offset of the image being sprited
    + *     within the sprite.
    + * @param {boolean=} opt_animated Whether the sprite info is for an animated
    + *     emoji.
    + */
    +function si(cssClass, opt_url, opt_width, opt_height, opt_xOffset,
    +            opt_yOffset, opt_animated) {
    +  return new goog.ui.emoji.SpriteInfo(cssClass, opt_url, opt_width,
    +      opt_height, opt_xOffset, opt_yOffset, opt_animated);
    +}
    +
    +// This group contains a mix of sprited emoji via css, sprited emoji via
    +// metadata, and non-sprited emoji.
    +var spritedEmoji2 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200', si('SPRITE_200')],
    +   ['../../demos/emoji/201.gif', 'std.201', si('SPRITE_201')],
    +   ['../../demos/emoji/202.gif', 'std.202', si('SPRITE_202')],
    +   ['../../demos/emoji/203.gif', 'std.203', si('SPRITE_203')],
    +   ['../../demos/emoji/204.gif', 'std.204', si('SPRITE_204')],
    +   ['../../demos/emoji/205.gif', 'std.205', si('SPRITE_205')],
    +   ['../../demos/emoji/206.gif', 'std.206', si('SPRITE_206')],
    +   ['../../demos/emoji/2BC.gif', 'std.2BC', si('SPRITE_2BC')],
    +   ['../../demos/emoji/2BD.gif', 'std.2BD', si('SPRITE_2BD')],
    +   ['../../demos/emoji/2BE.gif', 'std.2BE',
    +    si(null, sprite, 18, 18, 36, 54)],
    +   ['../../demos/emoji/2BF.gif', 'std.2BF',
    +    si(null, sprite, 18, 18, 0, 126)],
    +   ['../../demos/emoji/2C0.gif', 'std.2C0',
    +    si(null, sprite, 18, 18, 18, 305)],
    +   ['../../demos/emoji/2C1.gif', 'std.2C1',
    +    si(null, sprite, 18, 18, 0, 287)],
    +   ['../../demos/emoji/2C2.gif', 'std.2C2',
    +    si(null, sprite, 18, 18, 18, 126)],
    +   ['../../demos/emoji/2C3.gif', 'std.2C3',
    +    si(null, sprite, 18, 18, 36, 234)],
    +   ['../../demos/emoji/2C4.gif', 'std.2C4',
    +    si(null, sprite, 18, 18, 36, 72)],
    +   ['../../demos/emoji/2C5.gif', 'std.2C5',
    +    si(null, sprite, 18, 18, 54, 54)],
    +   ['../../demos/emoji/2C6.gif', 'std.2C6'],
    +   ['../../demos/emoji/2C7.gif', 'std.2C7'],
    +   ['../../demos/emoji/2C8.gif', 'std.2C8'],
    +   ['../../demos/emoji/2C9.gif', 'std.2C9'],
    +   ['../../demos/emoji/2CA.gif', 'std.2CA',
    +    si(null, sprite2, 18, 20, 36, 72, 1)],
    +   ['../../demos/emoji/2E3.gif', 'std.2E3',
    +    si(null, sprite2, 18, 18, 0, 0, 1)],
    +   ['../../demos/emoji/2EF.gif', 'std.2EF',
    +    si(null, sprite2, 18, 20, 0, 300, 1)],
    +   ['../../demos/emoji/2F1.gif', 'std.2F1',
    +    si(null, sprite2, 18, 18, 0, 320, 1)]
    +  ]];
    +
    +
    +/**
    + * Returns true if the two paths end with the same file.
    + *
    + * E.g., ('../../cool.gif', 'file:///home/usr/somewhere/cool.gif') --> true
    + *
    + * @param {string} path1 First url
    + * @param {string} path2 Second url
    + */
    +function checkPathsEndWithSameFile(path1, path2) {
    +  var pieces1 = path1.split('/');
    +  var file1 = pieces1[pieces1.length - 1];
    +  var pieces2 = path2.split('/');
    +  var file2 = pieces2[pieces2.length - 1];
    +
    +  return file1 == file2;
    +}
    +
    +
    +/**
    + * Checks and verifies the structure of a progressive fast-loading picker
    + * after the animated emoji have loaded.
    + *
    + * @param {goog.ui.emoji.EmojiPalette} palette Emoji palette to check.
    + * @param {Array<Array<string>>} emoji Emoji that should be in the palette.
    + * @param {Object} images Map of id -> Image for the images loaded in this
    + *     picker.
    + */
    +function checkPostLoadStructureForFastLoadProgressivePicker(palette,
    +                                                            emoji,
    +                                                            images) {
    +  for (var i = 0; i < emoji[1].length; i++) {
    +    palette.setSelectedIndex(i);
    +    var emojiInfo = emoji[1][i];
    +    var cell = palette.getSelectedItem();
    +    var id = cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE);
    +    var inner = /** @type {Element} */ (cell.firstChild);
    +
    +    // Check that the cell is a div wrapped around something else, and that the
    +    // outer div contains the goomoji attribute
    +    assertEquals('The palette item should be a div wrapped around something',
    +        cell.tagName, 'DIV');
    +    assertNotNull('The outer div is not wrapped around another element', inner);
    +    assertEquals('The palette item should have the goomoji attribute',
    +        cell.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE), emojiInfo[1]);
    +
    +    // Now check the contents of the cells
    +    var url = emojiInfo[0];  // url of the animated emoji
    +    var spriteInfo = emojiInfo[2];
    +    if (spriteInfo) {
    +      if (spriteInfo.isAnimated()) {
    +        var testImg = images[id];
    +        var img = inner.firstChild;
    +        checkPathsEndWithSameFile(img.src, url);
    +        assertEquals(testImg.width, img.width);
    +        assertEquals(testImg.height, img.height);
    +        assertEquals('0', goog.style.getStyle(img, 'left').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +        assertEquals('0', goog.style.getStyle(img, 'top').replace(/px/g, '').
    +            replace(/pt/g, ''));
    +      } else {
    +        var cssClass = spriteInfo.getCssClass();
    +        if (cssClass) {
    +          assertEquals('DIV', inner.tagName);
    +          assertTrue('Sprite should have its CSS class set',
    +              goog.dom.classlist.contains(inner, cssClass));
    +        } else {
    +          // There's an inner div wrapping an img tag
    +          assertEquals('DIV', inner.tagName);
    +          var img = inner.firstChild;
    +          assertNotNull('Div should be wrapping something', img);
    +          assertEquals('IMG', img.tagName);
    +          checkPathsEndWithSameFile(img.src, spriteInfo.getUrl());
    +          assertEquals(spriteInfo.getWidthCssValue(),
    +              goog.style.getStyle(inner, 'width'));
    +          assertEquals(spriteInfo.getHeightCssValue(),
    +              goog.style.getStyle(inner, 'height'));
    +          assertEquals(spriteInfo.getXOffsetCssValue().replace(/px/, '').
    +              replace(/pt/, ''),
    +              goog.style.getStyle(img, 'left').replace(/px/, '').
    +                  replace(/pt/, ''));
    +          assertEquals(spriteInfo.getYOffsetCssValue().replace(/px/, '').
    +              replace(/pt/, ''),
    +              goog.style.getStyle(img, 'top').replace(/px/, '').
    +                  replace(/pt/, ''));
    +        }
    +      }
    +    } else {
    +      // A non-sprited emoji is just an img
    +      assertEquals(inner.tagName, 'IMG');
    +      checkPathsEndWithSameFile(inner.src, emojiInfo[0]);
    +    }
    +  }
    +}
    +
    +var testCase = new goog.testing.AsyncTestCase(document.title);
    +testCase.stepTimeout = 4 * 1000;
    +
    +testCase.setUpPage = function() {
    +  testCase.waitForAsync('setUpPage');
    +  var defaultImg = '../../demos/emoji/none.gif';
    +  this.picker = new goog.ui.emoji.EmojiPicker(defaultImg);
    +  this.picker.setDelayedLoad(false);
    +  this.picker.setManualLoadOfAnimatedEmoji(true);
    +  this.picker.setProgressiveRender(true);
    +  this.picker.addEmojiGroup(spritedEmoji2[0], spritedEmoji2[1]);
    +  this.picker.render();
    +
    +  this.palette = this.picker.getPage(0);
    +  var imageLoader = this.palette.getImageLoader();
    +  this.images = {};
    +
    +  goog.events.listen(imageLoader, goog.net.EventType.COMPLETE,
    +      this.onImageLoaderComplete, false, this);
    +  goog.events.listen(imageLoader, goog.events.EventType.LOAD,
    +      this.onImageLoaded, false, this);
    +
    +  // Now we load the animated emoji and check the structure again. The animated
    +  // emoji will be different.
    +  this.picker.manuallyLoadAnimatedEmoji();
    +};
    +
    +testCase.onImageLoaded = function(e) {
    +  var image = e.target;
    +  this.log('Image loaded: ' + image.src);
    +  this.images[image.id] = image;
    +};
    +
    +testCase.onImageLoaderComplete = function(e) {
    +  this.log('Image loading complete');
    +  this.continueTesting();
    +};
    +
    +testCase.tearDownPage = function() {
    +  this.picker.dispose();
    +};
    +
    +
    +
    +testCase.addNewTest('testStructure', function() {
    +  // Bug 2280968
    +  this.log('Not testing emojipicker structure');
    +  return;
    +
    +  this.log('Testing emojipicker structure');
    +  checkPostLoadStructureForFastLoadProgressivePicker(this.palette,
    +      spritedEmoji2, this.images);
    +});
    +
    +// Standalone Closure Test Runner.
    +G_testRunner.initialize(testCase);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker.js
    new file mode 100644
    index 0000000000..8318f9a8df
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker.js
    @@ -0,0 +1,411 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Popup Emoji Picker implementation. This provides a UI widget
    + * for choosing an emoji from a grid of possible choices. The widget is a popup,
    + * so it is suitable for a toolbar, for instance the TrogEdit toolbar.
    + *
    + * @see ../demos/popupemojipicker.html for an example of how to instantiate
    + * an emoji picker.
    + *
    + * See goog.ui.emoji.EmojiPicker in emojipicker.js for more details.
    + *
    + * Based on goog.ui.PopupColorPicker (popupcolorpicker.js).
    + *
    + * @see ../../demos/popupemojipicker.html
    + */
    +
    +goog.provide('goog.ui.emoji.PopupEmojiPicker');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Popup');
    +goog.require('goog.ui.emoji.EmojiPicker');
    +
    +
    +
    +/**
    + * Constructs a popup emoji picker widget.
    + *
    + * @param {string} defaultImgUrl Url of the img that should be used to fill up
    + *     the cells in the emoji table, to prevent jittering. Should be the same
    + *     size as the emoji.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + * @final
    + */
    +goog.ui.emoji.PopupEmojiPicker =
    +    function(defaultImgUrl, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.emojiPicker_ = new goog.ui.emoji.EmojiPicker(defaultImgUrl,
    +                                                    opt_domHelper);
    +  this.addChild(this.emojiPicker_);
    +
    +  this.getHandler().listen(this.emojiPicker_,
    +      goog.ui.Component.EventType.ACTION, this.onEmojiPicked_);
    +};
    +goog.inherits(goog.ui.emoji.PopupEmojiPicker, goog.ui.Component);
    +
    +
    +/**
    + * Instance of an emoji picker control.
    + * @type {goog.ui.emoji.EmojiPicker}
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.emojiPicker_ = null;
    +
    +
    +/**
    + * Instance of goog.ui.Popup used to manage the behavior of the emoji picker.
    + * @type {goog.ui.Popup}
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.popup_ = null;
    +
    +
    +/**
    + * Reference to the element that triggered the last popup.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.lastTarget_ = null;
    +
    +
    +/**
    + * Whether the emoji picker can accept focus.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.focusable_ = true;
    +
    +
    +/**
    + * If true, then the emojipicker will toggle off if it is already visible.
    + * Default is true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.toggleMode_ = true;
    +
    +
    +/**
    + * Adds a group of emoji to the picker.
    + *
    + * @param {string|Element} title Title for the group.
    + * @param {Array<Array<?>>} emojiGroup A new group of emoji to be added. Each
    + *    internal array contains [emojiUrl, emojiId].
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.addEmojiGroup =
    +    function(title, emojiGroup) {
    +  this.emojiPicker_.addEmojiGroup(title, emojiGroup);
    +};
    +
    +
    +/**
    + * Sets whether the emoji picker should toggle if it is already open.
    + * @param {boolean} toggle The toggle mode to use.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setToggleMode = function(toggle) {
    +  this.toggleMode_ = toggle;
    +};
    +
    +
    +/**
    + * Gets whether the emojipicker is in toggle mode
    + * @return {boolean} toggle.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getToggleMode = function() {
    +  return this.toggleMode_;
    +};
    +
    +
    +/**
    + * Sets whether loading of images should be delayed until after dom creation.
    + * Thus, this function must be called before {@link #createDom}. If set to true,
    + * the client must call {@link #loadImages} when they wish the images to be
    + * loaded.
    + *
    + * @param {boolean} shouldDelay Whether to delay loading the images.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setDelayedLoad =
    +    function(shouldDelay) {
    +  if (this.emojiPicker_) {
    +    this.emojiPicker_.setDelayedLoad(shouldDelay);
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the emoji picker can accept focus.
    + * @param {boolean} focusable Whether the emoji picker should accept focus.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setFocusable = function(focusable) {
    +  this.focusable_ = focusable;
    +  if (this.emojiPicker_) {
    +    // TODO(user): In next revision sort the behavior of passing state to
    +    // children correctly
    +    this.emojiPicker_.setFocusable(focusable);
    +  }
    +};
    +
    +
    +/**
    + * Sets the URL prefix for the emoji URLs.
    + *
    + * @param {string} urlPrefix Prefix that should be prepended to all URLs.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
    +  this.emojiPicker_.setUrlPrefix(urlPrefix);
    +};
    +
    +
    +/**
    + * Sets the location of the tabs in relation to the emoji grids. This should
    + * only be called before the picker has been rendered.
    + *
    + * @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setTabLocation =
    +    function(tabLocation) {
    +  this.emojiPicker_.setTabLocation(tabLocation);
    +};
    +
    +
    +/**
    + * Sets the number of rows per grid in the emoji picker. This should only be
    + * called before the picker has been rendered.
    + *
    + * @param {number} numRows Number of rows per grid.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setNumRows = function(numRows) {
    +  this.emojiPicker_.setNumRows(numRows);
    +};
    +
    +
    +/**
    + * Sets the number of columns per grid in the emoji picker. This should only be
    + * called before the picker has been rendered.
    + *
    + * @param {number} numCols Number of columns per grid.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setNumColumns = function(numCols) {
    +  this.emojiPicker_.setNumColumns(numCols);
    +};
    +
    +
    +/**
    + * Sets the progressive rendering aspect of this emojipicker. Must be called
    + * before createDom to have an effect.
    + *
    + * @param {boolean} progressive Whether the picker should render progressively.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setProgressiveRender =
    +    function(progressive) {
    +  if (this.emojiPicker_) {
    +    this.emojiPicker_.setProgressiveRender(progressive);
    +  }
    +};
    +
    +
    +/**
    + * Returns the number of emoji groups in this picker.
    + *
    + * @return {number} The number of emoji groups in this picker.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getNumEmojiGroups = function() {
    +  return this.emojiPicker_.getNumEmojiGroups();
    +};
    +
    +
    +/**
    + * Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.loadImages = function() {
    +  if (this.emojiPicker_) {
    +    this.emojiPicker_.loadImages();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.PopupEmojiPicker.prototype.createDom = function() {
    +  goog.ui.emoji.PopupEmojiPicker.superClass_.createDom.call(this);
    +
    +  this.emojiPicker_.createDom();
    +
    +  this.getElement().className = goog.getCssName('goog-ui-popupemojipicker');
    +  this.getElement().appendChild(this.emojiPicker_.getElement());
    +
    +  this.popup_ = new goog.ui.Popup(this.getElement());
    +  this.getElement().unselectable = 'on';
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.PopupEmojiPicker.prototype.disposeInternal = function() {
    +  goog.ui.emoji.PopupEmojiPicker.superClass_.disposeInternal.call(this);
    +  this.emojiPicker_ = null;
    +  this.lastTarget_ = null;
    +  if (this.popup_) {
    +    this.popup_.dispose();
    +    this.popup_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Attaches the popup emoji picker to an element.
    + *
    + * @param {Element} element The element to attach to.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.attach = function(element) {
    +  // TODO(user): standardize event type, popups should use MOUSEDOWN, but
    +  // currently apps are using click.
    +  this.getHandler().listen(element, goog.events.EventType.CLICK, this.show_);
    +};
    +
    +
    +/**
    + * Detatches the popup emoji picker from an element.
    + *
    + * @param {Element} element The element to detach from.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.detach = function(element) {
    +  this.getHandler().unlisten(element, goog.events.EventType.CLICK, this.show_);
    +};
    +
    +
    +/**
    + * @return {goog.ui.emoji.EmojiPicker} The emoji picker instance.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getEmojiPicker = function() {
    +  return this.emojiPicker_;
    +};
    +
    +
    +/**
    + * Returns whether the Popup dismisses itself when the user clicks outside of
    + * it.
    + * @return {boolean} Whether the Popup autohides on an external click.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHide = function() {
    +  return !!this.popup_ && this.popup_.getAutoHide();
    +};
    +
    +
    +/**
    + * Sets whether the Popup dismisses itself when the user clicks outside of it -
    + * must be called after the Popup has been created (in createDom()),
    + * otherwise it does nothing.
    + *
    + * @param {boolean} autoHide Whether to autohide on an external click.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHide = function(autoHide) {
    +  if (this.popup_) {
    +    this.popup_.setAutoHide(autoHide);
    +  }
    +};
    +
    +
    +/**
    + * Returns the region inside which the Popup dismisses itself when the user
    + * clicks, or null if it was not set. Null indicates the entire document is
    + * the autohide region.
    + * @return {Element} The DOM element for autohide, or null if it hasn't been
    + *     set.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHideRegion = function() {
    +  return this.popup_ && this.popup_.getAutoHideRegion();
    +};
    +
    +
    +/**
    + * Sets the region inside which the Popup dismisses itself when the user
    + * clicks - must be called after the Popup has been created (in createDom()),
    + * otherwise it does nothing.
    + *
    + * @param {Element} element The DOM element for autohide.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHideRegion = function(element) {
    +  if (this.popup_) {
    +    this.popup_.setAutoHideRegion(element);
    +  }
    +};
    +
    +
    +/**
    + * Returns the {@link goog.ui.PopupBase} from this picker. Returns null if the
    + * popup has not yet been created.
    + *
    + * NOTE: This should *ONLY* be called from tests. If called before createDom(),
    + * this should return null.
    + *
    + * @return {goog.ui.PopupBase?} The popup, or null if it hasn't been created.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getPopup = function() {
    +  return this.popup_;
    +};
    +
    +
    +/**
    + * @return {Element} The last element that triggered the popup.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getLastTarget = function() {
    +  return this.lastTarget_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.emoji.Emoji} The currently selected emoji.
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.getSelectedEmoji = function() {
    +  return this.emojiPicker_.getSelectedEmoji();
    +};
    +
    +
    +/**
    + * Handles click events on the element this picker is attached to and shows the
    + * emoji picker in a popup.
    + *
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.show_ = function(e) {
    +  if (this.popup_.isOrWasRecentlyVisible() && this.toggleMode_ &&
    +      this.lastTarget_ == e.currentTarget) {
    +    this.popup_.setVisible(false);
    +    return;
    +  }
    +
    +  this.lastTarget_ = /** @type {Element} */ (e.currentTarget);
    +  this.popup_.setPosition(new goog.positioning.AnchoredPosition(
    +      this.lastTarget_, goog.positioning.Corner.BOTTOM_LEFT));
    +  this.popup_.setVisible(true);
    +};
    +
    +
    +/**
    + * Handles selection of an emoji.
    + *
    + * @param {goog.events.Event} e The event object.
    + * @private
    + */
    +goog.ui.emoji.PopupEmojiPicker.prototype.onEmojiPicked_ = function(e) {
    +  this.popup_.setVisible(false);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.html
    new file mode 100644
    index 0000000000..33d5378254
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.emoji.PopupEmojiPicker
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.emoji.PopupEmojiPickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="containingDiv">
    +   <button id="button1">
    +   </button>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.js
    new file mode 100644
    index 0000000000..0f7b329187
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/popupemojipicker_test.js
    @@ -0,0 +1,71 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.emoji.PopupEmojiPickerTest');
    +goog.setTestOnly('goog.ui.emoji.PopupEmojiPickerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.emoji.PopupEmojiPicker');
    +var emojiGroup1 = [
    +  'Emoji 1',
    +  [
    +   ['../../demos/emoji/200.gif', 'std.200'],
    +   ['../../demos/emoji/201.gif', 'std.201']
    +  ]];
    +
    +var emojiGroup2 = [
    +  'Emoji 2',
    +  [
    +   ['../../demos/emoji/2D0.gif', 'std.2D0'],
    +   ['../../demos/emoji/2D1.gif', 'std.2D1']
    +  ]];
    +
    +var emojiGroup3 = [
    +  'Emoji 3',
    +  [
    +   ['../../demos/emoji/2E4.gif', 'std.2E4'],
    +   ['../../demos/emoji/2E5.gif', 'std.2E5']
    +  ]];
    +
    +function testConstructAndRenderPopupEmojiPicker() {
    +  var picker = new goog.ui.emoji.PopupEmojiPicker(
    +      '../../demos/emoji/none.gif');
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.addEmojiGroup(emojiGroup2[0], emojiGroup2[1]);
    +  picker.addEmojiGroup(emojiGroup3[0], emojiGroup3[1]);
    +  picker.render();
    +  picker.attach(document.getElementById('button1'));
    +  picker.dispose();
    +}
    +
    +// Unittest to ensure that the popup gets created in createDom().
    +function testPopupCreation() {
    +  var picker = new goog.ui.emoji.PopupEmojiPicker();
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.createDom();
    +  assertNotNull(picker.getPopup());
    +}
    +
    +
    +function testAutoHideIsSetProperly() {
    +  var picker = new goog.ui.emoji.PopupEmojiPicker();
    +  picker.addEmojiGroup(emojiGroup1[0], emojiGroup1[1]);
    +  picker.createDom();
    +  picker.setAutoHide(true);
    +  var containingDiv = goog.dom.getElement('containingDiv');
    +  picker.setAutoHideRegion(containingDiv);
    +  assertTrue(picker.getAutoHide());
    +  assertEquals(containingDiv, picker.getAutoHideRegion());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js
    new file mode 100644
    index 0000000000..c6896a65f1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/progressiveemojipaletterenderer.js
    @@ -0,0 +1,99 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Progressive Emoji Palette renderer implementation.
    + *
    + */
    +
    +goog.provide('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
    +
    +goog.require('goog.style');
    +goog.require('goog.ui.emoji.EmojiPaletteRenderer');
    +
    +
    +
    +/**
    + * Progressively renders an emoji palette. The progressive renderer tries to
    + * use img tags instead of background-image for sprited emoji, since most
    + * browsers render img tags progressively (i.e., as the data comes in), while
    + * only very new browsers render background-image progressively.
    + *
    + * @param {string} defaultImgUrl Url of the img that should be used to fill up
    + *     the cells in the emoji table, to prevent jittering. Will be stretched
    + *     to the emoji cell size. A good image is a transparent dot.
    + * @constructor
    + * @extends {goog.ui.emoji.EmojiPaletteRenderer}
    + * @final
    + */
    +goog.ui.emoji.ProgressiveEmojiPaletteRenderer = function(defaultImgUrl) {
    +  goog.ui.emoji.EmojiPaletteRenderer.call(this, defaultImgUrl);
    +};
    +goog.inherits(goog.ui.emoji.ProgressiveEmojiPaletteRenderer,
    +              goog.ui.emoji.EmojiPaletteRenderer);
    +
    +
    +/** @override */
    +goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
    +    buildElementFromSpriteMetadata = function(dom, spriteInfo, displayUrl) {
    +  var width = spriteInfo.getWidthCssValue();
    +  var height = spriteInfo.getHeightCssValue();
    +  var x = spriteInfo.getXOffsetCssValue();
    +  var y = spriteInfo.getYOffsetCssValue();
    +  // Need this extra div for proper vertical centering.
    +  var inner = dom.createDom('img', {'src': displayUrl});
    +  var el = /** @type {!HTMLDivElement} */ (dom.createDom('div',
    +      goog.getCssName('goog-palette-cell-extra'), inner));
    +  goog.style.setStyle(el, {
    +    'width': width,
    +    'height': height,
    +    'overflow': 'hidden',
    +    'position': 'relative'
    +  });
    +  goog.style.setStyle(inner, {
    +    'left': x,
    +    'top': y,
    +    'position': 'absolute'
    +  });
    +
    +  return el;
    +};
    +
    +
    +/** @override */
    +goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
    +    updateAnimatedPaletteItem = function(item, animatedImg) {
    +  // Just to be safe, we check for the existence of the img element within this
    +  // palette item before attempting to modify it.
    +  var img;
    +  var el = item.firstChild;
    +  while (el) {
    +    if ('IMG' == el.tagName) {
    +      img = /** @type {!Element} */ (el);
    +      break;
    +    }
    +    el = el.firstChild;
    +  }
    +  if (!el) {
    +    return;
    +  }
    +
    +  img.width = animatedImg.width;
    +  img.height = animatedImg.height;
    +  goog.style.setStyle(img, {
    +    'left': 0,
    +    'top': 0
    +  });
    +  img.src = animatedImg.src;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo.js
    new file mode 100644
    index 0000000000..22f053d9b0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo.js
    @@ -0,0 +1,213 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview SpriteInfo implementation. This is a simple wrapper class to
    + * hold CSS metadata needed for sprited emoji.
    + *
    + * @see ../demos/popupemojipicker.html or emojipicker_test.html for examples
    + * of how to use this class.
    + *
    + */
    +goog.provide('goog.ui.emoji.SpriteInfo');
    +
    +
    +
    +/**
    + * Creates a SpriteInfo object with the specified properties. If the image is
    + * sprited via CSS, then only the first parameter needs a value. If the image
    + * is sprited via metadata, then the first parameter should be left null.
    + *
    + * @param {?string} cssClass CSS class to properly display the sprited image.
    + * @param {string=} opt_url Url of the sprite image.
    + * @param {number=} opt_width Width of the image being sprited.
    + * @param {number=} opt_height Height of the image being sprited.
    + * @param {number=} opt_xOffset Positive x offset of the image being sprited
    + *     within the sprite.
    + * @param {number=} opt_yOffset Positive y offset of the image being sprited
    + *     within the sprite.
    + * @param {boolean=} opt_animated Whether the sprite is animated.
    + * @constructor
    + * @final
    + */
    +goog.ui.emoji.SpriteInfo = function(cssClass, opt_url, opt_width, opt_height,
    +                                    opt_xOffset, opt_yOffset, opt_animated) {
    +  if (cssClass != null) {
    +    this.cssClass_ = cssClass;
    +  } else {
    +    if (opt_url == undefined || opt_width === undefined ||
    +        opt_height === undefined || opt_xOffset == undefined ||
    +        opt_yOffset === undefined) {
    +      throw Error('Sprite info is not fully specified');
    +    }
    +
    +    this.url_ = opt_url;
    +    this.width_ = opt_width;
    +    this.height_ = opt_height;
    +    this.xOffset_ = opt_xOffset;
    +    this.yOffset_ = opt_yOffset;
    +  }
    +
    +  this.animated_ = !!opt_animated;
    +};
    +
    +
    +/**
    + * Name of the CSS class to properly display the sprited image.
    + * @type {string}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.cssClass_;
    +
    +
    +/**
    + * Url of the sprite image.
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.url_;
    +
    +
    +/**
    + * Width of the image being sprited.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.width_;
    +
    +
    +/**
    + * Height of the image being sprited.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.height_;
    +
    +
    +/**
    + * Positive x offset of the image being sprited within the sprite.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.xOffset_;
    +
    +
    +/**
    + * Positive y offset of the image being sprited within the sprite.
    + * @type {number|undefined}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.yOffset_;
    +
    +
    +/**
    + * Whether the emoji specified by the sprite is animated.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.prototype.animated_;
    +
    +
    +/**
    + * Returns the css class of the sprited image.
    + * @return {?string} Name of the CSS class to properly display the sprited
    + *     image.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getCssClass = function() {
    +  return this.cssClass_ || null;
    +};
    +
    +
    +/**
    + * Returns the url of the sprite image.
    + * @return {?string} Url of the sprite image.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getUrl = function() {
    +  return this.url_ || null;
    +};
    +
    +
    +/**
    + * Returns whether the emoji specified by this sprite is animated.
    + * @return {boolean} Whether the emoji is animated.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.isAnimated = function() {
    +  return this.animated_;
    +};
    +
    +
    +/**
    + * Returns the width of the image being sprited, appropriate for a CSS value.
    + * @return {string} The width of the image being sprited.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getWidthCssValue = function() {
    +  return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.width_);
    +};
    +
    +
    +/**
    + * Returns the height of the image being sprited, appropriate for a CSS value.
    + * @return {string} The height of the image being sprited.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getHeightCssValue = function() {
    +  return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.height_);
    +};
    +
    +
    +/**
    + * Returns the x offset of the image being sprited within the sprite,
    + * appropriate for a CSS value.
    + * @return {string} The x offset of the image being sprited within the sprite.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getXOffsetCssValue = function() {
    +  return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.xOffset_);
    +};
    +
    +
    +/**
    + * Returns the positive y offset of the image being sprited within the sprite,
    + * appropriate for a CSS value.
    + * @return {string} The y offset of the image being sprited within the sprite.
    + */
    +goog.ui.emoji.SpriteInfo.prototype.getYOffsetCssValue = function() {
    +  return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.yOffset_);
    +};
    +
    +
    +/**
    + * Returns a string appropriate for use as a CSS value. If the value is zero,
    + * then there is no unit appended.
    + *
    + * @param {number|undefined} value A number to be turned into a
    + *     CSS size/location value.
    + * @return {string} A string appropriate for use as a CSS value.
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.getCssPixelValue_ = function(value) {
    +  return !value ? '0' : value + 'px';
    +};
    +
    +
    +/**
    + * Returns a string appropriate for use as a CSS value for a position offset,
    + * such as the position argument for sprites.
    + *
    + * @param {number|undefined} posOffset A positive offset for a position.
    + * @return {string} A string appropriate for use as a CSS value.
    + * @private
    + */
    +goog.ui.emoji.SpriteInfo.getOffsetCssValue_ = function(posOffset) {
    +  var offset = goog.ui.emoji.SpriteInfo.getCssPixelValue_(posOffset);
    +  return offset == '0' ? offset : '-' + offset;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.html b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.html
    new file mode 100644
    index 0000000000..0af1922cdf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.emoji.SpriteInfo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.emoji.SpriteInfoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.js b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.js
    new file mode 100644
    index 0000000000..c1a4e3c717
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/emoji/spriteinfo_test.js
    @@ -0,0 +1,45 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.emoji.SpriteInfoTest');
    +goog.setTestOnly('goog.ui.emoji.SpriteInfoTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.emoji.SpriteInfo');
    +function testGetCssValues() {
    +  var si = new goog.ui.emoji.SpriteInfo(null, 'im/s.png', 10, 10, 0, 128);
    +  assertEquals('10px', si.getWidthCssValue());
    +  assertEquals('10px', si.getHeightCssValue());
    +  assertEquals('0', si.getXOffsetCssValue());
    +  assertEquals('-128px', si.getYOffsetCssValue());
    +}
    +
    +function testIncompletelySpecifiedSpriteInfoFails() {
    +  assertThrows('CSS class can\'t be null if the rest of the metadata ' +
    +               'isn\'t specified', function() {
    +        new goog.ui.emoji.SpriteInfo(null)});
    +
    +  assertThrows('Can\'t create an incompletely specified sprite info',
    +      function() {
    +        new goog.ui.emoji.SpriteInfo(null, 's.png', 10);
    +      });
    +
    +  assertThrows('Can\'t create an incompletely specified sprite info',
    +      function() {
    +        new goog.ui.emoji.SpriteInfo(null, 's.png', 10, 10);
    +      });
    +
    +  assertThrows('Can\'t create an incompletely specified sprite info',
    +      function() {new goog.ui.emoji.SpriteInfo(null, 's.png', 10, 10, 0);});
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/filteredmenu.js b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu.js
    new file mode 100644
    index 0000000000..a5e5fd142e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu.js
    @@ -0,0 +1,633 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Menu where items can be filtered based on user keyboard input.
    + * If a filter is specified only the items matching it will be displayed.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/filteredmenu.html
    + */
    +
    +
    +goog.provide('goog.ui.FilteredMenu');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.AutoCompleteValues');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.FilterObservingMenuItem');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Filtered menu class.
    + * @param {goog.ui.MenuRenderer=} opt_renderer Renderer used to render filtered
    + *     menu; defaults to {@link goog.ui.MenuRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Menu}
    + */
    +goog.ui.FilteredMenu = function(opt_renderer, opt_domHelper) {
    +  goog.ui.Menu.call(this, opt_domHelper, opt_renderer);
    +};
    +goog.inherits(goog.ui.FilteredMenu, goog.ui.Menu);
    +goog.tagUnsealableClass(goog.ui.FilteredMenu);
    +
    +
    +/**
    + * Events fired by component.
    + * @enum {string}
    + */
    +goog.ui.FilteredMenu.EventType = {
    +  /** Dispatched after the component filter criteria has been changed. */
    +  FILTER_CHANGED: 'filterchange'
    +};
    +
    +
    +/**
    + * Filter menu element ids.
    + * @enum {string}
    + * @private
    + */
    +goog.ui.FilteredMenu.Id_ = {
    +  CONTENT_ELEMENT: 'content-el'
    +};
    +
    +
    +/**
    + * Filter input element.
    + * @type {Element|undefined}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.filterInput_;
    +
    +
    +/**
    + * The input handler that provides the input event.
    + * @type {goog.events.InputHandler|undefined}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.inputHandler_;
    +
    +
    +/**
    + * Maximum number of characters for filter input.
    + * @type {number}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.maxLength_ = 0;
    +
    +
    +/**
    + * Label displayed in the filter input when no text has been entered.
    + * @type {string}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.label_ = '';
    +
    +
    +/**
    + * Label element.
    + * @type {Element|undefined}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.labelEl_;
    +
    +
    +/**
    + * Whether multiple items can be entered comma separated.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.allowMultiple_ = false;
    +
    +
    +/**
    + * List of items entered in the search box if multiple entries are allowed.
    + * @type {Array<string>|undefined}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.enteredItems_;
    +
    +
    +/**
    + * Index of first item that should be affected by the filter. Menu items with
    + * a lower index will not be affected by the filter.
    + * @type {number}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.filterFromIndex_ = 0;
    +
    +
    +/**
    + * Filter applied to the menu.
    + * @type {string|undefined|null}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.filterStr_;
    +
    +
    +/**
    + * @private {Element}
    + */
    +goog.ui.FilteredMenu.prototype.contentElement_;
    +
    +
    +/**
    + * Map of child nodes that shouldn't be affected by filtering.
    + * @type {Object|undefined}
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.persistentChildren_;
    +
    +
    +/** @override */
    +goog.ui.FilteredMenu.prototype.createDom = function() {
    +  goog.ui.FilteredMenu.superClass_.createDom.call(this);
    +
    +  var dom = this.getDomHelper();
    +  var el = dom.createDom('div',
    +      goog.getCssName(this.getRenderer().getCssClass(), 'filter'),
    +      this.labelEl_ = dom.createDom('div', null, this.label_),
    +      this.filterInput_ = dom.createDom('input', {'type': 'text'}));
    +  var element = this.getElement();
    +  dom.appendChild(element, el);
    +  var contentElementId = this.makeId(goog.ui.FilteredMenu.Id_.CONTENT_ELEMENT);
    +  this.contentElement_ = dom.createDom('div', goog.object.create(
    +      'class', goog.getCssName(this.getRenderer().getCssClass(), 'content'),
    +      'id', contentElementId));
    +  dom.appendChild(element, this.contentElement_);
    +
    +  this.initFilterInput_();
    +
    +  goog.a11y.aria.setState(this.filterInput_, goog.a11y.aria.State.AUTOCOMPLETE,
    +      goog.a11y.aria.AutoCompleteValues.LIST);
    +  goog.a11y.aria.setState(this.filterInput_, goog.a11y.aria.State.OWNS,
    +      contentElementId);
    +  goog.a11y.aria.setState(this.filterInput_, goog.a11y.aria.State.EXPANDED,
    +      true);
    +};
    +
    +
    +/**
    + * Helper method that initializes the filter input element.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.initFilterInput_ = function() {
    +  this.setFocusable(true);
    +  this.setKeyEventTarget(this.filterInput_);
    +
    +  // Workaround for mozilla bug #236791.
    +  if (goog.userAgent.GECKO) {
    +    this.filterInput_.setAttribute('autocomplete', 'off');
    +  }
    +
    +  if (this.maxLength_) {
    +    this.filterInput_.maxLength = this.maxLength_;
    +  }
    +};
    +
    +
    +/**
    + * Sets up listeners and prepares the filter functionality.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.setUpFilterListeners_ = function() {
    +  if (!this.inputHandler_ && this.filterInput_) {
    +    this.inputHandler_ = new goog.events.InputHandler(
    +        /** @type {Element} */ (this.filterInput_));
    +    goog.style.setUnselectable(this.filterInput_, false);
    +    goog.events.listen(this.inputHandler_,
    +                       goog.events.InputHandler.EventType.INPUT,
    +                       this.handleFilterEvent, false, this);
    +    goog.events.listen(this.filterInput_.parentNode,
    +                       goog.events.EventType.CLICK,
    +                       this.onFilterLabelClick_, false, this);
    +    if (this.allowMultiple_) {
    +      this.enteredItems_ = [];
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Tears down listeners and resets the filter functionality.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.tearDownFilterListeners_ = function() {
    +  if (this.inputHandler_) {
    +    goog.events.unlisten(this.inputHandler_,
    +                         goog.events.InputHandler.EventType.INPUT,
    +                         this.handleFilterEvent, false, this);
    +    goog.events.unlisten(this.filterInput_.parentNode,
    +                         goog.events.EventType.CLICK,
    +                         this.onFilterLabelClick_, false, this);
    +
    +    this.inputHandler_.dispose();
    +    this.inputHandler_ = undefined;
    +    this.enteredItems_ = undefined;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.FilteredMenu.prototype.setVisible = function(show, opt_force, opt_e) {
    +  var visibilityChanged = goog.ui.FilteredMenu.superClass_.setVisible.call(this,
    +      show, opt_force, opt_e);
    +  if (visibilityChanged && show && this.isInDocument()) {
    +    this.setFilter('');
    +    this.setUpFilterListeners_();
    +  } else if (visibilityChanged && !show) {
    +    this.tearDownFilterListeners_();
    +  }
    +
    +  return visibilityChanged;
    +};
    +
    +
    +/** @override */
    +goog.ui.FilteredMenu.prototype.disposeInternal = function() {
    +  this.tearDownFilterListeners_();
    +  this.filterInput_ = undefined;
    +  this.labelEl_ = undefined;
    +  goog.ui.FilteredMenu.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Sets the filter label (the label displayed in the filter input element if no
    + * text has been entered).
    + * @param {?string} label Label text.
    + */
    +goog.ui.FilteredMenu.prototype.setFilterLabel = function(label) {
    +  this.label_ = label || '';
    +  if (this.labelEl_) {
    +    goog.dom.setTextContent(this.labelEl_, this.label_);
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The filter label.
    + */
    +goog.ui.FilteredMenu.prototype.getFilterLabel = function() {
    +  return this.label_;
    +};
    +
    +
    +/**
    + * Sets the filter string.
    + * @param {?string} str Filter string.
    + */
    +goog.ui.FilteredMenu.prototype.setFilter = function(str) {
    +  if (this.filterInput_) {
    +    this.filterInput_.value = str;
    +    this.filterItems_(str);
    +  }
    +};
    +
    +
    +/**
    + * Returns the filter string.
    + * @return {string} Current filter or an an empty string.
    + */
    +goog.ui.FilteredMenu.prototype.getFilter = function() {
    +  return this.filterInput_ && goog.isString(this.filterInput_.value) ?
    +      this.filterInput_.value : '';
    +};
    +
    +
    +/**
    + * Sets the index of first item that should be affected by the filter. Menu
    + * items with a lower index will not be affected by the filter.
    + * @param {number} index Index of first item that should be affected by filter.
    + */
    +goog.ui.FilteredMenu.prototype.setFilterFromIndex = function(index) {
    +  this.filterFromIndex_ = index;
    +};
    +
    +
    +/**
    + * Returns the index of first item that is affected by the filter.
    + * @return {number} Index of first item that is affected by filter.
    + */
    +goog.ui.FilteredMenu.prototype.getFilterFromIndex = function() {
    +  return this.filterFromIndex_;
    +};
    +
    +
    +/**
    + * Gets a list of items entered in the search box.
    + * @return {!Array<string>} The entered items.
    + */
    +goog.ui.FilteredMenu.prototype.getEnteredItems = function() {
    +  return this.enteredItems_ || [];
    +};
    +
    +
    +/**
    + * Sets whether multiple items can be entered comma separated.
    + * @param {boolean} b Whether multiple items can be entered.
    + */
    +goog.ui.FilteredMenu.prototype.setAllowMultiple = function(b) {
    +  this.allowMultiple_ = b;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether multiple items can be entered comma separated.
    + */
    +goog.ui.FilteredMenu.prototype.getAllowMultiple = function() {
    +  return this.allowMultiple_;
    +};
    +
    +
    +/**
    + * Sets whether the specified child should be affected (shown/hidden) by the
    + * filter criteria.
    + * @param {goog.ui.Component} child Child to change.
    + * @param {boolean} persistent Whether the child should be persistent.
    + */
    +goog.ui.FilteredMenu.prototype.setPersistentVisibility = function(child,
    +                                                                  persistent) {
    +  if (!this.persistentChildren_) {
    +    this.persistentChildren_ = {};
    +  }
    +  this.persistentChildren_[child.getId()] = persistent;
    +};
    +
    +
    +/**
    + * Returns whether the specified child should be affected (shown/hidden) by the
    + * filter criteria.
    + * @param {goog.ui.Component} child Menu item to check.
    + * @return {boolean} Whether the menu item is persistent.
    + */
    +goog.ui.FilteredMenu.prototype.hasPersistentVisibility = function(child) {
    +  return !!(this.persistentChildren_ &&
    +            this.persistentChildren_[child.getId()]);
    +};
    +
    +
    +/**
    + * Handles filter input events.
    + * @param {goog.events.BrowserEvent} e The event object.
    + */
    +goog.ui.FilteredMenu.prototype.handleFilterEvent = function(e) {
    +  this.filterItems_(this.filterInput_.value);
    +
    +  // Highlight the first visible item unless there's already a highlighted item.
    +  var highlighted = this.getHighlighted();
    +  if (!highlighted || !highlighted.isVisible()) {
    +    this.highlightFirst();
    +  }
    +  this.dispatchEvent(goog.ui.FilteredMenu.EventType.FILTER_CHANGED);
    +};
    +
    +
    +/**
    + * Shows/hides elements based on the supplied filter.
    + * @param {?string} str Filter string.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.filterItems_ = function(str) {
    +  // Do nothing unless the filter string has changed.
    +  if (this.filterStr_ == str) {
    +    return;
    +  }
    +
    +  if (this.labelEl_) {
    +    this.labelEl_.style.visibility = str == '' ? 'visible' : 'hidden';
    +  }
    +
    +  if (this.allowMultiple_ && this.enteredItems_) {
    +    // Matches all non space characters after the last comma.
    +    var lastWordRegExp = /^(.+),[ ]*([^,]*)$/;
    +    var matches = str.match(lastWordRegExp);
    +    // matches[1] is the string up to, but not including, the last comma and
    +    // matches[2] the part after the last comma. If there are no non-space
    +    // characters after the last comma matches[2] is undefined.
    +    var items = matches && matches[1] ? matches[1].split(',') : [];
    +
    +    // If the number of comma separated items has changes recreate the
    +    // entered items array and fire a change event.
    +    if (str.substr(str.length - 1, 1) == ',' ||
    +        items.length != this.enteredItems_.length) {
    +      var lastItem = items[items.length - 1] || '';
    +
    +      // Auto complete text in input box based on the highlighted item.
    +      if (this.getHighlighted() && lastItem != '') {
    +        var caption = this.getHighlighted().getCaption();
    +        if (caption.toLowerCase().indexOf(lastItem.toLowerCase()) == 0) {
    +          items[items.length - 1] = caption;
    +          this.filterInput_.value = items.join(',') + ',';
    +        }
    +      }
    +      this.enteredItems_ = items;
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +      this.setHighlightedIndex(-1);
    +    }
    +
    +    if (matches) {
    +      str = matches.length > 2 ? goog.string.trim(matches[2]) : '';
    +    }
    +  }
    +
    +  var matcher = new RegExp('(^|[- ,_/.:])' +
    +      goog.string.regExpEscape(str), 'i');
    +  for (var child, i = this.filterFromIndex_; child = this.getChildAt(i); i++) {
    +    if (child instanceof goog.ui.FilterObservingMenuItem) {
    +      child.callObserver(str);
    +    } else if (!this.hasPersistentVisibility(child)) {
    +      // Only show items matching the filter and highlight the part of the
    +      // caption that matches.
    +      var caption = child.getCaption();
    +      if (caption) {
    +        var matchArray = caption.match(matcher);
    +        if (str == '' || matchArray) {
    +          child.setVisible(true);
    +          var pos = caption.indexOf(matchArray[0]);
    +
    +          // If position is non zero increase by one to skip the separator.
    +          if (pos) {
    +            pos++;
    +          }
    +          this.boldContent_(child, pos, str.length);
    +        } else {
    +          child.setVisible(false);
    +        }
    +      } else {
    +
    +        // Hide separators and other items without a caption if a filter string
    +        // has been entered.
    +        child.setVisible(str == '');
    +      }
    +    }
    +  }
    +  this.filterStr_ = str;
    +};
    +
    +
    +/**
    + * Updates the content of the given menu item, bolding the part of its caption
    + * from start and through the next len characters.
    + * @param {!goog.ui.Control} child The control to bold content on.
    + * @param {number} start The index at which to start bolding.
    + * @param {number} len How many characters to bold.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.boldContent_ = function(child, start, len) {
    +  var caption = child.getCaption();
    +  var boldedCaption;
    +  if (len == 0) {
    +    boldedCaption = this.getDomHelper().createTextNode(caption);
    +  } else {
    +    var preMatch = caption.substr(0, start);
    +    var match = caption.substr(start, len);
    +    var postMatch = caption.substr(start + len);
    +    boldedCaption = this.getDomHelper().createDom(
    +        'span',
    +        null,
    +        preMatch,
    +        this.getDomHelper().createDom('b', null, match),
    +        postMatch);
    +  }
    +  var accelerator = child.getAccelerator && child.getAccelerator();
    +  if (accelerator) {
    +    child.setContent([boldedCaption, this.getDomHelper().createDom('span',
    +        goog.ui.MenuItem.ACCELERATOR_CLASS, accelerator)]);
    +  } else {
    +    child.setContent(boldedCaption);
    +  }
    +};
    +
    +
    +/**
    + * Handles the menu's behavior for a key event. The highlighted menu item will
    + * be given the opportunity to handle the key behavior.
    + * @param {goog.events.KeyEvent} e A browser event.
    + * @return {boolean} Whether the event was handled.
    + * @override
    + */
    +goog.ui.FilteredMenu.prototype.handleKeyEventInternal = function(e) {
    +  // Home, end and the arrow keys are normally used to change the selected menu
    +  // item. Return false here to prevent the menu from preventing the default
    +  // behavior for HOME, END and any key press with a modifier.
    +  if (e.shiftKey || e.ctrlKey || e.altKey ||
    +      e.keyCode == goog.events.KeyCodes.HOME ||
    +      e.keyCode == goog.events.KeyCodes.END) {
    +    return false;
    +  }
    +
    +  if (e.keyCode == goog.events.KeyCodes.ESC) {
    +    this.dispatchEvent(goog.ui.Component.EventType.BLUR);
    +    return true;
    +  }
    +
    +  return goog.ui.FilteredMenu.superClass_.handleKeyEventInternal.call(this, e);
    +};
    +
    +
    +/**
    + * Sets the highlighted index, unless the HIGHLIGHT event is intercepted and
    + * cancelled.  -1 = no highlight. Also scrolls the menu item into view.
    + * @param {number} index Index of menu item to highlight.
    + * @override
    + */
    +goog.ui.FilteredMenu.prototype.setHighlightedIndex = function(index) {
    +  goog.ui.FilteredMenu.superClass_.setHighlightedIndex.call(this, index);
    +  var contentEl = this.getContentElement();
    +  var el = this.getHighlighted() ? this.getHighlighted().getElement() : null;
    +  if (this.filterInput_) {
    +    goog.a11y.aria.setActiveDescendant(this.filterInput_, el);
    +  }
    +
    +  if (el && goog.dom.contains(contentEl, el)) {
    +    var contentTop = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(8) ?
    +        0 : contentEl.offsetTop;
    +
    +    // IE (tested on IE8) sometime does not scroll enough by about
    +    // 1px. So we add 1px to the scroll amount. This still looks ok in
    +    // other browser except for the most degenerate case (menu height <=
    +    // item height).
    +
    +    // Scroll down if the highlighted item is below the bottom edge.
    +    var diff = (el.offsetTop + el.offsetHeight - contentTop) -
    +        (contentEl.clientHeight + contentEl.scrollTop) + 1;
    +    contentEl.scrollTop += Math.max(diff, 0);
    +
    +    // Scroll up if the highlighted item is above the top edge.
    +    diff = contentEl.scrollTop - (el.offsetTop - contentTop) + 1;
    +    contentEl.scrollTop -= Math.max(diff, 0);
    +  }
    +};
    +
    +
    +/**
    + * Handles clicks on the filter label. Focuses the input element.
    + * @param {goog.events.BrowserEvent} e A browser event.
    + * @private
    + */
    +goog.ui.FilteredMenu.prototype.onFilterLabelClick_ = function(e) {
    +  this.filterInput_.focus();
    +};
    +
    +
    +/** @override */
    +goog.ui.FilteredMenu.prototype.getContentElement = function() {
    +  return this.contentElement_ || this.getElement();
    +};
    +
    +
    +/**
    + * Returns the filter input element.
    + * @return {Element} Input element.
    + */
    +goog.ui.FilteredMenu.prototype.getFilterInputElement = function() {
    +  return this.filterInput_ || null;
    +};
    +
    +
    +/** @override */
    +goog.ui.FilteredMenu.prototype.decorateInternal = function(element) {
    +  this.setElementInternal(element);
    +
    +  // Decorate the menu content.
    +  this.decorateContent(element);
    +
    +  // Locate internally managed elements.
    +  var el = this.getDomHelper().getElementsByTagNameAndClass('div',
    +      goog.getCssName(this.getRenderer().getCssClass(), 'filter'), element)[0];
    +  this.labelEl_ = goog.dom.getFirstElementChild(el);
    +  this.filterInput_ = goog.dom.getNextElementSibling(this.labelEl_);
    +  this.contentElement_ = goog.dom.getNextElementSibling(el);
    +
    +  // Decorate additional menu items (like 'apply').
    +  this.getRenderer().decorateChildren(this, el.parentNode,
    +      this.contentElement_);
    +
    +  this.initFilterInput_();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.html b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.html
    new file mode 100644
    index 0000000000..36ee121f6d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.html
    @@ -0,0 +1,61 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author eae@google.com (Emil A Eklund)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.FilteredMenu
    +  </title>
    +  <style>
    +   .goog-menu {
    +  position: absolute;
    +  background: #eee;
    +  border: 1px solid #aaa;
    +  font: menu;
    +}
    +.goog-menu-content {
    +  height: 2em;
    +  overflow: auto;
    +}
    +
    +#testmenu {
    +  right: 5px;
    +  bottom: 5px;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.FilteredMenuTest');
    +  </script>
    + </head>
    + <body>
    +  <div>
    +    <div id="wrapper">
    +      <div id="sandbox"></div>
    +    </div>
    +
    +    <div id="testmenu" class="goog-menu goog-menu-vertical">
    +      <div class="goog-menu-filter">
    +        <div></div>
    +        <input tabindex="0" autocomplete="off" type="text">
    +      </div>
    +      <div class="goog-menu-content">
    +        <div class="goog-menuitem">Apple<span class="goog-menuitem-accel">A</span></div>
    +        <div class="goog-menuitem">Lemon</div>
    +        <div class="goog-menuitem">Orange</div>
    +        <div class="goog-menuitem">Strawberry</div>
    +      </div>
    +    </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.js b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.js
    new file mode 100644
    index 0000000000..63a0de69cc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/filteredmenu_test.js
    @@ -0,0 +1,308 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.FilteredMenuTest');
    +goog.setTestOnly('goog.ui.FilteredMenuTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.AutoCompleteValues');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Rect');
    +goog.require('goog.style');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.FilteredMenu');
    +goog.require('goog.ui.MenuItem');
    +
    +var sandbox;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +}
    +
    +
    +function tearDown() {
    +  sandbox.innerHTML = '';
    +}
    +
    +
    +function testRender() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Menu Item 1'));
    +  menu.addItem(new goog.ui.MenuItem('Menu Item 2'));
    +  menu.render(sandbox);
    +
    +  assertEquals('Menu should contain two items.', 2, menu.getChildCount());
    +  assertEquals('Caption of first menu item should match supplied value.',
    +               'Menu Item 1',
    +               menu.getItemAt(0).getCaption());
    +  assertEquals('Caption of second menu item should match supplied value.',
    +               'Menu Item 2',
    +               menu.getItemAt(1).getCaption());
    +  assertTrue('Caption of first item should be in document.',
    +      sandbox.innerHTML.indexOf('Menu Item 1') != -1);
    +  assertTrue('Caption of second item should be in document.',
    +      sandbox.innerHTML.indexOf('Menu Item 2') != -1);
    +
    +  menu.dispose();
    +}
    +
    +
    +function testDecorate() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.decorate(goog.dom.getElement('testmenu'));
    +
    +  assertEquals('Menu should contain four items.', 4, menu.getChildCount());
    +  assertEquals('Caption of menu item should match decorated element',
    +               'Apple',
    +               menu.getItemAt(0).getCaption());
    +  assertEquals('Accelerator of menu item should match accelerator element',
    +               'A',
    +               menu.getItemAt(0).getAccelerator());
    +  assertEquals('Caption of menu item should match decorated element',
    +               'Lemon',
    +               menu.getItemAt(1).getCaption());
    +  assertEquals('Caption of menu item should match decorated element',
    +               'Orange',
    +               menu.getItemAt(2).getCaption());
    +  assertEquals('Caption of menu item should match decorated element',
    +               'Strawberry',
    +               menu.getItemAt(3).getCaption());
    +
    +  menu.dispose();
    +}
    +
    +
    +function testFilter() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Family'));
    +  menu.addItem(new goog.ui.MenuItem('Friends'));
    +  menu.addItem(new goog.ui.MenuItem('Photos'));
    +  menu.addItem(new goog.ui.MenuItem([
    +    goog.dom.createTextNode('Work'),
    +    goog.dom.createDom('span', goog.ui.MenuItem.ACCELERATOR_CLASS, 'W')
    +  ]));
    +
    +  menu.render(sandbox);
    +
    +  // Check menu items.
    +  assertEquals('Family should be the first label in the move to menu',
    +               'Family', menu.getChildAt(0).getCaption());
    +  assertEquals('Friends should be the second label in the move to menu',
    +               'Friends', menu.getChildAt(1).getCaption());
    +  assertEquals('Photos should be the third label in the move to menu',
    +               'Photos', menu.getChildAt(2).getCaption());
    +  assertEquals('Work should be the fourth label in the move to menu',
    +               'Work', menu.getChildAt(3).getCaption());
    +
    +  // Filter menu.
    +  menu.setFilter('W');
    +  assertFalse('Family should not be visible when the menu is filtered',
    +              menu.getChildAt(0).isVisible());
    +  assertFalse('Friends should not be visible when the menu is filtered',
    +              menu.getChildAt(1).isVisible());
    +  assertFalse('Photos should not be visible when the menu is filtered',
    +              menu.getChildAt(2).isVisible());
    +  assertTrue('Work should be visible when the menu is filtered',
    +             menu.getChildAt(3).isVisible());
    +  // Check accelerator.
    +  assertEquals('The accelerator for Work should be present',
    +               'W', menu.getChildAt(3).getAccelerator());
    +
    +  menu.setFilter('W,');
    +  for (var i = 0; i < menu.getChildCount(); i++) {
    +    assertFalse('W, should not match anything with allowMultiple set to false',
    +        menu.getChildAt(i).isVisible());
    +  }
    +
    +  // Clear filter.
    +  menu.setFilter('');
    +  for (var i = 0; i < menu.getChildCount(); i++) {
    +    assertTrue('All items should be visible', menu.getChildAt(i).isVisible());
    +  }
    +
    +  menu.dispose();
    +}
    +
    +
    +function testFilterAllowMultiple() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.setAllowMultiple(true);
    +  menu.addItem(new goog.ui.MenuItem('Family'));
    +  menu.addItem(new goog.ui.MenuItem('Friends'));
    +  menu.addItem(new goog.ui.MenuItem('Photos'));
    +  menu.addItem(new goog.ui.MenuItem('Work'));
    +
    +  menu.render(sandbox);
    +
    +  // Filter menu.
    +  menu.setFilter('W,');
    +  for (var i = 0; i < menu.getChildCount(); i++) {
    +    assertTrue('W, should show all items with allowMultiple set to true',
    +               menu.getChildAt(i).isVisible());
    +  }
    +
    +  // Filter second label.
    +  menu.setFilter('Work,P');
    +  assertFalse('Family should not be visible when the menu is filtered',
    +              menu.getChildAt(0).isVisible());
    +  assertFalse('Friends should not be visible when the menu is filtered',
    +              menu.getChildAt(1).isVisible());
    +  assertTrue('Photos should be visible when the menu is filtered',
    +             menu.getChildAt(2).isVisible());
    +  assertFalse('Work should not be visible when the menu is filtered',
    +              menu.getChildAt(3).isVisible());
    +
    +  // Clear filter.
    +  menu.setFilter('');
    +  for (var i = 0; i < menu.getChildCount(); i++) {
    +    assertTrue('All items should be visible', menu.getChildAt(i).isVisible());
    +  }
    +
    +  menu.dispose();
    +}
    +
    +
    +function testFilterWordBoundary() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Vacation Photos'));
    +  menu.addItem(new goog.ui.MenuItem('Work'));
    +  menu.addItem(new goog.ui.MenuItem('Receipts & Invoices'));
    +  menu.addItem(new goog.ui.MenuItem('Invitations'));
    +  menu.addItem(new goog.ui.MenuItem('3.Family'));
    +  menu.addItem(new goog.ui.MenuItem('No:Farm'));
    +  menu.addItem(new goog.ui.MenuItem('Syd/Family'));
    +
    +  menu.render(sandbox);
    +
    +  // Filter menu.
    +  menu.setFilter('Photos');
    +  assertTrue('Vacation Photos should be visible when the menu is filtered',
    +             menu.getChildAt(0).isVisible());
    +  assertFalse('Work should not be visible when the menu is filtered',
    +              menu.getChildAt(1).isVisible());
    +  assertFalse('Receipts & Invoices should not be visible when the menu is ' +
    +              'filtered',
    +              menu.getChildAt(2).isVisible());
    +  assertFalse('Invitations should not be visible when the menu is filtered',
    +              menu.getChildAt(3).isVisible());
    +
    +  menu.setFilter('I');
    +  assertFalse('Vacation Photos should not be visible when the menu is filtered',
    +      menu.getChildAt(0).isVisible());
    +  assertFalse('Work should not be visible when the menu is filtered',
    +              menu.getChildAt(1).isVisible());
    +  assertTrue('Receipts & Invoices should be visible when the menu is filtered',
    +             menu.getChildAt(2).isVisible());
    +  assertTrue('Invitations should be visible when the menu is filtered',
    +             menu.getChildAt(3).isVisible());
    +
    +  menu.setFilter('Fa');
    +  assertTrue('3.Family should be visible when the menu is filtered',
    +             menu.getChildAt(4).isVisible());
    +  assertTrue('No:Farm should be visible when the menu is filtered',
    +             menu.getChildAt(5).isVisible());
    +  assertTrue('Syd/Family should be visible when the menu is filtered',
    +             menu.getChildAt(6).isVisible());
    +
    +  menu.dispose();
    +}
    +
    +
    +function testScrollIntoView() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Family'));
    +  menu.addItem(new goog.ui.MenuItem('Friends'));
    +  menu.addItem(new goog.ui.MenuItem('Photos'));
    +  menu.addItem(new goog.ui.MenuItem('Work'));
    +  menu.render(sandbox);
    +
    +  menu.setHighlightedIndex(0);
    +  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
    +  menu.setHighlightedIndex(1);
    +  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
    +  menu.setHighlightedIndex(2);
    +  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
    +  menu.setHighlightedIndex(3);
    +  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
    +  menu.setHighlightedIndex(0);
    +  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
    +
    +  menu.dispose();
    +}
    +
    +function testEscapeKeyHandling() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Family'));
    +  menu.addItem(new goog.ui.MenuItem('Friends'));
    +  menu.render(sandbox);
    +
    +  var gotKeyCode = false;
    +  var wrapper = document.getElementById('wrapper');
    +  goog.events.listenOnce(wrapper, goog.events.EventType.KEYPRESS, function(e) {
    +    gotKeyCode = true;
    +  });
    +  goog.testing.events.fireKeySequence(menu.getFilterInputElement(),
    +      goog.events.KeyCodes.ESC);
    +  assertFalse('ESC key should not propagate out to parent', gotKeyCode);
    +}
    +
    +
    +function testAriaRoles() {
    +  menu = new goog.ui.FilteredMenu();
    +  menu.addItem(new goog.ui.MenuItem('Item 1'));
    +  menu.render(sandbox);
    +
    +  var input = menu.getFilterInputElement();
    +  assertEquals(goog.a11y.aria.AutoCompleteValues.LIST,
    +      goog.a11y.aria.getState(input, goog.a11y.aria.State.AUTOCOMPLETE));
    +  assertEquals(menu.getContentElement().id,
    +      goog.a11y.aria.getState(input, goog.a11y.aria.State.OWNS));
    +  assertEquals('true',
    +      goog.a11y.aria.getState(input, goog.a11y.aria.State.EXPANDED));
    +}
    +
    +
    +function testInputActiveDecendant() {
    +  menu = new goog.ui.FilteredMenu();
    +  var menuItem1 = new goog.ui.MenuItem('Item 1');
    +  var menuItem2 = new goog.ui.MenuItem('Item 2');
    +  menu.addItem(menuItem1);
    +  menu.addItem(menuItem2);
    +  menu.render(sandbox);
    +
    +  assertNull(goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
    +  menu.setHighlightedIndex(0);
    +  assertEquals(menuItem1.getElementStrict(),
    +      goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
    +  menu.setHighlightedIndex(1);
    +  assertEquals(menuItem2.getElementStrict(),
    +      goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
    +}
    +
    +
    +function isHighlightedVisible(menu) {
    +  var contRect = goog.style.getBounds(menu.getContentElement());
    +  // Expands the containing rectangle by 1px on top and bottom. The test
    +  // sometime fails with 1px out of bound on FF6/Linux. This is not
    +  // consistently reproducible.
    +  contRect = new goog.math.Rect(
    +      contRect.left, contRect.top - 1, contRect.width, contRect.height + 2);
    +  var itemRect = goog.style.getBounds(menu.getHighlighted().getElement());
    +  return contRect.contains(itemRect);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitem.js b/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitem.js
    new file mode 100644
    index 0000000000..2bee3f8474
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitem.js
    @@ -0,0 +1,98 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Menu item observing the filter text in a
    + * {@link goog.ui.FilteredMenu}. The observer method is called when the filter
    + * text changes and allows the menu item to update its content and state based
    + * on the filter.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.FilterObservingMenuItem');
    +
    +goog.require('goog.ui.FilterObservingMenuItemRenderer');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a filter observing menu item.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {*=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + */
    +goog.ui.FilterObservingMenuItem = function(content, opt_model, opt_domHelper,
    +                                           opt_renderer) {
    +  goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper,
    +      opt_renderer || new goog.ui.FilterObservingMenuItemRenderer());
    +};
    +goog.inherits(goog.ui.FilterObservingMenuItem, goog.ui.MenuItem);
    +goog.tagUnsealableClass(goog.ui.FilterObservingMenuItem);
    +
    +
    +/**
    + * Function called when the filter text changes.
    + * @type {Function} function(goog.ui.FilterObservingMenuItem, string)
    + * @private
    + */
    +goog.ui.FilterObservingMenuItem.prototype.observer_ = null;
    +
    +
    +/** @override */
    +goog.ui.FilterObservingMenuItem.prototype.enterDocument = function() {
    +  goog.ui.FilterObservingMenuItem.superClass_.enterDocument.call(this);
    +  this.callObserver();
    +};
    +
    +
    +/**
    + * Sets the observer functions.
    + * @param {Function} f function(goog.ui.FilterObservingMenuItem, string).
    + */
    +goog.ui.FilterObservingMenuItem.prototype.setObserver = function(f) {
    +  this.observer_ = f;
    +  this.callObserver();
    +};
    +
    +
    +/**
    + * Calls the observer function if one has been specified.
    + * @param {?string=} opt_str Filter string.
    + */
    +goog.ui.FilterObservingMenuItem.prototype.callObserver = function(opt_str) {
    +  if (this.observer_) {
    +    this.observer_(this, opt_str || '');
    +  }
    +};
    +
    +
    +// Register a decorator factory function for
    +// goog.ui.FilterObservingMenuItemRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS,
    +    function() {
    +      // FilterObservingMenuItem defaults to using
    +      // FilterObservingMenuItemRenderer.
    +      return new goog.ui.FilterObservingMenuItem(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js
    new file mode 100644
    index 0000000000..52894d295a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/filterobservingmenuitemrenderer.js
    @@ -0,0 +1,63 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Menu item observing the filter text in a
    + * {@link goog.ui.FilteredMenu}. The observer method is called when the filter
    + * text changes and allows the menu item to update its content and state based
    + * on the filter.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.FilterObservingMenuItemRenderer');
    +
    +goog.require('goog.ui.MenuItemRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.FilterObservingMenuItem}s. Each item has
    + * the following structure:
    + *    <div class="goog-filterobsmenuitem"><div>...(content)...</div></div>
    + *
    + * @constructor
    + * @extends {goog.ui.MenuItemRenderer}
    + * @final
    + */
    +goog.ui.FilterObservingMenuItemRenderer = function() {
    +  goog.ui.MenuItemRenderer.call(this);
    +};
    +goog.inherits(goog.ui.FilterObservingMenuItemRenderer,
    +              goog.ui.MenuItemRenderer);
    +goog.addSingletonGetter(goog.ui.FilterObservingMenuItemRenderer);
    +
    +
    +/**
    + * CSS class name the renderer applies to menu item elements.
    + * @type {string}
    + */
    +goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS =
    +    goog.getCssName('goog-filterobsmenuitem');
    +
    +
    +/**
    + * Returns the CSS class to be applied to menu items rendered using this
    + * renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.FilterObservingMenuItemRenderer.prototype.getCssClass = function() {
    +  return goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/flatbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/flatbuttonrenderer.js
    new file mode 100644
    index 0000000000..bb5b742fc1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/flatbuttonrenderer.js
    @@ -0,0 +1,147 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Similiar functionality of {@link goog.ui.ButtonRenderer},
    + * but uses a <div> element instead of a <button> or <input> element.
    + *
    + */
    +
    +goog.provide('goog.ui.FlatButtonRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Flat renderer for {@link goog.ui.Button}s.  Flat buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + * @constructor
    + * @extends {goog.ui.ButtonRenderer}
    + */
    +goog.ui.FlatButtonRenderer = function() {
    +  goog.ui.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.FlatButtonRenderer, goog.ui.ButtonRenderer);
    +goog.addSingletonGetter(goog.ui.FlatButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.FlatButtonRenderer.CSS_CLASS = goog.getCssName('goog-flat-button');
    +
    +
    +/**
    + * Returns the control's contents wrapped in a div element, with
    + * the renderer's own CSS class and additional state-specific classes applied
    + * to it, and the button's disabled attribute set or cleared as needed.
    + * Overrides {@link goog.ui.ButtonRenderer#createDom}.
    + * @param {goog.ui.Control} button Button to render.
    + * @return {!Element} Root element for the button.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.createDom = function(button) {
    +  var classNames = this.getClassNames(button);
    +  var attributes = {
    +    'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
    +  };
    +  var element = button.getDomHelper().createDom(
    +      'div', attributes, button.getContent());
    +  this.setTooltip(element, button.getTooltip());
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the ARIA role to be applied to flat buttons.
    + * @return {goog.a11y.aria.Role|undefined} ARIA role.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.BUTTON;
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element.  Overrides
    + * {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
    + * element is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == 'DIV';
    +};
    +
    +
    +/**
    + * Takes an existing element and decorates it with the flat button control.
    + * Initializes the control's ID, content, tooltip, value, and state based
    + * on the ID of the element, its child nodes, and its CSS classes, respectively.
    + * Returns the element.  Overrides {@link goog.ui.ButtonRenderer#decorate}.
    + * @param {goog.ui.Control} button Button instance to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.decorate = function(button, element) {
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.INLINE_BLOCK_CLASSNAME);
    +  return goog.ui.FlatButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Flat buttons can't use the value attribute since they are div elements.
    + * Overrides {@link goog.ui.ButtonRenderer#getValue} to prevent trying to
    + * access the element's value.
    + * @param {Element} element The button control's root element.
    + * @return {string} Value not valid for flat buttons.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.getValue = function(element) {
    +  // Flat buttons don't store their value in the DOM.
    +  return '';
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.FlatButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.FlatButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for Flat Buttons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.FlatButtonRenderer.CSS_CLASS,
    +    function() {
    +      // Uses goog.ui.Button, but with FlatButtonRenderer.
    +      return new goog.ui.Button(null, goog.ui.FlatButtonRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/flatmenubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/flatmenubuttonrenderer.js
    new file mode 100644
    index 0000000000..a3455b8563
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/flatmenubuttonrenderer.js
    @@ -0,0 +1,208 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Similiar functionality of {@link goog.ui.MenuButtonRenderer},
    + * but inherits from {@link goog.ui.FlatButtonRenderer} instead of
    + * {@link goog.ui.CustomButtonRenderer}. This creates a simpler menu button
    + * that will look more like a traditional <select> menu.
    + *
    + */
    +
    +goog.provide('goog.ui.FlatMenuButtonRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.ui.FlatButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Flat Menu Button renderer. Creates a simpler version of
    + * {@link goog.ui.MenuButton} that doesn't look like a button and
    + * doesn't have rounded corners. Uses just a <div> and looks more like
    + * a traditional <select> element.
    + * @constructor
    + * @extends {goog.ui.FlatButtonRenderer}
    + */
    +goog.ui.FlatMenuButtonRenderer = function() {
    +  goog.ui.FlatButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.FlatMenuButtonRenderer, goog.ui.FlatButtonRenderer);
    +goog.addSingletonGetter(goog.ui.FlatMenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.FlatMenuButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-flat-menu-button');
    +
    +
    +/**
    + * Returns the button's contents wrapped in the following DOM structure:
    + *    <div class="goog-inline-block goog-flat-menu-button">
    + *        <div class="goog-inline-block goog-flat-menu-button-caption">
    + *          Contents...
    + *        </div>
    + *        <div class="goog-inline-block goog-flat-menu-button-dropdown">
    + *          &nbsp;
    + *        </div>
    + *    </div>
    + * Overrides {@link goog.ui.FlatButtonRenderer#createDom}.
    + * @param {goog.ui.Control} control Button to render.
    + * @return {!Element} Root element for the button.
    + * @override
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.createDom = function(control) {
    +  var button = /** @type {goog.ui.Button} */ (control);
    +  var classNames = this.getClassNames(button);
    +  var attributes = {
    +    'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
    +  };
    +  var element = button.getDomHelper().createDom('div', attributes,
    +      [this.createCaption(button.getContent(), button.getDomHelper()),
    +       this.createDropdown(button.getDomHelper())]);
    +  this.setTooltip(
    +      element, /** @type {!string}*/ (button.getTooltip()));
    +  return element;
    +};
    +
    +
    +/**
    + * Takes the button's root element and returns the parent element of the
    + * button's contents.
    + * @param {Element} element Root element of the button whose content
    + * element is to be returned.
    + * @return {Element} The button's content element (if any).
    + * @override
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.getContentElement = function(element) {
    +  return element && /** @type {Element} */ (element.firstChild);
    +};
    +
    +
    +/**
    + * Takes an element, decorates it with the menu button control, and returns
    + * the element.  Overrides {@link goog.ui.CustomButtonRenderer#decorate} by
    + * looking for a child element that can be decorated by a menu, and if it
    + * finds one, decorates it and attaches it to the menu button.
    + * @param {goog.ui.Control} button Menu button to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.decorate = function(button, element) {
    +  // TODO(user): MenuButtonRenderer uses the exact same code.
    +  // Refactor this block to its own module where both can use it.
    +  var menuElem = goog.dom.getElementsByTagNameAndClass(
    +      '*', goog.ui.MenuRenderer.CSS_CLASS, element)[0];
    +  if (menuElem) {
    +    // Move the menu element directly under the body, but hide it first; see
    +    // bug 1089244.
    +    goog.style.setElementShown(menuElem, false);
    +    button.getDomHelper().getDocument().body.appendChild(menuElem);
    +
    +    // Decorate the menu and attach it to the button.
    +    var menu = new goog.ui.Menu();
    +    menu.decorate(menuElem);
    +    button.setMenu(menu);
    +  }
    +
    +  // Add the caption if it's not already there.
    +  var captionElem = goog.dom.getElementsByTagNameAndClass(
    +      '*', goog.getCssName(this.getCssClass(), 'caption'), element)[0];
    +  if (!captionElem) {
    +    element.appendChild(
    +        this.createCaption(element.childNodes, button.getDomHelper()));
    +  }
    +
    +  // Add the dropdown icon if it's not already there.
    +  var dropdownElem = goog.dom.getElementsByTagNameAndClass(
    +      '*', goog.getCssName(this.getCssClass(), 'dropdown'), element)[0];
    +  if (!dropdownElem) {
    +    element.appendChild(this.createDropdown(button.getDomHelper()));
    +  }
    +
    +  // Let the superclass do the rest.
    +  return goog.ui.FlatMenuButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns it wrapped in
    + * an appropriately-styled DIV.  Creates the following DOM structure:
    + *    <div class="goog-inline-block goog-flat-menu-button-caption">
    + *      Contents...
    + *    </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Caption element.
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.createCaption = function(content,
    +                                                                  dom) {
    +  return dom.createDom('div',
    +      goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +      goog.getCssName(this.getCssClass(), 'caption'), content);
    +};
    +
    +
    +/**
    + * Returns an appropriately-styled DIV containing a dropdown arrow element.
    + * Creates the following DOM structure:
    + *    <div class="goog-inline-block goog-flat-menu-button-dropdown">
    + *      &nbsp;
    + *    </div>
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Dropdown element.
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.createDropdown = function(dom) {
    +  // 00A0 is &nbsp;
    +  return dom.createDom('div', {
    +    'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +        goog.getCssName(this.getCssClass(), 'dropdown'),
    +    'aria-hidden': true
    +  }, '\u00A0');
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.FlatMenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.FlatMenuButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for Flat Menu Buttons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.FlatMenuButtonRenderer.CSS_CLASS,
    +    function() {
    +      // Uses goog.ui.MenuButton, but with FlatMenuButtonRenderer.
    +      return new goog.ui.MenuButton(null, null,
    +          goog.ui.FlatMenuButtonRenderer.getInstance());
    +    });
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/formpost.js b/mapbox-gl/closure-library/closure/goog/ui/formpost.js
    new file mode 100644
    index 0000000000..1c7fa57280
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/formpost.js
    @@ -0,0 +1,108 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utility for making the browser submit a hidden form, which can
    + * be used to effect a POST from JavaScript.
    + *
    + */
    +
    +goog.provide('goog.ui.FormPost');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.safe');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Creates a formpost object.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @param {goog.dom.DomHelper=} opt_dom The DOM helper.
    + * @final
    + */
    +goog.ui.FormPost = function(opt_dom) {
    +  goog.ui.Component.call(this, opt_dom);
    +};
    +goog.inherits(goog.ui.FormPost, goog.ui.Component);
    +
    +
    +/** @override */
    +goog.ui.FormPost.prototype.createDom = function() {
    +  this.setElementInternal(this.getDomHelper().createDom(goog.dom.TagName.FORM,
    +      {'method': 'POST', 'style': 'display:none'}));
    +};
    +
    +
    +/**
    + * Constructs a POST request and directs the browser as if a form were
    + * submitted.
    + * @param {Object} parameters Object with parameter values. Values can be
    + *     strings, numbers, or arrays of strings or numbers.
    + * @param {string=} opt_url The destination URL. If not specified, uses the
    + *     current URL for window for the DOM specified in the constructor.
    + * @param {string=} opt_target An optional name of a window in which to open the
    + *     URL. If not specified, uses the window for the DOM specified in the
    + *     constructor.
    + */
    +goog.ui.FormPost.prototype.post = function(parameters, opt_url, opt_target) {
    +  var form = this.getElement();
    +  if (!form) {
    +    this.render();
    +    form = this.getElement();
    +  }
    +  form.action = opt_url || '';
    +  form.target = opt_target || '';
    +  this.setParameters_(form, parameters);
    +  form.submit();
    +};
    +
    +
    +/**
    + * Creates hidden inputs in a form to match parameters.
    + * @param {!Element} form The form element.
    + * @param {Object} parameters Object with parameter values. Values can be
    + *     strings, numbers, or arrays of strings or numbers.
    + * @private
    + */
    +goog.ui.FormPost.prototype.setParameters_ = function(form, parameters) {
    +  var name, value, html = [];
    +  for (name in parameters) {
    +    value = parameters[name];
    +    if (goog.isArrayLike(value)) {
    +      goog.array.forEach(value, goog.bind(function(innerValue) {
    +        html.push(this.createInput_(name, String(innerValue)));
    +      }, this));
    +    } else {
    +      html.push(this.createInput_(name, String(value)));
    +    }
    +  }
    +  goog.dom.safe.setInnerHtml(form, goog.html.SafeHtml.concat(html));
    +};
    +
    +
    +/**
    + * Creates a hidden <input> tag.
    + * @param {string} name The name of the input.
    + * @param {string} value The value of the input.
    + * @return {!goog.html.SafeHtml}
    + * @private
    + */
    +goog.ui.FormPost.prototype.createInput_ = function(name, value) {
    +  return goog.html.SafeHtml.create('input',
    +      {'type': 'hidden', 'name': name, 'value': value});
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/formpost_test.html b/mapbox-gl/closure-library/closure/goog/ui/formpost_test.html
    new file mode 100644
    index 0000000000..2a831ce555
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/formpost_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.FormPost
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.FormPostTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/formpost_test.js b/mapbox-gl/closure-library/closure/goog/ui/formpost_test.js
    new file mode 100644
    index 0000000000..a80246fc77
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/formpost_test.js
    @@ -0,0 +1,109 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.FormPostTest');
    +goog.setTestOnly('goog.ui.FormPostTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.object');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.FormPost');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +var TARGET = 'target';
    +var ACTION_URL = 'http://url/';
    +var formPost;
    +var parameters;
    +var submits;
    +var originalCreateDom = goog.ui.FormPost.prototype.createDom;
    +
    +function isChrome7or8() {
    +  // Temporarily disabled in Chrome 7 & 8. See b/3176768
    +  if (goog.userAgent.product.CHROME &&
    +      goog.userAgent.product.isVersion('7.0') &&
    +      !goog.userAgent.product.isVersion('8.0')) {
    +    return false;
    +  }
    +
    +  return true;
    +}
    +
    +function setUp() {
    +  formPost = new goog.ui.FormPost();
    +  submits = 0;
    +
    +  // Replace the form's submit method with a fake.
    +  goog.ui.FormPost.prototype.createDom = function() {
    +    originalCreateDom.apply(this, arguments);
    +
    +    this.getElement().submit = function() { submits++ };
    +  };
    +  parameters = {'foo': 'bar', 'baz': 1, 'array': [0, 'yes']};
    +}
    +
    +function tearDown() {
    +  formPost.dispose();
    +  goog.ui.FormPost.prototype.createDom = originalCreateDom;
    +}
    +
    +function testPost() {
    +  formPost.post(parameters, ACTION_URL, TARGET);
    +  expectUrlAndParameters_(ACTION_URL, TARGET, parameters);
    +}
    +
    +function testPostWithDefaults() {
    +  // Temporarily disabled in Chrome 7. See See b/3176768
    +  if (isChrome7or8) {
    +    return;
    +  }
    +  formPost = new goog.ui.FormPost();
    +  formPost.post(parameters);
    +  expectUrlAndParameters_('', '', parameters);
    +}
    +
    +function expectUrlAndParameters_(url, target, parameters) {
    +  var form = formPost.getElement();
    +  assertEquals('element must be a form',
    +      goog.dom.TagName.FORM, form.tagName);
    +  assertEquals('form must be hidden', 'none', form.style.display);
    +  assertEquals('form method must be POST',
    +      'POST', form.method.toUpperCase());
    +  assertEquals('submits', 1, submits);
    +  assertEquals('action attribute', url, form.action);
    +  assertEquals('target attribute', target, form.target);
    +  var inputs = goog.dom.getElementsByTagNameAndClass(
    +      goog.dom.TagName.INPUT, null, form);
    +  var formValues = {};
    +  for (var i = 0, input = inputs[i]; input = inputs[i]; i++) {
    +    if (goog.isArray(formValues[input.name])) {
    +      formValues[input.name].push(input.value);
    +    } else if (input.name in formValues) {
    +      formValues[input.name] = [formValues[input.name], input.value];
    +    } else {
    +      formValues[input.name] = input.value;
    +    }
    +  }
    +  var expected = goog.object.map(parameters, valueToString);
    +  assertObjectEquals('form values must match', expected, formValues);
    +}
    +
    +function valueToString(value) {
    +  if (goog.isArray(value)) {
    +    return goog.array.map(value, valueToString);
    +  }
    +  return String(value);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/gauge.js b/mapbox-gl/closure-library/closure/goog/ui/gauge.js
    new file mode 100644
    index 0000000000..80ceecfbcb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/gauge.js
    @@ -0,0 +1,1012 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Gauge UI component, using browser vector graphics.
    + * @see ../demos/gauge.html
    + */
    +
    +
    +goog.provide('goog.ui.Gauge');
    +goog.provide('goog.ui.GaugeColoredRange');
    +
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.easing');
    +goog.require('goog.graphics');
    +goog.require('goog.graphics.Font');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.math');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.GaugeTheme');
    +
    +
    +
    +/**
    + * Information on how to decorate a range in the gauge.
    + * This is an internal-only class.
    + * @param {number} fromValue The range start (minimal) value.
    + * @param {number} toValue The range end (maximal) value.
    + * @param {string} backgroundColor Color to fill the range background with.
    + * @constructor
    + * @final
    + */
    +goog.ui.GaugeColoredRange = function(fromValue, toValue, backgroundColor) {
    +
    +  /**
    +   * The range start (minimal) value.
    +   * @type {number}
    +   */
    +  this.fromValue = fromValue;
    +
    +
    +  /**
    +   * The range end (maximal) value.
    +   * @type {number}
    +   */
    +  this.toValue = toValue;
    +
    +
    +  /**
    +   * Color to fill the range background with.
    +   * @type {string}
    +   */
    +  this.backgroundColor = backgroundColor;
    +};
    +
    +
    +
    +/**
    + * A UI component that displays a gauge.
    + * A gauge displayes a current value within a round axis that represents a
    + * given range.
    + * The gauge is built from an external border, and internal border inside it,
    + * ticks and labels inside the internal border, and a needle that points to
    + * the current value.
    + * @param {number} width The width in pixels.
    + * @param {number} height The height in pixels.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @constructor
    + * @extends {goog.ui.Component}
    + * @final
    + */
    +goog.ui.Gauge = function(width, height, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The width in pixels of this component.
    +   * @type {number}
    +   * @private
    +   */
    +  this.width_ = width;
    +
    +
    +  /**
    +   * The height in pixels of this component.
    +   * @type {number}
    +   * @private
    +   */
    +  this.height_ = height;
    +
    +
    +  /**
    +   * The underlying graphics.
    +   * @type {goog.graphics.AbstractGraphics}
    +   * @private
    +   */
    +  this.graphics_ = goog.graphics.createGraphics(width, height,
    +      null, null, opt_domHelper);
    +
    +
    +  /**
    +   * Colors to paint the background of certain ranges (optional).
    +   * @type {Array<goog.ui.GaugeColoredRange>}
    +   * @private
    +   */
    +  this.rangeColors_ = [];
    +};
    +goog.inherits(goog.ui.Gauge, goog.ui.Component);
    +
    +
    +/**
    + * Constant for a background color for a gauge area.
    + */
    +goog.ui.Gauge.RED = '#ffc0c0';
    +
    +
    +/**
    + * Constant for a background color for a gauge area.
    + */
    +goog.ui.Gauge.GREEN = '#c0ffc0';
    +
    +
    +/**
    + * Constant for a background color for a gauge area.
    + */
    +goog.ui.Gauge.YELLOW = '#ffffa0';
    +
    +
    +/**
    + * The radius of the entire gauge from the canvas size.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE = 0.45;
    +
    +
    +/**
    + * The ratio of internal gauge radius from entire radius.
    + * The remaining area is the border around the gauge.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_MAIN_AREA = 0.9;
    +
    +
    +/**
    + * The ratio of the colored background area for value ranges.
    + * The colored area width is computed as
    + * InternalRadius * (1 - FACTOR_COLOR_RADIUS)
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_COLOR_RADIUS = 0.75;
    +
    +
    +/**
    + * The ratio of the major ticks length start position, from the radius.
    + * The major ticks length width is computed as
    + * InternalRadius * (1 - FACTOR_MAJOR_TICKS)
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_MAJOR_TICKS = 0.8;
    +
    +
    +/**
    + * The ratio of the minor ticks length start position, from the radius.
    + * The minor ticks length width is computed as
    + * InternalRadius * (1 - FACTOR_MINOR_TICKS)
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_MINOR_TICKS = 0.9;
    +
    +
    +/**
    + * The length of the needle front (value facing) from the internal radius.
    + * The needle front is the part of the needle that points to the value.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_NEEDLE_FRONT = 0.95;
    +
    +
    +/**
    + * The length of the needle back relative to the internal radius.
    + * The needle back is the part of the needle that points away from the value.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_NEEDLE_BACK = 0.3;
    +
    +
    +/**
    + * The width of the needle front at the hinge.
    + * This is the width of the curve control point, the actual width is
    + * computed by the curve itself.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_NEEDLE_WIDTH = 0.07;
    +
    +
    +/**
    + * The width (radius) of the needle hinge from the gauge radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_NEEDLE_HINGE = 0.15;
    +
    +
    +/**
    + * The title font size (height) for titles relative to the internal radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE = 0.16;
    +
    +
    +/**
    + * The offset of the title from the center, relative to the internal radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_TITLE_OFFSET = 0.35;
    +
    +
    +/**
    + * The formatted value font size (height) relative to the internal radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE = 0.18;
    +
    +
    +/**
    + * The title font size (height) for tick labels relative to the internal radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE = 0.14;
    +
    +
    +/**
    + * The offset of the formatted value down from the center, relative to the
    + * internal radius.
    + * @type {number}
    + */
    +goog.ui.Gauge.FACTOR_VALUE_OFFSET = 0.75;
    +
    +
    +/**
    + * The font name for title text.
    + * @type {string}
    + */
    +goog.ui.Gauge.TITLE_FONT_NAME = 'arial';
    +
    +
    +/**
    + * The maximal size of a step the needle can move (percent from size of range).
    + * If the needle needs to move more, it will be moved in animated steps, to
    + * show a smooth transition between values.
    + * @type {number}
    + */
    +goog.ui.Gauge.NEEDLE_MOVE_MAX_STEP = 0.02;
    +
    +
    +/**
    + * Time in miliseconds for animating a move of the value pointer.
    + * @type {number}
    + */
    +goog.ui.Gauge.NEEDLE_MOVE_TIME = 400;
    +
    +
    +/**
    + * Tolerance factor for how much values can exceed the range (being too
    + * low or too high). The value is presented as a position (percentage).
    + * @type {number}
    + */
    +goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION = 0.02;
    +
    +
    +/**
    + * The minimal value that can be displayed.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.minValue_ = 0;
    +
    +
    +/**
    + * The maximal value that can be displayed.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.maxValue_ = 100;
    +
    +
    +/**
    + * The number of major tick sections.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.majorTicks_ = 5;
    +
    +
    +/**
    + * The number of minor tick sections in each major tick section.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.minorTicks_ = 2;
    +
    +
    +/**
    + * The current value that needs to be displayed in the gauge.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.value_ = 0;
    +
    +
    +/**
    + * The current value formatted into a String.
    + * @private
    + * @type {?string}
    + */
    +goog.ui.Gauge.prototype.formattedValue_ = null;
    +
    +
    +/**
    + * The current colors theme.
    + * @private
    + * @type {goog.ui.GaugeTheme?}
    + */
    +goog.ui.Gauge.prototype.theme_ = null;
    +
    +
    +/**
    + * Title to display above the gauge center.
    + * @private
    + * @type {?string}
    + */
    +goog.ui.Gauge.prototype.titleTop_ = null;
    +
    +
    +/**
    + * Title to display below the gauge center.
    + * @private
    + * @type {?string}
    + */
    +goog.ui.Gauge.prototype.titleBottom_ = null;
    +
    +
    +/**
    + * Font to use for drawing titles.
    + * If null (default), computed dynamically with a size relative to the
    + * gauge radius.
    + * @private
    + * @type {goog.graphics.Font?}
    + */
    +goog.ui.Gauge.prototype.titleFont_ = null;
    +
    +
    +/**
    + * Font to use for drawing the formatted value.
    + * If null (default), computed dynamically with a size relative to the
    + * gauge radius.
    + * @private
    + * @type {goog.graphics.Font?}
    + */
    +goog.ui.Gauge.prototype.valueFont_ = null;
    +
    +
    +/**
    + * Font to use for drawing tick labels.
    + * If null (default), computed dynamically with a size relative to the
    + * gauge radius.
    + * @private
    + * @type {goog.graphics.Font?}
    + */
    +goog.ui.Gauge.prototype.tickLabelFont_ = null;
    +
    +
    +/**
    + * The size in angles of the gauge axis area.
    + * @private
    + * @type {number}
    + */
    +goog.ui.Gauge.prototype.angleSpan_ = 270;
    +
    +
    +/**
    + * The radius for drawing the needle.
    + * Computed on full redraw, and used on every animation step of moving
    + * the needle.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Gauge.prototype.needleRadius_ = 0;
    +
    +
    +/**
    + * The group elemnt of the needle. Contains all elements that change when the
    + * gauge value changes.
    + * @type {goog.graphics.GroupElement?}
    + * @private
    + */
    +goog.ui.Gauge.prototype.needleGroup_ = null;
    +
    +
    +/**
    + * The current position (0-1) of the visible needle.
    + * Initially set to null to prevent animation on first opening of the gauge.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.Gauge.prototype.needleValuePosition_ = null;
    +
    +
    +/**
    + * Text labels to display by major tick marks.
    + * @type {Array<string>?}
    + * @private
    + */
    +goog.ui.Gauge.prototype.majorTickLabels_ = null;
    +
    +
    +/**
    + * Animation object while needle is being moved (animated).
    + * @type {goog.fx.Animation?}
    + * @private
    + */
    +goog.ui.Gauge.prototype.animation_ = null;
    +
    +
    +/**
    + * @return {number} The minimum value of the range.
    + */
    +goog.ui.Gauge.prototype.getMinimum = function() {
    +  return this.minValue_;
    +};
    +
    +
    +/**
    + * Sets the minimum value of the range
    + * @param {number} min The minimum value of the range.
    + */
    +goog.ui.Gauge.prototype.setMinimum = function(min) {
    +  this.minValue_ = min;
    +  var element = this.getElement();
    +  if (element) {
    +    goog.a11y.aria.setState(element, 'valuemin', min);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The maximum value of the range.
    + */
    +goog.ui.Gauge.prototype.getMaximum = function() {
    +  return this.maxValue_;
    +};
    +
    +
    +/**
    + * Sets the maximum number of the range
    + * @param {number} max The maximum value of the range.
    + */
    +goog.ui.Gauge.prototype.setMaximum = function(max) {
    +  this.maxValue_ = max;
    +
    +  var element = this.getElement();
    +  if (element) {
    +    goog.a11y.aria.setState(element, 'valuemax', max);
    +  }
    +};
    +
    +
    +/**
    + * Sets the current value range displayed by the gauge.
    + * @param {number} value The current value for the gauge. This value
    + *     determines the position of the needle of the gauge.
    + * @param {string=} opt_formattedValue The string value to show in the gauge.
    + *     If not specified, no string value will be displayed.
    + */
    +goog.ui.Gauge.prototype.setValue = function(value, opt_formattedValue) {
    +  this.value_ = value;
    +  this.formattedValue_ = opt_formattedValue || null;
    +
    +  this.stopAnimation_(); // Stop the active animation if exists
    +
    +  // Compute desired value position (normalize value to range 0-1)
    +  var valuePosition = this.valueToRangePosition_(value);
    +  if (this.needleValuePosition_ == null) {
    +    // No animation on initial display
    +    this.needleValuePosition_ = valuePosition;
    +    this.drawValue_();
    +  } else {
    +    // Animate move
    +    this.animation_ = new goog.fx.Animation([this.needleValuePosition_],
    +        [valuePosition],
    +        goog.ui.Gauge.NEEDLE_MOVE_TIME,
    +        goog.fx.easing.inAndOut);
    +
    +    var events = [goog.fx.Transition.EventType.BEGIN,
    +                  goog.fx.Animation.EventType.ANIMATE,
    +                  goog.fx.Transition.EventType.END];
    +    goog.events.listen(this.animation_, events, this.onAnimate_, false, this);
    +    goog.events.listen(this.animation_, goog.fx.Transition.EventType.END,
    +        this.onAnimateEnd_, false, this);
    +
    +    // Start animation
    +    this.animation_.play(false);
    +  }
    +
    +  var element = this.getElement();
    +  if (element) {
    +    goog.a11y.aria.setState(element, 'valuenow', this.value_);
    +  }
    +};
    +
    +
    +/**
    + * Sets the number of major tick sections and minor tick sections.
    + * @param {number} majorUnits The number of major tick sections.
    + * @param {number} minorUnits The number of minor tick sections for each major
    + *     tick section.
    + */
    +goog.ui.Gauge.prototype.setTicks = function(majorUnits, minorUnits) {
    +  this.majorTicks_ = Math.max(1, majorUnits);
    +  this.minorTicks_ = Math.max(1, minorUnits);
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Sets the labels of the major ticks.
    + * @param {Array<string>} tickLabels A text label for each major tick value.
    + */
    +goog.ui.Gauge.prototype.setMajorTickLabels = function(tickLabels) {
    +  this.majorTickLabels_ = tickLabels;
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Sets the top title of the gauge.
    + * The top title is displayed above the center.
    + * @param {string} text The top title text.
    + */
    +goog.ui.Gauge.prototype.setTitleTop = function(text) {
    +  this.titleTop_ = text;
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Sets the bottom title of the gauge.
    + * The top title is displayed below the center.
    + * @param {string} text The bottom title text.
    + */
    +goog.ui.Gauge.prototype.setTitleBottom = function(text) {
    +  this.titleBottom_ = text;
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Sets the font for displaying top and bottom titles.
    + * @param {goog.graphics.Font} font The font for titles.
    + */
    +goog.ui.Gauge.prototype.setTitleFont = function(font) {
    +  this.titleFont_ = font;
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Sets the font for displaying the formatted value.
    + * @param {goog.graphics.Font} font The font for displaying the value.
    + */
    +goog.ui.Gauge.prototype.setValueFont = function(font) {
    +  this.valueFont_ = font;
    +  this.drawValue_();
    +};
    +
    +
    +/**
    + * Sets the color theme for drawing the gauge.
    + * @param {goog.ui.GaugeTheme} theme The color theme to use.
    + */
    +goog.ui.Gauge.prototype.setTheme = function(theme) {
    +  this.theme_ = theme;
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Set the background color for a range of values on the gauge.
    + * @param {number} fromValue The lower (start) value of the colored range.
    + * @param {number} toValue The higher (end) value of the colored range.
    + * @param {string} color The color name to paint the range with. For example
    + *     'red', '#ffcc00' or constants like goog.ui.Gauge.RED.
    + */
    +goog.ui.Gauge.prototype.addBackgroundColor = function(fromValue, toValue,
    +    color) {
    +  this.rangeColors_.push(
    +      new goog.ui.GaugeColoredRange(fromValue, toValue, color));
    +  this.draw_();
    +};
    +
    +
    +/**
    + * Creates the DOM representation of the graphics area.
    + * @override
    + */
    +goog.ui.Gauge.prototype.createDom = function() {
    +  this.setElementInternal(this.getDomHelper().createDom(
    +      'div', goog.getCssName('goog-gauge'), this.graphics_.getElement()));
    +};
    +
    +
    +/**
    + * Clears the entire graphics area.
    + * @private
    + */
    +goog.ui.Gauge.prototype.clear_ = function() {
    +  this.graphics_.clear();
    +  this.needleGroup_ = null;
    +};
    +
    +
    +/**
    + * Redraw the entire gauge.
    + * @private
    + */
    +goog.ui.Gauge.prototype.draw_ = function() {
    +  if (!this.isInDocument()) {
    +    return;
    +  }
    +
    +  this.clear_();
    +
    +  var x, y;
    +  var size = Math.min(this.width_, this.height_);
    +  var r = Math.round(goog.ui.Gauge.FACTOR_RADIUS_FROM_SIZE * size);
    +  var cx = this.width_ / 2;
    +  var cy = this.height_ / 2;
    +
    +  var theme = this.theme_;
    +  if (!theme) {
    +    // Lazy allocation of default theme, common to all instances
    +    theme = goog.ui.Gauge.prototype.theme_ = new goog.ui.GaugeTheme();
    +  }
    +
    +  // Draw main circle frame around gauge
    +  var graphics = this.graphics_;
    +  var stroke = this.theme_.getExternalBorderStroke();
    +  var fill = theme.getExternalBorderFill(cx, cy, r);
    +  graphics.drawCircle(cx, cy, r, stroke, fill);
    +
    +  r -= stroke.getWidth();
    +  r = Math.round(r * goog.ui.Gauge.FACTOR_MAIN_AREA);
    +  stroke = theme.getInternalBorderStroke();
    +  fill = theme.getInternalBorderFill(cx, cy, r);
    +  graphics.drawCircle(cx, cy, r, stroke, fill);
    +  r -= stroke.getWidth() * 2;
    +
    +  // Draw Background with external and internal borders
    +  var rBackgroundInternal = r * goog.ui.Gauge.FACTOR_COLOR_RADIUS;
    +  for (var i = 0; i < this.rangeColors_.length; i++) {
    +    var rangeColor = this.rangeColors_[i];
    +    var fromValue = rangeColor.fromValue;
    +    var toValue = rangeColor.toValue;
    +    var path = new goog.graphics.Path();
    +    var fromAngle = this.valueToAngle_(fromValue);
    +    var toAngle = this.valueToAngle_(toValue);
    +    // Move to outer point at "from" angle
    +    path.moveTo(
    +        cx + goog.math.angleDx(fromAngle, r),
    +        cy + goog.math.angleDy(fromAngle, r));
    +    // Arc to outer point at "to" angle
    +    path.arcTo(r, r, fromAngle, toAngle - fromAngle);
    +    // Line to inner point at "to" angle
    +    path.lineTo(
    +        cx + goog.math.angleDx(toAngle, rBackgroundInternal),
    +        cy + goog.math.angleDy(toAngle, rBackgroundInternal));
    +    // Arc to inner point at "from" angle
    +    path.arcTo(
    +        rBackgroundInternal, rBackgroundInternal, toAngle, fromAngle - toAngle);
    +    path.close();
    +    fill = new goog.graphics.SolidFill(rangeColor.backgroundColor);
    +    graphics.drawPath(path, null, fill);
    +  }
    +
    +  // Draw titles
    +  if (this.titleTop_ || this.titleBottom_) {
    +    var font = this.titleFont_;
    +    if (!font) {
    +      // Lazy creation of font
    +      var fontSize =
    +          Math.round(r * goog.ui.Gauge.FACTOR_TITLE_FONT_SIZE);
    +      font = new goog.graphics.Font(
    +          fontSize, goog.ui.Gauge.TITLE_FONT_NAME);
    +      this.titleFont_ = font;
    +    }
    +    fill = new goog.graphics.SolidFill(theme.getTitleColor());
    +    if (this.titleTop_) {
    +      y = cy - Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET);
    +      graphics.drawTextOnLine(this.titleTop_, 0, y, this.width_, y,
    +          'center', font, null, fill);
    +    }
    +    if (this.titleBottom_) {
    +      y = cy + Math.round(r * goog.ui.Gauge.FACTOR_TITLE_OFFSET);
    +      graphics.drawTextOnLine(this.titleBottom_, 0, y, this.width_, y,
    +          'center', font, null, fill);
    +    }
    +  }
    +
    +  // Draw tick marks
    +  var majorTicks = this.majorTicks_;
    +  var minorTicks = this.minorTicks_;
    +  var rMajorTickInternal = r * goog.ui.Gauge.FACTOR_MAJOR_TICKS;
    +  var rMinorTickInternal = r * goog.ui.Gauge.FACTOR_MINOR_TICKS;
    +  var ticks = majorTicks * minorTicks;
    +  var valueRange = this.maxValue_ - this.minValue_;
    +  var tickValueSpan = valueRange / ticks;
    +  var majorTicksPath = new goog.graphics.Path();
    +  var minorTicksPath = new goog.graphics.Path();
    +
    +  var tickLabelFill = new goog.graphics.SolidFill(theme.getTickLabelColor());
    +  var tickLabelFont = this.tickLabelFont_;
    +  if (!tickLabelFont) {
    +    tickLabelFont = new goog.graphics.Font(
    +        Math.round(r * goog.ui.Gauge.FACTOR_TICK_LABEL_FONT_SIZE),
    +        goog.ui.Gauge.TITLE_FONT_NAME);
    +  }
    +  var tickLabelFontSize = tickLabelFont.size;
    +
    +  for (var i = 0; i <= ticks; i++) {
    +    var angle = this.valueToAngle_(i * tickValueSpan + this.minValue_);
    +    var isMajorTick = i % minorTicks == 0;
    +    var rInternal = isMajorTick ? rMajorTickInternal : rMinorTickInternal;
    +    var path = isMajorTick ? majorTicksPath : minorTicksPath;
    +    x = cx + goog.math.angleDx(angle, rInternal);
    +    y = cy + goog.math.angleDy(angle, rInternal);
    +    path.moveTo(x, y);
    +    x = cx + goog.math.angleDx(angle, r);
    +    y = cy + goog.math.angleDy(angle, r);
    +    path.lineTo(x, y);
    +
    +    // Draw the tick's label for major ticks
    +    if (isMajorTick && this.majorTickLabels_) {
    +      var tickIndex = Math.floor(i / minorTicks);
    +      var label = this.majorTickLabels_[tickIndex];
    +      if (label) {
    +        x = cx + goog.math.angleDx(angle, rInternal - tickLabelFontSize / 2);
    +        y = cy + goog.math.angleDy(angle, rInternal - tickLabelFontSize / 2);
    +        var x1, x2;
    +        var align = 'center';
    +        if (angle > 280 || angle < 90) {
    +          align = 'right';
    +          x1 = 0;
    +          x2 = x;
    +        } else if (angle >= 90 && angle < 260) {
    +          align = 'left';
    +          x1 = x;
    +          x2 = this.width_;
    +        } else {
    +          // Values around top (angle 260-280) are centered around point
    +          var dw = Math.min(x, this.width_ - x); // Nearest side border
    +          x1 = x - dw;
    +          x2 = x + dw;
    +          y += Math.round(tickLabelFontSize / 4); // Movea bit down
    +        }
    +        graphics.drawTextOnLine(label, x1, y, x2, y,
    +            align, tickLabelFont, null, tickLabelFill);
    +      }
    +    }
    +  }
    +  stroke = theme.getMinorTickStroke();
    +  graphics.drawPath(minorTicksPath, stroke, null);
    +  stroke = theme.getMajorTickStroke();
    +  graphics.drawPath(majorTicksPath, stroke, null);
    +
    +  // Draw the needle and the value label. Stop animation when doing
    +  // full redraw and jump to the final value position.
    +  this.stopAnimation_();
    +  this.needleRadius_ = r;
    +  this.drawValue_();
    +};
    +
    +
    +/**
    + * Handle animation events while the hand is moving.
    + * @param {goog.fx.AnimationEvent} e The event.
    + * @private
    + */
    +goog.ui.Gauge.prototype.onAnimate_ = function(e) {
    +  this.needleValuePosition_ = e.x;
    +  this.drawValue_();
    +};
    +
    +
    +/**
    + * Handle animation events when hand move is complete.
    + * @private
    + */
    +goog.ui.Gauge.prototype.onAnimateEnd_ = function() {
    +  this.stopAnimation_();
    +};
    +
    +
    +/**
    + * Stop the current animation, if it is active.
    + * @private
    + */
    +goog.ui.Gauge.prototype.stopAnimation_ = function() {
    +  if (this.animation_) {
    +    goog.events.removeAll(this.animation_);
    +    this.animation_.stop(false);
    +    this.animation_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Convert a value to the position in the range. The returned position
    + * is a value between 0 and 1, where 0 indicates the lowest range value,
    + * 1 is the highest range, and any value in between is proportional
    + * to mapping the range to (0-1).
    + * If the value is not within the range, the returned value may be a bit
    + * lower than 0, or a bit higher than 1. This is done so that values out
    + * of range will be displayed just a bit outside of the gauge axis.
    + * @param {number} value The value to convert.
    + * @private
    + * @return {number} The range position.
    + */
    +goog.ui.Gauge.prototype.valueToRangePosition_ = function(value) {
    +  var valueRange = this.maxValue_ - this.minValue_;
    +  var valuePct = (value - this.minValue_) / valueRange; // 0 to 1
    +
    +  // If value is out of range, trim it not to be too much out of range
    +  valuePct = Math.max(valuePct,
    +      -goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION);
    +  valuePct = Math.min(valuePct,
    +      1 + goog.ui.Gauge.MAX_EXCEED_POSITION_POSITION);
    +
    +  return valuePct;
    +};
    +
    +
    +/**
    + * Convert a value to an angle based on the value range and angle span
    + * @param {number} value The value.
    + * @return {number} The angle where this value is located on the round
    + *     axis, based on the range and angle span.
    + * @private
    + */
    +goog.ui.Gauge.prototype.valueToAngle_ = function(value) {
    +  var valuePct = this.valueToRangePosition_(value);
    +  return this.valuePositionToAngle_(valuePct);
    +};
    +
    +
    +/**
    + * Convert a value-position (percent in the range) to an angle based on
    + * the angle span. A value-position is a value that has been proportinally
    + * adjusted to a value betwwen 0-1, proportionaly to the range.
    + * @param {number} valuePct The value.
    + * @return {number} The angle where this value is located on the round
    + *     axis, based on the range and angle span.
    + * @private
    + */
    +goog.ui.Gauge.prototype.valuePositionToAngle_ = function(valuePct) {
    +  var startAngle = goog.math.standardAngle((360 - this.angleSpan_) / 2 + 90);
    +  return this.angleSpan_ * valuePct + startAngle;
    +};
    +
    +
    +/**
    + * Draw the elements that depend on the current value (the needle and
    + * the formatted value). This function is called whenever a value is changed
    + * or when the entire gauge is redrawn.
    + * @private
    + */
    +goog.ui.Gauge.prototype.drawValue_ = function() {
    +  if (!this.isInDocument()) {
    +    return;
    +  }
    +
    +  var r = this.needleRadius_;
    +  var graphics = this.graphics_;
    +  var theme = this.theme_;
    +  var cx = this.width_ / 2;
    +  var cy = this.height_ / 2;
    +  var angle = this.valuePositionToAngle_(
    +      /** @type {number} */(this.needleValuePosition_));
    +
    +  // Compute the needle path
    +  var frontRadius =
    +      Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_FRONT);
    +  var backRadius =
    +      Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_BACK);
    +  var frontDx = goog.math.angleDx(angle, frontRadius);
    +  var frontDy = goog.math.angleDy(angle, frontRadius);
    +  var backDx = goog.math.angleDx(angle, backRadius);
    +  var backDy = goog.math.angleDy(angle, backRadius);
    +  var angleRight = goog.math.standardAngle(angle + 90);
    +  var distanceControlPointBase = r * goog.ui.Gauge.FACTOR_NEEDLE_WIDTH;
    +  var controlPointMidDx = goog.math.angleDx(angleRight,
    +      distanceControlPointBase);
    +  var controlPointMidDy = goog.math.angleDy(angleRight,
    +      distanceControlPointBase);
    +
    +  var path = new goog.graphics.Path();
    +  path.moveTo(cx + frontDx, cy + frontDy);
    +  path.curveTo(cx + controlPointMidDx, cy + controlPointMidDy,
    +      cx - backDx + (controlPointMidDx / 2),
    +      cy - backDy + (controlPointMidDy / 2),
    +      cx - backDx, cy - backDy);
    +  path.curveTo(cx - backDx - (controlPointMidDx / 2),
    +      cy - backDy - (controlPointMidDy / 2),
    +      cx - controlPointMidDx, cy - controlPointMidDy,
    +      cx + frontDx, cy + frontDy);
    +
    +  // Draw the needle hinge
    +  var rh = Math.round(r * goog.ui.Gauge.FACTOR_NEEDLE_HINGE);
    +
    +  // Clean previous needle
    +  var needleGroup = this.needleGroup_;
    +  if (needleGroup) {
    +    needleGroup.clear();
    +  } else {
    +    needleGroup = this.needleGroup_ = graphics.createGroup();
    +  }
    +
    +  // Draw current formatted value if provided.
    +  if (this.formattedValue_) {
    +    var font = this.valueFont_;
    +    if (!font) {
    +      var fontSize =
    +          Math.round(r * goog.ui.Gauge.FACTOR_VALUE_FONT_SIZE);
    +      font = new goog.graphics.Font(fontSize,
    +          goog.ui.Gauge.TITLE_FONT_NAME);
    +      font.bold = true;
    +      this.valueFont_ = font;
    +    }
    +    var fill = new goog.graphics.SolidFill(theme.getValueColor());
    +    var y = cy + Math.round(r * goog.ui.Gauge.FACTOR_VALUE_OFFSET);
    +    graphics.drawTextOnLine(this.formattedValue_, 0, y, this.width_, y,
    +        'center', font, null, fill, needleGroup);
    +  }
    +
    +  // Draw the needle
    +  var stroke = theme.getNeedleStroke();
    +  var fill = theme.getNeedleFill(cx, cy, rh);
    +  graphics.drawPath(path, stroke, fill, needleGroup);
    +  stroke = theme.getHingeStroke();
    +  fill = theme.getHingeFill(cx, cy, rh);
    +  graphics.drawCircle(cx, cy, rh, stroke, fill, needleGroup);
    +};
    +
    +
    +/**
    + * Redraws the entire gauge.
    + * Should be called after theme colors have been changed.
    + */
    +goog.ui.Gauge.prototype.redraw = function() {
    +  this.draw_();
    +};
    +
    +
    +/** @override */
    +goog.ui.Gauge.prototype.enterDocument = function() {
    +  goog.ui.Gauge.superClass_.enterDocument.call(this);
    +
    +  // set roles and states
    +  var el = this.getElement();
    +  goog.asserts.assert(el, 'The DOM element for the gauge cannot be null.');
    +  goog.a11y.aria.setRole(el, 'progressbar');
    +  goog.a11y.aria.setState(el, 'live', 'polite');
    +  goog.a11y.aria.setState(el, 'valuemin', this.minValue_);
    +  goog.a11y.aria.setState(el, 'valuemax', this.maxValue_);
    +  goog.a11y.aria.setState(el, 'valuenow', this.value_);
    +  this.draw_();
    +};
    +
    +
    +/** @override */
    +goog.ui.Gauge.prototype.exitDocument = function() {
    +  goog.ui.Gauge.superClass_.exitDocument.call(this);
    +  this.stopAnimation_();
    +};
    +
    +
    +/** @override */
    +goog.ui.Gauge.prototype.disposeInternal = function() {
    +  this.stopAnimation_();
    +  this.graphics_.dispose();
    +  delete this.graphics_;
    +  delete this.needleGroup_;
    +  delete this.theme_;
    +  delete this.rangeColors_;
    +  goog.ui.Gauge.superClass_.disposeInternal.call(this);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/gaugetheme.js b/mapbox-gl/closure-library/closure/goog/ui/gaugetheme.js
    new file mode 100644
    index 0000000000..52008d424e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/gaugetheme.js
    @@ -0,0 +1,170 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview The color theme used by a gauge (goog.ui.Guage).
    + */
    +
    +
    +goog.provide('goog.ui.GaugeTheme');
    +
    +
    +goog.require('goog.graphics.LinearGradient');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.graphics.Stroke');
    +
    +
    +
    +/**
    + * A class for the default color theme for a Gauge.
    + * Users can extend this class to provide a custom color theme, and apply the
    + * custom color theme by calling  {@link goog.ui.Gauge#setTheme}.
    + * @constructor
    + * @final
    + */
    +goog.ui.GaugeTheme = function() {
    +};
    +
    +
    +/**
    + * Returns the stroke for the external border of the gauge.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getExternalBorderStroke = function() {
    +  return new goog.graphics.Stroke(1, '#333333');
    +};
    +
    +
    +/**
    + * Returns the fill for the external border of the gauge.
    + * @param {number} cx X coordinate of the center of the gauge.
    + * @param {number} cy Y coordinate of the center of the gauge.
    + * @param {number} r Radius of the gauge.
    + * @return {!goog.graphics.Fill} The fill to use.
    + */
    +goog.ui.GaugeTheme.prototype.getExternalBorderFill = function(cx, cy, r) {
    +  return new goog.graphics.LinearGradient(cx + r, cy - r, cx - r, cy + r,
    +      '#f7f7f7', '#cccccc');
    +};
    +
    +
    +/**
    + * Returns the stroke for the internal border of the gauge.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getInternalBorderStroke = function() {
    +  return new goog.graphics.Stroke(2, '#e0e0e0');
    +};
    +
    +
    +/**
    + * Returns the fill for the internal border of the gauge.
    + * @param {number} cx X coordinate of the center of the gauge.
    + * @param {number} cy Y coordinate of the center of the gauge.
    + * @param {number} r Radius of the gauge.
    + * @return {!goog.graphics.Fill} The fill to use.
    + */
    +goog.ui.GaugeTheme.prototype.getInternalBorderFill = function(cx, cy, r) {
    +  return new goog.graphics.SolidFill('#f7f7f7');
    +};
    +
    +
    +/**
    + * Returns the stroke for the major ticks of the gauge.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getMajorTickStroke = function() {
    +  return new goog.graphics.Stroke(2, '#333333');
    +};
    +
    +
    +/**
    + * Returns the stroke for the minor ticks of the gauge.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getMinorTickStroke = function() {
    +  return new goog.graphics.Stroke(1, '#666666');
    +};
    +
    +
    +/**
    + * Returns the stroke for the hinge at the center of the gauge.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getHingeStroke = function() {
    +  return new goog.graphics.Stroke(1, '#666666');
    +};
    +
    +
    +/**
    + * Returns the fill for the hinge at the center of the gauge.
    + * @param {number} cx  X coordinate of the center of the gauge.
    + * @param {number} cy  Y coordinate of the center of the gauge.
    + * @param {number} r  Radius of the hinge.
    + * @return {!goog.graphics.Fill} The fill to use.
    + */
    +goog.ui.GaugeTheme.prototype.getHingeFill = function(cx, cy, r) {
    +  return new goog.graphics.LinearGradient(cx + r, cy - r, cx - r, cy + r,
    +      '#4684ee', '#3776d6');
    +};
    +
    +
    +/**
    + * Returns the stroke for the gauge needle.
    + * @return {!goog.graphics.Stroke} The stroke to use.
    + */
    +goog.ui.GaugeTheme.prototype.getNeedleStroke = function() {
    +  return new goog.graphics.Stroke(1, '#c63310');
    +};
    +
    +
    +/**
    + * Returns the fill for the hinge at the center of the gauge.
    + * @param {number} cx X coordinate of the center of the gauge.
    + * @param {number} cy Y coordinate of the center of the gauge.
    + * @param {number} r Radius of the gauge.
    + * @return {!goog.graphics.Fill} The fill to use.
    + */
    +goog.ui.GaugeTheme.prototype.getNeedleFill = function(cx, cy, r) {
    +  // Make needle a bit transparent so that text underneeth is still visible.
    +  return new goog.graphics.SolidFill('#dc3912', 0.7);
    +};
    +
    +
    +/**
    + * Returns the color for the gauge title.
    + * @return {string} The color to use.
    + */
    +goog.ui.GaugeTheme.prototype.getTitleColor = function() {
    +  return '#333333';
    +};
    +
    +
    +/**
    + * Returns the color for the gauge value.
    + * @return {string} The color to use.
    + */
    +goog.ui.GaugeTheme.prototype.getValueColor = function() {
    +  return 'black';
    +};
    +
    +
    +/**
    + * Returns the color for the labels (formatted values) of tick marks.
    + * @return {string} The color to use.
    + */
    +goog.ui.GaugeTheme.prototype.getTickLabelColor = function() {
    +  return '#333333';
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hovercard.js b/mapbox-gl/closure-library/closure/goog/ui/hovercard.js
    new file mode 100644
    index 0000000000..d6ed3c77e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hovercard.js
    @@ -0,0 +1,458 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Show hovercards with a delay after the mouse moves over an
    + * element of a specified type and with a specific attribute.
    + *
    + * @see ../demos/hovercard.html
    + */
    +
    +goog.provide('goog.ui.HoverCard');
    +goog.provide('goog.ui.HoverCard.EventType');
    +goog.provide('goog.ui.HoverCard.TriggerEvent');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.AdvancedTooltip');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.ui.Tooltip');
    +
    +
    +
    +/**
    + * Create a hover card object.  Hover cards extend tooltips in that they don't
    + * have to be manually attached to each element that can cause them to display.
    + * Instead, you can create a function that gets called when the mouse goes over
    + * any element on your page, and returns whether or not the hovercard should be
    + * shown for that element.
    + *
    + * Alternatively, you can define a map of tag names to the attribute name each
    + * tag should have for that tag to trigger the hover card.  See example below.
    + *
    + * Hovercards can also be triggered manually by calling
    + * {@code triggerForElement}, shown without a delay by calling
    + * {@code showForElement}, or triggered over other elements by calling
    + * {@code attach}.  For the latter two cases, the application is responsible
    + * for calling {@code detach} when finished.
    + *
    + * HoverCard objects fire a TRIGGER event when the mouse moves over an element
    + * that can trigger a hovercard, and BEFORE_SHOW when the hovercard is
    + * about to be shown.  Clients can respond to these events and can prevent the
    + * hovercard from being triggered or shown.
    + *
    + * @param {Function|Object} isAnchor Function that returns true if a given
    + *     element should trigger the hovercard.  Alternatively, it can be a map of
    + *     tag names to the attribute that the tag should have in order to trigger
    + *     the hovercard, e.g., {A: 'href'} for all links.  Tag names must be all
    + *     upper case; attribute names are case insensitive.
    + * @param {boolean=} opt_checkDescendants Use false for a performance gain if
    + *     you are sure that none of your triggering elements have child elements.
    + *     Default is true.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper to use for
    + *     creating and rendering the hovercard element.
    + * @param {Document=} opt_triggeringDocument Optional document to use in place
    + *     of the one included in the DomHelper for finding triggering elements.
    + *     Defaults to the document included in the DomHelper.
    + * @constructor
    + * @extends {goog.ui.AdvancedTooltip}
    + */
    +goog.ui.HoverCard = function(isAnchor, opt_checkDescendants, opt_domHelper,
    +    opt_triggeringDocument) {
    +  goog.ui.AdvancedTooltip.call(this, null, null, opt_domHelper);
    +
    +  if (goog.isFunction(isAnchor)) {
    +    // Override default implementation of {@code isAnchor_}.
    +    this.isAnchor_ = isAnchor;
    +  } else {
    +
    +    /**
    +     * Map of tag names to attribute names that will trigger a hovercard.
    +     * @type {Object}
    +     * @private
    +     */
    +    this.anchors_ = isAnchor;
    +  }
    +
    +  /**
    +   * Whether anchors may have child elements.  If true, then we need to check
    +   * the parent chain of any mouse over event to see if any of those elements
    +   * could be anchors.  Default is true.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.checkDescendants_ = opt_checkDescendants != false;
    +
    +  /**
    +   * Array of anchor elements that should be detached when we are no longer
    +   * associated with them.
    +   * @type {!Array<Element>}
    +   * @private
    +   */
    +  this.tempAttachedAnchors_ = [];
    +
    +  /**
    +   * Document containing the triggering elements, to which we listen for
    +   * mouseover events.
    +   * @type {Document}
    +   * @private
    +   */
    +  this.document_ = opt_triggeringDocument || (opt_domHelper ?
    +      opt_domHelper.getDocument() : goog.dom.getDocument());
    +
    +  goog.events.listen(this.document_, goog.events.EventType.MOUSEOVER,
    +                     this.handleTriggerMouseOver_, false, this);
    +};
    +goog.inherits(goog.ui.HoverCard, goog.ui.AdvancedTooltip);
    +goog.tagUnsealableClass(goog.ui.HoverCard);
    +
    +
    +/**
    + * Enum for event type fired by HoverCard.
    + * @enum {string}
    + */
    +goog.ui.HoverCard.EventType = {
    +  TRIGGER: 'trigger',
    +  CANCEL_TRIGGER: 'canceltrigger',
    +  BEFORE_SHOW: goog.ui.PopupBase.EventType.BEFORE_SHOW,
    +  SHOW: goog.ui.PopupBase.EventType.SHOW,
    +  BEFORE_HIDE: goog.ui.PopupBase.EventType.BEFORE_HIDE,
    +  HIDE: goog.ui.PopupBase.EventType.HIDE
    +};
    +
    +
    +/** @override */
    +goog.ui.HoverCard.prototype.disposeInternal = function() {
    +  goog.ui.HoverCard.superClass_.disposeInternal.call(this);
    +
    +  goog.events.unlisten(this.document_, goog.events.EventType.MOUSEOVER,
    +                       this.handleTriggerMouseOver_, false, this);
    +};
    +
    +
    +/**
    + * Anchor of hovercard currently being shown.  This may be different from
    + * {@code anchor} property if a second hovercard is triggered, when
    + * {@code anchor} becomes the second hovercard while {@code currentAnchor_}
    + * is still the old (but currently displayed) anchor.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HoverCard.prototype.currentAnchor_;
    +
    +
    +/**
    + * Maximum number of levels to search up the dom when checking descendants.
    + * @type {number}
    + * @private
    + */
    +goog.ui.HoverCard.prototype.maxSearchSteps_;
    +
    +
    +/**
    + * This function can be overridden by passing a function as the first parameter
    + * to the constructor.
    + * @param {Node} node Node to test.
    + * @return {boolean} Whether or not hovercard should be shown.
    + * @private
    + */
    +goog.ui.HoverCard.prototype.isAnchor_ = function(node) {
    +  return node.tagName in this.anchors_ &&
    +      !!node.getAttribute(this.anchors_[node.tagName]);
    +};
    +
    +
    +/**
    + * If the user mouses over an element with the correct tag and attribute, then
    + * trigger the hovercard for that element.  If anchors could have children, then
    + * we also need to check the parent chain of the given element.
    + * @param {goog.events.Event} e Mouse over event.
    + * @private
    + */
    +goog.ui.HoverCard.prototype.handleTriggerMouseOver_ = function(e) {
    +  var target = /** @type {Element} */ (e.target);
    +  // Target might be null when hovering over disabled input textboxes in IE.
    +  if (!target) {
    +    return;
    +  }
    +  if (this.isAnchor_(target)) {
    +    this.setPosition(null);
    +    this.triggerForElement(target);
    +  } else if (this.checkDescendants_) {
    +    var trigger = goog.dom.getAncestor(target,
    +                                       goog.bind(this.isAnchor_, this),
    +                                       false,
    +                                       this.maxSearchSteps_);
    +    if (trigger) {
    +      this.setPosition(null);
    +      this.triggerForElement(/** @type {!Element} */ (trigger));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Triggers the hovercard to show after a delay.
    + * @param {Element} anchorElement Element that is triggering the hovercard.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display
    + *     hovercard.
    + * @param {Object=} opt_data Data to pass to the onTrigger event.
    + */
    +goog.ui.HoverCard.prototype.triggerForElement = function(anchorElement,
    +                                                         opt_pos, opt_data) {
    +  if (anchorElement == this.currentAnchor_) {
    +    // Element is already showing, just make sure it doesn't hide.
    +    this.clearHideTimer();
    +    return;
    +  }
    +  if (anchorElement == this.anchor) {
    +    // Hovercard is pending, no need to retrigger.
    +    return;
    +  }
    +
    +  // If a previous hovercard was being triggered, cancel it.
    +  this.maybeCancelTrigger_();
    +
    +  // Create a new event for this trigger
    +  var triggerEvent = new goog.ui.HoverCard.TriggerEvent(
    +      goog.ui.HoverCard.EventType.TRIGGER, this, anchorElement, opt_data);
    +
    +  if (!this.getElements().contains(anchorElement)) {
    +    this.attach(anchorElement);
    +    this.tempAttachedAnchors_.push(anchorElement);
    +  }
    +  this.anchor = anchorElement;
    +  if (!this.onTrigger(triggerEvent)) {
    +    this.onCancelTrigger();
    +    return;
    +  }
    +  var pos = opt_pos || this.position_;
    +  this.startShowTimer(anchorElement,
    +      /** @type {goog.positioning.AbstractPosition} */ (pos));
    +};
    +
    +
    +/**
    + * Sets the current anchor element at the time that the hovercard is shown.
    + * @param {Element} anchor New current anchor element, or null if there is
    + *     no current anchor.
    + * @private
    + */
    +goog.ui.HoverCard.prototype.setCurrentAnchor_ = function(anchor) {
    +  if (anchor != this.currentAnchor_) {
    +    this.detachTempAnchor_(this.currentAnchor_);
    +  }
    +  this.currentAnchor_ = anchor;
    +};
    +
    +
    +/**
    + * If given anchor is in the list of temporarily attached anchors, then
    + * detach and remove from the list.
    + * @param {Element|undefined} anchor Anchor element that we may want to detach
    + *     from.
    + * @private
    + */
    +goog.ui.HoverCard.prototype.detachTempAnchor_ = function(anchor) {
    +  var pos = goog.array.indexOf(this.tempAttachedAnchors_, anchor);
    +  if (pos != -1) {
    +    this.detach(anchor);
    +    this.tempAttachedAnchors_.splice(pos, 1);
    +  }
    +};
    +
    +
    +/**
    + * Called when an element triggers the hovercard.  This will return false
    + * if an event handler sets preventDefault to true, which will prevent
    + * the hovercard from being shown.
    + * @param {!goog.ui.HoverCard.TriggerEvent} triggerEvent Event object to use
    + *     for trigger event.
    + * @return {boolean} Whether hovercard should be shown or cancelled.
    + * @protected
    + */
    +goog.ui.HoverCard.prototype.onTrigger = function(triggerEvent) {
    +  return this.dispatchEvent(triggerEvent);
    +};
    +
    +
    +/**
    + * Abort pending hovercard showing, if any.
    + */
    +goog.ui.HoverCard.prototype.cancelTrigger = function() {
    +  this.clearShowTimer();
    +  this.onCancelTrigger();
    +};
    +
    +
    +/**
    + * If hovercard is in the process of being triggered, then cancel it.
    + * @private
    + */
    +goog.ui.HoverCard.prototype.maybeCancelTrigger_ = function() {
    +  if (this.getState() == goog.ui.Tooltip.State.WAITING_TO_SHOW ||
    +      this.getState() == goog.ui.Tooltip.State.UPDATING) {
    +    this.cancelTrigger();
    +  }
    +};
    +
    +
    +/**
    + * This method gets called when we detect that a trigger event will not lead
    + * to the hovercard being shown.
    + * @protected
    + */
    +goog.ui.HoverCard.prototype.onCancelTrigger = function() {
    +  var event = new goog.ui.HoverCard.TriggerEvent(
    +      goog.ui.HoverCard.EventType.CANCEL_TRIGGER, this, this.anchor || null);
    +  this.dispatchEvent(event);
    +  this.detachTempAnchor_(this.anchor);
    +  delete this.anchor;
    +};
    +
    +
    +/**
    + * Gets the DOM element that triggered the current hovercard.  Note that in
    + * the TRIGGER or CANCEL_TRIGGER events, the current hovercard's anchor may not
    + * be the one that caused the event, so use the event's anchor property instead.
    + * @return {Element} Object that caused the currently displayed hovercard (or
    + *     pending hovercard if none is displayed) to be triggered.
    + */
    +goog.ui.HoverCard.prototype.getAnchorElement = function() {
    +  // this.currentAnchor_ is only set if the hovercard is showing.  If it isn't
    +  // showing yet, then use this.anchor as the pending anchor.
    +  return /** @type {Element} */ (this.currentAnchor_ || this.anchor);
    +};
    +
    +
    +/**
    + * Make sure we detach from temp anchor when we are done displaying hovercard.
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.HoverCard.prototype.onHide_ = function() {
    +  goog.ui.HoverCard.superClass_.onHide_.call(this);
    +  this.setCurrentAnchor_(null);
    +};
    +
    +
    +/**
    + * This mouse over event is only received if the anchor is already attached.
    + * If it was attached manually, then it may need to be triggered.
    + * @param {goog.events.BrowserEvent} event Mouse over event.
    + * @override
    + */
    +goog.ui.HoverCard.prototype.handleMouseOver = function(event) {
    +  // If this is a child of a triggering element, find the triggering element.
    +  var trigger = this.getAnchorFromElement(
    +      /** @type {Element} */ (event.target));
    +
    +  // If we moused over an element different from the one currently being
    +  // triggered (if any), then trigger this new element.
    +  if (trigger && trigger != this.anchor) {
    +    this.triggerForElement(trigger);
    +    return;
    +  }
    +
    +  goog.ui.HoverCard.superClass_.handleMouseOver.call(this, event);
    +};
    +
    +
    +/**
    + * If the mouse moves out of the trigger while we're being triggered, then
    + * cancel it.
    + * @param {goog.events.BrowserEvent} event Mouse out or blur event.
    + * @override
    + */
    +goog.ui.HoverCard.prototype.handleMouseOutAndBlur = function(event) {
    +  // Get ready to see if a trigger should be cancelled.
    +  var anchor = this.anchor;
    +  var state = this.getState();
    +  goog.ui.HoverCard.superClass_.handleMouseOutAndBlur.call(this, event);
    +  if (state != this.getState() &&
    +      (state == goog.ui.Tooltip.State.WAITING_TO_SHOW ||
    +       state == goog.ui.Tooltip.State.UPDATING)) {
    +    // Tooltip's handleMouseOutAndBlur method sets anchor to null.  Reset
    +    // so that the cancel trigger event will have the right data, and so that
    +    // it will be properly detached.
    +    this.anchor = anchor;
    +    this.onCancelTrigger();  // This will remove and detach the anchor.
    +  }
    +};
    +
    +
    +/**
    + * Called by timer from mouse over handler. If this is called and the hovercard
    + * is not shown for whatever reason, then send a cancel trigger event.
    + * @param {Element} el Element to show tooltip for.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
    + *     at.
    + * @override
    + */
    +goog.ui.HoverCard.prototype.maybeShow = function(el, opt_pos) {
    +  goog.ui.HoverCard.superClass_.maybeShow.call(this, el, opt_pos);
    +
    +  if (!this.isVisible()) {
    +    this.cancelTrigger();
    +  } else {
    +    this.setCurrentAnchor_(el);
    +  }
    +};
    +
    +
    +/**
    + * Sets the max number of levels to search up the dom if checking descendants.
    + * @param {number} maxSearchSteps Maximum number of levels to search up the
    + *     dom if checking descendants.
    + */
    +goog.ui.HoverCard.prototype.setMaxSearchSteps = function(maxSearchSteps) {
    +  if (!maxSearchSteps) {
    +    this.checkDescendants_ = false;
    +  } else if (this.checkDescendants_) {
    +    this.maxSearchSteps_ = maxSearchSteps;
    +  }
    +};
    +
    +
    +
    +/**
    + * Create a trigger event for specified anchor and optional data.
    + * @param {goog.ui.HoverCard.EventType} type Event type.
    + * @param {goog.ui.HoverCard} target Hovercard that is triggering the event.
    + * @param {Element} anchor Element that triggered event.
    + * @param {Object=} opt_data Optional data to be available in the TRIGGER event.
    + * @constructor
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.HoverCard.TriggerEvent = function(type, target, anchor, opt_data) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * Element that triggered the hovercard event.
    +   * @type {Element}
    +   */
    +  this.anchor = anchor;
    +
    +  /**
    +   * Optional data to be passed to the listener.
    +   * @type {Object|undefined}
    +   */
    +  this.data = opt_data;
    +};
    +goog.inherits(goog.ui.HoverCard.TriggerEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.html b/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.html
    new file mode 100644
    index 0000000000..5618e21b07
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.html
    @@ -0,0 +1,55 @@
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.HoverCard
    +  </title>
    +  <style type="text/css">
    +   .goog-tooltip {
    +    background: infobackground;
    +    color: infotext;
    +    border: 1px solid infotext;
    +    padding: 1px;
    +    font:menu;
    +  }
    +  </style>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.HoverCardTest');
    +  </script>
    + </head>
    + <body>
    +  <p id="notpopup">
    +   Content
    +  </p>
    +  <span id="john" email="john@gmail.com">
    +   Span for John that can trigger a
    +    hovercard.
    +  </span>
    +  <br />
    +  <span id="jane">
    +   Span for Jane that doesn't trigger a hovercard (no email
    +    attribute)
    +  </span>
    +  <br />
    +  <span id="james" email="james@gmail.com">
    +   Span for James that can trigger a
    +    hovercard
    +   <span id="child">
    +    Child of james
    +   </span>
    +  </span>
    +  <br />
    +  <div id="bill" email="bill@gmail.com">
    +   Doesn't trigger for Bill because
    +    it's a div
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.js b/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.js
    new file mode 100644
    index 0000000000..6193db4fca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hovercard_test.js
    @@ -0,0 +1,355 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.HoverCardTest');
    +goog.setTestOnly('goog.ui.HoverCardTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.HoverCard');
    +
    +var timer = new goog.testing.MockClock();
    +var card;
    +
    +// Variables for mocks
    +var triggeredElement;
    +var cancelledElement;
    +var showDelay;
    +var shownCard;
    +var hideDelay;
    +
    +// spans
    +var john;
    +var jane;
    +var james;
    +var bill;
    +var child;
    +
    +// Inactive
    +var elsewhere;
    +var offAnchor;
    +
    +function setUpPage() {
    +  john = goog.dom.getElement('john');
    +  jane = goog.dom.getElement('jane');
    +  james = goog.dom.getElement('james');
    +  bill = goog.dom.getElement('bill');
    +  child = goog.dom.getElement('child');
    +}
    +
    +function setUp() {
    +  timer.install();
    +  triggeredElement = null;
    +  cancelledElement = null;
    +  showDelay = null;
    +  shownCard = null;
    +  hideDelay = null;
    +  elsewhere = goog.dom.getElement('notpopup');
    +  offAnchor = new goog.math.Coordinate(1, 1);
    +}
    +
    +function initCard(opt_isAnchor, opt_checkChildren, opt_maxSearchSteps) {
    +  var isAnchor = opt_isAnchor || {SPAN: 'email'};
    +  card = new goog.ui.HoverCard(isAnchor, opt_checkChildren);
    +  card.setText('Test hovercard');
    +
    +  if (opt_maxSearchSteps != null) {
    +    card.setMaxSearchSteps(opt_maxSearchSteps);
    +  }
    +
    +  goog.events.listen(card, goog.ui.HoverCard.EventType.TRIGGER, onTrigger);
    +  goog.events.listen(card, goog.ui.HoverCard.EventType.CANCEL_TRIGGER,
    +                     onCancel);
    +  goog.events.listen(card, goog.ui.HoverCard.EventType.BEFORE_SHOW,
    +                     onBeforeShow);
    +
    +  // This gets around the problem where AdvancedToolTip thinks it's
    +  // receiving a ghost event because cursor position hasn't moved off of
    +  // (0, 0).
    +  card.cursorPosition = new goog.math.Coordinate(1, 1);
    +}
    +
    +// Event handlers
    +function onTrigger(event) {
    +  triggeredElement = event.anchor;
    +  if (showDelay) {
    +    card.setShowDelayMs(showDelay);
    +  }
    +  return true;
    +}
    +
    +function onCancel(event) {
    +  cancelledElement = event.anchor;
    +}
    +
    +function onBeforeShow() {
    +  shownCard = card.getAnchorElement();
    +  if (hideDelay) {
    +    card.setHideDelayMs(hideDelay);
    +  }
    +  return true;
    +}
    +
    +function tearDown() {
    +  card.dispose();
    +  timer.uninstall();
    +}
    +
    +
    +/**
    + * Verify that hovercard displays and goes away under normal circumstances.
    + */
    +function testTrigger() {
    +  initCard();
    +
    +  // Mouse over correct element fires trigger
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  assertEquals('Hovercard should have triggered', john,
    +               triggeredElement);
    +
    +  // Show card after delay
    +  timer.tick(showDelay - 1);
    +  assertNull('Card should not have shown', shownCard);
    +  assertFalse(card.isVisible());
    +  hideDelay = 5000;
    +  timer.tick(1);
    +  assertEquals('Card should have shown', john, shownCard);
    +  assertTrue(card.isVisible());
    +
    +  // Mouse out leads to hide delay
    +  goog.testing.events.fireMouseOutEvent(john, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, offAnchor);
    +  timer.tick(hideDelay - 1);
    +  assertTrue('Card should still be visible', card.isVisible());
    +  timer.tick(10);
    +  assertFalse('Card should be hidden', card.isVisible());
    +}
    +
    +
    +/**
    + * Verify that CANCEL_TRIGGER event occurs when mouse goes out of
    + * triggering element before hovercard is shown.
    + */
    +function testOnCancel() {
    +  initCard();
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  timer.tick(showDelay - 1);
    +  goog.testing.events.fireMouseOutEvent(john, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, offAnchor);
    +  timer.tick(10);
    +  assertFalse('Card should be hidden', card.isVisible());
    +  assertEquals('Should have cancelled trigger', john, cancelledElement);
    +}
    +
    +
    +/**
    + * Verify that mousing over non-triggering elements don't interfere.
    + */
    +function testMouseOverNonTrigger() {
    +  initCard();
    +
    +  // Mouse over correct element fires trigger
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  timer.tick(showDelay);
    +
    +  // Mouse over and out other element does nothing
    +  triggeredElement = null;
    +  goog.testing.events.fireMouseOverEvent(jane, elsewhere);
    +  timer.tick(showDelay + 1);
    +  assertNull(triggeredElement);
    +}
    +
    +
    +/**
    + * Verify that a mouse over event with no target will not break
    + * hover card.
    + */
    +function testMouseOverNoTarget() {
    +  initCard();
    +  card.handleTriggerMouseOver_(new goog.testing.events.Event());
    +}
    +
    +
    +/**
    + * Verify that mousing over a second trigger before the first one shows
    + * will correctly cancel the first and show the second.
    + */
    +function testMultipleTriggers() {
    +  initCard();
    +
    +  // Test second trigger when first one still pending
    +  showDelay = 500;
    +  hideDelay = 1000;
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  timer.tick(250);
    +  goog.testing.events.fireMouseOutEvent(john, james);
    +  goog.testing.events.fireMouseOverEvent(james, john);
    +  // First trigger should cancel because it isn't showing yet
    +  assertEquals('Should cancel first trigger', john, cancelledElement);
    +  timer.tick(300);
    +  assertFalse(card.isVisible());
    +  timer.tick(250);
    +  assertEquals('Should show second card', james, shownCard);
    +  assertTrue(card.isVisible());
    +
    +  goog.testing.events.fireMouseOutEvent(james, john);
    +  goog.testing.events.fireMouseOverEvent(john, james);
    +  assertEquals('Should still show second card', james,
    +               card.getAnchorElement());
    +  assertTrue(card.isVisible());
    +
    +  shownCard = null;
    +  timer.tick(501);
    +  assertEquals('Should show first card again', john, shownCard);
    +  assertTrue(card.isVisible());
    +
    +  // Test that cancelling while another is showing gives correct cancel
    +  // information
    +  cancelledElement = null;
    +  goog.testing.events.fireMouseOutEvent(john, james);
    +  goog.testing.events.fireMouseOverEvent(james, john);
    +  goog.testing.events.fireMouseOutEvent(james, elsewhere);
    +  assertEquals('Should cancel second card', james, cancelledElement);
    +}
    +
    +
    +/**
    + * Verify manual triggering.
    + */
    +function testManualTrigger() {
    +  initCard();
    +
    +  // Doesn't normally trigger for div tag
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(bill, elsewhere);
    +  timer.tick(showDelay);
    +  assertFalse(card.isVisible());
    +
    +  // Manually trigger element
    +  card.triggerForElement(bill);
    +  hideDelay = 600;
    +  timer.tick(showDelay);
    +  assertTrue(card.isVisible());
    +  goog.testing.events.fireMouseOutEvent(bill, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, offAnchor);
    +  timer.tick(hideDelay);
    +  assertFalse(card.isVisible());
    +}
    +
    +
    +/**
    + * Verify creating with isAnchor function.
    + */
    +function testIsAnchor() {
    +  // Initialize card so only bill triggers it.
    +  initCard(function(element) {
    +    return element == bill;
    +  });
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(bill, elsewhere);
    +  timer.tick(showDelay);
    +  assertTrue('Should trigger card', card.isVisible());
    +
    +  hideDelay = 300;
    +  goog.testing.events.fireMouseOutEvent(bill, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(document, offAnchor);
    +  timer.tick(hideDelay);
    +  assertFalse(card.isVisible());
    +
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  timer.tick(showDelay);
    +  assertFalse('Should not trigger card', card.isVisible());
    +}
    +
    +
    +/**
    + * Verify mouse over child of anchor triggers hovercard.
    + */
    +function testAnchorWithChildren() {
    +  initCard();
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(james, elsewhere);
    +  timer.tick(250);
    +
    +  // Moving from an anchor to a child of that anchor shouldn't cancel
    +  // or retrigger.
    +  var childBounds = goog.style.getBounds(child);
    +  var inChild = new goog.math.Coordinate(childBounds.left + 1,
    +                                         childBounds.top + 1);
    +  goog.testing.events.fireMouseOutEvent(james, child);
    +  goog.testing.events.fireMouseMoveEvent(child, inChild);
    +  assertNull("Shouldn't cancel trigger", cancelledElement);
    +  triggeredElement = null;
    +  goog.testing.events.fireMouseOverEvent(child, james);
    +  assertNull("Shouldn't retrigger card", triggeredElement);
    +  timer.tick(250);
    +  assertTrue('Card should show with original delay', card.isVisible());
    +
    +  hideDelay = 300;
    +  goog.testing.events.fireMouseOutEvent(child, elsewhere);
    +  goog.testing.events.fireMouseMoveEvent(child, offAnchor);
    +  timer.tick(hideDelay);
    +  assertFalse(card.isVisible());
    +
    +  goog.testing.events.fireMouseOverEvent(child, elsewhere);
    +  timer.tick(showDelay);
    +  assertTrue('Mouse over child should trigger card', card.isVisible());
    +}
    +
    +function testNoTriggerWithMaxSearchSteps() {
    +  initCard(undefined, true, 0);
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(child, elsewhere);
    +  timer.tick(showDelay);
    +  assertFalse('Should not trigger card', card.isVisible());
    +}
    +
    +function testTriggerWithMaxSearchSteps() {
    +  initCard(undefined, true, 2);
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(child, elsewhere);
    +  timer.tick(showDelay);
    +  assertTrue('Should trigger card', card.isVisible());
    +}
    +
    +function testPositionAfterSecondTriggerWithMaxSearchSteps() {
    +  initCard(undefined, true, 2);
    +
    +  showDelay = 500;
    +  goog.testing.events.fireMouseOverEvent(john, elsewhere);
    +  timer.tick(showDelay);
    +  assertTrue('Should trigger card', card.isVisible());
    +  assertEquals('Card cursor x coordinate should be 1',
    +      card.position_.coordinate.x, 1);
    +  card.cursorPosition = new goog.math.Coordinate(2, 2);
    +  goog.testing.events.fireMouseOverEvent(child, elsewhere);
    +  timer.tick(showDelay);
    +  assertTrue('Should trigger card', card.isVisible());
    +  assertEquals('Card cursor x coordinate should be 2',
    +      card.position_.coordinate.x, 2);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvapalette.js b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette.js
    new file mode 100644
    index 0000000000..4c3abf7597
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette.js
    @@ -0,0 +1,295 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An HSVA (hue/saturation/value/alpha) color palette/picker
    + * implementation.
    + * Without the styles from the demo css file, only a hex color label and input
    + * field show up.
    + *
    + * @author chrisn@google.com (Chris Nokleberg)
    + * @see ../demos/hsvapalette.html
    + */
    +
    +goog.provide('goog.ui.HsvaPalette');
    +
    +goog.require('goog.array');
    +goog.require('goog.color.alpha');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.HsvPalette');
    +
    +
    +
    +/**
    + * Creates an HSVA palette. Allows a user to select the hue, saturation,
    + * value/brightness and alpha/opacity.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {string=} opt_color Optional initial color, without alpha (default is
    + *     red).
    + * @param {number=} opt_alpha Optional initial alpha (default is 1).
    + * @param {string=} opt_class Optional base for creating classnames (default is
    + *     'goog-hsva-palette').
    + * @extends {goog.ui.HsvPalette}
    + * @constructor
    + * @final
    + */
    +goog.ui.HsvaPalette = function(opt_domHelper, opt_color, opt_alpha, opt_class) {
    +  goog.ui.HsvaPalette.base(
    +      this, 'constructor', opt_domHelper, opt_color, opt_class);
    +
    +  /**
    +   * Alpha transparency of the currently selected color, in [0, 1]. When
    +   * undefined, the palette will behave as a non-transparent HSV palette,
    +   * assuming full opacity.
    +   * @type {number}
    +   * @private
    +   */
    +  this.alpha_ = goog.isDef(opt_alpha) ? opt_alpha : 1;
    +
    +  /**
    +   * @override
    +   */
    +  this.className = opt_class || goog.getCssName('goog-hsva-palette');
    +};
    +goog.inherits(goog.ui.HsvaPalette, goog.ui.HsvPalette);
    +
    +
    +/**
    + * DOM element representing the alpha background image.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvaPalette.prototype.aImageEl_;
    +
    +
    +/**
    + * DOM element representing the alpha handle.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvaPalette.prototype.aHandleEl_;
    +
    +
    +/**
    + * DOM element representing the swatch backdrop image.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvaPalette.prototype.swatchBackdropEl_;
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.getAlpha = function() {
    +  return this.alpha_;
    +};
    +
    +
    +/**
    + * Sets which color is selected and update the UI. The passed color should be
    + * in #rrggbb format. The alpha value will be set to 1.
    + * @param {number} alpha The selected alpha value, in [0, 1].
    + */
    +goog.ui.HsvaPalette.prototype.setAlpha = function(alpha) {
    +  this.setColorAlphaHelper_(this.color, alpha);
    +};
    +
    +
    +/**
    + * Sets which color is selected and update the UI. The passed color should be
    + * in #rrggbb format. The alpha value will be set to 1.
    + * @param {string} color The selected color.
    + * @override
    + */
    +goog.ui.HsvaPalette.prototype.setColor = function(color) {
    +  this.setColorAlphaHelper_(color, 1);
    +};
    +
    +
    +/**
    + * Gets the color that is currently selected in this color picker, in #rrggbbaa
    + * format.
    + * @return {string} The string of the selected color with alpha.
    + */
    +goog.ui.HsvaPalette.prototype.getColorRgbaHex = function() {
    +  var alphaHex = Math.floor(this.alpha_ * 255).toString(16);
    +  return this.color + (alphaHex.length == 1 ? '0' + alphaHex : alphaHex);
    +};
    +
    +
    +/**
    + * Sets which color is selected and update the UI. The passed color should be
    + * in #rrggbbaa format. The alpha value will be set to 1.
    + * @param {string} color The selected color with alpha.
    + */
    +goog.ui.HsvaPalette.prototype.setColorRgbaHex = function(color) {
    +  var parsed = goog.ui.HsvaPalette.parseColorRgbaHex_(color);
    +  this.setColorAlphaHelper_(parsed[0], parsed[1]);
    +};
    +
    +
    +/**
    + * Sets which color and alpha value are selected and update the UI. The passed
    + * color should be in #rrggbb format.
    + * @param {string} color The selected color in #rrggbb format.
    + * @param {number} alpha The selected alpha value, in [0, 1].
    + * @private
    + */
    +goog.ui.HsvaPalette.prototype.setColorAlphaHelper_ = function(color, alpha) {
    +  var colorChange = this.color != color;
    +  var alphaChange = this.alpha_ != alpha;
    +  this.alpha_ = alpha;
    +  this.color = color;
    +  if (colorChange) {
    +    // This is to prevent multiple event dispatches.
    +    this.setColorInternal(color);
    +  }
    +  if (colorChange || alphaChange) {
    +    this.updateUi();
    +    this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.createDom = function() {
    +  goog.ui.HsvaPalette.base(this, 'createDom');
    +
    +  var dom = this.getDomHelper();
    +  this.aImageEl_ = dom.createDom(
    +      goog.dom.TagName.DIV, goog.getCssName(this.className, 'a-image'));
    +  this.aHandleEl_ = dom.createDom(
    +      goog.dom.TagName.DIV, goog.getCssName(this.className, 'a-handle'));
    +  this.swatchBackdropEl_ = dom.createDom(
    +      goog.dom.TagName.DIV, goog.getCssName(this.className, 'swatch-backdrop'));
    +  var element = this.getElement();
    +  dom.appendChild(element, this.aImageEl_);
    +  dom.appendChild(element, this.aHandleEl_);
    +  dom.appendChild(element, this.swatchBackdropEl_);
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.disposeInternal = function() {
    +  goog.ui.HsvaPalette.base(this, 'disposeInternal');
    +
    +  delete this.aImageEl_;
    +  delete this.aHandleEl_;
    +  delete this.swatchBackdropEl_;
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.updateUi = function() {
    +  goog.ui.HsvaPalette.base(this, 'updateUi');
    +  if (this.isInDocument()) {
    +    var a = this.alpha_ * 255;
    +    var top = this.aImageEl_.offsetTop -
    +        Math.floor(this.aHandleEl_.offsetHeight / 2) +
    +        this.aImageEl_.offsetHeight * ((255 - a) / 255);
    +    this.aHandleEl_.style.top = top + 'px';
    +    this.aImageEl_.style.backgroundColor = this.color;
    +    goog.style.setOpacity(this.swatchElement, a / 255);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.updateInput = function() {
    +  if (!goog.array.equals([this.color, this.alpha_],
    +      goog.ui.HsvaPalette.parseUserInput_(this.inputElement.value))) {
    +    this.inputElement.value = this.getColorRgbaHex();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.handleMouseDown = function(e) {
    +  goog.ui.HsvaPalette.base(this, 'handleMouseDown', e);
    +  if (e.target == this.aImageEl_ || e.target == this.aHandleEl_) {
    +    // Setup value change listeners
    +    var b = goog.style.getBounds(this.valueBackgroundImageElement);
    +    this.handleMouseMoveA_(b, e);
    +    this.mouseMoveListener = goog.events.listen(
    +        this.getDomHelper().getDocument(),
    +        goog.events.EventType.MOUSEMOVE,
    +        goog.bind(this.handleMouseMoveA_, this, b));
    +    this.mouseUpListener = goog.events.listen(
    +        this.getDomHelper().getDocument(),
    +        goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Handles mousemove events on the document once a drag operation on the alpha
    + * slider has started.
    + * @param {goog.math.Rect} b Boundaries of the value slider object at the start
    + *     of the drag operation.
    + * @param {goog.events.Event} e Event object.
    + * @private
    + */
    +goog.ui.HsvaPalette.prototype.handleMouseMoveA_ = function(b, e) {
    +  e.preventDefault();
    +  var vportPos = this.getDomHelper().getDocumentScroll();
    +  var newA = (b.top + b.height - Math.min(
    +      Math.max(vportPos.y + e.clientY, b.top),
    +      b.top + b.height)) / b.height;
    +  this.setAlpha(newA);
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvaPalette.prototype.handleInput = function(e) {
    +  var parsed = goog.ui.HsvaPalette.parseUserInput_(this.inputElement.value);
    +  if (parsed) {
    +    this.setColorAlphaHelper_(parsed[0], parsed[1]);
    +  }
    +};
    +
    +
    +/**
    + * Parses an #rrggbb or #rrggbbaa color string.
    + * @param {string} value User-entered color value.
    + * @return {Array<?>} A two element array [color, alpha], where color is
    + *     #rrggbb and alpha is in [0, 1]. Null if the argument was invalid.
    + * @private
    + */
    +goog.ui.HsvaPalette.parseUserInput_ = function(value) {
    +  if (/^#?[0-9a-f]{8}$/i.test(value)) {
    +    return goog.ui.HsvaPalette.parseColorRgbaHex_(value);
    +  } else if (/^#?[0-9a-f]{6}$/i.test(value)) {
    +    return [value, 1];
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Parses a #rrggbbaa color string.
    + * @param {string} color The color and alpha in #rrggbbaa format.
    + * @return {!Array<?>} A two element array [color, alpha], where color is
    + *     #rrggbb and alpha is in [0, 1].
    + * @private
    + */
    +goog.ui.HsvaPalette.parseColorRgbaHex_ = function(color) {
    +  var hex = goog.color.alpha.parse(color).hex;
    +  return [
    +    goog.color.alpha.extractHexColor(hex),
    +    parseInt(goog.color.alpha.extractAlpha(hex), 16) / 255
    +  ];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.html b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.html
    new file mode 100644
    index 0000000000..228ad9e444
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   HsvaPalette Unit Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script src="../deps.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.HsvaPaletteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.js b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.js
    new file mode 100644
    index 0000000000..174801e04f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvapalette_test.js
    @@ -0,0 +1,144 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.HsvaPaletteTest');
    +goog.setTestOnly('goog.ui.HsvaPaletteTest');
    +
    +goog.require('goog.color.alpha');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.Event');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.HsvaPalette');
    +goog.require('goog.userAgent');
    +
    +var samplePalette;
    +var eventWasFired = false;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  samplePalette = new goog.ui.HsvaPalette();
    +}
    +
    +function tearDown() {
    +  samplePalette.dispose();
    +  stubs.reset();
    +}
    +
    +function testZeroAlpha() {
    +  var palette = new goog.ui.HsvaPalette(null, undefined, 0);
    +  assertEquals(0, palette.getAlpha());
    +}
    +
    +function testOptionalInitialColor() {
    +  var alpha = 0.5;
    +  var color = '#0000ff';
    +  var palette = new goog.ui.HsvaPalette(null, color, alpha);
    +  assertEquals(color, palette.getColor());
    +  assertEquals(alpha, palette.getAlpha());
    +}
    +
    +function testCustomClassName() {
    +  var customClassName = 'custom-plouf';
    +  var customClassPalette =
    +      new goog.ui.HsvaPalette(null, null, null, customClassName);
    +  customClassPalette.createDom();
    +  assertTrue(goog.dom.classlist.contains(customClassPalette.getElement(),
    +      customClassName));
    +}
    +
    +function testSetColor() {
    +  var color = '#abcdef01';
    +  samplePalette.setColorRgbaHex(color);
    +  assertEquals(color,
    +      goog.color.alpha.parse(samplePalette.getColorRgbaHex()).hex);
    +  color = 'abcdef01';
    +  samplePalette.setColorRgbaHex(color);
    +  assertEquals('#' + color,
    +      goog.color.alpha.parse(samplePalette.getColorRgbaHex()).hex);
    +}
    +
    +function testRender() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  assertTrue(samplePalette.isInDocument());
    +
    +  var elem = samplePalette.getElement();
    +  assertNotNull(elem);
    +  assertEquals(goog.dom.TagName.DIV, elem.tagName);
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
    +    assertSameElements('On IE6, the noalpha class must be present',
    +        ['goog-hsva-palette', 'goog-hsva-palette-noalpha'],
    +        goog.dom.classlist.get(elem));
    +  } else {
    +    assertEquals('The noalpha class must not be present',
    +        'goog-hsva-palette', elem.className);
    +  }
    +}
    +
    +function testInputColor() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +  var color = '#00112233';
    +  samplePalette.inputElement.value = color;
    +  samplePalette.handleInput(null);
    +  assertEquals(color,
    +      goog.color.alpha.parse(samplePalette.getColorRgbaHex()).hex);
    +}
    +
    +function testHandleMouseMoveAlpha() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +  stubs.set(goog.dom, 'getPageScroll', function() {
    +    return new goog.math.Coordinate(0, 0);
    +  });
    +
    +  // Lowering the opacity of a dark, opaque red should yield a
    +  // more transparent red.
    +  samplePalette.setColorRgbaHex('#630c0000');
    +  goog.style.setPageOffset(samplePalette.aImageEl_, 0, 0);
    +  goog.style.setSize(samplePalette.aImageEl_, 10, 100);
    +  var boundaries = goog.style.getBounds(samplePalette.aImageEl_);
    +
    +  var event = new goog.events.Event();
    +  event.clientY = boundaries.top;
    +  samplePalette.handleMouseMoveA_(boundaries, event);
    +
    +  assertEquals('#630c00ff', samplePalette.getColorRgbaHex());
    +}
    +
    +function testSwatchOpacity() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  samplePalette.setAlpha(1);
    +  assertEquals(1, goog.style.getOpacity(samplePalette.swatchElement));
    +
    +  samplePalette.setAlpha(0x99 / 0xff);
    +  assertEquals(0.6, goog.style.getOpacity(samplePalette.swatchElement));
    +
    +  samplePalette.setAlpha(0);
    +  assertEquals(0, goog.style.getOpacity(samplePalette.swatchElement));
    +}
    +
    +function testNoTransparencyBehavior() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  samplePalette.inputElement.value = '#abcdef22';
    +  samplePalette.handleInput(null);
    +  samplePalette.inputElement.value = '#abcdef';
    +  samplePalette.handleInput(null);
    +  assertEquals(1, goog.style.getOpacity(samplePalette.swatchElement));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvpalette.js b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette.js
    new file mode 100644
    index 0000000000..18d272b05b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette.js
    @@ -0,0 +1,524 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An HSV (hue/saturation/value) color palette/picker
    + * implementation. Inspired by examples like
    + * http://johndyer.name/lab/colorpicker/ and the author's initial work. This
    + * control allows for more control in picking colors than a simple swatch-based
    + * palette. Without the styles from the demo css file, only a hex color label
    + * and input field show up.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/hsvpalette.html
    + */
    +
    +goog.provide('goog.ui.HsvPalette');
    +
    +goog.require('goog.color');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Creates an HSV palette. Allows a user to select the hue, saturation and
    + * value/brightness.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {string=} opt_color Optional initial color (default is red).
    + * @param {string=} opt_class Optional base for creating classnames (default is
    + *     goog.getCssName('goog-hsv-palette')).
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.HsvPalette = function(opt_domHelper, opt_color, opt_class) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.setColorInternal(opt_color || '#f00');
    +
    +  /**
    +   * The base class name for the component.
    +   * @type {string}
    +   * @protected
    +   */
    +  this.className = opt_class || goog.getCssName('goog-hsv-palette');
    +
    +  /**
    +   * The document which is being listened to.
    +   * type {HTMLDocument}
    +   * @private
    +   */
    +  this.document_ = this.getDomHelper().getDocument();
    +};
    +goog.inherits(goog.ui.HsvPalette, goog.ui.Component);
    +// TODO(user): Make this inherit from goog.ui.Control and split this into
    +// a control and a renderer.
    +goog.tagUnsealableClass(goog.ui.HsvPalette);
    +
    +
    +/**
    + * @desc Label for an input field where a user can enter a hexadecimal color
    + * specification, such as #ff0000 for red.
    + * @private
    + */
    +goog.ui.HsvPalette.MSG_HSV_PALETTE_HEX_COLOR_ = goog.getMsg('Hex color');
    +
    +
    +/**
    + * DOM element representing the hue/saturation background image.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.hsImageEl_;
    +
    +
    +/**
    + * DOM element representing the hue/saturation handle.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.hsHandleEl_;
    +
    +
    +/**
    + * DOM element representing the value background image.
    + * @type {Element}
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.valueBackgroundImageElement;
    +
    +
    +/**
    + * DOM element representing the value handle.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.vHandleEl_;
    +
    +
    +/**
    + * DOM element representing the current color swatch.
    + * @type {Element}
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.swatchElement;
    +
    +
    +/**
    + * DOM element representing the hex color input text field.
    + * @type {Element}
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.inputElement;
    +
    +
    +/**
    + * Input handler object for the hex value input field.
    + * @type {goog.events.InputHandler}
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.inputHandler_;
    +
    +
    +/**
    + * Listener key for the mousemove event (during a drag operation).
    + * @type {goog.events.Key}
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.mouseMoveListener;
    +
    +
    +/**
    + * Listener key for the mouseup event (during a drag operation).
    + * @type {goog.events.Key}
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.mouseUpListener;
    +
    +
    +/** @private {!goog.color.Hsv} */
    +goog.ui.HsvPalette.prototype.hsv_;
    +
    +
    +/**
    + * Hex representation of the color.
    + * @protected {string}
    + */
    +goog.ui.HsvPalette.prototype.color;
    +
    +
    +/**
    + * Gets the color that is currently selected in this color picker.
    + * @return {string} The string of the selected color.
    + */
    +goog.ui.HsvPalette.prototype.getColor = function() {
    +  return this.color;
    +};
    +
    +
    +/**
    + * Alpha transparency of the currently selected color, in [0, 1].
    + * For the HSV palette this always returns 1. The HSVA palette overrides
    + * this method.
    + * @return {number} The current alpha value.
    + */
    +goog.ui.HsvPalette.prototype.getAlpha = function() {
    +  return 1;
    +};
    +
    +
    +/**
    + * Updates the text entry field.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.updateInput = function() {
    +  var parsed;
    +  try {
    +    parsed = goog.color.parse(this.inputElement.value).hex;
    +  } catch (e) {
    +    // ignore
    +  }
    +  if (this.color != parsed) {
    +    this.inputElement.value = this.color;
    +  }
    +};
    +
    +
    +/**
    + * Sets which color is selected and update the UI.
    + * @param {string} color The selected color.
    + */
    +goog.ui.HsvPalette.prototype.setColor = function(color) {
    +  if (color != this.color) {
    +    this.setColorInternal(color);
    +    this.updateUi();
    +    this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  }
    +};
    +
    +
    +/**
    + * Sets which color is selected.
    + * @param {string} color The selected color.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.setColorInternal = function(color) {
    +  var rgbHex = goog.color.parse(color).hex;
    +  var rgbArray = goog.color.hexToRgb(rgbHex);
    +  this.hsv_ = goog.color.rgbArrayToHsv(rgbArray);
    +  // Hue is divided by 360 because the documentation for goog.color is currently
    +  // incorrect.
    +  // TODO(user): Fix this, see http://1324469 .
    +  this.hsv_[0] = this.hsv_[0] / 360;
    +  this.color = rgbHex;
    +};
    +
    +
    +/**
    + * Alters the hue, saturation, and/or value of the currently selected color and
    + * updates the UI.
    + * @param {?number=} opt_hue (optional) hue in [0, 1].
    + * @param {?number=} opt_saturation (optional) saturation in [0, 1].
    + * @param {?number=} opt_value (optional) value in [0, 255].
    + */
    +goog.ui.HsvPalette.prototype.setHsv = function(opt_hue,
    +                                               opt_saturation,
    +                                               opt_value) {
    +  if (opt_hue != null || opt_saturation != null || opt_value != null) {
    +    this.setHsv_(opt_hue, opt_saturation, opt_value);
    +    this.updateUi();
    +    this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  }
    +};
    +
    +
    +/**
    + * Alters the hue, saturation, and/or value of the currently selected color.
    + * @param {?number=} opt_hue (optional) hue in [0, 1].
    + * @param {?number=} opt_saturation (optional) saturation in [0, 1].
    + * @param {?number=} opt_value (optional) value in [0, 255].
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.setHsv_ = function(opt_hue,
    +                                                opt_saturation,
    +                                                opt_value) {
    +  this.hsv_[0] = (opt_hue != null) ? opt_hue : this.hsv_[0];
    +  this.hsv_[1] = (opt_saturation != null) ? opt_saturation : this.hsv_[1];
    +  this.hsv_[2] = (opt_value != null) ? opt_value : this.hsv_[2];
    +  // Hue is multiplied by 360 because the documentation for goog.color is
    +  // currently incorrect.
    +  // TODO(user): Fix this, see http://1324469 .
    +  this.color = goog.color.hsvArrayToHex([
    +    this.hsv_[0] * 360,
    +    this.hsv_[1],
    +    this.hsv_[2]
    +  ]);
    +};
    +
    +
    +/**
    + * HsvPalettes cannot be used to decorate pre-existing html, since the
    + * structure they build is fairly complicated.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Returns always false.
    + * @override
    + */
    +goog.ui.HsvPalette.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvPalette.prototype.createDom = function() {
    +  var dom = this.getDomHelper();
    +  var noalpha = (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) ?
    +      ' ' + goog.getCssName(this.className, 'noalpha') : '';
    +
    +  var backdrop = dom.createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'hs-backdrop'));
    +
    +  this.hsHandleEl_ = dom.createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'hs-handle'));
    +
    +  this.hsImageEl_ = dom.createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'hs-image'),
    +      this.hsHandleEl_);
    +
    +  this.valueBackgroundImageElement = dom.createDom(
    +      goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'v-image'));
    +
    +  this.vHandleEl_ = dom.createDom(
    +      goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'v-handle'));
    +
    +  this.swatchElement = dom.createDom(goog.dom.TagName.DIV,
    +      goog.getCssName(this.className, 'swatch'));
    +
    +  this.inputElement = dom.createDom('input', {
    +    'class': goog.getCssName(this.className, 'input'),
    +    'aria-label': goog.ui.HsvPalette.MSG_HSV_PALETTE_HEX_COLOR_,
    +    'type': 'text', 'dir': 'ltr'
    +  });
    +
    +  var labelElement = dom.createDom('label', null, this.inputElement);
    +
    +  var element = dom.createDom(goog.dom.TagName.DIV,
    +      this.className + noalpha,
    +      backdrop,
    +      this.hsImageEl_,
    +      this.valueBackgroundImageElement,
    +      this.vHandleEl_,
    +      this.swatchElement,
    +      labelElement);
    +
    +  this.setElementInternal(element);
    +
    +  // TODO(arv): Set tabIndex
    +};
    +
    +
    +/**
    + * Renders the color picker inside the provided element. This will override the
    + * current content of the element.
    + * @override
    + */
    +goog.ui.HsvPalette.prototype.enterDocument = function() {
    +  goog.ui.HsvPalette.superClass_.enterDocument.call(this);
    +
    +  // TODO(user): Accessibility.
    +
    +  this.updateUi();
    +
    +  var handler = this.getHandler();
    +  handler.listen(this.getElement(), goog.events.EventType.MOUSEDOWN,
    +      this.handleMouseDown);
    +
    +  // Cannot create InputHandler in createDom because IE throws an exception
    +  // on document.activeElement
    +  if (!this.inputHandler_) {
    +    this.inputHandler_ = new goog.events.InputHandler(this.inputElement);
    +  }
    +
    +  handler.listen(this.inputHandler_,
    +      goog.events.InputHandler.EventType.INPUT, this.handleInput);
    +};
    +
    +
    +/** @override */
    +goog.ui.HsvPalette.prototype.disposeInternal = function() {
    +  goog.ui.HsvPalette.superClass_.disposeInternal.call(this);
    +
    +  delete this.hsImageEl_;
    +  delete this.hsHandleEl_;
    +  delete this.valueBackgroundImageElement;
    +  delete this.vHandleEl_;
    +  delete this.swatchElement;
    +  delete this.inputElement;
    +  if (this.inputHandler_) {
    +    this.inputHandler_.dispose();
    +    delete this.inputHandler_;
    +  }
    +  goog.events.unlistenByKey(this.mouseMoveListener);
    +  goog.events.unlistenByKey(this.mouseUpListener);
    +};
    +
    +
    +/**
    + * Updates the position, opacity, and styles for the UI representation of the
    + * palette.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.updateUi = function() {
    +  if (this.isInDocument()) {
    +    var h = this.hsv_[0];
    +    var s = this.hsv_[1];
    +    var v = this.hsv_[2];
    +
    +    var left = this.hsImageEl_.offsetWidth * h;
    +
    +    // We don't use a flipped gradient image in RTL, so we need to flip the
    +    // offset in RTL so that it still hovers over the correct color on the
    +    // gradiant.
    +    if (this.isRightToLeft()) {
    +      left = this.hsImageEl_.offsetWidth - left;
    +    }
    +
    +    // We also need to account for the handle size.
    +    var handleOffset = Math.ceil(this.hsHandleEl_.offsetWidth / 2);
    +    left -= handleOffset;
    +
    +    var top = this.hsImageEl_.offsetHeight * (1 - s);
    +    // Account for the handle size.
    +    top -= Math.ceil(this.hsHandleEl_.offsetHeight / 2);
    +
    +    goog.style.bidi.setPosition(this.hsHandleEl_, left, top,
    +        this.isRightToLeft());
    +
    +    top = this.valueBackgroundImageElement.offsetTop -
    +        Math.floor(this.vHandleEl_.offsetHeight / 2) +
    +        this.valueBackgroundImageElement.offsetHeight * ((255 - v) / 255);
    +
    +    this.vHandleEl_.style.top = top + 'px';
    +    goog.style.setOpacity(this.hsImageEl_, (v / 255));
    +
    +    goog.style.setStyle(this.valueBackgroundImageElement, 'background-color',
    +        goog.color.hsvToHex(this.hsv_[0] * 360, this.hsv_[1], 255));
    +
    +    goog.style.setStyle(this.swatchElement, 'background-color', this.color);
    +    goog.style.setStyle(this.swatchElement, 'color',
    +                        (this.hsv_[2] > 255 / 2) ? '#000' : '#fff');
    +    this.updateInput();
    +  }
    +};
    +
    +
    +/**
    + * Handles mousedown events on palette UI elements.
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.handleMouseDown = function(e) {
    +  if (e.target == this.valueBackgroundImageElement ||
    +      e.target == this.vHandleEl_) {
    +    // Setup value change listeners
    +    var b = goog.style.getBounds(this.valueBackgroundImageElement);
    +    this.handleMouseMoveV_(b, e);
    +    this.mouseMoveListener = goog.events.listen(this.document_,
    +        goog.events.EventType.MOUSEMOVE,
    +        goog.bind(this.handleMouseMoveV_, this, b));
    +    this.mouseUpListener = goog.events.listen(this.document_,
    +        goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
    +  } else if (e.target == this.hsImageEl_ || e.target == this.hsHandleEl_) {
    +    // Setup hue/saturation change listeners
    +    var b = goog.style.getBounds(this.hsImageEl_);
    +    this.handleMouseMoveHs_(b, e);
    +    this.mouseMoveListener = goog.events.listen(this.document_,
    +        goog.events.EventType.MOUSEMOVE,
    +        goog.bind(this.handleMouseMoveHs_, this, b));
    +    this.mouseUpListener = goog.events.listen(this.document_,
    +        goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Handles mousemove events on the document once a drag operation on the value
    + * slider has started.
    + * @param {goog.math.Rect} b Boundaries of the value slider object at the start
    + *     of the drag operation.
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.handleMouseMoveV_ = function(b, e) {
    +  e.preventDefault();
    +  var vportPos = this.getDomHelper().getDocumentScroll();
    +
    +  var height = Math.min(
    +      Math.max(vportPos.y + e.clientY, b.top),
    +      b.top + b.height);
    +
    +  var newV = Math.round(
    +      255 * (b.top + b.height - height) / b.height);
    +
    +  this.setHsv(null, null, newV);
    +};
    +
    +
    +/**
    + * Handles mousemove events on the document once a drag operation on the
    + * hue/saturation slider has started.
    + * @param {goog.math.Rect} b Boundaries of the value slider object at the start
    + *     of the drag operation.
    + * @param {goog.events.BrowserEvent} e Event object.
    + * @private
    + */
    +goog.ui.HsvPalette.prototype.handleMouseMoveHs_ = function(b, e) {
    +  e.preventDefault();
    +  var vportPos = this.getDomHelper().getDocumentScroll();
    +  var newH = (Math.min(Math.max(vportPos.x + e.clientX, b.left),
    +      b.left + b.width) - b.left) / b.width;
    +  var newS = (-Math.min(Math.max(vportPos.y + e.clientY, b.top),
    +      b.top + b.height) + b.top + b.height) / b.height;
    +  this.setHsv(newH, newS, null);
    +};
    +
    +
    +/**
    + * Handles mouseup events on the document, which ends a drag operation.
    + * @param {goog.events.Event} e Event object.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.handleMouseUp = function(e) {
    +  goog.events.unlistenByKey(this.mouseMoveListener);
    +  goog.events.unlistenByKey(this.mouseUpListener);
    +};
    +
    +
    +/**
    + * Handles input events on the hex value input field.
    + * @param {goog.events.Event} e Event object.
    + * @protected
    + */
    +goog.ui.HsvPalette.prototype.handleInput = function(e) {
    +  if (/^#?[0-9a-f]{6}$/i.test(this.inputElement.value)) {
    +    this.setColor(this.inputElement.value);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.html b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.html
    new file mode 100644
    index 0000000000..1f2919eccd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.html
    @@ -0,0 +1,34 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <!--
    +This test has not yet been updated to run on IE8. See http://b/hotlist?id=36311
    +-->
    +  <!--meta http-equiv="X-UA-Compatible" content="IE=edge"-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    +  <title>
    +   HsvPalette Unit Tests
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script src="../deps.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.HsvPaletteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    +  <div id="sandboxRtl" dir="rtl">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.js b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.js
    new file mode 100644
    index 0000000000..8edeb38950
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/hsvpalette_test.js
    @@ -0,0 +1,208 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.HsvPaletteTest');
    +goog.setTestOnly('goog.ui.HsvPaletteTest');
    +
    +goog.require('goog.color');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.HsvPalette');
    +goog.require('goog.userAgent');
    +
    +var samplePalette;
    +var eventWasFired = false;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  samplePalette = new goog.ui.HsvPalette();
    +}
    +
    +function tearDown() {
    +  samplePalette.dispose();
    +  stubs.reset();
    +}
    +
    +function testRtl() {
    +  samplePalette.render(document.getElementById('sandboxRtl'));
    +  var color = '#ffffff';
    +  samplePalette.inputElement.value = color;
    +  samplePalette.handleInput(null);
    +  var expectedRight = samplePalette.hsImageEl_.offsetWidth -
    +      Math.ceil(samplePalette.hsHandleEl_.offsetWidth / 2);
    +  assertEquals(expectedRight + 'px',
    +      samplePalette.hsHandleEl_.style['right']);
    +  assertEquals('', samplePalette.hsHandleEl_.style['left']);
    +}
    +
    +function testOptionalInitialColor() {
    +  var initialColor = '#0000ff';
    +  var customInitialPalette = new goog.ui.HsvPalette(null, initialColor);
    +  assertEquals(initialColor,
    +      goog.color.parse(customInitialPalette.getColor()).hex);
    +}
    +
    +function testCustomClassName() {
    +  var customClassName = 'custom-plouf';
    +  var customClassPalette =
    +      new goog.ui.HsvPalette(null, null, customClassName);
    +  customClassPalette.createDom();
    +  assertTrue(goog.dom.classlist.contains(customClassPalette.getElement(),
    +      customClassName));
    +}
    +
    +function testCannotDecorate() {
    +  assertFalse(samplePalette.canDecorate());
    +}
    +
    +function testSetColor() {
    +  var color = '#abcdef';
    +  samplePalette.setColor(color);
    +  assertEquals(color, goog.color.parse(samplePalette.getColor()).hex);
    +  color = 'abcdef';
    +  samplePalette.setColor(color);
    +  assertEquals('#' + color, goog.color.parse(samplePalette.getColor()).hex);
    +}
    +
    +function testChangeEvent() {
    +  // TODO(user): Add functionality to goog.testing.events to assert
    +  // an event was fired.
    +  goog.events.listen(samplePalette, goog.ui.Component.EventType.ACTION,
    +      function() {eventWasFired = true;});
    +  samplePalette.setColor('#123456');
    +  assertTrue(eventWasFired);
    +}
    +
    +function testSetHsv() {
    +  // Start from red.
    +  samplePalette.setColor('#ff0000');
    +
    +  // Setting hue to 0.5 should yield cyan.
    +  samplePalette.setHsv(0.5, null, null);
    +  assertEquals('#00ffff', goog.color.parse(samplePalette.getColor()).hex);
    +
    +  // Setting saturation to 0 should yield white.
    +  samplePalette.setHsv(null, 0, null);
    +  assertEquals('#ffffff',
    +      goog.color.parse(samplePalette.getColor()).hex);
    +
    +  // Setting value/brightness to 0 should yield black.
    +  samplePalette.setHsv(null, null, 0);
    +  assertEquals('#000000', goog.color.parse(samplePalette.getColor()).hex);
    +}
    +
    +function testRender() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  assertTrue(samplePalette.isInDocument());
    +
    +  var elem = samplePalette.getElement();
    +  assertNotNull(elem);
    +  assertEquals(goog.dom.TagName.DIV, elem.tagName);
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
    +    assertSameElements('On IE6, the noalpha class must be present',
    +        ['goog-hsv-palette', 'goog-hsv-palette-noalpha'],
    +        goog.dom.classlist.get(elem));
    +  } else {
    +    assertEquals('The noalpha class must not be present',
    +        'goog-hsv-palette', elem.className);
    +  }
    +}
    +
    +function testSwatchTextIsReadable() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +
    +  var swatchElement = samplePalette.swatchElement;
    +
    +  // Text should be black when background is light.
    +  samplePalette.setColor('#ccffff');
    +  assertEquals('#000000',
    +      goog.color.parse(goog.style.getStyle(swatchElement,
    +      'color')).hex);
    +
    +  // Text should be white when background is dark.
    +  samplePalette.setColor('#410800');
    +  assertEquals('#ffffff',
    +      goog.color.parse(goog.style.getStyle(swatchElement,
    +      'color')).hex);
    +}
    +
    +function testInputColor() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +  var color = '#001122';
    +  samplePalette.inputElement.value = color;
    +  samplePalette.handleInput(null);
    +  assertEquals(color, goog.color.parse(samplePalette.getColor()).hex);
    +}
    +
    +function testHandleMouseMoveValue() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +  stubs.set(goog.dom, 'getPageScroll', function() {
    +    return new goog.math.Coordinate(0, 0);
    +  });
    +
    +  // Raising the value/brightness of a dark red should yield a lighter red.
    +  samplePalette.setColor('#630c00');
    +  goog.style.setPageOffset(samplePalette.valueBackgroundImageElement, 0, 0);
    +  goog.style.setSize(samplePalette.valueBackgroundImageElement, 10, 100);
    +  var boundaries = goog.style.getBounds(
    +      samplePalette.valueBackgroundImageElement, 0, 0);
    +
    +  var event = new goog.events.Event();
    +  event.clientY = -50;
    +  // TODO(user): Use
    +  // goog.testing.events.fireMouseDownEvent(
    +  //     samplePalette.valueBackgroundImageElement);
    +  // when google.testing.events support specifying properties of the event
    +  // or find out how tod o it if it already supports it.
    +  samplePalette.handleMouseMoveV_(boundaries, event);
    +  assertEquals('#ff1e00', goog.color.parse(samplePalette.getColor()).hex);
    +}
    +
    +function testHandleMouseMoveHueSaturation() {
    +  samplePalette.render(document.getElementById('sandbox'));
    +  stubs.set(goog.dom, 'getPageScroll', function() {
    +    return new goog.math.Coordinate(0, 0);
    +  });
    +
    +  // The following hue/saturation selection should yield a light yellow.
    +  goog.style.setPageOffset(samplePalette.hsImageEl_, 0, 0);
    +  goog.style.setSize(samplePalette.hsImageEl_, 100, 100);
    +  var boundaries = goog.style.getBounds(samplePalette.hsImageEl_);
    +
    +  var event = new goog.events.Event();
    +  event.clientX = 20;
    +  event.clientY = 85;
    +  // TODO(user): Use goog.testing.events when appropriate (see above).
    +  samplePalette.handleMouseMoveHs_(boundaries, event);
    +  // TODO(user): Fix the main code for this, see bug #1324469.
    +  // NOTE(gboyer): It's a little better than before due to the
    +  // goog.style getBoundingClientRect fix, but still not the same. :-(
    +  if (goog.userAgent.IE) {
    +    var expectedColor = '#ffe0b2';
    +  } else {
    +    var expectedColor = '#ffeec4';
    +  }
    +
    +  assertEquals(expectedColor,
    +      goog.color.parse(samplePalette.getColor()).hex);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/idgenerator.js b/mapbox-gl/closure-library/closure/goog/ui/idgenerator.js
    new file mode 100644
    index 0000000000..c018a3857d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/idgenerator.js
    @@ -0,0 +1,48 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generator for unique element IDs.
    + *
    + */
    +
    +goog.provide('goog.ui.IdGenerator');
    +
    +
    +
    +/**
    + * Creates a new id generator.
    + * @constructor
    + * @final
    + */
    +goog.ui.IdGenerator = function() {
    +};
    +goog.addSingletonGetter(goog.ui.IdGenerator);
    +
    +
    +/**
    + * Next unique ID to use
    + * @type {number}
    + * @private
    + */
    +goog.ui.IdGenerator.prototype.nextId_ = 0;
    +
    +
    +/**
    + * Gets the next unique ID.
    + * @return {string} The next unique identifier.
    + */
    +goog.ui.IdGenerator.prototype.getNextUniqueId = function() {
    +  return ':' + (this.nextId_++).toString(36);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/idletimer.js b/mapbox-gl/closure-library/closure/goog/ui/idletimer.js
    new file mode 100644
    index 0000000000..8053fe0d68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/idletimer.js
    @@ -0,0 +1,300 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Idle Timer.
    + *
    + * Keeps track of transitions between active and idle. This class is built on
    + * top of ActivityMonitor. Whenever an active user becomes idle, this class
    + * dispatches a BECOME_IDLE event. Whenever an idle user becomes active, this
    + * class dispatches a BECOME_ACTIVE event. The amount of inactive time it
    + * takes for a user to be considered idle is specified by the client, and
    + * different instances of this class can all use different thresholds.
    + *
    + */
    +
    +goog.provide('goog.ui.IdleTimer');
    +goog.require('goog.Timer');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.structs.Set');
    +goog.require('goog.ui.ActivityMonitor');
    +
    +
    +
    +/**
    + * Event target that will give notification of state changes between active and
    + * idle. This class is designed to require few resources while the user is
    + * active.
    + * @param {number} idleThreshold Amount of time in ms at which we consider the
    + *     user has gone idle.
    + * @param {goog.ui.ActivityMonitor=} opt_activityMonitor The activity monitor
    + *     keeping track of user interaction. Defaults to a default-constructed
    + *     activity monitor. If a default activity monitor is used then this class
    + *     will dispose of it. If an activity monitor is passed in then the caller
    + *     remains responsible for disposing of it.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @final
    + */
    +goog.ui.IdleTimer = function(idleThreshold, opt_activityMonitor) {
    +  goog.events.EventTarget.call(this);
    +
    +  var activityMonitor = opt_activityMonitor ||
    +      this.getDefaultActivityMonitor_();
    +
    +  /**
    +   * The amount of time in ms at which we consider the user has gone idle
    +   * @type {number}
    +   * @private
    +   */
    +  this.idleThreshold_ = idleThreshold;
    +
    +  /**
    +   * The activity monitor keeping track of user interaction
    +   * @type {goog.ui.ActivityMonitor}
    +   * @private
    +   */
    +  this.activityMonitor_ = activityMonitor;
    +
    +  /**
    +   * Cached onActivityTick_ bound to the object for later use
    +   * @type {Function}
    +   * @private
    +   */
    +  this.boundOnActivityTick_ = goog.bind(this.onActivityTick_, this);
    +
    +  // Decide whether the user is currently active or idle. This method will
    +  // check whether it is correct to start with the user in the active state.
    +  this.maybeStillActive_();
    +};
    +goog.inherits(goog.ui.IdleTimer, goog.events.EventTarget);
    +
    +
    +/**
    + * Whether a listener is currently registered for an idle timer event. On
    + * initialization, the user is assumed to be active.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.hasActivityListener_ = false;
    +
    +
    +/**
    + * Handle to the timer ID used for checking ongoing activity, or null
    + * @type {?number}
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.onActivityTimerId_ = null;
    +
    +
    +/**
    + * Whether the user is currently idle
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.isIdle_ = false;
    +
    +
    +/**
    + * The default activity monitor created by this class, if any
    + * @type {goog.ui.ActivityMonitor?}
    + * @private
    + */
    +goog.ui.IdleTimer.defaultActivityMonitor_ = null;
    +
    +
    +/**
    + * The idle timers that currently reference the default activity monitor
    + * @type {goog.structs.Set}
    + * @private
    + */
    +goog.ui.IdleTimer.defaultActivityMonitorReferences_ = new goog.structs.Set();
    +
    +
    +/**
    + * Event constants for the idle timer event target
    + * @enum {string}
    + */
    +goog.ui.IdleTimer.Event = {
    +  /** Event fired when an idle user transitions into the active state */
    +  BECOME_ACTIVE: 'active',
    +  /** Event fired when an active user transitions into the idle state */
    +  BECOME_IDLE: 'idle'
    +};
    +
    +
    +/**
    + * Gets the default activity monitor used by this class. If a default has not
    + * been created yet, then a new one will be created.
    + * @return {!goog.ui.ActivityMonitor} The default activity monitor.
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.getDefaultActivityMonitor_ = function() {
    +  goog.ui.IdleTimer.defaultActivityMonitorReferences_.add(this);
    +  if (goog.ui.IdleTimer.defaultActivityMonitor_ == null) {
    +    goog.ui.IdleTimer.defaultActivityMonitor_ = new goog.ui.ActivityMonitor();
    +  }
    +  return goog.ui.IdleTimer.defaultActivityMonitor_;
    +};
    +
    +
    +/**
    + * Removes the reference to the default activity monitor. If there are no more
    + * references then the default activity monitor gets disposed.
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.maybeDisposeDefaultActivityMonitor_ = function() {
    +  goog.ui.IdleTimer.defaultActivityMonitorReferences_.remove(this);
    +  if (goog.ui.IdleTimer.defaultActivityMonitor_ != null &&
    +      goog.ui.IdleTimer.defaultActivityMonitorReferences_.isEmpty()) {
    +    goog.ui.IdleTimer.defaultActivityMonitor_.dispose();
    +    goog.ui.IdleTimer.defaultActivityMonitor_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Checks whether the user is active. If the user is still active, then a timer
    + * is started to check again later.
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.maybeStillActive_ = function() {
    +  // See how long before the user would go idle. The user is considered idle
    +  // after the idle time has passed, not exactly when the idle time arrives.
    +  var remainingIdleThreshold = this.idleThreshold_ + 1 -
    +      (goog.now() - this.activityMonitor_.getLastEventTime());
    +  if (remainingIdleThreshold > 0) {
    +    // The user is still active. Check again later.
    +    this.onActivityTimerId_ = goog.Timer.callOnce(
    +        this.boundOnActivityTick_, remainingIdleThreshold);
    +  } else {
    +    // The user has not been active recently.
    +    this.becomeIdle_();
    +  }
    +};
    +
    +
    +/**
    + * Handler for the timeout used for checking ongoing activity
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.onActivityTick_ = function() {
    +  // The timer has fired.
    +  this.onActivityTimerId_ = null;
    +
    +  // The maybeStillActive method will restart the timer, if appropriate.
    +  this.maybeStillActive_();
    +};
    +
    +
    +/**
    + * Transitions from the active state to the idle state
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.becomeIdle_ = function() {
    +  this.isIdle_ = true;
    +
    +  // The idle timer will send notification when the user does something
    +  // interactive.
    +  goog.events.listen(this.activityMonitor_,
    +      goog.ui.ActivityMonitor.Event.ACTIVITY,
    +      this.onActivity_, false, this);
    +  this.hasActivityListener_ = true;
    +
    +  // Notify clients of the state change.
    +  this.dispatchEvent(goog.ui.IdleTimer.Event.BECOME_IDLE);
    +};
    +
    +
    +/**
    + * Handler for idle timer events when the user does something interactive
    + * @param {goog.events.Event} e The event object.
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.onActivity_ = function(e) {
    +  this.becomeActive_();
    +};
    +
    +
    +/**
    + * Transitions from the idle state to the active state
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.becomeActive_ = function() {
    +  this.isIdle_ = false;
    +
    +  // Stop listening to every interactive event.
    +  this.removeActivityListener_();
    +
    +  // Notify clients of the state change.
    +  this.dispatchEvent(goog.ui.IdleTimer.Event.BECOME_ACTIVE);
    +
    +  // Periodically check whether the user has gone inactive.
    +  this.maybeStillActive_();
    +};
    +
    +
    +/**
    + * Removes the activity listener, if necessary
    + * @private
    + */
    +goog.ui.IdleTimer.prototype.removeActivityListener_ = function() {
    +  if (this.hasActivityListener_) {
    +    goog.events.unlisten(this.activityMonitor_,
    +        goog.ui.ActivityMonitor.Event.ACTIVITY,
    +        this.onActivity_, false, this);
    +    this.hasActivityListener_ = false;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.IdleTimer.prototype.disposeInternal = function() {
    +  this.removeActivityListener_();
    +  if (this.onActivityTimerId_ != null) {
    +    goog.global.clearTimeout(this.onActivityTimerId_);
    +    this.onActivityTimerId_ = null;
    +  }
    +  this.maybeDisposeDefaultActivityMonitor_();
    +  goog.ui.IdleTimer.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * @return {number} the amount of time at which we consider the user has gone
    + *     idle in ms.
    + */
    +goog.ui.IdleTimer.prototype.getIdleThreshold = function() {
    +  return this.idleThreshold_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.ActivityMonitor} the activity monitor keeping track of user
    + *     interaction.
    + */
    +goog.ui.IdleTimer.prototype.getActivityMonitor = function() {
    +  return this.activityMonitor_;
    +};
    +
    +
    +/**
    + * Returns true if there has been no user action for at least the specified
    + * interval, and false otherwise
    + * @return {boolean} true if the user is idle, false otherwise.
    + */
    +goog.ui.IdleTimer.prototype.isIdle = function() {
    +  return this.isIdle_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.html b/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.html
    new file mode 100644
    index 0000000000..d399e995d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.IdleTimer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.IdleTimerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.js b/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.js
    new file mode 100644
    index 0000000000..35d10ad1c8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/idletimer_test.js
    @@ -0,0 +1,97 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.IdleTimerTest');
    +goog.setTestOnly('goog.ui.IdleTimerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.IdleTimer');
    +goog.require('goog.ui.MockActivityMonitor');
    +
    +var clock;
    +
    +function setUp() {
    +  clock = new goog.testing.MockClock(true);
    +  goog.now = goog.bind(clock.getCurrentTime, clock);
    +}
    +
    +function tearDown() {
    +  clock.dispose();
    +}
    +
    +
    +/**
    + * Tests whether an event is fired when the user becomes idle
    + */
    +function testBecomeIdle() {
    +  var idleThreshold = 1000;
    +  var mockActivityMonitor = new goog.ui.MockActivityMonitor();
    +  var idleTimer = new goog.ui.IdleTimer(idleThreshold, mockActivityMonitor);
    +
    +  mockActivityMonitor.simulateEvent();
    +  assertFalse('Precondition: user should be active', idleTimer.isIdle());
    +
    +  var onBecomeIdleCount = 0;
    +  var onBecomeIdle = function() {
    +    onBecomeIdleCount += 1;
    +  };
    +  goog.events.listen(idleTimer,
    +      goog.ui.IdleTimer.Event.BECOME_IDLE,
    +      onBecomeIdle);
    +
    +  clock.tick(idleThreshold);
    +  mockActivityMonitor.simulateEvent();
    +  clock.tick(idleThreshold);
    +  assert('The BECOME_IDLE event fired too early', onBecomeIdleCount == 0);
    +  assertFalse('The user should still be active', idleTimer.isIdle());
    +
    +  clock.tick(1);
    +  assert('The BECOME_IDLE event fired too late', onBecomeIdleCount == 1);
    +  assert('The user should be idle', idleTimer.isIdle());
    +
    +  idleTimer.dispose();
    +}
    +
    +
    +/**
    + * Tests whether an event is fired when the user becomes active
    + */
    +function testBecomeActive() {
    +  var idleThreshold = 1000;
    +  var mockActivityMonitor = new goog.ui.MockActivityMonitor();
    +  var idleTimer = new goog.ui.IdleTimer(idleThreshold, mockActivityMonitor);
    +
    +  clock.tick(idleThreshold + 1);
    +  assert('Precondition: user should be idle', idleTimer.isIdle());
    +
    +  var onBecomeActiveCount = 0;
    +  var onBecomeActive = function() {
    +    onBecomeActiveCount += 1;
    +  };
    +  goog.events.listen(idleTimer,
    +      goog.ui.IdleTimer.Event.BECOME_ACTIVE,
    +      onBecomeActive);
    +
    +  clock.tick(idleThreshold);
    +  assert('The BECOME_ACTIVE event fired too early', onBecomeActiveCount == 0);
    +  assert('The user should still be idle', idleTimer.isIdle());
    +
    +  mockActivityMonitor.simulateEvent();
    +  assert('The BECOME_ACTIVE event fired too late', onBecomeActiveCount == 1);
    +  assertFalse('The user should be active', idleTimer.isIdle());
    +
    +  idleTimer.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/iframemask.js b/mapbox-gl/closure-library/closure/goog/ui/iframemask.js
    new file mode 100644
    index 0000000000..48ac98edfd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/iframemask.js
    @@ -0,0 +1,258 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Iframe shims, to protect controls on the underlying page
    + * from bleeding through popups.
    + *
    + * @author gboyer@google.com (Garrett Boyer)
    + * @author nicksantos@google.com (Nick Santos) (Ported to Closure)
    + */
    +
    +
    +goog.provide('goog.ui.IframeMask');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.dom.iframe');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Controller for an iframe mask. The mask is only valid in the current
    + * document, or else the document of the given DOM helper.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper for the relevant
    + *     document.
    + * @param {goog.structs.Pool=} opt_iframePool An optional source of iframes.
    + *     Iframes will be grabbed from the pool when they're needed and returned
    + *     to the pool (but still attached to the DOM) when they're done.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.ui.IframeMask = function(opt_domHelper, opt_iframePool) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * The DOM helper for this document.
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * An Element to snap the mask to. If none is given, defaults to
    +   * a full-screen iframe mask.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.snapElement_ = this.dom_.getDocument().documentElement;
    +
    +  /**
    +   * An event handler for listening to popups and the like.
    +   * @type {goog.events.EventHandler<!goog.ui.IframeMask>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * An iframe pool.
    +   * @type {goog.structs.Pool|undefined}
    +   * @private
    +   */
    +  this.iframePool_ = opt_iframePool;
    +};
    +goog.inherits(goog.ui.IframeMask, goog.Disposable);
    +goog.tagUnsealableClass(goog.ui.IframeMask);
    +
    +
    +/**
    + * An iframe.
    + * @type {HTMLIFrameElement}
    + * @private
    + */
    +goog.ui.IframeMask.prototype.iframe_;
    +
    +
    +/**
    + * The z-index of the iframe mask.
    + * @type {number}
    + * @private
    + */
    +goog.ui.IframeMask.prototype.zIndex_ = 1;
    +
    +
    +/**
    + * The opacity of the iframe mask, expressed as a value between 0 and 1, with
    + * 1 being totally opaque.
    + * @type {number}
    + * @private
    + */
    +goog.ui.IframeMask.prototype.opacity_ = 0;
    +
    +
    +/**
    + * Removes the iframe from the DOM.
    + * @override
    + * @protected
    + */
    +goog.ui.IframeMask.prototype.disposeInternal = function() {
    +  if (this.iframePool_) {
    +    this.iframePool_.releaseObject(
    +        /** @type {HTMLIFrameElement} */ (this.iframe_));
    +  } else {
    +    goog.dom.removeNode(this.iframe_);
    +  }
    +  this.iframe_ = null;
    +
    +  this.handler_.dispose();
    +  this.handler_ = null;
    +
    +  goog.ui.IframeMask.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * CSS for a hidden iframe.
    + * @type {string}
    + * @private
    + */
    +goog.ui.IframeMask.HIDDEN_CSS_TEXT_ =
    +    'position:absolute;display:none;z-index:1';
    +
    +
    +/**
    + * Removes the mask from the screen.
    + */
    +goog.ui.IframeMask.prototype.hideMask = function() {
    +  if (this.iframe_) {
    +    this.iframe_.style.cssText = goog.ui.IframeMask.HIDDEN_CSS_TEXT_;
    +    if (this.iframePool_) {
    +      this.iframePool_.releaseObject(this.iframe_);
    +      this.iframe_ = null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the iframe to use as a mask. Creates a new one if one has not been
    + * created yet.
    + * @return {!HTMLIFrameElement} The iframe.
    + * @private
    + */
    +goog.ui.IframeMask.prototype.getIframe_ = function() {
    +  if (!this.iframe_) {
    +    this.iframe_ = this.iframePool_ ?
    +        /** @type {HTMLIFrameElement} */ (this.iframePool_.getObject()) :
    +        goog.dom.iframe.createBlank(this.dom_);
    +    this.iframe_.style.cssText = goog.ui.IframeMask.HIDDEN_CSS_TEXT_;
    +    this.dom_.getDocument().body.appendChild(this.iframe_);
    +  }
    +  return this.iframe_;
    +};
    +
    +
    +/**
    + * Applies the iframe mask to the screen.
    + */
    +goog.ui.IframeMask.prototype.applyMask = function() {
    +  var iframe = this.getIframe_();
    +  var bounds = goog.style.getBounds(this.snapElement_);
    +  iframe.style.cssText =
    +      'position:absolute;' +
    +      'left:' + bounds.left + 'px;' +
    +      'top:' + bounds.top + 'px;' +
    +      'width:' + bounds.width + 'px;' +
    +      'height:' + bounds.height + 'px;' +
    +      'z-index:' + this.zIndex_;
    +  goog.style.setOpacity(iframe, this.opacity_);
    +  iframe.style.display = 'block';
    +};
    +
    +
    +/**
    + * Sets the opacity of the mask. Will take effect the next time the mask
    + * is applied.
    + * @param {number} opacity A value between 0 and 1, with 1 being
    + *     totally opaque.
    + */
    +goog.ui.IframeMask.prototype.setOpacity = function(opacity) {
    +  this.opacity_ = opacity;
    +};
    +
    +
    +/**
    + * Sets the z-index of the mask. Will take effect the next time the mask
    + * is applied.
    + * @param {number} zIndex A z-index value.
    + */
    +goog.ui.IframeMask.prototype.setZIndex = function(zIndex) {
    +  this.zIndex_ = zIndex;
    +};
    +
    +
    +/**
    + * Sets the element to use as the bounds of the mask. Takes effect immediately.
    + * @param {Element} snapElement The snap element, which the iframe will be
    + *     "snapped" around.
    + */
    +goog.ui.IframeMask.prototype.setSnapElement = function(snapElement) {
    +  this.snapElement_ = snapElement;
    +  if (this.iframe_ && goog.style.isElementShown(this.iframe_)) {
    +    this.applyMask();
    +  }
    +};
    +
    +
    +/**
    + * Listens on the specified target, hiding and showing the iframe mask
    + * when the given event types are dispatched.
    + * @param {goog.events.EventTarget} target The event target to listen on.
    + * @param {string} showEvent When this event fires, the mask will be applied.
    + * @param {string} hideEvent When this event fires, the mask will be hidden.
    + * @param {Element=} opt_snapElement When the mask is applied, it will
    + *     automatically snap to this element. If no element is specified, it will
    + *     use the default snap element.
    + */
    +goog.ui.IframeMask.prototype.listenOnTarget = function(target, showEvent,
    +    hideEvent, opt_snapElement) {
    +  var timerKey;
    +  this.handler_.listen(target, showEvent, function() {
    +    if (opt_snapElement) {
    +      this.setSnapElement(opt_snapElement);
    +    }
    +    // Check out the iframe asynchronously, so we don't block the SHOW
    +    // event and cause a bounce.
    +    timerKey = goog.Timer.callOnce(this.applyMask, 0, this);
    +  });
    +  this.handler_.listen(target, hideEvent, function() {
    +    if (timerKey) {
    +      goog.Timer.clear(timerKey);
    +      timerKey = null;
    +    }
    +    this.hideMask();
    +  });
    +};
    +
    +
    +/**
    + * Removes all handlers attached by listenOnTarget.
    + */
    +goog.ui.IframeMask.prototype.removeHandlers = function() {
    +  this.handler_.removeAll();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.html b/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.html
    new file mode 100644
    index 0000000000..98b6f4d60c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.html
    @@ -0,0 +1,38 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author gboyer@google.com (Garrett Boyer)
    +  @author nicksantos@google.com (Nick Santos) (Ported to Closure)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   goog.ui.IframeMask Unit Test
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.IframeMaskTest');
    +  </script>
    +  <style type="text/css">
    +   #popup {
    +  position: absolute;
    +  left: 100px;
    +  top: 900px; /* so that you can see unit test failures */
    +  width: 300px;
    +  height: 400px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.js b/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.js
    new file mode 100644
    index 0000000000..4dd24b3dfc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/iframemask_test.js
    @@ -0,0 +1,214 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.IframeMaskTest');
    +goog.setTestOnly('goog.ui.IframeMaskTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.iframe');
    +goog.require('goog.structs.Pool');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.IframeMask');
    +goog.require('goog.ui.Popup');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.userAgent');
    +
    +var iframeMask;
    +var mockClock;
    +
    +function setUp() {
    +  goog.dom.getElement('sandbox').innerHTML = '<div id="popup"></div>';
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  iframeMask = new goog.ui.IframeMask();
    +}
    +
    +function tearDown() {
    +  iframeMask.dispose();
    +  mockClock.dispose();
    +
    +  assertNoIframes();
    +}
    +
    +function findOneAndOnlyIframe() {
    +  var iframes = document.getElementsByTagName(goog.dom.TagName.IFRAME);
    +  assertEquals('There should be exactly 1 iframe in the document',
    +      1, iframes.length);
    +  return iframes[0];
    +}
    +
    +function assertNoIframes() {
    +  assertEquals('Expected no iframes in the document', 0,
    +      goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.IFRAME).length);
    +}
    +
    +function testApplyFullScreenMask() {
    +  iframeMask.applyMask();
    +
    +  var iframe = findOneAndOnlyIframe();
    +  assertEquals('block', iframe.style.display);
    +  assertEquals('absolute', iframe.style.position);
    +
    +  // coerce zindex to a string
    +  assertEquals('1', iframe.style.zIndex + '');
    +
    +  iframeMask.hideMask();
    +  assertEquals('none', iframe.style.display);
    +}
    +
    +function testApplyOpacity() {
    +  iframeMask.setOpacity(0.3);
    +  iframeMask.applyMask();
    +
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    assertContains('Expected opactity to be set in the CSS style',
    +        '30', findOneAndOnlyIframe().style.cssText);
    +  } else {
    +    assertContains('Expected opactity to be set in the CSS style',
    +        '0.3', findOneAndOnlyIframe().style.cssText);
    +  }
    +}
    +
    +function testApplyZIndex() {
    +  iframeMask.setZIndex(5);
    +  iframeMask.applyMask();
    +
    +  // coerce zindex to a string
    +  assertEquals('5', findOneAndOnlyIframe().style.zIndex + '');
    +}
    +
    +function testSnapElement() {
    +  iframeMask.setSnapElement(goog.dom.getElement('popup'));
    +  iframeMask.applyMask();
    +
    +  var iframe = findOneAndOnlyIframe();
    +  var bounds = goog.style.getBounds(iframe);
    +  assertEquals(100, bounds.left);
    +  assertEquals(900, bounds.top);
    +  assertEquals(300, bounds.width);
    +  assertEquals(400, bounds.height);
    +
    +  iframeMask.setSnapElement(document.documentElement);
    +
    +  // Make sure that snapping to a different element changes the bounds.
    +  assertNotEquals('Snap element not updated',
    +      400, goog.style.getBounds(iframe).height);
    +}
    +
    +function testAttachToPopup() {
    +  var popup = new goog.ui.Popup(goog.dom.getElement('popup'));
    +  iframeMask.listenOnTarget(popup, goog.ui.PopupBase.EventType.SHOW,
    +      goog.ui.PopupBase.EventType.HIDE, goog.dom.getElement('popup'));
    +
    +  assertNoIframes();
    +  popup.setVisible(true);
    +  assertNoIframes();
    +
    +  // Tick because the showing of the iframe mask happens asynchronously.
    +  // (Otherwise the handling of the mousedown can take so long that a bounce
    +  // occurs).
    +  mockClock.tick(1);
    +
    +  var iframe = findOneAndOnlyIframe();
    +  var bounds = goog.style.getBounds(iframe);
    +  assertEquals(300, bounds.width);
    +  assertEquals(400, bounds.height);
    +  assertEquals('block', iframe.style.display);
    +
    +  popup.setVisible(false);
    +  assertEquals('none', iframe.style.display);
    +}
    +
    +function testQuickHidingPopup() {
    +  var popup = new goog.ui.Popup(goog.dom.getElement('popup'));
    +  iframeMask.listenOnTarget(popup, goog.ui.PopupBase.EventType.SHOW,
    +      goog.ui.PopupBase.EventType.HIDE);
    +
    +  assertNoIframes();
    +  popup.setVisible(true);
    +  assertNoIframes();
    +  popup.setVisible(false);
    +  assertNoIframes();
    +
    +  // Tick because the showing of the iframe mask happens asynchronously.
    +  // (Otherwise the handling of the mousedown can take so long that a bounce
    +  // occurs).
    +  mockClock.tick(1);
    +  assertNoIframes();
    +}
    +
    +function testRemoveHandlers() {
    +  var popup = new goog.ui.Popup(goog.dom.getElement('popup'));
    +  iframeMask.listenOnTarget(popup, goog.ui.PopupBase.EventType.SHOW,
    +      goog.ui.PopupBase.EventType.HIDE);
    +  iframeMask.removeHandlers();
    +  popup.setVisible(true);
    +
    +  // Tick because the showing of the iframe mask happens asynchronously.
    +  // (Otherwise the handling of the mousedown can take so long that a bounce
    +  // occurs).
    +  mockClock.tick(1);
    +  assertNoIframes();
    +}
    +
    +function testIframePool() {
    +  var iframe = goog.dom.iframe.createBlank(goog.dom.getDomHelper());
    +  var mockPool = new goog.testing.StrictMock(goog.structs.Pool);
    +  mockPool.getObject();
    +  mockPool.$returns(iframe);
    +
    +  mockPool.$replay();
    +
    +  iframeMask.dispose();
    +
    +  // Create a new iframe mask with a pool, and verify that it checks
    +  // its iframe out of the pool instead of creating one.
    +  iframeMask = new goog.ui.IframeMask(null, mockPool);
    +  iframeMask.applyMask();
    +  mockPool.$verify();
    +  findOneAndOnlyIframe();
    +
    +  mockPool.$reset();
    +
    +  mockPool.releaseObject(iframe);
    +  mockPool.$replay();
    +
    +  // When the iframe mask has a pool, the pool is responsible for
    +  // removing the iframe from the DOM.
    +  iframeMask.hideMask();
    +  mockPool.$verify();
    +  findOneAndOnlyIframe();
    +
    +  // And showing the iframe again should check it out of the pool again.
    +  mockPool.$reset();
    +  mockPool.getObject();
    +  mockPool.$returns(iframe);
    +  mockPool.$replay();
    +
    +  iframeMask.applyMask();
    +  mockPool.$verify();
    +
    +  // When the test is over, the iframe mask should be disposed. Make sure
    +  // that the pool removes the iframe from the page.
    +  mockPool.$reset();
    +  mockPool.releaseObject(iframe);
    +  mockPool.$does(function() {
    +    goog.dom.removeNode(iframe);
    +  });
    +  mockPool.$replay();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/imagelessbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/imagelessbuttonrenderer.js
    new file mode 100644
    index 0000000000..d4c36d0bbd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/imagelessbuttonrenderer.js
    @@ -0,0 +1,207 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An alternative custom button renderer that uses even more CSS
    + * voodoo than the default implementation to render custom buttons with fake
    + * rounded corners and dimensionality (via a subtle flat shadow on the bottom
    + * half of the button) without the use of images.
    + *
    + * Based on the Custom Buttons 3.1 visual specification, see
    + * http://go/custombuttons
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/imagelessbutton.html
    + */
    +
    +goog.provide('goog.ui.ImagelessButtonRenderer');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.Button}s. Imageless buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + *
    + * @deprecated These contain a lot of unnecessary DOM for modern user agents.
    + *     Please use a simpler button renderer like css3buttonrenderer.
    + * @constructor
    + * @extends {goog.ui.CustomButtonRenderer}
    + */
    +goog.ui.ImagelessButtonRenderer = function() {
    +  goog.ui.CustomButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ImagelessButtonRenderer, goog.ui.CustomButtonRenderer);
    +
    +
    +/**
    + * The singleton instance of this renderer class.
    + * @type {goog.ui.ImagelessButtonRenderer?}
    + * @private
    + */
    +goog.ui.ImagelessButtonRenderer.instance_ = null;
    +goog.addSingletonGetter(goog.ui.ImagelessButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ImagelessButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-imageless-button');
    +
    +
    +/**
    + * Returns the button's contents wrapped in the following DOM structure:
    + *    <div class="goog-inline-block goog-imageless-button">
    + *      <div class="goog-inline-block goog-imageless-button-outer-box">
    + *        <div class="goog-imageless-button-inner-box">
    + *          <div class="goog-imageless-button-pos-box">
    + *            <div class="goog-imageless-button-top-shadow">&nbsp;</div>
    + *            <div class="goog-imageless-button-content">Contents...</div>
    + *          </div>
    + *        </div>
    + *      </div>
    + *    </div>
    + * @override
    + */
    +goog.ui.ImagelessButtonRenderer.prototype.createDom;
    +
    +
    +/** @override */
    +goog.ui.ImagelessButtonRenderer.prototype.getContentElement = function(
    +    element) {
    +  return /** @type {Element} */ (element && element.firstChild &&
    +      element.firstChild.firstChild &&
    +      element.firstChild.firstChild.firstChild.lastChild);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content
    + * wrapped in a pseudo-rounded-corner box.  Creates the following DOM structure:
    + *  <div class="goog-inline-block goog-imageless-button-outer-box">
    + *    <div class="goog-inline-block goog-imageless-button-inner-box">
    + *      <div class="goog-imageless-button-pos">
    + *        <div class="goog-imageless-button-top-shadow">&nbsp;</div>
    + *        <div class="goog-imageless-button-content">Contents...</div>
    + *      </div>
    + *    </div>
    + *  </div>
    + * Used by both {@link #createDom} and {@link #decorate}.  To be overridden
    + * by subclasses.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.ImagelessButtonRenderer.prototype.createButton = function(content,
    +                                                                  dom) {
    +  var baseClass = this.getCssClass();
    +  var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' ';
    +  return dom.createDom('div',
    +      inlineBlock + goog.getCssName(baseClass, 'outer-box'),
    +      dom.createDom('div',
    +          inlineBlock + goog.getCssName(baseClass, 'inner-box'),
    +          dom.createDom('div', goog.getCssName(baseClass, 'pos'),
    +              dom.createDom('div', goog.getCssName(baseClass, 'top-shadow'),
    +                  '\u00A0'),
    +              dom.createDom('div', goog.getCssName(baseClass, 'content'),
    +                  content))));
    +};
    +
    +
    +/**
    + * Check if the button's element has a box structure.
    + * @param {goog.ui.Button} button Button instance whose structure is being
    + *     checked.
    + * @param {Element} element Element of the button.
    + * @return {boolean} Whether the element has a box structure.
    + * @protected
    + * @override
    + */
    +goog.ui.ImagelessButtonRenderer.prototype.hasBoxStructure = function(
    +    button, element) {
    +  var outer = button.getDomHelper().getFirstElementChild(element);
    +  var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box');
    +  if (outer && goog.dom.classlist.contains(outer, outerClassName)) {
    +
    +    var inner = button.getDomHelper().getFirstElementChild(outer);
    +    var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box');
    +    if (inner && goog.dom.classlist.contains(inner, innerClassName)) {
    +
    +      var pos = button.getDomHelper().getFirstElementChild(inner);
    +      var posClassName = goog.getCssName(this.getCssClass(), 'pos');
    +      if (pos && goog.dom.classlist.contains(pos, posClassName)) {
    +
    +        var shadow = button.getDomHelper().getFirstElementChild(pos);
    +        var shadowClassName = goog.getCssName(
    +            this.getCssClass(), 'top-shadow');
    +        if (shadow && goog.dom.classlist.contains(shadow, shadowClassName)) {
    +
    +          var content = button.getDomHelper().getNextElementSibling(shadow);
    +          var contentClassName = goog.getCssName(
    +              this.getCssClass(), 'content');
    +          if (content &&
    +              goog.dom.classlist.contains(content, contentClassName)) {
    +            // We have a proper box structure.
    +            return true;
    +          }
    +        }
    +      }
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ImagelessButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ImagelessButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.ImagelessButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.ImagelessButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Button(null,
    +          goog.ui.ImagelessButtonRenderer.getInstance());
    +    });
    +
    +
    +// Register a decorator factory function for toggle buttons using the
    +// goog.ui.ImagelessButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-imageless-toggle-button'),
    +    function() {
    +      var button = new goog.ui.Button(null,
    +          goog.ui.ImagelessButtonRenderer.getInstance());
    +      button.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +      return button;
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/imagelessmenubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/imagelessmenubuttonrenderer.js
    new file mode 100644
    index 0000000000..1ad27d66e0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/imagelessmenubuttonrenderer.js
    @@ -0,0 +1,210 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview An alternative custom button renderer that uses even more CSS
    + * voodoo than the default implementation to render custom buttons with fake
    + * rounded corners and dimensionality (via a subtle flat shadow on the bottom
    + * half of the button) without the use of images.
    + *
    + * Based on the Custom Buttons 3.1 visual specification, see
    + * http://go/custombuttons
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/imagelessmenubutton.html
    + */
    +
    +goog.provide('goog.ui.ImagelessMenuButtonRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.MenuButton}s. Imageless buttons can
    + * contain almost arbitrary HTML content, will flow like inline elements, but
    + * can be styled like block-level elements.
    + *
    + * @deprecated These contain a lot of unnecessary DOM for modern user agents.
    + *     Please use a simpler button renderer like css3buttonrenderer.
    + * @constructor
    + * @extends {goog.ui.MenuButtonRenderer}
    + * @final
    + */
    +goog.ui.ImagelessMenuButtonRenderer = function() {
    +  goog.ui.MenuButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ImagelessMenuButtonRenderer, goog.ui.MenuButtonRenderer);
    +
    +
    +/**
    + * The singleton instance of this renderer class.
    + * @type {goog.ui.ImagelessMenuButtonRenderer?}
    + * @private
    + */
    +goog.ui.ImagelessMenuButtonRenderer.instance_ = null;
    +goog.addSingletonGetter(goog.ui.ImagelessMenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ImagelessMenuButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-imageless-button');
    +
    +
    +/** @override */
    +goog.ui.ImagelessMenuButtonRenderer.prototype.getContentElement = function(
    +    element) {
    +  if (element) {
    +    var captionElem = goog.dom.getElementsByTagNameAndClass(
    +        '*', goog.getCssName(this.getCssClass(), 'caption'), element)[0];
    +    return captionElem;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns true if this renderer can decorate the element.  Overrides
    + * {@link goog.ui.MenuButtonRenderer#canDecorate} by returning true if the
    + * element is a DIV, false otherwise.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.ImagelessMenuButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == goog.dom.TagName.DIV;
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content
    + * wrapped in a pseudo-rounded-corner box.  Creates the following DOM structure:
    + *  <div class="goog-inline-block goog-imageless-button">
    + *    <div class="goog-inline-block goog-imageless-button-outer-box">
    + *      <div class="goog-imageless-button-inner-box">
    + *        <div class="goog-imageless-button-pos-box">
    + *          <div class="goog-imageless-button-top-shadow">&nbsp;</div>
    + *          <div class="goog-imageless-button-content
    + *                      goog-imageless-menubutton-caption">Contents...
    + *          </div>
    + *          <div class="goog-imageless-menubutton-dropdown"></div>
    + *        </div>
    + *      </div>
    + *    </div>
    + *  </div>
    +
    + * Used by both {@link #createDom} and {@link #decorate}.  To be overridden
    + * by subclasses.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.ImagelessMenuButtonRenderer.prototype.createButton = function(content,
    +                                                                      dom) {
    +  var baseClass = this.getCssClass();
    +  var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' ';
    +  return dom.createDom('div',
    +      inlineBlock + goog.getCssName(baseClass, 'outer-box'),
    +      dom.createDom('div',
    +          inlineBlock + goog.getCssName(baseClass, 'inner-box'),
    +          dom.createDom('div', goog.getCssName(baseClass, 'pos'),
    +              dom.createDom('div', goog.getCssName(baseClass, 'top-shadow'),
    +                  '\u00A0'),
    +              dom.createDom('div', [goog.getCssName(baseClass, 'content'),
    +                                    goog.getCssName(baseClass, 'caption'),
    +                                    goog.getCssName('goog-inline-block')],
    +                            content),
    +              dom.createDom('div', [goog.getCssName(baseClass, 'dropdown'),
    +                                    goog.getCssName('goog-inline-block')]))));
    +};
    +
    +
    +/**
    + * Check if the button's element has a box structure.
    + * @param {goog.ui.Button} button Button instance whose structure is being
    + *     checked.
    + * @param {Element} element Element of the button.
    + * @return {boolean} Whether the element has a box structure.
    + * @protected
    + * @override
    + */
    +goog.ui.ImagelessMenuButtonRenderer.prototype.hasBoxStructure = function(
    +    button, element) {
    +  var outer = button.getDomHelper().getFirstElementChild(element);
    +  var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box');
    +  if (outer && goog.dom.classlist.contains(outer, outerClassName)) {
    +
    +    var inner = button.getDomHelper().getFirstElementChild(outer);
    +    var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box');
    +    if (inner && goog.dom.classlist.contains(inner, innerClassName)) {
    +
    +      var pos = button.getDomHelper().getFirstElementChild(inner);
    +      var posClassName = goog.getCssName(this.getCssClass(), 'pos');
    +      if (pos && goog.dom.classlist.contains(pos, posClassName)) {
    +
    +        var shadow = button.getDomHelper().getFirstElementChild(pos);
    +        var shadowClassName = goog.getCssName(
    +            this.getCssClass(), 'top-shadow');
    +        if (shadow && goog.dom.classlist.contains(shadow, shadowClassName)) {
    +
    +          var content = button.getDomHelper().getNextElementSibling(shadow);
    +          var contentClassName = goog.getCssName(
    +              this.getCssClass(), 'content');
    +          if (content &&
    +              goog.dom.classlist.contains(content, contentClassName)) {
    +            // We have a proper box structure.
    +            return true;
    +          }
    +        }
    +      }
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ImagelessMenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ImagelessMenuButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for
    +// goog.ui.ImagelessMenuButtonRenderer. Since we're using goog-imageless-button
    +// as the base class in order to get the same styling as
    +// goog.ui.ImagelessButtonRenderer, we need to be explicit about giving
    +// goog-imageless-menu-button here.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-imageless-menu-button'),
    +    function() {
    +      return new goog.ui.MenuButton(null, null,
    +          goog.ui.ImagelessMenuButtonRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker.js b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker.js
    new file mode 100644
    index 0000000000..1613c79767
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker.js
    @@ -0,0 +1,339 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Input Date Picker implementation.  Pairs a
    + * goog.ui.PopupDatePicker with an input element and handles the input from
    + * either.
    + *
    + * @see ../demos/inputdatepicker.html
    + */
    +
    +goog.provide('goog.ui.InputDatePicker');
    +
    +goog.require('goog.date.DateTime');
    +goog.require('goog.dom');
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.DatePicker');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.ui.PopupDatePicker');
    +
    +
    +
    +/**
    + * Input date picker widget.
    + *
    + * @param {goog.i18n.DateTimeFormat} dateTimeFormatter A formatter instance
    + *     used to format the date picker's date for display in the input element.
    + * @param {goog.i18n.DateTimeParse} dateTimeParser A parser instance used to
    + *     parse the input element's string as a date to set the picker.
    + * @param {goog.ui.DatePicker=} opt_datePicker Optional DatePicker.  This
    + *     enables the use of a custom date-picker instance.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.InputDatePicker = function(
    +    dateTimeFormatter, dateTimeParser, opt_datePicker, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.dateTimeFormatter_ = dateTimeFormatter;
    +  this.dateTimeParser_ = dateTimeParser;
    +
    +  this.popupDatePicker_ = new goog.ui.PopupDatePicker(
    +      opt_datePicker, opt_domHelper);
    +  this.addChild(this.popupDatePicker_);
    +  this.popupDatePicker_.setAllowAutoFocus(false);
    +};
    +goog.inherits(goog.ui.InputDatePicker, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.InputDatePicker);
    +
    +
    +/**
    + * Used to format the date picker's date for display in the input element.
    + * @type {goog.i18n.DateTimeFormat}
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.dateTimeFormatter_ = null;
    +
    +
    +/**
    + * Used to parse the input element's string as a date to set the picker.
    + * @type {goog.i18n.DateTimeParse}
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.dateTimeParser_ = null;
    +
    +
    +/**
    + * The instance of goog.ui.PopupDatePicker used to pop up and select the date.
    + * @type {goog.ui.PopupDatePicker}
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.popupDatePicker_ = null;
    +
    +
    +/**
    + * The element that the PopupDatePicker should be parented to. Defaults to the
    + * body element of the page.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.popupParentElement_ = null;
    +
    +
    +/**
    + * Returns the PopupDatePicker's internal DatePicker instance.  This can be
    + * used to customize the date picker's styling.
    + *
    + * @return {goog.ui.DatePicker} The internal DatePicker instance.
    + */
    +goog.ui.InputDatePicker.prototype.getDatePicker = function() {
    +  return this.popupDatePicker_.getDatePicker();
    +};
    +
    +
    +/**
    + * Returns the PopupDatePicker instance.
    + *
    + * @return {goog.ui.PopupDatePicker} Popup instance.
    + */
    +goog.ui.InputDatePicker.prototype.getPopupDatePicker = function() {
    +  return this.popupDatePicker_;
    +};
    +
    +
    +/**
    + * Returns the selected date, if any.  Compares the dates from the date picker
    + * and the input field, causing them to be synced if different.
    + * @return {goog.date.Date?} The selected date, if any.
    + */
    +goog.ui.InputDatePicker.prototype.getDate = function() {
    +
    +  // The user expectation is that the date be whatever the input shows.
    +  // This method biases towards the input value to conform to that expectation.
    +
    +  var inputDate = this.getInputValueAsDate_();
    +  var pickerDate = this.popupDatePicker_.getDate();
    +
    +  if (inputDate && pickerDate) {
    +    if (!inputDate.equals(pickerDate)) {
    +      this.popupDatePicker_.setDate(inputDate);
    +    }
    +  } else {
    +    this.popupDatePicker_.setDate(null);
    +  }
    +
    +  return inputDate;
    +};
    +
    +
    +/**
    + * Sets the selected date.  See goog.ui.PopupDatePicker.setDate().
    + * @param {goog.date.Date?} date The date to set.
    + */
    +goog.ui.InputDatePicker.prototype.setDate = function(date) {
    +  this.popupDatePicker_.setDate(date);
    +};
    +
    +
    +/**
    + * Sets the value of the input element.  This can be overridden to support
    + * alternative types of input setting.
    + *
    + * @param {string} value The value to set.
    + */
    +goog.ui.InputDatePicker.prototype.setInputValue = function(value) {
    +  var el = this.getElement();
    +  if (el.labelInput_) {
    +    var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_);
    +    labelInput.setValue(value);
    +  } else {
    +    el.value = value;
    +  }
    +};
    +
    +
    +/**
    + * Returns the value of the input element.  This can be overridden to support
    + * alternative types of input getting.
    + *
    + * @return {string} The input value.
    + */
    +goog.ui.InputDatePicker.prototype.getInputValue = function() {
    +  var el = this.getElement();
    +  if (el.labelInput_) {
    +    var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_);
    +    return labelInput.getValue();
    +  } else {
    +    return el.value;
    +  }
    +};
    +
    +
    +/**
    + * Sets the value of the input element from date object.
    + *
    + * @param {?goog.date.Date} date The value to set.
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.setInputValueAsDate_ = function(date) {
    +  this.setInputValue(date ? this.dateTimeFormatter_.format(date) : '');
    +};
    +
    +
    +/**
    + * Gets the input element value and attempts to parse it as a date.
    + *
    + * @return {goog.date.Date?} The date object is returned if the parse
    + *      is successful, null is returned on failure.
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.getInputValueAsDate_ = function() {
    +  var value = goog.string.trim(this.getInputValue());
    +  if (value) {
    +    var date = new goog.date.DateTime();
    +    // DateTime needed as parse assumes it can call getHours(), getMinutes(),
    +    // etc, on the date if hours and minutes aren't defined.
    +    if (this.dateTimeParser_.strictParse(value, date) > 0) {
    +      // Parser with YYYY format string will interpret 1 as year 1 A.D.
    +      // However, datepicker.setDate() method will change it into 1901.
    +      // Same is true for any other pattern when number entered by user is
    +      // different from number of digits in the pattern. (YY and 1 will be 1AD).
    +      // See i18n/datetimeparse.js
    +      // Conversion happens in goog.date.Date/DateTime constructor
    +      // when it calls new Date(year...). See ui/datepicker.js.
    +      return date;
    +    }
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Creates an input element for use with the popup date picker.
    + * @override
    + */
    +goog.ui.InputDatePicker.prototype.createDom = function() {
    +  this.setElementInternal(
    +      this.getDomHelper().createDom('input', {'type': 'text'}));
    +  this.popupDatePicker_.createDom();
    +};
    +
    +
    +/**
    + * Sets the element that the PopupDatePicker should be parented to. If not set,
    + * defaults to the body element of the page.
    + * @param {Element} el The element that the PopupDatePicker should be parented
    + *     to.
    + */
    +goog.ui.InputDatePicker.prototype.setPopupParentElement = function(el) {
    +  this.popupParentElement_ = el;
    +};
    +
    +
    +/** @override */
    +goog.ui.InputDatePicker.prototype.enterDocument = function() {
    +  goog.ui.InputDatePicker.superClass_.enterDocument.call(this);
    +  var el = this.getElement();
    +
    +  (this.popupParentElement_ || this.getDomHelper().getDocument().body).
    +      appendChild(this.popupDatePicker_.getElement());
    +  this.popupDatePicker_.enterDocument();
    +  this.popupDatePicker_.attach(el);
    +
    +  // Set the date picker to have the input's initial value, if any.
    +  this.popupDatePicker_.setDate(this.getInputValueAsDate_());
    +
    +  var handler = this.getHandler();
    +  handler.listen(this.popupDatePicker_, goog.ui.DatePicker.Events.CHANGE,
    +                 this.onDateChanged_);
    +  handler.listen(this.popupDatePicker_, goog.ui.PopupBase.EventType.SHOW,
    +                 this.onPopup_);
    +};
    +
    +
    +/** @override */
    +goog.ui.InputDatePicker.prototype.exitDocument = function() {
    +  goog.ui.InputDatePicker.superClass_.exitDocument.call(this);
    +  var el = this.getElement();
    +
    +  this.popupDatePicker_.detach(el);
    +  this.popupDatePicker_.exitDocument();
    +  goog.dom.removeNode(this.popupDatePicker_.getElement());
    +};
    +
    +
    +/** @override */
    +goog.ui.InputDatePicker.prototype.decorateInternal = function(element) {
    +  goog.ui.InputDatePicker.superClass_.decorateInternal.call(this, element);
    +
    +  this.popupDatePicker_.createDom();
    +};
    +
    +
    +/** @override */
    +goog.ui.InputDatePicker.prototype.disposeInternal = function() {
    +  goog.ui.InputDatePicker.superClass_.disposeInternal.call(this);
    +  this.popupDatePicker_.dispose();
    +  this.popupDatePicker_ = null;
    +  this.popupParentElement_ = null;
    +};
    +
    +
    +/**
    + * See goog.ui.PopupDatePicker.showPopup().
    + * @param {Element} element Reference element for displaying the popup -- popup
    + *     will appear at the bottom-left corner of this element.
    + */
    +goog.ui.InputDatePicker.prototype.showForElement = function(element) {
    +  this.popupDatePicker_.showPopup(element);
    +};
    +
    +
    +/**
    + * See goog.ui.PopupDatePicker.hidePopup().
    + */
    +goog.ui.InputDatePicker.prototype.hidePopup = function() {
    +  this.popupDatePicker_.hidePopup();
    +};
    +
    +
    +/**
    + * Event handler for popup date picker popup events.
    + *
    + * @param {goog.events.Event} e popup event.
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.onPopup_ = function(e) {
    +  var inputValueAsDate = this.getInputValueAsDate_();
    +  this.setDate(inputValueAsDate);
    +  // don't overwrite the input value with empty date if input is not valid
    +  if (inputValueAsDate) {
    +    this.setInputValueAsDate_(this.getDatePicker().getDate());
    +  }
    +};
    +
    +
    +/**
    + * Event handler for date change events.  Called when the date changes.
    + *
    + * @param {goog.ui.DatePickerEvent} e Date change event.
    + * @private
    + */
    +goog.ui.InputDatePicker.prototype.onDateChanged_ = function(e) {
    +  this.setInputValueAsDate_(e.date);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.html
    new file mode 100644
    index 0000000000..afc340a070
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: bolinfest@google.com (Michael Bolin)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.InputDatePicker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.InputDatePickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="renderElement">
    +  </div>
    +  <div id="popupParent">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.js
    new file mode 100644
    index 0000000000..5a43cf8af0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/inputdatepicker_test.js
    @@ -0,0 +1,130 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.InputDatePickerTest');
    +goog.setTestOnly('goog.ui.InputDatePickerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.i18n.DateTimeFormat');
    +goog.require('goog.i18n.DateTimeParse');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.InputDatePicker');
    +
    +var dateTimeFormatter = new goog.i18n.DateTimeFormat('MM/dd/yyyy');
    +var dateTimeParser = new goog.i18n.DateTimeParse('MM/dd/yyyy');
    +
    +var inputDatePicker;
    +var popupDatePicker;
    +
    +function setUp() {
    +}
    +
    +function tearDown() {
    +  if (inputDatePicker) {
    +    inputDatePicker.dispose();
    +  }
    +  if (popupDatePicker) {
    +    popupDatePicker.dispose();
    +  }
    +  goog.dom.getElement('renderElement').innerHTML = '';
    +  goog.dom.getElement('popupParent').innerHTML = '';
    +}
    +
    +
    +/**
    + * Ensure that if setPopupParentElement is not called, that the
    + * PopupDatePicker is parented to the body element.
    + */
    +function test_setPopupParentElementDefault() {
    +  setPopupParentElement_(null);
    +  assertEquals('PopupDatePicker should be parented to the body element',
    +      document.body,
    +      popupDatePicker.getElement().parentNode);
    +}
    +
    +
    +/**
    + * Ensure that if setPopupParentElement is called, that the
    + * PopupDatePicker is parented to the specified element.
    + */
    +function test_setPopupParentElement() {
    +  var popupParentElement = goog.dom.getElement('popupParent');
    +  setPopupParentElement_(popupParentElement);
    +  assertEquals('PopupDatePicker should be parented to the popupParent DIV',
    +      popupParentElement,
    +      popupDatePicker.getElement().parentNode);
    +}
    +
    +
    +/**
    + * Creates a new InputDatePicker and calls setPopupParentElement with the
    + * specified element, if provided. If el is null, then setPopupParentElement
    + * is not called.
    + * @param {Element} el If non-null, the argument to pass to
    + *     inputDatePicker.setPopupParentElement().
    + * @private
    + */
    +function setPopupParentElement_(el) {
    +  inputDatePicker = new goog.ui.InputDatePicker(
    +      dateTimeFormatter,
    +      dateTimeParser);
    +
    +  if (el) {
    +    inputDatePicker.setPopupParentElement(el);
    +  }
    +
    +  inputDatePicker.render(goog.dom.getElement('renderElement'));
    +  popupDatePicker = inputDatePicker.popupDatePicker_;
    +}
    +
    +
    +function test_ItParsesDataCorrectly() {
    +  inputDatePicker = new goog.ui.InputDatePicker(
    +      dateTimeFormatter,
    +      dateTimeParser);
    +  inputDatePicker.render(goog.dom.getElement('renderElement'));
    +
    +  inputDatePicker.createDom();
    +  inputDatePicker.setInputValue('8/9/2009');
    +
    +  var parsedDate = inputDatePicker.getInputValueAsDate_();
    +  assertEquals(2009, parsedDate.getYear());
    +  assertEquals(7, parsedDate.getMonth()); // Months start from 0
    +  assertEquals(9, parsedDate.getDate());
    +}
    +
    +function test_ItUpdatesItsValueOnPopupShown() {
    +  inputDatePicker = new goog.ui.InputDatePicker(
    +      dateTimeFormatter,
    +      dateTimeParser);
    +
    +  setPopupParentElement_(null);
    +  inputDatePicker.setInputValue('1/1/1');
    +  inputDatePicker.showForElement(document.body);
    +  var inputValue = inputDatePicker.getInputValue();
    +  assertEquals('01/01/0001', inputValue);
    +}
    +
    +function test_ItDoesNotClearInputOnPopupShown() {
    +  // if popup does not have a date set, don't update input value
    +  inputDatePicker = new goog.ui.InputDatePicker(
    +      dateTimeFormatter,
    +      dateTimeParser);
    +
    +  setPopupParentElement_(null);
    +  inputDatePicker.setInputValue('i_am_not_a_date');
    +  inputDatePicker.showForElement(document.body);
    +  var inputValue = inputDatePicker.getInputValue();
    +  assertEquals('i_am_not_a_date', inputValue);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/itemevent.js b/mapbox-gl/closure-library/closure/goog/ui/itemevent.js
    new file mode 100644
    index 0000000000..fb127b5e05
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/itemevent.js
    @@ -0,0 +1,51 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.ui.ItemEvent class.
    + *
    + */
    +
    +goog.provide('goog.ui.ItemEvent');
    +
    +
    +goog.require('goog.events.Event');
    +
    +
    +
    +/**
    + * Generic ui event class for events that take a single item like a menu click
    + * event.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @param {string} type Event Type.
    + * @param {Object} target Reference to the object that is the target
    + *                        of this event.
    + * @param {Object} item The item that was clicked.
    + * @final
    + */
    +goog.ui.ItemEvent = function(type, target, item) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * Item for the event. The type of this object is specific to the type
    +   * of event. For a menu, it would be the menu item that was clicked. For a
    +   * listbox selection, it would be the listitem that was selected.
    +   *
    +   * @type {Object}
    +   */
    +  this.item = item;
    +};
    +goog.inherits(goog.ui.ItemEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler.js b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler.js
    new file mode 100644
    index 0000000000..5745e95971
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler.js
    @@ -0,0 +1,1158 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Generic keyboard shortcut handler.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/keyboardshortcuts.html
    + */
    +
    +goog.provide('goog.ui.KeyboardShortcutEvent');
    +goog.provide('goog.ui.KeyboardShortcutHandler');
    +goog.provide('goog.ui.KeyboardShortcutHandler.EventType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyNames');
    +goog.require('goog.object');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Component for handling keyboard shortcuts. A shortcut is registered and bound
    + * to a specific identifier. Once the shortcut is triggered an event is fired
    + * with the identifier for the shortcut. This allows keyboard shortcuts to be
    + * customized without modifying the code that listens for them.
    + *
    + * Supports keyboard shortcuts triggered by a single key, a stroke stroke (key
    + * plus at least one modifier) and a sequence of keys or strokes.
    + *
    + * @param {goog.events.EventTarget|EventTarget} keyTarget Event target that the
    + *     key event listener is attached to, typically the applications root
    + *     container.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +goog.ui.KeyboardShortcutHandler = function(keyTarget) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Registered keyboard shortcuts tree. Stored as a map with the keyCode and
    +   * modifier(s) as the key and either a list of further strokes or the shortcut
    +   * task identifier as the value.
    +   * @type {!goog.ui.KeyboardShortcutHandler.SequenceTree_}
    +   * @see #makeStroke_
    +   * @private
    +   */
    +  this.shortcuts_ = {};
    +
    +  /**
    +   * The currently active shortcut sequence tree, which represents the position
    +   * in the complete shortcuts_ tree reached by recent key strokes.
    +   * @type {!goog.ui.KeyboardShortcutHandler.SequenceTree_}
    +   * @private
    +   */
    +  this.currentTree_ = this.shortcuts_;
    +
    +  /**
    +   * The time (in ms, epoch time) of the last keystroke which made progress in
    +   * the shortcut sequence tree (i.e. the time that currentTree_ was last set).
    +   * Used for timing out stroke sequences.
    +   * @type {number}
    +   * @private
    +   */
    +  this.lastStrokeTime_ = 0;
    +
    +  /**
    +   * List of numeric key codes for keys that are safe to always regarded as
    +   * shortcuts, even if entered in a textarea or input field.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.globalKeys_ = goog.object.createSet(
    +      goog.ui.KeyboardShortcutHandler.DEFAULT_GLOBAL_KEYS_);
    +
    +  /**
    +   * List of input types that should only accept ENTER as a shortcut.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.textInputs_ = goog.object.createSet(
    +      goog.ui.KeyboardShortcutHandler.DEFAULT_TEXT_INPUTS_);
    +
    +  /**
    +   * Whether to always prevent the default action if a shortcut event is fired.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.alwaysPreventDefault_ = true;
    +
    +  /**
    +   * Whether to always stop propagation if a shortcut event is fired.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.alwaysStopPropagation_ = false;
    +
    +  /**
    +   * Whether to treat all shortcuts as if they had been passed
    +   * to setGlobalKeys().
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.allShortcutsAreGlobal_ = false;
    +
    +  /**
    +   * Whether to treat shortcuts with modifiers as if they had been passed
    +   * to setGlobalKeys().  Ignored if allShortcutsAreGlobal_ is true.  Applies
    +   * only to form elements (not content-editable).
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.modifierShortcutsAreGlobal_ = true;
    +
    +  /**
    +   * Whether to treat space key as a shortcut when the focused element is a
    +   * checkbox, radiobutton or button.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.allowSpaceKeyOnButtons_ = false;
    +
    +  /**
    +   * Tracks the currently pressed shortcut key, for Firefox.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.activeShortcutKeyForGecko_ = null;
    +
    +  this.initializeKeyListener(keyTarget);
    +};
    +goog.inherits(goog.ui.KeyboardShortcutHandler, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.KeyboardShortcutHandler);
    +
    +
    +
    +/**
    + * A node in a keyboard shortcut sequence tree. A node is either:
    + * 1. A terminal node with a non-nullable shortcut string which is the
    + *    identifier for the shortcut triggered by traversing the tree to that node.
    + * 2. An internal node with a null shortcut string and a
    + *    {@code goog.ui.KeyboardShortcutHandler.SequenceTree_} representing the
    + *    continued stroke sequences from this node.
    + * For clarity, the static factory methods for creating internal and terminal
    + * nodes below should be used rather than using this constructor directly.
    + * @param {string=} opt_shortcut The shortcut identifier, for terminal nodes.
    + * @constructor
    + * @struct
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.SequenceNode_ = function(opt_shortcut) {
    +  /** @const {?string} The shorcut action identifier, for terminal nodes. */
    +  this.shortcut = opt_shortcut || null;
    +
    +  /** @const {goog.ui.KeyboardShortcutHandler.SequenceTree_} */
    +  this.next = opt_shortcut ? null : {};
    +};
    +
    +
    +/**
    + * Creates a terminal shortcut sequence node for the given shortcut identifier.
    + * @param {string} shortcut The shortcut identifier.
    + * @return {!goog.ui.KeyboardShortcutHandler.SequenceNode_}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.createTerminalNode_ = function(shortcut) {
    +  return new goog.ui.KeyboardShortcutHandler.SequenceNode_(shortcut);
    +};
    +
    +
    +/**
    + * Creates an internal shortcut sequence node - a non-terminal part of a
    + * keyboard sequence.
    + * @return {!goog.ui.KeyboardShortcutHandler.SequenceNode_}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.createInternalNode_ = function() {
    +  return new goog.ui.KeyboardShortcutHandler.SequenceNode_();
    +};
    +
    +
    +/**
    + * A map of strokes (represented as numbers) to the nodes reached by those
    + * strokes.
    + * @typedef {Object<number, goog.ui.KeyboardShortcutHandler.SequenceNode_>}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.SequenceTree_;
    +
    +
    +/**
    + * Maximum allowed delay, in milliseconds, allowed between the first and second
    + * key in a key sequence.
    + * @type {number}
    + */
    +goog.ui.KeyboardShortcutHandler.MAX_KEY_SEQUENCE_DELAY = 1500; // 1.5 sec
    +
    +
    +/**
    + * Bit values for modifier keys.
    + * @enum {number}
    + */
    +goog.ui.KeyboardShortcutHandler.Modifiers = {
    +  NONE: 0,
    +  SHIFT: 1,
    +  CTRL: 2,
    +  ALT: 4,
    +  META: 8
    +};
    +
    +
    +/**
    + * Keys marked as global by default.
    + * @type {Array<goog.events.KeyCodes>}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.DEFAULT_GLOBAL_KEYS_ = [
    +  goog.events.KeyCodes.ESC,
    +  goog.events.KeyCodes.F1,
    +  goog.events.KeyCodes.F2,
    +  goog.events.KeyCodes.F3,
    +  goog.events.KeyCodes.F4,
    +  goog.events.KeyCodes.F5,
    +  goog.events.KeyCodes.F6,
    +  goog.events.KeyCodes.F7,
    +  goog.events.KeyCodes.F8,
    +  goog.events.KeyCodes.F9,
    +  goog.events.KeyCodes.F10,
    +  goog.events.KeyCodes.F11,
    +  goog.events.KeyCodes.F12,
    +  goog.events.KeyCodes.PAUSE
    +];
    +
    +
    +/**
    + * Text input types to allow only ENTER shortcuts.
    + * Web Forms 2.0 for HTML5: Section 4.10.7 from 29 May 2012.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.DEFAULT_TEXT_INPUTS_ = [
    +  'color',
    +  'date',
    +  'datetime',
    +  'datetime-local',
    +  'email',
    +  'month',
    +  'number',
    +  'password',
    +  'search',
    +  'tel',
    +  'text',
    +  'time',
    +  'url',
    +  'week'
    +];
    +
    +
    +/**
    + * Events.
    + * @enum {string}
    + */
    +goog.ui.KeyboardShortcutHandler.EventType = {
    +  SHORTCUT_TRIGGERED: 'shortcut',
    +  SHORTCUT_PREFIX: 'shortcut_'
    +};
    +
    +
    +/**
    + * Cache for name to key code lookup.
    + * @type {Object.<number>}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.nameToKeyCodeCache_;
    +
    +
    +/**
    + * Target on which to listen for key events.
    + * @type {goog.events.EventTarget|EventTarget}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.keyTarget_;
    +
    +
    +/**
    + * Due to a bug in the way that Gecko on Mac handles cut/copy/paste key events
    + * using the meta key, it is necessary to fake the keyDown for the action key
    + * (C,V,X) by capturing it on keyUp.
    + * Because users will often release the meta key a slight moment before they
    + * release the action key, we need this variable that will store whether the
    + * meta key has been released recently.
    + * It will be cleared after a short delay in the key handling logic.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.metaKeyRecentlyReleased_;
    +
    +
    +/**
    + * Whether a key event is a printable-key event. Windows uses ctrl+alt
    + * (alt-graph) keys to type characters on European keyboards. For such keys, we
    + * cannot identify whether these keys are used for typing characters when
    + * receiving keydown events. Therefore, we set this flag when we receive their
    + * respective keypress events and fire shortcut events only when we do not
    + * receive them.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.isPrintableKey_;
    +
    +
    +/**
    + * Static method for getting the key code for a given key.
    + * @param {string} name Name of key.
    + * @return {number} The key code.
    + */
    +goog.ui.KeyboardShortcutHandler.getKeyCode = function(name) {
    +  // Build reverse lookup object the first time this method is called.
    +  if (!goog.ui.KeyboardShortcutHandler.nameToKeyCodeCache_) {
    +    var map = {};
    +    for (var key in goog.events.KeyNames) {
    +      // Explicitly convert the stringified map keys to numbers and normalize.
    +      map[goog.events.KeyNames[key]] =
    +          goog.events.KeyCodes.normalizeKeyCode(parseInt(key, 10));
    +    }
    +    goog.ui.KeyboardShortcutHandler.nameToKeyCodeCache_ = map;
    +  }
    +
    +  // Check if key is in cache.
    +  return goog.ui.KeyboardShortcutHandler.nameToKeyCodeCache_[name];
    +};
    +
    +
    +/**
    + * Sets whether to always prevent the default action when a shortcut event is
    + * fired. If false, the default action is prevented only if preventDefault is
    + * called on either of the corresponding SHORTCUT_TRIGGERED or SHORTCUT_PREFIX
    + * events. If true, the default action is prevented whenever a shortcut event
    + * is fired. The default value is true.
    + * @param {boolean} alwaysPreventDefault Whether to always call preventDefault.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setAlwaysPreventDefault = function(
    +    alwaysPreventDefault) {
    +  this.alwaysPreventDefault_ = alwaysPreventDefault;
    +};
    +
    +
    +/**
    + * Returns whether the default action will always be prevented when a shortcut
    + * event is fired. The default value is true.
    + * @see #setAlwaysPreventDefault
    + * @return {boolean} Whether preventDefault will always be called.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getAlwaysPreventDefault = function() {
    +  return this.alwaysPreventDefault_;
    +};
    +
    +
    +/**
    + * Sets whether to always stop propagation for the event when fired. If false,
    + * the propagation is stopped only if stopPropagation is called on either of the
    + * corresponding SHORT_CUT_TRIGGERED or SHORTCUT_PREFIX events. If true, the
    + * event is prevented from propagating beyond its target whenever it is fired.
    + * The default value is false.
    + * @param {boolean} alwaysStopPropagation Whether to always call
    + *     stopPropagation.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setAlwaysStopPropagation = function(
    +    alwaysStopPropagation) {
    +  this.alwaysStopPropagation_ = alwaysStopPropagation;
    +};
    +
    +
    +/**
    + * Returns whether the event will always be stopped from propagating beyond its
    + * target when a shortcut event is fired. The default value is false.
    + * @see #setAlwaysStopPropagation
    + * @return {boolean} Whether stopPropagation will always be called.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getAlwaysStopPropagation =
    +    function() {
    +  return this.alwaysStopPropagation_;
    +};
    +
    +
    +/**
    + * Sets whether to treat all shortcuts (including modifier shortcuts) as if the
    + * keys had been passed to the setGlobalKeys function.
    + * @param {boolean} allShortcutsGlobal Whether to treat all shortcuts as global.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setAllShortcutsAreGlobal = function(
    +    allShortcutsGlobal) {
    +  this.allShortcutsAreGlobal_ = allShortcutsGlobal;
    +};
    +
    +
    +/**
    + * Returns whether all shortcuts (including modifier shortcuts) are treated as
    + * if the keys had been passed to the setGlobalKeys function.
    + * @see #setAllShortcutsAreGlobal
    + * @return {boolean} Whether all shortcuts are treated as globals.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getAllShortcutsAreGlobal =
    +    function() {
    +  return this.allShortcutsAreGlobal_;
    +};
    +
    +
    +/**
    + * Sets whether to treat shortcuts with modifiers as if the keys had been
    + * passed to the setGlobalKeys function.  Ignored if you have called
    + * setAllShortcutsAreGlobal(true).  Applies only to form elements (not
    + * content-editable).
    + * @param {boolean} modifierShortcutsGlobal Whether to treat shortcuts with
    + *     modifiers as global.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setModifierShortcutsAreGlobal =
    +    function(modifierShortcutsGlobal) {
    +  this.modifierShortcutsAreGlobal_ = modifierShortcutsGlobal;
    +};
    +
    +
    +/**
    + * Returns whether shortcuts with modifiers are treated as if the keys had been
    + * passed to the setGlobalKeys function.  Ignored if you have called
    + * setAllShortcutsAreGlobal(true).  Applies only to form elements (not
    + * content-editable).
    + * @see #setModifierShortcutsAreGlobal
    + * @return {boolean} Whether shortcuts with modifiers are treated as globals.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getModifierShortcutsAreGlobal =
    +    function() {
    +  return this.modifierShortcutsAreGlobal_;
    +};
    +
    +
    +/**
    + * Sets whether to treat space key as a shortcut when the focused element is a
    + * checkbox, radiobutton or button.
    + * @param {boolean} allowSpaceKeyOnButtons Whether to treat space key as a
    + *     shortcut when the focused element is a checkbox, radiobutton or button.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setAllowSpaceKeyOnButtons = function(
    +    allowSpaceKeyOnButtons) {
    +  this.allowSpaceKeyOnButtons_ = allowSpaceKeyOnButtons;
    +};
    +
    +
    +/**
    + * Registers a keyboard shortcut.
    + * @param {string} identifier Identifier for the task performed by the keyboard
    + *                 combination. Multiple shortcuts can be provided for the same
    + *                 task by specifying the same identifier.
    + * @param {...(number|string|Array<number>)} var_args See below.
    + *
    + * param {number} keyCode Numeric code for key
    + * param {number=} opt_modifiers Bitmap indicating required modifier keys.
    + *                goog.ui.KeyboardShortcutHandler.Modifiers.SHIFT, CONTROL,
    + *                ALT, or META.
    + *
    + * The last two parameters can be repeated any number of times to create a
    + * shortcut using a sequence of strokes. Instead of varagrs the second parameter
    + * could also be an array where each element would be ragarded as a parameter.
    + *
    + * A string representation of the shortcut can be supplied instead of the last
    + * two parameters. In that case the method only takes two arguments, the
    + * identifier and the string.
    + *
    + * Examples:
    + *   g               registerShortcut(str, G_KEYCODE)
    + *   Ctrl+g          registerShortcut(str, G_KEYCODE, CTRL)
    + *   Ctrl+Shift+g    registerShortcut(str, G_KEYCODE, CTRL | SHIFT)
    + *   Ctrl+g a        registerShortcut(str, G_KEYCODE, CTRL, A_KEYCODE)
    + *   Ctrl+g Shift+a  registerShortcut(str, G_KEYCODE, CTRL, A_KEYCODE, SHIFT)
    + *   g a             registerShortcut(str, G_KEYCODE, NONE, A_KEYCODE)
    + *
    + * Examples using string representation for shortcuts:
    + *   g               registerShortcut(str, 'g')
    + *   Ctrl+g          registerShortcut(str, 'ctrl+g')
    + *   Ctrl+Shift+g    registerShortcut(str, 'ctrl+shift+g')
    + *   Ctrl+g a        registerShortcut(str, 'ctrl+g a')
    + *   Ctrl+g Shift+a  registerShortcut(str, 'ctrl+g shift+a')
    + *   g a             registerShortcut(str, 'g a').
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.registerShortcut = function(
    +    identifier, var_args) {
    +
    +  // Add shortcut to shortcuts_ tree
    +  goog.ui.KeyboardShortcutHandler.setShortcut_(
    +      this.shortcuts_, this.interpretStrokes_(1, arguments), identifier);
    +};
    +
    +
    +/**
    + * Unregisters a keyboard shortcut by keyCode and modifiers or string
    + * representation of sequence.
    + *
    + * param {number} keyCode Numeric code for key
    + * param {number=} opt_modifiers Bitmap indicating required modifier keys.
    + *                 goog.ui.KeyboardShortcutHandler.Modifiers.SHIFT, CONTROL,
    + *                 ALT, or META.
    + *
    + * The two parameters can be repeated any number of times to create a shortcut
    + * using a sequence of strokes.
    + *
    + * A string representation of the shortcut can be supplied instead see
    + * {@link #registerShortcut} for syntax. In that case the method only takes one
    + * argument.
    + *
    + * @param {...(number|string|Array<number>)} var_args String representation, or
    + *     array or list of alternating key codes and modifiers.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.unregisterShortcut = function(
    +    var_args) {
    +  // Remove shortcut from tree.
    +  goog.ui.KeyboardShortcutHandler.unsetShortcut_(
    +      this.shortcuts_, this.interpretStrokes_(0, arguments));
    +};
    +
    +
    +/**
    + * Verifies if a particular keyboard shortcut is registered already. It has
    + * the same interface as the unregistering of shortcuts.
    + *
    + * param {number} keyCode Numeric code for key
    + * param {number=} opt_modifiers Bitmap indicating required modifier keys.
    + *                 goog.ui.KeyboardShortcutHandler.Modifiers.SHIFT, CONTROL,
    + *                 ALT, or META.
    + *
    + * The two parameters can be repeated any number of times to create a shortcut
    + * using a sequence of strokes.
    + *
    + * A string representation of the shortcut can be supplied instead see
    + * {@link #registerShortcut} for syntax. In that case the method only takes one
    + * argument.
    + *
    + * @param {...(number|string|Array<number>)} var_args String representation, or
    + *     array or list of alternating key codes and modifiers.
    + * @return {boolean} Whether the specified keyboard shortcut is registered.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.isShortcutRegistered = function(
    +    var_args) {
    +  return this.checkShortcut_(this.interpretStrokes_(0, arguments));
    +};
    +
    +
    +/**
    + * Parses the variable arguments for registerShortcut and unregisterShortcut.
    + * @param {number} initialIndex The first index of "args" to treat as
    + *     variable arguments.
    + * @param {Object} args The "arguments" array passed
    + *     to registerShortcut or unregisterShortcut.  Please see the comments in
    + *     registerShortcut for list of allowed forms.
    + * @return {!Array<number>} The sequence of strokes, represented as numbers.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.interpretStrokes_ = function(
    +    initialIndex, args) {
    +  var strokes;
    +
    +  // Build strokes array from string.
    +  if (goog.isString(args[initialIndex])) {
    +    strokes = goog.array.map(
    +        goog.ui.KeyboardShortcutHandler.parseStringShortcut(args[initialIndex]),
    +        function(stroke) {
    +          goog.asserts.assertNumber(
    +              stroke.keyCode, 'A non-modifier key is needed in each stroke.');
    +          return goog.ui.KeyboardShortcutHandler.makeStroke_(
    +              stroke.keyCode, stroke.modifiers);
    +        });
    +
    +  // Build strokes array from arguments list or from array.
    +  } else {
    +    var strokesArgs = args, i = initialIndex;
    +    if (goog.isArray(args[initialIndex])) {
    +      strokesArgs = args[initialIndex];
    +      i = 0;
    +    }
    +
    +    strokes = [];
    +    for (; i < strokesArgs.length; i += 2) {
    +      strokes.push(goog.ui.KeyboardShortcutHandler.makeStroke_(
    +          strokesArgs[i], strokesArgs[i + 1]));
    +    }
    +  }
    +
    +  return strokes;
    +};
    +
    +
    +/**
    + * Unregisters all keyboard shortcuts.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.unregisterAll = function() {
    +  this.shortcuts_ = {};
    +};
    +
    +
    +/**
    + * Sets the global keys; keys that are safe to always regarded as shortcuts,
    + * even if entered in a textarea or input field.
    + * @param {Array<number>} keys List of keys.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setGlobalKeys = function(keys) {
    +  this.globalKeys_ = goog.object.createSet(keys);
    +};
    +
    +
    +/**
    + * @return {!Array<string>} The global keys, i.e. keys that are safe to always
    + *     regard as shortcuts, even if entered in a textarea or input field.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getGlobalKeys = function() {
    +  return goog.object.getKeys(this.globalKeys_);
    +};
    +
    +
    +/** @override */
    +goog.ui.KeyboardShortcutHandler.prototype.disposeInternal = function() {
    +  goog.ui.KeyboardShortcutHandler.superClass_.disposeInternal.call(this);
    +  this.unregisterAll();
    +  this.clearKeyListener();
    +};
    +
    +
    +/**
    + * Returns event type for a specific shortcut.
    + * @param {string} identifier Identifier for the shortcut task.
    + * @return {string} Theh event type.
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.getEventType =
    +    function(identifier) {
    +
    +  return goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_PREFIX + identifier;
    +};
    +
    +
    +/**
    + * Builds stroke array from string representation of shortcut.
    + * @param {string} s String representation of shortcut.
    + * @return {!Array<!{keyCode: ?number, modifiers: number}>} The stroke array.  A
    + *     null keyCode means no non-modifier key was part of the stroke.
    + */
    +goog.ui.KeyboardShortcutHandler.parseStringShortcut = function(s) {
    +  // Normalize whitespace and force to lower case.
    +  s = s.replace(/[ +]*\+[ +]*/g, '+').replace(/[ ]+/g, ' ').toLowerCase();
    +
    +  // Build strokes array from string, space separates strokes, plus separates
    +  // individual keys.
    +  var groups = s.split(' ');
    +  var strokes = [];
    +  for (var group, i = 0; group = groups[i]; i++) {
    +    var keys = group.split('+');
    +    // Explicitly re-initialize key data (JS does not have block scoping).
    +    var keyCode = null;
    +    var modifiers = goog.ui.KeyboardShortcutHandler.Modifiers.NONE;
    +    for (var key, j = 0; key = keys[j]; j++) {
    +      switch (key) {
    +        case 'shift':
    +          modifiers |= goog.ui.KeyboardShortcutHandler.Modifiers.SHIFT;
    +          continue;
    +        case 'ctrl':
    +          modifiers |= goog.ui.KeyboardShortcutHandler.Modifiers.CTRL;
    +          continue;
    +        case 'alt':
    +          modifiers |= goog.ui.KeyboardShortcutHandler.Modifiers.ALT;
    +          continue;
    +        case 'meta':
    +          modifiers |= goog.ui.KeyboardShortcutHandler.Modifiers.META;
    +          continue;
    +      }
    +      if (!goog.isNull(keyCode)) {
    +        goog.asserts.fail('At most one non-modifier key can be in a stroke.');
    +      }
    +      keyCode = goog.ui.KeyboardShortcutHandler.getKeyCode(key);
    +      goog.asserts.assertNumber(
    +          keyCode, 'Key name not found in goog.events.KeyNames: ' + key);
    +      break;
    +    }
    +    strokes.push({keyCode: keyCode, modifiers: modifiers});
    +  }
    +
    +  return strokes;
    +};
    +
    +
    +/**
    + * Adds a key event listener that triggers {@link #handleKeyDown_} when keys
    + * are pressed.
    + * @param {goog.events.EventTarget|EventTarget} keyTarget Event target that the
    + *     event listener should be attached to.
    + * @protected
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.initializeKeyListener =
    +    function(keyTarget) {
    +  this.keyTarget_ = keyTarget;
    +
    +  goog.events.listen(this.keyTarget_, goog.events.EventType.KEYDOWN,
    +      this.handleKeyDown_, false, this);
    +
    +  if (goog.userAgent.GECKO) {
    +    goog.events.listen(this.keyTarget_, goog.events.EventType.KEYUP,
    +        this.handleGeckoKeyUp_, false, this);
    +  }
    +
    +  // Windows uses ctrl+alt keys (a.k.a. alt-graph keys) for typing characters
    +  // on European keyboards (e.g. ctrl+alt+e for an an euro sign.) Unfortunately,
    +  // Windows browsers except Firefox does not have any methods except listening
    +  // keypress and keyup events to identify if ctrl+alt keys are really used for
    +  // inputting characters. Therefore, we listen to these events and prevent
    +  // firing shortcut-key events if ctrl+alt keys are used for typing characters.
    +  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    +    goog.events.listen(this.keyTarget_, goog.events.EventType.KEYPRESS,
    +                       this.handleWindowsKeyPress_, false, this);
    +    goog.events.listen(this.keyTarget_, goog.events.EventType.KEYUP,
    +                       this.handleWindowsKeyUp_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Handler for when a keyup event is fired in Firefox (Gecko).
    + * @param {goog.events.BrowserEvent} e The key event.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.handleGeckoKeyUp_ = function(e) {
    +  // Due to a bug in the way that Gecko on Mac handles cut/copy/paste key events
    +  // using the meta key, it is necessary to fake the keyDown for the action keys
    +  // (C,V,X) by capturing it on keyUp.
    +  // This is because the keyDown events themselves are not fired by the browser
    +  // in this case.
    +  // Because users will often release the meta key a slight moment before they
    +  // release the action key, we need to store whether the meta key has been
    +  // released recently to avoid "flaky" cutting/pasting behavior.
    +  if (goog.userAgent.MAC) {
    +    if (e.keyCode == goog.events.KeyCodes.MAC_FF_META) {
    +      this.metaKeyRecentlyReleased_ = true;
    +      goog.Timer.callOnce(function() {
    +        this.metaKeyRecentlyReleased_ = false;
    +      }, 400, this);
    +      return;
    +    }
    +
    +    var metaKey = e.metaKey || this.metaKeyRecentlyReleased_;
    +    if ((e.keyCode == goog.events.KeyCodes.C ||
    +        e.keyCode == goog.events.KeyCodes.X ||
    +        e.keyCode == goog.events.KeyCodes.V) && metaKey) {
    +      e.metaKey = metaKey;
    +      this.handleKeyDown_(e);
    +    }
    +  }
    +
    +  // Firefox triggers buttons on space keyUp instead of keyDown.  So if space
    +  // keyDown activated a shortcut, do NOT also trigger the focused button.
    +  if (goog.events.KeyCodes.SPACE == this.activeShortcutKeyForGecko_ &&
    +      goog.events.KeyCodes.SPACE == e.keyCode) {
    +    e.preventDefault();
    +  }
    +  this.activeShortcutKeyForGecko_ = null;
    +};
    +
    +
    +/**
    + * Returns whether this event is possibly used for typing a printable character.
    + * Windows uses ctrl+alt (a.k.a. alt-graph) keys for typing characters on
    + * European keyboards. Since only Firefox provides a method that can identify
    + * whether ctrl+alt keys are used for typing characters, we need to check
    + * whether Windows sends a keypress event to prevent firing shortcut event if
    + * this event is used for typing characters.
    + * @param {goog.events.BrowserEvent} e The key event.
    + * @return {boolean} Whether this event is a possible printable-key event.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.isPossiblePrintableKey_ =
    +    function(e) {
    +  return goog.userAgent.WINDOWS && !goog.userAgent.GECKO &&
    +      e.ctrlKey && e.altKey && !e.shiftKey;
    +};
    +
    +
    +/**
    + * Handler for when a keypress event is fired on Windows.
    + * @param {goog.events.BrowserEvent} e The key event.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.handleWindowsKeyPress_ = function(e) {
    +  // When this keypress event consists of a printable character, set the flag to
    +  // prevent firing shortcut key events when we receive the succeeding keyup
    +  // event. We accept all Unicode characters except control ones since this
    +  // keyCode may be a non-ASCII character.
    +  if (e.keyCode > 0x20 && this.isPossiblePrintableKey_(e)) {
    +    this.isPrintableKey_ = true;
    +  }
    +};
    +
    +
    +/**
    + * Handler for when a keyup event is fired on Windows.
    + * @param {goog.events.BrowserEvent} e The key event.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.handleWindowsKeyUp_ = function(e) {
    +  // For possible printable-key events, try firing a shortcut-key event only
    +  // when this event is not used for typing a character.
    +  if (!this.isPrintableKey_ && this.isPossiblePrintableKey_(e)) {
    +    this.handleKeyDown_(e);
    +  }
    +};
    +
    +
    +/**
    + * Removes the listener that was added by link {@link #initializeKeyListener}.
    + * @protected
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.clearKeyListener = function() {
    +  goog.events.unlisten(this.keyTarget_, goog.events.EventType.KEYDOWN,
    +      this.handleKeyDown_, false, this);
    +  if (goog.userAgent.GECKO) {
    +    goog.events.unlisten(this.keyTarget_, goog.events.EventType.KEYUP,
    +        this.handleGeckoKeyUp_, false, this);
    +  }
    +  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    +    goog.events.unlisten(this.keyTarget_, goog.events.EventType.KEYPRESS,
    +        this.handleWindowsKeyPress_, false, this);
    +    goog.events.unlisten(this.keyTarget_, goog.events.EventType.KEYUP,
    +        this.handleWindowsKeyUp_, false, this);
    +  }
    +  this.keyTarget_ = null;
    +};
    +
    +
    +/**
    + * Adds a shortcut stroke sequence to the given sequence tree. Recursive.
    + * @param {!goog.ui.KeyboardShortcutHandler.SequenceTree_} tree The stroke
    + *     sequence tree to add to.
    + * @param {Array<number>} strokes Array of strokes for shortcut.
    + * @param {string} identifier Identifier for the task performed by shortcut.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.setShortcut_ = function(
    +    tree, strokes, identifier) {
    +  var stroke = strokes.shift();
    +  var node = tree[stroke];
    +  if (node && (strokes.length == 0 || node.shortcut)) {
    +    // This new shortcut would override an existing shortcut or shortcut prefix
    +    // (since the new strokes end at an existing node), or an existing shortcut
    +    // would be triggered by the prefix to this new shortcut (since there is
    +    // already a terminal node on the path we are trying to create).
    +    throw Error('Keyboard shortcut conflicts with existing shortcut');
    +  }
    +
    +  if (strokes.length) {
    +    node = goog.object.setIfUndefined(tree, stroke.toString(),
    +        goog.ui.KeyboardShortcutHandler.createInternalNode_());
    +    goog.ui.KeyboardShortcutHandler.setShortcut_(
    +        goog.asserts.assert(node.next, 'An internal node must have a next map'),
    +        strokes, identifier);
    +  } else {
    +    // Add a terminal node.
    +    tree[stroke] =
    +        goog.ui.KeyboardShortcutHandler.createTerminalNode_(identifier);
    +  }
    +};
    +
    +
    +/**
    + * Removes a shortcut stroke sequence from the given sequence tree, pruning any
    + * dead branches of the tree. Recursive.
    + * @param {!goog.ui.KeyboardShortcutHandler.SequenceTree_} tree The stroke
    + *     sequence tree to remove from.
    + * @param {Array<number>} strokes Array of strokes for shortcut to remove.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.unsetShortcut_ = function(tree, strokes) {
    +  var stroke = strokes.shift();
    +  var node = tree[stroke];
    +
    +  if (!node) {
    +    // The given stroke sequence is not in the tree.
    +    return;
    +  }
    +  if (strokes.length == 0) {
    +    // Base case - the end of the stroke sequence.
    +    if (!node.shortcut) {
    +      // The given stroke sequence does not end at a terminal node.
    +      return;
    +    }
    +    delete tree[stroke];
    +  } else {
    +    if (!node.next) {
    +      // The given stroke sequence is not in the tree.
    +      return;
    +    }
    +    // Recursively remove the rest of the shortcut sequence from the node.next
    +    // subtree.
    +    goog.ui.KeyboardShortcutHandler.unsetShortcut_(node.next, strokes);
    +    if (goog.object.isEmpty(node.next)) {
    +      // The node.next subtree is now empty (the last stroke in it was just
    +      // removed), so prune this dead branch of the tree.
    +      delete tree[stroke];
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Checks if a particular keyboard shortcut is registered.
    + * @param {Array<number>} strokes Strokes array.
    + * @return {boolean} True iff the keyboard is registred.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.checkShortcut_ = function(strokes) {
    +  var tree = this.shortcuts_;
    +  while (strokes.length > 0 && tree) {
    +    var node = tree[strokes.shift()];
    +    if (!node) {
    +      return false;
    +    }
    +    if (strokes.length == 0 && node.shortcut) {
    +      return true;
    +    }
    +    tree = node.next;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Constructs key from key code and modifiers.
    + *
    + * The lower 8 bits are used for the key code, the following 3 for modifiers and
    + * the remaining bits are unused.
    + *
    + * @param {number} keyCode Numeric key code.
    + * @param {number} modifiers Required modifiers.
    + * @return {number} The key.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.makeStroke_ = function(keyCode, modifiers) {
    +  // Make sure key code is just 8 bits and OR it with the modifiers left shifted
    +  // 8 bits.
    +  return (keyCode & 255) | (modifiers << 8);
    +};
    +
    +
    +/**
    + * Keypress handler.
    + * @param {goog.events.BrowserEvent} event Keypress event.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.handleKeyDown_ = function(event) {
    +  if (!this.isValidShortcut_(event)) {
    +    return;
    +  }
    +  // For possible printable-key events, we cannot identify whether the events
    +  // are used for typing characters until we receive respective keyup events.
    +  // Therefore, we handle this event when we receive a succeeding keyup event
    +  // to verify this event is not used for typing characters.
    +  if (event.type == 'keydown' && this.isPossiblePrintableKey_(event)) {
    +    this.isPrintableKey_ = false;
    +    return;
    +  }
    +
    +  var keyCode = goog.events.KeyCodes.normalizeKeyCode(event.keyCode);
    +
    +  var modifiers =
    +      (event.shiftKey ? goog.ui.KeyboardShortcutHandler.Modifiers.SHIFT : 0) |
    +      (event.ctrlKey ? goog.ui.KeyboardShortcutHandler.Modifiers.CTRL : 0) |
    +      (event.altKey ? goog.ui.KeyboardShortcutHandler.Modifiers.ALT : 0) |
    +      (event.metaKey ? goog.ui.KeyboardShortcutHandler.Modifiers.META : 0);
    +  var stroke = goog.ui.KeyboardShortcutHandler.makeStroke_(keyCode, modifiers);
    +
    +  if (!this.currentTree_[stroke] || this.hasSequenceTimedOut_()) {
    +    // Either this stroke does not continue any active sequence, or the
    +    // currently active sequence has timed out. Reset shortcut tree progress.
    +    this.setCurrentTree_(this.shortcuts_);
    +  }
    +
    +  var node = this.currentTree_[stroke];
    +  if (!node) {
    +    // This stroke does not correspond to a shortcut or continued sequence.
    +    return;
    +  }
    +  if (node.next) {
    +    // This stroke does not trigger a shortcut, but entered stroke(s) are a part
    +    // of a sequence. Progress in the sequence tree and record time to allow the
    +    // following stroke(s) to trigger the shortcut.
    +    this.setCurrentTree_(node.next);
    +    // Prevent default action so that the rest of the stroke sequence can be
    +    // completed.
    +    event.preventDefault();
    +    return;
    +  }
    +  // This stroke triggers a shortcut. Any active sequence has been completed, so
    +  // reset the sequence tree.
    +  this.setCurrentTree_(this.shortcuts_);
    +
    +  // Dispatch the triggered keyboard shortcut event. In addition to the generic
    +  // keyboard shortcut event a more specific fine grained one, specific for the
    +  // shortcut identifier, is fired.
    +  if (this.alwaysPreventDefault_) {
    +    event.preventDefault();
    +  }
    +
    +  if (this.alwaysStopPropagation_) {
    +    event.stopPropagation();
    +  }
    +
    +  var shortcut = goog.asserts.assertString(
    +      node.shortcut, 'A terminal node must have a string shortcut identifier.');
    +  // Dispatch SHORTCUT_TRIGGERED event
    +  var target = /** @type {Node} */ (event.target);
    +  var triggerEvent = new goog.ui.KeyboardShortcutEvent(
    +      goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED, shortcut,
    +      target);
    +  var retVal = this.dispatchEvent(triggerEvent);
    +
    +  // Dispatch SHORTCUT_PREFIX_<identifier> event
    +  var prefixEvent = new goog.ui.KeyboardShortcutEvent(
    +      goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_PREFIX + shortcut,
    +      shortcut, target);
    +  retVal &= this.dispatchEvent(prefixEvent);
    +
    +  // The default action is prevented if 'preventDefault' was
    +  // called on either event, or if a listener returned false.
    +  if (!retVal) {
    +    event.preventDefault();
    +  }
    +
    +  // For Firefox, track which shortcut key was pushed.
    +  if (goog.userAgent.GECKO) {
    +    this.activeShortcutKeyForGecko_ = keyCode;
    +  }
    +};
    +
    +
    +/**
    + * Checks if a given keypress event may be treated as a shortcut.
    + * @param {goog.events.BrowserEvent} event Keypress event.
    + * @return {boolean} Whether to attempt to process the event as a shortcut.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.isValidShortcut_ = function(event) {
    +  var keyCode = event.keyCode;
    +
    +  // Ignore Ctrl, Shift and ALT
    +  if (keyCode == goog.events.KeyCodes.SHIFT ||
    +      keyCode == goog.events.KeyCodes.CTRL ||
    +      keyCode == goog.events.KeyCodes.ALT) {
    +    return false;
    +  }
    +  var el = /** @type {Element} */ (event.target);
    +  var isFormElement =
    +      el.tagName == 'TEXTAREA' || el.tagName == 'INPUT' ||
    +      el.tagName == 'BUTTON' || el.tagName == 'SELECT';
    +
    +  var isContentEditable = !isFormElement && (el.isContentEditable ||
    +      (el.ownerDocument && el.ownerDocument.designMode == 'on'));
    +
    +  if (!isFormElement && !isContentEditable) {
    +    return true;
    +  }
    +  // Always allow keys registered as global to be used (typically Esc, the
    +  // F-keys and other keys that are not typically used to manipulate text).
    +  if (this.globalKeys_[keyCode] || this.allShortcutsAreGlobal_) {
    +    return true;
    +  }
    +  if (isContentEditable) {
    +    // For events originating from an element in editing mode we only let
    +    // global key codes through.
    +    return false;
    +  }
    +  // Event target is one of (TEXTAREA, INPUT, BUTTON, SELECT).
    +  // Allow modifier shortcuts, unless we shouldn't.
    +  if (this.modifierShortcutsAreGlobal_ && (
    +      event.altKey || event.ctrlKey || event.metaKey)) {
    +    return true;
    +  }
    +  // Allow ENTER to be used as shortcut for text inputs.
    +  if (el.tagName == 'INPUT' && this.textInputs_[el.type]) {
    +    return keyCode == goog.events.KeyCodes.ENTER;
    +  }
    +  // Checkboxes, radiobuttons and buttons. Allow all but SPACE as shortcut.
    +  if (el.tagName == 'INPUT' || el.tagName == 'BUTTON') {
    +    // TODO(gboyer): If more flexibility is needed, create protected helper
    +    // methods for each case (e.g. button, input, etc).
    +    if (this.allowSpaceKeyOnButtons_) {
    +      return true;
    +    } else {
    +      return keyCode != goog.events.KeyCodes.SPACE;
    +    }
    +  }
    +  // Don't allow any additional shortcut keys for textareas or selects.
    +  return false;
    +};
    +
    +
    +/**
    + * @return {boolean} True iff the current stroke sequence has timed out.
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.hasSequenceTimedOut_ = function() {
    +  return goog.now() - this.lastStrokeTime_ >=
    +      goog.ui.KeyboardShortcutHandler.MAX_KEY_SEQUENCE_DELAY;
    +};
    +
    +
    +/**
    + * Sets the current keyboard shortcut sequence tree and updates the last stroke
    + * time.
    + * @param {!goog.ui.KeyboardShortcutHandler.SequenceTree_} tree
    + * @private
    + */
    +goog.ui.KeyboardShortcutHandler.prototype.setCurrentTree_ = function(tree) {
    +  this.currentTree_ = tree;
    +  this.lastStrokeTime_ = goog.now();
    +};
    +
    +
    +
    +/**
    + * Object representing a keyboard shortcut event.
    + * @param {string} type Event type.
    + * @param {string} identifier Task identifier for the triggered shortcut.
    + * @param {Node|goog.events.EventTarget} target Target the original key press
    + *     event originated from.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.ui.KeyboardShortcutEvent = function(type, identifier, target) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * Task identifier for the triggered shortcut
    +   * @type {string}
    +   */
    +  this.identifier = identifier;
    +};
    +goog.inherits(goog.ui.KeyboardShortcutEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.html b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.html
    new file mode 100644
    index 0000000000..d5c2e948a4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.html
    @@ -0,0 +1,54 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <!-- Author:  gboyer@google.com (Garrett Boyer) -->
    +  <title>
    +   Closure Unit Tests - goog.ui.KeyboardShortcutHandler
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.KeyboardShortcutHandlerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="rootDiv">
    +   <div id="targetDiv">
    +    The test presses keys on me!
    +   </div>
    +   <button id="targetButton">
    +    A Button
    +   </button>
    +   <input type="checkbox" id="targetCheckBox" />
    +   <input type="color" id="targetColor" value="#FF0000" />
    +   <input type="date" id="targetDate" value="1995-12-31" />
    +   <input type="datetime" id="targetDateTime" value="1995-12-31T23:59:59.99Z" />
    +   <input type="datetime-local" id="targetDateTimeLocal" value="1995-12-31T23:59:59.99" />
    +   <input type="email" id="targetEmail" value="test@google.com" />
    +   <input type="month" id="targetMonth" value="1995-12" />
    +   <input type="number" id="targetNumber" value="12345" />
    +   <input type="password" id="targetPassword" value="password" />
    +   <input type="search" id="targetSearch" value="search terms" />
    +   <input type="tel" id="targetTel" value="555 1212" />
    +   <input type="text" id="targetText" value="text box" />
    +   <input type="time" id="targetTime" value="12:00" />
    +   <input type="url" id="targetUrl" value="http://www.google.com" />
    +   <input type="week" id="targetWeek" value="2005-W52" />
    +   <select id="targetSelect">
    +    <option>
    +     select
    +    </option>
    +   </select>
    +   <textarea id="targetTextArea">
    +    text area
    +   </textarea>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.js b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.js
    new file mode 100644
    index 0000000000..80591055fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/keyboardshortcuthandler_test.js
    @@ -0,0 +1,737 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.KeyboardShortcutHandlerTest');
    +goog.setTestOnly('goog.ui.KeyboardShortcutHandlerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.StrictMock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.KeyboardShortcutHandler');
    +goog.require('goog.userAgent');
    +
    +var Modifiers = goog.ui.KeyboardShortcutHandler.Modifiers;
    +var KeyCodes = goog.events.KeyCodes;
    +
    +var handler;
    +var targetDiv;
    +var listener;
    +var mockClock;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +/**
    + * Fires a keypress on the target div.
    + * @return {boolean} The returnValue of the sequence: false if
    + *     preventDefault() was called on any of the events, true otherwise.
    + */
    +function fire(keycode, opt_extraProperties, opt_element) {
    +  return goog.testing.events.fireKeySequence(
    +      opt_element || targetDiv, keycode, opt_extraProperties);
    +}
    +
    +function fireAltGraphKey(keycode, keyPressKeyCode, opt_extraProperties,
    +                         opt_element) {
    +  return goog.testing.events.fireNonAsciiKeySequence(
    +      opt_element || targetDiv, keycode, keyPressKeyCode,
    +      opt_extraProperties);
    +}
    +
    +function setUp() {
    +  targetDiv = goog.dom.getElement('targetDiv');
    +  handler = new goog.ui.KeyboardShortcutHandler(
    +      goog.dom.getElement('rootDiv'));
    +
    +  // Create a mock event listener in order to set expectations on what
    +  // events are fired.  We create a fake class whose only method is
    +  // shortcutFired(shortcut identifier).
    +  listener = new goog.testing.StrictMock(
    +      {shortcutFired: goog.nullFunction});
    +  goog.events.listen(
    +      handler,
    +      goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED,
    +      function(event) { listener.shortcutFired(event.identifier); });
    +
    +  // Set up a fake clock, because keyboard shortcuts *are* time
    +  // sensitive.
    +  mockClock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDown() {
    +  mockClock.uninstall();
    +  handler.dispose();
    +  stubs.reset();
    +}
    +
    +function testAllowsSingleLetterKeyBindingsSpecifiedAsString() {
    +  listener.shortcutFired('lettergee');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergee', 'g');
    +  fire(KeyCodes.G);
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsSingleLetterKeyBindingsSpecifiedAsKeyCode() {
    +  listener.shortcutFired('lettergee');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergee', KeyCodes.G);
    +  fire(KeyCodes.G);
    +
    +  listener.$verify();
    +}
    +
    +function testDoesntFireWhenWrongKeyIsPressed() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('letterjay', 'j');
    +  fire(KeyCodes.G);
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsControlAndLetterSpecifiedAsAString() {
    +  listener.shortcutFired('lettergee');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergee', 'ctrl+g');
    +  fire(KeyCodes.G, {ctrlKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsControlAndLetterSpecifiedAsArgSequence() {
    +  listener.shortcutFired('lettergeectrl');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeectrl',
    +      KeyCodes.G, Modifiers.CTRL);
    +  fire(KeyCodes.G, {ctrlKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsControlAndLetterSpecifiedAsArray() {
    +  listener.shortcutFired('lettergeectrl');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeectrl',
    +      [KeyCodes.G, Modifiers.CTRL]);
    +  fire(KeyCodes.G, {ctrlKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsShift() {
    +  listener.shortcutFired('lettergeeshift');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeeshift',
    +      [KeyCodes.G, Modifiers.SHIFT]);
    +  fire(KeyCodes.G, {shiftKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsAlt() {
    +  listener.shortcutFired('lettergeealt');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeealt',
    +      [KeyCodes.G, Modifiers.ALT]);
    +  fire(KeyCodes.G, {altKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMeta() {
    +  listener.shortcutFired('lettergeemeta');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeemeta',
    +      [KeyCodes.G, Modifiers.META]);
    +  fire(KeyCodes.G, {metaKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMultipleModifiers() {
    +  listener.shortcutFired('lettergeectrlaltshift');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeectrlaltshift',
    +      KeyCodes.G, Modifiers.CTRL | Modifiers.ALT | Modifiers.SHIFT);
    +  fire(KeyCodes.G, {ctrlKey: true, altKey: true, shiftKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMultipleModifiersSpecifiedAsString() {
    +  listener.shortcutFired('lettergeectrlaltshiftmeta');
    +  listener.$replay();
    +
    +  handler.registerShortcut('lettergeectrlaltshiftmeta',
    +      'ctrl+shift+alt+meta+g');
    +  fire(KeyCodes.G,
    +      {ctrlKey: true, altKey: true, shiftKey: true, metaKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testPreventsDefaultOnReturnFalse() {
    +  listener.shortcutFired('x');
    +  listener.$replay();
    +
    +  handler.registerShortcut('x', 'x');
    +  var key = goog.events.listen(handler,
    +      goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED,
    +      function(event) { return false });
    +
    +  assertFalse('return false in listener must prevent default',
    +              fire(KeyCodes.X));
    +
    +  listener.$verify();
    +
    +  goog.events.unlistenByKey(key);
    +}
    +
    +function testPreventsDefaultWhenExceptionThrown() {
    +  handler.registerShortcut('x', 'x');
    +  handler.setAlwaysPreventDefault(true);
    +  goog.events.listenOnce(handler,
    +      goog.ui.KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED,
    +      function(event) { throw new Error('x'); });
    +
    +  // We can't use the standard infrastructure to detect that
    +  // the event was preventDefaulted, because of the exception.
    +  var callCount = 0;
    +  stubs.set(goog.events.BrowserEvent.prototype, 'preventDefault',
    +      function() {
    +        callCount++;
    +      });
    +
    +  var e = assertThrows(goog.partial(fire, KeyCodes.X));
    +  assertEquals('x', e.message);
    +
    +  assertEquals(1, callCount);
    +}
    +
    +function testDoesntFireWhenUserForgetsRequiredModifier() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('lettergeectrl',
    +      KeyCodes.G, Modifiers.CTRL);
    +  fire(KeyCodes.G);
    +
    +  listener.$verify();
    +}
    +
    +function testDoesntFireIfTooManyModifiersPressed() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('lettergeectrl',
    +      KeyCodes.G, Modifiers.CTRL);
    +  fire(KeyCodes.G, {ctrlKey: true, metaKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testDoesntFireIfAnyRequiredModifierForgotten() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('lettergeectrlaltshift',
    +      KeyCodes.G, Modifiers.CTRL | Modifiers.ALT | Modifiers.SHIFT);
    +  fire(KeyCodes.G, {altKey: true, shiftKey: true});
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMultiKeySequenceSpecifiedAsArray() {
    +  listener.shortcutFired('quitemacs');
    +  listener.$replay();
    +
    +  handler.registerShortcut('quitemacs',
    +      [KeyCodes.X, Modifiers.CTRL,
    +       KeyCodes.C]);
    +  assertFalse(fire(KeyCodes.X, {ctrlKey: true}));
    +  fire(KeyCodes.C);
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMultiKeySequenceSpecifiedAsArguments() {
    +  listener.shortcutFired('quitvi');
    +  listener.$replay();
    +
    +  handler.registerShortcut('quitvi',
    +      KeyCodes.SEMICOLON, Modifiers.SHIFT,
    +      KeyCodes.Q, Modifiers.NONE,
    +      KeyCodes.NUM_ONE, Modifiers.SHIFT);
    +  var shiftProperties = { shiftKey: true };
    +  assertFalse(fire(KeyCodes.SEMICOLON, shiftProperties));
    +  assertFalse(fire(KeyCodes.Q));
    +  fire(KeyCodes.NUM_ONE, shiftProperties);
    +
    +  listener.$verify();
    +}
    +
    +function testMultiKeyEventIsNotFiredIfUserIsTooSlow() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('quitemacs',
    +      [KeyCodes.X, Modifiers.CTRL,
    +       KeyCodes.C]);
    +
    +  fire(KeyCodes.X, {ctrlKey: true});
    +
    +  // Wait 3 seconds before hitting C.  Although the actual limit is 1500
    +  // at time of writing, it's best not to over-specify functionality.
    +  mockClock.tick(3000);
    +
    +  fire(KeyCodes.C);
    +
    +  listener.$verify();
    +}
    +
    +function testAllowsMultipleAHandlers() {
    +  listener.shortcutFired('quitvi');
    +  listener.shortcutFired('letterex');
    +  listener.shortcutFired('quitemacs');
    +  listener.$replay();
    +
    +  // register 3 handlers in 3 diferent ways
    +  handler.registerShortcut('quitvi',
    +      KeyCodes.SEMICOLON, Modifiers.SHIFT,
    +      KeyCodes.Q, Modifiers.NONE,
    +      KeyCodes.NUM_ONE, Modifiers.SHIFT);
    +  handler.registerShortcut('quitemacs',
    +      [KeyCodes.X, Modifiers.CTRL,
    +       KeyCodes.C]);
    +  handler.registerShortcut('letterex', 'x');
    +
    +
    +  // quit vi
    +  var shiftProperties = { shiftKey: true };
    +  fire(KeyCodes.SEMICOLON, shiftProperties);
    +  fire(KeyCodes.Q);
    +  fire(KeyCodes.NUM_ONE, shiftProperties);
    +
    +  // then press the letter x
    +  fire(KeyCodes.X);
    +
    +  // then quit emacs
    +  fire(KeyCodes.X, {ctrlKey: true});
    +  fire(KeyCodes.C);
    +
    +  listener.$verify();
    +}
    +
    +function testCanRemoveOneHandler() {
    +  listener.shortcutFired('letterex');
    +  listener.$replay();
    +
    +  // register 2 handlers, then remove quitvi
    +  handler.registerShortcut('quitvi',
    +      KeyCodes.COLON, Modifiers.NONE,
    +      KeyCodes.Q, Modifiers.NONE,
    +      KeyCodes.EXCLAMATION, Modifiers.NONE);
    +  handler.registerShortcut('letterex', 'x');
    +  handler.unregisterShortcut(
    +      KeyCodes.COLON, Modifiers.NONE,
    +      KeyCodes.Q, Modifiers.NONE,
    +      KeyCodes.EXCLAMATION, Modifiers.NONE);
    +
    +  // call the "quit VI" keycodes, even though it is removed
    +  fire(KeyCodes.COLON);
    +  fire(KeyCodes.Q);
    +  fire(KeyCodes.EXCLAMATION);
    +
    +  // press the letter x
    +  fire(KeyCodes.X);
    +
    +  listener.$verify();
    +}
    +
    +function testCanRemoveTwoHandlers() {
    +  listener.$replay(); // no events expected
    +
    +  handler.registerShortcut('quitemacs',
    +      [KeyCodes.X, Modifiers.CTRL,
    +       KeyCodes.C]);
    +  handler.registerShortcut('letterex', 'x');
    +  handler.unregisterShortcut(
    +      [KeyCodes.X, Modifiers.CTRL,
    +       KeyCodes.C]);
    +  handler.unregisterShortcut('x');
    +
    +  fire(KeyCodes.X, {ctrlKey: true});
    +  fire(KeyCodes.C);
    +  fire(KeyCodes.X);
    +
    +  listener.$verify();
    +}
    +
    +function testIsShortcutRegistered_single() {
    +  assertFalse(handler.isShortcutRegistered('x'));
    +  handler.registerShortcut('letterex', 'x');
    +  assertTrue(handler.isShortcutRegistered('x'));
    +  handler.unregisterShortcut('x');
    +  assertFalse(handler.isShortcutRegistered('x'));
    +}
    +
    +function testIsShortcutRegistered_multi() {
    +  assertFalse(handler.isShortcutRegistered('a'));
    +  assertFalse(handler.isShortcutRegistered('a b'));
    +  assertFalse(handler.isShortcutRegistered('a b c'));
    +
    +  handler.registerShortcut('ab', 'a b');
    +
    +  assertFalse(handler.isShortcutRegistered('a'));
    +  assertTrue(handler.isShortcutRegistered('a b'));
    +  assertFalse(handler.isShortcutRegistered('a b c'));
    +
    +  handler.unregisterShortcut('a b');
    +
    +  assertFalse(handler.isShortcutRegistered('a'));
    +  assertFalse(handler.isShortcutRegistered('a b'));
    +  assertFalse(handler.isShortcutRegistered('a b c'));
    +}
    +
    +function testUnregister_subsequence() {
    +  // Unregistering a partial sequence should not orphan shortcuts further in the
    +  // sequence.
    +  handler.registerShortcut('abc', 'a b c');
    +  handler.unregisterShortcut('a b');
    +  assertTrue(handler.isShortcutRegistered('a b c'));
    +}
    +
    +function testUnregister_supersequence() {
    +  // Unregistering a sequence that extends beyond a registered sequence should
    +  // do nothing.
    +  handler.registerShortcut('ab', 'a b');
    +  handler.unregisterShortcut('a b c');
    +  assertTrue(handler.isShortcutRegistered('a b'));
    +}
    +
    +function testUnregister_partialMatchSequence() {
    +  // Unregistering a sequence that partially matches a registered sequence
    +  // should do nothing.
    +  handler.registerShortcut('abc', 'a b c');
    +  handler.unregisterShortcut('a b x');
    +  assertTrue(handler.isShortcutRegistered('a b c'));
    +}
    +
    +function testUnregister_deadBranch() {
    +  // Unregistering a sequence should prune any dead branches in the tree.
    +  handler.registerShortcut('abc', 'a b c');
    +  handler.unregisterShortcut('a b c');
    +  // Default is not should not be prevented in the A key stroke because the A
    +  // branch has been removed from the tree.
    +  assertTrue(fire(KeyCodes.A));
    +}
    +
    +
    +/**
    + * Registers a slew of keyboard shortcuts to test each primary category
    + * of shortcuts.
    + */
    +function registerEnterSpaceXF1AltY() {
    +  // Enter and space are specially handled keys.
    +  handler.registerShortcut('enter', KeyCodes.ENTER);
    +  handler.registerShortcut('space', KeyCodes.SPACE);
    +  // 'x' should be treated as text in many contexts
    +  handler.registerShortcut('x', 'x');
    +  // F1 is a global shortcut.
    +  handler.registerShortcut('global', KeyCodes.F1);
    +  // Alt-Y has modifiers, which pass through most form elements.
    +  handler.registerShortcut('withAlt', 'alt+y');
    +}
    +
    +
    +/**
    + * Fires enter, space, X, F1, and Alt-Y keys on a widget.
    + * @param {Element} target The element on which to fire the events.
    + */
    +function fireEnterSpaceXF1AltY(target) {
    +  fire(KeyCodes.ENTER, undefined, target);
    +  fire(KeyCodes.SPACE, undefined, target);
    +  fire(KeyCodes.X, undefined, target);
    +  fire(KeyCodes.F1, undefined, target);
    +  fire(KeyCodes.Y, {altKey: true}, target);
    +}
    +
    +function testIgnoreNonGlobalShortcutsInSelect() {
    +  var targetSelect = goog.dom.getElement('targetSelect');
    +
    +  listener.shortcutFired('global');
    +  listener.shortcutFired('withAlt');
    +  listener.$replay();
    +
    +  registerEnterSpaceXF1AltY();
    +  fireEnterSpaceXF1AltY(goog.dom.getElement('targetSelect'));
    +
    +  listener.$verify();
    +}
    +
    +function testIgnoreNonGlobalShortcutsInTextArea() {
    +  listener.shortcutFired('global');
    +  listener.shortcutFired('withAlt');
    +  listener.$replay();
    +
    +  registerEnterSpaceXF1AltY();
    +  fireEnterSpaceXF1AltY(goog.dom.getElement('targetTextArea'));
    +
    +  listener.$verify();
    +}
    +
    +
    +/**
    + * Checks that the shortcuts are fired on each target.
    + * @param {Array<string>} shortcuts A list of shortcut identifiers.
    + * @param {Array<string>} targets A list of element IDs.
    + * @param {function(Element)} fireEvents Function that fires events.
    + */
    +function expectShortcutsOnTargets(shortcuts, targets, fireEvents) {
    +  for (var i = 0, ii = targets.length; i < ii; i++) {
    +    for (var j = 0, jj = shortcuts.length; j < jj; j++) {
    +      listener.shortcutFired(shortcuts[j]);
    +    }
    +    listener.$replay();
    +    fireEvents(goog.dom.getElement(targets[i]));
    +    listener.$verify();
    +    listener.$reset();
    +  }
    +}
    +
    +function testIgnoreShortcutsExceptEnterInTextInputFields() {
    +  var targets = [
    +    'targetColor',
    +    'targetDate',
    +    'targetDateTime',
    +    'targetDateTimeLocal',
    +    'targetEmail',
    +    'targetMonth',
    +    'targetNumber',
    +    'targetPassword',
    +    'targetSearch',
    +    'targetTel',
    +    'targetText',
    +    'targetTime',
    +    'targetUrl',
    +    'targetWeek'
    +  ];
    +  registerEnterSpaceXF1AltY();
    +  expectShortcutsOnTargets(
    +      ['enter', 'global', 'withAlt'], targets, fireEnterSpaceXF1AltY);
    +}
    +
    +function testIgnoreSpaceInCheckBoxAndButton() {
    +  registerEnterSpaceXF1AltY();
    +  expectShortcutsOnTargets(
    +      ['enter', 'x', 'global', 'withAlt'],
    +      ['targetCheckBox', 'targetButton'],
    +      fireEnterSpaceXF1AltY);
    +}
    +
    +function testIgnoreNonGlobalShortcutsInContentEditable() {
    +  // Don't set design mode in later IE as javascripts don't run when in
    +  // that mode.
    +  var setDesignMode = !goog.userAgent.IE ||
    +      !goog.userAgent.isVersionOrHigher('9');
    +  try {
    +    if (setDesignMode) {
    +      document.designMode = 'on';
    +    }
    +    targetDiv.contentEditable = 'true';
    +
    +    // Expect only global shortcuts.
    +    listener.shortcutFired('global');
    +    listener.$replay();
    +
    +    registerEnterSpaceXF1AltY();
    +    fireEnterSpaceXF1AltY(targetDiv);
    +
    +    listener.$verify();
    +  } finally {
    +    if (setDesignMode) {
    +      document.designMode = 'off';
    +    }
    +    targetDiv.contentEditable = 'false';
    +  }
    +}
    +
    +function testSetAllShortcutsAreGlobal() {
    +  handler.setAllShortcutsAreGlobal(true);
    +  registerEnterSpaceXF1AltY();
    +
    +  expectShortcutsOnTargets(
    +      ['enter', 'space', 'x', 'global', 'withAlt'], ['targetTextArea'],
    +      fireEnterSpaceXF1AltY);
    +}
    +
    +function testSetModifierShortcutsAreGlobalFalse() {
    +  handler.setModifierShortcutsAreGlobal(false);
    +  registerEnterSpaceXF1AltY();
    +
    +  expectShortcutsOnTargets(
    +      ['global'], ['targetTextArea'], fireEnterSpaceXF1AltY);
    +}
    +
    +function testAltGraphKeyOnUSLayout() {
    +  // Windows does not assign printable characters to any ctrl+alt keys of
    +  // the US layout. This test verifies we fire shortcut events when typing
    +  // ctrl+alt keys on the US layout.
    +  listener.shortcutFired('letterOne');
    +  listener.shortcutFired('letterTwo');
    +  listener.shortcutFired('letterThree');
    +  listener.shortcutFired('letterFour');
    +  listener.shortcutFired('letterFive');
    +  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    +    listener.$replay();
    +
    +    handler.registerShortcut('letterOne', 'ctrl+alt+1');
    +    handler.registerShortcut('letterTwo', 'ctrl+alt+2');
    +    handler.registerShortcut('letterThree', 'ctrl+alt+3');
    +    handler.registerShortcut('letterFour', 'ctrl+alt+4');
    +    handler.registerShortcut('letterFive', 'ctrl+alt+5');
    +
    +    // Send key events on the English (United States) layout.
    +    fireAltGraphKey(KeyCodes.ONE, 0, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.TWO, 0, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.THREE, 0, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FOUR, 0, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FIVE, 0, {ctrlKey: true, altKey: true});
    +
    +    listener.$verify();
    +  }
    +}
    +
    +function testAltGraphKeyOnFrenchLayout() {
    +  // Windows assigns printable characters to ctrl+alt+[2-5] keys of the
    +  // French layout. This test verifies we fire shortcut events only when
    +  // we type ctrl+alt+1 keys on the French layout.
    +  listener.shortcutFired('letterOne');
    +  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    +    listener.$replay();
    +
    +    handler.registerShortcut('letterOne', 'ctrl+alt+1');
    +    handler.registerShortcut('letterTwo', 'ctrl+alt+2');
    +    handler.registerShortcut('letterThree', 'ctrl+alt+3');
    +    handler.registerShortcut('letterFour', 'ctrl+alt+4');
    +    handler.registerShortcut('letterFive', 'ctrl+alt+5');
    +
    +    // Send key events on the French (France) layout.
    +    fireAltGraphKey(KeyCodes.ONE, 0, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.TWO, 0x0303, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.THREE, 0x0023, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FOUR, 0x007b, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FIVE, 0x205b, {ctrlKey: true, altKey: true});
    +
    +    listener.$verify();
    +  }
    +}
    +
    +function testAltGraphKeyOnSpanishLayout() {
    +  // Windows assigns printable characters to ctrl+alt+[1-5] keys of the
    +  // Spanish layout. This test verifies we do not fire shortcut events at
    +  // all when typing ctrl+alt+[1-5] keys on the Spanish layout.
    +  if (goog.userAgent.WINDOWS && !goog.userAgent.GECKO) {
    +    listener.$replay();
    +
    +    handler.registerShortcut('letterOne', 'ctrl+alt+1');
    +    handler.registerShortcut('letterTwo', 'ctrl+alt+2');
    +    handler.registerShortcut('letterThree', 'ctrl+alt+3');
    +    handler.registerShortcut('letterFour', 'ctrl+alt+4');
    +    handler.registerShortcut('letterFive', 'ctrl+alt+5');
    +
    +    // Send key events on the Spanish (Spain) layout.
    +    fireAltGraphKey(KeyCodes.ONE, 0x007c, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.TWO, 0x0040, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.THREE, 0x0023, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FOUR, 0x0303, {ctrlKey: true, altKey: true});
    +    fireAltGraphKey(KeyCodes.FIVE, 0x20ac, {ctrlKey: true, altKey: true});
    +
    +    listener.$verify();
    +  }
    +}
    +
    +function testNumpadKeyShortcuts() {
    +  var testCases = [
    +    ['letterNumpad0', 'num-0', KeyCodes.NUM_ZERO],
    +    ['letterNumpad1', 'num-1', KeyCodes.NUM_ONE],
    +    ['letterNumpad2', 'num-2', KeyCodes.NUM_TWO],
    +    ['letterNumpad3', 'num-3', KeyCodes.NUM_THREE],
    +    ['letterNumpad4', 'num-4', KeyCodes.NUM_FOUR],
    +    ['letterNumpad5', 'num-5', KeyCodes.NUM_FIVE],
    +    ['letterNumpad6', 'num-6', KeyCodes.NUM_SIX],
    +    ['letterNumpad7', 'num-7', KeyCodes.NUM_SEVEN],
    +    ['letterNumpad8', 'num-8', KeyCodes.NUM_EIGHT],
    +    ['letterNumpad9', 'num-9', KeyCodes.NUM_NINE],
    +    ['letterNumpadMultiply', 'num-multiply', KeyCodes.NUM_MULTIPLY],
    +    ['letterNumpadPlus', 'num-plus', KeyCodes.NUM_PLUS],
    +    ['letterNumpadMinus', 'num-minus', KeyCodes.NUM_MINUS],
    +    ['letterNumpadPERIOD', 'num-period', KeyCodes.NUM_PERIOD],
    +    ['letterNumpadDIVISION', 'num-division', KeyCodes.NUM_DIVISION]
    +  ];
    +  for (var i = 0; i < testCases.length; ++i) {
    +    listener.shortcutFired(testCases[i][0]);
    +  }
    +  listener.$replay();
    +
    +  // Register shortcuts for numpad keys and send numpad-key events.
    +  for (var i = 0; i < testCases.length; ++i) {
    +    handler.registerShortcut(testCases[i][0], testCases[i][1]);
    +    fire(testCases[i][2]);
    +  }
    +  listener.$verify();
    +}
    +
    +function testGeckoShortcuts() {
    +  listener.shortcutFired('1');
    +  listener.$replay();
    +
    +  handler.registerShortcut('1', 'semicolon');
    +
    +  if (goog.userAgent.GECKO) {
    +    fire(goog.events.KeyCodes.FF_SEMICOLON);
    +  } else {
    +    fire(goog.events.KeyCodes.SEMICOLON);
    +  }
    +
    +  listener.$verify();
    +}
    +
    +function testRegisterShortcut_modifierOnly() {
    +  assertThrows('Registering a shortcut with just modifiers should fail.',
    +      goog.bind(handler.registerShortcut, handler, 'name', 'Shift'));
    +}
    +
    +function testParseStringShortcut_unknownKey() {
    +  assertThrows('Unknown keys should fail.', goog.bind(
    +      goog.ui.KeyboardShortcutHandler.parseStringShortcut, null, 'NotAKey'));
    +}
    +
    +// Regression test for failure to reset keyCode between strokes.
    +function testParseStringShortcut_resetKeyCode() {
    +  var strokes = goog.ui.KeyboardShortcutHandler.parseStringShortcut('A Shift');
    +  assertNull('The second stroke only has a modifier key.', strokes[1].keyCode);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/labelinput.js b/mapbox-gl/closure-library/closure/goog/ui/labelinput.js
    new file mode 100644
    index 0000000000..a894f41fdf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/labelinput.js
    @@ -0,0 +1,612 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview This behavior is applied to a text input and it shows a text
    + * message inside the element if the user hasn't entered any text.
    + *
    + * This uses the HTML5 placeholder attribute where it is supported.
    + *
    + * This is ported from http://go/labelinput.js
    + *
    + * Known issue: Safari does not allow you get to the window object from a
    + * document. We need that to listen to the onload event. For now we hard code
    + * the window to the current window.
    + *
    + * Known issue: We need to listen to the form submit event but we attach the
    + * event only once (when created or when it is changed) so if you move the DOM
    + * node to another form it will not be cleared correctly before submitting.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/labelinput.html
    + */
    +
    +goog.provide('goog.ui.LabelInput');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This creates the label input object.
    + * @param {string=} opt_label The text to show as the label.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.LabelInput = function(opt_label, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The text to show as the label.
    +   * @type {string}
    +   * @private
    +   */
    +  this.label_ = opt_label || '';
    +};
    +goog.inherits(goog.ui.LabelInput, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.LabelInput);
    +
    +
    +/**
    + * Variable used to store the element value on keydown and restore it on
    + * keypress.  See {@link #handleEscapeKeys_}
    + * @type {?string}
    + * @private
    + */
    +goog.ui.LabelInput.prototype.ffKeyRestoreValue_ = null;
    +
    +
    +/**
    + * The label restore delay after leaving the input.
    + * @type {number} Delay for restoring the label.
    + * @protected
    + */
    +goog.ui.LabelInput.prototype.labelRestoreDelayMs = 10;
    +
    +
    +/** @private {boolean} */
    +goog.ui.LabelInput.prototype.inFocusAndSelect_;
    +
    +
    +/** @private {boolean} */
    +goog.ui.LabelInput.prototype.formAttached_;
    +
    +
    +/**
    + * Indicates whether the browser supports the placeholder attribute, new in
    + * HTML5.
    + * @type {?boolean}
    + * @private
    + */
    +goog.ui.LabelInput.SUPPORTS_PLACEHOLDER_;
    +
    +
    +/**
    + * Checks browser support for placeholder attribute.
    + * @return {boolean} Whether placeholder attribute is supported.
    + * @private
    + */
    +goog.ui.LabelInput.isPlaceholderSupported_ = function() {
    +  if (!goog.isDefAndNotNull(goog.ui.LabelInput.SUPPORTS_PLACEHOLDER_)) {
    +    goog.ui.LabelInput.SUPPORTS_PLACEHOLDER_ = (
    +        'placeholder' in document.createElement('input'));
    +  }
    +  return goog.ui.LabelInput.SUPPORTS_PLACEHOLDER_;
    +};
    +
    +
    +/**
    + * @type {goog.events.EventHandler}
    + * @private
    + */
    +goog.ui.LabelInput.prototype.eventHandler_;
    +
    +
    +/**
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.LabelInput.prototype.hasFocus_ = false;
    +
    +
    +/**
    + * Creates the DOM nodes needed for the label input.
    + * @override
    + */
    +goog.ui.LabelInput.prototype.createDom = function() {
    +  this.setElementInternal(
    +      this.getDomHelper().createDom('input', {'type': 'text'}));
    +};
    +
    +
    +/**
    + * Decorates an existing HTML input element as a label input. If the element
    + * has a "label" attribute then that will be used as the label property for the
    + * label input object.
    + * @param {Element} element The HTML input element to decorate.
    + * @override
    + */
    +goog.ui.LabelInput.prototype.decorateInternal = function(element) {
    +  goog.ui.LabelInput.superClass_.decorateInternal.call(this, element);
    +  if (!this.label_) {
    +    this.label_ = element.getAttribute('label') || '';
    +  }
    +
    +  // Check if we're attaching to an element that already has focus.
    +  if (goog.dom.getActiveElement(goog.dom.getOwnerDocument(element)) ==
    +      element) {
    +    this.hasFocus_ = true;
    +    var el = this.getElement();
    +    goog.asserts.assert(el);
    +    goog.dom.classlist.remove(el, this.labelCssClassName);
    +  }
    +
    +  if (goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    this.getElement().placeholder = this.label_;
    +  }
    +  var labelInputElement = this.getElement();
    +  goog.asserts.assert(labelInputElement,
    +      'The label input element cannot be null.');
    +  goog.a11y.aria.setState(labelInputElement,
    +      goog.a11y.aria.State.LABEL,
    +      this.label_);
    +};
    +
    +
    +/** @override */
    +goog.ui.LabelInput.prototype.enterDocument = function() {
    +  goog.ui.LabelInput.superClass_.enterDocument.call(this);
    +  this.attachEvents_();
    +  this.check_();
    +
    +  // Make it easy for other closure widgets to play nicely with inputs using
    +  // LabelInput:
    +  this.getElement().labelInput_ = this;
    +};
    +
    +
    +/** @override */
    +goog.ui.LabelInput.prototype.exitDocument = function() {
    +  goog.ui.LabelInput.superClass_.exitDocument.call(this);
    +  this.detachEvents_();
    +
    +  this.getElement().labelInput_ = null;
    +};
    +
    +
    +/**
    + * Attaches the events we need to listen to.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.attachEvents_ = function() {
    +  var eh = new goog.events.EventHandler(this);
    +  eh.listen(this.getElement(), goog.events.EventType.FOCUS, this.handleFocus_);
    +  eh.listen(this.getElement(), goog.events.EventType.BLUR, this.handleBlur_);
    +
    +  if (goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    this.eventHandler_ = eh;
    +    return;
    +  }
    +
    +  if (goog.userAgent.GECKO) {
    +    eh.listen(this.getElement(), [
    +      goog.events.EventType.KEYPRESS,
    +      goog.events.EventType.KEYDOWN,
    +      goog.events.EventType.KEYUP
    +    ], this.handleEscapeKeys_);
    +  }
    +
    +  // IE sets defaultValue upon load so we need to test that as well.
    +  var d = goog.dom.getOwnerDocument(this.getElement());
    +  var w = goog.dom.getWindow(d);
    +  eh.listen(w, goog.events.EventType.LOAD, this.handleWindowLoad_);
    +
    +  this.eventHandler_ = eh;
    +  this.attachEventsToForm_();
    +};
    +
    +
    +/**
    + * Adds a listener to the form so that we can clear the input before it is
    + * submitted.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.attachEventsToForm_ = function() {
    +  // in case we have are in a form we need to make sure the label is not
    +  // submitted
    +  if (!this.formAttached_ && this.eventHandler_ && this.getElement().form) {
    +    this.eventHandler_.listen(this.getElement().form,
    +                              goog.events.EventType.SUBMIT,
    +                              this.handleFormSubmit_);
    +    this.formAttached_ = true;
    +  }
    +};
    +
    +
    +/**
    + * Stops listening to the events.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.detachEvents_ = function() {
    +  if (this.eventHandler_) {
    +    this.eventHandler_.dispose();
    +    this.eventHandler_ = null;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.LabelInput.prototype.disposeInternal = function() {
    +  goog.ui.LabelInput.superClass_.disposeInternal.call(this);
    +  this.detachEvents_();
    +};
    +
    +
    +/**
    + * The CSS class name to add to the input when the user has not entered a
    + * value.
    + */
    +goog.ui.LabelInput.prototype.labelCssClassName =
    +    goog.getCssName('label-input-label');
    +
    +
    +/**
    + * Handler for the focus event.
    + * @param {goog.events.Event} e The event object passed in to the event handler.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleFocus_ = function(e) {
    +  this.hasFocus_ = true;
    +  var el = this.getElement();
    +  goog.asserts.assert(el);
    +  goog.dom.classlist.remove(el, this.labelCssClassName);
    +  if (goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    return;
    +  }
    +  if (!this.hasChanged() && !this.inFocusAndSelect_) {
    +    var me = this;
    +    var clearValue = function() {
    +      // Component could be disposed by the time this is called.
    +      if (me.getElement()) {
    +        me.getElement().value = '';
    +      }
    +    };
    +    if (goog.userAgent.IE) {
    +      goog.Timer.callOnce(clearValue, 10);
    +    } else {
    +      clearValue();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for the blur event.
    + * @param {goog.events.Event} e The event object passed in to the event handler.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleBlur_ = function(e) {
    +  // We listen to the click event when we enter focusAndSelect mode so we can
    +  // fake an artificial focus when the user clicks on the input box. However,
    +  // if the user clicks on something else (and we lose focus), there is no
    +  // need for an artificial focus event.
    +  if (!goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    this.eventHandler_.unlisten(
    +        this.getElement(), goog.events.EventType.CLICK, this.handleFocus_);
    +    this.ffKeyRestoreValue_ = null;
    +  }
    +  this.hasFocus_ = false;
    +  this.check_();
    +};
    +
    +
    +/**
    + * Handler for key events in Firefox.
    + *
    + * If the escape key is pressed when a text input has not been changed manually
    + * since being focused, the text input will revert to its previous value.
    + * Firefox does not honor preventDefault for the escape key. The revert happens
    + * after the keydown event and before every keypress. We therefore store the
    + * element's value on keydown and restore it on keypress. The restore value is
    + * nullified on keyup so that {@link #getValue} returns the correct value.
    + *
    + * IE and Chrome don't have this problem, Opera blurs in the input box
    + * completely in a way that preventDefault on the escape key has no effect.
    + *
    + * @param {goog.events.BrowserEvent} e The event object passed in to
    + *     the event handler.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleEscapeKeys_ = function(e) {
    +  if (e.keyCode == 27) {
    +    if (e.type == goog.events.EventType.KEYDOWN) {
    +      this.ffKeyRestoreValue_ = this.getElement().value;
    +    } else if (e.type == goog.events.EventType.KEYPRESS) {
    +      this.getElement().value = /** @type {string} */ (this.ffKeyRestoreValue_);
    +    } else if (e.type == goog.events.EventType.KEYUP) {
    +      this.ffKeyRestoreValue_ = null;
    +    }
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Handler for the submit event of the form element.
    + * @param {goog.events.Event} e The event object passed in to the event handler.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleFormSubmit_ = function(e) {
    +  if (!this.hasChanged()) {
    +    this.getElement().value = '';
    +    // allow form to be sent before restoring value
    +    goog.Timer.callOnce(this.handleAfterSubmit_, 10, this);
    +  }
    +};
    +
    +
    +/**
    + * Restore value after submit
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleAfterSubmit_ = function() {
    +  if (!this.hasChanged()) {
    +    this.getElement().value = this.label_;
    +  }
    +};
    +
    +
    +/**
    + * Handler for the load event the window. This is needed because
    + * IE sets defaultValue upon load.
    + * @param {Event} e The event object passed in to the event handler.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.handleWindowLoad_ = function(e) {
    +  this.check_();
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the control is currently focused on.
    + */
    +goog.ui.LabelInput.prototype.hasFocus = function() {
    +  return this.hasFocus_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the value has been changed by the user.
    + */
    +goog.ui.LabelInput.prototype.hasChanged = function() {
    +  return !!this.getElement() && this.getElement().value != '' &&
    +      this.getElement().value != this.label_;
    +};
    +
    +
    +/**
    + * Clears the value of the input element without resetting the default text.
    + */
    +goog.ui.LabelInput.prototype.clear = function() {
    +  this.getElement().value = '';
    +
    +  // Reset ffKeyRestoreValue_ when non-null
    +  if (this.ffKeyRestoreValue_ != null) {
    +    this.ffKeyRestoreValue_ = '';
    +  }
    +};
    +
    +
    +/**
    + * Clears the value of the input element and resets the default text.
    + */
    +goog.ui.LabelInput.prototype.reset = function() {
    +  if (this.hasChanged()) {
    +    this.clear();
    +    this.check_();
    +  }
    +};
    +
    +
    +/**
    + * Use this to set the value through script to ensure that the label state is
    + * up to date
    + * @param {string} s The new value for the input.
    + */
    +goog.ui.LabelInput.prototype.setValue = function(s) {
    +  if (this.ffKeyRestoreValue_ != null) {
    +    this.ffKeyRestoreValue_ = s;
    +  }
    +  this.getElement().value = s;
    +  this.check_();
    +};
    +
    +
    +/**
    + * Returns the current value of the text box, returning an empty string if the
    + * search box is the default value
    + * @return {string} The value of the input box.
    + */
    +goog.ui.LabelInput.prototype.getValue = function() {
    +  if (this.ffKeyRestoreValue_ != null) {
    +    // Fix the Firefox from incorrectly reporting the value to calling code
    +    // that attached the listener to keypress before the labelinput
    +    return this.ffKeyRestoreValue_;
    +  }
    +  return this.hasChanged() ? /** @type {string} */ (this.getElement().value) :
    +      '';
    +};
    +
    +
    +/**
    + * Sets the label text as aria-label, and placeholder when supported.
    + * @param {string} label The text to show as the label.
    + */
    +goog.ui.LabelInput.prototype.setLabel = function(label) {
    +  var labelInputElement = this.getElement();
    +
    +  if (goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    if (labelInputElement) {
    +      labelInputElement.placeholder = label;
    +    }
    +    this.label_ = label;
    +  } else if (!this.hasChanged()) {
    +    // The this.hasChanged() call relies on non-placeholder behavior checking
    +    // prior to setting this.label_ - it also needs to happen prior to the
    +    // this.restoreLabel_() call.
    +    if (labelInputElement) {
    +      labelInputElement.value = '';
    +    }
    +    this.label_ = label;
    +    this.restoreLabel_();
    +  }
    +  // Check if this has been called before DOM structure building
    +  if (labelInputElement) {
    +    goog.a11y.aria.setState(labelInputElement,
    +        goog.a11y.aria.State.LABEL,
    +        this.label_);
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The text to show as the label.
    + */
    +goog.ui.LabelInput.prototype.getLabel = function() {
    +  return this.label_;
    +};
    +
    +
    +/**
    + * Checks the state of the input element
    + * @private
    + */
    +goog.ui.LabelInput.prototype.check_ = function() {
    +  var labelInputElement = this.getElement();
    +  goog.asserts.assert(labelInputElement,
    +      'The label input element cannot be null.');
    +  if (!goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    // if we haven't got a form yet try now
    +    this.attachEventsToForm_();
    +  } else if (this.getElement().placeholder != this.label_) {
    +    this.getElement().placeholder = this.label_;
    +  }
    +  goog.a11y.aria.setState(labelInputElement,
    +      goog.a11y.aria.State.LABEL,
    +      this.label_);
    +
    +  if (!this.hasChanged()) {
    +    if (!this.inFocusAndSelect_ && !this.hasFocus_) {
    +      var el = this.getElement();
    +      goog.asserts.assert(el);
    +      goog.dom.classlist.add(el, this.labelCssClassName);
    +    }
    +
    +    // Allow browser to catchup with CSS changes before restoring the label.
    +    if (!goog.ui.LabelInput.isPlaceholderSupported_()) {
    +      goog.Timer.callOnce(this.restoreLabel_, this.labelRestoreDelayMs,
    +          this);
    +    }
    +  } else {
    +    var el = this.getElement();
    +    goog.asserts.assert(el);
    +    goog.dom.classlist.remove(el, this.labelCssClassName);
    +  }
    +};
    +
    +
    +/**
    + * This method focuses the input and selects all the text. If the value hasn't
    + * changed it will set the value to the label so that the label text is
    + * selected.
    + */
    +goog.ui.LabelInput.prototype.focusAndSelect = function() {
    +  // We need to check whether the input has changed before focusing
    +  var hc = this.hasChanged();
    +  this.inFocusAndSelect_ = true;
    +  this.getElement().focus();
    +  if (!hc && !goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    this.getElement().value = this.label_;
    +  }
    +  this.getElement().select();
    +
    +  // Since the object now has focus, we won't get a focus event when they
    +  // click in the input element. The expected behavior when you click on
    +  // the default text is that it goes away and allows you to type...so we
    +  // have to fire an artificial focus event when we're in focusAndSelect mode.
    +  if (goog.ui.LabelInput.isPlaceholderSupported_()) {
    +    return;
    +  }
    +  if (this.eventHandler_) {
    +    this.eventHandler_.listenOnce(
    +        this.getElement(), goog.events.EventType.CLICK, this.handleFocus_);
    +  }
    +
    +  // set to false in timer to let IE trigger the focus event
    +  goog.Timer.callOnce(this.focusAndSelect_, 10, this);
    +};
    +
    +
    +/**
    + * Enables/Disables the label input.
    + * @param {boolean} enabled Whether to enable (true) or disable (false) the
    + *     label input.
    + */
    +goog.ui.LabelInput.prototype.setEnabled = function(enabled) {
    +  this.getElement().disabled = !enabled;
    +  var el = this.getElement();
    +  goog.asserts.assert(el);
    +  goog.dom.classlist.enable(el,
    +      goog.getCssName(this.labelCssClassName, 'disabled'), !enabled);
    +};
    +
    +
    +/**
    + * @return {boolean} True if the label input is enabled, false otherwise.
    + */
    +goog.ui.LabelInput.prototype.isEnabled = function() {
    +  return !this.getElement().disabled;
    +};
    +
    +
    +/**
    + * @private
    + */
    +goog.ui.LabelInput.prototype.focusAndSelect_ = function() {
    +  this.inFocusAndSelect_ = false;
    +};
    +
    +
    +/**
    + * Sets the value of the input element to label.
    + * @private
    + */
    +goog.ui.LabelInput.prototype.restoreLabel_ = function() {
    +  // Check again in case something changed since this was scheduled.
    +  // We check that the element is still there since this is called by a timer
    +  // and the dispose method may have been called prior to this.
    +  if (this.getElement() && !this.hasChanged() && !this.hasFocus_) {
    +    this.getElement().value = this.label_;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.html b/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.html
    new file mode 100644
    index 0000000000..74f8ef011b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.LabelInput
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.LabelInputTest');
    +  </script>
    + </head>
    + <body>
    +  <input id="i" type="text" />
    +  <input id="p" type="text" placeholder="browser-ignorant placeholder" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.js b/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.js
    new file mode 100644
    index 0000000000..d739feda48
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/labelinput_test.js
    @@ -0,0 +1,250 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.LabelInputTest');
    +goog.setTestOnly('goog.ui.LabelInputTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.LabelInput');
    +goog.require('goog.userAgent');
    +
    +var labelInput;
    +var mockClock;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDown() {
    +  mockClock.dispose();
    +  labelInput.dispose();
    +}
    +
    +function testGetLabel() {
    +  labelInput = new goog.ui.LabelInput();
    +  assertEquals('no label', '', labelInput.getLabel());
    +
    +  labelInput = new goog.ui.LabelInput('search');
    +  assertEquals('label is given in the ctor', 'search', labelInput.getLabel());
    +}
    +
    +function testSetLabel() {
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.setLabel('search');
    +  assertEquals('label is set', 'search', labelInput.getLabel());
    +
    +  labelInput.createDom();
    +  labelInput.enterDocument();
    +  mockClock.tick(10);
    +  assertNotNull(labelInput.getElement());
    +  assertLabelValue(labelInput, 'search');
    +
    +  labelInput.setLabel('new label');
    +  assertLabelValue(labelInput, 'new label');
    +}
    +
    +function assertLabelValue(labelInput, expectedLabel) {
    +  assertEquals(
    +      'label should have aria-label attribute \'' + expectedLabel + '\'',
    +      expectedLabel, goog.a11y.aria.getState(labelInput.getElement(),
    +          goog.a11y.aria.State.LABEL));
    +  // When browsers support the placeholder attribute, we use that instead of
    +  // the value property - and this test will fail.
    +  if (!isPlaceholderSupported()) {
    +    assertEquals(
    +        'label is updated', expectedLabel, labelInput.getElement().value);
    +  } else {
    +    assertEquals('value is empty', '', labelInput.getElement().value);
    +  }
    +}
    +
    +function testPlaceholderAttribute() {
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.setLabel('search');
    +
    +  // Some browsers don't support the placeholder attribute, in which case we
    +  // this test will fail.
    +  if (! isPlaceholderSupported()) {
    +    return;
    +  }
    +
    +  labelInput.createDom();
    +  labelInput.enterDocument();
    +  mockClock.tick(10);
    +  assertEquals('label should have placeholder attribute \'search\'', 'search',
    +      labelInput.getElement().placeholder);
    +
    +  labelInput.setLabel('new label');
    +  assertEquals('label should have aria-label attribute \'new label\'',
    +      'new label', labelInput.getElement().placeholder);
    +}
    +
    +function testDecorateElementWithExistingPlaceholderAttribute() {
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.setLabel('search');
    +
    +  labelInput.decorate(goog.dom.getElement('p'));
    +  labelInput.enterDocument();
    +  mockClock.tick(10);
    +
    +  // The presence of an existing placeholder doesn't necessarily mean the
    +  // browser supports placeholders. Make sure labels are used for browsers
    +  // without placeholder support:
    +  if (isPlaceholderSupported()) {
    +    assertEquals('label should have placeholder attribute \'search\'', 'search',
    +        labelInput.getElement().placeholder);
    +  } else {
    +    assertNotNull(labelInput.getElement());
    +    assertEquals('label is rendered', 'search', labelInput.getElement().value);
    +    assertEquals('label should have aria-label attribute \'search\'', 'search',
    +        goog.a11y.aria.getState(labelInput.getElement(),
    +            goog.a11y.aria.State.LABEL));
    +  }
    +}
    +
    +function testDecorateElementWithFocus() {
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.setLabel('search');
    +
    +  var decoratedElement = goog.dom.getElement('i');
    +
    +  decoratedElement.value = '';
    +  decoratedElement.focus();
    +
    +  labelInput.decorate(decoratedElement);
    +  labelInput.enterDocument();
    +  mockClock.tick(10);
    +
    +  assertEquals('label for pre-focused input should not have LABEL_CLASS_NAME',
    +      -1,
    +      labelInput.getElement().className.indexOf(labelInput.labelCssClassName));
    +
    +  if (!isPlaceholderSupported()) {
    +    assertEquals('label rendered for pre-focused element',
    +        '', labelInput.getElement().value);
    +    // NOTE(user): element.blur() doesn't seem to trigger the BLUR event in
    +    // IE in the test environment. This could be related to the IE issues with
    +    // testClassName() below.
    +    goog.testing.events.fireBrowserEvent(
    +        new goog.testing.events.Event(
    +            goog.events.EventType.BLUR, decoratedElement));
    +    mockClock.tick(10);
    +    assertEquals('label not rendered for blurred element',
    +        'search', labelInput.getElement().value);
    +  }
    +}
    +
    +function testDecorateElementWithFocusDelay() {
    +  if (isPlaceholderSupported()) {
    +    return; // Delay only affects the older browsers.
    +  }
    +  var placeholder = 'search';
    +
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.setLabel(placeholder);
    +  var delay = 150;
    +  labelInput.labelRestoreDelayMs = delay;
    +
    +  var decoratedElement = goog.dom.getElement('i');
    +
    +  decoratedElement.value = '';
    +  decoratedElement.focus();
    +
    +  labelInput.decorate(decoratedElement);
    +  labelInput.enterDocument();
    +  // wait for all initial setup to settle
    +  mockClock.tick(delay);
    +
    +  // NOTE(user): element.blur() doesn't seem to trigger the BLUR event in
    +  // IE in the test environment. This could be related to the IE issues with
    +  // testClassName() below.
    +  goog.testing.events.fireBrowserEvent(
    +      new goog.testing.events.Event(
    +          goog.events.EventType.BLUR, decoratedElement));
    +
    +  mockClock.tick(delay - 1);
    +  assertEquals('label should not be restored before labelRestoreDelay',
    +      '', labelInput.getElement().value);
    +
    +  mockClock.tick(1);
    +  assertEquals('label not rendered for blurred element with labelRestoreDelay',
    +      placeholder, labelInput.getElement().value);
    +}
    +
    +function testClassName() {
    +  labelInput = new goog.ui.LabelInput();
    +
    +  // IE always fails this test, suspect it is a focus issue.
    +  if (goog.userAgent.IE) {
    +    return;
    +  }
    +  // FF does not perform focus if the window is not active in the first place.
    +  if (goog.userAgent.GECKO && document.hasFocus && !document.hasFocus()) {
    +    return;
    +  }
    +
    +  labelInput.decorate(goog.dom.getElement('i'));
    +  labelInput.setLabel('search');
    +
    +  var el = labelInput.getElement();
    +  assertTrue('label before focus should have LABEL_CLASS_NAME',
    +      goog.dom.classlist.contains(el, labelInput.labelCssClassName));
    +
    +  labelInput.getElement().focus();
    +
    +  assertFalse('label after focus should not have LABEL_CLASS_NAME',
    +      goog.dom.classlist.contains(el, labelInput.labelCssClassName));
    +
    +  labelInput.getElement().blur();
    +
    +  assertTrue('label after blur should have LABEL_CLASS_NAME',
    +      goog.dom.classlist.contains(el, labelInput.labelCssClassName));
    +}
    +
    +function isPlaceholderSupported() {
    +  if (goog.dom.getElement('i').placeholder != null) {
    +    return true;
    +  }
    +}
    +
    +function testEnable() {
    +  labelInput = new goog.ui.LabelInput();
    +  labelInput.createDom();
    +  labelInput.enterDocument();
    +
    +  var labelElement = labelInput.getElement();
    +  var disabledClass = goog.getCssName(labelInput.labelCssClassName, 'disabled');
    +
    +  assertTrue('label should be enabled', labelInput.isEnabled());
    +  assertFalse('label should not have the disabled class',
    +      goog.dom.classlist.contains(labelElement, disabledClass));
    +
    +  labelInput.setEnabled(false);
    +  assertFalse('label should be disabled', labelInput.isEnabled());
    +  assertTrue('label should have the disabled class',
    +      goog.dom.classlist.contains(labelElement, disabledClass));
    +
    +  labelInput.setEnabled(true);
    +  assertTrue('label should be enabled', labelInput.isEnabled());
    +  assertFalse('label should not have the disabled class',
    +      goog.dom.classlist.contains(labelElement, disabledClass));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/linkbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/linkbuttonrenderer.js
    new file mode 100644
    index 0000000000..4f720adaaf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/linkbuttonrenderer.js
    @@ -0,0 +1,67 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Similiar to {@link goog.ui.FlatButtonRenderer},
    + * but underlines text instead of adds borders.
    + *
    + * For accessibility reasons, it is best to use this with a goog.ui.Button
    + * instead of an A element for links that perform actions in the page.  Links
    + * that have an href and open a new page can and should remain as A elements.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.LinkButtonRenderer');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.FlatButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Link renderer for {@link goog.ui.Button}s.  Link buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + * @constructor
    + * @extends {goog.ui.FlatButtonRenderer}
    + */
    +goog.ui.LinkButtonRenderer = function() {
    +  goog.ui.FlatButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.LinkButtonRenderer, goog.ui.FlatButtonRenderer);
    +goog.addSingletonGetter(goog.ui.LinkButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.LinkButtonRenderer.CSS_CLASS = goog.getCssName('goog-link-button');
    +
    +
    +/** @override */
    +goog.ui.LinkButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.LinkButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +// Register a decorator factory function for Link Buttons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.LinkButtonRenderer.CSS_CLASS,
    +    function() {
    +      // Uses goog.ui.Button, but with LinkButtonRenderer.
    +      return new goog.ui.Button(null, goog.ui.LinkButtonRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flashobject.js b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject.js
    new file mode 100644
    index 0000000000..6940672d3d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject.js
    @@ -0,0 +1,631 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Wrapper on a Flash object embedded in the HTML page.
    + * This class contains routines for writing the HTML to create the Flash object
    + * using a goog.ui.Component approach. Tested on Firefox 1.5, 2 and 3, IE6, 7,
    + * Konqueror, Chrome and Safari.
    + *
    + * Based on http://go/flashobject.js
    + *
    + * Based on the following compatibility test suite:
    + * http://www.bobbyvandersluis.com/flashembed/testsuite/
    + *
    + * TODO(user): take a look at swfobject, and maybe use it instead of the current
    + * flash embedding method.
    + *
    + * Examples of usage:
    + *
    + * <pre>
    + *   var url = goog.html.TrustedResourceUrl.fromConstant(
    + *       goog.string.Const.from('https://hostname/flash.swf'))
    + *   var flash = new goog.ui.media.FlashObject(url);
    + *   flash.setFlashVar('myvar', 'foo');
    + *   flash.render(goog.dom.getElement('parent'));
    + * </pre>
    + *
    + * TODO(user, jessan): create a goog.ui.media.BrowserInterfaceFlashObject that
    + * subclasses goog.ui.media.FlashObject to provide all the goodness of
    + * http://go/browserinterface.as
    + *
    + */
    +
    +goog.provide('goog.ui.media.FlashObject');
    +goog.provide('goog.ui.media.FlashObject.ScriptAccessLevel');
    +goog.provide('goog.ui.media.FlashObject.Wmodes');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.safe');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.flash');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.log');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.structs.Map');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.flash');
    +
    +
    +
    +/**
    + * A very simple flash wrapper, that allows you to create flash object
    + * programmatically, instead of embedding your own HTML. It extends
    + * {@link goog.ui.Component}, which makes it very easy to be embedded on the
    + * page.
    + *
    + * @param {string|!goog.html.TrustedResourceUrl} flashUrl The Flash SWF URL.
    + *     If possible pass a TrustedResourceUrl. string is supported
    + *     for backwards-compatibility only, uses goog.html.legacyconversions,
    + *     and will be sanitized with goog.html.SafeUrl.sanitize() before being
    + *     used.
    + * @param {goog.dom.DomHelper=} opt_domHelper An optional DomHelper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.media.FlashObject = function(flashUrl, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  var trustedResourceUrl;
    +  if (flashUrl instanceof goog.html.TrustedResourceUrl) {
    +    trustedResourceUrl = flashUrl;
    +  } else {
    +    var flashUrlSanitized =
    +        goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(flashUrl));
    +    trustedResourceUrl = goog.html.legacyconversions
    +        .trustedResourceUrlFromString(flashUrlSanitized);
    +  }
    +
    +  /**
    +   * The URL of the flash movie to be embedded.
    +   *
    +   * @type {!goog.html.TrustedResourceUrl}
    +   * @private
    +   */
    +  this.flashUrl_ = trustedResourceUrl;
    +
    +  /**
    +   * An event handler used to handle events consistently between browsers.
    +   * @type {goog.events.EventHandler<!goog.ui.media.FlashObject>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * A map of variables to be passed to the flash movie.
    +   *
    +   * @type {goog.structs.Map}
    +   * @private
    +   */
    +  this.flashVars_ = new goog.structs.Map();
    +};
    +goog.inherits(goog.ui.media.FlashObject, goog.ui.Component);
    +
    +
    +/**
    + * Different states of loaded-ness in which the SWF itself can be
    + *
    + * Talked about at:
    + * http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12059&sliceId=1
    + *
    + * @enum {number}
    + * @private
    + */
    +goog.ui.media.FlashObject.SwfReadyStates_ = {
    +  LOADING: 0,
    +  UNINITIALIZED: 1,
    +  LOADED: 2,
    +  INTERACTIVE: 3,
    +  COMPLETE: 4
    +};
    +
    +
    +/**
    + * The different modes for displaying a SWF. Note that different wmodes
    + * can result in different bugs in different browsers and also that
    + * both OPAQUE and TRANSPARENT will result in a performance hit.
    + *
    + * @enum {string}
    + */
    +goog.ui.media.FlashObject.Wmodes = {
    +  /**
    +   * Allows for z-ordering of the SWF.
    +   */
    +  OPAQUE: 'opaque',
    +
    +  /**
    +   * Allows for z-ordering of the SWF and plays the SWF with a transparent BG.
    +   */
    +  TRANSPARENT: 'transparent',
    +
    +  /**
    +   * The default wmode. Does not allow for z-ordering of the SWF.
    +   */
    +  WINDOW: 'window'
    +};
    +
    +
    +/**
    + * The different levels of allowScriptAccess.
    + *
    + * Talked about at:
    + * http://kb2.adobe.com/cps/164/tn_16494.html
    + *
    + * @enum {string}
    + */
    +goog.ui.media.FlashObject.ScriptAccessLevel = {
    +  /*
    +   * The flash object can always communicate with its container page.
    +   */
    +  ALWAYS: 'always',
    +
    +  /*
    +   * The flash object can only communicate with its container page if they are
    +   * hosted in the same domain.
    +   */
    +  SAME_DOMAIN: 'sameDomain',
    +
    +  /*
    +   * The flash can not communicate with its container page.
    +   */
    +  NEVER: 'never'
    +};
    +
    +
    +/**
    + * The component CSS namespace.
    + *
    + * @type {string}
    + */
    +goog.ui.media.FlashObject.CSS_CLASS = goog.getCssName('goog-ui-media-flash');
    +
    +
    +/**
    + * The flash object CSS class.
    + *
    + * @type {string}
    + */
    +goog.ui.media.FlashObject.FLASH_CSS_CLASS =
    +    goog.getCssName('goog-ui-media-flash-object');
    +
    +
    +/**
    + * A logger used for debugging.
    + *
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.logger_ =
    +    goog.log.getLogger('goog.ui.media.FlashObject');
    +
    +
    +/**
    + * The wmode for the SWF.
    + *
    + * @type {goog.ui.media.FlashObject.Wmodes}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.wmode_ =
    +    goog.ui.media.FlashObject.Wmodes.WINDOW;
    +
    +
    +/**
    + * The minimum required flash version.
    + *
    + * @type {?string}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.requiredVersion_;
    +
    +
    +/**
    + * The flash movie width.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.width_;
    +
    +
    +/**
    + * The flash movie height.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.height_;
    +
    +
    +/**
    + * The flash movie background color.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.backgroundColor_ = '#000000';
    +
    +
    +/**
    + * The flash movie allowScriptAccess setting.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.allowScriptAccess_ =
    +    goog.ui.media.FlashObject.ScriptAccessLevel.SAME_DOMAIN;
    +
    +
    +/**
    + * Sets the flash movie Wmode.
    + *
    + * @param {goog.ui.media.FlashObject.Wmodes} wmode the flash movie Wmode.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setWmode = function(wmode) {
    +  this.wmode_ = wmode;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {string} Returns the flash movie wmode.
    + */
    +goog.ui.media.FlashObject.prototype.getWmode = function() {
    +  return this.wmode_;
    +};
    +
    +
    +/**
    + * Adds flash variables.
    + *
    + * @param {goog.structs.Map|Object} map A key-value map of variables.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.addFlashVars = function(map) {
    +  this.flashVars_.addAll(map);
    +  return this;
    +};
    +
    +
    +/**
    + * Sets a flash variable.
    + *
    + * @param {string} key The name of the flash variable.
    + * @param {string} value The value of the flash variable.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setFlashVar = function(key, value) {
    +  this.flashVars_.set(key, value);
    +  return this;
    +};
    +
    +
    +/**
    + * Sets flash variables. You can either pass a Map of key->value pairs or you
    + * can pass a key, value pair to set a specific variable.
    + *
    + * TODO(user, martino): Get rid of this method.
    + *
    + * @deprecated Use {@link #addFlashVars} or {@link #setFlashVar} instead.
    + * @param {goog.structs.Map|Object|string} flashVar A map of variables (given
    + *    as a goog.structs.Map or an Object literal) or a key to the optional
    + *    {@code opt_value}.
    + * @param {string=} opt_value The optional value for the flashVar key.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setFlashVars = function(flashVar,
    +                                                            opt_value) {
    +  if (flashVar instanceof goog.structs.Map ||
    +      goog.typeOf(flashVar) == 'object') {
    +    this.addFlashVars(/**@type {!goog.structs.Map|!Object}*/(flashVar));
    +  } else {
    +    goog.asserts.assert(goog.isString(flashVar) && goog.isDef(opt_value),
    +        'Invalid argument(s)');
    +    this.setFlashVar(/**@type {string}*/(flashVar),
    +                     /**@type {string}*/(opt_value));
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {goog.structs.Map} The current flash variables.
    + */
    +goog.ui.media.FlashObject.prototype.getFlashVars = function() {
    +  return this.flashVars_;
    +};
    +
    +
    +/**
    + * Sets the background color of the movie.
    + *
    + * @param {string} color The new color to be set.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setBackgroundColor = function(color) {
    +  this.backgroundColor_ = color;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {string} The background color of the movie.
    + */
    +goog.ui.media.FlashObject.prototype.getBackgroundColor = function() {
    +  return this.backgroundColor_;
    +};
    +
    +
    +/**
    + * Sets the allowScriptAccess setting of the movie.
    + *
    + * @param {string} value The new value to be set.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setAllowScriptAccess = function(value) {
    +  this.allowScriptAccess_ = value;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {string} The allowScriptAccess setting color of the movie.
    + */
    +goog.ui.media.FlashObject.prototype.getAllowScriptAccess = function() {
    +  return this.allowScriptAccess_;
    +};
    +
    +
    +/**
    + * Sets the width and height of the movie.
    + *
    + * @param {number|string} width The width of the movie.
    + * @param {number|string} height The height of the movie.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setSize = function(width, height) {
    +  this.width_ = goog.isString(width) ? width : Math.round(width) + 'px';
    +  this.height_ = goog.isString(height) ? height : Math.round(height) + 'px';
    +  if (this.getElement()) {
    +    goog.style.setSize(this.getFlashElement(), this.width_, this.height_);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {?string} The flash required version.
    + */
    +goog.ui.media.FlashObject.prototype.getRequiredVersion = function() {
    +  return this.requiredVersion_;
    +};
    +
    +
    +/**
    + * Sets the minimum flash required version.
    + *
    + * @param {?string} version The minimum required version for this movie to work,
    + *     or null if you want to unset it.
    + * @return {!goog.ui.media.FlashObject} The flash object instance for chaining.
    + */
    +goog.ui.media.FlashObject.prototype.setRequiredVersion = function(version) {
    +  this.requiredVersion_ = version;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns whether this SWF has a minimum required flash version.
    + *
    + * @return {boolean} Whether a required version was set or not.
    + */
    +goog.ui.media.FlashObject.prototype.hasRequiredVersion = function() {
    +  return this.requiredVersion_ != null;
    +};
    +
    +
    +/**
    + * Writes the Flash embedding {@code HTMLObjectElement} to this components root
    + * element and adds listeners for all events to handle them consistently.
    + * @override
    + */
    +goog.ui.media.FlashObject.prototype.enterDocument = function() {
    +  goog.ui.media.FlashObject.superClass_.enterDocument.call(this);
    +
    +  // The SWF tag must be written after this component's element is appended to
    +  // the DOM. Otherwise Flash's ExternalInterface is broken in IE.
    +  goog.dom.safe.setInnerHtml(
    +      /** @type {!Element} */ (this.getElement()), this.createSwfTag_());
    +  if (this.width_ && this.height_) {
    +    this.setSize(this.width_, this.height_);
    +  }
    +
    +  // Sinks all the events on the bubble phase.
    +  //
    +  // Flash plugins propagates events from/to the plugin to the browser
    +  // inconsistently:
    +  //
    +  // 1) FF2 + linux: the flash plugin will stop the propagation of all events
    +  // from the plugin to the browser.
    +  // 2) FF3 + mac: the flash plugin will propagate events on the <embed> object
    +  // but that will get propagated to its parents.
    +  // 3) Safari 3.1.1 + mac: the flash plugin will propagate the event to the
    +  // <object> tag that event will propagate to its parents.
    +  // 4) IE7 + windows: the flash plugin  will eat all events, not propagating
    +  // anything to the javascript.
    +  // 5) Chrome + windows: the flash plugin will eat all events, not propagating
    +  // anything to the javascript.
    +  //
    +  // To overcome this inconsistency, all events from/to the plugin are sinked,
    +  // since you can't assume that the events will be propagated.
    +  //
    +  // NOTE(user): we only sink events on the bubbling phase, since there are no
    +  // inexpensive/scalable way to stop events on the capturing phase unless we
    +  // added an event listener on the document for each flash object.
    +  this.eventHandler_.listen(
    +      this.getElement(),
    +      goog.object.getValues(goog.events.EventType),
    +      goog.events.Event.stopPropagation);
    +};
    +
    +
    +/**
    + * Creates the DOM structure.
    + *
    + * @override
    + */
    +goog.ui.media.FlashObject.prototype.createDom = function() {
    +  if (this.hasRequiredVersion() &&
    +      !goog.userAgent.flash.isVersion(
    +          /** @type {string} */ (this.getRequiredVersion()))) {
    +    goog.log.warning(this.logger_, 'Required flash version not found:' +
    +        this.getRequiredVersion());
    +    throw Error(goog.ui.Component.Error.NOT_SUPPORTED);
    +  }
    +
    +  var element = this.getDomHelper().createElement('div');
    +  element.className = goog.ui.media.FlashObject.CSS_CLASS;
    +  this.setElementInternal(element);
    +};
    +
    +
    +/**
    + * Creates the HTML to embed the flash object.
    + *
    + * @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
    + *     DOM.
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.createSwfTag_ = function() {
    +  var keys = this.flashVars_.getKeys();
    +  var values = this.flashVars_.getValues();
    +  var flashVars = [];
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = goog.string.urlEncode(keys[i]);
    +    var value = goog.string.urlEncode(values[i]);
    +    flashVars.push(key + '=' + value);
    +  }
    +  var flashVarsString = flashVars.join('&');
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
    +    return this.createSwfTagOldIe_(flashVarsString);
    +  } else {
    +    return this.createSwfTagModern_(flashVarsString);
    +  }
    +};
    +
    +
    +/**
    + * Creates the HTML to embed the flash object for IE>=11 and other browsers.
    + *
    + * @param {string} flashVars The value of the FlashVars attribute.
    + * @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
    + *     DOM.
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.createSwfTagModern_ = function(flashVars) {
    +  return goog.html.flash.createEmbed(
    +      this.flashUrl_,
    +      {
    +        'AllowScriptAccess': this.allowScriptAccess_,
    +        'allowFullScreen': 'true',
    +        'allowNetworking': 'all',
    +        'bgcolor': this.backgroundColor_,
    +        'class': goog.ui.media.FlashObject.FLASH_CSS_CLASS,
    +        'FlashVars': flashVars,
    +        'id': this.getId(),
    +        'name': this.getId(),
    +        'quality': 'high',
    +        'SeamlessTabbing': 'false',
    +        'wmode': this.wmode_
    +      });
    +};
    +
    +
    +/**
    + * Creates the HTML to embed the flash object for IE<11.
    + *
    + * @param {string} flashVars The value of the FlashVars attribute.
    + * @return {!goog.html.SafeHtml} Browser appropriate HTML to add the SWF to the
    + *     DOM.
    + * @private
    + */
    +goog.ui.media.FlashObject.prototype.createSwfTagOldIe_ = function(flashVars) {
    +  return goog.html.flash.createObjectForOldIe(
    +      this.flashUrl_,
    +      {
    +        'allowFullScreen': 'true',
    +        'AllowScriptAccess': this.allowScriptAccess_,
    +        'allowNetworking': 'all',
    +        'bgcolor': this.backgroundColor_,
    +        'FlashVars': flashVars,
    +        'quality': 'high',
    +        'SeamlessTabbing': 'false',
    +        'wmode': this.wmode_
    +      },
    +      {
    +        'class': goog.ui.media.FlashObject.FLASH_CSS_CLASS,
    +        'id': this.getId(),
    +        'name': this.getId()
    +      });
    +};
    +
    +
    +/**
    + * @return {HTMLObjectElement} The flash element or null if the element can't
    + *     be found.
    + */
    +goog.ui.media.FlashObject.prototype.getFlashElement = function() {
    +  return /** @type {HTMLObjectElement} */(this.getElement() ?
    +      this.getElement().firstChild : null);
    +};
    +
    +
    +/** @override */
    +goog.ui.media.FlashObject.prototype.disposeInternal = function() {
    +  goog.ui.media.FlashObject.superClass_.disposeInternal.call(this);
    +  this.flashVars_ = null;
    +
    +  this.eventHandler_.dispose();
    +  this.eventHandler_ = null;
    +};
    +
    +
    +/**
    + * @return {boolean} whether the SWF has finished loading or not.
    + */
    +goog.ui.media.FlashObject.prototype.isLoaded = function() {
    +  if (!this.isInDocument() || !this.getElement()) {
    +    return false;
    +  }
    +
    +  if (this.getFlashElement().readyState &&
    +      this.getFlashElement().readyState ==
    +          goog.ui.media.FlashObject.SwfReadyStates_.COMPLETE) {
    +    return true;
    +  }
    +
    +  if (this.getFlashElement().PercentLoaded &&
    +      this.getFlashElement().PercentLoaded() == 100) {
    +    return true;
    +  }
    +
    +  return false;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.html
    new file mode 100644
    index 0000000000..0a548c6cb1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.FlashObject
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.FlashObjectTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.js
    new file mode 100644
    index 0000000000..b7fb0fcacb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flashobject_test.js
    @@ -0,0 +1,347 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.FlashObjectTest');
    +goog.setTestOnly('goog.ui.media.FlashObjectTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.DomHelper');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.html.SafeUrl');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.userAgent');
    +
    +
    +var FLASH_URL = 'http://www.youtube.com/v/RbI7cCp0v6w&hl=en&fs=1';
    +var control = new goog.testing.MockControl();
    +var domHelper = control.createLooseMock(goog.dom.DomHelper);
    +// TODO(user): mocking window.document throws exceptions in FF2. find out how
    +// to mock it.
    +var documentHelper = {body: control.createLooseMock(goog.dom.DomHelper)};
    +var element = goog.dom.createElement('div');
    +
    +function setUp() {
    +  control.$resetAll();
    +  domHelper.getDocument().$returns(documentHelper).$anyTimes();
    +  domHelper.createElement('div').$returns(element).$anyTimes();
    +  documentHelper.body.appendChild(element).$anyTimes();
    +}
    +
    +function tearDown() {
    +  control.$verifyAll();
    +}
    +
    +function getFlashVarsFromElement(flash) {
    +  var el = flash.getFlashElement();
    +
    +  // This should work in everything except IE:
    +  if (el.hasAttribute && el.hasAttribute('flashvars'))
    +    return el.getAttribute('flashvars');
    +
    +  // For IE: find and return the value of the correct param element:
    +  el = el.firstChild;
    +  while (el) {
    +    if (el.name == 'FlashVars') {
    +      return el.value;
    +    }
    +    el = el.nextSibling;
    +  }
    +  return '';
    +}
    +
    +function testInstantiationAndRendering() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.render();
    +  flash.dispose();
    +}
    +
    +function testRenderedWithCorrectAttributes() {
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
    +    return;
    +  }
    +
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.setAllowScriptAccess('allowScriptAccess');
    +  flash.setBackgroundColor('backgroundColor');
    +  flash.setId('id');
    +  flash.setFlashVars({'k1': 'v1', 'k2': 'v2'});
    +  flash.setWmode('wmode');
    +  flash.render();
    +
    +  var el = flash.getFlashElement();
    +  assertEquals('true', el.getAttribute('allowFullScreen'));
    +  assertEquals('all', el.getAttribute('allowNetworking'));
    +  assertEquals('allowScriptAccess', el.getAttribute('allowScriptAccess'));
    +  assertEquals(
    +      goog.ui.media.FlashObject.FLASH_CSS_CLASS, el.getAttribute('class'));
    +  assertEquals('k1=v1&k2=v2', el.getAttribute('FlashVars'));
    +  assertEquals('id', el.getAttribute('id'));
    +  assertEquals('id', el.getAttribute('name'));
    +  assertEquals('https://www.macromedia.com/go/getflashplayer',
    +      el.getAttribute('pluginspage'));
    +  assertEquals('high', el.getAttribute('quality'));
    +  assertEquals('false', el.getAttribute('SeamlessTabbing'));
    +  assertEquals(FLASH_URL, el.getAttribute('src'));
    +  assertEquals('application/x-shockwave-flash',
    +      el.getAttribute('type'));
    +  assertEquals('wmode', el.getAttribute('wmode'));
    +}
    +
    +function testRenderedWithCorrectAttributesOldIe() {
    +  if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(11)) {
    +    return;
    +  }
    +
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.setAllowScriptAccess('allowScriptAccess');
    +  flash.setBackgroundColor('backgroundColor');
    +  flash.setId('id');
    +  flash.setFlashVars({'k1': 'v1', 'k2': 'v2'});
    +  flash.setWmode('wmode');
    +  flash.render();
    +
    +  var el = flash.getFlashElement();
    +  assertEquals('class',
    +      goog.ui.media.FlashObject.FLASH_CSS_CLASS, el.getAttribute('class'));
    +  assertEquals('clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
    +      el.getAttribute('classid'));
    +  assertEquals('id', 'id', el.getAttribute('id'));
    +  assertEquals('name', 'id', el.getAttribute('name'));
    +
    +  assertContainsParam(el, 'allowFullScreen', 'true');
    +  assertContainsParam(el, 'allowNetworking', 'all');
    +  assertContainsParam(el, 'AllowScriptAccess', 'allowScriptAccess');
    +  assertContainsParam(el, 'bgcolor', 'backgroundColor');
    +  assertContainsParam(el, 'FlashVars', 'FlashVars');
    +  assertContainsParam(el, 'movie', FLASH_URL);
    +  assertContainsParam(el, 'quality', 'high');
    +  assertContainsParam(el, 'SeamlessTabbing', 'false');
    +  assertContainsParam(el, 'wmode', 'wmode');
    +
    +}
    +
    +function testUrlIsSanitized() {
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(11)) {
    +    return;
    +  }
    +
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject('javascript:evil', domHelper);
    +  flash.render();
    +  var el = flash.getFlashElement();
    +
    +  assertEquals(goog.html.SafeUrl.INNOCUOUS_STRING, el.getAttribute('src'));
    +}
    +
    +function testUrlIsSanitizedOldIe() {
    +  if (!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(11)) {
    +    return;
    +  }
    +
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject('javascript:evil', domHelper);
    +  flash.render();
    +  var el = flash.getFlashElement();
    +
    +  assertContainsParam(el, 'movie', goog.html.SafeUrl.INNOCUOUS_STRING);
    +}
    +
    +function assertContainsParam(element, expectedName, expectedValue) {
    +  var failureMsg = 'Expected param with name \"' + expectedName +
    +      '\" and value \"' + expectedValue + '\". Not found in child nodes: ' +
    +      element.innerHTML;
    +  for (var i = 0; i < element.childNodes.length; i++) {
    +    var child = element.childNodes[i];
    +    var name = child.getAttribute('name');
    +    if (name === expectedName) {
    +      if (!child.getAttribute('value') === expectedValue) {
    +        fail(failureMsg);
    +      }
    +      return;
    +    }
    +  }
    +  fail(failureMsg);
    +}
    +
    +function testSetFlashVar() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +
    +  assertTrue(flash.getFlashVars().isEmpty());
    +  flash.setFlashVar('foo', 'bar');
    +  flash.setFlashVar('hello', 'world');
    +  assertFalse(flash.getFlashVars().isEmpty());
    +
    +  flash.render();
    +
    +  assertEquals('foo=bar&hello=world', getFlashVarsFromElement(flash));
    +  flash.dispose();
    +}
    +
    +function testAddFlashVars() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +
    +  assertTrue(flash.getFlashVars().isEmpty());
    +  flash.addFlashVars({
    +    'using': 'an',
    +    'object': 'literal'
    +  });
    +  assertFalse(flash.getFlashVars().isEmpty());
    +
    +  flash.render();
    +
    +  assertEquals('using=an&object=literal', getFlashVarsFromElement(flash));
    +  flash.dispose();
    +}
    +
    +
    +/**
    + * @deprecated Remove once setFlashVars is removed.
    + */
    +function testSetFlashVarsUsingFalseAsTheValue() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +
    +  assertTrue(flash.getFlashVars().isEmpty());
    +  flash.setFlashVars('beEvil', false);
    +  assertFalse(flash.getFlashVars().isEmpty());
    +
    +  flash.render();
    +
    +  assertEquals('beEvil=false', getFlashVarsFromElement(flash));
    +  flash.dispose();
    +}
    +
    +
    +/**
    + * @deprecated Remove once setFlashVars is removed.
    + */
    +function testSetFlashVarsWithWrongArgument() {
    +  control.$replayAll();
    +
    +  assertThrows(function() {
    +    var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +    flash.setFlashVars('foo=bar');
    +    flash.dispose();
    +  });
    +}
    +
    +function testSetFlashVarUrlEncoding() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.setFlashVar('foo', 'bar and some extra spaces');
    +  flash.render();
    +  assertEquals('foo=bar%20and%20some%20extra%20spaces',
    +      getFlashVarsFromElement(flash));
    +  flash.dispose();
    +}
    +
    +function testThrowsRequiredVersionOfFlashNotAvailable() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.setRequiredVersion('999.999.999');
    +
    +  assertTrue(flash.hasRequiredVersion());
    +
    +  assertThrows(function() {
    +    flash.render();
    +  });
    +
    +  flash.dispose();
    +}
    +
    +function testIsLoadedAfterDispose() {
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.render();
    +  // TODO(user): find out a way to test the loadness of flash movies on
    +  // asynchronous tests. if debugger; is left here, the test pass. if removed
    +  // the test fails. that happens because flash needs some time to be
    +  // considered loaded, after flash.render() is called (like img.src i guess).
    +  //debugger;
    +  //assertTrue(flash.isLoaded());
    +  flash.dispose();
    +  assertFalse(flash.isLoaded());
    +}
    +
    +function testXssAttacks() {
    +  control.$replayAll();
    +
    +  called = false;
    +  var injection = '' +
    +      '">' +
    +      '</embed>' +
    +      '<script>called = true; // evil arbitrary js injected here<\/script>' +
    +      '<embed src=""';
    +  var flash = new goog.ui.media.FlashObject(injection, domHelper);
    +  flash.render();
    +  // Makes sure FlashObject html escapes user input.
    +  // NOTE(user): this test fails if the URL is not HTML escaped, showing that
    +  // html escaping is necessary to avoid attacks.
    +  assertFalse(called);
    +}
    +
    +function testPropagatesEventsConsistently() {
    +  var event = control.createLooseMock(goog.events.Event);
    +
    +  // we expect any event to have its propagation stopped.
    +  event.stopPropagation();
    +
    +  control.$replayAll();
    +
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL, domHelper);
    +  flash.render();
    +  event.target = flash.getElement();
    +  event.type = goog.events.EventType.CLICK;
    +  goog.testing.events.fireBrowserEvent(event);
    +  flash.dispose();
    +}
    +
    +function testEventsGetsSinked() {
    +  var called = false;
    +  var flash = new goog.ui.media.FlashObject(FLASH_URL);
    +  var parent = goog.dom.createElement('div');
    +  flash.render(parent);
    +
    +  goog.events.listen(parent, goog.events.EventType.CLICK, function(e) {
    +    called = true;
    +  });
    +
    +  assertFalse(called);
    +
    +  goog.testing.events.fireClickSequence(flash.getElement());
    +
    +  assertFalse(called);
    +  flash.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flickr.js b/mapbox-gl/closure-library/closure/goog/ui/media/flickr.js
    new file mode 100644
    index 0000000000..b34755a4e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flickr.js
    @@ -0,0 +1,314 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview provides a reusable FlickrSet photo UI component given a public
    + * FlickrSetModel.
    + *
    + * goog.ui.media.FlickrSet is actually a {@link goog.ui.ControlRenderer}, a
    + * stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.FlickrSet.getInstance} -, that knows how to
    + * render Flickr sets. It is designed to be used with a {@link goog.ui.Control},
    + * which will actually control the media renderer and provide the
    + * {@link goog.ui.Component} base. This design guarantees that all different
    + * types of medias will behave alike but will look different.
    + *
    + * goog.ui.media.FlickrSet expects a {@code goog.ui.media.FlickrSetModel} on
    + * {@code goog.ui.Control.getModel} as data models, and renders a flash object
    + * that will show the contents of that set.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var flickrSet = goog.ui.media.FlickrSetModel.newInstance(flickrSetUrl);
    + *   goog.ui.media.FlickrSet.newControl(flickrSet).render();
    + * </pre>
    + *
    + * FlickrSet medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
    + *   <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
    + * </ul>
    + *
    + * Which can be accessed by
    + * <pre>
    + *   video.setEnabled(true);
    + *   video.setHighlighted(true);
    + *   video.setSelected(true);
    + * </pre>
    + *
    + *
    + * @supported IE6, FF2+, Safari. Requires flash to actually work.
    + *
    + * TODO(user): Support non flash users. Maybe show a link to the Flick set,
    + * or fetch the data and rendering it using javascript (instead of a broken
    + * 'You need to install flash' message).
    + */
    +
    +goog.provide('goog.ui.media.FlickrSet');
    +goog.provide('goog.ui.media.FlickrSetModel');
    +
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.string.Const');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a FlickrSet specific
    + * media renderer.
    + *
    + * This class knows how to parse FlickrSet URLs, and render the DOM structure
    + * of flickr set players. This class is meant to be used as a singleton static
    + * stateless class, that takes {@code goog.ui.media.Media} instances and renders
    + * it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
    + * previously constructed, set id {@see goog.ui.media.FlickrSet.parseUrl},
    + * which is the data model this renderer will use to construct the DOM
    + * structure. {@see goog.ui.media.FlickrSet.newControl} for a example of
    + * constructing a control with this renderer.
    + *
    + * This design is patterned after
    + * http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.FlickrSet = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.FlickrSet, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.FlickrSet);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.FlickrSet.CSS_CLASS = goog.getCssName('goog-ui-media-flickrset');
    +
    +
    +/**
    + * Flash player URL. Uses Flickr's flash player by default.
    + *
    + * @type {!goog.html.TrustedResourceUrl}
    + * @private
    + */
    +goog.ui.media.FlickrSet.flashUrl_ = goog.html.TrustedResourceUrl.fromConstant(
    +    goog.string.Const.from(
    +        'http://www.flickr.com/apps/slideshow/show.swf?v=63961'));
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a FlickrSet URL. It extracts the set id information on the URL, sets it
    + * as the data model goog.ui.media.FlickrSet renderer uses, sets the states
    + * supported by the renderer, and returns a Control that binds everything
    + * together. This is what you should be using for constructing FlickrSet videos,
    + * except if you need more fine control over the configuration.
    + *
    + * @param {goog.ui.media.FlickrSetModel} dataModel The Flickr Set data model.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A Control binded to the FlickrSet renderer.
    + * @throws exception in case {@code flickrSetUrl} is an invalid flickr set URL.
    + * TODO(user): use {@link goog.ui.media.MediaModel} once it is checked in.
    + */
    +goog.ui.media.FlickrSet.newControl = function(dataModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      dataModel, goog.ui.media.FlickrSet.getInstance(), opt_domHelper);
    +  control.setSelected(true);
    +  return control;
    +};
    +
    +
    +/**
    + * A static method that sets which flash URL this class should use. Use this if
    + * you want to host your own flash flickr player.
    + *
    + * @param {!goog.html.TrustedResourceUrl} flashUrl The URL of the flash flickr
    + *     player.
    + */
    +goog.ui.media.FlickrSet.setFlashUrl = function(flashUrl) {
    +  goog.ui.media.FlickrSet.flashUrl_ = flashUrl;
    +};
    +
    +
    +/**
    + * Creates the initial DOM structure of the flickr set, which is basically a
    + * the flash object pointing to a flickr set player.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} The DOM structure that represents this control.
    + * @override
    + */
    +goog.ui.media.FlickrSet.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.FlickrSet.superClass_.createDom.call(this, control);
    +
    +  var model =
    +      /** @type {goog.ui.media.FlickrSetModel} */ (control.getDataModel());
    +
    +  // TODO(user): find out what is the policy about hosting this SWF. figure out
    +  // if it works over https.
    +  var flash = new goog.ui.media.FlashObject(
    +      model.getPlayer().getTrustedResourceUrl(),
    +      control.getDomHelper());
    +  flash.addFlashVars(model.getPlayer().getVars());
    +  flash.render(div);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.FlickrSet.prototype.getCssClass = function() {
    +  return goog.ui.media.FlickrSet.CSS_CLASS;
    +};
    +
    +
    +
    +/**
    + * The {@code goog.ui.media.FlickrAlbum} media data model. It stores a required
    + * {@code userId} and {@code setId} fields, sets the flickr Set URL, and
    + * allows a few optional parameters.
    + *
    + * @param {string} userId The flickr userId associated with this set.
    + * @param {string} setId The flickr setId associated with this set.
    + * @param {string=} opt_caption An optional caption of the flickr set.
    + * @param {string=} opt_description An optional description of the flickr set.
    + * @constructor
    + * @extends {goog.ui.media.MediaModel}
    + * @final
    + */
    +goog.ui.media.FlickrSetModel = function(userId,
    +                                        setId,
    +                                        opt_caption,
    +                                        opt_description) {
    +  goog.ui.media.MediaModel.call(
    +      this,
    +      goog.ui.media.FlickrSetModel.buildUrl(userId, setId),
    +      opt_caption,
    +      opt_description,
    +      goog.ui.media.MediaModel.MimeType.FLASH);
    +
    +  /**
    +   * The Flickr user id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.userId_ = userId;
    +
    +  /**
    +   * The Flickr set id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.setId_ = setId;
    +
    +  var flashVars = {
    +    'offsite': 'true',
    +    'lang': 'en',
    +    'page_show_url': '/photos/' + userId + '/sets/' + setId + '/show/',
    +    'page_show_back_url': '/photos/' + userId + '/sets/' + setId,
    +    'set_id': setId
    +  };
    +
    +  var player = new goog.ui.media.MediaModel.Player(
    +      goog.ui.media.FlickrSet.flashUrl_, flashVars);
    +
    +  this.setPlayer(player);
    +};
    +goog.inherits(goog.ui.media.FlickrSetModel, goog.ui.media.MediaModel);
    +
    +
    +/**
    + * Regular expression used to extract the username and set id out of the flickr
    + * URLs.
    + *
    + * Copied from http://go/markdownlite.js and {@link FlickrExtractor.xml}.
    + *
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.ui.media.FlickrSetModel.MATCHER_ =
    +    /(?:http:\/\/)?(?:www\.)?flickr\.com\/(?:photos\/([\d\w@\-]+)\/sets\/(\d+))\/?/i;
    +
    +
    +/**
    + * Takes a {@code flickrSetUrl} and extracts the flickr username and set id.
    + *
    + * @param {string} flickrSetUrl A Flickr set URL.
    + * @param {string=} opt_caption An optional caption of the flickr set.
    + * @param {string=} opt_description An optional description of the flickr set.
    + * @return {!goog.ui.media.FlickrSetModel} The data model that represents the
    + *     Flickr set.
    + * @throws exception in case the parsing fails
    + */
    +goog.ui.media.FlickrSetModel.newInstance = function(flickrSetUrl,
    +                                                    opt_caption,
    +                                                    opt_description) {
    +  if (goog.ui.media.FlickrSetModel.MATCHER_.test(flickrSetUrl)) {
    +    var data = goog.ui.media.FlickrSetModel.MATCHER_.exec(flickrSetUrl);
    +    return new goog.ui.media.FlickrSetModel(
    +        data[1], data[2], opt_caption, opt_description);
    +  }
    +  throw Error('failed to parse flickr url: ' + flickrSetUrl);
    +};
    +
    +
    +/**
    + * Takes a flickr username and set id and returns an URL.
    + *
    + * @param {string} userId The owner of the set.
    + * @param {string} setId The set id.
    + * @return {string} The URL of the set.
    + */
    +goog.ui.media.FlickrSetModel.buildUrl = function(userId, setId) {
    +  return 'http://flickr.com/photos/' + userId + '/sets/' + setId;
    +};
    +
    +
    +/**
    + * Gets the Flickr user id.
    + * @return {string} The Flickr user id.
    + */
    +goog.ui.media.FlickrSetModel.prototype.getUserId = function() {
    +  return this.userId_;
    +};
    +
    +
    +/**
    + * Gets the Flickr set id.
    + * @return {string} The Flickr set id.
    + */
    +goog.ui.media.FlickrSetModel.prototype.getSetId = function() {
    +  return this.setId_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.html
    new file mode 100644
    index 0000000000..daa388c173
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.FlickrSet
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.FlickrSetTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.js
    new file mode 100644
    index 0000000000..abe6393245
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/flickr_test.js
    @@ -0,0 +1,106 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.FlickrSetTest');
    +goog.setTestOnly('goog.ui.media.FlickrSetTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.html.testing');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.FlickrSet');
    +goog.require('goog.ui.media.FlickrSetModel');
    +goog.require('goog.ui.media.Media');
    +var flickr;
    +var control;
    +var FLICKR_USER = 'ingawalker';
    +var FLICKR_SET = '72057594102831547';
    +var FLICKRSET_URL =
    +    'http://flickr.com/photos/' + FLICKR_USER + '/sets/' + FLICKR_SET;
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  flickr = new goog.ui.media.FlickrSet();
    +  var set = new goog.ui.media.FlickrSetModel(FLICKR_USER, FLICKR_SET,
    +      'caption');
    +  control = new goog.ui.media.Media(set, flickr);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.FlickrSet.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +  assertEquals(FLICKRSET_URL, control.getDataModel().getUrl());
    +}
    +
    +function testParsingUrl() {
    +  assertExtractsCorrectly(FLICKR_USER, FLICKR_SET, FLICKRSET_URL);
    +  // user id with @ sign
    +  assertExtractsCorrectly('30441750@N06', '7215760789302468',
    +      'http://flickr.com/photos/30441750@N06/sets/7215760789302468/');
    +  // user id with - sign
    +  assertExtractsCorrectly('30441750-N06', '7215760789302468',
    +      'http://flickr.com/photos/30441750-N06/sets/7215760789302468/');
    +
    +  var invalidUrl = 'http://invalidUrl/filename.doc';
    +  var e = assertThrows('', function() {
    +    goog.ui.media.FlickrSetModel.newInstance(invalidUrl);
    +  });
    +  assertEquals('failed to parse flickr url: ' + invalidUrl, e.message);
    +}
    +
    +function testBuildingUrl() {
    +  assertEquals(FLICKRSET_URL,
    +      goog.ui.media.FlickrSetModel.buildUrl(
    +          FLICKR_USER, FLICKR_SET, FLICKRSET_URL));
    +}
    +
    +function testCreatingModel() {
    +  var model = new goog.ui.media.FlickrSetModel(FLICKR_USER, FLICKR_SET);
    +  assertEquals(FLICKR_USER, model.getUserId());
    +  assertEquals(FLICKR_SET, model.getSetId());
    +  assertEquals(FLICKRSET_URL, model.getUrl());
    +  assertUndefined(model.getCaption());
    +}
    +
    +function testSettingWhichFlashUrlToUse() {
    +  goog.ui.media.FlickrSet.setFlashUrl(
    +      goog.html.testing.newTrustedResourceUrlForTest('http://foo'));
    +  assertEquals('http://foo',
    +      goog.ui.media.FlickrSet.flashUrl_.getTypedStringValue());
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.FlickrSet.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function assertExtractsCorrectly(expectedUserId, expectedSetId, url) {
    +  var flickr = goog.ui.media.FlickrSetModel.newInstance(url);
    +  assertEquals('userId for ' + url, expectedUserId, flickr.getUserId());
    +  assertEquals('setId for ' + url, expectedSetId, flickr.getSetId());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo.js b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo.js
    new file mode 100644
    index 0000000000..10cc4c90a8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo.js
    @@ -0,0 +1,283 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview provides a reusable GoogleVideo UI component given a public
    + * GoogleVideo video URL.
    + *
    + * goog.ui.media.GoogleVideo is actually a {@link goog.ui.ControlRenderer}, a
    + * stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.GoogleVideo.getInstance} -, that knows how to
    + * render GoogleVideo videos. It is designed to be used with a
    + * {@link goog.ui.Control}, which will actually control the media renderer and
    + * provide the {@link goog.ui.Component} base. This design guarantees that all
    + * different types of medias will behave alike but will look different.
    + *
    + * goog.ui.media.GoogleVideo expects {@code goog.ui.media.GoogleVideoModel} on
    + * {@code goog.ui.Control.getModel} as data models, and renders a flash object
    + * that will show the contents of that video.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var video = goog.ui.media.GoogleVideoModel.newInstance(
    + *       'http://video.google.com/videoplay?docid=6698933542780842398');
    + *   goog.ui.media.GoogleVideo.newControl(video).render();
    + * </pre>
    + *
    + * GoogleVideo medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
    + *   <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
    + * </ul>
    + *
    + * Which can be accessed by
    + * <pre>
    + *   video.setEnabled(true);
    + *   video.setHighlighted(true);
    + *   video.setSelected(true);
    + * </pre>
    + *
    + *
    + * @supported IE6+, FF2+, Chrome, Safari. Requires flash to actually work.
    + */
    +
    +
    +goog.provide('goog.ui.media.GoogleVideo');
    +goog.provide('goog.ui.media.GoogleVideoModel');
    +
    +goog.require('goog.string');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a GoogleVideo specific
    + * media renderer.
    + *
    + * This class knows how to parse GoogleVideo URLs, and render the DOM structure
    + * of GoogleVideo video players. This class is meant to be used as a singleton
    + * static stateless class, that takes {@code goog.ui.media.Media} instances and
    + * renders it. It expects {@code goog.ui.media.Media.getModel} to return a well
    + * formed, previously constructed, GoogleVideo video id, which is the data model
    + * this renderer will use to construct the DOM structure.
    + * {@see goog.ui.media.GoogleVideo.newControl} for a example of constructing a
    + * control with this renderer.
    + *
    + * This design is patterned after http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.GoogleVideo = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.GoogleVideo, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.GoogleVideo);
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a GoogleVideo model. It sets it as the data model goog.ui.media.GoogleVideo
    + * renderer uses, sets the states supported by the renderer, and returns a
    + * Control that binds everything together. This is what you should be using for
    + * constructing GoogleVideo videos, except if you need finer control over the
    + * configuration.
    + *
    + * @param {goog.ui.media.GoogleVideoModel} dataModel The GoogleVideo data model.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A Control binded to the GoogleVideo renderer.
    + */
    +goog.ui.media.GoogleVideo.newControl = function(dataModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      dataModel,
    +      goog.ui.media.GoogleVideo.getInstance(),
    +      opt_domHelper);
    +  // GoogleVideo videos don't have any thumbnail for now, so we show the
    +  // "selected" version of the UI at the start, which is the flash player.
    +  control.setSelected(true);
    +  return control;
    +};
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.GoogleVideo.CSS_CLASS =
    +    goog.getCssName('goog-ui-media-googlevideo');
    +
    +
    +/**
    + * Creates the initial DOM structure of the GoogleVideo video, which is
    + * basically a the flash object pointing to a GoogleVideo video player.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} The DOM structure that represents this control.
    + * @override
    + */
    +goog.ui.media.GoogleVideo.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.GoogleVideo.base(this, 'createDom', control);
    +
    +  var dataModel =
    +      /** @type {goog.ui.media.GoogleVideoModel} */ (control.getDataModel());
    +
    +  var flash = new goog.ui.media.FlashObject(
    +      dataModel.getPlayer().getTrustedResourceUrl(),
    +      control.getDomHelper());
    +  flash.render(div);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + *
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.GoogleVideo.prototype.getCssClass = function() {
    +  return goog.ui.media.GoogleVideo.CSS_CLASS;
    +};
    +
    +
    +
    +/**
    + * The {@code goog.ui.media.GoogleVideo} media data model. It stores a required
    + * {@code videoId} field, sets the GoogleVideo URL, and allows a few optional
    + * parameters.
    + *
    + * @param {string} videoId The GoogleVideo video id.
    + * @param {string=} opt_caption An optional caption of the GoogleVideo video.
    + * @param {string=} opt_description An optional description of the GoogleVideo
    + *     video.
    + * @param {boolean=} opt_autoplay Whether to autoplay video.
    + * @constructor
    + * @extends {goog.ui.media.MediaModel}
    + * @final
    + */
    +goog.ui.media.GoogleVideoModel = function(videoId, opt_caption, opt_description,
    +                                          opt_autoplay) {
    +  goog.ui.media.MediaModel.call(
    +      this,
    +      goog.ui.media.GoogleVideoModel.buildUrl(videoId),
    +      opt_caption,
    +      opt_description,
    +      goog.ui.media.MediaModel.MimeType.FLASH);
    +
    +  /**
    +   * The GoogleVideo video id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.videoId_ = videoId;
    +
    +  this.setPlayer(new goog.ui.media.MediaModel.Player(
    +      goog.ui.media.GoogleVideoModel.buildFlashUrl(videoId, opt_autoplay)));
    +};
    +goog.inherits(goog.ui.media.GoogleVideoModel, goog.ui.media.MediaModel);
    +
    +
    +/**
    + * Regular expression used to extract the GoogleVideo video id (docid) out of
    + * GoogleVideo URLs.
    + *
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.ui.media.GoogleVideoModel.MATCHER_ =
    +    /^http:\/\/(?:www\.)?video\.google\.com\/videoplay.*[\?#]docid=(-?[0-9]+)#?$/i;
    +
    +
    +/**
    + * A auxiliary static method that parses a GoogleVideo URL, extracting the ID of
    + * the video, and builds a GoogleVideoModel.
    + *
    + * @param {string} googleVideoUrl A GoogleVideo video URL.
    + * @param {string=} opt_caption An optional caption of the GoogleVideo video.
    + * @param {string=} opt_description An optional description of the GoogleVideo
    + *     video.
    + * @param {boolean=} opt_autoplay Whether to autoplay video.
    + * @return {!goog.ui.media.GoogleVideoModel} The data model that represents the
    + *     GoogleVideo URL.
    + * @see goog.ui.media.GoogleVideoModel.getVideoId()
    + * @throws Error in case the parsing fails.
    + */
    +goog.ui.media.GoogleVideoModel.newInstance = function(googleVideoUrl,
    +                                                      opt_caption,
    +                                                      opt_description,
    +                                                      opt_autoplay) {
    +  if (goog.ui.media.GoogleVideoModel.MATCHER_.test(googleVideoUrl)) {
    +    var data = goog.ui.media.GoogleVideoModel.MATCHER_.exec(googleVideoUrl);
    +    return new goog.ui.media.GoogleVideoModel(
    +        data[1], opt_caption, opt_description, opt_autoplay);
    +  }
    +
    +  throw Error('failed to parse video id from GoogleVideo url: ' +
    +      googleVideoUrl);
    +};
    +
    +
    +/**
    + * The opposite of {@code goog.ui.media.GoogleVideo.newInstance}: it takes a
    + * videoId and returns a GoogleVideo URL.
    + *
    + * @param {string} videoId The GoogleVideo video ID.
    + * @return {string} The GoogleVideo URL.
    + */
    +goog.ui.media.GoogleVideoModel.buildUrl = function(videoId) {
    +  return 'http://video.google.com/videoplay?docid=' +
    +      goog.string.urlEncode(videoId);
    +};
    +
    +
    +/**
    + * An auxiliary method that builds URL of the flash movie to be embedded,
    + * out of the GoogleVideo video id.
    + *
    + * @param {string} videoId The GoogleVideo video ID.
    + * @param {boolean=} opt_autoplay Whether the flash movie should start playing
    + *     as soon as it is shown, or if it should show a 'play' button.
    + * @return {string} The flash URL to be embedded on the page.
    + */
    +goog.ui.media.GoogleVideoModel.buildFlashUrl = function(videoId, opt_autoplay) {
    +  var autoplay = opt_autoplay ? '&autoplay=1' : '';
    +  return 'http://video.google.com/googleplayer.swf?docid=' +
    +      goog.string.urlEncode(videoId) +
    +      '&hl=en&fs=true' + autoplay;
    +};
    +
    +
    +/**
    + * Gets the GoogleVideo video id.
    + * @return {string} The GoogleVideo video id.
    + */
    +goog.ui.media.GoogleVideoModel.prototype.getVideoId = function() {
    +  return this.videoId_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.html
    new file mode 100644
    index 0000000000..28b71845ba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.GoogleVideo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.GoogleVideoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.js
    new file mode 100644
    index 0000000000..09131868d7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/googlevideo_test.js
    @@ -0,0 +1,94 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.GoogleVideoTest');
    +goog.setTestOnly('goog.ui.media.GoogleVideoTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.GoogleVideo');
    +goog.require('goog.ui.media.GoogleVideoModel');
    +goog.require('goog.ui.media.Media');
    +var video;
    +var control;
    +var VIDEO_URL_PREFIX = 'http://video.google.com/videoplay?docid=';
    +var VIDEO_ID = '7582902000166025817';
    +var VIDEO_URL = VIDEO_URL_PREFIX + VIDEO_ID;
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  video = new goog.ui.media.GoogleVideo();
    +  var model = new goog.ui.media.GoogleVideoModel(VIDEO_ID, 'video caption');
    +  control = new goog.ui.media.Media(model, video);
    +  control.setSelected(true);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.GoogleVideo.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +  assertEquals(VIDEO_URL, control.getDataModel().getUrl());
    +}
    +
    +function testParsingUrl() {
    +  assertExtractsCorrectly(VIDEO_ID, VIDEO_URL);
    +  // Test a url with # at the end.
    +  assertExtractsCorrectly(VIDEO_ID, VIDEO_URL + '#');
    +  // Test a url with a negative docid.
    +  assertExtractsCorrectly('-123', VIDEO_URL_PREFIX + '-123');
    +  // Test a url with two docids. The valid one is the second.
    +  assertExtractsCorrectly('123', VIDEO_URL + '#docid=123');
    +
    +  var invalidUrl = 'http://invalidUrl/filename.doc';
    +  var e = assertThrows('parser expects a well formed URL', function() {
    +    goog.ui.media.GoogleVideoModel.newInstance(invalidUrl);
    +  });
    +  assertEquals('failed to parse video id from GoogleVideo url: ' + invalidUrl,
    +      e.message);
    +}
    +
    +function testBuildingUrl() {
    +  assertEquals(VIDEO_URL, goog.ui.media.GoogleVideoModel.buildUrl(VIDEO_ID));
    +}
    +
    +function testCreatingModel() {
    +  var model = new goog.ui.media.GoogleVideoModel(VIDEO_ID);
    +  assertEquals(VIDEO_ID, model.getVideoId());
    +  assertEquals(VIDEO_URL, model.getUrl());
    +  assertUndefined(model.getCaption());
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.GoogleVideo.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function assertExtractsCorrectly(expectedVideoId, url) {
    +  var model = goog.ui.media.GoogleVideoModel.newInstance(url);
    +  assertEquals('Video id for ' + url, expectedVideoId, model.getVideoId());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/media.js b/mapbox-gl/closure-library/closure/goog/ui/media/media.js
    new file mode 100644
    index 0000000000..3001c5c299
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/media.js
    @@ -0,0 +1,288 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the base goog.ui.Control and goog.ui.ControlRenderer
    + * for media types, as well as a media model consistent with the Yahoo Media RSS
    + * specification {@link http://search.yahoo.com/mrss/}.
    + *
    + * The goog.ui.media.* package is basically a set of goog.ui.ControlRenderers
    + * subclasses (such as goog.ui.media.Youtube, goog.ui.media.Picasa, etc) that
    + * should all work with the same goog.ui.Control (goog.ui.media.Media) logic.
    + *
    + * This design guarantees that all different types of medias will behave alike
    + * (in a base level) but will look different.
    + *
    + * In MVC terms, {@link goog.ui.media.Media} is the Controller,
    + * {@link goog.ui.media.MediaRenderer} + CSS definitions are the View and
    + * {@code goog.ui.media.MediaModel} is the data Model. Typically,
    + * MediaRenderer will be subclassed to provide media specific renderers.
    + * MediaRenderer subclasses are also responsible for defining the data model.
    + *
    + * This design is strongly patterned after:
    + * http://go/closure_control_subclassing
    + *
    + * goog.ui.media.MediaRenderer handles the basic common ways to display media,
    + * such as displaying tooltips, frames, minimize/maximize buttons, play buttons,
    + * etc. Its subclasses are responsible for rendering media specific DOM
    + * structures, like youtube flash players, picasa albums, etc.
    + *
    + * goog.ui.media.Media handles the Control of Medias, by listening to events
    + * and firing the appropriate actions. It knows about the existence of captions,
    + * minimize/maximize buttons, and takes all the actions needed to change states,
    + * including delegating the UI actions to MediaRenderers.
    + *
    + * Although MediaRenderer is a base class designed to be subclassed, it can
    + * be used by itself:
    + *
    + * <pre>
    + *   var renderer = new goog.ui.media.MediaRenderer();
    + *   var control = new goog.ui.media.Media('hello world', renderer);
    + *   var control.render(goog.dom.getElement('mediaHolder'));
    + * </pre>
    + *
    + * It requires a few CSS rules to be defined, which you should use to control
    + * how the component is displayed. {@link goog.ui.ControlRenderer}s is very CSS
    + * intensive, which separates the UI structure (the HTML DOM elements, which is
    + * created by the {@code goog.ui.media.MediaRenderer}) from the UI view (which
    + * nodes are visible, which aren't, where they are positioned. These are defined
    + * on the CSS rules for each state). A few examples of CSS selectors that needs
    + * to be defined are:
    + *
    + * <ul>
    + *   <li>.goog-ui-media
    + *   <li>.goog-ui-media-hover
    + *   <li>.goog-ui-media-selected
    + * </ul>
    + *
    + * If you want to have different custom renderers CSS namespaces (eg. you may
    + * want to show a small thumbnail, or you may want to hide the caption, etc),
    + * you can do so by using:
    + *
    + * <pre>
    + *   var renderer = goog.ui.ControlRenderer.getCustomRenderer(
    + *       goog.ui.media.MediaRenderer, 'my-custom-namespace');
    + *   var media = new goog.ui.media.Media('', renderer);
    + *   media.render(goog.dom.getElement('parent'));
    + * </pre>
    + *
    + * Which will allow you to set your own .my-custom-namespace-hover,
    + * .my-custom-namespace-selected CSS selectors.
    + *
    + * NOTE(user): it seems like an overkill to subclass goog.ui.Control instead of
    + * using a factory, but we wanted to make sure we had more control over the
    + * events for future media implementations. Since we intent to use it in many
    + * different places, it makes sense to have a more flexible design that lets us
    + * control the inner workings of goog.ui.Control.
    + *
    + * TODO(user): implement, as needed, the Media specific state changes UI, such
    + * as minimize/maximize buttons, expand/close buttons, etc.
    + *
    + */
    +
    +goog.provide('goog.ui.media.Media');
    +goog.provide('goog.ui.media.MediaRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Provides the control mechanism of media types.
    + *
    + * @param {goog.ui.media.MediaModel} dataModel The data model to be used by the
    + *     renderer.
    + * @param {goog.ui.ControlRenderer=} opt_renderer Renderer used to render or
    + *     decorate the component; defaults to {@link goog.ui.ControlRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + * @final
    + */
    +goog.ui.media.Media = function(dataModel, opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, null, opt_renderer, opt_domHelper);
    +
    +  // Sets up the data model.
    +  this.setDataModel(dataModel);
    +  this.setSupportedState(goog.ui.Component.State.OPENED, true);
    +  this.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  // TODO(user): had to do this to for mouseDownHandler not to
    +  // e.preventDefault(), because it was not allowing the event to reach the
    +  // flash player. figure out a better way to not e.preventDefault().
    +  this.setAllowTextSelection(true);
    +
    +  // Media items don't use RTL styles, so avoid accessing computed styles to
    +  // figure out if the control is RTL.
    +  this.setRightToLeft(false);
    +};
    +goog.inherits(goog.ui.media.Media, goog.ui.Control);
    +
    +
    +/**
    + * The media data model used on the renderer.
    + *
    + * @type {goog.ui.media.MediaModel}
    + * @private
    + */
    +goog.ui.media.Media.prototype.dataModel_;
    +
    +
    +/**
    + * Sets the media model to be used on the renderer.
    + * @param {goog.ui.media.MediaModel} dataModel The media model the renderer
    + *     should use.
    + */
    +goog.ui.media.Media.prototype.setDataModel = function(dataModel) {
    +  this.dataModel_ = dataModel;
    +};
    +
    +
    +/**
    + * Gets the media model renderer is using.
    + * @return {goog.ui.media.MediaModel} The media model being used.
    + */
    +goog.ui.media.Media.prototype.getDataModel = function() {
    +  return this.dataModel_;
    +};
    +
    +
    +
    +/**
    + * Base class of all media renderers. Provides the common renderer functionality
    + * of medias.
    + *
    + * The current common functionality shared by Medias is to have an outer frame
    + * that gets highlighted on mouse hover.
    + *
    + * TODO(user): implement more common UI behavior, as needed.
    + *
    + * NOTE(user): I am not enjoying how the subclasses are changing their state
    + * through setState() ... maybe provide abstract methods like
    + * goog.ui.media.MediaRenderer.prototype.preview = goog.abstractMethod;
    + * goog.ui.media.MediaRenderer.prototype.play = goog.abstractMethod;
    + * goog.ui.media.MediaRenderer.prototype.minimize = goog.abstractMethod;
    + * goog.ui.media.MediaRenderer.prototype.maximize = goog.abstractMethod;
    + * and call them on this parent class setState ?
    + *
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.media.MediaRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.MediaRenderer, goog.ui.ControlRenderer);
    +
    +
    +/**
    + * Builds the common DOM structure of medias. Builds an outer div, and appends
    + * a child div with the {@code goog.ui.Control.getContent} content. Marks the
    + * caption with a {@code this.getClassClass()} + '-caption' css flag, so that
    + * specific renderers can hide/show the caption as desired.
    + *
    + * @param {goog.ui.Control} control The control instance.
    + * @return {!Element} The DOM structure that represents control.
    + * @override
    + */
    +goog.ui.media.MediaRenderer.prototype.createDom = function(control) {
    +  goog.asserts.assertInstanceof(control, goog.ui.media.Media);
    +  var domHelper = control.getDomHelper();
    +  var div = domHelper.createElement('div');
    +  div.className = this.getClassNames(control).join(' ');
    +
    +  var dataModel = control.getDataModel();
    +
    +  // Only creates DOMs if the data is available.
    +  if (dataModel.getCaption()) {
    +    var caption = domHelper.createElement('div');
    +    caption.className = goog.getCssName(this.getCssClass(), 'caption');
    +    caption.appendChild(domHelper.createDom(
    +        'p', goog.getCssName(this.getCssClass(), 'caption-text'),
    +        dataModel.getCaption()));
    +    domHelper.appendChild(div, caption);
    +  }
    +
    +  if (dataModel.getDescription()) {
    +    var description = domHelper.createElement('div');
    +    description.className = goog.getCssName(this.getCssClass(), 'description');
    +    description.appendChild(domHelper.createDom(
    +        'p', goog.getCssName(this.getCssClass(), 'description-text'),
    +        dataModel.getDescription()));
    +    domHelper.appendChild(div, description);
    +  }
    +
    +  // Creates thumbnails of the media.
    +  var thumbnails = dataModel.getThumbnails() || [];
    +  for (var index = 0; index < thumbnails.length; index++) {
    +    var thumbnail = thumbnails[index];
    +    var thumbnailElement = domHelper.createElement('img');
    +    thumbnailElement.src = thumbnail.getUrl();
    +    thumbnailElement.className = this.getThumbnailCssName(index);
    +
    +    // Check that the size is defined and that the size's height and width
    +    // are defined. Undefined height and width is deprecated but still
    +    // seems to exist in some cases.
    +    var size = thumbnail.getSize();
    +
    +    if (size && goog.isDefAndNotNull(size.height) &&
    +        goog.isDefAndNotNull(size.width)) {
    +      goog.style.setSize(thumbnailElement, size);
    +    }
    +    domHelper.appendChild(div, thumbnailElement);
    +  }
    +
    +  if (dataModel.getPlayer()) {
    +    // if medias have players, allow UI for a play button.
    +    var playButton = domHelper.createElement('div');
    +    playButton.className = goog.getCssName(this.getCssClass(), 'playbutton');
    +    domHelper.appendChild(div, playButton);
    +  }
    +
    +  control.setElementInternal(div);
    +
    +  this.setState(
    +      control,
    +      /** @type {goog.ui.Component.State} */ (control.getState()),
    +      true);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns a renamable CSS class name for a numbered thumbnail. The default
    + * implementation generates the class names goog-ui-media-thumbnail0,
    + * goog-ui-media-thumbnail1, and the generic goog-ui-media-thumbnailn.
    + * Subclasses can override this method when their media requires additional
    + * specific class names (Applications are supposed to know how many thumbnails
    + * media will have).
    + *
    + * @param {number} index The thumbnail index.
    + * @return {string} CSS class name.
    + * @protected
    + */
    +goog.ui.media.MediaRenderer.prototype.getThumbnailCssName = function(index) {
    +  switch (index) {
    +    case 0: return goog.getCssName(this.getCssClass(), 'thumbnail0');
    +    case 1: return goog.getCssName(this.getCssClass(), 'thumbnail1');
    +    case 2: return goog.getCssName(this.getCssClass(), 'thumbnail2');
    +    case 3: return goog.getCssName(this.getCssClass(), 'thumbnail3');
    +    case 4: return goog.getCssName(this.getCssClass(), 'thumbnail4');
    +    default: return goog.getCssName(this.getCssClass(), 'thumbnailn');
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/media_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/media_test.html
    new file mode 100644
    index 0000000000..ef31c83cb8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/media_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Media
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.MediaTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/media_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/media_test.js
    new file mode 100644
    index 0000000000..d61e0d20d6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/media_test.js
    @@ -0,0 +1,130 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.MediaTest');
    +goog.setTestOnly('goog.ui.media.MediaTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.math.Size');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +var control;  // The name 'media' collides with a built-in var in Chrome.
    +var renderer;
    +var model;
    +
    +function setUp() {
    +  renderer = new goog.ui.media.MediaRenderer();
    +  model = new goog.ui.media.MediaModel(
    +      'http://url.com', 'a caption', 'a description');
    +  control = new goog.ui.media.Media(model, renderer);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicElements() {
    +  var model = new goog.ui.media.MediaModel(
    +      'http://url.com', 'a caption', 'a description');
    +  var thumb1 = new goog.ui.media.MediaModel.Thumbnail(
    +      'http://thumb.com/small.jpg', new goog.math.Size(320, 288));
    +  var thumb2 = new goog.ui.media.MediaModel.Thumbnail(
    +      'http://thumb.com/big.jpg', new goog.math.Size(800, 600));
    +  model.setThumbnails([thumb1, thumb2]);
    +  model.setPlayer(new goog.ui.media.MediaModel.Player(
    +      'http://media/player.swf'));
    +  var control = new goog.ui.media.Media(model, renderer);
    +  control.render();
    +
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      undefined,
    +      goog.ui.ControlRenderer.CSS_CLASS + '-caption');
    +  var description = goog.dom.getElementsByTagNameAndClass(
    +      undefined,
    +      goog.ui.ControlRenderer.CSS_CLASS + '-description');
    +  var thumbnail0 = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.ControlRenderer.CSS_CLASS + '-thumbnail0');
    +  var thumbnail1 = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.ControlRenderer.CSS_CLASS + '-thumbnail1');
    +  var player = goog.dom.getElementsByTagNameAndClass(
    +      'iframe',
    +      goog.ui.ControlRenderer.CSS_CLASS + '-player');
    +
    +  assertNotNull(caption);
    +  assertEquals(1, caption.length);
    +  assertNotNull(description);
    +  assertEquals(1, description.length);
    +  assertNotNull(thumbnail0);
    +  assertEquals(1, thumbnail0.length);
    +  assertEquals('320px', thumbnail0[0].style.width);
    +  assertEquals('288px', thumbnail0[0].style.height);
    +  assertEquals('http://thumb.com/small.jpg', thumbnail0[0].src);
    +  assertNotNull(thumbnail1);
    +  assertEquals(1, thumbnail1.length);
    +  assertEquals('800px', thumbnail1[0].style.width);
    +  assertEquals('600px', thumbnail1[0].style.height);
    +  assertEquals('http://thumb.com/big.jpg', thumbnail1[0].src);
    +  // players are only shown when media is selected
    +  assertNotNull(player);
    +  assertEquals(0, player.length);
    +
    +  control.dispose();
    +}
    +
    +function testDoesntCreatesCaptionIfUnavailable() {
    +  var incompleteModel = new goog.ui.media.MediaModel(
    +      'http://url.com', undefined, 'a description');
    +  incompleteMedia = new goog.ui.media.Media('', renderer);
    +  incompleteMedia.setDataModel(incompleteModel);
    +  incompleteMedia.render();
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      undefined,
    +      goog.ui.ControlRenderer.CSS_CLASS + '-caption');
    +  var description = goog.dom.getElementsByTagNameAndClass(
    +      undefined,
    +      goog.ui.ControlRenderer.CSS_CLASS + '-description');
    +  assertEquals(0, caption.length);
    +  assertNotNull(description);
    +  incompleteMedia.dispose();
    +}
    +
    +function testSetAriaLabel() {
    +  var model = new goog.ui.media.MediaModel(
    +      'http://url.com', 'a caption', 'a description');
    +  var thumb1 = new goog.ui.media.MediaModel.Thumbnail(
    +      'http://thumb.com/small.jpg', new goog.math.Size(320, 288));
    +  var thumb2 = new goog.ui.media.MediaModel.Thumbnail(
    +      'http://thumb.com/big.jpg', new goog.math.Size(800, 600));
    +  model.setThumbnails([thumb1, thumb2]);
    +  model.setPlayer(new goog.ui.media.MediaModel.Player(
    +      'http://media/player.swf'));
    +  var control = new goog.ui.media.Media(model, renderer);
    +  assertNull('Media must not have aria label by default',
    +      control.getAriaLabel());
    +  control.setAriaLabel('My media');
    +  control.render();
    +  var element = control.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Media element must have expected aria-label', 'My media',
    +      element.getAttribute('aria-label'));
    +  assertTrue(goog.dom.isFocusableTabIndex(element));
    +  control.setAriaLabel('My new media');
    +  assertEquals('Media element must have updated aria-label', 'My new media',
    +      element.getAttribute('aria-label'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel.js b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel.js
    new file mode 100644
    index 0000000000..35654cf799
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel.js
    @@ -0,0 +1,978 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the base media model consistent with the Yahoo Media
    + * RSS specification {@link http://search.yahoo.com/mrss/}.
    + */
    +
    +goog.provide('goog.ui.media.MediaModel');
    +goog.provide('goog.ui.media.MediaModel.Category');
    +goog.provide('goog.ui.media.MediaModel.Credit');
    +goog.provide('goog.ui.media.MediaModel.Credit.Role');
    +goog.provide('goog.ui.media.MediaModel.Credit.Scheme');
    +goog.provide('goog.ui.media.MediaModel.Medium');
    +goog.provide('goog.ui.media.MediaModel.MimeType');
    +goog.provide('goog.ui.media.MediaModel.Player');
    +goog.provide('goog.ui.media.MediaModel.SubTitle');
    +goog.provide('goog.ui.media.MediaModel.Thumbnail');
    +
    +goog.require('goog.array');
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.html.legacyconversions');
    +
    +
    +
    +/**
    + * An base data value class for all media data models.
    + *
    + * MediaModels are exact matches to the fields defined in the Yahoo RSS media
    + * specification {@link http://search.yahoo.com/mrss/}.
    + *
    + * The current common data shared by medias is to have URLs, mime types,
    + * captions, descriptions, thumbnails and players. Some of these may not be
    + * available, or applications may not want to render them, so {@code null}
    + * values are allowed. {@code goog.ui.media.MediaRenderer} checks whether the
    + * values are available before creating DOMs for them.
    + *
    + * TODO(user): support asynchronous data models by subclassing
    + * {@link goog.events.EventTarget} or {@link goog.ds.DataNode}. Understand why
    + * {@link http://goto/datanode} is not available in closure. Add setters to
    + * MediaModel once this is supported.
    + *
    + * @param {string=} opt_url An optional URL of the media.
    + * @param {string=} opt_caption An optional caption of the media.
    + * @param {string=} opt_description An optional description of the media.
    + * @param {goog.ui.media.MediaModel.MimeType=} opt_type The type of the media.
    + * @param {goog.ui.media.MediaModel.Medium=} opt_medium The medium of the media.
    + * @param {number=} opt_duration The duration of the media in seconds.
    + * @param {number=} opt_width The width of the media in pixels.
    + * @param {number=} opt_height The height of the media in pixels.
    + * @constructor
    + */
    +goog.ui.media.MediaModel = function(opt_url,
    +                                    opt_caption,
    +                                    opt_description,
    +                                    opt_type,
    +                                    opt_medium,
    +                                    opt_duration,
    +                                    opt_width,
    +                                    opt_height) {
    +  /**
    +   * The URL of the media.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.url_ = opt_url;
    +
    +  /**
    +   * The caption of the media.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.caption_ = opt_caption;
    +
    +  /**
    +   * A description of the media, typically user generated comments about it.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.description_ = opt_description;
    +
    +  /**
    +   * The mime type of the media.
    +   * @type {goog.ui.media.MediaModel.MimeType|undefined}
    +   * @private
    +   */
    +  this.type_ = opt_type;
    +
    +  /**
    +   * The medium of the media.
    +   * @type {goog.ui.media.MediaModel.Medium|undefined}
    +   * @private
    +   */
    +  this.medium_ = opt_medium;
    +
    +  /**
    +   * The duration of the media in seconds.
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.duration_ = opt_duration;
    +
    +  /**
    +   * The width of the media in pixels.
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.width_ = opt_width;
    +
    +  /**
    +   * The height of the media in pixels.
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.height_ = opt_height;
    +
    +  /**
    +   * A list of thumbnails representations of the media (eg different sizes of
    +   * the same photo, etc).
    +   * @type {Array<goog.ui.media.MediaModel.Thumbnail>}
    +   * @private
    +   */
    +  this.thumbnails_ = [];
    +
    +  /**
    +   * The list of categories that are applied to this media.
    +   * @type {Array<goog.ui.media.MediaModel.Category>}
    +   * @private
    +   */
    +  this.categories_ = [];
    +
    +  /**
    +   * The list of credits that pertain to this media object.
    +   * @type {!Array<goog.ui.media.MediaModel.Credit>}
    +   * @private
    +   */
    +  this.credits_ = [];
    +
    +  /**
    +   * The list of subtitles for the media object.
    +   * @type {Array<goog.ui.media.MediaModel.SubTitle>}
    +   * @private
    +   */
    +  this.subTitles_ = [];
    +};
    +
    +
    +/**
    + * The supported media mime types, a subset of the media types found here:
    + * {@link http://www.iana.org/assignments/media-types/} and here
    + * {@link http://en.wikipedia.org/wiki/Internet_media_type}
    + * @enum {string}
    + */
    +goog.ui.media.MediaModel.MimeType = {
    +  HTML: 'text/html',
    +  PLAIN: 'text/plain',
    +  FLASH: 'application/x-shockwave-flash',
    +  JPEG: 'image/jpeg',
    +  GIF: 'image/gif',
    +  PNG: 'image/png'
    +};
    +
    +
    +/**
    + * Supported mediums, found here:
    + * {@link http://video.search.yahoo.com/mrss}
    + * @enum {string}
    + */
    +goog.ui.media.MediaModel.Medium = {
    +  IMAGE: 'image',
    +  AUDIO: 'audio',
    +  VIDEO: 'video',
    +  DOCUMENT: 'document',
    +  EXECUTABLE: 'executable'
    +};
    +
    +
    +/**
    + * The media player.
    + * @type {goog.ui.media.MediaModel.Player}
    + * @private
    + */
    +goog.ui.media.MediaModel.prototype.player_;
    +
    +
    +/**
    + * Gets the URL of this media.
    + * @return {string|undefined} The URL of the media.
    + */
    +goog.ui.media.MediaModel.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * Sets the URL of this media.
    + * @param {string} url The URL of the media.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setUrl = function(url) {
    +  this.url_ = url;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the caption of this media.
    + * @return {string|undefined} The caption of the media.
    + */
    +goog.ui.media.MediaModel.prototype.getCaption = function() {
    +  return this.caption_;
    +};
    +
    +
    +/**
    + * Sets the caption of this media.
    + * @param {string} caption The caption of the media.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setCaption = function(caption) {
    +  this.caption_ = caption;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the media mime type.
    + * @return {goog.ui.media.MediaModel.MimeType|undefined} The media mime type.
    + */
    +goog.ui.media.MediaModel.prototype.getType = function() {
    +  return this.type_;
    +};
    +
    +
    +/**
    + * Sets the media mime type.
    + * @param {goog.ui.media.MediaModel.MimeType} type The media mime type.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setType = function(type) {
    +  this.type_ = type;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the media medium.
    + * @return {goog.ui.media.MediaModel.Medium|undefined} The media medium.
    + */
    +goog.ui.media.MediaModel.prototype.getMedium = function() {
    +  return this.medium_;
    +};
    +
    +
    +/**
    + * Sets the media medium.
    + * @param {goog.ui.media.MediaModel.Medium} medium The media medium.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setMedium = function(medium) {
    +  this.medium_ = medium;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the description of this media.
    + * @return {string|undefined} The description of the media.
    + */
    +goog.ui.media.MediaModel.prototype.getDescription = function() {
    +  return this.description_;
    +};
    +
    +
    +/**
    + * Sets the description of this media.
    + * @param {string} description The description of the media.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setDescription = function(description) {
    +  this.description_ = description;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the thumbnail urls.
    + * @return {Array<goog.ui.media.MediaModel.Thumbnail>} The list of thumbnails.
    + */
    +goog.ui.media.MediaModel.prototype.getThumbnails = function() {
    +  return this.thumbnails_;
    +};
    +
    +
    +/**
    + * Sets the thumbnail list.
    + * @param {Array<goog.ui.media.MediaModel.Thumbnail>} thumbnails The list of
    + *     thumbnail.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setThumbnails = function(thumbnails) {
    +  this.thumbnails_ = thumbnails;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the duration of the media.
    + * @return {number|undefined} The duration in seconds.
    + */
    +goog.ui.media.MediaModel.prototype.getDuration = function() {
    +  return this.duration_;
    +};
    +
    +
    +/**
    + * Sets duration of the media.
    + * @param {number} duration The duration of the media, in seconds.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setDuration = function(duration) {
    +  this.duration_ = duration;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the width of the media in pixels.
    + * @return {number|undefined} The width in pixels.
    + */
    +goog.ui.media.MediaModel.prototype.getWidth = function() {
    +  return this.width_;
    +};
    +
    +
    +/**
    + * Sets the width of the media.
    + * @param {number} width The width of the media, in pixels.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setWidth = function(width) {
    +  this.width_ = width;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the height of the media in pixels.
    + * @return {number|undefined} The height in pixels.
    + */
    +goog.ui.media.MediaModel.prototype.getHeight = function() {
    +  return this.height_;
    +};
    +
    +
    +/**
    + * Sets the height of the media.
    + * @param {number} height The height of the media, in pixels.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setHeight = function(height) {
    +  this.height_ = height;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the player data.
    + * @return {goog.ui.media.MediaModel.Player|undefined} The media player data.
    + */
    +goog.ui.media.MediaModel.prototype.getPlayer = function() {
    +  return this.player_;
    +};
    +
    +
    +/**
    + * Sets the player data.
    + * @param {goog.ui.media.MediaModel.Player} player The media player data.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setPlayer = function(player) {
    +  this.player_ = player;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the categories of the media.
    + * @return {Array<goog.ui.media.MediaModel.Category>} The categories of the
    + *     media.
    + */
    +goog.ui.media.MediaModel.prototype.getCategories = function() {
    +  return this.categories_;
    +};
    +
    +
    +/**
    + * Sets the categories of the media
    + * @param {Array<goog.ui.media.MediaModel.Category>} categories The categories
    + *     of the media.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setCategories = function(categories) {
    +  this.categories_ = categories;
    +  return this;
    +};
    +
    +
    +/**
    + * Finds the first category with the given scheme.
    + * @param {string} scheme The scheme to search for.
    + * @return {goog.ui.media.MediaModel.Category} The category that has the
    + *     given scheme. May be null.
    + */
    +goog.ui.media.MediaModel.prototype.findCategoryWithScheme = function(scheme) {
    +  if (!this.categories_) {
    +    return null;
    +  }
    +  var category = goog.array.find(this.categories_, function(category) {
    +    return category ? (scheme == category.getScheme()) : false;
    +  });
    +  return /** @type {goog.ui.media.MediaModel.Category} */ (category);
    +};
    +
    +
    +/**
    + * Gets the credits of the media.
    + * @return {!Array<goog.ui.media.MediaModel.Credit>} The credits of the media.
    + */
    +goog.ui.media.MediaModel.prototype.getCredits = function() {
    +  return this.credits_;
    +};
    +
    +
    +/**
    + * Sets the credits of the media
    + * @param {!Array<goog.ui.media.MediaModel.Credit>} credits The credits of the
    + *     media.
    + * @return {!goog.ui.media.MediaModel} The object itself, used for chaining.
    + */
    +goog.ui.media.MediaModel.prototype.setCredits = function(credits) {
    +  this.credits_ = credits;
    +  return this;
    +};
    +
    +
    +/**
    + * Finds all credits with the given role.
    + * @param {string} role The role to search for.
    + * @return {!Array<!goog.ui.media.MediaModel.Credit>} An array of credits
    + *     with the given role. May be empty.
    + */
    +goog.ui.media.MediaModel.prototype.findCreditsWithRole = function(role) {
    +  var credits = goog.array.filter(this.credits_, function(credit) {
    +    return role == credit.getRole();
    +  });
    +  return /** @type {!Array<!goog.ui.media.MediaModel.Credit>} */ (credits);
    +};
    +
    +
    +/**
    + * Gets the subtitles for the media.
    + * @return {Array<goog.ui.media.MediaModel.SubTitle>} The subtitles.
    + */
    +goog.ui.media.MediaModel.prototype.getSubTitles = function() {
    +  return this.subTitles_;
    +};
    +
    +
    +/**
    + * Sets the subtitles for the media
    + * @param {Array<goog.ui.media.MediaModel.SubTitle>} subtitles The subtitles.
    + * @return {!goog.ui.media.MediaModel} The object itself.
    + */
    +goog.ui.media.MediaModel.prototype.setSubTitles = function(subtitles) {
    +  this.subTitles_ = subtitles;
    +  return this;
    +};
    +
    +
    +
    +/**
    + * Constructs a thumbnail containing details of the thumbnail's image URL and
    + * optionally its size.
    + * @param {string} url The URL of the thumbnail's image.
    + * @param {goog.math.Size=} opt_size The size of the thumbnail's image if known.
    + * @constructor
    + * @final
    + */
    +goog.ui.media.MediaModel.Thumbnail = function(url, opt_size) {
    +  /**
    +   * The thumbnail's image URL.
    +   * @type {string}
    +   * @private
    +   */
    +  this.url_ = url;
    +
    +  /**
    +   * The size of the thumbnail's image if known.
    +   * @type {goog.math.Size}
    +   * @private
    +   */
    +  this.size_ = opt_size || null;
    +};
    +
    +
    +/**
    + * Gets the thumbnail URL.
    + * @return {string} The thumbnail's image URL.
    + */
    +goog.ui.media.MediaModel.Thumbnail.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * Sets the thumbnail URL.
    + * @param {string} url The thumbnail's image URL.
    + * @return {!goog.ui.media.MediaModel.Thumbnail} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Thumbnail.prototype.setUrl = function(url) {
    +  this.url_ = url;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the thumbnail size.
    + * @return {goog.math.Size} The size of the thumbnail's image if known.
    + */
    +goog.ui.media.MediaModel.Thumbnail.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * Sets the thumbnail size.
    + * @param {goog.math.Size} size The size of the thumbnail's image.
    + * @return {!goog.ui.media.MediaModel.Thumbnail} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Thumbnail.prototype.setSize = function(size) {
    +  this.size_ = size;
    +  return this;
    +};
    +
    +
    +
    +/**
    + * Constructs a player containing details of the player's URL and
    + * optionally its size.
    + * @param {string|!goog.html.TrustedResourceUrl} url The URL of the player.
    + * @param {Object=} opt_vars Optional map of arguments to the player.
    + * @param {goog.math.Size=} opt_size The size of the player if known.
    + * @constructor
    + * @final
    + */
    +goog.ui.media.MediaModel.Player = function(url, opt_vars, opt_size) {
    +  /**
    +   * The player's URL.
    +   * @type {!goog.html.TrustedResourceUrl}
    +   * @private
    +   */
    +  this.trustedResourceUrl_ = url instanceof goog.html.TrustedResourceUrl ? url :
    +      goog.html.legacyconversions.trustedResourceUrlFromString(url);
    +
    +  /**
    +   * Player arguments, typically flash arguments.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.vars_ = opt_vars || null;
    +
    +  /**
    +   * The size of the player if known.
    +   * @type {goog.math.Size}
    +   * @private
    +   */
    +  this.size_ = opt_size || null;
    +};
    +
    +
    +/**
    + * Gets the player URL.
    + * @return {!goog.html.TrustedResourceUrl} The player's URL.
    + */
    +goog.ui.media.MediaModel.Player.prototype.getTrustedResourceUrl = function() {
    +  return this.trustedResourceUrl_;
    +};
    +
    +
    +/**
    + * Gets the player URL.
    + * @return {string} The player's URL.
    + */
    +goog.ui.media.MediaModel.Player.prototype.getUrl = function() {
    +  return this.trustedResourceUrl_.getTypedStringValue();
    +};
    +
    +
    +/**
    + * Sets the player URL.
    + * @param {string|!goog.html.TrustedResourceUrl} url The player's URL.
    + * @return {!goog.ui.media.MediaModel.Player} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Player.prototype.setUrl = function(url) {
    +  this.trustedResourceUrl_ = url instanceof goog.html.TrustedResourceUrl ? url :
    +      goog.html.legacyconversions.trustedResourceUrlFromString(url);
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the player arguments.
    + * @return {Object} The media player arguments.
    + */
    +goog.ui.media.MediaModel.Player.prototype.getVars = function() {
    +  return this.vars_;
    +};
    +
    +
    +/**
    + * Sets the player arguments.
    + * @param {Object} vars The media player arguments.
    + * @return {!goog.ui.media.MediaModel.Player} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Player.prototype.setVars = function(vars) {
    +  this.vars_ = vars;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the size of the player.
    + * @return {goog.math.Size} The size of the player if known.
    + */
    +goog.ui.media.MediaModel.Player.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * Sets the size of the player.
    + * @param {goog.math.Size} size The size of the player.
    + * @return {!goog.ui.media.MediaModel.Player} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Player.prototype.setSize = function(size) {
    +  this.size_ = size;
    +  return this;
    +};
    +
    +
    +
    +/**
    + * A taxonomy to be set that gives an indication of the type of media content,
    + * and its particular contents.
    + * @param {string} scheme The URI that identifies the categorization scheme.
    + * @param {string} value The value of the category.
    + * @param {string=} opt_label The human readable label that can be displayed in
    + *     end user applications.
    + * @constructor
    + * @final
    + */
    +goog.ui.media.MediaModel.Category = function(scheme, value, opt_label) {
    +  /**
    +   * The URI that identifies the categorization scheme.
    +   * @type {string}
    +   * @private
    +   */
    +  this.scheme_ = scheme;
    +
    +  /**
    +   * The value of the category.
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +
    +  /**
    +   * The human readable label that can be displayed in end user applications.
    +   * @type {string}
    +   * @private
    +   */
    +  this.label_ = opt_label || '';
    +};
    +
    +
    +/**
    + * Gets the category scheme.
    + * @return {string} The category scheme URI.
    + */
    +goog.ui.media.MediaModel.Category.prototype.getScheme = function() {
    +  return this.scheme_;
    +};
    +
    +
    +/**
    + * Sets the category scheme.
    + * @param {string} scheme The category's scheme.
    + * @return {!goog.ui.media.MediaModel.Category} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Category.prototype.setScheme = function(scheme) {
    +  this.scheme_ = scheme;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the categor's value.
    + * @return {string} The category's value.
    + */
    +goog.ui.media.MediaModel.Category.prototype.getValue = function() {
    +  return this.value_;
    +};
    +
    +
    +/**
    + * Sets the category value.
    + * @param {string} value The category value to be set.
    + * @return {!goog.ui.media.MediaModel.Category} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Category.prototype.setValue = function(value) {
    +  this.value_ = value;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the label of the category.
    + * @return {string} The label of the category.
    + */
    +goog.ui.media.MediaModel.Category.prototype.getLabel = function() {
    +  return this.label_;
    +};
    +
    +
    +/**
    + * Sets the label of the category.
    + * @param {string} label The label of the category.
    + * @return {!goog.ui.media.MediaModel.Category} The object itself, used for
    + *     chaining.
    + */
    +goog.ui.media.MediaModel.Category.prototype.setLabel = function(label) {
    +  this.label_ = label;
    +  return this;
    +};
    +
    +
    +
    +/**
    + * Indicates an entity that has contributed to a media object. Based on
    + * 'media.credit' in the rss spec.
    + * @param {string} value The name of the entity being credited.
    + * @param {goog.ui.media.MediaModel.Credit.Role=} opt_role The role the entity
    + *     played.
    + * @param {goog.ui.media.MediaModel.Credit.Scheme=} opt_scheme The URI that
    + *     identifies the role scheme.
    + * @constructor
    + * @final
    + */
    +goog.ui.media.MediaModel.Credit = function(value, opt_role, opt_scheme) {
    +  /**
    +   * The name of entity being credited.
    +   * @type {string}
    +   * @private
    +   */
    +  this.value_ = value;
    +
    +  /**
    +   * The role the entity played.
    +   * @type {goog.ui.media.MediaModel.Credit.Role|undefined}
    +   * @private
    +   */
    +  this.role_ = opt_role;
    +
    +  /**
    +   * The URI that identifies the role scheme
    +   * @type {goog.ui.media.MediaModel.Credit.Scheme|undefined}
    +   * @private
    +   */
    +  this.scheme_ = opt_scheme;
    +};
    +
    +
    +/**
    + * The types of known roles.
    + * @enum {string}
    + */
    +goog.ui.media.MediaModel.Credit.Role = {
    +  UPLOADER: 'uploader',
    +  OWNER: 'owner'
    +};
    +
    +
    +/**
    + * The types of known schemes.
    + * @enum {string}
    + */
    +goog.ui.media.MediaModel.Credit.Scheme = {
    +  EUROPEAN_BROADCASTING: 'urn:ebu',
    +  YAHOO: 'urn:yvs',
    +  YOUTUBE: 'urn:youtube'
    +};
    +
    +
    +/**
    + * Gets the name of the entity being credited.
    + * @return {string} The name of the entity.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.getValue = function() {
    +  return this.value_;
    +};
    +
    +
    +/**
    + * Sets the value of the credit object.
    + * @param {string} value The value.
    + * @return {!goog.ui.media.MediaModel.Credit} The object itself.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.setValue = function(value) {
    +  this.value_ = value;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the role of the entity being credited.
    + * @return {goog.ui.media.MediaModel.Credit.Role|undefined} The role of the
    + *     entity.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.getRole = function() {
    +  return this.role_;
    +};
    +
    +
    +/**
    + * Sets the role of the credit object.
    + * @param {goog.ui.media.MediaModel.Credit.Role} role The role.
    + * @return {!goog.ui.media.MediaModel.Credit} The object itself.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.setRole = function(role) {
    +  this.role_ = role;
    +  return this;
    +};
    +
    +
    +/**
    + * Gets the scheme of the credit object.
    + * @return {goog.ui.media.MediaModel.Credit.Scheme|undefined} The URI that
    + *     identifies the role scheme.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.getScheme = function() {
    +  return this.scheme_;
    +};
    +
    +
    +/**
    + * Sets the scheme of the credit object.
    + * @param {goog.ui.media.MediaModel.Credit.Scheme} scheme The scheme.
    + * @return {!goog.ui.media.MediaModel.Credit} The object itself.
    + */
    +goog.ui.media.MediaModel.Credit.prototype.setScheme = function(scheme) {
    +  this.scheme_ = scheme;
    +  return this;
    +};
    +
    +
    +
    +/**
    + * A reference to the subtitle URI for a media object.
    + * Implements the 'media.subTitle' in the rss spec.
    + *
    + * @param {string} href The subtitle's URI.
    + *     to fetch the subtitle file.
    + * @param {string} lang An RFC 3066 language.
    + * @param {string} type The MIME type of the URI.
    + * @constructor
    + * @final
    + */
    +goog.ui.media.MediaModel.SubTitle = function(href, lang, type) {
    +  /**
    +   * The subtitle href.
    +   * @type {string}
    +   * @private
    +   */
    +  this.href_ = href;
    +
    +  /**
    +   * The RFC 3066 language.
    +   * @type {string}
    +   * @private
    +   */
    +  this.lang_ = lang;
    +
    +  /**
    +   * The MIME type of the resource.
    +   * @type {string}
    +   * @private
    +   */
    +  this.type_ = type;
    +};
    +
    +
    +/**
    + * Sets the href for the subtitle object.
    + * @param {string} href The subtitle's URI.
    + * @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.setHref = function(href) {
    +  this.href_ = href;
    +  return this;
    +};
    +
    +
    +/**
    + * Get the href for the subtitle object.
    + * @return {string} href The subtitle's URI.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.getHref = function() {
    +  return this.href_;
    +};
    +
    +
    +/**
    + * Sets the language for the subtitle object.
    + * @param {string} lang The RFC 3066 language.
    + * @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.setLang = function(lang) {
    +  this.lang_ = lang;
    +  return this;
    +};
    +
    +
    +/**
    + * Get the lang for the subtitle object.
    + * @return {string} lang The RFC 3066 language.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.getLang = function() {
    +  return this.lang_;
    +};
    +
    +
    +/**
    + * Sets the type for the subtitle object.
    + * @param {string} type The MIME type.
    + * @return {!goog.ui.media.MediaModel.SubTitle} The object itself.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.setType = function(type) {
    +  this.type_ = type;
    +  return this;
    +};
    +
    +
    +/**
    + * Get the type for the subtitle object.
    + * @return {string} type The MIME type.
    + */
    +goog.ui.media.MediaModel.SubTitle.prototype.getType = function() {
    +  return this.type_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.html
    new file mode 100644
    index 0000000000..69493e1891
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +          deboer@google.com (James deBoer)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.MediaModel
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.MediaModelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.js
    new file mode 100644
    index 0000000000..8c012a03ff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mediamodel_test.js
    @@ -0,0 +1,95 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.MediaModelTest');
    +goog.setTestOnly('goog.ui.media.MediaModelTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.MediaModel');
    +
    +
    +/**
    + * A simple model used in many tests.
    + */
    +var model;
    +
    +function setUp() {
    +  model = new goog.ui.media.MediaModel(
    +      'http://url.com', 'a caption', 'a description');
    +}
    +
    +function testMediaModel() {
    +  assertEquals('http://url.com', model.getUrl());
    +  assertEquals('a caption', model.getCaption());
    +  assertEquals('a description', model.getDescription());
    +
    +  var incompleteModel = new goog.ui.media.MediaModel(
    +      'http://foo.bar',
    +      undefined,
    +      'This media has no caption but has a description and a URL');
    +  assertEquals('http://foo.bar', incompleteModel.getUrl());
    +  assertUndefined(incompleteModel.getCaption());
    +  assertEquals('This media has no caption but has a description and a URL',
    +      incompleteModel.getDescription());
    +  assertArrayEquals([], incompleteModel.getThumbnails());
    +}
    +
    +function testMediaModelFindCategoryWithScheme() {
    +  assertNull(model.findCategoryWithScheme('no such scheme'));
    +
    +  model.setCategories([
    +    new goog.ui.media.MediaModel.Category('scheme-a', 'value-a'),
    +    new goog.ui.media.MediaModel.Category('scheme-b', 'value-b')
    +  ]);
    +  assertNull(model.findCategoryWithScheme('no such scheme'));
    +  assertEquals('value-a',
    +      model.findCategoryWithScheme('scheme-a').getValue());
    +  assertEquals('value-b',
    +      model.findCategoryWithScheme('scheme-b').getValue());
    +}
    +
    +
    +function testMediaModelFindCreditsWithRole() {
    +  assertEquals(0, model.findCreditsWithRole('no such role').length);
    +
    +  model.setCredits([
    +    new goog.ui.media.MediaModel.Credit('value-a', 'role-a'),
    +    new goog.ui.media.MediaModel.Credit('value-a2', 'role-a'),
    +    new goog.ui.media.MediaModel.Credit('value-b', 'role-b')
    +  ]);
    +
    +  assertEquals(0, model.findCreditsWithRole('no such role').length);
    +  assertEquals(2, model.findCreditsWithRole('role-a').length);
    +  assertEquals('value-a',
    +      model.findCreditsWithRole('role-a')[0].getValue());
    +  assertEquals('value-a2',
    +      model.findCreditsWithRole('role-a')[1].getValue());
    +  assertEquals('value-b',
    +      model.findCreditsWithRole('role-b')[0].getValue());
    +}
    +
    +function testMediaModelSubtitles() {
    +  model.setSubTitles([
    +    new goog.ui.media.MediaModel.SubTitle(
    +        'uri', '*', 'application/tts+xml')
    +  ]);
    +  assertEquals(1, model.getSubTitles().length);
    +  assertEquals('uri', model.getSubTitles()[0].getHref());
    +  assertEquals('*', model.getSubTitles()[0].getLang());
    +  assertEquals('application/tts+xml', model.getSubTitles()[0].getType());
    +}
    +
    +function testMediaModelNoSubtitles() {
    +  assertEquals(0, model.getSubTitles().length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mp3.js b/mapbox-gl/closure-library/closure/goog/ui/media/mp3.js
    new file mode 100644
    index 0000000000..d42450ff74
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mp3.js
    @@ -0,0 +1,226 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview provides a reusable mp3 UI component given a mp3 URL.
    + *
    + * goog.ui.media.Mp3 is actually a {@link goog.ui.ControlRenderer}, a stateless
    + * class - that could/should be used as a Singleton with the static method
    + * {@code goog.ui.media.Mp3.getInstance} -, that knows how to render Mp3s. It is
    + * designed to be used with a {@link goog.ui.Control}, which will actually
    + * control the media renderer and provide the {@link goog.ui.Component} base.
    + * This design guarantees that all different types of medias will behave alike
    + * but will look different.
    + *
    + * goog.ui.media.Mp3 expects mp3 urls on {@code goog.ui.Control.getModel} as
    + * data models, and render a flash object that will play that URL.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   goog.ui.media.Mp3.newControl('http://hostname/file.mp3').render();
    + * </pre>
    + *
    + * Mp3 medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the mp3
    + *   <li> {@link goog.ui.Component.State.SELECTED}: mp3 is playing
    + * </ul>
    + *
    + * Which can be accessed by
    + *
    + * <pre>
    + *   mp3.setEnabled(true);
    + *   mp3.setHighlighted(true);
    + *   mp3.setSelected(true);
    + * </pre>
    + *
    + *
    + * @supported IE6, FF2+, Safari. Requires flash to actually work.
    + *
    + * TODO(user): test on other browsers
    + */
    +
    +goog.provide('goog.ui.media.Mp3');
    +
    +goog.require('goog.string');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a Mp3 specific media
    + * renderer.
    + *
    + * This class knows how to parse mp3 URLs, and render the DOM structure
    + * of mp3 flash players. This class is meant to be used as a singleton static
    + * stateless class, that takes {@code goog.ui.media.Media} instances and renders
    + * it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
    + * previously checked, mp3 URL {@see goog.ui.media.PicasaAlbum.parseUrl},
    + * which is the data model this renderer will use to construct the DOM
    + * structure. {@see goog.ui.media.PicasaAlbum.newControl} for an example of
    + * constructing a control with this renderer.
    + *
    + * This design is patterned after http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.Mp3 = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.Mp3, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.Mp3);
    +
    +
    +/**
    + * Flash player arguments. We expect that {@code flashUrl_} will contain a flash
    + * movie that takes an audioUrl parameter on its URL, containing the URL of the
    + * mp3 to be played.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.Mp3.PLAYER_ARGUMENTS_ = 'audioUrl=%s';
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.Mp3.CSS_CLASS = goog.getCssName('goog-ui-media-mp3');
    +
    +
    +/**
    + * Flash player URL. Uses Google Reader's mp3 flash player by default.
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.media.Mp3.flashUrl_ =
    +    'http://www.google.com/reader/ui/3523697345-audio-player.swf';
    +
    +
    +/**
    + * Regular expression to check if a given URL is a valid mp3 URL.
    + *
    + * Copied from http://go/markdownlite.js.
    +
    + *
    + * NOTE(user): although it would be easier to use goog.string.endsWith('.mp3'),
    + * in the future, we want to provide media inlining, which is basically getting
    + * a text and replacing all mp3 references with an mp3 player, so it makes sense
    + * to share the same regular expression to match everything.
    + *
    + * @type {RegExp}
    + */
    +goog.ui.media.Mp3.MATCHER =
    +    /(https?:\/\/[\w-%&\/.=:#\+~\(\)]+\.(mp3)+(\?[\w-%&\/.=:#\+~\(\)]+)?)/i;
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a mp3 URL. It checks the mp3 URL, sets it as the data model
    + * goog.ui.media.Mp3 renderer uses, sets the states supported by the renderer,
    + * and returns a Control that binds everything together. This is what you
    + * should be using for constructing Mp3 videos, except if you need more fine
    + * control over the configuration.
    + *
    + * @param {goog.ui.media.MediaModel} dataModel A media model that must contain
    + *     an mp3 url on {@code dataModel.getUrl}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A goog.ui.Control subclass with the mp3
    + *     renderer.
    + */
    +goog.ui.media.Mp3.newControl = function(dataModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      dataModel,
    +      goog.ui.media.Mp3.getInstance(),
    +      opt_domHelper);
    +  // mp3 ui doesn't have a non selected view: it shows the mp3 player by
    +  // default.
    +  control.setSelected(true);
    +  return control;
    +};
    +
    +
    +/**
    + * A static method that sets which flash URL this class should use. Use this if
    + * you want to host your own flash mp3 player.
    + *
    + * @param {string} flashUrl The URL of the flash mp3 player.
    + */
    +goog.ui.media.Mp3.setFlashUrl = function(flashUrl) {
    +  goog.ui.media.Mp3.flashUrl_ = flashUrl;
    +};
    +
    +
    +/**
    + * A static method that builds a URL that will contain the flash player that
    + * will play the {@code mp3Url}.
    + *
    + * @param {string} mp3Url The URL of the mp3 music.
    + * @return {string} An URL of a flash player that will know how to play the
    + *     given {@code mp3Url}.
    + */
    +goog.ui.media.Mp3.buildFlashUrl = function(mp3Url) {
    +  var flashUrl = goog.ui.media.Mp3.flashUrl_ + '?' + goog.string.subs(
    +      goog.ui.media.Mp3.PLAYER_ARGUMENTS_,
    +      goog.string.urlEncode(mp3Url));
    +  return flashUrl;
    +};
    +
    +
    +/**
    + * Creates the initial DOM structure of a mp3 video, which is basically a
    + * the flash object pointing to a flash mp3 player.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} A DOM structure that represents the control.
    + * @override
    + */
    +goog.ui.media.Mp3.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.Mp3.superClass_.createDom.call(this, control);
    +
    +  var dataModel =
    +      /** @type {goog.ui.media.MediaModel} */ (control.getDataModel());
    +  var flash = new goog.ui.media.FlashObject(
    +      dataModel.getPlayer().getTrustedResourceUrl(), control.getDomHelper());
    +  flash.setFlashVar('playerMode', 'embedded');
    +  flash.render(div);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.Mp3.prototype.getCssClass = function() {
    +  return goog.ui.media.Mp3.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.html
    new file mode 100644
    index 0000000000..37a61ecdb9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Mp3
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.Mp3Test');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.js
    new file mode 100644
    index 0000000000..5456c04f49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/mp3_test.js
    @@ -0,0 +1,66 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.Mp3Test');
    +goog.setTestOnly('goog.ui.media.Mp3Test');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.Mp3');
    +var mp3;
    +var control;
    +var MP3_URL = 'http://www.shellworld.net/~davidsky/surf-oxy.mp3';
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  mp3 = goog.ui.media.Mp3.getInstance();
    +  var flashUrl = goog.ui.media.Mp3.buildFlashUrl(MP3_URL);
    +  var model = new goog.ui.media.MediaModel(MP3_URL, 'mp3 caption', '');
    +  model.setPlayer(new goog.ui.media.MediaModel.Player(flashUrl));
    +  control = new goog.ui.media.Media(model, mp3);
    +  control.setSelected(true);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.Mp3.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +}
    +
    +function testParsingUrl() {
    +  assertTrue(goog.ui.media.Mp3.MATCHER.test(MP3_URL));
    +  assertFalse(
    +      goog.ui.media.Mp3.MATCHER.test('http://invalidUrl/filename.doc'));
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Mp3.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass(
    +      undefined, goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/photo.js b/mapbox-gl/closure-library/closure/goog/ui/media/photo.js
    new file mode 100644
    index 0000000000..b2a9fa3fdb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/photo.js
    @@ -0,0 +1,143 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview provides a reusable photo UI component that renders photos that
    + * contains metadata (such as captions, description, thumbnail/high resolution
    + * versions, etc).
    + *
    + * goog.ui.media.Photo is actually a {@link goog.ui.ControlRenderer},
    + * a stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.Photo.getInstance} -, that knows how to render
    + * Photos. It is designed to be used with a {@link goog.ui.Control}, which will
    + * actually control the media renderer and provide the {@link goog.ui.Component}
    + * base. This design guarantees that all different types of medias will behave
    + * alike but will look different.
    + *
    + * goog.ui.media.Photo expects {@code goog.ui.media.MediaModel} on
    + * {@code goog.ui.Control.getModel} as data models.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var photo = goog.ui.media.Photo.newControl(
    + *       new goog.ui.media.MediaModel('http://hostname/file.jpg'));
    + *   photo.render(goog.dom.getElement('parent'));
    + * </pre>
    + *
    + * Photo medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the photo.
    + *   <li> {@link goog.ui.Component.State.SELECTED}: photo is being displayed.
    + * </ul>
    + *
    + * Which can be accessed by
    + *
    + * <pre>
    + *   photo.setHighlighted(true);
    + *   photo.setSelected(true);
    + * </pre>
    + *
    + */
    +
    +goog.provide('goog.ui.media.Photo');
    +
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a Photo specific media
    + * renderer. Provides a base class for any other renderer that wants to display
    + * photos.
    + *
    + * This class is meant to be used as a singleton static stateless class, that
    + * takes {@code goog.ui.media.Media} instances and renders it.
    + *
    + * This design is patterned after
    + * http://go/closure_control_subclassing
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.Photo = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.Photo, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.Photo);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.Photo.CSS_CLASS = goog.getCssName('goog-ui-media-photo');
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a photo {@code goog.ui.media.MediaModel}. It sets it as the data model
    + * goog.ui.media.Photo renderer uses, sets the states supported by the renderer,
    + * and returns a Control that binds everything together. This is what you
    + * should be using for constructing Photos, except if you need finer control
    + * over the configuration.
    + *
    + * @param {goog.ui.media.MediaModel} dataModel The photo data model.
    + * @return {!goog.ui.media.Media} A goog.ui.Control subclass with the photo
    + *     renderer.
    + */
    +goog.ui.media.Photo.newControl = function(dataModel) {
    +  var control = new goog.ui.media.Media(
    +      dataModel,
    +      goog.ui.media.Photo.getInstance());
    +  return control;
    +};
    +
    +
    +/**
    + * Creates the initial DOM structure of a photo.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} A DOM structure that represents the control.
    + * @override
    + */
    +goog.ui.media.Photo.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.Photo.superClass_.createDom.call(this, control);
    +
    +  var img = control.getDomHelper().createDom('img', {
    +    src: control.getDataModel().getPlayer().getUrl(),
    +    className: goog.getCssName(this.getCssClass(), 'image')
    +  });
    +
    +  div.appendChild(img);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.Photo.prototype.getCssClass = function() {
    +  return goog.ui.media.Photo.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.html
    new file mode 100644
    index 0000000000..e29c32e943
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Photo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.PhotoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.js
    new file mode 100644
    index 0000000000..580f7e1fb9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/photo_test.js
    @@ -0,0 +1,49 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.PhotoTest');
    +goog.setTestOnly('goog.ui.media.PhotoTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.Photo');
    +var control;
    +var PHOTO_URL = 'http://foo/bar.jpg';
    +
    +function setUp() {
    +  var photo = new goog.ui.media.MediaModel(PHOTO_URL, 'title', 'description');
    +  photo.setPlayer(new goog.ui.media.MediaModel.Player(PHOTO_URL));
    +  control = goog.ui.media.Photo.newControl(photo);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render();
    +  var el = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.Photo.CSS_CLASS);
    +  assertEquals(1, el.length);
    +  var img = goog.dom.getElementsByTagNameAndClass('img',
    +      goog.ui.media.Photo.CSS_CLASS + '-image');
    +  assertEquals(1, img.length);
    +  var caption = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.Photo.CSS_CLASS + '-caption');
    +  assertEquals(1, caption.length);
    +  var content = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.Photo.CSS_CLASS + '-description');
    +  assertEquals(1, content.length);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/picasa.js b/mapbox-gl/closure-library/closure/goog/ui/media/picasa.js
    new file mode 100644
    index 0000000000..b61d17e8f2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/picasa.js
    @@ -0,0 +1,327 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview provides a reusable picasa album UI component given a public
    + * picasa album URL.
    + *
    + * TODO(user): implement the javascript viewer, for users without flash. Get it
    + * from the Gmail Picasa gadget.
    + *
    + * goog.ui.media.PicasaAlbum is actually a {@link goog.ui.ControlRenderer}, a
    + * stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.PicasaAlbum.getInstance} -, that knows how to
    + * render picasa albums. It is designed to be used with a
    + * {@link goog.ui.Control}, which will actually control the media renderer and
    + * provide the {@link goog.ui.Component} base. This design guarantees that all
    + * different types of medias will behave alike but will look different.
    + *
    + * goog.ui.media.PicasaAlbum expects {@code goog.ui.media.PicasaAlbumModel}s on
    + * {@code goog.ui.Control.getModel} as data models, and render a flash object
    + * that will show a slideshow with the contents of that album URL.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var album = goog.ui.media.PicasaAlbumModel.newInstance(
    + *       'http://picasaweb.google.com/username/SanFranciscoCalifornia');
    + *   goog.ui.media.PicasaAlbum.newControl(album).render();
    + * </pre>
    + *
    + * picasa medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the album
    + *   <li> {@link goog.ui.Component.State.SELECTED}: flash album is shown
    + * </ul>
    + *
    + * Which can be accessed by
    + *
    + * <pre>
    + *   picasa.setEnabled(true);
    + *   picasa.setHighlighted(true);
    + *   picasa.setSelected(true);
    + * </pre>
    + *
    + *
    + * @supported IE6, FF2+, Safari. Requires flash to actually work.
    + *
    + * TODO(user): test on other browsers
    + */
    +
    +goog.provide('goog.ui.media.PicasaAlbum');
    +goog.provide('goog.ui.media.PicasaAlbumModel');
    +
    +goog.require('goog.html.TrustedResourceUrl');
    +goog.require('goog.string.Const');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a Picasa specific media
    + * renderer.
    + *
    + * This class knows how to parse picasa URLs, and render the DOM structure
    + * of picasa album players and previews. This class is meant to be used as a
    + * singleton static stateless class, that takes {@code goog.ui.media.Media}
    + * instances and renders it. It expects {@code goog.ui.media.Media.getModel} to
    + * return a well formed, previously constructed, object with a user and album
    + * fields {@see goog.ui.media.PicasaAlbum.parseUrl}, which is the data model
    + * this renderer will use to construct the DOM structure.
    + * {@see goog.ui.media.PicasaAlbum.newControl} for a example of constructing a
    + * control with this renderer.
    + *
    + * goog.ui.media.PicasaAlbum currently displays a picasa-made flash slideshow
    + * with the photos, but could possibly display a handwritten js photo viewer,
    + * in case flash is not available.
    + *
    + * This design is patterned after http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.PicasaAlbum = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.PicasaAlbum, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.PicasaAlbum);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.PicasaAlbum.CSS_CLASS = goog.getCssName('goog-ui-media-picasa');
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a picasa data model. It sets it as the data model goog.ui.media.PicasaAlbum
    + * renderer uses, sets the states supported by the renderer, and returns a
    + * Control that binds everything together. This is what you should be using for
    + * constructing Picasa albums, except if you need finer control over the
    + * configuration.
    + *
    + * @param {goog.ui.media.PicasaAlbumModel} dataModel A picasa album data model.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A Control instance binded to the Picasa
    + *     renderer.
    + */
    +goog.ui.media.PicasaAlbum.newControl = function(dataModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      dataModel,
    +      goog.ui.media.PicasaAlbum.getInstance(),
    +      opt_domHelper);
    +  control.setSelected(true);
    +  return control;
    +};
    +
    +
    +/**
    + * Creates the initial DOM structure of the picasa album, which is basically a
    + * the flash object pointing to a flash picasa album player.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} The DOM structure that represents the control.
    + * @override
    + */
    +goog.ui.media.PicasaAlbum.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.PicasaAlbum.superClass_.createDom.call(this, control);
    +
    +  var picasaAlbum =
    +      /** @type {goog.ui.media.PicasaAlbumModel} */ (control.getDataModel());
    +  var authParam =
    +      picasaAlbum.getAuthKey() ? ('&authkey=' + picasaAlbum.getAuthKey()) : '';
    +  var flash = new goog.ui.media.FlashObject(
    +      picasaAlbum.getPlayer().getTrustedResourceUrl(),
    +      control.getDomHelper());
    +  flash.addFlashVars(picasaAlbum.getPlayer().getVars());
    +  flash.render(div);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.PicasaAlbum.prototype.getCssClass = function() {
    +  return goog.ui.media.PicasaAlbum.CSS_CLASS;
    +};
    +
    +
    +
    +/**
    + * The {@code goog.ui.media.PicasaAlbum} media data model. It stores a required
    + * {@code userId} and {@code albumId} fields, sets the picasa album URL, and
    + * allows a few optional parameters.
    + *
    + * @param {string} userId The picasa userId associated with this album.
    + * @param {string} albumId The picasa albumId associated with this album.
    + * @param {string=} opt_authKey An optional authentication key, used on private
    + *     albums.
    + * @param {string=} opt_caption An optional caption of the picasa album.
    + * @param {string=} opt_description An optional description of the picasa album.
    + * @param {boolean=} opt_autoplay Whether to autoplay the slideshow.
    + * @constructor
    + * @extends {goog.ui.media.MediaModel}
    + * @final
    + */
    +goog.ui.media.PicasaAlbumModel = function(userId,
    +                                          albumId,
    +                                          opt_authKey,
    +                                          opt_caption,
    +                                          opt_description,
    +                                          opt_autoplay) {
    +  goog.ui.media.MediaModel.call(
    +      this,
    +      goog.ui.media.PicasaAlbumModel.buildUrl(userId, albumId),
    +      opt_caption,
    +      opt_description,
    +      goog.ui.media.MediaModel.MimeType.FLASH);
    +
    +  /**
    +   * The Picasa user id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.userId_ = userId;
    +
    +  /**
    +   * The Picasa album id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.albumId_ = albumId;
    +
    +  /**
    +   * The Picasa authentication key, used on private albums.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.authKey_ = opt_authKey || null;
    +
    +  var authParam = opt_authKey ? ('&authkey=' + opt_authKey) : '';
    +
    +  var flashVars = {
    +    'host': 'picasaweb.google.com',
    +    'RGB': '0x000000',
    +    'feed': 'http://picasaweb.google.com/data/feed/api/user/' +
    +        userId + '/album/' + albumId + '?kind=photo&alt=rss' + authParam
    +  };
    +  flashVars[opt_autoplay ? 'autoplay' : 'noautoplay'] = '1';
    +
    +  var flashUrl = goog.html.TrustedResourceUrl.fromConstant(
    +      goog.string.Const.from(
    +          'http://picasaweb.google.com/s/c/bin/slideshow.swf'));
    +  var player = new goog.ui.media.MediaModel.Player(flashUrl, flashVars);
    +
    +  this.setPlayer(player);
    +};
    +goog.inherits(goog.ui.media.PicasaAlbumModel, goog.ui.media.MediaModel);
    +
    +
    +/**
    + * Regular expression used to extract the picasa username and albumid out of
    + * picasa URLs.
    + *
    + * Copied from http://go/markdownlite.js,
    + * and {@link PicasaWebExtractor.xml}.
    + *
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.ui.media.PicasaAlbumModel.MATCHER_ =
    +    /https?:\/\/(?:www\.)?picasaweb\.(?:google\.)?com\/([\d\w\.]+)\/([\d\w_\-\.]+)(?:\?[\w\d\-_=&amp;;\.]*&?authKey=([\w\d\-_=;\.]+))?(?:#([\d]+)?)?/im;
    +
    +
    +/**
    + * Gets a {@code picasaUrl} and extracts the user and album id.
    + *
    + * @param {string} picasaUrl A picasa album URL.
    + * @param {string=} opt_caption An optional caption of the picasa album.
    + * @param {string=} opt_description An optional description of the picasa album.
    + * @param {boolean=} opt_autoplay Whether to autoplay the slideshow.
    + * @return {!goog.ui.media.PicasaAlbumModel} The picasa album data model that
    + *     represents the picasa URL.
    + * @throws exception in case the parsing fails
    + */
    +goog.ui.media.PicasaAlbumModel.newInstance = function(picasaUrl,
    +                                                      opt_caption,
    +                                                      opt_description,
    +                                                      opt_autoplay) {
    +  if (goog.ui.media.PicasaAlbumModel.MATCHER_.test(picasaUrl)) {
    +    var data = goog.ui.media.PicasaAlbumModel.MATCHER_.exec(picasaUrl);
    +    return new goog.ui.media.PicasaAlbumModel(
    +        data[1], data[2], data[3], opt_caption, opt_description, opt_autoplay);
    +  }
    +  throw Error('failed to parse user and album from picasa url: ' + picasaUrl);
    +};
    +
    +
    +/**
    + * The opposite of {@code newInstance}: takes an {@code userId} and an
    + * {@code albumId} and builds a URL.
    + *
    + * @param {string} userId The user that owns the album.
    + * @param {string} albumId The album id.
    + * @return {string} The URL of the album.
    + */
    +goog.ui.media.PicasaAlbumModel.buildUrl = function(userId, albumId) {
    +  return 'http://picasaweb.google.com/' + userId + '/' + albumId;
    +};
    +
    +
    +/**
    + * Gets the Picasa user id.
    + * @return {string} The Picasa user id.
    + */
    +goog.ui.media.PicasaAlbumModel.prototype.getUserId = function() {
    +  return this.userId_;
    +};
    +
    +
    +/**
    + * Gets the Picasa album id.
    + * @return {string} The Picasa album id.
    + */
    +goog.ui.media.PicasaAlbumModel.prototype.getAlbumId = function() {
    +  return this.albumId_;
    +};
    +
    +
    +/**
    + * Gets the Picasa album authentication key.
    + * @return {?string} The Picasa album authentication key.
    + */
    +goog.ui.media.PicasaAlbumModel.prototype.getAuthKey = function() {
    +  return this.authKey_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.html
    new file mode 100644
    index 0000000000..9d60c98980
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Picasa
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.PicasaTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.js
    new file mode 100644
    index 0000000000..4e44d4dd81
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/picasa_test.js
    @@ -0,0 +1,110 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.PicasaTest');
    +goog.setTestOnly('goog.ui.media.PicasaTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.PicasaAlbum');
    +goog.require('goog.ui.media.PicasaAlbumModel');
    +var picasa;
    +var control;
    +var PICASA_USERNAME = 'username';
    +var PICASA_ALBUM = 'albumname';
    +var PICASA_URL = 'http://picasaweb.google.com/' + PICASA_USERNAME + '/' +
    +    PICASA_ALBUM;
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  picasa = new goog.ui.media.PicasaAlbum();
    +  var model = new goog.ui.media.PicasaAlbumModel(PICASA_USERNAME,
    +      PICASA_ALBUM, null, 'album title');
    +  control = new goog.ui.media.Media(model, picasa);
    +  control.setSelected(true);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.PicasaAlbum.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +  assertEquals(PICASA_URL, control.getDataModel().getUrl());
    +}
    +
    +function testParsingUrl() {
    +  assertExtractsCorrectly(PICASA_USERNAME, PICASA_ALBUM, null, PICASA_URL);
    +  assertExtractsCorrectly('foo', 'bar', null,
    +      'https://picasaweb.google.com/foo/bar');
    +  assertExtractsCorrectly('foo', 'bar', null,
    +      'https://www.picasaweb.google.com/foo/bar');
    +  assertExtractsCorrectly('foo', 'bar', null,
    +      'https://www.picasaweb.com/foo/bar');
    +  assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
    +      'https://www.picasaweb.com/foo/bar?authkey=8Hzg1CUUAZM#');
    +  assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
    +      'https://www.picasaweb.com/foo/bar?foo=bar&authkey=8Hzg1CUUAZM#');
    +  assertExtractsCorrectly('foo', 'bar', '8Hzg1CUUAZM',
    +      'https://www.picasaweb.com/foo/bar?foo=bar&authkey=8Hzg1CUUAZM&' +
    +          'hello=world#');
    +
    +  var invalidUrl = 'http://invalidUrl/watch?v=dMH0bHeiRNg';
    +  var e = assertThrows('parser expects a well formed URL', function() {
    +    goog.ui.media.PicasaAlbumModel.newInstance(invalidUrl);
    +  });
    +  assertEquals(
    +      'failed to parse user and album from picasa url: ' + invalidUrl,
    +      e.message);
    +}
    +
    +function testBuildingUrl() {
    +  assertEquals(PICASA_URL,
    +      goog.ui.media.PicasaAlbumModel.buildUrl(PICASA_USERNAME, PICASA_ALBUM));
    +}
    +
    +function testCreatingModel() {
    +  var model = new goog.ui.media.PicasaAlbumModel(
    +      PICASA_USERNAME, PICASA_ALBUM);
    +  assertEquals(PICASA_USERNAME, model.getUserId());
    +  assertEquals(PICASA_ALBUM, model.getAlbumId());
    +  assertEquals(PICASA_URL, model.getUrl());
    +  assertUndefined(model.getCaption());
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.PicasaAlbum.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function assertExtractsCorrectly(
    +    expectedUserId, expectedAlbumId, expectedAuthKey, url) {
    +  var model = goog.ui.media.PicasaAlbumModel.newInstance(url);
    +  assertEquals('User for ' + url, expectedUserId, model.getUserId());
    +  assertEquals('Album for ' + url, expectedAlbumId, model.getAlbumId());
    +  assertEquals('AuthKey for ' + url, expectedAuthKey, model.getAuthKey());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/vimeo.js b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo.js
    new file mode 100644
    index 0000000000..92ef50d3c6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo.js
    @@ -0,0 +1,278 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview provides a reusable Vimeo video UI component given a public
    + * Vimeo video URL.
    + *
    + * goog.ui.media.Vimeo is actually a {@link goog.ui.ControlRenderer}, a
    + * stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.Vimeo.getInstance} -, that knows how to render
    + * video videos. It is designed to be used with a {@link goog.ui.Control},
    + * which will actually control the media renderer and provide the
    + * {@link goog.ui.Component} base. This design guarantees that all different
    + * types of medias will behave alike but will look different.
    + *
    + * goog.ui.media.Vimeo expects vimeo video IDs on
    + * {@code goog.ui.Control.getModel} as data models, and renders a flash object
    + * that will show the contents of that video.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var video = goog.ui.media.VimeoModel.newInstance('http://vimeo.com/30012');
    + *   goog.ui.media.Vimeo.newControl(video).render();
    + * </pre>
    + *
    + * Vimeo medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
    + *   <li> {@link goog.ui.Component.State.SELECTED}: flash video is shown
    + * </ul>
    + *
    + * Which can be accessed by
    + * <pre>
    + *   video.setEnabled(true);
    + *   video.setHighlighted(true);
    + *   video.setSelected(true);
    + * </pre>
    + *
    + *
    + * @supported IE6, FF2+, Safari. Requires flash to actually work.
    + *
    + * TODO(user): test on other browsers
    + */
    +
    +goog.provide('goog.ui.media.Vimeo');
    +goog.provide('goog.ui.media.VimeoModel');
    +
    +goog.require('goog.string');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a Vimeo specific media
    + * renderer.
    + *
    + * This class knows how to parse Vimeo URLs, and render the DOM structure
    + * of vimeo video players. This class is meant to be used as a singleton static
    + * stateless class, that takes {@code goog.ui.media.Media} instances and renders
    + * it. It expects {@code goog.ui.media.Media.getModel} to return a well formed,
    + * previously constructed, vimeoId {@see goog.ui.media.Vimeo.parseUrl}, which is
    + * the data model this renderer will use to construct the DOM structure.
    + * {@see goog.ui.media.Vimeo.newControl} for a example of constructing a control
    + * with this renderer.
    + *
    + * This design is patterned after http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.Vimeo = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.Vimeo, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.Vimeo);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + *
    + * @type {string}
    + */
    +goog.ui.media.Vimeo.CSS_CLASS = goog.getCssName('goog-ui-media-vimeo');
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a Vimeo URL. It extracts the videoId information on the URL, sets it
    + * as the data model goog.ui.media.Vimeo renderer uses, sets the states
    + * supported by the renderer, and returns a Control that binds everything
    + * together. This is what you should be using for constructing Vimeo videos,
    + * except if you need more fine control over the configuration.
    + *
    + * @param {goog.ui.media.VimeoModel} dataModel A vimeo video URL.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A Control binded to the Vimeo renderer.
    + */
    +goog.ui.media.Vimeo.newControl = function(dataModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      dataModel, goog.ui.media.Vimeo.getInstance(), opt_domHelper);
    +  // vimeo videos don't have any thumbnail for now, so we show the
    +  // "selected" version of the UI at the start, which is the
    +  // flash player.
    +  control.setSelected(true);
    +  return control;
    +};
    +
    +
    +/**
    + * Creates the initial DOM structure of the vimeo video, which is basically a
    + * the flash object pointing to a vimeo video player.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @return {!Element} The DOM structure that represents this control.
    + * @override
    + */
    +goog.ui.media.Vimeo.prototype.createDom = function(c) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  var div = goog.ui.media.Vimeo.superClass_.createDom.call(this, control);
    +
    +  var dataModel =
    +      /** @type {goog.ui.media.VimeoModel} */ (control.getDataModel());
    +
    +  var flash = new goog.ui.media.FlashObject(
    +      dataModel.getPlayer().getTrustedResourceUrl(),
    +      control.getDomHelper());
    +  flash.render(div);
    +
    +  return div;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.Vimeo.prototype.getCssClass = function() {
    +  return goog.ui.media.Vimeo.CSS_CLASS;
    +};
    +
    +
    +
    +/**
    + * The {@code goog.ui.media.Vimeo} media data model. It stores a required
    + * {@code videoId} field, sets the vimeo URL, and allows a few optional
    + * parameters.
    + *
    + * @param {string} videoId The vimeo video id.
    + * @param {string=} opt_caption An optional caption of the vimeo video.
    + * @param {string=} opt_description An optional description of the vimeo video.
    + * @param {boolean=} opt_autoplay Whether to autoplay video.
    + * @constructor
    + * @extends {goog.ui.media.MediaModel}
    + * @final
    + */
    +goog.ui.media.VimeoModel = function(videoId, opt_caption, opt_description,
    +                                    opt_autoplay) {
    +  goog.ui.media.MediaModel.call(
    +      this,
    +      goog.ui.media.VimeoModel.buildUrl(videoId),
    +      opt_caption,
    +      opt_description,
    +      goog.ui.media.MediaModel.MimeType.FLASH);
    +
    +  /**
    +   * The Vimeo video id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.videoId_ = videoId;
    +
    +  this.setPlayer(new goog.ui.media.MediaModel.Player(
    +      goog.ui.media.VimeoModel.buildFlashUrl(videoId, opt_autoplay)));
    +};
    +goog.inherits(goog.ui.media.VimeoModel, goog.ui.media.MediaModel);
    +
    +
    +/**
    + * Regular expression used to extract the vimeo video id out of vimeo URLs.
    + *
    + * Copied from http://go/markdownlite.js
    + *
    + * TODO(user): add support to https.
    + *
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +goog.ui.media.VimeoModel.MATCHER_ =
    +    /https?:\/\/(?:www\.)?vimeo\.com\/(?:hd#)?([0-9]+)/i;
    +
    +
    +/**
    + * Takes a {@code vimeoUrl} and extracts the video id.
    + *
    + * @param {string} vimeoUrl A vimeo video URL.
    + * @param {string=} opt_caption An optional caption of the vimeo video.
    + * @param {string=} opt_description An optional description of the vimeo video.
    + * @param {boolean=} opt_autoplay Whether to autoplay video.
    + * @return {!goog.ui.media.VimeoModel} The vimeo data model that represents this
    + *     URL.
    + * @throws exception in case the parsing fails
    + */
    +goog.ui.media.VimeoModel.newInstance = function(vimeoUrl,
    +                                                opt_caption,
    +                                                opt_description,
    +                                                opt_autoplay) {
    +  if (goog.ui.media.VimeoModel.MATCHER_.test(vimeoUrl)) {
    +    var data = goog.ui.media.VimeoModel.MATCHER_.exec(vimeoUrl);
    +    return new goog.ui.media.VimeoModel(
    +        data[1], opt_caption, opt_description, opt_autoplay);
    +  }
    +  throw Error('failed to parse vimeo url: ' + vimeoUrl);
    +};
    +
    +
    +/**
    + * The opposite of {@code goog.ui.media.Vimeo.parseUrl}: it takes a videoId
    + * and returns a vimeo URL.
    + *
    + * @param {string} videoId The vimeo video ID.
    + * @return {string} The vimeo URL.
    + */
    +goog.ui.media.VimeoModel.buildUrl = function(videoId) {
    +  return 'http://vimeo.com/' + goog.string.urlEncode(videoId);
    +};
    +
    +
    +/**
    + * Builds a flash url from the vimeo {@code videoId}.
    + *
    + * @param {string} videoId The vimeo video ID.
    + * @param {boolean=} opt_autoplay Whether the flash movie should start playing
    + *     as soon as it is shown, or if it should show a 'play' button.
    + * @return {string} The vimeo flash URL.
    + */
    +goog.ui.media.VimeoModel.buildFlashUrl = function(videoId, opt_autoplay) {
    +  var autoplay = opt_autoplay ? '&autoplay=1' : '';
    +  return 'http://vimeo.com/moogaloop.swf?clip_id=' +
    +      goog.string.urlEncode(videoId) +
    +      '&server=vimeo.com&show_title=1&show_byline=1&show_portrait=0color=&' +
    +      'fullscreen=1' + autoplay;
    +};
    +
    +
    +/**
    + * Gets the Vimeo video id.
    + * @return {string} The Vimeo video id.
    + */
    +goog.ui.media.VimeoModel.prototype.getVideoId = function() {
    +  return this.videoId_;
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.html
    new file mode 100644
    index 0000000000..66dfa29032
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Vimeo
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.VimeoTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.js
    new file mode 100644
    index 0000000000..db7f6d9f8c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/vimeo_test.js
    @@ -0,0 +1,91 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.VimeoTest');
    +goog.setTestOnly('goog.ui.media.VimeoTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.Vimeo');
    +goog.require('goog.ui.media.VimeoModel');
    +var vimeo;
    +var control;
    +var VIMEO_ID = '3001295';
    +var VIMEO_URL = 'http://vimeo.com/' + VIMEO_ID;
    +var VIMEO_URL_HD = 'http://vimeo.com/hd#' + VIMEO_ID;
    +var VIMEO_URL_SECURE = 'https://vimeo.com/' + VIMEO_ID;
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  vimeo = new goog.ui.media.Vimeo();
    +  var model = new goog.ui.media.VimeoModel(VIMEO_ID, 'vimeo caption');
    +  control = new goog.ui.media.Media(model, vimeo);
    +  control.setSelected(true);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.Vimeo.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +  assertEquals(VIMEO_URL, control.getDataModel().getUrl());
    +}
    +
    +function testParsingUrl() {
    +  assertExtractsCorrectly(VIMEO_ID, VIMEO_URL);
    +  assertExtractsCorrectly(VIMEO_ID, VIMEO_URL_HD);
    +  assertExtractsCorrectly(VIMEO_ID, VIMEO_URL_SECURE);
    +
    +  var invalidUrl = 'http://invalidUrl/filename.doc';
    +  var e = assertThrows('parser expects a well formed URL', function() {
    +    goog.ui.media.VimeoModel.newInstance(invalidUrl);
    +  });
    +  assertEquals('failed to parse vimeo url: ' + invalidUrl, e.message);
    +}
    +
    +function testBuildingUrl() {
    +  assertEquals(
    +      VIMEO_URL, goog.ui.media.VimeoModel.buildUrl(VIMEO_ID));
    +}
    +
    +function testCreatingModel() {
    +  var model = new goog.ui.media.VimeoModel(VIMEO_ID);
    +  assertEquals(VIMEO_ID, model.getVideoId());
    +  assertEquals(VIMEO_URL, model.getUrl());
    +  assertUndefined(model.getCaption());
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Vimeo.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function assertExtractsCorrectly(expectedVideoId, url) {
    +  var model = goog.ui.media.VimeoModel.newInstance(url);
    +  assertEquals('Video id for ' + url, expectedVideoId, model.getVideoId());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/youtube.js b/mapbox-gl/closure-library/closure/goog/ui/media/youtube.js
    new file mode 100644
    index 0000000000..4c1d1ee507
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/youtube.js
    @@ -0,0 +1,358 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview provides a reusable youtube UI component given a youtube data
    + * model.
    + *
    + * goog.ui.media.Youtube is actually a {@link goog.ui.ControlRenderer}, a
    + * stateless class - that could/should be used as a Singleton with the static
    + * method {@code goog.ui.media.Youtube.getInstance} -, that knows how to render
    + * youtube videos. It is designed to be used with a {@link goog.ui.Control},
    + * which will actually control the media renderer and provide the
    + * {@link goog.ui.Component} base. This design guarantees that all different
    + * types of medias will behave alike but will look different.
    + *
    + * goog.ui.media.Youtube expects {@code goog.ui.media.YoutubeModel} on
    + * {@code goog.ui.Control.getModel} as data models, and render a flash object
    + * that will play that URL.
    + *
    + * Example of usage:
    + *
    + * <pre>
    + *   var video = goog.ui.media.YoutubeModel.newInstance(
    + *       'http://www.youtube.com/watch?v=ddl5f44spwQ');
    + *   goog.ui.media.Youtube.newControl(video).render();
    + * </pre>
    + *
    + * youtube medias currently support the following states:
    + *
    + * <ul>
    + *   <li> {@link goog.ui.Component.State.DISABLED}: shows 'flash not available'
    + *   <li> {@link goog.ui.Component.State.HOVER}: mouse cursor is over the video
    + *   <li> {@link !goog.ui.Component.State.SELECTED}: a static thumbnail is shown
    + *   <li> {@link goog.ui.Component.State.SELECTED}: video is playing
    + * </ul>
    + *
    + * Which can be accessed by
    + * <pre>
    + *   youtube.setEnabled(true);
    + *   youtube.setHighlighted(true);
    + *   youtube.setSelected(true);
    + * </pre>
    + *
    + * This package also provides a few static auxiliary methods, such as:
    + *
    + * <pre>
    + * var videoId = goog.ui.media.Youtube.parseUrl(
    + *     'http://www.youtube.com/watch?v=ddl5f44spwQ');
    + * </pre>
    + *
    + *
    + * @supported IE6, FF2+, Safari. Requires flash to actually work.
    + *
    + * TODO(user): test on other browsers
    + */
    +
    +
    +goog.provide('goog.ui.media.Youtube');
    +goog.provide('goog.ui.media.YoutubeModel');
    +
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Media');
    +goog.require('goog.ui.media.MediaModel');
    +goog.require('goog.ui.media.MediaRenderer');
    +
    +
    +
    +/**
    + * Subclasses a goog.ui.media.MediaRenderer to provide a Youtube specific media
    + * renderer.
    + *
    + * This class knows how to parse youtube urls, and render the DOM structure
    + * of youtube video players and previews. This class is meant to be used as a
    + * singleton static stateless class, that takes {@code goog.ui.media.Media}
    + * instances and renders it. It expects {@code goog.ui.media.Media.getModel} to
    + * return a well formed, previously constructed, youtube video id, which is the
    + * data model this renderer will use to construct the DOM structure.
    + * {@see goog.ui.media.Youtube.newControl} for a example of constructing a
    + * control with this renderer.
    + *
    + * goog.ui.media.Youtube currently supports all {@link goog.ui.Component.State}.
    + * It will change its DOM structure between SELECTED and !SELECTED, and rely on
    + * CSS definitions on the others. On !SELECTED, the renderer will render a
    + * youtube static <img>, with a thumbnail of the video. On SELECTED, the
    + * renderer will append to the DOM a flash object, that contains the youtube
    + * video.
    + *
    + * This design is patterned after http://go/closure_control_subclassing
    + *
    + * It uses {@link goog.ui.media.FlashObject} to embed the flash object.
    + *
    + * @constructor
    + * @extends {goog.ui.media.MediaRenderer}
    + * @final
    + */
    +goog.ui.media.Youtube = function() {
    +  goog.ui.media.MediaRenderer.call(this);
    +};
    +goog.inherits(goog.ui.media.Youtube, goog.ui.media.MediaRenderer);
    +goog.addSingletonGetter(goog.ui.media.Youtube);
    +
    +
    +/**
    + * A static convenient method to construct a goog.ui.media.Media control out of
    + * a youtube model. It sets it as the data model goog.ui.media.Youtube renderer
    + * uses, sets the states supported by the renderer, and returns a Control that
    + * binds everything together. This is what you should be using for constructing
    + * Youtube videos, except if you need finer control over the configuration.
    + *
    + * @param {goog.ui.media.YoutubeModel} youtubeModel The youtube data model.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @return {!goog.ui.media.Media} A Control binded to the youtube renderer.
    + */
    +goog.ui.media.Youtube.newControl = function(youtubeModel, opt_domHelper) {
    +  var control = new goog.ui.media.Media(
    +      youtubeModel,
    +      goog.ui.media.Youtube.getInstance(),
    +      opt_domHelper);
    +  control.setStateInternal(goog.ui.Component.State.ACTIVE);
    +  return control;
    +};
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.media.Youtube.CSS_CLASS = goog.getCssName('goog-ui-media-youtube');
    +
    +
    +/**
    + * Changes the state of a {@code control}. Currently only changes the DOM
    + * structure when the youtube movie is SELECTED (by default fired by a MOUSEUP
    + * on the thumbnail), which means we have to embed the youtube flash video and
    + * play it.
    + *
    + * @param {goog.ui.Control} c The media control.
    + * @param {goog.ui.Component.State} state The state to be set or cleared.
    + * @param {boolean} enable Whether the state is enabled or disabled.
    + * @override
    + */
    +goog.ui.media.Youtube.prototype.setState = function(c, state, enable) {
    +  var control = /** @type {goog.ui.media.Media} */ (c);
    +  goog.ui.media.Youtube.superClass_.setState.call(this, control, state, enable);
    +
    +  // control.createDom has to be called before any state is set.
    +  // Use control.setStateInternal if you need to set states
    +  if (!control.getElement()) {
    +    throw Error(goog.ui.Component.Error.STATE_INVALID);
    +  }
    +
    +  var domHelper = control.getDomHelper();
    +  var dataModel =
    +      /** @type {goog.ui.media.YoutubeModel} */ (control.getDataModel());
    +
    +  if (!!(state & goog.ui.Component.State.SELECTED) && enable) {
    +    var flashEls = domHelper.getElementsByTagNameAndClass(
    +        'div',
    +        goog.ui.media.FlashObject.CSS_CLASS,
    +        control.getElement());
    +    if (flashEls.length > 0) {
    +      return;
    +    }
    +    var youtubeFlash = new goog.ui.media.FlashObject(
    +        dataModel.getPlayer().getTrustedResourceUrl(),
    +        domHelper);
    +    control.addChild(youtubeFlash, true);
    +  }
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + *
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.media.Youtube.prototype.getCssClass = function() {
    +  return goog.ui.media.Youtube.CSS_CLASS;
    +};
    +
    +
    +
    +/**
    + * The {@code goog.ui.media.Youtube} media data model. It stores a required
    + * {@code videoId} field, sets the youtube URL, and allows a few optional
    + * parameters.
    + *
    + * @param {string} videoId The youtube video id.
    + * @param {string=} opt_caption An optional caption of the youtube video.
    + * @param {string=} opt_description An optional description of the youtube
    + *     video.
    + * @constructor
    + * @extends {goog.ui.media.MediaModel}
    + * @final
    + */
    +goog.ui.media.YoutubeModel = function(videoId, opt_caption, opt_description) {
    +  goog.ui.media.MediaModel.call(
    +      this,
    +      goog.ui.media.YoutubeModel.buildUrl(videoId),
    +      opt_caption,
    +      opt_description,
    +      goog.ui.media.MediaModel.MimeType.FLASH);
    +
    +  /**
    +   * The Youtube video id.
    +   * @type {string}
    +   * @private
    +   */
    +  this.videoId_ = videoId;
    +
    +  this.setThumbnails([new goog.ui.media.MediaModel.Thumbnail(
    +      goog.ui.media.YoutubeModel.getThumbnailUrl(videoId))]);
    +
    +  this.setPlayer(new goog.ui.media.MediaModel.Player(
    +      goog.ui.media.YoutubeModel.getFlashUrl(videoId, true)));
    +};
    +goog.inherits(goog.ui.media.YoutubeModel, goog.ui.media.MediaModel);
    +
    +
    +/**
    + * A youtube regular expression matcher. It matches the VIDEOID of URLs like
    + * http://www.youtube.com/watch?v=VIDEOID. Based on:
    + * googledata/contentonebox/opencob/specs/common/YTPublicExtractorCard.xml
    + * @type {RegExp}
    + * @private
    + * @const
    + */
    +// Be careful about the placement of the dashes in the character classes. Eg,
    +// use "[\\w=-]" instead of "[\\w-=]" if you mean to include the dash as a
    +// character and not create a character range like "[a-f]".
    +goog.ui.media.YoutubeModel.MATCHER_ = new RegExp(
    +    // Lead in.
    +    'https?://(?:[a-zA-Z]{1,3}\\.)?' +
    +    // Watch URL prefix.  This should handle new URLs of the form:
    +    // http://www.youtube.com/watch#!v=jqxENMKaeCU&feature=related
    +    // where the parameters appear after "#!" instead of "?".
    +    '(?:youtube\\.com/watch|youtu\\.be/watch)' +
    +    // Get the video id:
    +    // The video ID is a parameter v=[videoid] either right after the "?"
    +    // or after some other parameters.
    +    '(?:\\?(?:[\\w=-]+&(?:amp;)?)*v=([\\w-]+)' +
    +    '(?:&(?:amp;)?[\\w=-]+)*)?' +
    +    // Get any extra arguments in the URL's hash part.
    +    '(?:#[!]?(?:' +
    +    // Video ID from the v=[videoid] parameter, optionally surrounded by other
    +    // & separated parameters.
    +    '(?:(?:[\\w=-]+&(?:amp;)?)*(?:v=([\\w-]+))' +
    +    '(?:&(?:amp;)?[\\w=-]+)*)' +
    +    '|' +
    +    // Continue supporting "?" for the video ID
    +    // and "#" for other hash parameters.
    +    '(?:[\\w=&-]+)' +
    +    '))?' +
    +    // Should terminate with a non-word, non-dash (-) character.
    +    '[^\\w-]?', 'i');
    +
    +
    +/**
    + * A auxiliary static method that parses a youtube URL, extracting the ID of the
    + * video, and builds a YoutubeModel.
    + *
    + * @param {string} youtubeUrl A youtube URL.
    + * @param {string=} opt_caption An optional caption of the youtube video.
    + * @param {string=} opt_description An optional description of the youtube
    + *     video.
    + * @return {!goog.ui.media.YoutubeModel} The data model that represents the
    + *     youtube URL.
    + * @see goog.ui.media.YoutubeModel.getVideoId()
    + * @throws Error in case the parsing fails.
    + */
    +goog.ui.media.YoutubeModel.newInstance = function(youtubeUrl,
    +                                                  opt_caption,
    +                                                  opt_description) {
    +  var extract = goog.ui.media.YoutubeModel.MATCHER_.exec(youtubeUrl);
    +  if (extract) {
    +    var videoId = extract[1] || extract[2];
    +    return new goog.ui.media.YoutubeModel(
    +        videoId, opt_caption, opt_description);
    +  }
    +
    +  throw Error('failed to parse video id from youtube url: ' + youtubeUrl);
    +};
    +
    +
    +/**
    + * The opposite of {@code goog.ui.media.Youtube.newInstance}: it takes a videoId
    + * and returns a youtube URL.
    + *
    + * @param {string} videoId The youtube video ID.
    + * @return {string} The youtube URL.
    + */
    +goog.ui.media.YoutubeModel.buildUrl = function(videoId) {
    +  return 'http://www.youtube.com/watch?v=' + goog.string.urlEncode(videoId);
    +};
    +
    +
    +/**
    + * A static auxiliary method that builds a static image URL with a preview of
    + * the youtube video.
    + *
    + * NOTE(user): patterned after Gmail's gadgets/youtube,
    + *
    + * TODO(user): how do I specify the width/height of the resulting image on the
    + * url ? is there an official API for http://ytimg.com ?
    + *
    + * @param {string} youtubeId The youtube video ID.
    + * @return {string} An URL that contains an image with a preview of the youtube
    + *     movie.
    + */
    +goog.ui.media.YoutubeModel.getThumbnailUrl = function(youtubeId) {
    +  return 'http://i.ytimg.com/vi/' + youtubeId + '/default.jpg';
    +};
    +
    +
    +/**
    + * A static auxiliary method that builds URL of the flash movie to be embedded,
    + * out of the youtube video id.
    + *
    + * @param {string} videoId The youtube video ID.
    + * @param {boolean=} opt_autoplay Whether the flash movie should start playing
    + *     as soon as it is shown, or if it should show a 'play' button.
    + * @return {string} The flash URL to be embedded on the page.
    + */
    +goog.ui.media.YoutubeModel.getFlashUrl = function(videoId, opt_autoplay) {
    +  var autoplay = opt_autoplay ? '&autoplay=1' : '';
    +  // YouTube video ids are extracted from youtube URLs, which are user
    +  // generated input. the video id is later used to embed a flash object,
    +  // which is generated through HTML construction. We goog.string.urlEncode
    +  // the video id to make sure the URL is safe to be embedded.
    +  return 'http://www.youtube.com/v/' + goog.string.urlEncode(videoId) +
    +      '&hl=en&fs=1' + autoplay;
    +};
    +
    +
    +/**
    + * Gets the Youtube video id.
    + * @return {string} The Youtube video id.
    + */
    +goog.ui.media.YoutubeModel.prototype.getVideoId = function() {
    +  return this.videoId_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.html b/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.html
    new file mode 100644
    index 0000000000..76b9847698
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.media.Youtube
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.media.YoutubeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.js b/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.js
    new file mode 100644
    index 0000000000..234cbddfa4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/media/youtube_test.js
    @@ -0,0 +1,259 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.media.YoutubeTest');
    +goog.setTestOnly('goog.ui.media.YoutubeTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.media.FlashObject');
    +goog.require('goog.ui.media.Youtube');
    +goog.require('goog.ui.media.YoutubeModel');
    +var youtube;
    +var control;
    +var YOUTUBE_VIDEO_ID = 'dMH0bHeiRNg';
    +var YOUTUBE_URL = 'http://www.youtube.com/watch?v=' + YOUTUBE_VIDEO_ID;
    +var parent = goog.dom.createElement('div');
    +
    +function setUp() {
    +  var model = new goog.ui.media.YoutubeModel(
    +      YOUTUBE_VIDEO_ID, 'evolution of dance');
    +  control = goog.ui.media.Youtube.newControl(model);
    +}
    +
    +function tearDown() {
    +  control.dispose();
    +}
    +
    +function testBasicRendering() {
    +  control.render(parent);
    +  var el = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.Youtube.CSS_CLASS, parent);
    +  assertEquals(1, el.length);
    +  assertEquals(YOUTUBE_URL, control.getDataModel().getUrl());
    +}
    +
    +function testParsingUrl() {
    +  // a simple link
    +  assertExtractsCorrectly(
    +      'uddeBVmKTqE',
    +      'http://www.youtube.com/watch?v=uddeBVmKTqE');
    +  // a simple mobile link
    +  assertExtractsCorrectly(
    +      'uddeBVmKTqE',
    +      'http://m.youtube.com/watch?v=uddeBVmKTqE');
    +  // a secure mobile link
    +  assertExtractsCorrectly(
    +      'uddeBVmKTqE',
    +      'https://m.youtube.com/watch?v=uddeBVmKTqE');
    +  // a simple short link
    +  assertExtractsCorrectly(
    +      'uddeBVmKTqE',
    +      'http://youtu.be/watch?v=uddeBVmKTqE');
    +  // a secure short link
    +  assertExtractsCorrectly(
    +      'uddeBVmKTqE',
    +      'https://youtu.be/watch?v=uddeBVmKTqE');
    +  // a channel link
    +  assertExtractsCorrectly(
    +      '4Pb9e1uu3EQ',
    +      'http://www.youtube.com/watch?v=4Pb9e1uu3EQ&feature=channel');
    +  // a UK link
    +  assertExtractsCorrectly(
    +      'xqWXO87TlH4',
    +      'http://uk.youtube.com/watch?gl=GB&hl=en-GB&v=xqWXO87TlH4');
    +  // an India link
    +  assertExtractsCorrectly(
    +      '10FKWOn4qGA',
    +      'http://www.youtube.com/watch?gl=IN&hl=en-GB&v=10FKWOn4qGA');
    +  // an ad
    +  assertExtractsCorrectly(
    +      'wk1_kDJhyBk',
    +      'http://www.youtube.com/watch?v=wk1_kDJhyBk&feature=yva-video-display');
    +  // a related video
    +  assertExtractsCorrectly(
    +      '7qL2PuLF0SI',
    +      'http://www.youtube.com/watch?v=7qL2PuLF0SI&feature=related');
    +  // with a timestamp
    +  assertExtractsCorrectly(
    +      'siJZXtsdfsf',
    +      'http://www.youtube.com/watch?v=siJZXtsdfsf#t=2m59s');
    +  // with a timestamp and multiple hash params
    +  assertExtractsCorrectly(
    +      'siJZXtabdef',
    +      'http://www.youtube.com/watch?v=siJZXtabdef#t=1m59s&videos=foo');
    +  // with a timestamp, multiple regular and hash params
    +  assertExtractsCorrectly(
    +      'siJZXtabxyz',
    +      'http://www.youtube.com/watch?foo=bar&v=siJZXtabxyz&x=y#t=1m30s' +
    +          '&videos=bar');
    +  // only hash params
    +  assertExtractsCorrectly(
    +      'MWBpQoPwT3U',
    +      'http://www.youtube.com/watch#!playnext=1&playnext_from=TL' +
    +          '&videos=RX1XPmgerGo&v=MWBpQoPwT3U');
    +  // only hash params
    +  assertExtractsCorrectly(
    +      'MWBpQoPwT3V',
    +      'http://www.youtube.com/watch#!playnext=1&playnext_from=TL' +
    +          '&videos=RX1XPmgerGp&v=MWBpQoPwT3V&foo=bar');
    +  assertExtractsCorrectly(
    +      'jqxENMKaeCU',
    +      'http://www.youtube.com/watch#!v=jqxENMKaeCU&feature=related');
    +  // Lots of query params, some of them w/ numbers, one of them before the
    +  // video ID
    +  assertExtractsCorrectly(
    +      'qbce2yN81mE',
    +      'http://www.youtube.com/watch?usg=AFQjCNFf90T3fekgdVBmPp-Wgya5_CTSaw' +
    +          '&v=qbce2yN81mE&source=video&vgc=rss');
    +  assertExtractsCorrectly(
    +      'Lc-8onVA5Jk',
    +      'http://www.youtube.com/watch?v=Lc-8onVA5Jk&feature=dir');
    +  // Last character in the video ID is '-' (a non-word but valid character)
    +  // and the video ID is the last query parameter
    +  assertExtractsCorrectly(
    +      'Lc-8onV5Jk-',
    +      'http://www.youtube.com/watch?v=Lc-8onV5Jk-');
    +
    +  var invalidUrls = [
    +    'http://invalidUrl/watch?v=dMH0bHeiRNg',
    +    'http://www$youtube.com/watch?v=dMH0bHeiRNg',
    +    'http://www.youtube$com/watch?v=dMH0bHeiRNg',
    +    'http://w_w.youtube.com/watch?v=dMH0bHeiRNg'
    +  ];
    +  for (var i = 0, j = invalidUrls.length; i < j; ++i) {
    +    var e = assertThrows('parser expects a well formed URL', function() {
    +      goog.ui.media.YoutubeModel.newInstance(invalidUrls[i]);
    +    });
    +    assertEquals(
    +        'failed to parse video id from youtube url: ' + invalidUrls[i],
    +        e.message);
    +  }
    +}
    +
    +function testBuildingUrl() {
    +  assertEquals(
    +      YOUTUBE_URL, goog.ui.media.YoutubeModel.buildUrl(YOUTUBE_VIDEO_ID));
    +}
    +
    +function testCreatingModel() {
    +  var model = new goog.ui.media.YoutubeModel(YOUTUBE_VIDEO_ID);
    +  assertEquals(YOUTUBE_VIDEO_ID, model.getVideoId());
    +  assertEquals(YOUTUBE_URL, model.getUrl());
    +  assertUndefined(model.getCaption());
    +}
    +
    +function testCreatingDomOnInitialState() {
    +  control.render(parent);
    +  var preview = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.media.Youtube.CSS_CLASS + '-thumbnail0',
    +      parent);
    +  assertEquals(1, preview.length);
    +
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Youtube.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.FlashObject.CSS_CLASS);
    +  assertEquals(0, flash.length);
    +}
    +
    +function testCreatingDomOnSelectedState() {
    +  control.render(parent);
    +  control.setSelected(true);
    +  var preview = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.media.Youtube.CSS_CLASS + '-preview',
    +      parent);
    +  assertEquals(0, preview.length);
    +
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Youtube.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function testSettingSelectedStateAfterRender() {
    +  control.render(parent);
    +  control.setSelected(true);
    +
    +  var preview = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.media.Youtube.CSS_CLASS + '-preview',
    +      parent);
    +  assertEquals(0, preview.length);
    +
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Youtube.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +
    +  control.setSelected(false);
    +
    +  var preview = goog.dom.getElementsByTagNameAndClass(
    +      'img',
    +      goog.ui.media.Youtube.CSS_CLASS + '-thumbnail0',
    +      parent);
    +  assertEquals(1, preview.length);
    +
    +  var caption = goog.dom.getElementsByTagNameAndClass(
    +      'div',
    +      goog.ui.media.Youtube.CSS_CLASS + '-caption',
    +      parent);
    +  assertEquals(1, caption.length);
    +
    +  // setting select as false doesn't actually remove the flash movie from
    +  // the DOM tree, which means that setting selected to true won't actually
    +  // restart the movie. TODO(user): fix this.
    +  var flash = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +
    +  control.setSelected(true);
    +
    +  var flash = goog.dom.getElementsByTagNameAndClass('div',
    +      goog.ui.media.FlashObject.CSS_CLASS, parent);
    +  assertEquals(1, flash.length);
    +}
    +
    +function testUrlMatcher() {
    +  var matcher = goog.ui.media.YoutubeModel.MATCHER_;
    +  assertTrue(matcher.test('http://www.youtube.com/watch?v=55D-ybnYQSs'));
    +  assertTrue(matcher.test('https://youtube.com/watch?v=55D-ybnYQSs'));
    +  assertTrue(
    +      matcher.test('https://youtube.com/watch?blarg=blop&v=55D-ybnYQSs'));
    +  assertTrue(matcher.test('http://www.youtube.com/watch?v=55D-ybnYQSs#wee'));
    +
    +  assertFalse(matcher.test('http://www.cnn.com/watch?v=55D-ybnYQSs#wee'));
    +  assertFalse(matcher.test('ftp://www.youtube.com/watch?v=55D-ybnYQSs#wee'));
    +}
    +
    +function assertExtractsCorrectly(expectedVideoId, url) {
    +  var youtube = goog.ui.media.YoutubeModel.newInstance(url);
    +  assertEquals('videoid for ' + url, expectedVideoId, youtube.getVideoId());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menu.js b/mapbox-gl/closure-library/closure/goog/ui/menu.js
    new file mode 100644
    index 0000000000..0575786b1e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menu.js
    @@ -0,0 +1,477 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A base menu class that supports key and mouse events. The menu
    + * can be bound to an existing HTML structure or can generate its own DOM.
    + *
    + * To decorate, the menu should be bound to an element containing children
    + * with the classname 'goog-menuitem'.  HRs will be classed as separators.
    + *
    + * Decorate Example:
    + * <div id="menu" class="goog-menu" tabIndex="0">
    + *   <div class="goog-menuitem">Google</div>
    + *   <div class="goog-menuitem">Yahoo</div>
    + *   <div class="goog-menuitem">MSN</div>
    + *   <hr>
    + *   <div class="goog-menuitem">New...</div>
    + * </div>
    + * <script>
    + *
    + * var menu = new goog.ui.Menu();
    + * menu.decorate(goog.dom.getElement('menu'));
    + *
    + * TESTED=FireFox 2.0, IE6, Opera 9, Chrome.
    + * TODO(user): Key handling is flaky in Opera and Chrome
    + * TODO(user): Rename all references of "item" to child since menu is
    + * essentially very generic and could, in theory, host a date or color picker.
    + *
    + * @see ../demos/menu.html
    + * @see ../demos/menus.html
    + */
    +
    +goog.provide('goog.ui.Menu');
    +goog.provide('goog.ui.Menu.EventType');
    +
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component.EventType');
    +goog.require('goog.ui.Component.State');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Container.Orientation');
    +goog.require('goog.ui.MenuHeader');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuRenderer');
    +goog.require('goog.ui.MenuSeparator');
    +
    +// The dependencies MenuHeader, MenuItem, and MenuSeparator are implicit.
    +// There are no references in the code, but we need to load these
    +// classes before goog.ui.Menu.
    +
    +
    +
    +// TODO(robbyw): Reverse constructor argument order for consistency.
    +/**
    + * A basic menu class.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.MenuRenderer=} opt_renderer Renderer used to render or
    + *     decorate the container; defaults to {@link goog.ui.MenuRenderer}.
    + * @constructor
    + * @extends {goog.ui.Container}
    + */
    +goog.ui.Menu = function(opt_domHelper, opt_renderer) {
    +  goog.ui.Container.call(this, goog.ui.Container.Orientation.VERTICAL,
    +      opt_renderer || goog.ui.MenuRenderer.getInstance(), opt_domHelper);
    +
    +  // Unlike Containers, Menus aren't keyboard-accessible by default.  This line
    +  // preserves backwards compatibility with code that depends on menus not
    +  // receiving focus - e.g. {@code goog.ui.MenuButton}.
    +  this.setFocusable(false);
    +};
    +goog.inherits(goog.ui.Menu, goog.ui.Container);
    +goog.tagUnsealableClass(goog.ui.Menu);
    +
    +
    +// TODO(robbyw): Remove this and all references to it.
    +// Please ensure that BEFORE_SHOW behavior is not disrupted as a result.
    +/**
    + * Event types dispatched by the menu.
    + * @enum {string}
    + * @deprecated Use goog.ui.Component.EventType.
    + */
    +goog.ui.Menu.EventType = {
    +  /** Dispatched before the menu becomes visible */
    +  BEFORE_SHOW: goog.ui.Component.EventType.BEFORE_SHOW,
    +
    +  /** Dispatched when the menu is shown */
    +  SHOW: goog.ui.Component.EventType.SHOW,
    +
    +  /** Dispatched before the menu becomes hidden */
    +  BEFORE_HIDE: goog.ui.Component.EventType.HIDE,
    +
    +  /** Dispatched when the menu is hidden */
    +  HIDE: goog.ui.Component.EventType.HIDE
    +};
    +
    +
    +// TODO(robbyw): Remove this and all references to it.
    +/**
    + * CSS class for menus.
    + * @type {string}
    + * @deprecated Use goog.ui.MenuRenderer.CSS_CLASS.
    + */
    +goog.ui.Menu.CSS_CLASS = goog.ui.MenuRenderer.CSS_CLASS;
    +
    +
    +/**
    + * Coordinates of the mousedown event that caused this menu to be made visible.
    + * Used to prevent the consequent mouseup event due to a simple click from
    + * activating a menu item immediately. Considered protected; should only be used
    + * within this package or by subclasses.
    + * @type {goog.math.Coordinate|undefined}
    + */
    +goog.ui.Menu.prototype.openingCoords;
    +
    +
    +/**
    + * Whether the menu can move the focus to its key event target when it is
    + * shown.  Default = true
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Menu.prototype.allowAutoFocus_ = true;
    +
    +
    +/**
    + * Whether the menu should use windows syle behavior and allow disabled menu
    + * items to be highlighted (though not selectable).  Defaults to false
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Menu.prototype.allowHighlightDisabled_ = false;
    +
    +
    +/**
    + * Returns the CSS class applied to menu elements, also used as the prefix for
    + * derived styles, if any.  Subclasses should override this method as needed.
    + * Considered protected.
    + * @return {string} The CSS class applied to menu elements.
    + * @protected
    + * @deprecated Use getRenderer().getCssClass().
    + */
    +goog.ui.Menu.prototype.getCssClass = function() {
    +  return this.getRenderer().getCssClass();
    +};
    +
    +
    +/**
    + * Returns whether the provided element is to be considered inside the menu for
    + * purposes such as dismissing the menu on an event.  This is so submenus can
    + * make use of elements outside their own DOM.
    + * @param {Element} element The element to test for.
    + * @return {boolean} Whether the provided element is to be considered inside
    + *     the menu.
    + */
    +goog.ui.Menu.prototype.containsElement = function(element) {
    +  if (this.getRenderer().containsElement(this, element)) {
    +    return true;
    +  }
    +
    +  for (var i = 0, count = this.getChildCount(); i < count; i++) {
    +    var child = this.getChildAt(i);
    +    if (typeof child.containsElement == 'function' &&
    +        child.containsElement(element)) {
    +      return true;
    +    }
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Adds a new menu item at the end of the menu.
    + * @param {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu
    + *     item to add to the menu.
    + * @deprecated Use {@link #addChild} instead, with true for the second argument.
    + */
    +goog.ui.Menu.prototype.addItem = function(item) {
    +  this.addChild(item, true);
    +};
    +
    +
    +/**
    + * Adds a new menu item at a specific index in the menu.
    + * @param {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu
    + *     item to add to the menu.
    + * @param {number} n Index at which to insert the menu item.
    + * @deprecated Use {@link #addChildAt} instead, with true for the third
    + *     argument.
    + */
    +goog.ui.Menu.prototype.addItemAt = function(item, n) {
    +  this.addChildAt(item, n, true);
    +};
    +
    +
    +/**
    + * Removes an item from the menu and disposes of it.
    + * @param {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator} item The
    + *     menu item to remove.
    + * @deprecated Use {@link #removeChild} instead.
    + */
    +goog.ui.Menu.prototype.removeItem = function(item) {
    +  var removedChild = this.removeChild(item, true);
    +  if (removedChild) {
    +    removedChild.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Removes a menu item at a given index in the menu and disposes of it.
    + * @param {number} n Index of item.
    + * @deprecated Use {@link #removeChildAt} instead.
    + */
    +goog.ui.Menu.prototype.removeItemAt = function(n) {
    +  var removedChild = this.removeChildAt(n, true);
    +  if (removedChild) {
    +    removedChild.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Returns a reference to the menu item at a given index.
    + * @param {number} n Index of menu item.
    + * @return {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator|null}
    + *     Reference to the menu item.
    + * @deprecated Use {@link #getChildAt} instead.
    + */
    +goog.ui.Menu.prototype.getItemAt = function(n) {
    +  return /** @type {goog.ui.MenuItem?} */(this.getChildAt(n));
    +};
    +
    +
    +/**
    + * Returns the number of items in the menu (including separators).
    + * @return {number} The number of items in the menu.
    + * @deprecated Use {@link #getChildCount} instead.
    + */
    +goog.ui.Menu.prototype.getItemCount = function() {
    +  return this.getChildCount();
    +};
    +
    +
    +/**
    + * Returns an array containing the menu items contained in the menu.
    + * @return {!Array<goog.ui.MenuItem>} An array of menu items.
    + * @deprecated Use getChildAt, forEachChild, and getChildCount.
    + */
    +goog.ui.Menu.prototype.getItems = function() {
    +  // TODO(user): Remove reference to getItems and instead use getChildAt,
    +  // forEachChild, and getChildCount
    +  var children = [];
    +  this.forEachChild(function(child) {
    +    children.push(child);
    +  });
    +  return children;
    +};
    +
    +
    +/**
    + * Sets the position of the menu relative to the view port.
    + * @param {number|goog.math.Coordinate} x Left position or coordinate obj.
    + * @param {number=} opt_y Top position.
    + */
    +goog.ui.Menu.prototype.setPosition = function(x, opt_y) {
    +  // NOTE(user): It is necessary to temporarily set the display from none, so
    +  // that the position gets set correctly.
    +  var visible = this.isVisible();
    +  if (!visible) {
    +    goog.style.setElementShown(this.getElement(), true);
    +  }
    +  goog.style.setPageOffset(this.getElement(), x, opt_y);
    +  if (!visible) {
    +    goog.style.setElementShown(this.getElement(), false);
    +  }
    +};
    +
    +
    +/**
    + * Gets the page offset of the menu, or null if the menu isn't visible
    + * @return {goog.math.Coordinate?} Object holding the x-y coordinates of the
    + *     menu or null if the menu is not visible.
    + */
    +goog.ui.Menu.prototype.getPosition = function() {
    +  return this.isVisible() ? goog.style.getPageOffset(this.getElement()) : null;
    +};
    +
    +
    +/**
    + * Sets whether the menu can automatically move focus to its key event target
    + * when it is set to visible.
    + * @param {boolean} allow Whether the menu can automatically move focus to its
    + *     key event target when it is set to visible.
    + */
    +goog.ui.Menu.prototype.setAllowAutoFocus = function(allow) {
    +  this.allowAutoFocus_ = allow;
    +  if (allow) {
    +    this.setFocusable(true);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the menu can automatically move focus to its key
    + *     event target when it is set to visible.
    + */
    +goog.ui.Menu.prototype.getAllowAutoFocus = function() {
    +  return this.allowAutoFocus_;
    +};
    +
    +
    +/**
    + * Sets whether the menu will highlight disabled menu items or skip to the next
    + * active item.
    + * @param {boolean} allow Whether the menu will highlight disabled menu items or
    + *     skip to the next active item.
    + */
    +goog.ui.Menu.prototype.setAllowHighlightDisabled = function(allow) {
    +  this.allowHighlightDisabled_ = allow;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the menu will highlight disabled menu items or skip
    + *     to the next active item.
    + */
    +goog.ui.Menu.prototype.getAllowHighlightDisabled = function() {
    +  return this.allowHighlightDisabled_;
    +};
    +
    +
    +/**
    + * @override
    + * @param {goog.events.Event=} opt_e Mousedown event that caused this menu to
    + *     be made visible (ignored if show is false).
    + */
    +goog.ui.Menu.prototype.setVisible = function(show, opt_force, opt_e) {
    +  var visibilityChanged = goog.ui.Menu.superClass_.setVisible.call(this, show,
    +      opt_force);
    +  if (visibilityChanged && show && this.isInDocument() &&
    +      this.allowAutoFocus_) {
    +    this.getKeyEventTarget().focus();
    +  }
    +  if (show && opt_e && goog.isNumber(opt_e.clientX)) {
    +    this.openingCoords = new goog.math.Coordinate(opt_e.clientX, opt_e.clientY);
    +  } else {
    +    this.openingCoords = null;
    +  }
    +  return visibilityChanged;
    +};
    +
    +
    +/** @override */
    +goog.ui.Menu.prototype.handleEnterItem = function(e) {
    +  if (this.allowAutoFocus_) {
    +    this.getKeyEventTarget().focus();
    +  }
    +
    +  return goog.ui.Menu.superClass_.handleEnterItem.call(this, e);
    +};
    +
    +
    +/**
    + * Highlights the next item that begins with the specified string.  If no
    + * (other) item begins with the given string, the selection is unchanged.
    + * @param {string} charStr The prefix to match.
    + * @return {boolean} Whether a matching prefix was found.
    + */
    +goog.ui.Menu.prototype.highlightNextPrefix = function(charStr) {
    +  var re = new RegExp('^' + goog.string.regExpEscape(charStr), 'i');
    +  return this.highlightHelper(function(index, max) {
    +    // Index is >= -1 because it is set to -1 when nothing is selected.
    +    var start = index < 0 ? 0 : index;
    +    var wrapped = false;
    +
    +    // We always start looking from one after the current, because we
    +    // keep the current selection only as a last resort. This makes the
    +    // loop a little awkward in the case where there is no current
    +    // selection, as we need to stop somewhere but can't just stop
    +    // when index == start, which is why we need the 'wrapped' flag.
    +    do {
    +      ++index;
    +      if (index == max) {
    +        index = 0;
    +        wrapped = true;
    +      }
    +      var name = this.getChildAt(index).getCaption();
    +      if (name && name.match(re)) {
    +        return index;
    +      }
    +    } while (!wrapped || index != start);
    +    return this.getHighlightedIndex();
    +  }, this.getHighlightedIndex());
    +};
    +
    +
    +/** @override */
    +goog.ui.Menu.prototype.canHighlightItem = function(item) {
    +  return (this.allowHighlightDisabled_ || item.isEnabled()) &&
    +      item.isVisible() && item.isSupportedState(goog.ui.Component.State.HOVER);
    +};
    +
    +
    +/** @override */
    +goog.ui.Menu.prototype.decorateInternal = function(element) {
    +  this.decorateContent(element);
    +  goog.ui.Menu.superClass_.decorateInternal.call(this, element);
    +};
    +
    +
    +/** @override */
    +goog.ui.Menu.prototype.handleKeyEventInternal = function(e) {
    +  var handled = goog.ui.Menu.base(this, 'handleKeyEventInternal', e);
    +  if (!handled) {
    +    // Loop through all child components, and for each menu item call its
    +    // key event handler so that keyboard mnemonics can be handled.
    +    this.forEachChild(function(menuItem) {
    +      if (!handled && menuItem.getMnemonic &&
    +          menuItem.getMnemonic() == e.keyCode) {
    +        if (this.isEnabled()) {
    +          this.setHighlighted(menuItem);
    +        }
    +        // We still delegate to handleKeyEvent, so that it can handle
    +        // enabled/disabled state.
    +        handled = menuItem.handleKeyEvent(e);
    +      }
    +    }, this);
    +  }
    +  return handled;
    +};
    +
    +
    +/** @override */
    +goog.ui.Menu.prototype.setHighlightedIndex = function(index) {
    +  goog.ui.Menu.base(this, 'setHighlightedIndex', index);
    +
    +  // Bring the highlighted item into view. This has no effect if the menu is not
    +  // scrollable.
    +  var child = this.getChildAt(index);
    +  if (child) {
    +    goog.style.scrollIntoContainerView(child.getElement(), this.getElement());
    +  }
    +};
    +
    +
    +/**
    + * Decorate menu items located in any descendent node which as been explicitly
    + * marked as a 'content' node.
    + * @param {Element} element Element to decorate.
    + * @protected
    + */
    +goog.ui.Menu.prototype.decorateContent = function(element) {
    +  var renderer = this.getRenderer();
    +  var contentElements = this.getDomHelper().getElementsByTagNameAndClass('div',
    +      goog.getCssName(renderer.getCssClass(), 'content'), element);
    +
    +  // Some versions of IE do not like it when you access this nodeList
    +  // with invalid indices. See
    +  // http://code.google.com/p/closure-library/issues/detail?id=373
    +  var length = contentElements.length;
    +  for (var i = 0; i < length; i++) {
    +    renderer.decorateChildren(this, contentElements[i]);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menu_test.html b/mapbox-gl/closure-library/closure/goog/ui/menu_test.html
    new file mode 100644
    index 0000000000..469462eda8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menu_test.html
    @@ -0,0 +1,89 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Menu
    +  </title>
    +  <style type="text/css">
    +   .goog-menu {
    +  position: absolute;
    +  color: #aaa;
    +}
    +
    +.goog-scrollable-menu {
    +  max-height:30px;
    +  overflow-y:auto;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   Here's a menu defined in markup:
    +  </p>
    +  <div id="demoMenu" class="goog-menu">
    +   <div id="menuItem1" class="goog-menuitem">
    +    Annual Report.pdf
    +   </div>
    +   <div id="menuItem2" class="goog-menuitem">
    +    Quarterly Update.pdf
    +   </div>
    +   <div id="menuItem3" class="goog-menuitem">
    +    Enemies List.txt
    +   </div>
    +  </div>
    +  <p>
    +   Here's a menu which has been rendered with an explicit content node:
    +  </p>
    +  <div id="complexMenu" class="goog-menu">
    +   <div style="border:1px solid black;">
    +    <div class="goog-menu-content">
    +     <div id="complexItem1" class="goog-menuitem">
    +      Drizzle
    +     </div>
    +     <div id="complexItem2" class="goog-menuitem">
    +      Rain
    +     </div>
    +     <div id="complexItem3" class="goog-menuitem">
    +      Deluge
    +     </div>
    +    </div>
    +   </div>
    +  </div>
    +  <p>
    +   Here's a scrollable menu:
    +  </p>
    +  <div id="scrollableMenu" class="goog-menu goog-scrollable-menu">
    +   <div id="scrollableMenuItem1" class="goog-menuitem">
    +    Auk
    +   </div>
    +   <div id="scrollableMenuItem2" class="goog-menuitem">
    +    Bird
    +   </div>
    +   <div id="scrollableMenuItem3" class="goog-menuitem">
    +    Crow
    +   </div>
    +   <div id="scrollableMenuItem4" class="goog-menuitem">
    +    Duck
    +   </div>
    +   <div id="scrollableMenuItem5" class="goog-menuitem">
    +    Emu
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menu_test.js b/mapbox-gl/closure-library/closure/goog/ui/menu_test.js
    new file mode 100644
    index 0000000000..d9a495ce49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menu_test.js
    @@ -0,0 +1,114 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuTest');
    +goog.setTestOnly('goog.ui.MenuTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +
    +var menu;
    +var clonedMenuDom;
    +
    +function setUp() {
    +  clonedMenuDom = goog.dom.getElement('demoMenu').cloneNode(true);
    +
    +  menu = new goog.ui.Menu();
    +}
    +
    +function tearDown() {
    +  menu.dispose();
    +
    +  var element = goog.dom.getElement('demoMenu');
    +  element.parentNode.replaceChild(clonedMenuDom, element);
    +}
    +
    +
    +/** @bug 1463524 */
    +function testMouseupDoesntActivateMenuItemImmediately() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +
    +  var fakeEvent = {clientX: 42, clientY: 42};
    +  var itemElem = goog.dom.getElement('menuItem2');
    +  var coords = new goog.math.Coordinate(42, 42);
    +
    +  var menuItem = menu.getChildAt(1);
    +  var actionDispatched = false;
    +  goog.events.listen(menuItem, goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        actionDispatched = true;
    +      });
    +
    +  menu.setVisible(true, false, fakeEvent);
    +  // Makes the menu item active so it can be selected on mouseup.
    +  menuItem.setActive(true);
    +
    +  goog.testing.events.fireMouseUpEvent(itemElem, undefined, coords);
    +  assertFalse('ACTION should not be dispatched after the initial mouseup',
    +              actionDispatched);
    +
    +  goog.testing.events.fireMouseUpEvent(itemElem, undefined, coords);
    +  assertTrue('ACTION should be dispatched after the second mouseup',
    +             actionDispatched);
    +
    +}
    +
    +function testHoverBehavior() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +
    +  goog.testing.events.fireMouseOverEvent(goog.dom.getElement('menuItem2'),
    +      document.body);
    +  assertEquals(1, menu.getHighlightedIndex());
    +
    +  menu.exitDocument();
    +  assertEquals(-1, menu.getHighlightedIndex());
    +}
    +
    +function testIndirectionDecoration() {
    +  menu.decorate(goog.dom.getElement('complexMenu'));
    +
    +  goog.testing.events.fireMouseOverEvent(goog.dom.getElement('complexItem3'),
    +      document.body);
    +  assertEquals(2, menu.getHighlightedIndex());
    +
    +  menu.exitDocument();
    +  assertEquals(-1, menu.getHighlightedIndex());
    +}
    +
    +function testSetHighlightedIndex() {
    +  menu.decorate(goog.dom.getElement('scrollableMenu'));
    +  assertEquals(0, menu.getElement().scrollTop);
    +
    +  // Scroll down
    +  var element = goog.dom.getElement('scrollableMenuItem4');
    +  menu.setHighlightedIndex(3);
    +  assertTrue(element.offsetTop >= menu.getElement().scrollTop);
    +  assertTrue(element.offsetTop <=
    +      menu.getElement().scrollTop + menu.getElement().offsetHeight);
    +
    +  // Scroll up
    +  element = goog.dom.getElement('scrollableMenuItem3');
    +  menu.setHighlightedIndex(2);
    +  assertTrue(element.offsetTop >= menu.getElement().scrollTop);
    +  assertTrue(element.offsetTop <=
    +      menu.getElement().scrollTop + menu.getElement().offsetHeight);
    +
    +  menu.exitDocument();
    +  assertEquals(-1, menu.getHighlightedIndex());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubar.js b/mapbox-gl/closure-library/closure/goog/ui/menubar.js
    new file mode 100644
    index 0000000000..6afc3f8111
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubar.js
    @@ -0,0 +1,44 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A base menu bar factory. Can be bound to an existing
    + * HTML structure or can generate its own DOM.
    + *
    + * To decorate, the menu bar should be bound to an element containing children
    + * with the classname 'goog-menu-button'.  See menubar.html for example.
    + *
    + * @see ../demos/menubar.html
    + */
    +
    +goog.provide('goog.ui.menuBar');
    +
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.MenuBarRenderer');
    +
    +
    +/**
    + * The menuBar factory creates a new menu bar.
    + * @param {goog.ui.ContainerRenderer=} opt_renderer Renderer used to render or
    + *     decorate the menu bar; defaults to {@link goog.ui.MenuBarRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for document
    + *     interaction.
    + * @return {!goog.ui.Container} The created menu bar.
    + */
    +goog.ui.menuBar.create = function(opt_renderer, opt_domHelper) {
    +  return new goog.ui.Container(
    +      null,
    +      opt_renderer ? opt_renderer : goog.ui.MenuBarRenderer.getInstance(),
    +      opt_domHelper);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubardecorator.js b/mapbox-gl/closure-library/closure/goog/ui/menubardecorator.js
    new file mode 100644
    index 0000000000..2272e5f03b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubardecorator.js
    @@ -0,0 +1,35 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of MenuBarRenderer decorator, a static call into
    + * the goog.ui.registry.
    + *
    + * @see ../demos/menubar.html
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.ui.menuBarDecorator');
    +
    +goog.require('goog.ui.MenuBarRenderer');
    +goog.require('goog.ui.menuBar');
    +goog.require('goog.ui.registry');
    +
    +
    +/**
    + * Register a decorator factory function. 'goog-menubar' defaults to
    + * goog.ui.MenuBarRenderer.
    + */
    +goog.ui.registry.setDecoratorByClassName(goog.ui.MenuBarRenderer.CSS_CLASS,
    +    goog.ui.menuBar.create);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubarrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menubarrenderer.js
    new file mode 100644
    index 0000000000..df8ea96985
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubarrenderer.js
    @@ -0,0 +1,68 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.menuBar}.
    + *
    + */
    +
    +goog.provide('goog.ui.MenuBarRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.ContainerRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.menuBar}s, based on {@link
    + * goog.ui.ContainerRenderer}.
    + * @constructor
    + * @extends {goog.ui.ContainerRenderer}
    + * @final
    + */
    +goog.ui.MenuBarRenderer = function() {
    +  goog.ui.MenuBarRenderer.base(this, 'constructor',
    +      goog.a11y.aria.Role.MENUBAR);
    +};
    +goog.inherits(goog.ui.MenuBarRenderer, goog.ui.ContainerRenderer);
    +goog.addSingletonGetter(goog.ui.MenuBarRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of elements rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.MenuBarRenderer.CSS_CLASS = goog.getCssName('goog-menubar');
    +
    +
    +/**
    + * @override
    + */
    +goog.ui.MenuBarRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuBarRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Returns the default orientation of containers rendered or decorated by this
    + * renderer.  This implementation returns {@code HORIZONTAL}.
    + * @return {goog.ui.Container.Orientation} Default orientation for containers
    + *     created or decorated by this renderer.
    + * @override
    + */
    +goog.ui.MenuBarRenderer.prototype.getDefaultOrientation = function() {
    +  return goog.ui.Container.Orientation.HORIZONTAL;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubase.js b/mapbox-gl/closure-library/closure/goog/ui/menubase.js
    new file mode 100644
    index 0000000000..23f0b525e8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubase.js
    @@ -0,0 +1,190 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the MenuBase class.
    + *
    + */
    +
    +goog.provide('goog.ui.MenuBase');
    +
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.ui.Popup');
    +
    +
    +
    +/**
    + * The MenuBase class provides an abstract base class for different
    + * implementations of menu controls.
    + *
    + * @param {Element=} opt_element A DOM element for the popup.
    + * @deprecated Use goog.ui.Menu.
    + * @constructor
    + * @extends {goog.ui.Popup}
    + */
    +goog.ui.MenuBase = function(opt_element) {
    +  goog.ui.Popup.call(this, opt_element);
    +
    +  /**
    +   * Event handler for simplifiying adding/removing listeners.
    +   * @type {goog.events.EventHandler<!goog.ui.MenuBase>}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * KeyHandler to cope with the vagaries of cross-browser key events.
    +   * @type {goog.events.KeyHandler}
    +   * @private
    +   */
    +  this.keyHandler_ = new goog.events.KeyHandler(this.getElement());
    +};
    +goog.inherits(goog.ui.MenuBase, goog.ui.Popup);
    +
    +
    +/**
    + * Events fired by the Menu
    + */
    +goog.ui.MenuBase.Events = {};
    +
    +
    +/**
    + * Event fired by the Menu when an item is "clicked".
    + */
    +goog.ui.MenuBase.Events.ITEM_ACTION = 'itemaction';
    +
    +
    +/** @override */
    +goog.ui.MenuBase.prototype.disposeInternal = function() {
    +  goog.ui.MenuBase.superClass_.disposeInternal.call(this);
    +  this.eventHandler_.dispose();
    +  this.keyHandler_.dispose();
    +};
    +
    +
    +/**
    + * Called after the menu is shown. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + *
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.MenuBase.prototype.onShow_ = function() {
    +  goog.ui.MenuBase.superClass_.onShow_.call(this);
    +
    +  // register common event handlers for derived classes
    +  var el = this.getElement();
    +  this.eventHandler_.listen(
    +      el, goog.events.EventType.MOUSEOVER, this.onMouseOver);
    +  this.eventHandler_.listen(
    +      el, goog.events.EventType.MOUSEOUT, this.onMouseOut);
    +  this.eventHandler_.listen(
    +      el, goog.events.EventType.MOUSEDOWN, this.onMouseDown);
    +  this.eventHandler_.listen(
    +      el, goog.events.EventType.MOUSEUP, this.onMouseUp);
    +
    +  this.eventHandler_.listen(
    +      this.keyHandler_,
    +      goog.events.KeyHandler.EventType.KEY,
    +      this.onKeyDown);
    +};
    +
    +
    +/**
    + * Called after the menu is hidden. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + * @param {Object=} opt_target Target of the event causing the hide.
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.MenuBase.prototype.onHide_ = function(opt_target) {
    +  goog.ui.MenuBase.superClass_.onHide_.call(this, opt_target);
    +
    +  // remove listeners when hidden
    +  this.eventHandler_.removeAll();
    +};
    +
    +
    +/**
    + * Returns the selected item
    + *
    + * @return {Object} The item selected or null if no item is selected.
    + */
    +goog.ui.MenuBase.prototype.getSelectedItem = function() {
    +  return null;
    +};
    +
    +
    +/**
    + * Sets the selected item
    + *
    + * @param {Object} item The item to select. The type of this item is specific
    + *     to the menu class.
    + */
    +goog.ui.MenuBase.prototype.setSelectedItem = function(item) {
    +};
    +
    +
    +/**
    + * Mouse over handler for the menu. Derived classes should override.
    + *
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + */
    +goog.ui.MenuBase.prototype.onMouseOver = function(e) {
    +};
    +
    +
    +/**
    + * Mouse out handler for the menu. Derived classes should override.
    + *
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + */
    +goog.ui.MenuBase.prototype.onMouseOut = function(e) {
    +};
    +
    +
    +/**
    + * Mouse down handler for the menu. Derived classes should override.
    + *
    + * @param {!goog.events.Event} e The event object.
    + * @protected
    + */
    +goog.ui.MenuBase.prototype.onMouseDown = function(e) {
    +};
    +
    +
    +/**
    + * Mouse up handler for the menu. Derived classes should override.
    + *
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + */
    +goog.ui.MenuBase.prototype.onMouseUp = function(e) {
    +};
    +
    +
    +/**
    + * Key down handler for the menu. Derived classes should override.
    + *
    + * @param {goog.events.KeyEvent} e The event object.
    + * @protected
    + */
    +goog.ui.MenuBase.prototype.onKeyDown = function(e) {
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubutton.js b/mapbox-gl/closure-library/closure/goog/ui/menubutton.js
    new file mode 100644
    index 0000000000..c9e20fd977
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubutton.js
    @@ -0,0 +1,1052 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A menu button control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/menubutton.html
    + */
    +
    +goog.provide('goog.ui.MenuButton');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Rect');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.MenuAnchoredPosition');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.style');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.IdGenerator');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuRenderer');
    +goog.require('goog.ui.registry');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +
    +
    +/**
    + * A menu button control.  Extends {@link goog.ui.Button} by composing a button
    + * with a dropdown arrow and a popup menu.
    + *
    + * @param {goog.ui.ControlContent=} opt_content Text caption or existing DOM
    + *     structure to display as the button's caption (if any).
    + * @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
    + *     decorate the menu button; defaults to {@link goog.ui.MenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @param {!goog.ui.MenuRenderer=} opt_menuRenderer Renderer used to render or
    + *     decorate the menu; defaults to {@link goog.ui.MenuRenderer}.
    + * @constructor
    + * @extends {goog.ui.Button}
    + */
    +goog.ui.MenuButton = function(opt_content, opt_menu, opt_renderer,
    +    opt_domHelper, opt_menuRenderer) {
    +  goog.ui.Button.call(this, opt_content, opt_renderer ||
    +      goog.ui.MenuButtonRenderer.getInstance(), opt_domHelper);
    +
    +  // Menu buttons support the OPENED state.
    +  this.setSupportedState(goog.ui.Component.State.OPENED, true);
    +
    +  /**
    +   * The menu position on this button.
    +   * @type {!goog.positioning.AnchoredPosition}
    +   * @private
    +   */
    +  this.menuPosition_ = new goog.positioning.MenuAnchoredPosition(
    +      null, goog.positioning.Corner.BOTTOM_START);
    +
    +  if (opt_menu) {
    +    this.setMenu(opt_menu);
    +  }
    +  this.menuMargin_ = null;
    +  this.timer_ = new goog.Timer(500);  // 0.5 sec
    +
    +  // Phones running iOS prior to version 4.2.
    +  if ((goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) &&
    +      // Check the webkit version against the version for iOS 4.2.1.
    +      !goog.userAgent.isVersionOrHigher('533.17.9')) {
    +    // @bug 4322060 This is required so that the menu works correctly on
    +    // iOS prior to version 4.2. Otherwise, the blur action closes the menu
    +    // before the menu button click can be processed.
    +    this.setFocusablePopupMenu(true);
    +  }
    +
    +  /** @private {!goog.ui.MenuRenderer} */
    +  this.menuRenderer_ = opt_menuRenderer || goog.ui.MenuRenderer.getInstance();
    +};
    +goog.inherits(goog.ui.MenuButton, goog.ui.Button);
    +goog.tagUnsealableClass(goog.ui.MenuButton);
    +
    +
    +/**
    + * The menu.
    + * @type {goog.ui.Menu|undefined}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.menu_;
    +
    +
    +/**
    + * The position element.  If set, use positionElement_ to position the
    + * popup menu instead of the default which is to use the menu button element.
    + * @type {Element|undefined}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.positionElement_;
    +
    +
    +/**
    + * The margin to apply to the menu's position when it is shown.  If null, no
    + * margin will be applied.
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.menuMargin_;
    +
    +
    +/**
    + * Whether the attached popup menu is focusable or not (defaults to false).
    + * Popup menus attached to menu buttons usually don't need to be focusable,
    + * i.e. the button retains keyboard focus, and forwards key events to the
    + * menu for processing.  However, menus like {@link goog.ui.FilteredMenu}
    + * need to be focusable.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.isFocusablePopupMenu_ = false;
    +
    +
    +/**
    + * A Timer to correct menu position.
    + * @type {goog.Timer}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.timer_;
    +
    +
    +/**
    + * The bounding rectangle of the button element.
    + * @type {goog.math.Rect}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.buttonRect_;
    +
    +
    +/**
    + * The viewport rectangle.
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.viewportBox_;
    +
    +
    +/**
    + * The original size.
    + * @type {goog.math.Size|undefined}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.originalSize_;
    +
    +
    +/**
    + * Do we render the drop down menu as a sibling to the label, or at the end
    + * of the current dom?
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.MenuButton.prototype.renderMenuAsSibling_ = false;
    +
    +
    +/**
    + * Whether to select the first item in the menu when it is opened using
    + * enter or space. By default, the first item is selected only when
    + * opened by a key up or down event. When this is on, the first item will
    + * be selected due to any of the four events.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.selectFirstOnEnterOrSpace_ = false;
    +
    +
    +/**
    + * Sets up event handlers specific to menu buttons.
    + * @override
    + */
    +goog.ui.MenuButton.prototype.enterDocument = function() {
    +  goog.ui.MenuButton.superClass_.enterDocument.call(this);
    +  this.attachKeyDownEventListener_(true);
    +  if (this.menu_) {
    +    this.attachMenuEventListeners_(this.menu_, true);
    +  }
    +  goog.a11y.aria.setState(this.getElementStrict(),
    +      goog.a11y.aria.State.HASPOPUP, !!this.menu_);
    +};
    +
    +
    +/**
    + * Removes event handlers specific to menu buttons, and ensures that the
    + * attached menu also exits the document.
    + * @override
    + */
    +goog.ui.MenuButton.prototype.exitDocument = function() {
    +  goog.ui.MenuButton.superClass_.exitDocument.call(this);
    +  this.attachKeyDownEventListener_(false);
    +  if (this.menu_) {
    +    this.setOpen(false);
    +    this.menu_.exitDocument();
    +    this.attachMenuEventListeners_(this.menu_, false);
    +
    +    var menuElement = this.menu_.getElement();
    +    if (menuElement) {
    +      goog.dom.removeNode(menuElement);
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuButton.prototype.disposeInternal = function() {
    +  goog.ui.MenuButton.superClass_.disposeInternal.call(this);
    +  if (this.menu_) {
    +    this.menu_.dispose();
    +    delete this.menu_;
    +  }
    +  delete this.positionElement_;
    +  this.timer_.dispose();
    +};
    +
    +
    +/**
    + * Handles mousedown events.  Invokes the superclass implementation to dispatch
    + * an ACTIVATE event and activate the button.  Also toggles the visibility of
    + * the attached menu.
    + * @param {goog.events.Event} e Mouse event to handle.
    + * @override
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.handleMouseDown = function(e) {
    +  goog.ui.MenuButton.superClass_.handleMouseDown.call(this, e);
    +  if (this.isActive()) {
    +    // The component was allowed to activate; toggle menu visibility.
    +    this.setOpen(!this.isOpen(), e);
    +    if (this.menu_) {
    +      this.menu_.setMouseButtonPressed(this.isOpen());
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles mouseup events.  Invokes the superclass implementation to dispatch
    + * an ACTION event and deactivate the button.
    + * @param {goog.events.Event} e Mouse event to handle.
    + * @override
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.handleMouseUp = function(e) {
    +  goog.ui.MenuButton.superClass_.handleMouseUp.call(this, e);
    +  if (this.menu_ && !this.isActive()) {
    +    this.menu_.setMouseButtonPressed(false);
    +  }
    +};
    +
    +
    +/**
    + * Performs the appropriate action when the menu button is activated by the
    + * user.  Overrides the superclass implementation by not dispatching an {@code
    + * ACTION} event, because menu buttons exist only to reveal menus, not to
    + * perform actions themselves.  Calls {@link #setActive} to deactivate the
    + * button.
    + * @param {goog.events.Event} e Mouse or key event that triggered the action.
    + * @return {boolean} Whether the action was allowed to proceed.
    + * @override
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.performActionInternal = function(e) {
    +  this.setActive(false);
    +  return true;
    +};
    +
    +
    +/**
    + * Handles mousedown events over the document.  If the mousedown happens over
    + * an element unrelated to the component, hides the menu.
    + * TODO(attila): Reconcile this with goog.ui.Popup (and handle frames/windows).
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.handleDocumentMouseDown = function(e) {
    +  if (this.menu_ &&
    +      this.menu_.isVisible() &&
    +      !this.containsElement(/** @type {Element} */ (e.target))) {
    +    // User clicked somewhere else in the document while the menu was visible;
    +    // dismiss menu.
    +    this.setOpen(false);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the given element is to be considered part of the component,
    + * even if it isn't a DOM descendant of the component's root element.
    + * @param {Element} element Element to test (if any).
    + * @return {boolean} Whether the element is considered part of the component.
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.containsElement = function(element) {
    +  return element && goog.dom.contains(this.getElement(), element) ||
    +      this.menu_ && this.menu_.containsElement(element) || false;
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuButton.prototype.handleKeyEventInternal = function(e) {
    +  // Handle SPACE on keyup and all other keys on keypress.
    +  if (e.keyCode == goog.events.KeyCodes.SPACE) {
    +    // Prevent page scrolling in Chrome.
    +    e.preventDefault();
    +    if (e.type != goog.events.EventType.KEYUP) {
    +      // Ignore events because KeyCodes.SPACE is handled further down.
    +      return true;
    +    }
    +  } else if (e.type != goog.events.KeyHandler.EventType.KEY) {
    +    return false;
    +  }
    +
    +  if (this.menu_ && this.menu_.isVisible()) {
    +    // Menu is open.
    +    var isEnterOrSpace = e.keyCode == goog.events.KeyCodes.ENTER ||
    +        e.keyCode == goog.events.KeyCodes.SPACE;
    +    var handledByMenu = this.menu_.handleKeyEvent(e);
    +    if (e.keyCode == goog.events.KeyCodes.ESC || isEnterOrSpace) {
    +      // Dismiss the menu.
    +      this.setOpen(false);
    +      return true;
    +    }
    +    return handledByMenu;
    +  }
    +
    +  if (e.keyCode == goog.events.KeyCodes.DOWN ||
    +      e.keyCode == goog.events.KeyCodes.UP ||
    +      e.keyCode == goog.events.KeyCodes.SPACE ||
    +      e.keyCode == goog.events.KeyCodes.ENTER) {
    +    // Menu is closed, and the user hit the down/up/space/enter key; open menu.
    +    this.setOpen(true, e);
    +    return true;
    +  }
    +
    +  // Key event wasn't handled by the component.
    +  return false;
    +};
    +
    +
    +/**
    + * Handles {@code ACTION} events dispatched by an activated menu item.
    + * @param {goog.events.Event} e Action event to handle.
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.handleMenuAction = function(e) {
    +  // Close the menu on click.
    +  this.setOpen(false);
    +};
    +
    +
    +/**
    + * Handles {@code BLUR} events dispatched by the popup menu by closing it.
    + * Only registered if the menu is focusable.
    + * @param {goog.events.Event} e Blur event dispatched by a focusable menu.
    + */
    +goog.ui.MenuButton.prototype.handleMenuBlur = function(e) {
    +  // Close the menu when it reports that it lost focus, unless the button is
    +  // pressed (active).
    +  if (!this.isActive()) {
    +    this.setOpen(false);
    +  }
    +};
    +
    +
    +/**
    + * Handles blur events dispatched by the button's key event target when it
    + * loses keyboard focus by closing the popup menu (unless it is focusable).
    + * Only registered if the button is focusable.
    + * @param {goog.events.Event} e Blur event dispatched by the menu button.
    + * @override
    + * @protected
    + */
    +goog.ui.MenuButton.prototype.handleBlur = function(e) {
    +  if (!this.isFocusablePopupMenu()) {
    +    this.setOpen(false);
    +  }
    +  goog.ui.MenuButton.superClass_.handleBlur.call(this, e);
    +};
    +
    +
    +/**
    + * Returns the menu attached to the button.  If no menu is attached, creates a
    + * new empty menu.
    + * @return {goog.ui.Menu} Popup menu attached to the menu button.
    + */
    +goog.ui.MenuButton.prototype.getMenu = function() {
    +  if (!this.menu_) {
    +    this.setMenu(new goog.ui.Menu(this.getDomHelper(), this.menuRenderer_));
    +  }
    +  return this.menu_ || null;
    +};
    +
    +
    +/**
    + * Replaces the menu attached to the button with the argument, and returns the
    + * previous menu (if any).
    + * @param {goog.ui.Menu?} menu New menu to be attached to the menu button (null
    + *     to remove the menu).
    + * @return {goog.ui.Menu|undefined} Previous menu (undefined if none).
    + */
    +goog.ui.MenuButton.prototype.setMenu = function(menu) {
    +  var oldMenu = this.menu_;
    +
    +  // Do nothing unless the new menu is different from the current one.
    +  if (menu != oldMenu) {
    +    if (oldMenu) {
    +      this.setOpen(false);
    +      if (this.isInDocument()) {
    +        this.attachMenuEventListeners_(oldMenu, false);
    +      }
    +      delete this.menu_;
    +    }
    +    if (this.isInDocument()) {
    +      goog.a11y.aria.setState(this.getElementStrict(),
    +          goog.a11y.aria.State.HASPOPUP, !!menu);
    +    }
    +    if (menu) {
    +      this.menu_ = menu;
    +      menu.setParent(this);
    +      menu.setVisible(false);
    +      menu.setAllowAutoFocus(this.isFocusablePopupMenu());
    +      if (this.isInDocument()) {
    +        this.attachMenuEventListeners_(menu, true);
    +      }
    +    }
    +  }
    +
    +  return oldMenu;
    +};
    +
    +
    +/**
    + * Specify which positioning algorithm to use.
    + *
    + * This method is preferred over the fine-grained positioning methods like
    + * setPositionElement, setAlignMenuToStart, and setScrollOnOverflow. Calling
    + * this method will override settings by those methods.
    + *
    + * @param {goog.positioning.AnchoredPosition} position The position of the
    + *     Menu the button. If the position has a null anchor, we will use the
    + *     menubutton element as the anchor.
    + */
    +goog.ui.MenuButton.prototype.setMenuPosition = function(position) {
    +  if (position) {
    +    this.menuPosition_ = position;
    +    this.positionElement_ = position.element;
    +  }
    +};
    +
    +
    +/**
    + * Sets an element for anchoring the menu.
    + * @param {Element} positionElement New element to use for
    + *     positioning the dropdown menu.  Null to use the default behavior
    + *     of positioning to this menu button.
    + */
    +goog.ui.MenuButton.prototype.setPositionElement = function(
    +    positionElement) {
    +  this.positionElement_ = positionElement;
    +  this.positionMenu();
    +};
    +
    +
    +/**
    + * Sets a margin that will be applied to the menu's position when it is shown.
    + * If null, no margin will be applied.
    + * @param {goog.math.Box} margin Margin to apply.
    + */
    +goog.ui.MenuButton.prototype.setMenuMargin = function(margin) {
    +  this.menuMargin_ = margin;
    +};
    +
    +
    +/**
    + * Sets whether to select the first item in the menu when it is opened using
    + * enter or space. By default, the first item is selected only when
    + * opened by a key up or down event. When this is on, the first item will
    + * be selected due to any of the four events.
    + * @param {boolean} select
    + */
    +goog.ui.MenuButton.prototype.setSelectFirstOnEnterOrSpace = function(select) {
    +  this.selectFirstOnEnterOrSpace_ = select;
    +};
    +
    +
    +/**
    + * Adds a new menu item at the end of the menu.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator|goog.ui.Control} item Menu
    + *     item to add to the menu.
    + */
    +goog.ui.MenuButton.prototype.addItem = function(item) {
    +  this.getMenu().addChild(item, true);
    +};
    +
    +
    +/**
    + * Adds a new menu item at the specific index in the menu.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu item to add to the
    + *     menu.
    + * @param {number} index Index at which to insert the menu item.
    + */
    +goog.ui.MenuButton.prototype.addItemAt = function(item, index) {
    +  this.getMenu().addChildAt(item, index, true);
    +};
    +
    +
    +/**
    + * Removes the item from the menu and disposes of it.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator} item The menu item to remove.
    + */
    +goog.ui.MenuButton.prototype.removeItem = function(item) {
    +  var child = this.getMenu().removeChild(item, true);
    +  if (child) {
    +    child.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Removes the menu item at a given index in the menu and disposes of it.
    + * @param {number} index Index of item.
    + */
    +goog.ui.MenuButton.prototype.removeItemAt = function(index) {
    +  var child = this.getMenu().removeChildAt(index, true);
    +  if (child) {
    +    child.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Returns the menu item at a given index.
    + * @param {number} index Index of menu item.
    + * @return {goog.ui.MenuItem?} Menu item (null if not found).
    + */
    +goog.ui.MenuButton.prototype.getItemAt = function(index) {
    +  return this.menu_ ?
    +      /** @type {goog.ui.MenuItem} */ (this.menu_.getChildAt(index)) : null;
    +};
    +
    +
    +/**
    + * Returns the number of items in the menu (including separators).
    + * @return {number} The number of items in the menu.
    + */
    +goog.ui.MenuButton.prototype.getItemCount = function() {
    +  return this.menu_ ? this.menu_.getChildCount() : 0;
    +};
    +
    +
    +/**
    + * Shows/hides the menu button based on the value of the argument.  Also hides
    + * the popup menu if the button is being hidden.
    + * @param {boolean} visible Whether to show or hide the button.
    + * @param {boolean=} opt_force If true, doesn't check whether the component
    + *     already has the requested visibility, and doesn't dispatch any events.
    + * @return {boolean} Whether the visibility was changed.
    + * @override
    + */
    +goog.ui.MenuButton.prototype.setVisible = function(visible, opt_force) {
    +  var visibilityChanged = goog.ui.MenuButton.superClass_.setVisible.call(this,
    +      visible, opt_force);
    +  if (visibilityChanged && !this.isVisible()) {
    +    this.setOpen(false);
    +  }
    +  return visibilityChanged;
    +};
    +
    +
    +/**
    + * Enables/disables the menu button based on the value of the argument, and
    + * updates its CSS styling.  Also hides the popup menu if the button is being
    + * disabled.
    + * @param {boolean} enable Whether to enable or disable the button.
    + * @override
    + */
    +goog.ui.MenuButton.prototype.setEnabled = function(enable) {
    +  goog.ui.MenuButton.superClass_.setEnabled.call(this, enable);
    +  if (!this.isEnabled()) {
    +    this.setOpen(false);
    +  }
    +};
    +
    +
    +// TODO(nicksantos): AlignMenuToStart and ScrollOnOverflow and PositionElement
    +// should all be deprecated, in favor of people setting their own
    +// AnchoredPosition with the parameters they need. Right now, we try
    +// to be backwards-compatible as possible, but this is incomplete because
    +// the APIs are non-orthogonal.
    +
    +
    +/**
    + * @return {boolean} Whether the menu is aligned to the start of the button
    + *     (left if the render direction is left-to-right, right if the render
    + *     direction is right-to-left).
    + */
    +goog.ui.MenuButton.prototype.isAlignMenuToStart = function() {
    +  var corner = this.menuPosition_.corner;
    +  return corner == goog.positioning.Corner.BOTTOM_START ||
    +      corner == goog.positioning.Corner.TOP_START;
    +};
    +
    +
    +/**
    + * Sets whether the menu is aligned to the start or the end of the button.
    + * @param {boolean} alignToStart Whether the menu is to be aligned to the start
    + *     of the button (left if the render direction is left-to-right, right if
    + *     the render direction is right-to-left).
    + */
    +goog.ui.MenuButton.prototype.setAlignMenuToStart = function(alignToStart) {
    +  this.menuPosition_.corner = alignToStart ?
    +      goog.positioning.Corner.BOTTOM_START :
    +      goog.positioning.Corner.BOTTOM_END;
    +};
    +
    +
    +/**
    + * Sets whether the menu should scroll when it's too big to fix vertically on
    + * the screen.  The css of the menu element should have overflow set to auto.
    + * Note: Adding or removing items while the menu is open will not work correctly
    + * if scrollOnOverflow is on.
    + * @param {boolean} scrollOnOverflow Whether the menu should scroll when too big
    + *     to fit on the screen.  If false, adjust logic will be used to try and
    + *     reposition the menu to fit.
    + */
    +goog.ui.MenuButton.prototype.setScrollOnOverflow = function(scrollOnOverflow) {
    +  if (this.menuPosition_.setLastResortOverflow) {
    +    var overflowX = goog.positioning.Overflow.ADJUST_X;
    +    var overflowY = scrollOnOverflow ?
    +        goog.positioning.Overflow.RESIZE_HEIGHT :
    +        goog.positioning.Overflow.ADJUST_Y;
    +    this.menuPosition_.setLastResortOverflow(overflowX | overflowY);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Wether the menu will scroll when it's to big to fit
    + *     vertically on the screen.
    + */
    +goog.ui.MenuButton.prototype.isScrollOnOverflow = function() {
    +  return this.menuPosition_.getLastResortOverflow &&
    +      !!(this.menuPosition_.getLastResortOverflow() &
    +         goog.positioning.Overflow.RESIZE_HEIGHT);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the attached menu is focusable.
    + */
    +goog.ui.MenuButton.prototype.isFocusablePopupMenu = function() {
    +  return this.isFocusablePopupMenu_;
    +};
    +
    +
    +/**
    + * Sets whether the attached popup menu is focusable.  If the popup menu is
    + * focusable, it may steal keyboard focus from the menu button, so the button
    + * will not hide the menu on blur.
    + * @param {boolean} focusable Whether the attached menu is focusable.
    + */
    +goog.ui.MenuButton.prototype.setFocusablePopupMenu = function(focusable) {
    +  // TODO(attila):  The menu itself should advertise whether it is focusable.
    +  this.isFocusablePopupMenu_ = focusable;
    +};
    +
    +
    +/**
    + * Sets whether to render the menu as a sibling element of the button.
    + * Normally, the menu is a child of document.body.  This option is useful if
    + * you need the menu to inherit styles from a common parent element, or if you
    + * otherwise need it to share a parent element for desired event handling.  One
    + * example of the latter is if the parent is in a goog.ui.Popup, to ensure that
    + * clicks on the menu are considered being within the popup.
    + * @param {boolean} renderMenuAsSibling Whether we render the menu at the end
    + *     of the dom or as a sibling to the button/label that renders the drop
    + *     down.
    + */
    +goog.ui.MenuButton.prototype.setRenderMenuAsSibling = function(
    +    renderMenuAsSibling) {
    +  this.renderMenuAsSibling_ = renderMenuAsSibling;
    +};
    +
    +
    +/**
    + * Reveals the menu and hooks up menu-specific event handling.
    + * @deprecated Use {@link #setOpen} instead.
    + */
    +goog.ui.MenuButton.prototype.showMenu = function() {
    +  this.setOpen(true);
    +};
    +
    +
    +/**
    + * Hides the menu and cleans up menu-specific event handling.
    + * @deprecated Use {@link #setOpen} instead.
    + */
    +goog.ui.MenuButton.prototype.hideMenu = function() {
    +  this.setOpen(false);
    +};
    +
    +
    +/**
    + * Opens or closes the attached popup menu.
    + * @param {boolean} open Whether to open or close the menu.
    + * @param {goog.events.Event=} opt_e Event that caused the menu to be opened.
    + * @override
    + */
    +goog.ui.MenuButton.prototype.setOpen = function(open, opt_e) {
    +  goog.ui.MenuButton.superClass_.setOpen.call(this, open);
    +  if (this.menu_ && this.hasState(goog.ui.Component.State.OPENED) == open) {
    +    if (open) {
    +      if (!this.menu_.isInDocument()) {
    +        if (this.renderMenuAsSibling_) {
    +          // When we render the menu in the same parent as this button, we
    +          // prefer to add it immediately after the button. This way, the screen
    +          // readers will go to the menu on the very next element after the
    +          // button is read.
    +          var nextElementSibling =
    +              goog.dom.getNextElementSibling(this.getElement());
    +          if (nextElementSibling) {
    +            this.menu_.renderBefore(nextElementSibling);
    +          } else {
    +            this.menu_.render(/** @type {Element} */ (
    +                this.getElement().parentNode));
    +          }
    +        } else {
    +          this.menu_.render();
    +        }
    +      }
    +      this.viewportBox_ =
    +          goog.style.getVisibleRectForElement(this.getElement());
    +      this.buttonRect_ = goog.style.getBounds(this.getElement());
    +      this.positionMenu();
    +
    +      // As per aria spec, highlight the first element in the menu when
    +      // keyboarding up or down. Thus, the first menu item will be announced
    +      // for screen reader users. If selectFirstOnEnterOrSpace is set, do this
    +      // for enter or space as well.
    +      var isEnterOrSpace = !!opt_e &&
    +          (opt_e.keyCode == goog.events.KeyCodes.ENTER ||
    +           opt_e.keyCode == goog.events.KeyCodes.SPACE);
    +      var isUpOrDown = !!opt_e &&
    +          (opt_e.keyCode == goog.events.KeyCodes.DOWN ||
    +           opt_e.keyCode == goog.events.KeyCodes.UP);
    +      var focus = isUpOrDown ||
    +          (isEnterOrSpace && this.selectFirstOnEnterOrSpace_);
    +      if (focus) {
    +        this.menu_.highlightFirst();
    +      } else {
    +        this.menu_.setHighlightedIndex(-1);
    +      }
    +    } else {
    +      this.setActive(false);
    +      this.menu_.setMouseButtonPressed(false);
    +
    +      var element = this.getElement();
    +      // Clear any remaining a11y state.
    +      if (element) {
    +        goog.a11y.aria.setState(element,
    +            goog.a11y.aria.State.ACTIVEDESCENDANT,
    +            '');
    +        goog.a11y.aria.setState(element,
    +            goog.a11y.aria.State.OWNS,
    +            '');
    +      }
    +
    +      // Clear any sizes that might have been stored.
    +      if (goog.isDefAndNotNull(this.originalSize_)) {
    +        this.originalSize_ = undefined;
    +        var elem = this.menu_.getElement();
    +        if (elem) {
    +          goog.style.setSize(elem, '', '');
    +        }
    +      }
    +    }
    +    this.menu_.setVisible(open, false, opt_e);
    +    // In Pivot Tables the menu button somehow gets disposed of during the
    +    // setVisible call, causing attachPopupListeners_ to fail.
    +    // TODO(user): Debug what happens.
    +    if (!this.isDisposed()) {
    +      this.attachPopupListeners_(open);
    +    }
    +  }
    +  if (this.menu_ && this.menu_.getElement()) {
    +    // Remove the aria-hidden state on the menu element so that it won't be
    +    // hidden to screen readers if it's inside a dialog (see b/17610491).
    +    goog.a11y.aria.removeState(
    +        this.menu_.getElementStrict(), goog.a11y.aria.State.HIDDEN);
    +  }
    +};
    +
    +
    +/**
    + * Resets the MenuButton's size.  This is useful for cases where items are added
    + * or removed from the menu and scrollOnOverflow is on.  In those cases the
    + * menu will not behave correctly and resize itself unless this is called
    + * (usually followed by positionMenu()).
    + */
    +goog.ui.MenuButton.prototype.invalidateMenuSize = function() {
    +  this.originalSize_ = undefined;
    +};
    +
    +
    +/**
    + * Positions the menu under the button.  May be called directly in cases when
    + * the menu size is known to change.
    + */
    +goog.ui.MenuButton.prototype.positionMenu = function() {
    +  if (!this.menu_.isInDocument()) {
    +    return;
    +  }
    +
    +  var positionElement = this.positionElement_ || this.getElement();
    +  var position = this.menuPosition_;
    +  this.menuPosition_.element = positionElement;
    +
    +  var elem = this.menu_.getElement();
    +  if (!this.menu_.isVisible()) {
    +    elem.style.visibility = 'hidden';
    +    goog.style.setElementShown(elem, true);
    +  }
    +
    +  if (!this.originalSize_ && this.isScrollOnOverflow()) {
    +    this.originalSize_ = goog.style.getSize(elem);
    +  }
    +  var popupCorner = goog.positioning.flipCornerVertical(position.corner);
    +  position.reposition(elem, popupCorner, this.menuMargin_, this.originalSize_);
    +
    +  if (!this.menu_.isVisible()) {
    +    goog.style.setElementShown(elem, false);
    +    elem.style.visibility = 'visible';
    +  }
    +};
    +
    +
    +/**
    + * Periodically repositions the menu while it is visible.
    + *
    + * @param {goog.events.Event} e An event object.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.onTick_ = function(e) {
    +  // Call positionMenu() only if the button position or size was
    +  // changed, or if the window's viewport was changed.
    +  var currentButtonRect = goog.style.getBounds(this.getElement());
    +  var currentViewport = goog.style.getVisibleRectForElement(this.getElement());
    +  if (!goog.math.Rect.equals(this.buttonRect_, currentButtonRect) ||
    +      !goog.math.Box.equals(this.viewportBox_, currentViewport)) {
    +    this.buttonRect_ = currentButtonRect;
    +    this.viewportBox_ = currentViewport;
    +    this.positionMenu();
    +  }
    +};
    +
    +
    +/**
    + * Attaches or detaches menu event listeners to/from the given menu.
    + * Called each time a menu is attached to or detached from the button.
    + * @param {goog.ui.Menu} menu Menu on which to listen for events.
    + * @param {boolean} attach Whether to attach or detach event listeners.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.attachMenuEventListeners_ = function(menu,
    +    attach) {
    +  var handler = this.getHandler();
    +  var method = attach ? handler.listen : handler.unlisten;
    +
    +  // Handle events dispatched by menu items.
    +  method.call(handler, menu, goog.ui.Component.EventType.ACTION,
    +      this.handleMenuAction);
    +  method.call(handler, menu, goog.ui.Component.EventType.CLOSE,
    +      this.handleCloseItem);
    +  method.call(handler, menu, goog.ui.Component.EventType.HIGHLIGHT,
    +      this.handleHighlightItem);
    +  method.call(handler, menu, goog.ui.Component.EventType.UNHIGHLIGHT,
    +      this.handleUnHighlightItem);
    +};
    +
    +
    +/**
    + * Attaches or detaches a keydown event listener to/from the given element.
    + * Called each time the button enters or exits the document.
    + * @param {boolean} attach Whether to attach or detach the event listener.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.attachKeyDownEventListener_ = function(attach) {
    +  var handler = this.getHandler();
    +  var method = attach ? handler.listen : handler.unlisten;
    +
    +  // Handle keydown events dispatched by the button.
    +  method.call(handler, this.getElement(), goog.events.EventType.KEYDOWN,
    +      this.handleKeyDownEvent_);
    +};
    +
    +
    +/**
    + * Handles {@code HIGHLIGHT} events dispatched by the attached menu.
    + * @param {goog.events.Event} e Highlight event to handle.
    + */
    +goog.ui.MenuButton.prototype.handleHighlightItem = function(e) {
    +  var targetEl = e.target.getElement();
    +  if (targetEl) {
    +    this.setAriaActiveDescendant_(targetEl);
    +  }
    +};
    +
    +
    +/**
    + * Handles {@code KEYDOWN} events dispatched by the button element. When the
    + * button is focusable and the menu is present and visible, prevents the event
    + * from propagating since the desired behavior is only to close the menu.
    + * @param {goog.events.Event} e KeyDown event to handle.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.handleKeyDownEvent_ = function(e) {
    +  if (this.isSupportedState(goog.ui.Component.State.FOCUSED) &&
    +      this.getKeyEventTarget() && this.menu_ && this.menu_.isVisible()) {
    +    e.stopPropagation();
    +  }
    +};
    +
    +
    +/**
    + * Handles UNHIGHLIGHT events dispatched by the associated menu.
    + * @param {goog.events.Event} e Unhighlight event to handle.
    + */
    +goog.ui.MenuButton.prototype.handleUnHighlightItem = function(e) {
    +  if (!this.menu_.getHighlighted()) {
    +    var element = this.getElement();
    +    goog.asserts.assert(element, 'The menu button DOM element cannot be null.');
    +    goog.a11y.aria.setState(element,
    +        goog.a11y.aria.State.ACTIVEDESCENDANT, '');
    +    goog.a11y.aria.setState(element,
    +        goog.a11y.aria.State.OWNS, '');
    +  }
    +};
    +
    +
    +/**
    + * Handles {@code CLOSE} events dispatched by the associated menu.
    + * @param {goog.events.Event} e Close event to handle.
    + */
    +goog.ui.MenuButton.prototype.handleCloseItem = function(e) {
    +  // When a submenu is closed by pressing left arrow, no highlight event is
    +  // dispatched because the newly focused item was already highlighted, so this
    +  // scenario is handled by listening for the submenu close event instead.
    +  if (this.isOpen() && e.target instanceof goog.ui.MenuItem) {
    +    var menuItem = /** @type {!goog.ui.MenuItem} */ (e.target);
    +    var menuItemEl = menuItem.getElement();
    +    if (menuItem.isVisible() && menuItem.isHighlighted() &&
    +        menuItemEl != null) {
    +      this.setAriaActiveDescendant_(menuItemEl);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Updates the aria-activedescendant attribute to the given target element.
    + * @param {!Element} targetEl The target element.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.setAriaActiveDescendant_ = function(targetEl) {
    +  var element = this.getElement();
    +  goog.asserts.assert(element, 'The menu button DOM element cannot be null.');
    +
    +  // If target element has an activedescendant, then set this control's
    +  // activedescendant to that, otherwise set it to the target element. This is
    +  // a workaround for some screen readers which do not handle
    +  // aria-activedescendant redirection properly.
    +  var targetActiveDescendant = goog.a11y.aria.getActiveDescendant(targetEl);
    +  var activeDescendant = targetActiveDescendant || targetEl;
    +
    +  if (!activeDescendant.id) {
    +    // Create an id if there isn't one already.
    +    var idGenerator = goog.ui.IdGenerator.getInstance();
    +    activeDescendant.id = idGenerator.getNextUniqueId();
    +  }
    +
    +  goog.a11y.aria.setActiveDescendant(element, activeDescendant);
    +  goog.a11y.aria.setState(
    +      element, goog.a11y.aria.State.OWNS, activeDescendant.id);
    +};
    +
    +
    +/**
    + * Attaches or detaches event listeners depending on whether the popup menu
    + * is being shown or hidden.  Starts listening for document mousedown events
    + * and for menu blur events when the menu is shown, and stops listening for
    + * these events when it is hidden.  Called from {@link #setOpen}.
    + * @param {boolean} attach Whether to attach or detach event listeners.
    + * @private
    + */
    +goog.ui.MenuButton.prototype.attachPopupListeners_ = function(attach) {
    +  var handler = this.getHandler();
    +  var method = attach ? handler.listen : handler.unlisten;
    +
    +  // Listen for document mousedown events in the capture phase, because
    +  // the target may stop propagation of the event in the bubble phase.
    +  method.call(handler, this.getDomHelper().getDocument(),
    +      goog.events.EventType.MOUSEDOWN, this.handleDocumentMouseDown, true);
    +
    +  // Only listen for blur events dispatched by the menu if it is focusable.
    +  if (this.isFocusablePopupMenu()) {
    +    method.call(handler, /** @type {!goog.events.EventTarget} */ (this.menu_),
    +        goog.ui.Component.EventType.BLUR, this.handleMenuBlur);
    +  }
    +
    +  method.call(handler, this.timer_, goog.Timer.TICK, this.onTick_);
    +  if (attach) {
    +    this.timer_.start();
    +  } else {
    +    this.timer_.stop();
    +  }
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.MenuButtons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.MenuButtonRenderer.CSS_CLASS,
    +    function() {
    +      // MenuButton defaults to using MenuButtonRenderer.
    +      return new goog.ui.MenuButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.html b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.html
    new file mode 100644
    index 0000000000..60858ebb65
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.html
    @@ -0,0 +1,60 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.MenuButton
    +  </title>
    +  <style type="text/css">
    +   .goog-menu {
    +  position: absolute;
    +  color: #aaa;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuButtonTest');
    +  </script>
    + </head>
    + <body>
    +  <iframe id="iframe1" src="menubutton_test_frame.html" width="400" height="400">
    +  </iframe>
    +  <div id="positionElement" style="position: absolute; left: 205px">
    +  </div>
    +  <p>
    +   Here's a menubutton defined in markup:
    +  </p>
    +  <div id="siblingTest">
    +  </div>
    +  <div id="demoMenuButton" class="goog-menu-button">
    +   <div id="demoMenu" class="goog-menu">
    +    <div id="menuItem1" class="goog-menuitem">
    +     Annual Report.pdf
    +    </div>
    +    <div id="menuItem2" class="goog-menuitem">
    +     Quarterly Update.pdf
    +    </div>
    +    <div id="menuItem3" class="goog-menuitem">
    +     Enemies List.txt
    +    </div>
    +   </div>
    +  </div>
    +  <div id="button1" class="goog-menu-button">
    +  </div>
    +  <div id="button2" class="goog-menu-button">
    +  </div>
    +  <div id="footer">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.js b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.js
    new file mode 100644
    index 0000000000..24be05541e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test.js
    @@ -0,0 +1,843 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuButtonTest');
    +goog.setTestOnly('goog.ui.MenuButtonTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.MenuAnchoredPosition');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.SubMenu');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +
    +var menuButton;
    +var clonedMenuButtonDom;
    +var expectedFailures;
    +
    +function setUpPage() {
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +// Mock out goog.positioning.positionAtCoordinate to always ignore failure when
    +// the window is too small, since we don't care about the viewport size on
    +// the selenium farm.
    +// TODO(nicksantos): Move this into a common location if we ever have enough
    +// code for a general goog.testing.ui library.
    +var originalPositionAtCoordinate = goog.positioning.positionAtCoordinate;
    +goog.positioning.positionAtCoordinate = function(absolutePos, movableElement,
    +    movableElementCorner, opt_margin, opt_viewport, opt_overflow,
    +    opt_preferredSize) {
    +  return originalPositionAtCoordinate.call(this, absolutePos, movableElement,
    +      movableElementCorner, opt_margin, opt_viewport,
    +      goog.positioning.Overflow.IGNORE, opt_preferredSize);
    +};
    +
    +function MyFakeEvent(keyCode, opt_eventType) {
    +  this.type = opt_eventType || goog.events.KeyHandler.EventType.KEY;
    +  this.keyCode = keyCode;
    +  this.propagationStopped = false;
    +  this.preventDefault = goog.nullFunction;
    +  this.stopPropagation = function() {
    +    this.propagationStopped = true;
    +  };
    +}
    +
    +function setUp() {
    +  window.scrollTo(0, 0);
    +
    +  var viewportSize = goog.dom.getViewportSize();
    +  // Some tests need enough size viewport.
    +  if (viewportSize.width < 600 || viewportSize.height < 600) {
    +    window.moveTo(0, 0);
    +    window.resizeTo(640, 640);
    +  }
    +
    +  clonedMenuButtonDom = goog.dom.getElement('demoMenuButton').cloneNode(true);
    +
    +  menuButton = new goog.ui.MenuButton();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  menuButton.dispose();
    +
    +  var element = goog.dom.getElement('demoMenuButton');
    +  element.parentNode.replaceChild(clonedMenuButtonDom, element);
    +}
    +
    +
    +/**
    + * Check if the aria-haspopup property is set correctly.
    + */
    +function checkHasPopUp() {
    +  menuButton.enterDocument();
    +  assertFalse('Menu button must have aria-haspopup attribute set to false',
    +      goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.HASPOPUP));
    +  var menu = new goog.ui.Menu();
    +  menu.createDom();
    +  menuButton.setMenu(menu);
    +  assertTrue('Menu button must have aria-haspopup attribute set to true',
    +      goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.HASPOPUP));
    +  menuButton.setMenu(null);
    +  assertFale('Menu button must have aria-haspopup attribute set to false',
    +      goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.HASPOPUP));
    +}
    +
    +
    +/**
    + * Open the menu and click on the menu item inside.
    + * Check if the aria-haspopup property is set correctly.
    + */
    +function testBasicButtonBehavior() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  assertEquals('Menu button must have aria-haspopup attribute set to true',
    +      'true', goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.HASPOPUP));
    +
    +  goog.testing.events.fireClickSequence(node);
    +
    +  assertTrue('Menu must open after click', menuButton.isOpen());
    +
    +  var menuItemClicked = 0;
    +  var lastMenuItemClicked = null;
    +  goog.events.listen(menuButton.getMenu(),
    +      goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        menuItemClicked++;
    +        lastMenuItemClicked = e.target;
    +      });
    +
    +  var menuItem2 = goog.dom.getElement('menuItem2');
    +  goog.testing.events.fireClickSequence(menuItem2);
    +  assertFalse('Menu must close on clicking when open', menuButton.isOpen());
    +  assertEquals('Number of menu items clicked should be 1', 1, menuItemClicked);
    +  assertEquals('menuItem2 should be the last menuitem clicked', menuItem2,
    +      lastMenuItemClicked.getElement());
    +}
    +
    +
    +/**
    + * Open the menu, highlight first menuitem and then the second.
    + * Check if the aria-activedescendant property is set correctly.
    + */
    +function testHighlightItemBehavior() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  goog.testing.events.fireClickSequence(node);
    +
    +  assertTrue('Menu must open after click', menuButton.isOpen());
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN));
    +  assertNotNull(menuButton.getElement());
    +  assertEquals('First menuitem must be the aria-activedescendant',
    +      'menuItem1', goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.ACTIVEDESCENDANT));
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN));
    +  assertEquals('Second menuitem must be the aria-activedescendant',
    +      'menuItem2', goog.a11y.aria.getState(menuButton.getElement(),
    +      goog.a11y.aria.State.ACTIVEDESCENDANT));
    +}
    +
    +
    +/**
    + * Check that the appropriate items are selected when menus are opened with the
    + * keyboard and setSelectFirstOnEnterOrSpace is not set.
    + */
    +function testHighlightFirstOnOpen() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertEquals(
    +      'By default no items should be highlighted when opened with enter.',
    +      null, menuButton.getMenu().getHighlighted());
    +
    +  menuButton.setOpen(false);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN));
    +  assertTrue('Menu must open after down key', menuButton.isOpen());
    +  assertEquals('First menuitem must be highlighted',
    +      'menuItem1', menuButton.getMenu().getHighlighted().getElement().id);
    +}
    +
    +
    +/**
    + * Check that the appropriate items are selected when menus are opened with the
    + * keyboard, setSelectFirstOnEnterOrSpace is not set, and the first menu item is
    + * disabled.
    + */
    +function testHighlightFirstOnOpen_withFirstDisabled() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menu = menuButton.getMenu();
    +  menu.getItemAt(0).setEnabled(false);
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertEquals(
    +      'By default no items should be highlighted when opened with enter.',
    +      null, menuButton.getMenu().getHighlighted());
    +
    +  menuButton.setOpen(false);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN));
    +  assertTrue('Menu must open after down key', menuButton.isOpen());
    +  assertEquals('First enabled menuitem must be highlighted',
    +      'menuItem2', menuButton.getMenu().getHighlighted().getElement().id);
    +}
    +
    +
    +/**
    + * Check that the appropriate items are selected when menus are opened with the
    + * keyboard and setSelectFirstOnEnterOrSpace is set.
    + */
    +function testHighlightFirstOnOpen_withEnterOrSpaceSet() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  menuButton.setSelectFirstOnEnterOrSpace(true);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertEquals('The first item should be highlighted when opened with enter ' +
    +      'after setting selectFirstOnEnterOrSpace',
    +      'menuItem1', menuButton.getMenu().getHighlighted().getElement().id);
    +}
    +
    +
    +/**
    + * Check that the appropriate item is selected when a menu is opened with the
    + * keyboard, setSelectFirstOnEnterOrSpace is true, and the first menu item is
    + * disabled.
    + */
    +function testHighlightFirstOnOpen_withEnterOrSpaceSetAndFirstDisabled() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  menuButton.setSelectFirstOnEnterOrSpace(true);
    +  var menu = menuButton.getMenu();
    +  menu.getItemAt(0).setEnabled(false);
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertEquals('The first enabled item should be highlighted when opened ' +
    +      'with enter after setting selectFirstOnEnterOrSpace',
    +      'menuItem2', menuButton.getMenu().getHighlighted().getElement().id);
    +}
    +
    +
    +/**
    + * Open the menu, enter a submenu and then back out of it.
    + * Check if the aria-activedescendant property is set correctly.
    + */
    +function testCloseSubMenuBehavior() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menu = menuButton.getMenu();
    +  var subMenu = new goog.ui.SubMenu('Submenu');
    +  menu.addItem(subMenu);
    +  subMenu.getElement().id = 'subMenu';
    +  var subMenuMenu = new goog.ui.Menu();
    +  subMenu.setMenu(subMenuMenu);
    +  var subMenuItem = new goog.ui.MenuItem('Submenu item 1');
    +  subMenuMenu.addItem(subMenuItem);
    +  subMenuItem.getElement().id = 'subMenuItem1';
    +  menuButton.setOpen(true);
    +
    +  for (var i = 0; i < 4; i++) {
    +    menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.DOWN));
    +  }
    +  assertEquals('Submenu must be the aria-activedescendant',
    +      'subMenu', goog.a11y.aria.getState(menuButton.getElement(),
    +          goog.a11y.aria.State.ACTIVEDESCENDANT));
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.RIGHT));
    +  assertEquals('Submenu item 1 must be the aria-activedescendant',
    +      'subMenuItem1', goog.a11y.aria.getState(menuButton.getElement(),
    +          goog.a11y.aria.State.ACTIVEDESCENDANT));
    +
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.LEFT));
    +  assertEquals('Submenu must be the aria-activedescendant',
    +      'subMenu', goog.a11y.aria.getState(menuButton.getElement(),
    +          goog.a11y.aria.State.ACTIVEDESCENDANT));
    +}
    +
    +
    +/**
    + * Make sure the menu opens when enter is pressed.
    + */
    +function testEnterOpensMenu() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertTrue('Menu must open after enter', menuButton.isOpen());
    +}
    +
    +
    +/**
    + * Tests the behavior of the enter and space keys when the menu is open.
    + */
    +function testSpaceOrEnterClosesMenu() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  menuButton.setOpen(true);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.ENTER));
    +  assertFalse('Menu should close after pressing Enter', menuButton.isOpen());
    +
    +  menuButton.setOpen(true);
    +  menuButton.handleKeyEvent(new MyFakeEvent(goog.events.KeyCodes.SPACE,
    +      goog.events.EventType.KEYUP));
    +  assertFalse('Menu should close after pressing Space', menuButton.isOpen());
    +}
    +
    +
    +/**
    + * Tests that a keydown event of the escape key propagates normally when the
    + * menu is closed.
    + */
    +function testStopEscapePropagationMenuClosed() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  var fakeEvent = new MyFakeEvent(
    +      goog.events.KeyCodes.ESCAPE, goog.events.EventType.KEYDOWN);
    +  menuButton.decorate(node);
    +  menuButton.setOpen(false);
    +
    +  menuButton.handleKeyDownEvent_(fakeEvent);
    +  assertFalse('Event propagation was erroneously stopped.',
    +      fakeEvent.propagationStopped);
    +}
    +
    +
    +/**
    + * Tests that a keydown event of the escape key is prevented from propagating
    + * when the menu is open.
    + */
    +function testStopEscapePropagationMenuOpen() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  var fakeEvent = new MyFakeEvent(
    +      goog.events.KeyCodes.ESCAPE, goog.events.EventType.KEYDOWN);
    +  menuButton.decorate(node);
    +  menuButton.setOpen(true);
    +
    +  menuButton.handleKeyDownEvent_(fakeEvent);
    +  assertTrue(
    +      'Event propagation was not stopped.', fakeEvent.propagationStopped);
    +}
    +
    +
    +/**
    + * Open the menu and click on the menu item inside after exiting and entering
    + * the document once, to test proper setup/teardown behavior of MenuButton.
    + */
    +function testButtonAfterEnterDocument() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  menuButton.exitDocument();
    +  menuButton.enterDocument();
    +
    +  goog.testing.events.fireClickSequence(node);
    +  assertTrue('Menu must open after click', menuButton.isOpen());
    +
    +  var menuItem2 = goog.dom.getElement('menuItem2');
    +  goog.testing.events.fireClickSequence(menuItem2);
    +  assertFalse('Menu must close on clicking when open', menuButton.isOpen());
    +}
    +
    +
    +/**
    + * Renders the menu button, moves its menu and then repositions to make sure the
    + * position is more or less ok.
    + */
    +function testPositionMenu() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menu = menuButton.getMenu();
    +  menu.setVisible(true, true);
    +
    +  // Move to 500, 500
    +  menu.setPosition(500, 500);
    +
    +  // Now reposition and make sure position is more or less ok.
    +  menuButton.positionMenu();
    +  var menuNode = goog.dom.getElement('demoMenu');
    +  assertRoughlyEquals(menuNode.offsetTop, node.offsetTop + node.offsetHeight,
    +      20);
    +  assertRoughlyEquals(menuNode.offsetLeft, node.offsetLeft, 20);
    +}
    +
    +
    +/**
    + * Tests that calling positionMenu when the menu is not in the document does not
    + * throw an exception.
    + */
    +function testPositionMenuNotInDocument() {
    +  var menu = new goog.ui.Menu();
    +  menu.createDom();
    +  menuButton.setMenu(menu);
    +  menuButton.positionMenu();
    +}
    +
    +
    +/**
    + * Shows the menu and moves the menu button, a timer correct the menu position.
    + */
    +function testOpenedMenuPositionCorrection() {
    +  var iframe = goog.dom.getElement('iframe1');
    +  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
    +  var iframeDom = goog.dom.getDomHelper(iframeDoc);
    +  var iframeWindow = goog.dom.getWindow(iframeDoc);
    +
    +  var button = new goog.ui.MenuButton();
    +  iframeWindow.scrollTo(0, 0);
    +  var node = iframeDom.getElement('demoMenuButton');
    +  button.decorate(node);
    +  var mockTimer = new goog.Timer();
    +  // Don't start the timer.  We manually dispatch the Tick event.
    +  mockTimer.start = goog.nullFunction;
    +  button.timer_ = mockTimer;
    +
    +  var replacer = new goog.testing.PropertyReplacer();
    +  var positionMenuCalled;
    +  var origPositionMenu = goog.bind(button.positionMenu, button);
    +  replacer.set(button, 'positionMenu', function() {
    +    positionMenuCalled = true;
    +    origPositionMenu();
    +  });
    +
    +  // Show the menu.
    +  button.setOpen(true);
    +
    +  // Confirm the menu position
    +  var menuNode = iframeDom.getElement('demoMenu');
    +  assertRoughlyEquals(menuNode.offsetTop, node.offsetTop + node.offsetHeight,
    +      20);
    +  assertRoughlyEquals(menuNode.offsetLeft, node.offsetLeft, 20);
    +
    +  positionMenuCalled = false;
    +  // A Tick event is dispatched.
    +  mockTimer.dispatchEvent(goog.Timer.TICK);
    +  assertFalse('positionMenu() shouldn\'t be called.', positionMenuCalled);
    +
    +  // Move the menu button by DOM structure change
    +  var p1 = iframeDom.createDom('p', null, iframeDom.createTextNode('foo'));
    +  var p2 = iframeDom.createDom('p', null, iframeDom.createTextNode('foo'));
    +  var p3 = iframeDom.createDom('p', null, iframeDom.createTextNode('foo'));
    +  iframeDom.insertSiblingBefore(p1, node);
    +  iframeDom.insertSiblingBefore(p2, node);
    +  iframeDom.insertSiblingBefore(p3, node);
    +
    +  // Confirm the menu is detached from the button.
    +  assertTrue(Math.abs(node.offsetTop + node.offsetHeight -
    +      menuNode.offsetTop) > 20);
    +
    +  positionMenuCalled = false;
    +  // A Tick event is dispatched.
    +  mockTimer.dispatchEvent(goog.Timer.TICK);
    +  assertTrue('positionMenu() should be called.', positionMenuCalled);
    +
    +  // The menu is moved to appropriate position again.
    +  assertRoughlyEquals(menuNode.offsetTop, node.offsetTop + node.offsetHeight,
    +      20);
    +
    +  // Make the frame page scrollable.
    +  var viewportHeight = iframeDom.getViewportSize().height;
    +  var footer = iframeDom.getElement('footer');
    +  goog.style.setSize(footer, 1, viewportHeight * 2);
    +  // Change the viewport offset.
    +  iframeWindow.scrollTo(0, viewportHeight);
    +  // A Tick event is dispatched and positionMenu() should be called.
    +  positionMenuCalled = false;
    +  mockTimer.dispatchEvent(goog.Timer.TICK);
    +  assertTrue('positionMenu() should be called.', positionMenuCalled);
    +  goog.style.setSize(footer, 1, 1);
    +
    +  // Tear down.
    +  iframeDom.removeNode(p1);
    +  iframeDom.removeNode(p2);
    +  iframeDom.removeNode(p3);
    +  replacer.reset();
    +  button.dispose();
    +}
    +
    +
    +/**
    + * Use a different button to position the menu and make sure it does so
    + * correctly.
    + */
    +function testAlternatePositioningElement() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  var posElement = goog.dom.getElement('positionElement');
    +  menuButton.setPositionElement(posElement);
    +
    +  // Show the menu.
    +  menuButton.setOpen(true);
    +
    +  // Confirm the menu position
    +  var menuNode = menuButton.getMenu().getElement();
    +  assertRoughlyEquals(menuNode.offsetTop, posElement.offsetTop +
    +      posElement.offsetHeight, 20);
    +  assertRoughlyEquals(menuNode.offsetLeft, posElement.offsetLeft, 20);
    +}
    +
    +
    +/**
    + * Test forced positioning above the button.
    + */
    +function testPositioningAboveAnchor() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  // Show the menu.
    +  menuButton.setAlignMenuToStart(true);  // Should get overridden below
    +  menuButton.setScrollOnOverflow(true);  // Should get overridden below
    +
    +  var position = new goog.positioning.MenuAnchoredPosition(
    +      menuButton.getElement(),
    +      goog.positioning.Corner.TOP_START,
    +      /* opt_adjust */ false, /* opt_resize */ false);
    +  menuButton.setMenuPosition(position);
    +  menuButton.setOpen(true);
    +
    +  // Confirm the menu position
    +  var buttonBounds = goog.style.getBounds(node);
    +  var menuNode = menuButton.getMenu().getElement();
    +  var menuBounds = goog.style.getBounds(menuNode);
    +
    +  assertRoughlyEquals(menuBounds.top + menuBounds.height,
    +      buttonBounds.top, 3);
    +  assertRoughlyEquals(menuBounds.left, buttonBounds.left, 3);
    +  // For this test to be valid, the node must have non-trival height.
    +  assertRoughlyEquals(node.offsetHeight, 19, 3);
    +}
    +
    +
    +/**
    + * Test forced positioning below the button.
    + */
    +function testPositioningBelowAnchor() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  // Show the menu.
    +  menuButton.setAlignMenuToStart(true);  // Should get overridden below
    +  menuButton.setScrollOnOverflow(true);  // Should get overridden below
    +
    +  var position = new goog.positioning.MenuAnchoredPosition(
    +      menuButton.getElement(),
    +      goog.positioning.Corner.BOTTOM_START,
    +      /* opt_adjust */ false, /* opt_resize */ false);
    +  menuButton.setMenuPosition(position);
    +  menuButton.setOpen(true);
    +
    +  // Confirm the menu position
    +  var buttonBounds = goog.style.getBounds(node);
    +  var menuNode = menuButton.getMenu().getElement();
    +  var menuBounds = goog.style.getBounds(menuNode);
    +
    +  expectedFailures.expectFailureFor(isWinSafariBefore5());
    +  try {
    +    assertRoughlyEquals(menuBounds.top,
    +        buttonBounds.top + buttonBounds.height, 3);
    +    assertRoughlyEquals(menuBounds.left, buttonBounds.left, 3);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  // For this test to be valid, the node must have non-trival height.
    +  assertRoughlyEquals(node.offsetHeight, 19, 3);
    +}
    +
    +function isWinSafariBefore5() {
    +  return goog.userAgent.WINDOWS && goog.userAgent.product.SAFARI &&
    +      goog.userAgent.product.isVersion(4) &&
    +      !goog.userAgent.product.isVersion(5);
    +}
    +
    +
    +/**
    + * Tests that space, and only space, fire on key up.
    + */
    +function testSpaceFireOnKeyUp() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +
    +  e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, menuButton);
    +  e.preventDefault = goog.testing.recordFunction();
    +  e.keyCode = goog.events.KeyCodes.SPACE;
    +  menuButton.handleKeyEvent(e);
    +  assertFalse('Menu must not have been triggered by Space keypress',
    +      menuButton.isOpen());
    +  assertNotNull('Page scrolling is prevented', e.preventDefault.getLastCall());
    +
    +  e = new goog.events.Event(goog.events.EventType.KEYUP, menuButton);
    +  e.keyCode = goog.events.KeyCodes.SPACE;
    +  menuButton.handleKeyEvent(e);
    +  assertTrue('Menu must have been triggered by Space keyup',
    +      menuButton.isOpen());
    +  menuButton.getMenu().setHighlightedIndex(0);
    +  e = new goog.events.Event(goog.events.KeyHandler.EventType.KEY, menuButton);
    +  e.keyCode = goog.events.KeyCodes.DOWN;
    +  menuButton.handleKeyEvent(e);
    +  assertEquals('Highlighted menu item must have hanged by Down keypress',
    +      1,
    +      menuButton.getMenu().getHighlightedIndex());
    +
    +  menuButton.getMenu().setHighlightedIndex(0);
    +  e = new goog.events.Event(goog.events.EventType.KEYUP, menuButton);
    +  e.keyCode = goog.events.KeyCodes.DOWN;
    +  menuButton.handleKeyEvent(e);
    +  assertEquals('Highlighted menu item must not have changed by Down keyup',
    +      0,
    +      menuButton.getMenu().getHighlightedIndex());
    +}
    +
    +
    +/**
    + * Tests that preventing the button from closing also prevents the menu from
    + * being hidden.
    + */
    +function testPreventHide() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  menuButton.setDispatchTransitionEvents(goog.ui.Component.State.OPENED, true);
    +
    +  // Show the menu.
    +  menuButton.setOpen(true);
    +  assertTrue('Menu button should be open.', menuButton.isOpen());
    +  assertTrue('Menu should be visible.', menuButton.getMenu().isVisible());
    +
    +  var key = goog.events.listen(menuButton,
    +                               goog.ui.Component.EventType.CLOSE,
    +                               function(event) { event.preventDefault(); });
    +
    +  // Try to hide the menu.
    +  menuButton.setOpen(false);
    +  assertTrue('Menu button should still be open.', menuButton.isOpen());
    +  assertTrue('Menu should still be visible.', menuButton.getMenu().isVisible());
    +
    +  // Remove listener and try again.
    +  goog.events.unlistenByKey(key);
    +  menuButton.setOpen(false);
    +  assertFalse('Menu button should not be open.', menuButton.isOpen());
    +  assertFalse('Menu should not be visible.', menuButton.getMenu().isVisible());
    +}
    +
    +
    +/**
    + * Tests that opening and closing the menu does not affect how adding or
    + * removing menu items changes the size of the menu.
    + */
    +function testResizeOnItemAddOrRemove() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menu = menuButton.getMenu();
    +
    +  // Show the menu.
    +  menuButton.setOpen(true);
    +  var originalSize = goog.style.getSize(menu.getElement());
    +
    +  // Check that removing an item while the menu is left open correctly changes
    +  // the size of the menu.
    +  // Remove an item using a method on Menu.
    +  var item = menu.removeChildAt(0, true);
    +  // Confirm size of menu changed.
    +  var afterRemoveSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must decrease after removing a menu item.',
    +      afterRemoveSize.height < originalSize.height);
    +
    +  // Check that removing an item while the menu is closed, then opened
    +  // (so that reposition is called) correctly changes the size of the menu.
    +  // Hide menu.
    +  menuButton.setOpen(false);
    +  var item2 = menu.removeChildAt(0, true);
    +  menuButton.setOpen(true);
    +  // Confirm size of menu changed.
    +  var afterRemoveAgainSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must decrease after removing a second menu item.',
    +      afterRemoveAgainSize.height < afterRemoveSize.height);
    +
    +  // Check that adding an item while the menu is opened, then closed, then
    +  // opened, correctly changes the size of the menu.
    +  // Add an item, this time using a MenuButton method.
    +  menuButton.setOpen(true);
    +  menuButton.addItem(item2);
    +  menuButton.setOpen(false);
    +  menuButton.setOpen(true);
    +  // Confirm size of menu changed.
    +  var afterAddSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must increase after adding a menu item.',
    +      afterRemoveAgainSize.height < afterAddSize.height);
    +  assertEquals(
    +      'Removing and adding back items must not change the height of a menu.',
    +      afterRemoveSize.height, afterAddSize.height);
    +
    +  // Add back the last item to keep state consistent.
    +  menuButton.addItem(item);
    +}
    +
    +
    +/**
    + * Tests that adding and removing items from a menu with scrollOnOverflow is on
    + * correctly resizes the menu.
    + */
    +function testResizeOnItemAddOrRemoveWithScrollOnOverflow() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menu = menuButton.getMenu();
    +
    +  // Show the menu.
    +  menuButton.setScrollOnOverflow(true);
    +  menuButton.setOpen(true);
    +  var originalSize = goog.style.getSize(menu.getElement());
    +
    +  // Check that removing an item while the menu is left open correctly changes
    +  // the size of the menu.
    +  // Remove an item using a method on Menu.
    +  var item = menu.removeChildAt(0, true);
    +  menuButton.invalidateMenuSize();
    +  menuButton.positionMenu();
    +
    +  // Confirm size of menu changed.
    +  var afterRemoveSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must decrease after removing a menu item.',
    +      afterRemoveSize.height < originalSize.height);
    +
    +  var item2 = menu.removeChildAt(0, true);
    +  menuButton.invalidateMenuSize();
    +  menuButton.positionMenu();
    +
    +  // Confirm size of menu changed.
    +  var afterRemoveAgainSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must decrease after removing a second menu item.',
    +      afterRemoveAgainSize.height < afterRemoveSize.height);
    +
    +  // Check that adding an item while the menu is opened correctly changes the
    +  // size of the menu.
    +  menuButton.addItem(item2);
    +  menuButton.invalidateMenuSize();
    +  menuButton.positionMenu();
    +
    +  // Confirm size of menu changed.
    +  var afterAddSize = goog.style.getSize(menu.getElement());
    +  assertTrue('Height of menu must increase after adding a menu item.',
    +      afterRemoveAgainSize.height < afterAddSize.height);
    +  assertEquals(
    +      'Removing and adding back items must not change the height of a menu.',
    +      afterRemoveSize.height, afterAddSize.height);
    +}
    +
    +
    +/**
    + * Try rendering the menu as a sibling rather than as a child of the dom. This
    + * tests the case when the button is rendered, rather than decorated.
    + */
    +function testRenderMenuAsSibling() {
    +  menuButton.setRenderMenuAsSibling(true);
    +  menuButton.addItem(new goog.ui.MenuItem('Menu item 1'));
    +  menuButton.addItem(new goog.ui.MenuItem('Menu item 2'));
    +  // By default the menu is rendered into the top level dom and the button
    +  // is rendered into whatever parent we provide.  If we don't provide a
    +  // parent then we aren't really testing anything, since both would be, by
    +  // default, rendered into the top level dom, and therefore siblings.
    +  menuButton.render(goog.dom.getElement('siblingTest'));
    +  menuButton.setOpen(true);
    +  assertEquals(
    +      menuButton.getElement().parentNode,
    +      menuButton.getMenu().getElement().parentNode);
    +}
    +
    +
    +/**
    + * Check that we render the menu as a sibling of the menu button, immediately
    + * after the menu button.
    + */
    +function testRenderMenuAsSiblingForDecoratedButton() {
    +  var menu = new goog.ui.Menu();
    +  menu.addChild(new goog.ui.MenuItem('Menu item 1'), true /* render */);
    +  menu.addChild(new goog.ui.MenuItem('Menu item 2'), true /* render */);
    +  menu.addChild(new goog.ui.MenuItem('Menu item 3'), true /* render */);
    +
    +  var menuButton = new goog.ui.MenuButton();
    +  menuButton.setMenu(menu);
    +  menuButton.setRenderMenuAsSibling(true);
    +  var node = goog.dom.getElement('button1');
    +  menuButton.decorate(node);
    +
    +  menuButton.setOpen(true);
    +
    +  assertEquals('The menu should be rendered immediately after the menu button',
    +      goog.dom.getNextElementSibling(menuButton.getElement()),
    +      menu.getElement());
    +
    +  assertEquals('The menu should be rendered immediately before the next button',
    +      goog.dom.getNextElementSibling(menu.getElement()),
    +      goog.dom.getElement('button2'));
    +}
    +
    +function testAlignToStartSetter() {
    +  assertTrue(menuButton.isAlignMenuToStart());
    +
    +  menuButton.setAlignMenuToStart(false);
    +  assertFalse(menuButton.isAlignMenuToStart());
    +
    +  menuButton.setAlignMenuToStart(true);
    +  assertTrue(menuButton.isAlignMenuToStart());
    +}
    +
    +function testScrollOnOverflowSetter() {
    +  assertFalse(menuButton.isScrollOnOverflow());
    +
    +  menuButton.setScrollOnOverflow(true);
    +  assertTrue(menuButton.isScrollOnOverflow());
    +
    +  menuButton.setScrollOnOverflow(false);
    +  assertFalse(menuButton.isScrollOnOverflow());
    +}
    +
    +
    +/**
    + * Tests that the attached menu has been set to aria-hidden=false explicitly
    + * when the menu is opened.
    + */
    +function testSetOpenUnsetsAriaHidden() {
    +  var node = goog.dom.getElement('demoMenuButton');
    +  menuButton.decorate(node);
    +  var menuElem = menuButton.getMenu().getElementStrict();
    +  goog.a11y.aria.setState(menuElem, goog.a11y.aria.State.HIDDEN, true);
    +  menuButton.setOpen(true);
    +  assertEquals(
    +      '', goog.a11y.aria.getState(menuElem, goog.a11y.aria.State.HIDDEN));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubutton_test_frame.html b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test_frame.html
    new file mode 100644
    index 0000000000..b918890d3e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubutton_test_frame.html
    @@ -0,0 +1,41 @@
    +<!DOCTYPE html>
    +<!--
    +
    +  @author tkent@google.com (TAMURA Kent)
    +-->
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<style type='text/css'>
    +#demoMenuButton {
    +  /*
    +   * Set a fixed width because the button size can be changed by a scroll bar
    +   * without it.
    +   */
    +  width: 64px;
    +}
    +.goog-menu {
    +  position: absolute;
    +  color: #aaa;
    +}
    +</style>
    +</head>
    +<body>
    +<div id="demoMenuButton" class="goog-menu-button">
    +  Button
    +  <div id="demoMenu" class="goog-menu">
    +    <div id='menuItem1' class="goog-menuitem">Annual Report.pdf</div>
    +    <div id='menuItem2' class="goog-menuitem">Quarterly Update.pdf</div>
    +    <div id='menuItem3' class="goog-menuitem">Enemies List.txt</div>
    +  </div>
    +</div>
    +
    +<div id="footer"></div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer.js
    new file mode 100644
    index 0000000000..b956097404
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer.js
    @@ -0,0 +1,191 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.MenuButton}s and subclasses.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.MenuButtonRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuRenderer');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.MenuButton}s.  This implementation overrides
    + * {@link goog.ui.CustomButtonRenderer#createButton} to create a separate
    + * caption and dropdown element.
    + * @constructor
    + * @extends {goog.ui.CustomButtonRenderer}
    + */
    +goog.ui.MenuButtonRenderer = function() {
    +  goog.ui.CustomButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.MenuButtonRenderer, goog.ui.CustomButtonRenderer);
    +goog.addSingletonGetter(goog.ui.MenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.MenuButtonRenderer.CSS_CLASS = goog.getCssName('goog-menu-button');
    +
    +
    +/**
    + * Takes the button's root element and returns the parent element of the
    + * button's contents.  Overrides the superclass implementation by taking
    + * the nested DIV structure of menu buttons into account.
    + * @param {Element} element Root element of the button whose content element
    + *     is to be returned.
    + * @return {Element} The button's content element.
    + * @override
    + */
    +goog.ui.MenuButtonRenderer.prototype.getContentElement = function(element) {
    +  return goog.ui.MenuButtonRenderer.superClass_.getContentElement.call(this,
    +      /** @type {Element} */ (element && element.firstChild));
    +};
    +
    +
    +/**
    + * Takes an element, decorates it with the menu button control, and returns
    + * the element.  Overrides {@link goog.ui.CustomButtonRenderer#decorate} by
    + * looking for a child element that can be decorated by a menu, and if it
    + * finds one, decorates it and attaches it to the menu button.
    + * @param {goog.ui.Control} control goog.ui.MenuButton to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.MenuButtonRenderer.prototype.decorate = function(control, element) {
    +  var button = /** @type {goog.ui.MenuButton} */ (control);
    +  // TODO(attila):  Add more robust support for subclasses of goog.ui.Menu.
    +  var menuElem = goog.dom.getElementsByTagNameAndClass(
    +      '*', goog.ui.MenuRenderer.CSS_CLASS, element)[0];
    +  if (menuElem) {
    +    // Move the menu element directly under the body (but hide it first to
    +    // prevent flicker; see bug 1089244).
    +    goog.style.setElementShown(menuElem, false);
    +    goog.dom.appendChild(goog.dom.getOwnerDocument(menuElem).body, menuElem);
    +
    +    // Decorate the menu and attach it to the button.
    +    var menu = new goog.ui.Menu();
    +    menu.decorate(menuElem);
    +    button.setMenu(menu);
    +  }
    +
    +  // Let the superclass do the rest.
    +  return goog.ui.MenuButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content and
    + * a dropdown arrow element wrapped in a pseudo-rounded-corner box.  Creates
    + * the following DOM structure:
    + *    <div class="goog-inline-block goog-menu-button-outer-box">
    + *      <div class="goog-inline-block goog-menu-button-inner-box">
    + *        <div class="goog-inline-block goog-menu-button-caption">
    + *          Contents...
    + *        </div>
    + *        <div class="goog-inline-block goog-menu-button-dropdown">
    + *          &nbsp;
    + *        </div>
    + *      </div>
    + *    </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure
    + *     to wrap in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.MenuButtonRenderer.prototype.createButton = function(content, dom) {
    +  return goog.ui.MenuButtonRenderer.superClass_.createButton.call(this,
    +      [this.createCaption(content, dom), this.createDropdown(dom)], dom);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns it wrapped in
    + * an appropriately-styled DIV.  Creates the following DOM structure:
    + *    <div class="goog-inline-block goog-menu-button-caption">
    + *      Contents...
    + *    </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure
    + *     to wrap in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Caption element.
    + */
    +goog.ui.MenuButtonRenderer.prototype.createCaption = function(content, dom) {
    +  return goog.ui.MenuButtonRenderer.wrapCaption(
    +      content, this.getCssClass(), dom);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns it wrapped in
    + * an appropriately-styled DIV.  Creates the following DOM structure:
    + *    <div class="goog-inline-block goog-menu-button-caption">
    + *      Contents...
    + *    </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure
    + *     to wrap in a box.
    + * @param {string} cssClass The CSS class for the renderer.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Caption element.
    + */
    +goog.ui.MenuButtonRenderer.wrapCaption = function(content, cssClass, dom) {
    +  return dom.createDom(
    +      'div',
    +      goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +          goog.getCssName(cssClass, 'caption'),
    +      content);
    +};
    +
    +
    +/**
    + * Returns an appropriately-styled DIV containing a dropdown arrow element.
    + * Creates the following DOM structure:
    + *    <div class="goog-inline-block goog-menu-button-dropdown">
    + *      &nbsp;
    + *    </div>
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Dropdown element.
    + */
    +goog.ui.MenuButtonRenderer.prototype.createDropdown = function(dom) {
    +  // 00A0 is &nbsp;
    +  return dom.createDom('div',
    +      goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
    +      goog.getCssName(this.getCssClass(), 'dropdown'), '\u00A0');
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.MenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuButtonRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.html
    new file mode 100644
    index 0000000000..82de5295ea
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.html
    @@ -0,0 +1,35 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for MenuButtonRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <!-- A parent to attach rendered buttons to -->
    +   <div id="parent">
    +   </div>
    +   <!-- A button to decorate -->
    +   <div id="decoratedButton">
    +    Foo
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.js
    new file mode 100644
    index 0000000000..657026d567
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menubuttonrenderer_test.js
    @@ -0,0 +1,168 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuButtonRendererTest');
    +goog.setTestOnly('goog.ui.MenuButtonRendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.userAgent');
    +
    +var decoratedButton;
    +var renderedButton;
    +var savedRootTree;
    +
    +function setUp() {
    +  savedRootTree = goog.dom.getElement('root').cloneNode(true);
    +  decoratedButton = null;
    +  renderedButton = null;
    +}
    +
    +function tearDown() {
    +  if (decoratedButton) {
    +    decoratedButton.dispose();
    +  }
    +
    +  if (renderedButton) {
    +    renderedButton.dispose();
    +  }
    +
    +  var root = goog.dom.getElement('root');
    +  root.parentNode.replaceChild(savedRootTree, root);
    +}
    +
    +function testRendererWithTextContent() {
    +  renderedButton = new goog.ui.MenuButton('Foo');
    +  renderOnParent(renderedButton);
    +  checkButtonCaption(renderedButton);
    +  checkAriaState(renderedButton);
    +
    +  decoratedButton = new goog.ui.MenuButton();
    +  decorateDemoButton(decoratedButton);
    +  checkButtonCaption(decoratedButton);
    +  checkAriaState(decoratedButton);
    +
    +  assertButtonsEqual();
    +}
    +
    +function testRendererWithNodeContent() {
    +  renderedButton = new goog.ui.MenuButton(
    +      goog.dom.createDom('div', null, 'Foo'));
    +  renderOnParent(renderedButton);
    +
    +  var contentEl = renderedButton.getContentElement();
    +  if (goog.userAgent.IE || goog.userAgent.OPERA) {
    +    assertHTMLEquals('<div unselectable="on">Foo</div>', contentEl.innerHTML);
    +  } else {
    +    assertHTMLEquals('<div>Foo</div>', contentEl.innerHTML);
    +  }
    +  assertTrue(hasInlineBlock(contentEl));
    +}
    +
    +function testSetContent() {
    +  renderedButton = new goog.ui.MenuButton();
    +  renderOnParent(renderedButton);
    +
    +  var contentEl = renderedButton.getContentElement();
    +  assertHTMLEquals('', contentEl.innerHTML);
    +
    +  renderedButton.setContent('Foo');
    +  contentEl = renderedButton.getContentElement();
    +  assertHTMLEquals('Foo', contentEl.innerHTML);
    +  assertTrue(hasInlineBlock(contentEl));
    +
    +  renderedButton.setContent(goog.dom.createDom('div', null, 'Bar'));
    +  contentEl = renderedButton.getContentElement();
    +  assertHTMLEquals('<div>Bar</div>', contentEl.innerHTML);
    +
    +  renderedButton.setContent('Foo');
    +  contentEl = renderedButton.getContentElement();
    +  assertHTMLEquals('Foo', contentEl.innerHTML);
    +}
    +
    +function assertButtonsEqual() {
    +  assertHTMLEquals(
    +      'Rendered button and decorated button produced different HTML!',
    +      renderedButton.getElement().innerHTML,
    +      decoratedButton.getElement().innerHTML);
    +}
    +
    +
    +/**
    + * Render the given button as a child of 'parent'.
    + * @param {goog.ui.Button} button A button with content 'Foo'.
    + */
    +function renderOnParent(button) {
    +  button.render(goog.dom.getElement('parent'));
    +}
    +
    +
    +/**
    + * Decaorate the button with id 'button'.
    + * @param {goog.ui.Button} button A button with no content.
    + */
    +function decorateDemoButton(button) {
    +  button.decorate(goog.dom.getElement('decoratedButton'));
    +}
    +
    +
    +/**
    + * Verify that the button's caption is never the direct
    + * child of an inline-block element.
    + * @param {goog.ui.Button} button A button.
    + */
    +function checkButtonCaption(button) {
    +  var contentElement = button.getContentElement();
    +  assertEquals('Foo', contentElement.innerHTML);
    +  assertTrue(hasInlineBlock(contentElement));
    +  assert(hasInlineBlock(contentElement.parentNode));
    +
    +  button.setContent('Bar');
    +  contentElement = button.getContentElement();
    +  assertEquals('Bar', contentElement.innerHTML);
    +  assertTrue(hasInlineBlock(contentElement));
    +  assert(hasInlineBlock(contentElement.parentNode));
    +}
    +
    +
    +/**
    + * Verify that the menu button has the correct ARIA attributes
    + * @param {goog.ui.Button} button A button.
    + */
    +function checkAriaState(button) {
    +  assertEquals(
    +      'menu buttons should have default aria-expanded == false', 'false',
    +      goog.a11y.aria.getState(
    +          button.getElement(), goog.a11y.aria.State.EXPANDED));
    +  button.setOpen(true);
    +  assertEquals('menu buttons should not aria-expanded == true after ' +
    +      'opening', 'true',
    +      goog.a11y.aria.getState(
    +          button.getElement(), goog.a11y.aria.State.EXPANDED));
    +}
    +
    +function hasInlineBlock(el) {
    +  return goog.dom.classlist.contains(el, 'goog-inline-block');
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.MenuButtonRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuheader.js b/mapbox-gl/closure-library/closure/goog/ui/menuheader.js
    new file mode 100644
    index 0000000000..1e04f32e78
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuheader.js
    @@ -0,0 +1,62 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for representing menu headers.
    + * @see goog.ui.Menu
    + *
    + */
    +
    +goog.provide('goog.ui.MenuHeader');
    +
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.MenuHeaderRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a menu header.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @param {goog.ui.MenuHeaderRenderer=} opt_renderer Optional renderer.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.MenuHeader = function(content, opt_domHelper, opt_renderer) {
    +  goog.ui.Control.call(this, content, opt_renderer ||
    +      goog.ui.MenuHeaderRenderer.getInstance(), opt_domHelper);
    +
    +  this.setSupportedState(goog.ui.Component.State.DISABLED, false);
    +  this.setSupportedState(goog.ui.Component.State.HOVER, false);
    +  this.setSupportedState(goog.ui.Component.State.ACTIVE, false);
    +  this.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +
    +  // Headers are always considered disabled.
    +  this.setStateInternal(goog.ui.Component.State.DISABLED);
    +};
    +goog.inherits(goog.ui.MenuHeader, goog.ui.Control);
    +
    +
    +// Register a decorator factory function for goog.ui.MenuHeaders.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.MenuHeaderRenderer.CSS_CLASS,
    +    function() {
    +      // MenuHeader defaults to using MenuHeaderRenderer.
    +      return new goog.ui.MenuHeader(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuheaderrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menuheaderrenderer.js
    new file mode 100644
    index 0000000000..fd2360d5c5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuheaderrenderer.js
    @@ -0,0 +1,54 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.MenuHeader}s.
    + *
    + */
    +
    +goog.provide('goog.ui.MenuHeaderRenderer');
    +
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Renderer for menu headers.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.MenuHeaderRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.MenuHeaderRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.MenuHeaderRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.MenuHeaderRenderer.CSS_CLASS = goog.getCssName('goog-menuheader');
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.MenuHeaderRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuHeaderRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitem.js b/mapbox-gl/closure-library/closure/goog/ui/menuitem.js
    new file mode 100644
    index 0000000000..9db7b88450
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitem.js
    @@ -0,0 +1,322 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for representing items in menus.
    + * @see goog.ui.Menu
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/menuitem.html
    + */
    +
    +goog.provide('goog.ui.MenuItem');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.MenuItemRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing an item in a menu.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {*=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.MenuItem = function(content, opt_model, opt_domHelper, opt_renderer) {
    +  goog.ui.Control.call(this, content, opt_renderer ||
    +      goog.ui.MenuItemRenderer.getInstance(), opt_domHelper);
    +  this.setValue(opt_model);
    +};
    +goog.inherits(goog.ui.MenuItem, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.MenuItem);
    +
    +
    +/**
    + * The access key for this menu item. This key allows the user to quickly
    + * trigger this item's action with they keyboard. For example, setting the
    + * mnenomic key to 70 (F), when the user opens the menu and hits "F," the
    + * menu item is triggered.
    + *
    + * @type {goog.events.KeyCodes}
    + * @private
    + */
    +goog.ui.MenuItem.prototype.mnemonicKey_;
    +
    +
    +/**
    + * The class set on an element that contains a parenthetical mnemonic key hint.
    + * Parenthetical hints are added to items in which the mnemonic key is not found
    + * within the menu item's caption itself. For example, if you have a menu item
    + * with the caption "Record," but its mnemonic key is "I", the caption displayed
    + * in the menu will appear as "Record (I)".
    + *
    + * @type {string}
    + * @private
    + */
    +goog.ui.MenuItem.MNEMONIC_WRAPPER_CLASS_ =
    +    goog.getCssName('goog-menuitem-mnemonic-separator');
    +
    +
    +/**
    + * The class set on an element that contains a keyboard accelerator hint.
    + * @type {string}
    + */
    +goog.ui.MenuItem.ACCELERATOR_CLASS = goog.getCssName('goog-menuitem-accel');
    +
    +
    +// goog.ui.Component and goog.ui.Control implementation.
    +
    +
    +/**
    + * Returns the value associated with the menu item.  The default implementation
    + * returns the model object associated with the item (if any), or its caption.
    + * @return {*} Value associated with the menu item, if any, or its caption.
    + */
    +goog.ui.MenuItem.prototype.getValue = function() {
    +  var model = this.getModel();
    +  return model != null ? model : this.getCaption();
    +};
    +
    +
    +/**
    + * Sets the value associated with the menu item.  The default implementation
    + * stores the value as the model of the menu item.
    + * @param {*} value Value to be associated with the menu item.
    + */
    +goog.ui.MenuItem.prototype.setValue = function(value) {
    +  this.setModel(value);
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItem.prototype.setSupportedState = function(state, support) {
    +  goog.ui.MenuItem.base(this, 'setSupportedState', state, support);
    +  switch (state) {
    +    case goog.ui.Component.State.SELECTED:
    +      this.setSelectableInternal_(support);
    +      break;
    +    case goog.ui.Component.State.CHECKED:
    +      this.setCheckableInternal_(support);
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * Sets the menu item to be selectable or not.  Set to true for menu items
    + * that represent selectable options.
    + * @param {boolean} selectable Whether the menu item is selectable.
    + */
    +goog.ui.MenuItem.prototype.setSelectable = function(selectable) {
    +  this.setSupportedState(goog.ui.Component.State.SELECTED, selectable);
    +};
    +
    +
    +/**
    + * Sets the menu item to be selectable or not.
    + * @param {boolean} selectable  Whether the menu item is selectable.
    + * @private
    + */
    +goog.ui.MenuItem.prototype.setSelectableInternal_ = function(selectable) {
    +  if (this.isChecked() && !selectable) {
    +    this.setChecked(false);
    +  }
    +
    +  var element = this.getElement();
    +  if (element) {
    +    this.getRenderer().setSelectable(this, element, selectable);
    +  }
    +};
    +
    +
    +/**
    + * Sets the menu item to be checkable or not.  Set to true for menu items
    + * that represent checkable options.
    + * @param {boolean} checkable Whether the menu item is checkable.
    + */
    +goog.ui.MenuItem.prototype.setCheckable = function(checkable) {
    +  this.setSupportedState(goog.ui.Component.State.CHECKED, checkable);
    +};
    +
    +
    +/**
    + * Sets the menu item to be checkable or not.
    + * @param {boolean} checkable Whether the menu item is checkable.
    + * @private
    + */
    +goog.ui.MenuItem.prototype.setCheckableInternal_ = function(checkable) {
    +  var element = this.getElement();
    +  if (element) {
    +    this.getRenderer().setCheckable(this, element, checkable);
    +  }
    +};
    +
    +
    +/**
    + * Returns the text caption of the component while ignoring accelerators.
    + * @override
    + */
    +goog.ui.MenuItem.prototype.getCaption = function() {
    +  var content = this.getContent();
    +  if (goog.isArray(content)) {
    +    var acceleratorClass = goog.ui.MenuItem.ACCELERATOR_CLASS;
    +    var mnemonicWrapClass = goog.ui.MenuItem.MNEMONIC_WRAPPER_CLASS_;
    +    var caption = goog.array.map(content, function(node) {
    +      if (goog.dom.isElement(node) &&
    +          (goog.dom.classlist.contains(/** @type {!Element} */ (node),
    +              acceleratorClass) ||
    +          goog.dom.classlist.contains(/** @type {!Element} */ (node),
    +              mnemonicWrapClass))) {
    +        return '';
    +      } else {
    +        return goog.dom.getRawTextContent(node);
    +      }
    +    }).join('');
    +    return goog.string.collapseBreakingSpaces(caption);
    +  }
    +  return goog.ui.MenuItem.superClass_.getCaption.call(this);
    +};
    +
    +
    +/**
    + * @return {?string} The keyboard accelerator text, or null if the menu item
    + *     doesn't have one.
    + */
    +goog.ui.MenuItem.prototype.getAccelerator = function() {
    +  var dom = this.getDomHelper();
    +  var content = this.getContent();
    +  if (goog.isArray(content)) {
    +    var acceleratorEl = goog.array.find(content, function(e) {
    +      return goog.dom.classlist.contains(/** @type {!Element} */ (e),
    +          goog.ui.MenuItem.ACCELERATOR_CLASS);
    +    });
    +    if (acceleratorEl) {
    +      return dom.getTextContent(acceleratorEl);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItem.prototype.handleMouseUp = function(e) {
    +  var parentMenu = /** @type {goog.ui.Menu} */ (this.getParent());
    +
    +  if (parentMenu) {
    +    var oldCoords = parentMenu.openingCoords;
    +    // Clear out the saved opening coords immediately so they're not used twice.
    +    parentMenu.openingCoords = null;
    +
    +    if (oldCoords && goog.isNumber(e.clientX)) {
    +      var newCoords = new goog.math.Coordinate(e.clientX, e.clientY);
    +      if (goog.math.Coordinate.equals(oldCoords, newCoords)) {
    +        // This menu was opened by a mousedown and we're handling the consequent
    +        // mouseup. The coords haven't changed, meaning this was a simple click,
    +        // not a click and drag. Don't do the usual behavior because the menu
    +        // just popped up under the mouse and the user didn't mean to activate
    +        // this item.
    +        return;
    +      }
    +    }
    +  }
    +
    +  goog.ui.MenuItem.base(this, 'handleMouseUp', e);
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItem.prototype.handleKeyEventInternal = function(e) {
    +  if (e.keyCode == this.getMnemonic() && this.performActionInternal(e)) {
    +    return true;
    +  } else {
    +    return goog.ui.MenuItem.base(this, 'handleKeyEventInternal', e);
    +  }
    +};
    +
    +
    +/**
    + * Sets the mnemonic key code. The mnemonic is the key associated with this
    + * action.
    + * @param {goog.events.KeyCodes} key The key code.
    + */
    +goog.ui.MenuItem.prototype.setMnemonic = function(key) {
    +  this.mnemonicKey_ = key;
    +};
    +
    +
    +/**
    + * Gets the mnemonic key code. The mnemonic is the key associated with this
    + * action.
    + * @return {goog.events.KeyCodes} The key code of the mnemonic key.
    + */
    +goog.ui.MenuItem.prototype.getMnemonic = function() {
    +  return this.mnemonicKey_;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.MenuItems.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.MenuItemRenderer.CSS_CLASS,
    +    function() {
    +      // MenuItem defaults to using MenuItemRenderer.
    +      return new goog.ui.MenuItem(null);
    +    });
    +
    +
    +/**
    + * @override
    + */
    +goog.ui.MenuItem.prototype.getPreferredAriaRole = function() {
    +  if (this.isSupportedState(goog.ui.Component.State.CHECKED)) {
    +    return goog.a11y.aria.Role.MENU_ITEM_CHECKBOX;
    +  }
    +  if (this.isSupportedState(goog.ui.Component.State.SELECTED)) {
    +    return goog.a11y.aria.Role.MENU_ITEM_RADIO;
    +  }
    +  return goog.ui.MenuItem.base(this, 'getPreferredAriaRole');
    +};
    +
    +
    +/**
    + * @override
    + * @return {goog.ui.Menu}
    + */
    +goog.ui.MenuItem.prototype.getParent = function() {
    +  return /** @type {goog.ui.Menu} */ (
    +      goog.ui.Control.prototype.getParent.call(this));
    +};
    +
    +
    +/**
    + * @override
    + * @return {goog.ui.Menu}
    + */
    +goog.ui.MenuItem.prototype.getParentEventTarget = function() {
    +  return /** @type {goog.ui.Menu} */ (
    +      goog.ui.Control.prototype.getParentEventTarget.call(this));
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.html b/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.html
    new file mode 100644
    index 0000000000..c56b9bae2d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.MenuItem
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuItemTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    +  <div id="parentComponent">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.js b/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.js
    new file mode 100644
    index 0000000000..4ceda5dfe2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitem_test.js
    @@ -0,0 +1,583 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuItemTest');
    +goog.setTestOnly('goog.ui.MenuItemTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuItemRenderer');
    +
    +var sandbox;
    +var item;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  item = new goog.ui.MenuItem('Item');
    +}
    +
    +function tearDown() {
    +  item.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testMenuItem() {
    +  assertNotNull('Instance must not be null', item);
    +  assertEquals('Renderer must default to MenuItemRenderer singleton',
    +      goog.ui.MenuItemRenderer.getInstance(), item.getRenderer());
    +  assertEquals('Content must have expected value', 'Item',
    +      item.getContent());
    +  assertEquals('Caption must default to the content', item.getContent(),
    +      item.getCaption());
    +  assertEquals('Value must default to the caption', item.getCaption(),
    +      item.getValue());
    +}
    +
    +function testMenuItemConstructor() {
    +  var model = 'Hello';
    +  var fakeDom = {};
    +  var fakeRenderer = {};
    +
    +  var menuItem = new goog.ui.MenuItem('Item', model, fakeDom, fakeRenderer);
    +  assertEquals('Content must have expected value', 'Item',
    +      menuItem.getContent());
    +  assertEquals('Caption must default to the content', menuItem.getContent(),
    +      menuItem.getCaption());
    +  assertEquals('Model must be set', model, menuItem.getModel());
    +  assertNotEquals('Value must not equal the caption', menuItem.getCaption(),
    +      menuItem.getValue());
    +  assertEquals('Value must equal the model', model, menuItem.getValue());
    +  assertEquals('DomHelper must be set', fakeDom, menuItem.getDomHelper());
    +  assertEquals('Renderer must be set', fakeRenderer,
    +      menuItem.getRenderer());
    +}
    +
    +function testGetValue() {
    +  assertUndefined('Model must be undefined by default', item.getModel());
    +  assertEquals('Without a model, value must default to the caption',
    +      item.getCaption(), item.getValue());
    +  item.setModel('Foo');
    +  assertEquals('With a model, value must default to the model',
    +      item.getModel(), item.getValue());
    +}
    +
    +function testSetValue() {
    +  assertUndefined('Model must be undefined by default', item.getModel());
    +  assertEquals('Without a model, value must default to the caption',
    +      item.getCaption(), item.getValue());
    +  item.setValue(17);
    +  assertEquals('Value must be set', 17, item.getValue());
    +  assertEquals('Value and model must be the same', item.getValue(),
    +      item.getModel());
    +}
    +
    +function testGetSetContent() {
    +  assertEquals('Content must have expected value', 'Item',
    +      item.getContent());
    +  item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
    +  assertEquals('Content must be an element', goog.dom.NodeType.ELEMENT,
    +      item.getContent().nodeType);
    +  assertHTMLEquals('Content must be the expected element',
    +      '<div class="foo">Foo</div>',
    +      goog.dom.getOuterHtml(item.getContent()));
    +}
    +
    +function testGetSetCaption() {
    +  assertEquals('Caption must have expected value', 'Item',
    +      item.getCaption());
    +  item.setCaption('Hello, world!');
    +  assertTrue('Caption must be a string', goog.isString(item.getCaption()));
    +  assertEquals('Caption must have expected value', 'Hello, world!',
    +      item.getCaption());
    +  item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
    +  assertTrue('Caption must be a string', goog.isString(item.getCaption()));
    +  assertEquals('Caption must have expected value', 'Foo',
    +      item.getCaption());
    +}
    +
    +function testGetSetContentAfterCreateDom() {
    +  item.createDom();
    +  assertEquals('Content must have expected value', 'Item',
    +      item.getContent());
    +  item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
    +  assertEquals('Content must be an element', goog.dom.NodeType.ELEMENT,
    +      item.getContent().nodeType);
    +  assertHTMLEquals('Content must be the expected element',
    +      '<div class="foo">Foo</div>',
    +      goog.dom.getOuterHtml(item.getContent()));
    +}
    +
    +function testGetSetCaptionAfterCreateDom() {
    +  item.createDom();
    +  assertEquals('Caption must have expected value', 'Item',
    +      item.getCaption());
    +  item.setCaption('Hello, world!');
    +  assertTrue('Caption must be a string', goog.isString(item.getCaption()));
    +  assertEquals('Caption must have expected value', 'Hello, world!',
    +      item.getCaption());
    +  item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
    +  assertTrue('Caption must be a string', goog.isString(item.getCaption()));
    +  assertEquals('Caption must have expected value', 'Foo',
    +      item.getCaption());
    +
    +  var arrayContent = goog.array.clone(goog.dom.htmlToDocumentFragment(
    +      ' <b> \xa0foo</b><i>  bar</i> ').childNodes);
    +  item.setContent(arrayContent);
    +  assertEquals('whitespaces must be normalized in the caption',
    +      '\xa0foo bar', item.getCaption());
    +}
    +
    +function testSetSelectable() {
    +  assertFalse('Item must not be selectable by default',
    +      item.isSupportedState(goog.ui.Component.State.SELECTED));
    +  item.setSelectable(true);
    +  assertTrue('Item must be selectable',
    +      item.isSupportedState(goog.ui.Component.State.SELECTED));
    +  item.setSelected(true);
    +  assertTrue('Item must be selected', item.isSelected());
    +  assertFalse('Item must not be checked', item.isChecked());
    +  item.setSelectable(false);
    +  assertFalse('Item must not no longer be selectable',
    +      item.isSupportedState(goog.ui.Component.State.SELECTED));
    +  assertFalse('Item must no longer be selected', item.isSelected());
    +  assertFalse('Item must not be checked', item.isChecked());
    +}
    +
    +function testSetCheckable() {
    +  assertFalse('Item must not be checkable by default',
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  item.setCheckable(true);
    +  assertTrue('Item must be checkable',
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  item.setChecked(true);
    +  assertTrue('Item must be checked', item.isChecked());
    +  assertFalse('Item must not be selected', item.isSelected());
    +  item.setCheckable(false);
    +  assertFalse('Item must not no longer be checkable',
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  assertFalse('Item must no longer be checked', item.isChecked());
    +  assertFalse('Item must not be selected', item.isSelected());
    +}
    +
    +function testSetSelectableBeforeCreateDom() {
    +  item.setSelectable(true);
    +  item.createDom();
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  item.setSelectable(false);
    +  assertFalse('Item must no longer have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testSetCheckableBeforeCreateDom() {
    +  item.setCheckable(true);
    +  item.createDom();
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('Element must have ARIA role menuitemcheckbox',
    +      goog.a11y.aria.Role.MENU_ITEM_CHECKBOX,
    +      goog.a11y.aria.getRole(item.getElement()));
    +  item.setCheckable(false);
    +  assertFalse('Item must no longer have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testSetSelectableAfterCreateDom() {
    +  item.createDom();
    +  item.setSelectable(true);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('Element must have ARIA role menuitemradio',
    +      goog.a11y.aria.Role.MENU_ITEM_RADIO,
    +      goog.a11y.aria.getRole(item.getElement()));
    +  item.setSelectable(false);
    +  assertFalse('Item must no longer have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testSetCheckableAfterCreateDom() {
    +  item.createDom();
    +  item.setCheckable(true);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  item.setCheckable(false);
    +  assertFalse('Item must no longer have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testSelectableBehavior() {
    +  item.setSelectable(true);
    +  item.render(sandbox);
    +  assertFalse('Item must not be selected by default', item.isSelected());
    +  item.performActionInternal();
    +  assertTrue('Item must be selected', item.isSelected());
    +  item.performActionInternal();
    +  assertTrue('Item must still be selected', item.isSelected());
    +}
    +
    +function testCheckableBehavior() {
    +  item.setCheckable(true);
    +  item.render(sandbox);
    +  assertFalse('Item must not be checked by default', item.isChecked());
    +  item.performActionInternal();
    +  assertTrue('Item must be checked', item.isChecked());
    +  item.performActionInternal();
    +  assertFalse('Item must no longer be checked', item.isChecked());
    +}
    +
    +function testGetSetContentForItemWithCheckBox() {
    +  item.setSelectable(true);
    +  item.createDom();
    +
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('getContent() must not return the checkbox structure',
    +      'Item', item.getContent());
    +
    +  item.setContent('Hello');
    +  assertEquals('getContent() must not return the checkbox structure',
    +      'Hello', item.getContent());
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +
    +  item.setContent(goog.dom.createDom('span', 'foo', 'Foo'));
    +  assertEquals('getContent() must return element',
    +      goog.dom.NodeType.ELEMENT, item.getContent().nodeType);
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +
    +  item.setContent(null);
    +  assertNull('getContent() must return null', item.getContent());
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testGetSetCaptionForItemWithCheckBox() {
    +  item.setCheckable(true);
    +  item.createDom();
    +
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('getCaption() must not return the checkbox structure',
    +      'Item', item.getCaption());
    +
    +  item.setCaption('Hello');
    +  assertEquals('getCaption() must not return the checkbox structure',
    +      'Hello', item.getCaption());
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +
    +  item.setContent(goog.dom.createDom('span', 'foo', 'Foo'));
    +  assertEquals('getCaption() must return text content', 'Foo',
    +      item.getCaption());
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +
    +  item.setCaption('');
    +  assertEquals('getCaption() must return empty string', '',
    +      item.getCaption());
    +  assertTrue('Item must still have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +}
    +
    +function testGetSetCaptionForItemWithAccelerators() {
    +  var contentArr = [];
    +  contentArr.push(goog.dom.createDom('span',
    +      goog.getCssName('goog-menuitem-accel'), 'Ctrl+1'));
    +  contentArr.push(goog.dom.createTextNode('Hello'));
    +  item.setCaption(contentArr);
    +  assertEquals('getCaption() must not return the accelerator', 'Hello',
    +      item.getCaption());
    +
    +  item.setCaption([
    +    goog.dom.createDom('span', goog.getCssName('goog-menuitem-accel'), 'Ctrl+1')
    +  ]);
    +  assertEquals('getCaption() must return empty string', '',
    +      item.getCaption());
    +
    +  assertEquals('getAccelerator() should return the accelerator', 'Ctrl+1',
    +      item.getAccelerator());
    +}
    +
    +function testGetSetCaptionForItemWithMnemonics() {
    +  var contentArr = [];
    +  contentArr.push(goog.dom.createDom('span',
    +      goog.getCssName('goog-menuitem-mnemonic-hint'), 'H'));
    +  contentArr.push(goog.dom.createTextNode('ello'));
    +  item.setCaption(contentArr);
    +  assertEquals('getCaption() must not return hint markup', 'Hello',
    +      item.getCaption());
    +
    +  contentArr = [];
    +  contentArr.push(goog.dom.createTextNode('Hello'));
    +  contentArr.push(goog.dom.createDom('span',
    +      goog.getCssName('goog-menuitem-mnemonic-separator'), '(',
    +      goog.dom.createDom('span',
    +          goog.getCssName('goog-menuitem-mnemonic-hint'), 'J'), ')'));
    +  item.setCaption(contentArr);
    +  assertEquals('getCaption() must not return the paranethetical mnemonic',
    +      'Hello', item.getCaption());
    +
    +  item.setCaption('');
    +  assertEquals('getCaption() must return the empty string', '',
    +      item.getCaption());
    +}
    +
    +function testHandleKeyEventInternalWithMnemonic() {
    +  item.performActionInternal =
    +      goog.testing.recordFunction(item.performActionInternal);
    +  item.setMnemonic(goog.events.KeyCodes.F);
    +  item.handleKeyEventInternal({'keyCode': goog.events.KeyCodes.F});
    +  assertEquals('performActionInternal must be called', 1,
    +      item.performActionInternal.getCallCount());
    +}
    +
    +function testHandleKeyEventInternalWithoutMnemonic() {
    +  item.performActionInternal = goog.testing.recordFunction(
    +      item.performActionInternal);
    +  item.handleKeyEventInternal({'keyCode': goog.events.KeyCodes.F});
    +  assertEquals('performActionInternal must not be called without a' +
    +      ' mnemonic', 0, item.performActionInternal.getCallCount());
    +}
    +
    +function testRender() {
    +  item.render(sandbox);
    +  var contentElement = item.getContentElement();
    +  assertNotNull('Content element must exist', contentElement);
    +  assertTrue('Content element must have expected class name',
    +      goog.dom.classlist.contains(contentElement,
    +          item.getRenderer().getStructuralCssClass() + '-content'));
    +  assertHTMLEquals('Content element must have expected structure',
    +      'Item', contentElement.innerHTML);
    +}
    +
    +function testRenderSelectableItem() {
    +  item.setSelectable(true);
    +  item.render(sandbox);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('getCaption() return expected value', 'Item',
    +      item.getCaption());
    +}
    +
    +function testRenderSelectedItem() {
    +  item.setSelectable(true);
    +  item.setSelected(true);
    +  item.render(sandbox);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertTrue('Item must have selected style',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getClassForState(
    +              goog.ui.Component.State.SELECTED)));
    +}
    +
    +function testRenderCheckableItem() {
    +  item.setCheckable(true);
    +  item.render(sandbox);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('getCaption() return expected value', 'Item',
    +      item.getCaption());
    +}
    +
    +function testRenderCheckedItem() {
    +  item.setCheckable(true);
    +  item.setChecked(true);
    +  item.render(sandbox);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertTrue('Item must have checked style',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getClassForState(
    +              goog.ui.Component.State.CHECKED)));
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertTrue('Decorated element must have expected class name',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getCssClass()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertHTMLEquals('Content must have expected structure', 'Foo',
    +      item.getContentElement().innerHTML);
    +}
    +
    +function testDecorateCheckableItem() {
    +  sandbox.innerHTML = '<div id="foo" class="goog-option">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertTrue('Decorated element must have expected class name',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getCssClass()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertFalse('Item must not be checked', item.isChecked());
    +}
    +
    +function testDecorateCheckedItem() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="goog-option goog-option-selected">Foo</div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertSameElements('Decorated element must have expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(item.getElement()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertTrue('Item must be checked', item.isChecked());
    +}
    +
    +function testDecorateTemplate() {
    +  sandbox.innerHTML = '<div id="foo" class="goog-menuitem">' +
    +      '<div class="goog-menuitem-content">Foo</div></div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertTrue('Decorated element must have expected class name',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getCssClass()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertHTMLEquals('Content must have expected structure', 'Foo',
    +      item.getContentElement().innerHTML);
    +}
    +
    +function testDecorateCheckableItemTemplate() {
    +  sandbox.innerHTML = '<div id="foo" class="goog-menuitem goog-option">' +
    +      '<div class="goog-menuitem-content">' +
    +      '<div class="goog-menuitem-checkbox"></div>' +
    +      'Foo</div></div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertTrue('Decorated element must have expected class name',
    +      goog.dom.classlist.contains(item.getElement(),
    +          item.getRenderer().getCssClass()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('Item must have exactly one checkbox structure', 1,
    +      goog.dom.getElementsByTagNameAndClass('div', 'goog-menuitem-checkbox',
    +          item.getElement()).length);
    +  assertFalse('Item must not be checked', item.isChecked());
    +}
    +
    +function testDecorateCheckedItemTemplate() {
    +  sandbox.innerHTML = '<div id="foo" ' +
    +      'class="goog-menuitem goog-option goog-option-selected">' +
    +      '<div class="goog-menuitem-content">' +
    +      '<div class="goog-menuitem-checkbox"></div>' +
    +      'Foo</div></div>';
    +  var foo = goog.dom.getElement('foo');
    +  item.decorate(foo);
    +  assertEquals('Decorated element must be as expected', foo,
    +      item.getElement());
    +  assertSameElements('Decorated element must have expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(item.getElement()));
    +  assertEquals('Content element must be the decorated element\'s child',
    +      item.getContentElement(), item.getElement().firstChild);
    +  assertTrue('Item must have checkbox structure',
    +      item.getRenderer().hasCheckBoxStructure(item.getElement()));
    +  assertEquals('Item must have exactly one checkbox structure', 1,
    +      goog.dom.getElementsByTagNameAndClass('div', 'goog-menuitem-checkbox',
    +          item.getElement()).length);
    +  assertTrue('Item must be checked', item.isChecked());
    +}
    +
    +
    +/** @bug 1463524 */
    +function testHandleMouseUp() {
    +  var COORDS_1 = new goog.math.Coordinate(1, 1);
    +  var COORDS_2 = new goog.math.Coordinate(2, 2);
    +  item.setActive(true);
    +  // Override performActionInternal() for testing purposes.
    +  var actionPerformed;
    +  item.performActionInternal = function() {
    +    actionPerformed = true;
    +    return true;
    +  };
    +  item.render(sandbox);
    +
    +  // Scenario 1: item has no parent.
    +  actionPerformed = false;
    +  item.setActive(true);
    +  goog.testing.events.fireMouseUpEvent(item.getElement());
    +  assertTrue('Action should be performed on mouseup', actionPerformed);
    +
    +  // Scenario 2: item has a parent.
    +  actionPerformed = false;
    +  item.setActive(true);
    +  var parent = new goog.ui.Component();
    +  var parentElem = goog.dom.getElement('parentComponent');
    +  parent.render(parentElem);
    +  parent.addChild(item);
    +  parent.openingCoords = COORDS_1;
    +  goog.testing.events.fireMouseUpEvent(
    +      item.getElement(), undefined, COORDS_2);
    +  assertTrue('Action should be performed on mouseup', actionPerformed);
    +
    +  // Scenario 3: item has a parent which was opened during mousedown, and
    +  // item, and now the mouseup fires at the same coords.
    +  actionPerformed = false;
    +  item.setActive(true);
    +  parent.openingCoords = COORDS_2;
    +  goog.testing.events.fireMouseUpEvent(
    +      item.getElement(), undefined, COORDS_2);
    +  assertFalse('Action should not be performed on mouseup', actionPerformed);
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Item must not have aria label by default', item.getAriaLabel());
    +  item.setAriaLabel('Item 1');
    +  item.render(sandbox);
    +  var el = item.getElementStrict();
    +  assertEquals('Item element must have expected aria-label', 'Item 1',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Item element must have expected aria-role', 'menuitem',
    +      el.getAttribute('role'));
    +  item.setAriaLabel('Item 2');
    +  assertEquals('Item element must have updated aria-label', 'Item 2',
    +      el.getAttribute('aria-label'));
    +  assertEquals('Item element must have expected aria-role', 'menuitem',
    +      el.getAttribute('role'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer.js
    new file mode 100644
    index 0000000000..76e9765fab
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer.js
    @@ -0,0 +1,354 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.MenuItem}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.MenuItemRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.MenuItem}s.  Each item has the following
    + * structure:
    + * <pre>
    + *   <div class="goog-menuitem">
    + *     <div class="goog-menuitem-content">
    + *       ...(menu item contents)...
    + *     </div>
    + *   </div>
    + * </pre>
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.MenuItemRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +
    +  /**
    +   * Commonly used CSS class names, cached here for convenience (and to avoid
    +   * unnecessary string concatenation).
    +   * @type {!Array<string>}
    +   * @private
    +   */
    +  this.classNameCache_ = [];
    +};
    +goog.inherits(goog.ui.MenuItemRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.MenuItemRenderer);
    +
    +
    +/**
    + * CSS class name the renderer applies to menu item elements.
    + * @type {string}
    + */
    +goog.ui.MenuItemRenderer.CSS_CLASS = goog.getCssName('goog-menuitem');
    +
    +
    +/**
    + * Constants for referencing composite CSS classes.
    + * @enum {number}
    + * @private
    + */
    +goog.ui.MenuItemRenderer.CompositeCssClassIndex_ = {
    +  HOVER: 0,
    +  CHECKBOX: 1,
    +  CONTENT: 2
    +};
    +
    +
    +/**
    + * Returns the composite CSS class by using the cached value or by constructing
    + * the value from the base CSS class and the passed index.
    + * @param {goog.ui.MenuItemRenderer.CompositeCssClassIndex_} index Index for the
    + *     CSS class - could be highlight, checkbox or content in usual cases.
    + * @return {string} The composite CSS class.
    + * @private
    + */
    +goog.ui.MenuItemRenderer.prototype.getCompositeCssClass_ = function(index) {
    +  var result = this.classNameCache_[index];
    +  if (!result) {
    +    switch (index) {
    +      case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER:
    +        result = goog.getCssName(this.getStructuralCssClass(), 'highlight');
    +        break;
    +      case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX:
    +        result = goog.getCssName(this.getStructuralCssClass(), 'checkbox');
    +        break;
    +      case goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT:
    +        result = goog.getCssName(this.getStructuralCssClass(), 'content');
    +        break;
    +    }
    +    this.classNameCache_[index] = result;
    +  }
    +
    +  return result;
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItemRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.MENU_ITEM;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#createDom} by adding extra markup
    + * and stying to the menu item's element if it is selectable or checkable.
    + * @param {goog.ui.Control} item Menu item to render.
    + * @return {Element} Root element for the item.
    + * @override
    + */
    +goog.ui.MenuItemRenderer.prototype.createDom = function(item) {
    +  var element = item.getDomHelper().createDom(
    +      'div', this.getClassNames(item).join(' '),
    +      this.createContent(item.getContent(), item.getDomHelper()));
    +  this.setEnableCheckBoxStructure(item, element,
    +      item.isSupportedState(goog.ui.Component.State.SELECTED) ||
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  return element;
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItemRenderer.prototype.getContentElement = function(element) {
    +  return /** @type {Element} */ (element && element.firstChild);
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#decorate} by initializing the
    + * menu item to checkable based on whether the element to be decorated has
    + * extra stying indicating that it should be.
    + * @param {goog.ui.Control} item Menu item instance to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.MenuItemRenderer.prototype.decorate = function(item, element) {
    +  goog.asserts.assert(element);
    +  if (!this.hasContentStructure(element)) {
    +    element.appendChild(
    +        this.createContent(element.childNodes, item.getDomHelper()));
    +  }
    +  if (goog.dom.classlist.contains(element, goog.getCssName('goog-option'))) {
    +    (/** @type {goog.ui.MenuItem} */ (item)).setCheckable(true);
    +    this.setCheckable(item, element, true);
    +  }
    +  return goog.ui.MenuItemRenderer.superClass_.decorate.call(this, item,
    +      element);
    +};
    +
    +
    +/**
    + * Takes a menu item's root element, and sets its content to the given text
    + * caption or DOM structure.  Overrides the superclass immplementation by
    + * making sure that the checkbox structure (for selectable/checkable menu
    + * items) is preserved.
    + * @param {Element} element The item's root element.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to be
    + *     set as the item's content.
    + * @override
    + */
    +goog.ui.MenuItemRenderer.prototype.setContent = function(element, content) {
    +  // Save the checkbox element, if present.
    +  var contentElement = this.getContentElement(element);
    +  var checkBoxElement = this.hasCheckBoxStructure(element) ?
    +      contentElement.firstChild : null;
    +  goog.ui.MenuItemRenderer.superClass_.setContent.call(this, element, content);
    +  if (checkBoxElement && !this.hasCheckBoxStructure(element)) {
    +    // The call to setContent() blew away the checkbox element; reattach it.
    +    contentElement.insertBefore(checkBoxElement,
    +        contentElement.firstChild || null);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the element appears to have a proper menu item structure by
    + * checking whether its first child has the appropriate structural class name.
    + * @param {Element} element Element to check.
    + * @return {boolean} Whether the element appears to have a proper menu item DOM.
    + * @protected
    + */
    +goog.ui.MenuItemRenderer.prototype.hasContentStructure = function(element) {
    +  var child = goog.dom.getFirstElementChild(element);
    +  var contentClassName = this.getCompositeCssClass_(
    +      goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);
    +  return !!child && goog.dom.classlist.contains(child, contentClassName);
    +};
    +
    +
    +/**
    + * Wraps the given text caption or existing DOM node(s) in a structural element
    + * containing the menu item's contents.
    + * @param {goog.ui.ControlContent} content Menu item contents.
    + * @param {goog.dom.DomHelper} dom DOM helper for document interaction.
    + * @return {Element} Menu item content element.
    + * @protected
    + */
    +goog.ui.MenuItemRenderer.prototype.createContent = function(content, dom) {
    +  var contentClassName = this.getCompositeCssClass_(
    +      goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CONTENT);
    +  return dom.createDom('div', contentClassName, content);
    +};
    +
    +
    +/**
    + * Enables/disables radio button semantics on the menu item.
    + * @param {goog.ui.Control} item Menu item to update.
    + * @param {Element} element Menu item element to update (may be null if the
    + *     item hasn't been rendered yet).
    + * @param {boolean} selectable Whether the item should be selectable.
    + */
    +goog.ui.MenuItemRenderer.prototype.setSelectable = function(item, element,
    +    selectable) {
    +  if (item && element) {
    +    this.setEnableCheckBoxStructure(item, element, selectable);
    +  }
    +};
    +
    +
    +/**
    + * Enables/disables checkbox semantics on the menu item.
    + * @param {goog.ui.Control} item Menu item to update.
    + * @param {Element} element Menu item element to update (may be null if the
    + *     item hasn't been rendered yet).
    + * @param {boolean} checkable Whether the item should be checkable.
    + */
    +goog.ui.MenuItemRenderer.prototype.setCheckable = function(item, element,
    +    checkable) {
    +  if (item && element) {
    +    this.setEnableCheckBoxStructure(item, element, checkable);
    +  }
    +};
    +
    +
    +/**
    + * Determines whether the item contains a checkbox element.
    + * @param {Element} element Menu item root element.
    + * @return {boolean} Whether the element contains a checkbox element.
    + * @protected
    + */
    +goog.ui.MenuItemRenderer.prototype.hasCheckBoxStructure = function(element) {
    +  var contentElement = this.getContentElement(element);
    +  if (contentElement) {
    +    var child = contentElement.firstChild;
    +    var checkboxClassName = this.getCompositeCssClass_(
    +        goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);
    +    return !!child && goog.dom.isElement(child) &&
    +        goog.dom.classlist.contains(/** @type {!Element} */ (child),
    +            checkboxClassName);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Adds or removes extra markup and CSS styling to the menu item to make it
    + * selectable or non-selectable, depending on the value of the
    + * {@code selectable} argument.
    + * @param {!goog.ui.Control} item Menu item to update.
    + * @param {!Element} element Menu item element to update.
    + * @param {boolean} enable Whether to add or remove the checkbox structure.
    + * @protected
    + */
    +goog.ui.MenuItemRenderer.prototype.setEnableCheckBoxStructure = function(item,
    +    element, enable) {
    +  this.setAriaRole(element, item.getPreferredAriaRole());
    +  this.setAriaStates(item, element);
    +  if (enable != this.hasCheckBoxStructure(element)) {
    +    goog.dom.classlist.enable(element, goog.getCssName('goog-option'), enable);
    +    var contentElement = this.getContentElement(element);
    +    if (enable) {
    +      // Insert checkbox structure.
    +      var checkboxClassName = this.getCompositeCssClass_(
    +          goog.ui.MenuItemRenderer.CompositeCssClassIndex_.CHECKBOX);
    +      contentElement.insertBefore(
    +          item.getDomHelper().createDom('div', checkboxClassName),
    +          contentElement.firstChild || null);
    +    } else {
    +      // Remove checkbox structure.
    +      contentElement.removeChild(contentElement.firstChild);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Takes a single {@link goog.ui.Component.State}, and returns the
    + * corresponding CSS class name (null if none).  Overrides the superclass
    + * implementation by using 'highlight' as opposed to 'hover' as the CSS
    + * class name suffix for the HOVER state, for backwards compatibility.
    + * @param {goog.ui.Component.State} state Component state.
    + * @return {string|undefined} CSS class representing the given state
    + *     (undefined if none).
    + * @override
    + */
    +goog.ui.MenuItemRenderer.prototype.getClassForState = function(state) {
    +  switch (state) {
    +    case goog.ui.Component.State.HOVER:
    +      // We use 'highlight' as the suffix, for backwards compatibility.
    +      return this.getCompositeCssClass_(
    +          goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);
    +    case goog.ui.Component.State.CHECKED:
    +    case goog.ui.Component.State.SELECTED:
    +      // We use 'goog-option-selected' as the class, for backwards
    +      // compatibility.
    +      return goog.getCssName('goog-option-selected');
    +    default:
    +      return goog.ui.MenuItemRenderer.superClass_.getClassForState.call(this,
    +          state);
    +  }
    +};
    +
    +
    +/**
    + * Takes a single CSS class name which may represent a component state, and
    + * returns the corresponding component state (0x00 if none).  Overrides the
    + * superclass implementation by treating 'goog-option-selected' as special,
    + * for backwards compatibility.
    + * @param {string} className CSS class name, possibly representing a component
    + *     state.
    + * @return {goog.ui.Component.State} state Component state corresponding
    + *     to the given CSS class (0x00 if none).
    + * @override
    + */
    +goog.ui.MenuItemRenderer.prototype.getStateFromClass = function(className) {
    +  var hoverClassName = this.getCompositeCssClass_(
    +      goog.ui.MenuItemRenderer.CompositeCssClassIndex_.HOVER);
    +  switch (className) {
    +    case goog.getCssName('goog-option-selected'):
    +      return goog.ui.Component.State.CHECKED;
    +    case hoverClassName:
    +      return goog.ui.Component.State.HOVER;
    +    default:
    +      return goog.ui.MenuItemRenderer.superClass_.getStateFromClass.call(this,
    +          className);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuItemRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuItemRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.html
    new file mode 100644
    index 0000000000..0bad914022
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.MenuItemRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuItemRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.js
    new file mode 100644
    index 0000000000..58b638d97e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuitemrenderer_test.js
    @@ -0,0 +1,243 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuItemRendererTest');
    +goog.setTestOnly('goog.ui.MenuItemRendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuItemRenderer');
    +
    +var sandbox;
    +var item, renderer;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  item = new goog.ui.MenuItem('Hello');
    +  renderer = goog.ui.MenuItemRenderer.getInstance();
    +}
    +
    +function tearDown() {
    +  item.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testMenuItemRenderer() {
    +  assertNotNull('Instance must not be null', renderer);
    +  assertEquals('Singleton getter must always return same instance',
    +      renderer, goog.ui.MenuItemRenderer.getInstance());
    +}
    +
    +function testCreateDom() {
    +  var element = renderer.createDom(item);
    +  assertNotNull('Element must not be null', element);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem'], goog.dom.classlist.get(element));
    +  assertEquals('Element must have exactly one child element', 1,
    +      element.childNodes.length);
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">Hello</div>',
    +      element.innerHTML);
    +}
    +
    +function testCreateDomWithHoverState() {
    +  item.setHighlighted(true);
    +  var element = renderer.createDom(item);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-menuitem-highlight'],
    +      goog.dom.classlist.get(element));
    +}
    +
    +function testCreateDomForCheckableItem() {
    +  item.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  item.render();
    +  var element = item.getElement();
    +  assertNotNull(element);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-option'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Element must have ARIA role menuitemcheckbox',
    +      goog.a11y.aria.Role.MENU_ITEM_CHECKBOX,
    +      goog.a11y.aria.getRole(element));
    +
    +  item.setChecked(true);
    +  assertTrue('Item must be checked', item.isChecked());
    +  assertSameElements('Checked item must have the expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Item must have checked ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testCreateUpdateDomForCheckableItem() {
    +  // Render the item first, then update its supported states to include CHECKED.
    +  item.render();
    +  var element = item.getElement();
    +  item.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  assertNotNull(element);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-option'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Element must have ARIA role menuitemcheckbox',
    +      goog.a11y.aria.Role.MENU_ITEM_CHECKBOX,
    +      goog.a11y.aria.getRole(element));
    +
    +  // Now actually check the item.
    +  item.setChecked(true);
    +  assertTrue('Item must be checked', item.isChecked());
    +  assertSameElements('Checked item must have the expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Item must have checked ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testCreateDomForSelectableItem() {
    +  item.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  item.render();
    +  var element = item.getElement();
    +  assertNotNull(element);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-option'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Element must have ARIA role menuitemradio',
    +      goog.a11y.aria.Role.MENU_ITEM_RADIO,
    +      goog.a11y.aria.getRole(element));
    +
    +  item.setSelected(true);
    +  assertTrue('Item must be selected', item.isSelected());
    +  assertSameElements('Selected item must have the expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Item must have selected ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testCreateUpdateDomForSelectableItem() {
    +  // Render the item first, then update its supported states to include
    +  // SELECTED.
    +  item.render();
    +  var element = item.getElement();
    +  item.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +  assertNotNull(element);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-option'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Element must have ARIA role menuitemradio',
    +      goog.a11y.aria.Role.MENU_ITEM_RADIO,
    +      goog.a11y.aria.getRole(element));
    +
    +  // Now actually select the item.
    +  item.setSelected(true);
    +  assertTrue('Item must be selected', item.isSelected());
    +  assertSameElements('Selected item must have the expected class names',
    +      ['goog-menuitem', 'goog-option', 'goog-option-selected'],
    +      goog.dom.classlist.get(element));
    +  assertEquals('Item must have selected ARIA state', 'true',
    +      goog.a11y.aria.getState(element, goog.a11y.aria.State.CHECKED));
    +}
    +
    +function testGetContentElement() {
    +  assertNull('Content element must be the null initially',
    +      item.getContentElement());
    +  item.createDom();
    +  assertEquals('Content element must be the element\'s first child',
    +      item.getElement().firstChild, item.getContentElement());
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML = '<div id="foo">Hello</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  var element = renderer.decorate(item, foo);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem'], goog.dom.classlist.get(element));
    +  assertEquals('Element must have exactly one child element', 1,
    +      element.childNodes.length);
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">Hello</div>',
    +      element.innerHTML);
    +}
    +
    +function testDecorateWithContentStructure() {
    +  sandbox.innerHTML =
    +      '<div id="foo"><div class="goog-menuitem-content">Hello</div></div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  var element = renderer.decorate(item, foo);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem'], goog.dom.classlist.get(element));
    +  assertEquals('Element must have exactly one child element', 1,
    +      element.childNodes.length);
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">Hello</div>',
    +      element.innerHTML);
    +}
    +
    +function testDecorateWithHoverState() {
    +  sandbox.innerHTML =
    +      '<div id="foo" class="goog-menuitem-highlight">Hello</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  assertFalse('Item must not be highlighted', item.isHighlighted());
    +  var element = renderer.decorate(item, foo);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-menuitem-highlight'],
    +      goog.dom.classlist.get(element));
    +  assertTrue('Item must be highlighted', item.isHighlighted());
    +}
    +
    +function testDecorateCheckableItem() {
    +  sandbox.innerHTML = '<div id="foo" class="goog-option">Hello</div>';
    +  var foo = goog.dom.getElement('foo');
    +
    +  assertFalse('Item must not be checkable',
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  var element = renderer.decorate(item, foo);
    +  assertSameElements('Element must have the expected class names',
    +      ['goog-menuitem', 'goog-option'], goog.dom.classlist.get(element));
    +  assertTrue('Item must be checkable',
    +      item.isSupportedState(goog.ui.Component.State.CHECKED));
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">' +
    +          '<div class="goog-menuitem-checkbox"></div>Hello</div>',
    +      element.innerHTML);
    +}
    +
    +function testSetContent() {
    +  item.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +  var element = renderer.createDom(item);
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">' +
    +          '<div class="goog-menuitem-checkbox"></div>Hello</div>',
    +      element.innerHTML);
    +
    +  renderer.setContent(element, 'Goodbye');
    +  assertHTMLEquals('Child element must have the expected structure',
    +      '<div class="goog-menuitem-content">' +
    +          '<div class="goog-menuitem-checkbox"></div>Goodbye</div>',
    +      element.innerHTML);
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.MenuItemRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menurenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menurenderer.js
    new file mode 100644
    index 0000000000..f39134d91f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menurenderer.js
    @@ -0,0 +1,114 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.Menu}s.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + */
    +
    +goog.provide('goog.ui.MenuRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.ui.ContainerRenderer');
    +goog.require('goog.ui.Separator');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Menu}s, based on {@link
    + * goog.ui.ContainerRenderer}.
    + * @param {string=} opt_ariaRole Optional ARIA role used for the element.
    + * @constructor
    + * @extends {goog.ui.ContainerRenderer}
    + */
    +goog.ui.MenuRenderer = function(opt_ariaRole) {
    +  goog.ui.ContainerRenderer.call(this,
    +      opt_ariaRole || goog.a11y.aria.Role.MENU);
    +};
    +goog.inherits(goog.ui.MenuRenderer, goog.ui.ContainerRenderer);
    +goog.addSingletonGetter(goog.ui.MenuRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of toolbars rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.MenuRenderer.CSS_CLASS = goog.getCssName('goog-menu');
    +
    +
    +/**
    + * Returns whether the element is a UL or acceptable to our superclass.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.MenuRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == 'UL' ||
    +      goog.ui.MenuRenderer.superClass_.canDecorate.call(this, element);
    +};
    +
    +
    +/**
    + * Inspects the element, and creates an instance of {@link goog.ui.Control} or
    + * an appropriate subclass best suited to decorate it.  Overrides the superclass
    + * implementation by recognizing HR elements as separators.
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Control?} A new control suitable to decorate the element
    + *     (null if none).
    + * @override
    + */
    +goog.ui.MenuRenderer.prototype.getDecoratorForChild = function(element) {
    +  return element.tagName == 'HR' ?
    +      new goog.ui.Separator() :
    +      goog.ui.MenuRenderer.superClass_.getDecoratorForChild.call(this,
    +          element);
    +};
    +
    +
    +/**
    + * Returns whether the given element is contained in the menu's DOM.
    + * @param {goog.ui.Menu} menu The menu to test.
    + * @param {Element} element The element to test.
    + * @return {boolean} Whether the given element is contained in the menu.
    + */
    +goog.ui.MenuRenderer.prototype.containsElement = function(menu, element) {
    +  return goog.dom.contains(menu.getElement(), element);
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of containers
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.MenuRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuRenderer.CSS_CLASS;
    +};
    +
    +
    +/** @override */
    +goog.ui.MenuRenderer.prototype.initializeDom = function(container) {
    +  goog.ui.MenuRenderer.superClass_.initializeDom.call(this, container);
    +
    +  var element = container.getElement();
    +  goog.asserts.assert(element, 'The menu DOM element cannot be null.');
    +  goog.a11y.aria.setState(element, goog.a11y.aria.State.HASPOPUP, 'true');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuseparator.js b/mapbox-gl/closure-library/closure/goog/ui/menuseparator.js
    new file mode 100644
    index 0000000000..67365f58f0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuseparator.js
    @@ -0,0 +1,52 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for representing menu separators.
    + * @see goog.ui.Menu
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.MenuSeparator');
    +
    +goog.require('goog.ui.MenuSeparatorRenderer');
    +goog.require('goog.ui.Separator');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a menu separator.  A menu separator extends {@link
    + * goog.ui.Separator} by always setting its renderer to {@link
    + * goog.ui.MenuSeparatorRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @constructor
    + * @extends {goog.ui.Separator}
    + */
    +goog.ui.MenuSeparator = function(opt_domHelper) {
    +  goog.ui.Separator.call(this, goog.ui.MenuSeparatorRenderer.getInstance(),
    +      opt_domHelper);
    +};
    +goog.inherits(goog.ui.MenuSeparator, goog.ui.Separator);
    +
    +
    +// Register a decorator factory function for goog.ui.MenuSeparators.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.MenuSeparatorRenderer.CSS_CLASS,
    +    function() {
    +      // Separator defaults to using MenuSeparatorRenderer.
    +      return new goog.ui.Separator();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer.js
    new file mode 100644
    index 0000000000..c2e139ba8e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer.js
    @@ -0,0 +1,112 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.MenuSeparator}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.MenuSeparatorRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Renderer for menu separators.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.MenuSeparatorRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.MenuSeparatorRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.MenuSeparatorRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.MenuSeparatorRenderer.CSS_CLASS = goog.getCssName('goog-menuseparator');
    +
    +
    +/**
    + * Returns an empty, styled menu separator DIV.  Overrides {@link
    + * goog.ui.ControlRenderer#createDom}.
    + * @param {goog.ui.Control} separator goog.ui.Separator to render.
    + * @return {!Element} Root element for the separator.
    + * @override
    + */
    +goog.ui.MenuSeparatorRenderer.prototype.createDom = function(separator) {
    +  return separator.getDomHelper().createDom('div', this.getCssClass());
    +};
    +
    +
    +/**
    + * Takes an existing element, and decorates it with the separator.  Overrides
    + * {@link goog.ui.ControlRenderer#decorate}.
    + * @param {goog.ui.Control} separator goog.ui.MenuSeparator to decorate the
    + *     element.
    + * @param {Element} element Element to decorate.
    + * @return {!Element} Decorated element.
    + * @override
    + */
    +goog.ui.MenuSeparatorRenderer.prototype.decorate = function(separator,
    +                                                            element) {
    +  // Normally handled in the superclass. But we don't call the superclass.
    +  if (element.id) {
    +    separator.setId(element.id);
    +  }
    +
    +  if (element.tagName == 'HR') {
    +    // Replace HR with separator.
    +    var hr = element;
    +    element = this.createDom(separator);
    +    goog.dom.insertSiblingBefore(element, hr);
    +    goog.dom.removeNode(hr);
    +  } else {
    +    goog.dom.classlist.add(element, this.getCssClass());
    +  }
    +  return element;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#setContent} to do nothing, since
    + * separators are empty.
    + * @param {Element} separator The separator's root element.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to be
    + *    set as the separators's content (ignored).
    + * @override
    + */
    +goog.ui.MenuSeparatorRenderer.prototype.setContent = function(separator,
    +                                                              content) {
    +  // Do nothing.  Separators are empty.
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.MenuSeparatorRenderer.prototype.getCssClass = function() {
    +  return goog.ui.MenuSeparatorRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.html
    new file mode 100644
    index 0000000000..4a3947d273
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author mfrederick@google.com (Michael Frederick)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for MenuSeparatorRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.MenuSeparatorRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +   <!-- A menu separator to decorate -->
    +   <div id="separator" class="goog-menuseparator">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.js
    new file mode 100644
    index 0000000000..8caaf03408
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/menuseparatorrenderer_test.js
    @@ -0,0 +1,43 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.MenuSeparatorRendererTest');
    +goog.setTestOnly('goog.ui.MenuSeparatorRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.MenuSeparator');
    +goog.require('goog.ui.MenuSeparatorRenderer');
    +
    +var sandbox;
    +var originalSandbox;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  originalSandbox = sandbox.cloneNode(true);
    +}
    +
    +function tearDown() {
    +  sandbox.parentNode.replaceChild(originalSandbox, sandbox);
    +}
    +
    +function testDecorate() {
    +  var separator = new goog.ui.MenuSeparator();
    +  var dummyId = 'foo';
    +  separator.setId(dummyId);
    +  assertEquals(dummyId, separator.getId());
    +  var renderer = new goog.ui.MenuSeparatorRenderer();
    +  renderer.decorate(separator, goog.dom.getElement('separator'));
    +  assertEquals('separator', separator.getId());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor.js b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor.js
    new file mode 100644
    index 0000000000..10c1737991
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor.js
    @@ -0,0 +1,72 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of goog.ui.MockActivityMonitor.
    + */
    +
    +goog.provide('goog.ui.MockActivityMonitor');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.ActivityMonitor');
    +
    +
    +
    +/**
    + * A mock implementation of goog.ui.ActivityMonitor for unit testing. Clients
    + * of this class should override goog.now to return a synthetic time from
    + * the unit test.
    + * @constructor
    + * @extends {goog.ui.ActivityMonitor}
    + * @final
    + */
    +goog.ui.MockActivityMonitor = function() {
    +  goog.ui.MockActivityMonitor.base(this, 'constructor');
    +
    +  /**
    +   * Tracks whether an event has been fired. Used by simulateEvent.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.eventFired_ = false;
    +};
    +goog.inherits(goog.ui.MockActivityMonitor, goog.ui.ActivityMonitor);
    +
    +
    +/**
    + * Simulates an event that updates the user to being non-idle.
    + * @param {goog.events.EventType=} opt_type The type of event that made the user
    + *     not idle. If not specified, defaults to MOUSEMOVE.
    + */
    +goog.ui.MockActivityMonitor.prototype.simulateEvent = function(opt_type) {
    +  var eventTime = goog.now();
    +  var eventType = opt_type || goog.events.EventType.MOUSEMOVE;
    +
    +  this.eventFired_ = false;
    +  this.updateIdleTime(eventTime, eventType);
    +
    +  if (!this.eventFired_) {
    +    this.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.ui.MockActivityMonitor.prototype.dispatchEvent = function(e) {
    +  var rv = goog.ui.MockActivityMonitor.base(this, 'dispatchEvent', e);
    +  this.eventFired_ = true;
    +  return rv;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.html b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.html
    new file mode 100644
    index 0000000000..2b3eccbcda
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.html
    @@ -0,0 +1,19 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.ui.MockActivityMonitor</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.ui.MockActivityMonitorTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.js b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.js
    new file mode 100644
    index 0000000000..b0405ec387
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/mockactivitymonitor_test.js
    @@ -0,0 +1,94 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Tests for goog.ui.MockActivityMonitorTest.
    + * @author nnaze@google.com (Nathan Naze)
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.ui.MockActivityMonitorTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.ActivityMonitor');
    +goog.require('goog.ui.MockActivityMonitor');
    +
    +goog.setTestOnly('goog.ui.MockActivityMonitorTest');
    +
    +var googNow = goog.now;
    +var monitor;
    +var recordedFunction;
    +var replacer;
    +
    +function setUp() {
    +  monitor = new goog.ui.MockActivityMonitor();
    +  recordedFunction = goog.testing.recordFunction();
    +
    +  goog.events.listen(
    +      monitor,
    +      goog.ui.ActivityMonitor.Event.ACTIVITY,
    +      recordedFunction);
    +}
    +
    +function tearDown() {
    +  goog.dispose(monitor);
    +  goog.now = googNow;
    +}
    +
    +function testEventFireSameTime() {
    +  goog.now = goog.functions.constant(1000);
    +
    +  monitor.simulateEvent();
    +  assertEquals(1, recordedFunction.getCallCount());
    +
    +  monitor.simulateEvent();
    +  assertEquals(2, recordedFunction.getCallCount());
    +}
    +
    +function testEventFireDifferingTime() {
    +  goog.now = goog.functions.constant(1000);
    +  monitor.simulateEvent();
    +  assertEquals(1, recordedFunction.getCallCount());
    +
    +  goog.now = goog.functions.constant(1001);
    +  monitor.simulateEvent();
    +  assertEquals(2, recordedFunction.getCallCount());
    +}
    +
    +function testDispatchEventReturnValue() {
    +  assertTrue(monitor.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY));
    +  assertEquals(1, recordedFunction.getCallCount());
    +}
    +
    +function testDispatchEventPreventDefault() {
    +  // Undo the listen call in setUp.
    +  goog.events.unlisten(
    +      monitor,
    +      goog.ui.ActivityMonitor.Event.ACTIVITY,
    +      recordedFunction);
    +
    +  // Listen with a function that cancels the event.
    +  goog.events.listen(
    +      monitor,
    +      goog.ui.ActivityMonitor.Event.ACTIVITY,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +
    +  assertFalse(monitor.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/modalpopup.js b/mapbox-gl/closure-library/closure/goog/ui/modalpopup.js
    new file mode 100644
    index 0000000000..a9953c0a99
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/modalpopup.js
    @@ -0,0 +1,747 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for showing simple modal popup.
    + * @author chrishenry@google.com (Chris Henry)
    + */
    +
    +goog.provide('goog.ui.ModalPopup');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.dom.iframe');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.FocusHandler');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Base class for modal popup UI components. This can also be used as
    + * a standalone component to render a modal popup with an empty div.
    + *
    + * WARNING: goog.ui.ModalPopup is only guaranteed to work when it is rendered
    + * directly in the 'body' element.
    + *
    + * The Html structure of the modal popup is:
    + * <pre>
    + *  Element         Function              Class-name, goog-modalpopup = default
    + * ----------------------------------------------------------------------------
    + * - iframe         Iframe mask           goog-modalpopup-bg
    + * - div            Background mask       goog-modalpopup-bg
    + * - div            Modal popup area      goog-modalpopup
    + * - span           Tab catcher
    + * </pre>
    + * @constructor
    + * @param {boolean=} opt_useIframeMask Work around windowed controls z-index
    + *     issue by using an iframe instead of a div for bg element.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link
    + *     goog.ui.Component} for semantics.
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.ModalPopup = function(opt_useIframeMask, opt_domHelper) {
    +  goog.ui.ModalPopup.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * Whether the modal popup should use an iframe as the background
    +   * element to work around z-order issues.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useIframeMask_ = !!opt_useIframeMask;
    +
    +  /**
    +   * The element that had focus before the popup was displayed.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.lastFocus_ = null;
    +};
    +goog.inherits(goog.ui.ModalPopup, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.ModalPopup);
    +
    +
    +/**
    + * Focus handler. It will be initialized in enterDocument.
    + * @type {goog.events.FocusHandler}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.focusHandler_ = null;
    +
    +
    +/**
    + * Whether the modal popup is visible.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.visible_ = false;
    +
    +
    +/**
    + * Element for the background which obscures the UI and blocks events.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.bgEl_ = null;
    +
    +
    +/**
    + * Iframe element that is only used for IE as a workaround to keep select-type
    + * elements from burning through background.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.bgIframeEl_ = null;
    +
    +
    +/**
    + * Element used to catch focus and prevent the user from tabbing out
    + * of the popup.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.tabCatcherElement_ = null;
    +
    +
    +/**
    + * Whether the modal popup is in the process of wrapping focus from the top of
    + * the popup to the last tabbable element.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.backwardTabWrapInProgress_ = false;
    +
    +
    +/**
    + * Transition to show the popup.
    + * @type {goog.fx.Transition}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.popupShowTransition_;
    +
    +
    +/**
    + * Transition to hide the popup.
    + * @type {goog.fx.Transition}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.popupHideTransition_;
    +
    +
    +/**
    + * Transition to show the background.
    + * @type {goog.fx.Transition}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.bgShowTransition_;
    +
    +
    +/**
    + * Transition to hide the background.
    + * @type {goog.fx.Transition}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.bgHideTransition_;
    +
    +
    +/**
    + * The elements set to aria-hidden when the popup was made visible.
    + * @type {Array<!Element>}
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.hiddenElements_;
    +
    +
    +/**
    + * @return {string} Base CSS class for this component.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.getCssClass = function() {
    +  return goog.getCssName('goog-modalpopup');
    +};
    +
    +
    +/**
    + * Returns the background iframe mask element, if any.
    + * @return {Element} The background iframe mask element, may return
    + *     null/undefined if the modal popup does not use iframe mask.
    + */
    +goog.ui.ModalPopup.prototype.getBackgroundIframe = function() {
    +  return this.bgIframeEl_;
    +};
    +
    +
    +/**
    + * Returns the background mask element.
    + * @return {Element} The background mask element.
    + */
    +goog.ui.ModalPopup.prototype.getBackgroundElement = function() {
    +  return this.bgEl_;
    +};
    +
    +
    +/**
    + * Creates the initial DOM representation for the modal popup.
    + * @override
    + */
    +goog.ui.ModalPopup.prototype.createDom = function() {
    +  // Create the modal popup element, and make sure it's hidden.
    +  goog.ui.ModalPopup.base(this, 'createDom');
    +
    +  var element = this.getElement();
    +  goog.asserts.assert(element);
    +  var allClasses = goog.string.trim(this.getCssClass()).split(' ');
    +  goog.dom.classlist.addAll(element, allClasses);
    +  goog.dom.setFocusableTabIndex(element, true);
    +  goog.style.setElementShown(element, false);
    +
    +  // Manages the DOM for background mask elements.
    +  this.manageBackgroundDom_();
    +  this.createTabCatcher_();
    +};
    +
    +
    +/**
    + * Creates and disposes of the DOM for background mask elements.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.manageBackgroundDom_ = function() {
    +  if (this.useIframeMask_ && !this.bgIframeEl_) {
    +    // IE renders the iframe on top of the select elements while still
    +    // respecting the z-index of the other elements on the page.  See
    +    // http://support.microsoft.com/kb/177378 for more information.
    +    // Flash and other controls behave in similar ways for other browsers
    +    this.bgIframeEl_ = goog.dom.iframe.createBlank(this.getDomHelper());
    +    this.bgIframeEl_.className = goog.getCssName(this.getCssClass(), 'bg');
    +    goog.style.setElementShown(this.bgIframeEl_, false);
    +    goog.style.setOpacity(this.bgIframeEl_, 0);
    +  }
    +
    +  // Create the backgound mask, initialize its opacity, and make sure it's
    +  // hidden.
    +  if (!this.bgEl_) {
    +    this.bgEl_ = this.getDomHelper().createDom(
    +        'div', goog.getCssName(this.getCssClass(), 'bg'));
    +    goog.style.setElementShown(this.bgEl_, false);
    +  }
    +};
    +
    +
    +/**
    + * Creates the tab catcher element.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.createTabCatcher_ = function() {
    +  // Creates tab catcher element.
    +  if (!this.tabCatcherElement_) {
    +    this.tabCatcherElement_ = this.getDomHelper().createElement('span');
    +    goog.style.setElementShown(this.tabCatcherElement_, false);
    +    goog.dom.setFocusableTabIndex(this.tabCatcherElement_, true);
    +    this.tabCatcherElement_.style.position = 'absolute';
    +  }
    +};
    +
    +
    +/**
    + * Allow a shift-tab from the top of the modal popup to the last tabbable
    + * element by moving focus to the tab catcher. This should be called after
    + * catching a wrapping shift-tab event and before allowing it to propagate, so
    + * that focus will land on the last tabbable element before the tab catcher.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.setupBackwardTabWrap = function() {
    +  this.backwardTabWrapInProgress_ = true;
    +  try {
    +    this.tabCatcherElement_.focus();
    +  } catch (e) {
    +    // Swallow this. IE can throw an error if the element can not be focused.
    +  }
    +  // Reset the flag on a timer in case anything goes wrong with the followup
    +  // event.
    +  goog.Timer.callOnce(this.resetBackwardTabWrap_, 0, this);
    +};
    +
    +
    +/**
    + * Resets the backward tab wrap flag.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.resetBackwardTabWrap_ = function() {
    +  this.backwardTabWrapInProgress_ = false;
    +};
    +
    +
    +/**
    + * Renders the background mask.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.renderBackground_ = function() {
    +  goog.asserts.assert(!!this.bgEl_, 'Background element must not be null.');
    +  if (this.bgIframeEl_) {
    +    goog.dom.insertSiblingBefore(this.bgIframeEl_, this.getElement());
    +  }
    +  goog.dom.insertSiblingBefore(this.bgEl_, this.getElement());
    +};
    +
    +
    +/** @override */
    +goog.ui.ModalPopup.prototype.canDecorate = function(element) {
    +  // Assume we can decorate any DIV.
    +  return !!element && element.tagName == goog.dom.TagName.DIV;
    +};
    +
    +
    +/** @override */
    +goog.ui.ModalPopup.prototype.decorateInternal = function(element) {
    +  // Decorate the modal popup area element.
    +  goog.ui.ModalPopup.base(this, 'decorateInternal', element);
    +  var allClasses = goog.string.trim(this.getCssClass()).split(' ');
    +
    +  goog.dom.classlist.addAll(
    +      goog.asserts.assert(this.getElement()),
    +      allClasses);
    +
    +  // Create the background mask...
    +  this.manageBackgroundDom_();
    +  this.createTabCatcher_();
    +
    +  // Make sure the decorated modal popup is hidden.
    +  goog.style.setElementShown(this.getElement(), false);
    +};
    +
    +
    +/** @override */
    +goog.ui.ModalPopup.prototype.enterDocument = function() {
    +  this.renderBackground_();
    +  goog.ui.ModalPopup.base(this, 'enterDocument');
    +
    +  goog.dom.insertSiblingAfter(this.tabCatcherElement_, this.getElement());
    +
    +  this.focusHandler_ = new goog.events.FocusHandler(
    +      this.getDomHelper().getDocument());
    +
    +  // We need to watch the entire document so that we can detect when the
    +  // focus is moved out of this modal popup.
    +  this.getHandler().listen(
    +      this.focusHandler_, goog.events.FocusHandler.EventType.FOCUSIN,
    +      this.onFocus);
    +  this.setA11YDetectBackground(false);
    +};
    +
    +
    +/** @override */
    +goog.ui.ModalPopup.prototype.exitDocument = function() {
    +  if (this.isVisible()) {
    +    this.setVisible(false);
    +  }
    +
    +  goog.dispose(this.focusHandler_);
    +
    +  goog.ui.ModalPopup.base(this, 'exitDocument');
    +  goog.dom.removeNode(this.bgIframeEl_);
    +  goog.dom.removeNode(this.bgEl_);
    +  goog.dom.removeNode(this.tabCatcherElement_);
    +};
    +
    +
    +/**
    + * Sets the visibility of the modal popup box and focus to the popup.
    + * @param {boolean} visible Whether the modal popup should be visible.
    + */
    +goog.ui.ModalPopup.prototype.setVisible = function(visible) {
    +  goog.asserts.assert(
    +      this.isInDocument(), 'ModalPopup must be rendered first.');
    +
    +  if (visible == this.visible_) {
    +    return;
    +  }
    +
    +  if (this.popupShowTransition_) this.popupShowTransition_.stop();
    +  if (this.bgShowTransition_) this.bgShowTransition_.stop();
    +  if (this.popupHideTransition_) this.popupHideTransition_.stop();
    +  if (this.bgHideTransition_) this.bgHideTransition_.stop();
    +
    +  if (this.isInDocument()) {
    +    this.setA11YDetectBackground(visible);
    +  }
    +  if (visible) {
    +    this.show_();
    +  } else {
    +    this.hide_();
    +  }
    +};
    +
    +
    +/**
    + * Sets aria-hidden on the rest of the page to restrict screen reader focus.
    + * Top-level elements with an explicit aria-hidden state are not altered.
    + * @param {boolean} hide Whether to hide or show the rest of the page.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.setA11YDetectBackground = function(hide) {
    +  if (hide) {
    +    if (!this.hiddenElements_) {
    +      this.hiddenElements_ = [];
    +    }
    +    var dom = this.getDomHelper();
    +    var topLevelChildren = dom.getChildren(dom.getDocument().body);
    +    for (var i = 0; i < topLevelChildren.length; i++) {
    +      var child = topLevelChildren[i];
    +      if (child != this.getElementStrict() &&
    +          !goog.a11y.aria.getState(child, goog.a11y.aria.State.HIDDEN)) {
    +        goog.a11y.aria.setState(child, goog.a11y.aria.State.HIDDEN, true);
    +        this.hiddenElements_.push(child);
    +      }
    +    }
    +  } else if (this.hiddenElements_) {
    +    for (var i = 0; i < this.hiddenElements_.length; i++) {
    +      goog.a11y.aria.removeState(
    +          this.hiddenElements_[i], goog.a11y.aria.State.HIDDEN);
    +    }
    +    this.hiddenElements_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Sets the transitions to show and hide the popup and background.
    + * @param {!goog.fx.Transition} popupShowTransition Transition to show the
    + *     popup.
    + * @param {!goog.fx.Transition} popupHideTransition Transition to hide the
    + *     popup.
    + * @param {!goog.fx.Transition} bgShowTransition Transition to show
    + *     the background.
    + * @param {!goog.fx.Transition} bgHideTransition Transition to hide
    + *     the background.
    + */
    +goog.ui.ModalPopup.prototype.setTransition = function(popupShowTransition,
    +    popupHideTransition, bgShowTransition, bgHideTransition) {
    +  this.popupShowTransition_ = popupShowTransition;
    +  this.popupHideTransition_ = popupHideTransition;
    +  this.bgShowTransition_ = bgShowTransition;
    +  this.bgHideTransition_ = bgHideTransition;
    +};
    +
    +
    +/**
    + * Shows the popup.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.show_ = function() {
    +  if (!this.dispatchEvent(goog.ui.PopupBase.EventType.BEFORE_SHOW)) {
    +    return;
    +  }
    +
    +  try {
    +    this.lastFocus_ = this.getDomHelper().getDocument().activeElement;
    +  } catch (e) {
    +    // Focus-related actions often throw exceptions.
    +    // Sample past issue: https://bugzilla.mozilla.org/show_bug.cgi?id=656283
    +  }
    +  this.resizeBackground_();
    +  this.reposition();
    +
    +  // Listen for keyboard and resize events while the modal popup is visible.
    +  this.getHandler().listen(
    +      this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
    +      this.resizeBackground_);
    +
    +  this.showPopupElement_(true);
    +  this.focus();
    +  this.visible_ = true;
    +
    +  if (this.popupShowTransition_ && this.bgShowTransition_) {
    +    goog.events.listenOnce(
    +        /** @type {!goog.events.EventTarget} */ (this.popupShowTransition_),
    +        goog.fx.Transition.EventType.END, this.onShow, false, this);
    +    this.bgShowTransition_.play();
    +    this.popupShowTransition_.play();
    +  } else {
    +    this.onShow();
    +  }
    +};
    +
    +
    +/**
    + * Hides the popup.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.hide_ = function() {
    +  if (!this.dispatchEvent(goog.ui.PopupBase.EventType.BEFORE_HIDE)) {
    +    return;
    +  }
    +
    +  // Stop listening for keyboard and resize events while the modal
    +  // popup is hidden.
    +  this.getHandler().unlisten(
    +      this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
    +      this.resizeBackground_);
    +
    +  // Set visibility to hidden even if there is a transition. This
    +  // reduces complexity in subclasses who may want to override
    +  // setVisible (such as goog.ui.Dialog).
    +  this.visible_ = false;
    +
    +  if (this.popupHideTransition_ && this.bgHideTransition_) {
    +    goog.events.listenOnce(
    +        /** @type {!goog.events.EventTarget} */ (this.popupHideTransition_),
    +        goog.fx.Transition.EventType.END, this.onHide, false, this);
    +    this.bgHideTransition_.play();
    +    // The transition whose END event you are listening to must be played last
    +    // to prevent errors when disposing on hide event, which occur on browsers
    +    // that do not support CSS3 transitions.
    +    this.popupHideTransition_.play();
    +  } else {
    +    this.onHide();
    +  }
    +
    +  this.returnFocus_();
    +};
    +
    +
    +/**
    + * Attempts to return the focus back to the element that had it before the popup
    + * was opened.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.returnFocus_ = function() {
    +  try {
    +    var dom = this.getDomHelper();
    +    var body = dom.getDocument().body;
    +    var active = dom.getDocument().activeElement || body;
    +    if (!this.lastFocus_ || this.lastFocus_ == body) {
    +      this.lastFocus_ = null;
    +      return;
    +    }
    +    // We only want to move the focus if we actually have it, i.e.:
    +    //  - if we immediately hid the popup the focus should have moved to the
    +    // body element
    +    //  - if there is a hiding transition in progress the focus would still be
    +    // within the dialog and it is safe to move it if the current focused
    +    // element is a child of the dialog
    +    if (active == body || dom.contains(this.getElement(), active)) {
    +      this.lastFocus_.focus();
    +    }
    +  } catch (e) {
    +    // Swallow this. IE can throw an error if the element can not be focused.
    +  }
    +  // Explicitly want to null this out even if there was an error focusing to
    +  // avoid bleed over between dialog invocations.
    +  this.lastFocus_ = null;
    +};
    +
    +
    +/**
    + * Shows or hides the popup element.
    + * @param {boolean} visible Shows the popup element if true, hides if false.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.showPopupElement_ = function(visible) {
    +  if (this.bgIframeEl_) {
    +    goog.style.setElementShown(this.bgIframeEl_, visible);
    +  }
    +  if (this.bgEl_) {
    +    goog.style.setElementShown(this.bgEl_, visible);
    +  }
    +  goog.style.setElementShown(this.getElement(), visible);
    +  goog.style.setElementShown(this.tabCatcherElement_, visible);
    +};
    +
    +
    +/**
    + * Called after the popup is shown. If there is a transition, this
    + * will be called after the transition completed or stopped.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.onShow = function() {
    +  this.dispatchEvent(goog.ui.PopupBase.EventType.SHOW);
    +};
    +
    +
    +/**
    + * Called after the popup is hidden. If there is a transition, this
    + * will be called after the transition completed or stopped.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.onHide = function() {
    +  this.showPopupElement_(false);
    +  this.dispatchEvent(goog.ui.PopupBase.EventType.HIDE);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the modal popup is visible.
    + */
    +goog.ui.ModalPopup.prototype.isVisible = function() {
    +  return this.visible_;
    +};
    +
    +
    +/**
    + * Focuses on the modal popup.
    + */
    +goog.ui.ModalPopup.prototype.focus = function() {
    +  this.focusElement_();
    +};
    +
    +
    +/**
    + * Make the background element the size of the document.
    + *
    + * NOTE(user): We must hide the background element before measuring the
    + * document, otherwise the size of the background will stop the document from
    + * shrinking to fit a smaller window.  This does cause a slight flicker in Linux
    + * browsers, but should not be a common scenario.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.resizeBackground_ = function() {
    +  if (this.bgIframeEl_) {
    +    goog.style.setElementShown(this.bgIframeEl_, false);
    +  }
    +  if (this.bgEl_) {
    +    goog.style.setElementShown(this.bgEl_, false);
    +  }
    +
    +  var doc = this.getDomHelper().getDocument();
    +  var win = goog.dom.getWindow(doc) || window;
    +
    +  // Take the max of document height and view height, in case the document does
    +  // not fill the viewport. Read from both the body element and the html element
    +  // to account for browser differences in treatment of absolutely-positioned
    +  // content.
    +  var viewSize = goog.dom.getViewportSize(win);
    +  var w = Math.max(viewSize.width,
    +      Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth));
    +  var h = Math.max(viewSize.height,
    +      Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight));
    +
    +  if (this.bgIframeEl_) {
    +    goog.style.setElementShown(this.bgIframeEl_, true);
    +    goog.style.setSize(this.bgIframeEl_, w, h);
    +  }
    +  if (this.bgEl_) {
    +    goog.style.setElementShown(this.bgEl_, true);
    +    goog.style.setSize(this.bgEl_, w, h);
    +  }
    +};
    +
    +
    +/**
    + * Centers the modal popup in the viewport, taking scrolling into account.
    + */
    +goog.ui.ModalPopup.prototype.reposition = function() {
    +  // TODO(chrishenry): Make this use goog.positioning as in goog.ui.PopupBase?
    +
    +  // Get the current viewport to obtain the scroll offset.
    +  var doc = this.getDomHelper().getDocument();
    +  var win = goog.dom.getWindow(doc) || window;
    +  if (goog.style.getComputedPosition(this.getElement()) == 'fixed') {
    +    var x = 0;
    +    var y = 0;
    +  } else {
    +    var scroll = this.getDomHelper().getDocumentScroll();
    +    var x = scroll.x;
    +    var y = scroll.y;
    +  }
    +
    +  var popupSize = goog.style.getSize(this.getElement());
    +  var viewSize = goog.dom.getViewportSize(win);
    +
    +  // Make sure left and top are non-negatives.
    +  var left = Math.max(x + viewSize.width / 2 - popupSize.width / 2, 0);
    +  var top = Math.max(y + viewSize.height / 2 - popupSize.height / 2, 0);
    +  goog.style.setPosition(this.getElement(), left, top);
    +
    +  // We place the tab catcher at the same position as the dialog to
    +  // prevent IE from scrolling when users try to tab out of the dialog.
    +  goog.style.setPosition(this.tabCatcherElement_, left, top);
    +};
    +
    +
    +/**
    + * Handles focus events.  Makes sure that if the user tabs past the
    + * elements in the modal popup, the focus wraps back to the beginning, and that
    + * if the user shift-tabs past the front of the modal popup, focus wraps around
    + * to the end.
    + * @param {goog.events.BrowserEvent} e Browser's event object.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.onFocus = function(e) {
    +  if (this.backwardTabWrapInProgress_) {
    +    this.resetBackwardTabWrap_();
    +  } else if (e.target == this.tabCatcherElement_) {
    +    goog.Timer.callOnce(this.focusElement_, 0, this);
    +  }
    +};
    +
    +
    +/**
    + * Returns the magic tab catcher element used to detect when the user has
    + * rolled focus off of the popup content.  It is automatically created during
    + * the createDom method() and can be used by subclasses to implement custom
    + * tab-loop behavior.
    + * @return {Element} The tab catcher element.
    + * @protected
    + */
    +goog.ui.ModalPopup.prototype.getTabCatcherElement = function() {
    +  return this.tabCatcherElement_;
    +};
    +
    +
    +/**
    + * Moves the focus to the modal popup.
    + * @private
    + */
    +goog.ui.ModalPopup.prototype.focusElement_ = function() {
    +  try {
    +    if (goog.userAgent.IE) {
    +      // In IE, we must first focus on the body or else focussing on a
    +      // sub-element will not work.
    +      this.getDomHelper().getDocument().body.focus();
    +    }
    +    this.getElement().focus();
    +  } catch (e) {
    +    // Swallow this. IE can throw an error if the element can not be focused.
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.ModalPopup.prototype.disposeInternal = function() {
    +  goog.dispose(this.popupShowTransition_);
    +  this.popupShowTransition_ = null;
    +
    +  goog.dispose(this.popupHideTransition_);
    +  this.popupHideTransition_ = null;
    +
    +  goog.dispose(this.bgShowTransition_);
    +  this.bgShowTransition_ = null;
    +
    +  goog.dispose(this.bgHideTransition_);
    +  this.bgHideTransition_ = null;
    +
    +  goog.ui.ModalPopup.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.html b/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.html
    new file mode 100644
    index 0000000000..e1817ce836
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ModalPopup
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ModalPopupTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="main">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.js b/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.js
    new file mode 100644
    index 0000000000..f45f8c1bf5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/modalpopup_test.js
    @@ -0,0 +1,439 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ModalPopupTest');
    +goog.setTestOnly('goog.ui.ModalPopupTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.css3');
    +goog.require('goog.string');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ModalPopup');
    +goog.require('goog.ui.PopupBase');
    +
    +var popup;
    +var main;
    +var mockClock;
    +
    +
    +function setUp() {
    +  main = /** @type {!Element}*/ (goog.dom.getElement('main'));
    +  mockClock = new goog.testing.MockClock(true);
    +}
    +
    +
    +function tearDown() {
    +  goog.dispose(popup);
    +  mockClock.dispose();
    +  goog.a11y.aria.removeState(main, goog.a11y.aria.State.HIDDEN);
    +}
    +
    +
    +function testDispose() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  goog.dispose(popup);
    +  assertNull(goog.dom.getElementByClass('goog-modalpopup-bg'));
    +  assertNull(goog.dom.getElementByClass('goog-modalpopup'));
    +  assertEquals(0, goog.dom.getElementsByTagNameAndClass('span').length);
    +}
    +
    +
    +function testRenderWithoutIframeMask() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  assertEquals(0, goog.dom.getElementsByTagNameAndClass(
    +      'iframe', 'goog-modalpopup-bg').length);
    +
    +  var bg = goog.dom.getElementsByTagNameAndClass('div', 'goog-modalpopup-bg');
    +  assertEquals(1, bg.length);
    +  var content = goog.dom.getElementByClass('goog-modalpopup');
    +  assertNotNull(content);
    +  var tabCatcher = goog.dom.getElementsByTagNameAndClass('span');
    +  assertEquals(1, tabCatcher.length);
    +
    +  assertTrue(goog.dom.compareNodeOrder(bg[0], content) < 0);
    +  assertTrue(goog.dom.compareNodeOrder(content, tabCatcher[0]) < 0);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN))));
    +  popup.setVisible(true);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      popup.getElementStrict(), goog.a11y.aria.State.HIDDEN))));
    +  assertEquals('true', goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN));
    +  popup.setVisible(false);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN))));
    +}
    +
    +
    +function testRenderWithIframeMask() {
    +  popup = new goog.ui.ModalPopup(true);
    +  popup.render();
    +
    +  var iframe = goog.dom.getElementsByTagNameAndClass(
    +      'iframe', 'goog-modalpopup-bg');
    +  assertEquals(1, iframe.length);
    +  var bg = goog.dom.getElementsByTagNameAndClass('div', 'goog-modalpopup-bg');
    +  assertEquals(1, bg.length);
    +  var content = goog.dom.getElementByClass('goog-modalpopup');
    +  assertNotNull(content);
    +  var tabCatcher = goog.dom.getElementsByTagNameAndClass('span');
    +  assertEquals(1, tabCatcher.length);
    +
    +  assertTrue(goog.dom.compareNodeOrder(iframe[0], bg[0]) < 0);
    +  assertTrue(goog.dom.compareNodeOrder(bg[0], content) < 0);
    +  assertTrue(goog.dom.compareNodeOrder(content, tabCatcher[0]) < 0);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN))));
    +  popup.setVisible(true);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      popup.getElementStrict(), goog.a11y.aria.State.HIDDEN))));
    +  assertEquals('true', goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN));
    +  popup.setVisible(false);
    +  assertTrue(goog.string.isEmptyOrWhitespace(goog.string.makeSafe(goog.a11y.aria.getState(
    +      main, goog.a11y.aria.State.HIDDEN))));
    +}
    +
    +
    +function testRenderWithAriaState() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  goog.a11y.aria.setState(main, goog.a11y.aria.State.HIDDEN, true);
    +  popup.setVisible(true);
    +  assertEquals(
    +      'true', goog.a11y.aria.getState(main, goog.a11y.aria.State.HIDDEN));
    +  popup.setVisible(false);
    +  assertEquals(
    +      'true', goog.a11y.aria.getState(main, goog.a11y.aria.State.HIDDEN));
    +
    +  goog.a11y.aria.setState(main, goog.a11y.aria.State.HIDDEN, false);
    +  popup.setVisible(true);
    +  assertEquals(
    +      'false', goog.a11y.aria.getState(main, goog.a11y.aria.State.HIDDEN));
    +  popup.setVisible(false);
    +  assertEquals(
    +      'false', goog.a11y.aria.getState(main, goog.a11y.aria.State.HIDDEN));
    +}
    +
    +
    +function testRenderDoesNotShowAnyElement() {
    +  popup = new goog.ui.ModalPopup(true);
    +  popup.render();
    +
    +  var iframe = goog.dom.getElementsByTagNameAndClass(
    +      'iframe', 'goog-modalpopup-bg');
    +  assertFalse(goog.style.isElementShown(iframe[0]));
    +  var bg = goog.dom.getElementsByTagNameAndClass('div', 'goog-modalpopup-bg');
    +  assertFalse(goog.style.isElementShown(bg[0]));
    +  assertFalse(goog.style.isElementShown(
    +      goog.dom.getElementByClass('goog-modalpopup')));
    +  var tabCatcher = goog.dom.getElementsByTagNameAndClass('span');
    +  assertFalse(goog.style.isElementShown(tabCatcher[0]));
    +}
    +
    +
    +function testIframeOpacityIsSetToZero() {
    +  popup = new goog.ui.ModalPopup(true);
    +  popup.render();
    +
    +  var iframe = goog.dom.getElementsByTagNameAndClass(
    +      'iframe', 'goog-modalpopup-bg')[0];
    +  assertEquals(0, goog.style.getOpacity(iframe));
    +}
    +
    +
    +function testEventFiredOnShow() {
    +  popup = new goog.ui.ModalPopup(true);
    +  popup.render();
    +
    +  var beforeShowCallCount = 0;
    +  var beforeShowHandler = function() {
    +    beforeShowCallCount++;
    +  };
    +  var showCallCount = false;
    +  var showHandler = function() {
    +    assertEquals('BEFORE_SHOW is not dispatched before SHOW',
    +        1, beforeShowCallCount);
    +    showCallCount++;
    +  };
    +
    +  goog.events.listen(
    +      popup, goog.ui.PopupBase.EventType.BEFORE_SHOW, beforeShowHandler);
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, showHandler);
    +
    +  popup.setVisible(true);
    +
    +  assertEquals(1, beforeShowCallCount);
    +  assertEquals(1, showCallCount);
    +}
    +
    +
    +function testEventFiredOnHide() {
    +  popup = new goog.ui.ModalPopup(true);
    +  popup.render();
    +  popup.setVisible(true);
    +
    +  var beforeHideCallCount = 0;
    +  var beforeHideHandler = function() {
    +    beforeHideCallCount++;
    +  };
    +  var hideCallCount = false;
    +  var hideHandler = function() {
    +    assertEquals('BEFORE_HIDE is not dispatched before HIDE',
    +        1, beforeHideCallCount);
    +    hideCallCount++;
    +  };
    +
    +  goog.events.listen(
    +      popup, goog.ui.PopupBase.EventType.BEFORE_HIDE, beforeHideHandler);
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, hideHandler);
    +
    +  popup.setVisible(false);
    +
    +  assertEquals(1, beforeHideCallCount);
    +  assertEquals(1, hideCallCount);
    +}
    +
    +
    +function testShowEventFiredWithNoTransition() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  var showHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
    +    showHandlerCalled = true;
    +  });
    +
    +  popup.setVisible(true);
    +  assertTrue(showHandlerCalled);
    +}
    +
    +function testHideEventFiredWithNoTransition() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  var hideHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    hideHandlerCalled = true;
    +  });
    +
    +  popup.setVisible(true);
    +  popup.setVisible(false);
    +  assertTrue(hideHandlerCalled);
    +}
    +
    +function testTransitionsPlayedOnShow() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  var mockPopupShowTransition = new MockTransition();
    +  var mockPopupHideTransition = new MockTransition();
    +  var mockBgShowTransition = new MockTransition();
    +  var mockBgHideTransition = new MockTransition();
    +
    +  var showHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
    +    showHandlerCalled = true;
    +  });
    +
    +  popup.setTransition(mockPopupShowTransition, mockPopupHideTransition,
    +      mockBgShowTransition, mockBgHideTransition);
    +  assertFalse(mockPopupShowTransition.wasPlayed);
    +  assertFalse(mockBgShowTransition.wasPlayed);
    +
    +  popup.setVisible(true);
    +  assertTrue(mockPopupShowTransition.wasPlayed);
    +  assertTrue(mockBgShowTransition.wasPlayed);
    +
    +  assertFalse(showHandlerCalled);
    +  mockPopupShowTransition.dispatchEvent(goog.fx.Transition.EventType.END);
    +  assertTrue(showHandlerCalled);
    +}
    +
    +
    +function testTransitionsPlayedOnHide() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  var mockPopupShowTransition = new MockTransition();
    +  var mockPopupHideTransition = new MockTransition();
    +  var mockBgShowTransition = new MockTransition();
    +  var mockBgHideTransition = new MockTransition();
    +
    +  var hideHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    hideHandlerCalled = true;
    +  });
    +
    +  popup.setTransition(mockPopupShowTransition, mockPopupHideTransition,
    +      mockBgShowTransition, mockBgHideTransition);
    +  popup.setVisible(true);
    +  assertFalse(mockPopupHideTransition.wasPlayed);
    +  assertFalse(mockBgHideTransition.wasPlayed);
    +
    +  popup.setVisible(false);
    +  assertTrue(mockPopupHideTransition.wasPlayed);
    +  assertTrue(mockBgHideTransition.wasPlayed);
    +
    +  assertFalse(hideHandlerCalled);
    +  mockPopupHideTransition.dispatchEvent(goog.fx.Transition.EventType.END);
    +  assertTrue(hideHandlerCalled);
    +}
    +
    +
    +function testTransitionsAndDisposingOnHideWorks() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    popup.dispose();
    +  });
    +
    +  var popupShowTransition = goog.fx.css3.fadeIn(popup.getElement(),
    +      0.1 /* duration */);
    +  var popupHideTransition = goog.fx.css3.fadeOut(popup.getElement(),
    +      0.1 /* duration */);
    +  var bgShowTransition = goog.fx.css3.fadeIn(popup.getElement(),
    +      0.1 /* duration */);
    +  var bgHideTransition = goog.fx.css3.fadeOut(popup.getElement(),
    +      0.1 /* duration */);
    +
    +  popup.setTransition(popupShowTransition, popupHideTransition,
    +      bgShowTransition, bgHideTransition);
    +  popup.setVisible(true);
    +  popup.setVisible(false);
    +  // Nothing to assert. We only want to ensure that there is no error.
    +}
    +
    +
    +function testSetVisibleWorksCorrectlyWithTransitions() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +  popup.setTransition(
    +      goog.fx.css3.fadeIn(popup.getElement(), 1),
    +      goog.fx.css3.fadeIn(popup.getBackgroundElement(), 1),
    +      goog.fx.css3.fadeOut(popup.getElement(), 1),
    +      goog.fx.css3.fadeOut(popup.getBackgroundElement(), 1));
    +
    +  // Consecutive calls to setVisible works without needing to wait for
    +  // transition to finish.
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  popup.setVisible(false);
    +  assertFalse(popup.isVisible());
    +  mockClock.tick(1100);
    +
    +  // Calling setVisible(true) immediately changed the state to visible.
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  mockClock.tick(1100);
    +
    +  // Consecutive calls to setVisible, in opposite order.
    +  popup.setVisible(false);
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  mockClock.tick(1100);
    +
    +  // Calling setVisible(false) immediately changed the state to not visible.
    +  popup.setVisible(false);
    +  assertFalse(popup.isVisible());
    +  mockClock.tick(1100);
    +}
    +
    +
    +function testTransitionsDisposed() {
    +  popup = new goog.ui.ModalPopup();
    +  popup.render();
    +
    +  var transition = goog.fx.css3.fadeIn(popup.getElement(), 0.1 /* duration */);
    +
    +  var hideHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    hideHandlerCalled = true;
    +  });
    +
    +  popup.setTransition(transition, transition, transition, transition);
    +  popup.dispose();
    +
    +  transition.dispatchEvent(goog.fx.Transition.EventType.END);
    +  assertFalse(hideHandlerCalled);
    +}
    +
    +
    +function testBackgroundHeight() {
    +  // Insert an absolutely-positioned element larger than the viewport.
    +  var viewportSize = goog.dom.getViewportSize();
    +  var w = viewportSize.width * 2;
    +  var h = viewportSize.height * 2;
    +  var dummy = goog.dom.createElement('div');
    +  dummy.style.position = 'absolute';
    +  goog.style.setSize(dummy, w, h);
    +  document.body.appendChild(dummy);
    +
    +  try {
    +    popup = new goog.ui.ModalPopup();
    +    popup.render();
    +    popup.setVisible(true);
    +
    +    var size = goog.style.getSize(popup.getBackgroundElement());
    +    assertTrue('Background element must cover the size of the content',
    +        size.width >= w && size.height >= h);
    +  } finally {
    +    goog.dom.removeNode(dummy);
    +  }
    +}
    +
    +
    +function testSetupBackwardTabWrapResetsFlagAfterTimeout() {
    +  popup.setupBackwardTabWrap();
    +  assertTrue('Backward tab wrap should be in progress',
    +      popup.backwardTabWrapInProgress_);
    +  mockClock.tick(1);
    +  assertFalse('Backward tab wrap flag should be reset after delay',
    +      popup.backwardTabWrapInProgress_);
    +}
    +
    +
    +
    +/**
    + * @implements {goog.fx.Transition}
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +var MockTransition = function() {
    +  MockTransition.base(this, 'constructor');
    +  this.wasPlayed = false;
    +};
    +goog.inherits(MockTransition, goog.events.EventTarget);
    +
    +
    +MockTransition.prototype.play = function() {
    +  this.wasPlayed = true;
    +};
    +
    +
    +MockTransition.prototype.stop = goog.nullFunction;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer.js
    new file mode 100644
    index 0000000000..8129c789b7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer.js
    @@ -0,0 +1,209 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Native browser button renderer for {@link goog.ui.Button}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.NativeButtonRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.ButtonRenderer');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.Button}s.  Renders and decorates native HTML
    + * button elements.  Since native HTML buttons have built-in support for many
    + * features, overrides many expensive (and redundant) superclass methods to
    + * be no-ops.
    + * @constructor
    + * @extends {goog.ui.ButtonRenderer}
    + */
    +goog.ui.NativeButtonRenderer = function() {
    +  goog.ui.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.NativeButtonRenderer, goog.ui.ButtonRenderer);
    +goog.addSingletonGetter(goog.ui.NativeButtonRenderer);
    +
    +
    +/** @override */
    +goog.ui.NativeButtonRenderer.prototype.getAriaRole = function() {
    +  // Native buttons don't need ARIA roles to be recognized by screen readers.
    +  return undefined;
    +};
    +
    +
    +/**
    + * Returns the button's contents wrapped in a native HTML button element.  Sets
    + * the button's disabled attribute as needed.
    + * @param {goog.ui.Control} button Button to render.
    + * @return {Element} Root element for the button (a native HTML button element).
    + * @override
    + */
    +goog.ui.NativeButtonRenderer.prototype.createDom = function(button) {
    +  this.setUpNativeButton_(button);
    +  return button.getDomHelper().createDom('button', {
    +    'class': this.getClassNames(button).join(' '),
    +    'disabled': !button.isEnabled(),
    +    'title': button.getTooltip() || '',
    +    'value': button.getValue() || ''
    +  }, button.getCaption() || '');
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ButtonRenderer#canDecorate} by returning true only
    + * if the element is an HTML button.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.NativeButtonRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == 'BUTTON' ||
    +      (element.tagName == 'INPUT' && (element.type == 'button' ||
    +          element.type == 'submit' || element.type == 'reset'));
    +};
    +
    +
    +/** @override */
    +goog.ui.NativeButtonRenderer.prototype.decorate = function(button, element) {
    +  this.setUpNativeButton_(button);
    +  if (element.disabled) {
    +    // Add the marker class for the DISABLED state before letting the superclass
    +    // implementation decorate the element, so its state will be correct.
    +    var disabledClassName = goog.asserts.assertString(
    +        this.getClassForState(goog.ui.Component.State.DISABLED));
    +    goog.dom.classlist.add(element, disabledClassName);
    +  }
    +  return goog.ui.NativeButtonRenderer.superClass_.decorate.call(this, button,
    +      element);
    +};
    +
    +
    +/**
    + * Native buttons natively support BiDi and keyboard focus.
    + * @suppress {visibility} getHandler and performActionInternal
    + * @override
    + */
    +goog.ui.NativeButtonRenderer.prototype.initializeDom = function(button) {
    +  // WARNING:  This is a hack, and it is only applicable to native buttons,
    +  // which are special because they do natively what most goog.ui.Controls
    +  // do programmatically.  Do not use your renderer's initializeDom method
    +  // to hook up event handlers!
    +  button.getHandler().listen(button.getElement(), goog.events.EventType.CLICK,
    +      button.performActionInternal);
    +};
    +
    +
    +/**
    + * @override
    + * Native buttons don't support text selection.
    + */
    +goog.ui.NativeButtonRenderer.prototype.setAllowTextSelection =
    +    goog.nullFunction;
    +
    +
    +/**
    + * @override
    + * Native buttons natively support right-to-left rendering.
    + */
    +goog.ui.NativeButtonRenderer.prototype.setRightToLeft = goog.nullFunction;
    +
    +
    +/**
    + * @override
    + * Native buttons are always focusable as long as they are enabled.
    + */
    +goog.ui.NativeButtonRenderer.prototype.isFocusable = function(button) {
    +  return button.isEnabled();
    +};
    +
    +
    +/**
    + * @override
    + * Native buttons natively support keyboard focus.
    + */
    +goog.ui.NativeButtonRenderer.prototype.setFocusable = goog.nullFunction;
    +
    +
    +/**
    + * @override
    + * Native buttons also expose the DISABLED state in the HTML button's
    + * {@code disabled} attribute.
    + */
    +goog.ui.NativeButtonRenderer.prototype.setState = function(button, state,
    +    enable) {
    +  goog.ui.NativeButtonRenderer.superClass_.setState.call(this, button, state,
    +      enable);
    +  var element = button.getElement();
    +  if (element && state == goog.ui.Component.State.DISABLED) {
    +    element.disabled = enable;
    +  }
    +};
    +
    +
    +/**
    + * @override
    + * Native buttons store their value in the HTML button's {@code value}
    + * attribute.
    + */
    +goog.ui.NativeButtonRenderer.prototype.getValue = function(element) {
    +  // TODO(attila): Make this work on IE!  This never worked...
    +  // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +  // for a description of the problem.
    +  return element.value;
    +};
    +
    +
    +/**
    + * @override
    + * Native buttons also expose their value in the HTML button's {@code value}
    + * attribute.
    + */
    +goog.ui.NativeButtonRenderer.prototype.setValue = function(element, value) {
    +  if (element) {
    +    // TODO(attila): Make this work on IE!  This never worked...
    +    // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +    // for a description of the problem.
    +    element.value = value;
    +  }
    +};
    +
    +
    +/**
    + * @override
    + * Native buttons don't need ARIA states to support accessibility, so this is
    + * a no-op.
    + */
    +goog.ui.NativeButtonRenderer.prototype.updateAriaState = goog.nullFunction;
    +
    +
    +/**
    + * Sets up the button control such that it doesn't waste time adding
    + * functionality that is already natively supported by native browser
    + * buttons.
    + * @param {goog.ui.Control} button Button control to configure.
    + * @private
    + */
    +goog.ui.NativeButtonRenderer.prototype.setUpNativeButton_ = function(button) {
    +  button.setHandleMouseEvents(false);
    +  button.setAutoStates(goog.ui.Component.State.ALL, false);
    +  button.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.html
    new file mode 100644
    index 0000000000..5344c6f598
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.NativeButtonRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.NativeButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.js
    new file mode 100644
    index 0000000000..8b4ff07cdf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/nativebuttonrenderer_test.js
    @@ -0,0 +1,217 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.NativeButtonRendererTest');
    +goog.setTestOnly('goog.ui.NativeButtonRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.NativeButtonRenderer');
    +goog.require('goog.userAgent');
    +
    +var sandbox;
    +var renderer;
    +var expectedFailures;
    +var button;
    +
    +function setUpPage() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  renderer = goog.ui.NativeButtonRenderer.getInstance();
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function setUp() {
    +  button = new goog.ui.Button('Hello', renderer);
    +}
    +
    +function tearDown() {
    +  button.dispose();
    +  goog.dom.removeChildren(sandbox);
    +  expectedFailures.handleTearDown();
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Renderer must not be null', renderer);
    +}
    +
    +function testGetAriaRole() {
    +  assertUndefined('ARIA role must be undefined', renderer.getAriaRole());
    +}
    +
    +function testCreateDom() {
    +  button.setTooltip('Hello, world!');
    +  button.setValue('foo');
    +  var element = renderer.createDom(button);
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Element must be a button', 'BUTTON', element.tagName);
    +  assertSameElements('Button element must have expected class name',
    +      ['goog-button'], goog.dom.classlist.get(element));
    +  assertFalse('Button element must be enabled', element.disabled);
    +  assertEquals('Button element must have expected title', 'Hello, world!',
    +      element.title);
    +  // Expected to fail on IE.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE);
    +  try {
    +    // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +    // for a description of the problem.
    +    assertEquals('Button element must have expected value', 'foo',
    +        element.value);
    +    assertEquals('Button element must have expected contents', 'Hello',
    +        element.innerHTML);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  assertFalse('Button must not handle its own mouse events',
    +      button.isHandleMouseEvents());
    +  assertFalse('Button must not support the custom FOCUSED state',
    +      button.isSupportedState(goog.ui.Component.State.FOCUSED));
    +}
    +
    +function testCanDecorate() {
    +  sandbox.innerHTML =
    +      '<button id="buttonElement">Button</button>\n' +
    +      '<input id="inputButton" type="button" value="Input Button">\n' +
    +      '<input id="inputSubmit" type="submit" value="Input Submit">\n' +
    +      '<input id="inputReset" type="reset" value="Input Reset">\n' +
    +      '<input id="inputText" type="text" size="10">\n' +
    +      '<div id="divButton" class="goog-button">Hello</div>';
    +
    +  assertTrue('Must be able to decorate <button>',
    +      renderer.canDecorate(goog.dom.getElement('buttonElement')));
    +  assertTrue('Must be able to decorate <input type="button">',
    +      renderer.canDecorate(goog.dom.getElement('inputButton')));
    +  assertTrue('Must be able to decorate <input type="submit">',
    +      renderer.canDecorate(goog.dom.getElement('inputSubmit')));
    +  assertTrue('Must be able to decorate <input type="reset">',
    +      renderer.canDecorate(goog.dom.getElement('inputReset')));
    +  assertFalse('Must not be able to decorate <input type="text">',
    +      renderer.canDecorate(goog.dom.getElement('inputText')));
    +  assertFalse('Must not be able to decorate <div class="goog-button">',
    +      renderer.canDecorate(goog.dom.getElement('divButton')));
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML =
    +      '<button id="foo" title="Hello!" value="bar">Foo Button</button>\n' +
    +      '<button id="disabledButton" value="bah" disabled="disabled">Disabled' +
    +      '</button>';
    +
    +  var element = renderer.decorate(button, goog.dom.getElement('foo'));
    +  assertEquals('Decorated element must be as expected',
    +      goog.dom.getElement('foo'), element);
    +  assertEquals('Decorated button title must have expected value', 'Hello!',
    +      button.getTooltip());
    +  // Expected to fail on IE.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE);
    +  try {
    +    // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +    // for a description of the problem.
    +    assertEquals('Decorated button value must have expected value', 'bar',
    +        button.getValue());
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  assertFalse('Button must not handle its own mouse events',
    +      button.isHandleMouseEvents());
    +  assertFalse('Button must not support the custom FOCUSED state',
    +      button.isSupportedState(goog.ui.Component.State.FOCUSED));
    +
    +  element = renderer.decorate(button,
    +      goog.dom.getElement('disabledButton'));
    +  assertFalse('Decorated button must be disabled', button.isEnabled());
    +  assertSameElements('Decorated button must have expected class names',
    +      ['goog-button', 'goog-button-disabled'],
    +      goog.dom.classlist.get(element));
    +  // Expected to fail on IE.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE);
    +  try {
    +    // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +    // for a description of the problem.
    +    assertEquals('Decorated button value must have expected value', 'bah',
    +        button.getValue());
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +  assertFalse('Button must not handle its own mouse events',
    +      button.isHandleMouseEvents());
    +  assertFalse('Button must not support the custom FOCUSED state',
    +      button.isSupportedState(goog.ui.Component.State.FOCUSED));
    +}
    +
    +function testInitializeDom() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +  goog.events.listen(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +
    +  button.render(sandbox);
    +  goog.testing.events.fireClickSequence(button.getElement());
    +  assertEquals('Button must have dispatched ACTION on click', 1,
    +      dispatchedActionCount);
    +
    +  goog.events.unlisten(button, goog.ui.Component.EventType.ACTION,
    +      handleAction);
    +}
    +
    +function testIsFocusable() {
    +  assertTrue('Enabled button must be focusable',
    +      renderer.isFocusable(button));
    +  button.setEnabled(false);
    +  assertFalse('Disabled button must not be focusable',
    +      renderer.isFocusable(button));
    +}
    +
    +function testSetState() {
    +  button.render(sandbox);
    +  assertFalse('Button element must not be disabled',
    +      button.getElement().disabled);
    +  renderer.setState(button, goog.ui.Component.State.DISABLED, true);
    +  assertTrue('Button element must be disabled',
    +      button.getElement().disabled);
    +}
    +
    +function testGetValue() {
    +  sandbox.innerHTML = '<button id="foo" value="blah">Hello</button>';
    +  // Expected to fail on IE.
    +  expectedFailures.expectFailureFor(goog.userAgent.IE);
    +  try {
    +    // See http://www.fourmilab.ch/fourmilog/archives/2007-03/000824.html
    +    // for a description of the problem.
    +    assertEquals('Value must be as expected', 'blah',
    +        renderer.getValue(goog.dom.getElement('foo')));
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testSetValue() {
    +  button.render(sandbox);
    +  renderer.setValue(button.getElement(), 'What?');
    +  assertEquals('Button must have expected value', 'What?',
    +      renderer.getValue(button.getElement()));
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.NativeButtonRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/option.js b/mapbox-gl/closure-library/closure/goog/ui/option.js
    new file mode 100644
    index 0000000000..3b7f001735
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/option.js
    @@ -0,0 +1,68 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A menu item class that supports selection state.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.Option');
    +
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a menu option.  This is just a convenience class that
    + * extends {@link goog.ui.MenuItem} by making it selectable.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {*=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + */
    +goog.ui.Option = function(content, opt_model, opt_domHelper) {
    +  goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper);
    +  this.setSelectable(true);
    +};
    +goog.inherits(goog.ui.Option, goog.ui.MenuItem);
    +
    +
    +/**
    + * Performs the appropriate action when the option is activated by the user.
    + * Overrides the superclass implementation by not changing the selection state
    + * of the option and not dispatching any SELECTED events, for backwards
    + * compatibility with existing uses of this class.
    + * @param {goog.events.Event} e Mouse or key event that triggered the action.
    + * @return {boolean} True if the action was allowed to proceed, false otherwise.
    + * @override
    + */
    +goog.ui.Option.prototype.performActionInternal = function(e) {
    +  return this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Options.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-option'), function() {
    +      // Option defaults to using MenuItemRenderer.
    +      return new goog.ui.Option(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/palette.js b/mapbox-gl/closure-library/closure/goog/ui/palette.js
    new file mode 100644
    index 0000000000..8aeed61f0d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/palette.js
    @@ -0,0 +1,604 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A palette control.  A palette is a grid that the user can
    + * highlight or select via the keyboard or the mouse.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/palette.html
    + */
    +
    +goog.provide('goog.ui.Palette');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.math.Size');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.PaletteRenderer');
    +goog.require('goog.ui.SelectionModel');
    +
    +
    +
    +/**
    + * A palette is a grid of DOM nodes that the user can highlight or select via
    + * the keyboard or the mouse.  The selection state of the palette is controlled
    + * an ACTION event.  Event listeners may retrieve the selected item using the
    + * {@link #getSelectedItem} or {@link #getSelectedIndex} method.
    + *
    + * Use this class as the base for components like color palettes or emoticon
    + * pickers.  Use {@link #setContent} to set/change the items in the palette
    + * after construction.  See palette.html demo for example usage.
    + *
    + * @param {Array<Node>} items Array of DOM nodes to be displayed as items
    + *     in the palette grid (limited to one per cell).
    + * @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
    + *     decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Palette = function(items, opt_renderer, opt_domHelper) {
    +  goog.ui.Palette.base(this, 'constructor', items,
    +      opt_renderer || goog.ui.PaletteRenderer.getInstance(), opt_domHelper);
    +  this.setAutoStates(goog.ui.Component.State.CHECKED |
    +      goog.ui.Component.State.SELECTED | goog.ui.Component.State.OPENED, false);
    +
    +  /**
    +   * A fake component for dispatching events on palette cell changes.
    +   * @type {!goog.ui.Palette.CurrentCell_}
    +   * @private
    +   */
    +  this.currentCellControl_ = new goog.ui.Palette.CurrentCell_();
    +  this.currentCellControl_.setParentEventTarget(this);
    +
    +  /**
    +   * @private {number} The last highlighted index, or -1 if it never had one.
    +   */
    +  this.lastHighlightedIndex_ = -1;
    +};
    +goog.inherits(goog.ui.Palette, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.Palette);
    +
    +
    +/**
    + * Events fired by the palette object
    + * @enum {string}
    + */
    +goog.ui.Palette.EventType = {
    +  AFTER_HIGHLIGHT: goog.events.getUniqueId('afterhighlight')
    +};
    +
    +
    +/**
    + * Palette dimensions (columns x rows).  If the number of rows is undefined,
    + * it is calculated on first use.
    + * @type {goog.math.Size}
    + * @private
    + */
    +goog.ui.Palette.prototype.size_ = null;
    +
    +
    +/**
    + * Index of the currently highlighted item (-1 if none).
    + * @type {number}
    + * @private
    + */
    +goog.ui.Palette.prototype.highlightedIndex_ = -1;
    +
    +
    +/**
    + * Selection model controlling the palette's selection state.
    + * @type {goog.ui.SelectionModel}
    + * @private
    + */
    +goog.ui.Palette.prototype.selectionModel_ = null;
    +
    +
    +// goog.ui.Component / goog.ui.Control implementation.
    +
    +
    +/** @override */
    +goog.ui.Palette.prototype.disposeInternal = function() {
    +  goog.ui.Palette.superClass_.disposeInternal.call(this);
    +
    +  if (this.selectionModel_) {
    +    this.selectionModel_.dispose();
    +    this.selectionModel_ = null;
    +  }
    +
    +  this.size_ = null;
    +
    +  this.currentCellControl_.dispose();
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.Control#setContentInternal} by also updating the
    + * grid size and the selection model.  Considered protected.
    + * @param {goog.ui.ControlContent} content Array of DOM nodes to be displayed
    + *     as items in the palette grid (one item per cell).
    + * @protected
    + * @override
    + */
    +goog.ui.Palette.prototype.setContentInternal = function(content) {
    +  var items = /** @type {Array<Node>} */ (content);
    +  goog.ui.Palette.superClass_.setContentInternal.call(this, items);
    +
    +  // Adjust the palette size.
    +  this.adjustSize_();
    +
    +  // Add the items to the selection model, replacing previous items (if any).
    +  if (this.selectionModel_) {
    +    // We already have a selection model; just replace the items.
    +    this.selectionModel_.clear();
    +    this.selectionModel_.addItems(items);
    +  } else {
    +    // Create a selection model, initialize the items, and hook up handlers.
    +    this.selectionModel_ = new goog.ui.SelectionModel(items);
    +    this.selectionModel_.setSelectionHandler(goog.bind(this.selectItem_,
    +        this));
    +    this.getHandler().listen(this.selectionModel_,
    +        goog.events.EventType.SELECT, this.handleSelectionChange);
    +  }
    +
    +  // In all cases, clear the highlight.
    +  this.highlightedIndex_ = -1;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.Control#getCaption} to return the empty string,
    + * since palettes don't have text captions.
    + * @return {string} The empty string.
    + * @override
    + */
    +goog.ui.Palette.prototype.getCaption = function() {
    +  return '';
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.Control#setCaption} to be a no-op, since palettes
    + * don't have text captions.
    + * @param {string} caption Ignored.
    + * @override
    + */
    +goog.ui.Palette.prototype.setCaption = function(caption) {
    +  // Do nothing.
    +};
    +
    +
    +// Palette event handling.
    +
    +
    +/**
    + * Handles mouseover events.  Overrides {@link goog.ui.Control#handleMouseOver}
    + * by determining which palette item (if any) was moused over, highlighting it,
    + * and un-highlighting any previously-highlighted item.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + * @override
    + */
    +goog.ui.Palette.prototype.handleMouseOver = function(e) {
    +  goog.ui.Palette.superClass_.handleMouseOver.call(this, e);
    +
    +  var item = this.getRenderer().getContainingItem(this, e.target);
    +  if (item && e.relatedTarget && goog.dom.contains(item, e.relatedTarget)) {
    +    // Ignore internal mouse moves.
    +    return;
    +  }
    +
    +  if (item != this.getHighlightedItem()) {
    +    this.setHighlightedItem(item);
    +  }
    +};
    +
    +
    +/**
    + * Handles mousedown events.  Overrides {@link goog.ui.Control#handleMouseDown}
    + * by ensuring that the item on which the user moused down is highlighted.
    + * @param {goog.events.Event} e Mouse event to handle.
    + * @override
    + */
    +goog.ui.Palette.prototype.handleMouseDown = function(e) {
    +  goog.ui.Palette.superClass_.handleMouseDown.call(this, e);
    +
    +  if (this.isActive()) {
    +    // Make sure we move the highlight to the cell on which the user moused
    +    // down.
    +    var item = this.getRenderer().getContainingItem(this, e.target);
    +    if (item != this.getHighlightedItem()) {
    +      this.setHighlightedItem(item);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Selects the currently highlighted palette item (triggered by mouseup or by
    + * keyboard action).  Overrides {@link goog.ui.Control#performActionInternal}
    + * by selecting the highlighted item and dispatching an ACTION event.
    + * @param {goog.events.Event} e Mouse or key event that triggered the action.
    + * @return {boolean} True if the action was allowed to proceed, false otherwise.
    + * @override
    + */
    +goog.ui.Palette.prototype.performActionInternal = function(e) {
    +  var item = this.getHighlightedItem();
    +  if (item) {
    +    this.setSelectedItem(item);
    +    return goog.ui.Palette.base(this, 'performActionInternal', e);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Handles keyboard events dispatched while the palette has focus.  Moves the
    + * highlight on arrow keys, and selects the highlighted item on Enter or Space.
    + * Returns true if the event was handled, false otherwise.  In particular, if
    + * the user attempts to navigate out of the grid, the highlight isn't changed,
    + * and this method returns false; it is then up to the parent component to
    + * handle the event (e.g. by wrapping the highlight around).  Overrides {@link
    + * goog.ui.Control#handleKeyEvent}.
    + * @param {goog.events.KeyEvent} e Key event to handle.
    + * @return {boolean} True iff the key event was handled by the component.
    + * @override
    + */
    +goog.ui.Palette.prototype.handleKeyEvent = function(e) {
    +  var items = this.getContent();
    +  var numItems = items ? items.length : 0;
    +  var numColumns = this.size_.width;
    +
    +  // If the component is disabled or the palette is empty, bail.
    +  if (numItems == 0 || !this.isEnabled()) {
    +    return false;
    +  }
    +
    +  // User hit ENTER or SPACE; trigger action.
    +  if (e.keyCode == goog.events.KeyCodes.ENTER ||
    +      e.keyCode == goog.events.KeyCodes.SPACE) {
    +    return this.performActionInternal(e);
    +  }
    +
    +  // User hit HOME or END; move highlight.
    +  if (e.keyCode == goog.events.KeyCodes.HOME) {
    +    this.setHighlightedIndex(0);
    +    return true;
    +  } else if (e.keyCode == goog.events.KeyCodes.END) {
    +    this.setHighlightedIndex(numItems - 1);
    +    return true;
    +  }
    +
    +  // If nothing is highlighted, start from the selected index.  If nothing is
    +  // selected either, highlightedIndex is -1.
    +  var highlightedIndex = this.highlightedIndex_ < 0 ? this.getSelectedIndex() :
    +      this.highlightedIndex_;
    +
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.LEFT:
    +      // If the highlighted index is uninitialized, or is at the beginning, move
    +      // it to the end.
    +      if (highlightedIndex == -1 ||
    +          highlightedIndex == 0) {
    +        highlightedIndex = numItems;
    +      }
    +      this.setHighlightedIndex(highlightedIndex - 1);
    +      e.preventDefault();
    +      return true;
    +      break;
    +
    +    case goog.events.KeyCodes.RIGHT:
    +      // If the highlighted index at the end, move it to the beginning.
    +      if (highlightedIndex == numItems - 1) {
    +        highlightedIndex = -1;
    +      }
    +      this.setHighlightedIndex(highlightedIndex + 1);
    +      e.preventDefault();
    +      return true;
    +      break;
    +
    +    case goog.events.KeyCodes.UP:
    +      if (highlightedIndex == -1) {
    +        highlightedIndex = numItems + numColumns - 1;
    +      }
    +      if (highlightedIndex >= numColumns) {
    +        this.setHighlightedIndex(highlightedIndex - numColumns);
    +        e.preventDefault();
    +        return true;
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.DOWN:
    +      if (highlightedIndex == -1) {
    +        highlightedIndex = -numColumns;
    +      }
    +      if (highlightedIndex < numItems - numColumns) {
    +        this.setHighlightedIndex(highlightedIndex + numColumns);
    +        e.preventDefault();
    +        return true;
    +      }
    +      break;
    +  }
    +
    +  return false;
    +};
    +
    +
    +/**
    + * Handles selection change events dispatched by the selection model.
    + * @param {goog.events.Event} e Selection event to handle.
    + */
    +goog.ui.Palette.prototype.handleSelectionChange = function(e) {
    +  // No-op in the base class.
    +};
    +
    +
    +// Palette management.
    +
    +
    +/**
    + * Returns the size of the palette grid.
    + * @return {goog.math.Size} Palette size (columns x rows).
    + */
    +goog.ui.Palette.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * Sets the size of the palette grid to the given size.  Callers can either
    + * pass a single {@link goog.math.Size} or a pair of numbers (first the number
    + * of columns, then the number of rows) to this method.  In both cases, the
    + * number of rows is optional and will be calculated automatically if needed.
    + * It is an error to attempt to change the size of the palette after it has
    + * been rendered.
    + * @param {goog.math.Size|number} size Either a size object or the number of
    + *     columns.
    + * @param {number=} opt_rows The number of rows (optional).
    + */
    +goog.ui.Palette.prototype.setSize = function(size, opt_rows) {
    +  if (this.getElement()) {
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +
    +  this.size_ = goog.isNumber(size) ?
    +      new goog.math.Size(size, /** @type {number} */ (opt_rows)) : size;
    +
    +  // Adjust size, if needed.
    +  this.adjustSize_();
    +};
    +
    +
    +/**
    + * Returns the 0-based index of the currently highlighted palette item, or -1
    + * if no item is highlighted.
    + * @return {number} Index of the highlighted item (-1 if none).
    + */
    +goog.ui.Palette.prototype.getHighlightedIndex = function() {
    +  return this.highlightedIndex_;
    +};
    +
    +
    +/**
    + * Returns the currently highlighted palette item, or null if no item is
    + * highlighted.
    + * @return {Node} The highlighted item (undefined if none).
    + */
    +goog.ui.Palette.prototype.getHighlightedItem = function() {
    +  var items = this.getContent();
    +  return items && items[this.highlightedIndex_];
    +};
    +
    +
    +/**
    + * @return {Element} The highlighted cell.
    + * @private
    + */
    +goog.ui.Palette.prototype.getHighlightedCellElement_ = function() {
    +  return this.getRenderer().getCellForItem(this.getHighlightedItem());
    +};
    +
    +
    +/**
    + * Highlights the item at the given 0-based index, or removes the highlight
    + * if the argument is -1 or out of range.  Any previously-highlighted item
    + * will be un-highlighted.
    + * @param {number} index 0-based index of the item to highlight.
    + */
    +goog.ui.Palette.prototype.setHighlightedIndex = function(index) {
    +  if (index != this.highlightedIndex_) {
    +    this.highlightIndex_(this.highlightedIndex_, false);
    +    this.lastHighlightedIndex_ = this.highlightedIndex_;
    +    this.highlightedIndex_ = index;
    +    this.highlightIndex_(index, true);
    +    this.dispatchEvent(goog.ui.Palette.EventType.AFTER_HIGHLIGHT);
    +  }
    +};
    +
    +
    +/**
    + * Highlights the given item, or removes the highlight if the argument is null
    + * or invalid.  Any previously-highlighted item will be un-highlighted.
    + * @param {Node|undefined} item Item to highlight.
    + */
    +goog.ui.Palette.prototype.setHighlightedItem = function(item) {
    +  var items = /** @type {Array<Node>} */ (this.getContent());
    +  this.setHighlightedIndex(items ? goog.array.indexOf(items, item) : -1);
    +};
    +
    +
    +/**
    + * Returns the 0-based index of the currently selected palette item, or -1
    + * if no item is selected.
    + * @return {number} Index of the selected item (-1 if none).
    + */
    +goog.ui.Palette.prototype.getSelectedIndex = function() {
    +  return this.selectionModel_ ? this.selectionModel_.getSelectedIndex() : -1;
    +};
    +
    +
    +/**
    + * Returns the currently selected palette item, or null if no item is selected.
    + * @return {Node} The selected item (null if none).
    + */
    +goog.ui.Palette.prototype.getSelectedItem = function() {
    +  return this.selectionModel_ ?
    +      /** @type {Node} */ (this.selectionModel_.getSelectedItem()) : null;
    +};
    +
    +
    +/**
    + * Selects the item at the given 0-based index, or clears the selection
    + * if the argument is -1 or out of range.  Any previously-selected item
    + * will be deselected.
    + * @param {number} index 0-based index of the item to select.
    + */
    +goog.ui.Palette.prototype.setSelectedIndex = function(index) {
    +  if (this.selectionModel_) {
    +    this.selectionModel_.setSelectedIndex(index);
    +  }
    +};
    +
    +
    +/**
    + * Selects the given item, or clears the selection if the argument is null or
    + * invalid.  Any previously-selected item will be deselected.
    + * @param {Node} item Item to select.
    + */
    +goog.ui.Palette.prototype.setSelectedItem = function(item) {
    +  if (this.selectionModel_) {
    +    this.selectionModel_.setSelectedItem(item);
    +  }
    +};
    +
    +
    +/**
    + * Private helper; highlights or un-highlights the item at the given index
    + * based on the value of the Boolean argument.  This implementation simply
    + * applies highlight styling to the cell containing the item to be highighted.
    + * Does nothing if the palette hasn't been rendered yet.
    + * @param {number} index 0-based index of item to highlight or un-highlight.
    + * @param {boolean} highlight If true, the item is highlighted; otherwise it
    + *     is un-highlighted.
    + * @private
    + */
    +goog.ui.Palette.prototype.highlightIndex_ = function(index, highlight) {
    +  if (this.getElement()) {
    +    var items = this.getContent();
    +    if (items && index >= 0 && index < items.length) {
    +      var cellEl = this.getHighlightedCellElement_();
    +      if (this.currentCellControl_.getElement() != cellEl) {
    +        this.currentCellControl_.setElementInternal(cellEl);
    +      }
    +      if (this.currentCellControl_.tryHighlight(highlight)) {
    +        this.getRenderer().highlightCell(this, items[index], highlight);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Palette.prototype.setHighlighted = function(highlight) {
    +  if (highlight && this.highlightedIndex_ == -1) {
    +    // If there was a last highlighted index, use that. Otherwise, highlight the
    +    // first cell.
    +    this.setHighlightedIndex(
    +        this.lastHighlightedIndex_ > -1 ?
    +        this.lastHighlightedIndex_ :
    +        0);
    +  } else if (!highlight) {
    +    this.setHighlightedIndex(-1);
    +  }
    +  // The highlight event should be fired once the component has updated its own
    +  // state.
    +  goog.ui.Palette.base(this, 'setHighlighted', highlight);
    +};
    +
    +
    +/**
    + * Private helper; selects or deselects the given item based on the value of
    + * the Boolean argument.  This implementation simply applies selection styling
    + * to the cell containing the item to be selected.  Does nothing if the palette
    + * hasn't been rendered yet.
    + * @param {Node} item Item to select or deselect.
    + * @param {boolean} select If true, the item is selected; otherwise it is
    + *     deselected.
    + * @private
    + */
    +goog.ui.Palette.prototype.selectItem_ = function(item, select) {
    +  if (this.getElement()) {
    +    this.getRenderer().selectCell(this, item, select);
    +  }
    +};
    +
    +
    +/**
    + * Calculates and updates the size of the palette based on any preset values
    + * and the number of palette items.  If there is no preset size, sets the
    + * palette size to the smallest square big enough to contain all items.  If
    + * there is a preset number of columns, increases the number of rows to hold
    + * all items if needed.  (If there are too many rows, does nothing.)
    + * @private
    + */
    +goog.ui.Palette.prototype.adjustSize_ = function() {
    +  var items = this.getContent();
    +  if (items) {
    +    if (this.size_ && this.size_.width) {
    +      // There is already a size set; honor the number of columns (if >0), but
    +      // increase the number of rows if needed.
    +      var minRows = Math.ceil(items.length / this.size_.width);
    +      if (!goog.isNumber(this.size_.height) || this.size_.height < minRows) {
    +        this.size_.height = minRows;
    +      }
    +    } else {
    +      // No size has been set; size the grid to the smallest square big enough
    +      // to hold all items (hey, why not?).
    +      var length = Math.ceil(Math.sqrt(items.length));
    +      this.size_ = new goog.math.Size(length, length);
    +    }
    +  } else {
    +    // No items; set size to 0x0.
    +    this.size_ = new goog.math.Size(0, 0);
    +  }
    +};
    +
    +
    +
    +/**
    + * A component to represent the currently highlighted cell.
    + * @constructor
    + * @extends {goog.ui.Control}
    + * @private
    + */
    +goog.ui.Palette.CurrentCell_ = function() {
    +  goog.ui.Palette.CurrentCell_.base(this, 'constructor', null);
    +  this.setDispatchTransitionEvents(goog.ui.Component.State.HOVER, true);
    +};
    +goog.inherits(goog.ui.Palette.CurrentCell_, goog.ui.Control);
    +
    +
    +/**
    + * @param {boolean} highlight Whether to highlight or unhighlight the component.
    + * @return {boolean} Whether it was successful.
    + */
    +goog.ui.Palette.CurrentCell_.prototype.tryHighlight = function(highlight) {
    +  this.setHighlighted(highlight);
    +  return this.isHighlighted() == highlight;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/palette_test.html b/mapbox-gl/closure-library/closure/goog/ui/palette_test.html
    new file mode 100644
    index 0000000000..799cd8fd15
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/palette_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Palette
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PaletteTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/palette_test.js b/mapbox-gl/closure-library/closure/goog/ui/palette_test.js
    new file mode 100644
    index 0000000000..889be2cbf0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/palette_test.js
    @@ -0,0 +1,188 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PaletteTest');
    +goog.setTestOnly('goog.ui.PaletteTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyEvent');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Palette');
    +
    +var palette;
    +var nodes;
    +
    +function setUp() {
    +  nodes = [];
    +  for (var i = 0; i < 23; i++) {
    +    var node = goog.dom.createTextNode('node[' + i + ']');
    +    nodes.push(node);
    +  }
    +  palette = new goog.ui.Palette(nodes);
    +}
    +
    +function tearDown() {
    +  palette.dispose();
    +  document.getElementById('sandbox').innerHTML = '';
    +}
    +
    +function testAfterHighlightListener() {
    +  palette.setHighlightedIndex(0);
    +  var handler = new goog.testing.recordFunction();
    +  goog.events.listen(palette,
    +      goog.ui.Palette.EventType.AFTER_HIGHLIGHT, handler);
    +  palette.setHighlightedIndex(2);
    +  assertEquals(1, handler.getCallCount());
    +  palette.setHighlightedIndex(-1);
    +  assertEquals(2, handler.getCallCount());
    +}
    +
    +function testHighlightItemUpdatesParentA11yActiveDescendant() {
    +  var container = new goog.ui.Container();
    +  container.render(document.getElementById('sandbox'));
    +  container.addChild(palette, true);
    +
    +  palette.setHighlightedItem(nodes[0]);
    +  assertEquals('Node 0 cell should be the container\'s active descendant',
    +      palette.getRenderer().getCellForItem(nodes[0]),
    +      goog.a11y.aria.getActiveDescendant(container.getElement()));
    +
    +  palette.setHighlightedItem(nodes[1]);
    +  assertEquals('Node 1 cell should be the container\'s active descendant',
    +      palette.getRenderer().getCellForItem(nodes[1]),
    +      goog.a11y.aria.getActiveDescendant(container.getElement()));
    +
    +  palette.setHighlightedItem();
    +  assertNull('Container should have no active descendant',
    +      goog.a11y.aria.getActiveDescendant(container.getElement()));
    +
    +  container.dispose();
    +}
    +
    +function testHighlightCellEvents() {
    +  var container = new goog.ui.Container();
    +  container.render(document.getElementById('sandbox'));
    +  container.addChild(palette, true);
    +  var renderer = palette.getRenderer();
    +
    +  var events = [];
    +  var targetElements = [];
    +  var handleEvent = function(e) {
    +    events.push(e);
    +    targetElements.push(e.target.getElement());
    +  };
    +  palette.getHandler().listen(palette, [
    +    this, goog.ui.Component.EventType.HIGHLIGHT,
    +    this, goog.ui.Component.EventType.UNHIGHLIGHT
    +  ], handleEvent);
    +
    +  // Test highlight events on first selection
    +  palette.setHighlightedItem(nodes[0]);
    +  assertEquals('Should have fired 1 event', 1, events.length);
    +  assertEquals('HIGHLIGHT event should be fired',
    +      goog.ui.Component.EventType.HIGHLIGHT, events[0].type);
    +  assertEquals('Event should be fired for node[0] cell',
    +      renderer.getCellForItem(nodes[0]), targetElements[0]);
    +
    +  events = [];
    +  targetElements = [];
    +
    +  // Only fire highlight events when there is a selection change
    +  palette.setHighlightedItem(nodes[0]);
    +  assertEquals('Should have fired 0 events', 0, events.length);
    +
    +  // Test highlight events on cell change
    +  palette.setHighlightedItem(nodes[1]);
    +  assertEquals('Should have fired 2 events', 2, events.length);
    +  var unhighlightEvent = events.shift();
    +  var highlightEvent = events.shift();
    +  assertEquals('UNHIGHLIGHT should be fired first',
    +      goog.ui.Component.EventType.UNHIGHLIGHT, unhighlightEvent.type);
    +  assertEquals('UNHIGHLIGHT should be fired for node[0] cell',
    +      renderer.getCellForItem(nodes[0]), targetElements[0]);
    +  assertEquals('HIGHLIGHT should be fired after UNHIGHLIGHT',
    +      goog.ui.Component.EventType.HIGHLIGHT, highlightEvent.type);
    +  assertEquals('HIGHLIGHT should be fired for node[1] cell',
    +      renderer.getCellForItem(nodes[1]), targetElements[1]);
    +
    +  events = [];
    +  targetElements = [];
    +
    +  // Test highlight events when a cell is unselected
    +  palette.setHighlightedItem();
    +
    +  assertEquals('Should have fired 1 event', 1, events.length);
    +  assertEquals('UNHIGHLIGHT event should be fired',
    +      goog.ui.Component.EventType.UNHIGHLIGHT, events[0].type);
    +  assertEquals('Event should be fired for node[1] cell',
    +      renderer.getCellForItem(nodes[1]), targetElements[0]);
    +}
    +
    +function testHandleKeyEventLoops() {
    +  palette.setHighlightedIndex(0);
    +  var createKeyEvent = function(keyCode) {
    +    return new goog.events.KeyEvent(keyCode,
    +        0 /* charCode */,
    +        false /* repeat */,
    +        new goog.testing.events.Event(goog.events.EventType.KEYDOWN));
    +  };
    +  palette.handleKeyEvent(createKeyEvent(goog.events.KeyCodes.LEFT));
    +  assertEquals(nodes.length - 1, palette.getHighlightedIndex());
    +
    +  palette.handleKeyEvent(createKeyEvent(goog.events.KeyCodes.RIGHT));
    +  assertEquals(0, palette.getHighlightedIndex());
    +}
    +
    +function testSetHighlight() {
    +  assertEquals(-1, palette.getHighlightedIndex());
    +  palette.setHighlighted(true);
    +  assertEquals(0, palette.getHighlightedIndex());
    +
    +  palette.setHighlightedIndex(3);
    +  palette.setHighlighted(false);
    +  assertEquals(-1, palette.getHighlightedIndex());
    +  palette.setHighlighted(true);
    +  assertEquals(3, palette.getHighlightedIndex());
    +
    +  palette.setHighlighted(false);
    +  palette.setHighlightedIndex(5);
    +  palette.setHighlighted(true);
    +  assertEquals(5, palette.getHighlightedIndex());
    +  palette.setHighlighted(true);
    +  assertEquals(5, palette.getHighlightedIndex());
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Palette must not have aria label by default',
    +      palette.getAriaLabel());
    +  palette.setAriaLabel('My Palette');
    +  palette.render();
    +  var element = palette.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Palette element must have expected aria-label', 'My Palette',
    +      element.getAttribute('aria-label'));
    +  assertEquals('Palette element must have expected aria role', 'grid',
    +      element.getAttribute('role'));
    +  palette.setAriaLabel('My new Palette');
    +  assertEquals('Palette element must have updated aria-label', 'My new Palette',
    +      element.getAttribute('aria-label'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/paletterenderer.js b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer.js
    new file mode 100644
    index 0000000000..f1b2aa5c2e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer.js
    @@ -0,0 +1,383 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.Palette}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.PaletteRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeIterator');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.iter');
    +goog.require('goog.style');
    +goog.require('goog.ui.ControlRenderer');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Palette}s.  Renders the palette as an
    + * HTML table wrapped in a DIV, with one palette item per cell:
    + *
    + *    <div class="goog-palette">
    + *      <table class="goog-palette-table">
    + *        <tbody class="goog-palette-body">
    + *          <tr class="goog-palette-row">
    + *            <td class="goog-palette-cell">...Item 0...</td>
    + *            <td class="goog-palette-cell">...Item 1...</td>
    + *            ...
    + *          </tr>
    + *          <tr class="goog-palette-row">
    + *            ...
    + *          </tr>
    + *        </tbody>
    + *      </table>
    + *    </div>
    + *
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.PaletteRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.PaletteRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.PaletteRenderer);
    +
    +
    +/**
    + * Globally unique ID sequence for cells rendered by this renderer class.
    + * @type {number}
    + * @private
    + */
    +goog.ui.PaletteRenderer.cellId_ = 0;
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.PaletteRenderer.CSS_CLASS = goog.getCssName('goog-palette');
    +
    +
    +/**
    + * Returns the palette items arranged in a table wrapped in a DIV, with the
    + * renderer's own CSS class and additional state-specific classes applied to
    + * it.
    + * @param {goog.ui.Control} palette goog.ui.Palette to render.
    + * @return {!Element} Root element for the palette.
    + * @override
    + */
    +goog.ui.PaletteRenderer.prototype.createDom = function(palette) {
    +  var classNames = this.getClassNames(palette);
    +  var element = palette.getDomHelper().createDom(
    +      goog.dom.TagName.DIV, classNames ? classNames.join(' ') : null,
    +      this.createGrid(/** @type {Array<Node>} */(palette.getContent()),
    +          palette.getSize(), palette.getDomHelper()));
    +  goog.a11y.aria.setRole(element, goog.a11y.aria.Role.GRID);
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the given items in a table with {@code size.width} columns and
    + * {@code size.height} rows.  If the table is too big, empty cells will be
    + * created as needed.  If the table is too small, the items that don't fit
    + * will not be rendered.
    + * @param {Array<Node>} items Palette items.
    + * @param {goog.math.Size} size Palette size (columns x rows); both dimensions
    + *     must be specified as numbers.
    + * @param {goog.dom.DomHelper} dom DOM helper for document interaction.
    + * @return {!Element} Palette table element.
    + */
    +goog.ui.PaletteRenderer.prototype.createGrid = function(items, size, dom) {
    +  var rows = [];
    +  for (var row = 0, index = 0; row < size.height; row++) {
    +    var cells = [];
    +    for (var column = 0; column < size.width; column++) {
    +      var item = items && items[index++];
    +      cells.push(this.createCell(item, dom));
    +    }
    +    rows.push(this.createRow(cells, dom));
    +  }
    +
    +  return this.createTable(rows, dom);
    +};
    +
    +
    +/**
    + * Returns a table element (or equivalent) that wraps the given rows.
    + * @param {Array<Element>} rows Array of row elements.
    + * @param {goog.dom.DomHelper} dom DOM helper for document interaction.
    + * @return {!Element} Palette table element.
    + */
    +goog.ui.PaletteRenderer.prototype.createTable = function(rows, dom) {
    +  var table = dom.createDom(goog.dom.TagName.TABLE,
    +      goog.getCssName(this.getCssClass(), 'table'),
    +      dom.createDom(goog.dom.TagName.TBODY,
    +          goog.getCssName(this.getCssClass(), 'body'), rows));
    +  table.cellSpacing = 0;
    +  table.cellPadding = 0;
    +  return table;
    +};
    +
    +
    +/**
    + * Returns a table row element (or equivalent) that wraps the given cells.
    + * @param {Array<Element>} cells Array of cell elements.
    + * @param {goog.dom.DomHelper} dom DOM helper for document interaction.
    + * @return {!Element} Row element.
    + */
    +goog.ui.PaletteRenderer.prototype.createRow = function(cells, dom) {
    +  var row = dom.createDom(goog.dom.TagName.TR,
    +      goog.getCssName(this.getCssClass(), 'row'), cells);
    +  goog.a11y.aria.setRole(row, goog.a11y.aria.Role.ROW);
    +  return row;
    +};
    +
    +
    +/**
    + * Returns a table cell element (or equivalent) that wraps the given palette
    + * item (which must be a DOM node).
    + * @param {Node|string} node Palette item.
    + * @param {goog.dom.DomHelper} dom DOM helper for document interaction.
    + * @return {!Element} Cell element.
    + */
    +goog.ui.PaletteRenderer.prototype.createCell = function(node, dom) {
    +  var cell = dom.createDom(goog.dom.TagName.TD, {
    +    'class': goog.getCssName(this.getCssClass(), 'cell'),
    +    // Cells must have an ID, for accessibility, so we generate one here.
    +    'id': goog.getCssName(this.getCssClass(), 'cell-') +
    +        goog.ui.PaletteRenderer.cellId_++
    +  }, node);
    +  goog.a11y.aria.setRole(cell, goog.a11y.aria.Role.GRIDCELL);
    +  // Initialize to an unselected state.
    +  goog.a11y.aria.setState(cell, goog.a11y.aria.State.SELECTED, false);
    +
    +  if (!goog.dom.getTextContent(cell) && !goog.a11y.aria.getLabel(cell)) {
    +    var ariaLabelForCell = this.findAriaLabelForCell_(cell);
    +    if (ariaLabelForCell) {
    +      goog.a11y.aria.setLabel(cell, ariaLabelForCell);
    +    }
    +  }
    +  return cell;
    +};
    +
    +
    +/**
    + * Descends the DOM and tries to find an aria label for a grid cell
    + * from the first child with a label or title.
    + * @param {!Element} cell The cell.
    + * @return {string} The label to use.
    + * @private
    + */
    +goog.ui.PaletteRenderer.prototype.findAriaLabelForCell_ = function(cell) {
    +  var iter = new goog.dom.NodeIterator(cell);
    +  var label = '';
    +  var node;
    +  while (!label && (node = goog.iter.nextOrValue(iter, null))) {
    +    if (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +      label = goog.a11y.aria.getLabel(/** @type {!Element} */ (node)) ||
    +          node.title;
    +    }
    +  }
    +  return label;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#canDecorate} to always return false.
    + * @param {Element} element Ignored.
    + * @return {boolean} False, since palettes don't support the decorate flow (for
    + *     now).
    + * @override
    + */
    +goog.ui.PaletteRenderer.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#decorate} to be a no-op, since
    + * palettes don't support the decorate flow (for now).
    + * @param {goog.ui.Control} palette Ignored.
    + * @param {Element} element Ignored.
    + * @return {null} Always null.
    + * @override
    + */
    +goog.ui.PaletteRenderer.prototype.decorate = function(palette, element) {
    +  return null;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#setContent} for palettes.  Locates
    + * the HTML table representing the palette grid, and replaces the contents of
    + * each cell with a new element from the array of nodes passed as the second
    + * argument.  If the new content has too many items the table will have more
    + * rows added to fit, if there are less items than the table has cells, then the
    + * left over cells will be empty.
    + * @param {Element} element Root element of the palette control.
    + * @param {goog.ui.ControlContent} content Array of items to replace existing
    + *     palette items.
    + * @override
    + */
    +goog.ui.PaletteRenderer.prototype.setContent = function(element, content) {
    +  var items = /** @type {Array<Node>} */ (content);
    +  if (element) {
    +    var tbody = goog.dom.getElementsByTagNameAndClass(goog.dom.TagName.TBODY,
    +        goog.getCssName(this.getCssClass(), 'body'), element)[0];
    +    if (tbody) {
    +      var index = 0;
    +      goog.array.forEach(tbody.rows, function(row) {
    +        goog.array.forEach(row.cells, function(cell) {
    +          goog.dom.removeChildren(cell);
    +          if (items) {
    +            var item = items[index++];
    +            if (item) {
    +              goog.dom.appendChild(cell, item);
    +            }
    +          }
    +        });
    +      });
    +
    +      // Make space for any additional items.
    +      if (index < items.length) {
    +        var cells = [];
    +        var dom = goog.dom.getDomHelper(element);
    +        var width = tbody.rows[0].cells.length;
    +        while (index < items.length) {
    +          var item = items[index++];
    +          cells.push(this.createCell(item, dom));
    +          if (cells.length == width) {
    +            var row = this.createRow(cells, dom);
    +            goog.dom.appendChild(tbody, row);
    +            cells.length = 0;
    +          }
    +        }
    +        if (cells.length > 0) {
    +          while (cells.length < width) {
    +            cells.push(this.createCell('', dom));
    +          }
    +          var row = this.createRow(cells, dom);
    +          goog.dom.appendChild(tbody, row);
    +        }
    +      }
    +    }
    +    // Make sure the new contents are still unselectable.
    +    goog.style.setUnselectable(element, true, goog.userAgent.GECKO);
    +  }
    +};
    +
    +
    +/**
    + * Returns the item corresponding to the given node, or null if the node is
    + * neither a palette cell nor part of a palette item.
    + * @param {goog.ui.Palette} palette Palette in which to look for the item.
    + * @param {Node} node Node to look for.
    + * @return {Node} The corresponding palette item (null if not found).
    + */
    +goog.ui.PaletteRenderer.prototype.getContainingItem = function(palette, node) {
    +  var root = palette.getElement();
    +  while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) {
    +    if (node.tagName == goog.dom.TagName.TD && goog.dom.classlist.contains(
    +        /** @type {!Element} */ (node),
    +        goog.getCssName(this.getCssClass(), 'cell'))) {
    +      return node.firstChild;
    +    }
    +    node = node.parentNode;
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Updates the highlight styling of the palette cell containing the given node
    + * based on the value of the Boolean argument.
    + * @param {goog.ui.Palette} palette Palette containing the item.
    + * @param {Node} node Item whose cell is to be highlighted or un-highlighted.
    + * @param {boolean} highlight If true, the cell is highlighted; otherwise it is
    + *     un-highlighted.
    + */
    +goog.ui.PaletteRenderer.prototype.highlightCell = function(palette,
    +                                                           node,
    +                                                           highlight) {
    +  if (node) {
    +    var cell = this.getCellForItem(node);
    +    goog.asserts.assert(cell);
    +    goog.dom.classlist.enable(cell,
    +        goog.getCssName(this.getCssClass(), 'cell-hover'), highlight);
    +    // See http://www.w3.org/TR/2006/WD-aria-state-20061220/#activedescendent
    +    // for an explanation of the activedescendent.
    +    if (highlight) {
    +      goog.a11y.aria.setState(palette.getElementStrict(),
    +          goog.a11y.aria.State.ACTIVEDESCENDANT, cell.id);
    +    } else if (cell.id == goog.a11y.aria.getState(palette.getElementStrict(),
    +        goog.a11y.aria.State.ACTIVEDESCENDANT)) {
    +      goog.a11y.aria.removeState(palette.getElementStrict(),
    +          goog.a11y.aria.State.ACTIVEDESCENDANT);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Item whose cell is to be returned.
    + * @return {Element} The grid cell for the palette item.
    + */
    +goog.ui.PaletteRenderer.prototype.getCellForItem = function(node) {
    +  return /** @type {Element} */ (node ? node.parentNode : null);
    +};
    +
    +
    +/**
    + * Updates the selection styling of the palette cell containing the given node
    + * based on the value of the Boolean argument.
    + * @param {goog.ui.Palette} palette Palette containing the item.
    + * @param {Node} node Item whose cell is to be selected or deselected.
    + * @param {boolean} select If true, the cell is selected; otherwise it is
    + *     deselected.
    + */
    +goog.ui.PaletteRenderer.prototype.selectCell = function(palette, node, select) {
    +  if (node) {
    +    var cell = /** @type {!Element} */ (node.parentNode);
    +    goog.dom.classlist.enable(cell,
    +        goog.getCssName(this.getCssClass(), 'cell-selected'),
    +        select);
    +    goog.a11y.aria.setState(cell, goog.a11y.aria.State.SELECTED, select);
    +  }
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.PaletteRenderer.prototype.getCssClass = function() {
    +  return goog.ui.PaletteRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.html
    new file mode 100644
    index 0000000000..4c18fb8032
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  @author erickj@google.com (Erick Johnson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for PaletteRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PaletteRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <div id="sandbox">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.js
    new file mode 100644
    index 0000000000..f6cfec9aba
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/paletterenderer_test.js
    @@ -0,0 +1,91 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PaletteRendererTest');
    +goog.setTestOnly('goog.ui.PaletteRendererTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Palette');
    +goog.require('goog.ui.PaletteRenderer');
    +
    +var sandbox;
    +var items = [
    +  '<div aria-label="label-0"></div>',
    +  '<div title="title-1"></div>',
    +  '<div aria-label="label-2" title="title-2"></div>',
    +  '<div><span title="child-title-3"></span></div>'
    +];
    +var itemEls;
    +var renderer;
    +var palette;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  itemEls = goog.array.map(items, function(item, index, a) {
    +    return goog.dom.htmlToDocumentFragment(item);
    +  });
    +  renderer = new goog.ui.PaletteRenderer();
    +  palette = new goog.ui.Palette(itemEls, renderer);
    +  palette.setSize(4, 1);
    +}
    +
    +function tearDown() {
    +  palette.dispose();
    +}
    +
    +function testGridA11yRoles() {
    +  var grid = renderer.createDom(palette);
    +  assertEquals(goog.a11y.aria.Role.GRID, goog.a11y.aria.getRole(grid));
    +  var table = grid.getElementsByTagName('table')[0];
    +  var row = table.getElementsByTagName('tr')[0];
    +  assertEquals(goog.a11y.aria.Role.ROW, goog.a11y.aria.getRole(row));
    +  var cell = row.getElementsByTagName('td')[0];
    +  assertEquals(goog.a11y.aria.Role.GRIDCELL, goog.a11y.aria.getRole(cell));
    +}
    +
    +function testCellA11yLabels() {
    +  var grid = renderer.createDom(palette);
    +  var cells = grid.getElementsByTagName('td');
    +
    +  assertEquals('An aria-label is used as a label',
    +      'label-0', goog.a11y.aria.getLabel(cells[0]));
    +  assertEquals('A title is used as a label',
    +      'title-1', goog.a11y.aria.getLabel(cells[1]));
    +  assertEquals('An aria-label takes precedence over a title',
    +      'label-2', goog.a11y.aria.getLabel(cells[2]));
    +  assertEquals('Children are traversed to find labels',
    +      'child-title-3', goog.a11y.aria.getLabel(cells[3]));
    +}
    +
    +function testA11yActiveDescendant() {
    +  palette.render();
    +  var cells = palette.getElementStrict().getElementsByTagName('td');
    +
    +  renderer.highlightCell(palette, cells[1].firstChild, true);
    +  assertEquals(cells[1].id, goog.a11y.aria.getState(
    +      palette.getElementStrict(), goog.a11y.aria.State.ACTIVEDESCENDANT));
    +
    +  renderer.highlightCell(palette, cells[0].firstChild, false);
    +  assertEquals(cells[1].id, goog.a11y.aria.getState(
    +      palette.getElementStrict(), goog.a11y.aria.State.ACTIVEDESCENDANT));
    +
    +  renderer.highlightCell(palette, cells[1].firstChild, false);
    +  assertNotEquals(cells[1].id, goog.a11y.aria.getState(
    +      palette.getElementStrict(), goog.a11y.aria.State.ACTIVEDESCENDANT));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker.js b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker.js
    new file mode 100644
    index 0000000000..5f65cca035
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker.js
    @@ -0,0 +1,641 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Plain text spell checker implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/plaintextspellchecker.html
    + */
    +
    +goog.provide('goog.ui.PlainTextSpellChecker');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.style');
    +goog.require('goog.ui.AbstractSpellChecker');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Plain text spell checker implementation.
    + *
    + * @param {goog.spell.SpellCheck} handler Instance of the SpellCheckHandler
    + *     support object to use. A single instance can be shared by multiple
    + *     editor components.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.AbstractSpellChecker}
    + * @final
    + */
    +goog.ui.PlainTextSpellChecker = function(handler, opt_domHelper) {
    +  goog.ui.AbstractSpellChecker.call(this, handler, opt_domHelper);
    +
    +  /**
    +   * Correction UI container.
    +   * @private {!HTMLDivElement}
    +   */
    +  this.overlay_ = /** @type {!HTMLDivElement} */
    +      (this.getDomHelper().createDom('div'));
    +  goog.style.setPreWrap(this.overlay_);
    +
    +  /**
    +   * Bound async function (to avoid rebinding it on every call).
    +   * @type {Function}
    +   * @private
    +   */
    +  this.boundContinueAsyncFn_ = goog.bind(this.continueAsync_, this);
    +
    +  /**
    +   * Regular expression for matching line breaks.
    +   * @type {RegExp}
    +   * @private
    +   */
    +  this.endOfLineMatcher_ = new RegExp('(.*)(\n|\r\n){0,1}', 'g');
    +};
    +goog.inherits(goog.ui.PlainTextSpellChecker, goog.ui.AbstractSpellChecker);
    +
    +
    +/**
    + * Class name for invalid words.
    + * @type {string}
    + */
    +goog.ui.PlainTextSpellChecker.prototype.invalidWordClassName =
    +    goog.getCssName('goog-spellcheck-invalidword');
    +
    +
    +/**
    + * Class name for corrected words.
    + * @type {string}
    + */
    +goog.ui.PlainTextSpellChecker.prototype.correctedWordClassName =
    +    goog.getCssName('goog-spellcheck-correctedword');
    +
    +
    +/**
    + * Class name for correction pane.
    + * @type {string}
    + */
    +goog.ui.PlainTextSpellChecker.prototype.correctionPaneClassName =
    +    goog.getCssName('goog-spellcheck-correctionpane');
    +
    +
    +/**
    + * Number of words to scan to precharge the dictionary.
    + * @type {number}
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.dictionaryPreScanSize_ = 1000;
    +
    +
    +/**
    + * Size of window. Used to check if a resize operation actually changed the size
    + * of the window.
    + * @type {goog.math.Size|undefined}
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.winSize_;
    +
    +
    +/**
    + * Event handler for listening to events without leaking.
    + * @type {goog.events.EventHandler|undefined}
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.eventHandler_;
    +
    +
    +/**
    + * The object handling keyboard events.
    + * @type {goog.events.KeyHandler|undefined}
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.keyHandler_;
    +
    +
    +/** @private {number} */
    +goog.ui.PlainTextSpellChecker.prototype.textArrayIndex_;
    +
    +
    +/** @private {!Array<string>} */
    +goog.ui.PlainTextSpellChecker.prototype.textArray_;
    +
    +
    +/** @private {!Array<boolean>} */
    +goog.ui.PlainTextSpellChecker.prototype.textArrayProcess_;
    +
    +
    +/**
    + * Creates the initial DOM representation for the component.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.createDom = function() {
    +  this.setElementInternal(this.getDomHelper().createElement('textarea'));
    +};
    +
    +
    +/** @override */
    +goog.ui.PlainTextSpellChecker.prototype.enterDocument = function() {
    +  goog.ui.PlainTextSpellChecker.superClass_.enterDocument.call(this);
    +
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.keyHandler_ = new goog.events.KeyHandler(this.overlay_);
    +
    +  this.initSuggestionsMenu();
    +  this.initAccessibility_();
    +};
    +
    +
    +/** @override */
    +goog.ui.PlainTextSpellChecker.prototype.exitDocument = function() {
    +  goog.ui.PlainTextSpellChecker.superClass_.exitDocument.call(this);
    +
    +  if (this.eventHandler_) {
    +    this.eventHandler_.dispose();
    +    this.eventHandler_ = undefined;
    +  }
    +  if (this.keyHandler_) {
    +    this.keyHandler_.dispose();
    +    this.keyHandler_ = undefined;
    +  }
    +};
    +
    +
    +/**
    + * Initializes suggestions menu. Populates menu with separator and ignore option
    + * that are always valid. Suggestions are later added above the separator.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.initSuggestionsMenu = function() {
    +  goog.ui.PlainTextSpellChecker.superClass_.initSuggestionsMenu.call(this);
    +  this.eventHandler_.listen(/** @type {goog.ui.PopupMenu} */ (this.getMenu()),
    +      goog.ui.Component.EventType.HIDE, this.onCorrectionHide_);
    +};
    +
    +
    +/**
    + * Checks spelling for all text and displays correction UI.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.check = function() {
    +  var text = this.getElement().value;
    +  this.getElement().readOnly = true;
    +
    +  // Prepare and position correction UI.
    +  goog.dom.removeChildren(this.overlay_);
    +  this.overlay_.className = this.correctionPaneClassName;
    +  if (this.getElement().parentNode != this.overlay_.parentNode) {
    +    this.getElement().parentNode.appendChild(this.overlay_);
    +  }
    +  goog.style.setElementShown(this.overlay_, false);
    +
    +  this.preChargeDictionary_(text);
    +};
    +
    +
    +/**
    + * Final stage of spell checking - displays the correction UI.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.finishCheck_ = function() {
    +  // Show correction UI.
    +  this.positionOverlay_();
    +  goog.style.setElementShown(this.getElement(), false);
    +  goog.style.setElementShown(this.overlay_, true);
    +
    +  var eh = this.eventHandler_;
    +  eh.listen(this.overlay_, goog.events.EventType.CLICK, this.onWordClick_);
    +  eh.listen(/** @type {goog.events.KeyHandler} */ (this.keyHandler_),
    +      goog.events.KeyHandler.EventType.KEY, this.handleOverlayKeyEvent);
    +
    +  // The position and size of the overlay element needs to be recalculated if
    +  // the browser window is resized.
    +  var win = goog.dom.getWindow(this.getDomHelper().getDocument()) || window;
    +  this.winSize_ = goog.dom.getViewportSize(win);
    +  eh.listen(win, goog.events.EventType.RESIZE, this.onWindowResize_);
    +
    +  goog.ui.PlainTextSpellChecker.superClass_.check.call(this);
    +};
    +
    +
    +/**
    + * Start the scan after the dictionary was loaded.
    + *
    + * @param {string} text text to process.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.preChargeDictionary_ = function(text) {
    +  this.eventHandler_.listen(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.READY, this.onDictionaryCharged_, true);
    +
    +  this.populateDictionary(text, this.dictionaryPreScanSize_);
    +};
    +
    +
    +/**
    + * Loads few initial dictionary words into the cache.
    + *
    + * @param {goog.events.Event} e goog.spell.SpellCheck.EventType.READY event.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.onDictionaryCharged_ = function(e) {
    +  e.stopPropagation();
    +  this.eventHandler_.unlisten(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.READY, this.onDictionaryCharged_, true);
    +  this.checkAsync_(this.getElement().value);
    +};
    +
    +
    +/**
    + * Processes the included and skips the excluded text ranges.
    + * @return {goog.ui.AbstractSpellChecker.AsyncResult} Whether the spell
    + *     checking is pending or done.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.spellCheckLoop_ = function() {
    +  for (var i = this.textArrayIndex_; i < this.textArray_.length; ++i) {
    +    var text = this.textArray_[i];
    +    if (this.textArrayProcess_[i]) {
    +      var result = this.processTextAsync(this.overlay_, text);
    +      if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +        this.textArrayIndex_ = i + 1;
    +        goog.Timer.callOnce(this.boundContinueAsyncFn_);
    +        return result;
    +      }
    +    } else {
    +      this.processRange(this.overlay_, text);
    +    }
    +  }
    +
    +  this.textArray_ = [];
    +  this.textArrayProcess_ = [];
    +
    +  return goog.ui.AbstractSpellChecker.AsyncResult.DONE;
    +};
    +
    +
    +/**
    + * Breaks text into included and excluded ranges using the marker RegExp
    + * supplied by the caller.
    + *
    + * @param {string} text text to process.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.initTextArray_ = function(text) {
    +  if (!this.excludeMarker) {
    +    this.textArray_ = [text];
    +    this.textArrayProcess_ = [true];
    +    return;
    +  }
    +
    +  this.textArray_ = [];
    +  this.textArrayProcess_ = [];
    +  this.excludeMarker.lastIndex = 0;
    +  var stringSegmentStart = 0;
    +  var result;
    +  while (result = this.excludeMarker.exec(text)) {
    +    if (result[0].length == 0) {
    +      break;
    +    }
    +    var excludedRange = result[0];
    +    var includedRange = text.substr(stringSegmentStart, result.index -
    +        stringSegmentStart);
    +    if (includedRange) {
    +      this.textArray_.push(includedRange);
    +      this.textArrayProcess_.push(true);
    +    }
    +    this.textArray_.push(excludedRange);
    +    this.textArrayProcess_.push(false);
    +    stringSegmentStart = this.excludeMarker.lastIndex;
    +  }
    +
    +  var leftoverText = text.substr(stringSegmentStart);
    +  if (leftoverText) {
    +    this.textArray_.push(leftoverText);
    +    this.textArrayProcess_.push(true);
    +  }
    +};
    +
    +
    +/**
    + * Starts asynchrnonous spell checking.
    + *
    + * @param {string} text text to process.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.checkAsync_ = function(text) {
    +  this.initializeAsyncMode();
    +  this.initTextArray_(text);
    +  this.textArrayIndex_ = 0;
    +  if (this.spellCheckLoop_() ==
    +      goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    return;
    +  }
    +  this.finishAsyncProcessing();
    +  this.finishCheck_();
    +};
    +
    +
    +/**
    + * Continues asynchrnonous spell checking.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.continueAsync_ = function() {
    +  // First finish with the current segment.
    +  var result = this.continueAsyncProcessing();
    +  if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    goog.Timer.callOnce(this.boundContinueAsyncFn_);
    +    return;
    +  }
    +  if (this.spellCheckLoop_() ==
    +      goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    return;
    +  }
    +  this.finishAsyncProcessing();
    +  this.finishCheck_();
    +};
    +
    +
    +/**
    + * Processes word.
    + *
    + * @param {Node} node Node containing word.
    + * @param {string} word Word to process.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.processWord = function(node, word,
    +    status) {
    +  node.appendChild(this.createWordElement(word, status));
    +};
    +
    +
    +/**
    + * Processes range of text - recognized words and separators.
    + *
    + * @param {Node} node Node containing separator.
    + * @param {string} text text to process.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.processRange = function(node, text) {
    +  this.endOfLineMatcher_.lastIndex = 0;
    +  var result;
    +  while (result = this.endOfLineMatcher_.exec(text)) {
    +    if (result[0].length == 0) {
    +      break;
    +    }
    +    node.appendChild(this.getDomHelper().createTextNode(result[1]));
    +    if (result[2]) {
    +      node.appendChild(this.getDomHelper().createElement('br'));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Hides correction UI.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.resume = function() {
    +  var wasVisible = this.isVisible();
    +
    +  goog.ui.PlainTextSpellChecker.superClass_.resume.call(this);
    +
    +  goog.style.setElementShown(this.overlay_, false);
    +  goog.style.setElementShown(this.getElement(), true);
    +  this.getElement().readOnly = false;
    +
    +  if (wasVisible) {
    +    this.getElement().value = goog.dom.getRawTextContent(this.overlay_);
    +    goog.dom.removeChildren(this.overlay_);
    +
    +    var eh = this.eventHandler_;
    +    eh.unlisten(this.overlay_, goog.events.EventType.CLICK, this.onWordClick_);
    +    eh.unlisten(/** @type {goog.events.KeyHandler} */ (this.keyHandler_),
    +        goog.events.KeyHandler.EventType.KEY, this.handleOverlayKeyEvent);
    +
    +    var win = goog.dom.getWindow(this.getDomHelper().getDocument()) || window;
    +    eh.unlisten(win, goog.events.EventType.RESIZE, this.onWindowResize_);
    +  }
    +};
    +
    +
    +/**
    + * Returns desired element properties for the specified status.
    + *
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @return {!Object} Properties to apply to word element.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.getElementProperties =
    +    function(status) {
    +  if (status == goog.spell.SpellCheck.WordStatus.INVALID) {
    +    return {'class': this.invalidWordClassName};
    +  } else if (status == goog.spell.SpellCheck.WordStatus.CORRECTED) {
    +    return {'class': this.correctedWordClassName};
    +  }
    +  return {'class': ''};
    +};
    +
    +
    +/**
    + * Handles the click events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.onWordClick_ = function(event) {
    +  if (event.target.className == this.invalidWordClassName ||
    +      event.target.className == this.correctedWordClassName) {
    +    this.showSuggestionsMenu(/** @type {!Element} */ (event.target), event);
    +
    +    // Prevent document click handler from closing the menu.
    +    event.stopPropagation();
    +  }
    +};
    +
    +
    +/**
    + * Handles window resize events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.onWindowResize_ = function(event) {
    +  var win = goog.dom.getWindow(this.getDomHelper().getDocument()) || window;
    +  var size = goog.dom.getViewportSize(win);
    +
    +  if (size.width != this.winSize_.width ||
    +      size.height != this.winSize_.height) {
    +    goog.style.setElementShown(this.overlay_, false);
    +    goog.style.setElementShown(this.getElement(), true);
    +
    +    // IE requires a slight delay, allowing the resize operation to take effect.
    +    if (goog.userAgent.IE) {
    +      goog.Timer.callOnce(this.resizeOverlay_, 100, this);
    +    } else {
    +      this.resizeOverlay_();
    +    }
    +    this.winSize_ = size;
    +  }
    +};
    +
    +
    +/**
    + * Resizes overlay to match the size of the bound element then displays the
    + * overlay. Helper for {@link #onWindowResize_}.
    + *
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.resizeOverlay_ = function() {
    +  this.positionOverlay_();
    +  goog.style.setElementShown(this.getElement(), false);
    +  goog.style.setElementShown(this.overlay_, true);
    +};
    +
    +
    +/**
    + * Updates the position and size of the overlay to match the original element.
    + *
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.positionOverlay_ = function() {
    +  goog.style.setPosition(
    +      this.overlay_, goog.style.getPosition(this.getElement()));
    +  goog.style.setSize(this.overlay_, goog.style.getSize(this.getElement()));
    +};
    +
    +
    +/** @override */
    +goog.ui.PlainTextSpellChecker.prototype.disposeInternal = function() {
    +  this.getDomHelper().removeNode(this.overlay_);
    +  delete this.overlay_;
    +  delete this.boundContinueAsyncFn_;
    +  delete this.endOfLineMatcher_;
    +  goog.ui.PlainTextSpellChecker.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Specify ARIA roles and states as appropriate.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.initAccessibility_ = function() {
    +  goog.asserts.assert(this.overlay_,
    +      'The plain text spell checker DOM element cannot be null.');
    +  goog.a11y.aria.setRole(this.overlay_, 'region');
    +  goog.a11y.aria.setState(this.overlay_, 'live', 'assertive');
    +  this.overlay_.tabIndex = 0;
    +
    +  /** @desc Title for Spell Checker's overlay.*/
    +  var MSG_SPELLCHECKER_OVERLAY_TITLE = goog.getMsg('Spell Checker');
    +  this.overlay_.title = MSG_SPELLCHECKER_OVERLAY_TITLE;
    +};
    +
    +
    +/**
    + * Handles key down for overlay.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @return {boolean} The handled value.
    + */
    +goog.ui.PlainTextSpellChecker.prototype.handleOverlayKeyEvent = function(e) {
    +  var handled = false;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.RIGHT:
    +      if (e.ctrlKey) {
    +        handled = this.navigate(goog.ui.AbstractSpellChecker.Direction.NEXT);
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.LEFT:
    +      if (e.ctrlKey) {
    +        handled = this.navigate(
    +            goog.ui.AbstractSpellChecker.Direction.PREVIOUS);
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.DOWN:
    +      if (this.getFocusedElementIndex()) {
    +        var el = this.getDomHelper().getElement(this.makeElementId(
    +            this.getFocusedElementIndex()));
    +        if (el) {
    +          var position = goog.style.getPosition(el);
    +          var size = goog.style.getSize(el);
    +          position.x += size.width / 2;
    +          position.y += size.height / 2;
    +          this.showSuggestionsMenu(el, position);
    +          handled = true;
    +        }
    +      }
    +      break;
    +  }
    +
    +  if (handled) {
    +    e.preventDefault();
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Handles correction menu actions.
    + *
    + * @param {goog.events.Event} event Action event.
    + * @override
    + */
    +goog.ui.PlainTextSpellChecker.prototype.onCorrectionAction = function(event) {
    +  goog.ui.PlainTextSpellChecker.superClass_.onCorrectionAction.call(this,
    +      event);
    +
    +  // In case of editWord base class has already set the focus (on the input),
    +  // otherwise set the focus back on the word.
    +  if (event.target != this.getMenuEdit()) {
    +    this.reFocus_();
    +  }
    +};
    +
    +
    +/**
    + * Restores focus when the suggestion menu is hidden.
    + *
    + * @param {goog.events.BrowserEvent} event Blur event.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.onCorrectionHide_ = function(event) {
    +  this.reFocus_();
    +};
    +
    +
    +/**
    + * Sets the focus back on the previously focused word element.
    + * @private
    + */
    +goog.ui.PlainTextSpellChecker.prototype.reFocus_ = function() {
    +  var el = this.getElementByIndex(this.getFocusedElementIndex());
    +  if (el) {
    +    el.focus();
    +  } else {
    +    this.overlay_.focus();
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.html b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.html
    new file mode 100644
    index 0000000000..cd5521a12d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.html
    @@ -0,0 +1,42 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <!--
    +This test has not yet been updated to run on IE8. See http://b/hotlist?id=36311
    +-->
    +  <!--meta http-equiv="X-UA-Compatible" content="IE=edge"-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    +  <title>
    +   Closure Unit Tests - goog.ui.PlainTextSpellChecker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PlainTextSpellCheckerTest');
    +  </script>
    + </head>
    + <body>
    +  <textarea id="test1" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test2" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test3" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test4" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test5" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test6" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test7" style="width: 50ex; height: 15em;">
    +  </textarea>
    +  <textarea id="test8" style="width: 50ex; height: 15em;">
    +  </textarea>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.js b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.js
    new file mode 100644
    index 0000000000..5697f2037b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/plaintextspellchecker_test.js
    @@ -0,0 +1,439 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PlainTextSpellCheckerTest');
    +goog.setTestOnly('goog.ui.PlainTextSpellCheckerTest');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.PlainTextSpellChecker');
    +
    +var missspelling = 'missspelling';
    +var iggnore = 'iggnore';
    +var vocabulary = ['test', 'words', 'a', 'few', missspelling, iggnore];
    +
    +// We don't use Math.random() to make test predictable. Math.random is not
    +// repeatable, so a success on the dev machine != success in the lab (or on
    +// other dev machines). This is the same pseudorandom logic that CRT rand()
    +// uses.
    +var rseed = 1;
    +function random(range) {
    +  rseed = (rseed * 1103515245 + 12345) & 0xffffffff;
    +  return ((rseed >> 16) & 0x7fff) % range;
    +}
    +
    +function localSpellCheckingFunction(words, spellChecker, callback) {
    +  var len = words.length;
    +  var results = [];
    +  for (var i = 0; i < len; i++) {
    +    var word = words[i];
    +    var found = false;
    +    // Last two words are considered misspellings
    +    for (var j = 0; j < vocabulary.length - 2; ++j) {
    +      if (vocabulary[j] == word) {
    +        found = true;
    +        break;
    +      }
    +    }
    +    if (found) {
    +      results.push([word, goog.spell.SpellCheck.WordStatus.VALID]);
    +    } else {
    +      results.push([word, goog.spell.SpellCheck.WordStatus.INVALID,
    +        ['foo', 'bar']]);
    +    }
    +  }
    +  callback.call(spellChecker, results);
    +}
    +
    +function generateRandomSpace() {
    +  var string = '';
    +  var nSpace = 1 + random(4);
    +  for (var i = 0; i < nSpace; ++i) {
    +    string += ' ';
    +  }
    +  return string;
    +}
    +
    +function generateRandomString(maxWords, doQuotes) {
    +  var x = random(10);
    +  var string = '';
    +  if (doQuotes) {
    +    if (x == 0) {
    +      string = 'On xxxxx yyyy wrote:\n> ';
    +    } else if (x < 3) {
    +      string = '> ';
    +    }
    +  }
    +
    +  var nWords = 1 + random(maxWords);
    +  for (var i = 0; i < nWords; ++i) {
    +    string += vocabulary[random(vocabulary.length)];
    +    string += generateRandomSpace();
    +  }
    +  return string;
    +}
    +
    +var timerQueue = [];
    +function processTimerQueue() {
    +  while (timerQueue.length > 0) {
    +    var fn = timerQueue.shift();
    +    fn();
    +  }
    +}
    +
    +function localTimer(fn, delay, obj) {
    +  if (obj) {
    +    fn = goog.bind(fn, obj);
    +  }
    +  timerQueue.push(fn);
    +  return timerQueue.length;
    +}
    +
    +function testPlainTextSpellCheckerNoQuotes() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  s.asyncWordsPerBatch_ = 100;
    +  var el = document.getElementById('test1');
    +  s.decorate(el);
    +  var text = '';
    +  for (var i = 0; i < 10; ++i) {
    +    text += generateRandomString(10, false) + '\n';
    +  }
    +  el.value = text;
    +  // Yes this looks bizzare. This is for '\n' processing.
    +  // They get converted to CRLF as part of the above statement.
    +  text = el.value;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +  s.ignoreWord(iggnore);
    +  processTimerQueue();
    +  s.check();
    +  processTimerQueue();
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +
    +  assertEquals('Spell checker run should not change the underlying element.',
    +               text, el.value);
    +  s.dispose();
    +}
    +
    +function testPlainTextSpellCheckerWithQuotes() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  s.asyncWordsPerBatch_ = 100;
    +  var el = document.getElementById('test2');
    +  s.decorate(el);
    +  var text = '';
    +  for (var i = 0; i < 10; ++i) {
    +    text += generateRandomString(10, true) + '\n';
    +  }
    +  el.value = text;
    +  // Yes this looks bizzare. This is for '\n' processing.
    +  // They get converted to CRLF as part of the above statement.
    +  text = el.value;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.setExcludeMarker(new RegExp('\nOn .* wrote:\n(> .*\n)+|\n(> .*\n)', 'g'));
    +  s.check();
    +  processTimerQueue();
    +  s.ignoreWord(iggnore);
    +  processTimerQueue();
    +  s.check();
    +  processTimerQueue();
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +
    +  assertEquals('Spell checker run should not change the underlying element.',
    +               text, el.value);
    +  s.dispose();
    +}
    +
    +function testPlainTextSpellCheckerWordReplacement() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  s.asyncWordsPerBatch_ = 100;
    +  var el = document.getElementById('test3');
    +  s.decorate(el);
    +  var text = '';
    +  for (var i = 0; i < 10; ++i) {
    +    text += generateRandomString(10, false) + '\n';
    +  }
    +  el.value = text;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +  var wordEl = container.firstChild;
    +  while (wordEl) {
    +    if (goog.dom.getTextContent(wordEl) == missspelling) {
    +      break;
    +    }
    +    wordEl = wordEl.nextSibling;
    +  }
    +
    +  if (!wordEl) {
    +    assertTrue('Cannot find the world that should have been here.' +
    +               'Please revise the test', false);
    +    return;
    +  }
    +
    +  s.activeWord_ = missspelling;
    +  s.activeElement_ = wordEl;
    +  var suggestions = s.getSuggestions_();
    +  s.replaceWord(wordEl, missspelling, 'foo');
    +  assertEquals('Should have set the original word attribute!',
    +      wordEl.getAttribute(goog.ui.AbstractSpellChecker.ORIGINAL_),
    +      missspelling);
    +
    +  s.activeWord_ = goog.dom.getTextContent(wordEl);
    +  s.activeElement_ = wordEl;
    +  var newSuggestions = s.getSuggestions_();
    +  assertEquals('Suggestion list should still be present even if the word ' +
    +      'is now correct!', suggestions, newSuggestions);
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    +
    +
    +function testPlainTextSpellCheckerKeyboardNavigateNext() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  var el = document.getElementById('test4');
    +  s.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.value = text;
    +  var keyEventProperties = {};
    +  keyEventProperties.ctrlKey = true;
    +  keyEventProperties.shiftKey = false;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +
    +  // First call just moves focus to first misspelled word.
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving from first to second mispelled word.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(container,
    +      goog.events.KeyCodes.RIGHT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertEquals('The second misspelled word should have focus.',
    +      document.activeElement, container.children[1]);
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    +
    +
    +function testPlainTextSpellCheckerKeyboardNavigateNextOnLastWord() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  var el = document.getElementById('test5');
    +  s.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.value = text;
    +  var keyEventProperties = {};
    +  keyEventProperties.ctrlKey = true;
    +  keyEventProperties.shiftKey = false;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +
    +  // First call just moves focus to first misspelled word.
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving to the next invalid word.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(container,
    +      goog.events.KeyCodes.RIGHT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertEquals('The third/last misspelled word should have focus.',
    +      document.activeElement, container.children[2]);
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    +
    +
    +function testPlainTextSpellCheckerKeyboardNavigateOpenSuggestions() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  var el = document.getElementById('test6');
    +  s.decorate(el);
    +  var text = 'unit';
    +  el.value = text;
    +  var keyEventProperties = {};
    +  keyEventProperties.ctrlKey = true;
    +  keyEventProperties.shiftKey = false;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +  var suggestionMenu = s.getMenu();
    +
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  assertFalse('The suggestion menu should not be visible yet.',
    +      suggestionMenu.isVisible());
    +
    +  keyEventProperties.ctrlKey = false;
    +  var defaultExecuted = goog.testing.events.fireKeySequence(container,
    +      goog.events.KeyCodes.DOWN, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertTrue('The suggestion menu should be visible after the key event.',
    +      suggestionMenu.isVisible());
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    +
    +
    +function testPlainTextSpellCheckerKeyboardNavigatePrevious() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  var el = document.getElementById('test7');
    +  s.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.value = text;
    +  var keyEventProperties = {};
    +  keyEventProperties.ctrlKey = true;
    +  keyEventProperties.shiftKey = false;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +
    +  // Move to the third element, so we can test the move back to the second.
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving from third to second mispelled word.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(container,
    +      goog.events.KeyCodes.LEFT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertEquals('The second misspelled word should have focus.',
    +      document.activeElement, container.children[1]);
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    +
    +
    +function testPlainTextSpellCheckerKeyboardNavigatePreviousOnFirstWord() {
    +  var handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  var s = new goog.ui.PlainTextSpellChecker(handler);
    +  var el = document.getElementById('test8');
    +  s.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.value = text;
    +  var keyEventProperties = {};
    +  keyEventProperties.ctrlKey = true;
    +  keyEventProperties.shiftKey = false;
    +
    +  var timerSav = goog.Timer.callOnce;
    +  goog.Timer.callOnce = localTimer;
    +
    +  s.check();
    +  processTimerQueue();
    +
    +  var container = s.overlay_;
    +
    +  // Move to the first invalid word.
    +  goog.testing.events.fireKeySequence(container, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving to the previous invalid word.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(container,
    +      goog.events.KeyCodes.LEFT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertEquals('The first misspelled word should have focus.',
    +      document.activeElement, container.children[0]);
    +
    +  s.resume();
    +  processTimerQueue();
    +
    +  goog.Timer.callOnce = timerSav;
    +  s.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popup.js b/mapbox-gl/closure-library/closure/goog/ui/popup.js
    new file mode 100644
    index 0000000000..6cb03ec25b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popup.js
    @@ -0,0 +1,337 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the Popup class.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/popup.html
    + */
    +
    +goog.provide('goog.ui.Popup');
    +goog.provide('goog.ui.Popup.AbsolutePosition');
    +goog.provide('goog.ui.Popup.AnchoredPosition');
    +goog.provide('goog.ui.Popup.AnchoredViewPortPosition');
    +goog.provide('goog.ui.Popup.ClientPosition');
    +goog.provide('goog.ui.Popup.Overflow');
    +goog.provide('goog.ui.Popup.ViewPortClientPosition');
    +goog.provide('goog.ui.Popup.ViewPortPosition');
    +
    +goog.require('goog.math.Box');
    +goog.require('goog.positioning.AbsolutePosition');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.AnchoredViewportPosition');
    +goog.require('goog.positioning.ClientPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.ViewportClientPosition');
    +goog.require('goog.positioning.ViewportPosition');
    +goog.require('goog.style');
    +goog.require('goog.ui.PopupBase');
    +
    +
    +
    +/**
    + * The Popup class provides functionality for displaying an absolutely
    + * positioned element at a particular location in the window. It's designed to
    + * be used as the foundation for building controls like a menu or tooltip. The
    + * Popup class includes functionality for displaying a Popup near adjacent to
    + * an anchor element.
    + *
    + * This works cross browser and thus does not use IE's createPopup feature
    + * which supports extending outside the edge of the brower window.
    + *
    + * @param {Element=} opt_element A DOM element for the popup.
    + * @param {goog.positioning.AbstractPosition=} opt_position A positioning helper
    + *     object.
    + * @constructor
    + * @extends {goog.ui.PopupBase}
    + */
    +goog.ui.Popup = function(opt_element, opt_position) {
    +  /**
    +   * Corner of the popup to used in the positioning algorithm.
    +   *
    +   * @type {goog.positioning.Corner}
    +   * @private
    +   */
    +  this.popupCorner_ = goog.positioning.Corner.TOP_START;
    +
    +  /**
    +   * Positioning helper object.
    +   *
    +   * @type {goog.positioning.AbstractPosition|undefined}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.position_ = opt_position || undefined;
    +  goog.ui.PopupBase.call(this, opt_element);
    +};
    +goog.inherits(goog.ui.Popup, goog.ui.PopupBase);
    +goog.tagUnsealableClass(goog.ui.Popup);
    +
    +
    +/**
    + * Enum for representing position handling in cases where the element would be
    + * positioned outside the viewport.
    + *
    + * @enum {number}
    + *
    + * @deprecated Use {@link goog.positioning.Overflow} instead, this alias will be
    + *     removed at the end of Q1 2009.
    + */
    +goog.ui.Popup.Overflow = goog.positioning.Overflow;
    +
    +
    +/**
    + * Margin for the popup used in positioning algorithms.
    + *
    + * @type {goog.math.Box|undefined}
    + * @private
    + */
    +goog.ui.Popup.prototype.margin_;
    +
    +
    +/**
    + * Returns the corner of the popup to used in the positioning algorithm.
    + *
    + * @return {goog.positioning.Corner} The popup corner used for positioning.
    + */
    +goog.ui.Popup.prototype.getPinnedCorner = function() {
    +  return this.popupCorner_;
    +};
    +
    +
    +/**
    + * Sets the corner of the popup to used in the positioning algorithm.
    + *
    + * @param {goog.positioning.Corner} corner The popup corner used for
    + *     positioning.
    + */
    +goog.ui.Popup.prototype.setPinnedCorner = function(corner) {
    +  this.popupCorner_ = corner;
    +  if (this.isVisible()) {
    +    this.reposition();
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.positioning.AbstractPosition} The position helper object
    + *     associated with the popup.
    + */
    +goog.ui.Popup.prototype.getPosition = function() {
    +  return this.position_ || null;
    +};
    +
    +
    +/**
    + * Sets the position helper object associated with the popup.
    + *
    + * @param {goog.positioning.AbstractPosition} position A position helper object.
    + */
    +goog.ui.Popup.prototype.setPosition = function(position) {
    +  this.position_ = position || undefined;
    +  if (this.isVisible()) {
    +    this.reposition();
    +  }
    +};
    +
    +
    +/**
    + * Returns the margin to place around the popup.
    + *
    + * @return {goog.math.Box?} The margin.
    + */
    +goog.ui.Popup.prototype.getMargin = function() {
    +  return this.margin_ || null;
    +};
    +
    +
    +/**
    + * Sets the margin to place around the popup.
    + *
    + * @param {goog.math.Box|number|null} arg1 Top value or Box.
    + * @param {number=} opt_arg2 Right value.
    + * @param {number=} opt_arg3 Bottom value.
    + * @param {number=} opt_arg4 Left value.
    + */
    +goog.ui.Popup.prototype.setMargin = function(arg1, opt_arg2, opt_arg3,
    +                                             opt_arg4) {
    +  if (arg1 == null || arg1 instanceof goog.math.Box) {
    +    this.margin_ = arg1;
    +  } else {
    +    this.margin_ = new goog.math.Box(arg1,
    +        /** @type {number} */ (opt_arg2),
    +        /** @type {number} */ (opt_arg3),
    +        /** @type {number} */ (opt_arg4));
    +  }
    +  if (this.isVisible()) {
    +    this.reposition();
    +  }
    +};
    +
    +
    +/**
    + * Repositions the popup according to the current state.
    + * @override
    + */
    +goog.ui.Popup.prototype.reposition = function() {
    +  if (!this.position_) {
    +    return;
    +  }
    +
    +  var hideForPositioning = !this.isVisible() &&
    +      this.getType() != goog.ui.PopupBase.Type.MOVE_OFFSCREEN;
    +  var el = this.getElement();
    +  if (hideForPositioning) {
    +    el.style.visibility = 'hidden';
    +    goog.style.setElementShown(el, true);
    +  }
    +
    +  this.position_.reposition(el, this.popupCorner_, this.margin_);
    +
    +  if (hideForPositioning) {
    +    // NOTE(eae): The visibility property is reset to 'visible' by the show_
    +    // method in PopupBase. Resetting it here causes flickering in some
    +    // situations, even if set to visible after the display property has been
    +    // set to none by the call below.
    +    goog.style.setElementShown(el, false);
    +  }
    +};
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is anchored at a corner of
    + * an element.
    + *
    + * When using AnchoredPosition, it is recommended that the popup element
    + * specified in the Popup constructor or Popup.setElement be absolutely
    + * positioned.
    + *
    + * @param {Element} element The element to anchor the popup at.
    + * @param {goog.positioning.Corner} corner The corner of the element to anchor
    + *     the popup at.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + *
    + * @deprecated Use {@link goog.positioning.AnchoredPosition} instead, this
    + *     alias will be removed at the end of Q1 2009.
    + * @final
    + */
    +goog.ui.Popup.AnchoredPosition = goog.positioning.AnchoredPosition;
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is anchored at a corner of
    + * an element. The corners are swapped if dictated by the viewport. For instance
    + * if a popup is anchored with its top left corner to the bottom left corner of
    + * the anchor the popup is either displayed below the anchor (as specified) or
    + * above it if there's not enough room to display it below.
    + *
    + * When using AnchoredPosition, it is recommended that the popup element
    + * specified in the Popup constructor or Popup.setElement be absolutely
    + * positioned.
    + *
    + * @param {Element} element The element to anchor the popup at.
    + * @param {goog.positioning.Corner} corner The corner of the element to anchor
    + *    the popup at.
    + * @param {boolean=} opt_adjust Whether the positioning should be adjusted until
    + *    the element fits inside the viewport even if that means that the anchored
    + *    corners are ignored.
    + * @constructor
    + * @extends {goog.ui.Popup.AnchoredPosition}
    + *
    + * @deprecated Use {@link goog.positioning.AnchoredViewportPosition} instead,
    + *     this alias will be removed at the end of Q1 2009.
    + */
    +goog.ui.Popup.AnchoredViewPortPosition =
    +    goog.positioning.AnchoredViewportPosition;
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup absolutely positioned by
    + * setting the left/top style elements directly to the specified values.
    + * The position is generally relative to the element's offsetParent. Normally,
    + * this is the document body, but can be another element if the popup element
    + * is scoped by an element with relative position.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.positioning.AbstractPosition}
    + *
    + * @deprecated Use {@link goog.positioning.AbsolutePosition} instead, this alias
    + *     will be removed at the end of Q1 2009.
    + * @final
    + */
    +goog.ui.Popup.AbsolutePosition = goog.positioning.AbsolutePosition;
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned according to
    + * coordinates relative to the  element's view port (page). This calculates the
    + * correct position to use even if the element is relatively positioned to some
    + * other element.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.ui.Popup.AbsolutePosition}
    + *
    + * @deprecated Use {@link goog.positioning.ViewPortPosition} instead, this alias
    + *     will be removed at the end of Q1 2009.
    + */
    +goog.ui.Popup.ViewPortPosition = goog.positioning.ViewportPosition;
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned relative to the
    + * window (client) coordinates. This calculates the correct position to
    + * use even if the element is relatively positioned to some other element. This
    + * is for trying to position an element at the spot of the mouse cursor in
    + * a MOUSEMOVE event. Just use the event.clientX and event.clientY as the
    + * parameters.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.ui.Popup.AbsolutePosition}
    + *
    + * @deprecated Use {@link goog.positioning.ClientPosition} instead, this alias
    + *     will be removed at the end of Q1 2009.
    + * @final
    + */
    +goog.ui.Popup.ClientPosition = goog.positioning.ClientPosition;
    +
    +
    +
    +/**
    + * Encapsulates a popup position where the popup is positioned relative to the
    + * window (client) coordinates, and made to stay within the viewport.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position if arg1 is a number representing the
    + *     left position, ignored otherwise.
    + * @constructor
    + * @extends {goog.ui.Popup.ClientPosition}
    + *
    + * @deprecated Use {@link goog.positioning.ViewPortClientPosition} instead, this
    + *     alias will be removed at the end of Q1 2009.
    + */
    +goog.ui.Popup.ViewPortClientPosition = goog.positioning.ViewportClientPosition;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popup_test.html b/mapbox-gl/closure-library/closure/goog/ui/popup_test.html
    new file mode 100644
    index 0000000000..6c21c55bfa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popup_test.html
    @@ -0,0 +1,36 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Popup
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PopupTest');
    +  </script>
    +  <style>
    +   #popup {
    +  position: absolute;
    +}
    +#anchor {
    +  margin-left: 100px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <span id="anchor">
    +   anchor
    +  </span>
    +  <div id="popup">
    +   Popup.
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popup_test.js b/mapbox-gl/closure-library/closure/goog/ui/popup_test.js
    new file mode 100644
    index 0000000000..aaf95d280a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popup_test.js
    @@ -0,0 +1,120 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PopupTest');
    +goog.setTestOnly('goog.ui.PopupTest');
    +
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Popup');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * This is used to round pixel values on FF3 Mac.
    + */
    +function assertRoundedEquals(a, b, c) {
    +  function round(x) {
    +    return goog.userAgent.GECKO && (goog.userAgent.MAC || goog.userAgent.X11) &&
    +        goog.userAgent.isVersionOrHigher('1.9') ? Math.round(x) : x;
    +  }
    +  if (arguments.length == 3) {
    +    assertEquals(a, round(b), round(c));
    +  } else {
    +    assertEquals(round(a), round(b));
    +  }
    +}
    +
    +function testCreateAndReposition() {
    +  var anchorEl = document.getElementById('anchor');
    +  var popupEl = document.getElementById('popup');
    +  var corner = goog.positioning.Corner;
    +
    +  var pos = new goog.positioning.AnchoredPosition(anchorEl,
    +                                                  corner.BOTTOM_START);
    +  var popup = new goog.ui.Popup(popupEl, pos);
    +  popup.setVisible(true);
    +
    +  var anchorRect = goog.style.getBounds(anchorEl);
    +  var popupRect = goog.style.getBounds(popupEl);
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +
    +  // Reposition.
    +  anchorEl.style.marginTop = '7px';
    +  popup.reposition();
    +
    +  anchorRect = goog.style.getBounds(anchorEl);
    +  popupRect = goog.style.getBounds(popupEl);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +}
    +
    +
    +function testSetPinnedCorner() {
    +  var anchorEl = document.getElementById('anchor');
    +  var popupEl = document.getElementById('popup');
    +  var corner = goog.positioning.Corner;
    +
    +  var pos = new goog.positioning.AnchoredPosition(anchorEl,
    +                                                  corner.BOTTOM_START);
    +  var popup = new goog.ui.Popup(popupEl, pos);
    +  popup.setVisible(true);
    +
    +  var anchorRect = goog.style.getBounds(anchorEl);
    +  var popupRect = goog.style.getBounds(popupEl);
    +  assertRoundedEquals('Left edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left);
    +  assertRoundedEquals('Popup should be positioned just below the anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top);
    +
    +  // Change pinned corner.
    +  popup.setPinnedCorner(corner.BOTTOM_END);
    +
    +  anchorRect = goog.style.getBounds(anchorEl);
    +  popupRect = goog.style.getBounds(popupEl);
    +  assertRoundedEquals('Right edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left + popupRect.width);
    +  assertRoundedEquals('Bottom edge of popup should line up with bottom ' +
    +                      'of anchor.',
    +                      anchorRect.top + anchorRect.height,
    +                      popupRect.top + popupRect.height);
    +
    +  // Position outside the viewport.
    +  anchorEl.style.marginLeft = '0';
    +  popup.reposition();
    +
    +  anchorRect = goog.style.getBounds(anchorEl);
    +  popupRect = goog.style.getBounds(popupEl);
    +
    +  assertRoundedEquals('Right edge of popup should line up with left edge ' +
    +                      'of anchor.',
    +                      anchorRect.left,
    +                      popupRect.left + popupRect.width);
    +
    +  anchorEl.style.marginLeft = '';
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupbase.js b/mapbox-gl/closure-library/closure/goog/ui/popupbase.js
    new file mode 100644
    index 0000000000..8b986221ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupbase.js
    @@ -0,0 +1,892 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the PopupBase class.
    + *
    + */
    +
    +goog.provide('goog.ui.PopupBase');
    +goog.provide('goog.ui.PopupBase.EventType');
    +goog.provide('goog.ui.PopupBase.Type');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.style');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * The PopupBase class provides functionality for showing and hiding a generic
    + * container element. It also provides the option for hiding the popup element
    + * if the user clicks outside the popup or the popup loses focus.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @param {Element=} opt_element A DOM element for the popup.
    + * @param {goog.ui.PopupBase.Type=} opt_type Type of popup.
    + */
    +goog.ui.PopupBase = function(opt_element, opt_type) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * An event handler to manage the events easily
    +   * @type {goog.events.EventHandler<!goog.ui.PopupBase>}
    +   * @private
    +   */
    +  this.handler_ = new goog.events.EventHandler(this);
    +
    +  this.setElement(opt_element || null);
    +  if (opt_type) {
    +    this.setType(opt_type);
    +  }
    +};
    +goog.inherits(goog.ui.PopupBase, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.PopupBase);
    +
    +
    +/**
    + * Constants for type of Popup
    + * @enum {string}
    + */
    +goog.ui.PopupBase.Type = {
    +  TOGGLE_DISPLAY: 'toggle_display',
    +  MOVE_OFFSCREEN: 'move_offscreen'
    +};
    +
    +
    +/**
    + * The popup dom element that this Popup wraps.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.element_ = null;
    +
    +
    +/**
    + * Whether the Popup dismisses itself it the user clicks outside of it or the
    + * popup loses focus
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.autoHide_ = true;
    +
    +
    +/**
    + * Mouse events without auto hide partner elements will not dismiss the popup.
    + * @type {Array<Element>}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.autoHidePartners_ = null;
    +
    +
    +/**
    + * Clicks outside the popup but inside this element will cause the popup to
    + * hide if autoHide_ is true. If this is null, then the entire document is used.
    + * For example, you can use a body-size div so that clicks on the browser
    + * scrollbar do not dismiss the popup.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.autoHideRegion_ = null;
    +
    +
    +/**
    + * Whether the popup is currently being shown.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.isVisible_ = false;
    +
    +
    +/**
    + * Whether the popup should hide itself asynchrously. This was added because
    + * there are cases where hiding the element in mouse down handler in IE can
    + * cause textinputs to get into a bad state if the element that had focus is
    + * hidden.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.shouldHideAsync_ = false;
    +
    +
    +/**
    + * The time when the popup was last shown.
    + * @type {number}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.lastShowTime_ = -1;
    +
    +
    +/**
    + * The time when the popup was last hidden.
    + * @type {number}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.lastHideTime_ = -1;
    +
    +
    +/**
    + * Whether to hide when the escape key is pressed.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.hideOnEscape_ = false;
    +
    +
    +/**
    + * Whether to enable cross-iframe dismissal.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.enableCrossIframeDismissal_ = true;
    +
    +
    +/**
    + * The type of popup
    + * @type {goog.ui.PopupBase.Type}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.type_ = goog.ui.PopupBase.Type.TOGGLE_DISPLAY;
    +
    +
    +/**
    + * Transition to play on showing the popup.
    + * @type {goog.fx.Transition|undefined}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.showTransition_;
    +
    +
    +/**
    + * Transition to play on hiding the popup.
    + * @type {goog.fx.Transition|undefined}
    + * @private
    + */
    +goog.ui.PopupBase.prototype.hideTransition_;
    +
    +
    +/**
    + * Constants for event type fired by Popup
    + *
    + * @enum {string}
    + */
    +goog.ui.PopupBase.EventType = {
    +  BEFORE_SHOW: 'beforeshow',
    +  SHOW: 'show',
    +  BEFORE_HIDE: 'beforehide',
    +  HIDE: 'hide'
    +};
    +
    +
    +/**
    + * A time in ms used to debounce events that happen right after each other.
    + *
    + * A note about why this is necessary. There are two cases to consider.
    + * First case, a popup will usually see a focus event right after it's launched
    + * because it's typical for it to be launched in a mouse-down event which will
    + * then move focus to the launching button. We don't want to think this is a
    + * separate user action moving focus. Second case, a user clicks on the
    + * launcher button to close the menu. In that case, we'll close the menu in the
    + * focus event and then show it again because of the mouse down event, even
    + * though the intention is to just close the menu. This workaround appears to
    + * be the least intrusive fix.
    + *
    + * @type {number}
    + */
    +goog.ui.PopupBase.DEBOUNCE_DELAY_MS = 150;
    +
    +
    +/**
    + * @return {goog.ui.PopupBase.Type} The type of popup this is.
    + */
    +goog.ui.PopupBase.prototype.getType = function() {
    +  return this.type_;
    +};
    +
    +
    +/**
    + * Specifies the type of popup to use.
    + *
    + * @param {goog.ui.PopupBase.Type} type Type of popup.
    + */
    +goog.ui.PopupBase.prototype.setType = function(type) {
    +  this.type_ = type;
    +};
    +
    +
    +/**
    + * Returns whether the popup should hide itself asynchronously using a timeout
    + * instead of synchronously.
    + * @return {boolean} Whether to hide async.
    + */
    +goog.ui.PopupBase.prototype.shouldHideAsync = function() {
    +  return this.shouldHideAsync_;
    +};
    +
    +
    +/**
    + * Sets whether the popup should hide itself asynchronously using a timeout
    + * instead of synchronously.
    + * @param {boolean} b Whether to hide async.
    + */
    +goog.ui.PopupBase.prototype.setShouldHideAsync = function(b) {
    +  this.shouldHideAsync_ = b;
    +};
    +
    +
    +/**
    + * Returns the dom element that should be used for the popup.
    + *
    + * @return {Element} The popup element.
    + */
    +goog.ui.PopupBase.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * Specifies the dom element that should be used for the popup.
    + *
    + * @param {Element} elt A DOM element for the popup.
    + */
    +goog.ui.PopupBase.prototype.setElement = function(elt) {
    +  this.ensureNotVisible_();
    +  this.element_ = elt;
    +};
    +
    +
    +/**
    + * Returns whether the Popup dismisses itself when the user clicks outside of
    + * it.
    + * @return {boolean} Whether the Popup autohides on an external click.
    + */
    +goog.ui.PopupBase.prototype.getAutoHide = function() {
    +  return this.autoHide_;
    +};
    +
    +
    +/**
    + * Sets whether the Popup dismisses itself when the user clicks outside of it.
    + * @param {boolean} autoHide Whether to autohide on an external click.
    + */
    +goog.ui.PopupBase.prototype.setAutoHide = function(autoHide) {
    +  this.ensureNotVisible_();
    +  this.autoHide_ = autoHide;
    +};
    +
    +
    +/**
    + * Mouse events that occur within an autoHide partner will not hide a popup
    + * set to autoHide.
    + * @param {!Element} partner The auto hide partner element.
    + */
    +goog.ui.PopupBase.prototype.addAutoHidePartner = function(partner) {
    +  if (!this.autoHidePartners_) {
    +    this.autoHidePartners_ = [];
    +  }
    +
    +  goog.array.insert(this.autoHidePartners_, partner);
    +};
    +
    +
    +/**
    + * Removes a previously registered auto hide partner.
    + * @param {!Element} partner The auto hide partner element.
    + */
    +goog.ui.PopupBase.prototype.removeAutoHidePartner = function(partner) {
    +  if (this.autoHidePartners_) {
    +    goog.array.remove(this.autoHidePartners_, partner);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the Popup autohides on the escape key.
    + */
    +goog.ui.PopupBase.prototype.getHideOnEscape = function() {
    +  return this.hideOnEscape_;
    +};
    +
    +
    +/**
    + * Sets whether the Popup dismisses itself on the escape key.
    + * @param {boolean} hideOnEscape Whether to autohide on the escape key.
    + */
    +goog.ui.PopupBase.prototype.setHideOnEscape = function(hideOnEscape) {
    +  this.ensureNotVisible_();
    +  this.hideOnEscape_ = hideOnEscape;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether cross iframe dismissal is enabled.
    + */
    +goog.ui.PopupBase.prototype.getEnableCrossIframeDismissal = function() {
    +  return this.enableCrossIframeDismissal_;
    +};
    +
    +
    +/**
    + * Sets whether clicks in other iframes should dismiss this popup.  In some
    + * cases it should be disabled, because it can cause spurious
    + * @param {boolean} enable Whether to enable cross iframe dismissal.
    + */
    +goog.ui.PopupBase.prototype.setEnableCrossIframeDismissal = function(enable) {
    +  this.enableCrossIframeDismissal_ = enable;
    +};
    +
    +
    +/**
    + * Returns the region inside which the Popup dismisses itself when the user
    + * clicks, or null if it's the entire document.
    + * @return {Element} The DOM element for autohide, or null if it hasn't been
    + *     set.
    + */
    +goog.ui.PopupBase.prototype.getAutoHideRegion = function() {
    +  return this.autoHideRegion_;
    +};
    +
    +
    +/**
    + * Sets the region inside which the Popup dismisses itself when the user
    + * clicks.
    + * @param {Element} element The DOM element for autohide.
    + */
    +goog.ui.PopupBase.prototype.setAutoHideRegion = function(element) {
    +  this.autoHideRegion_ = element;
    +};
    +
    +
    +/**
    + * Sets transition animation on showing and hiding the popup.
    + * @param {goog.fx.Transition=} opt_showTransition Transition to play on
    + *     showing the popup.
    + * @param {goog.fx.Transition=} opt_hideTransition Transition to play on
    + *     hiding the popup.
    + */
    +goog.ui.PopupBase.prototype.setTransition = function(
    +    opt_showTransition, opt_hideTransition) {
    +  this.showTransition_ = opt_showTransition;
    +  this.hideTransition_ = opt_hideTransition;
    +};
    +
    +
    +/**
    + * Returns the time when the popup was last shown.
    + *
    + * @return {number} time in ms since epoch when the popup was last shown, or
    + * -1 if the popup was never shown.
    + */
    +goog.ui.PopupBase.prototype.getLastShowTime = function() {
    +  return this.lastShowTime_;
    +};
    +
    +
    +/**
    + * Returns the time when the popup was last hidden.
    + *
    + * @return {number} time in ms since epoch when the popup was last hidden, or
    + * -1 if the popup was never hidden or is currently showing.
    + */
    +goog.ui.PopupBase.prototype.getLastHideTime = function() {
    +  return this.lastHideTime_;
    +};
    +
    +
    +/**
    + * Returns the event handler for the popup. All event listeners belonging to
    + * this handler are removed when the tooltip is hidden. Therefore,
    + * the recommended usage of this handler is to listen on events in
    + * {@link #onShow_}.
    + * @return {goog.events.EventHandler<T>} Event handler for this popup.
    + * @protected
    + * @this T
    + * @template T
    + */
    +goog.ui.PopupBase.prototype.getHandler = function() {
    +  // As the template type is unbounded, narrow the "this" type
    +  var self = /** @type {!goog.ui.PopupBase} */ (this);
    +
    +  return self.handler_;
    +};
    +
    +
    +/**
    + * Helper to throw exception if the popup is showing.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.ensureNotVisible_ = function() {
    +  if (this.isVisible_) {
    +    throw Error('Can not change this state of the popup while showing.');
    +  }
    +};
    +
    +
    +/**
    + * Returns whether the popup is currently visible.
    + *
    + * @return {boolean} whether the popup is currently visible.
    + */
    +goog.ui.PopupBase.prototype.isVisible = function() {
    +  return this.isVisible_;
    +};
    +
    +
    +/**
    + * Returns whether the popup is currently visible or was visible within about
    + * 150 ms ago. This is used by clients to handle a very specific, but common,
    + * popup scenario. The button that launches the popup should close the popup
    + * on mouse down if the popup is alrady open. The problem is that the popup
    + * closes itself during the capture phase of the mouse down and thus the button
    + * thinks it's hidden and this should show it again. This method provides a
    + * good heuristic for clients. Typically in their event handler they will have
    + * code that is:
    + *
    + * if (menu.isOrWasRecentlyVisible()) {
    + *   menu.setVisible(false);
    + * } else {
    + *   ... // code to position menu and initialize other state
    + *   menu.setVisible(true);
    + * }
    + * @return {boolean} Whether the popup is currently visible or was visible
    + *     within about 150 ms ago.
    + */
    +goog.ui.PopupBase.prototype.isOrWasRecentlyVisible = function() {
    +  return this.isVisible_ ||
    +         (goog.now() - this.lastHideTime_ <
    +          goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +};
    +
    +
    +/**
    + * Sets whether the popup should be visible. After this method
    + * returns, isVisible() will always return the new state, even if
    + * there is a transition.
    + *
    + * @param {boolean} visible Desired visibility state.
    + */
    +goog.ui.PopupBase.prototype.setVisible = function(visible) {
    +  // Make sure that any currently running transition is stopped.
    +  if (this.showTransition_) this.showTransition_.stop();
    +  if (this.hideTransition_) this.hideTransition_.stop();
    +
    +  if (visible) {
    +    this.show_();
    +  } else {
    +    this.hide_();
    +  }
    +};
    +
    +
    +/**
    + * Repositions the popup according to the current state.
    + * Should be overriden by subclases.
    + */
    +goog.ui.PopupBase.prototype.reposition = goog.nullFunction;
    +
    +
    +/**
    + * Does the work to show the popup.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.show_ = function() {
    +  // Ignore call if we are already showing.
    +  if (this.isVisible_) {
    +    return;
    +  }
    +
    +  // Give derived classes and handlers a chance to customize popup.
    +  if (!this.onBeforeShow()) {
    +    return;
    +  }
    +
    +  // Allow callers to set the element in the BEFORE_SHOW event.
    +  if (!this.element_) {
    +    throw Error('Caller must call setElement before trying to show the popup');
    +  }
    +
    +  // Call reposition after onBeforeShow, as it may change the style and/or
    +  // content of the popup and thereby affecting the size which is used for the
    +  // viewport calculation.
    +  this.reposition();
    +
    +  var doc = goog.dom.getOwnerDocument(this.element_);
    +
    +  if (this.hideOnEscape_) {
    +
    +    // Handle the escape keys.  Listen in the capture phase so that we can
    +    // stop the escape key from propagating to other elements.  For example,
    +    // if there is a popup within a dialog box, we want the popup to be
    +    // dismissed first, rather than the dialog.
    +    this.handler_.listen(doc, goog.events.EventType.KEYDOWN,
    +        this.onDocumentKeyDown_, true);
    +  }
    +
    +  // Set up event handlers.
    +  if (this.autoHide_) {
    +
    +    // Even if the popup is not in the focused document, we want to
    +    // close it on mousedowns in the document it's in.
    +    this.handler_.listen(doc, goog.events.EventType.MOUSEDOWN,
    +        this.onDocumentMouseDown_, true);
    +
    +    if (goog.userAgent.IE) {
    +      // We want to know about deactivates/mousedowns on the document with focus
    +      // The top-level document won't get a deactivate event if the focus is
    +      // in an iframe and the deactivate fires within that iframe.
    +      // The active element in the top-level document will remain the iframe
    +      // itself.
    +      var activeElement;
    +      /** @preserveTry */
    +      try {
    +        activeElement = doc.activeElement;
    +      } catch (e) {
    +        // There is an IE browser bug which can cause just the reading of
    +        // document.activeElement to throw an Unspecified Error.  This
    +        // may have to do with loading a popup within a hidden iframe.
    +      }
    +      while (activeElement && activeElement.nodeName == 'IFRAME') {
    +        /** @preserveTry */
    +        try {
    +          var tempDoc = goog.dom.getFrameContentDocument(activeElement);
    +        } catch (e) {
    +          // The frame is on a different domain that its parent document
    +          // This way, we grab the lowest-level document object we can get
    +          // a handle on given cross-domain security.
    +          break;
    +        }
    +        doc = tempDoc;
    +        activeElement = doc.activeElement;
    +      }
    +
    +      // Handle mousedowns in the focused document in case the user clicks
    +      // on the activeElement (in which case the popup should hide).
    +      this.handler_.listen(doc, goog.events.EventType.MOUSEDOWN,
    +          this.onDocumentMouseDown_, true);
    +
    +      // If the active element inside the focused document changes, then
    +      // we probably need to hide the popup.
    +      this.handler_.listen(doc, goog.events.EventType.DEACTIVATE,
    +          this.onDocumentBlur_);
    +
    +    } else {
    +      this.handler_.listen(doc, goog.events.EventType.BLUR,
    +          this.onDocumentBlur_);
    +    }
    +  }
    +
    +  // Make the popup visible.
    +  if (this.type_ == goog.ui.PopupBase.Type.TOGGLE_DISPLAY) {
    +    this.showPopupElement();
    +  } else if (this.type_ == goog.ui.PopupBase.Type.MOVE_OFFSCREEN) {
    +    this.reposition();
    +  }
    +  this.isVisible_ = true;
    +
    +  this.lastShowTime_ = goog.now();
    +  this.lastHideTime_ = -1;
    +
    +  // If there is transition to play, we play it and fire SHOW event after
    +  // the transition is over.
    +  if (this.showTransition_) {
    +    goog.events.listenOnce(
    +        /** @type {!goog.events.EventTarget} */ (this.showTransition_),
    +        goog.fx.Transition.EventType.END, this.onShow_, false, this);
    +    this.showTransition_.play();
    +  } else {
    +    // Notify derived classes and handlers.
    +    this.onShow_();
    +  }
    +};
    +
    +
    +/**
    + * Hides the popup. This call is idempotent.
    + *
    + * @param {Object=} opt_target Target of the event causing the hide.
    + * @return {boolean} Whether the popup was hidden and not cancelled.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.hide_ = function(opt_target) {
    +  // Give derived classes and handlers a chance to cancel hiding.
    +  if (!this.isVisible_ || !this.onBeforeHide_(opt_target)) {
    +    return false;
    +  }
    +
    +  // Remove any listeners we attached when showing the popup.
    +  if (this.handler_) {
    +    this.handler_.removeAll();
    +  }
    +
    +  // Set visibility to hidden even if there is a transition.
    +  this.isVisible_ = false;
    +  this.lastHideTime_ = goog.now();
    +
    +  // If there is transition to play, we play it and only hide the element
    +  // (and fire HIDE event) after the transition is over.
    +  if (this.hideTransition_) {
    +    goog.events.listenOnce(
    +        /** @type {!goog.events.EventTarget} */ (this.hideTransition_),
    +        goog.fx.Transition.EventType.END,
    +        goog.partial(this.continueHidingPopup_, opt_target), false, this);
    +    this.hideTransition_.play();
    +  } else {
    +    this.continueHidingPopup_(opt_target);
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Continues hiding the popup. This is a continuation from hide_. It is
    + * a separate method so that we can add a transition before hiding.
    + * @param {Object=} opt_target Target of the event causing the hide.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.continueHidingPopup_ = function(opt_target) {
    +  // Hide the popup.
    +  if (this.type_ == goog.ui.PopupBase.Type.TOGGLE_DISPLAY) {
    +    if (this.shouldHideAsync_) {
    +      goog.Timer.callOnce(this.hidePopupElement, 0, this);
    +    } else {
    +      this.hidePopupElement();
    +    }
    +  } else if (this.type_ == goog.ui.PopupBase.Type.MOVE_OFFSCREEN) {
    +    this.moveOffscreen_();
    +  }
    +
    +  // Notify derived classes and handlers.
    +  this.onHide_(opt_target);
    +};
    +
    +
    +/**
    + * Shows the popup element.
    + * @protected
    + */
    +goog.ui.PopupBase.prototype.showPopupElement = function() {
    +  this.element_.style.visibility = 'visible';
    +  goog.style.setElementShown(this.element_, true);
    +};
    +
    +
    +/**
    + * Hides the popup element.
    + * @protected
    + */
    +goog.ui.PopupBase.prototype.hidePopupElement = function() {
    +  this.element_.style.visibility = 'hidden';
    +  goog.style.setElementShown(this.element_, false);
    +};
    +
    +
    +/**
    + * Hides the popup by moving it offscreen.
    + *
    + * @private
    + */
    +goog.ui.PopupBase.prototype.moveOffscreen_ = function() {
    +  this.element_.style.top = '-10000px';
    +};
    +
    +
    +/**
    + * Called before the popup is shown. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + *
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the handlers returns false this will also return false.
    + * @protected
    + */
    +goog.ui.PopupBase.prototype.onBeforeShow = function() {
    +  return this.dispatchEvent(goog.ui.PopupBase.EventType.BEFORE_SHOW);
    +};
    +
    +
    +/**
    + * Called after the popup is shown. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + * @protected
    + * @suppress {underscore|visibility}
    + */
    +goog.ui.PopupBase.prototype.onShow_ = function() {
    +  this.dispatchEvent(goog.ui.PopupBase.EventType.SHOW);
    +};
    +
    +
    +/**
    + * Called before the popup is hidden. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + *
    + * @param {Object=} opt_target Target of the event causing the hide.
    + * @return {boolean} If anyone called preventDefault on the event object (or
    + *     if any of the handlers returns false this will also return false.
    + * @protected
    + * @suppress {underscore|visibility}
    + */
    +goog.ui.PopupBase.prototype.onBeforeHide_ = function(opt_target) {
    +  return this.dispatchEvent({
    +    type: goog.ui.PopupBase.EventType.BEFORE_HIDE,
    +    target: opt_target
    +  });
    +};
    +
    +
    +/**
    + * Called after the popup is hidden. Derived classes can override to hook this
    + * event but should make sure to call the parent class method.
    + * @param {Object=} opt_target Target of the event causing the hide.
    + * @protected
    + * @suppress {underscore|visibility}
    + */
    +goog.ui.PopupBase.prototype.onHide_ = function(opt_target) {
    +  this.dispatchEvent({
    +    type: goog.ui.PopupBase.EventType.HIDE,
    +    target: opt_target
    +  });
    +};
    +
    +
    +/**
    + * Mouse down handler for the document on capture phase. Used to hide the
    + * popup for auto-hide mode.
    + *
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.onDocumentMouseDown_ = function(e) {
    +  var target = /** @type {Node} */ (e.target);
    +
    +  if (!goog.dom.contains(this.element_, target) &&
    +      !this.isOrWithinAutoHidePartner_(target) &&
    +      this.isWithinAutoHideRegion_(target) &&
    +      !this.shouldDebounce_()) {
    +
    +    // Mouse click was outside popup and partners, so hide.
    +    this.hide_(target);
    +  }
    +};
    +
    +
    +/**
    + * Handles key-downs on the document to handle the escape key.
    + *
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.onDocumentKeyDown_ = function(e) {
    +  if (e.keyCode == goog.events.KeyCodes.ESC) {
    +    if (this.hide_(e.target)) {
    +      // Eat the escape key, but only if this popup was actually closed.
    +      e.preventDefault();
    +      e.stopPropagation();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Deactivate handler(IE) and blur handler (other browsers) for document.
    + * Used to hide the popup for auto-hide mode.
    + *
    + * @param {goog.events.BrowserEvent} e The event object.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.onDocumentBlur_ = function(e) {
    +  if (!this.enableCrossIframeDismissal_) {
    +    return;
    +  }
    +
    +  var doc = goog.dom.getOwnerDocument(this.element_);
    +
    +  // Ignore blur events if the active element is still inside the popup or if
    +  // there is no longer an active element.  For example, a widget like a
    +  // goog.ui.Button might programatically blur itself before losing tabIndex.
    +  if (typeof document.activeElement != 'undefined') {
    +    var activeElement = doc.activeElement;
    +    if (!activeElement || goog.dom.contains(this.element_,
    +        activeElement) || activeElement.tagName == 'BODY') {
    +      return;
    +    }
    +
    +  // Ignore blur events not for the document itself in non-IE browsers.
    +  } else if (e.target != doc) {
    +    return;
    +  }
    +
    +  // Debounce the initial focus move.
    +  if (this.shouldDebounce_()) {
    +    return;
    +  }
    +
    +  this.hide_();
    +};
    +
    +
    +/**
    + * @param {Node} element The element to inspect.
    + * @return {boolean} Returns true if the given element is one of the auto hide
    + *     partners or is a child of an auto hide partner.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.isOrWithinAutoHidePartner_ = function(element) {
    +  return goog.array.some(this.autoHidePartners_ || [], function(partner) {
    +    return element === partner || goog.dom.contains(partner, element);
    +  });
    +};
    +
    +
    +/**
    + * @param {Node} element The element to inspect.
    + * @return {boolean} Returns true if the element is contained within
    + *     the autohide region. If unset, the autohide region is the entire
    + *     entire document.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.isWithinAutoHideRegion_ = function(element) {
    +  return this.autoHideRegion_ ?
    +      goog.dom.contains(this.autoHideRegion_, element) : true;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the time since last show is less than the debounce
    + *     delay.
    + * @private
    + */
    +goog.ui.PopupBase.prototype.shouldDebounce_ = function() {
    +  return goog.now() - this.lastShowTime_ < goog.ui.PopupBase.DEBOUNCE_DELAY_MS;
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupBase.prototype.disposeInternal = function() {
    +  goog.ui.PopupBase.base(this, 'disposeInternal');
    +  this.handler_.dispose();
    +  goog.dispose(this.showTransition_);
    +  goog.dispose(this.hideTransition_);
    +  delete this.element_;
    +  delete this.handler_;
    +  delete this.autoHidePartners_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.html b/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.html
    new file mode 100644
    index 0000000000..4967a37f89
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.html
    @@ -0,0 +1,47 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: gboyer@google.com (Garrett Boyer)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.PopupBase
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PopupBaseTest');
    +  </script>
    +  <style>
    +   #moveOffscreenPopupDiv {
    +  position: absolute;
    +  width: 300px;
    +  height: 300px;
    +  top: -1000px;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="commonAncestor">
    +   <div id="targetDiv">
    +    Mouse and key target
    +   </div>
    +   <div id="partnerDiv">
    +    Auto-hide partner
    +   </div>
    +   <div id="popupDiv" style="visibility:hidden">
    +    Popup Contents Here.
    +   </div>
    +   <div id="moveOffscreenPopupDiv">
    +    Move offscreen popup contents here.
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.js b/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.js
    new file mode 100644
    index 0000000000..d96e4f372c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupbase_test.js
    @@ -0,0 +1,485 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PopupBaseTest');
    +goog.setTestOnly('goog.ui.PopupBaseTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.css3');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.PopupBase');
    +
    +var targetDiv;
    +var popupDiv;
    +var partnerDiv;
    +var clock;
    +var popup;
    +
    +function setUpPage() {
    +  targetDiv = goog.dom.getElement('targetDiv');
    +  popupDiv = goog.dom.getElement('popupDiv');
    +  partnerDiv = goog.dom.getElement('partnerDiv');
    +}
    +
    +function setUp() {
    +  popup = new goog.ui.PopupBase(popupDiv);
    +  clock = new goog.testing.MockClock(true);
    +}
    +
    +function tearDown() {
    +  popup.dispose();
    +  clock.uninstall();
    +  document.body.setAttribute('dir', 'ltr');
    +}
    +
    +function testSetVisible() {
    +  popup.setVisible(true);
    +  assertEquals('visible', popupDiv.style.visibility);
    +  assertEquals('', popupDiv.style.display);
    +  popup.setVisible(false);
    +  assertEquals('hidden', popupDiv.style.visibility);
    +  assertEquals('none', popupDiv.style.display);
    +}
    +
    +function testEscapeDismissal() {
    +  popup.setHideOnEscape(true);
    +  assertTrue('Sanity check that getHideOnEscape is true when set to true.',
    +      popup.getHideOnEscape());
    +  popup.setVisible(true);
    +  assertFalse('Escape key should be cancelled',
    +      goog.testing.events.fireKeySequence(
    +          targetDiv, goog.events.KeyCodes.ESC));
    +  assertFalse(popup.isVisible());
    +}
    +
    +function testEscapeDismissalCanBeDisabled() {
    +  popup.setHideOnEscape(false);
    +  popup.setVisible(true);
    +  assertTrue('Escape key should be cancelled',
    +      goog.testing.events.fireKeySequence(
    +          targetDiv, goog.events.KeyCodes.ESC));
    +  assertTrue(popup.isVisible());
    +}
    +
    +function testEscapeDismissalIsDisabledByDefault() {
    +  assertFalse(popup.getHideOnEscape());
    +}
    +
    +function testEscapeDismissalDoesNotRecognizeOtherKeys() {
    +  popup.setHideOnEscape(true);
    +  popup.setVisible(true);
    +  var eventsPropagated = 0;
    +  goog.events.listenOnce(goog.dom.getElement('commonAncestor'),
    +      [goog.events.EventType.KEYDOWN,
    +       goog.events.EventType.KEYUP,
    +       goog.events.EventType.KEYPRESS],
    +      function() {
    +        ++eventsPropagated;
    +      });
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +  assertTrue('The key event default action should not be prevented',
    +      goog.testing.events.fireKeySequence(
    +          targetDiv, goog.events.KeyCodes.A));
    +  assertEquals('Keydown, keyup, and keypress should have all propagated',
    +      3, eventsPropagated);
    +}
    +
    +function testEscapeDismissalCanBeCancelledByBeforeHideEvent() {
    +  popup.setHideOnEscape(true);
    +  popup.setVisible(true);
    +  var eventsPropagated = 0;
    +  goog.events.listenOnce(goog.dom.getElement('commonAncestor'),
    +      goog.events.EventType.KEYDOWN,
    +      function() {
    +        ++eventsPropagated;
    +      });
    +  // Make a listener so that we stop hiding with an event handler.
    +  goog.events.listenOnce(popup, goog.ui.PopupBase.EventType.BEFORE_HIDE,
    +      function(e) {
    +        e.preventDefault();
    +      });
    +  assertEquals('The hide should have been cancelled',
    +      true, popup.isVisible());
    +  assertTrue('The key event default action should not be prevented',
    +      goog.testing.events.fireKeySequence(
    +          targetDiv, goog.events.KeyCodes.ESC));
    +  assertEquals('Keydown should have all propagated',
    +      1, eventsPropagated);
    +}
    +
    +function testEscapeDismissalProvidesKeyTargetAsTargetForHideEvents() {
    +  popup.setHideOnEscape(true);
    +  popup.setVisible(true);
    +  var calls = 0;
    +  goog.events.listenOnce(popup,
    +      [goog.ui.PopupBase.EventType.BEFORE_HIDE,
    +       goog.ui.PopupBase.EventType.HIDE],
    +      function(e) {
    +        calls++;
    +        assertEquals('The key target should be the hide event target',
    +            'targetDiv', e.target.id);
    +      });
    +  goog.testing.events.fireKeySequence(
    +      targetDiv, goog.events.KeyCodes.ESC);
    +}
    +
    +function testAutoHide() {
    +  popup.setAutoHide(true);
    +  popup.setVisible(true);
    +  clock.tick(1000); // avoid bouncing
    +  goog.testing.events.fireClickSequence(targetDiv);
    +  assertFalse(popup.isVisible());
    +}
    +
    +function testAutoHideCanBeDisabled() {
    +  popup.setAutoHide(false);
    +  popup.setVisible(true);
    +  clock.tick(1000); // avoid bouncing
    +  goog.testing.events.fireClickSequence(targetDiv);
    +  assertTrue(
    +      'Should not be hidden if auto hide is disabled', popup.isVisible());
    +}
    +
    +function testAutoHideEnabledByDefault() {
    +  assertTrue(popup.getAutoHide());
    +}
    +
    +function testAutoHideWithPartners() {
    +  popup.setAutoHide(true);
    +  popup.setVisible(true);
    +  popup.addAutoHidePartner(targetDiv);
    +  popup.addAutoHidePartner(partnerDiv);
    +  clock.tick(1000); // avoid bouncing
    +
    +  goog.testing.events.fireClickSequence(targetDiv);
    +  assertTrue(popup.isVisible());
    +  goog.testing.events.fireClickSequence(partnerDiv);
    +  assertTrue(popup.isVisible());
    +
    +  popup.removeAutoHidePartner(partnerDiv);
    +  goog.testing.events.fireClickSequence(partnerDiv);
    +  assertFalse(popup.isVisible());
    +}
    +
    +function testCanAddElementDuringBeforeShow() {
    +  popup.setElement(null);
    +  goog.events.listenOnce(popup, goog.ui.PopupBase.EventType.BEFORE_SHOW,
    +      function() {
    +        popup.setElement(popupDiv);
    +      });
    +  popup.setVisible(true);
    +  assertTrue('Popup should be shown', popup.isVisible());
    +}
    +
    +function testShowWithNoElementThrowsException() {
    +  popup.setElement(null);
    +  var e = assertThrows(function() {
    +    popup.setVisible(true);
    +  });
    +  assertEquals('Caller must call setElement before trying to show the popup',
    +      e.message);
    +}
    +
    +function testShowEventFiredWithNoTransition() {
    +  var showHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
    +    showHandlerCalled = true;
    +  });
    +
    +  popup.setVisible(true);
    +  assertTrue(showHandlerCalled);
    +}
    +
    +function testHideEventFiredWithNoTransition() {
    +  var hideHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    hideHandlerCalled = true;
    +  });
    +
    +  popup.setVisible(true);
    +  popup.setVisible(false);
    +  assertTrue(hideHandlerCalled);
    +}
    +
    +function testOnShowTransition() {
    +  var mockTransition = new MockTransition();
    +
    +  var showHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.SHOW, function() {
    +    showHandlerCalled = true;
    +  });
    +
    +  popup.setTransition(mockTransition);
    +  popup.setVisible(true);
    +  assertTrue(mockTransition.wasPlayed);
    +
    +  assertFalse(showHandlerCalled);
    +  mockTransition.dispatchEvent(goog.fx.Transition.EventType.END);
    +  assertTrue(showHandlerCalled);
    +}
    +
    +function testOnHideTransition() {
    +  var mockTransition = new MockTransition();
    +
    +  var hideHandlerCalled = false;
    +  goog.events.listen(popup, goog.ui.PopupBase.EventType.HIDE, function() {
    +    hideHandlerCalled = true;
    +  });
    +
    +  popup.setTransition(undefined, mockTransition);
    +  popup.setVisible(true);
    +  assertFalse(mockTransition.wasPlayed);
    +
    +  popup.setVisible(false);
    +  assertTrue(mockTransition.wasPlayed);
    +
    +  assertFalse(hideHandlerCalled);
    +  mockTransition.dispatchEvent(goog.fx.Transition.EventType.END);
    +  assertTrue(hideHandlerCalled);
    +}
    +
    +function testSetVisibleWorksCorrectlyWithTransitions() {
    +  popup.setTransition(
    +      goog.fx.css3.fadeIn(popup.getElement(), 1),
    +      goog.fx.css3.fadeOut(popup.getElement(), 1));
    +
    +  // Consecutive calls to setVisible works without needing to wait for
    +  // transition to finish.
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  popup.setVisible(false);
    +  assertFalse(popup.isVisible());
    +  clock.tick(1100);
    +
    +  // Calling setVisible(true) immediately changed the state to visible.
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  clock.tick(1100);
    +
    +  // Consecutive calls to setVisible, in opposite order.
    +  popup.setVisible(false);
    +  popup.setVisible(true);
    +  assertTrue(popup.isVisible());
    +  clock.tick(1100);
    +
    +  // Calling setVisible(false) immediately changed the state to not visible.
    +  popup.setVisible(false);
    +  assertFalse(popup.isVisible());
    +  clock.tick(1100);
    +}
    +
    +function testWasRecentlyVisibleWorksCorrectlyWithTransitions() {
    +  popup.setTransition(
    +      goog.fx.css3.fadeIn(popup.getElement(), 1),
    +      goog.fx.css3.fadeOut(popup.getElement(), 1));
    +
    +  popup.setVisible(true);
    +  clock.tick(1100);
    +  popup.setVisible(false);
    +  assertTrue(popup.isOrWasRecentlyVisible());
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  assertFalse(popup.isOrWasRecentlyVisible());
    +}
    +
    +function testMoveOffscreenRTL() {
    +  document.body.setAttribute('dir', 'rtl');
    +  popup.reposition = function() {
    +    this.element_.style.left = '100px';
    +    this.element_.style.top = '100px';
    +  };
    +  popup.setType(goog.ui.PopupBase.Type.MOVE_OFFSCREEN);
    +  popup.setElement(goog.dom.getElement('moveOffscreenPopupDiv'));
    +  originalScrollWidth = goog.dom.getDocumentScrollElement().scrollWidth;
    +  popup.setVisible(true);
    +  popup.setVisible(false);
    +  assertFalse('Moving a popup offscreen should not cause scrollbars',
    +      goog.dom.getDocumentScrollElement().scrollWidth != originalScrollWidth);
    +}
    +
    +function testOnDocumentBlurDisabledCrossIframeDismissalWithoutDelay() {
    +  popup.setEnableCrossIframeDismissal(false);
    +  popup.setVisible(true);
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurDisabledCrossIframeDismissalWithDelay() {
    +  popup.setEnableCrossIframeDismissal(false);
    +  popup.setVisible(true);
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurActiveElementInsidePopupWithoutDelay() {
    +  popup.setVisible(true);
    +  var elementInsidePopup = goog.dom.createDom('div');
    +  goog.dom.append(popupDiv, elementInsidePopup);
    +  elementInsidePopup.setAttribute('tabIndex', 0);
    +  elementInsidePopup.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurActiveElementInsidePopupWithDelay() {
    +  popup.setVisible(true);
    +  var elementInsidePopup = goog.dom.createDom('div');
    +  goog.dom.append(popupDiv, elementInsidePopup);
    +  elementInsidePopup.setAttribute('tabIndex', 0);
    +  elementInsidePopup.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurActiveElementIsBodyWithoutDelay() {
    +  popup.setVisible(true);
    +  var bodyElement = goog.dom.getDomHelper().
    +      getElementsByTagNameAndClass('body')[0];
    +  bodyElement.setAttribute('tabIndex', 0);
    +  bodyElement.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurActiveElementIsBodyWithDelay() {
    +  popup.setVisible(true);
    +  var bodyElement = goog.dom.getDomHelper().
    +      getElementsByTagNameAndClass('body')[0];
    +  bodyElement.setAttribute('tabIndex', 0);
    +  bodyElement.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurEventTargetNotDocumentWithoutDelay() {
    +  popup.setVisible(true);
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, targetDiv);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurEventTargetNotDocumentWithDelay() {
    +  popup.setVisible(true);
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, targetDiv);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should remain visible', popup.isVisible());
    +}
    +
    +function testOnDocumentBlurShouldDebounceWithoutDelay() {
    +  popup.setVisible(true);
    +  var commonAncestor = goog.dom.getElement('commonAncestor');
    +  var focusDiv = goog.dom.createDom('div', 'tabIndex');
    +  focusDiv.setAttribute('tabIndex', 0);
    +  goog.dom.appendChild(commonAncestor, focusDiv);
    +  focusDiv.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should be visible', popup.isVisible());
    +  goog.dom.removeNode(focusDiv);
    +}
    +
    +function testOnDocumentBlurShouldNotDebounceWithDelay() {
    +  popup.setVisible(true);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  var commonAncestor = goog.dom.getElement('commonAncestor');
    +  var focusDiv = goog.dom.createDom('div', 'tabIndex');
    +  focusDiv.setAttribute('tabIndex', 0);
    +  goog.dom.appendChild(commonAncestor, focusDiv);
    +  focusDiv.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertFalse('Popup should be invisible', popup.isVisible());
    +  goog.dom.removeNode(focusDiv);
    +}
    +
    +
    +function testOnDocumentBlurShouldNotHideBubbleWithoutDelay() {
    +  popup.setVisible(true);
    +  var commonAncestor = goog.dom.getElement('commonAncestor');
    +  var focusDiv = goog.dom.createDom('div', 'tabIndex');
    +  focusDiv.setAttribute('tabIndex', 0);
    +  goog.dom.appendChild(commonAncestor, focusDiv);
    +  focusDiv.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertTrue('Popup should be visible', popup.isVisible());
    +  goog.dom.removeNode(focusDiv);
    +}
    +
    +function testOnDocumentBlurShouldHideBubbleWithDelay() {
    +  popup.setVisible(true);
    +  clock.tick(goog.ui.PopupBase.DEBOUNCE_DELAY_MS);
    +  var commonAncestor = goog.dom.getElement('commonAncestor');
    +  var focusDiv = goog.dom.createDom('div', 'tabIndex');
    +  focusDiv.setAttribute('tabIndex', 0);
    +  goog.dom.appendChild(commonAncestor, focusDiv);
    +  focusDiv.focus();
    +  var e = new goog.testing.events.Event(
    +      goog.events.EventType.BLUR, document);
    +  goog.testing.events.fireBrowserEvent(e);
    +  assertFalse('Popup should be invisible', popup.isVisible());
    +  goog.dom.removeNode(focusDiv);
    +}
    +
    +
    +
    +/**
    + * @implements {goog.fx.Transition}
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +var MockTransition = function() {
    +  MockTransition.base(this, 'constructor');
    +  this.wasPlayed = false;
    +};
    +goog.inherits(MockTransition, goog.events.EventTarget);
    +
    +
    +MockTransition.prototype.play = function() {
    +  this.wasPlayed = true;
    +};
    +
    +
    +MockTransition.prototype.stop = goog.nullFunction;
    +
    +
    +// TODO(gboyer): Write better unit tests for click and cross-iframe dismissal.
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker.js b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker.js
    new file mode 100644
    index 0000000000..cb0373918a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker.js
    @@ -0,0 +1,475 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Popup Color Picker implementation.  This is intended to be
    + * less general than goog.ui.ColorPicker and presents a default set of colors
    + * that CCC apps currently use in their color pickers.
    + *
    + * @see ../demos/popupcolorpicker.html
    + */
    +
    +goog.provide('goog.ui.PopupColorPicker');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.ui.ColorPicker');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Popup');
    +
    +
    +
    +/**
    + * Popup color picker widget.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.ColorPicker=} opt_colorPicker Optional color picker to use
    + *     for this popup.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.PopupColorPicker = function(opt_domHelper, opt_colorPicker) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  if (opt_colorPicker) {
    +    this.colorPicker_ = opt_colorPicker;
    +  }
    +};
    +goog.inherits(goog.ui.PopupColorPicker, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.PopupColorPicker);
    +
    +
    +/**
    + * Whether the color picker is initialized.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.initialized_ = false;
    +
    +
    +/**
    + * Instance of a color picker control.
    + * @type {goog.ui.ColorPicker}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.colorPicker_ = null;
    +
    +
    +/**
    + * Instance of goog.ui.Popup used to manage the behavior of the color picker.
    + * @type {goog.ui.Popup}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.popup_ = null;
    +
    +
    +/**
    + * Corner of the popup which is pinned to the attaching element.
    + * @type {goog.positioning.Corner}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.pinnedCorner_ =
    +    goog.positioning.Corner.TOP_START;
    +
    +
    +/**
    + * Corner of the attaching element where the popup shows.
    + * @type {goog.positioning.Corner}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.popupCorner_ =
    +    goog.positioning.Corner.BOTTOM_START;
    +
    +
    +/**
    + * Reference to the element that triggered the last popup.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.lastTarget_ = null;
    +
    +
    +/** @private {boolean} */
    +goog.ui.PopupColorPicker.prototype.rememberSelection_;
    +
    +
    +/**
    + * Whether the color picker can move the focus to its key event target when it
    + * is shown.  The default is true.  Setting to false can break keyboard
    + * navigation, but this is needed for certain scenarios, for example the
    + * toolbar menu in trogedit which can't have the selection changed.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.allowAutoFocus_ = true;
    +
    +
    +/**
    + * Whether the color picker can accept focus.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.focusable_ = true;
    +
    +
    +/**
    + * If true, then the colorpicker will toggle off if it is already visible.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.toggleMode_ = true;
    +
    +
    +/**
    + * If true, the colorpicker will appear on hover.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.showOnHover_ = false;
    +
    +
    +/** @override */
    +goog.ui.PopupColorPicker.prototype.createDom = function() {
    +  goog.ui.PopupColorPicker.superClass_.createDom.call(this);
    +  this.popup_ = new goog.ui.Popup(this.getElement());
    +  this.popup_.setPinnedCorner(this.pinnedCorner_);
    +  goog.dom.classlist.set(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('goog-popupcolorpicker'));
    +  this.getElement().unselectable = 'on';
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupColorPicker.prototype.disposeInternal = function() {
    +  goog.ui.PopupColorPicker.superClass_.disposeInternal.call(this);
    +  this.colorPicker_ = null;
    +  this.lastTarget_ = null;
    +  this.initialized_ = false;
    +  if (this.popup_) {
    +    this.popup_.dispose();
    +    this.popup_ = null;
    +  }
    +};
    +
    +
    +/**
    + * ColorPickers cannot be used to decorate pre-existing html, since the
    + * structure they build is fairly complicated.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Returns always false.
    + * @override
    + */
    +goog.ui.PopupColorPicker.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/**
    + * @return {goog.ui.ColorPicker} The color picker instance.
    + */
    +goog.ui.PopupColorPicker.prototype.getColorPicker = function() {
    +  return this.colorPicker_;
    +};
    +
    +
    +/**
    + * Returns whether the Popup dismisses itself when the user clicks outside of
    + * it.
    + * @return {boolean} Whether the Popup autohides on an external click.
    + */
    +goog.ui.PopupColorPicker.prototype.getAutoHide = function() {
    +  return !!this.popup_ && this.popup_.getAutoHide();
    +};
    +
    +
    +/**
    + * Sets whether the Popup dismisses itself when the user clicks outside of it -
    + * must be called after the Popup has been created (in createDom()),
    + * otherwise it does nothing.
    + *
    + * @param {boolean} autoHide Whether to autohide on an external click.
    + */
    +goog.ui.PopupColorPicker.prototype.setAutoHide = function(autoHide) {
    +  if (this.popup_) {
    +    this.popup_.setAutoHide(autoHide);
    +  }
    +};
    +
    +
    +/**
    + * Returns the region inside which the Popup dismisses itself when the user
    + * clicks, or null if it was not set. Null indicates the entire document is
    + * the autohide region.
    + * @return {Element} The DOM element for autohide, or null if it hasn't been
    + *     set.
    + */
    +goog.ui.PopupColorPicker.prototype.getAutoHideRegion = function() {
    +  return this.popup_ && this.popup_.getAutoHideRegion();
    +};
    +
    +
    +/**
    + * Sets the region inside which the Popup dismisses itself when the user
    + * clicks - must be called after the Popup has been created (in createDom()),
    + * otherwise it does nothing.
    + *
    + * @param {Element} element The DOM element for autohide.
    + */
    +goog.ui.PopupColorPicker.prototype.setAutoHideRegion = function(element) {
    +  if (this.popup_) {
    +    this.popup_.setAutoHideRegion(element);
    +  }
    +};
    +
    +
    +/**
    + * Returns the {@link goog.ui.PopupBase} from this picker. Returns null if the
    + * popup has not yet been created.
    + *
    + * NOTE: This should *ONLY* be called from tests. If called before createDom(),
    + * this should return null.
    + *
    + * @return {goog.ui.PopupBase?} The popup or null if it hasn't been created.
    + */
    +goog.ui.PopupColorPicker.prototype.getPopup = function() {
    +  return this.popup_;
    +};
    +
    +
    +/**
    + * @return {Element} The last element that triggered the popup.
    + */
    +goog.ui.PopupColorPicker.prototype.getLastTarget = function() {
    +  return this.lastTarget_;
    +};
    +
    +
    +/**
    + * Attaches the popup color picker to an element.
    + * @param {Element} element The element to attach to.
    + */
    +goog.ui.PopupColorPicker.prototype.attach = function(element) {
    +  if (this.showOnHover_) {
    +    this.getHandler().listen(element, goog.events.EventType.MOUSEOVER,
    +                             this.show_);
    +  } else {
    +    this.getHandler().listen(element, goog.events.EventType.MOUSEDOWN,
    +                             this.show_);
    +  }
    +};
    +
    +
    +/**
    + * Detatches the popup color picker from an element.
    + * @param {Element} element The element to detach from.
    + */
    +goog.ui.PopupColorPicker.prototype.detach = function(element) {
    +  if (this.showOnHover_) {
    +    this.getHandler().unlisten(element, goog.events.EventType.MOUSEOVER,
    +                               this.show_);
    +  } else {
    +    this.getHandler().unlisten(element, goog.events.EventType.MOUSEOVER,
    +                               this.show_);
    +  }
    +};
    +
    +
    +/**
    + * Gets the color that is currently selected in this color picker.
    + * @return {?string} The hex string of the color selected, or null if no
    + *     color is selected.
    + */
    +goog.ui.PopupColorPicker.prototype.getSelectedColor = function() {
    +  return this.colorPicker_.getSelectedColor();
    +};
    +
    +
    +/**
    + * Sets whether the color picker can accept focus.
    + * @param {boolean} focusable True iff the color picker can accept focus.
    + */
    +goog.ui.PopupColorPicker.prototype.setFocusable = function(focusable) {
    +  this.focusable_ = focusable;
    +  if (this.colorPicker_) {
    +    // TODO(user): In next revision sort the behavior of passing state to
    +    // children correctly
    +    this.colorPicker_.setFocusable(focusable);
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the color picker can automatically move focus to its key event
    + * target when it is set to visible.
    + * @param {boolean} allow Whether to allow auto focus.
    + */
    +goog.ui.PopupColorPicker.prototype.setAllowAutoFocus = function(allow) {
    +  this.allowAutoFocus_ = allow;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the color picker can automatically move focus to
    + *     its key event target when it is set to visible.
    + */
    +goog.ui.PopupColorPicker.prototype.getAllowAutoFocus = function() {
    +  return this.allowAutoFocus_;
    +};
    +
    +
    +/**
    + * Sets whether the color picker should toggle off if it is already open.
    + * @param {boolean} toggle The new toggle mode.
    + */
    +goog.ui.PopupColorPicker.prototype.setToggleMode = function(toggle) {
    +  this.toggleMode_ = toggle;
    +};
    +
    +
    +/**
    + * Gets whether the colorpicker is in toggle mode
    + * @return {boolean} toggle.
    + */
    +goog.ui.PopupColorPicker.prototype.getToggleMode = function() {
    +  return this.toggleMode_;
    +};
    +
    +
    +/**
    + * Sets whether the picker remembers the last selected color between popups.
    + *
    + * @param {boolean} remember Whether to remember the selection.
    + */
    +goog.ui.PopupColorPicker.prototype.setRememberSelection = function(remember) {
    +  this.rememberSelection_ = remember;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the picker remembers the last selected color
    + *     between popups.
    + */
    +goog.ui.PopupColorPicker.prototype.getRememberSelection = function() {
    +  return this.rememberSelection_;
    +};
    +
    +
    +/**
    + * Add an array of colors to the colors displayed by the color picker.
    + * Does not add duplicated colors.
    + * @param {Array<string>} colors The array of colors to be added.
    + */
    +goog.ui.PopupColorPicker.prototype.addColors = function(colors) {
    +
    +};
    +
    +
    +/**
    + * Clear the colors displayed by the color picker.
    + */
    +goog.ui.PopupColorPicker.prototype.clearColors = function() {
    +
    +};
    +
    +
    +/**
    + * Set the pinned corner of the popup.
    + * @param {goog.positioning.Corner} corner The corner of the popup which is
    + *     pinned to the attaching element.
    + */
    +goog.ui.PopupColorPicker.prototype.setPinnedCorner = function(corner) {
    +  this.pinnedCorner_ = corner;
    +  if (this.popup_) {
    +    this.popup_.setPinnedCorner(this.pinnedCorner_);
    +  }
    +};
    +
    +
    +/**
    + * Sets which corner of the attaching element this popup shows up.
    + * @param {goog.positioning.Corner} corner The corner of the attaching element
    + *     where to show the popup.
    + */
    +goog.ui.PopupColorPicker.prototype.setPopupCorner = function(corner) {
    +  this.popupCorner_ = corner;
    +};
    +
    +
    +/**
    + * Sets whether the popup shows up on hover. By default, appears on click.
    + * @param {boolean} showOnHover True if popup should appear on hover.
    + */
    +goog.ui.PopupColorPicker.prototype.setShowOnHover = function(showOnHover) {
    +  this.showOnHover_ = showOnHover;
    +};
    +
    +
    +/**
    + * Handles click events on the targets and shows the color picker.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.show_ = function(e) {
    +  if (!this.initialized_) {
    +    this.colorPicker_ = this.colorPicker_ ||
    +        goog.ui.ColorPicker.createSimpleColorGrid(this.getDomHelper());
    +    this.colorPicker_.setFocusable(this.focusable_);
    +    this.addChild(this.colorPicker_, true);
    +    this.getHandler().listen(this.colorPicker_,
    +        goog.ui.ColorPicker.EventType.CHANGE, this.onColorPicked_);
    +    this.initialized_ = true;
    +  }
    +
    +  if (this.popup_.isOrWasRecentlyVisible() && this.toggleMode_ &&
    +      this.lastTarget_ == e.currentTarget) {
    +    this.popup_.setVisible(false);
    +    return;
    +  }
    +
    +  this.lastTarget_ = /** @type {Element} */ (e.currentTarget);
    +  this.popup_.setPosition(new goog.positioning.AnchoredPosition(
    +      this.lastTarget_, this.popupCorner_));
    +  if (!this.rememberSelection_) {
    +    this.colorPicker_.setSelectedIndex(-1);
    +  }
    +  this.popup_.setVisible(true);
    +  if (this.allowAutoFocus_) {
    +    this.colorPicker_.focus();
    +  }
    +};
    +
    +
    +/**
    + * Handles the color change event.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.PopupColorPicker.prototype.onColorPicked_ = function(e) {
    +  // When we show the color picker we reset the color, which triggers an event.
    +  // Here we block that event so that it doesn't dismiss the popup
    +  // TODO(user): Update the colorpicker to allow selection to be cleared
    +  if (this.colorPicker_.getSelectedIndex() == -1) {
    +    e.stopPropagation();
    +    return;
    +  }
    +  this.popup_.setVisible(false);
    +  if (this.allowAutoFocus_) {
    +    this.lastTarget_.focus();
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.html
    new file mode 100644
    index 0000000000..08d4efeee8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Popup
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PopupColorPickerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="containingDiv">
    +   <a href="javascript:void(0)" id="button1">
    +    color picker
    +   </a>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.js
    new file mode 100644
    index 0000000000..3ab408674c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupcolorpicker_test.js
    @@ -0,0 +1,66 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PopupColorPickerTest');
    +goog.setTestOnly('goog.ui.PopupColorPickerTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ColorPicker');
    +goog.require('goog.ui.PopupColorPicker');
    +
    +// Unittest to ensure that the popup gets created in createDom().
    +function testPopupCreation() {
    +  var picker = new goog.ui.PopupColorPicker();
    +  picker.createDom();
    +  assertNotNull(picker.getPopup());
    +}
    +
    +function testAutoHideIsSetProperly() {
    +  var picker = new goog.ui.PopupColorPicker();
    +  picker.createDom();
    +  picker.setAutoHide(true);
    +  var containingDiv = goog.dom.getElement('containingDiv');
    +  picker.setAutoHideRegion(containingDiv);
    +  assertTrue(picker.getAutoHide());
    +  assertEquals(containingDiv, picker.getAutoHideRegion());
    +}
    +
    +// Unittest to ensure the popup opens with a custom color picker.
    +function testCustomColorPicker() {
    +  var button1 = document.getElementById('button1');
    +  var domHelper = goog.dom.getDomHelper();
    +  var colorPicker = new goog.ui.ColorPicker();
    +  colorPicker.setColors(['#ffffff', '#000000']);
    +  var picker = new goog.ui.PopupColorPicker(domHelper, colorPicker);
    +  picker.render();
    +  picker.attach(button1);
    +  assertNotNull(picker.getColorPicker());
    +  assertNotNull(picker.getPopup().getElement());
    +  assertNull(picker.getSelectedColor());
    +
    +  var changeEvents = 0;
    +  goog.events.listen(picker, goog.ui.ColorPicker.EventType.CHANGE, function(e) {
    +    changeEvents++;
    +  });
    +
    +  // Select the first color.
    +  goog.testing.events.fireClickSequence(button1);
    +  goog.testing.events.fireClickSequence(
    +      document.getElementById('goog-palette-cell-0').firstChild);
    +  assertEquals('#ffffff', picker.getSelectedColor());
    +  assertEquals(1, changeEvents);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker.js b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker.js
    new file mode 100644
    index 0000000000..e5158ed199
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker.js
    @@ -0,0 +1,288 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Popup Date Picker implementation.  Pairs a goog.ui.DatePicker
    + * with a goog.ui.Popup allowing the DatePicker to be attached to elements.
    + *
    + * @see ../demos/popupdatepicker.html
    + */
    +
    +goog.provide('goog.ui.PopupDatePicker');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.DatePicker');
    +goog.require('goog.ui.Popup');
    +goog.require('goog.ui.PopupBase');
    +
    +
    +
    +/**
    + * Popup date picker widget. Fires goog.ui.PopupBase.EventType.SHOW or HIDE
    + * events when its visibility changes.
    + *
    + * @param {goog.ui.DatePicker=} opt_datePicker Optional DatePicker.  This
    + *     enables the use of a custom date-picker instance.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.PopupDatePicker = function(opt_datePicker, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  this.datePicker_ = opt_datePicker || new goog.ui.DatePicker();
    +};
    +goog.inherits(goog.ui.PopupDatePicker, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.PopupDatePicker);
    +
    +
    +/**
    + * Instance of a date picker control.
    + * @type {goog.ui.DatePicker?}
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.datePicker_ = null;
    +
    +
    +/**
    + * Instance of goog.ui.Popup used to manage the behavior of the date picker.
    + * @type {goog.ui.Popup?}
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.popup_ = null;
    +
    +
    +/**
    + * Reference to the element that triggered the last popup.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.lastTarget_ = null;
    +
    +
    +/**
    + * Whether the date picker can move the focus to its key event target when it
    + * is shown.  The default is true.  Setting to false can break keyboard
    + * navigation, but this is needed for certain scenarios, for example the
    + * toolbar menu in trogedit which can't have the selection changed.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.allowAutoFocus_ = true;
    +
    +
    +/** @override */
    +goog.ui.PopupDatePicker.prototype.createDom = function() {
    +  goog.ui.PopupDatePicker.superClass_.createDom.call(this);
    +  this.getElement().className = goog.getCssName('goog-popupdatepicker');
    +  this.popup_ = new goog.ui.Popup(this.getElement());
    +  this.popup_.setParentEventTarget(this);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the date picker is visible.
    + */
    +goog.ui.PopupDatePicker.prototype.isVisible = function() {
    +  return this.popup_ ? this.popup_.isVisible() : false;
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupDatePicker.prototype.enterDocument = function() {
    +  goog.ui.PopupDatePicker.superClass_.enterDocument.call(this);
    +  // Create the DatePicker, if it isn't already.
    +  // Done here as DatePicker assumes that the element passed to it is attached
    +  // to a document.
    +  if (!this.datePicker_.isInDocument()) {
    +    var el = this.getElement();
    +    // Make it initially invisible
    +    el.style.visibility = 'hidden';
    +    goog.style.setElementShown(el, false);
    +    this.datePicker_.decorate(el);
    +  }
    +  this.getHandler().listen(this.datePicker_, goog.ui.DatePicker.Events.CHANGE,
    +                           this.onDateChanged_);
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupDatePicker.prototype.disposeInternal = function() {
    +  goog.ui.PopupDatePicker.superClass_.disposeInternal.call(this);
    +  if (this.popup_) {
    +    this.popup_.dispose();
    +    this.popup_ = null;
    +  }
    +  this.datePicker_.dispose();
    +  this.datePicker_ = null;
    +  this.lastTarget_ = null;
    +};
    +
    +
    +/**
    + * DatePicker cannot be used to decorate pre-existing html, since they're
    + * not based on Components.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Returns always false.
    + * @override
    + */
    +goog.ui.PopupDatePicker.prototype.canDecorate = function(element) {
    +  return false;
    +};
    +
    +
    +/**
    + * @return {goog.ui.DatePicker} The date picker instance.
    + */
    +goog.ui.PopupDatePicker.prototype.getDatePicker = function() {
    +  return this.datePicker_;
    +};
    +
    +
    +/**
    + * @return {goog.date.Date?} The selected date, if any.  See
    + *     goog.ui.DatePicker.getDate().
    + */
    +goog.ui.PopupDatePicker.prototype.getDate = function() {
    +  return this.datePicker_.getDate();
    +};
    +
    +
    +/**
    + * Sets the selected date.  See goog.ui.DatePicker.setDate().
    + * @param {goog.date.Date?} date The date to select.
    + */
    +goog.ui.PopupDatePicker.prototype.setDate = function(date) {
    +  this.datePicker_.setDate(date);
    +};
    +
    +
    +/**
    + * @return {Element} The last element that triggered the popup.
    + */
    +goog.ui.PopupDatePicker.prototype.getLastTarget = function() {
    +  return this.lastTarget_;
    +};
    +
    +
    +/**
    + * Attaches the popup date picker to an element.
    + * @param {Element} element The element to attach to.
    + */
    +goog.ui.PopupDatePicker.prototype.attach = function(element) {
    +  this.getHandler().listen(element, goog.events.EventType.MOUSEDOWN,
    +                           this.showPopup_);
    +};
    +
    +
    +/**
    + * Detatches the popup date picker from an element.
    + * @param {Element} element The element to detach from.
    + */
    +goog.ui.PopupDatePicker.prototype.detach = function(element) {
    +  this.getHandler().unlisten(element, goog.events.EventType.MOUSEDOWN,
    +                             this.showPopup_);
    +};
    +
    +
    +/**
    + * Sets whether the date picker can automatically move focus to its key event
    + * target when it is set to visible.
    + * @param {boolean} allow Whether to allow auto focus.
    + */
    +goog.ui.PopupDatePicker.prototype.setAllowAutoFocus = function(allow) {
    +  this.allowAutoFocus_ = allow;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the date picker can automatically move focus to
    + * its key event target when it is set to visible.
    + */
    +goog.ui.PopupDatePicker.prototype.getAllowAutoFocus = function() {
    +  return this.allowAutoFocus_;
    +};
    +
    +
    +/**
    + * Show the popup at the bottom-left corner of the specified element.
    + * @param {Element} element Reference element for displaying the popup -- popup
    + *     will appear at the bottom-left corner of this element.
    + */
    +goog.ui.PopupDatePicker.prototype.showPopup = function(element) {
    +  this.lastTarget_ = element;
    +  this.popup_.setPosition(new goog.positioning.AnchoredPosition(
    +      element,
    +      goog.positioning.Corner.BOTTOM_START,
    +      (goog.positioning.Overflow.ADJUST_X_EXCEPT_OFFSCREEN |
    +      goog.positioning.Overflow.ADJUST_Y_EXCEPT_OFFSCREEN)));
    +
    +  // Don't listen to date changes while we're setting up the popup so we don't
    +  // have to worry about change events when we call setDate().
    +  this.getHandler().unlisten(this.datePicker_, goog.ui.DatePicker.Events.CHANGE,
    +                             this.onDateChanged_);
    +  this.datePicker_.setDate(null);
    +
    +  // Forward the change event onto our listeners.  Done before we start
    +  // listening to date changes again, so that listeners can change the date
    +  // without firing more events.
    +  this.dispatchEvent(goog.ui.PopupBase.EventType.SHOW);
    +
    +  this.getHandler().listen(this.datePicker_, goog.ui.DatePicker.Events.CHANGE,
    +                           this.onDateChanged_);
    +  this.popup_.setVisible(true);
    +  if (this.allowAutoFocus_) {
    +    this.getElement().focus();  // Our element contains the date picker.
    +  }
    +};
    +
    +
    +/**
    + * Handles click events on the targets and shows the date picker.
    + * @param {goog.events.Event} event The click event.
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.showPopup_ = function(event) {
    +  this.showPopup(/** @type {Element} */ (event.currentTarget));
    +};
    +
    +
    +/**
    + * Hides this popup.
    + */
    +goog.ui.PopupDatePicker.prototype.hidePopup = function() {
    +  this.popup_.setVisible(false);
    +  if (this.allowAutoFocus_ && this.lastTarget_) {
    +    this.lastTarget_.focus();
    +  }
    +};
    +
    +
    +/**
    + * Called when the date is changed.
    + *
    + * @param {goog.events.Event} event The date change event.
    + * @private
    + */
    +goog.ui.PopupDatePicker.prototype.onDateChanged_ = function(event) {
    +  this.hidePopup();
    +
    +  // Forward the change event onto our listeners.
    +  this.dispatchEvent(event);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.html b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.html
    new file mode 100644
    index 0000000000..b38237aa72
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.PopupDatePicker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PopupDatePickerTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.js b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.js
    new file mode 100644
    index 0000000000..83d477a1f7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupdatepicker_test.js
    @@ -0,0 +1,67 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PopupDatePickerTest');
    +goog.setTestOnly('goog.ui.PopupDatePickerTest');
    +
    +goog.require('goog.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.ui.PopupDatePicker');
    +
    +var popupDatePicker;
    +
    +function setUp() {
    +  popupDatePicker = new goog.ui.PopupDatePicker();
    +}
    +
    +function tearDown() {
    +  popupDatePicker.dispose();
    +}
    +
    +function testIsVisible() {
    +  assertFalse(popupDatePicker.isVisible());
    +  popupDatePicker.createDom();
    +  assertFalse(popupDatePicker.isVisible());
    +  popupDatePicker.render();
    +  assertFalse(popupDatePicker.isVisible());
    +  popupDatePicker.showPopup(document.body);
    +  assertTrue(popupDatePicker.isVisible());
    +  popupDatePicker.hidePopup();
    +  assertFalse(popupDatePicker.isVisible());
    +}
    +
    +function testFiresShowAndHideEvents() {
    +  var showHandler = goog.testing.recordFunction();
    +  var hideHandler = goog.testing.recordFunction();
    +  goog.events.listen(popupDatePicker, goog.ui.PopupBase.EventType.SHOW,
    +      showHandler);
    +  goog.events.listen(popupDatePicker, goog.ui.PopupBase.EventType.HIDE,
    +      hideHandler);
    +  popupDatePicker.createDom();
    +  popupDatePicker.render();
    +  assertEquals(0, showHandler.getCallCount());
    +  assertEquals(0, hideHandler.getCallCount());
    +
    +  popupDatePicker.showPopup(document.body);
    +  // Bug in goog.ui.Popup: the SHOW event is fired twice.
    +  assertEquals(2, showHandler.getCallCount());
    +  assertEquals(0, hideHandler.getCallCount());
    +  showHandler.reset();
    +
    +  popupDatePicker.hidePopup();
    +  assertEquals(0, showHandler.getCallCount());
    +  assertEquals(1, hideHandler.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupmenu.js b/mapbox-gl/closure-library/closure/goog/ui/popupmenu.js
    new file mode 100644
    index 0000000000..f33fdfc50c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupmenu.js
    @@ -0,0 +1,558 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A menu class for showing popups.  A single popup can be
    + * attached to multiple anchor points.  The menu will try to reposition itself
    + * if it goes outside the viewport.
    + *
    + * Decoration is the same as goog.ui.Menu except that the outer DIV can have a
    + * 'for' property, which is the ID of the element which triggers the popup.
    + *
    + * Decorate Example:
    + * <button id="dButton">Decorated Popup</button>
    + * <div id="dMenu" for="dButton" class="goog-menu">
    + *   <div class="goog-menuitem">A a</div>
    + *   <div class="goog-menuitem">B b</div>
    + *   <div class="goog-menuitem">C c</div>
    + *   <div class="goog-menuitem">D d</div>
    + *   <div class="goog-menuitem">E e</div>
    + *   <div class="goog-menuitem">F f</div>
    + * </div>
    + *
    + * TESTED=FireFox 2.0, IE6, Opera 9, Chrome.
    + * TODO(user): Key handling is flakey in Opera and Chrome
    + *
    + * @see ../demos/popupmenu.html
    + */
    +
    +goog.provide('goog.ui.PopupMenu');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.positioning.AnchoredViewportPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.MenuAnchoredPosition');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.ViewportClientPosition');
    +goog.require('goog.structs.Map');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A basic menu class.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {goog.ui.MenuRenderer=} opt_renderer Renderer used to render or
    + *     decorate the container; defaults to {@link goog.ui.MenuRenderer}.
    + * @extends {goog.ui.Menu}
    + * @constructor
    + */
    +goog.ui.PopupMenu = function(opt_domHelper, opt_renderer) {
    +  goog.ui.Menu.call(this, opt_domHelper, opt_renderer);
    +
    +  this.setAllowAutoFocus(true);
    +
    +  // Popup menus are hidden by default.
    +  this.setVisible(false, true);
    +
    +  /**
    +   * Map of attachment points for the menu.  Key -> Object
    +   * @type {!goog.structs.Map}
    +   * @private
    +   */
    +  this.targets_ = new goog.structs.Map();
    +};
    +goog.inherits(goog.ui.PopupMenu, goog.ui.Menu);
    +goog.tagUnsealableClass(goog.ui.PopupMenu);
    +
    +
    +/**
    + * If true, then if the menu will toggle off if it is already visible.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.toggleMode_ = false;
    +
    +
    +/**
    + * Time that the menu was last shown.
    + * @type {number}
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.lastHide_ = 0;
    +
    +
    +/**
    + * Current element where the popup menu is anchored.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.currentAnchor_ = null;
    +
    +
    +/**
    + * Decorate an existing HTML structure with the menu. Menu items will be
    + * constructed from elements with classname 'goog-menuitem', separators will be
    + * made from HR elements.
    + * @param {Element} element Element to decorate.
    + * @override
    + */
    +goog.ui.PopupMenu.prototype.decorateInternal = function(element) {
    +  goog.ui.PopupMenu.superClass_.decorateInternal.call(this, element);
    +  // 'for' is a custom attribute for attaching the menu to a click target
    +  var htmlFor = element.getAttribute('for') || element.htmlFor;
    +  if (htmlFor) {
    +    this.attach(
    +        this.getDomHelper().getElement(htmlFor),
    +        goog.positioning.Corner.BOTTOM_LEFT);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupMenu.prototype.enterDocument = function() {
    +  goog.ui.PopupMenu.superClass_.enterDocument.call(this);
    +
    +  this.targets_.forEach(this.attachEvent_, this);
    +
    +  var handler = this.getHandler();
    +  handler.listen(
    +      this, goog.ui.Component.EventType.ACTION, this.onAction_);
    +  handler.listen(this.getDomHelper().getDocument(),
    +      goog.events.EventType.MOUSEDOWN, this.onDocClick, true);
    +
    +  // Webkit doesn't fire a mousedown event when opening the context menu,
    +  // but we need one to update menu visibility properly. So in Safari handle
    +  // contextmenu mouse events like mousedown.
    +  // {@link http://bugs.webkit.org/show_bug.cgi?id=6595}
    +  if (goog.userAgent.WEBKIT) {
    +    handler.listen(this.getDomHelper().getDocument(),
    +        goog.events.EventType.CONTEXTMENU, this.onDocClick, true);
    +  }
    +};
    +
    +
    +/**
    + * Attaches the menu to a new popup position and anchor element.  A menu can
    + * only be attached to an element once, since attaching the same menu for
    + * multiple positions doesn't make sense.
    + *
    + * @param {Element} element Element whose click event should trigger the menu.
    + * @param {goog.positioning.Corner=} opt_targetCorner Corner of the target that
    + *     the menu should be anchored to.
    + * @param {goog.positioning.Corner=} opt_menuCorner Corner of the menu that
    + *     should be anchored.
    + * @param {boolean=} opt_contextMenu Whether the menu should show on
    + *     {@link goog.events.EventType.CONTEXTMENU} events, false if it should
    + *     show on {@link goog.events.EventType.MOUSEDOWN} events. Default is
    + *     MOUSEDOWN.
    + * @param {goog.math.Box=} opt_margin Margin for the popup used in positioning
    + *     algorithms.
    + */
    +goog.ui.PopupMenu.prototype.attach = function(
    +    element, opt_targetCorner, opt_menuCorner, opt_contextMenu, opt_margin) {
    +
    +  if (this.isAttachTarget(element)) {
    +    // Already in the popup, so just return.
    +    return;
    +  }
    +
    +  var target = this.createAttachTarget(element, opt_targetCorner,
    +      opt_menuCorner, opt_contextMenu, opt_margin);
    +
    +  if (this.isInDocument()) {
    +    this.attachEvent_(target);
    +  }
    +};
    +
    +
    +/**
    + * Creates an object describing how the popup menu should be attached to the
    + * anchoring element based on the given parameters. The created object is
    + * stored, keyed by {@code element} and is retrievable later by invoking
    + * {@link #getAttachTarget(element)} at a later point.
    + *
    + * Subclass may add more properties to the returned object, as needed.
    + *
    + * @param {Element} element Element whose click event should trigger the menu.
    + * @param {goog.positioning.Corner=} opt_targetCorner Corner of the target that
    + *     the menu should be anchored to.
    + * @param {goog.positioning.Corner=} opt_menuCorner Corner of the menu that
    + *     should be anchored.
    + * @param {boolean=} opt_contextMenu Whether the menu should show on
    + *     {@link goog.events.EventType.CONTEXTMENU} events, false if it should
    + *     show on {@link goog.events.EventType.MOUSEDOWN} events. Default is
    + *     MOUSEDOWN.
    + * @param {goog.math.Box=} opt_margin Margin for the popup used in positioning
    + *     algorithms.
    + *
    + * @return {Object} An object that describes how the popup menu should be
    + *     attached to the anchoring element.
    + *
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.createAttachTarget = function(
    +    element, opt_targetCorner, opt_menuCorner, opt_contextMenu, opt_margin) {
    +  if (!element) {
    +    return null;
    +  }
    +
    +  var target = {
    +    element_: element,
    +    targetCorner_: opt_targetCorner,
    +    menuCorner_: opt_menuCorner,
    +    eventType_: opt_contextMenu ? goog.events.EventType.CONTEXTMENU :
    +        goog.events.EventType.MOUSEDOWN,
    +    margin_: opt_margin
    +  };
    +
    +  this.targets_.set(goog.getUid(element), target);
    +
    +  return target;
    +};
    +
    +
    +/**
    + * Returns the object describing how the popup menu should be attach to given
    + * element or {@code null}. The object is created and the association is formed
    + * when {@link #attach} is invoked.
    + *
    + * @param {Element} element DOM element.
    + * @return {Object} The object created when {@link attach} is invoked on
    + *     {@code element}. Returns {@code null} if the element does not trigger
    + *     the menu (i.e. {@link attach} has never been invoked on
    + *     {@code element}).
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.getAttachTarget = function(element) {
    +  return element ?
    +      /** @type {Object} */(this.targets_.get(goog.getUid(element))) :
    +      null;
    +};
    +
    +
    +/**
    + * @param {Element} element Any DOM element.
    + * @return {boolean} Whether clicking on the given element will trigger the
    + *     menu.
    + *
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.isAttachTarget = function(element) {
    +  return element ? this.targets_.containsKey(goog.getUid(element)) : false;
    +};
    +
    +
    +/**
    + * @return {Element} The current element where the popup is anchored, if it's
    + *     visible.
    + */
    +goog.ui.PopupMenu.prototype.getAttachedElement = function() {
    +  return this.currentAnchor_;
    +};
    +
    +
    +/**
    + * Attaches an event listener to a target
    + * @param {Object} target The target to attach an event to.
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.attachEvent_ = function(target) {
    +  this.getHandler().listen(
    +      target.element_, target.eventType_, this.onTargetClick_);
    +};
    +
    +
    +/**
    + * Detaches all listeners
    + */
    +goog.ui.PopupMenu.prototype.detachAll = function() {
    +  if (this.isInDocument()) {
    +    var keys = this.targets_.getKeys();
    +    for (var i = 0; i < keys.length; i++) {
    +      this.detachEvent_(/** @type {Object} */ (this.targets_.get(keys[i])));
    +    }
    +  }
    +
    +  this.targets_.clear();
    +};
    +
    +
    +/**
    + * Detaches a menu from a given element.
    + * @param {Element} element Element whose click event should trigger the menu.
    + */
    +goog.ui.PopupMenu.prototype.detach = function(element) {
    +  if (!this.isAttachTarget(element)) {
    +    throw Error('Menu not attached to provided element, unable to detach.');
    +  }
    +
    +  var key = goog.getUid(element);
    +  if (this.isInDocument()) {
    +    this.detachEvent_(/** @type {Object} */ (this.targets_.get(key)));
    +  }
    +
    +  this.targets_.remove(key);
    +};
    +
    +
    +/**
    + * Detaches an event listener to a target
    + * @param {Object} target The target to detach events from.
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.detachEvent_ = function(target) {
    +  this.getHandler().unlisten(
    +      target.element_, target.eventType_, this.onTargetClick_);
    +};
    +
    +
    +/**
    + * Sets whether the menu should toggle if it is already open.  For context
    + * menus this should be false, for toolbar menus it makes more sense to be true.
    + * @param {boolean} toggle The new toggle mode.
    + */
    +goog.ui.PopupMenu.prototype.setToggleMode = function(toggle) {
    +  this.toggleMode_ = toggle;
    +};
    +
    +
    +/**
    + * Gets whether the menu is in toggle mode
    + * @return {boolean} toggle.
    + */
    +goog.ui.PopupMenu.prototype.getToggleMode = function() {
    +  return this.toggleMode_;
    +};
    +
    +
    +/**
    + * Show the menu using given positioning object.
    + * @param {goog.positioning.AbstractPosition} position The positioning instance.
    + * @param {goog.positioning.Corner=} opt_menuCorner The corner of the menu to be
    + *     positioned.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @param {Element=} opt_anchor The element which acts as visual anchor for this
    + *     menu.
    + */
    +goog.ui.PopupMenu.prototype.showWithPosition = function(position,
    +    opt_menuCorner, opt_margin, opt_anchor) {
    +  var isVisible = this.isVisible();
    +  if (this.isOrWasRecentlyVisible() && this.toggleMode_) {
    +    this.hide();
    +    return;
    +  }
    +
    +  // Set current anchor before dispatching BEFORE_SHOW. This is typically useful
    +  // when we would need to make modifications based on the current anchor to the
    +  // menu just before displaying it.
    +  this.currentAnchor_ = opt_anchor || null;
    +
    +  // Notify event handlers that the menu is about to be shown.
    +  if (!this.dispatchEvent(goog.ui.Component.EventType.BEFORE_SHOW)) {
    +    return;
    +  }
    +
    +  var menuCorner = typeof opt_menuCorner != 'undefined' ?
    +                   opt_menuCorner :
    +                   goog.positioning.Corner.TOP_START;
    +
    +  // This is a little hacky so that we can position the menu with minimal
    +  // flicker.
    +
    +  if (!isVisible) {
    +    // On IE, setting visibility = 'hidden' on a visible menu
    +    // will cause a blur, forcing the menu to close immediately.
    +    this.getElement().style.visibility = 'hidden';
    +  }
    +
    +  goog.style.setElementShown(this.getElement(), true);
    +  position.reposition(this.getElement(), menuCorner, opt_margin);
    +
    +  if (!isVisible) {
    +    this.getElement().style.visibility = 'visible';
    +  }
    +
    +  this.setHighlightedIndex(-1);
    +
    +  // setVisible dispatches a goog.ui.Component.EventType.SHOW event, which may
    +  // be canceled to prevent the menu from being shown.
    +  this.setVisible(true);
    +};
    +
    +
    +/**
    + * Show the menu at a given attached target.
    + * @param {Object} target Popup target.
    + * @param {number} x The client-X associated with the show event.
    + * @param {number} y The client-Y associated with the show event.
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.showMenu = function(target, x, y) {
    +  var position = goog.isDef(target.targetCorner_) ?
    +      new goog.positioning.AnchoredViewportPosition(target.element_,
    +          target.targetCorner_, true) :
    +      new goog.positioning.ViewportClientPosition(x, y);
    +  if (position.setLastResortOverflow) {
    +    // This is a ViewportClientPosition, so we can set the overflow policy.
    +    // Allow the menu to slide from the corner rather than clipping if it is
    +    // completely impossible to fit it otherwise.
    +    position.setLastResortOverflow(goog.positioning.Overflow.ADJUST_X |
    +                                   goog.positioning.Overflow.ADJUST_Y);
    +  }
    +  this.showWithPosition(position, target.menuCorner_, target.margin_,
    +                        target.element_);
    +};
    +
    +
    +/**
    + * Shows the menu immediately at the given client coordinates.
    + * @param {number} x The client-X associated with the show event.
    + * @param {number} y The client-Y associated with the show event.
    + * @param {goog.positioning.Corner=} opt_menuCorner Corner of the menu that
    + *     should be anchored.
    + */
    +goog.ui.PopupMenu.prototype.showAt = function(x, y, opt_menuCorner) {
    +  this.showWithPosition(
    +      new goog.positioning.ViewportClientPosition(x, y), opt_menuCorner);
    +};
    +
    +
    +/**
    + * Shows the menu immediately attached to the given element
    + * @param {Element} element The element to show at.
    + * @param {goog.positioning.Corner} targetCorner The corner of the target to
    + *     anchor to.
    + * @param {goog.positioning.Corner=} opt_menuCorner Corner of the menu that
    + *     should be anchored.
    + */
    +goog.ui.PopupMenu.prototype.showAtElement = function(element, targetCorner,
    +    opt_menuCorner) {
    +  this.showWithPosition(
    +      new goog.positioning.MenuAnchoredPosition(element, targetCorner, true),
    +      opt_menuCorner, null, element);
    +};
    +
    +
    +/**
    + * Hides the menu.
    + */
    +goog.ui.PopupMenu.prototype.hide = function() {
    +  if (!this.isVisible()) {
    +    return;
    +  }
    +
    +  // setVisible dispatches a goog.ui.Component.EventType.HIDE event, which may
    +  // be canceled to prevent the menu from being hidden.
    +  this.setVisible(false);
    +  if (!this.isVisible()) {
    +    // HIDE event wasn't canceled; the menu is now hidden.
    +    this.lastHide_ = goog.now();
    +    this.currentAnchor_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Returns whether the menu is currently visible or was visible within about
    + * 150 ms ago.  This stops the menu toggling back on if the toggleMode == false.
    + * @return {boolean} Whether the popup is currently visible or was visible
    + *     within about 150 ms ago.
    + */
    +goog.ui.PopupMenu.prototype.isOrWasRecentlyVisible = function() {
    +  return this.isVisible() || this.wasRecentlyHidden();
    +};
    +
    +
    +/**
    + * Used to stop the menu toggling back on if the toggleMode == false.
    + * @return {boolean} Whether the menu was recently hidden.
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.wasRecentlyHidden = function() {
    +  return goog.now() - this.lastHide_ < goog.ui.PopupBase.DEBOUNCE_DELAY_MS;
    +};
    +
    +
    +/**
    + * Dismiss the popup menu when an action fires.
    + * @param {goog.events.Event=} opt_e The optional event.
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.onAction_ = function(opt_e) {
    +  this.hide();
    +};
    +
    +
    +/**
    + * Handles a browser event on one of the popup targets
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.PopupMenu.prototype.onTargetClick_ = function(e) {
    +  var keys = this.targets_.getKeys();
    +  for (var i = 0; i < keys.length; i++) {
    +    var target = /** @type {Object} */(this.targets_.get(keys[i]));
    +    if (target.element_ == e.currentTarget) {
    +      this.showMenu(target,
    +                    /** @type {number} */ (e.clientX),
    +                    /** @type {number} */ (e.clientY));
    +      e.preventDefault();
    +      e.stopPropagation();
    +      return;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles click events that propagate to the document.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @protected
    + */
    +goog.ui.PopupMenu.prototype.onDocClick = function(e) {
    +  if (this.isVisible() &&
    +      !this.containsElement(/** @type {Element} */ (e.target))) {
    +    this.hide();
    +  }
    +};
    +
    +
    +/**
    + * Handles the key event target loosing focus.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @protected
    + * @override
    + */
    +goog.ui.PopupMenu.prototype.handleBlur = function(e) {
    +  goog.ui.PopupMenu.superClass_.handleBlur.call(this, e);
    +  this.hide();
    +};
    +
    +
    +/** @override */
    +goog.ui.PopupMenu.prototype.disposeInternal = function() {
    +  // Always call the superclass' disposeInternal() first (Bug 715885).
    +  goog.ui.PopupMenu.superClass_.disposeInternal.call(this);
    +
    +  // Disposes of the attachment target map.
    +  if (this.targets_) {
    +    this.targets_.clear();
    +    delete this.targets_;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.html b/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.html
    new file mode 100644
    index 0000000000..28567275da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.PopupMenu
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PopupMenuTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="popup-anchor">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.js b/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.js
    new file mode 100644
    index 0000000000..dc2d224681
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/popupmenu_test.js
    @@ -0,0 +1,313 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PopupMenuTest');
    +goog.setTestOnly('goog.ui.PopupMenuTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.PopupMenu');
    +
    +var anchor;
    +
    +// Event handler
    +var handler;
    +var showPopup;
    +var beforeShowPopupCalled;
    +var popup;
    +
    +function setUp() {
    +  anchor = goog.dom.getElement('popup-anchor');
    +  handler = new goog.events.EventHandler();
    +  popup = new goog.ui.PopupMenu();
    +  popup.render();
    +}
    +
    +function tearDown() {
    +  handler.dispose();
    +  popup.dispose();
    +}
    +
    +
    +/**
    + * Asserts properties of {@code target} matches the expected value.
    + *
    + * @param {Object} target The target specifying how the popup menu should be
    + *     attached to an anchor.
    + * @param {Element} expectedElement The expected anchoring element.
    + * @param {goog.positioning.Corner} expectedTargetCorner The expected value of
    + *     the {@code target.targetCorner_} property.
    + * @param {goog.positioning.Corner} expectedMenuCorner The expected value of
    + *     the {@code target.menuCorner_} property.
    + * @param {goog.events.EventType} expectedEventType The expected value of the
    + *     {@code target.eventType_} property.
    + * @param {goog.math.Box} expectedMargin The expected value of the
    + *     {@code target.margin_} property.
    + */
    +function assertTarget(target, expectedElement, expectedTargetCorner,
    +    expectedMenuCorner, expectedEventType, expectedMargin) {
    +  var expectedTarget = {
    +    element_: expectedElement,
    +    targetCorner_: expectedTargetCorner,
    +    menuCorner_: expectedMenuCorner,
    +    eventType_: expectedEventType,
    +    margin_: expectedMargin
    +  };
    +
    +  assertObjectEquals('Target does not match.', expectedTarget, target);
    +}
    +
    +
    +/**
    + * Test menu receives BEFORE_SHOW event before it's displayed.
    + */
    +function testBeforeShowEvent() {
    +  var target = popup.createAttachTarget(anchor);
    +  popup.attach(anchor);
    +
    +  function beforeShowPopup(e) {
    +    // Ensure that the element is not yet visible.
    +    assertFalse('The element should not be shown when BEFORE_SHOW event is ' +
    +        'being handled',
    +        goog.style.isElementShown(popup.getElement()));
    +    // Verify that current anchor is set before dispatching BEFORE_SHOW.
    +    assertNotNullNorUndefined(popup.getAttachedElement());
    +    assertEquals('The attached anchor element is incorrect',
    +        target.element_, popup.getAttachedElement());
    +    beforeShowPopupCalled = true;
    +    return showPopup;
    +
    +  };
    +  function onShowPopup(e) {
    +    assertEquals('The attached anchor element is incorrect',
    +        target.element_, popup.getAttachedElement());
    +  };
    +
    +  handler.listen(popup,
    +                 goog.ui.Menu.EventType.BEFORE_SHOW,
    +                 beforeShowPopup);
    +  handler.listen(popup,
    +                 goog.ui.Menu.EventType.SHOW,
    +                 onShowPopup);
    +
    +  beforeShowPopupCalled = false;
    +  showPopup = false;
    +  popup.showMenu(target, 0, 0);
    +  assertTrue('BEFORE_SHOW event handler should be called on #showMenu',
    +      beforeShowPopupCalled);
    +  assertFalse('The element should not be shown when BEFORE_SHOW handler ' +
    +      'returned false',
    +      goog.style.isElementShown(popup.getElement()));
    +
    +  beforeShowPopupCalled = false;
    +  showPopup = true;
    +  popup.showMenu(target, 0, 0);
    +  assertTrue('The element should be shown when BEFORE_SHOW handler ' +
    +      'returned true',
    +      goog.style.isElementShown(popup.getElement()));
    +}
    +
    +
    +/**
    + * Test the behavior of {@link PopupMenu.isAttachTarget}.
    + */
    +function testIsAttachTarget() {
    +  // Before 'attach' is called.
    +  assertFalse('Menu should not be attached to the element',
    +      popup.isAttachTarget(anchor));
    +
    +  popup.attach(anchor);
    +  assertTrue('Menu should be attached to the anchor',
    +      popup.isAttachTarget(anchor));
    +
    +  popup.detach(anchor);
    +  assertFalse('Menu is expected to be detached from the element',
    +      popup.isAttachTarget(anchor));
    +}
    +
    +
    +/**
    + * Tests the behavior of {@link PopupMenu.createAttachTarget}.
    + */
    +function testCreateAttachTarget() {
    +  // Randomly picking parameters.
    +  var targetCorner = goog.positioning.Corner.TOP_END;
    +  var menuCorner = goog.positioning.Corner.BOTTOM_LEFT;
    +  var contextMenu = false;   // Show menu on mouse down event.
    +  var margin = new goog.math.Box(0, 10, 5, 25);
    +
    +  // Simply setting the required parameters.
    +  var target = popup.createAttachTarget(anchor);
    +  assertTrue(popup.isAttachTarget(anchor));
    +  assertTarget(target, anchor, undefined, undefined,
    +      goog.events.EventType.MOUSEDOWN, undefined);
    +
    +  // Creating another target with all the parameters.
    +  target = popup.createAttachTarget(anchor, targetCorner, menuCorner,
    +      contextMenu, margin);
    +  assertTrue(popup.isAttachTarget(anchor));
    +  assertTarget(target, anchor, targetCorner, menuCorner,
    +      goog.events.EventType.MOUSEDOWN, margin);
    +
    +  // Finally, switch up the 'contextMenu'
    +  target = popup.createAttachTarget(anchor, undefined, undefined,
    +      true /*opt_contextMenu*/, undefined);
    +  assertTarget(target, anchor, undefined, undefined,
    +      goog.events.EventType.CONTEXTMENU, undefined);
    +}
    +
    +
    +/**
    + * Tests the behavior of {@link PopupMenu.getAttachTarget}.
    + */
    +function testGetAttachTarget() {
    +  // Before the menu is attached to the anchor.
    +  var target = popup.getAttachTarget(anchor);
    +  assertTrue('Not expecting a target before the element is attach to the menu',
    +      target == null);
    +
    +  // Randomly picking parameters.
    +  var targetCorner = goog.positioning.Corner.TOP_END;
    +  var menuCorner = goog.positioning.Corner.BOTTOM_LEFT;
    +  var contextMenu = false;   // Show menu on mouse down event.
    +  var margin = new goog.math.Box(0, 10, 5, 25);
    +
    +  popup.attach(anchor, targetCorner, menuCorner, contextMenu, margin);
    +  target = popup.getAttachTarget(anchor);
    +  assertTrue('Failed to get target after attaching element to menu',
    +      target != null);
    +
    +  // Make sure we got the right target back.
    +  assertTarget(target, anchor, targetCorner, menuCorner,
    +      goog.events.EventType.MOUSEDOWN, margin);
    +}
    +
    +function testSmallViewportSliding() {
    +  popup.getElement().style.position = 'absolute';
    +  popup.getElement().style.outline = '1px solid blue';
    +  var item = new goog.ui.MenuItem('Test Item');
    +  popup.addChild(item, true);
    +  item.getElement().style.overflow = 'hidden';
    +
    +  var viewport = goog.style.getClientViewportElement();
    +  var viewportRect = goog.style.getVisibleRectForElement(viewport);
    +
    +  var middlePos = Math.floor((viewportRect.right - viewportRect.left) / 2);
    +  var leftwardPos = Math.floor((viewportRect.right - viewportRect.left) / 3);
    +  var rightwardPos =
    +      Math.floor((viewportRect.right - viewportRect.left) / 3 * 2);
    +
    +  // Can interpret these positions as widths relative to the viewport as well.
    +  var smallWidth = leftwardPos;
    +  var mediumWidth = middlePos;
    +  var largeWidth = rightwardPos;
    +
    +  // Test small menu first.  This should be small enough that it will display
    +  // its upper left corner where we tell it to in all three positions.
    +  popup.getElement().style.width = smallWidth + 'px';
    +
    +  var target = popup.createAttachTarget(anchor);
    +  popup.attach(anchor);
    +
    +  popup.showMenu(target, leftwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: small size, leftward pos',
    +      new goog.math.Coordinate(leftwardPos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, middlePos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: small size, middle pos',
    +      new goog.math.Coordinate(middlePos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, rightwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: small size, rightward pos',
    +      new goog.math.Coordinate(rightwardPos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  // Test medium menu next.  This should display with its upper left corner
    +  // at the target when leftward and middle, but on the right it should
    +  // position its upper right corner at the target instead.
    +  popup.getElement().style.width = mediumWidth + 'px';
    +
    +  popup.showMenu(target, leftwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: medium size, leftward pos',
    +      new goog.math.Coordinate(leftwardPos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, middlePos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: medium size, middle pos',
    +      new goog.math.Coordinate(middlePos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, rightwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: medium size, rightward pos',
    +      new goog.math.Coordinate(rightwardPos - mediumWidth, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  // Test large menu next.  This should display with its upper left corner at
    +  // the target when leftward, and its upper right corner at the target when
    +  // rightward, but right in the middle neither corner can be at the target and
    +  // keep the entire menu onscreen, so it should place its upper right corner
    +  // at the very right edge of the viewport.
    +  popup.getElement().style.width = largeWidth + 'px';
    +  popup.showMenu(target, leftwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: large size, leftward pos',
    +      new goog.math.Coordinate(leftwardPos, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, middlePos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: large size, middle pos',
    +      new goog.math.Coordinate(
    +          viewportRect.right - viewportRect.left - largeWidth, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  popup.showMenu(target, rightwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: large size, rightward pos',
    +      new goog.math.Coordinate(rightwardPos - largeWidth, 0),
    +      goog.style.getPosition(popup.getElement()));
    +
    +  // Make sure that the menu still displays correctly if we give the target
    +  // a target corner.  We can't set the overflow policy in that case, but it
    +  // should still display.
    +  popup.detach(anchor);
    +  anchor.style.position = 'absolute';
    +  anchor.style.left = '24px';
    +  anchor.style.top = '24px';
    +  var targetCorner = goog.positioning.Corner.TOP_END;
    +  target = popup.createAttachTarget(anchor, targetCorner);
    +  popup.attach(anchor, targetCorner);
    +  popup.getElement().style.width = smallWidth + 'px';
    +  popup.showMenu(target, leftwardPos, 0);
    +  assertObjectEquals(
    +      'Popup in wrong position: small size, leftward pos, with target corner',
    +      new goog.math.Coordinate(24, 24),
    +      goog.style.getPosition(popup.getElement()));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/progressbar.js b/mapbox-gl/closure-library/closure/goog/ui/progressbar.js
    new file mode 100644
    index 0000000000..f3a53b8a9e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/progressbar.js
    @@ -0,0 +1,407 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of a progress bar.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/progressbar.html
    + */
    +
    +
    +goog.provide('goog.ui.ProgressBar');
    +goog.provide('goog.ui.ProgressBar.Orientation');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.RangeModel');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This creates a progress bar object.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.ProgressBar = function(opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /** @type {?HTMLDivElement} */
    +  this.thumbElement_;
    +
    +  /**
    +   * The underlying data model for the progress bar.
    +   * @type {goog.ui.RangeModel}
    +   * @private
    +   */
    +  this.rangeModel_ = new goog.ui.RangeModel;
    +  goog.events.listen(this.rangeModel_, goog.ui.Component.EventType.CHANGE,
    +                     this.handleChange_, false, this);
    +};
    +goog.inherits(goog.ui.ProgressBar, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.ProgressBar);
    +
    +
    +/**
    + * Enum for representing the orientation of the progress bar.
    + *
    + * @enum {string}
    + */
    +goog.ui.ProgressBar.Orientation = {
    +  VERTICAL: 'vertical',
    +  HORIZONTAL: 'horizontal'
    +};
    +
    +
    +/**
    + * Map from progress bar orientation to CSS class names.
    + * @type {Object}
    + * @private
    + */
    +goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_ = {};
    +goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[
    +    goog.ui.ProgressBar.Orientation.VERTICAL] =
    +    goog.getCssName('progress-bar-vertical');
    +goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[
    +    goog.ui.ProgressBar.Orientation.HORIZONTAL] =
    +    goog.getCssName('progress-bar-horizontal');
    +
    +
    +/**
    + * Creates the DOM nodes needed for the progress bar
    + * @override
    + */
    +goog.ui.ProgressBar.prototype.createDom = function() {
    +  this.thumbElement_ = this.createThumb_();
    +  var cs = goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_];
    +  this.setElementInternal(
    +      this.getDomHelper().createDom('div', cs, this.thumbElement_));
    +  this.setValueState_();
    +  this.setMinimumState_();
    +  this.setMaximumState_();
    +};
    +
    +
    +/** @override */
    +goog.ui.ProgressBar.prototype.enterDocument = function() {
    +  goog.ui.ProgressBar.superClass_.enterDocument.call(this);
    +  this.attachEvents_();
    +  this.updateUi_();
    +
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The progress bar DOM element cannot be null.');
    +  // state live = polite will notify the user of updates,
    +  // but will not interrupt ongoing feedback
    +  goog.a11y.aria.setRole(element, 'progressbar');
    +  goog.a11y.aria.setState(element, 'live', 'polite');
    +};
    +
    +
    +/** @override */
    +goog.ui.ProgressBar.prototype.exitDocument = function() {
    +  goog.ui.ProgressBar.superClass_.exitDocument.call(this);
    +  this.detachEvents_();
    +};
    +
    +
    +/**
    + * This creates the thumb element.
    + * @private
    + * @return {HTMLDivElement} The created thumb element.
    + */
    +goog.ui.ProgressBar.prototype.createThumb_ = function() {
    +  return /** @type {!HTMLDivElement} */ (this.getDomHelper().createDom('div',
    +      goog.getCssName('progress-bar-thumb')));
    +};
    +
    +
    +/**
    + * Adds the initial event listeners to the element.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.attachEvents_ = function() {
    +  if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    +    goog.events.listen(this.getElement(), goog.events.EventType.RESIZE,
    +                       this.updateUi_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Removes the event listeners added by attachEvents_.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.detachEvents_ = function() {
    +  if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    +    goog.events.unlisten(this.getElement(), goog.events.EventType.RESIZE,
    +                         this.updateUi_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Decorates an existing HTML DIV element as a progress bar input. If the
    + * element contains a child with a class name of 'progress-bar-thumb' that will
    + * be used as the thumb.
    + * @param {Element} element  The HTML element to decorate.
    + * @override
    + */
    +goog.ui.ProgressBar.prototype.decorateInternal = function(element) {
    +  goog.ui.ProgressBar.superClass_.decorateInternal.call(this, element);
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.getElement()),
    +      goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_]);
    +
    +  // find thumb
    +  var thumb = goog.dom.getElementsByTagNameAndClass(
    +      null, goog.getCssName('progress-bar-thumb'), this.getElement())[0];
    +  if (!thumb) {
    +    thumb = this.createThumb_();
    +    this.getElement().appendChild(thumb);
    +  }
    +  this.thumbElement_ = thumb;
    +};
    +
    +
    +/**
    + * @return {number} The value.
    + */
    +goog.ui.ProgressBar.prototype.getValue = function() {
    +  return this.rangeModel_.getValue();
    +};
    +
    +
    +/**
    + * Sets the value
    + * @param {number} v The value.
    + */
    +goog.ui.ProgressBar.prototype.setValue = function(v) {
    +  this.rangeModel_.setValue(v);
    +  if (this.getElement()) {
    +    this.setValueState_();
    +  }
    +};
    +
    +
    +/**
    + * Sets the state for a11y of the current value.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.setValueState_ = function() {
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The progress bar DOM element cannot be null.');
    +  goog.a11y.aria.setState(element, 'valuenow', this.getValue());
    +};
    +
    +
    +/**
    + * @return {number} The minimum value.
    + */
    +goog.ui.ProgressBar.prototype.getMinimum = function() {
    +  return this.rangeModel_.getMinimum();
    +};
    +
    +
    +/**
    + * Sets the minimum number
    + * @param {number} v The minimum value.
    + */
    +goog.ui.ProgressBar.prototype.setMinimum = function(v) {
    +  this.rangeModel_.setMinimum(v);
    +  if (this.getElement()) {
    +    this.setMinimumState_();
    +  }
    +};
    +
    +
    +/**
    + * Sets the state for a11y of the minimum value.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.setMinimumState_ = function() {
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The progress bar DOM element cannot be null.');
    +  goog.a11y.aria.setState(element, 'valuemin', this.getMinimum());
    +};
    +
    +
    +/**
    + * @return {number} The maximum value.
    + */
    +goog.ui.ProgressBar.prototype.getMaximum = function() {
    +  return this.rangeModel_.getMaximum();
    +};
    +
    +
    +/**
    + * Sets the maximum number
    + * @param {number} v The maximum value.
    + */
    +goog.ui.ProgressBar.prototype.setMaximum = function(v) {
    +  this.rangeModel_.setMaximum(v);
    +  if (this.getElement()) {
    +    this.setMaximumState_();
    +  }
    +};
    +
    +
    +/**
    + * Sets the state for a11y of the maximum valiue.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.setMaximumState_ = function() {
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The progress bar DOM element cannot be null.');
    +  goog.a11y.aria.setState(element, 'valuemax', this.getMaximum());
    +};
    +
    +
    +/**
    + *
    + * @type {goog.ui.ProgressBar.Orientation}
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.orientation_ =
    +    goog.ui.ProgressBar.Orientation.HORIZONTAL;
    +
    +
    +/**
    + * Call back when the internal range model changes
    + * @param {goog.events.Event} e The event object.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.handleChange_ = function(e) {
    +  this.updateUi_();
    +  this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * This is called when we need to update the size of the thumb. This happens
    + * when first created as well as when the value and the orientation changes.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.updateUi_ = function() {
    +  if (this.thumbElement_) {
    +    var min = this.getMinimum();
    +    var max = this.getMaximum();
    +    var val = this.getValue();
    +    var ratio = (val - min) / (max - min);
    +    var size = Math.round(ratio * 100);
    +    if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) {
    +      // Note(arv): IE up to version 6 has some serious computation bugs when
    +      // using percentages or bottom. We therefore first set the height to
    +      // 100% and measure that and base the top and height on that size instead.
    +      if (goog.userAgent.IE && goog.userAgent.VERSION < 7) {
    +        this.thumbElement_.style.top = 0;
    +        this.thumbElement_.style.height = '100%';
    +        var h = this.thumbElement_.offsetHeight;
    +        var bottom = Math.round(ratio * h);
    +        this.thumbElement_.style.top = h - bottom + 'px';
    +        this.thumbElement_.style.height = bottom + 'px';
    +      } else {
    +        this.thumbElement_.style.top = (100 - size) + '%';
    +        this.thumbElement_.style.height = size + '%';
    +      }
    +    } else {
    +      this.thumbElement_.style.width = size + '%';
    +    }
    +  }
    +};
    +
    +
    +/**
    + * This is called when we need to setup the UI sizes and positions. This
    + * happens when we create the element and when we change the orientation.
    + * @private
    + */
    +goog.ui.ProgressBar.prototype.initializeUi_ = function() {
    +  var tStyle = this.thumbElement_.style;
    +  if (this.orientation_ == goog.ui.ProgressBar.Orientation.VERTICAL) {
    +    tStyle.left = 0;
    +    tStyle.width = '100%';
    +  } else {
    +    tStyle.top = tStyle.left = 0;
    +    tStyle.height = '100%';
    +  }
    +};
    +
    +
    +/**
    + * Changes the orientation
    + * @param {goog.ui.ProgressBar.Orientation} orient The orientation.
    + */
    +goog.ui.ProgressBar.prototype.setOrientation = function(orient) {
    +  if (this.orientation_ != orient) {
    +    var oldCss =
    +        goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[this.orientation_];
    +    var newCss = goog.ui.ProgressBar.ORIENTATION_TO_CSS_NAME_[orient];
    +    this.orientation_ = orient;
    +
    +    // Update the DOM
    +    var element = this.getElement();
    +    if (element) {
    +      goog.dom.classlist.swap(element, oldCss, newCss);
    +      this.initializeUi_();
    +      this.updateUi_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.ui.ProgressBar.Orientation} The orientation of the
    + *     progress bar.
    + */
    +goog.ui.ProgressBar.prototype.getOrientation = function() {
    +  return this.orientation_;
    +};
    +
    +
    +/** @override */
    +goog.ui.ProgressBar.prototype.disposeInternal = function() {
    +  this.detachEvents_();
    +  goog.ui.ProgressBar.superClass_.disposeInternal.call(this);
    +  this.thumbElement_ = null;
    +  this.rangeModel_.dispose();
    +};
    +
    +
    +/**
    + * @return {?number} The step value used to determine how to round the value.
    + */
    +goog.ui.ProgressBar.prototype.getStep = function() {
    +  return this.rangeModel_.getStep();
    +};
    +
    +
    +/**
    + * Sets the step value. The step value is used to determine how to round the
    + * value.
    + * @param {?number} step  The step size.
    + */
    +goog.ui.ProgressBar.prototype.setStep = function(step) {
    +  this.rangeModel_.setStep(step);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/prompt.js b/mapbox-gl/closure-library/closure/goog/ui/prompt.js
    new file mode 100644
    index 0000000000..752f49ca8d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/prompt.js
    @@ -0,0 +1,409 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview DHTML prompt to replace javascript's prompt().
    + *
    + * @see ../demos/prompt.html
    + */
    +
    +
    +goog.provide('goog.ui.Prompt');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Dialog');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Creates an object that represents a prompt (used in place of javascript's
    + * prompt). The html structure of the prompt is the same as the layout for
    + * dialog.js except for the addition of a text box which is placed inside the
    + * "Content area" and has the default class-name 'modal-dialog-userInput'
    + *
    + * @param {string} promptTitle The title of the prompt.
    + * @param {string|!goog.html.SafeHtml} promptHtml The HTML body of the prompt.
    + *     The variable is trusted and it should be already properly escaped.
    + * @param {Function} callback The function to call when the user selects Ok or
    + *     Cancel. The function should expect a single argument which represents
    + *     what the user entered into the prompt. If the user presses cancel, the
    + *     value of the argument will be null.
    + * @param {string=} opt_defaultValue Optional default value that should be in
    + *     the text box when the prompt appears.
    + * @param {string=} opt_class Optional prefix for the classes.
    + * @param {boolean=} opt_useIframeForIE For IE, workaround windowed controls
    + *     z-index issue by using a an iframe instead of a div for bg element.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link
    + *    goog.ui.Component} for semantics.
    + * @constructor
    + * @extends {goog.ui.Dialog}
    + */
    +goog.ui.Prompt = function(promptTitle, promptHtml, callback, opt_defaultValue,
    +    opt_class, opt_useIframeForIE, opt_domHelper) {
    +  goog.ui.Prompt.base(this, 'constructor',
    +      opt_class, opt_useIframeForIE, opt_domHelper);
    +
    +  /**
    +   * The id of the input element.
    +   * @type {string}
    +   * @private
    +   */
    +  this.inputElementId_ = this.makeId('ie');
    +
    +  this.setTitle(promptTitle);
    +
    +  var label = goog.html.SafeHtml.create('label', {'for': this.inputElementId_},
    +      promptHtml instanceof goog.html.SafeHtml ? promptHtml :
    +          goog.html.legacyconversions.safeHtmlFromString(promptHtml));
    +  var br = goog.html.SafeHtml.create('br');
    +  this.setSafeHtmlContent(goog.html.SafeHtml.concat(label, br, br));
    +
    +  this.callback_ = callback;
    +  this.defaultValue_ = goog.isDef(opt_defaultValue) ? opt_defaultValue : '';
    +
    +  /** @desc label for a dialog button. */
    +  var MSG_PROMPT_OK = goog.getMsg('OK');
    +  /** @desc label for a dialog button. */
    +  var MSG_PROMPT_CANCEL = goog.getMsg('Cancel');
    +  var buttonSet = new goog.ui.Dialog.ButtonSet(opt_domHelper);
    +  buttonSet.set(goog.ui.Dialog.DefaultButtonKeys.OK, MSG_PROMPT_OK, true);
    +  buttonSet.set(goog.ui.Dialog.DefaultButtonKeys.CANCEL,
    +      MSG_PROMPT_CANCEL, false, true);
    +  this.setButtonSet(buttonSet);
    +};
    +goog.inherits(goog.ui.Prompt, goog.ui.Dialog);
    +goog.tagUnsealableClass(goog.ui.Prompt);
    +
    +
    +/**
    + * Callback function which is invoked with the response to the prompt
    + * @type {Function}
    + * @private
    + */
    +goog.ui.Prompt.prototype.callback_ = goog.nullFunction;
    +
    +
    +/**
    + * Default value to display in prompt window
    + * @type {string}
    + * @private
    + */
    +goog.ui.Prompt.prototype.defaultValue_ = '';
    +
    +
    +/**
    + * Element in which user enters response (HTML <input> text box)
    + * @type {HTMLInputElement}
    + * @private
    + */
    +goog.ui.Prompt.prototype.userInputEl_ = null;
    +
    +
    +/**
    + * Tracks whether the prompt is in the process of closing to prevent multiple
    + * calls to the callback when the user presses enter.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Prompt.prototype.isClosing_ = false;
    +
    +
    +/**
    + * Number of rows in the user input element.
    + * The default is 1 which means use an <input> element.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Prompt.prototype.rows_ = 1;
    +
    +
    +/**
    + * Number of cols in the user input element.
    + * The default is 0 which means use browser default.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Prompt.prototype.cols_ = 0;
    +
    +
    +/**
    + * The input decorator function.
    + * @type {function(Element)?}
    + * @private
    + */
    +goog.ui.Prompt.prototype.inputDecoratorFn_ = null;
    +
    +
    +/**
    + * A validation function that takes a string and returns true if the string is
    + * accepted, false otherwise.
    + * @type {function(string):boolean}
    + * @private
    + */
    +goog.ui.Prompt.prototype.validationFn_ = goog.functions.TRUE;
    +
    +
    +/**
    + * Sets the validation function that takes a string and returns true if the
    + * string is accepted, false otherwise.
    + * @param {function(string): boolean} fn The validation function to use on user
    + *     input.
    + */
    +goog.ui.Prompt.prototype.setValidationFunction = function(fn) {
    +  this.validationFn_ = fn;
    +};
    +
    +
    +/** @override */
    +goog.ui.Prompt.prototype.enterDocument = function() {
    +  if (this.inputDecoratorFn_) {
    +    this.inputDecoratorFn_(this.userInputEl_);
    +  }
    +  goog.ui.Prompt.superClass_.enterDocument.call(this);
    +  this.getHandler().listen(this,
    +      goog.ui.Dialog.EventType.SELECT, this.onPromptExit_);
    +
    +  this.getHandler().listen(this.userInputEl_,
    +      [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE],
    +      this.handleInputChanged_);
    +};
    +
    +
    +/**
    + * @return {HTMLInputElement} The user input element. May be null if the Prompt
    + *     has not been rendered.
    + */
    +goog.ui.Prompt.prototype.getInputElement = function() {
    +  return this.userInputEl_;
    +};
    +
    +
    +/**
    + * Sets an input decorator function.  This function will be called in
    + * #enterDocument and will be passed the input element.  This is useful for
    + * attaching handlers to the input element for specific change events,
    + * for example.
    + * @param {function(Element)} inputDecoratorFn A function to call on the input
    + *     element on #enterDocument.
    + */
    +goog.ui.Prompt.prototype.setInputDecoratorFn = function(inputDecoratorFn) {
    +  this.inputDecoratorFn_ = inputDecoratorFn;
    +};
    +
    +
    +/**
    + * Set the number of rows in the user input element.
    + * A values of 1 means use an <input> element.  If the prompt is already
    + * rendered then you cannot change from <input> to <textarea> or vice versa.
    + * @param {number} rows Number of rows for user input element.
    + * @throws {goog.ui.Component.Error.ALREADY_RENDERED} If the component is
    + *    already rendered and an attempt to change between <input> and <textarea>
    + *    is made.
    + */
    +goog.ui.Prompt.prototype.setRows = function(rows) {
    +  if (this.isInDocument()) {
    +    if (this.userInputEl_.tagName.toLowerCase() == 'input') {
    +      if (rows > 1) {
    +        throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +      }
    +    } else {
    +      if (rows <= 1) {
    +        throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +      }
    +      this.userInputEl_.rows = rows;
    +    }
    +  }
    +  this.rows_ = rows;
    +};
    +
    +
    +/**
    + * @return {number} The number of rows in the user input element.
    + */
    +goog.ui.Prompt.prototype.getRows = function() {
    +  return this.rows_;
    +};
    +
    +
    +/**
    + * Set the number of cols in the user input element.
    + * @param {number} cols Number of cols for user input element.
    + */
    +goog.ui.Prompt.prototype.setCols = function(cols) {
    +  this.cols_ = cols;
    +  if (this.userInputEl_) {
    +    if (this.userInputEl_.tagName.toLowerCase() == 'input') {
    +      this.userInputEl_.size = cols;
    +    } else {
    +      this.userInputEl_.cols = cols;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The number of cols in the user input element.
    + */
    +goog.ui.Prompt.prototype.getCols = function() {
    +  return this.cols_;
    +};
    +
    +
    +/**
    + * Create the initial DOM representation for the prompt.
    + * @override
    + */
    +goog.ui.Prompt.prototype.createDom = function() {
    +  goog.ui.Prompt.superClass_.createDom.call(this);
    +
    +  var cls = this.getClass();
    +
    +  // add input box to the content
    +  var attrs = {
    +    'className': goog.getCssName(cls, 'userInput'),
    +    'value': this.defaultValue_};
    +  if (this.rows_ == 1) {
    +    // If rows == 1 then use an input element.
    +    this.userInputEl_ = /** @type {!HTMLInputElement} */
    +        (this.getDomHelper().createDom('input', attrs));
    +    this.userInputEl_.type = 'text';
    +    if (this.cols_) {
    +      this.userInputEl_.size = this.cols_;
    +    }
    +  } else {
    +    // If rows > 1 then use a textarea.
    +    this.userInputEl_ = /** @type {!HTMLInputElement} */
    +        (this.getDomHelper().createDom('textarea', attrs));
    +    this.userInputEl_.rows = this.rows_;
    +    if (this.cols_) {
    +      this.userInputEl_.cols = this.cols_;
    +    }
    +  }
    +
    +  this.userInputEl_.id = this.inputElementId_;
    +  var contentEl = this.getContentElement();
    +  contentEl.appendChild(this.getDomHelper().createDom(
    +      'div', {'style': 'overflow: auto'}, this.userInputEl_));
    +};
    +
    +
    +/**
    + * Handles input change events on the input field.  Disables the OK button if
    + * validation fails on the new input value.
    + * @private
    + */
    +goog.ui.Prompt.prototype.handleInputChanged_ = function() {
    +  this.updateOkButtonState_();
    +};
    +
    +
    +/**
    + * Set OK button enabled/disabled state based on input.
    + * @private
    + */
    +goog.ui.Prompt.prototype.updateOkButtonState_ = function() {
    +  var enableOkButton = this.validationFn_(this.userInputEl_.value);
    +  var buttonSet = this.getButtonSet();
    +  buttonSet.setButtonEnabled(goog.ui.Dialog.DefaultButtonKeys.OK,
    +      enableOkButton);
    +};
    +
    +
    +/**
    + * Causes the prompt to appear, centered on the screen, gives focus
    + * to the text box, and selects the text
    + * @param {boolean} visible Whether the dialog should be visible.
    + * @override
    + */
    +goog.ui.Prompt.prototype.setVisible = function(visible) {
    +  goog.ui.Prompt.base(this, 'setVisible', visible);
    +
    +  if (visible) {
    +    this.isClosing_ = false;
    +    this.userInputEl_.value = this.defaultValue_;
    +    this.focus();
    +    this.updateOkButtonState_();
    +  }
    +};
    +
    +
    +/**
    + * Overrides setFocus to put focus on the input element.
    + * @override
    + */
    +goog.ui.Prompt.prototype.focus = function() {
    +  goog.ui.Prompt.base(this, 'focus');
    +
    +  if (goog.userAgent.OPERA) {
    +    // select() doesn't focus <input> elements in Opera.
    +    this.userInputEl_.focus();
    +  }
    +  this.userInputEl_.select();
    +};
    +
    +
    +/**
    + * Sets the default value of the prompt when it is displayed.
    + * @param {string} defaultValue The default value to display.
    + */
    +goog.ui.Prompt.prototype.setDefaultValue = function(defaultValue) {
    +  this.defaultValue_ = defaultValue;
    +};
    +
    +
    +/**
    + * Handles the closing of the prompt, invoking the callback function that was
    + * registered to handle the value returned by the prompt.
    + * @param {goog.ui.Dialog.Event} e The dialog's selection event.
    + * @private
    + */
    +goog.ui.Prompt.prototype.onPromptExit_ = function(e) {
    +  /*
    +   * The timeouts below are required for one edge case. If after the dialog
    +   * hides, suppose validation of the input fails which displays an alert. If
    +   * the user pressed the Enter key to dismiss the alert that was displayed it
    +   * can trigger the event handler a second time. This timeout ensures that the
    +   * alert is displayed only after the prompt is able to clean itself up.
    +   */
    +  if (!this.isClosing_) {
    +    this.isClosing_ = true;
    +    if (e.key == 'ok') {
    +      goog.Timer.callOnce(
    +          goog.bind(this.callback_, this, this.userInputEl_.value), 1);
    +    } else {
    +      goog.Timer.callOnce(goog.bind(this.callback_, this, null), 1);
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Prompt.prototype.disposeInternal = function() {
    +  goog.dom.removeNode(this.userInputEl_);
    +
    +  goog.events.unlisten(this, goog.ui.Dialog.EventType.SELECT,
    +      this.onPromptExit_, true, this);
    +
    +  goog.ui.Prompt.superClass_.disposeInternal.call(this);
    +
    +  this.userInputEl_ = null;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/prompt_test.html b/mapbox-gl/closure-library/closure/goog/ui/prompt_test.html
    new file mode 100644
    index 0000000000..079b198bd7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/prompt_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Prompt
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.PromptTest');
    +  </script>
    +  <link rel="stylesheet" type="text/css" href="../css/common.css" />
    +  <link rel="stylesheet" type="text/css" href="../css/dialog.css" />
    + </head>
    + <body>
    +  <input type="button" value="Prompt" onclick="newPrompt();" />
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/prompt_test.js b/mapbox-gl/closure-library/closure/goog/ui/prompt_test.js
    new file mode 100644
    index 0000000000..df289a54a5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/prompt_test.js
    @@ -0,0 +1,139 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.PromptTest');
    +goog.setTestOnly('goog.ui.PromptTest');
    +
    +goog.require('goog.dom.selection');
    +goog.require('goog.events.InputHandler');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.BidiInput');
    +goog.require('goog.ui.Dialog');
    +goog.require('goog.ui.Prompt');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +var prompt;
    +
    +function setUp() {
    +  document.body.focus();
    +}
    +
    +function tearDown() {
    +  goog.dispose(prompt);
    +}
    +
    +function testFocusOnInputElement() {
    +  // FF does not perform focus if the window is not active in the first place.
    +  if (goog.userAgent.GECKO && document.hasFocus && !document.hasFocus()) {
    +    return;
    +  }
    +
    +  var promptResult = undefined;
    +  prompt = new goog.ui.Prompt('title', 'Prompt:', function(result) {
    +    promptResult = result;
    +  }, 'defaultValue');
    +  prompt.setVisible(true);
    +
    +  if (goog.userAgent.product.CHROME) {
    +    // For some reason, this test fails non-deterministically on Chrome,
    +    // but only on the test farm.
    +    return;
    +  }
    +  assertEquals('defaultValue',
    +      goog.dom.selection.getText(prompt.userInputEl_));
    +}
    +
    +
    +function testValidationFunction() {
    +  var promptResult = undefined;
    +  prompt = new goog.ui.Prompt('title', 'Prompt:', function(result) {
    +    promptResult = result;
    +  }, '');
    +  prompt.setValidationFunction(goog.functions.not(goog.string.isEmptyOrWhitespace));
    +  prompt.setVisible(true);
    +
    +  var buttonSet = prompt.getButtonSet();
    +  var okButton = buttonSet.getButton(goog.ui.Dialog.DefaultButtonKeys.OK);
    +  assertTrue(okButton.disabled);
    +
    +  prompt.userInputEl_.value = '';
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.SPACE);
    +  assertTrue(okButton.disabled);
    +  prompt.userInputEl_.value = 'foo';
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.X);
    +  assertFalse(okButton.disabled);
    +}
    +
    +function testBidiInput() {
    +  var shalomInHebrew = '\u05e9\u05dc\u05d5\u05dd';
    +  var promptResult = undefined;
    +  prompt = new goog.ui.Prompt('title', 'Prompt:', goog.functions.NULL, '');
    +  var bidiInput = new goog.ui.BidiInput();
    +  prompt.setInputDecoratorFn(goog.bind(bidiInput.decorate, bidiInput));
    +  prompt.setVisible(true);
    +
    +  prompt.userInputEl_.value = shalomInHebrew;
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.SPACE);
    +  goog.testing.events.fireBrowserEvent(
    +      {'target' : prompt.userInputEl_, 'type' : 'input'});
    +  bidiInput.inputHandler_.dispatchEvent(
    +      goog.events.InputHandler.EventType.INPUT);
    +  assertEquals('rtl', prompt.userInputEl_.dir);
    +
    +  prompt.userInputEl_.value = 'shalomInEnglish';
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.SPACE);
    +  goog.testing.events.fireBrowserEvent(
    +      {'target' : prompt.userInputEl_, 'type' : 'input'});
    +  bidiInput.inputHandler_.dispatchEvent(
    +      goog.events.InputHandler.EventType.INPUT);
    +  assertEquals('ltr', prompt.userInputEl_.dir);
    +  goog.dispose(bidiInput);
    +}
    +
    +function testBidiInput_off() {
    +  var shalomInHebrew = '\u05e9\u05dc\u05d5\u05dd';
    +  var promptResult = undefined;
    +  prompt = new goog.ui.Prompt('title', 'Prompt:', goog.functions.NULL, '');
    +  prompt.setVisible(true);
    +
    +  prompt.userInputEl_.value = shalomInHebrew;
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.SPACE);
    +  goog.testing.events.fireBrowserEvent(
    +      {'target' : prompt.userInputEl_, 'type' : 'input'});
    +  assertEquals('', prompt.userInputEl_.dir);
    +
    +  prompt.userInputEl_.value = 'shalomInEnglish';
    +  goog.testing.events.fireKeySequence(prompt.userInputEl_,
    +      goog.events.KeyCodes.SPACE);
    +  assertEquals('', prompt.userInputEl_.dir);
    +}
    +
    +// An interactive test so we can manually see what it looks like.
    +function newPrompt() {
    +  prompt = new goog.ui.Prompt('title', 'Prompt:', function(result) {
    +    alert('Result: ' + result);
    +    goog.dispose(prompt);
    +  }, 'defaultValue');
    +  prompt.setVisible(true);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/rangemodel.js b/mapbox-gl/closure-library/closure/goog/ui/rangemodel.js
    new file mode 100644
    index 0000000000..0a8afac6d8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/rangemodel.js
    @@ -0,0 +1,303 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of a range model. This is an implementation of
    + * the BoundedRangeModel as described by Java at
    + * http://java.sun.com/javase/6/docs/api/javax/swing/BoundedRangeModel.html.
    + *
    + * One good way to understand the range model is to think of a scroll bar for
    + * a scrollable element. In that case minimum is 0, maximum is scrollHeight,
    + * value is scrollTop and extent is clientHeight.
    + *
    + * Based on http://webfx.eae.net/dhtml/slider/js/range.js
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.ui.RangeModel');
    +
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Creates a range model
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.ui.RangeModel = function() {
    +  goog.events.EventTarget.call(this);
    +};
    +goog.inherits(goog.ui.RangeModel, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.RangeModel);
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.value_ = 0;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.minimum_ = 0;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.maximum_ = 100;
    +
    +
    +/**
    + * @type {number}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.extent_ = 0;
    +
    +
    +/**
    + * @type {?number}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.step_ = 1;
    +
    +
    +/**
    + * This is true if something is changed as a side effect. This happens when for
    + * example we set the maximum below the current value.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.isChanging_ = false;
    +
    +
    +/**
    + * If set to true, we do not fire any change events.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.RangeModel.prototype.mute_ = false;
    +
    +
    +/**
    + * Sets the model to mute / unmute.
    + * @param {boolean} muteValue Whether or not to mute the range, i.e.,
    + *     suppress any CHANGE events.
    + */
    +goog.ui.RangeModel.prototype.setMute = function(muteValue) {
    +  this.mute_ = muteValue;
    +};
    +
    +
    +/**
    + * Sets the value.
    + * @param {number} value The new value.
    + */
    +goog.ui.RangeModel.prototype.setValue = function(value) {
    +  value = this.roundToStepWithMin(value);
    +  if (this.value_ != value) {
    +    if (value + this.extent_ > this.maximum_) {
    +      this.value_ = this.maximum_ - this.extent_;
    +    } else if (value < this.minimum_) {
    +      this.value_ = this.minimum_;
    +    } else {
    +      this.value_ = value;
    +    }
    +    if (!this.isChanging_ && !this.mute_) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} the current value.
    + */
    +goog.ui.RangeModel.prototype.getValue = function() {
    +  return this.roundToStepWithMin(this.value_);
    +};
    +
    +
    +/**
    + * Sets the extent. The extent is the 'size' of the value.
    + * @param {number} extent The new extent.
    + */
    +goog.ui.RangeModel.prototype.setExtent = function(extent) {
    +  extent = this.roundToStepWithMin(extent);
    +  if (this.extent_ != extent) {
    +    if (extent < 0) {
    +      this.extent_ = 0;
    +    } else if (this.value_ + extent > this.maximum_) {
    +      this.extent_ = this.maximum_ - this.value_;
    +    } else {
    +      this.extent_ = extent;
    +    }
    +    if (!this.isChanging_ && !this.mute_) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The extent for the range model.
    + */
    +goog.ui.RangeModel.prototype.getExtent = function() {
    +  return this.roundToStep(this.extent_);
    +};
    +
    +
    +/**
    + * Sets the minimum
    + * @param {number} minimum The new minimum.
    + */
    +goog.ui.RangeModel.prototype.setMinimum = function(minimum) {
    +  // Don't round minimum because it is the base
    +  if (this.minimum_ != minimum) {
    +    var oldIsChanging = this.isChanging_;
    +    this.isChanging_ = true;
    +
    +    this.minimum_ = minimum;
    +
    +    if (minimum + this.extent_ > this.maximum_) {
    +      this.extent_ = this.maximum_ - this.minimum_;
    +    }
    +    if (minimum > this.value_) {
    +      this.setValue(minimum);
    +    }
    +    if (minimum > this.maximum_) {
    +      this.extent_ = 0;
    +      this.setMaximum(minimum);
    +      this.setValue(minimum);
    +    }
    +
    +
    +    this.isChanging_ = oldIsChanging;
    +    if (!this.isChanging_ && !this.mute_) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The minimum value for the range model.
    + */
    +goog.ui.RangeModel.prototype.getMinimum = function() {
    +  return this.roundToStepWithMin(this.minimum_);
    +};
    +
    +
    +/**
    + * Sets the maximum
    + * @param {number} maximum The new maximum.
    + */
    +goog.ui.RangeModel.prototype.setMaximum = function(maximum) {
    +  maximum = this.roundToStepWithMin(maximum);
    +  if (this.maximum_ != maximum) {
    +    var oldIsChanging = this.isChanging_;
    +    this.isChanging_ = true;
    +
    +    this.maximum_ = maximum;
    +
    +    if (maximum < this.value_ + this.extent_) {
    +      this.setValue(maximum - this.extent_);
    +    }
    +    if (maximum < this.minimum_) {
    +      this.extent_ = 0;
    +      this.setMinimum(maximum);
    +      this.setValue(this.maximum_);
    +    }
    +    if (maximum < this.minimum_ + this.extent_) {
    +      this.extent_ = this.maximum_ - this.minimum_;
    +    }
    +
    +    this.isChanging_ = oldIsChanging;
    +    if (!this.isChanging_ && !this.mute_) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The maximimum value for the range model.
    + */
    +goog.ui.RangeModel.prototype.getMaximum = function() {
    +  return this.roundToStepWithMin(this.maximum_);
    +};
    +
    +
    +/**
    + * Returns the step value. The step value is used to determine how to round the
    + * value.
    + * @return {?number} The maximimum value for the range model.
    + */
    +goog.ui.RangeModel.prototype.getStep = function() {
    +  return this.step_;
    +};
    +
    +
    +/**
    + * Sets the step. The step value is used to determine how to round the value.
    + * @param {?number} step  The step size.
    + */
    +goog.ui.RangeModel.prototype.setStep = function(step) {
    +  if (this.step_ != step) {
    +    this.step_ = step;
    +
    +    // adjust value, extent and maximum
    +    var oldIsChanging = this.isChanging_;
    +    this.isChanging_ = true;
    +
    +    this.setMaximum(this.getMaximum());
    +    this.setExtent(this.getExtent());
    +    this.setValue(this.getValue());
    +
    +    this.isChanging_ = oldIsChanging;
    +    if (!this.isChanging_ && !this.mute_) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Rounds to the closest step using the minimum value as the base.
    + * @param {number} value  The number to round.
    + * @return {number} The number rounded to the closest step.
    + */
    +goog.ui.RangeModel.prototype.roundToStepWithMin = function(value) {
    +  if (this.step_ == null) return value;
    +  return this.minimum_ +
    +      Math.round((value - this.minimum_) / this.step_) * this.step_;
    +};
    +
    +
    +/**
    + * Rounds to the closest step.
    + * @param {number} value  The number to round.
    + * @return {number} The number rounded to the closest step.
    + */
    +goog.ui.RangeModel.prototype.roundToStep = function(value) {
    +  if (this.step_ == null) return value;
    +  return Math.round(value / this.step_) * this.step_;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.html b/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.html
    new file mode 100644
    index 0000000000..1dcfa4e37c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.RangeModel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.RangeModelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.js b/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.js
    new file mode 100644
    index 0000000000..e4901df726
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/rangemodel_test.js
    @@ -0,0 +1,266 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.RangeModelTest');
    +goog.setTestOnly('goog.ui.RangeModelTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.RangeModel');
    +
    +function reset(rm, step) {
    +  rm.setStep(step || 1);
    +  rm.setExtent(0);
    +  rm.setMinimum(0);
    +  rm.setMaximum(100);
    +  rm.setValue(0);
    +}
    +
    +function getDescriptiveString(rm) {
    +  return rm.getMinimum() + ' < ' + rm.getValue() + '[' + rm.getExtent() +
    +      '] < ' + rm.getMaximum();
    +}
    +
    +function testValue() {
    +  var rm = new goog.ui.RangeModel;
    +
    +  assertEquals(0, rm.getValue());
    +  rm.setValue(50);
    +  assertEquals(50, rm.getValue());
    +
    +  // setting smaller than min should keep min
    +  rm.setValue(-1);
    +  assertEquals(0, rm.getValue());
    +
    +  // setting larger than max should keep max - extent
    +  rm.setValue(101);
    +  assertEquals(100, rm.getValue());
    +  rm.setValue(0);
    +  rm.setExtent(10);
    +  rm.setValue(100);
    +  assertEquals(90, rm.getValue());
    +
    +  ////////////////
    +  // Change step
    +  reset(rm, 3);
    +
    +  assertEquals(0, rm.getValue());
    +  rm.setValue(50);
    +  assertEquals(51, rm.getValue());
    +
    +  // setting smaller than min should keep min
    +  rm.setValue(-1);
    +  assertEquals(0, rm.getValue());
    +
    +  // setting larger than max should keep max - extent
    +  rm.setValue(101);
    +  assertEquals(99, rm.getValue());
    +  rm.setValue(0);
    +  rm.setExtent(10);
    +  rm.setValue(100);
    +  assertEquals(90, rm.getValue());
    +
    +}
    +
    +function testMinium() {
    +  var rm = new goog.ui.RangeModel;
    +
    +  rm.setValue(50);
    +  rm.setMinimum(10);
    +  assertEquals(10, rm.getMinimum());
    +
    +  reset(rm);
    +
    +  // setting larger than value should change value
    +  rm.setMinimum(10);
    +  assertEquals(10, rm.getMinimum());
    +  assertEquals(10, rm.getValue());
    +
    +  // setting larger than max should set max = min
    +  rm.setMinimum(200);
    +  assertEquals(200, rm.getMinimum());
    +  assertEquals(200, rm.getValue());
    +  assertEquals(200, rm.getMaximum());
    +  assertEquals(0, rm.getExtent());
    +
    +  reset(rm);
    +
    +  // should change extent
    +  rm.setExtent(10);
    +  rm.setMinimum(95);
    +  assertEquals(95, rm.getMinimum());
    +  assertEquals(95, rm.getValue());
    +  assertEquals(100, rm.getMaximum());
    +  assertEquals(5, rm.getExtent());
    +
    +  ////////////////
    +  // Change step
    +  reset(rm, 3);
    +
    +  rm.setValue(50);
    +  rm.setMinimum(10);
    +  assertEquals(10, rm.getMinimum());
    +
    +  reset(rm, 3);
    +
    +  // setting larger than value should change value
    +  rm.setMinimum(10);
    +  assertEquals(10, rm.getMinimum());
    +  assertEquals(10, rm.getValue());
    +
    +  // setting larger than max should set max = min
    +  rm.setMinimum(200);
    +  assertEquals(200, rm.getMinimum());
    +  assertEquals(200, rm.getValue());
    +  assertEquals(200, rm.getMaximum());
    +  assertEquals(0, rm.getExtent());
    +
    +  reset(rm, 3);
    +
    +  // should change extent
    +  rm.setExtent(10); // 0 < 0[9] < 99
    +  rm.setMinimum(95); // 95 < 95[3] < 98
    +  assertEquals(95, rm.getMinimum());
    +  assertEquals(95, rm.getValue());
    +  assertEquals(98, rm.getMaximum());
    +  assertEquals(3, rm.getExtent());
    +}
    +
    +function testMaximum() {
    +  var rm = new goog.ui.RangeModel;
    +
    +  rm.setMaximum(50);
    +  assertEquals(50, rm.getMaximum());
    +
    +  reset(rm);
    +
    +  // setting to smaller than minimum should change minimum, value and extent
    +  rm.setValue(5);
    +  rm.setExtent(10);
    +  rm.setMinimum(50);
    +  rm.setMaximum(40);
    +  assertEquals(40, rm.getMaximum());
    +  assertEquals(0, rm.getExtent());
    +  assertEquals(40, rm.getValue());
    +  assertEquals(40, rm.getMinimum());
    +
    +  reset(rm);
    +
    +  // setting smaller than value should change value to max - extent
    +  rm.setExtent(10);
    +  rm.setValue(50);
    +  rm.setMaximum(40);
    +  assertEquals(40, rm.getMaximum());
    +  assertEquals(30, rm.getValue());
    +
    +  reset(rm);
    +
    +  // should change value, and keep extent constant,
    +  // unless extent is > max - min.
    +  rm.setExtent(10);
    +  rm.setValue(90);
    +  rm.setMaximum(95);
    +  assertEquals(95, rm.getMaximum());
    +  assertEquals(10, rm.getExtent());
    +  assertEquals(85, rm.getValue());
    +
    +  ////////////////
    +  // Change step
    +  reset(rm, 3);
    +
    +  rm.setMaximum(50); // 0 < 0[0] < 51
    +  assertEquals(51, rm.getMaximum());
    +
    +  reset(rm, 3);
    +
    +  // setting to smaller than minimum should change minimum, value and extent
    +  rm.setValue(5); // 0 < 6[0] < 99
    +  rm.setExtent(10); // 0 < 6[9] < 99
    +  rm.setMinimum(50); // 50 < 50[9] < 98
    +  rm.setMaximum(40); // 41 < 41[0] < 41
    +  assertEquals(41, rm.getMaximum());
    +  assertEquals(0, rm.getExtent());
    +  assertEquals(41, rm.getValue());
    +  assertEquals(41, rm.getMinimum());
    +
    +  reset(rm, 3);
    +
    +  // setting smaller than value should change value to max - extent
    +  rm.setExtent(10); // 0 < 0[9] < 99
    +  rm.setValue(50); // 0 < 51[9] < 99
    +  rm.setMaximum(40); // 0 < 30[9] < 39
    +  assertEquals(39, rm.getMaximum());
    +  assertEquals(30, rm.getValue());
    +
    +  reset(rm, 3);
    +
    +  // should change value, and keep extent constant,
    +  // unless extent is > max - min.
    +  rm.setExtent(10); // 0 < 0[9] < 99
    +  rm.setValue(90); // 0 < 90[9] < 99
    +  rm.setMaximum(95); // 0 < 90[6] < 96
    +  assertEquals(96, rm.getMaximum());
    +  assertEquals(87, rm.getValue());
    +  assertEquals(9, rm.getExtent());
    +}
    +
    +function testExtent() {
    +  var rm = new goog.ui.RangeModel;
    +
    +  rm.setExtent(10);
    +  assertEquals(10, rm.getExtent());
    +
    +  rm.setExtent(-10);
    +  assertEquals(0, rm.getExtent());
    +
    +  rm.setValue(50);
    +  rm.setExtent(100);
    +  assertEquals(50, rm.getExtent());
    +  assertEquals(50, rm.getValue());
    +
    +  ////////////////
    +  // Change step
    +  reset(rm, 3);
    +
    +  rm.setExtent(10); // 0 < 0[9] < 99
    +  assertEquals(9, rm.getExtent());
    +
    +  rm.setExtent(-10);
    +  assertEquals(0, rm.getExtent());
    +
    +  rm.setValue(50);  // 0 < 51[9] < 99
    +  rm.setExtent(100); // 0 < 51[48] < 99
    +  assertEquals(48, rm.getExtent());
    +  assertEquals(51, rm.getValue());
    +}
    +
    +function testRoundToStep() {
    +  var rm = new goog.ui.RangeModel;
    +  rm.setStep(0.5);
    +
    +  assertEquals(1, rm.roundToStep(1));
    +  assertEquals(0.5, rm.roundToStep(0.5));
    +  assertEquals(1, rm.roundToStep(0.75));
    +  assertEquals(0.5, rm.roundToStep(0.74));
    +}
    +
    +function testRoundToStepWithMin() {
    +  var rm = new goog.ui.RangeModel;
    +  rm.setStep(0.5);
    +  rm.setMinimum(0.25);
    +
    +  assertEquals(1.25, rm.roundToStepWithMin(1));
    +  assertEquals(0.75, rm.roundToStepWithMin(0.5));
    +  assertEquals(0.75, rm.roundToStepWithMin(0.75));
    +  assertEquals(0.75, rm.roundToStepWithMin(0.74));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/ratings.js b/mapbox-gl/closure-library/closure/goog/ui/ratings.js
    new file mode 100644
    index 0000000000..94591faaf4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/ratings.js
    @@ -0,0 +1,508 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A base ratings widget that allows the user to select a rating,
    + * like "star video" in Google Video. This fires a "change" event when the user
    + * selects a rating.
    + *
    + * Keyboard:
    + * ESC = Clear (if supported)
    + * Home = 1 star
    + * End = Full rating
    + * Left arrow = Decrease rating
    + * Right arrow = Increase rating
    + * 0 = Clear (if supported)
    + * 1 - 9 = nth star
    + *
    + * @see ../demos/ratings.html
    + */
    +
    +goog.provide('goog.ui.Ratings');
    +goog.provide('goog.ui.Ratings.EventType');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * A UI Control used for rating things, i.e. videos on Google Video.
    + * @param {Array<string>=} opt_ratings Ratings. Default: [1,2,3,4,5].
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.Ratings = function(opt_ratings, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Ordered ratings that can be picked, Default: [1,2,3,4,5]
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.ratings_ = opt_ratings || ['1', '2', '3', '4', '5'];
    +
    +  /**
    +   * Array containing references to the star elements
    +   * @type {Array<Element>}
    +   * @private
    +   */
    +  this.stars_ = [];
    +
    +
    +  // Awkward name because the obvious name is taken by subclasses already.
    +  /**
    +   * Whether the control is enabled.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isEnabled_ = true;
    +
    +
    +  /**
    +   * The last index to be highlighted
    +   * @type {number}
    +   * @private
    +   */
    +  this.highlightedIndex_ = -1;
    +
    +
    +  /**
    +   * The currently selected index
    +   * @type {number}
    +   * @private
    +   */
    +  this.selectedIndex_ = -1;
    +
    +
    +  /**
    +   * An attached form field to set the value to
    +   * @type {HTMLInputElement|HTMLSelectElement|null}
    +   * @private
    +   */
    +  this.attachedFormField_ = null;
    +};
    +goog.inherits(goog.ui.Ratings, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.Ratings);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.Ratings.CSS_CLASS = goog.getCssName('goog-ratings');
    +
    +
    +/**
    + * Enums for Ratings event type.
    + * @enum {string}
    + */
    +goog.ui.Ratings.EventType = {
    +  CHANGE: 'change',
    +  HIGHLIGHT_CHANGE: 'highlightchange',
    +  HIGHLIGHT: 'highlight',
    +  UNHIGHLIGHT: 'unhighlight'
    +};
    +
    +
    +/**
    + * Decorate a HTML structure already in the document.  Expects the structure:
    + * <pre>
    + * - div
    + *   - select
    + *       - option 1 #text = 1 star
    + *       - option 2 #text = 2 stars
    + *       - option 3 #text = 3 stars
    + *       - option N (where N is max number of ratings)
    + * </pre>
    + *
    + * The div can contain other elements for graceful degredation, but they will be
    + * hidden when the decoration occurs.
    + *
    + * @param {Element} el Div element to decorate.
    + * @override
    + */
    +goog.ui.Ratings.prototype.decorateInternal = function(el) {
    +  var select = el.getElementsByTagName('select')[0];
    +  if (!select) {
    +    throw Error('Can not decorate ' + el + ', with Ratings. Must ' +
    +                'contain select box');
    +  }
    +  this.ratings_.length = 0;
    +  for (var i = 0, n = select.options.length; i < n; i++) {
    +    var option = select.options[i];
    +    this.ratings_.push(option.text);
    +  }
    +  this.setSelectedIndex(select.selectedIndex);
    +  select.style.display = 'none';
    +  this.attachedFormField_ = select;
    +  this.createDom();
    +  el.insertBefore(this.getElement(), select);
    +};
    +
    +
    +/**
    + * Render the rating widget inside the provided element. This will override the
    + * current content of the element.
    + * @override
    + */
    +goog.ui.Ratings.prototype.enterDocument = function() {
    +  var el = this.getElement();
    +  goog.asserts.assert(el, 'The DOM element for ratings cannot be null.');
    +  el.tabIndex = 0;
    +  goog.dom.classlist.add(el, this.getCssClass());
    +  goog.a11y.aria.setRole(el, goog.a11y.aria.Role.SLIDER);
    +  goog.a11y.aria.setState(el, goog.a11y.aria.State.VALUEMIN, 0);
    +  var max = this.ratings_.length - 1;
    +  goog.a11y.aria.setState(el, goog.a11y.aria.State.VALUEMAX, max);
    +  var handler = this.getHandler();
    +  handler.listen(el, 'keydown', this.onKeyDown_);
    +
    +  // Create the elements for the stars
    +  for (var i = 0; i < this.ratings_.length; i++) {
    +    var star = this.getDomHelper().createDom('span', {
    +      'title': this.ratings_[i],
    +      'class': this.getClassName_(i, false),
    +      'index': i});
    +    this.stars_.push(star);
    +    el.appendChild(star);
    +  }
    +
    +  handler.listen(el, goog.events.EventType.CLICK, this.onClick_);
    +  handler.listen(el, goog.events.EventType.MOUSEOUT, this.onMouseOut_);
    +  handler.listen(el, goog.events.EventType.MOUSEOVER, this.onMouseOver_);
    +
    +  this.highlightIndex_(this.selectedIndex_);
    +};
    +
    +
    +/**
    + * Should be called when the widget is removed from the document but may be
    + * reused.  This removes all the listeners the widget has attached and destroys
    + * the DOM nodes it uses.
    + * @override
    + */
    +goog.ui.Ratings.prototype.exitDocument = function() {
    +  goog.ui.Ratings.superClass_.exitDocument.call(this);
    +  for (var i = 0; i < this.stars_.length; i++) {
    +    this.getDomHelper().removeNode(this.stars_[i]);
    +  }
    +  this.stars_.length = 0;
    +};
    +
    +
    +/** @override */
    +goog.ui.Ratings.prototype.disposeInternal = function() {
    +  goog.ui.Ratings.superClass_.disposeInternal.call(this);
    +  this.ratings_.length = 0;
    +};
    +
    +
    +/**
    + * Returns the base CSS class used by subcomponents of this component.
    + * @return {string} Component-specific CSS class.
    + */
    +goog.ui.Ratings.prototype.getCssClass = function() {
    +  return goog.ui.Ratings.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Sets the selected index. If the provided index is greater than the number of
    + * ratings then the max is set.  0 is the first item, -1 is no selection.
    + * @param {number} index The index of the rating to select.
    + */
    +goog.ui.Ratings.prototype.setSelectedIndex = function(index) {
    +  index = Math.max(-1, Math.min(index, this.ratings_.length - 1));
    +  if (index != this.selectedIndex_) {
    +    this.selectedIndex_ = index;
    +    this.highlightIndex_(this.selectedIndex_);
    +    if (this.attachedFormField_) {
    +      if (this.attachedFormField_.tagName == 'SELECT') {
    +        this.attachedFormField_.selectedIndex = index;
    +      } else {
    +        this.attachedFormField_.value =
    +            /** @type {string} */ (this.getValue());
    +      }
    +      var ratingsElement = this.getElement();
    +      goog.asserts.assert(ratingsElement,
    +          'The DOM ratings element cannot be null.');
    +      goog.a11y.aria.setState(ratingsElement,
    +          goog.a11y.aria.State.VALUENOW,
    +          this.ratings_[index]);
    +    }
    +    this.dispatchEvent(goog.ui.Ratings.EventType.CHANGE);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The index of the currently selected rating.
    + */
    +goog.ui.Ratings.prototype.getSelectedIndex = function() {
    +  return this.selectedIndex_;
    +};
    +
    +
    +/**
    + * Returns the rating value of the currently selected rating
    + * @return {?string} The value of the currently selected rating (or null).
    + */
    +goog.ui.Ratings.prototype.getValue = function() {
    +  return this.selectedIndex_ == -1 ? null : this.ratings_[this.selectedIndex_];
    +};
    +
    +
    +/**
    + * Returns the index of the currently highlighted rating, -1 if the mouse isn't
    + * currently over the widget
    + * @return {number} The index of the currently highlighted rating.
    + */
    +goog.ui.Ratings.prototype.getHighlightedIndex = function() {
    +  return this.highlightedIndex_;
    +};
    +
    +
    +/**
    + * Returns the value of the currently highlighted rating, null if the mouse
    + * isn't currently over the widget
    + * @return {?string} The value of the currently highlighted rating, or null.
    + */
    +goog.ui.Ratings.prototype.getHighlightedValue = function() {
    +  return this.highlightedIndex_ == -1 ? null :
    +      this.ratings_[this.highlightedIndex_];
    +};
    +
    +
    +/**
    + * Sets the array of ratings that the comonent
    + * @param {Array<string>} ratings Array of value to use as ratings.
    + */
    +goog.ui.Ratings.prototype.setRatings = function(ratings) {
    +  this.ratings_ = ratings;
    +  // TODO(user): If rendered update stars
    +};
    +
    +
    +/**
    + * Gets the array of ratings that the component
    + * @return {Array<string>} Array of ratings.
    + */
    +goog.ui.Ratings.prototype.getRatings = function() {
    +  return this.ratings_;
    +};
    +
    +
    +/**
    + * Attaches an input or select element to the ratings widget. The value or
    + * index of the field will be updated along with the ratings widget.
    + * @param {HTMLSelectElement|HTMLInputElement} field The field to attach to.
    + */
    +goog.ui.Ratings.prototype.setAttachedFormField = function(field) {
    +  this.attachedFormField_ = field;
    +};
    +
    +
    +/**
    + * Returns the attached input or select element to the ratings widget.
    + * @return {HTMLSelectElement|HTMLInputElement|null} The attached form field.
    + */
    +goog.ui.Ratings.prototype.getAttachedFormField = function() {
    +  return this.attachedFormField_;
    +};
    +
    +
    +/**
    + * Enables or disables the ratings control.
    + * @param {boolean} enable Whether to enable or disable the control.
    + */
    +goog.ui.Ratings.prototype.setEnabled = function(enable) {
    +  this.isEnabled_ = enable;
    +  if (!enable) {
    +    // Undo any highlighting done during mouseover when disabling the control
    +    // and highlight the last selected rating.
    +    this.resetHighlights_();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the ratings control is enabled.
    + */
    +goog.ui.Ratings.prototype.isEnabled = function() {
    +  return this.isEnabled_;
    +};
    +
    +
    +/**
    + * Handle the mouse moving over a star.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.Ratings.prototype.onMouseOver_ = function(e) {
    +  if (!this.isEnabled()) {
    +    return;
    +  }
    +  if (goog.isDef(e.target.index)) {
    +    var n = e.target.index;
    +    if (this.highlightedIndex_ != n) {
    +      this.highlightIndex_(n);
    +      this.highlightedIndex_ = n;
    +      this.dispatchEvent(goog.ui.Ratings.EventType.HIGHLIGHT_CHANGE);
    +      this.dispatchEvent(goog.ui.Ratings.EventType.HIGHLIGHT);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handle the mouse moving over a star.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.Ratings.prototype.onMouseOut_ = function(e) {
    +  // Only remove the highlight if the mouse is not moving to another star
    +  if (e.relatedTarget && !goog.isDef(e.relatedTarget.index)) {
    +    this.resetHighlights_();
    +  }
    +};
    +
    +
    +/**
    + * Handle the mouse moving over a star.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.Ratings.prototype.onClick_ = function(e) {
    +  if (!this.isEnabled()) {
    +    return;
    +  }
    +
    +  if (goog.isDef(e.target.index)) {
    +    this.setSelectedIndex(e.target.index);
    +  }
    +};
    +
    +
    +/**
    + * Handle the key down event. 0 = unselected in this case, 1 = the first rating
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.Ratings.prototype.onKeyDown_ = function(e) {
    +  if (!this.isEnabled()) {
    +    return;
    +  }
    +  switch (e.keyCode) {
    +    case 27: // esc
    +      this.setSelectedIndex(-1);
    +      break;
    +    case 36: // home
    +      this.setSelectedIndex(0);
    +      break;
    +    case 35: // end
    +      this.setSelectedIndex(this.ratings_.length);
    +      break;
    +    case 37: // left arrow
    +      this.setSelectedIndex(this.getSelectedIndex() - 1);
    +      break;
    +    case 39: // right arrow
    +      this.setSelectedIndex(this.getSelectedIndex() + 1);
    +      break;
    +    default:
    +      // Detected a numeric key stroke, such as 0 - 9.  0 clears, 1 is first
    +      // star, 9 is 9th star or last if there are less than 9 stars.
    +      var num = parseInt(String.fromCharCode(e.keyCode), 10);
    +      if (!isNaN(num)) {
    +        this.setSelectedIndex(num - 1);
    +      }
    +  }
    +};
    +
    +
    +/**
    + * Resets the highlights to the selected rating to undo highlights due to hover
    + * effects.
    + * @private
    + */
    +goog.ui.Ratings.prototype.resetHighlights_ = function() {
    +  this.highlightIndex_(this.selectedIndex_);
    +  this.highlightedIndex_ = -1;
    +  this.dispatchEvent(goog.ui.Ratings.EventType.HIGHLIGHT_CHANGE);
    +  this.dispatchEvent(goog.ui.Ratings.EventType.UNHIGHLIGHT);
    +};
    +
    +
    +/**
    + * Highlights the ratings up to a specific index
    + * @param {number} n Index to highlight.
    + * @private
    + */
    +goog.ui.Ratings.prototype.highlightIndex_ = function(n) {
    +  for (var i = 0, star; star = this.stars_[i]; i++) {
    +    goog.dom.classlist.set(star, this.getClassName_(i, i <= n));
    +  }
    +};
    +
    +
    +/**
    + * Get the class name for a given rating.  All stars have the class:
    + * goog-ratings-star.
    + * Other possible classnames dependent on position and state are:
    + * goog-ratings-firststar-on
    + * goog-ratings-firststar-off
    + * goog-ratings-midstar-on
    + * goog-ratings-midstar-off
    + * goog-ratings-laststar-on
    + * goog-ratings-laststar-off
    + * @param {number} i Index to get class name for.
    + * @param {boolean} on Whether it should be on.
    + * @return {string} The class name.
    + * @private
    + */
    +goog.ui.Ratings.prototype.getClassName_ = function(i, on) {
    +  var className;
    +  var enabledClassName;
    +  var baseClass = this.getCssClass();
    +
    +  if (i === 0) {
    +    className = goog.getCssName(baseClass, 'firststar');
    +  } else if (i == this.ratings_.length - 1) {
    +    className = goog.getCssName(baseClass, 'laststar');
    +  } else {
    +    className = goog.getCssName(baseClass, 'midstar');
    +  }
    +
    +  if (on) {
    +    className = goog.getCssName(className, 'on');
    +  } else {
    +    className = goog.getCssName(className, 'off');
    +  }
    +
    +  if (this.isEnabled_) {
    +    enabledClassName = goog.getCssName(baseClass, 'enabled');
    +  } else {
    +    enabledClassName = goog.getCssName(baseClass, 'disabled');
    +  }
    +
    +  return goog.getCssName(baseClass, 'star') + ' ' + className +
    +      ' ' + enabledClassName;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/registry.js b/mapbox-gl/closure-library/closure/goog/ui/registry.js
    new file mode 100644
    index 0000000000..91257dc42a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/registry.js
    @@ -0,0 +1,172 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Global renderer and decorator registry.
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.registry');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +
    +
    +/**
    + * Given a {@link goog.ui.Component} constructor, returns an instance of its
    + * default renderer.  If the default renderer is a singleton, returns the
    + * singleton instance; otherwise returns a new instance of the renderer class.
    + * @param {Function} componentCtor Component constructor function (for example
    + *     {@code goog.ui.Button}).
    + * @return {goog.ui.ControlRenderer?} Renderer instance (for example the
    + *     singleton instance of {@code goog.ui.ButtonRenderer}), or null if
    + *     no default renderer was found.
    + */
    +goog.ui.registry.getDefaultRenderer = function(componentCtor) {
    +  // Locate the default renderer based on the constructor's unique ID.  If no
    +  // renderer is registered for this class, walk up the superClass_ chain.
    +  var key;
    +  /** @type {Function|undefined} */ var rendererCtor;
    +  while (componentCtor) {
    +    key = goog.getUid(componentCtor);
    +    if ((rendererCtor = goog.ui.registry.defaultRenderers_[key])) {
    +      break;
    +    }
    +    componentCtor = componentCtor.superClass_ ?
    +        componentCtor.superClass_.constructor : null;
    +  }
    +
    +  // If the renderer has a static getInstance method, return the singleton
    +  // instance; otherwise create and return a new instance.
    +  if (rendererCtor) {
    +    return goog.isFunction(rendererCtor.getInstance) ?
    +        rendererCtor.getInstance() : new rendererCtor();
    +  }
    +
    +  return null;
    +};
    +
    +
    +/**
    + * Sets the default renderer for the given {@link goog.ui.Component}
    + * constructor.
    + * @param {Function} componentCtor Component constructor function (for example
    + *     {@code goog.ui.Button}).
    + * @param {Function} rendererCtor Renderer constructor function (for example
    + *     {@code goog.ui.ButtonRenderer}).
    + * @throws {Error} If the arguments aren't functions.
    + */
    +goog.ui.registry.setDefaultRenderer = function(componentCtor, rendererCtor) {
    +  // In this case, explicit validation has negligible overhead (since each
    +  // renderer is only registered once), and helps catch subtle bugs.
    +  if (!goog.isFunction(componentCtor)) {
    +    throw Error('Invalid component class ' + componentCtor);
    +  }
    +  if (!goog.isFunction(rendererCtor)) {
    +    throw Error('Invalid renderer class ' + rendererCtor);
    +  }
    +
    +  // Map the component constructor's unique ID to the renderer constructor.
    +  var key = goog.getUid(componentCtor);
    +  goog.ui.registry.defaultRenderers_[key] = rendererCtor;
    +};
    +
    +
    +/**
    + * Returns the {@link goog.ui.Component} instance created by the decorator
    + * factory function registered for the given CSS class name, or null if no
    + * decorator factory function was found.
    + * @param {string} className CSS class name.
    + * @return {goog.ui.Component?} Component instance.
    + */
    +goog.ui.registry.getDecoratorByClassName = function(className) {
    +  return className in goog.ui.registry.decoratorFunctions_ ?
    +      goog.ui.registry.decoratorFunctions_[className]() : null;
    +};
    +
    +
    +/**
    + * Maps a CSS class name to a function that returns a new instance of
    + * {@link goog.ui.Component} or a subclass, suitable to decorate an element
    + * that has the specified CSS class.
    + * @param {string} className CSS class name.
    + * @param {Function} decoratorFn No-argument function that returns a new
    + *     instance of a {@link goog.ui.Component} to decorate an element.
    + * @throws {Error} If the class name or the decorator function is invalid.
    + */
    +goog.ui.registry.setDecoratorByClassName = function(className, decoratorFn) {
    +  // In this case, explicit validation has negligible overhead (since each
    +  // decorator  is only registered once), and helps catch subtle bugs.
    +  if (!className) {
    +    throw Error('Invalid class name ' + className);
    +  }
    +  if (!goog.isFunction(decoratorFn)) {
    +    throw Error('Invalid decorator function ' + decoratorFn);
    +  }
    +
    +  goog.ui.registry.decoratorFunctions_[className] = decoratorFn;
    +};
    +
    +
    +/**
    + * Returns an instance of {@link goog.ui.Component} or a subclass suitable to
    + * decorate the given element, based on its CSS class.
    + *
    + * TODO(nnaze): Type of element should be {!Element}.
    + *
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Component?} Component to decorate the element (null if
    + *     none).
    + */
    +goog.ui.registry.getDecorator = function(element) {
    +  var decorator;
    +  goog.asserts.assert(element);
    +  var classNames = goog.dom.classlist.get(element);
    +  for (var i = 0, len = classNames.length; i < len; i++) {
    +    if ((decorator = goog.ui.registry.getDecoratorByClassName(classNames[i]))) {
    +      return decorator;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Resets the global renderer and decorator registry.
    + */
    +goog.ui.registry.reset = function() {
    +  goog.ui.registry.defaultRenderers_ = {};
    +  goog.ui.registry.decoratorFunctions_ = {};
    +};
    +
    +
    +/**
    + * Map of {@link goog.ui.Component} constructor unique IDs to the constructors
    + * of their default {@link goog.ui.Renderer}s.
    + * @type {Object}
    + * @private
    + */
    +goog.ui.registry.defaultRenderers_ = {};
    +
    +
    +/**
    + * Map of CSS class names to registry factory functions.  The keys are
    + * class names.  The values are function objects that return new instances
    + * of {@link goog.ui.registry} or one of its subclasses, suitable to
    + * decorate elements marked with the corresponding CSS class.  Used by
    + * containers while decorating their children.
    + * @type {Object}
    + * @private
    + */
    +goog.ui.registry.decoratorFunctions_ = {};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/registry_test.html b/mapbox-gl/closure-library/closure/goog/ui/registry_test.html
    new file mode 100644
    index 0000000000..b8f3764359
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/registry_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<!-- Author: attila@google.com (Attila Bodis) -->
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.registry
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.registryTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="x" class="fake-component-x">
    +  </div>
    +  <div id="y" class="fake-component-y fake-component-x">
    +  </div>
    +  <div id="z" class="fake-component-z">
    +  </div>
    +  <div id="u">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/registry_test.js b/mapbox-gl/closure-library/closure/goog/ui/registry_test.js
    new file mode 100644
    index 0000000000..80fa5bb5d2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/registry_test.js
    @@ -0,0 +1,230 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.registryTest');
    +goog.setTestOnly('goog.ui.registryTest');
    +
    +goog.require('goog.object');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.registry');
    +
    +// Fake component and renderer implementations, for testing only.
    +
    +// UnknownComponent has no default renderer or decorator registered.
    +function UnknownComponent() {
    +}
    +
    +// FakeComponentX's default renderer is FakeRenderer.  It also has a
    +// decorator.
    +function FakeComponentX() {
    +  this.element = null;
    +}
    +
    +FakeComponentX.prototype.decorate = function(element) {
    +  this.element = element;
    +};
    +
    +// FakeComponentY doesn't have an explicitly registered default
    +// renderer; it should inherit the default renderer from its superclass.
    +// It does have a decorator registered.
    +function FakeComponentY() {
    +  FakeComponentX.call(this);
    +}
    +goog.inherits(FakeComponentY, FakeComponentX);
    +
    +// FakeComponentZ is just another component.  Its default renderer is
    +// FakeSingletonRenderer, but it has no decorator registered.
    +function FakeComponentZ() {
    +}
    +
    +// FakeRenderer is a stateful renderer.
    +function FakeRenderer() {
    +}
    +
    +// FakeSingletonRenderer is a stateless renderer that can be used as a
    +// singleton.
    +function FakeSingletonRenderer() {
    +}
    +
    +FakeSingletonRenderer.instance_ = new FakeSingletonRenderer();
    +
    +FakeSingletonRenderer.getInstance = function() {
    +  return FakeSingletonRenderer.instance_;
    +};
    +
    +function setUp() {
    +  goog.ui.registry.setDefaultRenderer(FakeComponentX, FakeRenderer);
    +  goog.ui.registry.setDefaultRenderer(FakeComponentZ,
    +      FakeSingletonRenderer);
    +
    +  goog.ui.registry.setDecoratorByClassName('fake-component-x',
    +      function() {
    +        return new FakeComponentX();
    +      });
    +  goog.ui.registry.setDecoratorByClassName('fake-component-y',
    +      function() {
    +        return new FakeComponentY();
    +      });
    +}
    +
    +function tearDown() {
    +  goog.ui.registry.reset();
    +}
    +
    +function testGetDefaultRenderer() {
    +  var rx1 = goog.ui.registry.getDefaultRenderer(FakeComponentX);
    +  var rx2 = goog.ui.registry.getDefaultRenderer(FakeComponentX);
    +  assertTrue('FakeComponentX\'s default renderer must be a FakeRenderer',
    +      rx1 instanceof FakeRenderer);
    +  assertNotEquals('Each call to getDefaultRenderer must create a new ' +
    +      'FakeRenderer', rx1, rx2);
    +
    +  var ry = goog.ui.registry.getDefaultRenderer(FakeComponentY);
    +  assertTrue('FakeComponentY must inherit its default renderer from ' +
    +      'its superclass', ry instanceof FakeRenderer);
    +
    +  var rz1 = goog.ui.registry.getDefaultRenderer(FakeComponentZ);
    +  var rz2 = goog.ui.registry.getDefaultRenderer(FakeComponentZ);
    +  assertTrue('FakeComponentZ\' default renderer must be a ' +
    +      'FakeSingletonRenderer', rz1 instanceof FakeSingletonRenderer);
    +  assertEquals('Each call to getDefaultRenderer must return the ' +
    +      'singleton instance of FakeSingletonRenderer', rz1, rz2);
    +
    +  assertNull('getDefaultRenderer must return null for unknown component',
    +      goog.ui.registry.getDefaultRenderer(UnknownComponent));
    +}
    +
    +function testSetDefaultRenderer() {
    +  var rx1 = goog.ui.registry.getDefaultRenderer(FakeComponentX);
    +  assertTrue('FakeComponentX\'s renderer must be FakeRenderer',
    +      rx1 instanceof FakeRenderer);
    +
    +  var ry1 = goog.ui.registry.getDefaultRenderer(FakeComponentY);
    +  assertTrue('FakeComponentY must inherit its default renderer from ' +
    +      'its superclass', ry1 instanceof FakeRenderer);
    +
    +  goog.ui.registry.setDefaultRenderer(FakeComponentX,
    +      FakeSingletonRenderer);
    +
    +  var rx2 = goog.ui.registry.getDefaultRenderer(FakeComponentX);
    +  assertEquals('FakeComponentX\'s renderer must be FakeSingletonRenderer',
    +      FakeSingletonRenderer.getInstance(), rx2);
    +
    +  var ry2 = goog.ui.registry.getDefaultRenderer(FakeComponentY);
    +  assertEquals('FakeComponentY must inherit the new default renderer ' +
    +      'from its superclass', FakeSingletonRenderer.getInstance(), ry2);
    +
    +  goog.ui.registry.setDefaultRenderer(FakeComponentY, FakeRenderer);
    +
    +  var rx3 = goog.ui.registry.getDefaultRenderer(FakeComponentX);
    +  assertEquals('FakeComponentX\'s renderer must be unchanged',
    +      FakeSingletonRenderer.getInstance(), rx3);
    +
    +  var ry3 = goog.ui.registry.getDefaultRenderer(FakeComponentY);
    +  assertTrue('FakeComponentY must now have its own default renderer',
    +      ry3 instanceof FakeRenderer);
    +
    +  assertThrows('Calling setDefaultRenderer with non-function component ' +
    +      'must throw error',
    +      function() {
    +        goog.ui.registry.setDefaultRenderer('Not function', FakeRenderer);
    +      });
    +
    +  assertThrows('Calling setDefaultRenderer with non-function renderer ' +
    +      'must throw error',
    +      function() {
    +        goog.ui.registry.setDefaultRenderer(FakeComponentX, 'Not function');
    +      });
    +}
    +
    +function testGetDecoratorByClassName() {
    +  var dx1 = goog.ui.registry.getDecoratorByClassName('fake-component-x');
    +  var dx2 = goog.ui.registry.getDecoratorByClassName('fake-component-x');
    +  assertTrue('fake-component-x must be decorated by a FakeComponentX',
    +      dx1 instanceof FakeComponentX);
    +  assertNotEquals('Each call to getDecoratorByClassName must return a ' +
    +      'new FakeComponentX instance', dx1, dx2);
    +
    +  var dy1 = goog.ui.registry.getDecoratorByClassName('fake-component-y');
    +  var dy2 = goog.ui.registry.getDecoratorByClassName('fake-component-y');
    +  assertTrue('fake-component-y must be decorated by a FakeComponentY',
    +      dy1 instanceof FakeComponentY);
    +  assertNotEquals('Each call to getDecoratorByClassName must return a ' +
    +      'new FakeComponentY instance', dy1, dy2);
    +
    +  assertNull('getDecoratorByClassName must return null for unknown class',
    +      goog.ui.registry.getDecoratorByClassName('fake-component-z'));
    +  assertNull('getDecoratorByClassName must return null for empty string',
    +      goog.ui.registry.getDecoratorByClassName(''));
    +}
    +
    +function testSetDecoratorByClassName() {
    +  var dx1, dx2;
    +
    +  dx1 = goog.ui.registry.getDecoratorByClassName('fake-component-x');
    +  assertTrue('fake-component-x must be decorated by a FakeComponentX',
    +      dx1 instanceof FakeComponentX);
    +  goog.ui.registry.setDecoratorByClassName('fake-component-x',
    +      function() {
    +        return new UnknownComponent();
    +      });
    +  dx2 = goog.ui.registry.getDecoratorByClassName('fake-component-x');
    +  assertTrue('fake-component-x must now be decorated by UnknownComponent',
    +      dx2 instanceof UnknownComponent);
    +
    +  assertThrows('Calling setDecoratorByClassName with invalid class name ' +
    +      'must throw error',
    +      function() {
    +        goog.ui.registry.setDecoratorByClassName('', function() {
    +          return new UnknownComponent();
    +        });
    +      });
    +
    +  assertThrows('Calling setDecoratorByClassName with non-function ' +
    +      'decorator must throw error',
    +      function() {
    +        goog.ui.registry.setDecoratorByClassName('fake-component-x',
    +            'Not function');
    +      });
    +}
    +
    +function testGetDecorator() {
    +  var dx = goog.ui.registry.getDecorator(document.getElementById('x'));
    +  assertTrue('Decorator for element with fake-component-x class must be ' +
    +      'a FakeComponentX', dx instanceof FakeComponentX);
    +
    +  var dy = goog.ui.registry.getDecorator(document.getElementById('y'));
    +  assertTrue('Decorator for element with fake-component-y class must be ' +
    +      'a FakeComponentY', dy instanceof FakeComponentY);
    +
    +  var dz = goog.ui.registry.getDecorator(document.getElementById('z'));
    +  assertNull('Decorator for element with unknown class must be null', dz);
    +
    +  var du = goog.ui.registry.getDecorator(document.getElementById('u'));
    +  assertNull('Decorator for element without CSS class must be null', du);
    +}
    +
    +function testReset() {
    +  assertNotEquals('Some renderers must be registered', 0,
    +      goog.object.getCount(goog.ui.registry.defaultRenderers_));
    +  assertNotEquals('Some decorators must be registered', 0,
    +      goog.object.getCount(goog.ui.registry.decoratorFunctions_));
    +
    +  goog.ui.registry.reset();
    +
    +  assertTrue('No renderers must be registered',
    +      goog.object.isEmpty(goog.ui.registry.defaultRenderers_));
    +  assertTrue('No decorators must be registered',
    +      goog.object.isEmpty(goog.ui.registry.decoratorFunctions_));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker.js b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker.js
    new file mode 100644
    index 0000000000..c20143bf3f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker.js
    @@ -0,0 +1,780 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Rich text spell checker implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/richtextspellchecker.html
    + */
    +
    +goog.provide('goog.ui.RichTextSpellChecker');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.Range');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.string.StringBuffer');
    +goog.require('goog.style');
    +goog.require('goog.ui.AbstractSpellChecker');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.PopupMenu');
    +
    +
    +
    +/**
    + * Rich text spell checker implementation.
    + *
    + * @param {goog.spell.SpellCheck} handler Instance of the SpellCheckHandler
    + *     support object to use. A single instance can be shared by multiple editor
    + *     components.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.AbstractSpellChecker}
    + */
    +goog.ui.RichTextSpellChecker = function(handler, opt_domHelper) {
    +  goog.ui.AbstractSpellChecker.call(this, handler, opt_domHelper);
    +
    +  /**
    +   * String buffer for use in reassembly of the original text.
    +   * @type {goog.string.StringBuffer}
    +   * @private
    +   */
    +  this.workBuffer_ = new goog.string.StringBuffer();
    +
    +  /**
    +   * Bound async function (to avoid rebinding it on every call).
    +   * @type {Function}
    +   * @private
    +   */
    +  this.boundContinueAsyncFn_ = goog.bind(this.continueAsync_, this);
    +
    +  /**
    +   * Event handler for listening to events without leaking.
    +   * @private {!goog.events.EventHandler}
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler(this);
    +  this.registerDisposable(this.eventHandler_);
    +
    +  /**
    +   * The object handling keyboard events.
    +   * @private {!goog.events.KeyHandler}
    +   */
    +  this.keyHandler_ = new goog.events.KeyHandler();
    +  this.registerDisposable(this.keyHandler_);
    +};
    +goog.inherits(goog.ui.RichTextSpellChecker, goog.ui.AbstractSpellChecker);
    +goog.tagUnsealableClass(goog.ui.RichTextSpellChecker);
    +
    +
    +/**
    + * Root node for rich editor.
    + * @type {Node}
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.rootNode_;
    +
    +
    +/**
    + * Indicates whether the root node for the rich editor is an iframe.
    + * @private {boolean}
    + */
    +goog.ui.RichTextSpellChecker.prototype.rootNodeIframe_ = false;
    +
    +
    +/**
    + * Current node where spell checker has interrupted to go to the next stack
    + * frame.
    + * @type {Node}
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.currentNode_;
    +
    +
    +/**
    + * Counter of inserted elements. Used in processing loop to attempt to preserve
    + * existing nodes if they contain no misspellings.
    + * @type {number}
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.elementsInserted_ = 0;
    +
    +
    +/**
    + * Number of words to scan to precharge the dictionary.
    + * @type {number}
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.dictionaryPreScanSize_ = 1000;
    +
    +
    +/**
    + * Class name for word spans.
    + * @type {string}
    + */
    +goog.ui.RichTextSpellChecker.prototype.wordClassName =
    +    goog.getCssName('goog-spellcheck-word');
    +
    +
    +/**
    + * DomHelper to be used for interacting with the editable document/element.
    + *
    + * @type {goog.dom.DomHelper|undefined}
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.editorDom_;
    +
    +
    +/**
    + * Tag name portion of the marker for the text that does not need to be checked
    + * for spelling.
    + *
    + * @type {Array<string|undefined>}
    + */
    +goog.ui.RichTextSpellChecker.prototype.excludeTags;
    +
    +
    +/**
    + * CSS Style text for invalid words. As it's set inside the rich edit iframe
    + * classes defined in the parent document are not availble, thus the style is
    + * set inline.
    + * @type {string}
    + */
    +goog.ui.RichTextSpellChecker.prototype.invalidWordCssText =
    +    'background: yellow;';
    +
    +
    +/**
    + * Creates the initial DOM representation for the component.
    + *
    + * @throws {Error} Not supported. Use decorate.
    + * @see #decorate
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.createDom = function() {
    +  throw Error('Render not supported for goog.ui.RichTextSpellChecker.');
    +};
    +
    +
    +/**
    + * Decorates the element for the UI component.
    + *
    + * @param {Element} element Element to decorate.
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.decorateInternal = function(element) {
    +  this.setElementInternal(element);
    +  this.rootNodeIframe_ = element.contentDocument || element.contentWindow;
    +  if (this.rootNodeIframe_) {
    +    var doc = element.contentDocument || element.contentWindow.document;
    +    this.rootNode_ = doc.body;
    +    this.editorDom_ = goog.dom.getDomHelper(doc);
    +  } else {
    +    this.rootNode_ = element;
    +    this.editorDom_ = goog.dom.getDomHelper(element);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.enterDocument = function() {
    +  goog.ui.RichTextSpellChecker.superClass_.enterDocument.call(this);
    +
    +  var rootElement = goog.asserts.assertElement(this.rootNode_,
    +      'The rootNode_ of a richtextspellchecker must be an Element.');
    +  this.keyHandler_.attach(rootElement);
    +
    +  this.initSuggestionsMenu();
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.initSuggestionsMenu = function() {
    +  goog.ui.RichTextSpellChecker.base(this, 'initSuggestionsMenu');
    +
    +  var menu = goog.asserts.assertInstanceof(this.getMenu(), goog.ui.PopupMenu,
    +      'The menu of a richtextspellchecker must be a PopupMenu.');
    +  this.eventHandler_.listen(menu,
    +      goog.ui.Component.EventType.HIDE, this.onCorrectionHide_);
    +};
    +
    +
    +/**
    + * Checks spelling for all text and displays correction UI.
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.check = function() {
    +  this.blockReadyEvents();
    +  this.preChargeDictionary_(this.rootNode_, this.dictionaryPreScanSize_);
    +  this.unblockReadyEvents();
    +
    +  this.eventHandler_.listen(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.READY, this.onDictionaryCharged_, true);
    +  this.spellCheck.processPending();
    +};
    +
    +
    +/**
    + * Processes nodes recursively.
    + *
    + * @param {Node} node Node to start with.
    + * @param {number} words Max number of words to process.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.preChargeDictionary_ = function(node,
    +                                                                       words) {
    +  while (node) {
    +    var next = this.nextNode_(node);
    +    if (this.isExcluded_(node)) {
    +      node = next;
    +      continue;
    +    }
    +    if (node.nodeType == goog.dom.NodeType.TEXT) {
    +      if (node.nodeValue) {
    +        words -= this.populateDictionary(node.nodeValue, words);
    +        if (words <= 0) {
    +          return;
    +        }
    +      }
    +    } else if (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +      if (node.firstChild) {
    +        next = node.firstChild;
    +      }
    +    }
    +    node = next;
    +  }
    +};
    +
    +
    +/**
    + * Starts actual processing after the dictionary is charged.
    + * @param {goog.events.Event} e goog.spell.SpellCheck.EventType.READY event.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.onDictionaryCharged_ = function(e) {
    +  e.stopPropagation();
    +  this.eventHandler_.unlisten(this.spellCheck,
    +      goog.spell.SpellCheck.EventType.READY, this.onDictionaryCharged_, true);
    +
    +  // Now actually do the spell checking.
    +  this.clearWordElements();
    +  this.initializeAsyncMode();
    +  this.elementsInserted_ = 0;
    +  var result = this.processNode_(this.rootNode_);
    +  if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    goog.Timer.callOnce(this.boundContinueAsyncFn_);
    +    return;
    +  }
    +  this.finishAsyncProcessing();
    +  this.finishCheck_();
    +};
    +
    +
    +/**
    + * Continues asynchrnonous spell checking.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.continueAsync_ = function() {
    +  var result = this.continueAsyncProcessing();
    +  if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    goog.Timer.callOnce(this.boundContinueAsyncFn_);
    +    return;
    +  }
    +  result = this.processNode_(this.currentNode_);
    +  if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +    goog.Timer.callOnce(this.boundContinueAsyncFn_);
    +    return;
    +  }
    +  this.finishAsyncProcessing();
    +  this.finishCheck_();
    +};
    +
    +
    +/**
    + * Finalizes spelling check.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.finishCheck_ = function() {
    +  delete this.currentNode_;
    +  this.spellCheck.processPending();
    +
    +  if (!this.isVisible()) {
    +    this.eventHandler_.
    +        listen(this.rootNode_, goog.events.EventType.CLICK, this.onWordClick_).
    +        listen(this.keyHandler_, goog.events.KeyHandler.EventType.KEY,
    +            this.handleRootNodeKeyEvent);
    +  }
    +  goog.ui.RichTextSpellChecker.superClass_.check.call(this);
    +};
    +
    +
    +/**
    + * Finds next node in our enumeration of the tree.
    + *
    + * @param {Node} node The node to which we're computing the next node for.
    + * @return {Node} The next node or null if none was found.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.nextNode_ = function(node) {
    +  while (node != this.rootNode_) {
    +    if (node.nextSibling) {
    +      return node.nextSibling;
    +    }
    +    node = node.parentNode;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Determines if the node is text node without any children.
    + *
    + * @param {Node} node The node to check.
    + * @return {boolean} Whether the node is a text leaf node.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.isTextLeaf_ = function(node) {
    +  return node != null &&
    +         node.nodeType == goog.dom.NodeType.TEXT &&
    +         !node.firstChild;
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.setExcludeMarker = function(marker) {
    +  if (marker) {
    +    if (typeof marker == 'string') {
    +      marker = [marker];
    +    }
    +
    +    this.excludeTags = [];
    +    this.excludeMarker = [];
    +    for (var i = 0; i < marker.length; i++) {
    +      var parts = marker[i].split('.');
    +      if (parts.length == 2) {
    +        this.excludeTags.push(parts[0]);
    +        this.excludeMarker.push(parts[1]);
    +      } else {
    +        this.excludeMarker.push(parts[0]);
    +        this.excludeTags.push(undefined);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Determines if the node is excluded from checking.
    + *
    + * @param {Node} node The node to check.
    + * @return {boolean} Whether the node is excluded.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.isExcluded_ = function(node) {
    +  if (this.excludeMarker && node.className) {
    +    for (var i = 0; i < this.excludeMarker.length; i++) {
    +      var excludeTag = this.excludeTags[i];
    +      var excludeClass = this.excludeMarker[i];
    +      var isExcluded = !!(excludeClass &&
    +          node.className.indexOf(excludeClass) != -1 &&
    +          (!excludeTag || node.tagName == excludeTag));
    +      if (isExcluded) {
    +        return true;
    +      }
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Processes nodes recursively.
    + *
    + * @param {Node} node Node where to start.
    + * @return {goog.ui.AbstractSpellChecker.AsyncResult|undefined} Result code.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.processNode_ = function(node) {
    +  delete this.currentNode_;
    +  while (node) {
    +    var next = this.nextNode_(node);
    +    if (this.isExcluded_(node)) {
    +      node = next;
    +      continue;
    +    }
    +    if (node.nodeType == goog.dom.NodeType.TEXT) {
    +      var deleteNode = true;
    +      if (node.nodeValue) {
    +        var currentElements = this.elementsInserted_;
    +        var result = this.processTextAsync(node, node.nodeValue);
    +        if (result == goog.ui.AbstractSpellChecker.AsyncResult.PENDING) {
    +          // This markes node for deletion (empty nodes get deleted couple
    +          // of lines down this function). This is so our algorithm terminates.
    +          // In this case the node may be needlessly recreated, but it
    +          // happens rather infrequently and saves a lot of code.
    +          node.nodeValue = '';
    +          this.currentNode_ = node;
    +          return result;
    +        }
    +        // If we did not add nodes in processing, the current element is still
    +        // valid. Let's preserve it!
    +        if (currentElements == this.elementsInserted_) {
    +          deleteNode = false;
    +        }
    +      }
    +      if (deleteNode) {
    +        goog.dom.removeNode(node);
    +      }
    +    } else if (node.nodeType == goog.dom.NodeType.ELEMENT) {
    +      // If this is a spell checker element...
    +      if (node.className == this.wordClassName) {
    +        // First, reconsolidate the text nodes inside the element - editing
    +        // in IE splits them up.
    +        var runner = node.firstChild;
    +        while (runner) {
    +          if (this.isTextLeaf_(runner)) {
    +            while (this.isTextLeaf_(runner.nextSibling)) {
    +              // Yes, this is not super efficient in IE, but it will almost
    +              // never happen.
    +              runner.nodeValue += runner.nextSibling.nodeValue;
    +              goog.dom.removeNode(runner.nextSibling);
    +            }
    +          }
    +          runner = runner.nextSibling;
    +        }
    +        // Move its contents out and reprocess it on the next iteration.
    +        if (node.firstChild) {
    +          next = node.firstChild;
    +          while (node.firstChild) {
    +            node.parentNode.insertBefore(node.firstChild, node);
    +          }
    +        }
    +        // get rid of the empty shell.
    +        goog.dom.removeNode(node);
    +      } else {
    +        if (node.firstChild) {
    +          next = node.firstChild;
    +        }
    +      }
    +    }
    +    node = next;
    +  }
    +};
    +
    +
    +/**
    + * Processes word.
    + *
    + * @param {Node} node Node containing word.
    + * @param {string} word Word to process.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of the word.
    + * @protected
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.processWord = function(node, word,
    +                                                              status) {
    +  node.parentNode.insertBefore(this.createWordElement(word, status), node);
    +  this.elementsInserted_++;
    +};
    +
    +
    +/**
    + * Processes recognized text and separators.
    + *
    + * @param {Node} node Node containing separator.
    + * @param {string} text Text to process.
    + * @protected
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.processRange = function(node, text) {
    +  // The text does not change, it only gets split, so if the lengths are the
    +  // same, the text is the same, so keep the existing node.
    +  if (node.nodeType == goog.dom.NodeType.TEXT && node.nodeValue.length ==
    +      text.length) {
    +    return;
    +  }
    +
    +  node.parentNode.insertBefore(this.editorDom_.createTextNode(text), node);
    +  this.elementsInserted_++;
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.getElementByIndex = function(id) {
    +  return this.editorDom_.getElement(this.makeElementId(id));
    +};
    +
    +
    +/**
    + * Updates or replaces element based on word status.
    + * @see goog.ui.AbstractSpellChecker.prototype.updateElement_
    + *
    + * Overridden from AbstractSpellChecker because we need to be mindful of
    + * deleting the currentNode_ - this can break our pending processing.
    + *
    + * @param {Element} el Word element.
    + * @param {string} word Word to update status for.
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of word.
    + * @protected
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.updateElement = function(el, word,
    +    status) {
    +  if (status == goog.spell.SpellCheck.WordStatus.VALID && el !=
    +      this.currentNode_ && el.nextSibling != this.currentNode_) {
    +    this.removeMarkup(el);
    +  } else {
    +    goog.dom.setProperties(el, this.getElementProperties(status));
    +  }
    +};
    +
    +
    +/**
    + * Hides correction UI.
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.resume = function() {
    +  goog.ui.RichTextSpellChecker.superClass_.resume.call(this);
    +
    +  this.restoreNode_(this.rootNode_);
    +
    +  this.eventHandler_.
    +      unlisten(this.rootNode_, goog.events.EventType.CLICK, this.onWordClick_).
    +      unlisten(this.keyHandler_, goog.events.KeyHandler.EventType.KEY,
    +          this.handleRootNodeKeyEvent);
    +};
    +
    +
    +/**
    + * Processes nodes recursively, removes all spell checker markup, and
    + * consolidates text nodes.
    + *
    + * @param {Node} node node on which to recurse.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.restoreNode_ = function(node) {
    +  while (node) {
    +    if (this.isExcluded_(node)) {
    +      node = node.nextSibling;
    +      continue;
    +    }
    +    // Contents of the child of the element is usually 1 text element, but the
    +    // user can actually add multiple nodes in it during editing. So we move
    +    // all the children out, prepend, and reprocess (pointer is set back to
    +    // the first node that's been moved out, and the loop repeats).
    +    if (node.nodeType == goog.dom.NodeType.ELEMENT && node.className ==
    +        this.wordClassName) {
    +      var firstElement = node.firstChild;
    +      var next;
    +      for (var child = firstElement; child; child = next) {
    +        next = child.nextSibling;
    +        node.parentNode.insertBefore(child, node);
    +      }
    +      next = firstElement || node.nextSibling;
    +      goog.dom.removeNode(node);
    +      node = next;
    +      continue;
    +    }
    +    // If this is a chain of text elements, we're trying to consolidate it.
    +    var textLeaf = this.isTextLeaf_(node);
    +    if (textLeaf) {
    +      var textNodes = 1;
    +      var next = node.nextSibling;
    +      while (this.isTextLeaf_(node.previousSibling)) {
    +        node = node.previousSibling;
    +        ++textNodes;
    +      }
    +      while (this.isTextLeaf_(next)) {
    +        next = next.nextSibling;
    +        ++textNodes;
    +      }
    +      if (textNodes > 1) {
    +        this.workBuffer_.append(node.nodeValue);
    +        while (this.isTextLeaf_(node.nextSibling)) {
    +          this.workBuffer_.append(node.nextSibling.nodeValue);
    +          goog.dom.removeNode(node.nextSibling);
    +        }
    +        node.nodeValue = this.workBuffer_.toString();
    +        this.workBuffer_.clear();
    +      }
    +    }
    +    // Process child nodes, if any.
    +    if (node.firstChild) {
    +      this.restoreNode_(node.firstChild);
    +    }
    +    node = node.nextSibling;
    +  }
    +};
    +
    +
    +/**
    + * Returns desired element properties for the specified status.
    + *
    + * @param {goog.spell.SpellCheck.WordStatus} status Status of the word.
    + * @return {!Object} Properties to apply to word element.
    + * @protected
    + * @override
    + */
    +goog.ui.RichTextSpellChecker.prototype.getElementProperties =
    +    function(status) {
    +  return {
    +    'class': this.wordClassName,
    +    'style': (status == goog.spell.SpellCheck.WordStatus.INVALID) ?
    +        this.invalidWordCssText : ''
    +  };
    +};
    +
    +
    +/**
    + * Handler for click events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.onWordClick_ = function(event) {
    +  var target = /** @type {Element} */ (event.target);
    +  if (event.target.className == this.wordClassName &&
    +      this.spellCheck.checkWord(goog.dom.getTextContent(target)) ==
    +      goog.spell.SpellCheck.WordStatus.INVALID) {
    +
    +    this.showSuggestionsMenu(target, event);
    +
    +    // Prevent document click handler from closing the menu.
    +    event.stopPropagation();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.disposeInternal = function() {
    +  goog.ui.RichTextSpellChecker.superClass_.disposeInternal.call(this);
    +  this.rootNode_ = null;
    +  this.editorDom_ = null;
    +};
    +
    +
    +/**
    + * Returns whether the editor node is an iframe.
    + *
    + * @return {boolean} true the editor node is an iframe, otherwise false.
    + * @protected
    + */
    +goog.ui.RichTextSpellChecker.prototype.isEditorIframe = function() {
    +  return this.rootNodeIframe_;
    +};
    +
    +
    +/**
    + * Handles keyboard events inside the editor to allow keyboard navigation
    + * between misspelled words and activation of the suggestion menu.
    + *
    + * @param {goog.events.BrowserEvent} e the key event.
    + * @return {boolean} The handled value.
    + * @protected
    + */
    +goog.ui.RichTextSpellChecker.prototype.handleRootNodeKeyEvent = function(e) {
    +  var handled = false;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.RIGHT:
    +      if (e.ctrlKey) {
    +        handled = this.navigate(goog.ui.AbstractSpellChecker.Direction.NEXT);
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.LEFT:
    +      if (e.ctrlKey) {
    +        handled = this.navigate(
    +            goog.ui.AbstractSpellChecker.Direction.PREVIOUS);
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.DOWN:
    +      if (this.getFocusedElementIndex()) {
    +        var el = this.editorDom_.getElement(this.makeElementId(
    +            this.getFocusedElementIndex()));
    +        if (el) {
    +          var position = goog.style.getClientPosition(el);
    +
    +          if (this.isEditorIframe()) {
    +            var iframePosition = goog.style.getClientPosition(
    +                this.getElementStrict());
    +            position = goog.math.Coordinate.sum(iframePosition, position);
    +          }
    +
    +          var size = goog.style.getSize(el);
    +          position.x += size.width / 2;
    +          position.y += size.height / 2;
    +          this.showSuggestionsMenu(el, position);
    +          handled = true;
    +        }
    +      }
    +      break;
    +  }
    +
    +  if (handled) {
    +    e.preventDefault();
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.onCorrectionAction = function(event) {
    +  goog.ui.RichTextSpellChecker.base(this, 'onCorrectionAction', event);
    +
    +  // In case of editWord base class has already set the focus (on the input),
    +  // otherwise set the focus back on the word.
    +  if (event.target != this.getMenuEdit()) {
    +    this.reFocus_();
    +  }
    +};
    +
    +
    +/**
    + * Restores focus when the suggestion menu is hidden.
    + *
    + * @param {goog.events.BrowserEvent} event Blur event.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.onCorrectionHide_ = function(event) {
    +  this.reFocus_();
    +};
    +
    +
    +/**
    + * Sets the focus back on the previously focused word element.
    + * @private
    + */
    +goog.ui.RichTextSpellChecker.prototype.reFocus_ = function() {
    +  this.getElementStrict().focus();
    +
    +  var el = this.getElementByIndex(this.getFocusedElementIndex());
    +  if (el) {
    +    this.focusOnElement(el);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.RichTextSpellChecker.prototype.focusOnElement = function(element) {
    +  goog.dom.Range.createCaret(element, 0).select();
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.html b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.html
    new file mode 100644
    index 0000000000..2528646d1e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.html
    @@ -0,0 +1,38 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.RichTextSpellChecker
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.RichTextSpellCheckerTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="test1">
    +  </div>
    +  <div id="test2">
    +  </div>
    +  <div id="test3">
    +  </div>
    +  <div id="test4" contenteditable="true">
    +  </div>
    +  <div id="test5" contenteditable="true">
    +  </div>
    +  <div id="test6" contenteditable="true">
    +  </div>
    +  <div id="test7" contenteditable="true">
    +  </div>
    +  <div id="test8" contenteditable="true">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.js b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.js
    new file mode 100644
    index 0000000000..143a55a3c5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/richtextspellchecker_test.js
    @@ -0,0 +1,367 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.RichTextSpellCheckerTest');
    +goog.setTestOnly('goog.ui.RichTextSpellCheckerTest');
    +
    +goog.require('goog.dom.Range');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.object');
    +goog.require('goog.spell.SpellCheck');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.RichTextSpellChecker');
    +
    +var VOCABULARY = ['test', 'words', 'a', 'few'];
    +var SUGGESTIONS = ['foo', 'bar'];
    +var EXCLUDED_DATA = ['DIV.goog-quote', 'goog-comment', 'SPAN.goog-note'];
    +
    +
    +/**
    + * Delay in ms needed for the spell check word lookup to finish. Finishing the
    + * lookup also finishes the spell checking.
    + * @see goog.spell.SpellCheck.LOOKUP_DELAY_
    + */
    +var SPELL_CHECK_LOOKUP_DELAY = 100;
    +
    +var TEST_TEXT1 = 'this test is longer than a few words now';
    +var TEST_TEXT2 = 'test another simple text with misspelled words';
    +var TEST_TEXT3 = 'test another simple text with misspelled words' +
    +    '<b class="goog-quote">test another simple text with misspelled words<u> ' +
    +    'test another simple text with misspelled words<del class="goog-quote"> ' +
    +    'test another simple text with misspelled words<i>this test is longer ' +
    +    'than a few words now</i>test another simple text with misspelled words ' +
    +    '<i>this test is longer than a few words now</i></del>test another ' +
    +    'simple text with misspelled words<del class="goog-quote">test another ' +
    +    'simple text with misspelled words<i>this test is longer than a few ' +
    +    'words now</i>test another simple text with misspelled words<i>this test ' +
    +    'is longer than a few words now</i></del></u>test another simple text ' +
    +    'with misspelled words<u>test another simple text with misspelled words' +
    +    '<del class="goog-quote">test another simple text with misspelled words' +
    +    '<i> thistest is longer than a few words now</i>test another simple text ' +
    +    'with misspelled words<i>this test is longer than a few words ' +
    +    'now</i></del>test another simple text with misspelled words' +
    +    '<del class="goog-quote">test another simple text with misspelled words' +
    +    '<i>this test is longer than a few words now</i>test another simple text ' +
    +    'with misspelled words<i>this test is longer than a few words ' +
    +    'now</i></del></u></b>';
    +
    +var spellChecker;
    +var handler;
    +var mockClock;
    +
    +function setUp() {
    +  mockClock = new goog.testing.MockClock(true /* install */);
    +  handler = new goog.spell.SpellCheck(localSpellCheckingFunction);
    +  spellChecker = new goog.ui.RichTextSpellChecker(handler);
    +}
    +
    +function tearDown() {
    +  spellChecker.dispose();
    +  handler.dispose();
    +  mockClock.dispose();
    +}
    +
    +function waitForSpellCheckToFinish() {
    +  mockClock.tick(SPELL_CHECK_LOOKUP_DELAY);
    +}
    +
    +
    +/**
    + * @typedef {!Array<string><string>>}
    + * @suppress {missingProvide}
    + */
    +var lookupWordEntry;
    +
    +
    +/**
    + * Function to use for word lookup by the spell check handler. This function is
    + * supplied as a constructor parameter for the spell check handler.
    + * @param {!Array<string>} words Unknown words that need to be looked up.
    + * @param {!goog.spell.SpellCheck} spellChecker The spell check handler.
    + * @param {function(!Array.)} callback The lookup callback
    + *     function.
    + */
    +function localSpellCheckingFunction(words, spellChecker, callback) {
    +  var len = words.length;
    +  var results = [];
    +  for (var i = 0; i < len; i++) {
    +    var word = words[i];
    +    var found = false;
    +    for (var j = 0; j < VOCABULARY.length; ++j) {
    +      if (VOCABULARY[j] == word) {
    +        found = true;
    +        break;
    +      }
    +    }
    +    if (found) {
    +      results.push([word, goog.spell.SpellCheck.WordStatus.VALID]);
    +    } else {
    +      results.push([word, goog.spell.SpellCheck.WordStatus.INVALID,
    +        SUGGESTIONS]);
    +    }
    +  }
    +  callback.call(spellChecker, results);
    +}
    +
    +function testDocumentIntegrity() {
    +  var el = document.getElementById('test1');
    +  spellChecker.decorate(el);
    +  el.appendChild(document.createTextNode(TEST_TEXT3));
    +  var el2 = el.cloneNode(true);
    +
    +  spellChecker.setExcludeMarker('goog-quote');
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  spellChecker.ignoreWord('iggnore');
    +  waitForSpellCheckToFinish();
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  spellChecker.resume();
    +  waitForSpellCheckToFinish();
    +
    +  assertEquals('Spell checker run should not change the underlying element.',
    +               el2.innerHTML, el.innerHTML);
    +}
    +
    +function testExcludeMarkers() {
    +  var el = document.getElementById('test1');
    +  spellChecker.decorate(el);
    +  spellChecker.setExcludeMarker(
    +      ['DIV.goog-quote', 'goog-comment', 'SPAN.goog-note']);
    +  assertArrayEquals(['goog-quote', 'goog-comment', 'goog-note'],
    +      spellChecker.excludeMarker);
    +  assertArrayEquals(['DIV', undefined, 'SPAN'],
    +      spellChecker.excludeTags);
    +  el.innerHTML = '<div class="goog-quote">misspelling</div>' +
    +      '<div class="goog-yes">misspelling</div>' +
    +      '<div class="goog-note">misspelling</div>' +
    +      '<div class="goog-comment">misspelling</div>' +
    +      '<span>misspelling<span>';
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  assertEquals(3, spellChecker.getLastIndex());
    +}
    +
    +function testBiggerDocument() {
    +  var el = document.getElementById('test2');
    +  spellChecker.decorate(el);
    +  el.appendChild(document.createTextNode(TEST_TEXT3));
    +  var el2 = el.cloneNode(true);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  spellChecker.resume();
    +  waitForSpellCheckToFinish();
    +
    +  assertEquals('Spell checker run should not change the underlying element.',
    +               el2.innerHTML, el.innerHTML);
    +}
    +
    +function testElementOverflow() {
    +  var el = document.getElementById('test3');
    +  spellChecker.decorate(el);
    +  el.appendChild(document.createTextNode(TEST_TEXT3));
    +
    +  var el2 = el.cloneNode(true);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +  spellChecker.resume();
    +  waitForSpellCheckToFinish();
    +
    +  assertEquals('Spell checker run should not change the underlying element.',
    +               el2.innerHTML, el.innerHTML);
    +}
    +
    +function testKeyboardNavigateNext() {
    +  var el = document.getElementById('test4');
    +  spellChecker.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.appendChild(document.createTextNode(text));
    +  var keyEventProperties =
    +      goog.object.create('ctrlKey', true, 'shiftKey', false);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +
    +  // First call just moves focus to first misspelled word.
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving from first to second mispelled word.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(el,
    +      goog.events.KeyCodes.RIGHT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertCursorAtElement(spellChecker.makeElementId(2));
    +
    +  spellChecker.resume();
    +}
    +
    +function testKeyboardNavigateNextOnLastWord() {
    +  var el = document.getElementById('test5');
    +  spellChecker.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.appendChild(document.createTextNode(text));
    +  var keyEventProperties =
    +      goog.object.create('ctrlKey', true, 'shiftKey', false);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +
    +  // Move to the last invalid word.
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving to the next invalid word. Should have no effect.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(el,
    +      goog.events.KeyCodes.RIGHT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertCursorAtElement(spellChecker.makeElementId(3));
    +
    +  spellChecker.resume();
    +}
    +
    +function testKeyboardNavigateOpenSuggestions() {
    +  var el = document.getElementById('test6');
    +  spellChecker.decorate(el);
    +  var text = 'unit';
    +  el.appendChild(document.createTextNode(text));
    +  var keyEventProperties =
    +      goog.object.create('ctrlKey', true, 'shiftKey', false);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +
    +  var suggestionMenu = spellChecker.getMenu();
    +
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  assertFalse('The suggestion menu should not be visible yet.',
    +      suggestionMenu.isVisible());
    +
    +  keyEventProperties.ctrlKey = false;
    +  var defaultExecuted = goog.testing.events.fireKeySequence(el,
    +      goog.events.KeyCodes.DOWN, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertTrue('The suggestion menu should be visible after the key event.',
    +      suggestionMenu.isVisible());
    +
    +  spellChecker.resume();
    +}
    +
    +function testKeyboardNavigatePrevious() {
    +  var el = document.getElementById('test7');
    +  spellChecker.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.appendChild(document.createTextNode(text));
    +  var keyEventProperties =
    +      goog.object.create('ctrlKey', true, 'shiftKey', false);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +
    +  // Move to the third element, so we can test the move back to the second.
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  var defaultExecuted = goog.testing.events.fireKeySequence(el,
    +      goog.events.KeyCodes.LEFT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertCursorAtElement(spellChecker.makeElementId(2));
    +
    +  spellChecker.resume();
    +}
    +
    +function testKeyboardNavigatePreviousOnLastWord() {
    +  var el = document.getElementById('test8');
    +  spellChecker.decorate(el);
    +  var text = 'a unit test for keyboard test';
    +  el.appendChild(document.createTextNode(text));
    +  var keyEventProperties =
    +      goog.object.create('ctrlKey', true, 'shiftKey', false);
    +
    +  spellChecker.check();
    +  waitForSpellCheckToFinish();
    +
    +  // Move to the first invalid word.
    +  goog.testing.events.fireKeySequence(el, goog.events.KeyCodes.RIGHT,
    +      keyEventProperties);
    +
    +  // Test moving to the previous invalid word. Should have no effect.
    +  var defaultExecuted = goog.testing.events.fireKeySequence(el,
    +      goog.events.KeyCodes.LEFT, keyEventProperties);
    +
    +  assertFalse('The default action should be prevented for the key event',
    +      defaultExecuted);
    +  assertCursorAtElement(spellChecker.makeElementId(1));
    +
    +  spellChecker.resume();
    +}
    +
    +function assertCursorAtElement(expectedId) {
    +  var range = goog.dom.Range.createFromWindow();
    +
    +  if (isCaret(range)) {
    +    if (isMisspelledWordElement(range.getStartNode())) {
    +      var focusedElementId = range.getStartNode().id;
    +    }
    +
    +    // In Chrome a cursor at the start of a misspelled word will appear to be at
    +    // the end of the text node preceding it.
    +    if (isCursorAtEndOfStartNode(range) &&
    +        range.getStartNode().nextSibling != null &&
    +        isMisspelledWordElement(range.getStartNode().nextSibling)) {
    +      var focusedElementId = range.getStartNode().nextSibling.id;
    +    }
    +  }
    +
    +  assertEquals('The cursor is not at the expected misspelled word.',
    +      expectedId, focusedElementId);
    +}
    +
    +function isCaret(range) {
    +  return range.getStartNode() == range.getEndNode();
    +}
    +
    +function isMisspelledWordElement(element) {
    +  return goog.dom.classlist.contains(
    +      element, 'goog-spellcheck-word');
    +}
    +
    +function isCursorAtEndOfStartNode(range) {
    +  return range.getStartNode().length == range.getStartOffset();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/roundedpanel.js b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel.js
    new file mode 100644
    index 0000000000..cbddddbdaa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel.js
    @@ -0,0 +1,630 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class definition for a rounded corner panel.
    + * @supported IE 6.0+, Safari 2.0+, Firefox 1.5+, Opera 9.2+.
    + * @see ../demos/roundedpanel.html
    + */
    +
    +goog.provide('goog.ui.BaseRoundedPanel');
    +goog.provide('goog.ui.CssRoundedPanel');
    +goog.provide('goog.ui.GraphicsRoundedPanel');
    +goog.provide('goog.ui.RoundedPanel');
    +goog.provide('goog.ui.RoundedPanel.Corner');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.graphics');
    +goog.require('goog.graphics.Path');
    +goog.require('goog.graphics.SolidFill');
    +goog.require('goog.graphics.Stroke');
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Factory method that returns an instance of a BaseRoundedPanel.
    + * @param {number} radius The radius of the rounded corner(s), in pixels.
    + * @param {number} borderWidth The thickness of the border, in pixels.
    + * @param {string} borderColor The border color of the panel.
    + * @param {string=} opt_backgroundColor The background color of the panel.
    + * @param {number=} opt_corners The corners of the panel to be rounded. Any
    + *     corners not specified will be rendered as square corners. Will default
    + *     to all square corners if not specified.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @return {!goog.ui.BaseRoundedPanel} An instance of a
    + *     goog.ui.BaseRoundedPanel subclass.
    + */
    +goog.ui.RoundedPanel.create = function(radius,
    +                                       borderWidth,
    +                                       borderColor,
    +                                       opt_backgroundColor,
    +                                       opt_corners,
    +                                       opt_domHelper) {
    +  // This variable checks for the presence of Safari 3.0+ or Gecko 1.9+,
    +  // which can leverage special CSS styles to create rounded corners.
    +  var isCssReady = goog.userAgent.WEBKIT &&
    +      goog.userAgent.isVersionOrHigher('500') ||
    +      goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9a');
    +
    +  if (isCssReady) {
    +    // Safari 3.0+ and Firefox 3.0+ support this instance.
    +    return new goog.ui.CssRoundedPanel(
    +        radius,
    +        borderWidth,
    +        borderColor,
    +        opt_backgroundColor,
    +        opt_corners,
    +        opt_domHelper);
    +  } else {
    +    return new goog.ui.GraphicsRoundedPanel(
    +        radius,
    +        borderWidth,
    +        borderColor,
    +        opt_backgroundColor,
    +        opt_corners,
    +        opt_domHelper);
    +  }
    +};
    +
    +
    +/**
    + * Enum for specifying which corners to render.
    + * @enum {number}
    + */
    +goog.ui.RoundedPanel.Corner = {
    +  NONE: 0,
    +  BOTTOM_LEFT: 2,
    +  TOP_LEFT: 4,
    +  LEFT: 6, // BOTTOM_LEFT | TOP_LEFT
    +  TOP_RIGHT: 8,
    +  TOP: 12, // TOP_LEFT | TOP_RIGHT
    +  BOTTOM_RIGHT: 1,
    +  BOTTOM: 3, // BOTTOM_LEFT | BOTTOM_RIGHT
    +  RIGHT: 9, // TOP_RIGHT | BOTTOM_RIGHT
    +  ALL: 15 // TOP | BOTTOM
    +};
    +
    +
    +/**
    + * CSS class name suffixes for the elements comprising the RoundedPanel.
    + * @enum {string}
    + * @private
    + */
    +goog.ui.RoundedPanel.Classes_ = {
    +  BACKGROUND: goog.getCssName('goog-roundedpanel-background'),
    +  PANEL: goog.getCssName('goog-roundedpanel'),
    +  CONTENT: goog.getCssName('goog-roundedpanel-content')
    +};
    +
    +
    +
    +/**
    + * Base class for the hierarchy of RoundedPanel classes. Do not
    + * instantiate directly. Instead, call goog.ui.RoundedPanel.create().
    + * The HTML structure for the RoundedPanel is:
    + * <pre>
    + * - div (Contains the background and content. Class name: goog-roundedpanel)
    + *   - div (Contains the background/rounded corners. Class name:
    + *       goog-roundedpanel-bg)
    + *   - div (Contains the content. Class name: goog-roundedpanel-content)
    + * </pre>
    + * @param {number} radius The radius of the rounded corner(s), in pixels.
    + * @param {number} borderWidth The thickness of the border, in pixels.
    + * @param {string} borderColor The border color of the panel.
    + * @param {string=} opt_backgroundColor The background color of the panel.
    + * @param {number=} opt_corners The corners of the panel to be rounded. Any
    + *     corners not specified will be rendered as square corners. Will default
    + *     to all square corners if not specified.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.BaseRoundedPanel = function(radius,
    +                                    borderWidth,
    +                                    borderColor,
    +                                    opt_backgroundColor,
    +                                    opt_corners,
    +                                    opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The radius of the rounded corner(s), in pixels.
    +   * @type {number}
    +   * @private
    +   */
    +  this.radius_ = radius;
    +
    +  /**
    +   * The thickness of the border, in pixels.
    +   * @type {number}
    +   * @private
    +   */
    +  this.borderWidth_ = borderWidth;
    +
    +  /**
    +   * The border color of the panel.
    +   * @type {string}
    +   * @private
    +   */
    +  this.borderColor_ = borderColor;
    +
    +  /**
    +   * The background color of the panel.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.backgroundColor_ = opt_backgroundColor || null;
    +
    +  /**
    +   * The corners of the panel to be rounded; defaults to
    +   * goog.ui.RoundedPanel.Corner.NONE
    +   * @type {number}
    +   * @private
    +   */
    +  this.corners_ = opt_corners || goog.ui.RoundedPanel.Corner.NONE;
    +};
    +goog.inherits(goog.ui.BaseRoundedPanel, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.BaseRoundedPanel);
    +
    +
    +/**
    + * The element containing the rounded corners and background.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.BaseRoundedPanel.prototype.backgroundElement_;
    +
    +
    +/**
    + * The element containing the actual content.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.BaseRoundedPanel.prototype.contentElement_;
    +
    +
    +/**
    + * This method performs all the necessary DOM manipulation to create the panel.
    + * Overrides {@link goog.ui.Component#decorateInternal}.
    + * @param {Element} element The element to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.BaseRoundedPanel.prototype.decorateInternal = function(element) {
    +  goog.ui.BaseRoundedPanel.superClass_.decorateInternal.call(this, element);
    +  goog.dom.classlist.add(goog.asserts.assert(this.getElement()),
    +      goog.ui.RoundedPanel.Classes_.PANEL);
    +
    +  // Create backgroundElement_, and add it to the DOM.
    +  this.backgroundElement_ = this.getDomHelper().createElement('div');
    +  this.backgroundElement_.className = goog.ui.RoundedPanel.Classes_.BACKGROUND;
    +  this.getElement().appendChild(this.backgroundElement_);
    +
    +  // Set contentElement_ by finding a child node within element_ with the
    +  // proper class name. If none exists, create it and add it to the DOM.
    +  this.contentElement_ = goog.dom.getElementsByTagNameAndClass(
    +      null, goog.ui.RoundedPanel.Classes_.CONTENT, this.getElement())[0];
    +  if (!this.contentElement_) {
    +    this.contentElement_ = this.getDomHelper().createDom('div');
    +    this.contentElement_.className = goog.ui.RoundedPanel.Classes_.CONTENT;
    +    this.getElement().appendChild(this.contentElement_);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.BaseRoundedPanel.prototype.disposeInternal = function() {
    +  if (this.backgroundElement_) {
    +    this.getDomHelper().removeNode(this.backgroundElement_);
    +    this.backgroundElement_ = null;
    +  }
    +  this.contentElement_ = null;
    +  goog.ui.BaseRoundedPanel.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Returns the DOM element containing the actual content.
    + * @return {Element} The element containing the actual content (null if none).
    + * @override
    + */
    +goog.ui.BaseRoundedPanel.prototype.getContentElement = function() {
    +  return this.contentElement_;
    +};
    +
    +
    +
    +/**
    + * RoundedPanel class specifically for browsers that support CSS attributes
    + * for elements with rounded borders (ex. Safari 3.0+, Firefox 3.0+). Do not
    + * instantiate directly. Instead, call goog.ui.RoundedPanel.create().
    + * @param {number} radius The radius of the rounded corner(s), in pixels.
    + * @param {number} borderWidth The thickness of the border, in pixels.
    + * @param {string} borderColor The border color of the panel.
    + * @param {string=} opt_backgroundColor The background color of the panel.
    + * @param {number=} opt_corners The corners of the panel to be rounded. Any
    + *     corners not specified will be rendered as square corners. Will
    + *     default to all square corners if not specified.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @extends {goog.ui.BaseRoundedPanel}
    + * @constructor
    + * @final
    + */
    +goog.ui.CssRoundedPanel = function(radius,
    +                                   borderWidth,
    +                                   borderColor,
    +                                   opt_backgroundColor,
    +                                   opt_corners,
    +                                   opt_domHelper) {
    +  goog.ui.BaseRoundedPanel.call(this,
    +                                radius,
    +                                borderWidth,
    +                                borderColor,
    +                                opt_backgroundColor,
    +                                opt_corners,
    +                                opt_domHelper);
    +};
    +goog.inherits(goog.ui.CssRoundedPanel, goog.ui.BaseRoundedPanel);
    +
    +
    +/**
    + * This method performs all the necessary DOM manipulation to create the panel.
    + * Overrides {@link goog.ui.Component#decorateInternal}.
    + * @param {Element} element The element to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.CssRoundedPanel.prototype.decorateInternal = function(element) {
    +  goog.ui.CssRoundedPanel.superClass_.decorateInternal.call(this, element);
    +
    +  // Set the border width and background color, if needed.
    +  this.backgroundElement_.style.border = this.borderWidth_ +
    +      'px solid ' +
    +      this.borderColor_;
    +  if (this.backgroundColor_) {
    +    this.backgroundElement_.style.backgroundColor = this.backgroundColor_;
    +  }
    +
    +  // Set radii of the appropriate rounded corners.
    +  if (this.corners_ == goog.ui.RoundedPanel.Corner.ALL) {
    +    var styleName = this.getStyle_(goog.ui.RoundedPanel.Corner.ALL);
    +    this.backgroundElement_.style[styleName] = this.radius_ + 'px';
    +  } else {
    +    var topLeftRadius =
    +        this.corners_ & goog.ui.RoundedPanel.Corner.TOP_LEFT ?
    +        this.radius_ :
    +        0;
    +    var cornerStyle = this.getStyle_(goog.ui.RoundedPanel.Corner.TOP_LEFT);
    +    this.backgroundElement_.style[cornerStyle] = topLeftRadius + 'px';
    +    var topRightRadius =
    +        this.corners_ & goog.ui.RoundedPanel.Corner.TOP_RIGHT ?
    +        this.radius_ :
    +        0;
    +    cornerStyle = this.getStyle_(goog.ui.RoundedPanel.Corner.TOP_RIGHT);
    +    this.backgroundElement_.style[cornerStyle] = topRightRadius + 'px';
    +    var bottomRightRadius =
    +        this.corners_ & goog.ui.RoundedPanel.Corner.BOTTOM_RIGHT ?
    +        this.radius_ :
    +        0;
    +    cornerStyle = this.getStyle_(goog.ui.RoundedPanel.Corner.BOTTOM_RIGHT);
    +    this.backgroundElement_.style[cornerStyle] = bottomRightRadius + 'px';
    +    var bottomLeftRadius =
    +        this.corners_ & goog.ui.RoundedPanel.Corner.BOTTOM_LEFT ?
    +        this.radius_ :
    +        0;
    +    cornerStyle = this.getStyle_(goog.ui.RoundedPanel.Corner.BOTTOM_LEFT);
    +    this.backgroundElement_.style[cornerStyle] = bottomLeftRadius + 'px';
    +  }
    +};
    +
    +
    +/**
    + * This method returns the CSS style based on the corner of the panel, and the
    + * user-agent.
    + * @param {number} corner The corner whose style name to retrieve.
    + * @private
    + * @return {string} The CSS style based on the specified corner.
    + */
    +goog.ui.CssRoundedPanel.prototype.getStyle_ = function(corner) {
    +  // Determine the proper corner to work with.
    +  var cssCorner, suffixLeft, suffixRight;
    +  if (goog.userAgent.WEBKIT) {
    +    suffixLeft = 'Left';
    +    suffixRight = 'Right';
    +  } else {
    +    suffixLeft = 'left';
    +    suffixRight = 'right';
    +  }
    +  switch (corner) {
    +    case goog.ui.RoundedPanel.Corner.ALL:
    +      cssCorner = '';
    +      break;
    +    case goog.ui.RoundedPanel.Corner.TOP_LEFT:
    +      cssCorner = 'Top' + suffixLeft;
    +      break;
    +    case goog.ui.RoundedPanel.Corner.TOP_RIGHT:
    +      cssCorner = 'Top' + suffixRight;
    +      break;
    +    case goog.ui.RoundedPanel.Corner.BOTTOM_LEFT:
    +      cssCorner = 'Bottom' + suffixLeft;
    +      break;
    +    case goog.ui.RoundedPanel.Corner.BOTTOM_RIGHT:
    +      cssCorner = 'Bottom' + suffixRight;
    +      break;
    +  }
    +
    +  return goog.userAgent.WEBKIT ?
    +      'WebkitBorder' + cssCorner + 'Radius' :
    +      'MozBorderRadius' + cssCorner;
    +};
    +
    +
    +
    +/**
    + * RoundedPanel class that uses goog.graphics to create the rounded corners.
    + * Do not instantiate directly. Instead, call goog.ui.RoundedPanel.create().
    + * @param {number} radius The radius of the rounded corner(s), in pixels.
    + * @param {number} borderWidth The thickness of the border, in pixels.
    + * @param {string} borderColor The border color of the panel.
    + * @param {string=} opt_backgroundColor The background color of the panel.
    + * @param {number=} opt_corners The corners of the panel to be rounded. Any
    + *     corners not specified will be rendered as square corners. Will
    + *     default to all square corners if not specified.
    + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the
    + *     document we want to render in.
    + * @extends {goog.ui.BaseRoundedPanel}
    + * @constructor
    + * @final
    + */
    +goog.ui.GraphicsRoundedPanel = function(radius,
    +                                        borderWidth,
    +                                        borderColor,
    +                                        opt_backgroundColor,
    +                                        opt_corners,
    +                                        opt_domHelper) {
    +  goog.ui.BaseRoundedPanel.call(this,
    +                                radius,
    +                                borderWidth,
    +                                borderColor,
    +                                opt_backgroundColor,
    +                                opt_corners,
    +                                opt_domHelper);
    +};
    +goog.inherits(goog.ui.GraphicsRoundedPanel, goog.ui.BaseRoundedPanel);
    +
    +
    +/**
    + * A 4-element array containing the circle centers for the arcs in the
    + * bottom-left, top-left, top-right, and bottom-right corners, respectively.
    + * @type {Array<goog.math.Coordinate>}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.arcCenters_;
    +
    +
    +/**
    + * A 4-element array containing the start coordinates for rendering the arcs
    + * in the bottom-left, top-left, top-right, and bottom-right corners,
    + * respectively.
    + * @type {Array<goog.math.Coordinate>}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.cornerStarts_;
    +
    +
    +/**
    + * A 4-element array containing the arc end angles for the bottom-left,
    + * top-left, top-right, and bottom-right corners, respectively.
    + * @type {Array<number>}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.endAngles_;
    +
    +
    +/**
    + * Graphics object for rendering the background.
    + * @type {goog.graphics.AbstractGraphics}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.graphics_;
    +
    +
    +/**
    + * A 4-element array containing the rounded corner radii for the bottom-left,
    + * top-left, top-right, and bottom-right corners, respectively.
    + * @type {Array<number>}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.radii_;
    +
    +
    +/**
    + * A 4-element array containing the arc start angles for the bottom-left,
    + * top-left, top-right, and bottom-right corners, respectively.
    + * @type {Array<number>}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.startAngles_;
    +
    +
    +/**
    + * Thickness constant used as an offset to help determine where to start
    + * rendering.
    + * @type {number}
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.BORDER_WIDTH_FACTOR_ = 1 / 2;
    +
    +
    +/**
    + * This method performs all the necessary DOM manipulation to create the panel.
    + * Overrides {@link goog.ui.Component#decorateInternal}.
    + * @param {Element} element The element to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.decorateInternal =
    +    function(element) {
    +  goog.ui.GraphicsRoundedPanel.superClass_.decorateInternal.call(this,
    +                                                                 element);
    +
    +  // Calculate the points and angles for creating the rounded corners. Then
    +  // instantiate a Graphics object for drawing purposes.
    +  var elementSize = goog.style.getSize(this.getElement());
    +  this.calculateArcParameters_(elementSize);
    +  this.graphics_ = goog.graphics.createGraphics(
    +      /** @type {number} */ (elementSize.width),
    +      /** @type {number} */ (elementSize.height),
    +      /** @type {number} */ (elementSize.width),
    +      /** @type {number} */ (elementSize.height),
    +      this.getDomHelper());
    +  this.graphics_.createDom();
    +
    +  // Create the path, starting from the bottom-right corner, moving clockwise.
    +  // End with the top-right corner.
    +  var path = new goog.graphics.Path();
    +  for (var i = 0; i < 4; i++) {
    +    if (this.radii_[i]) {
    +      // If radius > 0, draw an arc, moving to the first point and drawing
    +      // a line to the others.
    +      var cx = this.arcCenters_[i].x;
    +      var cy = this.arcCenters_[i].y;
    +      var rx = this.radii_[i];
    +      var ry = rx;
    +      var fromAngle = this.startAngles_[i];
    +      var extent = this.endAngles_[i] - fromAngle;
    +      var startX = cx + goog.math.angleDx(fromAngle, rx);
    +      var startY = cy + goog.math.angleDy(fromAngle, ry);
    +      if (i > 0) {
    +        var currentPoint = path.getCurrentPoint();
    +        if (!currentPoint || startX != currentPoint[0] ||
    +            startY != currentPoint[1]) {
    +          path.lineTo(startX, startY);
    +        }
    +      } else {
    +        path.moveTo(startX, startY);
    +      }
    +      path.arcTo(rx, ry, fromAngle, extent);
    +    } else if (i == 0) {
    +      // If we're just starting out (ie. i == 0), move to the starting point.
    +      path.moveTo(this.cornerStarts_[i].x,
    +                  this.cornerStarts_[i].y);
    +    } else {
    +      // Otherwise, draw a line to the starting point.
    +      path.lineTo(this.cornerStarts_[i].x,
    +                  this.cornerStarts_[i].y);
    +    }
    +  }
    +
    +  // Close the path, create a stroke object, and fill the enclosed area, if
    +  // needed. Then render the path.
    +  path.close();
    +  var stroke = this.borderWidth_ ?
    +      new goog.graphics.Stroke(this.borderWidth_, this.borderColor_) :
    +      null;
    +  var fill = this.backgroundColor_ ?
    +      new goog.graphics.SolidFill(this.backgroundColor_, 1) :
    +      null;
    +  this.graphics_.drawPath(path, stroke, fill);
    +  this.graphics_.render(this.backgroundElement_);
    +};
    +
    +
    +/** @override */
    +goog.ui.GraphicsRoundedPanel.prototype.disposeInternal = function() {
    +  goog.ui.GraphicsRoundedPanel.superClass_.disposeInternal.call(this);
    +  this.graphics_.dispose();
    +  delete this.graphics_;
    +  delete this.radii_;
    +  delete this.cornerStarts_;
    +  delete this.arcCenters_;
    +  delete this.startAngles_;
    +  delete this.endAngles_;
    +};
    +
    +
    +/**
    + * Calculates the start coordinates, circle centers, and angles, for the rounded
    + * corners at each corner of the panel.
    + * @param {goog.math.Size} elementSize The size of element_.
    + * @private
    + */
    +goog.ui.GraphicsRoundedPanel.prototype.calculateArcParameters_ =
    +    function(elementSize) {
    +  // Initialize the arrays containing the key points and angles.
    +  this.radii_ = [];
    +  this.cornerStarts_ = [];
    +  this.arcCenters_ = [];
    +  this.startAngles_ = [];
    +  this.endAngles_ = [];
    +
    +  // Set the start points, circle centers, and angles for the bottom-right,
    +  // bottom-left, top-left and top-right corners, in that order.
    +  var angleInterval = 90;
    +  var borderWidthOffset = this.borderWidth_ *
    +      goog.ui.GraphicsRoundedPanel.BORDER_WIDTH_FACTOR_;
    +  var radius, xStart, yStart, xCenter, yCenter, startAngle, endAngle;
    +  for (var i = 0; i < 4; i++) {
    +    var corner = Math.pow(2, i);  // Determines which corner we're dealing with.
    +    var isLeft = corner & goog.ui.RoundedPanel.Corner.LEFT;
    +    var isTop = corner & goog.ui.RoundedPanel.Corner.TOP;
    +
    +    // Calculate the radius and the start coordinates.
    +    radius = corner & this.corners_ ? this.radius_ : 0;
    +    switch (corner) {
    +      case goog.ui.RoundedPanel.Corner.BOTTOM_LEFT:
    +        xStart = borderWidthOffset + radius;
    +        yStart = elementSize.height - borderWidthOffset;
    +        break;
    +      case goog.ui.RoundedPanel.Corner.TOP_LEFT:
    +        xStart = borderWidthOffset;
    +        yStart = radius + borderWidthOffset;
    +        break;
    +      case goog.ui.RoundedPanel.Corner.TOP_RIGHT:
    +        xStart = elementSize.width - radius - borderWidthOffset;
    +        yStart = borderWidthOffset;
    +        break;
    +      case goog.ui.RoundedPanel.Corner.BOTTOM_RIGHT:
    +        xStart = elementSize.width - borderWidthOffset;
    +        yStart = elementSize.height - radius - borderWidthOffset;
    +        break;
    +    }
    +
    +    // Calculate the circle centers and start/end angles.
    +    xCenter = isLeft ?
    +        radius + borderWidthOffset :
    +        elementSize.width - radius - borderWidthOffset;
    +    yCenter = isTop ?
    +        radius + borderWidthOffset :
    +        elementSize.height - radius - borderWidthOffset;
    +    startAngle = angleInterval * i;
    +    endAngle = startAngle + angleInterval;
    +
    +    // Append the radius, angles, and coordinates to their arrays.
    +    this.radii_[i] = radius;
    +    this.cornerStarts_[i] = new goog.math.Coordinate(xStart, yStart);
    +    this.arcCenters_[i] = new goog.math.Coordinate(xCenter, yCenter);
    +    this.startAngles_[i] = startAngle;
    +    this.endAngles_[i] = endAngle;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.html b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.html
    new file mode 100644
    index 0000000000..daa6980122
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  Unit test file for goog.ui.RoundedPanel component
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.RoundedPanel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.RoundedPanelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.js b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.js
    new file mode 100644
    index 0000000000..ae41547882
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/roundedpanel_test.js
    @@ -0,0 +1,54 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.RoundedPanelTest');
    +goog.setTestOnly('goog.ui.RoundedPanelTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.CssRoundedPanel');
    +goog.require('goog.ui.GraphicsRoundedPanel');
    +goog.require('goog.ui.RoundedPanel');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Tests goog.ui.RoundedPanel.create(), ensuring that the proper instance is
    + * created based on user-agent
    + */
    +function testRoundedPanelCreate() {
    +  var rcp = goog.ui.RoundedPanel.create(15,
    +                                        5,
    +                                        '#cccccc',
    +                                        '#cccccc',
    +                                        goog.ui.RoundedPanel.Corner.ALL);
    +
    +  if (goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9a')) {
    +    assertTrue('For Firefox 3.0+ (uses Gecko 1.9+), an instance of ' +
    +        'goog.ui.CssRoundedPanel should be returned.',
    +        rcp instanceof goog.ui.CssRoundedPanel);
    +  } else if (goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('500')) {
    +    assertTrue('For Safari 3.0+, an instance of goog.ui.CssRoundedPanel ' +
    +        'should be returned.', rcp instanceof goog.ui.CssRoundedPanel);
    +  } else if (goog.userAgent.GECKO ||
    +             goog.userAgent.IE ||
    +             goog.userAgent.OPERA ||
    +             goog.userAgent.WEBKIT) {
    +    assertTrue('For Gecko 1.8- (ex. Firefox 2.0-, Camino 1.5-, etc.), ' +
    +        'IE, Opera, and Safari 2.0-, an instance of ' +
    +        'goog.ui.GraphicsRoundedPanel should be returned.',
    +        rcp instanceof goog.ui.GraphicsRoundedPanel);
    +  } else {
    +    assertNull('For non-supported user-agents, null is returned.', rcp);
    +  }
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/roundedtabrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/roundedtabrenderer.js
    new file mode 100644
    index 0000000000..c270cd418b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/roundedtabrenderer.js
    @@ -0,0 +1,198 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Rounded corner tab renderer for {@link goog.ui.Tab}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.RoundedTabRenderer');
    +
    +goog.require('goog.dom');
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabBar');
    +goog.require('goog.ui.TabRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Rounded corner tab renderer for {@link goog.ui.Tab}s.
    + * @constructor
    + * @extends {goog.ui.TabRenderer}
    + * @final
    + */
    +goog.ui.RoundedTabRenderer = function() {
    +  goog.ui.TabRenderer.call(this);
    +};
    +goog.inherits(goog.ui.RoundedTabRenderer, goog.ui.TabRenderer);
    +goog.addSingletonGetter(goog.ui.RoundedTabRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.RoundedTabRenderer.CSS_CLASS = goog.getCssName('goog-rounded-tab');
    +
    +
    +/**
    + * Returns the CSS class name to be applied to the root element of all tabs
    + * rendered or decorated using this renderer.
    + * @return {string} Renderer-specific CSS class name.
    + * @override
    + */
    +goog.ui.RoundedTabRenderer.prototype.getCssClass = function() {
    +  return goog.ui.RoundedTabRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Creates the tab's DOM structure, based on the containing tab bar's location
    + * relative to tab contents.  For example, the DOM for a tab in a tab bar
    + * located above tab contents would look like this:
    + * <pre>
    + *   <div class="goog-rounded-tab" title="...">
    + *     <table class="goog-rounded-tab-table">
    + *       <tbody>
    + *         <tr>
    + *           <td nowrap>
    + *             <div class="goog-rounded-tab-outer-edge"></div>
    + *             <div class="goog-rounded-tab-inner-edge"></div>
    + *           </td>
    + *         </tr>
    + *         <tr>
    + *           <td nowrap>
    + *             <div class="goog-rounded-tab-caption">Hello, world</div>
    + *           </td>
    + *         </tr>
    + *       </tbody>
    + *     </table>
    + *   </div>
    + * </pre>
    + * @param {goog.ui.Control} tab Tab to render.
    + * @return {Element} Root element for the tab.
    + * @override
    + */
    +goog.ui.RoundedTabRenderer.prototype.createDom = function(tab) {
    +  return this.decorate(tab,
    +      goog.ui.RoundedTabRenderer.superClass_.createDom.call(this, tab));
    +};
    +
    +
    +/**
    + * Decorates the element with the tab.  Overrides the superclass implementation
    + * by wrapping the tab's content in a table that implements rounded corners.
    + * @param {goog.ui.Control} tab Tab to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.RoundedTabRenderer.prototype.decorate = function(tab, element) {
    +  var tabBar = tab.getParent();
    +
    +  if (!this.getContentElement(element)) {
    +    // The element to be decorated doesn't appear to have the full tab DOM,
    +    // so we have to create it.
    +    element.appendChild(this.createTab(tab.getDomHelper(), element.childNodes,
    +        tabBar.getLocation()));
    +  }
    +
    +  return goog.ui.RoundedTabRenderer.superClass_.decorate.call(this, tab,
    +      element);
    +};
    +
    +
    +/**
    + * Creates a table implementing a rounded corner tab.
    + * @param {goog.dom.DomHelper} dom DOM helper to use for element construction.
    + * @param {goog.ui.ControlContent} caption Text caption or DOM structure
    + *     to display as the tab's caption.
    + * @param {goog.ui.TabBar.Location} location Tab bar location relative to the
    + *     tab contents.
    + * @return {!Element} Table implementing a rounded corner tab.
    + * @protected
    + */
    +goog.ui.RoundedTabRenderer.prototype.createTab = function(dom, caption,
    +    location) {
    +  var rows = [];
    +
    +  if (location != goog.ui.TabBar.Location.BOTTOM) {
    +    // This is a left, right, or top tab, so it needs a rounded top edge.
    +    rows.push(this.createEdge(dom, /* isTopEdge */ true));
    +  }
    +  rows.push(this.createCaption(dom, caption));
    +  if (location != goog.ui.TabBar.Location.TOP) {
    +    // This is a left, right, or bottom tab, so it needs a rounded bottom edge.
    +    rows.push(this.createEdge(dom, /* isTopEdge */ false));
    +  }
    +
    +  return dom.createDom('table', {
    +    'cellPadding': 0,
    +    'cellSpacing': 0,
    +    'className': goog.getCssName(this.getStructuralCssClass(), 'table')
    +  }, dom.createDom('tbody', null, rows));
    +};
    +
    +
    +/**
    + * Creates a table row implementing the tab caption.
    + * @param {goog.dom.DomHelper} dom DOM helper to use for element construction.
    + * @param {goog.ui.ControlContent} caption Text caption or DOM structure
    + *     to display as the tab's caption.
    + * @return {!Element} Tab caption table row.
    + * @protected
    + */
    +goog.ui.RoundedTabRenderer.prototype.createCaption = function(dom, caption) {
    +  var baseClass = this.getStructuralCssClass();
    +  return dom.createDom('tr', null,
    +      dom.createDom('td', {'noWrap': true},
    +          dom.createDom('div', goog.getCssName(baseClass, 'caption'),
    +              caption)));
    +};
    +
    +
    +/**
    + * Creates a table row implementing a rounded tab edge.
    + * @param {goog.dom.DomHelper} dom DOM helper to use for element construction.
    + * @param {boolean} isTopEdge Whether to create a top or bottom edge.
    + * @return {!Element} Rounded tab edge table row.
    + * @protected
    + */
    +goog.ui.RoundedTabRenderer.prototype.createEdge = function(dom, isTopEdge) {
    +  var baseClass = this.getStructuralCssClass();
    +  var inner = dom.createDom('div', goog.getCssName(baseClass, 'inner-edge'));
    +  var outer = dom.createDom('div', goog.getCssName(baseClass, 'outer-edge'));
    +  return dom.createDom('tr', null,
    +      dom.createDom('td', {'noWrap': true},
    +          isTopEdge ? [outer, inner] : [inner, outer]));
    +};
    +
    +
    +/** @override */
    +goog.ui.RoundedTabRenderer.prototype.getContentElement = function(element) {
    +  var baseClass = this.getStructuralCssClass();
    +  return element && goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.getCssName(baseClass, 'caption'), element)[0];
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Tabs using the rounded
    +// tab renderer.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.RoundedTabRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Tab(null, goog.ui.RoundedTabRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/scrollfloater.js b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater.js
    new file mode 100644
    index 0000000000..3e7cafa417
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater.js
    @@ -0,0 +1,636 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview  Class for making an element detach and float to remain visible
    + * even when the viewport has been scrolled.
    + * <p>
    + * The element remains at its normal position in the layout until scrolling
    + * would cause its top edge to scroll off the top of the viewport; at that
    + * point, the element is replaced with an invisible placeholder (to keep the
    + * layout stable), reattached in the dom tree to a new parent (the body element
    + * by default), and set to "fixed" positioning (emulated for IE < 7) so that it
    + * remains at its original X position while staying fixed to the top of the
    + * viewport in the Y dimension.
    + * <p>
    + * When the window is scrolled up past the point where the original element
    + * would be fully visible again, the element snaps back into place, replacing
    + * the placeholder.
    + *
    + * @see ../demos/scrollfloater.html
    + *
    + * Adapted from http://go/elementfloater.js
    + */
    +
    +
    +goog.provide('goog.ui.ScrollFloater');
    +goog.provide('goog.ui.ScrollFloater.EventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * Creates a ScrollFloater; see file overview for details.
    + *
    + * @param {Element=} opt_parentElement Where to attach the element when it's
    + *     floating.  Default is the document body.  If the floating element
    + *     contains form inputs, it will be necessary to attach it to the
    + *     corresponding form element, or to an element in the DOM subtree under
    + *     the form element.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.ScrollFloater = function(opt_parentElement, opt_domHelper) {
    +  // If a parentElement is supplied, we want to use its domHelper,
    +  // ignoring the caller-supplied one.
    +  var domHelper = opt_parentElement ?
    +      goog.dom.getDomHelper(opt_parentElement) : opt_domHelper;
    +
    +  goog.ui.ScrollFloater.base(this, 'constructor', domHelper);
    +
    +  /**
    +   * The element to which the scroll-floated element will be attached
    +   * when it is floating.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.parentElement_ =
    +      opt_parentElement || this.getDomHelper().getDocument().body;
    +
    +  /**
    +   * The original styles applied to the element before it began floating;
    +   * used to restore those styles when the element stops floating.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.originalStyles_ = {};
    +
    +  /**
    +   * A vertical offset from which to start floating the element.  This is
    +   * useful in cases when there are 'position:fixed' elements covering up
    +   * part of the viewport.
    +   * @type {number}
    +   * @private
    +   */
    +  this.viewportTopOffset_ = 0;
    +
    +  /**
    +   * An element used to define the boundaries within which the floater can
    +   * be positioned.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.containerElement_ = null;
    +
    +  /**
    +   * Container element's bounding rectangle.
    +   * @type {goog.math.Rect}
    +   * @private
    +   */
    +  this.containerBounds_ = null;
    +
    +  /**
    +   * Element's original bounding rectangle.
    +   * @type {goog.math.Rect}
    +   * @private
    +   */
    +  this.originalBounds_ = null;
    +
    +  /**
    +   * Element's top offset when it's not floated or pinned.
    +   * @type {number}
    +   * @private
    +   */
    +  this.originalTopOffset_ = 0;
    +
    +  /**
    +   * The placeholder element dropped in to hold the layout for
    +   * the floated element.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.placeholder_ = null;
    +
    +  /**
    +   * Whether scrolling is enabled for this element; true by default.
    +   * The {@link #setScrollingEnabled} method can be used to change this value.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.scrollingEnabled_ = true;
    +
    +  /**
    +   * A flag indicating whether this instance is currently pinned to the bottom
    +   * of the container element.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.pinned_ = false;
    +
    +  /**
    +   * A flag indicating whether this instance is currently floating.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.floating_ = false;
    +};
    +goog.inherits(goog.ui.ScrollFloater, goog.ui.Component);
    +
    +
    +/**
    + * Events dispatched by this component.
    + * @enum {string}
    + */
    +goog.ui.ScrollFloater.EventType = {
    +  /**
    +   * Dispatched when the component starts floating. The event is
    +   * cancellable.
    +   */
    +  FLOAT: 'float',
    +
    +  /**
    +   * Dispatched when the component returns to its original state.
    +   * The event is cancellable.
    +   */
    +  DOCK: 'dock',
    +
    +  /**
    +   * Dispatched when the component gets pinned to the bottom of the
    +   * container element.  This event is cancellable.
    +   */
    +  PIN: 'pin'
    +};
    +
    +
    +/**
    + * The element can float at different positions on the page.
    + * @enum {number}
    + * @private
    + */
    +goog.ui.ScrollFloater.FloatMode_ = {
    +  TOP: 0,
    +  BOTTOM: 1
    +};
    +
    +
    +/**
    + * The style properties which are stored when we float an element, so they
    + * can be restored when it 'docks' again.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.ui.ScrollFloater.STORED_STYLE_PROPS_ = [
    +  'position', 'top', 'left', 'width', 'cssFloat'];
    +
    +
    +/**
    + * The style elements managed for the placeholder object.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.ui.ScrollFloater.PLACEHOLDER_STYLE_PROPS_ = [
    +  'position', 'top', 'left', 'display', 'cssFloat',
    +  'marginTop', 'marginLeft', 'marginRight', 'marginBottom'];
    +
    +
    +/**
    + * The class name applied to the floating element.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ScrollFloater.CSS_CLASS_ = goog.getCssName('goog-scrollfloater');
    +
    +
    +/**
    + * Delegates dom creation to superclass, then constructs and
    + * decorates required DOM elements.
    + * @override
    + */
    +goog.ui.ScrollFloater.prototype.createDom = function() {
    +  goog.ui.ScrollFloater.base(this, 'createDom');
    +
    +  this.decorateInternal(this.getElement());
    +};
    +
    +
    +/**
    + * Decorates the floated element with the standard ScrollFloater CSS class.
    + * @param {Element} element The element to decorate.
    + * @override
    + */
    +goog.ui.ScrollFloater.prototype.decorateInternal = function(element) {
    +  goog.ui.ScrollFloater.base(this, 'decorateInternal', element);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.ScrollFloater.CSS_CLASS_);
    +};
    +
    +
    +/** @override */
    +goog.ui.ScrollFloater.prototype.enterDocument = function() {
    +  goog.ui.ScrollFloater.base(this, 'enterDocument');
    +
    +  if (!this.placeholder_) {
    +    this.placeholder_ =
    +        this.getDomHelper().createDom('div', {'style': 'visibility:hidden'});
    +  }
    +
    +  this.update();
    +
    +  this.setScrollingEnabled(this.scrollingEnabled_);
    +  var win = this.getDomHelper().getWindow();
    +  this.getHandler().
    +      listen(win, goog.events.EventType.SCROLL, this.handleScroll_).
    +      listen(win, goog.events.EventType.RESIZE, this.update);
    +};
    +
    +
    +/**
    + * Forces the component to update the cached element positions and sizes and
    + * to re-evaluate whether the the component should be docked, floated or
    + * pinned.
    + */
    +goog.ui.ScrollFloater.prototype.update = function() {
    +  if (!this.isInDocument()) {
    +    return;
    +  }
    +
    +  // These values can only be calculated when the element is in its original
    +  // state, so we dock first, and then re-evaluate.
    +  this.dock_();
    +  if (this.containerElement_) {
    +    this.containerBounds_ = goog.style.getBounds(this.containerElement_);
    +  }
    +  this.originalBounds_ = goog.style.getBounds(this.getElement());
    +  this.originalTopOffset_ = goog.style.getPageOffset(this.getElement()).y;
    +  this.handleScroll_();
    +};
    +
    +
    +/** @override */
    +goog.ui.ScrollFloater.prototype.disposeInternal = function() {
    +  goog.ui.ScrollFloater.base(this, 'disposeInternal');
    +
    +  this.placeholder_ = null;
    +};
    +
    +
    +/**
    + * Sets whether the element should be floated if it scrolls out of view.
    + * @param {boolean} enable Whether floating is enabled for this element.
    + */
    +goog.ui.ScrollFloater.prototype.setScrollingEnabled = function(enable) {
    +  this.scrollingEnabled_ = enable;
    +
    +  if (enable) {
    +    this.applyIeBgHack_();
    +    this.handleScroll_();
    +  } else {
    +    this.dock_();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the component is enabled for scroll-floating.
    + */
    +goog.ui.ScrollFloater.prototype.isScrollingEnabled = function() {
    +  return this.scrollingEnabled_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the component is currently scroll-floating.
    + */
    +goog.ui.ScrollFloater.prototype.isFloating = function() {
    +  return this.floating_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the component is currently pinned to the bottom
    + *     of the container.
    + */
    +goog.ui.ScrollFloater.prototype.isPinned = function() {
    +  return this.pinned_;
    +};
    +
    +
    +/**
    + * @param {number} offset A vertical offset from the top of the viewport, from
    + *    which to start floating the element. Default is 0. This is useful in cases
    + *    when there are 'position:fixed' elements covering up part of the viewport.
    + */
    +goog.ui.ScrollFloater.prototype.setViewportTopOffset = function(offset) {
    +  this.viewportTopOffset_ = offset;
    +  this.update();
    +};
    +
    +
    +/**
    + * @param {Element} container An element used to define the boundaries within
    + *     which the floater can be positioned. If not specified, scrolling the page
    + *     down far enough may result in the floated element extending past the
    + *     containing element as it is being scrolled out of the viewport. In some
    + *     cases, such as a list with a sticky header, this may be undesirable. If
    + *     the container element is specified and the floated element extends past
    + *     the bottom of the container, the element will be pinned to the bottom of
    + *     the container.
    + */
    +goog.ui.ScrollFloater.prototype.setContainerElement = function(container) {
    +  this.containerElement_ = container;
    +  this.update();
    +};
    +
    +
    +/**
    + * When a scroll event occurs, compares the element's position to the current
    + * document scroll position, and stops or starts floating behavior if needed.
    + * @param {goog.events.Event=} opt_e The event, which is ignored.
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.handleScroll_ = function(opt_e) {
    +  if (this.scrollingEnabled_) {
    +    var scrollTop = this.getDomHelper().getDocumentScroll().y;
    +
    +    if (this.originalBounds_.top - scrollTop >= this.viewportTopOffset_) {
    +      this.dock_();
    +      return;
    +    }
    +
    +    var effectiveElementHeight = this.originalBounds_.height +
    +        this.viewportTopOffset_;
    +
    +    // If the element extends past the container, we need to pin it instead.
    +    if (this.containerElement_) {
    +      var containerBottom = this.containerBounds_.top +
    +          this.containerBounds_.height;
    +
    +      if (scrollTop > containerBottom - effectiveElementHeight) {
    +        this.pin_();
    +        return;
    +      }
    +    }
    +
    +    var windowHeight = this.getDomHelper().getViewportSize().height;
    +
    +    // If the element is shorter than the window or the user uses IE < 7,
    +    // float it at the top.
    +    if (this.needsIePositionHack_() || effectiveElementHeight < windowHeight) {
    +      this.float_(goog.ui.ScrollFloater.FloatMode_.TOP);
    +      return;
    +    }
    +
    +    // If the element is taller than the window and is extending past the
    +    // bottom, allow it scroll with the page until the bottom of the element is
    +    // fully visible.
    +    if (this.originalBounds_.height + this.originalTopOffset_ >
    +        windowHeight + scrollTop) {
    +      this.dock_();
    +    } else {
    +      // Pin the element to the bottom of the page since the user has scrolled
    +      // past it.
    +      this.float_(goog.ui.ScrollFloater.FloatMode_.BOTTOM);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Pins the element to the bottom of the container, making as much of the
    + * element visible as possible without extending past it.
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.pin_ = function() {
    +  if (this.floating_ && !this.dock_()) {
    +    return;
    +  }
    +
    +  // Ignore if the component is pinned or the PIN event is cancelled.
    +  if (this.pinned_ ||
    +      !this.dispatchEvent(goog.ui.ScrollFloater.EventType.PIN)) {
    +    return;
    +  }
    +
    +  var elem = this.getElement();
    +
    +  this.storeOriginalStyles_();
    +
    +  elem.style.position = 'relative';
    +  elem.style.top = this.containerBounds_.height - this.originalBounds_.height -
    +      this.originalBounds_.top + this.containerBounds_.top + 'px';
    +
    +  this.pinned_ = true;
    +};
    +
    +
    +/**
    + * Begins floating behavior, making the element position:fixed (or IE hacked
    + * equivalent) and inserting a placeholder where it used to be to keep the
    + * layout from shifting around. For IE < 7 users, we only support floating at
    + * the top.
    + * @param {goog.ui.ScrollFloater.FloatMode_} floatMode The position at which we
    + *     should float.
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.float_ = function(floatMode) {
    +  var isTop = floatMode == goog.ui.ScrollFloater.FloatMode_.TOP;
    +  if (this.pinned_ && !this.dock_()) {
    +    return;
    +  }
    +
    +  // Ignore if the component is floating or the FLOAT event is cancelled.
    +  if (this.floating_ ||
    +      !this.dispatchEvent(goog.ui.ScrollFloater.EventType.FLOAT)) {
    +    return;
    +  }
    +
    +  var elem = this.getElement();
    +  var doc = this.getDomHelper().getDocument();
    +
    +  // Read properties of element before modifying it.
    +  var originalLeft_ = goog.style.getPageOffsetLeft(elem);
    +  var originalWidth_ = goog.style.getContentBoxSize(elem).width;
    +
    +  this.storeOriginalStyles_();
    +
    +  goog.style.setSize(this.placeholder_, elem.offsetWidth, elem.offsetHeight);
    +
    +  // Make element float.
    +  goog.style.setStyle(elem, {
    +    'left': originalLeft_ + 'px',
    +    'width': originalWidth_ + 'px',
    +    'cssFloat': 'none'
    +  });
    +
    +  // If parents are the same, avoid detaching and reattaching elem.
    +  // This prevents Flash embeds from being reloaded, for example.
    +  if (elem.parentNode == this.parentElement_) {
    +    elem.parentNode.insertBefore(this.placeholder_, elem);
    +  } else {
    +    elem.parentNode.replaceChild(this.placeholder_, elem);
    +    this.parentElement_.appendChild(elem);
    +  }
    +
    +  // Versions of IE below 7-in-standards-mode don't handle 'position: fixed',
    +  // so we must emulate it using an IE-specific idiom for JS-based calculated
    +  // style values. These users will only ever float at the top (bottom floating
    +  // not supported.) Also checked in handleScroll_.
    +  if (this.needsIePositionHack_()) {
    +    elem.style.position = 'absolute';
    +    elem.style.setExpression('top',
    +        'document.compatMode=="CSS1Compat"?' +
    +        'documentElement.scrollTop:document.body.scrollTop');
    +  } else {
    +    elem.style.position = 'fixed';
    +    if (isTop) {
    +      elem.style.top = this.viewportTopOffset_ + 'px';
    +      elem.style.bottom = 'auto';
    +    } else {
    +      elem.style.top = 'auto';
    +      elem.style.bottom = 0;
    +    }
    +  }
    +
    +  this.floating_ = true;
    +};
    +
    +
    +/**
    + * Stops floating behavior, returning element to its original state.
    + * @return {boolean} True if the the element has been docked.  False if the
    + *     element is already docked or the event was cancelled.
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.dock_ = function() {
    +  // Ignore if the component is docked or the DOCK event is cancelled.
    +  if (!(this.floating_ || this.pinned_) ||
    +      !this.dispatchEvent(goog.ui.ScrollFloater.EventType.DOCK)) {
    +    return false;
    +  }
    +
    +  var elem = this.getElement();
    +
    +  if (this.floating_) {
    +    this.restoreOriginalStyles_();
    +
    +    if (this.needsIePositionHack_()) {
    +      elem.style.removeExpression('top');
    +    }
    +
    +    // If placeholder_ was inserted and didn't replace elem then elem has
    +    // the right parent already, no need to replace (which removes elem before
    +    // inserting it).
    +    if (this.placeholder_.parentNode == this.parentElement_) {
    +      this.placeholder_.parentNode.removeChild(this.placeholder_);
    +    } else {
    +      this.placeholder_.parentNode.replaceChild(elem, this.placeholder_);
    +    }
    +  }
    +
    +  if (this.pinned_) {
    +    this.restoreOriginalStyles_();
    +  }
    +
    +  this.floating_ = this.pinned_ = false;
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.storeOriginalStyles_ = function() {
    +  var elem = this.getElement();
    +  this.originalStyles_ = {};
    +
    +  // Store styles while not floating so we can restore them when the
    +  // element stops floating.
    +  goog.array.forEach(goog.ui.ScrollFloater.STORED_STYLE_PROPS_,
    +                     function(property) {
    +                       this.originalStyles_[property] = elem.style[property];
    +                     },
    +                     this);
    +
    +  // Copy relevant styles to placeholder so it will be layed out the same
    +  // as the element that's about to be floated.
    +  goog.array.forEach(goog.ui.ScrollFloater.PLACEHOLDER_STYLE_PROPS_,
    +                     function(property) {
    +                       this.placeholder_.style[property] =
    +                           elem.style[property] ||
    +                               goog.style.getCascadedStyle(elem, property) ||
    +                               goog.style.getComputedStyle(elem, property);
    +                     },
    +                     this);
    +};
    +
    +
    +/**
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.restoreOriginalStyles_ = function() {
    +  var elem = this.getElement();
    +  for (var prop in this.originalStyles_) {
    +    elem.style[prop] = this.originalStyles_[prop];
    +  }
    +};
    +
    +
    +/**
    + * Determines whether we need to apply the position hack to emulated position:
    + * fixed on this browser.
    + * @return {boolean} Whether the current browser needs the position hack.
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.needsIePositionHack_ = function() {
    +  return goog.userAgent.IE &&
    +      !(goog.userAgent.isVersionOrHigher('7') &&
    +          this.getDomHelper().isCss1CompatMode());
    +};
    +
    +
    +/**
    + * Sets some magic CSS properties that make float-scrolling work smoothly
    + * in IE6 (and IE7 in quirks mode). Without this hack, the floating element
    + * will appear jumpy when you scroll the document. This involves modifying
    + * the background of the HTML element (or BODY in quirks mode). If there's
    + * already a background image in use this is not required.
    + * For further reading, see
    + * http://annevankesteren.nl/2005/01/position-fixed-in-ie
    + * @private
    + */
    +goog.ui.ScrollFloater.prototype.applyIeBgHack_ = function() {
    +  if (this.needsIePositionHack_()) {
    +    var doc = this.getDomHelper().getDocument();
    +    var topLevelElement = goog.style.getClientViewportElement(doc);
    +
    +    if (topLevelElement.currentStyle.backgroundImage == 'none') {
    +      // Using an https URL if the current windowbp is https avoids an IE
    +      // "This page contains a mix of secure and nonsecure items" warning.
    +      topLevelElement.style.backgroundImage =
    +          this.getDomHelper().getWindow().location.protocol == 'https:' ?
    +              'url(https:///)' : 'url(about:blank)';
    +      topLevelElement.style.backgroundAttachment = 'fixed';
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.html b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.html
    new file mode 100644
    index 0000000000..c87036f925
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.html
    @@ -0,0 +1,29 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.ScrollFloater
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ScrollFloaterTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="container">
    +   <div id="floater">
    +    Content to be scroll-floated.
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.js b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.js
    new file mode 100644
    index 0000000000..f1ff6b8431
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/scrollfloater_test.js
    @@ -0,0 +1,136 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ScrollFloaterTest');
    +goog.setTestOnly('goog.ui.ScrollFloaterTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.style');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ScrollFloater');
    +
    +function testScrollFloater() {
    +  var scrollFloater = new goog.ui.ScrollFloater();
    +  var floater = goog.dom.getElement('floater');
    +  scrollFloater.decorate(floater);
    +
    +  assertTrue('Default state is enabled', scrollFloater.isScrollingEnabled());
    +  assertFalse('On unscrolled page should not be floating',
    +              scrollFloater.isFloating());
    +
    +  scrollFloater.setScrollingEnabled(false);
    +
    +  assertFalse('We can disable the floater',
    +              scrollFloater.isScrollingEnabled());
    +  scrollFloater.dispose();
    +}
    +
    +function testScrollFloaterEvents() {
    +  var scrollFloater = new goog.ui.ScrollFloater();
    +  var floater = goog.dom.getElement('floater');
    +  scrollFloater.setContainerElement(goog.dom.getElement('container'));
    +  scrollFloater.decorate(floater);
    +
    +  var floatWasCalled = false;
    +  var callRecorder = function() { floatWasCalled = true; };
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.FLOAT, callRecorder);
    +  scrollFloater.float_(goog.ui.ScrollFloater.FloatMode_.TOP);
    +  assertTrue('FLOAT event was called', floatWasCalled);
    +  assertTrue('Should be floating', scrollFloater.isFloating());
    +  assertFalse('Should not be pinned', scrollFloater.isPinned());
    +
    +  var dockWasCalled = false;
    +  callRecorder = function() { dockWasCalled = true; };
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.DOCK, callRecorder);
    +  scrollFloater.dock_();
    +  assertTrue('DOCK event was called', dockWasCalled);
    +  assertFalse('Should not be floating', scrollFloater.isFloating());
    +  assertFalse('Should not be pinned', scrollFloater.isPinned());
    +
    +  var pinWasCalled = false;
    +  callRecorder = function() { pinWasCalled = true; };
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.PIN, callRecorder);
    +  scrollFloater.pin_();
    +  assertTrue('PIN event was called', pinWasCalled);
    +  assertFalse('Should not be floating', scrollFloater.isFloating());
    +  assertTrue('Should be pinned', scrollFloater.isPinned());
    +
    +  scrollFloater.dispose();
    +}
    +
    +function testScrollFloaterEventCancellation() {
    +  var scrollFloater = new goog.ui.ScrollFloater();
    +  var floater = goog.dom.getElement('floater');
    +  scrollFloater.decorate(floater);
    +
    +  // Event handler that returns false to cancel the event.
    +  var eventCanceller = function() { return false; };
    +
    +  // Have eventCanceller handle the FLOAT event and verify cancellation.
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.FLOAT, eventCanceller);
    +  scrollFloater.float_(goog.ui.ScrollFloater.FloatMode_.TOP);
    +  assertFalse('Should not be floating', scrollFloater.isFloating());
    +
    +  // Have eventCanceller handle the PIN event and verify cancellation.
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.PIN, eventCanceller);
    +  scrollFloater.dock_();
    +  assertFalse('Should not be pinned', scrollFloater.isPinned());
    +
    +  // Detach eventCanceller and enable floating.
    +  goog.events.unlisten(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.FLOAT, eventCanceller);
    +  scrollFloater.float_(goog.ui.ScrollFloater.FloatMode_.TOP);
    +
    +  // Have eventCanceller handle the DOCK event and verify cancellation.
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.DOCK, eventCanceller);
    +  scrollFloater.dock_();
    +  assertTrue('Should still be floating', scrollFloater.isFloating());
    +
    +  scrollFloater.dispose();
    +}
    +
    +function testScrollFloaterUpdateStyleOnFloatEvent() {
    +  var scrollFloater = new goog.ui.ScrollFloater();
    +  var floater = goog.dom.getElement('floater');
    +  scrollFloater.decorate(floater);
    +
    +  // Event handler that sets the font size of the scrollfloater to 20px.
    +  var updateStyle = function(e) {
    +    goog.style.setStyle(e.target.getElement(), 'font-size', '20px');
    +  };
    +
    +  // Set the current font size to 10px.
    +  goog.style.setStyle(scrollFloater.getElement(), 'font-size', '10px');
    +  goog.events.listen(
    +      scrollFloater, goog.ui.ScrollFloater.EventType.FLOAT, updateStyle);
    +  scrollFloater.float_(goog.ui.ScrollFloater.FloatMode_.BOTTOM);
    +
    +  // Ensure event handler got called and updated the font size.
    +  assertEquals('Font size should be 20px',
    +      '20px', goog.style.getStyle(scrollFloater.getElement(), 'font-size'));
    +
    +  assertEquals('Top should be auto',
    +      'auto', goog.style.getStyle(scrollFloater.getElement(), 'top'));
    +  assertEquals('Bottom should be 0px',
    +      0, parseInt(goog.style.getStyle(scrollFloater.getElement(), 'bottom')));
    +
    +  scrollFloater.dispose();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/select.js b/mapbox-gl/closure-library/closure/goog/ui/select.js
    new file mode 100644
    index 0000000000..6afa9e250b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/select.js
    @@ -0,0 +1,526 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class that supports single selection from a dropdown menu,
    + * with semantics similar to the native HTML <code>&lt;select&gt;</code>
    + * element.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/select.html
    + */
    +
    +goog.provide('goog.ui.Select');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.events.EventType');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.IdGenerator');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.MenuRenderer');
    +goog.require('goog.ui.SelectionModel');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A selection control.  Extends {@link goog.ui.MenuButton} by composing a
    + * menu with a selection model, and automatically updating the button's caption
    + * based on the current selection.
    + *
    + * Select fires the following events:
    + *   CHANGE - after selection changes.
    + *
    + * @param {goog.ui.ControlContent=} opt_caption Default caption or existing DOM
    + *     structure to display as the button's caption when nothing is selected.
    + *     Defaults to no caption.
    + * @param {goog.ui.Menu=} opt_menu Menu containing selection options.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
    + *     decorate the control; defaults to {@link goog.ui.MenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @param {!goog.ui.MenuRenderer=} opt_menuRenderer Renderer used to render or
    + *     decorate the menu; defaults to {@link goog.ui.MenuRenderer}.
    + * @constructor
    + * @extends {goog.ui.MenuButton}
    + */
    +goog.ui.Select = function(opt_caption, opt_menu, opt_renderer, opt_domHelper,
    +    opt_menuRenderer) {
    +  goog.ui.Select.base(this, 'constructor',
    +      opt_caption, opt_menu, opt_renderer, opt_domHelper,
    +      opt_menuRenderer ||
    +          new goog.ui.MenuRenderer(goog.a11y.aria.Role.LISTBOX));
    +  /**
    +   * Default caption to show when no option is selected.
    +   * @private {goog.ui.ControlContent}
    +   */
    +  this.defaultCaption_ = this.getContent();
    +
    +  /**
    +   * The initial value of the aria label of the content element. This will be
    +   * null until the caption is first populated and will be non-null thereafter.
    +   * @private {?string}
    +   */
    +  this.initialAriaLabel_ = null;
    +
    +  this.setPreferredAriaRole(goog.a11y.aria.Role.LISTBOX);
    +};
    +goog.inherits(goog.ui.Select, goog.ui.MenuButton);
    +goog.tagUnsealableClass(goog.ui.Select);
    +
    +
    +/**
    + * The selection model controlling the items in the menu.
    + * @type {goog.ui.SelectionModel}
    + * @private
    + */
    +goog.ui.Select.prototype.selectionModel_ = null;
    +
    +
    +/** @override */
    +goog.ui.Select.prototype.enterDocument = function() {
    +  goog.ui.Select.superClass_.enterDocument.call(this);
    +  this.updateCaption();
    +  this.listenToSelectionModelEvents_();
    +};
    +
    +
    +/**
    + * Decorates the given element with this control.  Overrides the superclass
    + * implementation by initializing the default caption on the select button.
    + * @param {Element} element Element to decorate.
    + * @override
    + */
    +goog.ui.Select.prototype.decorateInternal = function(element) {
    +  goog.ui.Select.superClass_.decorateInternal.call(this, element);
    +  var caption = this.getCaption();
    +  if (caption) {
    +    // Initialize the default caption.
    +    this.setDefaultCaption(caption);
    +  } else if (!this.getSelectedItem()) {
    +    // If there is no default caption and no selected item, select the first
    +    // option (this is technically an arbitrary choice, but what most people
    +    // would expect to happen).
    +    this.setSelectedIndex(0);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Select.prototype.disposeInternal = function() {
    +  goog.ui.Select.superClass_.disposeInternal.call(this);
    +
    +  if (this.selectionModel_) {
    +    this.selectionModel_.dispose();
    +    this.selectionModel_ = null;
    +  }
    +
    +  this.defaultCaption_ = null;
    +};
    +
    +
    +/**
    + * Handles {@link goog.ui.Component.EventType.ACTION} events dispatched by
    + * the menu item clicked by the user.  Updates the selection model, calls
    + * the superclass implementation to hide the menu, stops the propagation of
    + * the event, and dispatches an ACTION event on behalf of the select control
    + * itself.  Overrides {@link goog.ui.MenuButton#handleMenuAction}.
    + * @param {goog.events.Event} e Action event to handle.
    + * @override
    + */
    +goog.ui.Select.prototype.handleMenuAction = function(e) {
    +  this.setSelectedItem(/** @type {goog.ui.MenuItem} */ (e.target));
    +  goog.ui.Select.base(this, 'handleMenuAction', e);
    +
    +  // NOTE(chrishenry): We should not stop propagation and then fire
    +  // our own ACTION event. Fixing this without breaking anyone
    +  // relying on this event is hard though.
    +  e.stopPropagation();
    +  this.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +};
    +
    +
    +/**
    + * Handles {@link goog.events.EventType.SELECT} events raised by the
    + * selection model when the selection changes.  Updates the contents of the
    + * select button.
    + * @param {goog.events.Event} e Selection event to handle.
    + */
    +goog.ui.Select.prototype.handleSelectionChange = function(e) {
    +  var item = this.getSelectedItem();
    +  goog.ui.Select.superClass_.setValue.call(this, item && item.getValue());
    +  this.updateCaption();
    +};
    +
    +
    +/**
    + * Replaces the menu currently attached to the control (if any) with the given
    + * argument, and updates the selection model.  Does nothing if the new menu is
    + * the same as the old one.  Overrides {@link goog.ui.MenuButton#setMenu}.
    + * @param {goog.ui.Menu} menu New menu to be attached to the menu button.
    + * @return {goog.ui.Menu|undefined} Previous menu (undefined if none).
    + * @override
    + */
    +goog.ui.Select.prototype.setMenu = function(menu) {
    +  // Call superclass implementation to replace the menu.
    +  var oldMenu = goog.ui.Select.superClass_.setMenu.call(this, menu);
    +
    +  // Do nothing unless the new menu is different from the current one.
    +  if (menu != oldMenu) {
    +    // Clear the old selection model (if any).
    +    if (this.selectionModel_) {
    +      this.selectionModel_.clear();
    +    }
    +
    +    // Initialize new selection model (unless the new menu is null).
    +    if (menu) {
    +      if (this.selectionModel_) {
    +        menu.forEachChild(function(child, index) {
    +          this.setCorrectAriaRole_(
    +              /** @type {goog.ui.MenuItem|goog.ui.MenuSeparator} */ (child));
    +          this.selectionModel_.addItem(child);
    +        }, this);
    +      } else {
    +        this.createSelectionModel_(menu);
    +      }
    +    }
    +  }
    +
    +  return oldMenu;
    +};
    +
    +
    +/**
    + * Returns the default caption to be shown when no option is selected.
    + * @return {goog.ui.ControlContent} Default caption.
    + */
    +goog.ui.Select.prototype.getDefaultCaption = function() {
    +  return this.defaultCaption_;
    +};
    +
    +
    +/**
    + * Sets the default caption to the given string or DOM structure.
    + * @param {goog.ui.ControlContent} caption Default caption to be shown
    + *    when no option is selected.
    + */
    +goog.ui.Select.prototype.setDefaultCaption = function(caption) {
    +  this.defaultCaption_ = caption;
    +  this.updateCaption();
    +};
    +
    +
    +/**
    + * Adds a new menu item at the end of the menu.
    + * @param {goog.ui.Control} item Menu item to add to the menu.
    + * @override
    + */
    +goog.ui.Select.prototype.addItem = function(item) {
    +  this.setCorrectAriaRole_(
    +      /** @type {goog.ui.MenuItem|goog.ui.MenuSeparator} */ (item));
    +  goog.ui.Select.superClass_.addItem.call(this, item);
    +
    +  if (this.selectionModel_) {
    +    this.selectionModel_.addItem(item);
    +  } else {
    +    this.createSelectionModel_(this.getMenu());
    +  }
    +  this.updateAriaActiveDescendant_();
    +};
    +
    +
    +/**
    + * Adds a new menu item at a specific index in the menu.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu item to add to the
    + *     menu.
    + * @param {number} index Index at which to insert the menu item.
    + * @override
    + */
    +goog.ui.Select.prototype.addItemAt = function(item, index) {
    +  this.setCorrectAriaRole_(
    +      /** @type {goog.ui.MenuItem|goog.ui.MenuSeparator} */ (item));
    +  goog.ui.Select.superClass_.addItemAt.call(this, item, index);
    +
    +  if (this.selectionModel_) {
    +    this.selectionModel_.addItemAt(item, index);
    +  } else {
    +    this.createSelectionModel_(this.getMenu());
    +  }
    +};
    +
    +
    +/**
    + * Removes an item from the menu and disposes it.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator} item The menu item to remove.
    + * @override
    + */
    +goog.ui.Select.prototype.removeItem = function(item) {
    +  goog.ui.Select.superClass_.removeItem.call(this, item);
    +  if (this.selectionModel_) {
    +    this.selectionModel_.removeItem(item);
    +  }
    +};
    +
    +
    +/**
    + * Removes a menu item at a given index in the menu and disposes it.
    + * @param {number} index Index of item.
    + * @override
    + */
    +goog.ui.Select.prototype.removeItemAt = function(index) {
    +  goog.ui.Select.superClass_.removeItemAt.call(this, index);
    +  if (this.selectionModel_) {
    +    this.selectionModel_.removeItemAt(index);
    +  }
    +};
    +
    +
    +/**
    + * Selects the specified option (assumed to be in the select menu), and
    + * deselects the previously selected option, if any.  A null argument clears
    + * the selection.
    + * @param {goog.ui.MenuItem} item Option to be selected (null to clear
    + *     the selection).
    + */
    +goog.ui.Select.prototype.setSelectedItem = function(item) {
    +  if (this.selectionModel_) {
    +    var prevItem = this.getSelectedItem();
    +    this.selectionModel_.setSelectedItem(item);
    +
    +    if (item != prevItem) {
    +      this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Selects the option at the specified index, or clears the selection if the
    + * index is out of bounds.
    + * @param {number} index Index of the option to be selected.
    + */
    +goog.ui.Select.prototype.setSelectedIndex = function(index) {
    +  if (this.selectionModel_) {
    +    this.setSelectedItem(/** @type {goog.ui.MenuItem} */
    +        (this.selectionModel_.getItemAt(index)));
    +  }
    +};
    +
    +
    +/**
    + * Selects the first option found with an associated value equal to the
    + * argument, or clears the selection if no such option is found.  A null
    + * argument also clears the selection.  Overrides {@link
    + * goog.ui.Button#setValue}.
    + * @param {*} value Value of the option to be selected (null to clear
    + *     the selection).
    + * @override
    + */
    +goog.ui.Select.prototype.setValue = function(value) {
    +  if (goog.isDefAndNotNull(value) && this.selectionModel_) {
    +    for (var i = 0, item; item = this.selectionModel_.getItemAt(i); i++) {
    +      if (item && typeof item.getValue == 'function' &&
    +          item.getValue() == value) {
    +        this.setSelectedItem(/** @type {!goog.ui.MenuItem} */ (item));
    +        return;
    +      }
    +    }
    +  }
    +
    +  this.setSelectedItem(null);
    +};
    +
    +
    +/**
    + * Gets the value associated with the currently selected option (null if none).
    + *
    + * Note that unlike {@link goog.ui.Button#getValue} which this method overrides,
    + * the "value" of a Select instance is the value of its selected menu item, not
    + * its own value. This makes a difference because the "value" of a Button is
    + * reset to the value of the element it decorates when it's added to the DOM
    + * (via ButtonRenderer), whereas the value of the selected item is unaffected.
    + * So while setValue() has no effect on a Button before it is added to the DOM,
    + * it will make a persistent change to a Select instance (which is consistent
    + * with any changes made by {@link goog.ui.Select#setSelectedItem} and
    + * {@link goog.ui.Select#setSelectedIndex}).
    + *
    + * @override
    + */
    +goog.ui.Select.prototype.getValue = function() {
    +  var selectedItem = this.getSelectedItem();
    +  return selectedItem ? selectedItem.getValue() : null;
    +};
    +
    +
    +/**
    + * Returns the currently selected option.
    + * @return {goog.ui.MenuItem} The currently selected option (null if none).
    + */
    +goog.ui.Select.prototype.getSelectedItem = function() {
    +  return this.selectionModel_ ?
    +      /** @type {goog.ui.MenuItem} */ (this.selectionModel_.getSelectedItem()) :
    +      null;
    +};
    +
    +
    +/**
    + * Returns the index of the currently selected option.
    + * @return {number} 0-based index of the currently selected option (-1 if none).
    + */
    +goog.ui.Select.prototype.getSelectedIndex = function() {
    +  return this.selectionModel_ ? this.selectionModel_.getSelectedIndex() : -1;
    +};
    +
    +
    +/**
    + * @return {goog.ui.SelectionModel} The selection model.
    + * @protected
    + */
    +goog.ui.Select.prototype.getSelectionModel = function() {
    +  return this.selectionModel_;
    +};
    +
    +
    +/**
    + * Creates a new selection model and sets up an event listener to handle
    + * {@link goog.events.EventType.SELECT} events dispatched by it.
    + * @param {goog.ui.Component=} opt_component If provided, will add the
    + *     component's children as items to the selection model.
    + * @private
    + */
    +goog.ui.Select.prototype.createSelectionModel_ = function(opt_component) {
    +  this.selectionModel_ = new goog.ui.SelectionModel();
    +  if (opt_component) {
    +    opt_component.forEachChild(function(child, index) {
    +      this.setCorrectAriaRole_(
    +          /** @type {goog.ui.MenuItem|goog.ui.MenuSeparator} */ (child));
    +      this.selectionModel_.addItem(child);
    +    }, this);
    +  }
    +  this.listenToSelectionModelEvents_();
    +};
    +
    +
    +/**
    + * Subscribes to events dispatched by the selection model.
    + * @private
    + */
    +goog.ui.Select.prototype.listenToSelectionModelEvents_ = function() {
    +  if (this.selectionModel_) {
    +    this.getHandler().listen(this.selectionModel_, goog.events.EventType.SELECT,
    +        this.handleSelectionChange);
    +  }
    +};
    +
    +
    +/**
    + * Updates the caption to be shown in the select button.  If no option is
    + * selected and a default caption is set, sets the caption to the default
    + * caption; otherwise to the empty string.
    + * @protected
    + */
    +goog.ui.Select.prototype.updateCaption = function() {
    +  var item = this.getSelectedItem();
    +  this.setContent(item ? item.getCaption() : this.defaultCaption_);
    +
    +  var contentElement = this.getRenderer().getContentElement(this.getElement());
    +  // Despite the ControlRenderer interface indicating the return value is
    +  // {Element}, many renderers cast element.firstChild to {Element} when it is
    +  // really {Node}. Checking tagName verifies this is an {!Element}.
    +  if (contentElement && this.getDomHelper().isElement(contentElement)) {
    +    if (this.initialAriaLabel_ == null) {
    +      this.initialAriaLabel_ = goog.a11y.aria.getLabel(contentElement);
    +    }
    +    var itemElement = item ? item.getElement() : null;
    +    goog.a11y.aria.setLabel(contentElement, itemElement ?
    +        goog.a11y.aria.getLabel(itemElement) : this.initialAriaLabel_);
    +    this.updateAriaActiveDescendant_();
    +  }
    +};
    +
    +
    +/**
    + * Updates the aria active descendant attribute.
    + * @private
    + */
    +goog.ui.Select.prototype.updateAriaActiveDescendant_ = function() {
    +  var renderer = this.getRenderer();
    +  if (renderer) {
    +    var contentElement = renderer.getContentElement(this.getElement());
    +    if (contentElement) {
    +      var buttonElement = this.getElementStrict();
    +      if (!contentElement.id) {
    +        contentElement.id = goog.ui.IdGenerator.getInstance().getNextUniqueId();
    +      }
    +      goog.a11y.aria.setRole(contentElement, goog.a11y.aria.Role.OPTION);
    +      goog.a11y.aria.setState(buttonElement,
    +          goog.a11y.aria.State.ACTIVEDESCENDANT, contentElement.id);
    +      if (this.selectionModel_) {
    +        // We can't use selectionmodel's getItemCount here because we need to
    +        // skip separators.
    +        var items = this.selectionModel_.getItems();
    +        var menuItemCount = goog.array.count(items, function(item) {
    +          return item instanceof goog.ui.MenuItem;
    +        });
    +        goog.a11y.aria.setState(contentElement, goog.a11y.aria.State.SETSIZE,
    +            menuItemCount);
    +        // Set a human-readable selection index.
    +        goog.a11y.aria.setState(contentElement, goog.a11y.aria.State.POSINSET,
    +            1 + this.selectionModel_.getSelectedIndex());
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets the correct ARIA role for the menu item or separator.
    + * @param {goog.ui.MenuItem|goog.ui.MenuSeparator} item The item to set.
    + * @private
    + */
    +goog.ui.Select.prototype.setCorrectAriaRole_ = function(item) {
    +  item.setPreferredAriaRole(item instanceof goog.ui.MenuItem ?
    +      goog.a11y.aria.Role.OPTION : goog.a11y.aria.Role.SEPARATOR);
    +};
    +
    +
    +/**
    + * Opens or closes the menu.  Overrides {@link goog.ui.MenuButton#setOpen} by
    + * highlighting the currently selected option on open.
    + * @param {boolean} open Whether to open or close the menu.
    + * @param {goog.events.Event=} opt_e Mousedown event that caused the menu to
    + *     be opened.
    + * @override
    + */
    +goog.ui.Select.prototype.setOpen = function(open, opt_e) {
    +  goog.ui.Select.superClass_.setOpen.call(this, open, opt_e);
    +
    +  if (this.isOpen()) {
    +    this.getMenu().setHighlightedIndex(this.getSelectedIndex());
    +  } else {
    +    this.updateAriaActiveDescendant_();
    +  }
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Selects.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-select'), function() {
    +      // Select defaults to using MenuButtonRenderer, since it shares its L&F.
    +      return new goog.ui.Select(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/select_test.html b/mapbox-gl/closure-library/closure/goog/ui/select_test.html
    new file mode 100644
    index 0000000000..d9f9683e9e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/select_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Select
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.SelectTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/select_test.js b/mapbox-gl/closure-library/closure/goog/ui/select_test.js
    new file mode 100644
    index 0000000000..5f42d6cff8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/select_test.js
    @@ -0,0 +1,329 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SelectTest');
    +goog.setTestOnly('goog.ui.SelectTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.Select');
    +goog.require('goog.ui.Separator');
    +
    +var defaultCaption = 'initial caption';
    +var sandboxEl;
    +var select;
    +
    +function setUp() {
    +  sandboxEl = goog.dom.getElement('sandbox');
    +  select = new goog.ui.Select(defaultCaption);
    +}
    +
    +function tearDown() {
    +  select.dispose();
    +  goog.dom.removeChildren(sandboxEl);
    +}
    +
    +
    +/**
    + * Checks that the default caption passed in the constructor and in the setter
    + * is returned by getDefaultCaption, and acts as a default caption, i.e. is
    + * shown as a caption when no items are selected.
    + */
    +function testDefaultCaption() {
    +  select.render(sandboxEl);
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  select.addItem(item1);
    +  select.addItem(new goog.ui.MenuItem('item 2'));
    +  assertEquals(defaultCaption, select.getDefaultCaption());
    +  assertEquals(defaultCaption, select.getCaption());
    +
    +  var newCaption = 'new caption';
    +  select.setDefaultCaption(newCaption);
    +  assertEquals(newCaption, select.getDefaultCaption());
    +  assertEquals(newCaption, select.getCaption());
    +
    +  select.setSelectedItem(item1);
    +  assertNotEquals(newCaption, select.getCaption());
    +
    +  select.setSelectedItem(null);
    +  assertEquals(newCaption, select.getCaption());
    +}
    +
    +function testNoDefaultCaption() {
    +  assertNull(new goog.ui.Select().getDefaultCaption());
    +  assertEquals('', new goog.ui.Select('').getDefaultCaption());
    +}
    +
    +// Confirms that aria roles for select conform to spec:
    +// http://www.w3.org/TR/wai-aria/roles#listbox
    +// Basically the select should have a role of LISTBOX and all the items should
    +// have a role of OPTION.
    +function testAriaRoles() {
    +  select.render(sandboxEl);
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  select.addItem(item1);
    +  // Added a separator to make sure that the SETSIZE ignores the separator
    +  // items.
    +  var separator = new goog.ui.Separator();
    +  select.addItem(separator);
    +  var item2 = new goog.ui.MenuItem('item 2');
    +  select.addItem(item2);
    +  assertNotNull(select.getElement());
    +  assertNotNull(item1.getElement());
    +  assertNotNull(item2.getElement());
    +  assertEquals(goog.a11y.aria.Role.LISTBOX,
    +      goog.a11y.aria.getRole(select.getElement()));
    +  assertEquals(goog.a11y.aria.Role.OPTION,
    +      goog.a11y.aria.getRole(item1.getElement()));
    +  assertEquals(goog.a11y.aria.Role.OPTION,
    +      goog.a11y.aria.getRole(item2.getElement()));
    +  assertNotNull(goog.a11y.aria.getState(select.getElement(),
    +      goog.a11y.aria.State.ACTIVEDESCENDANT));
    +  var contentElement = select.getRenderer().
    +      getContentElement(select.getElement());
    +  assertEquals('2', goog.a11y.aria.getState(contentElement,
    +      goog.a11y.aria.State.SETSIZE));
    +  assertEquals('0', goog.a11y.aria.getState(contentElement,
    +      goog.a11y.aria.State.POSINSET));
    +}
    +
    +
    +/**
    + * Checks that the select control handles ACTION events from its items.
    + */
    +function testHandlesItemActions() {
    +  select.render(sandboxEl);
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  var item2 = new goog.ui.MenuItem('item 2');
    +  select.addItem(item1);
    +  select.addItem(item2);
    +
    +  item1.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals(item1, select.getSelectedItem());
    +  assertEquals(item1.getCaption(), select.getCaption());
    +
    +  item2.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals(item2, select.getSelectedItem());
    +  assertEquals(item2.getCaption(), select.getCaption());
    +}
    +
    +
    +/**
    + * Tests goog.ui.Select.prototype.setValue.
    + */
    +function testSetValue() {
    +  select.render(sandboxEl);
    +  var item1 = new goog.ui.MenuItem('item 1', 1);
    +  var item2 = new goog.ui.MenuItem('item 2', 2);
    +  select.addItem(item1);
    +  select.addItem(item2);
    +
    +  select.setValue(1);
    +  assertEquals(item1, select.getSelectedItem());
    +
    +  select.setValue(2);
    +  assertEquals(item2, select.getSelectedItem());
    +
    +  select.setValue(3);
    +  assertNull(select.getSelectedItem());
    +}
    +
    +
    +/**
    + * Checks that the current selection is cleared when the selected item is
    + * removed.
    + */
    +function testSelectionIsClearedWhenSelectedItemIsRemoved() {
    +  select.render(sandboxEl);
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  select.addItem(item1);
    +  select.addItem(new goog.ui.MenuItem('item 2'));
    +
    +  select.setSelectedItem(item1);
    +  select.removeItem(item1);
    +  assertNull(select.getSelectedItem());
    +}
    +
    +
    +/**
    + * Check that the select control is subscribed to its selection model events
    + * after being added, removed and added back again into the document.
    + */
    +function testExitAndEnterDocument() {
    +  var component = new goog.ui.Component();
    +  component.render(sandboxEl);
    +
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  var item2 = new goog.ui.MenuItem('item 2');
    +  var item3 = new goog.ui.MenuItem('item 3');
    +
    +  select.addItem(item1);
    +  select.addItem(item2);
    +  select.addItem(item3);
    +
    +  component.addChild(select, true);
    +  item2.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals(item2.getCaption(), select.getCaption());
    +
    +  component.removeChild(select, true);
    +  item1.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals(item2.getCaption(), select.getCaption());
    +
    +  component.addChild(select, true);
    +  item3.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals(item3.getCaption(), select.getCaption());
    +}
    +
    +function testSelectEventFiresForProgrammaticChange() {
    +  select.render();
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  var item2 = new goog.ui.MenuItem('item 2');
    +  select.addItem(item1);
    +  select.addItem(item2);
    +
    +  var recordingHandler = new goog.testing.recordFunction();
    +  goog.events.listen(
    +      select, goog.ui.Component.EventType.CHANGE, recordingHandler);
    +
    +  select.setSelectedItem(item2);
    +  assertEquals('Selecting new item should fire CHANGE event.',
    +      1, recordingHandler.getCallCount());
    +
    +  select.setSelectedItem(item2);
    +  assertEquals('Selecting the same item should not fire CHANGE event.',
    +      1, recordingHandler.getCallCount());
    +
    +  select.setSelectedIndex(0);
    +  assertEquals('Selecting new item should fire CHANGE event.',
    +      2, recordingHandler.getCallCount());
    +
    +  select.setSelectedIndex(0);
    +  assertEquals('Selecting the same item should not fire CHANGE event.',
    +      2, recordingHandler.getCallCount());
    +}
    +
    +function testSelectEventFiresForUserInitiatedAction() {
    +  select.render();
    +  var item1 = new goog.ui.MenuItem('item 1');
    +  var item2 = new goog.ui.MenuItem('item 2');
    +  select.addItem(item1);
    +  select.addItem(item2);
    +
    +  var recordingHandler = new goog.testing.recordFunction();
    +  goog.events.listen(
    +      select, goog.ui.Component.EventType.CHANGE, recordingHandler);
    +
    +  select.setOpen(true);
    +
    +  item2.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals('Selecting new item should fire CHANGE event.',
    +      1, recordingHandler.getCallCount());
    +  assertFalse(select.isOpen());
    +
    +  select.setOpen(true);
    +
    +  item2.dispatchEvent(goog.ui.Component.EventType.ACTION);
    +  assertEquals('Selecting the same item should not fire CHANGE event.',
    +      1, recordingHandler.getCallCount());
    +  assertFalse(select.isOpen());
    +}
    +
    +
    +/**
    + * Checks that if an item is selected before decorate is called, the selection
    + * is preserved after decorate.
    + */
    +function testSetSelectedItemBeforeRender() {
    +  select.addItem(new goog.ui.MenuItem('item 1'));
    +  select.addItem(new goog.ui.MenuItem('item 2'));
    +  var item3 = new goog.ui.MenuItem('item 3');
    +  select.addItem(item3);
    +  select.setSelectedItem(item3);
    +  assertEquals(2, select.getSelectedIndex());
    +
    +  select.decorate(sandboxEl);
    +  assertEquals(2, select.getSelectedIndex());
    +}
    +
    +
    +/**
    + * Checks that if a value is set before decorate is called, the value is
    + * preserved after decorate.
    + */
    +function testSetValueBeforeRender() {
    +  select.addItem(new goog.ui.MenuItem('item 1', 1));
    +  select.addItem(new goog.ui.MenuItem('item 2', 2));
    +  select.setValue(2);
    +  assertEquals(2, select.getValue());
    +
    +  select.decorate(sandboxEl);
    +  assertEquals(2, select.getValue());
    +}
    +
    +
    +function testUpdateCaption_aria() {
    +  select.render(sandboxEl);
    +
    +  // Verify default state.
    +  assertEquals(defaultCaption, select.getCaption());
    +  assertFalse(
    +      !!goog.a11y.aria.getLabel(
    +          select.getRenderer().getContentElement(select.getElement())));
    +
    +  // Add and select an item with aria-label.
    +  var item1 = new goog.ui.MenuItem();
    +  select.addItem(item1);
    +  item1.getElement().setAttribute('aria-label', 'item1');
    +  select.setSelectedIndex(0);
    +  assertEquals(
    +      'item1',
    +      goog.a11y.aria.getLabel(
    +          select.getRenderer().getContentElement(select.getElement())));
    +
    +  // Add and select an item without a label.
    +  var item2 = new goog.ui.MenuItem();
    +  select.addItem(item2);
    +  select.setSelectedIndex(1);
    +  assertFalse(
    +      !!goog.a11y.aria.getLabel(
    +          select.getRenderer().getContentElement(select.getElement())));
    +}
    +
    +function testDisposeWhenInnerHTMLHasBeenClearedInIE10() {
    +  assertNotThrows(function() {
    +    var customSelect = new goog.ui.Select(null /* label */, new goog.ui.Menu(),
    +        new goog.ui.CustomButtonRenderer());
    +    customSelect.render(sandboxEl);
    +
    +    // In IE10 setting the innerHTML of a node invalidates the parent child
    +    // relation of all its child nodes (unlike removeNode).
    +    sandboxEl.innerHTML = '';
    +
    +    // goog.ui.Select's disposeInternal trigger's goog.ui.Component's
    +    // disposeInternal, which triggers goog.ui.MenuButton's exitDocument,
    +    // which closes the associated menu and updates the activeDescendent.
    +    // In the case of a CustomMenuButton the contentElement is referenced by
    +    // element.firstChild.firstChild, an invalid relation in IE 10.
    +    customSelect.dispose();
    +  });
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton.js b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton.js
    new file mode 100644
    index 0000000000..98445ff202
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton.js
    @@ -0,0 +1,300 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A customized MenuButton for selection of items among lists.
    + * Menu contains 'select all' and 'select none' MenuItems for selecting all and
    + * no items by default. Other MenuItems can be added by user.
    + *
    + * The checkbox content fires the action events associated with the 'select all'
    + * and 'select none' menu items.
    + *
    + * @see ../demos/selectionmenubutton.html
    + */
    +
    +goog.provide('goog.ui.SelectionMenuButton');
    +goog.provide('goog.ui.SelectionMenuButton.SelectionState');
    +
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A selection menu button control.  Extends {@link goog.ui.MenuButton}.
    + * Menu contains 'select all' and 'select none' MenuItems for selecting all and
    + * no items by default. Other MenuItems can be added by user.
    + *
    + * The checkbox content fires the action events associated with the 'select all'
    + * and 'select none' menu items.
    + *
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
    + *     decorate the menu button; defaults to {@link goog.ui.MenuButtonRenderer}.
    + * @param {goog.ui.MenuItemRenderer=} opt_itemRenderer Optional menu item
    + *     renderer.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.MenuButton}
    + */
    +goog.ui.SelectionMenuButton = function(opt_renderer,
    +                                       opt_itemRenderer,
    +                                       opt_domHelper) {
    +  goog.ui.MenuButton.call(this,
    +                          null,
    +                          null,
    +                          opt_renderer,
    +                          opt_domHelper);
    +  this.initialItemRenderer_ = opt_itemRenderer || null;
    +};
    +goog.inherits(goog.ui.SelectionMenuButton, goog.ui.MenuButton);
    +goog.tagUnsealableClass(goog.ui.SelectionMenuButton);
    +
    +
    +/**
    + * Constants for menu action types.
    + * @enum {number}
    + */
    +goog.ui.SelectionMenuButton.SelectionState = {
    +  ALL: 0,
    +  SOME: 1,
    +  NONE: 2
    +};
    +
    +
    +/**
    + * Select button state
    + * @type {goog.ui.SelectionMenuButton.SelectionState}
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.selectionState =
    +    goog.ui.SelectionMenuButton.SelectionState.NONE;
    +
    +
    +/**
    + * Item renderer used for the first 2 items, 'select all' and 'select none'.
    + * @type {goog.ui.MenuItemRenderer}
    + * @private
    + */
    +goog.ui.SelectionMenuButton.prototype.initialItemRenderer_;
    +
    +
    +/**
    + * Enables button and embedded checkbox.
    + * @param {boolean} enable Whether to enable or disable the button.
    + * @override
    + */
    +goog.ui.SelectionMenuButton.prototype.setEnabled = function(enable) {
    +  goog.ui.SelectionMenuButton.base(this, 'setEnabled', enable);
    +  this.setCheckboxEnabled(enable);
    +};
    +
    +
    +/**
    + * Enables the embedded checkbox.
    + * @param {boolean} enable Whether to enable or disable the checkbox.
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.setCheckboxEnabled = function(enable) {
    +  this.getCheckboxElement().disabled = !enable;
    +};
    +
    +
    +/** @override */
    +goog.ui.SelectionMenuButton.prototype.handleMouseDown = function(e) {
    +  if (!this.getDomHelper().contains(this.getCheckboxElement(),
    +      /** @type {Element} */ (e.target))) {
    +    goog.ui.SelectionMenuButton.superClass_.handleMouseDown.call(this, e);
    +  }
    +};
    +
    +
    +/**
    + * Gets the checkbox element. Needed because if decorating html, getContent()
    + * may include and comment/text elements in addition to the input element.
    + * @return {Element} Checkbox.
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.getCheckboxElement = function() {
    +  var elements = this.getDomHelper().getElementsByTagNameAndClass(
    +      'input',
    +      goog.getCssName('goog-selectionmenubutton-checkbox'),
    +      this.getContentElement());
    +  return elements[0];
    +};
    +
    +
    +/**
    + * Checkbox click handler.
    + * @param {goog.events.BrowserEvent} e Checkbox click event.
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.handleCheckboxClick = function(e) {
    +  if (this.selectionState == goog.ui.SelectionMenuButton.SelectionState.NONE) {
    +    this.setSelectionState(goog.ui.SelectionMenuButton.SelectionState.ALL);
    +    if (this.getItemAt(0)) {
    +      this.getItemAt(0).dispatchEvent(  // 'All' item
    +          goog.ui.Component.EventType.ACTION);
    +    }
    +  } else {
    +    this.setSelectionState(goog.ui.SelectionMenuButton.SelectionState.NONE);
    +    if (this.getItemAt(1)) {
    +      this.getItemAt(1).dispatchEvent(  // 'None' item
    +          goog.ui.Component.EventType.ACTION);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Menu action handler to update checkbox checked state.
    + * @param {goog.events.Event} e Menu action event.
    + * @private
    + */
    +goog.ui.SelectionMenuButton.prototype.handleMenuAction_ = function(e) {
    +  if (e.target.getModel() == goog.ui.SelectionMenuButton.SelectionState.ALL) {
    +    this.setSelectionState(goog.ui.SelectionMenuButton.SelectionState.ALL);
    +  } else {
    +    this.setSelectionState(goog.ui.SelectionMenuButton.SelectionState.NONE);
    +  }
    +};
    +
    +
    +/**
    + * Set up events related to the menu items.
    + * @private
    + */
    +goog.ui.SelectionMenuButton.prototype.addMenuEvent_ = function() {
    +  if (this.getItemAt(0) && this.getItemAt(1)) {
    +    this.getHandler().listen(this.getMenu(),
    +                             goog.ui.Component.EventType.ACTION,
    +                             this.handleMenuAction_);
    +    this.getItemAt(0).setModel(goog.ui.SelectionMenuButton.SelectionState.ALL);
    +    this.getItemAt(1).setModel(goog.ui.SelectionMenuButton.SelectionState.NONE);
    +  }
    +};
    +
    +
    +/**
    + * Set up events related to the checkbox.
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.addCheckboxEvent = function() {
    +  this.getHandler().listen(this.getCheckboxElement(),
    +                           goog.events.EventType.CLICK,
    +                           this.handleCheckboxClick);
    +};
    +
    +
    +/**
    + * Adds the checkbox to the button, and adds 2 items to the menu corresponding
    + * to 'select all' and 'select none'.
    + * @override
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.createDom = function() {
    +  goog.ui.SelectionMenuButton.superClass_.createDom.call(this);
    +
    +  this.createCheckbox();
    +
    +  /** @desc Text for 'All' button, used to select all items in a list. */
    +  var MSG_SELECTIONMENUITEM_ALL = goog.getMsg('All');
    +  /** @desc Text for 'None' button, used to unselect all items in a list. */
    +  var MSG_SELECTIONMENUITEM_NONE = goog.getMsg('None');
    +
    +  var itemAll = new goog.ui.MenuItem(MSG_SELECTIONMENUITEM_ALL,
    +                                     null,
    +                                     this.getDomHelper(),
    +                                     this.initialItemRenderer_);
    +  var itemNone = new goog.ui.MenuItem(MSG_SELECTIONMENUITEM_NONE,
    +                                      null,
    +                                      this.getDomHelper(),
    +                                      this.initialItemRenderer_);
    +  this.addItem(itemAll);
    +  this.addItem(itemNone);
    +
    +  this.addCheckboxEvent();
    +  this.addMenuEvent_();
    +};
    +
    +
    +/**
    + * Creates and adds the checkbox to the button.
    + * @protected
    + */
    +goog.ui.SelectionMenuButton.prototype.createCheckbox = function() {
    +  var checkbox = this.getDomHelper().createElement('input');
    +  checkbox.type = 'checkbox';
    +  checkbox.className = goog.getCssName('goog-selectionmenubutton-checkbox');
    +  this.setContent(checkbox);
    +};
    +
    +
    +/** @override */
    +goog.ui.SelectionMenuButton.prototype.decorateInternal = function(element) {
    +  goog.ui.SelectionMenuButton.superClass_.decorateInternal.call(this, element);
    +  this.addCheckboxEvent();
    +  this.addMenuEvent_();
    +};
    +
    +
    +/** @override */
    +goog.ui.SelectionMenuButton.prototype.setMenu = function(menu) {
    +  goog.ui.SelectionMenuButton.superClass_.setMenu.call(this, menu);
    +  this.addMenuEvent_();
    +};
    +
    +
    +/**
    + * Set selection state and update checkbox.
    + * @param {goog.ui.SelectionMenuButton.SelectionState} state Selection state.
    + */
    +goog.ui.SelectionMenuButton.prototype.setSelectionState = function(state) {
    +  if (this.selectionState != state) {
    +    var checkbox = this.getCheckboxElement();
    +    if (state == goog.ui.SelectionMenuButton.SelectionState.ALL) {
    +      checkbox.checked = true;
    +      goog.style.setOpacity(checkbox, 1);
    +    } else if (state == goog.ui.SelectionMenuButton.SelectionState.SOME) {
    +      checkbox.checked = true;
    +      // TODO(user): Get UX help to style this
    +      goog.style.setOpacity(checkbox, 0.5);
    +    } else { // NONE
    +      checkbox.checked = false;
    +      goog.style.setOpacity(checkbox, 1);
    +    }
    +    this.selectionState = state;
    +  }
    +};
    +
    +
    +/**
    +* Get selection state.
    +* @return {goog.ui.SelectionMenuButton.SelectionState} Selection state.
    +*/
    +goog.ui.SelectionMenuButton.prototype.getSelectionState = function() {
    +  return this.selectionState;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.SelectionMenuButton.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-selectionmenubutton-button'),
    +    function() {
    +      return new goog.ui.SelectionMenuButton();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.html b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.html
    new file mode 100644
    index 0000000000..065236acff
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.html
    @@ -0,0 +1,54 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author zhyder@google.com (Zohair Hyder)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.SelectionMenuButton
    +  </title>
    +  <style type="text/css">
    +   .goog-menu {
    +  position: absolute;
    +  color: #aaa;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.SelectionMenuButtonTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   Here's a selectionmenu defined in markup:
    +  </p>
    +  <div id="demoSelectionMenuButton" class="goog-selectionmenubutton-button">
    +   <input id="demoCheckbox" class="goog-selectionmenubutton-checkbox" type="checkbox" />
    +   <div id="demoMenu" class="goog-menu">
    +    <div id="menuItem1" class="goog-menuitem">
    +     All
    +    </div>
    +    <div id="menuItem2" class="goog-menuitem">
    +     None
    +    </div>
    +    <div id="menuItem3" class="goog-menuitem">
    +     Read
    +    </div>
    +    <div id="menuItem4" class="goog-menuitem">
    +     Unread
    +    </div>
    +   </div>
    +  </div>
    +  <div id="footer">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.js b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.js
    new file mode 100644
    index 0000000000..e7af08a2c1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmenubutton_test.js
    @@ -0,0 +1,201 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SelectionMenuButtonTest');
    +goog.setTestOnly('goog.ui.SelectionMenuButtonTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.SelectionMenuButton');
    +
    +var selectionMenuButton;
    +var clonedSelectionMenuButtonDom;
    +
    +
    +function setUp() {
    +  clonedSelectionMenuButtonDom =
    +      goog.dom.getElement('demoSelectionMenuButton').cloneNode(true);
    +
    +  selectionMenuButton = new goog.ui.SelectionMenuButton();
    +}
    +
    +function tearDown() {
    +  selectionMenuButton.dispose();
    +
    +  var element = goog.dom.getElement('demoSelectionMenuButton');
    +  element.parentNode.replaceChild(clonedSelectionMenuButtonDom, element);
    +}
    +
    +
    +/**
    + * Open the menu and click on the menu item inside.
    + */
    +function testBasicButtonBehavior() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +  goog.testing.events.fireClickSequence(node);
    +
    +  assertTrue('Menu must open after click', selectionMenuButton.isOpen());
    +
    +  var menuItemClicked = 0;
    +  var lastMenuItemClicked = null;
    +  goog.events.listen(selectionMenuButton.getMenu(),
    +      goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        menuItemClicked++;
    +        lastMenuItemClicked = e.target;
    +      });
    +
    +  var menuItem2 = goog.dom.getElement('menuItem2');
    +  goog.testing.events.fireClickSequence(menuItem2);
    +  assertFalse('Menu must close on clicking when open',
    +      selectionMenuButton.isOpen());
    +  assertEquals('Number of menu items clicked should be 1', 1, menuItemClicked);
    +  assertEquals('menuItem2 should be the last menuitem clicked', menuItem2,
    +      lastMenuItemClicked.getElement());
    +}
    +
    +
    +/**
    + * Tests that the checkbox fires the same events as the first 2 items.
    + */
    +function testCheckboxFireEvents() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +
    +  var menuItemClicked = 0;
    +  var lastMenuItemClicked = null;
    +  goog.events.listen(selectionMenuButton.getMenu(),
    +      goog.ui.Component.EventType.ACTION,
    +      function(e) {
    +        menuItemClicked++;
    +        lastMenuItemClicked = e.target;
    +      });
    +
    +  var checkbox = goog.dom.getElement('demoCheckbox');
    +  assertFalse('Checkbox must be unchecked (i.e. unselected)', checkbox.checked);
    +
    +  checkbox.checked = true;
    +  goog.testing.events.fireClickSequence(checkbox);
    +  assertFalse('Menu must be closed when clicking checkbox',
    +      selectionMenuButton.isOpen());
    +  assertEquals('Number of menu items clicked should be 1', 1, menuItemClicked);
    +  assertEquals('menuItem1 should be the last menuitem clicked',
    +      goog.dom.getElement('menuItem1'),
    +      lastMenuItemClicked.getElement());
    +
    +  checkbox.checked = false;
    +  goog.testing.events.fireClickSequence(checkbox);
    +  assertFalse('Menu must be closed when clicking checkbox',
    +      selectionMenuButton.isOpen());
    +  assertEquals('Number of menu items clicked should be 2', 2, menuItemClicked);
    +  assertEquals('menuItem2 should be the last menuitem clicked',
    +      goog.dom.getElement('menuItem2'), lastMenuItemClicked.getElement());
    +}
    +
    +
    +/**
    + * Tests that the checkbox state gets updated when the first 2 events fire
    + */
    +function testCheckboxReceiveEvents() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +
    +  var checkbox = goog.dom.getElement('demoCheckbox');
    +  assertFalse('Checkbox must be unchecked (i.e. unselected)', checkbox.checked);
    +
    +  goog.testing.events.fireClickSequence(goog.dom.getElement('menuItem1'));
    +  assertTrue('Checkbox must be checked (i.e. selected)', checkbox.checked);
    +
    +  goog.testing.events.fireClickSequence(goog.dom.getElement('menuItem2'));
    +  assertFalse('Checkbox must be unchecked (i.e. unselected)', checkbox.checked);
    +}
    +
    +
    +/**
    + * Tests that set/getSelectionState correctly changes the state
    + */
    +function testSelectionState() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +
    +  var checkbox = goog.dom.getElement('demoCheckbox');
    +  assertFalse('Checkbox must be unchecked (i.e. unselected)', checkbox.checked);
    +
    +  selectionMenuButton.setSelectionState(
    +      goog.ui.SelectionMenuButton.SelectionState.ALL);
    +  assertTrue('Checkbox should be checked when selecting all', checkbox.checked);
    +  assertEquals('selectionState should be ALL',
    +      selectionMenuButton.getSelectionState(),
    +      goog.ui.SelectionMenuButton.SelectionState.ALL);
    +
    +  selectionMenuButton.setSelectionState(
    +      goog.ui.SelectionMenuButton.SelectionState.NONE);
    +  assertFalse('Checkbox should be checked when selecting all',
    +      checkbox.checked);
    +  assertEquals('selectionState should be NONE',
    +      selectionMenuButton.getSelectionState(),
    +      goog.ui.SelectionMenuButton.SelectionState.NONE);
    +
    +  selectionMenuButton.setSelectionState(
    +      goog.ui.SelectionMenuButton.SelectionState.SOME);
    +  assertTrue('Checkbox should be checked when selecting all', checkbox.checked);
    +  assertEquals('selectionState should be SOME',
    +      selectionMenuButton.getSelectionState(),
    +      goog.ui.SelectionMenuButton.SelectionState.SOME);
    +}
    +
    +
    +/**
    + * Tests that the checkbox gets disabled when the button is disabled
    + */
    +function testCheckboxDisabled() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +
    +  var checkbox = goog.dom.getElement('demoCheckbox');
    +  assertFalse('Checkbox must be enabled', checkbox.disabled);
    +
    +  selectionMenuButton.setEnabled(false);
    +  assertTrue('Checkbox must be disabled', checkbox.disabled);
    +
    +  selectionMenuButton.setEnabled(true);
    +  assertFalse('Checkbox must be enabled', checkbox.disabled);
    +}
    +
    +
    +/**
    + * Tests that clicking the checkbox does not open the menu
    + */
    +function testCheckboxClickMenuClosed() {
    +  var node = goog.dom.getElement('demoSelectionMenuButton');
    +  selectionMenuButton.decorate(node);
    +
    +  var checkbox = goog.dom.getElement('demoCheckbox');
    +  goog.testing.events.fireMouseDownEvent(checkbox);
    +  assertFalse('Menu must be closed when mousedown checkbox',
    +      selectionMenuButton.isOpen());
    +  goog.testing.events.fireMouseUpEvent(checkbox);
    +  assertFalse('Menu must remain closed when mouseup checkbox',
    +      selectionMenuButton.isOpen());
    +
    +  selectionMenuButton.setOpen(true);
    +  goog.testing.events.fireClickSequence(checkbox);
    +  assertFalse('Menu must close when clickin checkbox',
    +      selectionMenuButton.isOpen());
    +
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmodel.js b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel.js
    new file mode 100644
    index 0000000000..4cde31bd82
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel.js
    @@ -0,0 +1,301 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Single-selection model implemenation.
    + *
    + * TODO(attila): Add keyboard & mouse event hooks?
    + * TODO(attila): Add multiple selection?
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +
    +goog.provide('goog.ui.SelectionModel');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +
    +
    +
    +/**
    + * Single-selection model.  Dispatches a {@link goog.events.EventType.SELECT}
    + * event when a selection is made.
    + * @param {Array<Object>=} opt_items Array of items; defaults to empty.
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + */
    +goog.ui.SelectionModel = function(opt_items) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * Array of items controlled by the selection model.  If the items support
    +   * the {@code setSelected(Boolean)} interface, they will be (de)selected
    +   * as needed.
    +   * @type {!Array<Object>}
    +   * @private
    +   */
    +  this.items_ = [];
    +  this.addItems(opt_items);
    +};
    +goog.inherits(goog.ui.SelectionModel, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.SelectionModel);
    +
    +
    +/**
    + * The currently selected item (null if none).
    + * @type {Object}
    + * @private
    + */
    +goog.ui.SelectionModel.prototype.selectedItem_ = null;
    +
    +
    +/**
    + * Selection handler function.  Called with two arguments (the item to be
    + * selected or deselected, and a Boolean indicating whether the item is to
    + * be selected or deselected).
    + * @type {Function}
    + * @private
    + */
    +goog.ui.SelectionModel.prototype.selectionHandler_ = null;
    +
    +
    +/**
    + * Returns the selection handler function used by the selection model to change
    + * the internal selection state of items under its control.
    + * @return {Function} Selection handler function (null if none).
    + */
    +goog.ui.SelectionModel.prototype.getSelectionHandler = function() {
    +  return this.selectionHandler_;
    +};
    +
    +
    +/**
    + * Sets the selection handler function to be used by the selection model to
    + * change the internal selection state of items under its control.  The
    + * function must take two arguments:  an item and a Boolean to indicate whether
    + * the item is to be selected or deselected.  Selection handler functions are
    + * only needed if the items in the selection model don't natively support the
    + * {@code setSelected(Boolean)} interface.
    + * @param {Function} handler Selection handler function.
    + */
    +goog.ui.SelectionModel.prototype.setSelectionHandler = function(handler) {
    +  this.selectionHandler_ = handler;
    +};
    +
    +
    +/**
    + * Returns the number of items controlled by the selection model.
    + * @return {number} Number of items.
    + */
    +goog.ui.SelectionModel.prototype.getItemCount = function() {
    +  return this.items_.length;
    +};
    +
    +
    +/**
    + * Returns the 0-based index of the given item within the selection model, or
    + * -1 if no such item is found.
    + * @param {Object|undefined} item Item to look for.
    + * @return {number} Index of the given item (-1 if none).
    + */
    +goog.ui.SelectionModel.prototype.indexOfItem = function(item) {
    +  return item ? goog.array.indexOf(this.items_, item) : -1;
    +};
    +
    +
    +/**
    + * @return {Object|undefined} The first item, or undefined if there are no items
    + *     in the model.
    + */
    +goog.ui.SelectionModel.prototype.getFirst = function() {
    +  return this.items_[0];
    +};
    +
    +
    +/**
    + * @return {Object|undefined} The last item, or undefined if there are no items
    + *     in the model.
    + */
    +goog.ui.SelectionModel.prototype.getLast = function() {
    +  return this.items_[this.items_.length - 1];
    +};
    +
    +
    +/**
    + * Returns the item at the given 0-based index.
    + * @param {number} index Index of the item to return.
    + * @return {Object} Item at the given index (null if none).
    + */
    +goog.ui.SelectionModel.prototype.getItemAt = function(index) {
    +  return this.items_[index] || null;
    +};
    +
    +
    +/**
    + * Bulk-adds items to the selection model.  This is more efficient than calling
    + * {@link #addItem} for each new item.
    + * @param {Array<Object>|undefined} items New items to add.
    + */
    +goog.ui.SelectionModel.prototype.addItems = function(items) {
    +  if (items) {
    +    // New items shouldn't be selected.
    +    goog.array.forEach(items, function(item) {
    +      this.selectItem_(item, false);
    +    }, this);
    +    goog.array.extend(this.items_, items);
    +  }
    +};
    +
    +
    +/**
    + * Adds an item at the end of the list.
    + * @param {Object} item Item to add.
    + */
    +goog.ui.SelectionModel.prototype.addItem = function(item) {
    +  this.addItemAt(item, this.getItemCount());
    +};
    +
    +
    +/**
    + * Adds an item at the given index.
    + * @param {Object} item Item to add.
    + * @param {number} index Index at which to add the new item.
    + */
    +goog.ui.SelectionModel.prototype.addItemAt = function(item, index) {
    +  if (item) {
    +    // New items must not be selected.
    +    this.selectItem_(item, false);
    +    goog.array.insertAt(this.items_, item, index);
    +  }
    +};
    +
    +
    +/**
    + * Removes the given item (if it exists).  Dispatches a {@code SELECT} event if
    + * the removed item was the currently selected item.
    + * @param {Object} item Item to remove.
    + */
    +goog.ui.SelectionModel.prototype.removeItem = function(item) {
    +  if (item && goog.array.remove(this.items_, item)) {
    +    if (item == this.selectedItem_) {
    +      this.selectedItem_ = null;
    +      this.dispatchEvent(goog.events.EventType.SELECT);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes the item at the given index.
    + * @param {number} index Index of the item to remove.
    + */
    +goog.ui.SelectionModel.prototype.removeItemAt = function(index) {
    +  this.removeItem(this.getItemAt(index));
    +};
    +
    +
    +/**
    + * @return {Object} The currently selected item, or null if none.
    + */
    +goog.ui.SelectionModel.prototype.getSelectedItem = function() {
    +  return this.selectedItem_;
    +};
    +
    +
    +/**
    + * @return {!Array<Object>} All items in the selection model.
    + */
    +goog.ui.SelectionModel.prototype.getItems = function() {
    +  return goog.array.clone(this.items_);
    +};
    +
    +
    +/**
    + * Selects the given item, deselecting any previously selected item, and
    + * dispatches a {@code SELECT} event.
    + * @param {Object} item Item to select (null to clear the selection).
    + */
    +goog.ui.SelectionModel.prototype.setSelectedItem = function(item) {
    +  if (item != this.selectedItem_) {
    +    this.selectItem_(this.selectedItem_, false);
    +    this.selectedItem_ = item;
    +    this.selectItem_(item, true);
    +  }
    +
    +  // Always dispatch a SELECT event; let listeners decide what to do if the
    +  // selected item hasn't changed.
    +  this.dispatchEvent(goog.events.EventType.SELECT);
    +};
    +
    +
    +/**
    + * @return {number} The 0-based index of the currently selected item, or -1
    + *     if none.
    + */
    +goog.ui.SelectionModel.prototype.getSelectedIndex = function() {
    +  return this.indexOfItem(this.selectedItem_);
    +};
    +
    +
    +/**
    + * Selects the item at the given index, deselecting any previously selected
    + * item, and dispatches a {@code SELECT} event.
    + * @param {number} index Index to select (-1 to clear the selection).
    + */
    +goog.ui.SelectionModel.prototype.setSelectedIndex = function(index) {
    +  this.setSelectedItem(this.getItemAt(index));
    +};
    +
    +
    +/**
    + * Clears the selection model by removing all items from the selection.
    + */
    +goog.ui.SelectionModel.prototype.clear = function() {
    +  goog.array.clear(this.items_);
    +  this.selectedItem_ = null;
    +};
    +
    +
    +/** @override */
    +goog.ui.SelectionModel.prototype.disposeInternal = function() {
    +  goog.ui.SelectionModel.superClass_.disposeInternal.call(this);
    +  delete this.items_;
    +  this.selectedItem_ = null;
    +};
    +
    +
    +/**
    + * Private helper; selects or deselects the given item based on the value of
    + * the {@code select} argument.  If a selection handler has been registered
    + * (via {@link #setSelectionHandler}, calls it to update the internal selection
    + * state of the item.  Otherwise, attempts to call {@code setSelected(Boolean)}
    + * on the item itself, provided the object supports that interface.
    + * @param {Object} item Item to select or deselect.
    + * @param {boolean} select If true, the object will be selected; if false, it
    + *     will be deselected.
    + * @private
    + */
    +goog.ui.SelectionModel.prototype.selectItem_ = function(item, select) {
    +  if (item) {
    +    if (typeof this.selectionHandler_ == 'function') {
    +      // Use the registered selection handler function.
    +      this.selectionHandler_(item, select);
    +    } else if (typeof item.setSelected == 'function') {
    +      // Call setSelected() on the item, if it supports it.
    +      item.setSelected(select);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.html b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.html
    new file mode 100644
    index 0000000000..72c9681eca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.html
    @@ -0,0 +1,23 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.ui.SelectionModel
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.SelectionModelTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.js b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.js
    new file mode 100644
    index 0000000000..7481b5d26e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/selectionmodel_test.js
    @@ -0,0 +1,220 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SelectionModelTest');
    +goog.setTestOnly('goog.ui.SelectionModelTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.SelectionModel');
    +
    +var selectionModel, items, addedItem, addedItems;
    +
    +function setUp() {
    +  items = [1, 2, 3, 4];
    +  addedItem = 5;
    +  addedItems = [6, 7, 8];
    +  selectionModel = new goog.ui.SelectionModel(items);
    +}
    +
    +function tearDown() {
    +  goog.dispose(selectionModel);
    +}
    +
    +/*
    + * Checks that the selection model returns the correct item count.
    + */
    +function testGetItemCount() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +}
    +
    +/*
    + * Checks that the correct first element is returned by the selection model.
    + */
    +function testGetFirst() {
    +  assertEquals(items[0], selectionModel.getFirst());
    +}
    +
    +/*
    + * Checks that the correct last element is returned by the selection model.
    + */
    +function testGetLast() {
    +  assertEquals(items[items.length - 1], selectionModel.getLast());
    +}
    +
    +/*
    + * Tests the behavior of goog.ui.SelectionModel.getItemAt(index).
    + */
    +function testGetItemAt() {
    +  goog.array.forEach(items,
    +      function(item, i) {
    +        assertEquals(item, selectionModel.getItemAt(i));
    +      });
    +}
    +
    +/*
    + * Checks that an item can be correctly added to the selection model.
    + */
    +function testAddItem() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +
    +  selectionModel.addItem(addedItem);
    +
    +  assertEquals(items.length + 1, selectionModel.getItemCount());
    +  assertEquals(addedItem, selectionModel.getLast());
    +}
    +
    +/*
    + * Checks that an item can be added to the selection model at a specific index.
    + */
    +function testAddItemAt() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +
    +  var insertIndex = 2;
    +  assertEquals(items[insertIndex], selectionModel.getItemAt(insertIndex));
    +
    +  selectionModel.addItemAt(addedItem, insertIndex);
    +
    +  var resultArray = goog.array.clone(items);
    +  goog.array.insertAt(resultArray, addedItem, insertIndex);
    +
    +  assertEquals(items.length + 1, selectionModel.getItemCount());
    +  assertEquals(addedItem, selectionModel.getItemAt(insertIndex));
    +  assertArrayEquals(resultArray, selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that multiple items can be correctly added to the selection model.
    + */
    +function testAddItems() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +
    +  selectionModel.addItems(addedItems);
    +
    +  assertEquals(items.length + addedItems.length, selectionModel.getItemCount());
    +
    +  var resultArray = goog.array.concat(items, addedItems);
    +  assertArrayEquals(resultArray, selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that all elements can be removed from the selection model.
    + */
    +function testClear() {
    +  assertArrayEquals(items, selectionModel.getItems());
    +
    +  selectionModel.clear();
    +
    +  assertArrayEquals([], selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that all items can be obtained from the selection model.
    + */
    +function testGetItems() {
    +  assertArrayEquals(items, selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that an item's index can be found in the selection model.
    + */
    +function testIndexOfItem() {
    +  goog.array.forEach(items,
    +      function(item, i) {
    +        assertEquals(i, selectionModel.indexOfItem(item));
    +      });
    +}
    +
    +/*
    + * Checks that an item can be removed from the selection model.
    + */
    +function testRemoveItem() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +
    +  var resultArray = goog.array.clone(items);
    +  goog.array.removeAt(resultArray, 2);
    +
    +  selectionModel.removeItem(items[2]);
    +
    +  assertEquals(items.length - 1, selectionModel.getItemCount());
    +  assertArrayEquals(resultArray, selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that an item at a particular index can be removed from the selection
    + * model.
    + */
    +function testRemoveItemAt() {
    +  assertEquals(items.length, selectionModel.getItemCount());
    +
    +  var resultArray = goog.array.clone(items);
    +  var removeIndex = 2;
    +
    +  goog.array.removeAt(resultArray, removeIndex);
    +
    +  selectionModel.removeItemAt(removeIndex);
    +
    +  assertEquals(items.length - 1, selectionModel.getItemCount());
    +  assertNotEquals(items[removeIndex], selectionModel.getItemAt(removeIndex));
    +  assertArrayEquals(resultArray, selectionModel.getItems());
    +}
    +
    +/*
    + * Checks that item selection at a particular index works.
    + */
    +function testSelectedIndex() {
    +  // Default selected index is -1
    +  assertEquals(-1, selectionModel.getSelectedIndex());
    +
    +  selectionModel.setSelectedIndex(2);
    +
    +  assertEquals(2, selectionModel.getSelectedIndex());
    +  assertEquals(items[2], selectionModel.getSelectedItem());
    +}
    +
    +/*
    + * Checks that items can be selected in the selection model.
    + */
    +function testSelectedItem() {
    +  assertNull(selectionModel.getSelectedItem());
    +
    +  selectionModel.setSelectedItem(items[1]);
    +
    +  assertNotNull(selectionModel.getSelectedItem());
    +  assertEquals(items[1], selectionModel.getSelectedItem());
    +  assertEquals(1, selectionModel.getSelectedIndex());
    +}
    +
    +/*
    + * Checks that an installed handler is called on selection change.
    + */
    +function testSelectionHandler() {
    +  var myRecordFunction = new goog.testing.recordFunction();
    +
    +  selectionModel.setSelectionHandler(myRecordFunction);
    +
    +  // Select index 2
    +  selectionModel.setSelectedIndex(2);
    +  // De-select 2 and select 3
    +  selectionModel.setSelectedIndex(3);
    +
    +  var recordCalls = myRecordFunction.getCalls();
    +
    +  assertEquals(3, recordCalls.length);
    +  // Calls: Select items[2], de-select items[2], select items[3]
    +  assertArrayEquals([items[2], true], recordCalls[0].getArguments());
    +  assertArrayEquals([items[2], false], recordCalls[1].getArguments());
    +  assertArrayEquals([items[3], true], recordCalls[2].getArguments());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/separator.js b/mapbox-gl/closure-library/closure/goog/ui/separator.js
    new file mode 100644
    index 0000000000..303d9d3c2b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/separator.js
    @@ -0,0 +1,80 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class for representing a separator, with renderers for both
    + * horizontal (menu) and vertical (toolbar) separators.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.Separator');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.MenuSeparatorRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a separator.  Although it extends {@link goog.ui.Control},
    + * the Separator class doesn't allocate any event handlers, nor does it change
    + * its appearance on mouseover, etc.
    + * @param {goog.ui.MenuSeparatorRenderer=} opt_renderer Renderer to render or
    + *    decorate the separator; defaults to {@link goog.ui.MenuSeparatorRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *    document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Separator = function(opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, null, opt_renderer ||
    +      goog.ui.MenuSeparatorRenderer.getInstance(), opt_domHelper);
    +
    +  this.setSupportedState(goog.ui.Component.State.DISABLED, false);
    +  this.setSupportedState(goog.ui.Component.State.HOVER, false);
    +  this.setSupportedState(goog.ui.Component.State.ACTIVE, false);
    +  this.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +
    +  // Separators are always considered disabled.
    +  this.setStateInternal(goog.ui.Component.State.DISABLED);
    +};
    +goog.inherits(goog.ui.Separator, goog.ui.Control);
    +
    +
    +/**
    + * Configures the component after its DOM has been rendered.  Overrides
    + * {@link goog.ui.Control#enterDocument} by making sure no event handler
    + * is allocated.
    + * @override
    + */
    +goog.ui.Separator.prototype.enterDocument = function() {
    +  goog.ui.Separator.superClass_.enterDocument.call(this);
    +  var element = this.getElement();
    +  goog.asserts.assert(element,
    +      'The DOM element for the separator cannot be null.');
    +  goog.a11y.aria.setRole(element, 'separator');
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.MenuSeparators.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.MenuSeparatorRenderer.CSS_CLASS,
    +    function() {
    +      // Separator defaults to using MenuSeparatorRenderer.
    +      return new goog.ui.Separator();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/serverchart.js b/mapbox-gl/closure-library/closure/goog/ui/serverchart.js
    new file mode 100644
    index 0000000000..1735c90bef
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/serverchart.js
    @@ -0,0 +1,1839 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Component for generating chart PNGs using Google Chart Server.
    + *
    + * @deprecated Google Chart Images service (the server-side component of this
    + *     class) has been deprecated. See
    + *     https://developers.google.com/chart/ for alternatives.
    + *
    + * @see ../demos/serverchart.html
    + */
    +
    +
    +/**
    + * Namespace for chart functions
    + */
    +goog.provide('goog.ui.ServerChart');
    +goog.provide('goog.ui.ServerChart.AxisDisplayType');
    +goog.provide('goog.ui.ServerChart.ChartType');
    +goog.provide('goog.ui.ServerChart.EncodingType');
    +goog.provide('goog.ui.ServerChart.Event');
    +goog.provide('goog.ui.ServerChart.LegendPosition');
    +goog.provide('goog.ui.ServerChart.MaximumValue');
    +goog.provide('goog.ui.ServerChart.MultiAxisAlignment');
    +goog.provide('goog.ui.ServerChart.MultiAxisType');
    +goog.provide('goog.ui.ServerChart.UriParam');
    +goog.provide('goog.ui.ServerChart.UriTooLongEvent');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events.Event');
    +goog.require('goog.string');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * Will construct a chart using Google's chartserver.
    + *
    + * @param {goog.ui.ServerChart.ChartType} type The chart type.
    + * @param {number=} opt_width The width of the chart.
    + * @param {number=} opt_height The height of the chart.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM Helper.
    + * @param {string=} opt_uri Optional uri used to connect to the chart server, if
    + *     different than goog.ui.ServerChart.CHART_SERVER_SCHEME_INDEPENDENT_URI.
    + * @constructor
    + * @extends {goog.ui.Component}
    + *
    + * @deprecated Google Chart Server has been deprecated. See
    + *     https://developers.google.com/chart/image/ for details.
    + * @final
    + */
    +goog.ui.ServerChart = function(type, opt_width, opt_height, opt_domHelper,
    +    opt_uri) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * Image URI.
    +   * @type {goog.Uri}
    +   * @private
    +   */
    +  this.uri_ = new goog.Uri(
    +      opt_uri || goog.ui.ServerChart.CHART_SERVER_SCHEME_INDEPENDENT_URI);
    +
    +  /**
    +   * Encoding method for the URI data format.
    +   * @type {goog.ui.ServerChart.EncodingType}
    +   * @private
    +   */
    +  this.encodingType_ = goog.ui.ServerChart.EncodingType.AUTOMATIC;
    +
    +  /**
    +   * Two-dimensional array of the data sets on the chart.
    +   * @type {Array<Array<number>>}
    +   * @private
    +   */
    +  this.dataSets_ = [];
    +
    +  /**
    +   * Colors for each data set.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.setColors_ = [];
    +
    +  /**
    +   * Legend texts for each data set.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.setLegendTexts_ = [];
    +
    +  /**
    +   * Labels on the X-axis.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.xLabels_ = [];
    +
    +  /**
    +   * Labels on the left along the Y-axis.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.leftLabels_ = [];
    +
    +  /**
    +   * Labels on the right along the Y-axis.
    +   * @type {Array<string>}
    +   * @private
    +   */
    +  this.rightLabels_ = [];
    +
    +  /**
    +   * Axis type for each multi-axis in the chart. The indices into this array
    +   * also work as the reference index for all other multi-axis properties.
    +   * @type {Array<goog.ui.ServerChart.MultiAxisType>}
    +   * @private
    +   */
    +  this.multiAxisType_ = [];
    +
    +  /**
    +   * Axis text for each multi-axis in the chart, indexed by the indices from
    +   * multiAxisType_ in a sparse array.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.multiAxisLabelText_ = {};
    +
    +
    +  /**
    +   * Axis position for each multi-axis in the chart, indexed by the indices
    +   * from multiAxisType_ in a sparse array.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.multiAxisLabelPosition_ = {};
    +
    +  /**
    +   * Axis range for each multi-axis in the chart, indexed by the indices from
    +   * multiAxisType_ in a sparse array.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.multiAxisRange_ = {};
    +
    +  /**
    +   * Axis style for each multi-axis in the chart, indexed by the indices from
    +   * multiAxisType_ in a sparse array.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.multiAxisLabelStyle_ = {};
    +
    +  this.setType(type);
    +  this.setSize(opt_width, opt_height);
    +
    +  /**
    +   * Minimum value for the chart (used for normalization). By default,
    +   * this is set to infinity, and is eventually updated to the lowest given
    +   * value in the data. The minimum value is then subtracted from all other
    +   * values. For a pie chart, subtracting the minimum value does not make
    +   * sense, so minValue_ is set to zero because 0 is the additive identity.
    +   * @type {number}
    +   * @private
    +   */
    +  this.minValue_ = this.isPieChart() ? 0 : Infinity;
    +};
    +goog.inherits(goog.ui.ServerChart, goog.ui.Component);
    +
    +
    +/**
    + * Base scheme-independent URI for the chart renderer.
    + * @type {string}
    + */
    +goog.ui.ServerChart.CHART_SERVER_SCHEME_INDEPENDENT_URI =
    +    '//chart.googleapis.com/chart';
    +
    +
    +/**
    + * Base HTTP URI for the chart renderer.
    + * @type {string}
    + */
    +goog.ui.ServerChart.CHART_SERVER_HTTP_URI =
    +    'http://chart.googleapis.com/chart';
    +
    +
    +/**
    + * Base HTTPS URI for the chart renderer.
    + * @type {string}
    + */
    +goog.ui.ServerChart.CHART_SERVER_HTTPS_URI =
    +    'https://chart.googleapis.com/chart';
    +
    +
    +/**
    + * Base URI for the chart renderer.
    + * @type {string}
    + * @deprecated Use
    + *     {@link goog.ui.ServerChart.CHART_SERVER_SCHEME_INDEPENDENT_URI},
    + *     {@link goog.ui.ServerChart.CHART_SERVER_HTTP_URI} or
    + *     {@link goog.ui.ServerChart.CHART_SERVER_HTTPS_URI} instead.
    + */
    +goog.ui.ServerChart.CHART_SERVER_URI =
    +    goog.ui.ServerChart.CHART_SERVER_HTTP_URI;
    +
    +
    +/**
    + * The 0 - 1.0 ("fraction of the range") value to use when getMinValue() ==
    + * getMaxValue(). This determines, for example, the vertical position
    + * of the line in a flat line-chart.
    + * @type {number}
    + */
    +goog.ui.ServerChart.DEFAULT_NORMALIZATION = 0.5;
    +
    +
    +/**
    + * The upper limit on the length of the chart image URI, after encoding.
    + * If the URI's length equals or exceeds it, goog.ui.ServerChart.UriTooLongEvent
    + * is dispatched on the goog.ui.ServerChart object.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.uriLengthLimit_ = 2048;
    +
    +
    +/**
    + * Number of gridlines along the X-axis.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.gridX_ = 0;
    +
    +
    +/**
    + * Number of gridlines along the Y-axis.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.gridY_ = 0;
    +
    +
    +/**
    + * Maximum value for the chart (used for normalization). The minimum is
    + * declared in the constructor.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.maxValue_ = -Infinity;
    +
    +
    +/**
    + * Chart title.
    + * @type {?string}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.title_ = null;
    +
    +
    +/**
    + * Chart title size.
    + * @type {number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.titleSize_ = 13.5;
    +
    +
    +/**
    + * Chart title color.
    + * @type {string}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.titleColor_ = '333333';
    +
    +
    +/**
    + * Chart legend.
    + * @type {Array<string>?}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.legend_ = null;
    +
    +
    +/**
    + * ChartServer supports using data sets to position markers. A data set
    + * that is being used for positioning only can be made "invisible", in other
    + * words, the caller can indicate to ChartServer that ordinary chart elements
    + * (e.g. bars in a bar chart) should not be drawn on the data points of the
    + * invisible data set. Such data sets must be provided at the end of the
    + * chd parameter, and if invisible data sets are being used, the chd
    + * parameter must indicate the number of visible data sets.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.ServerChart.prototype.numVisibleDataSets_ = null;
    +
    +
    +/**
    + * Creates the DOM node (image) needed for the Chart
    + * @override
    + */
    +goog.ui.ServerChart.prototype.createDom = function() {
    +  var size = this.getSize();
    +  this.setElementInternal(this.getDomHelper().createDom(
    +      'img', {'src': this.getUri(),
    +        'class': goog.getCssName('goog-serverchart-image'),
    +        'width': size[0], 'height': size[1]}));
    +};
    +
    +
    +/**
    + * Decorate an image already in the DOM.
    + * Expects the following structure:
    + * <pre>
    + *   - img
    + * </pre>
    + *
    + * @param {Element} img Image to decorate.
    + * @override
    + */
    +goog.ui.ServerChart.prototype.decorateInternal = function(img) {
    +  img.src = this.getUri();
    +  this.setElementInternal(img);
    +};
    +
    +
    +/**
    + * Updates the image if any of the data or settings have changed.
    + */
    +goog.ui.ServerChart.prototype.updateChart = function() {
    +  if (this.getElement()) {
    +    this.getElement().src = this.getUri();
    +  }
    +};
    +
    +
    +/**
    + * Sets the URI of the chart.
    + *
    + * @param {goog.Uri} uri The chart URI.
    + */
    +goog.ui.ServerChart.prototype.setUri = function(uri) {
    +  this.uri_ = uri;
    +};
    +
    +
    +/**
    + * Returns the URI of the chart.
    + *
    + * @return {goog.Uri} The chart URI.
    + */
    +goog.ui.ServerChart.prototype.getUri = function() {
    +  this.computeDataString_();
    +  return this.uri_;
    +};
    +
    +
    +/**
    + * Returns the upper limit on the length of the chart image URI, after encoding.
    + * If the URI's length equals or exceeds it, goog.ui.ServerChart.UriTooLongEvent
    + * is dispatched on the goog.ui.ServerChart object.
    + *
    + * @return {number} The chart URI length limit.
    + */
    +goog.ui.ServerChart.prototype.getUriLengthLimit = function() {
    +  return this.uriLengthLimit_;
    +};
    +
    +
    +/**
    + * Sets the upper limit on the length of the chart image URI, after encoding.
    + * If the URI's length equals or exceeds it, goog.ui.ServerChart.UriTooLongEvent
    + * is dispatched on the goog.ui.ServerChart object.
    + *
    + * @param {number} uriLengthLimit The chart URI length limit.
    + */
    +goog.ui.ServerChart.prototype.setUriLengthLimit = function(uriLengthLimit) {
    +  this.uriLengthLimit_ = uriLengthLimit;
    +};
    +
    +
    +/**
    + * Sets the 'chg' parameter of the chart Uri.
    + * This is used by various types of charts to specify Grids.
    + *
    + * @param {string} value Value for the 'chg' parameter in the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.setGridParameter = function(value) {
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.GRID, value);
    +};
    +
    +
    +/**
    + * Returns the 'chg' parameter of the chart Uri.
    + * This is used by various types of charts to specify Grids.
    + *
    + * @return {string|undefined} The 'chg' parameter of the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.getGridParameter = function() {
    +  return /** @type {string} */ (
    +      this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.GRID));
    +};
    +
    +
    +/**
    + * Sets the 'chm' parameter of the chart Uri.
    + * This is used by various types of charts to specify Markers.
    + *
    + * @param {string} value Value for the 'chm' parameter in the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.setMarkerParameter = function(value) {
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.MARKERS, value);
    +};
    +
    +
    +/**
    + * Returns the 'chm' parameter of the chart Uri.
    + * This is used by various types of charts to specify Markers.
    + *
    + * @return {string|undefined} The 'chm' parameter of the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.getMarkerParameter = function() {
    +  return /** @type {string} */ (
    +      this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.MARKERS));
    +};
    +
    +
    +/**
    + * Sets the 'chp' parameter of the chart Uri.
    + * This is used by various types of charts to specify certain options.
    + * e.g., finance charts use this to designate which line is the 0 axis.
    + *
    + * @param {string|number} value Value for the 'chp' parameter in the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.setMiscParameter = function(value) {
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.MISC_PARAMS,
    +                              String(value));
    +};
    +
    +
    +/**
    + * Returns the 'chp' parameter of the chart Uri.
    + * This is used by various types of charts to specify certain options.
    + * e.g., finance charts use this to designate which line is the 0 axis.
    + *
    + * @return {string|undefined} The 'chp' parameter of the chart Uri.
    + */
    +goog.ui.ServerChart.prototype.getMiscParameter = function() {
    +  return /** @type {string} */ (
    +      this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.MISC_PARAMS));
    +};
    +
    +
    +/**
    + * Enum of chart data encoding types
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.EncodingType = {
    +  AUTOMATIC: '',
    +  EXTENDED: 'e',
    +  SIMPLE: 's',
    +  TEXT: 't'
    +};
    +
    +
    +/**
    + * Enum of chart types with their short names used by the chartserver.
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.ChartType = {
    +  BAR: 'br',
    +  CLOCK: 'cf',
    +  CONCENTRIC_PIE: 'pc',
    +  FILLEDLINE: 'lr',
    +  FINANCE: 'lfi',
    +  GOOGLEOMETER: 'gom',
    +  HORIZONTAL_GROUPED_BAR: 'bhg',
    +  HORIZONTAL_STACKED_BAR: 'bhs',
    +  LINE: 'lc',
    +  MAP: 't',
    +  MAPUSA: 'tuss',
    +  MAPWORLD: 'twoc',
    +  PIE: 'p',
    +  PIE3D: 'p3',
    +  RADAR: 'rs',
    +  SCATTER: 's',
    +  SPARKLINE: 'ls',
    +  VENN: 'v',
    +  VERTICAL_GROUPED_BAR: 'bvg',
    +  VERTICAL_STACKED_BAR: 'bvs',
    +  XYLINE: 'lxy'
    +};
    +
    +
    +/**
    + * Enum of multi-axis types.
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.MultiAxisType = {
    +  X_AXIS: 'x',
    +  LEFT_Y_AXIS: 'y',
    +  RIGHT_Y_AXIS: 'r',
    +  TOP_AXIS: 't'
    +};
    +
    +
    +/**
    + * Enum of multi-axis alignments.
    + *
    + * @enum {number}
    + */
    +goog.ui.ServerChart.MultiAxisAlignment = {
    +  ALIGN_LEFT: -1,
    +  ALIGN_CENTER: 0,
    +  ALIGN_RIGHT: 1
    +};
    +
    +
    +/**
    + * Enum of legend positions.
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.LegendPosition = {
    +  TOP: 't',
    +  BOTTOM: 'b',
    +  LEFT: 'l',
    +  RIGHT: 'r'
    +};
    +
    +
    +/**
    + * Enum of line and tick options for an axis.
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.AxisDisplayType = {
    +  LINE_AND_TICKS: 'lt',
    +  LINE: 'l',
    +  TICKS: 't'
    +};
    +
    +
    +/**
    + * Enum of chart maximum values in pixels, as listed at:
    + * http://code.google.com/apis/chart/basics.html
    + *
    + * @enum {number}
    + */
    +goog.ui.ServerChart.MaximumValue = {
    +  WIDTH: 1000,
    +  HEIGHT: 1000,
    +  MAP_WIDTH: 440,
    +  MAP_HEIGHT: 220,
    +  TOTAL_AREA: 300000
    +};
    +
    +
    +/**
    + * Enum of ChartServer URI parameters.
    + *
    + * @enum {string}
    + */
    +goog.ui.ServerChart.UriParam = {
    +  BACKGROUND_FILL: 'chf',
    +  BAR_HEIGHT: 'chbh',
    +  DATA: 'chd',
    +  DATA_COLORS: 'chco',
    +  DATA_LABELS: 'chld',
    +  DATA_SCALING: 'chds',
    +  DIGITAL_SIGNATURE: 'sig',
    +  GEOGRAPHICAL_REGION: 'chtm',
    +  GRID: 'chg',
    +  LABEL_COLORS: 'chlc',
    +  LEFT_Y_LABELS: 'chly',
    +  LEGEND: 'chdl',
    +  LEGEND_POSITION: 'chdlp',
    +  LEGEND_TEXTS: 'chdl',
    +  LINE_STYLES: 'chls',
    +  MARGINS: 'chma',
    +  MARKERS: 'chm',
    +  MISC_PARAMS: 'chp',
    +  MULTI_AXIS_LABEL_POSITION: 'chxp',
    +  MULTI_AXIS_LABEL_TEXT: 'chxl',
    +  MULTI_AXIS_RANGE: 'chxr',
    +  MULTI_AXIS_STYLE: 'chxs',
    +  MULTI_AXIS_TYPES: 'chxt',
    +  RIGHT_LABELS: 'chlr',
    +  RIGHT_LABEL_POSITIONS: 'chlrp',
    +  SIZE: 'chs',
    +  TITLE: 'chtt',
    +  TITLE_FORMAT: 'chts',
    +  TYPE: 'cht',
    +  X_AXIS_STYLE: 'chx',
    +  X_LABELS: 'chl'
    +};
    +
    +
    +/**
    + * Sets the background fill.
    + *
    + * @param {Array<Object>} fill An array of background fill specification
    + *     objects. Each object may have the following properties:
    + *     {string} area The area to fill, either 'bg' for background or 'c' for
    + *         chart area.  The default is 'bg'.
    + *     {string} color (required) The color of the background fill.
    + *     // TODO(user): Add support for gradient/stripes, which requires
    + *     // a different object structure.
    + */
    +goog.ui.ServerChart.prototype.setBackgroundFill = function(fill) {
    +  var value = [];
    +  goog.array.forEach(fill, function(spec) {
    +    spec.area = spec.area || 'bg';
    +    spec.effect = spec.effect || 's';
    +    value.push([spec.area, spec.effect, spec.color].join(','));
    +  });
    +  value = value.join('|');
    +  this.setParameterValue(goog.ui.ServerChart.UriParam.BACKGROUND_FILL, value);
    +};
    +
    +
    +/**
    + * Returns the background fill.
    + *
    + * @return {!Array<Object>} An array of background fill specifications.
    + *     If the fill specification string is in an unsupported format, the method
    + *    returns an empty array.
    + */
    +goog.ui.ServerChart.prototype.getBackgroundFill = function() {
    +  var value =
    +      this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.BACKGROUND_FILL);
    +  var result = [];
    +  if (goog.isDefAndNotNull(value)) {
    +    var fillSpecifications = value.split('|');
    +    var valid = true;
    +    goog.array.forEach(fillSpecifications, function(spec) {
    +      var parts = spec.split(',');
    +      if (valid && parts[1] == 's') {
    +        result.push({area: parts[0], effect: parts[1], color: parts[2]});
    +      } else {
    +        // If the format is unsupported, return an empty array.
    +        result = [];
    +        valid = false;
    +      }
    +    });
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * Sets the encoding type.
    + *
    + * @param {goog.ui.ServerChart.EncodingType} type Desired data encoding type.
    + */
    +goog.ui.ServerChart.prototype.setEncodingType = function(type) {
    +  this.encodingType_ = type;
    +};
    +
    +
    +/**
    + * Gets the encoding type.
    + *
    + * @return {goog.ui.ServerChart.EncodingType} The encoding type.
    + */
    +goog.ui.ServerChart.prototype.getEncodingType = function() {
    +  return this.encodingType_;
    +};
    +
    +
    +/**
    + * Sets the chart type.
    + *
    + * @param {goog.ui.ServerChart.ChartType} type The desired chart type.
    + */
    +goog.ui.ServerChart.prototype.setType = function(type) {
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.TYPE, type);
    +};
    +
    +
    +/**
    + * Returns the chart type.
    + *
    + * @return {goog.ui.ServerChart.ChartType} The chart type.
    + */
    +goog.ui.ServerChart.prototype.getType = function() {
    +  return /** @type {goog.ui.ServerChart.ChartType} */ (
    +      this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.TYPE));
    +};
    +
    +
    +/**
    + * Sets the chart size.
    + *
    + * @param {number=} opt_width Optional chart width, defaults to 300.
    + * @param {number=} opt_height Optional chart height, defaults to 150.
    + */
    +goog.ui.ServerChart.prototype.setSize = function(opt_width, opt_height) {
    +  var sizeString = [opt_width || 300, opt_height || 150].join('x');
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.SIZE, sizeString);
    +};
    +
    +
    +/**
    + * Returns the chart size.
    + *
    + * @return {!Array<string>} [Width, Height].
    + */
    +goog.ui.ServerChart.prototype.getSize = function() {
    +  var sizeStr = this.uri_.getParameterValue(goog.ui.ServerChart.UriParam.SIZE);
    +  return sizeStr.split('x');
    +};
    +
    +
    +/**
    + * Sets the minimum value of the chart.
    + *
    + * @param {number} minValue The minimum value of the chart.
    + */
    +goog.ui.ServerChart.prototype.setMinValue = function(minValue) {
    +  this.minValue_ = minValue;
    +};
    +
    +
    +/**
    + * @return {number} The minimum value of the chart.
    + */
    +goog.ui.ServerChart.prototype.getMinValue = function() {
    +  return this.minValue_;
    +};
    +
    +
    +/**
    + * Sets the maximum value of the chart.
    + *
    + * @param {number} maxValue The maximum value of the chart.
    + */
    +goog.ui.ServerChart.prototype.setMaxValue = function(maxValue) {
    +  this.maxValue_ = maxValue;
    +};
    +
    +
    +/**
    + * @return {number} The maximum value of the chart.
    + */
    +goog.ui.ServerChart.prototype.getMaxValue = function() {
    +  return this.maxValue_;
    +};
    +
    +
    +/**
    + * Sets the chart margins.
    + *
    + * @param {number} leftMargin The size in pixels of the left margin.
    + * @param {number} rightMargin The size in pixels of the right margin.
    + * @param {number} topMargin The size in pixels of the top margin.
    + * @param {number} bottomMargin The size in pixels of the bottom margin.
    + */
    +goog.ui.ServerChart.prototype.setMargins = function(leftMargin, rightMargin,
    +    topMargin, bottomMargin) {
    +  var margins = [leftMargin, rightMargin, topMargin, bottomMargin].join(',');
    +  var UriParam = goog.ui.ServerChart.UriParam;
    +  this.uri_.setParameterValue(UriParam.MARGINS, margins);
    +};
    +
    +
    +/**
    + * Sets the number of grid lines along the X-axis.
    + *
    + * @param {number} gridlines The number of X-axis grid lines.
    + */
    +goog.ui.ServerChart.prototype.setGridX = function(gridlines) {
    +  // Need data for this to work.
    +  this.gridX_ = gridlines;
    +  this.setGrids_(this.gridX_, this.gridY_);
    +};
    +
    +
    +/**
    + * @return {number} The number of gridlines along the X-axis.
    + */
    +goog.ui.ServerChart.prototype.getGridX = function() {
    +  return this.gridX_;
    +};
    +
    +
    +/**
    + * Sets the number of grid lines along the Y-axis.
    + *
    + * @param {number} gridlines The number of Y-axis grid lines.
    + */
    +goog.ui.ServerChart.prototype.setGridY = function(gridlines) {
    +  // Need data for this to work.
    +  this.gridY_ = gridlines;
    +  this.setGrids_(this.gridX_, this.gridY_);
    +};
    +
    +
    +/**
    + * @return {number} The number of gridlines along the Y-axis.
    + */
    +goog.ui.ServerChart.prototype.getGridY = function() {
    +  return this.gridY_;
    +};
    +
    +
    +/**
    + * Sets the grids for the chart
    + *
    + * @private
    + * @param {number} x The number of grid lines along the x-axis.
    + * @param {number} y The number of grid lines along the y-axis.
    + */
    +goog.ui.ServerChart.prototype.setGrids_ = function(x, y) {
    +  var gridArray = [x == 0 ? 0 : 100 / x,
    +                   y == 0 ? 0 : 100 / y];
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.GRID,
    +                              gridArray.join(','));
    +};
    +
    +
    +/**
    + * Sets the X Labels for the chart.
    + *
    + * @param {Array<string>} labels The X Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.setXLabels = function(labels) {
    +  this.xLabels_ = labels;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.X_LABELS,
    +                              this.xLabels_.join('|'));
    +};
    +
    +
    +/**
    + * @return {Array<string>} The X Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.getXLabels = function() {
    +  return this.xLabels_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a bar chart.
    + */
    +goog.ui.ServerChart.prototype.isBarChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.BAR ||
    +      type == goog.ui.ServerChart.ChartType.HORIZONTAL_GROUPED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.VERTICAL_GROUPED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a pie chart.
    + */
    +goog.ui.ServerChart.prototype.isPieChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.PIE ||
    +      type == goog.ui.ServerChart.ChartType.PIE3D ||
    +      type == goog.ui.ServerChart.ChartType.CONCENTRIC_PIE;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a grouped bar chart.
    + */
    +goog.ui.ServerChart.prototype.isGroupedBarChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.HORIZONTAL_GROUPED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.VERTICAL_GROUPED_BAR;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a horizontal bar chart.
    + */
    +goog.ui.ServerChart.prototype.isHorizontalBarChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.BAR ||
    +      type == goog.ui.ServerChart.ChartType.HORIZONTAL_GROUPED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a line chart.
    + */
    +goog.ui.ServerChart.prototype.isLineChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.FILLEDLINE ||
    +      type == goog.ui.ServerChart.ChartType.LINE ||
    +      type == goog.ui.ServerChart.ChartType.SPARKLINE ||
    +      type == goog.ui.ServerChart.ChartType.XYLINE;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a map.
    + */
    +goog.ui.ServerChart.prototype.isMap = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.MAP ||
    +      type == goog.ui.ServerChart.ChartType.MAPUSA ||
    +      type == goog.ui.ServerChart.ChartType.MAPWORLD;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a stacked bar chart.
    + */
    +goog.ui.ServerChart.prototype.isStackedBarChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.BAR ||
    +      type == goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the chart is a vertical bar chart.
    + */
    +goog.ui.ServerChart.prototype.isVerticalBarChart = function() {
    +  var type = this.getType();
    +  return type == goog.ui.ServerChart.ChartType.VERTICAL_GROUPED_BAR ||
    +      type == goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR;
    +};
    +
    +
    +/**
    + * Sets the Left Labels for the chart.
    + * NOTE: The array should start with the lowest value, and then
    + *       move progessively up the axis. So if you want labels
    + *       from 0 to 100 with 0 at bottom of the graph, then you would
    + *       want to pass something like [0,25,50,75,100].
    + *
    + * @param {Array<string>} labels The Left Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.setLeftLabels = function(labels) {
    +  this.leftLabels_ = labels;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.LEFT_Y_LABELS,
    +                              this.leftLabels_.reverse().join('|'));
    +};
    +
    +
    +/**
    + * @return {Array<string>} The Left Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.getLeftLabels = function() {
    +  return this.leftLabels_;
    +};
    +
    +
    +/**
    + * Sets the given ChartServer parameter.
    + *
    + * @param {goog.ui.ServerChart.UriParam} key The ChartServer parameter to set.
    + * @param {string} value The value to set for the ChartServer parameter.
    + */
    +goog.ui.ServerChart.prototype.setParameterValue = function(key, value) {
    +  this.uri_.setParameterValue(key, value);
    +};
    +
    +
    +/**
    + * Removes the given ChartServer parameter.
    + *
    + * @param {goog.ui.ServerChart.UriParam} key The ChartServer parameter to
    + *     remove.
    + */
    +goog.ui.ServerChart.prototype.removeParameter = function(key) {
    +  this.uri_.removeParameter(key);
    +};
    +
    +
    +/**
    + * Sets the Right Labels for the chart.
    + * NOTE: The array should start with the lowest value, and then
    + *       move progessively up the axis. So if you want labels
    + *       from 0 to 100 with 0 at bottom of the graph, then you would
    + *       want to pass something like [0,25,50,75,100].
    + *
    + * @param {Array<string>} labels The Right Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.setRightLabels = function(labels) {
    +  this.rightLabels_ = labels;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.RIGHT_LABELS,
    +                              this.rightLabels_.reverse().join('|'));
    +};
    +
    +
    +/**
    + * @return {Array<string>} The Right Labels for the chart.
    + */
    +goog.ui.ServerChart.prototype.getRightLabels = function() {
    +  return this.rightLabels_;
    +};
    +
    +
    +/**
    + * Sets the position relative to the chart where the legend is to be displayed.
    + *
    + * @param {goog.ui.ServerChart.LegendPosition} value Legend position.
    + */
    +goog.ui.ServerChart.prototype.setLegendPosition = function(value) {
    +  this.uri_.setParameterValue(
    +      goog.ui.ServerChart.UriParam.LEGEND_POSITION, value);
    +};
    +
    +
    +/**
    + * Returns the position relative to the chart where the legend is to be
    + * displayed.
    + *
    + * @return {goog.ui.ServerChart.LegendPosition} Legend position.
    + */
    +goog.ui.ServerChart.prototype.getLegendPosition = function() {
    +  return /** @type {goog.ui.ServerChart.LegendPosition} */ (
    +      this.uri_.getParameterValue(
    +          goog.ui.ServerChart.UriParam.LEGEND_POSITION));
    +};
    +
    +
    +/**
    + * Sets the number of "visible" data sets. All data sets that come after
    + * the visible data set are not drawn as part of the chart. Instead, they
    + * are available for positioning markers.
    +
    + * @param {?number} n The number of visible data sets, or null if all data
    + * sets are to be visible.
    + */
    +goog.ui.ServerChart.prototype.setNumVisibleDataSets = function(n) {
    +  this.numVisibleDataSets_ = n;
    +};
    +
    +
    +/**
    + * Returns the number of "visible" data sets. All data sets that come after
    + * the visible data set are not drawn as part of the chart. Instead, they
    + * are available for positioning markers.
    + *
    + * @return {?number} The number of visible data sets, or null if all data
    + * sets are visible.
    + */
    +goog.ui.ServerChart.prototype.getNumVisibleDataSets = function() {
    +  return this.numVisibleDataSets_;
    +};
    +
    +
    +/**
    + * Sets the weight function for a Venn Diagram along with the associated
    + *     colors and legend text. Weights are assigned as follows:
    + *     weights[0] is relative area of circle A.
    + *     weights[1] is relative area of circle B.
    + *     weights[2] is relative area of circle C.
    + *     weights[3] is relative area of overlap of circles A and B.
    + *     weights[4] is relative area of overlap of circles A and C.
    + *     weights[5] is relative area of overlap of circles B and C.
    + *     weights[6] is relative area of overlap of circles A, B and C.
    + * For a two circle Venn Diagram the weights are assigned as follows:
    + *     weights[0] is relative area of circle A.
    + *     weights[1] is relative area of circle B.
    + *     weights[2] is relative area of overlap of circles A and B.
    + *
    + * @param {Array<number>} weights The relative weights of the circles.
    + * @param {Array<string>=} opt_legendText The legend labels for the circles.
    + * @param {Array<string>=} opt_colors The colors for the circles.
    + */
    +goog.ui.ServerChart.prototype.setVennSeries = function(
    +    weights, opt_legendText, opt_colors) {
    +  if (this.getType() != goog.ui.ServerChart.ChartType.VENN) {
    +    throw Error('Can only set a weight function for a Venn diagram.');
    +  }
    +  var dataMin = this.arrayMin_(weights);
    +  if (dataMin < this.minValue_) {
    +    this.minValue_ = dataMin;
    +  }
    +  var dataMax = this.arrayMax_(weights);
    +  if (dataMax > this.maxValue_) {
    +    this.maxValue_ = dataMax;
    +  }
    +  if (goog.isDef(opt_legendText)) {
    +    goog.array.forEach(
    +        opt_legendText,
    +        goog.bind(function(legend) {
    +          this.setLegendTexts_.push(legend);
    +        }, this));
    +    this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.LEGEND_TEXTS,
    +                                this.setLegendTexts_.join('|'));
    +  }
    +  // If the caller only gave three weights, then they wanted a two circle
    +  // Venn Diagram. Create a 3 circle weight function where circle C has
    +  // area zero.
    +  if (weights.length == 3) {
    +    weights[3] = weights[2];
    +    weights[2] = 0.0;
    +  }
    +  this.dataSets_.push(weights);
    +  if (goog.isDef(opt_colors)) {
    +    goog.array.forEach(opt_colors, goog.bind(function(color) {
    +      this.setColors_.push(color);
    +    }, this));
    +    this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.DATA_COLORS,
    +                                this.setColors_.join(','));
    +  }
    +};
    +
    +
    +/**
    + * Sets the title of the chart.
    + *
    + * @param {string} title The chart title.
    + */
    +goog.ui.ServerChart.prototype.setTitle = function(title) {
    +  this.title_ = title;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.TITLE,
    +                              this.title_.replace(/\n/g, '|'));
    +};
    +
    +
    +/**
    + * Sets the size of the chart title.
    + *
    + * @param {number} size The title size, in points.
    + */
    +goog.ui.ServerChart.prototype.setTitleSize = function(size) {
    +  this.titleSize_ = size;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.TITLE_FORMAT,
    +                              this.titleColor_ + ',' + this.titleSize_);
    +};
    +
    +
    +/**
    + * @return {number} size The title size, in points.
    + */
    +goog.ui.ServerChart.prototype.getTitleSize = function() {
    +  return this.titleSize_;
    +};
    +
    +
    +/**
    + * Sets the color of the chart title.
    + *
    + * NOTE: The color string should NOT have a '#' at the beginning of it.
    + *
    + * @param {string} color The hex value for the title color.
    + */
    +goog.ui.ServerChart.prototype.setTitleColor = function(color) {
    +  this.titleColor_ = color;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.TITLE_FORMAT,
    +                              this.titleColor_ + ',' + this.titleSize_);
    +};
    +
    +
    +/**
    + * @return {string} color The hex value for the title color.
    + */
    +goog.ui.ServerChart.prototype.getTitleColor = function() {
    +  return this.titleColor_;
    +};
    +
    +
    +/**
    + * Adds a legend to the chart.
    + *
    + * @param {Array<string>} legend The legend to add.
    + */
    +goog.ui.ServerChart.prototype.setLegend = function(legend) {
    +  this.legend_ = legend;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.LEGEND,
    +                              this.legend_.join('|'));
    +};
    +
    +
    +/**
    + * Sets the data scaling.
    + * NOTE: This also changes the encoding type because data scaling will
    + *     only work with {@code goog.ui.ServerChart.EncodingType.TEXT}
    + *     encoding.
    + * @param {number} minimum The lowest number to apply to the data.
    + * @param {number} maximum The highest number to apply to the data.
    + */
    +goog.ui.ServerChart.prototype.setDataScaling = function(minimum, maximum) {
    +  this.encodingType_ = goog.ui.ServerChart.EncodingType.TEXT;
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.DATA_SCALING,
    +                              minimum + ',' + maximum);
    +};
    +
    +
    +/**
    + * Sets the widths of the bars and the spaces between the bars in a bar
    + * chart.
    + * NOTE: If the space between groups is specified but the space between
    + *     bars is left undefined, the space between groups will be interpreted
    + *     as the space between bars because this is the behavior exposed
    + *     in the external developers guide.
    + * @param {number} barWidth The width of a bar in pixels.
    + * @param {number=} opt_spaceBars The width of the space between
    + *     bars in a group in pixels.
    + * @param {number=} opt_spaceGroups The width of the space between
    + *     groups.
    + */
    +goog.ui.ServerChart.prototype.setBarSpaceWidths = function(barWidth,
    +                                                           opt_spaceBars,
    +                                                           opt_spaceGroups) {
    +  var widths = [barWidth];
    +  if (goog.isDef(opt_spaceBars)) {
    +    widths.push(opt_spaceBars);
    +  }
    +  if (goog.isDef(opt_spaceGroups)) {
    +    widths.push(opt_spaceGroups);
    +  }
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT,
    +                              widths.join(','));
    +};
    +
    +
    +/**
    + * Specifies that the bar width in a bar chart should be calculated
    + * automatically given the space available in the chart, while optionally
    + * setting the spaces between the bars.
    + * NOTE: If the space between groups is specified but the space between
    + *     bars is left undefined, the space between groups will be interpreted
    + *     as the space between bars because this is the behavior exposed
    + *     in the external developers guide.
    + * @param {number=} opt_spaceBars The width of the space between
    + *     bars in a group in pixels.
    + * @param {number=} opt_spaceGroups The width of the space between
    + *     groups.
    + */
    +goog.ui.ServerChart.prototype.setAutomaticBarWidth = function(opt_spaceBars,
    +                                                              opt_spaceGroups) {
    +  var widths = ['a'];
    +  if (goog.isDef(opt_spaceBars)) {
    +    widths.push(opt_spaceBars);
    +  }
    +  if (goog.isDef(opt_spaceGroups)) {
    +    widths.push(opt_spaceGroups);
    +  }
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT,
    +                              widths.join(','));
    +};
    +
    +
    +/**
    + * Adds a multi-axis to the chart, and sets its type. Multiple axes of the same
    + * type can be added.
    + *
    + * @param {goog.ui.ServerChart.MultiAxisType} axisType The desired axis type.
    + * @return {number} The index of the newly inserted axis, suitable for feeding
    + *     to the setMultiAxis*() functions.
    + */
    +goog.ui.ServerChart.prototype.addMultiAxis = function(axisType) {
    +  this.multiAxisType_.push(axisType);
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.MULTI_AXIS_TYPES,
    +                              this.multiAxisType_.join(','));
    +  return this.multiAxisType_.length - 1;
    +};
    +
    +
    +/**
    + * Returns the axis type for the given axis, or all of them in an array if the
    + * axis number is not given.
    + *
    + * @param {number=} opt_axisNumber The axis index, as returned by addMultiAxis.
    + * @return {goog.ui.ServerChart.MultiAxisType|
    + *     Array<goog.ui.ServerChart.MultiAxisType>}
    + *     The axis type for the given axis, or all of them in an array if the
    + *     axis number is not given.
    + */
    +goog.ui.ServerChart.prototype.getMultiAxisType = function(opt_axisNumber) {
    +  if (goog.isDef(opt_axisNumber)) {
    +    return this.multiAxisType_[opt_axisNumber];
    +  }
    +  return this.multiAxisType_;
    +};
    +
    +
    +/**
    + * Sets the label text (usually multiple values) for a given axis, overwriting
    + * any existing values.
    + *
    + * @param {number} axisNumber The axis index, as returned by addMultiAxis.
    + * @param {Array<string>} labelText The actual label text to be added.
    + */
    +goog.ui.ServerChart.prototype.setMultiAxisLabelText = function(axisNumber,
    +                                                               labelText) {
    +  this.multiAxisLabelText_[axisNumber] = labelText;
    +
    +  var axisString = this.computeMultiAxisDataString_(this.multiAxisLabelText_,
    +                                                    ':|',
    +                                                    '|',
    +                                                    '|');
    +  this.uri_.setParameterValue(
    +      goog.ui.ServerChart.UriParam.MULTI_AXIS_LABEL_TEXT,
    +      axisString);
    +};
    +
    +
    +/**
    + * Returns the label text, or all of them in a two-dimensional array if the
    + * axis number is not given.
    + *
    + * @param {number=} opt_axisNumber The axis index, as returned by addMultiAxis.
    + * @return {Object|Array<string>} The label text, or all of them in a
    + *     two-dimensional array if the axis number is not given.
    + */
    +goog.ui.ServerChart.prototype.getMultiAxisLabelText = function(opt_axisNumber) {
    +  if (goog.isDef(opt_axisNumber)) {
    +    return this.multiAxisLabelText_[opt_axisNumber];
    +  }
    +  return this.multiAxisLabelText_;
    +};
    +
    +
    +/**
    + * Sets the label positions for a given axis, overwriting any existing values.
    + * The label positions are assumed to be floating-point numbers within the
    + * range of the axis.
    + *
    + * @param {number} axisNumber The axis index, as returned by addMultiAxis.
    + * @param {Array<number>} labelPosition The actual label positions to be added.
    + */
    +goog.ui.ServerChart.prototype.setMultiAxisLabelPosition = function(
    +    axisNumber, labelPosition) {
    +  this.multiAxisLabelPosition_[axisNumber] = labelPosition;
    +
    +  var positionString = this.computeMultiAxisDataString_(
    +      this.multiAxisLabelPosition_,
    +      ',',
    +      ',',
    +      '|');
    +  this.uri_.setParameterValue(
    +      goog.ui.ServerChart.UriParam.MULTI_AXIS_LABEL_POSITION,
    +      positionString);
    +};
    +
    +
    +/**
    + * Returns the label positions for a given axis number, or all of them in a
    + * two-dimensional array if the axis number is not given.
    + *
    + * @param {number=} opt_axisNumber The axis index, as returned by addMultiAxis.
    + * @return {Object|Array<number>} The label positions for a given axis number,
    + *     or all of them in a two-dimensional array if the axis number is not
    + *     given.
    + */
    +goog.ui.ServerChart.prototype.getMultiAxisLabelPosition =
    +    function(opt_axisNumber) {
    +  if (goog.isDef(opt_axisNumber)) {
    +    return this.multiAxisLabelPosition_[opt_axisNumber];
    +  }
    +  return this.multiAxisLabelPosition_;
    +};
    +
    +
    +/**
    + * Sets the label range for a given axis, overwriting any existing range.
    + * The default range is from 0 to 100. If the start value is larger than the
    + * end value, the axis direction is reversed.  rangeStart and rangeEnd must
    + * be two different finite numbers.
    + *
    + * @param {number} axisNumber The axis index, as returned by addMultiAxis.
    + * @param {number} rangeStart The new start of the range.
    + * @param {number} rangeEnd The new end of the range.
    + * @param {number=} opt_interval The interval between axis labels.
    + */
    +goog.ui.ServerChart.prototype.setMultiAxisRange = function(axisNumber,
    +                                                           rangeStart,
    +                                                           rangeEnd,
    +                                                           opt_interval) {
    +  goog.asserts.assert(rangeStart != rangeEnd,
    +      'Range start and end cannot be the same value.');
    +  goog.asserts.assert(isFinite(rangeStart) && isFinite(rangeEnd),
    +      'Range start and end must be finite numbers.');
    +  this.multiAxisRange_[axisNumber] = [rangeStart, rangeEnd];
    +  if (goog.isDef(opt_interval)) {
    +    this.multiAxisRange_[axisNumber].push(opt_interval);
    +  }
    +  var rangeString = this.computeMultiAxisDataString_(this.multiAxisRange_,
    +      ',', ',', '|');
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.MULTI_AXIS_RANGE,
    +      rangeString);
    +};
    +
    +
    +/**
    + * Returns the label range for a given axis number as a two-element array of
    + * (range start, range end), or all of them in a two-dimensional array if the
    + * axis number is not given.
    + *
    + * @param {number=} opt_axisNumber The axis index, as returned by addMultiAxis.
    + * @return {Object|Array<number>} The label range for a given axis number as a
    + *     two-element array of (range start, range end), or all of them in a
    + *     two-dimensional array if the axis number is not given.
    + */
    +goog.ui.ServerChart.prototype.getMultiAxisRange = function(opt_axisNumber) {
    +  if (goog.isDef(opt_axisNumber)) {
    +    return this.multiAxisRange_[opt_axisNumber];
    +  }
    +  return this.multiAxisRange_;
    +};
    +
    +
    +/**
    + * Sets the label style for a given axis, overwriting any existing style.
    + * The default style is as follows: Default is x-axis labels are centered, left
    + * hand y-axis labels are right aligned, right hand y-axis labels are left
    + * aligned. The font size and alignment are optional parameters.
    + *
    + * NOTE: The color string should NOT have a '#' at the beginning of it.
    + *
    + * @param {number} axisNumber The axis index, as returned by addMultiAxis.
    + * @param {string} color The hex value for this label's color.
    + * @param {number=} opt_fontSize The label font size, in pixels.
    + * @param {goog.ui.ServerChart.MultiAxisAlignment=} opt_alignment The label
    + *     alignment.
    + * @param {goog.ui.ServerChart.AxisDisplayType=} opt_axisDisplay The axis
    + *     line and ticks.
    + */
    +goog.ui.ServerChart.prototype.setMultiAxisLabelStyle = function(
    +    axisNumber, color, opt_fontSize, opt_alignment, opt_axisDisplay) {
    +  var style = [color];
    +  if (goog.isDef(opt_fontSize) || goog.isDef(opt_alignment)) {
    +    style.push(opt_fontSize || '');
    +  }
    +  if (goog.isDef(opt_alignment)) {
    +    style.push(opt_alignment);
    +  }
    +  if (opt_axisDisplay) {
    +    style.push(opt_axisDisplay);
    +  }
    +  this.multiAxisLabelStyle_[axisNumber] = style;
    +  var styleString = this.computeMultiAxisDataString_(this.multiAxisLabelStyle_,
    +                                                     ',',
    +                                                     ',',
    +                                                     '|');
    +  this.uri_.setParameterValue(
    +      goog.ui.ServerChart.UriParam.MULTI_AXIS_STYLE,
    +      styleString);
    +};
    +
    +
    +/**
    + * Returns the label style for a given axis number as a one- to three-element
    + * array, or all of them in a two-dimensional array if the axis number is not
    + * given.
    + *
    + * @param {number=} opt_axisNumber The axis index, as returned by addMultiAxis.
    + * @return {Object|Array<number>} The label style for a given axis number as a
    + *     one- to three-element array, or all of them in a two-dimensional array if
    + *     the axis number is not given.
    + */
    +goog.ui.ServerChart.prototype.getMultiAxisLabelStyle =
    +    function(opt_axisNumber) {
    +  if (goog.isDef(opt_axisNumber)) {
    +    return this.multiAxisLabelStyle_[opt_axisNumber];
    +  }
    +  return this.multiAxisLabelStyle_;
    +};
    +
    +
    +/**
    + * Adds a data set.
    + * NOTE: The color string should NOT have a '#' at the beginning of it.
    + *
    + * @param {Array<number|null>} data An array of numbers (values can be
    + *     NaN or null).
    + * @param {string} color The hex value for this data set's color.
    + * @param {string=} opt_legendText The legend text, if any, for this data
    + *     series. NOTE: If specified, all previously added data sets must also
    + *     have a legend text.
    + */
    +goog.ui.ServerChart.prototype.addDataSet = function(data,
    +                                                    color,
    +                                                    opt_legendText) {
    +  var dataMin = this.arrayMin_(data);
    +  if (dataMin < this.minValue_) {
    +    this.minValue_ = dataMin;
    +  }
    +
    +  var dataMax = this.arrayMax_(data);
    +  if (dataMax > this.maxValue_) {
    +    this.maxValue_ = dataMax;
    +  }
    +
    +  if (goog.isDef(opt_legendText)) {
    +    if (this.setLegendTexts_.length < this.dataSets_.length) {
    +      throw Error('Cannot start adding legends text after first element.');
    +    }
    +    this.setLegendTexts_.push(opt_legendText);
    +    this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.LEGEND_TEXTS,
    +                                this.setLegendTexts_.join('|'));
    +  }
    +
    +  this.dataSets_.push(data);
    +  this.setColors_.push(color);
    +
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.DATA_COLORS,
    +                              this.setColors_.join(','));
    +};
    +
    +
    +/**
    + * Clears the data sets from the graph. All data, including the colors and
    + * legend text, is cleared.
    + */
    +goog.ui.ServerChart.prototype.clearDataSets = function() {
    +  var queryData = this.uri_.getQueryData();
    +  queryData.remove(goog.ui.ServerChart.UriParam.LEGEND_TEXTS);
    +  queryData.remove(goog.ui.ServerChart.UriParam.DATA_COLORS);
    +  queryData.remove(goog.ui.ServerChart.UriParam.DATA);
    +  this.setLegendTexts_.length = 0;
    +  this.setColors_.length = 0;
    +  this.dataSets_.length = 0;
    +};
    +
    +
    +/**
    + * Returns the given data set or all of them in a two-dimensional array if
    + * the set number is not given.
    + *
    + * @param {number=} opt_setNumber Optional data set number to get.
    + * @return {Array<?>} The given data set or all of them in a two-dimensional
    + *     array if the set number is not given.
    + */
    +goog.ui.ServerChart.prototype.getData = function(opt_setNumber) {
    +  if (goog.isDef(opt_setNumber)) {
    +    return this.dataSets_[opt_setNumber];
    +  }
    +  return this.dataSets_;
    +};
    +
    +
    +/**
    + * Computes the data string using the data in this.dataSets_ and sets
    + * the object's URI accordingly. If the URI's length equals or exceeds the
    + * limit, goog.ui.ServerChart.UriTooLongEvent is dispatched on the
    + * goog.ui.ServerChart object.
    + * @private
    + */
    +goog.ui.ServerChart.prototype.computeDataString_ = function() {
    +  var ok;
    +  if (this.encodingType_ != goog.ui.ServerChart.EncodingType.AUTOMATIC) {
    +    ok = this.computeDataStringForEncoding_(this.encodingType_);
    +  } else {
    +    ok = this.computeDataStringForEncoding_(
    +        goog.ui.ServerChart.EncodingType.EXTENDED);
    +    if (!ok) {
    +      ok = this.computeDataStringForEncoding_(
    +          goog.ui.ServerChart.EncodingType.SIMPLE);
    +    }
    +  }
    +  if (!ok) {
    +    this.dispatchEvent(
    +        new goog.ui.ServerChart.UriTooLongEvent(this.uri_.toString()));
    +  }
    +};
    +
    +
    +/**
    + * Computes the data string using the data in this.dataSets_ and the encoding
    + * specified by the encoding parameter, which must not be AUTOMATIC, and sets
    + * the object's URI accordingly.
    + * @param {goog.ui.ServerChart.EncodingType} encoding The data encoding to use;
    + *     must not be AUTOMATIC.
    + * @return {boolean} False if the resulting URI is too long.
    + * @private
    + */
    +goog.ui.ServerChart.prototype.computeDataStringForEncoding_ = function(
    +    encoding) {
    +  var dataStrings = [];
    +  for (var i = 0, setLen = this.dataSets_.length; i < setLen; ++i) {
    +    dataStrings[i] = this.getChartServerValues_(this.dataSets_[i],
    +                                                this.minValue_,
    +                                                this.maxValue_,
    +                                                encoding);
    +  }
    +  var delimiter = encoding == goog.ui.ServerChart.EncodingType.TEXT ? '|' : ',';
    +  dataStrings = dataStrings.join(delimiter);
    +  var data;
    +  if (this.numVisibleDataSets_ == null) {
    +    data = goog.string.buildString(encoding, ':', dataStrings);
    +  } else {
    +    data = goog.string.buildString(encoding, this.numVisibleDataSets_, ':',
    +        dataStrings);
    +  }
    +  this.uri_.setParameterValue(goog.ui.ServerChart.UriParam.DATA, data);
    +  return this.uri_.toString().length < this.uriLengthLimit_;
    +};
    +
    +
    +/**
    + * Computes a multi-axis data string from the given data and separators. The
    + * general data format for each index/element in the array will be
    + * "<arrayIndex><indexSeparator><arrayElement.join(elementSeparator)>", with
    + * axisSeparator used between multiple elements.
    + * @param {Object} data The data to compute the data string for, as a
    + *     sparse array of arrays. NOTE: The function uses the length of
    + *     multiAxisType_ to determine the upper bound for the outer array.
    + * @param {string} indexSeparator The separator string inserted between each
    + *     index and the data itself, commonly a comma (,).
    + * @param {string} elementSeparator The separator string inserted between each
    + *     element inside each sub-array in the data, if there are more than one;
    + *     commonly a comma (,).
    + * @param {string} axisSeparator The separator string inserted between each
    + *     axis specification, if there are more than one; usually a pipe sign (|).
    + * @return {string} The multi-axis data string.
    + * @private
    + */
    +goog.ui.ServerChart.prototype.computeMultiAxisDataString_ = function(
    +    data,
    +    indexSeparator,
    +    elementSeparator,
    +    axisSeparator) {
    +  var elementStrings = [];
    +  for (var i = 0, setLen = this.multiAxisType_.length; i < setLen; ++i) {
    +    if (data[i]) {
    +      elementStrings.push(i + indexSeparator + data[i].join(elementSeparator));
    +    }
    +  }
    +  return elementStrings.join(axisSeparator);
    +};
    +
    +
    +/**
    + * Array of possible ChartServer data values
    + * @type {string}
    + */
    +goog.ui.ServerChart.CHART_VALUES = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
    +                                   'abcdefghijklmnopqrstuvwxyz' +
    +                                   '0123456789';
    +
    +
    +/**
    + * Array of extended ChartServer data values
    + * @type {string}
    + */
    +goog.ui.ServerChart.CHART_VALUES_EXTENDED = goog.ui.ServerChart.CHART_VALUES +
    +                                            '-.';
    +
    +
    +/**
    + * Upper bound for extended values
    + */
    +goog.ui.ServerChart.EXTENDED_UPPER_BOUND =
    +    Math.pow(goog.ui.ServerChart.CHART_VALUES_EXTENDED.length, 2) - 1;
    +
    +
    +/**
    + * Converts a single number to an encoded data value suitable for ChartServer.
    + * The TEXT encoding is the number in decimal; the SIMPLE encoding is a single
    + * character, and the EXTENDED encoding is two characters.  See
    + * https://developers.google.com/chart/image/docs/data_formats for the detailed
    + * specification of these encoding formats.
    + *
    + * @private
    + * @param {?number} value The value to convert (null for a missing data point).
    + * @param {number} minValue The minimum value (used for normalization).
    + * @param {number} maxValue The maximum value (used for normalization).
    + * @param {goog.ui.ServerChart.EncodingType} encoding The data encoding to use;
    + *     must not be AUTOMATIC.
    + * @return {string} The encoded data value.
    + */
    +goog.ui.ServerChart.prototype.getConvertedValue_ = function(value,
    +                                                            minValue,
    +                                                            maxValue,
    +                                                            encoding) {
    +  goog.asserts.assert(minValue <= maxValue,
    +      'minValue should be less than or equal to maxValue');
    +  var isExtended = (encoding == goog.ui.ServerChart.EncodingType.EXTENDED);
    +
    +  if (goog.isNull(value) || !goog.isDef(value) || isNaN(value) ||
    +      value < minValue || value > maxValue) {
    +    return isExtended ? '__' : '_';
    +  }
    +
    +  if (encoding == goog.ui.ServerChart.EncodingType.TEXT) {
    +    return String(value);
    +  }
    +
    +  var frac = goog.ui.ServerChart.DEFAULT_NORMALIZATION;
    +  if (maxValue > minValue) {
    +    frac = (value - minValue) / (maxValue - minValue);
    +    // Previous checks of value ensure that 0 <= frac <= 1 at this point.
    +  }
    +
    +  if (isExtended) {
    +    var maxIndex = goog.ui.ServerChart.CHART_VALUES_EXTENDED.length;
    +    var upperBound = goog.ui.ServerChart.EXTENDED_UPPER_BOUND;
    +    var index1 = Math.floor(frac * upperBound / maxIndex);
    +    var index2 = Math.floor((frac * upperBound) % maxIndex);
    +    var extendedVals = goog.ui.ServerChart.CHART_VALUES_EXTENDED;
    +    return extendedVals.charAt(index1) + extendedVals.charAt(index2);
    +  }
    +
    +  var index = Math.round(frac * (goog.ui.ServerChart.CHART_VALUES.length - 1));
    +  return goog.ui.ServerChart.CHART_VALUES.charAt(index);
    +};
    +
    +
    +/**
    + * Creates the chd string for chartserver.
    + *
    + * @private
    + * @param {Array<number>} values An array of numbers to graph.
    + * @param {number} minValue The minimum value (used for normalization).
    + * @param {number} maxValue The maximum value (used for normalization).
    + * @param {goog.ui.ServerChart.EncodingType} encoding The data encoding to use;
    + *     must not be AUTOMATIC.
    + * @return {string} The chd string for chartserver.
    + */
    +goog.ui.ServerChart.prototype.getChartServerValues_ = function(values,
    +                                                               minValue,
    +                                                               maxValue,
    +                                                               encoding) {
    +  var s = [];
    +  for (var i = 0, valuesLen = values.length; i < valuesLen; ++i) {
    +    s.push(this.getConvertedValue_(values[i], minValue,
    +                                   maxValue, encoding));
    +  }
    +  return s.join(
    +      this.encodingType_ == goog.ui.ServerChart.EncodingType.TEXT ? ',' : '');
    +};
    +
    +
    +/**
    + * Finds the minimum value in an array and returns it.
    + * Needed because Math.min does not handle sparse arrays the way we want.
    + *
    + * @param {Array<number?>} ary An array of values.
    + * @return {number} The minimum value.
    + * @private
    + */
    +goog.ui.ServerChart.prototype.arrayMin_ = function(ary) {
    +  var min = Infinity;
    +  for (var i = 0, aryLen = ary.length; i < aryLen; ++i) {
    +    var value = ary[i];
    +    if (value != null && value < min) {
    +      min = value;
    +    }
    +  }
    +  return min;
    +};
    +
    +
    +/**
    + * Finds the maximum value in an array and returns it.
    + * Needed because Math.max does not handle sparse arrays the way we want.
    + *
    + * @param {Array<number?>} ary An array of values.
    + * @return {number} The maximum value.
    + * @private
    + */
    +goog.ui.ServerChart.prototype.arrayMax_ = function(ary) {
    +  var max = -Infinity;
    +  for (var i = 0, aryLen = ary.length; i < aryLen; ++i) {
    +    var value = ary[i];
    +    if (value != null && value > max) {
    +      max = value;
    +    }
    +  }
    +  return max;
    +};
    +
    +
    +/** @override */
    +goog.ui.ServerChart.prototype.disposeInternal = function() {
    +  goog.ui.ServerChart.superClass_.disposeInternal.call(this);
    +  delete this.xLabels_;
    +  delete this.leftLabels_;
    +  delete this.rightLabels_;
    +  delete this.gridX_;
    +  delete this.gridY_;
    +  delete this.setColors_;
    +  delete this.setLegendTexts_;
    +  delete this.dataSets_;
    +  this.uri_ = null;
    +  delete this.minValue_;
    +  delete this.maxValue_;
    +  this.title_ = null;
    +  delete this.multiAxisType_;
    +  delete this.multiAxisLabelText_;
    +  delete this.multiAxisLabelPosition_;
    +  delete this.multiAxisRange_;
    +  delete this.multiAxisLabelStyle_;
    +  this.legend_ = null;
    +};
    +
    +
    +/**
    + * Event types dispatched by the ServerChart object
    + * @enum {string}
    + */
    +goog.ui.ServerChart.Event = {
    +  /**
    +   * Dispatched when the resulting URI reaches or exceeds the URI length limit.
    +   */
    +  URI_TOO_LONG: 'uritoolong'
    +};
    +
    +
    +
    +/**
    + * Class for the event dispatched on the ServerChart when the resulting URI
    + * exceeds the URI length limit.
    + * @constructor
    + * @param {string} uri The overly-long URI string.
    + * @extends {goog.events.Event}
    + * @final
    + */
    +goog.ui.ServerChart.UriTooLongEvent = function(uri) {
    +  goog.events.Event.call(this, goog.ui.ServerChart.Event.URI_TOO_LONG);
    +
    +  /**
    +   * The overly-long URI string.
    +   * @type {string}
    +   */
    +  this.uri = uri;
    +};
    +goog.inherits(goog.ui.ServerChart.UriTooLongEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.html b/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.html
    new file mode 100644
    index 0000000000..09ca81494c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.serverchart
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ServerChartTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.js b/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.js
    new file mode 100644
    index 0000000000..ef75fa9319
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/serverchart_test.js
    @@ -0,0 +1,635 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ServerChartTest');
    +goog.setTestOnly('goog.ui.ServerChartTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.ServerChart');
    +
    +function testSchemeIndependentBarChartRequest() {
    +  var bar = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR,
    +      180,
    +      104,
    +      null);
    +  tryToCreateBarChart(bar);
    +  var uri = bar.getUri();
    +  var schemeIndependentUri = new goog.Uri(
    +      goog.ui.ServerChart.CHART_SERVER_SCHEME_INDEPENDENT_URI);
    +  assertEquals('', uri.getScheme());
    +  assertEquals(schemeIndependentUri.getDomain(), uri.getDomain());
    +}
    +
    +function testHttpBarChartRequest() {
    +  var bar = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR,
    +      180,
    +      104,
    +      null,
    +      goog.ui.ServerChart.CHART_SERVER_HTTP_URI);
    +  tryToCreateBarChart(bar);
    +  var uri = bar.getUri();
    +  var httpUri = new goog.Uri(goog.ui.ServerChart.CHART_SERVER_HTTP_URI);
    +  assertEquals('http', uri.getScheme());
    +  assertEquals(httpUri.getDomain(), uri.getDomain());
    +}
    +
    +function testHttpsBarChartRequest() {
    +  var bar = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR,
    +      180,
    +      104,
    +      null,
    +      goog.ui.ServerChart.CHART_SERVER_HTTPS_URI);
    +  tryToCreateBarChart(bar);
    +  var uri = bar.getUri();
    +  var httpsUri = new goog.Uri(goog.ui.ServerChart.CHART_SERVER_HTTPS_URI);
    +  assertEquals('https', uri.getScheme());
    +  assertEquals(httpsUri.getDomain(), uri.getDomain());
    +}
    +
    +function testMinValue() {
    +  var pie = new goog.ui.ServerChart(goog.ui.ServerChart.ChartType.PIE3D,
    +      180, 104);
    +  pie.addDataSet([1, 2, 3], '000000');
    +  assertEquals(pie.getMinValue(), 0);
    +
    +  var line = new goog.ui.ServerChart(goog.ui.ServerChart.ChartType.LINE,
    +      180, 104);
    +  line.addDataSet([1, 2, 3], '000000');
    +  assertEquals(line.getMinValue(), 1);
    +}
    +
    +function testMargins() {
    +  var pie = new goog.ui.ServerChart(goog.ui.ServerChart.ChartType.PIE3D,
    +      180, 104);
    +  pie.setMargins(1, 2, 3, 4);
    +  assertEquals('1,2,3,4',
    +      pie.getUri().getParameterValue(goog.ui.ServerChart.UriParam.MARGINS));
    +}
    +
    +function testSetParameterValue() {
    +  var scatter = new goog.ui.ServerChart(goog.ui.ServerChart.ChartType.SCATTER,
    +      180, 104);
    +  var key = goog.ui.ServerChart.UriParam.DATA_COLORS;
    +  var value = '000000,FF0000|00FF00|0000FF';
    +  scatter.setParameterValue(key, value);
    +
    +  assertEquals('unexpected parameter value', value,
    +      scatter.getUri().getParameterValue(key));
    +
    +  scatter.removeParameter(key);
    +
    +  assertUndefined('parameter not removed',
    +      scatter.getUri().getParameterValue(key));
    +}
    +
    +function testTypes() {
    +  var chart;
    +
    +  chart = new goog.ui.ServerChart(goog.ui.ServerChart.ChartType.CONCENTRIC_PIE,
    +      180, 104);
    +
    +  assertTrue(chart.isPieChart());
    +  assertFalse(chart.isBarChart());
    +  assertFalse(chart.isMap());
    +  assertFalse(chart.isLineChart());
    +
    +  chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_GROUPED_BAR, 180, 104);
    +
    +  assertFalse(chart.isPieChart());
    +  assertTrue(chart.isBarChart());
    +  assertTrue(chart.isHorizontalBarChart());
    +  assertTrue(chart.isGroupedBarChart());
    +  assertFalse(chart.isVerticalBarChart());
    +  assertFalse(chart.isStackedBarChart());
    +
    +  chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  assertTrue(chart.isBarChart());
    +  assertTrue(chart.isStackedBarChart());
    +  assertFalse(chart.isGroupedBarChart());
    +
    +  chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.XYLINE, 180, 104);
    +  assertTrue('I thought lxy was a line chart', chart.isLineChart());
    +  assertFalse('lxy is definitely not a pie chart', chart.isPieChart());
    +}
    +
    +function testBarChartRequest() {
    +  var bar = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  tryToCreateBarChart(bar);
    +  var httpUri = new goog.Uri(goog.ui.ServerChart.CHART_SERVER_URI);
    +  var uri = bar.getUri();
    +  assertEquals(httpUri.getDomain(), uri.getDomain());
    +}
    +
    +function tryToCreateBarChart(bar) {
    +  bar.addDataSet([8, 23, 7], '008000');
    +  bar.addDataSet([31, 11, 7], 'ffcc33');
    +  bar.addDataSet([2, 43, 70, 3, 43, 74], '3072f3');
    +  bar.setLeftLabels(['', '20K', '', '60K', '', '100K']);
    +  bar.setXLabels(['O', 'N', 'D']);
    +  bar.setMaxValue(100);
    +  var uri = bar.getUri();
    +  assertEquals(
    +      'br',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TYPE));
    +  assertEquals(
    +      '180x104',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.SIZE));
    +  assertEquals(
    +      'e:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +  assertEquals(
    +      '008000,ffcc33,3072f3',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA_COLORS));
    +  assertEquals(
    +      '100K||60K||20K|',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.LEFT_Y_LABELS));
    +  assertEquals(
    +      'O|N|D',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.X_LABELS));
    +}
    +
    +function testClearDataSets() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  tryToCreateBarChart(chart);
    +  var uriBefore = chart.getUri();
    +  chart.clearDataSets();
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  tryToCreateBarChart(chart);
    +  var uriAfter = chart.getUri();
    +  assertEquals(uriBefore.getScheme(), uriAfter.getScheme());
    +  assertEquals(uriBefore.getDomain(), uriAfter.getDomain());
    +  assertEquals(uriBefore.getPath(), uriAfter.getPath());
    +}
    +
    +function testMultipleDatasetsTextEncoding() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  chart.setEncodingType(goog.ui.ServerChart.EncodingType.TEXT);
    +  chart.addDataSet([0, 25, 100], '008000');
    +  chart.addDataSet([12, 2, 7.1], '112233');
    +  chart.addDataSet([82, 16, 2], '3072f3');
    +  var uri = chart.getUri();
    +  assertEquals(
    +      't:0,25,100|12,2,7.1|82,16,2',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +}
    +
    +function testVennDiagramRequest() {
    +  var venn = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VENN, 300, 200);
    +  venn.setTitle('Google Employees');
    +  var weights = [80,  // Size of circle A
    +                 60,  // Size of circle B
    +                 40,  // Size of circle C
    +                 20,  // Overlap of A and B
    +                 20,  // Overlap of A and C
    +                 20,  // Overlap of B and C
    +                 5];  // Overlap of A, B and C
    +  var labels = [
    +    'C Hackers',      // Label for A
    +    'LISP Gurus',     // Label for B
    +    'Java Jockeys'];  // Label for C
    +  venn.setVennSeries(weights, labels);
    +  var uri = venn.getUri();
    +  var httpUri = new goog.Uri(goog.ui.ServerChart.CHART_SERVER_URI);
    +  assertEquals(httpUri.getDomain(), uri.getDomain());
    +  assertEquals(
    +      'v',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TYPE));
    +  assertEquals(
    +      '300x200',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.SIZE));
    +  assertEquals(
    +      'e:..u7d3MzMzMzAA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +  assertEquals(
    +      'Google Employees',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TITLE));
    +  assertEquals(
    +      labels.join('|'),
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.LEGEND_TEXTS));
    +}
    +
    +
    +function testSparklineChartRequest() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([8, 23, 7], '008000');
    +  chart.addDataSet([31, 11, 7], 'ffcc33');
    +  chart.addDataSet([2, 43, 70, 3, 43, 74], '3072f3');
    +  chart.setLeftLabels(['', '20K', '', '60K', '', '100K']);
    +  chart.setXLabels(['O', 'N', 'D']);
    +  chart.setMaxValue(100);
    +  var uri = chart.getUri();
    +  assertEquals(
    +      'ls',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TYPE));
    +  assertEquals(
    +      '300x200',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.SIZE));
    +  assertEquals(
    +      'e:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +  assertEquals(
    +      '008000,ffcc33,3072f3',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA_COLORS));
    +  assertEquals(
    +      '100K||60K||20K|',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.LEFT_Y_LABELS));
    +  assertEquals(
    +      'O|N|D',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.X_LABELS));
    +}
    +
    +function testLegendPositionRequest() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([0, 100], '008000', 'foo');
    +  chart.setLegendPosition(goog.ui.ServerChart.LegendPosition.TOP);
    +  assertEquals('t', chart.getLegendPosition());
    +  var uri = chart.getUri();
    +  assertEquals(
    +      't',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.LEGEND_POSITION));
    +}
    +
    +function testSetGridParameter() {
    +  var gridArg = '20,20,4,4';
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([0, 100], '008000', 'foo');
    +  chart.setGridParameter(gridArg);
    +  assertEquals(gridArg, chart.getGridParameter());
    +  var uri = chart.getUri();
    +  assertEquals(
    +      gridArg,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.GRID));
    +}
    +
    +function testSetMarkerParameter() {
    +  var markerArg = 's,FF0000,0,-1,5';
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([0, 100], '008000', 'foo');
    +  chart.setMarkerParameter(markerArg);
    +  assertEquals(markerArg, chart.getMarkerParameter());
    +  var uri = chart.getUri();
    +  assertEquals(
    +      markerArg,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.MARKERS));
    +}
    +
    +function testNullDataPointRequest() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([40, null, 10], '008000');
    +  assertEquals(10, chart.getMinValue());
    +  assertEquals(40, chart.getMaxValue());
    +  var uri = chart.getUri();
    +  assertEquals(
    +      'e:..__AA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +
    +  chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  chart.addDataSet([-5, null, -1], '008000');
    +  assertEquals(-5, chart.getMinValue());
    +  assertEquals(-1, chart.getMaxValue());
    +  uri = chart.getUri();
    +  assertEquals(
    +      'e:AA__..',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +}
    +
    +function testSetBarSpaceWidths() {
    +  var noSpaceBetweenBarsSpecified = '20';
    +  var noSpaceBetweenBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR);
    +  noSpaceBetweenBarsChart.setBarSpaceWidths(20);
    +  var uri = noSpaceBetweenBarsChart.getUri();
    +  assertEquals(noSpaceBetweenBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var spaceBetweenBarsSpecified = '20,5';
    +  var spaceBetweenBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  spaceBetweenBarsChart.setBarSpaceWidths(20, 5);
    +  var uri = spaceBetweenBarsChart.getUri();
    +  assertEquals(spaceBetweenBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var spaceBetweenGroupsSpecified = '20,5,6';
    +  var spaceBetweenGroupsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  spaceBetweenGroupsChart.setBarSpaceWidths(20, 5, 6);
    +  var uri = spaceBetweenGroupsChart.getUri();
    +  assertEquals(spaceBetweenGroupsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var groupsButNotBarsSpecified = '20,6';
    +  var groupsButNotBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  groupsButNotBarsChart.setBarSpaceWidths(20, undefined, 6);
    +  var uri = groupsButNotBarsChart.getUri();
    +  assertEquals(groupsButNotBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +}
    +
    +function testSetAutomaticBarWidth() {
    +  var noSpaceBetweenBarsSpecified = 'a';
    +  var noSpaceBetweenBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR);
    +  noSpaceBetweenBarsChart.setAutomaticBarWidth();
    +  var uri = noSpaceBetweenBarsChart.getUri();
    +  assertEquals(noSpaceBetweenBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var spaceBetweenBarsSpecified = 'a,5';
    +  var spaceBetweenBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  spaceBetweenBarsChart.setAutomaticBarWidth(5);
    +  uri = spaceBetweenBarsChart.getUri();
    +  assertEquals(spaceBetweenBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var spaceBetweenGroupsSpecified = 'a,5,6';
    +  var spaceBetweenGroupsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  spaceBetweenGroupsChart.setAutomaticBarWidth(5, 6);
    +  uri = spaceBetweenGroupsChart.getUri();
    +  assertEquals(spaceBetweenGroupsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +
    +  var groupsButNotBarsSpecified = 'a,6';
    +  var groupsButNotBarsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  groupsButNotBarsChart.setAutomaticBarWidth(undefined, 6);
    +  uri = groupsButNotBarsChart.getUri();
    +  assertEquals(groupsButNotBarsSpecified,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.BAR_HEIGHT));
    +}
    +
    +function testSetDataScaling() {
    +  var dataScalingArg = '0,160';
    +  var dataArg = 't:0,50,100,130';
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR, 300, 200);
    +  chart.addDataSet([0, 50, 100, 130], '008000');
    +  chart.setDataScaling(0, 160);
    +  var uri = chart.getUri();
    +  assertEquals(dataScalingArg,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA_SCALING));
    +  assertEquals(
    +      dataArg,
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +}
    +
    +function testSetMultiAxisLabelStyle() {
    +  var noFontSizeChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  noFontSizeChart.addDataSet([0, 50, 100, 130], '008000');
    +  var axisNumber = noFontSizeChart.addMultiAxis(
    +      goog.ui.ServerChart.MultiAxisType.LEFT_Y_AXIS);
    +  var noFontSizeArgs = axisNumber + ',009000';
    +  noFontSizeChart.setMultiAxisLabelStyle(axisNumber, '009000');
    +  var noFontSizeUri = noFontSizeChart.getUri();
    +  assertEquals(noFontSizeArgs,
    +      noFontSizeUri.getParameterValue(
    +          goog.ui.ServerChart.UriParam.MULTI_AXIS_STYLE));
    +
    +  var noAlignChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  noAlignChart.addDataSet([0, 50, 100, 130], '008000');
    +  var xAxisNumber = noAlignChart.addMultiAxis(
    +      goog.ui.ServerChart.MultiAxisType.X_AXIS);
    +  var yAxisNumber = noAlignChart.addMultiAxis(
    +      goog.ui.ServerChart.MultiAxisType.LEFT_Y_AXIS);
    +  var noAlignArgs = xAxisNumber + ',009000,12|' + yAxisNumber + ',007000,14';
    +  noAlignChart.setMultiAxisLabelStyle(xAxisNumber, '009000', 12);
    +  noAlignChart.setMultiAxisLabelStyle(yAxisNumber, '007000', 14);
    +  var noAlignUri = noAlignChart.getUri();
    +  assertEquals(noAlignArgs,
    +      noAlignUri.getParameterValue(
    +          goog.ui.ServerChart.UriParam.MULTI_AXIS_STYLE));
    +
    +  var noLineTicksChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  noLineTicksChart.addDataSet([0, 50, 100, 130], '008000');
    +  axisNumber = noLineTicksChart.addMultiAxis(
    +      goog.ui.ServerChart.MultiAxisType.LEFT_Y_AXIS);
    +  var noLineTicksArgs = axisNumber + ',009000,12,0';
    +  noLineTicksChart.setMultiAxisLabelStyle(axisNumber, '009000', 12,
    +      goog.ui.ServerChart.MultiAxisAlignment.ALIGN_CENTER);
    +  var noLineTicksUri = noLineTicksChart.getUri();
    +  assertEquals(noLineTicksArgs,
    +      noLineTicksUri.getParameterValue(
    +          goog.ui.ServerChart.UriParam.MULTI_AXIS_STYLE));
    +
    +
    +  var allParamsChart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  allParamsChart.addDataSet([0, 50, 100, 130], '008000');
    +  axisNumber = allParamsChart.addMultiAxis(
    +      goog.ui.ServerChart.MultiAxisType.LEFT_Y_AXIS);
    +  var allParamsArgs = axisNumber + ',009000,12,0,lt';
    +  allParamsChart.setMultiAxisLabelStyle(axisNumber, '009000', 12,
    +      goog.ui.ServerChart.MultiAxisAlignment.ALIGN_CENTER,
    +      goog.ui.ServerChart.AxisDisplayType.LINE_AND_TICKS);
    +  var allParamsUri = allParamsChart.getUri();
    +  assertEquals(allParamsArgs,
    +      allParamsUri.getParameterValue(
    +          goog.ui.ServerChart.UriParam.MULTI_AXIS_STYLE));
    +}
    +
    +function testSetBackgroundFill() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  assertEquals(0, chart.getBackgroundFill().length);
    +  chart.setBackgroundFill([{color: '00ff00'}]);
    +  assertObjectEquals({
    +    area: 'bg',
    +    effect: 's',
    +    color: '00ff00'}, chart.getBackgroundFill()[0]);
    +  chart.setBackgroundFill([
    +    {color: '00ff00'},
    +    {area: 'c', color: '00ff00'}
    +  ]);
    +  assertObjectEquals({
    +    area: 'bg',
    +    effect: 's',
    +    color: '00ff00'}, chart.getBackgroundFill()[0]);
    +  assertObjectEquals({
    +    area: 'c',
    +    effect: 's',
    +    color: '00ff00'}, chart.getBackgroundFill()[1]);
    +
    +  chart.setParameterValue(goog.ui.ServerChart.UriParam.BACKGROUND_FILL,
    +      'bg,s,00ff00|c,lg,45,ff00ff|bg,s,ff00ff');
    +  assertEquals(0, chart.getBackgroundFill().length);
    +}
    +
    +function testSetMultiAxisRange() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR, 300, 200);
    +  var x = chart.addMultiAxis(goog.ui.ServerChart.MultiAxisType.X_AXIS);
    +  var top = chart.addMultiAxis(goog.ui.ServerChart.MultiAxisType.TOP_AXIS);
    +  chart.setMultiAxisRange(x, -500, 500, 100);
    +  chart.setMultiAxisRange(top, 0, 10);
    +  var range = chart.getMultiAxisRange();
    +
    +  assertArrayEquals(range[x], [-500, 500, 100]);
    +  assertArrayEquals(range[top], [0, 10]);
    +}
    +
    +function testGetConvertedValue() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.VERTICAL_STACKED_BAR);
    +
    +  assertThrows('No exception thrown when minValue > maxValue', function() {
    +    var result = chart.getConvertedValue_(
    +        90, 24, 3, goog.ui.ServerChart.EncodingType.SIMPLE);
    +  });
    +
    +  assertEquals('_', chart.getConvertedValue_(90, 100, 101,
    +      goog.ui.ServerChart.EncodingType.SIMPLE));
    +
    +  assertEquals('_', chart.getConvertedValue_(
    +      null, 0, 5, goog.ui.ServerChart.EncodingType.SIMPLE));
    +  assertEquals('__', chart.getConvertedValue_(
    +      null, 0, 150, goog.ui.ServerChart.EncodingType.EXTENDED));
    +  assertEquals('24', chart.getConvertedValue_(
    +      24, 1, 200, goog.ui.ServerChart.EncodingType.TEXT));
    +  assertEquals('H', chart.getConvertedValue_(
    +      24, 1, 200, goog.ui.ServerChart.EncodingType.SIMPLE));
    +  assertEquals('HZ', chart.getConvertedValue_(
    +      24, 1, 200, goog.ui.ServerChart.EncodingType.EXTENDED));
    +
    +  // Out-of-range values should give a missing data point, not an empty string.
    +  assertEquals('__', chart.getConvertedValue_(
    +      0, 1, 200, goog.ui.ServerChart.EncodingType.EXTENDED));
    +  assertEquals('__', chart.getConvertedValue_(
    +      201, 1, 200, goog.ui.ServerChart.EncodingType.EXTENDED));
    +  assertEquals('_', chart.getConvertedValue_(
    +      0, 1, 200, goog.ui.ServerChart.EncodingType.SIMPLE));
    +  assertEquals('_', chart.getConvertedValue_(
    +      201, 1, 200, goog.ui.ServerChart.EncodingType.SIMPLE));
    +  assertEquals('_', chart.getConvertedValue_(
    +      0, 1, 200, goog.ui.ServerChart.EncodingType.TEXT));
    +  assertEquals('_', chart.getConvertedValue_(
    +      201, 1, 200, goog.ui.ServerChart.EncodingType.TEXT));
    +}
    +
    +function testGetChartServerValues() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.HORIZONTAL_STACKED_BAR);
    +  var values = [0, 1, 2, 56, 90, 120];
    +  var minValue = 0;
    +  var maxValue = 140;
    +  var expectedSimple = 'AABYn0';
    +  assertEquals(expectedSimple,
    +      chart.getChartServerValues_(values, minValue, maxValue));
    +  var expectedText = '0,1,2,56,90,120';
    +  assertEquals(expectedSimple,
    +      chart.getChartServerValues_(values, minValue, maxValue));
    +}
    +
    +function testUriLengthLimit() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.SPARKLINE, 300, 200);
    +  var longUri = null;
    +  goog.events.listen(chart, goog.ui.ServerChart.Event.URI_TOO_LONG,
    +                     function(e) {longUri = e.uri;});
    +  assertEquals(goog.ui.ServerChart.EncodingType.AUTOMATIC,
    +      chart.getEncodingType());
    +  chart.addDataSet([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    +      '008000');
    +  assertEquals(
    +      'e:AAHHOOVVccjjqqxx44..AAHHOOVVccjjqqxx44..',
    +      chart.getUri().getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +  chart.setUriLengthLimit(100);
    +  assertEquals(
    +      's:AHOUbipv29AHOUbipv29',
    +      chart.getUri().getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +  chart.setUriLengthLimit(80);
    +  assertEquals(null, longUri);
    +  chart.getUri();
    +  assertNotEquals(null, longUri);
    +}
    +
    +function testVisibleDataSets() {
    +  var uri;
    +
    +  var bar = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  bar.addDataSet([8, 23, 7], '008000');
    +  bar.addDataSet([31, 11, 7], 'ffcc33');
    +  bar.addDataSet([2, 43, 70, 3, 43, 74], '3072f3');
    +  bar.setMaxValue(100);
    +
    +  bar.setNumVisibleDataSets(0);
    +  uri = bar.getUri();
    +  assertEquals(
    +      'e0:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +
    +  bar.setNumVisibleDataSets(1);
    +  uri = bar.getUri();
    +  assertEquals(
    +      'e1:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +
    +  bar.setNumVisibleDataSets(2);
    +  uri = bar.getUri();
    +  assertEquals(
    +      'e2:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +
    +  bar.setNumVisibleDataSets(null);
    +  uri = bar.getUri();
    +  assertEquals(
    +      'e:D6NtDQ,S7F4DQ,AAaxsZApaxvA',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.DATA));
    +}
    +
    +function testTitle() {
    +  var chart = new goog.ui.ServerChart(
    +      goog.ui.ServerChart.ChartType.BAR, 180, 104);
    +  assertEquals('Default title size', 13.5, chart.getTitleSize());
    +  assertEquals('Default title color', '333333', chart.getTitleColor());
    +  chart.setTitle('Test title');
    +  chart.setTitleSize(7);
    +  chart.setTitleColor('ff0000');
    +  var uri = chart.getUri();
    +  assertEquals(
    +      'Changing chart title failed',
    +      'Test title',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TITLE));
    +  assertEquals(
    +      'Changing title size and color failed',
    +      'ff0000,7',
    +      uri.getParameterValue(goog.ui.ServerChart.UriParam.TITLE_FORMAT));
    +  assertEquals('New title size', 7, chart.getTitleSize());
    +  assertEquals('New title color', 'ff0000', chart.getTitleColor());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/slider.js b/mapbox-gl/closure-library/closure/goog/ui/slider.js
    new file mode 100644
    index 0000000000..bb243716c2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/slider.js
    @@ -0,0 +1,136 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A slider implementation that allows to select a value within a
    + * range by dragging a thumb. The selected value is exposed through getValue().
    + *
    + * To decorate, the slider should be bound to an element with the class name
    + * 'goog-slider' containing a child with the class name 'goog-slider-thumb',
    + * whose position is set to relative.
    + * Note that you won't be able to see these elements unless they are styled.
    + *
    + * Slider orientation is horizontal by default.
    + * Use setOrientation(goog.ui.Slider.Orientation.VERTICAL) for a vertical
    + * slider.
    + *
    + * Decorate Example:
    + * <div id="slider" class="goog-slider">
    + *   <div class="goog-slider-thumb"></div>
    + * </div>
    + *
    + * JavaScript code:
    + * <code>
    + *   var slider = new goog.ui.Slider;
    + *   slider.decorate(document.getElementById('slider'));
    + * </code>
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/slider.html
    + */
    +
    +// Implementation note: We implement slider by inheriting from baseslider,
    +// which allows to select sub-ranges within a range using two thumbs. All we do
    +// is we co-locate the two thumbs into one.
    +
    +goog.provide('goog.ui.Slider');
    +goog.provide('goog.ui.Slider.Orientation');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.ui.SliderBase');
    +
    +
    +
    +/**
    + * This creates a slider object.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {(function(number):?string)=} opt_labelFn An optional function mapping
    + *     slider values to a description of the value.
    + * @constructor
    + * @extends {goog.ui.SliderBase}
    + */
    +goog.ui.Slider = function(opt_domHelper, opt_labelFn) {
    +  goog.ui.SliderBase.call(this, opt_domHelper, opt_labelFn);
    +  this.rangeModel.setExtent(0);
    +};
    +goog.inherits(goog.ui.Slider, goog.ui.SliderBase);
    +goog.tagUnsealableClass(goog.ui.Slider);
    +
    +
    +/**
    + * Expose Enum of superclass (representing the orientation of the slider) within
    + * Slider namespace.
    + *
    + * @enum {string}
    + */
    +goog.ui.Slider.Orientation = goog.ui.SliderBase.Orientation;
    +
    +
    +/**
    + * The prefix we use for the CSS class names for the slider and its elements.
    + * @type {string}
    + */
    +goog.ui.Slider.CSS_CLASS_PREFIX = goog.getCssName('goog-slider');
    +
    +
    +/**
    + * CSS class name for the single thumb element.
    + * @type {string}
    + */
    +goog.ui.Slider.THUMB_CSS_CLASS =
    +    goog.getCssName(goog.ui.Slider.CSS_CLASS_PREFIX, 'thumb');
    +
    +
    +/**
    + * Returns CSS class applied to the slider element.
    + * @param {goog.ui.SliderBase.Orientation} orient Orientation of the slider.
    + * @return {string} The CSS class applied to the slider element.
    + * @protected
    + * @override
    + */
    +goog.ui.Slider.prototype.getCssClass = function(orient) {
    +  return orient == goog.ui.SliderBase.Orientation.VERTICAL ?
    +      goog.getCssName(goog.ui.Slider.CSS_CLASS_PREFIX, 'vertical') :
    +      goog.getCssName(goog.ui.Slider.CSS_CLASS_PREFIX, 'horizontal');
    +};
    +
    +
    +/** @override */
    +goog.ui.Slider.prototype.createThumbs = function() {
    +  // find thumb
    +  var element = this.getElement();
    +  var thumb = goog.dom.getElementsByTagNameAndClass(
    +      null, goog.ui.Slider.THUMB_CSS_CLASS, element)[0];
    +  if (!thumb) {
    +    thumb = this.createThumb_();
    +    element.appendChild(thumb);
    +  }
    +  this.valueThumb = this.extentThumb = thumb;
    +};
    +
    +
    +/**
    + * Creates the thumb element.
    + * @return {!HTMLDivElement} The created thumb element.
    + * @private
    + */
    +goog.ui.Slider.prototype.createThumb_ = function() {
    +  var thumb =
    +      this.getDomHelper().createDom('div', goog.ui.Slider.THUMB_CSS_CLASS);
    +  goog.a11y.aria.setRole(thumb, goog.a11y.aria.Role.BUTTON);
    +  return /** @type {!HTMLDivElement} */ (thumb);
    +};
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/sliderbase.js b/mapbox-gl/closure-library/closure/goog/ui/sliderbase.js
    new file mode 100644
    index 0000000000..2460da2f94
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/sliderbase.js
    @@ -0,0 +1,1672 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implementation of a basic slider control.
    + *
    + * Models a control that allows to select a sub-range within a given
    + * range of values using two thumbs.  The underlying range is modeled
    + * as a range model, where the min thumb points to value of the
    + * rangemodel, and the max thumb points to value + extent of the range
    + * model.
    + *
    + * The currently selected range is exposed through methods
    + * getValue() and getExtent().
    + *
    + * The reason for modelling the basic slider state as value + extent is
    + * to be able to capture both, a two-thumb slider to select a range, and
    + * a single-thumb slider to just select a value (in the latter case, extent
    + * is always zero). We provide subclasses (twothumbslider.js and slider.js)
    + * that model those special cases of this control.
    + *
    + * All rendering logic is left out, so that the subclasses can define
    + * their own rendering. To do so, the subclasses overwrite:
    + * - createDom
    + * - decorateInternal
    + * - getCssClass
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +goog.provide('goog.ui.SliderBase');
    +goog.provide('goog.ui.SliderBase.AnimationFactory');
    +goog.provide('goog.ui.SliderBase.Orientation');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.events.MouseWheelHandler');
    +goog.require('goog.functions');
    +goog.require('goog.fx.AnimationParallelQueue');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.fx.Transition');
    +goog.require('goog.fx.dom.ResizeHeight');
    +goog.require('goog.fx.dom.ResizeWidth');
    +goog.require('goog.fx.dom.Slide');
    +goog.require('goog.math');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.RangeModel');
    +
    +
    +
    +/**
    + * This creates a SliderBase object.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {(function(number):?string)=} opt_labelFn An optional function mapping
    + *     slider values to a description of the value.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.SliderBase = function(opt_domHelper, opt_labelFn) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The factory to use to generate additional animations when animating to a
    +   * new value.
    +   * @type {goog.ui.SliderBase.AnimationFactory}
    +   * @private
    +   */
    +  this.additionalAnimations_ = null;
    +
    +  /**
    +   * The model for the range of the slider.
    +   * @type {!goog.ui.RangeModel}
    +   */
    +  this.rangeModel = new goog.ui.RangeModel;
    +
    +  /**
    +   * A function mapping slider values to text description.
    +   * @private {function(number):?string}
    +   */
    +  this.labelFn_ = opt_labelFn || goog.functions.NULL;
    +
    +  // Don't use getHandler because it gets cleared in exitDocument.
    +  goog.events.listen(this.rangeModel, goog.ui.Component.EventType.CHANGE,
    +      this.handleRangeModelChange, false, this);
    +};
    +goog.inherits(goog.ui.SliderBase, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.SliderBase);
    +
    +
    +/**
    + * Event types used to listen for dragging events. Note that extent drag events
    + * are also sent for single-thumb sliders, since the one thumb controls both
    + * value and extent together; in this case, they can simply be ignored.
    + * @enum {string}
    + */
    +goog.ui.SliderBase.EventType = {
    +  /** User started dragging the value thumb */
    +  DRAG_VALUE_START: goog.events.getUniqueId('dragvaluestart'),
    +  /** User is done dragging the value thumb */
    +  DRAG_VALUE_END: goog.events.getUniqueId('dragvalueend'),
    +  /** User started dragging the extent thumb */
    +  DRAG_EXTENT_START: goog.events.getUniqueId('dragextentstart'),
    +  /** User is done dragging the extent thumb */
    +  DRAG_EXTENT_END: goog.events.getUniqueId('dragextentend'),
    +  // Note that the following two events are sent twice, once for the value
    +  // dragger, and once of the extent dragger. If you need to differentiate
    +  // between the two, or if your code relies on receiving a single event per
    +  // START/END event, it should listen to one of the VALUE/EXTENT-specific
    +  // events.
    +  /** User started dragging a thumb */
    +  DRAG_START: goog.events.getUniqueId('dragstart'),
    +  /** User is done dragging a thumb */
    +  DRAG_END: goog.events.getUniqueId('dragend')
    +};
    +
    +
    +/**
    + * Enum for representing the orientation of the slider.
    + *
    + * @enum {string}
    + */
    +goog.ui.SliderBase.Orientation = {
    +  VERTICAL: 'vertical',
    +  HORIZONTAL: 'horizontal'
    +};
    +
    +
    +/**
    + * Orientation of the slider.
    + * @type {goog.ui.SliderBase.Orientation}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.orientation_ =
    +    goog.ui.SliderBase.Orientation.HORIZONTAL;
    +
    +
    +/** @private {goog.fx.AnimationParallelQueue} */
    +goog.ui.SliderBase.prototype.currentAnimation_;
    +
    +
    +/** @private {!goog.Timer} */
    +goog.ui.SliderBase.prototype.incTimer_;
    +
    +
    +/** @private {boolean} */
    +goog.ui.SliderBase.prototype.incrementing_;
    +
    +
    +/** @private {number} */
    +goog.ui.SliderBase.prototype.lastMousePosition_;
    +
    +
    +/**
    + * When the user holds down the mouse on the slider background, the closest
    + * thumb will move in "lock-step" towards the mouse. This number indicates how
    + * long each step should take (in milliseconds).
    + * @type {number}
    + * @private
    + */
    +goog.ui.SliderBase.MOUSE_DOWN_INCREMENT_INTERVAL_ = 200;
    +
    +
    +/**
    + * How long the animations should take (in milliseconds).
    + * @type {number}
    + * @private
    + */
    +goog.ui.SliderBase.ANIMATION_INTERVAL_ = 100;
    +
    +
    +/**
    + * The underlying range model
    + * @type {goog.ui.RangeModel}
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.rangeModel;
    +
    +
    +/**
    + * The minThumb dom-element, pointing to the start of the selected range.
    + * @type {HTMLDivElement}
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.valueThumb;
    +
    +
    +/**
    + * The maxThumb dom-element, pointing to the end of the selected range.
    + * @type {HTMLDivElement}
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.extentThumb;
    +
    +
    +/**
    + * The dom-element highlighting the selected range.
    + * @type {HTMLDivElement}
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.rangeHighlight;
    +
    +
    +/**
    + * The thumb that we should be moving (only relevant when timed move is active).
    + * @type {HTMLDivElement}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.thumbToMove_;
    +
    +
    +/**
    + * The object handling keyboard events.
    + * @type {goog.events.KeyHandler}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.keyHandler_;
    +
    +
    +/**
    + * The object handling mouse wheel events.
    + * @type {goog.events.MouseWheelHandler}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.mouseWheelHandler_;
    +
    +
    +/**
    + * The Dragger for dragging the valueThumb.
    + * @type {goog.fx.Dragger}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.valueDragger_;
    +
    +
    +/**
    + * The Dragger for dragging the extentThumb.
    + * @type {goog.fx.Dragger}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.extentDragger_;
    +
    +
    +/**
    + * If we are currently animating the thumb.
    + * @private
    + * @type {boolean}
    + */
    +goog.ui.SliderBase.prototype.isAnimating_ = false;
    +
    +
    +/**
    + * Whether clicking on the backgtround should move directly to that point.
    + * @private
    + * @type {boolean}
    + */
    +goog.ui.SliderBase.prototype.moveToPointEnabled_ = false;
    +
    +
    +/**
    + * The amount to increment/decrement for page up/down as well as when holding
    + * down the mouse button on the background.
    + * @private
    + * @type {number}
    + */
    +goog.ui.SliderBase.prototype.blockIncrement_ = 10;
    +
    +
    +/**
    + * The minimal extent. The class will ensure that the extent cannot shrink
    + * to a value smaller than minExtent.
    + * @private
    + * @type {number}
    + */
    +goog.ui.SliderBase.prototype.minExtent_ = 0;
    +
    +
    +/**
    + * Whether the slider should handle mouse wheel events.
    + * @private
    + * @type {boolean}
    + */
    +goog.ui.SliderBase.prototype.isHandleMouseWheel_ = true;
    +
    +
    +/**
    + * The time the last mousedown event was received.
    + * @private
    + * @type {number}
    + */
    +goog.ui.SliderBase.prototype.mouseDownTime_ = 0;
    +
    +
    +/**
    + * The delay after mouseDownTime_ during which a click event is ignored.
    + * @private
    + * @type {number}
    + * @const
    + */
    +goog.ui.SliderBase.prototype.MOUSE_DOWN_DELAY_ = 1000;
    +
    +
    +/**
    + * Whether the slider is enabled or not.
    + * @private
    + * @type {boolean}
    + */
    +goog.ui.SliderBase.prototype.enabled_ = true;
    +
    +
    +/**
    + * Whether the slider implements the changes described in http://b/6324964,
    + * making it truly RTL.  This is a temporary flag to allow clients to transition
    + * to the new behavior at their convenience.  At some point it will be the
    + * default.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SliderBase.prototype.flipForRtl_ = false;
    +
    +
    +/**
    + * Enables/disables true RTL behavior.  This should be called immediately after
    + * construction.  This is a temporary flag to allow clients to transition
    + * to the new behavior at their convenience.  At some point it will be the
    + * default.
    + * @param {boolean} flipForRtl True if the slider should be flipped for RTL,
    + *     false otherwise.
    + */
    +goog.ui.SliderBase.prototype.enableFlipForRtl = function(flipForRtl) {
    +  this.flipForRtl_ = flipForRtl;
    +};
    +
    +
    +// TODO: Make this return a base CSS class (without orientation), in subclasses.
    +/**
    + * Returns the CSS class applied to the slider element for the given
    + * orientation. Subclasses must override this method.
    + * @param {goog.ui.SliderBase.Orientation} orient The orientation.
    + * @return {string} The CSS class applied to slider elements.
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.getCssClass = goog.abstractMethod;
    +
    +
    +/** @override */
    +goog.ui.SliderBase.prototype.createDom = function() {
    +  goog.ui.SliderBase.superClass_.createDom.call(this);
    +  var element =
    +      this.getDomHelper().createDom('div', this.getCssClass(this.orientation_));
    +  this.decorateInternal(element);
    +};
    +
    +
    +/**
    + * Subclasses must implement this method and set the valueThumb and
    + * extentThumb to non-null values. They can also set the rangeHighlight
    + * element if a range highlight is desired.
    + * @type {function() : void}
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.createThumbs = goog.abstractMethod;
    +
    +
    +/**
    + * CSS class name applied to the slider while its thumbs are being dragged.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SliderBase.SLIDER_DRAGGING_CSS_CLASS_ =
    +    goog.getCssName('goog-slider-dragging');
    +
    +
    +/**
    + * CSS class name applied to a thumb while it's being dragged.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SliderBase.THUMB_DRAGGING_CSS_CLASS_ =
    +    goog.getCssName('goog-slider-thumb-dragging');
    +
    +
    +/**
    + * CSS class name applied when the slider is disabled.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SliderBase.DISABLED_CSS_CLASS_ =
    +    goog.getCssName('goog-slider-disabled');
    +
    +
    +/** @override */
    +goog.ui.SliderBase.prototype.decorateInternal = function(element) {
    +  goog.ui.SliderBase.superClass_.decorateInternal.call(this, element);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, this.getCssClass(this.orientation_));
    +  this.createThumbs();
    +  this.setAriaRoles();
    +};
    +
    +
    +/**
    + * Called when the DOM for the component is for sure in the document.
    + * Subclasses should override this method to set this element's role.
    + * @override
    + */
    +goog.ui.SliderBase.prototype.enterDocument = function() {
    +  goog.ui.SliderBase.superClass_.enterDocument.call(this);
    +
    +  // Attach the events
    +  this.valueDragger_ = new goog.fx.Dragger(this.valueThumb);
    +  this.extentDragger_ = new goog.fx.Dragger(this.extentThumb);
    +  this.valueDragger_.enableRightPositioningForRtl(this.flipForRtl_);
    +  this.extentDragger_.enableRightPositioningForRtl(this.flipForRtl_);
    +
    +  // The slider is handling the positioning so make the defaultActions empty.
    +  this.valueDragger_.defaultAction = this.extentDragger_.defaultAction =
    +      goog.nullFunction;
    +  this.keyHandler_ = new goog.events.KeyHandler(this.getElement());
    +  this.enableEventHandlers_(true);
    +
    +  this.getElement().tabIndex = 0;
    +  this.updateUi_();
    +};
    +
    +
    +/**
    + * Attaches/Detaches the event handlers on the slider.
    + * @param {boolean} enable Whether to attach or detach the event handlers.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.enableEventHandlers_ = function(enable) {
    +  if (enable) {
    +    this.getHandler().
    +        listen(this.valueDragger_, goog.fx.Dragger.EventType.BEFOREDRAG,
    +            this.handleBeforeDrag_).
    +        listen(this.extentDragger_, goog.fx.Dragger.EventType.BEFOREDRAG,
    +            this.handleBeforeDrag_).
    +        listen(this.valueDragger_,
    +            [goog.fx.Dragger.EventType.START, goog.fx.Dragger.EventType.END],
    +            this.handleThumbDragStartEnd_).
    +        listen(this.extentDragger_,
    +            [goog.fx.Dragger.EventType.START, goog.fx.Dragger.EventType.END],
    +            this.handleThumbDragStartEnd_).
    +        listen(this.keyHandler_, goog.events.KeyHandler.EventType.KEY,
    +            this.handleKeyDown_).
    +        listen(this.getElement(), goog.events.EventType.CLICK,
    +            this.handleMouseDownAndClick_).
    +        listen(this.getElement(), goog.events.EventType.MOUSEDOWN,
    +            this.handleMouseDownAndClick_);
    +    if (this.isHandleMouseWheel()) {
    +      this.enableMouseWheelHandling_(true);
    +    }
    +  } else {
    +    this.getHandler().
    +        unlisten(this.valueDragger_, goog.fx.Dragger.EventType.BEFOREDRAG,
    +            this.handleBeforeDrag_).
    +        unlisten(this.extentDragger_, goog.fx.Dragger.EventType.BEFOREDRAG,
    +            this.handleBeforeDrag_).
    +        unlisten(this.valueDragger_,
    +            [goog.fx.Dragger.EventType.START, goog.fx.Dragger.EventType.END],
    +            this.handleThumbDragStartEnd_).
    +        unlisten(this.extentDragger_,
    +            [goog.fx.Dragger.EventType.START, goog.fx.Dragger.EventType.END],
    +            this.handleThumbDragStartEnd_).
    +        unlisten(this.keyHandler_, goog.events.KeyHandler.EventType.KEY,
    +            this.handleKeyDown_).
    +        unlisten(this.getElement(), goog.events.EventType.CLICK,
    +            this.handleMouseDownAndClick_).
    +        unlisten(this.getElement(), goog.events.EventType.MOUSEDOWN,
    +            this.handleMouseDownAndClick_);
    +    if (this.isHandleMouseWheel()) {
    +      this.enableMouseWheelHandling_(false);
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.SliderBase.prototype.exitDocument = function() {
    +  goog.ui.SliderBase.base(this, 'exitDocument');
    +  goog.disposeAll(this.valueDragger_, this.extentDragger_, this.keyHandler_,
    +                  this.mouseWheelHandler_);
    +};
    +
    +
    +/**
    + * Handler for the before drag event. We use the event properties to determine
    + * the new value.
    + * @param {goog.fx.DragEvent} e  The drag event used to drag the thumb.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleBeforeDrag_ = function(e) {
    +  var thumbToDrag = e.dragger == this.valueDragger_ ?
    +      this.valueThumb : this.extentThumb;
    +  var value;
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    var availHeight = this.getElement().clientHeight - thumbToDrag.offsetHeight;
    +    value = (availHeight - e.top) / availHeight *
    +        (this.getMaximum() - this.getMinimum()) + this.getMinimum();
    +  } else {
    +    var availWidth = this.getElement().clientWidth - thumbToDrag.offsetWidth;
    +    value = (e.left / availWidth) * (this.getMaximum() - this.getMinimum()) +
    +        this.getMinimum();
    +  }
    +  // Bind the value within valid range before calling setThumbPosition_.
    +  // This is necessary because setThumbPosition_ is a no-op for values outside
    +  // of the legal range. For drag operations, we want the handle to snap to the
    +  // last valid value instead of remaining at the previous position.
    +  if (e.dragger == this.valueDragger_) {
    +    value = Math.min(Math.max(value, this.getMinimum()),
    +        this.getValue() + this.getExtent());
    +  } else {
    +    value = Math.min(Math.max(value, this.getValue()), this.getMaximum());
    +  }
    +  this.setThumbPosition_(thumbToDrag, value);
    +};
    +
    +
    +/**
    + * Handler for the start/end drag event on the thumgs. Adds/removes
    + * the "-dragging" CSS classes on the slider and thumb.
    + * @param {goog.fx.DragEvent} e The drag event used to drag the thumb.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleThumbDragStartEnd_ = function(e) {
    +  var isDragStart = e.type == goog.fx.Dragger.EventType.START;
    +  goog.dom.classlist.enable(goog.asserts.assertElement(this.getElement()),
    +      goog.ui.SliderBase.SLIDER_DRAGGING_CSS_CLASS_, isDragStart);
    +  goog.dom.classlist.enable(goog.asserts.assertElement(e.target.handle),
    +      goog.ui.SliderBase.THUMB_DRAGGING_CSS_CLASS_, isDragStart);
    +  var isValueDragger = e.dragger == this.valueDragger_;
    +  if (isDragStart) {
    +    this.dispatchEvent(goog.ui.SliderBase.EventType.DRAG_START);
    +    this.dispatchEvent(isValueDragger ?
    +        goog.ui.SliderBase.EventType.DRAG_VALUE_START :
    +        goog.ui.SliderBase.EventType.DRAG_EXTENT_START);
    +  } else {
    +    this.dispatchEvent(goog.ui.SliderBase.EventType.DRAG_END);
    +    this.dispatchEvent(isValueDragger ?
    +        goog.ui.SliderBase.EventType.DRAG_VALUE_END :
    +        goog.ui.SliderBase.EventType.DRAG_EXTENT_END);
    +  }
    +};
    +
    +
    +/**
    + * Event handler for the key down event. This is used to update the value
    + * based on the key pressed.
    + * @param {goog.events.KeyEvent} e  The keyboard event object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleKeyDown_ = function(e) {
    +  var handled = true;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.HOME:
    +      this.animatedSetValue(this.getMinimum());
    +      break;
    +    case goog.events.KeyCodes.END:
    +      this.animatedSetValue(this.getMaximum());
    +      break;
    +    case goog.events.KeyCodes.PAGE_UP:
    +      this.moveThumbs(this.getBlockIncrement());
    +      break;
    +    case goog.events.KeyCodes.PAGE_DOWN:
    +      this.moveThumbs(-this.getBlockIncrement());
    +      break;
    +    case goog.events.KeyCodes.LEFT:
    +      var sign = this.flipForRtl_ && this.isRightToLeft() ? 1 : -1;
    +      this.moveThumbs(e.shiftKey ?
    +          sign * this.getBlockIncrement() : sign * this.getUnitIncrement());
    +      break;
    +    case goog.events.KeyCodes.DOWN:
    +      this.moveThumbs(e.shiftKey ?
    +          -this.getBlockIncrement() : -this.getUnitIncrement());
    +      break;
    +    case goog.events.KeyCodes.RIGHT:
    +      var sign = this.flipForRtl_ && this.isRightToLeft() ? -1 : 1;
    +      this.moveThumbs(e.shiftKey ?
    +          sign * this.getBlockIncrement() : sign * this.getUnitIncrement());
    +      break;
    +    case goog.events.KeyCodes.UP:
    +      this.moveThumbs(e.shiftKey ?
    +          this.getBlockIncrement() : this.getUnitIncrement());
    +      break;
    +
    +    default:
    +      handled = false;
    +  }
    +
    +  if (handled) {
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * Handler for the mouse down event and click event.
    + * @param {goog.events.Event} e  The mouse event object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleMouseDownAndClick_ = function(e) {
    +  if (this.getElement().focus) {
    +    this.getElement().focus();
    +  }
    +
    +  // Known Element.
    +  var target = /** @type {Element} */ (e.target);
    +
    +  if (!goog.dom.contains(this.valueThumb, target) &&
    +      !goog.dom.contains(this.extentThumb, target)) {
    +    var isClick = e.type == goog.events.EventType.CLICK;
    +    if (isClick && goog.now() < this.mouseDownTime_ + this.MOUSE_DOWN_DELAY_) {
    +      // Ignore a click event that comes a short moment after a mousedown
    +      // event.  This happens for desktop.  For devices with both a touch
    +      // screen and a mouse pad we do not get a mousedown event from the mouse
    +      // pad and do get a click event.
    +      return;
    +    }
    +    if (!isClick) {
    +      this.mouseDownTime_ = goog.now();
    +    }
    +
    +    if (this.moveToPointEnabled_) {
    +      // just set the value directly based on the position of the click
    +      this.animatedSetValue(this.getValueFromMousePosition(e));
    +    } else {
    +      // start a timer that incrementally moves the handle
    +      this.startBlockIncrementing_(e);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for the mouse wheel event.
    + * @param {goog.events.MouseWheelEvent} e  The mouse wheel event object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleMouseWheel_ = function(e) {
    +  // Just move one unit increment per mouse wheel event
    +  var direction = e.detail > 0 ? -1 : 1;
    +  this.moveThumbs(direction * this.getUnitIncrement());
    +  e.preventDefault();
    +};
    +
    +
    +/**
    + * Starts the animation that causes the thumb to increment/decrement by the
    + * block increment when the user presses down on the background.
    + * @param {goog.events.Event} e  The mouse event object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.startBlockIncrementing_ = function(e) {
    +  this.storeMousePos_(e);
    +  this.thumbToMove_ = this.getClosestThumb_(this.getValueFromMousePosition(e));
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    this.incrementing_ = this.lastMousePosition_ < this.thumbToMove_.offsetTop;
    +  } else {
    +    this.incrementing_ = this.lastMousePosition_ >
    +                         this.getOffsetStart_(this.thumbToMove_) +
    +                         this.thumbToMove_.offsetWidth;
    +  }
    +
    +  var doc = goog.dom.getOwnerDocument(this.getElement());
    +  this.getHandler().
    +      listen(doc, goog.events.EventType.MOUSEUP,
    +          this.stopBlockIncrementing_, true).
    +      listen(this.getElement(), goog.events.EventType.MOUSEMOVE,
    +          this.storeMousePos_);
    +
    +  if (!this.incTimer_) {
    +    this.incTimer_ = new goog.Timer(
    +        goog.ui.SliderBase.MOUSE_DOWN_INCREMENT_INTERVAL_);
    +    this.getHandler().listen(this.incTimer_, goog.Timer.TICK,
    +        this.handleTimerTick_);
    +  }
    +  this.handleTimerTick_();
    +  this.incTimer_.start();
    +};
    +
    +
    +/**
    + * Handler for the tick event dispatched by the timer used to update the value
    + * in a block increment. This is also called directly from
    + * startBlockIncrementing_.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.handleTimerTick_ = function() {
    +  var value;
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    var mouseY = this.lastMousePosition_;
    +    var thumbY = this.thumbToMove_.offsetTop;
    +    if (this.incrementing_) {
    +      if (mouseY < thumbY) {
    +        value = this.getThumbPosition_(this.thumbToMove_) +
    +            this.getBlockIncrement();
    +      }
    +    } else {
    +      var thumbH = this.thumbToMove_.offsetHeight;
    +      if (mouseY > thumbY + thumbH) {
    +        value = this.getThumbPosition_(this.thumbToMove_) -
    +            this.getBlockIncrement();
    +      }
    +    }
    +  } else {
    +    var mouseX = this.lastMousePosition_;
    +    var thumbX = this.getOffsetStart_(this.thumbToMove_);
    +    if (this.incrementing_) {
    +      var thumbW = this.thumbToMove_.offsetWidth;
    +      if (mouseX > thumbX + thumbW) {
    +        value = this.getThumbPosition_(this.thumbToMove_) +
    +            this.getBlockIncrement();
    +      }
    +    } else {
    +      if (mouseX < thumbX) {
    +        value = this.getThumbPosition_(this.thumbToMove_) -
    +            this.getBlockIncrement();
    +      }
    +    }
    +  }
    +
    +  if (goog.isDef(value)) { // not all code paths sets the value variable
    +    this.setThumbPosition_(this.thumbToMove_, value);
    +  }
    +};
    +
    +
    +/**
    + * Stops the block incrementing animation and unlistens the necessary
    + * event handlers.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.stopBlockIncrementing_ = function() {
    +  if (this.incTimer_) {
    +    this.incTimer_.stop();
    +  }
    +
    +  var doc = goog.dom.getOwnerDocument(this.getElement());
    +  this.getHandler().
    +      unlisten(doc, goog.events.EventType.MOUSEUP,
    +          this.stopBlockIncrementing_, true).
    +      unlisten(this.getElement(), goog.events.EventType.MOUSEMOVE,
    +          this.storeMousePos_);
    +};
    +
    +
    +/**
    + * Returns the relative mouse position to the slider.
    + * @param {goog.events.Event} e  The mouse event object.
    + * @return {number} The relative mouse position to the slider.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getRelativeMousePos_ = function(e) {
    +  var coord = goog.style.getRelativePosition(e, this.getElement());
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    return coord.y;
    +  } else {
    +    if (this.flipForRtl_ && this.isRightToLeft()) {
    +      return this.getElement().clientWidth - coord.x;
    +    } else {
    +      return coord.x;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Stores the current mouse position so that it can be used in the timer.
    + * @param {goog.events.Event} e  The mouse event object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.storeMousePos_ = function(e) {
    +  this.lastMousePosition_ = this.getRelativeMousePos_(e);
    +};
    +
    +
    +/**
    + * Returns the value to use for the current mouse position
    + * @param {goog.events.Event} e  The mouse event object.
    + * @return {number} The value that this mouse position represents.
    + */
    +goog.ui.SliderBase.prototype.getValueFromMousePosition = function(e) {
    +  var min = this.getMinimum();
    +  var max = this.getMaximum();
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    var thumbH = this.valueThumb.offsetHeight;
    +    var availH = this.getElement().clientHeight - thumbH;
    +    var y = this.getRelativeMousePos_(e) - thumbH / 2;
    +    return (max - min) * (availH - y) / availH + min;
    +  } else {
    +    var thumbW = this.valueThumb.offsetWidth;
    +    var availW = this.getElement().clientWidth - thumbW;
    +    var x = this.getRelativeMousePos_(e) - thumbW / 2;
    +    return (max - min) * x / availW + min;
    +  }
    +};
    +
    +
    +/**
    + * @deprecated Since 25-June-2012. Use public method getValueFromMousePosition.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getValueFromMousePosition_ =
    +    goog.ui.SliderBase.prototype.getValueFromMousePosition;
    +
    +
    +/**
    + * @param {HTMLDivElement} thumb  The thumb object.
    + * @return {number} The position of the specified thumb.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getThumbPosition_ = function(thumb) {
    +  if (thumb == this.valueThumb) {
    +    return this.rangeModel.getValue();
    +  } else if (thumb == this.extentThumb) {
    +    return this.rangeModel.getValue() + this.rangeModel.getExtent();
    +  } else {
    +    throw Error('Illegal thumb element. Neither minThumb nor maxThumb');
    +  }
    +};
    +
    +
    +/**
    + * Returns whether a thumb is currently being dragged with the mouse (or via
    + * touch). Note that changing the value with keyboard, mouswheel, or via
    + * move-to-point click immediately sends a CHANGE event without going through a
    + * dragged state.
    + * @return {boolean} Whether a dragger is currently being dragged.
    + */
    +goog.ui.SliderBase.prototype.isDragging = function() {
    +  return this.valueDragger_.isDragging() || this.extentDragger_.isDragging();
    +};
    +
    +
    +/**
    + * Moves the thumbs by the specified delta as follows
    + * - as long as both thumbs stay within [min,max], both thumbs are moved
    + * - once a thumb reaches or exceeds min (or max, respectively), it stays
    + * - at min (or max, respectively).
    + * In case both thumbs have reached min (or max), no change event will fire.
    + * If the specified delta is smaller than the step size, it will be rounded
    + * to the step size.
    + * @param {number} delta The delta by which to move the selected range.
    + */
    +goog.ui.SliderBase.prototype.moveThumbs = function(delta) {
    +  // Assume that a small delta is supposed to be at least a step.
    +  if (Math.abs(delta) < this.getStep()) {
    +    delta = goog.math.sign(delta) * this.getStep();
    +  }
    +  var newMinPos = this.getThumbPosition_(this.valueThumb) + delta;
    +  var newMaxPos = this.getThumbPosition_(this.extentThumb) + delta;
    +  // correct min / max positions to be within bounds
    +  newMinPos = goog.math.clamp(
    +      newMinPos, this.getMinimum(), this.getMaximum() - this.minExtent_);
    +  newMaxPos = goog.math.clamp(
    +      newMaxPos, this.getMinimum() + this.minExtent_, this.getMaximum());
    +  // Set value and extent atomically
    +  this.setValueAndExtent(newMinPos, newMaxPos - newMinPos);
    +};
    +
    +
    +/**
    + * Sets the position of the given thumb. The set is ignored and no CHANGE event
    + * fires if it violates the constraint minimum <= value (valueThumb position) <=
    + * value + extent (extentThumb position) <= maximum.
    + *
    + * Note: To keep things simple, the setThumbPosition_ function does not have the
    + * side-effect of "correcting" value or extent to fit the above constraint as it
    + * is the case in the underlying range model. Instead, we simply ignore the
    + * call. Callers must make these adjustements explicitly if they wish.
    + * @param {Element} thumb The thumb whose position to set.
    + * @param {number} position The position to move the thumb to.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.setThumbPosition_ = function(thumb, position) {
    +  // Round first so that all computations and checks are consistent.
    +  var roundedPosition = this.rangeModel.roundToStepWithMin(position);
    +  var value = thumb == this.valueThumb ? roundedPosition :
    +      this.rangeModel.getValue();
    +  var end = thumb == this.extentThumb ? roundedPosition :
    +      this.rangeModel.getValue() + this.rangeModel.getExtent();
    +  if (value >= this.getMinimum() && end >= value + this.minExtent_ &&
    +      this.getMaximum() >= end) {
    +    this.setValueAndExtent(value, end - value);
    +  }
    +};
    +
    +
    +/**
    + * Sets the value and extent of the underlying range model. We enforce that
    + * getMinimum() <= value <= getMaximum() - extent and
    + * getMinExtent <= extent <= getMaximum() - getValue()
    + * If this is not satisfied for the given extent, the call is ignored and no
    + * CHANGE event fires. This is a utility method to allow setting the thumbs
    + * simultaneously and ensuring that only one event fires.
    + * @param {number} value The value to which to set the value.
    + * @param {number} extent The value to which to set the extent.
    + */
    +goog.ui.SliderBase.prototype.setValueAndExtent = function(value, extent) {
    +  if (this.getMinimum() <= value &&
    +      value <= this.getMaximum() - extent &&
    +      this.minExtent_ <= extent &&
    +      extent <= this.getMaximum() - value) {
    +
    +    if (value == this.getValue() && extent == this.getExtent()) {
    +      return;
    +    }
    +    // because the underlying range model applies adjustements of value
    +    // and extent to fit within bounds, we need to reset the extent
    +    // first so these adjustements don't kick in.
    +    this.rangeModel.setMute(true);
    +    this.rangeModel.setExtent(0);
    +    this.rangeModel.setValue(value);
    +    this.rangeModel.setExtent(extent);
    +    this.rangeModel.setMute(false);
    +    this.handleRangeModelChange(null);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The minimum value.
    + */
    +goog.ui.SliderBase.prototype.getMinimum = function() {
    +  return this.rangeModel.getMinimum();
    +};
    +
    +
    +/**
    + * Sets the minimum number.
    + * @param {number} min The minimum value.
    + */
    +goog.ui.SliderBase.prototype.setMinimum = function(min) {
    +  this.rangeModel.setMinimum(min);
    +};
    +
    +
    +/**
    + * @return {number} The maximum value.
    + */
    +goog.ui.SliderBase.prototype.getMaximum = function() {
    +  return this.rangeModel.getMaximum();
    +};
    +
    +
    +/**
    + * Sets the maximum number.
    + * @param {number} max The maximum value.
    + */
    +goog.ui.SliderBase.prototype.setMaximum = function(max) {
    +  this.rangeModel.setMaximum(max);
    +};
    +
    +
    +/**
    + * @return {HTMLDivElement} The value thumb element.
    + */
    +goog.ui.SliderBase.prototype.getValueThumb = function() {
    +  return this.valueThumb;
    +};
    +
    +
    +/**
    + * @return {HTMLDivElement} The extent thumb element.
    + */
    +goog.ui.SliderBase.prototype.getExtentThumb = function() {
    +  return this.extentThumb;
    +};
    +
    +
    +/**
    + * @param {number} position The position to get the closest thumb to.
    + * @return {HTMLDivElement} The thumb that is closest to the given position.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getClosestThumb_ = function(position) {
    +  if (position <= (this.rangeModel.getValue() +
    +                   this.rangeModel.getExtent() / 2)) {
    +    return this.valueThumb;
    +  } else {
    +    return this.extentThumb;
    +  }
    +};
    +
    +
    +/**
    + * Call back when the internal range model changes. Sub-classes may override
    + * and re-enter this method to update a11y state. Consider protected.
    + * @param {goog.events.Event} e The event object.
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.handleRangeModelChange = function(e) {
    +  this.updateUi_();
    +  this.updateAriaStates();
    +  this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * This is called when we need to update the size of the thumb. This happens
    + * when first created as well as when the value and the orientation changes.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.updateUi_ = function() {
    +  if (this.valueThumb && !this.isAnimating_) {
    +    var minCoord = this.getThumbCoordinateForValue(
    +        this.getThumbPosition_(this.valueThumb));
    +    var maxCoord = this.getThumbCoordinateForValue(
    +        this.getThumbPosition_(this.extentThumb));
    +
    +    if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +      this.valueThumb.style.top = minCoord.y + 'px';
    +      this.extentThumb.style.top = maxCoord.y + 'px';
    +      if (this.rangeHighlight) {
    +        var highlightPositioning = this.calculateRangeHighlightPositioning_(
    +            maxCoord.y, minCoord.y, this.valueThumb.offsetHeight);
    +        this.rangeHighlight.style.top = highlightPositioning.offset + 'px';
    +        this.rangeHighlight.style.height = highlightPositioning.size + 'px';
    +      }
    +    } else {
    +      var pos = (this.flipForRtl_ && this.isRightToLeft()) ? 'right' : 'left';
    +      this.valueThumb.style[pos] = minCoord.x + 'px';
    +      this.extentThumb.style[pos] = maxCoord.x + 'px';
    +      if (this.rangeHighlight) {
    +        var highlightPositioning = this.calculateRangeHighlightPositioning_(
    +            minCoord.x, maxCoord.x, this.valueThumb.offsetWidth);
    +        this.rangeHighlight.style[pos] = highlightPositioning.offset + 'px';
    +        this.rangeHighlight.style.width = highlightPositioning.size + 'px';
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Calculates the start position (offset) and size of the range highlight, e.g.
    + * for a horizontal slider, this will return [left, width] for the highlight.
    + * @param {number} firstThumbPos The position of the first thumb along the
    + *     slider axis.
    + * @param {number} secondThumbPos The position of the second thumb along the
    + *     slider axis, must be >= firstThumbPos.
    + * @param {number} thumbSize The size of the thumb, along the slider axis.
    + * @return {{offset: number, size: number}} The positioning parameters for the
    + *     range highlight.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.calculateRangeHighlightPositioning_ = function(
    +    firstThumbPos, secondThumbPos, thumbSize) {
    +  // Highlight is inset by half the thumb size, from the edges of the thumb.
    +  var highlightInset = Math.ceil(thumbSize / 2);
    +  var size = secondThumbPos - firstThumbPos + thumbSize - 2 * highlightInset;
    +  // Don't return negative size since it causes an error. IE sometimes attempts
    +  // to position the thumbs while slider size is 0, resulting in size < 0 here.
    +  return {
    +    offset: firstThumbPos + highlightInset,
    +    size: Math.max(size, 0)
    +  };
    +};
    +
    +
    +/**
    + * Returns the position to move the handle to for a given value
    + * @param {number} val  The value to get the coordinate for.
    + * @return {!goog.math.Coordinate} Coordinate with either x or y set.
    + */
    +goog.ui.SliderBase.prototype.getThumbCoordinateForValue = function(val) {
    +  var coord = new goog.math.Coordinate;
    +  if (this.valueThumb) {
    +    var min = this.getMinimum();
    +    var max = this.getMaximum();
    +
    +    // This check ensures the ratio never take NaN value, which is possible when
    +    // the slider min & max are same numbers (i.e. 1).
    +    var ratio = (val == min && min == max) ? 0 : (val - min) / (max - min);
    +
    +    if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +      var thumbHeight = this.valueThumb.offsetHeight;
    +      var h = this.getElement().clientHeight - thumbHeight;
    +      var bottom = Math.round(ratio * h);
    +      coord.x = this.getOffsetStart_(this.valueThumb); // Keep x the same.
    +      coord.y = h - bottom;
    +    } else {
    +      var w = this.getElement().clientWidth - this.valueThumb.offsetWidth;
    +      var left = Math.round(ratio * w);
    +      coord.x = left;
    +      coord.y = this.valueThumb.offsetTop; // Keep y the same.
    +    }
    +  }
    +  return coord;
    +};
    +
    +
    +/**
    + * @deprecated Since 25-June-2012. Use public method getThumbCoordinateForValue.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getThumbCoordinateForValue_ =
    +    goog.ui.SliderBase.prototype.getThumbCoordinateForValue;
    +
    +
    +/**
    + * Sets the value and starts animating the handle towards that position.
    + * @param {number} v Value to set and animate to.
    + */
    +goog.ui.SliderBase.prototype.animatedSetValue = function(v) {
    +  // the value might be out of bounds
    +  v = goog.math.clamp(v, this.getMinimum(), this.getMaximum());
    +
    +  if (this.isAnimating_) {
    +    this.currentAnimation_.stop(true);
    +  }
    +  var animations = new goog.fx.AnimationParallelQueue();
    +  var end;
    +
    +  var thumb = this.getClosestThumb_(v);
    +  var previousValue = this.getValue();
    +  var previousExtent = this.getExtent();
    +  var previousThumbValue = this.getThumbPosition_(thumb);
    +  var previousCoord = this.getThumbCoordinateForValue(previousThumbValue);
    +  var stepSize = this.getStep();
    +
    +  // If the delta is less than a single step, increase it to a step, else the
    +  // range model will reduce it to zero.
    +  if (Math.abs(v - previousThumbValue) < stepSize) {
    +    var delta = v > previousThumbValue ? stepSize : -stepSize;
    +    v = previousThumbValue + delta;
    +
    +    // The resulting value may be out of bounds, sanitize.
    +    v = goog.math.clamp(v, this.getMinimum(), this.getMaximum());
    +  }
    +
    +  this.setThumbPosition_(thumb, v);
    +  var coord = this.getThumbCoordinateForValue(this.getThumbPosition_(thumb));
    +
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    end = [this.getOffsetStart_(thumb), coord.y];
    +  } else {
    +    end = [coord.x, thumb.offsetTop];
    +  }
    +
    +  var slide = new goog.fx.dom.Slide(thumb,
    +      [previousCoord.x, previousCoord.y],
    +      end,
    +      goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +  slide.enableRightPositioningForRtl(this.flipForRtl_);
    +  animations.add(slide);
    +  if (this.rangeHighlight) {
    +    this.addRangeHighlightAnimations_(thumb, previousValue, previousExtent,
    +        coord, animations);
    +  }
    +
    +  // Create additional animations to play if a factory has been set.
    +  if (this.additionalAnimations_) {
    +    var additionalAnimations = this.additionalAnimations_.createAnimations(
    +        previousValue, v, goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +    goog.array.forEach(additionalAnimations, function(animation) {
    +      animations.add(animation);
    +    });
    +  }
    +
    +  this.currentAnimation_ = animations;
    +  this.getHandler().listen(animations, goog.fx.Transition.EventType.END,
    +      this.endAnimation_);
    +
    +  this.isAnimating_ = true;
    +  animations.play(false);
    +};
    +
    +
    +/**
    + * @return {boolean} True if the slider is animating, false otherwise.
    + */
    +goog.ui.SliderBase.prototype.isAnimating = function() {
    +  return this.isAnimating_;
    +};
    +
    +
    +/**
    + * Sets the factory that will be used to create additional animations to be
    + * played when animating to a new value.  These animations can be for any
    + * element and the animations will be played in addition to the default
    + * animation(s).  The animations will also be played in the same parallel queue
    + * ensuring that all animations are played at the same time.
    + * @see #animatedSetValue
    + *
    + * @param {goog.ui.SliderBase.AnimationFactory} factory The animation factory to
    + *     use.  This will not change the default animations played by the slider.
    + *     It will only allow for additional animations.
    + */
    +goog.ui.SliderBase.prototype.setAdditionalAnimations = function(factory) {
    +  this.additionalAnimations_ = factory;
    +};
    +
    +
    +/**
    + * Adds animations for the range highlight element to the animation queue.
    + *
    + * @param {Element} thumb The thumb that's moving, must be
    + *     either valueThumb or extentThumb.
    + * @param {number} previousValue The previous value of the slider.
    + * @param {number} previousExtent The previous extent of the
    + *     slider.
    + * @param {goog.math.Coordinate} newCoord The new pixel coordinate of the
    + *     thumb that's moving.
    + * @param {goog.fx.AnimationParallelQueue} animations The animation queue.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.addRangeHighlightAnimations_ = function(thumb,
    +    previousValue, previousExtent, newCoord, animations) {
    +  var previousMinCoord = this.getThumbCoordinateForValue(previousValue);
    +  var previousMaxCoord = this.getThumbCoordinateForValue(
    +      previousValue + previousExtent);
    +  var minCoord = previousMinCoord;
    +  var maxCoord = previousMaxCoord;
    +  if (thumb == this.valueThumb) {
    +    minCoord = newCoord;
    +  } else {
    +    maxCoord = newCoord;
    +  }
    +
    +  if (this.orientation_ == goog.ui.SliderBase.Orientation.VERTICAL) {
    +    var previousHighlightPositioning = this.calculateRangeHighlightPositioning_(
    +        previousMaxCoord.y, previousMinCoord.y, this.valueThumb.offsetHeight);
    +    var highlightPositioning = this.calculateRangeHighlightPositioning_(
    +        maxCoord.y, minCoord.y, this.valueThumb.offsetHeight);
    +    var slide = new goog.fx.dom.Slide(this.rangeHighlight,
    +        [this.getOffsetStart_(this.rangeHighlight),
    +          previousHighlightPositioning.offset],
    +        [this.getOffsetStart_(this.rangeHighlight),
    +          highlightPositioning.offset],
    +        goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +    var resizeHeight = new goog.fx.dom.ResizeHeight(this.rangeHighlight,
    +        previousHighlightPositioning.size, highlightPositioning.size,
    +        goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +    slide.enableRightPositioningForRtl(this.flipForRtl_);
    +    resizeHeight.enableRightPositioningForRtl(this.flipForRtl_);
    +    animations.add(slide);
    +    animations.add(resizeHeight);
    +  } else {
    +    var previousHighlightPositioning = this.calculateRangeHighlightPositioning_(
    +        previousMinCoord.x, previousMaxCoord.x, this.valueThumb.offsetWidth);
    +    var highlightPositioning = this.calculateRangeHighlightPositioning_(
    +        minCoord.x, maxCoord.x, this.valueThumb.offsetWidth);
    +    var newWidth = highlightPositioning[1];
    +    var slide = new goog.fx.dom.Slide(this.rangeHighlight,
    +        [previousHighlightPositioning.offset, this.rangeHighlight.offsetTop],
    +        [highlightPositioning.offset, this.rangeHighlight.offsetTop],
    +        goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +    var resizeWidth = new goog.fx.dom.ResizeWidth(this.rangeHighlight,
    +        previousHighlightPositioning.size, highlightPositioning.size,
    +        goog.ui.SliderBase.ANIMATION_INTERVAL_);
    +    slide.enableRightPositioningForRtl(this.flipForRtl_);
    +    resizeWidth.enableRightPositioningForRtl(this.flipForRtl_);
    +    animations.add(slide);
    +    animations.add(resizeWidth);
    +  }
    +};
    +
    +
    +/**
    + * Sets the isAnimating_ field to false once the animation is done.
    + * @param {goog.fx.AnimationEvent} e Event object passed by the animation
    + *     object.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.endAnimation_ = function(e) {
    +  this.isAnimating_ = false;
    +};
    +
    +
    +/**
    + * Changes the orientation.
    + * @param {goog.ui.SliderBase.Orientation} orient The orientation.
    + */
    +goog.ui.SliderBase.prototype.setOrientation = function(orient) {
    +  if (this.orientation_ != orient) {
    +    var oldCss = this.getCssClass(this.orientation_);
    +    var newCss = this.getCssClass(orient);
    +    this.orientation_ = orient;
    +
    +    // Update the DOM
    +    if (this.getElement()) {
    +      goog.dom.classlist.swap(goog.asserts.assert(this.getElement()),
    +                              oldCss, newCss);
    +      // we need to reset the left and top, plus range highlight
    +      var pos = (this.flipForRtl_ && this.isRightToLeft()) ? 'right' : 'left';
    +      this.valueThumb.style[pos] = this.valueThumb.style.top = '';
    +      this.extentThumb.style[pos] = this.extentThumb.style.top = '';
    +      if (this.rangeHighlight) {
    +        this.rangeHighlight.style[pos] = this.rangeHighlight.style.top = '';
    +        this.rangeHighlight.style.width = this.rangeHighlight.style.height = '';
    +      }
    +      this.updateUi_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.ui.SliderBase.Orientation} the orientation of the slider.
    + */
    +goog.ui.SliderBase.prototype.getOrientation = function() {
    +  return this.orientation_;
    +};
    +
    +
    +/** @override */
    +goog.ui.SliderBase.prototype.disposeInternal = function() {
    +  goog.ui.SliderBase.superClass_.disposeInternal.call(this);
    +  if (this.incTimer_) {
    +    this.incTimer_.dispose();
    +  }
    +  delete this.incTimer_;
    +  if (this.currentAnimation_) {
    +    this.currentAnimation_.dispose();
    +  }
    +  delete this.currentAnimation_;
    +  delete this.valueThumb;
    +  delete this.extentThumb;
    +  if (this.rangeHighlight) {
    +    delete this.rangeHighlight;
    +  }
    +  this.rangeModel.dispose();
    +  delete this.rangeModel;
    +  if (this.keyHandler_) {
    +    this.keyHandler_.dispose();
    +    delete this.keyHandler_;
    +  }
    +  if (this.mouseWheelHandler_) {
    +    this.mouseWheelHandler_.dispose();
    +    delete this.mouseWheelHandler_;
    +  }
    +  if (this.valueDragger_) {
    +    this.valueDragger_.dispose();
    +    delete this.valueDragger_;
    +  }
    +  if (this.extentDragger_) {
    +    this.extentDragger_.dispose();
    +    delete this.extentDragger_;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The amount to increment/decrement for page up/down as well
    + *     as when holding down the mouse button on the background.
    + */
    +goog.ui.SliderBase.prototype.getBlockIncrement = function() {
    +  return this.blockIncrement_;
    +};
    +
    +
    +/**
    + * Sets the amount to increment/decrement for page up/down as well as when
    + * holding down the mouse button on the background.
    + *
    + * @param {number} value The value to set the block increment to.
    + */
    +goog.ui.SliderBase.prototype.setBlockIncrement = function(value) {
    +  this.blockIncrement_ = value;
    +};
    +
    +
    +/**
    + * Sets the minimal value that the extent may have.
    + *
    + * @param {number} value The minimal value for the extent.
    + */
    +goog.ui.SliderBase.prototype.setMinExtent = function(value) {
    +  this.minExtent_ = value;
    +};
    +
    +
    +/**
    + * The amount to increment/decrement for up, down, left and right arrow keys
    + * and mouse wheel events.
    + * @private
    + * @type {number}
    + */
    +goog.ui.SliderBase.prototype.unitIncrement_ = 1;
    +
    +
    +/**
    + * @return {number} The amount to increment/decrement for up, down, left and
    + *     right arrow keys and mouse wheel events.
    + */
    +goog.ui.SliderBase.prototype.getUnitIncrement = function() {
    +  return this.unitIncrement_;
    +};
    +
    +
    +/**
    + * Sets the amount to increment/decrement for up, down, left and right arrow
    + * keys and mouse wheel events.
    + * @param {number} value  The value to set the unit increment to.
    + */
    +goog.ui.SliderBase.prototype.setUnitIncrement = function(value) {
    +  this.unitIncrement_ = value;
    +};
    +
    +
    +/**
    + * @return {?number} The step value used to determine how to round the value.
    + */
    +goog.ui.SliderBase.prototype.getStep = function() {
    +  return this.rangeModel.getStep();
    +};
    +
    +
    +/**
    + * Sets the step value. The step value is used to determine how to round the
    + * value.
    + * @param {?number} step  The step size.
    + */
    +goog.ui.SliderBase.prototype.setStep = function(step) {
    +  this.rangeModel.setStep(step);
    +};
    +
    +
    +/**
    + * @return {boolean} Whether clicking on the backgtround should move directly to
    + *     that point.
    + */
    +goog.ui.SliderBase.prototype.getMoveToPointEnabled = function() {
    +  return this.moveToPointEnabled_;
    +};
    +
    +
    +/**
    + * Sets whether clicking on the background should move directly to that point.
    + * @param {boolean} val Whether clicking on the background should move directly
    + *     to that point.
    + */
    +goog.ui.SliderBase.prototype.setMoveToPointEnabled = function(val) {
    +  this.moveToPointEnabled_ = val;
    +};
    +
    +
    +/**
    + * @return {number} The value of the underlying range model.
    + */
    +goog.ui.SliderBase.prototype.getValue = function() {
    +  return this.rangeModel.getValue();
    +};
    +
    +
    +/**
    + * Sets the value of the underlying range model. We enforce that
    + * getMinimum() <= value <= getMaximum() - getExtent()
    + * If this is not satisifed for the given value, the call is ignored and no
    + * CHANGE event fires.
    + * @param {number} value The value.
    + */
    +goog.ui.SliderBase.prototype.setValue = function(value) {
    +  // Set the position through the thumb method to enforce constraints.
    +  this.setThumbPosition_(this.valueThumb, value);
    +};
    +
    +
    +/**
    + * @return {number} The value of the extent of the underlying range model.
    + */
    +goog.ui.SliderBase.prototype.getExtent = function() {
    +  return this.rangeModel.getExtent();
    +};
    +
    +
    +/**
    + * Sets the extent of the underlying range model. We enforce that
    + * getMinExtent() <= extent <= getMaximum() - getValue()
    + * If this is not satisifed for the given extent, the call is ignored and no
    + * CHANGE event fires.
    + * @param {number} extent The value to which to set the extent.
    + */
    +goog.ui.SliderBase.prototype.setExtent = function(extent) {
    +  // Set the position through the thumb method to enforce constraints.
    +  this.setThumbPosition_(this.extentThumb, (this.rangeModel.getValue() +
    +                                            extent));
    +};
    +
    +
    +/**
    + * Change the visibility of the slider.
    + * You must call this if you had set the slider's value when it was invisible.
    + * @param {boolean} visible Whether to show the slider.
    + */
    +goog.ui.SliderBase.prototype.setVisible = function(visible) {
    +  goog.style.setElementShown(this.getElement(), visible);
    +  if (visible) {
    +    this.updateUi_();
    +  }
    +};
    +
    +
    +/**
    + * Set a11y roles and state.
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.setAriaRoles = function() {
    +  var el = this.getElement();
    +  goog.asserts.assert(el,
    +      'The DOM element for the slider base cannot be null.');
    +  goog.a11y.aria.setRole(el, goog.a11y.aria.Role.SLIDER);
    +  this.updateAriaStates();
    +};
    +
    +
    +/**
    + * Set a11y roles and state when values change.
    + * @protected
    + */
    +goog.ui.SliderBase.prototype.updateAriaStates = function() {
    +  var element = this.getElement();
    +  if (element) {
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.VALUEMIN,
    +        this.getMinimum());
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.VALUEMAX,
    +        this.getMaximum());
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.VALUENOW,
    +        this.getValue());
    +    // Passing an empty value to setState will restore the default.
    +    goog.a11y.aria.setState(element, goog.a11y.aria.State.VALUETEXT,
    +        this.getTextValue() || '');
    +  }
    +};
    +
    +
    +/**
    + * Enables or disables mouse wheel handling for the slider. The mouse wheel
    + * handler enables the user to change the value of slider using a mouse wheel.
    + *
    + * @param {boolean} enable Whether to enable mouse wheel handling.
    + */
    +goog.ui.SliderBase.prototype.setHandleMouseWheel = function(enable) {
    +  if (this.isInDocument() && enable != this.isHandleMouseWheel()) {
    +    this.enableMouseWheelHandling_(enable);
    +  }
    +
    +  this.isHandleMouseWheel_ = enable;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the slider handles mousewheel.
    + */
    +goog.ui.SliderBase.prototype.isHandleMouseWheel = function() {
    +  return this.isHandleMouseWheel_;
    +};
    +
    +
    +/**
    + * Enable/Disable mouse wheel handling.
    + * @param {boolean} enable Whether to enable mouse wheel handling.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.enableMouseWheelHandling_ = function(enable) {
    +  if (enable) {
    +    if (!this.mouseWheelHandler_) {
    +      this.mouseWheelHandler_ = new goog.events.MouseWheelHandler(
    +          this.getElement());
    +    }
    +    this.getHandler().listen(this.mouseWheelHandler_,
    +        goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +        this.handleMouseWheel_);
    +  } else {
    +    this.getHandler().unlisten(this.mouseWheelHandler_,
    +        goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +        this.handleMouseWheel_);
    +  }
    +};
    +
    +
    +/**
    + * Enables or disables the slider. A disabled slider will ignore all
    + * user-initiated events. Also fires goog.ui.Component.EventType.ENABLE/DISABLE
    + * event as appropriate.
    + * @param {boolean} enable Whether to enable the slider or not.
    + */
    +goog.ui.SliderBase.prototype.setEnabled = function(enable) {
    +  if (this.enabled_ == enable) {
    +    return;
    +  }
    +
    +  var eventType = enable ?
    +      goog.ui.Component.EventType.ENABLE : goog.ui.Component.EventType.DISABLE;
    +  if (this.dispatchEvent(eventType)) {
    +    this.enabled_ = enable;
    +    this.enableEventHandlers_(enable);
    +    if (!enable) {
    +      // Disabling a slider is equivalent to a mouse up event when the block
    +      // increment (if happening) should be halted and any possible event
    +      // handlers be appropriately unlistened.
    +      this.stopBlockIncrementing_();
    +    }
    +    goog.dom.classlist.enable(
    +        goog.asserts.assert(this.getElement()),
    +        goog.ui.SliderBase.DISABLED_CSS_CLASS_, !enable);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the slider is enabled or not.
    + */
    +goog.ui.SliderBase.prototype.isEnabled = function() {
    +  return this.enabled_;
    +};
    +
    +
    +/**
    + * @param {Element} element An element for which we want offsetLeft.
    + * @return {number} Returns the element's offsetLeft, accounting for RTL if
    + *     flipForRtl_ is true.
    + * @private
    + */
    +goog.ui.SliderBase.prototype.getOffsetStart_ = function(element) {
    +  return this.flipForRtl_ ?
    +      goog.style.bidi.getOffsetStart(element) : element.offsetLeft;
    +};
    +
    +
    +/**
    + * @return {?string} The text value for the slider's current value, or null if
    + *     unavailable.
    + */
    +goog.ui.SliderBase.prototype.getTextValue = function() {
    +  return this.labelFn_(this.getValue());
    +};
    +
    +
    +
    +/**
    + * The factory for creating additional animations to be played when animating to
    + * a new value.
    + * @interface
    + */
    +goog.ui.SliderBase.AnimationFactory = function() {};
    +
    +
    +/**
    + * Creates an additonal animation to play when animating to a new value.
    + *
    + * @param {number} previousValue The previous value (before animation).
    + * @param {number} newValue The new value (after animation).
    + * @param {number} interval The animation interval.
    + * @return {!Array<!goog.fx.TransitionBase>} The additional animations to play.
    + */
    +goog.ui.SliderBase.AnimationFactory.prototype.createAnimations;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.html b/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.html
    new file mode 100644
    index 0000000000..4875a6be45
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.html
    @@ -0,0 +1,67 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.SliderBase
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.SliderBaseTest');
    +  </script>
    +  <style type="text/css">
    +   #oneThumbSlider {
    +  position: relative;
    +  width: 1000px;
    +  background: grey;
    +  height: 20px;
    +}
    +
    +#oneThumbSlider.test-slider-vertical {
    +  height: 1000px;
    +  width: 20px;
    +}
    +
    +#twoThumbSlider {
    +  position: relative;
    +  /* Extra 20px is so distance between thumb centers is 1000px */
    +  width: 1020px;
    +}
    +
    +#valueThumb, #extentThumb {
    +  position: absolute;
    +  width: 20px;
    +}
    +
    +#thumb {
    +  position: absolute;
    +  width: 20px;
    +  height: 20px;
    +  background: black;
    +  top: 5px;
    +}
    +
    +.test-slider-vertical > #thumb {
    +  left: 5px;
    +  top: auto;
    +}
    +
    +#rangeHighlight {
    +  position: absolute;
    +}
    +  </style>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.js b/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.js
    new file mode 100644
    index 0000000000..d9a6a92433
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/sliderbase_test.js
    @@ -0,0 +1,948 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SliderBaseTest');
    +goog.setTestOnly('goog.ui.SliderBaseTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.fx.Animation');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.style');
    +goog.require('goog.style.bidi');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.MockControl');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.mockmatchers');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.SliderBase');
    +goog.require('goog.userAgent');
    +
    +var oneThumbSlider;
    +var oneThumbSliderRtl;
    +var oneChangeEventCount;
    +
    +var twoThumbSlider;
    +var twoThumbSliderRtl;
    +var twoChangeEventCount;
    +
    +var mockClock;
    +var mockAnimation;
    +
    +
    +
    +/**
    + * A basic class to implement the abstract goog.ui.SliderBase for testing.
    + * @constructor
    + * @extends {goog.ui.SliderBase}
    + */
    +function OneThumbSlider() {
    +  goog.ui.SliderBase.call(this, undefined /* domHelper */, function(value) {
    +    return value > 5 ? 'A big value.' : 'A small value.';
    +  });
    +}
    +goog.inherits(OneThumbSlider, goog.ui.SliderBase);
    +
    +
    +/** @override */
    +OneThumbSlider.prototype.createThumbs = function() {
    +  this.valueThumb = this.extentThumb = goog.dom.getElement('thumb');
    +};
    +
    +
    +/** @override */
    +OneThumbSlider.prototype.getCssClass = function(orientation) {
    +  return goog.getCssName('test-slider', orientation);
    +};
    +
    +
    +
    +/**
    + * A basic class to implement the abstract goog.ui.SliderBase for testing.
    + * @constructor
    + * @extends {goog.ui.SliderBase}
    + */
    +function TwoThumbSlider() {
    +  goog.ui.SliderBase.call(this);
    +}
    +goog.inherits(TwoThumbSlider, goog.ui.SliderBase);
    +
    +
    +/** @override */
    +TwoThumbSlider.prototype.createThumbs = function() {
    +  this.valueThumb = goog.dom.getElement('valueThumb');
    +  this.extentThumb = goog.dom.getElement('extentThumb');
    +  this.rangeHighlight = goog.dom.getElement('rangeHighlight');
    +};
    +
    +
    +/** @override */
    +TwoThumbSlider.prototype.getCssClass = function(orientation) {
    +  return goog.getCssName('test-slider', orientation);
    +};
    +
    +
    +
    +/**
    + * Basic class that implements the AnimationFactory interface for testing.
    + * @param {!goog.fx.Animation|!Array<!goog.fx.Animation>} testAnimations The
    + *     test animations to use.
    + * @constructor
    + * @implements {goog.ui.SliderBase.AnimationFactory}
    + */
    +function AnimationFactory(testAnimations) {
    +  this.testAnimations = testAnimations;
    +}
    +
    +
    +/** @override */
    +AnimationFactory.prototype.createAnimations = function() {
    +  return this.testAnimations;
    +};
    +
    +
    +function setUp() {
    +  var sandBox = goog.dom.getElement('sandbox');
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  var oneThumbElem = goog.dom.createDom(
    +      'div', {'id': 'oneThumbSlider'},
    +      goog.dom.createDom('span', {'id': 'thumb'}));
    +  sandBox.appendChild(oneThumbElem);
    +  oneThumbSlider = new OneThumbSlider();
    +  oneThumbSlider.decorate(oneThumbElem);
    +  oneChangeEventCount = 0;
    +  goog.events.listen(oneThumbSlider, goog.ui.Component.EventType.CHANGE,
    +      function() {
    +        oneChangeEventCount++;
    +      });
    +
    +  var twoThumbElem = goog.dom.createDom(
    +      'div', {'id': 'twoThumbSlider'},
    +      goog.dom.createDom('div', {'id': 'rangeHighlight'}),
    +      goog.dom.createDom('span', {'id': 'valueThumb'}),
    +      goog.dom.createDom('span', {'id': 'extentThumb'}));
    +  sandBox.appendChild(twoThumbElem);
    +  twoThumbSlider = new TwoThumbSlider();
    +  twoThumbSlider.decorate(twoThumbElem);
    +  twoChangeEventCount = 0;
    +  goog.events.listen(twoThumbSlider, goog.ui.Component.EventType.CHANGE,
    +      function() {
    +        twoChangeEventCount++;
    +      });
    +
    +  var sandBoxRtl = goog.dom.createDom('div',
    +      {'dir': 'rtl', 'style': 'position:absolute;'});
    +  sandBox.appendChild(sandBoxRtl);
    +
    +  var oneThumbElemRtl = goog.dom.createDom(
    +      'div', {'id': 'oneThumbSliderRtl'},
    +      goog.dom.createDom('span', {'id': 'thumbRtl'}));
    +  sandBoxRtl.appendChild(oneThumbElemRtl);
    +  oneThumbSliderRtl = new OneThumbSlider();
    +  oneThumbSliderRtl.enableFlipForRtl(true);
    +  oneThumbSliderRtl.decorate(oneThumbElemRtl);
    +  goog.events.listen(oneThumbSliderRtl, goog.ui.Component.EventType.CHANGE,
    +      function() {
    +        oneChangeEventCount++;
    +      });
    +
    +  var twoThumbElemRtl = goog.dom.createDom(
    +      'div', {'id': 'twoThumbSliderRtl'},
    +      goog.dom.createDom('div', {'id': 'rangeHighlightRtl'}),
    +      goog.dom.createDom('span', {'id': 'valueThumbRtl'}),
    +      goog.dom.createDom('span', {'id': 'extentThumbRtl'}));
    +  sandBoxRtl.appendChild(twoThumbElemRtl);
    +  twoThumbSliderRtl = new TwoThumbSlider();
    +  twoThumbSliderRtl.enableFlipForRtl(true);
    +  twoThumbSliderRtl.decorate(twoThumbElemRtl);
    +  twoChangeEventCount = 0;
    +  goog.events.listen(twoThumbSliderRtl, goog.ui.Component.EventType.CHANGE,
    +      function() {
    +        twoChangeEventCount++;
    +      });
    +}
    +
    +function tearDown() {
    +  oneThumbSlider.dispose();
    +  twoThumbSlider.dispose();
    +  oneThumbSliderRtl.dispose();
    +  twoThumbSliderRtl.dispose();
    +  mockClock.dispose();
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +function testGetAndSetValue() {
    +  oneThumbSlider.setValue(30);
    +  assertEquals(30, oneThumbSlider.getValue());
    +  assertEquals('Setting valid value must dispatch only a single change event.',
    +      1, oneChangeEventCount);
    +
    +  oneThumbSlider.setValue(30);
    +  assertEquals(30, oneThumbSlider.getValue());
    +  assertEquals('Setting to same value must not dispatch change event.',
    +      1, oneChangeEventCount);
    +
    +  oneThumbSlider.setValue(-30);
    +  assertEquals('Setting invalid value must not change value.',
    +      30, oneThumbSlider.getValue());
    +  assertEquals('Setting invalid value must not dispatch change event.',
    +      1, oneChangeEventCount);
    +
    +
    +  // Value thumb can't go past extent thumb, so we must move that first to
    +  // allow setting value.
    +  twoThumbSlider.setExtent(70);
    +  twoChangeEventCount = 0;
    +  twoThumbSlider.setValue(60);
    +  assertEquals(60, twoThumbSlider.getValue());
    +  assertEquals('Setting valid value must dispatch only a single change event.',
    +      1, twoChangeEventCount);
    +
    +  twoThumbSlider.setValue(60);
    +  assertEquals(60, twoThumbSlider.getValue());
    +  assertEquals('Setting to same value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +
    +  twoThumbSlider.setValue(-60);
    +  assertEquals('Setting invalid value must not change value.',
    +      60, twoThumbSlider.getValue());
    +  assertEquals('Setting invalid value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +}
    +
    +function testGetAndSetValueRtl() {
    +  var thumbElement = goog.dom.getElement('thumbRtl');
    +  assertEquals(0, goog.style.bidi.getOffsetStart(thumbElement));
    +  assertEquals('', thumbElement.style.left);
    +  assertTrue(thumbElement.style.right >= 0);
    +
    +  oneThumbSliderRtl.setValue(30);
    +  assertEquals(30, oneThumbSliderRtl.getValue());
    +  assertEquals('Setting valid value must dispatch only a single change event.',
    +      1, oneChangeEventCount);
    +
    +  assertEquals('', thumbElement.style.left);
    +  assertTrue(thumbElement.style.right >= 0);
    +
    +  oneThumbSliderRtl.setValue(30);
    +  assertEquals(30, oneThumbSliderRtl.getValue());
    +  assertEquals('Setting to same value must not dispatch change event.',
    +      1, oneChangeEventCount);
    +
    +  oneThumbSliderRtl.setValue(-30);
    +  assertEquals('Setting invalid value must not change value.',
    +      30, oneThumbSliderRtl.getValue());
    +  assertEquals('Setting invalid value must not dispatch change event.',
    +      1, oneChangeEventCount);
    +
    +
    +  // Value thumb can't go past extent thumb, so we must move that first to
    +  // allow setting value.
    +  var valueThumbElement = goog.dom.getElement('valueThumbRtl');
    +  var extentThumbElement = goog.dom.getElement('extentThumbRtl');
    +  assertEquals(0, goog.style.bidi.getOffsetStart(valueThumbElement));
    +  assertEquals(0, goog.style.bidi.getOffsetStart(extentThumbElement));
    +  assertEquals('', valueThumbElement.style.left);
    +  assertTrue(valueThumbElement.style.right >= 0);
    +  assertEquals('', extentThumbElement.style.left);
    +  assertTrue(extentThumbElement.style.right >= 0);
    +
    +  twoThumbSliderRtl.setExtent(70);
    +  twoChangeEventCount = 0;
    +  twoThumbSliderRtl.setValue(60);
    +  assertEquals(60, twoThumbSliderRtl.getValue());
    +  assertEquals('Setting valid value must dispatch only a single change event.',
    +      1, twoChangeEventCount);
    +
    +  twoThumbSliderRtl.setValue(60);
    +  assertEquals(60, twoThumbSliderRtl.getValue());
    +  assertEquals('Setting to same value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +
    +  assertEquals('', valueThumbElement.style.left);
    +  assertTrue(valueThumbElement.style.right >= 0);
    +  assertEquals('', extentThumbElement.style.left);
    +  assertTrue(extentThumbElement.style.right >= 0);
    +
    +  twoThumbSliderRtl.setValue(-60);
    +  assertEquals('Setting invalid value must not change value.',
    +      60, twoThumbSliderRtl.getValue());
    +  assertEquals('Setting invalid value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +}
    +
    +function testGetAndSetExtent() {
    +  // Note(user): With a one thumb slider the API only really makes sense if you
    +  // always use setValue since there is no extent.
    +
    +  twoThumbSlider.setExtent(7);
    +  assertEquals(7, twoThumbSlider.getExtent());
    +  assertEquals('Setting valid value must dispatch only a single change event.',
    +      1, twoChangeEventCount);
    +
    +  twoThumbSlider.setExtent(7);
    +  assertEquals(7, twoThumbSlider.getExtent());
    +  assertEquals('Setting to same value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +
    +  twoThumbSlider.setExtent(-7);
    +  assertEquals('Setting invalid value must not change value.',
    +      7, twoThumbSlider.getExtent());
    +  assertEquals('Setting invalid value must not dispatch change event.',
    +      1, twoChangeEventCount);
    +}
    +
    +function testUpdateValueExtent() {
    +  twoThumbSlider.setValueAndExtent(30, 50);
    +
    +  assertNotNull(twoThumbSlider.getElement());
    +  assertEquals('Setting value results in updating aria-valuenow',
    +      '30',
    +      goog.a11y.aria.getState(twoThumbSlider.getElement(),
    +          goog.a11y.aria.State.VALUENOW));
    +  assertEquals(30, twoThumbSlider.getValue());
    +  assertEquals(50, twoThumbSlider.getExtent());
    +}
    +
    +function testValueText() {
    +  oneThumbSlider.setValue(10);
    +  assertEquals('Setting value results in correct aria-valuetext',
    +      'A big value.', goog.a11y.aria.getState(oneThumbSlider.getElement(),
    +          goog.a11y.aria.State.VALUETEXT));
    +  oneThumbSlider.setValue(2);
    +  assertEquals('Updating value results in updated aria-valuetext',
    +      'A small value.', goog.a11y.aria.getState(oneThumbSlider.getElement(),
    +          goog.a11y.aria.State.VALUETEXT));
    +}
    +
    +function testGetValueText() {
    +  oneThumbSlider.setValue(10);
    +  assertEquals('Getting the text value gets the correct description',
    +      'A big value.', oneThumbSlider.getTextValue());
    +  oneThumbSlider.setValue(2);
    +  assertEquals(
    +      'Getting the updated text value gets the correct updated description',
    +      'A small value.', oneThumbSlider.getTextValue());
    +}
    +
    +function testRangeListener() {
    +  var slider = new goog.ui.SliderBase;
    +  slider.updateUi_ = slider.updateAriaStates = function() {};
    +  slider.rangeModel.setValue(0);
    +
    +  var f = goog.testing.recordFunction();
    +  goog.events.listen(slider, goog.ui.Component.EventType.CHANGE, f);
    +
    +  slider.rangeModel.setValue(50);
    +  assertEquals(1, f.getCallCount());
    +
    +  slider.exitDocument();
    +  slider.rangeModel.setValue(0);
    +  assertEquals('The range model listener should not have been removed so we ' +
    +               'should have gotten a second event dispatch',
    +               2, f.getCallCount());
    +}
    +
    +
    +/**
    + * Verifies that rangeHighlight position and size are correct for the given
    + * startValue and endValue. Assumes slider has default min/max values [0, 100],
    + * width of 1020px, and thumb widths of 20px, with rangeHighlight drawn from
    + * the centers of the thumbs.
    + * @param {number} rangeHighlight The range highlight.
    + * @param {number} startValue The start value.
    + * @param {number} endValue The end value.
    + */
    +function assertHighlightedRange(rangeHighlight, startValue, endValue) {
    +  var rangeStr = '[' + startValue + ', ' + endValue + ']';
    +  var rangeStart = 10 + 10 * startValue;
    +  assertEquals('Range highlight for ' + rangeStr + ' should start at ' +
    +      rangeStart + 'px.', rangeStart, rangeHighlight.offsetLeft);
    +  var rangeSize = 10 * (endValue - startValue);
    +  assertEquals('Range highlight for ' + rangeStr + ' should have size ' +
    +      rangeSize + 'px.', rangeSize, rangeHighlight.offsetWidth);
    +}
    +
    +function testKeyHandlingTests() {
    +  twoThumbSlider.setValue(0);
    +  twoThumbSlider.setExtent(100);
    +  assertEquals(0, twoThumbSlider.getValue());
    +  assertEquals(100, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(1, twoThumbSlider.getValue());
    +  assertEquals(99, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(2, twoThumbSlider.getValue());
    +  assertEquals(98, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(1, twoThumbSlider.getValue());
    +  assertEquals(98, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(0, twoThumbSlider.getValue());
    +  assertEquals(98, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT,
    +      { shiftKey: true });
    +  assertEquals(10, twoThumbSlider.getValue());
    +  assertEquals(90, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT,
    +      { shiftKey: true });
    +  assertEquals(20, twoThumbSlider.getValue());
    +  assertEquals(80, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT,
    +      { shiftKey: true });
    +  assertEquals(10, twoThumbSlider.getValue());
    +  assertEquals(80, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT,
    +      { shiftKey: true });
    +  assertEquals(0, twoThumbSlider.getValue());
    +  assertEquals(80, twoThumbSlider.getExtent());
    +}
    +
    +function testKeyHandlingLargeStepSize() {
    +  twoThumbSlider.setValue(0);
    +  twoThumbSlider.setExtent(100);
    +  twoThumbSlider.setStep(5);
    +  assertEquals(0, twoThumbSlider.getValue());
    +  assertEquals(100, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(5, twoThumbSlider.getValue());
    +  assertEquals(95, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(10, twoThumbSlider.getValue());
    +  assertEquals(90, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(5, twoThumbSlider.getValue());
    +  assertEquals(90, twoThumbSlider.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSlider.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(0, twoThumbSlider.getValue());
    +  assertEquals(90, twoThumbSlider.getExtent());
    +}
    +
    +function testKeyHandlingRtl() {
    +  twoThumbSliderRtl.setValue(0);
    +  twoThumbSliderRtl.setExtent(100);
    +  assertEquals(0, twoThumbSliderRtl.getValue());
    +  assertEquals(100, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(0, twoThumbSliderRtl.getValue());
    +  assertEquals(99, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT);
    +  assertEquals(0, twoThumbSliderRtl.getValue());
    +  assertEquals(98, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(1, twoThumbSliderRtl.getValue());
    +  assertEquals(98, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT);
    +  assertEquals(2, twoThumbSliderRtl.getValue());
    +  assertEquals(98, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT,
    +      { shiftKey: true });
    +  assertEquals(0, twoThumbSliderRtl.getValue());
    +  assertEquals(90, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.RIGHT,
    +      { shiftKey: true });
    +  assertEquals(0, twoThumbSliderRtl.getValue());
    +  assertEquals(80, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT,
    +      { shiftKey: true });
    +  assertEquals(10, twoThumbSliderRtl.getValue());
    +  assertEquals(80, twoThumbSliderRtl.getExtent());
    +
    +  goog.testing.events.fireKeySequence(
    +      twoThumbSliderRtl.getElement(), goog.events.KeyCodes.LEFT,
    +      { shiftKey: true });
    +  assertEquals(20, twoThumbSliderRtl.getValue());
    +  assertEquals(80, twoThumbSliderRtl.getExtent());
    +}
    +
    +function testRangeHighlight() {
    +  var rangeHighlight = goog.dom.getElement('rangeHighlight');
    +
    +  // Test [0, 100]
    +  twoThumbSlider.setValue(0);
    +  twoThumbSlider.setExtent(100);
    +  assertHighlightedRange(rangeHighlight, 0, 100);
    +
    +  // Test [25, 75]
    +  twoThumbSlider.setValue(25);
    +  twoThumbSlider.setExtent(50);
    +  assertHighlightedRange(rangeHighlight, 25, 75);
    +
    +  // Test [50, 50]
    +  twoThumbSlider.setValue(50);
    +  twoThumbSlider.setExtent(0);
    +  assertHighlightedRange(rangeHighlight, 50, 50);
    +}
    +
    +function testRangeHighlightAnimation() {
    +  var animationDelay = 160; // Delay in ms, is a bit higher than actual delay.
    +  if (goog.userAgent.IE) {
    +    // For some reason, (probably due to how timing works), IE7 and IE8 will not
    +    // stop if we don't wait for it.
    +    animationDelay = 250;
    +  }
    +
    +  var rangeHighlight = goog.dom.getElement('rangeHighlight');
    +  twoThumbSlider.setValue(0);
    +  twoThumbSlider.setExtent(100);
    +
    +  // Animate right thumb, final range is [0, 75]
    +  twoThumbSlider.animatedSetValue(75);
    +  assertHighlightedRange(rangeHighlight, 0, 100);
    +  mockClock.tick(animationDelay);
    +  assertHighlightedRange(rangeHighlight, 0, 75);
    +
    +  // Animate left thumb, final range is [25, 75]
    +  twoThumbSlider.animatedSetValue(25);
    +  assertHighlightedRange(rangeHighlight, 0, 75);
    +  mockClock.tick(animationDelay);
    +  assertHighlightedRange(rangeHighlight, 25, 75);
    +}
    +
    +
    +/**
    + * Verifies that no error occurs and that the range highlight is sized correctly
    + * for a zero-size slider (i.e. doesn't attempt to set a negative size). The
    + * test tries to resize the slider from its original size to 0, then checks
    + * that the range highlight's size is correctly set to 0.
    + *
    + * The size verification is needed because Webkit/Gecko outright ignore calls
    + * to set negative sizes on an element, leaving it at its former size. IE
    + * throws an error in the same situation.
    + */
    +function testRangeHighlightForZeroSizeSlider() {
    +  // Make sure range highlight spans whole slider before zeroing width.
    +  twoThumbSlider.setExtent(100);
    +  twoThumbSlider.getElement().style.width = 0;
    +
    +  // The setVisible call is used to force a UI update.
    +  twoThumbSlider.setVisible(true);
    +  assertEquals('Range highlight size should be 0 when slider size is 0',
    +      0, goog.dom.getElement('rangeHighlight').offsetWidth);
    +}
    +
    +function testAnimatedSetValueAnimatesFactoryCreatedAnimations() {
    +  // Create and set the factory.
    +  var ignore = goog.testing.mockmatchers.ignoreArgument;
    +  var mockControl = new goog.testing.MockControl();
    +  var mockAnimation1 = mockControl.createLooseMock(goog.fx.Animation);
    +  var mockAnimation2 = mockControl.createLooseMock(goog.fx.Animation);
    +  var testAnimations = [mockAnimation1, mockAnimation2];
    +  oneThumbSlider.setAdditionalAnimations(new AnimationFactory(testAnimations));
    +
    +  // Expect the animations to be played.
    +  mockAnimation1.play(false);
    +  mockAnimation2.play(false);
    +  mockAnimation1.addEventListener(ignore, ignore, ignore);
    +  mockAnimation2.addEventListener(ignore, ignore, ignore);
    +
    +  // Animate and verify.
    +  mockControl.$replayAll();
    +  oneThumbSlider.animatedSetValue(50);
    +  mockControl.$verifyAll();
    +  mockControl.$resetAll();
    +  mockControl.$tearDown();
    +}
    +
    +function testMouseWheelEventHandlerEnable() {
    +  // Mouse wheel handling should be enabled by default.
    +  assertTrue(oneThumbSlider.isHandleMouseWheel());
    +
    +  // Test disabling the mouse wheel handler
    +  oneThumbSlider.setHandleMouseWheel(false);
    +  assertFalse(oneThumbSlider.isHandleMouseWheel());
    +
    +  // Test that enabling again works fine.
    +  oneThumbSlider.setHandleMouseWheel(true);
    +  assertTrue(oneThumbSlider.isHandleMouseWheel());
    +
    +  // Test that mouse wheel handling can be disabled before rendering a slider.
    +  var wheelDisabledElem = goog.dom.createDom(
    +      'div', {}, goog.dom.createDom('span'));
    +  var wheelDisabledSlider = new OneThumbSlider();
    +  wheelDisabledSlider.setHandleMouseWheel(false);
    +  wheelDisabledSlider.decorate(wheelDisabledElem);
    +  assertFalse(wheelDisabledSlider.isHandleMouseWheel());
    +}
    +
    +function testDisabledAndEnabledSlider() {
    +  // Check that a slider is enabled by default
    +  assertTrue(oneThumbSlider.isEnabled());
    +
    +  var listenerCount = oneThumbSlider.getHandler().getListenerCount();
    +  // Disable the slider and check its state
    +  oneThumbSlider.setEnabled(false);
    +  assertFalse(oneThumbSlider.isEnabled());
    +  assertTrue(goog.dom.classlist.contains(
    +      oneThumbSlider.getElement(), 'goog-slider-disabled'));
    +  assertEquals(0, oneThumbSlider.getHandler().getListenerCount());
    +
    +  // setValue should work unaffected even when the slider is disabled.
    +  oneThumbSlider.setValue(30);
    +  assertEquals(30, oneThumbSlider.getValue());
    +  assertEquals('Setting valid value must dispatch a change event ' +
    +      'even when slider is disabled.', 1, oneChangeEventCount);
    +
    +  // Test the transition from disabled to enabled
    +  oneThumbSlider.setEnabled(true);
    +  assertTrue(oneThumbSlider.isEnabled());
    +  assertFalse(goog.dom.classlist.contains(
    +      oneThumbSlider.getElement(), 'goog-slider-disabled'));
    +  assertTrue(listenerCount == oneThumbSlider.getHandler().getListenerCount());
    +}
    +
    +function testBlockIncrementingWithEnableAndDisabled() {
    +  var doc = goog.dom.getOwnerDocument(oneThumbSlider.getElement());
    +  // Case when slider is not disabled between the mouse down and up events.
    +  goog.testing.events.fireMouseDownEvent(oneThumbSlider.getElement());
    +  assertEquals(1, goog.events.getListeners(
    +      oneThumbSlider.getElement(),
    +      goog.events.EventType.MOUSEMOVE, false).length);
    +  assertEquals(1, goog.events.getListeners(
    +      doc, goog.events.EventType.MOUSEUP, true).length);
    +
    +  goog.testing.events.fireMouseUpEvent(oneThumbSlider.getElement());
    +
    +  assertEquals(0, goog.events.getListeners(
    +      oneThumbSlider.getElement(),
    +      goog.events.EventType.MOUSEMOVE, false).length);
    +  assertEquals(0, goog.events.getListeners(
    +      doc, goog.events.EventType.MOUSEUP, true).length);
    +
    +  // Case when the slider is disabled between the mouse down and up events.
    +  goog.testing.events.fireMouseDownEvent(oneThumbSlider.getElement());
    +  assertEquals(1, goog.events.getListeners(
    +      oneThumbSlider.getElement(),
    +      goog.events.EventType.MOUSEMOVE, false).length);
    +  assertEquals(1,
    +      goog.events.getListeners(doc,
    +      goog.events.EventType.MOUSEUP, true).length);
    +
    +  oneThumbSlider.setEnabled(false);
    +
    +  assertEquals(0, goog.events.getListeners(
    +      oneThumbSlider.getElement(),
    +      goog.events.EventType.MOUSEMOVE, false).length);
    +  assertEquals(0, goog.events.getListeners(
    +      doc, goog.events.EventType.MOUSEUP, true).length);
    +  assertEquals(1, oneThumbSlider.getHandler().getListenerCount());
    +
    +  goog.testing.events.fireMouseUpEvent(oneThumbSlider.getElement());
    +  assertEquals(0, goog.events.getListeners(
    +      oneThumbSlider.getElement(),
    +      goog.events.EventType.MOUSEMOVE, false).length);
    +  assertEquals(0, goog.events.getListeners(
    +      doc, goog.events.EventType.MOUSEUP, true).length);
    +}
    +
    +function testMouseClickWithMoveToPointEnabled() {
    +  var stepSize = 20;
    +  oneThumbSlider.setStep(stepSize);
    +  oneThumbSlider.setMoveToPointEnabled(true);
    +  var initialValue = oneThumbSlider.getValue();
    +
    +  // Figure out the number of pixels per step.
    +  var numSteps = Math.round(
    +      (oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum()) / stepSize);
    +  var size = goog.style.getSize(oneThumbSlider.getElement());
    +  var pixelsPerStep = Math.round(size.width / numSteps);
    +
    +  var coords = goog.style.getClientPosition(oneThumbSlider.getElement());
    +  coords.x += pixelsPerStep / 2;
    +
    +  // Case when value is increased
    +  goog.testing.events.fireClickSequence(oneThumbSlider.getElement(),
    +      /* opt_button */ undefined, coords);
    +  assertEquals(oneThumbSlider.getValue(), initialValue + stepSize);
    +
    +  // Case when value is decreased
    +  goog.testing.events.fireClickSequence(oneThumbSlider.getElement(),
    +      /* opt_button */ undefined, coords);
    +  assertEquals(oneThumbSlider.getValue(), initialValue);
    +
    +  // Case when thumb is clicked
    +  goog.testing.events.fireClickSequence(oneThumbSlider.getElement());
    +  assertEquals(oneThumbSlider.getValue(), initialValue);
    +}
    +
    +function testNonIntegerStepSize() {
    +  var stepSize = 0.02;
    +  oneThumbSlider.setStep(stepSize);
    +  oneThumbSlider.setMinimum(-1);
    +  oneThumbSlider.setMaximum(1);
    +  oneThumbSlider.setValue(0.7);
    +  assertRoughlyEquals(0.7, oneThumbSlider.getValue(), 0.000001);
    +  oneThumbSlider.setValue(0.3);
    +  assertRoughlyEquals(0.3, oneThumbSlider.getValue(), 0.000001);
    +}
    +
    +function testSingleThumbSliderHasZeroExtent() {
    +  var stepSize = 0.02;
    +  oneThumbSlider.setStep(stepSize);
    +  oneThumbSlider.setMinimum(-1);
    +  oneThumbSlider.setMaximum(1);
    +  oneThumbSlider.setValue(0.7);
    +  assertEquals(0, oneThumbSlider.getExtent());
    +  oneThumbSlider.setValue(0.3);
    +  assertEquals(0, oneThumbSlider.getExtent());
    +}
    +
    +
    +/**
    + * Tests getThumbCoordinateForValue method.
    + */
    +function testThumbCoordinateForValueWithHorizontalSlider() {
    +  // Make sure the y-coordinate stays the same for the horizontal slider.
    +  var originalY = goog.style.getPosition(oneThumbSlider.valueThumb).y;
    +  var width = oneThumbSlider.getElement().clientWidth -
    +      oneThumbSlider.valueThumb.offsetWidth;
    +  var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
    +
    +  // Verify coordinate for a particular value.
    +  var value = 20;
    +  var expectedX = Math.round(value / range * width);
    +  var expectedCoord = new goog.math.Coordinate(expectedX, originalY);
    +  var coord = oneThumbSlider.getThumbCoordinateForValue(value);
    +  assertObjectEquals(expectedCoord, coord);
    +
    +  // Verify this works regardless of current position.
    +  oneThumbSlider.setValue(value / 2);
    +  coord = oneThumbSlider.getThumbCoordinateForValue(value);
    +  assertObjectEquals(expectedCoord, coord);
    +}
    +
    +function testThumbCoordinateForValueWithVerticalSlider() {
    +  // Make sure the x-coordinate stays the same for the vertical slider.
    +  oneThumbSlider.setOrientation(goog.ui.SliderBase.Orientation.VERTICAL);
    +  var originalX = goog.style.getPosition(oneThumbSlider.valueThumb).x;
    +  var height = oneThumbSlider.getElement().clientHeight -
    +      oneThumbSlider.valueThumb.offsetHeight;
    +  var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
    +
    +  // Verify coordinate for a particular value.
    +  var value = 20;
    +  var expectedY = height - Math.round(value / range * height);
    +  var expectedCoord = new goog.math.Coordinate(originalX, expectedY);
    +  var coord = oneThumbSlider.getThumbCoordinateForValue(value);
    +  assertObjectEquals(expectedCoord, coord);
    +
    +  // Verify this works regardless of current position.
    +  oneThumbSlider.setValue(value / 2);
    +  coord = oneThumbSlider.getThumbCoordinateForValue(value);
    +  assertObjectEquals(expectedCoord, coord);
    +}
    +
    +
    +/**
    + * Tests getValueFromMousePosition method.
    + */
    +function testValueFromMousePosition() {
    +  var value = 30;
    +  oneThumbSlider.setValue(value);
    +  var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
    +  var size = goog.style.getSize(oneThumbSlider.valueThumb);
    +  offset.x += size.width / 2;
    +  offset.y += size.height / 2;
    +  var e = null;
    +  goog.events.listen(oneThumbSlider, goog.events.EventType.MOUSEMOVE,
    +      function(evt) {
    +        e = evt;
    +      });
    +  goog.testing.events.fireMouseMoveEvent(oneThumbSlider, offset);
    +  assertNotEquals(e, null);
    +  assertEquals(
    +      value, Math.round(oneThumbSlider.getValueFromMousePosition(e)));
    +  // Verify this works regardless of current position.
    +  oneThumbSlider.setValue(value / 2);
    +  assertEquals(
    +      value, Math.round(oneThumbSlider.getValueFromMousePosition(e)));
    +}
    +
    +
    +/**
    + * Tests ignoring click event after mousedown event.
    + */
    +function testClickAfterMousedown() {
    +  // Get the center of the thumb at value zero.
    +  oneThumbSlider.setValue(0);
    +  var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
    +  var size = goog.style.getSize(oneThumbSlider.valueThumb);
    +  offset.x += size.width / 2;
    +  offset.y += size.height / 2;
    +
    +  var sliderElement = oneThumbSlider.getElement();
    +  var width = sliderElement.clientWidth - size.width;
    +  var range = oneThumbSlider.getMaximum() - oneThumbSlider.getMinimum();
    +  var offsetXAtZero = offset.x;
    +
    +  // Temporarily control time.
    +  var theTime = goog.now();
    +  var saveGoogNow = goog.now;
    +  goog.now = function() { return theTime; };
    +
    +  // set coordinate for a particular value.
    +  var valueOne = 10;
    +  offset.x = offsetXAtZero + Math.round(valueOne / range * width);
    +  goog.testing.events.fireMouseDownEvent(sliderElement, null, offset);
    +  assertEquals(valueOne, oneThumbSlider.getValue());
    +
    +  // Verify a click event with another value that follows quickly is ignored.
    +  theTime += oneThumbSlider.MOUSE_DOWN_DELAY_ / 2;
    +  var valueTwo = 20;
    +  offset.x = offsetXAtZero + Math.round(valueTwo / range * width);
    +  goog.testing.events.fireClickEvent(sliderElement, null, offset);
    +  assertEquals(valueOne, oneThumbSlider.getValue());
    +
    +  // Verify a click later in time does move the thumb.
    +  theTime += oneThumbSlider.MOUSE_DOWN_DELAY_;
    +  goog.testing.events.fireClickEvent(sliderElement, null, offset);
    +  assertEquals(valueTwo, oneThumbSlider.getValue());
    +
    +  goog.now = saveGoogNow;
    +}
    +
    +
    +/**
    + * Tests dragging events.
    + */
    +function testDragEvents() {
    +  var offset = goog.style.getPageOffset(oneThumbSlider.valueThumb);
    +  var size = goog.style.getSize(oneThumbSlider.valueThumb);
    +  offset.x += size.width / 2;
    +  offset.y += size.height / 2;
    +  var event_types = [];
    +  var handler = function(evt) {
    +    event_types.push(evt.type);
    +  };
    +
    +  goog.events.listen(oneThumbSlider,
    +      [goog.ui.SliderBase.EventType.DRAG_START,
    +       goog.ui.SliderBase.EventType.DRAG_END,
    +       goog.ui.SliderBase.EventType.DRAG_VALUE_START,
    +       goog.ui.SliderBase.EventType.DRAG_VALUE_END,
    +       goog.ui.SliderBase.EventType.DRAG_EXTENT_START,
    +       goog.ui.SliderBase.EventType.DRAG_EXTENT_END,
    +       goog.ui.Component.EventType.CHANGE],
    +      handler);
    +
    +  // Since the order of the events between value and extent is not guaranteed
    +  // accross browsers, we need to allow for both here and once we have
    +  // them all, make sure that they were different.
    +  function isValueOrExtentDragStart(type) {
    +    return type == goog.ui.SliderBase.EventType.DRAG_VALUE_START ||
    +        type == goog.ui.SliderBase.EventType.DRAG_EXTENT_START;
    +  };
    +  function isValueOrExtentDragEnd(type) {
    +    return type == goog.ui.SliderBase.EventType.DRAG_VALUE_END ||
    +        type == goog.ui.SliderBase.EventType.DRAG_EXTENT_END;
    +  };
    +
    +  // Test that dragging the thumb calls all the correct events.
    +  goog.testing.events.fireMouseDownEvent(oneThumbSlider.valueThumb);
    +  offset.x += 100;
    +  goog.testing.events.fireMouseMoveEvent(oneThumbSlider.valueThumb, offset);
    +  goog.testing.events.fireMouseUpEvent(oneThumbSlider.valueThumb);
    +
    +  assertEquals(9, event_types.length);
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[0]);
    +  assertTrue(isValueOrExtentDragStart(event_types[1]));
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[2]);
    +  assertTrue(isValueOrExtentDragStart(event_types[3]));
    +
    +  assertEquals(goog.ui.Component.EventType.CHANGE, event_types[4]);
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[5]);
    +  assertTrue(isValueOrExtentDragEnd(event_types[6]));
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[7]);
    +  assertTrue(isValueOrExtentDragEnd(event_types[8]));
    +
    +  assertFalse(event_types[1] == event_types[3]);
    +  assertFalse(event_types[6] == event_types[8]);
    +
    +  // Test that clicking the thumb without moving the mouse does not cause a
    +  // CHANGE event between DRAG_START/DRAG_END.
    +  event_types = [];
    +  goog.testing.events.fireMouseDownEvent(oneThumbSlider.valueThumb);
    +  goog.testing.events.fireMouseUpEvent(oneThumbSlider.valueThumb);
    +
    +  assertEquals(8, event_types.length);
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[0]);
    +  assertTrue(isValueOrExtentDragStart(event_types[1]));
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_START, event_types[2]);
    +  assertTrue(isValueOrExtentDragStart(event_types[3]));
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[4]);
    +  assertTrue(isValueOrExtentDragEnd(event_types[5]));
    +
    +  assertEquals(goog.ui.SliderBase.EventType.DRAG_END, event_types[6]);
    +  assertTrue(isValueOrExtentDragEnd(event_types[7]));
    +
    +  assertFalse(event_types[1] == event_types[3]);
    +  assertFalse(event_types[5] == event_types[7]);
    +
    +  // Early listener removal, do not wait for tearDown, to avoid building up
    +  // arrays of events unnecessarilly in further tests.
    +  goog.events.removeAll(oneThumbSlider);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitbehavior.js b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior.js
    new file mode 100644
    index 0000000000..ba69c8f24f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior.js
    @@ -0,0 +1,342 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Behavior for combining two controls.
    + *
    + * @see ../demos/split.html
    + */
    +
    +goog.provide('goog.ui.SplitBehavior');
    +goog.provide('goog.ui.SplitBehavior.DefaultHandlers');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.dispose');
    +goog.require('goog.dom');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.ui.ButtonSide');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.decorate');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Creates a behavior for combining two controls. The behavior is triggered
    + * by a given event type which applies the behavior handler.
    + * Can be used to also render or decorate  the controls.
    + * For a usage example see {@link goog.ui.ColorSplitBehavior}
    + *
    + * @param {goog.ui.Control} first A ui control.
    + * @param {goog.ui.Control} second A ui control.
    + * @param {function(goog.ui.Control,Event)=} opt_behaviorHandler A handler
    + *     to apply for the behavior.
    + * @param {string=} opt_eventType The event type triggering the
    + *     handler.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +goog.ui.SplitBehavior = function(first, second, opt_behaviorHandler,
    +    opt_eventType, opt_domHelper) {
    +  goog.Disposable.call(this);
    +
    +  /**
    +   * @type {goog.ui.Control}
    +   * @private
    +   */
    +  this.first_ = first;
    +
    +  /**
    +   * @type {goog.ui.Control}
    +   * @private
    +   */
    +  this.second_ = second;
    +
    +  /**
    +   * Handler for this behavior.
    +   * @type {function(goog.ui.Control,Event)}
    +   * @private
    +   */
    +  this.behaviorHandler_ = opt_behaviorHandler ||
    +                          goog.ui.SplitBehavior.DefaultHandlers.CAPTION;
    +
    +  /**
    +   * Event type triggering the behavior.
    +   * @type {string}
    +   * @private
    +   */
    +  this.eventType_ = opt_eventType || goog.ui.Component.EventType.ACTION;
    +
    +  /**
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * True iff the behavior is active.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.isActive_ = false;
    +
    +  /**
    +   * Event handler.
    +   * @type {goog.events.EventHandler}
    +   * @private
    +   */
    +  this.eventHandler_ = new goog.events.EventHandler();
    +
    +  /**
    +   * Whether to dispose the first control when dispose is called.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.disposeFirst_ = true;
    +
    +  /**
    +   * Whether to dispose the second control when dispose is called.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.disposeSecond_ = true;
    +};
    +goog.inherits(goog.ui.SplitBehavior, goog.Disposable);
    +goog.tagUnsealableClass(goog.ui.SplitBehavior);
    +
    +
    +/**
    + * Css class for elements rendered by this behavior.
    + * @type {string}
    + */
    +goog.ui.SplitBehavior.CSS_CLASS = goog.getCssName('goog-split-behavior');
    +
    +
    +/**
    + * An emum of split behavior handlers.
    + * @enum {function(goog.ui.Control,Event)}
    + */
    +goog.ui.SplitBehavior.DefaultHandlers = {
    +  NONE: goog.nullFunction,
    +  CAPTION: function(targetControl, e) {
    +    var item = /** @type {goog.ui.MenuItem} */ (e.target);
    +    var value = (/** @type {string} */((item && item.getValue()) || ''));
    +    var button = /** @type {goog.ui.Button} */ (targetControl);
    +    button.setCaption && button.setCaption(value);
    +    button.setValue && button.setValue(value);
    +  },
    +  VALUE: function(targetControl, e) {
    +    var item = /** @type {goog.ui.MenuItem} */ (e.target);
    +    var value = (/** @type {string} */(item && item.getValue()) || '');
    +    var button = /** @type {goog.ui.Button} */ (targetControl);
    +    button.setValue && button.setValue(value);
    +  }
    +};
    +
    +
    +/**
    + * The element containing the controls.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.SplitBehavior.prototype.element_ = null;
    +
    +
    +/**
    + * @return {Element} The element containing the controls.
    + */
    +goog.ui.SplitBehavior.prototype.getElement = function() {
    +  return this.element_;
    +};
    +
    +
    +/**
    + * @return {function(goog.ui.Control,Event)} The behavior handler.
    + */
    +goog.ui.SplitBehavior.prototype.getBehaviorHandler = function() {
    +  return this.behaviorHandler_;
    +};
    +
    +
    +/**
    + * @return {string} The behavior event type.
    + */
    +goog.ui.SplitBehavior.prototype.getEventType = function() {
    +  return this.eventType_;
    +};
    +
    +
    +/**
    + * Sets the disposeControls flags.
    + * @param {boolean} disposeFirst Whether to dispose the first control
    + *     when dispose is called.
    + * @param {boolean} disposeSecond Whether to dispose the second control
    + *     when dispose is called.
    + */
    +goog.ui.SplitBehavior.prototype.setDisposeControls = function(disposeFirst,
    +    disposeSecond) {
    +  this.disposeFirst_ = !!disposeFirst;
    +  this.disposeSecond_ = !!disposeSecond;
    +};
    +
    +
    +/**
    + * Sets the behavior handler.
    + * @param {function(goog.ui.Control,Event)} behaviorHandler The behavior
    + *     handler.
    + */
    +goog.ui.SplitBehavior.prototype.setHandler = function(behaviorHandler) {
    +  this.behaviorHandler_ = behaviorHandler;
    +  if (this.isActive_) {
    +    this.setActive(false);
    +    this.setActive(true);
    +  }
    +};
    +
    +
    +/**
    + * Sets the behavior event type.
    + * @param {string} eventType The behavior event type.
    + */
    +goog.ui.SplitBehavior.prototype.setEventType = function(eventType) {
    +  this.eventType_ = eventType;
    +  if (this.isActive_) {
    +    this.setActive(false);
    +    this.setActive(true);
    +  }
    +};
    +
    +
    +/**
    + * Decorates an element and returns the behavior.
    + * @param {Element} element An element to decorate.
    + * @param {boolean=} opt_activate Whether to activate the behavior
    + *     (default=true).
    + * @return {!goog.ui.SplitBehavior} A split behavior.
    + */
    +goog.ui.SplitBehavior.prototype.decorate = function(element, opt_activate) {
    +  if (this.first_ || this.second_) {
    +    throw Error('Cannot decorate controls are already set');
    +  }
    +  this.decorateChildren_(element);
    +  var activate = goog.isDefAndNotNull(opt_activate) ? !!opt_activate : true;
    +  this.element_ = element;
    +  this.setActive(activate);
    +  return this;
    +};
    +
    +
    +/**
    + * Renders an element and returns the behavior.
    + * @param {Element} element An element to decorate.
    + * @param {boolean=} opt_activate Whether to activate the behavior
    + *     (default=true).
    + * @return {!goog.ui.SplitBehavior} A split behavior.
    + */
    +goog.ui.SplitBehavior.prototype.render = function(element, opt_activate) {
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.SplitBehavior.CSS_CLASS);
    +  this.first_.render(element);
    +  this.second_.render(element);
    +  this.collapseSides_(this.first_, this.second_);
    +  var activate = goog.isDefAndNotNull(opt_activate) ? !!opt_activate : true;
    +  this.element_ = element;
    +  this.setActive(activate);
    +  return this;
    +};
    +
    +
    +/**
    + * Activate or deactivate the behavior.
    + * @param {boolean} activate Whether to activate or deactivate the behavior.
    + */
    +goog.ui.SplitBehavior.prototype.setActive = function(activate) {
    +  if (this.isActive_ == activate) {
    +    return;
    +  }
    +  this.isActive_ = activate;
    +  if (activate) {
    +    this.eventHandler_.listen(this.second_, this.eventType_,
    +        goog.bind(this.behaviorHandler_, this, this.first_));
    +    // TODO(user): should we call the handler here to sync between
    +    // first_ and second_.
    +  } else {
    +    this.eventHandler_.removeAll();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.SplitBehavior.prototype.disposeInternal = function() {
    +  this.setActive(false);
    +  goog.dispose(this.eventHandler_);
    +  if (this.disposeFirst_) {
    +    goog.dispose(this.first_);
    +  }
    +  if (this.disposeSecond_) {
    +    goog.dispose(this.second_);
    +  }
    +  goog.ui.SplitBehavior.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * Decorates two child nodes of the given element.
    + * @param {Element} element An element to render two of it's child nodes.
    + * @private
    + */
    +goog.ui.SplitBehavior.prototype.decorateChildren_ = function(
    +    element) {
    +  var childNodes = element.childNodes;
    +  var len = childNodes.length;
    +  var finished = false;
    +  for (var i = 0; i < len && !finished; i++) {
    +    var child = childNodes[i];
    +    if (child.nodeType == goog.dom.NodeType.ELEMENT) {
    +      if (!this.first_) {
    +        this.first_ = /** @type {goog.ui.Control} */ (goog.ui.decorate(child));
    +      } else if (!this.second_) {
    +        this.second_ = /** @type {goog.ui.Control} */ (goog.ui.decorate(child));
    +        finished = true;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Collapse the the controls together.
    + * @param {goog.ui.Control} first The first element.
    + * @param {goog.ui.Control} second The second element.
    + * @private
    + */
    +goog.ui.SplitBehavior.prototype.collapseSides_ = function(first, second) {
    +  if (goog.isFunction(first.setCollapsed) &&
    +      goog.isFunction(second.setCollapsed)) {
    +    first.setCollapsed(goog.ui.ButtonSide.END);
    +    second.setCollapsed(goog.ui.ButtonSide.START);
    +  }
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Buttons.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.SplitBehavior.CSS_CLASS,
    +    function() {
    +      return new goog.ui.SplitBehavior(null, null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.html b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.html
    new file mode 100644
    index 0000000000..b6e352441e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.SplitBehavior
    +  </title>
    +  <script type="text/javascript" src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.SplitBehaviorTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="split">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.js b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.js
    new file mode 100644
    index 0000000000..1104056442
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitbehavior_test.js
    @@ -0,0 +1,154 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SplitBehaviorTest');
    +goog.setTestOnly('goog.ui.SplitBehaviorTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.CustomButton');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.SplitBehavior');
    +goog.require('goog.ui.decorate');
    +
    +var splitbehavior;
    +var button;
    +var menuValues;
    +var menu;
    +var menuButton;
    +var splitDiv;
    +
    +function setUp() {
    +  splitDiv = document.getElementById('split');
    +  button = new goog.ui.CustomButton('text');
    +  menu = new goog.ui.Menu();
    +  menuValues = ['a', 'b', 'c'];
    +  goog.array.forEach(menuValues, function(val) {
    +    menu.addItem(new goog.ui.MenuItem(val));
    +  });
    +  menuButton = new goog.ui.MenuButton('text', menu);
    +  splitbehavior = new goog.ui.SplitBehavior(button, menuButton);
    +}
    +
    +function tearDown() {
    +  button.dispose();
    +  menu.dispose();
    +  menuButton.dispose();
    +  splitbehavior.dispose();
    +  splitDiv.innerHTML = '';
    +  splitDiv.className = '';
    +}
    +
    +function testRender() {
    +  assertEquals('no elements in doc with goog-split-behavior class',
    +      0, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-split-behavior').length);
    +  splitbehavior.render(splitDiv);
    +  assertEquals('two childs are rendered', 2, splitDiv.childNodes.length);
    +  assertEquals('one element in doc with goog-split-behavior class',
    +      1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-split-behavior').length);
    +  assertEquals('one goog-custom-button',
    +      1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-custom-button', splitDiv).length);
    +  assertEquals('one goog-menu-button',
    +      1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-menu-button', splitDiv).length);
    +}
    +
    +function testDecorate() {
    +  var decorateDiv = goog.dom.createDom('div', 'goog-split-behavior',
    +      goog.dom.createDom('div', 'goog-custom-button'),
    +      goog.dom.createDom('div', 'goog-menu-button'));
    +  goog.dom.appendChild(splitDiv, decorateDiv);
    +  var split = goog.ui.decorate(decorateDiv);
    +  assertNotNull(split);
    +  assertTrue('instance of SplitBehavior',
    +      split.constructor == goog.ui.SplitBehavior);
    +  assertNotNull(split.first_);
    +  assertTrue('instance of CustomButton',
    +      split.first_.constructor == goog.ui.CustomButton);
    +  assertNotNull(split.second_);
    +  assertTrue('instance of MenuButton',
    +      split.second_.constructor == goog.ui.MenuButton);
    +}
    +
    +function testBehaviorDefault() {
    +  splitbehavior.render(splitDiv);
    +  assertEquals('original caption is "text"', 'text', button.getCaption());
    +  var menuItem = menuButton.getMenu().getChildAt(0);
    +  var type = goog.ui.Component.EventType.ACTION;
    +  goog.events.dispatchEvent(menuButton, new goog.events.Event(type, menuItem));
    +  assertEquals('caption is updated to "a"', 'a', button.getCaption());
    +}
    +
    +function testBehaviorCustomEvent() {
    +  splitbehavior.render(splitDiv);
    +  assertEquals('original caption is "text"', 'text', button.getCaption());
    +  var type = goog.ui.Component.EventType.ENTER;
    +  splitbehavior.setEventType(type);
    +  var menuItem = menuButton.getMenu().getChildAt(0);
    +  goog.events.dispatchEvent(menuButton, new goog.events.Event(type, menuItem));
    +  assertEquals('caption is updated to "a"', 'a', button.getCaption());
    +}
    +
    +function testBehaviorCustomHandler() {
    +  splitbehavior.render(splitDiv);
    +  var called = false;
    +  splitbehavior.setHandler(function() { called = true; });
    +  goog.events.dispatchEvent(menuButton, goog.ui.Component.EventType.ACTION);
    +  assertTrue('custom handler is called', called);
    +}
    +
    +function testSetActive() {
    +  splitbehavior.render(splitDiv, false);
    +  assertEquals('original caption is "text"', 'text', button.getCaption());
    +  var menuItem = menuButton.getMenu().getChildAt(0);
    +  var type = goog.ui.Component.EventType.ACTION;
    +  goog.events.dispatchEvent(menuButton, new goog.events.Event(type, menuItem));
    +  assertEquals('caption remains "text"', 'text', button.getCaption());
    +
    +  splitbehavior.setActive(true);
    +  goog.events.dispatchEvent(menuButton, new goog.events.Event(type, menuItem));
    +  assertEquals('caption is updated to "a"', 'a', button.getCaption());
    +}
    +
    +function testDispose() {
    +  goog.dispose(splitbehavior);
    +  assertTrue(splitbehavior.isDisposed());
    +  assertTrue(splitbehavior.first_.isDisposed());
    +  assertTrue(splitbehavior.second_.isDisposed());
    +}
    +
    +function testDisposeNoControls() {
    +  splitbehavior.setDisposeControls(false);
    +  goog.dispose(splitbehavior);
    +  assertTrue(splitbehavior.isDisposed());
    +  assertFalse(splitbehavior.first_.isDisposed());
    +  assertFalse(splitbehavior.second_.isDisposed());
    +}
    +
    +function testDisposeFirstAndNotSecondControl() {
    +  splitbehavior.setDisposeControls(true, false);
    +  goog.dispose(splitbehavior);
    +  assertTrue(splitbehavior.isDisposed());
    +  assertTrue(splitbehavior.first_.isDisposed());
    +  assertFalse(splitbehavior.second_.isDisposed());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitpane.js b/mapbox-gl/closure-library/closure/goog/ui/splitpane.js
    new file mode 100644
    index 0000000000..5a8975a103
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitpane.js
    @@ -0,0 +1,909 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview  Class for splitting two areas with draggable control for
    + * changing size.
    + *
    + * The DOM that is created (or that can be decorated) looks like this:
    + * <div class='goog-splitpane'>
    + *   <div class='goog-splitpane-first-container'></div>
    + *   <div class='goog-splitpane-second-container'></div>
    + *   <div class='goog-splitpane-handle'></div>
    + * </div>
    + *
    + * The content to be split goes in the first and second DIVs, the third one
    + * is for managing (and styling) the splitter handle.
    + *
    + * @see ../demos/splitpane.html
    + */
    +
    +
    +goog.provide('goog.ui.SplitPane');
    +goog.provide('goog.ui.SplitPane.Orientation');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.math.Rect');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A left/right up/down Container SplitPane.
    + * Create SplitPane with two goog.ui.Component opjects to split.
    + * TODO(user): Support minimum splitpane size.
    + * TODO(user): Allow component change/orientation after init.
    + * TODO(user): Support hiding either side of handle (plus handle).
    + * TODO(user): Look at setBorderBoxSize fixes and revist borderwidth code.
    + *
    + * @param {goog.ui.Component} firstComponent Left or Top component.
    + * @param {goog.ui.Component} secondComponent Right or Bottom component.
    + * @param {goog.ui.SplitPane.Orientation} orientation SplitPane orientation.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @extends {goog.ui.Component}
    + * @constructor
    + */
    +goog.ui.SplitPane = function(firstComponent, secondComponent, orientation,
    +    opt_domHelper) {
    +  goog.ui.SplitPane.base(this, 'constructor', opt_domHelper);
    +
    +  /**
    +   * The orientation of the containers.
    +   * @type {goog.ui.SplitPane.Orientation}
    +   * @private
    +   */
    +  this.orientation_ = orientation;
    +
    +  /**
    +   * The left/top component.
    +   * @type {goog.ui.Component}
    +   * @private
    +   */
    +  this.firstComponent_ = firstComponent;
    +  this.addChild(firstComponent);
    +
    +  /**
    +   * The right/bottom component.
    +   * @type {goog.ui.Component}
    +   * @private
    +   */
    +  this.secondComponent_ = secondComponent;
    +  this.addChild(secondComponent);
    +
    +  /** @private {Element} */
    +  this.splitpaneHandle_ = null;
    +};
    +goog.inherits(goog.ui.SplitPane, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.SplitPane);
    +
    +
    +/**
    + * Events.
    + * @enum {string}
    + */
    +goog.ui.SplitPane.EventType = {
    +
    +  /**
    +   * Dispatched after handle drag.
    +   */
    +  HANDLE_DRAG: 'handle_drag',
    +
    +  /**
    +   * Dispatched after handle drag end.
    +   */
    +  HANDLE_DRAG_END: 'handle_drag_end',
    +
    +  /**
    +   * Dispatched after handle snap (double-click splitter).
    +   */
    +  HANDLE_SNAP: 'handle_snap'
    +};
    +
    +
    +/**
    + * CSS class names for splitpane outer container.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.CLASS_NAME_ = goog.getCssName('goog-splitpane');
    +
    +
    +/**
    + * CSS class name for first splitpane container.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.FIRST_CONTAINER_CLASS_NAME_ =
    +    goog.getCssName('goog-splitpane-first-container');
    +
    +
    +/**
    + * CSS class name for second splitpane container.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.SECOND_CONTAINER_CLASS_NAME_ =
    +    goog.getCssName('goog-splitpane-second-container');
    +
    +
    +/**
    + * CSS class name for the splitpane handle.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.HANDLE_CLASS_NAME_ = goog.getCssName('goog-splitpane-handle');
    +
    +
    +/**
    + * CSS class name for the splitpane handle in horizontal orientation.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.HANDLE_CLASS_NAME_HORIZONTAL_ =
    +    goog.getCssName('goog-splitpane-handle-horizontal');
    +
    +
    +/**
    + * CSS class name for the splitpane handle in horizontal orientation.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SplitPane.HANDLE_CLASS_NAME_VERTICAL_ =
    +    goog.getCssName('goog-splitpane-handle-vertical');
    +
    +
    +/**
    +  * The dragger to move the drag handle.
    +  * @type {goog.fx.Dragger?}
    +  * @private
    +  */
    +goog.ui.SplitPane.prototype.splitDragger_ = null;
    +
    +
    +/**
    + * The left/top component dom container.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.firstComponentContainer_ = null;
    +
    +
    +/**
    + * The right/bottom component dom container.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.secondComponentContainer_ = null;
    +
    +
    +/**
    + * The size (width or height) of the splitpane handle, default = 5.
    + * @type {number}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.handleSize_ = 5;
    +
    +
    +/**
    + * The initial size (width or height) of the left or top component.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.initialSize_ = null;
    +
    +
    +/**
    + * The saved size (width or height) of the left or top component on a
    + * double-click (snap).
    + * This needs to be saved so it can be restored after another double-click.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.savedSnapSize_ = null;
    +
    +
    +/**
    + * The first component size, so we don't change it on a window resize.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.firstComponentSize_ = null;
    +
    +
    +/**
    + * If we resize as they user moves the handle (default = true).
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.continuousResize_ = true;
    +
    +
    +/**
    + * Iframe overlay to prevent iframes from grabbing events.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.SplitPane.prototype.iframeOverlay_ = null;
    +
    +
    +/**
    + * Z indices for iframe overlay and splitter handle.
    + * @enum {number}
    + * @private
    + */
    +goog.ui.SplitPane.IframeOverlayIndex_ = {
    +  HIDDEN: -1,
    +  OVERLAY: 1,
    +  SPLITTER_HANDLE: 2
    +};
    +
    +
    +/**
    +* Orientation values for the splitpane.
    +* @enum {string}
    +*/
    +goog.ui.SplitPane.Orientation = {
    +
    +  /**
    +   * Horizontal orientation means splitter moves right-left.
    +   */
    +  HORIZONTAL: 'horizontal',
    +
    +  /**
    +   * Vertical orientation means splitter moves up-down.
    +   */
    +  VERTICAL: 'vertical'
    +};
    +
    +
    +/**
    + * Create the DOM node & text node needed for the splitpane.
    + * @override
    + */
    +goog.ui.SplitPane.prototype.createDom = function() {
    +  var dom = this.getDomHelper();
    +
    +  // Create the components.
    +  var firstContainer = dom.createDom('div',
    +      goog.ui.SplitPane.FIRST_CONTAINER_CLASS_NAME_);
    +  var secondContainer = dom.createDom('div',
    +      goog.ui.SplitPane.SECOND_CONTAINER_CLASS_NAME_);
    +  var splitterHandle = dom.createDom('div',
    +      goog.ui.SplitPane.HANDLE_CLASS_NAME_);
    +
    +  // Create the primary element, a DIV that holds the two containers and handle.
    +  this.setElementInternal(dom.createDom('div', goog.ui.SplitPane.CLASS_NAME_,
    +      firstContainer, secondContainer, splitterHandle));
    +
    +  this.firstComponentContainer_ = firstContainer;
    +  this.secondComponentContainer_ = secondContainer;
    +  this.splitpaneHandle_ = splitterHandle;
    +  this.setUpHandle_();
    +
    +  this.finishSetup_();
    +};
    +
    +
    +/**
    + * Determines if a given element can be decorated by this type of component.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} True if the element can be decorated, false otherwise.
    + * @override
    + */
    +goog.ui.SplitPane.prototype.canDecorate = function(element) {
    +  var className = goog.ui.SplitPane.FIRST_CONTAINER_CLASS_NAME_;
    +  var firstContainer = this.getElementToDecorate_(element, className);
    +  if (!firstContainer) {
    +    return false;
    +  }
    +  // Since we have this component, save it so we don't have to get it
    +  // again in decorateInternal.  Same w/other components.
    +  this.firstComponentContainer_ = firstContainer;
    +
    +  className = goog.ui.SplitPane.SECOND_CONTAINER_CLASS_NAME_;
    +  var secondContainer = this.getElementToDecorate_(element, className);
    +
    +  if (!secondContainer) {
    +    return false;
    +  }
    +  this.secondComponentContainer_ = secondContainer;
    +
    +  className = goog.ui.SplitPane.HANDLE_CLASS_NAME_;
    +  var splitpaneHandle = this.getElementToDecorate_(element, className);
    +  if (!splitpaneHandle) {
    +    return false;
    +  }
    +  this.splitpaneHandle_ = splitpaneHandle;
    +
    +  // We found all the components we're looking for, so return true.
    +  return true;
    +};
    +
    +
    +/**
    + * Obtains the element to be decorated by class name. If multiple such elements
    + * are found, preference is given to those directly attached to the specified
    + * root element.
    + * @param {Element} rootElement The root element from which to retrieve the
    + *     element to be decorated.
    + * @param {!string} className The target class name.
    + * @return {Element} The element to decorate.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.getElementToDecorate_ = function(rootElement,
    +    className) {
    +
    +  // Decorate the root element's children, if available.
    +  var childElements = goog.dom.getChildren(rootElement);
    +  for (var i = 0; i < childElements.length; i++) {
    +    var childElement = goog.asserts.assertElement(childElements[i]);
    +    if (goog.dom.classlist.contains(childElement, className)) {
    +      return childElement;
    +    }
    +  }
    +
    +  // Default to the first descendent element with the correct class.
    +  return goog.dom.getElementsByTagNameAndClass(
    +      null, className, rootElement)[0];
    +};
    +
    +
    +/**
    + * Decorates the given HTML element as a SplitPane.  Overrides {@link
    + * goog.ui.Component#decorateInternal}.  Considered protected.
    + * @param {Element} element Element (SplitPane div) to decorate.
    + * @protected
    + * @override
    + */
    +goog.ui.SplitPane.prototype.decorateInternal = function(element) {
    +  goog.ui.SplitPane.base(this, 'decorateInternal', element);
    +
    +  this.setUpHandle_();
    +
    +  var elSize = goog.style.getBorderBoxSize(element);
    +  this.setSize(new goog.math.Size(elSize.width, elSize.height));
    +
    +  this.finishSetup_();
    +};
    +
    +
    +/**
    + * Parent the passed in components to the split containers.  Call their
    + * createDom methods if necessary.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.finishSetup_ = function() {
    +  var dom = this.getDomHelper();
    +
    +  if (!this.firstComponent_.getElement()) {
    +    this.firstComponent_.createDom();
    +  }
    +
    +  dom.appendChild(this.firstComponentContainer_,
    +      this.firstComponent_.getElement());
    +
    +  if (!this.secondComponent_.getElement()) {
    +    this.secondComponent_.createDom();
    +  }
    +
    +  dom.appendChild(this.secondComponentContainer_,
    +      this.secondComponent_.getElement());
    +
    +  this.splitDragger_ = new goog.fx.Dragger(this.splitpaneHandle_,
    +      this.splitpaneHandle_);
    +
    +  this.firstComponentContainer_.style.position = 'absolute';
    +  this.secondComponentContainer_.style.position = 'absolute';
    +  var handleStyle = this.splitpaneHandle_.style;
    +  handleStyle.position = 'absolute';
    +  handleStyle.overflow = 'hidden';
    +  handleStyle.zIndex =
    +      goog.ui.SplitPane.IframeOverlayIndex_.SPLITTER_HANDLE;
    +};
    +
    +
    +/**
    + * Setup all events and do an initial resize.
    + * @override
    + */
    +goog.ui.SplitPane.prototype.enterDocument = function() {
    +  goog.ui.SplitPane.base(this, 'enterDocument');
    +
    +  // If position is not set in the inline style of the element, it is not
    +  // possible to get the element's real CSS position until the element is in
    +  // the document.
    +  // When position:relative is set in the CSS and the element is not in the
    +  // document, Safari, Chrome, and Opera always return the empty string; while
    +  // IE always return "static".
    +  // Do the final check to see if element's position is set as "relative",
    +  // "absolute" or "fixed".
    +  var element = this.getElement();
    +  if (goog.style.getComputedPosition(element) == 'static') {
    +    element.style.position = 'relative';
    +  }
    +
    +  this.getHandler().
    +      listen(this.splitpaneHandle_, goog.events.EventType.DBLCLICK,
    +          this.handleDoubleClick_).
    +      listen(this.splitDragger_, goog.fx.Dragger.EventType.START,
    +          this.handleDragStart_).
    +      listen(this.splitDragger_, goog.fx.Dragger.EventType.DRAG,
    +          this.handleDrag_).
    +      listen(this.splitDragger_, goog.fx.Dragger.EventType.END,
    +          this.handleDragEnd_);
    +
    +  this.setFirstComponentSize(this.initialSize_);
    +};
    +
    +
    +/**
    + * Sets the initial size of the left or top component.
    + * @param {number} size The size in Pixels of the container.
    + */
    +goog.ui.SplitPane.prototype.setInitialSize = function(size) {
    +  this.initialSize_ = size;
    +};
    +
    +
    +/**
    + * Sets the SplitPane handle size.
    + * TODO(user): Make sure this works after initialization.
    + * @param {number} size The size of the handle in pixels.
    + */
    +goog.ui.SplitPane.prototype.setHandleSize = function(size) {
    +  this.handleSize_ = size;
    +};
    +
    +
    +/**
    + * Sets whether we resize on handle drag.
    + * @param {boolean} continuous The continuous resize value.
    + */
    +goog.ui.SplitPane.prototype.setContinuousResize = function(continuous) {
    +  this.continuousResize_ = continuous;
    +};
    +
    +
    +/**
    + * Returns whether the orientation for the split pane is vertical
    + * or not.
    + * @return {boolean} True if the orientation is vertical, false otherwise.
    + */
    +goog.ui.SplitPane.prototype.isVertical = function() {
    +  return this.orientation_ == goog.ui.SplitPane.Orientation.VERTICAL;
    +};
    +
    +
    +/**
    + * Initializes the handle by assigning the correct height/width and adding
    + * the correct class as per the orientation.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.setUpHandle_ = function() {
    +  if (this.isVertical()) {
    +    this.splitpaneHandle_.style.height = this.handleSize_ + 'px';
    +    goog.dom.classlist.add(this.splitpaneHandle_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_VERTICAL_);
    +  } else {
    +    this.splitpaneHandle_.style.width = this.handleSize_ + 'px';
    +    goog.dom.classlist.add(this.splitpaneHandle_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_HORIZONTAL_);
    +  }
    +};
    +
    +
    +/**
    + * Sets the orientation class for the split pane handle.
    + * @protected
    + */
    +goog.ui.SplitPane.prototype.setOrientationClassForHandle = function() {
    +  goog.asserts.assert(this.splitpaneHandle_);
    +  if (this.isVertical()) {
    +    goog.dom.classlist.swap(this.splitpaneHandle_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_HORIZONTAL_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_VERTICAL_);
    +  } else {
    +    goog.dom.classlist.swap(this.splitpaneHandle_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_VERTICAL_,
    +        goog.ui.SplitPane.HANDLE_CLASS_NAME_HORIZONTAL_);
    +  }
    +};
    +
    +
    +/**
    + * Sets the orientation of the split pane.
    + * @param {goog.ui.SplitPane.Orientation} orientation SplitPane orientation.
    + */
    +goog.ui.SplitPane.prototype.setOrientation = function(orientation) {
    +  if (this.orientation_ != orientation) {
    +    this.orientation_ = orientation;
    +    var isVertical = this.isVertical();
    +
    +    // If the split pane is already in document, then the positions and sizes
    +    // need to be adjusted.
    +    if (this.isInDocument()) {
    +      this.setOrientationClassForHandle();
    +      // TODO(user): Should handleSize_ and initialSize_ also be adjusted ?
    +      if (goog.isNumber(this.firstComponentSize_)) {
    +        var splitpaneSize = goog.style.getBorderBoxSize(this.getElement());
    +        var ratio = isVertical ? splitpaneSize.height / splitpaneSize.width :
    +            splitpaneSize.width / splitpaneSize.height;
    +        // TODO(user): Fix the behaviour for the case when the handle is
    +        // placed on either of  the edges of the split pane. Also, similar
    +        // behaviour is present in {@link #setSize}. Probably need to modify
    +        // {@link #setFirstComponentSize}.
    +        this.setFirstComponentSize(this.firstComponentSize_ * ratio);
    +      } else {
    +        this.setFirstComponentSize();
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Gets the orientation of the split pane.
    + * @return {goog.ui.SplitPane.Orientation} The orientation.
    + */
    +goog.ui.SplitPane.prototype.getOrientation = function() {
    +  return this.orientation_;
    +};
    +
    +
    +/**
    + * Move and resize a container.  The sizing changes the BorderBoxSize.
    + * @param {Element} element The element to move and size.
    + * @param {goog.math.Rect} rect The top, left, width and height to change to.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.moveAndSize_ = function(element, rect) {
    +  goog.style.setPosition(element, rect.left, rect.top);
    +  // TODO(user): Add a goog.math.Size.max call for below.
    +  goog.style.setBorderBoxSize(element,
    +      new goog.math.Size(Math.max(rect.width, 0), Math.max(rect.height, 0)));
    +};
    +
    +
    +/**
    + * @return {?number} The size of the left/top component.
    + */
    +goog.ui.SplitPane.prototype.getFirstComponentSize = function() {
    +  return this.firstComponentSize_;
    +};
    +
    +
    +/**
    + * Set the size of the left/top component, and resize the other component based
    + * on that size and handle size.
    + * @param {?number=} opt_size The size of the top or left, in pixels. If
    + *     unspecified, leaves the size of the first component unchanged but adjusts
    + *     the size of the second component to fit the split pane size.
    + */
    +goog.ui.SplitPane.prototype.setFirstComponentSize = function(opt_size) {
    +  this.setFirstComponentSize_(
    +      goog.style.getBorderBoxSize(this.getElement()), opt_size);
    +};
    +
    +
    +/**
    + * Set the size of the left/top component, and resize the other component based
    + * on that size and handle size. Unlike the public method, this takes the
    + * current pane size which avoids the expensive getBorderBoxSize() call
    + * when we have the size available.
    + *
    + * @param {!goog.math.Size} splitpaneSize The current size of the splitpane.
    + * @param {?number=} opt_size The size of the top or left, in pixels.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.setFirstComponentSize_ = function(
    +    splitpaneSize, opt_size) {
    +  var top = 0, left = 0;
    +
    +  var isVertical = this.isVertical();
    +  // Figure out first component size; it's either passed in, taken from the
    +  // saved size, or is half of the total size.
    +  var firstComponentSize = goog.isNumber(opt_size) ? opt_size :
    +      goog.isNumber(this.firstComponentSize_) ? this.firstComponentSize_ :
    +      Math.floor((isVertical ? splitpaneSize.height : splitpaneSize.width) / 2);
    +  this.firstComponentSize_ = firstComponentSize;
    +
    +  var firstComponentWidth;
    +  var firstComponentHeight;
    +  var secondComponentWidth;
    +  var secondComponentHeight;
    +  var handleWidth;
    +  var handleHeight;
    +  var secondComponentLeft;
    +  var secondComponentTop;
    +  var handleLeft;
    +  var handleTop;
    +
    +  if (isVertical) {
    +
    +    // Width for the handle and the first and second components will be the
    +    // width of the split pane. The height for the first component will be
    +    // the calculated first component size. The height for the second component
    +    // will be the  total height minus the heights of the first component and
    +    // the handle.
    +    firstComponentHeight = firstComponentSize;
    +    firstComponentWidth = splitpaneSize.width;
    +    handleWidth = splitpaneSize.width;
    +    handleHeight = this.handleSize_;
    +    secondComponentHeight = splitpaneSize.height - firstComponentHeight -
    +        handleHeight;
    +    secondComponentWidth = splitpaneSize.width;
    +    handleTop = top + firstComponentHeight;
    +    handleLeft = left;
    +    secondComponentTop = handleTop + handleHeight;
    +    secondComponentLeft = left;
    +  } else {
    +
    +    // Height for the handle and the first and second components will be the
    +    // height of the split pane. The width for the first component will be
    +    // the calculated first component size. The width for the second component
    +    // will be the  total width minus the widths of the first component and
    +    // the handle.
    +    firstComponentWidth = firstComponentSize;
    +    firstComponentHeight = splitpaneSize.height;
    +    handleWidth = this.handleSize_;
    +    handleHeight = splitpaneSize.height;
    +    secondComponentWidth = splitpaneSize.width - firstComponentWidth -
    +        handleWidth;
    +    secondComponentHeight = splitpaneSize.height;
    +    handleLeft = left + firstComponentWidth;
    +    handleTop = top;
    +    secondComponentLeft = handleLeft + handleWidth;
    +    secondComponentTop = top;
    +  }
    +
    +  // Now move and size the containers.
    +  this.moveAndSize_(this.firstComponentContainer_,
    +      new goog.math.Rect(left, top, firstComponentWidth, firstComponentHeight));
    +
    +  if (typeof this.firstComponent_.resize == 'function') {
    +    this.firstComponent_.resize(new goog.math.Size(
    +        firstComponentWidth, firstComponentHeight));
    +  }
    +
    +  this.moveAndSize_(this.splitpaneHandle_, new goog.math.Rect(handleLeft,
    +      handleTop, handleWidth, handleHeight));
    +
    +  this.moveAndSize_(this.secondComponentContainer_,
    +      new goog.math.Rect(secondComponentLeft, secondComponentTop,
    +          secondComponentWidth, secondComponentHeight));
    +
    +  if (typeof this.secondComponent_.resize == 'function') {
    +    this.secondComponent_.resize(new goog.math.Size(secondComponentWidth,
    +        secondComponentHeight));
    +  }
    +  // Fire a CHANGE event.
    +  this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * Set the size of the splitpane.  This is usually called by the controlling
    + * application.  This will set the SplitPane BorderBoxSize.
    + * @param {!goog.math.Size} size The size to set the splitpane.
    + * @param {?number=} opt_firstComponentSize The size of the top or left
    + *     component, in pixels.
    + */
    +goog.ui.SplitPane.prototype.setSize = function(size, opt_firstComponentSize) {
    +  goog.style.setBorderBoxSize(this.getElement(), size);
    +  if (this.iframeOverlay_) {
    +    goog.style.setBorderBoxSize(this.iframeOverlay_, size);
    +  }
    +  this.setFirstComponentSize_(size, opt_firstComponentSize);
    +};
    +
    +
    +/**
    + * Snap the container to the left or top on a Double-click.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.snapIt_ = function() {
    +  var handlePos = goog.style.getRelativePosition(this.splitpaneHandle_,
    +      this.firstComponentContainer_);
    +  var firstBorderBoxSize =
    +      goog.style.getBorderBoxSize(this.firstComponentContainer_);
    +  var firstContentBoxSize =
    +      goog.style.getContentBoxSize(this.firstComponentContainer_);
    +
    +  var isVertical = this.isVertical();
    +
    +  // Where do we snap the handle (what size to make the component) and what
    +  // is the current handle position.
    +  var snapSize;
    +  var handlePosition;
    +  if (isVertical) {
    +    snapSize = firstBorderBoxSize.height - firstContentBoxSize.height;
    +    handlePosition = handlePos.y;
    +  } else {
    +    snapSize = firstBorderBoxSize.width - firstContentBoxSize.width;
    +    handlePosition = handlePos.x;
    +  }
    +
    +  if (snapSize == handlePosition) {
    +    // This means we're 'unsnapping', set it back to where it was.
    +    this.setFirstComponentSize(this.savedSnapSize_);
    +  } else {
    +    // This means we're 'snapping', set the size to snapSize, and hide the
    +    // first component.
    +    if (isVertical) {
    +      this.savedSnapSize_ = goog.style.getBorderBoxSize(
    +          this.firstComponentContainer_).height;
    +    } else {
    +      this.savedSnapSize_ = goog.style.getBorderBoxSize(
    +          this.firstComponentContainer_).width;
    +    }
    +    this.setFirstComponentSize(snapSize);
    +  }
    +
    +  // Fire a SNAP event.
    +  this.dispatchEvent(goog.ui.SplitPane.EventType.HANDLE_SNAP);
    +};
    +
    +
    +/**
    + * Handle the start drag event - set up the dragger.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.handleDragStart_ = function(e) {
    +
    +  // Setup iframe overlay to prevent iframes from grabbing events.
    +  if (!this.iframeOverlay_) {
    +    // Create the overlay.
    +    var cssStyles = 'position: relative';
    +
    +    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('10')) {
    +      // IE doesn't look at this div unless it has a background, so we'll
    +      // put one on, but make it opaque.
    +      cssStyles += ';background-color: #000;filter: Alpha(Opacity=0)';
    +    }
    +    this.iframeOverlay_ =
    +        this.getDomHelper().createDom('div', {'style': cssStyles});
    +
    +    this.getDomHelper().appendChild(this.getElement(), this.iframeOverlay_);
    +  }
    +  this.iframeOverlay_.style.zIndex =
    +      goog.ui.SplitPane.IframeOverlayIndex_.OVERLAY;
    +
    +  goog.style.setBorderBoxSize(this.iframeOverlay_,
    +      goog.style.getBorderBoxSize(this.getElement()));
    +
    +  var pos = goog.style.getPosition(this.firstComponentContainer_);
    +
    +  // For the size of the limiting box, we add the container content box sizes
    +  // so that if the handle is placed all the way to the end or the start, the
    +  // border doesn't exceed the total size. For position, we add the difference
    +  // between the border box and content box sizes of the first container to the
    +  // position of the first container. The start position should be such that
    +  // there is no overlap of borders.
    +  var limitWidth = 0;
    +  var limitHeight = 0;
    +  var limitx = pos.x;
    +  var limity = pos.y;
    +  var firstBorderBoxSize =
    +      goog.style.getBorderBoxSize(this.firstComponentContainer_);
    +  var firstContentBoxSize =
    +      goog.style.getContentBoxSize(this.firstComponentContainer_);
    +  var secondContentBoxSize =
    +      goog.style.getContentBoxSize(this.secondComponentContainer_);
    +  if (this.isVertical()) {
    +    limitHeight = firstContentBoxSize.height + secondContentBoxSize.height;
    +    limity += firstBorderBoxSize.height - firstContentBoxSize.height;
    +  } else {
    +    limitWidth = firstContentBoxSize.width + secondContentBoxSize.width;
    +    limitx += firstBorderBoxSize.width - firstContentBoxSize.width;
    +  }
    +  var limits = new goog.math.Rect(limitx, limity, limitWidth, limitHeight);
    +  this.splitDragger_.setLimits(limits);
    +};
    +
    +
    +/**
    + * Find the location relative to the splitpane.
    + * @param {number} left The x location relative to the window.
    + * @return {number} The relative x location.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.getRelativeLeft_ = function(left) {
    +  return left - goog.style.getPosition(this.firstComponentContainer_).x;
    +};
    +
    +
    +/**
    + * Find the location relative to the splitpane.
    + * @param {number} top The y location relative to the window.
    + * @return {number} The relative y location.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.getRelativeTop_ = function(top) {
    +  return top - goog.style.getPosition(this.firstComponentContainer_).y;
    +};
    +
    +
    +/**
    + * Handle the drag event. Move the containers.
    + * @param {!goog.fx.DragEvent} e The event.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.handleDrag_ = function(e) {
    +  if (this.continuousResize_) {
    +    if (this.isVertical()) {
    +      var top = this.getRelativeTop_(e.top);
    +      this.setFirstComponentSize(top);
    +    } else {
    +      var left = this.getRelativeLeft_(e.left);
    +      this.setFirstComponentSize(left);
    +    }
    +    this.dispatchEvent(goog.ui.SplitPane.EventType.HANDLE_DRAG);
    +  }
    +};
    +
    +
    +/**
    + * Handle the drag end event. If we're not doing continuous resize,
    + * resize the component.  If we're doing continuous resize, the component
    + * is already the correct size.
    + * @param {!goog.fx.DragEvent} e The event.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.handleDragEnd_ = function(e) {
    +  // Push iframe overlay down.
    +  this.iframeOverlay_.style.zIndex =
    +      goog.ui.SplitPane.IframeOverlayIndex_.HIDDEN;
    +  if (!this.continuousResize_) {
    +    if (this.isVertical()) {
    +      var top = this.getRelativeTop_(e.top);
    +      this.setFirstComponentSize(top);
    +    } else {
    +      var left = this.getRelativeLeft_(e.left);
    +      this.setFirstComponentSize(left);
    +    }
    +  }
    +
    +  this.dispatchEvent(goog.ui.SplitPane.EventType.HANDLE_DRAG_END);
    +};
    +
    +
    +/**
    + * Handle the Double-click. Call the snapIt method which snaps the container
    + * to the top or left.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.SplitPane.prototype.handleDoubleClick_ = function(e) {
    +  this.snapIt_();
    +};
    +
    +
    +/** @override */
    +goog.ui.SplitPane.prototype.disposeInternal = function() {
    +  goog.dispose(this.splitDragger_);
    +  this.splitDragger_ = null;
    +
    +  goog.dom.removeNode(this.iframeOverlay_);
    +  this.iframeOverlay_ = null;
    +
    +  goog.ui.SplitPane.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.html b/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.html
    new file mode 100644
    index 0000000000..ae2f249376
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.SplitPane
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script type="text/javascript">
    +   goog.require('goog.ui.SplitPaneTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.js b/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.js
    new file mode 100644
    index 0000000000..ab0851fb98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/splitpane_test.js
    @@ -0,0 +1,223 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SplitPaneTest');
    +goog.setTestOnly('goog.ui.SplitPaneTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.recordFunction');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.SplitPane');
    +
    +var splitpane;
    +var leftComponent;
    +var rightComponent;
    +
    +function setUp() {
    +  leftComponent = new goog.ui.Component();
    +  rightComponent = new goog.ui.Component();
    +  splitpane = new goog.ui.SplitPane(leftComponent, rightComponent,
    +      goog.ui.SplitPane.Orientation.HORIZONTAL);
    +}
    +
    +function tearDown() {
    +  splitpane.dispose();
    +  leftComponent.dispose();
    +  rightComponent.dispose();
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +function testRender() {
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +  assertEquals(1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane').length);
    +  assertEquals(1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-first-container').length);
    +  assertEquals(1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-second-container').length);
    +  assertEquals(1, goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-handle').length);
    +}
    +
    +function testDecorate() {
    +  var mainDiv = goog.dom.createDom('div', 'goog-splitpane',
    +      goog.dom.createDom('div', 'goog-splitpane-first-container'),
    +      goog.dom.createDom('div', 'goog-splitpane-second-container'),
    +      goog.dom.createDom('div', 'goog-splitpane-handle'));
    +  var sandbox = goog.dom.getElement('sandbox');
    +  goog.dom.appendChild(sandbox, mainDiv);
    +
    +  splitpane.decorate(mainDiv);
    +}
    +
    +function testDecorateWithNestedSplitPane() {
    +
    +  // Create a standard split pane to be nested within another split pane.
    +  var innerSplitPaneDiv = goog.dom.createDom('div', 'goog-splitpane',
    +      goog.dom.createDom('div', 'goog-splitpane-first-container e1'),
    +      goog.dom.createDom('div', 'goog-splitpane-second-container e2'),
    +      goog.dom.createDom('div', 'goog-splitpane-handle e3'));
    +
    +  // Create a split pane containing a split pane instance.
    +  var outerSplitPaneDiv = goog.dom.createDom('div', 'goog-splitpane',
    +      goog.dom.createDom('div', 'goog-splitpane-first-container e4',
    +          innerSplitPaneDiv),
    +      goog.dom.createDom('div', 'goog-splitpane-second-container e5'),
    +      goog.dom.createDom('div', 'goog-splitpane-handle e6'));
    +
    +  var sandbox = goog.dom.getElement('sandbox');
    +  goog.dom.appendChild(sandbox, outerSplitPaneDiv);
    +
    +  // Decorate and check that the correct containers and handle are used.
    +  splitpane.decorate(outerSplitPaneDiv);
    +  assertTrue(goog.dom.classlist.contains(
    +      splitpane.firstComponentContainer_, 'e4'));
    +  assertTrue(goog.dom.classlist.contains(
    +      splitpane.secondComponentContainer_, 'e5'));
    +  assertTrue(goog.dom.classlist.contains(splitpane.splitpaneHandle_, 'e6'));
    +}
    +
    +function testSetSize() {
    +  splitpane.setInitialSize(200);
    +  splitpane.setHandleSize(10);
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +
    +  splitpane.setSize(new goog.math.Size(500, 300));
    +  assertEquals(200, splitpane.getFirstComponentSize());
    +
    +  var splitpaneSize = goog.style.getBorderBoxSize(splitpane.getElement());
    +  assertEquals(500, splitpaneSize.width);
    +  assertEquals(300, splitpaneSize.height);
    +}
    +
    +function testOrientationChange() {
    +  splitpane.setInitialSize(200);
    +  splitpane.setHandleSize(10);
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +  splitpane.setSize(new goog.math.Size(500, 300));
    +
    +  var first = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-first-container')[0];
    +  var second = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-second-container')[0];
    +  var handle = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-handle')[0];
    +
    +  var handleSize = goog.style.getBorderBoxSize(handle);
    +  assertEquals(10, handleSize.width);
    +  assertEquals(300, handleSize.height);
    +
    +  var firstSize = goog.style.getBorderBoxSize(first);
    +  assertEquals(200, firstSize.width);
    +  assertEquals(300, firstSize.height);
    +
    +  var secondSize = goog.style.getBorderBoxSize(second);
    +  assertEquals(290, secondSize.width); // 500 - 200 - 10 = 290
    +  assertEquals(300, secondSize.height);
    +
    +  splitpane.setOrientation(goog.ui.SplitPane.Orientation.VERTICAL);
    +
    +  handleSize = goog.style.getBorderBoxSize(handle);
    +  assertEquals(10, handleSize.height);
    +  assertEquals(500, handleSize.width);
    +
    +  firstSize = goog.style.getBorderBoxSize(first);
    +  assertEquals(120, firstSize.height); // 200 * 300/500 = 120
    +  assertEquals(500, firstSize.width);
    +
    +  secondSize = goog.style.getBorderBoxSize(second);
    +  assertEquals(170, secondSize.height); // 300 - 120 - 10 = 170
    +  assertEquals(500, secondSize.width);
    +
    +  splitpane.setOrientation(goog.ui.SplitPane.Orientation.HORIZONTAL);
    +
    +  handleSize = goog.style.getBorderBoxSize(handle);
    +  assertEquals(10, handleSize.width);
    +  assertEquals(300, handleSize.height);
    +
    +  firstSize = goog.style.getBorderBoxSize(first);
    +  assertEquals(200, firstSize.width);
    +  assertEquals(300, firstSize.height);
    +
    +  secondSize = goog.style.getBorderBoxSize(second);
    +  assertEquals(290, secondSize.width);
    +  assertEquals(300, secondSize.height);
    +}
    +
    +function testDragEvent() {
    +  splitpane.setInitialSize(200);
    +  splitpane.setHandleSize(10);
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +
    +  var handler = goog.testing.recordFunction();
    +  goog.events.listen(splitpane, goog.ui.SplitPane.EventType.HANDLE_DRAG,
    +      handler);
    +  var handle = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-handle')[0];
    +
    +  goog.testing.events.fireMouseDownEvent(handle);
    +  goog.testing.events.fireMouseMoveEvent(handle);
    +  goog.testing.events.fireMouseUpEvent(handle);
    +  assertEquals('HANDLE_DRAG event expected', 1, handler.getCallCount());
    +
    +  splitpane.setContinuousResize(false);
    +  handler.reset();
    +  goog.testing.events.fireMouseDownEvent(handle);
    +  goog.testing.events.fireMouseMoveEvent(handle);
    +  goog.testing.events.fireMouseUpEvent(handle);
    +  assertEquals('HANDLE_DRAG event not expected', 0, handler.getCallCount());
    +}
    +
    +function testDragEndEvent() {
    +  splitpane.setInitialSize(200);
    +  splitpane.setHandleSize(10);
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +  var handler = goog.testing.recordFunction();
    +  goog.events.listen(splitpane, goog.ui.SplitPane.EventType.HANDLE_DRAG_END,
    +      handler);
    +
    +  var handle = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-handle')[0];
    +
    +  goog.testing.events.fireMouseDownEvent(handle);
    +  goog.testing.events.fireMouseMoveEvent(handle);
    +  goog.testing.events.fireMouseUpEvent(handle);
    +  assertEquals('HANDLE_DRAG_END event expected', 1, handler.getCallCount());
    +
    +  splitpane.setContinuousResize(false);
    +  handler.reset();
    +  goog.testing.events.fireMouseDownEvent(handle);
    +  goog.testing.events.fireMouseMoveEvent(handle);
    +  goog.testing.events.fireMouseUpEvent(handle);
    +  assertEquals('HANDLE_DRAG_END event expected', 1, handler.getCallCount());
    +}
    +
    +function testSnapEvent() {
    +  splitpane.setInitialSize(200);
    +  splitpane.setHandleSize(10);
    +  splitpane.render(goog.dom.getElement('sandbox'));
    +  var handler = goog.testing.recordFunction();
    +  goog.events.listen(splitpane, goog.ui.SplitPane.EventType.HANDLE_SNAP,
    +      handler);
    +  var handle = goog.dom.getElementsByTagNameAndClass('div',
    +      'goog-splitpane-handle')[0];
    +  goog.testing.events.fireDoubleClickSequence(handle);
    +  assertEquals('HANDLE_SNAP event expected', 1, handler.getCallCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer.js
    new file mode 100644
    index 0000000000..145fd578d2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer.js
    @@ -0,0 +1,202 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.Button}s in App style.
    + *
    + * Based on ImagelessButtonRender. Uses even more CSS voodoo than the default
    + * implementation to render custom buttons with fake rounded corners and
    + * dimensionality (via a subtle flat shadow on the bottom half of the button)
    + * without the use of images.
    + *
    + * Based on the Custom Buttons 3.1 visual specification, see
    + * http://go/custombuttons
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.style.app.ButtonRenderer');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.Button}s. Imageless buttons can contain
    + * almost arbitrary HTML content, will flow like inline elements, but can be
    + * styled like block-level elements.
    + *
    + * @constructor
    + * @extends {goog.ui.CustomButtonRenderer}
    + */
    +goog.ui.style.app.ButtonRenderer = function() {
    +  goog.ui.CustomButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.style.app.ButtonRenderer, goog.ui.CustomButtonRenderer);
    +goog.addSingletonGetter(goog.ui.style.app.ButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.style.app.ButtonRenderer.CSS_CLASS = goog.getCssName('goog-button');
    +
    +
    +/**
    + * Array of arrays of CSS classes that we want composite classes added and
    + * removed for in IE6 and lower as a workaround for lack of multi-class CSS
    + * selector support.
    + * @type {Array<Array<string>>}
    + */
    +goog.ui.style.app.ButtonRenderer.IE6_CLASS_COMBINATIONS = [];
    +
    +
    +/**
    + * Returns the button's contents wrapped in the following DOM structure:
    + *    <div class="goog-inline-block goog-button-base goog-button">
    + *      <div class="goog-inline-block goog-button-base-outer-box">
    + *        <div class="goog-button-base-inner-box">
    + *          <div class="goog-button-base-pos">
    + *            <div class="goog-button-base-top-shadow">&nbsp;</div>
    + *            <div class="goog-button-base-content">Contents...</div>
    + *          </div>
    + *        </div>
    + *      </div>
    + *    </div>
    + * @override
    + */
    +goog.ui.style.app.ButtonRenderer.prototype.createDom;
    +
    +
    +/** @override */
    +goog.ui.style.app.ButtonRenderer.prototype.getContentElement = function(
    +    element) {
    +  return element && /** @type {Element} */(
    +      element.firstChild.firstChild.firstChild.lastChild);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content
    + * wrapped in a pseudo-rounded-corner box.  Creates the following DOM structure:
    + *  <div class="goog-inline-block goog-button-base-outer-box">
    + *    <div class="goog-inline-block goog-button-base-inner-box">
    + *      <div class="goog-button-base-pos">
    + *        <div class="goog-button-base-top-shadow">&nbsp;</div>
    + *        <div class="goog-button-base-content">Contents...</div>
    + *      </div>
    + *    </div>
    + *  </div>
    + * Used by both {@link #createDom} and {@link #decorate}.  To be overridden
    + * by subclasses.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.style.app.ButtonRenderer.prototype.createButton = function(content,
    +    dom) {
    +  var baseClass = this.getStructuralCssClass();
    +  var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' ';
    +  return dom.createDom(
    +      'div', inlineBlock + goog.getCssName(baseClass, 'outer-box'),
    +      dom.createDom(
    +          'div', inlineBlock + goog.getCssName(baseClass, 'inner-box'),
    +          dom.createDom('div', goog.getCssName(baseClass, 'pos'),
    +              dom.createDom(
    +                  'div', goog.getCssName(baseClass, 'top-shadow'), '\u00A0'),
    +              dom.createDom(
    +                  'div', goog.getCssName(baseClass, 'content'), content))));
    +};
    +
    +
    +/**
    + * Check if the button's element has a box structure.
    + * @param {goog.ui.Button} button Button instance whose structure is being
    + *     checked.
    + * @param {Element} element Element of the button.
    + * @return {boolean} Whether the element has a box structure.
    + * @protected
    + * @override
    + */
    +goog.ui.style.app.ButtonRenderer.prototype.hasBoxStructure = function(
    +    button, element) {
    +
    +  var baseClass = this.getStructuralCssClass();
    +  var outer = button.getDomHelper().getFirstElementChild(element);
    +  var outerClassName = goog.getCssName(baseClass, 'outer-box');
    +  if (outer && goog.dom.classlist.contains(outer, outerClassName)) {
    +
    +    var inner = button.getDomHelper().getFirstElementChild(outer);
    +    var innerClassName = goog.getCssName(baseClass, 'inner-box');
    +    if (inner && goog.dom.classlist.contains(inner, innerClassName)) {
    +
    +      var pos = button.getDomHelper().getFirstElementChild(inner);
    +      var posClassName = goog.getCssName(baseClass, 'pos');
    +      if (pos && goog.dom.classlist.contains(pos, posClassName)) {
    +
    +        var shadow = button.getDomHelper().getFirstElementChild(pos);
    +        var shadowClassName = goog.getCssName(baseClass, 'top-shadow');
    +        if (shadow && goog.dom.classlist.contains(shadow, shadowClassName)) {
    +
    +          var content = button.getDomHelper().getNextElementSibling(shadow);
    +          var contentClassName = goog.getCssName(baseClass, 'content');
    +          if (content &&
    +              goog.dom.classlist.contains(content, contentClassName)) {
    +            // We have a proper box structure.
    +            return true;
    +          }
    +        }
    +      }
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.ButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.style.app.ButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.ButtonRenderer.prototype.getStructuralCssClass = function() {
    +  // TODO(user): extract to a constant.
    +  return goog.getCssName('goog-button-base');
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.ButtonRenderer.prototype.getIe6ClassCombinations =
    +    function() {
    +  return goog.ui.style.app.ButtonRenderer.IE6_CLASS_COMBINATIONS;
    +};
    +
    +
    +
    +// Register a decorator factory function for goog.ui.style.app.ButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.style.app.ButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Button(null,
    +          goog.ui.style.app.ButtonRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.html
    new file mode 100644
    index 0000000000..62305b0d31
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.html
    @@ -0,0 +1,43 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.style.app.ButtonRenderer
    +  </title>
    +  <script src="../../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.style.app.ButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox"></div>
    +
    +  <div id="button" title="Click for Decorated">
    +    Hello Decorated
    +  </div>
    +
    +  <!-- The component DOM must always be created without whitespace. -->
    +  <div id="button-box" title="Click for Decorated Box" class="goog-button goog-button-base"><div class="goog-inline-block goog-button-base-outer-box"><div class="goog-inline-block goog-button-base-inner-box"><div class="goog-button-base-pos"><div class="goog-button-base-top-shadow">&nbsp;</div><div class="goog-button-base-content">Hello Decorated Box</div></div></div></div></div>
    +
    +  <!-- The component DOM must always be created without whitespace. This
    +       demonstrates what happens when the content has whitespace.
    +   -->
    +  <div id="button-box-with-space-in-content" class="goog-button goog-button-base"><div class="goog-inline-block goog-button-base-outer-box"><div class="goog-inline-block goog-button-base-inner-box"><div class="goog-button-base-pos"><div class="goog-button-base-top-shadow">&nbsp;</div><div class="goog-button-base-content">
    +    Hello Decorated Box with space
    +  </div></div></div></div></div>
    +
    +  <div id="button-box-with-dom-content">
    +    <strong>Hello</strong> <em>Box</em>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.js
    new file mode 100644
    index 0000000000..6b481419b3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/buttonrenderer_test.js
    @@ -0,0 +1,130 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.style.app.ButtonRendererTest');
    +goog.setTestOnly('goog.ui.style.app.ButtonRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.style');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.style.app.ButtonRenderer');
    +goog.require('goog.userAgent');
    +var renderer = goog.ui.style.app.ButtonRenderer.getInstance();
    +var button;
    +
    +// Write iFrame tag to load reference FastUI markup. Then, our tests will
    +// compare the generated markup to the reference markup.
    +var refPath = '../../../../../webutil/css/fastui/app/button_spec.html';
    +goog.testing.ui.style.writeReferenceFrame(refPath);
    +
    +function setUp() {
    +  button = new goog.ui.Button('Hello Generated', renderer);
    +  button.setTooltip('Click for Generated');
    +}
    +
    +function tearDown() {
    +  if (button) {
    +    button.dispose();
    +  }
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +function testGeneratedButton() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Generated', button.getContentElement().innerHTML);
    +  assertEquals('Click for Generated',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +function testButtonStates() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  button.setState(goog.ui.Component.State.HOVER, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-hover');
    +  button.setState(goog.ui.Component.State.HOVER, false);
    +  button.setState(goog.ui.Component.State.FOCUSED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-focused');
    +  button.setState(goog.ui.Component.State.FOCUSED, false);
    +  button.setState(goog.ui.Component.State.ACTIVE, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-active');
    +  button.setState(goog.ui.Component.State.ACTIVE, false);
    +  button.setState(goog.ui.Component.State.DISABLED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-disabled');
    +}
    +
    +function testDecoratedButton() {
    +  button.decorate(goog.dom.getElement('button'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Decorated', button.getContentElement().innerHTML);
    +  assertEquals('Click for Decorated',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +function testDecoratedButtonBox() {
    +  button.decorate(goog.dom.getElement('button-box'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Decorated Box',
    +      button.getContentElement().innerHTML);
    +  assertEquals('Click for Decorated Box',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +/*
    + * This test demonstrates what happens when you put whitespace in a
    + * decorated button's content, and the decorated element 'hasBoxStructure'.
    + * Note that this behavior is different than when the element does not
    + * have box structure. Should this be fixed?
    + */
    +function testDecoratedButtonBoxWithSpaceInContent() {
    +  button.decorate(goog.dom.getElement('button-box-with-space-in-content'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
    +    assertEquals('Hello Decorated Box with space ',
    +        button.getContentElement().innerHTML);
    +  } else {
    +    assertEquals('\n    Hello Decorated Box with space\n  ',
    +        button.getContentElement().innerHTML);
    +  }
    +}
    +
    +function testExistingContentIsUsed() {
    +  button.decorate(goog.dom.getElement('button-box-with-dom-content'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  // Safari 3 adds style="-webkit-user-select" to the strong tag, so we
    +  // can't simply look at the HTML.
    +  var content = button.getContentElement();
    +  assertEquals('Unexpected number of child nodes', 3,
    +      content.childNodes.length);
    +  assertEquals('Unexpected tag', 'STRONG',
    +      content.childNodes[0].tagName);
    +  assertEquals('Unexpected text content', 'Hello',
    +      content.childNodes[0].innerHTML);
    +  assertEquals('Unexpected tag', 'EM',
    +      content.childNodes[2].tagName);
    +  assertEquals('Unexpected text content', 'Box',
    +      content.childNodes[2].innerHTML);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer.js
    new file mode 100644
    index 0000000000..c28febfaa7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer.js
    @@ -0,0 +1,233 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.style.app.MenuButton}s and
    + * subclasses.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.style.app.MenuButtonRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuRenderer');
    +goog.require('goog.ui.style.app.ButtonRenderer');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.style.app.MenuButton}s.  This implementation
    + * overrides {@link goog.ui.style.app.ButtonRenderer#createButton} to insert a
    + * dropdown element into the content element after the specified content.
    + * @constructor
    + * @extends {goog.ui.style.app.ButtonRenderer}
    + * @final
    + */
    +goog.ui.style.app.MenuButtonRenderer = function() {
    +  goog.ui.style.app.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.style.app.MenuButtonRenderer,
    +    goog.ui.style.app.ButtonRenderer);
    +goog.addSingletonGetter(goog.ui.style.app.MenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.style.app.MenuButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-menu-button');
    +
    +
    +/**
    + * Array of arrays of CSS classes that we want composite classes added and
    + * removed for in IE6 and lower as a workaround for lack of multi-class CSS
    + * selector support.
    + * @type {Array<Array<string>>}
    + */
    +goog.ui.style.app.MenuButtonRenderer.IE6_CLASS_COMBINATIONS = [
    +  [goog.getCssName('goog-button-base-rtl'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-hover'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-focused'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-disabled'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-active'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-open'),
    +   goog.getCssName('goog-menu-button')],
    +
    +  [goog.getCssName('goog-button-base-active'),
    +   goog.getCssName('goog-button-base-open'),
    +   goog.getCssName('goog-menu-button')]
    +];
    +
    +
    +/**
    + * Returns the ARIA role to be applied to menu buttons, which
    + * have a menu attached to them.
    + * @return {goog.a11y.aria.Role} ARIA role.
    + * @override
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.getAriaRole = function() {
    +  // If we apply the 'button' ARIA role to the menu button, the
    +  // screen reader keeps referring to menus as buttons, which
    +  // might be misleading for the users. Hence the ARIA role
    +  // 'menu' is assigned.
    +  return goog.a11y.aria.Role.MENU;
    +};
    +
    +
    +/**
    + * Takes the button's root element and returns the parent element of the
    + * button's contents.  Overrides the superclass implementation by taking
    + * the nested DIV structure of menu buttons into account.
    + * @param {Element} element Root element of the button whose content element
    + *     is to be returned.
    + * @return {Element} The button's content element.
    + * @override
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.getContentElement =
    +    function(element) {
    +  return goog.ui.style.app.MenuButtonRenderer.superClass_.getContentElement
    +      .call(this, element);
    +};
    +
    +
    +/**
    + * Takes an element, decorates it with the menu button control, and returns
    + * the element.  Overrides {@link goog.ui.style.app.ButtonRenderer#decorate} by
    + * looking for a child element that can be decorated by a menu, and if it
    + * finds one, decorates it and attaches it to the menu button.
    + * @param {goog.ui.Control} control goog.ui.MenuButton to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.decorate =
    +    function(control, element) {
    +  var button = /** @type {goog.ui.MenuButton} */ (control);
    +  // TODO(attila):  Add more robust support for subclasses of goog.ui.Menu.
    +  var menuElem = goog.dom.getElementsByTagNameAndClass(
    +      '*', goog.ui.MenuRenderer.CSS_CLASS, element)[0];
    +  if (menuElem) {
    +    // Move the menu element directly under the body (but hide it first to
    +    // prevent flicker; see bug 1089244).
    +    goog.style.setElementShown(menuElem, false);
    +    goog.dom.appendChild(goog.dom.getOwnerDocument(menuElem).body, menuElem);
    +
    +    // Decorate the menu and attach it to the button.
    +    var menu = new goog.ui.Menu();
    +    menu.decorate(menuElem);
    +    button.setMenu(menu);
    +  }
    +
    +  // Let the superclass do the rest.
    +  return goog.ui.style.app.MenuButtonRenderer.superClass_.decorate.call(this,
    +      button, element);
    +};
    +
    +
    +/**
    + * Takes a text caption or existing DOM structure, and returns the content and
    + * a dropdown arrow element wrapped in a pseudo-rounded-corner box.  Creates
    + * the following DOM structure:
    + *  <div class="goog-inline-block goog-button-outer-box">
    + *    <div class="goog-inline-block goog-button-inner-box">
    + *      <div class="goog-button-pos">
    + *        <div class="goog-button-top-shadow">&nbsp;</div>
    + *        <div class="goog-button-content">
    + *          Contents...
    + *          <div class="goog-menu-button-dropdown"> </div>
    + *        </div>
    + *      </div>
    + *    </div>
    + *  </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
    + *     in a box.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Pseudo-rounded-corner box containing the content.
    + * @override
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.createButton = function(content,
    +    dom) {
    +  var contentWithDropdown = this.createContentWithDropdown(content, dom);
    +  return goog.ui.style.app.MenuButtonRenderer.superClass_.createButton.call(
    +      this, contentWithDropdown, dom);
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.MenuButtonRenderer.prototype.setContent = function(element,
    +    content) {
    +  var dom = goog.dom.getDomHelper(this.getContentElement(element));
    +  goog.ui.style.app.MenuButtonRenderer.superClass_.setContent.call(
    +      this, element, this.createContentWithDropdown(content, dom));
    +};
    +
    +
    +/**
    + * Inserts dropdown element as last child of existing content.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document ineraction.
    + * @return {Array<Node>} DOM structure to be set as the button's content.
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.createContentWithDropdown =
    +    function(content, dom) {
    +  var caption = dom.createDom('div', null, content, this.createDropdown(dom));
    +  return goog.array.toArray(caption.childNodes);
    +};
    +
    +
    +/**
    + * Returns an appropriately-styled DIV containing a dropdown arrow.
    + * Creates the following DOM structure:
    + *    <div class="goog-menu-button-dropdown"> </div>
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {Element} Dropdown element.
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.createDropdown = function(dom) {
    +  return dom.createDom('div', goog.getCssName(this.getCssClass(), 'dropdown'));
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.style.app.MenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.style.app.MenuButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.MenuButtonRenderer.prototype.getIe6ClassCombinations =
    +    function() {
    +  return goog.ui.style.app.MenuButtonRenderer.IE6_CLASS_COMBINATIONS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.html
    new file mode 100644
    index 0000000000..3f77a8ffc1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.html
    @@ -0,0 +1,48 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.style.app.MenuButtonRenderer
    +  </title>
    +  <script src="../../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.style.app.MenuButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox"></div>
    +
    +  <div id="button" title="Click for Decorated">
    +    Hello Decorated
    +  </div>
    +
    +  <div id="button-2" title="Click for Decorated">
    +    Hello Decorated
    +  </div>
    +
    +  <!-- The component DOM must always be created without whitespace. -->
    +  <div id="button-box" title="Click for Decorated Box" class="goog-menu-button goog-button-base"><div class="goog-inline-block goog-button-base-outer-box"><div class="goog-inline-block goog-button-base-inner-box"><div class="goog-button-base-pos"><div class="goog-button-base-top-shadow">&nbsp;</div><div class="goog-button-base-content">Hello Decorated Box<div class="goog-menu-button-dropdown"> </div></div></div></div></div></div>
    +
    +  <div id="button-with-dom-content" class="goog-menu-button">
    +    <strong>Hello Strong</strong> <em>Box</em>
    +  </div>
    +
    +  <div id="button-with-menu" class="goog-menu-button">
    +    Button with Menu
    +    <div class="goog-menu">
    +      <div class="goog-menuitem">Item 1</div>
    +      <div class="goog-menuitem">Item 2</div>
    +    </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.js
    new file mode 100644
    index 0000000000..f2c9194f6f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/menubuttonrenderer_test.js
    @@ -0,0 +1,136 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.style.app.MenuButtonRendererTest');
    +goog.setTestOnly('goog.ui.style.app.MenuButtonRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.style.app.MenuButtonRenderer');
    +var renderer = goog.ui.style.app.MenuButtonRenderer.getInstance();
    +var button;
    +
    +// Write iFrame tag to load reference FastUI markup. Then, our tests will
    +// compare the generated markup to the reference markup.
    +var refPath = '../../../../../webutil/css/fastui/app/menubutton_spec.html';
    +goog.testing.ui.style.writeReferenceFrame(refPath);
    +
    +function setUp() {
    +  button = new goog.ui.MenuButton('Hello Generated', null, renderer);
    +  button.setTooltip('Click for Generated');
    +}
    +
    +function tearDown() {
    +  if (button) {
    +    button.dispose();
    +  }
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +function testGeneratedButton() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Generated',
    +      button.getContentElement().firstChild.nodeValue);
    +  assertEquals('Click for Generated',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +function testButtonStates() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  button.setState(goog.ui.Component.State.HOVER, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-hover');
    +  button.setState(goog.ui.Component.State.HOVER, false);
    +  button.setState(goog.ui.Component.State.FOCUSED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-focused');
    +  button.setState(goog.ui.Component.State.FOCUSED, false);
    +  button.setState(goog.ui.Component.State.ACTIVE, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-active');
    +  button.setState(goog.ui.Component.State.ACTIVE, false);
    +  button.setState(goog.ui.Component.State.DISABLED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-disabled');
    +}
    +
    +function testDecoratedButton() {
    +  button.decorate(goog.dom.getElement('button'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Decorated',
    +      button.getContentElement().firstChild.nodeValue);
    +  assertEquals('Click for Decorated',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +function testDecoratedButtonBox() {
    +  button.decorate(goog.dom.getElement('button-box'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Hello Decorated Box',
    +      button.getContentElement().firstChild.nodeValue);
    +  assertEquals('Click for Decorated Box',
    +      button.getElement().getAttribute('title'));
    +}
    +
    +function testExistingContentIsUsed() {
    +  button.decorate(goog.dom.getElement('button-with-dom-content'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  // Safari 3 adds style="-webkit-user-select" to the strong tag, so we
    +  // can't simply look at the HTML.
    +  var content = button.getContentElement();
    +  assertEquals('Unexpected number of child nodes; expected existing number ' +
    +      'plus one for the dropdown element', 4, content.childNodes.length);
    +  assertEquals('Unexpected tag', 'STRONG',
    +      content.childNodes[0].tagName);
    +  assertEquals('Unexpected text content', 'Hello Strong',
    +      content.childNodes[0].innerHTML);
    +  assertEquals('Unexpected tag', 'EM',
    +      content.childNodes[2].tagName);
    +  assertEquals('Unexpected text content', 'Box',
    +      content.childNodes[2].innerHTML);
    +}
    +
    +function testDecoratedButtonWithMenu() {
    +  button.decorate(goog.dom.getElement('button-with-menu'));
    +  assertEquals('Unexpected number of menu items', 2, button.getItemCount());
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertFalse('Expected menu element to not be contained by button',
    +      goog.dom.contains(button.getElement(),
    +          button.getMenu().getElement()));
    +}
    +
    +function testDropDownExistsAfterButtonRename() {
    +  button.decorate(goog.dom.getElement('button-2'));
    +  button.setContent('New title');
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  assertEquals('Unexpected number of child nodes; expected text element ' +
    +      'and the dropdown element',
    +      2, button.getContentElement().childNodes.length);
    +  assertEquals('New title',
    +      button.getContentElement().firstChild.nodeValue);
    +  assertEquals('Click for Decorated',
    +      button.getElement().getAttribute('title'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer.js
    new file mode 100644
    index 0000000000..e2fc1cf6a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer.js
    @@ -0,0 +1,89 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.Button}s in App style. This
    + * type of button is typically used for an application's "primary action," eg
    + * in Gmail, it's "Compose," in Calendar, it's "Create Event".
    + *
    + */
    +
    +goog.provide('goog.ui.style.app.PrimaryActionButtonRenderer');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.registry');
    +goog.require('goog.ui.style.app.ButtonRenderer');
    +
    +
    +
    +/**
    + * Custom renderer for {@link goog.ui.Button}s. This renderer supports the
    + * "primary action" style for buttons.
    + *
    + * @constructor
    + * @extends {goog.ui.style.app.ButtonRenderer}
    + * @final
    + */
    +goog.ui.style.app.PrimaryActionButtonRenderer = function() {
    +  goog.ui.style.app.ButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.style.app.PrimaryActionButtonRenderer,
    +    goog.ui.style.app.ButtonRenderer);
    +goog.addSingletonGetter(goog.ui.style.app.PrimaryActionButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.style.app.PrimaryActionButtonRenderer.CSS_CLASS =
    +    'goog-primaryactionbutton';
    +
    +
    +/**
    + * Array of arrays of CSS classes that we want composite classes added and
    + * removed for in IE6 and lower as a workaround for lack of multi-class CSS
    + * selector support.
    + * @type {Array<Array<string>>}
    + */
    +goog.ui.style.app.PrimaryActionButtonRenderer.IE6_CLASS_COMBINATIONS = [
    +  ['goog-button-base-disabled', 'goog-primaryactionbutton'],
    +  ['goog-button-base-focused', 'goog-primaryactionbutton'],
    +  ['goog-button-base-hover', 'goog-primaryactionbutton']
    +];
    +
    +
    +/** @override */
    +goog.ui.style.app.PrimaryActionButtonRenderer.prototype.getCssClass =
    +    function() {
    +  return goog.ui.style.app.PrimaryActionButtonRenderer.CSS_CLASS;
    +};
    +
    +
    +/** @override */
    +goog.ui.style.app.PrimaryActionButtonRenderer.prototype.
    +    getIe6ClassCombinations = function() {
    +  return goog.ui.style.app.PrimaryActionButtonRenderer.IE6_CLASS_COMBINATIONS;
    +};
    +
    +
    +// Register a decorator factory function for
    +// goog.ui.style.app.PrimaryActionButtonRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.style.app.PrimaryActionButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Button(null,
    +          goog.ui.style.app.PrimaryActionButtonRenderer.getInstance());
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.html
    new file mode 100644
    index 0000000000..320658d5af
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests -
    +      goog.ui.style.app.PrimaryActionButtonRenderer
    +  </title>
    +  <script src="../../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.style.app.PrimaryActionButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.js
    new file mode 100644
    index 0000000000..b66ec0dbc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/style/app/primaryactionbuttonrenderer_test.js
    @@ -0,0 +1,69 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.style.app.PrimaryActionButtonRendererTest');
    +goog.setTestOnly('goog.ui.style.app.PrimaryActionButtonRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.style');
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.style.app.PrimaryActionButtonRenderer');
    +var renderer = goog.ui.style.app.PrimaryActionButtonRenderer.getInstance();
    +var button;
    +
    +// Write iFrame tag to load reference FastUI markup. Then, our tests will
    +// compare the generated markup to the reference markup.
    +var refPath = '../../../../../' +
    +    'webutil/css/fastui/app/primaryactionbutton_spec.html';
    +goog.testing.ui.style.writeReferenceFrame(refPath);
    +
    +function setUp() {
    +  button = new goog.ui.Button('Hello Generated', renderer);
    +}
    +
    +function tearDown() {
    +  if (button) {
    +    button.dispose();
    +  }
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +}
    +
    +function testGeneratedButton() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +}
    +
    +function testButtonStates() {
    +  button.render(goog.dom.getElement('sandbox'));
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-resting');
    +  button.setState(goog.ui.Component.State.HOVER, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-hover');
    +  button.setState(goog.ui.Component.State.HOVER, false);
    +  button.setState(goog.ui.Component.State.FOCUSED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-focused');
    +  button.setState(goog.ui.Component.State.FOCUSED, false);
    +  button.setState(goog.ui.Component.State.ACTIVE, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-active');
    +  button.setState(goog.ui.Component.State.ACTIVE, false);
    +  button.setState(goog.ui.Component.State.DISABLED, true);
    +  goog.testing.ui.style.assertStructureMatchesReference(button.getElement(),
    +      'normal-disabled');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/submenu.js b/mapbox-gl/closure-library/closure/goog/ui/submenu.js
    new file mode 100644
    index 0000000000..28869d3d3c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/submenu.js
    @@ -0,0 +1,671 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A class representing menu items that open a submenu.
    + * @see goog.ui.Menu
    + *
    + * @see ../demos/submenus.html
    + * @see ../demos/submenus2.html
    + */
    +
    +goog.provide('goog.ui.SubMenu');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.positioning.AnchoredViewportPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.SubMenuRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a submenu that can be added as an item to other menus.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the content of the submenu (use to add icons or styling to
    + *     menus).
    + * @param {*=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper used for dom
    + *     interactions.
    + * @param {goog.ui.MenuItemRenderer=} opt_renderer Renderer used to render or
    + *     decorate the component; defaults to {@link goog.ui.SubMenuRenderer}.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + */
    +goog.ui.SubMenu = function(content, opt_model, opt_domHelper, opt_renderer) {
    +  goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper,
    +                        opt_renderer || goog.ui.SubMenuRenderer.getInstance());
    +};
    +goog.inherits(goog.ui.SubMenu, goog.ui.MenuItem);
    +goog.tagUnsealableClass(goog.ui.SubMenu);
    +
    +
    +/**
    + * The delay before opening the sub menu in milliseconds.
    + * @type {number}
    + */
    +goog.ui.SubMenu.MENU_DELAY_MS = 218;
    +
    +
    +/**
    + * Timer used to dismiss the submenu when the item becomes unhighlighted.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.dismissTimer_ = null;
    +
    +
    +/**
    + * Timer used to show the submenu on mouseover.
    + * @type {?number}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.showTimer_ = null;
    +
    +
    +/**
    + * Whether the submenu believes the menu is visible.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.menuIsVisible_ = false;
    +
    +
    +/**
    + * The lazily created sub menu.
    + * @type {goog.ui.Menu?}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.subMenu_ = null;
    +
    +
    +/**
    + * Whether or not the sub-menu was set explicitly.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.externalSubMenu_ = false;
    +
    +
    +/**
    + * Whether or not to align the submenu at the end of the parent menu.
    + * If true, the menu expands to the right in LTR languages and to the left
    + * in RTL langauges.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.alignToEnd_ = true;
    +
    +
    +/**
    + * Whether the position of this submenu may be adjusted to fit
    + * the visible area, as in {@link goog.ui.Popup.positionAtCoordinate}.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.SubMenu.prototype.isPositionAdjustable_ = false;
    +
    +
    +/** @override */
    +goog.ui.SubMenu.prototype.enterDocument = function() {
    +  goog.ui.SubMenu.superClass_.enterDocument.call(this);
    +
    +  this.getHandler().listen(this.getParent(), goog.ui.Component.EventType.HIDE,
    +      this.onParentHidden_);
    +
    +  if (this.subMenu_) {
    +    this.setMenuListenersEnabled_(this.subMenu_, true);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.SubMenu.prototype.exitDocument = function() {
    +  this.getHandler().unlisten(this.getParent(), goog.ui.Component.EventType.HIDE,
    +      this.onParentHidden_);
    +
    +  if (this.subMenu_) {
    +    this.setMenuListenersEnabled_(this.subMenu_, false);
    +    if (!this.externalSubMenu_) {
    +      this.subMenu_.exitDocument();
    +      goog.dom.removeNode(this.subMenu_.getElement());
    +    }
    +  }
    +
    +  goog.ui.SubMenu.superClass_.exitDocument.call(this);
    +};
    +
    +
    +/** @override */
    +goog.ui.SubMenu.prototype.disposeInternal = function() {
    +  if (this.subMenu_ && !this.externalSubMenu_) {
    +    this.subMenu_.dispose();
    +  }
    +  this.subMenu_ = null;
    +  goog.ui.SubMenu.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +/**
    + * @override
    + * Dismisses the submenu on a delay, with the result that the user needs less
    + * accuracy when moving to submenus.  Alternate implementations could use
    + * geometry instead of a timer.
    + * @param {boolean} highlight Whether item should be highlighted.
    + * @param {boolean=} opt_btnPressed Whether the mouse button is held down.
    + */
    +goog.ui.SubMenu.prototype.setHighlighted = function(highlight,
    +                                                    opt_btnPressed) {
    +  goog.ui.SubMenu.superClass_.setHighlighted.call(this, highlight);
    +
    +  if (opt_btnPressed) {
    +    this.getMenu().setMouseButtonPressed(true);
    +  }
    +
    +  if (!highlight) {
    +    if (this.dismissTimer_) {
    +      goog.Timer.clear(this.dismissTimer_);
    +    }
    +    this.dismissTimer_ = goog.Timer.callOnce(
    +        this.dismissSubMenu, goog.ui.SubMenu.MENU_DELAY_MS, this);
    +  }
    +};
    +
    +
    +/**
    + * Show the submenu and ensure that all siblings are hidden.
    + */
    +goog.ui.SubMenu.prototype.showSubMenu = function() {
    +  // Only show the menu if this item is still selected. This is called on a
    +  // timeout, so make sure our parent still exists.
    +  var parent = this.getParent();
    +  if (parent && parent.getHighlighted() == this) {
    +    this.setSubMenuVisible_(true);
    +    this.dismissSiblings_();
    +  }
    +};
    +
    +
    +/**
    + * Dismisses the menu and all further submenus.
    + */
    +goog.ui.SubMenu.prototype.dismissSubMenu = function() {
    +  // Because setHighlighted calls this function on a timeout, we need to make
    +  // sure that the sub menu hasn't been disposed when we come back.
    +  var subMenu = this.subMenu_;
    +  if (subMenu && subMenu.getParent() == this) {
    +    this.setSubMenuVisible_(false);
    +    subMenu.forEachChild(function(child) {
    +      if (typeof child.dismissSubMenu == 'function') {
    +        child.dismissSubMenu();
    +      }
    +    });
    +  }
    +};
    +
    +
    +/**
    + * Clears the show and hide timers for the sub menu.
    + */
    +goog.ui.SubMenu.prototype.clearTimers = function() {
    +  if (this.dismissTimer_) {
    +    goog.Timer.clear(this.dismissTimer_);
    +  }
    +  if (this.showTimer_) {
    +    goog.Timer.clear(this.showTimer_);
    +  }
    +};
    +
    +
    +/**
    + * Sets the menu item to be visible or invisible.
    + * @param {boolean} visible Whether to show or hide the component.
    + * @param {boolean=} opt_force If true, doesn't check whether the component
    + *     already has the requested visibility, and doesn't dispatch any events.
    + * @return {boolean} Whether the visibility was changed.
    + * @override
    + */
    +goog.ui.SubMenu.prototype.setVisible = function(visible, opt_force) {
    +  var visibilityChanged = goog.ui.SubMenu.superClass_.setVisible.call(this,
    +      visible, opt_force);
    +  // For menus that allow menu items to be hidden (i.e. ComboBox) ensure that
    +  // the submenu is hidden.
    +  if (visibilityChanged && !this.isVisible()) {
    +    this.dismissSubMenu();
    +  }
    +  return visibilityChanged;
    +};
    +
    +
    +/**
    + * Dismiss all the sub menus of sibling menu items.
    + * @private
    + */
    +goog.ui.SubMenu.prototype.dismissSiblings_ = function() {
    +  this.getParent().forEachChild(function(child) {
    +    if (child != this && typeof child.dismissSubMenu == 'function') {
    +      child.dismissSubMenu();
    +      child.clearTimers();
    +    }
    +  }, this);
    +};
    +
    +
    +/**
    + * Handles a key event that is passed to the menu item from its parent because
    + * it is highlighted.  If the right key is pressed the sub menu takes control
    + * and delegates further key events to its menu until it is dismissed OR the
    + * left key is pressed.
    + * @param {goog.events.KeyEvent} e A key event.
    + * @return {boolean} Whether the event was handled.
    + * @override
    + */
    +goog.ui.SubMenu.prototype.handleKeyEvent = function(e) {
    +  var keyCode = e.keyCode;
    +  var openKeyCode = this.isRightToLeft() ? goog.events.KeyCodes.LEFT :
    +      goog.events.KeyCodes.RIGHT;
    +  var closeKeyCode = this.isRightToLeft() ? goog.events.KeyCodes.RIGHT :
    +      goog.events.KeyCodes.LEFT;
    +
    +  if (!this.menuIsVisible_) {
    +    // Menu item doesn't have keyboard control and the right key was pressed.
    +    // So open take keyboard control and open the sub menu.
    +    if (this.isEnabled() &&
    +        (keyCode == openKeyCode || keyCode == this.getMnemonic())) {
    +      this.showSubMenu();
    +      this.getMenu().highlightFirst();
    +      this.clearTimers();
    +
    +    // The menu item doesn't currently care about the key events so let the
    +    // parent menu handle them accordingly .
    +    } else {
    +      return false;
    +    }
    +
    +  // Menu item has control, so let its menu try to handle the keys (this may
    +  // in turn be handled by sub-sub menus).
    +  } else if (this.getMenu().handleKeyEvent(e)) {
    +    // Nothing to do
    +
    +  // The menu has control and the key hasn't yet been handled, on left arrow
    +  // we turn off key control.
    +  } else if (keyCode == closeKeyCode) {
    +    this.dismissSubMenu();
    +
    +  } else {
    +    // Submenu didn't handle the key so let the parent decide what to do.
    +    return false;
    +  }
    +
    +  e.preventDefault();
    +  return true;
    +};
    +
    +
    +/**
    + * Listens to the sub menus items and ensures that this menu item is selected
    + * while dismissing the others.  This handles the case when the user mouses
    + * over other items on their way to the sub menu.
    + * @param {goog.events.Event} e Enter event to handle.
    + * @private
    + */
    +goog.ui.SubMenu.prototype.onChildEnter_ = function(e) {
    +  if (this.subMenu_.getParent() == this) {
    +    this.clearTimers();
    +    this.getParentEventTarget().setHighlighted(this);
    +    this.dismissSiblings_();
    +  }
    +};
    +
    +
    +/**
    + * Listens to the parent menu's hide event and ensures that all submenus are
    + * hidden at the same time.
    + * @param {goog.events.Event} e The event.
    + * @private
    + */
    +goog.ui.SubMenu.prototype.onParentHidden_ = function(e) {
    +  // Ignore propagated events
    +  if (e.target == this.getParentEventTarget()) {
    +    // TODO(user): Using an event for this is expensive.  Consider having a
    +    // generalized interface that the parent menu calls on its children when
    +    // it is hidden.
    +    this.dismissSubMenu();
    +    this.clearTimers();
    +  }
    +};
    +
    +
    +/**
    + * @override
    + * Sets a timer to show the submenu and then dispatches an ENTER event to the
    + * parent menu.
    + * @param {goog.events.BrowserEvent} e Mouse event to handle.
    + * @protected
    + */
    +goog.ui.SubMenu.prototype.handleMouseOver = function(e) {
    +  if (this.isEnabled()) {
    +    this.clearTimers();
    +    this.showTimer_ = goog.Timer.callOnce(
    +        this.showSubMenu, goog.ui.SubMenu.MENU_DELAY_MS, this);
    +  }
    +  goog.ui.SubMenu.superClass_.handleMouseOver.call(this, e);
    +};
    +
    +
    +/**
    + * Overrides the default mouseup event handler, so that the ACTION isn't
    + * dispatched for the submenu itself, instead the submenu is shown instantly.
    + * @param {goog.events.Event} e The browser event.
    + * @return {boolean} True if the action was allowed to proceed, false otherwise.
    + * @override
    + */
    +goog.ui.SubMenu.prototype.performActionInternal = function(e) {
    +  this.clearTimers();
    +  var shouldHandleClick = this.isSupportedState(
    +      goog.ui.Component.State.SELECTED);
    +  if (shouldHandleClick) {
    +    return goog.ui.SubMenu.superClass_.performActionInternal.call(this, e);
    +  } else {
    +    this.showSubMenu();
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * Sets the visiblility of the sub menu.
    + * @param {boolean} visible Whether to show menu.
    + * @private
    + */
    +goog.ui.SubMenu.prototype.setSubMenuVisible_ = function(visible) {
    +  // Dispatch OPEN event before calling getMenu(), so we can create the menu
    +  // lazily on first access.
    +  this.dispatchEvent(goog.ui.Component.getStateTransitionEvent(
    +      goog.ui.Component.State.OPENED, visible));
    +  var subMenu = this.getMenu();
    +  if (visible != this.menuIsVisible_) {
    +    goog.dom.classlist.enable(
    +        goog.asserts.assert(this.getElement()),
    +        goog.getCssName('goog-submenu-open'), visible);
    +  }
    +  if (visible != subMenu.isVisible()) {
    +    if (visible) {
    +      // Lazy-render menu when first shown, if needed.
    +      if (!subMenu.isInDocument()) {
    +        subMenu.render();
    +      }
    +      subMenu.setHighlightedIndex(-1);
    +    }
    +    subMenu.setVisible(visible);
    +    // We must position after the menu is visible, otherwise positioning logic
    +    // breaks in RTL.
    +    if (visible) {
    +      this.positionSubMenu();
    +    }
    +  }
    +  this.menuIsVisible_ = visible;
    +};
    +
    +
    +/**
    + * Attaches or detaches menu event listeners to/from the given menu.  Called
    + * each time a menu is attached to or detached from the submenu.
    + * @param {goog.ui.Menu} menu Menu on which to listen for events.
    + * @param {boolean} attach Whether to attach or detach event listeners.
    + * @private
    + */
    +goog.ui.SubMenu.prototype.setMenuListenersEnabled_ = function(menu, attach) {
    +  var handler = this.getHandler();
    +  var method = attach ? handler.listen : handler.unlisten;
    +  method.call(handler, menu, goog.ui.Component.EventType.ENTER,
    +      this.onChildEnter_);
    +};
    +
    +
    +/**
    + * Sets whether the submenu is aligned at the end of the parent menu.
    + * @param {boolean} alignToEnd True to align to end, false to align to start.
    + */
    +goog.ui.SubMenu.prototype.setAlignToEnd = function(alignToEnd) {
    +  if (alignToEnd != this.alignToEnd_) {
    +    this.alignToEnd_ = alignToEnd;
    +    if (this.isInDocument()) {
    +      // Completely re-render the widget.
    +      var oldElement = this.getElement();
    +      this.exitDocument();
    +
    +      if (oldElement.nextSibling) {
    +        this.renderBefore(/** @type {!Element} */ (oldElement.nextSibling));
    +      } else {
    +        this.render(/** @type {Element} */ (oldElement.parentNode));
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Determines whether the submenu is aligned at the end of the parent menu.
    + * @return {boolean} True if aligned to the end (the default), false if
    + *     aligned to the start.
    + */
    +goog.ui.SubMenu.prototype.isAlignedToEnd = function() {
    +  return this.alignToEnd_;
    +};
    +
    +
    +/**
    + * Positions the submenu. This method should be called if the sub menu is
    + * opened and the menu element's size changes (e.g., when adding/removing items
    + * to an opened sub menu).
    + */
    +goog.ui.SubMenu.prototype.positionSubMenu = function() {
    +  var position = new goog.positioning.AnchoredViewportPosition(
    +      this.getElement(), this.isAlignedToEnd() ?
    +      goog.positioning.Corner.TOP_END : goog.positioning.Corner.TOP_START,
    +      this.isPositionAdjustable_);
    +
    +  // TODO(user): Clean up popup code and have this be a one line call
    +  var subMenu = this.getMenu();
    +  var el = subMenu.getElement();
    +  if (!subMenu.isVisible()) {
    +    el.style.visibility = 'hidden';
    +    goog.style.setElementShown(el, true);
    +  }
    +
    +  position.reposition(
    +      el, this.isAlignedToEnd() ?
    +      goog.positioning.Corner.TOP_START : goog.positioning.Corner.TOP_END);
    +
    +  if (!subMenu.isVisible()) {
    +    goog.style.setElementShown(el, false);
    +    el.style.visibility = 'visible';
    +  }
    +};
    +
    +
    +// Methods delegated to sub-menu but accessible here for convinience
    +
    +
    +/**
    + * Adds a new menu item at the end of the menu.
    + * @param {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu
    + *     item to add to the menu.
    + */
    +goog.ui.SubMenu.prototype.addItem = function(item) {
    +  this.getMenu().addChild(item, true);
    +};
    +
    +
    +/**
    + * Adds a new menu item at a specific index in the menu.
    + * @param {goog.ui.MenuHeader|goog.ui.MenuItem|goog.ui.MenuSeparator} item Menu
    + *     item to add to the menu.
    + * @param {number} n Index at which to insert the menu item.
    + */
    +goog.ui.SubMenu.prototype.addItemAt = function(item, n) {
    +  this.getMenu().addChildAt(item, n, true);
    +};
    +
    +
    +/**
    + * Removes an item from the menu and disposes it.
    + * @param {goog.ui.MenuItem} item The menu item to remove.
    + */
    +goog.ui.SubMenu.prototype.removeItem = function(item) {
    +  var child = this.getMenu().removeChild(item, true);
    +  if (child) {
    +    child.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Removes a menu item at a given index in the menu and disposes it.
    + * @param {number} n Index of item.
    + */
    +goog.ui.SubMenu.prototype.removeItemAt = function(n) {
    +  var child = this.getMenu().removeChildAt(n, true);
    +  if (child) {
    +    child.dispose();
    +  }
    +};
    +
    +
    +/**
    + * Returns a reference to the menu item at a given index.
    + * @param {number} n Index of menu item.
    + * @return {goog.ui.Component} Reference to the menu item.
    + */
    +goog.ui.SubMenu.prototype.getItemAt = function(n) {
    +  return this.getMenu().getChildAt(n);
    +};
    +
    +
    +/**
    + * Returns the number of items in the sub menu (including separators).
    + * @return {number} The number of items in the menu.
    + */
    +goog.ui.SubMenu.prototype.getItemCount = function() {
    +  return this.getMenu().getChildCount();
    +};
    +
    +
    +/**
    + * Returns the menu items contained in the sub menu.
    + * @return {!Array<!goog.ui.MenuItem>} An array of menu items.
    + * @deprecated Use getItemAt/getItemCount instead.
    + */
    +goog.ui.SubMenu.prototype.getItems = function() {
    +  return this.getMenu().getItems();
    +};
    +
    +
    +/**
    + * Gets a reference to the submenu's actual menu.
    + * @return {!goog.ui.Menu} Reference to the object representing the sub menu.
    + */
    +goog.ui.SubMenu.prototype.getMenu = function() {
    +  if (!this.subMenu_) {
    +    this.setMenu(
    +        new goog.ui.Menu(this.getDomHelper()), /* opt_internal */ true);
    +  } else if (this.externalSubMenu_ && this.subMenu_.getParent() != this) {
    +    // Since it is possible for the same popup menu to be attached to multiple
    +    // submenus, we need to ensure that it has the correct parent event target.
    +    this.subMenu_.setParent(this);
    +  }
    +  // Always create the menu DOM, for backward compatibility.
    +  if (!this.subMenu_.getElement()) {
    +    this.subMenu_.createDom();
    +  }
    +  return this.subMenu_;
    +};
    +
    +
    +/**
    + * Sets the submenu to a specific menu.
    + * @param {goog.ui.Menu} menu The menu to show when this item is selected.
    + * @param {boolean=} opt_internal Whether this menu is an "internal" menu, and
    + *     should be disposed of when this object is disposed of.
    + */
    +goog.ui.SubMenu.prototype.setMenu = function(menu, opt_internal) {
    +  var oldMenu = this.subMenu_;
    +  if (menu != oldMenu) {
    +    if (oldMenu) {
    +      this.dismissSubMenu();
    +      if (this.isInDocument()) {
    +        this.setMenuListenersEnabled_(oldMenu, false);
    +      }
    +    }
    +
    +    this.subMenu_ = menu;
    +    this.externalSubMenu_ = !opt_internal;
    +
    +    if (menu) {
    +      menu.setParent(this);
    +      // There's no need to dispatch a HIDE event during submenu construction.
    +      menu.setVisible(false, /* opt_force */ true);
    +      menu.setAllowAutoFocus(false);
    +      menu.setFocusable(false);
    +      if (this.isInDocument()) {
    +        this.setMenuListenersEnabled_(menu, true);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the provided element is to be considered inside the menu for
    + * purposes such as dismissing the menu on an event.  This is so submenus can
    + * make use of elements outside their own DOM.
    + * @param {Element} element The element to test for.
    + * @return {boolean} Whether or not the provided element is contained.
    + */
    +goog.ui.SubMenu.prototype.containsElement = function(element) {
    +  return this.getMenu().containsElement(element);
    +};
    +
    +
    +/**
    + * @param {boolean} isAdjustable Whether this submenu is adjustable.
    + */
    +goog.ui.SubMenu.prototype.setPositionAdjustable = function(isAdjustable) {
    +  this.isPositionAdjustable_ = !!isAdjustable;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether this submenu is adjustable.
    + */
    +goog.ui.SubMenu.prototype.isPositionAdjustable = function() {
    +  return this.isPositionAdjustable_;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.SubMenus.
    +goog.ui.registry.setDecoratorByClassName(goog.getCssName('goog-submenu'),
    +    function() {
    +      return new goog.ui.SubMenu(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/submenu_test.html b/mapbox-gl/closure-library/closure/goog/ui/submenu_test.html
    new file mode 100644
    index 0000000000..f7b4cf4433
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/submenu_test.html
    @@ -0,0 +1,102 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.SubMenu
    +  </title>
    +  <style type="text/css">
    +   .goog-menu {
    +  position: absolute;
    +  color: #000;
    +  border: 1px solid #B5B6B5;
    +  background-color: #F3F3F7;
    +  cursor: default;
    +  font: normal small arial, helvetica, sans-serif;
    +  margin: 0;
    +  padding: 0;
    +  outline: none;
    +}
    +
    +.goog-menuitem {
    +  padding: 2px 1.5em 2px 5px;
    +  margin: 0;
    +  list-style: none;
    +}
    +
    +.goog-menuitem-rtl {
    +  padding: 2px 5px 2px 1.5em;
    +}
    +
    +.goog-submenu-arrow {
    +  text-align: right;
    +  position: absolute;
    +  right: 0;
    +  left: auto;
    +}
    +
    +.goog-menuitem-rtl .goog-submenu-arrow {
    +  text-align: left;
    +  position: absolute;
    +  left: 0;
    +  right: auto;
    +}
    +
    +.goog-menuitem-disabled .goog-submenu-arrow {
    +  display: none;
    +}
    +  </style>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.SubMenuTest');
    +  </script>
    + </head>
    + <body>
    +  <p>
    +   Here's a menu (with submenus) defined in markup:
    +  </p>
    +  <div id="demoMenu" class="goog-menu">
    +   <div class="goog-menuitem">
    +    Open...
    +   </div>
    +   <div class="goog-submenu">
    +    Open Recent
    +    <div class="goog-menu">
    +     <div class="goog-menuitem">
    +      Annual Report.pdf
    +     </div>
    +     <div class="goog-menuitem">
    +      Quarterly Update.pdf
    +     </div>
    +     <div class="goog-menuitem">
    +      Enemies List.txt
    +     </div>
    +     <div class="goog-submenu">
    +      More
    +      <div class="goog-menu">
    +       <div class="goog-menuitem">
    +        Foo.txt
    +       </div>
    +       <div class="goog-menuitem">
    +        Bar.txt
    +       </div>
    +      </div>
    +     </div>
    +    </div>
    +   </div>
    +  </div>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/submenu_test.js b/mapbox-gl/closure-library/closure/goog/ui/submenu_test.js
    new file mode 100644
    index 0000000000..b2022d0dc6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/submenu_test.js
    @@ -0,0 +1,547 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.SubMenuTest');
    +goog.setTestOnly('goog.ui.SubMenuTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.functions');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.SubMenu');
    +goog.require('goog.ui.SubMenuRenderer');
    +
    +var menu;
    +var clonedMenuDom;
    +
    +var mockClock;
    +
    +// mock out goog.positioning.positionAtCoordinate so that
    +// the menu always fits. (we don't care about testing the
    +// dynamic menu positioning if the menu doesn't fit in the window.)
    +var oldPositionFn = goog.positioning.positionAtCoordinate;
    +goog.positioning.positionAtCoordinate = function(absolutePos, movableElement,
    +                                                 movableElementCorner,
    +                                                 opt_margin, opt_overflow) {
    +  return oldPositionFn.call(null, absolutePos, movableElement,
    +      movableElementCorner, opt_margin, goog.positioning.Overflow.IGNORE);
    +};
    +
    +function setUp() {
    +  clonedMenuDom = goog.dom.getElement('demoMenu').cloneNode(true);
    +
    +  menu = new goog.ui.Menu();
    +}
    +
    +function tearDown() {
    +  document.body.style.direction = 'ltr';
    +  menu.dispose();
    +
    +  var element = goog.dom.getElement('demoMenu');
    +  element.parentNode.replaceChild(clonedMenuDom, element);
    +
    +  goog.dom.getElement('sandbox').innerHTML = '';
    +
    +  if (mockClock) {
    +    mockClock.uninstall();
    +    mockClock = null;
    +  }
    +}
    +
    +function assertKeyHandlingIsCorrect(keyToOpenSubMenu, keyToCloseSubMenu) {
    +  menu.setFocusable(true);
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +
    +  var KeyCodes = goog.events.KeyCodes;
    +  var plainItem = menu.getChildAt(0);
    +  plainItem.setMnemonic(KeyCodes.F);
    +
    +  var subMenuItem1 = menu.getChildAt(1);
    +  subMenuItem1.setMnemonic(KeyCodes.S);
    +  var subMenuItem1Menu = subMenuItem1.getMenu();
    +  menu.setHighlighted(plainItem);
    +
    +  var fireKeySequence = goog.testing.events.fireKeySequence;
    +
    +  assertTrue(
    +      'Expected OpenSubMenu key to not be handled',
    +      fireKeySequence(plainItem.getElement(), keyToOpenSubMenu));
    +  assertFalse(subMenuItem1Menu.isVisible());
    +
    +  assertFalse(
    +      'Expected F key to be handled',
    +      fireKeySequence(plainItem.getElement(), KeyCodes.F));
    +
    +  assertFalse(
    +      'Expected DOWN key to be handled',
    +      fireKeySequence(plainItem.getElement(), KeyCodes.DOWN));
    +  assertEquals(subMenuItem1, menu.getChildAt(1));
    +
    +  assertFalse(
    +      'Expected OpenSubMenu key to be handled',
    +      fireKeySequence(subMenuItem1.getElement(), keyToOpenSubMenu));
    +  assertTrue(subMenuItem1Menu.isVisible());
    +
    +  assertFalse(
    +      'Expected CloseSubMenu key to be handled',
    +      fireKeySequence(subMenuItem1.getElement(), keyToCloseSubMenu));
    +  assertFalse(subMenuItem1Menu.isVisible());
    +
    +  assertFalse(
    +      'Expected UP key to be handled',
    +      fireKeySequence(subMenuItem1.getElement(), KeyCodes.UP));
    +
    +  assertFalse(
    +      'Expected S key to be handled',
    +      fireKeySequence(plainItem.getElement(), KeyCodes.S));
    +  assertTrue(subMenuItem1Menu.isVisible());
    +}
    +
    +function testKeyHandling_ltr() {
    +  assertKeyHandlingIsCorrect(goog.events.KeyCodes.RIGHT,
    +      goog.events.KeyCodes.LEFT);
    +}
    +
    +function testKeyHandling_rtl() {
    +  document.body.style.direction = 'rtl';
    +  assertKeyHandlingIsCorrect(goog.events.KeyCodes.LEFT,
    +      goog.events.KeyCodes.RIGHT);
    +}
    +
    +function testNormalLtrSubMenu() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  assertArrowDirection(subMenu, false);
    +  assertRenderDirection(subMenu, false);
    +  assertArrowPosition(subMenu, false);
    +}
    +
    +function testNormalRtlSubMenu() {
    +  document.body.style.direction = 'rtl';
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  assertArrowDirection(subMenu, true);
    +  assertRenderDirection(subMenu, true);
    +  assertArrowPosition(subMenu, true);
    +}
    +
    +function testLtrSubMenuAlignedToStart() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setAlignToEnd(false);
    +  assertArrowDirection(subMenu, true);
    +  assertRenderDirection(subMenu, true);
    +  assertArrowPosition(subMenu, false);
    +}
    +
    +function testNullContentElement() {
    +  var subMenu = new goog.ui.SubMenu();
    +  subMenu.setContent('demo');
    +}
    +
    +function testRtlSubMenuAlignedToStart() {
    +  document.body.style.direction = 'rtl';
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setAlignToEnd(false);
    +  assertArrowDirection(subMenu, false);
    +  assertRenderDirection(subMenu, false);
    +  assertArrowPosition(subMenu, true);
    +}
    +
    +function testSetContentKeepsArrow_ltr() {
    +  document.body.style.direction = 'ltr';
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setAlignToEnd(false);
    +  subMenu.setContent('test');
    +  assertArrowDirection(subMenu, true);
    +  assertRenderDirection(subMenu, true);
    +  assertArrowPosition(subMenu, false);
    +}
    +
    +function testSetContentKeepsArrow_rtl() {
    +  document.body.style.direction = 'rtl';
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setAlignToEnd(false);
    +  subMenu.setContent('test');
    +  assertArrowDirection(subMenu, false);
    +  assertRenderDirection(subMenu, false);
    +  assertArrowPosition(subMenu, true);
    +}
    +
    +function testExitDocument() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  var innerMenu = subMenu.getMenu();
    +
    +  assertTrue('Top-level menu was not in document', menu.isInDocument());
    +  assertTrue('Submenu was not in document', subMenu.isInDocument());
    +  assertTrue('Inner menu was not in document', innerMenu.isInDocument());
    +
    +  menu.exitDocument();
    +
    +  assertFalse('Top-level menu was in document', menu.isInDocument());
    +  assertFalse('Submenu was in document', subMenu.isInDocument());
    +  assertFalse('Inner menu was in document', innerMenu.isInDocument());
    +}
    +
    +function testDisposal() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  var innerMenu = subMenu.getMenu();
    +  menu.dispose();
    +
    +  assert('Top-level menu was not disposed', menu.getDisposed());
    +  assert('Submenu was not disposed', subMenu.getDisposed());
    +  assert('Inner menu was not disposed', innerMenu.getDisposed());
    +}
    +
    +function testShowAndDismissSubMenu() {
    +  var openEventDispatched = false;
    +  var closeEventDispatched = false;
    +
    +  function handleEvent(e) {
    +    switch (e.type) {
    +      case goog.ui.Component.EventType.OPEN:
    +        openEventDispatched = true;
    +        break;
    +      case goog.ui.Component.EventType.CLOSE:
    +        closeEventDispatched = true;
    +        break;
    +      default:
    +        fail('Invalid event type: ' + e.type);
    +    }
    +  }
    +
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setHighlighted(true);
    +
    +  goog.events.listen(subMenu, [
    +    goog.ui.Component.EventType.OPEN,
    +    goog.ui.Component.EventType.CLOSE
    +  ], handleEvent);
    +
    +  assertFalse('Submenu must not have "-open" CSS class',
    +      goog.dom.classlist.contains(subMenu.getElement(), 'goog-submenu-open'));
    +  assertFalse('Popup menu must not be visible',
    +      subMenu.getMenu().isVisible());
    +  assertFalse('No OPEN event must have been dispatched', openEventDispatched);
    +  assertFalse('No CLOSE event must have been dispatched', closeEventDispatched);
    +
    +  subMenu.showSubMenu();
    +  assertTrue('Submenu must have "-open" CSS class',
    +      goog.dom.classlist.contains(subMenu.getElement(), 'goog-submenu-open'));
    +  assertTrue('Popup menu must be visible',
    +      subMenu.getMenu().isVisible());
    +  assertTrue('OPEN event must have been dispatched', openEventDispatched);
    +  assertFalse('No CLOSE event must have been dispatched', closeEventDispatched);
    +
    +  subMenu.dismissSubMenu();
    +  assertFalse('Submenu must not have "-open" CSS class',
    +      goog.dom.classlist.contains(subMenu.getElement(), 'goog-submenu-open'));
    +  assertFalse('Popup menu must not be visible',
    +      subMenu.getMenu().isVisible());
    +  assertTrue('CLOSE event must have been dispatched', closeEventDispatched);
    +
    +  goog.events.unlisten(subMenu, [
    +    goog.ui.Component.EventType.OPEN,
    +    goog.ui.Component.EventType.CLOSE
    +  ], handleEvent);
    +}
    +
    +function testDismissWhenSubMenuNotVisible() {
    +  var openEventDispatched = false;
    +  var closeEventDispatched = false;
    +
    +  function handleEvent(e) {
    +    switch (e.type) {
    +      case goog.ui.Component.EventType.OPEN:
    +        openEventDispatched = true;
    +        break;
    +      case goog.ui.Component.EventType.CLOSE:
    +        closeEventDispatched = true;
    +        break;
    +      default:
    +        fail('Invalid event type: ' + e.type);
    +    }
    +  }
    +
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setHighlighted(true);
    +
    +  goog.events.listen(subMenu, [
    +    goog.ui.Component.EventType.OPEN,
    +    goog.ui.Component.EventType.CLOSE
    +  ], handleEvent);
    +
    +  assertFalse('Submenu must not have "-open" CSS class',
    +      goog.dom.classlist.contains(subMenu.getElement(), 'goog-submenu-open'));
    +  assertFalse('Popup menu must not be visible',
    +      subMenu.getMenu().isVisible());
    +  assertFalse('No OPEN event must have been dispatched', openEventDispatched);
    +  assertFalse('No CLOSE event must have been dispatched', closeEventDispatched);
    +
    +  subMenu.showSubMenu();
    +  subMenu.getMenu().setVisible(false);
    +
    +  subMenu.dismissSubMenu();
    +  assertFalse('Submenu must not have "-open" CSS class',
    +      goog.dom.classlist.contains(subMenu.getElement(), 'goog-submenu-open'));
    +  assertFalse(subMenu.menuIsVisible_);
    +  assertFalse('Popup menu must not be visible',
    +      subMenu.getMenu().isVisible());
    +  assertTrue('CLOSE event must have been dispatched', closeEventDispatched);
    +
    +  goog.events.unlisten(subMenu, [
    +    goog.ui.Component.EventType.OPEN,
    +    goog.ui.Component.EventType.CLOSE
    +  ], handleEvent);
    +}
    +
    +function testLazyInstantiateSubMenu() {
    +  menu.decorate(goog.dom.getElement('demoMenu'));
    +  var subMenu = menu.getChildAt(1);
    +  subMenu.setHighlighted(true);
    +
    +  var lazyMenu;
    +
    +  var key = goog.events.listen(subMenu, goog.ui.Component.EventType.OPEN,
    +      function(e) {
    +        lazyMenu = new goog.ui.Menu();
    +        lazyMenu.addItem(new goog.ui.MenuItem('foo'));
    +        lazyMenu.addItem(new goog.ui.MenuItem('bar'));
    +        subMenu.setMenu(lazyMenu, /* opt_internal */ false);
    +      });
    +
    +  subMenu.showSubMenu();
    +  assertNotNull('Popup menu must have been created', lazyMenu);
    +  assertEquals('Popup menu must be a child of the submenu', subMenu,
    +      lazyMenu.getParent());
    +  assertTrue('Popup menu must have been rendered', lazyMenu.isInDocument());
    +  assertTrue('Popup menu must be visible', lazyMenu.isVisible());
    +
    +  menu.dispose();
    +  assertTrue('Submenu must have been disposed of', subMenu.isDisposed());
    +  assertFalse('Popup menu must not have been disposed of',
    +      lazyMenu.isDisposed());
    +
    +  lazyMenu.dispose();
    +
    +  goog.events.unlistenByKey(key);
    +}
    +
    +function testReusableMenu() {
    +  var subMenuOne = new goog.ui.SubMenu('SubMenu One');
    +  var subMenuTwo = new goog.ui.SubMenu('SubMenu Two');
    +  menu.addItem(subMenuOne);
    +  menu.addItem(subMenuTwo);
    +  menu.render(goog.dom.getElement('sandbox'));
    +
    +  // It is possible for the same popup menu to be shared between different
    +  // submenus.
    +  var sharedMenu = new goog.ui.Menu();
    +  sharedMenu.addItem(new goog.ui.MenuItem('Hello'));
    +  sharedMenu.addItem(new goog.ui.MenuItem('World'));
    +
    +  assertNull('Shared menu must not have a parent', sharedMenu.getParent());
    +
    +  subMenuOne.setMenu(sharedMenu);
    +  assertEquals('SubMenuOne must point to the shared menu', sharedMenu,
    +      subMenuOne.getMenu());
    +  assertEquals('SubMenuOne must be the shared menu\'s parent', subMenuOne,
    +      sharedMenu.getParent());
    +
    +  subMenuTwo.setMenu(sharedMenu);
    +  assertEquals('SubMenuTwo must point to the shared menu', sharedMenu,
    +      subMenuTwo.getMenu());
    +  assertEquals('SubMenuTwo must be the shared menu\'s parent', subMenuTwo,
    +      sharedMenu.getParent());
    +  assertEquals('SubMenuOne must still point to the shared menu', sharedMenu,
    +      subMenuOne.getMenu());
    +
    +  menu.setHighlighted(subMenuOne);
    +  subMenuOne.showSubMenu();
    +  assertEquals('SubMenuOne must point to the shared menu', sharedMenu,
    +      subMenuOne.getMenu());
    +  assertEquals('SubMenuOne must be the shared menu\'s parent', subMenuOne,
    +      sharedMenu.getParent());
    +  assertEquals('SubMenuTwo must still point to the shared menu', sharedMenu,
    +      subMenuTwo.getMenu());
    +  assertTrue('Shared menu must be visible', sharedMenu.isVisible());
    +
    +  menu.setHighlighted(subMenuTwo);
    +  subMenuTwo.showSubMenu();
    +  assertEquals('SubMenuTwo must point to the shared menu', sharedMenu,
    +      subMenuTwo.getMenu());
    +  assertEquals('SubMenuTwo must be the shared menu\'s parent', subMenuTwo,
    +      sharedMenu.getParent());
    +  assertEquals('SubMenuOne must still point to the shared menu', sharedMenu,
    +      subMenuOne.getMenu());
    +  assertTrue('Shared menu must be visible', sharedMenu.isVisible());
    +}
    +
    +
    +/**
    + * If you remove a submenu in the interval between when a mouseover event
    + * is fired on it, and showSubMenu() is called, showSubMenu causes a null
    + * value to be dereferenced. This test validates that the fix for this works.
    + * (See bug 1823144).
    + */
    +function testDeleteItemDuringSubmenuDisplayInterval() {
    +  mockClock = new goog.testing.MockClock(true);
    +
    +  var submenu = new goog.ui.SubMenu('submenu');
    +  submenu.addItem(new goog.ui.MenuItem('submenu item 1'));
    +  menu.addItem(submenu);
    +
    +  // Trigger mouseover, and remove item before showSubMenu can be called.
    +  var e = new goog.events.Event();
    +  submenu.handleMouseOver(e);
    +  menu.removeItem(submenu);
    +  mockClock.tick(goog.ui.SubMenu.MENU_DELAY_MS);
    +  // (No JS error should occur.)
    +}
    +
    +function testShowSubMenuAfterRemoval() {
    +  var submenu = new goog.ui.SubMenu('submenu');
    +  menu.addItem(submenu);
    +  menu.removeItem(submenu);
    +  submenu.showSubMenu();
    +  // (No JS error should occur.)
    +}
    +
    +function testSubmenuSelectable() {
    +  var submenu = new goog.ui.SubMenu('submenu');
    +  submenu.addItem(new goog.ui.MenuItem('submenu item 1'));
    +  menu.addItem(submenu);
    +  submenu.setSelectable(true);
    +
    +  var numClicks = 0;
    +  var menuClickedFn = function(e) {
    +    numClicks++;
    +  };
    +
    +  goog.events.listen(submenu, goog.ui.Component.EventType.ACTION,
    +      menuClickedFn);
    +  submenu.performActionInternal(null);
    +  submenu.performActionInternal(null);
    +
    +  assertEquals('The submenu should have fired an event', 2, numClicks);
    +
    +  submenu.setSelectable(false);
    +  submenu.performActionInternal(null);
    +
    +  assertEquals('The submenu should not have fired any further events', 2,
    +      numClicks);
    +}
    +
    +
    +/**
    + * Tests that entering a child menu cancels the dismiss timer for the submenu.
    + */
    +function testEnteringChildCancelsDismiss() {
    +  var submenu = new goog.ui.SubMenu('submenu');
    +  submenu.isInDocument = goog.functions.TRUE;
    +  submenu.addItem(new goog.ui.MenuItem('submenu item 1'));
    +  menu.addItem(submenu);
    +
    +  mockClock = new goog.testing.MockClock(true);
    +  submenu.getMenu().setVisible(true);
    +
    +  // This starts the dismiss timer.
    +  submenu.setHighlighted(false);
    +
    +  // This should cancel the dismiss timer.
    +  submenu.getMenu().dispatchEvent(goog.ui.Component.EventType.ENTER);
    +
    +  // Tick the length of the dismiss timer.
    +  mockClock.tick(goog.ui.SubMenu.MENU_DELAY_MS);
    +
    +  // Check that the menu is now highlighted and still visible.
    +  assertTrue(submenu.getMenu().isVisible());
    +  assertTrue(submenu.isHighlighted());
    +}
    +
    +
    +/**
    + * Asserts that this sub menu renders in the right direction relative to
    + * the parent menu.
    + * @param {goog.ui.SubMenu} subMenu The sub menu.
    + * @param {boolean} left True for left-pointing, false for right-pointing.
    + */
    +function assertRenderDirection(subMenu, left) {
    +  subMenu.getParent().setHighlighted(subMenu);
    +  subMenu.showSubMenu();
    +  var menuItemPosition = goog.style.getPageOffset(subMenu.getElement());
    +  var menuPosition = goog.style.getPageOffset(subMenu.getMenu().getElement());
    +  assert(Math.abs(menuItemPosition.y - menuPosition.y) < 5);
    +  assertEquals(
    +      'Menu at: ' + menuPosition.x +
    +      ', submenu item at: ' + menuItemPosition.x,
    +      left, menuPosition.x < menuItemPosition.x);
    +}
    +
    +
    +/**
    + * Asserts that this sub menu has a properly-oriented arrow.
    + * @param {goog.ui.SubMenu} subMenu The sub menu.
    + * @param {boolean} left True for left-pointing, false for right-pointing.
    + */
    +function assertArrowDirection(subMenu, left) {
    +  assertEquals(
    +      left ? goog.ui.SubMenuRenderer.LEFT_ARROW_ :
    +      goog.ui.SubMenuRenderer.RIGHT_ARROW_,
    +      getArrowElement(subMenu).innerHTML);
    +}
    +
    +
    +/**
    + * Asserts that the arrow position is correct.
    + * @param {goog.ui.SubMenu} subMenu The sub menu.
    + * @param {boolean} leftAlign True for left-aligned, false for right-aligned.
    + */
    +function assertArrowPosition(subMenu, left) {
    +  var arrow = getArrowElement(subMenu);
    +  var expectedLeft =
    +      left ? 0 : arrow.offsetParent.offsetWidth - arrow.offsetWidth;
    +  var actualLeft = arrow.offsetLeft;
    +  assertTrue('Expected left offset: ' + expectedLeft + '\n' +
    +             'Actual left offset: ' + actualLeft + '\n',
    +             Math.abs(expectedLeft - actualLeft) < 5);
    +}
    +
    +
    +/**
    + * Gets the arrow element of a sub menu.
    + * @param {goog.ui.SubMenu} subMenu The sub menu.
    + * @return {Element} The arrow.
    + */
    +function getArrowElement(subMenu) {
    +  return subMenu.getContentElement().lastChild;
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/submenurenderer.js b/mapbox-gl/closure-library/closure/goog/ui/submenurenderer.js
    new file mode 100644
    index 0000000000..3ae9959044
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/submenurenderer.js
    @@ -0,0 +1,239 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.SubMenu}s.
    + *
    + */
    +
    +goog.provide('goog.ui.SubMenuRenderer');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.style');
    +goog.require('goog.ui.Menu');
    +goog.require('goog.ui.MenuItemRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.SubMenu}s.  Each item has the following
    + * structure:
    + *    <div class="goog-submenu">
    + *      ...(menuitem content)...
    + *      <div class="goog-menu">
    + *        ... (submenu content) ...
    + *      </div>
    + *    </div>
    + * @constructor
    + * @extends {goog.ui.MenuItemRenderer}
    + * @final
    + */
    +goog.ui.SubMenuRenderer = function() {
    +  goog.ui.MenuItemRenderer.call(this);
    +};
    +goog.inherits(goog.ui.SubMenuRenderer, goog.ui.MenuItemRenderer);
    +goog.addSingletonGetter(goog.ui.SubMenuRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.SubMenuRenderer.CSS_CLASS = goog.getCssName('goog-submenu');
    +
    +
    +/**
    + * The CSS class for submenus that displays the submenu arrow.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SubMenuRenderer.CSS_CLASS_SUBMENU_ =
    +    goog.getCssName('goog-submenu-arrow');
    +
    +
    +/**
    + * Overrides {@link goog.ui.MenuItemRenderer#createDom} by adding
    + * the additional class 'goog-submenu' to the created element,
    + * and passes the element to {@link goog.ui.SubMenuItemRenderer#addArrow_}
    + * to add an child element that can be styled to show an arrow.
    + * @param {goog.ui.Control} control goog.ui.SubMenu to render.
    + * @return {!Element} Root element for the item.
    + * @override
    + */
    +goog.ui.SubMenuRenderer.prototype.createDom = function(control) {
    +  var subMenu = /** @type {goog.ui.SubMenu} */ (control);
    +  var element = goog.ui.SubMenuRenderer.superClass_.createDom.call(this,
    +                                                                   subMenu);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.SubMenuRenderer.CSS_CLASS);
    +  this.addArrow_(subMenu, element);
    +  return element;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.MenuItemRenderer#decorate} by adding
    + * the additional class 'goog-submenu' to the decorated element,
    + * and passing the element to {@link goog.ui.SubMenuItemRenderer#addArrow_}
    + * to add a child element that can be styled to show an arrow.
    + * Also searches the element for a child with the class goog-menu. If a
    + * matching child element is found, creates a goog.ui.Menu, uses it to
    + * decorate the child element, and passes that menu to subMenu.setMenu.
    + * @param {goog.ui.Control} control goog.ui.SubMenu to render.
    + * @param {Element} element Element to decorate.
    + * @return {!Element} Root element for the item.
    + * @override
    + */
    +goog.ui.SubMenuRenderer.prototype.decorate = function(control, element) {
    +  var subMenu = /** @type {goog.ui.SubMenu} */ (control);
    +  element = goog.ui.SubMenuRenderer.superClass_.decorate.call(
    +      this, subMenu, element);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.SubMenuRenderer.CSS_CLASS);
    +  this.addArrow_(subMenu, element);
    +
    +  // Search for a child menu and decorate it.
    +  var childMenuEls = goog.dom.getElementsByTagNameAndClass(
    +      'div', goog.getCssName('goog-menu'), element);
    +  if (childMenuEls.length) {
    +    var childMenu = new goog.ui.Menu(subMenu.getDomHelper());
    +    var childMenuEl = childMenuEls[0];
    +    // Hide the menu element before attaching it to the document body; see
    +    // bug 1089244.
    +    goog.style.setElementShown(childMenuEl, false);
    +    subMenu.getDomHelper().getDocument().body.appendChild(childMenuEl);
    +    childMenu.decorate(childMenuEl);
    +    subMenu.setMenu(childMenu, true);
    +  }
    +  return element;
    +};
    +
    +
    +/**
    + * Takes a menu item's root element, and sets its content to the given text
    + * caption or DOM structure.  Overrides the superclass immplementation by
    + * making sure that the submenu arrow structure is preserved.
    + * @param {Element} element The item's root element.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to be
    + *     set as the item's content.
    + * @override
    + */
    +goog.ui.SubMenuRenderer.prototype.setContent = function(element, content) {
    +  // Save the submenu arrow element, if present.
    +  var contentElement = this.getContentElement(element);
    +  var arrowElement = contentElement && contentElement.lastChild;
    +  goog.ui.SubMenuRenderer.superClass_.setContent.call(this, element, content);
    +  // If the arrowElement was there, is no longer there, and really was an arrow,
    +  // reappend it.
    +  if (arrowElement &&
    +      contentElement.lastChild != arrowElement &&
    +      goog.dom.classlist.contains(/** @type {!Element} */ (arrowElement),
    +          goog.ui.SubMenuRenderer.CSS_CLASS_SUBMENU_)) {
    +    contentElement.appendChild(arrowElement);
    +  }
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.MenuItemRenderer#initializeDom} to tweak
    + * the DOM structure for the span.goog-submenu-arrow element
    + * depending on the text direction (LTR or RTL). When the SubMenu is RTL
    + * the arrow will be given the additional class of goog-submenu-arrow-rtl,
    + * and the arrow will be moved up to be the first child in the SubMenu's
    + * element. Otherwise the arrow will have the class goog-submenu-arrow-ltr,
    + * and be kept as the last child of the SubMenu's element.
    + * @param {goog.ui.Control} control goog.ui.SubMenu whose DOM is to be
    + *     initialized as it enters the document.
    + * @override
    + */
    +goog.ui.SubMenuRenderer.prototype.initializeDom = function(control) {
    +  var subMenu = /** @type {goog.ui.SubMenu} */ (control);
    +  goog.ui.SubMenuRenderer.superClass_.initializeDom.call(this, subMenu);
    +  var element = subMenu.getContentElement();
    +  var arrow = subMenu.getDomHelper().getElementsByTagNameAndClass(
    +      'span', goog.ui.SubMenuRenderer.CSS_CLASS_SUBMENU_, element)[0];
    +  goog.ui.SubMenuRenderer.setArrowTextContent_(subMenu, arrow);
    +  if (arrow != element.lastChild) {
    +    element.appendChild(arrow);
    +  }
    +  var subMenuElement = subMenu.getElement();
    +  goog.asserts.assert(subMenuElement,
    +      'The sub menu DOM element cannot be null.');
    +  goog.a11y.aria.setState(subMenuElement,
    +      goog.a11y.aria.State.HASPOPUP,
    +      'true');
    +};
    +
    +
    +/**
    + * Appends a child node with the class goog.getCssName('goog-submenu-arrow') or
    + * 'goog-submenu-arrow-rtl' which can be styled to show an arrow.
    + * @param {goog.ui.SubMenu} subMenu SubMenu to render.
    + * @param {Element} element Element to decorate.
    + * @private
    + */
    +goog.ui.SubMenuRenderer.prototype.addArrow_ = function(subMenu, element) {
    +  var arrow = subMenu.getDomHelper().createDom('span');
    +  arrow.className = goog.ui.SubMenuRenderer.CSS_CLASS_SUBMENU_;
    +  goog.ui.SubMenuRenderer.setArrowTextContent_(subMenu, arrow);
    +  this.getContentElement(element).appendChild(arrow);
    +};
    +
    +
    +/**
    + * The unicode char for a left arrow.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SubMenuRenderer.LEFT_ARROW_ = '\u25C4';
    +
    +
    +/**
    + * The unicode char for a right arrow.
    + * @type {string}
    + * @private
    + */
    +goog.ui.SubMenuRenderer.RIGHT_ARROW_ = '\u25BA';
    +
    +
    +/**
    + * Set the text content of an arrow.
    + * @param {goog.ui.SubMenu} subMenu The sub menu that owns the arrow.
    + * @param {Element} arrow The arrow element.
    + * @private
    + */
    +goog.ui.SubMenuRenderer.setArrowTextContent_ = function(subMenu, arrow) {
    +  // Fix arrow rtl
    +  var leftArrow = goog.ui.SubMenuRenderer.LEFT_ARROW_;
    +  var rightArrow = goog.ui.SubMenuRenderer.RIGHT_ARROW_;
    +
    +  goog.asserts.assert(arrow);
    +
    +  if (subMenu.isRightToLeft()) {
    +    goog.dom.classlist.add(arrow, goog.getCssName('goog-submenu-arrow-rtl'));
    +    // Unicode character - Black left-pointing pointer iff aligned to end.
    +    goog.dom.setTextContent(arrow, subMenu.isAlignedToEnd() ?
    +        leftArrow : rightArrow);
    +  } else {
    +    goog.dom.classlist.remove(arrow, goog.getCssName('goog-submenu-arrow-rtl'));
    +    // Unicode character - Black right-pointing pointer iff aligned to end.
    +    goog.dom.setTextContent(arrow, subMenu.isAlignedToEnd() ?
    +        rightArrow : leftArrow);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tab.js b/mapbox-gl/closure-library/closure/goog/ui/tab.js
    new file mode 100644
    index 0000000000..d589df2702
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tab.js
    @@ -0,0 +1,103 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A tab control, designed to be used in {@link goog.ui.TabBar}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/tabbar.html
    + */
    +
    +goog.provide('goog.ui.Tab');
    +
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.TabRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Tab control, designed to be hosted in a {@link goog.ui.TabBar}.  The tab's
    + * DOM may be different based on the configuration of the containing tab bar,
    + * so tabs should only be rendered or decorated as children of a tab bar.
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure to
    + *     display as the tab's caption (if any).
    + * @param {goog.ui.TabRenderer=} opt_renderer Optional renderer used to render
    + *     or decorate the tab.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Tab = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, content,
    +      opt_renderer || goog.ui.TabRenderer.getInstance(), opt_domHelper);
    +
    +  // Tabs support the SELECTED state.
    +  this.setSupportedState(goog.ui.Component.State.SELECTED, true);
    +
    +  // Tabs must dispatch state transition events for the DISABLED and SELECTED
    +  // states in order for the tab bar to function properly.
    +  this.setDispatchTransitionEvents(
    +      goog.ui.Component.State.DISABLED | goog.ui.Component.State.SELECTED,
    +      true);
    +};
    +goog.inherits(goog.ui.Tab, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.Tab);
    +
    +
    +/**
    + * Tooltip text for the tab, displayed on hover (if any).
    + * @type {string|undefined}
    + * @private
    + */
    +goog.ui.Tab.prototype.tooltip_;
    +
    +
    +/**
    + * @return {string|undefined} Tab tooltip text (if any).
    + */
    +goog.ui.Tab.prototype.getTooltip = function() {
    +  return this.tooltip_;
    +};
    +
    +
    +/**
    + * Sets the tab tooltip text.  If the tab has already been rendered, updates
    + * its tooltip.
    + * @param {string} tooltip New tooltip text.
    + */
    +goog.ui.Tab.prototype.setTooltip = function(tooltip) {
    +  this.getRenderer().setTooltip(this.getElement(), tooltip);
    +  this.setTooltipInternal(tooltip);
    +};
    +
    +
    +/**
    + * Sets the tab tooltip text.  Considered protected; to be called only by the
    + * renderer during element decoration.
    + * @param {string} tooltip New tooltip text.
    + * @protected
    + */
    +goog.ui.Tab.prototype.setTooltipInternal = function(tooltip) {
    +  this.tooltip_ = tooltip;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.Tabs.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.TabRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.Tab(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tab_test.html b/mapbox-gl/closure-library/closure/goog/ui/tab_test.html
    new file mode 100644
    index 0000000000..e8c7f00b9a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tab_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Tab
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TabTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tab_test.js b/mapbox-gl/closure-library/closure/goog/ui/tab_test.js
    new file mode 100644
    index 0000000000..04fd881d84
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tab_test.js
    @@ -0,0 +1,74 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TabTest');
    +goog.setTestOnly('goog.ui.TabTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabRenderer');
    +
    +var sandbox;
    +var tab;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  tab = new goog.ui.Tab('Hello');
    +}
    +
    +function tearDown() {
    +  tab.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Tab must not be null', tab);
    +  assertEquals('Tab must have expected content', 'Hello', tab.getContent());
    +  assertEquals('Tab\'s renderer must default to TabRenderer',
    +      goog.ui.TabRenderer.getInstance(), tab.getRenderer());
    +  assertTrue('Tab must support the SELECTED state',
    +      tab.isSupportedState(goog.ui.Component.State.SELECTED));
    +  assertTrue('SELECTED must be an auto-state',
    +      tab.isAutoState(goog.ui.Component.State.SELECTED));
    +  assertTrue('Tab must dispatch transition events for the DISABLED state',
    +      tab.isDispatchTransitionEvents(goog.ui.Component.State.DISABLED));
    +  assertTrue('Tab must dispatch transition events for the SELECTED state',
    +      tab.isDispatchTransitionEvents(goog.ui.Component.State.SELECTED));
    +}
    +
    +function testGetSetTooltip() {
    +  assertUndefined('Tooltip must be undefined by default', tab.getTooltip());
    +  tab.setTooltip('Hello, world!');
    +  assertEquals('Tooltip must have expected value', 'Hello, world!',
    +      tab.getTooltip());
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Tab must not have aria label by default', tab.getAriaLabel());
    +  tab.setAriaLabel('My tab');
    +  tab.render();
    +  var element = tab.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Tab element must have expected aria-label', 'My tab',
    +      element.getAttribute('aria-label'));
    +  assertEquals('Tab element must have expected aria role', 'tab',
    +      element.getAttribute('role'));
    +  tab.setAriaLabel('My new tab');
    +  assertEquals('Tab element must have updated aria-label', 'My new tab',
    +      element.getAttribute('aria-label'));
    +  assertEquals('Tab element must have expected aria role', 'tab',
    +      element.getAttribute('role'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbar.js b/mapbox-gl/closure-library/closure/goog/ui/tabbar.js
    new file mode 100644
    index 0000000000..a6fae43637
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbar.js
    @@ -0,0 +1,395 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tab bar UI component.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/tabbar.html
    + */
    +
    +goog.provide('goog.ui.TabBar');
    +goog.provide('goog.ui.TabBar.Location');
    +
    +goog.require('goog.ui.Component.EventType');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Container.Orientation');
    +// We need to include following dependency because of the magic with
    +// goog.ui.registry.setDecoratorByClassName
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabBarRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Tab bar UI component.  A tab bar contains tabs, rendered above, below,
    + * before, or after tab contents.  Tabs in tab bars dispatch the following
    + * events:
    + * <ul>
    + *   <li>{@link goog.ui.Component.EventType.ACTION} when activated via the
    + *       keyboard or the mouse,
    + *   <li>{@link goog.ui.Component.EventType.SELECT} when selected, and
    + *   <li>{@link goog.ui.Component.EventType.UNSELECT} when deselected.
    + * </ul>
    + * Clients may listen for all of the above events on the tab bar itself, and
    + * refer to the event target to identify the tab that dispatched the event.
    + * When an unselected tab is clicked for the first time, it dispatches both a
    + * {@code SELECT} event and an {@code ACTION} event; subsequent clicks on an
    + * already selected tab only result in {@code ACTION} events.
    + *
    + * @param {goog.ui.TabBar.Location=} opt_location Tab bar location; defaults to
    + *     {@link goog.ui.TabBar.Location.TOP}.
    + * @param {goog.ui.TabBarRenderer=} opt_renderer Renderer used to render or
    + *     decorate the container; defaults to {@link goog.ui.TabBarRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for document
    + *     interaction.
    + * @constructor
    + * @extends {goog.ui.Container}
    + */
    +goog.ui.TabBar = function(opt_location, opt_renderer, opt_domHelper) {
    +  this.setLocation(opt_location || goog.ui.TabBar.Location.TOP);
    +
    +  goog.ui.Container.call(this, this.getOrientation(),
    +      opt_renderer || goog.ui.TabBarRenderer.getInstance(),
    +      opt_domHelper);
    +
    +  this.listenToTabEvents_();
    +};
    +goog.inherits(goog.ui.TabBar, goog.ui.Container);
    +goog.tagUnsealableClass(goog.ui.TabBar);
    +
    +
    +/**
    + * Tab bar location relative to tab contents.
    + * @enum {string}
    + */
    +goog.ui.TabBar.Location = {
    +  // Above tab contents.
    +  TOP: 'top',
    +  // Below tab contents.
    +  BOTTOM: 'bottom',
    +  // To the left of tab contents (to the right if the page is right-to-left).
    +  START: 'start',
    +  // To the right of tab contents (to the left if the page is right-to-left).
    +  END: 'end'
    +};
    +
    +
    +/**
    + * Tab bar location; defaults to {@link goog.ui.TabBar.Location.TOP}.
    + * @type {goog.ui.TabBar.Location}
    + * @private
    + */
    +goog.ui.TabBar.prototype.location_;
    +
    +
    +/**
    + * Whether keyboard navigation should change the selected tab, or just move
    + * the highlight.  Defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.TabBar.prototype.autoSelectTabs_ = true;
    +
    +
    +/**
    + * The currently selected tab (null if none).
    + * @type {goog.ui.Control?}
    + * @private
    + */
    +goog.ui.TabBar.prototype.selectedTab_ = null;
    +
    +
    +/**
    + * @override
    + */
    +goog.ui.TabBar.prototype.enterDocument = function() {
    +  goog.ui.TabBar.superClass_.enterDocument.call(this);
    +
    +  this.listenToTabEvents_();
    +};
    +
    +
    +/** @override */
    +goog.ui.TabBar.prototype.disposeInternal = function() {
    +  goog.ui.TabBar.superClass_.disposeInternal.call(this);
    +  this.selectedTab_ = null;
    +};
    +
    +
    +/**
    + * Removes the tab from the tab bar.  Overrides the superclass implementation
    + * by deselecting the tab being removed.  Since {@link #removeChildAt} uses
    + * {@link #removeChild} internally, we only need to override this method.
    + * @param {string|goog.ui.Component} tab Tab to remove.
    + * @param {boolean=} opt_unrender Whether to call {@code exitDocument} on the
    + *     removed tab, and detach its DOM from the document (defaults to false).
    + * @return {goog.ui.Control} The removed tab, if any.
    + * @override
    + */
    +goog.ui.TabBar.prototype.removeChild = function(tab, opt_unrender) {
    +  // This actually only accepts goog.ui.Controls. There's a TODO
    +  // on the superclass method to fix this.
    +  this.deselectIfSelected(/** @type {goog.ui.Control} */ (tab));
    +  return goog.ui.TabBar.superClass_.removeChild.call(this, tab, opt_unrender);
    +};
    +
    +
    +/**
    + * @return {goog.ui.TabBar.Location} Tab bar location relative to tab contents.
    + */
    +goog.ui.TabBar.prototype.getLocation = function() {
    +  return this.location_;
    +};
    +
    +
    +/**
    + * Sets the location of the tab bar relative to tab contents.
    + * @param {goog.ui.TabBar.Location} location Tab bar location relative to tab
    + *     contents.
    + * @throws {Error} If the tab bar has already been rendered.
    + */
    +goog.ui.TabBar.prototype.setLocation = function(location) {
    +  // setOrientation() will take care of throwing an error if already rendered.
    +  this.setOrientation(goog.ui.TabBar.getOrientationFromLocation(location));
    +  this.location_ = location;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether keyboard navigation should change the selected tab,
    + *     or just move the highlight.
    + */
    +goog.ui.TabBar.prototype.isAutoSelectTabs = function() {
    +  return this.autoSelectTabs_;
    +};
    +
    +
    +/**
    + * Enables or disables auto-selecting tabs using the keyboard.  If auto-select
    + * is enabled, keyboard navigation switches tabs immediately, otherwise it just
    + * moves the highlight.
    + * @param {boolean} enable Whether keyboard navigation should change the
    + *     selected tab, or just move the highlight.
    + */
    +goog.ui.TabBar.prototype.setAutoSelectTabs = function(enable) {
    +  this.autoSelectTabs_ = enable;
    +};
    +
    +
    +/**
    + * Highlights the tab at the given index in response to a keyboard event.
    + * Overrides the superclass implementation by also selecting the tab if
    + * {@link #isAutoSelectTabs} returns true.
    + * @param {number} index Index of tab to highlight.
    + * @protected
    + * @override
    + */
    +goog.ui.TabBar.prototype.setHighlightedIndexFromKeyEvent = function(index) {
    +  goog.ui.TabBar.superClass_.setHighlightedIndexFromKeyEvent.call(this, index);
    +  if (this.autoSelectTabs_) {
    +    // Immediately select the tab.
    +    this.setSelectedTabIndex(index);
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.ui.Control?} The currently selected tab (null if none).
    + */
    +goog.ui.TabBar.prototype.getSelectedTab = function() {
    +  return this.selectedTab_;
    +};
    +
    +
    +/**
    + * Selects the given tab.
    + * @param {goog.ui.Control?} tab Tab to select (null to select none).
    + */
    +goog.ui.TabBar.prototype.setSelectedTab = function(tab) {
    +  if (tab) {
    +    // Select the tab and have it dispatch a SELECT event, to be handled in
    +    // handleTabSelect() below.
    +    tab.setSelected(true);
    +  } else if (this.getSelectedTab()) {
    +    // De-select the currently selected tab and have it dispatch an UNSELECT
    +    // event, to be handled in handleTabUnselect() below.
    +    this.getSelectedTab().setSelected(false);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} Index of the currently selected tab (-1 if none).
    + */
    +goog.ui.TabBar.prototype.getSelectedTabIndex = function() {
    +  return this.indexOfChild(this.getSelectedTab());
    +};
    +
    +
    +/**
    + * Selects the tab at the given index.
    + * @param {number} index Index of the tab to select (-1 to select none).
    + */
    +goog.ui.TabBar.prototype.setSelectedTabIndex = function(index) {
    +  this.setSelectedTab(/** @type {goog.ui.Tab} */ (this.getChildAt(index)));
    +};
    +
    +
    +/**
    + * If the specified tab is the currently selected tab, deselects it, and
    + * selects the closest selectable tab in the tab bar (first looking before,
    + * then after the deselected tab).  Does nothing if the argument is not the
    + * currently selected tab.  Called internally when a tab is removed, hidden,
    + * or disabled, to ensure that another tab is selected instead.
    + * @param {goog.ui.Control?} tab Tab to deselect (if any).
    + * @protected
    + */
    +goog.ui.TabBar.prototype.deselectIfSelected = function(tab) {
    +  if (tab && tab == this.getSelectedTab()) {
    +    var index = this.indexOfChild(tab);
    +    // First look for the closest selectable tab before this one.
    +    for (var i = index - 1;
    +         tab = /** @type {goog.ui.Tab} */ (this.getChildAt(i));
    +         i--) {
    +      if (this.isSelectableTab(tab)) {
    +        this.setSelectedTab(tab);
    +        return;
    +      }
    +    }
    +    // Next, look for the closest selectable tab after this one.
    +    for (var j = index + 1;
    +         tab = /** @type {goog.ui.Tab} */ (this.getChildAt(j));
    +         j++) {
    +      if (this.isSelectableTab(tab)) {
    +        this.setSelectedTab(tab);
    +        return;
    +      }
    +    }
    +    // If all else fails, just set the selection to null.
    +    this.setSelectedTab(null);
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the tab is selectable, false otherwise.  Only visible and
    + * enabled tabs are selectable.
    + * @param {goog.ui.Control} tab Tab to check.
    + * @return {boolean} Whether the tab is selectable.
    + * @protected
    + */
    +goog.ui.TabBar.prototype.isSelectableTab = function(tab) {
    +  return tab.isVisible() && tab.isEnabled();
    +};
    +
    +
    +/**
    + * Handles {@code SELECT} events dispatched by tabs as they become selected.
    + * @param {goog.events.Event} e Select event to handle.
    + * @protected
    + */
    +goog.ui.TabBar.prototype.handleTabSelect = function(e) {
    +  if (this.selectedTab_ && this.selectedTab_ != e.target) {
    +    // Deselect currently selected tab.
    +    this.selectedTab_.setSelected(false);
    +  }
    +  this.selectedTab_ = /** @type {goog.ui.Tab} */ (e.target);
    +};
    +
    +
    +/**
    + * Handles {@code UNSELECT} events dispatched by tabs as they become deselected.
    + * @param {goog.events.Event} e Unselect event to handle.
    + * @protected
    + */
    +goog.ui.TabBar.prototype.handleTabUnselect = function(e) {
    +  if (e.target == this.selectedTab_) {
    +    this.selectedTab_ = null;
    +  }
    +};
    +
    +
    +/**
    + * Handles {@code DISABLE} events displayed by tabs.
    + * @param {goog.events.Event} e Disable event to handle.
    + * @protected
    + */
    +goog.ui.TabBar.prototype.handleTabDisable = function(e) {
    +  this.deselectIfSelected(/** @type {goog.ui.Tab} */ (e.target));
    +};
    +
    +
    +/**
    + * Handles {@code HIDE} events displayed by tabs.
    + * @param {goog.events.Event} e Hide event to handle.
    + * @protected
    + */
    +goog.ui.TabBar.prototype.handleTabHide = function(e) {
    +  this.deselectIfSelected(/** @type {goog.ui.Tab} */ (e.target));
    +};
    +
    +
    +/**
    + * Handles focus events dispatched by the tab bar's key event target.  If no tab
    + * is currently highlighted, highlights the selected tab or the first tab if no
    + * tab is selected either.
    + * @param {goog.events.Event} e Focus event to handle.
    + * @protected
    + * @override
    + */
    +goog.ui.TabBar.prototype.handleFocus = function(e) {
    +  if (!this.getHighlighted()) {
    +    this.setHighlighted(this.getSelectedTab() ||
    +        /** @type {goog.ui.Tab} */ (this.getChildAt(0)));
    +  }
    +};
    +
    +
    +/**
    + * Subscribes to events dispatched by tabs.
    + * @private
    + */
    +goog.ui.TabBar.prototype.listenToTabEvents_ = function() {
    +  // Listen for SELECT, UNSELECT, DISABLE, and HIDE events dispatched by tabs.
    +  this.getHandler().
    +      listen(this, goog.ui.Component.EventType.SELECT, this.handleTabSelect).
    +      listen(this,
    +             goog.ui.Component.EventType.UNSELECT,
    +             this.handleTabUnselect).
    +      listen(this, goog.ui.Component.EventType.DISABLE, this.handleTabDisable).
    +      listen(this, goog.ui.Component.EventType.HIDE, this.handleTabHide);
    +};
    +
    +
    +/**
    + * Returns the {@link goog.ui.Container.Orientation} that is implied by the
    + * given {@link goog.ui.TabBar.Location}.
    + * @param {goog.ui.TabBar.Location} location Tab bar location.
    + * @return {goog.ui.Container.Orientation} Corresponding orientation.
    + */
    +goog.ui.TabBar.getOrientationFromLocation = function(location) {
    +  return location == goog.ui.TabBar.Location.START ||
    +         location == goog.ui.TabBar.Location.END ?
    +             goog.ui.Container.Orientation.VERTICAL :
    +             goog.ui.Container.Orientation.HORIZONTAL;
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.TabBars.
    +goog.ui.registry.setDecoratorByClassName(goog.ui.TabBarRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.TabBar();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.html b/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.html
    new file mode 100644
    index 0000000000..9d8fb203bf
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TabBar
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TabBarTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.js b/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.js
    new file mode 100644
    index 0000000000..8475a51bb3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbar_test.js
    @@ -0,0 +1,606 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TabBarTest');
    +goog.setTestOnly('goog.ui.TabBarTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabBar');
    +goog.require('goog.ui.TabBarRenderer');
    +
    +var sandbox;
    +var tabBar;
    +
    +// Fake keyboard event object.
    +function FakeKeyEvent(keyCode) {
    +  this.keyCode = keyCode;
    +  this.defaultPrevented = false;
    +  this.propagationStopped = false;
    +}
    +FakeKeyEvent.prototype.preventDefault = function() {
    +  this.defaultPrevented = true;
    +};
    +FakeKeyEvent.prototype.stopPropagation = function() {
    +  this.propagationStopped = true;
    +};
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  tabBar = new goog.ui.TabBar();
    +}
    +
    +function tearDown() {
    +  tabBar.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Tab bar must not be null', tabBar);
    +  assertEquals('Tab bar renderer must default to expected value',
    +      goog.ui.TabBarRenderer.getInstance(), tabBar.getRenderer());
    +  assertEquals('Tab bar location must default to expected value',
    +      goog.ui.TabBar.Location.TOP, tabBar.getLocation());
    +  assertEquals('Tab bar orientation must default to expected value',
    +      goog.ui.Container.Orientation.HORIZONTAL, tabBar.getOrientation());
    +
    +  var fakeRenderer = {};
    +  var fakeDomHelper = {};
    +  var bar = new goog.ui.TabBar(goog.ui.TabBar.Location.START, fakeRenderer,
    +      fakeDomHelper);
    +  assertNotNull('Tab bar must not be null', bar);
    +  assertEquals('Tab bar renderer must have expected value',
    +      fakeRenderer, bar.getRenderer());
    +  assertEquals('Tab bar DOM helper must have expected value',
    +      fakeDomHelper, bar.getDomHelper());
    +  assertEquals('Tab bar location must have expected value',
    +      goog.ui.TabBar.Location.START, bar.getLocation());
    +  assertEquals('Tab bar orientation must have expected value',
    +      goog.ui.Container.Orientation.VERTICAL, bar.getOrientation());
    +  bar.dispose();
    +}
    +
    +function testDispose() {
    +  // Set tabBar.selectedTab_ to something non-null, just to test dispose().
    +  tabBar.selectedTab_ = {};
    +  assertNotNull('Selected tab must be non-null', tabBar.getSelectedTab());
    +  assertFalse('Tab bar must not have been disposed of',
    +      tabBar.isDisposed());
    +  tabBar.dispose();
    +  assertNull('Selected tab must be null', tabBar.getSelectedTab());
    +  assertTrue('Tab bar must have been disposed of', tabBar.isDisposed());
    +}
    +
    +function testAddRemoveChild() {
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +
    +  var first = new goog.ui.Tab('First');
    +  tabBar.addChild(first);
    +  assertEquals('First tab must have been added at the expected index', 0,
    +      tabBar.indexOfChild(first));
    +  first.setSelected(true);
    +  assertEquals('First tab must be selected', 0,
    +      tabBar.getSelectedTabIndex());
    +
    +  var second = new goog.ui.Tab('Second');
    +  tabBar.addChild(second);
    +  assertEquals('Second tab must have been added at the expected index', 1,
    +      tabBar.indexOfChild(second));
    +  assertEquals('First tab must remain selected', 0,
    +      tabBar.getSelectedTabIndex());
    +
    +  var firstRemoved = tabBar.removeChild(first);
    +  assertEquals('removeChild() must return the removed tab', first,
    +      firstRemoved);
    +  assertEquals('First tab must no longer be in the tab bar', -1,
    +      tabBar.indexOfChild(first));
    +  assertEquals('Second tab must be at the expected index', 0,
    +      tabBar.indexOfChild(second));
    +  assertFalse('First tab must no longer be selected', first.isSelected());
    +  assertTrue('Remaining tab must be selected', second.isSelected());
    +
    +  var secondRemoved = tabBar.removeChild(second);
    +  assertEquals('removeChild() must return the removed tab', second,
    +      secondRemoved);
    +  assertFalse('Tab must no longer be selected', second.isSelected());
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +}
    +
    +function testGetSetLocation() {
    +  assertEquals('Location must default to TOP', goog.ui.TabBar.Location.TOP,
    +      tabBar.getLocation());
    +  tabBar.setLocation(goog.ui.TabBar.Location.START);
    +  assertEquals('Location must have expected value',
    +      goog.ui.TabBar.Location.START, tabBar.getLocation());
    +  tabBar.createDom();
    +  assertThrows('Attempting to change the location after the tab bar has ' +
    +      'been rendered must throw error',
    +      function() {
    +        tabBar.setLocation(goog.ui.TabBar.Location.BOTTOM);
    +      });
    +}
    +
    +function testIsSetAutoSelectTabs() {
    +  assertTrue('Tab bar must auto-select tabs by default',
    +      tabBar.isAutoSelectTabs());
    +  tabBar.setAutoSelectTabs(false);
    +  assertFalse('Tab bar must no longer auto-select tabs by default',
    +      tabBar.isAutoSelectTabs());
    +  tabBar.render(sandbox);
    +  assertFalse('Rendering must not change auto-select setting',
    +      tabBar.isAutoSelectTabs());
    +  tabBar.setAutoSelectTabs(true);
    +  assertTrue('Tab bar must once again auto-select tabs',
    +      tabBar.isAutoSelectTabs());
    +}
    +
    +function setHighlightedIndexFromKeyEvent() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  // Verify baseline assumptions.
    +  assertNull('No tab must be highlighted', tabBar.getHighlighted());
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +  assertTrue('Tab bar must auto-select tabs on keyboard highlight',
    +      tabBar.isAutoSelectTabs());
    +
    +  // Highlight and selection must move together.
    +  tabBar.setHighlightedIndexFromKeyEvent(0);
    +  assertTrue('Foo must be highlighted', foo.isHighlighted());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +
    +  // Highlight and selection must move together.
    +  tabBar.setHighlightedIndexFromKeyEvent(1);
    +  assertFalse('Foo must no longer be highlighted', foo.isHighlighted());
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertTrue('Bar must be highlighted', bar.isHighlighted());
    +  assertTrue('Bar must be selected', bar.isSelected());
    +
    +  // Turn off auto-select-on-keyboard-highlight.
    +  tabBar.setAutoSelectTabs(false);
    +
    +  // Selection must not change; only highlight should move.
    +  tabBar.setHighlightedIndexFromKeyEvent(2);
    +  assertFalse('Bar must no longer be highlighted', bar.isHighlighted());
    +  assertTrue('Bar must remain selected', bar.isSelected());
    +  assertTrue('Baz must be highlighted', baz.isHighlighted());
    +  assertFalse('Baz must not be selected', baz.isSelected());
    +}
    +
    +function testGetSetSelectedTab() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +
    +  tabBar.setSelectedTab(baz);
    +  assertTrue('Baz must be selected', baz.isSelected());
    +  assertEquals('Baz must be the selected tab', baz,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.setSelectedTab(foo);
    +  assertFalse('Baz must no longer be selected', baz.isSelected());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +  assertEquals('Foo must be the selected tab', foo,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.setSelectedTab(foo);
    +  assertTrue('Foo must remain selected', foo.isSelected());
    +  assertEquals('Foo must remain the selected tab', foo,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.setSelectedTab(null);
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +}
    +
    +function testGetSetSelectedTabIndex() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChildAt(foo = new goog.ui.Tab('foo'), 0);
    +  tabBar.addChildAt(bar = new goog.ui.Tab('bar'), 1);
    +  tabBar.addChildAt(baz = new goog.ui.Tab('baz'), 2);
    +
    +  assertEquals('No tab must be selected', -1, tabBar.getSelectedTabIndex());
    +
    +  tabBar.setSelectedTabIndex(2);
    +  assertTrue('Baz must be selected', baz.isSelected());
    +  assertEquals('Baz must be the selected tab', 2,
    +      tabBar.getSelectedTabIndex());
    +
    +  tabBar.setSelectedTabIndex(0);
    +  assertFalse('Baz must no longer be selected', baz.isSelected());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +  assertEquals('Foo must be the selected tab', 0,
    +      tabBar.getSelectedTabIndex());
    +
    +  tabBar.setSelectedTabIndex(0);
    +  assertTrue('Foo must remain selected', foo.isSelected());
    +  assertEquals('Foo must remain the selected tab', 0,
    +      tabBar.getSelectedTabIndex());
    +
    +  tabBar.setSelectedTabIndex(-1);
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertEquals('No tab must be selected', -1, tabBar.getSelectedTabIndex());
    +}
    +
    +function testDeselectIfSelected() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  // Start with the middle tab selected.
    +  bar.setSelected(true);
    +  assertTrue('Bar must be selected', bar.isSelected());
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  // Should be a no-op.
    +  tabBar.deselectIfSelected(null);
    +  assertTrue('Bar must remain selected', bar.isSelected());
    +  assertEquals('Bar must remain the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  // Should be a no-op.
    +  tabBar.deselectIfSelected(foo);
    +  assertTrue('Bar must remain selected', bar.isSelected());
    +  assertEquals('Bar must remain the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect bar and select the previous tab (foo).
    +  tabBar.deselectIfSelected(bar);
    +  assertFalse('Bar must no longer be selected', bar.isSelected());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +  assertEquals('Foo must be the selected tab', foo,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect foo and select the next tab (bar).
    +  tabBar.deselectIfSelected(foo);
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertTrue('Bar must be selected', bar.isSelected());
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +}
    +
    +function testHandleTabSelect() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +
    +  tabBar.handleTabSelect(new goog.events.Event(
    +      goog.ui.Component.EventType.SELECT, bar));
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.handleTabSelect(new goog.events.Event(
    +      goog.ui.Component.EventType.SELECT, bar));
    +  assertEquals('Bar must remain selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.handleTabSelect(new goog.events.Event(
    +      goog.ui.Component.EventType.SELECT, foo));
    +  assertEquals('Foo must now be the selected tab', foo,
    +      tabBar.getSelectedTab());
    +}
    +
    +function testHandleTabUnselect() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  bar.setSelected(true);
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.handleTabUnselect(new goog.events.Event(
    +      goog.ui.Component.EventType.UNSELECT, foo));
    +  assertEquals('Bar must remain the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  tabBar.handleTabUnselect(new goog.events.Event(
    +      goog.ui.Component.EventType.SELECT, bar));
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +}
    +
    +function testHandleTabDisable() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  // Start with the middle tab selected.
    +  bar.setSelected(true);
    +  assertTrue('Bar must be selected', bar.isSelected());
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect bar and select the previous enabled, visible tab (foo).
    +  bar.setEnabled(false);
    +  assertFalse('Bar must no longer be selected', bar.isSelected());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +  assertEquals('Foo must be the selected tab', foo,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect foo and select the next enabled, visible tab (baz).
    +  foo.setEnabled(false);
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertTrue('Baz must be selected', baz.isSelected());
    +  assertEquals('Baz must be the selected tab', baz,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect baz.  Since there are no enabled, visible tabs left,
    +  // the tab bar should have no selected tab.
    +  baz.setEnabled(false);
    +  assertFalse('Baz must no longer be selected', baz.isSelected());
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +}
    +
    +function testHandleTabHide() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'));
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'));
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'));
    +
    +  // Start with the middle tab selected.
    +  bar.setSelected(true);
    +  assertTrue('Bar must be selected', bar.isSelected());
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect bar and select the previous enabled, visible tab (foo).
    +  bar.setVisible(false);
    +  assertFalse('Bar must no longer be selected', bar.isSelected());
    +  assertTrue('Foo must be selected', foo.isSelected());
    +  assertEquals('Foo must be the selected tab', foo,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect foo and select the next enabled, visible tab (baz).
    +  foo.setVisible(false);
    +  assertFalse('Foo must no longer be selected', foo.isSelected());
    +  assertTrue('Baz must be selected', baz.isSelected());
    +  assertEquals('Baz must be the selected tab', baz,
    +      tabBar.getSelectedTab());
    +
    +  // Should deselect baz.  Since there are no enabled, visible tabs left,
    +  // the tab bar should have no selected tab.
    +  baz.setVisible(false);
    +  assertFalse('Baz must no longer be selected', baz.isSelected());
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +}
    +
    +function testHandleFocus() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'), true);
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'), true);
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'), true);
    +
    +  // Render the tab bar into the document, so highlight handling works as
    +  // expected.
    +  tabBar.render(sandbox);
    +
    +  // Start with the middle tab selected.
    +  bar.setSelected(true);
    +  assertTrue('Bar must be selected', bar.isSelected());
    +  assertEquals('Bar must be the selected tab', bar,
    +      tabBar.getSelectedTab());
    +
    +  assertNull('No tab must be highlighted', tabBar.getHighlighted());
    +  tabBar.handleFocus(new goog.events.Event(goog.events.EventType.FOCUS,
    +      tabBar.getElement()));
    +  assertTrue('Bar must be highlighted', bar.isHighlighted());
    +  assertEquals('Bar must be the highlighted tab', bar,
    +      tabBar.getHighlighted());
    +}
    +
    +function testHandleFocusWithoutSelectedTab() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'), true);
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'), true);
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'), true);
    +
    +  // Render the tab bar into the document, so highlight handling works as
    +  // expected.
    +  tabBar.render(sandbox);
    +
    +  // Start with no tab selected.
    +  assertNull('No tab must be selected', tabBar.getSelectedTab());
    +
    +  assertNull('No tab must be highlighted', tabBar.getHighlighted());
    +  tabBar.handleFocus(new goog.events.Event(goog.events.EventType.FOCUS,
    +      tabBar.getElement()));
    +  assertTrue('Foo must be highlighted', foo.isHighlighted());
    +  assertEquals('Foo must be the highlighted tab', foo,
    +      tabBar.getHighlighted());
    +}
    +
    +function testGetOrientationFromLocation() {
    +  assertEquals(goog.ui.Container.Orientation.HORIZONTAL,
    +      goog.ui.TabBar.getOrientationFromLocation(
    +          goog.ui.TabBar.Location.TOP));
    +  assertEquals(goog.ui.Container.Orientation.HORIZONTAL,
    +      goog.ui.TabBar.getOrientationFromLocation(
    +          goog.ui.TabBar.Location.BOTTOM));
    +  assertEquals(goog.ui.Container.Orientation.VERTICAL,
    +      goog.ui.TabBar.getOrientationFromLocation(
    +          goog.ui.TabBar.Location.START));
    +  assertEquals(goog.ui.Container.Orientation.VERTICAL,
    +      goog.ui.TabBar.getOrientationFromLocation(
    +          goog.ui.TabBar.Location.END));
    +}
    +
    +function testKeyboardNavigation() {
    +  var foo, bar, baz;
    +
    +  // Create a tab bar with some tabs.
    +  tabBar.addChild(foo = new goog.ui.Tab('foo'), true);
    +  tabBar.addChild(bar = new goog.ui.Tab('bar'), true);
    +  tabBar.addChild(baz = new goog.ui.Tab('baz'), true);
    +  tabBar.render(sandbox);
    +
    +  // Highlight the selected tab (this happens automatically when the tab
    +  // bar receives keyboard focus).
    +  tabBar.setSelectedTabIndex(0);
    +  tabBar.getSelectedTab().setHighlighted(true);
    +
    +  // Count events dispatched by each tab.
    +  var eventCount = {
    +    'foo': {'select': 0, 'unselect': 0},
    +    'bar': {'select': 0, 'unselect': 0},
    +    'baz': {'select': 0, 'unselect': 0}
    +  };
    +
    +  function countEvent(e) {
    +    var tabId = e.target.getContent();
    +    var type = e.type;
    +    eventCount[tabId][type]++;
    +  }
    +
    +  function getEventCount(tabId, type) {
    +    return eventCount[tabId][type];
    +  }
    +
    +  // Listen for SELECT and UNSELECT events on the tab bar.
    +  goog.events.listen(tabBar, [
    +    goog.ui.Component.EventType.SELECT,
    +    goog.ui.Component.EventType.UNSELECT
    +  ], countEvent);
    +
    +  // Verify baseline assumptions.
    +  assertTrue('Tab bar must auto-select tabs',
    +      tabBar.isAutoSelectTabs());
    +  assertEquals('First tab must be selected', 0,
    +      tabBar.getSelectedTabIndex());
    +
    +  // Simulate a right arrow key event.
    +  var rightEvent = new FakeKeyEvent(goog.events.KeyCodes.RIGHT);
    +  assertTrue('Key event must have beeen handled',
    +      tabBar.handleKeyEvent(rightEvent));
    +  assertTrue('Key event propagation must have been stopped',
    +      rightEvent.propagationStopped);
    +  assertTrue('Default key event must have been prevented',
    +      rightEvent.defaultPrevented);
    +  assertEquals('Foo must have dispatched UNSELECT', 1,
    +      getEventCount('foo', goog.ui.Component.EventType.UNSELECT));
    +  assertEquals('Bar must have dispatched SELECT', 1,
    +      getEventCount('bar', goog.ui.Component.EventType.SELECT));
    +  assertEquals('Bar must have been selected', bar, tabBar.getSelectedTab());
    +
    +  // Simulate a left arrow key event.
    +  var leftEvent = new FakeKeyEvent(goog.events.KeyCodes.LEFT);
    +  assertTrue('Key event must have beeen handled',
    +      tabBar.handleKeyEvent(leftEvent));
    +  assertTrue('Key event propagation must have been stopped',
    +      leftEvent.propagationStopped);
    +  assertTrue('Default key event must have been prevented',
    +      leftEvent.defaultPrevented);
    +  assertEquals('Bar must have dispatched UNSELECT', 1,
    +      getEventCount('bar', goog.ui.Component.EventType.UNSELECT));
    +  assertEquals('Foo must have dispatched SELECT', 1,
    +      getEventCount('foo', goog.ui.Component.EventType.SELECT));
    +  assertEquals('Foo must have been selected', foo, tabBar.getSelectedTab());
    +
    +  // Disable tab auto-selection.
    +  tabBar.setAutoSelectTabs(false);
    +
    +  // Simulate another left arrow key event.
    +  var anotherLeftEvent = new FakeKeyEvent(goog.events.KeyCodes.LEFT);
    +  assertTrue('Key event must have beeen handled',
    +      tabBar.handleKeyEvent(anotherLeftEvent));
    +  assertTrue('Key event propagation must have been stopped',
    +      anotherLeftEvent.propagationStopped);
    +  assertTrue('Default key event must have been prevented',
    +      anotherLeftEvent.defaultPrevented);
    +  assertEquals('Foo must remain selected', foo, tabBar.getSelectedTab());
    +  assertEquals('Foo must not have dispatched another UNSELECT event', 1,
    +      getEventCount('foo', goog.ui.Component.EventType.UNSELECT));
    +  assertEquals('Baz must not have dispatched a SELECT event', 0,
    +      getEventCount('baz', goog.ui.Component.EventType.SELECT));
    +  assertFalse('Baz must not be selected', baz.isSelected());
    +  assertTrue('Baz must be highlighted', baz.isHighlighted());
    +
    +  // Simulate 'g' key event.
    +  var gEvent = new FakeKeyEvent(goog.events.KeyCodes.G);
    +  assertFalse('Key event must not have beeen handled',
    +      tabBar.handleKeyEvent(gEvent));
    +  assertFalse('Key event propagation must not have been stopped',
    +      gEvent.propagationStopped);
    +  assertFalse('Default key event must not have been prevented',
    +      gEvent.defaultPrevented);
    +  assertEquals('Foo must remain selected', foo, tabBar.getSelectedTab());
    +
    +  // Clean up.
    +  goog.events.unlisten(tabBar, [
    +    goog.ui.Component.EventType.SELECT,
    +    goog.ui.Component.EventType.UNSELECT
    +  ], countEvent);
    +}
    +
    +function testExitAndEnterDocument() {
    +  var component = new goog.ui.Component();
    +  component.render(sandbox);
    +
    +  var tab1 = new goog.ui.Tab('tab1');
    +  var tab2 = new goog.ui.Tab('tab2');
    +  var tab3 = new goog.ui.Tab('tab3');
    +  tabBar.addChild(tab1, true);
    +  tabBar.addChild(tab2, true);
    +  tabBar.addChild(tab3, true);
    +
    +  component.addChild(tabBar, true);
    +  tab2.setSelected(true);
    +  assertEquals(tabBar.getSelectedTab(), tab2);
    +
    +  component.removeChild(tabBar, true);
    +  tab1.setSelected(true);
    +  assertEquals(tabBar.getSelectedTab(), tab2);
    +
    +  component.addChild(tabBar, true);
    +  tab3.setSelected(true);
    +  assertEquals(tabBar.getSelectedTab(), tab3);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer.js
    new file mode 100644
    index 0000000000..2d93929827
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer.js
    @@ -0,0 +1,165 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default renderer for {@link goog.ui.TabBar}s.  Based on the
    + * original {@code TabPane} code.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @author eae@google.com (Emil A. Eklund)
    + */
    +
    +goog.provide('goog.ui.TabBarRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.object');
    +goog.require('goog.ui.ContainerRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.TabBar}s, based on the {@code TabPane}
    + * code.  The tab bar's DOM structure is determined by its orientation and
    + * location relative to tab contents.  For example, a horizontal tab bar
    + * located above tab contents looks like this:
    + * <pre>
    + *   <div class="goog-tab-bar goog-tab-bar-horizontal goog-tab-bar-top">
    + *     ...(tabs here)...
    + *   </div>
    + * </pre>
    + * @constructor
    + * @extends {goog.ui.ContainerRenderer}
    + */
    +goog.ui.TabBarRenderer = function() {
    +  goog.ui.ContainerRenderer.call(this, goog.a11y.aria.Role.TAB_LIST);
    +};
    +goog.inherits(goog.ui.TabBarRenderer, goog.ui.ContainerRenderer);
    +goog.addSingletonGetter(goog.ui.TabBarRenderer);
    +goog.tagUnsealableClass(goog.ui.TabBarRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.TabBarRenderer.CSS_CLASS = goog.getCssName('goog-tab-bar');
    +
    +
    +/**
    + * Returns the CSS class name to be applied to the root element of all tab bars
    + * rendered or decorated using this renderer.
    + * @return {string} Renderer-specific CSS class name.
    + * @override
    + */
    +goog.ui.TabBarRenderer.prototype.getCssClass = function() {
    +  return goog.ui.TabBarRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Sets the tab bar's state based on the given CSS class name, encountered
    + * during decoration.  Overrides the superclass implementation by recognizing
    + * class names representing tab bar orientation and location.
    + * @param {goog.ui.Container} tabBar Tab bar to configure.
    + * @param {string} className CSS class name.
    + * @param {string} baseClass Base class name used as the root of state-specific
    + *     class names (typically the renderer's own class name).
    + * @protected
    + * @override
    + */
    +goog.ui.TabBarRenderer.prototype.setStateFromClassName = function(tabBar,
    +    className, baseClass) {
    +  // Create the class-to-location lookup table on first access.
    +  if (!this.locationByClass_) {
    +    this.createLocationByClassMap_();
    +  }
    +
    +  // If the class name corresponds to a location, update the tab bar's location;
    +  // otherwise let the superclass handle it.
    +  var location = this.locationByClass_[className];
    +  if (location) {
    +    tabBar.setLocation(location);
    +  } else {
    +    goog.ui.TabBarRenderer.superClass_.setStateFromClassName.call(this, tabBar,
    +        className, baseClass);
    +  }
    +};
    +
    +
    +/**
    + * Returns all CSS class names applicable to the tab bar, based on its state.
    + * Overrides the superclass implementation by appending the location-specific
    + * class name to the list.
    + * @param {goog.ui.Container} tabBar Tab bar whose CSS classes are to be
    + *     returned.
    + * @return {!Array<string>} Array of CSS class names applicable to the tab bar.
    + * @override
    + */
    +goog.ui.TabBarRenderer.prototype.getClassNames = function(tabBar) {
    +  var classNames = goog.ui.TabBarRenderer.superClass_.getClassNames.call(this,
    +      tabBar);
    +
    +  // Create the location-to-class lookup table on first access.
    +  if (!this.classByLocation_) {
    +    this.createClassByLocationMap_();
    +  }
    +
    +  // Apped the class name corresponding to the tab bar's location to the list.
    +  classNames.push(this.classByLocation_[tabBar.getLocation()]);
    +  return classNames;
    +};
    +
    +
    +/**
    + * Creates the location-to-class lookup table.
    + * @private
    + */
    +goog.ui.TabBarRenderer.prototype.createClassByLocationMap_ = function() {
    +  var baseClass = this.getCssClass();
    +
    +  /**
    +   * Map of locations to location-specific structural class names,
    +   * precomputed and cached on first use to minimize object allocations
    +   * and string concatenation.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.classByLocation_ = goog.object.create(
    +      goog.ui.TabBar.Location.TOP, goog.getCssName(baseClass, 'top'),
    +      goog.ui.TabBar.Location.BOTTOM, goog.getCssName(baseClass, 'bottom'),
    +      goog.ui.TabBar.Location.START, goog.getCssName(baseClass, 'start'),
    +      goog.ui.TabBar.Location.END, goog.getCssName(baseClass, 'end'));
    +};
    +
    +
    +/**
    + * Creates the class-to-location lookup table, used during decoration.
    + * @private
    + */
    +goog.ui.TabBarRenderer.prototype.createLocationByClassMap_ = function() {
    +  // We need the classByLocation_ map so we can transpose it.
    +  if (!this.classByLocation_) {
    +    this.createClassByLocationMap_();
    +  }
    +
    +  /**
    +   * Map of location-specific structural class names to locations, used during
    +   * element decoration.  Precomputed and cached on first use to minimize object
    +   * allocations and string concatenation.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.locationByClass_ = goog.object.transpose(this.classByLocation_);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.html
    new file mode 100644
    index 0000000000..71d010d5c4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TabBarRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TabBarRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.js
    new file mode 100644
    index 0000000000..436b4c1e78
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabbarrenderer_test.js
    @@ -0,0 +1,132 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TabBarRendererTest');
    +goog.setTestOnly('goog.ui.TabBarRendererTest');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.TabBar');
    +goog.require('goog.ui.TabBarRenderer');
    +
    +var sandbox;
    +var renderer;
    +var tabBar;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  renderer = goog.ui.TabBarRenderer.getInstance();
    +  tabBar = new goog.ui.TabBar();
    +}
    +
    +function tearDown() {
    +  tabBar.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Renderer must not be null', renderer);
    +}
    +
    +function testGetCssClass() {
    +  assertEquals('getCssClass() must return expected value',
    +      goog.ui.TabBarRenderer.CSS_CLASS, renderer.getCssClass());
    +}
    +
    +function testGetAriaRole() {
    +  assertEquals('getAriaRole() must return expected value',
    +      goog.a11y.aria.Role.TAB_LIST, renderer.getAriaRole());
    +}
    +
    +function testCreateDom() {
    +  var element = renderer.createDom(tabBar);
    +  assertNotNull('Created element must not be null', element);
    +  assertEquals('Created element must be a DIV', 'DIV', element.tagName);
    +  assertSameElements('Created element must have expected class names',
    +      ['goog-tab-bar', 'goog-tab-bar-horizontal', 'goog-tab-bar-top'],
    +      goog.dom.classlist.get(element));
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML = '<div id="start" class="goog-tab-bar-start"></div>';
    +  var element = renderer.decorate(tabBar, goog.dom.getElement('start'));
    +  assertNotNull('Decorated element must not be null', element);
    +  assertEquals('Decorated element must be as expected',
    +      goog.dom.getElement('start'), element);
    +  // Due to a bug in ContainerRenderer, the "-vertical" class isn't applied.
    +  // TODO(attila): Fix this!
    +  assertSameElements('Decorated element must have expected class names',
    +      ['goog-tab-bar', 'goog-tab-bar-start'],
    +      goog.dom.classlist.get(element));
    +}
    +
    +function testSetStateFromClassName() {
    +  renderer.setStateFromClassName(tabBar, 'goog-tab-bar-bottom',
    +      renderer.getCssClass());
    +  assertEquals('Location must be BOTTOM', goog.ui.TabBar.Location.BOTTOM,
    +      tabBar.getLocation());
    +  assertEquals('Orientation must be HORIZONTAL',
    +      goog.ui.Container.Orientation.HORIZONTAL, tabBar.getOrientation());
    +
    +  renderer.setStateFromClassName(tabBar, 'goog-tab-bar-end',
    +      renderer.getCssClass());
    +  assertEquals('Location must be END', goog.ui.TabBar.Location.END,
    +      tabBar.getLocation());
    +  assertEquals('Orientation must be VERTICAL',
    +      goog.ui.Container.Orientation.VERTICAL, tabBar.getOrientation());
    +
    +  renderer.setStateFromClassName(tabBar, 'goog-tab-bar-top',
    +      renderer.getCssClass());
    +  assertEquals('Location must be TOP', goog.ui.TabBar.Location.TOP,
    +      tabBar.getLocation());
    +  assertEquals('Orientation must be HORIZONTAL',
    +      goog.ui.Container.Orientation.HORIZONTAL, tabBar.getOrientation());
    +
    +  renderer.setStateFromClassName(tabBar, 'goog-tab-bar-start',
    +      renderer.getCssClass());
    +  assertEquals('Location must be START', goog.ui.TabBar.Location.START,
    +      tabBar.getLocation());
    +  assertEquals('Orientation must be VERTICAL',
    +      goog.ui.Container.Orientation.VERTICAL, tabBar.getOrientation());
    +}
    +
    +function testGetClassNames() {
    +  assertSameElements('Class names for TOP location must be as expected',
    +      ['goog-tab-bar', 'goog-tab-bar-horizontal', 'goog-tab-bar-top'],
    +      renderer.getClassNames(tabBar));
    +
    +  tabBar.setLocation(goog.ui.TabBar.Location.START);
    +  assertSameElements('Class names for START location must be as expected',
    +      ['goog-tab-bar', 'goog-tab-bar-vertical', 'goog-tab-bar-start'],
    +      renderer.getClassNames(tabBar));
    +
    +  tabBar.setLocation(goog.ui.TabBar.Location.BOTTOM);
    +  assertSameElements('Class names for BOTTOM location must be as expected',
    +      ['goog-tab-bar', 'goog-tab-bar-horizontal', 'goog-tab-bar-bottom'],
    +      renderer.getClassNames(tabBar));
    +
    +  tabBar.setLocation(goog.ui.TabBar.Location.END);
    +  assertSameElements('Class names for END location must be as expected',
    +      ['goog-tab-bar', 'goog-tab-bar-vertical', 'goog-tab-bar-end'],
    +      renderer.getClassNames(tabBar));
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.TabBarRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tablesorter.js b/mapbox-gl/closure-library/closure/goog/ui/tablesorter.js
    new file mode 100644
    index 0000000000..0375050023
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tablesorter.js
    @@ -0,0 +1,324 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A table sorting decorator.
    + *
    + * @author robbyw@google.com (Robby Walker)
    + * @see ../demos/tablesorter.html
    + */
    +
    +goog.provide('goog.ui.TableSorter');
    +goog.provide('goog.ui.TableSorter.EventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * A table sorter allows for sorting of a table by column.  This component can
    + * be used to decorate an already existing TABLE element with sorting
    + * features.
    + *
    + * The TABLE should use a THEAD containing TH elements for the table column
    + * headers.
    + *
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.TableSorter = function(opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The current sort header of the table, or null if none.
    +   * @type {HTMLTableCellElement}
    +   * @private
    +   */
    +  this.header_ = null;
    +
    +  /**
    +   * Whether the last sort was in reverse.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.reversed_ = false;
    +
    +  /**
    +   * The default sorting function.
    +   * @type {function(*, *) : number}
    +   * @private
    +   */
    +  this.defaultSortFunction_ = goog.ui.TableSorter.numericSort;
    +
    +  /**
    +   * Array of custom sorting functions per colun.
    +   * @type {Array<function(*, *) : number>}
    +   * @private
    +   */
    +  this.sortFunctions_ = [];
    +};
    +goog.inherits(goog.ui.TableSorter, goog.ui.Component);
    +goog.tagUnsealableClass(goog.ui.TableSorter);
    +
    +
    +/**
    + * Row number (in <thead>) to use for sorting.
    + * @type {number}
    + * @private
    + */
    +goog.ui.TableSorter.prototype.sortableHeaderRowIndex_ = 0;
    +
    +
    +/**
    + * Sets the row index (in <thead>) to be used for sorting.
    + * By default, the first row (index 0) is used.
    + * Must be called before decorate() is called.
    + * @param {number} index The row index.
    + */
    +goog.ui.TableSorter.prototype.setSortableHeaderRowIndex = function(index) {
    +  if (this.isInDocument()) {
    +    throw Error(goog.ui.Component.Error.ALREADY_RENDERED);
    +  }
    +  this.sortableHeaderRowIndex_ = index;
    +};
    +
    +
    +/**
    + * Table sorter events.
    + * @enum {string}
    + */
    +goog.ui.TableSorter.EventType = {
    +  BEFORESORT: 'beforesort',
    +  SORT: 'sort'
    +};
    +
    +
    +/** @override */
    +goog.ui.TableSorter.prototype.canDecorate = function(element) {
    +  return element.tagName == goog.dom.TagName.TABLE;
    +};
    +
    +
    +/** @override */
    +goog.ui.TableSorter.prototype.enterDocument = function() {
    +  goog.ui.TableSorter.superClass_.enterDocument.call(this);
    +
    +  var table = this.getElement();
    +  var headerRow = table.tHead.rows[this.sortableHeaderRowIndex_];
    +
    +  this.getHandler().listen(headerRow, goog.events.EventType.CLICK, this.sort_);
    +};
    +
    +
    +/**
    + * @return {number} The current sort column of the table, or -1 if none.
    + */
    +goog.ui.TableSorter.prototype.getSortColumn = function() {
    +  return this.header_ ? this.header_.cellIndex : -1;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the last sort was in reverse.
    + */
    +goog.ui.TableSorter.prototype.isSortReversed = function() {
    +  return this.reversed_;
    +};
    +
    +
    +/**
    + * @return {function(*, *) : number} The default sort function to be used by
    + *     all columns.
    + */
    +goog.ui.TableSorter.prototype.getDefaultSortFunction = function() {
    +  return this.defaultSortFunction_;
    +};
    +
    +
    +/**
    + * Sets the default sort function to be used by all columns.  If not set
    + * explicitly, this defaults to numeric sorting.
    + * @param {function(*, *) : number} sortFunction The new default sort function.
    + */
    +goog.ui.TableSorter.prototype.setDefaultSortFunction = function(sortFunction) {
    +  this.defaultSortFunction_ = sortFunction;
    +};
    +
    +
    +/**
    + * Gets the sort function to be used by the given column.  Returns the default
    + * sort function if no sort function is explicitly set for this column.
    + * @param {number} column The column index.
    + * @return {function(*, *) : number} The sort function used by the column.
    + */
    +goog.ui.TableSorter.prototype.getSortFunction = function(column) {
    +  return this.sortFunctions_[column] || this.defaultSortFunction_;
    +};
    +
    +
    +/**
    + * Set the sort function for the given column, overriding the default sort
    + * function.
    + * @param {number} column The column index.
    + * @param {function(*, *) : number} sortFunction The new sort function.
    + */
    +goog.ui.TableSorter.prototype.setSortFunction = function(column, sortFunction) {
    +  this.sortFunctions_[column] = sortFunction;
    +};
    +
    +
    +/**
    + * Sort the table contents by the values in the given column.
    + * @param {goog.events.BrowserEvent} e The click event.
    + * @private
    + */
    +goog.ui.TableSorter.prototype.sort_ = function(e) {
    +  // Determine what column was clicked.
    +  // TODO(robbyw): If this table cell contains another table, this could break.
    +  var target = /** @type {Node} */ (e.target);
    +  var th = goog.dom.getAncestorByTagNameAndClass(target,
    +      goog.dom.TagName.TH);
    +
    +  // If the user clicks on the same column, sort it in reverse of what it is
    +  // now.  Otherwise, sort forward.
    +  var reverse = th == this.header_ ? !this.reversed_ : false;
    +
    +  // Perform the sort.
    +  if (this.dispatchEvent(goog.ui.TableSorter.EventType.BEFORESORT)) {
    +    if (this.sort(th.cellIndex, reverse)) {
    +      this.dispatchEvent(goog.ui.TableSorter.EventType.SORT);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sort the table contents by the values in the given column.
    + * @param {number} column The column to sort by.
    + * @param {boolean=} opt_reverse Whether to sort in reverse.
    + * @return {boolean} Whether the sort was executed.
    + */
    +goog.ui.TableSorter.prototype.sort = function(column, opt_reverse) {
    +  var sortFunction = this.getSortFunction(column);
    +  if (sortFunction === goog.ui.TableSorter.noSort) {
    +    return false;
    +  }
    +
    +  // Remove old header classes.
    +  if (this.header_) {
    +    goog.dom.classlist.remove(this.header_, this.reversed_ ?
    +        goog.getCssName('goog-tablesorter-sorted-reverse') :
    +        goog.getCssName('goog-tablesorter-sorted'));
    +  }
    +
    +  // If the user clicks on the same column, sort it in reverse of what it is
    +  // now.  Otherwise, sort forward.
    +  this.reversed_ = !!opt_reverse;
    +  var multiplier = this.reversed_ ? -1 : 1;
    +  var cmpFn = function(a, b) {
    +    return multiplier * sortFunction(a[0], b[0]) || a[1] - b[1];
    +  };
    +
    +  // Sort all tBodies
    +  var table = this.getElement();
    +  goog.array.forEach(table.tBodies, function(tBody) {
    +    // Collect all of the rows into an array.
    +    var values = goog.array.map(tBody.rows, function(row, rowIndex) {
    +      return [goog.dom.getTextContent(row.cells[column]), rowIndex, row];
    +    });
    +
    +    goog.array.sort(values, cmpFn);
    +
    +    // Remove the tBody temporarily since this speeds up the sort on some
    +    // browsers.
    +    var nextSibling = tBody.nextSibling;
    +    table.removeChild(tBody);
    +
    +    // Sort the rows, using the resulting array.
    +    goog.array.forEach(values, function(row) {
    +      tBody.appendChild(row[2]);
    +    });
    +
    +    // Reinstate the tBody.
    +    table.insertBefore(tBody, nextSibling);
    +  });
    +
    +  // Mark this as the last sorted column.
    +  this.header_ = table.tHead.rows[this.sortableHeaderRowIndex_].cells[column];
    +
    +  // Update the header class.
    +  goog.dom.classlist.add(this.header_, this.reversed_ ?
    +      goog.getCssName('goog-tablesorter-sorted-reverse') :
    +      goog.getCssName('goog-tablesorter-sorted'));
    +
    +  return true;
    +};
    +
    +
    +/**
    + * Disables sorting on the specified column
    + * @param {*} a First sort value.
    + * @param {*} b Second sort value.
    + * @return {number} Negative if a < b, 0 if a = b, and positive if a > b.
    + */
    +goog.ui.TableSorter.noSort = goog.functions.error('no sort');
    +
    +
    +/**
    + * A numeric sort function.  NaN values (or values that do not parse as float
    + * numbers) compare equal to each other and greater to any other number.
    + * @param {*} a First sort value.
    + * @param {*} b Second sort value.
    + * @return {number} Negative if a < b, 0 if a = b, and positive if a > b.
    + */
    +goog.ui.TableSorter.numericSort = function(a, b) {
    +  a = parseFloat(a);
    +  b = parseFloat(b);
    +  // foo == foo is false if and only if foo is NaN.
    +  if (a == a) {
    +    return b == b ? a - b : -1;
    +  } else {
    +    return b == b ? 1 : 0;
    +  }
    +};
    +
    +
    +/**
    + * Alphabetic sort function.
    + * @param {*} a First sort value.
    + * @param {*} b Second sort value.
    + * @return {number} Negative if a < b, 0 if a = b, and positive if a > b.
    + */
    +goog.ui.TableSorter.alphaSort = goog.array.defaultCompare;
    +
    +
    +/**
    + * Returns a function that is the given sort function in reverse.
    + * @param {function(*, *) : number} sortFunction The original sort function.
    + * @return {function(*, *) : number} A new sort function that reverses the
    + *     given sort function.
    + */
    +goog.ui.TableSorter.createReverseSort = function(sortFunction) {
    +  return function(a, b) {
    +    return -1 * sortFunction(a, b);
    +  };
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.html b/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.html
    new file mode 100644
    index 0000000000..14e9fd0030
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.html
    @@ -0,0 +1,80 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!-- Author:  robbyw@google.com (Robby Walker) -->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TableSorter
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TableSorterTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="content">
    +    <table id="sortable">
    +      <thead>
    +        <tr><th>alpha</th><th>number</th><th>not sortable</th></tr>
    +        <tr><th id="not-sortable" colspan="3">cannot sort</th></tr>
    +      </thead>
    +      <tbody>
    +        <tr><td>C</td><td>10</td><td></td></tr>
    +        <tr><td>A</td><td>10</td><td></td></tr>
    +        <tr><td>C</td><td>17</td><td></td></tr>
    +        <tr><td>B</td><td>0</td><td></td></tr>
    +        <tr><td>C</td><td>3</td><td></td></tr>
    +      </tbody>
    +    </table>
    +    <table id="sortable-2">
    +      <thead>
    +        <tr><th>not sortable 1</th><th colspan="2">not sortable 2</th></tr>
    +        <tr>
    +          <th id="sorttable-2-col-1">Col 1</th>
    +          <th id="sorttable-2-col-2">Col 2</th>
    +          <th id="sorttable-2-col-3">Col 3</th>
    +        </tr>
    +      </thead>
    +      <tbody>
    +        <tr><td>4</td><td>5</td><td>6</td></tr>
    +        <tr><td>1</td><td>2</td><td>3</td></tr>
    +        <tr><td>3</td><td>1</td><td>9</td></tr>
    +      </tbody>
    +    </table>
    +    <table id="sortable-3">
    +      <thead>
    +        <tr><th id="sortable-3-col">Sortable</th></tr>
    +      </thead>
    +      <tbody>
    +        <tr><td>B</td></tr>
    +        <tr><td>C</td></tr>
    +        <tr><td>A</td></tr>
    +      </tbody>
    +      <tbody>
    +        <tr><td>B</td></tr>
    +        <tr><td>A</td></tr>
    +        <tr><td>C</td></tr>
    +      </tbody>
    +    </table>
    +    <table id="sortable-4">
    +      <thead>
    +        <tr><th id="sortable-4-col">Sortable</th></tr>
    +      </thead>
    +      <tbody>
    +        <tr><td>11</td></tr>
    +        <tr><td>Bar</td></tr>
    +        <tr><td>3</td></tr>
    +        <tr><td>Foo</td></tr>
    +        <tr><td>2</td></tr>
    +      </tbody>
    +    </table>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.js b/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.js
    new file mode 100644
    index 0000000000..a2cb1b84a6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tablesorter_test.js
    @@ -0,0 +1,229 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TableSorterTest');
    +goog.setTestOnly('goog.ui.TableSorterTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.TableSorter');
    +
    +var oldHtml;
    +var alphaHeader, numberHeader, notSortableHeader, table, tableSorter;
    +
    +function setUpPage() {
    +  oldHtml = goog.dom.getElement('content').innerHTML;
    +}
    +
    +function setUp() {
    +  goog.dom.getElement('content').innerHTML = oldHtml;
    +  table = goog.dom.getElement('sortable');
    +  alphaHeader = table.getElementsByTagName('TH')[0];
    +  numberHeader = table.getElementsByTagName('TH')[1];
    +  notSortableHeader = table.getElementsByTagName('TH')[2];
    +
    +  tableSorter = new goog.ui.TableSorter();
    +  tableSorter.setSortFunction(0, goog.ui.TableSorter.alphaSort);
    +  tableSorter.setSortFunction(2, goog.ui.TableSorter.noSort);
    +  tableSorter.decorate(table);
    +}
    +
    +function tearDown() {
    +  tableSorter.dispose();
    +  table = null;
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Should have successful construction', tableSorter);
    +  assertNotNull('Should be in document', tableSorter);
    +}
    +
    +function testForwardAlpha() {
    +  goog.testing.events.fireClickEvent(alphaHeader);
    +  assertOrder(['A', '10', 'B', '0', 'C', '10', 'C', '17', 'C', '3']);
    +  assertTrue(goog.dom.classlist.contains(alphaHeader,
    +      'goog-tablesorter-sorted'));
    +  assertEquals(0, tableSorter.getSortColumn());
    +  assertFalse(tableSorter.isSortReversed());
    +}
    +
    +function testBackwardAlpha() {
    +  goog.testing.events.fireClickEvent(alphaHeader);
    +  goog.testing.events.fireClickEvent(alphaHeader);
    +  assertOrder(['C', '10', 'C', '17', 'C', '3', 'B', '0', 'A', '10']);
    +  assertFalse(goog.dom.classlist.contains(alphaHeader,
    +      'goog-tablesorter-sorted'));
    +  assertTrue(goog.dom.classlist.contains(alphaHeader,
    +      'goog-tablesorter-sorted-reverse'));
    +  assertEquals(0, tableSorter.getSortColumn());
    +  assertTrue(tableSorter.isSortReversed());
    +}
    +
    +function testForwardNumeric() {
    +  goog.testing.events.fireClickEvent(numberHeader);
    +  assertOrder(['B', '0', 'C', '3', 'C', '10', 'A', '10', 'C', '17']);
    +  assertTrue(goog.dom.classlist.contains(numberHeader,
    +      'goog-tablesorter-sorted'));
    +  assertEquals(1, tableSorter.getSortColumn());
    +  assertFalse(tableSorter.isSortReversed());
    +}
    +
    +function testBackwardNumeric() {
    +  goog.testing.events.fireClickEvent(numberHeader);
    +  goog.testing.events.fireClickEvent(numberHeader);
    +  assertOrder(['C', '17', 'C', '10', 'A', '10', 'C', '3', 'B', '0']);
    +  assertTrue(goog.dom.classlist.contains(numberHeader,
    +      'goog-tablesorter-sorted-reverse'));
    +  assertEquals(1, tableSorter.getSortColumn());
    +  assertTrue(tableSorter.isSortReversed());
    +}
    +
    +function testAlphaThenNumeric() {
    +  testForwardAlpha();
    +  goog.testing.events.fireClickEvent(numberHeader);
    +  assertOrder(['B', '0', 'C', '3', 'A', '10', 'C', '10', 'C', '17']);
    +  assertFalse(goog.dom.classlist.contains(alphaHeader,
    +      'goog-tablesorter-sorted'));
    +  assertEquals(1, tableSorter.getSortColumn());
    +  assertFalse(tableSorter.isSortReversed());
    +}
    +
    +function testNotSortableUnchanged() {
    +  goog.testing.events.fireClickEvent(notSortableHeader);
    +  assertEquals(0, goog.dom.classlist.get(notSortableHeader).length);
    +  assertEquals(-1, tableSorter.getSortColumn());
    +}
    +
    +function testSortWithNonDefaultSortableHeaderRowIndex() {
    +  // Check that clicking on non-sortable header doesn't trigger any sorting.
    +  assertOrder(['C', '10', 'A', '10', 'C', '17', 'B', '0', 'C', '3']);
    +  goog.testing.events.fireClickEvent(goog.dom.getElement('not-sortable'));
    +  assertOrder(['C', '10', 'A', '10', 'C', '17', 'B', '0', 'C', '3']);
    +}
    +
    +function testsetSortableHeaderRowIndexAfterDecorateThrows() {
    +  var func = function() { tableSorter.setSortableHeaderRowIndex(0); };
    +  var msg = assertThrows('failFunc should throw.', func)['message'];
    +  assertEquals('Component already rendered', msg);
    +}
    +
    +function testSortOnSecondHeaderRow() {
    +  // Test a table with multiple table headers.
    +  // Using setSortableHeaderRowIndex one can specify table header columns to use
    +  // in sorting.
    +  var tableSorter2 = new goog.ui.TableSorter();
    +  tableSorter2.setSortableHeaderRowIndex(1);
    +  tableSorter2.decorate(goog.dom.getElement('sortable-2'));
    +
    +  // Initial order.
    +  assertOrder(['4', '5', '6', '1', '2', '3', '3', '1', '9'],
    +              goog.dom.getElement('sortable-2'));
    +
    +  // Sort on first column.
    +  goog.testing.events.fireClickEvent(
    +      goog.dom.getElement('sorttable-2-col-1'));
    +  assertOrder(['1', '2', '3', '3', '1', '9', '4', '5', '6'],
    +              goog.dom.getElement('sortable-2'));
    +
    +  // Sort on second column.
    +  goog.testing.events.fireClickEvent(
    +      goog.dom.getElement('sorttable-2-col-2'));
    +  assertOrder(['3', '1', '9', '1', '2', '3', '4', '5', '6'],
    +              goog.dom.getElement('sortable-2'));
    +
    +  // Sort on third column.
    +  goog.testing.events.fireClickEvent(
    +      goog.dom.getElement('sorttable-2-col-3'));
    +  assertOrder(['1', '2', '3', '4', '5', '6', '3', '1', '9'],
    +              goog.dom.getElement('sortable-2'));
    +
    +  // Reverse sort on third column.
    +  goog.testing.events.fireClickEvent(
    +      goog.dom.getElement('sorttable-2-col-3'));
    +  assertOrder(['3', '1', '9', '4', '5', '6', '1', '2', '3'],
    +              goog.dom.getElement('sortable-2'));
    +
    +  tableSorter2.dispose();
    +}
    +
    +function testSortAfterSwapping() {
    +  // First click
    +  goog.testing.events.fireClickEvent(alphaHeader);
    +  assertOrder(['A', '10', 'B', '0', 'C', '10', 'C', '17', 'C', '3']);
    +  assertEquals(0, tableSorter.getSortColumn());
    +
    +  // Move first column to the end
    +  for (var i = 0, r; (r = table.rows[i]); i++) {
    +    var cell = r.cells[0];
    +    cell.parentNode.appendChild(cell);
    +  }
    +  // Make sure the above worked as expected
    +  assertOrder(['10', 'A', '0', 'B', '10', 'C', '17', 'C', '3', 'C']);
    +
    +  // Our column is now the second one
    +  assertEquals(2, tableSorter.getSortColumn());
    +
    +  // Second click, should reverse
    +  tableSorter.setSortFunction(2, goog.ui.TableSorter.alphaSort);
    +  goog.testing.events.fireClickEvent(alphaHeader);
    +  assertOrder(['10', 'C', '17', 'C', '3', 'C', '0', 'B', '10', 'A']);
    +}
    +
    +function testTwoBodies() {
    +  var table3 = goog.dom.getElement('sortable-3');
    +  var header = goog.dom.getElement('sortable-3-col');
    +  var sorter3 = new goog.ui.TableSorter();
    +  sorter3.setSortFunction(0, goog.ui.TableSorter.alphaSort);
    +  try {
    +    sorter3.decorate(table3);
    +    goog.testing.events.fireClickEvent(header);
    +    assertOrder(['A', 'B', 'C', 'A', 'B', 'C'], table3);
    +    goog.testing.events.fireClickEvent(header);
    +    assertOrder(['C', 'B', 'A', 'C', 'B', 'A'], table3);
    +  } finally {
    +    sorter3.dispose();
    +  }
    +}
    +
    +function testNaNs() {
    +  var table = goog.dom.getElement('sortable-4');
    +  var header = goog.dom.getElement('sortable-4-col');
    +  var sorter = new goog.ui.TableSorter();
    +  try {
    +    // All non-numbers compare equal, i.e. Bar == Foo, so order of those
    +    // elements should not change (since we are using stable sort).
    +    sorter.decorate(table);
    +    goog.testing.events.fireClickEvent(header);
    +    assertOrder(['2', '3', '11', 'Bar', 'Foo'], table);
    +    goog.testing.events.fireClickEvent(header);
    +    assertOrder(['Bar', 'Foo', '11', '3', '2'], table);
    +  } finally {
    +    sorter.dispose();
    +  }
    +}
    +
    +function assertOrder(arr, opt_table) {
    +  var tbl = opt_table || table;
    +  var actual = [];
    +  goog.array.forEach(tbl.getElementsByTagName('TD'), function(td, idx) {
    +    var txt = goog.dom.getTextContent(td);
    +    if (txt) {
    +      actual.push(txt);
    +    }
    +  });
    +  assertArrayEquals(arr, actual);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabpane.js b/mapbox-gl/closure-library/closure/goog/ui/tabpane.js
    new file mode 100644
    index 0000000000..9847774c24
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabpane.js
    @@ -0,0 +1,680 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview TabPane widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.TabPane');
    +goog.provide('goog.ui.TabPane.Events');
    +goog.provide('goog.ui.TabPane.TabLocation');
    +goog.provide('goog.ui.TabPane.TabPage');
    +goog.provide('goog.ui.TabPaneEvent');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * TabPane widget. All children already inside the tab pane container element
    + * will be be converted to tabs. Each tab is represented by a goog.ui.TabPane.
    + * TabPage object. Further pages can be constructed either from an existing
    + * container or created from scratch.
    + *
    + * @param {Element} el Container element to create the tab pane out of.
    + * @param {goog.ui.TabPane.TabLocation=} opt_tabLocation Location of the tabs
    + *     in relation to the content container. Default is top.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @param {boolean=} opt_useMouseDown Whether to use MOUSEDOWN instead of CLICK
    + *     for tab changes.
    + * @extends {goog.events.EventTarget}
    + * @constructor
    + * @see ../demos/tabpane.html
    + * @deprecated Use goog.ui.TabBar instead.
    + */
    +goog.ui.TabPane = function(el, opt_tabLocation, opt_domHelper,
    +                           opt_useMouseDown) {
    +  goog.events.EventTarget.call(this);
    +
    +  /**
    +   * DomHelper used to interact with the document, allowing components to be
    +   * created in a different window.  This property is considered protected;
    +   * subclasses of Component may refer to it directly.
    +   * @type {goog.dom.DomHelper}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Tab pane element.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.el_ = el;
    +
    +  /**
    +   * Collection of tab panes.
    +   * @type {Array<goog.ui.TabPane.TabPage>}
    +   * @private
    +   */
    +  this.pages_ = [];
    +
    +  /**
    +   * Location of the tabs with respect to the content box.
    +   * @type {goog.ui.TabPane.TabLocation}
    +   * @private
    +   */
    +  this.tabLocation_ =
    +      opt_tabLocation ? opt_tabLocation : goog.ui.TabPane.TabLocation.TOP;
    +
    +  /**
    +   * Whether to use MOUSEDOWN instead of CLICK for tab change events. This
    +   * fixes some focus problems on Safari/Chrome.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.useMouseDown_ = !!opt_useMouseDown;
    +
    +  this.create_();
    +};
    +goog.inherits(goog.ui.TabPane, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.TabPane);
    +
    +
    +/**
    + * Element containing the tab buttons.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.TabPane.prototype.elButtonBar_;
    +
    +
    +/**
    + * Element containing the tab pages.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.TabPane.prototype.elContent_;
    +
    +
    +/**
    + * Selected page.
    + * @type {goog.ui.TabPane.TabPage?}
    + * @private
    + */
    +goog.ui.TabPane.prototype.selected_;
    +
    +
    +/**
    + * Constants for event names
    + *
    + * @const
    + */
    +goog.ui.TabPane.Events = {
    +  CHANGE: 'change'
    +};
    +
    +
    +/**
    + * Enum for representing the location of the tabs in relation to the content.
    + *
    + * @enum {number}
    + */
    +goog.ui.TabPane.TabLocation = {
    +  TOP: 0,
    +  BOTTOM: 1,
    +  LEFT: 2,
    +  RIGHT: 3
    +};
    +
    +
    +/**
    + * Creates HTML nodes for tab pane.
    + *
    + * @private
    + */
    +goog.ui.TabPane.prototype.create_ = function() {
    +  this.el_.className = goog.getCssName('goog-tabpane');
    +
    +  var nodes = this.getChildNodes_();
    +
    +  // Create tab strip
    +  this.elButtonBar_ = this.dom_.createDom('ul',
    +      {'className': goog.getCssName('goog-tabpane-tabs'), 'tabIndex': '0'});
    +
    +  // Create content area
    +  this.elContent_ =
    +      this.dom_.createDom('div', goog.getCssName('goog-tabpane-cont'));
    +  this.el_.appendChild(this.elContent_);
    +
    +  var element = goog.asserts.assertElement(this.el_);
    +
    +  switch (this.tabLocation_) {
    +    case goog.ui.TabPane.TabLocation.TOP:
    +      element.insertBefore(this.elButtonBar_, this.elContent_);
    +      element.insertBefore(this.createClear_(), this.elContent_);
    +      goog.dom.classlist.add(element, goog.getCssName('goog-tabpane-top'));
    +      break;
    +    case goog.ui.TabPane.TabLocation.BOTTOM:
    +      element.appendChild(this.elButtonBar_);
    +      element.appendChild(this.createClear_());
    +      goog.dom.classlist.add(element, goog.getCssName('goog-tabpane-bottom'));
    +      break;
    +    case goog.ui.TabPane.TabLocation.LEFT:
    +      element.insertBefore(this.elButtonBar_, this.elContent_);
    +      goog.dom.classlist.add(element, goog.getCssName('goog-tabpane-left'));
    +      break;
    +    case goog.ui.TabPane.TabLocation.RIGHT:
    +      element.insertBefore(this.elButtonBar_, this.elContent_);
    +      goog.dom.classlist.add(element, goog.getCssName('goog-tabpane-right'));
    +      break;
    +    default:
    +      throw Error('Invalid tab location');
    +  }
    +
    +  // Listen for click and keydown events on header
    +  this.elButtonBar_.tabIndex = 0;
    +  goog.events.listen(this.elButtonBar_,
    +      this.useMouseDown_ ?
    +      goog.events.EventType.MOUSEDOWN :
    +      goog.events.EventType.CLICK,
    +      this.onHeaderClick_, false, this);
    +  goog.events.listen(this.elButtonBar_, goog.events.EventType.KEYDOWN,
    +      this.onHeaderKeyDown_, false, this);
    +
    +  this.createPages_(nodes);
    +};
    +
    +
    +/**
    + * Creates the HTML node for the clearing div, and associated style in
    + * the <HEAD>.
    + *
    + * @return {!Element} Reference to a DOM div node.
    + * @private
    + */
    +goog.ui.TabPane.prototype.createClear_ = function() {
    +  var clearFloatStyle = '.' + goog.getCssName('goog-tabpane-clear') +
    +      ' { clear: both; height: 0px; overflow: hidden }';
    +  goog.style.installStyles(clearFloatStyle);
    +  return this.dom_.createDom('div', goog.getCssName('goog-tabpane-clear'));
    +};
    +
    +
    +/** @override */
    +goog.ui.TabPane.prototype.disposeInternal = function() {
    +  goog.ui.TabPane.superClass_.disposeInternal.call(this);
    +  goog.events.unlisten(this.elButtonBar_,
    +      this.useMouseDown_ ?
    +      goog.events.EventType.MOUSEDOWN :
    +      goog.events.EventType.CLICK,
    +      this.onHeaderClick_, false, this);
    +  goog.events.unlisten(this.elButtonBar_, goog.events.EventType.KEYDOWN,
    +      this.onHeaderKeyDown_, false, this);
    +  delete this.el_;
    +  this.elButtonBar_ = null;
    +  this.elContent_ = null;
    +};
    +
    +
    +/**
    + * @return {!Array<Element>} The element child nodes of tab pane container.
    + * @private
    + */
    +goog.ui.TabPane.prototype.getChildNodes_ = function() {
    +  var nodes = [];
    +
    +  var child = goog.dom.getFirstElementChild(this.el_);
    +  while (child) {
    +    nodes.push(child);
    +    child = goog.dom.getNextElementSibling(child);
    +  }
    +
    +  return nodes;
    +};
    +
    +
    +/**
    + * Creates pages out of a collection of elements.
    + *
    + * @param {Array<Element>} nodes Array of elements to create pages out of.
    + * @private
    + */
    +goog.ui.TabPane.prototype.createPages_ = function(nodes) {
    +  for (var node, i = 0; node = nodes[i]; i++) {
    +    this.addPage(new goog.ui.TabPane.TabPage(node));
    +  }
    +};
    +
    +
    +/**
    + * Adds a page to the tab pane.
    + *
    + * @param {goog.ui.TabPane.TabPage} page Tab page to add.
    + * @param {number=} opt_index Zero based index to insert tab at. Inserted at the
    + *                           end if not specified.
    + */
    +goog.ui.TabPane.prototype.addPage = function(page, opt_index) {
    +  // If page is already in another tab pane it's removed from that one before it
    +  // can be added to this one.
    +  if (page.parent_ && page.parent_ != this &&
    +      page.parent_ instanceof goog.ui.TabPane) {
    +    page.parent_.removePage(page);
    +  }
    +
    +  // Insert page at specified position
    +  var index = this.pages_.length;
    +  if (goog.isDef(opt_index) && opt_index != index) {
    +    index = opt_index;
    +    this.pages_.splice(index, 0, page);
    +    this.elButtonBar_.insertBefore(page.elTitle_,
    +                                   this.elButtonBar_.childNodes[index]);
    +  }
    +
    +  // Append page to end
    +  else {
    +    this.pages_.push(page);
    +    this.elButtonBar_.appendChild(page.elTitle_);
    +  }
    +
    +  page.setParent_(this, index);
    +
    +  // Select first page and fire change event
    +  if (!this.selected_) {
    +    this.selected_ = page;
    +    this.dispatchEvent(new goog.ui.TabPaneEvent(goog.ui.TabPane.Events.CHANGE,
    +                                                this, this.selected_));
    +  }
    +
    +  // Move page content to the tab pane and update visibility.
    +  this.elContent_.appendChild(page.elContent_);
    +  page.setVisible_(page == this.selected_);
    +
    +  // Update index for following pages
    +  for (var pg, i = index + 1; pg = this.pages_[i]; i++) {
    +    pg.index_ = i;
    +  }
    +};
    +
    +
    +/**
    + * Removes the specified page from the tab pane.
    + *
    + * @param {goog.ui.TabPane.TabPage|number} page Reference to tab page or zero
    + *     based index.
    + */
    +goog.ui.TabPane.prototype.removePage = function(page) {
    +  if (goog.isNumber(page)) {
    +    page = this.pages_[page];
    +  }
    +  this.pages_.splice(page.index_, 1);
    +  page.setParent_(null);
    +
    +  goog.dom.removeNode(page.elTitle_);
    +  goog.dom.removeNode(page.elContent_);
    +
    +  for (var pg, i = 0; pg = this.pages_[i]; i++) {
    +    pg.setParent_(this, i);
    +  }
    +};
    +
    +
    +/**
    + * Gets the tab page by zero based index.
    + *
    + * @param {number} index Index of page to return.
    + * @return {goog.ui.TabPane.TabPage?} page The tab page.
    + */
    +goog.ui.TabPane.prototype.getPage = function(index) {
    +  return this.pages_[index];
    +};
    +
    +
    +/**
    + * Sets the selected tab page by object reference.
    + *
    + * @param {goog.ui.TabPane.TabPage} page Tab page to select.
    + */
    +goog.ui.TabPane.prototype.setSelectedPage = function(page) {
    +  if (page.isEnabled() &&
    +      (!this.selected_ || page != this.selected_)) {
    +    this.selected_.setVisible_(false);
    +    page.setVisible_(true);
    +    this.selected_ = page;
    +
    +    // Fire changed event
    +    this.dispatchEvent(new goog.ui.TabPaneEvent(goog.ui.TabPane.Events.CHANGE,
    +                                                this, this.selected_));
    +  }
    +};
    +
    +
    +/**
    + * Sets the selected tab page by zero based index.
    + *
    + * @param {number} index Index of page to select.
    + */
    +goog.ui.TabPane.prototype.setSelectedIndex = function(index) {
    +  if (index >= 0 && index < this.pages_.length) {
    +    this.setSelectedPage(this.pages_[index]);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The index for the selected tab page or -1 if no page is
    + *     selected.
    + */
    +goog.ui.TabPane.prototype.getSelectedIndex = function() {
    +  return this.selected_ ? /** @type {number} */ (this.selected_.index_) : -1;
    +};
    +
    +
    +/**
    + * @return {goog.ui.TabPane.TabPage?} The selected tab page.
    + */
    +goog.ui.TabPane.prototype.getSelectedPage = function() {
    +  return this.selected_ || null;
    +};
    +
    +
    +/**
    + * @return {Element} The element that contains the tab pages.
    + */
    +goog.ui.TabPane.prototype.getContentElement = function() {
    +  return this.elContent_ || null;
    +};
    +
    +
    +/**
    + * @return {Element} The main element for the tabpane.
    + */
    +goog.ui.TabPane.prototype.getElement = function() {
    +  return this.el_ || null;
    +};
    +
    +
    +/**
    + * Click event handler for header element, handles clicks on tabs.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.TabPane.prototype.onHeaderClick_ = function(event) {
    +  var el = event.target;
    +
    +  // Determine index if a tab (li element) was clicked.
    +  while (el != this.elButtonBar_) {
    +    if (el.tagName == 'LI') {
    +      var i;
    +      // {} prevents compiler warning
    +      for (i = 0; el = el.previousSibling; i++) {}
    +      this.setSelectedIndex(i);
    +      break;
    +    }
    +    el = el.parentNode;
    +  }
    +  event.preventDefault();
    +};
    +
    +
    +/**
    + * KeyDown event handler for header element. Arrow keys moves between pages.
    + * Home and end selects the first/last page.
    + *
    + * @param {goog.events.BrowserEvent} event KeyDown event.
    + * @private
    + */
    +goog.ui.TabPane.prototype.onHeaderKeyDown_ = function(event) {
    +  if (event.altKey || event.metaKey || event.ctrlKey) {
    +    return;
    +  }
    +
    +  switch (event.keyCode) {
    +    case goog.events.KeyCodes.LEFT:
    +      var index = this.selected_.getIndex() - 1;
    +      this.setSelectedIndex(index < 0 ? this.pages_.length - 1 : index);
    +      break;
    +    case goog.events.KeyCodes.RIGHT:
    +      var index = this.selected_.getIndex() + 1;
    +      this.setSelectedIndex(index >= this.pages_.length ? 0 : index);
    +      break;
    +    case goog.events.KeyCodes.HOME:
    +      this.setSelectedIndex(0);
    +      break;
    +    case goog.events.KeyCodes.END:
    +      this.setSelectedIndex(this.pages_.length - 1);
    +      break;
    +  }
    +};
    +
    +
    +
    +/**
    + * Object representing an individual tab pane.
    + *
    + * @param {Element=} opt_el Container element to create the pane out of.
    + * @param {(Element|string)=} opt_title Pane title or element to use as the
    + *     title. If not specified the first element in the container is used as
    + *     the title.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper
    + * The first parameter can be omitted.
    + * @constructor
    + */
    +goog.ui.TabPane.TabPage = function(opt_el, opt_title, opt_domHelper) {
    +  var title, el;
    +  if (goog.isString(opt_el) && !goog.isDef(opt_title)) {
    +    title = opt_el;
    +  } else if (opt_title) {
    +    title = opt_title;
    +    el = opt_el;
    +  } else if (opt_el) {
    +    var child = goog.dom.getFirstElementChild(opt_el);
    +    if (child) {
    +      title = goog.dom.getTextContent(child);
    +      child.parentNode.removeChild(child);
    +    }
    +    el = opt_el;
    +  }
    +
    +  /**
    +   * DomHelper used to interact with the document, allowing components to be
    +   * created in a different window.  This property is considered protected;
    +   * subclasses of Component may refer to it directly.
    +   * @type {goog.dom.DomHelper}
    +   * @protected
    +   * @suppress {underscore|visibility}
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Content element
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elContent_ = el || this.dom_.createDom('div');
    +
    +  /**
    +   * Title element
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elTitle_ = this.dom_.createDom('li', null, title);
    +
    +  /**
    +   * Parent TabPane reference.
    +   * @type {goog.ui.TabPane?}
    +   * @private
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * Index for page in tab pane.
    +   * @type {?number}
    +   * @private
    +   */
    +  this.index_ = null;
    +
    +  /**
    +   * Flags if this page is enabled and can be selected.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.enabled_ = true;
    +};
    +
    +
    +/**
    + * @return {string} The title for tab page.
    + */
    +goog.ui.TabPane.TabPage.prototype.getTitle = function() {
    +  return goog.dom.getTextContent(this.elTitle_);
    +};
    +
    +
    +/**
    + * Sets title for tab page.
    + *
    + * @param {string} title Title for tab page.
    + */
    +goog.ui.TabPane.TabPage.prototype.setTitle = function(title) {
    +  goog.dom.setTextContent(this.elTitle_, title);
    +};
    +
    +
    +/**
    + * @return {Element} The title element.
    + */
    +goog.ui.TabPane.TabPage.prototype.getTitleElement = function() {
    +  return this.elTitle_;
    +};
    +
    +
    +/**
    + * @return {Element} The content element.
    + */
    +goog.ui.TabPane.TabPage.prototype.getContentElement = function() {
    +  return this.elContent_;
    +};
    +
    +
    +/**
    + * @return {?number} The index of page in tab pane.
    + */
    +goog.ui.TabPane.TabPage.prototype.getIndex = function() {
    +  return this.index_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.TabPane?} The parent tab pane for page.
    + */
    +goog.ui.TabPane.TabPage.prototype.getParent = function() {
    +  return this.parent_;
    +};
    +
    +
    +/**
    + * Selects page in the associated tab pane.
    + */
    +goog.ui.TabPane.TabPage.prototype.select = function() {
    +  if (this.parent_) {
    +    this.parent_.setSelectedPage(this);
    +  }
    +};
    +
    +
    +/**
    + * Sets the enabled state.
    + *
    + * @param {boolean} enabled Enabled state.
    + */
    +goog.ui.TabPane.TabPage.prototype.setEnabled = function(enabled) {
    +  this.enabled_ = enabled;
    +  this.elTitle_.className = enabled ?
    +      goog.getCssName('goog-tabpane-tab') :
    +      goog.getCssName('goog-tabpane-tab-disabled');
    +};
    +
    +
    +/**
    + * Returns if the page is enabled.
    + * @return {boolean} Whether the page is enabled or not.
    + */
    +goog.ui.TabPane.TabPage.prototype.isEnabled = function() {
    +  return this.enabled_;
    +};
    +
    +
    +/**
    + * Sets visible state for page content and updates style of tab.
    + *
    + * @param {boolean} visible Visible state.
    + * @private
    + */
    +goog.ui.TabPane.TabPage.prototype.setVisible_ = function(visible) {
    +  if (this.isEnabled()) {
    +    this.elContent_.style.display = visible ? '' : 'none';
    +    this.elTitle_.className = visible ?
    +        goog.getCssName('goog-tabpane-tab-selected') :
    +        goog.getCssName('goog-tabpane-tab');
    +  }
    +};
    +
    +
    +/**
    + * Sets parent tab pane for tab page.
    + *
    + * @param {goog.ui.TabPane?} tabPane Tab strip object.
    + * @param {number=} opt_index Index of page in pane.
    + * @private
    + */
    +goog.ui.TabPane.TabPage.prototype.setParent_ = function(tabPane, opt_index) {
    +  this.parent_ = tabPane;
    +  this.index_ = goog.isDef(opt_index) ? opt_index : null;
    +};
    +
    +
    +
    +/**
    + * Object representing a tab pane page changed event.
    + *
    + * @param {string} type Event type.
    + * @param {goog.ui.TabPane} target Tab widget initiating event.
    + * @param {goog.ui.TabPane.TabPage} page Selected page in tab pane.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.ui.TabPaneEvent = function(type, target, page) {
    +  goog.events.Event.call(this, type, target);
    +
    +  /**
    +   * The selected page.
    +   * @type {goog.ui.TabPane.TabPage}
    +   */
    +  this.page = page;
    +};
    +goog.inherits(goog.ui.TabPaneEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.html b/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.html
    new file mode 100644
    index 0000000000..e31edd5ba7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.html
    @@ -0,0 +1,24 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TabPane
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TabPaneTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="testBody">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.js b/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.js
    new file mode 100644
    index 0000000000..0023479764
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabpane_test.js
    @@ -0,0 +1,100 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TabPaneTest');
    +goog.setTestOnly('goog.ui.TabPaneTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.TabPane');
    +
    +var tabPane;
    +var page1;
    +var page2;
    +var page3;
    +
    +function setUp() {
    +  goog.dom.getElement('testBody').innerHTML =
    +      '<div id="tabpane"></div>' +
    +      '<div id="page1Content">' +
    +      '  Content for page 1' +
    +      '</div>' +
    +      '<div id="page2Content">' +
    +      '  Content for page 2' +
    +      '</div>' +
    +      '<div id="page3Content">' +
    +      '  Content for page 3' +
    +      '</div>';
    +
    +  tabPane = new goog.ui.TabPane(goog.dom.getElement('tabpane'));
    +  page1 = new goog.ui.TabPane.TabPage(goog.dom.getElement('page1Content'),
    +      'page1');
    +  page2 = new goog.ui.TabPane.TabPage(goog.dom.getElement('page2Content'),
    +      'page2');
    +  page3 = new goog.ui.TabPane.TabPage(goog.dom.getElement('page3Content'),
    +      'page3');
    +
    +  tabPane.addPage(page1);
    +  tabPane.addPage(page2);
    +  tabPane.addPage(page3);
    +}
    +
    +function tearDown() {
    +  tabPane.dispose();
    +}
    +
    +function testAllPagesEnabledAndSelectable() {
    +  tabPane.setSelectedIndex(0);
    +  var selected = tabPane.getSelectedPage();
    +  assertEquals('page1 should be selected', 'page1', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +
    +  tabPane.setSelectedIndex(1);
    +  selected = tabPane.getSelectedPage();
    +  assertEquals('page2 should be selected', 'page2', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +
    +  tabPane.setSelectedIndex(2);
    +  selected = tabPane.getSelectedPage();
    +  assertEquals('page3 should be selected', 'page3', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +}
    +
    +function testDisabledPageIsNotSelectable() {
    +  page2.setEnabled(false);
    +  assertEquals('goog-tabpane-tab-disabled',
    +               page2.getTitleElement().className);
    +
    +  tabPane.setSelectedIndex(0);
    +  var selected = tabPane.getSelectedPage();
    +  assertEquals('page1 should be selected', 'page1', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +
    +  tabPane.setSelectedIndex(1);
    +  selected = tabPane.getSelectedPage();
    +  assertEquals('page1 should remain selected, as page2 is disabled',
    +               'page1', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +
    +  tabPane.setSelectedIndex(2);
    +  selected = tabPane.getSelectedPage();
    +  assertEquals('page3 should be selected', 'page3', selected.getTitle());
    +  assertEquals('goog-tabpane-tab-selected',
    +               selected.getTitleElement().className);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer.js
    new file mode 100644
    index 0000000000..8c15644c65
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer.js
    @@ -0,0 +1,153 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Default renderer for {@link goog.ui.Tab}s.  Based on the
    + * original {@code TabPane} code.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.TabRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Tab}s, based on the {@code TabPane} code.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + */
    +goog.ui.TabRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.TabRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.TabRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.TabRenderer.CSS_CLASS = goog.getCssName('goog-tab');
    +
    +
    +/**
    + * Returns the CSS class name to be applied to the root element of all tabs
    + * rendered or decorated using this renderer.
    + * @return {string} Renderer-specific CSS class name.
    + * @override
    + */
    +goog.ui.TabRenderer.prototype.getCssClass = function() {
    +  return goog.ui.TabRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Returns the ARIA role to be applied to the tab element.
    + * See http://wiki/Main/ARIA for more info.
    + * @return {goog.a11y.aria.Role} ARIA role.
    + * @override
    + */
    +goog.ui.TabRenderer.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.TAB;
    +};
    +
    +
    +/**
    + * Returns the tab's contents wrapped in a DIV, with the renderer's own CSS
    + * class and additional state-specific classes applied to it.  Creates the
    + * following DOM structure:
    + * <pre>
    + *   <div class="goog-tab" title="Title">Content</div>
    + * </pre>
    + * @param {goog.ui.Control} tab Tab to render.
    + * @return {Element} Root element for the tab.
    + * @override
    + */
    +goog.ui.TabRenderer.prototype.createDom = function(tab) {
    +  var element = goog.ui.TabRenderer.superClass_.createDom.call(this, tab);
    +
    +  var tooltip = tab.getTooltip();
    +  if (tooltip) {
    +    // Only update the element if the tab has a tooltip.
    +    this.setTooltip(element, tooltip);
    +  }
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Decorates the element with the tab.  Initializes the tab's ID, content,
    + * tooltip, and state based on the ID of the element, its title, child nodes,
    + * and CSS classes, respectively.  Returns the element.
    + * @param {goog.ui.Control} tab Tab to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {Element} Decorated element.
    + * @override
    + */
    +goog.ui.TabRenderer.prototype.decorate = function(tab, element) {
    +  element = goog.ui.TabRenderer.superClass_.decorate.call(this, tab, element);
    +
    +  var tooltip = this.getTooltip(element);
    +  if (tooltip) {
    +    // Only update the tab if the element has a tooltip.
    +    tab.setTooltipInternal(tooltip);
    +  }
    +
    +  // If the tab is selected and hosted in a tab bar, update the tab bar's
    +  // selection model.
    +  if (tab.isSelected()) {
    +    var tabBar = tab.getParent();
    +    if (tabBar && goog.isFunction(tabBar.setSelectedTab)) {
    +      // We need to temporarily deselect the tab, so the tab bar can re-select
    +      // it and thereby correctly initialize its state.  We use the protected
    +      // setState() method to avoid dispatching useless events.
    +      tab.setState(goog.ui.Component.State.SELECTED, false);
    +      tabBar.setSelectedTab(tab);
    +    }
    +  }
    +
    +  return element;
    +};
    +
    +
    +/**
    + * Takes a tab's root element, and returns its tooltip text, or the empty
    + * string if the element has no tooltip.
    + * @param {Element} element The tab's root element.
    + * @return {string} The tooltip text (empty string if none).
    + */
    +goog.ui.TabRenderer.prototype.getTooltip = function(element) {
    +  return element.title || '';
    +};
    +
    +
    +/**
    + * Takes a tab's root element and a tooltip string, and updates the element
    + * with the new tooltip.  If the new tooltip is null or undefined, sets the
    + * element's title to the empty string.
    + * @param {Element} element The tab's root element.
    + * @param {string|null|undefined} tooltip New tooltip text (if any).
    + */
    +goog.ui.TabRenderer.prototype.setTooltip = function(element, tooltip) {
    +  if (element) {
    +    element.title = tooltip || '';
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.html
    new file mode 100644
    index 0000000000..6dc3f9a77c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.html
    @@ -0,0 +1,27 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +Author: attila@google.com (Attila Bodis)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TabRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TabRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="sandbox">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.js
    new file mode 100644
    index 0000000000..d9070e4941
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tabrenderer_test.js
    @@ -0,0 +1,140 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TabRendererTest');
    +goog.setTestOnly('goog.ui.TabRendererTest');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.Tab');
    +goog.require('goog.ui.TabRenderer');
    +
    +var sandbox;
    +var renderer;
    +var tab;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  renderer = goog.ui.TabRenderer.getInstance();
    +  tab = new goog.ui.Tab('Hello');
    +}
    +
    +function tearDown() {
    +  tab.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Renderer must not be null', renderer);
    +}
    +
    +function testGetCssClass() {
    +  assertEquals('CSS class must have expected value',
    +      goog.ui.TabRenderer.CSS_CLASS, renderer.getCssClass());
    +}
    +
    +function testGetAriaRole() {
    +  assertEquals('ARIA role must have expected value',
    +      goog.a11y.aria.Role.TAB, renderer.getAriaRole());
    +}
    +
    +function testCreateDom() {
    +  var element = renderer.createDom(tab);
    +  assertNotNull('Element must not be null', element);
    +  goog.testing.dom.assertHtmlMatches(
    +      '<div class="goog-tab">Hello</div>',
    +      goog.dom.getOuterHtml(element));
    +}
    +
    +function testCreateDomWithTooltip() {
    +  tab.setTooltip('Hello, world!');
    +  var element = renderer.createDom(tab);
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Element must have expected tooltip', 'Hello, world!',
    +      renderer.getTooltip(element));
    +}
    +
    +function testRender() {
    +  tab.setRenderer(renderer);
    +  tab.render();
    +  var element = tab.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('aria-selected should be false',
    +      'false', element.getAttribute('aria-selected'));
    +}
    +
    +function testDecorate() {
    +  sandbox.innerHTML =
    +      '<div id="foo">Foo</div>\n' +
    +      '<div id="bar" title="Yes">Bar</div>';
    +
    +  var foo = renderer.decorate(tab, goog.dom.getElement('foo'));
    +  assertNotNull('Decorated element must not be null', foo);
    +  assertSameElements('Decorated element must have expected class',
    +      ['goog-tab'], goog.dom.classlist.get(foo));
    +  assertEquals('Decorated tab must have expected content', 'Foo',
    +      tab.getContent().nodeValue);
    +  assertUndefined('Decorated tab must not have tooltip', tab.getTooltip());
    +  assertEquals('Decorated element must not have title', '',
    +      renderer.getTooltip(foo));
    +
    +  var bar = renderer.decorate(tab, goog.dom.getElement('bar'));
    +  assertNotNull('Decorated element must not be null', bar);
    +  assertSameElements('Decorated element must have expected class',
    +      ['goog-tab'], goog.dom.classlist.get(bar));
    +  assertEquals('Decorated tab must have expected content', 'Bar',
    +      tab.getContent().nodeValue);
    +  assertEquals('Decorated tab must have expected tooltip', 'Yes',
    +      tab.getTooltip());
    +  assertEquals('Decorated element must have expected title', 'Yes',
    +      renderer.getTooltip(bar));
    +}
    +
    +function testGetTooltip() {
    +  sandbox.innerHTML =
    +      '<div id="foo">Foo</div>\n' +
    +      '<div id="bar" title="">Bar</div>\n' +
    +      '<div id="baz" title="BazTitle">Baz</div>';
    +  assertEquals('getTooltip() must return empty string for no title', '',
    +      renderer.getTooltip(goog.dom.getElement('foo')));
    +  assertEquals('getTooltip() must return empty string for empty title', '',
    +      renderer.getTooltip(goog.dom.getElement('bar')));
    +  assertEquals('Tooltip must have expected value', 'BazTitle',
    +      renderer.getTooltip(goog.dom.getElement('baz')));
    +}
    +
    +function testSetTooltip() {
    +  sandbox.innerHTML = '<div id="foo">Foo</div>';
    +  var element = goog.dom.getElement('foo');
    +
    +  renderer.setTooltip(null, null); // Must not error.
    +
    +  renderer.setTooltip(element, null);
    +  assertEquals('Tooltip must be the empty string', '', element.title);
    +
    +  renderer.setTooltip(element, '');
    +  assertEquals('Tooltip must be the empty string', '', element.title);
    +
    +  renderer.setTooltip(element, 'Foo');
    +  assertEquals('Tooltip must have expected value', 'Foo', element.title);
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(goog.ui.TabRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/textarea.js b/mapbox-gl/closure-library/closure/goog/ui/textarea.js
    new file mode 100644
    index 0000000000..3b4d19bb27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/textarea.js
    @@ -0,0 +1,736 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A content-aware textarea control that grows and shrinks
    + * automatically. This implementation extends {@link goog.ui.Control}.
    + * This code is inspired by Dojo Dijit's Textarea implementation with
    + * modifications to support native (when available) textarea resizing and
    + * minHeight and maxHeight enforcement.
    + *
    + * @see ../demos/textarea.html
    + */
    +
    +goog.provide('goog.ui.Textarea');
    +goog.provide('goog.ui.Textarea.EventType');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.style');
    +goog.require('goog.ui.Control');
    +goog.require('goog.ui.TextareaRenderer');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * A textarea control to handle growing/shrinking with textarea.value.
    + *
    + * @param {string} content Text to set as the textarea's value.
    + * @param {goog.ui.TextareaRenderer=} opt_renderer Renderer used to render or
    + *     decorate the textarea. Defaults to {@link goog.ui.TextareaRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Control}
    + */
    +goog.ui.Textarea = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Control.call(this, content, opt_renderer ||
    +      goog.ui.TextareaRenderer.getInstance(), opt_domHelper);
    +
    +  this.setHandleMouseEvents(false);
    +  this.setAllowTextSelection(true);
    +  this.hasUserInput_ = (content != '');
    +  if (!content) {
    +    this.setContentInternal('');
    +  }
    +};
    +goog.inherits(goog.ui.Textarea, goog.ui.Control);
    +goog.tagUnsealableClass(goog.ui.Textarea);
    +
    +
    +/**
    + * Some UAs will shrink the textarea automatically, some won't.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.NEEDS_HELP_SHRINKING_ = goog.userAgent.GECKO ||
    +    goog.userAgent.WEBKIT ||
    +    (goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(11));
    +
    +
    +/**
    + * True if the resizing function is executing, false otherwise.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.isResizing_ = false;
    +
    +
    +/**
    + * Represents if we have focus on the textarea element, used only
    + * to render the placeholder if we don't have native placeholder
    + * support.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.hasFocusForPlaceholder_ = false;
    +
    +
    +/**
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.hasUserInput_ = false;
    +
    +
    +/**
    + * The height of the textarea as last measured.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Textarea.prototype.height_ = 0;
    +
    +
    +/**
    + * A maximum height for the textarea. When set to 0, the default, there is no
    + * enforcement of this value during resize.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Textarea.prototype.maxHeight_ = 0;
    +
    +
    +/**
    + * A minimum height for the textarea. When set to 0, the default, there is no
    + * enforcement of this value during resize.
    + * @type {number}
    + * @private
    + */
    +goog.ui.Textarea.prototype.minHeight_ = 0;
    +
    +
    +/**
    + * Whether or not textarea rendering characteristics have been discovered.
    + * Specifically we determine, at runtime:
    + *    If the padding and border box is included in offsetHeight.
    + *    @see {goog.ui.Textarea.prototype.needsPaddingBorderFix_}
    + *    If the padding and border box is included in scrollHeight.
    + *    @see {goog.ui.Textarea.prototype.scrollHeightIncludesPadding_} and
    + *    @see {goog.ui.Textarea.prototype.scrollHeightIncludesBorder_}
    + * TODO(user): See if we can determine goog.ui.Textarea.NEEDS_HELP_SHRINKING_.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.hasDiscoveredTextareaCharacteristics_ = false;
    +
    +
    +/**
    + * If a user agent doesn't correctly support the box-sizing:border-box CSS
    + * value then we'll need to adjust our height calculations.
    + * @see {goog.ui.Textarea.prototype.discoverTextareaCharacteristics_}
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.needsPaddingBorderFix_ = false;
    +
    +
    +/**
    + * Whether or not scrollHeight of a textarea includes the padding box.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.scrollHeightIncludesPadding_ = false;
    +
    +
    +/**
    + * Whether or not scrollHeight of a textarea includes the border box.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Textarea.prototype.scrollHeightIncludesBorder_ = false;
    +
    +
    +/**
    + * For storing the padding box size during enterDocument, to prevent possible
    + * measurement differences that can happen after text zooming.
    + * Note: runtime padding changes will cause problems with this.
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.Textarea.prototype.paddingBox_;
    +
    +
    +/**
    + * For storing the border box size during enterDocument, to prevent possible
    + * measurement differences that can happen after text zooming.
    + * Note: runtime border width changes will cause problems with this.
    + * @type {goog.math.Box}
    + * @private
    + */
    +goog.ui.Textarea.prototype.borderBox_;
    +
    +
    +/**
    + * Default text content for the textarea when it is unchanged and unfocussed.
    + * We use the placeholder attribute for all browsers that have support for
    + * it (new in HTML5 for the following browsers:
    + *
    + *   Internet Explorer 10.0
    + *   Firefox 4.0
    + *   Opera 11.6
    + *   Chrome 4.0
    + *   Safari 5.0
    + *
    + * For older browsers, we save the placeholderText_ and set it as the element's
    + * value and add the TEXTAREA_PLACEHOLDER_CLASS to indicate that it's a
    + * placeholder string.
    + * @type {string}
    + * @private
    + */
    +goog.ui.Textarea.prototype.placeholderText_ = '';
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +goog.ui.Textarea.EventType = {
    +  RESIZE: 'resize'
    +};
    +
    +
    +/**
    + * Sets the default text for the textarea.
    + * @param {string} text The default text for the textarea.
    + */
    +goog.ui.Textarea.prototype.setPlaceholder = function(text) {
    +  this.placeholderText_ = text;
    +  if (this.getElement()) {
    +    this.restorePlaceholder_();
    +  }
    +};
    +
    +
    +/**
    + * @return {number} The padding plus the border box height.
    + * @private
    + */
    +goog.ui.Textarea.prototype.getPaddingBorderBoxHeight_ = function() {
    +  var paddingBorderBoxHeight = this.paddingBox_.top + this.paddingBox_.bottom +
    +      this.borderBox_.top + this.borderBox_.bottom;
    +  return paddingBorderBoxHeight;
    +};
    +
    +
    +/**
    + * @return {number} The minHeight value.
    + */
    +goog.ui.Textarea.prototype.getMinHeight = function() {
    +  return this.minHeight_;
    +};
    +
    +
    +/**
    + * @return {number} The minHeight value with a potential padding fix.
    + * @private
    + */
    +goog.ui.Textarea.prototype.getMinHeight_ = function() {
    +  var minHeight = this.minHeight_;
    +  var textarea = this.getElement();
    +  if (minHeight && textarea && this.needsPaddingBorderFix_) {
    +    minHeight -= this.getPaddingBorderBoxHeight_();
    +  }
    +  return minHeight;
    +};
    +
    +
    +/**
    + * Sets a minimum height for the textarea, and calls resize if rendered.
    + * @param {number} height New minHeight value.
    + */
    +goog.ui.Textarea.prototype.setMinHeight = function(height) {
    +  this.minHeight_ = height;
    +  this.resize();
    +};
    +
    +
    +/**
    + * @return {number} The maxHeight value.
    + */
    +goog.ui.Textarea.prototype.getMaxHeight = function() {
    +  return this.maxHeight_;
    +};
    +
    +
    +/**
    + * @return {number} The maxHeight value with a potential padding fix.
    + * @private
    + */
    +goog.ui.Textarea.prototype.getMaxHeight_ = function() {
    +  var maxHeight = this.maxHeight_;
    +  var textarea = this.getElement();
    +  if (maxHeight && textarea && this.needsPaddingBorderFix_) {
    +    maxHeight -= this.getPaddingBorderBoxHeight_();
    +  }
    +  return maxHeight;
    +};
    +
    +
    +/**
    + * Sets a maximum height for the textarea, and calls resize if rendered.
    + * @param {number} height New maxHeight value.
    + */
    +goog.ui.Textarea.prototype.setMaxHeight = function(height) {
    +  this.maxHeight_ = height;
    +  this.resize();
    +};
    +
    +
    +/**
    + * Sets the textarea's value.
    + * @param {*} value The value property for the textarea, will be cast to a
    + *     string by the browser when setting textarea.value.
    + */
    +goog.ui.Textarea.prototype.setValue = function(value) {
    +  this.setContent(String(value));
    +};
    +
    +
    +/**
    + * Gets the textarea's value.
    + * @return {string} value The value of the textarea.
    + */
    +goog.ui.Textarea.prototype.getValue = function() {
    +  // We potentially have the placeholder stored in the value.
    +  // If a client of this class sets this.getElement().value directly
    +  // we don't set the this.hasUserInput_ boolean. Thus, we need to
    +  // explicitly check if the value != the placeholder text. This has
    +  // the unfortunate edge case of:
    +  //   If the client sets this.getElement().value to the placeholder
    +  //   text, we'll return the empty string.
    +  // The normal use case shouldn't be an issue, however, since the
    +  // default placeholderText is the empty string. Also, if the end user
    +  // inputs text, then this.hasUserInput_ will always be true.
    +  if (this.getElement().value != this.placeholderText_ ||
    +      this.supportsNativePlaceholder_() || this.hasUserInput_) {
    +    // We don't do anything fancy here.
    +    return this.getElement().value;
    +  }
    +  return '';
    +};
    +
    +
    +/** @override */
    +goog.ui.Textarea.prototype.setContent = function(content) {
    +  goog.ui.Textarea.superClass_.setContent.call(this, content);
    +  this.hasUserInput_ = (content != '');
    +  this.resize();
    +};
    +
    +
    +/** @override **/
    +goog.ui.Textarea.prototype.setEnabled = function(enable) {
    +  goog.ui.Textarea.superClass_.setEnabled.call(this, enable);
    +  this.getElement().disabled = !enable;
    +};
    +
    +
    +/**
    + * Resizes the textarea vertically.
    + */
    +goog.ui.Textarea.prototype.resize = function() {
    +  if (this.getElement()) {
    +    this.grow_();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} True if the element supports the placeholder attribute.
    + * @private
    + */
    +goog.ui.Textarea.prototype.supportsNativePlaceholder_ = function() {
    +  goog.asserts.assert(this.getElement());
    +  return 'placeholder' in this.getElement();
    +};
    +
    +
    +/**
    + * Sets the value of the textarea element to the default text.
    + * @private
    + */
    +goog.ui.Textarea.prototype.restorePlaceholder_ = function() {
    +  if (!this.placeholderText_) {
    +    // Return early if there is no placeholder to mess with.
    +    return;
    +  }
    +  // Check again in case something changed since this was scheduled.
    +  // We check that the element is still there since this is called by a timer
    +  // and the dispose method may have been called prior to this.
    +  if (this.supportsNativePlaceholder_()) {
    +    this.getElement().placeholder = this.placeholderText_;
    +  } else if (this.getElement() && !this.hasUserInput_ &&
    +      !this.hasFocusForPlaceholder_) {
    +    // We only want to set the value + placeholder CSS if we actually have
    +    // some placeholder text to show.
    +    goog.dom.classlist.add(
    +        goog.asserts.assert(this.getElement()),
    +        goog.ui.Textarea.TEXTAREA_PLACEHOLDER_CLASS);
    +    this.getElement().value = this.placeholderText_;
    +  }
    +};
    +
    +
    +/** @override **/
    +goog.ui.Textarea.prototype.enterDocument = function() {
    +  goog.ui.Textarea.base(this, 'enterDocument');
    +  var textarea = this.getElement();
    +
    +  // Eliminates the vertical scrollbar and changes the box-sizing mode for the
    +  // textarea to the border-box (aka quirksmode) paradigm.
    +  goog.style.setStyle(textarea, {
    +    'overflowY': 'hidden',
    +    'overflowX': 'auto',
    +    'boxSizing': 'border-box',
    +    'MsBoxSizing': 'border-box',
    +    'WebkitBoxSizing': 'border-box',
    +    'MozBoxSizing': 'border-box'});
    +
    +  this.paddingBox_ = goog.style.getPaddingBox(textarea);
    +  this.borderBox_ = goog.style.getBorderBox(textarea);
    +
    +  this.getHandler().
    +      listen(textarea, goog.events.EventType.SCROLL, this.grow_).
    +      listen(textarea, goog.events.EventType.FOCUS, this.grow_).
    +      listen(textarea, goog.events.EventType.KEYUP, this.grow_).
    +      listen(textarea, goog.events.EventType.MOUSEUP, this.mouseUpListener_).
    +      listen(textarea, goog.events.EventType.BLUR, this.blur_);
    +
    +  this.restorePlaceholder_();
    +  this.resize();
    +};
    +
    +
    +/**
    + * Gets the textarea's content height + padding height + border height.
    + * This is done by getting the scrollHeight and adjusting from there.
    + * In the end this result is what we want the new offsetHeight to equal.
    + * @return {number} The height of the textarea.
    + * @private
    + */
    +goog.ui.Textarea.prototype.getHeight_ = function() {
    +  this.discoverTextareaCharacteristics_();
    +  var textarea = this.getElement();
    +  // Because enterDocument can be called even when the component is rendered
    +  // without being in a document, we may not have cached the correct paddingBox
    +  // data on render(). We try to make up for this here.
    +  if (isNaN(this.paddingBox_.top)) {
    +    this.paddingBox_ = goog.style.getPaddingBox(textarea);
    +    this.borderBox_ = goog.style.getBorderBox(textarea);
    +  }
    +  // Accounts for a possible (though unlikely) horizontal scrollbar.
    +  var height = this.getElement().scrollHeight +
    +      this.getHorizontalScrollBarHeight_();
    +  if (this.needsPaddingBorderFix_) {
    +    height -= this.getPaddingBorderBoxHeight_();
    +  } else {
    +    if (!this.scrollHeightIncludesPadding_) {
    +      var paddingBox = this.paddingBox_;
    +      var paddingBoxHeight = paddingBox.top + paddingBox.bottom;
    +      height += paddingBoxHeight;
    +    }
    +    if (!this.scrollHeightIncludesBorder_) {
    +      var borderBox = goog.style.getBorderBox(textarea);
    +      var borderBoxHeight = borderBox.top + borderBox.bottom;
    +      height += borderBoxHeight;
    +    }
    +  }
    +  return height;
    +};
    +
    +
    +/**
    + * Sets the textarea's height.
    + * @param {number} height The height to set.
    + * @private
    + */
    +goog.ui.Textarea.prototype.setHeight_ = function(height) {
    +  if (this.height_ != height) {
    +    this.height_ = height;
    +    this.getElement().style.height = height + 'px';
    +  }
    +};
    +
    +
    +/**
    + * Sets the textarea's rows attribute to be the number of newlines + 1.
    + * This is necessary when the textarea is hidden, in which case scrollHeight
    + * is not available.
    + * @private
    + */
    +goog.ui.Textarea.prototype.setHeightToEstimate_ = function() {
    +  var textarea = this.getElement();
    +  textarea.style.height = 'auto';
    +  var newlines = textarea.value.match(/\n/g) || [];
    +  textarea.rows = newlines.length + 1;
    +  this.height_ = 0;
    +};
    +
    +
    +/**
    + * Gets the the height of (possibly present) horizontal scrollbar.
    + * @return {number} The height of the horizontal scrollbar.
    + * @private
    + */
    +goog.ui.Textarea.prototype.getHorizontalScrollBarHeight_ =
    +    function() {
    +  var textarea = this.getElement();
    +  var height = textarea.offsetHeight - textarea.clientHeight;
    +  if (!this.scrollHeightIncludesPadding_) {
    +    var paddingBox = this.paddingBox_;
    +    var paddingBoxHeight = paddingBox.top + paddingBox.bottom;
    +    height -= paddingBoxHeight;
    +  }
    +  if (!this.scrollHeightIncludesBorder_) {
    +    var borderBox = goog.style.getBorderBox(textarea);
    +    var borderBoxHeight = borderBox.top + borderBox.bottom;
    +    height -= borderBoxHeight;
    +  }
    +  // Prevent negative number results, which sometimes show up.
    +  return height > 0 ? height : 0;
    +};
    +
    +
    +/**
    + * In order to assess the correct height for a textarea, we need to know
    + * whether the scrollHeight (the full height of the text) property includes
    + * the values for padding and borders. We can also test whether the
    + * box-sizing: border-box setting is working and then tweak accordingly.
    + * Instead of hardcoding a list of currently known behaviors and testing
    + * for quirksmode, we do a runtime check out of the flow. The performance
    + * impact should be very small.
    + * @private
    + */
    +goog.ui.Textarea.prototype.discoverTextareaCharacteristics_ = function() {
    +  if (!this.hasDiscoveredTextareaCharacteristics_) {
    +    var textarea = /** @type {!Element} */ (this.getElement().cloneNode(false));
    +    // We need to overwrite/write box model specific styles that might
    +    // affect height.
    +    goog.style.setStyle(textarea, {
    +      'position': 'absolute',
    +      'height': 'auto',
    +      'top': '-9999px',
    +      'margin': '0',
    +      'padding': '1px',
    +      'border': '1px solid #000',
    +      'overflow': 'hidden'
    +    });
    +    goog.dom.appendChild(this.getDomHelper().getDocument().body, textarea);
    +    var initialScrollHeight = textarea.scrollHeight;
    +
    +    textarea.style.padding = '10px';
    +    var paddingScrollHeight = textarea.scrollHeight;
    +    this.scrollHeightIncludesPadding_ = paddingScrollHeight >
    +        initialScrollHeight;
    +
    +    initialScrollHeight = paddingScrollHeight;
    +    textarea.style.borderWidth = '10px';
    +    var borderScrollHeight = textarea.scrollHeight;
    +    this.scrollHeightIncludesBorder_ = borderScrollHeight > initialScrollHeight;
    +
    +    // Tests if border-box sizing is working or not.
    +    textarea.style.height = '100px';
    +    var offsetHeightAtHeight100 = textarea.offsetHeight;
    +    if (offsetHeightAtHeight100 != 100) {
    +      this.needsPaddingBorderFix_ = true;
    +    }
    +
    +    goog.dom.removeNode(textarea);
    +    this.hasDiscoveredTextareaCharacteristics_ = true;
    +  }
    +};
    +
    +
    +/**
    + * The CSS class name to add to the input when the user has not entered a
    + * value.
    + */
    +goog.ui.Textarea.TEXTAREA_PLACEHOLDER_CLASS =
    +    goog.getCssName('textarea-placeholder-input');
    +
    +
    +/**
    + * Called when the element goes out of focus.
    + * @param {goog.events.Event=} opt_e The browser event.
    + * @private
    + */
    +goog.ui.Textarea.prototype.blur_ = function(opt_e) {
    +  if (!this.supportsNativePlaceholder_()) {
    +    this.hasFocusForPlaceholder_ = false;
    +    if (this.getElement().value == '') {
    +      // Only transition to the default text if we have
    +      // no user input.
    +      this.hasUserInput_ = false;
    +      this.restorePlaceholder_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Resizes the textarea to grow/shrink to match its contents.
    + * @param {goog.events.Event=} opt_e The browser event.
    + * @private
    + */
    +goog.ui.Textarea.prototype.grow_ = function(opt_e) {
    +  if (this.isResizing_) {
    +    return;
    +  }
    +  var textarea = this.getElement();
    +  // If the element is getting focus and we don't support placeholders
    +  // natively, then remove the placeholder class.
    +  if (!this.supportsNativePlaceholder_() && opt_e &&
    +      opt_e.type == goog.events.EventType.FOCUS) {
    +    // We must have a textarea element, since we're growing it.
    +    // Remove the placeholder CSS + set the value to empty if we're currently
    +    // showing the placeholderText_ value if this is the first time we're
    +    // getting focus.
    +    if (textarea.value == this.placeholderText_ &&
    +        this.placeholderText_ &&
    +        !this.hasFocusForPlaceholder_) {
    +      goog.dom.classlist.remove(
    +          textarea, goog.ui.Textarea.TEXTAREA_PLACEHOLDER_CLASS);
    +      textarea.value = '';
    +    }
    +    this.hasFocusForPlaceholder_ = true;
    +    this.hasUserInput_ = (textarea.value != '');
    +  }
    +  var shouldCallShrink = false;
    +  this.isResizing_ = true;
    +  var oldHeight = this.height_;
    +  if (textarea.scrollHeight) {
    +    var setMinHeight = false;
    +    var setMaxHeight = false;
    +    var newHeight = this.getHeight_();
    +    var currentHeight = textarea.offsetHeight;
    +    var minHeight = this.getMinHeight_();
    +    var maxHeight = this.getMaxHeight_();
    +    if (minHeight && newHeight < minHeight) {
    +      this.setHeight_(minHeight);
    +      setMinHeight = true;
    +    } else if (maxHeight && newHeight > maxHeight) {
    +      this.setHeight_(maxHeight);
    +      // If the content is greater than the height, we'll want the vertical
    +      // scrollbar back.
    +      textarea.style.overflowY = '';
    +      setMaxHeight = true;
    +    } else if (currentHeight != newHeight) {
    +      this.setHeight_(newHeight);
    +    // Makes sure that height_ is at least set.
    +    } else if (!this.height_) {
    +      this.height_ = newHeight;
    +    }
    +    if (!setMinHeight && !setMaxHeight &&
    +        goog.ui.Textarea.NEEDS_HELP_SHRINKING_) {
    +      shouldCallShrink = true;
    +    }
    +  } else {
    +    this.setHeightToEstimate_();
    +  }
    +  this.isResizing_ = false;
    +
    +  if (shouldCallShrink) {
    +    this.shrink_();
    +  }
    +  if (oldHeight != this.height_) {
    +    this.dispatchEvent(goog.ui.Textarea.EventType.RESIZE);
    +  }
    +};
    +
    +
    +/**
    + * Resizes the textarea to shrink to fit its contents. The way this works is
    + * by increasing the padding of the textarea by 1px (it's important here that
    + * we're in box-sizing: border-box mode). If the size of the textarea grows,
    + * then the box is filled up to the padding box with text.
    + * If it doesn't change, then we can shrink.
    + * @private
    + */
    +goog.ui.Textarea.prototype.shrink_ = function() {
    +  var textarea = this.getElement();
    +  if (!this.isResizing_) {
    +    this.isResizing_ = true;
    +    var scrollHeight = textarea.scrollHeight;
    +    if (!scrollHeight) {
    +      this.setHeightToEstimate_();
    +    } else {
    +      var currentHeight = this.getHeight_();
    +      var minHeight = this.getMinHeight_();
    +      var maxHeight = this.getMaxHeight_();
    +      if (!(minHeight && currentHeight <= minHeight)) {
    +        // Nudge the padding by 1px.
    +        var paddingBox = this.paddingBox_;
    +        textarea.style.paddingBottom = paddingBox.bottom + 1 + 'px';
    +        var heightAfterNudge = this.getHeight_();
    +        // If the one px of padding had no effect, then we can shrink.
    +        if (heightAfterNudge == currentHeight) {
    +          textarea.style.paddingBottom = paddingBox.bottom + scrollHeight +
    +              'px';
    +          textarea.scrollTop = 0;
    +          var shrinkToHeight = this.getHeight_() - scrollHeight;
    +          if (shrinkToHeight >= minHeight) {
    +            this.setHeight_(shrinkToHeight);
    +          } else {
    +            this.setHeight_(minHeight);
    +          }
    +        }
    +        textarea.style.paddingBottom = paddingBox.bottom + 'px';
    +      }
    +    }
    +    this.isResizing_ = false;
    +  }
    +};
    +
    +
    +/**
    + * We use this listener to check if the textarea has been natively resized
    + * and if so we reset minHeight so that we don't ever shrink smaller than
    + * the user's manually set height. Note that we cannot check size on mousedown
    + * and then just compare here because we cannot capture mousedown on
    + * the textarea resizer, while mouseup fires reliably.
    + * @param {goog.events.BrowserEvent} e The mousedown event.
    + * @private
    + */
    +goog.ui.Textarea.prototype.mouseUpListener_ = function(e) {
    +  var textarea = this.getElement();
    +  var height = textarea.offsetHeight;
    +
    +  // This solves for when the MSIE DropShadow filter is enabled,
    +  // as it affects the offsetHeight value, even with MsBoxSizing:border-box.
    +  if (textarea['filters'] && textarea['filters'].length) {
    +    var dropShadow =
    +        textarea['filters']['item']('DXImageTransform.Microsoft.DropShadow');
    +    if (dropShadow) {
    +      height -= dropShadow['offX'];
    +    }
    +  }
    +
    +  if (height != this.height_) {
    +    this.minHeight_ = height;
    +    this.height_ = height;
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/textarea_test.html b/mapbox-gl/closure-library/closure/goog/ui/textarea_test.html
    new file mode 100644
    index 0000000000..15a87cc603
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/textarea_test.html
    @@ -0,0 +1,44 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Textarea
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TextareaTest');
    +  </script>
    +  <style>
    +   textarea {
    +      /* Some of the height tests are based on font size px values. */
    +      font-size: 1em;
    +      height: 25px; /* Need to force an initial height < our minHeight. */
    +      width: 150px;
    +      padding: 2px;
    +      margin: 0;
    +      border: 1px solid #000;
    +    }
    +    .drop-shadowed {
    +      filter:progid:DXImageTransform.Microsoft.DropShadow(color='#e7e7e7',
    +          offX='5',offY='5');
    +      box-shadow: 5px 5px 0 #e7e7e7;
    +      -moz-box-shadow: 5px 5px 0 #e7e7e7;
    +      -webkit-box-shadow: 5px 5px 0 #e7e7e7;
    +    }
    +  </style>
    + </head>
    + <body>
    +  <h1>goog.ui.Textarea tests</h1>
    +  <p>Here's a textarea defined in markup:</p>
    +  <textarea id="demo-textarea">Foo</textarea>
    +  <div id="sandbox"></div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/textarea_test.js b/mapbox-gl/closure-library/closure/goog/ui/textarea_test.js
    new file mode 100644
    index 0000000000..9a4aef0d7b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/textarea_test.js
    @@ -0,0 +1,348 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TextareaTest');
    +goog.setTestOnly('goog.ui.TextareaTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.style');
    +goog.require('goog.testing.ExpectedFailures');
    +goog.require('goog.testing.events.EventObserver');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Textarea');
    +goog.require('goog.ui.TextareaRenderer');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +var sandbox;
    +var textarea;
    +var demoTextareaElement;
    +var expectedFailures;
    +
    +function setUp() {
    +  sandbox = goog.dom.getElement('sandbox');
    +  textarea = new goog.ui.Textarea();
    +  demoTextareaElement = goog.dom.getElement('demo-textarea');
    +  expectedFailures = new goog.testing.ExpectedFailures();
    +}
    +
    +function tearDown() {
    +  expectedFailures.handleTearDown();
    +  textarea.dispose();
    +  goog.dom.removeChildren(sandbox);
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on Mac Safari 3.x.
    + */
    +function isMacSafari3() {
    +  return goog.userAgent.WEBKIT && goog.userAgent.MAC &&
    +      !goog.userAgent.isVersionOrHigher('527');
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on Linux Firefox 3.6.3.
    + */
    +function isLinuxFirefox() {
    +  return goog.userAgent.product.FIREFOX && goog.userAgent.LINUX;
    +}
    +
    +
    +/**
    + * @return {boolean} Whether we're on Firefox 3.0.
    + */
    +function isFirefox3() {
    +  return goog.userAgent.GECKO &&
    +      !goog.userAgent.isVersionOrHigher('1.9.1');
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Textarea must not be null', textarea);
    +  assertEquals('Renderer must default to expected value',
    +      goog.ui.TextareaRenderer.getInstance(), textarea.getRenderer());
    +
    +  var fakeDomHelper = {
    +    'getDocument': function() { return true; }
    +  };
    +  var testTextarea = new goog.ui.Textarea('Hello',
    +      goog.ui.TextareaRenderer.getInstance(), fakeDomHelper);
    +  assertEquals('Content must have expected content', 'Hello',
    +      testTextarea.getContent());
    +  assertEquals('Renderer must have expected value',
    +      goog.ui.TextareaRenderer.getInstance(), testTextarea.getRenderer());
    +  assertEquals('DOM helper must have expected value', fakeDomHelper,
    +      testTextarea.getDomHelper());
    +  testTextarea.dispose();
    +}
    +
    +function testConstructorWithDecorator() {
    +  var decoratedTextarea = new goog.ui.Textarea();
    +  decoratedTextarea.decorate(demoTextareaElement);
    +  assertEquals('Textarea should have current content after decoration',
    +      'Foo', decoratedTextarea.getContent());
    +  var initialHeight = decoratedTextarea.getHeight_();
    +  var initialOffsetHeight = decoratedTextarea.getElement().offsetHeight;
    +  // focus() will trigger the grow/shrink flow.
    +  decoratedTextarea.getElement().focus();
    +  assertEquals('Height should not have changed without content change',
    +      initialHeight, decoratedTextarea.getHeight_());
    +  assertEquals('offsetHeight should not have changed without content ' +
    +      'change', initialOffsetHeight,
    +      decoratedTextarea.getElement().offsetHeight);
    +  decoratedTextarea.dispose();
    +}
    +
    +function testGetSetContent() {
    +  textarea.render(sandbox);
    +  assertEquals('Textarea\'s content must default to an empty string',
    +      '', textarea.getContent());
    +  textarea.setContent(17);
    +  assertEquals('Textarea element must have expected content', '17',
    +      textarea.getElement().value);
    +  textarea.setContent('foo');
    +  assertEquals('Textarea element must have updated content', 'foo',
    +      textarea.getElement().value);
    +}
    +
    +function testGetSetValue() {
    +  textarea.render(sandbox);
    +  assertEquals('Textarea\'s content must default to an empty string',
    +      '', textarea.getValue());
    +  textarea.setValue(17);
    +  assertEquals('Textarea element must have expected content', '17',
    +      textarea.getValue());
    +  textarea.setValue('17');
    +  assertEquals('Textarea element must have expected content', '17',
    +      textarea.getValue());
    +}
    +
    +function testBasicTextareaBehavior() {
    +  var observer = new goog.testing.events.EventObserver();
    +  goog.events.listen(textarea, goog.ui.Textarea.EventType.RESIZE, observer);
    +  textarea.render(sandbox);
    +  var el = textarea.getElement();
    +  var heightBefore = textarea.getHeight_();
    +  assertTrue('One resize event should be fired during render',
    +      observer.getEvents().length == 1);
    +  textarea.setContent('Lorem ipsum dolor sit amet, consectetuer ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.');
    +  var heightAfter = textarea.getHeight_();
    +  assertTrue('With this much content, height should have grown.',
    +      heightAfter > heightBefore);
    +  assertTrue('With a height change, a resize event should have fired.',
    +      observer.getEvents().length == 2);
    +  textarea.setContent('');
    +  heightAfter = textarea.getHeight_();
    +  assertTrue('Textarea should shrink with no content.',
    +      heightAfter <= heightBefore);
    +  assertTrue('With a height change, a resize event should have fired.',
    +      observer.getEvents().length == 3);
    +  goog.events.unlisten(textarea, goog.ui.Textarea.EventType.RESIZE,
    +      observer);
    +}
    +
    +function testMinHeight() {
    +  textarea.render(sandbox);
    +  textarea.setMinHeight(50);
    +  assertEquals('offsetHeight should be 50 initially', 50,
    +      textarea.getElement().offsetHeight);
    +  textarea.setContent('Lorem ipsum dolor sit amet, consectetuer  ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.');
    +  assertTrue('getHeight_() should be > 50',
    +      textarea.getHeight_() > 50);
    +
    +  textarea.setContent('');
    +  assertEquals('With no content, offsetHeight should go back to 50, ' +
    +      'the minHeight.', 50, textarea.getElement().offsetHeight);
    +
    +  expectedFailures.expectFailureFor(isMacSafari3());
    +  try {
    +    textarea.setMinHeight(0);
    +    assertTrue('After setting minHeight to 0, offsetHeight should ' +
    +        'now be < 50, but it is ' + textarea.getElement().offsetHeight,
    +        textarea.getElement().offsetHeight < 50);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testMouseUpListener() {
    +  textarea.render(sandbox);
    +  textarea.setMinHeight(100);
    +  textarea.setMaxHeight(200);
    +  textarea.mouseUpListener_({});
    +  assertEquals('After a mouseup which is not a resize, minHeight should ' +
    +      'still be 100', 100, textarea.minHeight_);
    +
    +  // We need to test how CSS drop shadows effect this too.
    +  goog.dom.classlist.add(textarea.getElement(), 'drop-shadowed');
    +  textarea.mouseUpListener_({});
    +  assertEquals('After a mouseup which is not a resize, minHeight should ' +
    +      'still be 100 even with a shadow', 100, textarea.minHeight_);
    +
    +}
    +
    +function testMaxHeight() {
    +  textarea.render(sandbox);
    +  textarea.setMaxHeight(50);
    +  assertTrue('Initial offsetHeight should be less than 50',
    +      textarea.getElement().offsetHeight < 50);
    +  var newContent = 'Lorem ipsum dolor sit amet, consectetuer adipiscing ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.';
    +  textarea.setContent(newContent);
    +
    +  assertTrue('With lots of content, getHeight_() should be > 50',
    +      textarea.getHeight_() > 50);
    +  assertEquals('Even with lots of content, offsetHeight should be 50 ' +
    +      'with maxHeight set', 50, textarea.getElement().offsetHeight);
    +  textarea.setMaxHeight(0);
    +  assertTrue('After setting maxHeight to 0, offsetHeight should now ' +
    +      'be > 50', textarea.getElement().offsetHeight > 50);
    +}
    +
    +function testMaxHeight_canShrink() {
    +  textarea.render(sandbox);
    +  textarea.setMaxHeight(50);
    +  assertTrue('Initial offsetHeight should be less than 50',
    +      textarea.getElement().offsetHeight < 50);
    +  var newContent = 'Lorem ipsum dolor sit amet, consectetuer adipiscing ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.';
    +  textarea.setContent(newContent);
    +
    +  assertEquals('Even with lots of content, offsetHeight should be 50 ' +
    +      'with maxHeight set', 50, textarea.getElement().offsetHeight);
    +  textarea.setContent('');
    +  assertTrue('With no content, offsetHeight should be back to < 50',
    +      textarea.getElement().offsetHeight < 50);
    +}
    +
    +function testSetPlaceholder() {
    +  textarea.setPlaceholder('Some default text here.');
    +  textarea.setPlaceholder('new default text...');
    +  textarea.render(sandbox);
    +  if (textarea.supportsNativePlaceholder_()) {
    +    assertEquals('new default text...', textarea.getElement().placeholder);
    +  }
    +  assertEquals('', textarea.getValue());
    +  textarea.setValue('some value');
    +  assertEquals('some value', textarea.getValue());
    +  // ensure setting a new placeholder doesn't replace the value.
    +  textarea.setPlaceholder('some new placeholder');
    +  assertEquals('some value', textarea.getValue());
    +}
    +
    +function testSetPlaceholderForInitialContent() {
    +  var testTextarea = new goog.ui.Textarea('initial content');
    +  testTextarea.render(sandbox);
    +  assertEquals('initial content', testTextarea.getValue());
    +  testTextarea.setPlaceholder('new default text...');
    +  assertEquals('initial content', testTextarea.getValue());
    +  testTextarea.setValue('new content');
    +  assertEquals('new content', testTextarea.getValue());
    +  testTextarea.setValue('');
    +  assertEquals('', testTextarea.getValue());
    +  if (!testTextarea.supportsNativePlaceholder_()) {
    +    // Pretend we leave the textarea. When that happens, the
    +    // placeholder text should appear.
    +    assertEquals('', testTextarea.getElement().value);
    +    testTextarea.blur_();
    +    assertEquals('new default text...', testTextarea.getElement().value);
    +  }
    +}
    +
    +function testMinAndMaxHeight() {
    +  textarea.render(sandbox);
    +  textarea.setMinHeight(50);
    +  textarea.setMaxHeight(150);
    +  assertEquals('offsetHeight should be 50 initially', 50,
    +      textarea.getElement().offsetHeight);
    +
    +  textarea.setContent('Lorem ipsum dolor sit amet, consectetuer  ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.');
    +
    +  var height = textarea.getHeight_();
    +  // For some reason Mac Safari 3 has 136 and Linux FF has 146 here.
    +  expectedFailures.expectFailureFor(isMacSafari3() || isLinuxFirefox());
    +  try {
    +    assertTrue('With lots of content, getHeight_() should be > 150 ' +
    +        '(it is ' + height + ')', height > 150);
    +    assertEquals('Even with lots of content, offsetHeight should be 150 ' +
    +        'with maxHeight set', 150,
    +        textarea.getElement().offsetHeight);
    +
    +    textarea.setMaxHeight(0);
    +    assertTrue('After setting maxHeight to 0, offsetHeight should now ' +
    +        'be > 150 (it is ' + textarea.getElement().offsetHeight + ')',
    +        textarea.getElement().offsetHeight > 150);
    +
    +    textarea.setContent('');
    +    textarea.setMinHeight(0);
    +    assertTrue('After setting minHeight to 0, with no contents, ' +
    +        'offsetHeight should now be < 50',
    +        textarea.getElement().offsetHeight < 50);
    +  } catch (e) {
    +    expectedFailures.handleException(e);
    +  }
    +}
    +
    +function testSetValueWhenInvisible() {
    +  textarea.render(sandbox);
    +  var content = 'Lorem ipsum dolor sit amet, consectetuer  ' +
    +      'elit. Aenean sollicitudin ultrices urna. Proin vehicula mauris ac ' +
    +      'est. Ut scelerisque, risus ut facilisis dictum, est massa lacinia ' +
    +      'lorem, in fermentum purus ligula quis nunc.';
    +  textarea.setValue(content);
    +  var height = textarea.getHeight_();
    +  var elementHeight = goog.style.getStyle(textarea.getElement(), 'height');
    +  assertEquals(height + 'px', elementHeight);
    +
    +  // Hide the element, height_ should be invalidate when setValue().
    +  goog.style.setElementShown(textarea.getElement(), false);
    +  textarea.setValue(content);
    +
    +  // Show the element again.
    +  goog.style.setElementShown(textarea.getElement(), true);
    +  textarea.setValue(content);
    +  height = textarea.getHeight_();
    +  elementHeight = goog.style.getStyle(textarea.getElement(), 'height');
    +  assertEquals(height + 'px', elementHeight);
    +}
    +
    +function testSetAriaLabel() {
    +  assertNull('Textarea must not have aria label by default',
    +      textarea.getAriaLabel());
    +  textarea.setAriaLabel('My textarea');
    +  textarea.render(sandbox);
    +  var element = textarea.getElementStrict();
    +  assertNotNull('Element must not be null', element);
    +  assertEquals('Item element must have expected aria-label', 'My textarea',
    +      element.getAttribute('aria-label'));
    +  textarea.setAriaLabel('My new textarea');
    +  assertEquals('Item element must have updated aria-label', 'My new textarea',
    +      element.getAttribute('aria-label'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/textarearenderer.js b/mapbox-gl/closure-library/closure/goog/ui/textarearenderer.js
    new file mode 100644
    index 0000000000..a888e5da1d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/textarearenderer.js
    @@ -0,0 +1,170 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//     http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Native browser textarea renderer for {@link goog.ui.Textarea}s.
    + */
    +
    +goog.provide('goog.ui.TextareaRenderer');
    +
    +goog.require('goog.dom.TagName');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.ControlRenderer');
    +
    +
    +
    +/**
    + * Renderer for {@link goog.ui.Textarea}s.  Renders and decorates native HTML
    + * textarea elements.  Since native HTML textareas have built-in support for
    + * many features, overrides many expensive (and redundant) superclass methods to
    + * be no-ops.
    + * @constructor
    + * @extends {goog.ui.ControlRenderer}
    + * @final
    + */
    +goog.ui.TextareaRenderer = function() {
    +  goog.ui.ControlRenderer.call(this);
    +};
    +goog.inherits(goog.ui.TextareaRenderer, goog.ui.ControlRenderer);
    +goog.addSingletonGetter(goog.ui.TextareaRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.TextareaRenderer.CSS_CLASS = goog.getCssName('goog-textarea');
    +
    +
    +/** @override */
    +goog.ui.TextareaRenderer.prototype.getAriaRole = function() {
    +  // textareas don't need ARIA roles to be recognized by screen readers.
    +  return undefined;
    +};
    +
    +
    +/** @override */
    +goog.ui.TextareaRenderer.prototype.decorate = function(control, element) {
    +  this.setUpTextarea_(control);
    +  goog.ui.TextareaRenderer.superClass_.decorate.call(this, control,
    +      element);
    +  control.setContent(element.value);
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the textarea's contents wrapped in an HTML textarea element.  Sets
    + * the textarea's disabled attribute as needed.
    + * @param {goog.ui.Control} textarea Textarea to render.
    + * @return {!Element} Root element for the Textarea control (an HTML textarea
    + *     element).
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.createDom = function(textarea) {
    +  this.setUpTextarea_(textarea);
    +  var element = textarea.getDomHelper().createDom('textarea', {
    +    'class': this.getClassNames(textarea).join(' '),
    +    'disabled': !textarea.isEnabled()
    +  }, textarea.getContent() || '');
    +  return element;
    +};
    +
    +
    +/**
    + * Overrides {@link goog.ui.TextareaRenderer#canDecorate} by returning true only
    + * if the element is an HTML textarea.
    + * @param {Element} element Element to decorate.
    + * @return {boolean} Whether the renderer can decorate the element.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.canDecorate = function(element) {
    +  return element.tagName == goog.dom.TagName.TEXTAREA;
    +};
    +
    +
    +/**
    + * Textareas natively support right-to-left rendering.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.setRightToLeft = goog.nullFunction;
    +
    +
    +/**
    + * Textareas are always focusable as long as they are enabled.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.isFocusable = function(textarea) {
    +  return textarea.isEnabled();
    +};
    +
    +
    +/**
    + * Textareas natively support keyboard focus.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.setFocusable = goog.nullFunction;
    +
    +
    +/**
    + * Textareas also expose the DISABLED state in the HTML textarea's
    + * {@code disabled} attribute.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.setState = function(textarea, state,
    +    enable) {
    +  goog.ui.TextareaRenderer.superClass_.setState.call(this, textarea, state,
    +      enable);
    +  var element = textarea.getElement();
    +  if (element && state == goog.ui.Component.State.DISABLED) {
    +    element.disabled = enable;
    +  }
    +};
    +
    +
    +/**
    + * Textareas don't need ARIA states to support accessibility, so this is
    + * a no-op.
    + * @override
    + */
    +goog.ui.TextareaRenderer.prototype.updateAriaState = goog.nullFunction;
    +
    +
    +/**
    + * Sets up the textarea control such that it doesn't waste time adding
    + * functionality that is already natively supported by browser
    + * textareas.
    + * @param {goog.ui.Control} textarea Textarea control to configure.
    + * @private
    + */
    +goog.ui.TextareaRenderer.prototype.setUpTextarea_ = function(textarea) {
    +  textarea.setHandleMouseEvents(false);
    +  textarea.setAutoStates(goog.ui.Component.State.ALL, false);
    +  textarea.setSupportedState(goog.ui.Component.State.FOCUSED, false);
    +};
    +
    +
    +/** @override **/
    +goog.ui.TextareaRenderer.prototype.setContent = function(element, value) {
    +  if (element) {
    +    element.value = value;
    +  }
    +};
    +
    +
    +/** @override **/
    +goog.ui.TextareaRenderer.prototype.getCssClass = function() {
    +  return goog.ui.TextareaRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/togglebutton.js b/mapbox-gl/closure-library/closure/goog/ui/togglebutton.js
    new file mode 100644
    index 0000000000..3e9bdb01c7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/togglebutton.js
    @@ -0,0 +1,58 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toggle button control.  Extends {@link goog.ui.Button} by
    + * providing checkbox-like semantics.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToggleButton');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.CustomButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A toggle button, with checkbox-like semantics.  Rendered using
    + * {@link goog.ui.CustomButtonRenderer} by default, though any
    + * {@link goog.ui.ButtonRenderer} would work.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
    + *     decorate the button; defaults to {@link goog.ui.CustomButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Button}
    + */
    +goog.ui.ToggleButton = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Button.call(this, content, opt_renderer ||
    +      goog.ui.CustomButtonRenderer.getInstance(), opt_domHelper);
    +  this.setSupportedState(goog.ui.Component.State.CHECKED, true);
    +};
    +goog.inherits(goog.ui.ToggleButton, goog.ui.Button);
    +
    +
    +// Register a decorator factory function for goog.ui.ToggleButtons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-toggle-button'), function() {
    +      // ToggleButton defaults to using CustomButtonRenderer.
    +      return new goog.ui.ToggleButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbar.js b/mapbox-gl/closure-library/closure/goog/ui/toolbar.js
    new file mode 100644
    index 0000000000..45c25fb5ca
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbar.js
    @@ -0,0 +1,59 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar class that hosts {@link goog.ui.Control}s such as
    + * buttons and menus, along with toolbar-specific renderers of those controls.
    + *
    + * @author attila@google.com (Attila Bodis)
    + * @see ../demos/toolbar.html
    + */
    +
    +goog.provide('goog.ui.Toolbar');
    +
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.ToolbarRenderer');
    +
    +
    +
    +/**
    + * A toolbar class, implemented as a {@link goog.ui.Container} that defaults to
    + * having a horizontal orientation and {@link goog.ui.ToolbarRenderer} as its
    + * renderer.
    + * @param {goog.ui.ToolbarRenderer=} opt_renderer Renderer used to render or
    + *     decorate the toolbar; defaults to {@link goog.ui.ToolbarRenderer}.
    + * @param {?goog.ui.Container.Orientation=} opt_orientation Toolbar orientation;
    + *     defaults to {@code HORIZONTAL}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Container}
    + */
    +goog.ui.Toolbar = function(opt_renderer, opt_orientation, opt_domHelper) {
    +  goog.ui.Container.call(this, opt_orientation, opt_renderer ||
    +      goog.ui.ToolbarRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.Toolbar, goog.ui.Container);
    +
    +
    +/** @override */
    +goog.ui.Toolbar.prototype.handleFocus = function(e) {
    +  goog.ui.Toolbar.base(this, 'handleFocus', e);
    +  // Highlight the first highlightable item on focus via the keyboard for ARIA
    +  // spec compliance. Do not highlight the item if the mouse button is pressed,
    +  // since this method is also called from handleMouseDown when a toolbar button
    +  // is clicked.
    +  if (!this.isMouseButtonPressed()) {
    +    this.highlightFirst();
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.html b/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.html
    new file mode 100644
    index 0000000000..e44f32bc68
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Toolbar
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ToolbarTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="toolbar-wrapper">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.js b/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.js
    new file mode 100644
    index 0000000000..e8b8cbe084
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbar_test.js
    @@ -0,0 +1,92 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ToolbarTest');
    +goog.setTestOnly('goog.ui.ToolbarTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.dom');
    +goog.require('goog.events.EventType');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.events.Event');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Toolbar');
    +goog.require('goog.ui.ToolbarMenuButton');
    +
    +var toolbar;
    +var toolbarWrapper;
    +var buttons;
    +
    +function setUp() {
    +  toolbar = new goog.ui.Toolbar();
    +  toolbarWrapper = goog.dom.getElement('toolbar-wrapper');
    +
    +  // Render and populate the toolbar.
    +  toolbar.render(toolbarWrapper);
    +  var toolbarElem = toolbar.getElement();
    +  var button1 = new goog.ui.ToolbarMenuButton('button 1');
    +  var button2 = new goog.ui.ToolbarMenuButton('button 2');
    +  var button3 = new goog.ui.ToolbarMenuButton('button 3');
    +  button1.render(toolbarElem);
    +  button2.render(toolbarElem);
    +  button3.render(toolbarElem);
    +  toolbar.addChild(button1);
    +  toolbar.addChild(button2);
    +  toolbar.addChild(button3);
    +  buttons = [button1, button2, button3];
    +}
    +
    +function tearDown() {
    +  toolbar.dispose();
    +}
    +
    +function testHighlightFirstOnFocus() {
    +  var firstButton = buttons[0];
    +
    +  // Verify that focusing the toolbar via the keyboard (i.e. no click event)
    +  // highlights the first item and sets it as the active descendant.
    +  goog.testing.events.fireFocusEvent(toolbar.getElement());
    +  assertEquals(0, toolbar.getHighlightedIndex());
    +  assertTrue(firstButton.isHighlighted());
    +  assertEquals(
    +      firstButton.getElement(),
    +      goog.a11y.aria.getActiveDescendant(toolbar.getElement()));
    +
    +  // Verify that removing focus unhighlights the first item and removes it as
    +  // the active descendant.
    +  goog.testing.events.fireBlurEvent(toolbar.getElement());
    +  assertEquals(-1, toolbar.getHighlightedIndex());
    +  assertNull(goog.a11y.aria.getActiveDescendant(toolbar.getElement()));
    +  assertFalse(firstButton.isHighlighted());
    +}
    +
    +function testHighlightSelectedOnClick() {
    +  var firstButton = buttons[0];
    +  var secondButton = buttons[1];
    +
    +  // Verify that mousing over and clicking on a toolbar button selects only the
    +  // correct item.
    +  var mouseover = new goog.testing.events.Event(
    +      goog.events.EventType.MOUSEOVER, secondButton.getElement());
    +  goog.testing.events.fireBrowserEvent(mouseover);
    +  var mousedown = new goog.testing.events.Event(
    +      goog.events.EventType.MOUSEDOWN, toolbar.getElement());
    +  goog.testing.events.fireBrowserEvent(mousedown);
    +  assertEquals(1, toolbar.getHighlightedIndex());
    +  assertTrue(secondButton.isHighlighted());
    +  assertFalse(firstButton.isHighlighted());
    +  assertEquals(
    +      secondButton.getElement(),
    +      goog.a11y.aria.getActiveDescendant(toolbar.getElement()));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarbutton.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarbutton.js
    new file mode 100644
    index 0000000000..1582b62f95
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarbutton.js
    @@ -0,0 +1,54 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar button control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarButton');
    +
    +goog.require('goog.ui.Button');
    +goog.require('goog.ui.ToolbarButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A button control for a toolbar.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
    + *     render or decorate the button; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Button}
    + */
    +goog.ui.ToolbarButton = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.Button.call(this, content, opt_renderer ||
    +      goog.ui.ToolbarButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarButton, goog.ui.Button);
    +
    +
    +// Registers a decorator factory function for toolbar buttons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.ToolbarButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.ToolbarButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarbuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarbuttonrenderer.js
    new file mode 100644
    index 0000000000..155727b657
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarbuttonrenderer.js
    @@ -0,0 +1,57 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for toolbar buttons.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarButtonRenderer');
    +
    +goog.require('goog.ui.CustomButtonRenderer');
    +
    +
    +
    +/**
    + * Toolbar-specific renderer for {@link goog.ui.Button}s, based on {@link
    + * goog.ui.CustomButtonRenderer}.
    + * @constructor
    + * @extends {goog.ui.CustomButtonRenderer}
    + */
    +goog.ui.ToolbarButtonRenderer = function() {
    +  goog.ui.CustomButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ToolbarButtonRenderer, goog.ui.CustomButtonRenderer);
    +goog.addSingletonGetter(goog.ui.ToolbarButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of buttons rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ToolbarButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-toolbar-button');
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of buttons rendered
    + * using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ToolbarButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ToolbarButtonRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubutton.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubutton.js
    new file mode 100644
    index 0000000000..3df6918056
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubutton.js
    @@ -0,0 +1,57 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar color menu button control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarColorMenuButton');
    +
    +goog.require('goog.ui.ColorMenuButton');
    +goog.require('goog.ui.ToolbarColorMenuButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A color menu button control for a toolbar.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked;
    + *     should contain at least one {@link goog.ui.ColorPalette} if present.
    + * @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Optional
    + *     renderer used to render or decorate the button; defaults to
    + *     {@link goog.ui.ToolbarColorMenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.ColorMenuButton}
    + */
    +goog.ui.ToolbarColorMenuButton = function(
    +    content, opt_menu, opt_renderer, opt_domHelper) {
    +  goog.ui.ColorMenuButton.call(this, content, opt_menu, opt_renderer ||
    +      goog.ui.ToolbarColorMenuButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarColorMenuButton, goog.ui.ColorMenuButton);
    +
    +
    +// Registers a decorator factory function for toolbar color menu buttons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-toolbar-color-menu-button'),
    +    function() {
    +      return new goog.ui.ToolbarColorMenuButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer.js
    new file mode 100644
    index 0000000000..23954b4054
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer.js
    @@ -0,0 +1,101 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar-style renderer for {@link goog.ui.ColorMenuButton}.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarColorMenuButtonRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.ColorMenuButtonRenderer');
    +goog.require('goog.ui.MenuButtonRenderer');
    +goog.require('goog.ui.ToolbarMenuButtonRenderer');
    +
    +
    +
    +/**
    + * Toolbar-style renderer for {@link goog.ui.ColorMenuButton}s.
    + * @constructor
    + * @extends {goog.ui.ToolbarMenuButtonRenderer}
    + * @final
    + */
    +goog.ui.ToolbarColorMenuButtonRenderer = function() {
    +  goog.ui.ToolbarMenuButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ToolbarColorMenuButtonRenderer,
    +              goog.ui.ToolbarMenuButtonRenderer);
    +goog.addSingletonGetter(goog.ui.ToolbarColorMenuButtonRenderer);
    +
    +
    +/**
    + * Overrides the superclass implementation by wrapping the caption text or DOM
    + * structure in a color indicator element.  Creates the following DOM structure:
    + *   <div class="goog-inline-block goog-toolbar-menu-button-caption">
    + *     <div class="goog-color-menu-button-indicator">
    + *       Contents...
    + *     </div>
    + *   </div>
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure.
    + * @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
    + * @return {!Element} Caption element.
    + * @see goog.ui.ToolbarColorMenuButtonRenderer#createColorIndicator
    + * @override
    + */
    +goog.ui.ToolbarColorMenuButtonRenderer.prototype.createCaption = function(
    +    content, dom) {
    +  return goog.ui.MenuButtonRenderer.wrapCaption(
    +      goog.ui.ColorMenuButtonRenderer.wrapCaption(content, dom),
    +      this.getCssClass(),
    +      dom);
    +};
    +
    +
    +/**
    + * Takes a color menu button control's root element and a value object
    + * (which is assumed to be a color), and updates the button's DOM to reflect
    + * the new color.  Overrides {@link goog.ui.ButtonRenderer#setValue}.
    + * @param {Element} element The button control's root element (if rendered).
    + * @param {*} value New value; assumed to be a color spec string.
    + * @override
    + */
    +goog.ui.ToolbarColorMenuButtonRenderer.prototype.setValue = function(element,
    +    value) {
    +  if (element) {
    +    goog.ui.ColorMenuButtonRenderer.setCaptionValue(
    +        this.getContentElement(element), value);
    +  }
    +};
    +
    +
    +/**
    + * Initializes the button's DOM when it enters the document.  Overrides the
    + * superclass implementation by making sure the button's color indicator is
    + * initialized.
    + * @param {goog.ui.Control} button goog.ui.ColorMenuButton whose DOM is to be
    + *     initialized as it enters the document.
    + * @override
    + */
    +goog.ui.ToolbarColorMenuButtonRenderer.prototype.initializeDom = function(
    +    button) {
    +  this.setValue(button.getElement(), button.getValue());
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(button.getElement()),
    +      goog.getCssName('goog-toolbar-color-menu-button'));
    +  goog.ui.ToolbarColorMenuButtonRenderer.superClass_.initializeDom.call(this,
    +      button);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.html
    new file mode 100644
    index 0000000000..b1c289c0e4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.html
    @@ -0,0 +1,33 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for ToolbarColorMenuButtonRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ToolbarColorMenuButtonRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <!-- A parent to attach rendered buttons to -->
    +   <div id="parent">
    +   </div>
    +   <!-- A button to decorate -->
    +   <div id="decoratedButton"><div>Foo</div></div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.js
    new file mode 100644
    index 0000000000..7f444f4033
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarcolormenubuttonrenderer_test.js
    @@ -0,0 +1,50 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ToolbarColorMenuButtonRendererTest');
    +goog.setTestOnly('goog.ui.ToolbarColorMenuButtonRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.testing.ui.RendererHarness');
    +goog.require('goog.testing.ui.rendererasserts');
    +goog.require('goog.ui.ToolbarColorMenuButton');
    +goog.require('goog.ui.ToolbarColorMenuButtonRenderer');
    +
    +var harness;
    +
    +function setUp() {
    +  harness = new goog.testing.ui.RendererHarness(
    +      goog.ui.ToolbarColorMenuButtonRenderer.getInstance(),
    +      goog.dom.getElement('parent'),
    +      goog.dom.getElement('decoratedButton'));
    +}
    +
    +function tearDown() {
    +  harness.dispose();
    +}
    +
    +function testEquality() {
    +  harness.attachControlAndRender(
    +      new goog.ui.ToolbarColorMenuButton('Foo'));
    +  harness.attachControlAndDecorate(
    +      new goog.ui.ToolbarColorMenuButton());
    +  harness.assertDomMatches();
    +}
    +
    +function testDoesntCallGetCssClassInConstructor() {
    +  goog.testing.ui.rendererasserts.
    +      assertNoGetCssClassCallsInConstructor(
    +          goog.ui.ToolbarColorMenuButtonRenderer);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubutton.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubutton.js
    new file mode 100644
    index 0000000000..e3ecee2fdc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubutton.js
    @@ -0,0 +1,56 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar menu button control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarMenuButton');
    +
    +goog.require('goog.ui.MenuButton');
    +goog.require('goog.ui.ToolbarMenuButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A menu button control for a toolbar.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked.
    + * @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
    + *     render or decorate the button; defaults to
    + *     {@link goog.ui.ToolbarMenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.MenuButton}
    + */
    +goog.ui.ToolbarMenuButton = function(
    +    content, opt_menu, opt_renderer, opt_domHelper) {
    +  goog.ui.MenuButton.call(this, content, opt_menu, opt_renderer ||
    +      goog.ui.ToolbarMenuButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarMenuButton, goog.ui.MenuButton);
    +
    +
    +// Registers a decorator factory function for toolbar menu buttons.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.ToolbarMenuButtonRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.ToolbarMenuButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubuttonrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubuttonrenderer.js
    new file mode 100644
    index 0000000000..3c5ffe017f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarmenubuttonrenderer.js
    @@ -0,0 +1,57 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar menu button renderer.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarMenuButtonRenderer');
    +
    +goog.require('goog.ui.MenuButtonRenderer');
    +
    +
    +
    +/**
    + * Toolbar-specific renderer for {@link goog.ui.MenuButton}s, based on {@link
    + * goog.ui.MenuButtonRenderer}.
    + * @constructor
    + * @extends {goog.ui.MenuButtonRenderer}
    + */
    +goog.ui.ToolbarMenuButtonRenderer = function() {
    +  goog.ui.MenuButtonRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ToolbarMenuButtonRenderer, goog.ui.MenuButtonRenderer);
    +goog.addSingletonGetter(goog.ui.ToolbarMenuButtonRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of menu buttons rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ToolbarMenuButtonRenderer.CSS_CLASS =
    +    goog.getCssName('goog-toolbar-menu-button');
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of menu buttons
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ToolbarMenuButtonRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ToolbarMenuButtonRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarrenderer.js
    new file mode 100644
    index 0000000000..316814c266
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarrenderer.js
    @@ -0,0 +1,89 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.Toolbar}s.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarRenderer');
    +
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.ui.Container');
    +goog.require('goog.ui.ContainerRenderer');
    +goog.require('goog.ui.Separator');
    +goog.require('goog.ui.ToolbarSeparatorRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.Toolbar}s, based on {@link
    + * goog.ui.ContainerRenderer}.
    + * @constructor
    + * @extends {goog.ui.ContainerRenderer}
    + */
    +goog.ui.ToolbarRenderer = function() {
    +  goog.ui.ContainerRenderer.call(this, goog.a11y.aria.Role.TOOLBAR);
    +};
    +goog.inherits(goog.ui.ToolbarRenderer, goog.ui.ContainerRenderer);
    +goog.addSingletonGetter(goog.ui.ToolbarRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of toolbars rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ToolbarRenderer.CSS_CLASS = goog.getCssName('goog-toolbar');
    +
    +
    +/**
    + * Inspects the element, and creates an instance of {@link goog.ui.Control} or
    + * an appropriate subclass best suited to decorate it.  Overrides the superclass
    + * implementation by recognizing HR elements as separators.
    + * @param {Element} element Element to decorate.
    + * @return {goog.ui.Control?} A new control suitable to decorate the element
    + *     (null if none).
    + * @override
    + */
    +goog.ui.ToolbarRenderer.prototype.getDecoratorForChild = function(element) {
    +  return element.tagName == 'HR' ?
    +      new goog.ui.Separator(goog.ui.ToolbarSeparatorRenderer.getInstance()) :
    +      goog.ui.ToolbarRenderer.superClass_.getDecoratorForChild.call(this,
    +          element);
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of containers
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ToolbarRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ToolbarRenderer.CSS_CLASS;
    +};
    +
    +
    +/**
    + * Returns the default orientation of containers rendered or decorated by this
    + * renderer.  This implementation returns {@code HORIZONTAL}.
    + * @return {goog.ui.Container.Orientation} Default orientation for containers
    + *     created or decorated by this renderer.
    + * @override
    + */
    +goog.ui.ToolbarRenderer.prototype.getDefaultOrientation = function() {
    +  return goog.ui.Container.Orientation.HORIZONTAL;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarselect.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarselect.js
    new file mode 100644
    index 0000000000..008fe9fad1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarselect.js
    @@ -0,0 +1,55 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar select control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarSelect');
    +
    +goog.require('goog.ui.Select');
    +goog.require('goog.ui.ToolbarMenuButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A select control for a toolbar.
    + *
    + * @param {goog.ui.ControlContent} caption Default caption or existing DOM
    + *     structure to display as the button's caption when nothing is selected.
    + * @param {goog.ui.Menu=} opt_menu Menu containing selection options.
    + * @param {goog.ui.MenuButtonRenderer=} opt_renderer Renderer used to
    + *     render or decorate the control; defaults to
    + *     {@link goog.ui.ToolbarMenuButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.Select}
    + */
    +goog.ui.ToolbarSelect = function(
    +    caption, opt_menu, opt_renderer, opt_domHelper) {
    +  goog.ui.Select.call(this, caption, opt_menu, opt_renderer ||
    +      goog.ui.ToolbarMenuButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarSelect, goog.ui.Select);
    +
    +
    +// Registers a decorator factory function for select controls used in toolbars.
    +goog.ui.registry.setDecoratorByClassName(goog.getCssName('goog-toolbar-select'),
    +    function() {
    +      return new goog.ui.ToolbarSelect(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarseparator.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparator.js
    new file mode 100644
    index 0000000000..8bca9587de
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparator.js
    @@ -0,0 +1,53 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar separator control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarSeparator');
    +
    +goog.require('goog.ui.Separator');
    +goog.require('goog.ui.ToolbarSeparatorRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A separator control for a toolbar.
    + *
    + * @param {goog.ui.ToolbarSeparatorRenderer=} opt_renderer Renderer to render or
    + *    decorate the separator; defaults to
    + *     {@link goog.ui.ToolbarSeparatorRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *    document interaction.
    + * @constructor
    + * @extends {goog.ui.Separator}
    + * @final
    + */
    +goog.ui.ToolbarSeparator = function(opt_renderer, opt_domHelper) {
    +  goog.ui.Separator.call(this, opt_renderer ||
    +      goog.ui.ToolbarSeparatorRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarSeparator, goog.ui.Separator);
    +
    +
    +// Registers a decorator factory function for toolbar separators.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.ToolbarSeparatorRenderer.CSS_CLASS,
    +    function() {
    +      return new goog.ui.ToolbarSeparator();
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer.js
    new file mode 100644
    index 0000000000..6c3e9757f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer.js
    @@ -0,0 +1,94 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for toolbar separators.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarSeparatorRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.MenuSeparatorRenderer');
    +
    +
    +
    +/**
    + * Renderer for toolbar separators.
    + * @constructor
    + * @extends {goog.ui.MenuSeparatorRenderer}
    + */
    +goog.ui.ToolbarSeparatorRenderer = function() {
    +  goog.ui.MenuSeparatorRenderer.call(this);
    +};
    +goog.inherits(goog.ui.ToolbarSeparatorRenderer, goog.ui.MenuSeparatorRenderer);
    +goog.addSingletonGetter(goog.ui.ToolbarSeparatorRenderer);
    +
    +
    +/**
    + * Default CSS class to be applied to the root element of components rendered
    + * by this renderer.
    + * @type {string}
    + */
    +goog.ui.ToolbarSeparatorRenderer.CSS_CLASS =
    +    goog.getCssName('goog-toolbar-separator');
    +
    +
    +/**
    + * Returns a styled toolbar separator implemented by the following DOM:
    + * <div class="goog-toolbar-separator goog-inline-block">&nbsp;</div>
    + * Overrides {@link goog.ui.MenuSeparatorRenderer#createDom}.
    + * @param {goog.ui.Control} separator goog.ui.Separator to render.
    + * @return {!Element} Root element for the separator.
    + * @override
    + */
    +goog.ui.ToolbarSeparatorRenderer.prototype.createDom = function(separator) {
    +  // 00A0 is &nbsp;
    +  return separator.getDomHelper().createDom('div',
    +      this.getClassNames(separator).join(' ') +
    +          ' ' + goog.ui.INLINE_BLOCK_CLASSNAME,
    +      '\u00A0');
    +};
    +
    +
    +/**
    + * Takes an existing element, and decorates it with the separator.  Overrides
    + * {@link goog.ui.MenuSeparatorRenderer#decorate}.
    + * @param {goog.ui.Control} separator goog.ui.Separator to decorate the element.
    + * @param {Element} element Element to decorate.
    + * @return {!Element} Decorated element.
    + * @override
    + */
    +goog.ui.ToolbarSeparatorRenderer.prototype.decorate = function(separator,
    +                                                               element) {
    +  element = goog.ui.ToolbarSeparatorRenderer.superClass_.decorate.call(this,
    +      separator, element);
    +  goog.asserts.assert(element);
    +  goog.dom.classlist.add(element, goog.ui.INLINE_BLOCK_CLASSNAME);
    +  return element;
    +};
    +
    +
    +/**
    + * Returns the CSS class to be applied to the root element of components
    + * rendered using this renderer.
    + * @return {string} Renderer-specific CSS class.
    + * @override
    + */
    +goog.ui.ToolbarSeparatorRenderer.prototype.getCssClass = function() {
    +  return goog.ui.ToolbarSeparatorRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.html b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.html
    new file mode 100644
    index 0000000000..dfd2938fb6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.html
    @@ -0,0 +1,31 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author nicksantos@google.com (Nick Santos)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests for ToolbarSeparatorRenderer
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ToolbarSeparatorRendererTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="root">
    +   <!-- A parent to attach rendered buttons to -->
    +   <div id="parent">
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.js b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.js
    new file mode 100644
    index 0000000000..a263d09efc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbarseparatorrenderer_test.js
    @@ -0,0 +1,67 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ToolbarSeparatorRendererTest');
    +goog.setTestOnly('goog.ui.ToolbarSeparatorRendererTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
    +goog.require('goog.ui.ToolbarSeparator');
    +goog.require('goog.ui.ToolbarSeparatorRenderer');
    +
    +var parent;
    +var renderer;
    +var separator;
    +
    +function setUp() {
    +  parent = goog.dom.getElement('parent');
    +  renderer = goog.ui.ToolbarSeparatorRenderer.getInstance();
    +  separator = new goog.ui.ToolbarSeparator(renderer);
    +}
    +
    +function tearDown() {
    +  separator.dispose();
    +  goog.dom.removeChildren(parent);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('Renderer must not be null', renderer);
    +}
    +
    +function testGetCssClass() {
    +  assertEquals('getCssClass() must return expected value',
    +      goog.ui.ToolbarSeparatorRenderer.CSS_CLASS, renderer.getCssClass());
    +}
    +
    +function testCreateDom() {
    +  var element = renderer.createDom(separator);
    +  assertNotNull('Created element must not be null', element);
    +  assertEquals('Created element must be a DIV', 'DIV', element.tagName);
    +  assertSameElements('Created element must have expected class names',
    +      [goog.ui.ToolbarSeparatorRenderer.CSS_CLASS,
    +       // Separators are always in a disabled state.
    +       renderer.getClassForState(goog.ui.Component.State.DISABLED),
    +       goog.ui.INLINE_BLOCK_CLASSNAME],
    +      goog.dom.classlist.get(element));
    +}
    +
    +function testCreateDomWithExtraCssClass() {
    +  separator.addClassName('another-class');
    +  var element = renderer.createDom(separator);
    +  assertContains('Created element must contain extra CSS classes',
    +                 'another-class', goog.dom.classlist.get(element));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/toolbartogglebutton.js b/mapbox-gl/closure-library/closure/goog/ui/toolbartogglebutton.js
    new file mode 100644
    index 0000000000..54c914be32
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/toolbartogglebutton.js
    @@ -0,0 +1,53 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A toolbar toggle button control.
    + *
    + * @author attila@google.com (Attila Bodis)
    + */
    +
    +goog.provide('goog.ui.ToolbarToggleButton');
    +
    +goog.require('goog.ui.ToggleButton');
    +goog.require('goog.ui.ToolbarButtonRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * A toggle button control for a toolbar.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or existing DOM
    + *     structure to display as the button's caption.
    + * @param {goog.ui.ToolbarButtonRenderer=} opt_renderer Optional renderer used
    + *     to render or decorate the button; defaults to
    + *     {@link goog.ui.ToolbarButtonRenderer}.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
    + *     document interaction.
    + * @constructor
    + * @extends {goog.ui.ToggleButton}
    + */
    +goog.ui.ToolbarToggleButton = function(content, opt_renderer, opt_domHelper) {
    +  goog.ui.ToggleButton.call(this, content, opt_renderer ||
    +      goog.ui.ToolbarButtonRenderer.getInstance(), opt_domHelper);
    +};
    +goog.inherits(goog.ui.ToolbarToggleButton, goog.ui.ToggleButton);
    +
    +
    +// Registers a decorator factory function for toggle buttons in toolbars.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.getCssName('goog-toolbar-toggle-button'), function() {
    +      return new goog.ui.ToolbarToggleButton(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tooltip.js b/mapbox-gl/closure-library/closure/goog/ui/tooltip.js
    new file mode 100644
    index 0000000000..18040a1862
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tooltip.js
    @@ -0,0 +1,1017 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Tooltip widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/tooltip.html
    + */
    +
    +goog.provide('goog.ui.Tooltip');
    +goog.provide('goog.ui.Tooltip.CursorTooltipPosition');
    +goog.provide('goog.ui.Tooltip.ElementTooltipPosition');
    +goog.provide('goog.ui.Tooltip.State');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.dom.safe');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.math.Box');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning');
    +goog.require('goog.positioning.AnchoredPosition');
    +goog.require('goog.positioning.Corner');
    +goog.require('goog.positioning.Overflow');
    +goog.require('goog.positioning.OverflowStatus');
    +goog.require('goog.positioning.ViewportPosition');
    +goog.require('goog.structs.Set');
    +goog.require('goog.style');
    +goog.require('goog.ui.Popup');
    +goog.require('goog.ui.PopupBase');
    +
    +
    +
    +/**
    + * Tooltip widget. Can be attached to one or more elements and is shown, with a
    + * slight delay, when the the cursor is over the element or the element gains
    + * focus.
    + *
    + * @param {Element|string=} opt_el Element to display tooltip for, either
    + *     element reference or string id.
    + * @param {?string=} opt_str Text message to display in tooltip.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Popup}
    + */
    +goog.ui.Tooltip = function(opt_el, opt_str, opt_domHelper) {
    +  /**
    +   * Dom Helper
    +   * @type {goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || (opt_el ?
    +      goog.dom.getDomHelper(goog.dom.getElement(opt_el)) :
    +      goog.dom.getDomHelper());
    +
    +  goog.ui.Popup.call(this, this.dom_.createDom(
    +      'div', {'style': 'position:absolute;display:none;'}));
    +
    +  /**
    +   * Cursor position relative to the page.
    +   * @type {!goog.math.Coordinate}
    +   * @protected
    +   */
    +  this.cursorPosition = new goog.math.Coordinate(1, 1);
    +
    +  /**
    +   * Elements this widget is attached to.
    +   * @type {goog.structs.Set}
    +   * @private
    +   */
    +  this.elements_ = new goog.structs.Set();
    +
    +  // Attach to element, if specified
    +  if (opt_el) {
    +    this.attach(opt_el);
    +  }
    +
    +  // Set message, if specified.
    +  if (opt_str != null) {
    +    this.setText(opt_str);
    +  }
    +};
    +goog.inherits(goog.ui.Tooltip, goog.ui.Popup);
    +goog.tagUnsealableClass(goog.ui.Tooltip);
    +
    +
    +/**
    + * List of active (open) tooltip widgets. Used to prevent multiple tooltips
    + * from appearing at once.
    + *
    + * @type {!Array<goog.ui.Tooltip>}
    + * @private
    + */
    +goog.ui.Tooltip.activeInstances_ = [];
    +
    +
    +/**
    + * Active element reference. Used by the delayed show functionality to keep
    + * track of the element the mouse is over or the element with focus.
    + * @type {Element}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.activeEl_ = null;
    +
    +
    +/**
    + * CSS class name for tooltip.
    + *
    + * @type {string}
    + */
    +goog.ui.Tooltip.prototype.className = goog.getCssName('goog-tooltip');
    +
    +
    +/**
    + * Delay in milliseconds since the last mouseover or mousemove before the
    + * tooltip is displayed for an element.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.showDelayMs_ = 500;
    +
    +
    +/**
    + * Timer for when to show.
    + *
    + * @type {number|undefined}
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.showTimer;
    +
    +
    +/**
    + * Delay in milliseconds before tooltips are hidden.
    + *
    + * @type {number}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.hideDelayMs_ = 0;
    +
    +
    +/**
    + * Timer for when to hide.
    + *
    + * @type {number|undefined}
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.hideTimer;
    +
    +
    +/**
    + * Element that triggered the tooltip.  Note that if a second element triggers
    + * this tooltip, anchor becomes that second element, even if its show is
    + * cancelled and the original tooltip survives.
    + *
    + * @type {Element|undefined}
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.anchor;
    +
    +
    +/**
    + * Possible states for the tooltip to be in.
    + * @enum {number}
    + */
    +goog.ui.Tooltip.State = {
    +  INACTIVE: 0,
    +  WAITING_TO_SHOW: 1,
    +  SHOWING: 2,
    +  WAITING_TO_HIDE: 3,
    +  UPDATING: 4 // waiting to show new hovercard while old one still showing.
    +};
    +
    +
    +/**
    + * Popup activation types. Used to select a positioning strategy.
    + * @enum {number}
    + */
    +goog.ui.Tooltip.Activation = {
    +  CURSOR: 0,
    +  FOCUS: 1
    +};
    +
    +
    +/**
    + * Whether the anchor has seen the cursor move or has received focus since the
    + * tooltip was last shown. Used to ignore mouse over events triggered by view
    + * changes and UI updates.
    + * @type {boolean|undefined}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.seenInteraction_;
    +
    +
    +/**
    + * Whether the cursor must have moved before the tooltip will be shown.
    + * @type {boolean|undefined}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.requireInteraction_;
    +
    +
    +/**
    + * If this tooltip's element contains another tooltip that becomes active, this
    + * property identifies that tooltip so that we can check if this tooltip should
    + * not be hidden because the nested tooltip is active.
    + * @type {goog.ui.Tooltip}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.childTooltip_;
    +
    +
    +/**
    + * If this tooltip is inside another tooltip's element, then it may have
    + * prevented that tooltip from hiding.  When this tooltip hides, we'll need
    + * to check if the parent should be hidden as well.
    + * @type {goog.ui.Tooltip}
    + * @private
    + */
    +goog.ui.Tooltip.prototype.parentTooltip_;
    +
    +
    +/**
    + * Returns the dom helper that is being used on this component.
    + * @return {goog.dom.DomHelper} The dom helper used on this component.
    + */
    +goog.ui.Tooltip.prototype.getDomHelper = function() {
    +  return this.dom_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.Tooltip} Active tooltip in a child element, or null if none.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.getChildTooltip = function() {
    +  return this.childTooltip_;
    +};
    +
    +
    +/**
    + * Attach to element. Tooltip will be displayed when the cursor is over the
    + * element or when the element has been active for a few milliseconds.
    + *
    + * @param {Element|string} el Element to display tooltip for, either element
    + *                            reference or string id.
    + */
    +goog.ui.Tooltip.prototype.attach = function(el) {
    +  el = goog.dom.getElement(el);
    +
    +  this.elements_.add(el);
    +  goog.events.listen(el, goog.events.EventType.MOUSEOVER,
    +                     this.handleMouseOver, false, this);
    +  goog.events.listen(el, goog.events.EventType.MOUSEOUT,
    +                     this.handleMouseOutAndBlur, false, this);
    +  goog.events.listen(el, goog.events.EventType.MOUSEMOVE,
    +                     this.handleMouseMove, false, this);
    +  goog.events.listen(el, goog.events.EventType.FOCUS,
    +                     this.handleFocus, false, this);
    +  goog.events.listen(el, goog.events.EventType.BLUR,
    +                     this.handleMouseOutAndBlur, false, this);
    +};
    +
    +
    +/**
    + * Detach from element(s).
    + *
    + * @param {Element|string=} opt_el Element to detach from, either element
    + *                                reference or string id. If no element is
    + *                                specified all are detached.
    + */
    +goog.ui.Tooltip.prototype.detach = function(opt_el) {
    +  if (opt_el) {
    +    var el = goog.dom.getElement(opt_el);
    +    this.detachElement_(el);
    +    this.elements_.remove(el);
    +  } else {
    +    var a = this.elements_.getValues();
    +    for (var el, i = 0; el = a[i]; i++) {
    +      this.detachElement_(el);
    +    }
    +    this.elements_.clear();
    +  }
    +};
    +
    +
    +/**
    + * Detach from element.
    + *
    + * @param {Element} el Element to detach from.
    + * @private
    + */
    +goog.ui.Tooltip.prototype.detachElement_ = function(el) {
    +  goog.events.unlisten(el, goog.events.EventType.MOUSEOVER,
    +                       this.handleMouseOver, false, this);
    +  goog.events.unlisten(el, goog.events.EventType.MOUSEOUT,
    +                       this.handleMouseOutAndBlur, false, this);
    +  goog.events.unlisten(el, goog.events.EventType.MOUSEMOVE,
    +                       this.handleMouseMove, false, this);
    +  goog.events.unlisten(el, goog.events.EventType.FOCUS,
    +                       this.handleFocus, false, this);
    +  goog.events.unlisten(el, goog.events.EventType.BLUR,
    +                       this.handleMouseOutAndBlur, false, this);
    +};
    +
    +
    +/**
    + * Sets delay in milliseconds before tooltip is displayed for an element.
    + *
    + * @param {number} delay The delay in milliseconds.
    + */
    +goog.ui.Tooltip.prototype.setShowDelayMs = function(delay) {
    +  this.showDelayMs_ = delay;
    +};
    +
    +
    +/**
    + * @return {number} The delay in milliseconds before tooltip is displayed for an
    + *     element.
    + */
    +goog.ui.Tooltip.prototype.getShowDelayMs = function() {
    +  return this.showDelayMs_;
    +};
    +
    +
    +/**
    + * Sets delay in milliseconds before tooltip is hidden once the cursor leavs
    + * the element.
    + *
    + * @param {number} delay The delay in milliseconds.
    + */
    +goog.ui.Tooltip.prototype.setHideDelayMs = function(delay) {
    +  this.hideDelayMs_ = delay;
    +};
    +
    +
    +/**
    + * @return {number} The delay in milliseconds before tooltip is hidden once the
    + *     cursor leaves the element.
    + */
    +goog.ui.Tooltip.prototype.getHideDelayMs = function() {
    +  return this.hideDelayMs_;
    +};
    +
    +
    +/**
    + * Sets tooltip message as plain text.
    + *
    + * @param {string} str Text message to display in tooltip.
    + */
    +goog.ui.Tooltip.prototype.setText = function(str) {
    +  goog.dom.setTextContent(this.getElement(), str);
    +};
    +
    +
    +// TODO(user): Deprecate in favor of setSafeHtml, once developer docs on.
    +/**
    + * Sets tooltip message as HTML markup.
    + * using goog.html.SafeHtml are in place.
    + *
    + * @param {string} str HTML message to display in tooltip.
    + */
    +goog.ui.Tooltip.prototype.setHtml = function(str) {
    +  this.setSafeHtml(goog.html.legacyconversions.safeHtmlFromString(str));
    +};
    +
    +
    +/**
    + * Sets tooltip message as HTML markup.
    + * @param {!goog.html.SafeHtml} html HTML message to display in tooltip.
    + */
    +goog.ui.Tooltip.prototype.setSafeHtml = function(html) {
    +  var element = this.getElement();
    +  if (element) {
    +    goog.dom.safe.setInnerHtml(element, html);
    +  }
    +};
    +
    +
    +/**
    + * Sets tooltip element.
    + *
    + * @param {Element} el HTML element to use as the tooltip.
    + * @override
    + */
    +goog.ui.Tooltip.prototype.setElement = function(el) {
    +  var oldElement = this.getElement();
    +  if (oldElement) {
    +    goog.dom.removeNode(oldElement);
    +  }
    +  goog.ui.Tooltip.superClass_.setElement.call(this, el);
    +  if (el) {
    +    var body = this.dom_.getDocument().body;
    +    body.insertBefore(el, body.lastChild);
    +  }
    +};
    +
    +
    +/**
    + * @return {string} The tooltip message as plain text.
    + */
    +goog.ui.Tooltip.prototype.getText = function() {
    +  return goog.dom.getTextContent(this.getElement());
    +};
    +
    +
    +/**
    + * @return {string} The tooltip message as HTML as plain string.
    + */
    +goog.ui.Tooltip.prototype.getHtml = function() {
    +  return this.getElement().innerHTML;
    +};
    +
    +
    +/**
    + * @return {goog.ui.Tooltip.State} Current state of tooltip.
    + */
    +goog.ui.Tooltip.prototype.getState = function() {
    +  return this.showTimer ?
    +             (this.isVisible() ? goog.ui.Tooltip.State.UPDATING :
    +                                 goog.ui.Tooltip.State.WAITING_TO_SHOW) :
    +         this.hideTimer ? goog.ui.Tooltip.State.WAITING_TO_HIDE :
    +         this.isVisible() ? goog.ui.Tooltip.State.SHOWING :
    +         goog.ui.Tooltip.State.INACTIVE;
    +};
    +
    +
    +/**
    + * Sets whether tooltip requires the mouse to have moved or the anchor receive
    + * focus before the tooltip will be shown.
    + * @param {boolean} requireInteraction Whether tooltip should require some user
    + *     interaction before showing tooltip.
    + */
    +goog.ui.Tooltip.prototype.setRequireInteraction = function(requireInteraction) {
    +  this.requireInteraction_ = requireInteraction;
    +};
    +
    +
    +/**
    + * Returns true if the coord is in the tooltip.
    + * @param {goog.math.Coordinate} coord Coordinate being tested.
    + * @return {boolean} Whether the coord is in the tooltip.
    + */
    +goog.ui.Tooltip.prototype.isCoordinateInTooltip = function(coord) {
    +  // Check if coord is inside the the tooltip
    +  if (!this.isVisible()) {
    +    return false;
    +  }
    +
    +  var offset = goog.style.getPageOffset(this.getElement());
    +  var size = goog.style.getSize(this.getElement());
    +  return offset.x <= coord.x && coord.x <= offset.x + size.width &&
    +         offset.y <= coord.y && coord.y <= offset.y + size.height;
    +};
    +
    +
    +/**
    + * Called before the popup is shown.
    + *
    + * @return {boolean} Whether tooltip should be shown.
    + * @protected
    + * @override
    + */
    +goog.ui.Tooltip.prototype.onBeforeShow = function() {
    +  if (!goog.ui.PopupBase.prototype.onBeforeShow.call(this)) {
    +    return false;
    +  }
    +
    +  // Hide all open tooltips except if this tooltip is triggered by an element
    +  // inside another tooltip.
    +  if (this.anchor) {
    +    for (var tt, i = 0; tt = goog.ui.Tooltip.activeInstances_[i]; i++) {
    +      if (!goog.dom.contains(tt.getElement(), this.anchor)) {
    +        tt.setVisible(false);
    +      }
    +    }
    +  }
    +  goog.array.insert(goog.ui.Tooltip.activeInstances_, this);
    +
    +  var element = this.getElement();
    +  element.className = this.className;
    +  this.clearHideTimer();
    +
    +  // Register event handlers for tooltip. Used to prevent the tooltip from
    +  // closing if the cursor is over the tooltip rather then the element that
    +  // triggered it.
    +  goog.events.listen(element, goog.events.EventType.MOUSEOVER,
    +                     this.handleTooltipMouseOver, false, this);
    +  goog.events.listen(element, goog.events.EventType.MOUSEOUT,
    +                     this.handleTooltipMouseOut, false, this);
    +
    +  this.clearShowTimer();
    +  return true;
    +};
    +
    +
    +/**
    + * Called after the popup is hidden.
    + *
    + * @protected
    + * @suppress {underscore|visibility}
    + * @override
    + */
    +goog.ui.Tooltip.prototype.onHide_ = function() {
    +  goog.array.remove(goog.ui.Tooltip.activeInstances_, this);
    +
    +  // Hide all open tooltips triggered by an element inside this tooltip.
    +  var element = this.getElement();
    +  for (var tt, i = 0; tt = goog.ui.Tooltip.activeInstances_[i]; i++) {
    +    if (tt.anchor && goog.dom.contains(element, tt.anchor)) {
    +      tt.setVisible(false);
    +    }
    +  }
    +
    +  // If this tooltip is inside another tooltip, start hide timer for that
    +  // tooltip in case this tooltip was the only reason it was still showing.
    +  if (this.parentTooltip_) {
    +    this.parentTooltip_.startHideTimer();
    +  }
    +
    +  goog.events.unlisten(element, goog.events.EventType.MOUSEOVER,
    +                       this.handleTooltipMouseOver, false, this);
    +  goog.events.unlisten(element, goog.events.EventType.MOUSEOUT,
    +                       this.handleTooltipMouseOut, false, this);
    +
    +  this.anchor = undefined;
    +  // If we are still waiting to show a different hovercard, don't abort it
    +  // because you think you haven't seen a mouse move:
    +  if (this.getState() == goog.ui.Tooltip.State.INACTIVE) {
    +    this.seenInteraction_ = false;
    +  }
    +
    +  goog.ui.PopupBase.prototype.onHide_.call(this);
    +};
    +
    +
    +/**
    + * Called by timer from mouse over handler. Shows tooltip if cursor is still
    + * over the same element.
    + *
    + * @param {Element} el Element to show tooltip for.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
    + *     at.
    + */
    +goog.ui.Tooltip.prototype.maybeShow = function(el, opt_pos) {
    +  // Assert that the mouse is still over the same element, and that we have not
    +  // detached from the anchor in the meantime.
    +  if (this.anchor == el && this.elements_.contains(this.anchor)) {
    +    if (this.seenInteraction_ || !this.requireInteraction_) {
    +      // If it is currently showing, then hide it, and abort if it doesn't hide.
    +      this.setVisible(false);
    +      if (!this.isVisible()) {
    +        this.positionAndShow_(el, opt_pos);
    +      }
    +    } else {
    +      this.anchor = undefined;
    +    }
    +  }
    +  this.showTimer = undefined;
    +};
    +
    +
    +/**
    + * @return {goog.structs.Set} Elements this widget is attached to.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.getElements = function() {
    +  return this.elements_;
    +};
    +
    +
    +/**
    + * @return {Element} Active element reference.
    + */
    +goog.ui.Tooltip.prototype.getActiveElement = function() {
    +  return this.activeEl_;
    +};
    +
    +
    +/**
    + * @param {Element} activeEl Active element reference.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.setActiveElement = function(activeEl) {
    +  this.activeEl_ = activeEl;
    +};
    +
    +
    +/**
    + * Shows tooltip for a specific element.
    + *
    + * @param {Element} el Element to show tooltip for.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
    + *     at.
    + */
    +goog.ui.Tooltip.prototype.showForElement = function(el, opt_pos) {
    +  this.attach(el);
    +  this.activeEl_ = el;
    +
    +  this.positionAndShow_(el, opt_pos);
    +};
    +
    +
    +/**
    + * Sets tooltip position and shows it.
    + *
    + * @param {Element} el Element to show tooltip for.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
    + *     at.
    + * @private
    + */
    +goog.ui.Tooltip.prototype.positionAndShow_ = function(el, opt_pos) {
    +  this.anchor = el;
    +  this.setPosition(opt_pos ||
    +      this.getPositioningStrategy(goog.ui.Tooltip.Activation.CURSOR));
    +  this.setVisible(true);
    +};
    +
    +
    +/**
    + * Called by timer from mouse out handler. Hides tooltip if cursor is still
    + * outside element and tooltip, or if a child of tooltip has the focus.
    + * @param {Element} el Tooltip's anchor when hide timer was started.
    + */
    +goog.ui.Tooltip.prototype.maybeHide = function(el) {
    +  this.hideTimer = undefined;
    +  if (el == this.anchor) {
    +    if ((this.activeEl_ == null || (this.activeEl_ != this.getElement() &&
    +        !this.elements_.contains(this.activeEl_))) &&
    +        !this.hasActiveChild()) {
    +      this.setVisible(false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether tooltip element contains an active child tooltip,
    + *     and should thus not be hidden.  When the child tooltip is hidden, it
    + *     will check if the parent should be hidden, too.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.hasActiveChild = function() {
    +  return !!(this.childTooltip_ && this.childTooltip_.activeEl_);
    +};
    +
    +
    +/**
    + * Saves the current mouse cursor position to {@code this.cursorPosition}.
    + * @param {goog.events.BrowserEvent} event MOUSEOVER or MOUSEMOVE event.
    + * @private
    + */
    +goog.ui.Tooltip.prototype.saveCursorPosition_ = function(event) {
    +  var scroll = this.dom_.getDocumentScroll();
    +  this.cursorPosition.x = event.clientX + scroll.x;
    +  this.cursorPosition.y = event.clientY + scroll.y;
    +};
    +
    +
    +/**
    + * Handler for mouse over events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleMouseOver = function(event) {
    +  var el = this.getAnchorFromElement(/** @type {Element} */ (event.target));
    +  this.activeEl_ = /** @type {Element} */ (el);
    +  this.clearHideTimer();
    +  if (el != this.anchor) {
    +    this.anchor = el;
    +    this.startShowTimer(/** @type {Element} */ (el));
    +    this.checkForParentTooltip_();
    +    this.saveCursorPosition_(event);
    +  }
    +};
    +
    +
    +/**
    + * Find anchor containing the given element, if any.
    + *
    + * @param {Element} el Element that triggered event.
    + * @return {Element} Element in elements_ array that contains given element,
    + *     or null if not found.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.getAnchorFromElement = function(el) {
    +  // FireFox has a bug where mouse events relating to <input> elements are
    +  // sometimes duplicated (often in FF2, rarely in FF3): once for the
    +  // <input> element and once for a magic hidden <div> element.  Javascript
    +  // code does not have sufficient permissions to read properties on that
    +  // magic element and thus will throw an error in this call to
    +  // getAnchorFromElement_().  In that case we swallow the error.
    +  // See https://bugzilla.mozilla.org/show_bug.cgi?id=330961
    +  try {
    +    while (el && !this.elements_.contains(el)) {
    +      el = /** @type {Element} */ (el.parentNode);
    +    }
    +    return el;
    +  } catch (e) {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Handler for mouse move events.
    + *
    + * @param {goog.events.BrowserEvent} event MOUSEMOVE event.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleMouseMove = function(event) {
    +  this.saveCursorPosition_(event);
    +  this.seenInteraction_ = true;
    +};
    +
    +
    +/**
    + * Handler for focus events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleFocus = function(event) {
    +  var el = this.getAnchorFromElement(/** @type {Element} */ (event.target));
    +  this.activeEl_ = el;
    +  this.seenInteraction_ = true;
    +
    +  if (this.anchor != el) {
    +    this.anchor = el;
    +    var pos = this.getPositioningStrategy(goog.ui.Tooltip.Activation.FOCUS);
    +    this.clearHideTimer();
    +    this.startShowTimer(/** @type {Element} */ (el), pos);
    +
    +    this.checkForParentTooltip_();
    +  }
    +};
    +
    +
    +/**
    + * Return a Position instance for repositioning the tooltip. Override in
    + * subclasses to customize the way repositioning is done.
    + *
    + * @param {goog.ui.Tooltip.Activation} activationType Information about what
    + *    kind of event caused the popup to be shown.
    + * @return {!goog.positioning.AbstractPosition} The position object used
    + *    to position the tooltip.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.getPositioningStrategy = function(activationType) {
    +  if (activationType == goog.ui.Tooltip.Activation.CURSOR) {
    +    var coord = this.cursorPosition.clone();
    +    return new goog.ui.Tooltip.CursorTooltipPosition(coord);
    +  }
    +  return new goog.ui.Tooltip.ElementTooltipPosition(this.activeEl_);
    +};
    +
    +
    +/**
    + * Looks for an active tooltip whose element contains this tooltip's anchor.
    + * This allows us to prevent hides until they are really necessary.
    + *
    + * @private
    + */
    +goog.ui.Tooltip.prototype.checkForParentTooltip_ = function() {
    +  if (this.anchor) {
    +    for (var tt, i = 0; tt = goog.ui.Tooltip.activeInstances_[i]; i++) {
    +      if (goog.dom.contains(tt.getElement(), this.anchor)) {
    +        tt.childTooltip_ = this;
    +        this.parentTooltip_ = tt;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for mouse out and blur events.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleMouseOutAndBlur = function(event) {
    +  var el = this.getAnchorFromElement(/** @type {Element} */ (event.target));
    +  var elTo = this.getAnchorFromElement(
    +      /** @type {Element} */ (event.relatedTarget));
    +  if (el == elTo) {
    +    // We haven't really left the anchor, just moved from one child to
    +    // another.
    +    return;
    +  }
    +
    +  if (el == this.activeEl_) {
    +    this.activeEl_ = null;
    +  }
    +
    +  this.clearShowTimer();
    +  this.seenInteraction_ = false;
    +  if (this.isVisible() && (!event.relatedTarget ||
    +      !goog.dom.contains(this.getElement(), event.relatedTarget))) {
    +    this.startHideTimer();
    +  } else {
    +    this.anchor = undefined;
    +  }
    +};
    +
    +
    +/**
    + * Handler for mouse over events for the tooltip element.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleTooltipMouseOver = function(event) {
    +  var element = this.getElement();
    +  if (this.activeEl_ != element) {
    +    this.clearHideTimer();
    +    this.activeEl_ = element;
    +  }
    +};
    +
    +
    +/**
    + * Handler for mouse out events for the tooltip element.
    + *
    + * @param {goog.events.BrowserEvent} event Event object.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.handleTooltipMouseOut = function(event) {
    +  var element = this.getElement();
    +  if (this.activeEl_ == element && (!event.relatedTarget ||
    +      !goog.dom.contains(element, event.relatedTarget))) {
    +    this.activeEl_ = null;
    +    this.startHideTimer();
    +  }
    +};
    +
    +
    +/**
    + * Helper method, starts timer that calls maybeShow. Parameters are passed to
    + * the maybeShow method.
    + *
    + * @param {Element} el Element to show tooltip for.
    + * @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
    + *     at.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.startShowTimer = function(el, opt_pos) {
    +  if (!this.showTimer) {
    +    this.showTimer = goog.Timer.callOnce(
    +        goog.bind(this.maybeShow, this, el, opt_pos), this.showDelayMs_);
    +  }
    +};
    +
    +
    +/**
    + * Helper method called to clear the show timer.
    + *
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.clearShowTimer = function() {
    +  if (this.showTimer) {
    +    goog.Timer.clear(this.showTimer);
    +    this.showTimer = undefined;
    +  }
    +};
    +
    +
    +/**
    + * Helper method called to start the close timer.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.startHideTimer = function() {
    +  if (this.getState() == goog.ui.Tooltip.State.SHOWING) {
    +    this.hideTimer = goog.Timer.callOnce(
    +        goog.bind(this.maybeHide, this, this.anchor), this.getHideDelayMs());
    +  }
    +};
    +
    +
    +/**
    + * Helper method called to clear the close timer.
    + * @protected
    + */
    +goog.ui.Tooltip.prototype.clearHideTimer = function() {
    +  if (this.hideTimer) {
    +    goog.Timer.clear(this.hideTimer);
    +    this.hideTimer = undefined;
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.Tooltip.prototype.disposeInternal = function() {
    +  this.setVisible(false);
    +  this.clearShowTimer();
    +  this.detach();
    +  if (this.getElement()) {
    +    goog.dom.removeNode(this.getElement());
    +  }
    +  this.activeEl_ = null;
    +  delete this.dom_;
    +  goog.ui.Tooltip.superClass_.disposeInternal.call(this);
    +};
    +
    +
    +
    +/**
    + * Popup position implementation that positions the popup (the tooltip in this
    + * case) based on the cursor position. It's positioned below the cursor to the
    + * right if there's enough room to fit all of it inside the Viewport. Otherwise
    + * it's displayed as far right as possible either above or below the element.
    + *
    + * Used to position tooltips triggered by the cursor.
    + *
    + * @param {number|!goog.math.Coordinate} arg1 Left position or coordinate.
    + * @param {number=} opt_arg2 Top position.
    + * @constructor
    + * @extends {goog.positioning.ViewportPosition}
    + * @final
    + */
    +goog.ui.Tooltip.CursorTooltipPosition = function(arg1, opt_arg2) {
    +  goog.positioning.ViewportPosition.call(this, arg1, opt_arg2);
    +};
    +goog.inherits(goog.ui.Tooltip.CursorTooltipPosition,
    +              goog.positioning.ViewportPosition);
    +
    +
    +/**
    + * Repositions the popup based on cursor position.
    + *
    + * @param {Element} element The DOM element of the popup.
    + * @param {goog.positioning.Corner} popupCorner The corner of the popup element
    + *     that that should be positioned adjacent to the anchorElement.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @override
    + */
    +goog.ui.Tooltip.CursorTooltipPosition.prototype.reposition = function(
    +    element, popupCorner, opt_margin) {
    +  var viewportElt = goog.style.getClientViewportElement(element);
    +  var viewport = goog.style.getVisibleRectForElement(viewportElt);
    +  var margin = opt_margin ? new goog.math.Box(opt_margin.top + 10,
    +      opt_margin.right, opt_margin.bottom, opt_margin.left + 10) :
    +      new goog.math.Box(10, 0, 0, 10);
    +
    +  if (goog.positioning.positionAtCoordinate(this.coordinate, element,
    +      goog.positioning.Corner.TOP_START, margin, viewport,
    +      goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y
    +      ) & goog.positioning.OverflowStatus.FAILED) {
    +    goog.positioning.positionAtCoordinate(this.coordinate, element,
    +        goog.positioning.Corner.TOP_START, margin, viewport,
    +        goog.positioning.Overflow.ADJUST_X |
    +            goog.positioning.Overflow.ADJUST_Y);
    +  }
    +};
    +
    +
    +
    +/**
    + * Popup position implementation that positions the popup (the tooltip in this
    + * case) based on the element position. It's positioned below the element to the
    + * right if there's enough room to fit all of it inside the Viewport. Otherwise
    + * it's displayed as far right as possible either above or below the element.
    + *
    + * Used to position tooltips triggered by focus changes.
    + *
    + * @param {Element} element The element to anchor the popup at.
    + * @constructor
    + * @extends {goog.positioning.AnchoredPosition}
    + */
    +goog.ui.Tooltip.ElementTooltipPosition = function(element) {
    +  goog.positioning.AnchoredPosition.call(this, element,
    +      goog.positioning.Corner.BOTTOM_RIGHT);
    +};
    +goog.inherits(goog.ui.Tooltip.ElementTooltipPosition,
    +              goog.positioning.AnchoredPosition);
    +
    +
    +/**
    + * Repositions the popup based on element position.
    + *
    + * @param {Element} element The DOM element of the popup.
    + * @param {goog.positioning.Corner} popupCorner The corner of the popup element
    + *     that should be positioned adjacent to the anchorElement.
    + * @param {goog.math.Box=} opt_margin A margin specified in pixels.
    + * @override
    + */
    +goog.ui.Tooltip.ElementTooltipPosition.prototype.reposition = function(
    +    element, popupCorner, opt_margin) {
    +  var offset = new goog.math.Coordinate(10, 0);
    +
    +  if (goog.positioning.positionAtAnchor(this.element, this.corner, element,
    +      popupCorner, offset, opt_margin,
    +      goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y
    +      ) & goog.positioning.OverflowStatus.FAILED) {
    +    goog.positioning.positionAtAnchor(this.element,
    +        goog.positioning.Corner.TOP_RIGHT, element,
    +        goog.positioning.Corner.BOTTOM_LEFT, offset, opt_margin,
    +        goog.positioning.Overflow.ADJUST_X |
    +            goog.positioning.Overflow.ADJUST_Y);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.html b/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.html
    new file mode 100644
    index 0000000000..fe616e2cfd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!-- Author:  attila@google.com (Attila Bodis) -->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Tooltip
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TooltipTest');
    +  </script>
    + </head>
    + <body>
    +  <iframe id="testframe" style="width: 200px; height: 200px;" src="blank_test_helper.html">
    +  </iframe>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.js b/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.js
    new file mode 100644
    index 0000000000..4aa462a067
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tooltip_test.js
    @@ -0,0 +1,394 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TooltipTest');
    +goog.setTestOnly('goog.ui.TooltipTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventType');
    +goog.require('goog.html.testing');
    +goog.require('goog.math.Coordinate');
    +goog.require('goog.positioning.AbsolutePosition');
    +goog.require('goog.style');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.TestQueue');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.PopupBase');
    +goog.require('goog.ui.Tooltip');
    +goog.require('goog.userAgent');
    +
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +
    +/**
    + * A subclass of Tooltip that overrides {@code getPositioningStrategy}
    + * for testing purposes.
    + * @constructor
    + */
    +function TestTooltip(el, text, dom) {
    +  goog.ui.Tooltip.call(this, el, text, dom);
    +}
    +goog.inherits(TestTooltip, goog.ui.Tooltip);
    +
    +
    +/** @override */
    +TestTooltip.prototype.getPositioningStrategy = function() {
    +  return new goog.positioning.AbsolutePosition(13, 17);
    +};
    +
    +
    +var tt, clock, handler, eventQueue, dom;
    +
    +// Allow positions to be off by one in gecko as it reports scrolling
    +// offsets in steps of 2.
    +var ALLOWED_OFFSET = goog.userAgent.GECKO ? 1 : 0;
    +
    +function setUp() {
    +  // We get access denied error when accessing the iframe in IE on the farm
    +  // as IE doesn't have the same window size issues as firefox on the farm
    +  // we bypass the iframe and use the current document instead.
    +  if (goog.userAgent.IE) {
    +    dom = goog.dom.getDomHelper(document);
    +  } else {
    +    var frame = document.getElementById('testframe');
    +    var doc = goog.dom.getFrameContentDocument(frame);
    +    dom = goog.dom.getDomHelper(doc);
    +  }
    +
    +  // Host elements in fixed size iframe to avoid window size problems when
    +  // running under Selenium.
    +  dom.getDocument().body.innerHTML =
    +      '<p id="notpopup">Content</p>' +
    +      '<p id="hovertarget">Hover Here For Popup</p>' +
    +      '<p id="second">Secondary target</p>';
    +
    +  tt = new goog.ui.Tooltip(undefined, undefined, dom);
    +  tt.setElement(dom.createDom('div', {id: 'popup',
    +    style: 'visibility:hidden'},
    +  'Hello'));
    +  clock = new goog.testing.MockClock(true);
    +  eventQueue = new goog.testing.TestQueue();
    +  handler = new goog.events.EventHandler(eventQueue);
    +  handler.listen(tt, goog.ui.PopupBase.EventType.SHOW, eventQueue.enqueue);
    +  handler.listen(tt, goog.ui.PopupBase.EventType.HIDE, eventQueue.enqueue);
    +
    +  // Reset global flags to their defaults.
    +  /** @suppress {missingRequire} */
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', true);
    +}
    +
    +function tearDown() {
    +  // tooltip needs to be hidden as well as disposed of so that it doesn't
    +  // leave global state hanging around to trip up other tests.
    +  tt.onHide_();
    +  tt.dispose();
    +  clock.uninstall();
    +  handler.removeAll();
    +}
    +
    +function testConstructor() {
    +  var element = tt.getElement();
    +  assertNotNull('Tooltip should have non-null element', element);
    +  assertEquals('Tooltip element should be the DIV we created',
    +      dom.getElement('popup'), element);
    +  assertEquals('Tooltip element should be a child of the document body',
    +      dom.getDocument().body, element.parentNode);
    +}
    +
    +function testTooltipShowsAndHides() {
    +  var hoverTarget = dom.getElement('hovertarget');
    +  var elsewhere = dom.getElement('notpopup');
    +  var element = tt.getElement();
    +  var position = new goog.math.Coordinate(5, 5);
    +  assertNotNull('Tooltip should have non-null element', element);
    +  assertEquals('Initial state should be inactive',
    +               goog.ui.Tooltip.State.INACTIVE, tt.getState());
    +  tt.attach(hoverTarget);
    +  tt.setShowDelayMs(100);
    +  tt.setHideDelayMs(50);
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere, position);
    +  assertEquals(goog.ui.Tooltip.State.WAITING_TO_SHOW, tt.getState());
    +  clock.tick(101);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('tooltip y position (10px margin below the cursor)', '15px',
    +      tt.getElement().style.top);
    +  assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
    +  assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
    +  assertTrue(eventQueue.isEmpty());
    +
    +  goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
    +  assertEquals(goog.ui.Tooltip.State.WAITING_TO_HIDE, tt.getState());
    +  clock.tick(51);
    +  assertEquals('hidden', tt.getElement().style.visibility);
    +  assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
    +  assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
    +  assertTrue(eventQueue.isEmpty());
    +}
    +
    +function testMultipleTargets() {
    +  var firstTarget = dom.getElement('hovertarget');
    +  var secondTarget = dom.getElement('second');
    +  var elsewhere = dom.getElement('notpopup');
    +  var element = tt.getElement();
    +
    +  tt.attach(firstTarget);
    +  tt.attach(secondTarget);
    +  tt.setShowDelayMs(100);
    +  tt.setHideDelayMs(50);
    +
    +  // Move over first target
    +  goog.testing.events.fireMouseOverEvent(firstTarget, elsewhere);
    +  clock.tick(101);
    +  assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
    +  assertTrue(eventQueue.isEmpty());
    +
    +  // Move from first to second
    +  goog.testing.events.fireMouseOutEvent(firstTarget, secondTarget);
    +  goog.testing.events.fireMouseOverEvent(secondTarget, firstTarget);
    +  assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
    +  assertTrue(eventQueue.isEmpty());
    +
    +  // Move from second to element (before second shows)
    +  goog.testing.events.fireMouseOutEvent(secondTarget, element);
    +  goog.testing.events.fireMouseOverEvent(element, secondTarget);
    +  assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
    +  assertTrue(eventQueue.isEmpty());
    +
    +  // Move from element to second, and let it show
    +  goog.testing.events.fireMouseOutEvent(element, secondTarget);
    +  goog.testing.events.fireMouseOverEvent(secondTarget, element);
    +  assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
    +  clock.tick(101);
    +  assertEquals(goog.ui.Tooltip.State.SHOWING, tt.getState());
    +  assertEquals('Anchor should be second target', secondTarget, tt.anchor);
    +  assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
    +  assertEquals(goog.ui.PopupBase.EventType.SHOW, eventQueue.dequeue().type);
    +  assertTrue(eventQueue.isEmpty());
    +
    +  // Move from second to first and then off without first showing
    +  goog.testing.events.fireMouseOutEvent(secondTarget, firstTarget);
    +  goog.testing.events.fireMouseOverEvent(firstTarget, secondTarget);
    +  assertEquals(goog.ui.Tooltip.State.UPDATING, tt.getState());
    +  goog.testing.events.fireMouseOutEvent(firstTarget, elsewhere);
    +  assertEquals(goog.ui.Tooltip.State.WAITING_TO_HIDE, tt.getState());
    +  clock.tick(51);
    +  assertEquals('hidden', tt.getElement().style.visibility);
    +  assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
    +  assertEquals(goog.ui.PopupBase.EventType.HIDE, eventQueue.dequeue().type);
    +  assertTrue(eventQueue.isEmpty());
    +  clock.tick(200);
    +
    +  // Move from element to second, but detach second before it shows.
    +  goog.testing.events.fireMouseOutEvent(element, secondTarget);
    +  goog.testing.events.fireMouseOverEvent(secondTarget, element);
    +  assertEquals(goog.ui.Tooltip.State.WAITING_TO_SHOW, tt.getState());
    +  tt.detach(secondTarget);
    +  clock.tick(200);
    +  assertEquals(goog.ui.Tooltip.State.INACTIVE, tt.getState());
    +  assertEquals('Anchor should be second target', secondTarget, tt.anchor);
    +  assertTrue(eventQueue.isEmpty());
    +}
    +
    +function testRequireInteraction() {
    +  var hoverTarget = dom.getElement('hovertarget');
    +  var elsewhere = dom.getElement('notpopup');
    +
    +  tt.attach(hoverTarget);
    +  tt.setShowDelayMs(100);
    +  tt.setHideDelayMs(50);
    +  tt.setRequireInteraction(true);
    +
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
    +  clock.tick(101);
    +  assertEquals(
    +      'Tooltip should not show without mouse move event',
    +      'hidden', tt.getElement().style.visibility);
    +  goog.testing.events.fireMouseMoveEvent(hoverTarget);
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
    +  clock.tick(101);
    +  assertEquals(
    +      'Tooltip should show because we had mouse move event',
    +      'visible', tt.getElement().style.visibility);
    +
    +  goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
    +  clock.tick(51);
    +  assertEquals('hidden', tt.getElement().style.visibility);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, hoverTarget));
    +  clock.tick(101);
    +  assertEquals(
    +      'Tooltip should show because we had focus event',
    +      'visible', tt.getElement().style.visibility);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, hoverTarget));
    +  clock.tick(51);
    +  assertEquals('hidden', tt.getElement().style.visibility);
    +
    +  goog.testing.events.fireMouseMoveEvent(hoverTarget);
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
    +  goog.testing.events.fireMouseOutEvent(hoverTarget, elsewhere);
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
    +  clock.tick(101);
    +  assertEquals(
    +      'A cancelled trigger should also cancel the seen interaction',
    +      'hidden', tt.getElement().style.visibility);
    +}
    +
    +function testDispose() {
    +  var element = tt.getElement();
    +  tt.dispose();
    +  assertTrue('Tooltip should have been disposed of', tt.isDisposed());
    +  assertNull('Tooltip element reference should have been nulled out',
    +      tt.getElement());
    +  assertNotEquals('Tooltip element should not be a child of the body',
    +      document.body, element.parentNode);
    +}
    +
    +function testNested() {
    +  var ttNested;
    +  tt.getElement().appendChild(dom.createDom(
    +      'span', {id: 'nested'}, 'Goodbye'));
    +  ttNested = new goog.ui.Tooltip(undefined, undefined, dom);
    +  ttNested.setElement(dom.createDom('div', {id: 'nestedPopup'}, 'hi'));
    +  tt.setShowDelayMs(100);
    +  tt.setHideDelayMs(50);
    +  ttNested.setShowDelayMs(75);
    +  ttNested.setHideDelayMs(25);
    +  var nestedAnchor = dom.getElement('nested');
    +  var hoverTarget = dom.getElement('hovertarget');
    +  var outerTooltip = dom.getElement('popup');
    +  var innerTooltip = dom.getElement('nestedPopup');
    +  var elsewhere = dom.getElement('notpopup');
    +
    +  ttNested.attach(nestedAnchor);
    +  tt.attach(hoverTarget);
    +
    +  // Test mouse into, out of nested tooltip
    +  goog.testing.events.fireMouseOverEvent(hoverTarget, elsewhere);
    +  clock.tick(101);
    +  goog.testing.events.fireMouseOutEvent(hoverTarget, outerTooltip);
    +  goog.testing.events.fireMouseOverEvent(outerTooltip, hoverTarget);
    +  clock.tick(51);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  goog.testing.events.fireMouseOutEvent(outerTooltip, nestedAnchor);
    +  goog.testing.events.fireMouseOverEvent(nestedAnchor, outerTooltip);
    +  clock.tick(76);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('visible', ttNested.getElement().style.visibility);
    +  goog.testing.events.fireMouseOutEvent(nestedAnchor, outerTooltip);
    +  goog.testing.events.fireMouseOverEvent(outerTooltip, nestedAnchor);
    +  clock.tick(100);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('hidden', ttNested.getElement().style.visibility);
    +
    +  // Go back in nested tooltip and then out through tooltip element.
    +  goog.testing.events.fireMouseOutEvent(outerTooltip, nestedAnchor);
    +  goog.testing.events.fireMouseOverEvent(nestedAnchor, outerTooltip);
    +  clock.tick(76);
    +  goog.testing.events.fireMouseOutEvent(nestedAnchor, innerTooltip);
    +  goog.testing.events.fireMouseOverEvent(innerTooltip, nestedAnchor);
    +  clock.tick(15);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('visible', ttNested.getElement().style.visibility);
    +  goog.testing.events.fireMouseOutEvent(innerTooltip, elsewhere);
    +  clock.tick(26);
    +  assertEquals('hidden', ttNested.getElement().style.visibility);
    +  clock.tick(51);
    +  assertEquals('hidden', tt.getElement().style.visibility);
    +
    +  // Test with focus
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, hoverTarget));
    +  clock.tick(101);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, hoverTarget));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, nestedAnchor));
    +  clock.tick(76);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('visible', ttNested.getElement().style.visibility);
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.BLUR, nestedAnchor));
    +  goog.testing.events.fireBrowserEvent(new goog.events.Event(
    +      goog.events.EventType.FOCUS, hoverTarget));
    +  clock.tick(26);
    +  assertEquals('visible', tt.getElement().style.visibility);
    +  assertEquals('hidden', ttNested.getElement().style.visibility);
    +
    +  ttNested.onHide_();
    +  ttNested.dispose();
    +}
    +
    +function testPosition() {
    +  dom.getDocument().body.style.paddingBottom = '150%'; // force scrollbar
    +  var scrollEl = dom.getDocumentScrollElement();
    +
    +  var anchor = dom.getElement('hovertarget');
    +  var tooltip = new goog.ui.Tooltip(anchor, 'foo');
    +  tooltip.getElement().style.position = 'absolute';
    +
    +  tooltip.cursorPosition.x = 100;
    +  tooltip.cursorPosition.y = 100;
    +  tooltip.showForElement(anchor);
    +
    +  assertEquals('Tooltip should be at cursor position',
    +      '(110, 110)', // (100, 100) + padding (10, 10)
    +      goog.style.getPageOffset(tooltip.getElement()).toString());
    +
    +  scrollEl.scrollTop = 50;
    +
    +  var offset = goog.style.getPageOffset(tooltip.getElement());
    +  assertTrue('Tooltip should be at cursor position when scrolled',
    +      Math.abs(offset.x - 110) <= ALLOWED_OFFSET); // 100 + padding 10
    +  assertTrue('Tooltip should be at cursor position when scrolled',
    +      Math.abs(offset.y - 110) <= ALLOWED_OFFSET); // 100 + padding 10
    +
    +  tooltip.dispose();
    +  dom.getDocument().body.style.paddingTop = '';
    +  scrollEl.scrollTop = 0;
    +}
    +
    +function testPositionOverride() {
    +  var anchor = dom.getElement('hovertarget');
    +  var tooltip = new TestTooltip(anchor, 'foo', dom);
    +
    +  tooltip.showForElement(anchor);
    +
    +  assertEquals('Tooltip should be at absolute position', '(13, 17)',
    +      goog.style.getPageOffset(tooltip.getElement()).toString());
    +  tooltip.dispose();
    +}
    +
    +function testHtmlContent() {
    +  tt.setSafeHtml(goog.html.testing.newSafeHtmlForTest(
    +      '<span class="theSpan">Hello</span>'));
    +  var spanEl =
    +      goog.dom.getElementByClass('theSpan', tt.getElement());
    +  assertEquals('Hello', goog.dom.getTextContent(spanEl));
    +}
    +
    +function testSetContent_guardedByGlobalFlag() {
    +  /** @suppress {missingRequire} */
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', false);
    +  assertEquals(
    +      'Error: Legacy conversion from string to goog.html types is disabled',
    +      assertThrows(function() {
    +        tt.setHtml('<img src="blag" onerror="evil();">');
    +      }).message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/basenode.js b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode.js
    new file mode 100644
    index 0000000000..47f5d338a3
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode.js
    @@ -0,0 +1,1581 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.ui.tree.BaseNode class.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author eae@google.com (Emil A Eklund)
    + *
    + * This is a based on the webfx tree control. It since been updated to add
    + * typeahead support, as well as accessibility support using ARIA framework.
    + * See file comment in treecontrol.js.
    + */
    +
    +goog.provide('goog.ui.tree.BaseNode');
    +goog.provide('goog.ui.tree.BaseNode.EventType');
    +
    +goog.require('goog.Timer');
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.safe');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.html.SafeStyle');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.string');
    +goog.require('goog.string.StringBuffer');
    +goog.require('goog.style');
    +goog.require('goog.ui.Component');
    +
    +
    +
    +/**
    + * An abstract base class for a node in the tree.
    + *
    + * @param {string|!goog.html.SafeHtml} html The html content of the node label.
    + * @param {Object=} opt_config The configuration for the tree. See
    + *    {@link goog.ui.tree.BaseNode.defaultConfig}. If not specified the
    + *    default config will be used.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.Component}
    + */
    +goog.ui.tree.BaseNode = function(html, opt_config, opt_domHelper) {
    +  goog.ui.Component.call(this, opt_domHelper);
    +
    +  /**
    +   * The configuration for the tree.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.config_ = opt_config || goog.ui.tree.BaseNode.defaultConfig;
    +
    +  /**
    +   * HTML content of the node label.
    +   * @type {!goog.html.SafeHtml}
    +   * @private
    +   */
    +  this.html_ = (html instanceof goog.html.SafeHtml ? html :
    +      goog.html.legacyconversions.safeHtmlFromString(html));
    +
    +  /** @private {string} */
    +  this.iconClass_;
    +
    +  /** @private {string} */
    +  this.expandedIconClass_;
    +
    +  /** @protected {goog.ui.tree.TreeControl} */
    +  this.tree;
    +
    +  /** @private {goog.ui.tree.BaseNode} */
    +  this.previousSibling_;
    +
    +  /** @private {goog.ui.tree.BaseNode} */
    +  this.nextSibling_;
    +
    +  /** @private {goog.ui.tree.BaseNode} */
    +  this.firstChild_;
    +
    +  /** @private {goog.ui.tree.BaseNode} */
    +  this.lastChild_;
    +};
    +goog.inherits(goog.ui.tree.BaseNode, goog.ui.Component);
    +
    +
    +/**
    + * The event types dispatched by this class.
    + * @enum {string}
    + */
    +goog.ui.tree.BaseNode.EventType = {
    +  BEFORE_EXPAND: 'beforeexpand',
    +  EXPAND: 'expand',
    +  BEFORE_COLLAPSE: 'beforecollapse',
    +  COLLAPSE: 'collapse'
    +};
    +
    +
    +/**
    + * Map of nodes in existence. Needed to route events to the appropriate nodes.
    + * Nodes are added to the map at {@link #enterDocument} time and removed at
    + * {@link #exitDocument} time.
    + * @type {Object}
    + * @protected
    + */
    +goog.ui.tree.BaseNode.allNodes = {};
    +
    +
    +/**
    + * Whether the tree item is selected.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.selected_ = false;
    +
    +
    +/**
    + * Whether the tree node is expanded.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.expanded_ = false;
    +
    +
    +/**
    + * Tooltip for the tree item
    + * @type {?string}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.toolTip_ = null;
    +
    +
    +/**
    + * HTML that can appear after the label (so not inside the anchor).
    + * @type {!goog.html.SafeHtml}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.afterLabelHtml_ = goog.html.SafeHtml.EMPTY;
    +
    +
    +/**
    + * Whether to allow user to collapse this node.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.isUserCollapsible_ = true;
    +
    +
    +/**
    + * Nesting depth of this node; cached result of computeDepth_.
    + * -1 if value has not been cached.
    + * @type {number}
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.depth_ = -1;
    +
    +
    +/** @override */
    +goog.ui.tree.BaseNode.prototype.disposeInternal = function() {
    +  goog.ui.tree.BaseNode.superClass_.disposeInternal.call(this);
    +  if (this.tree) {
    +    this.tree.removeNode(this);
    +    this.tree = null;
    +  }
    +  this.setElementInternal(null);
    +};
    +
    +
    +/**
    + * Adds roles and states.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.initAccessibility = function() {
    +  var el = this.getElement();
    +  if (el) {
    +    // Set an id for the label
    +    var label = this.getLabelElement();
    +    if (label && !label.id) {
    +      label.id = this.getId() + '.label';
    +    }
    +
    +    goog.a11y.aria.setRole(el, 'treeitem');
    +    goog.a11y.aria.setState(el, 'selected', false);
    +    goog.a11y.aria.setState(el, 'expanded', false);
    +    goog.a11y.aria.setState(el, 'level', this.getDepth());
    +    if (label) {
    +      goog.a11y.aria.setState(el, 'labelledby', label.id);
    +    }
    +
    +    var img = this.getIconElement();
    +    if (img) {
    +      goog.a11y.aria.setRole(img, 'presentation');
    +    }
    +    var ei = this.getExpandIconElement();
    +    if (ei) {
    +      goog.a11y.aria.setRole(ei, 'presentation');
    +    }
    +
    +    var ce = this.getChildrenElement();
    +    if (ce) {
    +      goog.a11y.aria.setRole(ce, 'group');
    +
    +      // In case the children will be created lazily.
    +      if (ce.hasChildNodes()) {
    +        // do setsize for each child
    +        var count = this.getChildCount();
    +        for (var i = 1; i <= count; i++) {
    +          var child = this.getChildAt(i - 1).getElement();
    +          goog.asserts.assert(child, 'The child element cannot be null');
    +          goog.a11y.aria.setState(child, 'setsize', count);
    +          goog.a11y.aria.setState(child, 'posinset', i);
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.BaseNode.prototype.createDom = function() {
    +  var element = this.getDomHelper().safeHtmlToNode(this.toSafeHtml());
    +  this.setElementInternal(/** @type {!Element} */ (element));
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.BaseNode.prototype.enterDocument = function() {
    +  goog.ui.tree.BaseNode.superClass_.enterDocument.call(this);
    +  goog.ui.tree.BaseNode.allNodes[this.getId()] = this;
    +  this.initAccessibility();
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.BaseNode.prototype.exitDocument = function() {
    +  goog.ui.tree.BaseNode.superClass_.exitDocument.call(this);
    +  delete goog.ui.tree.BaseNode.allNodes[this.getId()];
    +};
    +
    +
    +/**
    + * The method assumes that the child doesn't have parent node yet.
    + * The {@code opt_render} argument is not used. If the parent node is expanded,
    + * the child node's state will be the same as the parent's. Otherwise the
    + * child's DOM tree won't be created.
    + * @override
    + */
    +goog.ui.tree.BaseNode.prototype.addChildAt = function(child, index,
    +    opt_render) {
    +  goog.asserts.assert(!child.getParent());
    +  goog.asserts.assertInstanceof(child, goog.ui.tree.BaseNode);
    +  var prevNode = this.getChildAt(index - 1);
    +  var nextNode = this.getChildAt(index);
    +
    +  goog.ui.tree.BaseNode.superClass_.addChildAt.call(this, child, index);
    +
    +  child.previousSibling_ = prevNode;
    +  child.nextSibling_ = nextNode;
    +
    +  if (prevNode) {
    +    prevNode.nextSibling_ = child;
    +  } else {
    +    this.firstChild_ = child;
    +  }
    +  if (nextNode) {
    +    nextNode.previousSibling_ = child;
    +  } else {
    +    this.lastChild_ = child;
    +  }
    +
    +  var tree = this.getTree();
    +  if (tree) {
    +    child.setTreeInternal(tree);
    +  }
    +
    +  child.setDepth_(this.getDepth() + 1);
    +
    +  if (this.getElement()) {
    +    this.updateExpandIcon();
    +    if (this.getExpanded()) {
    +      var el = this.getChildrenElement();
    +      if (!child.getElement()) {
    +        child.createDom();
    +      }
    +      var childElement = child.getElement();
    +      var nextElement = nextNode && nextNode.getElement();
    +      el.insertBefore(childElement, nextElement);
    +
    +      if (this.isInDocument()) {
    +        child.enterDocument();
    +      }
    +
    +      if (!nextNode) {
    +        if (prevNode) {
    +          prevNode.updateExpandIcon();
    +        } else {
    +          goog.style.setElementShown(el, true);
    +          this.setExpanded(this.getExpanded());
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Adds a node as a child to the current node.
    + * @param {goog.ui.tree.BaseNode} child The child to add.
    + * @param {goog.ui.tree.BaseNode=} opt_before If specified, the new child is
    + *    added as a child before this one. If not specified, it's appended to the
    + *    end.
    + * @return {!goog.ui.tree.BaseNode} The added child.
    + */
    +goog.ui.tree.BaseNode.prototype.add = function(child, opt_before) {
    +  goog.asserts.assert(!opt_before || opt_before.getParent() == this,
    +      'Can only add nodes before siblings');
    +  if (child.getParent()) {
    +    child.getParent().removeChild(child);
    +  }
    +  this.addChildAt(child,
    +      opt_before ? this.indexOfChild(opt_before) : this.getChildCount());
    +  return child;
    +};
    +
    +
    +/**
    + * Removes a child. The caller is responsible for disposing the node.
    + * @param {goog.ui.Component|string} childNode The child to remove. Must be a
    + *     {@link goog.ui.tree.BaseNode}.
    + * @param {boolean=} opt_unrender Unused. The child will always be unrendered.
    + * @return {!goog.ui.tree.BaseNode} The child that was removed.
    + * @override
    + */
    +goog.ui.tree.BaseNode.prototype.removeChild =
    +    function(childNode, opt_unrender) {
    +  // In reality, this only accepts BaseNodes.
    +  var child = /** @type {goog.ui.tree.BaseNode} */ (childNode);
    +
    +  // if we remove selected or tree with the selected we should select this
    +  var tree = this.getTree();
    +  var selectedNode = tree ? tree.getSelectedItem() : null;
    +  if (selectedNode == child || child.contains(selectedNode)) {
    +    if (tree.hasFocus()) {
    +      this.select();
    +      goog.Timer.callOnce(this.onTimeoutSelect_, 10, this);
    +    } else {
    +      this.select();
    +    }
    +  }
    +
    +  goog.ui.tree.BaseNode.superClass_.removeChild.call(this, child);
    +
    +  if (this.lastChild_ == child) {
    +    this.lastChild_ = child.previousSibling_;
    +  }
    +  if (this.firstChild_ == child) {
    +    this.firstChild_ = child.nextSibling_;
    +  }
    +  if (child.previousSibling_) {
    +    child.previousSibling_.nextSibling_ = child.nextSibling_;
    +  }
    +  if (child.nextSibling_) {
    +    child.nextSibling_.previousSibling_ = child.previousSibling_;
    +  }
    +
    +  var wasLast = child.isLastSibling();
    +
    +  child.tree = null;
    +  child.depth_ = -1;
    +
    +  if (tree) {
    +    // Tell the tree control that this node is now removed.
    +    tree.removeNode(this);
    +
    +    if (this.isInDocument()) {
    +      var el = this.getChildrenElement();
    +
    +      if (child.isInDocument()) {
    +        var childEl = child.getElement();
    +        el.removeChild(childEl);
    +
    +        child.exitDocument();
    +      }
    +
    +      if (wasLast) {
    +        var newLast = this.getLastChild();
    +        if (newLast) {
    +          newLast.updateExpandIcon();
    +        }
    +      }
    +      if (!this.hasChildren()) {
    +        el.style.display = 'none';
    +        this.updateExpandIcon();
    +        this.updateIcon_();
    +      }
    +    }
    +  }
    +
    +  return child;
    +};
    +
    +
    +/**
    + * @deprecated Use {@link #removeChild}.
    + */
    +goog.ui.tree.BaseNode.prototype.remove =
    +    goog.ui.tree.BaseNode.prototype.removeChild;
    +
    +
    +/**
    + * Handler for setting focus asynchronously.
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.onTimeoutSelect_ = function() {
    +  this.select();
    +};
    +
    +
    +/**
    + * Returns the tree.
    + */
    +goog.ui.tree.BaseNode.prototype.getTree = goog.abstractMethod;
    +
    +
    +/**
    + * Returns the depth of the node in the tree. Should not be overridden.
    + * @return {number} The non-negative depth of this node (the root is zero).
    + */
    +goog.ui.tree.BaseNode.prototype.getDepth = function() {
    +  var depth = this.depth_;
    +  if (depth < 0) {
    +    depth = this.computeDepth_();
    +    this.setDepth_(depth);
    +  }
    +  return depth;
    +};
    +
    +
    +/**
    + * Computes the depth of the node in the tree.
    + * Called only by getDepth, when the depth hasn't already been cached.
    + * @return {number} The non-negative depth of this node (the root is zero).
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.computeDepth_ = function() {
    +  var parent = this.getParent();
    +  if (parent) {
    +    return parent.getDepth() + 1;
    +  } else {
    +    return 0;
    +  }
    +};
    +
    +
    +/**
    + * Changes the depth of a node (and all its descendants).
    + * @param {number} depth The new nesting depth; must be non-negative.
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.setDepth_ = function(depth) {
    +  if (depth != this.depth_) {
    +    this.depth_ = depth;
    +    var row = this.getRowElement();
    +    if (row) {
    +      var indent = this.getPixelIndent_() + 'px';
    +      if (this.isRightToLeft()) {
    +        row.style.paddingRight = indent;
    +      } else {
    +        row.style.paddingLeft = indent;
    +      }
    +    }
    +    this.forEachChild(function(child) {
    +      child.setDepth_(depth + 1);
    +    });
    +  }
    +};
    +
    +
    +/**
    + * Returns true if the node is a descendant of this node
    + * @param {goog.ui.tree.BaseNode} node The node to check.
    + * @return {boolean} True if the node is a descendant of this node, false
    + *    otherwise.
    + */
    +goog.ui.tree.BaseNode.prototype.contains = function(node) {
    +  var current = node;
    +  while (current) {
    +    if (current == this) {
    +      return true;
    +    }
    +    current = current.getParent();
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * An array of empty children to return for nodes that have no children.
    + * @type {!Array<!goog.ui.tree.BaseNode>}
    + * @private
    + */
    +goog.ui.tree.BaseNode.EMPTY_CHILDREN_ = [];
    +
    +
    +/**
    + * @param {number} index 0-based index.
    + * @return {goog.ui.tree.BaseNode} The child at the given index; null if none.
    + */
    +goog.ui.tree.BaseNode.prototype.getChildAt;
    +
    +
    +/**
    + * Returns the children of this node.
    + * @return {!Array<!goog.ui.tree.BaseNode>} The children.
    + */
    +goog.ui.tree.BaseNode.prototype.getChildren = function() {
    +  var children = [];
    +  this.forEachChild(function(child) {
    +    children.push(child);
    +  });
    +  return children;
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The first child of this node.
    + */
    +goog.ui.tree.BaseNode.prototype.getFirstChild = function() {
    +  return this.getChildAt(0);
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The last child of this node.
    + */
    +goog.ui.tree.BaseNode.prototype.getLastChild = function() {
    +  return this.getChildAt(this.getChildCount() - 1);
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The previous sibling of this node.
    + */
    +goog.ui.tree.BaseNode.prototype.getPreviousSibling = function() {
    +  return this.previousSibling_;
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The next sibling of this node.
    + */
    +goog.ui.tree.BaseNode.prototype.getNextSibling = function() {
    +  return this.nextSibling_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the node is the last sibling.
    + */
    +goog.ui.tree.BaseNode.prototype.isLastSibling = function() {
    +  return !this.nextSibling_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the node is selected.
    + */
    +goog.ui.tree.BaseNode.prototype.isSelected = function() {
    +  return this.selected_;
    +};
    +
    +
    +/**
    + * Selects the node.
    + */
    +goog.ui.tree.BaseNode.prototype.select = function() {
    +  var tree = this.getTree();
    +  if (tree) {
    +    tree.setSelectedItem(this);
    +  }
    +};
    +
    +
    +/**
    + * Originally it was intended to deselect the node but never worked.
    + * @deprecated Use {@code tree.setSelectedItem(null)}.
    + */
    +goog.ui.tree.BaseNode.prototype.deselect = goog.nullFunction;
    +
    +
    +/**
    + * Called from the tree to instruct the node change its selection state.
    + * @param {boolean} selected The new selection state.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.setSelectedInternal = function(selected) {
    +  if (this.selected_ == selected) {
    +    return;
    +  }
    +  this.selected_ = selected;
    +
    +  this.updateRow();
    +
    +  var el = this.getElement();
    +  if (el) {
    +    goog.a11y.aria.setState(el, 'selected', selected);
    +    if (selected) {
    +      var treeElement = this.getTree().getElement();
    +      goog.asserts.assert(treeElement,
    +          'The DOM element for the tree cannot be null');
    +      goog.a11y.aria.setState(treeElement,
    +          'activedescendant',
    +          this.getId());
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the node is expanded.
    + */
    +goog.ui.tree.BaseNode.prototype.getExpanded = function() {
    +  return this.expanded_;
    +};
    +
    +
    +/**
    + * Sets the node to be expanded internally, without state change events.
    + * @param {boolean} expanded Whether to expand or close the node.
    + */
    +goog.ui.tree.BaseNode.prototype.setExpandedInternal = function(expanded) {
    +  this.expanded_ = expanded;
    +};
    +
    +
    +/**
    + * Sets the node to be expanded.
    + * @param {boolean} expanded Whether to expand or close the node.
    + */
    +goog.ui.tree.BaseNode.prototype.setExpanded = function(expanded) {
    +  var isStateChange = expanded != this.expanded_;
    +  if (isStateChange) {
    +    // Only fire events if the expanded state has actually changed.
    +    var prevented = !this.dispatchEvent(
    +        expanded ? goog.ui.tree.BaseNode.EventType.BEFORE_EXPAND :
    +        goog.ui.tree.BaseNode.EventType.BEFORE_COLLAPSE);
    +    if (prevented) return;
    +  }
    +  var ce;
    +  this.expanded_ = expanded;
    +  var tree = this.getTree();
    +  var el = this.getElement();
    +
    +  if (this.hasChildren()) {
    +    if (!expanded && tree && this.contains(tree.getSelectedItem())) {
    +      this.select();
    +    }
    +
    +    if (el) {
    +      ce = this.getChildrenElement();
    +      if (ce) {
    +        goog.style.setElementShown(ce, expanded);
    +
    +        // Make sure we have the HTML for the children here.
    +        if (expanded && this.isInDocument() && !ce.hasChildNodes()) {
    +          var children = [];
    +          this.forEachChild(function(child) {
    +            children.push(child.toSafeHtml());
    +          });
    +          goog.dom.safe.setInnerHtml(ce, goog.html.SafeHtml.concat(children));
    +          this.forEachChild(function(child) {
    +            child.enterDocument();
    +          });
    +        }
    +      }
    +      this.updateExpandIcon();
    +    }
    +  } else {
    +    ce = this.getChildrenElement();
    +    if (ce) {
    +      goog.style.setElementShown(ce, false);
    +    }
    +  }
    +  if (el) {
    +    this.updateIcon_();
    +    goog.a11y.aria.setState(el, 'expanded', expanded);
    +  }
    +
    +  if (isStateChange) {
    +    this.dispatchEvent(expanded ? goog.ui.tree.BaseNode.EventType.EXPAND :
    +                       goog.ui.tree.BaseNode.EventType.COLLAPSE);
    +  }
    +};
    +
    +
    +/**
    + * Toggles the expanded state of the node.
    + */
    +goog.ui.tree.BaseNode.prototype.toggle = function() {
    +  this.setExpanded(!this.getExpanded());
    +};
    +
    +
    +/**
    + * Expands the node.
    + */
    +goog.ui.tree.BaseNode.prototype.expand = function() {
    +  this.setExpanded(true);
    +};
    +
    +
    +/**
    + * Collapses the node.
    + */
    +goog.ui.tree.BaseNode.prototype.collapse = function() {
    +  this.setExpanded(false);
    +};
    +
    +
    +/**
    + * Collapses the children of the node.
    + */
    +goog.ui.tree.BaseNode.prototype.collapseChildren = function() {
    +  this.forEachChild(function(child) {
    +    child.collapseAll();
    +  });
    +};
    +
    +
    +/**
    + * Collapses the children and the node.
    + */
    +goog.ui.tree.BaseNode.prototype.collapseAll = function() {
    +  this.collapseChildren();
    +  this.collapse();
    +};
    +
    +
    +/**
    + * Expands the children of the node.
    + */
    +goog.ui.tree.BaseNode.prototype.expandChildren = function() {
    +  this.forEachChild(function(child) {
    +    child.expandAll();
    +  });
    +};
    +
    +
    +/**
    + * Expands the children and the node.
    + */
    +goog.ui.tree.BaseNode.prototype.expandAll = function() {
    +  this.expandChildren();
    +  this.expand();
    +};
    +
    +
    +/**
    + * Expands the parent chain of this node so that it is visible.
    + */
    +goog.ui.tree.BaseNode.prototype.reveal = function() {
    +  var parent = this.getParent();
    +  if (parent) {
    +    parent.setExpanded(true);
    +    parent.reveal();
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the node will allow the user to collapse it.
    + * @param {boolean} isCollapsible Whether to allow node collapse.
    + */
    +goog.ui.tree.BaseNode.prototype.setIsUserCollapsible = function(isCollapsible) {
    +  this.isUserCollapsible_ = isCollapsible;
    +  if (!this.isUserCollapsible_) {
    +    this.expand();
    +  }
    +  if (this.getElement()) {
    +    this.updateExpandIcon();
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the node is collapsible by user actions.
    + */
    +goog.ui.tree.BaseNode.prototype.isUserCollapsible = function() {
    +  return this.isUserCollapsible_;
    +};
    +
    +
    +/**
    + * Creates HTML for the node.
    + * @return {!goog.html.SafeHtml}
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.toSafeHtml = function() {
    +  var tree = this.getTree();
    +  var hideLines = !tree.getShowLines() ||
    +      tree == this.getParent() && !tree.getShowRootLines();
    +
    +  var childClass = hideLines ? this.config_.cssChildrenNoLines :
    +      this.config_.cssChildren;
    +
    +  var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
    +
    +  var attributes = {
    +    'class': childClass,
    +    'style': this.getLineStyle()
    +  };
    +
    +  var content = [];
    +  if (nonEmptyAndExpanded) {
    +    // children
    +    this.forEachChild(function(child) {
    +      content.push(child.toSafeHtml());
    +    });
    +  }
    +
    +  var children = goog.html.SafeHtml.create('div', attributes, content);
    +
    +  return goog.html.SafeHtml.create('div',
    +      {'class': this.config_.cssItem, 'id': this.getId()},
    +      [this.getRowSafeHtml(), children]);
    +};
    +
    +
    +/**
    + * @return {number} The pixel indent of the row.
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.getPixelIndent_ = function() {
    +  return Math.max(0, (this.getDepth() - 1) * this.config_.indentWidth);
    +};
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} The html for the row.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getRowSafeHtml = function() {
    +  var style = {};
    +  style['padding-' + (this.isRightToLeft() ? 'right' : 'left')] =
    +      this.getPixelIndent_() + 'px';
    +  var attributes = {
    +    'class': this.getRowClassName(),
    +    'style': style
    +  };
    +  var content = [
    +    this.getExpandIconSafeHtml(),
    +    this.getIconSafeHtml(),
    +    this.getLabelSafeHtml()
    +  ];
    +  return goog.html.SafeHtml.create('div', attributes, content);
    +};
    +
    +
    +/**
    + * @return {string} The class name for the row.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getRowClassName = function() {
    +  var selectedClass;
    +  if (this.isSelected()) {
    +    selectedClass = ' ' + this.config_.cssSelectedRow;
    +  } else {
    +    selectedClass = '';
    +  }
    +  return this.config_.cssTreeRow + selectedClass;
    +};
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} The html for the label.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getLabelSafeHtml = function() {
    +  var html = goog.html.SafeHtml.create('span',
    +      {
    +        'class': this.config_.cssItemLabel,
    +        'title': this.getToolTip() || null
    +      },
    +      this.getSafeHtml());
    +  return goog.html.SafeHtml.concat(html,
    +      goog.html.SafeHtml.create('span', {}, this.getAfterLabelSafeHtml()));
    +};
    +
    +
    +/**
    + * Returns the html that appears after the label. This is useful if you want to
    + * put extra UI on the row of the label but not inside the anchor tag.
    + * @return {string} The html.
    + * @final
    + */
    +goog.ui.tree.BaseNode.prototype.getAfterLabelHtml = function() {
    +  return goog.html.SafeHtml.unwrap(this.getAfterLabelSafeHtml());
    +};
    +
    +
    +/**
    + * Returns the html that appears after the label. This is useful if you want to
    + * put extra UI on the row of the label but not inside the anchor tag.
    + * @return {!goog.html.SafeHtml} The html.
    + */
    +goog.ui.tree.BaseNode.prototype.getAfterLabelSafeHtml = function() {
    +  return this.afterLabelHtml_;
    +};
    +
    +
    +// TODO(jakubvrana): Deprecate in favor of setSafeHtml, once developer docs on
    +// using goog.html.SafeHtml are in place.
    +/**
    + * Sets the html that appears after the label. This is useful if you want to
    + * put extra UI on the row of the label but not inside the anchor tag.
    + * @param {string} html The html.
    + */
    +goog.ui.tree.BaseNode.prototype.setAfterLabelHtml = function(html) {
    +  this.setAfterLabelSafeHtml(goog.html.legacyconversions.safeHtmlFromString(
    +      html));
    +};
    +
    +
    +/**
    + * Sets the html that appears after the label. This is useful if you want to
    + * put extra UI on the row of the label but not inside the anchor tag.
    + * @param {!goog.html.SafeHtml} html The html.
    + */
    +goog.ui.tree.BaseNode.prototype.setAfterLabelSafeHtml = function(html) {
    +  this.afterLabelHtml_ = html;
    +  var el = this.getAfterLabelElement();
    +  if (el) {
    +    goog.dom.safe.setInnerHtml(el, html);
    +  }
    +};
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} The html for the icon.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getIconSafeHtml = function() {
    +  return goog.html.SafeHtml.create('span', {
    +    'style': {'display': 'inline-block'},
    +    'class': this.getCalculatedIconClass()
    +  });
    +};
    +
    +
    +/**
    + * Gets the calculated icon class.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getCalculatedIconClass = goog.abstractMethod;
    +
    +
    +/**
    + * @return {!goog.html.SafeHtml} The source for the icon.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getExpandIconSafeHtml = function() {
    +  return goog.html.SafeHtml.create('span', {
    +    'type': 'expand',
    +    'style': {'display': 'inline-block'},
    +    'class': this.getExpandIconClass()
    +  });
    +};
    +
    +
    +/**
    + * @return {string} The class names of the icon used for expanding the node.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getExpandIconClass = function() {
    +  var tree = this.getTree();
    +  var hideLines = !tree.getShowLines() ||
    +      tree == this.getParent() && !tree.getShowRootLines();
    +
    +  var config = this.config_;
    +  var sb = new goog.string.StringBuffer();
    +  sb.append(config.cssTreeIcon, ' ', config.cssExpandTreeIcon, ' ');
    +
    +  if (this.hasChildren()) {
    +    var bits = 0;
    +    /*
    +      Bitmap used to determine which icon to use
    +      1  Plus
    +      2  Minus
    +      4  T Line
    +      8  L Line
    +    */
    +
    +    if (tree.getShowExpandIcons() && this.isUserCollapsible_) {
    +      if (this.getExpanded()) {
    +        bits = 2;
    +      } else {
    +        bits = 1;
    +      }
    +    }
    +
    +    if (!hideLines) {
    +      if (this.isLastSibling()) {
    +        bits += 4;
    +      } else {
    +        bits += 8;
    +      }
    +    }
    +
    +    switch (bits) {
    +      case 1:
    +        sb.append(config.cssExpandTreeIconPlus);
    +        break;
    +      case 2:
    +        sb.append(config.cssExpandTreeIconMinus);
    +        break;
    +      case 4:
    +        sb.append(config.cssExpandTreeIconL);
    +        break;
    +      case 5:
    +        sb.append(config.cssExpandTreeIconLPlus);
    +        break;
    +      case 6:
    +        sb.append(config.cssExpandTreeIconLMinus);
    +        break;
    +      case 8:
    +        sb.append(config.cssExpandTreeIconT);
    +        break;
    +      case 9:
    +        sb.append(config.cssExpandTreeIconTPlus);
    +        break;
    +      case 10:
    +        sb.append(config.cssExpandTreeIconTMinus);
    +        break;
    +      default:  // 0
    +        sb.append(config.cssExpandTreeIconBlank);
    +    }
    +  } else {
    +    if (hideLines) {
    +      sb.append(config.cssExpandTreeIconBlank);
    +    } else if (this.isLastSibling()) {
    +      sb.append(config.cssExpandTreeIconL);
    +    } else {
    +      sb.append(config.cssExpandTreeIconT);
    +    }
    +  }
    +  return sb.toString();
    +};
    +
    +
    +/**
    + * @return {!goog.html.SafeStyle} The line style.
    + */
    +goog.ui.tree.BaseNode.prototype.getLineStyle = function() {
    +  var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
    +  return goog.html.SafeStyle.create({
    +    'background-position': this.getBackgroundPosition(),
    +    'display': nonEmptyAndExpanded ? null : 'none'
    +  });
    +};
    +
    +
    +/**
    + * @return {string} The background position style value.
    + */
    +goog.ui.tree.BaseNode.prototype.getBackgroundPosition = function() {
    +  return (this.isLastSibling() ? '-100' :
    +          (this.getDepth() - 1) * this.config_.indentWidth) + 'px 0';
    +};
    +
    +
    +/**
    + * @return {Element} The element for the tree node.
    + * @override
    + */
    +goog.ui.tree.BaseNode.prototype.getElement = function() {
    +  var el = goog.ui.tree.BaseNode.superClass_.getElement.call(this);
    +  if (!el) {
    +    el = this.getDomHelper().getElement(this.getId());
    +    this.setElementInternal(el);
    +  }
    +  return el;
    +};
    +
    +
    +/**
    + * @return {Element} The row is the div that is used to draw the node without
    + *     the children.
    + */
    +goog.ui.tree.BaseNode.prototype.getRowElement = function() {
    +  var el = this.getElement();
    +  return el ? /** @type {Element} */ (el.firstChild) : null;
    +};
    +
    +
    +/**
    + * @return {Element} The expanded icon element.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getExpandIconElement = function() {
    +  var el = this.getRowElement();
    +  return el ? /** @type {Element} */ (el.firstChild) : null;
    +};
    +
    +
    +/**
    + * @return {Element} The icon element.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getIconElement = function() {
    +  var el = this.getRowElement();
    +  return el ? /** @type {Element} */ (el.childNodes[1]) : null;
    +};
    +
    +
    +/**
    + * @return {Element} The label element.
    + */
    +goog.ui.tree.BaseNode.prototype.getLabelElement = function() {
    +  var el = this.getRowElement();
    +  // TODO: find/fix race condition that requires us to add
    +  // the lastChild check
    +  return el && el.lastChild ?
    +      /** @type {Element} */ (el.lastChild.previousSibling) : null;
    +};
    +
    +
    +/**
    + * @return {Element} The element after the label.
    + */
    +goog.ui.tree.BaseNode.prototype.getAfterLabelElement = function() {
    +  var el = this.getRowElement();
    +  return el ? /** @type {Element} */ (el.lastChild) : null;
    +};
    +
    +
    +/**
    + * @return {Element} The div containing the children.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.getChildrenElement = function() {
    +  var el = this.getElement();
    +  return el ? /** @type {Element} */ (el.lastChild) : null;
    +};
    +
    +
    +/**
    + * Sets the icon class for the node.
    + * @param {string} s The icon class.
    + */
    +goog.ui.tree.BaseNode.prototype.setIconClass = function(s) {
    +  this.iconClass_ = s;
    +  if (this.isInDocument()) {
    +    this.updateIcon_();
    +  }
    +};
    +
    +
    +/**
    + * Gets the icon class for the node.
    + * @return {string} s The icon source.
    + */
    +goog.ui.tree.BaseNode.prototype.getIconClass = function() {
    +  return this.iconClass_;
    +};
    +
    +
    +/**
    + * Sets the icon class for when the node is expanded.
    + * @param {string} s The expanded icon class.
    + */
    +goog.ui.tree.BaseNode.prototype.setExpandedIconClass = function(s) {
    +  this.expandedIconClass_ = s;
    +  if (this.isInDocument()) {
    +    this.updateIcon_();
    +  }
    +};
    +
    +
    +/**
    + * Gets the icon class for when the node is expanded.
    + * @return {string} The class.
    + */
    +goog.ui.tree.BaseNode.prototype.getExpandedIconClass = function() {
    +  return this.expandedIconClass_;
    +};
    +
    +
    +/**
    + * Sets the text of the label.
    + * @param {string} s The plain text of the label.
    + */
    +goog.ui.tree.BaseNode.prototype.setText = function(s) {
    +  this.setSafeHtml(goog.html.SafeHtml.htmlEscape(s));
    +};
    +
    +
    +/**
    + * Returns the text of the label. If the text was originally set as HTML, the
    + * return value is unspecified.
    + * @return {string} The plain text of the label.
    + */
    +goog.ui.tree.BaseNode.prototype.getText = function() {
    +  return goog.string.unescapeEntities(goog.html.SafeHtml.unwrap(this.html_));
    +};
    +
    +
    +// TODO(jakubvrana): Deprecate in favor of setSafeHtml, once developer docs on
    +// using goog.html.SafeHtml are in place.
    +/**
    + * Sets the html of the label.
    + * @param {string} s The html string for the label.
    + */
    +goog.ui.tree.BaseNode.prototype.setHtml = function(s) {
    +  this.setSafeHtml(goog.html.legacyconversions.safeHtmlFromString(s));
    +};
    +
    +
    +/**
    + * Sets the HTML of the label.
    + * @param {!goog.html.SafeHtml} html The HTML object for the label.
    + */
    +goog.ui.tree.BaseNode.prototype.setSafeHtml = function(html) {
    +  this.html_ = html;
    +  var el = this.getLabelElement();
    +  if (el) {
    +    goog.dom.safe.setInnerHtml(el, html);
    +  }
    +  var tree = this.getTree();
    +  if (tree) {
    +    // Tell the tree control about the updated label text.
    +    tree.setNode(this);
    +  }
    +};
    +
    +
    +/**
    + * Returns the html of the label.
    + * @return {string} The html string of the label.
    + * @final
    + */
    +goog.ui.tree.BaseNode.prototype.getHtml = function() {
    +  return goog.html.SafeHtml.unwrap(this.getSafeHtml());
    +};
    +
    +
    +/**
    + * Returns the html of the label.
    + * @return {!goog.html.SafeHtml} The html string of the label.
    + */
    +goog.ui.tree.BaseNode.prototype.getSafeHtml = function() {
    +  return this.html_;
    +};
    +
    +
    +/**
    + * Sets the text of the tooltip.
    + * @param {string} s The tooltip text to set.
    + */
    +goog.ui.tree.BaseNode.prototype.setToolTip = function(s) {
    +  this.toolTip_ = s;
    +  var el = this.getLabelElement();
    +  if (el) {
    +    el.title = s;
    +  }
    +};
    +
    +
    +/**
    + * Returns the text of the tooltip.
    + * @return {?string} The tooltip text.
    + */
    +goog.ui.tree.BaseNode.prototype.getToolTip = function() {
    +  return this.toolTip_;
    +};
    +
    +
    +/**
    + * Updates the row styles.
    + */
    +goog.ui.tree.BaseNode.prototype.updateRow = function() {
    +  var rowEl = this.getRowElement();
    +  if (rowEl) {
    +    rowEl.className = this.getRowClassName();
    +  }
    +};
    +
    +
    +/**
    + * Updates the expand icon of the node.
    + */
    +goog.ui.tree.BaseNode.prototype.updateExpandIcon = function() {
    +  var img = this.getExpandIconElement();
    +  if (img) {
    +    img.className = this.getExpandIconClass();
    +  }
    +  var cel = this.getChildrenElement();
    +  if (cel) {
    +    cel.style.backgroundPosition = this.getBackgroundPosition();
    +  }
    +};
    +
    +
    +/**
    + * Updates the icon of the node. Assumes that this.getElement() is created.
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.updateIcon_ = function() {
    +  this.getIconElement().className = this.getCalculatedIconClass();
    +};
    +
    +
    +/**
    + * Handles mouse down event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.onMouseDown = function(e) {
    +  var el = e.target;
    +  // expand icon
    +  var type = el.getAttribute('type');
    +  if (type == 'expand' && this.hasChildren()) {
    +    if (this.isUserCollapsible_) {
    +      this.toggle();
    +    }
    +    return;
    +  }
    +
    +  this.select();
    +  this.updateRow();
    +};
    +
    +
    +/**
    + * Handles a click event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @protected
    + * @suppress {underscore|visibility}
    + */
    +goog.ui.tree.BaseNode.prototype.onClick_ = goog.events.Event.preventDefault;
    +
    +
    +/**
    + * Handles a double click event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @protected
    + * @suppress {underscore|visibility}
    + */
    +goog.ui.tree.BaseNode.prototype.onDoubleClick_ = function(e) {
    +  var el = e.target;
    +  // expand icon
    +  var type = el.getAttribute('type');
    +  if (type == 'expand' && this.hasChildren()) {
    +    return;
    +  }
    +
    +  if (this.isUserCollapsible_) {
    +    this.toggle();
    +  }
    +};
    +
    +
    +/**
    + * Handles a key down event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @return {boolean} The handled value.
    + * @protected
    + */
    +goog.ui.tree.BaseNode.prototype.onKeyDown = function(e) {
    +  var handled = true;
    +  switch (e.keyCode) {
    +    case goog.events.KeyCodes.RIGHT:
    +      if (e.altKey) {
    +        break;
    +      }
    +      if (this.hasChildren()) {
    +        if (!this.getExpanded()) {
    +          this.setExpanded(true);
    +        } else {
    +          this.getFirstChild().select();
    +        }
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.LEFT:
    +      if (e.altKey) {
    +        break;
    +      }
    +      if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) {
    +        this.setExpanded(false);
    +      } else {
    +        var parent = this.getParent();
    +        var tree = this.getTree();
    +        // don't go to root if hidden
    +        if (parent && (tree.getShowRootNode() || parent != tree)) {
    +          parent.select();
    +        }
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.DOWN:
    +      var nextNode = this.getNextShownNode();
    +      if (nextNode) {
    +        nextNode.select();
    +      }
    +      break;
    +
    +    case goog.events.KeyCodes.UP:
    +      var previousNode = this.getPreviousShownNode();
    +      if (previousNode) {
    +        previousNode.select();
    +      }
    +      break;
    +
    +    default:
    +      handled = false;
    +  }
    +
    +  if (handled) {
    +    e.preventDefault();
    +    var tree = this.getTree();
    +    if (tree) {
    +      // clear type ahead buffer as user navigates with arrow keys
    +      tree.clearTypeAhead();
    +    }
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Handles a key down event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.tree.BaseNode.prototype.onKeyPress_ = function(e) {
    +  if (!e.altKey && e.keyCode >= goog.events.KeyCodes.LEFT &&
    +      e.keyCode <= goog.events.KeyCodes.DOWN) {
    +    e.preventDefault();
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The last shown descendant.
    + */
    +goog.ui.tree.BaseNode.prototype.getLastShownDescendant = function() {
    +  if (!this.getExpanded() || !this.hasChildren()) {
    +    return this;
    +  }
    +  // we know there is at least 1 child
    +  return this.getLastChild().getLastShownDescendant();
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The next node to show or null if there isn't
    + *     a next node to show.
    + */
    +goog.ui.tree.BaseNode.prototype.getNextShownNode = function() {
    +  if (this.hasChildren() && this.getExpanded()) {
    +    return this.getFirstChild();
    +  } else {
    +    var parent = this;
    +    var next;
    +    while (parent != this.getTree()) {
    +      next = parent.getNextSibling();
    +      if (next != null) {
    +        return next;
    +      }
    +      parent = parent.getParent();
    +    }
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @return {goog.ui.tree.BaseNode} The previous node to show.
    + */
    +goog.ui.tree.BaseNode.prototype.getPreviousShownNode = function() {
    +  var ps = this.getPreviousSibling();
    +  if (ps != null) {
    +    return ps.getLastShownDescendant();
    +  }
    +  var parent = this.getParent();
    +  var tree = this.getTree();
    +  if (!tree.getShowRootNode() && parent == tree) {
    +    return null;
    +  }
    +  // The root is the first node.
    +  if (this == tree) {
    +    return null;
    +  }
    +  return /** @type {goog.ui.tree.BaseNode} */ (parent);
    +};
    +
    +
    +/**
    + * @return {*} Data set by the client.
    + * @deprecated Use {@link #getModel} instead.
    + */
    +goog.ui.tree.BaseNode.prototype.getClientData =
    +    goog.ui.tree.BaseNode.prototype.getModel;
    +
    +
    +/**
    + * Sets client data to associate with the node.
    + * @param {*} data The client data to associate with the node.
    + * @deprecated Use {@link #setModel} instead.
    + */
    +goog.ui.tree.BaseNode.prototype.setClientData =
    +    goog.ui.tree.BaseNode.prototype.setModel;
    +
    +
    +/**
    + * @return {Object} The configuration for the tree.
    + */
    +goog.ui.tree.BaseNode.prototype.getConfig = function() {
    +  return this.config_;
    +};
    +
    +
    +/**
    + * Internal method that is used to set the tree control on the node.
    + * @param {goog.ui.tree.TreeControl} tree The tree control.
    + */
    +goog.ui.tree.BaseNode.prototype.setTreeInternal = function(tree) {
    +  if (this.tree != tree) {
    +    this.tree = tree;
    +    // Add new node to the type ahead node map.
    +    tree.setNode(this);
    +    this.forEachChild(function(child) {
    +      child.setTreeInternal(tree);
    +    });
    +  }
    +};
    +
    +
    +/**
    + * A default configuration for the tree.
    + */
    +goog.ui.tree.BaseNode.defaultConfig = {
    +  indentWidth: 19,
    +  cssRoot: goog.getCssName('goog-tree-root') + ' ' +
    +      goog.getCssName('goog-tree-item'),
    +  cssHideRoot: goog.getCssName('goog-tree-hide-root'),
    +  cssItem: goog.getCssName('goog-tree-item'),
    +  cssChildren: goog.getCssName('goog-tree-children'),
    +  cssChildrenNoLines: goog.getCssName('goog-tree-children-nolines'),
    +  cssTreeRow: goog.getCssName('goog-tree-row'),
    +  cssItemLabel: goog.getCssName('goog-tree-item-label'),
    +  cssTreeIcon: goog.getCssName('goog-tree-icon'),
    +  cssExpandTreeIcon: goog.getCssName('goog-tree-expand-icon'),
    +  cssExpandTreeIconPlus: goog.getCssName('goog-tree-expand-icon-plus'),
    +  cssExpandTreeIconMinus: goog.getCssName('goog-tree-expand-icon-minus'),
    +  cssExpandTreeIconTPlus: goog.getCssName('goog-tree-expand-icon-tplus'),
    +  cssExpandTreeIconTMinus: goog.getCssName('goog-tree-expand-icon-tminus'),
    +  cssExpandTreeIconLPlus: goog.getCssName('goog-tree-expand-icon-lplus'),
    +  cssExpandTreeIconLMinus: goog.getCssName('goog-tree-expand-icon-lminus'),
    +  cssExpandTreeIconT: goog.getCssName('goog-tree-expand-icon-t'),
    +  cssExpandTreeIconL: goog.getCssName('goog-tree-expand-icon-l'),
    +  cssExpandTreeIconBlank: goog.getCssName('goog-tree-expand-icon-blank'),
    +  cssExpandedFolderIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
    +  cssCollapsedFolderIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
    +  cssFileIcon: goog.getCssName('goog-tree-file-icon'),
    +  cssExpandedRootIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
    +  cssCollapsedRootIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
    +  cssSelectedRow: goog.getCssName('selected')
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.html b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.html
    new file mode 100644
    index 0000000000..cc89e9bf6f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.tree.BaseNode
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.tree.BaseNodeTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.js b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.js
    new file mode 100644
    index 0000000000..6642ac8a49
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/basenode_test.js
    @@ -0,0 +1,290 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.tree.BaseNodeTest');
    +goog.setTestOnly('goog.ui.tree.BaseNodeTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.html.legacyconversions');
    +goog.require('goog.html.testing');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.tree.BaseNode');
    +goog.require('goog.ui.tree.TreeControl');
    +goog.require('goog.ui.tree.TreeNode');
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  // Reset global flags to their defaults.
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', true);
    +}
    +
    +function testAdd() {
    +  var node1 = new goog.ui.tree.TreeNode('node1');
    +  var node2 = new goog.ui.tree.TreeNode('node2');
    +  var node3 = new goog.ui.tree.TreeNode('node3');
    +  var node4 = new goog.ui.tree.TreeNode('node4');
    +
    +  assertEquals('node2 added', node2, node1.add(node2));
    +  assertEquals('node3 added', node3, node1.add(node3));
    +  assertEquals('node4 added', node4, node1.add(node4, node3));
    +
    +  assertEquals('node1 has 3 children', 3, node1.getChildCount());
    +  assertEquals('first child', node2, node1.getChildAt(0));
    +  assertEquals('second child', node4, node1.getChildAt(1));
    +  assertEquals('third child', node3, node1.getChildAt(2));
    +  assertNull('node1 has no parent', node1.getParent());
    +  assertEquals('the parent of node2 is node1', node1, node2.getParent());
    +
    +  assertEquals('node4 moved under node2', node4, node2.add(node4));
    +  assertEquals('node1 has 2 children', 2, node1.getChildCount());
    +  assertEquals('node2 has 1 child', 1, node2.getChildCount());
    +  assertEquals('the child of node2 is node4', node4, node2.getChildAt(0));
    +  assertEquals('the parent of node4 is node2', node2, node4.getParent());
    +}
    +
    +function testExpandIconAfterAddChild() {
    +  var tree = new goog.ui.tree.TreeControl('root');
    +  var node1 = new goog.ui.tree.TreeNode('node1');
    +  var node2 = new goog.ui.tree.TreeNode('node2');
    +  tree.render(goog.dom.createDom('div'));
    +  tree.addChild(node1);
    +
    +  node1.addChild(node2);
    +  assertTrue('expand icon of node1 changed to L+', goog.dom.classlist.contains(
    +      node1.getExpandIconElement(), 'goog-tree-expand-icon-lplus'));
    +
    +  node1.removeChild(node2);
    +  assertFalse('expand icon of node1 changed back to L',
    +      goog.dom.classlist.contains(
    +          node1.getExpandIconElement(), 'goog-tree-expand-icon-lplus'));
    +}
    +
    +function testExpandEvents() {
    +  var n = new goog.ui.tree.BaseNode('');
    +  n.getTree = function() {};
    +  var expanded = false;
    +  n.setExpanded(expanded);
    +  assertEquals(expanded, n.getExpanded());
    +  var callCount = 0;
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.BEFORE_EXPAND,
    +      function(e) {
    +        assertEquals(expanded, n.getExpanded());
    +        callCount++;
    +      });
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.EXPAND,
    +      function(e) {
    +        assertEquals(!expanded, n.getExpanded());
    +        callCount++;
    +      });
    +  n.setExpanded(!expanded);
    +  assertEquals(2, callCount);
    +}
    +
    +function testExpandEvents2() {
    +  var n = new goog.ui.tree.BaseNode('');
    +  n.getTree = function() {};
    +  var expanded = true;
    +  n.setExpanded(expanded);
    +  assertEquals(expanded, n.getExpanded());
    +  var callCount = 0;
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.BEFORE_COLLAPSE,
    +      function(e) {
    +        assertEquals(expanded, n.getExpanded());
    +        callCount++;
    +      });
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.COLLAPSE,
    +      function(e) {
    +        assertEquals(!expanded, n.getExpanded());
    +        callCount++;
    +      });
    +  n.setExpanded(!expanded);
    +  assertEquals(2, callCount);
    +}
    +
    +function testExpandEventsPreventDefault() {
    +  var n = new goog.ui.tree.BaseNode('');
    +  n.getTree = function() {};
    +  var expanded = true;
    +  n.setExpanded(expanded);
    +  assertEquals(expanded, n.getExpanded());
    +  var callCount = 0;
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.BEFORE_COLLAPSE,
    +      function(e) {
    +        assertEquals(expanded, n.getExpanded());
    +        e.preventDefault();
    +        callCount++;
    +      });
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.COLLAPSE,
    +      function(e) {
    +        fail('Should not fire COLLAPSE');
    +      });
    +  n.setExpanded(!expanded);
    +  assertEquals(1, callCount);
    +}
    +
    +function testExpandEventsPreventDefault2() {
    +  var n = new goog.ui.tree.BaseNode('');
    +  n.getTree = function() {};
    +  var expanded = false;
    +  n.setExpanded(expanded);
    +  assertEquals(expanded, n.getExpanded());
    +  var callCount = 0;
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.BEFORE_EXPAND,
    +      function(e) {
    +        assertEquals(expanded, n.getExpanded());
    +        e.preventDefault();
    +        callCount++;
    +      });
    +  n.addEventListener(goog.ui.tree.BaseNode.EventType.EXPAND,
    +      function(e) {
    +        fail('Should not fire EXPAND');
    +      });
    +  n.setExpanded(!expanded);
    +  assertEquals(1, callCount);
    +}
    +
    +function testGetNextShownNode() {
    +  var tree = new goog.ui.tree.TreeControl('tree');
    +  assertNull('next node for unpopulated tree', tree.getNextShownNode());
    +
    +  var node1 = new goog.ui.tree.TreeNode('node1');
    +  var node2 = new goog.ui.tree.TreeNode('node2');
    +  var node3 = new goog.ui.tree.TreeNode('node3');
    +  node1.add(node2);
    +  node1.add(node3);
    +
    +  assertNull('next node for unexpanded node1', node1.getNextShownNode());
    +  node1.expand();
    +  assertEquals('next node for expanded node1', node2, node1.getNextShownNode());
    +  assertEquals('next node for node2', node3, node2.getNextShownNode());
    +  assertNull('next node for node3', node3.getNextShownNode());
    +
    +  tree.add(node1);
    +  assertEquals('next node for populated tree', node1, tree.getNextShownNode());
    +  assertNull('next node for node3 inside the tree', node3.getNextShownNode());
    +
    +  var component = new goog.ui.Component();
    +  component.addChild(tree);
    +  assertNull('next node for node3 inside the tree if the tree has parent',
    +      node3.getNextShownNode());
    +}
    +
    +function testGetPreviousShownNode() {
    +  var tree = new goog.ui.tree.TreeControl('tree');
    +  assertNull('next node for unpopulated tree', tree.getPreviousShownNode());
    +
    +  var node1 = new goog.ui.tree.TreeNode('node1');
    +  var node2 = new goog.ui.tree.TreeNode('node2');
    +  var node3 = new goog.ui.tree.TreeNode('node3');
    +  tree.add(node1);
    +  node1.add(node2);
    +  tree.add(node3);
    +
    +  assertEquals('prev node for node3 when node1 is unexpanded',
    +               node1, node3.getPreviousShownNode());
    +  node1.expand();
    +  assertEquals('prev node for node3 when node1 is expanded',
    +               node2, node3.getPreviousShownNode());
    +  assertEquals('prev node for node2 when node1 is expanded',
    +               node1, node2.getPreviousShownNode());
    +  assertEquals('prev node for node1 when root is shown', tree,
    +               node1.getPreviousShownNode());
    +  tree.setShowRootNode(false);
    +  assertNull('next node for node1 when root is not shown',
    +             node1.getPreviousShownNode());
    +
    +  var component = new goog.ui.Component();
    +  component.addChild(tree);
    +  assertNull('prev node for root if the tree has parent',
    +             tree.getPreviousShownNode());
    +}
    +
    +function testInvisibleNodesInUnrenderedTree() {
    +  var tree = new goog.ui.tree.TreeControl('tree');
    +  var a = new goog.ui.tree.TreeNode('a');
    +  var b = new goog.ui.tree.TreeNode('b');
    +  tree.add(a);
    +  a.add(b);
    +  tree.render();
    +
    +  var textContent =
    +      tree.getElement().textContent || tree.getElement().innerText;
    +  assertContains('Node should be rendered.', 'tree', textContent);
    +  assertContains('Node should be rendered.', 'a', textContent);
    +  assertNotContains('Unexpanded node child should not be rendered.',
    +      'b', textContent);
    +
    +  a.expand();
    +  var textContent =
    +      tree.getElement().textContent || tree.getElement().innerText;
    +  assertContains('Node should be rendered.', 'tree', textContent);
    +  assertContains('Node should be rendered.', 'a', textContent);
    +  assertContains('Expanded node child should be rendered.', 'b', textContent);
    +  tree.dispose();
    +}
    +
    +function testInvisibleNodesInRenderedTree() {
    +  var tree = new goog.ui.tree.TreeControl('tree');
    +  tree.render();
    +  var a = new goog.ui.tree.TreeNode('a');
    +  var b = new goog.ui.tree.TreeNode('b');
    +  tree.add(a);
    +  a.add(b);
    +
    +  var textContent =
    +      tree.getElement().textContent || tree.getElement().innerText;
    +  assertContains('Node should be rendered.', 'tree', textContent);
    +  assertContains('Node should be rendered.', 'a', textContent);
    +  assertNotContains('Unexpanded node child should not be rendered.',
    +      'b', textContent);
    +
    +  a.expand();
    +  var textContent =
    +      tree.getElement().textContent || tree.getElement().innerText;
    +  assertContains('Node should be rendered.', 'tree', textContent);
    +  assertContains('Node should be rendered.', 'a', textContent);
    +  assertContains('Expanded node child should be rendered.', 'b', textContent);
    +  tree.dispose();
    +}
    +
    +function testConstructor() {
    +  var tree = new goog.ui.tree.TreeControl('tree');
    +  assertEquals('tree', tree.getHtml());
    +
    +  tree = new goog.ui.tree.TreeControl(
    +      goog.html.testing.newSafeHtmlForTest('tree'));
    +  assertEquals('tree', tree.getHtml());
    +}
    +
    +function testSetHtml() {
    +  var tree = new goog.ui.tree.TreeControl('');
    +  tree.setHtml('<b>tree</b>');
    +  assertEquals('<b>tree</b>', tree.getHtml());
    +
    +  tree = new goog.ui.tree.TreeControl('');
    +  tree.setSafeHtml(goog.html.testing.newSafeHtmlForTest('<b>tree</b>'));
    +  assertEquals('<b>tree</b>', tree.getHtml());
    +}
    +
    +function testSetHtml_guardedByGlobalFlag() {
    +  stubs.set(goog.html.legacyconversions, 'ALLOW_LEGACY_CONVERSIONS', false);
    +  assertEquals(
    +      'Error: Legacy conversion from string to goog.html types is disabled',
    +      assertThrows(function() {
    +        new goog.ui.tree.TreeControl('<img src="blag" onerror="evil();">');
    +      }).message);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol.js b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol.js
    new file mode 100644
    index 0000000000..b61a8fc88e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol.js
    @@ -0,0 +1,642 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.ui.tree.TreeControl class, which
    + * provides a way to view a hierarchical set of data.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author eae@google.com (Emil A Eklund)
    + *
    + * This is a based on the webfx tree control. It since been updated to add
    + * typeahead support, as well as accessibility support using ARIA framework.
    + *
    + * @see ../../demos/tree/demo.html
    + */
    +
    +goog.provide('goog.ui.tree.TreeControl');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.FocusHandler');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.html.SafeHtml');
    +goog.require('goog.log');
    +goog.require('goog.ui.tree.BaseNode');
    +goog.require('goog.ui.tree.TreeNode');
    +goog.require('goog.ui.tree.TypeAhead');
    +goog.require('goog.userAgent');
    +
    +
    +
    +/**
    + * This creates a TreeControl object. A tree control provides a way to
    + * view a hierarchical set of data.
    + * @param {string|!goog.html.SafeHtml} html The HTML content of the node label.
    + * @param {Object=} opt_config The configuration for the tree. See
    + *    goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
    + *    will be used.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.tree.BaseNode}
    + */
    +goog.ui.tree.TreeControl = function(html, opt_config, opt_domHelper) {
    +  goog.ui.tree.BaseNode.call(this, html, opt_config, opt_domHelper);
    +
    +  // The root is open and selected by default.
    +  this.setExpandedInternal(true);
    +  this.setSelectedInternal(true);
    +
    +  this.selectedItem_ = this;
    +
    +  /**
    +   * Used for typeahead support.
    +   * @type {!goog.ui.tree.TypeAhead}
    +   * @private
    +   */
    +  this.typeAhead_ = new goog.ui.tree.TypeAhead();
    +
    +  if (goog.userAgent.IE) {
    +    /** @preserveTry */
    +    try {
    +      // works since IE6SP1
    +      document.execCommand('BackgroundImageCache', false, true);
    +    } catch (e) {
    +      goog.log.warning(this.logger_, 'Failed to enable background image cache');
    +    }
    +  }
    +};
    +goog.inherits(goog.ui.tree.TreeControl, goog.ui.tree.BaseNode);
    +
    +
    +/**
    + * The object handling keyboard events.
    + * @type {goog.events.KeyHandler}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.keyHandler_ = null;
    +
    +
    +/**
    + * The object handling focus events.
    + * @type {goog.events.FocusHandler}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.focusHandler_ = null;
    +
    +
    +/**
    + * Logger
    + * @type {goog.log.Logger}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.logger_ =
    +    goog.log.getLogger('goog.ui.tree.TreeControl');
    +
    +
    +/**
    + * Whether the tree is focused.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.focused_ = false;
    +
    +
    +/**
    + * Child node that currently has focus.
    + * @type {goog.ui.tree.BaseNode}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.focusedNode_ = null;
    +
    +
    +/**
    + * Whether to show lines.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.showLines_ = true;
    +
    +
    +/**
    + * Whether to show expanded lines.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.showExpandIcons_ = true;
    +
    +
    +/**
    + * Whether to show the root node.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.showRootNode_ = true;
    +
    +
    +/**
    + * Whether to show the root lines.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.showRootLines_ = true;
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getTree = function() {
    +  return this;
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getDepth = function() {
    +  return 0;
    +};
    +
    +
    +/**
    + * Expands the parent chain of this node so that it is visible.
    + * @override
    + */
    +goog.ui.tree.TreeControl.prototype.reveal = function() {
    +  // always expanded by default
    +  // needs to be overriden so that we don't try to reveal our parent
    +  // which is a generic component
    +};
    +
    +
    +/**
    + * Handles focus on the tree.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.handleFocus_ = function(e) {
    +  this.focused_ = true;
    +  goog.dom.classlist.add(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('focused'));
    +
    +  if (this.selectedItem_) {
    +    this.selectedItem_.select();
    +  }
    +};
    +
    +
    +/**
    + * Handles blur on the tree.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.handleBlur_ = function(e) {
    +  this.focused_ = false;
    +  goog.dom.classlist.remove(
    +      goog.asserts.assert(this.getElement()),
    +      goog.getCssName('focused'));
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the tree has keyboard focus.
    + */
    +goog.ui.tree.TreeControl.prototype.hasFocus = function() {
    +  return this.focused_;
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getExpanded = function() {
    +  return !this.showRootNode_ ||
    +      goog.ui.tree.TreeControl.superClass_.getExpanded.call(this);
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.setExpanded = function(expanded) {
    +  if (!this.showRootNode_) {
    +    this.setExpandedInternal(expanded);
    +  } else {
    +    goog.ui.tree.TreeControl.superClass_.setExpanded.call(this, expanded);
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getExpandIconSafeHtml = function() {
    +  // no expand icon for root element
    +  return goog.html.SafeHtml.EMPTY;
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getIconElement = function() {
    +  var el = this.getRowElement();
    +  return el ? /** @type {Element} */ (el.firstChild) : null;
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getExpandIconElement = function() {
    +  // no expand icon for root element
    +  return null;
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.updateExpandIcon = function() {
    +  // no expand icon
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.getRowClassName = function() {
    +  return goog.ui.tree.TreeControl.superClass_.getRowClassName.call(this) +
    +      (this.showRootNode_ ? '' : ' ' + this.getConfig().cssHideRoot);
    +};
    +
    +
    +/**
    + * Returns the source for the icon.
    + * @return {string} Src for the icon.
    + * @override
    + */
    +goog.ui.tree.TreeControl.prototype.getCalculatedIconClass = function() {
    +  var expanded = this.getExpanded();
    +  var expandedIconClass = this.getExpandedIconClass();
    +  if (expanded && expandedIconClass) {
    +    return expandedIconClass;
    +  }
    +  var iconClass = this.getIconClass();
    +  if (!expanded && iconClass) {
    +    return iconClass;
    +  }
    +
    +  // fall back on default icons
    +  var config = this.getConfig();
    +  if (expanded && config.cssExpandedRootIcon) {
    +    return config.cssTreeIcon + ' ' + config.cssExpandedRootIcon;
    +  } else if (!expanded && config.cssCollapsedRootIcon) {
    +    return config.cssTreeIcon + ' ' + config.cssCollapsedRootIcon;
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Sets the selected item.
    + * @param {goog.ui.tree.BaseNode} node The item to select.
    + */
    +goog.ui.tree.TreeControl.prototype.setSelectedItem = function(node) {
    +  if (this.selectedItem_ == node) {
    +    return;
    +  }
    +
    +  var hadFocus = false;
    +  if (this.selectedItem_) {
    +    hadFocus = this.selectedItem_ == this.focusedNode_;
    +    this.selectedItem_.setSelectedInternal(false);
    +  }
    +
    +  this.selectedItem_ = node;
    +
    +  if (node) {
    +    node.setSelectedInternal(true);
    +    if (hadFocus) {
    +      node.select();
    +    }
    +  }
    +
    +  this.dispatchEvent(goog.events.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * Returns the selected item.
    + * @return {goog.ui.tree.BaseNode} The currently selected item.
    + */
    +goog.ui.tree.TreeControl.prototype.getSelectedItem = function() {
    +  return this.selectedItem_;
    +};
    +
    +
    +/**
    + * Sets whether to show lines.
    + * @param {boolean} b Whether to show lines.
    + */
    +goog.ui.tree.TreeControl.prototype.setShowLines = function(b) {
    +  if (this.showLines_ != b) {
    +    this.showLines_ = b;
    +    if (this.isInDocument()) {
    +      this.updateLinesAndExpandIcons_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to show lines.
    + */
    +goog.ui.tree.TreeControl.prototype.getShowLines = function() {
    +  return this.showLines_;
    +};
    +
    +
    +/**
    + * Updates the lines after the tree has been drawn.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.updateLinesAndExpandIcons_ = function() {
    +  var tree = this;
    +  var showLines = tree.getShowLines();
    +  var showRootLines = tree.getShowRootLines();
    +
    +  /**
    +   * Recursively walk through all nodes and update the class names of the
    +   * expand icon and the children element.
    +   * @param {!goog.ui.tree.BaseNode} node
    +   */
    +  function updateShowLines(node) {
    +    var childrenEl = node.getChildrenElement();
    +    if (childrenEl) {
    +      var hideLines = !showLines || tree == node.getParent() && !showRootLines;
    +      var childClass = hideLines ? node.getConfig().cssChildrenNoLines :
    +          node.getConfig().cssChildren;
    +      childrenEl.className = childClass;
    +
    +      var expandIconEl = node.getExpandIconElement();
    +      if (expandIconEl) {
    +        expandIconEl.className = node.getExpandIconClass();
    +      }
    +    }
    +    node.forEachChild(updateShowLines);
    +  }
    +  updateShowLines(this);
    +};
    +
    +
    +/**
    + * Sets whether to show root lines.
    + * @param {boolean} b Whether to show root lines.
    + */
    +goog.ui.tree.TreeControl.prototype.setShowRootLines = function(b) {
    +  if (this.showRootLines_ != b) {
    +    this.showRootLines_ = b;
    +    if (this.isInDocument()) {
    +      this.updateLinesAndExpandIcons_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to show root lines.
    + */
    +goog.ui.tree.TreeControl.prototype.getShowRootLines = function() {
    +  return this.showRootLines_;
    +};
    +
    +
    +/**
    + * Sets whether to show expand icons.
    + * @param {boolean} b Whether to show expand icons.
    + */
    +goog.ui.tree.TreeControl.prototype.setShowExpandIcons = function(b) {
    +  if (this.showExpandIcons_ != b) {
    +    this.showExpandIcons_ = b;
    +    if (this.isInDocument()) {
    +      this.updateLinesAndExpandIcons_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to show expand icons.
    + */
    +goog.ui.tree.TreeControl.prototype.getShowExpandIcons = function() {
    +  return this.showExpandIcons_;
    +};
    +
    +
    +/**
    + * Sets whether to show the root node.
    + * @param {boolean} b Whether to show the root node.
    + */
    +goog.ui.tree.TreeControl.prototype.setShowRootNode = function(b) {
    +  if (this.showRootNode_ != b) {
    +    this.showRootNode_ = b;
    +    if (this.isInDocument()) {
    +      var el = this.getRowElement();
    +      if (el) {
    +        el.className = this.getRowClassName();
    +      }
    +    }
    +    // Ensure that we do not hide the selected item.
    +    if (!b && this.getSelectedItem() == this && this.getFirstChild()) {
    +      this.setSelectedItem(this.getFirstChild());
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to show the root node.
    + */
    +goog.ui.tree.TreeControl.prototype.getShowRootNode = function() {
    +  return this.showRootNode_;
    +};
    +
    +
    +/**
    + * Add roles and states.
    + * @protected
    + * @override
    + */
    +goog.ui.tree.TreeControl.prototype.initAccessibility = function() {
    +  goog.ui.tree.TreeControl.superClass_.initAccessibility.call(this);
    +
    +  var elt = this.getElement();
    +  goog.asserts.assert(elt, 'The DOM element for the tree cannot be null.');
    +  goog.a11y.aria.setRole(elt, 'tree');
    +  goog.a11y.aria.setState(elt, 'labelledby', this.getLabelElement().id);
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.enterDocument = function() {
    +  goog.ui.tree.TreeControl.superClass_.enterDocument.call(this);
    +  var el = this.getElement();
    +  el.className = this.getConfig().cssRoot;
    +  el.setAttribute('hideFocus', 'true');
    +  this.attachEvents_();
    +  this.initAccessibility();
    +};
    +
    +
    +/** @override */
    +goog.ui.tree.TreeControl.prototype.exitDocument = function() {
    +  goog.ui.tree.TreeControl.superClass_.exitDocument.call(this);
    +  this.detachEvents_();
    +};
    +
    +
    +/**
    + * Adds the event listeners to the tree.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.attachEvents_ = function() {
    +  var el = this.getElement();
    +  el.tabIndex = 0;
    +
    +  var kh = this.keyHandler_ = new goog.events.KeyHandler(el);
    +  var fh = this.focusHandler_ = new goog.events.FocusHandler(el);
    +
    +  this.getHandler().
    +      listen(fh, goog.events.FocusHandler.EventType.FOCUSOUT, this.handleBlur_).
    +      listen(fh, goog.events.FocusHandler.EventType.FOCUSIN, this.handleFocus_).
    +      listen(kh, goog.events.KeyHandler.EventType.KEY, this.handleKeyEvent).
    +      listen(el, goog.events.EventType.MOUSEDOWN, this.handleMouseEvent_).
    +      listen(el, goog.events.EventType.CLICK, this.handleMouseEvent_).
    +      listen(el, goog.events.EventType.DBLCLICK, this.handleMouseEvent_);
    +};
    +
    +
    +/**
    + * Removes the event listeners from the tree.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.detachEvents_ = function() {
    +  this.keyHandler_.dispose();
    +  this.keyHandler_ = null;
    +  this.focusHandler_.dispose();
    +  this.focusHandler_ = null;
    +};
    +
    +
    +/**
    + * Handles mouse events.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.handleMouseEvent_ = function(e) {
    +  goog.log.fine(this.logger_, 'Received event ' + e.type);
    +  var node = this.getNodeFromEvent_(e);
    +  if (node) {
    +    switch (e.type) {
    +      case goog.events.EventType.MOUSEDOWN:
    +        node.onMouseDown(e);
    +        break;
    +      case goog.events.EventType.CLICK:
    +        node.onClick_(e);
    +        break;
    +      case goog.events.EventType.DBLCLICK:
    +        node.onDoubleClick_(e);
    +        break;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handles key down on the tree.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @return {boolean} The handled value.
    + */
    +goog.ui.tree.TreeControl.prototype.handleKeyEvent = function(e) {
    +  var handled = false;
    +
    +  // Handle typeahead and navigation keystrokes.
    +  handled = this.typeAhead_.handleNavigation(e) ||
    +            (this.selectedItem_ && this.selectedItem_.onKeyDown(e)) ||
    +            this.typeAhead_.handleTypeAheadChar(e);
    +
    +  if (handled) {
    +    e.preventDefault();
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Finds the containing node given an event.
    + * @param {!goog.events.BrowserEvent} e The browser event.
    + * @return {goog.ui.tree.BaseNode} The containing node or null if no node is
    + *     found.
    + * @private
    + */
    +goog.ui.tree.TreeControl.prototype.getNodeFromEvent_ = function(e) {
    +  // find the right node
    +  var node = null;
    +  var target = e.target;
    +  while (target != null) {
    +    var id = target.id;
    +    node = goog.ui.tree.BaseNode.allNodes[id];
    +    if (node) {
    +      return node;
    +    }
    +    if (target == this.getElement()) {
    +      break;
    +    }
    +    target = target.parentNode;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Creates a new tree node using the same config as the root.
    + * @param {string=} opt_html The HTML content of the node label.
    + * @return {!goog.ui.tree.TreeNode} The new item.
    + */
    +goog.ui.tree.TreeControl.prototype.createNode = function(opt_html) {
    +  return new goog.ui.tree.TreeNode(opt_html || goog.html.SafeHtml.EMPTY,
    +      this.getConfig(), this.getDomHelper());
    +};
    +
    +
    +/**
    + * Allows the caller to notify that the given node has been added or just had
    + * been updated in the tree.
    + * @param {goog.ui.tree.BaseNode} node New node being added or existing node
    + *    that just had been updated.
    + */
    +goog.ui.tree.TreeControl.prototype.setNode = function(node) {
    +  this.typeAhead_.setNodeInMap(node);
    +};
    +
    +
    +/**
    + * Allows the caller to notify that the given node is being removed from the
    + * tree.
    + * @param {goog.ui.tree.BaseNode} node Node being removed.
    + */
    +goog.ui.tree.TreeControl.prototype.removeNode = function(node) {
    +  this.typeAhead_.removeNodeFromMap(node);
    +};
    +
    +
    +/**
    + * Clear the typeahead buffer.
    + */
    +goog.ui.tree.TreeControl.prototype.clearTypeAhead = function() {
    +  this.typeAhead_.clear();
    +};
    +
    +
    +/**
    + * A default configuration for the tree.
    + */
    +goog.ui.tree.TreeControl.defaultConfig = goog.ui.tree.BaseNode.defaultConfig;
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.html b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.html
    new file mode 100644
    index 0000000000..fb7569eb79
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.tree.TreeControl
    +  </title>
    +  <script type="text/javascript" src="../../base.js">
    +  </script>
    +  <script type="text/javascript">
    +    goog.require('goog.ui.tree.TreeControlTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="treeContainer" style="width:400px">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.js b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.js
    new file mode 100644
    index 0000000000..325f76adb1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/treecontrol_test.js
    @@ -0,0 +1,106 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.tree.TreeControlTest');
    +goog.setTestOnly('goog.ui.tree.TreeControlTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.tree.TreeControl');
    +function makeATree() {
    +  var tree = new goog.ui.tree.TreeControl('root');
    +  var testData = ['A', [
    +    ['AA', [
    +      ['AAA', []],
    +      ['AAB', []]
    +    ]],
    +    ['AB', [
    +      ['ABA', []],
    +      ['ABB', []]
    +    ]]
    +  ]];
    +
    +  createTreeFromTestData(tree, testData, 3);
    +  tree.render(goog.dom.getElement('treeContainer'));
    +  return tree;
    +}
    +
    +function createTreeFromTestData(node, data, maxLevels) {
    +  node.setHtml(data[0]);
    +  if (maxLevels < 0) {
    +    return;
    +  }
    +
    +  var children = data[1];
    +  for (var i = 0; i < children.length; i++) {
    +    var child = children[i];
    +    var childNode = node.getTree().createNode('');
    +    node.add(childNode);
    +    createTreeFromTestData(childNode, child, maxLevels - 1);
    +  }
    +}
    +
    +
    +/**
    + * Test moving a node to a greater depth.
    + */
    +function testIndent() {
    +  var tree = makeATree();
    +  tree.expandAll();
    +
    +  var node = tree.getChildren()[0].getChildren()[0];
    +  assertEquals('AAA', node.getHtml());
    +  assertNotNull(node.getElement());
    +  assertEquals('19px', node.getRowElement().style.paddingLeft);
    +
    +  assertEquals(2, node.getDepth());
    +
    +  var newParent = node.getNextSibling();
    +  assertEquals('AAB', newParent.getHtml());
    +  assertEquals(2, newParent.getDepth());
    +
    +  newParent.add(node);
    +
    +  assertEquals(newParent, node.getParent());
    +  assertEquals(node, newParent.getChildren()[0]);
    +  assertEquals(3, node.getDepth());
    +  assertEquals('38px', node.getRowElement().style.paddingLeft);
    +}
    +
    +
    +/**
    + * Test moving a node to a lesser depth.
    + */
    +function testOutdent() {
    +  var tree = makeATree();
    +  tree.expandAll();
    +
    +  var node = tree.getChildren()[0].getChildren()[0];
    +  assertEquals('AAA', node.getHtml());
    +  assertNotNull(node.getElement());
    +  assertEquals('19px', node.getRowElement().style.paddingLeft);
    +
    +  assertEquals(2, node.getDepth());
    +
    +  var newParent = tree;
    +  assertEquals('A', newParent.getHtml());
    +  assertEquals(0, newParent.getDepth());
    +
    +  newParent.add(node);
    +
    +  assertEquals(newParent, node.getParent());
    +  assertEquals(node, newParent.getChildren()[2]);
    +  assertEquals(1, node.getDepth());
    +  assertEquals('0px', node.getRowElement().style.paddingLeft);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/treenode.js b/mapbox-gl/closure-library/closure/goog/ui/tree/treenode.js
    new file mode 100644
    index 0000000000..4f7aa96c21
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/treenode.js
    @@ -0,0 +1,100 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of the goog.ui.tree.TreeNode class.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author eae@google.com (Emil A Eklund)
    + *
    + * This is a based on the webfx tree control. See file comment in
    + * treecontrol.js.
    + */
    +
    +goog.provide('goog.ui.tree.TreeNode');
    +
    +goog.require('goog.ui.tree.BaseNode');
    +
    +
    +
    +/**
    + * A single node in the tree.
    + * @param {string|!goog.html.SafeHtml} html The html content of the node label.
    + * @param {Object=} opt_config The configuration for the tree. See
    + *    goog.ui.tree.TreeControl.defaultConfig. If not specified, a default config
    + *    will be used.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.tree.BaseNode}
    + */
    +goog.ui.tree.TreeNode = function(html, opt_config, opt_domHelper) {
    +  goog.ui.tree.BaseNode.call(this, html, opt_config, opt_domHelper);
    +};
    +goog.inherits(goog.ui.tree.TreeNode, goog.ui.tree.BaseNode);
    +
    +
    +/**
    + * Returns the tree.
    + * @return {?goog.ui.tree.TreeControl} The tree.
    + * @override
    + */
    +goog.ui.tree.TreeNode.prototype.getTree = function() {
    +  if (this.tree) {
    +    return this.tree;
    +  }
    +  var parent = this.getParent();
    +  if (parent) {
    +    var tree = parent.getTree();
    +    if (tree) {
    +      this.setTreeInternal(tree);
    +      return tree;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Returns the source for the icon.
    + * @return {string} Src for the icon.
    + * @override
    + */
    +goog.ui.tree.TreeNode.prototype.getCalculatedIconClass = function() {
    +  var expanded = this.getExpanded();
    +  var expandedIconClass = this.getExpandedIconClass();
    +  if (expanded && expandedIconClass) {
    +    return expandedIconClass;
    +  }
    +  var iconClass = this.getIconClass();
    +  if (!expanded && iconClass) {
    +    return iconClass;
    +  }
    +
    +  // fall back on default icons
    +  var config = this.getConfig();
    +  if (this.hasChildren()) {
    +    if (expanded && config.cssExpandedFolderIcon) {
    +      return config.cssTreeIcon + ' ' +
    +             config.cssExpandedFolderIcon;
    +    } else if (!expanded && config.cssCollapsedFolderIcon) {
    +      return config.cssTreeIcon + ' ' +
    +             config.cssCollapsedFolderIcon;
    +    }
    +  } else {
    +    if (config.cssFileIcon) {
    +      return config.cssTreeIcon + ' ' + config.cssFileIcon;
    +    }
    +  }
    +  return '';
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead.js b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead.js
    new file mode 100644
    index 0000000000..777c453a80
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead.js
    @@ -0,0 +1,332 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Provides the typeahead functionality for the tree class.
    + *
    + */
    +
    +goog.provide('goog.ui.tree.TypeAhead');
    +goog.provide('goog.ui.tree.TypeAhead.Offset');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.string');
    +goog.require('goog.structs.Trie');
    +
    +
    +
    +/**
    + * Constructs a TypeAhead object.
    + * @constructor
    + * @final
    + */
    +goog.ui.tree.TypeAhead = function() {
    +  this.nodeMap_ = new goog.structs.Trie();
    +};
    +
    +
    +/**
    + * Map of tree nodes to allow for quick access by characters in the label text.
    + * @type {goog.structs.Trie<Array<goog.ui.tree.BaseNode>>}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.nodeMap_;
    +
    +
    +/**
    + * Buffer for storing typeahead characters.
    + * @type {string}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.buffer_ = '';
    +
    +
    +/**
    + * Matching labels from the latest typeahead search.
    + * @type {?Array<string>}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.matchingLabels_ = null;
    +
    +
    +/**
    + * Matching nodes from the latest typeahead search. Used when more than
    + * one node is present with the same label text.
    + * @type {?Array<goog.ui.tree.BaseNode>}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.matchingNodes_ = null;
    +
    +
    +/**
    + * Specifies the current index of the label from the latest typeahead search.
    + * @type {number}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.matchingLabelIndex_ = 0;
    +
    +
    +/**
    + * Specifies the index into matching nodes when more than one node is found
    + * with the same label.
    + * @type {number}
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.matchingNodeIndex_ = 0;
    +
    +
    +/**
    + * Enum for offset values that are used for ctrl-key navigation among the
    + * multiple matches of a given typeahead buffer.
    + *
    + * @enum {number}
    + */
    +goog.ui.tree.TypeAhead.Offset = {
    +  DOWN: 1,
    +  UP: -1
    +};
    +
    +
    +/**
    + * Handles navigation keys.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + * @return {boolean} The handled value.
    + */
    +goog.ui.tree.TypeAhead.prototype.handleNavigation = function(e) {
    +  var handled = false;
    +
    +  switch (e.keyCode) {
    +    // Handle ctrl+down, ctrl+up to navigate within typeahead results.
    +    case goog.events.KeyCodes.DOWN:
    +    case goog.events.KeyCodes.UP:
    +      if (e.ctrlKey) {
    +        this.jumpTo_(e.keyCode == goog.events.KeyCodes.DOWN ?
    +                     goog.ui.tree.TypeAhead.Offset.DOWN :
    +                     goog.ui.tree.TypeAhead.Offset.UP);
    +        handled = true;
    +      }
    +      break;
    +
    +    // Remove the last typeahead char.
    +    case goog.events.KeyCodes.BACKSPACE:
    +      var length = this.buffer_.length - 1;
    +      handled = true;
    +      if (length > 0) {
    +        this.buffer_ = this.buffer_.substring(0, length);
    +        this.jumpToLabel_(this.buffer_);
    +      } else if (length == 0) {
    +        // Clear the last character in typeahead.
    +        this.buffer_ = '';
    +      } else {
    +        handled = false;
    +      }
    +      break;
    +
    +    // Clear typeahead buffer.
    +    case goog.events.KeyCodes.ESC:
    +      this.buffer_ = '';
    +      handled = true;
    +      break;
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Handles the character presses.
    + * @param {goog.events.BrowserEvent} e The browser event.
    + *    Expected event type is goog.events.KeyHandler.EventType.KEY.
    + * @return {boolean} The handled value.
    + */
    +goog.ui.tree.TypeAhead.prototype.handleTypeAheadChar = function(e) {
    +  var handled = false;
    +
    +  if (!e.ctrlKey && !e.altKey) {
    +    // Since goog.structs.Trie.getKeys compares characters during
    +    // lookup, we should use charCode instead of keyCode where possible.
    +    // Convert to lowercase, typeahead is case insensitive.
    +    var ch = String.fromCharCode(e.charCode || e.keyCode).toLowerCase();
    +    if (goog.string.isUnicodeChar(ch) && (ch != ' ' || this.buffer_)) {
    +      this.buffer_ += ch;
    +      handled = this.jumpToLabel_(this.buffer_);
    +    }
    +  }
    +
    +  return handled;
    +};
    +
    +
    +/**
    + * Adds or updates the given node in the nodemap. The label text is used as a
    + * key and the node id is used as a value. In the case that the key already
    + * exists, such as when more than one node exists with the same label, then this
    + * function creates an array to hold the multiple nodes.
    + * @param {goog.ui.tree.BaseNode} node Node to be added or updated.
    + */
    +goog.ui.tree.TypeAhead.prototype.setNodeInMap = function(node) {
    +  var labelText = node.getText();
    +  if (labelText && !goog.string.isEmptyOrWhitespace(goog.string.makeSafe(labelText))) {
    +    // Typeahead is case insensitive, convert to lowercase.
    +    labelText = labelText.toLowerCase();
    +
    +    var previousValue = this.nodeMap_.get(labelText);
    +    if (previousValue) {
    +      // Found a previously created array, add the given node.
    +      previousValue.push(node);
    +    } else {
    +      // Create a new array and set the array as value.
    +      var nodeList = [node];
    +      this.nodeMap_.set(labelText, nodeList);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Removes the given node from the nodemap.
    + * @param {goog.ui.tree.BaseNode} node Node to be removed.
    + */
    +goog.ui.tree.TypeAhead.prototype.removeNodeFromMap = function(node) {
    +  var labelText = node.getText();
    +  if (labelText && !goog.string.isEmptyOrWhitespace(goog.string.makeSafe(labelText))) {
    +    labelText = labelText.toLowerCase();
    +
    +    var nodeList = this.nodeMap_.get(labelText);
    +    if (nodeList) {
    +      // Remove the node from the array.
    +      goog.array.remove(nodeList, node);
    +      if (!!nodeList.length) {
    +        this.nodeMap_.remove(labelText);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Select the first matching node for the given typeahead.
    + * @param {string} typeAhead Typeahead characters to match.
    + * @return {boolean} True iff a node is found.
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.jumpToLabel_ = function(typeAhead) {
    +  var handled = false;
    +  var labels = this.nodeMap_.getKeys(typeAhead);
    +
    +  // Make sure we have at least one matching label.
    +  if (labels && labels.length) {
    +    this.matchingNodeIndex_ = 0;
    +    this.matchingLabelIndex_ = 0;
    +
    +    var nodes = this.nodeMap_.get(labels[0]);
    +    if ((handled = this.selectMatchingNode_(nodes))) {
    +      this.matchingLabels_ = labels;
    +    }
    +  }
    +
    +  // TODO(user): beep when no node is found
    +  return handled;
    +};
    +
    +
    +/**
    + * Select the next or previous node based on the offset.
    + * @param {goog.ui.tree.TypeAhead.Offset} offset DOWN or UP.
    + * @return {boolean} Whether a node is found.
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.jumpTo_ = function(offset) {
    +  var handled = false;
    +  var labels = this.matchingLabels_;
    +
    +  if (labels) {
    +    var nodes = null;
    +    var nodeIndexOutOfRange = false;
    +
    +    // Navigate within the nodes array.
    +    if (this.matchingNodes_) {
    +      var newNodeIndex = this.matchingNodeIndex_ + offset;
    +      if (newNodeIndex >= 0 && newNodeIndex < this.matchingNodes_.length) {
    +        this.matchingNodeIndex_ = newNodeIndex;
    +        nodes = this.matchingNodes_;
    +      } else {
    +        nodeIndexOutOfRange = true;
    +      }
    +    }
    +
    +    // Navigate to the next or previous label.
    +    if (!nodes) {
    +      var newLabelIndex = this.matchingLabelIndex_ + offset;
    +      if (newLabelIndex >= 0 && newLabelIndex < labels.length) {
    +        this.matchingLabelIndex_ = newLabelIndex;
    +      }
    +
    +      if (labels.length > this.matchingLabelIndex_) {
    +        nodes = this.nodeMap_.get(labels[this.matchingLabelIndex_]);
    +      }
    +
    +      // Handle the case where we are moving beyond the available nodes,
    +      // while going UP select the last item of multiple nodes with same label
    +      // and while going DOWN select the first item of next set of nodes
    +      if (nodes && nodes.length && nodeIndexOutOfRange) {
    +        this.matchingNodeIndex_ = (offset == goog.ui.tree.TypeAhead.Offset.UP) ?
    +                                  nodes.length - 1 : 0;
    +      }
    +    }
    +
    +    if ((handled = this.selectMatchingNode_(nodes))) {
    +      this.matchingLabels_ = labels;
    +    }
    +  }
    +
    +  // TODO(user): beep when no node is found
    +  return handled;
    +};
    +
    +
    +/**
    + * Given a nodes array reveals and selects the node while using node index.
    + * @param {Array<goog.ui.tree.BaseNode>|undefined} nodes Nodes array to select
    + *     the node from.
    + * @return {boolean} Whether a matching node was found.
    + * @private
    + */
    +goog.ui.tree.TypeAhead.prototype.selectMatchingNode_ = function(nodes) {
    +  var node;
    +
    +  if (nodes) {
    +    // Find the matching node.
    +    if (this.matchingNodeIndex_ < nodes.length) {
    +      node = nodes[this.matchingNodeIndex_];
    +      this.matchingNodes_ = nodes;
    +    }
    +
    +    if (node) {
    +      node.reveal();
    +      node.select();
    +    }
    +  }
    +
    +  return !!node;
    +};
    +
    +
    +/**
    + * Clears the typeahead buffer.
    + */
    +goog.ui.tree.TypeAhead.prototype.clear = function() {
    +  this.buffer_ = '';
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.html b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.html
    new file mode 100644
    index 0000000000..9194cd11fa
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.tree.TypeAhead
    +  </title>
    +  <script src="../../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.ui.tree.TypeAheadTest');
    +  </script>
    + </head>
    + <body>
    +  <div id="treeContainer" style="width:400px">
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.js b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.js
    new file mode 100644
    index 0000000000..b70e40ae54
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tree/typeahead_test.js
    @@ -0,0 +1,127 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.tree.TypeAheadTest');
    +goog.setTestOnly('goog.ui.tree.TypeAheadTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.tree.TreeControl');
    +goog.require('goog.ui.tree.TypeAhead');
    +function makeATree() {
    +  var tree = new goog.ui.tree.TreeControl('root');
    +  var testData = ['level1',
    +    [['level2', [['eve', []], ['eve2', []]], []],
    +     ['level22', [['eve', []], ['eve3', []]], []]],
    +    []];
    +
    +  createTreeFromTestData(tree, testData, 3);
    +
    +  tree.createDom();
    +  goog.dom.getElement('treeContainer').appendChild(tree.getElement());
    +  tree.enterDocument();
    +
    +  return tree;
    +}
    +
    +function createTreeFromTestData(node, data, maxLevels) {
    +  node.setHtml(data[0]);
    +  if (maxLevels < 0) {
    +    return;
    +  }
    +
    +  var children = data[1];
    +  for (var i = 0; i < children.length; i++) {
    +    var child = children[i];
    +    var childNode = node.getTree().createNode('');
    +    node.add(childNode);
    +    createTreeFromTestData(childNode, child, maxLevels - 1);
    +  }
    +}
    +
    +
    +/**
    + * Test jumpToLabel_ functionality.
    + */
    +function testJumpToLabel() {
    +  var tree = makeATree();
    +  var typeAhead = tree.typeAhead_;
    +
    +  // Test the case when only one matching entry exists.
    +  var handled = typeAhead.jumpToLabel_('level1');
    +  var selectedItem = tree.getSelectedItem();
    +  assertTrue(handled && selectedItem.getHtml() == 'level1');
    +
    +  // Test the case when more than one matching entry exists.
    +  handled = typeAhead.jumpToLabel_('eve');
    +  selectedItem = tree.getSelectedItem();
    +  assertTrue(handled && selectedItem.getHtml() == 'eve');
    +
    +  // Test the case when the matching entry is at a deeper level.
    +  handled = typeAhead.jumpToLabel_('eve3');
    +  selectedItem = tree.getSelectedItem();
    +  assertTrue(handled && selectedItem.getHtml() == 'eve3');
    +}
    +
    +
    +/**
    + * Test jumpTo_ functionality.
    + */
    +function testJumpTo() {
    +  var tree = makeATree();
    +  var typeAhead = tree.typeAhead_;
    +
    +  // Jump to the first matching 'eve', followed by Ctrl+DOWN to jump to
    +  // second matching 'eve'
    +  var handled = typeAhead.jumpToLabel_('eve') &&
    +                typeAhead.jumpTo_(goog.ui.tree.TypeAhead.Offset.DOWN);
    +  var selectedItem = tree.getSelectedItem();
    +  assertTrue(handled && selectedItem.getHtml() == 'eve');
    +
    +  // Simulate a DOWN key on the tree, now the selection should be on 'eve3'
    +  var e = new Object();
    +  e.keyCode = goog.events.KeyCodes.DOWN;
    +  e.preventDefault = function() {};
    +  handled = tree.handleKeyEvent(e);
    +  selectedItem = tree.getSelectedItem();
    +  assertTrue(handled && selectedItem.getHtml() == 'eve3');
    +}
    +
    +
    +/**
    + * Test handleTypeAheadChar functionality.
    + */
    +function testHandleTypeAheadChar() {
    +  var tree = makeATree();
    +  var typeAhead = tree.typeAhead_;
    +  var e = new Object();
    +
    +  // Period character('.'): keyCode = 190, charCode = 46
    +  // String.fromCharCode(190) = '3/4'  <-- incorrect
    +  // String.fromCharCode(46) = '.'  <-- correct
    +  e.keyCode = goog.events.KeyCodes.PERIOD;
    +  e.charCode = 46;
    +  e.preventDefault = function() {};
    +  typeAhead.handleTypeAheadChar(e);
    +  assertEquals('.', typeAhead.buffer_);
    +
    +  // charCode not supplied.
    +  // This is expected to work only for alpha-num characters.
    +  e.keyCode = goog.events.KeyCodes.A;
    +  e.charCode = undefined;
    +  typeAhead.buffer_ = '';
    +  typeAhead.handleTypeAheadChar(e);
    +  assertEquals('a', typeAhead.buffer_);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitem.js b/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitem.js
    new file mode 100644
    index 0000000000..b4f59a6a58
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitem.js
    @@ -0,0 +1,196 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview A menu item class that supports three state checkbox semantics.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.TriStateMenuItem');
    +goog.provide('goog.ui.TriStateMenuItem.State');
    +
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.Component');
    +goog.require('goog.ui.MenuItem');
    +goog.require('goog.ui.TriStateMenuItemRenderer');
    +goog.require('goog.ui.registry');
    +
    +
    +
    +/**
    + * Class representing a three state checkbox menu item.
    + *
    + * @param {goog.ui.ControlContent} content Text caption or DOM structure
    + *     to display as the content of the item (use to add icons or styling to
    + *     menus).
    + * @param {Object=} opt_model Data/model associated with the menu item.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
    + *     document interactions.
    + * @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
    + * @param {boolean=} opt_alwaysAllowPartial  If true, always allow partial
    + *     state.
    + * @constructor
    + * @extends {goog.ui.MenuItem}
    + * TODO(attila): Figure out how to better integrate this into the
    + * goog.ui.Control state management framework.
    + * @final
    + */
    +goog.ui.TriStateMenuItem = function(content, opt_model, opt_domHelper,
    +    opt_renderer, opt_alwaysAllowPartial) {
    +  goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper,
    +      opt_renderer || new goog.ui.TriStateMenuItemRenderer());
    +  this.setCheckable(true);
    +  this.alwaysAllowPartial_ = opt_alwaysAllowPartial || false;
    +};
    +goog.inherits(goog.ui.TriStateMenuItem, goog.ui.MenuItem);
    +
    +
    +/**
    + * Checked states for component.
    + * @enum {number}
    + */
    +goog.ui.TriStateMenuItem.State = {
    +  /**
    +   * Component is not checked.
    +   */
    +  NOT_CHECKED: 0,
    +
    +  /**
    +   * Component is partially checked.
    +   */
    +  PARTIALLY_CHECKED: 1,
    +
    +  /**
    +   * Component is fully checked.
    +   */
    +  FULLY_CHECKED: 2
    +};
    +
    +
    +/**
    + * Menu item's checked state.
    + * @type {goog.ui.TriStateMenuItem.State}
    + * @private
    + */
    +goog.ui.TriStateMenuItem.prototype.checkState_ =
    +    goog.ui.TriStateMenuItem.State.NOT_CHECKED;
    +
    +
    +/**
    + * Whether the partial state can be toggled.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.TriStateMenuItem.prototype.allowPartial_ = false;
    +
    +
    +/**
    + * Used to override allowPartial_ to force the third state to always be
    + * permitted.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.TriStateMenuItem.prototype.alwaysAllowPartial_ = false;
    +
    +
    +/**
    + * @return {goog.ui.TriStateMenuItem.State} The menu item's check state.
    + */
    +goog.ui.TriStateMenuItem.prototype.getCheckedState = function() {
    +  return this.checkState_;
    +};
    +
    +
    +/**
    + * Sets the checked state.
    + * @param {goog.ui.TriStateMenuItem.State} state The checked state.
    + */
    +goog.ui.TriStateMenuItem.prototype.setCheckedState = function(state) {
    +  this.setCheckedState_(state);
    +  this.allowPartial_ =
    +      state == goog.ui.TriStateMenuItem.State.PARTIALLY_CHECKED;
    +};
    +
    +
    +/**
    + * Sets the checked state and updates the CSS styling. Dispatches a
    + * {@code CHECK} or {@code UNCHECK} event prior to changing the component's
    + * state, which may be caught and canceled to prevent the component from
    + * changing state.
    + * @param {goog.ui.TriStateMenuItem.State} state The checked state.
    + * @private
    + */
    +goog.ui.TriStateMenuItem.prototype.setCheckedState_ = function(state) {
    +  if (this.dispatchEvent(state != goog.ui.TriStateMenuItem.State.NOT_CHECKED ?
    +                             goog.ui.Component.EventType.CHECK :
    +                             goog.ui.Component.EventType.UNCHECK)) {
    +    this.setState(goog.ui.Component.State.CHECKED,
    +        state != goog.ui.TriStateMenuItem.State.NOT_CHECKED);
    +    this.checkState_ = state;
    +    this.updatedCheckedStateClassNames_();
    +  }
    +};
    +
    +
    +/** @override */
    +goog.ui.TriStateMenuItem.prototype.performActionInternal = function(e) {
    +  switch (this.getCheckedState()) {
    +    case goog.ui.TriStateMenuItem.State.NOT_CHECKED:
    +      this.setCheckedState_(this.alwaysAllowPartial_ || this.allowPartial_ ?
    +          goog.ui.TriStateMenuItem.State.PARTIALLY_CHECKED :
    +          goog.ui.TriStateMenuItem.State.FULLY_CHECKED);
    +      break;
    +    case goog.ui.TriStateMenuItem.State.PARTIALLY_CHECKED:
    +      this.setCheckedState_(goog.ui.TriStateMenuItem.State.FULLY_CHECKED);
    +      break;
    +    case goog.ui.TriStateMenuItem.State.FULLY_CHECKED:
    +      this.setCheckedState_(goog.ui.TriStateMenuItem.State.NOT_CHECKED);
    +      break;
    +  }
    +
    +  var checkboxClass = goog.getCssName(
    +      this.getRenderer().getCssClass(), 'checkbox');
    +  var clickOnCheckbox = e.target && goog.dom.classlist.contains(
    +      /** @type {!Element} */ (e.target), checkboxClass);
    +
    +  return this.dispatchEvent(clickOnCheckbox || this.allowPartial_ ?
    +      goog.ui.Component.EventType.CHANGE :
    +      goog.ui.Component.EventType.ACTION);
    +};
    +
    +
    +/**
    + * Updates the extra class names applied to the menu item element.
    + * @private
    + */
    +goog.ui.TriStateMenuItem.prototype.updatedCheckedStateClassNames_ = function() {
    +  var renderer = this.getRenderer();
    +  renderer.enableExtraClassName(
    +      this, goog.getCssName(renderer.getCssClass(), 'partially-checked'),
    +      this.getCheckedState() ==
    +      goog.ui.TriStateMenuItem.State.PARTIALLY_CHECKED);
    +  renderer.enableExtraClassName(
    +      this, goog.getCssName(renderer.getCssClass(), 'fully-checked'),
    +      this.getCheckedState() == goog.ui.TriStateMenuItem.State.FULLY_CHECKED);
    +};
    +
    +
    +// Register a decorator factory function for goog.ui.TriStateMenuItemRenderer.
    +goog.ui.registry.setDecoratorByClassName(
    +    goog.ui.TriStateMenuItemRenderer.CSS_CLASS,
    +    function() {
    +      // TriStateMenuItem defaults to using TriStateMenuItemRenderer.
    +      return new goog.ui.TriStateMenuItem(null);
    +    });
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitemrenderer.js b/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitemrenderer.js
    new file mode 100644
    index 0000000000..961511f4fb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/tristatemenuitemrenderer.js
    @@ -0,0 +1,92 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Renderer for {@link goog.ui.TriStateMenuItem}s.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + */
    +
    +goog.provide('goog.ui.TriStateMenuItemRenderer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.ui.MenuItemRenderer');
    +
    +
    +
    +/**
    + * Default renderer for {@link goog.ui.TriStateMenuItemRenderer}s. Each item has
    + * the following structure:
    + *    <div class="goog-tristatemenuitem">
    + *        <div class="goog-tristatemenuitem-checkbox"></div>
    + *        <div>...(content)...</div>
    + *    </div>
    + * @constructor
    + * @extends {goog.ui.MenuItemRenderer}
    + * @final
    + */
    +goog.ui.TriStateMenuItemRenderer = function() {
    +  goog.ui.MenuItemRenderer.call(this);
    +};
    +goog.inherits(goog.ui.TriStateMenuItemRenderer, goog.ui.MenuItemRenderer);
    +goog.addSingletonGetter(goog.ui.TriStateMenuItemRenderer);
    +
    +
    +/**
    + * CSS class name the renderer applies to menu item elements.
    + * @type {string}
    + */
    +goog.ui.TriStateMenuItemRenderer.CSS_CLASS =
    +    goog.getCssName('goog-tristatemenuitem');
    +
    +
    +/**
    + * Overrides {@link goog.ui.ControlRenderer#decorate} by initializing the
    + * menu item to checkable based on whether the element to be decorated has
    + * extra styling indicating that it should be.
    + * @param {goog.ui.Control} item goog.ui.TriStateMenuItem to decorate
    + *     the element.
    + * @param {Element} element Element to decorate.
    + * @return {!Element} Decorated element.
    + * @override
    + */
    +goog.ui.TriStateMenuItemRenderer.prototype.decorate = function(item, element) {
    +  element = goog.ui.TriStateMenuItemRenderer.superClass_.decorate.call(this,
    +      item, element);
    +  this.setCheckable(item, element, true);
    +
    +  goog.asserts.assert(element);
    +
    +  if (goog.dom.classlist.contains(element,
    +      goog.getCssName(this.getCssClass(), 'fully-checked'))) {
    +    item.setCheckedState(/** @suppress {missingRequire} */
    +        goog.ui.TriStateMenuItem.State.FULLY_CHECKED);
    +  } else if (goog.dom.classlist.contains(element,
    +      goog.getCssName(this.getCssClass(), 'partially-checked'))) {
    +    item.setCheckedState(/** @suppress {missingRequire} */
    +        goog.ui.TriStateMenuItem.State.PARTIALLY_CHECKED);
    +  } else {
    +    item.setCheckedState(/** @suppress {missingRequire} */
    +        goog.ui.TriStateMenuItem.State.NOT_CHECKED);
    +  }
    +
    +  return element;
    +};
    +
    +
    +/** @override */
    +goog.ui.TriStateMenuItemRenderer.prototype.getCssClass = function() {
    +  return goog.ui.TriStateMenuItemRenderer.CSS_CLASS;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/twothumbslider.js b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider.js
    new file mode 100644
    index 0000000000..79485112f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider.js
    @@ -0,0 +1,158 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Twothumbslider is a slider that allows to select a subrange
    + * within a range by dragging two thumbs. The selected sub-range is exposed
    + * through getValue() and getExtent().
    + *
    + * To decorate, the twothumbslider should be bound to an element with the class
    + * name 'goog-twothumbslider-[vertical / horizontal]' containing children with
    + * the classname 'goog-twothumbslider-value-thumb' and
    + * 'goog-twothumbslider-extent-thumb', respectively.
    + *
    + * Decorate Example:
    + * <div id="twothumbslider" class="goog-twothumbslider-horizontal">
    + *   <div class="goog-twothumbslider-value-thumb">
    + *   <div class="goog-twothumbslider-extent-thumb">
    + * </div>
    + * <script>
    + *
    + * var slider = new goog.ui.TwoThumbSlider;
    + * slider.decorate(document.getElementById('twothumbslider'));
    + *
    + * TODO(user): add a11y once we know what this element is
    + *
    + * @see ../demos/twothumbslider.html
    + */
    +
    +goog.provide('goog.ui.TwoThumbSlider');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.dom');
    +goog.require('goog.ui.SliderBase');
    +
    +
    +
    +/**
    + * This creates a TwoThumbSlider object.
    + * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
    + * @constructor
    + * @extends {goog.ui.SliderBase}
    + */
    +goog.ui.TwoThumbSlider = function(opt_domHelper) {
    +  goog.ui.SliderBase.call(this, opt_domHelper);
    +  this.rangeModel.setValue(this.getMinimum());
    +  this.rangeModel.setExtent(this.getMaximum() - this.getMinimum());
    +};
    +goog.inherits(goog.ui.TwoThumbSlider, goog.ui.SliderBase);
    +goog.tagUnsealableClass(goog.ui.TwoThumbSlider);
    +
    +
    +/**
    + * The prefix we use for the CSS class names for the slider and its elements.
    + * @type {string}
    + */
    +goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX =
    +    goog.getCssName('goog-twothumbslider');
    +
    +
    +/**
    + * CSS class name for the value thumb element.
    + * @type {string}
    + */
    +goog.ui.TwoThumbSlider.VALUE_THUMB_CSS_CLASS =
    +    goog.getCssName(goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX, 'value-thumb');
    +
    +
    +/**
    + * CSS class name for the extent thumb element.
    + * @type {string}
    + */
    +goog.ui.TwoThumbSlider.EXTENT_THUMB_CSS_CLASS =
    +    goog.getCssName(goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX, 'extent-thumb');
    +
    +
    +/**
    + * CSS class name for the range highlight element.
    + * @type {string}
    + */
    +goog.ui.TwoThumbSlider.RANGE_HIGHLIGHT_CSS_CLASS =
    +    goog.getCssName(goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX, 'rangehighlight');
    +
    +
    +/**
    + * @param {goog.ui.SliderBase.Orientation} orient orientation of the slider.
    + * @return {string} The CSS class applied to the twothumbslider element.
    + * @protected
    + * @override
    + */
    +goog.ui.TwoThumbSlider.prototype.getCssClass = function(orient) {
    +  return orient == goog.ui.SliderBase.Orientation.VERTICAL ?
    +      goog.getCssName(goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX, 'vertical') :
    +      goog.getCssName(goog.ui.TwoThumbSlider.CSS_CLASS_PREFIX, 'horizontal');
    +};
    +
    +
    +/**
    + * This creates a thumb element with the specified CSS class name.
    + * @param {string} cs  CSS class name of the thumb to be created.
    + * @return {!HTMLDivElement} The created thumb element.
    + * @private
    + */
    +goog.ui.TwoThumbSlider.prototype.createThumb_ = function(cs) {
    +  var thumb = this.getDomHelper().createDom('div', cs);
    +  goog.a11y.aria.setRole(thumb, goog.a11y.aria.Role.BUTTON);
    +  return /** @type {!HTMLDivElement} */ (thumb);
    +};
    +
    +
    +/**
    + * Creates the thumb members for a twothumbslider. If the
    + * element contains a child with a class name 'goog-twothumbslider-value-thumb'
    + * (or 'goog-twothumbslider-extent-thumb', respectively), then that will be used
    + * as the valueThumb (or as the extentThumb, respectively). If the element
    + * contains a child with a class name 'goog-twothumbslider-rangehighlight',
    + * then that will be used as the range highlight.
    + * @override
    + */
    +goog.ui.TwoThumbSlider.prototype.createThumbs = function() {
    +  // find range highlight and thumbs
    +  var valueThumb = goog.dom.getElementsByTagNameAndClass(
    +      null, goog.ui.TwoThumbSlider.VALUE_THUMB_CSS_CLASS, this.getElement())[0];
    +  var extentThumb = goog.dom.getElementsByTagNameAndClass(null,
    +      goog.ui.TwoThumbSlider.EXTENT_THUMB_CSS_CLASS, this.getElement())[0];
    +  var rangeHighlight = goog.dom.getElementsByTagNameAndClass(null,
    +      goog.ui.TwoThumbSlider.RANGE_HIGHLIGHT_CSS_CLASS, this.getElement())[0];
    +  if (!valueThumb) {
    +    valueThumb =
    +        this.createThumb_(goog.ui.TwoThumbSlider.VALUE_THUMB_CSS_CLASS);
    +    this.getElement().appendChild(valueThumb);
    +  }
    +  if (!extentThumb) {
    +    extentThumb =
    +        this.createThumb_(goog.ui.TwoThumbSlider.EXTENT_THUMB_CSS_CLASS);
    +    this.getElement().appendChild(extentThumb);
    +  }
    +  if (!rangeHighlight) {
    +    rangeHighlight = this.getDomHelper().createDom('div',
    +        goog.ui.TwoThumbSlider.RANGE_HIGHLIGHT_CSS_CLASS);
    +    // Insert highlight before value thumb so that it renders under the thumbs.
    +    this.getDomHelper().insertSiblingBefore(rangeHighlight, valueThumb);
    +  }
    +  this.valueThumb = valueThumb;
    +  this.extentThumb = extentThumb;
    +  this.rangeHighlight = rangeHighlight;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.html b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.html
    new file mode 100644
    index 0000000000..00aa71b21b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.html
    @@ -0,0 +1,25 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +  Author: chrishenry@google.com (Chris Henry)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.TwoThumbSlider
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.TwoThumbSliderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.js b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.js
    new file mode 100644
    index 0000000000..8cd168f856
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/twothumbslider_test.js
    @@ -0,0 +1,34 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.TwoThumbSliderTest');
    +goog.setTestOnly('goog.ui.TwoThumbSliderTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.SliderBase');
    +goog.require('goog.ui.TwoThumbSlider');
    +
    +var slider;
    +
    +function tearDown() {
    +  goog.dispose(slider);
    +}
    +
    +function testGetCssClass() {
    +  slider = new goog.ui.TwoThumbSlider();
    +  assertEquals('goog-twothumbslider-horizontal',
    +      slider.getCssClass(goog.ui.SliderBase.Orientation.HORIZONTAL));
    +  assertEquals('goog-twothumbslider-vertical',
    +      slider.getCssClass(goog.ui.SliderBase.Orientation.VERTICAL));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/zippy.js b/mapbox-gl/closure-library/closure/goog/ui/zippy.js
    new file mode 100644
    index 0000000000..f48b8db115
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/zippy.js
    @@ -0,0 +1,461 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Zippy widget implementation.
    + *
    + * @author eae@google.com (Emil A Eklund)
    + * @see ../demos/zippy.html
    + */
    +
    +goog.provide('goog.ui.Zippy');
    +goog.provide('goog.ui.Zippy.Events');
    +goog.provide('goog.ui.ZippyEvent');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.a11y.aria.Role');
    +goog.require('goog.a11y.aria.State');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventHandler');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.style');
    +
    +
    +
    +/**
    + * Zippy widget. Expandable/collapsible container, clicking the header toggles
    + * the visibility of the content.
    + *
    + * @extends {goog.events.EventTarget}
    + * @param {Element|string|null} header Header element, either element
    + *     reference, string id or null if no header exists.
    + * @param {Element|string|function():Element=} opt_content Content element
    + *     (if any), either element reference or string id.  If skipped, the caller
    + *     should handle the TOGGLE event in its own way. If a function is passed,
    + *     then if will be called to create the content element the first time the
    + *     zippy is expanded.
    + * @param {boolean=} opt_expanded Initial expanded/visibility state. If
    + *     undefined, attempts to infer the state from the DOM. Setting visibility
    + *     using one of the standard Soy templates guarantees correct inference.
    + * @param {Element|string=} opt_expandedHeader Element to use as the header when
    + *     the zippy is expanded.
    + * @param {goog.dom.DomHelper=} opt_domHelper An optional DOM helper.
    + * @constructor
    + */
    +goog.ui.Zippy = function(header, opt_content, opt_expanded,
    +    opt_expandedHeader, opt_domHelper) {
    +  goog.ui.Zippy.base(this, 'constructor');
    +
    +  /**
    +   * DomHelper used to interact with the document, allowing components to be
    +   * created in a different window.
    +   * @type {!goog.dom.DomHelper}
    +   * @private
    +   */
    +  this.dom_ = opt_domHelper || goog.dom.getDomHelper();
    +
    +  /**
    +   * Header element or null if no header exists.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elHeader_ = this.dom_.getElement(header) || null;
    +
    +  /**
    +   * When present, the header to use when the zippy is expanded.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elExpandedHeader_ = this.dom_.getElement(opt_expandedHeader || null);
    +
    +  /**
    +   * Function that will create the content element, or false if there is no such
    +   * function.
    +   * @type {?function():Element}
    +   * @private
    +   */
    +  this.lazyCreateFunc_ = goog.isFunction(opt_content) ? opt_content : null;
    +
    +  /**
    +   * Content element.
    +   * @type {Element}
    +   * @private
    +   */
    +  this.elContent_ = this.lazyCreateFunc_ || !opt_content ? null :
    +      this.dom_.getElement(/** @type {!Element} */ (opt_content));
    +
    +  /**
    +   * Expanded state.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.expanded_ = opt_expanded == true;
    +  if (!goog.isDef(opt_expanded) && !this.lazyCreateFunc_) {
    +    // For the dual caption case, we can get expanded_ from the visibility of
    +    // the expandedHeader. For the single-caption case, we use the
    +    // presence/absence of the relevant class. Using one of the standard Soy
    +    // templates guarantees that this will work.
    +    if (this.elExpandedHeader_) {
    +      this.expanded_ = goog.style.isElementShown(this.elExpandedHeader_);
    +    } else if (this.elHeader_) {
    +      this.expanded_ = goog.dom.classlist.contains(
    +          this.elHeader_, goog.getCssName('goog-zippy-expanded'));
    +    }
    +  }
    +
    +
    +  /**
    +   * A keyboard events handler. If there are two headers it is shared for both.
    +   * @type {goog.events.EventHandler<!goog.ui.Zippy>}
    +   * @private
    +   */
    +  this.keyboardEventHandler_ = new goog.events.EventHandler(this);
    +
    +  /**
    +   * A mouse events handler. If there are two headers it is shared for both.
    +   * @type {goog.events.EventHandler<!goog.ui.Zippy>}
    +   * @private
    +   */
    +  this.mouseEventHandler_ = new goog.events.EventHandler(this);
    +
    +  var self = this;
    +  function addHeaderEvents(el) {
    +    if (el) {
    +      el.tabIndex = 0;
    +      goog.a11y.aria.setRole(el, self.getAriaRole());
    +      goog.dom.classlist.add(el, goog.getCssName('goog-zippy-header'));
    +      self.enableMouseEventsHandling_(el);
    +      self.enableKeyboardEventsHandling_(el);
    +    }
    +  }
    +  addHeaderEvents(this.elHeader_);
    +  addHeaderEvents(this.elExpandedHeader_);
    +
    +  // initialize based on expanded state
    +  this.setExpanded(this.expanded_);
    +};
    +goog.inherits(goog.ui.Zippy, goog.events.EventTarget);
    +goog.tagUnsealableClass(goog.ui.Zippy);
    +
    +
    +/**
    + * Constants for event names
    + *
    + * @const
    + */
    +goog.ui.Zippy.Events = {
    +  // Zippy will dispatch an ACTION event for user interaction. Mimics
    +  // {@code goog.ui.Controls#performActionInternal} by first changing
    +  // the toggle state and then dispatching an ACTION event.
    +  ACTION: 'action',
    +  // Zippy state is toggled from collapsed to expanded or vice versa.
    +  TOGGLE: 'toggle'
    +};
    +
    +
    +/**
    + * Whether to listen for and handle mouse events; defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Zippy.prototype.handleMouseEvents_ = true;
    +
    +
    +/**
    + * Whether to listen for and handle key events; defaults to true.
    + * @type {boolean}
    + * @private
    + */
    +goog.ui.Zippy.prototype.handleKeyEvents_ = true;
    +
    +
    +/** @override */
    +goog.ui.Zippy.prototype.disposeInternal = function() {
    +  goog.ui.Zippy.base(this, 'disposeInternal');
    +  goog.dispose(this.keyboardEventHandler_);
    +  goog.dispose(this.mouseEventHandler_);
    +};
    +
    +
    +/**
    + * @return {goog.a11y.aria.Role} The ARIA role to be applied to Zippy element.
    + */
    +goog.ui.Zippy.prototype.getAriaRole = function() {
    +  return goog.a11y.aria.Role.TAB;
    +};
    +
    +
    +/**
    + * @return {Element} The content element.
    + */
    +goog.ui.Zippy.prototype.getContentElement = function() {
    +  return this.elContent_;
    +};
    +
    +
    +/**
    + * @return {Element} The visible header element.
    + */
    +goog.ui.Zippy.prototype.getVisibleHeaderElement = function() {
    +  var expandedHeader = this.elExpandedHeader_;
    +  return expandedHeader && goog.style.isElementShown(expandedHeader) ?
    +      expandedHeader : this.elHeader_;
    +};
    +
    +
    +/**
    + * Expands content pane.
    + */
    +goog.ui.Zippy.prototype.expand = function() {
    +  this.setExpanded(true);
    +};
    +
    +
    +/**
    + * Collapses content pane.
    + */
    +goog.ui.Zippy.prototype.collapse = function() {
    +  this.setExpanded(false);
    +};
    +
    +
    +/**
    + * Toggles expanded state.
    + */
    +goog.ui.Zippy.prototype.toggle = function() {
    +  this.setExpanded(!this.expanded_);
    +};
    +
    +
    +/**
    + * Sets expanded state.
    + *
    + * @param {boolean} expanded Expanded/visibility state.
    + */
    +goog.ui.Zippy.prototype.setExpanded = function(expanded) {
    +  if (this.elContent_) {
    +    // Hide the element, if one is provided.
    +    goog.style.setElementShown(this.elContent_, expanded);
    +  } else if (expanded && this.lazyCreateFunc_) {
    +    // Assume that when the element is not hidden upon creation.
    +    this.elContent_ = this.lazyCreateFunc_();
    +  }
    +  if (this.elContent_) {
    +    goog.dom.classlist.add(this.elContent_,
    +        goog.getCssName('goog-zippy-content'));
    +  }
    +
    +  if (this.elExpandedHeader_) {
    +    // Hide the show header and show the hide one.
    +    goog.style.setElementShown(this.elHeader_, !expanded);
    +    goog.style.setElementShown(this.elExpandedHeader_, expanded);
    +  } else {
    +    // Update header image, if any.
    +    this.updateHeaderClassName(expanded);
    +  }
    +
    +  this.setExpandedInternal(expanded);
    +
    +  // Fire toggle event
    +  this.dispatchEvent(new goog.ui.ZippyEvent(goog.ui.Zippy.Events.TOGGLE,
    +                                            this, this.expanded_));
    +};
    +
    +
    +/**
    + * Sets expanded internal state.
    + *
    + * @param {boolean} expanded Expanded/visibility state.
    + * @protected
    + */
    +goog.ui.Zippy.prototype.setExpandedInternal = function(expanded) {
    +  this.expanded_ = expanded;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the zippy is expanded.
    + */
    +goog.ui.Zippy.prototype.isExpanded = function() {
    +  return this.expanded_;
    +};
    +
    +
    +/**
    + * Updates the header element's className and ARIA (accessibility) EXPANDED
    + * state.
    + *
    + * @param {boolean} expanded Expanded/visibility state.
    + * @protected
    + */
    +goog.ui.Zippy.prototype.updateHeaderClassName = function(expanded) {
    +  if (this.elHeader_) {
    +    goog.dom.classlist.enable(this.elHeader_,
    +        goog.getCssName('goog-zippy-expanded'), expanded);
    +    goog.dom.classlist.enable(this.elHeader_,
    +        goog.getCssName('goog-zippy-collapsed'), !expanded);
    +    goog.a11y.aria.setState(this.elHeader_,
    +        goog.a11y.aria.State.EXPANDED,
    +        expanded);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the Zippy handles its own key events.
    + */
    +goog.ui.Zippy.prototype.isHandleKeyEvents = function() {
    +  return this.handleKeyEvents_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the Zippy handles its own mouse events.
    + */
    +goog.ui.Zippy.prototype.isHandleMouseEvents = function() {
    +  return this.handleMouseEvents_;
    +};
    +
    +
    +/**
    + * Sets whether the Zippy handles it's own keyboard events.
    + * @param {boolean} enable Whether the Zippy handles keyboard events.
    + */
    +goog.ui.Zippy.prototype.setHandleKeyboardEvents = function(enable) {
    +  if (this.handleKeyEvents_ != enable) {
    +    this.handleKeyEvents_ = enable;
    +    if (enable) {
    +      this.enableKeyboardEventsHandling_(this.elHeader_);
    +      this.enableKeyboardEventsHandling_(this.elExpandedHeader_);
    +    } else {
    +      this.keyboardEventHandler_.removeAll();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Sets whether the Zippy handles it's own mouse events.
    + * @param {boolean} enable Whether the Zippy handles mouse events.
    + */
    +goog.ui.Zippy.prototype.setHandleMouseEvents = function(enable) {
    +  if (this.handleMouseEvents_ != enable) {
    +    this.handleMouseEvents_ = enable;
    +    if (enable) {
    +      this.enableMouseEventsHandling_(this.elHeader_);
    +      this.enableMouseEventsHandling_(this.elExpandedHeader_);
    +    } else {
    +      this.mouseEventHandler_.removeAll();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Enables keyboard events handling for the passed header element.
    + * @param {Element} header The header element.
    + * @private
    + */
    +goog.ui.Zippy.prototype.enableKeyboardEventsHandling_ = function(header) {
    +  if (header) {
    +    this.keyboardEventHandler_.listen(header, goog.events.EventType.KEYDOWN,
    +        this.onHeaderKeyDown_);
    +  }
    +};
    +
    +
    +/**
    + * Enables mouse events handling for the passed header element.
    + * @param {Element} header The header element.
    + * @private
    + */
    +goog.ui.Zippy.prototype.enableMouseEventsHandling_ = function(header) {
    +  if (header) {
    +    this.mouseEventHandler_.listen(header, goog.events.EventType.CLICK,
    +        this.onHeaderClick_);
    +  }
    +};
    +
    +
    +/**
    + * KeyDown event handler for header element. Enter and space toggles expanded
    + * state.
    + *
    + * @param {goog.events.BrowserEvent} event KeyDown event.
    + * @private
    + */
    +goog.ui.Zippy.prototype.onHeaderKeyDown_ = function(event) {
    +  if (event.keyCode == goog.events.KeyCodes.ENTER ||
    +      event.keyCode == goog.events.KeyCodes.SPACE) {
    +
    +    this.toggle();
    +    this.dispatchActionEvent_();
    +
    +    // Prevent enter key from submitting form.
    +    event.preventDefault();
    +
    +    event.stopPropagation();
    +  }
    +};
    +
    +
    +/**
    + * Click event handler for header element.
    + *
    + * @param {goog.events.BrowserEvent} event Click event.
    + * @private
    + */
    +goog.ui.Zippy.prototype.onHeaderClick_ = function(event) {
    +  this.toggle();
    +  this.dispatchActionEvent_();
    +};
    +
    +
    +/**
    + * Dispatch an ACTION event whenever there is user interaction with the header.
    + * Please note that after the zippy state change is completed a TOGGLE event
    + * will be dispatched. However, the TOGGLE event is dispatch on every toggle,
    + * including programmatic call to {@code #toggle}.
    + * @private
    + */
    +goog.ui.Zippy.prototype.dispatchActionEvent_ = function() {
    +  this.dispatchEvent(new goog.events.Event(goog.ui.Zippy.Events.ACTION, this));
    +};
    +
    +
    +
    +/**
    + * Object representing a zippy toggle event.
    + *
    + * @param {string} type Event type.
    + * @param {goog.ui.Zippy} target Zippy widget initiating event.
    + * @param {boolean} expanded Expanded state.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @final
    + */
    +goog.ui.ZippyEvent = function(type, target, expanded) {
    +  goog.ui.ZippyEvent.base(this, 'constructor', type, target);
    +
    +  /**
    +   * The expanded state.
    +   * @type {boolean}
    +   */
    +  this.expanded = expanded;
    +};
    +goog.inherits(goog.ui.ZippyEvent, goog.events.Event);
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/zippy_test.html b/mapbox-gl/closure-library/closure/goog/ui/zippy_test.html
    new file mode 100644
    index 0000000000..b473324fdb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/zippy_test.html
    @@ -0,0 +1,57 @@
    +<!DOCTYPE html>
    +<html>
    + <!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.ui.Zippy
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +   goog.require('goog.ui.ZippyTest');
    +  </script>
    +  <style type="text/css">
    +   .demo {
    +      border: solid 1px red;
    +      margin: 0 0 20px 0;
    +    }
    +
    +    .demo h2 {
    +      background-color: yellow;
    +      border: solid 1px #ccc;
    +      padding: 2px;
    +      margin: 0;
    +      fint-size: 100%;
    +    }
    +
    +    .demo div {
    +      border: solid 1px #ccc;
    +      padding: 2px;
    +    }
    +  </style>
    + </head>
    + <body>
    +  <div class="demo" id="d1">
    +   <h2 id="t1">
    +    handler
    +   </h2>
    +   <div id="c1">
    +    sem. Suspendisse porta felis ac ipsum. Sed tincidunt dui vitae nulla. Ut
    +    blandit. Nunc non neque. Mauris placerat. Vestibulum mollis tellus id dolor.
    +    Phasellus ac dolor molestie nunc euismod aliquam. Mauris tellus ipsum,
    +    fringilla id, tincidunt eu, vestibulum sit amet, metus. Quisque congue
    +    varius
    +    ligula. Quisque ornare mollis enim. Aliquam erat volutpat. Nulla mattis
    +    venenatis magna.
    +   </div>
    +  </div>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/ui/zippy_test.js b/mapbox-gl/closure-library/closure/goog/ui/zippy_test.js
    new file mode 100644
    index 0000000000..22f461fa0c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/ui/zippy_test.js
    @@ -0,0 +1,267 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.ui.ZippyTest');
    +goog.setTestOnly('goog.ui.ZippyTest');
    +
    +goog.require('goog.a11y.aria');
    +goog.require('goog.dom');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.object');
    +goog.require('goog.testing.events');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.ui.Zippy');
    +
    +var zippy, fakeZippy1, fakeZippy2, contentlessZippy, headerlessZippy;
    +var lazyZippy;
    +var lazyZippyCallCount;
    +var lazyZippyContentEl;
    +var dualHeaderZippy;
    +var dualHeaderZippyCollapsedHeaderEl;
    +var dualHeaderZippyExpandedHeaderEl;
    +
    +function setUp() {
    +  zippy = new goog.ui.Zippy(goog.dom.getElement('t1'),
    +      goog.dom.getElement('c1'));
    +
    +  var fakeControlEl = document.createElement('button');
    +  var fakeContentEl = document.createElement('div');
    +
    +  fakeZippy1 = new goog.ui.Zippy(fakeControlEl.cloneNode(true),
    +      fakeContentEl.cloneNode(true), true);
    +  fakeZippy2 = new goog.ui.Zippy(fakeControlEl.cloneNode(true),
    +      fakeContentEl.cloneNode(true), false);
    +  contentlessZippy = new goog.ui.Zippy(fakeControlEl.cloneNode(true),
    +      undefined, true);
    +  headerlessZippy = new goog.ui.Zippy(null, fakeContentEl.cloneNode(true),
    +      true);
    +
    +  lazyZippyCallCount = 0;
    +  lazyZippyContentEl = fakeContentEl.cloneNode(true);
    +  lazyZippy = new goog.ui.Zippy(goog.dom.getElement('t1'), function() {
    +    lazyZippyCallCount++;
    +    return lazyZippyContentEl;
    +  });
    +  dualHeaderZippyCollapsedHeaderEl = fakeControlEl.cloneNode(true);
    +  dualHeaderZippyExpandedHeaderEl = fakeControlEl.cloneNode(true);
    +  dualHeaderZippy = new goog.ui.Zippy(dualHeaderZippyCollapsedHeaderEl,
    +      fakeContentEl.cloneNode(true), false, dualHeaderZippyExpandedHeaderEl);
    +}
    +
    +function testConstructor() {
    +  assertNotNull('must not be null', zippy);
    +}
    +
    +function testIsExpanded() {
    +  assertEquals('Default expanded must be false', false, zippy.isExpanded());
    +  assertEquals('Expanded must be true', true, fakeZippy1.isExpanded());
    +  assertEquals('Expanded must be false', false, fakeZippy2.isExpanded());
    +  assertEquals('Expanded must be true', true, headerlessZippy.isExpanded());
    +  assertEquals('Expanded must be false', false, lazyZippy.isExpanded());
    +  assertEquals('Expanded must be false', false, dualHeaderZippy.isExpanded());
    +}
    +
    +function tearDown() {
    +  zippy.dispose();
    +  fakeZippy1.dispose();
    +  fakeZippy2.dispose();
    +  contentlessZippy.dispose();
    +  headerlessZippy.dispose();
    +  lazyZippy.dispose();
    +  dualHeaderZippy.dispose();
    +}
    +
    +function testExpandCollapse() {
    +  zippy.expand();
    +  headerlessZippy.expand();
    +  assertEquals('expanded must be true', true, zippy.isExpanded());
    +  assertEquals('expanded must be true', true, headerlessZippy.isExpanded());
    +
    +  zippy.collapse();
    +  headerlessZippy.collapse();
    +  assertEquals('expanded must be false', false, zippy.isExpanded());
    +  assertEquals('expanded must be false', false, headerlessZippy.isExpanded());
    +}
    +
    +function testExpandCollapse_lazyZippy() {
    +  assertEquals('callback should not be called #1.', 0, lazyZippyCallCount);
    +  lazyZippy.collapse();
    +  assertEquals('callback should not be called #2.', 0, lazyZippyCallCount);
    +
    +  lazyZippy.expand();
    +  assertEquals('callback should be called once #1.', 1, lazyZippyCallCount);
    +  assertEquals('expanded must be true', true, lazyZippy.isExpanded());
    +  assertEquals('contentEl should be visible', '',
    +      lazyZippyContentEl.style.display);
    +
    +  lazyZippy.collapse();
    +  assertEquals('callback should be called once #2.', 1, lazyZippyCallCount);
    +  assertEquals('expanded must be false', false, lazyZippy.isExpanded());
    +  assertEquals('contentEl should not be visible', 'none',
    +      lazyZippyContentEl.style.display);
    +
    +  lazyZippy.expand();
    +  assertEquals('callback should be called once #3.', 1, lazyZippyCallCount);
    +  assertEquals('expanded must be true #2', true, lazyZippy.isExpanded());
    +  assertEquals('contentEl should be visible #2', '',
    +      lazyZippyContentEl.style.display);
    +}
    +
    +function testExpandCollapse_dualHeaderZippy() {
    +  dualHeaderZippy.expand();
    +  assertEquals('expanded must be true', true, dualHeaderZippy.isExpanded());
    +  assertFalse('collapsed header should not have state class name #1',
    +      hasCollapseOrExpandClasses(dualHeaderZippyCollapsedHeaderEl));
    +  assertFalse('expanded header should not have state class name #1',
    +      hasCollapseOrExpandClasses(dualHeaderZippyExpandedHeaderEl));
    +
    +  dualHeaderZippy.collapse();
    +  assertEquals('expanded must be false', false, dualHeaderZippy.isExpanded());
    +  assertFalse('collapsed header should not have state class name #2',
    +      hasCollapseOrExpandClasses(dualHeaderZippyCollapsedHeaderEl));
    +  assertFalse('expanded header should not have state class name #2',
    +      hasCollapseOrExpandClasses(dualHeaderZippyExpandedHeaderEl));
    +}
    +
    +function testSetExpand() {
    +  var expanded = !zippy.isExpanded();
    +  zippy.setExpanded(expanded);
    +  assertEquals('expanded must be ' + expanded, expanded, zippy.isExpanded());
    +}
    +
    +function testCssClassesAndAria() {
    +  assertTrue('goog-zippy-header is enabled',
    +      goog.dom.classlist.contains(zippy.elHeader_, 'goog-zippy-header'));
    +  assertNotNull(zippy.elHeader_);
    +  assertEquals('header aria-expanded is false', 'false',
    +      goog.a11y.aria.getState(zippy.elHeader_, 'expanded'));
    +  zippy.setExpanded(true);
    +  assertTrue('goog-zippy-content is enabled',
    +      goog.dom.classlist.contains(zippy.getContentElement(),
    +      'goog-zippy-content'));
    +  assertEquals('header aria role is TAB', 'tab',
    +      goog.a11y.aria.getRole(zippy.elHeader_));
    +  assertEquals('header aria-expanded is true', 'true',
    +      goog.a11y.aria.getState(zippy.elHeader_, 'expanded'));
    +}
    +
    +function testHeaderTabIndex() {
    +  assertEquals('Header tabIndex is 0', 0, zippy.elHeader_.tabIndex);
    +}
    +
    +function testGetVisibleHeaderElement() {
    +  dualHeaderZippy.setExpanded(false);
    +  assertEquals(dualHeaderZippyCollapsedHeaderEl,
    +      dualHeaderZippy.getVisibleHeaderElement());
    +  dualHeaderZippy.setExpanded(true);
    +  assertEquals(dualHeaderZippyExpandedHeaderEl,
    +      dualHeaderZippy.getVisibleHeaderElement());
    +}
    +
    +function testToggle() {
    +  var expanded = !zippy.isExpanded();
    +  zippy.toggle();
    +  assertEquals('expanded must be ' + expanded, expanded, zippy.isExpanded());
    +}
    +
    +function testCustomEventTOGGLE() {
    +  var dispatchedActionCount;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +
    +  var doTest = function(zippyObj) {
    +    dispatchedActionCount = 0;
    +    goog.events.listen(zippyObj, goog.ui.Zippy.Events.TOGGLE, handleAction);
    +    zippy.toggle();
    +    assertEquals('Custom Event must be called ', 1, dispatchedActionCount);
    +  };
    +
    +  doTest(zippy);
    +  doTest(fakeZippy1);
    +  doTest(contentlessZippy);
    +  doTest(headerlessZippy);
    +}
    +
    +function testActionEvent() {
    +  var actionEventCount = 0;
    +  var toggleEventCount = 0;
    +  var handleEvent = function(e) {
    +    if (e.type == goog.ui.Zippy.Events.TOGGLE) {
    +      toggleEventCount++;
    +    } else if (e.type == goog.ui.Zippy.Events.ACTION) {
    +      actionEventCount++;
    +      assertTrue('toggle must have been called first',
    +          toggleEventCount >= actionEventCount);
    +    }
    +  };
    +  goog.events.listen(zippy, goog.object.getValues(goog.ui.Zippy.Events),
    +      handleEvent);
    +  goog.testing.events.fireClickSequence(zippy.elHeader_);
    +  assertEquals('Zippy ACTION event fired', 1, actionEventCount);
    +  assertEquals('Zippy TOGGLE event fired', 1, toggleEventCount);
    +
    +  zippy.toggle();
    +  assertEquals('Zippy ACTION event NOT fired', 1, actionEventCount);
    +  assertEquals('Zippy TOGGLE event fired', 2, toggleEventCount);
    +}
    +
    +function testBasicZippyBehavior() {
    +  var dispatchedActionCount = 0;
    +  var handleAction = function() {
    +    dispatchedActionCount++;
    +  };
    +
    +  goog.events.listen(zippy, goog.ui.Zippy.Events.TOGGLE, handleAction);
    +  goog.testing.events.fireClickSequence(zippy.elHeader_);
    +  assertEquals('Zippy  must have dispatched TOGGLE on click', 1,
    +      dispatchedActionCount);
    +
    +}
    +
    +function hasCollapseOrExpandClasses(el) {
    +  var isCollapsed = goog.dom.classlist.contains(el, 'goog-zippy-collapsed');
    +  var isExpanded = goog.dom.classlist.contains(el, 'goog-zippy-expanded');
    +  return isCollapsed || isExpanded;
    +}
    +
    +function testIsHandleKeyEvent() {
    +  zippy.setHandleKeyboardEvents(false);
    +  assertFalse('Zippy is not handling key events', zippy.isHandleKeyEvents());
    +  assertTrue('Zippy setHandleKeyEvents does not affect handling mouse events',
    +      zippy.isHandleMouseEvents());
    +  assertEquals(0, zippy.keyboardEventHandler_.getListenerCount());
    +
    +  zippy.setHandleKeyboardEvents(true);
    +  assertTrue('Zippy is handling key events', zippy.isHandleKeyEvents());
    +  assertTrue('Zippy setHandleKeyEvents does not affect handling mouse events',
    +      zippy.isHandleMouseEvents());
    +  assertNotEquals(0, zippy.keyboardEventHandler_.getListenerCount());
    +}
    +
    +function testIsHandleMouseEvent() {
    +  zippy.setHandleMouseEvents(false);
    +  assertFalse('Zippy is not handling mouse events',
    +      zippy.isHandleMouseEvents());
    +  assertTrue('Zippy setHandleMouseEvents does not affect handling key events',
    +      zippy.isHandleKeyEvents());
    +  assertEquals(0, zippy.mouseEventHandler_.getListenerCount());
    +
    +  zippy.setHandleMouseEvents(true);
    +  assertTrue('Zippy is handling mouse events', zippy.isHandleMouseEvents());
    +  assertTrue('Zippy setHandleMouseEvents does not affect handling key events',
    +      zippy.isHandleKeyEvents());
    +  assertNotEquals(0, zippy.mouseEventHandler_.getListenerCount());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/uri.js b/mapbox-gl/closure-library/closure/goog/uri/uri.js
    new file mode 100644
    index 0000000000..86b9d47a43
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/uri.js
    @@ -0,0 +1,1526 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Class for parsing and formatting URIs.
    + *
    + * Use goog.Uri(string) to parse a URI string.  Use goog.Uri.create(...) to
    + * create a new instance of the goog.Uri object from Uri parts.
    + *
    + * e.g: <code>var myUri = new goog.Uri(window.location);</code>
    + *
    + * Implements RFC 3986 for parsing/formatting URIs.
    + * http://www.ietf.org/rfc/rfc3986.txt
    + *
    + * Some changes have been made to the interface (more like .NETs), though the
    + * internal representation is now of un-encoded parts, this will change the
    + * behavior slightly.
    + *
    + */
    +
    +goog.provide('goog.Uri');
    +goog.provide('goog.Uri.QueryData');
    +
    +goog.require('goog.array');
    +goog.require('goog.string');
    +goog.require('goog.structs');
    +goog.require('goog.structs.Map');
    +goog.require('goog.uri.utils');
    +goog.require('goog.uri.utils.ComponentIndex');
    +goog.require('goog.uri.utils.StandardQueryParam');
    +
    +
    +
    +/**
    + * This class contains setters and getters for the parts of the URI.
    + * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part
    + * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the
    + * decoded path, <code>/foo bar</code>.
    + *
    + * Reserved characters (see RFC 3986 section 2.2) can be present in
    + * their percent-encoded form in scheme, domain, and path URI components and
    + * will not be auto-decoded. For example:
    + * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will
    + * return <code>relative/path%2fto/resource</code>.
    + *
    + * The constructor accepts an optional unparsed, raw URI string.  The parser
    + * is relaxed, so special characters that aren't escaped but don't cause
    + * ambiguities will not cause parse failures.
    + *
    + * All setters return <code>this</code> and so may be chained, a la
    + * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>.
    + *
    + * @param {*=} opt_uri Optional string URI to parse
    + *        (use goog.Uri.create() to create a URI from parts), or if
    + *        a goog.Uri is passed, a clone is created.
    + * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore
    + * the case of the parameter name.
    + *
    + * @constructor
    + * @struct
    + */
    +goog.Uri = function(opt_uri, opt_ignoreCase) {
    +  // Parse in the uri string
    +  var m;
    +  if (opt_uri instanceof goog.Uri) {
    +    this.ignoreCase_ = goog.isDef(opt_ignoreCase) ?
    +        opt_ignoreCase : opt_uri.getIgnoreCase();
    +    this.setScheme(opt_uri.getScheme());
    +    this.setUserInfo(opt_uri.getUserInfo());
    +    this.setDomain(opt_uri.getDomain());
    +    this.setPort(opt_uri.getPort());
    +    this.setPath(opt_uri.getPath());
    +    this.setQueryData(opt_uri.getQueryData().clone());
    +    this.setFragment(opt_uri.getFragment());
    +  } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {
    +    this.ignoreCase_ = !!opt_ignoreCase;
    +
    +    // Set the parts -- decoding as we do so.
    +    // COMPATABILITY NOTE - In IE, unmatched fields may be empty strings,
    +    // whereas in other browsers they will be undefined.
    +    this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);
    +    this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);
    +    this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);
    +    this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);
    +    this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);
    +    this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);
    +    this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);
    +
    +  } else {
    +    this.ignoreCase_ = !!opt_ignoreCase;
    +    this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_);
    +  }
    +};
    +
    +
    +/**
    + * If true, we preserve the type of query parameters set programmatically.
    + *
    + * This means that if you set a parameter to a boolean, and then call
    + * getParameterValue, you will get a boolean back.
    + *
    + * If false, we will coerce parameters to strings, just as they would
    + * appear in real URIs.
    + *
    + * TODO(nicksantos): Remove this once people have time to fix all tests.
    + *
    + * @type {boolean}
    + */
    +goog.Uri.preserveParameterTypesCompatibilityFlag = false;
    +
    +
    +/**
    + * Parameter name added to stop caching.
    + * @type {string}
    + */
    +goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
    +
    +
    +/**
    + * Scheme such as "http".
    + * @type {string}
    + * @private
    + */
    +goog.Uri.prototype.scheme_ = '';
    +
    +
    +/**
    + * User credentials in the form "username:password".
    + * @type {string}
    + * @private
    + */
    +goog.Uri.prototype.userInfo_ = '';
    +
    +
    +/**
    + * Domain part, e.g. "www.google.com".
    + * @type {string}
    + * @private
    + */
    +goog.Uri.prototype.domain_ = '';
    +
    +
    +/**
    + * Port, e.g. 8080.
    + * @type {?number}
    + * @private
    + */
    +goog.Uri.prototype.port_ = null;
    +
    +
    +/**
    + * Path, e.g. "/tests/img.png".
    + * @type {string}
    + * @private
    + */
    +goog.Uri.prototype.path_ = '';
    +
    +
    +/**
    + * Object representing query data.
    + * @type {!goog.Uri.QueryData}
    + * @private
    + */
    +goog.Uri.prototype.queryData_;
    +
    +
    +/**
    + * The fragment without the #.
    + * @type {string}
    + * @private
    + */
    +goog.Uri.prototype.fragment_ = '';
    +
    +
    +/**
    + * Whether or not this Uri should be treated as Read Only.
    + * @type {boolean}
    + * @private
    + */
    +goog.Uri.prototype.isReadOnly_ = false;
    +
    +
    +/**
    + * Whether or not to ignore case when comparing query params.
    + * @type {boolean}
    + * @private
    + */
    +goog.Uri.prototype.ignoreCase_ = false;
    +
    +
    +/**
    + * @return {string} The string form of the url.
    + * @override
    + */
    +goog.Uri.prototype.toString = function() {
    +  var out = [];
    +
    +  var scheme = this.getScheme();
    +  if (scheme) {
    +    out.push(goog.Uri.encodeSpecialChars_(
    +        scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), ':');
    +  }
    +
    +  var domain = this.getDomain();
    +  if (domain) {
    +    out.push('//');
    +
    +    var userInfo = this.getUserInfo();
    +    if (userInfo) {
    +      out.push(goog.Uri.encodeSpecialChars_(
    +          userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), '@');
    +    }
    +
    +    out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));
    +
    +    var port = this.getPort();
    +    if (port != null) {
    +      out.push(':', String(port));
    +    }
    +  }
    +
    +  var path = this.getPath();
    +  if (path) {
    +    if (this.hasDomain() && path.charAt(0) != '/') {
    +      out.push('/');
    +    }
    +    out.push(goog.Uri.encodeSpecialChars_(
    +        path,
    +        path.charAt(0) == '/' ?
    +            goog.Uri.reDisallowedInAbsolutePath_ :
    +            goog.Uri.reDisallowedInRelativePath_,
    +        true));
    +  }
    +
    +  var query = this.getEncodedQuery();
    +  if (query) {
    +    out.push('?', query);
    +  }
    +
    +  var fragment = this.getFragment();
    +  if (fragment) {
    +    out.push('#', goog.Uri.encodeSpecialChars_(
    +        fragment, goog.Uri.reDisallowedInFragment_));
    +  }
    +  return out.join('');
    +};
    +
    +
    +/**
    + * Resolves the given relative URI (a goog.Uri object), using the URI
    + * represented by this instance as the base URI.
    + *
    + * There are several kinds of relative URIs:<br>
    + * 1. foo - replaces the last part of the path, the whole query and fragment<br>
    + * 2. /foo - replaces the the path, the query and fragment<br>
    + * 3. //foo - replaces everything from the domain on.  foo is a domain name<br>
    + * 4. ?foo - replace the query and fragment<br>
    + * 5. #foo - replace the fragment only
    + *
    + * Additionally, if relative URI has a non-empty path, all ".." and "."
    + * segments will be resolved, as described in RFC 3986.
    + *
    + * @param {!goog.Uri} relativeUri The relative URI to resolve.
    + * @return {!goog.Uri} The resolved URI.
    + */
    +goog.Uri.prototype.resolve = function(relativeUri) {
    +
    +  var absoluteUri = this.clone();
    +
    +  // we satisfy these conditions by looking for the first part of relativeUri
    +  // that is not blank and applying defaults to the rest
    +
    +  var overridden = relativeUri.hasScheme();
    +
    +  if (overridden) {
    +    absoluteUri.setScheme(relativeUri.getScheme());
    +  } else {
    +    overridden = relativeUri.hasUserInfo();
    +  }
    +
    +  if (overridden) {
    +    absoluteUri.setUserInfo(relativeUri.getUserInfo());
    +  } else {
    +    overridden = relativeUri.hasDomain();
    +  }
    +
    +  if (overridden) {
    +    absoluteUri.setDomain(relativeUri.getDomain());
    +  } else {
    +    overridden = relativeUri.hasPort();
    +  }
    +
    +  var path = relativeUri.getPath();
    +  if (overridden) {
    +    absoluteUri.setPort(relativeUri.getPort());
    +  } else {
    +    overridden = relativeUri.hasPath();
    +    if (overridden) {
    +      // resolve path properly
    +      if (path.charAt(0) != '/') {
    +        // path is relative
    +        if (this.hasDomain() && !this.hasPath()) {
    +          // RFC 3986, section 5.2.3, case 1
    +          path = '/' + path;
    +        } else {
    +          // RFC 3986, section 5.2.3, case 2
    +          var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');
    +          if (lastSlashIndex != -1) {
    +            path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;
    +          }
    +        }
    +      }
    +      path = goog.Uri.removeDotSegments(path);
    +    }
    +  }
    +
    +  if (overridden) {
    +    absoluteUri.setPath(path);
    +  } else {
    +    overridden = relativeUri.hasQuery();
    +  }
    +
    +  if (overridden) {
    +    absoluteUri.setQueryData(relativeUri.getDecodedQuery());
    +  } else {
    +    overridden = relativeUri.hasFragment();
    +  }
    +
    +  if (overridden) {
    +    absoluteUri.setFragment(relativeUri.getFragment());
    +  }
    +
    +  return absoluteUri;
    +};
    +
    +
    +/**
    + * Clones the URI instance.
    + * @return {!goog.Uri} New instance of the URI object.
    + */
    +goog.Uri.prototype.clone = function() {
    +  return new goog.Uri(this);
    +};
    +
    +
    +/**
    + * @return {string} The encoded scheme/protocol for the URI.
    + */
    +goog.Uri.prototype.getScheme = function() {
    +  return this.scheme_;
    +};
    +
    +
    +/**
    + * Sets the scheme/protocol.
    + * @param {string} newScheme New scheme value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setScheme = function(newScheme, opt_decode) {
    +  this.enforceReadOnly();
    +  this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) :
    +      newScheme;
    +
    +  // remove an : at the end of the scheme so somebody can pass in
    +  // window.location.protocol
    +  if (this.scheme_) {
    +    this.scheme_ = this.scheme_.replace(/:$/, '');
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the scheme has been set.
    + */
    +goog.Uri.prototype.hasScheme = function() {
    +  return !!this.scheme_;
    +};
    +
    +
    +/**
    + * @return {string} The decoded user info.
    + */
    +goog.Uri.prototype.getUserInfo = function() {
    +  return this.userInfo_;
    +};
    +
    +
    +/**
    + * Sets the userInfo.
    + * @param {string} newUserInfo New userInfo value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {
    +  this.enforceReadOnly();
    +  this.userInfo_ = opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) :
    +                   newUserInfo;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the user info has been set.
    + */
    +goog.Uri.prototype.hasUserInfo = function() {
    +  return !!this.userInfo_;
    +};
    +
    +
    +/**
    + * @return {string} The decoded domain.
    + */
    +goog.Uri.prototype.getDomain = function() {
    +  return this.domain_;
    +};
    +
    +
    +/**
    + * Sets the domain.
    + * @param {string} newDomain New domain value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setDomain = function(newDomain, opt_decode) {
    +  this.enforceReadOnly();
    +  this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) :
    +      newDomain;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the domain has been set.
    + */
    +goog.Uri.prototype.hasDomain = function() {
    +  return !!this.domain_;
    +};
    +
    +
    +/**
    + * @return {?number} The port number.
    + */
    +goog.Uri.prototype.getPort = function() {
    +  return this.port_;
    +};
    +
    +
    +/**
    + * Sets the port number.
    + * @param {*} newPort Port number. Will be explicitly casted to a number.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setPort = function(newPort) {
    +  this.enforceReadOnly();
    +
    +  if (newPort) {
    +    newPort = Number(newPort);
    +    if (isNaN(newPort) || newPort < 0) {
    +      throw Error('Bad port number ' + newPort);
    +    }
    +    this.port_ = newPort;
    +  } else {
    +    this.port_ = null;
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the port has been set.
    + */
    +goog.Uri.prototype.hasPort = function() {
    +  return this.port_ != null;
    +};
    +
    +
    +/**
    +  * @return {string} The decoded path.
    + */
    +goog.Uri.prototype.getPath = function() {
    +  return this.path_;
    +};
    +
    +
    +/**
    + * Sets the path.
    + * @param {string} newPath New path value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setPath = function(newPath, opt_decode) {
    +  this.enforceReadOnly();
    +  this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the path has been set.
    + */
    +goog.Uri.prototype.hasPath = function() {
    +  return !!this.path_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the query string has been set.
    + */
    +goog.Uri.prototype.hasQuery = function() {
    +  return this.queryData_.toString() !== '';
    +};
    +
    +
    +/**
    + * Sets the query data.
    + * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + *     Applies only if queryData is a string.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setQueryData = function(queryData, opt_decode) {
    +  this.enforceReadOnly();
    +
    +  if (queryData instanceof goog.Uri.QueryData) {
    +    this.queryData_ = queryData;
    +    this.queryData_.setIgnoreCase(this.ignoreCase_);
    +  } else {
    +    if (!opt_decode) {
    +      // QueryData accepts encoded query string, so encode it if
    +      // opt_decode flag is not true.
    +      queryData = goog.Uri.encodeSpecialChars_(queryData,
    +                                               goog.Uri.reDisallowedInQuery_);
    +    }
    +    this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_);
    +  }
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Sets the URI query.
    + * @param {string} newQuery New query value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setQuery = function(newQuery, opt_decode) {
    +  return this.setQueryData(newQuery, opt_decode);
    +};
    +
    +
    +/**
    + * @return {string} The encoded URI query, not including the ?.
    + */
    +goog.Uri.prototype.getEncodedQuery = function() {
    +  return this.queryData_.toString();
    +};
    +
    +
    +/**
    + * @return {string} The decoded URI query, not including the ?.
    + */
    +goog.Uri.prototype.getDecodedQuery = function() {
    +  return this.queryData_.toDecodedString();
    +};
    +
    +
    +/**
    + * Returns the query data.
    + * @return {!goog.Uri.QueryData} QueryData object.
    + */
    +goog.Uri.prototype.getQueryData = function() {
    +  return this.queryData_;
    +};
    +
    +
    +/**
    + * @return {string} The encoded URI query, not including the ?.
    + *
    + * Warning: This method, unlike other getter methods, returns encoded
    + * value, instead of decoded one.
    + */
    +goog.Uri.prototype.getQuery = function() {
    +  return this.getEncodedQuery();
    +};
    +
    +
    +/**
    + * Sets the value of the named query parameters, clearing previous values for
    + * that key.
    + *
    + * @param {string} key The parameter to set.
    + * @param {*} value The new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setParameterValue = function(key, value) {
    +  this.enforceReadOnly();
    +  this.queryData_.set(key, value);
    +  return this;
    +};
    +
    +
    +/**
    + * Sets the values of the named query parameters, clearing previous values for
    + * that key.  Not new values will currently be moved to the end of the query
    + * string.
    + *
    + * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
    + * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>
    + *
    + * @param {string} key The parameter to set.
    + * @param {*} values The new values. If values is a single
    + *     string then it will be treated as the sole value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setParameterValues = function(key, values) {
    +  this.enforceReadOnly();
    +
    +  if (!goog.isArray(values)) {
    +    values = [String(values)];
    +  }
    +
    +  this.queryData_.setValues(key, values);
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the value<b>s</b> for a given cgi parameter as a list of decoded
    + * query parameter values.
    + * @param {string} name The parameter to get values for.
    + * @return {!Array<?>} The values for a given cgi parameter as a list of
    + *     decoded query parameter values.
    + */
    +goog.Uri.prototype.getParameterValues = function(name) {
    +  return this.queryData_.getValues(name);
    +};
    +
    +
    +/**
    + * Returns the first value for a given cgi parameter or undefined if the given
    + * parameter name does not appear in the query string.
    + * @param {string} paramName Unescaped parameter name.
    + * @return {string|undefined} The first value for a given cgi parameter or
    + *     undefined if the given parameter name does not appear in the query
    + *     string.
    + */
    +goog.Uri.prototype.getParameterValue = function(paramName) {
    +  // NOTE(nicksantos): This type-cast is a lie when
    +  // preserveParameterTypesCompatibilityFlag is set to true.
    +  // But this should only be set to true in tests.
    +  return /** @type {string|undefined} */ (this.queryData_.get(paramName));
    +};
    +
    +
    +/**
    + * @return {string} The URI fragment, not including the #.
    + */
    +goog.Uri.prototype.getFragment = function() {
    +  return this.fragment_;
    +};
    +
    +
    +/**
    + * Sets the URI fragment.
    + * @param {string} newFragment New fragment value.
    + * @param {boolean=} opt_decode Optional param for whether to decode new value.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.setFragment = function(newFragment, opt_decode) {
    +  this.enforceReadOnly();
    +  this.fragment_ = opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) :
    +                   newFragment;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the URI has a fragment set.
    + */
    +goog.Uri.prototype.hasFragment = function() {
    +  return !!this.fragment_;
    +};
    +
    +
    +/**
    + * Returns true if this has the same domain as that of uri2.
    + * @param {!goog.Uri} uri2 The URI object to compare to.
    + * @return {boolean} true if same domain; false otherwise.
    + */
    +goog.Uri.prototype.hasSameDomainAs = function(uri2) {
    +  return ((!this.hasDomain() && !uri2.hasDomain()) ||
    +          this.getDomain() == uri2.getDomain()) &&
    +      ((!this.hasPort() && !uri2.hasPort()) ||
    +          this.getPort() == uri2.getPort());
    +};
    +
    +
    +/**
    + * Adds a random parameter to the Uri.
    + * @return {!goog.Uri} Reference to this Uri object.
    + */
    +goog.Uri.prototype.makeUnique = function() {
    +  this.enforceReadOnly();
    +  this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());
    +
    +  return this;
    +};
    +
    +
    +/**
    + * Removes the named query parameter.
    + *
    + * @param {string} key The parameter to remove.
    + * @return {!goog.Uri} Reference to this URI object.
    + */
    +goog.Uri.prototype.removeParameter = function(key) {
    +  this.enforceReadOnly();
    +  this.queryData_.remove(key);
    +  return this;
    +};
    +
    +
    +/**
    + * Sets whether Uri is read only. If this goog.Uri is read-only,
    + * enforceReadOnly_ will be called at the start of any function that may modify
    + * this Uri.
    + * @param {boolean} isReadOnly whether this goog.Uri should be read only.
    + * @return {!goog.Uri} Reference to this Uri object.
    + */
    +goog.Uri.prototype.setReadOnly = function(isReadOnly) {
    +  this.isReadOnly_ = isReadOnly;
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the URI is read only.
    + */
    +goog.Uri.prototype.isReadOnly = function() {
    +  return this.isReadOnly_;
    +};
    +
    +
    +/**
    + * Checks if this Uri has been marked as read only, and if so, throws an error.
    + * This should be called whenever any modifying function is called.
    + */
    +goog.Uri.prototype.enforceReadOnly = function() {
    +  if (this.isReadOnly_) {
    +    throw Error('Tried to modify a read-only Uri');
    +  }
    +};
    +
    +
    +/**
    + * Sets whether to ignore case.
    + * NOTE: If there are already key/value pairs in the QueryData, and
    + * ignoreCase_ is set to false, the keys will all be lower-cased.
    + * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    + * @return {!goog.Uri} Reference to this Uri object.
    + */
    +goog.Uri.prototype.setIgnoreCase = function(ignoreCase) {
    +  this.ignoreCase_ = ignoreCase;
    +  if (this.queryData_) {
    +    this.queryData_.setIgnoreCase(ignoreCase);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether to ignore case.
    + */
    +goog.Uri.prototype.getIgnoreCase = function() {
    +  return this.ignoreCase_;
    +};
    +
    +
    +//==============================================================================
    +// Static members
    +//==============================================================================
    +
    +
    +/**
    + * Creates a uri from the string form.  Basically an alias of new goog.Uri().
    + * If a Uri object is passed to parse then it will return a clone of the object.
    + *
    + * @param {*} uri Raw URI string or instance of Uri
    + *     object.
    + * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter
    + * names in #getParameterValue.
    + * @return {!goog.Uri} The new URI object.
    + */
    +goog.Uri.parse = function(uri, opt_ignoreCase) {
    +  return uri instanceof goog.Uri ?
    +         uri.clone() : new goog.Uri(uri, opt_ignoreCase);
    +};
    +
    +
    +/**
    + * Creates a new goog.Uri object from unencoded parts.
    + *
    + * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.
    + * @param {?string=} opt_userInfo username:password.
    + * @param {?string=} opt_domain www.google.com.
    + * @param {?number=} opt_port 9830.
    + * @param {?string=} opt_path /some/path/to/a/file.html.
    + * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.
    + * @param {?string=} opt_fragment The fragment without the #.
    + * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in
    + *     #getParameterValue.
    + *
    + * @return {!goog.Uri} The new URI object.
    + */
    +goog.Uri.create = function(opt_scheme, opt_userInfo, opt_domain, opt_port,
    +                           opt_path, opt_query, opt_fragment, opt_ignoreCase) {
    +
    +  var uri = new goog.Uri(null, opt_ignoreCase);
    +
    +  // Only set the parts if they are defined and not empty strings.
    +  opt_scheme && uri.setScheme(opt_scheme);
    +  opt_userInfo && uri.setUserInfo(opt_userInfo);
    +  opt_domain && uri.setDomain(opt_domain);
    +  opt_port && uri.setPort(opt_port);
    +  opt_path && uri.setPath(opt_path);
    +  opt_query && uri.setQueryData(opt_query);
    +  opt_fragment && uri.setFragment(opt_fragment);
    +
    +  return uri;
    +};
    +
    +
    +/**
    + * Resolves a relative Uri against a base Uri, accepting both strings and
    + * Uri objects.
    + *
    + * @param {*} base Base Uri.
    + * @param {*} rel Relative Uri.
    + * @return {!goog.Uri} Resolved uri.
    + */
    +goog.Uri.resolve = function(base, rel) {
    +  if (!(base instanceof goog.Uri)) {
    +    base = goog.Uri.parse(base);
    +  }
    +
    +  if (!(rel instanceof goog.Uri)) {
    +    rel = goog.Uri.parse(rel);
    +  }
    +
    +  return base.resolve(rel);
    +};
    +
    +
    +/**
    + * Removes dot segments in given path component, as described in
    + * RFC 3986, section 5.2.4.
    + *
    + * @param {string} path A non-empty path component.
    + * @return {string} Path component with removed dot segments.
    + */
    +goog.Uri.removeDotSegments = function(path) {
    +  if (path == '..' || path == '.') {
    +    return '';
    +
    +  } else if (!goog.string.contains(path, './') &&
    +             !goog.string.contains(path, '/.')) {
    +    // This optimization detects uris which do not contain dot-segments,
    +    // and as a consequence do not require any processing.
    +    return path;
    +
    +  } else {
    +    var leadingSlash = goog.string.startsWith(path, '/');
    +    var segments = path.split('/');
    +    var out = [];
    +
    +    for (var pos = 0; pos < segments.length; ) {
    +      var segment = segments[pos++];
    +
    +      if (segment == '.') {
    +        if (leadingSlash && pos == segments.length) {
    +          out.push('');
    +        }
    +      } else if (segment == '..') {
    +        if (out.length > 1 || out.length == 1 && out[0] != '') {
    +          out.pop();
    +        }
    +        if (leadingSlash && pos == segments.length) {
    +          out.push('');
    +        }
    +      } else {
    +        out.push(segment);
    +        leadingSlash = true;
    +      }
    +    }
    +
    +    return out.join('/');
    +  }
    +};
    +
    +
    +/**
    + * Decodes a value or returns the empty string if it isn't defined or empty.
    + * @param {string|undefined} val Value to decode.
    + * @param {boolean=} opt_preserveReserved If true, restricted characters will
    + *     not be decoded.
    + * @return {string} Decoded value.
    + * @private
    + */
    +goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {
    +  // Don't use UrlDecode() here because val is not a query parameter.
    +  if (!val) {
    +    return '';
    +  }
    +
    +  return opt_preserveReserved ? decodeURI(val) : decodeURIComponent(val);
    +};
    +
    +
    +/**
    + * If unescapedPart is non null, then escapes any characters in it that aren't
    + * valid characters in a url and also escapes any special characters that
    + * appear in extra.
    + *
    + * @param {*} unescapedPart The string to encode.
    + * @param {RegExp} extra A character set of characters in [\01-\177].
    + * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent
    + *     encoding.
    + * @return {?string} null iff unescapedPart == null.
    + * @private
    + */
    +goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra,
    +    opt_removeDoubleEncoding) {
    +  if (goog.isString(unescapedPart)) {
    +    var encoded = encodeURI(unescapedPart).
    +        replace(extra, goog.Uri.encodeChar_);
    +    if (opt_removeDoubleEncoding) {
    +      // encodeURI double-escapes %XX sequences used to represent restricted
    +      // characters in some URI components, remove the double escaping here.
    +      encoded = goog.Uri.removeDoubleEncoding_(encoded);
    +    }
    +    return encoded;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Converts a character in [\01-\177] to its unicode character equivalent.
    + * @param {string} ch One character string.
    + * @return {string} Encoded string.
    + * @private
    + */
    +goog.Uri.encodeChar_ = function(ch) {
    +  var n = ch.charCodeAt(0);
    +  return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);
    +};
    +
    +
    +/**
    + * Removes double percent-encoding from a string.
    + * @param  {string} doubleEncodedString String
    + * @return {string} String with double encoding removed.
    + * @private
    + */
    +goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {
    +  return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');
    +};
    +
    +
    +/**
    + * Regular expression for characters that are disallowed in the scheme or
    + * userInfo part of the URI.
    + * @type {RegExp}
    + * @private
    + */
    +goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
    +
    +
    +/**
    + * Regular expression for characters that are disallowed in a relative path.
    + * Colon is included due to RFC 3986 3.3.
    + * @type {RegExp}
    + * @private
    + */
    +goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
    +
    +
    +/**
    + * Regular expression for characters that are disallowed in an absolute path.
    + * @type {RegExp}
    + * @private
    + */
    +goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
    +
    +
    +/**
    + * Regular expression for characters that are disallowed in the query.
    + * @type {RegExp}
    + * @private
    + */
    +goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
    +
    +
    +/**
    + * Regular expression for characters that are disallowed in the fragment.
    + * @type {RegExp}
    + * @private
    + */
    +goog.Uri.reDisallowedInFragment_ = /#/g;
    +
    +
    +/**
    + * Checks whether two URIs have the same domain.
    + * @param {string} uri1String First URI string.
    + * @param {string} uri2String Second URI string.
    + * @return {boolean} true if the two URIs have the same domain; false otherwise.
    + */
    +goog.Uri.haveSameDomain = function(uri1String, uri2String) {
    +  // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.
    +  // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.
    +  var pieces1 = goog.uri.utils.split(uri1String);
    +  var pieces2 = goog.uri.utils.split(uri2String);
    +  return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    +             pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    +         pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    +             pieces2[goog.uri.utils.ComponentIndex.PORT];
    +};
    +
    +
    +
    +/**
    + * Class used to represent URI query parameters.  It is essentially a hash of
    + * name-value pairs, though a name can be present more than once.
    + *
    + * Has the same interface as the collections in goog.structs.
    + *
    + * @param {?string=} opt_query Optional encoded query string to parse into
    + *     the object.
    + * @param {goog.Uri=} opt_uri Optional uri object that should have its
    + *     cache invalidated when this object updates. Deprecated -- this
    + *     is no longer required.
    + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    + *     name in #get.
    + * @constructor
    + * @struct
    + * @final
    + */
    +goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
    +  /**
    +   * Encoded query string, or null if it requires computing from the key map.
    +   * @type {?string}
    +   * @private
    +   */
    +  this.encodedQuery_ = opt_query || null;
    +
    +  /**
    +   * If true, ignore the case of the parameter name in #get.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.ignoreCase_ = !!opt_ignoreCase;
    +};
    +
    +
    +/**
    + * If the underlying key map is not yet initialized, it parses the
    + * query string and fills the map with parsed data.
    + * @private
    + */
    +goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
    +  if (!this.keyMap_) {
    +    this.keyMap_ = new goog.structs.Map();
    +    this.count_ = 0;
    +    if (this.encodedQuery_) {
    +      var self = this;
    +      goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {
    +        self.add(goog.string.urlDecode(name), value);
    +      });
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Creates a new query data instance from a map of names and values.
    + *
    + * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter
    + *     names to parameter value. If parameter value is an array, it is
    + *     treated as if the key maps to each individual value in the
    + *     array.
    + * @param {goog.Uri=} opt_uri URI object that should have its cache
    + *     invalidated when this object updates.
    + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    + *     name in #get.
    + * @return {!goog.Uri.QueryData} The populated query data instance.
    + */
    +goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
    +  var keys = goog.structs.getKeys(map);
    +  if (typeof keys == 'undefined') {
    +    throw Error('Keys are undefined');
    +  }
    +
    +  var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    +  var values = goog.structs.getValues(map);
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = keys[i];
    +    var value = values[i];
    +    if (!goog.isArray(value)) {
    +      queryData.add(key, value);
    +    } else {
    +      queryData.setValues(key, value);
    +    }
    +  }
    +  return queryData;
    +};
    +
    +
    +/**
    + * Creates a new query data instance from parallel arrays of parameter names
    + * and values. Allows for duplicate parameter names. Throws an error if the
    + * lengths of the arrays differ.
    + *
    + * @param {!Array<string>} keys Parameter names.
    + * @param {!Array<?>} values Parameter values.
    + * @param {goog.Uri=} opt_uri URI object that should have its cache
    + *     invalidated when this object updates.
    + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
    + *     name in #get.
    + * @return {!goog.Uri.QueryData} The populated query data instance.
    + */
    +goog.Uri.QueryData.createFromKeysValues = function(
    +    keys, values, opt_uri, opt_ignoreCase) {
    +  if (keys.length != values.length) {
    +    throw Error('Mismatched lengths for keys/values');
    +  }
    +  var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
    +  for (var i = 0; i < keys.length; i++) {
    +    queryData.add(keys[i], values[i]);
    +  }
    +  return queryData;
    +};
    +
    +
    +/**
    + * The map containing name/value or name/array-of-values pairs.
    + * May be null if it requires parsing from the query string.
    + *
    + * We need to use a Map because we cannot guarantee that the key names will
    + * not be problematic for IE.
    + *
    + * @private {goog.structs.Map<string, !Array<*>>}
    + */
    +goog.Uri.QueryData.prototype.keyMap_ = null;
    +
    +
    +/**
    + * The number of params, or null if it requires computing.
    + * @type {?number}
    + * @private
    + */
    +goog.Uri.QueryData.prototype.count_ = null;
    +
    +
    +/**
    + * @return {?number} The number of parameters.
    + */
    +goog.Uri.QueryData.prototype.getCount = function() {
    +  this.ensureKeyMapInitialized_();
    +  return this.count_;
    +};
    +
    +
    +/**
    + * Adds a key value pair.
    + * @param {string} key Name.
    + * @param {*} value Value.
    + * @return {!goog.Uri.QueryData} Instance of this object.
    + */
    +goog.Uri.QueryData.prototype.add = function(key, value) {
    +  this.ensureKeyMapInitialized_();
    +  this.invalidateCache_();
    +
    +  key = this.getKeyName_(key);
    +  var values = this.keyMap_.get(key);
    +  if (!values) {
    +    this.keyMap_.set(key, (values = []));
    +  }
    +  values.push(value);
    +  this.count_++;
    +  return this;
    +};
    +
    +
    +/**
    + * Removes all the params with the given key.
    + * @param {string} key Name.
    + * @return {boolean} Whether any parameter was removed.
    + */
    +goog.Uri.QueryData.prototype.remove = function(key) {
    +  this.ensureKeyMapInitialized_();
    +
    +  key = this.getKeyName_(key);
    +  if (this.keyMap_.containsKey(key)) {
    +    this.invalidateCache_();
    +
    +    // Decrement parameter count.
    +    this.count_ -= this.keyMap_.get(key).length;
    +    return this.keyMap_.remove(key);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Clears the parameters.
    + */
    +goog.Uri.QueryData.prototype.clear = function() {
    +  this.invalidateCache_();
    +  this.keyMap_ = null;
    +  this.count_ = 0;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we have any parameters.
    + */
    +goog.Uri.QueryData.prototype.isEmpty = function() {
    +  this.ensureKeyMapInitialized_();
    +  return this.count_ == 0;
    +};
    +
    +
    +/**
    + * Whether there is a parameter with the given name
    + * @param {string} key The parameter name to check for.
    + * @return {boolean} Whether there is a parameter with the given name.
    + */
    +goog.Uri.QueryData.prototype.containsKey = function(key) {
    +  this.ensureKeyMapInitialized_();
    +  key = this.getKeyName_(key);
    +  return this.keyMap_.containsKey(key);
    +};
    +
    +
    +/**
    + * Whether there is a parameter with the given value.
    + * @param {*} value The value to check for.
    + * @return {boolean} Whether there is a parameter with the given value.
    + */
    +goog.Uri.QueryData.prototype.containsValue = function(value) {
    +  // NOTE(arv): This solution goes through all the params even if it was the
    +  // first param. We can get around this by not reusing code or by switching to
    +  // iterators.
    +  var vals = this.getValues();
    +  return goog.array.contains(vals, value);
    +};
    +
    +
    +/**
    + * Returns all the keys of the parameters. If a key is used multiple times
    + * it will be included multiple times in the returned array
    + * @return {!Array<string>} All the keys of the parameters.
    + */
    +goog.Uri.QueryData.prototype.getKeys = function() {
    +  this.ensureKeyMapInitialized_();
    +  // We need to get the values to know how many keys to add.
    +  var vals = /** @type {!Array<*>} */ (this.keyMap_.getValues());
    +  var keys = this.keyMap_.getKeys();
    +  var rv = [];
    +  for (var i = 0; i < keys.length; i++) {
    +    var val = vals[i];
    +    for (var j = 0; j < val.length; j++) {
    +      rv.push(keys[i]);
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Returns all the values of the parameters with the given name. If the query
    + * data has no such key this will return an empty array. If no key is given
    + * all values wil be returned.
    + * @param {string=} opt_key The name of the parameter to get the values for.
    + * @return {!Array<?>} All the values of the parameters with the given name.
    + */
    +goog.Uri.QueryData.prototype.getValues = function(opt_key) {
    +  this.ensureKeyMapInitialized_();
    +  var rv = [];
    +  if (goog.isString(opt_key)) {
    +    if (this.containsKey(opt_key)) {
    +      rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
    +    }
    +  } else {
    +    // Return all values.
    +    var values = this.keyMap_.getValues();
    +    for (var i = 0; i < values.length; i++) {
    +      rv = goog.array.concat(rv, values[i]);
    +    }
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Sets a key value pair and removes all other keys with the same value.
    + *
    + * @param {string} key Name.
    + * @param {*} value Value.
    + * @return {!goog.Uri.QueryData} Instance of this object.
    + */
    +goog.Uri.QueryData.prototype.set = function(key, value) {
    +  this.ensureKeyMapInitialized_();
    +  this.invalidateCache_();
    +
    +  // TODO(chrishenry): This could be better written as
    +  // this.remove(key), this.add(key, value), but that would reorder
    +  // the key (since the key is first removed and then added at the
    +  // end) and we would have to fix unit tests that depend on key
    +  // ordering.
    +  key = this.getKeyName_(key);
    +  if (this.containsKey(key)) {
    +    this.count_ -= this.keyMap_.get(key).length;
    +  }
    +  this.keyMap_.set(key, [value]);
    +  this.count_++;
    +  return this;
    +};
    +
    +
    +/**
    + * Returns the first value associated with the key. If the query data has no
    + * such key this will return undefined or the optional default.
    + * @param {string} key The name of the parameter to get the value for.
    + * @param {*=} opt_default The default value to return if the query data
    + *     has no such key.
    + * @return {*} The first string value associated with the key, or opt_default
    + *     if there's no value.
    + */
    +goog.Uri.QueryData.prototype.get = function(key, opt_default) {
    +  var values = key ? this.getValues(key) : [];
    +  if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
    +    return values.length > 0 ? values[0] : opt_default;
    +  } else {
    +    return values.length > 0 ? String(values[0]) : opt_default;
    +  }
    +};
    +
    +
    +/**
    + * Sets the values for a key. If the key already exists, this will
    + * override all of the existing values that correspond to the key.
    + * @param {string} key The key to set values for.
    + * @param {!Array<?>} values The values to set.
    + */
    +goog.Uri.QueryData.prototype.setValues = function(key, values) {
    +  this.remove(key);
    +
    +  if (values.length > 0) {
    +    this.invalidateCache_();
    +    this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
    +    this.count_ += values.length;
    +  }
    +};
    +
    +
    +/**
    + * @return {string} Encoded query string.
    + * @override
    + */
    +goog.Uri.QueryData.prototype.toString = function() {
    +  if (this.encodedQuery_) {
    +    return this.encodedQuery_;
    +  }
    +
    +  if (!this.keyMap_) {
    +    return '';
    +  }
    +
    +  var sb = [];
    +
    +  // In the past, we use this.getKeys() and this.getVals(), but that
    +  // generates a lot of allocations as compared to simply iterating
    +  // over the keys.
    +  var keys = this.keyMap_.getKeys();
    +  for (var i = 0; i < keys.length; i++) {
    +    var key = keys[i];
    +    var encodedKey = goog.string.urlEncode(key);
    +    var val = this.getValues(key);
    +    for (var j = 0; j < val.length; j++) {
    +      var param = encodedKey;
    +      // Ensure that null and undefined are encoded into the url as
    +      // literal strings.
    +      if (val[j] !== '') {
    +        param += '=' + goog.string.urlEncode(val[j]);
    +      }
    +      sb.push(param);
    +    }
    +  }
    +
    +  return this.encodedQuery_ = sb.join('&');
    +};
    +
    +
    +/**
    + * @return {string} Decoded query string.
    + */
    +goog.Uri.QueryData.prototype.toDecodedString = function() {
    +  return goog.Uri.decodeOrEmpty_(this.toString());
    +};
    +
    +
    +/**
    + * Invalidate the cache.
    + * @private
    + */
    +goog.Uri.QueryData.prototype.invalidateCache_ = function() {
    +  this.encodedQuery_ = null;
    +};
    +
    +
    +/**
    + * Removes all keys that are not in the provided list. (Modifies this object.)
    + * @param {Array<string>} keys The desired keys.
    + * @return {!goog.Uri.QueryData} a reference to this object.
    + */
    +goog.Uri.QueryData.prototype.filterKeys = function(keys) {
    +  this.ensureKeyMapInitialized_();
    +  this.keyMap_.forEach(
    +      function(value, key) {
    +        if (!goog.array.contains(keys, key)) {
    +          this.remove(key);
    +        }
    +      }, this);
    +  return this;
    +};
    +
    +
    +/**
    + * Clone the query data instance.
    + * @return {!goog.Uri.QueryData} New instance of the QueryData object.
    + */
    +goog.Uri.QueryData.prototype.clone = function() {
    +  var rv = new goog.Uri.QueryData();
    +  rv.encodedQuery_ = this.encodedQuery_;
    +  if (this.keyMap_) {
    +    rv.keyMap_ = this.keyMap_.clone();
    +    rv.count_ = this.count_;
    +  }
    +  return rv;
    +};
    +
    +
    +/**
    + * Helper function to get the key name from a JavaScript object. Converts
    + * the object to a string, and to lower case if necessary.
    + * @private
    + * @param {*} arg The object to get a key name from.
    + * @return {string} valid key name which can be looked up in #keyMap_.
    + */
    +goog.Uri.QueryData.prototype.getKeyName_ = function(arg) {
    +  var keyName = String(arg);
    +  if (this.ignoreCase_) {
    +    keyName = keyName.toLowerCase();
    +  }
    +  return keyName;
    +};
    +
    +
    +/**
    + * Ignore case in parameter names.
    + * NOTE: If there are already key/value pairs in the QueryData, and
    + * ignoreCase_ is set to false, the keys will all be lower-cased.
    + * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
    + */
    +goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
    +  var resetKeys = ignoreCase && !this.ignoreCase_;
    +  if (resetKeys) {
    +    this.ensureKeyMapInitialized_();
    +    this.invalidateCache_();
    +    this.keyMap_.forEach(
    +        function(value, key) {
    +          var lowerCase = key.toLowerCase();
    +          if (key != lowerCase) {
    +            this.remove(key);
    +            this.setValues(lowerCase, value);
    +          }
    +        }, this);
    +  }
    +  this.ignoreCase_ = ignoreCase;
    +};
    +
    +
    +/**
    + * Extends a query data object with another query data or map like object. This
    + * operates 'in-place', it does not create a new QueryData object.
    + *
    + * @param {...(goog.Uri.QueryData|goog.structs.Map<?, ?>|Object)} var_args
    + *     The object from which key value pairs will be copied.
    + */
    +goog.Uri.QueryData.prototype.extend = function(var_args) {
    +  for (var i = 0; i < arguments.length; i++) {
    +    var data = arguments[i];
    +    goog.structs.forEach(data,
    +        /** @this {goog.Uri.QueryData} */
    +        function(value, key) {
    +          this.add(key, value);
    +        }, this);
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/uri_test.html b/mapbox-gl/closure-library/closure/goog/uri/uri_test.html
    new file mode 100644
    index 0000000000..8366d291da
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/uri_test.html
    @@ -0,0 +1,21 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.Uri</title>
    +<script src="../base.js"></script>
    +<script>
    +goog.require('goog.UriTest');
    +</script>
    +</head>
    +<body>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/uri_test.js b/mapbox-gl/closure-library/closure/goog/uri/uri_test.js
    new file mode 100644
    index 0000000000..793c0e9b52
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/uri_test.js
    @@ -0,0 +1,1096 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Unit tests for goog.Uri.
    + *
    + */
    +
    +goog.provide('goog.UriTest');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.testing.jsunit');
    +
    +goog.setTestOnly('goog.UriTest');
    +
    +function testUriParse() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +  assertEquals('http', uri.getScheme());
    +  assertEquals('', uri.getUserInfo());
    +  assertEquals('www.google.com', uri.getDomain());
    +  assertEquals(80, uri.getPort());
    +  assertEquals('/path', uri.getPath());
    +  assertEquals('q=query', uri.getQuery());
    +  assertEquals('fragmento', uri.getFragment());
    +
    +  assertEquals('terer258+foo@gmail.com',
    +               goog.Uri.parse('mailto:terer258+foo@gmail.com').getPath());
    +}
    +
    +function testUriParseAcceptsThingsWithToString() {
    +  // Ensure that the goog.Uri constructor coerces random types to strings.
    +  var uriStr = 'http://www.google.com:80/path?q=query#fragmento';
    +  var uri = new goog.Uri({toString: function() { return uriStr; }});
    +  assertEquals('http://www.google.com:80/path?q=query#fragmento',
    +      uri.toString());
    +}
    +
    +function testUriCreate() {
    +  assertEquals(
    +      'http://www.google.com:81/search%20path?q=what%20to%20eat%2Bdrink%3F',
    +      goog.Uri.create('http', null, 'www.google.com', 81, '/search path',
    +          (new goog.Uri.QueryData).set('q', 'what to eat+drink?'), null)
    +      .toString());
    +
    +  assertEquals(
    +      'http://www.google.com:80/search%20path?q=what%20to%20eat%2Bdrink%3F',
    +      goog.Uri.create('http', null, 'www.google.com', 80, '/search path',
    +          (new goog.Uri.QueryData).set('q', 'what to eat+drink?'), null)
    +      .toString());
    +
    +  assertEquals(
    +      'http://www.google.com/search%20path?q=what%20to%20eat%2Bdrink%3F',
    +      goog.Uri.create('http', null, 'www.google.com', null, '/search path',
    +          (new goog.Uri.QueryData).set('q', 'what to eat+drink?'), null)
    +      .toString());
    +
    +  var createdUri = goog.Uri.create(
    +      'http', null, 'www.google.com', null, '/search path',
    +      new goog.Uri.QueryData(null, null, true).set('Q', 'what to eat+drink?'),
    +      null);
    +
    +  assertEquals('http://www.google.com/search%20path?q=what%20to%20eat%2Bdrink%3F',
    +               createdUri.toString());
    +}
    +
    +function testClone() {
    +  var uri1 = new goog.Uri(
    +      'http://user:pass@www.google.com:8080/foo?a=1&b=2#c=3');
    +  // getCount forces instantiation of internal data structures to more
    +  // thoroughly test clone.
    +  uri1.getQueryData().getCount();
    +  var uri2 = uri1.clone();
    +
    +  assertNotEquals(uri1, uri2);
    +  assertEquals(uri1.toString(), uri2.toString());
    +  assertEquals(2, uri2.getQueryData().getCount());
    +
    +  uri2.setParameterValues('q', 'bar');
    +  assertFalse(uri1.getParameterValue('q') == 'bar');
    +}
    +
    +function testRelativeUris() {
    +  assertFalse(new goog.Uri('?hello').hasPath());
    +}
    +
    +function testAbsolutePathResolution() {
    +  var uri1 = new goog.Uri('http://www.google.com:8080/path?q=query#fragmento');
    +
    +  assertEquals('http://www.google.com:8080/foo',
    +               uri1.resolve(new goog.Uri('/foo')).toString());
    +
    +  assertEquals('http://www.google.com:8080/foo/bar',
    +               goog.Uri.resolve('http://www.google.com:8080/search/',
    +                                '/foo/bar').toString());
    +}
    +
    +function testRelativePathResolution() {
    +  var uri1 = new goog.Uri('http://www.google.com:8080/path?q=query#fragmento');
    +  assertEquals('http://www.google.com:8080/foo',
    +               uri1.resolve(goog.Uri.parse('foo')).toString());
    +
    +  var uri2 = new goog.Uri('http://www.google.com:8080/search');
    +  assertEquals('http://www.google.com:8080/foo/bar',
    +               uri2.resolve(new goog.Uri('foo/bar')).toString());
    +
    +  var uri3 = new goog.Uri('http://www.google.com:8080/search/');
    +  assertEquals('http://www.google.com:8080/search/foo/bar',
    +               uri3.resolve(new goog.Uri('foo/bar')).toString());
    +
    +  var uri4 = new goog.Uri('foo');
    +  assertEquals('bar',
    +               uri4.resolve(new goog.Uri('bar')).toString());
    +
    +  var uri5 = new goog.Uri('http://www.google.com:8080/search/');
    +  assertEquals('http://www.google.com:8080/search/..%2ffoo/bar',
    +               uri3.resolve(new goog.Uri('..%2ffoo/bar')).toString());
    +
    +}
    +
    +function testDomainResolution() {
    +  assertEquals('https://www.google.com/foo/bar',
    +               new goog.Uri('https://www.fark.com:443/search/').resolve(
    +                   new goog.Uri('//www.google.com/foo/bar')
    +               ).toString());
    +
    +  assertEquals('http://www.google.com/',
    +               goog.Uri.resolve('http://www.fark.com/search/',
    +                                '//www.google.com/').toString());
    +}
    +
    +function testQueryResolution() {
    +  assertEquals('http://www.google.com/search?q=new%20search',
    +               goog.Uri.parse('http://www.google.com/search?q=old+search').
    +                   resolve(goog.Uri.parse('?q=new%20search')).toString());
    +
    +  assertEquals('http://www.google.com/search?q=new%20search',
    +               goog.Uri.parse('http://www.google.com/search?q=old+search#hi').
    +                   resolve(goog.Uri.parse('?q=new%20search')).toString());
    +}
    +
    +function testFragmentResolution() {
    +  assertEquals('http://www.google.com/foo/bar?q=hi#there',
    +               goog.Uri.resolve('http://www.google.com/foo/bar?q=hi',
    +                                '#there').toString());
    +
    +  assertEquals('http://www.google.com/foo/bar?q=hi#there',
    +               goog.Uri.resolve('http://www.google.com/foo/bar?q=hi#you',
    +                                '#there').toString());
    +}
    +
    +function testBogusResolution() {
    +  var uri = goog.Uri.parse('some:base/url').resolve(
    +      goog.Uri.parse('a://completely.different/url'));
    +  assertEquals('a://completely.different/url', uri.toString());
    +}
    +
    +function testDotSegmentsRemovalRemoveLeadingDots() {
    +  // Test removing leading "../" and "./"
    +  assertEquals('bar', goog.Uri.removeDotSegments('../bar'));
    +  assertEquals('bar', goog.Uri.removeDotSegments('./bar'));
    +  assertEquals('bar', goog.Uri.removeDotSegments('.././bar'));
    +  assertEquals('bar', goog.Uri.removeDotSegments('.././bar'));
    +}
    +
    +function testDotSegmentRemovalRemoveSingleDot() {
    +  // Tests replacing "/./" with "/"
    +  assertEquals('/foo/bar', goog.Uri.removeDotSegments('/foo/./bar'));
    +  assertEquals('/bar/', goog.Uri.removeDotSegments('/bar/./'));
    +
    +  // Test replacing trailing "/." with "/"
    +  assertEquals('/', goog.Uri.removeDotSegments('/.'));
    +  assertEquals('/bar/', goog.Uri.removeDotSegments('/bar/.'));
    +}
    +
    +function testDotSegmentRemovalRemoveDoubleDot() {
    +  // Test resolving "/../"
    +  assertEquals('/bar', goog.Uri.removeDotSegments('/foo/../bar'));
    +  assertEquals('/', goog.Uri.removeDotSegments('/bar/../'));
    +
    +  // Test resolving trailing "/.."
    +  assertEquals('/', goog.Uri.removeDotSegments('/..'));
    +  assertEquals('/', goog.Uri.removeDotSegments('/bar/..'));
    +  assertEquals('/foo/', goog.Uri.removeDotSegments('/foo/bar/..'));
    +}
    +
    +function testDotSegmentRemovalRemovePlainDots() {
    +  // RFC 3986, section 5.2.4, point 2.D.
    +  // Test resolving plain ".." or "."
    +  assertEquals('', goog.Uri.removeDotSegments('.'));
    +  assertEquals('', goog.Uri.removeDotSegments('..'));
    +}
    +
    +function testPathConcatenation() {
    +  // Check accordenance with RFC 3986, section 5.2.4
    +  assertResolvedEquals('bar', '', 'bar');
    +  assertResolvedEquals('/bar', '/', 'bar');
    +  assertResolvedEquals('/bar', '/foo', '/bar');
    +  assertResolvedEquals('/foo/foo', '/foo/bar', 'foo');
    +}
    +
    +function testPathConcatenationDontRemoveForEmptyUri() {
    +  // Resolving URIs with empty path should not result in dot segments removal.
    +  // See: algorithm in section 5.2.2: code inside 'if (R.path == "")' clause.
    +  assertResolvedEquals('/search/../foo', '/search/../foo', '');
    +  assertResolvedEquals('/search/./foo', '/search/./foo', '');
    +}
    +
    +
    +function testParameterGetters() {
    +  function assertArraysEqual(l1, l2) {
    +    if (!l1 || !l2) {
    +      assertEquals(l1, l2);
    +      return;
    +    }
    +    var l1s = l1.toString(), l2s = l2.toString();
    +    assertEquals(l1s, l2s);
    +    assertEquals(l1s, l1.length, l2.length);
    +    for (var i = 0; i < l1.length; ++i) {
    +      assertEquals('part ' + i + ' of ' + l1.length + ' in ' + l1s,
    +                   l1[i], l2[i]);
    +    }
    +  }
    +
    +  assertArraysEqual(['v1', 'v2'],
    +      goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3').
    +          getParameterValues('key'));
    +
    +  assertArraysEqual(['v1', 'v2'],
    +      goog.Uri.parse('/path?a=b&keY=v1&c=d&KEy=v2&keywithsuffix=v3', true).
    +          getParameterValues('kEy'));
    +
    +  assertEquals('v1',
    +      goog.Uri.parse('/path?key=v1&c=d&keywithsuffix=v3&key=v2').
    +          getParameterValue('key'));
    +
    +  assertEquals('v1',
    +      goog.Uri.parse('/path?kEY=v1&c=d&keywithsuffix=v3&key=v2', true).
    +          getParameterValue('Key'));
    +
    +  assertEquals('v1=v2',
    +      goog.Uri.parse('/path?key=v1=v2', true).getParameterValue('key'));
    +
    +  assertEquals('v1=v2=v3',
    +      goog.Uri.parse('/path?key=v1=v2=v3', true).getParameterValue('key'));
    +
    +  assertArraysEqual(undefined,
    +                    goog.Uri.parse('/path?key=v1&c=d&keywithsuffix=v3&key=v2').
    +                    getParameterValue('nosuchkey'));
    +
    +  // test boundary conditions
    +  assertArraysEqual(['v1', 'v2'],
    +      goog.Uri.parse('/path?key=v1&c=d&key=v2&keywithsuffix=v3').
    +          getParameterValues('key'));
    +  assertArraysEqual(['v1', 'v2'],
    +      goog.Uri.parse('/path?key=v1&c=d&keywithsuffix=v3&key=v2').
    +          getParameterValues('key'));
    +
    +  // test no =
    +  assertArraysEqual([''],
    +      goog.Uri.parse('/path?key').getParameterValues('key'));
    +  assertArraysEqual([''],
    +      goog.Uri.parse('/path?key', true).getParameterValues('key'));
    +
    +  assertArraysEqual([''],
    +                    goog.Uri.parse('/path?foo=bar&key').
    +                    getParameterValues('key'));
    +  assertArraysEqual([''],
    +                    goog.Uri.parse('/path?foo=bar&key', true).
    +                    getParameterValues('key'));
    +
    +  assertEquals('',
    +               goog.Uri.parse('/path?key').getParameterValue('key'));
    +  assertEquals('',
    +               goog.Uri.parse('/path?key', true).getParameterValue('key'));
    +
    +  assertEquals('',
    +               goog.Uri.parse('/path?foo=bar&key').getParameterValue('key'));
    +  assertEquals('',
    +               goog.Uri.parse('/path?foo=bar&key', true).
    +               getParameterValue('key'));
    +
    +  var u = new goog.Uri('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3');
    +  assertArraysEqual(u.getParameterValues('a'), ['b']);
    +  assertArraysEqual(u.getParameterValues('key'), ['v1', 'v2']);
    +  assertArraysEqual(u.getParameterValues('c'), ['d']);
    +  assertArraysEqual(u.getParameterValues('keywithsuffix'), ['v3']);
    +  assertArraysEqual(u.getParameterValues('KeyWITHSuffix'), []);
    +
    +  // Make sure constructing from another URI preserves case-sensitivity
    +  var u2 = new goog.Uri(u);
    +  assertArraysEqual(u2.getParameterValues('a'), ['b']);
    +  assertArraysEqual(u2.getParameterValues('key'), ['v1', 'v2']);
    +  assertArraysEqual(u2.getParameterValues('c'), ['d']);
    +  assertArraysEqual(u2.getParameterValues('keywithsuffix'), ['v3']);
    +  assertArraysEqual(u2.getParameterValues('KeyWITHSuffix'), []);
    +
    +  u = new goog.Uri('/path?a=b&key=v1&c=d&kEy=v2&keywithsuffix=v3', true);
    +  assertArraysEqual(u.getParameterValues('A'), ['b']);
    +  assertArraysEqual(u.getParameterValues('keY'), ['v1', 'v2']);
    +  assertArraysEqual(u.getParameterValues('c'), ['d']);
    +  assertArraysEqual(u.getParameterValues('keyWITHsuffix'), ['v3']);
    +
    +  // Make sure constructing from another URI preserves case-insensitivity
    +  u2 = new goog.Uri(u);
    +  assertArraysEqual(u2.getParameterValues('A'), ['b']);
    +  assertArraysEqual(u2.getParameterValues('keY'), ['v1', 'v2']);
    +  assertArraysEqual(u2.getParameterValues('c'), ['d']);
    +  assertArraysEqual(u2.getParameterValues('keyWITHsuffix'), ['v3']);
    +}
    +
    +function testRemoveParameter() {
    +  assertEquals('/path?a=b&c=d&keywithsuffix=v3',
    +               goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3')
    +               .removeParameter('key').toString());
    +}
    +
    +function testParameterSetters() {
    +  assertEquals('/path?a=b&key=newval&c=d&keywithsuffix=v3',
    +               goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3')
    +               .setParameterValue('key', 'newval').toString());
    +
    +  assertEquals('/path?a=b&key=1&key=2&key=3&c=d&keywithsuffix=v3',
    +               goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3')
    +               .setParameterValues('key', ['1', '2', '3']).toString());
    +
    +  assertEquals('/path',
    +      goog.Uri.parse('/path?key=v1&key=v2')
    +          .setParameterValues('key', []).toString());
    +
    +  // Test case-insensitive setters
    +  assertEquals('/path?a=b&key=newval&c=d&keywithsuffix=v3',
    +      goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3', true)
    +               .setParameterValue('KEY', 'newval').toString());
    +
    +  assertEquals(
    +      '/path?a=b&key=1&key=2&key=3&c=d&keywithsuffix=v3',
    +      goog.Uri.parse('/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3', true)
    +          .setParameterValues('kEY', ['1', '2', '3']).toString());
    +}
    +
    +function testEncoding() {
    +  assertEquals('/foo bar baz',
    +               goog.Uri.parse('/foo%20bar%20baz').getPath());
    +  assertEquals('/foo+bar+baz',
    +               goog.Uri.parse('/foo+bar+baz').getPath());
    +}
    +
    +function testSetScheme() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setScheme('https');
    +  assertTrue(uri.hasScheme());
    +  assertEquals('https', uri.getScheme());
    +  assertEquals('https://www.google.com:80/path?q=query#fragmento',
    +               uri.toString());
    +
    +  uri.setScheme(encodeURIComponent('ab cd'), true);
    +  assertTrue(uri.hasScheme());
    +  assertEquals('ab cd', uri.getScheme());
    +  assertEquals('ab%20cd://www.google.com:80/path?q=query#fragmento',
    +               uri.toString());
    +
    +  uri.setScheme('http:');
    +  assertTrue(uri.hasScheme());
    +  assertEquals('http', uri.getScheme());
    +  assertEquals('http://www.google.com:80/path?q=query#fragmento',
    +               uri.toString());
    +
    +  uri.setScheme('');
    +  assertFalse(uri.hasScheme());
    +  assertEquals('', uri.getScheme());
    +  assertEquals('//www.google.com:80/path?q=query#fragmento',
    +               uri.toString());
    +}
    +
    +function testSetDomain() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setDomain('\u1e21oogle.com');
    +  assertTrue(uri.hasDomain());
    +  assertEquals('\u1e21oogle.com', uri.getDomain());
    +  assertEquals('http://%E1%B8%A1oogle.com:80/path?q=query#fragmento',
    +               uri.toString());
    +
    +  uri.setDomain(encodeURIComponent('\u1e21oogle.com'), true);
    +  assertTrue(uri.hasDomain());
    +  assertEquals('\u1e21oogle.com', uri.getDomain());
    +  assertEquals('http://%E1%B8%A1oogle.com:80/path?q=query#fragmento',
    +               uri.toString());
    +
    +  uri.setDomain('');
    +  assertFalse(uri.hasDomain());
    +  assertEquals('', uri.getDomain());
    +  assertEquals('http:/path?q=query#fragmento',
    +               uri.toString());
    +}
    +
    +function testSetPort() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  assertThrows(function() {
    +    uri.setPort(-1);
    +  });
    +  assertEquals(80, uri.getPort());
    +
    +  assertThrows(function() {
    +    uri.setPort('a');
    +  });
    +  assertEquals(80, uri.getPort());
    +
    +  uri.setPort(443);
    +  assertTrue(uri.hasPort());
    +  assertEquals(443, uri.getPort());
    +  assertEquals('http://www.google.com:443/path?q=query#fragmento',
    +               uri.toString());
    +
    +  // TODO(chrishenry): This is undocumented, but exist in previous unit
    +  // test. We should clarify whether this is intended (alternatively,
    +  // setPort(0) also works).
    +  uri.setPort(null);
    +  assertFalse(uri.hasPort());
    +  assertEquals(null, uri.getPort());
    +  assertEquals('http://www.google.com/path?q=query#fragmento',
    +               uri.toString());
    +}
    +
    +function testSetPath() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setPath('/search path/');
    +  assertTrue(uri.hasPath());
    +  assertEquals('/search path/', uri.getPath());
    +  assertEquals(
    +      'http://www.google.com:80/search%20path/?q=query#fragmento',
    +      uri.toString());
    +
    +  uri.setPath(encodeURIComponent('search path 2/'), true);
    +  assertTrue(uri.hasPath());
    +  assertEquals('search path 2%2F', uri.getPath());
    +  assertEquals(
    +      'http://www.google.com:80/search%20path%202%2F?q=query#fragmento',
    +      uri.toString());
    +
    +  uri.setPath('');
    +  assertFalse(uri.hasPath());
    +  assertEquals('', uri.getPath());
    +  assertEquals(
    +      'http://www.google.com:80?q=query#fragmento',
    +      uri.toString());
    +}
    +
    +function testSetFragment() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setFragment('foo?bar=a b&baz=2');
    +  assertTrue(uri.hasFragment());
    +  assertEquals('foo?bar=a b&baz=2', uri.getFragment());
    +  assertEquals(
    +      'http://www.google.com:80/path?q=query#foo?bar=a%20b&baz=2',
    +      uri.toString());
    +
    +  uri.setFragment(encodeURIComponent('foo?bar=a b&baz=3'), true);
    +  assertTrue(uri.hasFragment());
    +  assertEquals('foo?bar=a b&baz=3', uri.getFragment());
    +  assertEquals(
    +      'http://www.google.com:80/path?q=query#foo?bar=a%20b&baz=3',
    +      uri.toString());
    +
    +  uri.setFragment('');
    +  assertFalse(uri.hasFragment());
    +  assertEquals('', uri.getFragment());
    +  assertEquals(
    +      'http://www.google.com:80/path?q=query',
    +      uri.toString());
    +}
    +
    +function testSetUserInfo() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setUserInfo('user:pw d');
    +  assertTrue(uri.hasUserInfo());
    +  assertEquals('user:pw d', uri.getUserInfo());
    +  assertEquals('http://user:pw%20d@www.google.com:80/path?q=query#fragmento',
    +      uri.toString());
    +
    +  uri.setUserInfo(encodeURIComponent('user:pw d2'), true);
    +  assertTrue(uri.hasUserInfo());
    +  assertEquals('user:pw d2', uri.getUserInfo());
    +  assertEquals('http://user:pw%20d2@www.google.com:80/path?q=query#fragmento',
    +      uri.toString());
    +
    +  uri.setUserInfo('user');
    +  assertTrue(uri.hasUserInfo());
    +  assertEquals('user', uri.getUserInfo());
    +  assertEquals('http://user@www.google.com:80/path?q=query#fragmento',
    +      uri.toString());
    +
    +  uri.setUserInfo('');
    +  assertFalse(uri.hasUserInfo());
    +  assertEquals('', uri.getUserInfo());
    +  assertEquals('http://www.google.com:80/path?q=query#fragmento',
    +      uri.toString());
    +}
    +
    +function testSetParameterValues() {
    +  var uri = new goog.Uri('http://www.google.com:80/path?q=query#fragmento');
    +
    +  uri.setParameterValues('q', ['foo', 'other query']);
    +  assertEquals('http://www.google.com:80/path?q=foo&q=other%20query#fragmento',
    +      uri.toString());
    +
    +  uri.setParameterValues('lang', 'en');
    +  assertEquals(
    +      'http://www.google.com:80/path?q=foo&q=other%20query&lang=en#fragmento',
    +      uri.toString());
    +}
    +
    +function testTreatmentOfAt1() {
    +  var uri = new goog.Uri('http://www.google.com?q=johndoe@gmail.com');
    +  assertEquals('http', uri.getScheme());
    +  assertEquals('www.google.com', uri.getDomain());
    +  assertEquals('johndoe@gmail.com', uri.getParameterValue('q'));
    +
    +  uri = goog.Uri.create('http', null, 'www.google.com', null, null,
    +                        'q=johndoe@gmail.com', null);
    +  assertEquals('http://www.google.com?q=johndoe%40gmail.com', uri.toString());
    +}
    +
    +function testTreatmentOfAt2() {
    +  var uri = new goog.Uri('http://www/~johndoe@gmail.com/foo');
    +  assertEquals('http', uri.getScheme());
    +  assertEquals('www', uri.getDomain());
    +  assertEquals('/~johndoe@gmail.com/foo', uri.getPath());
    +
    +  assertEquals('http://www/~johndoe@gmail.com/foo',
    +               goog.Uri.create('http', null, 'www', null,
    +                               '/~johndoe@gmail.com/foo', null, null).
    +               toString());
    +}
    +
    +function testTreatmentOfAt3() {
    +  var uri = new goog.Uri('ftp://skroob:1234@teleport/~skroob@vacuum');
    +  assertEquals('ftp', uri.getScheme());
    +  assertEquals('skroob:1234', uri.getUserInfo());
    +  assertEquals('teleport', uri.getDomain());
    +  assertEquals('/~skroob@vacuum', uri.getPath());
    +
    +  assertEquals('ftp://skroob:1234@teleport/~skroob@vacuum',
    +               goog.Uri.create('ftp', 'skroob:1234', 'teleport', null,
    +                               '/~skroob@vacuum', null, null).toString());
    +}
    +
    +function testTreatmentOfAt4() {
    +  assertEquals('ftp://darkhelmet:45%4078@teleport/~dhelmet@vacuum',
    +               goog.Uri.create('ftp', 'darkhelmet:45@78', 'teleport', null,
    +                               '/~dhelmet@vacuum', null, null).toString());
    +}
    +
    +function testSameDomain1() {
    +  var uri1 = 'http://www.google.com/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertTrue(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertTrue(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testSameDomain2() {
    +  var uri1 = 'http://www.google.com:1234/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertFalse(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testSameDomain3() {
    +  var uri1 = 'www.google.com/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertFalse(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testSameDomain4() {
    +  var uri1 = '/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertFalse(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testSameDomain5() {
    +  var uri1 = 'http://www.google.com/a';
    +  var uri2 = 'http://mail.google.com/b';
    +  assertFalse(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testSameDomain6() {
    +  var uri1 = '/a';
    +  var uri2 = '/b';
    +  assertTrue(goog.Uri.haveSameDomain(uri1, uri2));
    +  assertTrue(goog.Uri.haveSameDomain(uri2, uri1));
    +}
    +
    +function testMakeUnique() {
    +  var uri1 = new goog.Uri('http://www.google.com/setgmail');
    +  uri1.makeUnique();
    +  var uri2 = new goog.Uri('http://www.google.com/setgmail');
    +  uri2.makeUnique();
    +  assertTrue(uri1.getQueryData().containsKey(goog.Uri.RANDOM_PARAM));
    +  assertTrue(uri1.toString() != uri2.toString());
    +}
    +
    +function testSetReadOnly() {
    +  var uri = new goog.Uri('http://www.google.com/setgmail');
    +  uri.setReadOnly(true);
    +  assertThrows(function() {
    +    uri.setParameterValue('cant', 'dothis');
    +  });
    +}
    +
    +function testSetReadOnlyChained() {
    +  var uri = new goog.Uri('http://www.google.com/setgmail').setReadOnly(true);
    +  assertThrows(function() {
    +    uri.setParameterValue('cant', 'dothis');
    +  });
    +}
    +
    +function testQueryDataCount() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  assertEquals(5, qd.getCount());
    +}
    +
    +function testQueryDataRemove() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.remove('c');
    +  assertEquals(4, qd.getCount());
    +  assertEquals('a=A&a=A2&b=B&b=B2', String(qd));
    +  qd.remove('a');
    +  assertEquals(2, qd.getCount());
    +  assertEquals('b=B&b=B2', String(qd));
    +}
    +
    +function testQueryDataClear() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.clear();
    +  assertEquals(0, qd.getCount());
    +  assertEquals('', String(qd));
    +}
    +
    +function testQueryDataIsEmpty() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.remove('a');
    +  assertFalse(qd.isEmpty());
    +  qd.remove('b');
    +  assertFalse(qd.isEmpty());
    +  qd.remove('c');
    +  assertTrue(qd.isEmpty());
    +
    +  qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.clear();
    +  assertTrue(qd.isEmpty());
    +
    +  qd = new goog.Uri.QueryData('');
    +  assertTrue(qd.isEmpty());
    +}
    +
    +function testQueryDataContainsKey() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  assertTrue(qd.containsKey('a'));
    +  assertTrue(qd.containsKey('b'));
    +  assertTrue(qd.containsKey('c'));
    +  qd.remove('a');
    +  assertFalse(qd.containsKey('a'));
    +  assertTrue(qd.containsKey('b'));
    +  assertTrue(qd.containsKey('c'));
    +  qd.remove('b');
    +  assertFalse(qd.containsKey('a'));
    +  assertFalse(qd.containsKey('b'));
    +  assertTrue(qd.containsKey('c'));
    +  qd.remove('c');
    +  assertFalse(qd.containsKey('a'));
    +  assertFalse(qd.containsKey('b'));
    +  assertFalse(qd.containsKey('c'));
    +
    +  qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.clear();
    +  assertFalse(qd.containsKey('a'));
    +  assertFalse(qd.containsKey('b'));
    +  assertFalse(qd.containsKey('c'));
    +
    +  // Test case-insensitive
    +  qd = new goog.Uri.QueryData('aaa=A&bbb=B&aaa=A2&bbbb=B2&ccc=C', null, true);
    +  assertTrue(qd.containsKey('aaa'));
    +  assertTrue(qd.containsKey('bBb'));
    +  assertTrue(qd.containsKey('CCC'));
    +
    +  qd = new goog.Uri.QueryData('a=b=c');
    +  assertTrue(qd.containsKey('a'));
    +  assertFalse(qd.containsKey('b'));
    +}
    +
    +function testQueryDataContainsValue() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +
    +  assertTrue(qd.containsValue('A'));
    +  assertTrue(qd.containsValue('B'));
    +  assertTrue(qd.containsValue('A2'));
    +  assertTrue(qd.containsValue('B2'));
    +  assertTrue(qd.containsValue('C'));
    +  qd.remove('a');
    +  assertFalse(qd.containsValue('A'));
    +  assertTrue(qd.containsValue('B'));
    +  assertFalse(qd.containsValue('A2'));
    +  assertTrue(qd.containsValue('B2'));
    +  assertTrue(qd.containsValue('C'));
    +  qd.remove('b');
    +  assertFalse(qd.containsValue('A'));
    +  assertFalse(qd.containsValue('B'));
    +  assertFalse(qd.containsValue('A2'));
    +  assertFalse(qd.containsValue('B2'));
    +  assertTrue(qd.containsValue('C'));
    +  qd.remove('c');
    +  assertFalse(qd.containsValue('A'));
    +  assertFalse(qd.containsValue('B'));
    +  assertFalse(qd.containsValue('A2'));
    +  assertFalse(qd.containsValue('B2'));
    +  assertFalse(qd.containsValue('C'));
    +
    +  qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +  qd.clear();
    +  assertFalse(qd.containsValue('A'));
    +  assertFalse(qd.containsValue('B'));
    +  assertFalse(qd.containsValue('A2'));
    +  assertFalse(qd.containsValue('B2'));
    +  assertFalse(qd.containsValue('C'));
    +
    +  qd = new goog.Uri.QueryData('a=b=c');
    +  assertTrue(qd.containsValue('b=c'));
    +  assertFalse(qd.containsValue('b'));
    +  assertFalse(qd.containsValue('c'));
    +}
    +
    +function testQueryDataGetKeys() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C=extra');
    +
    +  assertEquals('aabbc', qd.getKeys().join(''));
    +  qd.remove('a');
    +  assertEquals('bbc', qd.getKeys().join(''));
    +  qd.add('d', 'D');
    +  qd.add('d', 'D');
    +  assertEquals('bbcdd', qd.getKeys().join(''));
    +
    +  // Test case-insensitive
    +  qd = new goog.Uri.QueryData('A=A&B=B&a=A2&b=B2&C=C=extra', null, true);
    +
    +  assertEquals('aabbc', qd.getKeys().join(''));
    +  qd.remove('a');
    +  assertEquals('bbc', qd.getKeys().join(''));
    +  qd.add('d', 'D');
    +  qd.add('D', 'D');
    +  assertEquals('bbcdd', qd.getKeys().join(''));
    +}
    +
    +function testQueryDataGetValues() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C=extra');
    +
    +  assertArrayEquals(['A', 'A2', 'B', 'B2', 'C=extra'], qd.getValues());
    +  qd.remove('a');
    +  assertArrayEquals(['B', 'B2', 'C=extra'], qd.getValues());
    +  qd.add('d', 'D');
    +  qd.add('d', 'D');
    +  assertArrayEquals(['B', 'B2', 'C=extra', 'D', 'D'], qd.getValues());
    +
    +  qd.add('e', new String('Eee'));
    +  assertArrayEquals(['B', 'B2', 'C=extra', 'D', 'D', 'Eee'], qd.getValues());
    +
    +  assertArrayEquals(['Eee'], qd.getValues('e'));
    +}
    +
    +function testQueryDataSet() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +
    +  qd.set('d', 'D');
    +  assertEquals('a=A&a=A2&b=B&b=B2&c=C&d=D', String(qd));
    +  qd.set('d', 'D2');
    +  assertEquals('a=A&a=A2&b=B&b=B2&c=C&d=D2', String(qd));
    +  qd.set('a', 'A3');
    +  assertEquals('a=A3&b=B&b=B2&c=C&d=D2', String(qd));
    +  qd.remove('a');
    +  qd.set('a', 'A4');
    +  // this is different in IE and Mozilla so we cannot use the toString to test
    +  assertEquals('A4', qd.get('a'));
    +}
    +
    +function testQueryDataGet() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C=extra');
    +
    +  assertEquals('A', qd.get('a'));
    +  assertEquals('B', qd.get('b'));
    +  assertEquals('C=extra', qd.get('c'));
    +  assertEquals('Default', qd.get('d', 'Default'));
    +
    +  qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C=extra', null, true);
    +
    +  assertEquals('A', qd.get('A'));
    +  assertEquals('B', qd.get('b'));
    +  assertEquals('C=extra', qd.get('C'));
    +  assertEquals('Default', qd.get('D', 'Default'));
    +
    +  // Some unit tests pass undefined to get method (even though the type
    +  // for key is {string}). This is not caught by JsCompiler as
    +  // tests aren't typically compiled.
    +  assertUndefined(qd.get(undefined));
    +}
    +
    +
    +function testQueryDataSetValues() {
    +  var qd = new goog.Uri.QueryData('a=A&b=B&a=A2&b=B2&c=C');
    +
    +  qd.setValues('a', ['A3', 'A4', 'A5']);
    +  assertEquals('a=A3&a=A4&a=A5&b=B&b=B2&c=C', String(qd));
    +  qd.setValues('d', ['D']);
    +  assertEquals('a=A3&a=A4&a=A5&b=B&b=B2&c=C&d=D', String(qd));
    +  qd.setValues('e', []);
    +  assertEquals('a=A3&a=A4&a=A5&b=B&b=B2&c=C&d=D', String(qd));
    +}
    +
    +function testQueryDataSetIgnoreCase() {
    +  var qd = new goog.Uri.QueryData('aaA=one&BBb=two&cCc=three');
    +  assertEquals('one', qd.get('aaA'));
    +  assertEquals(undefined, qd.get('aaa'));
    +  qd.setIgnoreCase(true);
    +  assertEquals('one', qd.get('aaA'));
    +  assertEquals('one', qd.get('aaa'));
    +  qd.setIgnoreCase(false);
    +  assertEquals(undefined, qd.get('aaA'));
    +  assertEquals('one', qd.get('aaa'));
    +  qd.add('DdD', 'four');
    +  assertEquals('four', qd.get('DdD'));
    +  assertEquals(undefined, qd.get('ddd'));
    +}
    +
    +function testQueryDataSetIgnoreCaseWithMultipleValues() {
    +  var qd = new goog.Uri.QueryData('aaA=one&aaA=two');
    +  qd.setIgnoreCase(true);
    +  assertArrayEquals(['one', 'two'], qd.getValues('aaA'));
    +  assertArrayEquals(['one', 'two'], qd.getValues('aaa'));
    +}
    +
    +function testQueryDataExtend() {
    +  var qd1 = new goog.Uri.QueryData('a=A&b=B&c=C');
    +  var qd2 = new goog.Uri.QueryData('d=D&e=E');
    +  qd1.extend(qd2);
    +  assertEquals('a=A&b=B&c=C&d=D&e=E', String(qd1));
    +
    +  qd1 = new goog.Uri.QueryData('a=A&b=B&c=C');
    +  qd2 = new goog.Uri.QueryData('d=D&e=E');
    +  var qd3 = new goog.Uri.QueryData('f=F&g=G');
    +  qd1.extend(qd2, qd3);
    +  assertEquals('a=A&b=B&c=C&d=D&e=E&f=F&g=G', String(qd1));
    +
    +  qd1 = new goog.Uri.QueryData('a=A&b=B&c=C');
    +  qd2 = new goog.Uri.QueryData('a=A&c=C');
    +  qd1.extend(qd2);
    +  assertEquals('a=A&a=A&b=B&c=C&c=C', String(qd1));
    +}
    +
    +function testQueryDataCreateFromMap() {
    +  assertEquals('', String(goog.Uri.QueryData.createFromMap({})));
    +  assertEquals('a=A&b=B&c=C',
    +      String(goog.Uri.QueryData.createFromMap({a: 'A', b: 'B', c: 'C'})));
    +  assertEquals('a=foo%26bar',
    +      String(goog.Uri.QueryData.createFromMap({a: 'foo&bar'})));
    +}
    +
    +function testQueryDataCreateFromMapWithArrayValues() {
    +  var obj = {'key': ['1', '2', '3']};
    +  var qd = goog.Uri.QueryData.createFromMap(obj);
    +  assertEquals('key=1&key=2&key=3', qd.toString());
    +  qd.add('breakCache', 1);
    +  obj.key.push('4');
    +  assertEquals('key=1&key=2&key=3&breakCache=1', qd.toString());
    +
    +}
    +
    +function testQueryDataCreateFromKeysValues() {
    +  assertEquals('', String(goog.Uri.QueryData.createFromKeysValues([], [])));
    +  assertEquals('a=A&b=B&c=C', String(goog.Uri.QueryData.createFromKeysValues(
    +      ['a', 'b', 'c'], ['A', 'B', 'C'])));
    +  assertEquals('a=A&a=B&a=C', String(goog.Uri.QueryData.createFromKeysValues(
    +      ['a', 'a', 'a'], ['A', 'B', 'C'])));
    +}
    +
    +function testQueryDataAddMultipleValuesWithSameKey() {
    +  var qd = new goog.Uri.QueryData();
    +  qd.add('abc', 'v');
    +  qd.add('abc', 'v2');
    +  qd.add('abc', 'v3');
    +  assertEquals('abc=v&abc=v2&abc=v3', qd.toString());
    +}
    +
    +function testQueryDataAddWithArray() {
    +  var qd = new goog.Uri.QueryData();
    +  qd.add('abc', ['v', 'v2']);
    +  assertEquals('abc=v%2Cv2', qd.toString());
    +}
    +
    +function testFragmentEncoding() {
    +  var allowedInFragment = /[A-Za-z0-9\-\._~!$&'()*+,;=:@/?]/g;
    +
    +  var sb = [];
    +  for (var i = 33; i < 500; i++) {  // arbitrarily use first 500 chars.
    +    sb.push(String.fromCharCode(i));
    +  }
    +  var testString = sb.join('');
    +
    +  var fragment = new goog.Uri().setFragment(testString).toString();
    +
    +  // Remove first '#' character.
    +  fragment = fragment.substr(1);
    +
    +  // Strip all percent encoded characters, as they're ok.
    +  fragment = fragment.replace(/%[0-9A-F][0-9A-F]/g, '');
    +
    +  // Remove allowed characters.
    +  fragment = fragment.replace(allowedInFragment, '');
    +
    +  // Only illegal characters should remain, which is a fail.
    +  assertEquals('String should be empty', 0, fragment.length);
    +
    +}
    +
    +function testStrictDoubleEncodingRemoval() {
    +  var url = goog.Uri.parse('dummy/a%25invalid');
    +  assertEquals('dummy/a%25invalid', url.toString());
    +  url = goog.Uri.parse('dummy/a%252fdouble-encoded-slash');
    +  assertEquals('dummy/a%2fdouble-encoded-slash', url.toString());
    +}
    +
    +
    +// Tests, that creating URI from components and then
    +// getting the components back yields equal results.
    +// The special attention is payed to test proper encoding
    +// and decoding of URI components.
    +function testComponentsAfterUriCreate() {
    +  var createdUri = new goog.Uri.create('%40',  // scheme
    +                                       '%41',  // user info
    +                                       '%42',  // domain
    +                                       43,     // port
    +                                       '%44',  // path
    +                                       '%45',  // query
    +                                       '%46'); // fragment
    +
    +  assertEquals('%40', createdUri.getScheme());
    +  assertEquals('%41', createdUri.getUserInfo());
    +  assertEquals('%42', createdUri.getDomain());
    +  assertEquals(43, createdUri.getPort());
    +  assertEquals('%44', createdUri.getPath());
    +  assertEquals('%2545', createdUri.getQuery()); // returns encoded value
    +  assertEquals('%45', createdUri.getDecodedQuery());
    +  assertEquals('%2545', createdUri.getEncodedQuery());
    +  assertEquals('%46', createdUri.getFragment());
    +}
    +
    +// Tests setting the query string and then reading back
    +// query parameter values.
    +function testSetQueryAndGetParameterValue() {
    +  var uri = new goog.Uri();
    +
    +  // Sets query as decoded string.
    +  uri.setQuery('i=j&k');
    +  assertEquals('?i=j&k', uri.toString());
    +  assertEquals('i=j&k', uri.getDecodedQuery());
    +  assertEquals('i=j&k', uri.getEncodedQuery());
    +  assertEquals('i=j&k', uri.getQuery()); // returns encoded value
    +  assertEquals('j', uri.getParameterValue('i'));
    +  assertEquals('', uri.getParameterValue('k'));
    +
    +  // Sets query as encoded string.
    +  uri.setQuery('i=j&k', true);
    +  assertEquals('?i=j&k', uri.toString());
    +  assertEquals('i=j&k', uri.getDecodedQuery());
    +  assertEquals('i=j&k', uri.getEncodedQuery());
    +  assertEquals('i=j&k', uri.getQuery()); // returns encoded value
    +  assertEquals('j', uri.getParameterValue('i'));
    +  assertEquals('', uri.getParameterValue('k'));
    +
    +  // Sets query as decoded string.
    +  uri.setQuery('i=j%26k');
    +  assertEquals('?i=j%2526k', uri.toString());
    +  assertEquals('i=j%26k', uri.getDecodedQuery());
    +  assertEquals('i=j%2526k', uri.getEncodedQuery());
    +  assertEquals('i=j%2526k', uri.getQuery()); // returns encoded value
    +  assertEquals('j%26k', uri.getParameterValue('i'));
    +  assertUndefined(uri.getParameterValue('k'));
    +
    +  // Sets query as encoded string.
    +  uri.setQuery('i=j%26k', true);
    +  assertEquals('?i=j%26k', uri.toString());
    +  assertEquals('i=j&k', uri.getDecodedQuery());
    +  assertEquals('i=j%26k', uri.getEncodedQuery());
    +  assertEquals('i=j%26k', uri.getQuery()); // returns encoded value
    +  assertEquals('j&k', uri.getParameterValue('i'));
    +  assertUndefined(uri.getParameterValue('k'));
    +}
    +
    +// Tests setting query parameter values and the reading back the query string.
    +function testSetParameterValueAndGetQuery() {
    +  var uri = new goog.Uri();
    +
    +  uri.setParameterValue('a', 'b&c');
    +  assertEquals('?a=b%26c', uri.toString());
    +  assertEquals('a=b&c', uri.getDecodedQuery());
    +  assertEquals('a=b%26c', uri.getEncodedQuery());
    +  assertEquals('a=b%26c', uri.getQuery()); // returns encoded value
    +
    +  uri.setParameterValue('a', 'b%26c');
    +  assertEquals('?a=b%2526c', uri.toString());
    +  assertEquals('a=b%26c', uri.getDecodedQuery());
    +  assertEquals('a=b%2526c', uri.getEncodedQuery());
    +  assertEquals('a=b%2526c', uri.getQuery()); // returns encoded value
    +}
    +
    +
    +// Tests that building a URI with a query string and then reading it back
    +// gives the same result.
    +function testQueryNotModified() {
    +  assertEquals('?foo', new goog.Uri('?foo').toString());
    +  assertEquals('?foo=', new goog.Uri('?foo=').toString());
    +  assertEquals('?foo=bar', new goog.Uri('?foo=bar').toString());
    +  assertEquals('?&=&=&', new goog.Uri('?&=&=&').toString());
    +}
    +
    +
    +function testRelativePathEscapesColon() {
    +  assertEquals('javascript%3aalert(1)',
    +               new goog.Uri().setPath('javascript:alert(1)').toString());
    +}
    +
    +
    +function testAbsolutePathDoesNotEscapeColon() {
    +  assertEquals('/javascript:alert(1)',
    +               new goog.Uri('/javascript:alert(1)').toString());
    +}
    +
    +
    +function testColonInPathNotUnescaped() {
    +  assertEquals('/javascript%3aalert(1)',
    +               new goog.Uri('/javascript%3aalert(1)').toString());
    +  assertEquals('javascript%3aalert(1)',
    +               new goog.Uri('javascript%3aalert(1)').toString());
    +  assertEquals('javascript:alert(1)',
    +               new goog.Uri('javascript:alert(1)').toString());
    +  assertEquals('http://www.foo.bar/path:with:colon/x',
    +               new goog.Uri('http://www.foo.bar/path:with:colon/x').toString());
    +  assertEquals('//www.foo.bar/path:with:colon/x',
    +               new goog.Uri('//www.foo.bar/path:with:colon/x').toString());
    +}
    +
    +
    +// verifies bug http://b/9821952
    +function testGetQueryForEmptyString() {
    +  var queryData = new goog.Uri.QueryData('a=b&c=d');
    +  assertArrayEquals(['b', 'd'], queryData.getValues());
    +  assertArrayEquals([], queryData.getValues(''));
    +
    +  queryData = new goog.Uri.QueryData('a=b&c=d&=e');
    +  assertArrayEquals(['e'], queryData.getValues(''));
    +}
    +
    +
    +function testRestrictedCharactersArePreserved() {
    +  var uri = new goog.Uri(
    +      'ht%74p://hos%74.example.%2f.com/pa%74h%2f-with-embedded-slash/');
    +  assertEquals('http', uri.getScheme());
    +  assertEquals('host.example.%2f.com', uri.getDomain());
    +  assertEquals('/path%2f-with-embedded-slash/', uri.getPath());
    +  assertEquals('http://host.example.%2f.com/path%2f-with-embedded-slash/',
    +      uri.toString());
    +}
    +
    +function assertDotRemovedEquals(expected, path) {
    +  assertEquals(expected, goog.Uri.removeDotSegments(path));
    +}
    +
    +function assertResolvedEquals(expected, base, other) {
    +  assertEquals(expected, goog.Uri.resolve(base, other).toString());
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/utils.js b/mapbox-gl/closure-library/closure/goog/uri/utils.js
    new file mode 100644
    index 0000000000..602a1584ed
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/utils.js
    @@ -0,0 +1,1116 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Simple utilities for dealing with URI strings.
    + *
    + * This is intended to be a lightweight alternative to constructing goog.Uri
    + * objects.  Whereas goog.Uri adds several kilobytes to the binary regardless
    + * of how much of its functionality you use, this is designed to be a set of
    + * mostly-independent utilities so that the compiler includes only what is
    + * necessary for the task.  Estimated savings of porting is 5k pre-gzip and
    + * 1.5k post-gzip.  To ensure the savings remain, future developers should
    + * avoid adding new functionality to existing functions, but instead create
    + * new ones and factor out shared code.
    + *
    + * Many of these utilities have limited functionality, tailored to common
    + * cases.  The query parameter utilities assume that the parameter keys are
    + * already encoded, since most keys are compile-time alphanumeric strings.  The
    + * query parameter mutation utilities also do not tolerate fragment identifiers.
    + *
    + * By design, these functions can be slower than goog.Uri equivalents.
    + * Repeated calls to some of functions may be quadratic in behavior for IE,
    + * although the effect is somewhat limited given the 2kb limit.
    + *
    + * One advantage of the limited functionality here is that this approach is
    + * less sensitive to differences in URI encodings than goog.Uri, since these
    + * functions modify the strings in place, rather than decoding and
    + * re-encoding.
    + *
    + * Uses features of RFC 3986 for parsing/formatting URIs:
    + *   http://www.ietf.org/rfc/rfc3986.txt
    + *
    + * @author gboyer@google.com (Garrett Boyer) - The "lightened" design.
    + */
    +
    +goog.provide('goog.uri.utils');
    +goog.provide('goog.uri.utils.ComponentIndex');
    +goog.provide('goog.uri.utils.QueryArray');
    +goog.provide('goog.uri.utils.QueryValue');
    +goog.provide('goog.uri.utils.StandardQueryParam');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Character codes inlined to avoid object allocations due to charCode.
    + * @enum {number}
    + * @private
    + */
    +goog.uri.utils.CharCode_ = {
    +  AMPERSAND: 38,
    +  EQUAL: 61,
    +  HASH: 35,
    +  QUESTION: 63
    +};
    +
    +
    +/**
    + * Builds a URI string from already-encoded parts.
    + *
    + * No encoding is performed.  Any component may be omitted as either null or
    + * undefined.
    + *
    + * @param {?string=} opt_scheme The scheme such as 'http'.
    + * @param {?string=} opt_userInfo The user name before the '@'.
    + * @param {?string=} opt_domain The domain such as 'www.google.com', already
    + *     URI-encoded.
    + * @param {(string|number|null)=} opt_port The port number.
    + * @param {?string=} opt_path The path, already URI-encoded.  If it is not
    + *     empty, it must begin with a slash.
    + * @param {?string=} opt_queryData The URI-encoded query data.
    + * @param {?string=} opt_fragment The URI-encoded fragment identifier.
    + * @return {string} The fully combined URI.
    + */
    +goog.uri.utils.buildFromEncodedParts = function(opt_scheme, opt_userInfo,
    +    opt_domain, opt_port, opt_path, opt_queryData, opt_fragment) {
    +  var out = '';
    +
    +  if (opt_scheme) {
    +    out += opt_scheme + ':';
    +  }
    +
    +  if (opt_domain) {
    +    out += '//';
    +
    +    if (opt_userInfo) {
    +      out += opt_userInfo + '@';
    +    }
    +
    +    out += opt_domain;
    +
    +    if (opt_port) {
    +      out += ':' + opt_port;
    +    }
    +  }
    +
    +  if (opt_path) {
    +    out += opt_path;
    +  }
    +
    +  if (opt_queryData) {
    +    out += '?' + opt_queryData;
    +  }
    +
    +  if (opt_fragment) {
    +    out += '#' + opt_fragment;
    +  }
    +
    +  return out;
    +};
    +
    +
    +/**
    + * A regular expression for breaking a URI into its component parts.
    + *
    + * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B
    + * As the "first-match-wins" algorithm is identical to the "greedy"
    + * disambiguation method used by POSIX regular expressions, it is natural and
    + * commonplace to use a regular expression for parsing the potential five
    + * components of a URI reference.
    + *
    + * The following line is the regular expression for breaking-down a
    + * well-formed URI reference into its components.
    + *
    + * <pre>
    + * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
    + *  12            3  4          5       6  7        8 9
    + * </pre>
    + *
    + * The numbers in the second line above are only to assist readability; they
    + * indicate the reference points for each subexpression (i.e., each paired
    + * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
    + * For example, matching the above expression to
    + * <pre>
    + *     http://www.ics.uci.edu/pub/ietf/uri/#Related
    + * </pre>
    + * results in the following subexpression matches:
    + * <pre>
    + *    $1 = http:
    + *    $2 = http
    + *    $3 = //www.ics.uci.edu
    + *    $4 = www.ics.uci.edu
    + *    $5 = /pub/ietf/uri/
    + *    $6 = <undefined>
    + *    $7 = <undefined>
    + *    $8 = #Related
    + *    $9 = Related
    + * </pre>
    + * where <undefined> indicates that the component is not present, as is the
    + * case for the query component in the above example. Therefore, we can
    + * determine the value of the five components as
    + * <pre>
    + *    scheme    = $2
    + *    authority = $4
    + *    path      = $5
    + *    query     = $7
    + *    fragment  = $9
    + * </pre>
    + *
    + * The regular expression has been modified slightly to expose the
    + * userInfo, domain, and port separately from the authority.
    + * The modified version yields
    + * <pre>
    + *    $1 = http              scheme
    + *    $2 = <undefined>       userInfo -\
    + *    $3 = www.ics.uci.edu   domain     | authority
    + *    $4 = <undefined>       port     -/
    + *    $5 = /pub/ietf/uri/    path
    + *    $6 = <undefined>       query without ?
    + *    $7 = Related           fragment without #
    + * </pre>
    + * @type {!RegExp}
    + * @private
    + */
    +goog.uri.utils.splitRe_ = new RegExp(
    +    '^' +
    +    '(?:' +
    +        '([^:/?#.]+)' +                  // scheme - ignore special characters
    +                                         // used by other URL parts such as :,
    +                                         // ?, /, #, and .
    +    ':)?' +
    +    '(?://' +
    +        '(?:([^/?#]*)@)?' +              // userInfo
    +        '([^/#?]*?)' +                   // domain
    +        '(?::([0-9]+))?' +               // port
    +        '(?=[/#?]|$)' +                  // authority-terminating character
    +    ')?' +
    +    '([^?#]+)?' +                        // path
    +    '(?:\\?([^#]*))?' +                  // query
    +    '(?:#(.*))?' +                       // fragment
    +    '$');
    +
    +
    +/**
    + * The index of each URI component in the return value of goog.uri.utils.split.
    + * @enum {number}
    + */
    +goog.uri.utils.ComponentIndex = {
    +  SCHEME: 1,
    +  USER_INFO: 2,
    +  DOMAIN: 3,
    +  PORT: 4,
    +  PATH: 5,
    +  QUERY_DATA: 6,
    +  FRAGMENT: 7
    +};
    +
    +
    +/**
    + * Splits a URI into its component parts.
    + *
    + * Each component can be accessed via the component indices; for example:
    + * <pre>
    + * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA];
    + * </pre>
    + *
    + * @param {string} uri The URI string to examine.
    + * @return {!Array<string|undefined>} Each component still URI-encoded.
    + *     Each component that is present will contain the encoded value, whereas
    + *     components that are not present will be undefined or empty, depending
    + *     on the browser's regular expression implementation.  Never null, since
    + *     arbitrary strings may still look like path names.
    + */
    +goog.uri.utils.split = function(uri) {
    +  goog.uri.utils.phishingProtection_();
    +
    +  // See @return comment -- never null.
    +  return /** @type {!Array<string|undefined>} */ (
    +      uri.match(goog.uri.utils.splitRe_));
    +};
    +
    +
    +/**
    + * Safari has a nasty bug where if you have an http URL with a username, e.g.,
    + * http://evil.com%2F@google.com/
    + * Safari will report that window.location.href is
    + * http://evil.com/google.com/
    + * so that anyone who tries to parse the domain of that URL will get
    + * the wrong domain. We've seen exploits where people use this to trick
    + * Safari into loading resources from evil domains.
    + *
    + * To work around this, we run a little "Safari phishing check", and throw
    + * an exception if we see this happening.
    + *
    + * There is no convenient place to put this check. We apply it to
    + * anyone doing URI parsing on Webkit. We're not happy about this, but
    + * it fixes the problem.
    + *
    + * This should be removed once Safari fixes their bug.
    + *
    + * Exploit reported by Masato Kinugawa.
    + *
    + * @type {boolean}
    + * @private
    + */
    +goog.uri.utils.needsPhishingProtection_ = goog.userAgent.WEBKIT;
    +
    +
    +/**
    + * Check to see if the user is being phished.
    + * @private
    + */
    +goog.uri.utils.phishingProtection_ = function() {
    +  if (goog.uri.utils.needsPhishingProtection_) {
    +    // Turn protection off, so that we don't recurse.
    +    goog.uri.utils.needsPhishingProtection_ = false;
    +
    +    // Use quoted access, just in case the user isn't using location externs.
    +    var location = goog.global['location'];
    +    if (location) {
    +      var href = location['href'];
    +      if (href) {
    +        var domain = goog.uri.utils.getDomain(href);
    +        if (domain && domain != location['hostname']) {
    +          // Phishing attack
    +          goog.uri.utils.needsPhishingProtection_ = true;
    +          throw Error();
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {?string} uri A possibly null string.
    + * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986
    + *     reserved characters will not be removed.
    + * @return {?string} The string URI-decoded, or null if uri is null.
    + * @private
    + */
    +goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {
    +  if (!uri) {
    +    return uri;
    +  }
    +
    +  return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);
    +};
    +
    +
    +/**
    + * Gets a URI component by index.
    + *
    + * It is preferred to use the getPathEncoded() variety of functions ahead,
    + * since they are more readable.
    + *
    + * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
    + * @param {string} uri The URI to examine.
    + * @return {?string} The still-encoded component, or null if the component
    + *     is not present.
    + * @private
    + */
    +goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
    +  // Convert undefined, null, and empty string into null.
    +  return goog.uri.utils.split(uri)[componentIndex] || null;
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The protocol or scheme, or null if none.  Does not
    + *     include trailing colons or slashes.
    + */
    +goog.uri.utils.getScheme = function(uri) {
    +  return goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.SCHEME, uri);
    +};
    +
    +
    +/**
    + * Gets the effective scheme for the URL.  If the URL is relative then the
    + * scheme is derived from the page's location.
    + * @param {string} uri The URI to examine.
    + * @return {string} The protocol or scheme, always lower case.
    + */
    +goog.uri.utils.getEffectiveScheme = function(uri) {
    +  var scheme = goog.uri.utils.getScheme(uri);
    +  if (!scheme && self.location) {
    +    var protocol = self.location.protocol;
    +    scheme = protocol.substr(0, protocol.length - 1);
    +  }
    +  // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
    +  // All other browsers with web workers support self.location from the worker.
    +  return scheme ? scheme.toLowerCase() : '';
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The user name still encoded, or null if none.
    + */
    +goog.uri.utils.getUserInfoEncoded = function(uri) {
    +  return goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.USER_INFO, uri);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The decoded user info, or null if none.
    + */
    +goog.uri.utils.getUserInfo = function(uri) {
    +  return goog.uri.utils.decodeIfPossible_(
    +      goog.uri.utils.getUserInfoEncoded(uri));
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The domain name still encoded, or null if none.
    + */
    +goog.uri.utils.getDomainEncoded = function(uri) {
    +  return goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.DOMAIN, uri);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The decoded domain, or null if none.
    + */
    +goog.uri.utils.getDomain = function(uri) {
    +  return goog.uri.utils.decodeIfPossible_(
    +      goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?number} The port number, or null if none.
    + */
    +goog.uri.utils.getPort = function(uri) {
    +  // Coerce to a number.  If the result of getComponentByIndex_ is null or
    +  // non-numeric, the number coersion yields NaN.  This will then return
    +  // null for all non-numeric cases (though also zero, which isn't a relevant
    +  // port number).
    +  return Number(goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.PORT, uri)) || null;
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The path still encoded, or null if none. Includes the
    + *     leading slash, if any.
    + */
    +goog.uri.utils.getPathEncoded = function(uri) {
    +  return goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.PATH, uri);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The decoded path, or null if none.  Includes the leading
    + *     slash, if any.
    + */
    +goog.uri.utils.getPath = function(uri) {
    +  return goog.uri.utils.decodeIfPossible_(
    +      goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The query data still encoded, or null if none.  Does not
    + *     include the question mark itself.
    + */
    +goog.uri.utils.getQueryData = function(uri) {
    +  return goog.uri.utils.getComponentByIndex_(
    +      goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The fragment identifier, or null if none.  Does not
    + *     include the hash mark itself.
    + */
    +goog.uri.utils.getFragmentEncoded = function(uri) {
    +  // The hash mark may not appear in any other part of the URL.
    +  var hashIndex = uri.indexOf('#');
    +  return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @param {?string} fragment The encoded fragment identifier, or null if none.
    + *     Does not include the hash mark itself.
    + * @return {string} The URI with the fragment set.
    + */
    +goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
    +  return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
    +};
    +
    +
    +/**
    + * @param {string} uri The URI to examine.
    + * @return {?string} The decoded fragment identifier, or null if none.  Does
    + *     not include the hash mark.
    + */
    +goog.uri.utils.getFragment = function(uri) {
    +  return goog.uri.utils.decodeIfPossible_(
    +      goog.uri.utils.getFragmentEncoded(uri));
    +};
    +
    +
    +/**
    + * Extracts everything up to the port of the URI.
    + * @param {string} uri The URI string.
    + * @return {string} Everything up to and including the port.
    + */
    +goog.uri.utils.getHost = function(uri) {
    +  var pieces = goog.uri.utils.split(uri);
    +  return goog.uri.utils.buildFromEncodedParts(
    +      pieces[goog.uri.utils.ComponentIndex.SCHEME],
    +      pieces[goog.uri.utils.ComponentIndex.USER_INFO],
    +      pieces[goog.uri.utils.ComponentIndex.DOMAIN],
    +      pieces[goog.uri.utils.ComponentIndex.PORT]);
    +};
    +
    +
    +/**
    + * Extracts the path of the URL and everything after.
    + * @param {string} uri The URI string.
    + * @return {string} The URI, starting at the path and including the query
    + *     parameters and fragment identifier.
    + */
    +goog.uri.utils.getPathAndAfter = function(uri) {
    +  var pieces = goog.uri.utils.split(uri);
    +  return goog.uri.utils.buildFromEncodedParts(null, null, null, null,
    +      pieces[goog.uri.utils.ComponentIndex.PATH],
    +      pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
    +      pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
    +};
    +
    +
    +/**
    + * Gets the URI with the fragment identifier removed.
    + * @param {string} uri The URI to examine.
    + * @return {string} Everything preceding the hash mark.
    + */
    +goog.uri.utils.removeFragment = function(uri) {
    +  // The hash mark may not appear in any other part of the URL.
    +  var hashIndex = uri.indexOf('#');
    +  return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
    +};
    +
    +
    +/**
    + * Ensures that two URI's have the exact same domain, scheme, and port.
    + *
    + * Unlike the version in goog.Uri, this checks protocol, and therefore is
    + * suitable for checking against the browser's same-origin policy.
    + *
    + * @param {string} uri1 The first URI.
    + * @param {string} uri2 The second URI.
    + * @return {boolean} Whether they have the same scheme, domain and port.
    + */
    +goog.uri.utils.haveSameDomain = function(uri1, uri2) {
    +  var pieces1 = goog.uri.utils.split(uri1);
    +  var pieces2 = goog.uri.utils.split(uri2);
    +  return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
    +             pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
    +         pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
    +             pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
    +         pieces1[goog.uri.utils.ComponentIndex.PORT] ==
    +             pieces2[goog.uri.utils.ComponentIndex.PORT];
    +};
    +
    +
    +/**
    + * Asserts that there are no fragment or query identifiers, only in uncompiled
    + * mode.
    + * @param {string} uri The URI to examine.
    + * @private
    + */
    +goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
    +  // NOTE: would use goog.asserts here, but jscompiler doesn't know that
    +  // indexOf has no side effects.
    +  if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) {
    +    throw Error('goog.uri.utils: Fragment or query identifiers are not ' +
    +        'supported: [' + uri + ']');
    +  }
    +};
    +
    +
    +/**
    + * Supported query parameter values by the parameter serializing utilities.
    + *
    + * If a value is null or undefined, the key-value pair is skipped, as an easy
    + * way to omit parameters conditionally.  Non-array parameters are converted
    + * to a string and URI encoded.  Array values are expanded into multiple
    + * &key=value pairs, with each element stringized and URI-encoded.
    + *
    + * @typedef {*}
    + */
    +goog.uri.utils.QueryValue;
    +
    +
    +/**
    + * An array representing a set of query parameters with alternating keys
    + * and values.
    + *
    + * Keys are assumed to be URI encoded already and live at even indices.  See
    + * goog.uri.utils.QueryValue for details on how parameter values are encoded.
    + *
    + * Example:
    + * <pre>
    + * var data = [
    + *   // Simple param: ?name=BobBarker
    + *   'name', 'BobBarker',
    + *   // Conditional param -- may be omitted entirely.
    + *   'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
    + *   // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
    + *   'house', ['LosAngeles', 'NewYork', null]
    + * ];
    + * </pre>
    + *
    + * @typedef {!Array<string|goog.uri.utils.QueryValue>}
    + */
    +goog.uri.utils.QueryArray;
    +
    +
    +/**
    + * Parses encoded query parameters and calls callback function for every
    + * parameter found in the string.
    + *
    + * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an
    + * empty string.  Keys may be empty strings (e.g. “…&=value&…”) which also means
    + * that “…&=&…” and “…&&…” will result in an empty key and value.
    + *
    + * @param {string} encodedQuery Encoded query string excluding question mark at
    + *     the beginning.
    + * @param {function(string, string)} callback Function called for every
    + *     parameter found in query string.  The first argument (name) will not be
    + *     urldecoded (so the function is consistent with buildQueryData), but the
    + *     second will.  If the parameter has no value (i.e. “=” was not present)
    + *     the second argument (value) will be an empty string.
    + */
    +goog.uri.utils.parseQueryData = function(encodedQuery, callback) {
    +  var pairs = encodedQuery.split('&');
    +  for (var i = 0; i < pairs.length; i++) {
    +    var indexOfEquals = pairs[i].indexOf('=');
    +    var name = null;
    +    var value = null;
    +    if (indexOfEquals >= 0) {
    +      name = pairs[i].substring(0, indexOfEquals);
    +      value = pairs[i].substring(indexOfEquals + 1);
    +    } else {
    +      name = pairs[i];
    +    }
    +    callback(name, value ? goog.string.urlDecode(value) : '');
    +  }
    +};
    +
    +
    +/**
    + * Appends a URI and query data in a string buffer with special preconditions.
    + *
    + * Internal implementation utility, performing very few object allocations.
    + *
    + * @param {!Array<string|undefined>} buffer A string buffer.  The first element
    + *     must be the base URI, and may have a fragment identifier.  If the array
    + *     contains more than one element, the second element must be an ampersand,
    + *     and may be overwritten, depending on the base URI.  Undefined elements
    + *     are treated as empty-string.
    + * @return {string} The concatenated URI and query data.
    + * @private
    + */
    +goog.uri.utils.appendQueryData_ = function(buffer) {
    +  if (buffer[1]) {
    +    // At least one query parameter was added.  We need to check the
    +    // punctuation mark, which is currently an ampersand, and also make sure
    +    // there aren't any interfering fragment identifiers.
    +    var baseUri = /** @type {string} */ (buffer[0]);
    +    var hashIndex = baseUri.indexOf('#');
    +    if (hashIndex >= 0) {
    +      // Move the fragment off the base part of the URI into the end.
    +      buffer.push(baseUri.substr(hashIndex));
    +      buffer[0] = baseUri = baseUri.substr(0, hashIndex);
    +    }
    +    var questionIndex = baseUri.indexOf('?');
    +    if (questionIndex < 0) {
    +      // No question mark, so we need a question mark instead of an ampersand.
    +      buffer[1] = '?';
    +    } else if (questionIndex == baseUri.length - 1) {
    +      // Question mark is the very last character of the existing URI, so don't
    +      // append an additional delimiter.
    +      buffer[1] = undefined;
    +    }
    +  }
    +
    +  return buffer.join('');
    +};
    +
    +
    +/**
    + * Appends key=value pairs to an array, supporting multi-valued objects.
    + * @param {string} key The key prefix.
    + * @param {goog.uri.utils.QueryValue} value The value to serialize.
    + * @param {!Array<string>} pairs The array to which the 'key=value' strings
    + *     should be appended.
    + * @private
    + */
    +goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
    +  if (goog.isArray(value)) {
    +    // Convince the compiler it's an array.
    +    goog.asserts.assertArray(value);
    +    for (var j = 0; j < value.length; j++) {
    +      // Convert to string explicitly, to short circuit the null and array
    +      // logic in this function -- this ensures that null and undefined get
    +      // written as literal 'null' and 'undefined', and arrays don't get
    +      // expanded out but instead encoded in the default way.
    +      goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
    +    }
    +  } else if (value != null) {
    +    // Skip a top-level null or undefined entirely.
    +    pairs.push('&', key,
    +        // Check for empty string. Zero gets encoded into the url as literal
    +        // strings.  For empty string, skip the equal sign, to be consistent
    +        // with UriBuilder.java.
    +        value === '' ? '' : '=',
    +        goog.string.urlEncode(value));
    +  }
    +};
    +
    +
    +/**
    + * Builds a buffer of query data from a sequence of alternating keys and values.
    + *
    + * @param {!Array<string|undefined>} buffer A string buffer to append to.  The
    + *     first element appended will be an '&', and may be replaced by the caller.
    + * @param {!goog.uri.utils.QueryArray|!Arguments} keysAndValues An array with
    + *     alternating keys and values -- see the typedef.
    + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    + * @return {!Array<string|undefined>} The buffer argument.
    + * @private
    + */
    +goog.uri.utils.buildQueryDataBuffer_ = function(
    +    buffer, keysAndValues, opt_startIndex) {
    +  goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0),
    +      0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.');
    +
    +  for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
    +    goog.uri.utils.appendKeyValuePairs_(
    +        keysAndValues[i], keysAndValues[i + 1], buffer);
    +  }
    +
    +  return buffer;
    +};
    +
    +
    +/**
    + * Builds a query data string from a sequence of alternating keys and values.
    + * Currently generates "&key&" for empty args.
    + *
    + * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and
    + *     values.  See the typedef.
    + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
    + * @return {string} The encoded query string, in the form 'a=1&b=2'.
    + */
    +goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
    +  var buffer = goog.uri.utils.buildQueryDataBuffer_(
    +      [], keysAndValues, opt_startIndex);
    +  buffer[0] = ''; // Remove the leading ampersand.
    +  return buffer.join('');
    +};
    +
    +
    +/**
    + * Builds a buffer of query data from a map.
    + *
    + * @param {!Array<string|undefined>} buffer A string buffer to append to.  The
    + *     first element appended will be an '&', and may be replaced by the caller.
    + * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
    + *     are URI-encoded parameter keys, and the values conform to the contract
    + *     specified in the goog.uri.utils.QueryValue typedef.
    + * @return {!Array<string|undefined>} The buffer argument.
    + * @private
    + */
    +goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) {
    +  for (var key in map) {
    +    goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer);
    +  }
    +
    +  return buffer;
    +};
    +
    +
    +/**
    + * Builds a query data string from a map.
    + * Currently generates "&key&" for empty args.
    + *
    + * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
    + *     are URI-encoded parameter keys, and the values are arbitrary types
    + *     or arrays. Keys with a null value are dropped.
    + * @return {string} The encoded query string, in the form 'a=1&b=2'.
    + */
    +goog.uri.utils.buildQueryDataFromMap = function(map) {
    +  var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map);
    +  buffer[0] = '';
    +  return buffer.join('');
    +};
    +
    +
    +/**
    + * Appends URI parameters to an existing URI.
    + *
    + * The variable arguments may contain alternating keys and values.  Keys are
    + * assumed to be already URI encoded.  The values should not be URI-encoded,
    + * and will instead be encoded by this function.
    + * <pre>
    + * appendParams('http://www.foo.com?existing=true',
    + *     'key1', 'value1',
    + *     'key2', 'value?willBeEncoded',
    + *     'key3', ['valueA', 'valueB', 'valueC'],
    + *     'key4', null);
    + * result: 'http://www.foo.com?existing=true&' +
    + *     'key1=value1&' +
    + *     'key2=value%3FwillBeEncoded&' +
    + *     'key3=valueA&key3=valueB&key3=valueC'
    + * </pre>
    + *
    + * A single call to this function will not exhibit quadratic behavior in IE,
    + * whereas multiple repeated calls may, although the effect is limited by
    + * fact that URL's generally can't exceed 2kb.
    + *
    + * @param {string} uri The original URI, which may already have query data.
    + * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args
    + *     An array or argument list conforming to goog.uri.utils.QueryArray.
    + * @return {string} The URI with all query parameters added.
    + */
    +goog.uri.utils.appendParams = function(uri, var_args) {
    +  return goog.uri.utils.appendQueryData_(
    +      arguments.length == 2 ?
    +      goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) :
    +      goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1));
    +};
    +
    +
    +/**
    + * Appends query parameters from a map.
    + *
    + * @param {string} uri The original URI, which may already have query data.
    + * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are
    + *     URI-encoded parameter keys, and the values are arbitrary types or arrays.
    + *     Keys with a null value are dropped.
    + * @return {string} The new parameters.
    + */
    +goog.uri.utils.appendParamsFromMap = function(uri, map) {
    +  return goog.uri.utils.appendQueryData_(
    +      goog.uri.utils.buildQueryDataBufferFromMap_([uri], map));
    +};
    +
    +
    +/**
    + * Appends a single URI parameter.
    + *
    + * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
    + * way string append works, though it should be limited given the 2kb limit.
    + *
    + * @param {string} uri The original URI, which may already have query data.
    + * @param {string} key The key, which must already be URI encoded.
    + * @param {*=} opt_value The value, which will be stringized and encoded
    + *     (assumed not already to be encoded).  If omitted, undefined, or null, the
    + *     key will be added as a valueless parameter.
    + * @return {string} The URI with the query parameter added.
    + */
    +goog.uri.utils.appendParam = function(uri, key, opt_value) {
    +  var paramArr = [uri, '&', key];
    +  if (goog.isDefAndNotNull(opt_value)) {
    +    paramArr.push('=', goog.string.urlEncode(opt_value));
    +  }
    +  return goog.uri.utils.appendQueryData_(paramArr);
    +};
    +
    +
    +/**
    + * Finds the next instance of a query parameter with the specified name.
    + *
    + * Does not instantiate any objects.
    + *
    + * @param {string} uri The URI to search.  May contain a fragment identifier
    + *     if opt_hashIndex is specified.
    + * @param {number} startIndex The index to begin searching for the key at.  A
    + *     match may be found even if this is one character after the ampersand.
    + * @param {string} keyEncoded The URI-encoded key.
    + * @param {number} hashOrEndIndex Index to stop looking at.  If a hash
    + *     mark is present, it should be its index, otherwise it should be the
    + *     length of the string.
    + * @return {number} The position of the first character in the key's name,
    + *     immediately after either a question mark or a dot.
    + * @private
    + */
    +goog.uri.utils.findParam_ = function(
    +    uri, startIndex, keyEncoded, hashOrEndIndex) {
    +  var index = startIndex;
    +  var keyLength = keyEncoded.length;
    +
    +  // Search for the key itself and post-filter for surronuding punctuation,
    +  // rather than expensively building a regexp.
    +  while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
    +      index < hashOrEndIndex) {
    +    var precedingChar = uri.charCodeAt(index - 1);
    +    // Ensure that the preceding character is '&' or '?'.
    +    if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    +        precedingChar == goog.uri.utils.CharCode_.QUESTION) {
    +      // Ensure the following character is '&', '=', '#', or NaN
    +      // (end of string).
    +      var followingChar = uri.charCodeAt(index + keyLength);
    +      if (!followingChar ||
    +          followingChar == goog.uri.utils.CharCode_.EQUAL ||
    +          followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
    +          followingChar == goog.uri.utils.CharCode_.HASH) {
    +        return index;
    +      }
    +    }
    +    index += keyLength + 1;
    +  }
    +
    +  return -1;
    +};
    +
    +
    +/**
    + * Regular expression for finding a hash mark or end of string.
    + * @type {RegExp}
    + * @private
    + */
    +goog.uri.utils.hashOrEndRe_ = /#|$/;
    +
    +
    +/**
    + * Determines if the URI contains a specific key.
    + *
    + * Performs no object instantiations.
    + *
    + * @param {string} uri The URI to process.  May contain a fragment
    + *     identifier.
    + * @param {string} keyEncoded The URI-encoded key.  Case-sensitive.
    + * @return {boolean} Whether the key is present.
    + */
    +goog.uri.utils.hasParam = function(uri, keyEncoded) {
    +  return goog.uri.utils.findParam_(uri, 0, keyEncoded,
    +      uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
    +};
    +
    +
    +/**
    + * Gets the first value of a query parameter.
    + * @param {string} uri The URI to process.  May contain a fragment.
    + * @param {string} keyEncoded The URI-encoded key.  Case-sensitive.
    + * @return {?string} The first value of the parameter (URI-decoded), or null
    + *     if the parameter is not found.
    + */
    +goog.uri.utils.getParamValue = function(uri, keyEncoded) {
    +  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    +  var foundIndex = goog.uri.utils.findParam_(
    +      uri, 0, keyEncoded, hashOrEndIndex);
    +
    +  if (foundIndex < 0) {
    +    return null;
    +  } else {
    +    var endPosition = uri.indexOf('&', foundIndex);
    +    if (endPosition < 0 || endPosition > hashOrEndIndex) {
    +      endPosition = hashOrEndIndex;
    +    }
    +    // Progress forth to the end of the "key=" or "key&" substring.
    +    foundIndex += keyEncoded.length + 1;
    +    // Use substr, because it (unlike substring) will return empty string
    +    // if foundIndex > endPosition.
    +    return goog.string.urlDecode(
    +        uri.substr(foundIndex, endPosition - foundIndex));
    +  }
    +};
    +
    +
    +/**
    + * Gets all values of a query parameter.
    + * @param {string} uri The URI to process.  May contain a framgnet.
    + * @param {string} keyEncoded The URI-encoded key.  Case-snsitive.
    + * @return {!Array<string>} All URI-decoded values with the given key.
    + *     If the key is not found, this will have length 0, but never be null.
    + */
    +goog.uri.utils.getParamValues = function(uri, keyEncoded) {
    +  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    +  var position = 0;
    +  var foundIndex;
    +  var result = [];
    +
    +  while ((foundIndex = goog.uri.utils.findParam_(
    +      uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    +    // Find where this parameter ends, either the '&' or the end of the
    +    // query parameters.
    +    position = uri.indexOf('&', foundIndex);
    +    if (position < 0 || position > hashOrEndIndex) {
    +      position = hashOrEndIndex;
    +    }
    +
    +    // Progress forth to the end of the "key=" or "key&" substring.
    +    foundIndex += keyEncoded.length + 1;
    +    // Use substr, because it (unlike substring) will return empty string
    +    // if foundIndex > position.
    +    result.push(goog.string.urlDecode(uri.substr(
    +        foundIndex, position - foundIndex)));
    +  }
    +
    +  return result;
    +};
    +
    +
    +/**
    + * Regexp to find trailing question marks and ampersands.
    + * @type {RegExp}
    + * @private
    + */
    +goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
    +
    +
    +/**
    + * Removes all instances of a query parameter.
    + * @param {string} uri The URI to process.  Must not contain a fragment.
    + * @param {string} keyEncoded The URI-encoded key.
    + * @return {string} The URI with all instances of the parameter removed.
    + */
    +goog.uri.utils.removeParam = function(uri, keyEncoded) {
    +  var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
    +  var position = 0;
    +  var foundIndex;
    +  var buffer = [];
    +
    +  // Look for a query parameter.
    +  while ((foundIndex = goog.uri.utils.findParam_(
    +      uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
    +    // Get the portion of the query string up to, but not including, the ?
    +    // or & starting the parameter.
    +    buffer.push(uri.substring(position, foundIndex));
    +    // Progress to immediately after the '&'.  If not found, go to the end.
    +    // Avoid including the hash mark.
    +    position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex,
    +        hashOrEndIndex);
    +  }
    +
    +  // Append everything that is remaining.
    +  buffer.push(uri.substr(position));
    +
    +  // Join the buffer, and remove trailing punctuation that remains.
    +  return buffer.join('').replace(
    +      goog.uri.utils.trailingQueryPunctuationRe_, '$1');
    +};
    +
    +
    +/**
    + * Replaces all existing definitions of a parameter with a single definition.
    + *
    + * Repeated calls to this can exhibit quadratic behavior due to the need to
    + * find existing instances and reconstruct the string, though it should be
    + * limited given the 2kb limit.  Consider using appendParams to append multiple
    + * parameters in bulk.
    + *
    + * @param {string} uri The original URI, which may already have query data.
    + * @param {string} keyEncoded The key, which must already be URI encoded.
    + * @param {*} value The value, which will be stringized and encoded (assumed
    + *     not already to be encoded).
    + * @return {string} The URI with the query parameter added.
    + */
    +goog.uri.utils.setParam = function(uri, keyEncoded, value) {
    +  return goog.uri.utils.appendParam(
    +      goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
    +};
    +
    +
    +/**
    + * Generates a URI path using a given URI and a path with checks to
    + * prevent consecutive "//". The baseUri passed in must not contain
    + * query or fragment identifiers. The path to append may not contain query or
    + * fragment identifiers.
    + *
    + * @param {string} baseUri URI to use as the base.
    + * @param {string} path Path to append.
    + * @return {string} Updated URI.
    + */
    +goog.uri.utils.appendPath = function(baseUri, path) {
    +  goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
    +
    +  // Remove any trailing '/'
    +  if (goog.string.endsWith(baseUri, '/')) {
    +    baseUri = baseUri.substr(0, baseUri.length - 1);
    +  }
    +  // Remove any leading '/'
    +  if (goog.string.startsWith(path, '/')) {
    +    path = path.substr(1);
    +  }
    +  return goog.string.buildString(baseUri, '/', path);
    +};
    +
    +
    +/**
    + * Replaces the path.
    + * @param {string} uri URI to use as the base.
    + * @param {string} path New path.
    + * @return {string} Updated URI.
    + */
    +goog.uri.utils.setPath = function(uri, path) {
    +  // Add any missing '/'.
    +  if (!goog.string.startsWith(path, '/')) {
    +    path = '/' + path;
    +  }
    +  var parts = goog.uri.utils.split(uri);
    +  return goog.uri.utils.buildFromEncodedParts(
    +      parts[goog.uri.utils.ComponentIndex.SCHEME],
    +      parts[goog.uri.utils.ComponentIndex.USER_INFO],
    +      parts[goog.uri.utils.ComponentIndex.DOMAIN],
    +      parts[goog.uri.utils.ComponentIndex.PORT],
    +      path,
    +      parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
    +      parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
    +};
    +
    +
    +/**
    + * Standard supported query parameters.
    + * @enum {string}
    + */
    +goog.uri.utils.StandardQueryParam = {
    +
    +  /** Unused parameter for unique-ifying. */
    +  RANDOM: 'zx'
    +};
    +
    +
    +/**
    + * Sets the zx parameter of a URI to a random value.
    + * @param {string} uri Any URI.
    + * @return {string} That URI with the "zx" parameter added or replaced to
    + *     contain a random string.
    + */
    +goog.uri.utils.makeUnique = function(uri) {
    +  return goog.uri.utils.setParam(uri,
    +      goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/utils_test.html b/mapbox-gl/closure-library/closure/goog/uri/utils_test.html
    new file mode 100644
    index 0000000000..f11b40df8f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/utils_test.html
    @@ -0,0 +1,28 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  Most of these tests were stolen from the original goog.Uri tests.
    +
    +  Author: gboyer@google.com (Garrett Boyer)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.uri.utils
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.uri.utilsTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/uri/utils_test.js b/mapbox-gl/closure-library/closure/goog/uri/utils_test.js
    new file mode 100644
    index 0000000000..c46be3d471
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/uri/utils_test.js
    @@ -0,0 +1,601 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.uri.utilsTest');
    +goog.setTestOnly('goog.uri.utilsTest');
    +
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.uri.utils');
    +
    +var utils = goog.uri.utils;
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +
    +function setUpPage() {
    +  goog.string.getRandomString = goog.functions.constant('RANDOM');
    +}
    +
    +function tearDown() {
    +  stubs.reset();
    +}
    +
    +function testSplit() {
    +  var uri = 'http://www.google.com:80/path%20path+path?q=query&hl=en#fragment';
    +  assertEquals('http', utils.getScheme(uri));
    +  assertNull(utils.getUserInfoEncoded(uri));
    +  assertNull(utils.getUserInfo(uri));
    +  assertEquals('www.google.com', utils.getDomainEncoded(uri));
    +  assertEquals('www.google.com', utils.getDomain(uri));
    +  assertEquals(80, utils.getPort(uri));
    +  assertEquals('/path%20path+path', utils.getPathEncoded(uri));
    +  assertEquals('/path path+path', utils.getPath(uri));
    +  assertEquals('q=query&hl=en', utils.getQueryData(uri));
    +  assertEquals('fragment', utils.getFragmentEncoded(uri));
    +  assertEquals('fragment', utils.getFragment(uri));
    +
    +  assertEquals(utils.getDomain('http://[2607:f8b0:4006:802::1006]'),
    +      '[2607:f8b0:4006:802::1006]');
    +  assertEquals(utils.getDomain('http://[2607:f8b0:4006:802::1006]:80'),
    +      '[2607:f8b0:4006:802::1006]');
    +  assertEquals(utils.getPort('http://[2607:f8b0:4006:802::1006]:80'), 80);
    +  assertEquals(utils.getDomain('http://[2607]:80/?q=]'), '[2607]');
    +  assertEquals(utils.getDomain('http://!!!'), '!!!');
    +  assertNull(utils.getPath('http://!!!'));
    +  assertNull(utils.getScheme('www.x.com:80'));
    +  assertEquals('Query data with no fragment identifier', 'foo=bar&baz=bin',
    +      utils.getQueryData('http://google.com?foo=bar&baz=bin'));
    +}
    +
    +function testMailtoUri() {
    +  var uri = 'mailto:joe+random@hominid.com';
    +  assertNull(utils.getDomain(uri));
    +  assertEquals('mailto', utils.getScheme(uri));
    +  assertEquals('joe+random@hominid.com', utils.getPath(uri));
    +}
    +
    +function testSplitRelativeUri() {
    +  var uri = '/path%20path+path?q=query&hl=en#fragment';
    +  assertNull(utils.getScheme(uri));
    +  assertNull(utils.getDomain(uri));
    +  assertNull(utils.getDomainEncoded(uri));
    +  assertNull(utils.getPort(uri));
    +  assertEquals('/path%20path+path', utils.getPathEncoded(uri));
    +  assertEquals('/path path+path', utils.getPath(uri));
    +  assertEquals('q=query&hl=en', utils.getQueryData(uri));
    +  assertEquals('fragment', utils.getFragmentEncoded(uri));
    +  assertEquals('fragment', utils.getFragment(uri));
    +}
    +
    +function testSplitBadAuthority() {
    +  // This URL has a syntax error per the RFC (port number must be digits, and
    +  // host cannot contain a colon except in [...]). This test is solely to
    +  // 'document' the current behavior, which may affect application handling
    +  // of erroneous URLs.
    +  assertEquals(utils.getDomain('http://host:port/'), 'host:port');
    +  assertNull(utils.getPort('http://host:port/'));
    +}
    +
    +function testSplitIntoHostAndPath() {
    +  // Splitting into host and path takes care of one of the major use cases
    +  // of resolve, without implementing a generic algorithm that undoubtedly
    +  // requires a huge footprint.
    +  var uri = 'http://www.google.com:80/path%20path+path?q=query&hl=en#fragment';
    +  assertEquals('http://www.google.com:80',
    +      goog.uri.utils.getHost(uri));
    +  assertEquals('/path%20path+path?q=query&hl=en#fragment',
    +      goog.uri.utils.getPathAndAfter(uri));
    +
    +  var uri2 = 'http://www.google.com/calendar';
    +  assertEquals('should handle missing fields', 'http://www.google.com',
    +      goog.uri.utils.getHost(uri2));
    +  assertEquals('should handle missing fields', '/calendar',
    +      goog.uri.utils.getPathAndAfter(uri2));
    +}
    +
    +
    +function testRelativeUrisHaveNoPath() {
    +  assertNull(utils.getPathEncoded('?hello'));
    +}
    +
    +
    +function testReservedCharacters() {
    +  var o = '%6F';
    +  var uri = 'http://www.g' + o + 'ogle.com%40/xxx%2feee/ccc';
    +  assertEquals('Should not decode reserved characters in path',
    +      '/xxx%2feee/ccc', goog.uri.utils.getPath(uri));
    +  assertEquals('Should not decode reserved characters in domain',
    +      'www.google.com%40', goog.uri.utils.getDomain(uri));
    +}
    +
    +function testSetFragmentEncoded() {
    +  var expected = 'http://www.google.com/path#bar';
    +  assertEquals(expected,
    +      utils.setFragmentEncoded('http://www.google.com/path#foo', 'bar'));
    +
    +  assertEquals(expected,
    +      utils.setFragmentEncoded('http://www.google.com/path', 'bar'));
    +
    +  assertEquals('http://www.google.com/path',
    +      utils.setFragmentEncoded('http://www.google.com/path', ''));
    +
    +  assertEquals('http://www.google.com/path',
    +      utils.setFragmentEncoded('http://www.google.com/path', null));
    +}
    +
    +
    +function testGetParamValue() {
    +  assertEquals('v1',
    +      utils.getParamValue('/path?key=v1&c=d&keywithsuffix=v3&key=v2', 'key'));
    +
    +  assertEquals('v1',
    +      utils.getParamValue('/path?kEY=v1&c=d&keywithsuffix=v3&key=v2', 'kEY'));
    +}
    +
    +
    +function testGetParamValues() {
    +  assertArrayEquals('should ignore confusing suffixes', ['v1', 'v2'],
    +      utils.getParamValues(
    +          '/path?a=b&key=v1&c=d&key=v2&keywithsuffix=v3', 'key'));
    +  assertArrayEquals('should be case sensitive', ['v2'],
    +      utils.getParamValues('/path?a=b&keY=v1&c=d&KEy=v2&keywithsuffix=v3',
    +          'KEy'));
    +  assertArrayEquals('should work for the first parameter', ['v1', 'v2'],
    +      utils.getParamValues('/path?key=v1&c=d&key=v2&keywithsuffix=v3', 'key'));
    +  assertArrayEquals('should work for the last parameter', ['v1', 'v2'],
    +      utils.getParamValues('/path?key=v1&c=d&keywithsuffix=v3&key=v2', 'key'));
    +  assertArrayEquals(['1'],
    +      utils.getParamValues('http://foo.com?q=1#?q=2&q=3', 'q'));
    +}
    +
    +
    +function testGetParamValueAllowsEqualInValues() {
    +  assertEquals('equals signs can appear unencoded', 'v1=v2',
    +      utils.getParamValue('/path?key=v1=v2', 'key'));
    +  assertArrayEquals(['v1=v2=v3'],
    +      utils.getParamValues('/path?key=v1=v2=v3', 'key'));
    +}
    +
    +
    +function testGetParamValueNoSuchKey() {
    +  var uri = '/path?key=v1&c=d&keywithsuffix=v3&key=v2';
    +  assertNull(utils.getParamValue(uri, 'nosuchkey'));
    +  assertArrayEquals([], utils.getParamValues(uri, 'nosuchkey'));
    +  assertFalse(utils.hasParam(uri, 'nosuchkey'));
    +  assertNull(utils.getParamValue('q=1', 'q'));
    +  assertEquals('1', utils.getParamValue('?q=1', 'q'));
    +}
    +
    +
    +function testGetParamValueEmptyAndMissingValueStrings() {
    +  assertEquals('', utils.getParamValue('/path?key&bar', 'key'));
    +  assertEquals('', utils.getParamValue('/path?foo=bar&key', 'key'));
    +  assertEquals('', utils.getParamValue('/path?key', 'key'));
    +  assertEquals('', utils.getParamValue('/path?key=', 'key'));
    +  assertArrayEquals([''], utils.getParamValues('/path?key', 'key'));
    +  assertArrayEquals([''], utils.getParamValues('/path?key&bar', 'key'));
    +  assertArrayEquals([''], utils.getParamValues('/path?foo=bar&key', 'key'));
    +  assertArrayEquals([''], utils.getParamValues('/path?foo=bar&key=', 'key'));
    +  assertArrayEquals(['', '', '', 'j', ''],
    +      utils.getParamValues('/path?key&key&key=&key=j&key', 'key'));
    +  assertArrayEquals(['', '', '', '', ''],
    +      utils.getParamValues('/pathqqq?q&qq&q&q=&q&q', 'q'));
    +  assertTrue(utils.hasParam('/path?key=', 'key'));
    +}
    +
    +
    +function testGetParamValueDecoding() {
    +  assertEquals('plus should be supported as alias of space', 'foo bar baz',
    +      utils.getParamValue('/path?key=foo+bar%20baz', 'key'));
    +  assertArrayEquals(['foo bar baz'],
    +      utils.getParamValues('/path?key=foo%20bar%20baz', 'key'));
    +}
    +
    +
    +function testGetParamIgnoresParamsInFragmentIdentifiers() {
    +  assertFalse(utils.hasParam('/path?bah#a&key=foo', 'key'));
    +  assertEquals(null, utils.getParamValue('/path?bah#a&key=bar', 'key'));
    +  assertArrayEquals([], utils.getParamValues('/path?bah#a&key=bar', 'key'));
    +}
    +
    +
    +function testGetParamIgnoresExcludesFragmentFromParameterValue() {
    +  // Make sure the '#' doesn't get included anywhere, for parameter values
    +  // of different lengths.
    +  assertEquals('foo',
    +      utils.getParamValue('/path?key=foo#key=bar&key=baz', 'key'));
    +  assertArrayEquals(['foo'],
    +      utils.getParamValues('/path?key=foo#key=bar&key=baz', 'key'));
    +  assertEquals('',
    +      utils.getParamValue('/path?key#key=bar&key=baz', 'key'));
    +  assertArrayEquals([''],
    +      utils.getParamValues('/path?key#key=bar&key=baz', 'key'));
    +  assertEquals('x',
    +      utils.getParamValue('/path?key=x#key=bar&key=baz', 'key'));
    +  assertArrayEquals(['x'],
    +      utils.getParamValues('/path?key=x#key=bar&key=baz', 'key'));
    +
    +  // Simply make sure hasParam doesn't die in this case.
    +  assertTrue(utils.hasParam('/path?key=foo#key=bar&key=baz', 'key'));
    +  assertTrue(utils.hasParam('/path?key=foo#key&key=baz', 'key'));
    +}
    +
    +
    +function testSameDomainPathsDiffer() {
    +  var uri1 = 'http://www.google.com/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertTrue(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertTrue(goog.uri.utils.haveSameDomain(uri2, uri1));
    +}
    +
    +
    +function testSameDomainSchemesDiffer() {
    +  var uri1 = 'http://www.google.com';
    +  var uri2 = 'https://www.google.com';
    +  assertFalse(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.uri.utils.haveSameDomain(uri2, uri1));
    +}
    +
    +
    +function testSameDomainPortsDiffer() {
    +  var uri1 = 'http://www.google.com:1234/a';
    +  var uri2 = 'http://www.google.com/b';
    +  var uri3 = 'http://www.google.com:2345/b';
    +  assertFalse(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.uri.utils.haveSameDomain(uri2, uri1));
    +  assertFalse(goog.uri.utils.haveSameDomain(uri1, uri3));
    +}
    +
    +
    +function testSameDomainDomainsDiffer() {
    +  var uri1 = '/a';
    +  var uri2 = 'http://www.google.com/b';
    +  assertFalse(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.uri.utils.haveSameDomain(uri2, uri1));
    +}
    +
    +
    +function testSameDomainSubDomainDiffers() {
    +  var uri1 = 'http://www.google.com/a';
    +  var uri2 = 'http://mail.google.com/b';
    +  assertFalse(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertFalse(goog.uri.utils.haveSameDomain(uri2, uri1));
    +}
    +
    +
    +function testSameDomainNoDomain() {
    +  var uri1 = '/a';
    +  var uri2 = '/b';
    +  assertTrue(goog.uri.utils.haveSameDomain(uri1, uri2));
    +  assertTrue(goog.uri.utils.haveSameDomain(uri2, uri1));
    +}
    +
    +
    +
    +/**
    + * Simple class with a constant toString.
    + * @param {string} stringValue The result of toString.
    + * @constructor
    + */
    +function HasString(stringValue) {
    +  this.value_ = stringValue;
    +}
    +
    +
    +/** @override */
    +HasString.prototype.toString = function() {
    +  return this.value_;
    +};
    +
    +
    +function testBuildFromEncodedParts() {
    +  assertEquals('should handle full URL',
    +      'http://foo@www.google.com:80/path?q=query#fragment',
    +      utils.buildFromEncodedParts('http', 'foo', 'www.google.com',
    +          80, '/path', 'q=query', 'fragment'));
    +  assertEquals('should handle unspecified parameters', '/search',
    +      utils.buildFromEncodedParts(null, null, undefined, null, '/search'));
    +  assertEquals('should handle params of non-primitive types',
    +      'http://foo@www.google.com:80/path?q=query#fragment',
    +      utils.buildFromEncodedParts(new HasString('http'), new HasString('foo'),
    +          new HasString('www.google.com'), new HasString('80'),
    +          new HasString('/path'), new HasString('q=query'),
    +          new HasString('fragment')));
    +}
    +
    +
    +function testAppendParam() {
    +  assertEquals('http://foo.com?q=1',
    +      utils.appendParam('http://foo.com', 'q', 1));
    +  assertEquals('http://foo.com?q=1#preserve',
    +      utils.appendParam('http://foo.com#preserve', 'q', 1));
    +  assertEquals('should tolerate a lone question mark',
    +      'http://foo.com?q=1',
    +      utils.appendParam('http://foo.com?', 'q', 1));
    +  assertEquals('http://foo.com?q=1&r=2',
    +      utils.appendParam('http://foo.com?q=1', 'r', 2));
    +  assertEquals('http://foo.com?q=1&r=2&s=3#preserve',
    +      utils.appendParam('http://foo.com?q=1&r=2#preserve', 's', 3));
    +  assertEquals('q=1&r=2&s=3&s=4',
    +      utils.buildQueryData(['q', 1, 'r', 2, 's', [3, 4]]));
    +  assertEquals('',
    +      utils.buildQueryData([]));
    +  assertEquals('?q=1#preserve',
    +      utils.appendParam('#preserve', 'q', 1));
    +}
    +
    +function testAppendParams() {
    +  assertEquals('http://foo.com',
    +      utils.appendParams('http://foo.com'));
    +  assertEquals('http://foo.com?q=1&r=2&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com#preserve',
    +          'q', 1, 'r', 2, 's', [3, 4]));
    +  assertEquals('http://foo.com?a=1&q=1&r=2&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com?a=1#preserve',
    +          'q', 1, 'r', 2, 's', [3, 4]));
    +  assertEquals('http://foo.com?q=1&r=2&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com?#preserve',
    +          'q', 1, 'r', 2, 's', [3, 4]));
    +  assertEquals('?q=1&r=2&s=3&s=4#preserve',
    +      utils.appendParams('#preserve',
    +          'q', 1, 'r', 2, 's', [3, 4]));
    +  assertEquals('A question mark must not be appended if there are no ' +
    +      'parameters, otherwise repeated appends will be broken.',
    +      'http://foo.com#test', utils.appendParams('http://foo.com#test'));
    +  assertEquals('should handle objects with to-string',
    +      'http://foo.com?q=a&r=b',
    +      utils.appendParams('http://foo.com',
    +          'q', new HasString('a'), 'r', [new HasString('b')]));
    +
    +  assertThrows('appendParams should fail with an odd number of arguments.',
    +      function() {
    +        utils.appendParams('http://foo.com', 'a', 1, 'b');
    +      });
    +}
    +
    +
    +function testValuelessParam() {
    +  assertEquals('http://foo.com?q',
    +      utils.appendParam('http://foo.com', 'q'));
    +  assertEquals('http://foo.com?q',
    +      utils.appendParam('http://foo.com', 'q', null /* opt_value */));
    +  assertEquals('http://foo.com?q#preserve',
    +      utils.appendParam('http://foo.com#preserve', 'q'));
    +  assertEquals('should tolerate a lone question mark',
    +      'http://foo.com?q',
    +      utils.appendParam('http://foo.com?', 'q'));
    +  assertEquals('http://foo.com?q=1&r',
    +      utils.appendParam('http://foo.com?q=1', 'r'));
    +  assertEquals('http://foo.com?q=1&r=2&s#preserve',
    +      utils.appendParam('http://foo.com?q=1&r=2#preserve', 's'));
    +  assertTrue(utils.hasParam('http://foo.com?q=1&r=2&s#preserve', 's'));
    +}
    +
    +
    +function testAppendParamsAsArray() {
    +  assertEquals('http://foo.com?q=1&r=2&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com#preserve',
    +          ['q', 1, 'r', 2, 's', [3, 4]]));
    +  assertEquals('http://foo.com?q=1&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com#preserve',
    +          ['q', 1, 'r', null, 's', [3, 4]]));
    +  assertEquals('http://foo.com?q=1&s=3&s=4#preserve',
    +      utils.appendParams('http://foo.com#preserve',
    +          ['q', 1, 'r', undefined, 's', [3, 4]]));
    +  assertEquals('http://foo.com?q=1&r=2&s=3&s=4&s=null&s=undefined#preserve',
    +      utils.appendParams('http://foo.com#preserve',
    +          ['q', 1, 'r', 2, 's', [3, new HasString('4'), null, undefined]]));
    +}
    +
    +
    +function testAppendParamEscapes() {
    +  assertEquals('http://foo.com?h=a%20b',
    +      utils.appendParams('http://foo.com', 'h', 'a b'));
    +  assertEquals('h=a%20b', utils.buildQueryData(['h', 'a b']));
    +  assertEquals('h=a%20b', utils.buildQueryDataFromMap({'h': 'a b'}));
    +}
    +
    +
    +function testAppendParamsFromMap() {
    +  var uri = utils.appendParamsFromMap('http://www.foo.com',
    +      {'a': 1, 'b': 'bob', 'c': [1, 2, new HasString('3')]});
    +  assertArrayEquals(['1'], utils.getParamValues(uri, 'a'));
    +  assertArrayEquals(['bob'], utils.getParamValues(uri, 'b'));
    +  assertArrayEquals(['1', '2', '3'], utils.getParamValues(uri, 'c'));
    +}
    +
    +function testBuildQueryDataFromMap() {
    +  assertEquals('a=1', utils.buildQueryDataFromMap({'a': 1}));
    +  var uri = 'foo.com?' + utils.buildQueryDataFromMap(
    +      {'a': 1, 'b': 'bob', 'c': [1, 2, new HasString('3')]});
    +  assertArrayEquals(['1'], utils.getParamValues(uri, 'a'));
    +  assertArrayEquals(['bob'], utils.getParamValues(uri, 'b'));
    +  assertArrayEquals(['1', '2', '3'], utils.getParamValues(uri, 'c'));
    +}
    +
    +
    +function testMultiParamSkipsNullParams() {
    +  // For the multi-param functions, null and undefined keys should be
    +  // skipped, but null within a parameter array should still be appended.
    +  assertEquals('buildQueryDataFromMap', 'a=null',
    +      utils.buildQueryDataFromMap({'a': [null], 'b': null, 'c': undefined}));
    +  assertEquals('buildQueryData', 'a=null',
    +      utils.buildQueryData(['a', [null], 'b', null, 'c', undefined]));
    +  assertEquals('appendParams', 'foo.com?a=null',
    +      utils.appendParams('foo.com', 'a', [null], 'b', null, 'c', undefined));
    +  assertEquals('empty strings should NOT be skipped', 'foo.com?a&b',
    +      utils.appendParams('foo.com', 'a', [''], 'b', ''));
    +}
    +
    +
    +function testRemoveParam() {
    +  assertEquals('remove middle', 'http://foo.com?q=1&s=3',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3', 'r'));
    +  assertEquals('remove first', 'http://foo.com?r=2&s=3',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3', 'q'));
    +  assertEquals('remove last', 'http://foo.com?q=1&r=2',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3', 's'));
    +  assertEquals('remove only param', 'http://foo.com',
    +      utils.removeParam('http://foo.com?q=1', 'q'));
    +}
    +
    +
    +function testRemoveParamWithFragment() {
    +  assertEquals('remove middle', 'http://foo.com?q=1&s=3#?r=1&r=1',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3#?r=1&r=1', 'r'));
    +  assertEquals('remove first', 'http://foo.com?r=2&s=3#?q=1&q=1',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3#?q=1&q=1', 'q'));
    +  assertEquals('remove only param', 'http://foo.com#?q=1&q=1',
    +      utils.removeParam('http://foo.com?q=1#?q=1&q=1', 'q'));
    +  assertEquals('remove last', 'http://foo.com?q=1&r=2#?s=1&s=1',
    +      utils.removeParam('http://foo.com?q=1&r=2&s=3#?s=1&s=1', 's'));
    +}
    +
    +
    +function testRemoveNonExistent() {
    +  assertEquals('remove key not present', 'http://foo.com?q=1',
    +      utils.removeParam('http://foo.com?q=1', 'nosuchkey'));
    +  assertEquals('remove key not present', 'http://foo.com#q=1',
    +      utils.removeParam('http://foo.com#q=1', 'q'));
    +  assertEquals('remove key from empty string', '',
    +      utils.removeParam('', 'nosuchkey'));
    +}
    +
    +
    +function testRemoveMultiple() {
    +  assertEquals('remove four of the same', 'http://foo.com',
    +      utils.removeParam('http://foo.com?q=1&q=2&q=3&q=4', 'q'));
    +  assertEquals('remove four of the same with another one in the middle',
    +      'http://foo.com?a=99',
    +      utils.removeParam('http://foo.com?q=1&q=2&a=99&q=3&q=4', 'q'));
    +}
    +
    +
    +function testSetParam() {
    +  assertEquals('middle, no fragment', 'http://foo.com?q=1&s=3&r=999',
    +      utils.setParam('http://foo.com?q=1&r=2&s=3', 'r', 999));
    +  assertEquals('middle', 'http://foo.com?q=1&s=3&r=999#?r=1&r=1',
    +      utils.setParam('http://foo.com?q=1&r=2&s=3#?r=1&r=1', 'r', 999));
    +  assertEquals('first', 'http://foo.com?r=2&s=3&q=999#?q=1&q=1',
    +      utils.setParam('http://foo.com?q=1&r=2&s=3#?q=1&q=1', 'q', 999));
    +  assertEquals('only param', 'http://foo.com?q=999#?q=1&q=1',
    +      utils.setParam('http://foo.com?q=1#?q=1&q=1', 'q', 999));
    +  assertEquals('last', 'http://foo.com?q=1&r=2&s=999#?s=1&s=1',
    +      utils.setParam('http://foo.com?q=1&r=2&s=3#?s=1&s=1', 's', 999));
    +  assertEquals('multiple', 'http://foo.com?s=999#?s=1&s=1',
    +      utils.setParam('http://foo.com?s=1&s=2&s=3#?s=1&s=1', 's', 999));
    +  assertEquals('none', 'http://foo.com?r=1&s=999#?s=1&s=1',
    +      utils.setParam('http://foo.com?r=1#?s=1&s=1', 's', 999));
    +}
    +
    +
    +function testModifyQueryParams() {
    +  var uri = 'http://foo.com?a=A&a=A2&b=B&b=B2&c=C';
    +
    +  uri = utils.appendParam(uri, 'd', 'D');
    +  assertEquals('http://foo.com?a=A&a=A2&b=B&b=B2&c=C&d=D', uri);
    +
    +  uri = utils.removeParam(uri, 'd');
    +  uri = utils.appendParam(uri, 'd', 'D2');
    +  assertEquals('http://foo.com?a=A&a=A2&b=B&b=B2&c=C&d=D2', uri);
    +
    +  uri = utils.removeParam(uri, 'a');
    +  uri = utils.appendParam(uri, 'a', 'A3');
    +  assertEquals('http://foo.com?b=B&b=B2&c=C&d=D2&a=A3', uri);
    +
    +  uri = utils.removeParam(uri, 'a');
    +  uri = utils.appendParam(uri, 'a', 'A4');
    +  assertEquals('A4', utils.getParamValue(uri, 'a'));
    +}
    +
    +
    +function testBrowserEncoding() {
    +  // Sanity check borrowed from old code to ensure that encodeURIComponent
    +  // is good enough.  Entire test should be safe to delete.
    +  var allowedInFragment = /[A-Za-z0-9\-\._~!$&'()*+,;=:@/?]/g;
    +
    +  var sb = [];
    +  for (var i = 33; i < 500; i++) {  // arbitrarily use first 500 chars.
    +    sb.push(String.fromCharCode(i));
    +  }
    +  var testString = sb.join('');
    +
    +  var encodedStr = encodeURIComponent(testString);
    +
    +  // Strip all percent encoded characters, as they're ok.
    +  encodedStr = encodedStr.replace(/%[0-9A-F][0-9A-F]/g, '');
    +
    +  // Remove allowed characters.
    +  encodedStr = encodedStr.replace(allowedInFragment, '');
    +
    +  // Only illegal characters should remain, which is a fail.
    +  assertEquals('String should be empty', 0, encodedStr.length);
    +}
    +
    +
    +function testAppendPath() {
    +  var uri = 'http://www.foo.com';
    +  var expected = uri + '/dummy';
    +  assertEquals('Path has no trailing "/", adding with leading "/" failed',
    +      expected,
    +      goog.uri.utils.appendPath(uri, '/dummy'));
    +  assertEquals('Path has no trailing "/", adding with no leading "/" failed',
    +      expected,
    +      goog.uri.utils.appendPath(uri, 'dummy'));
    +  uri = uri + '/';
    +  assertEquals('Path has trailing "/", adding with leading "/" failed',
    +      expected,
    +      goog.uri.utils.appendPath(uri, '/dummy'));
    +
    +  assertEquals('Path has trailing "/", adding with no leading "/" failed',
    +      expected,
    +      goog.uri.utils.appendPath(uri, 'dummy'));
    +}
    +
    +
    +function testMakeUnique() {
    +  assertEquals('http://www.google.com?zx=RANDOM#blob',
    +      goog.uri.utils.makeUnique('http://www.google.com#blob'));
    +  assertEquals('http://www.google.com?a=1&b=2&zx=RANDOM#blob',
    +      goog.uri.utils.makeUnique('http://www.google.com?zx=9&a=1&b=2#blob'));
    +}
    +
    +
    +function testParseQuery() {
    +  var result = [];
    +  goog.uri.utils.parseQueryData(
    +      'foo=bar&no&empty=&tricky%3D%26=%3D%26&=nothing&=&',
    +      function(name, value) { result.push(name, value); });
    +  assertArrayEquals(
    +      ['foo', 'bar',
    +       'no', '',
    +       'empty', '',
    +       'tricky%3D%26', '=&',
    +       '', 'nothing',
    +       '', '',
    +       '', ''],
    +      result);
    +
    +  // Go thought buildQueryData and parseQueryData and see if we get the same
    +  // result.
    +  var result2 = [];
    +  goog.uri.utils.parseQueryData(
    +      goog.uri.utils.buildQueryData(result),
    +      function(name, value) { result2.push(name, value); });
    +  assertArrayEquals(result, result2);
    +}
    +
    +
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/adobereader.js b/mapbox-gl/closure-library/closure/goog/useragent/adobereader.js
    new file mode 100644
    index 0000000000..72ad22f8ad
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/adobereader.js
    @@ -0,0 +1,90 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detects the Adobe Reader PDF browser plugin.
    + *
    + * @author chrisn@google.com (Chris Nokleberg)
    + * @see ../demos/useragent.html
    + */
    +
    +/** @suppress {extraProvide} */
    +goog.provide('goog.userAgent.adobeReader');
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +(function() {
    +  var version = '';
    +  if (goog.userAgent.IE) {
    +    var detectOnIe = function(classId) {
    +      /** @preserveTry */
    +      try {
    +        new ActiveXObject(classId);
    +        return true;
    +      } catch (ex) {
    +        return false;
    +      }
    +    };
    +    if (detectOnIe('AcroPDF.PDF.1')) {
    +      version = '7';
    +    } else if (detectOnIe('PDF.PdfCtrl.6')) {
    +      version = '6';
    +    }
    +    // TODO(chrisn): Add detection for previous versions if anyone needs them.
    +  } else {
    +    if (navigator.mimeTypes && navigator.mimeTypes.length > 0) {
    +      var mimeType = navigator.mimeTypes['application/pdf'];
    +      if (mimeType && mimeType.enabledPlugin) {
    +        var description = mimeType.enabledPlugin.description;
    +        if (description && description.indexOf('Adobe') != -1) {
    +          // Newer plugins do not include the version in the description, so we
    +          // default to 7.
    +          version = description.indexOf('Version') != -1 ?
    +              description.split('Version')[1] : '7';
    +        }
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Whether we detect the user has the Adobe Reader browser plugin installed.
    +   * @type {boolean}
    +   */
    +  goog.userAgent.adobeReader.HAS_READER = !!version;
    +
    +
    +  /**
    +   * The version of the installed Adobe Reader plugin. Versions after 7
    +   * will all be reported as '7'.
    +   * @type {string}
    +   */
    +  goog.userAgent.adobeReader.VERSION = version;
    +
    +
    +  /**
    +   * On certain combinations of platform/browser/plugin, a print dialog
    +   * can be shown for PDF files without a download dialog or making the
    +   * PDF visible to the user, by loading the PDF into a hidden iframe.
    +   *
    +   * Currently this variable is true if Adobe Reader version 6 or later
    +   * is detected on Windows.
    +   *
    +   * @type {boolean}
    +   */
    +  goog.userAgent.adobeReader.SILENT_PRINT = goog.userAgent.WINDOWS &&
    +      goog.string.compareVersions(version, '6') >= 0;
    +
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.html b/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.html
    new file mode 100644
    index 0000000000..0e29db5ecd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent.adobeReader
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgent.adobeReaderTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.js b/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.js
    new file mode 100644
    index 0000000000..c5b8d7ac55
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/adobereader_test.js
    @@ -0,0 +1,29 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgent.adobeReaderTest');
    +goog.setTestOnly('goog.userAgent.adobeReaderTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent.adobeReader');
    +
    +// For now, just test that the variables exist, the test runner will
    +// pick up any runtime errors.
    +// TODO(chrisn): Mock out each browser implementation and test the code path
    +// correctly detects the version for each case.
    +function testAdobeReader() {
    +  assertNotUndefined(goog.userAgent.adobeReader.HAS_READER);
    +  assertNotUndefined(goog.userAgent.adobeReader.VERSION);
    +  assertNotUndefined(goog.userAgent.adobeReader.SILENT_PRINT);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/flash.js b/mapbox-gl/closure-library/closure/goog/useragent/flash.js
    new file mode 100644
    index 0000000000..69d8f1e094
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/flash.js
    @@ -0,0 +1,156 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Flash detection.
    + * @see ../demos/useragent.html
    + */
    +
    +goog.provide('goog.userAgent.flash');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser doesn't
    + * have flash.
    + */
    +goog.define('goog.userAgent.flash.ASSUME_NO_FLASH', false);
    +
    +
    +/**
    + * Whether we can detect that the browser has flash
    + * @type {boolean}
    + * @private
    + */
    +goog.userAgent.flash.detectedFlash_ = false;
    +
    +
    +/**
    + * Full version information of flash installed, in form 7.0.61
    + * @type {string}
    + * @private
    + */
    +goog.userAgent.flash.detectedFlashVersion_ = '';
    +
    +
    +/**
    + * Initializer for goog.userAgent.flash
    + *
    + * This is a named function so that it can be stripped via the jscompiler if
    + * goog.userAgent.flash.ASSUME_NO_FLASH is true.
    + * @private
    + */
    +goog.userAgent.flash.init_ = function() {
    +  if (navigator.plugins && navigator.plugins.length) {
    +    var plugin = navigator.plugins['Shockwave Flash'];
    +    if (plugin) {
    +      goog.userAgent.flash.detectedFlash_ = true;
    +      if (plugin.description) {
    +        goog.userAgent.flash.detectedFlashVersion_ =
    +            goog.userAgent.flash.getVersion_(plugin.description);
    +      }
    +    }
    +
    +    if (navigator.plugins['Shockwave Flash 2.0']) {
    +      goog.userAgent.flash.detectedFlash_ = true;
    +      goog.userAgent.flash.detectedFlashVersion_ = '2.0.0.11';
    +    }
    +
    +  } else if (navigator.mimeTypes && navigator.mimeTypes.length) {
    +    var mimeType = navigator.mimeTypes['application/x-shockwave-flash'];
    +    goog.userAgent.flash.detectedFlash_ = mimeType && mimeType.enabledPlugin;
    +    if (goog.userAgent.flash.detectedFlash_) {
    +      goog.userAgent.flash.detectedFlashVersion_ =
    +          goog.userAgent.flash.getVersion_(mimeType.enabledPlugin.description);
    +    }
    +
    +  } else {
    +    /** @preserveTry */
    +    try {
    +      // Try 7 first, since we know we can use GetVariable with it
    +      var ax = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7');
    +      goog.userAgent.flash.detectedFlash_ = true;
    +      goog.userAgent.flash.detectedFlashVersion_ =
    +          goog.userAgent.flash.getVersion_(ax.GetVariable('$version'));
    +    } catch (e) {
    +      // Try 6 next, some versions are known to crash with GetVariable calls
    +      /** @preserveTry */
    +      try {
    +        var ax = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
    +        goog.userAgent.flash.detectedFlash_ = true;
    +        // First public version of Flash 6
    +        goog.userAgent.flash.detectedFlashVersion_ = '6.0.21';
    +      } catch (e2) {
    +        /** @preserveTry */
    +        try {
    +          // Try the default activeX
    +          var ax = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
    +          goog.userAgent.flash.detectedFlash_ = true;
    +          goog.userAgent.flash.detectedFlashVersion_ =
    +              goog.userAgent.flash.getVersion_(ax.GetVariable('$version'));
    +        } catch (e3) {
    +          // No flash
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Derived from Apple's suggested sniffer.
    + * @param {string} desc e.g. Shockwave Flash 7.0 r61.
    + * @return {string} 7.0.61.
    + * @private
    + */
    +goog.userAgent.flash.getVersion_ = function(desc) {
    +  var matches = desc.match(/[\d]+/g);
    +  if (!matches) {
    +    return '';
    +  }
    +  matches.length = 3;  // To standardize IE vs FF
    +  return matches.join('.');
    +};
    +
    +
    +if (!goog.userAgent.flash.ASSUME_NO_FLASH) {
    +  goog.userAgent.flash.init_();
    +}
    +
    +
    +/**
    + * Whether we can detect that the browser has flash
    + * @type {boolean}
    + */
    +goog.userAgent.flash.HAS_FLASH = goog.userAgent.flash.detectedFlash_;
    +
    +
    +/**
    + * Full version information of flash installed, in form 7.0.61
    + * @type {string}
    + */
    +goog.userAgent.flash.VERSION = goog.userAgent.flash.detectedFlashVersion_;
    +
    +
    +/**
    + * Whether the installed flash version is as new or newer than a given version.
    + * @param {string} version The version to check.
    + * @return {boolean} Whether the installed flash version is as new or newer
    + *     than a given version.
    + */
    +goog.userAgent.flash.isVersion = function(version) {
    +  return goog.string.compareVersions(goog.userAgent.flash.VERSION,
    +                                     version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/flash_test.html b/mapbox-gl/closure-library/closure/goog/useragent/flash_test.html
    new file mode 100644
    index 0000000000..699559a900
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/flash_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent.flash
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgent.flashTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/flash_test.js b/mapbox-gl/closure-library/closure/goog/useragent/flash_test.js
    new file mode 100644
    index 0000000000..ea719f585a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/flash_test.js
    @@ -0,0 +1,29 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgent.flashTest');
    +goog.setTestOnly('goog.userAgent.flashTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent.flash');
    +
    +// For now, just test that the flash variables exist, the test runner will
    +// pick up any runtime errors.
    +// TODO(user): Mock out each browser implementation and test the code path
    +// correctly detects the flash version for each case.
    +function testFlash() {
    +  assertNotUndefined(goog.userAgent.flash.HAS_FLASH);
    +  assertNotUndefined(goog.userAgent.flash.VERSION);
    +  assertEquals(typeof goog.userAgent.flash.isVersion('5'), 'boolean');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/iphoto.js b/mapbox-gl/closure-library/closure/goog/useragent/iphoto.js
    new file mode 100644
    index 0000000000..7b315c9cc9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/iphoto.js
    @@ -0,0 +1,87 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Newer versions of iPhoto include a Safari plugin which allows
    + * the browser to detect if iPhoto is installed. Adapted from detection code
    + * built into the Mac.com Gallery RSS feeds.
    + * @author brenneman@google.com (Shawn Brenneman)
    + * @see ../demos/useragent.html
    + */
    +
    +
    +goog.provide('goog.userAgent.iphoto');
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +(function() {
    +  var hasIphoto = false;
    +  var version = '';
    +
    +  /**
    +   * The plugin description string contains the version number as in the form
    +   * 'iPhoto 700'. This returns just the version number as a dotted string,
    +   * e.g., '7.0.0', compatible with {@code goog.string.compareVersions}.
    +   * @param {string} desc The version string.
    +   * @return {string} The dotted version.
    +   */
    +  function getIphotoVersion(desc) {
    +    var matches = desc.match(/\d/g);
    +    return matches.join('.');
    +  }
    +
    +  if (goog.userAgent.WEBKIT &&
    +      navigator.mimeTypes &&
    +      navigator.mimeTypes.length > 0) {
    +    var iphoto = navigator.mimeTypes['application/photo'];
    +
    +    if (iphoto) {
    +      hasIphoto = true;
    +      var description = iphoto['description'];
    +
    +      if (description) {
    +        version = getIphotoVersion(description);
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Whether we can detect that the user has iPhoto installed.
    +   * @type {boolean}
    +   */
    +  goog.userAgent.iphoto.HAS_IPHOTO = hasIphoto;
    +
    +
    +  /**
    +   * The version of iPhoto installed if found.
    +   * @type {string}
    +   */
    +  goog.userAgent.iphoto.VERSION = version;
    +
    +})();
    +
    +
    +/**
    + * Whether the installed version of iPhoto is as new or newer than a given
    + * version.
    + * @param {string} version The version to check.
    + * @return {boolean} Whether the installed version of iPhoto is as new or newer
    + *     than a given version.
    + */
    +goog.userAgent.iphoto.isVersion = function(version) {
    +  return goog.string.compareVersions(
    +      goog.userAgent.iphoto.VERSION, version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/jscript.js b/mapbox-gl/closure-library/closure/goog/useragent/jscript.js
    new file mode 100644
    index 0000000000..b1a9ca99b4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/jscript.js
    @@ -0,0 +1,95 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detection of JScript version.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + */
    +
    +
    +goog.provide('goog.userAgent.jscript');
    +
    +goog.require('goog.string');
    +
    +
    +/**
    + * @define {boolean} True if it is known at compile time that the runtime
    + *     environment will not be using JScript.
    + */
    +goog.define('goog.userAgent.jscript.ASSUME_NO_JSCRIPT', false);
    +
    +
    +/**
    + * Initializer for goog.userAgent.jscript.  Detects if the user agent is using
    + * Microsoft JScript and which version of it.
    + *
    + * This is a named function so that it can be stripped via the jscompiler
    + * option for stripping types.
    + * @private
    + */
    +goog.userAgent.jscript.init_ = function() {
    +  var hasScriptEngine = 'ScriptEngine' in goog.global;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_ =
    +      hasScriptEngine && goog.global['ScriptEngine']() == 'JScript';
    +
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  goog.userAgent.jscript.DETECTED_VERSION_ =
    +      goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_ ?
    +      (goog.global['ScriptEngineMajorVersion']() + '.' +
    +       goog.global['ScriptEngineMinorVersion']() + '.' +
    +       goog.global['ScriptEngineBuildVersion']()) :
    +      '0';
    +};
    +
    +if (!goog.userAgent.jscript.ASSUME_NO_JSCRIPT) {
    +  goog.userAgent.jscript.init_();
    +}
    +
    +
    +/**
    + * Whether we detect that the user agent is using Microsoft JScript.
    + * @type {boolean}
    + */
    +goog.userAgent.jscript.HAS_JSCRIPT = goog.userAgent.jscript.ASSUME_NO_JSCRIPT ?
    +    false : goog.userAgent.jscript.DETECTED_HAS_JSCRIPT_;
    +
    +
    +/**
    + * The installed version of JScript.
    + * @type {string}
    + */
    +goog.userAgent.jscript.VERSION = goog.userAgent.jscript.ASSUME_NO_JSCRIPT ?
    +    '0' : goog.userAgent.jscript.DETECTED_VERSION_;
    +
    +
    +/**
    + * Whether the installed version of JScript is as new or newer than a given
    + * version.
    + * @param {string} version The version to check.
    + * @return {boolean} Whether the installed version of JScript is as new or
    + *     newer than the given version.
    + */
    +goog.userAgent.jscript.isVersion = function(version) {
    +  return goog.string.compareVersions(goog.userAgent.jscript.VERSION,
    +                                     version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.html b/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.html
    new file mode 100644
    index 0000000000..529b198d74
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.html
    @@ -0,0 +1,41 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent.jscript
    +  </title>
    +  <script>
    +// Mock JScript functions
    +
    +function ScriptEngine() {
    +  return 'JScript';
    +}
    +
    +function ScriptEngineMajorVersion() {
    +  return 1;
    +}
    +
    +function ScriptEngineMinorVersion() {
    +  return 2;
    +}
    +
    +function ScriptEngineBuildVersion() {
    +  return 3456;
    +}
    +  </script>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgent.jscriptTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.js b/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.js
    new file mode 100644
    index 0000000000..e92ff4b7d4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/jscript_test.js
    @@ -0,0 +1,54 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +// Mock JScript functions
    +goog.provide('goog.userAgent.jscriptTest');
    +goog.setTestOnly('goog.userAgent.jscriptTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent.jscript');
    +
    +function ScriptEngine() {
    +  return 'JScript';
    +}
    +
    +function ScriptEngineMajorVersion() {
    +  return 1;
    +}
    +
    +function ScriptEngineMinorVersion() {
    +  return 2;
    +}
    +
    +function ScriptEngineBuildVersion() {
    +  return 3456;
    +}
    +
    +function testHasJscript() {
    +  assertTrue('Should have jscript', goog.userAgent.jscript.HAS_JSCRIPT);
    +}
    +
    +function testVersion() {
    +  assertEquals('Version should be 1.2.3456', '1.2.3456',
    +               goog.userAgent.jscript.VERSION);
    +}
    +
    +function testIsVersion() {
    +  assertTrue('Should be version 1.2.3456 or larger',
    +             goog.userAgent.jscript.isVersion('1.2.3456'));
    +  assertTrue('Should be version 1.2 or larger',
    +             goog.userAgent.jscript.isVersion('1.2'));
    +  assertFalse('Should not be version 8.9 or larger',
    +      goog.userAgent.jscript.isVersion('8.9'));
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/keyboard.js b/mapbox-gl/closure-library/closure/goog/useragent/keyboard.js
    new file mode 100644
    index 0000000000..b062b55bee
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/keyboard.js
    @@ -0,0 +1,49 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Constants for determining keyboard support.
    + */
    +
    +goog.provide('goog.userAgent.keyboard');
    +
    +goog.require('goog.labs.userAgent.platform');
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running with in an environment
    + * that should use Mac-based keyboard shortcuts (Meta instead of Ctrl, etc.).
    + */
    +goog.define('goog.userAgent.keyboard.ASSUME_MAC_KEYBOARD', false);
    +
    +
    +/**
    + * Determines whether Mac-based keyboard shortcuts should be used.
    + * @return {boolean}
    + * @private
    + */
    +goog.userAgent.keyboard.determineMacKeyboard_ = function() {
    +  return goog.labs.userAgent.platform.isMacintosh() ||
    +      goog.labs.userAgent.platform.isIos();
    +};
    +
    +
    +/**
    + * Whether the user agent is running in an environment that uses Mac-based
    + * keyboard shortcuts.
    + * @type {boolean}
    + */
    +goog.userAgent.keyboard.MAC_KEYBOARD =
    +    goog.userAgent.keyboard.ASSUME_MAC_KEYBOARD ||
    +    goog.userAgent.keyboard.determineMacKeyboard_();
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/keyboard_test.js b/mapbox-gl/closure-library/closure/goog/useragent/keyboard_test.js
    new file mode 100644
    index 0000000000..e017d6a3cb
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/keyboard_test.js
    @@ -0,0 +1,225 @@
    +// Copyright 2014 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgent.keyboardTest');
    +goog.setTestOnly('goog.userAgent.keyboardTest');
    +
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent.keyboard');
    +goog.require('goog.userAgentTestUtil');
    +
    +
    +var mockAgent;
    +
    +function setUp() {
    +  mockAgent = new goog.testing.MockUserAgent();
    +  mockAgent.install();
    +}
    +
    +function tearDown() {
    +  mockAgent.dispose();
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +}
    +
    +function testAndroid() {
    +  mockAgent.setNavigator({platform: 'Linux'});
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.ANDROID_BROWSER_235);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.ANDROID_BROWSER_221);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.ANDROID_BROWSER_233);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.ANDROID_BROWSER_403);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.ANDROID_BROWSER_403_ALT);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testIe() {
    +  mockAgent.setNavigator({platform: 'Windows'});
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_6);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_7);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_8);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_8_COMPATIBILITY);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_9);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_10);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_10_COMPATIBILITY);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IE_11);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_11_COMPATIBILITY_MSIE_7);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.IE_11_COMPATIBILITY_MSIE_9);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testFirefoxMac() {
    +  mockAgent.setNavigator({platform: 'Macintosh'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_MAC);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testFirefoxNotMac() {
    +  mockAgent.setNavigator({platform: 'X11'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_LINUX);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'Windows'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.FIREFOX_WINDOWS);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testSafari() {
    +  mockAgent.setNavigator({platform: 'Macintosh'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_6);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_MAC);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'iPhone'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPHONE_32);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPHONE_421);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPHONE_431);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPHONE_6);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'iPod'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_IPOD);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testSafariWndows() {
    +  mockAgent.setNavigator({platform: 'Macintosh'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.SAFARI_WINDOWS);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testOperaMac() {
    +  mockAgent.setNavigator({platform: 'Macintosh'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_MAC);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testOperaNonMac() {
    +  mockAgent.setNavigator({platform: 'X11'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_LINUX);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'Windows'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.OPERA_15);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testIPad() {
    +  mockAgent.setNavigator({platform: 'iPad'});
    +  setUserAgent(goog.labs.userAgent.testAgents.IPAD_4);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IPAD_5);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  setUserAgent(goog.labs.userAgent.testAgents.IPAD_6);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testChromeMac() {
    +  mockAgent.setNavigator({platform: 'Macintosh'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_MAC);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'iPhone'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_IPHONE);
    +  assertTrue(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function testChromeNonMac() {
    +  mockAgent.setNavigator({platform: 'Linux'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_ANDROID);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'X11'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_OS);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'X11'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_LINUX);
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +
    +  mockAgent.setNavigator({platform: 'Windows'});
    +  setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_25);
    +
    +  assertFalse(goog.userAgent.keyboard.MAC_KEYBOARD);
    +}
    +
    +function setUserAgent(ua) {
    +  mockAgent.setUserAgentString(ua);
    +  goog.labs.userAgent.util.setUserAgent(ua);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/picasa.js b/mapbox-gl/closure-library/closure/goog/useragent/picasa.js
    new file mode 100644
    index 0000000000..f4b00a2ba0
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/picasa.js
    @@ -0,0 +1,112 @@
    +// Copyright 2007 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detection for whether the user has Picasa installed.
    + * Only Picasa versions 2 and later can be detected, and only from Firefox or
    + * Internet Explorer. Picasa for Linux cannot be detected.
    + *
    + * In the future, Picasa may provide access to the installed version number,
    + * but until then we can only detect that Picasa 2 or later is present.
    + *
    + * To check for Picasa on Internet Explorer requires using document.write, so
    + * this file must be included at page rendering time and cannot be imported
    + * later as part of a dynamically loaded module.
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + * @see ../demos/useragent.html
    + */
    +
    +
    +goog.provide('goog.userAgent.picasa');
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Variable name used to temporarily save the Picasa state in the global object
    + * in Internet Explorer.
    + * @type {string}
    + * @private
    + */
    +goog.userAgent.picasa.IE_HAS_PICASA_ = 'hasPicasa';
    +
    +
    +(function() {
    +  var hasPicasa = false;
    +
    +  if (goog.userAgent.IE) {
    +    // In Internet Explorer, Picasa 2 can be detected using conditional comments
    +    // due to some nice registry magic. The precise version number is not
    +    // available, only the major version. This may be updated for Picasa 3. This
    +    // check must pollute the global namespace.
    +    goog.global[goog.userAgent.picasa.IE_HAS_PICASA_] = hasPicasa;
    +
    +    // NOTE(user): Some browsers do not like seeing
    +    // slash-script anywhere in the text even if it's inside a string
    +    // and escaped with a backslash, make a string in a way that
    +    // avoids problems.
    +
    +    document.write(goog.string.subs(
    +        '<!--[if gte Picasa 2]>' +
    +        '<%s>' +
    +        'this.%s=true;' +
    +        '</%s>' +
    +        '<![endif]-->',
    +        'script', goog.userAgent.picasa.IE_HAS_PICASA_, 'script'));
    +
    +    hasPicasa = goog.global[goog.userAgent.picasa.IE_HAS_PICASA_];
    +
    +    // Unset the variable in a crude attempt to leave no trace.
    +    goog.global[goog.userAgent.picasa.IE_HAS_PICASA_] = undefined;
    +
    +  } else if (navigator.mimeTypes &&
    +             navigator.mimeTypes['application/x-picasa-detect']) {
    +    // Picasa 2.x registers a file handler for the MIME-type
    +    // 'application/x-picasa-detect' for detection in Firefox. Future versions
    +    // may make precise version detection possible.
    +    hasPicasa = true;
    +  }
    +
    +  /**
    +   * Whether we detect the user has Picasa installed.
    +   * @type {boolean}
    +   */
    +  goog.userAgent.picasa.HAS_PICASA = hasPicasa;
    +
    +
    +  /**
    +   * The installed version of Picasa. If Picasa is detected, this means it is
    +   * version 2 or later. The precise version number is not yet available to the
    +   * browser, this is a placeholder for later versions of Picasa.
    +   * @type {string}
    +   */
    +  goog.userAgent.picasa.VERSION = hasPicasa ? '2' : '';
    +
    +})();
    +
    +
    +/**
    + * Whether the installed Picasa version is as new or newer than a given version.
    + * This is not yet relevant, we can't detect the true Picasa version number yet,
    + * but this may be possible in future Picasa releases.
    + * @param {string} version The version to check.
    + * @return {boolean} Whether the installed Picasa version is as new or newer
    + *     than a given version.
    + */
    +goog.userAgent.picasa.isVersion = function(version) {
    +  return goog.string.compareVersions(
    +      goog.userAgent.picasa.VERSION, version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/platform.js b/mapbox-gl/closure-library/closure/goog/useragent/platform.js
    new file mode 100644
    index 0000000000..4eef510a1a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/platform.js
    @@ -0,0 +1,83 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for getting details about the user's platform.
    + */
    +
    +goog.provide('goog.userAgent.platform');
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Detects the version of Windows or Mac OS that is running.
    + *
    + * @private
    + * @return {string} The platform version.
    + */
    +goog.userAgent.platform.determineVersion_ = function() {
    +  var version = '', re;
    +  if (goog.userAgent.WINDOWS) {
    +    re = /Windows NT ([0-9.]+)/;
    +    var match = re.exec(goog.userAgent.getUserAgentString());
    +    if (match) {
    +      return match[1];
    +    } else {
    +      return '0';
    +    }
    +  } else if (goog.userAgent.MAC) {
    +    re = /10[_.][0-9_.]+/;
    +    var match = re.exec(goog.userAgent.getUserAgentString());
    +    // Note: some old versions of Camino do not report an OSX version.
    +    // Default to 10.
    +    return match ? match[0].replace(/_/g, '.') : '10';
    +  } else if (goog.userAgent.ANDROID) {
    +    re = /Android\s+([^\);]+)(\)|;)/;
    +    var match = re.exec(goog.userAgent.getUserAgentString());
    +    return match ? match[1] : '';
    +  } else if (goog.userAgent.IPHONE || goog.userAgent.IPAD) {
    +    re = /(?:iPhone|CPU)\s+OS\s+(\S+)/;
    +    var match = re.exec(goog.userAgent.getUserAgentString());
    +    // Report the version as x.y.z and not x_y_z
    +    return match ? match[1].replace(/_/g, '.') : '';
    +  }
    +
    +  return '';
    +};
    +
    +
    +/**
    + * The version of the platform. We only determine the version for Windows and
    + * Mac, since it doesn't make much sense on Linux. For Windows, we only look at
    + * the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given version
    + * 0.0
    + * @type {string}
    + */
    +goog.userAgent.platform.VERSION = goog.userAgent.platform.determineVersion_();
    +
    +
    +/**
    + * Whether the user agent platform version is higher or the same as the given
    + * version.
    + *
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the user agent platform version is higher or the
    + *     same as the given version.
    + */
    +goog.userAgent.platform.isVersion = function(version) {
    +  return goog.string.compareVersions(
    +      goog.userAgent.platform.VERSION, version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/platform_test.html b/mapbox-gl/closure-library/closure/goog/useragent/platform_test.html
    new file mode 100644
    index 0000000000..c020b46f3b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/platform_test.html
    @@ -0,0 +1,26 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author mpd@google.com (Michael Davidson)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent.platform
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgent.platformTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/platform_test.js b/mapbox-gl/closure-library/closure/goog/useragent/platform_test.js
    new file mode 100644
    index 0000000000..0b436eff9d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/platform_test.js
    @@ -0,0 +1,159 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgent.platformTest');
    +goog.setTestOnly('goog.userAgent.platformTest');
    +
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.platform');
    +goog.require('goog.userAgentTestUtil');
    +
    +var mockAgent;
    +
    +function setUp() {
    +  mockAgent = new goog.testing.MockUserAgent();
    +  mockAgent.install();
    +}
    +
    +function tearDown() {
    +  mockAgent.dispose();
    +  updateUserAgentUtils();
    +}
    +
    +function updateUserAgentUtils() {
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +}
    +
    +function testWindows() {
    +  mockAgent.setNavigator({platform: 'Win32'});
    +
    +  var win98 = 'Mozilla/4.0 (compatible; MSIE 6.0b; Windows 98; Win 9x 4.90)';
    +  var win2k = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.0; en-US)';
    +  var xp = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.1; en-US)';
    +  var vista = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)';
    +  var win7 = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.1; en-US)';
    +  var win81 = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
    +
    +  mockAgent.setUserAgentString(win98);
    +  updateUserAgentUtils();
    +  assertEquals('0', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(win2k);
    +  updateUserAgentUtils();
    +  assertEquals('5.0', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(xp);
    +  updateUserAgentUtils();
    +  assertEquals('5.1', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(vista);
    +  updateUserAgentUtils();
    +  assertEquals('6.0', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(win7);
    +  updateUserAgentUtils();
    +  assertEquals('6.1', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(win81);
    +  updateUserAgentUtils();
    +  assertEquals('6.3', goog.userAgent.platform.VERSION);
    +}
    +
    +function testMac() {
    +  // For some reason Chrome substitutes _ for . in the OS version.
    +  var chrome = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US)' +
    +      'AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.49 Safari/532.5';
    +
    +  var ff = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US;' +
    +      'rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6';
    +
    +  // An old camino version that doesn't report a Mac version.
    +  var camino = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.11)' +
    +      'Gecko/20071128 Camino/1.5.4';
    +
    +  mockAgent.setNavigator({platform: 'IntelMac'});
    +
    +  mockAgent.setUserAgentString(chrome);
    +  updateUserAgentUtils();
    +  assertEquals('10.5.8', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(ff);
    +  updateUserAgentUtils();
    +  assertEquals('10.5', goog.userAgent.platform.VERSION);
    +
    +  mockAgent.setUserAgentString(camino);
    +  updateUserAgentUtils();
    +  assertEquals('10', goog.userAgent.platform.VERSION);
    +}
    +
    +function testChromeOnAndroid() {
    +  // Borrowing search's test user agent string for android.
    +  var uaString = 'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus' +
    +      ' Build/ICL53F) AppleWebKit/535.7 (KHTML, like Gecko) ' +
    +      'Chrome/18.0.1025.133 Mobile Safari/535.7';
    +
    +  // Need to set this lest the testing platform be used for detection.
    +  mockAgent.setNavigator({platform: 'Android'});
    +
    +  mockAgent.setUserAgentString(uaString);
    +  updateUserAgentUtils();
    +  assertTrue(goog.userAgent.ANDROID);
    +  assertEquals('4.0.2', goog.userAgent.platform.VERSION);
    +}
    +
    +function testAndroidBrowser() {
    +  var uaString = 'Mozilla/5.0 (Linux; U; Android 2.3.4; fr-fr;' +
    +      'HTC Desire Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko)' +
    +      'Version/4.0 Mobile Safari/533.1';
    +
    +  // Need to set this lest the testing platform be used for detection.
    +  mockAgent.setNavigator({platform: 'Android'});
    +
    +  mockAgent.setUserAgentString(uaString);
    +  updateUserAgentUtils();
    +  assertTrue(goog.userAgent.ANDROID);
    +  assertEquals('2.3.4', goog.userAgent.platform.VERSION);
    +}
    +
    +function testIPhone() {
    +  // Borrowing search's test user agent string for the iPhone.
    +  var uaString = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; ' +
    +      'en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 ' +
    +      'Mobile/8A293 Safari/6531.22.7';
    +
    +  // Need to set this lest the testing platform be used for detection.
    +  mockAgent.setNavigator({platform: 'iPhone'});
    +
    +  mockAgent.setUserAgentString(uaString);
    +  updateUserAgentUtils();
    +  assertTrue(goog.userAgent.IPHONE);
    +  assertEquals('4.0', goog.userAgent.platform.VERSION);
    +}
    +
    +function testIPad() {
    +  // Borrowing search's test user agent string for the iPad.
    +  var uaString = 'Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) ' +
    +      'AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 ' +
    +      'Safari/6533.18.5';
    +
    +  // Need to set this lest the testing platform be used for detection.
    +  mockAgent.setNavigator({platform: 'iPad'});
    +
    +  mockAgent.setUserAgentString(uaString);
    +  updateUserAgentUtils();
    +  assertTrue(goog.userAgent.IPAD);
    +  assertEquals('4.2.1', goog.userAgent.platform.VERSION);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/product.js b/mapbox-gl/closure-library/closure/goog/useragent/product.js
    new file mode 100644
    index 0000000000..7cf6d26cf4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/product.js
    @@ -0,0 +1,253 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Detects the specific browser and not just the rendering engine.
    + *
    + */
    +
    +goog.provide('goog.userAgent.product');
    +
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * @define {boolean} Whether the code is running on the Firefox web browser.
    + */
    +goog.define('goog.userAgent.product.ASSUME_FIREFOX', false);
    +
    +
    +/**
    + * @define {boolean} Whether the code is running on the Camino web browser.
    + */
    +goog.define('goog.userAgent.product.ASSUME_CAMINO', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the product is an
    + *     iPhone.
    + */
    +goog.define('goog.userAgent.product.ASSUME_IPHONE', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the product is an
    + *     iPad.
    + */
    +goog.define('goog.userAgent.product.ASSUME_IPAD', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the product is an
    + *     Android phone.
    + */
    +goog.define('goog.userAgent.product.ASSUME_ANDROID', false);
    +
    +
    +/**
    + * @define {boolean} Whether the code is running on the Chrome web browser.
    + */
    +goog.define('goog.userAgent.product.ASSUME_CHROME', false);
    +
    +
    +/**
    + * @define {boolean} Whether the code is running on the Safari web browser.
    + */
    +goog.define('goog.userAgent.product.ASSUME_SAFARI', false);
    +
    +
    +/**
    + * Whether we know the product type at compile-time.
    + * @type {boolean}
    + * @private
    + */
    +goog.userAgent.product.PRODUCT_KNOWN_ =
    +    goog.userAgent.ASSUME_IE ||
    +    goog.userAgent.ASSUME_OPERA ||
    +    goog.userAgent.product.ASSUME_FIREFOX ||
    +    goog.userAgent.product.ASSUME_CAMINO ||
    +    goog.userAgent.product.ASSUME_IPHONE ||
    +    goog.userAgent.product.ASSUME_IPAD ||
    +    goog.userAgent.product.ASSUME_ANDROID ||
    +    goog.userAgent.product.ASSUME_CHROME ||
    +    goog.userAgent.product.ASSUME_SAFARI;
    +
    +
    +/**
    + * Right now we just focus on Tier 1-3 browsers at:
    + * http://wiki/Nonconf/ProductPlatformGuidelines
    + * As well as the YUI grade A browsers at:
    + * http://developer.yahoo.com/yui/articles/gbs/
    + *
    + * @private
    + */
    +goog.userAgent.product.init_ = function() {
    +
    +  /**
    +   * Whether the code is running on the Firefox web browser.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedFirefox_ = false;
    +
    +  /**
    +   * Whether the code is running on the Camino web browser.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedCamino_ = false;
    +
    +  /**
    +   * Whether the code is running on an iPhone or iPod touch.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedIphone_ = false;
    +
    +  /**
    +   * Whether the code is running on an iPad
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedIpad_ = false;
    +
    +  /**
    +   * Whether the code is running on the default browser on an Android phone.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedAndroid_ = false;
    +
    +  /**
    +   * Whether the code is running on the Chrome web browser.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedChrome_ = false;
    +
    +  /**
    +   * Whether the code is running on the Safari web browser.
    +   * @type {boolean}
    +   * @private
    +   */
    +  goog.userAgent.product.detectedSafari_ = false;
    +
    +  var ua = goog.userAgent.getUserAgentString();
    +  if (!ua) {
    +    return;
    +  }
    +
    +  // The order of the if-statements in the following code is important.
    +  // For example, in the WebKit section, we put Chrome in front of Safari
    +  // because the string 'Safari' is present on both of those browsers'
    +  // userAgent strings as well as the string we are looking for.
    +  // The idea is to prevent accidental detection of more than one client.
    +
    +  if (ua.indexOf('Firefox') != -1) {
    +    goog.userAgent.product.detectedFirefox_ = true;
    +  } else if (ua.indexOf('Camino') != -1) {
    +    goog.userAgent.product.detectedCamino_ = true;
    +  } else if (ua.indexOf('iPad') != -1) {
    +    goog.userAgent.product.detectedIpad_ = true;
    +  } else if (ua.indexOf('iPhone') != -1 || ua.indexOf('iPod') != -1) {
    +    goog.userAgent.product.detectedIphone_ = true;
    +  } else if (ua.indexOf('Chrome') != -1) {
    +    goog.userAgent.product.detectedChrome_ = true;
    +  } else if (ua.indexOf('Android') != -1) {
    +    goog.userAgent.product.detectedAndroid_ = true;
    +  } else if (ua.indexOf('Safari') != -1) {
    +    goog.userAgent.product.detectedSafari_ = true;
    +  }
    +};
    +
    +if (!goog.userAgent.product.PRODUCT_KNOWN_) {
    +  goog.userAgent.product.init_();
    +}
    +
    +
    +/**
    + * Whether the code is running on the Opera web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.OPERA = goog.userAgent.OPERA;
    +
    +
    +/**
    + * Whether the code is running on an IE web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.IE = goog.userAgent.IE;
    +
    +
    +/**
    + * Whether the code is running on the Firefox web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.FIREFOX = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_FIREFOX :
    +    goog.userAgent.product.detectedFirefox_;
    +
    +
    +/**
    + * Whether the code is running on the Camino web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.CAMINO = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_CAMINO :
    +    goog.userAgent.product.detectedCamino_;
    +
    +
    +/**
    + * Whether the code is running on an iPhone or iPod touch.
    + * @type {boolean}
    + */
    +goog.userAgent.product.IPHONE = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_IPHONE :
    +    goog.userAgent.product.detectedIphone_;
    +
    +
    +/**
    + * Whether the code is running on an iPad.
    + * @type {boolean}
    + */
    +goog.userAgent.product.IPAD = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_IPAD :
    +    goog.userAgent.product.detectedIpad_;
    +
    +
    +/**
    + * Whether the code is running on the default browser on an Android phone.
    + * @type {boolean}
    + */
    +goog.userAgent.product.ANDROID = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_ANDROID :
    +    goog.userAgent.product.detectedAndroid_;
    +
    +
    +/**
    + * Whether the code is running on the Chrome web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.CHROME = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_CHROME :
    +    goog.userAgent.product.detectedChrome_;
    +
    +
    +/**
    + * Whether the code is running on the Safari web browser.
    + * @type {boolean}
    + */
    +goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ?
    +    goog.userAgent.product.ASSUME_SAFARI :
    +    goog.userAgent.product.detectedSafari_;
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/product_isversion.js b/mapbox-gl/closure-library/closure/goog/useragent/product_isversion.js
    new file mode 100644
    index 0000000000..f40a41c7fd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/product_isversion.js
    @@ -0,0 +1,142 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Functions for understanding the version of the browser.
    + * This is pulled out of product.js to ensure that only builds that need
    + * this functionality actually get it, without having to rely on the compiler
    + * to strip out unneeded pieces.
    + *
    + * TODO(nnaze): Move to more appropriate filename/namespace.
    + *
    + */
    +
    +
    +goog.provide('goog.userAgent.product.isVersion');
    +
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +
    +
    +/**
    + * @return {string} The string that describes the version number of the user
    + *     agent product.  This is a string rather than a number because it may
    + *     contain 'b', 'a', and so on.
    + * @private
    + */
    +goog.userAgent.product.determineVersion_ = function() {
    +  // All browsers have different ways to detect the version and they all have
    +  // different naming schemes.
    +
    +  if (goog.userAgent.product.FIREFOX) {
    +    // Firefox/2.0.0.1 or Firefox/3.5.3
    +    return goog.userAgent.product.getFirstRegExpGroup_(/Firefox\/([0-9.]+)/);
    +  }
    +
    +  if (goog.userAgent.product.IE || goog.userAgent.product.OPERA) {
    +    return goog.userAgent.VERSION;
    +  }
    +
    +  if (goog.userAgent.product.CHROME) {
    +    // Chrome/4.0.223.1
    +    return goog.userAgent.product.getFirstRegExpGroup_(/Chrome\/([0-9.]+)/);
    +  }
    +
    +  if (goog.userAgent.product.SAFARI) {
    +    // Version/5.0.3
    +    //
    +    // NOTE: Before version 3, Safari did not report a product version number.
    +    // The product version number for these browsers will be the empty string.
    +    // They may be differentiated by WebKit version number in goog.userAgent.
    +    return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
    +  }
    +
    +  if (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) {
    +    // Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1
    +    // (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3
    +    // Version is the browser version, Mobile is the build number. We combine
    +    // the version string with the build number: 3.0.3A100a for the example.
    +    var arr = goog.userAgent.product.execRegExp_(
    +        /Version\/(\S+).*Mobile\/(\S+)/);
    +    if (arr) {
    +      return arr[1] + '.' + arr[2];
    +    }
    +  } else if (goog.userAgent.product.ANDROID) {
    +    // Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+
    +    // (KHTML, like Gecko) Safari/419.3
    +    //
    +    // Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+
    +    // (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2
    +    //
    +    // Prefer Version number if present, else make do with the OS number
    +    var version = goog.userAgent.product.getFirstRegExpGroup_(
    +        /Android\s+([0-9.]+)/);
    +    if (version) {
    +      return version;
    +    }
    +
    +    return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
    +  } else if (goog.userAgent.product.CAMINO) {
    +    return goog.userAgent.product.getFirstRegExpGroup_(/Camino\/([0-9.]+)/);
    +  }
    +
    +  return '';
    +};
    +
    +
    +/**
    + * Return the first group of the given regex.
    + * @param {!RegExp} re Regular expression with at least one group.
    + * @return {string} Contents of the first group or an empty string if no match.
    + * @private
    + */
    +goog.userAgent.product.getFirstRegExpGroup_ = function(re) {
    +  var arr = goog.userAgent.product.execRegExp_(re);
    +  return arr ? arr[1] : '';
    +};
    +
    +
    +/**
    + * Run regexp's exec() on the userAgent string.
    + * @param {!RegExp} re Regular expression.
    + * @return {Array<?>} A result array, or null for no match.
    + * @private
    + */
    +goog.userAgent.product.execRegExp_ = function(re) {
    +  return re.exec(goog.userAgent.getUserAgentString());
    +};
    +
    +
    +/**
    + * The version of the user agent. This is a string because it might contain
    + * 'b' (as in beta) as well as multiple dots.
    + * @type {string}
    + */
    +goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_();
    +
    +
    +/**
    + * Whether the user agent product version is higher or the same as the given
    + * version.
    + *
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the user agent product version is higher or the
    + *     same as the given version.
    + */
    +goog.userAgent.product.isVersion = function(version) {
    +  return goog.string.compareVersions(
    +      goog.userAgent.product.VERSION, version) >= 0;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/product_test.html b/mapbox-gl/closure-library/closure/goog/useragent/product_test.html
    new file mode 100644
    index 0000000000..a37d52266f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/product_test.html
    @@ -0,0 +1,30 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <!--
    +
    +  @author andybons@google.com (Andrew Boneventre)
    +-->
    + <head>
    +  <!--
    +This test has not yet been updated to run on IE8. See http://b/hotlist?id=36311
    +-->
    +  <!--meta http-equiv="X-UA-Compatible" content="IE=edge"-->
    +  <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent.product
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgent.productTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/product_test.js b/mapbox-gl/closure-library/closure/goog/useragent/product_test.js
    new file mode 100644
    index 0000000000..ea1a7dec12
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/product_test.js
    @@ -0,0 +1,380 @@
    +// Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgent.productTest');
    +goog.setTestOnly('goog.userAgent.productTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.MockUserAgent');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.product');
    +goog.require('goog.userAgent.product.isVersion');
    +goog.require('goog.userAgentTestUtil');
    +
    +var mockAgent;
    +var replacer;
    +
    +function setUp() {
    +  mockAgent = new goog.testing.MockUserAgent();
    +  mockAgent.install();
    +  replacer = new goog.testing.PropertyReplacer();
    +}
    +
    +function tearDown() {
    +  replacer.reset();
    +  mockAgent.dispose();
    +  updateUserAgentUtils();
    +}
    +
    +function updateUserAgentUtils() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +
    +  goog.userAgent.product.init_();
    +  // In an ideal world, this assignment would be just a function in
    +  // product.js that we could call, but putting it into a function causes
    +  // the compiler to fail to compile product.js to nothing when one of
    +  // the ASSUME flags is set.
    +  goog.userAgent.product.OPERA = goog.userAgent.OPERA;
    +  goog.userAgent.product.IE = goog.userAgent.IE;
    +  goog.userAgent.product.FIREFOX = goog.userAgent.product.detectedFirefox_;
    +  goog.userAgent.product.CAMINO = goog.userAgent.product.detectedCamino_;
    +  goog.userAgent.product.IPHONE = goog.userAgent.product.detectedIphone_;
    +  goog.userAgent.product.IPAD = goog.userAgent.product.detectedIpad_;
    +  goog.userAgent.product.ANDROID = goog.userAgent.product.detectedAndroid_;
    +  goog.userAgent.product.CHROME = goog.userAgent.product.detectedChrome_;
    +  goog.userAgent.product.SAFARI = goog.userAgent.product.detectedSafari_;
    +  goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_();
    +}
    +
    +// The set of products whose corresponding goog.userAgent.product value is set
    +// in goog.userAgent.product.init_().
    +var DETECTED_BROWSER_KEYS =
    +    ['FIREFOX', 'CAMINO', 'IPHONE', 'IPAD', 'ANDROID', 'CHROME', 'SAFARI'];
    +
    +function assertIsBrowser(browser) {
    +  function createDetectedBrowserKey(browser) {
    +    switch (browser) {
    +      case 'FIREFOX': return 'detectedFirefox_';
    +      case 'CAMINO': return 'detectedCamino_';
    +      case 'IPHONE': return 'detectedIphone_';
    +      case 'IPAD': return 'detectedIpad_';
    +      case 'ANDROID': return 'detectedAndroid_';
    +      case 'CHROME': return 'detectedChrome_';
    +      case 'SAFARI': return 'detectedSafari_';
    +      case 'IE': return 'IE';
    +      case 'OPERA': return 'OPERA';
    +    }
    +    throw Error('Unknown browser: ' + browser);
    +  }
    +
    +  var productKey = createDetectedBrowserKey(browser);
    +  assertTrue(goog.userAgent.product[productKey]);
    +  // Make sure we don't have any false positives for other browsers.
    +  goog.array.forEach(DETECTED_BROWSER_KEYS, function(el) {
    +    if (el != browser) {
    +      assertFalse('useragent should not match: ' + el,
    +          goog.userAgent.product[createDetectedBrowserKey(el)]);
    +    }
    +  });
    +}
    +
    +function assertBrowserAndVersion(userAgent, browser, version) {
    +  mockAgent.setUserAgentString(userAgent);
    +  updateUserAgentUtils();
    +  assertIsBrowser(browser);
    +  assertEquals('User agent should have this version',
    +               version, goog.userAgent.VERSION);
    +}
    +
    +
    +/**
    + * @param {Array<{
    + *           ua: string,
    + *           versions: Array<{
    + *             num: {string|number}, truth: boolean}>}>} userAgents
    + * @param {string} browser
    + */
    +function checkEachUserAgentDetected(userAgents, browser) {
    +  goog.array.forEach(userAgents, function(ua) {
    +    mockAgent.setUserAgentString(ua.ua);
    +    updateUserAgentUtils();
    +    assertIsBrowser(browser);
    +    goog.array.forEach(ua.versions, function(v) {
    +      assertEquals(
    +          'Expected version ' + v.num + ' from ' + ua.ua + ' but got ' +
    +              goog.userAgent.product.VERSION,
    +          v.truth, goog.userAgent.product.isVersion(v.num));
    +    });
    +  });
    +}
    +
    +function testInternetExplorer() {
    +  var userAgents = [
    +    {ua: 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; ' +
    +          'chromeframe; .NET CLR 1.1.4322; InfoPath.1; ' +
    +          '.NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; ' +
    +          '.NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727)',
    +      versions: [
    +        {num: 6, truth: true},
    +        {num: '7.0', truth: true},
    +        {num: 7.1, truth: false},
    +        {num: 8, truth: false}
    +      ]
    +    },
    +    {ua: 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
    +      versions: [
    +        {num: 10, truth: true},
    +        {num: 11, truth: true},
    +        {num: '11.0', truth: true},
    +        {num: '12', truth: false}
    +      ]
    +    }
    +  ];
    +  // hide any navigator.product value by putting in a navigator with no
    +  // properties.
    +  mockAgent.setNavigator({});
    +  checkEachUserAgentDetected(userAgents, 'IE');
    +}
    +
    +function testOpera() {
    +  var opera = {};
    +  var userAgents = [
    +    {ua: 'Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.01',
    +      versions: [
    +        {num: 9, truth: true},
    +        {num: '10.1', truth: true},
    +        {num: 11, truth: false}
    +      ]}
    +  ];
    +  replacer.set(goog.global, 'opera', opera);
    +  opera.version = '10.01';
    +  checkEachUserAgentDetected(userAgents, 'OPERA');
    +  userAgents = [
    +    {ua: 'Opera/9.63 (Windows NT 5.1; U; en) Presto/2.1.1',
    +      versions: [
    +        {num: 9, truth: true},
    +        {num: '10.1', truth: false},
    +        {num: '9.80', truth: false},
    +        {num: '9.60', truth: true}
    +      ]}
    +  ];
    +  opera.version = '9.63';
    +  checkEachUserAgentDetected(userAgents, 'OPERA');
    +}
    +
    +function testFirefox() {
    +  var userAgents = [
    +    {ua: 'Mozilla/6.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; ' +
    +          'rv:2.0.0.0) Gecko/20061028 Firefox/3.0',
    +      versions: [
    +        {num: 2, truth: true},
    +        {num: '3.0', truth: true},
    +        {num: '3.5.3', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; ' +
    +          'rv:1.8.1.4) Gecko/20070515 Firefox/2.0.4',
    +      versions: [
    +        {num: 2, truth: true},
    +        {num: '2.0.4', truth: true},
    +        {num: 3, truth: false},
    +        {num: '3.5.3', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/6.0 Firefox/6.0',
    +      versions: [
    +        {num: 6, truth: true},
    +        {num: '6.0', truth: true},
    +        {num: 7, truth: false},
    +        {num: '7.0', truth: false}
    +      ]}
    +  ];
    +
    +  checkEachUserAgentDetected(userAgents, 'FIREFOX');
    +
    +  // Mozilla reported to us that they plan this UA format starting
    +  // in Firefox 13.
    +  // See bug at https://bugzilla.mozilla.org/show_bug.cgi?id=588909
    +  // and thread at http://goto.google.com/pfltz
    +  mockAgent.setNavigator({product: 'Gecko'});
    +  assertBrowserAndVersion(
    +      'Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/6.0 Firefox/6.0',
    +      'FIREFOX', '6.0');
    +}
    +
    +function testCamino() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.11) ' +
    +          'Gecko/20071128 Camino/1.5.4',
    +      versions: [
    +        {num: '1.5.4', truth: true},
    +        {num: 1, truth: true},
    +        {num: '2.0', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.10) ' +
    +          'Gecko/20070228 Camino/1.0.4',
    +      versions: [
    +        {num: '1.5.4', truth: false},
    +        {num: 1, truth: true},
    +        {num: '2.0', truth: false}
    +      ]}
    +  ];
    +  mockAgent.setNavigator({vendor: 'Camino', product: 'Gecko'});
    +  checkEachUserAgentDetected(userAgents, 'CAMINO');
    +}
    +
    +function testChrome() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) ' +
    +          'AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.2.153.0 ' +
    +          'Safari/525.19',
    +      versions: [
    +        {num: '0.2.153', truth: true},
    +        {num: 1, truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) ' +
    +          'AppleWebKit/532.3 (KHTML, like Gecko) Chrome/4.0.223.11 ' +
    +          'Safari/532.3',
    +      versions: [
    +        {num: 4, truth: true},
    +        {num: '0.2.153', truth: true},
    +        {num: '4.1.223.13', truth: false},
    +        {num: '4.0.223.10', truth: true}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B)' +
    +          'AppleWebKit/535.19 (KHTML, like Gecko) ' +
    +          'Chrome/18.0.1025.133 Mobile' +
    +          'Safari/535.19',
    +      versions: [
    +        {num: 18, truth: true},
    +        {num: '0.2.153', truth: true},
    +        {num: 29, truth: false},
    +        {num: '18.0.1025.133', truth: true}
    +      ]}
    +  ];
    +  checkEachUserAgentDetected(userAgents, 'CHROME');
    +}
    +
    +function testSafari() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; de-de) ' +
    +          'AppleWebKit/534.16+ (KHTML, like Gecko) Version/5.0.3 ' +
    +          'Safari/533.19.4',
    +      versions: [
    +        {num: 5, truth: true},
    +        {num: '5.0.3', truth: true},
    +        {num: '5.0.4', truth: false},
    +        {num: '533', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Windows; U; Windows NT 6.0; pl-PL) ' +
    +          'AppleWebKit/525.19 (KHTML, like Gecko) Version/3.1.2 Safari/525.21',
    +      versions: [
    +        {num: 3, truth: true},
    +        {num: '3.0', truth: true},
    +        {num: '3.1.2', truth: true}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_3; en-us) ' +
    +          'AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.1 Safari/525.20',
    +      versions: [
    +        {num: 3, truth: true},
    +        {num: '3.1.1', truth: true},
    +        {num: '3.1.2', truth: false},
    +        {num: '525.21', truth: false}
    +      ]},
    +
    +    // Safari 1 and 2 do not report product version numbers in their
    +    // user-agent strings. VERSION for these browsers will be set to ''.
    +    {ua: 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ja-jp) ' +
    +          'AppleWebKit/418.9.1 (KHTML, like Gecko) Safari/419.3',
    +      versions: [
    +        {num: 3, truth: false},
    +        {num: 2, truth: false},
    +        {num: 1, truth: false},
    +        {num: 0, truth: true},
    +        {num: '0', truth: true},
    +        {num: '', truth: true}
    +      ]}
    +  ];
    +  checkEachUserAgentDetected(userAgents, 'SAFARI');
    +}
    +
    +function testIphone() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ ' +
    +          '(KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
    +      versions: [
    +        {num: '3.0.1A543a', truth: true},
    +        {num: '3.0', truth: true},
    +        {num: '3.0.1B543a', truth: false},
    +        {num: '3.1.1A543a', truth: false},
    +        {num: '3.0.1A320c', truth: true},
    +        {num: '3.0.3A100a', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 ' +
    +          '(KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3',
    +      versions: [
    +        {num: '3.0.1A543a', truth: true},
    +        {num: '3.0.3A100a', truth: true}
    +      ]}
    +  ];
    +  checkEachUserAgentDetected(userAgents, 'IPHONE');
    +}
    +
    +function testIpad() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) ' +
    +          'AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 ' +
    +          'Mobile/7B334b Safari/531.21.10',
    +      versions: [
    +        {num: '4.0.4.7B334b', truth: true},
    +        {num: '4.0', truth: true},
    +        {num: '4.0.4.7C334b', truth: false},
    +        {num: '4.1.7B334b', truth: false},
    +        {num: '4.0.4.7B320c', truth: true},
    +        {num: '4.0.4.8B334b', truth: false}
    +      ]},
    +    // Webview in the Facebook iOS app
    +    {ua: 'Mozilla/5.0 (iPad; CPU OS 8_1 like Mac OS X) AppleWebKit/600.1.4' +
    +          '(KHTML, like Gecko) Mobile/12B410 [FBAN/FBIOS;FBAV/16.0.0.13.22;' +
    +          'FBBV/4697910;FBDV/iPad3,4;FBMD/iPad;FBSN/iPhone OS;FBSV/8.1;' +
    +          'FBSS/2; FBCR/;FBID/tablet;FBLC/ja_JP;FBOP/1]',
    +      versions: [
    +        {num: '', truth: true}
    +      ]}
    +  ];
    +  checkEachUserAgentDetected(userAgents, 'IPAD');
    +}
    +
    +function testAndroid() {
    +  var userAgents = [
    +    {ua: 'Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+ ' +
    +          '(KHTML, like Gecko) Safari/419.3',
    +      versions: [
    +        {num: 0.5, truth: true},
    +        {num: '1.0', truth: false}
    +      ]},
    +    {ua: 'Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) ' +
    +          'AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile ' +
    +          'Safari/523.12.2',
    +      versions: [
    +        {num: 0.5, truth: true},
    +        {num: 1, truth: true},
    +        {num: '1.0', truth: true},
    +        {num: '3.0.12', truth: false}
    +      ]}
    +  ];
    +  checkEachUserAgentDetected(userAgents, 'ANDROID');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragent.js b/mapbox-gl/closure-library/closure/goog/useragent/useragent.js
    new file mode 100644
    index 0000000000..c8999024c1
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragent.js
    @@ -0,0 +1,519 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Rendering engine detection.
    + * @see <a href="http://www.useragentstring.com/">User agent strings</a>
    + * For information on the browser brand (such as Safari versus Chrome), see
    + * goog.userAgent.product.
    + * @author arv@google.com (Erik Arvidsson)
    + * @see ../demos/useragent.html
    + */
    +
    +goog.provide('goog.userAgent');
    +
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.labs.userAgent.engine');
    +goog.require('goog.labs.userAgent.platform');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.string');
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser is IE.
    + */
    +goog.define('goog.userAgent.ASSUME_IE', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser is GECKO.
    + */
    +goog.define('goog.userAgent.ASSUME_GECKO', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
    + */
    +goog.define('goog.userAgent.ASSUME_WEBKIT', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser is a
    + *     mobile device running WebKit e.g. iPhone or Android.
    + */
    +goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
    +
    +
    +/**
    + * @define {boolean} Whether we know at compile-time that the browser is OPERA.
    + */
    +goog.define('goog.userAgent.ASSUME_OPERA', false);
    +
    +
    +/**
    + * @define {boolean} Whether the
    + *     {@code goog.userAgent.isVersionOrHigher}
    + *     function will return true for any version.
    + */
    +goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
    +
    +
    +/**
    + * Whether we know the browser engine at compile-time.
    + * @type {boolean}
    + * @private
    + */
    +goog.userAgent.BROWSER_KNOWN_ =
    +    goog.userAgent.ASSUME_IE ||
    +    goog.userAgent.ASSUME_GECKO ||
    +    goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    +    goog.userAgent.ASSUME_WEBKIT ||
    +    goog.userAgent.ASSUME_OPERA;
    +
    +
    +/**
    + * Returns the userAgent string for the current browser.
    + *
    + * @return {string} The userAgent string.
    + */
    +goog.userAgent.getUserAgentString = function() {
    +  return goog.labs.userAgent.util.getUserAgent();
    +};
    +
    +
    +/**
    + * TODO(nnaze): Change type to "Navigator" and update compilation targets.
    + * @return {Object} The native navigator object.
    + */
    +goog.userAgent.getNavigator = function() {
    +  // Need a local navigator reference instead of using the global one,
    +  // to avoid the rare case where they reference different objects.
    +  // (in a WorkerPool, for example).
    +  return goog.global['navigator'] || null;
    +};
    +
    +
    +/**
    + * Whether the user agent is Opera.
    + * @type {boolean}
    + */
    +goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
    +    goog.userAgent.ASSUME_OPERA :
    +    goog.labs.userAgent.browser.isOpera();
    +
    +
    +/**
    + * Whether the user agent is Internet Explorer.
    + * @type {boolean}
    + */
    +goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
    +    goog.userAgent.ASSUME_IE :
    +    goog.labs.userAgent.browser.isIE();
    +
    +
    +/**
    + * Whether the user agent is Gecko. Gecko is the rendering engine used by
    + * Mozilla, Firefox, and others.
    + * @type {boolean}
    + */
    +goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
    +    goog.userAgent.ASSUME_GECKO :
    +    goog.labs.userAgent.engine.isGecko();
    +
    +
    +/**
    + * Whether the user agent is WebKit. WebKit is the rendering engine that
    + * Safari, Android and others use.
    + * @type {boolean}
    + */
    +goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
    +    goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
    +    goog.labs.userAgent.engine.isWebKit();
    +
    +
    +/**
    + * Whether the user agent is running on a mobile device.
    + *
    + * This is a separate function so that the logic can be tested.
    + *
    + * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
    + *
    + * @return {boolean} Whether the user agent is running on a mobile device.
    + * @private
    + */
    +goog.userAgent.isMobile_ = function() {
    +  return goog.userAgent.WEBKIT &&
    +         goog.labs.userAgent.util.matchUserAgent('Mobile');
    +};
    +
    +
    +/**
    + * Whether the user agent is running on a mobile device.
    + *
    + * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
    + *   is promoted as the gecko/webkit logic is likely inaccurate.
    + *
    + * @type {boolean}
    + */
    +goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
    +                        goog.userAgent.isMobile_();
    +
    +
    +/**
    + * Used while transitioning code to use WEBKIT instead.
    + * @type {boolean}
    + * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
    + * TODO(nicksantos): Delete this from goog.userAgent.
    + */
    +goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
    +
    +
    +/**
    + * @return {string} the platform (operating system) the user agent is running
    + *     on. Default to empty string because navigator.platform may not be defined
    + *     (on Rhino, for example).
    + * @private
    + */
    +goog.userAgent.determinePlatform_ = function() {
    +  var navigator = goog.userAgent.getNavigator();
    +  return navigator && navigator.platform || '';
    +};
    +
    +
    +/**
    + * The platform (operating system) the user agent is running on. Default to
    + * empty string because navigator.platform may not be defined (on Rhino, for
    + * example).
    + * @type {string}
    + */
    +goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on a Macintosh operating
    + *     system.
    + */
    +goog.define('goog.userAgent.ASSUME_MAC', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on a Windows operating
    + *     system.
    + */
    +goog.define('goog.userAgent.ASSUME_WINDOWS', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on a Linux operating
    + *     system.
    + */
    +goog.define('goog.userAgent.ASSUME_LINUX', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on a X11 windowing
    + *     system.
    + */
    +goog.define('goog.userAgent.ASSUME_X11', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on Android.
    + */
    +goog.define('goog.userAgent.ASSUME_ANDROID', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on an iPhone.
    + */
    +goog.define('goog.userAgent.ASSUME_IPHONE', false);
    +
    +
    +/**
    + * @define {boolean} Whether the user agent is running on an iPad.
    + */
    +goog.define('goog.userAgent.ASSUME_IPAD', false);
    +
    +
    +/**
    + * @type {boolean}
    + * @private
    + */
    +goog.userAgent.PLATFORM_KNOWN_ =
    +    goog.userAgent.ASSUME_MAC ||
    +    goog.userAgent.ASSUME_WINDOWS ||
    +    goog.userAgent.ASSUME_LINUX ||
    +    goog.userAgent.ASSUME_X11 ||
    +    goog.userAgent.ASSUME_ANDROID ||
    +    goog.userAgent.ASSUME_IPHONE ||
    +    goog.userAgent.ASSUME_IPAD;
    +
    +
    +/**
    + * Whether the user agent is running on a Macintosh operating system.
    + * @type {boolean}
    + */
    +goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_MAC : goog.labs.userAgent.platform.isMacintosh();
    +
    +
    +/**
    + * Whether the user agent is running on a Windows operating system.
    + * @type {boolean}
    + */
    +goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_WINDOWS :
    +    goog.labs.userAgent.platform.isWindows();
    +
    +
    +/**
    + * Whether the user agent is Linux per the legacy behavior of
    + * goog.userAgent.LINUX, which considered ChromeOS to also be
    + * Linux.
    + * @return {boolean}
    + * @private
    + */
    +goog.userAgent.isLegacyLinux_ = function() {
    +  return goog.labs.userAgent.platform.isLinux() ||
    +      goog.labs.userAgent.platform.isChromeOS();
    +};
    +
    +
    +/**
    + * Whether the user agent is running on a Linux operating system.
    + *
    + * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
    + * while goog.labs.userAgent.platform considers ChromeOS and
    + * Linux to be different OSes.
    + *
    + * @type {boolean}
    + */
    +goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_LINUX :
    +    goog.userAgent.isLegacyLinux_();
    +
    +
    +/**
    + * @return {boolean} Whether the user agent is an X11 windowing system.
    + * @private
    + */
    +goog.userAgent.isX11_ = function() {
    +  var navigator = goog.userAgent.getNavigator();
    +  return !!navigator &&
    +      goog.string.contains(navigator['appVersion'] || '', 'X11');
    +};
    +
    +
    +/**
    + * Whether the user agent is running on a X11 windowing system.
    + * @type {boolean}
    + */
    +goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_X11 :
    +    goog.userAgent.isX11_();
    +
    +
    +/**
    + * Whether the user agent is running on Android.
    + * @type {boolean}
    + */
    +goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_ANDROID :
    +    goog.labs.userAgent.platform.isAndroid();
    +
    +
    +/**
    + * Whether the user agent is running on an iPhone.
    + * @type {boolean}
    + */
    +goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_IPHONE :
    +    goog.labs.userAgent.platform.isIphone();
    +
    +
    +/**
    + * Whether the user agent is running on an iPad.
    + * @type {boolean}
    + */
    +goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
    +    goog.userAgent.ASSUME_IPAD :
    +    goog.labs.userAgent.platform.isIpad();
    +
    +
    +/**
    + * @return {string} The string that describes the version number of the user
    + *     agent.
    + * @private
    + */
    +goog.userAgent.determineVersion_ = function() {
    +  // All browsers have different ways to detect the version and they all have
    +  // different naming schemes.
    +
    +  // version is a string rather than a number because it may contain 'b', 'a',
    +  // and so on.
    +  var version = '', re;
    +
    +  if (goog.userAgent.OPERA && goog.global['opera']) {
    +    var operaVersion = goog.global['opera'].version;
    +    return goog.isFunction(operaVersion) ? operaVersion() : operaVersion;
    +  }
    +
    +  if (goog.userAgent.GECKO) {
    +    re = /rv\:([^\);]+)(\)|;)/;
    +  } else if (goog.userAgent.IE) {
    +    re = /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/;
    +  } else if (goog.userAgent.WEBKIT) {
    +    // WebKit/125.4
    +    re = /WebKit\/(\S+)/;
    +  }
    +
    +  if (re) {
    +    var arr = re.exec(goog.userAgent.getUserAgentString());
    +    version = arr ? arr[1] : '';
    +  }
    +
    +  if (goog.userAgent.IE) {
    +    // IE9 can be in document mode 9 but be reporting an inconsistent user agent
    +    // version.  If it is identifying as a version lower than 9 we take the
    +    // documentMode as the version instead.  IE8 has similar behavior.
    +    // It is recommended to set the X-UA-Compatible header to ensure that IE9
    +    // uses documentMode 9.
    +    var docMode = goog.userAgent.getDocumentMode_();
    +    if (docMode > parseFloat(version)) {
    +      return String(docMode);
    +    }
    +  }
    +
    +  return version;
    +};
    +
    +
    +/**
    + * @return {number|undefined} Returns the document mode (for testing).
    + * @private
    + */
    +goog.userAgent.getDocumentMode_ = function() {
    +  // NOTE(user): goog.userAgent may be used in context where there is no DOM.
    +  var doc = goog.global['document'];
    +  return doc ? doc['documentMode'] : undefined;
    +};
    +
    +
    +/**
    + * The version of the user agent. This is a string because it might contain
    + * 'b' (as in beta) as well as multiple dots.
    + * @type {string}
    + */
    +goog.userAgent.VERSION = goog.userAgent.determineVersion_();
    +
    +
    +/**
    + * Compares two version numbers.
    + *
    + * @param {string} v1 Version of first item.
    + * @param {string} v2 Version of second item.
    + *
    + * @return {number}  1 if first argument is higher
    + *                   0 if arguments are equal
    + *                  -1 if second argument is higher.
    + * @deprecated Use goog.string.compareVersions.
    + */
    +goog.userAgent.compare = function(v1, v2) {
    +  return goog.string.compareVersions(v1, v2);
    +};
    +
    +
    +/**
    + * Cache for {@link goog.userAgent.isVersionOrHigher}.
    + * Calls to compareVersions are surprisingly expensive and, as a browser's
    + * version number is unlikely to change during a session, we cache the results.
    + * @const
    + * @private
    + */
    +goog.userAgent.isVersionOrHigherCache_ = {};
    +
    +
    +/**
    + * Whether the user agent version is higher or the same as the given version.
    + * NOTE: When checking the version numbers for Firefox and Safari, be sure to
    + * use the engine's version, not the browser's version number.  For example,
    + * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
    + * Opera and Internet Explorer versions match the product release number.<br>
    + * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
    + *     Webkit</a>
    + * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
    + *
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the user agent version is higher or the same as
    + *     the given version.
    + */
    +goog.userAgent.isVersionOrHigher = function(version) {
    +  return goog.userAgent.ASSUME_ANY_VERSION ||
    +      goog.userAgent.isVersionOrHigherCache_[version] ||
    +      (goog.userAgent.isVersionOrHigherCache_[version] =
    +          goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
    +};
    +
    +
    +/**
    + * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
    + * @param {string|number} version The version to check.
    + * @return {boolean} Whether the user agent version is higher or the same as
    + *     the given version.
    + * @deprecated Use goog.userAgent.isVersionOrHigher().
    + */
    +goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
    +
    +
    +/**
    + * Whether the IE effective document mode is higher or the same as the given
    + * document mode version.
    + * NOTE: Only for IE, return false for another browser.
    + *
    + * @param {number} documentMode The document mode version to check.
    + * @return {boolean} Whether the IE effective document mode is higher or the
    + *     same as the given version.
    + */
    +goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
    +  return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
    +};
    +
    +
    +/**
    + * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
    + * @param {number} version The version to check.
    + * @return {boolean} Whether the IE effective document mode is higher or the
    + *      same as the given version.
    + * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
    + */
    +goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
    +
    +
    +/**
    + * For IE version < 7, documentMode is undefined, so attempt to use the
    + * CSS1Compat property to see if we are in standards mode. If we are in
    + * standards mode, treat the browser version as the document mode. Otherwise,
    + * IE is emulating version 5.
    + * @type {number|undefined}
    + * @const
    + */
    +goog.userAgent.DOCUMENT_MODE = (function() {
    +  var doc = goog.global['document'];
    +  if (!doc || !goog.userAgent.IE) {
    +    return undefined;
    +  }
    +  var mode = goog.userAgent.getDocumentMode_();
    +  return mode || (doc['compatMode'] == 'CSS1Compat' ?
    +      parseInt(goog.userAgent.VERSION, 10) : 5);
    +})();
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.html b/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.html
    new file mode 100644
    index 0000000000..35d5db10e9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.html
    @@ -0,0 +1,21 @@
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <title>
    +   Closure Unit Tests - goog.userAgent quirks
    +  </title>
    +  <meta http-equiv="X-UA-Compatible" content="IE=5">
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgentQuirksTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.js b/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.js
    new file mode 100644
    index 0000000000..960d0be432
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragent_quirks_test.js
    @@ -0,0 +1,25 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgentQuirksTest');
    +goog.setTestOnly('goog.userAgentQuirksTest');
    +
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +
    +function testGetDocumentModeInQuirksMode() {
    +  // This test file is forcing quirks mode.
    +  var expected = goog.userAgent.IE ? 5 : undefined;
    +  assertEquals(expected, goog.userAgent.DOCUMENT_MODE);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.html b/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.html
    new file mode 100644
    index 0000000000..72429ee232
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.html
    @@ -0,0 +1,22 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.userAgent
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.userAgentTest');
    +  </script>
    + </head>
    + <body>
    + </body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.js b/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.js
    new file mode 100644
    index 0000000000..c2245a620e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragent_test.js
    @@ -0,0 +1,290 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.userAgentTest');
    +goog.setTestOnly('goog.userAgentTest');
    +
    +goog.require('goog.array');
    +goog.require('goog.labs.userAgent.platform');
    +goog.require('goog.labs.userAgent.testAgents');
    +goog.require('goog.labs.userAgent.util');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgentTestUtil');
    +
    +
    +var documentMode;
    +goog.userAgent.getDocumentMode_ = function() {
    +  return documentMode;
    +};
    +
    +
    +var propertyReplacer = new goog.testing.PropertyReplacer();
    +
    +var UserAgents = {
    +  GECKO: 'GECKO',
    +  IE: 'IE',
    +  OPERA: 'OPERA',
    +  WEBKIT: 'WEBKIT'
    +};
    +
    +
    +function tearDown() {
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  documentMode = undefined;
    +  propertyReplacer.reset();
    +}
    +
    +
    +/**
    + * Test browser detection for a user agent configuration.
    + * @param {Array<number>} expectedAgents Array of expected userAgents.
    + * @param {string} uaString User agent string.
    + * @param {string=} opt_product Navigator product string.
    + * @param {string=} opt_vendor Navigator vendor string.
    + */
    +function assertUserAgent(expectedAgents, uaString, opt_product, opt_vendor) {
    +  var mockGlobal = {
    +    'navigator': {
    +      'userAgent': uaString,
    +      'product': opt_product,
    +      'vendor': opt_vendor
    +    }
    +  };
    +  propertyReplacer.set(goog, 'global', mockGlobal);
    +
    +  goog.labs.userAgent.util.setUserAgent(null);
    +
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  for (var ua in UserAgents) {
    +    var isExpected = goog.array.contains(expectedAgents, UserAgents[ua]);
    +    assertEquals(isExpected,
    +        goog.userAgentTestUtil.getUserAgentDetected(UserAgents[ua]));
    +  }
    +}
    +
    +function testOperaInit() {
    +  var mockOpera = {
    +    'version': function() {
    +      return '9.20';
    +    }
    +  };
    +
    +  var mockGlobal = {
    +    'navigator': {
    +      'userAgent': 'Opera/9.20 (Windows NT 5.1; U; de),gzip(gfe)'
    +    },
    +    'opera': mockOpera
    +  };
    +  propertyReplacer.set(goog, 'global', mockGlobal);
    +
    +  propertyReplacer.set(goog.userAgent, 'getUserAgentString', function() {
    +    return 'Opera/9.20 (Windows NT 5.1; U; de),gzip(gfe)';
    +  });
    +
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  assertTrue(goog.userAgent.OPERA);
    +  assertEquals('9.20', goog.userAgent.VERSION);
    +
    +  // What if 'opera' global has been overwritten?
    +  // We must degrade gracefully (rather than throwing JS errors).
    +  propertyReplacer.set(goog.global, 'opera', 'bobloblaw');
    +
    +  // NOTE(nnaze): window.opera is now ignored with the migration to
    +  // goog.labs.userAgent.*. Version is expected to should stay the same.
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  assertUndefined(goog.userAgent.VERSION);
    +}
    +
    +function testCompare() {
    +  assertTrue('exact equality broken',
    +             goog.userAgent.compare('1.0', '1.0') == 0);
    +  assertTrue('mutlidot equality broken',
    +             goog.userAgent.compare('1.0.0.0', '1.0') == 0);
    +  assertTrue('less than broken',
    +             goog.userAgent.compare('1.0.2.1', '1.1') < 0);
    +  assertTrue('greater than broken',
    +             goog.userAgent.compare('1.1', '1.0.2.1') > 0);
    +
    +  assertTrue('b broken', goog.userAgent.compare('1.1', '1.1b') > 0);
    +  assertTrue('b broken', goog.userAgent.compare('1.1b', '1.1') < 0);
    +  assertTrue('b broken', goog.userAgent.compare('1.1b', '1.1b') == 0);
    +
    +  assertTrue('b>a broken', goog.userAgent.compare('1.1b', '1.1a') > 0);
    +  assertTrue('a<b broken', goog.userAgent.compare('1.1a', '1.1b') < 0);
    +}
    +
    +function testGecko() {
    +
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; nl-NL; rv:1.7.5)' +
    +      'Gecko/20041202 Gecko/1.0', '1.7.5');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.7.6)' +
    +      'Gecko/20050512 Gecko', '1.7.6');
    +  assertGecko('Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.8)' +
    +      'Gecko/20050609 Gecko/1.0.4', '1.7.8');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9)' +
    +      'Gecko/20050711 Gecko/1.0.5', '1.7.9');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10)' +
    +      'Gecko/20050716 Gecko/1.0.6', '1.7.10');
    +  assertGecko('Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-GB;' +
    +      'rv:1.7.10) Gecko/20050717 Gecko/1.0.6', '1.7.10');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12)' +
    +      'Gecko/20050915 Gecko/1.0.7', '1.7.12');
    +  assertGecko('Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US;' +
    +      'rv:1.7.12) Gecko/20050915 Gecko/1.0.7', '1.7.12');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4)' +
    +      'Gecko/20050908 Gecko/1.4', '1.8b4');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8)' +
    +      'Gecko/20051107 Gecko/1.5', '1.8');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.1)' +
    +      'Gecko/20060111 Gecko/1.5.0.1', '1.8.0.1');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.1)' +
    +      'Gecko/20060111 Gecko/1.5.0.1', '1.8.0.1');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.2)' +
    +      'Gecko/20060308 Gecko/1.5.0.2', '1.8.0.2');
    +  assertGecko('Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US;' +
    +      'rv:1.8.0.3) Gecko/20060426 Gecko/1.5.0.3', '1.8.0.3');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.3)' +
    +      'Gecko/20060426 Gecko/1.5.0.3', '1.8.0.3');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4)' +
    +      'Gecko/20060508 Gecko/1.5.0.4', '1.8.0.4');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4)' +
    +      'Gecko/20060508 Gecko/1.5.0.4', '1.8.0.4');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.0.4)' +
    +      'Gecko/20060508 Gecko/1.5.0.4', '1.8.0.4');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.0.6)' +
    +      'Gecko/20060728 Gecko/1.5.0.6', '1.8.0.6');
    +  assertGecko('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.6)' +
    +      'Gecko/20060808 Fedora/1.5.0.6-2.fc5 Gecko/1.5.0.6 pango-text',
    +      '1.8.0.6');
    +  assertGecko('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8)' +
    +      'Gecko/20060321 Gecko/2.0a1', '1.8');
    +  assertGecko('Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/6.0 Firefox/6.0',
    +      '6.0');
    +}
    +
    +function testIe() {
    +  assertIe('Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)', '5.01');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC)', '5.17');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)', '5.23');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)', '5.5');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 6.0; MSN 2.5; Windows 98)', '6.0');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)', '6.0');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; ' +
    +      '.NET CLR 1.1.4322)', '6.0');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; ' +
    +      '.NET CLR 2.0.50727)', '6.0');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1)', '7.0b');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 7.0b; Win32)', '7.0b');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)', '7.0b');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1;' +
    +      'Arcor 5.005; .NET CLR 1.0.3705; .NET CLR 1.1.4322)', '7.0');
    +  assertIe(
    +      'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko', '11.0');
    +}
    +
    +function testIeDocumentModeOverride() {
    +  documentMode = 9;
    +  assertIe('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0',
    +           '9');
    +  assertIe('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/5.0',
    +           '9');
    +
    +  documentMode = 8;
    +  assertIe('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/5.0',
    +           '8.0');
    +}
    +
    +function testDocumentModeInStandardsMode() {
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  var expectedMode = goog.userAgent.IE ? parseInt(goog.userAgent.VERSION) :
    +                                         undefined;
    +  assertEquals(expectedMode, goog.userAgent.DOCUMENT_MODE);
    +}
    +
    +function testOpera() {
    +  var assertOpera = function(uaString) {
    +    assertUserAgent([UserAgents.OPERA], uaString);
    +  };
    +  assertOpera('Opera/7.23 (Windows 98; U) [en]');
    +  assertOpera('Opera/8.00 (Windows NT 5.1; U; en)');
    +  assertOpera('Opera/8.0 (X11; Linux i686; U; cs)');
    +  assertOpera('Opera/8.02 (Windows NT 5.1; U; en)');
    +  assertOpera('Opera/8.50 (Windows NT 5.1; U; en)');
    +  assertOpera('Opera/8.5 (X11; Linux i686; U; cs)');
    +  assertOpera('Opera/8.51 (Windows NT 5.1; U; en)');
    +  assertOpera('Opera/9.0 (Windows NT 5.0; U; en)');
    +  assertOpera('Opera/9.00 (Macintosh; PPC Mac OS X; U; en)');
    +  assertOpera('Opera/9.00 (Windows NT 5.1; U; en)');
    +  assertOpera('Opera/9.00 (Windows NT 5.2; U; en)');
    +  assertOpera('Opera/9.00 (Windows NT 6.0; U; en)');
    +}
    +
    +function testWebkit() {
    +  var testAgents = goog.labs.userAgent.testAgents;
    +  assertWebkit(testAgents.ANDROID_BROWSER_403);
    +  assertWebkit(testAgents.ANDROID_BROWSER_403_ALT);
    +}
    +
    +function testUnknownBrowser() {
    +  assertUserAgent([], 'MyWebBrowser');
    +  assertUserAgent([], undefined);
    +}
    +
    +function testNoNavigator() {
    +  // global object has no "navigator" property.
    +  var mockGlobal = {};
    +  propertyReplacer.set(goog, 'global', mockGlobal);
    +  goog.labs.userAgent.util.setUserAgent(null);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +
    +  assertEquals('Platform should be the empty string', '',
    +      goog.userAgent.PLATFORM);
    +  assertEquals('Version should be the empty string', '',
    +      goog.userAgent.VERSION);
    +}
    +
    +function testLegacyChromeOsAndLinux() {
    +  // As a legacy behavior, goog.userAgent.LINUX considers
    +  // ChromeOS to be Linux.
    +  // goog.labs.userAgent.platform.isLinux() does not.
    +  goog.labs.userAgent.util.setUserAgent(
    +      goog.labs.userAgent.testAgents.CHROME_OS);
    +  goog.userAgentTestUtil.reinitializeUserAgent();
    +  assertTrue(goog.userAgent.LINUX);
    +  assertFalse(goog.labs.userAgent.platform.isLinux());
    +}
    +
    +function assertIe(uaString, expectedVersion) {
    +  assertUserAgent([UserAgents.IE], uaString);
    +  assertEquals('User agent ' + uaString + ' should have had version ' +
    +      expectedVersion + ' but had ' + goog.userAgent.VERSION,
    +      expectedVersion,
    +      goog.userAgent.VERSION);
    +}
    +
    +function assertGecko(uaString, expectedVersion) {
    +  assertUserAgent([UserAgents.GECKO], uaString, 'Gecko');
    +  assertEquals('User agent ' + uaString + ' should have had version ' +
    +      expectedVersion + ' but had ' + goog.userAgent.VERSION,
    +      expectedVersion,
    +      goog.userAgent.VERSION);
    +}
    +
    +function assertWebkit(uaString) {
    +  assertUserAgent([UserAgents.WEBKIT], uaString, 'WebKit');
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/useragent/useragenttestutil.js b/mapbox-gl/closure-library/closure/goog/useragent/useragenttestutil.js
    new file mode 100644
    index 0000000000..186c080b4f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/useragent/useragenttestutil.js
    @@ -0,0 +1,116 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Shared test function to reset the constants in
    + * goog.userAgent.*
    + */
    +
    +goog.provide('goog.userAgentTestUtil');
    +goog.provide('goog.userAgentTestUtil.UserAgents');
    +
    +goog.require('goog.labs.userAgent.browser');
    +goog.require('goog.labs.userAgent.engine');
    +goog.require('goog.labs.userAgent.platform');
    +goog.require('goog.userAgent');
    +goog.require('goog.userAgent.keyboard');
    +goog.require('goog.userAgent.platform');
    +goog.require('goog.userAgent.product');
    +/** @suppress {extraRequire} */
    +goog.require('goog.userAgent.product.isVersion');
    +
    +goog.setTestOnly('goog.userAgentTestUtil');
    +
    +
    +/**
    + * Rerun the initialization code to set all of the goog.userAgent constants.
    + * @suppress {accessControls}
    + */
    +goog.userAgentTestUtil.reinitializeUserAgent = function() {
    +  // Unfortunately we can't isolate the useragent setting in a function
    +  // we can call, because things rely on it compiling to nothing when
    +  // one of the ASSUME flags is set, and the compiler isn't smart enough
    +  // to do that when the setting is done inside a function that's inlined.
    +  goog.userAgent.OPERA = goog.labs.userAgent.browser.isOpera();
    +  goog.userAgent.IE = goog.labs.userAgent.browser.isIE();
    +  goog.userAgent.GECKO = goog.labs.userAgent.engine.isGecko();
    +  goog.userAgent.WEBKIT = goog.labs.userAgent.engine.isWebKit();
    +  goog.userAgent.MOBILE = goog.userAgent.isMobile_();
    +  goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
    +
    +  // Platform in goog.userAgent.
    +  goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
    +
    +  goog.userAgent.MAC = goog.labs.userAgent.platform.isMacintosh();
    +  goog.userAgent.WINDOWS = goog.labs.userAgent.platform.isWindows();
    +  goog.userAgent.LINUX = goog.userAgent.isLegacyLinux_();
    +  goog.userAgent.X11 = goog.userAgent.isX11_();
    +  goog.userAgent.ANDROID = goog.labs.userAgent.platform.isAndroid();
    +  goog.userAgent.IPAD = goog.labs.userAgent.platform.isIpad();
    +  goog.userAgent.IPHONE = goog.labs.userAgent.platform.isIphone();
    +  goog.userAgent.VERSION = goog.userAgent.determineVersion_();
    +
    +  // Platform in goog.userAgent.platform.
    +  goog.userAgent.platform.VERSION = goog.userAgent.platform.determineVersion_();
    +
    +  // Update goog.userAgent.product
    +  goog.userAgent.product.init_();
    +  goog.userAgent.product.ANDROID = goog.userAgent.product.detectedAndroid_;
    +  goog.userAgent.product.CAMINO = goog.userAgent.product.detectedCamino_;
    +  goog.userAgent.product.CHROME = goog.userAgent.product.detectedChrome_;
    +  goog.userAgent.product.FIREFOX = goog.userAgent.product.detectedFirefox_;
    +  goog.userAgent.product.IE = goog.userAgent.IE;
    +  goog.userAgent.product.IPAD = goog.userAgent.product.detectedIpad_;
    +  goog.userAgent.product.IPHONE = goog.userAgent.product.detectedIphone_;
    +  goog.userAgent.product.OPERA = goog.userAgent.OPERA;
    +  goog.userAgent.product.SAFARI = goog.userAgent.product.detectedSafari_;
    +  goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_();
    +
    +  // goog.userAgent.keyboard
    +  goog.userAgent.keyboard.MAC_KEYBOARD =
    +      goog.userAgent.keyboard.determineMacKeyboard_();
    +};
    +
    +
    +/**
    + * Browser definitions.
    + * @enum {string}
    + */
    +goog.userAgentTestUtil.UserAgents = {
    +  GECKO: 'GECKO',
    +  IE: 'IE',
    +  OPERA: 'OPERA',
    +  WEBKIT: 'WEBKIT'
    +};
    +
    +
    +/**
    + * Return whether a given user agent has been detected.
    + * @param {string} agent Value in UserAgents.
    + * @return {boolean} Whether the user agent has been detected.
    + */
    +goog.userAgentTestUtil.getUserAgentDetected = function(agent) {
    +  switch (agent) {
    +    case goog.userAgentTestUtil.UserAgents.GECKO:
    +      return goog.userAgent.GECKO;
    +    case goog.userAgentTestUtil.UserAgents.IE:
    +      return goog.userAgent.IE;
    +    case goog.userAgentTestUtil.UserAgents.OPERA:
    +      return goog.userAgent.OPERA;
    +    case goog.userAgentTestUtil.UserAgents.WEBKIT:
    +      return goog.userAgent.WEBKIT;
    +  }
    +
    +  throw Error('Unrecognized user agent');
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/float32array.js b/mapbox-gl/closure-library/closure/goog/vec/float32array.js
    new file mode 100644
    index 0000000000..87e37ecba4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/float32array.js
    @@ -0,0 +1,111 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Supplies a Float32Array implementation that implements
    + *     most of the Float32Array spec and that can be used when a built-in
    + *     implementation is not available.
    + *
    + *     Note that if no existing Float32Array implementation is found then
    + *     this class and all its public properties are exported as Float32Array.
    + *
    + *     Adding support for the other TypedArray classes here does not make sense
    + *     since this vector math library only needs Float32Array.
    + *
    + */
    +goog.provide('goog.vec.Float32Array');
    +
    +
    +
    +/**
    + * Constructs a new Float32Array. The new array is initialized to all zeros.
    + *
    + * @param {goog.vec.Float32Array|Array|ArrayBuffer|number} p0
    + *     The length of the array, or an array to initialize the contents of the
    + *     new Float32Array.
    + * @constructor
    + * @final
    + */
    +goog.vec.Float32Array = function(p0) {
    +  this.length = /** @type {number} */ (p0.length || p0);
    +  for (var i = 0; i < this.length; i++) {
    +    this[i] = p0[i] || 0;
    +  }
    +};
    +
    +
    +/**
    + * The number of bytes in an element (as defined by the Typed Array
    + * specification).
    + *
    + * @type {number}
    + */
    +goog.vec.Float32Array.BYTES_PER_ELEMENT = 4;
    +
    +
    +/**
    + * The number of bytes in an element (as defined by the Typed Array
    + * specification).
    + *
    + * @type {number}
    + */
    +goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT = 4;
    +
    +
    +/**
    + * Sets elements of the array.
    + * @param {Array<number>|Float32Array} values The array of values.
    + * @param {number=} opt_offset The offset in this array to start.
    + */
    +goog.vec.Float32Array.prototype.set = function(values, opt_offset) {
    +  opt_offset = opt_offset || 0;
    +  for (var i = 0; i < values.length && opt_offset + i < this.length; i++) {
    +    this[opt_offset + i] = values[i];
    +  }
    +};
    +
    +
    +/**
    + * Creates a string representation of this array.
    + * @return {string} The string version of this array.
    + * @override
    + */
    +goog.vec.Float32Array.prototype.toString = Array.prototype.join;
    +
    +
    +/**
    + * Note that we cannot implement the subarray() or (deprecated) slice()
    + * methods properly since doing so would require being able to overload
    + * the [] operator which is not possible in javascript.  So we leave
    + * them unimplemented.  Any attempt to call these methods will just result
    + * in a javascript error since we leave them undefined.
    + */
    +
    +
    +/**
    + * If no existing Float32Array implementation is found then we export
    + * goog.vec.Float32Array as Float32Array.
    + */
    +if (typeof Float32Array == 'undefined') {
    +  goog.exportProperty(goog.vec.Float32Array, 'BYTES_PER_ELEMENT',
    +                      goog.vec.Float32Array.BYTES_PER_ELEMENT);
    +  goog.exportProperty(goog.vec.Float32Array.prototype, 'BYTES_PER_ELEMENT',
    +                      goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT);
    +  goog.exportProperty(goog.vec.Float32Array.prototype, 'set',
    +                      goog.vec.Float32Array.prototype.set);
    +  goog.exportProperty(goog.vec.Float32Array.prototype, 'toString',
    +                      goog.vec.Float32Array.prototype.toString);
    +  goog.exportSymbol('Float32Array', goog.vec.Float32Array);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/float32array_test.html b/mapbox-gl/closure-library/closure/goog/vec/float32array_test.html
    new file mode 100644
    index 0000000000..227797d47a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/float32array_test.html
    @@ -0,0 +1,69 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Float32Array</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructorInitializesElementsToZero() {
    +    var f = new goog.vec.Float32Array(3);
    +    assertEquals(3, f.length);
    +    assertEquals(0, f[0]);
    +    assertEquals(0, f[1]);
    +    assertEquals(0, f[2]);
    +    assertEquals(4, f.BYTES_PER_ELEMENT);
    +    assertEquals(4, goog.vec.Float32Array.BYTES_PER_ELEMENT);
    +  }
    +
    +  function testConstructorWithArrayAsArgument() {
    +    var f0 = new goog.vec.Float32Array([0, 0, 1, 0]);
    +    var f1 = new goog.vec.Float32Array(4);
    +    f1[0] = 0;
    +    f1[1] = 0;
    +    f1[2] = 1;
    +    f1[3] = 0;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testSet() {
    +    var f0 = new goog.vec.Float32Array(4);
    +    var f1 = new goog.vec.Float32Array(4);
    +    f0.set([1, 2, 3, 4]);
    +    f1[0] = 1;
    +    f1[1] = 2;
    +    f1[2] = 3;
    +    f1[3] = 4;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testSetWithOffset() {
    +    var f0 = new goog.vec.Float32Array(4);
    +    var f1 = new goog.vec.Float32Array(4);
    +    f0.set([5], 1);
    +    f1[0] = 0;
    +    f1[1] = 5;
    +    f1[2] = 0;
    +    f1[3] = 0;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testToString() {
    +    var f = new goog.vec.Float32Array([4, 3, 2, 1]);
    +    assertEquals('4,3,2,1', f.toString());
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/float64array.js b/mapbox-gl/closure-library/closure/goog/vec/float64array.js
    new file mode 100644
    index 0000000000..a71c97d101
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/float64array.js
    @@ -0,0 +1,118 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Supplies a Float64Array implementation that implements
    + * most of the Float64Array spec and that can be used when a built-in
    + * implementation is not available.
    + *
    + * Note that if no existing Float64Array implementation is found then this
    + * class and all its public properties are exported as Float64Array.
    + *
    + * Adding support for the other TypedArray classes here does not make sense
    + * since this vector math library only needs Float32Array and Float64Array.
    + *
    + */
    +goog.provide('goog.vec.Float64Array');
    +
    +
    +
    +/**
    + * Constructs a new Float64Array. The new array is initialized to all zeros.
    + *
    + * @param {goog.vec.Float64Array|Array|ArrayBuffer|number} p0
    + *     The length of the array, or an array to initialize the contents of the
    + *     new Float64Array.
    + * @constructor
    + * @final
    + */
    +goog.vec.Float64Array = function(p0) {
    +  this.length = /** @type {number} */ (p0.length || p0);
    +  for (var i = 0; i < this.length; i++) {
    +    this[i] = p0[i] || 0;
    +  }
    +};
    +
    +
    +/**
    + * The number of bytes in an element (as defined by the Typed Array
    + * specification).
    + *
    + * @type {number}
    + */
    +goog.vec.Float64Array.BYTES_PER_ELEMENT = 8;
    +
    +
    +/**
    + * The number of bytes in an element (as defined by the Typed Array
    + * specification).
    + *
    + * @type {number}
    + */
    +goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT = 8;
    +
    +
    +/**
    + * Sets elements of the array.
    + * @param {Array<number>|Float64Array} values The array of values.
    + * @param {number=} opt_offset The offset in this array to start.
    + */
    +goog.vec.Float64Array.prototype.set = function(values, opt_offset) {
    +  opt_offset = opt_offset || 0;
    +  for (var i = 0; i < values.length && opt_offset + i < this.length; i++) {
    +    this[opt_offset + i] = values[i];
    +  }
    +};
    +
    +
    +/**
    + * Creates a string representation of this array.
    + * @return {string} The string version of this array.
    + * @override
    + */
    +goog.vec.Float64Array.prototype.toString = Array.prototype.join;
    +
    +
    +/**
    + * Note that we cannot implement the subarray() or (deprecated) slice()
    + * methods properly since doing so would require being able to overload
    + * the [] operator which is not possible in javascript.  So we leave
    + * them unimplemented.  Any attempt to call these methods will just result
    + * in a javascript error since we leave them undefined.
    + */
    +
    +
    +/**
    + * If no existing Float64Array implementation is found then we export
    + * goog.vec.Float64Array as Float64Array.
    + */
    +if (typeof Float64Array == 'undefined') {
    +  try {
    +    goog.exportProperty(goog.vec.Float64Array, 'BYTES_PER_ELEMENT',
    +                        goog.vec.Float64Array.BYTES_PER_ELEMENT);
    +  } catch (float64ArrayError) {
    +    // Do nothing.  This code is in place to fix b/7225850, in which an error
    +    // is incorrectly thrown for Google TV on an old Chrome.
    +    // TODO(user): remove after that version is retired.
    +  }
    +
    +  goog.exportProperty(goog.vec.Float64Array.prototype, 'BYTES_PER_ELEMENT',
    +                      goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT);
    +  goog.exportProperty(goog.vec.Float64Array.prototype, 'set',
    +                      goog.vec.Float64Array.prototype.set);
    +  goog.exportProperty(goog.vec.Float64Array.prototype, 'toString',
    +                      goog.vec.Float64Array.prototype.toString);
    +  goog.exportSymbol('Float64Array', goog.vec.Float64Array);
    +}
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/float64array_test.html b/mapbox-gl/closure-library/closure/goog/vec/float64array_test.html
    new file mode 100644
    index 0000000000..0e72e00924
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/float64array_test.html
    @@ -0,0 +1,69 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Float64Array</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float64Array');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructorInitializesElementsToZero() {
    +    var f = new goog.vec.Float64Array(3);
    +    assertEquals(3, f.length);
    +    assertEquals(0, f[0]);
    +    assertEquals(0, f[1]);
    +    assertEquals(0, f[2]);
    +    assertEquals(8, f.BYTES_PER_ELEMENT);
    +    assertEquals(8, goog.vec.Float64Array.BYTES_PER_ELEMENT);
    +  }
    +
    +  function testConstructorWithArrayAsArgument() {
    +    var f0 = new goog.vec.Float64Array([0, 0, 1, 0]);
    +    var f1 = new goog.vec.Float64Array(4);
    +    f1[0] = 0;
    +    f1[1] = 0;
    +    f1[2] = 1;
    +    f1[3] = 0;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testSet() {
    +    var f0 = new goog.vec.Float64Array(4);
    +    var f1 = new goog.vec.Float64Array(4);
    +    f0.set([1, 2, 3, 4]);
    +    f1[0] = 1;
    +    f1[1] = 2;
    +    f1[2] = 3;
    +    f1[3] = 4;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testSetWithOffset() {
    +    var f0 = new goog.vec.Float64Array(4);
    +    var f1 = new goog.vec.Float64Array(4);
    +    f0.set([5], 1);
    +    f1[0] = 0;
    +    f1[1] = 5;
    +    f1[2] = 0;
    +    f1[3] = 0;
    +    assertObjectEquals(f0, f1);
    +  }
    +
    +  function testToString() {
    +    var f = new goog.vec.Float64Array([4, 3, 2, 1]);
    +    assertEquals('4,3,2,1', f.toString());
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3.js b/mapbox-gl/closure-library/closure/goog/vec/mat3.js
    new file mode 100644
    index 0000000000..cb747e344f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3.js
    @@ -0,0 +1,1211 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implements 3x3 matrices and their related functions which are
    + * compatible with WebGL. The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted. Matrix operations follow the mathematical form when multiplying
    + * vectors as follows: resultVec = matrix * vec.
    + *
    + * The matrices are stored in column-major order.
    + *
    + */
    +goog.provide('goog.vec.Mat3');
    +
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Mat3.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Mat3.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Mat3.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Mat3.AnyType;
    +
    +// The following two types are deprecated - use the above types instead.
    +/** @typedef {Float32Array} */ goog.vec.Mat3.Type;
    +/** @typedef {goog.vec.ArrayType} */ goog.vec.Mat3.Mat3Like;
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix of Float32.
    + * The use of the array directly instead of a class reduces overhead.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat3.Float32} The new matrix.
    + */
    +goog.vec.Mat3.createFloat32 = function() {
    +  return new Float32Array(9);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix of Float64.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat3.Float64} The new matrix.
    + */
    +goog.vec.Mat3.createFloat64 = function() {
    +  return new Float64Array(9);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix of Number.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat3.Number} The new matrix.
    + */
    +goog.vec.Mat3.createNumber = function() {
    +  var a = new Array(9);
    +  goog.vec.Mat3.setFromValues(a,
    +                              0, 0, 0,
    +                              0, 0, 0,
    +                              0, 0, 0);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix of Float32.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @deprecated Use createFloat32.
    + * @return {!goog.vec.Mat3.Type} The new matrix.
    + */
    +goog.vec.Mat3.create = function() {
    +  return goog.vec.Mat3.createFloat32();
    +};
    +
    +
    +/**
    + * Creates a 3x3 identity matrix of Float32.
    + *
    + * @return {!goog.vec.Mat3.Float32} The new 9 element array.
    + */
    +goog.vec.Mat3.createFloat32Identity = function() {
    +  var mat = goog.vec.Mat3.createFloat32();
    +  mat[0] = mat[4] = mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 3x3 identity matrix of Float64.
    + *
    + * @return {!goog.vec.Mat3.Float64} The new 9 element array.
    + */
    +goog.vec.Mat3.createFloat64Identity = function() {
    +  var mat = goog.vec.Mat3.createFloat64();
    +  mat[0] = mat[4] = mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 3x3 identity matrix of Number.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat3.Number} The new 9 element array.
    + */
    +goog.vec.Mat3.createNumberIdentity = function() {
    +  var a = new Array(9);
    +  goog.vec.Mat3.setFromValues(a,
    +                              1, 0, 0,
    +                              0, 1, 0,
    +                              0, 0, 1);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix of Float32.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @deprecated Use createFloat32Identity.
    + * @return {!goog.vec.Mat3.Type} The new 9 element array.
    + */
    +goog.vec.Mat3.createIdentity = function() {
    +  return goog.vec.Mat3.createFloat32Identity();
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float32 initialized from the given array.
    + *
    + * @param {goog.vec.Mat3.AnyType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat3.Float32} The new, nine element array.
    + */
    +goog.vec.Mat3.createFloat32FromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat3.createFloat32();
    +  goog.vec.Mat3.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float32 initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {!goog.vec.Mat3.Float32} The new, nine element array.
    + */
    +goog.vec.Mat3.createFloat32FromValues = function(
    +    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  var newMatrix = goog.vec.Mat3.createFloat32();
    +  goog.vec.Mat3.setFromValues(
    +      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 3x3 matrix of Float32.
    + *
    + * @param {goog.vec.Mat3.Float32} matrix The source 3x3 matrix.
    + * @return {!goog.vec.Mat3.Float32} The new 3x3 element matrix.
    + */
    +goog.vec.Mat3.cloneFloat32 = goog.vec.Mat3.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float64 initialized from the given array.
    + *
    + * @param {goog.vec.Mat3.AnyType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat3.Float64} The new, nine element array.
    + */
    +goog.vec.Mat3.createFloat64FromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat3.createFloat64();
    +  goog.vec.Mat3.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float64 initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {!goog.vec.Mat3.Float64} The new, nine element array.
    + */
    +goog.vec.Mat3.createFloat64FromValues = function(
    +    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  var newMatrix = goog.vec.Mat3.createFloat64();
    +  goog.vec.Mat3.setFromValues(
    +      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 3x3 matrix of Float64.
    + *
    + * @param {goog.vec.Mat3.Float64} matrix The source 3x3 matrix.
    + * @return {!goog.vec.Mat3.Float64} The new 3x3 element matrix.
    + */
    +goog.vec.Mat3.cloneFloat64 = goog.vec.Mat3.createFloat64FromArray;
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float32 initialized from the given array.
    + *
    + * @deprecated Use createFloat32FromArray.
    + * @param {goog.vec.Mat3.Mat3Like} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat3.Type} The new, nine element array.
    + */
    +goog.vec.Mat3.createFromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat3.createFloat32();
    +  goog.vec.Mat3.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix of Float32 initialized from the given values.
    + *
    + * @deprecated Use createFloat32FromValues.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {!goog.vec.Mat3.Type} The new, nine element array.
    + */
    +goog.vec.Mat3.createFromValues = function(
    +    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  var newMatrix = goog.vec.Mat3.create();
    +  goog.vec.Mat3.setFromValues(
    +      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 3x3 matrix of Float32.
    + *
    + * @deprecated Use cloneFloat32.
    + * @param {goog.vec.Mat3.Mat3Like} matrix The source 3x3 matrix.
    + * @return {!goog.vec.Mat3.Type} The new 3x3 element matrix.
    + */
    +goog.vec.Mat3.clone = goog.vec.Mat3.createFromArray;
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.Mat3.getElement = function(mat, row, column) {
    +  return mat[row + column * 3];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setElement = function(mat, row, column, value) {
    +  mat[row + column * 3] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setFromValues = function(
    +    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v01;
    +  mat[4] = v11;
    +  mat[5] = v21;
    +  mat[6] = v02;
    +  mat[7] = v12;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in column major order.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Mat3.AnyType} values The column major ordered
    + *     array of values to store in the matrix.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setFromArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[1];
    +  mat[2] = values[2];
    +  mat[3] = values[3];
    +  mat[4] = values[4];
    +  mat[5] = values[5];
    +  mat[6] = values[6];
    +  mat[7] = values[7];
    +  mat[8] = values[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in row major order.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Mat3.AnyType} values The row major ordered array
    + *     of values to store in the matrix.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setFromRowMajorArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[3];
    +  mat[2] = values[6];
    +  mat[3] = values[1];
    +  mat[4] = values[4];
    +  mat[5] = values[7];
    +  mat[6] = values[2];
    +  mat[7] = values[5];
    +  mat[8] = values[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setDiagonalValues = function(mat, v00, v11, v22) {
    +  mat[0] = v00;
    +  mat[4] = v11;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec3.AnyType} vec The vector containing the values.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[4] = vec[1];
    +  mat[8] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setColumnValues = function(mat, column, v0, v1, v2) {
    +  var i = column * 3;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied array.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.Vec3.AnyType} vec The vector elements for the column.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector
    + * array.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.Vec3.AnyType} vec The vector elements to receive the
    + *     column.
    + * @return {goog.vec.Vec3.AnyType} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.getColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the set of vector elements.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec3.AnyType} vec0 The values for column 0.
    + * @param {goog.vec.Vec3.AnyType} vec1 The values for column 1.
    + * @param {goog.vec.Vec3.AnyType} vec2 The values for column 2.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.Mat3.setColumn(mat, 0, vec0);
    +  goog.vec.Mat3.setColumn(mat, 1, vec1);
    +  goog.vec.Mat3.setColumn(mat, 2, vec2);
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vector
    + * elements.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the columns.
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector to receive column 0.
    + * @param {goog.vec.Vec3.AnyType} vec1 The vector to receive column 1.
    + * @param {goog.vec.Vec3.AnyType} vec2 The vector to receive column 2.
    + */
    +goog.vec.Mat3.getColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.Mat3.getColumn(mat, 0, vec0);
    +  goog.vec.Mat3.getColumn(mat, 1, vec1);
    +  goog.vec.Mat3.getColumn(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setRowValues = function(mat, row, v0, v1, v2) {
    +  mat[row] = v0;
    +  mat[row + 3] = v1;
    +  mat[row + 6] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.Vec3.AnyType} vec The vector containing the values.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 3] = vec[1];
    +  mat[row + 6] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.Vec3.AnyType} vec The vector to receive the row.
    + * @return {goog.vec.Vec3.AnyType} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 3];
    +  vec[2] = mat[row + 6];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec3.AnyType} vec0 The values for row 0.
    + * @param {goog.vec.Vec3.AnyType} vec1 The values for row 1.
    + * @param {goog.vec.Vec3.AnyType} vec2 The values for row 2.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.setRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.Mat3.setRow(mat, 0, vec0);
    +  goog.vec.Mat3.setRow(mat, 1, vec1);
    +  goog.vec.Mat3.setRow(mat, 2, vec2);
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to supplying the values.
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector to receive row 0.
    + * @param {goog.vec.Vec3.AnyType} vec1 The vector to receive row 1.
    + * @param {goog.vec.Vec3.AnyType} vec2 The vector to receive row 2.
    + */
    +goog.vec.Mat3.getRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.Mat3.getRow(mat, 0, vec0);
    +  goog.vec.Mat3.getRow(mat, 1, vec1);
    +  goog.vec.Mat3.getRow(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the zero matrix.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @return {goog.vec.Mat3.AnyType} return mat so operations can be chained.
    + */
    +goog.vec.Mat3.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the identity matrix.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @return {goog.vec.Mat3.AnyType} return mat so operations can be chained.
    + */
    +goog.vec.Mat3.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrices mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat0 The first addend.
    + * @param {goog.vec.Mat3.AnyType} mat1 The second addend.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat3.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrices mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat0 The minuend.
    + * @param {goog.vec.Mat3.AnyType} mat1 The subtrahend.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat3.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat0 with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} scalar The scalar value to multiple to each element of mat.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.Mat3.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat0 The first (left hand) matrix.
    + * @param {goog.vec.Mat3.AnyType} mat1 The second (right hand) matrix.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat3.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
    +  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
    +  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
    +  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
    +  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
    +  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
    +  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
    +  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
    +  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix to transpose.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.Mat3.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = a10;
    +    resultMat[5] = mat[7];
    +    resultMat[6] = a20;
    +    resultMat[7] = a21;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = mat[1];
    +    resultMat[4] = mat[4];
    +    resultMat[5] = mat[7];
    +    resultMat[6] = mat[2];
    +    resultMat[7] = mat[5];
    +    resultMat[8] = mat[8];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat0 storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat0 The matrix to invert.
    + * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
    + *     the result (may be mat0).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.Mat3.invert = function(mat0, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var t00 = a11 * a22 - a12 * a21;
    +  var t10 = a12 * a20 - a10 * a22;
    +  var t20 = a10 * a21 - a11 * a20;
    +  var det = a00 * t00 + a01 * t10 + a02 * t20;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1 / det;
    +  resultMat[0] = t00 * idet;
    +  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
    +  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
    +
    +  resultMat[1] = t10 * idet;
    +  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
    +  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
    +
    +  resultMat[2] = t20 * idet;
    +  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
    +  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat0 The first matrix.
    + * @param {goog.vec.Mat3.AnyType} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.Mat3.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed matrix into resultVec.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the transformation.
    + * @param {goog.vec.Vec3.AnyType} vec The vector to transform.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
    +  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
    +  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a translation matrix with x and y
    + * translation values.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeTranslate = function(mat, x, y) {
    +  goog.vec.Mat3.makeIdentity(mat);
    +  return goog.vec.Mat3.setColumnValues(mat, 2, x, y, 1);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a scale matrix with x, y, and z scale factors.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The 3x3 (9-element) matrix
    + *     array to receive the new scale matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeScale = function(mat, x, y, z) {
    +  goog.vec.Mat3.makeIdentity(mat);
    +  return goog.vec.Mat3.setDiagonalValues(mat, x, y, z);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  return goog.vec.Mat3.setFromValues(mat,
    +      ax * ax * d + c,
    +      ax * ay * d + az * s,
    +      ax * az * d - ay * s,
    +
    +      ax * ay * d - az * s,
    +      ay * ay * d + c,
    +      ay * az * d + ax * s,
    +
    +      ax * az * d + ay * s,
    +      ay * az * d - ax * s,
    +      az * az * d + c);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat3.setFromValues(mat,
    +                                     1, 0, 0,
    +                                     0, c, s,
    +                                     0, -s, c);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat3.setFromValues(mat,
    +                                     c, 0, -s,
    +                                     0, 1, 0,
    +                                     s, 0, c);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat3.setFromValues(mat,
    +                                     c, s, 0,
    +                                     -s, c, 0,
    +                                     0, 0, 1);
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.Mat3.multMat(
    + *     mat,
    + *     goog.vec.Mat3.makeRotate(goog.vec.Mat3.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  return goog.vec.Mat3.setFromValues(
    +      mat,
    +      m00 * r00 + m01 * r10 + m02 * r20,
    +      m10 * r00 + m11 * r10 + m12 * r20,
    +      m20 * r00 + m21 * r10 + m22 * r20,
    +
    +      m00 * r01 + m01 * r11 + m02 * r21,
    +      m10 * r01 + m11 * r11 + m12 * r21,
    +      m20 * r01 + m21 * r11 + m22 * r21,
    +
    +      m00 * r02 + m01 * r12 + m02 * r22,
    +      m10 * r02 + m11 * r12 + m12 * r22,
    +      m20 * r02 + m21 * r12 + m22 * r22);
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.Mat3.multMat(
    + *     mat,
    + *     goog.vec.Mat3.makeRotateX(goog.vec.Mat3.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.rotateX = function(mat, angle) {
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[3] = m01 * c + m02 * s;
    +  mat[4] = m11 * c + m12 * s;
    +  mat[5] = m21 * c + m22 * s;
    +  mat[6] = m01 * -s + m02 * c;
    +  mat[7] = m11 * -s + m12 * c;
    +  mat[8] = m21 * -s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.Mat3.multMat(
    + *     mat,
    + *     goog.vec.Mat3.makeRotateY(goog.vec.Mat3.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[6] = m00 * s + m02 * c;
    +  mat[7] = m10 * s + m12 * c;
    +  mat[8] = m20 * s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.Mat3.multMat(
    + *     mat,
    + *     goog.vec.Mat3.makeRotateZ(goog.vec.Mat3.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m00 * -s + m01 * c;
    +  mat[4] = m10 * -s + m11 * c;
    +  mat[5] = m20 * -s + m21 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {goog.vec.Mat3.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat3.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +
    +  mat[3] = -c1 * s3 - c3 * c2 * s1;
    +  mat[4] = c1 * c2 * c3 - s1 * s3;
    +  mat[5] = c3 * s2;
    +
    +  mat[6] = s2 * s1;
    +  mat[7] = -c1 * s2;
    +  mat[8] = c2;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.Mat3.AnyType} mat The matrix.
    + * @param {goog.vec.Vec3.AnyType} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {goog.vec.Vec3.AnyType} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat3.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[5] * mat[5]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[5] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[0] = Math.atan2(mat[6] * signTheta2, -mat[7] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat3_test.html
    new file mode 100644
    index 0000000000..55dbc86e6f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3_test.html
    @@ -0,0 +1,465 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Mat3</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Mat3');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randomMat3 = goog.vec.Mat3.createFloat32FromValues(
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197);
    +
    +  function testDeprecatedConstructor() {
    +    var m0 = goog.vec.Mat3.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Mat3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +
    +    var m2 = goog.vec.Mat3.createFromArray(m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m2);
    +
    +    var m3 = goog.vec.Mat3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m3);
    +
    +    var m4 = goog.vec.Mat3.createIdentity();
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m4);
    +  }
    +
    +  function testConstructor() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Mat3.createFloat32FromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +
    +    var m2 = goog.vec.Mat3.createFloat32FromArray(m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m2);
    +
    +    var m3 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m3);
    +
    +    var m4 = goog.vec.Mat3.createFloat32Identity();
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m4);
    +
    +    var n0 = goog.vec.Mat3.createFloat64();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], n0);
    +
    +    var n1 = goog.vec.Mat3.createFloat64FromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], n1);
    +
    +    var n2 = goog.vec.Mat3.createFloat64FromArray(n1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], n1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], n2);
    +
    +    var n3 = goog.vec.Mat3.createFloat64FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], n3);
    +
    +    var n4 = goog.vec.Mat3.createFloat64Identity();
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], n4);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    goog.vec.Mat3.setFromArray(m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    goog.vec.Mat3.setFromValues(m0, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 10], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setDiagonalValues(m0, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], m0);
    +
    +    goog.vec.Mat3.setDiagonal(m0, [4, 5, 6]);
    +    assertElementsEquals([4, 0, 0, 0, 5, 0, 0, 0, 6], m0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setColumn(m0, 0, [1, 2, 3]);
    +    goog.vec.Mat3.setColumn(m0, 1, [4, 5, 6]);
    +    goog.vec.Mat3.setColumn(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.Mat3.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.Mat3.getColumn(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.Mat3.getColumn(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setColumns(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.Mat3.getColumns(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setRow(m0, 0, [1, 2, 3]);
    +    goog.vec.Mat3.setRow(m0, 1, [4, 5, 6]);
    +    goog.vec.Mat3.setRow(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.Mat3.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.Mat3.getRow(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.Mat3.getRow(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setRows(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.Mat3.getRows(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetRowMajorArray() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setFromRowMajorArray(
    +        m0, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.Mat3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    goog.vec.Mat3.makeZero(m0);
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeIdentity(m0);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    for (var r = 0; r < 3; r++) {
    +      for (var c = 0; c < 3; c++) {
    +        var value = c * 3 + r + 1;
    +        goog.vec.Mat3.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.Mat3.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFloat32FromValues(3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.Mat3.create();
    +    goog.vec.Mat3.addMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m2);
    +
    +    goog.vec.Mat3.addMat(m0, m1, m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFloat32FromValues(3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.Mat3.create();
    +
    +    goog.vec.Mat3.subMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([-2, -2, -2, -2, -2, -2, -2, 7, 7], m2);
    +
    +    goog.vec.Mat3.subMat(m1, m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([2, 2, 2, 2, 2, 2, 2, -7, -7], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFloat32();
    +
    +    goog.vec.Mat3.multScalar(m0, 5, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m1);
    +
    +    goog.vec.Mat3.multScalar(m0, 5, m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m2 = goog.vec.Mat3.create();
    +
    +    goog.vec.Mat3.multMat(m0, m1, m2);
    +    assertElementsEquals([30, 36, 42, 66, 81, 96, 102, 126, 150], m2);
    +
    +    goog.vec.Mat3.addMat(m0, m1, m1);
    +    goog.vec.Mat3.multMat(m0, m1, m1);
    +    assertElementsEquals([60, 72, 84, 132, 162, 192, 204, 252, 300], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.transpose(m0, m1);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m1);
    +    goog.vec.Mat3.transpose(m1, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.Mat3.invert(m0, m0));
    +    assertElementsEquals([1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Mat3.setFromValues(m0, 1, 2, 3, 1, 3, 4, 3, 4, 5);
    +    assertTrue(goog.vec.Mat3.invert(m0, m0));
    +    assertElementsEquals([0.5, -1.0, 0.5, -3.5, 2.0, 0.5, 2.5, -1.0, -0.5], m0);
    +
    +    goog.vec.Mat3.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.Mat3.invert(m0, m0));
    +    var m1 = goog.vec.Mat3.create();
    +    goog.vec.Mat3.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Mat3.createFromArray(m0);
    +    assertTrue(goog.vec.Mat3.equals(m0, m1));
    +    assertTrue(goog.vec.Mat3.equals(m1, m0));
    +    for (var i = 0; i < 9; i++) {
    +      m1[i] = 15;
    +      assertFalse(goog.vec.Mat3.equals(m0, m1));
    +      assertFalse(goog.vec.Mat3.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.Mat3.createFloat32FromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Mat3.multVec3(m0, v0, v1);
    +    assertElementsEquals([30, 36, 42], v1);
    +    goog.vec.Mat3.multVec3(m0, v0, v0);
    +    assertElementsEquals([30, 36, 42], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.Mat3.createFloat32();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    goog.vec.Mat3.setFromValues(a0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a0);
    +
    +    var a1 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.setDiagonalValues(a1, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], a1);
    +
    +    goog.vec.Mat3.setColumnValues(a1, 0, 2, 3, 4);
    +    goog.vec.Mat3.setColumnValues(a1, 1, 5, 6, 7);
    +    goog.vec.Mat3.setColumnValues(a1, 2, 8, 9, 1);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 1], a1);
    +
    +    goog.vec.Mat3.setRowValues(a1, 0, 1, 4, 7);
    +    goog.vec.Mat3.setRowValues(a1, 1, 2, 5, 8);
    +    goog.vec.Mat3.setRowValues(a1, 2, 3, 6, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeTranslate(m0, 3, 4);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 3, 4, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 4, 0, 0, 0, 5], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    var v0 = [0, 1, 0, -1, 0, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m0, v0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.Mat3.multMat(m0, m1, m1);
    +    var v1 = [0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m1, v1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32()
    +
    +    goog.vec.Mat3.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.Mat3.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32()
    +
    +    goog.vec.Mat3.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.Mat3.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32()
    +
    +    goog.vec.Mat3.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.Mat3.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.Mat3.createIdentity();
    +    goog.vec.Mat3.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, -1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.Mat3.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0,
    +         -0.7071068, 0.7071068, 0,
    +         0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32FromArray(randomMat3)
    +
    +    goog.vec.Mat3.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.Mat3.multMat(m1, m0, m0);
    +    goog.vec.Mat3.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32FromArray(randomMat3)
    +
    +    goog.vec.Mat3.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.Mat3.multMat(m1, m0, m0);
    +    goog.vec.Mat3.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var m1 = goog.vec.Mat3.createFloat32FromArray(randomMat3)
    +
    +    goog.vec.Mat3.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.Mat3.multMat(m1, m0, m0);
    +    goog.vec.Mat3.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.Mat3.createFloat32();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.Mat3.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.Mat3.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.Mat3.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.Mat3.createFloat32();
    +    goog.vec.Mat3.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat3.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.Mat3.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.Mat3.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.Mat3.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.Mat3.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat3.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.Mat3.createFloat32FromArray(
    +        [1, 0, 0, 0, 0, -1, 0, 1, 0]);
    +    var m1 = goog.vec.Mat3.createFloat32FromArray(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat3.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.Mat3.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3d.js b/mapbox-gl/closure-library/closure/goog/vec/mat3d.js
    new file mode 100644
    index 0000000000..1ebddc65f6
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3d.js
    @@ -0,0 +1,1039 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat3f.js by running:            //
    +//   swap_type.sh mat3d.js > mat3f.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 3x3 double (64bit)
    + * matrices.  The matrices are stored in column-major order.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.mat3d');
    +goog.provide('goog.vec.mat3d.Type');
    +
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float64} */ goog.vec.mat3d.Type;
    +
    +
    +/**
    + * Creates a mat3d with all elements initialized to zero.
    + *
    + * @return {!goog.vec.mat3d.Type} The new mat3d.
    + */
    +goog.vec.mat3d.create = function() {
    +  return new Float64Array(9);
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setFromValues = function(
    +    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v01;
    +  mat[4] = v11;
    +  mat[5] = v21;
    +  mat[6] = v02;
    +  mat[7] = v12;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3d mat from mat3d src.
    + *
    + * @param {goog.vec.mat3d.Type} mat The destination matrix.
    + * @param {goog.vec.mat3d.Type} src The source matrix.
    + * @return {!goog.vec.mat3d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setFromMat3d = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3d mat from mat3f src (typed as a Float32Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.mat3d.Type} mat The destination matrix.
    + * @param {Float32Array} src The source matrix.
    + * @return {!goog.vec.mat3d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setFromMat3f = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3d mat from Array src.
    + *
    + * @param {goog.vec.mat3d.Type} mat The destination matrix.
    + * @param {Array<number>} src The source matrix.
    + * @return {!goog.vec.mat3d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setFromArray = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.mat3d.getElement = function(mat, row, column) {
    +  return mat[row + column * 3];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setElement = function(mat, row, column, value) {
    +  mat[row + column * 3] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setDiagonalValues = function(mat, v00, v11, v22) {
    +  mat[0] = v00;
    +  mat[4] = v11;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3d.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[4] = vec[1];
    +  mat[8] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setColumnValues = function(mat, column, v0, v1, v2) {
    +  var i = column * 3;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied array.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.vec3d.Type} vec The vector elements for the column.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector
    + * array.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.vec3d.Type} vec The vector elements to receive the
    + *     column.
    + * @return {!goog.vec.vec3d.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.getColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the set of vector elements.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3d.Type} vec0 The values for column 0.
    + * @param {goog.vec.vec3d.Type} vec1 The values for column 1.
    + * @param {goog.vec.vec3d.Type} vec2 The values for column 2.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3d.setColumn(mat, 0, vec0);
    +  goog.vec.mat3d.setColumn(mat, 1, vec1);
    +  goog.vec.mat3d.setColumn(mat, 2, vec2);
    +  return /** @type {!goog.vec.mat3d.Type} */(mat);
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vector
    + * elements.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix supplying the columns.
    + * @param {goog.vec.vec3d.Type} vec0 The vector to receive column 0.
    + * @param {goog.vec.vec3d.Type} vec1 The vector to receive column 1.
    + * @param {goog.vec.vec3d.Type} vec2 The vector to receive column 2.
    + */
    +goog.vec.mat3d.getColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3d.getColumn(mat, 0, vec0);
    +  goog.vec.mat3d.getColumn(mat, 1, vec1);
    +  goog.vec.mat3d.getColumn(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setRowValues = function(mat, row, v0, v1, v2) {
    +  mat[row] = v0;
    +  mat[row + 3] = v1;
    +  mat[row + 6] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.vec3d.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 3] = vec[1];
    +  mat[row + 6] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.vec3d.Type} vec The vector to receive the row.
    + * @return {!goog.vec.vec3d.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 3];
    +  vec[2] = mat[row + 6];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3d.Type} vec0 The values for row 0.
    + * @param {goog.vec.vec3d.Type} vec1 The values for row 1.
    + * @param {goog.vec.vec3d.Type} vec2 The values for row 2.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.setRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3d.setRow(mat, 0, vec0);
    +  goog.vec.mat3d.setRow(mat, 1, vec1);
    +  goog.vec.mat3d.setRow(mat, 2, vec2);
    +  return /** @type {!goog.vec.mat3d.Type} */(mat);
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to supplying the values.
    + * @param {goog.vec.vec3d.Type} vec0 The vector to receive row 0.
    + * @param {goog.vec.vec3d.Type} vec1 The vector to receive row 1.
    + * @param {goog.vec.vec3d.Type} vec2 The vector to receive row 2.
    + */
    +goog.vec.mat3d.getRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3d.getRow(mat, 0, vec0);
    +  goog.vec.mat3d.getRow(mat, 1, vec1);
    +  goog.vec.mat3d.getRow(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the zero matrix.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @return {!goog.vec.mat3d.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat3d.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the identity matrix.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @return {!goog.vec.mat3d.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat3d.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrices mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.mat3d.Type} mat0 The first addend.
    + * @param {goog.vec.mat3d.Type} mat1 The second addend.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrices mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3d.Type} mat0 The minuend.
    + * @param {goog.vec.mat3d.Type} mat1 The subtrahend.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat0 with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} scalar The scalar value to multiple to each element of mat.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat3d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3d.Type} mat0 The first (left hand) matrix.
    + * @param {goog.vec.mat3d.Type} mat1 The second (right hand) matrix.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
    +  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
    +  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
    +  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
    +  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
    +  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
    +  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
    +  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
    +  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix to transpose.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat3d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = a10;
    +    resultMat[5] = mat[7];
    +    resultMat[6] = a20;
    +    resultMat[7] = a21;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = mat[1];
    +    resultMat[4] = mat[4];
    +    resultMat[5] = mat[7];
    +    resultMat[6] = mat[2];
    +    resultMat[7] = mat[5];
    +    resultMat[8] = mat[8];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat0 storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.mat3d.Type} mat0 The matrix to invert.
    + * @param {goog.vec.mat3d.Type} resultMat The matrix to receive
    + *     the result (may be mat0).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.mat3d.invert = function(mat0, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var t00 = a11 * a22 - a12 * a21;
    +  var t10 = a12 * a20 - a10 * a22;
    +  var t20 = a10 * a21 - a11 * a20;
    +  var det = a00 * t00 + a01 * t10 + a02 * t20;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1 / det;
    +  resultMat[0] = t00 * idet;
    +  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
    +  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
    +
    +  resultMat[1] = t10 * idet;
    +  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
    +  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
    +
    +  resultMat[2] = t20 * idet;
    +  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
    +  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.mat3d.Type} mat0 The first matrix.
    + * @param {goog.vec.mat3d.Type} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.mat3d.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed matrix into resultVec.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3d.Type} vec The vector to transform.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3d.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
    +  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
    +  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a translation matrix with x and y
    + * translation values.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeTranslate = function(mat, x, y) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = x;
    +  mat[7] = y;
    +  mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a scale matrix with x, y, and z scale factors.
    + *
    + * @param {goog.vec.mat3d.Type} mat The 3x3 (9-element) matrix
    + *     array to receive the new scale matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeScale = function(mat, x, y, z) {
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = y;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = z;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  mat[0] = ax * ax * d + c;
    +  mat[1] = ax * ay * d + az * s;
    +  mat[2] = ax * az * d - ay * s;
    +  mat[3] = ax * ay * d - az * s;
    +  mat[4] = ay * ay * d + c;
    +  mat[5] = ay * az * d + ax * s;
    +  mat[6] = ax * az * d + ay * s;
    +  mat[7] = ay * az * d - ax * s;
    +  mat[8] = az * az * d + c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = c;
    +  mat[5] = s;
    +  mat[6] = 0;
    +  mat[7] = -s;
    +  mat[8] = c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = 0;
    +  mat[2] = -s;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = s;
    +  mat[7] = 0;
    +  mat[8] = c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = s;
    +  mat[2] = 0;
    +  mat[3] = -s;
    +  mat[4] = c;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.mat3d.multMat(
    + *     mat,
    + *     goog.vec.mat3d.makeRotate(goog.vec.mat3d.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  mat[0] = m00 * r00 + m01 * r10 + m02 * r20;
    +  mat[1] = m10 * r00 + m11 * r10 + m12 * r20;
    +  mat[2] = m20 * r00 + m21 * r10 + m22 * r20;
    +  mat[3] = m00 * r01 + m01 * r11 + m02 * r21;
    +  mat[4] = m10 * r01 + m11 * r11 + m12 * r21;
    +  mat[5] = m20 * r01 + m21 * r11 + m22 * r21;
    +  mat[6] = m00 * r02 + m01 * r12 + m02 * r22;
    +  mat[7] = m10 * r02 + m11 * r12 + m12 * r22;
    +  mat[8] = m20 * r02 + m21 * r12 + m22 * r22;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.mat3d.multMat(
    + *     mat,
    + *     goog.vec.mat3d.makeRotateX(goog.vec.mat3d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.rotateX = function(mat, angle) {
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[3] = m01 * c + m02 * s;
    +  mat[4] = m11 * c + m12 * s;
    +  mat[5] = m21 * c + m22 * s;
    +  mat[6] = m01 * -s + m02 * c;
    +  mat[7] = m11 * -s + m12 * c;
    +  mat[8] = m21 * -s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.mat3d.multMat(
    + *     mat,
    + *     goog.vec.mat3d.makeRotateY(goog.vec.mat3d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[6] = m00 * s + m02 * c;
    +  mat[7] = m10 * s + m12 * c;
    +  mat[8] = m20 * s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.mat3d.multMat(
    + *     mat,
    + *     goog.vec.mat3d.makeRotateZ(goog.vec.mat3d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m00 * -s + m01 * c;
    +  mat[4] = m10 * -s + m11 * c;
    +  mat[5] = m20 * -s + m21 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {!goog.vec.mat3d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3d.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +
    +  mat[3] = -c1 * s3 - c3 * c2 * s1;
    +  mat[4] = c1 * c2 * c3 - s1 * s3;
    +  mat[5] = c3 * s2;
    +
    +  mat[6] = s2 * s1;
    +  mat[7] = -c1 * s2;
    +  mat[8] = c2;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat3d.Type} mat The matrix.
    + * @param {goog.vec.vec3d.Type} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {!goog.vec.vec3d.Type} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3d.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[5] * mat[5]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[5] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[0] = Math.atan2(mat[6] * signTheta2, -mat[7] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3d_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat3d_test.html
    new file mode 100644
    index 0000000000..8bf68c6b3a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3d_test.html
    @@ -0,0 +1,432 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat3f_test.html by running:     //
    +//   swap_type.sh mat3d_test.html > mat3f_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.mat3d</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.mat3d');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randommat3d = goog.vec.mat3d.setFromValues(goog.vec.mat3d.create(),
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197);
    +
    +  function testCreate() {
    +    var m = goog.vec.mat3d.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.setFromArray(
    +        goog.vec.mat3d.create(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    goog.vec.mat3d.setFromArray(m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    goog.vec.mat3d.setFromValues(m0, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 10], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setDiagonalValues(m0, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], m0);
    +
    +    goog.vec.mat3d.setDiagonal(m0, [4, 5, 6]);
    +    assertElementsEquals([4, 0, 0, 0, 5, 0, 0, 0, 6], m0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setColumn(m0, 0, [1, 2, 3]);
    +    goog.vec.mat3d.setColumn(m0, 1, [4, 5, 6]);
    +    goog.vec.mat3d.setColumn(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.mat3d.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.mat3d.getColumn(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.mat3d.getColumn(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setColumns(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.mat3d.getColumns(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setRow(m0, 0, [1, 2, 3]);
    +    goog.vec.mat3d.setRow(m0, 1, [4, 5, 6]);
    +    goog.vec.mat3d.setRow(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.mat3d.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.mat3d.getRow(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.mat3d.getRow(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setRows(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.mat3d.getRows(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.mat3d.setFromArray(
    +        goog.vec.mat3d.create(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    goog.vec.mat3d.makeZero(m0);
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeIdentity(m0);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.mat3d.create();
    +    for (var r = 0; r < 3; r++) {
    +      for (var c = 0; c < 3; c++) {
    +        var value = c * 3 + r + 1;
    +        goog.vec.mat3d.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.mat3d.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.addMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m2);
    +
    +    goog.vec.mat3d.addMat(m0, m1, m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.mat3d.create();
    +
    +    goog.vec.mat3d.subMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([-2, -2, -2, -2, -2, -2, -2, 7, 7], m2);
    +
    +    goog.vec.mat3d.subMat(m1, m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([2, 2, 2, 2, 2, 2, 2, -7, -7], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.create();
    +
    +    goog.vec.mat3d.multScalar(m0, 5, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m1);
    +
    +    goog.vec.mat3d.multScalar(m0, 5, m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m2 = goog.vec.mat3d.create();
    +
    +    goog.vec.mat3d.multMat(m0, m1, m2);
    +    assertElementsEquals([30, 36, 42, 66, 81, 96, 102, 126, 150], m2);
    +
    +    goog.vec.mat3d.addMat(m0, m1, m1);
    +    goog.vec.mat3d.multMat(m0, m1, m1);
    +    assertElementsEquals([60, 72, 84, 132, 162, 192, 204, 252, 300], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.transpose(m0, m1);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m1);
    +    goog.vec.mat3d.transpose(m1, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.mat3d.invert(m0, m0));
    +    assertElementsEquals([1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat3d.setFromValues(m0, 1, 2, 3, 1, 3, 4, 3, 4, 5);
    +    assertTrue(goog.vec.mat3d.invert(m0, m0));
    +    assertElementsEquals([0.5, -1.0, 0.5, -3.5, 2.0, 0.5, 2.5, -1.0, -0.5], m0);
    +
    +    goog.vec.mat3d.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.mat3d.invert(m0, m0));
    +    var m1 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), m0);
    +    assertTrue(goog.vec.mat3d.equals(m0, m1));
    +    assertTrue(goog.vec.mat3d.equals(m1, m0));
    +    for (var i = 0; i < 9; i++) {
    +      m1[i] = 15;
    +      assertFalse(goog.vec.mat3d.equals(m0, m1));
    +      assertFalse(goog.vec.mat3d.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.mat3d.setFromValues(
    +        goog.vec.mat3d.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat3d.multVec3(m0, v0, v1);
    +    assertElementsEquals([30, 36, 42], v1);
    +    goog.vec.mat3d.multVec3(m0, v0, v0);
    +    assertElementsEquals([30, 36, 42], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.mat3d.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    goog.vec.mat3d.setFromValues(a0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a0);
    +
    +    var a1 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.setDiagonalValues(a1, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], a1);
    +
    +    goog.vec.mat3d.setColumnValues(a1, 0, 2, 3, 4);
    +    goog.vec.mat3d.setColumnValues(a1, 1, 5, 6, 7);
    +    goog.vec.mat3d.setColumnValues(a1, 2, 8, 9, 1);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 1], a1);
    +
    +    goog.vec.mat3d.setRowValues(a1, 0, 1, 4, 7);
    +    goog.vec.mat3d.setRowValues(a1, 1, 2, 5, 8);
    +    goog.vec.mat3d.setRowValues(a1, 2, 3, 6, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeTranslate(m0, 3, 4);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 3, 4, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 4, 0, 0, 0, 5], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    var v0 = [0, 1, 0, -1, 0, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m0, v0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.mat3d.multMat(m0, m1, m1);
    +    var v1 = [0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m1, v1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.create()
    +
    +    goog.vec.mat3d.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat3d.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.create()
    +
    +    goog.vec.mat3d.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat3d.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.create()
    +
    +    goog.vec.mat3d.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat3d.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.mat3d.makeIdentity(goog.vec.mat3d.create());
    +    goog.vec.mat3d.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, -1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.mat3d.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0,
    +         -0.7071068, 0.7071068, 0,
    +         0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), randommat3d);
    +
    +    goog.vec.mat3d.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat3d.multMat(m1, m0, m0);
    +    goog.vec.mat3d.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), randommat3d);
    +
    +    goog.vec.mat3d.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat3d.multMat(m1, m0, m0);
    +    goog.vec.mat3d.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.mat3d.create();
    +    var m1 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), randommat3d);
    +
    +    goog.vec.mat3d.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat3d.multMat(m1, m0, m0);
    +    goog.vec.mat3d.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.mat3d.create();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.mat3d.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat3d.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.mat3d.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.mat3d.create();
    +    goog.vec.mat3d.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3d.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.mat3d.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat3d.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.mat3d.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.mat3d.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3d.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), 
    +        [1, 0, 0, 0, 0, -1, 0, 1, 0]);
    +    var m1 = goog.vec.mat3d.setFromArray(goog.vec.mat3d.create(), 
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3d.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.mat3d.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3f.js b/mapbox-gl/closure-library/closure/goog/vec/mat3f.js
    new file mode 100644
    index 0000000000..28b0497087
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3f.js
    @@ -0,0 +1,1039 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat3d.js by running:            //
    +//   swap_type.sh mat3f.js > mat3d.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 3x3 float (32bit)
    + * matrices.  The matrices are stored in column-major order.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.mat3f');
    +goog.provide('goog.vec.mat3f.Type');
    +
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.mat3f.Type;
    +
    +
    +/**
    + * Creates a mat3f with all elements initialized to zero.
    + *
    + * @return {!goog.vec.mat3f.Type} The new mat3f.
    + */
    +goog.vec.mat3f.create = function() {
    +  return new Float32Array(9);
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setFromValues = function(
    +    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v01;
    +  mat[4] = v11;
    +  mat[5] = v21;
    +  mat[6] = v02;
    +  mat[7] = v12;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3f mat from mat3f src.
    + *
    + * @param {goog.vec.mat3f.Type} mat The destination matrix.
    + * @param {goog.vec.mat3f.Type} src The source matrix.
    + * @return {!goog.vec.mat3f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setFromMat3f = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3f mat from mat3d src (typed as a Float64Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.mat3f.Type} mat The destination matrix.
    + * @param {Float64Array} src The source matrix.
    + * @return {!goog.vec.mat3f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setFromMat3d = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat3f mat from Array src.
    + *
    + * @param {goog.vec.mat3f.Type} mat The destination matrix.
    + * @param {Array<number>} src The source matrix.
    + * @return {!goog.vec.mat3f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setFromArray = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.mat3f.getElement = function(mat, row, column) {
    +  return mat[row + column * 3];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setElement = function(mat, row, column, value) {
    +  mat[row + column * 3] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setDiagonalValues = function(mat, v00, v11, v22) {
    +  mat[0] = v00;
    +  mat[4] = v11;
    +  mat[8] = v22;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3f.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[4] = vec[1];
    +  mat[8] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setColumnValues = function(mat, column, v0, v1, v2) {
    +  var i = column * 3;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied array.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.vec3f.Type} vec The vector elements for the column.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector
    + * array.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.vec3f.Type} vec The vector elements to receive the
    + *     column.
    + * @return {!goog.vec.vec3f.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.getColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the set of vector elements.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3f.Type} vec0 The values for column 0.
    + * @param {goog.vec.vec3f.Type} vec1 The values for column 1.
    + * @param {goog.vec.vec3f.Type} vec2 The values for column 2.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3f.setColumn(mat, 0, vec0);
    +  goog.vec.mat3f.setColumn(mat, 1, vec1);
    +  goog.vec.mat3f.setColumn(mat, 2, vec2);
    +  return /** @type {!goog.vec.mat3f.Type} */(mat);
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vector
    + * elements.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix supplying the columns.
    + * @param {goog.vec.vec3f.Type} vec0 The vector to receive column 0.
    + * @param {goog.vec.vec3f.Type} vec1 The vector to receive column 1.
    + * @param {goog.vec.vec3f.Type} vec2 The vector to receive column 2.
    + */
    +goog.vec.mat3f.getColumns = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3f.getColumn(mat, 0, vec0);
    +  goog.vec.mat3f.getColumn(mat, 1, vec1);
    +  goog.vec.mat3f.getColumn(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setRowValues = function(mat, row, v0, v1, v2) {
    +  mat[row] = v0;
    +  mat[row + 3] = v1;
    +  mat[row + 6] = v2;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.vec3f.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 3] = vec[1];
    +  mat[row + 6] = vec[2];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.vec3f.Type} vec The vector to receive the row.
    + * @return {!goog.vec.vec3f.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 3];
    +  vec[2] = mat[row + 6];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec3f.Type} vec0 The values for row 0.
    + * @param {goog.vec.vec3f.Type} vec1 The values for row 1.
    + * @param {goog.vec.vec3f.Type} vec2 The values for row 2.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.setRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3f.setRow(mat, 0, vec0);
    +  goog.vec.mat3f.setRow(mat, 1, vec1);
    +  goog.vec.mat3f.setRow(mat, 2, vec2);
    +  return /** @type {!goog.vec.mat3f.Type} */(mat);
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to supplying the values.
    + * @param {goog.vec.vec3f.Type} vec0 The vector to receive row 0.
    + * @param {goog.vec.vec3f.Type} vec1 The vector to receive row 1.
    + * @param {goog.vec.vec3f.Type} vec2 The vector to receive row 2.
    + */
    +goog.vec.mat3f.getRows = function(mat, vec0, vec1, vec2) {
    +  goog.vec.mat3f.getRow(mat, 0, vec0);
    +  goog.vec.mat3f.getRow(mat, 1, vec1);
    +  goog.vec.mat3f.getRow(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the zero matrix.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @return {!goog.vec.mat3f.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat3f.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix the identity matrix.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @return {!goog.vec.mat3f.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat3f.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrices mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.mat3f.Type} mat0 The first addend.
    + * @param {goog.vec.mat3f.Type} mat1 The second addend.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrices mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3f.Type} mat0 The minuend.
    + * @param {goog.vec.mat3f.Type} mat1 The subtrahend.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat0 with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} scalar The scalar value to multiple to each element of mat.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat3f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3f.Type} mat0 The first (left hand) matrix.
    + * @param {goog.vec.mat3f.Type} mat1 The second (right hand) matrix.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat3f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
    +  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
    +  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
    +  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
    +  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
    +  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
    +  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
    +  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
    +  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix to transpose.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat3f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = a10;
    +    resultMat[5] = mat[7];
    +    resultMat[6] = a20;
    +    resultMat[7] = a21;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = mat[1];
    +    resultMat[4] = mat[4];
    +    resultMat[5] = mat[7];
    +    resultMat[6] = mat[2];
    +    resultMat[7] = mat[5];
    +    resultMat[8] = mat[8];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat0 storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.mat3f.Type} mat0 The matrix to invert.
    + * @param {goog.vec.mat3f.Type} resultMat The matrix to receive
    + *     the result (may be mat0).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.mat3f.invert = function(mat0, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var t00 = a11 * a22 - a12 * a21;
    +  var t10 = a12 * a20 - a10 * a22;
    +  var t20 = a10 * a21 - a11 * a20;
    +  var det = a00 * t00 + a01 * t10 + a02 * t20;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1 / det;
    +  resultMat[0] = t00 * idet;
    +  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
    +  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
    +
    +  resultMat[1] = t10 * idet;
    +  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
    +  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
    +
    +  resultMat[2] = t20 * idet;
    +  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
    +  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.mat3f.Type} mat0 The first matrix.
    + * @param {goog.vec.mat3f.Type} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.mat3f.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed matrix into resultVec.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3f.Type} vec The vector to transform.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3f.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
    +  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
    +  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a translation matrix with x and y
    + * translation values.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeTranslate = function(mat, x, y) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = x;
    +  mat[7] = y;
    +  mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a scale matrix with x, y, and z scale factors.
    + *
    + * @param {goog.vec.mat3f.Type} mat The 3x3 (9-element) matrix
    + *     array to receive the new scale matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeScale = function(mat, x, y, z) {
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = y;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = z;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  mat[0] = ax * ax * d + c;
    +  mat[1] = ax * ay * d + az * s;
    +  mat[2] = ax * az * d - ay * s;
    +  mat[3] = ax * ay * d - az * s;
    +  mat[4] = ay * ay * d + c;
    +  mat[5] = ay * az * d + ax * s;
    +  mat[6] = ax * az * d + ay * s;
    +  mat[7] = ay * az * d - ax * s;
    +  mat[8] = az * az * d + c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = c;
    +  mat[5] = s;
    +  mat[6] = 0;
    +  mat[7] = -s;
    +  mat[8] = c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = 0;
    +  mat[2] = -s;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = s;
    +  mat[7] = 0;
    +  mat[8] = c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = s;
    +  mat[2] = 0;
    +  mat[3] = -s;
    +  mat[4] = c;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.mat3f.multMat(
    + *     mat,
    + *     goog.vec.mat3f.makeRotate(goog.vec.mat3f.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  mat[0] = m00 * r00 + m01 * r10 + m02 * r20;
    +  mat[1] = m10 * r00 + m11 * r10 + m12 * r20;
    +  mat[2] = m20 * r00 + m21 * r10 + m22 * r20;
    +  mat[3] = m00 * r01 + m01 * r11 + m02 * r21;
    +  mat[4] = m10 * r01 + m11 * r11 + m12 * r21;
    +  mat[5] = m20 * r01 + m21 * r11 + m22 * r21;
    +  mat[6] = m00 * r02 + m01 * r12 + m02 * r22;
    +  mat[7] = m10 * r02 + m11 * r12 + m12 * r22;
    +  mat[8] = m20 * r02 + m21 * r12 + m22 * r22;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.mat3f.multMat(
    + *     mat,
    + *     goog.vec.mat3f.makeRotateX(goog.vec.mat3f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.rotateX = function(mat, angle) {
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[3] = m01 * c + m02 * s;
    +  mat[4] = m11 * c + m12 * s;
    +  mat[5] = m21 * c + m22 * s;
    +  mat[6] = m01 * -s + m02 * c;
    +  mat[7] = m11 * -s + m12 * c;
    +  mat[8] = m21 * -s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.mat3f.multMat(
    + *     mat,
    + *     goog.vec.mat3f.makeRotateY(goog.vec.mat3f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m02 = mat[6], m12 = mat[7], m22 = mat[8];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[6] = m00 * s + m02 * c;
    +  mat[7] = m10 * s + m12 * c;
    +  mat[8] = m20 * s + m22 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.mat3f.multMat(
    + *     mat,
    + *     goog.vec.mat3f.makeRotateZ(goog.vec.mat3f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2];
    +  var m01 = mat[3], m11 = mat[4], m21 = mat[5];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m00 * -s + m01 * c;
    +  mat[4] = m10 * -s + m11 * c;
    +  mat[5] = m20 * -s + m21 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 3x3 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {!goog.vec.mat3f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat3f.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +
    +  mat[3] = -c1 * s3 - c3 * c2 * s1;
    +  mat[4] = c1 * c2 * c3 - s1 * s3;
    +  mat[5] = c3 * s2;
    +
    +  mat[6] = s2 * s1;
    +  mat[7] = -c1 * s2;
    +  mat[8] = c2;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat3f.Type} mat The matrix.
    + * @param {goog.vec.vec3f.Type} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {!goog.vec.vec3f.Type} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat3f.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[5] * mat[5]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[5] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[0] = Math.atan2(mat[6] * signTheta2, -mat[7] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[8]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat3f_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat3f_test.html
    new file mode 100644
    index 0000000000..0f6633aef4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat3f_test.html
    @@ -0,0 +1,432 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat3d_test.html by running:     //
    +//   swap_type.sh mat3f_test.html > mat3d_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.mat3f</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.mat3f');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randommat3f = goog.vec.mat3f.setFromValues(goog.vec.mat3f.create(),
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197);
    +
    +  function testCreate() {
    +    var m = goog.vec.mat3f.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.setFromArray(
    +        goog.vec.mat3f.create(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    goog.vec.mat3f.setFromArray(m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    goog.vec.mat3f.setFromValues(m0, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 10], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setDiagonalValues(m0, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], m0);
    +
    +    goog.vec.mat3f.setDiagonal(m0, [4, 5, 6]);
    +    assertElementsEquals([4, 0, 0, 0, 5, 0, 0, 0, 6], m0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setColumn(m0, 0, [1, 2, 3]);
    +    goog.vec.mat3f.setColumn(m0, 1, [4, 5, 6]);
    +    goog.vec.mat3f.setColumn(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.mat3f.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.mat3f.getColumn(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.mat3f.getColumn(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setColumns(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.mat3f.getColumns(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setRow(m0, 0, [1, 2, 3]);
    +    goog.vec.mat3f.setRow(m0, 1, [4, 5, 6]);
    +    goog.vec.mat3f.setRow(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.mat3f.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.mat3f.getRow(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.mat3f.getRow(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setRows(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.mat3f.getRows(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.mat3f.setFromArray(
    +        goog.vec.mat3f.create(), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    goog.vec.mat3f.makeZero(m0);
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeIdentity(m0);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.mat3f.create();
    +    for (var r = 0; r < 3; r++) {
    +      for (var c = 0; c < 3; c++) {
    +        var value = c * 3 + r + 1;
    +        goog.vec.mat3f.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.mat3f.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.addMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m2);
    +
    +    goog.vec.mat3f.addMat(m0, m1, m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.mat3f.create();
    +
    +    goog.vec.mat3f.subMat(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([-2, -2, -2, -2, -2, -2, -2, 7, 7], m2);
    +
    +    goog.vec.mat3f.subMat(m1, m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([2, 2, 2, 2, 2, 2, 2, -7, -7], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.create();
    +
    +    goog.vec.mat3f.multScalar(m0, 5, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m1);
    +
    +    goog.vec.mat3f.multScalar(m0, 5, m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m2 = goog.vec.mat3f.create();
    +
    +    goog.vec.mat3f.multMat(m0, m1, m2);
    +    assertElementsEquals([30, 36, 42, 66, 81, 96, 102, 126, 150], m2);
    +
    +    goog.vec.mat3f.addMat(m0, m1, m1);
    +    goog.vec.mat3f.multMat(m0, m1, m1);
    +    assertElementsEquals([60, 72, 84, 132, 162, 192, 204, 252, 300], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.transpose(m0, m1);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m1);
    +    goog.vec.mat3f.transpose(m1, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.mat3f.invert(m0, m0));
    +    assertElementsEquals([1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat3f.setFromValues(m0, 1, 2, 3, 1, 3, 4, 3, 4, 5);
    +    assertTrue(goog.vec.mat3f.invert(m0, m0));
    +    assertElementsEquals([0.5, -1.0, 0.5, -3.5, 2.0, 0.5, 2.5, -1.0, -0.5], m0);
    +
    +    goog.vec.mat3f.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.mat3f.invert(m0, m0));
    +    var m1 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), m0);
    +    assertTrue(goog.vec.mat3f.equals(m0, m1));
    +    assertTrue(goog.vec.mat3f.equals(m1, m0));
    +    for (var i = 0; i < 9; i++) {
    +      m1[i] = 15;
    +      assertFalse(goog.vec.mat3f.equals(m0, m1));
    +      assertFalse(goog.vec.mat3f.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.mat3f.setFromValues(
    +        goog.vec.mat3f.create(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat3f.multVec3(m0, v0, v1);
    +    assertElementsEquals([30, 36, 42], v1);
    +    goog.vec.mat3f.multVec3(m0, v0, v0);
    +    assertElementsEquals([30, 36, 42], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.mat3f.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    goog.vec.mat3f.setFromValues(a0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a0);
    +
    +    var a1 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.setDiagonalValues(a1, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], a1);
    +
    +    goog.vec.mat3f.setColumnValues(a1, 0, 2, 3, 4);
    +    goog.vec.mat3f.setColumnValues(a1, 1, 5, 6, 7);
    +    goog.vec.mat3f.setColumnValues(a1, 2, 8, 9, 1);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 1], a1);
    +
    +    goog.vec.mat3f.setRowValues(a1, 0, 1, 4, 7);
    +    goog.vec.mat3f.setRowValues(a1, 1, 2, 5, 8);
    +    goog.vec.mat3f.setRowValues(a1, 2, 3, 6, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeTranslate(m0, 3, 4);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 3, 4, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 4, 0, 0, 0, 5], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    var v0 = [0, 1, 0, -1, 0, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m0, v0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.mat3f.multMat(m0, m1, m1);
    +    var v1 = [0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 0, 0, 1];
    +    assertElementsRoughlyEqual(m1, v1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.create()
    +
    +    goog.vec.mat3f.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat3f.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.create()
    +
    +    goog.vec.mat3f.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat3f.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.create()
    +
    +    goog.vec.mat3f.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat3f.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.mat3f.makeIdentity(goog.vec.mat3f.create());
    +    goog.vec.mat3f.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, -1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.mat3f.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0,
    +         -0.7071068, 0.7071068, 0,
    +         0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), randommat3f);
    +
    +    goog.vec.mat3f.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat3f.multMat(m1, m0, m0);
    +    goog.vec.mat3f.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), randommat3f);
    +
    +    goog.vec.mat3f.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat3f.multMat(m1, m0, m0);
    +    goog.vec.mat3f.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.mat3f.create();
    +    var m1 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), randommat3f);
    +
    +    goog.vec.mat3f.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat3f.multMat(m1, m0, m0);
    +    goog.vec.mat3f.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.mat3f.create();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.mat3f.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat3f.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.mat3f.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.mat3f.create();
    +    goog.vec.mat3f.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3f.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.mat3f.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat3f.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.mat3f.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.mat3f.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3f.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), 
    +        [1, 0, 0, 0, 0, -1, 0, 1, 0]);
    +    var m1 = goog.vec.mat3f.setFromArray(goog.vec.mat3f.create(), 
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat3f.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.mat3f.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4.js b/mapbox-gl/closure-library/closure/goog/vec/mat4.js
    new file mode 100644
    index 0000000000..d047b7a79d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4.js
    @@ -0,0 +1,1822 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implements 4x4 matrices and their related functions which are
    + * compatible with WebGL. The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted. Matrix operations follow the mathematical form when multiplying
    + * vectors as follows: resultVec = matrix * vec.
    + *
    + * The matrices are stored in column-major order.
    + *
    + */
    +goog.provide('goog.vec.Mat4');
    +
    +goog.require('goog.vec');
    +goog.require('goog.vec.Vec3');
    +goog.require('goog.vec.Vec4');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Mat4.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Mat4.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Mat4.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Mat4.AnyType;
    +
    +// The following two types are deprecated - use the above types instead.
    +/** @typedef {!Float32Array} */ goog.vec.Mat4.Type;
    +/** @typedef {goog.vec.ArrayType} */ goog.vec.Mat4.Mat4Like;
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix of Float32.
    + * The use of the array directly instead of a class reduces overhead.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat4.Float32} The new matrix.
    + */
    +goog.vec.Mat4.createFloat32 = function() {
    +  return new Float32Array(16);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix of Float64.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat4.Float64} The new matrix.
    + */
    +goog.vec.Mat4.createFloat64 = function() {
    +  return new Float64Array(16);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix of Number.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat4.Number} The new matrix.
    + */
    +goog.vec.Mat4.createNumber = function() {
    +  var a = new Array(16);
    +  goog.vec.Mat4.setFromValues(a,
    +                              0, 0, 0, 0,
    +                              0, 0, 0, 0,
    +                              0, 0, 0, 0,
    +                              0, 0, 0, 0);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix of Float32.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @deprecated Use createFloat32.
    + * @return {!goog.vec.Mat4.Type} The new matrix.
    + */
    +goog.vec.Mat4.create = function() {
    +  return goog.vec.Mat4.createFloat32();
    +};
    +
    +
    +/**
    + * Creates a 4x4 identity matrix of Float32.
    + *
    + * @return {!goog.vec.Mat4.Float32} The new 16 element array.
    + */
    +goog.vec.Mat4.createFloat32Identity = function() {
    +  var mat = goog.vec.Mat4.createFloat32();
    +  mat[0] = mat[5] = mat[10] = mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 4x4 identity matrix of Float64.
    + *
    + * @return {!goog.vec.Mat4.Float64} The new 16 element array.
    + */
    +goog.vec.Mat4.createFloat64Identity = function() {
    +  var mat = goog.vec.Mat4.createFloat64();
    +  mat[0] = mat[5] = mat[10] = mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 4x4 identity matrix of Number.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @return {!goog.vec.Mat4.Number} The new 16 element array.
    + */
    +goog.vec.Mat4.createNumberIdentity = function() {
    +  var a = new Array(16);
    +  goog.vec.Mat4.setFromValues(a,
    +                              1, 0, 0, 0,
    +                              0, 1, 0, 0,
    +                              0, 0, 1, 0,
    +                              0, 0, 0, 1);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix of Float32.
    + * The returned matrix is cleared to all zeros.
    + *
    + * @deprecated Use createFloat32Identity.
    + * @return {!goog.vec.Mat4.Type} The new 16 element array.
    + */
    +goog.vec.Mat4.createIdentity = function() {
    +  return goog.vec.Mat4.createFloat32Identity();
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float32 initialized from the given array.
    + *
    + * @param {goog.vec.Mat4.AnyType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat4.Float32} The new, 16 element array.
    + */
    +goog.vec.Mat4.createFloat32FromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat4.createFloat32();
    +  goog.vec.Mat4.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float32 initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {!goog.vec.Mat4.Float32} The new, 16 element array.
    + */
    +goog.vec.Mat4.createFloat32FromValues = function(
    +    v00, v10, v20, v30,
    +    v01, v11, v21, v31,
    +    v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  var newMatrix = goog.vec.Mat4.createFloat32();
    +  goog.vec.Mat4.setFromValues(
    +      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 4x4 matrix of Float32.
    + *
    + * @param {goog.vec.Mat4.Float32} matrix The source 4x4 matrix.
    + * @return {!goog.vec.Mat4.Float32} The new 4x4 element matrix.
    + */
    +goog.vec.Mat4.cloneFloat32 = goog.vec.Mat4.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float64 initialized from the given array.
    + *
    + * @param {goog.vec.Mat4.AnyType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat4.Float64} The new, nine element array.
    + */
    +goog.vec.Mat4.createFloat64FromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat4.createFloat64();
    +  goog.vec.Mat4.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float64 initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {!goog.vec.Mat4.Float64} The new, 16 element array.
    + */
    +goog.vec.Mat4.createFloat64FromValues = function(
    +    v00, v10, v20, v30,
    +    v01, v11, v21, v31,
    +    v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  var newMatrix = goog.vec.Mat4.createFloat64();
    +  goog.vec.Mat4.setFromValues(
    +      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 4x4 matrix of Float64.
    + *
    + * @param {goog.vec.Mat4.Float64} matrix The source 4x4 matrix.
    + * @return {!goog.vec.Mat4.Float64} The new 4x4 element matrix.
    + */
    +goog.vec.Mat4.cloneFloat64 = goog.vec.Mat4.createFloat64FromArray;
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float32 initialized from the given array.
    + *
    + * @deprecated Use createFloat32FromArray.
    + * @param {goog.vec.Mat4.Mat4Like} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {!goog.vec.Mat4.Type} The new, nine element array.
    + */
    +goog.vec.Mat4.createFromArray = function(matrix) {
    +  var newMatrix = goog.vec.Mat4.createFloat32();
    +  goog.vec.Mat4.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix of Float32 initialized from the given values.
    + *
    + * @deprecated Use createFloat32FromValues.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {!goog.vec.Mat4.Type} The new, 16 element array.
    + */
    +goog.vec.Mat4.createFromValues = function(
    +    v00, v10, v20, v30,
    +    v01, v11, v21, v31,
    +    v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  return goog.vec.Mat4.createFloat32FromValues(
    +      v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +};
    +
    +
    +/**
    + * Creates a clone of a 4x4 matrix of Float32.
    + *
    + * @deprecated Use cloneFloat32.
    + * @param {goog.vec.Mat4.Mat4Like} matrix The source 4x4 matrix.
    + * @return {!goog.vec.Mat4.Type} The new 4x4 element matrix.
    + */
    +goog.vec.Mat4.clone = goog.vec.Mat4.createFromArray;
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix containing the
    + *     value to retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.Mat4.getElement = function(mat, row, column) {
    +  return mat[row + column * 4];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to set the value on.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setElement = function(mat, row, column, value) {
    +  mat[row + column * 4] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setFromValues = function(
    +    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v30;
    +  mat[4] = v01;
    +  mat[5] = v11;
    +  mat[6] = v21;
    +  mat[7] = v31;
    +  mat[8] = v02;
    +  mat[9] = v12;
    +  mat[10] = v22;
    +  mat[11] = v32;
    +  mat[12] = v03;
    +  mat[13] = v13;
    +  mat[14] = v23;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in column major order.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Mat4.AnyType} values The column major ordered
    + *     array of values to store in the matrix.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setFromArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[1];
    +  mat[2] = values[2];
    +  mat[3] = values[3];
    +  mat[4] = values[4];
    +  mat[5] = values[5];
    +  mat[6] = values[6];
    +  mat[7] = values[7];
    +  mat[8] = values[8];
    +  mat[9] = values[9];
    +  mat[10] = values[10];
    +  mat[11] = values[11];
    +  mat[12] = values[12];
    +  mat[13] = values[13];
    +  mat[14] = values[14];
    +  mat[15] = values[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in row major order.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Mat4.AnyType} values The row major ordered array of
    + *     values to store in the matrix.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setFromRowMajorArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[4];
    +  mat[2] = values[8];
    +  mat[3] = values[12];
    +
    +  mat[4] = values[1];
    +  mat[5] = values[5];
    +  mat[6] = values[9];
    +  mat[7] = values[13];
    +
    +  mat[8] = values[2];
    +  mat[9] = values[6];
    +  mat[10] = values[10];
    +  mat[11] = values[14];
    +
    +  mat[12] = values[3];
    +  mat[13] = values[7];
    +  mat[14] = values[11];
    +  mat[15] = values[15];
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @param {number} v33 The values for (3, 3).
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setDiagonalValues = function(mat, v00, v11, v22, v33) {
    +  mat[0] = v00;
    +  mat[5] = v11;
    +  mat[10] = v22;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec4.AnyType} vec The vector containing the values.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[5] = vec[1];
    +  mat[10] = vec[2];
    +  mat[15] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Gets the diagonal values of the matrix into the given vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix containing the values.
    + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values.
    + * @param {number=} opt_diagonal Which diagonal to get. A value of 0 selects the
    + *     main diagonal, a positive number selects a super diagonal and a negative
    + *     number selects a sub diagonal.
    + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.getDiagonal = function(mat, vec, opt_diagonal) {
    +  if (!opt_diagonal) {
    +    // This is the most common case, so we avoid the for loop.
    +    vec[0] = mat[0];
    +    vec[1] = mat[5];
    +    vec[2] = mat[10];
    +    vec[3] = mat[15];
    +  } else {
    +    var offset = opt_diagonal > 0 ? 4 * opt_diagonal : -opt_diagonal;
    +    for (var i = 0; i < 4 - Math.abs(opt_diagonal); i++) {
    +      vec[i] = mat[offset + 5 * i];
    +    }
    +  }
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @param {number} v3 The value for row 3.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setColumnValues = function(mat, column, v0, v1, v2, v3) {
    +  var i = column * 4;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  mat[i + 3] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.Vec4.AnyType} vec The vector of elements for the column.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  mat[i + 3] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.Vec4.AnyType} vec The vector of elements to
    + *     receive the column.
    + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.getColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  vec[3] = mat[i + 3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the given vectors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec4.AnyType} vec0 The values for column 0.
    + * @param {goog.vec.Vec4.AnyType} vec1 The values for column 1.
    + * @param {goog.vec.Vec4.AnyType} vec2 The values for column 2.
    + * @param {goog.vec.Vec4.AnyType} vec3 The values for column 3.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Mat4.setColumn(mat, 0, vec0);
    +  goog.vec.Mat4.setColumn(mat, 1, vec1);
    +  goog.vec.Mat4.setColumn(mat, 2, vec2);
    +  goog.vec.Mat4.setColumn(mat, 3, vec3);
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vectors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the columns.
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive column 0.
    + * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive column 1.
    + * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive column 2.
    + * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive column 3.
    + */
    +goog.vec.Mat4.getColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Mat4.getColumn(mat, 0, vec0);
    +  goog.vec.Mat4.getColumn(mat, 1, vec1);
    +  goog.vec.Mat4.getColumn(mat, 2, vec2);
    +  goog.vec.Mat4.getColumn(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @param {number} v3 The value for column 3.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setRowValues = function(mat, row, v0, v1, v2, v3) {
    +  mat[row] = v0;
    +  mat[row + 4] = v1;
    +  mat[row + 8] = v2;
    +  mat[row + 12] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.Vec4.AnyType} vec The vector containing the values.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 4] = vec[1];
    +  mat[row + 8] = vec[2];
    +  mat[row + 12] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the row.
    + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 4];
    +  vec[2] = mat[row + 8];
    +  vec[3] = mat[row + 12];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
    + * @param {goog.vec.Vec4.AnyType} vec0 The values for row 0.
    + * @param {goog.vec.Vec4.AnyType} vec1 The values for row 1.
    + * @param {goog.vec.Vec4.AnyType} vec2 The values for row 2.
    + * @param {goog.vec.Vec4.AnyType} vec3 The values for row 3.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.setRows = function(mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Mat4.setRow(mat, 0, vec0);
    +  goog.vec.Mat4.setRow(mat, 1, vec1);
    +  goog.vec.Mat4.setRow(mat, 2, vec2);
    +  goog.vec.Mat4.setRow(mat, 3, vec3);
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to supply the values.
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive row 0.
    + * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive row 1.
    + * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive row 2.
    + * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive row 3.
    + */
    +goog.vec.Mat4.getRows = function(mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Mat4.getRow(mat, 0, vec0);
    +  goog.vec.Mat4.getRow(mat, 1, vec1);
    +  goog.vec.Mat4.getRow(mat, 2, vec2);
    +  goog.vec.Mat4.getRow(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the zero matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @return {!goog.vec.Mat4.AnyType} return mat so operations can be chained.
    + */
    +goog.vec.Mat4.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 0;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the identity matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @return {goog.vec.Mat4.AnyType} return mat so operations can be chained.
    + */
    +goog.vec.Mat4.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrix mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat0 The first addend.
    + * @param {goog.vec.Mat4.AnyType} mat1 The second addend.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  resultMat[9] = mat0[9] + mat1[9];
    +  resultMat[10] = mat0[10] + mat1[10];
    +  resultMat[11] = mat0[11] + mat1[11];
    +  resultMat[12] = mat0[12] + mat1[12];
    +  resultMat[13] = mat0[13] + mat1[13];
    +  resultMat[14] = mat0[14] + mat1[14];
    +  resultMat[15] = mat0[15] + mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrix mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat0 The minuend.
    + * @param {goog.vec.Mat4.AnyType} mat1 The subtrahend.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  resultMat[9] = mat0[9] - mat1[9];
    +  resultMat[10] = mat0[10] - mat1[10];
    +  resultMat[11] = mat0[11] - mat1[11];
    +  resultMat[12] = mat0[12] - mat1[12];
    +  resultMat[13] = mat0[13] - mat1[13];
    +  resultMat[14] = mat0[14] - mat1[14];
    +  resultMat[15] = mat0[15] - mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} scalar The scalar value to multiply to each element of mat.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  resultMat[9] = mat[9] * scalar;
    +  resultMat[10] = mat[10] * scalar;
    +  resultMat[11] = mat[11] * scalar;
    +  resultMat[12] = mat[12] * scalar;
    +  resultMat[13] = mat[13] * scalar;
    +  resultMat[14] = mat[14] * scalar;
    +  resultMat[15] = mat[15] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat0 The first (left hand) matrix.
    + * @param {goog.vec.Mat4.AnyType} mat1 The second (right hand) matrix.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
    +  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
    +  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
    +  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
    +  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
    +  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
    +  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
    +  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
    +
    +  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
    +  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
    +  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
    +  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
    +
    +  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
    +  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
    +  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
    +  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
    +
    +  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
    +  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
    +  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
    +  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to transpose.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
    +    var a21 = mat[6], a31 = mat[7];
    +    var a32 = mat[11];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +    resultMat[4] = a10;
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +    resultMat[8] = a20;
    +    resultMat[9] = a21;
    +    resultMat[11] = mat[14];
    +    resultMat[12] = a30;
    +    resultMat[13] = a31;
    +    resultMat[14] = a32;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +
    +    resultMat[4] = mat[1];
    +    resultMat[5] = mat[5];
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +
    +    resultMat[8] = mat[2];
    +    resultMat[9] = mat[6];
    +    resultMat[10] = mat[10];
    +    resultMat[11] = mat[14];
    +
    +    resultMat[12] = mat[3];
    +    resultMat[13] = mat[7];
    +    resultMat[14] = mat[11];
    +    resultMat[15] = mat[15];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the determinant of the matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to compute the matrix for.
    + * @return {number} The determinant of the matrix.
    + */
    +goog.vec.Mat4.determinant = function(mat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix to invert.
    + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
    + *     the result (may be mat).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.Mat4.invert = function(mat, resultMat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1.0 / det;
    +  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
    +  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
    +  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
    +  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
    +  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
    +  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
    +  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
    +  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
    +  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
    +  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
    +  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
    +  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
    +  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
    +  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
    +  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
    +  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat0 The first matrix.
    + * @param {goog.vec.Mat4.AnyType} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.Mat4.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] &&
    +      mat0[1] == mat1[1] &&
    +      mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] &&
    +      mat0[4] == mat1[4] &&
    +      mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] &&
    +      mat0[7] == mat1[7] &&
    +      mat0[8] == mat1[8] &&
    +      mat0[9] == mat1[9] &&
    +      mat0[10] == mat1[10] &&
    +      mat0[11] == mat1[11] &&
    +      mat0[12] == mat1[12] &&
    +      mat0[13] == mat1[13] &&
    +      mat0[14] == mat1[14] &&
    +      mat0[15] == mat1[15];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
    + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
    + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x3 matrix omitting the projective component and translation
    + * components.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
    + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
    + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multVec3NoTranslate = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
    + * vector to a 3 element vector.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
    + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
    + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector
    + *     to receive the results (may be vec).
    + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multVec3Projective = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
    +  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
    +  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
    +  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
    + * @param {goog.vec.Vec4.AnyType} vec The vector to transform.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.Vec4.AnyType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.multVec4 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
    +  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a translation matrix with x, y and z
    + * translation factors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeTranslate = function(mat, x, y, z) {
    +  goog.vec.Mat4.makeIdentity(mat);
    +  return goog.vec.Mat4.setColumnValues(mat, 3, x, y, z, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix as a scale matrix with x, y and z scale factors.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeScale = function(mat, x, y, z) {
    +  goog.vec.Mat4.makeIdentity(mat);
    +  return goog.vec.Mat4.setDiagonalValues(mat, x, y, z, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  return goog.vec.Mat4.setFromValues(mat,
    +      ax * ax * d + c,
    +      ax * ay * d + az * s,
    +      ax * az * d - ay * s,
    +      0,
    +
    +      ax * ay * d - az * s,
    +      ay * ay * d + c,
    +      ay * az * d + ax * s,
    +      0,
    +
    +      ax * az * d + ay * s,
    +      ay * az * d - ax * s,
    +      az * az * d + c,
    +      0,
    +
    +      0, 0, 0, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat4.setFromValues(
    +      mat, 1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat4.setFromValues(
    +      mat, c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +  return goog.vec.Mat4.setFromValues(
    +      mat, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a perspective projection matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeFrustum = function(mat, left, right, bottom, top, near, far) {
    +  var x = (2 * near) / (right - left);
    +  var y = (2 * near) / (top - bottom);
    +  var a = (right + left) / (right - left);
    +  var b = (top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +  var d = -(2 * far * near) / (far - near);
    +
    +  return goog.vec.Mat4.setFromValues(mat,
    +      x, 0, 0, 0,
    +      0, y, 0, 0,
    +      a, b, c, -1,
    +      0, 0, d, 0
    +  );
    +};
    +
    +
    +/**
    + * Makse the given 4x4 matrix  perspective projection matrix given a
    + * field of view and aspect ratio.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} fovy The field of view along the y (vertical) axis in
    + *     radians.
    + * @param {number} aspect The x (width) to y (height) aspect ratio.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makePerspective = function(mat, fovy, aspect, near, far) {
    +  var angle = fovy / 2;
    +  var dz = far - near;
    +  var sinAngle = Math.sin(angle);
    +  if (dz == 0 || sinAngle == 0 || aspect == 0) {
    +    return mat;
    +  }
    +
    +  var cot = Math.cos(angle) / sinAngle;
    +  return goog.vec.Mat4.setFromValues(mat,
    +      cot / aspect, 0, 0, 0,
    +      0, cot, 0, 0,
    +      0, 0, -(far + near) / dz, -1,
    +      0, 0, -(2 * near * far) / dz, 0
    +  );
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix an orthographic projection matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeOrtho = function(mat, left, right, bottom, top, near, far) {
    +  var x = 2 / (right - left);
    +  var y = 2 / (top - bottom);
    +  var z = -2 / (far - near);
    +  var a = -(right + left) / (right - left);
    +  var b = -(top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +
    +  return goog.vec.Mat4.setFromValues(mat,
    +      x, 0, 0, 0,
    +      0, y, 0, 0,
    +      0, 0, z, 0,
    +      a, b, c, 1
    +  );
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a modelview matrix of a camera so that
    + * the camera is 'looking at' the given center point.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.Vec3.AnyType} centerPt The point to aim the camera at.
    + * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that identifies
    + *     the up direction for the camera.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeLookAt = function(mat, eyePt, centerPt, worldUpVec) {
    +  // Compute the direction vector from the eye point to the center point and
    +  // normalize.
    +  var fwdVec = goog.vec.Mat4.tmpVec4_[0];
    +  goog.vec.Vec3.subtract(centerPt, eyePt, fwdVec);
    +  goog.vec.Vec3.normalize(fwdVec, fwdVec);
    +  fwdVec[3] = 0;
    +
    +  // Compute the side vector from the forward vector and the input up vector.
    +  var sideVec = goog.vec.Mat4.tmpVec4_[1];
    +  goog.vec.Vec3.cross(fwdVec, worldUpVec, sideVec);
    +  goog.vec.Vec3.normalize(sideVec, sideVec);
    +  sideVec[3] = 0;
    +
    +  // Now the up vector to form the orthonormal basis.
    +  var upVec = goog.vec.Mat4.tmpVec4_[2];
    +  goog.vec.Vec3.cross(sideVec, fwdVec, upVec);
    +  goog.vec.Vec3.normalize(upVec, upVec);
    +  upVec[3] = 0;
    +
    +  // Update the view matrix with the new orthonormal basis and position the
    +  // camera at the given eye point.
    +  goog.vec.Vec3.negate(fwdVec, fwdVec);
    +  goog.vec.Mat4.setRow(mat, 0, sideVec);
    +  goog.vec.Mat4.setRow(mat, 1, upVec);
    +  goog.vec.Mat4.setRow(mat, 2, fwdVec);
    +  goog.vec.Mat4.setRowValues(mat, 3, 0, 0, 0, 1);
    +  goog.vec.Mat4.translate(
    +      mat, -eyePt[0], -eyePt[1], -eyePt[2]);
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
    + * The matrix represents the modelview matrix of a camera. It is the inverse
    + * of lookAt except for the output of the fwdVec instead of centerPt.
    + * The centerPt itself cannot be recovered from a modelview matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.Vec3.AnyType} fwdVec The vector describing where
    + *     the camera points to.
    + * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that
    + *     identifies the up direction for the camera.
    + * @return {boolean} True if the method succeeds, false otherwise.
    + *     The method can only fail if the inverse of viewMatrix is not defined.
    + */
    +goog.vec.Mat4.toLookAt = function(mat, eyePt, fwdVec, worldUpVec) {
    +  // Get eye of the camera.
    +  var matInverse = goog.vec.Mat4.tmpMat4_[0];
    +  if (!goog.vec.Mat4.invert(mat, matInverse)) {
    +    // The input matrix does not have a valid inverse.
    +    return false;
    +  }
    +
    +  if (eyePt) {
    +    eyePt[0] = matInverse[12];
    +    eyePt[1] = matInverse[13];
    +    eyePt[2] = matInverse[14];
    +  }
    +
    +  // Get forward vector from the definition of lookAt.
    +  if (fwdVec || worldUpVec) {
    +    if (!fwdVec) {
    +      fwdVec = goog.vec.Mat4.tmpVec3_[0];
    +    }
    +    fwdVec[0] = -mat[2];
    +    fwdVec[1] = -mat[6];
    +    fwdVec[2] = -mat[10];
    +    // Normalize forward vector.
    +    goog.vec.Vec3.normalize(fwdVec, fwdVec);
    +  }
    +
    +  if (worldUpVec) {
    +    // Get side vector from the definition of gluLookAt.
    +    var side = goog.vec.Mat4.tmpVec3_[1];
    +    side[0] = mat[0];
    +    side[1] = mat[4];
    +    side[2] = mat[8];
    +    // Compute up vector as a up = side x forward.
    +    goog.vec.Vec3.cross(side, fwdVec, worldUpVec);
    +    // Normalize up vector.
    +    goog.vec.Vec3.normalize(worldUpVec, worldUpVec);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians,
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +  mat[3] = 0;
    +
    +  mat[4] = -c1 * s3 - c3 * c2 * s1;
    +  mat[5] = c1 * c2 * c3 - s1 * s3;
    +  mat[6] = c3 * s2;
    +  mat[7] = 0;
    +
    +  mat[8] = s2 * s1;
    +  mat[9] = -c1 * s2;
    +  mat[10] = c2;
    +  mat[11] = 0;
    +
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {goog.vec.Vec3.AnyType} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {goog.vec.Vec4.AnyType} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.Mat4.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[6] * mat[6]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[6] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[0] = Math.atan2(mat[8] * signTheta2, -mat[9] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    +
    +
    +/**
    + * Translates the given matrix by x,y,z.  Equvialent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeTranslate(goog.vec.Mat4.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.translate = function(mat, x, y, z) {
    +  return goog.vec.Mat4.setColumnValues(
    +      mat, 3,
    +      mat[0] * x + mat[4] * y + mat[8] * z + mat[12],
    +      mat[1] * x + mat[5] * y + mat[9] * z + mat[13],
    +      mat[2] * x + mat[6] * y + mat[10] * z + mat[14],
    +      mat[3] * x + mat[7] * y + mat[11] * z + mat[15]);
    +};
    +
    +
    +/**
    + * Scales the given matrix by x,y,z.  Equivalent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeScale(goog.vec.Mat4.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} x The x scale factor.
    + * @param {number} y The y scale factor.
    + * @param {number} z The z scale factor.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.scale = function(mat, x, y, z) {
    +  return goog.vec.Mat4.setFromValues(
    +      mat,
    +      mat[0] * x, mat[1] * x, mat[2] * x, mat[3] * x,
    +      mat[4] * y, mat[5] * y, mat[6] * y, mat[7] * y,
    +      mat[8] * z, mat[9] * z, mat[10] * z, mat[11] * z,
    +      mat[12], mat[13], mat[14], mat[15]);
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeRotate(goog.vec.Mat4.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  return goog.vec.Mat4.setFromValues(
    +      mat,
    +      m00 * r00 + m01 * r10 + m02 * r20,
    +      m10 * r00 + m11 * r10 + m12 * r20,
    +      m20 * r00 + m21 * r10 + m22 * r20,
    +      m30 * r00 + m31 * r10 + m32 * r20,
    +
    +      m00 * r01 + m01 * r11 + m02 * r21,
    +      m10 * r01 + m11 * r11 + m12 * r21,
    +      m20 * r01 + m21 * r11 + m22 * r21,
    +      m30 * r01 + m31 * r11 + m32 * r21,
    +
    +      m00 * r02 + m01 * r12 + m02 * r22,
    +      m10 * r02 + m11 * r12 + m12 * r22,
    +      m20 * r02 + m21 * r12 + m22 * r22,
    +      m30 * r02 + m31 * r12 + m32 * r22,
    +
    +      m03, m13, m23, m33);
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeRotateX(goog.vec.Mat4.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.rotateX = function(mat, angle) {
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[4] = m01 * c + m02 * s;
    +  mat[5] = m11 * c + m12 * s;
    +  mat[6] = m21 * c + m22 * s;
    +  mat[7] = m31 * c + m32 * s;
    +  mat[8] = m01 * -s + m02 * c;
    +  mat[9] = m11 * -s + m12 * c;
    +  mat[10] = m21 * -s + m22 * c;
    +  mat[11] = m31 * -s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeRotateY(goog.vec.Mat4.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[3] = m30 * c + m32 * -s;
    +  mat[8] = m00 * s + m02 * c;
    +  mat[9] = m10 * s + m12 * c;
    +  mat[10] = m20 * s + m22 * c;
    +  mat[11] = m30 * s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.Mat4.multMat(
    + *     mat,
    + *     goog.vec.Mat4.makeRotateZ(goog.vec.Mat4.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m30 * c + m31 * s;
    +  mat[4] = m00 * -s + m01 * c;
    +  mat[5] = m10 * -s + m11 * c;
    +  mat[6] = m20 * -s + m21 * c;
    +  mat[7] = m30 * -s + m31 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the translation component of the transformation matrix.
    + *
    + * @param {goog.vec.Mat4.AnyType} mat The transformation matrix.
    + * @param {goog.vec.Vec3.AnyType} translation The vector for storing the
    + *     result.
    + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.Mat4.getTranslation = function(mat, translation) {
    +  translation[0] = mat[12];
    +  translation[1] = mat[13];
    +  translation[2] = mat[14];
    +  return translation;
    +};
    +
    +
    +/**
    + * @type {!Array<!goog.vec.Vec3.Type>}
    + * @private
    + */
    +goog.vec.Mat4.tmpVec3_ = [
    +  goog.vec.Vec3.createFloat64(),
    +  goog.vec.Vec3.createFloat64()
    +];
    +
    +
    +/**
    + * @type {!Array<!goog.vec.Vec4.Type>}
    + * @private
    + */
    +goog.vec.Mat4.tmpVec4_ = [
    +  goog.vec.Vec4.createFloat64(),
    +  goog.vec.Vec4.createFloat64(),
    +  goog.vec.Vec4.createFloat64()
    +];
    +
    +
    +/**
    + * @type {!Array<!goog.vec.Mat4.Type>}
    + * @private
    + */
    +goog.vec.Mat4.tmpMat4_ = [
    +  goog.vec.Mat4.createFloat64()
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat4_test.html
    new file mode 100644
    index 0000000000..3f8c6c2024
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4_test.html
    @@ -0,0 +1,781 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Mat4</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Mat4');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randomMat4 = goog.vec.Mat4.createFloat32FromValues(
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197,
    +      0.5310639142990112,
    +      0.8962187170982361,
    +      0.280601441860199,
    +      0.594650387763977,
    +      0.4134795069694519,
    +      0.06632178276777267,
    +      0.8837796449661255);
    +
    +  function testDeprecatedConstructor() {
    +    var m0 = goog.vec.Mat4.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Mat4.createFromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +
    +    var m2 = goog.vec.Mat4.clone(m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m2);
    +
    +    var m3 = goog.vec.Mat4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m3);
    +
    +    var m4 = goog.vec.Mat4.createIdentity();
    +    assertElementsEquals([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m4);
    +  }
    +
    +  function testConstructor() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +
    +    var m2 = goog.vec.Mat4.clone(m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m2);
    +
    +    var m3 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m3);
    +
    +    var m4 = goog.vec.Mat4.createIdentity();
    +    assertElementsEquals([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m4);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    goog.vec.Mat4.setFromArray(m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    goog.vec.Mat4.setFromValues(
    +        m0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setDiagonalValues(m0, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], m0);
    +
    +    goog.vec.Mat4.setDiagonal(m0, [4, 5, 6, 7]);
    +    assertElementsEquals(
    +        [4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7], m0);
    +  }
    +
    +  function testGetDiagonal() {
    +    var v0 = goog.vec.Vec4.create();
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setFromArray(
    +        m0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
    +
    +    goog.vec.Mat4.getDiagonal(m0, v0);
    +    assertElementsEquals([0, 5, 10, 15], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, 1);
    +    assertElementsEquals([4, 9, 14, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, 2);
    +    assertElementsEquals([8, 13, 0, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, 3);
    +    assertElementsEquals([12, 0, 0, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, 4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, -1);
    +    assertElementsEquals([1, 6, 11, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, -2);
    +    assertElementsEquals([2, 7, 0, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, -3);
    +    assertElementsEquals([3, 0, 0, 0], v0);
    +
    +    goog.vec.Vec4.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.Mat4.getDiagonal(m0, v0, -4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setColumn(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.Mat4.setColumn(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.Mat4.setColumn(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.Mat4.setColumn(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.Mat4.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.Mat4.getColumn(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.Mat4.getColumn(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.Mat4.getColumn(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setColumns(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.Mat4.getColumns(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setRow(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.Mat4.setRow(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.Mat4.setRow(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.Mat4.setRow(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.Mat4.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.Mat4.getRow(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.Mat4.getRow(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.Mat4.getRow(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setRows(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.Mat4.getRows(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetRowMajorArray() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setFromRowMajorArray(
    +        m0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.Mat4.createFloat32FromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    goog.vec.Mat4.makeZero(m0);
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeIdentity(m0);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    for (var r = 0; r < 4; r++) {
    +      for (var c = 0; c < 4; c++) {
    +        var value = c * 4 + r + 1;
    +        goog.vec.Mat4.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.Mat4.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.createFloat32FromValues(
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.addMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m2);
    +
    +    goog.vec.Mat4.addMat(m0, m1, m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.createFloat32FromValues(
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.Mat4.createFloat32();
    +
    +    goog.vec.Mat4.subMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [-8, -8, -8, -8, -8, -8, -8, -8, 8, 8, 8, 8, 8, 8, 8, 8], m2);
    +
    +    goog.vec.Mat4.subMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [8, 8, 8, 8, 8, 8, 8, 8, -8, -8, -8, -8, -8, -8, -8, -8], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.createFloat32();
    +
    +    goog.vec.Mat4.multScalar(m0, 2, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32], m1);
    +
    +    goog.vec.Mat4.multScalar(m0, 5, m0);
    +    assertElementsEquals(
    +        [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m2 = goog.vec.Mat4.createFloat32();
    +
    +    goog.vec.Mat4.multMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [90, 100, 110, 120, 202, 228, 254, 280,
    +         314, 356, 398, 440, 426, 484, 542, 600], m2);
    +
    +    goog.vec.Mat4.multScalar(m1, 2, m1);
    +    goog.vec.Mat4.multMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [180, 200, 220, 240, 404, 456, 508, 560,
    +         628, 712, 796, 880, 852, 968, 1084, 1200], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.transpose(m0, m1);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m1);
    +
    +    goog.vec.Mat4.transpose(m1, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +  }
    +
    +  function testDeterminant() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertEquals(0, goog.vec.Mat4.determinant(m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Mat4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertEquals(160, goog.vec.Mat4.determinant(m0));
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3], m0);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.Mat4.invert(m0, m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Mat4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertTrue(goog.vec.Mat4.invert(m0, m0));
    +    assertElementsRoughlyEqual(
    +        [-0.225, 0.025, 0.025, 0.275, 0.025, 0.025, 0.275, -0.225,
    +         0.025, 0.275, -0.225, 0.025, 0.275, -0.225, 0.025, 0.025], m0,
    +         goog.vec.EPSILON);
    +
    +    goog.vec.Mat4.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.Mat4.invert(m0, m0));
    +    var m1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Mat4.clone(m0);
    +    assertTrue(goog.vec.Mat4.equals(m0, m1));
    +    assertTrue(goog.vec.Mat4.equals(m1, m0));
    +    for (var i = 0; i < 16; i++) {
    +      m1[i] = 18;
    +      assertFalse(goog.vec.Mat4.equals(m0, m1));
    +      assertFalse(goog.vec.Mat4.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Mat4.multVec3(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([51, 58, 65], v1);
    +
    +    goog.vec.Mat4.multVec3(m0, v0, v0);
    +    assertElementsEquals([51, 58, 65], v0);
    +  }
    +
    +  function testMultVec3NoTranslate() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Mat4.multVec3NoTranslate(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([38, 44, 50], v1);
    +
    +    goog.vec.Mat4.multVec3NoTranslate(m0, v0, v0);
    +    assertElementsEquals([38, 44, 50], v0);
    +  }
    +
    +  function testMultVec3Projective() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +    var invw = 1 / 72;
    +
    +    goog.vec.Mat4.multVec3Projective(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v1);
    +
    +    goog.vec.Mat4.multVec3Projective(m0, v0, v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v0);
    +  }
    +
    +  function testMultVec4() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3, 4];
    +    var v1 = [0, 0, 0, 0];
    +
    +    goog.vec.Mat4.multVec4(m0, v0, v1);
    +    assertElementsEquals([90, 100, 110, 120], v1);
    +    goog.vec.Mat4.multVec4(m0, v0, v0);
    +    assertElementsEquals([90, 100, 110, 120], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.Mat4.createFloat32();
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    a0 = goog.vec.Mat4.createFloat32FromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a0);
    +
    +    var a1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.setDiagonalValues(a1, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], a1);
    +
    +    goog.vec.Mat4.setColumnValues(a1, 0, 2, 3, 4, 5);
    +    goog.vec.Mat4.setColumnValues(a1, 1, 6, 7, 8, 9);
    +    goog.vec.Mat4.setColumnValues(a1, 2, 10, 11, 12, 13);
    +    goog.vec.Mat4.setColumnValues(a1, 3, 14, 15, 16, 1);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1], a1);
    +
    +    goog.vec.Mat4.setRowValues(a1, 0, 1, 5, 9, 13);
    +    goog.vec.Mat4.setRowValues(a1, 1, 2, 6, 10, 14);
    +    goog.vec.Mat4.setRowValues(a1, 2, 3, 7, 11, 15);
    +    goog.vec.Mat4.setRowValues(a1, 3, 4, 8, 12, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeTranslate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.Mat4.multMat(m0, m1, m1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32()
    +
    +    goog.vec.Mat4.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.Mat4.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32()
    +
    +    goog.vec.Mat4.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.Mat4.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32()
    +
    +    goog.vec.Mat4.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.Mat4.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +
    +  function testTranslate() {
    +    var m0 = goog.vec.Mat4.createIdentity();
    +    goog.vec.Mat4.translate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +
    +    goog.vec.Mat4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +
    +    var m1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeTranslate(m1, 5, 6, 7);
    +    var m2 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.multMat(m0, m1, m2);
    +    goog.vec.Mat4.translate(m0, 5, 6, 7);
    +    assertElementsEquals(m2, m0);
    +  }
    +
    +  function testScale() {
    +    var m0 = goog.vec.Mat4.createIdentity();
    +    goog.vec.Mat4.scale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.Mat4.createIdentity();
    +    goog.vec.Mat4.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.Mat4.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(randomMat4)
    +
    +    goog.vec.Mat4.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.Mat4.multMat(m1, m0, m0);
    +    goog.vec.Mat4.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(randomMat4)
    +
    +    goog.vec.Mat4.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.Mat4.multMat(m1, m0, m0);
    +    goog.vec.Mat4.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(randomMat4)
    +
    +    goog.vec.Mat4.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.Mat4.multMat(m1, m0, m0);
    +    goog.vec.Mat4.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testGetTranslation() {
    +    var mat = goog.vec.Mat4.createFloat32FromArray(randomMat4);
    +    var translation = goog.vec.Vec3.createFloat32();
    +    goog.vec.Mat4.getTranslation(mat, translation);
    +    assertElementsRoughlyEqual(
    +        [0.59465038776, 0.413479506969, 0.0663217827677],
    +        translation, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeFrustum() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeFrustum(m0, -1, 2, -2, 1, .1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.06666666, 0, 0, 0,
    +         0, 0.06666666, 0, 0,
    +         0.33333333, -0.33333333, -1.2, -1,
    +         0, 0, -0.22, 0], m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakePerspective() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makePerspective(m0, 90 * Math.PI / 180, 2, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1.2, -1, 0, 0, -0.22, 0],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeOrtho() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeOrtho(m0, -1, 2, -2, 1, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.6666666, 0, 0, 0,
    +         0, 0.6666666, 0, 0,
    +         0, 0, -2, 0,
    +         -0.333333, 0.3333333, -1.2, 1], m0, goog.vec.EPSILON);
    +
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.Mat4.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.Mat4.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.Mat4.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat4.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.Mat4.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.Mat4.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.Mat4.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.Mat4.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat4.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.Mat4.createFloat32FromArray(
    +    [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
    +    var m1 = goog.vec.Mat4.createFloat32FromArray(
    +    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Mat4.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.Mat4.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testLookAt() {
    +    var viewMatrix = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeLookAt(
    +      viewMatrix, [0, 0, 0], [1, 0, 0], [0, 1, 0]);
    +    assertElementsRoughlyEqual(
    +      [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], viewMatrix,
    +      goog.vec.EPSILON);
    +  }
    +
    +  function testToLookAt() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [1, 0, 0];
    +    var upExp = [0, 1, 0];
    +
    +    var centerExp = [0, 0, 0];
    +    goog.vec.Vec3.add(eyeExp, fwdExp, centerExp);
    +
    +    var view = goog.vec.Mat4.createFloat32();
    +    goog.vec.Mat4.makeLookAt(view, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    goog.vec.Mat4.toLookAt(view, eyeRes, fwdRes, upRes);
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +  }
    +
    +  function testLookAtDecomposition() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var viewExp = goog.vec.Mat4.createFloat32();
    +    var viewRes = goog.vec.Mat4.createFloat32();
    +
    +    // Get a valid set of random vectors eye, forward, up by decomposing
    +    // a random matrix into a set of lookAt vectors.
    +    var tmp = goog.vec.Mat4.createFloat32FromArray(randomMat4);
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [0, 0, 0];
    +    var upExp = [0, 0, 0];
    +    var centerExp = [0, 0, 0];
    +    // Project the random matrix into a real modelview matrix.
    +    goog.vec.Mat4.toLookAt(tmp, eyeExp, fwdExp, upExp);
    +    goog.vec.Vec3.add(eyeExp, fwdExp, centerExp);
    +
    +    // Compute the expected modelview matrix from a set of valid random vectors.
    +    goog.vec.Mat4.makeLookAt(viewExp, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    var centerRes = [0, 0, 0];
    +    goog.vec.Mat4.toLookAt(viewExp, eyeRes, fwdRes, upRes);
    +    goog.vec.Vec3.add(eyeRes, fwdRes, centerRes);
    +
    +    goog.vec.Mat4.makeLookAt(viewRes, eyeRes, centerRes, upRes);
    +
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +    assertElementsRoughlyEqual(viewExp, viewRes, EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4d.js b/mapbox-gl/closure-library/closure/goog/vec/mat4d.js
    new file mode 100644
    index 0000000000..ad0a83171c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4d.js
    @@ -0,0 +1,1769 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat4f.js by running:            //
    +//   swap_type.sh mat4d.js > mat4f.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 4x4 double (64bit)
    + * matrices.  The matrices are stored in column-major order.
    + *
    + * The last parameter will typically be the output matrix and an
    + * object can be both an input and output parameter to all methods except
    + * where noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.mat4d');
    +goog.provide('goog.vec.mat4d.Type');
    +
    +goog.require('goog.vec');
    +goog.require('goog.vec.vec3d');
    +goog.require('goog.vec.vec4d');
    +
    +
    +/** @typedef {goog.vec.Float64} */ goog.vec.mat4d.Type;
    +
    +
    +/**
    + * Creates a mat4d with all elements initialized to zero.
    + *
    + * @return {!goog.vec.mat4d.Type} The new mat4d.
    + */
    +goog.vec.mat4d.create = function() {
    +  return new Float64Array(16);
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setFromValues = function(
    +    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v30;
    +  mat[4] = v01;
    +  mat[5] = v11;
    +  mat[6] = v21;
    +  mat[7] = v31;
    +  mat[8] = v02;
    +  mat[9] = v12;
    +  mat[10] = v22;
    +  mat[11] = v32;
    +  mat[12] = v03;
    +  mat[13] = v13;
    +  mat[14] = v23;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4d mat from mat4d src.
    + *
    + * @param {goog.vec.mat4d.Type} mat The destination matrix.
    + * @param {goog.vec.mat4d.Type} src The source matrix.
    + * @return {!goog.vec.mat4d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setFromMat4d = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4d mat from mat4f src (typed as a Float32Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.mat4d.Type} mat The destination matrix.
    + * @param {Float32Array} src The source matrix.
    + * @return {!goog.vec.mat4d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setFromMat4f = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4d mat from Array src.
    + *
    + * @param {goog.vec.mat4d.Type} mat The destination matrix.
    + * @param {Array<number>} src The source matrix.
    + * @return {!goog.vec.mat4d.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setFromArray = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.mat4d.getElement = function(mat, row, column) {
    +  return mat[row + column * 4];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setElement = function(mat, row, column, value) {
    +  mat[row + column * 4] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @param {number} v33 The values for (3, 3).
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setDiagonalValues = function(mat, v00, v11, v22, v33) {
    +  mat[0] = v00;
    +  mat[5] = v11;
    +  mat[10] = v22;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4d.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[5] = vec[1];
    +  mat[10] = vec[2];
    +  mat[15] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Gets the diagonal values of the matrix into the given vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix containing the values.
    + * @param {goog.vec.vec4d.Type} vec The vector to receive the values.
    + * @param {number=} opt_diagonal Which diagonal to get. A value of 0 selects the
    + *     main diagonal, a positive number selects a super diagonal and a negative
    + *     number selects a sub diagonal.
    + * @return {goog.vec.vec4d.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.getDiagonal = function(mat, vec, opt_diagonal) {
    +  if (!opt_diagonal) {
    +    // This is the most common case, so we avoid the for loop.
    +    vec[0] = mat[0];
    +    vec[1] = mat[5];
    +    vec[2] = mat[10];
    +    vec[3] = mat[15];
    +  } else {
    +    var offset = opt_diagonal > 0 ? 4 * opt_diagonal : -opt_diagonal;
    +    for (var i = 0; i < 4 - Math.abs(opt_diagonal); i++) {
    +      vec[i] = mat[offset + 5 * i];
    +    }
    +  }
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @param {number} v3 The value for row 3.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setColumnValues = function(mat, column, v0, v1, v2, v3) {
    +  var i = column * 4;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  mat[i + 3] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.vec4d.Type} vec The vector of elements for the column.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  mat[i + 3] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.vec4d.Type} vec The vector of elements to
    + *     receive the column.
    + * @return {!goog.vec.vec4d.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.getColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  vec[3] = mat[i + 3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the given vectors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4d.Type} vec0 The values for column 0.
    + * @param {goog.vec.vec4d.Type} vec1 The values for column 1.
    + * @param {goog.vec.vec4d.Type} vec2 The values for column 2.
    + * @param {goog.vec.vec4d.Type} vec3 The values for column 3.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  mat[0] = vec0[0];
    +  mat[1] = vec0[1];
    +  mat[2] = vec0[2];
    +  mat[3] = vec0[3];
    +  mat[4] = vec1[0];
    +  mat[5] = vec1[1];
    +  mat[6] = vec1[2];
    +  mat[7] = vec1[3];
    +  mat[8] = vec2[0];
    +  mat[9] = vec2[1];
    +  mat[10] = vec2[2];
    +  mat[11] = vec2[3];
    +  mat[12] = vec3[0];
    +  mat[13] = vec3[1];
    +  mat[14] = vec3[2];
    +  mat[15] = vec3[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vectors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the columns.
    + * @param {goog.vec.vec4d.Type} vec0 The vector to receive column 0.
    + * @param {goog.vec.vec4d.Type} vec1 The vector to receive column 1.
    + * @param {goog.vec.vec4d.Type} vec2 The vector to receive column 2.
    + * @param {goog.vec.vec4d.Type} vec3 The vector to receive column 3.
    + */
    +goog.vec.mat4d.getColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  vec0[0] = mat[0];
    +  vec0[1] = mat[1];
    +  vec0[2] = mat[2];
    +  vec0[3] = mat[3];
    +  vec1[0] = mat[4];
    +  vec1[1] = mat[5];
    +  vec1[2] = mat[6];
    +  vec1[3] = mat[7];
    +  vec2[0] = mat[8];
    +  vec2[1] = mat[9];
    +  vec2[2] = mat[10];
    +  vec2[3] = mat[11];
    +  vec3[0] = mat[12];
    +  vec3[1] = mat[13];
    +  vec3[2] = mat[14];
    +  vec3[3] = mat[15];
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @param {number} v3 The value for column 3.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setRowValues = function(mat, row, v0, v1, v2, v3) {
    +  mat[row] = v0;
    +  mat[row + 4] = v1;
    +  mat[row + 8] = v2;
    +  mat[row + 12] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.vec4d.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 4] = vec[1];
    +  mat[row + 8] = vec[2];
    +  mat[row + 12] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.vec4d.Type} vec The vector to receive the row.
    + * @return {!goog.vec.vec4d.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 4];
    +  vec[2] = mat[row + 8];
    +  vec[3] = mat[row + 12];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4d.Type} vec0 The values for row 0.
    + * @param {goog.vec.vec4d.Type} vec1 The values for row 1.
    + * @param {goog.vec.vec4d.Type} vec2 The values for row 2.
    + * @param {goog.vec.vec4d.Type} vec3 The values for row 3.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.setRows = function(mat, vec0, vec1, vec2, vec3) {
    +  mat[0] = vec0[0];
    +  mat[1] = vec1[0];
    +  mat[2] = vec2[0];
    +  mat[3] = vec3[0];
    +  mat[4] = vec0[1];
    +  mat[5] = vec1[1];
    +  mat[6] = vec2[1];
    +  mat[7] = vec3[1];
    +  mat[8] = vec0[2];
    +  mat[9] = vec1[2];
    +  mat[10] = vec2[2];
    +  mat[11] = vec3[2];
    +  mat[12] = vec0[3];
    +  mat[13] = vec1[3];
    +  mat[14] = vec2[3];
    +  mat[15] = vec3[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to supply the values.
    + * @param {goog.vec.vec4d.Type} vec0 The vector to receive row 0.
    + * @param {goog.vec.vec4d.Type} vec1 The vector to receive row 1.
    + * @param {goog.vec.vec4d.Type} vec2 The vector to receive row 2.
    + * @param {goog.vec.vec4d.Type} vec3 The vector to receive row 3.
    + */
    +goog.vec.mat4d.getRows = function(mat, vec0, vec1, vec2, vec3) {
    +  vec0[0] = mat[0];
    +  vec1[0] = mat[1];
    +  vec2[0] = mat[2];
    +  vec3[0] = mat[3];
    +  vec0[1] = mat[4];
    +  vec1[1] = mat[5];
    +  vec2[1] = mat[6];
    +  vec3[1] = mat[7];
    +  vec0[2] = mat[8];
    +  vec1[2] = mat[9];
    +  vec2[2] = mat[10];
    +  vec3[2] = mat[11];
    +  vec0[3] = mat[12];
    +  vec1[3] = mat[13];
    +  vec2[3] = mat[14];
    +  vec3[3] = mat[15];
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the zero matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @return {!goog.vec.mat4d.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat4d.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 0;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the identity matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @return {!goog.vec.mat4d.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat4d.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrix mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.mat4d.Type} mat0 The first addend.
    + * @param {goog.vec.mat4d.Type} mat1 The second addend.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  resultMat[9] = mat0[9] + mat1[9];
    +  resultMat[10] = mat0[10] + mat1[10];
    +  resultMat[11] = mat0[11] + mat1[11];
    +  resultMat[12] = mat0[12] + mat1[12];
    +  resultMat[13] = mat0[13] + mat1[13];
    +  resultMat[14] = mat0[14] + mat1[14];
    +  resultMat[15] = mat0[15] + mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrix mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4d.Type} mat0 The minuend.
    + * @param {goog.vec.mat4d.Type} mat1 The subtrahend.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  resultMat[9] = mat0[9] - mat1[9];
    +  resultMat[10] = mat0[10] - mat1[10];
    +  resultMat[11] = mat0[11] - mat1[11];
    +  resultMat[12] = mat0[12] - mat1[12];
    +  resultMat[13] = mat0[13] - mat1[13];
    +  resultMat[14] = mat0[14] - mat1[14];
    +  resultMat[15] = mat0[15] - mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} scalar The scalar value to multiply to each element of mat.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat4d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  resultMat[9] = mat[9] * scalar;
    +  resultMat[10] = mat[10] * scalar;
    +  resultMat[11] = mat[11] * scalar;
    +  resultMat[12] = mat[12] * scalar;
    +  resultMat[13] = mat[13] * scalar;
    +  resultMat[14] = mat[14] * scalar;
    +  resultMat[15] = mat[15] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4d.Type} mat0 The first (left hand) matrix.
    + * @param {goog.vec.mat4d.Type} mat1 The second (right hand) matrix.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
    +  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
    +  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
    +  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
    +  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
    +  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
    +  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
    +  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
    +
    +  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
    +  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
    +  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
    +  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
    +
    +  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
    +  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
    +  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
    +  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
    +
    +  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
    +  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
    +  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
    +  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to transpose.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat4d.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
    +    var a21 = mat[6], a31 = mat[7];
    +    var a32 = mat[11];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +    resultMat[4] = a10;
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +    resultMat[8] = a20;
    +    resultMat[9] = a21;
    +    resultMat[11] = mat[14];
    +    resultMat[12] = a30;
    +    resultMat[13] = a31;
    +    resultMat[14] = a32;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +
    +    resultMat[4] = mat[1];
    +    resultMat[5] = mat[5];
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +
    +    resultMat[8] = mat[2];
    +    resultMat[9] = mat[6];
    +    resultMat[10] = mat[10];
    +    resultMat[11] = mat[14];
    +
    +    resultMat[12] = mat[3];
    +    resultMat[13] = mat[7];
    +    resultMat[14] = mat[11];
    +    resultMat[15] = mat[15];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the determinant of the matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to compute the matrix for.
    + * @return {number} The determinant of the matrix.
    + */
    +goog.vec.mat4d.determinant = function(mat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix to invert.
    + * @param {goog.vec.mat4d.Type} resultMat The matrix to receive
    + *     the result (may be mat).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.mat4d.invert = function(mat, resultMat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1.0 / det;
    +  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
    +  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
    +  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
    +  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
    +  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
    +  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
    +  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
    +  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
    +  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
    +  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
    +  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
    +  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
    +  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
    +  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
    +  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
    +  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.mat4d.Type} mat0 The first matrix.
    + * @param {goog.vec.mat4d.Type} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.mat4d.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] &&
    +      mat0[1] == mat1[1] &&
    +      mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] &&
    +      mat0[4] == mat1[4] &&
    +      mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] &&
    +      mat0[7] == mat1[7] &&
    +      mat0[8] == mat1[8] &&
    +      mat0[9] == mat1[9] &&
    +      mat0[10] == mat1[10] &&
    +      mat0[11] == mat1[11] &&
    +      mat0[12] == mat1[12] &&
    +      mat0[13] == mat1[13] &&
    +      mat0[14] == mat1[14] &&
    +      mat0[15] == mat1[15];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3d.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3d.Type} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3d.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x3 matrix omitting the projective component and translation
    + * components.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3d.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3d.Type} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3d.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multVec3NoTranslate = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
    + * vector to a 3 element vector.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3d.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3d.Type} resultVec The 3 element vector
    + *     to receive the results (may be vec).
    + * @return {!goog.vec.vec3d.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multVec3Projective = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
    +  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
    +  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
    +  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec4d.Type} vec The vector to transform.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec4d.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.multVec4 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
    +  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a translation matrix with x, y and z
    + * translation factors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeTranslate = function(mat, x, y, z) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = x;
    +  mat[13] = y;
    +  mat[14] = z;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix as a scale matrix with x, y and z scale factors.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeScale = function(mat, x, y, z) {
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = z;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  mat[0] = ax * ax * d + c;
    +  mat[1] = ax * ay * d + az * s;
    +  mat[2] = ax * az * d - ay * s;
    +  mat[3] = 0;
    +  mat[4] = ax * ay * d - az * s;
    +  mat[5] = ay * ay * d + c;
    +  mat[6] = ay * az * d + ax * s;
    +  mat[7] = 0;
    +  mat[8] = ax * az * d + ay * s;
    +  mat[9] = ay * az * d - ax * s;
    +  mat[10] = az * az * d + c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = c;
    +  mat[6] = s;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = -s;
    +  mat[10] = c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = 0;
    +  mat[2] = -s;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = s;
    +  mat[9] = 0;
    +  mat[10] = c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = s;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = -s;
    +  mat[5] = c;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a perspective projection matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeFrustum = function(
    +    mat, left, right, bottom, top, near, far) {
    +  var x = (2 * near) / (right - left);
    +  var y = (2 * near) / (top - bottom);
    +  var a = (right + left) / (right - left);
    +  var b = (top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +  var d = -(2 * far * near) / (far - near);
    +
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = a;
    +  mat[9] = b;
    +  mat[10] = c;
    +  mat[11] = -1;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = d;
    +  mat[15] = 0;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makse the given 4x4 matrix  perspective projection matrix given a
    + * field of view and aspect ratio.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} fovy The field of view along the y (vertical) axis in
    + *     radians.
    + * @param {number} aspect The x (width) to y (height) aspect ratio.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makePerspective = function(mat, fovy, aspect, near, far) {
    +  var angle = fovy / 2;
    +  var dz = far - near;
    +  var sinAngle = Math.sin(angle);
    +  if (dz == 0 || sinAngle == 0 || aspect == 0) {
    +    return mat;
    +  }
    +
    +  var cot = Math.cos(angle) / sinAngle;
    +
    +  mat[0] = cot / aspect;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = cot;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = -(far + near) / dz;
    +  mat[11] = -1;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = -(2 * near * far) / dz;
    +  mat[15] = 0;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix an orthographic projection matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeOrtho = function(mat, left, right, bottom, top, near, far) {
    +  var x = 2 / (right - left);
    +  var y = 2 / (top - bottom);
    +  var z = -2 / (far - near);
    +  var a = -(right + left) / (right - left);
    +  var b = -(top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = z;
    +  mat[11] = 0;
    +  mat[12] = a;
    +  mat[13] = b;
    +  mat[14] = c;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a modelview matrix of a camera so that
    + * the camera is 'looking at' the given center point.
    + *
    + * Note that unlike most other goog.vec functions where we inline
    + * everything, this function does not inline various goog.vec
    + * functions.  This makes the code more readable, but somewhat
    + * less efficient.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {goog.vec.vec3d.Type} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.vec3d.Type} centerPt The point to aim the camera at.
    + * @param {goog.vec.vec3d.Type} worldUpVec The vector that identifies
    + *     the up direction for the camera.
    + * @return {goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeLookAt = function(mat, eyePt, centerPt, worldUpVec) {
    +  // Compute the direction vector from the eye point to the center point and
    +  // normalize.
    +  var fwdVec = goog.vec.mat4d.tmpvec4d_[0];
    +  goog.vec.vec3d.subtract(centerPt, eyePt, fwdVec);
    +  goog.vec.vec3d.normalize(fwdVec, fwdVec);
    +  fwdVec[3] = 0;
    +
    +  // Compute the side vector from the forward vector and the input up vector.
    +  var sideVec = goog.vec.mat4d.tmpvec4d_[1];
    +  goog.vec.vec3d.cross(fwdVec, worldUpVec, sideVec);
    +  goog.vec.vec3d.normalize(sideVec, sideVec);
    +  sideVec[3] = 0;
    +
    +  // Now the up vector to form the orthonormal basis.
    +  var upVec = goog.vec.mat4d.tmpvec4d_[2];
    +  goog.vec.vec3d.cross(sideVec, fwdVec, upVec);
    +  goog.vec.vec3d.normalize(upVec, upVec);
    +  upVec[3] = 0;
    +
    +  // Update the view matrix with the new orthonormal basis and position the
    +  // camera at the given eye point.
    +  goog.vec.vec3d.negate(fwdVec, fwdVec);
    +  goog.vec.mat4d.setRow(mat, 0, sideVec);
    +  goog.vec.mat4d.setRow(mat, 1, upVec);
    +  goog.vec.mat4d.setRow(mat, 2, fwdVec);
    +  goog.vec.mat4d.setRowValues(mat, 3, 0, 0, 0, 1);
    +  goog.vec.mat4d.translate(
    +      mat, -eyePt[0], -eyePt[1], -eyePt[2]);
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
    + * The matrix represents the modelview matrix of a camera. It is the inverse
    + * of lookAt except for the output of the fwdVec instead of centerPt.
    + * The centerPt itself cannot be recovered from a modelview matrix.
    + *
    + * Note that unlike most other goog.vec functions where we inline
    + * everything, this function does not inline various goog.vec
    + * functions.  This makes the code more readable, but somewhat
    + * less efficient.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {goog.vec.vec3d.Type} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.vec3d.Type} fwdVec The vector describing where
    + *     the camera points to.
    + * @param {goog.vec.vec3d.Type} worldUpVec The vector that
    + *     identifies the up direction for the camera.
    + * @return {boolean} True if the method succeeds, false otherwise.
    + *     The method can only fail if the inverse of viewMatrix is not defined.
    + */
    +goog.vec.mat4d.toLookAt = function(mat, eyePt, fwdVec, worldUpVec) {
    +  // Get eye of the camera.
    +  var matInverse = goog.vec.mat4d.tmpmat4d_[0];
    +  if (!goog.vec.mat4d.invert(mat, matInverse)) {
    +    // The input matrix does not have a valid inverse.
    +    return false;
    +  }
    +
    +  if (eyePt) {
    +    eyePt[0] = matInverse[12];
    +    eyePt[1] = matInverse[13];
    +    eyePt[2] = matInverse[14];
    +  }
    +
    +  // Get forward vector from the definition of lookAt.
    +  if (fwdVec || worldUpVec) {
    +    if (!fwdVec) {
    +      fwdVec = goog.vec.mat4d.tmpvec3d_[0];
    +    }
    +    fwdVec[0] = -mat[2];
    +    fwdVec[1] = -mat[6];
    +    fwdVec[2] = -mat[10];
    +    // Normalize forward vector.
    +    goog.vec.vec3d.normalize(fwdVec, fwdVec);
    +  }
    +
    +  if (worldUpVec) {
    +    // Get side vector from the definition of gluLookAt.
    +    var side = goog.vec.mat4d.tmpvec3d_[1];
    +    side[0] = mat[0];
    +    side[1] = mat[4];
    +    side[2] = mat[8];
    +    // Compute up vector as a up = side x forward.
    +    goog.vec.vec3d.cross(side, fwdVec, worldUpVec);
    +    // Normalize up vector.
    +    goog.vec.vec3d.normalize(worldUpVec, worldUpVec);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians,
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +  mat[3] = 0;
    +
    +  mat[4] = -c1 * s3 - c3 * c2 * s1;
    +  mat[5] = c1 * c2 * c3 - s1 * s3;
    +  mat[6] = c3 * s2;
    +  mat[7] = 0;
    +
    +  mat[8] = s2 * s1;
    +  mat[9] = -c1 * s2;
    +  mat[10] = c2;
    +  mat[11] = 0;
    +
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {goog.vec.vec3d.Type} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {!goog.vec.vec4d.Type} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4d.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[6] * mat[6]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[6] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[0] = Math.atan2(mat[8] * signTheta2, -mat[9] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    +
    +
    +/**
    + * Translates the given matrix by x,y,z.  Equvialent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeTranslate(goog.vec.mat4d.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.translate = function(mat, x, y, z) {
    +  mat[12] += mat[0] * x + mat[4] * y + mat[8] * z;
    +  mat[13] += mat[1] * x + mat[5] * y + mat[9] * z;
    +  mat[14] += mat[2] * x + mat[6] * y + mat[10] * z;
    +  mat[15] += mat[3] * x + mat[7] * y + mat[11] * z;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Scales the given matrix by x,y,z.  Equivalent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeScale(goog.vec.mat4d.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} x The x scale factor.
    + * @param {number} y The y scale factor.
    + * @param {number} z The z scale factor.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.scale = function(mat, x, y, z) {
    +  mat[0] = mat[0] * x;
    +  mat[1] = mat[1] * x;
    +  mat[2] = mat[2] * x;
    +  mat[3] = mat[3] * x;
    +  mat[4] = mat[4] * y;
    +  mat[5] = mat[5] * y;
    +  mat[6] = mat[6] * y;
    +  mat[7] = mat[7] * y;
    +  mat[8] = mat[8] * z;
    +  mat[9] = mat[9] * z;
    +  mat[10] = mat[10] * z;
    +  mat[11] = mat[11] * z;
    +  mat[12] = mat[12];
    +  mat[13] = mat[13];
    +  mat[14] = mat[14];
    +  mat[15] = mat[15];
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeRotate(goog.vec.mat4d.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  mat[0] = m00 * r00 + m01 * r10 + m02 * r20;
    +  mat[1] = m10 * r00 + m11 * r10 + m12 * r20;
    +  mat[2] = m20 * r00 + m21 * r10 + m22 * r20;
    +  mat[3] = m30 * r00 + m31 * r10 + m32 * r20;
    +  mat[4] = m00 * r01 + m01 * r11 + m02 * r21;
    +  mat[5] = m10 * r01 + m11 * r11 + m12 * r21;
    +  mat[6] = m20 * r01 + m21 * r11 + m22 * r21;
    +  mat[7] = m30 * r01 + m31 * r11 + m32 * r21;
    +  mat[8] = m00 * r02 + m01 * r12 + m02 * r22;
    +  mat[9] = m10 * r02 + m11 * r12 + m12 * r22;
    +  mat[10] = m20 * r02 + m21 * r12 + m22 * r22;
    +  mat[11] = m30 * r02 + m31 * r12 + m32 * r22;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeRotateX(goog.vec.mat4d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.rotateX = function(mat, angle) {
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[4] = m01 * c + m02 * s;
    +  mat[5] = m11 * c + m12 * s;
    +  mat[6] = m21 * c + m22 * s;
    +  mat[7] = m31 * c + m32 * s;
    +  mat[8] = m01 * -s + m02 * c;
    +  mat[9] = m11 * -s + m12 * c;
    +  mat[10] = m21 * -s + m22 * c;
    +  mat[11] = m31 * -s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeRotateY(goog.vec.mat4d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[3] = m30 * c + m32 * -s;
    +  mat[8] = m00 * s + m02 * c;
    +  mat[9] = m10 * s + m12 * c;
    +  mat[10] = m20 * s + m22 * c;
    +  mat[11] = m30 * s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.mat4d.multMat(
    + *     mat,
    + *     goog.vec.mat4d.makeRotateZ(goog.vec.mat4d.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4d.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4d.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m30 * c + m31 * s;
    +  mat[4] = m00 * -s + m01 * c;
    +  mat[5] = m10 * -s + m11 * c;
    +  mat[6] = m20 * -s + m21 * c;
    +  mat[7] = m30 * -s + m31 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the translation component of the transformation matrix.
    + *
    + * @param {goog.vec.mat4d.Type} mat The transformation matrix.
    + * @param {goog.vec.vec3d.Type} translation The vector for storing the
    + *     result.
    + * @return {!goog.vec.vec3d.Type} return translation so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4d.getTranslation = function(mat, translation) {
    +  translation[0] = mat[12];
    +  translation[1] = mat[13];
    +  translation[2] = mat[14];
    +  return translation;
    +};
    +
    +
    +/**
    + * @type {Array<goog.vec.vec3d.Type>}
    + * @private
    + */
    +goog.vec.mat4d.tmpvec3d_ = [
    +  goog.vec.vec3d.create(),
    +  goog.vec.vec3d.create()
    +];
    +
    +
    +/**
    + * @type {Array<goog.vec.vec4d.Type>}
    + * @private
    + */
    +goog.vec.mat4d.tmpvec4d_ = [
    +  goog.vec.vec4d.create(),
    +  goog.vec.vec4d.create(),
    +  goog.vec.vec4d.create()
    +];
    +
    +
    +/**
    + * @type {Array<goog.vec.mat4d.Type>}
    + * @private
    + */
    +goog.vec.mat4d.tmpmat4d_ = [
    +  goog.vec.mat4d.create()
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4d_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat4d_test.html
    new file mode 100644
    index 0000000000..7ec7e7489f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4d_test.html
    @@ -0,0 +1,738 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat4f_test.html by running:     //
    +//   swap_type.sh mat4d_test.html > mat4f_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.mat4d</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.mat4d');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randommat4d = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(),
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197,
    +      0.5310639142990112,
    +      0.8962187170982361,
    +      0.280601441860199,
    +      0.594650387763977,
    +      0.4134795069694519,
    +      0.06632178276777267,
    +      0.8837796449661255);
    +
    +  function testCreate() {
    +    var m = goog.vec.mat4d.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    goog.vec.mat4d.setFromArray(m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    goog.vec.mat4d.setFromValues(
    +        m0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setDiagonalValues(m0, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], m0);
    +
    +    goog.vec.mat4d.setDiagonal(m0, [4, 5, 6, 7]);
    +    assertElementsEquals(
    +        [4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7], m0);
    +  }
    +
    +  function testGetDiagonal() {
    +    var v0 = goog.vec.vec4d.create();
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setFromArray(
    +        m0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
    +
    +    goog.vec.mat4d.getDiagonal(m0, v0);
    +    assertElementsEquals([0, 5, 10, 15], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, 1);
    +    assertElementsEquals([4, 9, 14, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, 2);
    +    assertElementsEquals([8, 13, 0, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, 3);
    +    assertElementsEquals([12, 0, 0, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, 4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, -1);
    +    assertElementsEquals([1, 6, 11, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, -2);
    +    assertElementsEquals([2, 7, 0, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, -3);
    +    assertElementsEquals([3, 0, 0, 0], v0);
    +
    +    goog.vec.vec4d.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4d.getDiagonal(m0, v0, -4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setColumn(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.mat4d.setColumn(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.mat4d.setColumn(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.mat4d.setColumn(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.mat4d.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.mat4d.getColumn(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.mat4d.getColumn(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.mat4d.getColumn(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setColumns(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.mat4d.getColumns(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setRow(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.mat4d.setRow(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.mat4d.setRow(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.mat4d.setRow(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.mat4d.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.mat4d.getRow(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.mat4d.getRow(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.mat4d.getRow(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setRows(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.mat4d.getRows(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    goog.vec.mat4d.makeZero(m0);
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeIdentity(m0);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.mat4d.create();
    +    for (var r = 0; r < 4; r++) {
    +      for (var c = 0; c < 4; c++) {
    +        var value = c * 4 + r + 1;
    +        goog.vec.mat4d.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.mat4d.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.addMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m2);
    +
    +    goog.vec.mat4d.addMat(m0, m1, m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.mat4d.create();
    +
    +    goog.vec.mat4d.subMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [-8, -8, -8, -8, -8, -8, -8, -8, 8, 8, 8, 8, 8, 8, 8, 8], m2);
    +
    +    goog.vec.mat4d.subMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [8, 8, 8, 8, 8, 8, 8, 8, -8, -8, -8, -8, -8, -8, -8, -8], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.create();
    +
    +    goog.vec.mat4d.multScalar(m0, 2, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32], m1);
    +
    +    goog.vec.mat4d.multScalar(m0, 5, m0);
    +    assertElementsEquals(
    +        [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m2 = goog.vec.mat4d.create();
    +
    +    goog.vec.mat4d.multMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [90, 100, 110, 120, 202, 228, 254, 280,
    +         314, 356, 398, 440, 426, 484, 542, 600], m2);
    +
    +    goog.vec.mat4d.multScalar(m1, 2, m1);
    +    goog.vec.mat4d.multMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [180, 200, 220, 240, 404, 456, 508, 560,
    +         628, 712, 796, 880, 852, 968, 1084, 1200], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.transpose(m0, m1);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m1);
    +
    +    goog.vec.mat4d.transpose(m1, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +  }
    +
    +  function testDeterminant() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertEquals(0, goog.vec.mat4d.determinant(m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat4d.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertEquals(160, goog.vec.mat4d.determinant(m0));
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3], m0);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.mat4d.invert(m0, m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat4d.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertTrue(goog.vec.mat4d.invert(m0, m0));
    +    assertElementsRoughlyEqual(
    +        [-0.225, 0.025, 0.025, 0.275, 0.025, 0.025, 0.275, -0.225,
    +         0.025, 0.275, -0.225, 0.025, 0.275, -0.225, 0.025, 0.025], m0,
    +         goog.vec.EPSILON);
    +
    +    goog.vec.mat4d.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.mat4d.invert(m0, m0));
    +    var m1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4d.setFromMat4d(goog.vec.mat4d.create(), m0);
    +    assertTrue(goog.vec.mat4d.equals(m0, m1));
    +    assertTrue(goog.vec.mat4d.equals(m1, m0));
    +    for (var i = 0; i < 16; i++) {
    +      m1[i] = 18;
    +      assertFalse(goog.vec.mat4d.equals(m0, m1));
    +      assertFalse(goog.vec.mat4d.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat4d.multVec3(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([51, 58, 65], v1);
    +
    +    goog.vec.mat4d.multVec3(m0, v0, v0);
    +    assertElementsEquals([51, 58, 65], v0);
    +  }
    +
    +  function testMultVec3NoTranslate() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat4d.multVec3NoTranslate(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([38, 44, 50], v1);
    +
    +    goog.vec.mat4d.multVec3NoTranslate(m0, v0, v0);
    +    assertElementsEquals([38, 44, 50], v0);
    +  }
    +
    +  function testMultVec3Projective() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +    var invw = 1 / 72;
    +
    +    goog.vec.mat4d.multVec3Projective(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v1);
    +
    +    goog.vec.mat4d.multVec3Projective(m0, v0, v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v0);
    +  }
    +
    +  function testMultVec4() {
    +    var m0 = goog.vec.mat4d.setFromValues(goog.vec.mat4d.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3, 4];
    +    var v1 = [0, 0, 0, 0];
    +
    +    goog.vec.mat4d.multVec4(m0, v0, v1);
    +    assertElementsEquals([90, 100, 110, 120], v1);
    +    goog.vec.mat4d.multVec4(m0, v0, v0);
    +    assertElementsEquals([90, 100, 110, 120], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.mat4d.create();
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    a0 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a0);
    +
    +    var a1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.setDiagonalValues(a1, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], a1);
    +
    +    goog.vec.mat4d.setColumnValues(a1, 0, 2, 3, 4, 5);
    +    goog.vec.mat4d.setColumnValues(a1, 1, 6, 7, 8, 9);
    +    goog.vec.mat4d.setColumnValues(a1, 2, 10, 11, 12, 13);
    +    goog.vec.mat4d.setColumnValues(a1, 3, 14, 15, 16, 1);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1], a1);
    +
    +    goog.vec.mat4d.setRowValues(a1, 0, 1, 5, 9, 13);
    +    goog.vec.mat4d.setRowValues(a1, 1, 2, 6, 10, 14);
    +    goog.vec.mat4d.setRowValues(a1, 2, 3, 7, 11, 15);
    +    goog.vec.mat4d.setRowValues(a1, 3, 4, 8, 12, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeTranslate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.mat4d.multMat(m0, m1, m1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.create()
    +
    +    goog.vec.mat4d.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat4d.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.create()
    +
    +    goog.vec.mat4d.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat4d.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.create()
    +
    +    goog.vec.mat4d.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat4d.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +
    +  function testTranslate() {
    +    var m0 = goog.vec.mat4d.makeIdentity(goog.vec.mat4d.create());
    +    goog.vec.mat4d.translate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +
    +    goog.vec.mat4d.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +
    +    var m1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeTranslate(m1, 5, 6, 7);
    +    var m2 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.multMat(m0, m1, m2);
    +    goog.vec.mat4d.translate(m0, 5, 6, 7);
    +    assertElementsEquals(m2, m0);
    +  }
    +
    +  function testScale() {
    +    var m0 = goog.vec.mat4d.makeIdentity(goog.vec.mat4d.create());
    +    goog.vec.mat4d.scale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.mat4d.makeIdentity(goog.vec.mat4d.create());
    +    goog.vec.mat4d.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.mat4d.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), randommat4d)
    +
    +    goog.vec.mat4d.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat4d.multMat(m1, m0, m0);
    +    goog.vec.mat4d.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), randommat4d)
    +
    +    goog.vec.mat4d.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat4d.multMat(m1, m0, m0);
    +    goog.vec.mat4d.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.mat4d.create();
    +    var m1 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), randommat4d)
    +
    +    goog.vec.mat4d.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat4d.multMat(m1, m0, m0);
    +    goog.vec.mat4d.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testGetTranslation() {
    +    var mat = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), randommat4d);
    +    var translation = goog.vec.vec3d.create();
    +    goog.vec.mat4d.getTranslation(mat, translation);
    +    assertElementsRoughlyEqual(
    +        [0.59465038776, 0.413479506969, 0.0663217827677],
    +        translation, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeFrustum() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeFrustum(m0, -1, 2, -2, 1, .1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.06666666, 0, 0, 0,
    +         0, 0.06666666, 0, 0,
    +         0.33333333, -0.33333333, -1.2, -1,
    +         0, 0, -0.22, 0], m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakePerspective() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makePerspective(m0, 90 * Math.PI / 180, 2, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1.2, -1, 0, 0, -0.22, 0],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeOrtho() {
    +    var m0 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeOrtho(m0, -1, 2, -2, 1, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.6666666, 0, 0, 0,
    +         0, 0.6666666, 0, 0,
    +         0, 0, -2, 0,
    +         -0.333333, 0.3333333, -1.2, 1], m0, goog.vec.EPSILON);
    +
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.mat4d.create();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.mat4d.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat4d.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.mat4d.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4d.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.mat4d.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat4d.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.mat4d.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.mat4d.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4d.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), 
    +    [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
    +    var m1 = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), 
    +    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4d.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.mat4d.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testLookAt() {
    +    var viewMatrix = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeLookAt(
    +      viewMatrix, [0, 0, 0], [1, 0, 0], [0, 1, 0]);
    +    assertElementsRoughlyEqual(
    +      [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], viewMatrix,
    +      goog.vec.EPSILON);
    +  }
    +
    +  function testToLookAt() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [1, 0, 0];
    +    var upExp = [0, 1, 0];
    +
    +    var centerExp = [0, 0, 0];
    +    goog.vec.vec3d.add(eyeExp, fwdExp, centerExp);
    +
    +    var view = goog.vec.mat4d.create();
    +    goog.vec.mat4d.makeLookAt(view, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    goog.vec.mat4d.toLookAt(view, eyeRes, fwdRes, upRes);
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +  }
    +
    +  function testLookAtDecomposition() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var viewExp = goog.vec.mat4d.create();
    +    var viewRes = goog.vec.mat4d.create();
    +
    +    // Get a valid set of random vectors eye, forward, up by decomposing
    +    // a random matrix into a set of lookAt vectors.
    +    var tmp = goog.vec.mat4d.setFromArray(goog.vec.mat4d.create(), randommat4d);
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [0, 0, 0];
    +    var upExp = [0, 0, 0];
    +    var centerExp = [0, 0, 0];
    +    // Project the random matrix into a real modelview matrix.
    +    goog.vec.mat4d.toLookAt(tmp, eyeExp, fwdExp, upExp);
    +    goog.vec.vec3d.add(eyeExp, fwdExp, centerExp);
    +
    +    // Compute the expected modelview matrix from a set of valid random vectors.
    +    goog.vec.mat4d.makeLookAt(viewExp, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    var centerRes = [0, 0, 0];
    +    goog.vec.mat4d.toLookAt(viewExp, eyeRes, fwdRes, upRes);
    +    goog.vec.vec3d.add(eyeRes, fwdRes, centerRes);
    +
    +    goog.vec.mat4d.makeLookAt(viewRes, eyeRes, centerRes, upRes);
    +
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +    assertElementsRoughlyEqual(viewExp, viewRes, EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4f.js b/mapbox-gl/closure-library/closure/goog/vec/mat4f.js
    new file mode 100644
    index 0000000000..666438f589
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4f.js
    @@ -0,0 +1,1769 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat4d.js by running:            //
    +//   swap_type.sh mat4f.js > mat4d.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 4x4 float (32bit)
    + * matrices.  The matrices are stored in column-major order.
    + *
    + * The last parameter will typically be the output matrix and an
    + * object can be both an input and output parameter to all methods except
    + * where noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.mat4f');
    +goog.provide('goog.vec.mat4f.Type');
    +
    +goog.require('goog.vec');
    +goog.require('goog.vec.vec3f');
    +goog.require('goog.vec.vec4f');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.mat4f.Type;
    +
    +
    +/**
    + * Creates a mat4f with all elements initialized to zero.
    + *
    + * @return {!goog.vec.mat4f.Type} The new mat4f.
    + */
    +goog.vec.mat4f.create = function() {
    +  return new Float32Array(16);
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setFromValues = function(
    +    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v30;
    +  mat[4] = v01;
    +  mat[5] = v11;
    +  mat[6] = v21;
    +  mat[7] = v31;
    +  mat[8] = v02;
    +  mat[9] = v12;
    +  mat[10] = v22;
    +  mat[11] = v32;
    +  mat[12] = v03;
    +  mat[13] = v13;
    +  mat[14] = v23;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4f mat from mat4f src.
    + *
    + * @param {goog.vec.mat4f.Type} mat The destination matrix.
    + * @param {goog.vec.mat4f.Type} src The source matrix.
    + * @return {!goog.vec.mat4f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setFromMat4f = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4f mat from mat4d src (typed as a Float64Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.mat4f.Type} mat The destination matrix.
    + * @param {Float64Array} src The source matrix.
    + * @return {!goog.vec.mat4f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setFromMat4d = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Initializes mat4f mat from Array src.
    + *
    + * @param {goog.vec.mat4f.Type} mat The destination matrix.
    + * @param {Array<number>} src The source matrix.
    + * @return {!goog.vec.mat4f.Type} Return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setFromArray = function(mat, src) {
    +  mat[0] = src[0];
    +  mat[1] = src[1];
    +  mat[2] = src[2];
    +  mat[3] = src[3];
    +  mat[4] = src[4];
    +  mat[5] = src[5];
    +  mat[6] = src[6];
    +  mat[7] = src[7];
    +  mat[8] = src[8];
    +  mat[9] = src[9];
    +  mat[10] = src[10];
    +  mat[11] = src[11];
    +  mat[12] = src[12];
    +  mat[13] = src[13];
    +  mat[14] = src[14];
    +  mat[15] = src[15];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.mat4f.getElement = function(mat, row, column) {
    +  return mat[row + column * 4];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix containing the value to
    + *     retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setElement = function(mat, row, column, value) {
    +  mat[row + column * 4] = value;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @param {number} v33 The values for (3, 3).
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setDiagonalValues = function(mat, v00, v11, v22, v33) {
    +  mat[0] = v00;
    +  mat[5] = v11;
    +  mat[10] = v22;
    +  mat[15] = v33;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4f.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[5] = vec[1];
    +  mat[10] = vec[2];
    +  mat[15] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Gets the diagonal values of the matrix into the given vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix containing the values.
    + * @param {goog.vec.vec4f.Type} vec The vector to receive the values.
    + * @param {number=} opt_diagonal Which diagonal to get. A value of 0 selects the
    + *     main diagonal, a positive number selects a super diagonal and a negative
    + *     number selects a sub diagonal.
    + * @return {goog.vec.vec4f.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.getDiagonal = function(mat, vec, opt_diagonal) {
    +  if (!opt_diagonal) {
    +    // This is the most common case, so we avoid the for loop.
    +    vec[0] = mat[0];
    +    vec[1] = mat[5];
    +    vec[2] = mat[10];
    +    vec[3] = mat[15];
    +  } else {
    +    var offset = opt_diagonal > 0 ? 4 * opt_diagonal : -opt_diagonal;
    +    for (var i = 0; i < 4 - Math.abs(opt_diagonal); i++) {
    +      vec[i] = mat[offset + 5 * i];
    +    }
    +  }
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to recieve the values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @param {number} v3 The value for row 3.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setColumnValues = function(mat, column, v0, v1, v2, v3) {
    +  var i = column * 4;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  mat[i + 3] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.vec4f.Type} vec The vector of elements for the column.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  mat[i + 3] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.vec4f.Type} vec The vector of elements to
    + *     receive the column.
    + * @return {!goog.vec.vec4f.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.getColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  vec[3] = mat[i + 3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the given vectors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4f.Type} vec0 The values for column 0.
    + * @param {goog.vec.vec4f.Type} vec1 The values for column 1.
    + * @param {goog.vec.vec4f.Type} vec2 The values for column 2.
    + * @param {goog.vec.vec4f.Type} vec3 The values for column 3.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  mat[0] = vec0[0];
    +  mat[1] = vec0[1];
    +  mat[2] = vec0[2];
    +  mat[3] = vec0[3];
    +  mat[4] = vec1[0];
    +  mat[5] = vec1[1];
    +  mat[6] = vec1[2];
    +  mat[7] = vec1[3];
    +  mat[8] = vec2[0];
    +  mat[9] = vec2[1];
    +  mat[10] = vec2[2];
    +  mat[11] = vec2[3];
    +  mat[12] = vec3[0];
    +  mat[13] = vec3[1];
    +  mat[14] = vec3[2];
    +  mat[15] = vec3[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vectors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the columns.
    + * @param {goog.vec.vec4f.Type} vec0 The vector to receive column 0.
    + * @param {goog.vec.vec4f.Type} vec1 The vector to receive column 1.
    + * @param {goog.vec.vec4f.Type} vec2 The vector to receive column 2.
    + * @param {goog.vec.vec4f.Type} vec3 The vector to receive column 3.
    + */
    +goog.vec.mat4f.getColumns = function(mat, vec0, vec1, vec2, vec3) {
    +  vec0[0] = mat[0];
    +  vec0[1] = mat[1];
    +  vec0[2] = mat[2];
    +  vec0[3] = mat[3];
    +  vec1[0] = mat[4];
    +  vec1[1] = mat[5];
    +  vec1[2] = mat[6];
    +  vec1[3] = mat[7];
    +  vec2[0] = mat[8];
    +  vec2[1] = mat[9];
    +  vec2[2] = mat[10];
    +  vec2[3] = mat[11];
    +  vec3[0] = mat[12];
    +  vec3[1] = mat[13];
    +  vec3[2] = mat[14];
    +  vec3[3] = mat[15];
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @param {number} v3 The value for column 3.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setRowValues = function(mat, row, v0, v1, v2, v3) {
    +  mat[row] = v0;
    +  mat[row + 4] = v1;
    +  mat[row + 8] = v2;
    +  mat[row + 12] = v3;
    +  return mat;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.vec4f.Type} vec The vector containing the values.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 4] = vec[1];
    +  mat[row + 8] = vec[2];
    +  mat[row + 12] = vec[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.vec4f.Type} vec The vector to receive the row.
    + * @return {!goog.vec.vec4f.Type} return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 4];
    +  vec[2] = mat[row + 8];
    +  vec[3] = mat[row + 12];
    +  return vec;
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to receive the values.
    + * @param {goog.vec.vec4f.Type} vec0 The values for row 0.
    + * @param {goog.vec.vec4f.Type} vec1 The values for row 1.
    + * @param {goog.vec.vec4f.Type} vec2 The values for row 2.
    + * @param {goog.vec.vec4f.Type} vec3 The values for row 3.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.setRows = function(mat, vec0, vec1, vec2, vec3) {
    +  mat[0] = vec0[0];
    +  mat[1] = vec1[0];
    +  mat[2] = vec2[0];
    +  mat[3] = vec3[0];
    +  mat[4] = vec0[1];
    +  mat[5] = vec1[1];
    +  mat[6] = vec2[1];
    +  mat[7] = vec3[1];
    +  mat[8] = vec0[2];
    +  mat[9] = vec1[2];
    +  mat[10] = vec2[2];
    +  mat[11] = vec3[2];
    +  mat[12] = vec0[3];
    +  mat[13] = vec1[3];
    +  mat[14] = vec2[3];
    +  mat[15] = vec3[3];
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to supply the values.
    + * @param {goog.vec.vec4f.Type} vec0 The vector to receive row 0.
    + * @param {goog.vec.vec4f.Type} vec1 The vector to receive row 1.
    + * @param {goog.vec.vec4f.Type} vec2 The vector to receive row 2.
    + * @param {goog.vec.vec4f.Type} vec3 The vector to receive row 3.
    + */
    +goog.vec.mat4f.getRows = function(mat, vec0, vec1, vec2, vec3) {
    +  vec0[0] = mat[0];
    +  vec1[0] = mat[1];
    +  vec2[0] = mat[2];
    +  vec3[0] = mat[3];
    +  vec0[1] = mat[4];
    +  vec1[1] = mat[5];
    +  vec2[1] = mat[6];
    +  vec3[1] = mat[7];
    +  vec0[2] = mat[8];
    +  vec1[2] = mat[9];
    +  vec2[2] = mat[10];
    +  vec3[2] = mat[11];
    +  vec0[3] = mat[12];
    +  vec1[3] = mat[13];
    +  vec2[3] = mat[14];
    +  vec3[3] = mat[15];
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the zero matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @return {!goog.vec.mat4f.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat4f.makeZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 0;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 0;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix the identity matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @return {!goog.vec.mat4f.Type} return mat so operations can be chained.
    + */
    +goog.vec.mat4f.makeIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrix mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.mat4f.Type} mat0 The first addend.
    + * @param {goog.vec.mat4f.Type} mat1 The second addend.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.addMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  resultMat[9] = mat0[9] + mat1[9];
    +  resultMat[10] = mat0[10] + mat1[10];
    +  resultMat[11] = mat0[11] + mat1[11];
    +  resultMat[12] = mat0[12] + mat1[12];
    +  resultMat[13] = mat0[13] + mat1[13];
    +  resultMat[14] = mat0[14] + mat1[14];
    +  resultMat[15] = mat0[15] + mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrix mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4f.Type} mat0 The minuend.
    + * @param {goog.vec.mat4f.Type} mat1 The subtrahend.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.subMat = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  resultMat[9] = mat0[9] - mat1[9];
    +  resultMat[10] = mat0[10] - mat1[10];
    +  resultMat[11] = mat0[11] - mat1[11];
    +  resultMat[12] = mat0[12] - mat1[12];
    +  resultMat[13] = mat0[13] - mat1[13];
    +  resultMat[14] = mat0[14] - mat1[14];
    +  resultMat[15] = mat0[15] - mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies matrix mat with the given scalar, storing the result
    + * into resultMat.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} scalar The scalar value to multiply to each element of mat.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat4f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multScalar = function(mat, scalar, resultMat) {
    +  resultMat[0] = mat[0] * scalar;
    +  resultMat[1] = mat[1] * scalar;
    +  resultMat[2] = mat[2] * scalar;
    +  resultMat[3] = mat[3] * scalar;
    +  resultMat[4] = mat[4] * scalar;
    +  resultMat[5] = mat[5] * scalar;
    +  resultMat[6] = mat[6] * scalar;
    +  resultMat[7] = mat[7] * scalar;
    +  resultMat[8] = mat[8] * scalar;
    +  resultMat[9] = mat[9] * scalar;
    +  resultMat[10] = mat[10] * scalar;
    +  resultMat[11] = mat[11] * scalar;
    +  resultMat[12] = mat[12] * scalar;
    +  resultMat[13] = mat[13] * scalar;
    +  resultMat[14] = mat[14] * scalar;
    +  resultMat[15] = mat[15] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4f.Type} mat0 The first (left hand) matrix.
    + * @param {goog.vec.mat4f.Type} mat1 The second (right hand) matrix.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {!goog.vec.mat4f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
    +  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
    +  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
    +  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
    +  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
    +  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
    +  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
    +  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
    +
    +  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
    +  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
    +  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
    +  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
    +
    +  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
    +  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
    +  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
    +  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
    +
    +  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
    +  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
    +  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
    +  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to transpose.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {!goog.vec.mat4f.Type} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
    +    var a21 = mat[6], a31 = mat[7];
    +    var a32 = mat[11];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +    resultMat[4] = a10;
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +    resultMat[8] = a20;
    +    resultMat[9] = a21;
    +    resultMat[11] = mat[14];
    +    resultMat[12] = a30;
    +    resultMat[13] = a31;
    +    resultMat[14] = a32;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +
    +    resultMat[4] = mat[1];
    +    resultMat[5] = mat[5];
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +
    +    resultMat[8] = mat[2];
    +    resultMat[9] = mat[6];
    +    resultMat[10] = mat[10];
    +    resultMat[11] = mat[14];
    +
    +    resultMat[12] = mat[3];
    +    resultMat[13] = mat[7];
    +    resultMat[14] = mat[11];
    +    resultMat[15] = mat[15];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the determinant of the matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to compute the matrix for.
    + * @return {number} The determinant of the matrix.
    + */
    +goog.vec.mat4f.determinant = function(mat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix to invert.
    + * @param {goog.vec.mat4f.Type} resultMat The matrix to receive
    + *     the result (may be mat).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.mat4f.invert = function(mat, resultMat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1.0 / det;
    +  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
    +  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
    +  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
    +  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
    +  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
    +  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
    +  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
    +  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
    +  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
    +  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
    +  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
    +  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
    +  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
    +  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
    +  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
    +  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.mat4f.Type} mat0 The first matrix.
    + * @param {goog.vec.mat4f.Type} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.mat4f.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] &&
    +      mat0[1] == mat1[1] &&
    +      mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] &&
    +      mat0[4] == mat1[4] &&
    +      mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] &&
    +      mat0[7] == mat1[7] &&
    +      mat0[8] == mat1[8] &&
    +      mat0[9] == mat1[9] &&
    +      mat0[10] == mat1[10] &&
    +      mat0[11] == mat1[11] &&
    +      mat0[12] == mat1[12] &&
    +      mat0[13] == mat1[13] &&
    +      mat0[14] == mat1[14] &&
    +      mat0[15] == mat1[15];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3f.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3f.Type} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3f.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x3 matrix omitting the projective component and translation
    + * components.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3f.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3f.Type} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec3f.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multVec3NoTranslate = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
    + * vector to a 3 element vector.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec3f.Type} vec The 3 element vector to transform.
    + * @param {goog.vec.vec3f.Type} resultVec The 3 element vector
    + *     to receive the results (may be vec).
    + * @return {!goog.vec.vec3f.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multVec3Projective = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
    +  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
    +  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
    +  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix supplying the transformation.
    + * @param {goog.vec.vec4f.Type} vec The vector to transform.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {!goog.vec.vec4f.Type} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.multVec4 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
    +  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a translation matrix with x, y and z
    + * translation factors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeTranslate = function(mat, x, y, z) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = x;
    +  mat[13] = y;
    +  mat[14] = z;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix as a scale matrix with x, y and z scale factors.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeScale = function(mat, x, y, z) {
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = z;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeRotate = function(mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  mat[0] = ax * ax * d + c;
    +  mat[1] = ax * ay * d + az * s;
    +  mat[2] = ax * az * d - ay * s;
    +  mat[3] = 0;
    +  mat[4] = ax * ay * d - az * s;
    +  mat[5] = ay * ay * d + c;
    +  mat[6] = ay * az * d + ax * s;
    +  mat[7] = 0;
    +  mat[8] = ax * az * d + ay * s;
    +  mat[9] = ay * az * d - ax * s;
    +  mat[10] = az * az * d + c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the X axis.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeRotateX = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = c;
    +  mat[6] = s;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = -s;
    +  mat[10] = c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Y axis.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeRotateY = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = 0;
    +  mat[2] = -s;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = s;
    +  mat[9] = 0;
    +  mat[10] = c;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix with the given rotation
    + * angle about the Z axis.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeRotateZ = function(mat, angle) {
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = c;
    +  mat[1] = s;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = -s;
    +  mat[5] = c;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a perspective projection matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeFrustum = function(
    +    mat, left, right, bottom, top, near, far) {
    +  var x = (2 * near) / (right - left);
    +  var y = (2 * near) / (top - bottom);
    +  var a = (right + left) / (right - left);
    +  var b = (top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +  var d = -(2 * far * near) / (far - near);
    +
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = a;
    +  mat[9] = b;
    +  mat[10] = c;
    +  mat[11] = -1;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = d;
    +  mat[15] = 0;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makse the given 4x4 matrix  perspective projection matrix given a
    + * field of view and aspect ratio.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} fovy The field of view along the y (vertical) axis in
    + *     radians.
    + * @param {number} aspect The x (width) to y (height) aspect ratio.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makePerspective = function(mat, fovy, aspect, near, far) {
    +  var angle = fovy / 2;
    +  var dz = far - near;
    +  var sinAngle = Math.sin(angle);
    +  if (dz == 0 || sinAngle == 0 || aspect == 0) {
    +    return mat;
    +  }
    +
    +  var cot = Math.cos(angle) / sinAngle;
    +
    +  mat[0] = cot / aspect;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = cot;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = -(far + near) / dz;
    +  mat[11] = -1;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = -(2 * near * far) / dz;
    +  mat[15] = 0;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix an orthographic projection matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeOrtho = function(mat, left, right, bottom, top, near, far) {
    +  var x = 2 / (right - left);
    +  var y = 2 / (top - bottom);
    +  var z = -2 / (far - near);
    +  var a = -(right + left) / (right - left);
    +  var b = -(top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +
    +  mat[0] = x;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = y;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = z;
    +  mat[11] = 0;
    +  mat[12] = a;
    +  mat[13] = b;
    +  mat[14] = c;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a modelview matrix of a camera so that
    + * the camera is 'looking at' the given center point.
    + *
    + * Note that unlike most other goog.vec functions where we inline
    + * everything, this function does not inline various goog.vec
    + * functions.  This makes the code more readable, but somewhat
    + * less efficient.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {goog.vec.vec3f.Type} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.vec3f.Type} centerPt The point to aim the camera at.
    + * @param {goog.vec.vec3f.Type} worldUpVec The vector that identifies
    + *     the up direction for the camera.
    + * @return {goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeLookAt = function(mat, eyePt, centerPt, worldUpVec) {
    +  // Compute the direction vector from the eye point to the center point and
    +  // normalize.
    +  var fwdVec = goog.vec.mat4f.tmpvec4f_[0];
    +  goog.vec.vec3f.subtract(centerPt, eyePt, fwdVec);
    +  goog.vec.vec3f.normalize(fwdVec, fwdVec);
    +  fwdVec[3] = 0;
    +
    +  // Compute the side vector from the forward vector and the input up vector.
    +  var sideVec = goog.vec.mat4f.tmpvec4f_[1];
    +  goog.vec.vec3f.cross(fwdVec, worldUpVec, sideVec);
    +  goog.vec.vec3f.normalize(sideVec, sideVec);
    +  sideVec[3] = 0;
    +
    +  // Now the up vector to form the orthonormal basis.
    +  var upVec = goog.vec.mat4f.tmpvec4f_[2];
    +  goog.vec.vec3f.cross(sideVec, fwdVec, upVec);
    +  goog.vec.vec3f.normalize(upVec, upVec);
    +  upVec[3] = 0;
    +
    +  // Update the view matrix with the new orthonormal basis and position the
    +  // camera at the given eye point.
    +  goog.vec.vec3f.negate(fwdVec, fwdVec);
    +  goog.vec.mat4f.setRow(mat, 0, sideVec);
    +  goog.vec.mat4f.setRow(mat, 1, upVec);
    +  goog.vec.mat4f.setRow(mat, 2, fwdVec);
    +  goog.vec.mat4f.setRowValues(mat, 3, 0, 0, 0, 1);
    +  goog.vec.mat4f.translate(
    +      mat, -eyePt[0], -eyePt[1], -eyePt[2]);
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
    + * The matrix represents the modelview matrix of a camera. It is the inverse
    + * of lookAt except for the output of the fwdVec instead of centerPt.
    + * The centerPt itself cannot be recovered from a modelview matrix.
    + *
    + * Note that unlike most other goog.vec functions where we inline
    + * everything, this function does not inline various goog.vec
    + * functions.  This makes the code more readable, but somewhat
    + * less efficient.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {goog.vec.vec3f.Type} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.vec3f.Type} fwdVec The vector describing where
    + *     the camera points to.
    + * @param {goog.vec.vec3f.Type} worldUpVec The vector that
    + *     identifies the up direction for the camera.
    + * @return {boolean} True if the method succeeds, false otherwise.
    + *     The method can only fail if the inverse of viewMatrix is not defined.
    + */
    +goog.vec.mat4f.toLookAt = function(mat, eyePt, fwdVec, worldUpVec) {
    +  // Get eye of the camera.
    +  var matInverse = goog.vec.mat4f.tmpmat4f_[0];
    +  if (!goog.vec.mat4f.invert(mat, matInverse)) {
    +    // The input matrix does not have a valid inverse.
    +    return false;
    +  }
    +
    +  if (eyePt) {
    +    eyePt[0] = matInverse[12];
    +    eyePt[1] = matInverse[13];
    +    eyePt[2] = matInverse[14];
    +  }
    +
    +  // Get forward vector from the definition of lookAt.
    +  if (fwdVec || worldUpVec) {
    +    if (!fwdVec) {
    +      fwdVec = goog.vec.mat4f.tmpvec3f_[0];
    +    }
    +    fwdVec[0] = -mat[2];
    +    fwdVec[1] = -mat[6];
    +    fwdVec[2] = -mat[10];
    +    // Normalize forward vector.
    +    goog.vec.vec3f.normalize(fwdVec, fwdVec);
    +  }
    +
    +  if (worldUpVec) {
    +    // Get side vector from the definition of gluLookAt.
    +    var side = goog.vec.mat4f.tmpvec3f_[1];
    +    side[0] = mat[0];
    +    side[1] = mat[4];
    +    side[2] = mat[8];
    +    // Compute up vector as a up = side x forward.
    +    goog.vec.vec3f.cross(side, fwdVec, worldUpVec);
    +    // Normalize up vector.
    +    goog.vec.vec3f.normalize(worldUpVec, worldUpVec);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Makes the given 4x4 matrix a rotation matrix given Euler angles using
    + * the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians,
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  mat[0] = c1 * c3 - c2 * s1 * s3;
    +  mat[1] = c2 * c1 * s3 + c3 * s1;
    +  mat[2] = s3 * s2;
    +  mat[3] = 0;
    +
    +  mat[4] = -c1 * s3 - c3 * c2 * s1;
    +  mat[5] = c1 * c2 * c3 - s1 * s3;
    +  mat[6] = c3 * s2;
    +  mat[7] = 0;
    +
    +  mat[8] = s2 * s1;
    +  mat[9] = -c1 * s2;
    +  mat[10] = c2;
    +  mat[11] = 0;
    +
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
    + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
    + * rotation_x(theta) means rotation around the X axis of theta radians.
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {goog.vec.vec3f.Type} euler The ZXZ Euler angles in
    + *     radians as [theta1, theta2, theta3].
    + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
    + *     of the default [0, pi].
    + * @return {!goog.vec.vec4f.Type} return euler so that operations can be
    + *     chained together.
    + */
    +goog.vec.mat4f.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
    +  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
    +  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[6] * mat[6]);
    +
    +  // By default we explicitely constrain theta2 to be in [0, pi],
    +  // so sinTheta2 is always positive. We can change the behavior and specify
    +  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
    +  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
    +
    +  if (sinTheta2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(mat[2] * signTheta2, mat[6] * signTheta2);
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[0] = Math.atan2(mat[8] * signTheta2, -mat[9] * signTheta2);
    +  } else {
    +    // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here.
    +    // We assume theta1 = 0 as some applications do not allow the camera to roll
    +    // (i.e. have theta1 != 0).
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
    +    euler[2] = Math.atan2(mat[1], mat[0]);
    +  }
    +
    +  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
    +  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
    +  // signTheta2.
    +  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
    +      signTheta2;
    +
    +  return euler;
    +};
    +
    +
    +/**
    + * Translates the given matrix by x,y,z.  Equvialent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeTranslate(goog.vec.mat4f.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.translate = function(mat, x, y, z) {
    +  mat[12] += mat[0] * x + mat[4] * y + mat[8] * z;
    +  mat[13] += mat[1] * x + mat[5] * y + mat[9] * z;
    +  mat[14] += mat[2] * x + mat[6] * y + mat[10] * z;
    +  mat[15] += mat[3] * x + mat[7] * y + mat[11] * z;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Scales the given matrix by x,y,z.  Equivalent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeScale(goog.vec.mat4f.create(), x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} x The x scale factor.
    + * @param {number} y The y scale factor.
    + * @param {number} z The z scale factor.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.scale = function(mat, x, y, z) {
    +  mat[0] = mat[0] * x;
    +  mat[1] = mat[1] * x;
    +  mat[2] = mat[2] * x;
    +  mat[3] = mat[3] * x;
    +  mat[4] = mat[4] * y;
    +  mat[5] = mat[5] * y;
    +  mat[6] = mat[6] * y;
    +  mat[7] = mat[7] * y;
    +  mat[8] = mat[8] * z;
    +  mat[9] = mat[9] * z;
    +  mat[10] = mat[10] * z;
    +  mat[11] = mat[11] * z;
    +  mat[12] = mat[12];
    +  mat[13] = mat[13];
    +  mat[14] = mat[14];
    +  mat[15] = mat[15];
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x,y,z axis.  Equivalent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeRotate(goog.vec.mat4f.create(), angle, x, y, z),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.rotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  mat[0] = m00 * r00 + m01 * r10 + m02 * r20;
    +  mat[1] = m10 * r00 + m11 * r10 + m12 * r20;
    +  mat[2] = m20 * r00 + m21 * r10 + m22 * r20;
    +  mat[3] = m30 * r00 + m31 * r10 + m32 * r20;
    +  mat[4] = m00 * r01 + m01 * r11 + m02 * r21;
    +  mat[5] = m10 * r01 + m11 * r11 + m12 * r21;
    +  mat[6] = m20 * r01 + m21 * r11 + m22 * r21;
    +  mat[7] = m30 * r01 + m31 * r11 + m32 * r21;
    +  mat[8] = m00 * r02 + m01 * r12 + m02 * r22;
    +  mat[9] = m10 * r02 + m11 * r12 + m12 * r22;
    +  mat[10] = m20 * r02 + m21 * r12 + m22 * r22;
    +  mat[11] = m30 * r02 + m31 * r12 + m32 * r22;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the x axis.  Equivalent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeRotateX(goog.vec.mat4f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.rotateX = function(mat, angle) {
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[4] = m01 * c + m02 * s;
    +  mat[5] = m11 * c + m12 * s;
    +  mat[6] = m21 * c + m22 * s;
    +  mat[7] = m31 * c + m32 * s;
    +  mat[8] = m01 * -s + m02 * c;
    +  mat[9] = m11 * -s + m12 * c;
    +  mat[10] = m21 * -s + m22 * c;
    +  mat[11] = m31 * -s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the y axis.  Equivalent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeRotateY(goog.vec.mat4f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.rotateY = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m02 * -s;
    +  mat[1] = m10 * c + m12 * -s;
    +  mat[2] = m20 * c + m22 * -s;
    +  mat[3] = m30 * c + m32 * -s;
    +  mat[8] = m00 * s + m02 * c;
    +  mat[9] = m10 * s + m12 * c;
    +  mat[10] = m20 * s + m22 * c;
    +  mat[11] = m30 * s + m32 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Rotate the given matrix by angle about the z axis.  Equivalent to:
    + * goog.vec.mat4f.multMat(
    + *     mat,
    + *     goog.vec.mat4f.makeRotateZ(goog.vec.mat4f.create(), angle),
    + *     mat);
    + *
    + * @param {goog.vec.mat4f.Type} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @return {!goog.vec.mat4f.Type} return mat so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.rotateZ = function(mat, angle) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +
    +  var c = Math.cos(angle);
    +  var s = Math.sin(angle);
    +
    +  mat[0] = m00 * c + m01 * s;
    +  mat[1] = m10 * c + m11 * s;
    +  mat[2] = m20 * c + m21 * s;
    +  mat[3] = m30 * c + m31 * s;
    +  mat[4] = m00 * -s + m01 * c;
    +  mat[5] = m10 * -s + m11 * c;
    +  mat[6] = m20 * -s + m21 * c;
    +  mat[7] = m30 * -s + m31 * c;
    +
    +  return mat;
    +};
    +
    +
    +/**
    + * Retrieves the translation component of the transformation matrix.
    + *
    + * @param {goog.vec.mat4f.Type} mat The transformation matrix.
    + * @param {goog.vec.vec3f.Type} translation The vector for storing the
    + *     result.
    + * @return {!goog.vec.vec3f.Type} return translation so that operations can be
    + *     chained.
    + */
    +goog.vec.mat4f.getTranslation = function(mat, translation) {
    +  translation[0] = mat[12];
    +  translation[1] = mat[13];
    +  translation[2] = mat[14];
    +  return translation;
    +};
    +
    +
    +/**
    + * @type {Array<goog.vec.vec3f.Type>}
    + * @private
    + */
    +goog.vec.mat4f.tmpvec3f_ = [
    +  goog.vec.vec3f.create(),
    +  goog.vec.vec3f.create()
    +];
    +
    +
    +/**
    + * @type {Array<goog.vec.vec4f.Type>}
    + * @private
    + */
    +goog.vec.mat4f.tmpvec4f_ = [
    +  goog.vec.vec4f.create(),
    +  goog.vec.vec4f.create(),
    +  goog.vec.vec4f.create()
    +];
    +
    +
    +/**
    + * @type {Array<goog.vec.mat4f.Type>}
    + * @private
    + */
    +goog.vec.mat4f.tmpmat4f_ = [
    +  goog.vec.mat4f.create()
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/mat4f_test.html b/mapbox-gl/closure-library/closure/goog/vec/mat4f_test.html
    new file mode 100644
    index 0000000000..67c8b68b98
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/mat4f_test.html
    @@ -0,0 +1,738 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to mat4d_test.html by running:     //
    +//   swap_type.sh mat4f_test.html > mat4d_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.mat4f</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.mat4f');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var randommat4f = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(),
    +      0.8025078773498535,
    +      0.7559120655059814,
    +      0.15274643898010254,
    +      0.19196106493473053,
    +      0.0890120416879654,
    +      0.15422114729881287,
    +      0.09754583984613419,
    +      0.44862601161003113,
    +      0.9196512699127197,
    +      0.5310639142990112,
    +      0.8962187170982361,
    +      0.280601441860199,
    +      0.594650387763977,
    +      0.4134795069694519,
    +      0.06632178276777267,
    +      0.8837796449661255);
    +
    +  function testCreate() {
    +    var m = goog.vec.mat4f.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    goog.vec.mat4f.setFromArray(m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    goog.vec.mat4f.setFromValues(
    +        m0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setDiagonalValues(m0, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], m0);
    +
    +    goog.vec.mat4f.setDiagonal(m0, [4, 5, 6, 7]);
    +    assertElementsEquals(
    +        [4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7], m0);
    +  }
    +
    +  function testGetDiagonal() {
    +    var v0 = goog.vec.vec4f.create();
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setFromArray(
    +        m0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
    +
    +    goog.vec.mat4f.getDiagonal(m0, v0);
    +    assertElementsEquals([0, 5, 10, 15], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, 1);
    +    assertElementsEquals([4, 9, 14, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, 2);
    +    assertElementsEquals([8, 13, 0, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, 3);
    +    assertElementsEquals([12, 0, 0, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, 4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, -1);
    +    assertElementsEquals([1, 6, 11, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, -2);
    +    assertElementsEquals([2, 7, 0, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, -3);
    +    assertElementsEquals([3, 0, 0, 0], v0);
    +
    +    goog.vec.vec4f.setFromArray(v0, [0, 0, 0, 0]);
    +    goog.vec.mat4f.getDiagonal(m0, v0, -4);
    +    assertElementsEquals([0, 0, 0, 0], v0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setColumn(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.mat4f.setColumn(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.mat4f.setColumn(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.mat4f.setColumn(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.mat4f.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.mat4f.getColumn(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.mat4f.getColumn(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.mat4f.getColumn(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setColumns(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.mat4f.getColumns(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setRow(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.mat4f.setRow(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.mat4f.setRow(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.mat4f.setRow(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.mat4f.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.mat4f.getRow(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.mat4f.getRow(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.mat4f.getRow(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setRows(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.mat4f.getRows(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testMakeZero() {
    +    var m0 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    goog.vec.mat4f.makeZero(m0);
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testMakeIdentity() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeIdentity(m0);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.mat4f.create();
    +    for (var r = 0; r < 4; r++) {
    +      for (var c = 0; c < 4; c++) {
    +        var value = c * 4 + r + 1;
    +        goog.vec.mat4f.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.mat4f.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +  }
    +
    +  function testAddMat() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.addMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m2);
    +
    +    goog.vec.mat4f.addMat(m0, m1, m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m0);
    +  }
    +
    +  function testSubMat() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.mat4f.create();
    +
    +    goog.vec.mat4f.subMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [-8, -8, -8, -8, -8, -8, -8, -8, 8, 8, 8, 8, 8, 8, 8, 8], m2);
    +
    +    goog.vec.mat4f.subMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [8, 8, 8, 8, 8, 8, 8, 8, -8, -8, -8, -8, -8, -8, -8, -8], m1);
    +  }
    +
    +  function testMultScalar() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.create();
    +
    +    goog.vec.mat4f.multScalar(m0, 2, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32], m1);
    +
    +    goog.vec.mat4f.multScalar(m0, 5, m0);
    +    assertElementsEquals(
    +        [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m2 = goog.vec.mat4f.create();
    +
    +    goog.vec.mat4f.multMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [90, 100, 110, 120, 202, 228, 254, 280,
    +         314, 356, 398, 440, 426, 484, 542, 600], m2);
    +
    +    goog.vec.mat4f.multScalar(m1, 2, m1);
    +    goog.vec.mat4f.multMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [180, 200, 220, 240, 404, 456, 508, 560,
    +         628, 712, 796, 880, 852, 968, 1084, 1200], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.transpose(m0, m1);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m1);
    +
    +    goog.vec.mat4f.transpose(m1, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +  }
    +
    +  function testDeterminant() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertEquals(0, goog.vec.mat4f.determinant(m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat4f.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertEquals(160, goog.vec.mat4f.determinant(m0));
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3], m0);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.mat4f.invert(m0, m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.mat4f.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertTrue(goog.vec.mat4f.invert(m0, m0));
    +    assertElementsRoughlyEqual(
    +        [-0.225, 0.025, 0.025, 0.275, 0.025, 0.025, 0.275, -0.225,
    +         0.025, 0.275, -0.225, 0.025, 0.275, -0.225, 0.025, 0.025], m0,
    +         goog.vec.EPSILON);
    +
    +    goog.vec.mat4f.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.mat4f.invert(m0, m0));
    +    var m1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.mat4f.setFromMat4f(goog.vec.mat4f.create(), m0);
    +    assertTrue(goog.vec.mat4f.equals(m0, m1));
    +    assertTrue(goog.vec.mat4f.equals(m1, m0));
    +    for (var i = 0; i < 16; i++) {
    +      m1[i] = 18;
    +      assertFalse(goog.vec.mat4f.equals(m0, m1));
    +      assertFalse(goog.vec.mat4f.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat4f.multVec3(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([51, 58, 65], v1);
    +
    +    goog.vec.mat4f.multVec3(m0, v0, v0);
    +    assertElementsEquals([51, 58, 65], v0);
    +  }
    +
    +  function testMultVec3NoTranslate() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.mat4f.multVec3NoTranslate(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([38, 44, 50], v1);
    +
    +    goog.vec.mat4f.multVec3NoTranslate(m0, v0, v0);
    +    assertElementsEquals([38, 44, 50], v0);
    +  }
    +
    +  function testMultVec3Projective() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +    var invw = 1 / 72;
    +
    +    goog.vec.mat4f.multVec3Projective(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v1);
    +
    +    goog.vec.mat4f.multVec3Projective(m0, v0, v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v0);
    +  }
    +
    +  function testMultVec4() {
    +    var m0 = goog.vec.mat4f.setFromValues(goog.vec.mat4f.create(), 
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3, 4];
    +    var v1 = [0, 0, 0, 0];
    +
    +    goog.vec.mat4f.multVec4(m0, v0, v1);
    +    assertElementsEquals([90, 100, 110, 120], v1);
    +    goog.vec.mat4f.multVec4(m0, v0, v0);
    +    assertElementsEquals([90, 100, 110, 120], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.mat4f.create();
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    a0 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), 
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a0);
    +
    +    var a1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.setDiagonalValues(a1, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], a1);
    +
    +    goog.vec.mat4f.setColumnValues(a1, 0, 2, 3, 4, 5);
    +    goog.vec.mat4f.setColumnValues(a1, 1, 6, 7, 8, 9);
    +    goog.vec.mat4f.setColumnValues(a1, 2, 10, 11, 12, 13);
    +    goog.vec.mat4f.setColumnValues(a1, 3, 14, 15, 16, 1);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1], a1);
    +
    +    goog.vec.mat4f.setRowValues(a1, 0, 1, 5, 9, 13);
    +    goog.vec.mat4f.setRowValues(a1, 1, 2, 6, 10, 14);
    +    goog.vec.mat4f.setRowValues(a1, 2, 3, 7, 11, 15);
    +    goog.vec.mat4f.setRowValues(a1, 3, 4, 8, 12, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeTranslate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeRotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.mat4f.multMat(m0, m1, m1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateX() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.create()
    +
    +    goog.vec.mat4f.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat4f.makeRotate(m1, Math.PI / 7, 1, 0, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateY() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.create()
    +
    +    goog.vec.mat4f.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat4f.makeRotate(m1, Math.PI / 7, 0, 1, 0);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeRotateZ() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.create()
    +
    +    goog.vec.mat4f.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat4f.makeRotate(m1, Math.PI / 7, 0, 0, 1);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +
    +  function testTranslate() {
    +    var m0 = goog.vec.mat4f.makeIdentity(goog.vec.mat4f.create());
    +    goog.vec.mat4f.translate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +
    +    goog.vec.mat4f.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +
    +    var m1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeTranslate(m1, 5, 6, 7);
    +    var m2 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.multMat(m0, m1, m2);
    +    goog.vec.mat4f.translate(m0, 5, 6, 7);
    +    assertElementsEquals(m2, m0);
    +  }
    +
    +  function testScale() {
    +    var m0 = goog.vec.mat4f.makeIdentity(goog.vec.mat4f.create());
    +    goog.vec.mat4f.scale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testRotate() {
    +    var m0 = goog.vec.mat4f.makeIdentity(goog.vec.mat4f.create());
    +    goog.vec.mat4f.rotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.mat4f.rotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateX() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), randommat4f)
    +
    +    goog.vec.mat4f.makeRotateX(m0, Math.PI / 7);
    +    goog.vec.mat4f.multMat(m1, m0, m0);
    +    goog.vec.mat4f.rotateX(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateY() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), randommat4f)
    +
    +    goog.vec.mat4f.makeRotateY(m0, Math.PI / 7);
    +    goog.vec.mat4f.multMat(m1, m0, m0);
    +    goog.vec.mat4f.rotateY(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testRotateZ() {
    +    var m0 = goog.vec.mat4f.create();
    +    var m1 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), randommat4f)
    +
    +    goog.vec.mat4f.makeRotateZ(m0, Math.PI / 7);
    +    goog.vec.mat4f.multMat(m1, m0, m0);
    +    goog.vec.mat4f.rotateZ(m1, Math.PI / 7);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testGetTranslation() {
    +    var mat = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), randommat4f);
    +    var translation = goog.vec.vec3f.create();
    +    goog.vec.mat4f.getTranslation(mat, translation);
    +    assertElementsRoughlyEqual(
    +        [0.59465038776, 0.413479506969, 0.0663217827677],
    +        translation, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeFrustum() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeFrustum(m0, -1, 2, -2, 1, .1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.06666666, 0, 0, 0,
    +         0, 0.06666666, 0, 0,
    +         0.33333333, -0.33333333, -1.2, -1,
    +         0, 0, -0.22, 0], m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakePerspective() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makePerspective(m0, 90 * Math.PI / 180, 2, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1.2, -1, 0, 0, -0.22, 0],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeOrtho() {
    +    var m0 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeOrtho(m0, -1, 2, -2, 1, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.6666666, 0, 0, 0,
    +         0, 0.6666666, 0, 0,
    +         0, 0, -2, 0,
    +         -0.333333, 0.3333333, -1.2, 1], m0, goog.vec.EPSILON);
    +
    +  }
    +
    +  function testMakeEulerZXZ() {
    +    var m0 = goog.vec.mat4f.create();
    +    var roll = 0.200982 * 2 * Math.PI;
    +    var tilt = 0.915833 * Math.PI;
    +    var yaw = 0.839392 * 2 * Math.PI;
    +
    +    goog.vec.mat4f.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat4f.rotate(m0, tilt, 1, 0, 0);
    +    goog.vec.mat4f.rotate(m0, yaw, 0, 0, 1);
    +
    +    var m1 = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeEulerZXZ(m1, roll, tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4f.toEulerZXZ(m0, euler);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +
    +    // Test negative tilt now.
    +    goog.vec.mat4f.makeRotate(m0, roll, 0, 0, 1);
    +    goog.vec.mat4f.rotate(m0, -tilt, 1, 0, 0);
    +    goog.vec.mat4f.rotate(m0, yaw, 0, 0, 1);
    +
    +    goog.vec.mat4f.makeEulerZXZ(m1, roll, -tilt, yaw);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4f.toEulerZXZ(m0, euler, true);
    +
    +    assertRoughlyEquals(roll, euler[0], goog.vec.EPSILON);
    +    assertRoughlyEquals(-tilt, euler[1], goog.vec.EPSILON);
    +    assertRoughlyEquals(yaw, euler[2], goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), 
    +    [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
    +    var m1 = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), 
    +    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.mat4f.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual(
    +        [Math.PI, Math.PI / 2, Math.PI], euler, goog.vec.EPSILON);
    +    goog.vec.mat4f.makeEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testLookAt() {
    +    var viewMatrix = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeLookAt(
    +      viewMatrix, [0, 0, 0], [1, 0, 0], [0, 1, 0]);
    +    assertElementsRoughlyEqual(
    +      [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], viewMatrix,
    +      goog.vec.EPSILON);
    +  }
    +
    +  function testToLookAt() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [1, 0, 0];
    +    var upExp = [0, 1, 0];
    +
    +    var centerExp = [0, 0, 0];
    +    goog.vec.vec3f.add(eyeExp, fwdExp, centerExp);
    +
    +    var view = goog.vec.mat4f.create();
    +    goog.vec.mat4f.makeLookAt(view, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    goog.vec.mat4f.toLookAt(view, eyeRes, fwdRes, upRes);
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +  }
    +
    +  function testLookAtDecomposition() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var viewExp = goog.vec.mat4f.create();
    +    var viewRes = goog.vec.mat4f.create();
    +
    +    // Get a valid set of random vectors eye, forward, up by decomposing
    +    // a random matrix into a set of lookAt vectors.
    +    var tmp = goog.vec.mat4f.setFromArray(goog.vec.mat4f.create(), randommat4f);
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [0, 0, 0];
    +    var upExp = [0, 0, 0];
    +    var centerExp = [0, 0, 0];
    +    // Project the random matrix into a real modelview matrix.
    +    goog.vec.mat4f.toLookAt(tmp, eyeExp, fwdExp, upExp);
    +    goog.vec.vec3f.add(eyeExp, fwdExp, centerExp);
    +
    +    // Compute the expected modelview matrix from a set of valid random vectors.
    +    goog.vec.mat4f.makeLookAt(viewExp, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    var centerRes = [0, 0, 0];
    +    goog.vec.mat4f.toLookAt(viewExp, eyeRes, fwdRes, upRes);
    +    goog.vec.vec3f.add(eyeRes, fwdRes, centerRes);
    +
    +    goog.vec.mat4f.makeLookAt(viewRes, eyeRes, centerRes, upRes);
    +
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +    assertElementsRoughlyEqual(viewExp, viewRes, EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/matrix3.js b/mapbox-gl/closure-library/closure/goog/vec/matrix3.js
    new file mode 100644
    index 0000000000..71f554ac83
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/matrix3.js
    @@ -0,0 +1,720 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview WARNING: DEPRECATED.  Use Mat3 instead.
    + * Implements 3x3 matrices and their related functions which are
    + * compatible with WebGL. The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted. Matrix operations follow the mathematical form when multiplying
    + * vectors as follows: resultVec = matrix * vec.
    + *
    + */
    +goog.provide('goog.vec.Matrix3');
    +
    +
    +/**
    + * @typedef {goog.vec.ArrayType}
    + */
    +goog.vec.Matrix3.Type;
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix. The use of the array
    + * directly eliminates any overhead associated with the class representation
    + * defined above. The returned matrix is cleared to all zeros.
    + *
    + * @return {goog.vec.Matrix3.Type} The new, nine element array.
    + */
    +goog.vec.Matrix3.create = function() {
    +  return new Float32Array(9);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 3x3 matrix. The use of the array
    + * directly eliminates any overhead associated with the class representation
    + * defined above. The returned matrix is initialized with the identity.
    + *
    + * @return {goog.vec.Matrix3.Type} The new, nine element array.
    + */
    +goog.vec.Matrix3.createIdentity = function() {
    +  var mat = goog.vec.Matrix3.create();
    +  mat[0] = mat[4] = mat[8] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix initialized from the given array.
    + *
    + * @param {goog.vec.ArrayType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {goog.vec.Matrix3.Type} The new, nine element array.
    + */
    +goog.vec.Matrix3.createFromArray = function(matrix) {
    +  var newMatrix = goog.vec.Matrix3.create();
    +  goog.vec.Matrix3.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 3x3 matrix initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @return {goog.vec.Matrix3.Type} The new, nine element array.
    + */
    +goog.vec.Matrix3.createFromValues = function(
    +    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  var newMatrix = goog.vec.Matrix3.create();
    +  goog.vec.Matrix3.setFromValues(
    +      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 3x3 matrix.
    + *
    + * @param {goog.vec.Matrix3.Type} matrix The source 3x3 matrix.
    + * @return {goog.vec.Matrix3.Type} The new 3x3 element matrix.
    + */
    +goog.vec.Matrix3.clone =
    +    goog.vec.Matrix3.createFromArray;
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     value to retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.Matrix3.getElement = function(mat, row, column) {
    +  return mat[row + column * 3];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     value to retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + */
    +goog.vec.Matrix3.setElement = function(mat, row, column, value) {
    +  mat[row + column * 3] = value;
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + */
    +goog.vec.Matrix3.setFromValues = function(
    +    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v01;
    +  mat[4] = v11;
    +  mat[5] = v21;
    +  mat[6] = v02;
    +  mat[7] = v12;
    +  mat[8] = v22;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in column major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} values The column major ordered
    + *     array of values to store in the matrix.
    + */
    +goog.vec.Matrix3.setFromArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[1];
    +  mat[2] = values[2];
    +  mat[3] = values[3];
    +  mat[4] = values[4];
    +  mat[5] = values[5];
    +  mat[6] = values[6];
    +  mat[7] = values[7];
    +  mat[8] = values[8];
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in row major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} values The row major ordered array
    + *     of values to store in the matrix.
    + */
    +goog.vec.Matrix3.setFromRowMajorArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[3];
    +  mat[2] = values[6];
    +  mat[3] = values[1];
    +  mat[4] = values[4];
    +  mat[5] = values[7];
    +  mat[6] = values[2];
    +  mat[7] = values[5];
    +  mat[8] = values[8];
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + */
    +goog.vec.Matrix3.setDiagonalValues = function(mat, v00, v11, v22) {
    +  mat[0] = v00;
    +  mat[4] = v11;
    +  mat[8] = v22;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec The vector containing the
    + *     values.
    + */
    +goog.vec.Matrix3.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[4] = vec[1];
    +  mat[8] = vec[2];
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to recieve the
    + *     values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + */
    +goog.vec.Matrix3.setColumnValues = function(
    +    mat, column, v0, v1, v2) {
    +  var i = column * 3;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied array.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.ArrayType} vec The vector elements for the
    + *     column.
    + */
    +goog.vec.Matrix3.setColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector
    + * array.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.ArrayType} vec The vector elements to receive
    + *     the column.
    + */
    +goog.vec.Matrix3.getColumn = function(mat, column, vec) {
    +  var i = column * 3;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the set of vector elements.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec0 The values for column 0.
    + * @param {goog.vec.ArrayType} vec1 The values for column 1.
    + * @param {goog.vec.ArrayType} vec2 The values for column 2.
    + */
    +goog.vec.Matrix3.setColumns = function(
    +    mat, vec0, vec1, vec2) {
    +  goog.vec.Matrix3.setColumn(mat, 0, vec0);
    +  goog.vec.Matrix3.setColumn(mat, 1, vec1);
    +  goog.vec.Matrix3.setColumn(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vector
    + * elements.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     columns to retrieve.
    + * @param {goog.vec.ArrayType} vec0 The vector elements to receive
    + *     column 0.
    + * @param {goog.vec.ArrayType} vec1 The vector elements to receive
    + *     column 1.
    + * @param {goog.vec.ArrayType} vec2 The vector elements to receive
    + *     column 2.
    + */
    +goog.vec.Matrix3.getColumns = function(
    +    mat, vec0, vec1, vec2) {
    +  goog.vec.Matrix3.getColumn(mat, 0, vec0);
    +  goog.vec.Matrix3.getColumn(mat, 1, vec1);
    +  goog.vec.Matrix3.getColumn(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + */
    +goog.vec.Matrix3.setRowValues = function(mat, row, v0, v1, v2) {
    +  mat[row] = v0;
    +  mat[row + 3] = v1;
    +  mat[row + 6] = v2;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.ArrayType} vec The vector containing the values.
    + */
    +goog.vec.Matrix3.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 3] = vec[1];
    +  mat[row + 6] = vec[2];
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.ArrayType} vec The vector to receive the row.
    + */
    +goog.vec.Matrix3.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 3];
    +  vec[2] = mat[row + 6];
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec0 The values for row 0.
    + * @param {goog.vec.ArrayType} vec1 The values for row 1.
    + * @param {goog.vec.ArrayType} vec2 The values for row 2.
    + */
    +goog.vec.Matrix3.setRows = function(
    +    mat, vec0, vec1, vec2) {
    +  goog.vec.Matrix3.setRow(mat, 0, vec0);
    +  goog.vec.Matrix3.setRow(mat, 1, vec1);
    +  goog.vec.Matrix3.setRow(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to supplying
    + *     the values.
    + * @param {goog.vec.ArrayType} vec0 The vector to receive row 0.
    + * @param {goog.vec.ArrayType} vec1 The vector to receive row 1.
    + * @param {goog.vec.ArrayType} vec2 The vector to receive row 2.
    + */
    +goog.vec.Matrix3.getRows = function(
    +    mat, vec0, vec1, vec2) {
    +  goog.vec.Matrix3.getRow(mat, 0, vec0);
    +  goog.vec.Matrix3.getRow(mat, 1, vec1);
    +  goog.vec.Matrix3.getRow(mat, 2, vec2);
    +};
    +
    +
    +/**
    + * Clears the given matrix to zero.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to clear.
    + */
    +goog.vec.Matrix3.setZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +};
    +
    +
    +/**
    + * Sets the given matrix to the identity matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to set.
    + */
    +goog.vec.Matrix3.setIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 1;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 1;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrices mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first addend.
    + * @param {goog.vec.ArrayType} mat1 The second addend.
    + * @param {goog.vec.ArrayType} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.add = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrices mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The minuend.
    + * @param {goog.vec.ArrayType} mat1 The subtrahend.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.subtract = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a component-wise multiplication of mat0 with the given scalar
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The matrix to scale.
    + * @param {number} scalar The scalar value to multiple to each element of mat0.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be mat0).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.scale = function(mat0, scalar, resultMat) {
    +  resultMat[0] = mat0[0] * scalar;
    +  resultMat[1] = mat0[1] * scalar;
    +  resultMat[2] = mat0[2] * scalar;
    +  resultMat[3] = mat0[3] * scalar;
    +  resultMat[4] = mat0[4] * scalar;
    +  resultMat[5] = mat0[5] * scalar;
    +  resultMat[6] = mat0[6] * scalar;
    +  resultMat[7] = mat0[7] * scalar;
    +  resultMat[8] = mat0[8] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first (left hand) matrix.
    + * @param {goog.vec.ArrayType} mat1 The second (right hand)
    + *     matrix.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
    +  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
    +  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
    +  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
    +  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
    +  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
    +  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
    +  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
    +  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + * @param {goog.vec.ArrayType} mat The matrix to transpose.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = a10;
    +    resultMat[5] = mat[7];
    +    resultMat[6] = a20;
    +    resultMat[7] = a21;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[3];
    +    resultMat[2] = mat[6];
    +    resultMat[3] = mat[1];
    +    resultMat[4] = mat[4];
    +    resultMat[5] = mat[7];
    +    resultMat[6] = mat[2];
    +    resultMat[7] = mat[5];
    +    resultMat[8] = mat[8];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat0 storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + * @param {goog.vec.ArrayType} mat0 The matrix to invert.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the result (may be mat0).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.Matrix3.invert = function(mat0, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
    +  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
    +  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
    +
    +  var t00 = a11 * a22 - a12 * a21;
    +  var t10 = a12 * a20 - a10 * a22;
    +  var t20 = a10 * a21 - a11 * a20;
    +  var det = a00 * t00 + a01 * t10 + a02 * t20;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1 / det;
    +  resultMat[0] = t00 * idet;
    +  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
    +  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
    +
    +  resultMat[1] = t10 * idet;
    +  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
    +  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
    +
    +  resultMat[2] = t20 * idet;
    +  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
    +  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first matrix.
    + * @param {goog.vec.ArrayType} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.Matrix3.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed matrix into resultVec.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The vector to transform.
    + * @param {goog.vec.ArrayType} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix3.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
    +  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
    +  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Initializes the given 3x3 matrix as a translation matrix with x and y
    + * translation values.
    + *
    + * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + */
    +goog.vec.Matrix3.makeTranslate = function(mat, x, y) {
    +  goog.vec.Matrix3.setIdentity(mat);
    +  goog.vec.Matrix3.setColumnValues(mat, 2, x, y, 1);
    +};
    +
    +
    +/**
    + * Initializes the given 3x3 matrix as a scale matrix with x, y and z scale
    + * factors.
    + * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
    + *     array to receive the new scale matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + */
    +goog.vec.Matrix3.makeScale = function(mat, x, y, z) {
    +  goog.vec.Matrix3.setIdentity(mat);
    +  goog.vec.Matrix3.setDiagonalValues(mat, x, y, z);
    +};
    +
    +
    +/**
    + * Initializes the given 3x3 matrix as a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
    + *     array to receive the new scale matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + */
    +goog.vec.Matrix3.makeAxisAngleRotate = function(
    +    mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  goog.vec.Matrix3.setFromValues(mat,
    +      ax * ax * d + c,
    +      ax * ay * d + az * s,
    +      ax * az * d - ay * s,
    +
    +      ax * ay * d - az * s,
    +      ay * ay * d + c,
    +      ay * az * d + ax * s,
    +
    +      ax * az * d + ay * s,
    +      ay * az * d - ax * s,
    +      az * az * d + c);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/matrix3_test.html b/mapbox-gl/closure-library/closure/goog/vec/matrix3_test.html
    new file mode 100644
    index 0000000000..d66b04fae2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/matrix3_test.html
    @@ -0,0 +1,301 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Vec3</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Matrix3');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructor() {
    +    var m0 = goog.vec.Matrix3.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Matrix3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +
    +    var m2 = goog.vec.Matrix3.createFromArray(m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m2);
    +
    +    var m3 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m3);
    +
    +    var m4 = goog.vec.Matrix3.createIdentity();
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m4);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.Matrix3.create();
    +    var m1 = goog.vec.Matrix3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    goog.vec.Matrix3.setFromArray(m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    goog.vec.Matrix3.setFromValues(m0, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 10], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setDiagonalValues(m0, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], m0);
    +
    +    goog.vec.Matrix3.setDiagonal(m0, [4, 5, 6]);
    +    assertElementsEquals([4, 0, 0, 0, 5, 0, 0, 0, 6], m0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setColumn(m0, 0, [1, 2, 3]);
    +    goog.vec.Matrix3.setColumn(m0, 1, [4, 5, 6]);
    +    goog.vec.Matrix3.setColumn(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.Matrix3.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.Matrix3.getColumn(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.Matrix3.getColumn(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setColumns(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.Matrix3.getColumns(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setRow(m0, 0, [1, 2, 3]);
    +    goog.vec.Matrix3.setRow(m0, 1, [4, 5, 6]);
    +    goog.vec.Matrix3.setRow(m0, 2, [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0];
    +    goog.vec.Matrix3.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +    goog.vec.Matrix3.getRow(m0, 1, v0);
    +    assertElementsEquals([4, 5, 6], v0);
    +    goog.vec.Matrix3.getRow(m0, 2, v0);
    +    assertElementsEquals([7, 8, 9], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setRows(m0, [1, 2, 3], [4, 5, 6], [7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +
    +    var v0 = [0, 0, 0], v1 = [0, 0, 0], v2 = [0, 0, 0];
    +    goog.vec.Matrix3.getRows(m0, v0, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([7, 8, 9], v2);
    +  }
    +
    +  function testSetRowMajorArray() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setFromRowMajorArray(
    +        m0, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m0);
    +  }
    +
    +  function testSetZero() {
    +    var m0 = goog.vec.Matrix3.createFromArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    goog.vec.Matrix3.setZero(m0);
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testSetIdentity() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setIdentity(m0);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.Matrix3.create();
    +    for (var r = 0; r < 3; r++) {
    +      for (var c = 0; c < 3; c++) {
    +        var value = c * 3 + r + 1;
    +        goog.vec.Matrix3.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.Matrix3.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +  }
    +
    +  function testAdd() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.createFromValues(3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.add(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m2);
    +
    +    goog.vec.Matrix3.add(m0, m1, m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([4, 6, 8, 10, 12, 14, 16, 9, 11], m0);
    +  }
    +
    +  function testSubtract() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.createFromValues(3, 4, 5, 6, 7, 8, 9, 1, 2);
    +    var m2 = goog.vec.Matrix3.create();
    +
    +    goog.vec.Matrix3.subtract(m0, m1, m2);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([3, 4, 5, 6, 7, 8, 9, 1, 2], m1);
    +    assertElementsEquals([-2, -2, -2, -2, -2, -2, -2, 7, 7], m2);
    +
    +    goog.vec.Matrix3.subtract(m1, m0, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([2, 2, 2, 2, 2, 2, 2, -7, -7], m1);
    +  }
    +
    +  function testScale() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.create();
    +
    +    goog.vec.Matrix3.scale(m0, 5, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m1);
    +
    +    goog.vec.Matrix3.scale(m0, 5, m0);
    +    assertElementsEquals([5, 10, 15, 20, 25, 30, 35, 40, 45], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m2 = goog.vec.Matrix3.create();
    +
    +    goog.vec.Matrix3.multMat(m0, m1, m2);
    +    assertElementsEquals([30, 36, 42, 66, 81, 96, 102, 126, 150], m2);
    +
    +    goog.vec.Matrix3.add(m0, m1, m1);
    +    goog.vec.Matrix3.multMat(m0, m1, m1);
    +    assertElementsEquals([60, 72, 84, 132, 162, 192, 204, 252, 300], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.transpose(m0, m1);
    +    assertElementsEquals([1, 4, 7, 2, 5, 8, 3, 6, 9], m1);
    +    goog.vec.Matrix3.transpose(m1, m1);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], m1);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.Matrix3.invert(m0, m0));
    +    assertElementsEquals([1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Matrix3.setFromValues(m0, 1, 2, 3, 1, 3, 4, 3, 4, 5);
    +    assertTrue(goog.vec.Matrix3.invert(m0, m0));
    +    assertElementsEquals([0.5, -1.0, 0.5, -3.5, 2.0, 0.5, 2.5, -1.0, -0.5], m0);
    +
    +    goog.vec.Matrix3.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.Matrix3.invert(m0, m0));
    +    var m1 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var m1 = goog.vec.Matrix3.createFromArray(m0);
    +    assertTrue(goog.vec.Matrix3.equals(m0, m1));
    +    assertTrue(goog.vec.Matrix3.equals(m1, m0));
    +    for (var i = 0; i < 9; i++) {
    +      m1[i] = 15;
    +      assertFalse(goog.vec.Matrix3.equals(m0, m1));
    +      assertFalse(goog.vec.Matrix3.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.Matrix3.createFromValues(1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Matrix3.multVec3(m0, v0, v1);
    +    assertElementsEquals([30, 36, 42], v1);
    +    goog.vec.Matrix3.multVec3(m0, v0, v0);
    +    assertElementsEquals([30, 36, 42], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.Matrix3.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    goog.vec.Matrix3.setFromValues(a0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a0);
    +
    +    var a1 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.setDiagonalValues(a1, 1, 2, 3);
    +    assertElementsEquals([1, 0, 0, 0, 2, 0, 0, 0, 3], a1);
    +
    +    goog.vec.Matrix3.setColumnValues(a1, 0, 2, 3, 4);
    +    goog.vec.Matrix3.setColumnValues(a1, 1, 5, 6, 7);
    +    goog.vec.Matrix3.setColumnValues(a1, 2, 8, 9, 1);
    +    assertElementsEquals([2, 3, 4, 5, 6, 7, 8, 9, 1], a1);
    +
    +    goog.vec.Matrix3.setRowValues(a1, 0, 1, 4, 7);
    +    goog.vec.Matrix3.setRowValues(a1, 1, 2, 5, 8);
    +    goog.vec.Matrix3.setRowValues(a1, 2, 3, 6, 9);
    +    assertElementsEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.makeTranslate(m0, 3, 4);
    +    assertElementsEquals([1, 0, 0, 0, 1, 0, 3, 4, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 4, 0, 0, 0, 5], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.makeAxisAngleRotate(m0, Math.PI / 2, 0, 0, 1);
    +    var v0 = [0, 1, 0, -1, 0, 0, 0, 0, 1];
    +    for (var i = 0; i < 9; ++i) {
    +      assertTrue(Math.abs(m0[i] - v0[i]) < 1e-6);
    +    }
    +
    +    var m1 = goog.vec.Matrix3.create();
    +    goog.vec.Matrix3.makeAxisAngleRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.Matrix3.multMat(m0, m1, m1);
    +    var v1 = [0.7071068, 0.7071068, 0, -0.7071068, 0.7071068, 0, 0, 0, 1];
    +    for (var i = 0; i < 9; ++i) {
    +      assertTrue(Math.abs(m1[i] - v1[i]) < 1e-6);
    +    }
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/matrix4.js b/mapbox-gl/closure-library/closure/goog/vec/matrix4.js
    new file mode 100644
    index 0000000000..c5a693f2cd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/matrix4.js
    @@ -0,0 +1,1405 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview WARNING: DEPRECATED.  Use Mat4 instead.
    + * Implements 4x4 matrices and their related functions which are
    + * compatible with WebGL. The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted. Matrix operations follow the mathematical form when multiplying
    + * vectors as follows: resultVec = matrix * vec.
    + *
    + */
    +goog.provide('goog.vec.Matrix4');
    +
    +goog.require('goog.vec');
    +goog.require('goog.vec.Vec3');
    +goog.require('goog.vec.Vec4');
    +
    +
    +/**
    + * @typedef {goog.vec.ArrayType}
    + */
    +goog.vec.Matrix4.Type;
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix. The use of the array
    + * directly eliminates any overhead associated with the class representation
    + * defined above. The returned matrix is cleared to all zeros.
    + *
    + * @return {goog.vec.Matrix4.Type} The new, sixteen element array.
    + */
    +goog.vec.Matrix4.create = function() {
    +  return new Float32Array(16);
    +};
    +
    +
    +/**
    + * Creates the array representation of a 4x4 matrix. The use of the array
    + * directly eliminates any overhead associated with the class representation
    + * defined above. The returned matrix is initialized with the identity
    + *
    + * @return {goog.vec.Matrix4.Type} The new, sixteen element array.
    + */
    +goog.vec.Matrix4.createIdentity = function() {
    +  var mat = goog.vec.Matrix4.create();
    +  mat[0] = mat[5] = mat[10] = mat[15] = 1;
    +  return mat;
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix initialized from the given array.
    + *
    + * @param {goog.vec.ArrayType} matrix The array containing the
    + *     matrix values in column major order.
    + * @return {goog.vec.Matrix4.Type} The new, 16 element array.
    + */
    +goog.vec.Matrix4.createFromArray = function(matrix) {
    +  var newMatrix = goog.vec.Matrix4.create();
    +  goog.vec.Matrix4.setFromArray(newMatrix, matrix);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a 4x4 matrix initialized from the given values.
    + *
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + * @return {goog.vec.Matrix4.Type} The new, 16 element array.
    + */
    +goog.vec.Matrix4.createFromValues = function(
    +    v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  var newMatrix = goog.vec.Matrix4.create();
    +  goog.vec.Matrix4.setFromValues(
    +      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +  return newMatrix;
    +};
    +
    +
    +/**
    + * Creates a clone of a 4x4 matrix.
    + *
    + * @param {goog.vec.Matrix4.Type} matrix The source 4x4 matrix.
    + * @return {goog.vec.Matrix4.Type} The new, 16 element matrix.
    + */
    +goog.vec.Matrix4.clone =
    +    goog.vec.Matrix4.createFromArray;
    +
    +
    +/**
    + * Retrieves the element at the requested row and column.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     value to retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @return {number} The element value at the requested row, column indices.
    + */
    +goog.vec.Matrix4.getElement = function(mat, row, column) {
    +  return mat[row + column * 4];
    +};
    +
    +
    +/**
    + * Sets the element at the requested row and column.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     value to retrieve.
    + * @param {number} row The row index.
    + * @param {number} column The column index.
    + * @param {number} value The value to set at the requested row, column.
    + */
    +goog.vec.Matrix4.setElement = function(mat, row, column, value) {
    +  mat[row + column * 4] = value;
    +};
    +
    +
    +/**
    + * Initializes the matrix from the set of values. Note the values supplied are
    + * in column major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values at (0, 0).
    + * @param {number} v10 The values at (1, 0).
    + * @param {number} v20 The values at (2, 0).
    + * @param {number} v30 The values at (3, 0).
    + * @param {number} v01 The values at (0, 1).
    + * @param {number} v11 The values at (1, 1).
    + * @param {number} v21 The values at (2, 1).
    + * @param {number} v31 The values at (3, 1).
    + * @param {number} v02 The values at (0, 2).
    + * @param {number} v12 The values at (1, 2).
    + * @param {number} v22 The values at (2, 2).
    + * @param {number} v32 The values at (3, 2).
    + * @param {number} v03 The values at (0, 3).
    + * @param {number} v13 The values at (1, 3).
    + * @param {number} v23 The values at (2, 3).
    + * @param {number} v33 The values at (3, 3).
    + */
    +goog.vec.Matrix4.setFromValues = function(
    +    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
    +    v03, v13, v23, v33) {
    +  mat[0] = v00;
    +  mat[1] = v10;
    +  mat[2] = v20;
    +  mat[3] = v30;
    +  mat[4] = v01;
    +  mat[5] = v11;
    +  mat[6] = v21;
    +  mat[7] = v31;
    +  mat[8] = v02;
    +  mat[9] = v12;
    +  mat[10] = v22;
    +  mat[11] = v32;
    +  mat[12] = v03;
    +  mat[13] = v13;
    +  mat[14] = v23;
    +  mat[15] = v33;
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in column major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} values The column major ordered
    + *     array of values to store in the matrix.
    + */
    +goog.vec.Matrix4.setFromArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[1];
    +  mat[2] = values[2];
    +  mat[3] = values[3];
    +  mat[4] = values[4];
    +  mat[5] = values[5];
    +  mat[6] = values[6];
    +  mat[7] = values[7];
    +  mat[8] = values[8];
    +  mat[9] = values[9];
    +  mat[10] = values[10];
    +  mat[11] = values[11];
    +  mat[12] = values[12];
    +  mat[13] = values[13];
    +  mat[14] = values[14];
    +  mat[15] = values[15];
    +};
    +
    +
    +/**
    + * Sets the matrix from the array of values stored in row major order.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} values The row major ordered array of
    + *     values to store in the matrix.
    + */
    +goog.vec.Matrix4.setFromRowMajorArray = function(mat, values) {
    +  mat[0] = values[0];
    +  mat[1] = values[4];
    +  mat[2] = values[8];
    +  mat[3] = values[12];
    +
    +  mat[4] = values[1];
    +  mat[5] = values[5];
    +  mat[6] = values[9];
    +  mat[7] = values[13];
    +
    +  mat[8] = values[2];
    +  mat[9] = values[6];
    +  mat[10] = values[10];
    +  mat[11] = values[14];
    +
    +  mat[12] = values[3];
    +  mat[13] = values[7];
    +  mat[14] = values[11];
    +  mat[15] = values[15];
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} v00 The values for (0, 0).
    + * @param {number} v11 The values for (1, 1).
    + * @param {number} v22 The values for (2, 2).
    + * @param {number} v33 The values for (3, 3).
    + */
    +goog.vec.Matrix4.setDiagonalValues = function(
    +    mat, v00, v11, v22, v33) {
    +  mat[0] = v00;
    +  mat[5] = v11;
    +  mat[10] = v22;
    +  mat[15] = v33;
    +};
    +
    +
    +/**
    + * Sets the diagonal values of the matrix from the given vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec The vector containing the
    + *     values.
    + */
    +goog.vec.Matrix4.setDiagonal = function(mat, vec) {
    +  mat[0] = vec[0];
    +  mat[5] = vec[1];
    +  mat[10] = vec[2];
    +  mat[15] = vec[3];
    +};
    +
    +
    +/**
    + * Sets the specified column with the supplied values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to recieve the
    + *     values.
    + * @param {number} column The column index to set the values on.
    + * @param {number} v0 The value for row 0.
    + * @param {number} v1 The value for row 1.
    + * @param {number} v2 The value for row 2.
    + * @param {number} v3 The value for row 3.
    + */
    +goog.vec.Matrix4.setColumnValues = function(
    +    mat, column, v0, v1, v2, v3) {
    +  var i = column * 4;
    +  mat[i] = v0;
    +  mat[i + 1] = v1;
    +  mat[i + 2] = v2;
    +  mat[i + 3] = v3;
    +};
    +
    +
    +/**
    + * Sets the specified column with the value from the supplied array.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} column The column index to set the values on.
    + * @param {goog.vec.ArrayType} vec The vector of elements for the
    + *     column.
    + */
    +goog.vec.Matrix4.setColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  mat[i] = vec[0];
    +  mat[i + 1] = vec[1];
    +  mat[i + 2] = vec[2];
    +  mat[i + 3] = vec[3];
    +};
    +
    +
    +/**
    + * Retrieves the specified column from the matrix into the given vector
    + * array.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     values.
    + * @param {number} column The column to get the values from.
    + * @param {goog.vec.ArrayType} vec The vector of elements to
    + *     receive the column.
    + */
    +goog.vec.Matrix4.getColumn = function(mat, column, vec) {
    +  var i = column * 4;
    +  vec[0] = mat[i];
    +  vec[1] = mat[i + 1];
    +  vec[2] = mat[i + 2];
    +  vec[3] = mat[i + 3];
    +};
    +
    +
    +/**
    + * Sets the columns of the matrix from the set of vector elements.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec0 The values for column 0.
    + * @param {goog.vec.ArrayType} vec1 The values for column 1.
    + * @param {goog.vec.ArrayType} vec2 The values for column 2.
    + * @param {goog.vec.ArrayType} vec3 The values for column 3.
    + */
    +goog.vec.Matrix4.setColumns = function(
    +    mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Matrix4.setColumn(mat, 0, vec0);
    +  goog.vec.Matrix4.setColumn(mat, 1, vec1);
    +  goog.vec.Matrix4.setColumn(mat, 2, vec2);
    +  goog.vec.Matrix4.setColumn(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Retrieves the column values from the given matrix into the given vector
    + * elements.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix containing the
    + *     columns to retrieve.
    + * @param {goog.vec.ArrayType} vec0 The vector elements to receive
    + *     column 0.
    + * @param {goog.vec.ArrayType} vec1 The vector elements to receive
    + *     column 1.
    + * @param {goog.vec.ArrayType} vec2 The vector elements to receive
    + *     column 2.
    + * @param {goog.vec.ArrayType} vec3 The vector elements to receive
    + *     column 3.
    + */
    +goog.vec.Matrix4.getColumns = function(
    +    mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Matrix4.getColumn(mat, 0, vec0);
    +  goog.vec.Matrix4.getColumn(mat, 1, vec1);
    +  goog.vec.Matrix4.getColumn(mat, 2, vec2);
    +  goog.vec.Matrix4.getColumn(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied values.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {number} row The index of the row to receive the values.
    + * @param {number} v0 The value for column 0.
    + * @param {number} v1 The value for column 1.
    + * @param {number} v2 The value for column 2.
    + * @param {number} v3 The value for column 3.
    + */
    +goog.vec.Matrix4.setRowValues = function(mat, row, v0, v1, v2, v3) {
    +  mat[row] = v0;
    +  mat[row + 4] = v1;
    +  mat[row + 8] = v2;
    +  mat[row + 12] = v3;
    +};
    +
    +
    +/**
    + * Sets the row values from the supplied vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     row values.
    + * @param {number} row The index of the row.
    + * @param {goog.vec.ArrayType} vec The vector containing the
    + *     values.
    + */
    +goog.vec.Matrix4.setRow = function(mat, row, vec) {
    +  mat[row] = vec[0];
    +  mat[row + 4] = vec[1];
    +  mat[row + 8] = vec[2];
    +  mat[row + 12] = vec[3];
    +};
    +
    +
    +/**
    + * Retrieves the row values into the given vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     values.
    + * @param {number} row The index of the row supplying the values.
    + * @param {goog.vec.ArrayType} vec The vector to receive the
    + *     row.
    + */
    +goog.vec.Matrix4.getRow = function(mat, row, vec) {
    +  vec[0] = mat[row];
    +  vec[1] = mat[row + 4];
    +  vec[2] = mat[row + 8];
    +  vec[3] = mat[row + 12];
    +};
    +
    +
    +/**
    + * Sets the rows of the matrix from the supplied vectors.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to receive the
    + *     values.
    + * @param {goog.vec.ArrayType} vec0 The values for row 0.
    + * @param {goog.vec.ArrayType} vec1 The values for row 1.
    + * @param {goog.vec.ArrayType} vec2 The values for row 2.
    + * @param {goog.vec.ArrayType} vec3 The values for row 3.
    + */
    +goog.vec.Matrix4.setRows = function(
    +    mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Matrix4.setRow(mat, 0, vec0);
    +  goog.vec.Matrix4.setRow(mat, 1, vec1);
    +  goog.vec.Matrix4.setRow(mat, 2, vec2);
    +  goog.vec.Matrix4.setRow(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Retrieves the rows of the matrix into the supplied vectors.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to supply the
    + *     values.
    + * @param {goog.vec.ArrayType} vec0 The vector to receive row 0.
    + * @param {goog.vec.ArrayType} vec1 The vector to receive row 1.
    + * @param {goog.vec.ArrayType} vec2 The vector to receive row 2.
    + * @param {goog.vec.ArrayType} vec3 The vector to receive row 3.
    + */
    +goog.vec.Matrix4.getRows = function(
    +    mat, vec0, vec1, vec2, vec3) {
    +  goog.vec.Matrix4.getRow(mat, 0, vec0);
    +  goog.vec.Matrix4.getRow(mat, 1, vec1);
    +  goog.vec.Matrix4.getRow(mat, 2, vec2);
    +  goog.vec.Matrix4.getRow(mat, 3, vec3);
    +};
    +
    +
    +/**
    + * Clears the given matrix to zero.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to clear.
    + */
    +goog.vec.Matrix4.setZero = function(mat) {
    +  mat[0] = 0;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 0;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 0;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 0;
    +};
    +
    +
    +/**
    + * Sets the given matrix to the identity matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to set.
    + */
    +goog.vec.Matrix4.setIdentity = function(mat) {
    +  mat[0] = 1;
    +  mat[1] = 0;
    +  mat[2] = 0;
    +  mat[3] = 0;
    +  mat[4] = 0;
    +  mat[5] = 1;
    +  mat[6] = 0;
    +  mat[7] = 0;
    +  mat[8] = 0;
    +  mat[9] = 0;
    +  mat[10] = 1;
    +  mat[11] = 0;
    +  mat[12] = 0;
    +  mat[13] = 0;
    +  mat[14] = 0;
    +  mat[15] = 1;
    +};
    +
    +
    +/**
    + * Performs a per-component addition of the matrix mat0 and mat1, storing
    + * the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first addend.
    + * @param {goog.vec.ArrayType} mat1 The second addend.
    + * @param {goog.vec.ArrayType} resultMat The matrix to
    + *     receive the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.add = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] + mat1[0];
    +  resultMat[1] = mat0[1] + mat1[1];
    +  resultMat[2] = mat0[2] + mat1[2];
    +  resultMat[3] = mat0[3] + mat1[3];
    +  resultMat[4] = mat0[4] + mat1[4];
    +  resultMat[5] = mat0[5] + mat1[5];
    +  resultMat[6] = mat0[6] + mat1[6];
    +  resultMat[7] = mat0[7] + mat1[7];
    +  resultMat[8] = mat0[8] + mat1[8];
    +  resultMat[9] = mat0[9] + mat1[9];
    +  resultMat[10] = mat0[10] + mat1[10];
    +  resultMat[11] = mat0[11] + mat1[11];
    +  resultMat[12] = mat0[12] + mat1[12];
    +  resultMat[13] = mat0[13] + mat1[13];
    +  resultMat[14] = mat0[14] + mat1[14];
    +  resultMat[15] = mat0[15] + mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a per-component subtraction of the matrix mat0 and mat1,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The minuend.
    + * @param {goog.vec.ArrayType} mat1 The subtrahend.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.subtract = function(mat0, mat1, resultMat) {
    +  resultMat[0] = mat0[0] - mat1[0];
    +  resultMat[1] = mat0[1] - mat1[1];
    +  resultMat[2] = mat0[2] - mat1[2];
    +  resultMat[3] = mat0[3] - mat1[3];
    +  resultMat[4] = mat0[4] - mat1[4];
    +  resultMat[5] = mat0[5] - mat1[5];
    +  resultMat[6] = mat0[6] - mat1[6];
    +  resultMat[7] = mat0[7] - mat1[7];
    +  resultMat[8] = mat0[8] - mat1[8];
    +  resultMat[9] = mat0[9] - mat1[9];
    +  resultMat[10] = mat0[10] - mat1[10];
    +  resultMat[11] = mat0[11] - mat1[11];
    +  resultMat[12] = mat0[12] - mat1[12];
    +  resultMat[13] = mat0[13] - mat1[13];
    +  resultMat[14] = mat0[14] - mat1[14];
    +  resultMat[15] = mat0[15] - mat1[15];
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Performs a component-wise multiplication of mat0 with the given scalar
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The matrix to scale.
    + * @param {number} scalar The scalar value to multiple to each element of mat0.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be mat0).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.scale = function(mat0, scalar, resultMat) {
    +  resultMat[0] = mat0[0] * scalar;
    +  resultMat[1] = mat0[1] * scalar;
    +  resultMat[2] = mat0[2] * scalar;
    +  resultMat[3] = mat0[3] * scalar;
    +  resultMat[4] = mat0[4] * scalar;
    +  resultMat[5] = mat0[5] * scalar;
    +  resultMat[6] = mat0[6] * scalar;
    +  resultMat[7] = mat0[7] * scalar;
    +  resultMat[8] = mat0[8] * scalar;
    +  resultMat[9] = mat0[9] * scalar;
    +  resultMat[10] = mat0[10] * scalar;
    +  resultMat[11] = mat0[11] * scalar;
    +  resultMat[12] = mat0[12] * scalar;
    +  resultMat[13] = mat0[13] * scalar;
    +  resultMat[14] = mat0[14] * scalar;
    +  resultMat[15] = mat0[15] * scalar;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
    + * storing the result into resultMat.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first (left hand) matrix.
    + * @param {goog.vec.ArrayType} mat1 The second (right hand)
    + *     matrix.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be either mat0 or mat1).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multMat = function(mat0, mat1, resultMat) {
    +  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
    +  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
    +  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
    +  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
    +
    +  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
    +  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
    +  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
    +  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
    +
    +  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
    +  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
    +  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
    +  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
    +
    +  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
    +  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
    +  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
    +  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
    +
    +  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
    +  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
    +  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
    +  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
    +
    +  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
    +  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
    +  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
    +  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Transposes the given matrix mat storing the result into resultMat.
    + * @param {goog.vec.ArrayType} mat The matrix to transpose.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the results (may be mat).
    + * @return {goog.vec.ArrayType} return resultMat so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.transpose = function(mat, resultMat) {
    +  if (resultMat == mat) {
    +    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
    +    var a21 = mat[6], a31 = mat[7];
    +    var a32 = mat[11];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +    resultMat[4] = a10;
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +    resultMat[8] = a20;
    +    resultMat[9] = a21;
    +    resultMat[11] = mat[14];
    +    resultMat[12] = a30;
    +    resultMat[13] = a31;
    +    resultMat[14] = a32;
    +  } else {
    +    resultMat[0] = mat[0];
    +    resultMat[1] = mat[4];
    +    resultMat[2] = mat[8];
    +    resultMat[3] = mat[12];
    +
    +    resultMat[4] = mat[1];
    +    resultMat[5] = mat[5];
    +    resultMat[6] = mat[9];
    +    resultMat[7] = mat[13];
    +
    +    resultMat[8] = mat[2];
    +    resultMat[9] = mat[6];
    +    resultMat[10] = mat[10];
    +    resultMat[11] = mat[14];
    +
    +    resultMat[12] = mat[3];
    +    resultMat[13] = mat[7];
    +    resultMat[14] = mat[11];
    +    resultMat[15] = mat[15];
    +  }
    +  return resultMat;
    +};
    +
    +
    +/**
    + * Computes the determinant of the matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to compute the
    + *     matrix for.
    + * @return {number} The determinant of the matrix.
    + */
    +goog.vec.Matrix4.determinant = function(mat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +};
    +
    +
    +/**
    + * Computes the inverse of mat storing the result into resultMat. If the
    + * inverse is defined, this function returns true, false otherwise.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix to invert.
    + * @param {goog.vec.ArrayType} resultMat The matrix to receive
    + *     the result (may be mat).
    + * @return {boolean} True if the inverse is defined. If false is returned,
    + *     resultMat is not modified.
    + */
    +goog.vec.Matrix4.invert = function(mat, resultMat) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var a0 = m00 * m11 - m10 * m01;
    +  var a1 = m00 * m21 - m20 * m01;
    +  var a2 = m00 * m31 - m30 * m01;
    +  var a3 = m10 * m21 - m20 * m11;
    +  var a4 = m10 * m31 - m30 * m11;
    +  var a5 = m20 * m31 - m30 * m21;
    +  var b0 = m02 * m13 - m12 * m03;
    +  var b1 = m02 * m23 - m22 * m03;
    +  var b2 = m02 * m33 - m32 * m03;
    +  var b3 = m12 * m23 - m22 * m13;
    +  var b4 = m12 * m33 - m32 * m13;
    +  var b5 = m22 * m33 - m32 * m23;
    +
    +  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
    +  if (det == 0) {
    +    return false;
    +  }
    +
    +  var idet = 1.0 / det;
    +  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
    +  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
    +  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
    +  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
    +  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
    +  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
    +  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
    +  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
    +  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
    +  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
    +  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
    +  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
    +  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
    +  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
    +  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
    +  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
    +  return true;
    +};
    +
    +
    +/**
    + * Returns true if the components of mat0 are equal to the components of mat1.
    + *
    + * @param {goog.vec.ArrayType} mat0 The first matrix.
    + * @param {goog.vec.ArrayType} mat1 The second matrix.
    + * @return {boolean} True if the the two matrices are equivalent.
    + */
    +goog.vec.Matrix4.equals = function(mat0, mat1) {
    +  return mat0.length == mat1.length &&
    +      mat0[0] == mat1[0] &&
    +      mat0[1] == mat1[1] &&
    +      mat0[2] == mat1[2] &&
    +      mat0[3] == mat1[3] &&
    +      mat0[4] == mat1[4] &&
    +      mat0[5] == mat1[5] &&
    +      mat0[6] == mat1[6] &&
    +      mat0[7] == mat1[7] &&
    +      mat0[8] == mat1[8] &&
    +      mat0[9] == mat1[9] &&
    +      mat0[10] == mat1[10] &&
    +      mat0[11] == mat1[11] &&
    +      mat0[12] == mat1[12] &&
    +      mat0[13] == mat1[13] &&
    +      mat0[14] == mat1[14] &&
    +      mat0[15] == mat1[15];
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The 3 element vector to
    + *     transform.
    + * @param {goog.vec.ArrayType} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec3 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 3x3 matrix omitting the projective component and translation
    + * components.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The 3 element vector to
    + *     transform.
    + * @param {goog.vec.ArrayType} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec3NoTranslate = function(
    +    mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
    + * vector to a 3 element vector.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The 3 element vector to
    + *     transform.
    + * @param {goog.vec.ArrayType} resultVec The 3 element vector
    + *     to receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec3Projective = function(
    +    mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2];
    +  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
    +  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
    +  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
    +  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The vector to transform.
    + * @param {goog.vec.ArrayType} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec4 = function(mat, vec, resultVec) {
    +  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
    +  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
    +  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
    +  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
    +  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input matrix is multiplied against the
    + * upper 3x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The 3 element vector to
    + *     transform.
    + * @param {goog.vec.ArrayType} resultVec The 3 element vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec3ToArray = function(mat, vec, resultVec) {
    +  goog.vec.Matrix4.multVec3(
    +      mat, vec, /** @type {goog.vec.ArrayType} */ (resultVec));
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix supplying the
    + *     transformation.
    + * @param {goog.vec.ArrayType} vec The vector to transform.
    + * @param {goog.vec.ArrayType} resultVec The vector to
    + *     receive the results (may be vec).
    + * @return {goog.vec.ArrayType} return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Matrix4.multVec4ToArray = function(mat, vec, resultVec) {
    +  goog.vec.Matrix4.multVec4(
    +      mat, vec, /** @type {goog.vec.ArrayType} */ (resultVec));
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as a translation matrix with x, y and z
    + * translation factors.
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + */
    +goog.vec.Matrix4.makeTranslate = function(mat, x, y, z) {
    +  goog.vec.Matrix4.setIdentity(mat);
    +  goog.vec.Matrix4.setColumnValues(mat, 3, x, y, z, 1);
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as a scale matrix with x, y and z scale
    + * factors.
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} x The scale along the x axis.
    + * @param {number} y The scale along the y axis.
    + * @param {number} z The scale along the z axis.
    + */
    +goog.vec.Matrix4.makeScale = function(mat, x, y, z) {
    +  goog.vec.Matrix4.setIdentity(mat);
    +  goog.vec.Matrix4.setDiagonalValues(mat, x, y, z, 1);
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as a rotation matrix with the given rotation
    + * angle about the axis defined by the vector (ax, ay, az).
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} angle The rotation angle in radians.
    + * @param {number} ax The x component of the rotation axis.
    + * @param {number} ay The y component of the rotation axis.
    + * @param {number} az The z component of the rotation axis.
    + */
    +goog.vec.Matrix4.makeAxisAngleRotate = function(
    +    mat, angle, ax, ay, az) {
    +  var c = Math.cos(angle);
    +  var d = 1 - c;
    +  var s = Math.sin(angle);
    +
    +  goog.vec.Matrix4.setFromValues(mat,
    +      ax * ax * d + c,
    +      ax * ay * d + az * s,
    +      ax * az * d - ay * s,
    +      0,
    +
    +      ax * ay * d - az * s,
    +      ay * ay * d + c,
    +      ay * az * d + ax * s,
    +      0,
    +
    +      ax * az * d + ay * s,
    +      ay * az * d - ax * s,
    +      az * az * d + c,
    +      0,
    +
    +      0, 0, 0, 1);
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as a perspective projection matrix.
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + */
    +goog.vec.Matrix4.makeFrustum = function(
    +    mat, left, right, bottom, top, near, far) {
    +  var x = (2 * near) / (right - left);
    +  var y = (2 * near) / (top - bottom);
    +  var a = (right + left) / (right - left);
    +  var b = (top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +  var d = -(2 * far * near) / (far - near);
    +
    +  goog.vec.Matrix4.setFromValues(mat,
    +      x, 0, 0, 0,
    +      0, y, 0, 0,
    +      a, b, c, -1,
    +      0, 0, d, 0
    +  );
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as a perspective projection matrix given a
    + * field of view and aspect ratio.
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} fovy The field of view along the y (vertical) axis in
    + *     radians.
    + * @param {number} aspect The x (width) to y (height) aspect ratio.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + */
    +goog.vec.Matrix4.makePerspective = function(
    +    mat, fovy, aspect, near, far) {
    +  var angle = fovy / 2;
    +  var dz = far - near;
    +  var sinAngle = Math.sin(angle);
    +  if (dz == 0 || sinAngle == 0 || aspect == 0) return;
    +
    +  var cot = Math.cos(angle) / sinAngle;
    +  goog.vec.Matrix4.setFromValues(mat,
    +      cot / aspect, 0, 0, 0,
    +      0, cot, 0, 0,
    +      0, 0, -(far + near) / dz, -1,
    +      0, 0, -(2 * near * far) / dz, 0
    +  );
    +};
    +
    +
    +/**
    + * Initializes the given 4x4 matrix as an orthographic projection matrix.
    + * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
    + *     array to receive the new translation matrix.
    + * @param {number} left The coordinate of the left clipping plane.
    + * @param {number} right The coordinate of the right clipping plane.
    + * @param {number} bottom The coordinate of the bottom clipping plane.
    + * @param {number} top The coordinate of the top clipping plane.
    + * @param {number} near The distance to the near clipping plane.
    + * @param {number} far The distance to the far clipping plane.
    + */
    +goog.vec.Matrix4.makeOrtho = function(
    +    mat, left, right, bottom, top, near, far) {
    +  var x = 2 / (right - left);
    +  var y = 2 / (top - bottom);
    +  var z = -2 / (far - near);
    +  var a = -(right + left) / (right - left);
    +  var b = -(top + bottom) / (top - bottom);
    +  var c = -(far + near) / (far - near);
    +
    +  goog.vec.Matrix4.setFromValues(mat,
    +      x, 0, 0, 0,
    +      0, y, 0, 0,
    +      0, 0, z, 0,
    +      a, b, c, 1
    +  );
    +};
    +
    +
    +/**
    + * Updates a matrix representing the modelview matrix of a camera so that
    + * the camera is 'looking at' the given center point.
    + * @param {goog.vec.ArrayType} viewMatrix The matrix.
    + * @param {goog.vec.ArrayType} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.ArrayType} centerPt The point to aim the camera
    + *     at.
    + * @param {goog.vec.ArrayType} worldUpVec The vector that
    + *     identifies the up direction for the camera.
    + */
    +goog.vec.Matrix4.lookAt = function(
    +    viewMatrix, eyePt, centerPt, worldUpVec) {
    +  // Compute the direction vector from the eye point to the center point and
    +  // normalize.
    +  var fwdVec = goog.vec.Matrix4.tmpVec4_[0];
    +  goog.vec.Vec3.subtract(centerPt, eyePt, fwdVec);
    +  goog.vec.Vec3.normalize(fwdVec, fwdVec);
    +  fwdVec[3] = 0;
    +
    +  // Compute the side vector from the forward vector and the input up vector.
    +  var sideVec = goog.vec.Matrix4.tmpVec4_[1];
    +  goog.vec.Vec3.cross(fwdVec, worldUpVec, sideVec);
    +  goog.vec.Vec3.normalize(sideVec, sideVec);
    +  sideVec[3] = 0;
    +
    +  // Now the up vector to form the orthonormal basis.
    +  var upVec = goog.vec.Matrix4.tmpVec4_[2];
    +  goog.vec.Vec3.cross(sideVec, fwdVec, upVec);
    +  goog.vec.Vec3.normalize(upVec, upVec);
    +  upVec[3] = 0;
    +
    +  // Update the view matrix with the new orthonormal basis and position the
    +  // camera at the given eye point.
    +  goog.vec.Vec3.negate(fwdVec, fwdVec);
    +  goog.vec.Matrix4.setRow(viewMatrix, 0, sideVec);
    +  goog.vec.Matrix4.setRow(viewMatrix, 1, upVec);
    +  goog.vec.Matrix4.setRow(viewMatrix, 2, fwdVec);
    +  goog.vec.Matrix4.setRowValues(viewMatrix, 3, 0, 0, 0, 1);
    +  goog.vec.Matrix4.applyTranslate(
    +      viewMatrix, -eyePt[0], -eyePt[1], -eyePt[2]);
    +};
    +
    +
    +/**
    + * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
    + * The matrix represents the modelview matrix of a camera. It is the inverse
    + * of lookAt except for the output of the fwdVec instead of centerPt.
    + * The centerPt itself cannot be recovered from a modelview matrix.
    + * @param {goog.vec.ArrayType} viewMatrix The matrix.
    + * @param {goog.vec.ArrayType} eyePt The position of the eye point
    + *     (camera origin).
    + * @param {goog.vec.ArrayType} fwdVec The vector describing where
    + *     the camera points to.
    + * @param {goog.vec.ArrayType} worldUpVec The vector that
    + *     identifies the up direction for the camera.
    + * @return {boolean} True if the method succeeds, false otherwise.
    + *     The method can only fail if the inverse of viewMatrix is not defined.
    + */
    +goog.vec.Matrix4.toLookAt = function(
    +    viewMatrix, eyePt, fwdVec, worldUpVec) {
    +  // Get eye of the camera.
    +  var viewMatrixInverse = goog.vec.Matrix4.tmpMatrix4_[0];
    +  if (!goog.vec.Matrix4.invert(viewMatrix, viewMatrixInverse)) {
    +    // The input matrix does not have a valid inverse.
    +    return false;
    +  }
    +
    +  if (eyePt) {
    +    eyePt[0] = viewMatrixInverse[12];
    +    eyePt[1] = viewMatrixInverse[13];
    +    eyePt[2] = viewMatrixInverse[14];
    +  }
    +
    +  // Get forward vector from the definition of lookAt.
    +  if (fwdVec || worldUpVec) {
    +    if (!fwdVec) {
    +      fwdVec = goog.vec.Matrix4.tmpVec3_[0];
    +    }
    +    fwdVec[0] = -viewMatrix[2];
    +    fwdVec[1] = -viewMatrix[6];
    +    fwdVec[2] = -viewMatrix[10];
    +    // Normalize forward vector.
    +    goog.vec.Vec3.normalize(fwdVec, fwdVec);
    +  }
    +
    +  if (worldUpVec) {
    +    // Get side vector from the definition of gluLookAt.
    +    var side = goog.vec.Matrix4.tmpVec3_[1];
    +    side[0] = viewMatrix[0];
    +    side[1] = viewMatrix[4];
    +    side[2] = viewMatrix[8];
    +    // Compute up vector as a up = side x forward.
    +    goog.vec.Vec3.cross(side, fwdVec, worldUpVec);
    +    // Normalize up vector.
    +    goog.vec.Vec3.normalize(worldUpVec, worldUpVec);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Constructs a rotation matrix from its Euler angles using the ZXZ convention.
    + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
    + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
    + * where rotation_x(theta) means rotation around the X axis of theta radians.
    + * @param {goog.vec.ArrayType} matrix The rotation matrix.
    + * @param {number} theta1 The angle of rotation around the Z axis in radians.
    + * @param {number} theta2 The angle of rotation around the X axis in radians.
    + * @param {number} theta3 The angle of rotation around the Z axis in radians.
    + */
    +goog.vec.Matrix4.fromEulerZXZ = function(
    +    matrix, theta1, theta2, theta3) {
    +  var c1 = Math.cos(theta1);
    +  var s1 = Math.sin(theta1);
    +
    +  var c2 = Math.cos(theta2);
    +  var s2 = Math.sin(theta2);
    +
    +  var c3 = Math.cos(theta3);
    +  var s3 = Math.sin(theta3);
    +
    +  matrix[0] = c1 * c3 - c2 * s1 * s3;
    +  matrix[1] = c2 * c1 * s3 + c3 * s1;
    +  matrix[2] = s3 * s2;
    +  matrix[3] = 0;
    +
    +  matrix[4] = -c1 * s3 - c3 * c2 * s1;
    +  matrix[5] = c1 * c2 * c3 - s1 * s3;
    +  matrix[6] = c3 * s2;
    +  matrix[7] = 0;
    +
    +  matrix[8] = s2 * s1;
    +  matrix[9] = -c1 * s2;
    +  matrix[10] = c2;
    +  matrix[11] = 0;
    +
    +  matrix[12] = 0;
    +  matrix[13] = 0;
    +  matrix[14] = 0;
    +  matrix[15] = 1;
    +};
    +
    +
    +/**
    + * Decomposes a rotation matrix into Euler angles using the ZXZ convention.
    + * @param {goog.vec.ArrayType} matrix The rotation matrix.
    + * @param {goog.vec.ArrayType} euler The ZXZ Euler angles in
    + *     radians. euler = [roll, tilt, pan].
    + */
    +goog.vec.Matrix4.toEulerZXZ = function(matrix, euler) {
    +  var s2 = Math.sqrt(matrix[2] * matrix[2] + matrix[6] * matrix[6]);
    +
    +  // There is an ambiguity in the sign of s2. We assume the tilt value
    +  // is between [-pi/2, 0], so s2 is always negative.
    +  if (s2 > goog.vec.EPSILON) {
    +    euler[2] = Math.atan2(-matrix[2], -matrix[6]);
    +    euler[1] = Math.atan2(-s2, matrix[10]);
    +    euler[0] = Math.atan2(-matrix[8], matrix[9]);
    +  } else {
    +    // There is also an arbitrary choice for roll = 0 or pan = 0 in this case.
    +    // We assume roll = 0 as some applications do not allow the camera to roll.
    +    euler[0] = 0;
    +    euler[1] = Math.atan2(-s2, matrix[10]);
    +    euler[2] = Math.atan2(matrix[1], matrix[0]);
    +  }
    +};
    +
    +
    +/**
    + * Applies a translation by x,y,z to the given matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix.
    + * @param {number} x The translation along the x axis.
    + * @param {number} y The translation along the y axis.
    + * @param {number} z The translation along the z axis.
    + */
    +goog.vec.Matrix4.applyTranslate = function(mat, x, y, z) {
    +  goog.vec.Matrix4.setColumnValues(
    +      mat, 3,
    +      mat[0] * x + mat[4] * y + mat[8] * z + mat[12],
    +      mat[1] * x + mat[5] * y + mat[9] * z + mat[13],
    +      mat[2] * x + mat[6] * y + mat[10] * z + mat[14],
    +      mat[3] * x + mat[7] * y + mat[11] * z + mat[15]);
    +};
    +
    +
    +/**
    + * Applies an x,y,z scale to the given matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix.
    + * @param {number} x The x scale factor.
    + * @param {number} y The y scale factor.
    + * @param {number} z The z scale factor.
    + */
    +goog.vec.Matrix4.applyScale = function(mat, x, y, z) {
    +  goog.vec.Matrix4.setFromValues(
    +      mat,
    +      mat[0] * x, mat[1] * x, mat[2] * x, mat[3] * x,
    +      mat[4] * y, mat[5] * y, mat[6] * y, mat[7] * y,
    +      mat[8] * z, mat[9] * z, mat[10] * z, mat[11] * z,
    +      mat[12], mat[13], mat[14], mat[15]);
    +};
    +
    +
    +/**
    + * Applies a rotation by angle about the x,y,z axis to the given matrix.
    + *
    + * @param {goog.vec.ArrayType} mat The matrix.
    + * @param {number} angle The angle in radians.
    + * @param {number} x The x component of the rotation axis.
    + * @param {number} y The y component of the rotation axis.
    + * @param {number} z The z component of the rotation axis.
    + */
    +goog.vec.Matrix4.applyRotate = function(mat, angle, x, y, z) {
    +  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
    +  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
    +  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
    +  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
    +
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var diffCosAngle = 1 - cosAngle;
    +  var r00 = x * x * diffCosAngle + cosAngle;
    +  var r10 = x * y * diffCosAngle + z * sinAngle;
    +  var r20 = x * z * diffCosAngle - y * sinAngle;
    +
    +  var r01 = x * y * diffCosAngle - z * sinAngle;
    +  var r11 = y * y * diffCosAngle + cosAngle;
    +  var r21 = y * z * diffCosAngle + x * sinAngle;
    +
    +  var r02 = x * z * diffCosAngle + y * sinAngle;
    +  var r12 = y * z * diffCosAngle - x * sinAngle;
    +  var r22 = z * z * diffCosAngle + cosAngle;
    +
    +  goog.vec.Matrix4.setFromValues(
    +      mat,
    +      m00 * r00 + m01 * r10 + m02 * r20,
    +      m10 * r00 + m11 * r10 + m12 * r20,
    +      m20 * r00 + m21 * r10 + m22 * r20,
    +      m30 * r00 + m31 * r10 + m32 * r20,
    +
    +      m00 * r01 + m01 * r11 + m02 * r21,
    +      m10 * r01 + m11 * r11 + m12 * r21,
    +      m20 * r01 + m21 * r11 + m22 * r21,
    +      m30 * r01 + m31 * r11 + m32 * r21,
    +
    +      m00 * r02 + m01 * r12 + m02 * r22,
    +      m10 * r02 + m11 * r12 + m12 * r22,
    +      m20 * r02 + m21 * r12 + m22 * r22,
    +      m30 * r02 + m31 * r12 + m32 * r22,
    +
    +      m03, m13, m23, m33);
    +};
    +
    +
    +/**
    + * @type {!Array<!goog.vec.Vec3.Type>}
    + * @private
    + */
    +goog.vec.Matrix4.tmpVec3_ = [
    +  goog.vec.Vec3.createNumber(),
    +  goog.vec.Vec3.createNumber()
    +];
    +
    +
    +/**
    + * @type {!Array<!goog.vec.Vec4.Type>}
    + * @private
    + */
    +goog.vec.Matrix4.tmpVec4_ = [
    +  goog.vec.Vec4.createNumber(),
    +  goog.vec.Vec4.createNumber(),
    +  goog.vec.Vec4.createNumber()
    +];
    +
    +
    +/**
    + * @type {Array<goog.vec.Matrix4.Type>}
    + * @private
    + */
    +goog.vec.Matrix4.tmpMatrix4_ = [
    +  goog.vec.Matrix4.create()
    +];
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/matrix4_test.html b/mapbox-gl/closure-library/closure/goog/vec/matrix4_test.html
    new file mode 100644
    index 0000000000..498a2f8739
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/matrix4_test.html
    @@ -0,0 +1,626 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Matrix4</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Matrix4');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructor() {
    +    var m0 = goog.vec.Matrix4.create();
    +    assertElementsEquals([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +
    +    var m1 = goog.vec.Matrix4.createFromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +
    +    var m2 = goog.vec.Matrix4.clone(m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m2);
    +
    +    var m3 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m3);
    +
    +    var m4 = goog.vec.Matrix4.createIdentity();
    +    assertElementsEquals([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m4);
    +  }
    +
    +  function testSet() {
    +    var m0 = goog.vec.Matrix4.create();
    +    var m1 = goog.vec.Matrix4.createFromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    goog.vec.Matrix4.setFromArray(m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    goog.vec.Matrix4.setFromValues(
    +        m0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], m0);
    +  }
    +
    +  function testSetDiagonal() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setDiagonalValues(m0, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], m0);
    +
    +    goog.vec.Matrix4.setDiagonal(m0, [4, 5, 6, 7]);
    +    assertElementsEquals(
    +        [4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 7], m0);
    +  }
    +
    +  function testSetGetColumn() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setColumn(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.Matrix4.setColumn(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.Matrix4.setColumn(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.Matrix4.setColumn(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.Matrix4.getColumn(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.Matrix4.getColumn(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.Matrix4.getColumn(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.Matrix4.getColumn(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetColumns() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setColumns(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.Matrix4.getColumns(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetGetRow() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setRow(m0, 0, [1, 2, 3, 4]);
    +    goog.vec.Matrix4.setRow(m0, 1, [5, 6, 7, 8]);
    +    goog.vec.Matrix4.setRow(m0, 2, [9, 10, 11, 12]);
    +    goog.vec.Matrix4.setRow(m0, 3, [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0];
    +    goog.vec.Matrix4.getRow(m0, 0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    goog.vec.Matrix4.getRow(m0, 1, v0);
    +    assertElementsEquals([5, 6, 7, 8], v0);
    +    goog.vec.Matrix4.getRow(m0, 2, v0);
    +    assertElementsEquals([9, 10, 11, 12], v0);
    +    goog.vec.Matrix4.getRow(m0, 3, v0);
    +    assertElementsEquals([13, 14, 15, 16], v0);
    +  }
    +
    +  function testSetGetRows() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setRows(
    +        m0, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +
    +    var v0 = [0, 0, 0, 0], v1 = [0, 0, 0, 0];
    +    var v2 = [0, 0, 0, 0], v3 = [0, 0, 0, 0];
    +    goog.vec.Matrix4.getRows(m0, v0, v1, v2, v3);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([9, 10, 11, 12], v2);
    +    assertElementsEquals([13, 14, 15, 16], v3);
    +  }
    +
    +  function testSetRowMajorArray() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setFromRowMajorArray(
    +        m0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m0);
    +  }
    +
    +  function testSetZero() {
    +    var m0 = goog.vec.Matrix4.createFromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    goog.vec.Matrix4.setZero(m0);
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], m0);
    +  }
    +
    +  function testSetIdentity() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setIdentity(m0);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testSetGetElement() {
    +    var m0 = goog.vec.Matrix4.create();
    +    for (var r = 0; r < 4; r++) {
    +      for (var c = 0; c < 4; c++) {
    +        var value = c * 4 + r + 1;
    +        goog.vec.Matrix4.setElement(m0, r, c, value);
    +        assertEquals(value, goog.vec.Matrix4.getElement(m0, r, c));
    +      }
    +    }
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +  }
    +
    +  function testAdd() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.createFromValues(
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.add(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m2);
    +
    +    goog.vec.Matrix4.add(m0, m1, m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [10, 12, 14, 16, 18, 20, 22, 24, 10, 12, 14, 16, 18, 20, 22, 24], m0);
    +  }
    +
    +  function testSubtract() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.createFromValues(
    +        9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8);
    +    var m2 = goog.vec.Matrix4.create();
    +
    +    goog.vec.Matrix4.subtract(m0, m1, m2);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8], m1);
    +    assertElementsEquals(
    +        [-8, -8, -8, -8, -8, -8, -8, -8, 8, 8, 8, 8, 8, 8, 8, 8], m2);
    +
    +    goog.vec.Matrix4.subtract(m1, m0, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [8, 8, 8, 8, 8, 8, 8, 8, -8, -8, -8, -8, -8, -8, -8, -8], m1);
    +  }
    +
    +  function testScale() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.create();
    +
    +    goog.vec.Matrix4.scale(m0, 2, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m0);
    +    assertElementsEquals(
    +        [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32], m1);
    +
    +    goog.vec.Matrix4.scale(m0, 5, m0);
    +    assertElementsEquals(
    +        [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80], m0);
    +  }
    +
    +  function testMultMat() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m2 = goog.vec.Matrix4.create();
    +
    +    goog.vec.Matrix4.multMat(m0, m1, m2);
    +    assertElementsEquals(
    +        [90, 100, 110, 120, 202, 228, 254, 280,
    +         314, 356, 398, 440, 426, 484, 542, 600], m2);
    +
    +    goog.vec.Matrix4.scale(m1, 2, m1);
    +    goog.vec.Matrix4.multMat(m1, m0, m1);
    +    assertElementsEquals(
    +        [180, 200, 220, 240, 404, 456, 508, 560,
    +         628, 712, 796, 880, 852, 968, 1084, 1200], m1);
    +  }
    +
    +  function testTranspose() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.transpose(m0, m1);
    +    assertElementsEquals(
    +        [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16], m1);
    +
    +    goog.vec.Matrix4.transpose(m1, m1);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], m1);
    +  }
    +
    +  function testDeterminant() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertEquals(0, goog.vec.Matrix4.determinant(m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Matrix4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertEquals(160, goog.vec.Matrix4.determinant(m0));
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3], m0);
    +  }
    +
    +  function testInvert() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
    +    assertFalse(goog.vec.Matrix4.invert(m0, m0));
    +    assertElementsEquals(
    +        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], m0);
    +
    +    goog.vec.Matrix4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +    assertTrue(goog.vec.Matrix4.invert(m0, m0));
    +    assertElementsRoughlyEqual(
    +        [-0.225, 0.025, 0.025, 0.275, 0.025, 0.025, 0.275, -0.225,
    +         0.025, 0.275, -0.225, 0.025, 0.275, -0.225, 0.025, 0.025], m0,
    +         goog.vec.EPSILON);
    +
    +    goog.vec.Matrix4.makeScale(m0, .01, .01, .01);
    +    assertTrue(goog.vec.Matrix4.invert(m0, m0));
    +    var m1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeScale(m1, 100, 100, 100);
    +    assertElementsEquals(m1, m0);
    +  }
    +
    +  function testEquals() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var m1 = goog.vec.Matrix4.clone(m0);
    +    assertTrue(goog.vec.Matrix4.equals(m0, m1));
    +    assertTrue(goog.vec.Matrix4.equals(m1, m0));
    +    for (var i = 0; i < 16; i++) {
    +      m1[i] = 18;
    +      assertFalse(goog.vec.Matrix4.equals(m0, m1));
    +      assertFalse(goog.vec.Matrix4.equals(m1, m0));
    +      m1[i] = i + 1;
    +    }
    +  }
    +
    +  function testMultVec3() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Matrix4.multVec3(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([51, 58, 65], v1);
    +
    +    goog.vec.Matrix4.multVec3(m0, v0, v0);
    +    assertElementsEquals([51, 58, 65], v0);
    +
    +    v0 = [1, 2, 3];
    +    goog.vec.Matrix4.multVec3ToArray(m0, v0, v0);
    +    assertElementsEquals([51, 58, 65], v0);
    +  }
    +
    +  function testMultVec3NoTranslate() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +
    +    goog.vec.Matrix4.multVec3NoTranslate(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([38, 44, 50], v1);
    +
    +    goog.vec.Matrix4.multVec3NoTranslate(m0, v0, v0);
    +    assertElementsEquals([38, 44, 50], v0);
    +  }
    +
    +  function testMultVec3Projective() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3];
    +    var v1 = [0, 0, 0];
    +    var invw = 1 / 72;
    +
    +    goog.vec.Matrix4.multVec3Projective(m0, v0, v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v1);
    +
    +    goog.vec.Matrix4.multVec3Projective(m0, v0, v0);
    +    assertElementsEquals(
    +        [51 * invw, 58 * invw, 65 * invw], v0);
    +  }
    +
    +  function testMultVec4() {
    +    var m0 = goog.vec.Matrix4.createFromValues(
    +        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    +    var v0 = [1, 2, 3, 4];
    +    var v1 = [0, 0, 0, 0];
    +
    +    goog.vec.Matrix4.multVec4(m0, v0, v1);
    +    assertElementsEquals([90, 100, 110, 120], v1);
    +    goog.vec.Matrix4.multVec4(m0, v0, v0);
    +    assertElementsEquals([90, 100, 110, 120], v0);
    +
    +    var v0 = [1, 2, 3, 4];
    +    goog.vec.Matrix4.multVec4ToArray(m0, v0, v0);
    +    assertElementsEquals([90, 100, 110, 120], v0);
    +  }
    +
    +  function testSetValues() {
    +    var a0 = goog.vec.Matrix4.create();
    +    assertElementsEquals(
    +        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], a0);
    +    a0 = goog.vec.Matrix4.createFromArray(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a0);
    +
    +    var a1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.setDiagonalValues(a1, 1, 2, 3, 4);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4], a1);
    +
    +    goog.vec.Matrix4.setColumnValues(a1, 0, 2, 3, 4, 5);
    +    goog.vec.Matrix4.setColumnValues(a1, 1, 6, 7, 8, 9);
    +    goog.vec.Matrix4.setColumnValues(a1, 2, 10, 11, 12, 13);
    +    goog.vec.Matrix4.setColumnValues(a1, 3, 14, 15, 16, 1);
    +    assertElementsEquals(
    +        [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1], a1);
    +
    +    goog.vec.Matrix4.setRowValues(a1, 0, 1, 5, 9, 13);
    +    goog.vec.Matrix4.setRowValues(a1, 1, 2, 6, 10, 14);
    +    goog.vec.Matrix4.setRowValues(a1, 2, 3, 7, 11, 15);
    +    goog.vec.Matrix4.setRowValues(a1, 3, 4, 8, 12, 16);
    +    assertElementsEquals(
    +        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], a1);
    +  }
    +
    +  function testMakeTranslate() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeTranslate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +  }
    +
    +  function testMakeScale() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeScale(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testMakeRotate() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeAxisAngleRotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    var m1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeAxisAngleRotate(m1, -Math.PI / 4, 0, 0, 1);
    +    goog.vec.Matrix4.multMat(m0, m1, m1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m1, goog.vec.EPSILON);
    +  }
    +
    +  function testApplyTranslate() {
    +    var m0 = goog.vec.Matrix4.createIdentity();
    +    goog.vec.Matrix4.applyTranslate(m0, 3, 4, 5);
    +    assertElementsEquals(
    +        [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 4, 5, 1], m0);
    +
    +    goog.vec.Matrix4.setFromValues(
    +        m0, 1, 2, 3, 4, 2, 3, 4, 1, 3, 4, 1, 2, 4, 1, 2, 3);
    +
    +    var m1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeTranslate(m1, 5, 6, 7);
    +    var m2 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.multMat(m0, m1, m2);
    +    goog.vec.Matrix4.applyTranslate(m0, 5, 6, 7);
    +    assertElementsEquals(m2, m0);
    +  }
    +
    +  function testApplyScale() {
    +    var m0 = goog.vec.Matrix4.createIdentity();
    +    goog.vec.Matrix4.applyScale(m0, 3, 4, 5);
    +    assertElementsEquals([3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1], m0);
    +  }
    +
    +  function testApplyRotate() {
    +    var m0 = goog.vec.Matrix4.createIdentity();
    +    goog.vec.Matrix4.applyRotate(m0, Math.PI / 2, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +
    +    goog.vec.Matrix4.applyRotate(m0, -Math.PI / 4, 0, 0, 1);
    +    assertElementsRoughlyEqual(
    +        [0.7071068, 0.7071068, 0, 0,
    +         -0.7071068, 0.7071068, 0, 0,
    +         0, 0, 1, 0,
    +         0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeFrustum() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeFrustum(m0, -1, 2, -2, 1, .1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.06666666, 0, 0, 0,
    +         0, 0.06666666, 0, 0,
    +         0.33333333, -0.33333333, -1.2, -1,
    +         0, 0, -0.22, 0], m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakePerspective() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makePerspective(m0, 90 * Math.PI / 180, 2, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1.2, -1, 0, 0, -0.22, 0],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testMakeOrtho() {
    +    var m0 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.makeOrtho(m0, -1, 2, -2, 1, 0.1, 1.1);
    +    assertElementsRoughlyEqual(
    +        [0.6666666, 0, 0, 0,
    +         0, 0.6666666, 0, 0,
    +         0, 0, -2, 0,
    +         -0.333333, 0.3333333, -1.2, 1], m0, goog.vec.EPSILON);
    +
    +  }
    +
    +  function testEulerZXZ() {
    +    var m0 = goog.vec.Matrix4.create();
    +    var roll = Math.random() * 2 * Math.PI;
    +    var tilt = -Math.random() * Math.PI / 2;
    +    var pan = Math.random() * 2 * Math.PI;
    +
    +    goog.vec.Matrix4.makeAxisAngleRotate(m0, roll, 0, 0, 1);
    +    goog.vec.Matrix4.applyRotate(m0, tilt, 1, 0, 0);
    +    goog.vec.Matrix4.applyRotate(m0, pan, 0, 0, 1);
    +
    +    var m1 = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.fromEulerZXZ(m1, roll, tilt, pan);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Matrix4.toEulerZXZ(m0, euler);
    +    // Bring them to their range.
    +    euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
    +    euler[1] = (euler[1] - Math.PI * 2) % (Math.PI * 2);
    +    euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
    +    assertElementsRoughlyEqual([roll, tilt, pan], euler, goog.vec.EPSILON);
    +  }
    +
    +  function testEulerZXZExtrema() {
    +    var m0 = goog.vec.Matrix4.createFromArray(
    +    [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1]);
    +    var m1 = goog.vec.Matrix4.createFromArray(
    +    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    +
    +    var euler = [0, 0, 0];
    +    goog.vec.Matrix4.toEulerZXZ(m0, euler);
    +    assertElementsRoughlyEqual([0, -Math.PI / 2, 0], euler, goog.vec.EPSILON);
    +    goog.vec.Matrix4.fromEulerZXZ(m1, euler[0], euler[1], euler[2]);
    +    assertElementsRoughlyEqual(m0, m1, goog.vec.EPSILON);
    +  }
    +
    +  function testLookAt() {
    +    var viewMatrix = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.lookAt(
    +      viewMatrix, [0, 0, 0], [1, 0, 0], [0, 1, 0]);
    +    assertElementsRoughlyEqual(
    +      [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], viewMatrix,
    +      goog.vec.EPSILON);
    +  }
    +
    +  function testToLookAt() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers leading to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [1, 0, 0];
    +    var upExp = [0, 1, 0];
    +
    +    var centerExp = [0, 0, 0];
    +    goog.vec.Vec3.add(eyeExp, fwdExp, centerExp);
    +
    +    var view = goog.vec.Matrix4.create();
    +    goog.vec.Matrix4.lookAt(view, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    goog.vec.Matrix4.toLookAt(view, eyeRes, fwdRes, upRes);
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +  }
    +
    +  function testLookAtDecomposition() {
    +    // This test does not use the default precision goog.vec.EPSILON due to
    +    // precision issues in some browsers, which leads to flaky tests.
    +    var EPSILON = 1e-4;
    +
    +    var viewExp = goog.vec.Matrix4.create();
    +    var viewRes = goog.vec.Matrix4.create();
    +    var tmp = goog.vec.Matrix4.create();
    +
    +    // Get a valid set of random vectors eye, forward, up by decomposing
    +    // a random matrix into a set of lookAt vectors.
    +    for (var i = 0; i < tmp.length; i++)
    +      tmp[i] = Math.random();
    +    var eyeExp = [0, 0, 0];
    +    var fwdExp = [0, 0, 0];
    +    var upExp = [0, 0, 0];
    +    var centerExp = [0, 0, 0];
    +    // Project the random matrix into a real modelview matrix.
    +    goog.vec.Matrix4.toLookAt(tmp, eyeExp, fwdExp, upExp);
    +    goog.vec.Vec3.add(eyeExp, fwdExp, centerExp);
    +
    +    // Compute the expected modelview matrix from a set of valid random vectors.
    +    goog.vec.Matrix4.lookAt(viewExp, eyeExp, centerExp, upExp);
    +
    +    var eyeRes = [0, 0, 0];
    +    var fwdRes = [0, 0, 0];
    +    var upRes = [0, 0, 0];
    +    var centerRes = [0, 0, 0];
    +    goog.vec.Matrix4.toLookAt(viewExp, eyeRes, fwdRes, upRes);
    +    goog.vec.Vec3.add(eyeRes, fwdRes, centerRes);
    +
    +    goog.vec.Matrix4.lookAt(viewRes, eyeRes, centerRes, upRes);
    +
    +    assertElementsRoughlyEqual(eyeExp, eyeRes, EPSILON);
    +    assertElementsRoughlyEqual(fwdExp, fwdRes, EPSILON);
    +    assertElementsRoughlyEqual(upExp, upRes, EPSILON);
    +    assertElementsRoughlyEqual(viewExp, viewRes, EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/quaternion.js b/mapbox-gl/closure-library/closure/goog/vec/quaternion.js
    new file mode 100644
    index 0000000000..35d40b937e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/quaternion.js
    @@ -0,0 +1,458 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Implements quaternions and their conversion functions. In this
    + * implementation, quaternions are represented as 4 element vectors with the
    + * first 3 elements holding the imaginary components and the 4th element holding
    + * the real component.
    + *
    + */
    +goog.provide('goog.vec.Quaternion');
    +
    +goog.require('goog.vec');
    +goog.require('goog.vec.Vec3');
    +goog.require('goog.vec.Vec4');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Quaternion.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Quaternion.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Quaternion.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Quaternion.AnyType;
    +
    +
    +/**
    + * Creates a Float32 quaternion, initialized to zero.
    + *
    + * @return {!goog.vec.Quaternion.Float32} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat32 = goog.vec.Vec4.createFloat32;
    +
    +
    +/**
    + * Creates a Float64 quaternion, initialized to zero.
    + *
    + * @return {goog.vec.Quaternion.Float64} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat64 = goog.vec.Vec4.createFloat64;
    +
    +
    +/**
    + * Creates a Number quaternion, initialized to zero.
    + *
    + * @return {goog.vec.Quaternion.Number} The new quaternion.
    + */
    +goog.vec.Quaternion.createNumber = goog.vec.Vec4.createNumber;
    +
    +
    +/**
    + * Creates a new Float32 quaternion initialized with the values from the
    + * supplied array.
    + *
    + * @param {goog.vec.AnyType} vec The source 4 element array.
    + * @return {!goog.vec.Quaternion.Float32} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat32FromArray =
    +    goog.vec.Vec4.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a new Float64 quaternion initialized with the values from the
    + * supplied array.
    + *
    + * @param {goog.vec.AnyType} vec The source 4 element array.
    + * @return {!goog.vec.Quaternion.Float64} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat64FromArray =
    +    goog.vec.Vec4.createFloat64FromArray;
    +
    +
    +/**
    + * Creates a new Float32 quaternion initialized with the supplied values.
    + *
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Quaternion.Float32} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat32FromValues =
    +    goog.vec.Vec4.createFloat32FromValues;
    +
    +
    +/**
    + * Creates a new Float64 quaternion initialized with the supplied values.
    + *
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Quaternion.Float64} The new quaternion.
    + */
    +goog.vec.Quaternion.createFloat64FromValues =
    +    goog.vec.Vec4.createFloat64FromValues;
    +
    +
    +/**
    + * Creates a clone of the given Float32 quaternion.
    + *
    + * @param {goog.vec.Quaternion.Float32} q The source quaternion.
    + * @return {goog.vec.Quaternion.Float32} The new quaternion.
    + */
    +goog.vec.Quaternion.cloneFloat32 = goog.vec.Vec4.cloneFloat32;
    +
    +
    +/**
    + * Creates a clone of the given Float64 quaternion.
    + *
    + * @param {goog.vec.Quaternion.Float64} q The source quaternion.
    + * @return {goog.vec.Quaternion.Float64} The new quaternion.
    + */
    +goog.vec.Quaternion.cloneFloat64 = goog.vec.Vec4.cloneFloat64;
    +
    +
    +/**
    + * Initializes the quaternion with the given values.
    + *
    + * @param {goog.vec.Quaternion.AnyType} q The quaternion to receive
    + *     the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Vec4.AnyType} return q so that operations can be
    + *     chained together.
    + */
    +goog.vec.Quaternion.setFromValues = goog.vec.Vec4.setFromValues;
    +
    +
    +/**
    + * Initializes the quaternion with the given array of values.
    + *
    + * @param {goog.vec.Quaternion.AnyType} q The quaternion to receive
    + *     the values.
    + * @param {goog.vec.AnyType} values The array of values.
    + * @return {!goog.vec.Quaternion.AnyType} return q so that operations can be
    + *     chained together.
    + */
    +goog.vec.Quaternion.setFromArray = goog.vec.Vec4.setFromArray;
    +
    +
    +/**
    + * Adds the two quaternions.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The first addend.
    + * @param {goog.vec.Quaternion.AnyType} quat1 The second addend.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result. May be quat0 or quat1.
    + */
    +goog.vec.Quaternion.add = goog.vec.Vec4.add;
    +
    +
    +/**
    + * Negates a quaternion, storing the result into resultQuat.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion to negate.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result. May be quat0.
    + */
    +goog.vec.Quaternion.negate = goog.vec.Vec4.negate;
    +
    +
    +/**
    + * Multiplies each component of quat0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The source quaternion.
    + * @param {number} scalar The value to multiply with each component of quat0.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result. May be quat0.
    + */
    +goog.vec.Quaternion.scale = goog.vec.Vec4.scale;
    +
    +
    +/**
    + * Returns the square magnitude of the given quaternion.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion.
    + * @return {number} The magnitude of the quaternion.
    + */
    +goog.vec.Quaternion.magnitudeSquared =
    +    goog.vec.Vec4.magnitudeSquared;
    +
    +
    +/**
    + * Returns the magnitude of the given quaternion.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion.
    + * @return {number} The magnitude of the quaternion.
    + */
    +goog.vec.Quaternion.magnitude =
    +    goog.vec.Vec4.magnitude;
    +
    +
    +/**
    + * Normalizes the given quaternion storing the result into resultVec.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion to
    + *     normalize.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result. May be quat0.
    + */
    +goog.vec.Quaternion.normalize = goog.vec.Vec4.normalize;
    +
    +
    +/**
    + * Computes the dot (scalar) product of two quaternions.
    + *
    + * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
    + * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
    + * @return {number} The scalar product.
    + */
    +goog.vec.Quaternion.dot = goog.vec.Vec4.dot;
    +
    +
    +/**
    + * Computes the conjugate of the quaternion in quat storing the result into
    + * resultQuat.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat The source quaternion.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result.
    + * @return {!goog.vec.Quaternion.AnyType} Return q so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.conjugate = function(quat, resultQuat) {
    +  resultQuat[0] = -quat[0];
    +  resultQuat[1] = -quat[1];
    +  resultQuat[2] = -quat[2];
    +  resultQuat[3] = quat[3];
    +  return resultQuat;
    +};
    +
    +
    +/**
    + * Concatenates the two quaternions storing the result into resultQuat.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat0 The first quaternion.
    + * @param {goog.vec.Quaternion.AnyType} quat1 The second quaternion.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result.
    + * @return {!goog.vec.Quaternion.AnyType} Return q so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.concat = function(quat0, quat1, resultQuat) {
    +  var x0 = quat0[0], y0 = quat0[1], z0 = quat0[2], w0 = quat0[3];
    +  var x1 = quat1[0], y1 = quat1[1], z1 = quat1[2], w1 = quat1[3];
    +  resultQuat[0] = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1;
    +  resultQuat[1] = w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1;
    +  resultQuat[2] = w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1;
    +  resultQuat[3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
    +  return resultQuat;
    +};
    +
    +
    +/**
    + * Generates a unit quaternion from the given angle-axis rotation pair.
    + * The rotation axis is not required to be a unit vector, but should
    + * have non-zero length.  The angle should be specified in radians.
    + *
    + * @param {number} angle The angle (in radians) to rotate about the axis.
    + * @param {goog.vec.Quaternion.AnyType} axis Unit vector specifying the
    + *     axis of rotation.
    + * @param {goog.vec.Quaternion.AnyType} quat Unit quaternion to store the
    + *     result.
    + * @return {goog.vec.Quaternion.AnyType} Return q so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.fromAngleAxis = function(angle, axis, quat) {
    +  // Normalize the axis of rotation.
    +  goog.vec.Vec3.normalize(axis, axis);
    +
    +  var halfAngle = 0.5 * angle;
    +  var sin = Math.sin(halfAngle);
    +  goog.vec.Quaternion.setFromValues(
    +      quat, sin * axis[0], sin * axis[1], sin * axis[2], Math.cos(halfAngle));
    +
    +  // Normalize the resulting quaternion.
    +  goog.vec.Quaternion.normalize(quat, quat);
    +  return quat;
    +};
    +
    +
    +/**
    + * Generates an angle-axis rotation pair from a unit quaternion.
    + * The quaternion is assumed to be of unit length.  The calculated
    + * values are returned via the passed 'axis' object and the 'angle'
    + * number returned by the function itself. The returned rotation axis
    + * is a non-zero length unit vector, and the returned angle is in
    + * radians in the range of [-PI, +PI].
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat Unit quaternion to convert.
    + * @param {goog.vec.Quaternion.AnyType} axis Vector to store the returned
    + *     rotation axis.
    + * @return {number} angle Angle (in radians) to rotate about 'axis'.
    + *     The range of the returned angle is [-PI, +PI].
    + */
    +goog.vec.Quaternion.toAngleAxis = function(quat, axis) {
    +  var angle = 2 * Math.acos(quat[3]);
    +  var magnitude = Math.min(Math.max(1 - quat[3] * quat[3], 0), 1);
    +  if (magnitude < goog.vec.EPSILON) {
    +    // This is nearly an identity rotation, so just use a fixed +X axis.
    +    goog.vec.Vec3.setFromValues(axis, 1, 0, 0);
    +  } else {
    +    // Compute the proper rotation axis.
    +    goog.vec.Vec3.setFromValues(axis, quat[0], quat[1], quat[2]);
    +    // Make sure the rotation axis is of unit length.
    +    goog.vec.Vec3.normalize(axis, axis);
    +  }
    +  // Adjust the range of the returned angle to [-PI, +PI].
    +  if (angle > Math.PI) {
    +    angle -= 2 * Math.PI;
    +  }
    +  return angle;
    +};
    +
    +
    +/**
    + * Generates the quaternion from the given rotation matrix.
    + *
    + * @param {goog.vec.Quaternion.AnyType} matrix The source matrix.
    + * @param {goog.vec.Quaternion.AnyType} quat The resulting quaternion.
    + * @return {!goog.vec.Quaternion.AnyType} Return q so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.fromRotationMatrix4 = function(matrix, quat) {
    +  var sx = matrix[0], sy = matrix[5], sz = matrix[10];
    +  quat[3] = Math.sqrt(Math.max(0, 1 + sx + sy + sz)) / 2;
    +  quat[0] = Math.sqrt(Math.max(0, 1 + sx - sy - sz)) / 2;
    +  quat[1] = Math.sqrt(Math.max(0, 1 - sx + sy - sz)) / 2;
    +  quat[2] = Math.sqrt(Math.max(0, 1 - sx - sy + sz)) / 2;
    +
    +  quat[0] = (matrix[6] - matrix[9] < 0) != (quat[0] < 0) ? -quat[0] : quat[0];
    +  quat[1] = (matrix[8] - matrix[2] < 0) != (quat[1] < 0) ? -quat[1] : quat[1];
    +  quat[2] = (matrix[1] - matrix[4] < 0) != (quat[2] < 0) ? -quat[2] : quat[2];
    +  return quat;
    +};
    +
    +
    +/**
    + * Generates the rotation matrix from the given quaternion.
    + *
    + * @param {goog.vec.Quaternion.AnyType} quat The source quaternion.
    + * @param {goog.vec.AnyType} matrix The resulting matrix.
    + * @return {!goog.vec.AnyType} Return resulting matrix so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.toRotationMatrix4 = function(quat, matrix) {
    +  var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
    +  var x2 = 2 * x, y2 = 2 * y, z2 = 2 * z;
    +  var wx = x2 * w;
    +  var wy = y2 * w;
    +  var wz = z2 * w;
    +  var xx = x2 * x;
    +  var xy = y2 * x;
    +  var xz = z2 * x;
    +  var yy = y2 * y;
    +  var yz = z2 * y;
    +  var zz = z2 * z;
    +
    +  matrix[0] = 1 - (yy + zz);
    +  matrix[1] = xy + wz;
    +  matrix[2] = xz - wy;
    +  matrix[3] = 0;
    +  matrix[4] = xy - wz;
    +  matrix[5] = 1 - (xx + zz);
    +  matrix[6] = yz + wx;
    +  matrix[7] = 0;
    +  matrix[8] = xz + wy;
    +  matrix[9] = yz - wx;
    +  matrix[10] = 1 - (xx + yy);
    +  matrix[11] = 0;
    +  matrix[12] = 0;
    +  matrix[13] = 0;
    +  matrix[14] = 0;
    +  matrix[15] = 1;
    +  return matrix;
    +};
    +
    +
    +/**
    + * Computes the spherical linear interpolated value from the given quaternions
    + * q0 and q1 according to the coefficient t. The resulting quaternion is stored
    + * in resultQuat.
    + *
    + * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
    + * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
    + * @param {number} t The interpolating coefficient.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the result.
    + * @return {goog.vec.Quaternion.AnyType} Return q so that
    + *     operations can be chained together.
    + */
    +goog.vec.Quaternion.slerp = function(q0, q1, t, resultQuat) {
    +  // Compute the dot product between q0 and q1 (cos of the angle between q0 and
    +  // q1). If it's outside the interval [-1,1], then the arccos is not defined.
    +  // The usual reason for this is that q0 and q1 are colinear. In this case
    +  // the angle between the two is zero, so just return q1.
    +  var cosVal = goog.vec.Quaternion.dot(q0, q1);
    +  if (cosVal > 1 || cosVal < -1) {
    +    goog.vec.Vec4.setFromArray(resultQuat, q1);
    +    return resultQuat;
    +  }
    +
    +  // Quaternions are a double cover on the space of rotations. That is, q and -q
    +  // represent the same rotation. Thus we have two possibilities when
    +  // interpolating between q0 and q1: going the short way or the long way. We
    +  // prefer the short way since that is the likely expectation from users.
    +  var factor = 1;
    +  if (cosVal < 0) {
    +    factor = -1;
    +    cosVal = -cosVal;
    +  }
    +
    +  // Compute the angle between q0 and q1. If it's very small, then just return
    +  // q1 to avoid a very large denominator below.
    +  var angle = Math.acos(cosVal);
    +  if (angle <= goog.vec.EPSILON) {
    +    goog.vec.Vec4.setFromArray(resultQuat, q1);
    +    return resultQuat;
    +  }
    +
    +  // Compute the coefficients and interpolate.
    +  var invSinVal = 1 / Math.sin(angle);
    +  var c0 = Math.sin((1 - t) * angle) * invSinVal;
    +  var c1 = factor * Math.sin(t * angle) * invSinVal;
    +
    +  resultQuat[0] = q0[0] * c0 + q1[0] * c1;
    +  resultQuat[1] = q0[1] * c0 + q1[1] * c1;
    +  resultQuat[2] = q0[2] * c0 + q1[2] * c1;
    +  resultQuat[3] = q0[3] * c0 + q1[3] * c1;
    +  return resultQuat;
    +};
    +
    +
    +/**
    + * Compute the simple linear interpolation of the two quaternions q0 and q1
    + * according to the coefficient t. The resulting quaternion is stored in
    + * resultVec.
    + *
    + * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
    + * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
    + * @param {number} t The interpolation factor.
    + * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
    + *     receive the results (may be q0 or q1).
    + */
    +goog.vec.Quaternion.nlerp = goog.vec.Vec4.lerp;
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/quaternion_test.html b/mapbox-gl/closure-library/closure/goog/vec/quaternion_test.html
    new file mode 100644
    index 0000000000..4ca760045e
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/quaternion_test.html
    @@ -0,0 +1,195 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Quaternion</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Mat4');
    +  goog.require('goog.vec.Quaternion');
    +  goog.require('goog.vec.Vec3');
    +  goog.require('goog.vec.Vec4');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConjugate() {
    +    var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
    +    var q1 = goog.vec.Quaternion.createFloat32();
    +
    +    goog.vec.Quaternion.conjugate(q0, q1);
    +    assertElementsEquals([1, 2, 3, 4], q0);
    +    assertElementsEquals([-1, -2, -3, 4], q1);
    +
    +    goog.vec.Quaternion.conjugate(q1, q1);
    +    assertElementsEquals([1, 2, 3, 4], q1);
    +  }
    +
    +  function testConcat() {
    +    var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
    +    var q1 = goog.vec.Quaternion.createFloat32FromValues(2, 3, 4, 5);
    +    var q2 = goog.vec.Quaternion.createFloat32();
    +    goog.vec.Quaternion.concat(q0, q1, q2);
    +    assertElementsEquals([12, 24, 30, 0], q2);
    +
    +    goog.vec.Quaternion.concat(q0, q1, q0);
    +    assertElementsEquals([12, 24, 30, 0], q0);
    +  }
    +
    +  function testSlerp() {
    +    var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
    +    var q1 = goog.vec.Quaternion.createFloat32FromValues(5, -6, 7, -8);
    +    var q2 = goog.vec.Quaternion.createFloat32();
    +
    +    goog.vec.Quaternion.slerp(q0, q1, 0, q2);
    +    assertElementsEquals([5, -6, 7, -8], q2);
    +
    +    goog.vec.Quaternion.normalize(q0, q0);
    +    goog.vec.Quaternion.normalize(q1, q1);
    +
    +    goog.vec.Quaternion.slerp(q0, q0, .5, q2);
    +    assertElementsEquals(q0, q2);
    +
    +    goog.vec.Quaternion.slerp(q0, q1, 0, q2);
    +    assertElementsEquals(q0, q2);
    +
    +    goog.vec.Quaternion.slerp(q0, q1, 1, q2);
    +    if (q1[3] * q2[3] < 0) {
    +      goog.vec.Quaternion.negate(q2, q2);
    +    }
    +    assertElementsEquals(q1, q2);
    +
    +    goog.vec.Quaternion.slerp(q0, q1, .3, q2);
    +    assertElementsRoughlyEqual(
    +        [-0.000501537327541, 0.4817612034640, 0.2398775270769, 0.842831337398],
    +        q2, goog.vec.EPSILON);
    +
    +    goog.vec.Quaternion.slerp(q0, q1, .5, q2);
    +    assertElementsRoughlyEqual(
    +        [-0.1243045421171, 0.51879732466, 0.0107895780990, 0.845743047108],
    +        q2, goog.vec.EPSILON);
    +
    +    goog.vec.Quaternion.slerp(q0, q1, .8, q0);
    +    assertElementsRoughlyEqual(
    +        [-0.291353561485, 0.506925588797, -0.3292443285721, 0.741442999653],
    +        q0, goog.vec.EPSILON);
    +  }
    +
    +  function testToRotMatrix() {
    +    var q0 = goog.vec.Quaternion.createFloat32FromValues(
    +        0.22094256606638, 0.53340203646030,
    +        0.64777022739548, 0.497051689967954);
    +    var m0 = goog.vec.Mat4.createFloat32();
    +    goog.vec.Quaternion.toRotationMatrix4(q0, m0);
    +
    +    assertElementsRoughlyEqual(
    +        [-0.408248, 0.8796528, -0.244016935, 0,
    +         -0.4082482, 0.06315623, 0.9106836, 0,
    +         0.8164965, 0.47140452, 0.3333333, 0,
    +         0, 0, 0, 1],
    +        m0, goog.vec.EPSILON);
    +  }
    +
    +  function testFromRotMatrix() {
    +    var m0 = goog.vec.Mat4.createFloat32FromValues(
    +        -0.408248, 0.8796528, -0.244016935, 0,
    +        -0.4082482, 0.06315623, 0.9106836, 0,
    +        0.8164965, 0.47140452, 0.3333333, 0,
    +        0, 0, 0, 1);
    +    var q0 = goog.vec.Quaternion.createFloat32();
    +    goog.vec.Quaternion.fromRotationMatrix4(m0, q0);
    +    assertElementsRoughlyEqual(
    +        [0.22094256606638, 0.53340203646030,
    +         0.64777022739548, 0.497051689967954],
    +        q0, goog.vec.EPSILON);
    +  }
    +
    +  function testToAngleAxis() {
    +    // Test the identity rotation.
    +    var q0 = goog.vec.Quaternion.createFloat32FromValues(0, 0, 0, 1);
    +    var axis = goog.vec.Vec3.createFloat32();
    +    var angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
    +    assertRoughlyEquals(0.0, angle, goog.vec.EPSILON);
    +    assertElementsRoughlyEqual([1, 0, 0], axis, goog.vec.EPSILON);
    +
    +    // Check equivalent representations of the same rotation.
    +    goog.vec.Quaternion.setFromValues(
    +        q0, -0.288675032, 0.622008682, -0.17254543, 0.70710678);
    +    angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
    +    assertRoughlyEquals(Math.PI / 2, angle, goog.vec.EPSILON);
    +    assertElementsRoughlyEqual([-0.408248, 0.8796528, -0.244016],
    +                               axis, goog.vec.EPSILON);
    +    // The polar opposite unit quaternion is the same rotation, so we
    +    // check that the negated quaternion yields the negated angle and axis.
    +    goog.vec.Quaternion.negate(q0, q0);
    +    angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
    +    assertRoughlyEquals(-Math.PI / 2, angle, goog.vec.EPSILON);
    +    assertElementsRoughlyEqual([0.408248, -0.8796528, 0.244016],
    +                               axis, goog.vec.EPSILON);
    +
    +    // Verify that the inverse rotation yields the inverse axis.
    +    goog.vec.Quaternion.conjugate(q0, q0);
    +    angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
    +    assertRoughlyEquals(-Math.PI / 2, angle, goog.vec.EPSILON);
    +    assertElementsRoughlyEqual([-0.408248, 0.8796528, -0.244016],
    +                               axis, goog.vec.EPSILON);
    +  }
    +
    +  function testFromAngleAxis() {
    +    // Test identity rotation (zero angle or multiples of TWO_PI).
    +    var angle = 0.0;
    +    var axis = goog.vec.Vec3.createFloat32FromValues(-0.408248, 0.8796528,
    +                                                     -0.244016);
    +    var q0 = goog.vec.Quaternion.createFloat32();
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual([0, 0, 0, 1], q0, goog.vec.EPSILON);
    +    angle = 4 * Math.PI;
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual([0, 0, 0, 1], q0, goog.vec.EPSILON);
    +
    +    // General test of various rotations around axes of different lengths.
    +    angle = Math.PI / 2;
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual(
    +        [-0.288675032, 0.622008682, -0.17254543, 0.70710678],
    +        q0, goog.vec.EPSILON);
    +    // Angle multiples of TWO_PI with a scaled axis should be the same.
    +    angle += 4 * Math.PI;
    +    goog.vec.Vec3.scale(axis, 7.0, axis);
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual(
    +        [-0.288675032, 0.622008682, -0.17254543, 0.70710678],
    +        q0, goog.vec.EPSILON);
    +    goog.vec.Vec3.setFromValues(axis, 1, 5, 8);
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual(
    +        [0.074535599, 0.372677996, 0.596284794, 0.70710678],
    +        q0, goog.vec.EPSILON);
    +
    +    // Check equivalent representations of the same rotation.
    +    angle = Math.PI / 5;
    +    goog.vec.Vec3.setFromValues(axis, 5, -2, -10);
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual(
    +        [0.136037146, -0.0544148586, -0.27207429, 0.951056516],
    +        q0, goog.vec.EPSILON);
    +    // The negated angle and axis should yield the same rotation.
    +    angle = -Math.PI / 5;
    +    goog.vec.Vec3.negate(axis, axis);
    +    goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
    +    assertElementsRoughlyEqual(
    +        [0.136037146, -0.0544148586, -0.27207429, 0.951056516],
    +        q0, goog.vec.EPSILON);
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/ray.js b/mapbox-gl/closure-library/closure/goog/vec/ray.js
    new file mode 100644
    index 0000000000..84ca1f7df9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/ray.js
    @@ -0,0 +1,95 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Implements a 3D ray that are compatible with WebGL.
    + * Each element is a float64 in case high precision is required.
    + * The API is structured to avoid unnecessary memory allocations.
    + * The last parameter will typically be the output vector and an
    + * object can be both an input and output parameter to all methods
    + * except where noted.
    + *
    + */
    +goog.provide('goog.vec.Ray');
    +
    +goog.require('goog.vec.Vec3');
    +
    +
    +
    +/**
    + * Constructs a new ray with an optional origin and direction. If not specified,
    + * the default is [0, 0, 0].
    + * @param {goog.vec.Vec3.AnyType=} opt_origin The optional origin.
    + * @param {goog.vec.Vec3.AnyType=} opt_dir The optional direction.
    + * @constructor
    + * @final
    + */
    +goog.vec.Ray = function(opt_origin, opt_dir) {
    +  /**
    +   * @type {goog.vec.Vec3.Float64}
    +   */
    +  this.origin = goog.vec.Vec3.createFloat64();
    +  if (opt_origin) {
    +    goog.vec.Vec3.setFromArray(this.origin, opt_origin);
    +  }
    +
    +  /**
    +   * @type {goog.vec.Vec3.Float64}
    +   */
    +  this.dir = goog.vec.Vec3.createFloat64();
    +  if (opt_dir) {
    +    goog.vec.Vec3.setFromArray(this.dir, opt_dir);
    +  }
    +};
    +
    +
    +/**
    + * Sets the origin and direction of the ray.
    + * @param {goog.vec.AnyType} origin The new origin.
    + * @param {goog.vec.AnyType} dir The new direction.
    + */
    +goog.vec.Ray.prototype.set = function(origin, dir) {
    +  goog.vec.Vec3.setFromArray(this.origin, origin);
    +  goog.vec.Vec3.setFromArray(this.dir, dir);
    +};
    +
    +
    +/**
    + * Sets the origin of the ray.
    + * @param {goog.vec.AnyType} origin the new origin.
    + */
    +goog.vec.Ray.prototype.setOrigin = function(origin) {
    +  goog.vec.Vec3.setFromArray(this.origin, origin);
    +};
    +
    +
    +/**
    + * Sets the direction of the ray.
    + * @param {goog.vec.AnyType} dir The new direction.
    + */
    +goog.vec.Ray.prototype.setDir = function(dir) {
    +  goog.vec.Vec3.setFromArray(this.dir, dir);
    +};
    +
    +
    +/**
    + * Returns true if this ray is equal to the other ray.
    + * @param {goog.vec.Ray} other The other ray.
    + * @return {boolean} True if this ray is equal to the other ray.
    + */
    +goog.vec.Ray.prototype.equals = function(other) {
    +  return other != null &&
    +      goog.vec.Vec3.equals(this.origin, other.origin) &&
    +      goog.vec.Vec3.equals(this.dir, other.dir);
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/ray_test.html b/mapbox-gl/closure-library/closure/goog/vec/ray_test.html
    new file mode 100644
    index 0000000000..61d878736b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/ray_test.html
    @@ -0,0 +1,66 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Ray</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Ray');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructor() {
    +    var new_ray = new goog.vec.Ray();
    +    assertElementsEquals([0, 0, 0], new_ray.origin);
    +    assertElementsEquals([0, 0, 0], new_ray.dir);
    +
    +    new_ray = new goog.vec.Ray([1, 2, 3], [4, 5, 6]);
    +    assertElementsEquals([1, 2, 3], new_ray.origin);
    +    assertElementsEquals([4, 5, 6], new_ray.dir);
    +  }
    +
    +  function testSet() {
    +    var new_ray = new goog.vec.Ray();
    +    new_ray.set([2, 3, 4], [5, 6, 7]);
    +    assertElementsEquals([2, 3, 4], new_ray.origin);
    +    assertElementsEquals([5, 6, 7], new_ray.dir);
    +  }
    +
    +  function testSetOrigin() {
    +    var new_ray = new goog.vec.Ray();
    +    new_ray.setOrigin([1, 2, 3]);
    +    assertElementsEquals([1, 2, 3], new_ray.origin);
    +    assertElementsEquals([0, 0, 0], new_ray.dir);
    +  }
    +
    +
    +  function testSetDir() {
    +    var new_ray = new goog.vec.Ray();
    +    new_ray.setDir([2, 3, 4]);
    +    assertElementsEquals([0, 0, 0], new_ray.origin);
    +    assertElementsEquals([2, 3, 4], new_ray.dir);
    +  }
    +
    +  function testEquals() {
    +    var r0 = new goog.vec.Ray([1, 2, 3], [4, 5, 6]);
    +    var r1 = new goog.vec.Ray([5, 2, 3], [4, 5, 6]);
    +    assertFalse(r0.equals(r1));
    +    assertFalse(r0.equals(null));
    +    assertTrue(r1.equals(r1));
    +    r1.setOrigin(r0.origin);
    +    assertTrue(r1.equals(r0));
    +    assertTrue(r0.equals(r1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec.js b/mapbox-gl/closure-library/closure/goog/vec/vec.js
    new file mode 100644
    index 0000000000..de493fee5b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec.js
    @@ -0,0 +1,73 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Supplies global data types and constants for the vector math
    + *     library.
    + */
    +goog.provide('goog.vec');
    +goog.provide('goog.vec.AnyType');
    +goog.provide('goog.vec.ArrayType');
    +goog.provide('goog.vec.Float32');
    +goog.provide('goog.vec.Float64');
    +goog.provide('goog.vec.Number');
    +
    +
    +/**
    + * On platforms that don't have native Float32Array or Float64Array support we
    + * use a javascript implementation so that this math library can be used on all
    + * platforms.
    + * @suppress {extraRequire}
    + */
    +goog.require('goog.vec.Float32Array');
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec.Float64Array');
    +
    +// All vector and matrix operations are based upon arrays of numbers using
    +// either Float32Array, Float64Array, or a standard Javascript Array of
    +// Numbers.
    +
    +
    +/** @typedef {!Float32Array} */
    +goog.vec.Float32;
    +
    +
    +/** @typedef {!Float64Array} */
    +goog.vec.Float64;
    +
    +
    +/** @typedef {!Array<number>} */
    +goog.vec.Number;
    +
    +
    +/** @typedef {!goog.vec.Float32|!goog.vec.Float64|!goog.vec.Number} */
    +goog.vec.AnyType;
    +
    +
    +/**
    + * @deprecated Use AnyType.
    + * @typedef {!Float32Array|!Array<number>}
    + */
    +goog.vec.ArrayType;
    +
    +
    +/**
    + * For graphics work, 6 decimal places of accuracy are typically all that is
    + * required.
    + *
    + * @type {number}
    + * @const
    + */
    +goog.vec.EPSILON = 1e-6;
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2.js b/mapbox-gl/closure-library/closure/goog/vec/vec2.js
    new file mode 100644
    index 0000000000..daf789a160
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2.js
    @@ -0,0 +1,439 @@
    +// Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Definition of 2 element vectors.  This follows the same design
    + * patterns as Vec3 and Vec4.
    + *
    + */
    +
    +goog.provide('goog.vec.Vec2');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Vec2.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Vec2.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Vec2.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Vec2.AnyType;
    +
    +
    +/**
    + * Creates a 2 element vector of Float32. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec2.Float32} The new 2 element array.
    + */
    +goog.vec.Vec2.createFloat32 = function() {
    +  return new Float32Array(2);
    +};
    +
    +
    +/**
    + * Creates a 2 element vector of Float64. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec2.Float64} The new 2 element array.
    + */
    +goog.vec.Vec2.createFloat64 = function() {
    +  return new Float64Array(2);
    +};
    +
    +
    +/**
    + * Creates a 2 element vector of Number. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec2.Number} The new 2 element array.
    + */
    +goog.vec.Vec2.createNumber = function() {
    +  var a = new Array(2);
    +  goog.vec.Vec2.setFromValues(a, 0, 0);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates a new 2 element FLoat32 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec The source 2 element array.
    + * @return {!goog.vec.Vec2.Float32} The new 2 element array.
    + */
    +goog.vec.Vec2.createFloat32FromArray = function(vec) {
    +  var newVec = goog.vec.Vec2.createFloat32();
    +  goog.vec.Vec2.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Creates a new 2 element Float32 vector initialized with the supplied values.
    + *
    + * @param {number} vec0 The value for element at index 0.
    + * @param {number} vec1 The value for element at index 1.
    + * @return {!goog.vec.Vec2.Float32} The new vector.
    + */
    +goog.vec.Vec2.createFloat32FromValues = function(vec0, vec1) {
    +  var a = goog.vec.Vec2.createFloat32();
    +  goog.vec.Vec2.setFromValues(a, vec0, vec1);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 2 element Float32 vector.
    + *
    + * @param {goog.vec.Vec2.Float32} vec The source 2 element vector.
    + * @return {!goog.vec.Vec2.Float32} The new cloned vector.
    + */
    +goog.vec.Vec2.cloneFloat32 = goog.vec.Vec2.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a new 2 element Float64 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec The source 2 element array.
    + * @return {!goog.vec.Vec2.Float64} The new 2 element array.
    + */
    +goog.vec.Vec2.createFloat64FromArray = function(vec) {
    +  var newVec = goog.vec.Vec2.createFloat64();
    +  goog.vec.Vec2.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    +* Creates a new 2 element Float64 vector initialized with the supplied values.
    +*
    +* @param {number} vec0 The value for element at index 0.
    +* @param {number} vec1 The value for element at index 1.
    +* @return {!goog.vec.Vec2.Float64} The new vector.
    +*/
    +goog.vec.Vec2.createFloat64FromValues = function(vec0, vec1) {
    +  var vec = goog.vec.Vec2.createFloat64();
    +  goog.vec.Vec2.setFromValues(vec, vec0, vec1);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 2 element vector.
    + *
    + * @param {goog.vec.Vec2.Float64} vec The source 2 element vector.
    + * @return {!goog.vec.Vec2.Float64} The new cloned vector.
    + */
    +goog.vec.Vec2.cloneFloat64 = goog.vec.Vec2.createFloat64FromArray;
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec The vector to receive the values.
    + * @param {number} vec0 The value for element at index 0.
    + * @param {number} vec1 The value for element at index 1.
    + * @return {!goog.vec.Vec2.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.setFromValues = function(vec, vec0, vec1) {
    +  vec[0] = vec0;
    +  vec[1] = vec1;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes the vector with the given array of values.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec The vector to receive the
    + *     values.
    + * @param {goog.vec.Vec2.AnyType} values The array of values.
    + * @return {!goog.vec.Vec2.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.setFromArray = function(vec, values) {
    +  vec[0] = values[0];
    +  vec[1] = values[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The first addend.
    + * @param {goog.vec.Vec2.AnyType} vec1 The second addend.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The minuend.
    + * @param {goog.vec.Vec2.AnyType} vec1 The subtrahend.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The vector to negate.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec2.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec2.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return Math.sqrt(x * x + y * y);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The vector to normalize.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.normalize = function(vec0, resultVec) {
    +  var ilen = 1 / goog.vec.Vec2.magnitude(vec0);
    +  resultVec[0] = vec0[0] * ilen;
    +  resultVec[1] = vec0[1] * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors vec0 and vec1.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
    + * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.Vec2.dot = function(vec0, vec1) {
    +  return vec0[0] * vec1[0] + vec0[1] * vec1[1];
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 First point.
    + * @param {goog.vec.Vec2.AnyType} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.Vec2.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 First point.
    + * @param {goog.vec.Vec2.AnyType} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.Vec2.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.Vec2.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 Origin point.
    + * @param {goog.vec.Vec2.AnyType} vec1 Target point.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var d = Math.sqrt(x * x + y * y);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to vec1 according to f. The value of f should
    + * be in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
    + * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.lerp = function(vec0, vec1, f, resultVec) {
    +  var x = vec0[0], y = vec0[1];
    +  resultVec[0] = (vec1[0] - x) * f + x;
    +  resultVec[1] = (vec1[1] - y) * f + y;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec2.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec2.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec2.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of vec0 are equal to the components of vec1.
    + *
    + * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
    + * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.Vec2.equals = function(vec0, vec1) {
    +  return vec0.length == vec1.length &&
    +      vec0[0] == vec1[0] && vec0[1] == vec1[1];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec2_test.html
    new file mode 100644
    index 0000000000..4ec7a17bf7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2_test.html
    @@ -0,0 +1,254 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2012 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Vec2</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Vec2');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testConstructor() {
    +    var v = goog.vec.Vec2.createFloat32();
    +    assertElementsEquals(0, v[0]);
    +    assertEquals(0, v[1]);
    +
    +    assertElementsEquals([0, 0], goog.vec.Vec2.createFloat32());
    +
    +    goog.vec.Vec2.setFromValues(v, 1, 2);
    +    assertElementsEquals([1, 2], v);
    +
    +    var w = goog.vec.Vec2.createFloat64();
    +    assertElementsEquals(0, w[0]);
    +    assertEquals(0, w[1]);
    +
    +    assertElementsEquals([0, 0], goog.vec.Vec2.createFloat64());
    +
    +    goog.vec.Vec2.setFromValues(w, 1, 2);
    +    assertElementsEquals([1, 2], w);
    +  }
    +
    +
    +  function testSet() {
    +    var v = goog.vec.Vec2.createFloat32();
    +    goog.vec.Vec2.setFromValues(v, 1, 2);
    +    assertElementsEquals([1, 2], v);
    +
    +    goog.vec.Vec2.setFromArray(v, [4, 5]);
    +    assertElementsEquals([4, 5], v);
    +
    +    var w = goog.vec.Vec2.createFloat32();
    +    goog.vec.Vec2.setFromValues(w, 1, 2);
    +    assertElementsEquals([1, 2], w);
    +
    +    goog.vec.Vec2.setFromArray(w, [4, 5]);
    +    assertElementsEquals([4, 5], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    var v1 = goog.vec.Vec2.createFloat32FromArray([4, 5]);
    +    var v2 = goog.vec.Vec2.cloneFloat32(v0);
    +
    +    goog.vec.Vec2.add(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([5, 7], v2);
    +
    +    goog.vec.Vec2.add(goog.vec.Vec2.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    var v1 = goog.vec.Vec2.createFloat32FromArray([4, 5]);
    +    var v2 = goog.vec.Vec2.cloneFloat32(v0);
    +
    +    goog.vec.Vec2.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.Vec2.setFromValues(v2, 0, 0, 0);
    +    goog.vec.Vec2.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3], v2);
    +
    +    v2 = goog.vec.Vec2.cloneFloat32(v0);
    +    goog.vec.Vec2.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.Vec2.subtract(goog.vec.Vec2.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    var v1 = goog.vec.Vec2.createFloat32();
    +
    +    goog.vec.Vec2.negate(v0, v1);
    +    assertElementsEquals([-1, -2], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.Vec2.negate(v0, v0);
    +    assertElementsEquals([-1, -2], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(-1, -2);
    +    var v1 = goog.vec.Vec2.createFloat32();
    +
    +    goog.vec.Vec2.abs(v0, v1);
    +    assertElementsEquals([1, 2], v1);
    +    assertElementsEquals([-1, -2], v0);
    +
    +    goog.vec.Vec2.abs(v0, v0);
    +    assertElementsEquals([1, 2], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    var v1 = goog.vec.Vec2.createFloat32();
    +
    +    goog.vec.Vec2.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.Vec2.setFromArray(v1, v0);
    +    goog.vec.Vec2.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    assertEquals(5, goog.vec.Vec2.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    assertEquals(Math.sqrt(5), goog.vec.Vec2.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([2, 3]);
    +    var v1 = goog.vec.Vec2.createFloat32();
    +    var v2 = goog.vec.Vec2.createFloat32();
    +    goog.vec.Vec2.scale(
    +        v0, 1 / goog.vec.Vec2.magnitude(v0), v2);
    +
    +    goog.vec.Vec2.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3], v0);
    +
    +    goog.vec.Vec2.setFromArray(v1, v0);
    +    goog.vec.Vec2.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.Vec2.createFloat32FromArray([1, 2]);
    +    var v1 = goog.vec.Vec2.createFloat32FromArray([4, 5]);
    +    assertEquals(14, goog.vec.Vec2.dot(v0, v1));
    +    assertEquals(14, goog.vec.Vec2.dot(v1, v0));
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    assertEquals(0, goog.vec.Vec2.distanceSquared(v0, v1));
    +    goog.vec.Vec2.setFromValues(v0, 1, 2);
    +    goog.vec.Vec2.setFromValues(v1, -1, -2);
    +    assertEquals(20, goog.vec.Vec2.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    assertEquals(0, goog.vec.Vec2.distance(v0, v1));
    +    goog.vec.Vec2.setFromValues(v0, 2, 3);
    +    goog.vec.Vec2.setFromValues(v1, -2, 0);
    +    assertEquals(5, goog.vec.Vec2.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var dirVec = goog.vec.Vec2.createFloat32FromValues(4, 5);
    +    goog.vec.Vec2.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0], dirVec);
    +    goog.vec.Vec2.setFromValues(v0, 0, 0);
    +    goog.vec.Vec2.setFromValues(v1, 1, 0);
    +    goog.vec.Vec2.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0], dirVec);
    +    goog.vec.Vec2.setFromValues(v0, 1, 1);
    +    goog.vec.Vec2.setFromValues(v1, 0, 0);
    +    goog.vec.Vec2.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.707106781, -0.707106781],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(10, 20);
    +    var v2 = goog.vec.Vec2.cloneFloat32(v0);
    +
    +    goog.vec.Vec2.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2], v2);
    +    goog.vec.Vec2.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20], v2);
    +    goog.vec.Vec2.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(10, 20);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(5, 25);
    +    var v2 = goog.vec.Vec2.createFloat32();
    +
    +    goog.vec.Vec2.max(v0, v1, v2);
    +    assertElementsEquals([10, 25], v2);
    +    goog.vec.Vec2.max(v1, v0, v1);
    +    assertElementsEquals([10, 25], v1);
    +    goog.vec.Vec2.max(v2, 20, v2);
    +    assertElementsEquals([20, 25], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(10, 20);
    +    var v1 = goog.vec.Vec2.createFloat32FromValues(5, 25);
    +    var v2 = goog.vec.Vec2.createFloat32();
    +
    +    goog.vec.Vec2.min(v0, v1, v2);
    +    assertElementsEquals([5, 20], v2);
    +    goog.vec.Vec2.min(v1, v0, v1);
    +    assertElementsEquals([5, 20], v1);
    +    goog.vec.Vec2.min(v2, 10, v2);
    +    assertElementsEquals([5, 10], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.Vec2.createFloat32FromValues(1, 2);
    +    var v1 = goog.vec.Vec2.cloneFloat32(v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.Vec2.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec2.cloneFloat32(v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.Vec2.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2d.js b/mapbox-gl/closure-library/closure/goog/vec/vec2d.js
    new file mode 100644
    index 0000000000..ad07200923
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2d.js
    @@ -0,0 +1,424 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec2f.js by running:            //
    +//   swap_type.sh vec2d.js > vec2f.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 2 element double (64bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +
    +goog.provide('goog.vec.vec2d');
    +goog.provide('goog.vec.vec2d.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float64} */ goog.vec.vec2d.Type;
    +
    +
    +/**
    + * Creates a vec2d with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec2d.Type} The new vec2d.
    + */
    +goog.vec.vec2d.create = function() {
    +  return new Float64Array(2);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec2d.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @return {!goog.vec.vec2d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.setFromValues = function(vec, v0, v1) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2d vec from vec2d src.
    + *
    + * @param {goog.vec.vec2d.Type} vec The destination vector.
    + * @param {goog.vec.vec2d.Type} src The source vector.
    + * @return {!goog.vec.vec2d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.setFromVec2d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2d vec from vec2f src (typed as a Float32Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec2d.Type} vec The destination vector.
    + * @param {Float32Array} src The source vector.
    + * @return {!goog.vec.vec2d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.setFromVec2f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2d vec from Array src.
    + *
    + * @param {goog.vec.vec2d.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec2d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The first addend.
    + * @param {goog.vec.vec2d.Type} vec1 The second addend.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The minuend.
    + * @param {goog.vec.vec2d.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with the matching element of vec0
    + * storing the products into resultVec.
    + *
    + * @param {!goog.vec.vec2d.Type} vec0 The first vector.
    + * @param {!goog.vec.vec2d.Type} vec1 The second vector.
    + * @param {!goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.componentMultiply = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] * vec1[0];
    +  resultVec[1] = vec0[1] * vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Divides each component of vec0 with the matching element of vec0
    + * storing the divisor into resultVec.
    + *
    + * @param {!goog.vec.vec2d.Type} vec0 The first vector.
    + * @param {!goog.vec.vec2d.Type} vec1 The second vector.
    + * @param {!goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.componentDivide = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] / vec1[0];
    +  resultVec[1] = vec0[1] / vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The source vector.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec2d.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec2d.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return Math.sqrt(x * x + y * y);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1];
    +  var ilen = 1 / Math.sqrt(x * x + y * y);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors vec0 and vec1.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The first vector.
    + * @param {goog.vec.vec2d.Type} vec1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec2d.dot = function(vec0, vec1) {
    +  return vec0[0] * vec1[0] + vec0[1] * vec1[1];
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 First point.
    + * @param {goog.vec.vec2d.Type} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.vec2d.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 First point.
    + * @param {goog.vec.vec2d.Type} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.vec2d.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.vec2d.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 Origin point.
    + * @param {goog.vec.vec2d.Type} vec1 Target point.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var d = Math.sqrt(x * x + y * y);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to vec1 according to f. The value of f should
    + * be in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The first vector.
    + * @param {goog.vec.vec2d.Type} vec1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.lerp = function(vec0, vec1, f, resultVec) {
    +  var x = vec0[0], y = vec0[1];
    +  resultVec[0] = (vec1[0] - x) * f + x;
    +  resultVec[1] = (vec1[1] - y) * f + y;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The source vector.
    + * @param {goog.vec.vec2d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The source vector.
    + * @param {goog.vec.vec2d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec2d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec2d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2d.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of vec0 are equal to the components of vec1.
    + *
    + * @param {goog.vec.vec2d.Type} vec0 The first vector.
    + * @param {goog.vec.vec2d.Type} vec1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec2d.equals = function(vec0, vec1) {
    +  return vec0.length == vec1.length &&
    +      vec0[0] == vec1[0] && vec0[1] == vec1[1];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2d_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec2d_test.html
    new file mode 100644
    index 0000000000..af74c8bbec
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2d_test.html
    @@ -0,0 +1,282 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec2f_test.html by running:     //
    +//   swap_type.sh vec2d_test.html > vec2f_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec2d</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float64Array');
    +  goog.require('goog.vec.vec2d');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec2d.create();
    +    assertElementsEquals([0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec2d.create();
    +    goog.vec.vec2d.setFromValues(v, 1, 2);
    +    assertElementsEquals([1, 2], v);
    +
    +    goog.vec.vec2d.setFromArray(v, [4, 5]);
    +    assertElementsEquals([4, 5], v);
    +
    +    var w = goog.vec.vec2d.create();
    +    goog.vec.vec2d.setFromValues(w, 1, 2);
    +    assertElementsEquals([1, 2], w);
    +
    +    goog.vec.vec2d.setFromArray(w, [4, 5]);
    +    assertElementsEquals([4, 5], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [4, 5]);
    +    var v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +
    +    goog.vec.vec2d.add(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([5, 7], v2);
    +
    +    goog.vec.vec2d.add(goog.vec.vec2d.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [4, 5]);
    +    var v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +
    +    goog.vec.vec2d.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.vec2d.setFromValues(v2, 0, 0);
    +    goog.vec.vec2d.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3], v2);
    +
    +    v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +    goog.vec.vec2d.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.vec2d.subtract(goog.vec.vec2d.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1], v2);
    +  }
    +
    +  function testMultiply() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [4, 5]);
    +    var v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +
    +    goog.vec.vec2d.componentMultiply(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([4, 10], v2);
    +
    +    goog.vec.vec2d.componentMultiply(goog.vec.vec2d.componentMultiply(v0, v1, v2), v0, v2);
    +    assertElementsEquals([4, 20], v2);
    +  }
    +
    +  function testDivide() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [4, 5]);
    +    var v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +
    +    goog.vec.vec2d.componentDivide(v2, v1, v2);
    +    assertElementsRoughlyEqual([1, 2], v0, 10e-5);
    +    assertElementsRoughlyEqual([4, 5], v1, 10e-5);
    +    assertElementsRoughlyEqual([.25, .4], v2, 10e-5);
    +
    +    goog.vec.vec2d.setFromValues(v2, 0, 0);
    +    goog.vec.vec2d.componentDivide(v1, v0, v2);
    +    assertElementsRoughlyEqual([4, 2.5], v2, 10e-5);
    +
    +    v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +    goog.vec.vec2d.componentDivide(v2, v1, v2);
    +    assertElementsRoughlyEqual([.25, .4], v2, 10e-5);
    +
    +    goog.vec.vec2d.componentDivide(goog.vec.vec2d.componentDivide(v1, v0, v2), v0, v2);
    +    assertElementsRoughlyEqual([4, 1.25], v2, 10e-5);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.create();
    +
    +    goog.vec.vec2d.negate(v0, v1);
    +    assertElementsEquals([-1, -2], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.vec2d.negate(v0, v0);
    +    assertElementsEquals([-1, -2], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [-1, -2]);
    +    var v1 = goog.vec.vec2d.create();
    +
    +    goog.vec.vec2d.abs(v0, v1);
    +    assertElementsEquals([1, 2], v1);
    +    assertElementsEquals([-1, -2], v0);
    +
    +    goog.vec.vec2d.abs(v0, v0);
    +    assertElementsEquals([1, 2], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.create();
    +
    +    goog.vec.vec2d.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.vec2d.setFromArray(v1, v0);
    +    goog.vec.vec2d.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    assertEquals(5, goog.vec.vec2d.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    assertEquals(Math.sqrt(5), goog.vec.vec2d.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [2, 3]);
    +    var v1 = goog.vec.vec2d.create();
    +    var v2 = goog.vec.vec2d.create();
    +    goog.vec.vec2d.scale(
    +        v0, 1 / goog.vec.vec2d.magnitude(v0), v2);
    +
    +    goog.vec.vec2d.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3], v0);
    +
    +    goog.vec.vec2d.setFromArray(v1, v0);
    +    goog.vec.vec2d.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [1, 2]);
    +    var v1 = goog.vec.vec2d.setFromArray(goog.vec.vec2d.create(), [4, 5]);
    +    assertEquals(14, goog.vec.vec2d.dot(v0, v1));
    +    assertEquals(14, goog.vec.vec2d.dot(v1, v0));
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    assertEquals(0, goog.vec.vec2d.distanceSquared(v0, v1));
    +    goog.vec.vec2d.setFromValues(v0, 1, 2);
    +    goog.vec.vec2d.setFromValues(v1, -1, -2);
    +    assertEquals(20, goog.vec.vec2d.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    assertEquals(0, goog.vec.vec2d.distance(v0, v1));
    +    goog.vec.vec2d.setFromValues(v0, 2, 3);
    +    goog.vec.vec2d.setFromValues(v1, -2, 0);
    +    assertEquals(5, goog.vec.vec2d.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var dirVec = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 4, 5);
    +    goog.vec.vec2d.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0], dirVec);
    +    goog.vec.vec2d.setFromValues(v0, 0, 0);
    +    goog.vec.vec2d.setFromValues(v1, 1, 0);
    +    goog.vec.vec2d.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0], dirVec);
    +    goog.vec.vec2d.setFromValues(v0, 1, 1);
    +    goog.vec.vec2d.setFromValues(v1, 0, 0);
    +    goog.vec.vec2d.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.707106781, -0.707106781],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 10, 20);
    +    var v2 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +
    +    goog.vec.vec2d.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2], v2);
    +    goog.vec.vec2d.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20], v2);
    +    goog.vec.vec2d.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 10, 20);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 5, 25);
    +    var v2 = goog.vec.vec2d.create();
    +
    +    goog.vec.vec2d.max(v0, v1, v2);
    +    assertElementsEquals([10, 25], v2);
    +    goog.vec.vec2d.max(v1, v0, v1);
    +    assertElementsEquals([10, 25], v1);
    +    goog.vec.vec2d.max(v2, 20, v2);
    +    assertElementsEquals([20, 25], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 10, 20);
    +    var v1 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 5, 25);
    +    var v2 = goog.vec.vec2d.create();
    +
    +    goog.vec.vec2d.min(v0, v1, v2);
    +    assertElementsEquals([5, 20], v2);
    +    goog.vec.vec2d.min(v1, v0, v1);
    +    assertElementsEquals([5, 20], v1);
    +    goog.vec.vec2d.min(v2, 10, v2);
    +    assertElementsEquals([5, 10], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec2d.setFromValues(goog.vec.vec2d.create(), 1, 2);
    +    var v1 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.vec2d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec2d.setFromVec2d(goog.vec.vec2d.create(), v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.vec2d.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2f.js b/mapbox-gl/closure-library/closure/goog/vec/vec2f.js
    new file mode 100644
    index 0000000000..8ec5643a70
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2f.js
    @@ -0,0 +1,424 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec2d.js by running:            //
    +//   swap_type.sh vec2f.js > vec2d.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 2 element float (32bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +
    +goog.provide('goog.vec.vec2f');
    +goog.provide('goog.vec.vec2f.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.vec2f.Type;
    +
    +
    +/**
    + * Creates a vec2f with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec2f.Type} The new vec2f.
    + */
    +goog.vec.vec2f.create = function() {
    +  return new Float32Array(2);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec2f.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @return {!goog.vec.vec2f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.setFromValues = function(vec, v0, v1) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2f vec from vec2f src.
    + *
    + * @param {goog.vec.vec2f.Type} vec The destination vector.
    + * @param {goog.vec.vec2f.Type} src The source vector.
    + * @return {!goog.vec.vec2f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.setFromVec2f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2f vec from vec2d src (typed as a Float64Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec2f.Type} vec The destination vector.
    + * @param {Float64Array} src The source vector.
    + * @return {!goog.vec.vec2f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.setFromVec2d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec2f vec from Array src.
    + *
    + * @param {goog.vec.vec2f.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec2f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The first addend.
    + * @param {goog.vec.vec2f.Type} vec1 The second addend.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The minuend.
    + * @param {goog.vec.vec2f.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with the matching element of vec0
    + * storing the products into resultVec.
    + *
    + * @param {!goog.vec.vec2f.Type} vec0 The first vector.
    + * @param {!goog.vec.vec2f.Type} vec1 The second vector.
    + * @param {!goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.componentMultiply = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] * vec1[0];
    +  resultVec[1] = vec0[1] * vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Divides each component of vec0 with the matching element of vec0
    + * storing the divisor into resultVec.
    + *
    + * @param {!goog.vec.vec2f.Type} vec0 The first vector.
    + * @param {!goog.vec.vec2f.Type} vec1 The second vector.
    + * @param {!goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.componentDivide = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] / vec1[0];
    +  resultVec[1] = vec0[1] / vec1[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The source vector.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec2f.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec2f.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1];
    +  return Math.sqrt(x * x + y * y);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1];
    +  var ilen = 1 / Math.sqrt(x * x + y * y);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors vec0 and vec1.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The first vector.
    + * @param {goog.vec.vec2f.Type} vec1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec2f.dot = function(vec0, vec1) {
    +  return vec0[0] * vec1[0] + vec0[1] * vec1[1];
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 First point.
    + * @param {goog.vec.vec2f.Type} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.vec2f.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  return x * x + y * y;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 First point.
    + * @param {goog.vec.vec2f.Type} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.vec2f.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.vec2f.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 Origin point.
    + * @param {goog.vec.vec2f.Type} vec1 Target point.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var d = Math.sqrt(x * x + y * y);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to vec1 according to f. The value of f should
    + * be in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The first vector.
    + * @param {goog.vec.vec2f.Type} vec1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.lerp = function(vec0, vec1, f, resultVec) {
    +  var x = vec0[0], y = vec0[1];
    +  resultVec[0] = (vec1[0] - x) * f + x;
    +  resultVec[1] = (vec1[1] - y) * f + y;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The source vector.
    + * @param {goog.vec.vec2f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The source vector.
    + * @param {goog.vec.vec2f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec2f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec2f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec2f.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of vec0 are equal to the components of vec1.
    + *
    + * @param {goog.vec.vec2f.Type} vec0 The first vector.
    + * @param {goog.vec.vec2f.Type} vec1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec2f.equals = function(vec0, vec1) {
    +  return vec0.length == vec1.length &&
    +      vec0[0] == vec1[0] && vec0[1] == vec1[1];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec2f_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec2f_test.html
    new file mode 100644
    index 0000000000..ec6940dc39
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec2f_test.html
    @@ -0,0 +1,282 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec2d_test.html by running:     //
    +//   swap_type.sh vec2f_test.html > vec2d_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec2f</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.vec2f');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec2f.create();
    +    assertElementsEquals([0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec2f.create();
    +    goog.vec.vec2f.setFromValues(v, 1, 2);
    +    assertElementsEquals([1, 2], v);
    +
    +    goog.vec.vec2f.setFromArray(v, [4, 5]);
    +    assertElementsEquals([4, 5], v);
    +
    +    var w = goog.vec.vec2f.create();
    +    goog.vec.vec2f.setFromValues(w, 1, 2);
    +    assertElementsEquals([1, 2], w);
    +
    +    goog.vec.vec2f.setFromArray(w, [4, 5]);
    +    assertElementsEquals([4, 5], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [4, 5]);
    +    var v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +
    +    goog.vec.vec2f.add(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([5, 7], v2);
    +
    +    goog.vec.vec2f.add(goog.vec.vec2f.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [4, 5]);
    +    var v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +
    +    goog.vec.vec2f.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.vec2f.setFromValues(v2, 0, 0);
    +    goog.vec.vec2f.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3], v2);
    +
    +    v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +    goog.vec.vec2f.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3], v2);
    +
    +    goog.vec.vec2f.subtract(goog.vec.vec2f.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1], v2);
    +  }
    +
    +  function testMultiply() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [4, 5]);
    +    var v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +
    +    goog.vec.vec2f.componentMultiply(v2, v1, v2);
    +    assertElementsEquals([1, 2], v0);
    +    assertElementsEquals([4, 5], v1);
    +    assertElementsEquals([4, 10], v2);
    +
    +    goog.vec.vec2f.componentMultiply(goog.vec.vec2f.componentMultiply(v0, v1, v2), v0, v2);
    +    assertElementsEquals([4, 20], v2);
    +  }
    +
    +  function testDivide() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [4, 5]);
    +    var v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +
    +    goog.vec.vec2f.componentDivide(v2, v1, v2);
    +    assertElementsRoughlyEqual([1, 2], v0, 10e-5);
    +    assertElementsRoughlyEqual([4, 5], v1, 10e-5);
    +    assertElementsRoughlyEqual([.25, .4], v2, 10e-5);
    +
    +    goog.vec.vec2f.setFromValues(v2, 0, 0);
    +    goog.vec.vec2f.componentDivide(v1, v0, v2);
    +    assertElementsRoughlyEqual([4, 2.5], v2, 10e-5);
    +
    +    v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +    goog.vec.vec2f.componentDivide(v2, v1, v2);
    +    assertElementsRoughlyEqual([.25, .4], v2, 10e-5);
    +
    +    goog.vec.vec2f.componentDivide(goog.vec.vec2f.componentDivide(v1, v0, v2), v0, v2);
    +    assertElementsRoughlyEqual([4, 1.25], v2, 10e-5);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.create();
    +
    +    goog.vec.vec2f.negate(v0, v1);
    +    assertElementsEquals([-1, -2], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.vec2f.negate(v0, v0);
    +    assertElementsEquals([-1, -2], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [-1, -2]);
    +    var v1 = goog.vec.vec2f.create();
    +
    +    goog.vec.vec2f.abs(v0, v1);
    +    assertElementsEquals([1, 2], v1);
    +    assertElementsEquals([-1, -2], v0);
    +
    +    goog.vec.vec2f.abs(v0, v0);
    +    assertElementsEquals([1, 2], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.create();
    +
    +    goog.vec.vec2f.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8], v1);
    +    assertElementsEquals([1, 2], v0);
    +
    +    goog.vec.vec2f.setFromArray(v1, v0);
    +    goog.vec.vec2f.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    assertEquals(5, goog.vec.vec2f.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    assertEquals(Math.sqrt(5), goog.vec.vec2f.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [2, 3]);
    +    var v1 = goog.vec.vec2f.create();
    +    var v2 = goog.vec.vec2f.create();
    +    goog.vec.vec2f.scale(
    +        v0, 1 / goog.vec.vec2f.magnitude(v0), v2);
    +
    +    goog.vec.vec2f.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3], v0);
    +
    +    goog.vec.vec2f.setFromArray(v1, v0);
    +    goog.vec.vec2f.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [1, 2]);
    +    var v1 = goog.vec.vec2f.setFromArray(goog.vec.vec2f.create(), [4, 5]);
    +    assertEquals(14, goog.vec.vec2f.dot(v0, v1));
    +    assertEquals(14, goog.vec.vec2f.dot(v1, v0));
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    assertEquals(0, goog.vec.vec2f.distanceSquared(v0, v1));
    +    goog.vec.vec2f.setFromValues(v0, 1, 2);
    +    goog.vec.vec2f.setFromValues(v1, -1, -2);
    +    assertEquals(20, goog.vec.vec2f.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    assertEquals(0, goog.vec.vec2f.distance(v0, v1));
    +    goog.vec.vec2f.setFromValues(v0, 2, 3);
    +    goog.vec.vec2f.setFromValues(v1, -2, 0);
    +    assertEquals(5, goog.vec.vec2f.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var dirVec = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 4, 5);
    +    goog.vec.vec2f.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0], dirVec);
    +    goog.vec.vec2f.setFromValues(v0, 0, 0);
    +    goog.vec.vec2f.setFromValues(v1, 1, 0);
    +    goog.vec.vec2f.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0], dirVec);
    +    goog.vec.vec2f.setFromValues(v0, 1, 1);
    +    goog.vec.vec2f.setFromValues(v1, 0, 0);
    +    goog.vec.vec2f.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.707106781, -0.707106781],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 10, 20);
    +    var v2 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +
    +    goog.vec.vec2f.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2], v2);
    +    goog.vec.vec2f.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20], v2);
    +    goog.vec.vec2f.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 10, 20);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 5, 25);
    +    var v2 = goog.vec.vec2f.create();
    +
    +    goog.vec.vec2f.max(v0, v1, v2);
    +    assertElementsEquals([10, 25], v2);
    +    goog.vec.vec2f.max(v1, v0, v1);
    +    assertElementsEquals([10, 25], v1);
    +    goog.vec.vec2f.max(v2, 20, v2);
    +    assertElementsEquals([20, 25], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 10, 20);
    +    var v1 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 5, 25);
    +    var v2 = goog.vec.vec2f.create();
    +
    +    goog.vec.vec2f.min(v0, v1, v2);
    +    assertElementsEquals([5, 20], v2);
    +    goog.vec.vec2f.min(v1, v0, v1);
    +    assertElementsEquals([5, 20], v1);
    +    goog.vec.vec2f.min(v2, 10, v2);
    +    assertElementsEquals([5, 10], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec2f.setFromValues(goog.vec.vec2f.create(), 1, 2);
    +    var v1 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.vec2f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec2f.setFromVec2f(goog.vec.vec2f.create(), v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.vec2f.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3.js b/mapbox-gl/closure-library/closure/goog/vec/vec3.js
    new file mode 100644
    index 0000000000..4528887c48
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3.js
    @@ -0,0 +1,542 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Supplies 3 element vectors that are compatible with WebGL.
    + * Each element is a float32 since that is typically the desired size of a
    + * 3-vector in the GPU.  The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted.
    + *
    + */
    +goog.provide('goog.vec.Vec3');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Vec3.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Vec3.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Vec3.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Vec3.AnyType;
    +
    +// The following two types are deprecated - use the above types instead.
    +/** @typedef {Float32Array} */ goog.vec.Vec3.Type;
    +/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec3.Vec3Like;
    +
    +
    +/**
    + * Creates a 3 element vector of Float32. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec3.Float32} The new 3 element array.
    + */
    +goog.vec.Vec3.createFloat32 = function() {
    +  return new Float32Array(3);
    +};
    +
    +
    +/**
    + * Creates a 3 element vector of Float64. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec3.Float64} The new 3 element array.
    + */
    +goog.vec.Vec3.createFloat64 = function() {
    +  return new Float64Array(3);
    +};
    +
    +
    +/**
    + * Creates a 3 element vector of Number. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec3.Number} The new 3 element array.
    + */
    +goog.vec.Vec3.createNumber = function() {
    +  var a = new Array(3);
    +  goog.vec.Vec3.setFromValues(a, 0, 0, 0);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates a 3 element vector of Float32Array. The array is initialized to zero.
    + *
    + * @deprecated Use createFloat32.
    + * @return {!goog.vec.Vec3.Type} The new 3 element array.
    + */
    +goog.vec.Vec3.create = function() {
    +  return new Float32Array(3);
    +};
    +
    +
    +/**
    + * Creates a new 3 element FLoat32 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec The source 3 element array.
    + * @return {!goog.vec.Vec3.Float32} The new 3 element array.
    + */
    +goog.vec.Vec3.createFloat32FromArray = function(vec) {
    +  var newVec = goog.vec.Vec3.createFloat32();
    +  goog.vec.Vec3.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Creates a new 3 element Float32 vector initialized with the supplied values.
    + *
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @return {!goog.vec.Vec3.Float32} The new vector.
    + */
    +goog.vec.Vec3.createFloat32FromValues = function(v0, v1, v2) {
    +  var a = goog.vec.Vec3.createFloat32();
    +  goog.vec.Vec3.setFromValues(a, v0, v1, v2);
    +  return a;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 3 element Float32 vector.
    + *
    + * @param {goog.vec.Vec3.Float32} vec The source 3 element vector.
    + * @return {!goog.vec.Vec3.Float32} The new cloned vector.
    + */
    +goog.vec.Vec3.cloneFloat32 = goog.vec.Vec3.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a new 3 element Float64 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec The source 3 element array.
    + * @return {!goog.vec.Vec3.Float64} The new 3 element array.
    + */
    +goog.vec.Vec3.createFloat64FromArray = function(vec) {
    +  var newVec = goog.vec.Vec3.createFloat64();
    +  goog.vec.Vec3.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    +* Creates a new 3 element Float64 vector initialized with the supplied values.
    +*
    +* @param {number} v0 The value for element at index 0.
    +* @param {number} v1 The value for element at index 1.
    +* @param {number} v2 The value for element at index 2.
    +* @return {!goog.vec.Vec3.Float64} The new vector.
    +*/
    +goog.vec.Vec3.createFloat64FromValues = function(v0, v1, v2) {
    +  var vec = goog.vec.Vec3.createFloat64();
    +  goog.vec.Vec3.setFromValues(vec, v0, v1, v2);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 3 element vector.
    + *
    + * @param {goog.vec.Vec3.Float64} vec The source 3 element vector.
    + * @return {!goog.vec.Vec3.Float64} The new cloned vector.
    + */
    +goog.vec.Vec3.cloneFloat64 = goog.vec.Vec3.createFloat64FromArray;
    +
    +
    +/**
    + * Creates a new 3 element vector initialized with the value from the given
    + * array.
    + *
    + * @deprecated Use createFloat32FromArray.
    + * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element array.
    + * @return {!goog.vec.Vec3.Type} The new 3 element array.
    + */
    +goog.vec.Vec3.createFromArray = function(vec) {
    +  var newVec = goog.vec.Vec3.create();
    +  goog.vec.Vec3.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Creates a new 3 element vector initialized with the supplied values.
    + *
    + * @deprecated Use createFloat32FromValues.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @return {!goog.vec.Vec3.Type} The new vector.
    + */
    +goog.vec.Vec3.createFromValues = function(v0, v1, v2) {
    +  var vec = goog.vec.Vec3.create();
    +  goog.vec.Vec3.setFromValues(vec, v0, v1, v2);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 3 element vector.
    + *
    + * @deprecated Use cloneFloat32.
    + * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element vector.
    + * @return {!goog.vec.Vec3.Type} The new cloned vector.
    + */
    +goog.vec.Vec3.clone = function(vec) {
    +  var newVec = goog.vec.Vec3.create();
    +  goog.vec.Vec3.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @return {!goog.vec.Vec3.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.setFromValues = function(vec, v0, v1, v2) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes the vector with the given array of values.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec The vector to receive the
    + *     values.
    + * @param {goog.vec.Vec3.AnyType} values The array of values.
    + * @return {!goog.vec.Vec3.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.setFromArray = function(vec, values) {
    +  vec[0] = values[0];
    +  vec[1] = values[1];
    +  vec[2] = values[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The first addend.
    + * @param {goog.vec.Vec3.AnyType} vec1 The second addend.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The minuend.
    + * @param {goog.vec.Vec3.AnyType} vec1 The subtrahend.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector to negate.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec3.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec3.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return Math.sqrt(x * x + y * y + z * z);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The vector to normalize.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.normalize = function(vec0, resultVec) {
    +  var ilen = 1 / goog.vec.Vec3.magnitude(vec0);
    +  resultVec[0] = vec0[0] * ilen;
    +  resultVec[1] = vec0[1] * ilen;
    +  resultVec[2] = vec0[2] * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.Vec3.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec3.AnyType} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.Vec3.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
    +};
    +
    +
    +/**
    + * Computes the vector (cross) product of v0 and v1 storing the result into
    + * resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec3.AnyType} v1 The second vector.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
    + *     results. May be either v0 or v1.
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.cross = function(v0, v1, resultVec) {
    +  var x0 = v0[0], y0 = v0[1], z0 = v0[2];
    +  var x1 = v1[0], y1 = v1[1], z1 = v1[2];
    +  resultVec[0] = y0 * z1 - z0 * y1;
    +  resultVec[1] = z0 * x1 - x0 * z1;
    +  resultVec[2] = x0 * y1 - y0 * x1;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 First point.
    + * @param {goog.vec.Vec3.AnyType} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.Vec3.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  var z = vec0[2] - vec1[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 First point.
    + * @param {goog.vec.Vec3.AnyType} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.Vec3.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.Vec3.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 Origin point.
    + * @param {goog.vec.Vec3.AnyType} vec1 Target point.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var z = vec1[2] - vec0[2];
    +  var d = Math.sqrt(x * x + y * y + z * z);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +    resultVec[2] = z * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = resultVec[2] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.Vec3.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec3.AnyType} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec3.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.Vec3.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec3.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec3.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.Vec3.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec3.AnyType} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.Vec3.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec3_test.html
    new file mode 100644
    index 0000000000..c2e26aaef9
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3_test.html
    @@ -0,0 +1,296 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Vec3</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Vec3');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testDeprecatedConstructor() {
    +    var v = goog.vec.Vec3.create();
    +    assertElementsEquals(0, v[0]);
    +    assertEquals(0, v[1]);
    +    assertEquals(0, v[2]);
    +
    +    assertElementsEquals([0, 0, 0], goog.vec.Vec3.create());
    +
    +    assertElementsEquals([1, 2, 3], goog.vec.Vec3.createFromValues(1, 2, 3));
    +
    +    assertElementsEquals([1, 2, 3], goog.vec.Vec3.createFromArray([1, 2, 3]));
    +
    +    v = goog.vec.Vec3.createFromValues(1, 2, 3);
    +    assertElementsEquals([1, 2, 3], goog.vec.Vec3.clone(v));
    +  }
    +
    +  function testConstructor() {
    +    var v = goog.vec.Vec3.createFloat32();
    +    assertElementsEquals(0, v[0]);
    +    assertEquals(0, v[1]);
    +    assertEquals(0, v[2]);
    +
    +    assertElementsEquals([0, 0, 0], goog.vec.Vec3.createFloat32());
    +
    +    goog.vec.Vec3.setFromValues(v, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], v);
    +
    +    var w = goog.vec.Vec3.createFloat64();
    +    assertElementsEquals(0, w[0]);
    +    assertEquals(0, w[1]);
    +    assertEquals(0, w[2]);
    +
    +    assertElementsEquals([0, 0, 0], goog.vec.Vec3.createFloat64());
    +
    +    goog.vec.Vec3.setFromValues(w, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], w);
    +  }
    +
    +
    +  function testSet() {
    +    var v = goog.vec.Vec3.createFloat32();
    +    goog.vec.Vec3.setFromValues(v, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], v);
    +
    +    goog.vec.Vec3.setFromArray(v, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], v);
    +
    +    var w = goog.vec.Vec3.createFloat32();
    +    goog.vec.Vec3.setFromValues(w, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], w);
    +
    +    goog.vec.Vec3.setFromArray(w, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32FromArray([4, 5, 6]);
    +    var v2 = goog.vec.Vec3.cloneFloat32(v0);
    +
    +    goog.vec.Vec3.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([5, 7, 9], v2);
    +
    +    goog.vec.Vec3.add(goog.vec.Vec3.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9, 12], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32FromArray([4, 5, 6]);
    +    var v2 = goog.vec.Vec3.cloneFloat32(v0);
    +
    +    goog.vec.Vec3.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.Vec3.setFromValues(v2, 0, 0, 0);
    +    goog.vec.Vec3.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3, 3], v2);
    +
    +    v2 = goog.vec.Vec3.cloneFloat32(v0);
    +    goog.vec.Vec3.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.Vec3.subtract(goog.vec.Vec3.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1, 0], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32();
    +
    +    goog.vec.Vec3.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.Vec3.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(-1, -2, -3);
    +    var v1 = goog.vec.Vec3.createFloat32();
    +
    +    goog.vec.Vec3.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3], v1);
    +    assertElementsEquals([-1, -2, -3], v0);
    +
    +    goog.vec.Vec3.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32();
    +
    +    goog.vec.Vec3.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.Vec3.setFromArray(v1, v0);
    +    goog.vec.Vec3.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    assertEquals(14, goog.vec.Vec3.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    assertEquals(Math.sqrt(14), goog.vec.Vec3.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([2, 3, 4]);
    +    var v1 = goog.vec.Vec3.create();
    +    var v2 = goog.vec.Vec3.create();
    +    goog.vec.Vec3.scale(
    +        v0, 1 / goog.vec.Vec3.magnitude(v0), v2);
    +
    +    goog.vec.Vec3.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4], v0);
    +
    +    goog.vec.Vec3.setFromArray(v1, v0);
    +    goog.vec.Vec3.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32FromArray([4, 5, 6]);
    +    assertEquals(32, goog.vec.Vec3.dot(v0, v1));
    +    assertEquals(32, goog.vec.Vec3.dot(v1, v0));
    +  }
    +
    +  function testCross() {
    +    var v0 = goog.vec.Vec3.createFloat32FromArray([1, 2, 3]);
    +    var v1 = goog.vec.Vec3.createFloat32FromArray([4, 5, 6]);
    +    var crossVec = goog.vec.Vec3.create();
    +
    +    goog.vec.Vec3.cross(v0, v1, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, 6, -3], crossVec);
    +
    +    goog.vec.Vec3.setFromArray(crossVec, v1);
    +    goog.vec.Vec3.cross(crossVec, v0, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([3, -6, 3], crossVec);
    +
    +    goog.vec.Vec3.cross(v0, v0, v0);
    +    assertElementsEquals([0, 0, 0], v0);
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    assertEquals(0, goog.vec.Vec3.distanceSquared(v0, v1));
    +    goog.vec.Vec3.setFromValues(v0, 1, 2, 3);
    +    goog.vec.Vec3.setFromValues(v1, -1, -2, -1);
    +    assertEquals(36, goog.vec.Vec3.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    assertEquals(0, goog.vec.Vec3.distance(v0, v1));
    +    goog.vec.Vec3.setFromValues(v0, 1, 2, 3);
    +    goog.vec.Vec3.setFromValues(v1, -1, -2, -1);
    +    assertEquals(6, goog.vec.Vec3.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var dirVec = goog.vec.Vec3.createFloat32FromValues(4, 5, 6);
    +    goog.vec.Vec3.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0, 0], dirVec);
    +    goog.vec.Vec3.setFromValues(v0, 0, 0, 0);
    +    goog.vec.Vec3.setFromValues(v1, 1, 0, 0);
    +    goog.vec.Vec3.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0, 0], dirVec);
    +    goog.vec.Vec3.setFromValues(v0, 1, 1, 1);
    +    goog.vec.Vec3.setFromValues(v1, 0, 0, 0);
    +    goog.vec.Vec3.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.5773502588272095, -0.5773502588272095, -0.5773502588272095],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(10, 20, 30);
    +    var v2 = goog.vec.Vec3.cloneFloat32(v0);
    +
    +    goog.vec.Vec3.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3], v2);
    +    goog.vec.Vec3.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30], v2);
    +    goog.vec.Vec3.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(10, 20, 30);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(5, 25, 35);
    +    var v2 = goog.vec.Vec3.createFloat32();
    +
    +    goog.vec.Vec3.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35], v2);
    +    goog.vec.Vec3.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35], v1);
    +    goog.vec.Vec3.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(10, 20, 30);
    +    var v1 = goog.vec.Vec3.createFloat32FromValues(5, 25, 35);
    +    var v2 = goog.vec.Vec3.createFloat32();
    +
    +    goog.vec.Vec3.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30], v2);
    +    goog.vec.Vec3.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30], v1);
    +    goog.vec.Vec3.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.Vec3.createFloat32FromValues(1, 2, 3);
    +    var v1 = goog.vec.Vec3.cloneFloat32(v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.Vec3.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec3.cloneFloat32(v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.Vec3.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec3.cloneFloat32(v0);
    +    v1[2] = 4;
    +    assertFalse(goog.vec.Vec3.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3d.js b/mapbox-gl/closure-library/closure/goog/vec/vec3d.js
    new file mode 100644
    index 0000000000..97e8d77091
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3d.js
    @@ -0,0 +1,426 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec3f.js by running:            //
    +//   swap_type.sh vec3d.js > vec3f.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 3 element double (64bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.vec3d');
    +goog.provide('goog.vec.vec3d.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float64} */ goog.vec.vec3d.Type;
    +
    +
    +/**
    + * Creates a vec3d with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec3d.Type} The new vec3d.
    + */
    +goog.vec.vec3d.create = function() {
    +  return new Float64Array(3);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec3d.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @return {!goog.vec.vec3d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.setFromValues = function(vec, v0, v1, v2) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3d vec from vec3d src.
    + *
    + * @param {goog.vec.vec3d.Type} vec The destination vector.
    + * @param {goog.vec.vec3d.Type} src The source vector.
    + * @return {!goog.vec.vec3d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.setFromVec3d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3d vec from vec3f src (typed as a Float32Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec3d.Type} vec The destination vector.
    + * @param {Float32Array} src The source vector.
    + * @return {!goog.vec.vec3d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.setFromVec3f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3d vec from Array src.
    + *
    + * @param {goog.vec.vec3d.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec3d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The first addend.
    + * @param {goog.vec.vec3d.Type} vec1 The second addend.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The minuend.
    + * @param {goog.vec.vec3d.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The source vector.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec3d.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec3d.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return Math.sqrt(x * x + y * y + z * z);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  var ilen = 1 / Math.sqrt(x * x + y * y + z * z);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  resultVec[2] = z * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.vec3d.Type} v0 The first vector.
    + * @param {goog.vec.vec3d.Type} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec3d.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
    +};
    +
    +
    +/**
    + * Computes the vector (cross) product of v0 and v1 storing the result into
    + * resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} v0 The first vector.
    + * @param {goog.vec.vec3d.Type} v1 The second vector.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the
    + *     results. May be either v0 or v1.
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.cross = function(v0, v1, resultVec) {
    +  var x0 = v0[0], y0 = v0[1], z0 = v0[2];
    +  var x1 = v1[0], y1 = v1[1], z1 = v1[2];
    +  resultVec[0] = y0 * z1 - z0 * y1;
    +  resultVec[1] = z0 * x1 - x0 * z1;
    +  resultVec[2] = x0 * y1 - y0 * x1;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 First point.
    + * @param {goog.vec.vec3d.Type} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.vec3d.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  var z = vec0[2] - vec1[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 First point.
    + * @param {goog.vec.vec3d.Type} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.vec3d.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.vec3d.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 Origin point.
    + * @param {goog.vec.vec3d.Type} vec1 Target point.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var z = vec1[2] - vec0[2];
    +  var d = Math.sqrt(x * x + y * y + z * z);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +    resultVec[2] = z * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = resultVec[2] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec3d.Type} v0 The first vector.
    + * @param {goog.vec.vec3d.Type} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The source vector.
    + * @param {goog.vec.vec3d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec3d.Type} vec0 The source vector.
    + * @param {goog.vec.vec3d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec3d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec3d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3d.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.vec3d.Type} v0 The first vector.
    + * @param {goog.vec.vec3d.Type} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec3d.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3d_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec3d_test.html
    new file mode 100644
    index 0000000000..305b485f80
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3d_test.html
    @@ -0,0 +1,270 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec3f_test.html by running:     //
    +//   swap_type.sh vec3d_test.html > vec3f_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec3d</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float64Array');
    +  goog.require('goog.vec.vec3d');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec3d.create();
    +    assertElementsEquals([0, 0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec3d.create();
    +    goog.vec.vec3d.setFromValues(v, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], v);
    +
    +    goog.vec.vec3d.setFromArray(v, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], v);
    +
    +    var w = goog.vec.vec3d.create();
    +    goog.vec.vec3d.setFromValues(w, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], w);
    +
    +    goog.vec.vec3d.setFromArray(w, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [4, 5, 6]);
    +    var v2 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +
    +    goog.vec.vec3d.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([5, 7, 9], v2);
    +
    +    goog.vec.vec3d.add(goog.vec.vec3d.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9, 12], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [4, 5, 6]);
    +    var v2 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +
    +    goog.vec.vec3d.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.vec3d.setFromValues(v2, 0, 0, 0);
    +    goog.vec.vec3d.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3, 3], v2);
    +
    +    v2 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +    goog.vec.vec3d.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.vec3d.subtract(goog.vec.vec3d.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1, 0], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.vec3d.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [-1, -2, -3]);
    +    var v1 = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3], v1);
    +    assertElementsEquals([-1, -2, -3], v0);
    +
    +    goog.vec.vec3d.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.vec3d.setFromArray(v1, v0);
    +    goog.vec.vec3d.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    assertEquals(14, goog.vec.vec3d.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    assertEquals(Math.sqrt(14), goog.vec.vec3d.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [2, 3, 4]);
    +    var v1 = goog.vec.vec3d.create();
    +    var v2 = goog.vec.vec3d.create();
    +    goog.vec.vec3d.scale(
    +        v0, 1 / goog.vec.vec3d.magnitude(v0), v2);
    +
    +    goog.vec.vec3d.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4], v0);
    +
    +    goog.vec.vec3d.setFromArray(v1, v0);
    +    goog.vec.vec3d.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [4, 5, 6]);
    +    assertEquals(32, goog.vec.vec3d.dot(v0, v1));
    +    assertEquals(32, goog.vec.vec3d.dot(v1, v0));
    +  }
    +
    +  function testCross() {
    +    var v0 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3d.setFromArray(goog.vec.vec3d.create(), [4, 5, 6]);
    +    var crossVec = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.cross(v0, v1, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, 6, -3], crossVec);
    +
    +    goog.vec.vec3d.setFromArray(crossVec, v1);
    +    goog.vec.vec3d.cross(crossVec, v0, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([3, -6, 3], crossVec);
    +
    +    goog.vec.vec3d.cross(v0, v0, v0);
    +    assertElementsEquals([0, 0, 0], v0);
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    assertEquals(0, goog.vec.vec3d.distanceSquared(v0, v1));
    +    goog.vec.vec3d.setFromValues(v0, 1, 2, 3);
    +    goog.vec.vec3d.setFromValues(v1, -1, -2, -1);
    +    assertEquals(36, goog.vec.vec3d.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    assertEquals(0, goog.vec.vec3d.distance(v0, v1));
    +    goog.vec.vec3d.setFromValues(v0, 1, 2, 3);
    +    goog.vec.vec3d.setFromValues(v1, -1, -2, -1);
    +    assertEquals(6, goog.vec.vec3d.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var dirVec = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 4, 5, 6);
    +    goog.vec.vec3d.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0, 0], dirVec);
    +    goog.vec.vec3d.setFromValues(v0, 0, 0, 0);
    +    goog.vec.vec3d.setFromValues(v1, 1, 0, 0);
    +    goog.vec.vec3d.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0, 0], dirVec);
    +    goog.vec.vec3d.setFromValues(v0, 1, 1, 1);
    +    goog.vec.vec3d.setFromValues(v1, 0, 0, 0);
    +    goog.vec.vec3d.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.5773502588272095, -0.5773502588272095, -0.5773502588272095],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 10, 20, 30);
    +    var v2 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +
    +    goog.vec.vec3d.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3], v2);
    +    goog.vec.vec3d.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30], v2);
    +    goog.vec.vec3d.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 10, 20, 30);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 5, 25, 35);
    +    var v2 = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35], v2);
    +    goog.vec.vec3d.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35], v1);
    +    goog.vec.vec3d.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 10, 20, 30);
    +    var v1 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 5, 25, 35);
    +    var v2 = goog.vec.vec3d.create();
    +
    +    goog.vec.vec3d.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30], v2);
    +    goog.vec.vec3d.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30], v1);
    +    goog.vec.vec3d.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec3d.setFromValues(goog.vec.vec3d.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.vec3d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.vec3d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec3d.setFromVec3d(goog.vec.vec3d.create(), v0);
    +    v1[2] = 4;
    +    assertFalse(goog.vec.vec3d.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3f.js b/mapbox-gl/closure-library/closure/goog/vec/vec3f.js
    new file mode 100644
    index 0000000000..8048ec7ab4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3f.js
    @@ -0,0 +1,426 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec3d.js by running:            //
    +//   swap_type.sh vec3f.js > vec3d.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 3 element float (32bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.vec3f');
    +goog.provide('goog.vec.vec3f.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.vec3f.Type;
    +
    +
    +/**
    + * Creates a vec3f with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec3f.Type} The new vec3f.
    + */
    +goog.vec.vec3f.create = function() {
    +  return new Float32Array(3);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec3f.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @return {!goog.vec.vec3f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.setFromValues = function(vec, v0, v1, v2) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3f vec from vec3f src.
    + *
    + * @param {goog.vec.vec3f.Type} vec The destination vector.
    + * @param {goog.vec.vec3f.Type} src The source vector.
    + * @return {!goog.vec.vec3f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.setFromVec3f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3f vec from vec3d src (typed as a Float64Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec3f.Type} vec The destination vector.
    + * @param {Float64Array} src The source vector.
    + * @return {!goog.vec.vec3f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.setFromVec3d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec3f vec from Array src.
    + *
    + * @param {goog.vec.vec3f.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec3f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The first addend.
    + * @param {goog.vec.vec3f.Type} vec1 The second addend.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The minuend.
    + * @param {goog.vec.vec3f.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The source vector.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec3f.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec3f.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  return Math.sqrt(x * x + y * y + z * z);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2];
    +  var ilen = 1 / Math.sqrt(x * x + y * y + z * z);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  resultVec[2] = z * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.vec3f.Type} v0 The first vector.
    + * @param {goog.vec.vec3f.Type} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec3f.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
    +};
    +
    +
    +/**
    + * Computes the vector (cross) product of v0 and v1 storing the result into
    + * resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} v0 The first vector.
    + * @param {goog.vec.vec3f.Type} v1 The second vector.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the
    + *     results. May be either v0 or v1.
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.cross = function(v0, v1, resultVec) {
    +  var x0 = v0[0], y0 = v0[1], z0 = v0[2];
    +  var x1 = v1[0], y1 = v1[1], z1 = v1[2];
    +  resultVec[0] = y0 * z1 - z0 * y1;
    +  resultVec[1] = z0 * x1 - x0 * z1;
    +  resultVec[2] = x0 * y1 - y0 * x1;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the squared distance between two points.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 First point.
    + * @param {goog.vec.vec3f.Type} vec1 Second point.
    + * @return {number} The squared distance between the points.
    + */
    +goog.vec.vec3f.distanceSquared = function(vec0, vec1) {
    +  var x = vec0[0] - vec1[0];
    +  var y = vec0[1] - vec1[1];
    +  var z = vec0[2] - vec1[2];
    +  return x * x + y * y + z * z;
    +};
    +
    +
    +/**
    + * Returns the distance between two points.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 First point.
    + * @param {goog.vec.vec3f.Type} vec1 Second point.
    + * @return {number} The distance between the points.
    + */
    +goog.vec.vec3f.distance = function(vec0, vec1) {
    +  return Math.sqrt(goog.vec.vec3f.distanceSquared(vec0, vec1));
    +};
    +
    +
    +/**
    + * Returns a unit vector pointing from one point to another.
    + * If the input points are equal then the result will be all zeros.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 Origin point.
    + * @param {goog.vec.vec3f.Type} vec1 Target point.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or vec1).
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.direction = function(vec0, vec1, resultVec) {
    +  var x = vec1[0] - vec0[0];
    +  var y = vec1[1] - vec0[1];
    +  var z = vec1[2] - vec0[2];
    +  var d = Math.sqrt(x * x + y * y + z * z);
    +  if (d) {
    +    d = 1 / d;
    +    resultVec[0] = x * d;
    +    resultVec[1] = y * d;
    +    resultVec[2] = z * d;
    +  } else {
    +    resultVec[0] = resultVec[1] = resultVec[2] = 0;
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Linearly interpolate from vec0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec3f.Type} v0 The first vector.
    + * @param {goog.vec.vec3f.Type} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The source vector.
    + * @param {goog.vec.vec3f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec3f.Type} vec0 The source vector.
    + * @param {goog.vec.vec3f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec3f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec3f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec3f.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.vec3f.Type} v0 The first vector.
    + * @param {goog.vec.vec3f.Type} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec3f.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec3f_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec3f_test.html
    new file mode 100644
    index 0000000000..32fe1dccb2
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec3f_test.html
    @@ -0,0 +1,270 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec3d_test.html by running:     //
    +//   swap_type.sh vec3f_test.html > vec3d_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec3f</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.vec3f');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec3f.create();
    +    assertElementsEquals([0, 0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec3f.create();
    +    goog.vec.vec3f.setFromValues(v, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], v);
    +
    +    goog.vec.vec3f.setFromArray(v, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], v);
    +
    +    var w = goog.vec.vec3f.create();
    +    goog.vec.vec3f.setFromValues(w, 1, 2, 3);
    +    assertElementsEquals([1, 2, 3], w);
    +
    +    goog.vec.vec3f.setFromArray(w, [4, 5, 6]);
    +    assertElementsEquals([4, 5, 6], w);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [4, 5, 6]);
    +    var v2 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +
    +    goog.vec.vec3f.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([5, 7, 9], v2);
    +
    +    goog.vec.vec3f.add(goog.vec.vec3f.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([6, 9, 12], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [4, 5, 6]);
    +    var v2 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +
    +    goog.vec.vec3f.subtract(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.vec3f.setFromValues(v2, 0, 0, 0);
    +    goog.vec.vec3f.subtract(v1, v0, v2);
    +    assertElementsEquals([3, 3, 3], v2);
    +
    +    v2 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +    goog.vec.vec3f.subtract(v2, v1, v2);
    +    assertElementsEquals([-3, -3, -3], v2);
    +
    +    goog.vec.vec3f.subtract(goog.vec.vec3f.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([2, 1, 0], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.vec3f.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [-1, -2, -3]);
    +    var v1 = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3], v1);
    +    assertElementsEquals([-1, -2, -3], v0);
    +
    +    goog.vec.vec3f.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12], v1);
    +    assertElementsEquals([1, 2, 3], v0);
    +
    +    goog.vec.vec3f.setFromArray(v1, v0);
    +    goog.vec.vec3f.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    assertEquals(14, goog.vec.vec3f.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    assertEquals(Math.sqrt(14), goog.vec.vec3f.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [2, 3, 4]);
    +    var v1 = goog.vec.vec3f.create();
    +    var v2 = goog.vec.vec3f.create();
    +    goog.vec.vec3f.scale(
    +        v0, 1 / goog.vec.vec3f.magnitude(v0), v2);
    +
    +    goog.vec.vec3f.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4], v0);
    +
    +    goog.vec.vec3f.setFromArray(v1, v0);
    +    goog.vec.vec3f.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [4, 5, 6]);
    +    assertEquals(32, goog.vec.vec3f.dot(v0, v1));
    +    assertEquals(32, goog.vec.vec3f.dot(v1, v0));
    +  }
    +
    +  function testCross() {
    +    var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [1, 2, 3]);
    +    var v1 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [4, 5, 6]);
    +    var crossVec = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.cross(v0, v1, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([-3, 6, -3], crossVec);
    +
    +    goog.vec.vec3f.setFromArray(crossVec, v1);
    +    goog.vec.vec3f.cross(crossVec, v0, crossVec);
    +    assertElementsEquals([1, 2, 3], v0);
    +    assertElementsEquals([4, 5, 6], v1);
    +    assertElementsEquals([3, -6, 3], crossVec);
    +
    +    goog.vec.vec3f.cross(v0, v0, v0);
    +    assertElementsEquals([0, 0, 0], v0);
    +  }
    +
    +  function testDistanceSquared() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    assertEquals(0, goog.vec.vec3f.distanceSquared(v0, v1));
    +    goog.vec.vec3f.setFromValues(v0, 1, 2, 3);
    +    goog.vec.vec3f.setFromValues(v1, -1, -2, -1);
    +    assertEquals(36, goog.vec.vec3f.distanceSquared(v0, v1));
    +  }
    +
    +  function testDistance() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    assertEquals(0, goog.vec.vec3f.distance(v0, v1));
    +    goog.vec.vec3f.setFromValues(v0, 1, 2, 3);
    +    goog.vec.vec3f.setFromValues(v1, -1, -2, -1);
    +    assertEquals(6, goog.vec.vec3f.distance(v0, v1));
    +  }
    +
    +  function testDirection() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var dirVec = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 4, 5, 6);
    +    goog.vec.vec3f.direction(v0, v1, dirVec);
    +    assertElementsEquals([0, 0, 0], dirVec);
    +    goog.vec.vec3f.setFromValues(v0, 0, 0, 0);
    +    goog.vec.vec3f.setFromValues(v1, 1, 0, 0);
    +    goog.vec.vec3f.direction(v0, v1, dirVec);
    +    assertElementsEquals([1, 0, 0], dirVec);
    +    goog.vec.vec3f.setFromValues(v0, 1, 1, 1);
    +    goog.vec.vec3f.setFromValues(v1, 0, 0, 0);
    +    goog.vec.vec3f.direction(v0, v1, dirVec);
    +    assertElementsRoughlyEqual(
    +        [-0.5773502588272095, -0.5773502588272095, -0.5773502588272095],
    +        dirVec, goog.vec.EPSILON);
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 10, 20, 30);
    +    var v2 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +
    +    goog.vec.vec3f.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3], v2);
    +    goog.vec.vec3f.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30], v2);
    +    goog.vec.vec3f.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 10, 20, 30);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 5, 25, 35);
    +    var v2 = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35], v2);
    +    goog.vec.vec3f.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35], v1);
    +    goog.vec.vec3f.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 10, 20, 30);
    +    var v1 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 5, 25, 35);
    +    var v2 = goog.vec.vec3f.create();
    +
    +    goog.vec.vec3f.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30], v2);
    +    goog.vec.vec3f.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30], v1);
    +    goog.vec.vec3f.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec3f.setFromValues(goog.vec.vec3f.create(), 1, 2, 3);
    +    var v1 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 4;
    +    assertFalse(goog.vec.vec3f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +    v1[1] = 4;
    +    assertFalse(goog.vec.vec3f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec3f.setFromVec3f(goog.vec.vec3f.create(), v0);
    +    v1[2] = 4;
    +    assertFalse(goog.vec.vec3f.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4.js b/mapbox-gl/closure-library/closure/goog/vec/vec4.js
    new file mode 100644
    index 0000000000..3936a08ff8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4.js
    @@ -0,0 +1,479 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Supplies 4 element vectors that are compatible with WebGL.
    + * Each element is a float32 since that is typically the desired size of a
    + * 4-vector in the GPU.  The API is structured to avoid unnecessary memory
    + * allocations.  The last parameter will typically be the output vector and
    + * an object can be both an input and output parameter to all methods except
    + * where noted.
    + *
    + */
    +goog.provide('goog.vec.Vec4');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.Vec4.Float32;
    +/** @typedef {goog.vec.Float64} */ goog.vec.Vec4.Float64;
    +/** @typedef {goog.vec.Number} */ goog.vec.Vec4.Number;
    +/** @typedef {goog.vec.AnyType} */ goog.vec.Vec4.AnyType;
    +
    +// The following two types are deprecated - use the above types instead.
    +/** @typedef {Float32Array} */ goog.vec.Vec4.Type;
    +/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec4.Vec4Like;
    +
    +
    +/**
    + * Creates a 4 element vector of Float32. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec4.Float32} The new 3 element array.
    + */
    +goog.vec.Vec4.createFloat32 = function() {
    +  return new Float32Array(4);
    +};
    +
    +
    +/**
    + * Creates a 4 element vector of Float64. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec4.Float64} The new 4 element array.
    + */
    +goog.vec.Vec4.createFloat64 = function() {
    +  return new Float64Array(4);
    +};
    +
    +
    +/**
    + * Creates a 4 element vector of Number. The array is initialized to zero.
    + *
    + * @return {!goog.vec.Vec4.Number} The new 4 element array.
    + */
    +goog.vec.Vec4.createNumber = function() {
    +  var v = new Array(4);
    +  goog.vec.Vec4.setFromValues(v, 0, 0, 0, 0);
    +  return v;
    +};
    +
    +
    +/**
    + * Creates a 4 element vector of Float32Array. The array is initialized to zero.
    + *
    + * @deprecated Use createFloat32.
    + * @return {!goog.vec.Vec4.Type} The new 4 element array.
    + */
    +goog.vec.Vec4.create = function() {
    +  return new Float32Array(4);
    +};
    +
    +
    +/**
    + * Creates a new 4 element vector initialized with the value from the given
    + * array.
    + *
    + * @deprecated Use createFloat32FromArray.
    + * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element array.
    + * @return {!goog.vec.Vec4.Type} The new 4 element array.
    + */
    +goog.vec.Vec4.createFromArray = function(vec) {
    +  var newVec = goog.vec.Vec4.create();
    +  goog.vec.Vec4.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Creates a new 4 element FLoat32 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec The source 3 element array.
    + * @return {!goog.vec.Vec4.Float32} The new 3 element array.
    + */
    +goog.vec.Vec4.createFloat32FromArray = function(vec) {
    +  var newVec = goog.vec.Vec4.createFloat32();
    +  goog.vec.Vec4.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    + * Creates a new 4 element Float32 vector initialized with the supplied values.
    + *
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Vec4.Float32} The new vector.
    + */
    +goog.vec.Vec4.createFloat32FromValues = function(v0, v1, v2, v3) {
    +  var vec = goog.vec.Vec4.createFloat32();
    +  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 4 element Float32 vector.
    + *
    + * @param {goog.vec.Vec4.Float32} vec The source 3 element vector.
    + * @return {!goog.vec.Vec4.Float32} The new cloned vector.
    + */
    +goog.vec.Vec4.cloneFloat32 = goog.vec.Vec4.createFloat32FromArray;
    +
    +
    +/**
    + * Creates a new 4 element Float64 vector initialized with the value from the
    + * given array.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec The source 4 element array.
    + * @return {!goog.vec.Vec4.Float64} The new 4 element array.
    + */
    +goog.vec.Vec4.createFloat64FromArray = function(vec) {
    +  var newVec = goog.vec.Vec4.createFloat64();
    +  goog.vec.Vec4.setFromArray(newVec, vec);
    +  return newVec;
    +};
    +
    +
    +/**
    +* Creates a new 4 element Float64 vector initialized with the supplied values.
    +*
    +* @param {number} v0 The value for element at index 0.
    +* @param {number} v1 The value for element at index 1.
    +* @param {number} v2 The value for element at index 2.
    +* @param {number} v3 The value for element at index 3.
    +* @return {!goog.vec.Vec4.Float64} The new vector.
    +*/
    +goog.vec.Vec4.createFloat64FromValues = function(v0, v1, v2, v3) {
    +  var vec = goog.vec.Vec4.createFloat64();
    +  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 4 element vector.
    + *
    + * @param {goog.vec.Vec4.Float64} vec The source 4 element vector.
    + * @return {!goog.vec.Vec4.Float64} The new cloned vector.
    + */
    +goog.vec.Vec4.cloneFloat64 = goog.vec.Vec4.createFloat64FromArray;
    +
    +
    +/**
    + * Creates a new 4 element vector initialized with the supplied values.
    + *
    + * @deprecated Use createFloat32FromValues.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Vec4.Type} The new vector.
    + */
    +goog.vec.Vec4.createFromValues = function(v0, v1, v2, v3) {
    +  var vec = goog.vec.Vec4.create();
    +  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
    +  return vec;
    +};
    +
    +
    +/**
    + * Creates a clone of the given 4 element vector.
    + *
    + * @deprecated Use cloneFloat32.
    + * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element vector.
    + * @return {!goog.vec.Vec4.Type} The new cloned vector.
    + */
    +goog.vec.Vec4.clone = goog.vec.Vec4.createFromArray;
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.Vec4.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.setFromValues = function(vec, v0, v1, v2, v3) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  vec[3] = v3;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes the vector with the given array of values.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the
    + *     values.
    + * @param {goog.vec.Vec4.AnyType} values The array of values.
    + * @return {!goog.vec.Vec4.AnyType} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.setFromArray = function(vec, values) {
    +  vec[0] = values[0];
    +  vec[1] = values[1];
    +  vec[2] = values[2];
    +  vec[3] = values[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The first addend.
    + * @param {goog.vec.Vec4.AnyType} vec1 The second addend.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  resultVec[3] = vec0[3] + vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The minuend.
    + * @param {goog.vec.Vec4.AnyType} vec1 The subtrahend.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  resultVec[3] = vec0[3] - vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector to negate.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  resultVec[3] = -vec0[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  resultVec[3] = Math.abs(vec0[3]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  resultVec[3] = vec0[3] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec4.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return x * x + y * y + z * z + w * w;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.Vec4.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return Math.sqrt(x * x + y * y + z * z + w * w);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The vector to normalize.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.normalize = function(vec0, resultVec) {
    +  var ilen = 1 / goog.vec.Vec4.magnitude(vec0);
    +  resultVec[0] = vec0[0] * ilen;
    +  resultVec[1] = vec0[1] * ilen;
    +  resultVec[2] = vec0[2] * ilen;
    +  resultVec[3] = vec0[3] * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.Vec4.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec4.AnyType} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.Vec4.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3];
    +};
    +
    +
    +/**
    + * Linearly interpolate from v0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.Vec4.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec4.AnyType} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2], w = v0[3];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  resultVec[3] = (v1[3] - w) * f + w;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec4.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +    resultVec[3] = Math.max(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +    resultVec[3] = Math.max(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.Vec4.AnyType} vec0 The source vector.
    + * @param {goog.vec.Vec4.AnyType|number} limit The limit vector or scalar.
    + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.Vec4.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +    resultVec[3] = Math.min(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +    resultVec[3] = Math.min(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.Vec4.AnyType} v0 The first vector.
    + * @param {goog.vec.Vec4.AnyType} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.Vec4.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2] && v0[3] == v1[3];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec4_test.html
    new file mode 100644
    index 0000000000..e66ac6df1c
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4_test.html
    @@ -0,0 +1,230 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.Vec4</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.Vec4');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testDeprecatedConstructor() {
    +    assertElementsEquals([0, 0, 0, 0], goog.vec.Vec4.create());
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFromValues(1, 2, 3, 4));
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFromArray([1, 2, 3, 4]));
    +
    +    var v = goog.vec.Vec4.createFromValues(1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], goog.vec.Vec4.clone(v));
    +  }
    +
    +  function testConstructor() {
    +    assertElementsEquals([0, 0, 0, 0], goog.vec.Vec4.createFloat32());
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFloat32FromValues(1, 2, 3, 4));
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]));
    +
    +    var v = goog.vec.Vec4.createFloat32FromValues(1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], goog.vec.Vec4.cloneFloat32(v));
    +
    +    assertElementsEquals([0, 0, 0, 0], goog.vec.Vec4.createFloat64());
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFloat64FromValues(1, 2, 3, 4));
    +
    +    assertElementsEquals([1, 2, 3, 4],
    +        goog.vec.Vec4.createFloat64FromArray([1, 2, 3, 4]));
    +
    +    var w = goog.vec.Vec4.createFloat64FromValues(1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], goog.vec.Vec4.cloneFloat64(w));
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.Vec4.createFloat32();
    +    goog.vec.Vec4.setFromValues(v, 1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], v);
    +
    +    goog.vec.Vec4.setFromArray(v, [4, 5, 6, 7]);
    +    assertElementsEquals([4, 5, 6, 7], v);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    var v1 = goog.vec.Vec4.createFloat32FromArray([5, 6, 7, 8]);
    +    var v2 = goog.vec.Vec4.cloneFloat32(v0);
    +
    +    goog.vec.Vec4.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([6, 8, 10, 12], v2);
    +
    +    goog.vec.Vec4.add(goog.vec.Vec4.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([7, 10, 13, 16], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([4, 3, 2, 1]);
    +    var v1 = goog.vec.Vec4.createFloat32FromArray([5, 6, 7, 8]);
    +    var v2 = goog.vec.Vec4.cloneFloat32(v0);
    +
    +    goog.vec.Vec4.subtract(v2, v1, v2);
    +    assertElementsEquals([4, 3, 2, 1], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([-1, -3, -5, -7], v2);
    +
    +    goog.vec.Vec4.setFromValues(v2, 0, 0, 0, 0);
    +    goog.vec.Vec4.subtract(v1, v0, v2);
    +    assertElementsEquals([1, 3, 5, 7], v2);
    +
    +    goog.vec.Vec4.subtract(goog.vec.Vec4.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([-3, 0, 3, 6], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    var v1 = goog.vec.Vec4.createFloat32();
    +
    +    goog.vec.Vec4.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3, -4], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.Vec4.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.Vec4.createFloat32FromValues(-1, -2, -3, -4);
    +    var v1 = goog.vec.Vec4.createFloat32();
    +
    +    goog.vec.Vec4.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3, 4], v1);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +
    +    goog.vec.Vec4.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    var v1 = goog.vec.Vec4.createFloat32();
    +
    +    goog.vec.Vec4.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12, 16], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.Vec4.setFromArray(v1, v0);
    +    goog.vec.Vec4.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15, 20], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    assertEquals(30, goog.vec.Vec4.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    assertEquals(Math.sqrt(30), goog.vec.Vec4.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([2, 3, 4, 5]);
    +    var v1 = goog.vec.Vec4.createFloat32();
    +    var v2 = goog.vec.Vec4.createFloat32();
    +    goog.vec.Vec4.scale(v0, 1 / goog.vec.Vec4.magnitude(v0), v2);
    +
    +    goog.vec.Vec4.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4, 5], v0);
    +
    +    goog.vec.Vec4.setFromArray(v1, v0);
    +    goog.vec.Vec4.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.Vec4.createFloat32FromArray([1, 2, 3, 4]);
    +    var v1 = goog.vec.Vec4.createFloat32FromArray([5, 6, 7, 8]);
    +    assertEquals(70, goog.vec.Vec4.dot(v0, v1));
    +    assertEquals(70, goog.vec.Vec4.dot(v1, v0));
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.Vec4.createFloat32FromValues(1, 2, 3, 4);
    +    var v1 = goog.vec.Vec4.createFloat32FromValues(10, 20, 30, 40);
    +    var v2 = goog.vec.Vec4.cloneFloat32(v0);
    +
    +    goog.vec.Vec4.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3, 4], v2);
    +    goog.vec.Vec4.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30, 40], v2);
    +    goog.vec.Vec4.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5, 22], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.Vec4.createFloat32FromValues(10, 20, 30, 40);
    +    var v1 = goog.vec.Vec4.createFloat32FromValues(5, 25, 35, 30);
    +    var v2 = goog.vec.Vec4.createFloat32();
    +
    +    goog.vec.Vec4.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35, 40], v2);
    +    goog.vec.Vec4.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35, 40], v1);
    +    goog.vec.Vec4.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35, 40], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.Vec4.createFloat32FromValues(10, 20, 30, 40);
    +    var v1 = goog.vec.Vec4.createFloat32FromValues(5, 25, 35, 30);
    +    var v2 = goog.vec.Vec4.createFloat32();
    +
    +    goog.vec.Vec4.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30, 30], v2);
    +    goog.vec.Vec4.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30, 30], v1);
    +    goog.vec.Vec4.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.Vec4.createFloat32FromValues(1, 2, 3, 4);
    +    var v1 = goog.vec.Vec4.cloneFloat32(v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 5;
    +    assertFalse(goog.vec.Vec4.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec4.cloneFloat32(v0);
    +    v1[1] = 5;
    +    assertFalse(goog.vec.Vec4.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec4.cloneFloat32(v0);
    +    v1[2] = 5;
    +    assertFalse(goog.vec.Vec4.equals(v0, v1));
    +
    +    v1 = goog.vec.Vec4.cloneFloat32(v0);
    +    v1[3] = 5;
    +    assertFalse(goog.vec.Vec4.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4d.js b/mapbox-gl/closure-library/closure/goog/vec/vec4d.js
    new file mode 100644
    index 0000000000..b0baa41149
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4d.js
    @@ -0,0 +1,366 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec4f.js by running:            //
    +//   swap_type.sh vec4d.js > vec4f.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 4 element double (64bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.vec4d');
    +goog.provide('goog.vec.vec4d.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float64} */ goog.vec.vec4d.Type;
    +
    +
    +/**
    + * Creates a vec4d with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec4d.Type} The new vec4d.
    + */
    +goog.vec.vec4d.create = function() {
    +  return new Float64Array(4);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec4d.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.vec4d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.setFromValues = function(vec, v0, v1, v2, v3) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  vec[3] = v3;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4d vec from vec4d src.
    + *
    + * @param {goog.vec.vec4d.Type} vec The destination vector.
    + * @param {goog.vec.vec4d.Type} src The source vector.
    + * @return {!goog.vec.vec4d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.setFromVec4d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4d vec from vec4f src (typed as a Float32Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec4d.Type} vec The destination vector.
    + * @param {Float32Array} src The source vector.
    + * @return {!goog.vec.vec4d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.setFromVec4f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4d vec from Array src.
    + *
    + * @param {goog.vec.vec4d.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec4d.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The first addend.
    + * @param {goog.vec.vec4d.Type} vec1 The second addend.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  resultVec[3] = vec0[3] + vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The minuend.
    + * @param {goog.vec.vec4d.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  resultVec[3] = vec0[3] - vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  resultVec[3] = -vec0[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The source vector.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  resultVec[3] = Math.abs(vec0[3]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  resultVec[3] = vec0[3] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec4d.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return x * x + y * y + z * z + w * w;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec4d.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return Math.sqrt(x * x + y * y + z * z + w * w);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  var ilen = 1 / Math.sqrt(x * x + y * y + z * z + w * w);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  resultVec[2] = z * ilen;
    +  resultVec[3] = w * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.vec4d.Type} v0 The first vector.
    + * @param {goog.vec.vec4d.Type} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec4d.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3];
    +};
    +
    +
    +/**
    + * Linearly interpolate from v0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec4d.Type} v0 The first vector.
    + * @param {goog.vec.vec4d.Type} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2], w = v0[3];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  resultVec[3] = (v1[3] - w) * f + w;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The source vector.
    + * @param {goog.vec.vec4d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +    resultVec[3] = Math.max(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +    resultVec[3] = Math.max(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec4d.Type} vec0 The source vector.
    + * @param {goog.vec.vec4d.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec4d.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec4d.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4d.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +    resultVec[3] = Math.min(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +    resultVec[3] = Math.min(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.vec4d.Type} v0 The first vector.
    + * @param {goog.vec.vec4d.Type} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec4d.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2] && v0[3] == v1[3];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4d_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec4d_test.html
    new file mode 100644
    index 0000000000..74a0382259
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4d_test.html
    @@ -0,0 +1,206 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec4f_test.html by running:     //
    +//   swap_type.sh vec4d_test.html > vec4f_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec4d</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float64Array');
    +  goog.require('goog.vec.vec4d');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec4d.create();
    +    assertElementsEquals([0, 0, 0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec4d.create();
    +    goog.vec.vec4d.setFromValues(v, 1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], v);
    +
    +    goog.vec.vec4d.setFromArray(v, [4, 5, 6, 7]);
    +    assertElementsEquals([4, 5, 6, 7], v);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [5, 6, 7, 8]);
    +    var v2 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +
    +    goog.vec.vec4d.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([6, 8, 10, 12], v2);
    +
    +    goog.vec.vec4d.add(goog.vec.vec4d.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([7, 10, 13, 16], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [4, 3, 2, 1]);
    +    var v1 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [5, 6, 7, 8]);
    +    var v2 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +
    +    goog.vec.vec4d.subtract(v2, v1, v2);
    +    assertElementsEquals([4, 3, 2, 1], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([-1, -3, -5, -7], v2);
    +
    +    goog.vec.vec4d.setFromValues(v2, 0, 0, 0, 0);
    +    goog.vec.vec4d.subtract(v1, v0, v2);
    +    assertElementsEquals([1, 3, 5, 7], v2);
    +
    +    goog.vec.vec4d.subtract(goog.vec.vec4d.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([-3, 0, 3, 6], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4d.create();
    +
    +    goog.vec.vec4d.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3, -4], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.vec4d.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [-1, -2, -3, -4]);
    +    var v1 = goog.vec.vec4d.create();
    +
    +    goog.vec.vec4d.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3, 4], v1);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +
    +    goog.vec.vec4d.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4d.create();
    +
    +    goog.vec.vec4d.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12, 16], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.vec4d.setFromArray(v1, v0);
    +    goog.vec.vec4d.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15, 20], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    assertEquals(30, goog.vec.vec4d.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    assertEquals(Math.sqrt(30), goog.vec.vec4d.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [2, 3, 4, 5]);
    +    var v1 = goog.vec.vec4d.create();
    +    var v2 = goog.vec.vec4d.create();
    +    goog.vec.vec4d.scale(v0, 1 / goog.vec.vec4d.magnitude(v0), v2);
    +
    +    goog.vec.vec4d.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4, 5], v0);
    +
    +    goog.vec.vec4d.setFromArray(v1, v0);
    +    goog.vec.vec4d.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4d.setFromArray(goog.vec.vec4d.create(), [5, 6, 7, 8]);
    +    assertEquals(70, goog.vec.vec4d.dot(v0, v1));
    +    assertEquals(70, goog.vec.vec4d.dot(v1, v0));
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 1, 2, 3, 4);
    +    var v1 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 10, 20, 30, 40);
    +    var v2 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +
    +    goog.vec.vec4d.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3, 4], v2);
    +    goog.vec.vec4d.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30, 40], v2);
    +    goog.vec.vec4d.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5, 22], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 10, 20, 30, 40);
    +    var v1 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 5, 25, 35, 30);
    +    var v2 = goog.vec.vec4d.create();
    +
    +    goog.vec.vec4d.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35, 40], v2);
    +    goog.vec.vec4d.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35, 40], v1);
    +    goog.vec.vec4d.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35, 40], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 10, 20, 30, 40);
    +    var v1 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 5, 25, 35, 30);
    +    var v2 = goog.vec.vec4d.create();
    +
    +    goog.vec.vec4d.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30, 30], v2);
    +    goog.vec.vec4d.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30, 30], v1);
    +    goog.vec.vec4d.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec4d.setFromValues(goog.vec.vec4d.create(), 1, 2, 3, 4);
    +    var v1 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 5;
    +    assertFalse(goog.vec.vec4d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +    v1[1] = 5;
    +    assertFalse(goog.vec.vec4d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +    v1[2] = 5;
    +    assertFalse(goog.vec.vec4d.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4d.setFromVec4d(goog.vec.vec4d.create(), v0);
    +    v1[3] = 5;
    +    assertFalse(goog.vec.vec4d.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4f.js b/mapbox-gl/closure-library/closure/goog/vec/vec4f.js
    new file mode 100644
    index 0000000000..9b4f0bf646
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4f.js
    @@ -0,0 +1,366 @@
    +// Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec4d.js by running:            //
    +//   swap_type.sh vec4f.js > vec4d.js                                        //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +
    +/**
    + * @fileoverview Provides functions for operating on 4 element float (32bit)
    + * vectors.
    + *
    + * The last parameter will typically be the output object and an object
    + * can be both an input and output parameter to all methods except where
    + * noted.
    + *
    + * See the README for notes about the design and structure of the API
    + * (especially related to performance).
    + *
    + */
    +goog.provide('goog.vec.vec4f');
    +goog.provide('goog.vec.vec4f.Type');
    +
    +/** @suppress {extraRequire} */
    +goog.require('goog.vec');
    +
    +/** @typedef {goog.vec.Float32} */ goog.vec.vec4f.Type;
    +
    +
    +/**
    + * Creates a vec4f with all elements initialized to zero.
    + *
    + * @return {!goog.vec.vec4f.Type} The new vec4f.
    + */
    +goog.vec.vec4f.create = function() {
    +  return new Float32Array(4);
    +};
    +
    +
    +/**
    + * Initializes the vector with the given values.
    + *
    + * @param {goog.vec.vec4f.Type} vec The vector to receive the values.
    + * @param {number} v0 The value for element at index 0.
    + * @param {number} v1 The value for element at index 1.
    + * @param {number} v2 The value for element at index 2.
    + * @param {number} v3 The value for element at index 3.
    + * @return {!goog.vec.vec4f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.setFromValues = function(vec, v0, v1, v2, v3) {
    +  vec[0] = v0;
    +  vec[1] = v1;
    +  vec[2] = v2;
    +  vec[3] = v3;
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4f vec from vec4f src.
    + *
    + * @param {goog.vec.vec4f.Type} vec The destination vector.
    + * @param {goog.vec.vec4f.Type} src The source vector.
    + * @return {!goog.vec.vec4f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.setFromVec4f = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4f vec from vec4d src (typed as a Float64Array to
    + * avoid circular goog.requires).
    + *
    + * @param {goog.vec.vec4f.Type} vec The destination vector.
    + * @param {Float64Array} src The source vector.
    + * @return {!goog.vec.vec4f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.setFromVec4d = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Initializes vec4f vec from Array src.
    + *
    + * @param {goog.vec.vec4f.Type} vec The destination vector.
    + * @param {Array<number>} src The source vector.
    + * @return {!goog.vec.vec4f.Type} Return vec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.setFromArray = function(vec, src) {
    +  vec[0] = src[0];
    +  vec[1] = src[1];
    +  vec[2] = src[2];
    +  vec[3] = src[3];
    +  return vec;
    +};
    +
    +
    +/**
    + * Performs a component-wise addition of vec0 and vec1 together storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The first addend.
    + * @param {goog.vec.vec4f.Type} vec1 The second addend.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.add = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] + vec1[0];
    +  resultVec[1] = vec0[1] + vec1[1];
    +  resultVec[2] = vec0[2] + vec1[2];
    +  resultVec[3] = vec0[3] + vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Performs a component-wise subtraction of vec1 from vec0 storing the
    + * result into resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The minuend.
    + * @param {goog.vec.vec4f.Type} vec1 The subtrahend.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the result. May be vec0 or vec1.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.subtract = function(vec0, vec1, resultVec) {
    +  resultVec[0] = vec0[0] - vec1[0];
    +  resultVec[1] = vec0[1] - vec1[1];
    +  resultVec[2] = vec0[2] - vec1[2];
    +  resultVec[3] = vec0[3] - vec1[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Negates vec0, storing the result into resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The vector to negate.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.negate = function(vec0, resultVec) {
    +  resultVec[0] = -vec0[0];
    +  resultVec[1] = -vec0[1];
    +  resultVec[2] = -vec0[2];
    +  resultVec[3] = -vec0[3];
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Takes the absolute value of each component of vec0 storing the result in
    + * resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The source vector.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to receive the result.
    + *     May be vec0.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.abs = function(vec0, resultVec) {
    +  resultVec[0] = Math.abs(vec0[0]);
    +  resultVec[1] = Math.abs(vec0[1]);
    +  resultVec[2] = Math.abs(vec0[2]);
    +  resultVec[3] = Math.abs(vec0[3]);
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Multiplies each component of vec0 with scalar storing the product into
    + * resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The source vector.
    + * @param {number} scalar The value to multiply with each component of vec0.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.scale = function(vec0, scalar, resultVec) {
    +  resultVec[0] = vec0[0] * scalar;
    +  resultVec[1] = vec0[1] * scalar;
    +  resultVec[2] = vec0[2] * scalar;
    +  resultVec[3] = vec0[3] * scalar;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the magnitudeSquared of the given vector.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec4f.magnitudeSquared = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return x * x + y * y + z * z + w * w;
    +};
    +
    +
    +/**
    + * Returns the magnitude of the given vector.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The vector.
    + * @return {number} The magnitude of the vector.
    + */
    +goog.vec.vec4f.magnitude = function(vec0) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  return Math.sqrt(x * x + y * y + z * z + w * w);
    +};
    +
    +
    +/**
    + * Normalizes the given vector storing the result into resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The vector to normalize.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to
    + *     receive the result. May be vec0.
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.normalize = function(vec0, resultVec) {
    +  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
    +  var ilen = 1 / Math.sqrt(x * x + y * y + z * z + w * w);
    +  resultVec[0] = x * ilen;
    +  resultVec[1] = y * ilen;
    +  resultVec[2] = z * ilen;
    +  resultVec[3] = w * ilen;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns the scalar product of vectors v0 and v1.
    + *
    + * @param {goog.vec.vec4f.Type} v0 The first vector.
    + * @param {goog.vec.vec4f.Type} v1 The second vector.
    + * @return {number} The scalar product.
    + */
    +goog.vec.vec4f.dot = function(v0, v1) {
    +  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3];
    +};
    +
    +
    +/**
    + * Linearly interpolate from v0 to v1 according to f. The value of f should be
    + * in the range [0..1] otherwise the results are undefined.
    + *
    + * @param {goog.vec.vec4f.Type} v0 The first vector.
    + * @param {goog.vec.vec4f.Type} v1 The second vector.
    + * @param {number} f The interpolation factor.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to receive the
    + *     results (may be v0 or v1).
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.lerp = function(v0, v1, f, resultVec) {
    +  var x = v0[0], y = v0[1], z = v0[2], w = v0[3];
    +  resultVec[0] = (v1[0] - x) * f + x;
    +  resultVec[1] = (v1[1] - y) * f + y;
    +  resultVec[2] = (v1[2] - z) * f + z;
    +  resultVec[3] = (v1[3] - w) * f + w;
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the larger values in resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The source vector.
    + * @param {goog.vec.vec4f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.max = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.max(vec0[0], limit);
    +    resultVec[1] = Math.max(vec0[1], limit);
    +    resultVec[2] = Math.max(vec0[2], limit);
    +    resultVec[3] = Math.max(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.max(vec0[0], limit[0]);
    +    resultVec[1] = Math.max(vec0[1], limit[1]);
    +    resultVec[2] = Math.max(vec0[2], limit[2]);
    +    resultVec[3] = Math.max(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Compares the components of vec0 with the components of another vector or
    + * scalar, storing the smaller values in resultVec.
    + *
    + * @param {goog.vec.vec4f.Type} vec0 The source vector.
    + * @param {goog.vec.vec4f.Type|number} limit The limit vector or scalar.
    + * @param {goog.vec.vec4f.Type} resultVec The vector to receive the
    + *     results (may be vec0 or limit).
    + * @return {!goog.vec.vec4f.Type} Return resultVec so that operations can be
    + *     chained together.
    + */
    +goog.vec.vec4f.min = function(vec0, limit, resultVec) {
    +  if (goog.isNumber(limit)) {
    +    resultVec[0] = Math.min(vec0[0], limit);
    +    resultVec[1] = Math.min(vec0[1], limit);
    +    resultVec[2] = Math.min(vec0[2], limit);
    +    resultVec[3] = Math.min(vec0[3], limit);
    +  } else {
    +    resultVec[0] = Math.min(vec0[0], limit[0]);
    +    resultVec[1] = Math.min(vec0[1], limit[1]);
    +    resultVec[2] = Math.min(vec0[2], limit[2]);
    +    resultVec[3] = Math.min(vec0[3], limit[3]);
    +  }
    +  return resultVec;
    +};
    +
    +
    +/**
    + * Returns true if the components of v0 are equal to the components of v1.
    + *
    + * @param {goog.vec.vec4f.Type} v0 The first vector.
    + * @param {goog.vec.vec4f.Type} v1 The second vector.
    + * @return {boolean} True if the vectors are equal, false otherwise.
    + */
    +goog.vec.vec4f.equals = function(v0, v1) {
    +  return v0.length == v1.length &&
    +      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2] && v0[3] == v1[3];
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec4f_test.html b/mapbox-gl/closure-library/closure/goog/vec/vec4f_test.html
    new file mode 100644
    index 0000000000..12d138a7fd
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec4f_test.html
    @@ -0,0 +1,206 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +//                                                                           //
    +// Any edits to this file must be applied to vec4d_test.html by running:     //
    +//   swap_type.sh vec4f_test.html > vec4d_test.html                          //
    +//                                                                           //
    +////////////////////////// NOTE ABOUT EDITING THIS FILE ///////////////////////
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +<title>Closure Unit Tests - goog.vec.vec4f</title>
    +<script src="../base.js"></script>
    +<script>
    +  goog.require('goog.vec.Float32Array');
    +  goog.require('goog.vec.vec4f');
    +  goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  function testCreate() {
    +    var v = goog.vec.vec4f.create();
    +    assertElementsEquals([0, 0, 0, 0], v);
    +  }
    +
    +  function testSet() {
    +    var v = goog.vec.vec4f.create();
    +    goog.vec.vec4f.setFromValues(v, 1, 2, 3, 4);
    +    assertElementsEquals([1, 2, 3, 4], v);
    +
    +    goog.vec.vec4f.setFromArray(v, [4, 5, 6, 7]);
    +    assertElementsEquals([4, 5, 6, 7], v);
    +  }
    +
    +  function testAdd() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [5, 6, 7, 8]);
    +    var v2 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +
    +    goog.vec.vec4f.add(v2, v1, v2);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([6, 8, 10, 12], v2);
    +
    +    goog.vec.vec4f.add(goog.vec.vec4f.add(v0, v1, v2), v0, v2);
    +    assertElementsEquals([7, 10, 13, 16], v2);
    +  }
    +
    +  function testSubtract() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [4, 3, 2, 1]);
    +    var v1 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [5, 6, 7, 8]);
    +    var v2 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +
    +    goog.vec.vec4f.subtract(v2, v1, v2);
    +    assertElementsEquals([4, 3, 2, 1], v0);
    +    assertElementsEquals([5, 6, 7, 8], v1);
    +    assertElementsEquals([-1, -3, -5, -7], v2);
    +
    +    goog.vec.vec4f.setFromValues(v2, 0, 0, 0, 0);
    +    goog.vec.vec4f.subtract(v1, v0, v2);
    +    assertElementsEquals([1, 3, 5, 7], v2);
    +
    +    goog.vec.vec4f.subtract(goog.vec.vec4f.subtract(v1, v0, v2), v0, v2);
    +    assertElementsEquals([-3, 0, 3, 6], v2);
    +  }
    +
    +  function testNegate() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4f.create();
    +
    +    goog.vec.vec4f.negate(v0, v1);
    +    assertElementsEquals([-1, -2, -3, -4], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.vec4f.negate(v0, v0);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +  }
    +
    +  function testAbs() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [-1, -2, -3, -4]);
    +    var v1 = goog.vec.vec4f.create();
    +
    +    goog.vec.vec4f.abs(v0, v1);
    +    assertElementsEquals([1, 2, 3, 4], v1);
    +    assertElementsEquals([-1, -2, -3, -4], v0);
    +
    +    goog.vec.vec4f.abs(v0, v0);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +  }
    +
    +  function testScale() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4f.create();
    +
    +    goog.vec.vec4f.scale(v0, 4, v1);
    +    assertElementsEquals([4, 8, 12, 16], v1);
    +    assertElementsEquals([1, 2, 3, 4], v0);
    +
    +    goog.vec.vec4f.setFromArray(v1, v0);
    +    goog.vec.vec4f.scale(v1, 5, v1);
    +    assertElementsEquals([5, 10, 15, 20], v1);
    +  }
    +
    +  function testMagnitudeSquared() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    assertEquals(30, goog.vec.vec4f.magnitudeSquared(v0));
    +  }
    +
    +  function testMagnitude() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    assertEquals(Math.sqrt(30), goog.vec.vec4f.magnitude(v0));
    +  }
    +
    +  function testNormalize() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [2, 3, 4, 5]);
    +    var v1 = goog.vec.vec4f.create();
    +    var v2 = goog.vec.vec4f.create();
    +    goog.vec.vec4f.scale(v0, 1 / goog.vec.vec4f.magnitude(v0), v2);
    +
    +    goog.vec.vec4f.normalize(v0, v1);
    +    assertElementsEquals(v2, v1);
    +    assertElementsEquals([2, 3, 4, 5], v0);
    +
    +    goog.vec.vec4f.setFromArray(v1, v0);
    +    goog.vec.vec4f.normalize(v1, v1);
    +    assertElementsEquals(v2, v1);
    +  }
    +
    +  function testDot() {
    +    var v0 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [1, 2, 3, 4]);
    +    var v1 = goog.vec.vec4f.setFromArray(goog.vec.vec4f.create(), [5, 6, 7, 8]);
    +    assertEquals(70, goog.vec.vec4f.dot(v0, v1));
    +    assertEquals(70, goog.vec.vec4f.dot(v1, v0));
    +  }
    +
    +  function testLerp() {
    +    var v0 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 1, 2, 3, 4);
    +    var v1 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 10, 20, 30, 40);
    +    var v2 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +
    +    goog.vec.vec4f.lerp(v2, v1, 0, v2);
    +    assertElementsEquals([1, 2, 3, 4], v2);
    +    goog.vec.vec4f.lerp(v2, v1, 1, v2);
    +    assertElementsEquals([10, 20, 30, 40], v2);
    +    goog.vec.vec4f.lerp(v0, v1, .5, v2);
    +    assertElementsEquals([5.5, 11, 16.5, 22], v2);
    +  }
    +
    +  function testMax() {
    +    var v0 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 10, 20, 30, 40);
    +    var v1 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 5, 25, 35, 30);
    +    var v2 = goog.vec.vec4f.create();
    +
    +    goog.vec.vec4f.max(v0, v1, v2);
    +    assertElementsEquals([10, 25, 35, 40], v2);
    +    goog.vec.vec4f.max(v1, v0, v1);
    +    assertElementsEquals([10, 25, 35, 40], v1);
    +    goog.vec.vec4f.max(v2, 20, v2);
    +    assertElementsEquals([20, 25, 35, 40], v2);
    +  }
    +
    +  function testMin() {
    +    var v0 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 10, 20, 30, 40);
    +    var v1 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 5, 25, 35, 30);
    +    var v2 = goog.vec.vec4f.create();
    +
    +    goog.vec.vec4f.min(v0, v1, v2);
    +    assertElementsEquals([5, 20, 30, 30], v2);
    +    goog.vec.vec4f.min(v1, v0, v1);
    +    assertElementsEquals([5, 20, 30, 30], v1);
    +    goog.vec.vec4f.min(v2, 20, v2);
    +    assertElementsEquals([5, 20, 20, 20], v2);
    +  }
    +
    +  function testEquals() {
    +    var v0 = goog.vec.vec4f.setFromValues(goog.vec.vec4f.create(), 1, 2, 3, 4);
    +    var v1 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +    assertElementsEquals(v0, v1);
    +
    +    v1[0] = 5;
    +    assertFalse(goog.vec.vec4f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +    v1[1] = 5;
    +    assertFalse(goog.vec.vec4f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +    v1[2] = 5;
    +    assertFalse(goog.vec.vec4f.equals(v0, v1));
    +
    +    v1 = goog.vec.vec4f.setFromVec4f(goog.vec.vec4f.create(), v0);
    +    v1[3] = 5;
    +    assertFalse(goog.vec.vec4f.equals(v0, v1));
    +  }
    +
    +</script>
    +</body>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec_array_perf.html b/mapbox-gl/closure-library/closure/goog/vec/vec_array_perf.html
    new file mode 100644
    index 0000000000..67e5728615
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec_array_perf.html
    @@ -0,0 +1,443 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - Vector Array math</title>
    +  <link rel="stylesheet" type="text/css"
    +        href="../testing/performancetable.css"/>
    +  <script type="text/javascript" src="../base.js"></script>
    +  <script type="text/javascript">
    +    goog.require('goog.testing.jsunit');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.vec.Vec4');
    +    goog.require('goog.vec.Mat4');
    +  </script>
    +</head>
    +<body>
    +  <h1>Closure Performance Tests - Vector Array Math</h1>
    +  <p>
    +    <strong>User-agent:</strong>
    +    <script type="text/javascript">document.write(navigator.userAgent);</script>
    +  </p>
    +  <p>
    +    These tests compare various methods of performing vector operations on
    +    arrays of vectors.
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    + <script type="text/javascript">
    +
    +var table = new goog.testing.PerformanceTable(
    +    goog.dom.getElement('perfTable'));
    +
    +function createRandomFloat32Array(length) {
    +  var array = new Float32Array(length);
    +  for (var i = 0; i < length; i++) {
    +    array[i] = Math.random();
    +  }
    +  return array;
    +}
    +
    +function createRandomIndexArray(length) {
    +  var array = [];
    +  for (var i = 0; i < length; i++) {
    +    array[i] = Math.floor(Math.random() * length);
    +    array[i] = Math.min(length - 1, array[i]);
    +  }
    +  return array;
    +}
    +
    +function createRandomVec4Array(length) {
    +  var a = [];
    +  for (var i = 0; i < length; i++) {
    +    a[i] = goog.vec.Vec4.createFromValues(
    +        Math.random(), Math.random(), Math.random(), Math.random());
    +  }
    +  return a;
    +}
    +
    +function createRandomMat4() {
    +  var m = goog.vec.Mat4.createFromValues(
    +      Math.random(), Math.random(), Math.random(), Math.random(),
    +      Math.random(), Math.random(), Math.random(), Math.random(),
    +      Math.random(), Math.random(), Math.random(), Math.random(),
    +      Math.random(), Math.random(), Math.random(), Math.random());
    +  return m;
    +}
    +
    +function createRandomMat4Array(length) {
    +  var m = [];
    +  for (var i = 0; i < length; i++) {
    +    m[i] = createRandomMat4();
    +  }
    +  return m;
    +}
    +
    +/**
    + * Vec4Object is a 4-vector object with x,y,z,w components.
    + * @param {number} x The x component.
    + * @param {number} y The y component.
    + * @param {number} z The z component.
    + * @param {number} w The w component.
    + * @constructor
    + */
    +Vec4Object = function(x, y, z, w) {
    +  this.x = x;
    +  this.y = y;
    +  this.z = z;
    +  this.w = w;
    +};
    +
    +/**
    + * Add two vectors.
    + * @param {Vec4Object} v0 A vector.
    + * @param {Vec4Object} v1 Another vector.
    + * @param {Vec4Object} r The result.
    + */
    +Vec4Object.add = function(v0, v1, r) {
    +  r.x = v0.x + v1.x;
    +  r.y = v0.y + v1.y;
    +  r.z = v0.z + v1.z;
    +  r.w = v0.w + v1.w;
    +};
    +
    +function createRandomVec4ObjectArray(length) {
    +  var a = [];
    +  for (var i = 0; i < length; i++) {
    +    a[i] = new Vec4Object(
    +        Math.random(), Math.random(), Math.random(), Math.random());
    +  }
    +  return a;
    +}
    +
    +function setVec4FromArray(v, a, o) {
    +  v[0] = a[o + 0];
    +  v[1] = a[o + 1];
    +  v[2] = a[o + 2];
    +  v[3] = a[o + 3];
    +}
    +
    +function setArrayFromVec4(a, o, v) {
    +  a[o + 0] = v[0];
    +  a[o + 1] = v[1];
    +  a[o + 2] = v[2];
    +  a[o + 3] = v[3];
    +}
    +
    +/**
    + * This is the same as goog.vec.Vec4.add().  Use this to avoid namespace lookup
    + * overheads.
    + * @param {goog.vec.Vec4.Vec4Like} v0 A vector.
    + * @param {goog.vec.Vec4.Vec4Like} v1 Another vector.
    + * @param {goog.vec.Vec4.Vec4Like} r The result.
    + */
    +function addVec4(v0, v1, r) {
    +  r[0] = v0[0] + v1[0];
    +  r[1] = v0[1] + v1[1];
    +  r[2] = v0[2] + v1[2];
    +  r[3] = v0[3] + v1[3];
    +}
    +
    +function addVec4ByOffset(v0Buf, v0Off, v1Buf, v1Off, rBuf, rOff) {
    +  rBuf[rOff + 0] = v0Buf[v0Off + 0] + v1Buf[v1Off + 0];
    +  rBuf[rOff + 1] = v0Buf[v0Off + 1] + v1Buf[v1Off + 1];
    +  rBuf[rOff + 2] = v0Buf[v0Off + 2] + v1Buf[v1Off + 2];
    +  rBuf[rOff + 3] = v0Buf[v0Off + 3] + v1Buf[v1Off + 3];
    +}
    +
    +function addVec4ByOptionalOffset(v0, v1, r, opt_v0Off, opt_v1Off, opt_rOff) {
    +  if (opt_v0Off && opt_v1Off && opt_rOff) {
    +    r[opt_rOff + 0] = v0[opt_v0Off + 0] + v1[opt_v1Off + 0];
    +    r[opt_rOff + 1] = v0[opt_v0Off + 1] + v1[opt_v1Off + 1];
    +    r[opt_rOff + 2] = v0[opt_v0Off + 2] + v1[opt_v1Off + 2];
    +    r[opt_rOff + 3] = v0[opt_v0Off + 3] + v1[opt_v1Off + 3];
    +  } else {
    +    r[0] = v0[0] + v1[0];
    +    r[1] = v0[1] + v1[1];
    +    r[2] = v0[2] + v1[2];
    +    r[3] = v0[3] + v1[3];
    +  }
    +}
    +
    +function mat4MultVec4ByOffset(mBuf, mOff, vBuf, vOff, rBuf, rOff) {
    +  var x = vBuf[vOff + 0], y = vBuf[vOff + 1],
    +      z = vBuf[vOff + 2], w = vBuf[vOff + 3];
    +  rBuf[rOff + 0] = x * mBuf[mOff + 0] + y * mBuf[mOff + 4] +
    +      z * mBuf[mOff + 8] + w * mBuf[mOff + 12];
    +  rBuf[rOff + 1] = x * mBuf[mOff + 1] + y * mBuf[mOff + 5] +
    +      z * mBuf[mOff + 9] + w * mBuf[mOff + 13];
    +  rBuf[rOff + 2] = x * mBuf[mOff + 2] + y * mBuf[mOff + 6] +
    +      z * mBuf[mOff + 10] + w * mBuf[mOff + 14];
    +  rBuf[rOff + 3] = x * mBuf[mOff + 3] + y * mBuf[mOff + 7] +
    +      z * mBuf[mOff + 11] + w * mBuf[mOff + 15];
    +}
    +
    +var NUM_ITERATIONS = 200000;
    +
    +function testAddVec4ByOffset() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          addVec4ByOffset(a0, i * 4, a1, i * 4, a2, i * 4);
    +        }
    +      },
    +      'Add vectors using offsets');
    +}
    +
    +function testAddVec4ByOptionalOffset() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          addVec4ByOptionalOffset(a0, a1, a2, i * 4, i * 4, i * 4);
    +        }
    +      },
    +      'Add vectors with optional offsets (requires branch)');
    +}
    +
    +/**
    + * Check the overhead of using an array of individual
    + * Vec4s (Float32Arrays of length 4).
    + */
    +function testAddVec4ByVec4s() {
    +  var nVecs = NUM_ITERATIONS;
    +  var a0 = createRandomVec4Array(nVecs);
    +  var a1 = createRandomVec4Array(nVecs);
    +  var a2 = createRandomVec4Array(nVecs);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          addVec4(a0[i], a1[i], a2[i]);
    +        }
    +      },
    +      'Add vectors using an array of Vec4s (Float32Arrays of length 4)');
    +}
    +
    +function testAddVec4ByTmp() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +
    +  table.run(
    +      function() {
    +        var t0 = new Float32Array(4);
    +        var t1 = new Float32Array(4);
    +        for (var i = 0; i < nVecs; i++) {
    +          setVec4FromArray(t0, a0, i * 4);
    +          setVec4FromArray(t1, a1, i * 4);
    +          addVec4(t0, t1, t0);
    +          setArrayFromVec4(a2, i * 4, t0);
    +        }
    +      },
    +      'Add vectors using tmps');
    +}
    +
    +/**
    + * Check the overhead of using an array of Objects with the implicit hash
    + * lookups for the x,y,z,w components.
    + */
    +function testAddVec4ByObjects() {
    +  var nVecs = NUM_ITERATIONS;
    +  var a0 = createRandomVec4ObjectArray(nVecs);
    +  var a1 = createRandomVec4ObjectArray(nVecs);
    +  var a2 = createRandomVec4ObjectArray(nVecs);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          Vec4Object.add(a0[i], a1[i], a2[i]);
    +        }
    +      },
    +      'Add vectors using an array of Objects ' +
    +      '(with implicit hash lookups for the x,y,z,w components)');
    +}
    +
    +function testAddVec4BySubarray() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          var t0 = a0.subarray(i * 4 * 4);
    +          var t1 = a1.subarray(i * 4 * 4);
    +          var t2 = a2.subarray(i * 4 * 4);
    +          addVec4(t0, t1, t2);
    +        }
    +      },
    +      'Add vectors using Float32Array.subarray()');
    +}
    +
    +function testAddVec4ByView() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          var t0 = new Float32Array(a0.buffer, i * 4 * 4);
    +          var t1 = new Float32Array(a1.buffer, i * 4 * 4);
    +          var t2 = new Float32Array(a2.buffer, i * 4 * 4);
    +          addVec4(t0, t1, t2);
    +        }
    +      },
    +      'Add vectors using Float32 view');
    +}
    +
    +function testMat4MultVec4ByOffset() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVecVals = nVecs * 4;
    +  var nMatVals = nVecs * 16;
    +  var m = createRandomFloat32Array(nMatVals);
    +  var a0 = createRandomFloat32Array(nVecVals);
    +  var a1 = new Float32Array(nVecVals);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          mat4MultVec4ByOffset(m, i * 16, a0, i * 4, a1, i * 4);
    +        }
    +      },
    +      'vec4 = mat4 * vec4 using offsets.');
    +}
    +
    +/**
    + * Check the overhead of using an array of individual
    + * Vec4s (Float32Arrays of length 4).
    + */
    +function testMat4MultVec4ByVec4s() {
    +  var nVecs = NUM_ITERATIONS;
    +  var a0 = createRandomVec4Array(nVecs);
    +  var a1 = createRandomVec4Array(nVecs);
    +  var m = createRandomMat4Array(nVecs);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          goog.vec.Mat4.multVec4(m[i], a0[i], a1[i]);
    +        }
    +      },
    +      'vec4 = mat4 * vec4  using arrays of Vec4s and Mat4s');
    +}
    +
    +/**
    + * Do 10x as many for the one vector tests.
    + * @type {number}
    + */
    +var NUM_ONE_ITERATIONS = NUM_ITERATIONS * 10;
    +
    +function testAddOneVec4ByOffset() {
    +  var a0 = createRandomFloat32Array(4);
    +  var a1 = createRandomFloat32Array(4);
    +  var a2 = new Float32Array(4);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < NUM_ONE_ITERATIONS; i++) {
    +          addVec4ByOffset(a0, 0, a1, 0, a2, 0);
    +        }
    +      },
    +      'Add one vector using offset of 0');
    +}
    +
    +function testAddOneVec4() {
    +  var a0 = createRandomFloat32Array(4);
    +  var a1 = createRandomFloat32Array(4);
    +  var a2 = new Float32Array(4);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < NUM_ONE_ITERATIONS; i++) {
    +          addVec4(a0, a1, a2);
    +        }
    +      },
    +      'Add one vector');
    +}
    +
    +function testAddOneVec4ByOptionalOffset() {
    +  var a0 = createRandomFloat32Array(4);
    +  var a1 = createRandomFloat32Array(4);
    +  var a2 = new Float32Array(4);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < NUM_ONE_ITERATIONS; i++) {
    +          addVec4ByOptionalOffset(a0, a1, a2);
    +        }
    +      },
    +      'Add one vector with optional offsets (requires branch)');
    +}
    +
    +function testAddRandomVec4ByOffset() {
    +  var nVecs = NUM_ITERATIONS;
    +  var nVals = nVecs * 4;
    +  var a0 = createRandomFloat32Array(nVals);
    +  var a1 = createRandomFloat32Array(nVals);
    +  var a2 = new Float32Array(nVals);
    +  var i0 = createRandomIndexArray(nVecs);
    +  var i1 = createRandomIndexArray(nVecs);
    +  var i2 = createRandomIndexArray(nVecs);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          addVec4ByOffset(a0, i0[i] * 4, a1, i1[i] * 4, a2, i2[i] * 4);
    +        }
    +      },
    +      'Add random vectors using offsets');
    +}
    +
    +function testAddRandomVec4ByVec4s() {
    +  var nVecs = NUM_ITERATIONS;
    +  var a0 = createRandomVec4Array(nVecs);
    +  var a1 = createRandomVec4Array(nVecs);
    +  var a2 = createRandomVec4Array(nVecs);
    +  var i0 = createRandomIndexArray(nVecs);
    +  var i1 = createRandomIndexArray(nVecs);
    +  var i2 = createRandomIndexArray(nVecs);
    +
    +  table.run(
    +      function() {
    +        for (var i = 0; i < nVecs; i++) {
    +          addVec4(a0[i0[i]], a1[i1[i]], a2[i2[i]]);
    +        }
    +      },
    +      'Add random vectors using an array of Vec4s');
    +}
    +
    +// Make sure the tests are run in the order they are defined.
    +var testCase = new goog.testing.TestCase(document.title);
    +testCase.order = goog.testing.TestCase.Order.NATURAL;
    +testCase.autoDiscoverTests();
    +G_testRunner.initialize(testCase);
    +
    + </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/vec/vec_perf.html b/mapbox-gl/closure-library/closure/goog/vec/vec_perf.html
    new file mode 100644
    index 0000000000..1aacb5f676
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/vec/vec_perf.html
    @@ -0,0 +1,101 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +
    +Author: nicksantos@google.com (Nick Santos)
    +-->
    +<head>
    +<meta http-equiv="X-UA-Compatible" content="IE=edge">
    +  <title>Closure Performance Tests - Vector math</title>
    +  <link rel="stylesheet" type="text/css"
    +        href="../testing/performancetable.css"/>
    +  <script src="../base.js"></script>
    +  <script>
    +    goog.require('goog.crypt');
    +    goog.require('goog.string');
    +    goog.require('goog.testing.PerformanceTable');
    +    goog.require('goog.testing.jsunit');
    +    goog.require('goog.vec.Vec4');
    +  </script>
    +</head>
    +<body>
    +  <h1>Closure Performance Tests - Vector Math</h1>
    +  <p>
    +    <strong>User-agent:</strong>
    +    <script>document.write(navigator.userAgent);</script>
    +  </p>
    +  <div id="perfTable"></div>
    +  <hr>
    +
    +  <script>
    +    var table = new goog.testing.PerformanceTable(
    +        goog.dom.getElement('perfTable'));
    +    var createVec4FromValues = goog.vec.Vec4.createFromValues;
    +    var scaleVec4 = goog.vec.Vec4.scale;
    +
    +    var negateVec4ByScaling = function(v, result) {
    +      return scaleVec4(v, -1, result);
    +    };
    +
    +    var negateVec4ByNegation = function(v, result) {
    +      result[0] = -v[0];
    +      result[1] = -v[1];
    +      result[2] = -v[2];
    +      result[3] = -v[3];
    +      return result;
    +    };
    +
    +    var negateVec4ByMultiplication = function(v, result) {
    +      result[0] = -1 * v[0];
    +      result[1] = -1 * v[1];
    +      result[2] = -1 * v[2];
    +      result[3] = -1 * v[3];
    +      return result;
    +    };
    +
    +    function createRandomVec4() {
    +      return createVec4FromValues(
    +          Math.random(),
    +          Math.random(),
    +          Math.random(),
    +          Math.random());
    +    }
    +
    +    function testNegateVec4ByScaling() {
    +      var v = createRandomVec4();
    +      for (var i = 0; i < 2000000; i++) {
    +        // Warm the trace tree to see if that makes a difference.
    +        scaleVec4(v, 1, v);
    +      }
    +
    +      table.run(
    +          function() {
    +            for (var i = 0; i < 2000000; i++) {
    +              negateVec4ByScaling(v, v);
    +            }
    +          },
    +          'Negate vector by calling scale()');
    +    }
    +
    +    function testNegateVec4ByNegation() {
    +      var v = createRandomVec4();
    +      for (var i = 0; i < 2000000; i++) {
    +        // Warm the trace tree to see if that makes a difference.
    +        scaleVec4(v, 1, v);
    +      }
    +
    +      table.run(
    +          function() {
    +            for (var i = 0; i < 2000000; i++) {
    +              negateVec4ByNegation(v, v);
    +            }
    +          },
    +          'Negate vector by negating directly');
    +    }
    +  </script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/webgl/webgl.js b/mapbox-gl/closure-library/closure/goog/webgl/webgl.js
    new file mode 100644
    index 0000000000..3c4235739b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/webgl/webgl.js
    @@ -0,0 +1,2194 @@
    +// Copyright 2011 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview Constants used by the WebGL rendering, including all of the
    + * constants used from the WebGL context.  For example, instead of using
    + * context.ARRAY_BUFFER, your code can use
    + * goog.webgl.ARRAY_BUFFER. The benefits for doing this include allowing
    + * the compiler to optimize your code so that the compiled code does not have to
    + * contain large strings to reference these properties, and reducing runtime
    + * property access.
    + *
    + * Values are taken from the WebGL Spec:
    + * https://www.khronos.org/registry/webgl/specs/1.0/#WEBGLRENDERINGCONTEXT
    + */
    +
    +goog.provide('goog.webgl');
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_BUFFER_BIT = 0x00000100;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BUFFER_BIT = 0x00000400;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COLOR_BUFFER_BIT = 0x00004000;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.POINTS = 0x0000;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINES = 0x0001;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINE_LOOP = 0x0002;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINE_STRIP = 0x0003;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TRIANGLES = 0x0004;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TRIANGLE_STRIP = 0x0005;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TRIANGLE_FAN = 0x0006;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ZERO = 0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE = 1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SRC_COLOR = 0x0300;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_SRC_COLOR = 0x0301;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SRC_ALPHA = 0x0302;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_SRC_ALPHA = 0x0303;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DST_ALPHA = 0x0304;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_DST_ALPHA = 0x0305;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DST_COLOR = 0x0306;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_DST_COLOR = 0x0307;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SRC_ALPHA_SATURATE = 0x0308;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FUNC_ADD = 0x8006;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_EQUATION = 0x8009;
    +
    +
    +/**
    + * Same as BLEND_EQUATION
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_EQUATION_RGB = 0x8009;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_EQUATION_ALPHA = 0x883D;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FUNC_SUBTRACT = 0x800A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FUNC_REVERSE_SUBTRACT = 0x800B;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_DST_RGB = 0x80C8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_SRC_RGB = 0x80C9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_DST_ALPHA = 0x80CA;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_SRC_ALPHA = 0x80CB;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CONSTANT_COLOR = 0x8001;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_CONSTANT_COLOR = 0x8002;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CONSTANT_ALPHA = 0x8003;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ONE_MINUS_CONSTANT_ALPHA = 0x8004;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND_COLOR = 0x8005;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ARRAY_BUFFER = 0x8892;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ELEMENT_ARRAY_BUFFER = 0x8893;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ARRAY_BUFFER_BINDING = 0x8894;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ELEMENT_ARRAY_BUFFER_BINDING = 0x8895;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STREAM_DRAW = 0x88E0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STATIC_DRAW = 0x88E4;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DYNAMIC_DRAW = 0x88E8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BUFFER_SIZE = 0x8764;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BUFFER_USAGE = 0x8765;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CURRENT_VERTEX_ATTRIB = 0x8626;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRONT = 0x0404;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BACK = 0x0405;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRONT_AND_BACK = 0x0408;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CULL_FACE = 0x0B44;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLEND = 0x0BE2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DITHER = 0x0BD0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_TEST = 0x0B90;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_TEST = 0x0B71;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SCISSOR_TEST = 0x0C11;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.POLYGON_OFFSET_FILL = 0x8037;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLE_ALPHA_TO_COVERAGE = 0x809E;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLE_COVERAGE = 0x80A0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NO_ERROR = 0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INVALID_ENUM = 0x0500;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INVALID_VALUE = 0x0501;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INVALID_OPERATION = 0x0502;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.OUT_OF_MEMORY = 0x0505;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CW = 0x0900;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CCW = 0x0901;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINE_WIDTH = 0x0B21;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ALIASED_POINT_SIZE_RANGE = 0x846D;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ALIASED_LINE_WIDTH_RANGE = 0x846E;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CULL_FACE_MODE = 0x0B45;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRONT_FACE = 0x0B46;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_RANGE = 0x0B70;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_WRITEMASK = 0x0B72;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_CLEAR_VALUE = 0x0B73;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_FUNC = 0x0B74;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_CLEAR_VALUE = 0x0B91;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_FUNC = 0x0B92;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_FAIL = 0x0B94;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_PASS_DEPTH_FAIL = 0x0B95;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_PASS_DEPTH_PASS = 0x0B96;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_REF = 0x0B97;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_VALUE_MASK = 0x0B93;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_WRITEMASK = 0x0B98;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_FUNC = 0x8800;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_FAIL = 0x8801;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_PASS_DEPTH_PASS = 0x8803;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_REF = 0x8CA3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_VALUE_MASK = 0x8CA4;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BACK_WRITEMASK = 0x8CA5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VIEWPORT = 0x0BA2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SCISSOR_BOX = 0x0C10;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COLOR_CLEAR_VALUE = 0x0C22;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COLOR_WRITEMASK = 0x0C23;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNPACK_ALIGNMENT = 0x0CF5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.PACK_ALIGNMENT = 0x0D05;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_TEXTURE_SIZE = 0x0D33;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_VIEWPORT_DIMS = 0x0D3A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SUBPIXEL_BITS = 0x0D50;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RED_BITS = 0x0D52;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.GREEN_BITS = 0x0D53;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BLUE_BITS = 0x0D54;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ALPHA_BITS = 0x0D55;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_BITS = 0x0D56;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_BITS = 0x0D57;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.POLYGON_OFFSET_UNITS = 0x2A00;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.POLYGON_OFFSET_FACTOR = 0x8038;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_BINDING_2D = 0x8069;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLE_BUFFERS = 0x80A8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLES = 0x80A9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLE_COVERAGE_VALUE = 0x80AA;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLE_COVERAGE_INVERT = 0x80AB;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPRESSED_TEXTURE_FORMATS = 0x86A3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DONT_CARE = 0x1100;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FASTEST = 0x1101;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NICEST = 0x1102;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.GENERATE_MIPMAP_HINT = 0x8192;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BYTE = 0x1400;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_BYTE = 0x1401;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SHORT = 0x1402;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_SHORT = 0x1403;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INT = 0x1404;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_INT = 0x1405;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT = 0x1406;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_COMPONENT = 0x1902;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ALPHA = 0x1906;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RGB = 0x1907;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RGBA = 0x1908;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LUMINANCE = 0x1909;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LUMINANCE_ALPHA = 0x190A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_SHORT_4_4_4_4 = 0x8033;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_SHORT_5_5_5_1 = 0x8034;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNSIGNED_SHORT_5_6_5 = 0x8363;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAGMENT_SHADER = 0x8B30;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_SHADER = 0x8B31;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_VERTEX_ATTRIBS = 0x8869;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_VARYING_VECTORS = 0x8DFC;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_TEXTURE_IMAGE_UNITS = 0x8872;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SHADER_TYPE = 0x8B4F;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DELETE_STATUS = 0x8B80;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINK_STATUS = 0x8B82;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VALIDATE_STATUS = 0x8B83;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ATTACHED_SHADERS = 0x8B85;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ACTIVE_UNIFORMS = 0x8B86;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ACTIVE_ATTRIBUTES = 0x8B89;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SHADING_LANGUAGE_VERSION = 0x8B8C;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CURRENT_PROGRAM = 0x8B8D;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NEVER = 0x0200;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LESS = 0x0201;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.EQUAL = 0x0202;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LEQUAL = 0x0203;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.GREATER = 0x0204;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NOTEQUAL = 0x0205;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.GEQUAL = 0x0206;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ALWAYS = 0x0207;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.KEEP = 0x1E00;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.REPLACE = 0x1E01;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INCR = 0x1E02;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DECR = 0x1E03;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INVERT = 0x150A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INCR_WRAP = 0x8507;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DECR_WRAP = 0x8508;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VENDOR = 0x1F00;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERER = 0x1F01;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERSION = 0x1F02;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NEAREST = 0x2600;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINEAR = 0x2601;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NEAREST_MIPMAP_NEAREST = 0x2700;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINEAR_MIPMAP_NEAREST = 0x2701;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NEAREST_MIPMAP_LINEAR = 0x2702;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LINEAR_MIPMAP_LINEAR = 0x2703;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_MAG_FILTER = 0x2800;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_MIN_FILTER = 0x2801;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_WRAP_S = 0x2802;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_WRAP_T = 0x2803;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_2D = 0x0DE1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE = 0x1702;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP = 0x8513;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_BINDING_CUBE_MAP = 0x8514;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE0 = 0x84C0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE1 = 0x84C1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE2 = 0x84C2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE3 = 0x84C3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE4 = 0x84C4;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE5 = 0x84C5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE6 = 0x84C6;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE7 = 0x84C7;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE8 = 0x84C8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE9 = 0x84C9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE10 = 0x84CA;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE11 = 0x84CB;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE12 = 0x84CC;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE13 = 0x84CD;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE14 = 0x84CE;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE15 = 0x84CF;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE16 = 0x84D0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE17 = 0x84D1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE18 = 0x84D2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE19 = 0x84D3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE20 = 0x84D4;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE21 = 0x84D5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE22 = 0x84D6;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE23 = 0x84D7;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE24 = 0x84D8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE25 = 0x84D9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE26 = 0x84DA;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE27 = 0x84DB;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE28 = 0x84DC;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE29 = 0x84DD;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE30 = 0x84DE;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE31 = 0x84DF;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.ACTIVE_TEXTURE = 0x84E0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.REPEAT = 0x2901;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CLAMP_TO_EDGE = 0x812F;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MIRRORED_REPEAT = 0x8370;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_VEC2 = 0x8B50;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_VEC3 = 0x8B51;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_VEC4 = 0x8B52;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INT_VEC2 = 0x8B53;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INT_VEC3 = 0x8B54;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INT_VEC4 = 0x8B55;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BOOL = 0x8B56;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BOOL_VEC2 = 0x8B57;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BOOL_VEC3 = 0x8B58;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BOOL_VEC4 = 0x8B59;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_MAT2 = 0x8B5A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_MAT3 = 0x8B5B;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FLOAT_MAT4 = 0x8B5C;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLER_2D = 0x8B5E;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.SAMPLER_CUBE = 0x8B60;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_SIZE = 0x8623;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_TYPE = 0x8625;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_POINTER = 0x8645;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPILE_STATUS = 0x8B81;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LOW_FLOAT = 0x8DF0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MEDIUM_FLOAT = 0x8DF1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.HIGH_FLOAT = 0x8DF2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.LOW_INT = 0x8DF3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MEDIUM_INT = 0x8DF4;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.HIGH_INT = 0x8DF5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER = 0x8D40;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER = 0x8D41;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RGBA4 = 0x8056;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RGB5_A1 = 0x8057;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RGB565 = 0x8D62;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_COMPONENT16 = 0x81A5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_INDEX = 0x1901;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_INDEX8 = 0x8D48;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_STENCIL = 0x84F9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_WIDTH = 0x8D42;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_HEIGHT = 0x8D43;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_INTERNAL_FORMAT = 0x8D44;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_RED_SIZE = 0x8D50;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_GREEN_SIZE = 0x8D51;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_BLUE_SIZE = 0x8D52;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_ALPHA_SIZE = 0x8D53;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_DEPTH_SIZE = 0x8D54;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_STENCIL_SIZE = 0x8D55;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COLOR_ATTACHMENT0 = 0x8CE0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_ATTACHMENT = 0x8D00;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.STENCIL_ATTACHMENT = 0x8D20;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.DEPTH_STENCIL_ATTACHMENT = 0x821A;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.NONE = 0;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_COMPLETE = 0x8CD5;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_UNSUPPORTED = 0x8CDD;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAMEBUFFER_BINDING = 0x8CA6;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.RENDERBUFFER_BINDING = 0x8CA7;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_RENDERBUFFER_SIZE = 0x84E8;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.INVALID_FRAMEBUFFER_OPERATION = 0x0506;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNPACK_FLIP_Y_WEBGL = 0x9240;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.CONTEXT_LOST_WEBGL = 0x9242;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +goog.webgl.BROWSER_DEFAULT_WEBGL = 0x9244;
    +
    +
    +/**
    + * From the OES_texture_half_float extension.
    + * http://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.HALF_FLOAT_OES = 0x8D61;
    +
    +
    +/**
    + * From the OES_standard_derivatives extension.
    + * http://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B;
    +
    +
    +/**
    + * From the OES_vertex_array_object extension.
    + * http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.VERTEX_ARRAY_BINDING_OES = 0x85B5;
    +
    +
    +/**
    + * From the WEBGL_debug_renderer_info extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNMASKED_VENDOR_WEBGL = 0x9245;
    +
    +
    +/**
    + * From the WEBGL_debug_renderer_info extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.UNMASKED_RENDERER_WEBGL = 0x9246;
    +
    +
    +/**
    + * From the WEBGL_compressed_texture_s3tc extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0;
    +
    +
    +/**
    + * From the WEBGL_compressed_texture_s3tc extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
    +
    +
    +/**
    + * From the WEBGL_compressed_texture_s3tc extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
    +
    +
    +/**
    + * From the WEBGL_compressed_texture_s3tc extension.
    + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
    +
    +
    +/**
    + * From the EXT_texture_filter_anisotropic extension.
    + * http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE;
    +
    +
    +/**
    + * From the EXT_texture_filter_anisotropic extension.
    + * http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
    + * @const
    + * @type {number}
    + */
    +goog.webgl.MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF;
    diff --git a/mapbox-gl/closure-library/closure/goog/window/window.js b/mapbox-gl/closure-library/closure/goog/window/window.js
    new file mode 100644
    index 0000000000..a937727c85
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/window/window.js
    @@ -0,0 +1,225 @@
    +// Copyright 2006 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +/**
    + * @fileoverview Utilities for window manipulation.
    + */
    +
    +
    +goog.provide('goog.window');
    +
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * Default height for popup windows
    + * @type {number}
    + */
    +goog.window.DEFAULT_POPUP_HEIGHT = 500;
    +
    +
    +/**
    + * Default width for popup windows
    + * @type {number}
    + */
    +goog.window.DEFAULT_POPUP_WIDTH = 690;
    +
    +
    +/**
    + * Default target for popup windows
    + * @type {string}
    + */
    +goog.window.DEFAULT_POPUP_TARGET = 'google_popup';
    +
    +
    +/**
    + * Opens a new window.
    + *
    + * @param {string|Object} linkRef A string or an object that supports toString,
    + *     for example goog.Uri.  If this is an object with a 'href' attribute, such
    + *     as HTMLAnchorElement, it will be used instead.
    + *
    + * @param {Object=} opt_options supports the following options:
    + *  'target': (string) target (window name). If null, linkRef.target will
    + *          be used.
    + *  'width': (number) window width.
    + *  'height': (number) window height.
    + *  'top': (number) distance from top of screen
    + *  'left': (number) distance from left of screen
    + *  'toolbar': (boolean) show toolbar
    + *  'scrollbars': (boolean) show scrollbars
    + *  'location': (boolean) show location
    + *  'statusbar': (boolean) show statusbar
    + *  'menubar': (boolean) show menubar
    + *  'resizable': (boolean) resizable
    + *  'noreferrer': (boolean) whether to attempt to remove the referrer header
    + *      from the request headers. Does this by opening a blank window that
    + *      then redirects to the target url, so users may see some flickering.
    + *
    + * @param {Window=} opt_parentWin Parent window that should be used to open the
    + *                 new window.
    + *
    + * @return {Window} Returns the window object that was opened. This returns
    + *                  null if a popup blocker prevented the window from being
    + *                  opened.
    + */
    +goog.window.open = function(linkRef, opt_options, opt_parentWin) {
    +  if (!opt_options) {
    +    opt_options = {};
    +  }
    +  var parentWin = opt_parentWin || window;
    +
    +  // HTMLAnchorElement has a toString() method with the same behavior as
    +  // goog.Uri in all browsers except for Safari, which returns
    +  // '[object HTMLAnchorElement]'.  We check for the href first, then
    +  // assume that it's a goog.Uri or String otherwise.
    +  var href = typeof linkRef.href != 'undefined' ? linkRef.href :
    +      String(linkRef);
    +  var target = opt_options.target || linkRef.target;
    +
    +  var sb = [];
    +  for (var option in opt_options) {
    +    switch (option) {
    +      case 'width':
    +      case 'height':
    +      case 'top':
    +      case 'left':
    +        sb.push(option + '=' + opt_options[option]);
    +        break;
    +      case 'target':
    +      case 'noreferrer':
    +        break;
    +      default:
    +        sb.push(option + '=' + (opt_options[option] ? 1 : 0));
    +    }
    +  }
    +  var optionString = sb.join(',');
    +
    +  var newWin;
    +  if (opt_options['noreferrer']) {
    +    // Use a meta-refresh to stop the referrer from being included in the
    +    // request headers.
    +    newWin = parentWin.open('', target, optionString);
    +    if (newWin) {
    +      if (goog.userAgent.IE) {
    +        // IE has problems parsing the content attribute if the url contains
    +        // a semicolon. We can fix this by adding quotes around the url, but
    +        // then we can't parse quotes in the URL correctly. We take a
    +        // best-effort approach.
    +        //
    +        // If the URL has semicolons, wrap it in single quotes to protect
    +        // the semicolons.
    +        // If the URL has semicolons and single quotes, url-encode the single
    +        // quotes as well.
    +        //
    +        // This is imperfect. Notice that both ' and ; are reserved characters
    +        // in URIs, so this could do the wrong thing, but at least it will
    +        // do the wrong thing in only rare cases.
    +        // ugh.
    +        if (href.indexOf(';') != -1) {
    +          href = "'" + href.replace(/'/g, '%27') + "'";
    +        }
    +      }
    +      newWin.opener = null;
    +      href = goog.string.htmlEscape(href);
    +      newWin.document.write('<META HTTP-EQUIV="refresh" content="0; url=' +
    +                            href + '">');
    +      newWin.document.close();
    +    }
    +  } else {
    +    newWin = parentWin.open(href, target, optionString);
    +  }
    +  // newWin is null if a popup blocker prevented the window open.
    +  return newWin;
    +};
    +
    +
    +/**
    + * Opens a new window without any real content in it.
    + *
    + * This can be used to get around popup blockers if you need to open a window
    + * in response to a user event, but need to do asynchronous work to determine
    + * the URL to open, and then set the URL later.
    + *
    + * Example usage:
    + *
    + * var newWin = goog.window.openBlank('Loading...');
    + * setTimeout(
    + *     function() {
    + *       newWin.location.href = 'http://www.google.com';
    + *     }, 100);
    + *
    + * @param {string=} opt_message String to show in the new window. This string
    + *     will be HTML-escaped to avoid XSS issues.
    + * @param {Object=} opt_options Options to open window with.
    + *     {@see goog.window.open for exact option semantics}.
    + * @param {Window=} opt_parentWin Parent window that should be used to open the
    + *                 new window.
    + * @return {Window} Returns the window object that was opened. This returns
    + *                  null if a popup blocker prevented the window from being
    + *                  opened.
    + */
    +goog.window.openBlank = function(opt_message, opt_options, opt_parentWin) {
    +
    +  // Open up a window with the loading message and nothing else.
    +  // This will be interpreted as HTML content type with a missing doctype
    +  // and html/body tags, but is otherwise acceptable.
    +  var loadingMessage = opt_message ? goog.string.htmlEscape(opt_message) : '';
    +  return /** @type {Window} */ (goog.window.open(
    +      'javascript:"' + encodeURI(loadingMessage) + '"',
    +      opt_options, opt_parentWin));
    +};
    +
    +
    +/**
    + * Raise a help popup window, defaulting to "Google standard" size and name.
    + *
    + * (If your project is using GXPs, consider using {@link PopUpLink.gxp}.)
    + *
    + * @param {string|Object} linkRef if this is a string, it will be used as the
    + * URL of the popped window; otherwise it's assumed to be an HTMLAnchorElement
    + * (or some other object with "target" and "href" properties).
    + *
    + * @param {Object=} opt_options Options to open window with.
    + *     {@see goog.window.open for exact option semantics}
    + *     Additional wrinkles to the options:
    + *     - if 'target' field is null, linkRef.target will be used. If *that's*
    + *     null, the default is "google_popup".
    + *     - if 'width' field is not specified, the default is 690.
    + *     - if 'height' field is not specified, the default is 500.
    + *
    + * @return {boolean} true if the window was not popped up, false if it was.
    + */
    +goog.window.popup = function(linkRef, opt_options) {
    +  if (!opt_options) {
    +    opt_options = {};
    +  }
    +
    +  // set default properties
    +  opt_options['target'] = opt_options['target'] ||
    +      linkRef['target'] || goog.window.DEFAULT_POPUP_TARGET;
    +  opt_options['width'] = opt_options['width'] ||
    +      goog.window.DEFAULT_POPUP_WIDTH;
    +  opt_options['height'] = opt_options['height'] ||
    +      goog.window.DEFAULT_POPUP_HEIGHT;
    +
    +  var newWin = goog.window.open(linkRef, opt_options);
    +  if (!newWin) {
    +    return true;
    +  }
    +  newWin.focus();
    +
    +  return false;
    +};
    diff --git a/mapbox-gl/closure-library/closure/goog/window/window_test.html b/mapbox-gl/closure-library/closure/goog/window/window_test.html
    new file mode 100644
    index 0000000000..20f779386f
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/window/window_test.html
    @@ -0,0 +1,44 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +Use of this source code is governed by the Apache License, Version 2.0.
    +See the COPYING file for details.
    +-->
    +<!--
    +
    +  @author marcosalmeida@google.com (Marcos Almeida)
    +-->
    + <head>
    +  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    +  <title>
    +   Closure Unit Tests - goog.window
    +  </title>
    +  <script src="../base.js">
    +  </script>
    +  <script>
    +    goog.require('goog.windowTest');
    +  </script>
    +  <style type="text/css">
    +   .goog-like-link {
    +  color: blue;
    +  text-decoration: underline;
    +  cursor: pointer;
    +}
    +
    +</style>
    +</head>
    +<body>
    +
    +<h4>Some links for testing referrer stripping manually.</h4>
    +<div class='goog-like-link'>http://www.google.com/search?q=;</div>
    +<div class='goog-like-link'>http://www.google.com/search?q=x&amp;lang=en</div>
    +<div class='goog-like-link'>http://www.google.com/search?q=x;lang=en</div>
    +<div class='goog-like-link'>http://www.google.com/search?q="</div>
    +<div class='goog-like-link'>http://www.google.com/search?q='</div>
    +<div class='goog-like-link'>http://www.google.com/search?q=&lt;</div>
    +<div class='goog-like-link'>http://www.google.com/search?q=&gt;</div>
    +
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/closure/goog/window/window_test.js b/mapbox-gl/closure-library/closure/goog/window/window_test.js
    new file mode 100644
    index 0000000000..add715d0e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/closure/goog/window/window_test.js
    @@ -0,0 +1,217 @@
    +// Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +goog.provide('goog.windowTest');
    +goog.setTestOnly('goog.windowTest');
    +
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('goog.string');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.jsunit');
    +goog.require('goog.window');
    +
    +var newWin;
    +var REDIRECT_URL_PREFIX = 'window_test.html?runTests=';
    +var asyncTestCase =
    +    goog.testing.AsyncTestCase.createAndInstall(document.title);
    +asyncTestCase.stepTimeout = 5000;
    +
    +var WIN_LOAD_TRY_TIMEOUT = 100;
    +var MAX_WIN_LOAD_TRIES = 50; // 50x100ms = 5s waiting for window to load.
    +var winLoadCounter;
    +
    +function setUpPage() {
    +  var anchors = goog.dom.getElementsByTagNameAndClass(
    +      'div', 'goog-like-link');
    +  for (var i = 0; i < anchors.length; i++) {
    +    goog.events.listen(
    +        anchors[i], 'click',
    +        function(e) {
    +          goog.window.open(
    +              goog.dom.getTextContent(e.target), {'noreferrer': true});
    +        });
    +  }
    +}
    +
    +
    +/**
    + * Some tests should only run locally, because they will trigger
    + * popup blockers on http urls.
    + */
    +function canOpenPopups() {
    +  // TODO(nicksantos): Fix the test runner farm.
    +  return window.location.toString().indexOf('file://') == 0;
    +}
    +
    +if (canOpenPopups()) {
    +  // To test goog.window.open we open a new window with this file again. Once
    +  // the new window gets to this point in the file it notifies the opener that
    +  // it has loaded, so that the opener knows that the new window has been
    +  // populated with properties like referrer and location.
    +  var newWinLoaded = false;
    +  if (window.opener && window.opener.newWinLoaded === false) {
    +    window.opener.newWinLoaded = true;
    +  }
    +}
    +
    +function setUp() {
    +  newWinLoaded = false;
    +}
    +
    +function tearDown() {
    +  if (newWin) {
    +    newWin.close();
    +  }
    +}
    +
    +
    +/**
    + * Uses setTimeout to keep checking if a new window has been loaded, and once
    + * it has, calls the given continuation function and then calls
    + * asyncTestCase.continueTesting() to resume the flow of the test.
    + * @param {Function} continueFn Continuation function to be called when the
    + *     new window has loaded.
    + * @param {number=} opt_numTries Number of times this method has checked if
    + *     the window has loaded, to prevent getting in an endless setTimeout
    + *     loop. (Used internally, callers should omit.)
    + */
    +function continueAfterWindowLoaded(continueFn, opt_numTries) {
    +  opt_numTries = opt_numTries || 0;
    +  if (newWinLoaded) {
    +    continueFn();
    +    asyncTestCase.continueTesting();
    +  } else if (opt_numTries > MAX_WIN_LOAD_TRIES) {
    +    fail('Window did not load after maximum number of checks.');
    +    asyncTestCase.continueTesting();
    +  } else {
    +    setTimeout(goog.partial(continueAfterWindowLoaded,
    +                            continueFn, ++opt_numTries),
    +               WIN_LOAD_TRY_TIMEOUT);
    +  }
    +}
    +
    +
    +/**
    + * Helper to kick off a test that opens a window and checks that the referrer
    + * is hidden if requested and the url is properly encoded/decoded.
    + * @param {boolean} noreferrer Whether to test the noreferrer option.
    + * @param {string} urlParam Url param to append to the url being opened.
    + */
    +function doTestOpenWindow(noreferrer, urlParam) {
    +  if (!canOpenPopups()) {
    +    return;
    +  }
    +  newWin = goog.window.open(REDIRECT_URL_PREFIX + urlParam,
    +                            {'noreferrer': noreferrer});
    +  asyncTestCase.waitForAsync('Waiting for window to open and load.');
    +  continueAfterWindowLoaded(
    +      goog.partial(continueTestOpenWindow, noreferrer, urlParam));
    +}
    +
    +
    +/**
    + * Helper callback to do asserts after the window opens.
    + * @param {boolean} noreferrer Whether the noreferrer option is being tested.
    + * @param {string} urlParam Url param appended to the url being opened.
    + */
    +function continueTestOpenWindow(noreferrer, urlParam) {
    +  if (noreferrer) {
    +    assertEquals('Referrer should have been stripped',
    +                 '', newWin.document.referrer);
    +  }
    +
    +  var newWinUrl = decodeURI(newWin.location);
    +  var expectedUrlSuffix = decodeURI(urlParam);
    +  assertTrue('New window href should have ended with <' + expectedUrlSuffix +
    +      '> but was <' + newWinUrl + '>',
    +      goog.string.endsWith(newWinUrl, expectedUrlSuffix));
    +}
    +
    +
    +function testOpenNotEncoded() {
    +  doTestOpenWindow(false, '"bogus~"');
    +}
    +
    +function testOpenEncoded() {
    +  doTestOpenWindow(false, '"bogus%7E"');
    +}
    +
    +function testOpenEncodedPercent() {
    +  // Intent of url is to pass %7E to the server, so it was encoded to %257E .
    +  doTestOpenWindow(false, '"bogus%257E"');
    +}
    +
    +function testOpenNotEncodedHidingReferrer() {
    +  doTestOpenWindow(true, '"bogus~"');
    +}
    +
    +function testOpenEncodedHidingReferrer() {
    +  doTestOpenWindow(true, '"bogus%7E"');
    +}
    +
    +function testOpenEncodedPercentHidingReferrer() {
    +  // Intent of url is to pass %7E to the server, so it was encoded to %257E .
    +  doTestOpenWindow(true, '"bogus%257E"');
    +}
    +
    +function testOpenSemicolon() {
    +  doTestOpenWindow(true, 'beforesemi;aftersemi');
    +}
    +
    +function testTwoSemicolons() {
    +  doTestOpenWindow(true, 'a;b;c');
    +}
    +
    +function testOpenAmpersand() {
    +  doTestOpenWindow(true, 'this&that');
    +}
    +
    +function testOpenSingleQuote() {
    +  doTestOpenWindow(true, "'");
    +}
    +
    +function testOpenDoubleQuote() {
    +  doTestOpenWindow(true, '"');
    +}
    +
    +function testOpenDoubleQuote() {
    +  doTestOpenWindow(true, '<');
    +}
    +
    +function testOpenDoubleQuote() {
    +  doTestOpenWindow(true, '>');
    +}
    +
    +function testOpenBlank() {
    +  if (!canOpenPopups()) {
    +    return;
    +  }
    +  newWin = goog.window.openBlank('Loading...');
    +  asyncTestCase.waitForAsync('Waiting for temp window to open and load.');
    +  var urlParam = 'bogus~';
    +
    +  var continueFn = function() {
    +    newWin.location.href = REDIRECT_URL_PREFIX + urlParam;
    +    continueAfterWindowLoaded(
    +        goog.partial(continueTestOpenWindow, false, urlParam));
    +  };
    +  setTimeout(continueFn, 100);
    +}
    +
    +
    +/** @this {Element} */
    +function stripReferrer() {
    +  goog.window.open(this.href, {'noreferrer': true});
    +}
    diff --git a/mapbox-gl/closure-library/shamcs.py b/mapbox-gl/closure-library/shamcs.py
    new file mode 100644
    index 0000000000..d7c679b5a8
    --- /dev/null
    +++ b/mapbox-gl/closure-library/shamcs.py
    @@ -0,0 +1,63 @@
    +"""Script to generate MC tests for Closure SHA functions"""
    +
    +from binascii import hexlify
    +from hashlib import sha1, sha224, sha256, sha384, sha512
    +
    +from Crypto.Cipher import AES
    +
    +C = AES.new('Closure Library SHA KATs        ')
    +MC_START = ''.join(C.encrypt(('MC' * 15) + '\x00' + chr(i)) for i in range(16))
    +
    +
    +TEST_TEMPLATE = '''\
    +function test{shatitle}() {{
    +  var sha = new goog.crypt.{shatitle}();
    +  var initial_state = '{initial_state}';
    +  var count = {count};
    +  var state = goog.crypt.stringToByteArray(initial_state);
    +  var digest;
    +  for (var i = 0; i < count; i++) {{
    +    sha.reset();
    +    sha.update(state);
    +    digest = sha.digest();
    +    state = goog.array.concat(digest, state);
    +  }}
    +  assertEquals({lenfinal}, state.length);
    +  assertEquals('{expected}',
    +    goog.crypt.byteArrayToHex(digest));
    +  sha.reset();
    +  for (var i = 0; i < ({lenfinal} + 10); i++) {{
    +    sha.update(state, i);
    +  }}
    +  assertEquals('{expected2}',
    +    goog.crypt.byteArrayToHex(sha.digest()));
    +}}
    +'''
    +
    +def genmc(sha, name, blocklen, digestlen):
    +    """Generate a Monte Carlo test for each SHA instance."""
    +    count = (2 ** 15) // digestlen
    +    state = name
    +    for i in range(count):
    +        digest = sha(state).digest()
    +        state = digest + state
    +    final_state = state
    +    lenfinal = len(final_state)
    +    h = sha()
    +    for i in range(len(final_state) + 10):
    +        h.update(final_state[:i])
    +    print(TEST_TEMPLATE.format(initial_state=name, count=count,
    +                               lenfinal=len(final_state),
    +                               expected=hexlify(digest),
    +                               shatitle=name,
    +                               expected2=hexlify(h.digest())))
    +
    +
    +SHAS = [(sha1, 'Sha1', 64, 160//8),
    +        (sha224, 'Sha224', 64, 224//8),
    +        (sha256, 'Sha256', 64, 256//8),
    +        (sha384, 'Sha384', 128, 384//8),
    +        (sha512, 'Sha512', 128, 512//8)]
    +
    +for s in SHAS:
    +    genmc(*s)
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/base.js b/mapbox-gl/closure-library/third_party/closure/goog/base.js
    new file mode 100644
    index 0000000000..c8890433f5
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/base.js
    @@ -0,0 +1,2 @@
    +// This is a dummy file to trick genjsdeps into doing the right thing.
    +// TODO(nicksantos): fix this
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlparser.js b/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlparser.js
    new file mode 100644
    index 0000000000..d241d4bb27
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlparser.js
    @@ -0,0 +1,611 @@
    +// Copyright 2006-2008, The Google Caja project.
    +// Modifications Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +// All Rights Reserved
    +
    +/**
    + * @license Portions of this code are from the google-caja project, received by
    + * Google under the Apache license (http://code.google.com/p/google-caja/).
    + * All other code is Copyright 2009 Google, Inc. All Rights Reserved.
    +
    +// Copyright (C) 2006 Google Inc.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    + */
    +
    +/**
    + * @fileoverview A Html SAX parser.
    + *
    + * Examples of usage of the {@code goog.string.html.HtmlParser}:
    + * <pre>
    + *   var handler = new MyCustomHtmlVisitorHandlerThatExtendsHtmlSaxHandler();
    + *   var parser = new goog.string.html.HtmlParser();
    + *   parser.parse(handler, '<html><a href="google.com">link found!</a></html>');
    + * </pre>
    + *
    + * TODO(user, msamuel): validate sanitizer regex against the HTML5 grammar at
    + * http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html
    + * http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
    + * http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
    + * http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html
    + *
    + * @supported IE6, IE7, IE8, FF1.5, FF2, FF3, Chrome 3.0, Safari and Opera 10.
    + */
    +
    +goog.provide('goog.string.html.HtmlParser');
    +goog.provide('goog.string.html.HtmlParser.EFlags');
    +goog.provide('goog.string.html.HtmlParser.Elements');
    +goog.provide('goog.string.html.HtmlParser.Entities');
    +goog.provide('goog.string.html.HtmlSaxHandler');
    +
    +
    +/**
    + * An Html parser: {@code parse} takes a string and calls methods on
    + * {@code goog.string.html.HtmlSaxHandler} while it is visiting it.
    + *
    + * @constructor
    + */
    +goog.string.html.HtmlParser = function() {
    +};
    +
    +
    +/**
    + * HTML entities that are encoded/decoded.
    + * TODO(user): use {@code goog.string.htmlEncode} instead.
    + * @enum {string}
    + */
    +goog.string.html.HtmlParser.Entities = {
    +  lt: '<',
    +  gt: '>',
    +  amp: '&',
    +  nbsp: '\240',
    +  quot: '"',
    +  apos: '\''
    +};
    +
    +
    +/**
    + * The html eflags, used internally on the parser.
    + * @enum {number}
    + */
    +goog.string.html.HtmlParser.EFlags = {
    +  OPTIONAL_ENDTAG: 1,
    +  EMPTY: 2,
    +  CDATA: 4,
    +  RCDATA: 8,
    +  UNSAFE: 16,
    +  FOLDABLE: 32
    +};
    +
    +
    +/**
    + * A map of element to a bitmap of flags it has, used internally on the parser.
    + * @type {Object}
    + */
    +goog.string.html.HtmlParser.Elements = {
    +  'a': 0,
    +  'abbr': 0,
    +  'acronym': 0,
    +  'address': 0,
    +  'applet': goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'area': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'b': 0,
    +  'base': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'basefont': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'bdo': 0,
    +  'big': 0,
    +  'blockquote': 0,
    +  'body': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.FOLDABLE,
    +  'br': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'button': 0,
    +  'caption': 0,
    +  'center': 0,
    +  'cite': 0,
    +  'code': 0,
    +  'col': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'colgroup': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'dd': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'del': 0,
    +  'dfn': 0,
    +  'dir': 0,
    +  'div': 0,
    +  'dl': 0,
    +  'dt': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'em': 0,
    +  'fieldset': 0,
    +  'font': 0,
    +  'form': 0,
    +  'frame': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'frameset': goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'h1': 0,
    +  'h2': 0,
    +  'h3': 0,
    +  'h4': 0,
    +  'h5': 0,
    +  'h6': 0,
    +  'head': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.FOLDABLE,
    +  'hr': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'html': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.FOLDABLE,
    +  'i': 0,
    +  'iframe': goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.CDATA,
    +  'img': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'input': goog.string.html.HtmlParser.EFlags.EMPTY,
    +  'ins': 0,
    +  'isindex': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'kbd': 0,
    +  'label': 0,
    +  'legend': 0,
    +  'li': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'link': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'map': 0,
    +  'menu': 0,
    +  'meta': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'noframes': goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.CDATA,
    +  'noscript': goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.CDATA,
    +  'object': goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'ol': 0,
    +  'optgroup': 0,
    +  'option': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'p': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'param': goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'pre': 0,
    +  'q': 0,
    +  's': 0,
    +  'samp': 0,
    +  'script': goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.CDATA,
    +  'select': 0,
    +  'small': 0,
    +  'span': 0,
    +  'strike': 0,
    +  'strong': 0,
    +  'style': goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.CDATA,
    +  'sub': 0,
    +  'sup': 0,
    +  'table': 0,
    +  'tbody': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'td': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'textarea': goog.string.html.HtmlParser.EFlags.RCDATA,
    +  'tfoot': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'th': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'thead': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'title': goog.string.html.HtmlParser.EFlags.RCDATA |
    +      goog.string.html.HtmlParser.EFlags.UNSAFE,
    +  'tr': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
    +  'tt': 0,
    +  'u': 0,
    +  'ul': 0,
    +  'var': 0
    +};
    +
    +
    +/**
    + * Regular expression that matches &s.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.AMP_RE_ = /&/g;
    +
    +
    +/**
    + * Regular expression that matches loose &s.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.LOOSE_AMP_RE_ =
    +    /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi;
    +
    +
    +/**
    + * Regular expression that matches <.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.LT_RE_ = /</g;
    +
    +
    +/**
    + * Regular expression that matches >.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.GT_RE_ = />/g;
    +
    +
    +/**
    + * Regular expression that matches ".
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.QUOTE_RE_ = /\"/g;
    +
    +
    +/**
    + * Regular expression that matches =.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.EQUALS_RE_ = /=/g;
    +
    +
    +/**
    + * Regular expression that matches null characters.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.NULL_RE_ = /\0/g;
    +
    +
    +/**
    + * Regular expression that matches entities.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.ENTITY_RE_ = /&(#\d+|#x[0-9A-Fa-f]+|\w+);/g;
    +
    +
    +/**
    + * Regular expression that matches decimal numbers.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.DECIMAL_ESCAPE_RE_ = /^#(\d+)$/;
    +
    +
    +/**
    + * Regular expression that matches hexadecimal numbers.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.HEX_ESCAPE_RE_ = /^#x([0-9A-Fa-f]+)$/;
    +
    +
    +/**
    + * Regular expression that matches the next token to be processed.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.INSIDE_TAG_TOKEN_ = new RegExp(
    +    // Don't capture space.
    +    '^\\s*(?:' +
    +    // Capture an attribute name in group 1, and value in group 3.
    +    // We capture the fact that there was an attribute in group 2, since
    +    // interpreters are inconsistent in whether a group that matches nothing
    +    // is null, undefined, or the empty string.
    +    ('(?:' +
    +       '([a-z][a-z-]*)' +                   // attribute name
    +       ('(' +                               // optionally followed
    +          '\\s*=\\s*' +
    +          ('(' +
    +             // A double quoted string.
    +             '\"[^\"]*\"' +
    +             // A single quoted string.
    +             '|\'[^\']*\'' +
    +             // The positive lookahead is used to make sure that in
    +             // <foo bar= baz=boo>, the value for bar is blank, not "baz=boo".
    +             '|(?=[a-z][a-z-]*\\s*=)' +
    +             // An unquoted value that is not an attribute name.
    +             // We know it is not an attribute name because the previous
    +             // zero-width match would've eliminated that possibility.
    +             '|[^>\"\'\\s]*' +
    +             ')'
    +             ) +
    +          ')'
    +          ) + '?' +
    +       ')'
    +       ) +
    +    // End of tag captured in group 3.
    +    '|(/?>)' +
    +    // Don't capture cruft
    +    '|[^a-z\\s>]+)',
    +    'i');
    +
    +
    +/**
    + * Regular expression that matches the next token to be processed when we are
    + * outside a tag.
    + * @type {RegExp}
    + * @private
    + */
    +goog.string.html.HtmlParser.OUTSIDE_TAG_TOKEN_ = new RegExp(
    +    '^(?:' +
    +    // Entity captured in group 1.
    +    '&(\\#[0-9]+|\\#[x][0-9a-f]+|\\w+);' +
    +    // Comment, doctypes, and processing instructions not captured.
    +    '|<[!]--[\\s\\S]*?-->|<!\\w[^>]*>|<\\?[^>*]*>' +
    +    // '/' captured in group 2 for close tags, and name captured in group 3.
    +    '|<(/)?([a-z][a-z0-9]*)' +
    +    // Text captured in group 4.
    +    '|([^<&>]+)' +
    +    // Cruft captured in group 5.
    +    '|([<&>]))',
    +    'i');
    +
    +
    +/**
    + * Given a SAX-like {@code goog.string.html.HtmlSaxHandler} parses a
    + * {@code htmlText} and lets the {@code handler} know the structure while
    + * visiting the nodes.
    + *
    + * @param {goog.string.html.HtmlSaxHandler} handler The HtmlSaxHandler that will
    + *     receive the events.
    + * @param {string} htmlText The html text.
    + */
    +goog.string.html.HtmlParser.prototype.parse = function(handler, htmlText) {
    +  var htmlLower = null;
    +  var inTag = false;  // True iff we're currently processing a tag.
    +  var attribs = [];  // Accumulates attribute names and values.
    +  var tagName;  // The name of the tag currently being processed.
    +  var eflags;  // The element flags for the current tag.
    +  var openTag;  // True if the current tag is an open tag.
    +
    +  // Lets the handler know that we are starting to parse the document.
    +  handler.startDoc();
    +
    +  // Consumes tokens from the htmlText and stops once all tokens are processed.
    +  while (htmlText) {
    +    var regex = inTag ?
    +        goog.string.html.HtmlParser.INSIDE_TAG_TOKEN_ :
    +        goog.string.html.HtmlParser.OUTSIDE_TAG_TOKEN_;
    +    // Gets the next token
    +    var m = htmlText.match(regex);
    +    // And removes it from the string
    +    htmlText = htmlText.substring(m[0].length);
    +
    +    // TODO(goto): cleanup this code breaking it into separate methods.
    +    if (inTag) {
    +      if (m[1]) { // Attribute.
    +        // SetAttribute with uppercase names doesn't work on IE6.
    +        var attribName = goog.string.html.toLowerCase(m[1]);
    +        var decodedValue;
    +        if (m[2]) {
    +          var encodedValue = m[3];
    +          switch (encodedValue.charCodeAt(0)) {  // Strip quotes.
    +            case 34: case 39:
    +              encodedValue = encodedValue.substring(
    +                  1, encodedValue.length - 1);
    +              break;
    +          }
    +          decodedValue = this.unescapeEntities_(this.stripNULs_(encodedValue));
    +        } else {
    +          // Use name as value for valueless attribs, so
    +          //   <input type=checkbox checked>
    +          // gets attributes ['type', 'checkbox', 'checked', 'checked']
    +          decodedValue = attribName;
    +        }
    +        attribs.push(attribName, decodedValue);
    +      } else if (m[4]) {
    +        if (eflags !== void 0) {  // False if not in whitelist.
    +          if (openTag) {
    +            if (handler.startTag) {
    +              handler.startTag(/** @type {string} */ (tagName), attribs);
    +            }
    +          } else {
    +            if (handler.endTag) {
    +              handler.endTag(/** @type {string} */ (tagName));
    +            }
    +          }
    +        }
    +
    +        if (openTag && (eflags &
    +            (goog.string.html.HtmlParser.EFlags.CDATA |
    +             goog.string.html.HtmlParser.EFlags.RCDATA))) {
    +          if (htmlLower === null) {
    +            htmlLower = goog.string.html.toLowerCase (htmlText);
    +          } else {
    +           htmlLower = htmlLower.substring(
    +                htmlLower.length - htmlText.length);
    +          }
    +          var dataEnd = htmlLower.indexOf('</' + tagName);
    +          if (dataEnd < 0) {
    +            dataEnd = htmlText.length;
    +          }
    +          if (eflags & goog.string.html.HtmlParser.EFlags.CDATA) {
    +            if (handler.cdata) {
    +              handler.cdata(htmlText.substring(0, dataEnd));
    +            }
    +          } else if (handler.rcdata) {
    +            handler.rcdata(
    +                this.normalizeRCData_(htmlText.substring(0, dataEnd)));
    +          }
    +          htmlText = htmlText.substring(dataEnd);
    +        }
    +
    +        tagName = eflags = openTag = void 0;
    +        attribs.length = 0;
    +        inTag = false;
    +      }
    +    } else {
    +      if (m[1]) {  // Entity.
    +        handler.pcdata(m[0]);
    +      } else if (m[3]) {  // Tag.
    +        openTag = !m[2];
    +        inTag = true;
    +        tagName = goog.string.html.toLowerCase (m[3]);
    +        eflags = goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName) ?
    +            goog.string.html.HtmlParser.Elements[tagName] : void 0;
    +      } else if (m[4]) {  // Text.
    +        handler.pcdata(m[4]);
    +      } else if (m[5]) {  // Cruft.
    +        switch (m[5]) {
    +          case '<': handler.pcdata('&lt;'); break;
    +          case '>': handler.pcdata('&gt;'); break;
    +          default: handler.pcdata('&amp;'); break;
    +        }
    +      }
    +    }
    +  }
    +
    +  // Lets the handler know that we are done parsing the document.
    +  handler.endDoc();
    +};
    +
    +
    +/**
    + * Decodes an HTML entity.
    + *
    + * @param {string} name The content between the '&' and the ';'.
    + * @return {string} A single unicode code-point as a string.
    + * @private
    + */
    +goog.string.html.HtmlParser.prototype.lookupEntity_ = function(name) {
    +  // TODO(goto): use {goog.string.htmlDecode} instead ?
    +  // TODO(goto): &pi; is different from &Pi;
    +  name = goog.string.html.toLowerCase(name);
    +  if (goog.string.html.HtmlParser.Entities.hasOwnProperty(name)) {
    +    return goog.string.html.HtmlParser.Entities[name];
    +  }
    +  var m = name.match(goog.string.html.HtmlParser.DECIMAL_ESCAPE_RE_);
    +  if (m) {
    +    return String.fromCharCode(parseInt(m[1], 10));
    +  } else if (
    +      !!(m = name.match(goog.string.html.HtmlParser.HEX_ESCAPE_RE_))) {
    +    return String.fromCharCode(parseInt(m[1], 16));
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Removes null characters on the string.
    + * @param {string} s The string to have the null characters removed.
    + * @return {string} A string without null characters.
    + * @private
    + */
    +goog.string.html.HtmlParser.prototype.stripNULs_ = function(s) {
    +  return s.replace(goog.string.html.HtmlParser.NULL_RE_, '');
    +};
    +
    +
    +/**
    + * The plain text of a chunk of HTML CDATA which possibly containing.
    + *
    + * TODO(goto): use {@code goog.string.unescapeEntities} instead ?
    + * @param {string} s A chunk of HTML CDATA.  It must not start or end inside
    + *   an HTML entity.
    + * @return {string} The unescaped entities.
    + * @private
    + */
    +goog.string.html.HtmlParser.prototype.unescapeEntities_ = function(s) {
    +  return s.replace(
    +      goog.string.html.HtmlParser.ENTITY_RE_,
    +      goog.bind(this.lookupEntity_, this));
    +};
    +
    +
    +/**
    + * Escape entities in RCDATA that can be escaped without changing the meaning.
    + * @param {string} rcdata The RCDATA string we want to normalize.
    + * @return {string} A normalized version of RCDATA.
    + * @private
    + */
    +goog.string.html.HtmlParser.prototype.normalizeRCData_ = function(rcdata) {
    +  return rcdata.
    +      replace(goog.string.html.HtmlParser.LOOSE_AMP_RE_, '&amp;$1').
    +      replace(goog.string.html.HtmlParser.LT_RE_, '&lt;').
    +      replace(goog.string.html.HtmlParser.GT_RE_, '&gt;');
    +};
    +
    +
    +/**
    + * TODO(goto): why isn't this in the string package ? does this solves any
    + * real problem ? move it to the goog.string package if it does.
    + *
    + * @param {string} str The string to lower case.
    + * @return {string} The str in lower case format.
    + */
    +goog.string.html.toLowerCase = function(str) {
    +  // The below may not be true on browsers in the Turkish locale.
    +  if ('script' === 'SCRIPT'.toLowerCase()) {
    +    return str.toLowerCase();
    +  } else {
    +    return str.replace(/[A-Z]/g, function(ch) {
    +      return String.fromCharCode(ch.charCodeAt(0) | 32);
    +    });
    +  }
    +};
    +
    +
    +/**
    + * An interface to the {@code goog.string.html.HtmlParser} visitor, that gets
    + * called while the HTML is being parsed.
    + *
    + * @constructor
    + */
    +goog.string.html.HtmlSaxHandler = function() {
    +};
    +
    +
    +/**
    + * Handler called when the parser found a new tag.
    + * @param {string} name The name of the tag that is starting.
    + * @param {Array.<string>} attributes The attributes of the tag.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.startTag = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when the parser found a closing tag.
    + * @param {string} name The name of the tag that is ending.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.endTag = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when PCDATA is found.
    + * @param {string} text The PCDATA text found.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.pcdata = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when RCDATA is found.
    + * @param {string} text The RCDATA text found.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.rcdata = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when CDATA is found.
    + * @param {string} text The CDATA text found.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.cdata = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when the parser is starting to parse the document.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.startDoc = goog.abstractMethod;
    +
    +
    +/**
    + * Handler called when the parsing is done.
    + */
    +goog.string.html.HtmlSaxHandler.prototype.endDoc = goog.abstractMethod;
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlsanitizer.js b/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlsanitizer.js
    new file mode 100644
    index 0000000000..c027bf09a7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/caja/string/html/htmlsanitizer.js
    @@ -0,0 +1,605 @@
    +// Copyright 2006-2008, The Google Caja project.
    +// Modifications Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +// All Rights Reserved
    +
    +/**
    + * @license Portions of this code are from the google-caja project, received by
    + * Google under the Apache license (http://code.google.com/p/google-caja/).
    + * All other code is Copyright 2009 Google, Inc. All Rights Reserved.
    +
    +// Copyright (C) 2006 Google Inc.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    + */
    +
    +/**
    + * @fileoverview An HTML sanitizer that can satisfy a variety of security
    + * policies. The HTML sanitizer is built around a SAX parser and HTML element
    + * and attributes schemas.
    + *
    + * This package provides html sanitizing and parsing functions.
    + * {@code goog.string.htmlSanitize} is basically just using a custom written
    + * {@code goog.string.HtmlSaxHandler} that outputs safe html as the unsafe
    + * html content is parsed by {@code goog.string.HtmlParser}.
    + *
    + * Examples of usage of the static {@code goog.string.htmlSanitize}:
    + * <pre>
    + *   var safeHtml = goog.string.html.htmlSanitize('<script src="xss.js" />');
    + *   el.innerHTML = safeHtml;
    + * </pre>
    + *
    + * We use {@code goog.string.StringBuffer} for fast string concatenation, since
    + * htmlSanitize is relatively heavy considering that it is designed to parse
    + * large html files.
    + *
    + * @supported IE6, IE7, IE8, FF1.5, FF2, FF3, Chrome 4.0, Safari and Opera 10.
    + */
    +
    +goog.provide('goog.string.html.HtmlSanitizer');
    +goog.provide('goog.string.html.HtmlSanitizer.AttributeType');
    +goog.provide('goog.string.html.HtmlSanitizer.Attributes');
    +goog.provide('goog.string.html.htmlSanitize');
    +
    +goog.require('goog.string.StringBuffer');
    +goog.require('goog.string.html.HtmlParser');
    +goog.require('goog.string.html.HtmlParser.EFlags');
    +goog.require('goog.string.html.HtmlParser.Elements');
    +goog.require('goog.string.html.HtmlSaxHandler');
    +
    +
    +/**
    + * Strips unsafe tags and attributes from HTML.
    + *
    + * @param {string} htmlText The HTML text to sanitize.
    + * @param {function(string): string=} opt_urlPolicy A transform to apply to URL
    + *     attribute values.
    + * @param {function(string): string=} opt_nmTokenPolicy A transform to apply to
    + *     names, IDs, and classes.
    + * @return {string} A sanitized HTML, safe to be embedded on the page.
    + */
    +goog.string.html.htmlSanitize = function(
    +    htmlText, opt_urlPolicy, opt_nmTokenPolicy) {
    +  var stringBuffer = new goog.string.StringBuffer();
    +  var handler = new goog.string.html.HtmlSanitizer(
    +      stringBuffer, opt_urlPolicy, opt_nmTokenPolicy);
    +  var parser = new goog.string.html.HtmlParser();
    +  parser.parse(handler, htmlText);
    +  return stringBuffer.toString();
    +};
    +
    +
    +/**
    + * An implementation of the {@code goog.string.HtmlSaxHandler} interface that
    + * will take each of the html tags and sanitize it.
    + *
    + * @param {goog.string.StringBuffer} stringBuffer A string buffer, used to
    + *     output the html as we sanitize it.
    + * @param {?function(string):string} opt_urlPolicy An optional function to be
    + *     applied in URLs.
    + * @param {?function(string):string} opt_nmTokenPolicy An optional function to
    + *     be applied in names.
    + * @constructor
    + * @extends {goog.string.html.HtmlSaxHandler}
    + */
    +goog.string.html.HtmlSanitizer = function(
    +    stringBuffer, opt_urlPolicy, opt_nmTokenPolicy) {
    +  goog.string.html.HtmlSaxHandler.call(this);
    +
    +  /**
    +   * The string buffer that holds the sanitized version of the html. Used
    +   * during the parse time.
    +   * @type {goog.string.StringBuffer}
    +   * @private
    +   */
    +  this.stringBuffer_ = stringBuffer;
    +
    +  /**
    +   * A stack that holds how the handler is being called.
    +   * @type {Array}
    +   * @private
    +   */
    +  this.stack_ = [];
    +
    +  /**
    +   * Whether we are ignoring what is being processed or not.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.ignoring_ = false;
    +
    +  /**
    +   * A function to be applied to urls found on the parsing process.
    +   * @type {?function(string):string}
    +   * @private
    +   */
    +  this.urlPolicy_ = opt_urlPolicy;
    +
    +  /**
    +   * A function to be applied to names fround on the parsing process.
    +   * @type {?function(string):string}
    +   * @private
    +   */
    +  this.nmTokenPolicy_ = opt_nmTokenPolicy;
    +};
    +goog.inherits(
    +    goog.string.html.HtmlSanitizer,
    +    goog.string.html.HtmlSaxHandler);
    +
    +
    +
    +/**
    + * The HTML types the parser supports.
    + * @enum {number}
    + */
    +goog.string.html.HtmlSanitizer.AttributeType = {
    +  NONE: 0,
    +  URI: 1,
    +  URI_FRAGMENT: 11,
    +  SCRIPT: 2,
    +  STYLE: 3,
    +  ID: 4,
    +  IDREF: 5,
    +  IDREFS: 6,
    +  GLOBAL_NAME: 7,
    +  LOCAL_NAME: 8,
    +  CLASSES: 9,
    +  FRAME_TARGET: 10
    +};
    +
    +
    +/**
    + * A map of attributes to types it has.
    + * @enum {number}
    + */
    +goog.string.html.HtmlSanitizer.Attributes = {
    +  '*::class': goog.string.html.HtmlSanitizer.AttributeType.CLASSES,
    +  '*::dir': 0,
    +  '*::id': goog.string.html.HtmlSanitizer.AttributeType.ID,
    +  '*::lang': 0,
    +  '*::onclick': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::ondblclick': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onkeydown': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onkeypress': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onkeyup': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onload': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onmousedown': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onmousemove': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onmouseout': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onmouseover': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onmouseup': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::style': goog.string.html.HtmlSanitizer.AttributeType.STYLE,
    +  '*::title': 0,
    +  '*::accesskey': 0,
    +  '*::tabindex': 0,
    +  '*::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  '*::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'a::coords': 0,
    +  'a::href': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'a::hreflang': 0,
    +  'a::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
    +  'a::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'a::rel': 0,
    +  'a::rev': 0,
    +  'a::shape': 0,
    +  'a::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
    +  'a::type': 0,
    +  'area::accesskey': 0,
    +  'area::alt': 0,
    +  'area::coords': 0,
    +  'area::href': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'area::nohref': 0,
    +  'area::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'area::shape': 0,
    +  'area::tabindex': 0,
    +  'area::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
    +  'bdo::dir': 0,
    +  'blockquote::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'br::clear': 0,
    +  'button::accesskey': 0,
    +  'button::disabled': 0,
    +  'button::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
    +  'button::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'button::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'button::tabindex': 0,
    +  'button::type': 0,
    +  'button::value': 0,
    +  'caption::align': 0,
    +  'col::align': 0,
    +  'col::char': 0,
    +  'col::charoff': 0,
    +  'col::span': 0,
    +  'col::valign': 0,
    +  'col::width': 0,
    +  'colgroup::align': 0,
    +  'colgroup::char': 0,
    +  'colgroup::charoff': 0,
    +  'colgroup::span': 0,
    +  'colgroup::valign': 0,
    +  'colgroup::width': 0,
    +  'del::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'del::datetime': 0,
    +  'dir::compact': 0,
    +  'div::align': 0,
    +  'dl::compact': 0,
    +  'font::color': 0,
    +  'font::face': 0,
    +  'font::size': 0,
    +  'form::accept': 0,
    +  'form::action': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'form::autocomplete': 0,
    +  'form::enctype': 0,
    +  'form::method': 0,
    +  'form::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
    +  'form::onreset': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'form::onsubmit': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'form::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
    +  'h1::align': 0,
    +  'h2::align': 0,
    +  'h3::align': 0,
    +  'h4::align': 0,
    +  'h5::align': 0,
    +  'h6::align': 0,
    +  'hr::align': 0,
    +  'hr::noshade': 0,
    +  'hr::size': 0,
    +  'hr::width': 0,
    +  'img::align': 0,
    +  'img::alt': 0,
    +  'img::border': 0,
    +  'img::height': 0,
    +  'img::hspace': 0,
    +  'img::ismap': 0,
    +  'img::longdesc': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'img::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
    +  'img::src': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'img::usemap': goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT,
    +  'img::vspace': 0,
    +  'img::width': 0,
    +  'input::accept': 0,
    +  'input::accesskey': 0,
    +  'input::autocomplete': 0,
    +  'input::align': 0,
    +  'input::alt': 0,
    +  'input::checked': 0,
    +  'input::disabled': 0,
    +  'input::ismap': 0,
    +  'input::maxlength': 0,
    +  'input::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
    +  'input::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'input::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'input::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'input::onselect': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'input::readonly': 0,
    +  'input::size': 0,
    +  'input::src': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'input::tabindex': 0,
    +  'input::type': 0,
    +  'input::usemap': goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT,
    +  'input::value': 0,
    +  'ins::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'ins::datetime': 0,
    +  'label::accesskey': 0,
    +  'label::for': goog.string.html.HtmlSanitizer.AttributeType.IDREF,
    +  'label::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'label::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'legend::accesskey': 0,
    +  'legend::align': 0,
    +  'li::type': 0,
    +  'li::value': 0,
    +  'map::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
    +  'menu::compact': 0,
    +  'ol::compact': 0,
    +  'ol::start': 0,
    +  'ol::type': 0,
    +  'optgroup::disabled': 0,
    +  'optgroup::label': 0,
    +  'option::disabled': 0,
    +  'option::label': 0,
    +  'option::selected': 0,
    +  'option::value': 0,
    +  'p::align': 0,
    +  'pre::width': 0,
    +  'q::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
    +  'select::disabled': 0,
    +  'select::multiple': 0,
    +  'select::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
    +  'select::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'select::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'select::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'select::size': 0,
    +  'select::tabindex': 0,
    +  'table::align': 0,
    +  'table::bgcolor': 0,
    +  'table::border': 0,
    +  'table::cellpadding': 0,
    +  'table::cellspacing': 0,
    +  'table::frame': 0,
    +  'table::rules': 0,
    +  'table::summary': 0,
    +  'table::width': 0,
    +  'tbody::align': 0,
    +  'tbody::char': 0,
    +  'tbody::charoff': 0,
    +  'tbody::valign': 0,
    +  'td::abbr': 0,
    +  'td::align': 0,
    +  'td::axis': 0,
    +  'td::bgcolor': 0,
    +  'td::char': 0,
    +  'td::charoff': 0,
    +  'td::colspan': 0,
    +  'td::headers': goog.string.html.HtmlSanitizer.AttributeType.IDREFS,
    +  'td::height': 0,
    +  'td::nowrap': 0,
    +  'td::rowspan': 0,
    +  'td::scope': 0,
    +  'td::valign': 0,
    +  'td::width': 0,
    +  'textarea::accesskey': 0,
    +  'textarea::cols': 0,
    +  'textarea::disabled': 0,
    +  'textarea::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
    +  'textarea::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'textarea::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'textarea::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'textarea::onselect': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
    +  'textarea::readonly': 0,
    +  'textarea::rows': 0,
    +  'textarea::tabindex': 0,
    +  'tfoot::align': 0,
    +  'tfoot::char': 0,
    +  'tfoot::charoff': 0,
    +  'tfoot::valign': 0,
    +  'th::abbr': 0,
    +  'th::align': 0,
    +  'th::axis': 0,
    +  'th::bgcolor': 0,
    +  'th::char': 0,
    +  'th::charoff': 0,
    +  'th::colspan': 0,
    +  'th::headers': goog.string.html.HtmlSanitizer.AttributeType.IDREFS,
    +  'th::height': 0,
    +  'th::nowrap': 0,
    +  'th::rowspan': 0,
    +  'th::scope': 0,
    +  'th::valign': 0,
    +  'th::width': 0,
    +  'thead::align': 0,
    +  'thead::char': 0,
    +  'thead::charoff': 0,
    +  'thead::valign': 0,
    +  'tr::align': 0,
    +  'tr::bgcolor': 0,
    +  'tr::char': 0,
    +  'tr::charoff': 0,
    +  'tr::valign': 0,
    +  'ul::compact': 0,
    +  'ul::type': 0
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.startTag =
    +    function(tagName, attribs) {
    +  if (this.ignoring_) {
    +    return;
    +  }
    +  if (!goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName)) {
    +    return;
    +  }
    +  var eflags = goog.string.html.HtmlParser.Elements[tagName];
    +  if (eflags & goog.string.html.HtmlParser.EFlags.FOLDABLE) {
    +    return;
    +  } else if (eflags & goog.string.html.HtmlParser.EFlags.UNSAFE) {
    +    this.ignoring_ = !(eflags & goog.string.html.HtmlParser.EFlags.EMPTY);
    +    return;
    +  }
    +  attribs = this.sanitizeAttributes_(tagName, attribs);
    +  if (attribs) {
    +    if (!(eflags & goog.string.html.HtmlParser.EFlags.EMPTY)) {
    +      this.stack_.push(tagName);
    +    }
    +
    +    this.stringBuffer_.append('<', tagName);
    +    for (var i = 0, n = attribs.length; i < n; i += 2) {
    +      var attribName = attribs[i],
    +          value = attribs[i + 1];
    +      if (value !== null && value !== void 0) {
    +        this.stringBuffer_.append(' ', attribName, '="',
    +            this.escapeAttrib_(value), '"');
    +      }
    +    }
    +    this.stringBuffer_.append('>');
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.endTag = function(tagName) {
    +  if (this.ignoring_) {
    +    this.ignoring_ = false;
    +    return;
    +  }
    +  if (!goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName)) {
    +    return;
    +  }
    +  var eflags = goog.string.html.HtmlParser.Elements[tagName];
    +  if (!(eflags & (goog.string.html.HtmlParser.EFlags.UNSAFE |
    +      goog.string.html.HtmlParser.EFlags.EMPTY |
    +      goog.string.html.HtmlParser.EFlags.FOLDABLE))) {
    +    var index;
    +    if (eflags & goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG) {
    +      for (index = this.stack_.length; --index >= 0;) {
    +        var stackEl = this.stack_[index];
    +        if (stackEl === tagName) {
    +          break;
    +        }
    +        if (!(goog.string.html.HtmlParser.Elements[stackEl] &
    +            goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG)) {
    +          // Don't pop non optional end tags looking for a match.
    +          return;
    +        }
    +      }
    +    } else {
    +      for (index = this.stack_.length; --index >= 0;) {
    +        if (this.stack_[index] === tagName) {
    +          break;
    +        }
    +      }
    +    }
    +    if (index < 0) { return; }  // Not opened.
    +    for (var i = this.stack_.length; --i > index;) {
    +      var stackEl = this.stack_[i];
    +      if (!(goog.string.html.HtmlParser.Elements[stackEl] &
    +          goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG)) {
    +        this.stringBuffer_.append('</', stackEl, '>');
    +      }
    +    }
    +    this.stack_.length = index;
    +    this.stringBuffer_.append('</', tagName, '>');
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.pcdata = function(text) {
    +  if (!this.ignoring_) {
    +    this.stringBuffer_.append(text);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.rcdata = function(text) {
    +  if (!this.ignoring_) {
    +    this.stringBuffer_.append(text);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.cdata = function(text) {
    +  if (!this.ignoring_) {
    +    this.stringBuffer_.append(text);
    +  }
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.startDoc = function() {
    +  this.stack_ = [];
    +  this.ignoring_ = false;
    +};
    +
    +
    +/**
    + * @override
    + */
    +goog.string.html.HtmlSanitizer.prototype.endDoc = function() {
    +  for (var i = this.stack_.length; --i >= 0;) {
    +    this.stringBuffer_.append('</', this.stack_[i], '>');
    +  }
    +  this.stack_.length = 0;
    +};
    +
    +
    +/**
    + * Escapes HTML special characters in attribute values as HTML entities.
    + *
    + * TODO(user): use {@code goog.string.htmlEscape} instead ?
    + * @param {string} s The string to be escaped.
    + * @return {string} An escaped version of {@code s}.
    + * @private
    + */
    +goog.string.html.HtmlSanitizer.prototype.escapeAttrib_ = function(s) {
    +  // Escaping '=' defangs many UTF-7 and SGML short-tag attacks.
    +  return s.replace(goog.string.html.HtmlParser.AMP_RE_, '&amp;').
    +      replace(goog.string.html.HtmlParser.LT_RE_, '&lt;').
    +      replace(goog.string.html.HtmlParser.GT_RE_, '&gt;').
    +      replace(goog.string.html.HtmlParser.QUOTE_RE_, '&#34;').
    +      replace(goog.string.html.HtmlParser.EQUALS_RE_, '&#61;');
    +};
    +
    +
    +/**
    + * Sanitizes attributes found on html entities.
    + * @param {string} tagName The name of the tag in which the {@code attribs} were
    + *     found.
    + * @param {Array.<?string>} attribs An array of attributes.
    + * @return {Array.<?string>} A sanitized version of the {@code attribs}.
    + * @private
    + */
    +goog.string.html.HtmlSanitizer.prototype.sanitizeAttributes_ =
    +    function(tagName, attribs) {
    +  for (var i = 0; i < attribs.length; i += 2) {
    +    var attribName = attribs[i];
    +    var value = attribs[i + 1];
    +    var atype = null, attribKey;
    +    if ((attribKey = tagName + '::' + attribName,
    +        goog.string.html.HtmlSanitizer.Attributes.hasOwnProperty(attribKey)) ||
    +        (attribKey = '*::' + attribName,
    +        goog.string.html.HtmlSanitizer.Attributes.hasOwnProperty(attribKey))) {
    +      atype = goog.string.html.HtmlSanitizer.Attributes[attribKey];
    +    }
    +    if (atype !== null) {
    +      switch (atype) {
    +        case 0: break;
    +        case goog.string.html.HtmlSanitizer.AttributeType.SCRIPT:
    +        case goog.string.html.HtmlSanitizer.AttributeType.STYLE:
    +          value = null;
    +          break;
    +        case goog.string.html.HtmlSanitizer.AttributeType.ID:
    +        case goog.string.html.HtmlSanitizer.AttributeType.IDREF:
    +        case goog.string.html.HtmlSanitizer.AttributeType.IDREFS:
    +        case goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME:
    +        case goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME:
    +        case goog.string.html.HtmlSanitizer.AttributeType.CLASSES:
    +          value = this.nmTokenPolicy_ ?
    +            this.nmTokenPolicy_(/** @type {string} */ (value)) : value;
    +          break;
    +        case goog.string.html.HtmlSanitizer.AttributeType.URI:
    +          value = this.urlPolicy_ && this.urlPolicy_(
    +              /** @type {string} */ (value));
    +          break;
    +        case goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT:
    +          if (value && '#' === value.charAt(0)) {
    +            value = this.nmTokenPolicy_ ? this.nmTokenPolicy_(value) : value;
    +            if (value) { value = '#' + value; }
    +          } else {
    +            value = null;
    +          }
    +          break;
    +        default:
    +          value = null;
    +          break;
    +      }
    +    } else {
    +      value = null;
    +    }
    +    attribs[i + 1] = value;
    +  }
    +  return attribs;
    +};
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/deps.js b/mapbox-gl/closure-library/third_party/closure/goog/deps.js
    new file mode 100644
    index 0000000000..fa140dbec4
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/deps.js
    @@ -0,0 +1,20 @@
    +// Copyright 2010 The Closure Library Authors. All Rights Reserved.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS-IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @deprecated This file is deprecated. The contents have been
    + * migrated to the main deps.js instead (which is auto-included by
    + * base.js).  Please do not add new dependencies here.
    + */
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query.js b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query.js
    new file mode 100644
    index 0000000000..c4bbbb7ec7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query.js
    @@ -0,0 +1,1545 @@
    +// Copyright 2005-2009, The Dojo Foundation
    +// Modifications Copyright 2008 The Closure Library Authors.
    +// All Rights Reserved.
    +
    +/**
    + * @license Portions of this code are from the Dojo Toolkit, received by
    + * The Closure Library Authors under the BSD license. All other code is
    + * Copyright 2005-2009 The Closure Library Authors. All Rights Reserved.
    +
    +The "New" BSD License:
    +
    +Copyright (c) 2005-2009, The Dojo Foundation
    +All rights reserved.
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions are met:
    +
    +  * Redistributions of source code must retain the above copyright notice, this
    +    list of conditions and the following disclaimer.
    +  * Redistributions in binary form must reproduce the above copyright notice,
    +    this list of conditions and the following disclaimer in the documentation
    +    and/or other materials provided with the distribution.
    +  * Neither the name of the Dojo Foundation nor the names of its contributors
    +    may be used to endorse or promote products derived from this software
    +    without specific prior written permission.
    +
    +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    +DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +*/
    +
    +/**
    + * @fileoverview This code was ported from the Dojo Toolkit
    +   http://dojotoolkit.org and modified slightly for Closure.
    + *
    + *  goog.dom.query is a relatively full-featured CSS3 query function. It is
    + *  designed to take any valid CSS3 selector and return the nodes matching
    + *  the selector. To do this quickly, it processes queries in several
    + *  steps, applying caching where profitable.
    + *    The steps (roughly in reverse order of the way they appear in the code):
    + *    1.) check to see if we already have a "query dispatcher"
    + *      - if so, use that with the given parameterization. Skip to step 4.
    + *    2.) attempt to determine which branch to dispatch the query to:
    + *      - JS (optimized DOM iteration)
    + *      - native (FF3.1, Safari 3.2+, Chrome, some IE 8 doctypes). If native,
    + *        skip to step 4, using a stub dispatcher for QSA queries.
    + *    3.) tokenize and convert to executable "query dispatcher"
    + *        assembled as a chain of "yes/no" test functions pertaining to
    + *        a section of a simple query statement (".blah:nth-child(odd)"
    + *        but not "div div", which is 2 simple statements).
    + *    4.) the resulting query dispatcher is called in the passed scope
    + *        (by default the top-level document)
    + *      - for DOM queries, this results in a recursive, top-down
    + *        evaluation of nodes based on each simple query section
    + *      - querySelectorAll is used instead of DOM where possible. If a query
    + *        fails in this mode, it is re-run against the DOM evaluator and all
    + *        future queries using the same selector evaluate against the DOM branch
    + *        too.
    + *    5.) matched nodes are pruned to ensure they are unique
    + * @deprecated This is an all-software query selector. When developing for
    + *     recent browsers, use document.querySelector. See information at
    + *     http://caniuse.com/queryselector and
    + *     https://developer.mozilla.org/en-US/docs/DOM/Document.querySelector .
    + */
    +
    +goog.provide('goog.dom.query');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.functions');
    +goog.require('goog.string');
    +goog.require('goog.userAgent');
    +
    +  /**
    +   * Returns nodes which match the given CSS3 selector, searching the
    +   * entire document by default but optionally taking a node to scope
    +   * the search by.
    +   *
    +   * dojo.query() is the swiss army knife of DOM node manipulation in
    +   * Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
    +   * "$" function, dojo.query provides robust, high-performance
    +   * CSS-based node selector support with the option of scoping searches
    +   * to a particular sub-tree of a document.
    +   *
    +   * Supported Selectors:
    +   * --------------------
    +   *
    +   * dojo.query() supports a rich set of CSS3 selectors, including:
    +   *
    +   *   * class selectors (e.g., `.foo`)
    +   *   * node type selectors like `span`
    +   *   * ` ` descendant selectors
    +   *   * `>` child element selectors
    +   *   * `#foo` style ID selectors
    +   *   * `*` universal selector
    +   *   * `~`, the immediately preceded-by sibling selector
    +   *   * `+`, the preceded-by sibling selector
    +   *   * attribute queries:
    +   *   |  * `[foo]` attribute presence selector
    +   *   |  * `[foo='bar']` attribute value exact match
    +   *   |  * `[foo~='bar']` attribute value list item match
    +   *   |  * `[foo^='bar']` attribute start match
    +   *   |  * `[foo$='bar']` attribute end match
    +   *   |  * `[foo*='bar']` attribute substring match
    +   *   * `:first-child`, `:last-child` positional selectors
    +   *   * `:empty` content empty selector
    +   *   * `:empty` content empty selector
    +   *   * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
    +   *   * `:nth-child(even)`, `:nth-child(odd)` positional selectors
    +   *   * `:not(...)` negation pseudo selectors
    +   *
    +   * Any legal combination of these selectors will work with
    +   * `dojo.query()`, including compound selectors ("," delimited).
    +   * Very complex and useful searches can be constructed with this
    +   * palette of selectors.
    +   *
    +   * Unsupported Selectors:
    +   * ----------------------
    +   *
    +   * While dojo.query handles many CSS3 selectors, some fall outside of
    +   * what's reasonable for a programmatic node querying engine to
    +   * handle. Currently unsupported selectors include:
    +   *
    +   *   * namespace-differentiated selectors of any form
    +   *   * all `::` pseudo-element selectors
    +   *   * certain pseudo-selectors which don't get a lot of day-to-day use:
    +   *   |  * `:root`, `:lang()`, `:target`, `:focus`
    +   *   * all visual and state selectors:
    +   *   |  * `:root`, `:active`, `:hover`, `:visited`, `:link`,
    +   *       `:enabled`, `:disabled`, `:checked`
    +   *   * `:*-of-type` pseudo selectors
    +   *
    +   * dojo.query and XML Documents:
    +   * -----------------------------
    +   *
    +   * `dojo.query` currently only supports searching XML documents
    +   * whose tags and attributes are 100% lower-case. This is a known
    +   * limitation and will [be addressed soon]
    +   * (http://trac.dojotoolkit.org/ticket/3866)
    +   *
    +   * Non-selector Queries:
    +   * ---------------------
    +   *
    +   * If something other than a String is passed for the query,
    +   * `dojo.query` will return a new array constructed from
    +   * that parameter alone and all further processing will stop. This
    +   * means that if you have a reference to a node or array or nodes, you
    +   * can quickly construct a new array of nodes from the original by
    +   * calling `dojo.query(node)` or `dojo.query(array)`.
    +   *
    +   * example:
    +   *   search the entire document for elements with the class "foo":
    +   * |  dojo.query(".foo");
    +   *   these elements will match:
    +   * |  <span class="foo"></span>
    +   * |  <span class="foo bar"></span>
    +   * |  <p class="thud foo"></p>
    +   * example:
    +   *   search the entire document for elements with the classes "foo" *and*
    +   *   "bar":
    +   * |  dojo.query(".foo.bar");
    +   *   these elements will match:
    +   * |  <span class="foo bar"></span>
    +   *   while these will not:
    +   * |  <span class="foo"></span>
    +   * |  <p class="thud foo"></p>
    +   * example:
    +   *   find `<span>` elements which are descendants of paragraphs and
    +   *   which have a "highlighted" class:
    +   * |  dojo.query("p span.highlighted");
    +   *   the innermost span in this fragment matches:
    +   * |  <p class="foo">
    +   * |    <span>...
    +   * |      <span class="highlighted foo bar">...</span>
    +   * |    </span>
    +   * |  </p>
    +   * example:
    +   *   find all odd table rows inside of the table
    +   *   `#tabular_data`, using the `>` (direct child) selector to avoid
    +   *   affecting any nested tables:
    +   * |  dojo.query("#tabular_data > tbody > tr:nth-child(odd)");
    +   *
    +   * @param {string|Array} query The CSS3 expression to match against.
    +   *     For details on the syntax of CSS3 selectors, see
    +   *     http://www.w3.org/TR/css3-selectors/#selectors.
    +   * @param {(string|Node)=} opt_root A Node (or node id) to scope the search
    +   *     from (optional).
    +   * @return { {length: number} } The elements that matched the query.
    +   *
    +   * @deprecated This is an all-software query selector. Use
    +   *     document.querySelector. See
    +   *     https://developer.mozilla.org/en-US/docs/DOM/Document.querySelector .
    +   */
    +goog.dom.query = (function() {
    +  ////////////////////////////////////////////////////////////////////////
    +  // Global utilities
    +  ////////////////////////////////////////////////////////////////////////
    +
    +  var cssCaseBug = (goog.userAgent.WEBKIT &&
    +                     ((goog.dom.getDocument().compatMode) == 'BackCompat')
    +                   );
    +
    +  var legacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
    +
    +  // On browsers that support the "children" collection we can avoid a lot of
    +  // iteration on chaff (non-element) nodes.
    +  var childNodesName = !!goog.dom.getDocument().firstChild['children'] ?
    +                          'children' :
    +                          'childNodes';
    +
    +  var specials = '>~+';
    +
    +  // Global thunk to determine whether we should treat the current query as
    +  // case sensitive or not. This switch is flipped by the query evaluator based
    +  // on the document passed as the context to search.
    +  var caseSensitive = false;
    +
    +
    +  ////////////////////////////////////////////////////////////////////////
    +  // Tokenizer
    +  ////////////////////////////////////////////////////////////////////////
    +
    +  var getQueryParts = function(query) {
    +    //  summary:
    +    //    state machine for query tokenization
    +    //  description:
    +    //    instead of using a brittle and slow regex-based CSS parser,
    +    //    dojo.query implements an AST-style query representation. This
    +    //    representation is only generated once per query. For example,
    +    //    the same query run multiple times or under different root nodes
    +    //    does not re-parse the selector expression but instead uses the
    +    //    cached data structure. The state machine implemented here
    +    //    terminates on the last " " (space) character and returns an
    +    //    ordered array of query component structures (or "parts"). Each
    +    //    part represents an operator or a simple CSS filtering
    +    //    expression. The structure for parts is documented in the code
    +    //    below.
    +
    +
    +    // NOTE:
    +    //    this code is designed to run fast and compress well. Sacrifices
    +    //    to readability and maintainability have been made.
    +    if (specials.indexOf(query.slice(-1)) >= 0) {
    +      // If we end with a ">", "+", or "~", that means we're implicitly
    +      // searching all children, so make it explicit.
    +      query += ' * '
    +    } else {
    +      // if you have not provided a terminator, one will be provided for
    +      // you...
    +      query += ' ';
    +    }
    +
    +    var ts = function(/*Integer*/ s, /*Integer*/ e) {
    +      // trim and slice.
    +
    +      // take an index to start a string slice from and an end position
    +      // and return a trimmed copy of that sub-string
    +      return goog.string.trim(query.slice(s, e));
    +    };
    +
    +    // The overall data graph of the full query, as represented by queryPart
    +    // objects.
    +    var queryParts = [];
    +
    +
    +    // state keeping vars
    +    var inBrackets = -1,
    +        inParens = -1,
    +        inMatchFor = -1,
    +        inPseudo = -1,
    +        inClass = -1,
    +        inId = -1,
    +        inTag = -1,
    +        lc = '',
    +        cc = '',
    +        pStart;
    +
    +    // iteration vars
    +    var x = 0, // index in the query
    +        ql = query.length,
    +        currentPart = null, // data structure representing the entire clause
    +        cp = null; // the current pseudo or attr matcher
    +
    +    // several temporary variables are assigned to this structure during a
    +    // potential sub-expression match:
    +    //    attr:
    +    //      a string representing the current full attribute match in a
    +    //      bracket expression
    +    //    type:
    +    //      if there's an operator in a bracket expression, this is
    +    //      used to keep track of it
    +    //    value:
    +    //      the internals of parenthetical expression for a pseudo. for
    +    //      :nth-child(2n+1), value might be '2n+1'
    +
    +    var endTag = function() {
    +      // called when the tokenizer hits the end of a particular tag name.
    +      // Re-sets state variables for tag matching and sets up the matcher
    +      // to handle the next type of token (tag or operator).
    +      if (inTag >= 0) {
    +        var tv = (inTag == x) ? null : ts(inTag, x);
    +        if (specials.indexOf(tv) < 0) {
    +          currentPart.tag = tv;
    +        } else {
    +          currentPart.oper = tv;
    +        }
    +        inTag = -1;
    +      }
    +    };
    +
    +    var endId = function() {
    +      // Called when the tokenizer might be at the end of an ID portion of a
    +      // match.
    +      if (inId >= 0) {
    +        currentPart.id = ts(inId, x).replace(/\\/g, '');
    +        inId = -1;
    +      }
    +    };
    +
    +    var endClass = function() {
    +      // Called when the tokenizer might be at the end of a class name
    +      // match. CSS allows for multiple classes, so we augment the
    +      // current item with another class in its list.
    +      if (inClass >= 0) {
    +        currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ''));
    +        inClass = -1;
    +      }
    +    };
    +
    +    var endAll = function() {
    +      // at the end of a simple fragment, so wall off the matches
    +      endId(); endTag(); endClass();
    +    };
    +
    +    var endPart = function() {
    +      endAll();
    +      if (inPseudo >= 0) {
    +        currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
    +      }
    +      // Hint to the selector engine to tell it whether or not it
    +      // needs to do any iteration. Many simple selectors don't, and
    +      // we can avoid significant construction-time work by advising
    +      // the system to skip them.
    +      currentPart.loops = currentPart.pseudos.length ||
    +                          currentPart.attrs.length ||
    +                          currentPart.classes.length;
    +
    +      // save the full expression as a string
    +      currentPart.oquery = currentPart.query = ts(pStart, x);
    +
    +
    +      // otag/tag are hints to suggest to the system whether or not
    +      // it's an operator or a tag. We save a copy of otag since the
    +      // tag name is cast to upper-case in regular HTML matches. The
    +      // system has a global switch to figure out if the current
    +      // expression needs to be case sensitive or not and it will use
    +      // otag or tag accordingly
    +      currentPart.otag = currentPart.tag = (currentPart.oper) ?
    +                                                     null :
    +                                                     (currentPart.tag || '*');
    +
    +      if (currentPart.tag) {
    +        // if we're in a case-insensitive HTML doc, we likely want
    +        // the toUpperCase when matching on element.tagName. If we
    +        // do it here, we can skip the string op per node
    +        // comparison
    +        currentPart.tag = currentPart.tag.toUpperCase();
    +      }
    +
    +      // add the part to the list
    +      if (queryParts.length && (queryParts[queryParts.length - 1].oper)) {
    +        // operators are always infix, so we remove them from the
    +        // list and attach them to the next match. The evaluator is
    +        // responsible for sorting out how to handle them.
    +        currentPart.infixOper = queryParts.pop();
    +        currentPart.query = currentPart.infixOper.query + ' ' +
    +            currentPart.query;
    +      }
    +      queryParts.push(currentPart);
    +
    +      currentPart = null;
    +    }
    +
    +    // iterate over the query, character by character, building up a
    +    // list of query part objects
    +    for (; lc = cc, cc = query.charAt(x), x < ql; x++) {
    +      //    cc: the current character in the match
    +      //    lc: the last character (if any)
    +
    +      // someone is trying to escape something, so don't try to match any
    +      // fragments. We assume we're inside a literal.
    +      if (lc == '\\') {
    +        continue;
    +      }
    +      if (!currentPart) { // a part was just ended or none has yet been created
    +        // NOTE: I hate all this alloc, but it's shorter than writing tons of
    +        // if's
    +        pStart = x;
    +        //  rules describe full CSS sub-expressions, like:
    +        //    #someId
    +        //    .className:first-child
    +        //  but not:
    +        //    thinger > div.howdy[type=thinger]
    +        //  the individual components of the previous query would be
    +        //  split into 3 parts that would be represented a structure
    +        //  like:
    +        //    [
    +        //      {
    +        //        query: 'thinger',
    +        //        tag: 'thinger',
    +        //      },
    +        //      {
    +        //        query: 'div.howdy[type=thinger]',
    +        //        classes: ['howdy'],
    +        //        infixOper: {
    +        //          query: '>',
    +        //          oper: '>',
    +        //        }
    +        //      },
    +        //    ]
    +        currentPart = {
    +          query: null, // the full text of the part's rule
    +          pseudos: [], // CSS supports multiple pseudo-class matches in a single
    +              // rule
    +          attrs: [],  // CSS supports multi-attribute match, so we need an array
    +          classes: [], // class matches may be additive,
    +              // e.g.: .thinger.blah.howdy
    +          tag: null,  // only one tag...
    +          oper: null, // ...or operator per component. Note that these wind up
    +              // being exclusive.
    +          id: null,   // the id component of a rule
    +          getTag: function() {
    +            return (caseSensitive) ? this.otag : this.tag;
    +          }
    +        };
    +
    +        // if we don't have a part, we assume we're going to start at
    +        // the beginning of a match, which should be a tag name. This
    +        // might fault a little later on, but we detect that and this
    +        // iteration will still be fine.
    +        inTag = x;
    +      }
    +
    +      if (inBrackets >= 0) {
    +        // look for a the close first
    +        if (cc == ']') { // if we're in a [...] clause and we end, do assignment
    +          if (!cp.attr) {
    +            // no attribute match was previously begun, so we
    +            // assume this is an attribute existence match in the
    +            // form of [someAttributeName]
    +            cp.attr = ts(inBrackets + 1, x);
    +          } else {
    +            // we had an attribute already, so we know that we're
    +            // matching some sort of value, as in [attrName=howdy]
    +            cp.matchFor = ts((inMatchFor || inBrackets + 1), x);
    +          }
    +          var cmf = cp.matchFor;
    +          if (cmf) {
    +            // try to strip quotes from the matchFor value. We want
    +            // [attrName=howdy] to match the same
    +            //  as [attrName = 'howdy' ]
    +            if ((cmf.charAt(0) == '"') || (cmf.charAt(0) == "'")) {
    +              cp.matchFor = cmf.slice(1, -1);
    +            }
    +          }
    +          // end the attribute by adding it to the list of attributes.
    +          currentPart.attrs.push(cp);
    +          cp = null; // necessary?
    +          inBrackets = inMatchFor = -1;
    +        } else if (cc == '=') {
    +          // if the last char was an operator prefix, make sure we
    +          // record it along with the '=' operator.
    +          var addToCc = ('|~^$*'.indexOf(lc) >= 0) ? lc : '';
    +          cp.type = addToCc + cc;
    +          cp.attr = ts(inBrackets + 1, x - addToCc.length);
    +          inMatchFor = x + 1;
    +        }
    +        // now look for other clause parts
    +      } else if (inParens >= 0) {
    +        // if we're in a parenthetical expression, we need to figure
    +        // out if it's attached to a pseudo-selector rule like
    +        // :nth-child(1)
    +        if (cc == ')') {
    +          if (inPseudo >= 0) {
    +            cp.value = ts(inParens + 1, x);
    +          }
    +          inPseudo = inParens = -1;
    +        }
    +      } else if (cc == '#') {
    +        // start of an ID match
    +        endAll();
    +        inId = x + 1;
    +      } else if (cc == '.') {
    +        // start of a class match
    +        endAll();
    +        inClass = x;
    +      } else if (cc == ':') {
    +        // start of a pseudo-selector match
    +        endAll();
    +        inPseudo = x;
    +      } else if (cc == '[') {
    +        // start of an attribute match.
    +        endAll();
    +        inBrackets = x;
    +        // provide a new structure for the attribute match to fill-in
    +        cp = {
    +          /*=====
    +          attr: null, type: null, matchFor: null
    +          =====*/
    +        };
    +      } else if (cc == '(') {
    +        // we really only care if we've entered a parenthetical
    +        // expression if we're already inside a pseudo-selector match
    +        if (inPseudo >= 0) {
    +          // provide a new structure for the pseudo match to fill-in
    +          cp = {
    +            name: ts(inPseudo + 1, x),
    +            value: null
    +          }
    +          currentPart.pseudos.push(cp);
    +        }
    +        inParens = x;
    +      } else if (
    +        (cc == ' ') &&
    +        // if it's a space char and the last char is too, consume the
    +        // current one without doing more work
    +        (lc != cc)
    +      ) {
    +        endPart();
    +      }
    +    }
    +    return queryParts;
    +  };
    +
    +
    +  ////////////////////////////////////////////////////////////////////////
    +  // DOM query infrastructure
    +  ////////////////////////////////////////////////////////////////////////
    +
    +  var agree = function(first, second) {
    +    // the basic building block of the yes/no chaining system. agree(f1,
    +    // f2) generates a new function which returns the boolean results of
    +    // both of the passed functions to a single logical-anded result. If
    +    // either are not passed, the other is used exclusively.
    +    if (!first) {
    +      return second;
    +    }
    +    if (!second) {
    +      return first;
    +    }
    +
    +    return function() {
    +      return first.apply(window, arguments) && second.apply(window, arguments);
    +    }
    +  };
    +
    +  /**
    +   * @param {Array=} opt_arr
    +   */
    +  function getArr(i, opt_arr) {
    +    // helps us avoid array alloc when we don't need it
    +    var r = opt_arr || [];
    +    if (i) {
    +      r.push(i);
    +    }
    +    return r;
    +  };
    +
    +  var isElement = function(n) {
    +    return (1 == n.nodeType);
    +  };
    +
    +  // FIXME: need to coalesce getAttr with defaultGetter
    +  var blank = '';
    +  var getAttr = function(elem, attr) {
    +    if (!elem) {
    +      return blank;
    +    }
    +    if (attr == 'class') {
    +      return elem.className || blank;
    +    }
    +    if (attr == 'for') {
    +      return elem.htmlFor || blank;
    +    }
    +    if (attr == 'style') {
    +      return elem.style.cssText || blank;
    +    }
    +    return (caseSensitive ? elem.getAttribute(attr) :
    +        elem.getAttribute(attr, 2)) || blank;
    +  };
    +
    +  var attrs = {
    +    '*=': function(attr, value) {
    +      return function(elem) {
    +        // E[foo*="bar"]
    +        //    an E element whose "foo" attribute value contains
    +        //    the substring "bar"
    +        return (getAttr(elem, attr).indexOf(value) >= 0);
    +      }
    +    },
    +    '^=': function(attr, value) {
    +      // E[foo^="bar"]
    +      //    an E element whose "foo" attribute value begins exactly
    +      //    with the string "bar"
    +      return function(elem) {
    +        return (getAttr(elem, attr).indexOf(value) == 0);
    +      }
    +    },
    +    '$=': function(attr, value) {
    +      // E[foo$="bar"]
    +      //    an E element whose "foo" attribute value ends exactly
    +      //    with the string "bar"
    +      var tval = ' ' + value;
    +      return function(elem) {
    +        var ea = ' ' + getAttr(elem, attr);
    +        return (ea.lastIndexOf(value) == (ea.length - value.length));
    +      }
    +    },
    +    '~=': function(attr, value) {
    +      // E[foo~="bar"]
    +      //    an E element whose "foo" attribute value is a list of
    +      //    space-separated values, one of which is exactly equal
    +      //    to "bar"
    +
    +      var tval = ' ' + value + ' ';
    +      return function(elem) {
    +        var ea = ' ' + getAttr(elem, attr) + ' ';
    +        return (ea.indexOf(tval) >= 0);
    +      }
    +    },
    +    '|=': function(attr, value) {
    +      // E[hreflang|="en"]
    +      //    an E element whose "hreflang" attribute has a
    +      //    hyphen-separated list of values beginning (from the
    +      //    left) with "en"
    +      value = ' ' + value;
    +      return function(elem) {
    +        var ea = ' ' + getAttr(elem, attr);
    +        return (
    +          (ea == value) ||
    +          (ea.indexOf(value + '-') == 0)
    +        );
    +      }
    +    },
    +    '=': function(attr, value) {
    +      return function(elem) {
    +        return (getAttr(elem, attr) == value);
    +      }
    +    }
    +  };
    +
    +  // avoid testing for node type if we can. Defining this in the negative
    +  // here to avoid negation in the fast path.
    +  var noNextElementSibling = (
    +    typeof goog.dom.getDocument().firstChild.nextElementSibling == 'undefined'
    +  );
    +  var nSibling = !noNextElementSibling ? 'nextElementSibling' : 'nextSibling';
    +  var pSibling = !noNextElementSibling ?
    +                    'previousElementSibling' :
    +                    'previousSibling';
    +  var simpleNodeTest = (noNextElementSibling ? isElement : goog.functions.TRUE);
    +
    +  var _lookLeft = function(node) {
    +    while (node = node[pSibling]) {
    +      if (simpleNodeTest(node)) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  };
    +
    +  var _lookRight = function(node) {
    +    while (node = node[nSibling]) {
    +      if (simpleNodeTest(node)) {
    +        return false;
    +      }
    +    }
    +    return true;
    +  };
    +
    +  var getNodeIndex = function(node) {
    +    var root = node.parentNode;
    +    var i = 0,
    +        tret = root[childNodesName],
    +        ci = (node['_i'] || -1),
    +        cl = (root['_l'] || -1);
    +
    +    if (!tret) {
    +      return -1;
    +    }
    +    var l = tret.length;
    +
    +    // we calculate the parent length as a cheap way to invalidate the
    +    // cache. It's not 100% accurate, but it's much more honest than what
    +    // other libraries do
    +    if (cl == l && ci >= 0 && cl >= 0) {
    +      // if it's legit, tag and release
    +      return ci;
    +    }
    +
    +    // else re-key things
    +    root['_l'] = l;
    +    ci = -1;
    +    var te = root['firstElementChild'] || root['firstChild'];
    +    for (; te; te = te[nSibling]) {
    +      if (simpleNodeTest(te)) {
    +        te['_i'] = ++i;
    +        if (node === te) {
    +          // NOTE:
    +          //  shortcutting the return at this step in indexing works
    +          //  very well for benchmarking but we avoid it here since
    +          //  it leads to potential O(n^2) behavior in sequential
    +          //  getNodexIndex operations on a previously un-indexed
    +          //  parent. We may revisit this at a later time, but for
    +          //  now we just want to get the right answer more often
    +          //  than not.
    +          ci = i;
    +        }
    +      }
    +    }
    +    return ci;
    +  };
    +
    +  var isEven = function(elem) {
    +    return !((getNodeIndex(elem)) % 2);
    +  };
    +
    +  var isOdd = function(elem) {
    +    return (getNodeIndex(elem)) % 2;
    +  };
    +
    +  var pseudos = {
    +    'checked': function(name, condition) {
    +      return function(elem) {
    +        return elem.checked || elem.attributes['checked'];
    +      }
    +    },
    +    'first-child': function() {
    +      return _lookLeft;
    +    },
    +    'last-child': function() {
    +      return _lookRight;
    +    },
    +    'only-child': function(name, condition) {
    +      return function(node) {
    +        if (!_lookLeft(node)) {
    +          return false;
    +        }
    +        if (!_lookRight(node)) {
    +          return false;
    +        }
    +        return true;
    +      };
    +    },
    +    'empty': function(name, condition) {
    +      return function(elem) {
    +        // DomQuery and jQuery get this wrong, oddly enough.
    +        // The CSS 3 selectors spec is pretty explicit about it, too.
    +        var cn = elem.childNodes;
    +        var cnl = elem.childNodes.length;
    +        // if(!cnl) { return true; }
    +        for (var x = cnl - 1; x >= 0; x--) {
    +          var nt = cn[x].nodeType;
    +          if ((nt === 1) || (nt == 3)) {
    +            return false;
    +          }
    +        }
    +        return true;
    +      }
    +    },
    +    'contains': function(name, condition) {
    +      var cz = condition.charAt(0);
    +      if (cz == '"' || cz == "'") { // Remove quotes.
    +        condition = condition.slice(1, -1);
    +      }
    +      return function(elem) {
    +        return (elem.innerHTML.indexOf(condition) >= 0);
    +      }
    +    },
    +    'not': function(name, condition) {
    +      var p = getQueryParts(condition)[0];
    +      var ignores = { el: 1 };
    +      if (p.tag != '*') {
    +        ignores.tag = 1;
    +      }
    +      if (!p.classes.length) {
    +        ignores.classes = 1;
    +      }
    +      var ntf = getSimpleFilterFunc(p, ignores);
    +      return function(elem) {
    +        return !ntf(elem);
    +      }
    +    },
    +    'nth-child': function(name, condition) {
    +      function pi(n) {
    +        return parseInt(n, 10);
    +      }
    +      // avoid re-defining function objects if we can
    +      if (condition == 'odd') {
    +        return isOdd;
    +      } else if (condition == 'even') {
    +        return isEven;
    +      }
    +      // FIXME: can we shorten this?
    +      if (condition.indexOf('n') != -1) {
    +        var tparts = condition.split('n', 2);
    +        var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
    +        var idx = tparts[1] ? pi(tparts[1]) : 0;
    +        var lb = 0, ub = -1;
    +        if (pred > 0) {
    +          if (idx < 0) {
    +            idx = (idx % pred) && (pred + (idx % pred));
    +          } else if (idx > 0) {
    +            if (idx >= pred) {
    +              lb = idx - idx % pred;
    +            }
    +            idx = idx % pred;
    +          }
    +        } else if (pred < 0) {
    +          pred *= -1;
    +          // idx has to be greater than 0 when pred is negative;
    +          // shall we throw an error here?
    +          if (idx > 0) {
    +            ub = idx;
    +            idx = idx % pred;
    +          }
    +        }
    +        if (pred > 0) {
    +          return function(elem) {
    +            var i = getNodeIndex(elem);
    +            return (i >= lb) && (ub < 0 || i <= ub) && ((i % pred) == idx);
    +          }
    +        } else {
    +          condition = idx;
    +        }
    +      }
    +      var ncount = pi(condition);
    +      return function(elem) {
    +        return (getNodeIndex(elem) == ncount);
    +      }
    +    }
    +  };
    +
    +  var defaultGetter = (legacyIE) ? function(cond) {
    +    var clc = cond.toLowerCase();
    +    if (clc == 'class') {
    +      cond = 'className';
    +    }
    +    return function(elem) {
    +      return caseSensitive ? elem.getAttribute(cond) : elem[cond] || elem[clc];
    +    }
    +  } : function(cond) {
    +    return function(elem) {
    +      return elem && elem.getAttribute && elem.hasAttribute(cond);
    +    }
    +  };
    +
    +  var getSimpleFilterFunc = function(query, ignores) {
    +    // Generates a node tester function based on the passed query part. The
    +    // query part is one of the structures generated by the query parser when it
    +    // creates the query AST. The 'ignores' object specifies which (if any)
    +    // tests to skip, allowing the system to avoid duplicating work where it
    +    // may have already been taken into account by other factors such as how
    +    // the nodes to test were fetched in the first place.
    +    if (!query) {
    +      return goog.functions.TRUE;
    +    }
    +    ignores = ignores || {};
    +
    +    var ff = null;
    +
    +    if (!ignores.el) {
    +      ff = agree(ff, isElement);
    +    }
    +
    +    if (!ignores.tag) {
    +      if (query.tag != '*') {
    +        ff = agree(ff, function(elem) {
    +          return (elem && (elem.tagName == query.getTag()));
    +        });
    +      }
    +    }
    +
    +    if (!ignores.classes) {
    +      goog.array.forEach(query.classes, function(cname, idx, arr) {
    +        // Get the class name.
    +        var re = new RegExp('(?:^|\\s)' + cname + '(?:\\s|$)');
    +        ff = agree(ff, function(elem) {
    +          return re.test(elem.className);
    +        });
    +        ff.count = idx;
    +      });
    +    }
    +
    +    if (!ignores.pseudos) {
    +      goog.array.forEach(query.pseudos, function(pseudo) {
    +        var pn = pseudo.name;
    +        if (pseudos[pn]) {
    +          ff = agree(ff, pseudos[pn](pn, pseudo.value));
    +        }
    +      });
    +    }
    +
    +    if (!ignores.attrs) {
    +      goog.array.forEach(query.attrs, function(attr) {
    +        var matcher;
    +        var a = attr.attr;
    +        // type, attr, matchFor
    +        if (attr.type && attrs[attr.type]) {
    +          matcher = attrs[attr.type](a, attr.matchFor);
    +        } else if (a.length) {
    +          matcher = defaultGetter(a);
    +        }
    +        if (matcher) {
    +          ff = agree(ff, matcher);
    +        }
    +      });
    +    }
    +
    +    if (!ignores.id) {
    +      if (query.id) {
    +        ff = agree(ff, function(elem) {
    +          return (!!elem && (elem.id == query.id));
    +        });
    +      }
    +    }
    +
    +    if (!ff) {
    +      if (!('default' in ignores)) {
    +        ff = goog.functions.TRUE;
    +      }
    +    }
    +    return ff;
    +  };
    +
    +  var nextSiblingIterator = function(filterFunc) {
    +    return function(node, ret, bag) {
    +      while (node = node[nSibling]) {
    +        if (noNextElementSibling && (!isElement(node))) {
    +          continue;
    +        }
    +        if (
    +          (!bag || _isUnique(node, bag)) &&
    +          filterFunc(node)
    +        ) {
    +          ret.push(node);
    +        }
    +        break;
    +      }
    +      return ret;
    +    };
    +  };
    +
    +  var nextSiblingsIterator = function(filterFunc) {
    +    return function(root, ret, bag) {
    +      var te = root[nSibling];
    +      while (te) {
    +        if (simpleNodeTest(te)) {
    +          if (bag && !_isUnique(te, bag)) {
    +            break;
    +          }
    +          if (filterFunc(te)) {
    +            ret.push(te);
    +          }
    +        }
    +        te = te[nSibling];
    +      }
    +      return ret;
    +    };
    +  };
    +
    +  // Get an array of child *elements*, skipping text and comment nodes
    +  var _childElements = function(filterFunc) {
    +    filterFunc = filterFunc || goog.functions.TRUE;
    +    return function(root, ret, bag) {
    +      var te, x = 0, tret = root[childNodesName];
    +      while (te = tret[x++]) {
    +        if (
    +          simpleNodeTest(te) &&
    +          (!bag || _isUnique(te, bag)) &&
    +          (filterFunc(te, x))
    +        ) {
    +          ret.push(te);
    +        }
    +      }
    +      return ret;
    +    };
    +  };
    +
    +  // test to see if node is below root
    +  var _isDescendant = function(node, root) {
    +    var pn = node.parentNode;
    +    while (pn) {
    +      if (pn == root) {
    +        break;
    +      }
    +      pn = pn.parentNode;
    +    }
    +    return !!pn;
    +  };
    +
    +  var _getElementsFuncCache = {};
    +
    +  var getElementsFunc = function(query) {
    +    var retFunc = _getElementsFuncCache[query.query];
    +    // If we've got a cached dispatcher, just use that.
    +    if (retFunc) {
    +      return retFunc;
    +    }
    +    // Else, generate a new one.
    +
    +    // NOTE:
    +    //    This function returns a function that searches for nodes and
    +    //    filters them. The search may be specialized by infix operators
    +    //    (">", "~", or "+") else it will default to searching all
    +    //    descendants (the " " selector). Once a group of children is
    +    //    found, a test function is applied to weed out the ones we
    +    //    don't want. Many common cases can be fast-pathed. We spend a
    +    //    lot of cycles to create a dispatcher that doesn't do more work
    +    //    than necessary at any point since, unlike this function, the
    +    //    dispatchers will be called every time. The logic of generating
    +    //    efficient dispatchers looks like this in pseudo code:
    +    //
    +    //    # if it's a purely descendant query (no ">", "+", or "~" modifiers)
    +    //    if infixOperator == " ":
    +    //      if only(id):
    +    //        return def(root):
    +    //          return d.byId(id, root);
    +    //
    +    //      elif id:
    +    //        return def(root):
    +    //          return filter(d.byId(id, root));
    +    //
    +    //      elif cssClass && getElementsByClassName:
    +    //        return def(root):
    +    //          return filter(root.getElementsByClassName(cssClass));
    +    //
    +    //      elif only(tag):
    +    //        return def(root):
    +    //          return root.getElementsByTagName(tagName);
    +    //
    +    //      else:
    +    //        # search by tag name, then filter
    +    //        return def(root):
    +    //          return filter(root.getElementsByTagName(tagName||"*"));
    +    //
    +    //    elif infixOperator == ">":
    +    //      # search direct children
    +    //      return def(root):
    +    //        return filter(root.children);
    +    //
    +    //    elif infixOperator == "+":
    +    //      # search next sibling
    +    //      return def(root):
    +    //        return filter(root.nextElementSibling);
    +    //
    +    //    elif infixOperator == "~":
    +    //      # search rightward siblings
    +    //      return def(root):
    +    //        return filter(nextSiblings(root));
    +
    +    var io = query.infixOper;
    +    var oper = (io ? io.oper : '');
    +    // The default filter func which tests for all conditions in the query
    +    // part. This is potentially inefficient, so some optimized paths may
    +    // re-define it to test fewer things.
    +    var filterFunc = getSimpleFilterFunc(query, { el: 1 });
    +    var qt = query.tag;
    +    var wildcardTag = ('*' == qt);
    +    var ecs = goog.dom.getDocument()['getElementsByClassName'];
    +
    +    if (!oper) {
    +      // If there's no infix operator, then it's a descendant query. ID
    +      // and "elements by class name" variants can be accelerated so we
    +      // call them out explicitly:
    +      if (query.id) {
    +        // Testing shows that the overhead of goog.functions.TRUE() is
    +        // acceptable and can save us some bytes vs. re-defining the function
    +        // everywhere.
    +        filterFunc = (!query.loops && wildcardTag) ?
    +          goog.functions.TRUE :
    +          getSimpleFilterFunc(query, { el: 1, id: 1 });
    +
    +        retFunc = function(root, arr) {
    +          var te = goog.dom.getDomHelper(root).getElement(query.id);
    +          if (!te || !filterFunc(te)) {
    +            return;
    +          }
    +          if (9 == root.nodeType) { // If root's a doc, we just return directly.
    +            return getArr(te, arr);
    +          } else { // otherwise check ancestry
    +            if (_isDescendant(te, root)) {
    +              return getArr(te, arr);
    +            }
    +          }
    +        }
    +      } else if (
    +        ecs &&
    +        // isAlien check. Workaround for Prototype.js being totally evil/dumb.
    +        /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
    +        query.classes.length &&
    +        // WebKit bug where quirks-mode docs select by class w/o case
    +        // sensitivity.
    +        !cssCaseBug
    +      ) {
    +        // it's a class-based query and we've got a fast way to run it.
    +
    +        // ignore class and ID filters since we will have handled both
    +        filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
    +        var classesString = query.classes.join(' ');
    +        retFunc = function(root, arr) {
    +          var ret = getArr(0, arr), te, x = 0;
    +          var tret = root.getElementsByClassName(classesString);
    +          while ((te = tret[x++])) {
    +            if (filterFunc(te, root)) {
    +              ret.push(te);
    +            }
    +          }
    +          return ret;
    +        };
    +
    +      } else if (!wildcardTag && !query.loops) {
    +        // it's tag only. Fast-path it.
    +        retFunc = function(root, arr) {
    +          var ret = getArr(0, arr), te, x = 0;
    +          var tret = root.getElementsByTagName(query.getTag());
    +          while ((te = tret[x++])) {
    +            ret.push(te);
    +          }
    +          return ret;
    +        };
    +      } else {
    +        // the common case:
    +        //    a descendant selector without a fast path. By now it's got
    +        //    to have a tag selector, even if it's just "*" so we query
    +        //    by that and filter
    +        filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
    +        retFunc = function(root, arr) {
    +          var ret = getArr(0, arr), te, x = 0;
    +          // we use getTag() to avoid case sensitivity issues
    +          var tret = root.getElementsByTagName(query.getTag());
    +          while (te = tret[x++]) {
    +            if (filterFunc(te, root)) {
    +              ret.push(te);
    +            }
    +          }
    +          return ret;
    +        };
    +      }
    +    } else {
    +      // the query is scoped in some way. Instead of querying by tag we
    +      // use some other collection to find candidate nodes
    +      var skipFilters = { el: 1 };
    +      if (wildcardTag) {
    +        skipFilters.tag = 1;
    +      }
    +      filterFunc = getSimpleFilterFunc(query, skipFilters);
    +      if ('+' == oper) {
    +        retFunc = nextSiblingIterator(filterFunc);
    +      } else if ('~' == oper) {
    +        retFunc = nextSiblingsIterator(filterFunc);
    +      } else if ('>' == oper) {
    +        retFunc = _childElements(filterFunc);
    +      }
    +    }
    +    // cache it and return
    +    return _getElementsFuncCache[query.query] = retFunc;
    +  };
    +
    +  var filterDown = function(root, queryParts) {
    +    // NOTE:
    +    //    this is the guts of the DOM query system. It takes a list of
    +    //    parsed query parts and a root and finds children which match
    +    //    the selector represented by the parts
    +    var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
    +
    +    for (var i = 0; i < qpl; i++) {
    +      ret = [];
    +      qp = queryParts[i];
    +      x = candidates.length - 1;
    +      if (x > 0) {
    +        // if we have more than one root at this level, provide a new
    +        // hash to use for checking group membership but tell the
    +        // system not to post-filter us since we will already have been
    +        // guaranteed to be unique
    +        bag = {};
    +        ret.nozip = true;
    +      }
    +      var gef = getElementsFunc(qp);
    +      for (var j = 0; te = candidates[j]; j++) {
    +        // for every root, get the elements that match the descendant
    +        // selector, adding them to the 'ret' array and filtering them
    +        // via membership in this level's bag. If there are more query
    +        // parts, then this level's return will be used as the next
    +        // level's candidates
    +        gef(te, ret, bag);
    +      }
    +      if (!ret.length) { break; }
    +      candidates = ret;
    +    }
    +    return ret;
    +  };
    +
    +  ////////////////////////////////////////////////////////////////////////
    +  // the query runner
    +  ////////////////////////////////////////////////////////////////////////
    +
    +  // these are the primary caches for full-query results. The query
    +  // dispatcher functions are generated then stored here for hash lookup in
    +  // the future
    +  var _queryFuncCacheDOM = {},
    +    _queryFuncCacheQSA = {};
    +
    +  // this is the second level of splitting, from full-length queries (e.g.,
    +  // 'div.foo .bar') into simple query expressions (e.g., ['div.foo',
    +  // '.bar'])
    +  var getStepQueryFunc = function(query) {
    +    var qparts = getQueryParts(goog.string.trim(query));
    +
    +    // if it's trivial, avoid iteration and zipping costs
    +    if (qparts.length == 1) {
    +      // We optimize this case here to prevent dispatch further down the
    +      // chain, potentially slowing things down. We could more elegantly
    +      // handle this in filterDown(), but it's slower for simple things
    +      // that need to be fast (e.g., '#someId').
    +      var tef = getElementsFunc(qparts[0]);
    +      return function(root) {
    +        var r = tef(root, []);
    +        if (r) { r.nozip = true; }
    +        return r;
    +      }
    +    }
    +
    +    // otherwise, break it up and return a runner that iterates over the parts
    +    // recursively
    +    return function(root) {
    +      return filterDown(root, qparts);
    +    }
    +  };
    +
    +  // NOTES:
    +  //  * we can't trust QSA for anything but document-rooted queries, so
    +  //    caching is split into DOM query evaluators and QSA query evaluators
    +  //  * caching query results is dirty and leak-prone (or, at a minimum,
    +  //    prone to unbounded growth). Other toolkits may go this route, but
    +  //    they totally destroy their own ability to manage their memory
    +  //    footprint. If we implement it, it should only ever be with a fixed
    +  //    total element reference # limit and an LRU-style algorithm since JS
    +  //    has no weakref support. Caching compiled query evaluators is also
    +  //    potentially problematic, but even on large documents the size of the
    +  //    query evaluators is often < 100 function objects per evaluator (and
    +  //    LRU can be applied if it's ever shown to be an issue).
    +  //  * since IE's QSA support is currently only for HTML documents and even
    +  //    then only in IE 8's 'standards mode', we have to detect our dispatch
    +  //    route at query time and keep 2 separate caches. Ugg.
    +
    +  var qsa = 'querySelectorAll';
    +
    +  // some versions of Safari provided QSA, but it was buggy and crash-prone.
    +  // We need to detect the right 'internal' webkit version to make this work.
    +  var qsaAvail = (
    +    !!goog.dom.getDocument()[qsa] &&
    +    // see #5832
    +    (!goog.userAgent.WEBKIT || goog.userAgent.isVersionOrHigher('526'))
    +  );
    +
    +  /** @param {boolean=} opt_forceDOM */
    +  var getQueryFunc = function(query, opt_forceDOM) {
    +
    +    if (qsaAvail) {
    +      // if we've got a cached variant and we think we can do it, run it!
    +      var qsaCached = _queryFuncCacheQSA[query];
    +      if (qsaCached && !opt_forceDOM) {
    +        return qsaCached;
    +      }
    +    }
    +
    +    // else if we've got a DOM cached variant, assume that we already know
    +    // all we need to and use it
    +    var domCached = _queryFuncCacheDOM[query];
    +    if (domCached) {
    +      return domCached;
    +    }
    +
    +    // TODO:
    +    //    today we're caching DOM and QSA branches separately so we
    +    //    recalc useQSA every time. If we had a way to tag root+query
    +    //    efficiently, we'd be in good shape to do a global cache.
    +
    +    var qcz = query.charAt(0);
    +    var nospace = (-1 == query.indexOf(' '));
    +
    +    // byId searches are wicked fast compared to QSA, even when filtering
    +    // is required
    +    if ((query.indexOf('#') >= 0) && (nospace)) {
    +      opt_forceDOM = true;
    +    }
    +
    +    var useQSA = (
    +      qsaAvail && (!opt_forceDOM) &&
    +      // as per CSS 3, we can't currently start w/ combinator:
    +      //    http://www.w3.org/TR/css3-selectors/#w3cselgrammar
    +      (specials.indexOf(qcz) == -1) &&
    +      // IE's QSA impl sucks on pseudos
    +      (!legacyIE || (query.indexOf(':') == -1)) &&
    +
    +      (!(cssCaseBug && (query.indexOf('.') >= 0))) &&
    +
    +      // FIXME:
    +      //    need to tighten up browser rules on ':contains' and '|=' to
    +      //    figure out which aren't good
    +      (query.indexOf(':contains') == -1) &&
    +      (query.indexOf('|=') == -1) // some browsers don't understand it
    +    );
    +
    +    // TODO:
    +    //    if we've got a descendant query (e.g., '> .thinger' instead of
    +    //    just '.thinger') in a QSA-able doc, but are passed a child as a
    +    //    root, it should be possible to give the item a synthetic ID and
    +    //    trivially rewrite the query to the form '#synid > .thinger' to
    +    //    use the QSA branch
    +
    +
    +    if (useQSA) {
    +      var tq = (specials.indexOf(query.charAt(query.length - 1)) >= 0) ?
    +            (query + ' *') : query;
    +      return _queryFuncCacheQSA[query] = function(root) {
    +        try {
    +          // the QSA system contains an egregious spec bug which
    +          // limits us, effectively, to only running QSA queries over
    +          // entire documents.  See:
    +          //    http://ejohn.org/blog/thoughts-on-queryselectorall/
    +          //  despite this, we can also handle QSA runs on simple
    +          //  selectors, but we don't want detection to be expensive
    +          //  so we're just checking for the presence of a space char
    +          //  right now. Not elegant, but it's cheaper than running
    +          //  the query parser when we might not need to
    +          if (!((9 == root.nodeType) || nospace)) {
    +            throw '';
    +          }
    +          var r = root[qsa](tq);
    +          // IE QSA queries may incorrectly include comment nodes, so we throw
    +          // the zipping function into 'remove' comments mode instead of the
    +          // normal 'skip it' which every other QSA-clued browser enjoys
    +          // skip expensive duplication checks and just wrap in an array.
    +          if (legacyIE) {
    +            r.commentStrip = true;
    +          } else {
    +            r.nozip = true;
    +          }
    +          return r;
    +        } catch (e) {
    +          // else run the DOM branch on this query, ensuring that we
    +          // default that way in the future
    +          return getQueryFunc(query, true)(root);
    +        }
    +      }
    +    } else {
    +      // DOM branch
    +      var parts = query.split(/\s*,\s*/);
    +      return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
    +        // if not a compound query (e.g., '.foo, .bar'), cache and return a
    +        // dispatcher
    +        getStepQueryFunc(query) :
    +        // if it *is* a complex query, break it up into its
    +        // constituent parts and return a dispatcher that will
    +        // merge the parts when run
    +        function(root) {
    +          var pindex = 0, // avoid array alloc for every invocation
    +            ret = [],
    +            tp;
    +          while (tp = parts[pindex++]) {
    +            ret = ret.concat(getStepQueryFunc(tp)(root));
    +          }
    +          return ret;
    +        }
    +      );
    +    }
    +  };
    +
    +  var _zipIdx = 0;
    +
    +  // NOTE:
    +  //    this function is Moo inspired, but our own impl to deal correctly
    +  //    with XML in IE
    +  var _nodeUID = legacyIE ? function(node) {
    +    if (caseSensitive) {
    +      // XML docs don't have uniqueID on their nodes
    +      return node.getAttribute('_uid') ||
    +          node.setAttribute('_uid', ++_zipIdx) || _zipIdx;
    +
    +    } else {
    +      return node.uniqueID;
    +    }
    +  } :
    +  function(node) {
    +    return (node['_uid'] || (node['_uid'] = ++_zipIdx));
    +  };
    +
    +  // determine if a node in is unique in a 'bag'. In this case we don't want
    +  // to flatten a list of unique items, but rather just tell if the item in
    +  // question is already in the bag. Normally we'd just use hash lookup to do
    +  // this for us but IE's DOM is busted so we can't really count on that. On
    +  // the upside, it gives us a built in unique ID function.
    +  var _isUnique = function(node, bag) {
    +    if (!bag) {
    +      return 1;
    +    }
    +    var id = _nodeUID(node);
    +    if (!bag[id]) {
    +      return bag[id] = 1;
    +    }
    +    return 0;
    +  };
    +
    +  // attempt to efficiently determine if an item in a list is a dupe,
    +  // returning a list of 'uniques', hopefully in document order
    +  var _zipIdxName = '_zipIdx';
    +  var _zip = function(arr) {
    +    if (arr && arr.nozip) {
    +      return arr;
    +    }
    +    var ret = [];
    +    if (!arr || !arr.length) {
    +      return ret;
    +    }
    +    if (arr[0]) {
    +      ret.push(arr[0]);
    +    }
    +    if (arr.length < 2) {
    +      return ret;
    +    }
    +
    +    _zipIdx++;
    +
    +    // we have to fork here for IE and XML docs because we can't set
    +    // expandos on their nodes (apparently). *sigh*
    +    if (legacyIE && caseSensitive) {
    +      var szidx = _zipIdx + '';
    +      arr[0].setAttribute(_zipIdxName, szidx);
    +      for (var x = 1, te; te = arr[x]; x++) {
    +        if (arr[x].getAttribute(_zipIdxName) != szidx) {
    +          ret.push(te);
    +        }
    +        te.setAttribute(_zipIdxName, szidx);
    +      }
    +    } else if (legacyIE && arr.commentStrip) {
    +      try {
    +        for (var x = 1, te; te = arr[x]; x++) {
    +          if (isElement(te)) {
    +            ret.push(te);
    +          }
    +        }
    +      } catch (e) { /* squelch */ }
    +    } else {
    +      if (arr[0]) {
    +        arr[0][_zipIdxName] = _zipIdx;
    +      }
    +      for (var x = 1, te; te = arr[x]; x++) {
    +        if (arr[x][_zipIdxName] != _zipIdx) {
    +          ret.push(te);
    +        }
    +        te[_zipIdxName] = _zipIdx;
    +      }
    +    }
    +    return ret;
    +  };
    +
    +  /**
    +   * The main executor. Type specification from above.
    +   * @param {string|Array} query The query.
    +   * @param {(string|Node)=} root The root.
    +   * @return {!Array} The elements that matched the query.
    +   */
    +  var query = function(query, root) {
    +    // NOTE: elementsById is not currently supported
    +    // NOTE: ignores xpath-ish queries for now
    +
    +    //Set list constructor to desired value. This can change
    +    //between calls, so always re-assign here.
    +
    +    if (!query) {
    +      return [];
    +    }
    +
    +    if (query.constructor == Array) {
    +      return /** @type {!Array} */ (query);
    +    }
    +
    +    if (!goog.isString(query)) {
    +      return [query];
    +    }
    +
    +    if (goog.isString(root)) {
    +      root = goog.dom.getElement(root);
    +      if (!root) {
    +        return [];
    +      }
    +    }
    +
    +    root = root || goog.dom.getDocument();
    +    var od = root.ownerDocument || root.documentElement;
    +
    +    // throw the big case sensitivity switch
    +
    +    // NOTE:
    +    //    Opera in XHTML mode doesn't detect case-sensitivity correctly
    +    //    and it's not clear that there's any way to test for it
    +    caseSensitive =
    +        root.contentType && root.contentType == 'application/xml' ||
    +        goog.userAgent.OPERA &&
    +          (root.doctype || od.toString() == '[object XMLDocument]') ||
    +        !!od &&
    +        (legacyIE ? od.xml : (root.xmlVersion || od.xmlVersion));
    +
    +    // NOTE:
    +    //    adding 'true' as the 2nd argument to getQueryFunc is useful for
    +    //    testing the DOM branch without worrying about the
    +    //    behavior/performance of the QSA branch.
    +    var r = getQueryFunc(query)(root);
    +
    +    // FIXME(slightlyoff):
    +    //    need to investigate this branch WRT dojo:#8074 and dojo:#8075
    +    if (r && r.nozip) {
    +      return r;
    +    }
    +    return _zip(r);
    +  }
    +
    +  // FIXME: need to add infrastructure for post-filtering pseudos, ala :last
    +  query.pseudos = pseudos;
    +
    +  return query;
    +})();
    +
    +// TODO(arv): Please don't export here since it clobbers dead code elimination.
    +goog.exportSymbol('goog.dom.query', goog.dom.query);
    +goog.exportSymbol('goog.dom.query.pseudos', goog.dom.query.pseudos);
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.html b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.html
    new file mode 100644
    index 0000000000..21dc81186d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.html
    @@ -0,0 +1,64 @@
    +<!DOCTYPE html>
    +<!--
    +Copyright 2005-2008, The Dojo Foundation
    +Modifications Copyright 2008 The Closure Library Authors. All Rights Reserved.
    +All Rights Reserved
    +-->
    +<html>
    +<head>
    +  <title>Closure Unit Tests - goog.dom</title>
    +  <script src="../../../../../closure/goog/base.js"></script>
    +  <script>
    +    goog.require('goog.dom');
    +    goog.require('goog.dom.query');
    +    goog.require('goog.testing.asserts');
    +    goog.require('goog.testing.jsunit');
    +  </script>
    +  <script src="query_test.js"></script>
    +</head>
    +<body>
    +<h1>testing goog.dom.query()</h1>
    +<div id="t">
    +  <h3>h3 <span>span</span> endh3 </h3>
    +  <!-- comment to throw things off -->
    +  <div class="foo bar" id="_foo">
    +    <h3>h3</h3>
    +    <span id="foo"></span>
    +    <span></span>
    +  </div>
    +  <h3>h3</h3>
    +  <h3 class="baz" title="thud">h3</h3>
    +  <span class="foobar baz foo"></span>
    +  <span foo="bar"></span>
    +  <span foo="baz bar thud"></span>
    +  <!-- FIXME: should foo="bar-baz-thud" match? [foo$=thud] ??? -->
    +  <span foo="bar-baz-thudish" id="silly:id::with:colons"></span>
    +  <div id="container">
    +    <div id="child1" qux="true"></div>
    +    <div id="child2"></div>
    +    <div id="child3" qux="true"></div>
    +  </div>
    +  <div qux="true"></div>
    +  <input id="notbug" name="bug" type="hidden" value="failed">
    +  <input id="bug" type="hidden" value="passed">
    +</div>
    +
    +<div class="myupperclass">
    +  <span class="myclass">
    +    <input id="myid1">
    +  </span>
    +  <span class="myclass">
    +    <input id="myid2">
    +  </span>
    +</div>
    +
    +<iframe name=ifr></iframe>
    +<div id=iframe-test>
    +  <div id=if1>
    +    <div class=if2>
    +      <div id=if3></div>
    +    </div>
    +  </div>
    +</div>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.js b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.js
    new file mode 100644
    index 0000000000..68f539a9e7
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/dojo/dom/query_test.js
    @@ -0,0 +1,173 @@
    +goog.setTestOnly('query_test');
    +
    +goog.require('goog.dom');
    +goog.require('goog.userAgent');
    +
    +function testBasicSelectors() {
    +  assertQuery(4, 'h3');
    +  assertQuery(1, 'h1:first-child');
    +  assertQuery(2, 'h3:first-child');
    +  assertQuery(1, '#t');
    +  assertQuery(1, '#bug');
    +  assertQuery(4, '#t h3');
    +  assertQuery(1, 'div#t');
    +  assertQuery(4, 'div#t h3');
    +  assertQuery(0, 'span#t');
    +  assertQuery(1, '#t div > h3');
    +  assertQuery(2, '.foo');
    +  assertQuery(1, '.foo.bar');
    +  assertQuery(2, '.baz');
    +  assertQuery(3, '#t > h3');
    +}
    +
    +function testSyntacticEquivalents() {
    +  // syntactic equivalents
    +  assertQuery(12, '#t > *');
    +  assertQuery(12, '#t >');
    +  assertQuery(3, '.foo > *');
    +  assertQuery(3, '.foo >');
    +}
    +
    +function testWithARootById() {
    +  // Broken in latest chrome.
    +  if (goog.userAgent.WEBKIT) {
    +    return;
    +  }
    +
    +  // with a root, by ID
    +  assertQuery(3, '> *', 'container');
    +  assertQuery(3, '> h3', 't');
    +}
    +
    +function testCompoundQueries() {
    +  // compound queries
    +  assertQuery(2, '.foo, .bar');
    +  assertQuery(2, '.foo,.bar');
    +}
    +
    +function testMultipleClassAttributes() {
    +  // multiple class attribute
    +  assertQuery(1, '.foo.bar');
    +  assertQuery(2, '.foo');
    +  assertQuery(2, '.baz');
    +}
    +
    +function testCaseSensitivity() {
    +  // case sensitivity
    +  assertQuery(1, 'span.baz');
    +  assertQuery(1, 'sPaN.baz');
    +  assertQuery(1, 'SPAN.baz');
    +  assertQuery(1, '[class = \"foo bar\"]');
    +  assertQuery(2, '[foo~=\"bar\"]');
    +  assertQuery(2, '[ foo ~= \"bar\" ]');
    +}
    +
    +function testAttributes() {
    +  assertQuery(3, '[foo]');
    +  assertQuery(1, '[foo$=\"thud\"]');
    +  assertQuery(1, '[foo$=thud]');
    +  assertQuery(1, '[foo$=\"thudish\"]');
    +  assertQuery(1, '#t [foo$=thud]');
    +  assertQuery(1, '#t [ title $= thud ]');
    +  assertQuery(0, '#t span[ title $= thud ]');
    +  assertQuery(2, '[foo|=\"bar\"]');
    +  assertQuery(1, '[foo|=\"bar-baz\"]');
    +  assertQuery(0, '[foo|=\"baz\"]');
    +}
    +
    +function testDescendantSelectors() {
    +
    +  // Broken in latest chrome.
    +  if (goog.userAgent.WEBKIT) {
    +    return;
    +  }
    +
    +  assertQuery(3, '>', 'container');
    +  assertQuery(3, '> *', 'container');
    +  assertQuery(2, '> [qux]', 'container');
    +  assertEquals('child1', goog.dom.query('> [qux]', 'container')[0].id);
    +  assertEquals('child3', goog.dom.query('> [qux]', 'container')[1].id);
    +  assertQuery(3, '>', 'container');
    +  assertQuery(3, '> *', 'container');
    +}
    +
    +function testSiblingSelectors() {
    +  assertQuery(1, '+', 'container');
    +  assertQuery(3, '~', 'container');
    +  assertQuery(1, '.foo + span');
    +  assertQuery(4, '.foo ~ span');
    +  assertQuery(1, '#foo ~ *');
    +  assertQuery(1, '#foo ~');
    +}
    +
    +function testSubSelectors() {
    +  // sub-selector parsing
    +  assertQuery(1, '#t span.foo:not(span:first-child)');
    +  assertQuery(1, '#t span.foo:not(:first-child)');
    +}
    +
    +function testNthChild() {
    +  assertEquals(goog.dom.$('_foo'), goog.dom.query('.foo:nth-child(2)')[0]);
    +  assertQuery(2, '#t > h3:nth-child(odd)');
    +  assertQuery(3, '#t h3:nth-child(odd)');
    +  assertQuery(3, '#t h3:nth-child(2n+1)');
    +  assertQuery(1, '#t h3:nth-child(even)');
    +  assertQuery(1, '#t h3:nth-child(2n)');
    +  assertQuery(1, '#t h3:nth-child(2n+3)');
    +  assertQuery(2, '#t h3:nth-child(1)');
    +  assertQuery(1, '#t > h3:nth-child(1)');
    +  assertQuery(3, '#t :nth-child(3)');
    +  assertQuery(0, '#t > div:nth-child(1)');
    +  assertQuery(7, '#t span');
    +  assertQuery(3, '#t > *:nth-child(n+10)');
    +  assertQuery(1, '#t > *:nth-child(n+12)');
    +  assertQuery(10, '#t > *:nth-child(-n+10)');
    +  assertQuery(5, '#t > *:nth-child(-2n+10)');
    +  assertQuery(6, '#t > *:nth-child(2n+2)');
    +  assertQuery(5, '#t > *:nth-child(2n+4)');
    +  assertQuery(5, '#t > *:nth-child(2n+4)');
    +  assertQuery(12, '#t > *:nth-child(n-5)');
    +  assertQuery(6, '#t > *:nth-child(2n-5)');
    +}
    +
    +function testEmptyPseudoSelector() {
    +  assertQuery(4, '#t > span:empty');
    +  assertQuery(6, '#t span:empty');
    +  assertQuery(0, 'h3 span:empty');
    +  assertQuery(1, 'h3 :not(:empty)');
    +}
    +
    +function testIdsWithColons() {
    +  assertQuery(1, '#silly\\:id\\:\\:with\\:colons');
    +}
    +
    +function testOrder() {
    +  var els = goog.dom.query('.myupperclass .myclass input');
    +  assertEquals('myid1', els[0].id);
    +  assertEquals('myid2', els[1].id);
    +}
    +
    +function testCorrectDocumentInFrame() {
    +  var frameDocument = window.frames['ifr'].document;
    +  frameDocument.body.innerHTML =
    +      document.getElementById('iframe-test').innerHTML;
    +
    +  var els = goog.dom.query('#if1 .if2 div', document);
    +  var frameEls = goog.dom.query('#if1 .if2 div', frameDocument);
    +
    +  assertEquals(els.length, frameEls.length);
    +  assertEquals(1, frameEls.length);
    +  assertNotEquals(document.getElementById('if3'),
    +                  frameDocument.getElementById('if3'));
    +}
    +
    +
    +/**
    + * @param {number} expectedNumberOfNodes
    + * @param {...*} var_args
    + */
    +function assertQuery(expectedNumberOfNodes, var_args) {
    +  var args = Array.prototype.slice.call(arguments, 1);
    +  assertEquals(expectedNumberOfNodes,
    +               goog.dom.query.apply(null, args).length);
    +}
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/jpeg_encoder/jpeg_encoder_basic.js b/mapbox-gl/closure-library/third_party/closure/goog/jpeg_encoder/jpeg_encoder_basic.js
    new file mode 100644
    index 0000000000..aadccfb709
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/jpeg_encoder/jpeg_encoder_basic.js
    @@ -0,0 +1,751 @@
    +/**
    + * @license
    +  Copyright (c) 2008, Adobe Systems Incorporated
    +  All rights reserved.
    +
    +  Redistribution and use in source and binary forms, with or without
    +  modification, are permitted provided that the following conditions are
    +  met:
    +
    +  * Redistributions of source code must retain the above copyright notice,
    +    this list of conditions and the following disclaimer.
    +
    +  * Redistributions in binary form must reproduce the above copyright
    +    notice, this list of conditions and the following disclaimer in the
    +    documentation and/or other materials provided with the distribution.
    +
    +  * Neither the name of Adobe Systems Incorporated nor the names of its
    +    contributors may be used to endorse or promote products derived from
    +    this software without specific prior written permission.
    +
    +  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    +  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    +  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    +  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    +  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    +  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    +  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    +  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    +  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    +  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    +  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +*/
    +/**
    + * @license
    +JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009
    +
    +Basic GUI blocking jpeg encoder
    +
    +v 0.9
    +*/
    +
    +/**
    + * @fileoverview This code was ported from
    + * http://www.bytestrom.eu/blog/2009/1120a_jpeg_encoder_for_javascript and
    + * modified slightly for Closure.
    + */
    +
    +goog.provide('goog.crypt.JpegEncoder');
    +
    +goog.require('goog.crypt.base64');
    +
    +/**
    + * Initializes the JpegEncoder.
    + *
    + * @constructor
    + * @param {number=} opt_quality The compression quality. Default 50.
    + */
    +goog.crypt.JpegEncoder = function(opt_quality) {
    +  var self = this;
    +  var fround = Math.round;
    +  var ffloor = Math.floor;
    +  var YTable = new Array(64);
    +  var UVTable = new Array(64);
    +  var fdtbl_Y = new Array(64);
    +  var fdtbl_UV = new Array(64);
    +  var YDC_HT;
    +  var UVDC_HT;
    +  var YAC_HT;
    +  var UVAC_HT;
    +
    +  var bitcode = new Array(65535);
    +  var category = new Array(65535);
    +  var outputfDCTQuant = new Array(64);
    +  var DU = new Array(64);
    +  var byteout = [];
    +  var bytenew = 0;
    +  var bytepos = 7;
    +
    +  var YDU = new Array(64);
    +  var UDU = new Array(64);
    +  var VDU = new Array(64);
    +  var clt = new Array(256);
    +  var RGB_YUV_TABLE = new Array(2048);
    +  var currentQuality;
    +
    +  var ZigZag = [
    +       0, 1, 5, 6,14,15,27,28,
    +       2, 4, 7,13,16,26,29,42,
    +       3, 8,12,17,25,30,41,43,
    +       9,11,18,24,31,40,44,53,
    +      10,19,23,32,39,45,52,54,
    +      20,22,33,38,46,51,55,60,
    +      21,34,37,47,50,56,59,61,
    +      35,36,48,49,57,58,62,63
    +    ];
    +
    +  var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
    +  var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
    +  var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
    +  var std_ac_luminance_values = [
    +      0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
    +      0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
    +      0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
    +      0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
    +      0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
    +      0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
    +      0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
    +      0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
    +      0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
    +      0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
    +      0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
    +      0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
    +      0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
    +      0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
    +      0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
    +      0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
    +      0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
    +      0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
    +      0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
    +      0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
    +      0xf9,0xfa
    +    ];
    +
    +  var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
    +  var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
    +  var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
    +  var std_ac_chrominance_values = [
    +      0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
    +      0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
    +      0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
    +      0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
    +      0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
    +      0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
    +      0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
    +      0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
    +      0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
    +      0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
    +      0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
    +      0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
    +      0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
    +      0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
    +      0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
    +      0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
    +      0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
    +      0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
    +      0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
    +      0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
    +      0xf9,0xfa
    +    ];
    +
    +  function initQuantTables(sf){
    +      var YQT = [
    +        16, 11, 10, 16, 24, 40, 51, 61,
    +        12, 12, 14, 19, 26, 58, 60, 55,
    +        14, 13, 16, 24, 40, 57, 69, 56,
    +        14, 17, 22, 29, 51, 87, 80, 62,
    +        18, 22, 37, 56, 68,109,103, 77,
    +        24, 35, 55, 64, 81,104,113, 92,
    +        49, 64, 78, 87,103,121,120,101,
    +        72, 92, 95, 98,112,100,103, 99
    +      ];
    +
    +      for (var i = 0; i < 64; i++) {
    +        var t = ffloor((YQT[i]*sf+50)/100);
    +        if (t < 1) {
    +          t = 1;
    +        } else if (t > 255) {
    +          t = 255;
    +        }
    +        YTable[ZigZag[i]] = t;
    +      }
    +      var UVQT = [
    +        17, 18, 24, 47, 99, 99, 99, 99,
    +        18, 21, 26, 66, 99, 99, 99, 99,
    +        24, 26, 56, 99, 99, 99, 99, 99,
    +        47, 66, 99, 99, 99, 99, 99, 99,
    +        99, 99, 99, 99, 99, 99, 99, 99,
    +        99, 99, 99, 99, 99, 99, 99, 99,
    +        99, 99, 99, 99, 99, 99, 99, 99,
    +        99, 99, 99, 99, 99, 99, 99, 99
    +      ];
    +      for (var j = 0; j < 64; j++) {
    +        var u = ffloor((UVQT[j]*sf+50)/100);
    +        if (u < 1) {
    +          u = 1;
    +        } else if (u > 255) {
    +          u = 255;
    +        }
    +        UVTable[ZigZag[j]] = u;
    +      }
    +      var aasf = [
    +        1.0, 1.387039845, 1.306562965, 1.175875602,
    +        1.0, 0.785694958, 0.541196100, 0.275899379
    +      ];
    +      var k = 0;
    +      for (var row = 0; row < 8; row++)
    +      {
    +        for (var col = 0; col < 8; col++)
    +        {
    +          fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
    +          fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
    +          k++;
    +        }
    +      }
    +    }
    +
    +    function computeHuffmanTbl(nrcodes, std_table){
    +      var codevalue = 0;
    +      var pos_in_table = 0;
    +      var HT = new Array();
    +      for (var k = 1; k <= 16; k++) {
    +        for (var j = 1; j <= nrcodes[k]; j++) {
    +          HT[std_table[pos_in_table]] = [];
    +          HT[std_table[pos_in_table]][0] = codevalue;
    +          HT[std_table[pos_in_table]][1] = k;
    +          pos_in_table++;
    +          codevalue++;
    +        }
    +        codevalue*=2;
    +      }
    +      return HT;
    +    }
    +
    +    function initHuffmanTbl()
    +    {
    +      YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
    +      UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
    +      YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
    +      UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
    +    }
    +
    +    function initCategoryNumber()
    +    {
    +      var nrlower = 1;
    +      var nrupper = 2;
    +      for (var cat = 1; cat <= 15; cat++) {
    +        //Positive numbers
    +        for (var nr = nrlower; nr<nrupper; nr++) {
    +          category[32767+nr] = cat;
    +          bitcode[32767+nr] = [];
    +          bitcode[32767+nr][1] = cat;
    +          bitcode[32767+nr][0] = nr;
    +        }
    +        //Negative numbers
    +        for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {
    +          category[32767+nrneg] = cat;
    +          bitcode[32767+nrneg] = [];
    +          bitcode[32767+nrneg][1] = cat;
    +          bitcode[32767+nrneg][0] = nrupper-1+nrneg;
    +        }
    +        nrlower <<= 1;
    +        nrupper <<= 1;
    +      }
    +    }
    +
    +    function initRGBYUVTable() {
    +      for(var i = 0; i < 256;i++) {
    +        RGB_YUV_TABLE[i]          =  19595 * i;
    +        RGB_YUV_TABLE[(i+ 256)>>0]   =  38470 * i;
    +        RGB_YUV_TABLE[(i+ 512)>>0]   =   7471 * i + 0x8000;
    +        RGB_YUV_TABLE[(i+ 768)>>0]   = -11059 * i;
    +        RGB_YUV_TABLE[(i+1024)>>0]   = -21709 * i;
    +        RGB_YUV_TABLE[(i+1280)>>0]   =  32768 * i + 0x807FFF;
    +        RGB_YUV_TABLE[(i+1536)>>0]   = -27439 * i;
    +        RGB_YUV_TABLE[(i+1792)>>0]   = - 5329 * i;
    +      }
    +    }
    +
    +    // IO functions
    +    function writeBits(bs)
    +    {
    +      var value = bs[0];
    +      var posval = bs[1]-1;
    +      while ( posval >= 0 ) {
    +        if (value & (1 << posval) ) {
    +          bytenew |= (1 << bytepos);
    +        }
    +        posval--;
    +        bytepos--;
    +        if (bytepos < 0) {
    +          if (bytenew == 0xFF) {
    +            writeByte(0xFF);
    +            writeByte(0);
    +          }
    +          else {
    +            writeByte(bytenew);
    +          }
    +          bytepos=7;
    +          bytenew=0;
    +        }
    +      }
    +    }
    +
    +    function writeByte(value)
    +    {
    +      byteout.push(clt[value]); // write char directly instead of converting later
    +    }
    +
    +    function writeWord(value)
    +    {
    +      writeByte((value>>8)&0xFF);
    +      writeByte((value   )&0xFF);
    +    }
    +
    +    // DCT & quantization core
    +    function fDCTQuant(data, fdtbl)
    +    {
    +      var d0, d1, d2, d3, d4, d5, d6, d7;
    +      /* Pass 1: process rows. */
    +      var dataOff=0;
    +      var i;
    +      var I8 = 8;
    +      var I64 = 64;
    +      for (i=0; i<I8; ++i)
    +      {
    +        d0 = data[dataOff];
    +        d1 = data[dataOff+1];
    +        d2 = data[dataOff+2];
    +        d3 = data[dataOff+3];
    +        d4 = data[dataOff+4];
    +        d5 = data[dataOff+5];
    +        d6 = data[dataOff+6];
    +        d7 = data[dataOff+7];
    +
    +        var tmp0 = d0 + d7;
    +        var tmp7 = d0 - d7;
    +        var tmp1 = d1 + d6;
    +        var tmp6 = d1 - d6;
    +        var tmp2 = d2 + d5;
    +        var tmp5 = d2 - d5;
    +        var tmp3 = d3 + d4;
    +        var tmp4 = d3 - d4;
    +
    +        /* Even part */
    +        var tmp10 = tmp0 + tmp3;  /* phase 2 */
    +        var tmp13 = tmp0 - tmp3;
    +        var tmp11 = tmp1 + tmp2;
    +        var tmp12 = tmp1 - tmp2;
    +
    +        data[dataOff] = tmp10 + tmp11; /* phase 3 */
    +        data[dataOff+4] = tmp10 - tmp11;
    +
    +        var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
    +        data[dataOff+2] = tmp13 + z1; /* phase 5 */
    +        data[dataOff+6] = tmp13 - z1;
    +
    +        /* Odd part */
    +        tmp10 = tmp4 + tmp5; /* phase 2 */
    +        tmp11 = tmp5 + tmp6;
    +        tmp12 = tmp6 + tmp7;
    +
    +        /* The rotator is modified from fig 4-8 to avoid extra negations. */
    +        var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
    +        var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
    +        var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
    +        var z3 = tmp11 * 0.707106781; /* c4 */
    +
    +        var z11 = tmp7 + z3;  /* phase 5 */
    +        var z13 = tmp7 - z3;
    +
    +        data[dataOff+5] = z13 + z2;  /* phase 6 */
    +        data[dataOff+3] = z13 - z2;
    +        data[dataOff+1] = z11 + z4;
    +        data[dataOff+7] = z11 - z4;
    +
    +        dataOff += 8; /* advance pointer to next row */
    +      }
    +
    +      /* Pass 2: process columns. */
    +      dataOff = 0;
    +      for (i=0; i<I8; ++i)
    +      {
    +        d0 = data[dataOff];
    +        d1 = data[dataOff + 8];
    +        d2 = data[dataOff + 16];
    +        d3 = data[dataOff + 24];
    +        d4 = data[dataOff + 32];
    +        d5 = data[dataOff + 40];
    +        d6 = data[dataOff + 48];
    +        d7 = data[dataOff + 56];
    +
    +        var tmp0p2 = d0 + d7;
    +        var tmp7p2 = d0 - d7;
    +        var tmp1p2 = d1 + d6;
    +        var tmp6p2 = d1 - d6;
    +        var tmp2p2 = d2 + d5;
    +        var tmp5p2 = d2 - d5;
    +        var tmp3p2 = d3 + d4;
    +        var tmp4p2 = d3 - d4;
    +
    +        /* Even part */
    +        var tmp10p2 = tmp0p2 + tmp3p2;  /* phase 2 */
    +        var tmp13p2 = tmp0p2 - tmp3p2;
    +        var tmp11p2 = tmp1p2 + tmp2p2;
    +        var tmp12p2 = tmp1p2 - tmp2p2;
    +
    +        data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
    +        data[dataOff+32] = tmp10p2 - tmp11p2;
    +
    +        var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
    +        data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
    +        data[dataOff+48] = tmp13p2 - z1p2;
    +
    +        /* Odd part */
    +        tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
    +        tmp11p2 = tmp5p2 + tmp6p2;
    +        tmp12p2 = tmp6p2 + tmp7p2;
    +
    +        /* The rotator is modified from fig 4-8 to avoid extra negations. */
    +        var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
    +        var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
    +        var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
    +        var z3p2 = tmp11p2 * 0.707106781; /* c4 */
    +
    +        var z11p2 = tmp7p2 + z3p2;  /* phase 5 */
    +        var z13p2 = tmp7p2 - z3p2;
    +
    +        data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
    +        data[dataOff+24] = z13p2 - z2p2;
    +        data[dataOff+ 8] = z11p2 + z4p2;
    +        data[dataOff+56] = z11p2 - z4p2;
    +
    +        dataOff++; /* advance pointer to next column */
    +      }
    +
    +      // Quantize/descale the coefficients
    +      var fDCTQuant;
    +      for (i=0; i<I64; ++i)
    +      {
    +        // Apply the quantization and scaling factor & Round to nearest integer
    +        fDCTQuant = data[i]*fdtbl[i];
    +        outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);
    +        //outputfDCTQuant[i] = fround(fDCTQuant);
    +
    +      }
    +      return outputfDCTQuant;
    +    }
    +
    +    function writeAPP0()
    +    {
    +      writeWord(0xFFE0); // marker
    +      writeWord(16); // length
    +      writeByte(0x4A); // J
    +      writeByte(0x46); // F
    +      writeByte(0x49); // I
    +      writeByte(0x46); // F
    +      writeByte(0); // = "JFIF",'\0'
    +      writeByte(1); // versionhi
    +      writeByte(1); // versionlo
    +      writeByte(0); // xyunits
    +      writeWord(1); // xdensity
    +      writeWord(1); // ydensity
    +      writeByte(0); // thumbnwidth
    +      writeByte(0); // thumbnheight
    +    }
    +
    +    function writeSOF0(width, height)
    +    {
    +      writeWord(0xFFC0); // marker
    +      writeWord(17);   // length, truecolor YUV JPG
    +      writeByte(8);    // precision
    +      writeWord(height);
    +      writeWord(width);
    +      writeByte(3);    // nrofcomponents
    +      writeByte(1);    // IdY
    +      writeByte(0x11); // HVY
    +      writeByte(0);    // QTY
    +      writeByte(2);    // IdU
    +      writeByte(0x11); // HVU
    +      writeByte(1);    // QTU
    +      writeByte(3);    // IdV
    +      writeByte(0x11); // HVV
    +      writeByte(1);    // QTV
    +    }
    +
    +    function writeDQT()
    +    {
    +      writeWord(0xFFDB); // marker
    +      writeWord(132);     // length
    +      writeByte(0);
    +      for (var i=0; i<64; i++) {
    +        writeByte(YTable[i]);
    +      }
    +      writeByte(1);
    +      for (var j=0; j<64; j++) {
    +        writeByte(UVTable[j]);
    +      }
    +    }
    +
    +    function writeDHT()
    +    {
    +      writeWord(0xFFC4); // marker
    +      writeWord(0x01A2); // length
    +
    +      writeByte(0); // HTYDCinfo
    +      for (var i=0; i<16; i++) {
    +        writeByte(std_dc_luminance_nrcodes[i+1]);
    +      }
    +      for (var j=0; j<=11; j++) {
    +        writeByte(std_dc_luminance_values[j]);
    +      }
    +
    +      writeByte(0x10); // HTYACinfo
    +      for (var k=0; k<16; k++) {
    +        writeByte(std_ac_luminance_nrcodes[k+1]);
    +      }
    +      for (var l=0; l<=161; l++) {
    +        writeByte(std_ac_luminance_values[l]);
    +      }
    +
    +      writeByte(1); // HTUDCinfo
    +      for (var m=0; m<16; m++) {
    +        writeByte(std_dc_chrominance_nrcodes[m+1]);
    +      }
    +      for (var n=0; n<=11; n++) {
    +        writeByte(std_dc_chrominance_values[n]);
    +      }
    +
    +      writeByte(0x11); // HTUACinfo
    +      for (var o=0; o<16; o++) {
    +        writeByte(std_ac_chrominance_nrcodes[o+1]);
    +      }
    +      for (var p=0; p<=161; p++) {
    +        writeByte(std_ac_chrominance_values[p]);
    +      }
    +    }
    +
    +    function writeSOS()
    +    {
    +      writeWord(0xFFDA); // marker
    +      writeWord(12); // length
    +      writeByte(3); // nrofcomponents
    +      writeByte(1); // IdY
    +      writeByte(0); // HTY
    +      writeByte(2); // IdU
    +      writeByte(0x11); // HTU
    +      writeByte(3); // IdV
    +      writeByte(0x11); // HTV
    +      writeByte(0); // Ss
    +      writeByte(0x3f); // Se
    +      writeByte(0); // Bf
    +    }
    +
    +    function processDU(CDU, fdtbl, DC, HTDC, HTAC){
    +      var EOB = HTAC[0x00];
    +      var M16zeroes = HTAC[0xF0];
    +      var pos;
    +      var I16 = 16;
    +      var I63 = 63;
    +      var I64 = 64;
    +      var DU_DCT = fDCTQuant(CDU, fdtbl);
    +      //ZigZag reorder
    +      for (var j=0;j<I64;++j) {
    +        DU[ZigZag[j]]=DU_DCT[j];
    +      }
    +      var Diff = DU[0] - DC; DC = DU[0];
    +      //Encode DC
    +      if (Diff==0) {
    +        writeBits(HTDC[0]); // Diff might be 0
    +      } else {
    +        pos = 32767+Diff;
    +        writeBits(HTDC[category[pos]]);
    +        writeBits(bitcode[pos]);
    +      }
    +      //Encode ACs
    +      var end0pos = 63; // was const... which is crazy
    +      for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};
    +      //end0pos = first element in reverse order !=0
    +      if ( end0pos == 0) {
    +        writeBits(EOB);
    +        return DC;
    +      }
    +      var i = 1;
    +      var lng;
    +      while ( i <= end0pos ) {
    +        var startpos = i;
    +        for (; (DU[i]==0) && (i<=end0pos); ++i) {}
    +        var nrzeroes = i-startpos;
    +        if ( nrzeroes >= I16 ) {
    +          lng = nrzeroes>>4;
    +          for (var nrmarker=1; nrmarker <= lng; ++nrmarker)
    +            writeBits(M16zeroes);
    +          nrzeroes = nrzeroes&0xF;
    +        }
    +        pos = 32767+DU[i];
    +        writeBits(HTAC[(nrzeroes<<4)+category[pos]]);
    +        writeBits(bitcode[pos]);
    +        i++;
    +      }
    +      if ( end0pos != I63 ) {
    +        writeBits(EOB);
    +      }
    +      return DC;
    +    }
    +
    +    function initCharLookupTable(){
    +      var sfcc = String.fromCharCode;
    +      for(var i=0; i < 256; i++){ ///// ACHTUNG // 255
    +        clt[i] = sfcc(i);
    +      }
    +    }
    +
    +/**
    + * Encodes ImageData to JPEG.
    + *
    + * @param {ImageData} image
    + * @param {number=} opt_quality The compression quality.
    + * @return {string} base64-encoded JPEG data.
    + */
    +    this.encode = function(image,opt_quality) // image data object
    +    {
    +      if(opt_quality) setQuality(opt_quality);
    +
    +      // Initialize bit writer
    +      byteout = new Array();
    +      bytenew=0;
    +      bytepos=7;
    +
    +      // Add JPEG headers
    +      writeWord(0xFFD8); // SOI
    +      writeAPP0();
    +      writeDQT();
    +      writeSOF0(image.width,image.height);
    +      writeDHT();
    +      writeSOS();
    +
    +
    +      // Encode 8x8 macroblocks
    +      var _DCY=0;
    +      var _DCU=0;
    +      var _DCV=0;
    +
    +      bytenew=0;
    +      bytepos=7;
    +
    +
    +      this.encode.displayName = "_encode_";
    +
    +      var imageData = image.data;
    +      var width = image.width;
    +      var height = image.height;
    +
    +      var quadWidth = width*4;
    +      var tripleWidth = width*3;
    +
    +      var x, y = 0;
    +      var r, g, b;
    +      var start,p, col,row,pos;
    +      while(y < height){
    +        x = 0;
    +        while(x < quadWidth){
    +        start = quadWidth * y + x;
    +        p = start;
    +        col = -1;
    +        row = 0;
    +
    +        for(pos=0; pos < 64; pos++){
    +          row = pos >> 3;// /8
    +          col = ( pos & 7 ) * 4; // %8
    +          p = start + ( row * quadWidth ) + col;
    +
    +          if(y+row >= height){ // padding bottom
    +            p-= (quadWidth*(y+1+row-height));
    +          }
    +
    +          if(x+col >= quadWidth){ // padding right
    +            p-= ((x+col) - quadWidth +4)
    +          }
    +
    +          r = imageData[ p++ ];
    +          g = imageData[ p++ ];
    +          b = imageData[ p++ ];
    +
    +
    +          /* // calculate YUV values dynamically
    +          YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
    +          UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
    +          VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
    +          */
    +
    +          // use lookup table (slightly faster)
    +          YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;
    +          UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;
    +          VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;
    +
    +        }
    +
    +        _DCY = processDU(YDU, fdtbl_Y, _DCY, YDC_HT, YAC_HT);
    +        _DCU = processDU(UDU, fdtbl_UV, _DCU, UVDC_HT, UVAC_HT);
    +        _DCV = processDU(VDU, fdtbl_UV, _DCV, UVDC_HT, UVAC_HT);
    +        x+=32;
    +        }
    +        y+=8;
    +      }
    +
    +
    +      ////////////////////////////////////////////////////////////////
    +
    +      // Do the bit alignment of the EOI marker
    +      if ( bytepos >= 0 ) {
    +        var fillbits = [];
    +        fillbits[1] = bytepos+1;
    +        fillbits[0] = (1<<(bytepos+1))-1;
    +        writeBits(fillbits);
    +      }
    +
    +      writeWord(0xFFD9); //EOI
    +
    +      var jpegDataUri = 'data:image/jpeg;base64,'
    +        + goog.crypt.base64.encodeString(byteout.join(''));
    +
    +      byteout = [];
    +
    +      return jpegDataUri
    +  }
    +
    +  function setQuality(quality){
    +    if (quality <= 0) {
    +      quality = 1;
    +    }
    +    if (quality > 100) {
    +      quality = 100;
    +    }
    +
    +    if(currentQuality == quality) return // don't recalc if unchanged
    +
    +    var sf = 0;
    +    if (quality < 50) {
    +      sf = Math.floor(5000 / quality);
    +    } else {
    +      sf = Math.floor(200 - quality*2);
    +    }
    +
    +    initQuantTables(sf);
    +    currentQuality = quality;
    +  }
    +
    +  function init(){
    +    if(!opt_quality) opt_quality = 50;
    +    // Create tables
    +    initCharLookupTable()
    +    initHuffmanTbl();
    +    initCategoryNumber();
    +    initRGBYUVTable();
    +
    +    setQuality(opt_quality);
    +  }
    +
    +  init();
    +
    +};
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum.js b/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum.js
    new file mode 100644
    index 0000000000..127f509f87
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum.js
    @@ -0,0 +1,712 @@
    +//   Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +
    +/**
    + * @fileoverview A generator of lorem ipsum text based on the python
    + * implementation at http://code.google.com/p/lorem-ipsum-generator/.
    + *
    + */
    +
    +goog.provide('goog.text.LoremIpsum');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +goog.require('goog.string');
    +goog.require('goog.structs.Map');
    +goog.require('goog.structs.Set');
    +
    +
    +/**
    + * Generates random strings of "lorem ipsum" text, based on the word
    + * distribution of a sample text, using the words in a dictionary.
    + * @constructor
    + */
    +goog.text.LoremIpsum = function() {
    +  this.generateChains_(this.sample_);
    +  this.generateStatistics_(this.sample_);
    +
    +  this.initializeDictionary_(this.dictionary_);
    +};
    +
    +
    +/**
    + * Delimiters that end sentences.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.text.LoremIpsum.DELIMITERS_SENTENCES_ = ['.', '?', '!'];
    +
    +
    +/**
    + * Regular expression for spliting a text into sentences.
    + * @type {RegExp}
    + * @private
    + */
    +goog.text.LoremIpsum.SENTENCE_SPLIT_REGEX_ = /[\.\?\!]/;
    +
    +
    +/**
    + * Delimiters that end words.
    + * @type {Array<string>}
    + * @private
    + */
    +goog.text.LoremIpsum.DELIMITERS_WORDS_ = [',', '.', '?', '!'];
    +
    +
    +/**
    + * Regular expression for spliting text into words.
    + * @type {RegExp}
    + * @private
    + */
    +goog.text.LoremIpsum.WORD_SPLIT_REGEX_ = /\s/;
    +
    +
    +/**
    + * Words that can be used in the generated output.
    + * Maps a word-length to a list of words of that length.
    + * @type {goog.structs.Map}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.words_;
    +
    +
    +/**
    + * Chains of three words that appear in the sample text
    + * Maps a pair of word-lengths to a third word-length and an optional
    + * piece of trailing punctuation (for example, a period, comma, etc.).
    + * @type {goog.structs.Map}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.chains_;
    +
    +
    +/**
    + * Pairs of word-lengths that can appear at the beginning of sentences.
    + * @type {Array}
    + */
    +goog.text.LoremIpsum.prototype.starts_;
    +
    +
    +/**
    + * Averange sentence length in words.
    + * @type {number}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.sentenceMean_;
    +
    +
    +/**
    + * Sigma (sqrt of variance) for the sentence length in words.
    + * @type {number}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.sentenceSigma_;
    +
    +
    +/**
    + * Averange paragraph length in sentences.
    + * @type {number}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.paragraphMean_;
    +
    +
    +/**
    + * Sigma (sqrt of variance) for the paragraph length in sentences.
    + * @type {number}
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.paragraphSigma_;
    +
    +
    +/**
    + * Generates the chains and starts values required for sentence generation.
    + * @param {string} sample The same text.
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.generateChains_ = function(sample) {
    +  var words = goog.text.LoremIpsum.splitWords_(sample);
    +  var wordInfo = goog.array.map(words, goog.text.LoremIpsum.getWordInfo_);
    +
    +  var previous = [0, 0];
    +  var previousKey = previous.join('-');
    +  var chains = new goog.structs.Map();
    +  var starts = [previousKey];
    +  var chainKeys = {};
    +
    +  goog.array.forEach(wordInfo, function(pair) {
    +    var chain = chains.get(previousKey);
    +    if (chain) {
    +      chain.push(pair);
    +    } else {
    +      chain = [pair];
    +      chains.set(previousKey, chain);
    +    }
    +
    +    if (goog.array.contains(
    +        goog.text.LoremIpsum.DELIMITERS_SENTENCES_, pair[1])) {
    +      starts.push(previousKey);
    +    }
    +    chainKeys[previousKey] = previous;
    +    previous = [previous[1], pair[0]];
    +    previousKey = previous.join('-');
    +  });
    +
    +  if (chains.getCount() > 0) {
    +    this.chains_ = chains;
    +    this.starts_ = starts;
    +    this.chainKeys_ = chainKeys;
    +  } else {
    +    throw Error('Could not generate chains from sample text.');
    +  }
    +};
    +
    +
    +/**
    + * Calculates the mean and standard deviation of sentence and paragraph lengths.
    + * @param {string} sample The same text.
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.generateStatistics_ = function(sample) {
    +  this.generateSentenceStatistics_(sample);
    +  this.generateParagraphStatistics_(sample);
    +};
    +
    +
    +/**
    + * Calculates the mean and standard deviation of the lengths of sentences
    + * (in words) in a sample text.
    + * @param {string} sample The same text.
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.generateSentenceStatistics_ = function(sample) {
    +  var sentences = goog.array.filter(
    +      goog.text.LoremIpsum.splitSentences_(sample),
    +      goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
    +
    +  var sentenceLengths = goog.array.map(
    +      goog.array.map(sentences, goog.text.LoremIpsum.splitWords_),
    +      goog.text.LoremIpsum.arrayLength_);
    +  this.sentenceMean_ = goog.math.average.apply(null, sentenceLengths);
    +  this.sentenceSigma_ = goog.math.standardDeviation.apply(
    +      null, sentenceLengths);
    +};
    +
    +
    +/**
    + * Calculates the mean and standard deviation of the lengths of paragraphs
    + * (in sentences) in a sample text.
    + * @param {string} sample The same text.
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.generateParagraphStatistics_ = function(sample) {
    +  var paragraphs = goog.array.filter(
    +      goog.text.LoremIpsum.splitParagraphs_(sample),
    +      goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
    +
    +  var paragraphLengths = goog.array.map(
    +    goog.array.map(paragraphs, goog.text.LoremIpsum.splitSentences_),
    +    goog.text.LoremIpsum.arrayLength_);
    +
    +  this.paragraphMean_ = goog.math.average.apply(null, paragraphLengths);
    +  this.paragraphSigma_ = goog.math.standardDeviation.apply(
    +      null, paragraphLengths);
    +};
    +
    +
    +/**
    + * Sets the generator to use a given selection of words for generating
    + * sentences with.
    + * @param {string} dictionary The dictionary to use.
    + */
    +goog.text.LoremIpsum.prototype.initializeDictionary_ = function(dictionary) {
    +  var dictionaryWords = goog.text.LoremIpsum.splitWords_(dictionary);
    +
    +  var words = new goog.structs.Map();
    +  goog.array.forEach(dictionaryWords, function(word) {
    +    var set = words.get(word.length);
    +    if (!set) {
    +      set = new goog.structs.Set();
    +      words.set(word.length, set);
    +    }
    +    set.add(word);
    +  });
    +
    +  this.words_ = words;
    +};
    +
    +
    +/**
    + * Picks a random starting chain.
    + * @return {Array<string>} The starting key.
    + * @private
    + */
    +goog.text.LoremIpsum.prototype.chooseRandomStart_ = function() {
    +  var key = goog.text.LoremIpsum.randomChoice_(this.starts_);
    +  return this.chainKeys_[key];
    +};
    +
    +
    +/**
    + * Generates a single sentence, of random length.
    + * @param {boolean} opt_startWithLorem Whether to start the setnence with the
    + *     standard "Lorem ipsum..." first sentence.
    + * @return {string} The generated sentence.
    + */
    +goog.text.LoremIpsum.prototype.generateSentence = function(opt_startWithLorem) {
    +  if (this.chains_.getCount() == 0 || this.starts_.length == 0) {
    +    throw Error('No chains created');
    +  }
    +
    +  if (this.words_.getCount() == 0) {
    +    throw Error('No dictionary');
    +  }
    +
    +  // The length of the sentence is a normally distributed random variable.
    +  var sentenceLength = goog.text.LoremIpsum.randomNormal_(
    +      this.sentenceMean_, this.sentenceSigma_)
    +  sentenceLength = Math.max(Math.floor(sentenceLength), 1);
    +
    +  var wordDelimiter = ''; // Defined here in case while loop doesn't run
    +
    +  // Start the sentence with "Lorem ipsum...", if desired
    +  var sentence;
    +  if (opt_startWithLorem) {
    +    var lorem = 'lorem ipsum dolor sit amet, consecteteur adipiscing elit';
    +    sentence = goog.text.LoremIpsum.splitWords_(lorem);
    +    if (sentence.length > sentenceLength) {
    +      sentence.length = sentenceLength;
    +    }
    +    var lastWord = sentence[sentence.length - 1];
    +    var lastChar = lastWord.substring(lastWord.length - 1);
    +    if (goog.array.contains(goog.text.LoremIpsum.DELIMITERS_WORDS_, lastChar)) {
    +      wordDelimiter = lastChar;
    +    }
    +  } else {
    +    sentence = [];
    +  }
    +
    +  var previous = [];
    +  var previousKey = '';
    +  // Generate a sentence from the "chains"
    +  while (sentence.length < sentenceLength) {
    +    // If the current starting point is invalid, choose another randomly
    +    if (!this.chains_.containsKey(previousKey)) {
    +      previous = this.chooseRandomStart_();
    +      previousKey = previous.join('-');
    +    }
    +
    +    // Choose the next "chain" to go to. This determines the next word
    +    // length we'll use, and whether there is e.g. a comma at the end of
    +    // the word.
    +    var chain = /** @type {Array} */ (goog.text.LoremIpsum.randomChoice_(
    +        /** @type {Array} */ (this.chains_.get(previousKey))));
    +    var wordLength = chain[0];
    +
    +    // If the word delimiter contained in the chain is also a sentence
    +    // delimiter, then we don't include it because we don't want the
    +    // sentence to end prematurely (we want the length to match the
    +    // sentence_length value).
    +    //debugger;
    +    if (goog.array.contains(goog.text.LoremIpsum.DELIMITERS_SENTENCES_,
    +        chain[1])) {
    +      wordDelimiter = '';
    +    } else {
    +      wordDelimiter = chain[1];
    +    }
    +
    +    // Choose a word randomly that matches (or closely matches) the
    +    // length we're after.
    +    var closestLength = goog.text.LoremIpsum.chooseClosest(
    +            this.words_.getKeys(), wordLength);
    +    var word = goog.text.LoremIpsum.randomChoice_(
    +        this.words_.get(closestLength).getValues());
    +
    +    sentence.push(word + wordDelimiter);
    +    previous = [previous[1], wordLength];
    +    previousKey = previous.join('-');
    +  }
    +
    +  // Finish the sentence off with capitalisation, a period and
    +  // form it into a string
    +  sentence = sentence.join(' ');
    +  sentence = sentence.slice(0, 1).toUpperCase() + sentence.slice(1);
    +  if (sentence.substring(sentence.length - 1) == wordDelimiter) {
    +    sentence = sentence.slice(0, sentence.length - 1);
    +  }
    +  return sentence + '.';
    +};
    +
    +/**
    + * Generates a single lorem ipsum paragraph, of random length.
    + * @param {boolean} opt_startWithLorem Whether to start the sentence with the
    + *     standard "Lorem ipsum..." first sentence.
    + * @return {string} The generated sentence.
    + */
    +goog.text.LoremIpsum.prototype.generateParagraph = function(
    +    opt_startWithLorem) {
    +  // The length of the paragraph is a normally distributed random variable.
    +  var paragraphLength = goog.text.LoremIpsum.randomNormal_(
    +      this.paragraphMean_, this.paragraphSigma_);
    +  paragraphLength = Math.max(Math.floor(paragraphLength), 1);
    +
    +  // Construct a paragraph from a number of sentences.
    +  var paragraph = []
    +  var startWithLorem = opt_startWithLorem;
    +  while (paragraph.length < paragraphLength) {
    +      var sentence = this.generateSentence(startWithLorem);
    +      paragraph.push(sentence);
    +      startWithLorem = false;
    +  }
    +
    +  // Form the paragraph into a string.
    +  paragraph = paragraph.join(' ')
    +  return paragraph
    +};
    +
    +
    +/**
    + * Splits a piece of text into paragraphs.
    + * @param {string} text The text to split.
    + * @return {Array<string>} An array of paragraphs.
    + * @private
    + */
    +goog.text.LoremIpsum.splitParagraphs_ = function(text) {
    +  return text.split('\n')
    +};
    +
    +
    +/**
    + * Splits a piece of text into sentences.
    + * @param {string} text The text to split.
    + * @return {Array<string>} An array of sentences.
    + * @private
    + */
    +goog.text.LoremIpsum.splitSentences_ = function(text) {
    +  return goog.array.filter(
    +      text.split(goog.text.LoremIpsum.SENTENCE_SPLIT_REGEX_),
    +      goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
    +};
    +
    +
    +/**
    + * Splits a piece of text into words..
    + * @param {string} text The text to split.
    + * @return {Array<string>} An array of words.
    + * @private
    + */
    +goog.text.LoremIpsum.splitWords_ = function(text) {
    +  return goog.array.filter(
    +      text.split(goog.text.LoremIpsum.WORD_SPLIT_REGEX_),
    +      goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
    +};
    +
    +
    +/**
    + * Returns the text is not empty or just whitespace.
    + * @param {string} text The text to check.
    + * @return {boolean} Whether the text is nether empty nor whitespace.
    + * @private
    + */
    +goog.text.LoremIpsum.isNotEmptyOrWhitepace_ = function(text) {
    +  return goog.string.trim(text).length > 0;
    +};
    +
    +
    +/**
    + * Returns the length of an array. Written as a function so it can be used
    + * as a function parameter.
    + * @param {Array} array The array to check.
    + * @return {number} The length of the array.
    + */
    +goog.text.LoremIpsum.arrayLength_ = function(array) {
    +  return array.length;
    +};
    +
    +
    +/**
    + * Find the number in the list of values that is closest to the target.
    + * @param {Array<number>} values The values.
    + * @param {number} target The target value.
    + * @return {number} The closest value.
    + */
    +goog.text.LoremIpsum.chooseClosest = function(values, target) {
    +  var closest = values[0];
    +  goog.array.forEach(values, function(value) {
    +    if (Math.abs(target - value) < Math.abs(target - closest)) {
    +      closest = value;
    +    }
    +  });
    +
    +  return closest;
    +};
    +
    +/**
    + * Gets info about a word used as part of the lorem ipsum algorithm.
    + * @param {string} word The word to check.
    + * @return {Array} A two element array. The first element is the size of the
    + *    word. The second element is the delimter used in the word.
    + * @private
    + */
    +goog.text.LoremIpsum.getWordInfo_ = function(word) {
    +  var ret;
    +  goog.array.some(goog.text.LoremIpsum.DELIMITERS_WORDS_,
    +      function (delimiter) {
    +        if (goog.string.endsWith(word, delimiter)) {
    +          ret = [word.length - delimiter.length, delimiter];
    +          return true;
    +        }
    +        return false;
    +      }
    +  );
    +  return ret || [word.length, ''];
    +};
    +
    +
    +/**
    + * Constant used for {@link #randomNormal_}.
    + * @type {number}
    + * @private
    + */
    +goog.text.LoremIpsum.NV_MAGICCONST_ = 4 * Math.exp(-0.5) / Math.sqrt(2.0);
    +
    +
    +/**
    + * Generates a random number for a normal distribution with the specified
    + * mean and sigma.
    + * @param {number} mu The mean of the distribution.
    + * @param {number} sigma The sigma of the distribution.
    + * @private
    + */
    +goog.text.LoremIpsum.randomNormal_ = function(mu, sigma) {
    +  while (true) {
    +    var u1 = Math.random();
    +    var u2 = 1.0 - Math.random();
    +    var z = goog.text.LoremIpsum.NV_MAGICCONST_ * (u1 - 0.5) / u2;
    +    var zz = z * z / 4.0;
    +    if (zz <= -Math.log(u2)) {
    +      break;
    +    }
    +  }
    +  return mu + z * sigma;
    +};
    +
    +
    +/**
    + * Picks a random element of the array.
    + * @param {Array} array The array to pick from.
    + * @return {*} An element from the array.
    + */
    +goog.text.LoremIpsum.randomChoice_ = function(array) {
    +  return array[goog.math.randomInt(array.length)];
    +};
    +
    +
    +/**
    + * Dictionary of words for lorem ipsum.
    + * @type {string}
    + * @private
    + */
    +goog.text.LoremIpsum.DICT_ =
    +    'a ac accumsan ad adipiscing aenean aliquam aliquet amet ante ' +
    +    'aptent arcu at auctor augue bibendum blandit class commodo ' +
    +    'condimentum congue consectetuer consequat conubia convallis cras ' +
    +    'cubilia cum curabitur curae cursus dapibus diam dictum dictumst ' +
    +    'dignissim dis dolor donec dui duis egestas eget eleifend elementum ' +
    +    'elit eni enim erat eros est et etiam eu euismod facilisi facilisis ' +
    +    'fames faucibus felis fermentum feugiat fringilla fusce gravida ' +
    +    'habitant habitasse hac hendrerit hymenaeos iaculis id imperdiet ' +
    +    'in inceptos integer interdum ipsum justo lacinia lacus laoreet ' +
    +    'lectus leo libero ligula litora lobortis lorem luctus maecenas ' +
    +    'magna magnis malesuada massa mattis mauris metus mi molestie ' +
    +    'mollis montes morbi mus nam nascetur natoque nec neque netus ' +
    +    'nibh nisi nisl non nonummy nostra nulla nullam nunc odio orci ' +
    +    'ornare parturient pede pellentesque penatibus per pharetra ' +
    +    'phasellus placerat platea porta porttitor posuere potenti praesent ' +
    +    'pretium primis proin pulvinar purus quam quis quisque rhoncus ' +
    +    'ridiculus risus rutrum sagittis sapien scelerisque sed sem semper ' +
    +    'senectus sit sociis sociosqu sodales sollicitudin suscipit ' +
    +    'suspendisse taciti tellus tempor tempus tincidunt torquent tortor ' +
    +    'tristique turpis ullamcorper ultrices ultricies urna ut varius ve ' +
    +    'vehicula vel velit venenatis vestibulum vitae vivamus viverra ' +
    +    'volutpat vulputate';
    +
    +
    +/**
    + * A sample to use for generating the distribution of word and sentence lengths
    + * in lorem ipsum.
    + * @type {string}
    + * @private
    + */
    +goog.text.LoremIpsum.SAMPLE_ =
    +    'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean ' +
    +    'commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus ' +
    +    'et magnis dis parturient montes, nascetur ridiculus mus. Donec quam ' +
    +    'felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla ' +
    +    'consequat massa quis enim. Donec pede justo, fringilla vel, aliquet ' +
    +    'nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, ' +
    +    'venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. ' +
    +    'Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean ' +
    +    'vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat ' +
    +    'vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra ' +
    +    'quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius ' +
    +    'laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel ' +
    +    'augue. Curabitur ullamcorper ultricies nisi. Nam eget dui.\n\n' +
    +
    +    'Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem ' +
    +    'quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam ' +
    +    'nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec ' +
    +    'odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis ' +
    +    'faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus ' +
    +    'tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales ' +
    +    'sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit ' +
    +    'cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend ' +
    +    'sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, ' +
    +    'metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis ' +
    +    'hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci ' +
    +    'luctus et ultrices posuere cubilia Curae; In ac dui quis mi ' +
    +    'consectetuer lacinia.\n\n' +
    +
    +    'Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet ' +
    +    'nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ' +
    +    'ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent ' +
    +    'adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy ' +
    +    'metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros ' +
    +    'et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, ' +
    +    'nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit ' +
    +    'risus. Phasellus nec sem in justo pellentesque facilisis. Etiam ' +
    +    'imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus ' +
    +    'non, auctor et, hendrerit quis, nisi.\n\n' +
    +
    +    'Curabitur ligula sapien, tincidunt non, euismod vitae, posuere ' +
    +    'imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed ' +
    +    'cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus ' +
    +    'accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci ' +
    +    'luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis ' +
    +    'porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis ' +
    +    'orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, ' +
    +    'bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede ' +
    +    'sit amet augue. In turpis. Pellentesque posuere. Praesent turpis.\n\n' +
    +
    +    'Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu ' +
    +    'sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales ' +
    +    'nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse ' +
    +    'pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, ' +
    +    'nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in ' +
    +    'faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id ' +
    +    'purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum ' +
    +    'mollis diam. Pellentesque ut neque. Pellentesque habitant morbi ' +
    +    'tristique senectus et netus et malesuada fames ac turpis egestas.\n\n' +
    +
    +    'In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac ' +
    +    'felis quis tortor malesuada pretium. Pellentesque auctor neque nec ' +
    +    'urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean ' +
    +    'viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et ' +
    +    'netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis ' +
    +    'pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna.\n\n' +
    +
    +    'In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare ' +
    +    'lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ' +
    +    'ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. ' +
    +    'Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, ' +
    +    'quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at ' +
    +    'pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo ' +
    +    'quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam ' +
    +    'sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce ' +
    +    'risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis ' +
    +    'vulputate lorem.\n\n' +
    +
    +    'Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, ' +
    +    'dui et placerat feugiat, eros pede varius nisi, condimentum viverra ' +
    +    'felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, ' +
    +    'varius ut, felis. In auctor lobortis lacus. Quisque libero metus, ' +
    +    'condimentum nec, tempor a, commodo mollis, magna. Vestibulum ' +
    +    'ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia ' +
    +    'erat. Praesent blandit laoreet nibh.\n\n' +
    +
    +    'Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, ' +
    +    'neque sit amet convallis pulvinar, justo nulla eleifend augue, ac ' +
    +    'auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. ' +
    +    'Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. ' +
    +    'Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In ' +
    +    'hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis ' +
    +    'mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat ' +
    +    'nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, ' +
    +    'lacus.\n\n' +
    +
    +    'Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, ' +
    +    'dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi ' +
    +    'congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin ' +
    +    'fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit ' +
    +    'amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam ' +
    +    'gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac ' +
    +    'sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus ' +
    +    'blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in ' +
    +    'libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In ' +
    +    'consectetuer turpis ut velit. Nulla sit amet est. Praesent metus ' +
    +    'tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ' +
    +    'ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse ' +
    +    'feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum ' +
    +    'nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac ' +
    +    'massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, ' +
    +    'iaculis quis, molestie non, velit.\n\n' +
    +
    +    'Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. ' +
    +    'Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus ' +
    +    'at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet ' +
    +    'velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. ' +
    +    'Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, ' +
    +    'sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla ' +
    +    'facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere ' +
    +    'iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. ' +
    +    'Curabitur suscipit suscipit tellus.\n\n' +
    +
    +    'Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id ' +
    +    'nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu ' +
    +    'pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante ' +
    +    'odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque ' +
    +    'suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ' +
    +    'ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu ' +
    +    'quis ligula mattis placerat. Duis lobortis massa imperdiet quam. ' +
    +    'Suspendisse potenti.\n\n' +
    +
    +    'Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, ' +
    +    'lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat ' +
    +    'volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam ' +
    +    'eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ' +
    +    'ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta ' +
    +    'dolor. Class aptent taciti sociosqu ad litora torquent per conubia ' +
    +    'nostra, per inceptos hymenaeos.\n\n' +
    +
    +    'Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. ' +
    +    'Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. ' +
    +    'Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, ' +
    +    'elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum ' +
    +    'sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus ' +
    +    'non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. ' +
    +    'Vestibulum eu odio.\n\n' +
    +
    +    'Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. ' +
    +    'Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique ' +
    +    'sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse ' +
    +    'faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, ' +
    +    'vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce ' +
    +    'fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae ' +
    +    'odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. ' +
    +    'Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus ' +
    +    'consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna ' +
    +    'cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit ' +
    +    'quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar ' +
    +    'varius.\n\n';
    +
    +/**
    + * Sample that the generated text is based on .
    + * @type {string}
    + */
    +goog.text.LoremIpsum.prototype.sample_ = goog.text.LoremIpsum.SAMPLE_;
    +
    +
    +/**
    + * Dictionary of words.
    + * @type {string}
    + */
    +goog.text.LoremIpsum.prototype.dictionary_ = goog.text.LoremIpsum.DICT_;
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum_test.html b/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum_test.html
    new file mode 100644
    index 0000000000..edc0a16840
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/loremipsum/text/loremipsum_test.html
    @@ -0,0 +1,60 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +  Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +  Author: dgajda@google.com (Damian Gajda)
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.text.LorumIpsum</title>
    +  <script src="../../../../../closure/goog/base.js"></script>
    +<script>
    +  goog.require('goog.testing.jsunit');
    +  goog.require('goog.testing.PseudoRandom');
    +  goog.require('goog.text.LoremIpsum');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +  var pseudoRandom;
    +
    +  function setUp() {
    +    pseudoRandom = new goog.testing.PseudoRandom(100);
    +    pseudoRandom.install();
    +  }
    +
    +  function tearDown() {
    +    pseudoRandom.uninstall();
    +  }
    +
    +  function testLoremIpsum() {
    +    var generator = new goog.text.LoremIpsum();
    +    assertEquals(
    +        'Lorem ipsum dolor sit amet, consecteteur. Elementum adipiscing ' +
    +        'nisl. Nisi egestas a, taciti enim, scelerisque. Vestibulum ' +
    +        'facilisis, quis vel faucibus a, pellentesque enim, nonummy vivamus ' +
    +        'sodales. Montes. Donec eu, risus luctus ligula ante tempor euismod. ' +
    +        'Porta nostra. Tincidunt in tincidunt eros, sit ante volutpat ' +
    +        'molestie semper parturient. Vestibulum. Nisi elit elit habitant ' +
    +        'torquent. A, pellentesque quis, aliquam a, varius enim, amet est ' +
    +        'hendrerit.',
    +        generator.generateParagraph(true));
    +
    +    assertEquals(
    +        'Non elit adipiscing libero quis rhoncus a, condimentum per, eget ' +
    +        'faucibus. Duis ac consectetuer sodales. Lectus euismod sed, in a ' +
    +        'nostra felis vitae molestie imperdiet. Interdum mi, aptent nonummy ' +
    +        'dui, ve. Quisque auctor ut torquent congue, torquent erat primis ' +
    +        'ornare. Nunc at. Risus leo integer mattis enim quis nisi laoreet ' +
    +        'quisque. Eleifend gravida lacinia varius quam ullamcorper iaculis. ' +
    +        'Vivamus. Suscipit suscipit, libero parturient justo feugiat sapien, ' +
    +        'ad pharetra. Rutrum, viverra potenti tempor nisi in amet dictumst ' +
    +        'vitae. Fermentum lacus venenatis parturient vel risus. Congue ac, ' +
    +        'pharetra diam cum massa curae, vel leo elementum tempus platea, sit ' +
    +        'aliquam ve, ac.',
    +        generator.generateParagraph(false));
    +  }
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred.js b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred.js
    new file mode 100644
    index 0000000000..3151bb897b
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred.js
    @@ -0,0 +1,924 @@
    +// Copyright 2007 Bob Ippolito. All Rights Reserved.
    +// Modifications Copyright 2009 The Closure Library Authors. All Rights
    +// Reserved.
    +
    +/**
    + * @license Portions of this code are from MochiKit, received by
    + * The Closure Authors under the MIT license. All other code is Copyright
    + * 2005-2009 The Closure Authors. All Rights Reserved.
    + */
    +
    +/**
    + * @fileoverview Classes for tracking asynchronous operations and handling the
    + * results. The Deferred object here is patterned after the Deferred object in
    + * the Twisted python networking framework.
    + *
    + * See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
    + *
    + * Based on the Dojo code which in turn is based on the MochiKit code.
    + *
    + * @author arv@google.com (Erik Arvidsson)
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +goog.provide('goog.async.Deferred');
    +goog.provide('goog.async.Deferred.AlreadyCalledError');
    +goog.provide('goog.async.Deferred.CanceledError');
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.debug.Error');
    +
    +
    +
    +/**
    + * A Deferred represents the result of an asynchronous operation. A Deferred
    + * instance has no result when it is created, and is "fired" (given an initial
    + * result) by calling {@code callback} or {@code errback}.
    + *
    + * Once fired, the result is passed through a sequence of callback functions
    + * registered with {@code addCallback} or {@code addErrback}. The functions may
    + * mutate the result before it is passed to the next function in the sequence.
    + *
    + * Callbacks and errbacks may be added at any time, including after the Deferred
    + * has been "fired". If there are no pending actions in the execution sequence
    + * of a fired Deferred, any new callback functions will be called with the last
    + * computed result. Adding a callback function is the only way to access the
    + * result of the Deferred.
    + *
    + * If a Deferred operation is canceled, an optional user-provided cancellation
    + * function is invoked which may perform any special cleanup, followed by firing
    + * the Deferred's errback sequence with a {@code CanceledError}. If the
    + * Deferred has already fired, cancellation is ignored.
    + *
    + * Deferreds may be templated to a specific type they produce using generics
    + * with syntax such as:
    + * <code>
    + *   /** @type {goog.async.Deferred<string>} *&#47;
    + *   var d = new goog.async.Deferred();
    + *   // Compiler can infer that foo is a string.
    + *   d.addCallback(function(foo) {...});
    + *   d.callback('string');  // Checked to be passed a string
    + * </code>
    + * Since deferreds are often used to produce different values across a chain,
    + * the type information is not propagated across chains, but rather only
    + * associated with specifically cast objects.
    + *
    + * @param {Function=} opt_onCancelFunction A function that will be called if the
    + *     Deferred is canceled. If provided, this function runs before the
    + *     Deferred is fired with a {@code CanceledError}.
    + * @param {Object=} opt_defaultScope The default object context to call
    + *     callbacks and errbacks in.
    + * @constructor
    + * @implements {goog.Thenable<VALUE>}
    + * @template VALUE
    + */
    +goog.async.Deferred = function(opt_onCancelFunction, opt_defaultScope) {
    +  /**
    +   * Entries in the sequence are arrays containing a callback, an errback, and
    +   * an optional scope. The callback or errback in an entry may be null.
    +   * @type {!Array<!Array>}
    +   * @private
    +   */
    +  this.sequence_ = [];
    +
    +  /**
    +   * Optional function that will be called if the Deferred is canceled.
    +   * @type {Function|undefined}
    +   * @private
    +   */
    +  this.onCancelFunction_ = opt_onCancelFunction;
    +
    +  /**
    +   * The default scope to execute callbacks and errbacks in.
    +   * @type {Object}
    +   * @private
    +   */
    +  this.defaultScope_ = opt_defaultScope || null;
    +
    +  /**
    +   * Whether the Deferred has been fired.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.fired_ = false;
    +
    +  /**
    +   * Whether the last result in the execution sequence was an error.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.hadError_ = false;
    +
    +  /**
    +   * The current Deferred result, updated as callbacks and errbacks are
    +   * executed.
    +   * @type {*}
    +   * @private
    +   */
    +  this.result_ = undefined;
    +
    +  /**
    +   * Whether the Deferred is blocked waiting on another Deferred to fire. If a
    +   * callback or errback returns a Deferred as a result, the execution sequence
    +   * is blocked until that Deferred result becomes available.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.blocked_ = false;
    +
    +  /**
    +   * Whether this Deferred is blocking execution of another Deferred. If this
    +   * instance was returned as a result in another Deferred's execution
    +   * sequence,that other Deferred becomes blocked until this instance's
    +   * execution sequence completes. No additional callbacks may be added to a
    +   * Deferred once it is blocking another instance.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.blocking_ = false;
    +
    +  /**
    +   * Whether the Deferred has been canceled without having a custom cancel
    +   * function.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.silentlyCanceled_ = false;
    +
    +  /**
    +   * If an error is thrown during Deferred execution with no errback to catch
    +   * it, the error is rethrown after a timeout. Reporting the error after a
    +   * timeout allows execution to continue in the calling context (empty when
    +   * no error is scheduled).
    +   * @type {number}
    +   * @private
    +   */
    +  this.unhandledErrorId_ = 0;
    +
    +  /**
    +   * If this Deferred was created by branch(), this will be the "parent"
    +   * Deferred.
    +   * @type {goog.async.Deferred}
    +   * @private
    +   */
    +  this.parent_ = null;
    +
    +  /**
    +   * The number of Deferred objects that have been branched off this one. This
    +   * will be decremented whenever a branch is fired or canceled.
    +   * @type {number}
    +   * @private
    +   */
    +  this.branches_ = 0;
    +
    +  if (goog.async.Deferred.LONG_STACK_TRACES) {
    +    /**
    +     * Holds the stack trace at time of deferred creation if the JS engine
    +     * provides the Error.captureStackTrace API.
    +     * @private {?string}
    +     */
    +    this.constructorStack_ = null;
    +    if (Error.captureStackTrace) {
    +      var target = { stack: '' };
    +      Error.captureStackTrace(target, goog.async.Deferred);
    +      // Check if Error.captureStackTrace worked. It fails in gjstest.
    +      if (typeof target.stack == 'string') {
    +        // Remove first line and force stringify to prevent memory leak due to
    +        // holding on to actual stack frames.
    +        this.constructorStack_ = target.stack.replace(/^[^\n]*\n/, '');
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @define {boolean} Whether unhandled errors should always get rethrown to the
    + * global scope. Defaults to the value of goog.DEBUG.
    + */
    +goog.define('goog.async.Deferred.STRICT_ERRORS', false);
    +
    +
    +/**
    + * @define {boolean} Whether to attempt to make stack traces long.  Defaults to
    + * the value of goog.DEBUG.
    + */
    +goog.define('goog.async.Deferred.LONG_STACK_TRACES', false);
    +
    +
    +/**
    + * Cancels a Deferred that has not yet been fired, or is blocked on another
    + * deferred operation. If this Deferred is waiting for a blocking Deferred to
    + * fire, the blocking Deferred will also be canceled.
    + *
    + * If this Deferred was created by calling branch() on a parent Deferred with
    + * opt_propagateCancel set to true, the parent may also be canceled. If
    + * opt_deepCancel is set, cancel() will be called on the parent (as well as any
    + * other ancestors if the parent is also a branch). If one or more branches were
    + * created with opt_propagateCancel set to true, the parent will be canceled if
    + * cancel() is called on all of those branches.
    + *
    + * @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even
    + *     if cancel() hasn't been called on some of the parent's branches. Has no
    + *     effect on a branch without opt_propagateCancel set to true.
    + */
    +goog.async.Deferred.prototype.cancel = function(opt_deepCancel) {
    +  if (!this.hasFired()) {
    +    if (this.parent_) {
    +      // Get rid of the parent reference before potentially running the parent's
    +      // canceler function to ensure that this cancellation isn't
    +      // double-counted.
    +      var parent = this.parent_;
    +      delete this.parent_;
    +      if (opt_deepCancel) {
    +        parent.cancel(opt_deepCancel);
    +      } else {
    +        parent.branchCancel_();
    +      }
    +    }
    +
    +    if (this.onCancelFunction_) {
    +      // Call in user-specified scope.
    +      this.onCancelFunction_.call(this.defaultScope_, this);
    +    } else {
    +      this.silentlyCanceled_ = true;
    +    }
    +    if (!this.hasFired()) {
    +      this.errback(new goog.async.Deferred.CanceledError(this));
    +    }
    +  } else if (this.result_ instanceof goog.async.Deferred) {
    +    this.result_.cancel();
    +  }
    +};
    +
    +
    +/**
    + * Handle a single branch being canceled. Once all branches are canceled, this
    + * Deferred will be canceled as well.
    + *
    + * @private
    + */
    +goog.async.Deferred.prototype.branchCancel_ = function() {
    +  this.branches_--;
    +  if (this.branches_ <= 0) {
    +    this.cancel();
    +  }
    +};
    +
    +
    +/**
    + * Called after a blocking Deferred fires. Unblocks this Deferred and resumes
    + * its execution sequence.
    + *
    + * @param {boolean} isSuccess Whether the result is a success or an error.
    + * @param {*} res The result of the blocking Deferred.
    + * @private
    + */
    +goog.async.Deferred.prototype.continue_ = function(isSuccess, res) {
    +  this.blocked_ = false;
    +  this.updateResult_(isSuccess, res);
    +};
    +
    +
    +/**
    + * Updates the current result based on the success or failure of the last action
    + * in the execution sequence.
    + *
    + * @param {boolean} isSuccess Whether the new result is a success or an error.
    + * @param {*} res The result.
    + * @private
    + */
    +goog.async.Deferred.prototype.updateResult_ = function(isSuccess, res) {
    +  this.fired_ = true;
    +  this.result_ = res;
    +  this.hadError_ = !isSuccess;
    +  this.fire_();
    +};
    +
    +
    +/**
    + * Verifies that the Deferred has not yet been fired.
    + *
    + * @private
    + * @throws {Error} If this has already been fired.
    + */
    +goog.async.Deferred.prototype.check_ = function() {
    +  if (this.hasFired()) {
    +    if (!this.silentlyCanceled_) {
    +      throw new goog.async.Deferred.AlreadyCalledError(this);
    +    }
    +    this.silentlyCanceled_ = false;
    +  }
    +};
    +
    +
    +/**
    + * Fire the execution sequence for this Deferred by passing the starting result
    + * to the first registered callback.
    + * @param {VALUE=} opt_result The starting result.
    + */
    +goog.async.Deferred.prototype.callback = function(opt_result) {
    +  this.check_();
    +  this.assertNotDeferred_(opt_result);
    +  this.updateResult_(true /* isSuccess */, opt_result);
    +};
    +
    +
    +/**
    + * Fire the execution sequence for this Deferred by passing the starting error
    + * result to the first registered errback.
    + * @param {*=} opt_result The starting error.
    + */
    +goog.async.Deferred.prototype.errback = function(opt_result) {
    +  this.check_();
    +  this.assertNotDeferred_(opt_result);
    +  this.makeStackTraceLong_(opt_result);
    +  this.updateResult_(false /* isSuccess */, opt_result);
    +};
    +
    +
    +/**
    + * Attempt to make the error's stack trace be long in that it contains the
    + * stack trace from the point where the deferred was created on top of the
    + * current stack trace to give additional context.
    + * @param {*} error
    + * @private
    + */
    +goog.async.Deferred.prototype.makeStackTraceLong_ = function(error) {
    +  if (!goog.async.Deferred.LONG_STACK_TRACES) {
    +    return;
    +  }
    +  if (this.constructorStack_ && goog.isObject(error) && error.stack &&
    +      // Stack looks like it was system generated. See
    +      // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
    +      (/^[^\n]+(\n   [^\n]+)+/).test(error.stack)) {
    +    error.stack = error.stack + '\nDEFERRED OPERATION:\n' +
    +        this.constructorStack_;
    +  }
    +};
    +
    +
    +/**
    + * Asserts that an object is not a Deferred.
    + * @param {*} obj The object to test.
    + * @throws {Error} Throws an exception if the object is a Deferred.
    + * @private
    + */
    +goog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {
    +  goog.asserts.assert(
    +      !(obj instanceof goog.async.Deferred),
    +      'An execution sequence may not be initiated with a blocking Deferred.');
    +};
    +
    +
    +/**
    + * Register a callback function to be called with a successful result. If no
    + * value is returned by the callback function, the result value is unchanged. If
    + * a new value is returned, it becomes the Deferred result and will be passed to
    + * the next callback in the execution sequence.
    + *
    + * If the function throws an error, the error becomes the new result and will be
    + * passed to the next errback in the execution chain.
    + *
    + * If the function returns a Deferred, the execution sequence will be blocked
    + * until that Deferred fires. Its result will be passed to the next callback (or
    + * errback if it is an error result) in this Deferred's execution sequence.
    + *
    + * @param {!function(this:T,VALUE):?} cb The function to be called with a
    + *     successful result.
    + * @param {T=} opt_scope An optional scope to call the callback in.
    + * @return {!goog.async.Deferred} This Deferred.
    + * @template T
    + */
    +goog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {
    +  return this.addCallbacks(cb, null, opt_scope);
    +};
    +
    +
    +/**
    + * Register a callback function to be called with an error result. If no value
    + * is returned by the function, the error result is unchanged. If a new error
    + * value is returned or thrown, that error becomes the Deferred result and will
    + * be passed to the next errback in the execution sequence.
    + *
    + * If the errback function handles the error by returning a non-error value,
    + * that result will be passed to the next normal callback in the sequence.
    + *
    + * If the function returns a Deferred, the execution sequence will be blocked
    + * until that Deferred fires. Its result will be passed to the next callback (or
    + * errback if it is an error result) in this Deferred's execution sequence.
    + *
    + * @param {!function(this:T,?):?} eb The function to be called on an
    + *     unsuccessful result.
    + * @param {T=} opt_scope An optional scope to call the errback in.
    + * @return {!goog.async.Deferred<VALUE>} This Deferred.
    + * @template T
    + */
    +goog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {
    +  return this.addCallbacks(null, eb, opt_scope);
    +};
    +
    +
    +/**
    + * Registers one function as both a callback and errback.
    + *
    + * @param {!function(this:T,?):?} f The function to be called on any result.
    + * @param {T=} opt_scope An optional scope to call the function in.
    + * @return {!goog.async.Deferred} This Deferred.
    + * @template T
    + */
    +goog.async.Deferred.prototype.addBoth = function(f, opt_scope) {
    +  return this.addCallbacks(f, f, opt_scope);
    +};
    +
    +
    +/**
    + * Registers a callback function and an errback function at the same position
    + * in the execution sequence. Only one of these functions will execute,
    + * depending on the error state during the execution sequence.
    + *
    + * NOTE: This is not equivalent to {@code def.addCallback().addErrback()}! If
    + * the callback is invoked, the errback will be skipped, and vice versa.
    + *
    + * @param {(function(this:T,VALUE):?)|null} cb The function to be called on a
    + *     successful result.
    + * @param {(function(this:T,?):?)|null} eb The function to be called on an
    + *     unsuccessful result.
    + * @param {T=} opt_scope An optional scope to call the functions in.
    + * @return {!goog.async.Deferred} This Deferred.
    + * @template T
    + */
    +goog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {
    +  goog.asserts.assert(!this.blocking_, 'Blocking Deferreds can not be re-used');
    +  this.sequence_.push([cb, eb, opt_scope]);
    +  if (this.hasFired()) {
    +    this.fire_();
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Implements {@see goog.Thenable} for seamless integration with
    + * {@see goog.Promise}.
    + * Deferred results are mutable and may represent multiple values over
    + * their lifetime. Calling {@code then} on a Deferred returns a Promise
    + * with the result of the Deferred at that point in its callback chain.
    + * Note that if the Deferred result is never mutated, and only
    + * {@code then} calls are made, the Deferred will behave like a Promise.
    + *
    + * @override
    + */
    +goog.async.Deferred.prototype.then = function(opt_onFulfilled, opt_onRejected,
    +    opt_context) {
    +  var resolve, reject;
    +  var promise = new goog.Promise(function(res, rej) {
    +    // Copying resolvers to outer scope, so that they are available when the
    +    // deferred callback fires (which may be synchronous).
    +    resolve = res;
    +    reject = rej;
    +  });
    +  this.addCallbacks(resolve, function(reason) {
    +    if (reason instanceof goog.async.Deferred.CanceledError) {
    +      promise.cancel();
    +    } else {
    +      reject(reason);
    +    }
    +  });
    +  return promise.then(opt_onFulfilled, opt_onRejected, opt_context);
    +};
    +goog.Thenable.addImplementation(goog.async.Deferred);
    +
    +
    +/**
    + * Links another Deferred to the end of this Deferred's execution sequence. The
    + * result of this execution sequence will be passed as the starting result for
    + * the chained Deferred, invoking either its first callback or errback.
    + *
    + * @param {!goog.async.Deferred} otherDeferred The Deferred to chain.
    + * @return {!goog.async.Deferred} This Deferred.
    + */
    +goog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {
    +  this.addCallbacks(
    +      otherDeferred.callback, otherDeferred.errback, otherDeferred);
    +  return this;
    +};
    +
    +
    +/**
    + * Makes this Deferred wait for another Deferred's execution sequence to
    + * complete before continuing.
    + *
    + * This is equivalent to adding a callback that returns {@code otherDeferred},
    + * but doesn't prevent additional callbacks from being added to
    + * {@code otherDeferred}.
    + *
    + * @param {!goog.async.Deferred|!goog.Thenable} otherDeferred The Deferred
    + *     to wait for.
    + * @return {!goog.async.Deferred} This Deferred.
    + */
    +goog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {
    +  if (!(otherDeferred instanceof goog.async.Deferred)) {
    +    // The Thenable case.
    +    return this.addCallback(function() {
    +      return otherDeferred;
    +    });
    +  }
    +  return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));
    +};
    +
    +
    +/**
    + * Creates a branch off this Deferred's execution sequence, and returns it as a
    + * new Deferred. The branched Deferred's starting result will be shared with the
    + * parent at the point of the branch, even if further callbacks are added to the
    + * parent.
    + *
    + * All branches at the same stage in the execution sequence will receive the
    + * same starting value.
    + *
    + * @param {boolean=} opt_propagateCancel If cancel() is called on every child
    + *     branch created with opt_propagateCancel, the parent will be canceled as
    + *     well.
    + * @return {!goog.async.Deferred<VALUE>} A Deferred that will be started with
    + *     the computed result from this stage in the execution sequence.
    + */
    +goog.async.Deferred.prototype.branch = function(opt_propagateCancel) {
    +  var d = new goog.async.Deferred();
    +  this.chainDeferred(d);
    +  if (opt_propagateCancel) {
    +    d.parent_ = this;
    +    this.branches_++;
    +  }
    +  return d;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the execution sequence has been started on this
    + *     Deferred by invoking {@code callback} or {@code errback}.
    + */
    +goog.async.Deferred.prototype.hasFired = function() {
    +  return this.fired_;
    +};
    +
    +
    +/**
    + * @param {*} res The latest result in the execution sequence.
    + * @return {boolean} Whether the current result is an error that should cause
    + *     the next errback to fire. May be overridden by subclasses to handle
    + *     special error types.
    + * @protected
    + */
    +goog.async.Deferred.prototype.isError = function(res) {
    +  return res instanceof Error;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether an errback exists in the remaining sequence.
    + * @private
    + */
    +goog.async.Deferred.prototype.hasErrback_ = function() {
    +  return goog.array.some(this.sequence_, function(sequenceRow) {
    +    // The errback is the second element in the array.
    +    return goog.isFunction(sequenceRow[1]);
    +  });
    +};
    +
    +
    +/**
    + * Exhausts the execution sequence while a result is available. The result may
    + * be modified by callbacks or errbacks, and execution will block if the
    + * returned result is an incomplete Deferred.
    + *
    + * @private
    + */
    +goog.async.Deferred.prototype.fire_ = function() {
    +  if (this.unhandledErrorId_ && this.hasFired() && this.hasErrback_()) {
    +    // It is possible to add errbacks after the Deferred has fired. If a new
    +    // errback is added immediately after the Deferred encountered an unhandled
    +    // error, but before that error is rethrown, the error is unscheduled.
    +    goog.async.Deferred.unscheduleError_(this.unhandledErrorId_);
    +    this.unhandledErrorId_ = 0;
    +  }
    +
    +  if (this.parent_) {
    +    this.parent_.branches_--;
    +    delete this.parent_;
    +  }
    +
    +  var res = this.result_;
    +  var unhandledException = false;
    +  var isNewlyBlocked = false;
    +
    +  while (this.sequence_.length && !this.blocked_) {
    +    var sequenceEntry = this.sequence_.shift();
    +
    +    var callback = sequenceEntry[0];
    +    var errback = sequenceEntry[1];
    +    var scope = sequenceEntry[2];
    +
    +    var f = this.hadError_ ? errback : callback;
    +    if (f) {
    +      /** @preserveTry */
    +      try {
    +        var ret = f.call(scope || this.defaultScope_, res);
    +
    +        // If no result, then use previous result.
    +        if (goog.isDef(ret)) {
    +          // Bubble up the error as long as the return value hasn't changed.
    +          this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));
    +          this.result_ = res = ret;
    +        }
    +
    +        if (goog.Thenable.isImplementedBy(res)) {
    +          isNewlyBlocked = true;
    +          this.blocked_ = true;
    +        }
    +
    +      } catch (ex) {
    +        res = ex;
    +        this.hadError_ = true;
    +        this.makeStackTraceLong_(res);
    +
    +        if (!this.hasErrback_()) {
    +          // If an error is thrown with no additional errbacks in the queue,
    +          // prepare to rethrow the error.
    +          unhandledException = true;
    +        }
    +      }
    +    }
    +  }
    +
    +  this.result_ = res;
    +
    +  if (isNewlyBlocked) {
    +    var onCallback = goog.bind(this.continue_, this, true /* isSuccess */);
    +    var onErrback = goog.bind(this.continue_, this, false /* isSuccess */);
    +
    +    if (res instanceof goog.async.Deferred) {
    +      res.addCallbacks(onCallback, onErrback);
    +      res.blocking_ = true;
    +    } else {
    +      res.then(onCallback, onErrback);
    +    }
    +  } else if (goog.async.Deferred.STRICT_ERRORS && this.isError(res) &&
    +      !(res instanceof goog.async.Deferred.CanceledError)) {
    +    this.hadError_ = true;
    +    unhandledException = true;
    +  }
    +
    +  if (unhandledException) {
    +    // Rethrow the unhandled error after a timeout. Execution will continue, but
    +    // the error will be seen by global handlers and the user. The throw will
    +    // be canceled if another errback is appended before the timeout executes.
    +    // The error's original stack trace is preserved where available.
    +    this.unhandledErrorId_ = goog.async.Deferred.scheduleError_(res);
    +  }
    +};
    +
    +
    +/**
    + * Creates a Deferred that has an initial result.
    + *
    + * @param {*=} opt_result The result.
    + * @return {!goog.async.Deferred} The new Deferred.
    + */
    +goog.async.Deferred.succeed = function(opt_result) {
    +  var d = new goog.async.Deferred();
    +  d.callback(opt_result);
    +  return d;
    +};
    +
    +
    +/**
    + * Creates a Deferred that fires when the given promise resolves.
    + * Use only during migration to Promises.
    + *
    + * @param {!goog.Promise<T>} promise
    + * @return {!goog.async.Deferred<T>} The new Deferred.
    + * @template T
    + */
    +goog.async.Deferred.fromPromise = function(promise) {
    +  var d = new goog.async.Deferred();
    +  d.callback();
    +  d.addCallback(function() {
    +    return promise;
    +  });
    +  return d;
    +};
    +
    +
    +/**
    + * Creates a Deferred that has an initial error result.
    + *
    + * @param {*} res The error result.
    + * @return {!goog.async.Deferred} The new Deferred.
    + */
    +goog.async.Deferred.fail = function(res) {
    +  var d = new goog.async.Deferred();
    +  d.errback(res);
    +  return d;
    +};
    +
    +
    +/**
    + * Creates a Deferred that has already been canceled.
    + *
    + * @return {!goog.async.Deferred} The new Deferred.
    + */
    +goog.async.Deferred.canceled = function() {
    +  var d = new goog.async.Deferred();
    +  d.cancel();
    +  return d;
    +};
    +
    +
    +/**
    + * Normalizes values that may or may not be Deferreds.
    + *
    + * If the input value is a Deferred, the Deferred is branched (so the original
    + * execution sequence is not modified) and the input callback added to the new
    + * branch. The branch is returned to the caller.
    + *
    + * If the input value is not a Deferred, the callback will be executed
    + * immediately and an already firing Deferred will be returned to the caller.
    + *
    + * In the following (contrived) example, if <code>isImmediate</code> is true
    + * then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.
    + *
    + * <pre>
    + * var value;
    + * if (isImmediate) {
    + *   value = 3;
    + * } else {
    + *   value = new goog.async.Deferred();
    + *   setTimeout(function() { value.callback(6); }, 2000);
    + * }
    + *
    + * var d = goog.async.Deferred.when(value, alert);
    + * </pre>
    + *
    + * @param {*} value Deferred or normal value to pass to the callback.
    + * @param {!function(this:T, ?):?} callback The callback to execute.
    + * @param {T=} opt_scope An optional scope to call the callback in.
    + * @return {!goog.async.Deferred} A new Deferred that will call the input
    + *     callback with the input value.
    + * @template T
    + */
    +goog.async.Deferred.when = function(value, callback, opt_scope) {
    +  if (value instanceof goog.async.Deferred) {
    +    return value.branch(true).addCallback(callback, opt_scope);
    +  } else {
    +    return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);
    +  }
    +};
    +
    +
    +
    +/**
    + * An error sub class that is used when a Deferred has already been called.
    + * @param {!goog.async.Deferred} deferred The Deferred.
    + *
    + * @constructor
    + * @extends {goog.debug.Error}
    + */
    +goog.async.Deferred.AlreadyCalledError = function(deferred) {
    +  goog.debug.Error.call(this);
    +
    +  /**
    +   * The Deferred that raised this error.
    +   * @type {goog.async.Deferred}
    +   */
    +  this.deferred = deferred;
    +};
    +goog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);
    +
    +
    +/** @override */
    +goog.async.Deferred.AlreadyCalledError.prototype.message =
    +    'Deferred has already fired';
    +
    +
    +/** @override */
    +goog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';
    +
    +
    +
    +/**
    + * An error sub class that is used when a Deferred is canceled.
    + *
    + * @param {!goog.async.Deferred} deferred The Deferred object.
    + * @constructor
    + * @extends {goog.debug.Error}
    + */
    +goog.async.Deferred.CanceledError = function(deferred) {
    +  goog.debug.Error.call(this);
    +
    +  /**
    +   * The Deferred that raised this error.
    +   * @type {goog.async.Deferred}
    +   */
    +  this.deferred = deferred;
    +};
    +goog.inherits(goog.async.Deferred.CanceledError, goog.debug.Error);
    +
    +
    +/** @override */
    +goog.async.Deferred.CanceledError.prototype.message = 'Deferred was canceled';
    +
    +
    +/** @override */
    +goog.async.Deferred.CanceledError.prototype.name = 'CanceledError';
    +
    +
    +
    +/**
    + * Wrapper around errors that are scheduled to be thrown by failing deferreds
    + * after a timeout.
    + *
    + * @param {*} error Error from a failing deferred.
    + * @constructor
    + * @final
    + * @private
    + * @struct
    + */
    +goog.async.Deferred.Error_ = function(error) {
    +  /** @const @private {number} */
    +  this.id_ = goog.global.setTimeout(goog.bind(this.throwError, this), 0);
    +
    +  /** @const @private {*} */
    +  this.error_ = error;
    +};
    +
    +
    +/**
    + * Actually throws the error and removes it from the list of pending
    + * deferred errors.
    + */
    +goog.async.Deferred.Error_.prototype.throwError = function() {
    +  goog.asserts.assert(goog.async.Deferred.errorMap_[this.id_],
    +      'Cannot throw an error that is not scheduled.');
    +  delete goog.async.Deferred.errorMap_[this.id_];
    +  throw this.error_;
    +};
    +
    +
    +/**
    + * Resets the error throw timer.
    + */
    +goog.async.Deferred.Error_.prototype.resetTimer = function() {
    +  goog.global.clearTimeout(this.id_);
    +};
    +
    +
    +/**
    + * Map of unhandled errors scheduled to be rethrown in a future timestep.
    + * @private {!Object<number|string, goog.async.Deferred.Error_>}
    + */
    +goog.async.Deferred.errorMap_ = {};
    +
    +
    +/**
    + * Schedules an error to be thrown after a delay.
    + * @param {*} error Error from a failing deferred.
    + * @return {number} Id of the error.
    + * @private
    + */
    +goog.async.Deferred.scheduleError_ = function(error) {
    +  var deferredError = new goog.async.Deferred.Error_(error);
    +  goog.async.Deferred.errorMap_[deferredError.id_] = deferredError;
    +  return deferredError.id_;
    +};
    +
    +
    +/**
    + * Unschedules an error from being thrown.
    + * @param {number} id Id of the deferred error to unschedule.
    + * @private
    + */
    +goog.async.Deferred.unscheduleError_ = function(id) {
    +  var error = goog.async.Deferred.errorMap_[id];
    +  if (error) {
    +    error.resetTimer();
    +    delete goog.async.Deferred.errorMap_[id];
    +  }
    +};
    +
    +
    +/**
    + * Asserts that there are no pending deferred errors. If there are any
    + * scheduled errors, one will be thrown immediately to make this function fail.
    + */
    +goog.async.Deferred.assertNoErrors = function() {
    +  var map = goog.async.Deferred.errorMap_;
    +  for (var key in map) {
    +    var error = map[key];
    +    error.resetTimer();
    +    error.throwError();
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_async_test.html b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_async_test.html
    new file mode 100644
    index 0000000000..a77c6d1c2a
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_async_test.html
    @@ -0,0 +1,145 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +  Copyright 2013 The Closure Library Authors. All Rights Reserved.
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.async.Deferred</title>
    +<script src="../../../../../closure/goog/base.js"></script>
    +<script>
    +
    +goog.require('goog.async.Deferred');
    +goog.require('goog.testing.AsyncTestCase');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.jsunit');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
    +var realSetTimeout = window.setTimeout;
    +var mockClock = new goog.testing.MockClock();
    +
    +function setUp() {
    +  mockClock.install();
    +  goog.async.Deferred.LONG_STACK_TRACES = true;
    +}
    +
    +function tearDown() {
    +  // Advance the mockClock to fire any unhandled exception timeouts.
    +  mockClock.tick();
    +  mockClock.uninstall();
    +}
    +
    +function testErrorStack() {
    +  if (!Error.captureStackTrace) {
    +    return;
    +  }
    +  var d;
    +  // Get the deferred from somewhere deep in the callstack.
    +  (function immediate() {
    +    (function immediate2() {
    +      d = new goog.async.Deferred();
    +      d.addCallback(function actuallyThrows() {
    +        throw new Error('Foo');
    +      });
    +    })();
    +  })();
    +  d.addCallback(function actuallyThrows() {
    +    throw new Error('Foo');
    +  });
    +  asyncTestCase.waitForAsync('Wait for timeout');
    +  realSetTimeout(function willThrow() {
    +    var error = assertThrows(function callbackCaller() {
    +      d.callback();
    +      mockClock.tick();
    +    });
    +    assertContains('Foo', error.stack);
    +    assertContains('testErrorStack', error.stack);
    +    assertContains('callbackCaller', error.stack);
    +    assertContains('willThrow', error.stack);
    +    assertContains('actuallyThrows', error.stack);
    +    assertContains('DEFERRED OPERATION', error.stack);
    +    assertContains('immediate', error.stack);
    +    assertContains('immediate2', error.stack);
    +
    +    asyncTestCase.continueTesting();
    +  }, 0);
    +}
    +
    +function testErrorStack_forErrback() {
    +  if (!Error.captureStackTrace) {
    +    return;
    +  }
    +  var d = new goog.async.Deferred();
    +  asyncTestCase.waitForAsync('Wait for timeout');
    +  realSetTimeout(function willThrow() {
    +    d.errback(new Error('Foo'));
    +    asyncTestCase.continueTesting();
    +  }, 0);
    +
    +  d.addErrback(function(error) {
    +    assertContains('Foo', error.stack);
    +    assertContains('testErrorStack_forErrback', error.stack);
    +    assertContains('willThrow', error.stack);
    +    assertContains('DEFERRED OPERATION', error.stack);
    +  });
    +}
    +
    +function testErrorStack_nested() {
    +  if (!Error.captureStackTrace) {
    +    return;
    +  }
    +  var d = new goog.async.Deferred();
    +  d.addErrback(function(error) {
    +    assertContains('Foo', error.stack);
    +    assertContains('testErrorStack_nested', error.stack);
    +    assertContains('async1', error.stack);
    +    assertContains('async2', error.stack);
    +    assertContains('immediate', error.stack);
    +    assertContains('DEFERRED OPERATION', error.stack);
    +  });
    +  asyncTestCase.waitForAsync('Wait for timeout');
    +  realSetTimeout(function async1() {
    +    var nested = new goog.async.Deferred();
    +    nested.addErrback(function nestedErrback(error) {
    +      d.errback(error);
    +      mockClock.tick();
    +    });
    +    realSetTimeout(function async2() {
    +      (function immediate() {
    +        nested.errback(new Error('Foo'));
    +        mockClock.tick();
    +      })();
    +
    +      asyncTestCase.continueTesting();
    +    });
    +  }, 0);
    +}
    +
    +function testErrorStack_doesNotTouchCustomStack() {
    +  if (!Error.captureStackTrace) {
    +    return;
    +  }
    +  var d = new goog.async.Deferred();
    +  d.addCallback(function actuallyThrows() {
    +    var e = new Error('Foo');
    +    e.stack = 'STACK';
    +    throw e;
    +  });
    +  asyncTestCase.waitForAsync('Wait for timeout');
    +  realSetTimeout(function willThrow() {
    +    var error = assertThrows(function callbackCaller() {
    +      d.callback();
    +      mockClock.tick();
    +    });
    +    assertContains('STACK', error.stack);
    +    asyncTestCase.continueTesting();
    +  }, 0);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_test.html b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_test.html
    new file mode 100644
    index 0000000000..05e63615bc
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferred_test.html
    @@ -0,0 +1,1070 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +  Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +  Author: arv@google.com (Erik Arvidsson)
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.async.Deferred</title>
    +<script src="../../../../../closure/goog/base.js"></script>
    +<script>
    +
    +goog.require('goog.Promise');
    +goog.require('goog.Thenable');
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.string');
    +goog.require('goog.testing.MockClock');
    +goog.require('goog.testing.PropertyReplacer');
    +goog.require('goog.testing.jsunit');
    +
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var Deferred = goog.async.Deferred;
    +var AlreadyCalledError = Deferred.AlreadyCalledError;
    +var CanceledError = Deferred.CanceledError;
    +
    +// Unhandled errors may be sent to the browser on a timeout.
    +var mockClock = new goog.testing.MockClock();
    +var stubs = new goog.testing.PropertyReplacer();
    +
    +function setUp() {
    +  mockClock.install();
    +}
    +
    +function tearDown() {
    +  // Advance the mockClock to fire any unhandled exception timeouts.
    +  mockClock.tick();
    +  mockClock.uninstall();
    +  stubs.reset();
    +}
    +
    +function assertEqualsCallback(msg, expected) {
    +  return function(res) {
    +    assertEquals(msg, expected, res);
    +    // Since the assertion is an exception that will be caught inside the
    +    // Deferred object, we must advance the clock to see if it has failed.
    +    mockClock.tick();
    +    return res;
    +  };
    +}
    +
    +function increment(res) {
    +  return res + 1;
    +}
    +
    +function throwStuff(res) {
    +  throw res;
    +}
    +
    +function catchStuff(res) {
    +  return res;
    +}
    +
    +function returnError(res) {
    +  return Error(res);
    +}
    +
    +function neverHappen(res) {
    +  fail('This should not happen');
    +}
    +
    +function testNormal() {
    +  var d = new Deferred();
    +  d.addCallback(assertEqualsCallback('pre-deferred callback', 1));
    +  d.callback(1);
    +  d.addCallback(increment);
    +  d.addCallback(assertEqualsCallback('post-deferred callback', 2));
    +  d.addCallback(throwStuff);
    +  d.addCallback(neverHappen);
    +  d.addErrback(catchStuff);
    +  d.addCallback(assertEqualsCallback('throw -> err, catch -> success', 2));
    +  d.addCallback(returnError);
    +  d.addCallback(neverHappen);
    +  d.addErrback(catchStuff);
    +  d.addCallback(assertEqualsCallback('return -> err, catch -> succcess', 2));
    +}
    +
    +function testCancel() {
    +  var count = 0;
    +  function canceled(d) {
    +    count++;
    +  }
    +
    +  function canceledError(res) {
    +    assertTrue(res instanceof CanceledError);
    +  }
    +
    +  var d = new Deferred(canceled);
    +  d.addCallback(neverHappen);
    +  d.addErrback(canceledError);
    +  d.cancel();
    +
    +  assertEquals(1, count);
    +}
    +
    +function testSucceedFail() {
    +  var count = 0;
    +
    +  var d = Deferred.succeed(1).addCallback(assertEqualsCallback('succeed', 1));
    +
    +  // default error
    +  d = Deferred.fail().addCallback(neverHappen);
    +  d = d.addErrback(function(res) {
    +    count++;
    +    return res;
    +  });
    +
    +  // default wrapped error
    +  d = Deferred.fail('web taco').addCallback(neverHappen).addErrback(catchStuff);
    +  d = d.addCallback(assertEqualsCallback('wrapped fail', 'web taco'));
    +
    +  // default unwrapped error
    +  d = Deferred.fail(Error('ugh')).addCallback(neverHappen).addErrback(
    +      catchStuff);
    +  d = d.addCallback(assertEqualsCallback('unwrapped fail', 'ugh'));
    +
    +  assertEquals(1, count);
    +}
    +
    +function testDeferredDependencies() {
    +  function deferredIncrement(res) {
    +    var rval = Deferred.succeed(res);
    +    rval.addCallback(increment);
    +    return rval;
    +  }
    +
    +  var d = Deferred.succeed(1).addCallback(deferredIncrement);
    +  d = d.addCallback(assertEqualsCallback('dependent deferred succeed', 2));
    +
    +  function deferredFailure(res) {
    +    return Deferred.fail(res);
    +  }
    +
    +  d = Deferred.succeed('ugh').addCallback(deferredFailure).addErrback(
    +      catchStuff);
    +  d = d.addCallback(assertEqualsCallback('dependent deferred fail', 'ugh'));
    +}
    +
    +// Test double-calling, double-failing, etc.
    +function testDoubleCalling() {
    +  var ex = assertThrows(function() {
    +    Deferred.succeed(1).callback(2);
    +    neverHappen();
    +  });
    +  assertTrue('double call', ex instanceof AlreadyCalledError);
    +}
    +
    +function testDoubleCalling2() {
    +  var ex = assertThrows(function() {
    +    Deferred.fail(1).errback(2);
    +    neverHappen();
    +  });
    +  assertTrue('double-fail', ex instanceof AlreadyCalledError);
    +}
    +
    +function testDoubleCalling3() {
    +  var ex = assertThrows(function() {
    +    var d = Deferred.succeed(1);
    +    d.cancel();
    +    d = d.callback(2);
    +    assertTrue('swallowed one callback, no canceler', true);
    +    d.callback(3);
    +    neverHappen();
    +  });
    +  assertTrue('swallow cancel', ex instanceof AlreadyCalledError);
    +}
    +
    +function testDoubleCalling4() {
    +  var count = 0;
    +  function canceled(d) {
    +    count++;
    +  }
    +
    +  var ex = assertThrows(function() {
    +    var d = new Deferred(canceled);
    +    d.cancel();
    +    d = d.callback(1);
    +  });
    +
    +  assertTrue('non-swallowed cancel', ex instanceof AlreadyCalledError);
    +  assertEquals(1, count);
    +}
    +
    +// Test incorrect Deferred usage
    +function testIncorrectUsage() {
    +  var d = new Deferred();
    +
    +  var ex = assertThrows(function() {
    +    d.callback(new Deferred());
    +    neverHappen();
    +  });
    +  assertTrue('deferred not allowed for callback', ex instanceof Error);
    +}
    +
    +function testIncorrectUsage2() {
    +  var d = new Deferred();
    +
    +  var ex = assertThrows(function() {
    +    d.errback(new Deferred());
    +    neverHappen();
    +  });
    +  assertTrue('deferred not allowed for errback', ex instanceof Error);
    +}
    +
    +function testIncorrectUsage3() {
    +  var d = new Deferred();
    +  (new Deferred()).addCallback(function() {return d;}).callback(1);
    +
    +  var ex = assertThrows(function() {
    +    d.addCallback(function() {});
    +    neverHappen();
    +  });
    +  assertTrue('chained deferred not allowed to be re-used', ex instanceof Error);
    +}
    +
    +function testCallbackScope1() {
    +  var c1 = {}, c2 = {};
    +  var callbackScope = null;
    +  var errbackScope = null;
    +
    +  var d = new Deferred();
    +  d.addCallback(function() {
    +    callbackScope = this;
    +    throw Error('Foo');
    +  }, c1);
    +  d.addErrback(function() {
    +    errbackScope = this;
    +  }, c2);
    +  d.callback();
    +  assertEquals('Incorrect callback scope', c1, callbackScope);
    +  assertEquals('Incorrect errback scope', c2, errbackScope);
    +}
    +
    +function testCallbackScope2() {
    +  var callbackScope = null;
    +  var errbackScope = null;
    +
    +  var d = new Deferred();
    +  d.addCallback(function() {
    +    callbackScope = this;
    +    throw Error('Foo');
    +  });
    +  d.addErrback(function() {
    +    errbackScope = this;
    +  });
    +  d.callback();
    +  assertEquals('Incorrect callback scope', window, callbackScope);
    +  assertEquals('Incorrect errback scope', window, errbackScope);
    +}
    +
    +function testCallbackScope3() {
    +  var c = {};
    +  var callbackScope = null;
    +  var errbackScope = null;
    +
    +  var d = new Deferred(null, c);
    +  d.addCallback(function() {
    +    callbackScope = this;
    +    throw Error('Foo');
    +  });
    +  d.addErrback(function() {
    +    errbackScope = this;
    +  });
    +  d.callback();
    +  assertEquals('Incorrect callback scope', c, callbackScope);
    +  assertEquals('Incorrect errback scope', c, errbackScope);
    +}
    +
    +function testChainedDeferred1() {
    +  var calls = [];
    +
    +  var d2 = new Deferred();
    +  d2.addCallback(function() {calls.push('B1');});
    +  d2.addCallback(function() {calls.push('B2');});
    +
    +  var d1 = new Deferred();
    +  d1.addCallback(function() {calls.push('A1');});
    +  d1.addCallback(function() {calls.push('A2');});
    +  d1.chainDeferred(d2);
    +  d1.addCallback(function() {calls.push('A3');});
    +
    +  d1.callback();
    +  assertEquals('A1,A2,B1,B2,A3', calls.join(','));
    +}
    +
    +function testChainedDeferred2() {
    +  var calls = [];
    +
    +  var d2 = new Deferred();
    +  d2.addCallback(function() {calls.push('B1');});
    +  d2.addErrback(function(err) {calls.push('B2'); throw Error('x');});
    +
    +  var d1 = new Deferred();
    +  d1.addCallback(function(err) {throw Error('foo');});
    +  d1.chainDeferred(d2);
    +  d1.addCallback(function() {calls.push('A1');});
    +  d1.addErrback(function() {calls.push('A2');});
    +
    +  d1.callback();
    +  assertEquals('B2,A2', calls.join(','));
    +
    +  var ex = assertThrows(function() {
    +    mockClock.tick();
    +    neverHappen();
    +  });
    +  assertTrue('Should catch unhandled throw from d2.', ex.message == 'x');
    +}
    +
    +function testUndefinedResultAndCallbackSequence() {
    +  var results = [];
    +  var d = new Deferred();
    +  d.addCallback(function(res) {return 'foo';});
    +  d.addCallback(function(res) {results.push(res); return 'bar';});
    +  d.addCallback(function(res) {results.push(res);});
    +  d.addCallback(function(res) {results.push(res);});
    +  d.callback();
    +  assertEquals('foo,bar,bar', results.join(','));
    +}
    +
    +function testUndefinedResultAndErrbackSequence() {
    +  var results = [];
    +  var d = new Deferred();
    +  d.addCallback(function(res) {throw Error('uh oh');});
    +  d.addErrback(function(res) {results.push('A');});
    +  d.addCallback(function(res) {results.push('B');});
    +  d.addErrback(function(res) {results.push('C');});
    +  d.callback();
    +  assertEquals('A,C', results.join(','));
    +}
    +
    +function testHasFired() {
    +  var d1 = new Deferred();
    +  var d2 = new Deferred();
    +
    +  assertFalse(d1.hasFired());
    +  assertFalse(d2.hasFired());
    +
    +  d1.callback();
    +  d2.errback();
    +  assertTrue(d1.hasFired());
    +  assertTrue(d2.hasFired());
    +}
    +
    +function testUnhandledErrors() {
    +  var d = new Deferred();
    +  d.addCallback(throwStuff);
    +
    +  var ex = assertThrows(function() {
    +    d.callback(123);
    +    mockClock.tick();
    +    neverHappen();
    +  });
    +  assertEquals('Unhandled throws should hit the browser.', 123, ex);
    +
    +  assertNotThrows(
    +      'Errbacks added after a failure should resume.',
    +      function() {
    +        d.addErrback(catchStuff);
    +        mockClock.tick();
    +      });
    +
    +  d.addCallback(assertEqualsCallback('Should recover after throw.', 1));
    +  mockClock.tick();
    +}
    +
    +function testStrictUnhandledErrors() {
    +  stubs.replace(Deferred, 'STRICT_ERRORS', true);
    +  var err = Error('never handled');
    +
    +  // The registered errback exists, but doesn't modify the error value.
    +  var d = Deferred.succeed();
    +  d.addCallback(function(res) { throw err; });
    +  d.addErrback(function(unhandledErr) {});
    +
    +  var caught = assertThrows(
    +      'The error should be rethrown at the next clock tick.',
    +      function() { mockClock.tick(); });
    +  assertEquals(err, caught);
    +}
    +
    +function testStrictHandledErrors() {
    +  stubs.replace(Deferred, 'STRICT_ERRORS', true);
    +
    +  // The registered errback returns a non-error value.
    +  var d = Deferred.succeed();
    +  d.addCallback(function(res) { throw Error('eventually handled'); });
    +  d.addErrback(function(unhandledErr) { return true; });
    +
    +  assertNotThrows(
    +      'The error was handled and should not be rethrown',
    +      function() { mockClock.tick(); });
    +  d.addCallback(function(res) { assertTrue(res); });
    +}
    +
    +function testStrictBlockedErrors() {
    +  stubs.replace(Deferred, 'STRICT_ERRORS', true);
    +
    +  var d1 = Deferred.fail(Error('blocked failure'));
    +  var d2 = new Deferred();
    +
    +  d1.addBoth(function() { return d2; });
    +  assertNotThrows(
    +      'd1 should be blocked until d2 fires.',
    +      function() { mockClock.tick(); });
    +
    +  d2.callback('unblocked');
    +  d1.addCallback(assertEqualsCallback(
    +      'd1 should receive the fired result from d2.', 'unblocked'));
    +}
    +
    +function testStrictCanceledErrors() {
    +  stubs.replace(Deferred, 'STRICT_ERRORS', true);
    +
    +  var d = Deferred.canceled();
    +  assertNotThrows(
    +      'CanceledErrors should not be rethrown to the global scope.',
    +      function() { mockClock.tick(); });
    +}
    +
    +function testSynchronousErrorCanceling() {
    +  var d = new Deferred();
    +  d.addCallback(throwStuff);
    +
    +  assertNotThrows(
    +      'Adding an errback to the end of a failing Deferred should cancel the ' +
    +      'unhandled error timeout.',
    +      function() {
    +        d.callback(1);
    +        d.addErrback(catchStuff);
    +        mockClock.tick();
    +      });
    +
    +  d.addCallback(assertEqualsCallback('Callback should fire', 1));
    +}
    +
    +function testThrowNonError() {
    +  var results = [];
    +
    +  var d = new Deferred();
    +  d.addCallback(function(res) {
    +    throw res;
    +  });
    +  d.addErrback(function(res) {
    +    results.push(res);
    +    return 6;
    +  });
    +  d.addCallback(function(res) {
    +    results.push(res);
    +  });
    +
    +  d.callback(7);
    +  assertArrayEquals(
    +      'Errback should have been called with 7, followed by callback with 6.',
    +      [7, 6], results);
    +}
    +
    +function testThrownErrorWithNoErrbacks() {
    +  var d = new Deferred();
    +  d.addCallback(function() {
    +    throw Error('foo');
    +  });
    +  d.addCallback(goog.nullFunction);
    +
    +  function assertCallback() {
    +    d.callback(1);
    +    mockClock.tick(); // Should cause error because throwing is delayed.
    +  }
    +
    +  assertThrows('A thrown error should be rethrown if there is no ' +
    +               'errback to catch it.', assertCallback);
    +}
    +
    +function testThrownErrorCallbacksDoNotCancel() {
    +  var d = new Deferred();
    +  d.addCallback(function() {
    +    throw Error('foo');
    +  });
    +
    +  function assertCallback() {
    +    d.callback(1);
    +    // Add another callback after the fact.  Note this is not an errback!
    +    d.addCallback(neverHappen);
    +    mockClock.tick(); // Should cause error because throwing is delayed.
    +  }
    +
    +  assertThrows('A thrown error should be rethrown if there is no ' +
    +               'errback to catch it.', assertCallback);
    +}
    +
    +function testAwaitDeferred() {
    +
    +  var results = [];
    +
    +  function fn(x) {
    +    return function() {
    +      results.push(x);
    +    };
    +  }
    +
    +  var d2 = new Deferred();
    +  d2.addCallback(fn('b'));
    +
    +  // d1 -> a -> (wait for d2) -> c
    +  var d1 = new Deferred();
    +  d1.addCallback(fn('a'));
    +  d1.awaitDeferred(d2);
    +  d1.addCallback(fn('c'));
    +
    +  // calls 'a' then yields for d2.
    +  d1.callback(null);
    +
    +  // will get called after d2.
    +  d1.addCallback(fn('d'));
    +
    +  assertEquals('a', results.join(''));
    +
    +  // d3 -> w -> (wait for d2) -> x
    +  var d3 = new Deferred();
    +  d3.addCallback(fn('w'));
    +  d3.awaitDeferred(d2);
    +  d3.addCallback(fn('x'));
    +
    +  // calls 'w', then yields for d2.
    +  d3.callback();
    +
    +
    +  // will get called after d2.
    +  d3.addCallback(fn('y'));
    +
    +  assertEquals('aw', results.join(''));
    +
    +  // d1 calls 'd', d3 calls 'y'
    +  d2.callback(null);
    +
    +  assertEquals('awbcdxy', results.join(''));
    +
    +  // d3 and d2 already called, so 'z' called immediately.
    +  d3.addCallback(fn('z'));
    +
    +  assertEquals('awbcdxyz', results.join(''));
    +}
    +
    +function testAwaitDeferred_withPromise() {
    +
    +  var results = [];
    +
    +  function fn(x) {
    +    return function() {
    +      results.push(x);
    +    };
    +  }
    +
    +  var resolver = new goog.Promise.withResolver();
    +  resolver.promise.then(fn('b'));
    +
    +  // d1 -> a -> (wait for promise) -> c
    +  var d1 = new Deferred();
    +  d1.addCallback(fn('a'));
    +  d1.awaitDeferred(resolver.promise);
    +  d1.addCallback(fn('c'));
    +
    +  // calls 'a' then yields for promise.
    +  d1.callback(1);
    +
    +  // will get called after promise.
    +  d1.addCallback(fn('d'));
    +
    +  assertEquals('a', results.join(''));
    +
    +  // d3 -> w -> (wait for promise) -> x
    +  var d3 = new Deferred();
    +  d3.addCallback(fn('w'));
    +  d3.awaitDeferred(resolver.promise);
    +  d3.addCallback(fn('x'));
    +
    +  // calls 'w', then yields for promise.
    +  d3.callback(2);
    +
    +
    +  // will get called after promise.
    +  d3.addCallback(fn('y'));
    +
    +  assertEquals('aw', results.join(''));
    +
    +  // d1 calls 'd', d3 calls 'y'
    +  resolver.resolve();
    +  mockClock.tick();
    +
    +  assertEquals('awbcdxy', results.join(''));
    +
    +  // d3 and promise already called, so 'z' called immediately.
    +  d3.addCallback(fn('z'));
    +
    +  assertEquals('awbcdxyz', results.join(''));
    +}
    +
    +function testAwaitDeferredWithErrors() {
    +  var results = [];
    +
    +  function fn(x) {
    +    return function(e) {
    +      results.push(x);
    +    };
    +  }
    +
    +  var d2 = new Deferred();
    +  d2.addErrback(fn('a'));
    +
    +  var d1 = new Deferred();
    +  d1.awaitDeferred(d2);
    +  d1.addCallback(fn('x'));
    +  d1.addErrback(fn('b'));
    +  d1.callback(null);
    +
    +  assertEquals('', results.join(''));
    +
    +  d2.addCallback(fn('z'));
    +  d2.addErrback(fn('c'));
    +  d2.errback(null);
    +
    +  // First errback added to d2 prints 'a'.
    +  // Next 'd' was chained, so execute its err backs, printing 'b'.
    +  // Finally 'c' was added last by d2's errback.
    +  assertEquals('abc', results.join(''));
    +}
    +
    +function testNonErrorErrback() {
    +  var results = [];
    +
    +  function fn(x) {
    +    return function(e) {
    +      results.push(x);
    +    };
    +  }
    +
    +  var d = new Deferred();
    +  d.addCallback(fn('a'));
    +  d.addErrback(fn('b'));
    +
    +  d.addCallback(fn('c'));
    +  d.addErrback(fn('d'));
    +
    +  d.errback('foo');
    +
    +  assertEquals('bd', results.join(''));
    +}
    +
    +function testUnequalReturnValueForErrback() {
    +  var results = [];
    +
    +  function fn(x) {
    +    return function(e) {
    +      results.push(x);
    +    };
    +  }
    +
    +  var d = new Deferred();
    +  d.addCallback(fn('a'));
    +  d.addErrback(function() {
    +    results.push('b');
    +    return 'bar';
    +  });
    +
    +  d.addCallback(fn('c'));
    +  d.addErrback(fn('d'));
    +
    +  d.errback('foo');
    +
    +  assertEquals('bc', results.join(''));
    +}
    +
    +function testBranch() {
    +  function fn(x) {
    +    return function(arr) {
    +      return arr.concat(x);
    +    };
    +  }
    +
    +  var d = new Deferred();
    +  d.addCallback(fn(1));
    +  d.addCallback(fn(2));
    +  var d2 = d.branch();
    +  d.addCallback(fn(3));
    +  d2.addCallback(fn(4));
    +
    +  d.callback([]);
    +
    +  assertTrue('both deferreds should have fired', d.hasFired());
    +  assertTrue('both deferreds should have fired', d2.hasFired());
    +  d.addCallback(function(arr) { assertArrayEquals([1, 2, 3], arr); });
    +  d2.addCallback(function(arr) { assertArrayEquals([1, 2, 4], arr); });
    +}
    +
    +function testDiamondBranch() {
    +  function fn(x) {
    +    return function(arr) {
    +      return arr.concat(x);
    +    };
    +  }
    +
    +  var d = new Deferred();
    +  d.addCallback(fn(1));
    +
    +  var d2 = d.branch();
    +  d2.addCallback(fn(2));
    +
    +  // Chain the branch back to the original. There is no good reason to do this
    +  // cever.
    +  d.addCallback(function(ret) {return d2;});
    +  d.callback([]);
    +
    +  // But no reason it shouldn't work!
    +  d.addCallback(function(arr) { assertArrayEquals([1, 2], arr); });
    +}
    +
    +function testRepeatedBranch() {
    +  var d = new Deferred().addCallback(increment);
    +
    +  d.branch().
    +      addCallback(assertEqualsCallback('branch should be after increment', 2)).
    +      addCallback(function(res) {return d.branch();}).
    +      addCallback(assertEqualsCallback('second branch should be the same', 2));
    +  d.callback(1);
    +}
    +
    +function testCancelThroughBranch() {
    +  var wasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var branch1 = d.branch(true);
    +  var branch2 = d.branch(true);
    +
    +  branch1.cancel();
    +  assertFalse(wasCanceled);
    +  branch2.cancel();
    +  assertTrue(wasCanceled);
    +}
    +
    +function testCancelThroughSeveralBranches() {
    +  var wasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var branch = d.branch(true).branch(true).branch(true);
    +
    +  branch.cancel();
    +  assertTrue(wasCanceled);
    +}
    +
    +function testBranchCancelThenCallback() {
    +  var wasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var wasCalled = false;
    +  d.addCallback(function() { wasCalled = true; });
    +  var branch1 = d.branch();
    +  var branch2 = d.branch();
    +
    +  var branch1WasCalled = false;
    +  var branch2WasCalled = false;
    +  branch1.addCallback(function() { branch1WasCalled = true; });
    +  branch2.addCallback(function() { branch2WasCalled = true; });
    +
    +  var branch1HadErrback = false;
    +  var branch2HadErrback = false;
    +  branch1.addErrback(function() { branch1HadErrback = true; });
    +  branch2.addErrback(function() { branch2HadErrback = true; });
    +
    +  branch1.cancel();
    +  assertFalse(wasCanceled);
    +  assertTrue(branch1HadErrback);
    +  assertFalse(branch2HadErrback);
    +
    +  d.callback();
    +  assertTrue(wasCalled);
    +  assertFalse(branch1WasCalled);
    +  assertTrue(branch2WasCalled);
    +}
    +
    +function testDeepCancelOnBranch() {
    +  var wasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var branch1 = d.branch(true);
    +  var branch2 = d.branch(true).branch(true).branch(true);
    +
    +  var branch1HadErrback = false;
    +  var branch2HadErrback = false;
    +  branch1.addErrback(function() { branch1HadErrback = true; });
    +  branch2.addErrback(function() { branch2HadErrback = true; });
    +
    +  branch2.cancel(true /* opt_deepCancel */);
    +  assertTrue(wasCanceled);
    +  assertTrue(branch1HadErrback);
    +  assertTrue(branch2HadErrback);
    +}
    +
    +function testCancelOnRoot() {
    +  var wasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var branch = d.branch(true).branch(true).branch(true);
    +
    +  d.cancel();
    +  assertTrue(wasCanceled);
    +}
    +
    +function testCancelOnLeafBranch() {
    +  var wasCanceled = false;
    +  var branchWasCanceled = false;
    +  var d = new Deferred(function() { wasCanceled = true; });
    +  var branch = d.branch(true).branch(true).branch(true);
    +  branch.addErrback(function() { branchWasCanceled = true; });
    +
    +  branch.cancel();
    +  assertTrue(wasCanceled);
    +  assertTrue(branchWasCanceled);
    +}
    +
    +function testCancelOnIntermediateBranch() {
    +  var rootWasCanceled = false;
    +
    +  var d = new Deferred(function() { rootWasCanceled = true; });
    +  var branch = d.branch(true).branch(true).branch(true);
    +
    +  var deepBranch1 = branch.branch(true);
    +  var deepBranch2 = branch.branch(true);
    +
    +  branch.cancel();
    +  assertTrue(rootWasCanceled);
    +  assertTrue(deepBranch1.hasFired());
    +  assertTrue(deepBranch2.hasFired());
    +}
    +
    +function testCancelWithSomeCompletedBranches() {
    +  var d = new Deferred();
    +  var branch1 = d.branch(true);
    +
    +  var branch1HadCallback = false;
    +  var branch1HadErrback = false;
    +  branch1.
    +      addCallback(function() { branch1HadCallback = true; }).
    +      addErrback(function() { branch1HadErrback = true; });
    +  d.callback(true);
    +
    +  assertTrue(branch1HadCallback);
    +  assertFalse(branch1HadErrback);
    +
    +  var rootHadCallback = false;
    +  var rootHadErrback = false;
    +  // Block the root on a new Deferred indefinitely.
    +  d.
    +      addCallback(function() { rootHadCallback = true; }).
    +      addCallback(function() { return new Deferred(); }).
    +      addErrback(function() { rootHadErrback = true; });
    +  var branch2 = d.branch(true);
    +
    +  assertTrue(rootHadCallback);
    +  assertFalse(rootHadErrback);
    +
    +  branch2.cancel();
    +  assertFalse(branch1HadErrback);
    +  assertTrue('Canceling the last active branch should cancel the parent.',
    +             rootHadErrback);
    +}
    +
    +function testStaticCanceled() {
    +  var callbackCalled = false;
    +  var errbackResult = null;
    +
    +  var d = goog.async.Deferred.canceled();
    +  d.addCallback(function() { callbackCalled = true;} );
    +  d.addErrback(function(err) { errbackResult = err;} );
    +
    +  assertTrue('Errback should have been called with a canceled error',
    +      errbackResult instanceof goog.async.Deferred.CanceledError);
    +  assertFalse('Callback should not have been called', callbackCalled);
    +}
    +
    +function testWhenWithValues() {
    +  var called = false;
    +  Deferred.when(4, function(obj) {
    +    called = true;
    +    assertEquals(4, obj);
    +  });
    +  assertTrue('Fn should have been called', called);
    +}
    +
    +function testWhenWithDeferred() {
    +  var called = false;
    +
    +  var d = new Deferred();
    +  Deferred.when(d, function(obj) {
    +    called = true;
    +    assertEquals(6, obj);
    +  });
    +  assertFalse('Fn should not have been called yet', called);
    +  d.callback(6);
    +  assertTrue('Fn should have been called', called);
    +}
    +
    +function testWhenDoesntAlterOriginalChain() {
    +  var calls = 0;
    +
    +  var d1 = new Deferred();
    +  var d2 = Deferred.when(d1, function(obj) {
    +    calls++;
    +    return obj * 2;
    +  });
    +  d1.addCallback(function(obj) {
    +    assertEquals('Original chain should get original value', 5, obj);
    +    calls++;
    +  });
    +  d2.addCallback(function(obj) {
    +    assertEquals('Branched chain should get modified value', 10, obj);
    +    calls++;
    +  });
    +
    +  d1.callback(5);
    +
    +  assertEquals('There should have been 3 callbacks', 3, calls);
    +}
    +
    +function testAssertNoErrors() {
    +  var d = new Deferred();
    +  d.addCallback(function() {
    +    throw new Error('Foo');
    +  });
    +  d.callback(1);
    +
    +  var ex = assertThrows(function() {
    +    Deferred.assertNoErrors();
    +    neverHappen();
    +  });
    +  assertEquals('Expected to get thrown error', 'Foo', ex.message);
    +
    +  assertNotThrows(
    +      'Calling Deferred.assertNoErrors() a second time with only one ' +
    +          'scheduled error should pass.',
    +      function() {
    +        Deferred.assertNoErrors();
    +      });
    +}
    +
    +function testThen() {
    +  var result;
    +  var result2;
    +  var d = new Deferred();
    +  assertEquals(d.then, d['then']);
    +  d.then(function(r) {
    +    return result = r;
    +  }).then(function (r2) {
    +    result2 = r2;
    +  });
    +  d.callback('done');
    +  assertUndefined(result);
    +  mockClock.tick();
    +  assertEquals('done', result);
    +  assertEquals('done', result2);
    +}
    +
    +function testThen_reject() {
    +  var result, error, error2;
    +  var d = new Deferred();
    +  assertEquals(d.then, d['then']);
    +  d.then(function(r) {
    +    result = r;
    +  }, function(e) {
    +    error = e;
    +  });
    +  d.errback(new Error('boom'));
    +  assertUndefined(result);
    +  mockClock.tick();
    +  assertUndefined(result);
    +  assertEquals('boom', error.message);
    +}
    +
    +function testPromiseAll() {
    +  var d = new Deferred();
    +  var p = new goog.Promise(function(resolve) {
    +    resolve('promise');
    +  });
    +  goog.Promise.all([d, p]).then(function(values) {
    +    assertEquals(2, values.length);
    +    assertEquals('deferred', values[0]);
    +    assertEquals('promise', values[1]);
    +  });
    +  d.callback('deferred');
    +  mockClock.tick();
    +}
    +
    +function testPromiseBlocksDeferred() {
    +  var result;
    +  var d = new Deferred();
    +  var p = new goog.Promise(function(resolve) {
    +    resolve('promise');
    +  });
    +  d.callback();
    +  d.addCallback(function() {
    +    return p;
    +  });
    +  d.addCallback(function(r) {
    +    result = r;
    +  });
    +
    +  assertUndefined(result);
    +  mockClock.tick();
    +  assertEquals('promise', result);
    +}
    +
    +function testFromPromise() {
    +  var result;
    +  var p = new goog.Promise(function(resolve) {
    +    resolve('promise');
    +  });
    +  var d = Deferred.fromPromise(p);
    +  d.addCallback(function(value) {
    +    result = value;
    +  });
    +  assertUndefined(result);
    +  mockClock.tick();
    +  assertEquals('promise', result);
    +}
    +
    +function testPromiseBlocksDeferredAndRejects() {
    +  var result;
    +  var d = new Deferred();
    +  var p = new goog.Promise(function(resolve, reject) {
    +    reject(new Error('error'));
    +  });
    +  d.callback();
    +  d.addCallback(function(r) {
    +    return p;
    +  });
    +  d.addErrback(function(r) {
    +    result = r;
    +  });
    +
    +  assertUndefined(result);
    +  mockClock.tick();
    +  assertEquals('error', result.message);
    +}
    +
    +function testPromiseFromCanceledDeferred() {
    +  var result;
    +  var d = new Deferred();
    +  d.cancel();
    +
    +  var p = d.then(neverHappen, function(reason) {
    +    result = reason;
    +  });
    +
    +  mockClock.tick();
    +  assertTrue(result instanceof goog.Promise.CancellationError);
    +}
    +
    +function testThenableInterface() {
    +  var d = new Deferred();
    +  assertTrue(goog.Thenable.isImplementedBy(d));
    +}
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist.js b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist.js
    new file mode 100644
    index 0000000000..59a1587c10
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist.js
    @@ -0,0 +1,206 @@
    +// Copyright 2005 Bob Ippolito. All Rights Reserved.
    +// Modifications Copyright 2009 The Closure Library Authors.
    +// All Rights Reserved.
    +
    +/**
    + * Portions of this code are from MochiKit, received by The Closure
    + * Library Authors under the MIT license. All other code is Copyright
    + * 2005-2009 The Closure Library Authors. All Rights Reserved.
    + */
    +
    +/**
    + * @fileoverview Class for tracking multiple asynchronous operations and
    + * handling the results. The DeferredList object here is patterned after the
    + * DeferredList object in the Twisted python networking framework.
    + *
    + * Based on the MochiKit code.
    + *
    + * See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
    + *
    + * @author brenneman@google.com (Shawn Brenneman)
    + */
    +
    +goog.provide('goog.async.DeferredList');
    +
    +goog.require('goog.async.Deferred');
    +
    +
    +
    +/**
    + * Constructs an object that waits on the results of multiple asynchronous
    + * operations and marshals the results. It is itself a <code>Deferred</code>,
    + * and may have an execution sequence of callback functions added to it. Each
    + * <code>DeferredList</code> instance is single use and may be fired only once.
    + *
    + * The default behavior of a <code>DeferredList</code> is to wait for a success
    + * or error result from every <code>Deferred</code> in its input list. Once
    + * every result is available, the <code>DeferredList</code>'s execution sequence
    + * is fired with a list of <code>[success, result]</code> array pairs, where
    + * <code>success</code> is a boolean indicating whether <code>result</code> was
    + * the product of a callback or errback. The list's completion criteria and
    + * result list may be modified by setting one or more of the boolean options
    + * documented below.
    + *
    + * <code>Deferred</code> instances passed into a <code>DeferredList</code> are
    + * independent, and may have additional callbacks and errbacks added to their
    + * execution sequences after they are passed as inputs to the list.
    + *
    + * @param {!Array<!goog.async.Deferred>} list An array of deferred results to
    + *     wait for.
    + * @param {boolean=} opt_fireOnOneCallback Whether to stop waiting as soon as
    + *     one input completes successfully. In this case, the
    + *     <code>DeferredList</code>'s callback chain will be called with a two
    + *     element array, <code>[index, result]</code>, where <code>index</code>
    + *     identifies which input <code>Deferred</code> produced the successful
    + *     <code>result</code>.
    + * @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one
    + *     input reports an error. The failing result is passed to the
    + *     <code>DeferredList</code>'s errback sequence.
    + * @param {boolean=} opt_consumeErrors When true, any errors fired by a
    + *     <code>Deferred</code> in the input list will be captured and replaced
    + *     with a succeeding null result. Any callbacks added to the
    + *     <code>Deferred</code> after its use in the <code>DeferredList</code> will
    + *     receive null instead of the error.
    + * @param {Function=} opt_canceler A function that will be called if the
    + *     <code>DeferredList</code> is canceled. @see goog.async.Deferred#cancel
    + * @param {Object=} opt_defaultScope The default scope to invoke callbacks or
    + *     errbacks in.
    + * @constructor
    + * @extends {goog.async.Deferred}
    + */
    +goog.async.DeferredList = function(
    +    list, opt_fireOnOneCallback, opt_fireOnOneErrback, opt_consumeErrors,
    +    opt_canceler, opt_defaultScope) {
    +
    +  goog.async.DeferredList.base(this, 'constructor',
    +      opt_canceler, opt_defaultScope);
    +
    +  /**
    +   * The list of Deferred objects to wait for.
    +   * @const {!Array<!goog.async.Deferred>}
    +   * @private
    +   */
    +  this.list_ = list;
    +
    +  /**
    +   * The stored return values of the Deferred objects.
    +   * @const {!Array}
    +   * @private
    +   */
    +  this.deferredResults_ = [];
    +
    +  /**
    +   * Whether to fire on the first successful callback instead of waiting for
    +   * every Deferred to complete.
    +   * @const {boolean}
    +   * @private
    +   */
    +  this.fireOnOneCallback_ = !!opt_fireOnOneCallback;
    +
    +  /**
    +   * Whether to fire on the first error result received instead of waiting for
    +   * every Deferred to complete.
    +   * @const {boolean}
    +   * @private
    +   */
    +  this.fireOnOneErrback_ = !!opt_fireOnOneErrback;
    +
    +  /**
    +   * Whether to stop error propagation on the input Deferred objects. If the
    +   * DeferredList sees an error from one of the Deferred inputs, the error will
    +   * be captured, and the Deferred will be returned to success state with a null
    +   * return value.
    +   * @const {boolean}
    +   * @private
    +   */
    +  this.consumeErrors_ = !!opt_consumeErrors;
    +
    +  /**
    +   * The number of input deferred objects that have fired.
    +   * @private {number}
    +   */
    +  this.numFinished_ = 0;
    +
    +  for (var i = 0; i < list.length; i++) {
    +    var d = list[i];
    +    d.addCallbacks(goog.bind(this.handleCallback_, this, i, true),
    +                   goog.bind(this.handleCallback_, this, i, false));
    +  }
    +
    +  if (list.length == 0 && !this.fireOnOneCallback_) {
    +    this.callback(this.deferredResults_);
    +  }
    +};
    +goog.inherits(goog.async.DeferredList, goog.async.Deferred);
    +
    +
    +/**
    + * Registers the result from an input deferred callback or errback. The result
    + * is returned and may be passed to additional handlers in the callback chain.
    + *
    + * @param {number} index The index of the firing deferred object in the input
    + *     list.
    + * @param {boolean} success Whether the result is from a callback or errback.
    + * @param {*} result The result of the callback or errback.
    + * @return {*} The result, to be handled by the next handler in the deferred's
    + *     callback chain (if any). If consumeErrors is set, an error result is
    + *     replaced with null.
    + * @private
    + */
    +goog.async.DeferredList.prototype.handleCallback_ = function(
    +    index, success, result) {
    +
    +  this.numFinished_++;
    +  this.deferredResults_[index] = [success, result];
    +
    +  if (!this.hasFired()) {
    +    if (this.fireOnOneCallback_ && success) {
    +      this.callback([index, result]);
    +    } else if (this.fireOnOneErrback_ && !success) {
    +      this.errback(result);
    +    } else if (this.numFinished_ == this.list_.length) {
    +      this.callback(this.deferredResults_);
    +    }
    +  }
    +
    +  if (this.consumeErrors_ && !success) {
    +    result = null;
    +  }
    +
    +  return result;
    +};
    +
    +
    +/** @override */
    +goog.async.DeferredList.prototype.errback = function(res) {
    +  goog.async.DeferredList.base(this, 'errback', res);
    +
    +  // On error, cancel any pending requests.
    +  for (var i = 0; i < this.list_.length; i++) {
    +    this.list_[i].cancel();
    +  }
    +};
    +
    +
    +/**
    + * Creates a <code>DeferredList</code> that gathers results from multiple
    + * <code>Deferred</code> inputs. If all inputs succeed, the callback is fired
    + * with the list of results as a flat array. If any input fails, the list's
    + * errback is fired immediately with the offending error, and all other pending
    + * inputs are canceled.
    + *
    + * @param {!Array<!goog.async.Deferred>} list The list of <code>Deferred</code>
    + *     inputs to wait for.
    + * @return {!goog.async.Deferred} The deferred list of results from the inputs
    + *     if they all succeed, or the error result of the first input to fail.
    + */
    +goog.async.DeferredList.gatherResults = function(list) {
    +  return new goog.async.DeferredList(list, false, true).
    +      addCallback(function(results) {
    +        var output = [];
    +        for (var i = 0; i < results.length; i++) {
    +          output[i] = results[i][1];
    +        }
    +        return output;
    +      });
    +};
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist_test.html b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist_test.html
    new file mode 100644
    index 0000000000..bb8c63f485
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/mochikit/async/deferredlist_test.html
    @@ -0,0 +1,504 @@
    +<!DOCTYPE html>
    +<html>
    +<!--
    +  Copyright 2009 The Closure Library Authors. All Rights Reserved.
    +  Author: brenneman@google.com (Shawn Brenneman)
    +-->
    +<head>
    +<title>Closure Unit Tests - goog.async.DeferredList</title>
    +<script src="../../../../../closure/goog/base.js"></script>
    +<script>
    +goog.require('goog.array');
    +goog.require('goog.async.Deferred');
    +goog.require('goog.async.DeferredList');
    +goog.require('goog.testing.jsunit');
    +</script>
    +</head>
    +<body>
    +<script>
    +
    +var Deferred = goog.async.Deferred;
    +var DeferredList = goog.async.DeferredList;
    +
    +
    +// Re-throw (after a timeout) any errors not handled in an errback.
    +Deferred.STRICT_ERRORS = true;
    +
    +
    +/**
    + * A list of unhandled errors.
    + * @type {Array.<Error>}
    + */
    +var storedErrors = [];
    +
    +
    +/**
    + * Adds a catch-all error handler to deferred objects. Unhandled errors that
    + * reach the catch-all will be rethrown during tearDown.
    + *
    + * @param {...Deferred} var_args A list of deferred objects.
    + */
    +function addCatchAll(var_args) {
    +  for (var i = 0, d; d = arguments[i]; i++) {
    +    d.addErrback(function(res) {
    +      storedErrors.push(res);
    +    });
    +  }
    +}
    +
    +
    +/**
    + * Checks storedErrors for unhandled errors. If found, the error is rethrown.
    + */
    +function checkCatchAll() {
    +  var err = storedErrors.shift();
    +  goog.array.clear(storedErrors);
    +
    +  if (err) {
    +    throw err;
    +  }
    +}
    +
    +
    +function tearDown() {
    +  checkCatchAll();
    +}
    +
    +
    +function neverHappen(res) {
    +  fail('This should not happen');
    +}
    +
    +
    +function testNoInputs() {
    +  var count = 0;
    +  var d = new DeferredList([]);
    +
    +  d.addCallback(function(res) {
    +    assertArrayEquals([], res);
    +    count++;
    +  });
    +  addCatchAll(d);
    +
    +  assertEquals('An empty DeferredList should fire immediately with an empty ' +
    +               'list of results.',
    +               1, count);
    +}
    +
    +
    +function testNoInputsAndFireOnOneCallback() {
    +  var count = 0;
    +  var d = new DeferredList([], true);
    +
    +  d.addCallback(function(res) {
    +    assertArrayEquals([], res);
    +    count++;
    +  });
    +  addCatchAll(d);
    +
    +  assertEquals('An empty DeferredList with opt_fireOnOneCallback set should ' +
    +               'not fire unless callback is invoked explicitly.',
    +               0, count);
    +
    +  d.callback([]);
    +  assertEquals('Calling callback explicitly should still fire.', 1, count);
    +}
    +
    +
    +function testDeferredList() {
    +  var count = 0;
    +  var results;
    +
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +
    +  var dl = new DeferredList([a, b, c]);
    +
    +  dl.addCallback(function(res) {
    +    assertEquals('Expected 3 Deferred results.', 3, res.length);
    +
    +    assertTrue('Deferred a should return success.', res[0][0]);
    +    assertFalse('Deferred b should return failure.', res[1][0]);
    +    assertTrue('Deferred c should return success.', res[2][0]);
    +
    +    assertEquals('Unexpected return value for a.', 'A', res[0][1]);
    +    assertEquals('Unexpected return value for c.', 'C', res[2][1]);
    +
    +    assertEquals('B', res[1][1]);
    +
    +    count++;
    +  });
    +
    +  addCatchAll(dl);
    +
    +  c.callback('C');
    +  assertEquals(0, count);
    +
    +  b.errback('B');
    +  assertEquals(0, count);
    +
    +  a.callback('A');
    +
    +  checkCatchAll();
    +  assertEquals('DeferredList should fire on last call or errback.', 1, count);
    +}
    +
    +
    +function testFireOnFirstCallback() {
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +
    +  var dl = new DeferredList([a, b, c], true);
    +
    +  dl.addCallback(function(res) {
    +    assertEquals('Should be the deferred index in this mode.', 1, res[0]);
    +    assertEquals('B', res[1]);
    +  });
    +  dl.addErrback(neverHappen);
    +
    +  addCatchAll(dl);
    +
    +  a.errback('A');
    +  b.callback('B');
    +
    +  // Shouldn't cause any more callbacks on the DeferredList.
    +  c.callback('C');
    +}
    +
    +
    +function testFireOnFirstErrback() {
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +
    +  var dl = new DeferredList([a, b, c], false, true);
    +
    +  dl.addCallback(neverHappen);
    +  dl.addErrback(function(res) {
    +    assertEquals('A', res);
    +
    +    // Return a non-error value to break out of the errback path.
    +    return null;
    +  });
    +  addCatchAll(dl);
    +
    +  b.callback('B');
    +  a.errback('A');
    +
    +  assertTrue(c.hasFired());
    +  c.addErrback(function(res) {
    +    assertTrue(
    +        'The DeferredList errback should have canceled all pending inputs.',
    +        res instanceof Deferred.CanceledError);
    +    return null;
    +  });
    +  addCatchAll(c);
    +
    +  // Shouldn't cause any more callbacks on the DeferredList.
    +  c.callback('C');
    +}
    +
    +
    +function testNoConsumeErrors() {
    +  var count = 0;
    +
    +  var a = new Deferred();
    +  var dl = new DeferredList([a]);
    +
    +  a.addErrback(function(res) {
    +    count++;
    +    return null;
    +  });
    +
    +  addCatchAll(a, dl);
    +
    +  a.errback('oh noes');
    +  assertEquals(1, count);
    +}
    +
    +
    +function testConsumeErrors() {
    +  var count = 0;
    +
    +  var a = new Deferred();
    +  var dl = new DeferredList([a], false, false, true);
    +
    +  a.addErrback(neverHappen);
    +
    +  addCatchAll(a, dl);
    +
    +  a.errback('oh noes');
    +  assertEquals(0, count);
    +}
    +
    +
    +function testNesting() {
    +
    +  function upperCase(res) {
    +    return res.toUpperCase();
    +  }
    +
    +  // Concatenates a list of callback or errback results into a single string.
    +  function combine(res) {
    +    return goog.array.map(res, function(result) {
    +      return result[1];
    +    }).join('');
    +  }
    +
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +  var d = new Deferred();
    +
    +  a.addCallback(upperCase);
    +  b.addCallback(upperCase);
    +  c.addCallback(upperCase);
    +  d.addCallback(upperCase);
    +
    +  var dl1 = new DeferredList([a, b]);
    +  var dl2 = new DeferredList([c, d]);
    +
    +  dl1.addCallback(combine);
    +  dl2.addCallback(combine);
    +
    +  var dl3 = new DeferredList([dl1, dl2]);
    +  dl3.addCallback(combine);
    +  dl3.addCallback(function(res) {
    +    assertEquals('AbCd', res);
    +  });
    +
    +  addCatchAll(dl1, dl2, dl3);
    +
    +  a.callback('a');
    +  c.callback('c');
    +  b.errback('b');
    +  d.errback('d');
    +}
    +
    +
    +function testGatherResults() {
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +
    +  var dl = DeferredList.gatherResults([a, b, c]);
    +
    +  dl.addCallback(function(res) {
    +    assertArrayEquals(['A', 'B', 'C'], res);
    +  });
    +
    +  addCatchAll(dl);
    +
    +  b.callback('B');
    +  a.callback('A');
    +  c.callback('C');
    +}
    +
    +
    +function testGatherResultsFailure() {
    +  var a = new Deferred();
    +  var b = new Deferred();
    +  var c = new Deferred();
    +
    +  var dl = DeferredList.gatherResults([a, b, c]);
    +
    +  var firedErrback = false;
    +  var firedCallback = false;
    +  dl.addCallback(function() {
    +    firedCallback = true;
    +  });
    +  dl.addErrback(function() {
    +    firedErrback = true;
    +    return null;
    +  });
    +
    +  addCatchAll(dl);
    +
    +  b.callback('B');
    +  a.callback('A');
    +  c.errback();
    +
    +  assertTrue('Errback should be called', firedErrback);
    +  assertFalse('Callback should not be called', firedCallback);
    +}
    +
    +
    +function testGatherResults_cancelCancelsChildren() {
    +  var canceled = [];
    +  var a = new Deferred(function() {
    +    canceled.push('a');
    +  });
    +  var b = new Deferred(function() {
    +    canceled.push('b');
    +  });
    +  var c = new Deferred(function() {
    +    canceled.push('c');
    +  });
    +
    +  var dl = new DeferredList([a, b, c]);
    +
    +  var firedErrback = false;
    +  var firedCallback = false;
    +  dl.addCallback(function() {
    +    firedCallback = true;
    +  });
    +  dl.addErrback(function() {
    +    firedErrback = true;
    +    return null;
    +  });
    +
    +  addCatchAll(dl);
    +
    +  b.callback('b');
    +  dl.cancel();
    +
    +  assertTrue('Errback should be called', firedErrback);
    +  assertFalse('Callback should not be called', firedCallback);
    +  assertArrayEquals(['a', 'c'], canceled);
    +}
    +
    +
    +function testErrorCancelsPendingChildrenWhenFireOnFirstError() {
    +  var canceled = [];
    +  var a = new Deferred(function() {
    +    canceled.push('a');
    +  });
    +  var b = new Deferred(function() {
    +    canceled.push('b');
    +  });
    +  var c = new Deferred(function() {
    +    canceled.push('c');
    +  });
    +
    +  var dl = new DeferredList([a, b, c], false, true);
    +
    +  var firedErrback = false;
    +  var firedCallback = false;
    +  dl.addCallback(function() {
    +    firedCallback = true;
    +  });
    +  dl.addErrback(function() {
    +    firedErrback = true;
    +    return null;
    +  });
    +
    +  addCatchAll(dl);
    +
    +  a.callback('a')
    +  b.errback();
    +
    +  assertTrue('Errback should be called', firedErrback);
    +  assertFalse('Callback should not be called', firedCallback);
    +  assertArrayEquals('Only C should be canceled since A and B fired.',
    +      ['c'], canceled);
    +}
    +
    +
    +function testErrorDoesNotCancelPendingChildrenForVanillaLists() {
    +  var canceled = [];
    +  var a = new Deferred(function() {
    +    canceled.push('a');
    +  });
    +  var b = new Deferred(function() {
    +    canceled.push('b');
    +  });
    +  var c = new Deferred(function() {
    +    canceled.push('c');
    +  });
    +
    +  var dl = new DeferredList([a, b, c]);
    +
    +  var firedErrback = false;
    +  var firedCallback = false;
    +  dl.addCallback(function() {
    +    firedCallback = true;
    +  });
    +  dl.addErrback(function() {
    +    firedErrback = true;
    +    return null;
    +  });
    +
    +  addCatchAll(dl);
    +
    +  a.callback('a')
    +  b.errback();
    +  c.callback('c')
    +
    +  assertFalse('Errback should not be called', firedErrback);
    +  assertTrue('Callback should be called', firedCallback);
    +  assertArrayEquals('No cancellations', [], canceled);
    +}
    +
    +
    +function testInputDeferredsStillUsable() {
    +  var increment = function(res) {
    +    return res + 1;
    +  };
    +  var incrementErrback = function(res) {
    +    throw res + 1;
    +  };
    +
    +  var aComplete = false;
    +  var bComplete = false;
    +  var hadListCallback = false;
    +
    +  var a = new Deferred().addCallback(increment);
    +  var b = new Deferred().addErrback(incrementErrback);
    +  var c = new Deferred();
    +
    +  var dl = new DeferredList([a, b, c]);
    +
    +  a.callback(0);
    +  a.addCallback(increment);
    +  a.addCallback(function(res) {
    +    aComplete = true;
    +    assertEquals(
    +        'The "a" Deferred should have had two increment callbacks.',
    +        2, res);
    +  });
    +  assertTrue('The "a" deferred should complete before the list.', aComplete);
    +
    +  b.errback(0);
    +  b.addErrback(incrementErrback);
    +  b.addErrback(function(res) {
    +    bComplete = true;
    +    assertEquals(
    +        'The "b" Deferred should have had two increment errbacks.',
    +        2, res);
    +  });
    +  assertTrue('The "b" deferred should complete before the list.', bComplete);
    +
    +  assertFalse('The list should not fire until every input has.', dl.hasFired());
    +  c.callback();
    +  assertTrue(dl.hasFired());
    +
    +  assertFalse(hadListCallback);
    +  dl.addCallback(function(results) {
    +    hadListCallback = true;
    +
    +    var aResult = results[0];
    +    var bResult = results[1];
    +    var cResult = results[2];
    +
    +    assertTrue(aResult[0]);
    +    assertEquals(
    +        'Should see the result from before the second callback was added.',
    +        1, aResult[1]);
    +
    +    assertFalse(bResult[0]);
    +    assertEquals(
    +        'Should see the result from before the second errback was added.',
    +        1, aResult[1]);
    +
    +    assertTrue(cResult[0]);
    +  });
    +  assertTrue(hadListCallback);
    +
    +  addCatchAll(dl);
    +}
    +
    +</script>
    +</body>
    +</html>
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/osapi/osapi.js b/mapbox-gl/closure-library/third_party/closure/goog/osapi/osapi.js
    new file mode 100644
    index 0000000000..277a9ad62d
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/osapi/osapi.js
    @@ -0,0 +1,95 @@
    +/**
    + * @license
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements. See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership. The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License. You may obtain a copy of the License at
    + * http://www.apache.org/licenses/LICENSE-2.0
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied. See the License for the
    + * specific language governing permissions and limitations under the License.
    + */
    +
    +/**
    + * @fileoverview Base OSAPI binding.
    + * This file was copied from
    + * http://svn.apache.org/repos/asf/shindig/trunk/features/src/main/javascript/features/shindig.container/osapi.js
    + * and it's slightly modified for Closure.
    + */
    +
    +goog.provide('goog.osapi');
    +
    +
    +// Expose osapi from container side.
    +var osapi = osapi || {};
    +goog.exportSymbol('osapi', osapi);
    +
    +
    +/** @type {Function} */
    +osapi.callback;
    +
    +
    +/**
    + * Dispatch a JSON-RPC batch request to services defined in the osapi namespace
    + * @param {Array<Object>} requests an array of rpc requests.
    + */
    +goog.osapi.handleGadgetRpcMethod = function(requests) {
    +  var responses = new Array(requests.length);
    +  var callCount = 0;
    +  var callback = osapi.callback;
    +  var dummy = function(params, apiCallback) {
    +    apiCallback({});
    +  };
    +  for (var i = 0; i < requests.length; i++) {
    +    // Don't allow underscores in any part of the method name as a
    +    // convention for restricted methods
    +    var current = osapi;
    +    if (requests[i]['method'].indexOf('_') == -1) {
    +      var path = requests[i]['method'].split('.');
    +      for (var j = 0; j < path.length; j++) {
    +        if (current.hasOwnProperty(path[j])) {
    +          current = current[path[j]];
    +        } else {
    +          // No matching api
    +          current = dummy;
    +          break;
    +        }
    +      }
    +    } else {
    +      current = dummy;
    +    }
    +
    +    // Execute the call and latch the rpc callback until all
    +    // complete
    +    current(requests[i]['params'], function(i) {
    +      return function(response) {
    +        // Put back in json-rpc format
    +        responses[i] = {'id': requests[i].id, 'data': response};
    +        callCount++;
    +        if (callCount == requests.length) {
    +          callback(responses);
    +        }
    +      };
    +    }(i));
    +  }
    +};
    +
    +
    +/**
    + * Initializes container side osapi binding.
    + */
    +goog.osapi.init = function() {
    +   // Container-side binding for the gadgetsrpctransport used by osapi.
    +   // Containers add services to the client-side osapi implementation by
    +   // defining them in the osapi namespace
    +  if (gadgets && gadgets.rpc) { // Only define if gadgets rpc exists.
    +    // Register the osapi RPC dispatcher.
    +    gadgets.rpc.register('osapi._handleGadgetRpcMethod',
    +        /** @type {!Function} */ (goog.osapi.handleGadgetRpcMethod));
    +  }
    +};
    diff --git a/mapbox-gl/closure-library/third_party/closure/goog/svgpan/svgpan.js b/mapbox-gl/closure-library/third_party/closure/goog/svgpan/svgpan.js
    new file mode 100644
    index 0000000000..0a3682ffab
    --- /dev/null
    +++ b/mapbox-gl/closure-library/third_party/closure/goog/svgpan/svgpan.js
    @@ -0,0 +1,425 @@
    +/**
    + *  SVGPan library 1.2.2
    + * ======================
    + *
    + * Given an unique existing element with a given id (or by default, the first
    + * g-element), including the library into any SVG adds the following
    + * capabilities:
    + *
    + *  - Mouse panning
    + *  - Mouse zooming (using the wheel)
    + *  - Object dragging
    + *
    + * You can configure the behaviour of the pan/zoom/drag via setOptions().
    + *
    + * Known issues:
    + *
    + *  - Zooming (while panning) on Safari has still some issues
    + *
    + * Releases:
    + *
    + * 1.2.2, Tue Aug 30 17:21:56 CEST 2011, Andrea Leofreddi
    + *  - Fixed viewBox on root tag (#7)
    + *  - Improved zoom speed (#2)
    + *
    + * 1.2.1, Mon Jul  4 00:33:18 CEST 2011, Andrea Leofreddi
    + *  - Fixed a regression with mouse wheel (now working on Firefox 5)
    + *  - Working with viewBox attribute (#4)
    + *  - Added "use strict;" and fixed resulting warnings (#5)
    + *  - Added configuration variables, dragging is disabled by default (#3)
    + *
    + * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
    + *  Fixed a bug with browser mouse handler interaction
    + *
    + * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
    + *  Updated the zoom code to support the mouse wheel on Safari/Chrome
    + *
    + * 1.0, Andrea Leofreddi
    + *  First release
    + */
    +
    +/**
    + * @license
    + * This code is licensed under the following BSD license:
    + * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights
    + * reserved.
    + *
    + * Redistribution and use in source and binary forms, with or without
    + * modification, are permitted provided that the following conditions are met:
    + *
    + *    1. Redistributions of source code must retain the above copyright notice,
    + *       this list of conditions and the following disclaimer.
    + *
    + *    2. Redistributions in binary form must reproduce the above copyright
    + *       notice, this list of conditions and the following disclaimer in the
    + *       documentation and/or other materials provided with the distribution.
    + *
    + * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR
    + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    + * EVENT SHALL Andrea Leofreddi OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + *
    + * The views and conclusions contained in the software and documentation are
    + * those of the authors and should not be interpreted as representing official
    + * policies, either expressed or implied, of Andrea Leofreddi.
    + *
    + */
    +
    +goog.provide('svgpan.SvgPan');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.MouseWheelHandler');
    +
    +
    +
    +/**
    + * Instantiates an SvgPan object.
    + * @param {string=} opt_graphElementId The id of the graph element.
    + * @param {Element=} opt_root An optional document root.
    + * @constructor
    + * @extends {goog.Disposable}
    + */
    +svgpan.SvgPan = function(opt_graphElementId, opt_root) {
    +  svgpan.SvgPan.base(this, 'constructor');
    +
    +  /** @private {Element} */
    +  this.root_ = opt_root || document.documentElement;
    +
    +  /** @private {?string} */
    +  this.graphElementId_ = opt_graphElementId || null;
    +
    +  /** @private {boolean} */
    +  this.cancelNextClick_ = false;
    +
    +  /** @private {boolean} */
    +  this.enablePan_ = true;
    +
    +  /** @private {boolean} */
    +  this.enableZoom_ = true;
    +
    +  /** @private {boolean} */
    +  this.enableDrag_ = false;
    +
    +  /** @private {number} */
    +  this.zoomScale_ = 0.4;
    +
    +  /** @private {svgpan.SvgPan.State} */
    +  this.state_ = svgpan.SvgPan.State.NONE;
    +
    +  /** @private {Element} */
    +  this.svgRoot_ = null;
    +
    +  /** @private {Element} */
    +  this.stateTarget_ = null;
    +
    +  /** @private {SVGPoint} */
    +  this.stateOrigin_ = null;
    +
    +  /** @private {SVGMatrix} */
    +  this.stateTf_ = null;
    +
    +  /** @private {goog.events.MouseWheelHandler} */
    +  this.mouseWheelHandler_ = null;
    +
    +  this.setupHandlers_();
    +};
    +goog.inherits(svgpan.SvgPan, goog.Disposable);
    +
    +
    +/** @override */
    +svgpan.SvgPan.prototype.disposeInternal = function() {
    +  svgpan.SvgPan.base(this, 'disposeInternal');
    +  goog.events.removeAll(this.root_);
    +  this.mouseWheelHandler_.dispose();
    +};
    +
    +
    +/**
    + * @enum {string}
    + */
    +svgpan.SvgPan.State = {
    +  NONE: 'none',
    +  PAN: 'pan',
    +  DRAG: 'drag'
    +};
    +
    +
    +/**
    + * Enables/disables panning the entire SVG (default = true).
    + * @param {boolean} enabled Whether or not to allow panning.
    + */
    +svgpan.SvgPan.prototype.setPanEnabled = function(enabled) {
    +  this.enablePan_ = enabled;
    +};
    +
    +
    +/**
    + * Enables/disables zooming (default = true).
    + * @param {boolean} enabled Whether or not to allow zooming (default = true).
    + */
    +svgpan.SvgPan.prototype.setZoomEnabled = function(enabled) {
    +  this.enableZoom_ = enabled;
    +};
    +
    +
    +/**
    + * Enables/disables dragging individual SVG objects (default = false).
    + * @param {boolean} enabled Whether or not to allow dragging of objects.
    + */
    +svgpan.SvgPan.prototype.setDragEnabled = function(enabled) {
    +  this.enableDrag_ = enabled;
    +};
    +
    +
    +/**
    + * Sets the sensitivity of mousewheel zooming (default = 0.4).
    + * @param {number} scale The new zoom scale.
    + */
    +svgpan.SvgPan.prototype.setZoomScale = function(scale) {
    +  this.zoomScale_ = scale;
    +};
    +
    +
    +/**
    + * Registers mouse event handlers.
    + * @private
    + */
    +svgpan.SvgPan.prototype.setupHandlers_ = function() {
    +  goog.events.listen(this.root_, goog.events.EventType.CLICK,
    +      goog.bind(this.handleMouseClick_, this));
    +  goog.events.listen(this.root_, goog.events.EventType.MOUSEUP,
    +      goog.bind(this.handleMouseUp_, this));
    +  goog.events.listen(this.root_, goog.events.EventType.MOUSEDOWN,
    +      goog.bind(this.handleMouseDown_, this));
    +  goog.events.listen(this.root_, goog.events.EventType.MOUSEMOVE,
    +      goog.bind(this.handleMouseMove_, this));
    +  this.mouseWheelHandler_ = new goog.events.MouseWheelHandler(this.root_);
    +  goog.events.listen(this.mouseWheelHandler_,
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +      goog.bind(this.handleMouseWheel_, this));
    +};
    +
    +
    +/**
    + * Retrieves the root element for SVG manipulation. The element is then cached.
    + * @param {Document} svgDoc The document.
    + * @return {Element} The svg root.
    + * @private
    + */
    +svgpan.SvgPan.prototype.getRoot_ = function(svgDoc) {
    +  if (!this.svgRoot_) {
    +    var r = this.graphElementId_ ?
    +        svgDoc.getElementById(this.graphElementId_) : svgDoc.documentElement;
    +    var t = r;
    +    while (t != svgDoc) {
    +      if (t.getAttribute('viewBox')) {
    +        this.setCtm_(r, r.getCTM());
    +        t.removeAttribute('viewBox');
    +      }
    +      t = t.parentNode;
    +    }
    +    this.svgRoot_ = r;
    +  }
    +  return this.svgRoot_;
    +};
    +
    +
    +/**
    + * Instantiates an SVGPoint object with given event coordinates.
    + * @param {!goog.events.Event} evt The event with coordinates.
    + * @return {SVGPoint} The created point.
    + * @private
    + */
    +svgpan.SvgPan.prototype.getEventPoint_ = function(evt) {
    +  return this.newPoint_(evt.clientX, evt.clientY);
    +};
    +
    +
    +/**
    + * Instantiates an SVGPoint object with given coordinates.
    + * @param {number} x The x coordinate.
    + * @param {number} y The y coordinate.
    + * @return {SVGPoint} The created point.
    + * @private
    + */
    +svgpan.SvgPan.prototype.newPoint_ = function(x, y) {
    +  var p = this.root_.createSVGPoint();
    +  p.x = x;
    +  p.y = y;
    +  return p;
    +};
    +
    +
    +/**
    + * Sets the current transform matrix of an element.
    + * @param {Element} element The element.
    + * @param {SVGMatrix} matrix The transform matrix.
    + * @private
    + */
    +svgpan.SvgPan.prototype.setCtm_ = function(element, matrix) {
    +  var s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' +
    +      matrix.d + ',' + matrix.e + ',' + matrix.f + ')';
    +  element.setAttribute('transform', s);
    +};
    +
    +
    +/**
    + * Handle mouse wheel event.
    + * @param {!goog.events.Event} evt The event.
    + * @private
    + */
    +svgpan.SvgPan.prototype.handleMouseWheel_ = function(evt) {
    +  if (!this.enableZoom_)
    +    return;
    +
    +  // Prevents scrolling.
    +  evt.preventDefault();
    +
    +  var svgDoc = evt.target.ownerDocument;
    +
    +  var delta = evt.deltaY / -9;
    +  var z = Math.pow(1 + this.zoomScale_, delta);
    +  var g = this.getRoot_(svgDoc);
    +  var p = this.getEventPoint_(evt);
    +  p = p.matrixTransform(g.getCTM().inverse());
    +
    +  // Compute new scale matrix in current mouse position
    +  var k = this.root_.createSVGMatrix().translate(
    +      p.x, p.y).scale(z).translate(-p.x, -p.y);
    +  this.setCtm_(g, g.getCTM().multiply(k));
    +
    +  if (typeof(this.stateTf_) == 'undefined') {
    +    this.stateTf_ = g.getCTM().inverse();
    +  }
    +  this.stateTf_ =
    +      this.stateTf_ ? this.stateTf_.multiply(k.inverse()) : this.stateTf_;
    +};
    +
    +
    +/**
    + * Handle mouse move event.
    + * @param {!goog.events.Event} evt The event.
    + * @private
    + */
    +svgpan.SvgPan.prototype.handleMouseMove_ = function(evt) {
    +  if (evt.button != 0) {
    +    return;
    +  }
    +  this.handleMove(evt.clientX, evt.clientY, evt.target.ownerDocument);
    +};
    +
    +
    +/**
    + * Handles mouse motion for the given coordinates.
    + * @param {number} x The x coordinate.
    + * @param {number} y The y coordinate.
    + * @param {Document} svgDoc The svg document.
    + */
    +svgpan.SvgPan.prototype.handleMove = function(x, y, svgDoc) {
    +  var g = this.getRoot_(svgDoc);
    +  if (this.state_ == svgpan.SvgPan.State.PAN && this.enablePan_) {
    +    // Pan mode
    +    var p = this.newPoint_(x, y).matrixTransform(
    +        /** @type {!SVGMatrix} */ (this.stateTf_));
    +    this.setCtm_(g, this.stateTf_.inverse().translate(
    +        p.x - this.stateOrigin_.x, p.y - this.stateOrigin_.y));
    +    this.cancelNextClick_ = true;
    +  } else if (this.state_ == svgpan.SvgPan.State.DRAG && this.enableDrag_) {
    +    // Drag mode
    +    var p = this.newPoint_(x, y).matrixTransform(g.getCTM().inverse());
    +    this.setCtm_(this.stateTarget_, this.root_.createSVGMatrix().translate(
    +        p.x - this.stateOrigin_.x, p.y - this.stateOrigin_.y).multiply(
    +        g.getCTM().inverse()).multiply(this.stateTarget_.getCTM()));
    +    this.stateOrigin_ = p;
    +  }
    +};
    +
    +
    +/**
    + * Handle click event.
    + * @param {!goog.events.Event} evt The event.
    + * @private
    + */
    +svgpan.SvgPan.prototype.handleMouseDown_ = function(evt) {
    +  if (evt.button != 0) {
    +    return;
    +  }
    +  // Prevent selection while dragging.
    +  evt.preventDefault();
    +  var svgDoc = evt.target.ownerDocument;
    +
    +  var g = this.getRoot_(svgDoc);
    +
    +  if (evt.target.tagName == 'svg' || !this.enableDrag_) {
    +    // Pan mode
    +    this.state_ = svgpan.SvgPan.State.PAN;
    +    this.stateTf_ = g.getCTM().inverse();
    +    this.stateOrigin_ = this.getEventPoint_(evt).matrixTransform(this.stateTf_);
    +  } else {
    +    // Drag mode
    +    this.state_ = svgpan.SvgPan.State.DRAG;
    +    this.stateTarget_ = /** @type {Element} */ (evt.target);
    +    this.stateTf_ = g.getCTM().inverse();
    +    this.stateOrigin_ = this.getEventPoint_(evt).matrixTransform(this.stateTf_);
    +  }
    +};
    +
    +
    +/**
    + * Handle mouse button release event.
    + * @param {!goog.events.Event} evt The event.
    + * @private
    + */
    +svgpan.SvgPan.prototype.handleMouseUp_ = function(evt) {
    +  if (this.state_ != svgpan.SvgPan.State.NONE) {
    +    this.endPanOrDrag();
    +  }
    +};
    +
    +
    +/**
    + * Ends pan/drag mode.
    + */
    +svgpan.SvgPan.prototype.endPanOrDrag = function() {
    +  if (this.state_ != svgpan.SvgPan.State.NONE) {
    +    this.state_ = svgpan.SvgPan.State.NONE;
    +  }
    +};
    +
    +
    +/**
    + * Handle mouse clicks.
    + * @param {!goog.events.Event} evt The event.
    + * @private
    + */
    +svgpan.SvgPan.prototype.handleMouseClick_ = function(evt) {
    +  // We only set cancelNextClick_ after panning occurred, and use it to prevent
    +  // the default action that would otherwise take place when clicking on the
    +  // element (for instance, navigation on clickable links, but also any click
    +  // handler that may be set on an SVG element, in the case of active SVG
    +  // content)
    +  if (this.cancelNextClick_) {
    +    // Cancel potential click handler on active SVG content.
    +    evt.stopPropagation();
    +    // Cancel navigation when panning on clickable links.
    +    evt.preventDefault();
    +  }
    +  this.cancelNextClick_ = false;
    +};
    +
    +
    +/**
    + * Returns the current state.
    + * @return {!svgpan.SvgPan.State}
    + */
    +svgpan.SvgPan.prototype.getState = function() {
    +  return this.state_;
    +};
    diff --git a/mapbox-gl/css/ol.css b/mapbox-gl/css/ol.css
    new file mode 100644
    index 0000000000..2b52dc141e
    --- /dev/null
    +++ b/mapbox-gl/css/ol.css
    @@ -0,0 +1 @@
    +.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:#95b9e6;background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width}.ol-overlay-container{will-change:left,right,top,bottom}.ol-unsupported{display:none}.ol-viewport .ol-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-control{position:absolute;background-color:#eee;background-color:rgba(255,255,255,.4);border-radius:4px;padding:2px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}@media print{.ol-control{display:none}}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:#7b98bc;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:#4c6079;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;font-size:.7rem;line-height:1.375em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none;line-height:inherit}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution.ol-logo-only ul{display:block}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0;height:1.1em;line-height:1em}.ol-attribution.ol-logo-only{background:0 0;bottom:.4em;height:1.1em;line-height:1em}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{position:absolute;top:4.5em;left:.5em;background:#eee;background:rgba(255,255,255,.4);width:24px;height:200px}.ol-zoomslider-thumb{position:absolute;background:#7b98bc;background:rgba(0,60,136,.5);border-radius:2px;cursor:pointer;height:10px;width:22px;margin:3px}.ol-touch .ol-zoomslider{top:5.5em;width:2.052em}.ol-touch .ol-zoomslider-thumb{width:1.8em}.ol-overviewmap{position:absolute;left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)}
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/Jugl.js b/mapbox-gl/examples/Jugl.js
    new file mode 100644
    index 0000000000..4f81a275cb
    --- /dev/null
    +++ b/mapbox-gl/examples/Jugl.js
    @@ -0,0 +1,8 @@
    +/*
    + * Jugl.js -- JavaScript Template Library
    + *
    + * Copyright 2007-2010 Tim Schaub
    + * Released under the MIT license.  Please see 
    + * http://github.com/tschaub/jugl/blob/master/license.txt for the full license.
    + */
    +(function(){var f={prefix:"jugl",namespaceURI:null,loadTemplate:function(h){var i=function(l){var m,k,n=!l.status||(l.status>=200&&l.status<300);if(n){try{m=l.responseXML;k=new e(m.documentElement)}catch(j){m=document.createElement("div");m.innerHTML=l.responseText;k=new e(m.firstChild)}if(h.callback){h.callback.call(h.scope,k)}}else{if(h.failure){h.failure.call(h.scope,l)}}};d(h.url,i)}};var g=function(h,j){h=h||{};j=j||{};for(var i in j){h[i]=j[i]}return h};var a=function(l,o){var m,n,k,j,h;if(typeof(l)==="string"){m=document.getElementById(l);if(!m){throw Error("Element id not found: "+l)}l=m}if(typeof(o)==="string"){m=document.getElementById(o);if(!m){throw Error("Element id not found: "+o)}o=m}if(o.namespaceURI&&o.xml){n=document.createElement("div");n.innerHTML=o.xml;k=n.childNodes;for(j=0,h=k.length;j<h;++j){l.appendChild(k[j])}}else{if(l.ownerDocument&&l.ownerDocument.importNode&&l.ownerDocument!==o.ownerDocument){o=l.ownerDocument.importNode(o,true)}l.appendChild(o)}return o};var d=function(h,k,i){var j;if(typeof XMLHttpRequest!=="undefined"){j=new XMLHttpRequest()}else{if(typeof ActiveXObject!=="undefined"){j=new ActiveXObject("Microsoft.XMLHTTP")}else{throw new Error("XMLHttpRequest not supported")}}j.open("GET",h);j.onreadystatechange=function(){if(j.readyState===4){k.call(i,j)}};j.send(null)};var b=function(h,i){this.template=h;this.node=i;this.scope={};this.scope.repeat={}};g(b.prototype,{clone:function(){var i=this.node.cloneNode(true);i.removeAttribute("id");var h=new b(this.template,i);g(h.scope,this.scope);return h},getAttribute:function(h){var j;if(this.node.nodeType===1){if(this.template.usingNS){j=this.node.getAttributeNodeNS(f.namespaceURI,h)}else{j=this.node.getAttributeNode(f.prefix+":"+h)}if(j&&!j.specified){j=false}}var i;if(j){i=new c(this,j,h)}else{i=j}return i},setAttribute:function(h,i){this.node.setAttribute(h,i)},removeAttributeNode:function(h){this.node.removeAttributeNode(h.node)},getChildNodes:function(){var k=this.node.childNodes.length;var j=new Array(k);var l;for(var h=0;h<k;++h){l=new b(this.template,this.node.childNodes[h]);l.scope=g({},this.scope);j[h]=l}return j},removeChildNodes:function(){while(this.node.hasChildNodes()){this.node.removeChild(this.node.firstChild)}},removeChild:function(h){this.node.removeChild(h.node);return node},removeSelf:function(){this.node.parentNode.removeChild(this.node)},importNode:function(h){if(this.node.ownerDocument&&this.node.ownerDocument.importNode){if(h.node.ownerDocument!==this.node.ownerDocument){h.node=this.node.ownerDocument.importNode(h.node,true)}}},appendChild:function(h){this.importNode(h);this.node.appendChild(h.node)},insertAfter:function(h){this.importNode(h);var j=this.node.parentNode;var i=this.node.nextSibling;if(i){j.insertBefore(h.node,i)}else{j.appendChild(h.node)}},insertBefore:function(h){this.importNode(h);var i=this.node.parentNode;i.insertBefore(h.node,this.node)},process:function(){var j;var r=true;var n=["define","condition","repeat"];for(var o=0,p=n.length;o<p;++o){j=this.getAttribute(n[o]);if(j){r=j.process();if(!r){return}}}var q=this.getAttribute("content");if(q){q.process()}else{var k=this.getAttribute("replace");if(k){k.process()}}var m=this.getAttribute("attributes");if(m){m.process()}if(!q&&!k){this.processChildNodes()}var h=this.getAttribute("omit-tag");if(h){h.process()}var l=this.getAttribute("reflow");if(l){l.process()}},processChildNodes:function(){var k=this.getChildNodes();for(var j=0,h=k.length;j<h;++j){k[j].process()}}});var e=function(h){h=h||{};if(typeof h==="string"||(h.nodeType===1)){h={node:h}}if(typeof(h.node)==="string"){h.node=document.getElementById(h.node);if(!h.node){throw Error("Element id not found: "+h.node)}}if(h.node){this.node=h.node;this.loaded=true}else{if(h.url){this.load({url:h.url,callback:h.callback,scope:h.scope})}}};g(e.prototype,{node:null,usingNS:false,xmldom:window.ActiveXObject?new ActiveXObject("Microsoft.XMLDOM"):null,trimSpace:(/^\s*(\w+)\s+(.*?)\s*$/),loaded:false,loading:false,process:function(h){var i,j;h=g({context:null,clone:false,string:false},h);this.usingNS=this.node.getAttributeNodeNS&&f.namespaceURI;i=new b(this,this.node);if(h.clone||h.string){i=i.clone()}if(h.context){i.scope=h.context}i.process();if(h.string){if(i.node.innerHTML){j=i.node.innerHTML}else{if(this.xmldom){j=i.node.xml}else{j=(new XMLSerializer).serializeToString(i.node)}}}else{j=i.node;if(h.parent){if(h.clone){j=a(h.parent,i.node)}else{this.appendTo(h.parent)}}}return j},load:function(j){if(typeof j==="string"){j={url:j}}j=j||{};this.loading=true;var h=function(k){this.node=k.node;this.loading=false;this.loaded=true;if(j.callback){j.callback.apply(j.scope,[k])}};var i;if(j.failure){i=(function(){return function(k){j.failure.call(j.scope,k)}})()}f.loadTemplate({url:j.url,callback:h,failure:i,scope:this})},appendTo:function(h){this.node=a(h,this.node);return this}});var c=function(h,j,i){this.element=h;this.node=j;this.type=i;this.nodeValue=j.nodeValue;this.nodeName=j.nodeName;this.template=h.template};g(c.prototype,{splitAttributeValue:function(i){i=(i!=null)?i:this.nodeValue;var h=this.template.trimSpace.exec(i);return h&&h.length===3&&[h[1],h[2]]},splitExpressionPrefix:function(){var h=this.splitAttributeValue();if(!h||(h[0]!="structure"&&h[0]!="text")){h=[null,this.nodeValue]}return h},getAttributeValues:function(){return this.nodeValue.replace(/[\t\n]/g,"").replace(/;\s*$/,"").replace(/;;/g,"\t").split(";").join("\n").replace(/\t/g,";").split(/\n/g)},removeSelf:function(){this.element.removeAttributeNode(this)},process:function(){return this.processAttribute[this.type].apply(this,[])},evalInScope:function(k){var i=this.element.scope;var h=[];var j=[];for(key in i){h.push(key);j.push(i[key])}var l=new Function(h.join(","),"return "+k);return l.apply({},j)},processAttribute:{define:function(){var l,k,j,h=this.getAttributeValues();for(k=0,j=h.length;k<j;++k){l=this.splitAttributeValue(h[k]);this.element.scope[l[0]]=this.evalInScope(l[1])}this.removeSelf();return true},condition:function(){var h=!!(this.evalInScope(this.nodeValue));this.removeSelf();if(!h){this.element.removeSelf()}return h},repeat:function(){var l=this.splitAttributeValue();var r=l[0];var o=this.evalInScope(l[1]);this.removeSelf();if(!(o instanceof Array)){var q=new Array();for(var j in o){q.push(j)}o=q}var m;var h=this.element;for(var n=0,k=o.length;n<k;++n){m=this.element.clone();m.scope[r]=o[n];m.scope.repeat[r]={index:n,number:n+1,even:!(n%2),odd:!!(n%2),start:(n===0),end:(n===k-1),length:k};h.insertAfter(m);m.process();h=m}this.element.removeSelf();return false},content:function(){var m=this.splitExpressionPrefix();var p=this.evalInScope(m[1]);this.removeSelf();if(m[0]==="structure"){try{this.element.node.innerHTML=p}catch(l){var h=document.createElement("div");h.innerHTML=p;if(this.element.node.xml&&this.template.xmldom){while(this.element.node.firstChild){this.element.node.removeChild(this.element.node.firstChild)}this.template.xmldom.loadXML(h.outerHTML);var k=this.template.xmldom.firstChild.childNodes;for(var n=0,o=k.length;n<o;++n){this.element.node.appendChild(k[n])}}else{this.element.node.innerHTML=h.innerHTML}}}else{var q;if(this.element.node.xml&&this.template.xmldom){q=this.template.xmldom.createTextNode(p)}else{q=document.createTextNode(p)}var j=new b(this.template,q);this.element.removeChildNodes();this.element.appendChild(j)}return true},replace:function(){var k=this.splitExpressionPrefix();var j=this.evalInScope(k[1]);this.removeSelf();if(k[0]==="structure"){var m=document.createElement("div");m.innerHTML=j;if(this.element.node.xml&&this.template.xmldom){this.template.xmldom.loadXML(m.outerHTML);m=this.template.xmldom.firstChild}while(m.firstChild){var l=m.removeChild(m.firstChild);if(this.element.node.ownerDocument&&this.element.node.ownerDocument.importNode){if(l.ownerDocument!=this.element.node.ownerDocument){l=this.element.node.ownerDocument.importNode(l,true)}}this.element.node.parentNode.insertBefore(l,this.element.node)}}else{var i;if(this.element.node.xml&&this.template.xmldom){i=this.template.xmldom.createTextNode(j)}else{i=document.createTextNode(j)}var h=new b(this.template,i);this.element.insertBefore(h)}this.element.removeSelf();return true},attributes:function(){var h=this.getAttributeValues();var n,k,m;for(var l=0,j=h.length;l<j;++l){n=this.splitAttributeValue(h[l]);k=n[0];m=this.evalInScope(n[1]);if(m!==false){this.element.setAttribute(k,m)}}this.removeSelf();return true},"omit-tag":function(){var l=((this.nodeValue==="")||!!(this.evalInScope(this.nodeValue)));this.removeSelf();if(l){var k=this.element.getChildNodes();for(var j=0,h=k.length;j<h;++j){this.element.insertBefore(k[j])}this.element.removeSelf()}},reflow:function(){var h=((this.nodeValue==="")||!!(this.evalInScope(this.nodeValue)));this.removeSelf();if(h){if(this.element.node.outerHTML){this.element.node.outerHTML=this.element.node.outerHTML}else{this.element.node.innerHTML=this.element.node.innerHTML}}}}});window.jugl=g(f,{Template:e})})();
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/accessible-require.js b/mapbox-gl/examples/accessible-require.js
    new file mode 100644
    index 0000000000..ff458ec379
    --- /dev/null
    +++ b/mapbox-gl/examples/accessible-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/accessible.html b/mapbox-gl/examples/accessible.html
    new file mode 100644
    index 0000000000..71e821d804
    --- /dev/null
    +++ b/mapbox-gl/examples/accessible.html
    @@ -0,0 +1,72 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Accessibility example</title>
    +    <style>
    +      a.skiplink {
    +        position: absolute;
    +        clip: rect(1px, 1px, 1px, 1px);
    +        padding: 0;
    +        border: 0;
    +        height: 1px;
    +        width: 1px;
    +        overflow: hidden;
    +      }
    +      a.skiplink:focus {
    +        clip: auto;
    +        height: auto;
    +        width: auto;
    +        background-color: #fff;
    +        padding: 0.3em;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png" alt=""> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <a class="skiplink" href="#map">Go to map</a>
    +          <div id="map" class="map" tabindex="0"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Accessibility example</h4>
    +          <p id="shortdesc">Example of an accessible map.</p>
    +          <div id="docs">
    +            <p>This page's <code>map</code> element has its <code>tabindex</code> attribute set to <code>"0"</code>, that makes it focusable. To focus the map element you can either navigate to it using the "tab" key or use the skip link. When the <code>map</code> element is focused the + and - keys can be used to zoom in and out and the arrow keys can be used to pan.</p>
    +            <p>When clicked the "Zoom in" and "Zoom out" buttons below the map zoom the map in and out, respectively. You can navigate to the buttons using the "tab" key, and press the "enter" key to trigger the zooming action.</p>
    +            <p>See the <a href="accessible.js" target="_blank">accessible.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">accessibility, tabindex</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=accessible" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/accessible.js b/mapbox-gl/examples/accessible.js
    new file mode 100644
    index 0000000000..75aa0d4ffd
    --- /dev/null
    +++ b/mapbox-gl/examples/accessible.js
    @@ -0,0 +1,25 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +jQuery('#map').after('<button type="button" ' +
    +    'onclick="map.getView().setZoom(map.getView().getZoom() - 1);">' +
    +    'Zoom out</button>');
    +jQuery('#map').after('<button type="button" ' +
    +    'onclick="map.getView().setZoom(map.getView().getZoom() + 1);">' +
    +    'Zoom in</button>');
    diff --git a/mapbox-gl/examples/animation-require.js b/mapbox-gl/examples/animation-require.js
    new file mode 100644
    index 0000000000..af8235acbc
    --- /dev/null
    +++ b/mapbox-gl/examples/animation-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.animation');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/animation.html b/mapbox-gl/examples/animation.html
    new file mode 100644
    index 0000000000..d072983e10
    --- /dev/null
    +++ b/mapbox-gl/examples/animation.html
    @@ -0,0 +1,65 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Animation example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <button id="rotate-left"><i class="icon-arrow-left"></i></button>
    +          <button id="rotate-right"><i class="icon-arrow-right"></i></button>
    +          <button id="rotate-around-rome">Rotate around Rome</button>
    +          <button id="pan-to-london">Pan to London</button>
    +          <button id="elastic-to-moscow">Elastic to Moscow</button>
    +          <button id="bounce-to-istanbul">Bounce to Istanbul</button>
    +          <button id="spin-to-rome">Spin to Rome</button>
    +          <button id="fly-to-bern">Fly to Bern</button>
    +          <button id="spiral-to-madrid">Spiral to Madrid</button>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Animation example</h4>
    +          <p id="shortdesc">Demonstrates animated pan, zoom, and rotation.</p>
    +          <div id="docs">
    +            <p>See the <a href="animation.js" target="_blank">animation.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">animation</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=animation" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/animation.js b/mapbox-gl/examples/animation.js
    new file mode 100644
    index 0000000000..7f8c41ce07
    --- /dev/null
    +++ b/mapbox-gl/examples/animation.js
    @@ -0,0 +1,179 @@
    +// from https://github.com/DmitryBaranovskiy/raphael
    +function bounce(t) {
    +  var s = 7.5625, p = 2.75, l;
    +  if (t < (1 / p)) {
    +    l = s * t * t;
    +  } else {
    +    if (t < (2 / p)) {
    +      t -= (1.5 / p);
    +      l = s * t * t + 0.75;
    +    } else {
    +      if (t < (2.5 / p)) {
    +        t -= (2.25 / p);
    +        l = s * t * t + 0.9375;
    +      } else {
    +        t -= (2.625 / p);
    +        l = s * t * t + 0.984375;
    +      }
    +    }
    +  }
    +  return l;
    +}
    +
    +// from https://github.com/DmitryBaranovskiy/raphael
    +function elastic(t) {
    +  return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
    +}
    +
    +var london = ol.proj.transform([-0.12755, 51.507222], 'EPSG:4326', 'EPSG:3857');
    +var moscow = ol.proj.transform([37.6178, 55.7517], 'EPSG:4326', 'EPSG:3857');
    +var istanbul = ol.proj.transform([28.9744, 41.0128], 'EPSG:4326', 'EPSG:3857');
    +var rome = ol.proj.transform([12.5, 41.9], 'EPSG:4326', 'EPSG:3857');
    +var bern = ol.proj.transform([7.4458, 46.95], 'EPSG:4326', 'EPSG:3857');
    +var madrid = ol.proj.transform([-3.683333, 40.4], 'EPSG:4326', 'EPSG:3857');
    +
    +var view = new ol.View({
    +  // the view's initial state
    +  center: istanbul,
    +  zoom: 6
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      preload: 4,
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  // Improve user experience by loading tiles while animating. Will make
    +  // animations stutter on mobile or slow devices.
    +  loadTilesWhileAnimating: true,
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +var rotateLeft = document.getElementById('rotate-left');
    +rotateLeft.addEventListener('click', function() {
    +  var rotateLeft = ol.animation.rotate({
    +    duration: 2000,
    +    rotation: -4 * Math.PI
    +  });
    +  map.beforeRender(rotateLeft);
    +}, false);
    +var rotateRight = document.getElementById('rotate-right');
    +rotateRight.addEventListener('click', function() {
    +  var rotateRight = ol.animation.rotate({
    +    duration: 2000,
    +    rotation: 4 * Math.PI
    +  });
    +  map.beforeRender(rotateRight);
    +}, false);
    +
    +var rotateAroundRome = document.getElementById('rotate-around-rome');
    +rotateAroundRome.addEventListener('click', function() {
    +  var currentRotation = view.getRotation();
    +  var rotateAroundRome = ol.animation.rotate({
    +    anchor: rome,
    +    duration: 1000,
    +    rotation: currentRotation
    +  });
    +  map.beforeRender(rotateAroundRome);
    +  view.rotate(currentRotation + (Math.PI / 2), rome);
    +}, false);
    +
    +var panToLondon = document.getElementById('pan-to-london');
    +panToLondon.addEventListener('click', function() {
    +  var pan = ol.animation.pan({
    +    duration: 2000,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter())
    +  });
    +  map.beforeRender(pan);
    +  view.setCenter(london);
    +}, false);
    +
    +var elasticToMoscow = document.getElementById('elastic-to-moscow');
    +elasticToMoscow.addEventListener('click', function() {
    +  var pan = ol.animation.pan({
    +    duration: 2000,
    +    easing: elastic,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter())
    +  });
    +  map.beforeRender(pan);
    +  view.setCenter(moscow);
    +}, false);
    +
    +var bounceToIstanbul = document.getElementById('bounce-to-istanbul');
    +bounceToIstanbul.addEventListener('click', function() {
    +  var pan = ol.animation.pan({
    +    duration: 2000,
    +    easing: bounce,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter())
    +  });
    +  map.beforeRender(pan);
    +  view.setCenter(istanbul);
    +}, false);
    +
    +var spinToRome = document.getElementById('spin-to-rome');
    +spinToRome.addEventListener('click', function() {
    +  var duration = 2000;
    +  var start = +new Date();
    +  var pan = ol.animation.pan({
    +    duration: duration,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter()),
    +    start: start
    +  });
    +  var rotate = ol.animation.rotate({
    +    duration: duration,
    +    rotation: 2 * Math.PI,
    +    start: start
    +  });
    +  map.beforeRender(pan, rotate);
    +  view.setCenter(rome);
    +}, false);
    +
    +var flyToBern = document.getElementById('fly-to-bern');
    +flyToBern.addEventListener('click', function() {
    +  var duration = 2000;
    +  var start = +new Date();
    +  var pan = ol.animation.pan({
    +    duration: duration,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter()),
    +    start: start
    +  });
    +  var bounce = ol.animation.bounce({
    +    duration: duration,
    +    resolution: 4 * view.getResolution(),
    +    start: start
    +  });
    +  map.beforeRender(pan, bounce);
    +  view.setCenter(bern);
    +}, false);
    +
    +var spiralToMadrid = document.getElementById('spiral-to-madrid');
    +spiralToMadrid.addEventListener('click', function() {
    +  var duration = 2000;
    +  var start = +new Date();
    +  var pan = ol.animation.pan({
    +    duration: duration,
    +    source: /** @type {ol.Coordinate} */ (view.getCenter()),
    +    start: start
    +  });
    +  var bounce = ol.animation.bounce({
    +    duration: duration,
    +    resolution: 2 * view.getResolution(),
    +    start: start
    +  });
    +  var rotate = ol.animation.rotate({
    +    duration: duration,
    +    rotation: -4 * Math.PI,
    +    start: start
    +  });
    +  map.beforeRender(pan, bounce, rotate);
    +  view.setCenter(madrid);
    +}, false);
    diff --git a/mapbox-gl/examples/arcgis-tiled-require.js b/mapbox-gl/examples/arcgis-tiled-require.js
    new file mode 100644
    index 0000000000..71f9258de1
    --- /dev/null
    +++ b/mapbox-gl/examples/arcgis-tiled-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.TileArcGISRest');
    diff --git a/mapbox-gl/examples/arcgis-tiled.html b/mapbox-gl/examples/arcgis-tiled.html
    new file mode 100644
    index 0000000000..d922b78ca3
    --- /dev/null
    +++ b/mapbox-gl/examples/arcgis-tiled.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tiled ArcGIS MapServer example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tiled ArcGIS MapServer example</h4>
    +          <p id="shortdesc">Example of a tiled ArcGIS layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="arcgis-tiled.js" target="_blank">arcgis-tiled.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">arcgis, tile, tilelayer</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=arcgis-tiled" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/arcgis-tiled.js b/mapbox-gl/examples/arcgis-tiled.js
    new file mode 100644
    index 0000000000..46c2d9d17a
    --- /dev/null
    +++ b/mapbox-gl/examples/arcgis-tiled.js
    @@ -0,0 +1,22 @@
    +var url = 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/' +
    +    'Specialty/ESRI_StateCityHighway_USA/MapServer';
    +
    +var layers = [
    +  new ol.layer.Tile({
    +    source: new ol.source.MapQuest({layer: 'sat'})
    +  }),
    +  new ol.layer.Tile({
    +    extent: [-13884991, 2870341, -7455066, 6338219],
    +    source: new ol.source.TileArcGISRest({
    +      url: url
    +    })
    +  })
    +];
    +var map = new ol.Map({
    +  layers: layers,
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-10997148, 4569099],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/attributions-require.js b/mapbox-gl/examples/attributions-require.js
    new file mode 100644
    index 0000000000..bb8b592ea8
    --- /dev/null
    +++ b/mapbox-gl/examples/attributions-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.Attribution');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/attributions.html b/mapbox-gl/examples/attributions.html
    new file mode 100644
    index 0000000000..1ab192dce4
    --- /dev/null
    +++ b/mapbox-gl/examples/attributions.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Attributions example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Attributions example</h4>
    +          <p id="shortdesc">Example of a attributions visibily change on map resize, to collapse them on small maps.</p>
    +          <div id="docs">
    +            <p>See the <a href="attributions.js" target="_blank">attributions.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">attributions, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=attributions" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/attributions.js b/mapbox-gl/examples/attributions.js
    new file mode 100644
    index 0000000000..8f71aefb0b
    --- /dev/null
    +++ b/mapbox-gl/examples/attributions.js
    @@ -0,0 +1,26 @@
    +var attribution = new ol.control.Attribution({
    +  collapsible: false
    +});
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  controls: ol.control.defaults({ attribution: false }).extend([attribution]),
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +function checkSize() {
    +  var small = map.getSize()[0] < 600;
    +  attribution.setCollapsible(small);
    +  attribution.setCollapsed(small);
    +}
    +
    +$(window).on('resize', checkSize);
    +checkSize();
    diff --git a/mapbox-gl/examples/bind-input-require.js b/mapbox-gl/examples/bind-input-require.js
    new file mode 100644
    index 0000000000..29a3fc59e0
    --- /dev/null
    +++ b/mapbox-gl/examples/bind-input-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.dom.Input');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/bind-input.html b/mapbox-gl/examples/bind-input.html
    new file mode 100644
    index 0000000000..341c4ddbc5
    --- /dev/null
    +++ b/mapbox-gl/examples/bind-input.html
    @@ -0,0 +1,92 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Bind HTML input example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">Bind HTML input example</h4>
    +          <p id="shortdesc">Demonstrates two-way binding of HTML input elements to OpenLayers objects.</p>
    +          <div id="docs">
    +            <p>See the <a href="bind-input.js" target="_blank">bind-input.js source</a> to see how this is done.</p>
    +            <p id="has-webgl" style="display: none">With the <a href="?renderer=webgl">WebGL renderer</a>, <strong>hue</strong>, <strong>saturation</strong>, <strong>contrast</strong> and <strong>brightness</strong> can also be controlled.</p>
    +            <div id="no-webgl" class="alert alert-warning" style="display: none">
    +              <h4>Warning!</h4>
    +              A browser that supports <a href="http://get.webgl.org/">WebGL</a> is required to change the
    +              <strong>hue</strong>, <strong>saturation</strong>, <strong>contrast</strong> and <strong>brightness</strong>.
    +            </div>
    +          </div>
    +          <div id="tags">input, bind, openstreetmap</div>
    +        </div>
    +
    +        <div class="span4">
    +          <form class="">
    +            <fieldset>
    +              <legend>Layer</legend>
    +              <label class="checkbox" for="visible">
    +                <input id="visible" type="checkbox"/>visibility
    +              </label>
    +              <label>opacity</label>
    +              <input id="opacity" type="range" min="0" max="1" step="0.01"/>
    +            </fieldset>
    +            <fieldset id="webgl" style="display: none">
    +              <label>hue</label>
    +              <input id="hue" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +              <label>saturation</label>
    +              <input id="saturation" type="range" min="0" max="5" step="0.01"/>
    +              <label>contrast</label>
    +              <input id="contrast" type="range" min="0" max="2" step="0.01"/>
    +              <label>brightness</label>
    +              <input id="brightness" type="range" min="-1" max="1" step="0.01"/>
    +            </fieldset>
    +          </form>
    +        </div>
    +
    +        <div class="span4">
    +          <form class="">
    +            <fieldset>
    +              <legend>View</legend>
    +              <label>rotation</label>
    +              <input id="rotation" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +              <label>resolution</label>
    +              <input id="resolution" type="number" min="0" step="250"/>
    +            </fieldset>
    +          </form>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=bind-input" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/bind-input.js b/mapbox-gl/examples/bind-input.js
    new file mode 100644
    index 0000000000..c9bc9685e2
    --- /dev/null
    +++ b/mapbox-gl/examples/bind-input.js
    @@ -0,0 +1,60 @@
    +function checkWebGL(evt) {
    +  document.getElementById('no-webgl').style.display =
    +      ol.has.WEBGL ? 'none' : '';
    +  document.getElementById('has-webgl').style.display =
    +      ol.has.WEBGL && !evt.glContext ? '' : 'none';
    +  document.getElementById('webgl').style.display =
    +      evt.glContext ? '' : 'none';
    +}
    +
    +var layer = new ol.layer.Tile({
    +  source: new ol.source.OSM()
    +});
    +layer.once('precompose', checkWebGL);
    +
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 2
    +});
    +
    +var map = new ol.Map({
    +  layers: [layer],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +var visible = new ol.dom.Input(document.getElementById('visible'));
    +visible.bindTo('checked', layer, 'visible');
    +
    +var opacity = new ol.dom.Input(document.getElementById('opacity'));
    +opacity.bindTo('value', layer, 'opacity')
    +    .transform(parseFloat, String);
    +
    +var hue = new ol.dom.Input(document.getElementById('hue'));
    +hue.bindTo('value', layer, 'hue')
    +    .transform(parseFloat, String);
    +
    +var saturation = new ol.dom.Input(document.getElementById('saturation'));
    +saturation.bindTo('value', layer, 'saturation')
    +    .transform(parseFloat, String);
    +
    +var contrast = new ol.dom.Input(document.getElementById('contrast'));
    +contrast.bindTo('value', layer, 'contrast')
    +    .transform(parseFloat, String);
    +
    +var brightness = new ol.dom.Input(document.getElementById('brightness'));
    +brightness.bindTo('value', layer, 'brightness')
    +    .transform(parseFloat, String);
    +
    +
    +var rotation = new ol.dom.Input(document.getElementById('rotation'));
    +rotation.bindTo('value', view, 'rotation').transform(parseFloat, String);
    +
    +var resolution = new ol.dom.Input(document.getElementById('resolution'));
    +resolution.bindTo('value', view, 'resolution').transform(parseFloat, String);
    diff --git a/mapbox-gl/examples/bing-maps-require.js b/mapbox-gl/examples/bing-maps-require.js
    new file mode 100644
    index 0000000000..6c7285a11d
    --- /dev/null
    +++ b/mapbox-gl/examples/bing-maps-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/bing-maps.html b/mapbox-gl/examples/bing-maps.html
    new file mode 100644
    index 0000000000..a97c1dadcb
    --- /dev/null
    +++ b/mapbox-gl/examples/bing-maps.html
    @@ -0,0 +1,60 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Bing Maps example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +
    +          <select id="layer-select">
    +            <option value="Aerial">Aerial</option>
    +            <option value="AerialWithLabels" selected>Aerial with labels</option>
    +            <option value="Road">Road</option>
    +            <option value="collinsBart">Collins Bart</option>
    +            <option value="ordnanceSurvey">Ordnance Survey</option>
    +          </select>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Bing Maps example</h4>
    +          <p id="shortdesc">Example of a Bing Maps layer.</p>
    +          <div id="docs">
    +            <p>When the Bing Maps tile service doesn't have tiles for a given resolution and region it returns "placeholder" tiles indicating that. Zoom the map beyond level 19 to see the "placeholder" tiles. If you want OpenLayers to display stretched tiles in place of "placeholder" tiles beyond zoom level 19 then set <code>maxZoom</code> to <code>19</code> in the options passed to <code>ol.source.BingMaps</code>.</p>
    +            <p>See the <a href="bing-maps.js" target="_blank">bing-maps.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">bing, bing-maps</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=bing-maps" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/bing-maps.js b/mapbox-gl/examples/bing-maps.js
    new file mode 100644
    index 0000000000..b03839303f
    --- /dev/null
    +++ b/mapbox-gl/examples/bing-maps.js
    @@ -0,0 +1,43 @@
    +var styles = [
    +  'Road',
    +  'Aerial',
    +  'AerialWithLabels',
    +  'collinsBart',
    +  'ordnanceSurvey'
    +];
    +var layers = [];
    +var i, ii;
    +for (i = 0, ii = styles.length; i < ii; ++i) {
    +  layers.push(new ol.layer.Tile({
    +    visible: false,
    +    preload: Infinity,
    +    source: new ol.source.BingMaps({
    +      key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +      imagerySet: styles[i]
    +      // use maxZoom 19 to see stretched tiles instead of the BingMaps
    +      // "no photos at this zoom level" tiles
    +      // maxZoom: 19
    +    })
    +  }));
    +}
    +var map = new ol.Map({
    +  layers: layers,
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  // Improve user experience by loading tiles while dragging/zooming. Will make
    +  // zooming choppy on mobile or slow devices.
    +  loadTilesWhileInteracting: true,
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-6655.5402445057125, 6709968.258934638],
    +    zoom: 13
    +  })
    +});
    +
    +$('#layer-select').change(function() {
    +  var style = $(this).find(':selected').val();
    +  var i, ii;
    +  for (i = 0, ii = layers.length; i < ii; ++i) {
    +    layers[i].setVisible(styles[i] == style);
    +  }
    +});
    +$('#layer-select').trigger('change');
    diff --git a/mapbox-gl/examples/box-selection-require.js b/mapbox-gl/examples/box-selection-require.js
    new file mode 100644
    index 0000000000..e39d6608fc
    --- /dev/null
    +++ b/mapbox-gl/examples/box-selection-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.DragBox');
    +goog.require('ol.interaction.Select');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/box-selection.html b/mapbox-gl/examples/box-selection.html
    new file mode 100644
    index 0000000000..4fd498b1b6
    --- /dev/null
    +++ b/mapbox-gl/examples/box-selection.html
    @@ -0,0 +1,59 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Box selection example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">Box selection example</h4>
    +          <p id="shortdesc">Using a DragBox interaction to select features.</p>
    +          <div id="docs">
    +            <p>This example shows how to use a <code>DragBox</code> interaction to select features. Selected features are added
    +            to the feature overlay of a select interaction (<code>ol.interaction.Select</code>) for highlighting.</p>
    +            <p>Use <code>SHIFT+drag</code> to draw boxes.</p>
    +            <p>See the <a href="box-selection.js" target="_blank">box-selection.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">DragBox, feature, selection, box</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=box-selection" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/box-selection.js b/mapbox-gl/examples/box-selection.js
    new file mode 100644
    index 0000000000..548c176e76
    --- /dev/null
    +++ b/mapbox-gl/examples/box-selection.js
    @@ -0,0 +1,67 @@
    +var vectorSource = new ol.source.GeoJSON({
    +  projection: 'EPSG:3857',
    +  url: 'data/geojson/countries.geojson'
    +});
    +
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    new ol.layer.Vector({
    +      source: vectorSource
    +    })
    +  ],
    +  renderer: 'canvas',
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +// a normal select interaction to handle click
    +var select = new ol.interaction.Select();
    +map.addInteraction(select);
    +
    +var selectedFeatures = select.getFeatures();
    +
    +// a DragBox interaction used to select features by drawing boxes
    +var dragBox = new ol.interaction.DragBox({
    +  condition: ol.events.condition.shiftKeyOnly,
    +  style: new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: [0, 0, 255, 1]
    +    })
    +  })
    +});
    +
    +map.addInteraction(dragBox);
    +
    +var infoBox = document.getElementById('info');
    +
    +dragBox.on('boxend', function(e) {
    +  // features that intersect the box are added to the collection of
    +  // selected features, and their names are displayed in the "info"
    +  // div
    +  var info = [];
    +  var extent = dragBox.getGeometry().getExtent();
    +  vectorSource.forEachFeatureIntersectingExtent(extent, function(feature) {
    +    selectedFeatures.push(feature);
    +    info.push(feature.get('name'));
    +  });
    +  if (info.length > 0) {
    +    infoBox.innerHTML = info.join(', ');
    +  }
    +});
    +
    +// clear selection when drawing a new box and when clicking on the map
    +dragBox.on('boxstart', function(e) {
    +  selectedFeatures.clear();
    +  infoBox.innerHTML = '&nbsp;';
    +});
    +map.on('click', function() {
    +  selectedFeatures.clear();
    +  infoBox.innerHTML = '&nbsp;';
    +});
    diff --git a/mapbox-gl/examples/brightness-contrast-require.js b/mapbox-gl/examples/brightness-contrast-require.js
    new file mode 100644
    index 0000000000..b95503f517
    --- /dev/null
    +++ b/mapbox-gl/examples/brightness-contrast-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/brightness-contrast.html b/mapbox-gl/examples/brightness-contrast.html
    new file mode 100644
    index 0000000000..fb499eeba1
    --- /dev/null
    +++ b/mapbox-gl/examples/brightness-contrast.html
    @@ -0,0 +1,74 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Brightness/contrast example</title>
    +    <style>
    +      #reset-brightness {
    +        min-width: 138px;
    +      }
    +      #reset-contrast {
    +        min-width: 120px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +
    +          <div id="no-webgl" class="alert alert-error" style="display: none">
    +            This example requires a browser that supports <a href="http://get.webgl.org/">WebGL</a>.
    +          </div>
    +
    +          <div class="btn-group">
    +            <button id="increase-brightness"><i class="icon-plus"></i></button>
    +            <button id="reset-brightness">Brightness</button>
    +            <button id="decrease-brightness"><i class="icon-minus"></i></button>
    +          </div>
    +          <div class="btn-group">
    +            <button id="increase-contrast"><i class="icon-plus"></i></button>
    +            <button id="reset-contrast">Contrast</button>
    +            <button id="decrease-contrast"><i class="icon-minus"></i></button>
    +          </div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Brightness/contrast example</h4>
    +          <p id="shortdesc">Example of brightness/contrast control on the client (WebGL only).</p>
    +          <div id="docs">
    +            <p>See the <a href="brightness-contrast.js" target="_blank">brightness-contrast.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">brightness, contrast, webgl</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=brightness-contrast" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/brightness-contrast.js b/mapbox-gl/examples/brightness-contrast.js
    new file mode 100644
    index 0000000000..4bbcb01243
    --- /dev/null
    +++ b/mapbox-gl/examples/brightness-contrast.js
    @@ -0,0 +1,68 @@
    +function setResetBrightnessButtonHTML() {
    +  resetBrightness.innerHTML = 'Brightness (' +
    +      layer.getBrightness().toFixed(3) + ')';
    +}
    +
    +function setResetContrastButtonHTML() {
    +  resetContrast.innerHTML = 'Contrast (' + layer.getContrast().toFixed(3) + ')';
    +}
    +
    +if (!ol.has.WEBGL) {
    +  var info = document.getElementById('no-webgl');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +} else {
    +  var layer = new ol.layer.Tile({
    +    source: new ol.source.MapQuest({layer: 'sat'})
    +  });
    +
    +  var map = new ol.Map({
    +    layers: [layer],
    +    renderer: 'webgl',
    +    target: 'map',
    +    view: new ol.View({
    +      center: [0, 0],
    +      zoom: 2
    +    })
    +  });
    +
    +  var increaseBrightness = document.getElementById('increase-brightness');
    +  var resetBrightness = document.getElementById('reset-brightness');
    +  var decreaseBrightness = document.getElementById('decrease-brightness');
    +
    +  setResetBrightnessButtonHTML();
    +
    +  increaseBrightness.addEventListener('click', function() {
    +    layer.setBrightness(Math.min(layer.getBrightness() + 0.125, 1));
    +    setResetBrightnessButtonHTML();
    +  }, false);
    +  resetBrightness.addEventListener('click', function() {
    +    layer.setBrightness(0);
    +    setResetBrightnessButtonHTML();
    +  }, false);
    +  decreaseBrightness.addEventListener('click', function() {
    +    layer.setBrightness(Math.max(layer.getBrightness() - 0.125, -1));
    +    setResetBrightnessButtonHTML();
    +  }, false);
    +
    +  var increaseContrast = document.getElementById('increase-contrast');
    +  var resetContrast = document.getElementById('reset-contrast');
    +  var decreaseContrast = document.getElementById('decrease-contrast');
    +
    +  setResetContrastButtonHTML();
    +
    +  increaseContrast.addEventListener('click', function() {
    +    layer.setContrast(layer.getContrast() + 0.125);
    +    setResetContrastButtonHTML();
    +  }, false);
    +  resetContrast.addEventListener('click', function() {
    +    layer.setContrast(1);
    +    setResetContrastButtonHTML();
    +  }, false);
    +  decreaseContrast.addEventListener('click', function() {
    +    layer.setContrast(Math.max(layer.getContrast() - 0.125, 0));
    +    setResetContrastButtonHTML();
    +  }, false);
    +}
    diff --git a/mapbox-gl/examples/button-title-require.js b/mapbox-gl/examples/button-title-require.js
    new file mode 100644
    index 0000000000..7c5fe7a237
    --- /dev/null
    +++ b/mapbox-gl/examples/button-title-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/button-title.html b/mapbox-gl/examples/button-title.html
    new file mode 100644
    index 0000000000..1247a7accd
    --- /dev/null
    +++ b/mapbox-gl/examples/button-title.html
    @@ -0,0 +1,62 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      .tooltip-inner {
    +        white-space: nowrap;
    +      }
    +    </style>
    +    <title>ol3 custom tooltips example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <h4 id="title">Custom tooltips</h4>
    +          <p id="shortdesc">
    +            This example shows how to customize the buttons tooltips with
    +            <a href="http://getbootstrap.com/javascript/#tooltips">Bootstrap</a>.
    +          </p>
    +          <div id="docs">
    +            <p>
    +              See the <a href="button-title.js" target="_blank">button-title.js source</a> to see how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">
    +            custom, tooltip
    +          </div>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=button-title" type="text/javascript"></script>
    +
    +  </body>
    +
    +</html>
    diff --git a/mapbox-gl/examples/button-title.js b/mapbox-gl/examples/button-title.js
    new file mode 100644
    index 0000000000..3949034f16
    --- /dev/null
    +++ b/mapbox-gl/examples/button-title.js
    @@ -0,0 +1,22 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-8730000, 5930000],
    +    rotation: Math.PI / 5,
    +    zoom: 8
    +  })
    +});
    +
    +
    +$('.ol-zoom-in, .ol-zoom-out').tooltip({
    +  placement: 'right'
    +});
    +$('.ol-rotate-reset, .ol-attribution button[title]').tooltip({
    +  placement: 'left'
    +});
    diff --git a/mapbox-gl/examples/canvas-tiles-require.js b/mapbox-gl/examples/canvas-tiles-require.js
    new file mode 100644
    index 0000000000..80f4539a3c
    --- /dev/null
    +++ b/mapbox-gl/examples/canvas-tiles-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.TileDebug');
    +goog.require('ol.tilegrid.XYZ');
    diff --git a/mapbox-gl/examples/canvas-tiles.html b/mapbox-gl/examples/canvas-tiles.html
    new file mode 100644
    index 0000000000..90eb1866ca
    --- /dev/null
    +++ b/mapbox-gl/examples/canvas-tiles.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Canvas tiles example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Canvas tiles example</h4>
    +          <p id="shortdesc">Renders tiles with coordinates for debugging.</p>
    +          <div id="docs">
    +            <p>The black grid tiles are generated on the client with an HTML5 canvas. Note that the tile coordinates are ol3 normalized tile coordinates (origin bottom left), not OSM tile coordinates (origin top left).</p>
    +            <p>See the <a href="canvas-tiles.js" target="_blank">canvas-tiles.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">layers, openstreetmap, canvas</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=canvas-tiles" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/canvas-tiles.js b/mapbox-gl/examples/canvas-tiles.js
    new file mode 100644
    index 0000000000..892ae05f5b
    --- /dev/null
    +++ b/mapbox-gl/examples/canvas-tiles.js
    @@ -0,0 +1,27 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    new ol.layer.Tile({
    +      source: new ol.source.TileDebug({
    +        projection: 'EPSG:3857',
    +        tileGrid: new ol.tilegrid.XYZ({
    +          maxZoom: 22
    +        })
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: ol.proj.transform(
    +        [-0.1275, 51.507222], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 10
    +  })
    +});
    diff --git a/mapbox-gl/examples/center-require.js b/mapbox-gl/examples/center-require.js
    new file mode 100644
    index 0000000000..ea5057c53c
    --- /dev/null
    +++ b/mapbox-gl/examples/center-require.js
    @@ -0,0 +1,13 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/center.html b/mapbox-gl/examples/center.html
    new file mode 100644
    index 0000000000..bf56e78d0c
    --- /dev/null
    +++ b/mapbox-gl/examples/center.html
    @@ -0,0 +1,131 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style>
    +      .mapcontainer {
    +        position: relative;
    +        margin-bottom: 20px;
    +      }
    +      .map {
    +        width: 1000px;
    +        height: 600px;
    +      }
    +      div.ol-zoom {
    +        top: 178px;
    +        left: 158px;
    +      }
    +      div.ol-attribution {
    +        bottom: 30px;
    +        right: 50px;
    +      }
    +      .padding-top {
    +        position: absolute;
    +        top: 0;
    +        left: 0px;
    +        width: 1000px;
    +        height: 170px;
    +        background: rgba(255, 255, 255, 0.5);
    +      }
    +      .padding-left {
    +        position: absolute;
    +        top: 170px;
    +        left: 0;
    +        width: 150px;
    +        height: 400px;
    +        background: rgba(255, 255, 255, 0.5);
    +      }
    +      .padding-right {
    +        position: absolute;
    +        top: 170px;
    +        left: 950px;
    +        width: 50px;
    +        height: 400px;
    +        background: rgba(255, 255, 255, 0.5);
    +      }
    +      .padding-bottom {
    +        position: absolute;
    +        top: 570px;
    +        left: 0px;
    +        width: 1000px;
    +        height: 30px;
    +        background: rgba(255, 255, 255, 0.5);
    +      }
    +      .center {
    +        position: absolute;
    +        border: solid 1px black;
    +        top: 490px;
    +        left: 560px;
    +        width: 20px;
    +        height: 20px;
    +      }
    +    </style>
    +    <title>Advanced View Positioning example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png">OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12 mapcontainer">
    +          <div id="map" class="map"></div>
    +          <div class="padding-top"></div>
    +          <div class="padding-left"></div>
    +          <div class="padding-right"></div>
    +          <div class="padding-bottom"></div>
    +          <div class="center"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <button id="zoomtoswitzerlandbest">Zoom to Switzerland</button> (best fit),<br/>
    +          <button id="zoomtoswitzerlandconstrained">Zoom to Switzerland</button> (respect resolution constraint).<br/>
    +          <button id="zoomtoswitzerlandnearest">Zoom to Switzerland</button> (nearest),<br/>
    +          <button id="zoomtolausanne">Zoom to Lausanne</button> (with min resolution),<br/>
    +          <button id="centerlausanne">Center on Lausanne</button>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Advanced View Positioning example</h4>
    +          <p id="shortdesc">This example demonstrates how a map's view can be
    +            adjusted so a geometry or coordinate is positioned at a specific
    +            pixel location. The map above has top, right, bottom, and left
    +            padding applied inside the viewport. The view's <code>fitGeometry</code> method
    +            is used to fit a geometry in the view with the same padding. The
    +            view's <code>centerOn</code> method is used to position a coordinate (Lausanne)
    +            at a specific pixel location (the center of the black box).</p>
    +          <div id="docs">
    +            <p>Use <code>Alt</code>+<code>Shift</code>+drag to rotate the map.</p>
    +            <p>See the <a href="center.js" target="_blank">center.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">center, rotation, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="loader.js?id=center" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/center.js b/mapbox-gl/examples/center.js
    new file mode 100644
    index 0000000000..edaea02a67
    --- /dev/null
    +++ b/mapbox-gl/examples/center.js
    @@ -0,0 +1,119 @@
    +var source = new ol.source.GeoJSON({
    +  projection: 'EPSG:3857',
    +  url: 'data/geojson/switzerland.geojson'
    +});
    +var style = new ol.style.Style({
    +  fill: new ol.style.Fill({
    +    color: 'rgba(255, 255, 255, 0.6)'
    +  }),
    +  stroke: new ol.style.Stroke({
    +    color: '#319FD3',
    +    width: 1
    +  }),
    +  image: new ol.style.Circle({
    +    radius: 5,
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 255, 0.6)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#319FD3',
    +      width: 1
    +    })
    +  })
    +});
    +var vectorLayer = new ol.layer.Vector({
    +  source: source,
    +  style: style
    +});
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 1
    +});
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    vectorLayer
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +var zoomtoswitzerlandbest = document.getElementById('zoomtoswitzerlandbest');
    +zoomtoswitzerlandbest.addEventListener('click', function() {
    +  var feature = source.getFeatures()[0];
    +  var polygon = /** @type {ol.geom.SimpleGeometry} */ (feature.getGeometry());
    +  var size = /** @type {ol.Size} */ (map.getSize());
    +  view.fitGeometry(
    +      polygon,
    +      size,
    +      {
    +        padding: [170, 50, 30, 150],
    +        constrainResolution: false
    +      }
    +  );
    +}, false);
    +
    +var zoomtoswitzerlandconstrained =
    +    document.getElementById('zoomtoswitzerlandconstrained');
    +zoomtoswitzerlandconstrained.addEventListener('click', function() {
    +  var feature = source.getFeatures()[0];
    +  var polygon = /** @type {ol.geom.SimpleGeometry} */ (feature.getGeometry());
    +  var size = /** @type {ol.Size} */ (map.getSize());
    +  view.fitGeometry(
    +      polygon,
    +      size,
    +      {
    +        padding: [170, 50, 30, 150]
    +      }
    +  );
    +}, false);
    +
    +var zoomtoswitzerlandnearest =
    +    document.getElementById('zoomtoswitzerlandnearest');
    +zoomtoswitzerlandnearest.addEventListener('click', function() {
    +  var feature = source.getFeatures()[0];
    +  var polygon = /** @type {ol.geom.SimpleGeometry} */ (feature.getGeometry());
    +  var size = /** @type {ol.Size} */ (map.getSize());
    +  view.fitGeometry(
    +      polygon,
    +      size,
    +      {
    +        padding: [170, 50, 30, 150],
    +        nearest: true
    +      }
    +  );
    +}, false);
    +
    +var zoomtolausanne = document.getElementById('zoomtolausanne');
    +zoomtolausanne.addEventListener('click', function() {
    +  var feature = source.getFeatures()[1];
    +  var point = /** @type {ol.geom.SimpleGeometry} */ (feature.getGeometry());
    +  var size = /** @type {ol.Size} */ (map.getSize());
    +  view.fitGeometry(
    +      point,
    +      size,
    +      {
    +        padding: [170, 50, 30, 150],
    +        minResolution: 50
    +      }
    +  );
    +}, false);
    +
    +var centerlausanne = document.getElementById('centerlausanne');
    +centerlausanne.addEventListener('click', function() {
    +  var feature = source.getFeatures()[1];
    +  var point = /** @type {ol.geom.Point} */ (feature.getGeometry());
    +  var size = /** @type {ol.Size} */ (map.getSize());
    +  view.centerOn(
    +      point.getCoordinates(),
    +      size,
    +      [570, 500]
    +  );
    +}, false);
    diff --git a/mapbox-gl/examples/cluster-require.js b/mapbox-gl/examples/cluster-require.js
    new file mode 100644
    index 0000000000..2e648b4644
    --- /dev/null
    +++ b/mapbox-gl/examples/cluster-require.js
    @@ -0,0 +1,14 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Cluster');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.style.Text');
    diff --git a/mapbox-gl/examples/cluster.html b/mapbox-gl/examples/cluster.html
    new file mode 100644
    index 0000000000..13b29c0249
    --- /dev/null
    +++ b/mapbox-gl/examples/cluster.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Clustering example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Clustering example</h4>
    +          <p id="shortdesc">Example of using <code>ol.Cluster</code>.</p>
    +          <div id="docs">
    +            <p>See the <a href="cluster.js" target="_blank">cluster.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">cluster vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=cluster" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/cluster.js b/mapbox-gl/examples/cluster.js
    new file mode 100644
    index 0000000000..b80a409f80
    --- /dev/null
    +++ b/mapbox-gl/examples/cluster.js
    @@ -0,0 +1,64 @@
    +var count = 20000;
    +var features = new Array(count);
    +var e = 4500000;
    +for (var i = 0; i < count; ++i) {
    +  var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
    +  features[i] = new ol.Feature(new ol.geom.Point(coordinates));
    +}
    +
    +var source = new ol.source.Vector({
    +  features: features
    +});
    +
    +var clusterSource = new ol.source.Cluster({
    +  distance: 40,
    +  source: source
    +});
    +
    +var styleCache = {};
    +var clusters = new ol.layer.Vector({
    +  source: clusterSource,
    +  style: function(feature, resolution) {
    +    var size = feature.get('features').length;
    +    var style = styleCache[size];
    +    if (!style) {
    +      style = [new ol.style.Style({
    +        image: new ol.style.Circle({
    +          radius: 10,
    +          stroke: new ol.style.Stroke({
    +            color: '#fff'
    +          }),
    +          fill: new ol.style.Fill({
    +            color: '#3399CC'
    +          })
    +        }),
    +        text: new ol.style.Text({
    +          text: size.toString(),
    +          fill: new ol.style.Fill({
    +            color: '#fff'
    +          })
    +        })
    +      })];
    +      styleCache[size] = style;
    +    }
    +    return style;
    +  }
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var raw = new ol.layer.Vector({
    +  source: source
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, clusters],
    +  renderer: 'canvas',
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/custom-controls-require.js b/mapbox-gl/examples/custom-controls-require.js
    new file mode 100644
    index 0000000000..eb3b04a96d
    --- /dev/null
    +++ b/mapbox-gl/examples/custom-controls-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.Control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/custom-controls.html b/mapbox-gl/examples/custom-controls.html
    new file mode 100644
    index 0000000000..5e5cfb4050
    --- /dev/null
    +++ b/mapbox-gl/examples/custom-controls.html
    @@ -0,0 +1,64 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      .rotate-north {
    +        top: 65px;
    +        left: .5em;
    +      }
    +      .ol-touch .rotate-north {
    +        top: 80px;
    +      }
    +    </style>
    +    <title>ol3 custom controls example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <h4 id="title">Custom controls</h4>
    +          <p id="shortdesc">This example shows how to create custom controls.</p>
    +          <div id="docs">
    +            <p>
    +              This example creates a "rotate to north" button.
    +              See the <a href="custom-controls.js" target="_blank">custom-controls.js
    +              source</a> to see how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">
    +            custom, control
    +          </div>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=custom-controls" type="text/javascript"></script>
    +
    +  </body>
    +
    +</html>
    diff --git a/mapbox-gl/examples/custom-controls.js b/mapbox-gl/examples/custom-controls.js
    new file mode 100644
    index 0000000000..3895209ed5
    --- /dev/null
    +++ b/mapbox-gl/examples/custom-controls.js
    @@ -0,0 +1,72 @@
    +/**
    + * Define a namespace for the application.
    + */
    +window.app = {};
    +var app = window.app;
    +
    +
    +//
    +// Define rotate to north control.
    +//
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {Object=} opt_options Control options.
    + */
    +app.RotateNorthControl = function(opt_options) {
    +
    +  var options = opt_options || {};
    +
    +  var button = document.createElement('button');
    +  button.innerHTML = 'N';
    +
    +  var this_ = this;
    +  var handleRotateNorth = function(e) {
    +    this_.getMap().getView().setRotation(0);
    +  };
    +
    +  button.addEventListener('click', handleRotateNorth, false);
    +  button.addEventListener('touchstart', handleRotateNorth, false);
    +
    +  var element = document.createElement('div');
    +  element.className = 'rotate-north ol-unselectable ol-control';
    +  element.appendChild(button);
    +
    +  ol.control.Control.call(this, {
    +    element: element,
    +    target: options.target
    +  });
    +
    +};
    +ol.inherits(app.RotateNorthControl, ol.control.Control);
    +
    +
    +//
    +// Create map, giving it a rotate to north control.
    +//
    +
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }).extend([
    +    new app.RotateNorthControl()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2,
    +    rotation: 1
    +  })
    +});
    diff --git a/mapbox-gl/examples/d3-require.js b/mapbox-gl/examples/d3-require.js
    new file mode 100644
    index 0000000000..aeca2588ca
    --- /dev/null
    +++ b/mapbox-gl/examples/d3-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.ImageCanvas');
    +goog.require('ol.source.Stamen');
    diff --git a/mapbox-gl/examples/d3.html b/mapbox-gl/examples/d3.html
    new file mode 100644
    index 0000000000..aa1825d802
    --- /dev/null
    +++ b/mapbox-gl/examples/d3.html
    @@ -0,0 +1,54 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>d3 integration example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">d3 integration example</h4>
    +          <p id="shortdesc">Example of using ol3 and d3 together.</p>
    +          <div id="docs">
    +            <p>The example loads TopoJSON geometries and uses d3 (<code>d3.geo.path</code>) to render these geometries to a canvas element that is then used as the image of an ol3 image layer.</p>
    +            <p>See the <a href="d3.js" target="_blank">d3.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">d3</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="http://d3js.org/d3.v3.min.js"></script>
    +    <script src="http://d3js.org/topojson.v1.min.js"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=d3" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/d3.js b/mapbox-gl/examples/d3.js
    new file mode 100644
    index 0000000000..3802407056
    --- /dev/null
    +++ b/mapbox-gl/examples/d3.js
    @@ -0,0 +1,83 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.Stamen({
    +        layer: 'watercolor'
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([-97, 38], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 4
    +  })
    +});
    +
    +
    +/**
    + * Load the topojson data and create an ol.layer.Image for that data.
    + */
    +d3.json('data/topojson/us.json', function(error, us) {
    +  var features = topojson.feature(us, us.objects.counties);
    +
    +  /**
    +   * This function uses d3 to render the topojson features to a canvas.
    +   * @param {ol.Extent} extent Extent.
    +   * @param {number} resolution Resolution.
    +   * @param {number} pixelRatio Pixel ratio.
    +   * @param {ol.Size} size Size.
    +   * @param {ol.proj.Projection} projection Projection.
    +   * @return {HTMLCanvasElement}
    +   */
    +  var canvasFunction = function(extent, resolution, pixelRatio,
    +      size, projection) {
    +    var canvasWidth = size[0];
    +    var canvasHeight = size[1];
    +
    +    var canvas = d3.select(document.createElement('canvas'));
    +    canvas.attr('width', canvasWidth).attr('height', canvasHeight);
    +
    +    var context = canvas.node().getContext('2d');
    +
    +    var d3Projection = d3.geo.mercator().scale(1).translate([0, 0]);
    +    var d3Path = d3.geo.path().projection(d3Projection);
    +
    +    var pixelBounds = d3Path.bounds(features);
    +    var pixelBoundsWidth = pixelBounds[1][0] - pixelBounds[0][0];
    +    var pixelBoundsHeight = pixelBounds[1][1] - pixelBounds[0][1];
    +
    +    var geoBounds = d3.geo.bounds(features);
    +    var geoBoundsLeftBottom = ol.proj.transform(
    +        geoBounds[0], 'EPSG:4326', projection);
    +    var geoBoundsRightTop = ol.proj.transform(
    +        geoBounds[1], 'EPSG:4326', projection);
    +    var geoBoundsWidth = geoBoundsRightTop[0] - geoBoundsLeftBottom[0];
    +    if (geoBoundsWidth < 0) {
    +      geoBoundsWidth += ol.extent.getWidth(projection.getExtent());
    +    }
    +    var geoBoundsHeight = geoBoundsRightTop[1] - geoBoundsLeftBottom[1];
    +
    +    var widthResolution = geoBoundsWidth / pixelBoundsWidth;
    +    var heightResolution = geoBoundsHeight / pixelBoundsHeight;
    +    var r = Math.max(widthResolution, heightResolution);
    +    var scale = r / (resolution / pixelRatio);
    +
    +    var center = ol.proj.transform(ol.extent.getCenter(extent),
    +        projection, 'EPSG:4326');
    +    d3Projection.scale(scale).center(center)
    +        .translate([canvasWidth / 2, canvasHeight / 2]);
    +    d3Path = d3Path.projection(d3Projection).context(context);
    +    d3Path(features);
    +    context.stroke();
    +
    +    return canvas[0][0];
    +  };
    +
    +  var layer = new ol.layer.Image({
    +    source: new ol.source.ImageCanvas({
    +      canvasFunction: canvasFunction,
    +      projection: 'EPSG:3857'
    +    })
    +  });
    +  map.addLayer(layer);
    +});
    diff --git a/mapbox-gl/examples/data/Butterfly.png b/mapbox-gl/examples/data/Butterfly.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..0a32d018ce20d1b1e81e0af890d09ebb1410b029
    GIT binary patch
    literal 62674
    zcmV)!K#;$QP)<h;3K|Lk000e1NJLTq00961009691^@s6Tym&p00006VoOIv0RI60
    z0RN!9r;`8x010qNS#tmY5_A9n5_AFHW*>L}000McNliru-U|l_4GGOeBQXE~AOJ~3
    zK~#9!?45U*T}9cze>3OYvb`po-V>6LLLhWPFQPQ*O{9o?HdGM(Kt<3G>{1jJ5D^>F
    zq$u421nD&m(nv3x?S1dP=gfS6%zJjvZZ?%9gvk3m_u1XMx6I7j>%d3w5qtz6!AI~B
    zd<6e}pguZ+kKiNN=|KbIfwO_Vfqb9}cmVh&U_PjY6$57hHv*5!Vv2TAB=~^$A>YG)
    z6TSdO05gD+59#_#fhT|tz;58t0{nSLFDwas3s@`CC%&Ls;7(w`E<!|ufzJXz0`365
    z0elh|@*%RI{+sYM1^5Z@7SJlp(<VZ%Y$soP8n_;K2ABslZiMlBKW5AOw>u01UK2uU
    z0qTG?z$*FKD*syn?E4`nrjunq7RU)PWn_N__%|?pC*Q+=3%-)TcVgNyBYSHHvhv)!
    zz|fu4#J@YX&J8_6p~D;y$lvk*|7Cl}0E&PYfpI{METXT^OaT4l@DByv_z)7*bl^Eb
    zPGbx?kvmWf6ar1a0V1(?Hu!JBS0Qi@@Nr>^b;2a^J(dX0-UJR4yzMN^|Bm2jLtxNY
    zJf#S&#dOhO1fJ~C0($_nHuuZ7#HY;z9s>501MW>sRwVGtz;Gd|GSQ;{5cBiH0-pF)
    zBZ2DpUK2o73fmg0PM}i=>rAl*R_{c6*j4dWA`E=E2;v6dR^Sw|WU5grM5(xPn**f8
    zN*E4I6>VvEg8e)R?6VQ^5LT3qX7unGj2>~gD$1{-wr*i>0d7Q)C}5Jfbek=GHW#i0
    zjub7C1IY7Gv4i%2CBUzN&j@p@1<HjvPWTWBfWv@;MN8BH?>X*Irykjh#J~3;F>@d5
    ze|{K&uUIR=n#XaG+MNY<HGK60t`#%>@51yi0E<Nc1Jsrz2(KDtz2o~?EqnEV?~A~g
    zFZ(<exOykp)2YBwT|P{zq^I1{gi|J*%07Etqy`O|u1bmq5r)~`pV1I=0T63cZ)*XN
    z1TK`{TT2tD`}ZI*=O7Y?mRZ$hrog$l6vYUU&Vgv3eBeIdf)9xR_^RxuPBh{fKs#YJ
    zchgeBoPmBR)R%|jT{jku=QoJ<yjS+RGx)>1621-)x8&Qn^j8#<F}UER2Q-f>AoK7v
    z5*G|Y4^9Er+E_eb74VYa)Zl`-6F%U5{W{{pRH~F9*D6Z}mBdUKeJq0pPE*!~fMMa{
    zf7k_!pSC?*12_V>j_y>T2YhZ@3xHGQpbbi8t{sCnwHReAlqY(`5sm?#$3<MTvZzq}
    zPc0ihW+!L<GO+;K#W#CTh^7%^%o7csI`(yPPuF1$aAFnu<cc5*sz%JO>vj^&va8^0
    zGVnKC)+HwzJPLvZk@ibzT7NMCe?SUWWZ)00yBNX%Hv#_#94-566qn!zKDbQI#Xv>0
    zLSq7q38VjozDe7mL-$v?TnDSyylH2?aFboU^jW)JY`|qWe$y-Faq_kb0*(Q$0Zsx6
    zfQ|x>n&<W-S?obz;mB9Xt;%}3)C)~xuFDKjz}Il;jLUHa3<;n=F7iB0G|kRx{{K^$
    zzExrY36wf(YN6%Kf#~KO{HGC9pTmBBC>{#+`FDMb35rGg9xT4~j)q+gUr`WqcVYG%
    zN-0s3N`zK1(0X(wjX$4&-)Z3FHwn@j_ANy{U5i=KN%Z=A#4V~2OCl-4@_`RvZ;OD@
    zeFpr%XzkIjVw|dLSVF?jWAMOfloStELC|RzFMWpAwwefct8uB^DY0N+HJh2%^*8YR
    z0P&m7ltYRjH@yU1;yJPV&sxmd4E9IkLiLIMBpzLd`T9c7VnQW^vkv&4_&F8AR0DDO
    z^pivy?~DQ<BCdp((pj{puNW1m>BUZHo<9V=wiA2(62gDfVkE}aEc^Q*E}FihU{}T0
    zr-k_eg0zRKN?2BOcG<gy^1tRug{bBLE}lnlN)`ITA!rX6laIP;p$f5fF7Ur1Bp<`2
    zL@VUEhv_>*vb~@Z7`1`_SpZ`~8k&}~`)<cGaKIF0tOX2Oc`BVMvM;~&TP4|mLGtMR
    z`CIJ*Oc$wpo-jft7GMbbryK(W7Jj%CbKCADK3RoMDQF6?Xip2Fd|%ARlO#xnm@AFI
    zabmXQ?}P$iTFkusC2%@0n=mk6m`Cu3)r480zN$3p#tHbP$u3JO5Ub%3@mqHU?8^9x
    z0%So#VW$+LhNV#n)q{QmAoqGR_Waod2bQ2N80_eJejE1s#e`xp^p^nVQlMU3yim^X
    zxE;K&%Qs+s$Afw7Ipr)haKIFW(rBeHCS=v>H|(5uABh+LhOzh9SR7!pih_Du07HOB
    zMCy0OJ}_Ert|sH2`}H!y!6`VX-2JaTgkLVl5M(1QF%-C+?leV&uvU5Qd?B>6<hynj
    z7%l5f1NQ?9fP;W3O*!m!OFV8|8s?5DN9|F9?w9D=TqI0e#B@DHqSV_Hc6EG3x$$sJ
    z9xA1w!$1b5+}bQ6@YJ*Q%I;la)n|v|DFuI9hxxzv2wTIhGpUpyZIcKCE5DZm|AR}J
    z?kG4d!jGJ-A7kJeacffZ-u=7v{3N+t2SJb}n`^~*Mv=27rf??7-e%G>tvLZ#^RPu0
    zz_mh50ZNhbP$>@;6IR~L2FxwX2!FnU@X}#u4P*>_ejY(X=(*I3mrUR%6ZokFN?G}y
    zM<rtUtT0JEaH0fwc9s&&QZc7OA*}xuO@0Poo&RY}8@~5Q4d%Ox2);Dmvkh4+E_HT`
    zAf*lX6kC>N-5S`H@s%Gl@oh=Mas8?eY=O26PTO8*Yi*eiZ!}>~d4t>)3$$s<VNqHq
    zH44x7#q6FIqy09)^^v$Hn;ik^nE4;}!TjUI+M>C2Ee(y!ZEIVN?aVY;V?rBa{^&i6
    zC0L#9!!q0=7oZ>(@eR|;+I@>rWAjj`E{am2g&USBlK}QEMK|T()3eAu-{dK*wL?;t
    zU+$LWxxKRL-bopoue(w)6}UX}*Kp;Ut+L_6gn0(-1Oi|%uFxc^`a2T1AEnAm6R69E
    zS$*3?>)$&$Q0EWEt4N}tD<Jha@B>`+U>h!Pebjbb>#mNkK@zC53aqD~BZS%<c1jV-
    zSmNnS0Pm?f%ms6GIJXT8*6ve+zHh4W-Z+57LsJ7aGOz1BFNlTKC=U<#(hj1rD`FN}
    zx)(Foifb~w>op0bmC~xXaFD9%H$jz`j!?1pCvY_pf0L|69E!Kp1t=AB2$W`Qfzeic
    zM_F7{G74DhDR`;|bHj4NMQsX{F(cEK`=;TS_-=vC0knrIVv_yETzx0D*iMO9PQcZx
    zp9}o!0~7!gW2RZ;8|P4-|Gy&xPO9n_m{W@It{92;w{^x`yO^*e<U-(8@iDXV-V<<@
    zUfauB*_H8C5&6VcDb*HQ7I$Dr<e@wTou-QkLSg9N1GiueD6LPeGJJ0=e)OXLNmP5c
    z?##;ie~YX6C=e^}7r-mqna0lB!1G45{|cbLQksggQ4AZrw=F3hrqZbrTs;6ZHLv8g
    zx9%dB>$JefdinqH49XT+08wDXdKg>mxqsH@upTz-EwS1Sd||$47qu(E+LW@~GO?SO
    zl!E)KF({?bo`>gqXgvn_L`;Gyx@)(~aV4lf>?Yg~#Q%>7<ZjUto!Bt^{VKwyY|lgX
    z6r5CrzHv7^v^N2GTA0F!3vt{=GA-{Hc6EHEyXezeJF5lL9%3sJC~G0posUqsdW9!|
    z<tVK$9&FTgG3GZGzPpH^I@9GE9f9kP+%I;Pz6gZv9q1gnp~%1Nr9Z%4Q_p4kv`?$b
    zeiKw8kw<8<7#m`2NJ;S!Mhu%luRx;eB?SB}6#!8cSa$`5bp$mkug8aKNq0Si2|KXX
    zl1wFU-5q~yLHB}|v|)d@k`S!@;*gMQMg^Q$X;iOGUjbYu>85o;&|eqne4d!&iXB(1
    zQ7!}?0uSO^N}I9P&TGRS`zFEP*R4N%VgE98n$V!MA3M_yU>Mu4`)pUoSH0jtb?wo*
    z_{Hp2Y)qR65~Mu{rG}qSY53|0yf`OZ{~qBJ^`1&-OKAdyQU+Iuvw!R}ns6z{A8kka
    zI)J|WZ=&6(gr8ObiA0_<QUTb-tU5t9+sd4I588#UA~33myjpM^KljGXS&-Lj5yt^z
    z(At)GmUaVe28O3mt213i1{vTFz?Xp(O6|8#QD83}?xL@eD7t15W?{znK0DZ`uZ_f;
    zT!cEd61^@5b6QmtE&m$bol6JP-MTqUwC}mNI`zYF4IJe$!rLBjzL;TcBB?8!woo2Q
    z>#VW%$p&A~Z!zJtVsw$n*w>q|4^$IgKGJ&Yve^1SMUnI(F;C}ihqdnN_-Yj<Et0)j
    z3u<i^o3T+A04;9GWZ+bk*5?nldSant1P{1n8R6}#v>lW(_TD{6TsjP|GJ$%oL7AB5
    zG$I7@a4pe`asBn%8ZMD@>kF4^!q{V(w#TPbTU!myEvxC@Z=wQ=RvtCA^X<HM|IRxL
    z9;LZ;P5iwzxcL;5<QyUeG*jYHTa*S|3@D5-@3vyKMpgRQgu*5@CS%ZInvIp89u{>(
    z1zN{s&ysfRqYX)2NH+JC5hP8_SsRpevJApvTsPXoa_BauIWrOg?kB;Z@iFOEFX^gZ
    zi@&|CAcdPA6Q#KJ0`@{FH6h<}=?LrXS&V<rn!wz&4D(Dq*^7qa9afJ1)*^x%#^Zmi
    zANuGD)N!xdT(-MR^$bZrZ4cO$@f9^E4HTlz;p+K^V(t2NuTr+mH{86Oa7;dGN)c*y
    zEB5Eh3G+Oo?wU+us5nID3_{<w(uUO;kl^k0;{Wf3E2{d9SUFE@V|-2PT6Yv_k$BC3
    zeiIowbYEpmjxnPSS2JI{$&4I!pvq?3S@7OJY-8(6g|${GZPTd|)tPC+S`)bpd-dV-
    zcWkK(0Nv%QIsmdpQJaIs?bwXzvPJU5<a);CPpx^X78AFN?^%l>49$sEc*z){g%%#D
    z#$dw`?P-)gN4chxBYXKTR!o{|LHcT3rP^A-`By~~eSI4uXOfufnVzPUQa4T@aacL3
    z#D~ugML#f&M3ImB^>V^9=B1Pm9NiCXf#G?mPY%SRdw|0fw%;hRT^U~nSQw+zb@KO0
    zKqa;Nek<#!NLZfTm&EtS;-52%;Leqp&(0;VN~ueS;SGz$+naOHX}aXcOkf%CbHP_V
    z-8CR5ZDV|uZHV=T4cWKrxq|#kQprN~pI2@%ue^PyZERVAuTplmk;ic8fnU>y9(0A;
    zZ~DbLkw`}$^dE`U*e%Th^cDb5isVhW!?8AN(QC3Ox=ST}c?<-!0tO`Eb8`v)x)$?T
    z4d%Bi36TI<Ji2lB8qCs+r#%H%jttc=#v9!~g^}EI+%365(i<kmNP!SmyAYPeRj+-2
    z8zJYMnE(Hb1oRZ1@Aa;K9bbTYWEy_IB#LY{cU(WZ6siS|?}x_o6SC$(bXV4GYuMHC
    z^;C>bON23f!1hn}q~TMgx^cf!)YnJh{a~p!wOQ=Me9Hwxy7v`YxMmSSQ%*(bI#-1B
    z(ejcuT#Idi2!U^~ed{@FUb=+Rvf(Q3?-vygK&pT3p#D=iaNjSeks}ULiDW*Z$x>20
    zgrS4>>C2Umwz}`u2!M4s39n)wI!h#^f-b+QP?V!OeIVXV6YzgL9{<f2?3Wj&v;njU
    z!?F3@nlxkKr%MP?p1yhv{zZe)NA*MBG7-P42U1=N+$MS1d9iigCPACa<nIQN)|bbk
    zxmyeQLI@c#Q_mM)_O}Gt+!yAOo7IAiM_4LTt_)aVZCSEs&}&)|Dq(_XWK;fj3<U1#
    z_<BqjQpp|;00yD8uIp}O(kQLR7tke@&KiK;J>OWA*5?fBX?^`<3E^XP8kAC1DO6fB
    zQQUyjV-dF&i9vX{+K$J>>M3}i_{y#y_k;4Xk!amBk>We;&cLe4YSP3{s#K~7V`HjF
    zX~Hl=uPi`36EJye1OT{p)~ra{s3(3AkVz<;6ACE+G}`kf6u56J_Tc}<;$1%~V-%3D
    zA*H)lcGD8VR-@H!>1=T3KqrwkP~oo^GEM>C6?1V?Y@PFDU5|))sYJVEfUDjoV$K%-
    zwL>&b6dM^1s3jfP)8E$NSKlS9&2*bt2b7{KQ>fndeV&IJmbac3-d?aP<7*AB5;4+p
    z3a|y**pluOO@QrD7U(IscqkrP>nTOu0l`16!Q8pVLtA668)Njd`;fR}a@QOL+l!eR
    zM8IZo1y@hu%EG_G`b9UJ3u~j(iHT5KQal7>a=jcNgHkYY{E0n|TPu$s$g*VVb9VWv
    zm*cYn|4GlWb^Yks7`E90U_P$<ONq%f1=tNp<dmmucd2L|CJgJdJ#CNE2cWMRi{I*{
    zQ!6^$V%}@V-nNnuFX4Y<6khyGUuwcyFBunqp#;vpg!X;BWKy2{wnRIxz*UIF(;bJu
    z4=tOIo1PPj04S3n&@WKxh-oFLDMe_{8uM6<GDp5f?(Q{Ce0LbE2ekE?(UvpVs#Fg?
    z*`B%OT^(O{(LM4Clv2afsKMPneG<BS`Yt%E0-ZEL@LWB{2OeIBxo#1GHeq=EIQ&xw
    zxX;_C7&R`x2d;eJuOdjJK;$~HM5@Hyd6=ymr~Yy;<?p`GTc1xexmJ8Xt+aB<4y_Y3
    zG%RD*y!-9U7k*~vzxQM$M3h)4$Ka+U#RTJ~Ev*e{rmJvu-vfzNWaX*Q3`r3z>>vRY
    zD{I>yuf<#%%ez|O?1AW)8#;5>y$9Eh#{0@U1fbOPf=n=@tmk(=R)fLhdRH-pDG$DP
    zSkT%K!uOx?O0i+NL~3n3KP<<Y5Fh>rTY;1-akbfvG1MFas1qu!x^V)2)<ShB_NPm2
    z_(-i**Dl8Vd9?{g<)PMQ6|KSgqvcAxF6MykKY(&q#@F9)qsvBv8)>Goq&3!85LkSy
    z)%Cp;Xj;RBLiBB`FiN3*y3_>(FBoj~DOKnQF%3DW?b!(2EN;s%T$Q6If}m4`P_Z!E
    z0}^=4ZAH*_laR}Gkjr)2)z!0TZeB%Q{UX{s8eD~^cCmrj%LmEYdMldCH<b$9P;jGB
    zeBzuS%c7Jj_Mm?XljmW9goVO|nWVq8kKVBF?S?<U4SU$DDwx-<6<)&s=BS?j*pd$H
    z%qC4QpOS(k_SuVQxcmS*$e}f1Fs__XL4eXxnd_^#X*N+)$9Y>3Z6EC-<JdlO)plF~
    zIx8!yN~3-}9{<kWjW;l9?7~j1o~*-Qt!)o`n#I0?)!joVwzmtgtK+K!_+8gIT5FpF
    ztOi@?cQHX~X=hI>%mT+&qMLHqA1&1;M5#l|tor&$x`Mh2_}wbP`dkkyI{_R>_Z)&q
    zkl&80=V+Apz)?awTLQ1p*C0}**ZVt=*?)82dC0C<^{Q>}Xt3H#r~y@zm_GG<b;Q9}
    z=|c{@To)G(0@wIci<@0xV~Xs^O$9JF_RoLEBA_1D+E(Mj$U{<Hnymzrv$rl=e^5lR
    z2j3ryZ?)D~W2P5o!^uTG-N*aaItJ>wdC&Xy!L;u+fZzu&kT-J~i8ua-`ggueef8I<
    zJ$H&}cYR@8CPehS5Xl5wG0Nsb6|UN^g6^ubR6GUvdNb>l!tGy%dU866^9CAMGTVm(
    znX}+~M8ztW9dmWlu8gn0$a{ROrCv04iQjdu>6r}}(~GqZk=AFdF@sW;n<n%W#H%~8
    zcdcG;PYGNZ_^)CD>M4n}oGpr?Ue<Hv=BC>OqInTLpA6QQ$R;-Y)AN#Q*x-HCLHk~+
    z_LzK@DlZ#>=O;;}is;|3YYuHuADMuB;0s$T003^r)kh}))n>G+$wL1mVX>3G8f(k$
    zuO^KA$2c5Rj{5S@5d2i~Q-iuwG2oh3{<dy?f>MOxfy2n2bT=h)){=PYJQ^og5)wc~
    z9`@w%WMn<($p(w*9^f%`3pD?~C1_P68tJ)MzT9zCJ?5$fguN~Nq=u_T;oZ4=s4J7G
    zW$isP1PKMz8La9a)m+1lfn60}i*S7h0DfzL?FgYV(Iv>zt20`?($rJXj><<(EO4pM
    z8^+_ObWh*+s)dAYVV|SHLzGr19lc|O{H({-Aw*W!K;ZLRfFB=Nz9|8a%XN^=b=W8i
    zaM+A*sEOl`SLt+7k6@uz9-W<iR{X~IboIt8&`k*dBNF5#Ace7ZQ9CxYFgl+=x-If7
    z6n=ItL1!O&?sLQNhWVM?z9l`s>*Yr58k_2w`%p?*ol4EP)9d&4TD)I<f|dwrfkNFi
    zht!1+Q;fA)+~m_2B<K~vs?9*cIAQiN65xq^yT1yd-5fuc2i9qO!EAzcne_=}|5DV=
    z6Yy)Z&L54S(>N=@y3c9ZLO=2Sz^;g|E-yvS(FgR(PHeu1E%GqVGBAlps@J#Jj;uhP
    zJ`iuuUe?S*>o6}isXmxrsUt>_`{$=k{ZEfJO(Fy);0lF)Lido8Xh7+yn-OUD#btP@
    zS3tB+R%~uwWdX*FJXB5I{WPVu*E0o(CJY1S%zMzTT=NEfWH92P#!5CaC3tfR96E6m
    zOxA!+1{UVFVe>U+L>_^BTj`<>mAG<#*WW!-GM5a)d$F-wI&0upD+tMDb1_rWKW3E8
    zJbGqk-I<fa)^U}DMFrS(?dV^;PTrWCD8KL_zdUP*By*1LI`NeJd}b4SDZmx1^rPox
    z85Xz*H^=X+xEp{q=G7+5ajz3RSJ#uk0LJH|YH~d>V1TAv*H#-Pu(YFKSHf2uUX_dh
    zK%zPaEg@8;y4G3vXf3wB*I0}(1*nm!p2G|~4g7FP*Ql@_%zx$-+wsWRG*&mG?|g&2
    z*lUibdy-FAuwH~i(FS<y^SE~ECS1|hWMRnmSWSS+X4}xQ`vBV8Ybh=oq(+TARITql
    zdaSLUW&iW?&+W?U^_V}vm0U;H*G2#P5gQnIvbhybtHgv0fK*GU_3Somd4h0Anovy3
    z%D>lY@7}fR`@1Jqp>J7E_+~TKSoqme!k1fo9UZPhsqY;gHvR1^nm#s)Y+(ZX>{60@
    z-a^@spHcDce^PMZV6so18`L)bFE#g{VH(lCAIlGyOTKth*K-{!n!iSJ%-;oGmxG!l
    zzTfHeG5oSQ#D4Z2ZGN_tFpM2}OX&LOn_@b?Gwdo+^<_;R>{!@U@YNf>gR3soSiE^H
    zn93w3>8Lm_V^B}j_1w>}G-`fFoPAt@Q%z2<*Rhz;)}A?m%!FT1aqfc@kE|g4@lm$r
    zwB5<9$A#U&!94mN@Hs`YAY<h<Il5;<G)kt)0)$}}W4r5`8k&|<Ryw>Vqv`}Lt<}8r
    z%CGFKckZXNv#IwLVNqH07lA+0-MVchJ^Wl>*2<Pdr~gS*9cN9VIncS+n@qTSAwe#M
    zTARVgn(}KFv2H{@YVYE1Et*ttLVvt7-Xb?Bg<6&KG_Fz(1>AnTZMt9@87$0PLE^4A
    z$ty`?4jfMI7ayl(j{$`F2`m<B+tI&#g)}w{MS%4aLOO3V_HaJkv-7?$X8%jT<De)`
    z*-YAVTC7LAM$8(xb*0v`TTO87XuOg6sOK9n8I|;xc4qDF(uLL<LIytxfsbpI{UBgh
    zz*lcrjq4D6*g_&}m08qg&CnFV;tqTu?}4?npEUp-?WW9!r)n`*jKHf%!mn2l{$Y)_
    zShJqs%UPvPzt5HoD#pC=S?VWO5k&|(nlqF<xrmgc1|OE7U=%c-3cTG*vEClD5)kG*
    zAGj3P9QH1*YI90kd!3Cmy)^-AYv$R?vQf(SQW$H<WSUvH=#j|WhrX{gvy#2|Io)N=
    zk&udpgx)RdT(~s?09;$C1#ZWhbf{ErRvY2Kgw0l^NK|J?WepV<&7$`HX(YzQ`pr+N
    zLfx<wR%NgdhLOqj{iAI21$&Um1gINcA$|ATq<`@VTF#h6rYK7DC|K5v_w$!Y-}XBB
    zZ8@c|HiawNc~Q*wP2FYJPfYw42}&)K=c|EmP_eade}Vd9ja}1@{`t!`@24;HQNUx3
    zd7%N0dJVIGDSCbz7L#L6D_$f3mUdvVVN~7mA0deGVhOBZ$Bm%=Fym`O@R%^)cfclU
    zb7WJRAW!3U7>eh#>5lnr#_lG#@HMoBiqqe=xs-y%9oQabwbFWIiOC&5p3LF~>piuQ
    z)PrY9AT!2hCypoc<YJY^+D9aNG+BfZtbZk(5i>tto|}>&;x#cLF%y{D(6rnd6DqAe
    zIy&kwVNR{9oo}Ci@g|H7X>F_77~{tw74!GwZ5@#i@TIO|{Fb@^arh&ylIsnuO<-+e
    zO-`$pfLv9QKtq0qq4HC+2v)~(vjbA7llu4iKBaX-2j18JMZu__Q@*+tJ@aB3KeGp&
    zMLxDQME!9N=@b4!$><v?zu_fa*p|h|+6qbEJtY!#(PpfBUopip^0*RL>U$>Ot!`G{
    zCw@=K#g9^S+pDBMxjUH|!#5thdr>Y2!CE`7)nx*F1y9v>nTBhH5FQiByBIfQ<P$s4
    z68Ny<Yh&<T(ZU)?tja<~LTVta$$Kf54Jb>X$`Y29o!G_k%!g8{ED!smBTe({FH(QQ
    zQM4T~l3d!yjxEQaAZJl;)!<+HB!%C2ib7$kbLj5xh)Qw$8j|`*FD1J>Jxg<qFr~HD
    zsOsuD7BuyZD{OS?&8=&#TmWJ>$o!tt>0~h#!$;b(p~H;UUi4ZeZpSl1?~z-(8?Jr0
    zN5pVzAT1K`TA&nEPC;0iw0>QVgt6pGJR0ttLRUWd*?R0}=IU^L^Gm5Q<u>@;DKr&&
    zsCVk{9(jk<BMV5j<-7zo3}U&?f}=*$^wwGuwQVFUDhc*&T#4piHgZ@$72;b%&-v-#
    z`FigGLHqP!1SSh}YViNF(#ykIGqThK`wb?yxB)%C7QZQ%Oksnd_Z|kPQ8V`?S)awu
    zc$r*>p-NIl<&yf+F~npgqWmv0)prDZ*zmO}C>G+LrWAp)m|`EVF+i0iS@-l_Q0P1E
    zJMArkH(O$AUuk{(IMZ>*iL_Qo5OGN>`mj4FUD2cua2)^uAOJ~3K~$priaZ;RD8Vdm
    zLf3R?AFPc91C<1jE9Lj0a;|suT3^2S`U$#cOgt-oc&oz!ugd!qsbry@y!)wY@q7QW
    z4Nc286xr`-@@v$@!;TI{?y-kSrPDT<pU;AqU-91f+k?J%uTP4y`4O%qJL;$S6RvP<
    zn~ZK6j=Ov9c=@|kOq~LtEUoGM!Z4c0<-^@;G0!w8yMFXFvPb*`#)K%PV?>mY!%WD5
    zHr;(How*!b{xtp_C*qxcALX3^Y1{>p-x4#kH{6Y@O|R~))viC>G+%K59Z}NWD)`3|
    z636|C(pyiY`TWUbtaYiq#SM5*EFgLP^W@icbZJPfAn~a|<j-xxf4#|RCSmk1i}i7#
    zxF0id_t-~K+e;*OzoVcxzB(khHW8)HN-G=gB>38tsr!)BZLAdSR-_gEt;MrSscS~r
    znllGE`u@fO!iTG4fnBA&yG}BVhmR!N*opf2E9BiXhjd{IJEEAdAPEVLDapg8eQa?G
    z=2YYTV~I}p5{60K0Fg=bJ@sj(tTBwGf;;53V{wgVktOcM6hx2n9=Xr{rns`7Ri4J=
    z0+gp&Id`tETe?`M5(PHnb$Dcr#m#_-^Yoh%7x)A2zQCw!^i||a@@+M1&lf~qh3lBo
    zf}{{d5qN1d3S+WDOu;7n;AoOflrCx(!4d+THwE+Kj}Z*~G07)CMeYx8;s1UX$(Sj2
    zI^A7lT`7H$hSf1dcqpBI{sL3C|KRo4bM`|Ny||p@?><TMAtT6PKxu4y26fWCl>B3{
    zN`dvoyiLTII_l2*f%ts+xK`G=vXAG)hxEi89=n4Bgb}{JjT=!FU0^a1I2rAqgR%oY
    zV0`rjN-U}$;chOT-)mjb?0=Qkv|lxvwn>Gk?<^#o-P$udpk<*`stCTkKkbJNCpWYh
    z(_^qhj{qSIxq-^swjKjgqk!gK8(8nrF2LM1(J0)+-uf(!o^jrV!SLND8lCXE*45t7
    z#9zOEU9xoWU^`*P!C^_i{x-<vs9(KW&wA<!FWc4<PgSORozXmTNw!&ydME`BP2&3w
    zlMQuXSq7nXBPY5{km!iLScEbSw4ODIpg2W%>+5vVePeA8XzYZd1a^EScIHylF=GjS
    zKdZ|$>yPW^n@Z36{>37(J*frw4zMS1>rF3_&wjKID#oTYCKICXdYjbW=aXv7Dt-9x
    z6aC9m+05v2f`)eV%2sq|PHC`t!u(lDy*2g<z6_Q#Zl}AF?M!)YwU~c}F?!rl5XxE1
    zmLO#WbAh>-;03V~K1h7^1-nTAZXB-mJsKAlY0!EJ?)K|6q^@410o&^G5q7;OT|p8a
    zJ%^@21+g{TZdT0YP%B!|udc+u^G(vV?J8>I^#t#cAl(o=CCoM{#%miA0#V8MdU3g#
    zfBbD?cK$$9HerNK7ZuttlT|C`ysb@`W5${11jSXAU7M>KH6pAWI@COI^Usp8;A3xB
    zBDJ+yfDOS7xR%g0xH8*iqG5-KuT&$8>8(YAm^K~*)E+aM_8f5Q>l<F%f;u-FepDHD
    zejWPb;|OB6#%6HcQRfI@MThfl%<o?ymcXl6<K6d;O5gXcE&xw^Hk%Dms$ZTBj~`3+
    zz~MGGwmchn3hLU?Edkv40tItxY=IC#8?L<5^x?1VFKL<EMKGR*n>&~$n(6QSShIsI
    zfPSKNGa>{d>n%E?x}5-DeOVcoiOKzLOdwUr|Ffcij0j^7YtkZMlkz=1>M`20aPXa!
    z9W#bZ{{l>P8@i?yy}AXxya}(q6Wtj|Ij}WGmX;@$ORE(6Jidvu{+O8D1BKtzMyx3l
    zqtUK&NO=YJfX|#0C{I(pWQneR?>$UM012gM-t%XF&tpFkRt_0rlKCmDh5X`TyCHW%
    zV8yl+00-d8Pv^^*E$mg0vN;`J)&)vbQPQ&=s~a{R6k5~jYRftUu{}LBs8TV<Q-bip
    zy*B@HY|m#&G=6`OiZE-!LxzPN9~(#Jle^jM*nWh{KwwY}o#=I~csYaD){fU1kP`pR
    zh$c51+1Ekxcvf727T`{C4{RU7qwNizFjBeT=TwOYL~{f@IiC+2UwwV&C^4nucjHxW
    z;I&cXO?0<mG*YbV@>B~fX;9v;UyaheeZ@LVq($=te{B-s{~?=qiO4Fkh}VhW@M1!s
    z&;6zfi*>4~C~RKiDy1{IHWs}0inq_lKOU418W>JI@Bo6&49n)dtKWV8S??dW{UVu2
    zrcGgGf16)ch6%z*5p{~Yd3Y~t?w)Na01lEvu^U0erXZTaQ!uO)@2*)C$on#Vs;_)(
    zXMkEzNB+k~)3LCDB-p~3$+H4?voF2O+qWeL+Ft@Au2M=VeeEH(<=cnSZbMkrg8#cW
    zDSBxIsh3ugx4emDdk&p5lGjyTreG`U-^tdOZ}$t)Zm)}<{cj<nv)J)B?po3Qwet7z
    zxN_D0Vg($|rmvUY5%}s0-VzHc!)DICkL|fptc9ZDH0_xjIup8k5Ms3J#~uk0ObM+B
    zlOADbK&J@uZ-`5|IZ!D9;IbG!L_y-P50}6z88|SUan8B92fzE>q%{B7MbAH{*S`C1
    za`+y5g}d#$w<#zsv!nK$YDP_)Z0grk2Xmi$&Rg;J8@gfD3Ka|5&X$1lY@z!o6ZO|^
    zCICDq+490I_hXe1e^Hm8tne=R8&z{_y7Hy*=xOvGHDfp}bE;`y-mLqbHpwi%Zw~ob
    z>&48|Gw8bm@0Yk)b%pe3%T%mtwTUa9r0S9R<h|F39~w7&Qlx03{Iq!q)Fpe_;Kr9#
    z9@ds`#LS9AlQ78M;`c9=+5kg$LDCMh0P>-=-MMFL=^oZmBZ6UP!dGu5z82T}9$mfO
    zxP^Of2Xq)x|HdWQFFr=ezn3~D7iN$1L-w$d)EqyKmT&(Xv+#>#_W2E^E1TI*;wHD@
    zI_)-v`ypa2+=FW|{%6c8_T~5J1`q|Prd2D0y!<@dZ{&!edhx>OHKw(8ot^uS$Gy4#
    zc--qhdYm0Tby_%b>SR+sV4ywttTTe_2`BLEpZ}Ct^UmCeCdcDuDU6P3{}p15#PZ-<
    zuS(PjaA`SApXto2<_-96k<?3y8CEFLI8UB`S2WR0HSM&EDOwPdl-;JHQi)GZrS;dZ
    zQPGyQzO{J2eT#v{(kYStQ^g(W3*Hn0J}e6E%1%<Rucqw9mE_lVkZ&ykZYErfnCww-
    z%PKI_2NAx!QYpzrcWxrO3+|GUhlSWXr6%D_u>yX%y<-4#;K0wprJsX?zX0b9fhkYB
    zI6!$UAQUx^>`eIT4O!f+#fJ(}r|F}=epA_soT2@byAf38lX-j*o$?t?g7fIny!T+*
    zuRfT}k6*x>JODeWh}_KObjar%BKzFb<r|0K<{nHH!T%C&u7K`k5kvu2V}j63I_xmJ
    zeC{08Qd<+@#gE~}wl&nL)$h#J?>s-#t6jB9ceb^I(sZ`As=5_RWoSA{y2sK-!tEMa
    z!~3%U-i9G_;5P~Q*B{_U+_1zka(Ef}?DAg04I7KvCgtESkeqJxnjZi^lhjId+exKt
    zWFXcSUj8H{vZ)ofzRrAHZS~U<1>Tev`I=~gtAwzle)YtD!)aVv$I7*>8#e26*7pV%
    z1_9aw6J*64uGxSlh(<^a6UjeEwAXmxJ@LiE?aBOa!=Bcet)8fbg~hHG;3PTc=tg7}
    zOZV+8`05L4a5o}7C;`mT;?A^f%&(3yn*~_chRRQFTzfaPLw*AG8HD}Ma@5Dh5~7rc
    zwNW|wS8+u(OE&T`1+m~{lo?o#!$CmL>Oc#G)*8@V?H3XMWMURWWSv?Q<m{?BZ?kI7
    z+lelbV%FPNr08ejc$N1h0A{-?qEiC+pJ`62Y4IV?mMAT;sB6Axjt23AmP$(O-?))V
    zBm2;RSzK|*iyJ_qdLLTTiWIDs!|=7D-Jn*UG2S-6yb}GFxf>~wwn6xIHF|3QApQ2*
    zuH1hmJ@2xMied62Us=rm>7qSvz?HK$vYkP>l4mcr`FAdHmd}0iI1JY%d6+O$!A^#+
    zzMxLl^0;I)`U}0a$Af(IoJZ&jJ~~lK1=bR5Vny%Nx>-iWDQrzEYE&^+n#6R>B|00|
    zo3J5>2B7Fznr|Sk_F{%8hQniwo=^&_Ak(pWJ@SYIv<G;JM82|~)j?-RobfF^?`MpD
    zK1cU;@;x-v7HR$2@PLIOli`!oy03aNlEyh8X6}rYU_;cO{wX1pak$dSKg;1=PtPF{
    zo5R5Gmzc(i1;FrPYEGHJVytanP>cHXo`j&(Moh?(d*(6Z!ad3P?(_<UX$Hz8y4Su?
    zfgO~hHBVDVcY%<HYslE{%>OnFQQhuNL=Cs~dfC5mBfU=+t-3Sft1p-8WwEUCMBo;Q
    zn-aM;rShntyByoOCYFkX#(%G2<MiK_Ls<p&ov6+bT5|YUTP1jog!{Tpto1cn+ne%t
    z)J6Ce`TGp9US1NqP^pp%wQl7q-PE=&)ywpcHx~s{jy*cF*GZ>k0W;>1BZEXiAu)?B
    zDmkylbs-)?cS-V=3xL^hk%k8?Tr*|E(_CK^rk^FBeHLyk^!*a_T0+mB<dwMEf$=*q
    z2v$V*P>@xv0c-ahh(T%n$|}@hBQV2CY;S^12r&%=ib8|_!Az?15){#0`rJ<UC6k>%
    z`<Vmjd~$E}%ll)_8ALrwDPgEb=q|<GE)d~Mxd`-c70gs{@nq*iZ%DZ9jQHA6ETDI=
    zF|ug$1jj}4d%SolA229YP??W@cs~8b%1QN#eRzOBp&wp0M9p1C*`5Q?vua48x^nq>
    z!myhv<9;4jjynlgZ}BCuTwWK;WxfcJ8FKy)b~e`Kp8my6shIg51u3IP0!R!RKfx}W
    z^`=)kY^0rdz<z=0%*KW8lR~ot=pLl9Ra#`^Y<OZeJoxsO-;8pNm|)L}6pDszyak*f
    zulWnEBy~r@WpY64fZ0kh`}X5_GohKSVD^75<c)70#9Jse7gy7;ws&v{$fkV4^Y>)s
    zsS{bCT_k_K$I%t6Sa>jN-krgkc{5mZ+CWyKbe)_*mMxzu``?JK4Z#FSt<ID{t=-5Y
    zC%C&z0*H^w-%VnPMUO5#D@Ef~GgxrvNxbW8>KA>9H)ag&x*woJl*r~@`F57LCV{M_
    zQXXq07UcI5#9DB%I+h_Yu|Tc${X8>dul+)$um$}F7^QXO?!3$vber~R>MntF3w&O=
    zffBm`uS|ji)s~xbMdEi{j%z-+GM3BzhPVr3aRoWQ5_8mUXTo_+0#?hTti`Dl++~@6
    zeFuH+6!@Qg2@krRVX?evgqReNn%tkZ+mC12-h=44Yc8n^A5^7jZNpBZy?C>Tr{JM!
    zB*sdgy#>BJkKm~~e3Aa(il~15em1m=<7;DZi7;0w>kk-Rhs)Jx#OfM^8v^sqSc>fP
    zvfq9&3&0i3C}mHdV4G&Hptv)Om!HC}YQ_`oeTN8u&CNVi<7zFQ7T5D_u^4BF8?$C3
    z@Aq-zy{F6BPm%rFn6<eSca!lixAXw}6aaJKa0B19-ID3%Db5GnQpf{t5woR6T!a*^
    zJhniH>v&wLY%#8~>Wb}5G;?r`5Y4itTtcI7{y5pMJWfBYNWS)YmY?+(2FzMV<bx%F
    zL|Go;Hx6L+7xtzu?PKnEi}Wq8koT=aXg_!a*^}?4<i(Zr7@T=P8TvQ7<3~*<4LLaH
    zZF29q%Q~t=YF~&eOxh~g#qqT<I008`TO*Gp!t~2ozpr4l1P}X)z4Q~tsuWXwu*7y+
    z=q@dO7nio&(gCj;I6{~oIll*RLs9-mc<_J^>HyUT@SWe5!@TbU+Vj114|j|jSk(9m
    zauxaTpA{4gE5+2dk!a63b1XkWc>Yvsu04#k<!QoMwRr#gG=<fz=*Q2csec~kwYB&&
    z?w~AddSc#a-@k84pyD(3*K}f!d!1aHA?hdS6tnGL?0oP+<LiTj%_;I?D+Iz9anq+G
    z4IlhB34jk8s&Kj6BQ}a9g&k8t=h?ecd(ocMm*x{bx`4!OuaP%%8OgMdo&RO(s?ykH
    z&3Fg=hSIfd-9gnOD$w5^h4-^%m;w*JGp1+J)r$?-OXiZxT8hP_TZfw)u#}JBBlt)F
    zZ1;ViE&k(3Ty(#T?r~wkal6rU?`hPpXvVws71DRlCB3H2OXPcDkTa-5hm(Ec9GV)k
    zs2O)s_RhL)1~2vCseMQmyIC@vJDcG8vG~LDdM@CJ8q8Pb6Ix5TnAk7j(srF6k>*FR
    zBf#56pECwmo%I=9QA6b0)_=f3qK(09oS*zFV44Jlsw1~xLA~~Gf6b<EewqB4%T;n@
    z8R1<g+U7e>prtK@XP1&nde|>MO3}QUo&{B>4a7UT(pdw_UCpKLTt#?nWzXAo#ulJL
    z1FyEIPVv8o%lFUR$?0ai#4WxK{2y+@ZzzGTU>D(y1g=7Is>Bb<<hc%ZeEarx1AiO5
    z|2pw$@&x%I(FK*xoV_1cH_)_`o`J;$1v`2OD{D+N9{jU=+4dWbr8O-cVP!scd9(Jv
    z^tdfvzlBt({Yq?)D`$#)_<N1A$5vW%L}j;|V1X}<z?<D_gP9F9;4%Z>#MM%~vlBAJ
    zIfCoHCgA)A7ZBVZmrDDvffhPA1(yLCE%BM+SS%oiOA9yQg2{ivmG;^Xs{nYvF2K)l
    z6-_-xKnWz}@bYn~w`174+VAZR1$2+7uE#}+Uj&XtDgX1+ZTrtZ*4=QD2~pE-qs&*>
    zz{336UP4rR-oK`Y-k{hVmfx?&T)$X_dD_|srjZ!c^FDg!w_#6wgB-46tq@mWa>7n>
    zK55|7xHRC%*z@<}Qer=$yVcd=D$Ztph<ISIxP#}40Py-y5b-HbaoiaZ0-wXx2Ye`{
    z-qyllxT>Zm5dd9N2#&2pf1)4Zz!Y^TT_a}HYiwBw>5hW7nAHNV?)?kESFo0~t?1|`
    zpd4`FBNQ#F$GiS0+DcOEOa2N!Gy{@7eXJ*IT;F+zfy>_|$n-KGcPl_0P}X&gScpq)
    zP5yw-r3^PVZ5ggkp_ZOkd7X&sr6}ulmal$D%=;O*D!#e68G}*RTvW}O6?d%D#f4N>
    zDKbitL8%-{MRon35<yt<-xLGz#eW`z%aMLpG{vyNY1CblNqoE?`n<t-lL~D3&pKKI
    z%RpTE?J4@6;_^YlFknAHb`n<;-vnIrPOb9itizjAhj-=E6u!2a#KULPG_VkR%`+7A
    zmH-EpqK}Ks(_fc`A1xs?N~zroQ1jZbtpOZd-c$G%Xn3qvMb%!>7^_D<pz}BbS6K3K
    zv5@|QYg#!BC{e(;0_?;BbZG)xo$aEdAJLuf-jP<U-h$U&fa{UJ0@qEL7NHwe=~d(M
    z|6L*5Vjs0<5o%Z(Rh0q{OmooHHK}p82M-at`LD15W{98E0^BMGdOeU>mC@>$*U0_5
    z-f73fE6}%0Kr5H#D#f*uR_tWw@LgQtNGWdY)J!q$3|RZ%yQHprmV!6d;9qti?ITMF
    z&wYR*(}(9bxsV?I|KHbPa#pEfDa-xSNL(@$?~iMhd9ksl^{`(lx;SCu7Rjk1V0W};
    zyaiY88^_UsWeKhvmgm+pp^)5}185&vKo~oNhs0vok+8ck-9xxxA$gmi*%w9YPon$c
    zjHnhQl-R{vxBzhnD3wys9AKAsx{F`Z8b;)yCKPsc_eGa(oGt?8zrq5z1UCvS^66_t
    zyHD6H-!i=f{Y*XPkE>m@^vHhbl^vM*?OlUHcM~6SX9=FPiZDEi?vdUlz2YPxpkG=^
    z;+L<Gx28?4A3)Wfcky7WhURtqiPtP5s0$R|9E~@=5Vda!`l&kXy=w>$Dns{6$`w`$
    z9$trO$aM|7ED>Sw%nmw(ez@}1Q-zs4V0dW)d+7*T4=g3~Rtw3N0A1i=*JSYiSx0J3
    z22ZTIQ`vFXphqs~L*gox;6`v);+mLx3$^QT*`DFCU|tTTEV{=fp$VabVL(Zq2YVMg
    z95siqxI=n?JyekgH90pfobJ(Pd9tQmItUnDEU>!-&yw;v+43a3urCP@cT+}N2#?ob
    zZd*aPyb~*jRU(gz*s&L4Yzj(oHQEE{d2iiYxO#y-#nOpl1sPl?*W~y)KCu^x5fbEE
    z*oHm+O%)Vs%X9mX$oJg~FP=m2TmvSd;A^As&l`mHfqh>lSD)>g{xS$xBR+xcpFx?9
    zD^DFJ#5}uSl1VjL^zb}_Rh{_zmy$W5Kb?n^lPUHwGaE>Keja5x!xHhITetHX2Met&
    z5$3;Fg3L>Vp-XV%){YmSJQQ610ar_pGxJrTu(mvbsz{<z8afOtZo}B#7s4u~Mx-r6
    z(+;0a0nBbuR$*;IL&mE3{Jk`;1bHeuZ^mnzSO7)1kyafN{5w<p!-G3Ql26oP3Vqa&
    zJXE0v;|frdiqQ9TUqhV{zj7L`sUo|RoJFUYfF}zRMC*D3H_mH0F8@7F82(yZWPB7+
    zd#uuWYN1u*3sD|$^&-OeIus{Xp+6?+zl3sGf$E&%z*6*c^_Y9tV%}`VzSXMY(dB$0
    z>bbb~>g@#=i8WRMFsYE>e@By_?_p=Rkl4F~?2Qv>Jby6lyA=>5754Y5$^X_uN^_<w
    zSU6soaSq#O!qEf?B7X^Yk;)w+u$RQF4kJu>F3#P|;+mDF;aY%m2}Lk6A3eSRH6Vp5
    z@W59nPoe8`s8SzY<l}W1Jn5_OngaZW0Iwmy3oKfJ>aa?MwohIv!1YahXfgs@V*%`q
    zYdLJh&DTFv%%*3FI@~<pPun1zSO}x?QO!Acs$N;F&4~uh6Lak(VXmDCqF#Yox*JM<
    zBE)p7xG)OWQu=*dL&rJMo6{P;I1F!T2JT<04A$C5_aZS`tb$Gh`@TR{D|l{SS1mZJ
    z1$*TJf<^6EA#e@sAw>Q!aZSv25dJsgI^ymHP8ySs-mjF*qwB~k^s(O=P18Z;-2%Ec
    zi~jn%l)TW?HAnD%!J!|E1Dw7MU4auti|;Lh3c*Qqu}W(%#gr7q6wM^80u=(eiiBmi
    z0+d$J7@+I2=;i?3VNho49MPy7uS32!g=<8bxPx7x9WDSa!KH<2M4P`R&+moOs!$s{
    zAms>sW!BSJV`65~F+$9ji;4S`m~1;!Aw^nD{WFBv8U@h_)}J}2W7;SFoRQH{cDDl5
    zBYTpF-ZZxrd&*lX*ssK@yC&mD&;NN1=DUj&U=I>&<axU1;E!Y5_6VftzAF*HDA1mQ
    zz{1H@bX+}}=CtSbnlq?BuOaVO%V}v0xLT}|thh%%6`IJGvzabd)3$(L3PX7kb3)Fe
    ziS@4+A15@~fUBn%1mqM1qw-MYN%WcwUQHIAF+J!$vi5R<U!!EKmR;Xcj{i)>@E?ug
    zx&v1Y`=NoYsRW!JLxjRrg;|tVcT6<;;Bs_AJ3?C67TUkpz+cv4>atz8xcpL>s~DH>
    zo`AbPX=i~>TyA$At{z~ZNcni$C!RJ6dNKcHE{be#1x5@>Id>qC!+NmV35QSgvpQZ~
    z@^mfH%`;;p#u5dq5@HQ(D*-S}H2ySTb)Lt-FASsYkE_YA&EkD?G|itH)ZI$^LIcTb
    z7gMsVgJxX0_gk^g8ZGp&T-NQ2Cf-(1D^}3x*mF^|zAw-*+RS;1U{oHuCBT2L2`|Rb
    z8M<=(Du;@Mx>8sZHwfBs<<8L+V3mCc(&)kwBoO+lSc<!>0GJ#j{6FJn-<^cDwpAvZ
    zcZxn7nUA`3INrrW@jg9^;O$oRC#-!{q;s42k0<VA0w5xwONCLcl%P}51}vsFx(j%6
    z;;+xgwa;GhpGM4EE!aJZP;=W{^uJgeKGDDX>}R)NUu^8MB5oGnHo9FnAwqUrM3>JM
    zAGK8c^-<G{$=<qxLTll+-KjsI%$fWP+VFq5oPsB7$yrwn)Q+qC{DA}~4;NRdOfm?b
    zFy@=v5D)9<Ip`z0c(kv$Q8p$3N(wY#NrJ?C9r)s}2S6K2wJB?ZJ&I9#6r)GvS)HdL
    zsUTx4GaKNsS|TaXBJr&b$ufLAk}?+#!Aom`f7Vg+ZW~GK&a^}noZ@{BdD&TA04>Zq
    zSp5He<nfnCf}YUNs;>;k8=TiOAO2J==F<6|?d1|w;bx;9xRak#1ul0SDf7`1{5*p0
    zd)!(@5X=(|a0ad}-i=I7p=z?&5T!mn$m**`IgO-%^JWvgECQfc1A3k?S2RC=YoP#l
    z+2=*Qksp266(eamdm!zvG?Bb}4SCNs&|xg=HsH@LmyF5&5)V26muJ6~Z9C@dI9yqD
    zTq8$!HS~t)&H|Oz4D}SMEkw(ET2VSXFlp>5Rp{gUqet|j&n}Iq;F)^tXXbeZYr&dm
    z-s8YR5B13b=)bSSoK}UtY^1~CQ#F{cFCfSoN^SRD5eIHVS7@gZ07baUzsQ98lQ8iS
    zD6P`U7-L;-^x`3SpBai*;=9pUm%T%<v=dIQLjUkRf)1zeiUnz-a5eZJCQxEdUyN&z
    zDG_F=iABO~OmjKyq&@GClS4hd*o_{3WF6*;1=?VZIj9W%<>7c|yoChVQMlCBT(P{i
    zCRVa9@O$x*yI^pN&{MFybG=MZyJ+~14Se1lTs7U5ViwPqjL2VsKZ^UbEu;T&>#$kY
    zmfHY6N`QeP*g}+Q@UWSqE6^ua;ms&>my~WQviF1c2<Nw9&lrHdc^To7P6gK5iX`ga
    zdy{C$!DaIa4lF@kIsz}W@Yib0m5a2&n0$#rydlal+?k1ZODh2n6>ZWfM12-;cv8bv
    zql})pFUeO9Ao<l1_;;=%JoFWE-&sufy~TuYwqWm`iht%n^bHfba_LzS0w?p~1B)|_
    zzlBSK-6BLdP_$9RtglP(^H~68!m!MT<^Ue5#(Z-D0oIsNd8ix4<5wn8`5vk~*)>bw
    z$eCAe!P<N=_n*a8zm1Tnc^3eKKzzSs={p+$03ZNKL_t)n%HY+8sG?*KVYL=Fq_Sfp
    zpB)MRYeaA#5L?@lZG6VddYS)bBSJ7Ot_@bH%;Z99ZraU;&+bj)#tHa`mT#K*8*=c~
    zcL<)Z$NYK%{;~bgf0;^RVu4jCrRuVZ->oDZkb*lV;lI#?y=p#T=mMvDk0Jxzw+!te
    z0$`W10F?OrCyQ?unZCi)BJ}-x;D@mnF6n?v=aE~`p%q%|pNt8JYUF+36K{~4?<VLB
    z#?6@fEFXTDfg8a*Qq0sw31l5FuEX<0H^KPH1ST|?Ff8#<5AQ`{K*|LPAFaV$F%s{@
    zSt$rI(WN6}H(>fkP%Z(v{c)+eJvQ{hPfelbGka3k+=;&7W%~VViOR`k7g;v%cBRka
    zhL<Q@`|Rx^G!Dk4F}EdD<I>AHiMOn!@0kO|psL2?+t#Z`lijCu)3hBm5v<B!|6YUr
    z%}T=N0Q=;0l0(zouWQV~X>XBR(TUC1@Q=MomM39ZCmi#d%8kjhoI42bj+KNfI(rmy
    zeT$7>8uFpC0LBVowd0yYrsHzRoeSEquQt0{>4-u`<iXujNsLMxtHL1IyR`ee0^HDy
    zm_(n-t}&fxCaz-eUU8SATWg+{;9i<&@@$aJVZsol)I~$l15)lH>ffy*{Mtyo(fKGZ
    z%mj4LjBD@X`%<_mv#*Mvc@kHg)HPA4G(~XH9#sF}2$ml*!c9TGXD+2j{ec0;|AoTA
    zMXVZKW~=4%qf0iRyI!ME_EIcDW`vmgcjD5x_lfEKFZqA@Ha^qEvA}F(3G@Ypd7iDi
    zZTF2czrvmK%Z=FU-XlEXRdR>ELhdIklBzL>H5UGFF+qDw#FQrBx0CSG3fiqwUt2(s
    zwJ<CNgHx78?bvG;5eV}~nF`C!+5NiJS%6E0fE&awTaBx`zOuuRK6{o54lcFo(h+!5
    z3tja^fe+sshkwp2f@H4`+q)Qr_7o-zg=qB7?k{a7G>U6fgG(b;iM6z!5btd9pDzMF
    zsjRgpRpI#xZeNKh_E8fGP@M)63LRRMFt_RhcR#{S=80Q#dGcKVbcW~$=TqL;NzPa|
    z2{{{fnfU7QMWn}9lDD{la*2TzB{eFk(Iq}+K$5UDf$@8)iZTsnDzF(#=Gl5uIm0=)
    zf~Zj9Ij>0U;&-^AqFW2mRbSbS2mlRu<q5)}X%bPurNcmzF!hQIc6A0`YsNlXkEzaT
    zg*9eOKI-<}Nt7qDJY9!(&sxm0^~xUgy3O4*0e|me7c3l_hdOHz-kqy4FEn7zeT&?Q
    z1Mt>nx@rJ)Orfb*%xsqs0DFnq*CCSl3^5h=@)U!|<zv@oEzdP5`&^SYhnI%-8zb>X
    z6`+be0Ik|WW6NShJS2resfg(db`j=}`>S8Z6~|m4mOvCykGjpS29C5!6<@v321(_r
    zC4W55dKNb%q!aB6y$5}my>|-3^`-4h+NbrKhtPEM|Fd`AVRlv3-~a4=&TZ3YGU*BF
    zA)SPdh)73K5h;SAQpAQxvA_DW3n-|Ff`}lBfP$!qqBI5Ry`+%dd#1Nr&)NI^W3My!
    z%rGGdQj*|)o_TUJ<=%7l*{glmcdhj}#ilHUnBb&<!AXzG*+VgFJ4rvXraYkXE92=r
    zbr`vnxBY<QM_#1i=}z|^p29n-k(&Rsa9qyt9`O?2&#UivJh|W57^qWk1yEd{$6m0A
    zFtFGaS?t<8Hjn4+kqXV>4d{_6SeeCcE-J9bBo+L8D*m_%C*(7#@y-~D_w8ji_-C`S
    zXU?^ytEQ27M}ynry=zc6tal$cw;MZmS()B}8@aY8jy0N+@3-v^3Lr_j2qv20UIly^
    zrPR--T6J_II&a|l4r?x76Pkawd+LE_!_wh{(5rJ!Bj)?*?SzJu4T6IEZwKW$>ATAc
    zaSXY75rCl*&z>gAb16`RwLI7wr2sAah{d^VC84q=BPV^s0Pp{Oc_v4R2i1Y{+J16W
    z*!9KxT(7~CXHeYKg@4skq~v#M#Y5oa=`<g@2VFPKXVAuOC%gyOQ@nHqH9uQT^?tP!
    zW>yo7N)gl~ut|lDK(w9^)e)e(0@VC&67#xU*l<uSg)>K!&4=(nOD{R5T0({saCGV&
    zSra6d4r1rS>!%DO^uHV6uBO--Qc9gXEacl0NJKS%XN-jFHdu4{3g^b3HJ{-3v+#$e
    z-M-f);OEouZ{1|g|1BZ-<N``xpMZDjaJ;!)SSc3Lk_{Oopzp2X?d&VhBnpGB-q{c7
    z_Qc(n;YMor&_7j|uE;#JS2i5duLI8A&6pppBwST+ZWewr@t2uJrdE|dueAtodrAjb
    zGelB5W7$1d5?7HHjoFGk0WEIwb>bx)fcE|0&kWQft(dFU5{lA@;`~dtg3HX6=eQK5
    z)Y-FbxS|yvSgrhHC)&!5-88giNqleytqZo0`Ny(89enz%At{Wq*n{gSZZ4po?;@EE
    zQT0A%S`{Tvxer><L!!Beo?JmND2Z9rgFh}qI66(RxQE1|o|hRGgG-n0X!U13j)`|L
    zt{xyKU)_`^{#H@0=kIsAmHO;xtIr$LhXsg-qhx>bC4$Wb?BFEo+UfZ5^XUQCuESim
    zN*jQEY7Fx`F$Iejp!b{Nx&B-5lLa_t>qyFd6+XMOAI?{*22A6~Bj*eH9L>dBB3jQ9
    zh9(<YduTm6nx|`l2^FXhkHD++EYEgggV2~G8&G3nz3@5%2Y3m%w3?Sst=*kGfje>e
    z{JrJS6vc|&ip!m^06vU0!JnFa{d5NwtdC1;wcz;W167!vBZsbAo(*d|NDQrF@Tzu3
    zFKeZ~xrgN5!|AzeDfRQVyz;ETp3sqJHWcvd5}0g=&W5N84_lkSwwCZV7ttLhbYM}l
    zYbn)wn00yl6*>IHJtUe-y{*me6alW5EWvkQ#opybz$QypBTsoSXDa4uZ?&Ryem5N6
    zfNo5d&z=Pulc@I$#(SXyvpk3W%Vx~1D%2jC@));lfW32zYYMZmfDOw=^B=exn3Hi#
    z)><4<Y`7?;s{}K%yQ%^hg`=K+Kn`FueDMez+iQPd53qJYkK%z=>>+iinnZb%m4Z1n
    zsFQ}FA8x~pt3=O;Y1lP+?5$0{#+oWoD!*m-T_KLq)+Kvb6|>+a5%hNeu6WA1nGIq|
    z&S&dQHjl({Ga|QmP&Q!Hs&=ZP437o1vYm837%=0rg1$ZdRsi({gW6EQ%Z79SYjTFh
    z4F$ZG61p>>DX^>q2CdBEFUgX~8Lk(!e!9HRY61S<CV=sW1k8IiYXtBsl=i`PilWN`
    z*%8$gx{4%T1`KLH5zS~US-5ixc4W$dt8s~}^x*x&@K)upi?Uk%eTxkTr%+QWQAG=v
    ztZ;oS<Qd&A&#78K*3)t6T_t<iAlLC_+%%<KqEqld3SbZ5Aso47OH8}^xZH1x9K2qa
    zy{U-3Wg|u@)IPPScwAB8p$yPbgm@}&VK@A3i#6f_JSuVG?z}9Hl=Bp>wqS@HsJDv{
    z$46W@2(a}#aW%Jgn1+ZI@?!D5+6JPoaAUp$%H^|GUM~^lAFU0*TKaYaz8A;$A0wYz
    z!m$*`%Gp>!nJs#aSi=9NKK|V4gE{CgAG8~e97q1WlQE~xApGjv35Qftb^i)X{GNwZ
    zV(vQKwB5agWOvDZzhvRwHcvg(Va=>6)R2@rlMbGSqXwbpby>42kA0w3**msiu3AmF
    zJl9+D`%wvB#)>DR<=oZ?4v?08Iz>FXU1kk*Sgw;B-h}H-|00f)T?zO~5!!Maq<-(F
    z^@NPEKi(66&zQCU<0j05t(dE)mCIlM*@U@pu~Jw&T+(GXhy_{6&OK1%wODmHCfz6H
    z@RTSQ{v<_sE|$3em1@g1u6gHC+o62c;M!tE0z42PoRohnDf0nJlxyFuxZ&A1==mfi
    zwjO-UL|P9WO~>FG_sN^PP}e-m@O7Pz^Pqr~hn+K=;E|8he%m}!r~O&i#iun(mV%Fu
    z!u#A9yf`y(ZWs2Hxt@`<bblF%hM}H9x#F=(epbL0NnOG#6SZ~;1uz&_cAAqoZXZGD
    zzvwi%Vp4-Gb(c^ZyU-}D`)_b`DnB{erBntb;lpzYR_Cz)*}q&Df89FF<*RfCYicER
    za=gTPR>F^q=t~9N9oIDAE4V7NItdm24E%t;Ww)=iHVIf5g&L0vdj6aUdlJV$e1=_b
    zqRm0L_FjlLTIhFnM*v+4_}FZ6pW2W7C-0>Cxlhw(LOA#a>YnO1tvOobGN_Bj;vF`~
    zbp^a~72z+}Vyr8?dI2~CM|F;WUR_M^BMj$acD`!X?Oy%S+LPtLw1^UjHoOy*`p|6K
    zecgNM&iL4s?Rb|yY%<qA>(yYxpg-jMi*?HUVyz7)R-l&Uw8C086)ZzjZuge-V6Y}~
    zS-;p%A$<VH4*Z#bNh^0#1@Mx1Tb>-aM{uRYJF@;RSVE0p1-Hrj><(*2OtBTV;sF70
    z`Ya0jjU*UWjd^e-iFfWn>4!(t{mx&jhL=xof|WV!c?$_9WKf+3nu-drt0e1C63^`G
    za-1^~mU&|3ZxWzy!LGi+{XKyDaMfdN^0VjUduO855Og9@>lX`^DeR<r!u>{5`pKhI
    zgvw8VRwyhc2nX^C@ch)rhX;wGA+;)p|3nA2Xh?|=d`3W)!N6m<RLU6n%Z)gW{c`@d
    z-fHVg+*G0*We!RlzZwHHqdkAgx#8vsb(qVZAambxQvW!ec5Pwel{BpG*zVq!0gj+$
    zaU<Gl#p+keIs64KTeW$2+2FVb&>#SZiN%d`<ckl^u({8@t=KxMny|GS@5v4L-+hou
    zUt{n1P|!4@0=u9I?}bhH&u_$kb_0pdg3=NyRuTra*RAX2|Ci+7oLIo;a3qR};=&#*
    z3ZWjCS~(k+3;*AG>nw$<GTa*0kcfK#8+-63)L{-8P3bHD^6TGwb6`I*n|x=%x%k#@
    z7ii(*8lKpg)z<>XUdXOjvc5wKV7P!A*_ehk07EMYZ#j{!h-xt^LojPF!Mi3>ob*!$
    zt!TzSW<PRsh7+7Vg8~}cnnRENv2DzidgtA>3%J)4d%zY)pL|Y0sx`RW?#VbLS&J5~
    z*@gEwfop6z6j$FqNPx6X0a?3nsQj)z5<EdUgVD}w(-}7!*EP^Gy+Eb4W@87Mkua(j
    zlOQPlYX!;2*OKTiqNWWd{Mm7IeezDKm$vr4pOU?=lyhcdhA$e|-iqr)e9P{tW#bB9
    zfoQl|+4P^{sGH}u<~ZW+Wh7<~CCntT6&`HpLjPtSsldSb_xSZcdMwDy97<T9!EEV4
    zE!aXLS5l2&D+H)&;^o5?m4IM%xZ3q8xcut)nCk_vo=5&>3c~?7)cCz($<nf~@uJD7
    zw_rq^`6e%KcDb{zA-FyV2MFjmM!r`ef^(1D&$|EOGlmavX8L0Q8f)#^Zs!eDBpe6j
    z!?Va;eH=ZvEg*UJ9n{=5pVa+l&_3hmG;|eFVqlIHMe?wCn@YmaG>)qMZeIKH=A9Y@
    zoQum*TO4s^5%6%ysH2X%SvBCO(551R^)-yGBmDHf<faY@x;=$j--*Apg=8s2Z|I~F
    zYzD_tYuUy_{EWEycTyhya5s)rv7VPtd)Zl6J+46K92~;me}6;zPMY=PcZT7Lt&S7`
    z^u?X{9`}^^_+-k>Fl|4jJ`z`@_%7U(<7ZxF2vCJmC9ECb?5IG%y#(08s&;4D)1GTM
    zH?<Ly(b$j8q_C!)p35Gi;^HIdIc*mCYoAdSU_Ej3-w|{5hluiiL9TrhyZP#WK<^i=
    zu5h`{TY)pchFH|-S{t6TFS&z9QOfw}ht`pPZXK0>dxT6&7C#@NL(?nN(d@MjwjF*&
    z-Gv%)EyKa(RnHgqe`ii#De+v+kb4*vQ+81on@I32N-1rvO?#-oLeWi^(ef<Di{d+6
    z!h`Kv3*hhycx>G38%EUOQeJ0^l8#Dr4O(kujd7)&T47BTLJr6E3pnUi2$Kdne{{6%
    zn7<kI@M>&R7B##IJ9!YcE^W<&Ysju?cf5%Woz4SDdDwIUySSwsI-GwHxgS1C#n=8#
    z<;E_wD2`!b`4e(4weor;aNJu#_y<w|H^<P=>9~rs5MWS+H9tC*!XM{R_p>KRH)rv!
    z?WLe*ahZb>!273Q7d2xSHE+G}WPpbqRmhE#0~S?(tzqY_5*#C-(eacAQ%2*ZLy0lh
    z<MP!t0G_h!U5&2r+%I$omY1@G#ZZO9{~v~{B0Fq*FT%igx*UJt$FI|U9)?SOjiB7F
    zJVY*dN`<ktzEgUgp<!tcCU2F++5pEay#!bAv?D73yt|fA^ZmoweC4|-rG1xQ3N3V%
    zP^aET_nLNUfUcEoct%Oi&ci;onnb9;vrv=3&KgSai>FnEI4qVj`!SM(N_k!<FmUzd
    zZn^sN!#ClYRUij85<K0KRW+yH#;8BOM14~a71mOe-(QPsSdo|C54#Ek2@fL{Vq5go
    z=sKcqvaJF}{zd-wGTwMr;I>9iCxHtDygXH|GoW{td~dnxQrVx4fJ!LmufEug?Fifl
    zRQs@Z6)NGT1eE0KkR4F~);03@`0I2&E)cI|xIEu1y{tf`w05*wjk}0CANH?Br`-KS
    z%HmX9hG$3DhWT4a9rSBGdiqt=yz3?sC*DS4-)l(@zl`KF8_5Z1td_LkWlL@bLq!sn
    zwCTi?>m5kgRYEOl!IxY>Nid}sV&}3(UVmXH55Hb_4**=T&w3m+HyVoJgG~bZ12ft5
    z=LLx#Olhmd9nWn<pRpI=yv^h#F0NwhG|8m6mtPgsSK+9wb+~rpH@|@)fEG9YOi@IC
    zz>O{o`^EXcp<D>!6rGCZ$;hE{+>^<2-@`!JDOFb2de9NV!frQ1agPkDN<*~=tu7o0
    z#TETHJ61ox=UjKKcU8lUuR%DF#*tml>b*yY<;=xpGG--}HH|*3$-|Nm+mJvtBv8J3
    zS$LPiQJ+uW!E0E2!W8OGnr>SEI3G53LnZ+yO(i^JEOvbdg){z&29nv(3vZhrrrtT8
    zVp|S{HRh}LQ+eDZ3cr4iOna^ub2*1HL-Ky{;wl8JyqUMydL_#|6~`)@p%vCx)$S|1
    z>7{eY&G{wCp3*D7?Zrn}^Nj}>8kpWWavcH)Mhz8G;&!D)5miu5$E*_s`NlWc{r?^=
    z-y6!cHcC8rpZt6>j-N0y?h2=r>-#?_;bzZmDPY%?JgrO^+E@phv_{RUM*HqA?F(Jl
    ztkKHDn2?uEnq7<IU~H>NCv)LL%2l9Ek#9XP)8PoOc8!$S{lJ*JA0g+9#hQ;9p2SY5
    zK$R@2wS-!cgU}gag(b?oM=i2)0(9Qa_78;pps)GeS+iMx)I_qA>M7<!%<?wWJxlR#
    zdx^pMfChQLR!>7!65ElF&b3mwR+W(f@o<%i56ivXDB<p4Tn)@Q|5E_v^(l!(&DnEU
    zbNfs5bQBZ*BWIR!*FTTG;RWT#4s3iSfST!p(eE5j?y9Hs5Ufe!^1=})7Nx`Nk%I_N
    z7)ohX4z6BHbx%NA4%|<0tgPZ2v=5i!8aM<Zd<dYoML@YmafOHCdd>%x+S+j$RDGh{
    zY_eog9RV!M!tfNjrG(0clFAYQ5QXx=Y6W?tEDkk`x4lk@z)aj;FlwH2YooAX*-S?d
    zel_a|5scwP{rFTL<y4aEvw)w{=%PVyXN(rg{rp;Rf!sEJzX6wqOQ2K;svB&(TuVZp
    z#l5(pDeL=%iWXO5zEHxL;qv{Q7?_D-aE8f!?#qtTuy@N409-oiN_p6;E##&)qMq0g
    znn%~6KCw6EhIxquL9y7+Wvr5aS<r+E3>CFWldMj%Wu2oDC1T*7L3{S2BSNAst_Rl9
    z`N;)zH5aMHr2r2WxAFBX3aXF;e~bwKn%K`ai;MV#?A1^pK&fDO(lRmQ9R98V)f(Wn
    zmE5%1K<v%h{!X~H7-t<R5>2GDalFBb5>SFh?+Ej3OzidiSGgA-5liTUDxj6B)P^c;
    z=?qah>*<6Ib;+uPv=OIZ`UqTvgG3dC@n@b8#W51+tsI0)dA?EwU8`V0r#PXveoV;g
    z1%o+Po?Sz~{kl!ONaW{{0?6Q$qlZiT`MAQfx18`tF3~{N@OQY{=aoQVV>jxoIfVDE
    zuxfb=>U)P1{AqqjbFQ}~Q-c6^ek`u9tWLsBr?7l+GiF04x*|zf2zwh`hFEJ}?82Tl
    z98W12l0?0K2;9*`cP_->a?T$V(B}0@%X}WkH&2N0TRC_OC9`o0E~h^RC}{{LSE5Iz
    z(JcYq(k%Y!Jbp)jF1k_EaY4ulXqFQXs3-PXhEw)}YSHtEQmDQN(0sO|nZ~*9S)C;B
    zvGEr>-eBc)!F_z7=qs39Vb#P6RDA;Ff!7+4uoge&IR<hsow8S5a!*B1Q985%vtJ#-
    z;3S3SB0VKk7v-J=d*V0(cWgrmJWaW5Iy#@V^7<13w5DU;Ph^zFiP!dFF=#6G_tAKI
    zE!fjfc#E!qLV!tf_?7%?QNH)<_XUOX@5b*cB@%aisFc5Z85KW!QVqpg(@&vfe2sU~
    zG+NHsi%s8nkjx2FDSmVgx$XjL%9RZ2?=GG*3hxW!%6;2bX0d0?C)ivdBY;yEj#YQh
    ze|f)?M92#<Yp_79(#F_9{2q=1dN7U=`ETG31^H=JXanA=EMCs^$@fRXT#yh!^$U@#
    zyw@XQwGQvKW=bjiRFa?w)>H~M6213xxYX2|Z6apSXdKLaj)HYFx4${XL2{2NlrB|!
    z#!RntlXSKe@zxdaa$%o)?GcP58g{Z&ta(iodtW%l>T}1G4NOM}kF;XGxs0&AMC8IZ
    z3aI$DZG?LJ;W#Oy<@YuSNcx|cC;K}w7M*f!5pEHAAHKY<kk^ml>P!B^TX+qm0H%ph
    zSaE^7fN<sz^t&fve(<Ooa=<8C{LKg0JoQR7R?e_5?wvoSk?b)O*}SBc@QJk~{&qU;
    z2Mr_WETWFSiQ1<(sAMD<qtmF7Y19jy*a!9_Q6FnAx-yGBbuK|isaFZ)1bKh8wBX32
    zu@d7ir#!VMCmu!^a~U@XIMf8p(TeA$R#IG_$8RsU*UAH3_<ooUgW{M9^zalkCR|sf
    zrCIC?-O9@S8r+20r8o-ebtq*(se|eapWDN=rn+M@;gen3!<ZV%t;3!Xg|XcYC+5Ll
    z44k*uwyC&jTmjbJC|#`ajhR`A${O_YEPiLudqzqkT)7yiwh`COC@=B$J1exy)0cqr
    zM&o^IjGK87trt77C%w?y<ir=({2QWlU+J6<#xX|M3tFEYAe8%z2z7ldl!{ZY+vHwv
    zz_Ct4-r{Q@1rQAYt(Hy8M%wfPQ|W$SB{gk%5_=9JSkgkuip!eNl<Ly7<wJ5^VMQfD
    zE_d;fw(H9Wk!#MQj{Gfk^O|%rDit1=LH%iO5;Yob+=yA3#eQu<-;iWo4*RkB1ncvp
    z#ai@;a6Y!3_xCZ;`oG52&9B0BcbS0Wd4E(6b{l<rxW8Sp7SaBEFsb={Xx-t)jhXIN
    z))r<}p+2z(-a)nK${3>JDfhcic49v^m%uum(2BLoadqygx7VX9G(6Idy=fNyv?`Pj
    z{IeNz!6JgtQX$GuiJSU)wv!X_JdE|>ni=pe^~!&99&jZ}l{{;MeQMFICA?KR{J{2F
    z{0;@(Rw>Y)hY7=&QoDwz1-kY!SM3?9qX(m(ZpU1?2D82ZUmlA-q7m&Y*Me)tBb0Js
    z?=>~ZbDb;-=CufG564xuP86lTK)l&CZxE=w%Vz<O!sWV0Y~$|@s1n#Nv|rk1XpsB#
    z{$!^NB|PQ#)Sfh*{2_Z#`o9OLTGSL1ZtZ!$oD!--2bE_9etZGJg=6vdtn7PD8*<pw
    z<`HZz^a{g9TvO8RDKgPc7Fa6o<J)3d{~TOxce}i2+!}lxms3}Ai2jO8i}nERp2i$r
    zk2-%0-jpgw>D}5|nS&Ewr1ZW<*Bo_y)GA$j-mj*H>Y#e`Up8ZYvYPOwnfMbboa=qf
    zYQoFc`HC=zz^c5s>f23JneUogOqk)a8<9&K_5%)|2Wn8NG^^68wg7)!zBe4pp;Q|>
    zp(dq@!MhsK4M|wo1GjCAbfMBN>1QoUsfiVqKg=Rgn{e9Ukyh*{7El_NLLK0gi+!-I
    z*DFH2oT!H4_x}^gohJ=gi(^I|Ch4#-lv!>@UgrQ?k-+}!IJ!PMi-MM{?}67-_w)uY
    zgZ4CGpKJjY9_sM}Nu*+2yC1D2{6{n9_PzRs0+A>1zUL@qO>b%OgW?Hnk2XJD6vpNK
    z;^>D&A++-H1`nZFj^PSsTkz4ky-Jzv2Zo`~8i_YD^~(OE0#@YU^tqJ2H4*>agHY{(
    z8$|J9m$G2()C$y}_jW@o?rp(-Z7IQ}lkksebX{qWc$(6Nd~a>~VBkA3WP6+Imyqa$
    z)!&5XtlPKSaCG4&v6Qz1??dV0@Px5ek=#`19dXsJp!=kuc%K}DH#Xh3is}4?1e=O*
    zbR+s-EtsdWDW77Y2ukf!WBKi@^7H-6X3UqCdUk+*dl6tfOz@El{wI=eeik5F1soIY
    z1_=pHraT+qk779{YlVK|<Fp?;o>HVu?pRFfCr|3?&+Tuzo?jp8u%CsTR*5=wMESE7
    z@ck8pt8>@~+A!}Mf)`f~wFy+Ek9w#>=dm^|E_|D$=eD)h$$iFucQ0IFOEHGj_xBv!
    zAWWqy8dJ1fJkgfEI~o7*26UCbb)P!o1N?py=De2(P8x<ja|Aj7k{XU{L_gSuZ3z_3
    zq3?}N8@p#EYH}rNc@{2Tt-{q=Y}!Npy$K^}wopQ{q&$!9T7QQV?}M{S?(e#B6ypzx
    zz#Z(oAQe=2)+Rj?n+sCgp;Xhv3i7wlCh>t`=(>K%;?5BM(S*5SCjQ<vs1t^u_pJ);
    zKbx^3ij5^t&+W80`!Y2E03ZNKL_t*Mm_~F`!}v<nEgNhYn+~P>#pu<dh;P}=<t>dX
    zpFT@6HhbYJZnJOJ1#QoCU@O)z5KH9Y8Yf-^{KBBTE1x8j3sHIC%4bNQb~`nn-q&=W
    zFpWads=lA0lsdGb@8>Sg!m_-lQA(}Id-}5r2}-8#1)Mq@Jvd<uF4r5iY}!_%kc5C!
    z*GoF>%i>0Vk#fOKxYdWHz04;@+NOW)i#c{MI;FQR_{PG#F6>pS36FY)(s!0?Q?OcP
    zVgpkEDtx$hI{u)91rr39t|V+OU;_*9AA&}E-d~$F=PdMXXQ@w<icrFlaXavQe@vNt
    zQ=r_g``x%)JPN{U4_}18fYMFxX|%y#_9ihp^KuVS0hg^HJZ2C&dWc%V!S$%W%)uX*
    zwqUJ&q{CB(K1Jz<^@MkA!L*m}<sNbOzuE5$KDHYRen!sgd>jk$Tez_763L<*xic$(
    z2!+0da`%+)<47ZOur~b73o0|@G6pqXO5=rhtJ+_kK<C9r()0O$Qr$022q^XTm;&&C
    z->xIXgyEbTtG+cZWNs(sdn*Xzr_Wb#<_J7j2Tq6J0bh89Yo36k@ODU=?<evQ9+Ly8
    zW8us|mb`9KI&8jZ9K^j58@p~_!Nvkyv5N56XDFRKKgrFTFoP1PN^LAksjJs2^X0_^
    zx!9)+O~F^k;i0r{DJo8Ukzj2e=5}GQ)?$q%4C4LnBTJc(``(^)nA~?~Ov(LCp4)6(
    z9el1aY5k@mK9Egnwp={P*ej;ur}e<z&25`7e`rd1=>dGlNtLKS&cPp*G8Su0TM7I9
    z)k*!OrM(&6Rvgv(VL|O9>)(PSp6!lnPr2Xk2@cgEAy~6ybtdEbUA%W^!vOlPL4u~%
    zOUxW`+^xui8xo;C{r*W|_Suaj)^;hcKV3NEp&s3zM1{DO?Ik$mNs57mJNG6ru@d!U
    z8}^KaNj@>6Xf7IC4iy%5V^4U|Gnmi{K$(>d-1rLD{a)Z-^1CUqe&>rTmX`<8H9%-I
    z<Q?_oelrulWTB%3tMb^c5_U@o=67S~c4A-Zac(cb34^V=Xe{2a6soNRcWfd2!AeYM
    z6=O4&n`V({6nvv#;NYhyb(b8~Hi@-G+pCnH5JfcsS87b`erK_LU6Ow&S%bBdCp%3Q
    zCGY`xeiv!Q<;IfDXqvB|MPk32t)GcoHe$ZDlpreRIBOK%2Zy1f>AKOnuNnK9#YqT@
    zCCa(@sI%_{l8w0zmzo_aUfP+vq44j8qbd)PXV=m%i=!p18!Q0x5xBuIJDmrBT>NL`
    zf~)8|3;kCDsQnQWnmZPgTHA@n+9vwdR*y)z64+?{VIyG)r4Ap2o?Pj2Wz(uqURW$$
    zyH=S~o+o&z+fAkoEMQE;neUYA_{J+-a}BPO>$7tB3pi@&Q<Mj8sI9ncrH9)vr_Lof
    z>KRH0Jwfr5ms0BUO9-0^*!K-apFIj)<2$bg+`PGADO3p5B;aEs@$TLSe@?ActMXcX
    z@I^`+^N#!LDab{3*;-X}3_C3c<<nvj@05FuMt$A89l}3GEX`q*i*S%bxSGDrRi_zC
    zsUbnj?Q^y+{GB13{}RDBSEap5EdjjtyzAC`9Q8ty^A{4X%3`CDP&2A~Ya$Wg^;GfJ
    zlsu2W$#o8vu;i<|fs6k>k%RkiCG5xI_zFYh+_fmMsNTp)Noe{nk*z9RPlw?<s|OG%
    zj2i@5Z=%eo`--G+(!e}X5`id%4&X^#c`ub7>h{_AlPXcGa&Y`}lzNO-x9(-Uy{g^y
    zY%IWGPpOiptUYHG-u*3@h9s(`fR%AgTE0dIxHXM(0Z)8=NR(7Gd+;dn79Ph9>dcDf
    zZ5zPb3HoVkY7?lt_aV`cC~Ll^B7A-^!IPby!WeUrFyKnDzbqK|)Jp`9w<%kbuzX{@
    zOS64(i8fecal<ce6>#VPv4-sq$Y-i}T^)j7EZoWg^u^+TNuI?Ca;TQbecUF%XQXzf
    zqtw&2+IHQ!58;UPz-Qr+R_s@o66{%t`r3HB(HT_1IFi#Hn=yagOxTU@69&N<qwr1|
    ziq3{`%KVffD3lz~$UPzmPs($P8lUvYGkJ0s7XIS}Pnq2hRF95G2y8mE0sHNV`2T9b
    ze0O>8xbx$1ZSntr<1W;Rl7AO(Pzk)ZgTHS@!|jDDRgLPz-zl!#(ektCy+=xncZj&(
    zlq~#tGiFf_{9qMfPk=q7-l|WHE>nT;ZN~h&P1z3($NS27ypN8=+p7w7`({jcNDlas
    zyzgkiB?_WR`>%Ndylj+zZx<mtMm&fp;aG~fV->N`rvJ^WGe+QLV!Kx1!SO@T%|&Y$
    z<dWVKZ6@pt;m~??6vX=q-cgUfVI5)Kz`d=S`<rbjO#_w)Cb2vgvYaU28!d;Th@-wf
    zxRvK}tz7TVaKxSq<UT@CBvzi!%`pY=skG*8zuSxa#LAZoe`w+AHH2SX?%AFI`<of~
    zdsMh`=cI;VDb!&N=(9%Sl|o~l?NYqZrMYDT=B_5flJnG*DBBfcBxZ^54-u<(6tAvd
    z;LftXAs)>L%I)qWa5<`YZ98oQ!DW;2>l1M8+TNLi;Aqeb#CsSjihQ!1+l4!$0*Hj=
    zkCYpCRLl2g;@XJC>6eoP;Y`KfAh6uEG)uN<7>3gNvwK)QSq`D6;HRqzR~0?|#W7Zo
    zOuLRVbqUl>8!(*#%Or+x5i6I*@f3c#jq6@5%Ip7$AdM8k$jN)eM@|pe?I5&Dt;k{D
    zGZ-Bg8KvOJ26RS+_OW)%^WC0am$T;ZMs)ORT>;#%J`z3~#l_Lona9M7xmZ45ix*NY
    zis}>zUADGP7HhIn6u>fEW0NA~o{SZuP?~}1X#Xqc?_oG@NZ&B9XyH$rF&8W(xVIza
    z6P5y$)=#xtb6_31A<<V^cnS`wM;i-Ibvcq#$@ZQBBgWx0T>ZU{#k)r`@QlJ;Wt}aC
    zD4Gu#Z}=KUUhv=~?Z2FkpVTgOdeu5@@jOqI|B>RQPNUpou~LlG^}GSBMg1!OE}Ecn
    zh`6s6q6J5aFpiY35ps~@^V#MKmT<BYN^9qKVSVK+<>PJG2ila~t17fnnJ~Z|TQKX2
    zI;tZ3u54U)Ebbk<9oM}?(r9Uvu5>I$Iz*W?RU~Nk^#Gl8l;=gk`UCBrI%7V;rb6HN
    zDFi+-8vWxb_{xOgUz$DjnFRz}3JyTLbVV;6SmEfizlaIuUvPBgdN}|i<ohc{$=<b%
    z`)b0Goo<N5{cQt;dGE#*&HNN)hfS_1AA&6f>@U_}j(?U?g@)T^llbYRg4wqgjrP5l
    zdK5=LL+OVr2#dDwn;M`o<@z1Otp49(MkYD~(G0_nvHi0Tcg8hIvPv2#p%ndM`{;G&
    zF^#B<b}8cTtRVdSSfl?kD^SNb+6Y9ik^Oi;3{MW%NAV3IMXA^5eTGX&u&3NWO1y#N
    zC^wQUP@e66g&^Hm1C5~6o|RS&O2V`4n1b@W>!yYJ?e*^ZI!bWtvy|FOz0E1&8M(n?
    zMNhz$mTqfJ0Up2ibT%|+F(pHg^stQ?=qgfi;vcDWW&3U&H<99rGs&NIM|{$(QW<Ue
    z!g##XhxcuU;Q`mLCA@Tv2iDr8GS&hm+uJSoTcX|HE~(WhR;(0@pAda=C$4I6<96KB
    zDWU*Z;n;#vW7Tf?`~Ky`K1vOGXT2?)J_7HqErfS$arb=dY!Y)~V4%Vs-tVs<e5S{F
    z1XbGD&yT}9Z8+Lf?w&7PMEKWcZ82t<xU|D0EHiR%gXDmJiX9*`?WF5+dh0S&VQA7a
    zI*od<3nSQ1TDETO7sjGz*P_3;h~S#(_){uT8n}2R;SKAR!P=)K<f|0LIav0tN#v<*
    zS5N?3TS>7@AICADD&<gi#yp8AtcX&Hc4!Lq;C>{Obb0;Me1eDCl~vZ-Z%)MT3gAbp
    zRaB4?9j@)A$zBP3Y|lor$M4COiGwg2$QDsI&SlUu8(jRKkgELi4<LKVk@OsQ6SeoQ
    zP^mtHCY9Fv*BUP06Mty3tdX(?4tktYzh)WHem^02#A#yXD<#g4!m(vIlGWeYzQUjX
    z6T#ms-q`7abu5usdowSe(vtw{QK|q+MT%`?3ia>(9W{BN^>7>J2P+5{=aW7*G-Fet
    zIdLd@LlJ(zp%+`Y3;4DO$)N&7)`=oIi~qi)duLd#`;t;Yy>EjVl`akXL<c5t;ZIuJ
    z_f5l7u0zyS)9?<ebDqJC8!#6y?>+mWgcECVH78Z_+|oG0+U2{l0_c+g1U@HUNzrT(
    z1@fB`2Y(AVcwz<Wf-!jAA^c$@CMv1DWHSB-hN7=si}`yKVQUGRioK(^elGFvw$@^C
    zMY{*ku2InqUwk{+8G{KPTutJ-<LG{31OCC+4?JnMHi0^KH2SpRcoiPp-HbW+B|=F*
    z-XRuo|9;4BMqK_VDY^yMg!3ot*qr&3CH?d>0T5@)_kShIpk;vjOo+D;^bH6-@7z%#
    z7mdZ+8U$@z>ehT`89`{H@W_}L-y`z&GV&R9qRh^gd)(ER#?f~t0fpJsP~}5M0Q0&v
    zK1Kz$*ZXP(IB_W6_b216%3?2FN!YI#DsBj)vyhTAx>P*r((a%DqO~WEqWW&i^8}(M
    zptD3l7?gHEn=uBI`uYUxePjf>WZ~FnC|xrR|H)45cb6*<Ylli){poFd_Fz!}nwQU;
    zk3yLqt>E!b&^BWz!M@kf@bo#fr9A9$w@`cUvVLAb83|Nl)0S!vHLtsO7|1*1-e%+2
    zUXiC$FV-JH$ZwQzsOeSiY1D%2Bncg&6zK27+v?vh&yRh6KUmoJ52y~zr$^%*Q1`M9
    zzrBa%`t_JgR(ci_#yJ`B-;O#({YE^%-KF+^kes1GK>lsAM=x|?Ykbh)ugT+!*VO{B
    zX9a3jE&5Lzk-0fajZ9l+RiR$&#!A6g^c*(|rqw7)^uxT4H4^$w*uceNsXihHW;kvl
    z&h@>*5A(6+4vfhIj7(cj9qy*QUbBYqoek(om8cQPUV1FsX<cT;?zuF;xlRBWtJLTk
    z!i<JJY6yQnkCX>|WEOe6L}I{qw$`r7VMX{OAv;Vo__yTqGO={ei@26nv&3a?`Y-nM
    z1&Jdg;U6Wjb_9}Eykho8d0s?jA`F5jJCr?RaY`Nj6s5~n60Xek4Mjp=WYXQU9M1i4
    zbrz8VNQ;uYLvW1Usqw}TT%~Mps<^4-UW{`b3DwW4w=d3O|0u%m0sl9_diU;2;)<#G
    z59~)`zuMk2c_)rQ^aADd?Rl><Ten@yf0H)R;M!HKk$82mSdn%S!v7}%auRSwXfc1?
    zjCsBj`{&Js_wVoW_b>JI(vV?0`sRsNjnY$gE0~2sp*CgFL(_!&kD_$mT~zgyP|ZaY
    zHVo08kF}-@3J$CiaK#?c>j>d*6Jh!7YnbMFuUPy);L>fwWe=|q#ai7D2k3L4zI<@l
    zRi7dJ>U~sOQDzhi#f`wV>y){6y|RbY8s0q^ePAuBC4ld)AXL_9>*Cg2Ovrt)j{g#z
    zBhT)&1`!+cwxM9{+B|l8l>>ug3cyN8cReocT$57pvuPv_YjA8)3(W1pKGV@_H1?G<
    zcsGtu(}g33eL}KEyNv>f)<d{F@joT?kr21(3W@1w18P$N{k7$u#Tqlc(yBdDuqubW
    zek}pkMkBe_ZpY`%pqzhK11JHP!m*ewU1%BzCo~Y|L!~eHr>b!TS7U$uKHGN7eA4$X
    z?-LH_*n66ou}o2JK>4Jkd|vAgw7(KTn~S59E|S+;fTSs6wW2OZ>9U1VDzKI>9!S11
    zjXmdYs@BFs9BVDs+K1bfdbl&8R9JLCw^B;({S$InKPB(oC?Mx4yP@z$XGHaSeR-u)
    zSZX}z386deV*nl#9H29ysQJ|l5(m^d;RnYey>KD%ellZljLio5e}_D)%W!GJMY}@?
    z(0{!o0NF$08V{kIXFXG_<@+(=TCBCL0sOrQbIt;StRW#9vxlv-<l<{2eJkt+dbl1+
    zsiqtn3pFV$+VlVXA=CQE$4&FtT3h;m|EB5>r_woRs4d3+?n1#e7WM=BhR8l&BdGZ6
    z(SACrdHAjzpt&(mD{6bzfFlA;q}*Ybi48Wmc`oTfh&po)h3_0;yHTD$P>4e${Pep2
    z23NrHHF^KRqQKwH>zuxN6W40-czTsU!78;Xhn?GniAl4?r3x=asj*)ehqr%SxoE1h
    z1ZU2pw7#I2RgFGj2)f3RtR{;RjozbOJjdVCk5IR}C;)J&s$)dcX>nmI<lwgB7-P2s
    zYGVO=!4l6(Tv{i8|Gn+>0IE>RDmB2&dB<TX0Y28+742?sk{)2f;GhwN2Mr_m>xb#s
    z(v7}v5y`K;ldO=0O1J`})s&f$m9Yo;LtOoL;dR=-r`Y-!u^yE2K!u>Pa*Ylf(0<}K
    zC(?1^bn-uWiZm9!d=S}V#)nxU^=!|I6hVV1<I~x_hih&T<)+G^lx1B}MLp8u*^s>@
    z3<{fz<+#gJ@VP|<%d(1N8?Cx+HvXl1;@>ihL`)zfk1pcR>@S|4zdI^`=!U<9qem{6
    zSZf%rZ1h0^xui{HXvC$>2ncoAc78_JDN}97SKrQ-gGOWP(lEXr``*czlc(8m&M?~p
    zBvG)j0}lmNDGb{4H}*J%F>^5CNz=&x{3$XG6`1mjdVYtaNRJXhh&S{6f4siM9baVY
    zoPm!{Z?xefXW2Eke86t`&GBSUn<4JMF=3U5`rhGWfBz!shu4zu6nySLa_D5z=|s;P
    z06+}-;RqQ~c$e|&yEgACD^Iz#oD%QzDXh)K1iz1AIbn#^54B-0S>bS!8`fi<==9Y1
    zjO9mDoIqQbEnsB1DC=6mc77`gpd`Tg<-CU3JG~Qb@P8zxyinZ9p#p?V#if=8;W!08
    z<$3kni!z&s*HSZS5a!;GklXx43X48V@lU5vy7oPk4jWH5ki=TMz7vfH84s}5JhIkh
    z2y6iFnL_c=^?v54-%u|=pv5uq_7p*n23P!DLKyQV?PU@;dqES)?><E3sx~(ow<bjh
    zXq3_mTkvW;?0E;0JL*R2KKT!-em9R4CI}xoleSBb2s<m2efpr>ipyO5h%)W9UaZyE
    zd26f%vHj?xoN84y{p|BzRaRZTC*E&o;@{JPIr(`?7cVDJCJetb8Q+VE^89Y>`g|`l
    zG~&oSEdMskIr<zfwfdTb1O86B!8ri}BQnd`B3w1%g54mNb;S<8@A`ZZ@0>dsa=}08
    znLdQJh75K~56s<+wop=vgte%JoAE;h=>!%VhJT(%>fC+F6T<o)G$u4*Uo0WYE+oVv
    zE(gw_KiTGuUHeE}&OOTr?pfCRyd4l|!|a_4Nu4r<!Utzi`0D*s-tZimL&s7aRTUO4
    zeVEESPNU<U<0&3`J@p0G`J{>R+(JuvBGiv~9SJKt+4?hXT2VBwV6<R8w%UizK#Z$W
    z>b*m(E?KBcz>lZkpDHsEj%{=ql?XFvE?__V646vSi>n0~Bp`V-h<3Ah#UGXZKSdPJ
    zZsh^=jbB|&{6X^dNr|h@dlkYDu&5%1U34z1&Od<mh72~7fJ64cTz5Rd{LhdRX-+O{
    z!+U89URxf8Qu^VwB%fH1f5Ti-KYoY`8R)qLSG&Dm%qmpL1N#O$Hrwy@STp3`upj<V
    zj4a5VOGtKysA?a3>P!kVh7jC$CLQxXLt9M>`@_e`>^F?yyNA(@_Q8>@{w6?Q<o>V2
    zr32q8>k%AbC*qDc<D|6Sknb~zm_aet$n0v=2}AMFo_FF<bR_&e0i3gdV0}SR<+(;O
    zdsUIa^-`D=3oA<E352*_8%sn!j;37NzFT+z{a1*~7QFOdzR#c5ck-Jv?_ku2=g@lW
    zBnl~yU{xnPyB_~5_fcgmB>^A{$KFVt30(*fqSV3H>A_$vHZuM^6chZ_5~gJ2A^l?~
    z*teN-Nc&snsH;+<r{vi?7KW+QXOKT_I{9BdO=e36dPoK4`s3+5`bO%y^Jo&n&g`pL
    zA_0ip;J3=k;wm^lBQjJ37@jcHCD02U<09eeEc&JeJ~RUT=VoOa9f=JKd|@%c!mMIs
    z+VZ;@Bt~XXEhW6eo}ySVFT-DQqI@dFBmRVZJs`??@$RSqUaM6C{`v4)j(&KZN@596
    zY?72#)Sz+&E)~<ZsVn9oSsOrjFD|FPhdjIYhyvILm&J%W^ZXl^y?C=%<12uJoj?sX
    z?6_?KsnchYpFNDQGKsxyA*s*qLoTJ^=kK9w?`vo<cs{mRlKYDkNW^`3iMQUmQ7p7C
    zV4oV4v?}8f)%hl|*5>=|{`5*zgBJwXtVR7~D*hK15<JkZsrL=Po{m2v<5Go#5->4?
    zTF}$`{CpgT<WyXVco#04lM=7<FkCg^QMekEH#7wBMQA>ZtG4^KXvnWqt^wa!*7bmP
    zn%R2T-n#6_bw6D~kE(u}_k0|u{o}FkM@2{<A*!%*DWb3KB=<Utt<y-m<(m1&5|XPs
    z@hTG7cTb>j`J+?>($#O~Fv5xTgxD~UkmfvbJ5@{pEM_}LmhDFCDcqdF-gKY=(i)r8
    z5kuAb>+*f~MJw2+8g*X_=Cl_G?rO$pW6TdG;f<*%qx(y<*d;xE{<a0juDob~aIO{C
    z`n?WEsvG`BDuDgvL!ZOZd(XoWc;1gAraU7;v$Nn0f0vES$iu4>u%!-{dU-*#doATU
    z1B1&<?1|&HtA1WZG~LFCqFBC@-0N@{fb&oetJ;BTtF->vla2>)=04=tcKXSi=ed}&
    zr3<yR)pbF~m0bTB$7^Vcz3=n9^_CT5;)LB=+bXjUs{CHTO)bqjk0Aa>M>nEFlzO2H
    zgHq}%6YvjfEX#+l+@!32aa%3$16<umMC5A1r3!x`ilAA%zVEyd3c!<?<6g>bzdA)z
    zwaVej<MQIa6wRe}l81LXu5P_jtabvIU;TzWqo@{PupE3V0OUQmCXf-8IEf>`43T?3
    z1IGv08J5BkpLm6R?*J;XMqm3Z70o&HVdE%OB!b|ge^UL|B_uDpk1Fe8e=D(nT9o@x
    zdFHKb9}9DLTz63(v!+{$E|?sY=mi4P54U1E`{jWSYd|M)vlNeLu<Fba^qHP@e+%}(
    z)&Wz;A0*C)aURGEq8K&_KG2EF5=`4(A%3;O|9!FW72=X?7J)B`HY!jqK*>mqvPnFJ
    zH+$uA%)$+Fc+bZ53yix}56OX_B?mnj172$7-)d0?S4sHu_)c^<HSoC~9Yf2=YKq+j
    zilIe~tHZu+H1>dz*smNysk1<8MH@9GqjJw}!mCNc&2x1oA6RaEN!85VtR!5*;4(0$
    z$RUi_e^*hiLVT;OyePuA0op)V>!a$Du(5z=!3)qm<1?12Rb?fd((qyzwkL!eW|D~0
    z{|W}qo=?y{5VJ*j-iR6-q&;l5gc8GZa||D-68Wo>@BMvyti-EXr-`MDXt0}bwIQ}2
    z(!3Z~4jXla$>W-1+_#H7V2^U8-8;nkZN_!rS%~Z2dN==NF7|a?t%r{z`2OK+o-veS
    z+IO_2&_Y)Ub<bkd=kI4+Q;$sNQL2FQJWLRXSM&=BhklGpb;fC~^>Th@$$q?5SEGQS
    zgB6sje8Sn)s6{;_nu=(kdA}NRH|<3tE*!UPAnXj_(|eS=VSB(OD+zz?T7~t&4pJWW
    z%6HNIvDxHrnNRAhzxKAM>k?x-IQD!RBpevOEo^3c6o86px#74*mEXpdE*b@!ty00U
    zgRDwv)L*ysvhuRHTyX=A&O1RY=4M{MqL|myogOOpvWhoxnBxwv`9mrhy(O#d9yO+E
    z@*uKlA6B%X7B!;=Ww6az##`qF9WBo$ay`4`nP)`_yghc9Dr7IdDtN$KeSJp2U{I<!
    zK5cE4k2klA1W-V!EqBbZ{*+kz^@m$A^SiL09#i(hSLEQhXDGf*VwKicA7?tI4kq}?
    zV^sWo2`OVyD6L~D>G+Dg(-CZ=PsX+|fc<boCYFlJ{BwzW_LZNv*CZ(Ya;o*Nnuh<i
    z@vf3C29CAiQWC$G*d%h9u4dOtL0So4UfP9(A7JqbQ|P+yBWBI96UbHgP?>;}rV`%s
    z5lYt|k7rRyT-M@oaVfnR==6K>^bV0dh^8jjis1iG_}#h=N7+@%T7N|jtHIPbFffJ1
    z|3_2bn0{;q^>}9)AS_w<@}fSa!M!V?j9Ju#ci3<JA?rKwZaRt1br;cm`^l!YI@!l7
    ziyFX0wHni3Ee4PfVBuQIsibI<ZiFg_DAjty3=(gvcYX42+JJeq!&BnER0t5*B5~t*
    ziLI;T^<r_M|F>R8&*jC<d?ckQ95s>fq^Sh^jU?0>{`?aDxBf$8U|1?x&Lg<#G-D|b
    zxEO-#FdSL@B#v-$7OqI;f6J{92Zo;p)S?t^CBiWoXfC=&x2yByPalC7asP86T)cvC
    zY9%VPaKR$N7rIqH`Bw_<CyX)X$qg!X+Dy}P$EkGgHJG3}iJjU=xV8)Z%mz|8YW(K~
    zYikradzNj?8uYi~6<k5Nzh1sy9FY*8xclIGtM;!&Sqsl~V1KlV02?B%iNSTO{SxIV
    zDN!H(I?9z}n|7iH>5FpO4_AOxC9&?axVnVRyV$|JFEDgp`y7qeKCePh-C)~AK_>*$
    z_=)`PGF&^V8zn^3u{~KR-@lpdE4zLxt_?W!{cfyH6f9<4p0FW-ZY@#OT+-dobsBS^
    zbOx^SU4G`=g#@6KYr@joJX)m_)}A%TX3v-%<nLWh;`<NTsvjRqPfC|%v0$M(g#~57
    zMpK9H#igsk)eEqr3gA2uekJh>pdx{eh#$skFZZs-PzM&a6ydWA2@b7Ce`*iB+JsSG
    zT1e@|Zc1{{&J*kLU5PED_~fg&y!p<s_Qz#54#J`R@%4UO8t~f^&X`@~P`2U-S*JKK
    z8#SE(03ZNKL_t)cw|-2S*W)rWe~A75Ccy%x;fhd3;?kjiV)yFn_j;{=;rc#Y4J5ll
    zRUDGAK|+x%SyO%27CH~AlL{&yhNV%iA!xkSyiy;SV)GXtLC>THx97LaBaOk%-Gn!(
    z9<>0kKv2KD4Snx&lDEw#wYpO$z(o9$B{)WE)T3dW;c9;nuog#c9Vsq;X>8i6K?!)i
    z3uD{_+XRruPqkk?sbHp7p-!Ji@PVOtpBv-ynF_dY5y3y2mBrc!^vmM702flu3UuHG
    zimZGSADV~bQePtXAIP;vqph`A%Cv0imvNPEy9)DdIIcxkM$qCe%B7_5$IU6Q|C8O<
    zWNC@Je<#+r1XR{|=*|$O747%zLt%6V<pF2SCwRCmW=yp2Uvpg0cJ^%YT5y5uUL<qT
    zT~rl~Qh3QErc^@tJ{aqavb9(nZ$ul&xgCw;4cxXP9>7>48lh6*n3QFYw7U>&AJDK!
    zP$*u#7S&XOx7VW1A5*Swx?&aKKbkc%`F02{m3Er|fxdth)go{g;%c1VEaCsY#7@2l
    z$d&;FqEoK57T1HILqdXGVQm!G{#H2@NpV*{&d$YK*d5jgTw2(R88zJ*GE_m0vD99(
    zmX=E=JE0%me<HID!v|N8>?xs|dQi8`CtaV$oU<>v3H3IpP8UdOY+a^ks}k6%B=(sN
    z_!r(ybusJ({}5vp4f5R~58&N6l;0*ag#&6FroXWO%W@jenndW7Knt$blp341+`1Qu
    zx&*AwxvnP<w0jB@hW$X21vp|&q(rL44c#nS^UWxoeXH;pqPq3Z;5Z96vukBDcJ}27
    zfO-|KvF92b;2n*z55=&iL7Dw(ZR_l6)NPwF9i_g;MgdkS^|g1{o(m5oTc5&i)jw0P
    zwhKM$Y8pBVy|SDTqkE%(wL63XMBsr*Dp$A=S|`|3DvVl_M`gp_8wP7_Pl#$OVXs(&
    z`NIZji>8c1dtQ>z6m=>=P$=TE3Goe!ynvyCIehO85Q6@EhKuA{jDlvX6vAiFF+M|H
    zuCG6z;dkshd<H9?&9@l18}k3wf4ANz9#&QiT0O45B-;B8;)&IPO+DQ~{nH)f2U5Zo
    z$oSSAIFe#@LZ(T2U`nW_JZe=tdR-^p(pJ1@*W>?tCCQlZN7JU(Q*KGWLmt2nasA~=
    zDNT4_EownGHsiq_Y1C64BxUq-o5YeyqTcpyXs1Pc-T^hCJ%0?|^eWWG0$jO@;2$l@
    zVy%`7YZKQyYVoxmS4-dhIz6EM#LYiW0KWeB-CCQV`-hY8k8DJ@6k-1-RY^R74jc=x
    zUBJvQ%QdHQS&U<NbG!chZ@u}}|MxheRNRZr^oGkX2T&C*wOS)axU;`f)(VDIV+N%$
    zTYAtPd34?=jku`JFX$~}PszEi5KQM(%6$vB30lNgZwYL{D&}=#TT0kbX;-5h_7$8a
    z5s1x^H-PrMkB<nsc^3ZOHK>$^aTyqzMoHUUgG(dbf}0wXm9LSacwXz3z@-}>6+k2-
    z!dfKWyI-lEUrZzM&OvAc+`hR_^NV^36~^J_6c|xpR@}Z*{wD)(=@r#PRQ3CQj=lp^
    z`WyNl@d`Fk?juotQwtk9(et+8Z|*_or7;cGgsz0$X5@26;PU=Y;)>jk+Fs!wC<G|<
    zYA9FUNR{VQ+={%z9VvH#xemt;9D-w#1>YPWst*k>gMkWos1@_ub%e3Z*$>33MlG}3
    zMT31AmqvN$HAH0Js(gmmdPSL#33BN2V(m7(NmxP}*M#M$m<M?sj{p8<gjc_o&~lF*
    z5k6TZQ503fD(M{GK;nsYLiH^Z?>srLQ5H^DBq(U;Iea|bcP=J_%Q_6+(eSRn0+<_f
    zYf1p!A?k@vY+(9?jt18Nq*5$n_>~Fxrw>PmF(2pIPV8qF5}HVmwC7>M3IU;_TxE#M
    zR}aB;J3WB^Zcbjb-l}|t*J=$HQ21zEU%Fu;{PD?3n<Z>pB7o>`*|EvSJGwH0+nk9b
    zY{fm&GjKWh``?7U`U8$TaiJ)I_3RjLq5vc}_0aXyITU~V1c@u2ASGumMY-*m1&U|S
    zq5Ja(lb`l;l9wDwu_a6D(X~_y#`9rZpURf))$H-LMm|KvEM0S8h{iz0=iHwRJgVTd
    z8V`H&kiNpdpc{L}TuKF_IJ&`b-+m;2J3Z8sDo`k8VlKER7Ntv+$+f)728Xd#D<MTh
    zS+---R_#-5(5laU7tucT9&F5GPkWKlsl)MpIRk%IHEL`cMrKe_->?za58zhZlphs4
    z>>rbB-_=(}LYlj9^&i*C|L2H8NXp*%@-+(ABJF*+@#K%=`VhVzYfteGzlTd_M$ETn
    ziJ>RUb-(vb3Io25tCi7m76!tawzhhe#d8}`Qw9-=!1su8+!DXnwj9CeD(v72?ETBo
    zr_LmhJ=i2(+D$v++Qrw>SQy~rkn$JYhb!fkg;vW=*IT7br_uVdRfJJb<AQ<n77`S#
    zYm0ovRQ%Ct*uM_-<0<&PlW1M%oX_HBo^^=Ac|Wex%73E-#xek(A*tCKpCLSTF2R8{
    zj?WOjjL%@>d<I2dK0`>E&#<emiE@DF383|EIluz_P?B?z7jLSl-0Qr2KLSCg<0=c^
    z_8MQ`ULySa#*p?-ah<dBx=AeNEjU)^o3XxvBZ(CR*BU{&ZMxb9R%HP?3aI2Z%H-B{
    zLehtQM`EAZfST6WE0=*Njkn`Qw(YP2*obT475%I}5=4cyBhr0POi)T4SZCG!`x>vc
    zh&|y2f|Xh91&avQ7d<^HZTa>DJh8ecAmgF5bHh~(4E&@Vv}VeE`EC-Sdo^pMT)#~y
    zYeox**;E9hwD!smqXaO&N0HI+rSbTog*7?srxy_XV!cPd3BZ@&nqPb^1|&w^a8IF}
    z`gyZf<hgBcr>Fhoe&$M8Ga+`63Sxa$%WD%IirH0Gy{*pmwVzsnO(;sD3<3!gZk2fS
    z)p#Js<49Kb3BGcygp5PvI$9Li7)YzcVEjwYPZh3h+3+`E|E+jUcguccBy<`k`!NKU
    ziXJTcG)VqmFMFpv1zp>i8NI9(<%<dKE}=$LW6@q(?mZ(id(5j*043S%Xk&-qN<kZ~
    z*1DzCt7)WHo>HG3jUJUoT`?7ZWD4H(tP1|!s;o9)c*$hE3coM1`GXY%ru^^@5e?Zu
    zdH6w-9JZ5j6xPp_KW~TJMTqekOqtI>%Ij4CaXtfWCG5AB6CU#nr3YFu5pI#xDEw4X
    zet%YM-y%LkK$*|*X02)wnENC|neqx&XM_mZqvF*>*^$VimqZ~yk1K&*501X76fDnS
    zi^h3WjVaWe8g$z0JwHWJz@Od0>pvb>Qu`+yOYunH3%K^`)8$MwAKHL=-~f{UH^FEL
    z%`~pw;bZdm?eh7*;f-%WH%`3S58_gMQ5+xjovgv7>eBLWMt+_EDp6`weH#CTjig@b
    z+C2@W5Y}~*2raDXAc+YF%jcwIum1G^**oueJF2ShfA^l5b8hb`q>)NOXrT*A6QqlR
    zBBB&QL_`$vMG$xtd_)BwMa72)R8)|v6h%b@X(9+FA%TPtLVCL?xA#+K_J04^YtET_
    zbJGY3g4v(XAqn@Mb7uDJ)qd-@)_N-n073R!23QhX=NUUPjZMVRDupp)OWp5i1>c{H
    zKeEL6&j-|?52)_{`ByI>{9CKW;Ry;{>Eho7MDY@6ee4Mg!Lh6UB!_lOA84%oO(@^A
    zpa(lV?b<EpO+SqdkZQ_dkAIo`eQT7l0NV2ouD0sly&^s15Q4u>%W`4^@uBl@oQW$a
    zHrI^%23EWS!aqdz=u4uYP8Xr58{~IS1ZIek1qH!ZDnx6Q=;xpXXxmL<Zo79S`oJpm
    z@D#k#g<05x?K6;8Ff2euh8N=53n`9B+uXWVtoDAgkJ*GWidWm2BjC2F_@mPBuQuYY
    zIaU-z{Jt(xLdOE9?cnE-Bj&{vjcC$X#DZTTfA{3y83~8g-<DGO@EZ&huc20cH-1$D
    zRUa5bNpl8oeiIF+OvZb0H6`+0zF7aO*i0GlrW8PP!VB<XSATQ~YIq9Lg_g$zIkZj^
    z6{~!>b^<<LD)>M(D*55xc$4T4EgGyb=Z>)YZwK1o8x!$FGo}ZmFESG6#RC_Q!%>MJ
    z#8s(nP7%D<_zas`_2R}KDqzt%TzYOdQBvu|v%U^D!DocrLl)P@iZN-+o@Iuq<yMvZ
    zD703hNXLK!LKpX8Jq5$kr~|6eoe|9JRCam?W@QGOpioDmln&W~>;EjS8LLWoAMw1!
    zxUeN0lA`a*aW0(va})N7Rt=u-yHZ$gz~#X|AfMSPf8IoKTJw%rtAY1RNb)>`6Wdfm
    z8TjJ090?qSwO{-3OEh+53P5XxDDjwCSc_6M$4nsl$xGDMmf(-B#QXV7hKb;JipTsD
    zwlE#Iv4$WEB&|{^C_2LeREY=eA-XRTbX|MlX+w>wOBDrbbSdhQwNbuI!{K#kPr(nC
    z65YMl^*Q_QZul3E!K?IP|0>i|E!d`9zl-e=K(JK)-Yc6{E}H&qIhfB4(6-5S4Dh}f
    z#OgDS&oCm5TAG2*u#nG>aJ+=yh&4PCFxO77-bJJFOyY?>*NVA*F_A5nZXPKi$_H@#
    z@0@7!L*?3*?|=hWCBlDz2!DGbhPM)89Vp(x{o<i$<z^s;!&5M&96h=ORTH4nuAh_6
    z8gyi>8lJ|>7<A;SK)tNNZ^_}chIri(y3|8=g^I|1S+f1OP8{w{4XKzUmcIlDQOYI&
    z;`s9dp#5XR2|rkeeytmO`2xa=$5`)*U5)CCunT&z@_iS`p6n{`YmoEv+zwO%&2m2X
    z5Cb-Rz|YqU=CWAM^gbfEk5TNNHA9|dmSTJQI34KCSx3d=i>d9+le%jz)m=Fr6J`Ge
    zdG8jljp2<;<-wdMzZ16PvDE>p*2lECW{0IHdK$AZF;P7qDEMF<`ZsGmj%<m{bM2Ui
    zntYAMyJ~F6ClWK!vStGpM2SE?C~@rJVi97Eo|X-{NDlW24BnvPy~JnO)cT3I&AkcX
    zpDT)JzQpj~7G!>3cbzI9C~KxxqANY<jZiBxcpV|SJ1h>X1XwjlCcD?T7aK%T7_oRM
    zii1*L-OTlWUM#dlsc2YW8C`;UwF{ef9~;BYJ;MU@%uejr-XQ$-p7>MBorhVUmNc!k
    zb8+2@{)MB%req(!EMa8Z4pahh`?zKS1(%7yFK0vI)P%(OjyLovdBv+laIFzQ#i~xa
    zWPck6WbU?lZ7SjLGaRWPz}m1WhaFdnstYilD-sGYk)GL!IlQju0Ynx)HXMCdW0b%2
    z4W-aZVU0O&gi)UvftMuBKiY(u+Y@IL3-T71GBzjDjagid`F-;Dp`u~GA+hGbHIUDc
    z13E*)5E<&3{MqTi2Ng6_dPE!JGvusN$JQIZHl9>tCbHKr#w_gX*S&QxKEoNC@KUxG
    z-PfY!Z^Ml*I}F#J+mq}~Z251+wS%s}HKzETWK|*{-w;?Pmg6<&(2IM}UC{<%U`&;c
    zU2&tt&z60Qn~Hde5Hhw-Rf)Y_%jR3Y?H7M%t!J53R-Bh7-x)tUd(Rro|E<LQ-&Fj`
    z<?ecZy_)FPtB6DiO%qIMrCe8xBMgx+;|DuX0X)K%4@=#WAu<_Jrmw!;7rLo*(F}1*
    z6p+eUXwEwfpvo5}n6;&kt|uDU$#7~pYH9^qiWWF!h*4h~Uu=I2(2<85ml6#~vl&UT
    zy##!mBIE2^g7SV*uDMAx>FE?H#mV(v;WH!^_072U<S*iA)Bi1k*OAbmZ;0DnFZ=Qj
    zV5L$R6#0>9sP^$^b&^`ti!Z_-isfzwR-@D!lxjigE>UJZ*8RL2*FNkclICg?u((BH
    z-^Kwk?+{Uf6`MGFqeQ)#v85;!bc7J1wWkV3B?qu3i#?$MeQ-5uSuggYd4%6xs?30J
    z_7`$(@ppD40Lg%zYpWE1!Bx<;#WCH|KG))7WV(R#maoox-eXOeB&rqx7mPyV`Tn>P
    zW3QWlm;546q5j_j!sgtdK!ru=I<zm9PyGP9*&_H>4qhdLHxqfU@EJZW_jf;zVK-Tn
    z%JmXr<Ptz>jGx3-Ja&0^Q<iX43f-C~y|@oQXE5@+>ri^_h!Wb)*oEws<4`wFvffW7
    zTkl7cjsE;-y!upu38^KPeoinGFHQv?QHS@{vDjl9Xisa_NKEU=_5XGwO0c$&rLeBz
    zR0GR1*ik8CN0t=uslxMrtqb$b#Y88*O#azc3>HR~piXE&k0>di;4y7BuF22hh)$>P
    z6!*bh%gwDz#QG>8TNhxbmZRqOkm_*LNt!2?(f8;5`)k<V5DuSC{;s|8lT%R?aQ7O_
    zx0h&xG5ve%DWyhL+VHYN=-Yo3`A<DSbz>)5fRhKr?LR6Jc-9MGc_171>AOhW;W$0y
    zJWH)rnKG(3A|;l34NB{~_cHo`YP7L%@#}=owknGPZLPg?Z~OxjDNSSH()p6obX||0
    zk$svZ`PaC3rXk_6TqhPPzZK7?9LKZI$UVlf;*+>u1l^+SVujo#p2vFG>W^#1y;UBn
    zJuKiMoj_k&V^3;8oiYr4P_<zGgB*^mg(Ln&zAe-WY>2hXB{Se1TaRuFp*Mm%_QJ37
    z9oOQL`GjxwkWSqHb>hV(*Y4ud`atH;8gyG8R%Nl%Dp7Ct60FOj#fo<TZ75Y2XKFkJ
    zSC7a4<SuAmuKlbzgflt{Iq+}bSd9k=DEE>e^k%2nZ<z-WuiGeci-Xossil3`T}udM
    z-)k22DfPhG{u^5v!0r_=Jk?*xEbqhKxR`JQ;YX<hM%etzU!ZO35W-ttqO7qK52Zb^
    zpkEZ^-44jR?#hNuJmI~;XXp|4_1uK;k4}K218dDI<?#Pr!k+uB#kPkiparGZ?O8$Z
    zwG+|L9!T)L$@uT98C>||SbSCow#0+)O)~nF2J55r1i(XSedaEBx9^F6WF6{ruM=hr
    zj44I^d=G-^fUMvE5so6e_aNy@@%WT#%wXT_#f+En*qQzS<SoF_SlfZMwnU?D-5vj&
    z5iZMQaig|ob`;Lw0TLcPmQZ>Ji-)#zZKVPT#hN~a8>{WF&M7+cL_<;-z{lFspRXh`
    z3DGnbcCSFs>?|sV$ii3OAj%bE=fstG-nsjbIp}B9op3ibnGk+?Djm0<YTBj^cde5*
    za@}PVDXat2G4FOhL!8k_rgq|ZHr$9eQ6rv6H4s^)RBej&SB)q0$TZ_UIE~=UUC_0u
    zO(%*Y>pZm!=MmmK1^?7x=o=>Ee{Q6SC(@W7EG0~9xMU38v32OvW)n7MVPqP<I0g@;
    zBJoB_a4oY3Iv7f#GP^9R@PSGXdLs8qgrYBs0_;p{)X(?8KfJyO);8tfEA#se)!0=+
    zj|&o5K!{^XPT48;{ehD$S$VNu$Hf+`J<_%=uyjXw7E+zo>l<5QZF2n4(q8PXD={Yz
    z!CRBXe(`mpIX$|6&po9SI+emk=CMVjYtlqdpGU`K2h%rgDAB${i4Gr6{?_S~ia?!#
    zYiE6oxR2EW{0!Eb>)p<07>Z+bEtj-XjigEI<vqO{d9M%DmuNEoHJ*;^C!r3iK{upO
    zn^HMSfX^qJF;^`hd}zJqYh$f<{1B&bm4f%zqSt1vdA&!`l-Fw463gTY)V@`yhu2|l
    zT|v|n!J=Mxvp42lQ7RdN+Xj5)g`zYgz(y!#tfLx7wvc7$lQTCBt&hB7tmUMkMFY?o
    z!ntz^muD1vSE3KCK{e(eYp56Fkn|SoaD_s5v9rzj7JC5kY7?RRC*WaCdS!-QV_6Jn
    zqNwZ_D~Y_s0UMr1t;}h4^i1+6zD)j=F5%aU<d+86&+Kb^|9*CE{m(vX+E9W2%a74D
    zZ3tnkA`FlTQ59*d9Qb(xxUERUbCWi5%`u;$kn(JiCXBUy;k~6OXdIA9jrk0}Z}HUA
    zEtqQ-65hVXS7^_>Y6AWi@)?eh&>&W#t%8`ZlYd_%)@}44*FHg9*f|3HohvczUa`Qi
    zaU))W(z&ZA(DTU=s0x46Lcg>Zd&erwCte{u@So(qve0K<uU1%NZeL9_uN&(n!h&y4
    z!au0WDy%WrFCkjg<9>c{4XP)?-nNqHPfh(&q<I12#wNaceFCVI{tT8?S%~Zgk;s0)
    zr?56`&K0ee2Yh)x;lf_c@%2{yb{hT-lkxA}3t#v5!sf^|bcq7^1@ErWblYuh?IyI~
    z>b?(=L-{4y;ERA;7WR^RwuSHmbxt1ksX!@XOy7VHDNDh{2iTsk97^9VWf(O4{W;1|
    z@Z=%_8=}_spk7=_@YE7g3tDtah9EXbK6f?6`un^nrH!q3E1zL8uIr5_;MJqJ{C`J6
    zac|6uRe<f@v%>wJ59F*Xb_xx&g|H%nUDShpvIYBkkGt^d0CnlEc!$@8YEBpWo<{8J
    zy^4>`w)roO!Mj)nSt{Ub<M2*@Rnw6-=98}yUOfTtZ_SDHs<p|wfVct6Xo{1BGve8d
    zk)L;pm$O%5?~)J^rw+CH?`^KW<I3@PziPz1)S($)V(d**2x5xY<Z{%=64c5JR#K5a
    z#1W!CBcFY?oaL|bAKoB!YpSJ6Mffh3LwFyq5yUtV!s~%6ho@<M@<1YAxeq^LI{CHP
    z#3n1PpTEGg96HLyY;)J)-8O^L+h*zt_b`-3`<^!0Y)Rl^bqlm-`q4~d=b%D>rad;}
    z<vfhbUmGAhr5rsZ1+R4zwB;SrJTgt^;|CJ>s(*a+(b<Iax?PXJQ-&CI!{q)c&5k^r
    zG>iNoDNTiA=haf=Gh8LO#4*6H<?zMs{3JGPX%*nQ?0i;S=s?1ncur$08#2p8&{W4>
    z6;_QeN3H0^F3Vus@``fLa%=;7aw#m$!td6^ot(A4Vyw{@jzTAu+pkw)ZeBsuXS6zN
    z7o)Bo@6w^6h5i0XK4;{<<EF4qIbSNS{Z%5+!{v;OpqQJF0p`!k{wxy(Fb<ed<>PmT
    z_#&u1dsm=-zaK$U4nF=0`MLl#r^}_E@7jmpfU4qIp5KlA*lhB~?Zpi^Le#Nhv^-qp
    zrjk(LLbicP_n+bc#E16+9Mh@;SNJkq6hj`@P~w=iS@yehJ}uvvfUOCjR1auFZH@WF
    zZ>ip;oXGP~qicu`9!c&W=bM(16+{(jDEA|)G*(-b=V9{(K6Do~FDxgZKZ&P8T<uM*
    zn9p#y2&2EWkElLCjZ71X0w`ad_j(>{ip(iP(aDOI3r3+YTj=X?sW7^3LO<6s<}>6s
    z$!AbnVIoU$5G)k=3?*{FN8s}P-yHB6r{So|H;LjsMwCF@YksijSM3h8wR5|%a}(5@
    zQ6*OWZXbfWR8hg5HVl2)8-$HnYk#<maA*quBMCO-`J>RsG@#F&Yw~xm^;B~%3cooK
    ze|-*{Q@#gb2sC5udQrsB;5Zl~6JTT@LfA3jS|1h#pv4>Nl#Ge3^j&=Kex}qZ!>pIL
    zP!qrpr{JIQiUUATXmEWA;&7}v>(Y=W1}&_;RAT=)gOryLY`6fE6LGy9R`2uxHf=@!
    zAhB2+x4j_N=tqg)c@#%hsn?1=AF??LmZ85p5nx}wh}OyVm_kGw*GXwN?W`+@TGWE~
    z_##r@dycZ+eBpX4#3D||QDV0wxBG78Gx#_b<6RPJHHdZXp-A|NGa6+SDajjx3Z<yO
    z96&JeVPuWtH9Xs<tg_Z#Gtt#U^h@7n;N+Ld=Z#`m+ES)rX&+X?l23>?a)Wq<Pe_<E
    zSyGb=By1eW$1lSTCEbN&r)I5{GDcZPAnF3u{-Y_@J7Gxinyy+v`1=-L-##TI4mo>5
    zIBO31`MruN-*W0u^eY{hSGxyQ&}Cu}JW<AXOA7T_-X8k)d&&cd3tk#<<deL_rGdm%
    zy&{OCV#M26N^?*y11Y0{hcc5#oqRVnM~){qv<$Pp58c*_Ztg*^=|ng6p?h<vyipFh
    zb~+?328zTrzK$&?cHAZa(AhXXLw;$WwY!!O&dn28$7jequ-2N>hZhgpX88<u;MY4o
    z!=q=>K5I38g3q9_CgwB1#`z3ggQQ`<DXGKXi>2I4_SuSnB|FD<ZfpY<_hEm&lIWt*
    zcuDXet>Lyk@V~#*Mt7_tx_*ggSNBEH<>QLutanL4xd-jW+I9UM<_`JJiQ=J@0H;!%
    zoiJO%ufd9OdgY!jCLTerl-eaQw#G-#?~yaAm12Mm`Sb|9-?v~JQpNg#%NG#N?@^2_
    zvD~#6{>TzkOAhaYGstI5zx|GdzAs@+E|k|ZxT?AZ{|O4f+lt?vi=$JHkOywW%{x@k
    z?y-ZU4tqsmWGOZ*h~r_}GRm91Rt3*5Cz#zx@J18<iVnQi3@RI<jV)AkRY?kLk;Kqn
    z6L7C{E3V(*ntHxZ5O&pNlqpr%lt%YNjw!dOm)uz+@RF>9`91D}CrGhMWZ`qK5v|D&
    z;y`KNzv=^~`@BC;b<1>0C)5#Lc{n|%>~1qF+VIwPD{p{jWks=-iZHC*NIKI1k16;<
    zpfLvBfua{wA1cUJrA=QpqGAK<ue2-j6$yH=L#y_@wFlJ{8FcYFyw*`_pKi9%>~5`=
    z^;&aC4La??{cA8!w5ZtAcm~J)A1&7V3<(e8#uY<E89j#UIJAB<!16i7gDsU(V@pxx
    z9yDgfAGEgX$a-|r0~?Y;-PdTsHCgO&4d~0?Abg@#QyVzpA6@Exx6FgTwP2cZ{fypu
    zvftxH`F1C8fZcF47f(?vvf2p($~p>HT~@`0#UGnnX%XU%VHK2S@}xSl1p$a>sQGfh
    z;wFk#;<X>*T3yY18}7Ll*T;XFvp6~&oi=)PmcUp-Ja7H~OpHik0l!&IbXOzhxBC#Z
    zhuEv%BzmP=_wN^|ltKjoHjKd9U8`*V;WO#ncW5!CIlm2Wum7daIwAeE?)CB^001BW
    zNkl<Z#K<>@l54=xcpuqB81}@elbP~^lWAJoif!*kW%Jmvbx@MR-~Tp-B;8jH{Q6Y7
    z9(<kDpWaj@g-}dsJtQ#p%5iuf9oirCQ^133G1n~825W6~U@6hCF6*Y&Neb<H9NX@p
    zghDS9ZzLtcbR(`{DcXWF__W06S(LI<%dM&lU}h(2VFa%E;%Lm5#`ZT@UwWPJ=@!gh
    zmFQQxFdDdR4}xSiXHgIKBeTgTQi~n98vIJZD&qEADxn~T;pp4b-s?)h+sfZxCYyGW
    zxS~M<FSA?lU1^~6ibLr+dOTf!TT<vOo0nIQyju6)h1Mg+VqRNMr69x0Dc1RK%gTs5
    zEsZ>^-x<<v9SBs1wPtxAnX^Y08-lFM!Ee`V?p<S}J698}%_V2LJ3hl%`_T834@I4a
    zjI-H4FGy89^r@)rkm1FVVFp;;jd$N%N^%ihBKU7e%v>k#|I3?5X?_s6Y(Wd@e=H%N
    zH-uV4Nq}0=ioRtA)l1tbNNITrauNLWM7mF#O78Yqlw`vAkXzdu!4s{@&gj4%T#c&o
    zi$XKC0`<UJ8}&rktZ}*hMA#B{yuDUj{Mh1`%QJplLVyRhBE=TTdo)T#o`U)S)lnpC
    z<?B-9PaM)8mxwGp-m2`{EEcWxRpYI9YC>^)z|{+hmh|<zjuP=&j;2UYZpMw%ipSOG
    z<$PD;2yp-L7(jA8BM<9067$DyXEq_sp`3Y>h)*lxc)hL^yS}&3ktc3D-EWPl#@_i6
    zR^R^`r5~M4+4C#;QFAXB3NpTK8}Iww$Y(fNBrrc~9obpyiu7i8ylvX;yHaCoZS>rE
    zw4HPh)$JM7)Ow<;K0wbEPwJ|EUW>K%#dhq;v#{rmK!0W=UTpx=JFy+^iOd&pEN;GH
    zWzP-~fv-szk(r{@9%k!v>yL`X9g4N8C5M?<imDHa&1ar#rMokP%EXC1st%o2Hp-&Z
    z(Y1#2MijFb08h4Ho^2fzBRYfrLsDN5pmna`6~jdtoFgFj>+e+spx@&AUK9;FSA;!o
    za#$^gd$?c)BL}1lGXV9a*k!Go(M;0K7cfVTrR(VNtQA!Gcl4)tZtW_?6_?y67($36
    z-J}FpxWPjGe=Q=2Q%l23P{Yy#!X>5k6X(+Q{p0A_r4lo@8SjgaQt{1abQLBHZ}^8-
    z{h@o(L(hGfsykkxbbU9vCT+rmLvV_KO>sJJ;U*M-rr2x0uYX$A#@<MwxBPJv$&M(;
    zTzUw-KXnvpE<TvPan)wfO2nQB`+uvHIer%TlU^qO=~u~T3?8oO>D|D06Rx~3)-f*T
    zi7oyi5)!->VojSE6o4RaU0;Cu03&B0-Dj2eWJ`ZxuJGZIYSfUxa>H);(SXdxb&Cg*
    z3X8bNh)SX%Wrfc>EOCb8)g^oS<((eD;A@3!+`V#`E9H=wgk?`M!QzfF>Zq}_-tfYv
    zl}}{}CKJYI9EOw<iIv!04&6yx2?ECP=DU&4P$77MV&kNhlz^@bEq9-N)UM>e`d2Ei
    zIE>zL6_^iCA$#*n8&RIt+SVMj=K8ti=SWKLO9?NoT9oEp;ziAPo6h2mIJ)x?z+0BV
    zOsjNCpecvO+Vt<&lR0fz(ZvTiq8>f1%JBx0vF4AL5v|K@I0?NZfS(*^+RxpW%)N6-
    zpZ|!i!J0Z;xp0=^e7(45!g0KZN2+ftA37`X)J1||pCeXnbi(BiDYj_xhF4HIdo5ja
    z)={}B5b>Z9*mXV5>XxUVFDxjNCSXtbzGJrZ!OVkHBmCeRIDWeOjF^<ti0eP!kr2pM
    zVC7RS)IZaj;4=*Ps>V)qTOVo*_zWyshyVB@QnMTT?Rk>Vu&HrQ6T}*sO{6^qK(*nz
    z9;aMKS79pB*rR?)&1Hwux3~=trBtA-&6{FoU=yj|GvpOlJEjDPJ)bRXJG~XF8JE`m
    zUm)m;RBlzq+FjB_O)d<moZg}O=672=C6Nsnm4@~KS^I^(*gIAiT7~uV+HN}DbnMnZ
    z_=(?9_2)N9W35GL4{Iuk`xEw0!HyEIwrQsV7<^5YG*2sT8t;!q5!@-Rb{s=~11MkH
    zP0J@9C=|cMm2x?X<D~%DXE=6xqdUk`hG75nX0fJU4&+f1hjwoyXL+gzv!+USUIRZ`
    z2TR%<ka;-oE<VEti1Xx|R(L)THgy+!8;q?YdSR7HUHh~x&xELyvi5hU({cZNo4$XJ
    zDorSX7>KP(JPRXx91m`}MZ$vZH4*rBQ8<TVt+%X?{61BfDj(Y!y1s*dX~s;cD1z5x
    zOHsG4##}5Hn744{o30FcAOS5<#w=KmfAOP!-TuSEzPmq8=i#Gm{-sscKkpAzb>#|{
    zzDJaEB>TRnU|%~Gz<^aJ2l<sjKsMxX&k{FrUx~4<>dx~6N&A#g>|1YQ05Q-+`|U#0
    zSD&E1Dvfv02%4T>K^?{BlX2>1;vnD6Yw$?}BaHjbZ_aT=HJs(riGP)p;y7HjU>-*h
    zI%p$Sus3nw<G~9lIULFYOWKsbWETD)jK?^|8Gub=DUtWa?&Z0-w%(gt<F?S#@c|mU
    z(6NmyPeFaDjh0_V>%vy_srOOy<;SU<e+ey3-PU_*34t8)vjhygNr2XvWK=6k?*fYb
    z6}H#P0cXm28DYV^(Ty2bO4u0^U~T!s>*%;-3|>mdLJiZ~F;6vP#+0J2T}(8$yU?jA
    zHvJ0i`FRuOe==Q_pSfq)^~+P}mYxutI+@&)dy~0sCgr#@a*tp{d&oI^&nf`Djqfj&
    zhxMWeW4w;WkymCV5bO~+g2f_SzWYR6PPsXeA}j^UKfM>>Z|70|;!0}HpGNkc*C=hv
    zP$8B&<_XMU@Rs0MIC(Gl@t(r1S8$VZ)=Q!RJ}vmcIJRz*Pbh*P>@k$w;bY_cw=Kik
    z(u7N2C$Z#+5})DaDJn}mqRS4YIqkDV6i)<PjjM*++^Q(^3DLe^K>qu=wkqzP8vv~6
    zz>66cfyRuj#*}*4y&H)Ba0czGI?#{2PWtM@>5&<GMy`7dj?@(^_gYa@r|tmZClQjJ
    zBYU077}TmPQGMWwznb&fo84JdhULEN6m!XZ!jor_f4QT7$$3ct7awSQW?m4kzy4^t
    zOSFw{I)R>8_;Fr?s!U@=pT|>&pGbH@6L*4ugRj+MIWG|Fagcz6$KwWRBB0$5#f?p(
    z(EpUzRpNGkSYE>xwo>)yg`}!d=qU|&S3Sv45&YPqejE4>8|klS;UBL#w%0G`I@VzK
    zp8Zof&*1p$_fcdvb_>G3IpT^1@D!O}eT1&5w~)?-1tL>Gk?jbOf9R9+E^WbH@>iYu
    z%E2b{$b3>u94U6aSeAQ4=r$Dslz(zhy3U*yw%+?H`d_Q8@s&%JMiw<?n2naS(fP>h
    zuK8$d2A$8N0*$RM#ZF(1&Kp#quzL+7I%zW5@BV|b^Y$xrDzXCTkx<653w)M%O*?Wu
    zCFuK4fmBzd!XduR2by%=n94sl)A_y{*JZlOcYP-svm2BTtJ9{~%=eC_XJVaurgyzc
    z8Vz&S;!UbWEp9_Uv6$3vUn9MuLj}?@{!&~Y#^VxTVDC-^u=&{ScwE)pKLiAPSmMWr
    z#qu|8!m2)lE5i6XaQq7^IYz9Hx#-bC^Ul|CM41;U^56$ud&AFOgRjhWskJ!*S`A#=
    z#AjIAjv7{h%}0e1+8gp2bl{hHc{9Ei@B)8HM^=rl#x7|qC<`U&y@LdW-q;H6e~sFT
    zG`UOPPv`DK2(<+hx|sLPHq<x%R+yrrGND#0yNw=SMO5NroBFgrui1Fhh7e-m@X_S|
    z^oA<=e^01Xf)|h^h1JNP*Wil4cJ%TRsW^?b>zne744l7O@z7fG7mg$w(|sqD4Nw5e
    z^KUyjYWv(Yhu7o{>h@PDxpf9*Iiu8Z_oRkl@_Cf+gRyQ83gKHVF4Y_+yyV$C6~Ly~
    z`I4r3M6~y_xQ@KPl{j)YHa#LLwn90HiT}8*7qn*)klfUY`3w(=r&1w8nI7cYRO0WQ
    z6)8vhEf28Wxs6b&O+H-L1J$MOaF%-5I@i8BE|iH6*rd%UfVESHlDmH{6*vD2HM)|n
    z;Z+btu%HE%jUY0lDApC^W~{;=Kb5E|4PfoBW>IqUaSkw@-bmnPHdu=z`z*uN%f~b8
    zQn*y>|GY!z@&sJv<{cDCTIG4ekAZWIrTW&DwA{EG#sIsN4A_5bqGy+o>dB*;deOg|
    zOS--c`?&+?n^0$?%2d{-JZxQQ&X%RH6#;f;BmQNNQJFIZqZEg@RxqQT3Sbi}qA+?2
    z1Vs89jz;-+!2teU*IVH;RJQd|v8IO=-FaONre{FtAoB)v5O~JCyqbV0tSe2?yP%~Y
    z{7MAv=`B!r^IQ4|UcHnhkG+BS@@mLfXh_4Q@5fFZ3P=2cir$cJpcHE|zn)FWX}gns
    zZ5<xg+TXlF$sR)qA9;iH<BJM?`s0bhv9<r4!!)osrs9rnp<O0Q=VJPkw8n|ocbQ}f
    zyocARij^7LI;|4*c=N#PwD$gar0;)Sm0WYU?U{ZdZMErS+t&e0!LSO<RZn7b*@7|&
    z#2dJS9XnQRhpz28KYapQHumV>W&IOJ3m(X4D4{=}p$mA~(;PImn*8Fn0t3$^FbcP~
    zO6#Z3Gs`Z0lwo}#_K#<?cF&(M&bWq`9XMd}2NZ{SZfxy~Qvcem&bFVrJDmrPptmYb
    z*whQJuED=&9yO~whzoVT2{fV<4Q19e_i7Joi~xlyF;W{GU;lCe{O%RhdlwwldN+!L
    zMt0O${1$HN(K=i=rDZt6R~<#3#$V*xN+rY$DGrP2uT4_$+mF+J?07=0v1o|$sFn<B
    zX*+sVJKn-}ycw(TpIJ((E3dTdPnl%5*5J4%3wJ7j_x@_f^$JK9f5d!-o9WMIm@DAr
    zCvoY_krd|-6y^}5J;Ki)NcT@)qGn7rQDjl8I_VHW{S~lkE3WTVU_440w4cS&f(auO
    z8f%A3xbssHnk@Y%1}Vwb+#_DjQHh_uDHuqZ#Qs%cX@9(f_g{<hJ%Hj2gpD<w8I`!N
    zm{Z{mh7D;2!z(fMC7AU+==KacYjoU%)H=Vw+Qes7h;n&Jp6`nH@}b|U0Nkp>aTmtW
    zKec!gZqVlb6jPdaiC1uX0<an`pD{|5K?AO&bdm_vo4Bd9GjJot!?$pa|1BVEUIaX@
    zu^);{D;^>L{~;0bb&0T~2}geF72xxyY?$q+lf(Zwt{^7+4xGh@CH`Mdv8;Kz0Lw8+
    z>&Q*Kzky<^c1uIxlswNk-v5JK#~*jF2eeZGyn}0`Sf3Gcz!u5>zX``zh=)m>EY|UO
    zHb3Gk2^zi~s~T5vH;Q5ti#YuFs8}qE%P2f9hq{Sk`|!Vsa{DCxCn*JD`Ag+@KE{S!
    ziFVZ51DAhqph(i%npF-ANqd;x8t9p~p0v<`qJ(D_Bm_TZ{zc+3T_a&&=0D}&d++e~
    z$K7Nu#kI;hOu(PzJNP=f#4Ya>H~9hi`;TxXuCL;<1iu!A@J<LwR^s2IW#8hd$Db1K
    zB3`S-ZT%R<0w;qbv+SR6aZ5$1)Z)lk*WlV@_q~JXp$W(A{IGac*{xIp8qi9yX3oWQ
    z^hET`Sk-TQnksluO0{2i468mok+x@+()<2VsFj_ZB+7j@gLh`y=>fbAs{%(ZITOdj
    zAE<;ZE)RR&J9HQy#Ie!-A#wT$5ullJ-7mkx&#V^5WL$vDAZ*OtJptFbXTO9OlJrXY
    za4EnCaUFX*|BcS&Q8-Ytj;$kUrJPdJEr0x^$u{?)&VNJ&8!LJ`dvDD3#}bbH0jXEM
    zMD7cJpyHn^sS@RSE3WCudwOnwzk`*{7sb*HPq?NbD65o>ACnfZU^Ff_y(8nWw}a2H
    zy;mEqVs3NmF?sEQ>w|cJy!Q?9XqLQ#uKwoMGvXEgOfZxZ`uCX3Z$be?C~LcN;2N54
    zyyCIB4;s?giZr~m40Yl}GXGqqa#&l3OOMWAXGfs7W!2!&^iyyxtMXQR*0)jAkV2)E
    zr8R`ENP)GXD2qdINc87+;C{TD`3xId^W^o*ciDCSPCT(oB<v~`0gubfW3EP$rRl|<
    zOnJ(cer{@+2z(8b>oE&j(Fg8QsQzn}KW`8t(Dhzb0D1>}$2b9~P7#ZrwMs>`Ix_F8
    zM(<XJ8dHkeuM)LirH!jxVpiY<xP1JMT6fZ){F28_<v|YCz3f~&ay^d&E$<iMFBMO$
    zN)$mE#fs9j{JR2lZEXeWwe_1m-^)5szU!T+y7Dl*98f`AaHepbu1|e0Z*ad2nhUqj
    zfqQ2cx!~Kn#>+#T>|A}aaZD8gjah70<k~l7jT<TM6$)7k<eTrw1AnL1;&<6I^~8I+
    zMnadr;DF8dzl+c5p17X<LvTRlA~{DDq7X_c&J$E+UQ{}|5*~lEFhpgLfK;i<6m<5X
    zR(GM(3Kq9hf{ljbK-N<6ko)*gS##m}xnSqGfr{I@>IA5Y$1yeG(B?jc>KhW!J1j+C
    zrSJZY))|zFy98&&1IWw!|BNH)?40es>9wC=6mjE_*hP-<mjB&BUf<ij+HskI<T^+0
    z=_j~}N?#R>BL`|>Z3(TQI_1615>Bityp&#j-BHYY{4>0UmumjU1<d};zATg9?~pV2
    zbKdz(*^XOt;H9}>=R(&!3ViXmvQiQ=$8DsWaSX1%0e!UreMi)iIlPw4AvI(tmyz>T
    zq4Lgd=JiKh-hSumzb>NKFtnXwI&dW}rIkx4fp-)CqgJka;0X1TD2@(~*Kcqs+-h7o
    z@j_7wu~2u)tHaZ@&-)^?j~Pef;k(lK<oV32O3{e}ox5?9q)PsyPS9MKBMQLIDK>_9
    zE9;ho@b?O+@+1y{&Z2at!Y4B<MJSCKk}o4(K>Q%O<kgq3Y*`$KeS0h=q0503TXz4u
    zt!f<8FrLfs`NZC|;CK|t*Ah_#u@boaU34r*;MjnxaBRkS{ZP(RyiUetZj!Sd(>P++
    zf8o-dvtsM+YkEdh(ybs@ohGB)8G9X9kFei=qzRb|uOt+}>2L94zC1wqdt<jBr7|eh
    zmvHTquO1w|btSO8DqzLIHLTpTf|a!?RwNGd8hMCTK)bg~==YI_{{oIi+bW9ZAsh+l
    z-^InhlOo@LA+8rgJ1)D@fz~UhRkLz*Nny`c;IbL#;)p|`?Ef+Evg7$7vEJw4`cSsv
    zh-0O=^zuuB5o~G&xEYLx%IBSmJLhp9$p7p$2Tq?0i|4|;*NQ_dw`QFzhbss4qEug?
    zoDxvbTjh}*;O0jOceP-#zC4!IZyd&&g@+ekf8C$PY1K|S$ZMT^#x>h`+m$%VZ*St7
    zV*DXWxYVC~hb{hH6hYKv8Z6$+Oj@)2d%LlE!C|bqZen3iSK+t-;5ZFy<@Z;}y}w)5
    z`8fW<St7)b;tI4rEZ*V8JEOp@U;O}~z2SkqV1j{zEPUqGt<#G8;816cQjxD<uX3y2
    zSB;)hj@F8x+Z6cMMy&X9vH1OpW3nM;-GCNP6UtFPn~I;f;EaR_*KLEVJ_g68yNn`t
    zezkZRYXmrI7XiFe;?5nt%5gk|vjmWeI~HXV3IPS}-`@@Yv|;GT!rhGp)||m1=!c3T
    z$jZKr##J=FTUN}`xLeN6N5yLYNy4G_o#MQW3LqiLJr>T_t_*fdR$3lR8IVgW%f40U
    zngH4(^vVpW$l@E<lA%)sYaNabd??b{cYG=8qz3fMUGSk9<S(2@_~?4fx-4W2wB|OT
    zW%l4wHJ^BkRy8g^wGw_Lr|!gmc8a^1!tnqO+JVnUiPb(#1UW~4o<dH3Co`p-+_6K@
    zo`UNZ5-sXej4VNUZgAMCqMXa*nYW8TeO27)cf;Dr(b!Q6K)ivu7N*a2mS=P8YFw^1
    z?<?555|vi4JcB>4o1n)Hc+82AwNq@gxJY8S20)!M6#e6=`1kFDKmAZrCpMtJvxMja
    z(^dY!7s#FPGWh|mU$Wvo9KVJ4TZf||2LhbMU{4F4AB`wMRRslvT;V9epTlwHJ8*o2
    z`*C^V9kGtZ@&4ljPnm}*H1}|O2heeJy_=+V^D?3(8SLW+*x<<n2(F%hFUo8UZouf(
    zvj5$pxUYT}pWnqhMR+?Y08s)r03X{-C{T^7Q@1E(b}L7vHEK~G!HPaSDaY&t)}d4`
    z(bBpOM-qAw7==>Yyn^VLt1-qxxd$H|hW_(@1S8Xy%*IPHvN*2$rq)q7GR$mII<s({
    zX8^iAMepUKLi^N#1V5aNkLSnZ$rt00<<HAK^h&sK3p*y2_Gt;J3}AMzKp#+rN~;3D
    zJ?=o9%^0|8DbWiZ*xUES-?hYPj7QcMLV*_XnC=lp+)Z)%&mKEP;XiY2wiY0S%Y3-q
    z!U?L_@+xVF<NESkg=cN8k9wmAzd09orl$?1dOR#g)>>7cM$PTS%<t7Iz?69kGRpTc
    zQDiLKu*_$ATNr+SGJbUcbt$-LG~PEB7I1=BaSa=e#j)EI#bzA?*Y3DVx5Fs56}J=z
    zK3KHw{PAStpD@H(&_Ayua;+w@5}w>ekr~-4Siva*cy8}Pkv-%1cXSD=A%M{0+#sbi
    z)+)gR_|+<+>HzhJeF;i6wB+FOHwou-7ebjmaaoSlqI@G!0+l$x_nijOJRH&McwE!e
    z*vt75+dd6?=X$FOU|(4F8a#am{9408<1GHe6ua1-0m`D39am!1vc7)8-;DNhXAFz<
    zC1dc03n{ZHhj;obCjXngNbOPruXl&`j#a2Xw|E*8MNhXWKJ>E9e|QM`_#x<jwiOi4
    z8eIK*p8!HJ{?ND)Q1Y+h^2cMZVqr+hf$KTA(nKdT5JVQ9YQa9ztSnl4SY<KM`*3MN
    zod7S}<bdA9_AL|%#JfUa&2X7cye^BiC{^QG)gBhWF${3>P;_koKU+a`+e)Hds}zcO
    ze7M1-0!b+FE{f&Z-+BiHZK~|&(Q+nZMXf|JJ%$@ew)|ZzXEC?rVA=Cp9BcCz@1%m;
    zA~*IJ*!^I5{YJ=!MO6GA97XpbAS(BXMyBu=_TpQ2z?yeSll|#b{Cz6>f6sHR*ku{)
    zIV1XqAhSEM-(5`fiW}`@Kq)-W!&-}pA~^uZ2$=OKt~|Fv;`G}SA2nPAcRX?4yErSZ
    z*!ElL_1~LhqEm;Vm-J#kIfw9=I`l_}qUUsBez1&4g#YiN^sbc$7vl)m;#zfWk2M-c
    zbSYJe?4i|e0?v$1?BsG(TONO922ZSbD?o*ZN_)`arsCQX1!s=H+ph{WrwjX=)kImR
    z0#`|BbFSP^C9aw1x8H#?_%U3iVyWOPv7$Dz_wmrqHMo@Dtaq94AA}oXa|r!AF~w_@
    zDBoYP<GV0#aUA;$#a3Av(P$G39)wc4T~e0yIZ}~B`kN<}k-2vtf|_8%5BuC}gcpy&
    z+qY`N@6Q>y`c0xonzRgTDEv++R=Zi^{?Q_ctJ!dN-p_EUFAFLgXiKFBt)T*IwRiwo
    zf|B<8re;i9!v*sQzcms6ga%Y-VPytRnn^Ba@o{wsPmA*T6~%#>@pPZ<F*$Y`jsfR^
    z${kW|)ryQW;}gqJ^Lq%y)9MhcVmhvD7L-z>)0SWDi60Noh%B5lm+<8d3@#&Z1+E46
    z8927xdSEY#a~*fYI+x<eww6ucR6dU6(=1`xl6QgNdvXT9M{%TjuPC^<Jy{9GuG3fI
    z*o`~10m+sWhD@Y5O*ih9HbdN*$-s}4wSITx+-RVENSe%V_8wgLXLMkn?eO&z&6uu1
    z`n#kwTssLbtqQ{(LlMCD;kxoP;A#PCWs|==VAJ9l9Ux43mi;SHdz7O**Bn9@z{4<`
    zyLkoCS#t<4+ZFHFdQ@bgC5t_K4tX~c93w&!;JhCtU|NK0$ho~@=c=Oevvyt&c6Amz
    zLPCLDe_L_AT*n%eQd7&VdSDtsEc^iXuOWK5v+%f26G7^fy=%o~Hh#1N&)zBG{+0mk
    zD5VC>JoMyxek9hvN%s2=@c{qbh5tLaG<vldtTl3eVv2jGDBTV@li$Uazw_^=06rpr
    z#(mlQT!MhF<GS5`)xw{;LKM(l8_;$C-UJPUloGY);hHxIiK6KCRmvRp68SS<CH(0M
    z%$q&Md9IZn>{$#OW&+QN_K$(UGO@znVZ)&sqY9v%Qf6uyD$vkvAVO<U@jd6U*1p)T
    z`9KZo)M032;g9Pvr@TVAHmi`3!Qk3!{Y3!5n0*%$PtMthYqzZ{n}~zy46$hiWg1py
    zuw6y!R3!US3Y3j2vHW^Z{8;%a3AwIc9A^VmJVNX)ip^H~<Y1mH7|V_jkQ?MWI#Jpl
    zTy52bqpU8`{k+p9q5#g5>ubTynEVynR;;!?R=s#qr8r*7<>I;If%T{joLz%UFL%jV
    z3PfRi=-&ziq{tbM2PEz$S}pDyUn#C*rp%-L?!5`dln!#54SaDv;nJ`qmDZV@2e!w0
    z-po#8W_D?N^Kxq@ml+PPMeS3GTGm&v>UHAo?~Q9}sm1!gBC*rJRT%?GDP?ViOpjgB
    zhb_|vV^JBC41cWcHK-TcFgGnFnxFAKOlVO`SNN8`2qLQ$*6b#qAA1o!vJc}$XnyfF
    zKbtqjO2mk`&03{qc3InJT#TRi-R}ckC|&pEU5%<vcwRF)v0r?hyav8H*67J)aAzat
    zA8iG&Ej9%0a(I6#;l!ftc<#;@@6n)C&jllmx_UgG0hae+WztWG%k=z3_BoI<=!-{Y
    z1iUowpwOqplQ|#9DXAAvB?g&|f@Z4}$Jbkx*62sp7t*pBT(g@x!LKIB*}V%_h_~RK
    zRsg#uV$rxm4>-ov9dbw{3&2>kUjLm*M(<X>VO-xE!G-e(vl0A$uZ)>gM&K#v4kH%z
    zp<il?%!BK&3wpI$&|?jr=V4+RK9eX`d1=`&jbpJ5s^({G001BWNkl<Z)?a`3c=LVb
    z{?ZXbcZl7;8a=b4@O#d>TC1OLBYM8WQ<%u2qNqNFx@w%!A8K&Sy2qM~xncpv7Mn#r
    zD+1ppvFR6aOtswG_-wkx;?>LjMqqpUBFd7&FMX}cfHIWgw`(y+)uKy$=ndi0`Gk3^
    z)OUBY-s!{85pX~a`p_52^*Yg6f+IJ5S-{Z-5xT?Kew}fTk$dW{@vXUZEUCD#VNq|t
    zaPDMW%64c13+j~jo{1}Ny5=1c{v&av)q5l?y2a6wV?U<7Dj@&;N!CB60o{^=N1Buo
    z&oVFPXQ_BJSIQY5hD%)^$6K2pu}vXB+;ZtUaZdp;GtSJelYb|hFxH6h$7uScDAj$z
    z2os$=bi<IM&%kG2Bb?uZy?Y=0Ju6UY4Zeb!01l`^U9v0Q-wq@=vR0IN6oyzEiN=p-
    z=qdT`GEqR!Y$kTLnF!k(!RtNP-73&1R7k&$Lr+WGm33JLr6!kI_3$);lZT?y8UiIB
    zrnF8x<mYjt#rxzM_okR)d%ISiBGXMv2=tQtiB|n7%UZ$hQzJdO9QCnR$gj-6WeW&f
    za*7igtU6=36Mk^_RFf*~!yN*eekTzMOv0t!M!b#pQz3}|UlSNj=7b@5rE+R!c3>B0
    zJRK+)S6WEDj+Zldg?J|!iVZuyhU0kb==D*_LhT_+KiR~_6hbqldTyOcaAZB&01vLi
    z;LZ`^#hxhu_B4tqW{YENequ-5$UuR;MKsShfUgKxl&08t5pnlVpxBII%ES`e{r|>o
    z*f}L<;7hL)&g|6mh454}Cfo?d87Mfa9*yV4%^kmrWAMc-nabrL%*Az#-PkHLaWG1$
    zS>0F-w%UWxiEpw+_APQCUqflVPo-7&?TtSy(L|~-gZ=L%M3Rv(I9A|iDUx*BBo4n(
    z+}gKuO;0?h7?A7)Xb1|@uK-SLKp$C$`ucdhN#&^HW|I4RE2cKETssjzpV;?@)?pem
    zDw%DFN0lo91T*sgpU3ejwpH-IflFyVAlBbKBhQaMJQQukP<(SC(I<vQ_WAdbdg4I*
    zQ-`7j7;G1;=LZt*#Q)F7bp#4_pzt3eZh9GU#b`0UkDHUCp!);0RvlFBK&M$9*dHz<
    z#6%HAWAY6#LLZ_yvmo|S|3|W1J5&LraJ1g%aqW;kOObBMfSB_s;BKwReRnd-AK>_}
    z%D^XPlmC00r%+1eE%qyKdiJn?k^k{hqE&;;UI`6gjFI^BG08y0O&{xU4I7SP)8w0$
    zgnMrzgN-b7L@=idV~R+#6+mecN=+D<HuhJ0;+G|YqUIc&HJ7k6uL=?<|GkbP{WuaO
    zP%B|T`fYqZ56S0ian1J!#(+|hO~mg?>%(f%p=g6^C*oK6?n2KP?#My$dZ86_-9kdJ
    z7VUY;S>>m2e1_P=X_CV@4mTg+!nX)?ju)@xza>=4CfwQ(WAbI%UGoo?5=|~geQy$e
    zT>w4qdD(>WKP>@{#tg`I93kw&9T@Zfn7A{sQc;DE8lCPJktx7f?e(3|;GW5v99%M=
    z@Q7OU@1_wvet_}!sVE41AO`JnxwkHP<|Ehv)tUpX_F=No-@;LD$0o7>14m$&D@E<m
    zwd5`s-5=U~cpc`B)kIfK!2kGgt0tCNd<|<e)^u9mpV48>ooiG2iKZybSX6}v%QNuZ
    zr9|CfVLEGx#HG~|11=Wpv2F_<()pseB0w3?>vPJ6PH1ADf1h|TXQNd4Nkh;lH8@u-
    zwn%5ZLVjgdt3zt7`t3A=b4TE<%VHPy=@^8IbJSV+bF2vJW4t~2+-7+m!(?mI;_VZG
    zirkdp5Jlg~L(mfie<^iORsFL&<y|#4QbQzlIKLbFnYo0a()#dPtNv%Q_38td*`=xn
    zF$R6||0-|<oVXFkn`{|ZpjP%_uY{$H1;lsPt$Ie6S+y>M{mF97J=5@0+I{IQD>09^
    zr#vBmX&g)NL<u8eVav($J_?tbUb>y-WmiepX&_(aWA~{*Rr{zlS*$6xCePxfG9Pb<
    zY>9?X&mla0INtTU;SEipYJK=;2X<kfLU5M<5>Pu34<q(ur{Fjn?K?^VRO9H0O=5j6
    z5?mljVn{4ieq=ql!)o2(S=NjF!s|pYw7GbHcp5c4joP;g_2HrDPwj#?xztz%tn7=-
    z`n*=pw<~+kTFry&wC&DYlK>|V5SPD3He*Z2G<)Kvr$hmC;yUQmCw?AVkUY3!xg>)f
    zTk6`BYv7x26291>*tgoM+xNh)^r75`_tl{9Y_yT2EN94fO_2j%f#XM%h-<g4O2En<
    zd?*oDs)^<3Q6=cLS*dSOs^^3C=*bBHc0?NPSQ+I%RF5~d6tymcJ$o);R|F>xvFhgC
    z-Sau92K~FWnBJ&g`mIgwp)Y~M)QJc38L?8+H*@Wulk}ZIsW8w`>!bRtiiIQu+x=1-
    z=IK_<XGY?Eunw&i{9qZ;|23vGVJ?sMJiJtpNVUZznpxsi<mB9b6jz(kv>g?|fr9l|
    zAir-VT5ITvU`<Xdu%297FDB|)p27ZR4d&2l)PGIH6P^q|Sw?i<S{<jE%W=(FUKdQ^
    z7&)8eB70gC>2?nRz9AOo6#=-uB?tOR+4Mo;ENlPTgk9K!y?qtYNiUOsxl?oANOUj=
    z$FL@Vj|@Zq?^OI34<WdGtkG$Vqxm{bZwImF6%yyYx`p?&HU`S%pf$<{uLV?@H{d&z
    zQg`l+|Ij`J*DWIa?oy&3EhBn(y|SLNe18&NTK7+9s-mF$QTFQ^`FH%iG0X1l7`$??
    z#Dz|ADWys^lxVD0vPTxxHy}tGnMRE%LEX5NaA`00%sGTjIqbd_sP9k4m-k{UROt=!
    z(}#&mDwCUN#Zgw9<c!=b_qeHbVyv`0tUaI#y}Ki0B^8Y;Wwcs<bQpR~7C!YV;jOED
    zMU)Rc;EZ7rH}96USC5NSePBghvtClKXN#~jiK6_o3z4?lia}9Z>N$Ri1{U{XhX%%0
    zdIbfX2Y9VVQL5pJ@%WK-spv1vBfNFBCsg;s`ktJnYZ5?WJgV&z3FEb06~G&UvK|J^
    z5hakv@w7F?+Vg&gr7?>=@n!NiE+NWUz*_rg6Q*-BBOR+e`21+}`J>Q6PWh8q{Tm0k
    zk#A5e-Q803{7;Gd8z~!Dsle6;g%jujOslZ!;40L(Qn-6>f_ob=|F=q8wANRSLyr+Q
    zUCapj<|4veEPBPRH2Wpu(^v_771wZNTOZzPTpfbDW@~NMfUmHnIwlI)Ai<j`Nb!;N
    z=taHQQ)ZEG%wmV7Q8({Fki=`AX~8V+|K1+ix91WbpTgB)bciAi#T7nc6X&YVy;IvM
    z<<21V8HkEbicf-z@zwc+$Il}FuMTA~VOZf?{;(gx^^@?9Ye1bd0zIzCY^;^gXu4R=
    zNR+}TNnsA#PVzFTfG^8UHfCK$Yqv5~R0PUBxh>lhL0cYs-D0BSW|DuV6@xVf?RhDU
    z!cWJXfxQ#*7}t1&axY)l5enb|aXY?_D{_g~3bFpFK>)6BFt6aHcFgg!$ltmGGrJSp
    zk>AV(IIaGO2+x{?!bwROvbFj3-^sPb#U%Ry6;^xRnk>aILn$?>4CTpV8(#`nj&~2Q
    zTt()iL-CS@_tz{Se74<zn&K7gFM{%&1U}Ly7BkLc|4XdZwhUYOwaB!OwYDXPRX~+r
    zxR;VwI^Cg6#tui<Ie;){-JHCiO~oIQ;Lk-CZd$g%z|Bg@jQlb2yl0DRdX8Mjdijd)
    zGI-y71C%<=X-uW>Bx79`8%PbaJR2=wtvA01)sx3!txajv9ed*M?tn5pU`9J;W|yzU
    zZT4{V=f6m{B0h`jaYVI8w-bV19dB=6gk6(^MZK6wPWT;&ok-0BsiqwE_?OAww???_
    z+Vc*sw(8!!B0b{}g1=77a$-Xv+u;eoeg(yTnO4HgiXD*!cttd8Os^e<qiimehdS^g
    zHabVWDT}>nxi+78Ev*iIk=zl}$$xo1(Qg_tFSI%8@6*kg+gD+P1bmVlvP%<M{w@))
    ztzG{T%MhO*i&BgVA~Ufp5nfn#n&Q~Z0-qd?9+@_FLK!MX&jaNE%HKD8N|`A7`k06p
    z4<Wc^Y60{atX0`sQ3exm?b6=F5pE{Dh5LC)?neP$YaSa~s897%0Cu1ka9}lRSjt$G
    zQkRd%-=iX7eihujoM>s^2G?$#tE+}5?yFUl@Jo`Ri8CLECUW`%ahR-?_j|ZL25WNI
    z-TR*)A4jS>21oe@FBROo5B{`DRQ%;#A@=JFiNM+tjj9MxXwM&zc8#CII#CKw3jn!Y
    z<i!GS_9|QHL74|#A>?g85?m8*!05Wz8$f&B1tTKv*&BbqD#wQzR|>;QP!#J<R!Z3S
    z2Z~*_N8&gS+g$;CSlpcZ1vpzQmibTteo6;;0Rtj^NwLMLHfs!FHWx`g{jnyZZ!gy7
    zGq3xcG)qUHf5Wp`Q}D1)6H7M^N9GB|8+j`$k7MfDq=g!qMpgU7oke}XYtHu<08hck
    zhN4$zv1_vq)wyLk(H*OlQ6`FRnu7n?(RgJVj&DHkRncz`HIBmBDA(U49!<9>v@--a
    zJo_!&&rS08z@pT;9Mt$2nX8btO8I|U-#=aJDLA4IePESU7mO$pfdCeCJKSclFRM&p
    zFFe5Zet0rjd8UWsXt~Mu*%`UV^9K2d2yjdT?dkRqR%W5jM^$>*K=<p*FdIiY`zK22
    zbB5<kQuq~c<5JA(e9GUqGO{xcCHVV61V7t7(ka#N`>qviBIyY{EveqOYc1?2H`dzj
    zNU1k^u=9Gbwx}=z9KH8J9MQ@LxORM`zBUdo=E6vwhPiW9foEbR1U^EtNl@(HcqRSk
    zB5W50@C!*pye4kQfw&UI)&%I&AObp^0Zg$ICfaPrl{8;^knLq-rN-76$MiGC0e?2h
    zXfv{TRl-A9vkmulj{sNVW;(zt=odSaUX$I>{2f+@ZZRqUre#FmUrKb-a%HR+_+J}`
    z|KXuULF$d5E9$2}lz?Z)%b|{iw@s8n3|8HaV}WhWx<fo01=e5D=US%?iM;@8%O6^Y
    ziERISIiL!4Y`t?)lcw^&mk>pR<R~W95xsT^tv4J^&!{SrS(#snWhza4d@rsm(_n=;
    z?XC|=WM1#atjJ*N0;iM$)lX2)l+fZMXwN&M))9l^^=cRP{^k<DO6S5`r{I-nhaVl&
    zfI4|d0Y7RK<MBU&yTnSMT6he<5>7$|+t$i$$$(6fve;;S4y^5W4vZ+EaxrMp*C*hg
    zwM+5d6!3B<_LBKTCT0g}&yy7RJvgdytlV0~@ZQ1ZXCG{n0;s}q-LIE8dQReJ_lrg=
    zk$5&l2mMkm&nzd^nL*ujEZvWsL3?@dmeODqa<H~_=tr9XnG)|qOWq;pwL#%vmo4wb
    z^b9~U_NYLO@pIwto0R><YGu$`pD{FNJ~zq{JK_g;-x{K288uLU#KltS#2(L=xVl#k
    zrjO%$Zw=MfiW`^%{5;BA(1YD2O(a)Pz9y&DKic~5`G_>E&i2<(e_W6GSEsjuASmEx
    zr_lAi=O`QfBkJ#bg_8G;vbmbl!n}lN5KCXX5#;?9QM_W!wOW&ddEM9^qfEjB7%o`C
    z7>tSTY$Vdk3F-fD@4Un8s;a;L*{9rlr<e3hLV7|8NDCbW6pW%^3`GS55f!n4pr}8i
    zs4t4xP(T3@m5vla2;ER5q9Gv(=^>dkCdp(nedf+B=j{FdvDcY<W|ELenE2y;o%KBT
    z;Ypa>Ic4otzU#Zz@)Z1PJt4*d#zRwu!)pv|E?`TeC92vl_Du7VhILAmvRI<CzhGC6
    zPh!vaB(9F(UXB2xb15{+#n1QIz+45NEO#+y#~g>kK8K6mAfMHW)2cPkO(XH=gF<Ig
    zF)Eq?88bE+iNME+V@&KT129j*rd`QIlrp%<%;+*2emy<krc~jj96XHt#g9{_4SZxW
    zxtA`Yr84!lMj!zGNnxayBNlLuL?Q1~{v>(0hcRr++Hj-V>sI7c#`O}fw)PC29R(jA
    z5<MHGjxG<)_b1@SwZA@R$tnU$#V!9Yv3&~TVoGtL06F)_p=l5!v8Q?6Ke|%^W4y+^
    zqA7<SmLRnEs1uLXcN+wIGLon{O*UFk!0JxS_tp@VU3W;}zA~G#Tjx{sjpInyT}9*E
    zi)mSZIgOL5yPw$>3LJ+(^m%8SlU|KQ4`8%u3s8-DkUozn0<gR$rTE|=&HJixE_j3d
    zliSeO*Al+c;*ivt?0F*!b##E28qxo15gMWEc_`;vEQ9p~dX9E)lssHv31u^-+O<TS
    z+Mev0+$f*uV1`opX$Gk9dURK6pvowv+qC2SY&~HF5@t1gWd%XbDD}~y#<}4DywNE*
    zsuK0f{qZPpi`R;=T1jDA(#Nnn5C`oG1MqeV=>Z2HHq><9Gb7vZ<`v|Q-;cm|>~}Z{
    zp1*+B3DtyGJ!iqf!-f-F^FBIJjyti6FyY4V0s8>|Bq87((Sek|e|9I{W^ZJrU>E?2
    ztbyh{lzA9I&>e!ny=^05Zz}V-BXC^|Rh~A#h}F%z<NQ?wt@)iAH-mDMsQAC=&gT|T
    zU=DUlg=0?_oqi9?&2Rv}whL3{5r{u8eYO$Z5L>F^DA<^Fo$s$Dym15Jxr+%p!`|1e
    z)R+pwiX>)v1MVL_M%%0tNtb)JbE3?{Tzq&pGw=rSsL#gAggcumJV$V(h#XTd4knM|
    z;1v2xQq~d}^Mgsa7mmdF$z;46))W42v!^h|oH_`%BF3oLnuRM?6Jm^cZ>3SU?;mi%
    zNDYqX?nG}Q`f;N;nk^J|K%61b+12!I3vj1I5YgEgE2ZlZqS(3zHl~z1rqZbAkJN5c
    z4l`qs<z!sGlAtc<I-^pCA56e?V#Jan7v&TtVMSnIwEJ;8g-On9C6yf0p9bLVBmtyy
    z8ujveijKXF#F4joMY&LhQW~gh#kp}h9h+KlPQ8c92bYuFnns}<=jW%=(fm!eU-y2~
    zA<C+;)Y6d@mTmYk)Vn{Zv_^o$Cb?I4U6)nrnpF48Dtn^?v$CV-?;D+h14~ffpMV=L
    zohjh%E$DfzJ9~6W9X5>oL+6A|b1u@2a-T*H%`8#0=+JbC@=xExd&*0=y9tbIz^m;-
    z4@(dL#UU!WpsjE(6+S9O7(TdF>6<nwosJ)V<v2%;BLCvWrfJPrY5L9Sw3j9@=Nwch
    zyM_w>xfy@<n<TN(`~fk3LPXx$*=DAUL_Cr6Xp{F)ht>>D7Sb?)N*`6}+g~s;34b{Z
    ze_}ES@e=-F<+h3tDeSdt3EM)~nOtJHbt>M8gK)k(9`~s7K*t<RR9Zd>OQ-c>X-m}g
    zSTB0`6M08{!3XZ5u#m--6i9i>n6O{~qH!$h%cF5drBF8<fLD`*4=qr^oF-))9fm)d
    zj2rJg(FT56OP~uKgC`1RR4varFVUlx2=cQMDPOv`*v6h100rn}4HVrvpNfTBQRpCA
    zH0c1`{RT-#!7U%5WAPT;PdwsO-m{pZ6KjIbPd`TaYn$=DaV+Vl&+o2po*-e{hj)TS
    z^w(^(4cK9q2JkzzQfs;})jnFlz?4xc`RfgYZVxsC98!)Nkt`t609&%~^FntZ@x%0i
    z2`2m8CA8)P)U4+z4VCA~1ft2<w&UTw4zk|MQq@-RUQK`>8WODmrpU!$Ox5jg6`oy@
    z1EW*6F)E&}3@<_7_i^1a_cOGdSVN!{+_i{QI*+<+HYL};O7Ul(pls?*RG)Z<Q~k;Y
    zd;x4Plv?$<6sGs?YIw-+C=iRri@_g`a_9&N#q*^Ar!9|()zPW+;rCPVogkZQlOAqI
    z!Gl}TPd6tVM~C5!2jF>1LLmqCOIl|W)9tJr^0_e&-y!;TriAXT5*=Jd-xlJoz&20C
    zOU!CY3C`5gZcd2<s03P%Eh<FF*H6K#N!eF$WF^j#6+O@2cUBQR*A(@cD6l;aFG;H6
    z#tiHB*!d{McC}v1UUWt7i2>-8kj{yHq7bVH;Kq3rw`Nd-iqO9}jSh^~51viyix<(l
    z@C!80dXbVF=TUUTXoAt@-ODZA7_eeuR=Pjsw^(+d!WeH$PO&|Q8CxvB$C%PN&6qe?
    z76MaCQU7j<5o`>6Yc)ZaRy%Y4&Znl5nfe>5&wGUOnsUNBX9P_@nQq#K6!!=XBNAD3
    zc=l%Va8kThsc;Og?+kf7qX_|bNvEos*J7Wg2F4fn@(zyYKX5K>=NwGdapA>{_#eKT
    z%4gSkC60pomr(S*Ig~uSoMcM|HMT+r70Dg|*SUglN8$aVU0i1!a0>-0`DlCX##DJ2
    zUv*FZEzY{h#QOA(QP@@E@c!0d!Q15>nD4A6z=ZlsWAG-#?yEh3#~XTJ8zFXQ#ZzLi
    zqAtQ!a)=j5i0zARclI$AFxa-_ApWzyvh2)+(_MP@b2v_{{hV?SzCQsEH{qX9QJ75!
    zFl#m8qm6bN_Jt#j^V~G!&zguUxqh;jL(GU_>x!M@Z;N3%X-}eoJq;UvB;~*^2}L3z
    zQ1|TpFHNKCH`D3R!0~rbb>v9$H%_Mmpgo5=^=>K_Y{u6(4&A*!0t6MBf6k=;mu^&v
    zH9Z&z9LOI~ic^=tZwYXKx=+`@|BScnKLh-;34P^i!l#bFHwOOaO@bF%T-6gmQwrDj
    zF=2?&W^9GYKXx9i2M-}M0+Idk4U(7q&8foZ^%4nuN$zo&7}djKNb>I9SG6eg#gg94
    zl_(1FVD|!QVlgeZ9Yk()3U&EP!r2Wyjq30c^tdX*bRIRl41MxAawm=^KdJ&<>_f`M
    zBwdV_sY>hHaE`o{>efsTov9ETGvsZIb|toPXKbV|<sRyYGE`j_cXe9k<~eS|)nh^~
    z8Qs$<1qM!;N50a7+B7DM;~ZY5%{}iaP?{BR=VtT|*LilBOmA_MNpz?&VIWb=RZ`Ns
    zUaA36XXh|6$ZPxHIOj;cK8I3ze9BOhLM`vaZ;YljJ6`7#hX!h5tVv4)r@cmgS{dqx
    zlkt4VwnE=tXD0>ycrxBO!)>+6S8&Gb1j{;lfYhC$D?_8GR?baP44C}AoYVIHi~zun
    zK#Gvx^4Q-ui865We5YhXE3N~4^ElGCFQ907gU!Fn5|{^O(k>KH11uBX!Flq%(Ra7e
    z|I!ScC6?3E;4SY!*QB}uw8}@eqGetn3MlhYEh_0xUqJq(*T}!nq)g%PD+NlOHqm77
    zIy1NZyC;Wjpw!)G(>Ar5(1e97@qnR(g6yu8&rPBWc~QEteY}S$a@a2tK<iL3YE|<8
    z9}=(Iu_jH)>91o>dX0SV!w;~v6KCNTyj#zp{mJub{l+nL9aBRvsuW$FL>GG)SJ}FK
    zIzYWxOQIv!<M5Y>4*YmG5nBtE5obN4QYxw`4oaXCN|acKse8BV_&uJ2YbM~X$Rxb1
    zvB283lWgeUeIhj6w2@HlaQKy~DMQcxM0VTXKNoC^aZYZ)(uF7XiaK>4F&QoCjTZdU
    zGzhX&RiI}Vp=_@}TzmzmRO9@$(N%LA(f{)%!R=c-h2y&4o`84GutKRht9Lk{iULvS
    zrC2K2B=5am{(PxaVMh120jLL_6HAk%FnuQ{Q9&_A>n}cOs{m*2PxgQz1fO`A@<3bV
    z8B;-6p6U*hZWpr7d<whjh?QFUTUKKy9&G`91Mb>1dcPET1tlHEDSn}`U~R{yP;M@p
    zYt3VF8q=*n%5hE`Z?bbgYno<%iq`X{k{z7DU@+S{QQNw37H`MBc_FFy|B;IK{(&m&
    zki%23Y|FDLP**b)dIxs*uD&kO)=G)Mip0vd$HLQv@=P~&g}8MYO6BKvi1xA}>Xz3?
    zy-<t4wgu;vI{XKhll<j8ioW_RrDr`v`Lx@p8u4=meQvf}6&8l64iW?KfTUNulP9I(
    z<6{i%p`tScS7AmMbszeQO?l6o*Rtb~&-V|;d0#~?L^;lk!J(PdYwqOzTL_y1*Xa!!
    zO|CM*>zC1V+9Yz%t+2K8v6X0WG|I{MdbSZ}&{t6Q2}vas;I{{u&Je17v^W$W9Xj*d
    zdZsm@ftkZ`ln%qMtyJN|+Z@nZe`a`KE*gmww_vjy&`Ua<-Uc8=v~{az8vdf-8QVms
    z4iezGUsHf6%)E!fp89$Vgm_71f>8z<L76*enAY>AlKpob{&9Cu8Knlb&A5|pc86fX
    zP^{E9vCT4H7DcUR->$57u(ad5CE~~xyOb)0*BPJ(Cu#iqk?5q9m`|Ngeq)?3uUz+z
    zkA=+_98A_R(4|rLEg|`p*_38N<>0s;It*}J4`VbY3}ugBoY)c3K8H%Gb8a6%PbKBQ
    zsq&JMTK4aRAAo${!}M%CiWX3m$o+;pQvhSR_1J4Rrq|TrXf9)kp!V(JMgI?dA6!`_
    z#%UDF$q(^02b7@l2K7d}-5{+Lt-}(uJwFXU*~>N6rZLm!t6<Ji#v2tQ1a)aR<=^BR
    z^F1#Z<+^h&3>zm`6Mp$AN{f8>;mLH4ts-3AfcyE!DVw{Igp}CclG^%6actwF+Wv0j
    zBT}YwfozqJnOcfk+JWDkw`o94lD222_3XfOfsZYe&|W#t8AG(XZi<bXBWcaU>93RD
    zp4&k$qH$<d6iTVzmNWSQEF;p0k<Lkqv;FOA`+F+S0<9=jX3IjVBI$-L=S~f}4j;)|
    zS3XTms7>Ql&ro*qSn@~LkUw=ixtr&!lGRPd-O!AO38TYsoP;tdx!AH;>1bccNKxL%
    zT9OXC^S|=#IW_3!4W{Ai<7~J#Jc;UI08AJ@yoA*9dc4LA>R)e>*sq-M8z+*UP;J8U
    zWMC35lqLgR<U-2FG-r%D=RPX7rK9P--OoNHIeT9#9t)*%k`pC37|VZODY@Z+@(g>2
    zt5H))HHBGrIfzv_bQtt98VL#=ph;1*SFVkc*#H0(3rR#lRE0$ITpy#B-YM8ufk!dg
    zZO9RfOrpv?%!Fcsr5*U%P_{Mabp3pt4rfiUW5OeuRDwF8%BaDGVWueS{q=T}^dZ}$
    zV>Ic6!R-r3J-nRMt!L7H!BjF4pK<?c!Y>^|dhRB-1QTwSQrc26phM+g-_^&+<{m7S
    zInf-bTwTVPnk1nZfRehr+xb*OsLvQ$paF-#C8Kbzc++#nC4%sp2~m5B7zl;Ba@9@`
    zzwk_)YLsKASYuQ#{YZ;mHHxl(Rk$Pl76AZ!S1GQ!`b0LIGJ(#iL&+8Us7IIKeDQH=
    zx&n%UrXxm>f9PylKlcRX&)4Ep=>BSzP&odMQvxO(M`7`3e~K$1_{~_#=q5P?QTP{H
    zLI)`v*Gx)<+eai(|7b?<>@D~%+<pdaXB|wo$boKdTO2EQZ5@62Hr(lVQn|IgJ7n|a
    zUSGyG+4QyQQ6>??aS|!*>8Pk8U{HCIuCE+J?w-Y@*0+#|NJYK=YsOa5*_DU-4iw4(
    zqaP9ju($j9lz7ZDfSj)gr&ZX|>u+W8)}?WUD75YIL$$MiX;1xpv>x4_$6PSV8i&>#
    zobnp^jySi$@x1C}ke^UZ_{J98nFo=*YX<F6_eVy<W2;D9@g(K-ow$-p-6IjGgKc)Y
    z3_B^gFEUgju*pE6P{DzvM%86WwB+rOn2E)7J#j?O5?wlg_s%1K*CB<r=Qz*gN41WY
    z`IvD?Lyamk!4=2Sb;Kz0pL&pr&22d1$R3oi|Kyk!4w9<JG<LB+VQ&pU3Gl|0K@2^r
    zhUQJJIE(9PYYvj$M<->oOY0fGt|^*us#GkTd<#3?%E%$Fj2(!fl2e|yk39d!rJkA=
    zkg=GabjmvU`${08AZ2WLn-N3N!w&ckTz(wsvkoFVx}2~+jZ@!_)6j)e+k&&D6}PSp
    zXGJ~Un&uv*rBVS*7t6h_+{61Af^FB1DhFHW`*Mr~P=S+ZS@FeO=bxA2UiY%{g&z<d
    ze#!Jzc<resd-zbynfH>s_B7mC&oMY-iC;Hko5VWzG6H*5jBqxg(1(`Wfg#H~yF=%;
    z14?Lp<nW%Mb4J4v|0Ms@$+*LlsBf+!*pw+ONm0NR$B_Qs$)w9m(TCqi^?S#W|N8Ny
    z7uDfCzM8}%%Sbloi%QVB9B3{7)R%W0?I1i_ylG!$vP81OfxM###unkNOXC|m`*7=z
    zCK~7L;RVdiRq(0B<bSh2epQTUwy_KIkp<*)X2&}7ku@ei`+Qnnufw}zAw>@^BZYDt
    zj1J$%VQ5Kd+WXT0AXfa*gNM>Iri!+4)pT6&9`Ym0;k*YZd1<2>hcWHFl;llpzyA?J
    zT+pJFgC(^%XD^kiFM0p{5<WM@4s%fq#0+=xY@!lNsThYH9<-xC7uIl_?k&VtFHJ0e
    zE|%ZdDf#Ai1o+$=8H}A0bhs#Oi-53QyNM=}_I_03eKSo?gF5eF<@d_>3kmKd^j&9>
    zA95YZ$ImB!=R&;umQo`5=V}4W_GVz^ky5iR19HA&him!{YIz5KtE~lXzhDH}YbN5w
    zNfhV5LGZ6u$1-n?7Q8~KK}AOY<rB1=G}>Yk^S9z1_j{_OM^`0W%;doY?;lJ4L;I60
    zO<}(FH_GO3=}v={9R643zV>to&GDkc#X!zcge4A%_7E3n9GxQl%#rwU+V8csgwx7V
    zrw+D;AvAFAB7&uz%Jk|n0mt)hoF26N{GSwWXu<jD6tW*XkgQQ~(?2O*w6({3jCv8u
    zB(gqkS6q#EGPCY+;D_7NeCul`(CiqzpS?`Qvunw;=9~bd-;(lHg2H5z$-pzQki1en
    z?A6%0f8jpz{zmfO(q6f1*KYnzPI9R>iBObM<c;ax+b8=ByPH8BjBx=EV{>Sw@G(jm
    zOoY^4Ea}HPk?m5_d>+fNtERB3EE9`bv^>+un8}<4t0-N0*wx_&a9y#^C74y)Q12T<
    zc;8ae0{l(EPP4sgFRp*SfXVlRs>*AVThK;$U@6XlrR0});<x4*bl0|I(@_=K+=qtP
    z{Mi6AhT;6H-RV*9A5?6@*FHz{*a~#Uz<o<eUNM`pqiV>1bbqp^Pa(HoIl4TBA+Ry&
    z+E$#VY_~^NVpQ9)a|QNh!_!=GiVp#m+K|g=@-8?bRBXz-+1W8HzyPC?aQ!;MBg%1F
    z0?c<-6E5v^C~V0TjZ?ml3Bs>DrOL;YoBV=HX+CTyLG<R18A)*PO{Ut|_kNxLf7i*o
    zFT(Z<^t%Btzz^y>sJP@2bvdXsia?=5jII|YjJ)}9IYiNF@iOs7_lPH~+sDA45p?ue
    zsg3SwnK3Wky5xi7Y5wZ*m`^@HX=@f=3|TZX%`*Lz3QiqwI#M2utG1K<)#)ZU<8CTK
    z&EXO%UWaY#d?&IE%N4&sjLJcBpAlMbb^$JCKv9}B)b7T2H+6#V!MIAys%F#&$IE6L
    zqqCBJoG&2c-jr>hwO-TQ4IFMDzpO)r`<ED|7L!}mh2NOZR)2K`+wV#c9$g{a*lL`F
    z4s!7mI<gGCv<3IZc@#adisZEY$S?XLO=GLkCMdX@bQbmUCcHnrPSIm4NXmv~PY!>*
    z7}a_2;GO&(yENwkV00dp_m#;TNa!#u{{1G}XT<9K!;`2@IY+&JzRq`b;~3rXjw<qv
    zKKEeL^_hcn*_CZLUwAAr_zyF4O@|IC9DWUC0#s=d)1D=b<<=|{93$FiIjKLZ0KE^^
    zH^8)m`4$Fa>@^LO1745q_WK%ku6x9V-;5m_{S5y(xev?ox<c~Lx;>0IvOwZrZ;`vV
    zhTI3nlYMwa4-eAgsT@9v;FAXt9(qfnWJFmm_n`^2KJg~);-!Bg@9y?@cu%cZQqhOx
    z;MWNV<i|>WxtL*yu!yn|6>R+7ytjY<^_?g$0Zm;f4W`5g9KVz>t0lB724+a~wl}gu
    zLg!ytg*9qr+6nhdY7~SRTe-M+3F&(eq4Usk%ce>=s9X&ATe400XWZ!w!Wi@X#k%p7
    zu>=@M=TK{#asIZN<i9rHzq}s5J#ajX(F&80h^P(QV*T5Gh|Cad>jdCgjG2;$z*A@q
    z6>p}=z1T=_LbcVK_mrZP*1C5XTAYB(jx?Q@A4k`SVl)oC^fJXLczzAO2~b-*QLk>o
    z|9dToRgF$UaDc&BnsOb52CY1{<$j+S07TKgFE8`Q5-R)hV6Uby6f+?xdlI|AA<utS
    zw1lj_SjTY!pr!j*h6@2&a*nfVyXJtwJ^qd9Z5JEbVO#}_D#_*N)Z&~viQtJfP6DHw
    z<+)!YWu(phaBrjJ`yP@A=L=HGdJl!M*`uTDOt{8*a~sJQ*D;JjXI!oq{$a*fVK%nl
    zZfqkltc1+MdP>pxYI*0r7{!;~;k9S5)aV7kuQ9r0V+Jick^zeIh9T!HqG(JJ9nCp(
    zhgPb`BR9r?WoVvtFBMaV5_aTJwas{KIoAVYl!=BVX%`q&l0bFkaY930o=q9uyH?+k
    zBqh455d=O63}wJj1B1T1hNkDI+4l3{Db%neYHPM<hvu^v>c-<n*#^iZ^|-gZM$s+v
    zol*#b>;B<ZTsPNq9i7dld`LQ&%A^ijGTp1=&WP!2k>GCo)Br>W;64GSreYUmZ0@BT
    zIc#U%|F&E$p6j#n;@ibob;$$V6{2nta$`(u7pi18Gg;o)f>Z1h)RbeEZ9{!}DjMZF
    zmR;Y?=|6|Un)SUEg{}Wb*v2L?bT3N1{+ptZV<j{_G#B#cPyW@Li4Hu$MM^1<lcd({
    zx*t0*-2C*Lc$vH!bn)S4<0XH!OwU-g;8J$o2(+hihm;rpB2kiqrRrh~4F=NdGO8S7
    zd@zm}p&gt^O9tnajd6x+7-B0r(X5Fsl-hjt$)<gnPvY}WsG<p#X8PJz^-+v@0L!-Q
    z+zUex@6(tr)tx8-X%|N`W@}DWe{F@?K5GJ|(uX3am(ej=pYs3}BT5Ke7d5()@aP(H
    z|F~E;)|3*KCZWU&jdC!KLAfp_ui@0YsGPeYR>di7b^7VsU9{KFhbt?Vd;#xX8HeT4
    zM-_l7$(bYYEP9!70L_z6C*Wj4R3><P1=-k!il$zqGbjgK&_gNk4m>G~zEc@Pfm9Ti
    z;c^erK$Hy>Moj++9sCSS5;<L-K{V&i0Z6%FFtQBy@+TQGeWJ<!bS}f@tfN|<$r|hu
    zizoZ}-INFt-zLVnQFLk$b|*q(uLBpo8$b4;B85r0nEK9c0#gL$rp1V}ybg8Ybh^}R
    z6Fz<cO&`DCRIhAcrj%DN-3uL_f}KY=St70x@+_l{Nz2zaX4u~wDSx4fHV?8ndPOt9
    z<~E$>F4TfcX__(!-92?d6~0YtXwSi#dc0@WkXX36$5}3w^kyEG@%kS#0RJyYZYQ22
    zV!f47n0?pV!+1amit=)wjmsKH%FpF{V*pB_2A5#6dDO-h%1#(f`?@wfjE?yKDJ*68
    z;eNQE2J!NLltUTe3e&N32%}w!H&fu`M<v;>$Y(0Hv1p0_i{ml@MQ7g22m{o%kYNqA
    z5=uUVr6B8melJy09_<j1nUxRncf~u2I-^QWt~(V{PWZrC`mK3&@Vysy-P)@hD0U#}
    z!Qw5b)5g=bvVpYVLyyUG=<BY*-hCQTugl=rHMUY1`|WB&Q4Wg1L>z=3Bla_S)JN{7
    z@}%+Ph8Cl@b>TFpakh8jY-zK6lXMQ1*D{pPqJl|@o<i(Q&1-h|p8gvRK%D?jSBk<P
    zN@3qY+}kH(QGkP`vMvI3)J^kD^;2uQ6*7n!0R=e5;2b`jwuPIaKJ8SWb%<`gVJ_tu
    z<5AeUa~b_J^rRCz4fkOR<dv0Jrse74RnNl`aK5yQ?}(_dJ<f<JG<^lJ!?>^H3^_0x
    z<T+-09rnKPL$-q&HDwUteaqfPx0O;#8&jUd3`t=&wxdQ@poQ%idGDV|RiLja@B|8_
    z*GMn6?&4<ydNy&DOGKKJ|ECO4+pN6W*X=TxoqUH7w@ea4N*tILJ^dVo-5$HOT>p)-
    z0z02SWaqu#+pvU?m&L=nf^VFT(Vu)|B`<UZWGK|vqb8YB8zneJBTG?NKgWowr1q|U
    zn$Zs~V<>jY^5?}nH}~^BorIk&kQML#vV^jyU`ut+?V-Vm6t(j|aQGu#jJ84TH5TQm
    zP?aR$**D+jdyFw1d6erya~9<qDDopPv3mmYiS(hJHYN+Pyq3M$0M%xnwNw6uBMDC)
    zhqC%)iZP}r7SdNsiZn9iF}x%}S$UH1isNbU6t&{qB7pg@zWid3|6bXG-L}&pVe>jM
    z1{>LNIL8FZA#)N&KPkxj34$rSM-+D=Fc_l~*T3on7`+v!ePtuBOD?vazIV^<sf6;<
    zBM<a(IrNWHXmEK~%3r{J1%fpAx7%mfL}B(_UyVmGP!Sr;XC7tfmJU@0rbE()-dnBJ
    zlp)3}-h$f^`2LuReDK10iorzSV@V8O+0;j4`2#G&@wf<&+{4F^2IF=GwEyL7f=Rz2
    z*^=qT528Mrh^8IfIn$)Oa_CDQ!@KfWatrJ5UtCXC!0fTucIBgYhchV#q`xGBXo7dq
    zHBDgm({27&#eiA{w-`9{2KeD0EHP|^D0_P>0tsX37!=ZzWGrgP0FR;6nc_JkiBh?!
    z%A*&3iEVejPQ@JyVfEK&dfy)ywPJfWXjqIL7&3DYv5t73MyEKLzD8XMprSHK!`7?F
    z&bS}{skOU`(3SepNhW{g@q|@BC3)kA3F_Nm){DxQUl+AC&!VtVY9IyJ_qzP9I{dub
    z5C38qwbah+TbqT^f3RenLnyFAE3x(Tu>#JG5#=6<C7X?qpUH~HI|aBDV~)jcwcaEL
    z-=#1Uq2$E<zybHR`BYtb48fdQN>?{gBAzxSQPcT*0dzJ9_VT&_$`j@K{608a1(=qc
    z=WgjhjjVV(!(q&G>u^#js4jvv&8SI3P`KV;0U{#|D>IM+49L4ume>O}Gk{AEv#C)e
    zP1xG?ujD|VCZ7WZn;9l4S|hgXHbK1gbun+d9t-liSo-%N*hx^w0u_t4;v97=gR>#d
    zZLi~RYF8<Y*&?{fy^=cR_VU)s<o8AGzcun*5!WQXv?N|;E(WqFrRvjAyqo#Ho7+%b
    zS(E~|J&SJ7kU$4T^1VrHsrLQ>127=(I<N1*uMNC*h@}zlN^)3E`wRi&&XEK3S8VmB
    zPLzF=L^`wSxymlWMP8B5Z$;m0+H(w%?@VlJqYca(SWq_I54RYVB}-zSJt^sdj-?|z
    zB{E@xRBIQTw{~=sdct1Og9CVDDj}UGXiPI?+6d~`G~;1p52k${Gw6Syc|Zo_9eMrF
    zE`itH==&~&F>)wBA3IDd*>SPzdNC017C8V5EdN7r46}=mrZ8GB1FS{4?X%uT?TAvg
    z%K>h{mJ=H!>S+R6K0l4Mcg|pC(xvTBGg&>PxO>jU%@l^j4#<GKE4===CGhkT`0L_=
    zXY8w7BVO<&EQ9kL`ST(Qy!ZR*!vM_xuzUea$6mCPgI|wr8d@rc++eYg$-oa#>fhKZ
    zLj2Q8Hz=w)-!q)XvIJc=+EAM%1)8#_7dcP`7?6D;>mbz*mnpjkVG1yBDjcu&99S!p
    z{QXMo3b}8~p*j)U*D({@?s_bCK<W$h-&U!2B4^5VZx`%hy@ci)*m;?`iNf3rSDx9?
    zSQ_+xl2%m7^XRmXm@QcT!4me8t1(~z24tVeGB`!U4^07Q`pqF0VB1qElCU-*VeqSx
    zQ{O0g_986u{(B1i_iqfmfdLti|9@o`ecR(VAOkWW12P~3G9UvoAOkWW12P~3G9Uvo
    nAOkWW12P~3G9Ux;Un>6>7KN}mp#}-t00000NkvXXu0mjfkxOCu
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/examples/data/IGNWMTSCapabilities.xml b/mapbox-gl/examples/data/IGNWMTSCapabilities.xml
    new file mode 100644
    index 0000000000..03def672fe
    --- /dev/null
    +++ b/mapbox-gl/examples/data/IGNWMTSCapabilities.xml
    @@ -0,0 +1 @@
    +<?xml version="1.0" encoding="UTF-8" ?><Capabilities version="1.0.0" xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd"><ows:ServiceIdentification><ows:Title>Service de visualisation WMTS</ows:Title><ows:Abstract>Ce service permet la visualisation de couches de données raster IGN au travers d&apos;un flux WMTS</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword><ows:Keyword>Limites administratives</ows:Keyword><ows:Keyword>Surfaces bâties</ows:Keyword><ows:Keyword>Réseaux de transport</ows:Keyword><ows:Keyword>Routes</ows:Keyword><ows:Keyword>Réseaux ferroviaires</ows:Keyword><ows:Keyword>Aérodromes</ows:Keyword><ows:Keyword>Réseau hydrographique</ows:Keyword><ows:Keyword>Parcelles cadastrales</ows:Keyword><ows:Keyword>Bâtiments</ows:Keyword><ows:Keyword>Services d&apos;utilité publique et services publics</ows:Keyword><ows:Keyword>Réseaux de transport</ows:Keyword><ows:Keyword>Hydrographie</ows:Keyword><ows:Keyword>Photographies aériennes</ows:Keyword><ows:Keyword>Cartes</ows:Keyword><ows:Keyword>Cartes historiques</ows:Keyword><ows:Keyword>Altitude</ows:Keyword></ows:Keywords><ows:ServiceType>OGC WMTS</ows:ServiceType><ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion><ows:Fees>licences</ows:Fees><ows:AccessConstraints>Conditions Générales d&apos;Utilisation disponibles ici : http://professionnels.ign.fr/sites/default/files/CGU_API_Pro.pdf</ows:AccessConstraints></ows:ServiceIdentification><ows:ServiceProvider><ows:ProviderName>IGN</ows:ProviderName><ows:ProviderSite xlink:href="" /><ows:ServiceContact><ows:IndividualName>Géoportail SAV</ows:IndividualName><ows:PositionName>custodian</ows:PositionName><ows:ContactInfo><ows:Phone><ows:Voice></ows:Voice><ows:Facsimile></ows:Facsimile></ows:Phone><ows:Address><ows:DeliveryPoint>73 avenue de Paris</ows:DeliveryPoint><ows:City>Saint Mandé</ows:City><ows:AdministrativeArea></ows:AdministrativeArea><ows:PostalCode>94160</ows:PostalCode><ows:Country>France</ows:Country><ows:ElectronicMailAddress>geop_services@geoportail.fr</ows:ElectronicMailAddress></ows:Address></ows:ContactInfo></ows:ServiceContact></ows:ServiceProvider><ows:OperationsMetadata><ows:Operation name="GetCapabilities"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://wxs.ign.fr/cle/geoportail/wmts?"><ows:Constraint name="GetEncoding"><ows:AllowedValues><ows:Value>KVP</ows:Value></ows:AllowedValues></ows:Constraint></ows:Get></ows:HTTP></ows:DCP></ows:Operation><ows:Operation name="GetTile"><ows:DCP><ows:HTTP><ows:Get xlink:href="http://wxs.ign.fr/cle/geoportail/wmts?"><ows:Constraint name="GetEncoding"><ows:AllowedValues><ows:Value>KVP</ows:Value></ows:AllowedValues></ows:Constraint></ows:Get></ows:HTTP></ows:DCP></ows:Operation></ows:OperationsMetadata><Contents><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ADMINISTRATIVEUNITS.BOUNDARIES</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1344</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4608</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5376</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9200</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>26</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>49</MinTileRow><MaxTileRow>114</MaxTileRow><MinTileCol>108</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Aéroports</ows:Title><ows:Abstract>Localisation des aéroports, aérodromes et héliports, des pistes, bâtiments et équipements dédiés.</ows:Abstract><ows:Keywords><ows:Keyword>Réseaux de transports</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-68.709 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 76.5354</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>AEROPORT_SYMBO_PYR-PNG_WLD_WM_2013-03-28</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2679</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5359</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10720</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10960</MinTileRow><MaxTileRow>18375</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21439</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>36744</MaxTileRow><MinTileCol>21272</MinTileCol><MaxTileCol>42879</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43840</MinTileRow><MaxTileRow>73488</MaxTileRow><MinTileCol>42544</MinTileCol><MaxTileCol>85759</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87696</MinTileRow><MaxTileRow>146976</MaxTileRow><MinTileCol>85104</MinTileCol><MaxTileCol>171519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>72</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>88</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>72</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sylvoécorégions d&apos;alluvions récentes</ows:Title><ows:Abstract>Affichage des cinq sylvoécorégions d’alluvions récentes correspondant aux cinq vallées des bassins des grands fleuves français et de leurs affluents, à enjeux importants.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ALLUVIONS-RECENTS_SYMBO_PYR-PNG_FXX_WM_20120807</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3986</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7973</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10958</MinTileRow><MaxTileRow>12238</MaxTileRow><MinTileCol>15955</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21916</MinTileRow><MaxTileRow>24468</MaxTileRow><MinTileCol>31918</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones franches urbaines</ows:Title><ows:Abstract>Zones franches créées au sein de quartiers de plus de 8 500 habitants particulièrement défavorisés, en tenant compte des éléments de nature à faciliter l’implantation d’entreprises ou le développement d’activités économiques.</ows:Abstract><ows:Keywords><ows:Keyword>Zones de gestion</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>AREAMANAGEMENT.ZFU</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones urbaines sensibles</ows:Title><ows:Abstract>Zones urbaines caractérisées par la présence de grands ensembles ou de quartiers d’habitat dégradé et par un déséquilibre accentué entre l’habitat et l’emploi.</ows:Abstract><ows:Keywords><ows:Keyword>Zones de gestion</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>AREAMANAGEMENT.ZUS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Bâtiments</ows:Title><ows:Abstract>Affichage des constructions couvrant le territoire français.</ows:Abstract><ows:Keywords><ows:Keyword>Bâtiments</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BATI_SYMBO_PYR-PNG_WLD_20120704</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21900</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171744</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Bâtiments</ows:Title><ows:Abstract>Affichage des constructions couvrant le territoire français.</ows:Abstract><ows:Keywords><ows:Keyword>Bâtiments</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BATI_SYMBO_PYR-PNG_WLD_20130131</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171727</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Bâtiments</ows:Title><ows:Abstract>Affichage des constructions couvrant le territoire français.</ows:Abstract><ows:Keywords><ows:Keyword>Bâtiments</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BATI_SYMBO_PYR-PNG_WLD_20130204</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171727</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Altitude</ows:Title><ows:Abstract>La couche altitude se compose d&apos;un MNT (Modèle Numérique de Terrain) affiché en teintes hypsométriques et issu de la BD ALTI®.</ows:Abstract><ows:Keywords><ows:Keyword>Altitude</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.206 -22.5952</ows:LowerCorner><ows:UpperCorner>167.432 50.9308</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDALTI_PYR-JPEG_WLD_WM_WMTS_20120617</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>578</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>988</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1156</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1976</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2312</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4624</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7906</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9248</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15812</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>36</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>61</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>72</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>289</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>494</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte du relief</ows:Title><ows:Abstract>La couche altitude se compose d&apos;un MNT (Modèle Numérique de Terrain) affiché en teintes hypsométriques et issu de la BD ALTI®.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.206 -22.5952</ows:LowerCorner><ows:UpperCorner>167.432 50.9308</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDALTI_PYR-JPEG_WLD_WM_WMTS_20120618</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>578</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>988</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1156</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1976</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2312</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4624</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7906</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9248</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15812</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>36</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>61</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>72</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>289</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>494</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte forestière v1 (1987-2004)</ows:Title><ows:Abstract>La cartographie forestière est une base de données cartographique des formations végétales forestières et naturelles. Elle couvre l&apos;ensemble du territoire de France métropolitaine. Elle est réalisée par interprétation de photographies aériennes infrarouges et affinée par des contrôles sur le terrain (le seuil minimal de surface cartographiée est de 2,25 ha).</ows:Abstract><ows:Keywords><ows:Keyword>Inventaire forestier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDFORETV1_PYR-PNG_FXX_WM_1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte forestière (v2 : 2006-)</ows:Title><ows:Abstract>Localisation de 32 types de formations végétales sur le territoire. Carte en cours de réalisation depuis 2006.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDFORETV2_PYR-PNG_FXX_WM_20120625</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcelles cadastrales</ows:Title><ows:Abstract>Limites des parcelles cadastrales issues de plans scannés et de plans numériques.</ows:Abstract><ows:Keywords><ows:Keyword>Parcelles cadastrales</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 14.3811</ows:LowerCorner><ows:UpperCorner>9.66314 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDPARCEL_PYR-PNG_WLD_WM_20120712</ows:Identifier><Style isDefault="true"><ows:Title>BD Parcellaire Noire et transparente</ows:Title><ows:Abstract>tracé noir sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>noir</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Style><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>BD Parcellaire orange transparente</ows:Title><ows:Abstract>Orange sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>orange</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire_o</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="999999" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>470</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>941</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1882</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2156</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>3765</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>4313</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>7530</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>8627</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>15060</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>30121</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>60243</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>69020</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>120487</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>138041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>175302</MinTileRow><MaxTileRow>240975</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>276082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>350605</MinTileRow><MaxTileRow>481951</MaxTileRow><MinTileCol>340318</MinTileCol><MaxTileCol>552164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>117</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>235</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcelles cadastrales</ows:Title><ows:Abstract>Limites des parcelles cadastrales issues de plans scannés et de plans numériques.</ows:Abstract><ows:Keywords><ows:Keyword>Parcelles cadastrales</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 14.3811</ows:LowerCorner><ows:UpperCorner>9.66314 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDPARCEL_PYR-PNG_WLD_WM_20120716</ows:Identifier><Style isDefault="true"><ows:Title>BD Parcellaire Noire et transparente</ows:Title><ows:Abstract>tracé noir sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>noir</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Style><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>BD Parcellaire orange transparente</ows:Title><ows:Abstract>Orange sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>orange</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire_o</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="999999" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>470</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>941</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1882</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2156</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>3765</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>4313</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>7530</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>8627</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>15060</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>30121</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>60243</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>69020</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>120487</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>138041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>175302</MinTileRow><MaxTileRow>240975</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>276082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>350605</MinTileRow><MaxTileRow>481951</MaxTileRow><MinTileCol>340318</MinTileCol><MaxTileCol>552164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>117</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>235</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcelles cadastrales</ows:Title><ows:Abstract>Limites des parcelles cadastrales issues de plans scannés et de plans numériques.</ows:Abstract><ows:Keywords><ows:Keyword>Parcelles cadastrales</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 -21.3922</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDPARCEL_PYR-PNG_WLD_WM_20120723</ows:Identifier><Style isDefault="true"><ows:Title>BD Parcellaire Noire et transparente</ows:Title><ows:Abstract>tracé noir sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>noir</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Style><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>BD Parcellaire orange transparente</ows:Title><ows:Abstract>Orange sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>orange</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire_o</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="999999" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9188</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18377</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36755</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73511</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147023</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>175302</MinTileRow><MaxTileRow>294046</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>350605</MinTileRow><MaxTileRow>588093</MaxTileRow><MinTileCol>340318</MinTileCol><MaxTileCol>686946</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcelles cadastrales</ows:Title><ows:Abstract>Limites des parcelles cadastrales issues de plans scannés et de plans numériques.</ows:Abstract><ows:Keywords><ows:Keyword>Parcelles cadastrales</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 -21.3922</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BDPARCEL_PYR-PNG_WLD_WM_20121220</ows:Identifier><Style isDefault="true"><ows:Title>BD Parcellaire Noire et transparente</ows:Title><ows:Abstract>tracé noir sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>noir</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Style><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>BD Parcellaire orange transparente</ows:Title><ows:Abstract>Orange sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>orange</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire_o</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="999999" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9188</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18377</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36755</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73511</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147023</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>175302</MinTileRow><MaxTileRow>294046</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>350605</MinTileRow><MaxTileRow>588093</MaxTileRow><MinTileCol>340318</MinTileCol><MaxTileCol>686946</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Guyane Françoise (1780)</ows:Title><ows:Abstract>Carte de Guyane française établie par M. Bonne, Ingénieur-Hydrographe de la Marine. Cette carte figure dans l&apos;&quot;atlas de toutes les parties connues du globe terrestre&quot;, ouvrage rédigé par l&apos;abbé Guillaume-Thomas RAYNAL et publié à Genève en 1780.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-55.9127 -0.49941</ows:LowerCorner><ows:UpperCorner>-50.0835 7.88966</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BONNE_PYR-JPG_GUF_WM_20121214</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>489</MinTileRow><MaxTileRow>513</MaxTileRow><MinTileCol>352</MinTileCol><MaxTileCol>369</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>11</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>30</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>23</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>61</MinTileRow><MaxTileRow>64</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>46</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>122</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>88</MinTileCol><MaxTileCol>92</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>244</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>176</MinTileCol><MaxTileCol>184</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Guyane française (1780)</ows:Title><ows:Abstract>Carte de Guyane française établie par M. Bonne, Ingénieur-Hydrographe de la Marine. Cette carte figure dans l&apos;&quot;atlas de toutes les parties connues du globe terrestre&quot;, ouvrage rédigé par l&apos;abbé Guillaume-Thomas RAYNAL et publié à Genève en 1780.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-55.9127 -0.49941</ows:LowerCorner><ows:UpperCorner>-50.0835 7.88966</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BONNE_PYR-JPG_GUF_WM_20130124</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>489</MinTileRow><MaxTileRow>513</MaxTileRow><MinTileCol>352</MinTileCol><MaxTileCol>369</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>11</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>30</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>23</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>61</MinTileRow><MaxTileRow>64</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>46</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>122</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>88</MinTileCol><MaxTileCol>92</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>244</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>176</MinTileCol><MaxTileCol>184</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Bâtiments</ows:Title><ows:Abstract>Affichage des constructions couvrant le territoire français.</ows:Abstract><ows:Keywords><ows:Keyword>Bâtiments</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>BUILDINGS.BUILDINGS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171727</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcelles cadastrales</ows:Title><ows:Abstract>Limites des parcelles cadastrales issues de plans scannés et de plans numériques.</ows:Abstract><ows:Keywords><ows:Keyword>Parcelles cadastrales</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 -21.3922</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CADASTRALPARCELS.PARCELS</ows:Identifier><Style isDefault="true"><ows:Title>BD Parcellaire Noire et transparente</ows:Title><ows:Abstract>tracé noir sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>noir</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Style><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>BD Parcellaire orange transparente</ows:Title><ows:Abstract>Orange sur fond transparent</ows:Abstract><ows:Keywords><ows:Keyword>orange</ows:Keyword><ows:Keyword>transparent</ows:Keyword></ows:Keywords><ows:Identifier>bdparcellaire_o</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="999999" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9188</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18377</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36755</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73511</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147023</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>175302</MinTileRow><MaxTileRow>294046</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>350605</MinTileRow><MaxTileRow>588093</MaxTileRow><MinTileCol>340318</MinTileCol><MaxTileCol>686946</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte France Raster ®</ows:Title><ows:Abstract>Carte à grande échelle spécialement adaptée à l&apos;affichage de données urbaines.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.7576 -21.5655</ows:LowerCorner><ows:UpperCorner>56.0405 51.0019</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE-FRANCERASTER_PYR-JPEG_WLD_WM_20120706</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2299</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4598</MaxTileRow><MinTileCol>2644</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5465</MinTileRow><MaxTileRow>9197</MaxTileRow><MinTileCol>5311</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10931</MinTileRow><MaxTileRow>18394</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36771</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43804</MinTileRow><MaxTileRow>64779</MaxTileRow><MinTileCol>42529</MinTileCol><MaxTileCol>69075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87609</MinTileRow><MaxTileRow>147051</MaxTileRow><MinTileCol>85058</MinTileCol><MaxTileCol>171737</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>34</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>68</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Mondiale pour la mini-vue</ows:Title><ows:Abstract>Carte Mondiale pour la mini-vue</ows:Abstract><ows:Keywords><ows:Keyword>Mini-Vue</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-179.5 -75</ows:LowerCorner><ows:UpperCorner>179.5 75</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE-MINIVUE_PYR-JPG_WLD_WM_20120706</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>13</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>26</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>52</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>105</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>210</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -60</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20120509</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5316</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 90</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20121005</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1554</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3108</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>5294</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>10107</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171752</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 90</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20121121</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1554</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3108</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171752</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 90</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20121122</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171752</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -67.7557</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20130115</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43696</MinTileRow><MaxTileRow>73552</MaxTileRow><MinTileCol>42447</MinTileCol><MaxTileCol>85903</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87392</MinTileRow><MaxTileRow>147104</MaxTileRow><MinTileCol>84912</MinTileCol><MaxTileCol>171807</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -67.7557</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20130205</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43696</MinTileRow><MaxTileRow>73552</MaxTileRow><MinTileCol>42447</MinTileCol><MaxTileCol>85903</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87392</MinTileRow><MaxTileRow>147104</MaxTileRow><MinTileCol>84912</MinTileCol><MaxTileCol>171807</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -67.7557</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_20130227</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43696</MinTileRow><MaxTileRow>73552</MaxTileRow><MinTileCol>42447</MinTileCol><MaxTileCol>85903</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87392</MinTileRow><MaxTileRow>147104</MaxTileRow><MinTileCol>84912</MinTileCol><MaxTileCol>171807</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -67.7557</ows:LowerCorner><ows:UpperCorner>180 85.0511</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CARTE_PYR-JPG_WLD_WM_WM_20130603</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43696</MinTileRow><MaxTileRow>73552</MaxTileRow><MinTileCol>42447</MinTileCol><MaxTileCol>85903</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87392</MinTileRow><MaxTileRow>147104</MaxTileRow><MinTileCol>84912</MinTileCol><MaxTileCol>171807</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de Cassini</ows:Title><ows:Abstract>Carte de Cassini en couleur (feuilles gravées et aquarellées), issue de l’exemplaire dit de « Marie-Antoinette » du XVIIIe siècle.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.00063 42.019</ows:LowerCorner><ows:UpperCorner>9.2809 51.2455</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CASSINI_PYR-JPG_WLD_WM_2012-06-01</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>494</MinTileCol><MaxTileCol>538</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>760</MaxTileRow><MinTileCol>989</MinTileCol><MaxTileCol>1076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1520</MaxTileRow><MinTileCol>1979</MinTileCol><MaxTileCol>2153</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3040</MaxTileRow><MinTileCol>3959</MinTileCol><MaxTileCol>4307</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>6080</MaxTileRow><MinTileCol>7918</MinTileCol><MaxTileCol>8614</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10934</MinTileRow><MaxTileRow>12161</MaxTileRow><MinTileCol>15837</MinTileCol><MaxTileCol>17228</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de Cassini (XVIIIe siècle)</ows:Title><ows:Abstract>Carte de Cassini en couleur (feuilles gravées et aquarellées), issue de l’exemplaire dit de « Marie-Antoinette » du XVIIIe siècle.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.00063 42.019</ows:LowerCorner><ows:UpperCorner>9.2809 51.2455</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CASSINI_PYR-JPG_WLD_WM_20130125</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>494</MinTileCol><MaxTileCol>538</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>760</MaxTileRow><MinTileCol>989</MinTileCol><MaxTileCol>1076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1520</MaxTileRow><MinTileCol>1979</MinTileCol><MaxTileCol>2153</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3040</MaxTileRow><MinTileCol>3959</MinTileCol><MaxTileCol>4307</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>6080</MaxTileRow><MinTileCol>7918</MinTileCol><MaxTileCol>8614</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10934</MinTileRow><MaxTileRow>12161</MaxTileRow><MinTileCol>15837</MinTileCol><MaxTileCol>17228</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Iris</ows:Title><ows:Abstract>Zones Iris définies par l&apos;INSEE pour les besoins des recensements sur l&apos;ensemble des communes de plus de 10 000 habitants et la plupart des communes de 5 000 à 10 000 habitants.</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>CONTOURS_IRIS_WM_20120830</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Lignes électriques</ows:Title><ows:Abstract>Affichage des lignes électriques.</ows:Abstract><ows:Keywords><ows:Keyword>Energie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEC_SYMBO_PYR-PNG_WLD_WM_20130313</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1536</MaxTileRow><MinTileCol>1992</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10959</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10639</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>36752</MaxTileRow><MinTileCol>21280</MinTileCol><MaxTileCol>42927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43840</MinTileRow><MaxTileRow>73504</MaxTileRow><MinTileCol>42559</MinTileCol><MaxTileCol>85855</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87696</MinTileRow><MaxTileRow>147008</MaxTileRow><MinTileCol>85136</MinTileCol><MaxTileCol>171695</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>MNT BDAlti V1</ows:Title><ows:Abstract>BDAlti V1 au pas de 25m</ows:Abstract><ows:Keywords><ows:Keyword>MNT</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-64 -23</ows:LowerCorner><ows:UpperCorner>168 52</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEVATION.ELEVATIONGRIDCOVERAGE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/x-bil;bits=32</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>216</MinTileRow><MaxTileRow>642</MaxTileRow><MinTileCol>659</MinTileCol><MaxTileCol>1979</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>432</MinTileRow><MaxTileRow>1285</MaxTileRow><MinTileCol>1319</MinTileCol><MaxTileCol>3959</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>10</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>30</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>6</MinTileRow><MaxTileRow>20</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>61</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>40</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>80</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>54</MinTileRow><MaxTileRow>160</MaxTileRow><MinTileCol>164</MinTileCol><MaxTileCol>494</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>108</MinTileRow><MaxTileRow>321</MaxTileRow><MinTileCol>329</MinTileCol><MaxTileCol>989</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Trait de côte Histolitt</ows:Title><ows:Abstract>Formalisation de la limite entre le domaine terrestre et le domaine marin.</ows:Abstract><ows:Keywords><ows:Keyword>Altitude</ows:Keyword><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2529 -21.51</ows:LowerCorner><ows:UpperCorner>55.9472 51.1388</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEVATION.LEVEL0</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>676</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>4597</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5474</MinTileRow><MaxTileRow>9194</MaxTileRow><MinTileCol>5313</MinTileCol><MaxTileCol>10737</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10949</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21466</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21907</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42933</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43814</MinTileRow><MaxTileRow>73526</MaxTileRow><MinTileCol>42538</MinTileCol><MaxTileCol>85866</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87644</MinTileRow><MaxTileRow>147036</MaxTileRow><MinTileCol>85076</MinTileCol><MaxTileCol>171732</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>171</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>342</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte du relief</ows:Title><ows:Abstract>La couche altitude se compose d&apos;un MNT (Modèle Numérique de Terrain) affiché en teintes hypsométriques et issu de la BD ALTI®.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.206 -22.5952</ows:LowerCorner><ows:UpperCorner>167.432 50.9308</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEVATION.SLOPES</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>578</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>988</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1156</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1976</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2312</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4624</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7906</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9248</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15812</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>36</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>61</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>72</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>289</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>494</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Litto3D ®</ows:Title><ows:Abstract>Modèle numérique continu terre-mer sur la frange littorale.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.75296 42.9786</ows:LowerCorner><ows:UpperCorner>6.25761 43.2733</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEVATION.SLOPES.HIGHRES</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>375</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>750</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1056</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1500</MinTileRow><MaxTileRow>1505</MaxTileRow><MinTileCol>2113</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3001</MinTileRow><MaxTileRow>3010</MaxTileRow><MinTileCol>4226</MinTileCol><MaxTileCol>4238</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6003</MinTileRow><MaxTileRow>6021</MaxTileRow><MinTileCol>8453</MinTileCol><MaxTileCol>8476</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12006</MinTileRow><MaxTileRow>12043</MaxTileRow><MinTileCol>16907</MinTileCol><MaxTileCol>16953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24012</MinTileRow><MaxTileRow>24086</MaxTileRow><MinTileCol>33815</MinTileCol><MaxTileCol>33907</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Altitude</ows:Title><ows:Abstract>Images en teintes hypsométriques de la BDAlti à 25m</ows:Abstract><ows:Keywords><ows:Keyword>géographie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.2057 -21.3923</ows:LowerCorner><ows:UpperCorner>55.8466 -20.8627</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ELEVATION.SLOPES_WLD_2.0_3857_JPG</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>668</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1145</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>1337</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2290</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>2675</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4581</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>5351</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Europe politique</ows:Title><ows:Abstract>Carte Europe politique</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-41.4623 23.8101</ows:LowerCorner><ows:UpperCorner>74.8102 68.9745</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>EUROPEPOLITIQUE_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>200</MinTileRow><MaxTileRow>442</MaxTileRow><MinTileCol>387</MinTileCol><MaxTileCol>731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>6</MinTileCol><MaxTileCol>11</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>6</MinTileRow><MaxTileRow>13</MaxTileRow><MinTileCol>12</MinTileCol><MaxTileCol>22</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>27</MaxTileRow><MinTileCol>24</MinTileCol><MaxTileCol>45</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>25</MinTileRow><MaxTileRow>55</MaxTileRow><MinTileCol>48</MinTileCol><MaxTileCol>91</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>110</MaxTileRow><MinTileCol>96</MinTileCol><MaxTileCol>182</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>100</MinTileRow><MaxTileRow>221</MaxTileRow><MinTileCol>193</MinTileCol><MaxTileCol>365</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Europe politique</ows:Title><ows:Abstract>Carte Europe politique</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-41.4623 23.8101</ows:LowerCorner><ows:UpperCorner>74.8102 68.9745</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>EUROPEPOLITIQUE_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>200</MinTileRow><MaxTileRow>442</MaxTileRow><MinTileCol>387</MinTileCol><MaxTileCol>731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>6</MinTileCol><MaxTileCol>11</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>6</MinTileRow><MaxTileRow>13</MaxTileRow><MinTileCol>12</MinTileCol><MaxTileCol>22</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>27</MaxTileRow><MinTileCol>24</MinTileCol><MaxTileCol>45</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>25</MinTileRow><MaxTileRow>55</MaxTileRow><MinTileCol>48</MinTileCol><MaxTileCol>91</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>110</MaxTileRow><MinTileCol>96</MinTileCol><MaxTileCol>182</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>100</MinTileRow><MaxTileRow>221</MaxTileRow><MinTileCol>193</MinTileCol><MaxTileCol>365</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réseau ferroviaire</ows:Title><ows:Abstract>Carte du réseau ferroviaire</ows:Abstract><ows:Keywords><ows:Keyword>Voies ferrées</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>FERRE_SYMBO_PYR-PNG_WLD_WM_20120713</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réseau ferroviaire</ows:Title><ows:Abstract>Carte du réseau ferroviaire</ows:Abstract><ows:Keywords><ows:Keyword>Voies ferrées</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>FERRE_SYMBO_PYR-PNG_WLD_WM_20120809</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10958</MinTileRow><MaxTileRow>12174</MaxTileRow><MinTileCol>15971</MinTileCol><MaxTileCol>17251</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21916</MinTileRow><MaxTileRow>24348</MaxTileRow><MinTileCol>31950</MinTileCol><MaxTileCol>34494</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réseau ferroviaire</ows:Title><ows:Abstract>Carte du réseau ferroviaire</ows:Abstract><ows:Keywords><ows:Keyword>Voies ferrées</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>FERRE_SYMBO_PYR-PNG_WLD_WM_20120814</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10958</MinTileRow><MaxTileRow>12174</MaxTileRow><MinTileCol>15971</MinTileCol><MaxTileCol>17251</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21916</MinTileRow><MaxTileRow>24348</MaxTileRow><MinTileCol>31950</MinTileCol><MaxTileCol>34494</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43833</MinTileRow><MaxTileRow>48697</MaxTileRow><MinTileCol>63900</MinTileCol><MaxTileCol>68988</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87666</MinTileRow><MaxTileRow>97394</MaxTileRow><MinTileCol>127801</MinTileCol><MaxTileCol>137961</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réseau ferroviaire</ows:Title><ows:Abstract>Carte du réseau ferroviaire</ows:Abstract><ows:Keywords><ows:Keyword>Voies ferrées</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>FERRE_SYMBO_PYR-PNG_WLD_WM_20130312</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3048</MaxTileRow><MinTileCol>3992</MinTileCol><MaxTileCol>4312</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>6088</MaxTileRow><MinTileCol>7984</MinTileCol><MaxTileCol>8624</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>12175</MaxTileRow><MinTileCol>15968</MinTileCol><MaxTileCol>17248</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24352</MaxTileRow><MinTileCol>31936</MinTileCol><MaxTileCol>34496</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>48704</MaxTileRow><MinTileCol>63872</MinTileCol><MaxTileCol>68991</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87664</MinTileRow><MaxTileRow>97392</MaxTileRow><MinTileCol>127744</MinTileCol><MaxTileCol>137968</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Forêt</ows:Title><ows:Abstract>Représentation simplifiée des forêts.</ows:Abstract><ows:Keywords><ows:Keyword>Forêt</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>FORESTCOVER_SYMBO_PYR-PNG_WLD_WM_20130523</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>18383</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36760</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Paris haute résolution</ows:Title><ows:Abstract>Prises de vues aériennes de Paris à haute résolution.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.19509 48.8095</ows:LowerCorner><ows:UpperCorner>2.42968 48.9124</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEN_ORTHOPARIS_FXX_PYR-PNG_WM_20120705</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2072</MinTileCol><MaxTileCol>2075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2816</MinTileRow><MaxTileRow>2819</MaxTileRow><MinTileCol>4145</MinTileCol><MaxTileCol>4151</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5632</MinTileRow><MaxTileRow>5639</MaxTileRow><MinTileCol>8291</MinTileCol><MaxTileCol>8302</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11265</MinTileRow><MaxTileRow>11279</MaxTileRow><MinTileCol>16583</MinTileCol><MaxTileCol>16605</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22530</MinTileRow><MaxTileRow>22559</MaxTileRow><MinTileCol>33167</MinTileCol><MaxTileCol>33210</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45061</MinTileRow><MaxTileRow>45118</MaxTileRow><MinTileCol>66335</MinTileCol><MaxTileCol>66420</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>90123</MinTileRow><MaxTileRow>90237</MaxTileRow><MinTileCol>132670</MinTileCol><MaxTileCol>132841</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>180246</MinTileRow><MaxTileRow>180474</MaxTileRow><MinTileCol>265340</MinTileCol><MaxTileCol>265682</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>360492</MinTileRow><MaxTileRow>360948</MaxTileRow><MinTileCol>530681</MinTileCol><MaxTileCol>531364</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte topographique - environs de Paris (1906)</ows:Title><ows:Abstract>Carte topographique de Paris et de ses environs éditée en 1906.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.62941 48.4726</ows:LowerCorner><ows:UpperCorner>3 49.1548</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.1900TYPEMAPS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>701</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1033</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1403</MinTileRow><MaxTileRow>1415</MaxTileRow><MinTileCol>2066</MinTileCol><MaxTileCol>2082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2807</MinTileRow><MaxTileRow>2831</MaxTileRow><MinTileCol>4132</MinTileCol><MaxTileCol>4164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5615</MinTileRow><MaxTileRow>5663</MaxTileRow><MinTileCol>8265</MinTileCol><MaxTileCol>8328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11231</MinTileRow><MaxTileRow>11326</MaxTileRow><MinTileCol>16531</MinTileCol><MaxTileCol>16657</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>France administrative</ows:Title><ows:Abstract>Carte de la France administrative (régions, départements, arrondissements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2459 -21.6371</ows:LowerCorner><ows:UpperCorner>56.052 51.5596</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.ADMINISTRATIVEUNITS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>339</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>84</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>169</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Guyane française (1780)</ows:Title><ows:Abstract>Carte de Guyane française établie par M. Bonne, Ingénieur-Hydrographe de la Marine. Cette carte figure dans l&apos;&quot;atlas de toutes les parties connues du globe terrestre&quot;, ouvrage rédigé par l&apos;abbé Guillaume-Thomas RAYNAL et publié à Genève en 1780.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-55.9127 -0.49941</ows:LowerCorner><ows:UpperCorner>-50.0835 7.88966</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.BONNE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>489</MinTileRow><MaxTileRow>513</MaxTileRow><MinTileCol>352</MinTileCol><MaxTileCol>369</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>11</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>30</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>23</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>61</MinTileRow><MaxTileRow>64</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>46</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>122</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>88</MinTileCol><MaxTileCol>92</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>244</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>176</MinTileCol><MaxTileCol>184</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de Cassini (XVIIIe siècle)</ows:Title><ows:Abstract>Carte de Cassini en couleur (feuilles gravées et aquarellées), issue de l’exemplaire dit de « Marie-Antoinette » du XVIIIe siècle.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.00063 42.019</ows:LowerCorner><ows:UpperCorner>9.2809 51.2455</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.CASSINI</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>494</MinTileCol><MaxTileCol>538</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>760</MaxTileRow><MinTileCol>989</MinTileCol><MaxTileCol>1076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1520</MaxTileRow><MinTileCol>1979</MinTileCol><MaxTileCol>2153</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3040</MaxTileRow><MinTileCol>3959</MinTileCol><MaxTileCol>4307</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>6080</MaxTileRow><MinTileCol>7918</MinTileCol><MaxTileCol>8614</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10934</MinTileRow><MaxTileRow>12161</MaxTileRow><MinTileCol>15837</MinTileCol><MaxTileCol>17228</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte littorale (SHOM/IGN)</ows:Title><ows:Abstract>Assemblage des cartes marines du Service hydrographique et océanographique de la marine (SHOM) et des cartes terrestres de l’IGN.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.9629 -21.5348</ows:LowerCorner><ows:UpperCorner>56.1621 51.454</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.COASTALMAPS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>335</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2299</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4598</MaxTileRow><MinTileCol>2686</MinTileCol><MaxTileCol>5374</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9196</MaxTileRow><MinTileCol>5372</MinTileCol><MaxTileCol>10748</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10904</MinTileRow><MaxTileRow>18392</MaxTileRow><MinTileCol>10744</MinTileCol><MaxTileCol>21496</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21808</MinTileRow><MaxTileRow>36784</MaxTileRow><MinTileCol>21488</MinTileCol><MaxTileCol>42992</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie AJACCIO 1976</ows:Title><ows:Abstract>Zone d&apos;Ajaccio - Cartographie 1976</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>8.507 41.6654</ows:LowerCorner><ows:UpperCorner>9.13252 42.0381</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.AJACCIO1976</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>379</MinTileRow><MaxTileRow>381</MaxTileRow><MinTileCol>536</MinTileCol><MaxTileCol>537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>759</MinTileRow><MaxTileRow>762</MaxTileRow><MinTileCol>1072</MinTileCol><MaxTileCol>1075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1519</MinTileRow><MaxTileRow>1525</MaxTileRow><MinTileCol>2144</MinTileCol><MaxTileCol>2151</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3039</MinTileRow><MaxTileRow>3051</MaxTileRow><MinTileCol>4289</MinTileCol><MaxTileCol>4303</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6079</MinTileRow><MaxTileRow>6102</MaxTileRow><MinTileCol>8579</MinTileCol><MaxTileCol>8607</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12159</MinTileRow><MaxTileRow>12204</MaxTileRow><MinTileCol>17158</MinTileCol><MaxTileCol>17215</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24318</MinTileRow><MaxTileRow>24409</MaxTileRow><MinTileCol>34316</MinTileCol><MaxTileCol>34430</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>47</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>67</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>94</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>134</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>189</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>268</MinTileCol><MaxTileCol>268</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie BELFORT MONTBELLIARD 1973</ows:Title><ows:Abstract>Zone d&apos;Ajaccio - BELFORT MONTBELLIARD 1973</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>6.50535 47.3676</ows:LowerCorner><ows:UpperCorner>7.25597 47.8735</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.BELFORT-MONTBELLIARD1973</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>530</MinTileCol><MaxTileCol>532</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>712</MinTileRow><MaxTileRow>717</MaxTileRow><MinTileCol>1061</MinTileCol><MaxTileCol>1065</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1425</MinTileRow><MaxTileRow>1434</MaxTileRow><MinTileCol>2122</MinTileCol><MaxTileCol>2130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2851</MinTileRow><MaxTileRow>2869</MaxTileRow><MinTileCol>4244</MinTileCol><MaxTileCol>4261</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5703</MinTileRow><MaxTileRow>5738</MaxTileRow><MinTileCol>8488</MinTileCol><MaxTileCol>8522</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11407</MinTileRow><MaxTileRow>11476</MaxTileRow><MinTileCol>16976</MinTileCol><MaxTileCol>17044</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22815</MinTileRow><MaxTileRow>22952</MaxTileRow><MinTileCol>33952</MinTileCol><MaxTileCol>34088</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>265</MinTileCol><MaxTileCol>266</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie BERRY SUD 1952</ows:Title><ows:Abstract>Zone de BERRY SUD - Cartographie 1952</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.00082 46.168</ows:LowerCorner><ows:UpperCorner>1.75144 46.6854</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.BERRY-SUD1952</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>361</MinTileRow><MaxTileRow>363</MaxTileRow><MinTileCol>514</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>722</MinTileRow><MaxTileRow>727</MaxTileRow><MinTileCol>1029</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1445</MinTileRow><MaxTileRow>1454</MaxTileRow><MinTileCol>2059</MinTileCol><MaxTileCol>2067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2891</MinTileRow><MaxTileRow>2908</MaxTileRow><MinTileCol>4118</MinTileCol><MaxTileCol>4135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5783</MinTileRow><MaxTileRow>5817</MaxTileRow><MinTileCol>8237</MinTileCol><MaxTileCol>8271</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11567</MinTileRow><MaxTileRow>11635</MaxTileRow><MinTileCol>16475</MinTileCol><MaxTileCol>16543</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23134</MinTileRow><MaxTileRow>23271</MaxTileRow><MinTileCol>32950</MinTileCol><MaxTileCol>33086</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>180</MinTileRow><MaxTileRow>181</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie BETHUNE 1956</ows:Title><ows:Abstract>Zone de BETHUNE 1956</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.37696 50.17</ows:LowerCorner><ows:UpperCorner>3.37778 50.6484</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.BETHUNE1956</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>344</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>688</MinTileRow><MaxTileRow>693</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1043</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1377</MinTileRow><MaxTileRow>1386</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2086</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2755</MinTileRow><MaxTileRow>2772</MaxTileRow><MinTileCol>4150</MinTileCol><MaxTileCol>4172</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5510</MinTileRow><MaxTileRow>5544</MaxTileRow><MinTileCol>8300</MinTileCol><MaxTileCol>8345</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11020</MinTileRow><MaxTileRow>11088</MaxTileRow><MinTileCol>16600</MinTileCol><MaxTileCol>16691</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22041</MinTileRow><MaxTileRow>22177</MaxTileRow><MinTileCol>33200</MinTileCol><MaxTileCol>33382</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>86</MinTileRow><MaxTileRow>86</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>172</MinTileRow><MaxTileRow>173</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie BIARRITZ 1979</ows:Title><ows:Abstract>Zone de Biarritz 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.87654 43.1433</ows:LowerCorner><ows:UpperCorner>-1.25103 43.6885</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.BIARRITZ1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>508</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>747</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1013</MinTileCol><MaxTileCol>1016</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1494</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2026</MinTileCol><MaxTileCol>2033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2988</MinTileRow><MaxTileRow>3005</MaxTileRow><MinTileCol>4053</MinTileCol><MaxTileCol>4067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5977</MinTileRow><MaxTileRow>6011</MaxTileRow><MinTileCol>8106</MinTileCol><MaxTileCol>8135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11954</MinTileRow><MaxTileRow>12022</MaxTileRow><MinTileCol>16213</MinTileCol><MaxTileCol>16270</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23908</MinTileRow><MaxTileRow>24045</MaxTileRow><MinTileCol>32426</MinTileCol><MaxTileCol>32540</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie BOURG St-MAURICE 1974</ows:Title><ows:Abstract>Zone de BOURG St-MAURICE - Cartographie 1974</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>6.50535 45.3827</ows:LowerCorner><ows:UpperCorner>7.13087 45.7331</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.BOURG-ST-MAURICE1974</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>365</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>530</MinTileCol><MaxTileCol>532</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>730</MinTileRow><MaxTileRow>733</MaxTileRow><MinTileCol>1061</MinTileCol><MaxTileCol>1064</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1461</MinTileRow><MaxTileRow>1467</MaxTileRow><MinTileCol>2122</MinTileCol><MaxTileCol>2129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2923</MinTileRow><MaxTileRow>2934</MaxTileRow><MinTileCol>4244</MinTileCol><MaxTileCol>4258</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5846</MinTileRow><MaxTileRow>5869</MaxTileRow><MinTileCol>8488</MinTileCol><MaxTileCol>8516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11692</MinTileRow><MaxTileRow>11738</MaxTileRow><MinTileCol>16976</MinTileCol><MaxTileCol>17033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23384</MinTileRow><MaxTileRow>23476</MaxTileRow><MinTileCol>33952</MinTileCol><MaxTileCol>34066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>265</MinTileCol><MaxTileCol>266</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie CAEN 1969</ows:Title><ows:Abstract>Zone de CAEN - Cartographie 1969</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-0.875721 49.035</ows:LowerCorner><ows:UpperCorner>-0.125103 49.4434</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.CAEN1969</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>509</MinTileCol><MaxTileCol>511</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>699</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1019</MinTileCol><MaxTileCol>1023</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1398</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2038</MinTileCol><MaxTileCol>2046</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2797</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4076</MinTileCol><MaxTileCol>4093</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5595</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8152</MinTileCol><MaxTileCol>8186</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11191</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16304</MinTileCol><MaxTileCol>16372</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22382</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>32608</MinTileCol><MaxTileCol>32745</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>255</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Cap d&apos;AGDE 1971</ows:Title><ows:Abstract>Zone du Cap d&apos;Agde 1971</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.00247 43.1433</ows:LowerCorner><ows:UpperCorner>3.62799 43.5073</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.CAP-DAGDE1971</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>522</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1041</MinTileCol><MaxTileCol>1044</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1497</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2082</MinTileCol><MaxTileCol>2089</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2994</MinTileRow><MaxTileRow>3005</MaxTileRow><MinTileCol>4164</MinTileCol><MaxTileCol>4178</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5988</MinTileRow><MaxTileRow>6011</MaxTileRow><MinTileCol>8328</MinTileCol><MaxTileCol>8357</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11977</MinTileRow><MaxTileRow>12022</MaxTileRow><MinTileCol>16657</MinTileCol><MaxTileCol>16714</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23954</MinTileRow><MaxTileRow>24045</MaxTileRow><MinTileCol>33314</MinTileCol><MaxTileCol>33428</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>261</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie CLERMONT FERRAND 1966</ows:Title><ows:Abstract>Zone de CLERMONT FERRAND 1966</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.75227 45.6457</ows:LowerCorner><ows:UpperCorner>3.37778 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.CLERMONT-FERRAND1966</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>519</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>731</MaxTileRow><MinTileCol>1039</MinTileCol><MaxTileCol>1043</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1462</MaxTileRow><MinTileCol>2079</MinTileCol><MaxTileCol>2086</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2925</MaxTileRow><MinTileCol>4158</MinTileCol><MaxTileCol>4172</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5851</MaxTileRow><MinTileCol>8317</MinTileCol><MaxTileCol>8345</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11703</MaxTileRow><MinTileCol>16634</MinTileCol><MaxTileCol>16691</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23407</MaxTileRow><MinTileCol>33269</MinTileCol><MaxTileCol>33382</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie CREIL SUD PICARDIE 1979</ows:Title><ows:Abstract>Zone de CREIL SUD PICARDIE 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.25185 49.035</ows:LowerCorner><ows:UpperCorner>3.12757 49.6058</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.CREIL-SUD-PICARDIE1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1036</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1396</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2073</MinTileCol><MaxTileCol>2083</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2792</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4147</MinTileCol><MaxTileCol>4167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5584</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8294</MinTileCol><MaxTileCol>8334</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11168</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16588</MinTileCol><MaxTileCol>16668</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22337</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>33177</MinTileCol><MaxTileCol>33337</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Dijon 1962</ows:Title><ows:Abstract>Zone de Dijon 1962</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.75391 46.9422</ows:LowerCorner><ows:UpperCorner>5.37943 47.5368</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.DIJON1962</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>360</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>527</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>715</MinTileRow><MaxTileRow>720</MaxTileRow><MinTileCol>1051</MinTileCol><MaxTileCol>1054</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1431</MinTileRow><MaxTileRow>1441</MaxTileRow><MinTileCol>2102</MinTileCol><MaxTileCol>2109</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2863</MinTileRow><MaxTileRow>2883</MaxTileRow><MinTileCol>4204</MinTileCol><MaxTileCol>4218</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5726</MinTileRow><MaxTileRow>5766</MaxTileRow><MinTileCol>8408</MinTileCol><MaxTileCol>8436</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11453</MinTileRow><MaxTileRow>11533</MaxTileRow><MinTileCol>16816</MinTileCol><MaxTileCol>16873</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22906</MinTileRow><MaxTileRow>23066</MaxTileRow><MinTileCol>33633</MinTileCol><MaxTileCol>33747</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>180</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie GRENOBLE 1965</ows:Title><ows:Abstract>Zone de Grenoble 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.50453 44.9416</ows:LowerCorner><ows:UpperCorner>6.13005 45.3827</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.GRENOBLE1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>733</MinTileRow><MaxTileRow>737</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1467</MinTileRow><MaxTileRow>1474</MaxTileRow><MinTileCol>2110</MinTileCol><MaxTileCol>2117</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2934</MinTileRow><MaxTileRow>2948</MaxTileRow><MinTileCol>4221</MinTileCol><MaxTileCol>4235</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5869</MinTileRow><MaxTileRow>5897</MaxTileRow><MinTileCol>8442</MinTileCol><MaxTileCol>8470</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11738</MinTileRow><MaxTileRow>11794</MaxTileRow><MinTileCol>16885</MinTileCol><MaxTileCol>16941</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23476</MinTileRow><MaxTileRow>23589</MaxTileRow><MinTileCol>33770</MinTileCol><MaxTileCol>33883</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie GRENOBLE 1976</ows:Title><ows:Abstract>Zone de Grenoble 1976</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.50453 45.0301</ows:LowerCorner><ows:UpperCorner>6.25515 45.4705</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.GRENOBLE1976</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>732</MinTileRow><MaxTileRow>736</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1465</MinTileRow><MaxTileRow>1472</MaxTileRow><MinTileCol>2110</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2931</MinTileRow><MaxTileRow>2945</MaxTileRow><MinTileCol>4221</MinTileCol><MaxTileCol>4238</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5863</MinTileRow><MaxTileRow>5891</MaxTileRow><MinTileCol>8442</MinTileCol><MaxTileCol>8476</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11726</MinTileRow><MaxTileRow>11783</MaxTileRow><MinTileCol>16885</MinTileCol><MaxTileCol>16953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23453</MinTileRow><MaxTileRow>23567</MaxTileRow><MinTileCol>33770</MinTileCol><MaxTileCol>33906</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie GRENOBLE 1991</ows:Title><ows:Abstract>Zone de Grenoble 1991</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.50453 44.9416</ows:LowerCorner><ows:UpperCorner>6.13005 45.3827</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.GRENOBLE1991</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>733</MinTileRow><MaxTileRow>737</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1467</MinTileRow><MaxTileRow>1474</MaxTileRow><MinTileCol>2110</MinTileCol><MaxTileCol>2117</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2934</MinTileRow><MaxTileRow>2948</MaxTileRow><MinTileCol>4221</MinTileCol><MaxTileCol>4235</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5869</MinTileRow><MaxTileRow>5897</MaxTileRow><MinTileCol>8442</MinTileCol><MaxTileCol>8470</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11738</MinTileRow><MaxTileRow>11794</MaxTileRow><MinTileCol>16885</MinTileCol><MaxTileCol>16941</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23476</MinTileRow><MaxTileRow>23589</MaxTileRow><MinTileCol>33770</MinTileCol><MaxTileCol>33883</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Guadeloupe 1955</ows:Title><ows:Abstract>Zone de Guadeloupe 1955</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.6758 16.1695</ows:LowerCorner><ows:UpperCorner>-61.3005 16.6495</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.GUADELOUPE1955</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>463</MinTileRow><MaxTileRow>465</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>337</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>927</MinTileRow><MaxTileRow>930</MaxTileRow><MinTileCol>673</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1855</MinTileRow><MaxTileRow>1861</MaxTileRow><MinTileCol>1346</MinTileCol><MaxTileCol>1350</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3711</MinTileRow><MaxTileRow>3723</MaxTileRow><MinTileCol>2692</MinTileCol><MaxTileCol>2701</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>7423</MinTileRow><MaxTileRow>7446</MaxTileRow><MinTileCol>5385</MinTileCol><MaxTileCol>5402</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>14846</MinTileRow><MaxTileRow>14892</MaxTileRow><MinTileCol>10770</MinTileCol><MaxTileCol>10804</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>29693</MinTileRow><MaxTileRow>29784</MaxTileRow><MinTileCol>21540</MinTileCol><MaxTileCol>21608</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>28</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>21</MinTileCol><MaxTileCol>21</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>57</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>42</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>115</MinTileRow><MaxTileRow>116</MaxTileRow><MinTileCol>84</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>231</MinTileRow><MaxTileRow>232</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Guyane 1958</ows:Title><ows:Abstract>Zone de Guyane 1958</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-54.4198 5.12238</ows:LowerCorner><ows:UpperCorner>-53.419 5.99398</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.GUYANE1958</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>494</MinTileRow><MaxTileRow>497</MaxTileRow><MinTileCol>357</MinTileCol><MaxTileCol>360</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>989</MinTileRow><MaxTileRow>994</MaxTileRow><MinTileCol>714</MinTileCol><MaxTileCol>720</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1979</MinTileRow><MaxTileRow>1989</MaxTileRow><MinTileCol>1428</MinTileCol><MaxTileCol>1440</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3959</MinTileRow><MaxTileRow>3979</MaxTileRow><MinTileCol>2857</MinTileCol><MaxTileCol>2880</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>7918</MinTileRow><MaxTileRow>7958</MaxTileRow><MinTileCol>5715</MinTileCol><MaxTileCol>5760</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>15837</MinTileRow><MaxTileRow>15917</MaxTileRow><MinTileCol>11430</MinTileCol><MaxTileCol>11521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>31674</MinTileRow><MaxTileRow>31834</MaxTileRow><MinTileCol>22861</MinTileCol><MaxTileCol>23043</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>30</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>22</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>61</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>45</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>123</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>89</MinTileCol><MaxTileCol>90</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>247</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>178</MinTileCol><MaxTileCol>180</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LA REUNION 1980</ows:Title><ows:Abstract>Zone de la Réunion 1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.0453 -21.4954</ows:LowerCorner><ows:UpperCorner>55.6708 -20.6783</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LA-REUNION1980</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>668</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1144</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>1337</MinTileCol><MaxTileCol>1340</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2288</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>2674</MinTileCol><MaxTileCol>2681</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4577</MinTileRow><MaxTileRow>4597</MaxTileRow><MinTileCol>5348</MinTileCol><MaxTileCol>5362</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9154</MinTileRow><MaxTileRow>9194</MaxTileRow><MinTileCol>10697</MinTileCol><MaxTileCol>10725</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18308</MinTileRow><MaxTileRow>18388</MaxTileRow><MinTileCol>21394</MinTileCol><MaxTileCol>21451</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36616</MinTileRow><MaxTileRow>36776</MaxTileRow><MinTileCol>42788</MinTileCol><MaxTileCol>42902</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LA ROCHELLE-ROCHEFORT 1959</ows:Title><ows:Abstract>Zone de LA ROCHELLE-ROCHEFORT 1959</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.37613 45.7331</ows:LowerCorner><ows:UpperCorner>-0.750618 46.4273</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LA-ROCHELLE-ROCHEFORT1959</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>362</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>725</MinTileRow><MaxTileRow>730</MaxTileRow><MinTileCol>1016</MinTileCol><MaxTileCol>1019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1450</MinTileRow><MaxTileRow>1461</MaxTileRow><MinTileCol>2032</MinTileCol><MaxTileCol>2039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2900</MinTileRow><MaxTileRow>2923</MaxTileRow><MinTileCol>4064</MinTileCol><MaxTileCol>4078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5800</MinTileRow><MaxTileRow>5846</MaxTileRow><MinTileCol>8129</MinTileCol><MaxTileCol>8157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11601</MinTileRow><MaxTileRow>11692</MaxTileRow><MinTileCol>16258</MinTileCol><MaxTileCol>16315</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23202</MinTileRow><MaxTileRow>23384</MaxTileRow><MinTileCol>32517</MinTileCol><MaxTileCol>32631</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Limoges 1966</ows:Title><ows:Abstract>Zone de Limoges 1966</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.00082 45.6457</ows:LowerCorner><ows:UpperCorner>1.50124 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LIMOGES1966</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>514</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>731</MaxTileRow><MinTileCol>1029</MinTileCol><MaxTileCol>1032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1462</MaxTileRow><MinTileCol>2059</MinTileCol><MaxTileCol>2065</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2925</MaxTileRow><MinTileCol>4118</MinTileCol><MaxTileCol>4130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5851</MaxTileRow><MinTileCol>8237</MinTileCol><MaxTileCol>8260</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11703</MaxTileRow><MinTileCol>16475</MinTileCol><MaxTileCol>16520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23407</MaxTileRow><MinTileCol>32950</MinTileCol><MaxTileCol>33041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LYON 1947</ows:Title><ows:Abstract>Zone de LYON - Cartographie 1947</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.50371 45.4705</ows:LowerCorner><ows:UpperCorner>5.37943 46.0813</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LYON1947</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>363</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>524</MinTileCol><MaxTileCol>527</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>727</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1049</MinTileCol><MaxTileCol>1054</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1455</MinTileRow><MaxTileRow>1465</MaxTileRow><MinTileCol>2099</MinTileCol><MaxTileCol>2109</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2911</MinTileRow><MaxTileRow>2931</MaxTileRow><MinTileCol>4198</MinTileCol><MaxTileCol>4218</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5823</MinTileRow><MaxTileRow>5863</MaxTileRow><MinTileCol>8396</MinTileCol><MaxTileCol>8436</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11646</MinTileRow><MaxTileRow>11726</MaxTileRow><MinTileCol>16793</MinTileCol><MaxTileCol>16873</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23293</MinTileRow><MaxTileRow>23453</MaxTileRow><MinTileCol>33587</MinTileCol><MaxTileCol>33747</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LYON 1980</ows:Title><ows:Abstract>Zone de LYON - Cartographie 1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.62881 45.4705</ows:LowerCorner><ows:UpperCorner>5.12922 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LYON1980</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1465</MaxTileRow><MinTileCol>2100</MinTileCol><MaxTileCol>2106</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2931</MaxTileRow><MinTileCol>4201</MinTileCol><MaxTileCol>4212</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5863</MaxTileRow><MinTileCol>8402</MinTileCol><MaxTileCol>8425</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11726</MaxTileRow><MinTileCol>16805</MinTileCol><MaxTileCol>16850</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23453</MaxTileRow><MinTileCol>33610</MinTileCol><MaxTileCol>33701</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LYON 1985</ows:Title><ows:Abstract>Zone de LYON - Cartographie 1985</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.62881 45.5582</ows:LowerCorner><ows:UpperCorner>5.12922 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.LYON1985</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1464</MaxTileRow><MinTileCol>2100</MinTileCol><MaxTileCol>2106</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2928</MaxTileRow><MinTileCol>4201</MinTileCol><MaxTileCol>4212</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5857</MaxTileRow><MinTileCol>8402</MinTileCol><MaxTileCol>8425</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11715</MaxTileRow><MinTileCol>16805</MinTileCol><MaxTileCol>16850</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23430</MaxTileRow><MinTileCol>33610</MinTileCol><MaxTileCol>33701</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARNE LA VALLÉE 1966</ows:Title><ows:Abstract>Zone de MARNE LA VALLÉE 1966</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.37696 48.7059</ows:LowerCorner><ows:UpperCorner>3.12757 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARNE-LA-VALLEE1966</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1411</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2083</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2823</MaxTileRow><MinTileCol>4150</MinTileCol><MaxTileCol>4167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5646</MaxTileRow><MinTileCol>8300</MinTileCol><MaxTileCol>8334</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11293</MaxTileRow><MinTileCol>16600</MinTileCol><MaxTileCol>16668</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22587</MaxTileRow><MinTileCol>33200</MinTileCol><MaxTileCol>33337</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARNE LA VALLÉE 1978</ows:Title><ows:Abstract>Zone de MARNE LA VALLÉE 1978</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.37696 48.7059</ows:LowerCorner><ows:UpperCorner>2.75227 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARNE-LA-VALLEE1978</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1411</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2823</MaxTileRow><MinTileCol>4150</MinTileCol><MaxTileCol>4158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5646</MaxTileRow><MinTileCol>8300</MinTileCol><MaxTileCol>8317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11293</MaxTileRow><MinTileCol>16600</MinTileCol><MaxTileCol>16634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22587</MaxTileRow><MinTileCol>33200</MinTileCol><MaxTileCol>33269</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARNE LA VALLÉE 1987</ows:Title><ows:Abstract>Zone de MARNE LA VALLÉE 1987</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.37696 48.7059</ows:LowerCorner><ows:UpperCorner>3.12757 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARNE-LA-VALLEE1987</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1411</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2083</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2823</MaxTileRow><MinTileCol>4150</MinTileCol><MaxTileCol>4167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5646</MaxTileRow><MinTileCol>8300</MinTileCol><MaxTileCol>8334</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11293</MaxTileRow><MinTileCol>16600</MinTileCol><MaxTileCol>16668</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22587</MaxTileRow><MinTileCol>33200</MinTileCol><MaxTileCol>33337</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARSEILLE MARTIGUES 1947</ows:Title><ows:Abstract>Zone de MARSEILLE MARTIGUES 1947</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.50371 43.052</ows:LowerCorner><ows:UpperCorner>5.62963 43.6885</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARSEILLE-MARTIGUES1947</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>524</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>747</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1049</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1494</MinTileRow><MaxTileRow>1504</MaxTileRow><MinTileCol>2099</MinTileCol><MaxTileCol>2112</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2988</MinTileRow><MaxTileRow>3008</MaxTileRow><MinTileCol>4198</MinTileCol><MaxTileCol>4224</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5977</MinTileRow><MaxTileRow>6017</MaxTileRow><MinTileCol>8396</MinTileCol><MaxTileCol>8448</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11954</MinTileRow><MaxTileRow>12034</MaxTileRow><MinTileCol>16793</MinTileCol><MaxTileCol>16896</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23908</MinTileRow><MaxTileRow>24068</MaxTileRow><MinTileCol>33587</MinTileCol><MaxTileCol>33792</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARSEILLE MARTIGUES 1980</ows:Title><ows:Abstract>Zone de MARSEILLE MARTIGUES 1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.00412 43.052</ows:LowerCorner><ows:UpperCorner>5.62963 43.5073</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARSEILLE-MARTIGUES1980</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>526</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1052</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1497</MinTileRow><MaxTileRow>1504</MaxTileRow><MinTileCol>2104</MinTileCol><MaxTileCol>2112</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2994</MinTileRow><MaxTileRow>3008</MaxTileRow><MinTileCol>4209</MinTileCol><MaxTileCol>4224</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5988</MinTileRow><MaxTileRow>6017</MaxTileRow><MinTileCol>8419</MinTileCol><MaxTileCol>8448</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11977</MinTileRow><MaxTileRow>12034</MaxTileRow><MinTileCol>16839</MinTileCol><MaxTileCol>16896</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23954</MinTileRow><MaxTileRow>24068</MaxTileRow><MinTileCol>33678</MinTileCol><MaxTileCol>33792</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie MARSEILLE MARTIGUES 1986</ows:Title><ows:Abstract>Zone de MARSEILLE MARTIGUES 1986</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.87902 43.052</ows:LowerCorner><ows:UpperCorner>5.62963 43.6885</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.MARSEILLE-MARTIGUES1986</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>747</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1051</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1494</MinTileRow><MaxTileRow>1504</MaxTileRow><MinTileCol>2103</MinTileCol><MaxTileCol>2112</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2988</MinTileRow><MaxTileRow>3008</MaxTileRow><MinTileCol>4207</MinTileCol><MaxTileCol>4224</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5977</MinTileRow><MaxTileRow>6017</MaxTileRow><MinTileCol>8414</MinTileCol><MaxTileCol>8448</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11954</MinTileRow><MaxTileRow>12034</MaxTileRow><MinTileCol>16828</MinTileCol><MaxTileCol>16896</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23908</MinTileRow><MaxTileRow>24068</MaxTileRow><MinTileCol>33656</MinTileCol><MaxTileCol>33792</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie METZ NANCY 1983</ows:Title><ows:Abstract>Zone de METZ NANCY 1983</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.75474 48.4576</ows:LowerCorner><ows:UpperCorner>6.50535 49.362</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.METZ-NANCY1983</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>354</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>530</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>708</MaxTileRow><MinTileCol>1056</MinTileCol><MaxTileCol>1061</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1400</MinTileRow><MaxTileRow>1416</MaxTileRow><MinTileCol>2113</MinTileCol><MaxTileCol>2122</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2800</MinTileRow><MaxTileRow>2832</MaxTileRow><MinTileCol>4226</MinTileCol><MaxTileCol>4244</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5601</MinTileRow><MaxTileRow>5664</MaxTileRow><MinTileCol>8453</MinTileCol><MaxTileCol>8488</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11202</MinTileRow><MaxTileRow>11328</MaxTileRow><MinTileCol>16907</MinTileCol><MaxTileCol>16976</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22405</MinTileRow><MaxTileRow>22656</MaxTileRow><MinTileCol>33815</MinTileCol><MaxTileCol>33952</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie NANTES 1972</ows:Title><ows:Abstract>Zone de NANTES 1972</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2.50206 47.0276</ows:LowerCorner><ows:UpperCorner>-1.25103 47.4522</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.NANTES1972</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>358</MinTileRow><MaxTileRow>360</MaxTileRow><MinTileCol>504</MinTileCol><MaxTileCol>508</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>716</MinTileRow><MaxTileRow>720</MaxTileRow><MinTileCol>1009</MinTileCol><MaxTileCol>1016</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1433</MinTileRow><MaxTileRow>1440</MaxTileRow><MinTileCol>2019</MinTileCol><MaxTileCol>2033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2866</MinTileRow><MaxTileRow>2880</MaxTileRow><MinTileCol>4039</MinTileCol><MaxTileCol>4067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5732</MinTileRow><MaxTileRow>5760</MaxTileRow><MinTileCol>8078</MinTileCol><MaxTileCol>8135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11464</MinTileRow><MaxTileRow>11521</MaxTileRow><MinTileCol>16156</MinTileCol><MaxTileCol>16270</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22929</MinTileRow><MaxTileRow>23043</MaxTileRow><MinTileCol>32312</MinTileCol><MaxTileCol>32540</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>179</MinTileRow><MaxTileRow>180</MaxTileRow><MinTileCol>252</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie 1964</ows:Title><ows:Abstract>Zone de Paris - Cartographie 1964</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.00165 48.7884</ows:LowerCorner><ows:UpperCorner>2.50206 49.117</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.PARIS1964</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>702</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1035</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1404</MinTileRow><MaxTileRow>1410</MaxTileRow><MinTileCol>2070</MinTileCol><MaxTileCol>2076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2809</MinTileRow><MaxTileRow>2820</MaxTileRow><MinTileCol>4141</MinTileCol><MaxTileCol>4152</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5618</MinTileRow><MaxTileRow>5641</MaxTileRow><MinTileCol>8283</MinTileCol><MaxTileCol>8305</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11237</MinTileRow><MaxTileRow>11282</MaxTileRow><MinTileCol>16566</MinTileCol><MaxTileCol>16611</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22474</MinTileRow><MaxTileRow>22565</MaxTileRow><MinTileCol>33132</MinTileCol><MaxTileCol>33223</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie 1979</ows:Title><ows:Abstract>Zone de Paris - Cartographie 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.00165 48.7059</ows:LowerCorner><ows:UpperCorner>2.62716 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.PARIS1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1035</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1411</MaxTileRow><MinTileCol>2070</MinTileCol><MaxTileCol>2077</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2823</MaxTileRow><MinTileCol>4141</MinTileCol><MaxTileCol>4155</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5646</MaxTileRow><MinTileCol>8283</MinTileCol><MaxTileCol>8311</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11293</MaxTileRow><MinTileCol>16566</MinTileCol><MaxTileCol>16623</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22587</MaxTileRow><MinTileCol>33132</MinTileCol><MaxTileCol>33246</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie REIMS 1974</ows:Title><ows:Abstract>Zone de REIMS 1974</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.75309 49.035</ows:LowerCorner><ows:UpperCorner>4.50371 49.4434</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.REIMS1974</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>522</MinTileCol><MaxTileCol>524</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>699</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1045</MinTileCol><MaxTileCol>1049</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1398</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2090</MinTileCol><MaxTileCol>2099</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2797</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4181</MinTileCol><MaxTileCol>4198</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5595</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8362</MinTileCol><MaxTileCol>8396</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11191</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16725</MinTileCol><MaxTileCol>16793</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22382</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>33451</MinTileCol><MaxTileCol>33587</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>261</MinTileCol><MaxTileCol>262</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie REIMS 1983</ows:Title><ows:Abstract>Zone de REIMS 1983</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.75309 49.035</ows:LowerCorner><ows:UpperCorner>4.50371 49.6058</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.REIMS1983</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>522</MinTileCol><MaxTileCol>524</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1045</MinTileCol><MaxTileCol>1049</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1396</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2090</MinTileCol><MaxTileCol>2099</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2792</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4181</MinTileCol><MaxTileCol>4198</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5584</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8362</MinTileCol><MaxTileCol>8396</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11168</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16725</MinTileCol><MaxTileCol>16793</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22337</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>33451</MinTileCol><MaxTileCol>33587</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>261</MinTileCol><MaxTileCol>262</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Roissy 1973</ows:Title><ows:Abstract>Zone de Roissy - Cartographie 1973</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.25185 48.8707</ows:LowerCorner><ows:UpperCorner>2.75227 49.117</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.ROISSY1973</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>702</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1036</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1404</MinTileRow><MaxTileRow>1408</MaxTileRow><MinTileCol>2073</MinTileCol><MaxTileCol>2079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2809</MinTileRow><MaxTileRow>2817</MaxTileRow><MinTileCol>4147</MinTileCol><MaxTileCol>4158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5618</MinTileRow><MaxTileRow>5635</MaxTileRow><MinTileCol>8294</MinTileCol><MaxTileCol>8317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11237</MinTileRow><MaxTileRow>11271</MaxTileRow><MinTileCol>16588</MinTileCol><MaxTileCol>16634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22474</MinTileRow><MaxTileRow>22542</MaxTileRow><MinTileCol>33177</MinTileCol><MaxTileCol>33269</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Roissy 1978</ows:Title><ows:Abstract>Zone de Roissy - Cartographie 1978</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.25185 48.8707</ows:LowerCorner><ows:UpperCorner>2.75227 49.2805</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.ROISSY1978</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1036</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1401</MinTileRow><MaxTileRow>1408</MaxTileRow><MinTileCol>2073</MinTileCol><MaxTileCol>2079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2803</MinTileRow><MaxTileRow>2817</MaxTileRow><MinTileCol>4147</MinTileCol><MaxTileCol>4158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5607</MinTileRow><MaxTileRow>5635</MaxTileRow><MinTileCol>8294</MinTileCol><MaxTileCol>8317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11214</MinTileRow><MaxTileRow>11271</MaxTileRow><MinTileCol>16588</MinTileCol><MaxTileCol>16634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22428</MinTileRow><MaxTileRow>22542</MaxTileRow><MinTileCol>33177</MinTileCol><MaxTileCol>33269</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie STRASBOURG 1956</ows:Title><ows:Abstract>Zone de STRASBOURG 1956</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>7.25597 48.2081</ows:LowerCorner><ows:UpperCorner>8.13169 48.7884</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.STRASBOURG1956</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>352</MinTileRow><MaxTileRow>355</MaxTileRow><MinTileCol>532</MinTileCol><MaxTileCol>535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>705</MinTileRow><MaxTileRow>710</MaxTileRow><MinTileCol>1065</MinTileCol><MaxTileCol>1070</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1410</MinTileRow><MaxTileRow>1420</MaxTileRow><MinTileCol>2130</MinTileCol><MaxTileCol>2140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2820</MinTileRow><MaxTileRow>2840</MaxTileRow><MinTileCol>4261</MinTileCol><MaxTileCol>4281</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5641</MinTileRow><MaxTileRow>5681</MaxTileRow><MinTileCol>8522</MinTileCol><MaxTileCol>8562</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11282</MinTileRow><MaxTileRow>11362</MaxTileRow><MinTileCol>17044</MinTileCol><MaxTileCol>17124</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22565</MinTileRow><MaxTileRow>22724</MaxTileRow><MinTileCol>34088</MinTileCol><MaxTileCol>34248</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>133</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>266</MinTileCol><MaxTileCol>267</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie STRASBOURG 1978</ows:Title><ows:Abstract>Zone de STRASBOURG 1978</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>7.25597 48.1246</ows:LowerCorner><ows:UpperCorner>8.00659 48.8707</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.STRASBOURG1978</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>352</MinTileRow><MaxTileRow>355</MaxTileRow><MinTileCol>532</MinTileCol><MaxTileCol>534</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>704</MinTileRow><MaxTileRow>710</MaxTileRow><MinTileCol>1065</MinTileCol><MaxTileCol>1069</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1421</MaxTileRow><MinTileCol>2130</MinTileCol><MaxTileCol>2139</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2817</MinTileRow><MaxTileRow>2843</MaxTileRow><MinTileCol>4261</MinTileCol><MaxTileCol>4278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5635</MinTileRow><MaxTileRow>5686</MaxTileRow><MinTileCol>8522</MinTileCol><MaxTileCol>8556</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11271</MinTileRow><MaxTileRow>11373</MaxTileRow><MinTileCol>17044</MinTileCol><MaxTileCol>17112</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22542</MinTileRow><MaxTileRow>22747</MaxTileRow><MinTileCol>34088</MinTileCol><MaxTileCol>34225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>133</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>266</MinTileCol><MaxTileCol>267</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie TOULON HYERES 1976</ows:Title><ows:Abstract>Zone de TOULON HYERES 1976</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.62963 42.8689</ows:LowerCorner><ows:UpperCorner>6.38025 43.2345</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.TOULON-HYERES1976</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>375</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>530</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>750</MinTileRow><MaxTileRow>753</MaxTileRow><MinTileCol>1056</MinTileCol><MaxTileCol>1060</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1501</MinTileRow><MaxTileRow>1507</MaxTileRow><MinTileCol>2112</MinTileCol><MaxTileCol>2120</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3002</MinTileRow><MaxTileRow>3014</MaxTileRow><MinTileCol>4224</MinTileCol><MaxTileCol>4241</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6005</MinTileRow><MaxTileRow>6028</MaxTileRow><MinTileCol>8448</MinTileCol><MaxTileCol>8482</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12011</MinTileRow><MaxTileRow>12056</MaxTileRow><MinTileCol>16896</MinTileCol><MaxTileCol>16964</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24022</MinTileRow><MaxTileRow>24113</MaxTileRow><MinTileCol>33792</MinTileCol><MaxTileCol>33929</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie TOULOUSE 1948</ows:Title><ows:Abstract>Zone de TOULOUSE 1948</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.12593 43.4165</ows:LowerCorner><ows:UpperCorner>1.75144 43.7789</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.TOULOUSE1948</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>374</MaxTileRow><MinTileCol>515</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>746</MinTileRow><MaxTileRow>749</MaxTileRow><MinTileCol>1030</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1492</MinTileRow><MaxTileRow>1498</MaxTileRow><MinTileCol>2060</MinTileCol><MaxTileCol>2067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2985</MinTileRow><MaxTileRow>2997</MaxTileRow><MinTileCol>4121</MinTileCol><MaxTileCol>4135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5971</MinTileRow><MaxTileRow>5994</MaxTileRow><MinTileCol>8243</MinTileCol><MaxTileCol>8271</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11943</MinTileRow><MaxTileRow>11988</MaxTileRow><MinTileCol>16486</MinTileCol><MaxTileCol>16543</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23886</MinTileRow><MaxTileRow>23977</MaxTileRow><MinTileCol>32972</MinTileCol><MaxTileCol>33086</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Vannes Golf du Morbihan 1960</ows:Title><ows:Abstract>Zone de Vannes Golf du Morbihan - Cartographie 1960</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.37778 47.3676</ows:LowerCorner><ows:UpperCorner>-2.50206 47.8735</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.VANNES-GOLF-DU-MORBIHAN1960</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>504</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>712</MinTileRow><MaxTileRow>717</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1425</MinTileRow><MaxTileRow>1434</MaxTileRow><MinTileCol>2009</MinTileCol><MaxTileCol>2019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2851</MinTileRow><MaxTileRow>2869</MaxTileRow><MinTileCol>4019</MinTileCol><MaxTileCol>4039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5703</MinTileRow><MaxTileRow>5738</MaxTileRow><MinTileCol>8038</MinTileCol><MaxTileCol>8078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11407</MinTileRow><MaxTileRow>11476</MaxTileRow><MinTileCol>16076</MinTileCol><MaxTileCol>16156</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22815</MinTileRow><MaxTileRow>22952</MaxTileRow><MinTileCol>32153</MinTileCol><MaxTileCol>32312</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>125</MinTileCol><MaxTileCol>126</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>251</MinTileCol><MaxTileCol>252</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Vannes Golf du Morbihan 1971</ows:Title><ows:Abstract>Zone de Vannes Golf du Morbihan - Cartographie 1971</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.37778 47.3676</ows:LowerCorner><ows:UpperCorner>-2.50206 47.8735</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.VANNES-GOLF-DU-MORBIHAN1971</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>504</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>712</MinTileRow><MaxTileRow>717</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1425</MinTileRow><MaxTileRow>1434</MaxTileRow><MinTileCol>2009</MinTileCol><MaxTileCol>2019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2851</MinTileRow><MaxTileRow>2869</MaxTileRow><MinTileCol>4019</MinTileCol><MaxTileCol>4039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5703</MinTileRow><MaxTileRow>5738</MaxTileRow><MinTileCol>8038</MinTileCol><MaxTileCol>8078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11407</MinTileRow><MaxTileRow>11476</MaxTileRow><MinTileCol>16076</MinTileCol><MaxTileCol>16156</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22815</MinTileRow><MaxTileRow>22952</MaxTileRow><MinTileCol>32153</MinTileCol><MaxTileCol>32312</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>125</MinTileCol><MaxTileCol>126</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>251</MinTileCol><MaxTileCol>252</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Versailles 1979</ows:Title><ows:Abstract>Zone de Versailles - Cartographie 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.87654 48.5405</ows:LowerCorner><ows:UpperCorner>2.50206 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.EDUGEO.VERSAILLES1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1034</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1414</MaxTileRow><MinTileCol>2069</MinTileCol><MaxTileCol>2076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2829</MaxTileRow><MinTileCol>4138</MinTileCol><MaxTileCol>4152</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5658</MaxTileRow><MinTileCol>8277</MinTileCol><MaxTileCol>8305</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11316</MaxTileRow><MinTileCol>16554</MinTileCol><MaxTileCol>16611</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22633</MaxTileRow><MinTileCol>33109</MinTileCol><MaxTileCol>33223</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de l&apos;état-major - environs de Paris (1818 - 1824)</ows:Title><ows:Abstract>Carte des environs de Paris au 1 : 10 000 établie entre 1818 et 1824.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.82682 48.3847</ows:LowerCorner><ows:UpperCorner>2.79738 49.5142</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.ETATMAJOR10</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>354</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>708</MaxTileRow><MinTileCol>1034</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1397</MinTileRow><MaxTileRow>1417</MaxTileRow><MinTileCol>2068</MinTileCol><MaxTileCol>2079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2795</MinTileRow><MaxTileRow>2834</MaxTileRow><MinTileCol>4137</MinTileCol><MaxTileCol>4159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5590</MinTileRow><MaxTileRow>5669</MaxTileRow><MinTileCol>8275</MinTileCol><MaxTileCol>8319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11181</MinTileRow><MaxTileRow>11338</MaxTileRow><MinTileCol>16550</MinTileCol><MaxTileCol>16638</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22363</MinTileRow><MaxTileRow>22676</MaxTileRow><MinTileCol>33100</MinTileCol><MaxTileCol>33277</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de l&apos;état-major (1820-1866)</ows:Title><ows:Abstract>Carte française en couleurs du XIXè siècle en couleurs superposable aux cartes et données modernes.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.08889 41.1844</ows:LowerCorner><ows:UpperCorner>10.961 51.2745</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.ETATMAJOR40</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>495</MinTileCol><MaxTileCol>540</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>991</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1531</MaxTileRow><MinTileCol>1983</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3063</MaxTileRow><MinTileCol>3966</MinTileCol><MaxTileCol>4321</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6127</MaxTileRow><MinTileCol>7933</MinTileCol><MaxTileCol>8642</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12255</MaxTileRow><MinTileCol>15866</MinTileCol><MaxTileCol>17285</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte France Raster ®</ows:Title><ows:Abstract>Carte à grande échelle spécialement adaptée à l&apos;affichage de données urbaines.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.7576 -21.5655</ows:LowerCorner><ows:UpperCorner>56.0405 51.0019</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.FRANCERASTER</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2299</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4598</MaxTileRow><MinTileCol>2644</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5465</MinTileRow><MaxTileRow>9197</MaxTileRow><MinTileCol>5311</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10931</MinTileRow><MaxTileRow>18394</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36771</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43804</MinTileRow><MaxTileRow>64779</MaxTileRow><MinTileCol>42529</MinTileCol><MaxTileCol>69075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87609</MinTileRow><MaxTileRow>147051</MaxTileRow><MinTileCol>85058</MinTileCol><MaxTileCol>171737</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>34</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>68</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Le Havre 1975</ows:Title><ows:Abstract>Zone du Havre 1975</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-0.125103 49.2805</ows:LowerCorner><ows:UpperCorner>0.500412 49.6868</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.LE-HAVRE1975</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>348</MinTileRow><MaxTileRow>350</MaxTileRow><MinTileCol>511</MinTileCol><MaxTileCol>513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>697</MinTileRow><MaxTileRow>700</MaxTileRow><MinTileCol>1023</MinTileCol><MaxTileCol>1026</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1394</MinTileRow><MaxTileRow>1401</MaxTileRow><MinTileCol>2046</MinTileCol><MaxTileCol>2053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2789</MinTileRow><MaxTileRow>2803</MaxTileRow><MinTileCol>4093</MinTileCol><MaxTileCol>4107</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5578</MinTileRow><MaxTileRow>5607</MaxTileRow><MinTileCol>8186</MinTileCol><MaxTileCol>8214</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11157</MinTileRow><MaxTileRow>11214</MaxTileRow><MinTileCol>16372</MinTileCol><MaxTileCol>16429</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22314</MinTileRow><MaxTileRow>22428</MaxTileRow><MinTileCol>32745</MinTileCol><MaxTileCol>32859</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>255</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie Le Havre 1979</ows:Title><ows:Abstract>Zone du Havre 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-0.125103 49.2805</ows:LowerCorner><ows:UpperCorner>0.625515 49.6868</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.LE-HAVRE1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>348</MinTileRow><MaxTileRow>350</MaxTileRow><MinTileCol>511</MinTileCol><MaxTileCol>513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>697</MinTileRow><MaxTileRow>700</MaxTileRow><MinTileCol>1023</MinTileCol><MaxTileCol>1027</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1394</MinTileRow><MaxTileRow>1401</MaxTileRow><MinTileCol>2046</MinTileCol><MaxTileCol>2055</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2789</MinTileRow><MaxTileRow>2803</MaxTileRow><MinTileCol>4093</MinTileCol><MaxTileCol>4110</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5578</MinTileRow><MaxTileRow>5607</MaxTileRow><MinTileCol>8186</MinTileCol><MaxTileCol>8220</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11157</MinTileRow><MaxTileRow>11214</MaxTileRow><MinTileCol>16372</MinTileCol><MaxTileCol>16440</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22314</MinTileRow><MaxTileRow>22428</MaxTileRow><MinTileCol>32745</MinTileCol><MaxTileCol>32881</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>255</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartes IGN</ows:Title><ows:Abstract>Cartes IGN</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -67.7557</ows:LowerCorner><ows:UpperCorner>180 85.0511</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.MAPS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>777</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>990</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1981</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>3088</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>3963</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>6177</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>7926</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>12355</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>15851</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>20215</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>31702</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>40431</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>46895</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43696</MinTileRow><MaxTileRow>73552</MaxTileRow><MinTileCol>42447</MinTileCol><MaxTileCol>85903</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87392</MinTileRow><MaxTileRow>147104</MaxTileRow><MinTileCol>84912</MinTileCol><MaxTileCol>171807</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>97</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>194</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>388</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>495</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Mondiale pour la mini-vue</ows:Title><ows:Abstract>Carte Mondiale pour la mini-vue</ows:Abstract><ows:Keywords><ows:Keyword>Mini-Vue</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-179.5 -75</ows:LowerCorner><ows:UpperCorner>179.5 75</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.MAPS.OVERVIEW</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>13</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>26</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>52</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>105</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>210</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>PYRAMIDE SCAN EXPRESS CLASSIQUE</ows:Title><ows:Abstract>Cartographie topographique multi-échelles du territoire français issue des bases de données vecteur de l’IGN –réalisée selon un processus cartographique innovant fortement automatisé.test</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-22.5 39.8841</ows:LowerCorner><ows:UpperCorner>22.5 55.7766</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.CLASSIQUE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>332</MinTileRow><MaxTileRow>386</MaxTileRow><MinTileCol>478</MinTileCol><MaxTileCol>546</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>772</MaxTileRow><MinTileCol>986</MinTileCol><MaxTileCol>1090</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>1972</MinTileCol><MaxTileCol>2180</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2723</MinTileRow><MaxTileRow>3079</MaxTileRow><MinTileCol>3955</MinTileCol><MaxTileCol>4332</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7951</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15903</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21863</MinTileRow><MaxTileRow>24530</MaxTileRow><MinTileCol>31753</MinTileCol><MaxTileCol>34575</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43776</MinTileRow><MaxTileRow>49024</MaxTileRow><MinTileCol>63552</MinTileCol><MaxTileCol>69103</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>20</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>40</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>56</MinTileCol><MaxTileCol>71</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>112</MinTileCol><MaxTileCol>143</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>166</MinTileRow><MaxTileRow>193</MaxTileRow><MinTileCol>239</MinTileCol><MaxTileCol>273</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>PYRAMIDE SCAN EXPRESS STANDARD</ows:Title><ows:Abstract>Cartographie topographique multi-échelles du territoire français issue des bases de données vecteur de l’IGN –réalisée selon un processus cartographique innovant fortement automatisé.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-22.5 39.8841</ows:LowerCorner><ows:UpperCorner>22.5 55.7766</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-EXPRESS.STANDARD</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>332</MinTileRow><MaxTileRow>386</MaxTileRow><MinTileCol>478</MinTileCol><MaxTileCol>546</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>772</MaxTileRow><MinTileCol>986</MinTileCol><MaxTileCol>1090</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>1972</MinTileCol><MaxTileCol>2180</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2723</MinTileRow><MaxTileRow>3079</MaxTileRow><MinTileCol>3955</MinTileCol><MaxTileCol>4332</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7951</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15903</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21863</MinTileRow><MaxTileRow>24530</MaxTileRow><MinTileCol>31753</MinTileCol><MaxTileCol>34575</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43776</MinTileRow><MaxTileRow>49024</MaxTileRow><MinTileCol>63552</MinTileCol><MaxTileCol>69103</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>20</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>40</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>56</MinTileCol><MaxTileCol>71</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>112</MinTileCol><MaxTileCol>143</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>166</MinTileRow><MaxTileRow>193</MaxTileRow><MinTileCol>239</MinTileCol><MaxTileCol>273</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>GEOGRAPHICALNAMES.NAMES</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43808</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87632</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Hydrographie</ows:Title><ows:Abstract>Affichage des lacs, réservoirs, rivières et cours d&apos;eau en France.</ows:Abstract><ows:Keywords><ows:Keyword>Hydrographie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>HYDROGRAPHY.HYDROGRAPHY</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>339</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1364</MinTileRow><MaxTileRow>2300</MaxTileRow><MinTileCol>1324</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5320</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42944</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87664</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Hydrographie</ows:Title><ows:Abstract>Affichage des lacs, réservoirs, rivières et cours d&apos;eau en France.</ows:Abstract><ows:Keywords><ows:Keyword>Hydrographie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>HYDRO_SYMBO_PYR-PNG_WLD_WM_20120629</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>494</MinTileRow><MaxTileRow>506</MaxTileRow><MinTileCol>356</MinTileCol><MaxTileCol>366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5477</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21900</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171744</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Hydrographie</ows:Title><ows:Abstract>Affichage des lacs, réservoirs, rivières et cours d&apos;eau en France.</ows:Abstract><ows:Keywords><ows:Keyword>Hydrographie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>HYDRO_SYMBO_PYR-PNG_WLD_WM_20130319</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>494</MinTileRow><MaxTileRow>506</MaxTileRow><MinTileCol>356</MinTileCol><MaxTileCol>366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5477</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42944</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87664</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Hydrographie</ows:Title><ows:Abstract>Affichage des lacs, réservoirs, rivières et cours d&apos;eau en France.</ows:Abstract><ows:Keywords><ows:Keyword>Hydrographie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>HYDRO_SYMBO_PYR-PNG_WLD_WM_20130320</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>339</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1364</MinTileRow><MaxTileRow>2300</MaxTileRow><MinTileCol>1324</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5320</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42944</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87664</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Berck St-Quentin 1934</ows:Title><ows:Abstract>Photographie aérienne de Berck St-Quentin en 1934 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.51075 50.251</ows:LowerCorner><ows:UpperCorner>1.66952 50.4143</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>IFREMER-BERK_PYR-PNG_FXX_1934-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>345</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>690</MinTileRow><MaxTileRow>692</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1381</MinTileRow><MaxTileRow>1384</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2763</MinTileRow><MaxTileRow>2769</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5527</MinTileRow><MaxTileRow>5538</MaxTileRow><MinTileCol>8260</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11054</MinTileRow><MaxTileRow>11077</MaxTileRow><MinTileCol>16521</MinTileCol><MaxTileCol>16535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22108</MinTileRow><MaxTileRow>22154</MaxTileRow><MinTileCol>33043</MinTileCol><MaxTileCol>33071</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44217</MinTileRow><MaxTileRow>44309</MaxTileRow><MinTileCol>66086</MinTileCol><MaxTileCol>66142</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88434</MinTileRow><MaxTileRow>88619</MaxTileRow><MinTileCol>132173</MinTileCol><MaxTileCol>132285</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Groix 1938</ows:Title><ows:Abstract>Photographies aériennes de Groix en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.52232 47.6057</ows:LowerCorner><ows:UpperCorner>-3.39634 47.6661</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>IFREMER-GROIX_PYR-PNG_FXX_1938-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>715</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1429</MinTileRow><MaxTileRow>1430</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2859</MinTileRow><MaxTileRow>2860</MaxTileRow><MinTileCol>4015</MinTileCol><MaxTileCol>4018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5718</MinTileRow><MaxTileRow>5721</MaxTileRow><MinTileCol>8031</MinTileCol><MaxTileCol>8037</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11436</MinTileRow><MaxTileRow>11443</MaxTileRow><MinTileCol>16063</MinTileCol><MaxTileCol>16074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22872</MinTileRow><MaxTileRow>22887</MaxTileRow><MinTileCol>32126</MinTileCol><MaxTileCol>32149</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45745</MinTileRow><MaxTileRow>45775</MaxTileRow><MinTileCol>64253</MinTileCol><MaxTileCol>64299</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91491</MinTileRow><MaxTileRow>91550</MaxTileRow><MinTileCol>128506</MinTileCol><MaxTileCol>128598</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Abords de Lorient</ows:Title><ows:Abstract>Photographie aérienne des abords de Lorient en 1924 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.56436 47.6222</ows:LowerCorner><ows:UpperCorner>-3.25248 47.7997</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>IFREMER-QUIBERON_PYR-PNG_FXX_1924-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>715</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1005</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1430</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2010</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2854</MinTileRow><MaxTileRow>2860</MaxTileRow><MinTileCol>4014</MinTileCol><MaxTileCol>4021</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5709</MinTileRow><MaxTileRow>5720</MaxTileRow><MinTileCol>8029</MinTileCol><MaxTileCol>8043</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11419</MinTileRow><MaxTileRow>11441</MaxTileRow><MinTileCol>16059</MinTileCol><MaxTileCol>16087</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22839</MinTileRow><MaxTileRow>22883</MaxTileRow><MinTileCol>32119</MinTileCol><MaxTileCol>32175</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45678</MinTileRow><MaxTileRow>45766</MaxTileRow><MinTileCol>64238</MinTileCol><MaxTileCol>64350</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91357</MinTileRow><MaxTileRow>91532</MaxTileRow><MinTileCol>128477</MinTileCol><MaxTileCol>128700</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Somme 1935</ows:Title><ows:Abstract>Photographie aérienne de la Baie de Somme en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.48669 50.1699</ows:LowerCorner><ows:UpperCorner>1.65766 50.2795</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>IFREMER-SOMME_PYR-PNG_FXX_WM_1935-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>346</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>692</MinTileRow><MaxTileRow>693</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1384</MinTileRow><MaxTileRow>1386</MaxTileRow><MinTileCol>2064</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2768</MinTileRow><MaxTileRow>2772</MaxTileRow><MinTileCol>4129</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5536</MinTileRow><MaxTileRow>5544</MaxTileRow><MinTileCol>8259</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11073</MinTileRow><MaxTileRow>11088</MaxTileRow><MinTileCol>16519</MinTileCol><MaxTileCol>16534</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22146</MinTileRow><MaxTileRow>22177</MaxTileRow><MinTileCol>33038</MinTileCol><MaxTileCol>33069</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44293</MinTileRow><MaxTileRow>44355</MaxTileRow><MinTileCol>66077</MinTileCol><MaxTileCol>66139</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88587</MinTileRow><MaxTileRow>88711</MaxTileRow><MinTileCol>132155</MinTileCol><MaxTileCol>132278</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Corine Land Cover 2006</ows:Title><ows:Abstract>Visualisation de l’occupation du sol en France métropolitaine. Outil de référence pour mesurer les impacts environnementaux.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.CORINELANDCOVER</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>41</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>68</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>83</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>169</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Régions forestières nationales</ows:Title><ows:Abstract>Cette cartographie représente des zones homogènes du point de vue des types de forêt ou de paysage sur la base de conditions physiques dominantes (climat, sol, relief).</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.FORESTAREAS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>15915</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>24484</MaxTileRow><MinTileCol>31830</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Forêt</ows:Title><ows:Abstract>Représentation simplifiée des forêts.</ows:Abstract><ows:Keywords><ows:Keyword>Forêt</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.FORESTCOVER</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>18383</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36760</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte forestière v1 (1987-2004)</ows:Title><ows:Abstract>La cartographie forestière est une base de données cartographique des formations végétales forestières et naturelles. Elle couvre l&apos;ensemble du territoire de France métropolitaine. Elle est réalisée par interprétation de photographies aériennes infrarouges et affinée par des contrôles sur le terrain (le seuil minimal de surface cartographiée est de 2,25 ha).</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.FORESTINVENTORY.V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte forestière (v2 : 2006-)</ows:Title><ows:Abstract>Localisation de 32 types de formations végétales sur le territoire. Carte en cours de réalisation depuis 2006.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.FORESTINVENTORY.V2</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sylvoécorégions</ows:Title><ows:Abstract>Découpage de la France métropolitaine prenant en compte les facteurs biogéographiques déterminant la production forestière et la répartition des grands types d’habitat forestier.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.SYLVOECOREGIONS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>384</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>672</MinTileRow><MaxTileRow>768</MaxTileRow><MinTileCol>992</MinTileCol><MaxTileCol>1088</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1359</MinTileRow><MaxTileRow>1536</MaxTileRow><MinTileCol>1984</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3072</MaxTileRow><MinTileCol>3968</MinTileCol><MaxTileCol>4320</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6128</MaxTileRow><MinTileCol>7952</MinTileCol><MaxTileCol>8640</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>12255</MaxTileRow><MinTileCol>15904</MinTileCol><MaxTileCol>17264</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24496</MaxTileRow><MinTileCol>31824</MinTileCol><MaxTileCol>34512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>48</MinTileCol><MaxTileCol>79</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>111</MinTileCol><MaxTileCol>144</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>160</MinTileRow><MaxTileRow>192</MaxTileRow><MinTileCol>240</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sylvoécorégions d&apos;alluvions récentes</ows:Title><ows:Abstract>Affichage des cinq sylvoécorégions d’alluvions récentes correspondant aux cinq vallées des bassins des grands fleuves français et de leurs affluents, à enjeux importants.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDCOVER.SYLVOECOREGIONS.ALLUVIUM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3986</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7973</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10958</MinTileRow><MaxTileRow>12238</MaxTileRow><MinTileCol>15955</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21916</MinTileRow><MaxTileRow>24468</MaxTileRow><MinTileCol>31918</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2007</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2007.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDUSE.AGRICULTURE2007</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2008</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2008.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDUSE.AGRICULTURE2008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2009</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2009.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDUSE.AGRICULTURE2009</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2010</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de culture déclarées par les exploitants en 2010.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LANDUSE.AGRICULTURE2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LIMADM_SYMBO_PYR-PNG_WLD_20120620</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2725</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LIMADM_SYMBO_PYR-PNG_WLD_20120704</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>677</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1346</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2725</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21900</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87650</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171744</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LIMADM_SYMBO_PYR-PNG_WLD_20130204</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>677</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1346</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2725</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5467</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LIMADM_SYMBO_PYR-PNG_WLD_20130205</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1344</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4608</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5376</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9200</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Limites administratives</ows:Title><ows:Abstract>Représentation des limites administratives (régions, départements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Unités administratives</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LIMADM_SYMBO_PYR-PNG_WLD_20130219</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1344</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4608</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5376</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9200</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>26</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>49</MinTileRow><MaxTileRow>114</MaxTileRow><MinTileCol>108</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>MNT LITTO3D</ows:Title><ows:Abstract>MNT LITTO3D au pas de 1M</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.45 42.95</ows:LowerCorner><ows:UpperCorner>6.3 43.3</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LITTO3D_PYR-RAW_WLD_WGS84G_2012-06-25</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/x-bil;bits=32</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>265</MinTileRow><MaxTileRow>267</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>531</MinTileRow><MaxTileRow>535</MaxTileRow><MinTileCol>2110</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1062</MinTileRow><MaxTileRow>1070</MaxTileRow><MinTileCol>4220</MinTileCol><MaxTileCol>4239</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2125</MinTileRow><MaxTileRow>2141</MaxTileRow><MinTileCol>8440</MinTileCol><MaxTileCol>8478</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>4250</MinTileRow><MaxTileRow>4282</MaxTileRow><MinTileCol>16880</MinTileCol><MaxTileCol>16957</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>8501</MinTileRow><MaxTileRow>8565</MaxTileRow><MinTileCol>33760</MinTileCol><MaxTileCol>33914</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>4</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>8</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>4</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>33</MinTileRow><MaxTileRow>33</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>66</MinTileRow><MaxTileRow>66</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>132</MinTileRow><MaxTileRow>133</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Crues du bassin de la Seine - PHEC</ows:Title><ows:Abstract>Carte des plus hautes eaux connues (PHEC) sur le bassin de la Seine : crue de la seine et de ses affluents en 1910, ainsi que d&apos;autres crues à des dates postérieures sur d&apos;autres cours d&apos;eau.</ows:Abstract><ows:Keywords><ows:Keyword>Zone à risques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.419457 47.4163</ows:LowerCorner><ows:UpperCorner>5.43248 50.064</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LSEP-CRUE_PYR-PNG_WLD_WM_20120904</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>513</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>692</MinTileRow><MaxTileRow>716</MaxTileRow><MinTileCol>1026</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1385</MinTileRow><MaxTileRow>1433</MaxTileRow><MinTileCol>2052</MinTileCol><MaxTileCol>2116</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2771</MinTileRow><MaxTileRow>2867</MaxTileRow><MinTileCol>4105</MinTileCol><MaxTileCol>4225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5550</MinTileRow><MaxTileRow>5734</MaxTileRow><MinTileCol>8211</MinTileCol><MaxTileCol>8443</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11101</MinTileRow><MaxTileRow>11469</MaxTileRow><MinTileCol>16422</MinTileCol><MaxTileCol>16886</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22203</MinTileRow><MaxTileRow>22939</MaxTileRow><MinTileCol>32844</MinTileCol><MaxTileCol>33764</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>14</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>81</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Crues du bassin de la Seine - PHEC</ows:Title><ows:Abstract>Carte des plus hautes eaux connues (PHEC) sur le bassin de la Seine : crue de la seine et de ses affluents en 1910, ainsi que d&apos;autres crues à des dates postérieures sur d&apos;autres cours d&apos;eau.</ows:Abstract><ows:Keywords><ows:Keyword>Zone à risques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.419457 47.4163</ows:LowerCorner><ows:UpperCorner>5.43248 50.064</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LSEP-CRUE_PYR-PNG_WLD_WM_20121009</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>513</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>692</MinTileRow><MaxTileRow>716</MaxTileRow><MinTileCol>1026</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1385</MinTileRow><MaxTileRow>1433</MaxTileRow><MinTileCol>2052</MinTileCol><MaxTileCol>2116</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2771</MinTileRow><MaxTileRow>2867</MaxTileRow><MinTileCol>4105</MinTileCol><MaxTileCol>4225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5550</MinTileRow><MaxTileRow>5734</MaxTileRow><MinTileCol>8211</MinTileCol><MaxTileCol>8443</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11101</MinTileRow><MaxTileRow>11469</MaxTileRow><MinTileCol>16422</MinTileCol><MaxTileCol>16886</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22203</MinTileRow><MaxTileRow>22939</MaxTileRow><MinTileCol>32844</MinTileCol><MaxTileCol>33764</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>14</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>81</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Crues du bassin de la Seine - PHEC</ows:Title><ows:Abstract>Carte des plus hautes eaux connues (PHEC) sur le bassin de la Seine : crue de la seine et de ses affluents en 1910, ainsi que d&apos;autres crues à des dates postérieures sur d&apos;autres cours d&apos;eau.</ows:Abstract><ows:Keywords><ows:Keyword>Risques naturels</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.419457 47.4163</ows:LowerCorner><ows:UpperCorner>5.43248 50.064</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>LSEP-CRUE_PYR-PNG_WLD_WM_20130128</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>360</MaxTileRow><MinTileCol>512</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>688</MinTileRow><MaxTileRow>720</MaxTileRow><MinTileCol>1024</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1384</MinTileRow><MaxTileRow>1439</MaxTileRow><MinTileCol>2048</MinTileCol><MaxTileCol>2116</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2771</MinTileRow><MaxTileRow>2872</MaxTileRow><MinTileCol>4104</MinTileCol><MaxTileCol>4225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5550</MinTileRow><MaxTileRow>5736</MaxTileRow><MinTileCol>8208</MinTileCol><MaxTileCol>8443</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11101</MinTileRow><MaxTileRow>11472</MaxTileRow><MinTileCol>16416</MinTileCol><MaxTileCol>16886</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22203</MinTileRow><MaxTileRow>22944</MaxTileRow><MinTileCol>32840</MinTileCol><MaxTileCol>33764</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>14</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Corine Land Cover 2006</ows:Title><ows:Abstract>Visualisation de l’occupation du sol en France métropolitaine. Outil de référence pour mesurer les impacts environnementaux.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MEDDE-CLC_PYR-PNG_FXX_WM_20120703</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2737</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>41</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>68</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>83</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>169</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Ortholittorale 2000</ows:Title><ows:Abstract>Donnée issue du Référentiel géographique du littoral. Zone de couverture de l&apos;orthophotographie : côte de la mer du Nord, de la Manche et de l&apos;Atlantique.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.21565 43.301</ows:LowerCorner><ows:UpperCorner>2.60783 51.1233</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MEDDE-ORTHOLITTORAL_PYR_PNG_WLD_WM_20120926</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>1988</MinTileCol><MaxTileCol>2077</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2738</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>3977</MinTileCol><MaxTileCol>4155</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5476</MinTileRow><MaxTileRow>6001</MaxTileRow><MinTileCol>7954</MinTileCol><MaxTileCol>8310</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>12003</MaxTileRow><MinTileCol>15909</MinTileCol><MaxTileCol>16621</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24006</MaxTileRow><MinTileCol>31818</MinTileCol><MaxTileCol>33242</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43808</MinTileRow><MaxTileRow>48012</MaxTileRow><MinTileCol>63637</MinTileCol><MaxTileCol>66485</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87617</MinTileRow><MaxTileRow>96024</MaxTileRow><MinTileCol>127274</MinTileCol><MaxTileCol>132970</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Arrêtés de protection de biotope</ows:Title><ows:Abstract>Conservation de l&apos;habitat d&apos;espèces protégées. Outil réglementaires de niveau départemental.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>55.8659 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-APB_PYR-PNG_WLD_WM_20120615</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10734</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21469</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Arrêtés de protection de biotope</ows:Title><ows:Abstract>Conservation de l&apos;habitat d&apos;espèces protégées. Outil réglementaires de niveau départemental.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-APB_PYR-PNG_WLD_WM_20121005</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1351</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5481</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10962</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10631</MinTileCol><MaxTileCol>21463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21940</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21263</MinTileCol><MaxTileCol>42911</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>178</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves de biosphère</ows:Title><ows:Abstract>Réserves naturelles reconnues par le Programme sur l&apos;Homme et la Biosphère créé par l&apos;Unesco.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.25 15.7184</ows:LowerCorner><ows:UpperCorner>9.72255 51.1534</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-BIOS_PYR-PNG_WLD_WM_20120629</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>466</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>933</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1866</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3733</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>7467</MaxTileRow><MinTileCol>5313</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10947</MinTileRow><MaxTileRow>14934</MaxTileRow><MinTileCol>10626</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21895</MinTileRow><MaxTileRow>29869</MaxTileRow><MinTileCol>21253</MinTileCol><MaxTileCol>34537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>116</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>233</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Périmètres d&apos;intervention</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-CDL_PYR-PNG_WLD_WM_20121212_2</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>677</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1346</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>175</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>342</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Périmètres d&apos;intervention</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-CDL_PYR-PNG_WLD_WM_20130116</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>677</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1346</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>175</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>342</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Périmètres d&apos;intervention</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-CDL_PYR-PNG_WLD_WM_20130319</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36760</MaxTileRow><MinTileCol>21272</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Parcelles protégées</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-CEN_PYR-PNG_WLD_WM_20121213</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels marins</ows:Title><ows:Abstract>Zones maritimes identifiées pour leur biodiversité ou par les activités humaines faisant l&apos;objet de protection.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PNM_PYR-PNG_WLD_WM_20120717</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels marins</ows:Title><ows:Abstract>Zones maritimes identifiées pour leur biodiversité ou par les activités humaines faisant l&apos;objet de protection.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PNM_PYR-PNG_WLD_WM_20121002</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels régionaux</ows:Title><ows:Abstract>Les parcs naturels régionaux ont pour but de valoriser de vastes espaces de fort intérêt culturel et naturel, et de veiller au développement durable de ces territoires dont le caractère rural est souvent très affirmé.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.388 1.98479</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PNR_PYR-PNG_WLD_WM_20120622</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>506</MaxTileRow><MinTileCol>337</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1012</MaxTileRow><MinTileCol>674</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2025</MaxTileRow><MinTileCol>1349</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4050</MaxTileRow><MinTileCol>2699</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>8101</MaxTileRow><MinTileCol>5398</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>16203</MaxTileRow><MinTileCol>10796</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>32406</MaxTileRow><MinTileCol>21592</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>21</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>63</MaxTileRow><MinTileCol>42</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>126</MaxTileRow><MinTileCol>84</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>253</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels régionaux</ows:Title><ows:Abstract>Les parcs naturels régionaux ont pour but de valoriser de vastes espaces de fort intérêt culturel et naturel, et de veiller au développement durable de ces territoires dont le caractère rural est souvent très affirmé.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PNR_PYR-PNG_WLD_WM_20121005</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>510</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1005</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2010</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4004</MaxTileRow><MinTileCol>2701</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5481</MinTileRow><MaxTileRow>8009</MaxTileRow><MinTileCol>5403</MinTileCol><MaxTileCol>8635</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10962</MinTileRow><MaxTileRow>16002</MaxTileRow><MinTileCol>10807</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21940</MinTileRow><MaxTileRow>31988</MaxTileRow><MinTileCol>21615</MinTileCol><MaxTileCol>34495</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>36</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>73</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>127</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>146</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>255</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>277</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs nationaux</ows:Title><ows:Abstract>Zones naturelles labellisées en raison de leur richesse naturelle exceptionnelle.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-54.7027 -21.4871</ows:LowerCorner><ows:UpperCorner>55.9848 51.1534</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PN_PYR-PNG_WLD_WM_20120628</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>356</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>712</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1425</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2851</MinTileCol><MaxTileCol>5369</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5702</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10947</MinTileRow><MaxTileRow>18387</MaxTileRow><MinTileCol>11404</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21895</MinTileRow><MaxTileRow>36774</MaxTileRow><MinTileCol>22809</MinTileCol><MaxTileCol>42959</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>89</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>178</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs nationaux</ows:Title><ows:Abstract>Les parcs nationaux sont de vastes espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, et dont le but est à la fois de protéger des processus écologiques à large échelle et de fournir des possibilités de découverte de la nature.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.9679 -21.3877</ows:LowerCorner><ows:UpperCorner>55.8779 51.0863</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PN_PYR-PNG_WLD_WM_20120719</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>335</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2685</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9188</MaxTileRow><MinTileCol>5371</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10957</MinTileRow><MaxTileRow>18377</MaxTileRow><MinTileCol>10743</MinTileCol><MaxTileCol>21470</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21914</MinTileRow><MaxTileRow>36755</MaxTileRow><MinTileCol>21487</MinTileCol><MaxTileCol>42940</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs nationaux</ows:Title><ows:Abstract>Les parcs nationaux sont de vastes espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, et dont le but est à la fois de protéger des processus écologiques à large échelle et de fournir des possibilités de découverte de la nature.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-PN_PYR-PNG_WLD_WM_20120810</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones humides d&apos;importance internationale</ows:Title><ows:Abstract>Un site Ramsar est un espace désigné en application de la Convention relative aux zones humides d&apos;importance internationale, particulièrement comme habitats des oiseaux d&apos;eau, dont le traité a été signé en 1971 sur les bords de la mer Caspienne (Iran).</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -50.5714</ows:LowerCorner><ows:UpperCorner>80.7449 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RAMSAR_PYR-PNG_WLD_WM_20121213</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>688</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>672</MinTileRow><MaxTileRow>1360</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1359</MinTileRow><MaxTileRow>2704</MaxTileRow><MinTileCol>1344</MinTileCol><MaxTileCol>2944</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>5408</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5872</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5488</MinTileRow><MaxTileRow>10816</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>11743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10991</MinTileRow><MaxTileRow>21631</MaxTileRow><MinTileCol>10752</MinTileCol><MaxTileCol>23472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21984</MinTileRow><MaxTileRow>43264</MaxTileRow><MinTileCol>21503</MinTileCol><MaxTileCol>46943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>95</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>191</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>160</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>368</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves biologiques</ows:Title><ows:Abstract>Une réserve biologique est un espace protégé en milieu forestier ou en milieu associé à la forêt (landes, mares, tourbières, dunes). Ce statut s&apos;applique aux forêts gérées par l&apos;Office National des Forêts et a pour but la protection d&apos;habitats remarquables ou représentatifs.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.388 -21.469</ows:LowerCorner><ows:UpperCorner>55.8906 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RB_PYR-PNG_WLD_WM_20120622</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>337</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>674</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1349</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2699</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5398</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18385</MaxTileRow><MinTileCol>10796</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36771</MaxTileRow><MinTileCol>21592</MinTileCol><MaxTileCol>42942</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>21</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>42</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>84</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves biologiques</ows:Title><ows:Abstract>Une réserve biologique est un espace protégé en milieu forestier ou en milieu associé à la forêt (landes, mares, tourbières, dunes). Ce statut s&apos;applique aux forêts gérées par l&apos;Office National des Forêts et a pour but la protection d&apos;habitats remarquables ou représentatifs.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RB_PYR-PNG_WLD_WM_20121002</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1351</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2701</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5497</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5403</MinTileCol><MaxTileCol>10731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10994</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10807</MinTileCol><MaxTileCol>21463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22004</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21615</MinTileCol><MaxTileCol>42927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>178</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves nationales de chasse et de faune sauvage</ows:Title><ows:Abstract>Les réserves nationales de chasse et de faune sauvage sont des espaces protégés terrestres ou marins dont la gestion est principalement assurée par l&apos;Office National de la Chasse et de la Faune Sauvage.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2.99155 41.687</ows:LowerCorner><ows:UpperCorner>9.6157 51.0863</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RNCF_PYR-PNG_WLD_WM_20120717</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>381</MaxTileRow><MinTileCol>503</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>762</MaxTileRow><MinTileCol>1006</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1525</MaxTileRow><MinTileCol>2013</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>3050</MaxTileRow><MinTileCol>4027</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>6101</MaxTileRow><MinTileCol>8055</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10957</MinTileRow><MaxTileRow>12202</MaxTileRow><MinTileCol>16111</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21914</MinTileRow><MaxTileRow>24404</MaxTileRow><MinTileCol>32223</MinTileCol><MaxTileCol>34518</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>125</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>251</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves Naturelles de Corse</ows:Title><ows:Abstract>Les réserves naturelles sont des espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, tant sur le plan de la biodiversité que parfois sur celui de la géodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RNC_PYR-PNG_WLD_WM_20120907</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>529</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>1066</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>2141</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3005</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>4282</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6019</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>8573</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12038</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>17155</MinTileCol><MaxTileCol>17251</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24084</MinTileRow><MaxTileRow>24492</MaxTileRow><MinTileCol>34318</MinTileCol><MaxTileCol>34502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves naturelles nationales</ows:Title><ows:Abstract>Les réserves naturelles sont des espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, tant sur le plan de la biodiversité que parfois sur celui de la géodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.25 -21.4871</ows:LowerCorner><ows:UpperCorner>55.8779 51.0863</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RN_PYR-PNG_WLD_WM_20120720</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5313</MinTileCol><MaxTileCol>10735</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10957</MinTileRow><MaxTileRow>18387</MaxTileRow><MinTileCol>10626</MinTileCol><MaxTileCol>21470</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21914</MinTileRow><MaxTileRow>36774</MaxTileRow><MinTileCol>21253</MinTileCol><MaxTileCol>42940</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves naturelles nationales</ows:Title><ows:Abstract>Les réserves naturelles sont des espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, tant sur le plan de la biodiversité que parfois sur celui de la géodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -50.5714</ows:LowerCorner><ows:UpperCorner>80.7449 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-RN_PYR-PNG_WLD_WM_20121107</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>335</MinTileRow><MaxTileRow>679</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>678</MinTileRow><MaxTileRow>1358</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1365</MinTileRow><MaxTileRow>2709</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2738</MinTileRow><MaxTileRow>5410</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5869</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5476</MinTileRow><MaxTileRow>10820</MaxTileRow><MinTileCol>5315</MinTileCol><MaxTileCol>11739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>21632</MaxTileRow><MinTileCol>10639</MinTileCol><MaxTileCol>23471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>43256</MaxTileRow><MinTileCol>21287</MinTileCol><MaxTileCol>46943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>18</MinTileRow><MaxTileRow>42</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>84</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>97</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>81</MinTileRow><MaxTileRow>169</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>186</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>163</MinTileRow><MaxTileRow>339</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>373</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites Natura 2000 au titre de la Directive Habitats</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-SIC_PYR-PNG_WLD_WM_20120719</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites Natura 2000 au titre de la Directive Habitats</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-SIC_PYR-PNG_WLD_WM_20121001</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>15915</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>24492</MaxTileRow><MinTileCol>31830</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones écologiques de nature remarquable (ZNIEFF1)</ows:Title><ows:Abstract>Inventaire des espaces naturels abritant la nature remarquable</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-ZNIEFF1_PYR-PNG_WLD_WM_20121210</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>344</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1016</MaxTileRow><MinTileCol>696</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2024</MaxTileRow><MinTileCol>1400</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4048</MaxTileRow><MinTileCol>2808</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>8096</MaxTileRow><MinTileCol>5616</MinTileCol><MaxTileCol>8632</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>16184</MaxTileRow><MinTileCol>11231</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>32367</MaxTileRow><MinTileCol>22471</MinTileCol><MaxTileCol>34511</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>63</MaxTileRow><MinTileCol>39</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones fonctionnelles de nature remarquable (ZNIEFF2)</ows:Title><ows:Abstract>Inventaire des espaces fonctionnels indispensables à la nature remarquable.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-ZNIEFF2_PYR-PNG_WLD_WM20121204</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>344</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1016</MaxTileRow><MinTileCol>696</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2024</MaxTileRow><MinTileCol>1400</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4048</MaxTileRow><MinTileCol>2808</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>8096</MaxTileRow><MinTileCol>5624</MinTileCol><MaxTileCol>8632</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10960</MinTileRow><MaxTileRow>16191</MaxTileRow><MinTileCol>11247</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>32383</MaxTileRow><MinTileCol>22495</MinTileCol><MaxTileCol>34503</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>63</MaxTileRow><MinTileCol>39</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites NATURA 2000 au titre de la Directive Oiseaux</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-ZPS_PYR-PNG_WLD_WM_20120717</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites NATURA 2000 au titre de la Directive Oiseaux</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNHN-ZPS_PYR-PNG_WLD_WM_20121001</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>390</MaxTileRow><MinTileCol>491</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>773</MaxTileRow><MinTileCol>991</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1538</MaxTileRow><MinTileCol>1982</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>3068</MaxTileRow><MinTileCol>3973</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>6129</MaxTileRow><MinTileCol>7947</MinTileCol><MaxTileCol>8635</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>12258</MaxTileRow><MinTileCol>15895</MinTileCol><MaxTileCol>17263</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>24516</MaxTileRow><MinTileCol>31799</MinTileCol><MaxTileCol>34519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>27</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>36</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>55</MaxTileRow><MinTileCol>57</MinTileCol><MaxTileCol>73</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>103</MaxTileRow><MinTileCol>122</MinTileCol><MaxTileCol>138</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>199</MaxTileRow><MinTileCol>245</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>MNT BDAlti V1</ows:Title><ows:Abstract>BDAlti V1 au pas de 25m</ows:Abstract><ows:Keywords><ows:Keyword>MNT</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-64 -23</ows:LowerCorner><ows:UpperCorner>168 52</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MNT_PYR-RAW_WLD_WGS84G_2012-07-04</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/x-bil;bits=32</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>216</MinTileRow><MaxTileRow>642</MaxTileRow><MinTileCol>659</MinTileCol><MaxTileCol>1979</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>432</MinTileRow><MaxTileRow>1285</MaxTileRow><MinTileCol>1319</MinTileCol><MaxTileCol>3959</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>10</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>30</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>6</MinTileRow><MaxTileRow>20</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>61</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>40</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>123</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>80</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>247</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>54</MinTileRow><MaxTileRow>160</MaxTileRow><MinTileCol>164</MinTileCol><MaxTileCol>494</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>108</MinTileRow><MaxTileRow>321</MaxTileRow><MinTileCol>329</MinTileCol><MaxTileCol>989</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Monde 12 feuilles</ows:Title><ows:Abstract>Carte de monde</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -66.5133</ows:LowerCorner><ows:UpperCorner>180 85.0511</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MONDE12F_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Monde 12 feuilles</ows:Title><ows:Abstract>Carte de monde</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -60</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MONDE12F_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Monde 12 feuilles</ows:Title><ows:Abstract>Carte de monde</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -60</ows:LowerCorner><ows:UpperCorner>180 80</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MONDE12F_PYR-JPEG_WLD_WM_WMTS_3D_V2</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte Monde 12 feuilles</ows:Title><ows:Abstract>Carte de monde</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -66.5133</ows:LowerCorner><ows:UpperCorner>180 85.0511</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>MONDE12F_PYR-JPEG_WLD_WM_WMTS_3D_V2-20130601</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Crues du bassin de la Seine - PHEC</ows:Title><ows:Abstract>Carte des plus hautes eaux connues (PHEC) sur le bassin de la Seine : crue de la seine et de ses affluents en 1910, ainsi que d&apos;autres crues à des dates postérieures sur d&apos;autres cours d&apos;eau.</ows:Abstract><ows:Keywords><ows:Keyword>Risques naturels</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.419457 47.4163</ows:LowerCorner><ows:UpperCorner>5.43248 50.064</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NATURALRISKZONES.1910FLOODEDWATERSHEDS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>360</MaxTileRow><MinTileCol>512</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>688</MinTileRow><MaxTileRow>720</MaxTileRow><MinTileCol>1024</MinTileCol><MaxTileCol>1058</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1384</MinTileRow><MaxTileRow>1439</MaxTileRow><MinTileCol>2048</MinTileCol><MaxTileCol>2116</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2771</MinTileRow><MaxTileRow>2872</MaxTileRow><MinTileCol>4104</MinTileCol><MaxTileCol>4225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5550</MinTileRow><MaxTileRow>5736</MaxTileRow><MinTileCol>8208</MinTileCol><MaxTileCol>8443</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11101</MinTileRow><MaxTileRow>11472</MaxTileRow><MinTileCol>16416</MinTileCol><MaxTileCol>16886</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22203</MinTileRow><MaxTileRow>22944</MaxTileRow><MinTileCol>32840</MinTileCol><MaxTileCol>33764</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>14</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>BDORTHO-NC</ows:Title><ows:Abstract>La BDORTHO-NC est une orthophotographie du territoire de Nouvelle-Calédonie réalisée par la DITTT pour le compte du Gouvernement de Nouvelle-Calédonie.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178 -84</ows:LowerCorner><ows:UpperCorner>178 84</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NCL-DITTT-ORTHO_PYR-JPEG_WLD_WM_WMTS_20121214</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>Style INSPIRE</ows:Title><ows:Abstract>Style par défault INSPIRE</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword><ows:Keyword>INSPIRE</ows:Keyword></ows:Keywords><ows:Identifier>inspire_common:DEFAULT</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr/inspire" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>31</MinTileRow><MaxTileRow>992</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>62</MinTileRow><MaxTileRow>1985</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>125</MinTileRow><MaxTileRow>3970</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4548</MinTileRow><MaxTileRow>4635</MaxTileRow><MinTileCol>7816</MinTileCol><MaxTileCol>7924</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9096</MinTileRow><MaxTileRow>9270</MaxTileRow><MinTileCol>15633</MinTileCol><MaxTileCol>15848</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18193</MinTileRow><MaxTileRow>18540</MaxTileRow><MinTileCol>31266</MinTileCol><MaxTileCol>31697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36386</MinTileRow><MaxTileRow>37080</MaxTileRow><MinTileCol>62533</MinTileCol><MaxTileCol>63395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>72773</MinTileRow><MaxTileRow>74160</MaxTileRow><MinTileCol>125066</MinTileCol><MaxTileCol>126791</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>145546</MinTileRow><MaxTileRow>148320</MaxTileRow><MinTileCol>250132</MinTileCol><MaxTileCol>253582</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>7</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>15</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NOMS_SYMBO_PYR-PNG_WLD_WM_20120814</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2685</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5371</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NOMS_SYMBO_PYR-PNG_WLD_WM_20120815</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2685</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5371</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10954</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10631</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21263</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NOMS_SYMBO_PYR-PNG_WLD_WM_20120829</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2685</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5371</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10954</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10631</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21263</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43816</MinTileRow><MaxTileRow>73512</MaxTileRow><MinTileCol>42526</MinTileCol><MaxTileCol>85870</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87633</MinTileRow><MaxTileRow>147025</MaxTileRow><MinTileCol>85069</MinTileCol><MaxTileCol>171741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NOMS_SYMBO_PYR-PNG_WLD_WM_20120912</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2732</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2685</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5371</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10954</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10631</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21263</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43816</MinTileRow><MaxTileRow>73512</MaxTileRow><MinTileCol>42526</MinTileCol><MaxTileCol>85870</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87633</MinTileRow><MaxTileRow>147025</MaxTileRow><MinTileCol>85069</MinTileCol><MaxTileCol>171741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Dénominations géographiques</ows:Title><ows:Abstract>Affichage des noms des villes, villages, lieux-dits ...</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>NOMS_SYMBO_PYR-PNG_WLD_WM_20130311</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43808</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87632</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>78</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>48</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>105</MinTileRow><MaxTileRow>217</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>314</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Biarritz 1977</ows:Title><ows:Abstract>Photographie de Biarritz 1977</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.8265 43.3165</ows:LowerCorner><ows:UpperCorner>-1.18848 43.7157</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHO-EDUGEO_PYR-PNG_WLD_WM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>508</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>746</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>1013</MinTileCol><MaxTileCol>1017</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1493</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>2027</MinTileCol><MaxTileCol>2034</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2987</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>4054</MinTileCol><MaxTileCol>4068</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5975</MinTileRow><MaxTileRow>6000</MaxTileRow><MinTileCol>8108</MinTileCol><MaxTileCol>8137</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11950</MinTileRow><MaxTileRow>12001</MaxTileRow><MinTileCol>16217</MinTileCol><MaxTileCol>16275</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23901</MinTileRow><MaxTileRow>24002</MaxTileRow><MinTileCol>32435</MinTileCol><MaxTileCol>32551</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Suisse - photographies aériennes du canton de Genève (SITG)</ows:Title><ows:Abstract>Photographie aérienne du canton de Genève.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.95007 46.1241</ows:LowerCorner><ows:UpperCorner>6.31198 46.3658</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOGENEVE_PYR-PNG_CHE_WM_20100612</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>362</MinTileRow><MaxTileRow>363</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>725</MinTileRow><MaxTileRow>727</MaxTileRow><MinTileCol>1057</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1451</MinTileRow><MaxTileRow>1455</MaxTileRow><MinTileCol>2115</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2902</MinTileRow><MaxTileRow>2910</MaxTileRow><MinTileCol>4231</MinTileCol><MaxTileCol>4239</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5804</MinTileRow><MaxTileRow>5820</MaxTileRow><MinTileCol>8462</MinTileCol><MaxTileCol>8479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11609</MinTileRow><MaxTileRow>11641</MaxTileRow><MinTileCol>16925</MinTileCol><MaxTileCol>16958</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23219</MinTileRow><MaxTileRow>23282</MaxTileRow><MinTileCol>33851</MinTileCol><MaxTileCol>33917</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>46438</MinTileRow><MaxTileRow>46565</MaxTileRow><MinTileCol>67702</MinTileCol><MaxTileCol>67834</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>92876</MinTileRow><MaxTileRow>93130</MaxTileRow><MinTileCol>135404</MinTileCol><MaxTileCol>135668</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>185752</MinTileRow><MaxTileRow>186261</MaxTileRow><MinTileCol>270809</MinTileCol><MaxTileCol>271336</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>371504</MinTileRow><MaxTileRow>372522</MaxTileRow><MinTileCol>541618</MinTileCol><MaxTileCol>542672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>181</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - AJACCIO 1975</ows:Title><ows:Abstract>Photographie - Ajaccio 1975</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>8.65713 41.8334</ows:LowerCorner><ows:UpperCorner>8.98239 42.0846</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.AJACCIO1975</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>379</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>536</MinTileCol><MaxTileCol>537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>759</MinTileRow><MaxTileRow>761</MaxTileRow><MinTileCol>1073</MinTileCol><MaxTileCol>1075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1519</MinTileRow><MaxTileRow>1523</MaxTileRow><MinTileCol>2146</MinTileCol><MaxTileCol>2150</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3038</MinTileRow><MaxTileRow>3046</MaxTileRow><MinTileCol>4292</MinTileCol><MaxTileCol>4300</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6076</MinTileRow><MaxTileRow>6092</MaxTileRow><MinTileCol>8585</MinTileCol><MaxTileCol>8600</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12153</MinTileRow><MaxTileRow>12184</MaxTileRow><MinTileCol>17171</MinTileCol><MaxTileCol>17201</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24307</MinTileRow><MaxTileRow>24368</MaxTileRow><MinTileCol>34343</MinTileCol><MaxTileCol>34403</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>47</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>67</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>94</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>134</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>189</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>268</MinTileCol><MaxTileCol>268</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - AJACCIO 1990</ows:Title><ows:Abstract>Photographie - Ajaccio 1990</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>8.64462 41.7868</ows:LowerCorner><ows:UpperCorner>8.98239 42.0939</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.AJACCIO1990</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>379</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>536</MinTileCol><MaxTileCol>537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>759</MinTileRow><MaxTileRow>761</MaxTileRow><MinTileCol>1073</MinTileCol><MaxTileCol>1075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1519</MinTileRow><MaxTileRow>1523</MaxTileRow><MinTileCol>2146</MinTileCol><MaxTileCol>2150</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3038</MinTileRow><MaxTileRow>3047</MaxTileRow><MinTileCol>4292</MinTileCol><MaxTileCol>4300</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6076</MinTileRow><MaxTileRow>6095</MaxTileRow><MinTileCol>8585</MinTileCol><MaxTileCol>8600</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12152</MinTileRow><MaxTileRow>12190</MaxTileRow><MinTileCol>17170</MinTileCol><MaxTileCol>17201</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24305</MinTileRow><MaxTileRow>24380</MaxTileRow><MinTileCol>34341</MinTileCol><MaxTileCol>34403</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>47</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>67</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>94</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>134</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>189</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>268</MinTileCol><MaxTileCol>268</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT ARCACHON 2010</ows:Title><ows:Abstract>Zone d&apos;ARCACHON - 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.87654 44.2289</ows:LowerCorner><ows:UpperCorner>-0.750618 45.0301</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.ARCACHON2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>368</MinTileRow><MaxTileRow>371</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>736</MinTileRow><MaxTileRow>742</MaxTileRow><MinTileCol>1013</MinTileCol><MaxTileCol>1019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1472</MinTileRow><MaxTileRow>1485</MaxTileRow><MinTileCol>2026</MinTileCol><MaxTileCol>2039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2945</MinTileRow><MaxTileRow>2971</MaxTileRow><MinTileCol>4053</MinTileCol><MaxTileCol>4078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5891</MinTileRow><MaxTileRow>5943</MaxTileRow><MinTileCol>8106</MinTileCol><MaxTileCol>8157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11783</MinTileRow><MaxTileRow>11886</MaxTileRow><MinTileCol>16213</MinTileCol><MaxTileCol>16315</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23567</MinTileRow><MaxTileRow>23772</MaxTileRow><MinTileCol>32426</MinTileCol><MaxTileCol>32631</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>92</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>184</MinTileRow><MaxTileRow>185</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT ARRAS LENS BETHUNE 2008</ows:Title><ows:Abstract>Zone de ARRAS LENS BETHUNE - 2008</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.75144 49.929</ows:LowerCorner><ows:UpperCorner>3.25268 50.8068</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.ARRAS-LENS-BETHUNE2008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>343</MinTileRow><MaxTileRow>347</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>687</MinTileRow><MaxTileRow>695</MaxTileRow><MinTileCol>1033</MinTileCol><MaxTileCol>1042</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1374</MinTileRow><MaxTileRow>1390</MaxTileRow><MinTileCol>2067</MinTileCol><MaxTileCol>2085</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2749</MinTileRow><MaxTileRow>2780</MaxTileRow><MinTileCol>4135</MinTileCol><MaxTileCol>4170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5498</MinTileRow><MaxTileRow>5561</MaxTileRow><MinTileCol>8271</MinTileCol><MaxTileCol>8340</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10997</MinTileRow><MaxTileRow>11123</MaxTileRow><MinTileCol>16543</MinTileCol><MaxTileCol>16680</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21995</MinTileRow><MaxTileRow>22246</MaxTileRow><MinTileCol>33086</MinTileCol><MaxTileCol>33360</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>86</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>173</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BELFORT MONTBELLIARD 1951</ows:Title><ows:Abstract>Photographie - BELFORT MONTBELLIARD 1951</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>6.69301 47.4607</ows:LowerCorner><ows:UpperCorner>6.96824 47.6801</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BELFORT-MONTBELLIARD1951</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>531</MinTileCol><MaxTileCol>531</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>716</MaxTileRow><MinTileCol>1062</MinTileCol><MaxTileCol>1063</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1429</MinTileRow><MaxTileRow>1432</MaxTileRow><MinTileCol>2124</MinTileCol><MaxTileCol>2127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2858</MinTileRow><MaxTileRow>2865</MaxTileRow><MinTileCol>4248</MinTileCol><MaxTileCol>4254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5717</MinTileRow><MaxTileRow>5731</MaxTileRow><MinTileCol>8496</MinTileCol><MaxTileCol>8509</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11434</MinTileRow><MaxTileRow>11463</MaxTileRow><MinTileCol>16993</MinTileCol><MaxTileCol>17018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22868</MinTileRow><MaxTileRow>22927</MaxTileRow><MinTileCol>33986</MinTileCol><MaxTileCol>34036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>265</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BERRY SUD 1950</ows:Title><ows:Abstract>Photographie - BERRY SUD 1950</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.23852 46.3669</ows:LowerCorner><ows:UpperCorner>1.48873 46.4531</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BERRY-SUD1950</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>362</MinTileRow><MaxTileRow>362</MaxTileRow><MinTileCol>515</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>724</MinTileRow><MaxTileRow>725</MaxTileRow><MinTileCol>1031</MinTileCol><MaxTileCol>1032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1449</MinTileRow><MaxTileRow>1451</MaxTileRow><MinTileCol>2062</MinTileCol><MaxTileCol>2064</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2899</MinTileRow><MaxTileRow>2902</MaxTileRow><MinTileCol>4124</MinTileCol><MaxTileCol>4129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5798</MinTileRow><MaxTileRow>5804</MaxTileRow><MinTileCol>8248</MinTileCol><MaxTileCol>8259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11597</MinTileRow><MaxTileRow>11609</MaxTileRow><MinTileCol>16496</MinTileCol><MaxTileCol>16519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23195</MinTileRow><MaxTileRow>23218</MaxTileRow><MinTileCol>32993</MinTileCol><MaxTileCol>33039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>181</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BERRY SUD 1970</ows:Title><ows:Abstract>Photographie - BERRY SUD 1970</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.01333 46.6081</ows:LowerCorner><ows:UpperCorner>1.42617 46.7283</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BERRY-SUD1970</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>361</MinTileRow><MaxTileRow>361</MaxTileRow><MinTileCol>514</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>722</MinTileRow><MaxTileRow>723</MaxTileRow><MinTileCol>1029</MinTileCol><MaxTileCol>1032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1445</MinTileRow><MaxTileRow>1447</MaxTileRow><MinTileCol>2059</MinTileCol><MaxTileCol>2064</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2890</MinTileRow><MaxTileRow>2894</MaxTileRow><MinTileCol>4119</MinTileCol><MaxTileCol>4128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5780</MinTileRow><MaxTileRow>5788</MaxTileRow><MinTileCol>8238</MinTileCol><MaxTileCol>8256</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11561</MinTileRow><MaxTileRow>11577</MaxTileRow><MinTileCol>16476</MinTileCol><MaxTileCol>16513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23123</MinTileRow><MaxTileRow>23154</MaxTileRow><MinTileCol>32952</MinTileCol><MaxTileCol>33027</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>180</MinTileRow><MaxTileRow>180</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BETHUNE 1963</ows:Title><ows:Abstract>Photographie - BETHUNE 1963</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.62716 50.3939</ows:LowerCorner><ows:UpperCorner>2.95243 50.5054</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BETHUNE1963</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>345</MinTileRow><MaxTileRow>345</MaxTileRow><MinTileCol>519</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>690</MinTileRow><MaxTileRow>691</MaxTileRow><MinTileCol>1038</MinTileCol><MaxTileCol>1040</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1380</MinTileRow><MaxTileRow>1382</MaxTileRow><MinTileCol>2077</MinTileCol><MaxTileCol>2081</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2760</MinTileRow><MaxTileRow>2764</MaxTileRow><MinTileCol>4155</MinTileCol><MaxTileCol>4163</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5520</MinTileRow><MaxTileRow>5528</MaxTileRow><MinTileCol>8311</MinTileCol><MaxTileCol>8326</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11041</MinTileRow><MaxTileRow>11057</MaxTileRow><MinTileCol>16623</MinTileCol><MaxTileCol>16652</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22082</MinTileRow><MaxTileRow>22114</MaxTileRow><MinTileCol>33246</MinTileCol><MaxTileCol>33305</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>86</MinTileRow><MaxTileRow>86</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>172</MinTileRow><MaxTileRow>172</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BETHUNE 1964</ows:Title><ows:Abstract>Photographie - BETHUNE 1964</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.83984 50.33</ows:LowerCorner><ows:UpperCorner>3.29021 50.4098</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BETHUNE1964</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>345</MinTileRow><MaxTileRow>345</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>690</MinTileRow><MaxTileRow>691</MaxTileRow><MinTileCol>1040</MinTileCol><MaxTileCol>1042</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1381</MinTileRow><MaxTileRow>1383</MaxTileRow><MinTileCol>2080</MinTileCol><MaxTileCol>2085</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2763</MinTileRow><MaxTileRow>2766</MaxTileRow><MinTileCol>4160</MinTileCol><MaxTileCol>4170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5527</MinTileRow><MaxTileRow>5533</MaxTileRow><MinTileCol>8321</MinTileCol><MaxTileCol>8341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11054</MinTileRow><MaxTileRow>11066</MaxTileRow><MinTileCol>16642</MinTileCol><MaxTileCol>16683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22109</MinTileRow><MaxTileRow>22132</MaxTileRow><MinTileCol>33284</MinTileCol><MaxTileCol>33366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>86</MinTileRow><MaxTileRow>86</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>172</MinTileRow><MaxTileRow>172</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BETHUNE 1988</ows:Title><ows:Abstract>Photographie - BETHUNE 1988</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.5521 50.322</ows:LowerCorner><ows:UpperCorner>3.24017 50.4895</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BETHUNE1988</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>345</MinTileRow><MaxTileRow>345</MaxTileRow><MinTileCol>519</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>690</MinTileRow><MaxTileRow>691</MaxTileRow><MinTileCol>1038</MinTileCol><MaxTileCol>1042</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1380</MinTileRow><MaxTileRow>1383</MaxTileRow><MinTileCol>2077</MinTileCol><MaxTileCol>2084</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2760</MinTileRow><MaxTileRow>2766</MaxTileRow><MinTileCol>4154</MinTileCol><MaxTileCol>4169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5521</MinTileRow><MaxTileRow>5533</MaxTileRow><MinTileCol>8308</MinTileCol><MaxTileCol>8339</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11043</MinTileRow><MaxTileRow>11067</MaxTileRow><MinTileCol>16616</MinTileCol><MaxTileCol>16678</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22086</MinTileRow><MaxTileRow>22134</MaxTileRow><MinTileCol>33232</MinTileCol><MaxTileCol>33357</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>86</MinTileRow><MaxTileRow>86</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>172</MinTileRow><MaxTileRow>172</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Biarritz 1977</ows:Title><ows:Abstract>Photographie de Biarritz 1977</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.8265 43.3165</ows:LowerCorner><ows:UpperCorner>-1.18848 43.7157</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BIARRITZ1977</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>508</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>746</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>1013</MinTileCol><MaxTileCol>1017</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1493</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>2027</MinTileCol><MaxTileCol>2034</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2987</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>4054</MinTileCol><MaxTileCol>4068</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5975</MinTileRow><MaxTileRow>6000</MaxTileRow><MinTileCol>8108</MinTileCol><MaxTileCol>8137</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11950</MinTileRow><MaxTileRow>12001</MaxTileRow><MinTileCol>16217</MinTileCol><MaxTileCol>16275</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23901</MinTileRow><MaxTileRow>24002</MaxTileRow><MinTileCol>32435</MinTileCol><MaxTileCol>32551</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BIARRTITZ 1979</ows:Title><ows:Abstract>Photographie - Biarritz 1979</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.13844 43.6795</ows:LowerCorner><ows:UpperCorner>-0.975803 43.7609</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BIARRTITZ1979</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>373</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>746</MinTileRow><MaxTileRow>747</MaxTileRow><MinTileCol>1017</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1493</MinTileRow><MaxTileRow>1494</MaxTileRow><MinTileCol>2035</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2986</MinTileRow><MaxTileRow>2988</MaxTileRow><MinTileCol>4070</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5972</MinTileRow><MaxTileRow>5977</MaxTileRow><MinTileCol>8140</MinTileCol><MaxTileCol>8147</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11945</MinTileRow><MaxTileRow>11955</MaxTileRow><MinTileCol>16280</MinTileCol><MaxTileCol>16295</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23890</MinTileRow><MaxTileRow>23911</MaxTileRow><MinTileCol>32560</MinTileCol><MaxTileCol>32590</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>186</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT BORDEAUX 2003</ows:Title><ows:Abstract>Zone de Bordeaux 2003</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.12593 44.6754</ows:LowerCorner><ows:UpperCorner>0.125103 45.5582</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BORDEAUX2003</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>369</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>732</MinTileRow><MaxTileRow>739</MaxTileRow><MinTileCol>1017</MinTileCol><MaxTileCol>1024</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1464</MinTileRow><MaxTileRow>1478</MaxTileRow><MinTileCol>2035</MinTileCol><MaxTileCol>2049</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2928</MinTileRow><MaxTileRow>2957</MaxTileRow><MinTileCol>4070</MinTileCol><MaxTileCol>4098</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5857</MinTileRow><MaxTileRow>5914</MaxTileRow><MinTileCol>8140</MinTileCol><MaxTileCol>8197</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11715</MinTileRow><MaxTileRow>11829</MaxTileRow><MinTileCol>16281</MinTileCol><MaxTileCol>16395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23430</MinTileRow><MaxTileRow>23658</MaxTileRow><MinTileCol>32563</MinTileCol><MaxTileCol>32790</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - BOURG SAINT MAURICE 1956</ows:Title><ows:Abstract>Photographie - BOURG SAINT MAURICE 1956</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>6.61795 45.4793</ows:LowerCorner><ows:UpperCorner>6.85564 45.6282</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.BOURG-ST-MAURICE1956</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>365</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>530</MinTileCol><MaxTileCol>531</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>731</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1061</MinTileCol><MaxTileCol>1063</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1463</MinTileRow><MaxTileRow>1465</MaxTileRow><MinTileCol>2123</MinTileCol><MaxTileCol>2126</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2926</MinTileRow><MaxTileRow>2931</MaxTileRow><MinTileCol>4246</MinTileCol><MaxTileCol>4252</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5853</MinTileRow><MaxTileRow>5862</MaxTileRow><MinTileCol>8493</MinTileCol><MaxTileCol>8504</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11706</MinTileRow><MaxTileRow>11725</MaxTileRow><MinTileCol>16986</MinTileCol><MaxTileCol>17008</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23412</MinTileRow><MaxTileRow>23451</MaxTileRow><MinTileCol>33972</MinTileCol><MaxTileCol>34016</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>265</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - CAEN 1991</ows:Title><ows:Abstract>Photographie - CAEN 1991</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-0.975803 49.035</ows:LowerCorner><ows:UpperCorner>0.0250206 49.4109</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CAEN1991</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>509</MinTileCol><MaxTileCol>512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>699</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1018</MinTileCol><MaxTileCol>1024</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1399</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2036</MinTileCol><MaxTileCol>2048</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2799</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4073</MinTileCol><MaxTileCol>4096</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5598</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8147</MinTileCol><MaxTileCol>8193</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11196</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16295</MinTileCol><MaxTileCol>16386</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22392</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>32590</MinTileCol><MaxTileCol>32772</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Le Cap d&apos;Agde 1968</ows:Title><ows:Abstract>Photographie du Cap d&apos;Agde 1968</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.02749 43.1889</ows:LowerCorner><ows:UpperCorner>3.60297 43.3984</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CAP-D-AGDE1968</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>522</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1041</MinTileCol><MaxTileCol>1044</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2082</MinTileCol><MaxTileCol>2088</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2997</MinTileRow><MaxTileRow>3004</MaxTileRow><MinTileCol>4164</MinTileCol><MaxTileCol>4177</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5995</MinTileRow><MaxTileRow>6008</MaxTileRow><MinTileCol>8329</MinTileCol><MaxTileCol>8355</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11990</MinTileRow><MaxTileRow>12017</MaxTileRow><MinTileCol>16659</MinTileCol><MaxTileCol>16711</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23981</MinTileRow><MaxTileRow>24034</MaxTileRow><MinTileCol>33319</MinTileCol><MaxTileCol>33423</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>261</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT Cap d&apos;Agde 2008</ows:Title><ows:Abstract>Zone du Cap d&apos;Agde - 2008</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.12757 43.1433</ows:LowerCorner><ows:UpperCorner>3.75309 43.5073</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CAP-D-AGDE2008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>522</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1041</MinTileCol><MaxTileCol>1045</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1497</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2083</MinTileCol><MaxTileCol>2090</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2994</MinTileRow><MaxTileRow>3005</MaxTileRow><MinTileCol>4167</MinTileCol><MaxTileCol>4181</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5988</MinTileRow><MaxTileRow>6011</MaxTileRow><MinTileCol>8334</MinTileCol><MaxTileCol>8362</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11977</MinTileRow><MaxTileRow>12022</MaxTileRow><MinTileCol>16668</MinTileCol><MaxTileCol>16725</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23954</MinTileRow><MaxTileRow>24045</MaxTileRow><MinTileCol>33337</MinTileCol><MaxTileCol>33451</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>261</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Le Cap d&apos;Agde 1981</ows:Title><ows:Abstract>Photographie du Cap d&apos;Agde 1981</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.01498 43.1889</ows:LowerCorner><ows:UpperCorner>3.66552 43.3984</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CAP-DAGE1981</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>522</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1041</MinTileCol><MaxTileCol>1044</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2082</MinTileCol><MaxTileCol>2089</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2997</MinTileRow><MaxTileRow>3004</MaxTileRow><MinTileCol>4164</MinTileCol><MaxTileCol>4179</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5995</MinTileRow><MaxTileRow>6008</MaxTileRow><MinTileCol>8329</MinTileCol><MaxTileCol>8358</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11990</MinTileRow><MaxTileRow>12017</MaxTileRow><MinTileCol>16658</MinTileCol><MaxTileCol>16717</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23981</MinTileRow><MaxTileRow>24034</MaxTileRow><MinTileCol>33316</MinTileCol><MaxTileCol>33435</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>261</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - CLERMONT FERRAND 1965</ows:Title><ows:Abstract>Photographie - CLERMONT FERRAND 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.82733 45.7156</ows:LowerCorner><ows:UpperCorner>3.29021 45.9596</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CLERMONT-FERRAND1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>730</MaxTileRow><MinTileCol>1040</MinTileCol><MaxTileCol>1042</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1461</MaxTileRow><MinTileCol>2080</MinTileCol><MaxTileCol>2085</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2915</MinTileRow><MaxTileRow>2923</MaxTileRow><MinTileCol>4160</MinTileCol><MaxTileCol>4170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5831</MinTileRow><MaxTileRow>5847</MaxTileRow><MinTileCol>8320</MinTileCol><MaxTileCol>8341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11662</MinTileRow><MaxTileRow>11694</MaxTileRow><MinTileCol>16641</MinTileCol><MaxTileCol>16683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23325</MinTileRow><MaxTileRow>23389</MaxTileRow><MinTileCol>33282</MinTileCol><MaxTileCol>33366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - CLERMONT FERRAND 1985</ows:Title><ows:Abstract>Photographie - CLERMONT FERRAND 1985</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.88988 45.7069</ows:LowerCorner><ows:UpperCorner>3.2777 45.9423</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CLERMONT-FERRAND1985</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>520</MinTileCol><MaxTileCol>521</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>729</MinTileRow><MaxTileRow>730</MaxTileRow><MinTileCol>1040</MinTileCol><MaxTileCol>1042</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1458</MinTileRow><MaxTileRow>1461</MaxTileRow><MinTileCol>2080</MinTileCol><MaxTileCol>2085</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2916</MinTileRow><MaxTileRow>2923</MaxTileRow><MinTileCol>4161</MinTileCol><MaxTileCol>4170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5832</MinTileRow><MaxTileRow>5847</MaxTileRow><MinTileCol>8323</MinTileCol><MaxTileCol>8341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11665</MinTileRow><MaxTileRow>11695</MaxTileRow><MinTileCol>16647</MinTileCol><MaxTileCol>16682</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23330</MinTileRow><MaxTileRow>23391</MaxTileRow><MinTileCol>33294</MinTileCol><MaxTileCol>33364</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - CREIL SUD PICARDIE 1975</ows:Title><ows:Abstract>Photographie - CREIL SUD PICARDIE 1975</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.25185 49.076</ows:LowerCorner><ows:UpperCorner>2.96494 49.5085</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.CREIL-SUD-PICARDIE1975</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>702</MaxTileRow><MinTileCol>1036</MinTileCol><MaxTileCol>1040</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1397</MinTileRow><MaxTileRow>1405</MaxTileRow><MinTileCol>2073</MinTileCol><MaxTileCol>2081</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2795</MinTileRow><MaxTileRow>2810</MaxTileRow><MinTileCol>4147</MinTileCol><MaxTileCol>4163</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5591</MinTileRow><MaxTileRow>5621</MaxTileRow><MinTileCol>8294</MinTileCol><MaxTileCol>8326</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11182</MinTileRow><MaxTileRow>11242</MaxTileRow><MinTileCol>16588</MinTileCol><MaxTileCol>16653</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22364</MinTileRow><MaxTileRow>22485</MaxTileRow><MinTileCol>33177</MinTileCol><MaxTileCol>33307</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Dijon 1971</ows:Title><ows:Abstract>Photographie de Dijon 1971</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.87902 47.1893</ows:LowerCorner><ows:UpperCorner>5.24181 47.3676</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.DIJON1971</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>358</MinTileRow><MaxTileRow>359</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>717</MinTileRow><MaxTileRow>718</MaxTileRow><MinTileCol>1051</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1434</MinTileRow><MaxTileRow>1437</MaxTileRow><MinTileCol>2103</MinTileCol><MaxTileCol>2107</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2869</MinTileRow><MaxTileRow>2875</MaxTileRow><MinTileCol>4207</MinTileCol><MaxTileCol>4215</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5738</MinTileRow><MaxTileRow>5750</MaxTileRow><MinTileCol>8414</MinTileCol><MaxTileCol>8430</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11476</MinTileRow><MaxTileRow>11500</MaxTileRow><MinTileCol>16828</MinTileCol><MaxTileCol>16861</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22952</MinTileRow><MaxTileRow>23000</MaxTileRow><MinTileCol>33656</MinTileCol><MaxTileCol>33722</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>179</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Grenoble 1966</ows:Title><ows:Abstract>Photographie de Grenoble 1966</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.61712 45.0655</ows:LowerCorner><ows:UpperCorner>5.94239 45.286</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.GRENOBLE1966</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>367</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>734</MinTileRow><MaxTileRow>736</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1057</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1468</MinTileRow><MaxTileRow>1472</MaxTileRow><MinTileCol>2111</MinTileCol><MaxTileCol>2115</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2937</MinTileRow><MaxTileRow>2944</MaxTileRow><MinTileCol>4223</MinTileCol><MaxTileCol>4231</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5875</MinTileRow><MaxTileRow>5889</MaxTileRow><MinTileCol>8447</MinTileCol><MaxTileCol>8462</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11750</MinTileRow><MaxTileRow>11779</MaxTileRow><MinTileCol>16895</MinTileCol><MaxTileCol>16924</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23501</MinTileRow><MaxTileRow>23558</MaxTileRow><MinTileCol>33790</MinTileCol><MaxTileCol>33849</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Grenoble 1989</ows:Title><ows:Abstract>Photographie de Grenoble 1989</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.54206 45.0566</ows:LowerCorner><ows:UpperCorner>5.96741 45.3387</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.GRENOBLE1989</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>733</MinTileRow><MaxTileRow>736</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1057</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1467</MinTileRow><MaxTileRow>1472</MaxTileRow><MinTileCol>2111</MinTileCol><MaxTileCol>2115</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2935</MinTileRow><MaxTileRow>2945</MaxTileRow><MinTileCol>4222</MinTileCol><MaxTileCol>4231</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5871</MinTileRow><MaxTileRow>5890</MaxTileRow><MinTileCol>8444</MinTileCol><MaxTileCol>8463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11743</MinTileRow><MaxTileRow>11780</MaxTileRow><MinTileCol>16888</MinTileCol><MaxTileCol>16927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23487</MinTileRow><MaxTileRow>23560</MaxTileRow><MinTileCol>33776</MinTileCol><MaxTileCol>33854</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - GUADELOUPE 1984</ows:Title><ows:Abstract>Photographie - GUADELOUPE 1984</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.7633 15.9411</ows:LowerCorner><ows:UpperCorner>-61.4756 16.3376</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.GUADELOUPE1984</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>464</MinTileRow><MaxTileRow>466</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>337</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>929</MinTileRow><MaxTileRow>932</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>674</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1859</MinTileRow><MaxTileRow>1864</MaxTileRow><MinTileCol>1345</MinTileCol><MaxTileCol>1348</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3719</MinTileRow><MaxTileRow>3728</MaxTileRow><MinTileCol>2690</MinTileCol><MaxTileCol>2697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>7438</MinTileRow><MaxTileRow>7456</MaxTileRow><MinTileCol>5381</MinTileCol><MaxTileCol>5394</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>14876</MinTileRow><MaxTileRow>14913</MaxTileRow><MinTileCol>10762</MinTileCol><MaxTileCol>10788</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>29752</MinTileRow><MaxTileRow>29827</MaxTileRow><MinTileCol>21524</MinTileCol><MaxTileCol>21576</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>29</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>21</MinTileCol><MaxTileCol>21</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>58</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>42</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>116</MinTileRow><MaxTileRow>116</MaxTileRow><MinTileCol>84</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>232</MinTileRow><MaxTileRow>233</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - GUYANE 1955</ows:Title><ows:Abstract>Photographie - Guyane 1955</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-54.1321 5.384</ows:LowerCorner><ows:UpperCorner>-53.5065 5.79487</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.GUYANE1955</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>495</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>358</MinTileCol><MaxTileCol>359</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>990</MinTileRow><MaxTileRow>993</MaxTileRow><MinTileCol>716</MinTileCol><MaxTileCol>719</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1981</MinTileRow><MaxTileRow>1986</MaxTileRow><MinTileCol>1432</MinTileCol><MaxTileCol>1439</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3963</MinTileRow><MaxTileRow>3973</MaxTileRow><MinTileCol>2864</MinTileCol><MaxTileCol>2878</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>7927</MinTileRow><MaxTileRow>7946</MaxTileRow><MinTileCol>5728</MinTileCol><MaxTileCol>5756</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>15855</MinTileRow><MaxTileRow>15893</MaxTileRow><MinTileCol>11456</MinTileCol><MaxTileCol>11513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>31711</MinTileRow><MaxTileRow>31786</MaxTileRow><MinTileCol>22913</MinTileCol><MaxTileCol>23027</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>30</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>22</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>61</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>44</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>123</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>89</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>247</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>179</MinTileCol><MaxTileCol>179</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LA REUNION 1961</ows:Title><ows:Abstract>Photographie - LA REUNION 1961</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.258 -21.3906</ows:LowerCorner><ows:UpperCorner>55.5833 -20.8538</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-REUNION1961</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>669</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1145</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>1338</MinTileCol><MaxTileCol>1340</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2290</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>2676</MinTileCol><MaxTileCol>2680</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4581</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>5353</MinTileCol><MaxTileCol>5360</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9162</MinTileRow><MaxTileRow>9188</MaxTileRow><MinTileCol>10706</MinTileCol><MaxTileCol>10721</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18325</MinTileRow><MaxTileRow>18377</MaxTileRow><MinTileCol>21413</MinTileCol><MaxTileCol>21443</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36651</MinTileRow><MaxTileRow>36755</MaxTileRow><MinTileCol>42827</MinTileCol><MaxTileCol>42886</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LA REUNION 1980</ows:Title><ows:Abstract>Photographie - LA REUNION 1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.1579 -21.4372</ows:LowerCorner><ows:UpperCorner>55.6583 -20.7836</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-REUNION1980</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>668</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1144</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>1337</MinTileCol><MaxTileCol>1340</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2289</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>2675</MinTileCol><MaxTileCol>2681</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4579</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>5351</MinTileCol><MaxTileCol>5362</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9159</MinTileRow><MaxTileRow>9191</MaxTileRow><MinTileCol>10702</MinTileCol><MaxTileCol>10725</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18318</MinTileRow><MaxTileRow>18382</MaxTileRow><MinTileCol>21404</MinTileCol><MaxTileCol>21450</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36637</MinTileRow><MaxTileRow>36764</MaxTileRow><MinTileCol>42809</MinTileCol><MaxTileCol>42900</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LA REUNION 1989</ows:Title><ows:Abstract>Photographie - LA REUNION 1989</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.2079 -21.4022</ows:LowerCorner><ows:UpperCorner>55.7334 -20.8538</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-REUNION1989</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>669</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1145</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>1338</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2290</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>2676</MinTileCol><MaxTileCol>2682</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4581</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>5352</MinTileCol><MaxTileCol>5364</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9162</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>10704</MinTileCol><MaxTileCol>10728</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18325</MinTileRow><MaxTileRow>18379</MaxTileRow><MinTileCol>21409</MinTileCol><MaxTileCol>21456</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36651</MinTileRow><MaxTileRow>36758</MaxTileRow><MinTileCol>42818</MinTileCol><MaxTileCol>42913</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT Ls Réunion 2010</ows:Title><ows:Abstract>Zone de la Réunion - 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.0453 -21.4954</ows:LowerCorner><ows:UpperCorner>55.921 -20.6783</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-REUNION2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>668</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1144</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>1337</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2288</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>2674</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4577</MinTileRow><MaxTileRow>4597</MaxTileRow><MinTileCol>5348</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9154</MinTileRow><MaxTileRow>9194</MaxTileRow><MinTileCol>10697</MinTileCol><MaxTileCol>10737</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18308</MinTileRow><MaxTileRow>18388</MaxTileRow><MinTileCol>21394</MinTileCol><MaxTileCol>21474</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36616</MinTileRow><MaxTileRow>36776</MaxTileRow><MinTileCol>42788</MinTileCol><MaxTileCol>42948</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LA ROCHELLE ROCHEFORT 1950</ows:Title><ows:Abstract>Photographie LA ROCHELLE ROCHEFORT 1950</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.06338 45.0566</ows:LowerCorner><ows:UpperCorner>5.96741 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-ROCHELLE-ROCHEFORT1950</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>368</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>736</MaxTileRow><MinTileCol>1017</MinTileCol><MaxTileCol>1057</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1472</MaxTileRow><MinTileCol>2035</MinTileCol><MaxTileCol>2115</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2945</MaxTileRow><MinTileCol>4071</MinTileCol><MaxTileCol>4231</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5890</MaxTileRow><MinTileCol>8143</MinTileCol><MaxTileCol>8463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11780</MaxTileRow><MinTileCol>16287</MinTileCol><MaxTileCol>16927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23560</MaxTileRow><MinTileCol>32574</MinTileCol><MaxTileCol>33854</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LA-ROCHELLE-ROCHEFORT 1973</ows:Title><ows:Abstract>Photographie de La Rochelle Rochefort 1973</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.37613 46.0726</ows:LowerCorner><ows:UpperCorner>-1.03835 46.2286</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LA-ROCHELLE-ROCHEFORT1973</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>363</MinTileRow><MaxTileRow>364</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>726</MinTileRow><MaxTileRow>728</MaxTileRow><MinTileCol>1016</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1453</MinTileRow><MaxTileRow>1456</MaxTileRow><MinTileCol>2032</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2906</MinTileRow><MaxTileRow>2912</MaxTileRow><MinTileCol>4064</MinTileCol><MaxTileCol>4072</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5813</MinTileRow><MaxTileRow>5824</MaxTileRow><MinTileCol>8129</MinTileCol><MaxTileCol>8144</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11627</MinTileRow><MaxTileRow>11648</MaxTileRow><MinTileCol>16258</MinTileCol><MaxTileCol>16289</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23255</MinTileRow><MaxTileRow>23296</MaxTileRow><MinTileCol>32517</MinTileCol><MaxTileCol>32578</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Le Havre 1955</ows:Title><ows:Abstract>Photographie du Havre 1955</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.0500412 49.4597</ows:LowerCorner><ows:UpperCorner>0.387819 49.5409</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LE-HAVRE1955</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>349</MaxTileRow><MinTileCol>512</MinTileCol><MaxTileCol>513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>699</MaxTileRow><MinTileCol>1024</MinTileCol><MaxTileCol>1026</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1397</MinTileRow><MaxTileRow>1398</MaxTileRow><MinTileCol>2048</MinTileCol><MaxTileCol>2052</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2794</MinTileRow><MaxTileRow>2797</MaxTileRow><MinTileCol>4097</MinTileCol><MaxTileCol>4104</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5588</MinTileRow><MaxTileRow>5594</MaxTileRow><MinTileCol>8194</MinTileCol><MaxTileCol>8209</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11177</MinTileRow><MaxTileRow>11189</MaxTileRow><MinTileCol>16388</MinTileCol><MaxTileCol>16419</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22355</MinTileRow><MaxTileRow>22378</MaxTileRow><MinTileCol>32777</MinTileCol><MaxTileCol>32838</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>174</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Le Havre 1964</ows:Title><ows:Abstract>Photographie du Havre 1964</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>0.0500412 49.4434</ows:LowerCorner><ows:UpperCorner>0.375309 49.622</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LE-HAVRE1964</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>348</MinTileRow><MaxTileRow>349</MaxTileRow><MinTileCol>512</MinTileCol><MaxTileCol>513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>697</MinTileRow><MaxTileRow>699</MaxTileRow><MinTileCol>1024</MinTileCol><MaxTileCol>1026</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1395</MinTileRow><MaxTileRow>1398</MaxTileRow><MinTileCol>2048</MinTileCol><MaxTileCol>2052</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2791</MinTileRow><MaxTileRow>2797</MaxTileRow><MinTileCol>4097</MinTileCol><MaxTileCol>4104</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5583</MinTileRow><MaxTileRow>5595</MaxTileRow><MinTileCol>8194</MinTileCol><MaxTileCol>8209</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11166</MinTileRow><MaxTileRow>11191</MaxTileRow><MinTileCol>16388</MinTileCol><MaxTileCol>16418</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22332</MinTileRow><MaxTileRow>22382</MaxTileRow><MinTileCol>32777</MinTileCol><MaxTileCol>32836</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>174</MaxTileRow><MinTileCol>256</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT LE HAVRE 2008</ows:Title><ows:Abstract>Zone du HAVRE - 2008</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-0.375309 49.035</ows:LowerCorner><ows:UpperCorner>1.00082 49.8484</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LE-HAVRE2008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>347</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>510</MinTileCol><MaxTileCol>514</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>695</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1021</MinTileCol><MaxTileCol>1029</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1391</MinTileRow><MaxTileRow>1406</MaxTileRow><MinTileCol>2043</MinTileCol><MaxTileCol>2059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2783</MinTileRow><MaxTileRow>2812</MaxTileRow><MinTileCol>4087</MinTileCol><MaxTileCol>4118</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5567</MinTileRow><MaxTileRow>5624</MaxTileRow><MinTileCol>8174</MinTileCol><MaxTileCol>8237</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11134</MinTileRow><MaxTileRow>11248</MaxTileRow><MinTileCol>16349</MinTileCol><MaxTileCol>16475</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22269</MinTileRow><MaxTileRow>22496</MaxTileRow><MinTileCol>32699</MinTileCol><MaxTileCol>32950</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>86</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>173</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>255</MinTileCol><MaxTileCol>257</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Limoges 1974</ows:Title><ows:Abstract>Photographie de Limoges 1974</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.00082 45.7069</ows:LowerCorner><ows:UpperCorner>1.42617 45.9596</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LIMOGES1974</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>514</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>730</MaxTileRow><MinTileCol>1029</MinTileCol><MaxTileCol>1032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1461</MaxTileRow><MinTileCol>2059</MinTileCol><MaxTileCol>2064</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2915</MinTileRow><MaxTileRow>2923</MaxTileRow><MinTileCol>4118</MinTileCol><MaxTileCol>4128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5831</MinTileRow><MaxTileRow>5847</MaxTileRow><MinTileCol>8237</MinTileCol><MaxTileCol>8256</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11662</MinTileRow><MaxTileRow>11695</MaxTileRow><MinTileCol>16475</MinTileCol><MaxTileCol>16513</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23325</MinTileRow><MaxTileRow>23391</MaxTileRow><MinTileCol>32950</MinTileCol><MaxTileCol>33027</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LYON 1965</ows:Title><ows:Abstract>Photographie - LYON 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.71638 45.5494</ows:LowerCorner><ows:UpperCorner>5.07918 45.8987</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LYON1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>729</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1052</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1458</MinTileRow><MaxTileRow>1464</MaxTileRow><MinTileCol>2101</MinTileCol><MaxTileCol>2105</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2917</MinTileRow><MaxTileRow>2929</MaxTileRow><MinTileCol>4203</MinTileCol><MaxTileCol>4211</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5835</MinTileRow><MaxTileRow>5858</MaxTileRow><MinTileCol>8406</MinTileCol><MaxTileCol>8423</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11670</MinTileRow><MaxTileRow>11716</MaxTileRow><MinTileCol>16813</MinTileCol><MaxTileCol>16846</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23341</MinTileRow><MaxTileRow>23432</MaxTileRow><MinTileCol>33626</MinTileCol><MaxTileCol>33692</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>46683</MinTileRow><MaxTileRow>46865</MaxTileRow><MinTileCol>67253</MinTileCol><MaxTileCol>67385</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>93366</MinTileRow><MaxTileRow>93731</MaxTileRow><MinTileCol>134506</MinTileCol><MaxTileCol>134770</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LYON 1984</ows:Title><ows:Abstract>Photographie - LYON 1984</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.70387 45.7069</ows:LowerCorner><ows:UpperCorner>5.17926 45.8726</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LYON1984</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>365</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>729</MinTileRow><MaxTileRow>730</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1459</MinTileRow><MaxTileRow>1461</MaxTileRow><MinTileCol>2101</MinTileCol><MaxTileCol>2106</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2918</MinTileRow><MaxTileRow>2923</MaxTileRow><MinTileCol>4203</MinTileCol><MaxTileCol>4213</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5837</MinTileRow><MaxTileRow>5847</MaxTileRow><MinTileCol>8406</MinTileCol><MaxTileCol>8427</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11674</MinTileRow><MaxTileRow>11695</MaxTileRow><MinTileCol>16812</MinTileCol><MaxTileCol>16855</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23348</MinTileRow><MaxTileRow>23391</MaxTileRow><MinTileCol>33624</MinTileCol><MaxTileCol>33710</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>182</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - LYON 1988</ows:Title><ows:Abstract>Photographie - LYON 1988</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.62881 45.5319</ows:LowerCorner><ows:UpperCorner>5.12922 45.9161</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LYON1988</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>366</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>729</MinTileRow><MaxTileRow>732</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1458</MinTileRow><MaxTileRow>1464</MaxTileRow><MinTileCol>2100</MinTileCol><MaxTileCol>2106</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2917</MinTileRow><MaxTileRow>2929</MaxTileRow><MinTileCol>4201</MinTileCol><MaxTileCol>4212</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5834</MinTileRow><MaxTileRow>5859</MaxTileRow><MinTileCol>8402</MinTileCol><MaxTileCol>8425</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11668</MinTileRow><MaxTileRow>11718</MaxTileRow><MinTileCol>16805</MinTileCol><MaxTileCol>16850</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23337</MinTileRow><MaxTileRow>23437</MaxTileRow><MinTileCol>33610</MinTileCol><MaxTileCol>33701</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT LYON 2008</ows:Title><ows:Abstract>Zone de LYON - 2008</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.50371 45.2067</ows:LowerCorner><ows:UpperCorner>5.75474 45.9944</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.LYON2008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>367</MaxTileRow><MinTileCol>524</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>735</MaxTileRow><MinTileCol>1049</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1470</MaxTileRow><MinTileCol>2099</MinTileCol><MaxTileCol>2113</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>2940</MaxTileRow><MinTileCol>4198</MinTileCol><MaxTileCol>4226</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5829</MinTileRow><MaxTileRow>5880</MaxTileRow><MinTileCol>8396</MinTileCol><MaxTileCol>8453</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11658</MinTileRow><MaxTileRow>11760</MaxTileRow><MinTileCol>16793</MinTileCol><MaxTileCol>16907</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23316</MinTileRow><MaxTileRow>23521</MaxTileRow><MinTileCol>33587</MinTileCol><MaxTileCol>33815</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>91</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>183</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Marne La Vallée 1965</ows:Title><ows:Abstract>Photographie de Marne la Vallée 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.40198 48.846</ows:LowerCorner><ows:UpperCorner>2.93992 48.9858</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARNE-LA-VALLEE1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1040</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2081</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2813</MinTileRow><MaxTileRow>2818</MaxTileRow><MinTileCol>4150</MinTileCol><MaxTileCol>4162</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5627</MinTileRow><MaxTileRow>5637</MaxTileRow><MinTileCol>8301</MinTileCol><MaxTileCol>8325</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11255</MinTileRow><MaxTileRow>11274</MaxTileRow><MinTileCol>16602</MinTileCol><MaxTileCol>16651</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22510</MinTileRow><MaxTileRow>22549</MaxTileRow><MinTileCol>33205</MinTileCol><MaxTileCol>33303</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Marne La Vallée 1977</ows:Title><ows:Abstract>Photographie de Marne la Vallée 1977</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.48955 48.8131</ows:LowerCorner><ows:UpperCorner>2.92741 48.9694</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARNE-LA-VALLEE1977</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>519</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1038</MinTileCol><MaxTileCol>1040</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1407</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2076</MinTileCol><MaxTileCol>2081</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2814</MinTileRow><MaxTileRow>2819</MaxTileRow><MinTileCol>4152</MinTileCol><MaxTileCol>4162</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5628</MinTileRow><MaxTileRow>5639</MaxTileRow><MinTileCol>8305</MinTileCol><MaxTileCol>8325</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11257</MinTileRow><MaxTileRow>11279</MaxTileRow><MinTileCol>16610</MinTileCol><MaxTileCol>16650</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22515</MinTileRow><MaxTileRow>22558</MaxTileRow><MinTileCol>33221</MinTileCol><MaxTileCol>33300</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Marne la Vallée 1987</ows:Title><ows:Abstract>Photographie de Marne la Vallée 1987</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.47704 48.8625</ows:LowerCorner><ows:UpperCorner>2.92741 48.9447</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARNE-LA-VALLEE1987</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>519</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1038</MinTileCol><MaxTileCol>1040</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1407</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2076</MinTileCol><MaxTileCol>2081</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2815</MinTileRow><MaxTileRow>2818</MaxTileRow><MinTileCol>4152</MinTileCol><MaxTileCol>4162</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5630</MinTileRow><MaxTileRow>5636</MaxTileRow><MinTileCol>8304</MinTileCol><MaxTileCol>8325</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11260</MinTileRow><MaxTileRow>11272</MaxTileRow><MinTileCol>16609</MinTileCol><MaxTileCol>16650</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22521</MinTileRow><MaxTileRow>22544</MaxTileRow><MinTileCol>33218</MinTileCol><MaxTileCol>33300</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - MARSEILLE MARTIGUES 1969</ows:Title><ows:Abstract>Photographie MARSEILLE MARTIGUES1969</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.20428 43.1798</ows:LowerCorner><ows:UpperCorner>5.69219 43.3984</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARSEILLE-MARTIGUES1969</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>526</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1053</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2107</MinTileCol><MaxTileCol>2112</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2997</MinTileRow><MaxTileRow>3004</MaxTileRow><MinTileCol>4214</MinTileCol><MaxTileCol>4225</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5995</MinTileRow><MaxTileRow>6009</MaxTileRow><MinTileCol>8428</MinTileCol><MaxTileCol>8451</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11990</MinTileRow><MaxTileRow>12018</MaxTileRow><MinTileCol>16857</MinTileCol><MaxTileCol>16902</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23981</MinTileRow><MaxTileRow>24036</MaxTileRow><MinTileCol>33715</MinTileCol><MaxTileCol>33804</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - MARSEILLE MARTIGUES 1980</ows:Title><ows:Abstract>Photographie MARSEILLE MARTIGUES1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.67885 43.3165</ows:LowerCorner><ows:UpperCorner>5.29186 43.5708</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARSEILLE-MARTIGUES1980</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>527</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1054</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1496</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>2101</MinTileCol><MaxTileCol>2108</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2992</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>4202</MinTileCol><MaxTileCol>4216</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5984</MinTileRow><MaxTileRow>6000</MaxTileRow><MinTileCol>8404</MinTileCol><MaxTileCol>8432</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11969</MinTileRow><MaxTileRow>12001</MaxTileRow><MinTileCol>16809</MinTileCol><MaxTileCol>16865</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23938</MinTileRow><MaxTileRow>24002</MaxTileRow><MinTileCol>33619</MinTileCol><MaxTileCol>33731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - MARSEILLE MARTIGUES 1987</ows:Title><ows:Abstract>Photographie MARSEILLE MARTIGUES1987</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.65383 43.3165</ows:LowerCorner><ows:UpperCorner>5.1042 43.5708</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARSEILLE-MARTIGUES1987</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>1050</MinTileCol><MaxTileCol>1053</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1496</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>2100</MinTileCol><MaxTileCol>2106</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2992</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>4201</MinTileCol><MaxTileCol>4212</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5984</MinTileRow><MaxTileRow>6000</MaxTileRow><MinTileCol>8403</MinTileCol><MaxTileCol>8424</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11969</MinTileRow><MaxTileRow>12001</MaxTileRow><MinTileCol>16807</MinTileCol><MaxTileCol>16848</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23938</MinTileRow><MaxTileRow>24002</MaxTileRow><MinTileCol>33615</MinTileCol><MaxTileCol>33697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - MARSEILLE MARTIGUES 1988</ows:Title><ows:Abstract>Photographie MARSEILLE MARTIGUES1988</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>4.91655 43.3711</ows:LowerCorner><ows:UpperCorner>5.39194 43.5708</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARSEILLE-MARTIGUES1988</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>374</MaxTileRow><MinTileCol>525</MinTileCol><MaxTileCol>527</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>748</MinTileRow><MaxTileRow>749</MaxTileRow><MinTileCol>1051</MinTileCol><MaxTileCol>1054</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1496</MinTileRow><MaxTileRow>1499</MaxTileRow><MinTileCol>2103</MinTileCol><MaxTileCol>2109</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2992</MinTileRow><MaxTileRow>2998</MaxTileRow><MinTileCol>4207</MinTileCol><MaxTileCol>4218</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5984</MinTileRow><MaxTileRow>5997</MaxTileRow><MinTileCol>8415</MinTileCol><MaxTileCol>8437</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11969</MinTileRow><MaxTileRow>11994</MaxTileRow><MinTileCol>16831</MinTileCol><MaxTileCol>16874</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23938</MinTileRow><MaxTileRow>23988</MaxTileRow><MinTileCol>33663</MinTileCol><MaxTileCol>33749</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>262</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT MARSEILLE-MARTIGUES 2010</ows:Title><ows:Abstract>Zone de MARSEILLE-MARTIGUES - 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.25433 43.1433</ows:LowerCorner><ows:UpperCorner>5.75474 43.4165</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARSEILLE-MARTIGUES2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>526</MinTileCol><MaxTileCol>528</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>751</MaxTileRow><MinTileCol>1053</MinTileCol><MaxTileCol>1056</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1502</MaxTileRow><MinTileCol>2107</MinTileCol><MaxTileCol>2113</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2997</MinTileRow><MaxTileRow>3005</MaxTileRow><MinTileCol>4215</MinTileCol><MaxTileCol>4226</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5994</MinTileRow><MaxTileRow>6011</MaxTileRow><MinTileCol>8431</MinTileCol><MaxTileCol>8453</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11988</MinTileRow><MaxTileRow>12022</MaxTileRow><MinTileCol>16862</MinTileCol><MaxTileCol>16907</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23977</MinTileRow><MaxTileRow>24045</MaxTileRow><MinTileCol>33724</MinTileCol><MaxTileCol>33815</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - La Martinique 1988</ows:Title><ows:Abstract>Photographie de la Martinique 1988</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.1378 14.3713</ows:LowerCorner><ows:UpperCorner>-60.8251 14.7104</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.MARTINIQUE1988</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>469</MinTileRow><MaxTileRow>470</MaxTileRow><MinTileCol>338</MinTileCol><MaxTileCol>338</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>939</MinTileRow><MaxTileRow>941</MaxTileRow><MinTileCol>676</MinTileCol><MaxTileCol>677</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1878</MinTileRow><MaxTileRow>1882</MaxTileRow><MinTileCol>1352</MinTileCol><MaxTileCol>1355</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3757</MinTileRow><MaxTileRow>3765</MaxTileRow><MinTileCol>2704</MinTileCol><MaxTileCol>2711</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>7515</MinTileRow><MaxTileRow>7530</MaxTileRow><MinTileCol>5409</MinTileCol><MaxTileCol>5423</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>15030</MinTileRow><MaxTileRow>15061</MaxTileRow><MinTileCol>10819</MinTileCol><MaxTileCol>10847</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>30060</MinTileRow><MaxTileRow>30123</MaxTileRow><MinTileCol>21638</MinTileCol><MaxTileCol>21695</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>29</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>21</MinTileCol><MaxTileCol>21</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>58</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>42</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>117</MinTileRow><MaxTileRow>117</MaxTileRow><MinTileCol>84</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>234</MinTileRow><MaxTileRow>235</MaxTileRow><MinTileCol>169</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - METZ NANCY 1982</ows:Title><ows:Abstract>Photographie - METZ NANCY 1982</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.9549 48.5902</ows:LowerCorner><ows:UpperCorner>6.43029 49.3376</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.METZ-NANCY1982</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>530</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>706</MaxTileRow><MinTileCol>1057</MinTileCol><MaxTileCol>1060</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1400</MinTileRow><MaxTileRow>1413</MaxTileRow><MinTileCol>2115</MinTileCol><MaxTileCol>2121</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2801</MinTileRow><MaxTileRow>2827</MaxTileRow><MinTileCol>4231</MinTileCol><MaxTileCol>4242</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5603</MinTileRow><MaxTileRow>5654</MaxTileRow><MinTileCol>8463</MinTileCol><MaxTileCol>8484</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11206</MinTileRow><MaxTileRow>11309</MaxTileRow><MinTileCol>16926</MinTileCol><MaxTileCol>16969</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22412</MinTileRow><MaxTileRow>22619</MaxTileRow><MinTileCol>33852</MinTileCol><MaxTileCol>33938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44825</MinTileRow><MaxTileRow>45239</MaxTileRow><MinTileCol>67704</MinTileCol><MaxTileCol>67877</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>89650</MinTileRow><MaxTileRow>90479</MaxTileRow><MinTileCol>135408</MinTileCol><MaxTileCol>135754</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Nantes 1971</ows:Title><ows:Abstract>Photographie de Nantes 1971</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2.27687 47.1042</ows:LowerCorner><ows:UpperCorner>-1.40115 47.3591</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.NANTES1971</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>358</MinTileRow><MaxTileRow>359</MaxTileRow><MinTileCol>505</MinTileCol><MaxTileCol>508</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>717</MinTileRow><MaxTileRow>719</MaxTileRow><MinTileCol>1011</MinTileCol><MaxTileCol>1016</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1434</MinTileRow><MaxTileRow>1438</MaxTileRow><MinTileCol>2022</MinTileCol><MaxTileCol>2032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2869</MinTileRow><MaxTileRow>2877</MaxTileRow><MinTileCol>4044</MinTileCol><MaxTileCol>4064</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5738</MinTileRow><MaxTileRow>5755</MaxTileRow><MinTileCol>8088</MinTileCol><MaxTileCol>8128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11477</MinTileRow><MaxTileRow>11511</MaxTileRow><MinTileCol>16176</MinTileCol><MaxTileCol>16256</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22954</MinTileRow><MaxTileRow>23022</MaxTileRow><MinTileCol>32353</MinTileCol><MaxTileCol>32512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>179</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>252</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Paris 1949</ows:Title><ows:Abstract>Photographie - Paris 1949</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.20181 48.7636</ows:LowerCorner><ows:UpperCorner>2.52708 48.9529</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.PARIS1949</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>705</MaxTileRow><MinTileCol>1036</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1407</MinTileRow><MaxTileRow>1410</MaxTileRow><MinTileCol>2073</MinTileCol><MaxTileCol>2076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2814</MinTileRow><MaxTileRow>2821</MaxTileRow><MinTileCol>4146</MinTileCol><MaxTileCol>4153</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5629</MinTileRow><MaxTileRow>5642</MaxTileRow><MinTileCol>8292</MinTileCol><MaxTileCol>8307</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11259</MinTileRow><MaxTileRow>11285</MaxTileRow><MinTileCol>16584</MinTileCol><MaxTileCol>16614</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22519</MinTileRow><MaxTileRow>22571</MaxTileRow><MinTileCol>33168</MinTileCol><MaxTileCol>33228</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45039</MinTileRow><MaxTileRow>45143</MaxTileRow><MinTileCol>66337</MinTileCol><MaxTileCol>66456</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>90078</MinTileRow><MaxTileRow>90287</MaxTileRow><MinTileCol>132675</MinTileCol><MaxTileCol>132912</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Paris 1965</ows:Title><ows:Abstract>Photographie - Paris 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.02667 48.846</ows:LowerCorner><ows:UpperCorner>2.48955 48.9858</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.PARIS1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1035</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2071</MinTileCol><MaxTileCol>2076</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2813</MinTileRow><MaxTileRow>2818</MaxTileRow><MinTileCol>4142</MinTileCol><MaxTileCol>4152</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5627</MinTileRow><MaxTileRow>5637</MaxTileRow><MinTileCol>8284</MinTileCol><MaxTileCol>8305</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11255</MinTileRow><MaxTileRow>11274</MaxTileRow><MinTileCol>16568</MinTileCol><MaxTileCol>16610</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22510</MinTileRow><MaxTileRow>22549</MaxTileRow><MinTileCol>33136</MinTileCol><MaxTileCol>33221</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT PARIS 2010</ows:Title><ows:Abstract>Zone de Paris - SPOT 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.87654 48.5405</ows:LowerCorner><ows:UpperCorner>3.00247 49.362</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.PARIS2010SPOT</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1034</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1400</MinTileRow><MaxTileRow>1414</MaxTileRow><MinTileCol>2069</MinTileCol><MaxTileCol>2082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2800</MinTileRow><MaxTileRow>2829</MaxTileRow><MinTileCol>4138</MinTileCol><MaxTileCol>4164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5601</MinTileRow><MaxTileRow>5658</MaxTileRow><MinTileCol>8277</MinTileCol><MaxTileCol>8328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11202</MinTileRow><MaxTileRow>11316</MaxTileRow><MinTileCol>16554</MinTileCol><MaxTileCol>16657</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22405</MinTileRow><MaxTileRow>22633</MaxTileRow><MinTileCol>33109</MinTileCol><MaxTileCol>33314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Reims 1963</ows:Title><ows:Abstract>Photographie de Reims 1963</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.95325 49.117</ows:LowerCorner><ows:UpperCorner>4.21597 49.2968</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.REIMS1963</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>523</MinTileCol><MaxTileCol>523</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>702</MaxTileRow><MinTileCol>1046</MinTileCol><MaxTileCol>1047</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1401</MinTileRow><MaxTileRow>1404</MaxTileRow><MinTileCol>2092</MinTileCol><MaxTileCol>2095</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2802</MinTileRow><MaxTileRow>2809</MaxTileRow><MinTileCol>4185</MinTileCol><MaxTileCol>4191</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5605</MinTileRow><MaxTileRow>5618</MaxTileRow><MinTileCol>8371</MinTileCol><MaxTileCol>8383</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11211</MinTileRow><MaxTileRow>11237</MaxTileRow><MinTileCol>16743</MinTileCol><MaxTileCol>16767</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22423</MinTileRow><MaxTileRow>22474</MaxTileRow><MinTileCol>33487</MinTileCol><MaxTileCol>33535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>261</MinTileCol><MaxTileCol>261</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Reims 1973</ows:Title><ows:Abstract>Photographie de Reims 1973</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.95325 49.1252</ows:LowerCorner><ows:UpperCorner>4.22848 49.2886</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.REIMS1973</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>523</MinTileCol><MaxTileCol>524</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>702</MaxTileRow><MinTileCol>1046</MinTileCol><MaxTileCol>1048</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1401</MinTileRow><MaxTileRow>1404</MaxTileRow><MinTileCol>2092</MinTileCol><MaxTileCol>2096</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2803</MinTileRow><MaxTileRow>2808</MaxTileRow><MinTileCol>4185</MinTileCol><MaxTileCol>4192</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5606</MinTileRow><MaxTileRow>5617</MaxTileRow><MinTileCol>8371</MinTileCol><MaxTileCol>8384</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11213</MinTileRow><MaxTileRow>11235</MaxTileRow><MinTileCol>16743</MinTileCol><MaxTileCol>16768</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22426</MinTileRow><MaxTileRow>22471</MaxTileRow><MinTileCol>33487</MinTileCol><MaxTileCol>33537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>261</MinTileCol><MaxTileCol>262</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Reims 1988</ows:Title><ows:Abstract>Photographie de Reims 1988</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.91572 49.1252</ows:LowerCorner><ows:UpperCorner>4.2535 49.2805</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.REIMS1988</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>523</MinTileCol><MaxTileCol>524</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>702</MaxTileRow><MinTileCol>1046</MinTileCol><MaxTileCol>1048</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1401</MinTileRow><MaxTileRow>1404</MaxTileRow><MinTileCol>2092</MinTileCol><MaxTileCol>2096</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2803</MinTileRow><MaxTileRow>2808</MaxTileRow><MinTileCol>4185</MinTileCol><MaxTileCol>4192</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5607</MinTileRow><MaxTileRow>5617</MaxTileRow><MinTileCol>8370</MinTileCol><MaxTileCol>8385</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11214</MinTileRow><MaxTileRow>11235</MaxTileRow><MinTileCol>16740</MinTileCol><MaxTileCol>16771</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22428</MinTileRow><MaxTileRow>22471</MaxTileRow><MinTileCol>33480</MinTileCol><MaxTileCol>33542</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>261</MinTileCol><MaxTileCol>262</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT REIMS 2010</ows:Title><ows:Abstract>Zone de Paris - REIMS 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>3.50288 48.9529</ows:LowerCorner><ows:UpperCorner>4.75391 49.362</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.REIMS2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>521</MinTileCol><MaxTileCol>525</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>700</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1043</MinTileCol><MaxTileCol>1051</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1400</MinTileRow><MaxTileRow>1407</MaxTileRow><MinTileCol>2087</MinTileCol><MaxTileCol>2102</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2800</MinTileRow><MaxTileRow>2814</MaxTileRow><MinTileCol>4175</MinTileCol><MaxTileCol>4204</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5601</MinTileRow><MaxTileRow>5629</MaxTileRow><MinTileCol>8351</MinTileCol><MaxTileCol>8408</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11202</MinTileRow><MaxTileRow>11259</MaxTileRow><MinTileCol>16702</MinTileCol><MaxTileCol>16816</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22405</MinTileRow><MaxTileRow>22519</MaxTileRow><MinTileCol>33405</MinTileCol><MaxTileCol>33633</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>130</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>260</MinTileCol><MaxTileCol>262</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - ROISSY 1965</ows:Title><ows:Abstract>Photographie - ROISSY 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.35194 48.9036</ows:LowerCorner><ows:UpperCorner>2.70222 49.0432</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.ROISSY1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>702</MinTileRow><MaxTileRow>704</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1405</MinTileRow><MaxTileRow>1408</MaxTileRow><MinTileCol>2074</MinTileCol><MaxTileCol>2078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2811</MinTileRow><MaxTileRow>2816</MaxTileRow><MinTileCol>4149</MinTileCol><MaxTileCol>4157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5623</MinTileRow><MaxTileRow>5633</MaxTileRow><MinTileCol>8299</MinTileCol><MaxTileCol>8314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11247</MinTileRow><MaxTileRow>11266</MaxTileRow><MinTileCol>16598</MinTileCol><MaxTileCol>16629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22494</MinTileRow><MaxTileRow>22533</MaxTileRow><MinTileCol>33196</MinTileCol><MaxTileCol>33259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44989</MinTileRow><MaxTileRow>45066</MaxTileRow><MinTileCol>66392</MinTileCol><MaxTileCol>66519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>89978</MinTileRow><MaxTileRow>90132</MaxTileRow><MinTileCol>132784</MinTileCol><MaxTileCol>133039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - ROISSY 1987</ows:Title><ows:Abstract>Photographie - ROISSY 1987</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.45202 48.9447</ows:LowerCorner><ows:UpperCorner>2.65218 49.035</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.ROISSY1987</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>351</MinTileRow><MaxTileRow>351</MaxTileRow><MinTileCol>518</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>703</MinTileRow><MaxTileRow>703</MaxTileRow><MinTileCol>1037</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1406</MinTileRow><MaxTileRow>1407</MaxTileRow><MinTileCol>2075</MinTileCol><MaxTileCol>2078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2812</MinTileRow><MaxTileRow>2815</MaxTileRow><MinTileCol>4151</MinTileCol><MaxTileCol>4156</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5624</MinTileRow><MaxTileRow>5630</MaxTileRow><MinTileCol>8303</MinTileCol><MaxTileCol>8312</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11248</MinTileRow><MaxTileRow>11260</MaxTileRow><MinTileCol>16607</MinTileCol><MaxTileCol>16625</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22496</MinTileRow><MaxTileRow>22521</MaxTileRow><MinTileCol>33214</MinTileCol><MaxTileCol>33250</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44993</MinTileRow><MaxTileRow>45043</MaxTileRow><MinTileCol>66428</MinTileCol><MaxTileCol>66501</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>89987</MinTileRow><MaxTileRow>90087</MaxTileRow><MinTileCol>132857</MinTileCol><MaxTileCol>133003</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>21</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>43</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>87</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>175</MaxTileRow><MinTileCol>259</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Strasbourg 1975</ows:Title><ows:Abstract>Photographie de Strasbourg 1975</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>7.31852 48.4078</ows:LowerCorner><ows:UpperCorner>7.93153 48.6563</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.STRASBOURG1975</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>353</MinTileRow><MaxTileRow>354</MaxTileRow><MinTileCol>532</MinTileCol><MaxTileCol>534</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>706</MinTileRow><MaxTileRow>708</MaxTileRow><MinTileCol>1065</MinTileCol><MaxTileCol>1069</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1412</MinTileRow><MaxTileRow>1416</MaxTileRow><MinTileCol>2131</MinTileCol><MaxTileCol>2138</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2825</MinTileRow><MaxTileRow>2833</MaxTileRow><MinTileCol>4262</MinTileCol><MaxTileCol>4276</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5650</MinTileRow><MaxTileRow>5667</MaxTileRow><MinTileCol>8525</MinTileCol><MaxTileCol>8552</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11300</MinTileRow><MaxTileRow>11334</MaxTileRow><MinTileCol>17050</MinTileCol><MaxTileCol>17105</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22601</MinTileRow><MaxTileRow>22669</MaxTileRow><MinTileCol>34100</MinTileCol><MaxTileCol>34211</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>133</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>266</MinTileCol><MaxTileCol>267</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Strasbourg 1985</ows:Title><ows:Abstract>Photographie de Strasbourg 1985</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>7.46865 48.3995</ows:LowerCorner><ows:UpperCorner>7.894 48.6232</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.STRASBOURG1985</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>353</MinTileRow><MaxTileRow>354</MaxTileRow><MinTileCol>533</MinTileCol><MaxTileCol>534</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>706</MinTileRow><MaxTileRow>708</MaxTileRow><MinTileCol>1066</MinTileCol><MaxTileCol>1068</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1413</MinTileRow><MaxTileRow>1417</MaxTileRow><MinTileCol>2132</MinTileCol><MaxTileCol>2137</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2826</MinTileRow><MaxTileRow>2834</MaxTileRow><MinTileCol>4265</MinTileCol><MaxTileCol>4275</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5652</MinTileRow><MaxTileRow>5668</MaxTileRow><MinTileCol>8531</MinTileCol><MaxTileCol>8551</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11305</MinTileRow><MaxTileRow>11336</MaxTileRow><MinTileCol>17063</MinTileCol><MaxTileCol>17102</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22610</MinTileRow><MaxTileRow>22672</MaxTileRow><MinTileCol>34127</MinTileCol><MaxTileCol>34205</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>133</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>266</MinTileCol><MaxTileCol>267</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT STRASBOURG 2010</ows:Title><ows:Abstract>Zone de Paris - STRASBOURG 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>7.13087 48.0411</ows:LowerCorner><ows:UpperCorner>8.2568 48.8707</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.STRASBOURG2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>352</MinTileRow><MaxTileRow>355</MaxTileRow><MinTileCol>532</MinTileCol><MaxTileCol>535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>704</MinTileRow><MaxTileRow>711</MaxTileRow><MinTileCol>1064</MinTileCol><MaxTileCol>1070</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1423</MaxTileRow><MinTileCol>2129</MinTileCol><MaxTileCol>2141</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2817</MinTileRow><MaxTileRow>2846</MaxTileRow><MinTileCol>4258</MinTileCol><MaxTileCol>4283</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5635</MinTileRow><MaxTileRow>5692</MaxTileRow><MinTileCol>8516</MinTileCol><MaxTileCol>8567</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11271</MinTileRow><MaxTileRow>11385</MaxTileRow><MinTileCol>17033</MinTileCol><MaxTileCol>17135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22542</MinTileRow><MaxTileRow>22770</MaxTileRow><MinTileCol>34066</MinTileCol><MaxTileCol>34271</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>133</MinTileCol><MaxTileCol>133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>266</MinTileCol><MaxTileCol>267</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - TOULON-HYERES 1972</ows:Title><ows:Abstract>Photographie de TOULON-HYERES 1972</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.74223 42.9696</ows:LowerCorner><ows:UpperCorner>6.33021 43.2163</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.TOULON-HYERES1972</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>375</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>530</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>750</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1056</MinTileCol><MaxTileCol>1060</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1501</MinTileRow><MaxTileRow>1505</MaxTileRow><MinTileCol>2113</MinTileCol><MaxTileCol>2120</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3003</MinTileRow><MaxTileRow>3011</MaxTileRow><MinTileCol>4226</MinTileCol><MaxTileCol>4240</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6006</MinTileRow><MaxTileRow>6022</MaxTileRow><MinTileCol>8453</MinTileCol><MaxTileCol>8480</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12013</MinTileRow><MaxTileRow>12044</MaxTileRow><MinTileCol>16906</MinTileCol><MaxTileCol>16960</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24027</MinTileRow><MaxTileRow>24088</MaxTileRow><MinTileCol>33813</MinTileCol><MaxTileCol>33920</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>265</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie -Toulouse 1954</ows:Title><ows:Abstract>Photographie de Toulouse 1954</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.23852 43.5618</ows:LowerCorner><ows:UpperCorner>1.55128 43.7247</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.TOULOUSE1954</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>373</MinTileRow><MaxTileRow>374</MaxTileRow><MinTileCol>515</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>746</MinTileRow><MaxTileRow>748</MaxTileRow><MinTileCol>1031</MinTileCol><MaxTileCol>1032</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1493</MinTileRow><MaxTileRow>1496</MaxTileRow><MinTileCol>2062</MinTileCol><MaxTileCol>2065</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2987</MinTileRow><MaxTileRow>2992</MaxTileRow><MinTileCol>4124</MinTileCol><MaxTileCol>4131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5974</MinTileRow><MaxTileRow>5985</MaxTileRow><MinTileCol>8248</MinTileCol><MaxTileCol>8262</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11949</MinTileRow><MaxTileRow>11970</MaxTileRow><MinTileCol>16496</MinTileCol><MaxTileCol>16525</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23899</MinTileRow><MaxTileRow>23940</MaxTileRow><MinTileCol>32993</MinTileCol><MaxTileCol>33050</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>128</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>186</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>257</MinTileCol><MaxTileCol>258</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT Vallée du Drac 2010</ows:Title><ows:Abstract>Zone de la Vallée du Drac - 2010</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.12922 44.7643</ows:LowerCorner><ows:UpperCorner>6.25515 45.0301</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.VALEE-DU-DRAC2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>368</MinTileRow><MaxTileRow>369</MaxTileRow><MinTileCol>526</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>736</MinTileRow><MaxTileRow>738</MaxTileRow><MinTileCol>1053</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1472</MinTileRow><MaxTileRow>1477</MaxTileRow><MinTileCol>2106</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2945</MinTileRow><MaxTileRow>2954</MaxTileRow><MinTileCol>4212</MinTileCol><MaxTileCol>4238</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5891</MinTileRow><MaxTileRow>5908</MaxTileRow><MinTileCol>8425</MinTileCol><MaxTileCol>8476</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11783</MinTileRow><MaxTileRow>11817</MaxTileRow><MinTileCol>16850</MinTileCol><MaxTileCol>16953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23567</MinTileRow><MaxTileRow>23635</MaxTileRow><MinTileCol>33701</MinTileCol><MaxTileCol>33906</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>92</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>184</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - VANNES GOLF DU MORBIHAN 1970</ows:Title><ows:Abstract>Photographie - VANNES GOLF DU MORBIHAN 1970</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.20264 47.4607</ows:LowerCorner><ows:UpperCorner>-2.62716 47.7138</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.VANNES-GOLF-DU-MORBIHAN1970</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>358</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>504</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>716</MaxTileRow><MinTileCol>1005</MinTileCol><MaxTileCol>1009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1428</MinTileRow><MaxTileRow>1432</MaxTileRow><MinTileCol>2011</MinTileCol><MaxTileCol>2018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2857</MinTileRow><MaxTileRow>2865</MaxTileRow><MinTileCol>4023</MinTileCol><MaxTileCol>4036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5714</MinTileRow><MaxTileRow>5731</MaxTileRow><MinTileCol>8046</MinTileCol><MaxTileCol>8072</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11429</MinTileRow><MaxTileRow>11463</MaxTileRow><MinTileCol>16092</MinTileCol><MaxTileCol>16144</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22858</MinTileRow><MaxTileRow>22927</MaxTileRow><MinTileCol>32184</MinTileCol><MaxTileCol>32289</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45717</MinTileRow><MaxTileRow>45854</MaxTileRow><MinTileCol>64369</MinTileCol><MaxTileCol>64579</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91435</MinTileRow><MaxTileRow>91708</MaxTileRow><MinTileCol>128739</MinTileCol><MaxTileCol>129158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>89</MinTileRow><MaxTileRow>89</MaxTileRow><MinTileCol>125</MinTileCol><MaxTileCol>126</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>178</MinTileRow><MaxTileRow>179</MaxTileRow><MinTileCol>251</MinTileCol><MaxTileCol>252</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Versailles 1965</ows:Title><ows:Abstract>Photographie - Versailles 1965</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.98914 48.6067</ows:LowerCorner><ows:UpperCorner>2.32692 48.8789</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.VERSAILLES1965</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>352</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>518</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>704</MinTileRow><MaxTileRow>706</MaxTileRow><MinTileCol>1035</MinTileCol><MaxTileCol>1037</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1413</MaxTileRow><MinTileCol>2070</MinTileCol><MaxTileCol>2074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2817</MinTileRow><MaxTileRow>2826</MaxTileRow><MinTileCol>4141</MinTileCol><MaxTileCol>4148</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5635</MinTileRow><MaxTileRow>5653</MaxTileRow><MinTileCol>8282</MinTileCol><MaxTileCol>8297</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11270</MinTileRow><MaxTileRow>11307</MaxTileRow><MinTileCol>16565</MinTileCol><MaxTileCol>16595</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22540</MinTileRow><MaxTileRow>22615</MaxTileRow><MinTileCol>33130</MinTileCol><MaxTileCol>33191</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45080</MinTileRow><MaxTileRow>45230</MaxTileRow><MinTileCol>66260</MinTileCol><MaxTileCol>66383</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>90160</MinTileRow><MaxTileRow>90460</MaxTileRow><MinTileCol>132520</MinTileCol><MaxTileCol>132766</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographie - Versailles 1989</ows:Title><ows:Abstract>Photographie de Versailles 1989</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.87654 48.6315</ows:LowerCorner><ows:UpperCorner>2.33943 48.8789</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.EDUGEO.VERSAILLES1989</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>352</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>518</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>704</MinTileRow><MaxTileRow>706</MaxTileRow><MinTileCol>1034</MinTileCol><MaxTileCol>1037</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1413</MaxTileRow><MinTileCol>2069</MinTileCol><MaxTileCol>2074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2817</MinTileRow><MaxTileRow>2826</MaxTileRow><MinTileCol>4138</MinTileCol><MaxTileCol>4149</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5635</MinTileRow><MaxTileRow>5652</MaxTileRow><MinTileCol>8277</MinTileCol><MaxTileCol>8298</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11270</MinTileRow><MaxTileRow>11304</MaxTileRow><MinTileCol>16554</MinTileCol><MaxTileCol>16596</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22540</MinTileRow><MaxTileRow>22608</MaxTileRow><MinTileCol>33109</MinTileCol><MaxTileCol>33193</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>44</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>88</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>176</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes</ows:Title><ows:Abstract>Photographies aériennes</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178 -84</ows:LowerCorner><ows:UpperCorner>178 84</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>31</MinTileRow><MaxTileRow>992</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>62</MinTileRow><MaxTileRow>1985</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>125</MinTileRow><MaxTileRow>3970</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4635</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>7924</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9270</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>15848</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18540</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>31697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>37072</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>63395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>74144</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>126791</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>148272</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>253582</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>178772</MinTileRow><MaxTileRow>294060</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>7</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>15</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Ortholittorale 2000</ows:Title><ows:Abstract>Donnée issue du Référentiel géographique du littoral. Zone de couverture de l&apos;orthophotographie : côte de la mer du Nord, de la Manche et de l&apos;Atlantique.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.21565 43.301</ows:LowerCorner><ows:UpperCorner>2.60783 51.1233</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.COAST2000</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>375</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>750</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1038</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1500</MaxTileRow><MinTileCol>1988</MinTileCol><MaxTileCol>2077</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2738</MinTileRow><MaxTileRow>3000</MaxTileRow><MinTileCol>3977</MinTileCol><MaxTileCol>4155</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5476</MinTileRow><MaxTileRow>6001</MaxTileRow><MinTileCol>7954</MinTileCol><MaxTileCol>8310</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>12003</MaxTileRow><MinTileCol>15909</MinTileCol><MaxTileCol>16621</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24006</MaxTileRow><MinTileCol>31818</MinTileCol><MaxTileCol>33242</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43808</MinTileRow><MaxTileRow>48012</MaxTileRow><MinTileCol>63637</MinTileCol><MaxTileCol>66485</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87617</MinTileRow><MaxTileRow>96024</MaxTileRow><MinTileCol>127274</MinTileCol><MaxTileCol>132970</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>93</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>187</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies arériennes - (CRS84)</ows:Title><ows:Abstract>Orthophotographies</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.CRS84</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>221</MinTileRow><MaxTileRow>633</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>442</MinTileRow><MaxTileRow>1267</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>885</MinTileRow><MaxTileRow>2534</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>1770</MinTileRow><MaxTileRow>5069</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>3541</MinTileRow><MaxTileRow>10139</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>7082</MinTileRow><MaxTileRow>20279</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>14165</MinTileRow><MaxTileRow>40559</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>28330</MinTileRow><MaxTileRow>81118</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>79</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>55</MinTileRow><MaxTileRow>158</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>110</MinTileRow><MaxTileRow>316</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Suisse - photographies aériennes du canton de Genève (SITG)</ows:Title><ows:Abstract>Photographie aérienne du canton de Genève.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.95007 46.1241</ows:LowerCorner><ows:UpperCorner>6.31198 46.3658</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.GENEVE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>362</MinTileRow><MaxTileRow>363</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>725</MinTileRow><MaxTileRow>727</MaxTileRow><MinTileCol>1057</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1451</MinTileRow><MaxTileRow>1455</MaxTileRow><MinTileCol>2115</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2902</MinTileRow><MaxTileRow>2910</MaxTileRow><MinTileCol>4231</MinTileCol><MaxTileCol>4239</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5804</MinTileRow><MaxTileRow>5820</MaxTileRow><MinTileCol>8462</MinTileCol><MaxTileCol>8479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11609</MinTileRow><MaxTileRow>11641</MaxTileRow><MinTileCol>16925</MinTileCol><MaxTileCol>16958</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23219</MinTileRow><MaxTileRow>23282</MaxTileRow><MinTileCol>33851</MinTileCol><MaxTileCol>33917</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>46438</MinTileRow><MaxTileRow>46565</MaxTileRow><MinTileCol>67702</MinTileCol><MaxTileCol>67834</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>92876</MinTileRow><MaxTileRow>93130</MaxTileRow><MinTileCol>135404</MinTileCol><MaxTileCol>135668</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>185752</MinTileRow><MaxTileRow>186261</MaxTileRow><MinTileCol>270809</MinTileCol><MaxTileCol>271336</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>371504</MinTileRow><MaxTileRow>372522</MaxTileRow><MinTileCol>541618</MinTileCol><MaxTileCol>542672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>45</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>90</MinTileRow><MaxTileRow>90</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>181</MinTileRow><MaxTileRow>181</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Authie 1935</ows:Title><ows:Abstract>Photographie aérienne d&apos;Authie en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.52005 50.1704</ows:LowerCorner><ows:UpperCorner>1.67164 50.5759</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.AUTHIE.1935</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>344</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>689</MinTileRow><MaxTileRow>693</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1378</MinTileRow><MaxTileRow>1386</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2757</MinTileRow><MaxTileRow>2772</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5515</MinTileRow><MaxTileRow>5544</MaxTileRow><MinTileCol>8261</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11031</MinTileRow><MaxTileRow>11088</MaxTileRow><MinTileCol>16523</MinTileCol><MaxTileCol>16535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22062</MinTileRow><MaxTileRow>22177</MaxTileRow><MinTileCol>33046</MinTileCol><MaxTileCol>33071</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44124</MinTileRow><MaxTileRow>44355</MaxTileRow><MinTileCol>66093</MinTileCol><MaxTileCol>66142</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88249</MinTileRow><MaxTileRow>88711</MaxTileRow><MinTileCol>132186</MinTileCol><MaxTileCol>132285</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Authie 1935</ows:Title><ows:Abstract>Photographie aérienne d&apos;Authie en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.52005 50.1704</ows:LowerCorner><ows:UpperCorner>1.67164 50.5759</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.AUTHIE.1935-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>344</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>689</MinTileRow><MaxTileRow>693</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1378</MinTileRow><MaxTileRow>1386</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2757</MinTileRow><MaxTileRow>2772</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5515</MinTileRow><MaxTileRow>5544</MaxTileRow><MinTileCol>8261</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11031</MinTileRow><MaxTileRow>11088</MaxTileRow><MinTileCol>16523</MinTileCol><MaxTileCol>16535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22062</MinTileRow><MaxTileRow>22177</MaxTileRow><MinTileCol>33046</MinTileCol><MaxTileCol>33071</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44124</MinTileRow><MaxTileRow>44355</MaxTileRow><MinTileCol>66093</MinTileCol><MaxTileCol>66142</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88249</MinTileRow><MaxTileRow>88711</MaxTileRow><MinTileCol>132186</MinTileCol><MaxTileCol>132285</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Berck St-Quentin 1934</ows:Title><ows:Abstract>Photographie aérienne de Berck St-Quentin en 1934 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.51075 50.251</ows:LowerCorner><ows:UpperCorner>1.66952 50.4143</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.BERCK.1934</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>345</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>690</MinTileRow><MaxTileRow>692</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1381</MinTileRow><MaxTileRow>1384</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2763</MinTileRow><MaxTileRow>2769</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5527</MinTileRow><MaxTileRow>5538</MaxTileRow><MinTileCol>8260</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11054</MinTileRow><MaxTileRow>11077</MaxTileRow><MinTileCol>16521</MinTileCol><MaxTileCol>16535</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22108</MinTileRow><MaxTileRow>22154</MaxTileRow><MinTileCol>33043</MinTileCol><MaxTileCol>33071</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44217</MinTileRow><MaxTileRow>44309</MaxTileRow><MinTileCol>66086</MinTileCol><MaxTileCol>66142</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88434</MinTileRow><MaxTileRow>88619</MaxTileRow><MinTileCol>132173</MinTileCol><MaxTileCol>132285</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Canche-Authie 1935</ows:Title><ows:Abstract>Photographie aérienne de Canche-Authie en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.52005 50.314</ows:LowerCorner><ows:UpperCorner>1.68189 50.576</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.CANCHE.1935</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>344</MinTileRow><MaxTileRow>345</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>689</MinTileRow><MaxTileRow>691</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1378</MinTileRow><MaxTileRow>1383</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2757</MinTileRow><MaxTileRow>2767</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5515</MinTileRow><MaxTileRow>5534</MaxTileRow><MinTileCol>8261</MinTileCol><MaxTileCol>8268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11031</MinTileRow><MaxTileRow>11068</MaxTileRow><MinTileCol>16522</MinTileCol><MaxTileCol>16536</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22062</MinTileRow><MaxTileRow>22136</MaxTileRow><MinTileCol>33045</MinTileCol><MaxTileCol>33073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44124</MinTileRow><MaxTileRow>44273</MaxTileRow><MinTileCol>66091</MinTileCol><MaxTileCol>66146</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88249</MinTileRow><MaxTileRow>88547</MaxTileRow><MinTileCol>132183</MinTileCol><MaxTileCol>132293</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Canche-Authie 1935</ows:Title><ows:Abstract>Photographie aérienne de Canche-Authie en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.52005 50.314</ows:LowerCorner><ows:UpperCorner>1.68189 50.576</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.CANCHE.1935-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>344</MinTileRow><MaxTileRow>345</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>689</MinTileRow><MaxTileRow>691</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1378</MinTileRow><MaxTileRow>1383</MaxTileRow><MinTileCol>2065</MinTileCol><MaxTileCol>2067</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2757</MinTileRow><MaxTileRow>2767</MaxTileRow><MinTileCol>4130</MinTileCol><MaxTileCol>4134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5515</MinTileRow><MaxTileRow>5534</MaxTileRow><MinTileCol>8261</MinTileCol><MaxTileCol>8268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11031</MinTileRow><MaxTileRow>11068</MaxTileRow><MinTileCol>16522</MinTileCol><MaxTileCol>16536</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22062</MinTileRow><MaxTileRow>22136</MaxTileRow><MinTileCol>33045</MinTileCol><MaxTileCol>33073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44124</MinTileRow><MaxTileRow>44273</MaxTileRow><MinTileCol>66091</MinTileCol><MaxTileCol>66146</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88249</MinTileRow><MaxTileRow>88547</MaxTileRow><MinTileCol>132183</MinTileCol><MaxTileCol>132293</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Douelan 1938</ows:Title><ows:Abstract>Photographie aérienne de Douelan en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.54994 47.6947</ows:LowerCorner><ows:UpperCorner>-3.44699 47.7806</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.DOUELAN.1938</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1428</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2008</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2855</MinTileRow><MaxTileRow>2857</MaxTileRow><MinTileCol>4015</MinTileCol><MaxTileCol>4017</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5710</MinTileRow><MaxTileRow>5715</MaxTileRow><MinTileCol>8030</MinTileCol><MaxTileCol>8035</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11420</MinTileRow><MaxTileRow>11431</MaxTileRow><MinTileCol>16060</MinTileCol><MaxTileCol>16070</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22841</MinTileRow><MaxTileRow>22863</MaxTileRow><MinTileCol>32121</MinTileCol><MaxTileCol>32140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45682</MinTileRow><MaxTileRow>45726</MaxTileRow><MinTileCol>64243</MinTileCol><MaxTileCol>64281</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91365</MinTileRow><MaxTileRow>91453</MaxTileRow><MinTileCol>128486</MinTileCol><MaxTileCol>128562</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Douelan 1938</ows:Title><ows:Abstract>Photographie aérienne de Douelan en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.54994 47.6947</ows:LowerCorner><ows:UpperCorner>-3.44699 47.7806</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.DOUELAN.1938-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1428</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2008</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2855</MinTileRow><MaxTileRow>2857</MaxTileRow><MinTileCol>4015</MinTileCol><MaxTileCol>4017</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5710</MinTileRow><MaxTileRow>5715</MaxTileRow><MinTileCol>8030</MinTileCol><MaxTileCol>8035</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11420</MinTileRow><MaxTileRow>11431</MaxTileRow><MinTileCol>16060</MinTileCol><MaxTileCol>16070</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22841</MinTileRow><MaxTileRow>22863</MaxTileRow><MinTileCol>32121</MinTileCol><MaxTileCol>32140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45682</MinTileRow><MaxTileRow>45726</MaxTileRow><MinTileCol>64243</MinTileCol><MaxTileCol>64281</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91365</MinTileRow><MaxTileRow>91453</MaxTileRow><MinTileCol>128486</MinTileCol><MaxTileCol>128562</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Groix 1938</ows:Title><ows:Abstract>Photographies aériennes de Groix en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.52232 47.6057</ows:LowerCorner><ows:UpperCorner>-3.39634 47.6661</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.GROIX.1938</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>715</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1429</MinTileRow><MaxTileRow>1430</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2859</MinTileRow><MaxTileRow>2860</MaxTileRow><MinTileCol>4015</MinTileCol><MaxTileCol>4018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5718</MinTileRow><MaxTileRow>5721</MaxTileRow><MinTileCol>8031</MinTileCol><MaxTileCol>8037</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11436</MinTileRow><MaxTileRow>11443</MaxTileRow><MinTileCol>16063</MinTileCol><MaxTileCol>16074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22872</MinTileRow><MaxTileRow>22887</MaxTileRow><MinTileCol>32126</MinTileCol><MaxTileCol>32149</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45745</MinTileRow><MaxTileRow>45775</MaxTileRow><MinTileCol>64253</MinTileCol><MaxTileCol>64299</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91491</MinTileRow><MaxTileRow>91550</MaxTileRow><MinTileCol>128506</MinTileCol><MaxTileCol>128598</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Le Pouldu Kernevel 1938</ows:Title><ows:Abstract>Photographie aérienne de Le Pouldou Kernevel en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.49123 47.6797</ows:LowerCorner><ows:UpperCorner>-3.35182 47.7408</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.LEPOULDOU.1938</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1428</MinTileRow><MaxTileRow>1429</MaxTileRow><MinTileCol>2008</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2856</MinTileRow><MaxTileRow>2858</MaxTileRow><MinTileCol>4016</MinTileCol><MaxTileCol>4019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5713</MinTileRow><MaxTileRow>5716</MaxTileRow><MinTileCol>8033</MinTileCol><MaxTileCol>8039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11426</MinTileRow><MaxTileRow>11433</MaxTileRow><MinTileCol>16066</MinTileCol><MaxTileCol>16078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22852</MinTileRow><MaxTileRow>22867</MaxTileRow><MinTileCol>32132</MinTileCol><MaxTileCol>32157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45705</MinTileRow><MaxTileRow>45734</MaxTileRow><MinTileCol>64264</MinTileCol><MaxTileCol>64315</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91411</MinTileRow><MaxTileRow>91468</MaxTileRow><MinTileCol>128529</MinTileCol><MaxTileCol>128630</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Le Pouldu Kernevel 1938</ows:Title><ows:Abstract>Photographie aérienne de Le Pouldou Kernevel en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.49123 47.6797</ows:LowerCorner><ows:UpperCorner>-3.35182 47.7408</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.LEPOULDOU.1938-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1428</MinTileRow><MaxTileRow>1429</MaxTileRow><MinTileCol>2008</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2856</MinTileRow><MaxTileRow>2858</MaxTileRow><MinTileCol>4016</MinTileCol><MaxTileCol>4019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5713</MinTileRow><MaxTileRow>5716</MaxTileRow><MinTileCol>8033</MinTileCol><MaxTileCol>8039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11426</MinTileRow><MaxTileRow>11433</MaxTileRow><MinTileCol>16066</MinTileCol><MaxTileCol>16078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22852</MinTileRow><MaxTileRow>22867</MaxTileRow><MinTileCol>32132</MinTileCol><MaxTileCol>32157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45705</MinTileRow><MaxTileRow>45734</MaxTileRow><MinTileCol>64264</MinTileCol><MaxTileCol>64315</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91411</MinTileRow><MaxTileRow>91468</MaxTileRow><MinTileCol>128529</MinTileCol><MaxTileCol>128630</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Port Louis Gâvres 1938</ows:Title><ows:Abstract>Photographie aérienne de Port Louis Gâvres en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.39707 47.6579</ows:LowerCorner><ows:UpperCorner>-3.21579 47.739</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.PORTLOUIS.1938</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1005</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1428</MinTileRow><MaxTileRow>1429</MaxTileRow><MinTileCol>2009</MinTileCol><MaxTileCol>2011</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2856</MinTileRow><MaxTileRow>2859</MaxTileRow><MinTileCol>4018</MinTileCol><MaxTileCol>4022</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5713</MinTileRow><MaxTileRow>5718</MaxTileRow><MinTileCol>8037</MinTileCol><MaxTileCol>8045</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11426</MinTileRow><MaxTileRow>11436</MaxTileRow><MinTileCol>16075</MinTileCol><MaxTileCol>16091</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22853</MinTileRow><MaxTileRow>22872</MaxTileRow><MinTileCol>32150</MinTileCol><MaxTileCol>32182</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45707</MinTileRow><MaxTileRow>45744</MaxTileRow><MinTileCol>64300</MinTileCol><MaxTileCol>64364</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91414</MinTileRow><MaxTileRow>91489</MaxTileRow><MinTileCol>128600</MinTileCol><MaxTileCol>128729</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Port Louis Gâvres 1938</ows:Title><ows:Abstract>Photographie aérienne de Port Louis Gâvres en 1938 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.39707 47.6579</ows:LowerCorner><ows:UpperCorner>-3.21579 47.739</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.PORTLOUIS.1938-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>357</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>714</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1005</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1428</MinTileRow><MaxTileRow>1429</MaxTileRow><MinTileCol>2009</MinTileCol><MaxTileCol>2011</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2856</MinTileRow><MaxTileRow>2859</MaxTileRow><MinTileCol>4018</MinTileCol><MaxTileCol>4022</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5713</MinTileRow><MaxTileRow>5718</MaxTileRow><MinTileCol>8037</MinTileCol><MaxTileCol>8045</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11426</MinTileRow><MaxTileRow>11436</MaxTileRow><MinTileCol>16075</MinTileCol><MaxTileCol>16091</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22853</MinTileRow><MaxTileRow>22872</MaxTileRow><MinTileCol>32150</MinTileCol><MaxTileCol>32182</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45707</MinTileRow><MaxTileRow>45744</MaxTileRow><MinTileCol>64300</MinTileCol><MaxTileCol>64364</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91414</MinTileRow><MaxTileRow>91489</MaxTileRow><MinTileCol>128600</MinTileCol><MaxTileCol>128729</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Abords de Lorient</ows:Title><ows:Abstract>Photographie aérienne des abords de Lorient en 1924 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.56436 47.6222</ows:LowerCorner><ows:UpperCorner>-3.25248 47.7997</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.QUIBERON.1924</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>501</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>715</MaxTileRow><MinTileCol>1003</MinTileCol><MaxTileCol>1005</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1430</MaxTileRow><MinTileCol>2007</MinTileCol><MaxTileCol>2010</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2854</MinTileRow><MaxTileRow>2860</MaxTileRow><MinTileCol>4014</MinTileCol><MaxTileCol>4021</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5709</MinTileRow><MaxTileRow>5720</MaxTileRow><MinTileCol>8029</MinTileCol><MaxTileCol>8043</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11419</MinTileRow><MaxTileRow>11441</MaxTileRow><MinTileCol>16059</MinTileCol><MaxTileCol>16087</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22839</MinTileRow><MaxTileRow>22883</MaxTileRow><MinTileCol>32119</MinTileCol><MaxTileCol>32175</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45678</MinTileRow><MaxTileRow>45766</MaxTileRow><MinTileCol>64238</MinTileCol><MaxTileCol>64350</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91357</MinTileRow><MaxTileRow>91532</MaxTileRow><MinTileCol>128477</MinTileCol><MaxTileCol>128700</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Somme 1935</ows:Title><ows:Abstract>Photographie aérienne de la Baie de Somme en 1935 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.48669 50.1699</ows:LowerCorner><ows:UpperCorner>1.65766 50.2795</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.SOMME.1935</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>346</MinTileRow><MaxTileRow>346</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>516</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>692</MinTileRow><MaxTileRow>693</MaxTileRow><MinTileCol>1032</MinTileCol><MaxTileCol>1033</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1384</MinTileRow><MaxTileRow>1386</MaxTileRow><MinTileCol>2064</MinTileCol><MaxTileCol>2066</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2768</MinTileRow><MaxTileRow>2772</MaxTileRow><MinTileCol>4129</MinTileCol><MaxTileCol>4133</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5536</MinTileRow><MaxTileRow>5544</MaxTileRow><MinTileCol>8259</MinTileCol><MaxTileCol>8267</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11073</MinTileRow><MaxTileRow>11088</MaxTileRow><MinTileCol>16519</MinTileCol><MaxTileCol>16534</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22146</MinTileRow><MaxTileRow>22177</MaxTileRow><MinTileCol>33038</MinTileCol><MaxTileCol>33069</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>44293</MinTileRow><MaxTileRow>44355</MaxTileRow><MinTileCol>66077</MinTileCol><MaxTileCol>66139</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>88587</MinTileRow><MaxTileRow>88711</MaxTileRow><MinTileCol>132155</MinTileCol><MaxTileCol>132278</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Rivière du Ter 1939</ows:Title><ows:Abstract>Photographie aérienne de la Rivière du Ter en 1939 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.44022 47.7006</ows:LowerCorner><ows:UpperCorner>-3.34067 47.7595</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.TER.1939</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1428</MaxTileRow><MinTileCol>2008</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2855</MinTileRow><MaxTileRow>2857</MaxTileRow><MinTileCol>4017</MinTileCol><MaxTileCol>4019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5711</MinTileRow><MaxTileRow>5715</MaxTileRow><MinTileCol>8035</MinTileCol><MaxTileCol>8039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11423</MinTileRow><MaxTileRow>11431</MaxTileRow><MinTileCol>16070</MinTileCol><MaxTileCol>16079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22846</MinTileRow><MaxTileRow>22862</MaxTileRow><MinTileCol>32141</MinTileCol><MaxTileCol>32159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45693</MinTileRow><MaxTileRow>45724</MaxTileRow><MinTileCol>64283</MinTileCol><MaxTileCol>64319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91387</MinTileRow><MaxTileRow>91448</MaxTileRow><MinTileCol>128567</MinTileCol><MaxTileCol>128638</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>IFREMER - Rivière du Ter 1939</ows:Title><ows:Abstract>Photographie aérienne de la Rivière du Ter en 1939 (archives de l&apos;IFREMER).</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-3.44022 47.7006</ows:LowerCorner><ows:UpperCorner>-3.34067 47.7595</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.IFREMER.TER.1939-V1</ows:Identifier><Style isDefault="true"><ows:Title>Blanc est transparent</ows:Title><ows:Abstract>Remplacement du blanc (valeur de nodata) par du transparent</ows:Abstract><ows:Keywords><ows:Keyword>IFREMER</ows:Keyword></ows:Keywords><ows:Identifier>white_is_transparent</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>356</MinTileRow><MaxTileRow>357</MaxTileRow><MinTileCol>502</MinTileCol><MaxTileCol>502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>713</MinTileRow><MaxTileRow>714</MaxTileRow><MinTileCol>1004</MinTileCol><MaxTileCol>1004</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1427</MinTileRow><MaxTileRow>1428</MaxTileRow><MinTileCol>2008</MinTileCol><MaxTileCol>2009</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2855</MinTileRow><MaxTileRow>2857</MaxTileRow><MinTileCol>4017</MinTileCol><MaxTileCol>4019</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5711</MinTileRow><MaxTileRow>5715</MaxTileRow><MinTileCol>8035</MinTileCol><MaxTileCol>8039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11423</MinTileRow><MaxTileRow>11431</MaxTileRow><MinTileCol>16070</MinTileCol><MaxTileCol>16079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22846</MinTileRow><MaxTileRow>22862</MaxTileRow><MinTileCol>32141</MinTileCol><MaxTileCol>32159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45693</MinTileRow><MaxTileRow>45724</MaxTileRow><MinTileCol>64283</MinTileCol><MaxTileCol>64319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>91387</MinTileRow><MaxTileRow>91448</MaxTileRow><MinTileCol>128567</MinTileCol><MaxTileCol>128638</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Planet Observer</ows:Title><ows:Abstract>Image satellite retravaillée pour obtenir une couverture mondiale esthétique.</ows:Abstract><ows:Keywords><ows:Keyword>planet observer</ows:Keyword><ows:Keyword>ortho image</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 88</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.LOW_RES.CRS84</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G_PO</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1024</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2048</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2048</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>4096</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>4096</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>8192</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>64</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1024</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>BDORTHO-NC</ows:Title><ows:Abstract>La BDORTHO-NC est une orthophotographie du territoire de Nouvelle-Calédonie réalisée par la DITTT pour le compte du Gouvernement de Nouvelle-Calédonie.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178 -84</ows:LowerCorner><ows:UpperCorner>178 84</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.NCL</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Style><ows:Title>Style INSPIRE</ows:Title><ows:Abstract>Style par défault INSPIRE</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword><ows:Keyword>INSPIRE</ows:Keyword></ows:Keywords><ows:Identifier>inspire_common:DEFAULT</ows:Identifier><LegendURL format="image/png" xlink:href="http://ign.fr/inspire" width="100" height="100" maxScaleDenominator="30" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>31</MinTileRow><MaxTileRow>992</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>62</MinTileRow><MaxTileRow>1985</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>125</MinTileRow><MaxTileRow>3970</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4548</MinTileRow><MaxTileRow>4635</MaxTileRow><MinTileCol>7816</MinTileCol><MaxTileCol>7924</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9096</MinTileRow><MaxTileRow>9270</MaxTileRow><MinTileCol>15633</MinTileCol><MaxTileCol>15848</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18193</MinTileRow><MaxTileRow>18540</MaxTileRow><MinTileCol>31266</MinTileCol><MaxTileCol>31697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36386</MinTileRow><MaxTileRow>37080</MaxTileRow><MinTileCol>62533</MinTileCol><MaxTileCol>63395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>72773</MinTileRow><MaxTileRow>74160</MaxTileRow><MinTileCol>125066</MinTileCol><MaxTileCol>126791</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>145546</MinTileRow><MaxTileRow>148320</MaxTileRow><MinTileCol>250132</MinTileCol><MaxTileCol>253582</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>7</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>15</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Paris haute résolution</ows:Title><ows:Abstract>Prises de vues aériennes de Paris à haute résolution.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>2.19509 48.8095</ows:LowerCorner><ows:UpperCorner>2.42968 48.9124</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.PARIS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1408</MinTileRow><MaxTileRow>1409</MaxTileRow><MinTileCol>2072</MinTileCol><MaxTileCol>2075</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2816</MinTileRow><MaxTileRow>2819</MaxTileRow><MinTileCol>4145</MinTileCol><MaxTileCol>4151</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5632</MinTileRow><MaxTileRow>5639</MaxTileRow><MinTileCol>8291</MinTileCol><MaxTileCol>8302</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11265</MinTileRow><MaxTileRow>11279</MaxTileRow><MinTileCol>16583</MinTileCol><MaxTileCol>16605</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22530</MinTileRow><MaxTileRow>22559</MaxTileRow><MinTileCol>33167</MinTileCol><MaxTileCol>33210</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>45061</MinTileRow><MaxTileRow>45118</MaxTileRow><MinTileCol>66335</MinTileCol><MaxTileCol>66420</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>90123</MinTileRow><MaxTileRow>90237</MaxTileRow><MinTileCol>132670</MinTileCol><MaxTileCol>132841</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>180246</MinTileRow><MaxTileRow>180474</MaxTileRow><MinTileCol>265340</MinTileCol><MaxTileCol>265682</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>20</TileMatrix><MinTileRow>360492</MinTileRow><MaxTileRow>360948</MaxTileRow><MinTileCol>530681</MinTileCol><MaxTileCol>531364</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>GEOSUD / RapidEye (2010)</ows:Title><ows:Abstract>Image satellitaire de la France métropolitaine effectuée en 2010 par les satellites de télédétection RapidEye dans le cadre du projet EQUIPEX GEOSUD.</ows:Abstract><ows:Keywords><ows:Keyword>Images satellitaires</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.80725 41.2014</ows:LowerCorner><ows:UpperCorner>10.961 50.9218</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS.RAPIDEYE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1531</MaxTileRow><MinTileCol>1986</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3063</MaxTileRow><MinTileCol>3972</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7945</MinTileCol><MaxTileCol>8638</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15890</MinTileCol><MaxTileCol>17277</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes 2000-2005</ows:Title><ows:Abstract>Prises de vues aériennes des territoires réalisées entre 2000 et 2005.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8561 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ORTHOIMAGERY.ORTHOPHOTOS2000-2005</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Planet Observer</ows:Title><ows:Abstract>Image satellite retravaillée pour obtenir une couverture mondiale esthétique.</ows:Abstract><ows:Keywords><ows:Keyword>planet observer</ows:Keyword><ows:Keyword>ortho image</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 88</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO-LOWRES_PYR-JPEG_WLD_WGS84G_2012-05-04</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G_PO</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1024</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2048</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>2048</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>4096</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>4096</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>8192</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>64</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1024</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes 2000-2005</ows:Title><ows:Abstract>Prises de vues aériennes des territoires réalisées entre 2000 et 2005.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8561 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTOHISTO_PYR-JPEG_WLD_WM_20121015</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies arériennes (CRS84)</ows:Title><ows:Abstract>Orthophotographies</ows:Abstract><ows:Keywords><ows:Keyword>ortho-images</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 50.3854</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WGS84G_2012-05-25</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>225</MinTileRow><MaxTileRow>633</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>450</MinTileRow><MaxTileRow>1267</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>901</MinTileRow><MaxTileRow>2534</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>1803</MinTileRow><MaxTileRow>5069</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>3606</MinTileRow><MaxTileRow>10139</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>7212</MinTileRow><MaxTileRow>20279</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>14425</MinTileRow><MaxTileRow>40559</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>28851</MinTileRow><MaxTileRow>81118</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>14</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>28</MinTileRow><MaxTileRow>79</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>56</MinTileRow><MaxTileRow>158</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>112</MinTileRow><MaxTileRow>316</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies arériennes (CRS84)</ows:Title><ows:Abstract>Orthophotographies</ows:Abstract><ows:Keywords><ows:Keyword>ortho-images</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WGS84G_2012-06-11</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>221</MinTileRow><MaxTileRow>633</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>442</MinTileRow><MaxTileRow>1267</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>885</MinTileRow><MaxTileRow>2534</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>1770</MinTileRow><MaxTileRow>5069</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>3541</MinTileRow><MaxTileRow>10139</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>7082</MinTileRow><MaxTileRow>20279</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>14165</MinTileRow><MaxTileRow>40559</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>28330</MinTileRow><MaxTileRow>81118</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>79</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>55</MinTileRow><MaxTileRow>158</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>110</MinTileRow><MaxTileRow>316</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies arériennes - (CRS84)</ows:Title><ows:Abstract>Orthophotographies</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WGS84G_2012-10-01</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>221</MinTileRow><MaxTileRow>633</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>442</MinTileRow><MaxTileRow>1267</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>885</MinTileRow><MaxTileRow>2534</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>1770</MinTileRow><MaxTileRow>5069</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>3541</MinTileRow><MaxTileRow>10139</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>7082</MinTileRow><MaxTileRow>20279</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>14165</MinTileRow><MaxTileRow>40559</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>28330</MinTileRow><MaxTileRow>81118</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>79</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>55</MinTileRow><MaxTileRow>158</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>110</MinTileRow><MaxTileRow>316</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies arériennes - (CRS84)</ows:Title><ows:Abstract>Orthophotographies</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.187 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 51.091</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WGS84G_20121008</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>221</MinTileRow><MaxTileRow>633</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>442</MinTileRow><MaxTileRow>1267</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>885</MinTileRow><MaxTileRow>2534</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>1770</MinTileRow><MaxTileRow>5069</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>3541</MinTileRow><MaxTileRow>10139</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>7082</MinTileRow><MaxTileRow>20279</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>14165</MinTileRow><MaxTileRow>40559</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>28330</MinTileRow><MaxTileRow>81118</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>13</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>27</MinTileRow><MaxTileRow>79</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>55</MinTileRow><MaxTileRow>158</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>110</MinTileRow><MaxTileRow>316</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes</ows:Title><ows:Abstract>Photographies aériennes</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1607 -21.4013</ows:LowerCorner><ows:UpperCorner>55.8464 60.6416</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WM_2012-05-14</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>178772</MinTileRow><MaxTileRow>240989</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>273392</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes</ows:Title><ows:Abstract>Photographies aériennes</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-180 -90</ows:LowerCorner><ows:UpperCorner>180 90</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WM_WMT_20121004</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>31</MinTileRow><MaxTileRow>992</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>62</MinTileRow><MaxTileRow>1985</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>125</MinTileRow><MaxTileRow>3970</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>85868</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>171736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>178772</MinTileRow><MaxTileRow>294060</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>7</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>15</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Photographies aériennes</ows:Title><ows:Abstract>Photographies aériennes</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178 -84</ows:LowerCorner><ows:UpperCorner>178 84</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PHOTO_PYR-JPEG_WLD_WM_WMT_20130603</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>31</MinTileRow><MaxTileRow>992</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>1018</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>62</MinTileRow><MaxTileRow>1985</MaxTileRow><MinTileCol>11</MinTileCol><MaxTileCol>2036</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>125</MinTileRow><MaxTileRow>3970</MaxTileRow><MinTileCol>22</MinTileCol><MaxTileCol>4073</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>4635</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>7924</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>9270</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>15848</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10956</MinTileRow><MaxTileRow>18540</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>31697</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>37072</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>63395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43825</MinTileRow><MaxTileRow>74144</MaxTileRow><MinTileCol>660</MinTileCol><MaxTileCol>126791</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87651</MinTileRow><MaxTileRow>148272</MaxTileRow><MinTileCol>1320</MinTileCol><MaxTileCol>253582</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>19</TileMatrix><MinTileRow>178772</MinTileRow><MaxTileRow>294060</MaxTileRow><MinTileCol>170159</MinTileCol><MaxTileCol>343473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>3</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>7</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>7</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>15</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>15</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>31</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>62</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>63</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>3</MinTileRow><MaxTileRow>124</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>127</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>7</MinTileRow><MaxTileRow>248</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>254</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>496</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>509</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Arrêtés de protection de biotope</ows:Title><ows:Abstract>Conservation de l&apos;habitat d&apos;espèces protégées. Outil réglementaires de niveau départemental.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.APB</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1351</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5481</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10962</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10631</MinTileCol><MaxTileCol>21463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21940</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21263</MinTileCol><MaxTileCol>42911</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>178</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves de biosphère</ows:Title><ows:Abstract>Réserves naturelles reconnues par le Programme sur l&apos;Homme et la Biosphère créé par l&apos;Unesco.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.25 15.7184</ows:LowerCorner><ows:UpperCorner>9.72255 51.1534</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.BIOS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>466</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>933</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1866</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3733</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5473</MinTileRow><MaxTileRow>7467</MaxTileRow><MinTileCol>5313</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10947</MinTileRow><MaxTileRow>14934</MaxTileRow><MinTileCol>10626</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21895</MinTileRow><MaxTileRow>29869</MaxTileRow><MinTileCol>21253</MinTileCol><MaxTileCol>34537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>116</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>233</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Périmètres d&apos;intervention</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.CDL</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>36760</MaxTileRow><MinTileCol>21272</MinTileCol><MaxTileCol>42935</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Conservatoire du littoral : Parcelles protégées</ows:Title><ows:Abstract>Les sites du conservatoire du littoral ont pour vocation la sauvegarde des espaces côtiers et lacustres. Leur accès au public est encouragé mais reste défini dans des limites compatibles avec la vulnérabilité de chaque site.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.CEN</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>328</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs nationaux</ows:Title><ows:Abstract>Les parcs nationaux sont de vastes espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, et dont le but est à la fois de protéger des processus écologiques à large échelle et de fournir des possibilités de découverte de la nature.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.PN</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels marins</ows:Title><ows:Abstract>Zones maritimes identifiées pour leur biodiversité ou par les activités humaines faisant l&apos;objet de protection.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.PNM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Parcs naturels régionaux</ows:Title><ows:Abstract>Les parcs naturels régionaux ont pour but de valoriser de vastes espaces de fort intérêt culturel et naturel, et de veiller au développement durable de ces territoires dont le caractère rural est souvent très affirmé.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.PNR</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>510</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1005</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2010</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4004</MaxTileRow><MinTileCol>2701</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5481</MinTileRow><MaxTileRow>8009</MaxTileRow><MinTileCol>5403</MinTileCol><MaxTileCol>8635</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10962</MinTileRow><MaxTileRow>16002</MaxTileRow><MinTileCol>10807</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21940</MinTileRow><MaxTileRow>31988</MaxTileRow><MinTileCol>21615</MinTileCol><MaxTileCol>34495</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>36</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>73</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>127</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>146</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>255</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>277</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones humides d&apos;importance internationale</ows:Title><ows:Abstract>Un site Ramsar est un espace désigné en application de la Convention relative aux zones humides d&apos;importance internationale, particulièrement comme habitats des oiseaux d&apos;eau, dont le traité a été signé en 1971 sur les bords de la mer Caspienne (Iran).</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -50.5714</ows:LowerCorner><ows:UpperCorner>80.7449 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.RAMSAR</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>688</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>672</MinTileRow><MaxTileRow>1360</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1359</MinTileRow><MaxTileRow>2704</MaxTileRow><MinTileCol>1344</MinTileCol><MaxTileCol>2944</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>5408</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5872</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5488</MinTileRow><MaxTileRow>10816</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>11743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10991</MinTileRow><MaxTileRow>21631</MaxTileRow><MinTileCol>10752</MinTileCol><MaxTileCol>23472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21984</MinTileRow><MaxTileRow>43264</MaxTileRow><MinTileCol>21503</MinTileCol><MaxTileCol>46943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>95</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>191</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>160</MinTileRow><MaxTileRow>352</MaxTileRow><MinTileCol>160</MinTileCol><MaxTileCol>368</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves biologiques</ows:Title><ows:Abstract>Une réserve biologique est un espace protégé en milieu forestier ou en milieu associé à la forêt (landes, mares, tourbières, dunes). Ce statut s&apos;applique aux forêts gérées par l&apos;Office National des Forêts et a pour but la protection d&apos;habitats remarquables ou représentatifs.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.RB</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1351</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1370</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1342</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2740</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2701</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5497</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5403</MinTileCol><MaxTileCol>10731</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10994</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10807</MinTileCol><MaxTileCol>21463</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22004</MinTileRow><MaxTileRow>36756</MaxTileRow><MinTileCol>21615</MinTileCol><MaxTileCol>42927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>178</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>159</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves naturelles nationales</ows:Title><ows:Abstract>Les réserves naturelles sont des espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, tant sur le plan de la biodiversité que parfois sur celui de la géodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -50.5714</ows:LowerCorner><ows:UpperCorner>80.7449 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.RN</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>335</MinTileRow><MaxTileRow>679</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>678</MinTileRow><MaxTileRow>1358</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1365</MinTileRow><MaxTileRow>2709</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2738</MinTileRow><MaxTileRow>5410</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5869</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5476</MinTileRow><MaxTileRow>10820</MaxTileRow><MinTileCol>5315</MinTileCol><MaxTileCol>11739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>21632</MaxTileRow><MinTileCol>10639</MinTileCol><MaxTileCol>23471</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>43256</MaxTileRow><MinTileCol>21287</MinTileCol><MaxTileCol>46943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>18</MinTileRow><MaxTileRow>42</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>52</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>36</MinTileRow><MaxTileRow>84</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>97</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>81</MinTileRow><MaxTileRow>169</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>186</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>163</MinTileRow><MaxTileRow>339</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>373</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves Naturelles de Corse</ows:Title><ows:Abstract>Les réserves naturelles sont des espaces protégés terrestres ou marins dont le patrimoine naturel est exceptionnel, tant sur le plan de la biodiversité que parfois sur celui de la géodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.RNC</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>374</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>529</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>749</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>1066</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1498</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>2141</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3005</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>4282</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6019</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>8573</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12038</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>17155</MinTileCol><MaxTileCol>17251</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24084</MinTileRow><MaxTileRow>24492</MaxTileRow><MinTileCol>34318</MinTileCol><MaxTileCol>34502</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réserves nationales de chasse et de faune sauvage</ows:Title><ows:Abstract>Les réserves nationales de chasse et de faune sauvage sont des espaces protégés terrestres ou marins dont la gestion est principalement assurée par l&apos;Office National de la Chasse et de la Faune Sauvage.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2.99155 41.687</ows:LowerCorner><ows:UpperCorner>9.6157 51.0863</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.RNCF</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>381</MaxTileRow><MinTileCol>503</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>762</MaxTileRow><MinTileCol>1006</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1369</MinTileRow><MaxTileRow>1525</MaxTileRow><MinTileCol>2013</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2739</MinTileRow><MaxTileRow>3050</MaxTileRow><MinTileCol>4027</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5478</MinTileRow><MaxTileRow>6101</MaxTileRow><MinTileCol>8055</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10957</MinTileRow><MaxTileRow>12202</MaxTileRow><MinTileCol>16111</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21914</MinTileRow><MaxTileRow>24404</MaxTileRow><MinTileCol>32223</MinTileCol><MaxTileCol>34518</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>125</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>251</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites Natura 2000 au titre de la Directive Habitats</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.SIC</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>15915</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>24492</MaxTileRow><MinTileCol>31830</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones écologiques de nature remarquable (ZNIEFF1)</ows:Title><ows:Abstract>Inventaire des espaces naturels abritant la nature remarquable</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.ZNIEFF1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>344</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1016</MaxTileRow><MinTileCol>696</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2024</MaxTileRow><MinTileCol>1400</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4048</MaxTileRow><MinTileCol>2808</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>8096</MaxTileRow><MinTileCol>5616</MinTileCol><MaxTileCol>8632</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10952</MinTileRow><MaxTileRow>16184</MaxTileRow><MinTileCol>11231</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21912</MinTileRow><MaxTileRow>32367</MaxTileRow><MinTileCol>22471</MinTileCol><MaxTileCol>34511</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>63</MaxTileRow><MinTileCol>39</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones fonctionnelles de nature remarquable (ZNIEFF2)</ows:Title><ows:Abstract>Inventaire des espaces fonctionnels indispensables à la nature remarquable.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.ZNIEFF2</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>512</MaxTileRow><MinTileCol>344</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1016</MaxTileRow><MinTileCol>696</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2024</MaxTileRow><MinTileCol>1400</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4048</MaxTileRow><MinTileCol>2808</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>8096</MaxTileRow><MinTileCol>5624</MinTileCol><MaxTileCol>8632</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10960</MinTileRow><MaxTileRow>16191</MaxTileRow><MinTileCol>11247</MinTileCol><MaxTileCol>17255</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>32383</MaxTileRow><MinTileCol>22495</MinTileCol><MaxTileCol>34503</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>31</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>63</MaxTileRow><MinTileCol>39</MinTileCol><MaxTileCol>72</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>128</MaxTileRow><MinTileCol>80</MinTileCol><MaxTileCol>136</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>256</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sites NATURA 2000 au titre de la Directive Oiseaux</ows:Title><ows:Abstract>Le réseau Natura 2000 s&apos;inscrit au coeur de la politique de conservation de la nature de l&apos;Union européenne et est un élément clé de l&apos;objectif visant à enrayer l&apos;érosion de la biodiversité.</ows:Abstract><ows:Keywords><ows:Keyword>Ecologie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>PROTECTEDAREAS.ZPS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>390</MaxTileRow><MinTileCol>491</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>773</MaxTileRow><MinTileCol>991</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1538</MaxTileRow><MinTileCol>1982</MinTileCol><MaxTileCol>2158</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>3068</MaxTileRow><MinTileCol>3973</MinTileCol><MaxTileCol>4317</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>6129</MaxTileRow><MinTileCol>7947</MinTileCol><MaxTileCol>8635</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>12258</MaxTileRow><MinTileCol>15895</MinTileCol><MaxTileCol>17263</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>24516</MaxTileRow><MinTileCol>31799</MinTileCol><MaxTileCol>34519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>27</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>36</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>55</MaxTileRow><MinTileCol>57</MinTileCol><MaxTileCol>73</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>103</MaxTileRow><MinTileCol>122</MinTileCol><MaxTileCol>138</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>199</MaxTileRow><MinTileCol>245</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>GEOSUD / RapidEye (2010)</ows:Title><ows:Abstract>Image satellitaire de la France métropolitaine effectuée en 2010 par les satellites de télédétection RapidEye dans le cadre du projet EQUIPEX GEOSUD.</ows:Abstract><ows:Keywords><ows:Keyword>Images satellitaires</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.80725 41.2014</ows:LowerCorner><ows:UpperCorner>10.961 50.9218</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RAPIDEYE_PYR-JPEG_WLD_WM_2010</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1531</MaxTileRow><MinTileCol>1986</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3063</MaxTileRow><MinTileCol>3972</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7945</MinTileCol><MaxTileCol>8638</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15890</MinTileCol><MaxTileCol>17277</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Régions forestières nationales</ows:Title><ows:Abstract>Cette cartographie représente des zones homogènes du point de vue des types de forêt ou de paysage sur la base de conditions physiques dominantes (climat, sol, relief).</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>REGIONS-FORESTIERES-NATIONALES_SYMBO_PYR-PNG_FXX_WM_20120809</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>15915</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>24484</MaxTileRow><MinTileCol>31830</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Routes</ows:Title><ows:Abstract>Affichage du réseau routier français et européen.</ows:Abstract><ows:Keywords><ows:Keyword>Réseau routier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1614 -21.3998</ows:LowerCorner><ows:UpperCorner>55.8465 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ROUTE_SYMBO_PYR-PNG_WLD_20120629</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2684</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>4594</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>9189</MaxTileRow><MinTileCol>5317</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>18378</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21473</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21900</MinTileRow><MaxTileRow>36757</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42938</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43817</MinTileRow><MaxTileRow>73515</MaxTileRow><MinTileCol>42539</MinTileCol><MaxTileCol>85876</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87634</MinTileRow><MaxTileRow>147030</MaxTileRow><MinTileCol>85079</MinTileCol><MaxTileCol>171744</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Routes</ows:Title><ows:Abstract>Affichage du réseau routier français et européen.</ows:Abstract><ows:Keywords><ows:Keyword>Réseau routier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ROUTE_SYMBO_PYR-PNG_WLD_20130121</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>682</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>662</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2300</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>26</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>49</MinTileRow><MaxTileRow>114</MaxTileRow><MinTileCol>108</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>102</MinTileRow><MaxTileRow>226</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Routes</ows:Title><ows:Abstract>Affichage du réseau routier français et européen.</ows:Abstract><ows:Keywords><ows:Keyword>Réseau routier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ROUTE_SYMBO_PYR-PNG_WLD_20130131</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>682</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>662</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2300</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>26</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>49</MinTileRow><MaxTileRow>114</MaxTileRow><MinTileCol>108</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>102</MinTileRow><MaxTileRow>226</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Routes</ows:Title><ows:Abstract>Affichage du réseau routier français et européen.</ows:Abstract><ows:Keywords><ows:Keyword>Réseau routier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ROUTE_SYMBO_PYR-PNG_WLD_ERM_20120530</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Référentiel parcellaire graphique (RPG) 2007</ows:Title><ows:Abstract>Référentiel parcellaire graphique : zones de cultures déclarées par les exploitants en 2007.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2007_PYR-PNG_WLD_20120615</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2007</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2007.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2007_PYR-PNG_WLD_20121001</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Référentiel parcellaire graphique (RPG) 2008</ows:Title><ows:Abstract>Référentiel parcellaire graphique : zones de cultures déclarées par les exploitants en 2008.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2008_PYR-PNG_WLD_20120615</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2008</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2008.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2008_PYR-PNG_WLD_20121001</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Référentiel parcellaire graphique (RPG) 2009</ows:Title><ows:Abstract>Référentiel parcellaire graphique : zones de cultures déclarées par les exploitants en 2009.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2009_PYR-PNG_WLD_20120615</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2009</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de cultures déclarées par les exploitants en 2009.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2635 -21.419</ows:LowerCorner><ows:UpperCorner>56.0237 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2009_PYR-PNG_WLD_20121001</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5370</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10741</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>18380</MaxTileRow><MinTileCol>10625</MinTileCol><MaxTileCol>21483</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>36761</MaxTileRow><MinTileCol>21251</MinTileCol><MaxTileCol>42966</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Référentiel parcellaire graphique (RPG) 2010</ows:Title><ows:Abstract>Référentiel parcellaire graphique : zones de culture déclarées par les exploitants en 2010.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2010_PYR-PNG_WLD_20120709</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Registre parcellaire graphique (RPG) 2010</ows:Title><ows:Abstract>Registre parcellaire graphique : zones de culture déclarées par les exploitants en 2010.</ows:Abstract><ows:Keywords><ows:Keyword>Agriculture</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.34206 41.2065</ows:LowerCorner><ows:UpperCorner>9.82939 51.2203</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>RPG2010_PYR-PNG_WLD_20120924</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>683</MinTileRow><MaxTileRow>766</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1367</MinTileRow><MaxTileRow>1532</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2734</MinTileRow><MaxTileRow>3065</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5469</MinTileRow><MaxTileRow>6130</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>8639</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10938</MinTileRow><MaxTileRow>12260</MaxTileRow><MinTileCol>15897</MinTileCol><MaxTileCol>17278</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21876</MinTileRow><MaxTileRow>24521</MaxTileRow><MinTileCol>31795</MinTileCol><MaxTileCol>34557</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Cartographie LA REUNION 1980</ows:Title><ows:Abstract>Zone de la Réunion 1980</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>55.0453 -21.4954</ows:LowerCorner><ows:UpperCorner>55.6708 -20.6783</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN-EDUGEO_PYR-PNG_FXX_WM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>572</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>668</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>1144</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>1337</MinTileCol><MaxTileCol>1340</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>2288</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>2674</MinTileCol><MaxTileCol>2681</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>4577</MinTileRow><MaxTileRow>4597</MaxTileRow><MinTileCol>5348</MinTileCol><MaxTileCol>5362</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>9154</MinTileRow><MaxTileRow>9194</MaxTileRow><MinTileCol>10697</MinTileCol><MaxTileCol>10725</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>18308</MinTileRow><MaxTileRow>18388</MaxTileRow><MinTileCol>21394</MinTileCol><MaxTileCol>21451</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>36616</MinTileRow><MaxTileRow>36776</MaxTileRow><MinTileCol>42788</MinTileCol><MaxTileCol>42902</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>35</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>71</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>143</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>286</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>334</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>PYRAMIDE SCAN EXPRESS CLASSIQUE</ows:Title><ows:Abstract>Cartographie topographique multi-échelles du territoire français issue des bases de données vecteur de l’IGN –réalisée selon un processus cartographique innovant fortement automatisé.test</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-22.5 39.8841</ows:LowerCorner><ows:UpperCorner>22.5 55.7766</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN-EXPRESS-CLASSIQUE_PYR-JPG_WLD_WM_WMTS_20130612</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>332</MinTileRow><MaxTileRow>386</MaxTileRow><MinTileCol>478</MinTileCol><MaxTileCol>546</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>772</MaxTileRow><MinTileCol>986</MinTileCol><MaxTileCol>1090</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>1972</MinTileCol><MaxTileCol>2180</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2723</MinTileRow><MaxTileRow>3079</MaxTileRow><MinTileCol>3955</MinTileCol><MaxTileCol>4332</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7951</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15903</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21863</MinTileRow><MaxTileRow>24530</MaxTileRow><MinTileCol>31753</MinTileCol><MaxTileCol>34575</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43776</MinTileRow><MaxTileRow>49024</MaxTileRow><MinTileCol>63552</MinTileCol><MaxTileCol>69103</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>20</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>40</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>56</MinTileCol><MaxTileCol>71</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>112</MinTileCol><MaxTileCol>143</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>166</MinTileRow><MaxTileRow>193</MaxTileRow><MinTileCol>239</MinTileCol><MaxTileCol>273</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>PYRAMIDE SCAN EXPRESS STANDARD</ows:Title><ows:Abstract>Cartographie topographique multi-échelles du territoire français issue des bases de données vecteur de l’IGN –réalisée selon un processus cartographique innovant fortement automatisé.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-22.5 39.8841</ows:LowerCorner><ows:UpperCorner>22.5 55.7766</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN-EXPRESS-STANDARD_PYR-JPG_WLD_WM_WMTS_20130612</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>332</MinTileRow><MaxTileRow>386</MaxTileRow><MinTileCol>478</MinTileCol><MaxTileCol>546</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>772</MaxTileRow><MinTileCol>986</MinTileCol><MaxTileCol>1090</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1544</MaxTileRow><MinTileCol>1972</MinTileCol><MaxTileCol>2180</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2723</MinTileRow><MaxTileRow>3079</MaxTileRow><MinTileCol>3955</MinTileCol><MaxTileCol>4332</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6126</MaxTileRow><MinTileCol>7951</MinTileCol><MaxTileCol>8634</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12253</MaxTileRow><MinTileCol>15903</MinTileCol><MaxTileCol>17268</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21863</MinTileRow><MaxTileRow>24530</MaxTileRow><MinTileCol>31753</MinTileCol><MaxTileCol>34575</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43776</MinTileRow><MaxTileRow>49024</MaxTileRow><MinTileCol>63552</MinTileCol><MaxTileCol>69103</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>20</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>28</MinTileCol><MaxTileCol>35</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>40</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>56</MinTileCol><MaxTileCol>71</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>112</MinTileCol><MaxTileCol>143</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>166</MinTileRow><MaxTileRow>193</MaxTileRow><MinTileCol>239</MinTileCol><MaxTileCol>273</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 1000</ows:Title><ows:Abstract>Le produit SCAN 1000® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rastérisation des données vecteur de la carte nationale France (901) à l&apos;échelle 1 :1,000,000ième. Le produit couvre l&apos;ensemble du territoire français métropolitain et la Corse.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.24618 41.0864</ows:LowerCorner><ows:UpperCorner>10.8338 51.252</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN1000_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>493</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>246</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 1000</ows:Title><ows:Abstract>Le produit SCAN 1000® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rastérisation des données vecteur de la carte nationale France (901) à l&apos;échelle 1 :1,000,000ième. Le produit couvre l&apos;ensemble du territoire français métropolitain et la Corse.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.24618 41.0864</ows:LowerCorner><ows:UpperCorner>10.8338 51.02</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN1000_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>493</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>246</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 1000</ows:Title><ows:Abstract>Le produit SCAN 1000® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rastérisation des données vecteur de la carte nationale France (901) à l&apos;échelle 1 :1,000,000ième. Le produit couvre l&apos;ensemble du territoire français métropolitain et la Corse.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.24618 41.0864</ows:LowerCorner><ows:UpperCorner>10.8338 51.252</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN1000_PYR-JPEG_WLD_WM_WMTS_3D_V2</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>341</MinTileRow><MaxTileRow>383</MaxTileRow><MinTileCol>493</MinTileCol><MaxTileCol>539</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>5</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>11</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>134</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>246</MinTileCol><MaxTileCol>269</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 100</ows:Title><ows:Abstract>Le produit SCAN 100® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rasterisation des données vecteur servant à la réalisation des cartes de la série au 1:100,000ième de l&apos;IGN. Le produit SCAN 100® version 2 se compose d&apos;images numériques sans habillage ni surcharge et d&apos;indications de géoréférencement.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3788 -21.7006</ows:LowerCorner><ows:UpperCorner>56.0391 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN100_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2301</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4602</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9204</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 100</ows:Title><ows:Abstract>Le produit SCAN 100® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rasterisation des données vecteur servant à la réalisation des cartes de la série au 1:100,000ième de l&apos;IGN. Le produit SCAN 100® version 2 se compose d&apos;images numériques sans habillage ni surcharge et d&apos;indications de géoréférencement.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3788 -21.7006</ows:LowerCorner><ows:UpperCorner>56.0391 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN100_PYR-JPEG_WLD_WM_WMTS_3D_20110801</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2301</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4602</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9204</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 100</ows:Title><ows:Abstract>Le produit SCAN 100® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rasterisation des données vecteur servant à la réalisation des cartes de la série au 1:100,000ième de l&apos;IGN. Le produit SCAN 100® version 2 se compose d&apos;images numériques sans habillage ni surcharge et d&apos;indications de géoréférencement.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3788 -21.7006</ows:LowerCorner><ows:UpperCorner>56.0391 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN100_PYR-JPEG_WLD_WM_WMTS_3D_20121201</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2301</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4602</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9204</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 100</ows:Title><ows:Abstract>Le produit SCAN 100® version 2 est une collection d&apos;images numériques en couleurs, obtenues par rasterisation des données vecteur servant à la réalisation des cartes de la série au 1:100,000ième de l&apos;IGN. Le produit SCAN 100® version 2 se compose d&apos;images numériques sans habillage ni surcharge et d&apos;indications de géoréférencement.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.71319 -21.7006</ows:LowerCorner><ows:UpperCorner>56.0391 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN100_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1342</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2301</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2685</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4602</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>5371</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9204</MaxTileRow><MinTileCol>7948</MinTileCol><MaxTileCol>10742</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 25 Touristique</ows:Title><ows:Abstract>Le produit SCAN 25® version 2 est une collection d&apos;images cartographiques numériques en couleurs, obtenues par rasterisation des données servant à la réalisation des cartes de la série au 1 : 25 000. Le produit SCAN 25® version 2 se compose d&apos;images numériques sans habillage ni surchage et d&apos;indications de géoréférencement</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.66635 41.2097</ows:LowerCorner><ows:UpperCorner>10.8198 50.9307</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN25_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>34</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 25 Touristique</ows:Title><ows:Abstract>Le produit SCAN 25® version 2 est une collection d&apos;images cartographiques numériques en couleurs, obtenues par rasterisation des données servant à la réalisation des cartes de la série au 1 : 25 000. Le produit SCAN 25® version 2 se compose d&apos;images numériques sans habillage ni surchage et d&apos;indications de géoréférencement</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.206 -21.4284</ows:LowerCorner><ows:UpperCorner>55.847 50.9307</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN25_PYR-JPEG_WLD_WM_WMTS_3D_2012-07-01</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>34</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 25 Touristique</ows:Title><ows:Abstract>Le produit SCAN 25® version 2 est une collection d&apos;images cartographiques numériques en couleurs, obtenues par rasterisation des données servant à la réalisation des cartes de la série au 1 : 25 000. Le produit SCAN 25® version 2 se compose d&apos;images numériques sans habillage ni surchage et d&apos;indications de géoréférencement</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-178.206 -21.4284</ows:LowerCorner><ows:UpperCorner>55.847 50.9307</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN25_PYR-JPEG_WLD_WM_WMTS_3D_2012-10-01</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>34</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 25 Touristique</ows:Title><ows:Abstract>Le produit SCAN 25® version 2 est une collection d&apos;images cartographiques numériques en couleurs, obtenues par rasterisation des données servant à la réalisation des cartes de la série au 1 : 25 000. Le produit SCAN 25® version 2 se compose d&apos;images numériques sans habillage ni surchage et d&apos;indications de géoréférencement</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.66635 41.2097</ows:LowerCorner><ows:UpperCorner>10.8198 50.9307</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN25_PYR-JPEG_WLD_WM_WMTS_3D_2013-01-01</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>5</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>10</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>40</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>81</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>163</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>326</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>34</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>40</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCAN 25 Touristique</ows:Title><ows:Abstract>Le produit SCAN 25® version 2 est une collection d&apos;images cartographiques numériques en couleurs, obtenues par rasterisation des données servant à la réalisation des cartes de la série au 1 : 25 000. Le produit SCAN 25® version 2 se compose d&apos;images numériques sans habillage ni surchage et d&apos;indications de géoréférencement</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.1891 -21.4284</ows:LowerCorner><ows:UpperCorner>55.847 50.9307</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCAN25_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>670</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>1148</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1341</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2297</MaxTileRow><MinTileCol>1329</MinTileCol><MaxTileCol>2683</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4595</MaxTileRow><MinTileCol>2658</MinTileCol><MaxTileCol>5366</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>9190</MaxTileRow><MinTileCol>5316</MinTileCol><MaxTileCol>10733</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21467</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21889</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42934</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>4</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>4</MinTileCol><MaxTileCol>5</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>9</MinTileCol><MaxTileCol>10</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>17</MinTileRow><MaxTileRow>17</MaxTileRow><MinTileCol>19</MinTileCol><MaxTileCol>20</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>France administrative</ows:Title><ows:Abstract>Carte de la France administrative (régions, départements, arrondissements, cantons, communes)</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2459 -21.6371</ows:LowerCorner><ows:UpperCorner>56.052 51.5596</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANADM_PYR-JPEG_WLD_WM_20120624</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>339</MinTileRow><MaxTileRow>575</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>84</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>169</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de l&apos;état-major - environs de Paris (1818 - 1824)</ows:Title><ows:Abstract>Carte des environs de Paris au 1 : 10 000 établie entre 1818 et 1824.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.82682 48.3847</ows:LowerCorner><ows:UpperCorner>2.79738 49.5142</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANEM10_PYR-JPEG_FXX_WM_1824</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>349</MinTileRow><MaxTileRow>354</MaxTileRow><MinTileCol>517</MinTileCol><MaxTileCol>519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>698</MinTileRow><MaxTileRow>708</MaxTileRow><MinTileCol>1034</MinTileCol><MaxTileCol>1039</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1397</MinTileRow><MaxTileRow>1417</MaxTileRow><MinTileCol>2068</MinTileCol><MaxTileCol>2079</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2795</MinTileRow><MaxTileRow>2834</MaxTileRow><MinTileCol>4137</MinTileCol><MaxTileCol>4159</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5590</MinTileRow><MaxTileRow>5669</MaxTileRow><MinTileCol>8275</MinTileCol><MaxTileCol>8319</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11181</MinTileRow><MaxTileRow>11338</MaxTileRow><MinTileCol>16550</MinTileCol><MaxTileCol>16638</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>22363</MinTileRow><MaxTileRow>22676</MaxTileRow><MinTileCol>33100</MinTileCol><MaxTileCol>33277</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>129</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>174</MinTileRow><MaxTileRow>177</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>259</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte de l&apos;état-major (1820-1866)</ows:Title><ows:Abstract>Carte française en couleurs du XIXè siècle en couleurs superposable aux cartes et données modernes.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-6.08889 41.1844</ows:LowerCorner><ows:UpperCorner>10.961 51.2745</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANEM40_PYR-JPEG_FXX_WM_20130124</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>495</MinTileCol><MaxTileCol>540</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>684</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>991</MinTileCol><MaxTileCol>1080</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1531</MaxTileRow><MinTileCol>1983</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3063</MaxTileRow><MinTileCol>3966</MinTileCol><MaxTileCol>4321</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6127</MaxTileRow><MinTileCol>7933</MinTileCol><MaxTileCol>8642</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10944</MinTileRow><MaxTileRow>12255</MaxTileRow><MinTileCol>15866</MinTileCol><MaxTileCol>17285</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>30</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>61</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>123</MinTileCol><MaxTileCol>135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>171</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>247</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Litto3D ®</ows:Title><ows:Abstract>Modèle numérique continu terre-mer sur la frange littorale.</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.75296 42.9786</ows:LowerCorner><ows:UpperCorner>6.25761 43.2733</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANLITTO3D_PYR-PNG_WLD_WM_20120704</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>375</MinTileRow><MaxTileRow>376</MaxTileRow><MinTileCol>528</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>750</MinTileRow><MaxTileRow>752</MaxTileRow><MinTileCol>1056</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1500</MinTileRow><MaxTileRow>1505</MaxTileRow><MinTileCol>2113</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>3001</MinTileRow><MaxTileRow>3010</MaxTileRow><MinTileCol>4226</MinTileCol><MaxTileCol>4238</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>6003</MinTileRow><MaxTileRow>6021</MaxTileRow><MinTileCol>8453</MinTileCol><MaxTileCol>8476</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>12006</MinTileRow><MaxTileRow>12043</MaxTileRow><MinTileCol>16907</MinTileCol><MaxTileCol>16953</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>24012</MinTileRow><MaxTileRow>24086</MaxTileRow><MinTileCol>33815</MinTileCol><MaxTileCol>33907</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>23</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>33</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>46</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>66</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>93</MinTileRow><MaxTileRow>94</MaxTileRow><MinTileCol>132</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>187</MinTileRow><MaxTileRow>188</MaxTileRow><MinTileCol>264</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte littorale (SHOM/IGN)</ows:Title><ows:Abstract>Assemblage des cartes marines du Service hydrographique et océanographique de la marine (SHOM) et des cartes terrestres de l’IGN.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-61.9629 -21.5348</ows:LowerCorner><ows:UpperCorner>56.1621 51.454</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANLITTO_PYR-JPEG_WLD_WM_20120709</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>335</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>671</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2299</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2687</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>4598</MaxTileRow><MinTileCol>2686</MinTileCol><MaxTileCol>5374</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5452</MinTileRow><MaxTileRow>9196</MaxTileRow><MinTileCol>5372</MinTileCol><MaxTileCol>10748</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10904</MinTileRow><MaxTileRow>18392</MaxTileRow><MinTileCol>10744</MinTileCol><MaxTileCol>21496</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21808</MinTileRow><MaxTileRow>36784</MaxTileRow><MinTileCol>21488</MinTileCol><MaxTileCol>42992</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>41</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>83</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>167</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>167</MinTileCol><MaxTileCol>335</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCANREG</ows:Title><ows:Abstract>Le produit SCAN Régional® version 2 est une collection d&apos;images cartographiques numériques en couleur, obtenues par rastérisation des données vecteur de la série « Cartes régionales » éditées par l&apos;IGN. Ces images couvrent l&apos;ensemble du territoire français métropolitain.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.71319 40.8523</ows:LowerCorner><ows:UpperCorner>11.4297 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANREG_PYR-JPEG_WLD_WM_WMTS_3D</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>384</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>768</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>1537</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>3074</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>192</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCANREG</ows:Title><ows:Abstract>Le produit SCAN Régional® version 2 est une collection d&apos;images cartographiques numériques en couleur, obtenues par rastérisation des données vecteur de la série « Cartes régionales » éditées par l&apos;IGN. Ces images couvrent l&apos;ensemble du territoire français métropolitain.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.71319 40.8523</ows:LowerCorner><ows:UpperCorner>11.4297 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANREG_PYR-JPEG_WLD_WM_WMTS_3D_2013</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>384</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>768</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>1537</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>3074</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>192</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte SCANREG</ows:Title><ows:Abstract>Le produit SCAN Régional® version 2 est une collection d&apos;images cartographiques numériques en couleur, obtenues par rastérisation des données vecteur de la série « Cartes régionales » éditées par l&apos;IGN. Ces images couvrent l&apos;ensemble du territoire français métropolitain.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.71319 40.8523</ows:LowerCorner><ows:UpperCorner>11.4297 51.1615</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANREG_PYR-JPEG_WLD_WM_WMTS_3D_V1</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>0</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>0</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>384</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>768</MaxTileRow><MinTileCol>993</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>1537</MaxTileRow><MinTileCol>1987</MinTileCol><MaxTileCol>2164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2726</MinTileRow><MaxTileRow>3074</MaxTileRow><MinTileCol>3974</MinTileCol><MaxTileCol>4328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>3</MaxTileRow><MinTileCol>3</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>5</MinTileRow><MaxTileRow>6</MaxTileRow><MinTileCol>7</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>10</MinTileRow><MaxTileRow>12</MaxTileRow><MinTileCol>15</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>24</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>42</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>67</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>85</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>135</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>170</MinTileRow><MaxTileRow>192</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>270</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte topographique type 1900</ows:Title><ows:Abstract>Cartes au 1 : 50 000 (1ère édition issue de la Cartothèque de l&apos;IGN) N°XXIII-14 Paris (format utile original 53 x 40 cm) Et ses 8 feuilles environnantes : XXII-13 Pontoise XXII-14 Versailles XXII-15 Rambouillet XXIII-13 L&apos;Isle-Adam XXIII-15 Corbeil XXIV-13 Dammartin-en-Goëlle XXIV-14 Lagny XXIV-15 Brie-Comte-Robert Dressées, héliogravées et publiées par le Service géographique de l&apos;armée en 1906, d&apos;après les travaux exécutés sur le terrain entre 1878 et 1901.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.62941 48.4726</ows:LowerCorner><ows:UpperCorner>3 49.1548</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANTYPE1900_PYR-JPG_WLD_WM_20120510</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>701</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1033</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1403</MinTileRow><MaxTileRow>1415</MaxTileRow><MinTileCol>2066</MinTileCol><MaxTileCol>2082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2807</MinTileRow><MaxTileRow>2831</MaxTileRow><MinTileCol>4132</MinTileCol><MaxTileCol>4164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5615</MinTileRow><MaxTileRow>5663</MaxTileRow><MinTileCol>8265</MinTileCol><MaxTileCol>8328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11231</MinTileRow><MaxTileRow>11326</MaxTileRow><MinTileCol>16531</MinTileCol><MaxTileCol>16657</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>21</MinTileRow><MaxTileRow>22</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>43</MinTileRow><MaxTileRow>44</MaxTileRow><MinTileCol>64</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>87</MinTileRow><MaxTileRow>88</MaxTileRow><MinTileCol>129</MinTileCol><MaxTileCol>130</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>175</MinTileRow><MaxTileRow>176</MaxTileRow><MinTileCol>258</MinTileCol><MaxTileCol>260</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte topographique type 1900</ows:Title><ows:Abstract>Cartes au 1 : 50 000 (1ère édition issue de la Cartothèque de l&apos;IGN) N°XXIII-14 Paris (format utile original 53 x 40 cm) Et ses 8 feuilles environnantes : XXII-13 Pontoise XXII-14 Versailles XXII-15 Rambouillet XXIII-13 L&apos;Isle-Adam XXIII-15 Corbeil XXIV-13 Dammartin-en-Goëlle XXIV-14 Lagny XXIV-15 Brie-Comte-Robert Dressées, héliogravées et publiées par le Service géographique de l&apos;armée en 1906, d&apos;après les travaux exécutés sur le terrain entre 1878 et 1901.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.62941 48.4726</ows:LowerCorner><ows:UpperCorner>3 49.1548</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANTYPE1900_PYR-JPG_WLD_WM_20120705</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>701</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1033</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1403</MinTileRow><MaxTileRow>1415</MaxTileRow><MinTileCol>2066</MinTileCol><MaxTileCol>2082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2807</MinTileRow><MaxTileRow>2831</MaxTileRow><MinTileCol>4132</MinTileCol><MaxTileCol>4164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5615</MinTileRow><MaxTileRow>5663</MaxTileRow><MinTileCol>8265</MinTileCol><MaxTileCol>8328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11231</MinTileRow><MaxTileRow>11326</MaxTileRow><MinTileCol>16531</MinTileCol><MaxTileCol>16657</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Carte topographique - environs de Paris (1906)</ows:Title><ows:Abstract>Carte topographique de Paris et de ses environs éditée en 1906.</ows:Abstract><ows:Keywords><ows:Keyword>Cartes historiques</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>1.62941 48.4726</ows:LowerCorner><ows:UpperCorner>3 49.1548</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SCANTYPE1900_PYR-JPG_WM_20130124</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>350</MinTileRow><MaxTileRow>353</MaxTileRow><MinTileCol>516</MinTileCol><MaxTileCol>520</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>701</MinTileRow><MaxTileRow>707</MaxTileRow><MinTileCol>1033</MinTileCol><MaxTileCol>1041</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1403</MinTileRow><MaxTileRow>1415</MaxTileRow><MinTileCol>2066</MinTileCol><MaxTileCol>2082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2807</MinTileRow><MaxTileRow>2831</MaxTileRow><MinTileCol>4132</MinTileCol><MaxTileCol>4164</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5615</MinTileRow><MaxTileRow>5663</MaxTileRow><MinTileCol>8265</MinTileCol><MaxTileCol>8328</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11231</MinTileRow><MaxTileRow>11326</MaxTileRow><MinTileCol>16531</MinTileCol><MaxTileCol>16657</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Trait de côte Histolitt</ows:Title><ows:Abstract>Formalisation de la limite entre le domaine terrestre et le domaine marin.</ows:Abstract><ows:Keywords><ows:Keyword>Altitude</ows:Keyword><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.2529 -21.51</ows:LowerCorner><ows:UpperCorner>55.9472 51.1388</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SHOM-TDC_PYR-PNG_WLD_WM_20120904</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>332</MinTileCol><MaxTileCol>676</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>664</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1328</MinTileCol><MaxTileCol>2688</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>4597</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5474</MinTileRow><MaxTileRow>9194</MaxTileRow><MinTileCol>5313</MinTileCol><MaxTileCol>10737</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10949</MinTileRow><MaxTileRow>18381</MaxTileRow><MinTileCol>10634</MinTileCol><MaxTileCol>21466</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21907</MinTileRow><MaxTileRow>36763</MaxTileRow><MinTileCol>21269</MinTileCol><MaxTileCol>42933</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43814</MinTileRow><MaxTileRow>73526</MaxTileRow><MinTileCol>42538</MinTileCol><MaxTileCol>85866</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87644</MinTileRow><MaxTileRow>147036</MaxTileRow><MinTileCol>85076</MinTileCol><MaxTileCol>171732</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>83</MinTileCol><MaxTileCol>171</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>166</MinTileCol><MaxTileCol>342</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>SPOT BORDEAUX 2003</ows:Title><ows:Abstract>Zone de Bordeaux 2003</ows:Abstract><ows:Keywords><ows:Keyword>EDUGEO</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-1.12593 44.6754</ows:LowerCorner><ows:UpperCorner>0.125103 45.5582</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SPOT-EDUGEO_PYR-PNG_WLD_WM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>366</MinTileRow><MaxTileRow>369</MaxTileRow><MinTileCol>508</MinTileCol><MaxTileCol>512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>732</MinTileRow><MaxTileRow>739</MaxTileRow><MinTileCol>1017</MinTileCol><MaxTileCol>1024</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1464</MinTileRow><MaxTileRow>1478</MaxTileRow><MinTileCol>2035</MinTileCol><MaxTileCol>2049</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2928</MinTileRow><MaxTileRow>2957</MaxTileRow><MinTileCol>4070</MinTileCol><MaxTileCol>4098</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5857</MinTileRow><MaxTileRow>5914</MaxTileRow><MinTileCol>8140</MinTileCol><MaxTileCol>8197</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11715</MinTileRow><MaxTileRow>11829</MaxTileRow><MinTileCol>16281</MinTileCol><MaxTileCol>16395</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23430</MinTileRow><MaxTileRow>23658</MaxTileRow><MinTileCol>32563</MinTileCol><MaxTileCol>32790</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>46</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>64</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>92</MaxTileRow><MinTileCol>127</MinTileCol><MaxTileCol>128</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>183</MinTileRow><MaxTileRow>184</MaxTileRow><MinTileCol>254</MinTileCol><MaxTileCol>256</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Spot 5</ows:Title><ows:Abstract>Cette couche est formée d&apos;images prises par le satellite SPOT 5 en 2009 et 2010 depuis une altitude de 800 km. La résolution des images initiales est de 2,5 mètres/pixel, ce qui permet d&apos;identifier l&apos;existence de bâtiments, routes et chemins, et sa précision est homogène avec celle du référentiel à Grande Echelle. Le satellite SPOT 5 est exploité depuis 2002 par le Centre national d&apos;études spatiales (CNES) et les images sont distribuées par la société Spot Image (Astrium GEO-Information Services). L&apos;expertise de l&apos;IGN menée à la demande du CNES a permis d&apos;affiner les performances géométriques du capteur SPOT5, dans l&apos;intérêt de tous les utilisateurs. La couche &quot; SPOT 5 &quot; visible sur Géoportail est issue de la co-édition Reference3D® entre Spot Image et l&apos;IGN. Aucune légende n&apos;est associée à cette couche. La radiométrie des images est traitée par des couleurs naturelles.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2 42.0001</ows:LowerCorner><ows:UpperCorner>4.99991 46</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SPOT5_PYR-JPEG_WLD_WM</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>760</MaxTileRow><MinTileCol>1012</MinTileCol><MaxTileCol>1052</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1520</MaxTileRow><MinTileCol>2025</MinTileCol><MaxTileCol>2104</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>3041</MaxTileRow><MinTileCol>4050</MinTileCol><MaxTileCol>4209</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5828</MinTileRow><MaxTileRow>6082</MaxTileRow><MinTileCol>8100</MinTileCol><MaxTileCol>8419</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11657</MinTileRow><MaxTileRow>12164</MaxTileRow><MinTileCol>16201</MinTileCol><MaxTileCol>16839</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23315</MinTileRow><MaxTileRow>24328</MaxTileRow><MinTileCol>32403</MinTileCol><MaxTileCol>33678</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Spot 5</ows:Title><ows:Abstract>Cette couche est formée d&apos;images prises par le satellite SPOT 5 en 2009 et 2010 depuis une altitude de 800 km. La résolution des images initiales est de 2,5 mètres/pixel, ce qui permet d&apos;identifier l&apos;existence de bâtiments, routes et chemins, et sa précision est homogène avec celle du référentiel à Grande Echelle. Le satellite SPOT 5 est exploité depuis 2002 par le Centre national d&apos;études spatiales (CNES) et les images sont distribuées par la société Spot Image (Astrium GEO-Information Services). L&apos;expertise de l&apos;IGN menée à la demande du CNES a permis d&apos;affiner les performances géométriques du capteur SPOT5, dans l&apos;intérêt de tous les utilisateurs. La couche &quot; SPOT 5 &quot; visible sur Géoportail est issue de la co-édition Reference3D® entre Spot Image et l&apos;IGN. Aucune légende n&apos;est associée à cette couche. La radiométrie des images est traitée par des couleurs naturelles.</ows:Abstract><ows:Keywords><ows:Keyword>Photographies</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-2 42.0001</ows:LowerCorner><ows:UpperCorner>4.99991 46</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SPOT5_PYR-JPEG_WLD_WM_20120624</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/jpeg</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>364</MinTileRow><MaxTileRow>380</MaxTileRow><MinTileCol>506</MinTileCol><MaxTileCol>526</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>728</MinTileRow><MaxTileRow>760</MaxTileRow><MinTileCol>1012</MinTileCol><MaxTileCol>1052</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1457</MinTileRow><MaxTileRow>1520</MaxTileRow><MinTileCol>2025</MinTileCol><MaxTileCol>2104</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2914</MinTileRow><MaxTileRow>3041</MaxTileRow><MinTileCol>4050</MinTileCol><MaxTileCol>4209</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5828</MinTileRow><MaxTileRow>6082</MaxTileRow><MinTileCol>8100</MinTileCol><MaxTileCol>8419</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>11657</MinTileRow><MaxTileRow>12164</MaxTileRow><MinTileCol>16201</MinTileCol><MaxTileCol>16839</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>23315</MinTileRow><MaxTileRow>24328</MaxTileRow><MinTileCol>32403</MinTileCol><MaxTileCol>33678</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>22</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>32</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>45</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>63</MinTileCol><MaxTileCol>65</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>91</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>126</MinTileCol><MaxTileCol>131</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>182</MinTileRow><MaxTileRow>190</MaxTileRow><MinTileCol>253</MinTileCol><MaxTileCol>263</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Iris</ows:Title><ows:Abstract>Zones Iris définies par l&apos;INSEE pour les besoins des recensements sur l&apos;ensemble des communes de plus de 10 000 habitants et la plupart des communes de 5 000 à 10 000 habitants.</ows:Abstract><ows:Keywords><ows:Keyword>Divers</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>STATISTICALUNITS.IRIS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sylvoécorégions</ows:Title><ows:Abstract>Découpage de la France métropolitaine prenant en compte les facteurs biogéographiques déterminant la production forestière et la répartition des grands types d’habitat forestier.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SYLVOECOREGIONS_SYMBO_PYR-PNG_FXX_WM_20120807</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>545</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1082</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2733</MinTileRow><MaxTileRow>3061</MaxTileRow><MinTileCol>3978</MinTileCol><MaxTileCol>4314</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5475</MinTileRow><MaxTileRow>6123</MaxTileRow><MinTileCol>7957</MinTileCol><MaxTileCol>8629</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10950</MinTileRow><MaxTileRow>12246</MaxTileRow><MinTileCol>15915</MinTileCol><MaxTileCol>17259</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21908</MinTileRow><MaxTileRow>24492</MaxTileRow><MinTileCol>31830</MinTileCol><MaxTileCol>34510</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Sylvoécorégions</ows:Title><ows:Abstract>Découpage de la France métropolitaine prenant en compte les facteurs biogéographiques déterminant la production forestière et la répartition des grands types d’habitat forestier.</ows:Abstract><ows:Keywords><ows:Keyword>Occupation des terres</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-5.15047 41.3252</ows:LowerCorner><ows:UpperCorner>9.57054 51.0991</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>SYLVOECOREGIONS_SYMBO_PYR-PNG_FXX_WM_20121031</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>384</MaxTileRow><MinTileCol>496</MinTileCol><MaxTileCol>544</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>672</MinTileRow><MaxTileRow>768</MaxTileRow><MinTileCol>992</MinTileCol><MaxTileCol>1088</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1359</MinTileRow><MaxTileRow>1536</MaxTileRow><MinTileCol>1984</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3072</MaxTileRow><MinTileCol>3968</MinTileCol><MaxTileCol>4320</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5472</MinTileRow><MaxTileRow>6128</MaxTileRow><MinTileCol>7952</MinTileCol><MaxTileCol>8640</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>12255</MaxTileRow><MinTileCol>15904</MinTileCol><MaxTileCol>17264</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24496</MaxTileRow><MinTileCol>31824</MinTileCol><MaxTileCol>34512</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>32</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>32</MinTileRow><MaxTileRow>48</MaxTileRow><MinTileCol>48</MinTileCol><MaxTileCol>79</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>80</MinTileRow><MaxTileRow>96</MaxTileRow><MinTileCol>111</MinTileCol><MaxTileCol>144</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>160</MinTileRow><MaxTileRow>192</MaxTileRow><MinTileCol>240</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Réseau ferroviaire</ows:Title><ows:Abstract>Carte du réseau ferroviaire</ows:Abstract><ows:Keywords><ows:Keyword>Voies ferrées</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-26.711 20.9918</ows:LowerCorner><ows:UpperCorner>54.9094 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>TRANSPORTNETWORKS.RAILWAYS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>338</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>541</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>681</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1078</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1366</MinTileRow><MaxTileRow>1530</MaxTileRow><MinTileCol>1989</MinTileCol><MaxTileCol>2157</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>3048</MaxTileRow><MinTileCol>3992</MinTileCol><MaxTileCol>4312</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>6088</MaxTileRow><MinTileCol>7984</MinTileCol><MaxTileCol>8624</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>12175</MaxTileRow><MinTileCol>15968</MinTileCol><MaxTileCol>17248</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>24352</MaxTileRow><MinTileCol>31936</MinTileCol><MaxTileCol>34496</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>48704</MaxTileRow><MinTileCol>63872</MinTileCol><MaxTileCol>68991</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87664</MinTileRow><MaxTileRow>97392</MaxTileRow><MinTileCol>127744</MinTileCol><MaxTileCol>137968</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>28</MaxTileRow><MinTileCol>27</MinTileCol><MaxTileCol>43</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>56</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>50</MinTileRow><MaxTileRow>112</MaxTileRow><MinTileCol>109</MinTileCol><MaxTileCol>169</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>101</MinTileRow><MaxTileRow>225</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Routes</ows:Title><ows:Abstract>Affichage du réseau routier français et européen.</ows:Abstract><ows:Keywords><ows:Keyword>Réseau routier</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 72.679</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>TRANSPORTNETWORKS.ROADS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>340</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>330</MinTileCol><MaxTileCol>672</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>682</MinTileRow><MaxTileRow>1150</MaxTileRow><MinTileCol>662</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1363</MinTileRow><MaxTileRow>2300</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2656</MinTileCol><MaxTileCol>5367</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5471</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5312</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10943</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10624</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21904</MinTileRow><MaxTileRow>36768</MaxTileRow><MinTileCol>21264</MinTileCol><MaxTileCol>42943</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43824</MinTileRow><MaxTileRow>73520</MaxTileRow><MinTileCol>42528</MinTileCol><MaxTileCol>85871</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87648</MinTileRow><MaxTileRow>147024</MaxTileRow><MinTileCol>85071</MinTileCol><MaxTileCol>171743</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>12</MinTileRow><MaxTileRow>29</MaxTileRow><MinTileCol>26</MinTileCol><MaxTileCol>42</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>24</MinTileRow><MaxTileRow>58</MaxTileRow><MinTileCol>54</MinTileCol><MaxTileCol>84</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>49</MinTileRow><MaxTileRow>114</MaxTileRow><MinTileCol>108</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>102</MinTileRow><MaxTileRow>226</MaxTileRow><MinTileCol>218</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Aéroports</ows:Title><ows:Abstract>Localisation des aéroports, aérodromes et héliports, des pistes, bâtiments et équipements dédiés.</ows:Abstract><ows:Keywords><ows:Keyword>Réseaux de transports</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-68.709 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 76.5354</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>TRANSPORTNETWORKS.RUNWAYS</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>336</MinTileRow><MaxTileRow>576</MaxTileRow><MinTileCol>336</MinTileCol><MaxTileCol>671</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>680</MinTileRow><MaxTileRow>1152</MaxTileRow><MinTileCol>672</MinTileCol><MaxTileCol>1344</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>2304</MaxTileRow><MinTileCol>1343</MinTileCol><MaxTileCol>2679</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5359</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10720</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10960</MinTileRow><MaxTileRow>18375</MaxTileRow><MinTileCol>10632</MinTileCol><MaxTileCol>21439</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>36744</MaxTileRow><MinTileCol>21272</MinTileCol><MaxTileCol>42879</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43840</MinTileRow><MaxTileRow>73488</MaxTileRow><MinTileCol>42544</MinTileCol><MaxTileCol>85759</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87696</MinTileRow><MaxTileRow>146976</MaxTileRow><MinTileCol>85104</MinTileCol><MaxTileCol>171519</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>39</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>47</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>72</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>88</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>144</MaxTileRow><MinTileCol>72</MinTileCol><MaxTileCol>168</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>168</MinTileRow><MaxTileRow>288</MaxTileRow><MinTileCol>168</MinTileCol><MaxTileCol>336</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>MNT LITTO3D</ows:Title><ows:Abstract>MNT LITTO3D au pas de 1M</ows:Abstract><ows:Keywords><ows:Keyword>Littoral</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>5.45 42.95</ows:LowerCorner><ows:UpperCorner>6.3 43.3</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>UNDERWATER.ELEVATION.ELEVATIONGRIDCOVERAGE</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/x-bil;bits=32</Format><TileMatrixSetLink><TileMatrixSet>WGS84G</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>0</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>1</MinTileCol><MaxTileCol>1</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>1</TileMatrix><MinTileRow>0</MinTileRow><MaxTileRow>0</MaxTileRow><MinTileCol>2</MinTileCol><MaxTileCol>2</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>265</MinTileRow><MaxTileRow>267</MaxTileRow><MinTileCol>1055</MinTileCol><MaxTileCol>1059</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>531</MinTileRow><MaxTileRow>535</MaxTileRow><MinTileCol>2110</MinTileCol><MaxTileCol>2119</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1062</MinTileRow><MaxTileRow>1070</MaxTileRow><MinTileCol>4220</MinTileCol><MaxTileCol>4239</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2125</MinTileRow><MaxTileRow>2141</MaxTileRow><MinTileCol>8440</MinTileCol><MaxTileCol>8478</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>4250</MinTileRow><MaxTileRow>4282</MaxTileRow><MinTileCol>16880</MinTileCol><MaxTileCol>16957</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>8501</MinTileRow><MaxTileRow>8565</MaxTileRow><MinTileCol>33760</MinTileCol><MaxTileCol>33914</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>2</TileMatrix><MinTileRow>1</MinTileRow><MaxTileRow>1</MaxTileRow><MinTileCol>4</MinTileCol><MaxTileCol>4</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>3</TileMatrix><MinTileRow>2</MinTileRow><MaxTileRow>2</MaxTileRow><MinTileCol>8</MinTileCol><MaxTileCol>8</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>4</TileMatrix><MinTileRow>4</MinTileRow><MaxTileRow>4</MaxTileRow><MinTileCol>16</MinTileCol><MaxTileCol>16</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>5</TileMatrix><MinTileRow>8</MinTileRow><MaxTileRow>8</MaxTileRow><MinTileCol>32</MinTileCol><MaxTileCol>33</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>16</MinTileRow><MaxTileRow>16</MaxTileRow><MinTileCol>65</MinTileCol><MaxTileCol>66</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>33</MinTileRow><MaxTileRow>33</MaxTileRow><MinTileCol>131</MinTileCol><MaxTileCol>132</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>66</MinTileRow><MaxTileRow>66</MaxTileRow><MinTileCol>263</MinTileCol><MaxTileCol>264</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>132</MinTileRow><MaxTileRow>133</MaxTileRow><MinTileCol>527</MinTileCol><MaxTileCol>529</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Lignes électriques</ows:Title><ows:Abstract>Affichage des lignes électriques.</ows:Abstract><ows:Keywords><ows:Keyword>Energie</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>UTILITYANDGOVERNMENTALSERVICES.ALL</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>342</MinTileRow><MaxTileRow>382</MaxTileRow><MinTileCol>497</MinTileCol><MaxTileCol>537</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>685</MinTileRow><MaxTileRow>765</MaxTileRow><MinTileCol>994</MinTileCol><MaxTileCol>1074</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1368</MinTileRow><MaxTileRow>1536</MaxTileRow><MinTileCol>1992</MinTileCol><MaxTileCol>2160</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2736</MinTileRow><MaxTileRow>4600</MaxTileRow><MinTileCol>2688</MinTileCol><MaxTileCol>5368</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5479</MinTileRow><MaxTileRow>9192</MaxTileRow><MinTileCol>5376</MinTileCol><MaxTileCol>10736</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10959</MinTileRow><MaxTileRow>18384</MaxTileRow><MinTileCol>10639</MinTileCol><MaxTileCol>21472</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21920</MinTileRow><MaxTileRow>36752</MaxTileRow><MinTileCol>21280</MinTileCol><MaxTileCol>42927</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>17</TileMatrix><MinTileRow>43840</MinTileRow><MaxTileRow>73504</MaxTileRow><MinTileCol>42559</MinTileCol><MaxTileCol>85855</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>18</TileMatrix><MinTileRow>87696</MinTileRow><MaxTileRow>147008</MaxTileRow><MinTileCol>85136</MinTileCol><MaxTileCol>171695</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>15</MinTileRow><MaxTileRow>23</MaxTileRow><MinTileCol>31</MinTileCol><MaxTileCol>39</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>47</MaxTileRow><MinTileCol>62</MinTileCol><MaxTileCol>70</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>95</MaxTileRow><MinTileCol>124</MinTileCol><MaxTileCol>140</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>191</MaxTileRow><MinTileCol>248</MinTileCol><MaxTileCol>272</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones franches urbaines</ows:Title><ows:Abstract>Zones franches créées au sein de quartiers de plus de 8 500 habitants particulièrement défavorisés, en tenant compte des éléments de nature à faciliter l’implantation d’entreprises ou le développement d’activités économiques.</ows:Abstract><ows:Keywords><ows:Keyword>Zones de gestion</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ZFU_BDD_WLD_WM_20120925</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><Layer><ows:Title>Zones urbaines sensibles</ows:Title><ows:Abstract>Zones urbaines caractérisées par la présence de grands ensembles ou de quartiers d’habitat dégradé et par un déséquilibre accentué entre l’habitat et l’emploi.</ows:Abstract><ows:Keywords><ows:Keyword>Zones de gestion</ows:Keyword></ows:Keywords><ows:WGS84BoundingBox><ows:LowerCorner>-63.3725 -21.4756</ows:LowerCorner><ows:UpperCorner>55.9259 51.3121</ows:UpperCorner></ows:WGS84BoundingBox><ows:Identifier>ZUS_BDD_WLD_WM_20120925</ows:Identifier><Style isDefault="true"><ows:Title>Données Brutes</ows:Title><ows:Abstract>Données brutes sans changement de palette</ows:Abstract><ows:Keywords><ows:Keyword>Défaut</ows:Keyword></ows:Keywords><ows:Identifier>normal</ows:Identifier><LegendURL format="image/png" xlink:href="http://gpp3-wxs.ign.fr/static/LEGENDES/NOLEGEND.JPG" width="1" height="1" minScaleDenominator="200" maxScaleDenominator="100000000" /></Style><Format>image/png</Format><TileMatrixSetLink><TileMatrixSet>PM</TileMatrixSet><TileMatrixSetLimits><TileMatrixLimits><TileMatrix>10</TileMatrix><MinTileRow>334</MinTileRow><MaxTileRow>574</MaxTileRow><MinTileCol>331</MinTileCol><MaxTileCol>675</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>11</TileMatrix><MinTileRow>677</MinTileRow><MaxTileRow>1149</MaxTileRow><MinTileCol>663</MinTileCol><MaxTileCol>1343</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>12</TileMatrix><MinTileRow>1362</MinTileRow><MaxTileRow>2298</MaxTileRow><MinTileCol>1326</MinTileCol><MaxTileCol>2686</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>13</TileMatrix><MinTileRow>2724</MinTileRow><MaxTileRow>4596</MaxTileRow><MinTileCol>2653</MinTileCol><MaxTileCol>5373</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>14</TileMatrix><MinTileRow>5457</MinTileRow><MaxTileRow>9193</MaxTileRow><MinTileCol>5307</MinTileCol><MaxTileCol>10739</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>15</TileMatrix><MinTileRow>10922</MinTileRow><MaxTileRow>18386</MaxTileRow><MinTileCol>10615</MinTileCol><MaxTileCol>21479</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>16</TileMatrix><MinTileRow>21844</MinTileRow><MaxTileRow>36772</MaxTileRow><MinTileCol>21231</MinTileCol><MaxTileCol>42951</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>6</TileMatrix><MinTileRow>19</MinTileRow><MaxTileRow>35</MaxTileRow><MinTileCol>20</MinTileCol><MaxTileCol>44</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>7</TileMatrix><MinTileRow>39</MinTileRow><MaxTileRow>71</MaxTileRow><MinTileCol>41</MinTileCol><MaxTileCol>89</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>8</TileMatrix><MinTileRow>79</MinTileRow><MaxTileRow>143</MaxTileRow><MinTileCol>82</MinTileCol><MaxTileCol>170</MaxTileCol></TileMatrixLimits><TileMatrixLimits><TileMatrix>9</TileMatrix><MinTileRow>167</MinTileRow><MaxTileRow>287</MaxTileRow><MinTileCol>165</MinTileCol><MaxTileCol>341</MaxTileCol></TileMatrixLimits></TileMatrixSetLimits></TileMatrixSetLink></Layer><TileMatrixSet><ows:Identifier>PM</ows:Identifier><ows:SupportedCRS>EPSG:3857</ows:SupportedCRS><TileMatrix><ows:Identifier>0</ows:Identifier><ScaleDenominator>559082264.0287178958533332</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1</MatrixWidth><MatrixHeight>1</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>1</ows:Identifier><ScaleDenominator>279541132.0143588959472254</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2</MatrixWidth><MatrixHeight>2</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>10</ows:Identifier><ScaleDenominator>545978.7734655447186469</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1024</MatrixWidth><MatrixHeight>1024</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>11</ows:Identifier><ScaleDenominator>272989.3867327723085907</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2048</MatrixWidth><MatrixHeight>2048</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>12</ows:Identifier><ScaleDenominator>136494.6933663861796617</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4096</MatrixWidth><MatrixHeight>4096</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>13</ows:Identifier><ScaleDenominator>68247.3466831930771477</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8192</MatrixWidth><MatrixHeight>8192</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>14</ows:Identifier><ScaleDenominator>34123.6733415965449154</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>16384</MatrixWidth><MatrixHeight>16384</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>15</ows:Identifier><ScaleDenominator>17061.8366707982724577</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>32768</MatrixWidth><MatrixHeight>32768</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>16</ows:Identifier><ScaleDenominator>8530.9183353991362289</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>65536</MatrixWidth><MatrixHeight>65536</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>17</ows:Identifier><ScaleDenominator>4265.4591676995681144</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>131072</MatrixWidth><MatrixHeight>131072</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>18</ows:Identifier><ScaleDenominator>2132.7295838497840572</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>262144</MatrixWidth><MatrixHeight>262144</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>19</ows:Identifier><ScaleDenominator>1066.3647919248918304</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>524288</MatrixWidth><MatrixHeight>524288</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>2</ows:Identifier><ScaleDenominator>139770566.0071793960087234</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4</MatrixWidth><MatrixHeight>4</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>20</ows:Identifier><ScaleDenominator>533.1823959624461134</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1048576</MatrixWidth><MatrixHeight>1048576</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>21</ows:Identifier><ScaleDenominator>266.5911979812228585</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2097152</MatrixWidth><MatrixHeight>2097152</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>3</ows:Identifier><ScaleDenominator>69885283.0035897239868063</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8</MatrixWidth><MatrixHeight>8</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>4</ows:Identifier><ScaleDenominator>34942641.5017948619934032</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>16</MatrixWidth><MatrixHeight>16</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>5</ows:Identifier><ScaleDenominator>17471320.7508974309967016</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>32</MatrixWidth><MatrixHeight>32</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>6</ows:Identifier><ScaleDenominator>8735660.3754487154983508</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>64</MatrixWidth><MatrixHeight>64</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>7</ows:Identifier><ScaleDenominator>4367830.1877243577491754</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>128</MatrixWidth><MatrixHeight>128</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>8</ows:Identifier><ScaleDenominator>2183915.0938621788745877</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>256</MatrixWidth><MatrixHeight>256</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>9</ows:Identifier><ScaleDenominator>1091957.5469310886252288</ScaleDenominator><TopLeftCorner>-20037508 20037508</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>512</MatrixWidth><MatrixHeight>512</MatrixHeight></TileMatrix></TileMatrixSet><TileMatrixSet><ows:Identifier>WGS84G</ows:Identifier><ows:SupportedCRS>IGNF:WGS84G</ows:SupportedCRS><TileMatrix><ows:Identifier>0</ows:Identifier><ScaleDenominator>279541132.0143588675418869</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2</MatrixWidth><MatrixHeight>1</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>1</ows:Identifier><ScaleDenominator>139770566.0071794337709434</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4</MatrixWidth><MatrixHeight>2</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>10</ows:Identifier><ScaleDenominator>272989.3867327723315839</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2048</MatrixWidth><MatrixHeight>1024</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>11</ows:Identifier><ScaleDenominator>136494.6933663861657919</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4096</MatrixWidth><MatrixHeight>2048</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>12</ows:Identifier><ScaleDenominator>68247.3466831930828960</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8192</MatrixWidth><MatrixHeight>4096</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>13</ows:Identifier><ScaleDenominator>34123.6733415965414480</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>16384</MatrixWidth><MatrixHeight>8192</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>14</ows:Identifier><ScaleDenominator>17061.8366707982707240</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>32768</MatrixWidth><MatrixHeight>16384</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>15</ows:Identifier><ScaleDenominator>8530.9183353991353620</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>65536</MatrixWidth><MatrixHeight>32768</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>16</ows:Identifier><ScaleDenominator>4265.4591676995676810</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>131072</MatrixWidth><MatrixHeight>65536</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>17</ows:Identifier><ScaleDenominator>2132.7295838497838405</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>262144</MatrixWidth><MatrixHeight>131072</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>18</ows:Identifier><ScaleDenominator>1066.3647919248919202</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>524288</MatrixWidth><MatrixHeight>262144</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>19</ows:Identifier><ScaleDenominator>533.1823959624459601</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1048576</MatrixWidth><MatrixHeight>524288</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>2</ows:Identifier><ScaleDenominator>69885283.0035897168854717</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8</MatrixWidth><MatrixHeight>4</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>3</ows:Identifier><ScaleDenominator>34942641.5017948584427359</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>16</MatrixWidth><MatrixHeight>8</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>4</ows:Identifier><ScaleDenominator>17471320.7508974292213679</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>32</MatrixWidth><MatrixHeight>16</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>5</ows:Identifier><ScaleDenominator>8735660.3754487146106840</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>64</MatrixWidth><MatrixHeight>32</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>6</ows:Identifier><ScaleDenominator>4367830.1877243573053420</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>128</MatrixWidth><MatrixHeight>64</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>7</ows:Identifier><ScaleDenominator>2183915.0938621786526710</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>256</MatrixWidth><MatrixHeight>128</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>8</ows:Identifier><ScaleDenominator>1091957.5469310893263355</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>512</MatrixWidth><MatrixHeight>256</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>9</ows:Identifier><ScaleDenominator>545978.7734655446631677</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1024</MatrixWidth><MatrixHeight>512</MatrixHeight></TileMatrix></TileMatrixSet><TileMatrixSet><ows:Identifier>WGS84G_PO</ows:Identifier><ows:SupportedCRS>IGNF:WGS84G</ows:SupportedCRS><TileMatrix><ows:Identifier>0</ows:Identifier><ScaleDenominator>279541132.0143588675418869</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2</MatrixWidth><MatrixHeight>1</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>1</ows:Identifier><ScaleDenominator>139770566.0071794337709434</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4</MatrixWidth><MatrixHeight>2</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>10</ows:Identifier><ScaleDenominator>272989.3867327723315839</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>2048</MatrixWidth><MatrixHeight>1024</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>11</ows:Identifier><ScaleDenominator>136494.6933663861657919</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>4096</MatrixWidth><MatrixHeight>2048</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>12</ows:Identifier><ScaleDenominator>68247.3466831930828960</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8192</MatrixWidth><MatrixHeight>4096</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>2</ows:Identifier><ScaleDenominator>69885283.0035897168854717</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>8</MatrixWidth><MatrixHeight>4</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>3</ows:Identifier><ScaleDenominator>34942641.5017948584427359</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>16</MatrixWidth><MatrixHeight>8</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>4</ows:Identifier><ScaleDenominator>17471320.7508974292213679</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>32</MatrixWidth><MatrixHeight>16</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>5</ows:Identifier><ScaleDenominator>8735660.3754487146106840</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>64</MatrixWidth><MatrixHeight>32</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>6</ows:Identifier><ScaleDenominator>4367830.1877243573053420</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>128</MatrixWidth><MatrixHeight>64</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>7</ows:Identifier><ScaleDenominator>2183915.0938621786526710</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>256</MatrixWidth><MatrixHeight>128</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>8</ows:Identifier><ScaleDenominator>1091957.5469310893263355</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>512</MatrixWidth><MatrixHeight>256</MatrixHeight></TileMatrix><TileMatrix><ows:Identifier>9</ows:Identifier><ScaleDenominator>545978.7734655446631677</ScaleDenominator><TopLeftCorner>-180 90</TopLeftCorner><TileWidth>256</TileWidth><TileHeight>256</TileHeight><MatrixWidth>1024</MatrixWidth><MatrixHeight>512</MatrixHeight></TileMatrix></TileMatrixSet></Contents></Capabilities>
    diff --git a/mapbox-gl/examples/data/WMTSCapabilities.xml b/mapbox-gl/examples/data/WMTSCapabilities.xml
    new file mode 100644
    index 0000000000..79f10e4ba4
    --- /dev/null
    +++ b/mapbox-gl/examples/data/WMTSCapabilities.xml
    @@ -0,0 +1,279 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<Capabilities version="1.0.0" xmlns="http://www.opengis.net/wmts/1.0" xmlns:gml="http://www.opengis.net/gml" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd">
    +    <ows:ServiceIdentification>
    +        <ows:Title>Koordinates Labs</ows:Title>
    +        <ows:ServiceType>OGC WMTS</ows:ServiceType>
    +        <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
    +    </ows:ServiceIdentification>
    +    <ows:ServiceProvider>
    +        <ows:ProviderName>Koordinates</ows:ProviderName>
    +        <ows:ProviderSite xlink:href="http://labs.koordinates.com"/>
    +        <ows:ServiceContact/>
    +    </ows:ServiceProvider>
    +    <ows:OperationsMetadata>
    +        <ows:Operation name="GetCapabilities">
    +            <ows:DCP>
    +                <ows:HTTP>
    +                    <ows:Get xlink:href="https://labs.koordinates.com/services;key=d740ea02e0c44cafb70dce31a774ca10/wmts/1.0.0/layer/7328/WMTSCapabilities.xml?">
    +                        <ows:Constraint name="GetEncoding">
    +                            <ows:AllowedValues>
    +                                <ows:Value>KVP</ows:Value>
    +                            </ows:AllowedValues>
    +                        </ows:Constraint>
    +                    </ows:Get>
    +                </ows:HTTP>
    +            </ows:DCP>
    +        </ows:Operation>
    +        <ows:Operation name="GetFeatureInfo">
    +            <ows:DCP>
    +                <ows:HTTP>
    +                    <ows:Get xlink:href="https://labs.koordinates.com/services;key=d740ea02e0c44cafb70dce31a774ca10/wmts/?">
    +                        <ows:Constraint name="GetEncoding">
    +                            <ows:AllowedValues>
    +                                <ows:Value>KVP</ows:Value>
    +                            </ows:AllowedValues>
    +                        </ows:Constraint>
    +                    </ows:Get>
    +                </ows:HTTP>
    +            </ows:DCP>
    +        </ows:Operation>
    +    </ows:OperationsMetadata>
    +    <Contents>
    +        <Layer>
    +            <ows:Title>New Zealand Earthquakes</ows:Title>
    +            <ows:Abstract>Historical earthquake data, accessed via the [GeoNet WFS feed](http://info.geonet.org.nz/display/appdata/Advanced+Queries). The data has been filtered to only include quakes in proximity to New Zealand with an `eventtype` of &quot;Earthquake&quot; or &quot;none&quot; per the [GeoNet catalogue](http://info.geonet.org.nz/display/appdata/Catalogue+Output). Most fields have been removed. Please also note the excluded data per this [GeoNet page](http://info.geonet.org.nz/display/appdata/The+Gap). We acknowledge the New Zealand GeoNet project and its sponsors EQC, GNS Science and LINZ, for providing data used in this layer.</ows:Abstract>
    +            <ows:Identifier>layer-7328</ows:Identifier>
    +            <ows:BoundingBox crs="urn:ogc:def:crs:EPSG::3857">
    +                <ows:LowerCorner>-20037508.342789 -6406581.708337</ows:LowerCorner>
    +                <ows:UpperCorner>20037508.342789 -3653545.667928</ows:UpperCorner>
    +            </ows:BoundingBox>
    +            <ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">
    +                <ows:LowerCorner>-180.000000 -49.454297</ows:LowerCorner>
    +                <ows:UpperCorner>180.000000 -31.160000</ows:UpperCorner>
    +            </ows:WGS84BoundingBox>
    +            <Style isDefault="true">
    +                <ows:Title>Weighted point styles</ows:Title>
    +                <ows:Identifier>style=39</ows:Identifier>
    +            </Style>
    +            <Format>image/png</Format>
    +            <InfoFormat>application/json</InfoFormat>
    +            <InfoFormat>text/html</InfoFormat>
    +            <TileMatrixSetLink>
    +                <TileMatrixSet>EPSG:3857</TileMatrixSet>
    +            </TileMatrixSetLink>
    +            <ResourceURL format="image/png" resourceType="tile" template="https://koordinates-tiles-a.global.ssl.fastly.net/services;key=d740ea02e0c44cafb70dce31a774ca10/tiles/v4/layer=7328,{style}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png"/>
    +            <ResourceURL format="application/json" resourceType="FeatureInfo" template="https://labs.koordinates.com/services;key=d740ea02e0c44cafb70dce31a774ca10/wmts/1.0.0/layer/7328/featureinfo/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}/{I}/{J}.json"/>
    +            <ResourceURL format="text/html" resourceType="FeatureInfo" template="https://labs.koordinates.com/services;key=d740ea02e0c44cafb70dce31a774ca10/wmts/1.0.0/layer/7328/featureinfo/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}/{I}/{J}.html"/>
    +        </Layer>
    +        <TileMatrixSet>
    +            <ows:Title>GoogleMapsCompatible</ows:Title>
    +            <ows:Abstract>The well-known 'GoogleMapsCompatible' tile matrix set defined by the OGC WMTS specification</ows:Abstract>
    +            <ows:Identifier>EPSG:3857</ows:Identifier>
    +            <ows:BoundingBox crs="urn:ogc:def:crs:EPSG::3857">
    +                <ows:LowerCorner>-20037508.342789 -20037508.342789</ows:LowerCorner>
    +                <ows:UpperCorner>20037508.342789 20037508.342789</ows:UpperCorner>
    +            </ows:BoundingBox>
    +            <ows:SupportedCRS>urn:ogc:def:crs:EPSG::3857</ows:SupportedCRS>
    +            <WellKnownScaleSet>urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible</WellKnownScaleSet>
    +            <TileMatrix>
    +                <ows:Identifier>0</ows:Identifier>
    +                <ScaleDenominator>559082264.029</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>1</MatrixWidth>
    +                <MatrixHeight>1</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>1</ows:Identifier>
    +                <ScaleDenominator>279541132.014</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>2</MatrixWidth>
    +                <MatrixHeight>2</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>2</ows:Identifier>
    +                <ScaleDenominator>139770566.007</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>4</MatrixWidth>
    +                <MatrixHeight>4</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>3</ows:Identifier>
    +                <ScaleDenominator>69885283.0036</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>8</MatrixWidth>
    +                <MatrixHeight>8</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>4</ows:Identifier>
    +                <ScaleDenominator>34942641.5018</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>16</MatrixWidth>
    +                <MatrixHeight>16</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>5</ows:Identifier>
    +                <ScaleDenominator>17471320.7509</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>32</MatrixWidth>
    +                <MatrixHeight>32</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>6</ows:Identifier>
    +                <ScaleDenominator>8735660.37545</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>64</MatrixWidth>
    +                <MatrixHeight>64</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>7</ows:Identifier>
    +                <ScaleDenominator>4367830.18772</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>128</MatrixWidth>
    +                <MatrixHeight>128</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>8</ows:Identifier>
    +                <ScaleDenominator>2183915.09386</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>256</MatrixWidth>
    +                <MatrixHeight>256</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>9</ows:Identifier>
    +                <ScaleDenominator>1091957.54693</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>512</MatrixWidth>
    +                <MatrixHeight>512</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>10</ows:Identifier>
    +                <ScaleDenominator>545978.773466</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>1024</MatrixWidth>
    +                <MatrixHeight>1024</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>11</ows:Identifier>
    +                <ScaleDenominator>272989.386733</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>2048</MatrixWidth>
    +                <MatrixHeight>2048</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>12</ows:Identifier>
    +                <ScaleDenominator>136494.693366</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>4096</MatrixWidth>
    +                <MatrixHeight>4096</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>13</ows:Identifier>
    +                <ScaleDenominator>68247.3466832</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>8192</MatrixWidth>
    +                <MatrixHeight>8192</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>14</ows:Identifier>
    +                <ScaleDenominator>34123.6733416</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>16384</MatrixWidth>
    +                <MatrixHeight>16384</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>15</ows:Identifier>
    +                <ScaleDenominator>17061.8366708</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>32768</MatrixWidth>
    +                <MatrixHeight>32768</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>16</ows:Identifier>
    +                <ScaleDenominator>8530.9183354</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>65536</MatrixWidth>
    +                <MatrixHeight>65536</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>17</ows:Identifier>
    +                <ScaleDenominator>4265.4591677</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>131072</MatrixWidth>
    +                <MatrixHeight>131072</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>18</ows:Identifier>
    +                <ScaleDenominator>2132.72958385</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>262144</MatrixWidth>
    +                <MatrixHeight>262144</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>19</ows:Identifier>
    +                <ScaleDenominator>1066.36479192</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>524288</MatrixWidth>
    +                <MatrixHeight>524288</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>20</ows:Identifier>
    +                <ScaleDenominator>533.182395962</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>1048576</MatrixWidth>
    +                <MatrixHeight>1048576</MatrixHeight>
    +            </TileMatrix>
    +            <TileMatrix>
    +                <ows:Identifier>21</ows:Identifier>
    +                <ScaleDenominator>266.591197981</ScaleDenominator>
    +                <TopLeftCorner>-20037508.3428 20037508.3428</TopLeftCorner>
    +                <TileWidth>256</TileWidth>
    +                <TileHeight>256</TileHeight>
    +                <MatrixWidth>2097152</MatrixWidth>
    +                <MatrixHeight>2097152</MatrixHeight>
    +            </TileMatrix>
    +        </TileMatrixSet>
    +    </Contents>
    +    <ServiceMetadataURL xlink:href="https://labs.koordinates.com/services;key=d740ea02e0c44cafb70dce31a774ca10/wmts/1.0.0/layer/7328/WMTSCapabilities.xml"/>
    +</Capabilities>
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/data/arrow.png b/mapbox-gl/examples/data/arrow.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..a0d3834586169c71ca1e2fec8d23a4235e1a950d
    GIT binary patch
    literal 398
    zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3-oPYs<_AQq09po*^6@9Je3(KLBz$3p^r=
    z85p>QL70(Y)*K0-pk#?_L`iUdT1k0gQ7S`udAVL@UUqSEVnM22eo^}DcQ#T$MMVKV
    zA+G;HfB}S7h5p~~@c)eQ|Et>nFY5h2Vhv(n(*1wZ48%QU`u~DHh&*Qq5=Ta-O+Z`-
    z2^UA>!o|-RFV0N33$$IeB*-tAfsu)sg_Vt)mtRm=OhQ`L*=_cMB`epi+q8Ax;p3;z
    zUAlVh=ACcW5zeoH8e%<N978y+r=EA`J7B=U;;{Vtz3Qj`<Il)#Xc5g*X3_DQ8oZz+
    zR9|=DF$b6IBOlx|x+CNG-*TRQyFfYBQnw^cXLsYfIjL7K1i!e)diH<coQb!e$j&@)
    gB5jG;zlrnfwuo>a=h5HS477;B)78&qol`;+0Q0M`nE(I)
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/examples/data/geojson/countries-110m.geojson b/mapbox-gl/examples/data/geojson/countries-110m.geojson
    new file mode 100644
    index 0000000000..fd580e655d
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/countries-110m.geojson
    @@ -0,0 +1,176 @@
    +{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[61.2,35.7],[62.2,35.3],[63,35.4],[63.2,35.9],[64,36],[64.5,36.3],[64.7,37.1],[65.6,37.3],[65.7,37.7],[66.2,37.4],[66.5,37.4],[67.1,37.4],[67.8,37.1],[68.1,37],[68.9,37.3],[69.2,37.2],[69.5,37.6],[70.1,37.6],[70.3,37.7],[70.4,38.1],[70.8,38.5],[71.3,38.3],[71.2,38],[71.5,37.9],[71.4,37.1],[71.8,36.7],[72.2,36.9],[72.6,37],[73.3,37.5],[73.9,37.4],[75,37.4],[75.2,37.1],[74.6,37],[74.1,36.8],[72.9,36.7],[71.8,36.5],[71.3,36.1],[71.5,35.7],[71.6,35.2],[71.1,34.7],[71.2,34.3],[70.9,34],[69.9,34],[70.3,33.4],[69.7,33.1],[69.3,32.5],[69.3,31.9],[68.9,31.6],[68.6,31.7],[67.8,31.6],[67.7,31.3],[66.9,31.3],[66.4,30.7],[66.3,29.9],[65,29.5],[64.4,29.6],[64.1,29.3],[63.6,29.5],[62.5,29.3],[60.9,29.8],[61.8,30.7],[61.7,31.4],[60.9,31.5],[60.9,32.2],[60.5,33],[61,33.5],[60.5,33.7],[60.8,34.4],[61.2,35.7]]]},"id":"374"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[16.3,-5.9],[16.6,-6.6],[16.9,-7.2],[17.1,-7.5],[17.5,-8.1],[18.1,-8],[18.5,-7.8],[19,-8],[19.2,-7.7],[19.4,-7.2],[20,-7.1],[20.1,-6.9],[20.6,-6.9],[20.5,-7.3],[21.7,-7.3],[21.7,-7.9],[21.9,-8.3],[21.8,-8.9],[21.9,-9.5],[22.2,-9.9],[22.2,-11.1],[22.4,-11],[22.8,-11],[23.5,-10.9],[23.9,-10.9],[24,-11.2],[23.9,-11.7],[24.1,-12.2],[23.9,-12.6],[24,-12.9],[21.9,-12.9],[21.9,-16.1],[22.6,-16.9],[23.2,-17.5],[21.4,-17.9],[19,-17.8],[18.3,-17.3],[14.2,-17.4],[14.1,-17.4],[13.5,-17],[12.8,-16.9],[12.2,-17.1],[11.7,-17.3],[11.6,-16.7],[11.8,-15.8],[12.1,-14.9],[12.2,-14.4],[12.5,-13.5],[12.7,-13.1],[13.3,-12.5],[13.6,-12],[13.7,-11.3],[13.7,-10.7],[13.4,-10.4],[13.1,-9.8],[12.9,-9.2],[12.9,-9],[13.2,-8.6],[12.9,-7.6],[12.7,-6.9],[12.2,-6.3],[12.3,-6.1],[12.7,-6],[13,-6],[13.4,-5.9],[16.3,-5.9]]],[[[12.4,-5.7],[12.2,-5.8],[11.9,-5],[12.3,-4.6],[12.6,-4.4],[13,-4.8],[12.6,-5],[12.5,-5.2],[12.4,-5.7]]]]},"id":"191"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.6,41.9],[20.5,41.5],[20.6,41.1],[21,40.8],[21,40.6],[20.7,40.4],[20.6,40.1],[20.2,39.6],[20,39.7],[20,39.9],[19.4,40.3],[19.3,40.7],[19.4,41.4],[19.5,41.7],[19.4,41.9],[19.3,42.2],[19.7,42.7],[19.8,42.5],[20.1,42.6],[20.3,42.3],[20.5,42.2],[20.6,41.9]]]},"id":"188"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[51.6,24.2],[51.8,24.3],[51.8,24],[52.6,24.2],[53.4,24.2],[54,24.1],[54.7,24.8],[55.4,25.4],[56.1,26.1],[56.3,25.7],[56.4,24.9],[55.9,24.9],[55.8,24.3],[56,24.1],[55.5,23.9],[55.5,23.5],[55.2,23.1],[55.2,22.7],[55,22.5],[52,23],[51.6,24],[51.6,24.2]]]},"id":"193"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-65.5,-55.2],[-66.5,-55.3],[-67,-54.9],[-67.6,-54.9],[-68.6,-54.9],[-68.6,-52.6],[-68.3,-53.1],[-67.8,-53.8],[-66.5,-54.5],[-65.1,-54.7],[-65.5,-55.2]]],[[[-65,-22.1],[-64.4,-22.8],[-64,-22],[-62.8,-22],[-62.7,-22.2],[-60.8,-23.9],[-60,-24],[-58.8,-24.8],[-57.8,-25.2],[-57.6,-25.6],[-58.6,-27.1],[-57.6,-27.4],[-56.5,-27.5],[-55.7,-27.4],[-54.8,-26.6],[-54.6,-25.7],[-54.1,-25.5],[-53.6,-26.1],[-53.6,-26.9],[-54.5,-27.5],[-55.2,-27.9],[-56.3,-28.9],[-57.6,-30.2],[-57.9,-31],[-58.1,-32],[-58.1,-33],[-58.3,-33.3],[-58.4,-33.9],[-58.5,-34.4],[-57.2,-35.3],[-57.4,-36],[-56.7,-36.4],[-56.8,-36.9],[-57.7,-38.2],[-59.2,-38.7],[-61.2,-38.9],[-62.3,-38.8],[-62.1,-39.4],[-62.3,-40.2],[-62.1,-40.7],[-62.7,-41],[-63.8,-41.2],[-64.7,-40.8],[-65.1,-41.1],[-65,-42.1],[-64.3,-42.4],[-63.8,-42],[-63.5,-42.6],[-64.4,-42.9],[-65.2,-43.5],[-65.3,-44.5],[-65.6,-45],[-66.5,-45],[-67.3,-45.6],[-67.6,-46.3],[-66.6,-47],[-65.6,-47.2],[-66,-48.1],[-67.2,-48.7],[-67.8,-49.9],[-68.7,-50.3],[-69.1,-50.7],[-68.8,-51.8],[-68.1,-52.3],[-68.6,-52.3],[-69.5,-52.1],[-71.9,-52],[-72.3,-51.4],[-72.3,-50.7],[-73,-50.7],[-73.3,-50.4],[-73.4,-49.3],[-72.6,-48.9],[-72.3,-48.2],[-72.4,-47.7],[-71.9,-46.9],[-71.6,-45.6],[-71.7,-45],[-71.2,-44.8],[-71.3,-44.4],[-71.8,-44.2],[-71.5,-43.8],[-71.9,-43.4],[-72.1,-42.3],[-71.7,-42.1],[-71.9,-40.8],[-71.7,-39.8],[-71.4,-38.9],[-70.8,-38.6],[-71.1,-37.6],[-71.1,-36.7],[-70.4,-36],[-70.4,-35.2],[-69.8,-34.2],[-69.8,-33.3],[-70.1,-33.1],[-70.5,-31.4],[-69.9,-30.3],[-70,-29.4],[-69.7,-28.5],[-69,-27.5],[-68.3,-26.9],[-68.6,-26.5],[-68.4,-26.2],[-68.4,-24.5],[-67.3,-24],[-67,-23],[-67.1,-22.7],[-66.3,-21.8],[-65,-22.1]]]]},"id":"194"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[43.6,41.1],[45,41.2],[45.2,41],[45.6,40.8],[45.4,40.6],[45.9,40.2],[45.6,39.9],[46,39.6],[46.5,39.5],[46.5,38.8],[46.1,38.7],[45.7,39.3],[45.7,39.5],[45.3,39.5],[45,39.7],[44.8,39.7],[44.4,40],[43.7,40.3],[43.8,40.7],[43.6,41.1]]]},"id":"195"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-59.6,-80],[-59.9,-80.5],[-60.2,-81],[-62.3,-80.9],[-64.5,-80.9],[-65.7,-80.6],[-65.7,-80.5],[-66.3,-80.3],[-64,-80.3],[-61.9,-80.4],[-61.1,-80],[-60.6,-79.6],[-59.6,-80]]],[[[-159.2,-79.5],[-161.1,-79.6],[-162.4,-79.3],[-163,-78.9],[-163.1,-78.9],[-163.7,-78.6],[-163.7,-78.6],[-163.1,-78.2],[-161.2,-78.4],[-160.2,-78.7],[-159.5,-79],[-159.2,-79.5]]],[[[-45.2,-78],[-43.9,-78.5],[-43.5,-79.1],[-43.4,-79.5],[-43.3,-80],[-44.9,-80.3],[-46.5,-80.6],[-48.4,-80.8],[-50.5,-81],[-52.9,-81],[-54.2,-80.6],[-54,-80.2],[-51.9,-79.9],[-51,-79.6],[-50.4,-79.2],[-49.9,-78.8],[-49.3,-78.5],[-48.7,-78],[-48.7,-78],[-48.2,-78],[-46.7,-77.8],[-45.2,-78]]],[[[-121.2,-73.5],[-119.9,-73.7],[-118.7,-73.5],[-119.3,-73.8],[-120.2,-74.1],[-121.6,-74],[-122.6,-73.7],[-122.6,-73.7],[-122.4,-73.3],[-121.2,-73.5]]],[[[-125.6,-73.5],[-124,-73.9],[-124.6,-73.8],[-125.9,-73.7],[-127.3,-73.5],[-127.3,-73.5],[-126.6,-73.2],[-125.6,-73.5]]],[[[-99,-71.9],[-97.9,-72.1],[-96.8,-72],[-96.2,-72.5],[-97,-72.4],[-98.2,-72.5],[-99.4,-72.4],[-100.8,-72.5],[-101.8,-72.3],[-102.3,-71.9],[-102.3,-71.9],[-101.7,-71.7],[-100.4,-71.9],[-99,-71.9]]],[[[-68.5,-71],[-68.3,-71.4],[-68.5,-71.8],[-68.8,-72.2],[-70,-72.3],[-71.1,-72.5],[-72.4,-72.5],[-71.9,-72.1],[-73.1,-72.2],[-74.2,-72.4],[-75,-72.1],[-75,-71.7],[-73.9,-71.3],[-73.9,-71.3],[-73.2,-71.2],[-72.1,-71.2],[-71.8,-70.7],[-71.7,-70.3],[-71.7,-69.5],[-71.2,-69],[-70.3,-68.9],[-69.7,-69.3],[-69.5,-69.6],[-69.1,-70.1],[-68.7,-70.5],[-68.5,-71]]],[[[-58.6,-64.2],[-59,-64.4],[-59.8,-64.2],[-60.6,-64.3],[-61.3,-64.5],[-62,-64.8],[-62.5,-65.1],[-62.6,-65.5],[-62.6,-65.9],[-62.1,-66.2],[-62.8,-66.4],[-63.7,-66.5],[-64.3,-66.8],[-64.9,-67.2],[-65.5,-67.6],[-65.7,-68],[-65.3,-68.4],[-64.8,-68.7],[-64,-68.9],[-63.2,-69.2],[-62.8,-69.6],[-62.6,-70],[-62.3,-70.4],[-61.8,-70.7],[-61.5,-71.1],[-61.4,-72],[-61.1,-72.4],[-61,-72.8],[-60.7,-73.2],[-60.8,-73.7],[-61.4,-74.1],[-62,-74.4],[-63.3,-74.6],[-63.7,-74.9],[-64.4,-75.3],[-65.9,-75.6],[-67.2,-75.8],[-68.4,-76],[-69.8,-76.2],[-70.6,-76.6],[-72.2,-76.7],[-74,-76.6],[-75.6,-76.7],[-77.2,-76.7],[-76.9,-77.1],[-75.4,-77.3],[-74.3,-77.6],[-73.7,-77.9],[-74.8,-78.2],[-76.5,-78.1],[-77.9,-78.4],[-78,-78.8],[-78,-79.2],[-76.8,-79.5],[-76.6,-79.9],[-75.4,-80.3],[-73.2,-80.4],[-71.4,-80.7],[-70,-81],[-68.2,-81.3],[-65.7,-81.5],[-63.3,-81.7],[-61.6,-82],[-59.7,-82.4],[-58.7,-82.8],[-58.2,-83.2],[-57,-82.9],[-55.4,-82.6],[-53.6,-82.3],[-51.5,-82],[-49.8,-81.7],[-47.3,-81.7],[-44.8,-81.8],[-42.8,-82.1],[-42.2,-81.7],[-40.8,-81.4],[-38.2,-81.3],[-36.3,-81.1],[-34.4,-80.9],[-32.3,-80.8],[-30.1,-80.6],[-28.5,-80.3],[-29.3,-80],[-29.7,-79.6],[-29.7,-79.3],[-31.6,-79.3],[-33.7,-79.5],[-35.6,-79.5],[-35.9,-79.1],[-35.8,-78.3],[-35.3,-78.1],[-33.9,-77.9],[-32.2,-77.7],[-31,-77.4],[-29.8,-77.1],[-28.9,-76.7],[-27.5,-76.5],[-26.2,-76.4],[-25.5,-76.3],[-23.9,-76.2],[-22.5,-76.1],[-21.2,-75.9],[-20,-75.7],[-18.9,-75.4],[-17.5,-75.1],[-16.6,-74.8],[-15.7,-74.5],[-15.4,-74.1],[-16.5,-73.9],[-16.1,-73.5],[-15.4,-73.1],[-14.4,-73],[-13.3,-72.7],[-12.3,-72.4],[-11.5,-72],[-11,-71.5],[-10.3,-71.3],[-9.1,-71.3],[-8.6,-71.7],[-7.4,-71.7],[-7.4,-71.3],[-6.9,-70.9],[-5.8,-71],[-5.5,-71.4],[-4.3,-71.5],[-3,-71.3],[-1.8,-71.2],[-0.7,-71.2],[-0.2,-71.6],[0.9,-71.3],[1.9,-71.1],[3,-71],[4.1,-70.9],[5.2,-70.6],[6.3,-70.5],[7.1,-70.2],[7.7,-69.9],[8.5,-70.1],[9.5,-70],[10.2,-70.5],[10.8,-70.8],[12,-70.6],[12.4,-70.2],[13.4,-70],[14.7,-70],[15.1,-70.4],[15.9,-70],[17,-69.9],[18.2,-69.9],[19.3,-69.9],[20.4,-70],[21.5,-70.1],[21.9,-70.4],[22.6,-70.7],[23.7,-70.5],[24.8,-70.5],[26,-70.5],[27.1,-70.5],[28.1,-70.3],[29.2,-70.2],[30,-69.9],[31,-69.8],[32,-69.7],[32.8,-69.4],[33.3,-68.8],[33.9,-68.5],[34.9,-68.7],[35.3,-69],[36.2,-69.2],[37.2,-69.2],[37.9,-69.5],[38.6,-69.8],[39.7,-69.5],[40,-69.1],[40.9,-68.9],[42,-68.6],[42.9,-68.5],[44.1,-68.3],[44.9,-68.1],[45.7,-67.8],[46.5,-67.6],[47.4,-67.7],[48.3,-67.4],[49,-67.1],[49.9,-67.1],[50.8,-66.9],[50.9,-66.5],[51.8,-66.2],[52.6,-66.1],[53.6,-65.9],[54.5,-65.8],[55.4,-65.9],[56.4,-66],[57.2,-66.2],[57.3,-66.7],[58.1,-67],[58.7,-67.3],[59.9,-67.4],[60.6,-67.7],[61.4,-68],[62.4,-68],[63.2,-67.8],[64.1,-67.4],[65,-67.6],[66,-67.7],[66.9,-67.9],[67.9,-67.9],[68.9,-67.9],[69.7,-69],[69.7,-69.2],[69.6,-69.7],[68.6,-69.9],[67.8,-70.3],[67.9,-70.7],[69.1,-70.7],[68.9,-71.1],[68.4,-71.4],[67.9,-71.9],[68.7,-72.2],[69.9,-72.3],[71,-72.1],[71.6,-71.7],[71.9,-71.3],[72.5,-71],[73.1,-70.7],[73.3,-70.4],[73.9,-69.9],[74.5,-69.8],[75.6,-69.7],[76.6,-69.6],[77.6,-69.5],[78.1,-69.1],[78.4,-68.7],[79.1,-68.3],[80.1,-68.1],[80.9,-67.9],[81.5,-67.5],[82.1,-67.4],[82.8,-67.2],[83.8,-67.3],[84.7,-67.2],[85.7,-67.1],[86.8,-67.2],[87.5,-66.9],[88,-66.2],[88.4,-66.5],[88.8,-67],[89.7,-67.2],[90.6,-67.2],[91.6,-67.1],[92.6,-67.2],[93.5,-67.2],[94.2,-67.1],[95,-67.2],[95.8,-67.4],[96.7,-67.2],[97.8,-67.2],[98.7,-67.1],[99.7,-67.2],[100.4,-66.9],[100.9,-66.6],[101.6,-66.3],[102.8,-65.6],[103.5,-65.7],[104.2,-66],[104.9,-66.3],[106.2,-66.9],[107.2,-67],[108.1,-67],[109.2,-66.8],[110.2,-66.7],[111.1,-66.4],[111.7,-66.1],[112.9,-66.1],[113.6,-65.9],[114.4,-66.1],[114.9,-66.4],[115.6,-66.7],[116.7,-66.7],[117.4,-66.9],[118.6,-67.2],[119.8,-67.3],[120.9,-67.2],[121.7,-66.9],[122.3,-66.6],[123.2,-66.5],[124.1,-66.6],[125.2,-66.7],[126.1,-66.6],[127,-66.6],[127.9,-66.7],[128.8,-66.8],[129.7,-66.6],[130.8,-66.4],[131.8,-66.4],[132.9,-66.4],[133.9,-66.3],[134.8,-66.2],[135,-65.7],[135.1,-65.3],[135.7,-65.6],[135.9,-66],[136.2,-66.4],[136.6,-66.8],[137.5,-67],[138.6,-66.9],[139.9,-66.9],[140.8,-66.8],[142.1,-66.8],[143.1,-66.8],[144.4,-66.8],[145.5,-66.9],[146.2,-67.2],[146,-67.6],[146.6,-67.9],[147.7,-68.1],[148.8,-68.4],[150.1,-68.6],[151.5,-68.7],[152.5,-68.9],[153.6,-68.9],[154.3,-68.6],[155.2,-68.8],[155.9,-69.1],[156.8,-69.4],[158,-69.5],[159.2,-69.6],[159.7,-70],[160.8,-70.2],[161.6,-70.6],[162.7,-70.7],[163.8,-70.7],[164.9,-70.8],[166.1,-70.8],[167.3,-70.8],[168.4,-71],[169.5,-71.2],[170.5,-71.4],[171.2,-71.7],[171.1,-72.1],[170.6,-72.4],[170.1,-72.9],[169.8,-73.2],[169.3,-73.7],[168,-73.8],[167.4,-74.2],[166.1,-74.4],[165.6,-74.8],[165,-75.1],[164.2,-75.5],[163.8,-75.9],[163.6,-76.2],[163.5,-76.7],[163.5,-77.1],[164.1,-77.5],[164.3,-77.8],[164.7,-78.2],[166.6,-78.3],[167,-78.8],[165.2,-78.9],[163.7,-79.1],[161.8,-79.2],[160.9,-79.7],[160.7,-80.2],[160.3,-80.6],[159.8,-80.9],[161.1,-81.3],[161.6,-81.7],[162.5,-82.1],[163.7,-82.4],[165.1,-82.7],[166.6,-83],[168.9,-83.3],[169.4,-83.8],[172.3,-84],[172.5,-84.1],[173.2,-84.4],[176,-84.2],[178.3,-84.5],[179.9,-84.7],[179.9,-90],[-179.9,-90],[-179.9,-84.7],[-179.9,-84.7],[-179.1,-84.1],[-177.3,-84.5],[-177.1,-84.4],[-176.1,-84.1],[-175.9,-84.1],[-175.8,-84.1],[-174.4,-84.5],[-173.1,-84.1],[-172.9,-84.1],[-170,-83.9],[-169,-84.1],[-168.5,-84.2],[-167,-84.6],[-164.2,-84.8],[-161.9,-85.1],[-158.1,-85.4],[-155.2,-85.1],[-150.9,-85.3],[-148.5,-85.6],[-145.9,-85.3],[-143.1,-85],[-142.9,-84.6],[-146.8,-84.5],[-150.1,-84.3],[-150.9,-83.9],[-153.6,-83.7],[-153.4,-83.2],[-153,-82.8],[-152.7,-82.5],[-152.9,-82],[-154.5,-81.8],[-155.3,-81.4],[-156.8,-81.1],[-154.4,-81.2],[-152.1,-81],[-150.6,-81.3],[-148.9,-81],[-147.2,-80.7],[-146.4,-80.3],[-146.8,-79.9],[-148.1,-79.7],[-149.5,-79.4],[-151.6,-79.3],[-153.4,-79.2],[-155.3,-79.1],[-156,-78.7],[-157.3,-78.4],[-158.1,-78],[-158.4,-76.9],[-157.9,-77],[-157,-77.3],[-155.3,-77.2],[-153.7,-77.1],[-152.9,-77.5],[-151.3,-77.4],[-150,-77.2],[-148.7,-76.9],[-147.6,-76.6],[-146.1,-76.5],[-146.1,-76.1],[-146.5,-75.7],[-146.2,-75.4],[-144.9,-75.2],[-144.3,-75.5],[-142.8,-75.3],[-141.6,-75.1],[-140.2,-75.1],[-138.9,-75],[-137.5,-74.7],[-136.4,-74.5],[-135.2,-74.3],[-134.4,-74.4],[-133.7,-74.4],[-132.3,-74.3],[-130.9,-74.5],[-129.6,-74.5],[-128.2,-74.3],[-126.9,-74.4],[-125.4,-74.5],[-124,-74.5],[-122.6,-74.5],[-121.1,-74.5],[-119.7,-74.5],[-118.7,-74.2],[-117.5,-74],[-116.2,-74.2],[-115,-74.1],[-113.9,-73.7],[-113.3,-74],[-112.9,-74.4],[-112.3,-74.7],[-111.3,-74.4],[-110.1,-74.8],[-108.7,-74.9],[-107.6,-75.2],[-106.1,-75.1],[-104.9,-74.9],[-103.4,-75],[-102,-75.1],[-100.6,-75.3],[-100.1,-74.9],[-100.8,-74.5],[-101.3,-74.2],[-102.5,-74.1],[-103.1,-73.7],[-103.3,-73.4],[-103.7,-72.6],[-102.9,-72.8],[-101.6,-72.8],[-100.3,-72.8],[-99.1,-72.9],[-98.1,-73.2],[-97.7,-73.6],[-96.3,-73.6],[-95,-73.5],[-93.7,-73.3],[-92.4,-73.2],[-91.4,-73.4],[-90.1,-73.3],[-89.2,-72.6],[-88.4,-73],[-87.3,-73.2],[-86,-73.1],[-85.2,-73.5],[-83.9,-73.5],[-82.7,-73.6],[-81.5,-73.9],[-80.7,-73.5],[-80.3,-73.1],[-79.3,-73.5],[-77.9,-73.4],[-76.9,-73.6],[-76.2,-74],[-74.9,-73.9],[-73.9,-73.7],[-72.8,-73.4],[-71.6,-73.3],[-70.2,-73.1],[-68.9,-73],[-68,-72.8],[-67.4,-72.5],[-67.1,-72],[-67.3,-71.6],[-67.6,-71.2],[-67.9,-70.9],[-68.2,-70.5],[-68.5,-70.1],[-68.5,-69.7],[-68.4,-69.3],[-68,-69],[-67.6,-68.5],[-67.4,-68.1],[-67.6,-67.7],[-67.7,-67.3],[-67.3,-66.9],[-66.7,-66.6],[-66.1,-66.2],[-65.4,-65.9],[-64.6,-65.6],[-64.2,-65.2],[-63.6,-64.9],[-63,-64.6],[-62,-64.6],[-61.4,-64.3],[-60.7,-64.1],[-59.9,-64],[-59.2,-63.7],[-58.6,-63.4],[-57.8,-63.3],[-57.2,-63.5],[-57.6,-63.9],[-58.6,-64.2]]]]}},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[68.9,-48.6],[69.6,-48.9],[70.5,-49.1],[70.6,-49.3],[70.3,-49.7],[68.7,-49.8],[68.7,-49.2],[68.9,-48.8],[68.9,-48.6]]]}},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[145.4,-40.8],[146.4,-41.1],[146.9,-41],[147.7,-40.8],[148.3,-40.9],[148.4,-42.1],[148,-42.4],[147.9,-43.2],[147.6,-42.9],[146.9,-43.6],[146.7,-43.6],[146,-43.5],[145.4,-42.7],[145.3,-42],[144.7,-41.2],[144.7,-40.7],[145.4,-40.8]]],[[[143.6,-13.8],[143.9,-14.5],[144.6,-14.2],[144.9,-14.6],[145.4,-15],[145.3,-15.4],[145.5,-16.3],[145.6,-16.8],[145.9,-16.9],[146.2,-17.8],[146.1,-18.3],[146.4,-19],[147.5,-19.5],[148.2,-20],[148.8,-20.4],[148.7,-20.6],[149.3,-21.3],[149.7,-22.3],[150.1,-22.1],[150.5,-22.6],[150.7,-22.4],[150.9,-23.5],[151.6,-24.1],[152.1,-24.5],[152.9,-25.3],[153.1,-26.1],[153.2,-26.6],[153.1,-27.3],[153.6,-28.1],[153.5,-29],[153.3,-29.5],[153.1,-30.4],[153.1,-30.9],[152.9,-31.6],[152.5,-32.6],[151.7,-33],[151.3,-33.8],[151,-34.3],[150.7,-35.2],[150.3,-35.7],[150.1,-36.4],[149.9,-37.1],[150,-37.4],[149.4,-37.8],[148.3,-37.8],[147.4,-38.2],[146.9,-38.6],[146.3,-39],[145.5,-38.6],[144.9,-38.4],[145,-37.9],[144.5,-38.1],[143.6,-38.8],[142.7,-38.5],[142.2,-38.4],[141.6,-38.3],[140.6,-38],[140,-37.4],[139.8,-36.6],[139.6,-36.1],[139.1,-35.7],[138.1,-35.6],[138.4,-35.1],[138.2,-34.4],[137.7,-35.1],[136.8,-35.3],[137.4,-34.7],[137.5,-34.1],[137.9,-33.6],[137.8,-32.9],[137,-33.8],[136.4,-34.1],[136,-34.9],[135.2,-34.5],[135.2,-33.9],[134.6,-33.2],[134.1,-32.8],[134.3,-32.6],[133,-32],[132.3,-32],[131.3,-31.5],[129.5,-31.6],[128.2,-31.9],[127.1,-32.3],[126.1,-32.2],[125.1,-32.7],[124.2,-33],[124,-33.5],[123.7,-33.9],[122.8,-33.9],[122.2,-34],[121.3,-33.8],[120.6,-33.9],[119.9,-34],[119.3,-34.5],[119,-34.5],[118.5,-34.7],[118,-35.1],[117.3,-35],[116.6,-35],[115.6,-34.4],[115,-34.2],[115,-33.6],[115.5,-33.5],[115.7,-33.3],[115.7,-32.9],[115.8,-32.2],[115.7,-31.6],[115.2,-30.6],[115,-30],[115,-29.5],[114.6,-28.8],[114.6,-28.5],[114.2,-28.1],[114,-27.3],[113.5,-26.5],[113.3,-26.1],[113.8,-26.5],[113.4,-25.6],[113.9,-25.9],[114.2,-26.3],[114.2,-25.8],[113.7,-25],[113.6,-24.7],[113.4,-24.4],[113.5,-23.8],[113.7,-23.6],[113.8,-23.1],[113.7,-22.5],[114.1,-21.8],[114.2,-22.5],[114.6,-21.8],[115.5,-21.5],[115.9,-21.1],[116.7,-20.7],[117.2,-20.6],[117.4,-20.7],[118.2,-20.4],[118.8,-20.3],[119,-20],[119.3,-20],[119.8,-20],[120.9,-19.7],[121.4,-19.2],[121.7,-18.7],[122.2,-18.2],[122.3,-17.8],[122.3,-17.3],[123,-16.4],[123.4,-17.3],[123.9,-17.1],[123.5,-16.6],[123.8,-16.1],[124.3,-16.3],[124.4,-15.6],[124.9,-15.1],[125.2,-14.7],[125.7,-14.5],[125.7,-14.2],[126.1,-14.3],[126.1,-14.1],[126.6,-14],[127.1,-13.8],[127.8,-14.3],[128.4,-14.9],[129,-14.9],[129.6,-15],[129.4,-14.4],[129.9,-13.6],[130.3,-13.4],[130.2,-13.1],[130.6,-12.5],[131.2,-12.2],[131.7,-12.3],[132.6,-12.1],[132.6,-11.6],[131.8,-11.3],[132.4,-11.1],[133,-11.4],[133.6,-11.8],[134.4,-12],[134.7,-11.9],[135.3,-12.2],[135.9,-12],[136.3,-12],[136.5,-11.9],[137,-12.4],[136.7,-12.9],[136.3,-13.3],[136,-13.3],[136.1,-13.7],[135.8,-14.2],[135.4,-14.7],[135.5,-15],[136.3,-15.6],[137.1,-15.9],[137.6,-16.2],[138.3,-16.8],[138.6,-16.8],[139.1,-17.1],[139.3,-17.4],[140.2,-17.7],[140.9,-17.4],[141.1,-16.8],[141.3,-16.4],[141.4,-15.8],[141.7,-15],[141.6,-14.6],[141.6,-14.3],[141.5,-13.7],[141.7,-12.9],[141.8,-12.7],[141.7,-12.4],[141.9,-11.9],[142.1,-11.3],[142.1,-11],[142.5,-10.7],[142.8,-11.2],[142.9,-11.8],[143.1,-11.9],[143.2,-12.3],[143.5,-12.8],[143.6,-13.4],[143.6,-13.8]]]]},"id":"196"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[17,48.1],[16.9,47.7],[16.3,47.7],[16.5,47.5],[16.2,46.9],[16,46.7],[15.1,46.7],[14.6,46.4],[13.8,46.5],[12.4,46.8],[12.2,47.1],[11.2,46.9],[11,46.8],[10.4,46.9],[9.9,46.9],[9.5,47.1],[9.6,47.3],[9.6,47.5],[9.9,47.6],[10.4,47.3],[10.5,47.6],[11.4,47.5],[12.1,47.7],[12.6,47.7],[12.9,47.5],[13,47.6],[12.9,48.3],[13.2,48.4],[13.6,48.9],[14.3,48.6],[14.9,49],[15.3,49],[16,48.7],[16.5,48.8],[17,48.6],[16.9,48.5],[17,48.1]]]},"id":"369"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[45,39.7],[45.3,39.5],[45.7,39.5],[45.7,39.3],[46.1,38.7],[45.5,38.9],[45,39.3],[44.8,39.7],[45,39.7]]],[[[47.4,41.2],[47.8,41.2],[48,41.4],[48.6,41.8],[49.1,41.3],[49.6,40.6],[50.1,40.5],[50.4,40.3],[49.6,40.2],[49.4,39.4],[49.2,39],[48.9,38.8],[48.9,38.3],[48.6,38.3],[48,38.8],[48.4,39.3],[48.1,39.6],[47.7,39.5],[46.5,38.8],[46.5,39.5],[46,39.6],[45.6,39.9],[45.9,40.2],[45.4,40.6],[45.6,40.8],[45.2,41],[45,41.2],[45.2,41.4],[46,41.1],[46.5,41.1],[46.6,41.2],[46.1,41.7],[46.4,41.9],[46.7,41.8],[47.4,41.2]]]]},"id":"233"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[29.3,-4.5],[29.3,-3.3],[29,-2.8],[29.6,-2.9],[29.9,-2.3],[30.5,-2.4],[30.5,-2.8],[30.7,-3],[30.8,-3.4],[30.5,-3.6],[30.1,-4.1],[29.8,-4.5],[29.3,-4.5]]]},"id":"212"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[3.3,51.3],[4,51.3],[5,51.5],[5.6,51],[6.2,50.8],[6,50.1],[5.8,50.1],[5.7,49.5],[4.8,50],[4.3,49.9],[3.6,50.4],[3.1,50.8],[2.7,50.8],[2.5,51.1],[3.3,51.3]]]},"id":"201"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[2.7,6.3],[1.9,6.1],[1.6,6.8],[1.7,9.1],[1.5,9.3],[1.4,9.8],[1.1,10.2],[0.8,10.5],[0.9,11],[1.2,11.1],[1.4,11.5],[1.9,11.6],[2.2,11.9],[2.5,12.2],[2.8,12.2],[3.6,11.7],[3.6,11.3],[3.8,10.7],[3.6,10.3],[3.7,10.1],[3.2,9.4],[2.9,9.1],[2.7,8.5],[2.7,7.9],[2.7,6.3]]]},"id":"203"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-2.8,9.6],[-3.5,9.9],[-4,9.9],[-4.3,9.6],[-4.8,9.8],[-5,10.2],[-5.4,10.4],[-5.5,11],[-5.2,11.4],[-5.2,11.7],[-4.4,12.5],[-4.3,13.2],[-4,13.5],[-3.5,13.3],[-3.1,13.5],[-3,13.8],[-2.2,14.2],[-2,14.6],[-1.1,15],[-0.5,15.1],[-0.3,14.9],[0.4,14.9],[0.3,14.4],[0.4,14],[1,13.3],[1,12.9],[2.2,12.6],[2.2,11.9],[1.9,11.6],[1.4,11.5],[1.2,11.1],[0.9,11],[0,11],[-0.4,11.1],[-0.8,10.9],[-1.2,11],[-2.9,11],[-3,10.4],[-2.8,9.6]]]},"id":"211"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[92.7,22],[92.7,21.3],[92.3,21.5],[92.4,20.7],[92.1,21.2],[92,21.7],[91.8,22.2],[91.4,22.8],[90.5,22.8],[90.6,22.4],[90.3,21.8],[89.8,22],[89.7,21.9],[89.4,22],[89,22.1],[88.9,22.9],[88.5,23.6],[88.7,24.2],[88.1,24.5],[88.3,24.9],[88.9,25.2],[88.2,25.8],[88.6,26.4],[89.4,26],[89.8,26],[89.9,25.3],[90.9,25.1],[91.8,25.1],[92.4,25],[91.9,24.1],[91.5,24.1],[91.2,23.5],[91.7,23],[91.9,23.6],[92.1,23.6],[92.7,22]]]},"id":"199"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.7,44.2],[22.9,43.8],[23.3,43.9],[24.1,43.7],[25.6,43.7],[26.1,43.9],[27.2,44.2],[28,43.8],[28.6,43.7],[28,43.3],[27.7,42.6],[28,42],[27.1,42.1],[26.1,41.8],[26.1,41.3],[25.2,41.2],[24.5,41.6],[23.7,41.3],[23,41.3],[22.9,42],[22.4,42.3],[22.5,42.5],[22.4,42.6],[22.6,42.9],[23,43.2],[22.5,43.6],[22.4,44],[22.7,44.2]]]},"id":"210"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-77.5,23.8],[-77.8,23.7],[-78,24.3],[-78.4,24.6],[-78.2,25.2],[-77.9,25.2],[-77.5,24.3],[-77.5,23.8]]],[[[-77.8,26.6],[-78.9,26.4],[-79,26.8],[-78.5,26.9],[-77.9,26.8],[-77.8,26.6]]],[[[-77,26.6],[-77.2,25.9],[-77.4,26],[-77.3,26.5],[-77.8,26.9],[-77.8,27],[-77,26.6]]]]},"id":"197"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[19,44.9],[19.4,44.9],[19.1,44.4],[19.6,44],[19.5,43.6],[19.2,43.5],[19,43.4],[18.7,43.2],[18.6,42.6],[17.7,43],[17.3,43.4],[16.9,43.7],[16.5,44],[16.2,44.4],[15.8,44.8],[16,45.2],[16.3,45],[16.5,45.2],[17,45.2],[17.9,45.1],[18.6,45.1],[19,44.9]]]},"id":"206"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[23.5,53.9],[24.5,53.9],[25.5,54.3],[25.8,54.8],[26.6,55.2],[26.5,55.6],[27.1,55.8],[28.2,56.2],[29.2,55.9],[29.4,55.7],[29.9,55.8],[30.9,55.6],[31,55.1],[30.8,54.8],[31.4,54.2],[31.8,54],[31.7,53.8],[32.4,53.6],[32.7,53.4],[32.3,53.1],[31.5,53.2],[31.3,53.1],[31.5,52.7],[31.8,52.1],[30.9,52],[30.6,51.8],[30.6,51.3],[30.2,51.4],[29.3,51.4],[29,51.6],[28.6,51.4],[28.2,51.6],[27.5,51.6],[26.3,51.8],[25.3,51.9],[24.6,51.9],[24,51.6],[23.5,51.6],[23.5,52],[23.2,52.5],[23.8,52.7],[23.8,53.1],[23.5,53.5],[23.5,53.9]]]},"id":"247"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-89.1,17.8],[-89.2,18],[-89,18],[-88.8,17.9],[-88.5,18.5],[-88.3,18.5],[-88.3,18.4],[-88.1,18.3],[-88.1,18.1],[-88.3,17.6],[-88.2,17.5],[-88.3,17.1],[-88.2,17],[-88.4,16.5],[-88.6,16.3],[-88.7,16.2],[-88.9,15.9],[-89.2,15.9],[-89.2,17],[-89.1,17.8]]]},"id":"202"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-62.8,-22],[-64,-22],[-64.4,-22.8],[-65,-22.1],[-66.3,-21.8],[-67.1,-22.7],[-67.8,-22.9],[-68.2,-21.5],[-68.8,-20.4],[-68.4,-19.4],[-69,-19],[-69.1,-18.3],[-69.6,-17.6],[-69,-16.5],[-69.4,-15.7],[-69.2,-15.3],[-69.3,-15],[-68.9,-14.5],[-68.9,-13.6],[-68.9,-12.9],[-68.7,-12.6],[-69.5,-11],[-68.8,-11],[-68.3,-11],[-68,-10.7],[-67.2,-10.3],[-66.6,-9.9],[-65.3,-9.8],[-65.4,-10.5],[-65.3,-10.9],[-65.4,-11.6],[-64.3,-12.5],[-63.2,-12.6],[-62.8,-13],[-62.1,-13.2],[-61.7,-13.5],[-61.1,-13.5],[-60.5,-13.8],[-60.5,-14.4],[-60.3,-14.6],[-60.3,-15.1],[-60.5,-15.1],[-60.2,-16.3],[-58.2,-16.3],[-58.4,-16.9],[-58.3,-17.3],[-57.7,-17.6],[-57.5,-18.2],[-57.7,-19],[-57.9,-19.4],[-57.9,-20],[-58.2,-20.2],[-58.2,-19.9],[-59.1,-19.4],[-60,-19.3],[-61.8,-19.6],[-62.3,-20.5],[-62.3,-21.1],[-62.7,-22.2],[-62.8,-22]]]},"id":"205"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.6,-30.2],[-56.3,-28.9],[-55.2,-27.9],[-54.5,-27.5],[-53.6,-26.9],[-53.6,-26.1],[-54.1,-25.5],[-54.6,-25.7],[-54.4,-25.2],[-54.3,-24.6],[-54.3,-24],[-54.7,-23.8],[-55,-24],[-55.4,-24],[-55.5,-23.6],[-55.6,-22.7],[-55.8,-22.4],[-56.5,-22.1],[-56.9,-22.3],[-57.9,-22.1],[-57.9,-20.7],[-58.2,-20.2],[-57.9,-20],[-57.9,-19.4],[-57.7,-19],[-57.5,-18.2],[-57.7,-17.6],[-58.3,-17.3],[-58.4,-16.9],[-58.2,-16.3],[-60.2,-16.3],[-60.5,-15.1],[-60.3,-15.1],[-60.3,-14.6],[-60.5,-14.4],[-60.5,-13.8],[-61.1,-13.5],[-61.7,-13.5],[-62.1,-13.2],[-62.8,-13],[-63.2,-12.6],[-64.3,-12.5],[-65.4,-11.6],[-65.3,-10.9],[-65.4,-10.5],[-65.3,-9.8],[-66.6,-9.9],[-67.2,-10.3],[-68,-10.7],[-68.3,-11],[-68.8,-11],[-69.5,-11],[-70.1,-11.1],[-70.5,-11],[-70.5,-9.5],[-71.3,-10.1],[-72.2,-10.1],[-72.6,-9.5],[-73.2,-9.5],[-73,-9],[-73.6,-8.4],[-74,-7.5],[-73.7,-7.3],[-73.7,-6.9],[-73.1,-6.6],[-73.2,-6.1],[-73,-5.7],[-72.9,-5.3],[-71.7,-4.6],[-70.9,-4.4],[-70.8,-4.3],[-69.9,-4.3],[-69.4,-1.6],[-69.4,-1.1],[-69.6,-0.5],[-70,-0.2],[-70,0.5],[-69.5,0.7],[-69.3,0.6],[-69.2,1],[-69.8,1.1],[-69.8,1.7],[-67.9,1.7],[-67.5,2],[-67.3,1.7],[-67.1,1.1],[-66.9,1.3],[-66.3,0.7],[-65.5,0.8],[-65.4,1.1],[-64.6,1.3],[-64.2,1.5],[-64.1,1.9],[-63.4,2.2],[-63.4,2.4],[-64.3,2.5],[-64.4,3.1],[-64.4,3.8],[-64.8,4.1],[-64.6,4.1],[-63.9,4],[-63.1,3.8],[-62.8,4],[-62.1,4.2],[-61,4.5],[-60.6,4.9],[-60.7,5.2],[-60.2,5.2],[-60,5],[-60.1,4.6],[-59.8,4.4],[-59.5,4],[-59.8,3.6],[-60,2.8],[-59.7,2.2],[-59.6,1.8],[-59,1.3],[-58.5,1.3],[-58.4,1.5],[-58.1,1.5],[-57.7,1.7],[-57.3,1.9],[-56.8,1.9],[-56.5,1.9],[-56,1.8],[-55.9,2],[-56.1,2.2],[-56,2.5],[-55.6,2.4],[-55.1,2.5],[-54.5,2.3],[-54.1,2.1],[-53.8,2.4],[-53.6,2.3],[-53.4,2.1],[-52.9,2.1],[-52.6,2.5],[-52.2,3.2],[-51.7,4.2],[-51.3,4.2],[-51.1,3.7],[-50.5,1.9],[-50,1.7],[-49.9,1],[-50.7,0.2],[-50.4,-0.1],[-48.6,-0.2],[-48.6,-1.2],[-47.8,-0.6],[-46.6,-0.9],[-44.9,-1.6],[-44.4,-2.1],[-44.6,-2.7],[-43.4,-2.4],[-41.5,-2.9],[-40,-2.9],[-38.5,-3.7],[-37.2,-4.8],[-36.5,-5.1],[-35.6,-5.1],[-35.2,-5.5],[-34.9,-6.7],[-34.7,-7.3],[-35.1,-9],[-35.6,-9.6],[-37,-11],[-37.7,-12.2],[-38.4,-13],[-38.7,-13.1],[-39,-13.8],[-38.9,-15.7],[-39.2,-17.2],[-39.3,-17.9],[-39.6,-18.3],[-39.8,-19.6],[-40.8,-20.9],[-40.9,-21.9],[-41.8,-22.4],[-42,-23],[-43.1,-23],[-44.6,-23.4],[-45.4,-23.8],[-46.5,-24.1],[-47.6,-24.9],[-48.5,-25.9],[-48.6,-26.6],[-48.5,-27.2],[-48.7,-28.2],[-48.9,-28.7],[-49.6,-29.2],[-50.7,-31],[-51.6,-31.8],[-52.3,-32.2],[-52.7,-33.2],[-53.4,-33.8],[-53.7,-33.2],[-53.2,-32.7],[-53.8,-32],[-54.6,-31.5],[-55.6,-30.9],[-56,-30.9],[-57,-30.1],[-57.6,-30.2]]]},"id":"208"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[114.2,4.5],[114.6,4.9],[115.5,5.4],[115.4,5],[115.3,4.3],[114.9,4.3],[114.7,4],[114.2,4.5]]]},"id":"209"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[91.7,27.8],[92.1,27.5],[92,26.8],[91.2,26.8],[90.4,26.9],[89.7,26.7],[88.8,27.1],[88.8,27.3],[89.5,28],[90,28.3],[90.7,28.1],[91.3,28],[91.7,27.8]]]},"id":"204"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[25.6,-18.5],[25.9,-18.7],[26.2,-19.3],[27.3,-20.4],[27.7,-20.5],[27.7,-20.9],[28,-21.5],[28.8,-21.6],[29.4,-22.1],[28,-22.8],[27.1,-23.6],[26.8,-24.2],[26.5,-24.6],[25.9,-24.7],[25.8,-25.2],[25.7,-25.5],[25,-25.7],[24.2,-25.7],[23.7,-25.4],[23.3,-25.3],[22.8,-25.5],[22.6,-26],[22.1,-26.3],[21.6,-26.7],[20.9,-26.8],[20.7,-26.5],[20.8,-25.9],[20.2,-24.9],[19.9,-24.8],[19.9,-21.8],[20.9,-21.8],[20.9,-18.3],[21.7,-18.2],[23.2,-17.9],[23.6,-18.3],[24.2,-17.9],[24.5,-17.9],[25.1,-17.7],[25.3,-17.7],[25.6,-18.5]]]},"id":"207"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[15.3,7.4],[16.1,7.5],[16.3,7.8],[16.5,7.7],[16.7,7.5],[18,7.9],[18.4,8.3],[18.9,8.6],[18.8,9],[19.1,9.1],[20.1,9],[21,9.5],[21.7,10.6],[22.2,11],[22.9,11.1],[23,10.7],[23.6,10.1],[23.6,9.7],[23.4,9.3],[23.5,9],[23.8,8.7],[24.6,8.2],[25.1,7.8],[25.1,7.5],[25.8,7],[26.2,6.5],[26.5,5.9],[27.2,5.6],[27.4,5.2],[27,5.1],[26.4,5.2],[25.7,5.3],[25.3,5.2],[25.1,4.9],[24.8,4.9],[24.4,5.1],[23.3,4.6],[22.8,4.7],[22.7,4.6],[22.4,4],[21.7,4.2],[20.9,4.3],[20.3,4.7],[19.5,5],[18.9,4.7],[18.5,4.2],[18.5,3.5],[17.8,3.6],[17.1,3.7],[16.5,3.2],[16,2.3],[15.9,2.6],[15.9,3],[15.4,3.3],[15,3.9],[15,4.2],[14.5,4.7],[14.6,5],[14.5,5.5],[14.5,6.2],[14.8,6.4],[15.3,7.4]]]},"id":"329"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-63.7,46.6],[-62.9,46.4],[-62,46.4],[-62.5,46],[-62.9,46],[-64.1,46.4],[-64.4,46.7],[-64,47],[-63.7,46.6]]],[[[-61.8,49.1],[-62.3,49.1],[-63.6,49.4],[-64.5,49.9],[-64.2,50],[-62.9,49.7],[-61.8,49.3],[-61.8,49.1]]],[[[-123.5,48.5],[-124,48.4],[-125.7,48.8],[-126,49.2],[-126.9,49.5],[-127,49.8],[-128.1,50],[-128.4,50.5],[-128.4,50.8],[-127.3,50.6],[-126.7,50.4],[-125.8,50.3],[-125.4,50],[-124.9,49.5],[-123.9,49.1],[-123.5,48.5]]],[[[-56.1,50.7],[-56.8,49.8],[-56.1,50.2],[-55.5,49.9],[-55.8,49.6],[-54.9,49.3],[-54.5,49.6],[-53.5,49.2],[-53.8,48.5],[-53.1,48.7],[-53,48.2],[-52.6,47.5],[-53.1,46.7],[-53.5,46.6],[-54.2,46.8],[-54,47.6],[-54.2,47.8],[-55.4,46.9],[-56,46.9],[-55.3,47.4],[-56.3,47.6],[-57.3,47.6],[-59.3,47.6],[-59.4,47.9],[-58.8,48.3],[-59.2,48.5],[-58.4,49.1],[-57.4,50.7],[-56.7,51.3],[-55.9,51.6],[-55.4,51.6],[-55.6,51.3],[-56.1,50.7]]],[[[-133.2,54.2],[-132.7,54],[-131.7,54.1],[-132,53],[-131.2,52.2],[-131.6,52.2],[-132.2,52.6],[-132.5,53.1],[-133.1,53.4],[-133.2,53.9],[-133.2,54.2]]],[[[-79.3,62.2],[-79.7,61.6],[-80.1,61.7],[-80.4,62],[-80.3,62.1],[-79.9,62.4],[-79.5,62.4],[-79.3,62.2]]],[[[-81.9,62.7],[-83.1,62.2],[-83.8,62.2],[-84,62.5],[-83.3,62.9],[-81.9,62.9],[-81.9,62.7]]],[[[-85.2,65.7],[-85,65.2],[-84.5,65.4],[-83.9,65.1],[-82.8,64.8],[-81.6,64.5],[-81.6,64],[-80.8,64.1],[-80.1,63.7],[-81,63.4],[-82.5,63.7],[-83.1,64.1],[-84.1,63.6],[-85.5,63.1],[-85.9,63.6],[-87.2,63.5],[-86.4,64],[-86.2,64.8],[-85.9,65.7],[-85.2,65.7]]],[[[-75.9,67.1],[-77,67.1],[-77.2,67.6],[-76.8,68.1],[-75.9,68.3],[-75.1,68],[-75.1,67.6],[-75.2,67.4],[-75.9,67.1]]],[[[-95.6,69.1],[-96.3,68.8],[-97.6,69.1],[-98.4,69],[-99.8,69.4],[-98.9,69.7],[-98.2,70.1],[-97.2,69.9],[-96.6,69.7],[-96.3,69.5],[-95.6,69.1]]],[[[-90.5,69.5],[-90.6,68.5],[-89.2,69.3],[-88,68.6],[-88.3,67.9],[-87.4,67.2],[-86.3,67.9],[-85.6,68.8],[-85.5,69.9],[-84.1,69.8],[-82.6,69.7],[-81.3,69.2],[-81.2,68.7],[-82,68.1],[-81.3,67.6],[-81.4,67.1],[-83.3,66.4],[-84.7,66.3],[-85.8,66.6],[-86.1,66.1],[-87,65.2],[-87.3,64.8],[-88.5,64.1],[-89.9,64],[-90.7,63.6],[-90.8,63],[-91.9,62.8],[-93.2,62],[-94.2,60.9],[-94.6,60.1],[-94.7,58.9],[-93.2,58.8],[-92.8,57.8],[-92.3,57.1],[-90.9,57.3],[-89,56.9],[-88,56.5],[-87.3,56],[-86.1,55.7],[-85,55.3],[-83.4,55.2],[-82.3,55.1],[-82.4,54.3],[-82.1,53.3],[-81.4,52.2],[-79.9,51.2],[-79.1,51.5],[-78.6,52.6],[-79.1,54.1],[-79.8,54.7],[-78.2,55.1],[-77.1,55.8],[-76.5,56.5],[-76.6,57.2],[-77.3,58.1],[-78.5,58.8],[-77.3,59.9],[-77.8,60.8],[-78.1,62.3],[-77.4,62.6],[-75.7,62.3],[-74.7,62.2],[-73.8,62.4],[-72.9,62.1],[-71.7,61.5],[-71.4,61.1],[-69.6,61.1],[-69.6,60.2],[-69.3,59],[-68.4,58.8],[-67.6,58.2],[-66.2,58.8],[-65.2,59.9],[-64.6,60.3],[-63.8,59.4],[-62.5,58.2],[-61.4,57],[-61.8,56.3],[-60.5,55.8],[-59.6,55.2],[-58,54.9],[-57.3,54.6],[-56.9,53.8],[-56.2,53.6],[-55.8,53.3],[-55.7,52.1],[-56.4,51.8],[-57.1,51.4],[-58.8,51.1],[-60,50.2],[-61.7,50.1],[-63.9,50.3],[-65.4,50.3],[-66.4,50.2],[-67.2,49.5],[-68.5,49.1],[-70,47.7],[-71.1,46.8],[-70.3,47],[-68.6,48.3],[-66.6,49.1],[-65.1,49.2],[-64.2,48.7],[-65.1,48.1],[-64.8,47],[-64.5,46.2],[-63.2,45.7],[-61.5,45.9],[-60.5,47],[-60.4,46.3],[-59.8,45.9],[-61,45.3],[-63.3,44.7],[-64.2,44.3],[-65.4,43.5],[-66.1,43.6],[-66.2,44.5],[-64.4,45.3],[-66,45.3],[-67.1,45.1],[-67.8,45.7],[-67.8,47.1],[-68.2,47.4],[-68.9,47.2],[-69.2,47.4],[-70,46.7],[-70.3,45.9],[-70.7,45.5],[-71.1,45.3],[-71.4,45.3],[-71.5,45],[-73.3,45],[-74.9,45],[-75.3,44.8],[-76.4,44.1],[-76.5,44],[-76.8,43.6],[-77.7,43.6],[-78.7,43.6],[-79.2,43.5],[-79,43.3],[-78.9,43],[-78.9,42.9],[-80.2,42.4],[-81.3,42.2],[-82.4,41.7],[-82.7,41.7],[-83,41.8],[-83.1,42],[-83.1,42.1],[-82.9,42.4],[-82.4,43],[-82.1,43.6],[-82.3,44.4],[-82.6,45.3],[-83.6,45.8],[-83.5,46],[-83.6,46.1],[-83.9,46.1],[-84.1,46.3],[-84.1,46.5],[-84.3,46.4],[-84.6,46.4],[-84.5,46.5],[-84.8,46.6],[-84.9,46.9],[-85.7,47.2],[-86.5,47.6],[-87.4,47.9],[-88.4,48.3],[-89.3,48],[-89.6,48],[-90.8,48.3],[-91.6,48.1],[-92.6,48.4],[-93.6,48.6],[-94.3,48.7],[-94.6,48.8],[-94.8,49.4],[-95.2,49.4],[-95.2,49],[-97.2,49],[-100.6,49],[-104,49],[-107.1,49],[-110.1,49],[-113,49],[-116,49],[-117,49],[-120,49],[-122.8,49],[-123,49],[-124.9,50],[-125.6,50.4],[-127.4,50.8],[-128,51.7],[-127.9,52.3],[-129.1,52.8],[-129.3,53.6],[-130.5,54.3],[-130.5,54.8],[-130,55.3],[-130,55.9],[-131.7,56.6],[-132.7,57.7],[-133.4,58.4],[-134.3,58.9],[-134.9,59.3],[-135.5,59.8],[-136.5,59.5],[-137.5,58.9],[-138.3,59.6],[-139,60],[-140,60.3],[-141,60.3],[-141,66],[-141,69.7],[-139.1,69.5],[-137.5,69],[-136.5,68.9],[-135.6,69.3],[-134.4,69.6],[-132.9,69.5],[-131.4,69.9],[-129.8,70.2],[-129.1,69.8],[-128.4,70],[-128.1,70.5],[-127.4,70.4],[-125.8,69.5],[-124.4,70.2],[-124.3,69.4],[-123.1,69.6],[-122.7,69.9],[-121.5,69.8],[-119.9,69.4],[-117.6,69],[-116.2,68.8],[-115.2,68.9],[-113.9,68.4],[-115.3,67.9],[-113.5,67.7],[-110.8,67.8],[-109.9,68],[-108.9,67.4],[-107.8,67.9],[-108.8,68.3],[-108.2,68.7],[-106.9,68.7],[-106.1,68.8],[-105.3,68.6],[-104.3,68],[-103.2,68.1],[-101.5,67.6],[-99.9,67.8],[-98.4,67.8],[-98.6,68.4],[-97.7,68.6],[-96.1,68.2],[-96.1,67.3],[-95.5,68.1],[-94.7,68.1],[-94.2,69.1],[-95.3,69.7],[-96.5,70.1],[-96.4,71.2],[-95.2,71.9],[-93.9,71.8],[-92.9,71.3],[-91.5,70.2],[-92.4,69.7],[-90.5,69.5]]],[[[-114.2,73.1],[-114.7,72.7],[-112.4,73],[-111.1,72.5],[-109.9,73],[-109,72.6],[-108.2,71.7],[-107.7,72.1],[-108.4,73.1],[-107.5,73.2],[-106.5,73.1],[-105.4,72.7],[-104.8,71.7],[-104.5,71],[-102.8,70.5],[-101,70],[-101.1,69.6],[-102.7,69.5],[-102.1,69.1],[-102.4,68.8],[-104.2,68.9],[-106,69.2],[-107.1,69.1],[-109,68.8],[-111.5,68.6],[-113.3,68.5],[-113.9,69],[-115.2,69.3],[-116.1,69.2],[-117.3,70],[-116.7,70.1],[-115.1,70.2],[-113.7,70.2],[-112.4,70.4],[-114.4,70.6],[-116.5,70.5],[-117.9,70.5],[-118.4,70.9],[-116.1,71.3],[-117.7,71.3],[-119.4,71.6],[-118.6,72.3],[-117.9,72.7],[-115.2,73.3],[-114.2,73.1]]],[[[-104.5,73.4],[-105.4,72.8],[-106.9,73.5],[-106.6,73.6],[-105.3,73.6],[-104.5,73.4]]],[[[-76.3,73.1],[-76.3,72.8],[-77.3,72.9],[-78.4,72.9],[-79.5,72.7],[-79.8,72.8],[-80.9,73.3],[-80.8,73.7],[-80.4,73.8],[-78.1,73.7],[-76.3,73.1]]],[[[-86.6,73.2],[-85.8,72.5],[-84.9,73.3],[-82.3,73.8],[-80.6,72.7],[-80.7,72.1],[-78.8,72.4],[-77.8,72.7],[-75.6,72.2],[-74.2,71.8],[-74.1,71.3],[-72.2,71.6],[-71.2,70.9],[-68.8,70.5],[-67.9,70.1],[-67,69.2],[-68.8,68.7],[-66.4,68.1],[-64.9,67.8],[-63.4,66.9],[-61.9,66.9],[-62.2,66.2],[-63.9,65],[-65.1,65.4],[-66.7,66.4],[-68,66.3],[-68.1,65.7],[-67.1,65.1],[-65.7,64.6],[-65.3,64.4],[-64.7,63.4],[-65,62.7],[-66.3,62.9],[-68.8,63.7],[-67.4,62.9],[-66.3,62.3],[-66.2,61.9],[-68.9,62.3],[-71,62.9],[-72.2,63.4],[-71.9,63.7],[-73.4,64.2],[-74.8,64.7],[-74.8,64.4],[-77.7,64.2],[-78.6,64.6],[-77.9,65.3],[-76,65.3],[-74,65.5],[-74.3,65.8],[-73.9,66.3],[-72.7,67.3],[-72.9,67.7],[-73.3,68.1],[-74.8,68.6],[-76.9,68.9],[-76.2,69.1],[-77.3,69.8],[-78.2,69.8],[-79,70.2],[-79.5,69.9],[-81.3,69.7],[-84.9,70],[-87.1,70.3],[-88.7,70.4],[-89.5,70.8],[-88.5,71.2],[-89.9,71.2],[-90.2,72.2],[-89.4,73.1],[-88.4,73.5],[-85.8,73.8],[-86.6,73.2]]],[[[-100.4,73.8],[-99.2,73.6],[-97.4,73.8],[-97.1,73.5],[-98.1,73],[-96.5,72.6],[-96.7,71.7],[-98.4,71.3],[-99.3,71.4],[-100,71.7],[-102.5,72.5],[-102.5,72.8],[-100.4,72.7],[-101.5,73.4],[-100.4,73.8]]],[[[-93.2,72.8],[-94.3,72],[-95.4,72.1],[-96,72.9],[-96,73.4],[-95.5,73.9],[-94.5,74.1],[-92.4,74.1],[-90.5,73.9],[-92,73],[-93.2,72.8]]],[[[-120.5,71.4],[-123.1,70.9],[-123.6,71.3],[-125.9,71.9],[-125.5,72.3],[-124.8,73],[-123.9,73.7],[-124.9,74.3],[-121.5,74.4],[-120.1,74.2],[-117.6,74.2],[-116.6,73.9],[-115.5,73.5],[-116.8,73.2],[-119.2,72.5],[-120.5,71.8],[-120.5,71.4]]],[[[-93.6,75],[-94.2,74.6],[-95.6,74.7],[-96.8,74.9],[-96.3,75.4],[-94.9,75.6],[-94,75.3],[-93.6,75]]],[[[-98.5,76.7],[-97.7,76.3],[-97.7,75.7],[-98.2,75],[-99.8,74.9],[-100.9,75.1],[-100.9,75.6],[-102.5,75.6],[-102.6,76.3],[-101.5,76.3],[-100,76.6],[-98.6,76.6],[-98.5,76.7]]],[[[-108.2,76.2],[-107.8,75.8],[-106.9,76],[-105.9,76],[-105.7,75.5],[-106.3,75],[-109.7,74.8],[-112.2,74.4],[-113.7,74.4],[-113.9,74.7],[-111.8,75.2],[-116.3,75],[-117.7,75.2],[-116.3,76.2],[-115.4,76.5],[-112.6,76.1],[-110.8,75.5],[-109.1,75.5],[-110.5,76.4],[-109.6,76.8],[-108.5,76.7],[-108.2,76.2]]],[[[-94.7,77.1],[-93.6,76.8],[-91.6,76.8],[-90.7,76.4],[-91,76.1],[-89.8,75.8],[-89.2,75.6],[-87.8,75.6],[-86.4,75.5],[-84.8,75.7],[-82.8,75.8],[-81.1,75.7],[-80.1,75.3],[-79.8,74.9],[-80.5,74.7],[-81.9,74.4],[-83.2,74.6],[-86.1,74.4],[-88.2,74.4],[-89.8,74.5],[-92.4,74.8],[-92.8,75.4],[-92.9,75.9],[-93.9,76.3],[-96,76.4],[-97.1,76.8],[-96.7,77.2],[-94.7,77.1]]],[[[-116.2,77.6],[-116.3,76.9],[-117.1,76.5],[-118,76.5],[-119.9,76.1],[-121.5,75.9],[-122.9,76.1],[-122.9,76.1],[-121.2,76.9],[-119.1,77.5],[-117.6,77.5],[-116.2,77.6]]],[[[-93.8,77.5],[-94.3,77.5],[-96.2,77.6],[-96.4,77.8],[-94.4,77.8],[-93.7,77.6],[-93.8,77.5]]],[[[-110.2,77.7],[-112.1,77.4],[-113.5,77.7],[-112.7,78.1],[-111.3,78.2],[-109.9,78],[-110.2,77.7]]],[[[-109.7,78.6],[-110.9,78.4],[-112.5,78.4],[-112.5,78.6],[-111.5,78.8],[-111,78.8],[-109.7,78.6]]],[[[-95.8,78.1],[-97.3,77.9],[-98.1,78.1],[-98.6,78.5],[-98.6,78.9],[-97.3,78.8],[-96.8,78.8],[-95.6,78.4],[-95.8,78.1]]],[[[-100.1,78.3],[-99.7,77.9],[-101.3,78],[-102.9,78.3],[-105.2,78.4],[-104.2,78.7],[-105.4,78.9],[-105.5,79.3],[-103.5,79.2],[-100.8,78.8],[-100.1,78.3]]],[[[-87,79.7],[-85.8,79.3],[-87.2,79],[-89,78.3],[-90.8,78.2],[-92.9,78.3],[-94,78.8],[-93.9,79.1],[-93.1,79.4],[-95,79.4],[-96.1,79.7],[-96.7,80.2],[-96,80.6],[-95.3,80.9],[-94.3,81],[-94.7,81.2],[-92.4,81.3],[-91.1,80.7],[-89.4,80.5],[-87.8,80.3],[-87,79.7]]],[[[-68.5,83.1],[-65.8,83],[-63.7,82.9],[-61.8,82.6],[-61.9,82.4],[-64.3,81.9],[-66.8,81.7],[-67.7,81.5],[-65.5,81.5],[-67.8,80.9],[-69.5,80.6],[-71.2,79.8],[-73.2,79.6],[-73.9,79.4],[-76.9,79.3],[-75.5,79.2],[-76.2,79],[-75.4,78.5],[-76.3,78.2],[-77.9,77.9],[-78.4,77.5],[-79.8,77.2],[-79.6,77],[-77.9,77],[-77.9,76.8],[-80.6,76.2],[-83.2,76.5],[-86.1,76.3],[-87.6,76.4],[-89.5,76.5],[-89.6,77],[-87.8,77.2],[-88.3,77.9],[-87.7,78],[-85,77.5],[-86.3,78.2],[-88,78.4],[-87.2,78.8],[-85.4,79],[-85.1,79.3],[-86.5,79.7],[-86.9,80.3],[-84.2,80.2],[-83.4,80.1],[-81.8,80.5],[-84.1,80.6],[-87.6,80.5],[-89.4,80.9],[-90.2,81.3],[-91.4,81.6],[-91.6,81.9],[-90.1,82.1],[-88.9,82.1],[-87,82.3],[-85.5,82.7],[-84.3,82.6],[-83.2,82.3],[-82.4,82.9],[-81.1,83],[-79.3,83.1],[-76.3,83.2],[-75.7,83.1],[-72.8,83.2],[-70.7,83.2],[-68.5,83.1]]]]},"id":"213"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.6,47.5],[9.6,47.3],[9.5,47.1],[9.9,46.9],[10.4,46.9],[10.4,46.5],[9.9,46.3],[9.2,46.4],[9,46],[8.5,46],[8.3,46.2],[7.8,45.8],[7.3,45.8],[6.8,46],[6.5,46.4],[6,46.3],[6,46.7],[6.8,47.3],[6.7,47.5],[7.2,47.4],[7.5,47.6],[8.3,47.6],[8.5,47.8],[9.6,47.5]]]},"id":"340"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-68.6,-52.6],[-68.6,-54.9],[-67.6,-54.9],[-67,-54.9],[-67.3,-55.3],[-68.1,-55.6],[-68.6,-55.6],[-69.2,-55.5],[-70,-55.2],[-71,-55.1],[-72.3,-54.5],[-73.3,-54],[-74.7,-52.8],[-73.8,-53],[-72.4,-53.7],[-71.1,-54.1],[-70.6,-53.6],[-70.3,-52.9],[-69.3,-52.5],[-68.6,-52.6]]],[[[-68.2,-21.5],[-67.8,-22.9],[-67.1,-22.7],[-67,-23],[-67.3,-24],[-68.4,-24.5],[-68.4,-26.2],[-68.6,-26.5],[-68.3,-26.9],[-69,-27.5],[-69.7,-28.5],[-70,-29.4],[-69.9,-30.3],[-70.5,-31.4],[-70.1,-33.1],[-69.8,-33.3],[-69.8,-34.2],[-70.4,-35.2],[-70.4,-36],[-71.1,-36.7],[-71.1,-37.6],[-70.8,-38.6],[-71.4,-38.9],[-71.7,-39.8],[-71.9,-40.8],[-71.7,-42.1],[-72.1,-42.3],[-71.9,-43.4],[-71.5,-43.8],[-71.8,-44.2],[-71.3,-44.4],[-71.2,-44.8],[-71.7,-45],[-71.6,-45.6],[-71.9,-46.9],[-72.4,-47.7],[-72.3,-48.2],[-72.6,-48.9],[-73.4,-49.3],[-73.3,-50.4],[-73,-50.7],[-72.3,-50.7],[-72.3,-51.4],[-71.9,-52],[-69.5,-52.1],[-68.6,-52.3],[-69.5,-52.3],[-69.9,-52.5],[-70.8,-52.9],[-71,-53.8],[-71.4,-53.9],[-72.6,-53.5],[-73.7,-52.8],[-73.7,-52.8],[-74.9,-52.3],[-75.3,-51.6],[-75,-51],[-75.5,-50.4],[-75.6,-48.7],[-75.2,-47.7],[-74.1,-46.9],[-75.6,-46.6],[-74.7,-45.8],[-74.4,-44.1],[-73.2,-44.5],[-72.7,-42.4],[-73.4,-42.1],[-73.7,-43.4],[-74.3,-43.2],[-74,-41.8],[-73.7,-39.9],[-73.2,-39.3],[-73.5,-38.3],[-73.6,-37.2],[-73.2,-37.1],[-72.6,-35.5],[-71.9,-33.9],[-71.4,-32.4],[-71.7,-30.9],[-71.4,-30.1],[-71.5,-28.9],[-70.9,-27.6],[-70.7,-25.7],[-70.4,-23.6],[-70.1,-21.4],[-70.2,-19.8],[-70.4,-18.3],[-69.9,-18.1],[-69.6,-17.6],[-69.1,-18.3],[-69,-19],[-68.4,-19.4],[-68.8,-20.4],[-68.2,-21.5]]]]},"id":"214"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[110.3,18.7],[109.5,18.2],[108.7,18.5],[108.6,19.4],[109.1,19.8],[110.2,20.1],[110.8,20.1],[111,19.7],[110.6,19.3],[110.3,18.7]]],[[[127.7,49.8],[129.4,49.4],[130.6,48.7],[131,47.8],[132.5,47.8],[133.4,48.2],[135,48.5],[134.5,47.6],[134.1,47.2],[133.8,46.1],[133.1,45.1],[131.9,45.3],[131,45],[131.3,44.1],[131.1,42.9],[130.6,42.9],[130.6,42.4],[130,43],[129.6,42.4],[128.1,42],[128.2,41.5],[127.3,41.5],[126.9,41.8],[126.2,41.1],[125.1,40.6],[124.3,39.9],[122.9,39.6],[122.1,39.2],[121.1,38.9],[121.6,39.4],[121.4,39.8],[122.2,40.4],[121.6,40.9],[120.8,40.6],[119.6,39.9],[119,39.3],[118,39.2],[117.5,38.7],[118.1,38.1],[118.9,37.9],[118.9,37.4],[119.7,37.2],[120.8,37.9],[121.7,37.5],[122.4,37.5],[122.5,36.9],[121.1,36.7],[120.6,36.1],[119.7,35.6],[119.2,34.9],[120.2,34.4],[120.6,33.4],[121.2,32.5],[121.9,31.7],[121.9,30.9],[121.3,30.7],[121.5,30.1],[122.1,29.8],[121.9,29],[121.7,28.2],[121.1,28.1],[120.4,27.1],[119.6,25.7],[118.7,24.5],[117.3,23.6],[115.9,22.8],[114.8,22.7],[114.2,22.2],[113.8,22.5],[113.2,22.1],[111.8,21.6],[110.8,21.4],[110.4,20.3],[109.9,20.3],[109.6,21],[109.9,21.4],[108.5,21.7],[108.1,21.6],[107,21.8],[106.6,22.2],[106.7,22.8],[105.8,23],[105.3,23.4],[104.5,22.8],[103.5,22.7],[102.7,22.7],[102.2,22.5],[101.7,22.3],[101.8,21.2],[101.3,21.2],[101.2,21.4],[101.2,21.8],[100.4,21.6],[100,21.7],[99.2,22.1],[99.5,22.9],[98.9,23.1],[98.7,24.1],[97.6,23.9],[97.7,25.1],[98.7,25.9],[98.7,26.7],[98.7,27.5],[98.2,27.7],[97.9,28.3],[97.3,28.3],[96.2,28.4],[96.6,28.8],[96.1,29.5],[95.4,29],[94.6,29.3],[93.4,28.6],[92.5,27.9],[91.7,27.8],[91.3,28],[90.7,28.1],[90,28.3],[89.5,28],[88.8,27.3],[88.7,28.1],[88.1,27.9],[87,28],[85.8,28.2],[85,28.6],[84.2,28.8],[83.9,29.3],[83.3,29.5],[82.3,30.1],[81.5,30.4],[81.1,30.2],[79.7,30.9],[78.7,31.5],[78.5,32.6],[79.2,32.5],[79.2,33],[78.8,33.5],[78.9,34.3],[77.8,35.5],[76.2,35.9],[75.9,36.7],[75.2,37.1],[75,37.4],[74.8,38],[74.9,38.4],[74.3,38.6],[73.9,38.5],[73.7,39.4],[74,39.7],[73.8,39.9],[74.8,40.4],[75.5,40.6],[76.5,40.4],[76.9,41.1],[78.2,41.2],[78.5,41.6],[80.1,42.1],[80.3,42.3],[80.2,42.9],[80.9,43.2],[80,44.9],[81.9,45.3],[82.5,45.5],[83.2,47.3],[85.2,47],[85.7,47.5],[85.8,48.5],[86.6,48.5],[87.4,49.2],[87.8,49.3],[88,48.6],[88.9,48.1],[90.3,47.7],[91,46.9],[90.6,45.7],[90.9,45.3],[92.1,45.1],[93.5,45],[94.7,44.4],[95.3,44.2],[95.8,43.3],[96.3,42.7],[97.5,42.7],[99.5,42.5],[100.8,42.7],[101.8,42.5],[103.3,41.9],[104.5,41.9],[105,41.6],[106.1,42.1],[107.7,42.5],[109.2,42.5],[110.4,42.9],[111.1,43.4],[111.8,43.7],[111.7,44.1],[111.3,44.5],[111.9,45.1],[112.4,45],[113.5,44.8],[114.5,45.3],[116,45.7],[116.7,46.4],[117.4,46.7],[118.9,46.8],[119.7,46.7],[119.8,47],[118.9,47.7],[118.1,48.1],[117.3,47.7],[116.3,47.9],[115.7,47.7],[115.5,48.1],[116.2,49.1],[116.7,49.9],[117.9,49.5],[119.3,50.1],[119.3,50.6],[120.2,51.6],[120.7,52],[120.7,52.5],[120.2,52.8],[121,53.3],[122.2,53.4],[123.6,53.5],[125.1,53.2],[125.9,52.8],[126.6,51.8],[126.9,51.4],[127.3,50.7],[127.7,49.8]]]]},"id":"263"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-2.9,5],[-3.3,5],[-4,5.2],[-4.6,5.2],[-5.8,5],[-6.5,4.7],[-7.5,4.3],[-7.7,4.4],[-7.6,5.2],[-7.5,5.3],[-7.6,5.7],[-8,6.1],[-8.3,6.2],[-8.6,6.5],[-8.4,6.9],[-8.5,7.4],[-8.4,7.7],[-8.3,7.7],[-8.2,8.1],[-8.3,8.3],[-8.2,8.5],[-7.8,8.6],[-8.1,9.4],[-8.3,9.8],[-8.2,10.1],[-8,10.2],[-7.9,10.3],[-7.6,10.1],[-6.9,10.1],[-6.7,10.4],[-6.5,10.4],[-6.2,10.5],[-6.1,10.1],[-5.8,10.2],[-5.4,10.4],[-5,10.2],[-4.8,9.8],[-4.3,9.6],[-4,9.9],[-3.5,9.9],[-2.8,9.6],[-2.6,8.2],[-3,7.4],[-3.2,6.3],[-2.8,5.4],[-2.9,5]]]},"id":"226"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.1,2.3],[13,2.3],[12.4,2.2],[11.8,2.3],[11.3,2.3],[9.6,2.3],[9.8,3.1],[9.4,3.7],[8.9,3.9],[8.7,4.4],[8.5,4.5],[8.5,4.8],[8.8,5.5],[9.2,6.4],[9.5,6.5],[10.1,7],[10.5,7.1],[11.1,6.6],[11.7,7],[11.8,7.4],[12.1,7.8],[12.2,8.3],[12.8,8.7],[13,9.4],[13.2,9.6],[13.3,10.2],[13.6,10.8],[14.4,11.6],[14.5,11.9],[14.6,12.1],[14.2,12.5],[14.2,12.8],[14.5,12.9],[14.9,12.2],[15,11.6],[14.9,10.9],[15.5,10],[14.9,10],[14.6,9.9],[14.2,10],[14,9.5],[14.5,9],[15,8.8],[15.1,8.4],[15.4,7.7],[15.3,7.4],[14.8,6.4],[14.5,6.2],[14.5,5.5],[14.6,5],[14.5,4.7],[15,4.2],[15,3.9],[15.4,3.3],[15.9,3],[15.9,2.6],[16,2.3],[15.9,1.7],[15.1,2],[14.3,2.2],[13.1,2.3]]]},"id":"259"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.8,3.5],[30.8,2.3],[31.2,2.2],[30.9,1.8],[30.5,1.6],[30.1,1.1],[29.9,0.6],[29.8,-0.2],[29.6,-0.6],[29.6,-1.3],[29.3,-1.6],[29.3,-2.2],[29.1,-2.3],[29,-2.8],[29.3,-3.3],[29.3,-4.5],[29.5,-5.4],[29.4,-5.9],[29.6,-6.5],[30.2,-7.1],[30.7,-8.3],[30.3,-8.2],[29,-8.4],[28.7,-8.5],[28.4,-9.2],[28.7,-9.6],[28.5,-10.8],[28.4,-11.8],[28.6,-12],[29.3,-12.4],[29.6,-12.2],[29.7,-13.3],[28.9,-13.2],[28.5,-12.7],[28.2,-12.3],[27.4,-12.1],[27.2,-11.6],[26.6,-11.9],[25.8,-11.8],[25.4,-11.3],[24.8,-11.2],[24.3,-11.3],[24.3,-11],[23.9,-10.9],[23.5,-10.9],[22.8,-11],[22.4,-11],[22.2,-11.1],[22.2,-9.9],[21.9,-9.5],[21.8,-8.9],[21.9,-8.3],[21.7,-7.9],[21.7,-7.3],[20.5,-7.3],[20.6,-6.9],[20.1,-6.9],[20,-7.1],[19.4,-7.2],[19.2,-7.7],[19,-8],[18.5,-7.8],[18.1,-8],[17.5,-8.1],[17.1,-7.5],[16.9,-7.2],[16.6,-6.6],[16.3,-5.9],[13.4,-5.9],[13,-6],[12.7,-6],[12.3,-6.1],[12.2,-5.8],[12.4,-5.7],[12.5,-5.2],[12.6,-5],[13,-4.8],[13.3,-4.9],[13.6,-4.5],[14.1,-4.5],[14.2,-4.8],[14.6,-5],[15.2,-4.3],[15.8,-3.9],[16,-3.5],[16,-2.7],[16.4,-1.7],[16.9,-1.2],[17.5,-0.7],[17.6,-0.4],[17.7,-0.1],[17.8,0.3],[17.8,0.9],[17.9,1.7],[18.1,2.4],[18.4,2.9],[18.5,3.5],[18.5,4.2],[18.9,4.7],[19.5,5],[20.3,4.7],[20.9,4.3],[21.7,4.2],[22.4,4],[22.7,4.6],[22.8,4.7],[23.3,4.6],[24.4,5.1],[24.8,4.9],[25.1,4.9],[25.3,5.2],[25.7,5.3],[26.4,5.2],[27,5.1],[27.4,5.2],[28,4.4],[28.4,4.3],[28.7,4.5],[29.2,4.4],[29.7,4.6],[30,4.2],[30.8,3.5]]]},"id":"268"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13,-4.8],[12.6,-4.4],[12.3,-4.6],[11.9,-5],[11.1,-4],[11.9,-3.4],[11.5,-2.8],[11.8,-2.5],[12.5,-2.4],[12.6,-1.9],[13.1,-2.4],[14,-2.5],[14.3,-2],[14.4,-1.3],[14.3,-0.6],[13.8,0],[14.3,1.2],[14,1.4],[13.3,1.3],[13,1.8],[13.1,2.3],[14.3,2.2],[15.1,2],[15.9,1.7],[16,2.3],[16.5,3.2],[17.1,3.7],[17.8,3.6],[18.5,3.5],[18.4,2.9],[18.1,2.4],[17.9,1.7],[17.8,0.9],[17.8,0.3],[17.7,-0.1],[17.6,-0.4],[17.5,-0.7],[16.9,-1.2],[16.4,-1.7],[16,-2.7],[16,-3.5],[15.8,-3.9],[15.2,-4.3],[14.6,-5],[14.2,-4.8],[14.1,-4.5],[13.6,-4.5],[13.3,-4.9],[13,-4.8]]]},"id":"267"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-75.4,-0.2],[-75.8,0.1],[-76.3,0.4],[-76.6,0.3],[-77.4,0.4],[-77.7,0.8],[-77.9,0.8],[-78.9,1.4],[-79,1.7],[-78.6,1.8],[-78.7,2.3],[-78.4,2.6],[-77.9,2.7],[-77.5,3.3],[-77.1,3.8],[-77.5,4.1],[-77.3,4.7],[-77.5,5.6],[-77.3,5.8],[-77.5,6.7],[-77.9,7.2],[-77.8,7.7],[-77.4,7.6],[-77.2,7.9],[-77.5,8.5],[-77.4,8.7],[-76.8,8.6],[-76.1,9.3],[-75.7,9.4],[-75.7,9.8],[-75.5,10.6],[-74.9,11.1],[-74.3,11.1],[-74.2,11.3],[-73.4,11.2],[-72.6,11.7],[-72.2,12],[-71.8,12.4],[-71.4,12.4],[-71.1,12.1],[-71.3,11.8],[-72,11.6],[-72.2,11.1],[-72.6,10.8],[-72.9,10.5],[-73,9.7],[-73.3,9.2],[-72.8,9.1],[-72.7,8.6],[-72.4,8.4],[-72.4,8],[-72.5,7.6],[-72.4,7.4],[-72.2,7.3],[-72,7],[-70.7,7.1],[-70.1,7],[-69.4,6.1],[-69,6.2],[-68.3,6.2],[-67.7,6.3],[-67.3,6.1],[-67.5,5.6],[-67.7,5.2],[-67.8,4.5],[-67.6,3.8],[-67.3,3.5],[-67.3,3.3],[-67.8,2.8],[-67.4,2.6],[-67.2,2.3],[-66.9,1.3],[-67.1,1.1],[-67.3,1.7],[-67.5,2],[-67.9,1.7],[-69.8,1.7],[-69.8,1.1],[-69.2,1],[-69.3,0.6],[-69.5,0.7],[-70,0.5],[-70,-0.2],[-69.6,-0.5],[-69.4,-1.1],[-69.4,-1.6],[-69.9,-4.3],[-70.4,-3.8],[-70.7,-3.7],[-70,-2.7],[-70.8,-2.3],[-71.4,-2.3],[-71.8,-2.2],[-72.3,-2.4],[-73.1,-2.3],[-73.7,-1.3],[-74.1,-1],[-74.4,-0.5],[-75.1,-0.1],[-75.4,-0.2]]]},"id":"215"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-83,8.2],[-83.5,8.4],[-83.7,8.7],[-83.6,8.8],[-83.6,9.1],[-83.9,9.3],[-84.3,9.5],[-84.6,9.6],[-84.7,9.9],[-85,10.1],[-84.9,9.8],[-85.1,9.6],[-85.3,9.8],[-85.7,9.9],[-85.8,10.1],[-85.8,10.4],[-85.7,10.8],[-85.9,10.9],[-85.7,11.1],[-85.6,11.2],[-84.9,11],[-84.7,11.1],[-84.4,11],[-84.2,10.8],[-83.9,10.7],[-83.7,10.9],[-83.4,10.4],[-83,10],[-82.5,9.6],[-82.9,9.5],[-82.9,9.1],[-82.7,8.9],[-82.9,8.8],[-82.8,8.6],[-82.9,8.4],[-83,8.2]]]},"id":"216"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-82.3,23.2],[-81.4,23.1],[-80.6,23.1],[-79.7,22.8],[-79.3,22.4],[-78.3,22.5],[-78,22.3],[-77.1,21.7],[-76.5,21.2],[-76.2,21.2],[-75.6,21],[-75.7,20.7],[-74.9,20.7],[-74.2,20.3],[-74.3,20.1],[-75,19.9],[-75.6,19.9],[-76.3,20],[-77.8,19.9],[-77.1,20.4],[-77.5,20.7],[-78.1,20.7],[-78.5,21],[-78.7,21.6],[-79.3,21.6],[-80.2,21.8],[-80.5,22],[-81.8,22.2],[-82.2,22.4],[-81.8,22.6],[-82.8,22.7],[-83.5,22.2],[-83.9,22.2],[-84.1,21.9],[-84.5,21.8],[-85,21.9],[-84.4,22.2],[-84.2,22.6],[-83.8,22.8],[-83.3,23],[-82.5,23.1],[-82.3,23.2]]]},"id":"217"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[17,48.6],[16.5,48.8],[16,48.7],[15.3,49],[14.9,49],[14.3,48.6],[13.6,48.9],[13,49.3],[12.5,49.5],[12.4,50],[12.2,50.3],[13,50.5],[13.3,50.7],[14.1,50.9],[14.3,51.1],[14.6,51],[15,51.1],[15.5,50.8],[16.2,50.7],[16.2,50.4],[16.7,50.2],[16.9,50.5],[17.6,50.4],[17.6,50],[18.4,50],[18.9,49.5],[18.6,49.5],[18.4,49.3],[18.2,49.3],[18.1,49],[17.9,49],[17.9,48.9],[17.5,48.8],[17.1,48.8],[17,48.6]]]},"id":"353"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.9,55],[9.9,54.6],[11,54.4],[10.9,54],[12,54.2],[12.5,54.5],[13.6,54.1],[14.1,53.8],[14.4,53.2],[14.1,53],[14.4,52.6],[14.7,52.1],[14.6,51.7],[15,51.1],[14.6,51],[14.3,51.1],[14.1,50.9],[13.3,50.7],[13,50.5],[12.2,50.3],[12.4,50],[12.5,49.5],[13,49.3],[13.6,48.9],[13.2,48.4],[12.9,48.3],[13,47.6],[12.9,47.5],[12.6,47.7],[12.1,47.7],[11.4,47.5],[10.5,47.6],[10.4,47.3],[9.9,47.6],[9.6,47.5],[8.5,47.8],[8.3,47.6],[7.5,47.6],[7.6,48.3],[8.1,49],[6.7,49.2],[6.2,49.5],[6.2,49.9],[6,50.1],[6.2,50.8],[6,51.9],[6.6,51.9],[6.8,52.2],[7.1,53.1],[6.9,53.5],[7.1,53.7],[7.9,53.7],[8.1,53.5],[8.8,54],[8.6,54.4],[8.5,55],[9.3,54.8],[9.9,55]]]},"id":"358"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[43.1,12.7],[43.3,12.4],[43.3,12],[42.7,11.7],[43.1,11.5],[42.8,10.9],[42.6,11.1],[42.3,11],[41.8,11.1],[41.7,11.4],[41.7,11.6],[42,12.1],[42.4,12.5],[42.8,12.5],[43.1,12.7]]]},"id":"219"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[12.7,55.6],[12.1,54.8],[11,55.4],[10.9,55.8],[12.4,56.1],[12.7,55.6]]],[[[10.9,56.5],[10.7,56.1],[10.4,56.2],[9.6,55.5],[9.9,55],[9.3,54.8],[8.5,55],[8.1,55.5],[8.1,56.5],[8.3,56.8],[8.5,57.1],[9.4,57.2],[9.8,57.4],[10.6,57.7],[10.5,57.2],[10.3,56.9],[10.4,56.6],[10.9,56.5]]]]},"id":"218"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-71.7,19.7],[-71.6,19.9],[-70.8,19.9],[-70.2,19.6],[-70,19.6],[-69.8,19.3],[-69.2,19.3],[-69.3,19],[-68.8,19],[-68.3,18.6],[-68.7,18.2],[-69.2,18.4],[-69.6,18.4],[-70,18.4],[-70.1,18.2],[-70.5,18.2],[-70.7,18.4],[-71,18.3],[-71.4,17.6],[-71.7,17.8],[-71.7,18],[-71.7,18.3],[-71.9,18.6],[-71.7,18.8],[-71.6,19.2],[-71.7,19.7]]]},"id":"221"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[12,23.5],[8.6,21.6],[5.7,19.6],[4.3,19.2],[3.2,19.1],[3.1,19.7],[2.7,19.9],[2.1,20.1],[1.8,20.6],[-1.6,22.8],[-4.9,25],[-8.7,27.4],[-8.7,27.6],[-8.7,27.7],[-8.7,28.8],[-7.1,29.6],[-6.1,29.7],[-5.2,30],[-4.9,30.5],[-3.7,30.9],[-3.6,31.6],[-3.1,31.7],[-2.6,32.1],[-1.3,32.3],[-1.1,32.7],[-1.4,32.9],[-1.7,33.9],[-1.8,34.5],[-2.2,35.2],[-1.2,35.7],[-0.1,35.9],[0.5,36.3],[1.5,36.6],[3.2,36.8],[4.8,36.9],[5.3,36.7],[6.3,37.1],[7.3,37.1],[7.7,36.9],[8.4,36.9],[8.2,36.4],[8.4,35.5],[8.1,34.7],[7.5,34.1],[7.6,33.3],[8.4,32.7],[8.4,32.5],[9.1,32.1],[9.5,30.3],[9.8,29.4],[9.9,29],[9.7,28.1],[9.8,27.7],[9.6,27.1],[9.7,26.5],[9.3,26.1],[9.9,25.4],[9.9,24.9],[10.3,24.4],[10.8,24.6],[11.6,24.1],[12,23.5]]]},"id":"189"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-80.3,-3.4],[-79.8,-2.7],[-80,-2.2],[-80.4,-2.7],[-81,-2.2],[-80.8,-2],[-80.9,-1.1],[-80.6,-0.9],[-80.4,-0.3],[-80,0.4],[-80.1,0.8],[-79.5,1],[-78.9,1.4],[-77.9,0.8],[-77.7,0.8],[-77.4,0.4],[-76.6,0.3],[-76.3,0.4],[-75.8,0.1],[-75.4,-0.2],[-75.2,-0.9],[-75.5,-1.6],[-76.6,-2.6],[-77.8,-3],[-78.5,-3.9],[-78.6,-4.5],[-79.2,-5],[-79.6,-4.5],[-80,-4.3],[-80.4,-4.4],[-80.5,-4.1],[-80.2,-3.8],[-80.3,-3.4]]]},"id":"222"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34.9,29.5],[34.6,29.1],[34.4,28.3],[34.2,27.8],[33.9,27.6],[33.6,28],[33.1,28.4],[32.4,29.9],[32.3,29.8],[32.7,28.7],[33.3,27.7],[34.1,26.1],[34.5,25.6],[34.8,25],[35.7,23.9],[35.5,23.8],[35.5,23.1],[36.7,22.2],[36.9,22],[32.9,22],[29,22],[25,22],[25,25.7],[25,29.2],[24.7,30],[25,30.7],[24.8,31.1],[25.2,31.6],[26.5,31.6],[27.5,31.3],[28.5,31],[28.9,30.9],[29.7,31.2],[30.1,31.5],[31,31.6],[31.7,31.4],[32,30.9],[32.2,31.3],[33,31],[33.8,31],[34.3,31.2],[34.9,29.5]]]},"id":"223"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[42.4,12.5],[42,12.9],[41.6,13.5],[41.2,13.8],[40.9,14.1],[40,14.5],[39.3,14.5],[39.1,14.7],[38.5,14.5],[37.9,15],[37.6,14.2],[36.4,14.4],[36.3,14.8],[36.8,16.3],[36.9,17],[37.2,17.3],[37.9,17.4],[38.4,18],[39,16.8],[39.3,15.9],[39.8,15.4],[41.2,14.5],[41.7,13.9],[42.3,13.3],[42.6,13],[43.1,12.7],[42.8,12.5],[42.4,12.5]]]},"id":"227"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-9,41.9],[-9,42.6],[-9.4,43],[-8,43.7],[-6.8,43.6],[-5.4,43.6],[-4.3,43.4],[-3.5,43.5],[-1.9,43.4],[-1.5,43],[0.3,42.6],[0.7,42.8],[1.8,42.3],[3,42.5],[3,41.9],[2.1,41.2],[0.8,41],[0.7,40.7],[0.1,40.1],[-0.3,39.3],[0.1,38.7],[-0.5,38.3],[-0.7,37.6],[-1.4,37.4],[-2.1,36.7],[-3.4,36.7],[-4.4,36.7],[-5,36.3],[-5.4,35.9],[-5.9,36],[-6.2,36.4],[-6.5,36.9],[-7.5,37.1],[-7.5,37.4],[-7.2,37.8],[-7,38.1],[-7.4,38.4],[-7.1,39],[-7.5,39.6],[-7.1,39.7],[-7,40.2],[-6.9,40.3],[-6.9,41.1],[-6.4,41.4],[-6.7,41.9],[-7.3,41.9],[-7.4,41.8],[-8,41.8],[-8.3,42.3],[-8.7,42.1],[-9,41.9]]]},"id":"336"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[24.3,57.8],[24.4,58.4],[24.1,58.3],[23.4,58.6],[23.3,59.2],[24.6,59.5],[25.9,59.6],[26.9,59.4],[28,59.5],[28.1,59.3],[27.4,58.7],[27.7,57.8],[27.3,57.5],[26.5,57.5],[25.6,57.8],[25.2,58],[24.3,57.8]]]},"id":"228"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[37.9,15],[38.5,14.5],[39.1,14.7],[39.3,14.5],[40,14.5],[40.9,14.1],[41.2,13.8],[41.6,13.5],[42,12.9],[42.4,12.5],[42,12.1],[41.7,11.6],[41.7,11.4],[41.8,11.1],[42.3,11],[42.6,11.1],[42.8,10.9],[42.6,10.6],[42.9,10],[43.3,9.5],[43.7,9.2],[46.9,8],[47.8,8],[45,5],[43.7,5],[42.8,4.3],[42.1,4.2],[41.9,3.9],[41.2,3.9],[40.8,4.3],[39.9,3.8],[39.6,3.4],[38.9,3.5],[38.7,3.6],[38.4,3.6],[38.1,3.6],[36.9,4.4],[36.2,4.4],[35.8,4.8],[35.8,5.3],[35.3,5.5],[34.7,6.6],[34.3,6.8],[34.1,7.2],[33.6,7.7],[33,7.8],[33.3,8.4],[33.8,8.4],[34,8.7],[34,9.6],[34.3,10.6],[34.7,10.9],[34.8,11.3],[35.3,12.1],[35.9,12.6],[36.3,13.6],[36.4,14.4],[37.6,14.2],[37.9,15]]]},"id":"229"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[28.6,69.1],[28.4,68.4],[30,67.7],[29.1,66.9],[30.2,65.8],[29.5,64.9],[30.4,64.2],[30,63.6],[31.5,62.9],[31.1,62.4],[30.2,61.8],[28.1,60.5],[26.3,60.4],[24.5,60.1],[22.9,59.8],[22.3,60.4],[21.3,60.7],[21.5,61.7],[21.1,62.6],[21.5,63.2],[22.4,63.8],[24.7,64.9],[25.4,65.1],[25.3,65.5],[23.9,66],[23.6,66.4],[23.5,67.9],[22,68.6],[20.6,69.1],[21.2,69.4],[22.4,68.8],[23.7,68.9],[24.7,68.6],[25.7,69.1],[26.2,69.8],[27.7,70.2],[29,69.8],[28.6,69.1]]]},"id":"232"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[178.4,-17.3],[178.7,-17.6],[178.6,-18.2],[177.9,-18.3],[177.4,-18.2],[177.3,-17.7],[177.7,-17.4],[178.1,-17.5],[178.4,-17.3]]],[[[179.4,-16.8],[178.7,-17],[178.6,-16.6],[179.1,-16.4],[179.4,-16.4],[179.9,-16.1],[179.9,-16.6],[179.4,-16.8]]],[[[-179.9,-16.5],[-179.9,-16.6],[-179.9,-16.1],[-179.8,-16],[-179.9,-16.5]]]]},"id":"230"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.2,-51.9],[-60,-51.3],[-59.1,-51.5],[-58.6,-51.1],[-57.8,-51.5],[-58.1,-51.9],[-59.4,-52.2],[-59.9,-51.9],[-60.7,-52.3],[-61.2,-51.9]]]}},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-52.6,2.5],[-52.9,2.1],[-53.4,2.1],[-53.6,2.3],[-53.8,2.4],[-54.1,2.1],[-54.5,2.3],[-54.3,2.7],[-54.2,3.2],[-54,3.6],[-54.4,4.2],[-54.5,4.9],[-54,5.8],[-53.6,5.6],[-52.9,5.4],[-51.8,4.6],[-51.7,4.2],[-52.2,3.2],[-52.6,2.5]]],[[[9.6,42.2],[9.2,41.4],[8.8,41.6],[8.5,42.3],[8.7,42.6],[9.4,43],[9.6,42.2]]],[[[3.6,50.4],[4.3,49.9],[4.8,50],[5.7,49.5],[5.9,49.4],[6.2,49.5],[6.7,49.2],[8.1,49],[7.6,48.3],[7.5,47.6],[7.2,47.4],[6.7,47.5],[6.8,47.3],[6,46.7],[6,46.3],[6.5,46.4],[6.8,46],[6.8,45.7],[7.1,45.3],[6.7,45],[7,44.3],[7.5,44.1],[7.4,43.7],[6.5,43.1],[4.6,43.4],[3.1,43.1],[3,42.5],[1.8,42.3],[0.7,42.8],[0.3,42.6],[-1.5,43],[-1.9,43.4],[-1.4,44],[-1.2,46],[-2.2,47.1],[-3,47.6],[-4.5,48],[-4.6,48.7],[-3.3,48.9],[-1.6,48.6],[-1.9,49.8],[-1,49.3],[1.3,50.1],[1.6,50.9],[2.5,51.1],[2.7,50.8],[3.1,50.8],[3.6,50.4]]]]},"id":"234"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[11.1,-4],[10.1,-3],[9.4,-2.1],[8.8,-1.1],[8.8,-0.8],[9,-0.5],[9.3,0.3],[9.5,1],[9.8,1.1],[11.3,1.1],[11.3,2.3],[11.8,2.3],[12.4,2.2],[13,2.3],[13.1,2.3],[13,1.8],[13.3,1.3],[14,1.4],[14.3,1.2],[13.8,0],[14.3,-0.6],[14.4,-1.3],[14.3,-2],[14,-2.5],[13.1,-2.4],[12.6,-1.9],[12.5,-2.4],[11.8,-2.5],[11.5,-2.8],[11.9,-3.4],[11.1,-4]]]},"id":"235"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-5.7,54.6],[-6.2,53.9],[-7,54.1],[-7.6,54.1],[-7.4,54.6],[-7.6,55.1],[-6.7,55.2],[-5.7,54.6]]],[[[-3,58.6],[-4.1,57.6],[-3.1,57.7],[-2,57.7],[-2.2,56.9],[-3.1,56],[-2.1,55.9],[-2,55.8],[-1.1,54.6],[-0.4,54.5],[0.2,53.3],[0.5,52.9],[1.7,52.7],[1.6,52.1],[1.1,51.8],[1.4,51.3],[0.6,50.8],[-0.8,50.8],[-2.5,50.5],[-3,50.7],[-3.6,50.2],[-4.5,50.3],[-5.2,50],[-5.8,50.2],[-4.3,51.2],[-3.4,51.4],[-3.4,51.4],[-5,51.6],[-5.3,52],[-4.2,52.3],[-4.8,52.8],[-4.6,53.5],[-3.1,53.4],[-3.1,53.4],[-2.9,54],[-3.6,54.6],[-3.6,54.6],[-4.8,54.8],[-5.1,55.1],[-4.7,55.5],[-5,55.8],[-5.6,55.3],[-5.6,56.3],[-6.1,56.8],[-5.8,57.8],[-5,58.6],[-4.2,58.6],[-3,58.6]]]]},"id":"338"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[41.6,41.5],[41.7,42],[41.5,42.6],[40.9,43],[40.3,43.1],[40,43.4],[40.1,43.6],[40.9,43.4],[42.4,43.2],[43.8,42.7],[43.9,42.6],[44.5,42.7],[45.5,42.5],[45.8,42.1],[46.4,41.9],[46.1,41.7],[46.6,41.2],[46.5,41.1],[46,41.1],[45.2,41.4],[45,41.2],[43.6,41.1],[42.6,41.6],[41.6,41.5]]]},"id":"237"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.1,5.9],[-0.5,5.3],[-1.1,5],[-2,4.7],[-2.9,5],[-2.8,5.4],[-3.2,6.3],[-3,7.4],[-2.6,8.2],[-2.8,9.6],[-3,10.4],[-2.9,11],[-1.2,11],[-0.8,10.9],[-0.4,11.1],[0,11],[0,10.7],[0.4,10.2],[0.4,9.5],[0.5,8.7],[0.7,8.3],[0.5,7.4],[0.6,6.9],[0.8,6.3],[1.1,5.9]]]},"id":"238"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-8.4,7.7],[-8.7,7.7],[-8.9,7.3],[-9.2,7.3],[-9.4,7.5],[-9.3,7.9],[-9.8,8.5],[-10,8.4],[-10.2,8.4],[-10.5,8.3],[-10.5,8.7],[-10.7,9],[-10.6,9.3],[-10.8,9.7],[-11.1,10],[-11.9,10],[-12.2,9.9],[-12.4,9.8],[-12.6,9.6],[-12.7,9.3],[-13.2,8.9],[-13.7,9.5],[-14.1,9.9],[-14.3,10],[-14.6,10.2],[-14.7,10.7],[-14.8,10.9],[-15.1,11],[-14.7,11.5],[-14.4,11.5],[-14.1,11.7],[-13.9,11.7],[-13.7,11.8],[-13.8,12.1],[-13.7,12.2],[-13.7,12.6],[-13.2,12.6],[-12.5,12.3],[-12.3,12.4],[-12.2,12.5],[-11.7,12.4],[-11.5,12.4],[-11.5,12.1],[-11.3,12.1],[-11,12.2],[-10.9,12.2],[-10.6,11.9],[-10.2,11.8],[-9.9,12.1],[-9.6,12.2],[-9.3,12.3],[-9.1,12.3],[-8.9,12.1],[-8.8,11.8],[-8.4,11.4],[-8.6,11.1],[-8.6,10.8],[-8.4,10.9],[-8.3,10.8],[-8.3,10.5],[-8,10.2],[-8.2,10.1],[-8.3,9.8],[-8.1,9.4],[-7.8,8.6],[-8.2,8.5],[-8.3,8.3],[-8.2,8.1],[-8.3,7.7],[-8.4,7.7]]]},"id":"241"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-16.8,13.2],[-16.7,13.6],[-15.6,13.6],[-15.4,13.9],[-15.1,13.9],[-14.7,13.6],[-14.4,13.6],[-14,13.8],[-13.8,13.5],[-14.3,13.3],[-14.7,13.3],[-15.1,13.5],[-15.5,13.3],[-15.7,13.3],[-15.9,13.1],[-16.8,13.2]]]},"id":"236"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-15.1,11],[-15.7,11.5],[-16.1,11.5],[-16.3,11.8],[-16.3,12],[-16.6,12.2],[-16.7,12.4],[-16.1,12.5],[-15.8,12.5],[-15.5,12.6],[-13.7,12.6],[-13.7,12.2],[-13.8,12.1],[-13.7,11.8],[-13.9,11.7],[-14.1,11.7],[-14.4,11.5],[-14.7,11.5],[-15.1,11]]]},"id":"242"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.5,1],[9.3,1.2],[9.6,2.3],[11.3,2.3],[11.3,1.1],[9.8,1.1],[9.5,1]]]},"id":"224"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[23.7,35.7],[24.2,35.4],[25,35.4],[25.8,35.4],[25.7,35.2],[26.3,35.3],[26.2,35],[24.7,34.9],[24.7,35.1],[23.5,35.3],[23.7,35.7]]],[[[26.6,41.6],[26.3,40.9],[26.1,40.8],[25.4,40.9],[24.9,40.9],[23.7,40.7],[24.4,40.1],[23.9,40],[23.3,40],[22.8,40.5],[22.6,40.3],[22.8,39.7],[23.4,39.2],[23,39],[23.5,38.5],[24,38.2],[24,37.7],[23.1,37.9],[23.4,37.4],[22.8,37.3],[23.2,36.4],[22.5,36.4],[21.7,36.8],[21.3,37.6],[21.1,38.3],[20.7,38.8],[20.2,39.3],[20.2,39.6],[20.6,40.1],[20.7,40.4],[21,40.6],[21,40.8],[21.7,40.9],[22.1,41.1],[22.6,41.1],[22.8,41.3],[23,41.3],[23.7,41.3],[24.5,41.6],[25.2,41.2],[26.1,41.3],[26.1,41.8],[26.6,41.6]]]]},"id":"245"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-46.8,82.6],[-43.4,83.2],[-39.9,83.2],[-38.6,83.5],[-35.1,83.6],[-27.1,83.5],[-20.8,82.7],[-22.7,82.3],[-26.5,82.3],[-31.9,82.2],[-31.4,82],[-27.9,82.1],[-24.8,81.8],[-22.9,82.1],[-22.1,81.7],[-23.2,81.2],[-20.6,81.5],[-15.8,81.9],[-12.8,81.7],[-12.2,81.3],[-16.3,80.6],[-16.9,80.3],[-20,80.2],[-17.7,80.1],[-18.9,79.4],[-19.7,78.8],[-19.7,77.6],[-18.5,77],[-20,76.9],[-21.7,76.6],[-19.8,76.1],[-19.6,75.2],[-20.7,75.2],[-19.4,74.3],[-21.6,74.2],[-20.4,73.8],[-20.8,73.5],[-22.2,73.3],[-23.6,73.3],[-22.3,72.6],[-22.3,72.2],[-24.3,72.6],[-24.8,72.3],[-23.4,72.1],[-22.1,71.5],[-21.8,70.7],[-23.5,70.5],[-24.3,70.9],[-25.5,71.4],[-25.2,70.8],[-26.4,70.2],[-23.7,70.2],[-22.3,70.1],[-25,69.3],[-27.7,68.5],[-30.7,68.1],[-31.8,68.1],[-32.8,67.7],[-34.2,66.7],[-36.4,66],[-37,65.9],[-38.4,65.7],[-39.8,65.5],[-40.7,64.8],[-40.7,64.1],[-41.2,63.5],[-42.8,62.7],[-42.4,61.9],[-42.9,61.1],[-43.4,60.1],[-44.8,60],[-46.3,60.9],[-48.3,60.9],[-49.2,61.4],[-49.9,62.4],[-51.6,63.6],[-52.1,64.3],[-52.3,65.2],[-53.7,66.1],[-53.3,66.8],[-54,67.2],[-53,68.4],[-51.5,68.7],[-51.1,69.1],[-50.9,69.9],[-52,69.6],[-52.6,69.4],[-53.5,69.3],[-54.7,69.6],[-54.8,70.3],[-54.4,70.8],[-53.4,70.8],[-51.4,70.6],[-53.1,71.2],[-54,71.5],[-55,71.4],[-55.8,71.7],[-54.7,72.6],[-55.3,73],[-56.1,73.6],[-57.3,74.7],[-58.6,75.1],[-58.6,75.5],[-61.3,76.1],[-63.4,76.2],[-66.1,76.1],[-68.5,76.1],[-69.7,76.4],[-71.4,77],[-68.8,77.3],[-66.8,77.4],[-71,77.6],[-73.3,78],[-73.2,78.4],[-69.4,78.9],[-65.7,79.4],[-65.3,79.8],[-68,80.1],[-67.2,80.5],[-63.7,81.2],[-62.2,81.3],[-62.7,81.8],[-60.3,82],[-57.2,82.2],[-54.1,82.2],[-53,81.9],[-50.4,82.4],[-48,82.1],[-46.6,82],[-44.5,81.7],[-46.9,82.2],[-46.8,82.6]]]}},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-90.1,13.7],[-90.6,13.9],[-91.2,13.9],[-91.7,14.1],[-92.2,14.5],[-92.2,14.8],[-92.1,15.1],[-92.2,15.3],[-91.7,16.1],[-90.5,16.1],[-90.4,16.4],[-90.6,16.5],[-90.7,16.7],[-91.1,16.9],[-91.5,17.3],[-91,17.3],[-91,17.8],[-90.1,17.8],[-89.1,17.8],[-89.2,17],[-89.2,15.9],[-88.9,15.9],[-88.6,15.7],[-88.5,15.9],[-88.2,15.7],[-88.7,15.3],[-89.2,15.1],[-89.2,14.9],[-89.1,14.7],[-89.4,14.4],[-89.6,14.4],[-89.5,14.2],[-89.7,14.1],[-90.1,13.9],[-90.1,13.7]]]},"id":"240"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-59.8,8.4],[-59.1,8],[-58.5,7.3],[-58.5,6.8],[-58.1,6.8],[-57.5,6.3],[-57.1,6],[-57.3,5.1],[-57.9,4.8],[-57.9,4.6],[-58,4.1],[-57.6,3.3],[-57.3,3.3],[-57.2,2.8],[-56.5,1.9],[-56.8,1.9],[-57.3,1.9],[-57.7,1.7],[-58.1,1.5],[-58.4,1.5],[-58.5,1.3],[-59,1.3],[-59.6,1.8],[-59.7,2.2],[-60,2.8],[-59.8,3.6],[-59.5,4],[-59.8,4.4],[-60.1,4.6],[-60,5],[-60.2,5.2],[-60.7,5.2],[-61.4,6],[-61.1,6.2],[-61.2,6.7],[-60.5,6.9],[-60.3,7],[-60.6,7.4],[-60.6,7.8],[-59.8,8.4]]]},"id":"243"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-87.3,13],[-87.5,13.3],[-87.8,13.4],[-87.7,13.8],[-87.9,13.9],[-88.1,14],[-88.5,13.8],[-88.5,14],[-88.8,14.1],[-89.1,14.3],[-89.4,14.4],[-89.1,14.7],[-89.2,14.9],[-89.2,15.1],[-88.7,15.3],[-88.2,15.7],[-88.1,15.7],[-87.9,15.9],[-87.6,15.9],[-87.5,15.8],[-87.4,15.8],[-86.9,15.8],[-86.4,15.8],[-86.1,15.9],[-86,16],[-85.7,16],[-85.4,15.9],[-85.2,15.9],[-85,16],[-84.5,15.9],[-84.4,15.8],[-84.1,15.6],[-83.8,15.4],[-83.4,15.3],[-83.1,15],[-83.5,15],[-83.6,14.9],[-84,14.7],[-84.2,14.7],[-84.4,14.6],[-84.6,14.7],[-84.8,14.8],[-84.9,14.8],[-85.1,14.6],[-85.1,14.6],[-85.2,14.4],[-85.5,14.1],[-85.7,14],[-85.8,13.8],[-86.1,14],[-86.3,13.8],[-86.5,13.8],[-86.8,13.8],[-86.7,13.3],[-86.9,13.3],[-87,13],[-87.3,13]]]},"id":"246"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[18.8,45.9],[19.1,45.5],[19.4,45.2],[19,44.9],[18.6,45.1],[17.9,45.1],[17,45.2],[16.5,45.2],[16.3,45],[16,45.2],[15.8,44.8],[16.2,44.4],[16.5,44],[16.9,43.7],[17.3,43.4],[17.7,43],[18.6,42.6],[18.5,42.5],[17.5,42.8],[16.9,43.2],[16,43.5],[15.2,44.2],[15.4,44.3],[14.9,44.7],[14.9,45.1],[14.3,45.2],[14,44.8],[13.7,45.1],[13.7,45.5],[13.7,45.5],[14.4,45.5],[14.6,45.6],[14.9,45.5],[15.3,45.5],[15.3,45.7],[15.7,45.8],[15.8,46.2],[16.6,46.5],[16.9,46.4],[17.6,46],[18.5,45.8],[18.8,45.9]]]},"id":"269"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-73.2,19.9],[-72.6,19.9],[-71.7,19.7],[-71.6,19.2],[-71.7,18.8],[-71.9,18.6],[-71.7,18.3],[-71.7,18],[-72.4,18.2],[-72.8,18.1],[-73.5,18.2],[-73.9,18],[-74.5,18.3],[-74.4,18.7],[-73.4,18.5],[-72.7,18.4],[-72.3,18.7],[-72.8,19.1],[-72.8,19.5],[-73.4,19.6],[-73.2,19.9]]]},"id":"244"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[16.2,46.9],[16.5,47.5],[16.3,47.7],[16.9,47.7],[17,48.1],[17.5,47.9],[17.9,47.8],[18.7,47.9],[18.8,48.1],[19.2,48.1],[19.7,48.3],[19.8,48.2],[20.2,48.3],[20.5,48.6],[20.8,48.6],[21.9,48.3],[22.1,48.4],[22.6,48.2],[22.7,47.9],[22.1,47.7],[21.6,47],[21,46.3],[20.2,46.1],[19.6,46.2],[18.8,45.9],[18.5,45.8],[17.6,46],[16.9,46.4],[16.6,46.5],[16.4,46.8],[16.2,46.9]]]},"id":"361"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[120.7,-10.2],[120.3,-10.3],[119,-9.6],[119.9,-9.4],[120.4,-9.7],[120.8,-10],[120.7,-10.2]]],[[[124.4,-10.1],[123.6,-10.4],[123.5,-10.2],[123.6,-9.9],[124,-9.3],[125,-8.9],[125.1,-9.1],[125.1,-9.4],[124.4,-10.1]]],[[[117.9,-8.1],[118.3,-8.4],[118.9,-8.3],[119.1,-8.7],[118,-8.9],[117.3,-9],[116.7,-9],[117.1,-8.5],[117.6,-8.4],[117.9,-8.1]]],[[[122.9,-8.1],[122.8,-8.6],[121.3,-8.9],[119.9,-8.8],[119.9,-8.4],[120.7,-8.2],[121.3,-8.5],[122,-8.5],[122.9,-8.1]]],[[[108.6,-6.8],[110.5,-6.9],[110.8,-6.5],[112.6,-6.9],[113,-7.6],[114.5,-7.8],[115.7,-8.4],[114.6,-8.8],[113.5,-8.3],[112.6,-8.4],[111.5,-8.3],[110.6,-8.1],[109.4,-7.7],[108.7,-7.6],[108.3,-7.8],[106.5,-7.4],[106.3,-6.9],[105.4,-6.9],[106.1,-5.9],[107.3,-6],[108.1,-6.3],[108.5,-6.4],[108.6,-6.8]]],[[[134.7,-6.2],[134.2,-6.9],[134.1,-6.1],[134.3,-5.8],[134.5,-5.4],[134.7,-5.7],[134.7,-6.2]]],[[[127.2,-3.5],[126.9,-3.8],[126.2,-3.6],[126,-3.2],[127,-3.1],[127.2,-3.5]]],[[[130.5,-3.1],[130.8,-3.9],[130,-3.4],[129.2,-3.4],[128.6,-3.4],[127.9,-3.4],[128.1,-2.8],[129.4,-2.8],[130.5,-3.1]]],[[[134.1,-1.2],[134.4,-2.8],[135.5,-3.4],[136.3,-2.3],[137.4,-1.7],[138.3,-1.7],[139.2,-2.1],[139.9,-2.4],[141,-2.6],[141,-5.9],[141,-9.1],[140.1,-8.3],[139.1,-8.1],[138.9,-8.4],[137.6,-8.4],[138,-7.6],[138.7,-7.3],[138.4,-6.2],[137.9,-5.4],[136,-4.5],[135.2,-4.5],[133.7,-3.5],[133.4,-4],[133,-4.1],[132.8,-3.7],[132.8,-3.3],[132,-2.8],[133.1,-2.5],[133.8,-2.5],[133.7,-2.2],[132.2,-2.2],[131.8,-1.6],[130.9,-1.4],[130.5,-0.9],[131.9,-0.7],[132.4,-0.4],[134,-0.8],[134.1,-1.2]]],[[[125.2,1.4],[124.4,0.4],[123.7,0.2],[122.7,0.4],[121.1,0.4],[120.2,0.2],[120,-0.5],[120.9,-1.4],[121.5,-1],[123.3,-0.6],[123.3,-1.1],[122.8,-0.9],[122.4,-1.5],[121.5,-1.9],[122.5,-3.2],[122.3,-3.5],[123.2,-4.7],[123.2,-5.3],[122.6,-5.6],[122.2,-5.3],[122.7,-4.5],[121.7,-4.9],[121.5,-4.6],[121.6,-4.2],[120.9,-3.6],[121,-2.6],[120.3,-2.9],[120.4,-4.1],[120.4,-5.5],[119.8,-5.7],[119.4,-5.4],[119.7,-4.5],[119.5,-3.5],[119.1,-3.5],[118.8,-2.8],[119.2,-2.1],[119.3,-1.4],[119.8,0.2],[120,0.6],[120.9,1.3],[121.7,1],[122.9,0.9],[124.1,0.9],[125.1,1.6],[125.2,1.4]]],[[[128.7,1.1],[128.6,0.3],[128.1,0.4],[128,-0.3],[128.4,-0.8],[128.1,-0.9],[127.7,-0.3],[127.4,1],[127.6,1.8],[127.9,2.2],[128,1.6],[128.6,1.5],[128.7,1.1]]],[[[117.9,1.8],[119,0.9],[117.8,0.8],[117.5,0.1],[117.5,-0.8],[116.6,-1.5],[116.5,-2.5],[116.1,-4],[116,-3.7],[114.9,-4.1],[114.5,-3.5],[113.8,-3.4],[113.3,-3.1],[112.1,-3.5],[111.7,-3],[111,-3],[110.2,-2.9],[110.1,-1.6],[109.6,-1.3],[109.1,-0.5],[109,0.4],[109.1,1.3],[109.7,2],[109.8,1.3],[110.5,0.8],[111.2,1],[111.8,0.9],[112.4,1.4],[112.9,1.5],[113.8,1.2],[114.6,1.4],[115.1,2.8],[115.5,3.2],[115.9,4.3],[117,4.3],[117.9,4.1],[117.3,3.2],[118,2.3],[117.9,1.8]]],[[[105.8,-5.9],[104.7,-5.9],[103.9,-5],[102.6,-4.2],[102.2,-3.6],[101.4,-2.8],[100.9,-2.1],[100.1,-0.7],[99.3,0.2],[99,1],[98.6,1.8],[97.7,2.5],[97.2,3.3],[96.4,3.9],[95.4,5],[95.3,5.5],[95.9,5.4],[97.5,5.2],[98.4,4.3],[99.1,3.6],[99.7,3.2],[100.6,2.1],[101.7,2.1],[102.5,1.4],[103.1,0.6],[103.8,0.1],[103.4,-0.7],[104,-1.1],[104.4,-1.1],[104.5,-1.8],[104.9,-2.3],[105.6,-2.4],[106.1,-3.1],[105.9,-4.3],[105.8,-5.9]]]]},"id":"248"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[77.8,35.5],[78.9,34.3],[78.8,33.5],[79.2,33],[79.2,32.5],[78.5,32.6],[78.7,31.5],[79.7,30.9],[81.1,30.2],[80.5,29.7],[80.1,28.8],[81.1,28.4],[82,27.9],[83.3,27.4],[84.7,27.2],[85.3,26.7],[86,26.6],[87.2,26.4],[88.1,26.4],[88.2,26.8],[88,27.4],[88.1,27.9],[88.7,28.1],[88.8,27.3],[88.8,27.1],[89.7,26.7],[90.4,26.9],[91.2,26.8],[92,26.8],[92.1,27.5],[91.7,27.8],[92.5,27.9],[93.4,28.6],[94.6,29.3],[95.4,29],[96.1,29.5],[96.6,28.8],[96.2,28.4],[97.3,28.3],[97.4,27.9],[97.1,27.7],[97.1,27.1],[96.4,27.3],[95.1,26.6],[95.2,26],[94.6,25.2],[94.6,24.7],[94.1,23.9],[93.3,24.1],[93.3,23],[93.1,22.7],[93.2,22.3],[92.7,22],[92.1,23.6],[91.9,23.6],[91.7,23],[91.2,23.5],[91.5,24.1],[91.9,24.1],[92.4,25],[91.8,25.1],[90.9,25.1],[89.9,25.3],[89.8,26],[89.4,26],[88.6,26.4],[88.2,25.8],[88.9,25.2],[88.3,24.9],[88.1,24.5],[88.7,24.2],[88.5,23.6],[88.9,22.9],[89,22.1],[88.9,21.7],[88.2,21.7],[87,21.5],[87,20.7],[86.5,20.2],[85.1,19.5],[83.9,18.3],[83.2,17.7],[82.2,17],[82.2,16.6],[81.7,16.3],[80.8,16],[80.3,15.9],[80,15.1],[80.2,13.8],[80.3,13],[79.9,12.1],[79.9,10.4],[79.3,10.3],[78.9,9.5],[79.2,9.2],[78.3,8.9],[77.9,8.3],[77.5,8],[76.6,8.9],[76.1,10.3],[75.7,11.3],[75.4,11.8],[74.9,12.7],[74.6,14],[74.4,14.6],[73.5,16],[73.1,17.9],[72.8,19.2],[72.8,20.4],[72.6,21.4],[71.2,20.8],[70.5,20.9],[69.2,22.1],[69.6,22.5],[69.3,22.8],[68.2,23.7],[68.8,24.4],[71,24.4],[70.8,25.2],[70.3,25.7],[70.2,26.5],[69.5,26.9],[70.6,28],[71.8,27.9],[72.8,29],[73.5,30],[74.4,31],[74.4,31.7],[75.3,32.3],[74.5,32.8],[74.1,33.4],[73.7,34.3],[74.2,34.7],[75.8,34.5],[76.9,34.7],[77.8,35.5]]]},"id":"375"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-6.2,53.9],[-6,53.2],[-6.8,52.3],[-8.6,51.7],[-10,51.8],[-9.2,52.9],[-9.7,53.9],[-8.3,54.7],[-7.6,55.1],[-7.4,54.6],[-7.6,54.1],[-7,54.1],[-6.2,53.9]]]},"id":"251"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[53.9,37.2],[54.8,37.4],[55.5,38],[56.2,37.9],[56.6,38.1],[57.3,38],[58.4,37.5],[59.2,37.4],[60.4,36.5],[61.1,36.5],[61.2,35.7],[60.8,34.4],[60.5,33.7],[61,33.5],[60.5,33],[60.9,32.2],[60.9,31.5],[61.7,31.4],[61.8,30.7],[60.9,29.8],[61.4,29.3],[61.8,28.7],[62.7,28.3],[62.8,27.4],[63.2,27.2],[63.3,26.8],[61.9,26.2],[61.5,25.1],[59.6,25.4],[58.5,25.6],[57.4,25.7],[57,27],[56.5,27.1],[55.7,27],[54.7,26.5],[53.5,26.8],[52.5,27.6],[51.5,27.9],[50.9,28.8],[50.1,30.1],[49.6,30],[48.9,30.3],[48.6,29.9],[48,30.5],[48,31],[47.7,31],[47.8,31.7],[47.3,32.5],[46.1,33],[45.4,34],[45.6,34.7],[46.2,35.1],[46.1,35.7],[45.4,36],[44.8,37.2],[44.2,38],[44.4,38.3],[44.1,39.4],[44.8,39.7],[45,39.3],[45.5,38.9],[46.1,38.7],[46.5,38.8],[47.7,39.5],[48.1,39.6],[48.4,39.3],[48,38.8],[48.6,38.3],[48.9,38.3],[49.2,37.6],[50.1,37.4],[50.8,36.9],[52.3,36.7],[53.8,37],[53.9,37.2]]]},"id":"250"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[45.4,36],[46.1,35.7],[46.2,35.1],[45.6,34.7],[45.4,34],[46.1,33],[47.3,32.5],[47.8,31.7],[47.7,31],[48,31],[48,30.5],[48.6,29.9],[48,30],[47.3,30.1],[46.6,29.1],[44.7,29.2],[41.9,31.2],[40.4,31.9],[39.2,32.2],[38.8,33.4],[41,34.4],[41.4,35.6],[41.3,36.4],[41.8,36.6],[42.3,37.2],[42.8,37.4],[43.9,37.3],[44.3,37],[44.8,37.2],[45.4,36]]]},"id":"249"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-14.5,66.5],[-14.7,65.8],[-13.6,65.1],[-14.9,64.4],[-17.8,63.7],[-18.7,63.5],[-20,63.6],[-22.8,64],[-21.8,64.4],[-24,64.9],[-22.2,65.1],[-22.2,65.4],[-24.3,65.6],[-23.7,66.3],[-22.1,66.4],[-20.6,65.7],[-19.1,66.3],[-17.8,66],[-16.2,66.5],[-14.5,66.5]]]},"id":"252"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.7,32.7],[35.5,32.4],[35.2,32.5],[35,31.9],[35.2,31.8],[35,31.6],[34.9,31.4],[35.4,31.5],[35.4,31.1],[34.9,29.5],[34.3,31.2],[34.6,31.5],[34.5,31.6],[34.8,32.1],[35,32.8],[35.1,33.1],[35.1,33.1],[35.5,33.1],[35.6,33.3],[35.8,33.3],[35.8,32.9],[35.7,32.7],[35.7,32.7]]]},"id":"253"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[15.5,38.2],[15.2,37.4],[15.3,37.1],[15.1,36.6],[14.3,37],[13.8,37.1],[12.4,37.6],[12.6,38.1],[13.7,38],[14.8,38.1],[15.5,38.2]]],[[[9.2,41.2],[9.8,40.5],[9.7,39.2],[9.2,39.2],[8.8,38.9],[8.4,39.2],[8.4,40.4],[8.2,41],[8.7,40.9],[9.2,41.2]]],[[[12.4,46.8],[13.8,46.5],[13.7,46],[13.9,45.6],[13.1,45.7],[12.3,45.4],[12.4,44.9],[12.3,44.6],[12.6,44.1],[13.5,43.6],[14,42.8],[15.1,42],[15.9,42],[16.2,41.7],[15.9,41.5],[16.8,41.2],[17.5,40.9],[18.4,40.4],[18.5,40.2],[18.3,39.8],[17.7,40.3],[16.9,40.4],[16.4,39.8],[17.2,39.4],[17.1,38.9],[16.6,38.8],[16.1,38],[15.7,37.9],[15.7,38.2],[15.9,38.8],[16.1,39],[15.7,39.5],[15.4,40],[15,40.2],[14.7,40.6],[14.1,40.8],[13.6,41.2],[12.9,41.3],[12.1,41.7],[11.2,42.4],[10.5,42.9],[10.2,43.9],[9.7,44],[8.9,44.4],[8.4,44.2],[7.9,43.8],[7.4,43.7],[7.5,44.1],[7,44.3],[6.7,45],[7.1,45.3],[6.8,45.7],[6.8,46],[7.3,45.8],[7.8,45.8],[8.3,46.2],[8.5,46],[9,46],[9.2,46.4],[9.9,46.3],[10.4,46.5],[10.4,46.9],[11,46.8],[11.2,46.9],[12.2,47.1],[12.4,46.8]]]]},"id":"254"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.6,18.5],[-76.9,18.4],[-76.4,18.2],[-76.2,17.9],[-76.9,17.9],[-77.2,17.7],[-77.8,17.9],[-78.3,18.2],[-78.2,18.5],[-77.8,18.5],[-77.6,18.5]]]},"id":"255"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.5,32.4],[35.7,32.7],[36.8,32.3],[38.8,33.4],[39.2,32.2],[39,32],[37,31.5],[38,30.5],[37.7,30.3],[37.5,30],[36.7,29.9],[36.5,29.5],[36.1,29.2],[35,29.4],[34.9,29.5],[35.4,31.1],[35.4,31.5],[35.5,31.8],[35.5,32.4]]]},"id":"257"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[134.6,34.1],[134.8,33.8],[134.2,33.2],[133.8,33.5],[133.3,33.3],[133,32.7],[132.4,33],[132.4,33.5],[132.9,34.1],[133.5,33.9],[133.9,34.4],[134.6,34.1]]],[[[141,37.1],[140.6,36.3],[140.8,35.8],[140.3,35.1],[139,34.7],[137.2,34.6],[135.8,33.5],[135.1,33.8],[135.1,34.6],[133.3,34.4],[132.2,33.9],[131,33.9],[132,33.1],[131.3,31.5],[130.7,31],[130.2,31.4],[130.4,32.3],[129.8,32.6],[129.4,33.3],[130.4,33.6],[130.9,34.2],[131.9,34.7],[132.6,35.4],[134.6,35.7],[135.7,35.5],[136.7,37.3],[137.4,36.8],[138.9,37.8],[139.4,38.2],[140.1,39.4],[139.9,40.6],[140.3,41.2],[141.4,41.4],[141.9,40],[141.9,39.2],[141,38.2],[141,37.1]]],[[[143.9,44.2],[144.6,44],[145.3,44.4],[145.5,43.3],[144.1,43],[143.2,42],[141.6,42.7],[141.1,41.6],[140,41.6],[139.8,42.6],[140.3,43.3],[141.4,43.4],[141.7,44.8],[142,45.6],[143.1,44.5],[143.9,44.2]]]]},"id":"256"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[71,42.3],[70.4,42.1],[69.1,41.4],[68.6,40.7],[68.3,40.7],[68,41.1],[66.7,41.2],[66.5,42],[66,42],[66.1,43],[64.9,43.7],[63.2,43.7],[62,43.5],[61.1,44.4],[60.2,44.8],[58.7,45.5],[58.5,45.6],[55.9,45],[56,41.3],[55.5,41.3],[54.8,42],[54.1,42.3],[52.9,42.1],[52.5,41.8],[52.4,42],[52.7,42.4],[52.5,42.8],[51.3,43.1],[50.9,44],[50.3,44.3],[50.3,44.6],[51.3,44.5],[51.3,45.2],[52.2,45.4],[53,45.3],[53.2,46.2],[53,46.9],[52,46.8],[51.2,47],[50,46.6],[49.1,46.4],[48.6,46.6],[48.7,47.1],[48.1,47.7],[47.3,47.7],[46.5,48.4],[47,49.2],[46.8,49.4],[47.5,50.5],[48.6,49.9],[48.7,50.6],[50.8,51.7],[52.3,51.7],[54.5,51],[55.7,50.6],[56.8,51],[58.4,51.1],[59.6,50.5],[59.9,50.8],[61.3,50.8],[61.6,51.3],[60,52],[60.9,52.4],[60.7,52.7],[61.7,53],[61,53.7],[61.4,54],[65.2,54.4],[65.7,54.6],[68.2,55],[69.1,55.4],[70.9,55.2],[71.2,54.1],[72.2,54.4],[73.5,54],[73.4,53.5],[74.4,53.5],[76.9,54.5],[76.5,54.2],[77.8,53.4],[80,50.9],[80.6,51.4],[81.9,50.8],[83.4,51.1],[83.9,50.9],[84.4,50.3],[85.1,50.1],[85.5,49.7],[86.8,49.8],[87.4,49.2],[86.6,48.5],[85.8,48.5],[85.7,47.5],[85.2,47],[83.2,47.3],[82.5,45.5],[81.9,45.3],[80,44.9],[80.9,43.2],[80.2,42.9],[80.3,42.3],[79.6,42.5],[79.1,42.9],[77.7,43],[76,43],[75.6,42.9],[74.2,43.3],[73.6,43.1],[73.5,42.5],[71.8,42.8],[71.2,42.7],[71,42.3]]]},"id":"261"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[41,-0.9],[41.6,-1.7],[40.9,-2.1],[40.6,-2.5],[40.3,-2.6],[40.1,-3.3],[39.8,-3.7],[39.6,-4.3],[39.2,-4.7],[37.8,-3.7],[37.7,-3.1],[34.1,-1.1],[33.9,-1],[33.9,0.1],[34.2,0.5],[34.7,1.2],[35,1.9],[34.6,3.1],[34.5,3.6],[34,4.2],[34.6,4.8],[35.3,5.5],[35.8,5.3],[35.8,4.8],[36.2,4.4],[36.9,4.4],[38.1,3.6],[38.4,3.6],[38.7,3.6],[38.9,3.5],[39.6,3.4],[39.9,3.8],[40.8,4.3],[41.2,3.9],[41.9,3.9],[41,2.8],[41,-0.9]]]},"id":"262"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[71,42.3],[71.2,42.7],[71.8,42.8],[73.5,42.5],[73.6,43.1],[74.2,43.3],[75.6,42.9],[76,43],[77.7,43],[79.1,42.9],[79.6,42.5],[80.3,42.3],[80.1,42.1],[78.5,41.6],[78.2,41.2],[76.9,41.1],[76.5,40.4],[75.5,40.6],[74.8,40.4],[73.8,39.9],[74,39.7],[73.7,39.4],[71.8,39.3],[70.5,39.6],[69.5,39.5],[69.6,40.1],[70.6,39.9],[71,40.2],[71.8,40.1],[73.1,40.9],[71.9,41.4],[71.2,41.1],[70.4,41.5],[71.3,42.2],[71,42.3]]]},"id":"264"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[103.5,10.6],[103.1,11.2],[102.6,12.2],[102.3,13.4],[103,14.2],[104.3,14.4],[105.2,14.3],[106,13.9],[106.5,14.6],[107.4,14.2],[107.6,13.5],[107.5,12.3],[105.8,11.6],[106.2,11],[105.2,10.9],[104.3,10.5],[103.5,10.6]]]},"id":"258"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[128.3,38.6],[129.2,37.4],[129.5,36.8],[129.5,35.6],[129.1,35.1],[128.2,34.9],[127.4,34.5],[126.5,34.4],[126.4,34.9],[126.6,35.7],[126.1,36.7],[126.9,36.9],[126.2,37.7],[126.2,37.8],[126.7,37.8],[127.1,38.3],[127.8,38.3],[128.2,38.4],[128.3,38.6]]]},"id":"345"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.8,42.1],[20.7,41.8],[20.6,41.9],[20.5,42.2],[20.3,42.3],[20.1,42.6],[20.3,42.8],[20.5,42.9],[20.6,43.2],[20.8,43.3],[21,43.1],[21.1,43.1],[21.3,42.9],[21.4,42.9],[21.6,42.7],[21.8,42.7],[21.7,42.4],[21.5,42.3],[21.6,42.2],[21.4,42.2],[20.8,42.1]]]},"id":"12244"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[48,30],[48.2,29.5],[48.1,29.3],[48.4,28.6],[47.7,28.5],[47.5,29],[46.6,29.1],[47.3,30.1],[48,30]]]},"id":"270"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[105.2,14.3],[105.5,14.7],[105.6,15.6],[104.8,16.4],[104.7,17.4],[104,18.2],[103.2,18.3],[103,18],[102.4,17.9],[102.1,18.1],[101.1,17.5],[101,18.4],[101.3,19.5],[100.6,19.5],[100.5,20.1],[100.1,20.4],[100.3,20.8],[101.2,21.4],[101.3,21.2],[101.8,21.2],[101.7,22.3],[102.2,22.5],[102.8,21.7],[103.2,20.8],[104.4,20.8],[104.8,19.9],[104.2,19.6],[103.9,19.3],[105.1,18.7],[105.9,17.5],[106.6,16.6],[107.3,15.9],[107.6,15.2],[107.4,14.2],[106.5,14.6],[106,13.9],[105.2,14.3]]]},"id":"272"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.8,33.3],[35.6,33.3],[35.5,33.1],[35.1,33.1],[35.5,33.9],[36,34.6],[36,34.6],[36.4,34.6],[36.6,34.2],[36.1,33.8],[35.8,33.3]]]},"id":"275"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-7.7,4.4],[-8,4.4],[-9,4.8],[-9.9,5.6],[-10.8,6.1],[-11.4,6.8],[-11.2,7.1],[-11.1,7.4],[-10.7,7.9],[-10.2,8.4],[-10,8.4],[-9.8,8.5],[-9.3,7.9],[-9.4,7.5],[-9.2,7.3],[-8.9,7.3],[-8.7,7.7],[-8.4,7.7],[-8.5,7.4],[-8.4,6.9],[-8.6,6.5],[-8.3,6.2],[-8,6.1],[-7.6,5.7],[-7.5,5.3],[-7.6,5.2],[-7.7,4.4]]]},"id":"276"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[14.9,22.9],[14.1,22.5],[13.6,23],[12,23.5],[11.6,24.1],[10.8,24.6],[10.3,24.4],[9.9,24.9],[9.9,25.4],[9.3,26.1],[9.7,26.5],[9.6,27.1],[9.8,27.7],[9.7,28.1],[9.9,29],[9.8,29.4],[9.5,30.3],[10,30.5],[10.1,31],[10,31.4],[10.6,31.8],[10.9,32.1],[11.4,32.4],[11.5,33.1],[12.7,32.8],[13.1,32.9],[13.9,32.7],[15.2,32.3],[15.7,31.4],[16.6,31.2],[18,30.8],[19.1,30.3],[19.6,30.5],[20.1,31],[19.8,31.8],[20.1,32.2],[20.9,32.7],[21.5,32.8],[22.9,32.6],[23.2,32.2],[23.6,32.2],[23.9,32],[24.9,31.9],[25.2,31.6],[24.8,31.1],[25,30.7],[24.7,30],[25,29.2],[25,25.7],[25,22],[25,20],[23.9,20],[23.8,19.6],[19.8,21.5],[15.9,23.4],[14.9,22.9]]]},"id":"277"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[81.8,7.5],[81.6,6.5],[81.2,6.2],[80.3,6],[79.9,6.8],[79.7,8.2],[80.1,9.8],[80.8,9.3],[81.3,8.6],[81.8,7.5]]]},"id":"337"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[29,-29],[29.3,-29.3],[29,-29.7],[28.8,-30.1],[28.3,-30.2],[28.1,-30.5],[27.7,-30.6],[27,-29.9],[27.5,-29.2],[28.1,-28.9],[28.5,-28.6],[29,-29]]]},"id":"274"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.7,54.3],[22.7,54.6],[22.8,54.9],[22.3,55],[21.3,55.2],[21.1,56],[22.2,56.3],[23.9,56.3],[24.9,56.4],[25,56.2],[25.5,56.1],[26.5,55.6],[26.6,55.2],[25.8,54.8],[25.5,54.3],[24.5,53.9],[23.5,53.9],[23.2,54.2],[22.7,54.3]]]},"id":"279"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[6,50.1],[6.2,49.9],[6.2,49.5],[5.9,49.4],[5.7,49.5],[5.8,50.1],[6,50.1]]]},"id":"280"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[21.1,56],[21.1,56.8],[21.6,57.4],[22.5,57.8],[23.3,57],[24.1,57],[24.3,57.8],[25.2,58],[25.6,57.8],[26.5,57.5],[27.3,57.5],[27.8,57.2],[27.9,56.8],[28.2,56.2],[27.1,55.8],[26.5,55.6],[25.5,56.1],[25,56.2],[24.9,56.4],[23.9,56.3],[22.2,56.3],[21.1,56]]]},"id":"273"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-8.8,27.7],[-13.1,27.7],[-12.6,28],[-11.7,28.1],[-10.9,28.8],[-10.4,29.1],[-9.6,29.9],[-9.8,31.2],[-9.4,32],[-9.3,32.6],[-8.7,33.2],[-7.7,33.7],[-6.9,34.1],[-6.2,35.1],[-5.9,35.8],[-5.2,35.8],[-4.6,35.3],[-3.6,35.4],[-2.6,35.2],[-2.2,35.2],[-1.8,34.5],[-1.7,33.9],[-1.4,32.9],[-1.1,32.7],[-1.3,32.3],[-2.6,32.1],[-3.1,31.7],[-3.6,31.6],[-3.7,30.9],[-4.9,30.5],[-5.2,30],[-6.1,29.7],[-7.1,29.6],[-8.7,28.8],[-8.7,27.7],[-8.8,27.7],[-8.8,27.7]]]},"id":"288"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[26.6,48.2],[26.9,48.4],[27.5,48.5],[28.3,48.2],[28.7,48.1],[29.1,47.8],[29.1,47.5],[29.4,47.3],[29.6,46.9],[29.9,46.7],[29.8,46.5],[30,46.4],[29.8,46.3],[29.2,46.4],[29.1,46.5],[28.9,46.4],[28.9,46.3],[28.7,45.9],[28.5,45.6],[28.2,45.5],[28.1,45.9],[28.2,46.4],[28.1,46.8],[27.6,47.4],[27.2,47.8],[26.9,48.1],[26.6,48.2]]]},"id":"294"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[49.5,-12.5],[49.8,-12.9],[50.1,-13.6],[50.2,-14.8],[50.5,-15.2],[50.4,-15.7],[50.2,-16],[49.9,-15.4],[49.7,-15.7],[49.9,-16.5],[49.8,-16.9],[49.5,-17.1],[49.4,-18],[49,-19.1],[48.5,-20.5],[47.9,-22.4],[47.5,-23.8],[47.1,-24.9],[46.3,-25.2],[45.4,-25.6],[44.8,-25.3],[44,-25],[43.8,-24.5],[43.7,-23.6],[43.3,-22.8],[43.3,-22.1],[43.4,-21.3],[43.9,-21.2],[43.9,-20.8],[44.4,-20.1],[44.5,-19.4],[44.2,-19],[44,-18.3],[44,-17.4],[44.3,-16.9],[44.4,-16.2],[44.9,-16.2],[45.5,-16],[45.9,-15.8],[46.3,-15.8],[46.9,-15.2],[47.7,-14.6],[48,-14.1],[47.9,-13.7],[48.3,-13.8],[48.8,-13.1],[48.9,-12.5],[49.2,-12],[49.5,-12.5]]]},"id":"281"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-97.1,25.9],[-97.5,25],[-97.7,24.3],[-97.8,22.9],[-97.9,22.4],[-97.7,21.9],[-97.4,21.4],[-97.2,20.6],[-96.5,19.9],[-96.3,19.3],[-95.9,18.8],[-94.8,18.6],[-94.4,18.1],[-93.5,18.4],[-92.8,18.5],[-92,18.7],[-91.4,18.9],[-90.8,19.3],[-90.5,19.9],[-90.5,20.7],[-90.3,21],[-89.6,21.3],[-88.5,21.5],[-87.7,21.5],[-87.1,21.5],[-86.8,21.3],[-86.8,20.8],[-87.4,20.3],[-87.6,19.6],[-87.4,19.5],[-87.6,19],[-87.8,18.3],[-88.1,18.5],[-88.3,18.5],[-88.5,18.5],[-88.8,17.9],[-89,18],[-89.2,18],[-89.1,17.8],[-90.1,17.8],[-91,17.8],[-91,17.3],[-91.5,17.3],[-91.1,16.9],[-90.7,16.7],[-90.6,16.5],[-90.4,16.4],[-90.5,16.1],[-91.7,16.1],[-92.2,15.3],[-92.1,15.1],[-92.2,14.8],[-92.2,14.5],[-93.4,15.6],[-93.9,15.9],[-94.7,16.2],[-95.3,16.1],[-96.1,15.8],[-96.6,15.7],[-97.3,15.9],[-98,16.1],[-98.9,16.6],[-99.7,16.7],[-100.8,17.2],[-101.7,17.6],[-101.9,17.9],[-102.5,18],[-103.5,18.3],[-103.9,18.7],[-105,19.3],[-105.5,19.9],[-105.7,20.4],[-105.4,20.5],[-105.5,20.8],[-105.3,21.1],[-105.3,21.4],[-105.6,21.9],[-105.7,22.3],[-106,22.8],[-106.9,23.8],[-107.9,24.5],[-108.4,25.2],[-109.3,25.6],[-109.4,25.8],[-109.3,26.4],[-109.8,26.7],[-110.4,27.2],[-110.6,27.9],[-111.2,27.9],[-111.8,28.5],[-112.2,29],[-112.3,29.3],[-112.8,30],[-113.2,30.8],[-113.1,31.2],[-113.9,31.6],[-114.2,31.5],[-114.8,31.8],[-114.9,31.4],[-114.8,30.9],[-114.7,30.2],[-114.3,29.8],[-113.6,29.1],[-113.4,28.8],[-113.3,28.8],[-113.1,28.4],[-113,28.4],[-112.8,27.8],[-112.5,27.5],[-112.2,27.2],[-111.6,26.7],[-111.3,25.7],[-111,25.3],[-110.7,24.8],[-110.7,24.3],[-110.2,24.3],[-109.8,23.8],[-109.4,23.4],[-109.4,23.2],[-109.9,22.8],[-110,22.8],[-110.3,23.4],[-110.9,24],[-111.7,24.5],[-112.2,24.7],[-112.1,25.5],[-112.3,26],[-112.8,26.3],[-113.5,26.8],[-113.6,26.6],[-113.8,26.9],[-114.5,27.1],[-115.1,27.7],[-115,27.8],[-114.6,27.7],[-114.2,28.1],[-114.2,28.6],[-114.9,29.3],[-115.5,29.6],[-115.9,30.2],[-116.3,30.8],[-116.7,31.6],[-117.1,32.5],[-116,32.6],[-114.7,32.7],[-114.8,32.5],[-113.3,32],[-111,31.3],[-109,31.3],[-108.2,31.3],[-108.2,31.8],[-106.5,31.8],[-106.1,31.4],[-105.6,31.1],[-105,30.6],[-104.7,30.1],[-104.5,29.6],[-103.9,29.3],[-103.1,29],[-102.5,29.8],[-101.7,29.8],[-101,29.4],[-100.5,28.7],[-100.1,28.1],[-99.5,27.5],[-99.3,26.8],[-99,26.4],[-98.2,26.1],[-97.5,25.8],[-97.1,25.9]]]},"id":"292"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.6,41.9],[20.7,41.8],[20.8,42.1],[21.4,42.2],[21.6,42.2],[21.9,42.3],[22.4,42.3],[22.9,42],[23,41.3],[22.8,41.3],[22.6,41.1],[22.1,41.1],[21.7,40.9],[21,40.8],[20.6,41.1],[20.5,41.5],[20.6,41.9]]]},"id":"282"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-12.2,14.6],[-11.8,14.8],[-11.7,15.4],[-11.3,15.4],[-10.7,15.1],[-10.1,15.3],[-9.7,15.3],[-9.6,15.5],[-5.5,15.5],[-5.3,16.2],[-5.5,16.3],[-6,20.6],[-6.5,25],[-4.9,25],[-1.6,22.8],[1.8,20.6],[2.1,20.1],[2.7,19.9],[3.1,19.7],[3.2,19.1],[4.3,19.2],[4.3,16.9],[3.7,16.2],[3.6,15.6],[2.7,15.4],[1.4,15.3],[1,15],[0.4,14.9],[-0.3,14.9],[-0.5,15.1],[-1.1,15],[-2,14.6],[-2.2,14.2],[-3,13.8],[-3.1,13.5],[-3.5,13.3],[-4,13.5],[-4.3,13.2],[-4.4,12.5],[-5.2,11.7],[-5.2,11.4],[-5.5,11],[-5.4,10.4],[-5.8,10.2],[-6.1,10.1],[-6.2,10.5],[-6.5,10.4],[-6.7,10.4],[-6.9,10.1],[-7.6,10.1],[-7.9,10.3],[-8,10.2],[-8.3,10.5],[-8.3,10.8],[-8.4,10.9],[-8.6,10.8],[-8.6,11.1],[-8.4,11.4],[-8.8,11.8],[-8.9,12.1],[-9.1,12.3],[-9.3,12.3],[-9.6,12.2],[-9.9,12.1],[-10.2,11.8],[-10.6,11.9],[-10.9,12.2],[-11,12.2],[-11.3,12.1],[-11.5,12.1],[-11.5,12.4],[-11.5,12.8],[-11.6,13.1],[-11.9,13.4],[-12.1,14],[-12.2,14.6]]]},"id":"286"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[99.5,20.2],[99,19.8],[98.3,19.7],[97.8,18.6],[97.4,18.4],[97.9,17.6],[98.5,16.8],[98.9,16.2],[98.5,15.3],[98.2,15.1],[98.4,14.6],[99.1,13.8],[99.2,13.3],[99.2,12.8],[99.6,11.9],[99,11],[98.6,9.9],[98.5,10.7],[98.8,11.4],[98.4,12],[98.5,13.1],[98.1,13.6],[97.8,14.8],[97.6,16.1],[97.2,16.9],[96.5,16.4],[95.4,15.7],[94.8,15.8],[94.2,16],[94.5,17.3],[94.3,18.2],[93.5,19.4],[93.7,19.7],[93.1,19.9],[92.4,20.7],[92.3,21.5],[92.7,21.3],[92.7,22],[93.2,22.3],[93.1,22.7],[93.3,23],[93.3,24.1],[94.1,23.9],[94.6,24.7],[94.6,25.2],[95.2,26],[95.1,26.6],[96.4,27.3],[97.1,27.1],[97.1,27.7],[97.4,27.9],[97.3,28.3],[97.9,28.3],[98.2,27.7],[98.7,27.5],[98.7,26.7],[98.7,25.9],[97.7,25.1],[97.6,23.9],[98.7,24.1],[98.9,23.1],[99.5,22.9],[99.2,22.1],[100,21.7],[100.4,21.6],[101.2,21.8],[101.2,21.4],[100.3,20.8],[100.1,20.4],[99.5,20.2]]]},"id":"298"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[19.8,42.5],[19.7,42.7],[19.3,42.2],[19.4,41.9],[19.2,42],[18.9,42.3],[18.5,42.5],[18.6,42.6],[18.7,43.2],[19,43.4],[19.2,43.5],[19.5,43.4],[19.6,43.2],[20,43.1],[20.3,42.9],[20.3,42.8],[20.1,42.6],[19.8,42.5]]]},"id":"934"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[87.8,49.3],[88.8,49.5],[90.7,50.3],[92.2,50.8],[93.1,50.5],[94.1,50.5],[94.8,50],[95.8,50],[97.3,49.7],[98.2,50.4],[97.8,51],[98.9,52],[100,51.6],[100.9,51.5],[102.1,51.3],[102.3,50.5],[103.7,50.1],[104.6,50.3],[105.9,50.4],[106.9,50.3],[107.9,49.8],[108.5,49.3],[109.4,49.3],[110.7,49.1],[111.6,49.4],[112.9,49.5],[114.4,50.2],[115,50.1],[115.5,49.8],[116.7,49.9],[116.2,49.1],[115.5,48.1],[115.7,47.7],[116.3,47.9],[117.3,47.7],[118.1,48.1],[118.9,47.7],[119.8,47],[119.7,46.7],[118.9,46.8],[117.4,46.7],[116.7,46.4],[116,45.7],[114.5,45.3],[113.5,44.8],[112.4,45],[111.9,45.1],[111.3,44.5],[111.7,44.1],[111.8,43.7],[111.1,43.4],[110.4,42.9],[109.2,42.5],[107.7,42.5],[106.1,42.1],[105,41.6],[104.5,41.9],[103.3,41.9],[101.8,42.5],[100.8,42.7],[99.5,42.5],[97.5,42.7],[96.3,42.7],[95.8,43.3],[95.3,44.2],[94.7,44.4],[93.5,45],[92.1,45.1],[90.9,45.3],[90.6,45.7],[91,46.9],[90.3,47.7],[88.9,48.1],[88,48.6],[87.8,49.3]]]},"id":"296"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34.6,-11.5],[35.3,-11.4],[36.5,-11.7],[36.8,-11.6],[37.5,-11.6],[37.8,-11.3],[38.4,-11.3],[39.5,-10.9],[40.3,-10.3],[40.5,-10.8],[40.4,-11.8],[40.6,-12.6],[40.6,-14.2],[40.8,-14.7],[40.5,-15.4],[40.1,-16.1],[39.5,-16.7],[38.5,-17.1],[37.4,-17.6],[36.3,-18.7],[35.9,-18.8],[35.2,-19.6],[34.8,-19.8],[34.7,-20.5],[35.2,-21.3],[35.4,-21.8],[35.4,-22.1],[35.6,-22.1],[35.5,-23.1],[35.4,-23.5],[35.6,-23.7],[35.5,-24.1],[35,-24.5],[34.2,-24.8],[33,-25.4],[32.6,-25.7],[32.7,-26.1],[32.9,-26.2],[32.8,-26.7],[32.1,-26.7],[32,-26.3],[31.8,-25.8],[31.8,-25.5],[31.9,-24.4],[31.7,-23.7],[31.2,-22.3],[32.2,-21.1],[32.5,-20.4],[32.7,-20.3],[32.8,-19.7],[32.6,-19.4],[32.7,-18.7],[32.8,-18],[32.8,-16.7],[32.3,-16.4],[31.9,-16.3],[31.6,-16.1],[31.2,-15.9],[30.3,-15.9],[30.3,-15.5],[30.2,-14.8],[33.2,-14],[33.8,-14.5],[34.1,-14.4],[34.5,-14.6],[34.5,-15],[34.3,-15.5],[34.4,-16.2],[35,-16.8],[35.3,-16.1],[35.8,-15.9],[35.7,-14.6],[35.3,-13.9],[34.9,-13.6],[34.6,-13.6],[34.3,-12.3],[34.6,-11.5]]]},"id":"297"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-12.2,14.6],[-12.8,15.3],[-13.4,16],[-14.1,16.3],[-14.6,16.6],[-15.1,16.6],[-15.6,16.4],[-16.1,16.5],[-16.5,16.1],[-16.5,16.7],[-16.3,17.2],[-16.1,18.1],[-16.3,19.1],[-16.4,19.6],[-16.3,20.1],[-16.5,20.6],[-17.1,21],[-16.8,21.3],[-12.9,21.3],[-13.1,22.8],[-12.9,23.3],[-11.9,23.4],[-12,25.9],[-8.7,25.9],[-8.7,27.4],[-4.9,25],[-6.5,25],[-6,20.6],[-5.5,16.3],[-5.3,16.2],[-5.5,15.5],[-9.6,15.5],[-9.7,15.3],[-10.1,15.3],[-10.7,15.1],[-11.3,15.4],[-11.7,15.4],[-11.8,14.8],[-12.2,14.6]]]},"id":"290"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34.6,-11.5],[34.3,-12.3],[34.6,-13.6],[34.9,-13.6],[35.3,-13.9],[35.7,-14.6],[35.8,-15.9],[35.3,-16.1],[35,-16.8],[34.4,-16.2],[34.3,-15.5],[34.5,-15],[34.5,-14.6],[34.1,-14.4],[33.8,-14.5],[33.2,-14],[32.7,-13.7],[33,-12.8],[33.3,-12.4],[33.1,-11.6],[33.3,-10.8],[33.5,-10.5],[33.2,-9.7],[32.8,-9.2],[33.7,-9.4],[33.9,-9.7],[34.3,-10.2],[34.6,-11.5]]]},"id":"283"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[101.1,6.2],[101.2,5.7],[101.8,5.8],[102.1,6.2],[102.4,6.1],[103,5.5],[103.4,4.9],[103.4,4.2],[103.3,3.7],[103.4,3.4],[103.5,2.8],[103.9,2.5],[104.2,1.6],[104.2,1.3],[103.5,1.2],[102.6,2],[101.4,2.8],[101.3,3.3],[100.7,3.9],[100.6,4.8],[100.2,5.3],[100.3,6],[100.1,6.5],[100.3,6.6],[101.1,6.2]]],[[[118.6,4.5],[117.9,4.1],[117,4.3],[115.9,4.3],[115.5,3.2],[115.1,2.8],[114.6,1.4],[113.8,1.2],[112.9,1.5],[112.4,1.4],[111.8,0.9],[111.2,1],[110.5,0.8],[109.8,1.3],[109.7,2],[110.4,1.7],[111.2,1.9],[111.4,2.7],[111.8,2.9],[113,3.1],[113.7,3.9],[114.2,4.5],[114.7,4],[114.9,4.3],[115.3,4.3],[115.4,5],[115.5,5.4],[116.2,6.1],[116.7,6.9],[117.1,6.9],[117.6,6.4],[117.7,6],[118.3,5.7],[119.2,5.4],[119.1,5],[118.4,5],[118.6,4.5]]]]},"id":"284"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[16.3,-28.6],[15.6,-27.8],[15.2,-27.1],[15,-26.1],[14.7,-25.4],[14.4,-23.9],[14.4,-22.7],[14.3,-22.1],[13.9,-21.7],[13.4,-20.9],[12.8,-19.7],[12.6,-19],[11.8,-18.1],[11.7,-17.3],[12.2,-17.1],[12.8,-16.9],[13.5,-17],[14.1,-17.4],[14.2,-17.4],[18.3,-17.3],[19,-17.8],[21.4,-17.9],[23.2,-17.5],[24,-17.3],[24.7,-17.4],[25.1,-17.6],[25.1,-17.7],[24.5,-17.9],[24.2,-17.9],[23.6,-18.3],[23.2,-17.9],[21.7,-18.2],[20.9,-18.3],[20.9,-21.8],[19.9,-21.8],[19.9,-24.8],[19.9,-28.5],[19,-29],[18.5,-29],[17.8,-28.9],[17.4,-28.8],[17.2,-28.4],[16.8,-28.1],[16.3,-28.6]]]},"id":"118"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[165.8,-21.1],[166.6,-21.7],[167.1,-22.2],[166.7,-22.4],[166.2,-22.1],[165.5,-21.7],[164.8,-21.1],[164.2,-20.4],[164,-20.1],[164.5,-20.1],[165,-20.5],[165.5,-20.8],[165.8,-21.1]]]}},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[2.2,11.9],[2.2,12.6],[1,12.9],[1,13.3],[0.4,14],[0.3,14.4],[0.4,14.9],[1,15],[1.4,15.3],[2.7,15.4],[3.6,15.6],[3.7,16.2],[4.3,16.9],[4.3,19.2],[5.7,19.6],[8.6,21.6],[12,23.5],[13.6,23],[14.1,22.5],[14.9,22.9],[15.1,21.3],[15.5,21],[15.5,20.7],[15.9,20.4],[15.7,20],[15.3,17.9],[15.2,16.6],[14,15.7],[13.5,14.4],[14,14],[14,13.4],[14.6,13.3],[14.5,12.9],[14.2,12.8],[14.2,12.5],[14,12.5],[13.3,13.6],[13.1,13.6],[12.3,13],[11.5,13.3],[11,13.4],[10.7,13.2],[10.1,13.3],[9.5,12.9],[9,12.8],[7.8,13.3],[7.3,13.1],[6.8,13.1],[6.4,13.5],[5.4,13.9],[4.4,13.7],[4.1,13.5],[4,13],[3.7,12.6],[3.6,11.7],[2.8,12.2],[2.5,12.2],[2.2,11.9]]]},"id":"303"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[8.5,4.8],[7.5,4.4],[7.1,4.5],[6.7,4.2],[5.9,4.3],[5.4,4.9],[5,5.6],[4.3,6.3],[3.6,6.3],[2.7,6.3],[2.7,7.9],[2.7,8.5],[2.9,9.1],[3.2,9.4],[3.7,10.1],[3.6,10.3],[3.8,10.7],[3.6,11.3],[3.6,11.7],[3.7,12.6],[4,13],[4.1,13.5],[4.4,13.7],[5.4,13.9],[6.4,13.5],[6.8,13.1],[7.3,13.1],[7.8,13.3],[9,12.8],[9.5,12.9],[10.1,13.3],[10.7,13.2],[11,13.4],[11.5,13.3],[12.3,13],[13.1,13.6],[13.3,13.6],[14,12.5],[14.2,12.5],[14.6,12.1],[14.5,11.9],[14.4,11.6],[13.6,10.8],[13.3,10.2],[13.2,9.6],[13,9.4],[12.8,8.7],[12.2,8.3],[12.1,7.8],[11.8,7.4],[11.7,7],[11.1,6.6],[10.5,7.1],[10.1,7],[9.5,6.5],[9.2,6.4],[8.8,5.5],[8.5,4.8]]]},"id":"304"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-85.7,11.1],[-86.1,11.4],[-86.5,11.8],[-86.7,12.1],[-87.2,12.5],[-87.7,12.9],[-87.6,13.1],[-87.4,12.9],[-87.3,13],[-87,13],[-86.9,13.3],[-86.7,13.3],[-86.8,13.8],[-86.5,13.8],[-86.3,13.8],[-86.1,14],[-85.8,13.8],[-85.7,14],[-85.5,14.1],[-85.2,14.4],[-85.1,14.6],[-85.1,14.6],[-84.9,14.8],[-84.8,14.8],[-84.6,14.7],[-84.4,14.6],[-84.2,14.7],[-84,14.7],[-83.6,14.9],[-83.5,15],[-83.1,15],[-83.2,14.9],[-83.3,14.7],[-83.2,14.3],[-83.4,14],[-83.5,13.6],[-83.6,13.1],[-83.5,12.9],[-83.5,12.4],[-83.6,12.3],[-83.7,11.9],[-83.7,11.6],[-83.9,11.4],[-83.8,11.1],[-83.7,10.9],[-83.9,10.7],[-84.2,10.8],[-84.4,11],[-84.7,11.1],[-84.9,11],[-85.6,11.2],[-85.7,11.1]]]},"id":"302"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[6.1,53.5],[6.9,53.5],[7.1,53.1],[6.8,52.2],[6.6,51.9],[6,51.9],[6.2,50.8],[5.6,51],[5,51.5],[4,51.3],[3.3,51.3],[3.8,51.6],[4.7,53.1],[6.1,53.5]]]},"id":"300"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[28.2,71.2],[31.3,70.5],[30,70.2],[31.1,69.6],[29.4,69.2],[28.6,69.1],[29,69.8],[27.7,70.2],[26.2,69.8],[25.7,69.1],[24.7,68.6],[23.7,68.9],[22.4,68.8],[21.2,69.4],[20.6,69.1],[20,69.1],[19.9,68.4],[18,68.6],[17.7,68],[16.8,68],[16.1,67.3],[15.1,66.2],[13.6,64.8],[13.9,64.4],[13.6,64],[12.6,64.1],[11.9,63.1],[12,61.8],[12.6,61.3],[12.3,60.1],[11.5,59.4],[11,58.9],[10.4,59.5],[8.4,58.3],[7,58.1],[5.7,58.6],[5.3,59.7],[5,62],[5.9,62.6],[8.6,63.5],[10.5,64.5],[12.4,65.9],[14.8,67.8],[16.4,68.6],[19.2,69.8],[21.4,70.3],[23,70.2],[24.5,71],[26.4,71],[28.2,71.2]]],[[[24.7,77.9],[22.5,77.4],[20.7,77.7],[21.4,77.9],[20.8,78.3],[22.9,78.5],[23.3,78.1],[24.7,77.9]]],[[[18.3,79.7],[21.5,79],[19,78.6],[18.5,77.8],[17.6,77.6],[17.1,76.8],[15.9,76.8],[13.8,77.4],[14.7,77.7],[13.2,78],[11.2,78.9],[10.4,79.7],[13.2,80],[13.7,79.7],[15.1,79.7],[15.5,80],[17,80.1],[18.3,79.7]]],[[[25.4,80.4],[27.4,80.1],[25.9,79.5],[23,79.4],[20.1,79.6],[19.9,79.8],[18.5,79.9],[17.4,80.3],[20.5,80.6],[21.9,80.4],[22.9,80.7],[25.4,80.4]]]]},"id":"306"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[88.1,27.9],[88,27.4],[88.2,26.8],[88.1,26.4],[87.2,26.4],[86,26.6],[85.3,26.7],[84.7,27.2],[83.3,27.4],[82,27.9],[81.1,28.4],[80.1,28.8],[80.5,29.7],[81.1,30.2],[81.5,30.4],[82.3,30.1],[83.3,29.5],[83.9,29.3],[84.2,28.8],[85,28.6],[85.8,28.2],[87,28],[88.1,27.9]]]},"id":"187"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[173,-40.9],[173.2,-41.3],[174,-40.9],[174.2,-41.3],[174.2,-41.8],[173.9,-42.2],[173.2,-43],[172.7,-43.4],[173.1,-43.9],[172.3,-43.9],[171.5,-44.2],[171.2,-44.9],[170.6,-45.9],[169.8,-46.4],[169.3,-46.6],[168.4,-46.6],[167.8,-46.3],[166.7,-46.2],[166.5,-45.9],[167,-45.1],[168.3,-44.1],[168.9,-43.9],[169.7,-43.6],[170.5,-43],[171.1,-42.5],[171.6,-41.8],[171.9,-41.5],[172.1,-41],[172.8,-40.5],[173,-40.9]]],[[[174.6,-36.2],[175.3,-37.2],[175.4,-36.5],[175.8,-36.8],[176,-37.6],[176.8,-37.9],[177.4,-38],[178,-37.6],[178.5,-37.7],[178.3,-38.6],[178,-39.2],[177.2,-39.1],[176.9,-39.4],[177,-39.9],[176.9,-40.1],[176.5,-40.6],[176,-41.3],[175.2,-41.7],[175.1,-41.4],[174.7,-41.3],[175.2,-40.5],[174.9,-39.9],[173.8,-39.5],[173.9,-39.1],[174.6,-38.8],[174.7,-38],[174.7,-37.4],[174.3,-36.7],[174.3,-36.5],[173.8,-36.1],[173.1,-35.2],[172.6,-34.5],[173,-34.5],[173.6,-35],[174.3,-35.3],[174.6,-36.2]]]]},"id":"301"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[58.9,21.1],[58.5,20.4],[58,20.5],[57.8,20.2],[57.7,19.7],[57.8,19.1],[57.7,18.9],[57.2,18.9],[56.6,18.6],[56.5,18.1],[56.3,17.9],[55.7,17.9],[55.3,17.6],[55.3,17.2],[54.8,17],[54.2,17],[53.6,16.7],[53.1,16.7],[52.8,17.3],[52,19],[55,20],[55.7,22],[55.2,22.7],[55.2,23.1],[55.5,23.5],[55.5,23.9],[56,24.1],[55.8,24.3],[55.9,24.9],[56.4,24.9],[56.8,24.2],[57.4,23.9],[58.1,23.7],[58.7,23.6],[59.2,23],[59.5,22.7],[59.8,22.5],[59.8,22.3],[59.4,21.7],[59.3,21.4],[58.9,21.1]]],[[[56.4,25.9],[56.3,25.7],[56.1,26.1],[56.4,26.4],[56.5,26.3],[56.4,25.9]]]]},"id":"307"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[75.2,37.1],[75.9,36.7],[76.2,35.9],[77.8,35.5],[76.9,34.7],[75.8,34.5],[74.2,34.7],[73.7,34.3],[74.1,33.4],[74.5,32.8],[75.3,32.3],[74.4,31.7],[74.4,31],[73.5,30],[72.8,29],[71.8,27.9],[70.6,28],[69.5,26.9],[70.2,26.5],[70.3,25.7],[70.8,25.2],[71,24.4],[68.8,24.4],[68.2,23.7],[67.4,23.9],[67.1,24.7],[66.4,25.4],[64.5,25.2],[62.9,25.2],[61.5,25.1],[61.9,26.2],[63.3,26.8],[63.2,27.2],[62.8,27.4],[62.7,28.3],[61.8,28.7],[61.4,29.3],[60.9,29.8],[62.5,29.3],[63.6,29.5],[64.1,29.3],[64.4,29.6],[65,29.5],[66.3,29.9],[66.4,30.7],[66.9,31.3],[67.7,31.3],[67.8,31.6],[68.6,31.7],[68.9,31.6],[69.3,31.9],[69.3,32.5],[69.7,33.1],[70.3,33.4],[69.9,34],[70.9,34],[71.2,34.3],[71.1,34.7],[71.6,35.2],[71.5,35.7],[71.3,36.1],[71.8,36.5],[72.9,36.7],[74.1,36.8],[74.6,37],[75.2,37.1]]]},"id":"308"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.9,7.2],[-78.2,7.5],[-78.4,8.1],[-78.2,8.3],[-78.4,8.4],[-78.6,8.7],[-79.1,9],[-79.6,8.9],[-79.8,8.6],[-80.2,8.3],[-80.4,8.3],[-80.5,8.1],[-80,7.5],[-80.3,7.4],[-80.4,7.3],[-80.9,7.2],[-81.1,7.8],[-81.2,7.6],[-81.5,7.7],[-81.7,8.1],[-82.1,8.2],[-82.4,8.3],[-82.8,8.3],[-82.9,8.1],[-83,8.2],[-82.9,8.4],[-82.8,8.6],[-82.9,8.8],[-82.7,8.9],[-82.9,9.1],[-82.9,9.5],[-82.5,9.6],[-82.2,9.2],[-82.2,9],[-81.8,9],[-81.7,9],[-81.4,8.8],[-80.9,8.9],[-80.5,9.1],[-79.9,9.3],[-79.6,9.6],[-79,9.6],[-79.1,9.5],[-78.5,9.4],[-78.1,9.2],[-77.7,8.9],[-77.4,8.7],[-77.5,8.5],[-77.2,7.9],[-77.4,7.6],[-77.8,7.7],[-77.9,7.2]]]},"id":"310"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-69.6,-17.6],[-69.9,-18.1],[-70.4,-18.3],[-71.4,-17.8],[-71.5,-17.4],[-73.4,-16.4],[-75.2,-15.3],[-76,-14.6],[-76.4,-13.8],[-76.3,-13.5],[-77.1,-12.2],[-78.1,-10.4],[-79,-8.4],[-79.4,-7.9],[-79.8,-7.2],[-80.5,-6.5],[-81.2,-6.1],[-80.9,-5.7],[-81.4,-4.7],[-81.1,-4],[-80.3,-3.4],[-80.2,-3.8],[-80.5,-4.1],[-80.4,-4.4],[-80,-4.3],[-79.6,-4.5],[-79.2,-5],[-78.6,-4.5],[-78.5,-3.9],[-77.8,-3],[-76.6,-2.6],[-75.5,-1.6],[-75.2,-0.9],[-75.4,-0.2],[-75.1,-0.1],[-74.4,-0.5],[-74.1,-1],[-73.7,-1.3],[-73.1,-2.3],[-72.3,-2.4],[-71.8,-2.2],[-71.4,-2.3],[-70.8,-2.3],[-70,-2.7],[-70.7,-3.7],[-70.4,-3.8],[-69.9,-4.3],[-70.8,-4.3],[-70.9,-4.4],[-71.7,-4.6],[-72.9,-5.3],[-73,-5.7],[-73.2,-6.1],[-73.1,-6.6],[-73.7,-6.9],[-73.7,-7.3],[-74,-7.5],[-73.6,-8.4],[-73,-9],[-73.2,-9.5],[-72.6,-9.5],[-72.2,-10.1],[-71.3,-10.1],[-70.5,-9.5],[-70.5,-11],[-70.1,-11.1],[-69.5,-11],[-68.7,-12.6],[-68.9,-12.9],[-68.9,-13.6],[-68.9,-14.5],[-69.3,-15],[-69.2,-15.3],[-69.4,-15.7],[-69,-16.5],[-69.6,-17.6]]]},"id":"313"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[126.4,8.4],[126.5,7.8],[126.5,7.2],[126.2,6.3],[125.8,7.3],[125.4,6.8],[125.7,6],[125.4,5.6],[124.2,6.2],[123.9,6.9],[124.2,7.4],[123.6,7.8],[123.3,7.4],[122.8,7.5],[122.1,6.9],[121.9,7.2],[122.3,8],[122.9,8.3],[123.5,8.7],[123.8,8.2],[124.6,8.5],[124.8,9],[125.5,9],[125.4,9.8],[126.2,9.3],[126.3,8.8],[126.4,8.4]]],[[[124,10.3],[123.6,10],[123.3,9.3],[123,9],[122.4,9.7],[122.6,10],[122.8,10.3],[122.9,10.9],[123.5,10.9],[123.3,10.3],[124.1,11.2],[124,10.3]]],[[[118.5,9.3],[117.2,8.4],[117.7,9.1],[118.4,9.7],[119,10.4],[119.5,11.4],[119.7,10.6],[119,10],[118.5,9.3]]],[[[121.9,11.9],[122.5,11.6],[123.1,11.6],[123.1,11.2],[122.6,10.7],[122,10.4],[122,10.9],[122,11.4],[121.9,11.9]]],[[[125.5,12.2],[125.8,11],[125,11.3],[125,11],[125.3,10.4],[124.8,10.1],[124.8,10.8],[124.5,10.9],[124.3,11.5],[124.9,11.4],[124.9,11.8],[124.3,12.6],[125.2,12.5],[125.5,12.2]]],[[[121.5,13.1],[121.3,12.2],[120.8,12.7],[120.3,13.5],[121.2,13.4],[121.5,13.1]]],[[[121.3,18.5],[121.9,18.2],[122.2,18.5],[122.3,18.2],[122.2,17.8],[122.5,17.1],[122.3,16.3],[121.7,15.9],[121.5,15.1],[121.7,14.3],[122.3,14.2],[122.7,14.3],[124,13.8],[123.9,13.2],[124.2,13],[124.1,12.5],[123.3,13],[122.9,13.6],[122.7,13.2],[122,13.8],[121.1,13.6],[120.6,13.9],[120.7,14.3],[121,14.5],[120.7,14.8],[120.6,14.4],[120.1,15],[119.9,15.4],[119.9,16.4],[120.3,16],[120.4,17.6],[120.7,18.5],[121.3,18.5]]]]},"id":"231"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[155.9,-6.8],[155.6,-6.9],[155.2,-6.5],[154.7,-5.9],[154.5,-5.1],[154.7,-5],[154.8,-5.3],[155.1,-5.6],[155.5,-6.2],[156,-6.5],[155.9,-6.8]]],[[[152,-5.5],[151.5,-5.6],[151.3,-5.8],[150.8,-6.1],[150.2,-6.3],[149.7,-6.3],[148.9,-6],[148.3,-5.7],[148.4,-5.4],[149.3,-5.6],[149.8,-5.5],[150,-5],[150.1,-5],[150.2,-5.5],[150.8,-5.5],[151.1,-5.1],[151.6,-4.8],[151.5,-4.2],[152.1,-4.1],[152.3,-4.3],[152.3,-4.9],[152,-5.5]]],[[[147.2,-7.4],[148.1,-8],[148.7,-9.1],[149.3,-9.1],[149.3,-9.5],[150,-9.7],[149.7,-9.9],[150.8,-10.3],[150.7,-10.6],[150,-10.7],[149.8,-10.4],[148.9,-10.3],[147.9,-10.1],[147.1,-9.5],[146.6,-8.9],[146,-8.1],[144.7,-7.6],[143.9,-7.9],[143.3,-8.2],[143.4,-9],[142.6,-9.3],[142.1,-9.2],[141,-9.1],[141,-5.9],[141,-2.6],[142.7,-3.3],[144.6,-3.9],[145.3,-4.4],[145.8,-4.9],[146,-5.5],[147.6,-6.1],[147.9,-6.6],[147,-6.7],[147.2,-7.4]]],[[[153.1,-4.5],[152.8,-4.8],[152.6,-4.2],[152.4,-3.8],[152,-3.5],[151.4,-3],[150.7,-2.7],[150.9,-2.5],[151.5,-2.8],[151.8,-3],[152.2,-3.2],[152.6,-3.7],[153,-4],[153.1,-4.5]]]]},"id":"311"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[15,51.1],[14.6,51.7],[14.7,52.1],[14.4,52.6],[14.1,53],[14.4,53.2],[14.1,53.8],[14.8,54.1],[16.4,54.5],[17.6,54.9],[18.6,54.7],[18.7,54.4],[19.7,54.4],[20.9,54.3],[22.7,54.3],[23.2,54.2],[23.5,53.9],[23.5,53.5],[23.8,53.1],[23.8,52.7],[23.2,52.5],[23.5,52],[23.5,51.6],[24,50.7],[23.9,50.4],[23.4,50.3],[22.5,49.5],[22.8,49],[22.6,49.1],[21.6,49.5],[20.9,49.3],[20.4,49.4],[19.8,49.2],[19.3,49.6],[18.9,49.4],[18.9,49.5],[18.4,50],[17.6,50],[17.6,50.4],[16.9,50.5],[16.7,50.2],[16.2,50.4],[16.2,50.7],[15.5,50.8],[15,51.1]]]},"id":"314"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-66.3,18.5],[-65.8,18.4],[-65.6,18.2],[-65.8,18],[-66.6,18],[-67.2,17.9],[-67.2,18.4],[-67.1,18.5],[-66.3,18.5]]]}},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[130.6,42.4],[130.8,42.2],[130.4,42.3],[130,41.9],[129.7,41.6],[129.7,40.9],[129.2,40.7],[129,40.5],[128.6,40.2],[128,40],[127.5,39.8],[127.5,39.3],[127.4,39.2],[127.8,39.1],[128.3,38.6],[128.2,38.4],[127.8,38.3],[127.1,38.3],[126.7,37.8],[126.2,37.8],[126.2,37.7],[125.7,37.9],[125.6,37.8],[125.3,37.7],[125.2,37.9],[125,37.9],[124.7,38.1],[125,38.5],[125.2,38.7],[125.1,38.8],[125.4,39.4],[125.3,39.6],[124.7,39.7],[124.3,39.9],[125.1,40.6],[126.2,41.1],[126.9,41.8],[127.3,41.5],[128.2,41.5],[128.1,42],[129.6,42.4],[130,43],[130.6,42.4]]]},"id":"305"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-9,41.9],[-8.7,42.1],[-8.3,42.3],[-8,41.8],[-7.4,41.8],[-7.3,41.9],[-6.7,41.9],[-6.4,41.4],[-6.9,41.1],[-6.9,40.3],[-7,40.2],[-7.1,39.7],[-7.5,39.6],[-7.1,39],[-7.4,38.4],[-7,38.1],[-7.2,37.8],[-7.5,37.4],[-7.5,37.1],[-7.9,36.8],[-8.4,37],[-8.9,36.9],[-8.7,37.7],[-8.8,38.3],[-9.3,38.4],[-9.5,38.7],[-9.4,39.4],[-9,39.8],[-9,40.2],[-8.8,40.8],[-8.8,41.2],[-9,41.5],[-9,41.9]]]},"id":"315"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-62.7,-22.2],[-62.3,-21.1],[-62.3,-20.5],[-61.8,-19.6],[-60,-19.3],[-59.1,-19.4],[-58.2,-19.9],[-58.2,-20.2],[-57.9,-20.7],[-57.9,-22.1],[-56.9,-22.3],[-56.5,-22.1],[-55.8,-22.4],[-55.6,-22.7],[-55.5,-23.6],[-55.4,-24],[-55,-24],[-54.7,-23.8],[-54.3,-24],[-54.3,-24.6],[-54.4,-25.2],[-54.6,-25.7],[-54.8,-26.6],[-55.7,-27.4],[-56.5,-27.5],[-57.6,-27.4],[-58.6,-27.1],[-57.6,-25.6],[-57.8,-25.2],[-58.8,-24.8],[-60,-24],[-60.8,-23.9],[-62.7,-22.2]]]},"id":"312"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[35.5,32.4],[35.5,31.8],[35.4,31.5],[34.9,31.4],[35,31.6],[35.2,31.8],[35,31.9],[35.2,32.5],[35.5,32.4]]]},"id":"884"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[50.8,24.8],[50.7,25.5],[51,26],[51.3,26.1],[51.6,25.8],[51.6,25.2],[51.4,24.6],[51.1,24.6],[50.8,24.8]]]},"id":"316"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.7,47.9],[23.1,48.1],[23.8,48],[24.4,48],[24.9,47.7],[25.2,47.9],[25.9,48],[26.2,48.2],[26.6,48.2],[26.9,48.1],[27.2,47.8],[27.6,47.4],[28.1,46.8],[28.2,46.4],[28.1,45.9],[28.2,45.5],[28.7,45.3],[29.1,45.5],[29.6,45.3],[29.6,45],[29.1,44.8],[28.8,44.9],[28.6,43.7],[28,43.8],[27.2,44.2],[26.1,43.9],[25.6,43.7],[24.1,43.7],[23.3,43.9],[22.9,43.8],[22.7,44.2],[22.5,44.4],[22.7,44.6],[22.5,44.7],[22.1,44.5],[21.6,44.8],[21.5,45.2],[20.9,45.4],[20.8,45.7],[20.2,46.1],[21,46.3],[21.6,47],[22.1,47.7],[22.7,47.9]]]},"id":"317"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[143.6,50.7],[144.7,49],[143.2,49.3],[142.6,47.9],[143.5,46.8],[143.5,46.1],[142.7,46.7],[142.1,46],[141.9,46.8],[142,47.8],[141.9,48.9],[142.1,49.6],[142.2,51],[141.6,51.9],[141.7,53.3],[142.6,53.8],[142.2,54.2],[142.7,54.4],[142.9,53.7],[143.3,52.7],[143.2,51.8],[143.6,50.7]]],[[[22.7,54.3],[20.9,54.3],[19.7,54.4],[19.9,54.9],[21.3,55.2],[22.3,55],[22.8,54.9],[22.7,54.6],[22.7,54.3]]],[[[-175,66.6],[-174.3,66.3],[-174.6,67.1],[-171.9,66.9],[-169.9,66],[-170.9,65.5],[-172.5,65.4],[-172.6,64.5],[-173,64.3],[-173.9,64.3],[-174.7,64.6],[-176,64.9],[-176.2,65.4],[-177.2,65.5],[-178.4,65.4],[-178.9,65.7],[-178.7,66.1],[-179.9,65.9],[-179.4,65.4],[-179.9,65],[-179.9,69],[-177.6,68.2],[-174.9,67.2],[-175,66.6]]],[[[179.9,70.8],[178.9,70.8],[178.7,71.1],[179.9,71.5],[179.9,70.8]]],[[[-178.7,70.9],[-179.9,70.8],[-179.9,71.5],[-179.9,71.6],[-179,71.6],[-177.6,71.3],[-177.7,71.1],[-178.7,70.9]]],[[[143.6,73.2],[142.1,73.2],[140,73.3],[139.9,73.4],[140.8,73.8],[142.1,73.9],[143.5,73.5],[143.6,73.2]]],[[[150.7,75.1],[149.6,74.7],[148,74.8],[146.1,75.2],[146.4,75.5],[148.2,75.3],[150.7,75.1]]],[[[145.1,75.6],[144.3,74.8],[140.6,74.8],[139,74.6],[137,75.3],[137.5,75.9],[138.8,76.1],[141.5,76.1],[145.1,75.6]]],[[[57.5,70.7],[56.9,70.6],[53.7,70.8],[53.4,71.2],[51.6,71.5],[51.5,72],[52.5,72.2],[52.4,72.8],[54.4,73.6],[53.5,73.7],[55.9,74.6],[55.6,75.1],[57.9,75.6],[61.2,76.3],[64.5,76.4],[66.2,76.8],[68.2,76.9],[68.9,76.5],[68.2,76.2],[64.6,75.7],[61.6,75.3],[58.5,74.3],[57,73.3],[55.4,72.4],[55.6,71.5],[57.5,70.7]]],[[[107,77],[107.2,76.5],[108.2,76.7],[111.1,76.7],[113.3,76.2],[114.1,75.8],[113.9,75.3],[112.8,75],[110.2,74.5],[109.4,74.2],[110.6,74],[112.1,73.8],[113,74],[113.5,73.3],[114,73.6],[115.6,73.8],[118.8,73.6],[119,73.1],[123.2,73],[123.3,73.7],[125.4,73.6],[127,73.6],[128.6,73],[129.1,72.4],[128.5,72],[129.7,71.2],[131.3,70.8],[132.3,71.8],[133.9,71.4],[135.6,71.7],[137.5,71.3],[138.2,71.6],[139.9,71.5],[139.1,72.4],[140.5,72.8],[149.5,72.2],[150.4,71.6],[153,70.8],[157,71],[159,70.9],[159.8,70.5],[159.7,69.7],[160.9,69.4],[162.3,69.6],[164.1,69.7],[165.9,69.5],[167.8,69.6],[169.6,68.7],[170.8,69],[170,69.7],[170.5,70.1],[173.6,69.8],[175.7,69.9],[178.6,69.4],[179.9,69],[179.9,65],[179.9,65],[178.7,64.5],[177.4,64.6],[178.3,64.1],[178.9,63.3],[179.4,63],[179.5,62.6],[179.2,62.3],[177.4,62.5],[174.6,61.8],[173.7,61.7],[172.2,60.9],[170.7,60.3],[170.3,59.9],[168.9,60.6],[166.3,59.8],[165.8,60.2],[164.9,59.7],[163.5,59.9],[163.2,59.2],[162,58.2],[162.1,57.8],[163.2,57.6],[163.1,56.2],[162.1,56.1],[161.7,55.3],[162.1,54.9],[160.4,54.3],[160,53.2],[158.5,53],[158.2,51.9],[156.8,51],[156.4,51.7],[156,53.2],[155.4,55.4],[155.9,56.8],[156.8,57.4],[156.8,57.8],[158.4,58.1],[160.2,59.3],[161.9,60.3],[163.7,61.1],[164.5,62.6],[163.3,62.5],[162.7,61.6],[160.1,60.5],[159.3,61.8],[156.7,61.4],[154.2,59.8],[155,59.1],[152.8,58.9],[151.3,58.8],[151.3,59.5],[149.8,59.7],[148.5,59.2],[145.5,59.3],[142.2,59],[139,57.1],[135.1,54.7],[136.7,54.6],[137.2,54],[138.2,53.8],[138.8,54.3],[139.9,54.2],[141.3,53.1],[141.4,52.2],[140.6,51.2],[140.5,50],[140.1,48.4],[138.6,47],[138.2,46.3],[136.9,45.1],[135.5,44],[134.9,43.4],[133.5,42.8],[132.9,42.8],[132.3,43.3],[130.9,42.6],[130.8,42.2],[130.6,42.4],[130.6,42.9],[131.1,42.9],[131.3,44.1],[131,45],[131.9,45.3],[133.1,45.1],[133.8,46.1],[134.1,47.2],[134.5,47.6],[135,48.5],[133.4,48.2],[132.5,47.8],[131,47.8],[130.6,48.7],[129.4,49.4],[127.7,49.8],[127.3,50.7],[126.9,51.4],[126.6,51.8],[125.9,52.8],[125.1,53.2],[123.6,53.5],[122.2,53.4],[121,53.3],[120.2,52.8],[120.7,52.5],[120.7,52],[120.2,51.6],[119.3,50.6],[119.3,50.1],[117.9,49.5],[116.7,49.9],[115.5,49.8],[115,50.1],[114.4,50.2],[112.9,49.5],[111.6,49.4],[110.7,49.1],[109.4,49.3],[108.5,49.3],[107.9,49.8],[106.9,50.3],[105.9,50.4],[104.6,50.3],[103.7,50.1],[102.3,50.5],[102.1,51.3],[100.9,51.5],[100,51.6],[98.9,52],[97.8,51],[98.2,50.4],[97.3,49.7],[95.8,50],[94.8,50],[94.1,50.5],[93.1,50.5],[92.2,50.8],[90.7,50.3],[88.8,49.5],[87.8,49.3],[87.4,49.2],[86.8,49.8],[85.5,49.7],[85.1,50.1],[84.4,50.3],[83.9,50.9],[83.4,51.1],[81.9,50.8],[80.6,51.4],[80,50.9],[77.8,53.4],[76.5,54.2],[76.9,54.5],[74.4,53.5],[73.4,53.5],[73.5,54],[72.2,54.4],[71.2,54.1],[70.9,55.2],[69.1,55.4],[68.2,55],[65.7,54.6],[65.2,54.4],[61.4,54],[61,53.7],[61.7,53],[60.7,52.7],[60.9,52.4],[60,52],[61.6,51.3],[61.3,50.8],[59.9,50.8],[59.6,50.5],[58.4,51.1],[56.8,51],[55.7,50.6],[54.5,51],[52.3,51.7],[50.8,51.7],[48.7,50.6],[48.6,49.9],[47.5,50.5],[46.8,49.4],[47,49.2],[46.5,48.4],[47.3,47.7],[48.1,47.7],[48.7,47.1],[48.6,46.6],[49.1,46.4],[48.6,45.8],[47.7,45.6],[46.7,44.6],[47.6,43.7],[47.5,43],[48.6,41.8],[48,41.4],[47.8,41.2],[47.4,41.2],[46.7,41.8],[46.4,41.9],[45.8,42.1],[45.5,42.5],[44.5,42.7],[43.9,42.6],[43.8,42.7],[42.4,43.2],[40.9,43.4],[40.1,43.6],[40,43.4],[38.7,44.3],[37.5,44.7],[36.7,45.2],[37.4,45.4],[38.2,46.2],[37.7,46.6],[39.1,47],[39.1,47.3],[38.2,47.1],[38.3,47.5],[38.8,47.8],[39.7,47.9],[39.9,48.2],[39.7,48.8],[40.1,49.3],[40.1,49.6],[38.6,49.9],[38,49.9],[37.4,50.4],[36.6,50.2],[35.4,50.6],[35.4,50.8],[35,51.2],[34.2,51.3],[34.1,51.6],[34.4,51.8],[33.8,52.3],[32.7,52.2],[32.4,52.3],[32.2,52.1],[31.8,52.1],[31.5,52.7],[31.3,53.1],[31.5,53.2],[32.3,53.1],[32.7,53.4],[32.4,53.6],[31.7,53.8],[31.8,54],[31.4,54.2],[30.8,54.8],[31,55.1],[30.9,55.6],[29.9,55.8],[29.4,55.7],[29.2,55.9],[28.2,56.2],[27.9,56.8],[27.8,57.2],[27.3,57.5],[27.7,57.8],[27.4,58.7],[28.1,59.3],[28,59.5],[29.1,60],[28.1,60.5],[30.2,61.8],[31.1,62.4],[31.5,62.9],[30,63.6],[30.4,64.2],[29.5,64.9],[30.2,65.8],[29.1,66.9],[30,67.7],[28.4,68.4],[28.6,69.1],[29.4,69.2],[31.1,69.6],[32.1,69.9],[33.8,69.3],[36.5,69.1],[40.3,67.9],[41.1,67.5],[41.1,66.8],[40,66.3],[38.4,66],[33.9,66.8],[33.2,66.6],[34.8,65.9],[34.9,65.4],[34.9,64.4],[36.2,64.1],[37,63.8],[37.1,64.3],[36.5,64.8],[37.2,65.1],[39.6,64.5],[40.4,64.8],[39.8,65.5],[42.1,66.5],[43,66.4],[43.9,66.1],[44.5,66.8],[43.7,67.4],[44.2,68],[43.5,68.6],[46.3,68.3],[46.8,67.7],[45.6,67.6],[45.6,67],[46.3,66.7],[47.9,66.9],[48.1,67.5],[50.2,68],[53.7,68.9],[54.5,68.8],[53.5,68.2],[54.7,68.1],[55.4,68.4],[57.3,68.5],[58.8,68.9],[59.9,68.3],[61.1,68.9],[60,69.5],[60.6,69.9],[63.5,69.5],[64.9,69.2],[68.5,68.1],[69.2,68.6],[68.2,69.1],[68.1,69.4],[66.9,69.5],[67.3,69.9],[66.7,70.7],[66.7,71],[68.5,71.9],[69.2,72.8],[69.9,73],[72.6,72.8],[72.8,72.2],[71.8,71.4],[72.5,71.1],[72.8,70.4],[72.6,69],[73.7,68.4],[73.2,67.7],[71.3,66.3],[72.4,66.2],[72.8,66.5],[73.9,66.8],[74.2,67.3],[75.1,67.8],[74.5,68.3],[74.9,69],[73.8,69.1],[73.6,69.6],[74.4,70.6],[73.1,71.4],[74.9,72.1],[74.7,72.8],[75.2,72.9],[75.7,72.3],[75.3,71.3],[76.4,71.2],[75.9,71.9],[77.6,72.3],[79.7,72.3],[81.5,71.8],[80.6,72.6],[80.5,73.6],[82.3,73.9],[84.7,73.8],[86.8,73.9],[86,74.5],[87.2,75.1],[88.3,75.1],[90.3,75.6],[92.9,75.8],[93.2,76],[95.9,76.1],[96.7,75.9],[98.9,76.4],[100.8,76.4],[101,76.9],[102,77.3],[104.4,77.7],[106.1,77.4],[104.7,77.1],[107,77]]],[[[105.1,78.3],[99.4,77.9],[101.3,79.2],[102.1,79.3],[102.8,79.3],[105.4,78.7],[105.1,78.3]]],[[[51.1,80.5],[49.8,80.4],[48.9,80.3],[48.8,80.2],[47.6,80],[46.5,80.2],[47.1,80.6],[44.8,80.6],[46.8,80.8],[48.3,80.8],[48.5,80.5],[49.1,80.8],[50,80.9],[51.5,80.7],[51.1,80.5]]],[[[99.9,78.9],[97.8,78.8],[95,79],[93.3,79.4],[92.5,80.1],[91.2,80.3],[93.8,81],[95.9,81.3],[97.9,80.7],[100.2,79.8],[99.9,78.9]]]]},"id":"318"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[30.4,-1.1],[30.8,-1.7],[30.8,-2.3],[30.5,-2.4],[29.9,-2.3],[29.6,-2.9],[29,-2.8],[29.1,-2.3],[29.3,-2.2],[29.3,-1.6],[29.6,-1.3],[29.8,-1.4],[30.4,-1.1]]]},"id":"319"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[42.8,16.3],[42.6,16.8],[42.3,17.1],[42.3,17.5],[41.8,17.8],[41.2,18.7],[40.9,19.5],[40.2,20.2],[39.8,20.3],[39.1,21.3],[39,22],[39.1,22.6],[38.5,23.7],[38,24.1],[37.5,24.3],[37.2,24.9],[37.2,25.1],[36.9,25.6],[36.6,25.8],[36.2,26.6],[35.6,27.4],[35.1,28.1],[34.6,28.1],[34.8,28.6],[34.8,29],[35,29.4],[36.1,29.2],[36.5,29.5],[36.7,29.9],[37.5,30],[37.7,30.3],[38,30.5],[37,31.5],[39,32],[39.2,32.2],[40.4,31.9],[41.9,31.2],[44.7,29.2],[46.6,29.1],[47.5,29],[47.7,28.5],[48.4,28.6],[48.8,27.7],[49.3,27.5],[49.5,27.1],[50.2,26.7],[50.2,26.3],[50.1,25.9],[50.2,25.6],[50.5,25.3],[50.7,25],[50.8,24.8],[51.1,24.6],[51.4,24.6],[51.6,24.2],[51.6,24],[52,23],[55,22.5],[55.2,22.7],[55.7,22],[55,20],[52,19],[49.1,18.6],[48.2,18.2],[47.5,17.1],[47,16.9],[46.7,17.3],[46.4,17.2],[45.4,17.3],[45.2,17.4],[44.1,17.4],[43.8,17.3],[43.4,17.6],[43.1,17.1],[43.2,16.7],[42.8,16.3]]]},"id":"328"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34,9.5],[33.8,9.5],[33.8,10],[33.7,10.3],[33.2,10.7],[33.1,11.4],[33.2,12.2],[32.7,12.2],[32.7,12],[32.1,12],[32.3,11.7],[32.4,11.1],[31.9,10.5],[31.4,9.8],[30.8,9.7],[30,10.3],[29.6,10.1],[29.5,9.8],[29,9.6],[29,9.4],[28,9.4],[27.8,9.6],[27.1,9.6],[26.8,9.5],[26.5,9.6],[26,10.1],[25.8,10.4],[25.1,10.3],[24.8,9.8],[24.5,8.9],[24.2,8.7],[23.9,8.6],[23.8,8.7],[23.5,9],[23.4,9.3],[23.6,9.7],[23.6,10.1],[23,10.7],[22.9,11.1],[22.9,11.4],[22.5,11.7],[22.5,12.3],[22.3,12.6],[21.9,12.6],[22,13],[22.3,13.4],[22.2,13.8],[22.5,14.1],[22.3,14.3],[22.6,14.9],[23,15.7],[23.9,15.6],[23.8,19.6],[23.9,20],[25,20],[25,22],[29,22],[32.9,22],[36.9,22],[37.2,21],[37,20.8],[37.1,19.8],[37.5,18.6],[37.9,18.4],[38.4,18],[37.9,17.4],[37.2,17.3],[36.9,17],[36.8,16.3],[36.3,14.8],[36.4,14.4],[36.3,13.6],[35.9,12.6],[35.3,12.1],[34.8,11.3],[34.7,10.9],[34.3,10.6],[34,9.6],[34,9.5]]]},"id":"117"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[34,9.5],[34,8.7],[33.8,8.4],[33.3,8.4],[33,7.8],[33.6,7.7],[34.1,7.2],[34.3,6.8],[34.7,6.6],[35.3,5.5],[34.6,4.8],[34,4.2],[33.4,3.8],[32.7,3.8],[31.9,3.6],[31.2,3.8],[30.8,3.5],[30,4.2],[29.7,4.6],[29.2,4.4],[28.7,4.5],[28.4,4.3],[28,4.4],[27.4,5.2],[27.2,5.6],[26.5,5.9],[26.2,6.5],[25.8,7],[25.1,7.5],[25.1,7.8],[24.6,8.2],[23.9,8.6],[24.2,8.7],[24.5,8.9],[24.8,9.8],[25.1,10.3],[25.8,10.4],[26,10.1],[26.5,9.6],[26.8,9.5],[27.1,9.6],[27.8,9.6],[28,9.4],[29,9.4],[29,9.6],[29.5,9.8],[29.6,10.1],[30,10.3],[30.8,9.7],[31.4,9.8],[31.9,10.5],[32.4,11.1],[32.3,11.7],[32.1,12],[32.7,12],[32.7,12.2],[33.2,12.2],[33.1,11.4],[33.2,10.7],[33.7,10.3],[33.8,10],[33.8,9.5],[34,9.5]]]},"id":"13863"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-16.7,13.6],[-17.1,14.4],[-17.6,14.7],[-17.2,14.9],[-16.7,15.6],[-16.5,16.1],[-16.1,16.5],[-15.6,16.4],[-15.1,16.6],[-14.6,16.6],[-14.1,16.3],[-13.4,16],[-12.8,15.3],[-12.2,14.6],[-12.1,14],[-11.9,13.4],[-11.6,13.1],[-11.5,12.8],[-11.5,12.4],[-11.7,12.4],[-12.2,12.5],[-12.3,12.4],[-12.5,12.3],[-13.2,12.6],[-13.7,12.6],[-15.5,12.6],[-15.8,12.5],[-16.1,12.5],[-16.7,12.4],[-16.8,13.2],[-15.9,13.1],[-15.7,13.3],[-15.5,13.3],[-15.1,13.5],[-14.7,13.3],[-14.3,13.3],[-13.8,13.5],[-14,13.8],[-14.4,13.6],[-14.7,13.6],[-15.1,13.9],[-15.4,13.9],[-15.6,13.6],[-16.7,13.6]]]},"id":"327"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[162.1,-10.5],[162.4,-10.8],[161.7,-10.8],[161.3,-10.2],[161.9,-10.4],[162.1,-10.5]]],[[[160.9,-9.9],[160.5,-9.9],[159.8,-9.8],[159.6,-9.6],[159.7,-9.2],[160.4,-9.4],[160.7,-9.6],[160.9,-9.9]]],[[[161.7,-9.6],[161.5,-9.8],[160.8,-8.9],[160.6,-8.3],[160.9,-8.3],[161.3,-9.1],[161.7,-9.6]]],[[[159.9,-8.3],[159.9,-8.5],[159.1,-8.1],[158.6,-7.8],[158.2,-7.4],[158.4,-7.3],[158.8,-7.6],[159.6,-8],[159.9,-8.3]]],[[[157.5,-7.3],[157.3,-7.4],[156.9,-7.2],[156.5,-6.8],[156.5,-6.6],[157.1,-7],[157.5,-7.3]]]]},"id":"323"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-11.4,6.8],[-11.7,6.9],[-12.4,7.3],[-12.9,7.8],[-13.1,8.2],[-13.2,8.9],[-12.7,9.3],[-12.6,9.6],[-12.4,9.8],[-12.2,9.9],[-11.9,10],[-11.1,10],[-10.8,9.7],[-10.6,9.3],[-10.7,9],[-10.5,8.7],[-10.5,8.3],[-10.2,8.4],[-10.7,7.9],[-11.1,7.4],[-11.2,7.1],[-11.4,6.8]]]},"id":"331"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-87.8,13.4],[-87.9,13.1],[-88.5,13.2],[-88.8,13.3],[-89.3,13.5],[-89.8,13.5],[-90.1,13.7],[-90.1,13.9],[-89.7,14.1],[-89.5,14.2],[-89.6,14.4],[-89.4,14.4],[-89.1,14.3],[-88.8,14.1],[-88.5,14],[-88.5,13.8],[-88.1,14],[-87.9,13.9],[-87.7,13.8],[-87.8,13.4]]]},"id":"225"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[20.9,45.4],[21.5,45.2],[21.6,44.8],[22.1,44.5],[22.5,44.7],[22.7,44.6],[22.5,44.4],[22.7,44.2],[22.4,44],[22.5,43.6],[23,43.2],[22.6,42.9],[22.4,42.6],[22.5,42.5],[22.4,42.3],[21.9,42.3],[21.6,42.2],[21.5,42.3],[21.7,42.4],[21.8,42.7],[21.6,42.7],[21.4,42.9],[21.3,42.9],[21.1,43.1],[21,43.1],[20.8,43.3],[20.6,43.2],[20.5,42.9],[20.3,42.8],[20.3,42.9],[20,43.1],[19.6,43.2],[19.5,43.4],[19.2,43.5],[19.5,43.6],[19.6,44],[19.1,44.4],[19.4,44.9],[19,44.9],[19.4,45.2],[19.1,45.5],[18.8,45.9],[19.6,46.2],[20.2,46.1],[20.8,45.7],[20.9,45.4]]]},"id":"372"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.1,6],[-55.9,5.8],[-55.8,6],[-55,6],[-54,5.8],[-54.5,4.9],[-54.4,4.2],[-54,3.6],[-54.2,3.2],[-54.3,2.7],[-54.5,2.3],[-55.1,2.5],[-55.6,2.4],[-56,2.5],[-56.1,2.2],[-55.9,2],[-56,1.8],[-56.5,1.9],[-57.2,2.8],[-57.3,3.3],[-57.6,3.3],[-58,4.1],[-57.9,4.6],[-57.9,4.8],[-57.3,5.1],[-57.1,6]]]},"id":"339"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[18.9,49.5],[18.9,49.4],[19.3,49.6],[19.8,49.2],[20.4,49.4],[20.9,49.3],[21.6,49.5],[22.6,49.1],[22.3,48.8],[22.1,48.4],[21.9,48.3],[20.8,48.6],[20.5,48.6],[20.2,48.3],[19.8,48.2],[19.7,48.3],[19.2,48.1],[18.8,48.1],[18.7,47.9],[17.9,47.8],[17.5,47.9],[17,48.1],[16.9,48.5],[17,48.6],[17.1,48.8],[17.5,48.8],[17.9,48.9],[17.9,49],[18.1,49],[18.2,49.3],[18.4,49.3],[18.6,49.5],[18.9,49.5]]]},"id":"333"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[13.8,46.5],[14.6,46.4],[15.1,46.7],[16,46.7],[16.2,46.9],[16.4,46.8],[16.6,46.5],[15.8,46.2],[15.7,45.8],[15.3,45.7],[15.3,45.5],[14.9,45.5],[14.6,45.6],[14.4,45.5],[13.7,45.5],[13.9,45.6],[13.7,46],[13.8,46.5]]]},"id":"334"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[22.2,65.7],[21.2,65],[21.4,64.4],[19.8,63.6],[17.8,62.7],[17.1,61.3],[17.8,60.6],[18.8,60.1],[17.9,59],[16.8,58.7],[16.4,57],[15.9,56.1],[14.7,56.2],[14.1,55.4],[12.9,55.4],[12.6,56.3],[11.8,57.4],[11,58.9],[11.5,59.4],[12.3,60.1],[12.6,61.3],[12,61.8],[11.9,63.1],[12.6,64.1],[13.6,64],[13.9,64.4],[13.6,64.8],[15.1,66.2],[16.1,67.3],[16.8,68],[17.7,68],[18,68.6],[19.9,68.4],[20,69.1],[20.6,69.1],[22,68.6],[23.5,67.9],[23.6,66.4],[23.9,66],[22.2,65.7]]]},"id":"341"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[32.1,-26.7],[31.9,-27.2],[31.3,-27.3],[30.7,-26.7],[30.7,-26.4],[30.9,-26],[31,-25.7],[31.3,-25.7],[31.8,-25.8],[32,-26.3],[32.1,-26.7]]]},"id":"342"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[38.8,33.4],[36.8,32.3],[35.7,32.7],[35.7,32.7],[35.8,32.9],[35.8,33.3],[36.1,33.8],[36.6,34.2],[36.4,34.6],[36,34.6],[35.9,35.4],[36.1,35.8],[36.4,36],[36.7,36.3],[36.7,36.8],[37.1,36.6],[38.2,36.9],[38.7,36.7],[39.5,36.7],[40.7,37.1],[41.2,37.1],[42.3,37.2],[41.8,36.6],[41.3,36.4],[41.4,35.6],[41,34.4],[38.8,33.4]]]},"id":"343"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[14.5,12.9],[14.6,13.3],[14,13.4],[14,14],[13.5,14.4],[14,15.7],[15.2,16.6],[15.3,17.9],[15.7,20],[15.9,20.4],[15.5,20.7],[15.5,21],[15.1,21.3],[14.9,22.9],[15.9,23.4],[19.8,21.5],[23.8,19.6],[23.9,15.6],[23,15.7],[22.6,14.9],[22.3,14.3],[22.5,14.1],[22.2,13.8],[22.3,13.4],[22,13],[21.9,12.6],[22.3,12.6],[22.5,12.3],[22.5,11.7],[22.9,11.4],[22.9,11.1],[22.2,11],[21.7,10.6],[21,9.5],[20.1,9],[19.1,9.1],[18.8,9],[18.9,8.6],[18.4,8.3],[18,7.9],[16.7,7.5],[16.5,7.7],[16.3,7.8],[16.1,7.5],[15.3,7.4],[15.4,7.7],[15.1,8.4],[15,8.8],[14.5,9],[14,9.5],[14.2,10],[14.6,9.9],[14.9,10],[15.5,10],[14.9,10.9],[15,11.6],[14.9,12.2],[14.5,12.9]]]},"id":"352"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.9,6.1],[1.1,5.9],[0.8,6.3],[0.6,6.9],[0.5,7.4],[0.7,8.3],[0.5,8.7],[0.4,9.5],[0.4,10.2],[0,10.7],[0,11],[0.9,11],[0.8,10.5],[1.1,10.2],[1.4,9.8],[1.5,9.3],[1.7,9.1],[1.6,6.8],[1.9,6.1]]]},"id":"349"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[102.6,12.2],[101.7,12.6],[100.8,12.6],[101,13.4],[100.1,13.4],[100,12.3],[99.5,10.8],[99.2,10],[99.2,9.2],[99.9,9.2],[100.3,8.3],[100.5,7.4],[101,6.9],[101.6,6.7],[102.1,6.2],[101.8,5.8],[101.2,5.7],[101.1,6.2],[100.3,6.6],[100.1,6.5],[99.7,6.8],[99.5,7.3],[99,7.9],[98.5,8.4],[98.3,7.8],[98.2,8.4],[98.3,9],[98.6,9.9],[99,11],[99.6,11.9],[99.2,12.8],[99.2,13.3],[99.1,13.8],[98.4,14.6],[98.2,15.1],[98.5,15.3],[98.9,16.2],[98.5,16.8],[97.9,17.6],[97.4,18.4],[97.8,18.6],[98.3,19.7],[99,19.8],[99.5,20.2],[100.1,20.4],[100.5,20.1],[100.6,19.5],[101.3,19.5],[101,18.4],[101.1,17.5],[102.1,18.1],[102.4,17.9],[103,18],[103.2,18.3],[104,18.2],[104.7,17.4],[104.8,16.4],[105.6,15.6],[105.5,14.7],[105.2,14.3],[104.3,14.4],[103,14.2],[102.3,13.4],[102.6,12.2]]]},"id":"348"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[71,40.2],[70.6,39.9],[69.6,40.1],[69.5,39.5],[70.5,39.6],[71.8,39.3],[73.7,39.4],[73.9,38.5],[74.3,38.6],[74.9,38.4],[74.8,38],[75,37.4],[73.9,37.4],[73.3,37.5],[72.6,37],[72.2,36.9],[71.8,36.7],[71.4,37.1],[71.5,37.9],[71.2,38],[71.3,38.3],[70.8,38.5],[70.4,38.1],[70.3,37.7],[70.1,37.6],[69.5,37.6],[69.2,37.2],[68.9,37.3],[68.1,37],[67.8,37.1],[68.4,38.2],[68.2,38.9],[67.4,39.1],[67.7,39.6],[68.5,39.5],[69,40.1],[69.3,40.7],[70.7,41],[70.5,40.5],[70.6,40.2],[71,40.2]]]},"id":"346"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[61.2,35.7],[61.1,36.5],[60.4,36.5],[59.2,37.4],[58.4,37.5],[57.3,38],[56.6,38.1],[56.2,37.9],[55.5,38],[54.8,37.4],[53.9,37.2],[53.7,37.9],[53.9,39],[53.1,39.3],[53.4,40],[52.7,40],[52.9,40.9],[53.9,40.6],[54.7,41],[54,41.6],[53.7,42.1],[52.9,41.9],[52.8,41.1],[52.5,41.8],[52.9,42.1],[54.1,42.3],[54.8,42],[55.5,41.3],[56,41.3],[57.1,41.3],[56.9,41.8],[57.8,42.2],[58.6,42.8],[60,42.2],[60.1,41.4],[60.5,41.2],[61.5,41.3],[61.9,41.1],[62.4,40.1],[63.5,39.4],[64.2,38.9],[65.2,38.4],[66.5,38],[66.5,37.4],[66.2,37.4],[65.7,37.7],[65.6,37.3],[64.7,37.1],[64.5,36.3],[64,36],[63.2,35.9],[63,35.4],[62.2,35.3],[61.2,35.7]]]},"id":"355"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[125,-8.9],[125.1,-8.7],[125.9,-8.4],[126.6,-8.4],[127,-8.3],[127.3,-8.4],[127,-8.7],[125.9,-9.1],[125.1,-9.4],[125.1,-9.1],[125,-8.9]]]},"id":"370"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-61.7,10.8],[-61.1,10.9],[-60.9,10.9],[-60.9,10.1],[-61.8,10],[-62,10.1],[-61.7,10.4],[-61.7,10.8]]]},"id":"351"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.5,30.3],[9.1,32.1],[8.4,32.5],[8.4,32.7],[7.6,33.3],[7.5,34.1],[8.1,34.7],[8.4,35.5],[8.2,36.4],[8.4,36.9],[9.5,37.3],[10.2,37.2],[10.2,36.7],[11,37.1],[11.1,36.9],[10.6,36.4],[10.6,35.9],[10.9,35.7],[10.8,34.8],[10.1,34.3],[10.3,33.8],[10.9,33.8],[11.1,33.3],[11.5,33.1],[11.4,32.4],[10.9,32.1],[10.6,31.8],[10,31.4],[10.1,31],[10,30.5],[9.5,30.3]]]},"id":"354"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[36.9,41.3],[38.3,40.9],[39.5,41.1],[40.4,41],[41.6,41.5],[42.6,41.6],[43.6,41.1],[43.8,40.7],[43.7,40.3],[44.4,40],[44.8,39.7],[44.1,39.4],[44.4,38.3],[44.2,38],[44.8,37.2],[44.3,37],[43.9,37.3],[42.8,37.4],[42.3,37.2],[41.2,37.1],[40.7,37.1],[39.5,36.7],[38.7,36.7],[38.2,36.9],[37.1,36.6],[36.7,36.8],[36.7,36.3],[36.4,36],[36.1,35.8],[35.8,36.3],[36.2,36.7],[35.6,36.6],[34.7,36.8],[34,36.2],[32.5,36.1],[31.7,36.6],[30.6,36.7],[30.4,36.3],[29.7,36.1],[28.7,36.7],[27.6,36.7],[27,37.7],[26.3,38.2],[26.8,39],[26.2,39.5],[27.3,40.4],[28.8,40.5],[29.2,41.2],[31.1,41.1],[32.3,41.7],[33.5,42],[35.2,42],[36.9,41.3]]],[[[27.2,40.7],[26.4,40.2],[26,40.6],[26.1,40.8],[26.3,40.9],[26.6,41.6],[26.1,41.8],[27.1,42.1],[28,42],[28.1,41.6],[29,41.3],[28.8,41.1],[27.6,41],[27.2,40.7]]]]},"id":"357"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[121.8,24.4],[121.2,22.8],[120.7,22],[120.2,22.8],[120.1,23.6],[120.7,24.5],[121.5,25.3],[122,25],[121.8,24.4]]]},"id":"3165"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[33.9,-0.9],[34.1,-1.1],[37.7,-3.1],[37.8,-3.7],[39.2,-4.7],[38.7,-5.9],[38.8,-6.5],[39.4,-6.8],[39.5,-7.1],[39.2,-7.7],[39.3,-8],[39.2,-8.5],[39.5,-9.1],[39.9,-10.1],[40.3,-10.3],[39.5,-10.9],[38.4,-11.3],[37.8,-11.3],[37.5,-11.6],[36.8,-11.6],[36.5,-11.7],[35.3,-11.4],[34.6,-11.5],[34.3,-10.2],[33.9,-9.7],[33.7,-9.4],[32.8,-9.2],[32.2,-8.9],[31.6,-8.8],[31.2,-8.6],[30.7,-8.3],[30.2,-7.1],[29.6,-6.5],[29.4,-5.9],[29.5,-5.4],[29.3,-4.5],[29.8,-4.5],[30.1,-4.1],[30.5,-3.6],[30.8,-3.4],[30.7,-3],[30.5,-2.8],[30.5,-2.4],[30.8,-2.3],[30.8,-1.7],[30.4,-1.1],[30.8,-1],[31.9,-1],[33.9,-0.9]]]},"id":"347"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.9,-1],[30.8,-1],[30.4,-1.1],[29.8,-1.4],[29.6,-1.3],[29.6,-0.6],[29.8,-0.2],[29.9,0.6],[30.1,1.1],[30.5,1.6],[30.9,1.8],[31.2,2.2],[30.8,2.3],[30.8,3.5],[31.2,3.8],[31.9,3.6],[32.7,3.8],[33.4,3.8],[34,4.2],[34.5,3.6],[34.6,3.1],[35,1.9],[34.7,1.2],[34.2,0.5],[33.9,0.1],[33.9,-0.9],[31.9,-1]]]},"id":"359"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.8,52.1],[32.2,52.1],[32.4,52.3],[32.7,52.2],[33.8,52.3],[34.4,51.8],[34.1,51.6],[34.2,51.3],[35,51.2],[35.4,50.8],[35.4,50.6],[36.6,50.2],[37.4,50.4],[38,49.9],[38.6,49.9],[40.1,49.6],[40.1,49.3],[39.7,48.8],[39.9,48.2],[39.7,47.9],[38.8,47.8],[38.3,47.5],[38.2,47.1],[37.4,47],[36.8,46.7],[35.8,46.6],[35,46.3],[35,45.7],[35.5,45.4],[36.5,45.5],[36.3,45.1],[35.2,44.9],[33.9,44.4],[33.3,44.6],[33.5,45],[32.5,45.3],[32.6,45.5],[33.6,45.9],[33.3,46.1],[31.7,46.3],[31.7,46.7],[30.7,46.6],[30.4,46],[29.6,45.3],[29.1,45.5],[28.7,45.3],[28.2,45.5],[28.5,45.6],[28.7,45.9],[28.9,46.3],[28.9,46.4],[29.1,46.5],[29.2,46.4],[29.8,46.3],[30,46.4],[29.8,46.5],[29.9,46.7],[29.6,46.9],[29.4,47.3],[29.1,47.5],[29.1,47.8],[28.7,48.1],[28.3,48.2],[27.5,48.5],[26.9,48.4],[26.6,48.2],[26.2,48.2],[25.9,48],[25.2,47.9],[24.9,47.7],[24.4,48],[23.8,48],[23.1,48.1],[22.7,47.9],[22.6,48.2],[22.1,48.4],[22.3,48.8],[22.6,49.1],[22.8,49],[22.5,49.5],[23.4,50.3],[23.9,50.4],[24,50.7],[23.5,51.6],[24,51.6],[24.6,51.9],[25.3,51.9],[26.3,51.8],[27.5,51.6],[28.2,51.6],[28.6,51.4],[29,51.6],[29.3,51.4],[30.2,51.4],[30.6,51.3],[30.6,51.8],[30.9,52],[31.8,52.1]]]},"id":"360"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.6,-30.2],[-57,-30.1],[-56,-30.9],[-55.6,-30.9],[-54.6,-31.5],[-53.8,-32],[-53.2,-32.7],[-53.7,-33.2],[-53.4,-33.8],[-53.8,-34.4],[-54.9,-35],[-55.7,-34.8],[-56.2,-34.9],[-57.1,-34.4],[-57.8,-34.5],[-58.4,-33.9],[-58.3,-33.3],[-58.1,-33],[-58.1,-32],[-57.9,-31],[-57.6,-30.2]]]},"id":"362"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-155.5,19.1],[-155.7,18.9],[-155.9,19.1],[-155.9,19.3],[-156.1,19.7],[-156,19.8],[-155.9,20],[-155.9,20.2],[-155.9,20.3],[-155.8,20.2],[-155.4,20.1],[-155.2,20],[-155.1,19.9],[-154.8,19.5],[-154.8,19.5],[-155.2,19.2],[-155.5,19.1]]],[[[-156.1,20.6],[-156.4,20.6],[-156.6,20.8],[-156.7,20.9],[-156.7,20.9],[-156.6,21],[-156.3,20.9],[-156,20.8],[-156.1,20.6]]],[[[-156.8,21.2],[-156.8,21.1],[-157.3,21.1],[-157.3,21.2],[-156.8,21.2]]],[[[-157.7,21.3],[-157.7,21.3],[-157.8,21.3],[-158.1,21.3],[-158.3,21.5],[-158.3,21.6],[-158,21.7],[-157.9,21.7],[-157.7,21.3]]],[[[-159.3,22],[-159.5,21.9],[-159.8,22.1],[-159.7,22.1],[-159.6,22.2],[-159.4,22.2],[-159.3,22]]],[[[-94.8,49.4],[-94.6,48.8],[-94.3,48.7],[-93.6,48.6],[-92.6,48.4],[-91.6,48.1],[-90.8,48.3],[-89.6,48],[-89.3,48],[-88.4,48.3],[-87.4,47.9],[-86.5,47.6],[-85.7,47.2],[-84.9,46.9],[-84.8,46.6],[-84.5,46.5],[-84.6,46.4],[-84.3,46.4],[-84.1,46.5],[-84.1,46.3],[-83.9,46.1],[-83.6,46.1],[-83.5,46],[-83.6,45.8],[-82.6,45.3],[-82.3,44.4],[-82.1,43.6],[-82.4,43],[-82.9,42.4],[-83.1,42.1],[-83.1,42],[-83,41.8],[-82.7,41.7],[-82.4,41.7],[-81.3,42.2],[-80.2,42.4],[-78.9,42.9],[-78.9,43],[-79,43.3],[-79.2,43.5],[-78.7,43.6],[-77.7,43.6],[-76.8,43.6],[-76.5,44],[-76.4,44.1],[-75.3,44.8],[-74.9,45],[-73.3,45],[-71.5,45],[-71.4,45.3],[-71.1,45.3],[-70.7,45.5],[-70.3,45.9],[-70,46.7],[-69.2,47.4],[-68.9,47.2],[-68.2,47.4],[-67.8,47.1],[-67.8,45.7],[-67.1,45.1],[-67,44.8],[-68,44.3],[-69.1,44],[-70.1,43.7],[-70.6,43.1],[-70.8,42.9],[-70.8,42.3],[-70.5,41.8],[-70.1,41.8],[-70.2,42.1],[-69.9,41.9],[-70,41.6],[-70.6,41.5],[-71.1,41.5],[-71.9,41.3],[-72.3,41.3],[-72.9,41.2],[-73.7,40.9],[-72.2,41.1],[-71.9,40.9],[-73.3,40.6],[-74,40.6],[-74,40.8],[-74.3,40.5],[-74,40.4],[-74.2,39.7],[-74.9,38.9],[-75,39.2],[-75.2,39.2],[-75.5,39.5],[-75.3,39],[-75.1,38.8],[-75.1,38.4],[-75.4,38],[-75.9,37.2],[-76,37.3],[-75.7,37.9],[-76.2,38.3],[-76.4,39.1],[-76.5,38.7],[-76.3,38.1],[-77,38.2],[-76.3,37.9],[-76.3,37],[-76,36.9],[-75.9,36.6],[-75.7,35.6],[-76.4,34.8],[-77.4,34.5],[-78.1,33.9],[-78.6,33.9],[-79.1,33.5],[-79.2,33.2],[-80.3,32.5],[-80.9,32],[-81.3,31.4],[-81.5,30.7],[-81.3,30],[-81,29.2],[-80.5,28.5],[-80.5,28],[-80.1,26.9],[-80.1,26.2],[-80.1,25.8],[-80.4,25.2],[-80.7,25.1],[-81.2,25.2],[-81.3,25.6],[-81.7,25.9],[-82.2,26.7],[-82.7,27.5],[-82.9,27.9],[-82.6,28.6],[-82.9,29.1],[-83.7,29.9],[-84.1,30.1],[-85.1,29.6],[-85.3,29.7],[-85.8,30.2],[-86.4,30.4],[-87.5,30.3],[-88.4,30.4],[-89.2,30.3],[-89.6,30.2],[-89.4,29.9],[-89.4,29.5],[-89.2,29.3],[-89.4,29.2],[-89.8,29.3],[-90.2,29.1],[-90.9,29.1],[-91.6,29.7],[-92.5,29.6],[-93.2,29.8],[-93.8,29.7],[-94.7,29.5],[-95.6,28.7],[-96.6,28.3],[-97.1,27.8],[-97.4,27.4],[-97.4,26.7],[-97.3,26.2],[-97.1,25.9],[-97.5,25.8],[-98.2,26.1],[-99,26.4],[-99.3,26.8],[-99.5,27.5],[-100.1,28.1],[-100.5,28.7],[-101,29.4],[-101.7,29.8],[-102.5,29.8],[-103.1,29],[-103.9,29.3],[-104.5,29.6],[-104.7,30.1],[-105,30.6],[-105.6,31.1],[-106.1,31.4],[-106.5,31.8],[-108.2,31.8],[-108.2,31.3],[-109,31.3],[-111,31.3],[-113.3,32],[-114.8,32.5],[-114.7,32.7],[-116,32.6],[-117.1,32.5],[-117.3,33],[-117.9,33.6],[-118.4,33.7],[-118.5,34],[-119.1,34.1],[-119.4,34.3],[-120.4,34.4],[-120.6,34.6],[-120.7,35.2],[-121.7,36.2],[-122.5,37.6],[-122.5,37.8],[-123,38.1],[-123.7,39],[-123.9,39.8],[-124.4,40.3],[-124.2,41.1],[-124.2,42],[-124.5,42.8],[-124.1,43.7],[-124,44.6],[-123.9,45.5],[-124.1,46.9],[-124.4,47.7],[-124.7,48.2],[-124.6,48.4],[-123.1,48],[-122.6,47.1],[-122.3,47.4],[-122.5,48.2],[-122.8,49],[-120,49],[-117,49],[-116,49],[-113,49],[-110,49],[-107,49],[-104,49],[-100.6,49],[-97.2,49],[-95.2,49],[-95.2,49.4],[-94.8,49.4]]],[[[-153,57.1],[-154,56.7],[-154.5,57],[-154.7,57.5],[-153.8,57.8],[-153.2,58],[-152.6,57.9],[-152.1,57.6],[-153,57.1]]],[[[-165.6,59.9],[-166.2,59.8],[-166.8,59.9],[-167.5,60.2],[-166.5,60.4],[-165.7,60.3],[-165.6,59.9]]],[[[-171.7,63.8],[-171.1,63.6],[-170.5,63.7],[-169.7,63.4],[-168.7,63.3],[-168.8,63.2],[-169.5,63],[-170.3,63.2],[-170.7,63.4],[-171.6,63.3],[-171.8,63.4],[-171.7,63.8]]],[[[-155.1,71.1],[-154.3,70.7],[-153.9,70.9],[-152.2,70.8],[-152.3,70.6],[-150.7,70.4],[-149.7,70.5],[-147.6,70.2],[-145.7,70.1],[-144.9,70],[-143.6,70.2],[-142.1,69.9],[-141,69.7],[-141,69.7],[-141,66],[-141,60.3],[-140,60.3],[-139,60],[-138.3,59.6],[-137.5,58.9],[-136.5,59.5],[-135.5,59.8],[-134.9,59.3],[-134.3,58.9],[-133.4,58.4],[-132.7,57.7],[-131.7,56.6],[-130,55.9],[-130,55.3],[-130.5,54.8],[-131.1,55.2],[-132,55.5],[-132.3,56.4],[-133.5,57.2],[-134.1,58.1],[-135,58.2],[-136.6,58.2],[-137.8,58.5],[-139.9,59.5],[-140.8,59.7],[-142.6,60.1],[-144,60],[-145.9,60.5],[-147.1,60.9],[-148.2,60.7],[-148,60],[-148.6,59.9],[-149.7,59.7],[-150.6,59.4],[-151.7,59.2],[-151.9,59.7],[-151.4,60.7],[-150.3,61],[-150.6,61.3],[-151.9,60.7],[-152.6,60.1],[-154,59.4],[-153.3,58.9],[-154.2,58.1],[-155.3,57.7],[-156.3,57.4],[-156.6,57],[-158.1,56.5],[-158.4,56],[-159.6,55.6],[-160.3,55.6],[-161.2,55.4],[-162.2,55],[-163.1,54.7],[-164.8,54.4],[-164.9,54.6],[-163.8,55],[-162.9,55.3],[-161.8,55.9],[-160.6,56],[-160.1,56.4],[-158.7,57],[-158.5,57.2],[-157.7,57.6],[-157.6,58.3],[-157,58.9],[-158.2,58.6],[-158.5,58.8],[-159.1,58.4],[-159.7,58.9],[-160,58.6],[-160.4,59.1],[-161.4,58.7],[-162,58.7],[-162.1,59.3],[-161.9,59.6],[-162.5,60],[-163.8,59.8],[-164.7,60.3],[-165.3,60.5],[-165.4,61.1],[-166.1,61.5],[-165.7,62.1],[-164.9,62.6],[-164.6,63.1],[-163.8,63.2],[-163.1,63.1],[-162.3,63.5],[-161.5,63.5],[-160.8,63.8],[-161,64.2],[-161.5,64.4],[-160.8,64.8],[-161.4,64.8],[-162.5,64.6],[-162.8,64.3],[-163.5,64.6],[-165,64.4],[-166.4,64.7],[-166.8,65.1],[-168.1,65.7],[-166.7,66.1],[-164.5,66.6],[-163.7,66.6],[-163.8,66.1],[-161.7,66.1],[-162.5,66.7],[-163.7,67.1],[-164.4,67.6],[-165.4,68],[-166.8,68.4],[-166.2,68.9],[-164.4,68.9],[-163.2,69.4],[-162.9,69.9],[-161.9,70.3],[-160.9,70.4],[-159,70.9],[-158.1,70.8],[-156.6,71.4],[-155.1,71.1]]]]},"id":"373"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[66.5,37.4],[66.5,38],[65.2,38.4],[64.2,38.9],[63.5,39.4],[62.4,40.1],[61.9,41.1],[61.5,41.3],[60.5,41.2],[60.1,41.4],[60,42.2],[58.6,42.8],[57.8,42.2],[56.9,41.8],[57.1,41.3],[56,41.3],[55.9,45],[58.5,45.6],[58.7,45.5],[60.2,44.8],[61.1,44.4],[62,43.5],[63.2,43.7],[64.9,43.7],[66.1,43],[66,42],[66.5,42],[66.7,41.2],[68,41.1],[68.3,40.7],[68.6,40.7],[69.1,41.4],[70.4,42.1],[71,42.3],[71.3,42.2],[70.4,41.5],[71.2,41.1],[71.9,41.4],[73.1,40.9],[71.8,40.1],[71,40.2],[70.6,40.2],[70.5,40.5],[70.7,41],[69.3,40.7],[69,40.1],[68.5,39.5],[67.7,39.6],[67.4,39.1],[68.2,38.9],[68.4,38.2],[67.8,37.1],[67.1,37.4],[66.5,37.4]]]},"id":"363"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-71.3,11.8],[-71.4,11.5],[-71.9,11.4],[-71.6,11],[-71.6,10.4],[-72.1,9.9],[-71.7,9.1],[-71.3,9.1],[-71,9.9],[-71.4,10.2],[-71.4,11],[-70.2,11.4],[-70.3,11.8],[-69.9,12.2],[-69.6,11.5],[-68.9,11.4],[-68.2,10.9],[-68.2,10.6],[-67.3,10.5],[-66.2,10.6],[-65.7,10.2],[-64.9,10.1],[-64.3,10.4],[-64.3,10.6],[-63.1,10.7],[-61.9,10.7],[-62.7,10.4],[-62.4,9.9],[-61.6,9.9],[-60.8,9.4],[-60.7,8.6],[-60.2,8.6],[-59.8,8.4],[-60.6,7.8],[-60.6,7.4],[-60.3,7],[-60.5,6.9],[-61.2,6.7],[-61.1,6.2],[-61.4,6],[-60.7,5.2],[-60.6,4.9],[-61,4.5],[-62.1,4.2],[-62.8,4],[-63.1,3.8],[-63.9,4],[-64.6,4.1],[-64.8,4.1],[-64.4,3.8],[-64.4,3.1],[-64.3,2.5],[-63.4,2.4],[-63.4,2.2],[-64.1,1.9],[-64.2,1.5],[-64.6,1.3],[-65.4,1.1],[-65.5,0.8],[-66.3,0.7],[-66.9,1.3],[-67.2,2.3],[-67.4,2.6],[-67.8,2.8],[-67.3,3.3],[-67.3,3.5],[-67.6,3.8],[-67.8,4.5],[-67.7,5.2],[-67.5,5.6],[-67.3,6.1],[-67.7,6.3],[-68.3,6.2],[-69,6.2],[-69.4,6.1],[-70.1,7],[-70.7,7.1],[-72,7],[-72.2,7.3],[-72.4,7.4],[-72.5,7.6],[-72.4,8],[-72.4,8.4],[-72.7,8.6],[-72.8,9.1],[-73.3,9.2],[-73,9.7],[-72.9,10.5],[-72.6,10.8],[-72.2,11.1],[-72,11.6],[-71.3,11.8]]]},"id":"365"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[108.1,21.6],[106.7,20.7],[105.9,19.8],[105.7,19.1],[106.4,18],[107.4,16.7],[108.3,16.1],[108.9,15.3],[109.3,13.4],[109.2,11.7],[108.4,11],[107.2,10.4],[106.4,9.5],[105.2,8.6],[104.8,9.2],[105.1,9.9],[104.3,10.5],[105.2,10.9],[106.2,11],[105.8,11.6],[107.5,12.3],[107.6,13.5],[107.4,14.2],[107.6,15.2],[107.3,15.9],[106.6,16.6],[105.9,17.5],[105.1,18.7],[103.9,19.3],[104.2,19.6],[104.8,19.9],[104.4,20.8],[103.2,20.8],[102.8,21.7],[102.2,22.5],[102.7,22.7],[103.5,22.7],[104.5,22.8],[105.3,23.4],[105.8,23],[106.7,22.8],[106.6,22.2],[107,21.8],[108.1,21.6]]]},"id":"366"},
    +{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[167.8,-16.5],[167.5,-16.6],[167.2,-16.2],[167.2,-15.9],[167.8,-16.5]]],[[[167.1,-14.9],[167.3,-15.7],[167,-15.6],[166.8,-15.7],[166.6,-15.4],[166.6,-14.6],[167.1,-14.9]]]]},"id":"364"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[53.1,16.7],[52.4,16.4],[52.2,15.9],[52.2,15.6],[51.2,15.2],[49.6,14.7],[48.7,14],[48.2,13.9],[47.9,14],[47.4,13.6],[46.7,13.4],[45.9,13.3],[45.6,13.3],[45.4,13],[45.1,13],[45,12.7],[44.5,12.7],[44.2,12.6],[43.5,12.6],[43.2,13.2],[43.3,13.8],[43.1,14.1],[42.9,14.8],[42.6,15.2],[42.8,15.3],[42.7,15.7],[42.8,15.9],[42.8,16.3],[43.2,16.7],[43.1,17.1],[43.4,17.6],[43.8,17.3],[44.1,17.4],[45.2,17.4],[45.4,17.3],[46.4,17.2],[46.7,17.3],[47,16.9],[47.5,17.1],[48.2,18.2],[49.1,18.6],[52,19],[52.8,17.3],[53.1,16.7]]]},"id":"371"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.5,-29.3],[31.3,-29.4],[30.9,-29.9],[30.6,-30.4],[30.1,-31.1],[28.9,-32.2],[28.2,-32.8],[27.5,-33.2],[26.4,-33.6],[25.9,-33.7],[25.8,-33.9],[25.2,-33.8],[24.7,-34],[23.6,-33.8],[23,-33.9],[22.6,-33.9],[21.5,-34.3],[20.7,-34.4],[20.1,-34.8],[19.6,-34.8],[19.2,-34.5],[18.9,-34.4],[18.4,-34],[18.4,-34.1],[18.2,-33.9],[18.3,-33.3],[17.9,-32.6],[18.2,-32.4],[18.2,-31.7],[17.6,-30.7],[17.1,-29.9],[17.1,-29.9],[16.3,-28.6],[16.8,-28.1],[17.2,-28.4],[17.4,-28.8],[17.8,-28.9],[18.5,-29],[19,-29],[19.9,-28.5],[19.9,-24.8],[20.2,-24.9],[20.8,-25.9],[20.7,-26.5],[20.9,-26.8],[21.6,-26.7],[22.1,-26.3],[22.6,-26],[22.8,-25.5],[23.3,-25.3],[23.7,-25.4],[24.2,-25.7],[25,-25.7],[25.7,-25.5],[25.8,-25.2],[25.9,-24.7],[26.5,-24.6],[26.8,-24.2],[27.1,-23.6],[28,-22.8],[29.4,-22.1],[29.8,-22.1],[30.3,-22.3],[30.7,-22.2],[31.2,-22.3],[31.7,-23.7],[31.9,-24.4],[31.8,-25.5],[31.8,-25.8],[31.3,-25.7],[31,-25.7],[30.9,-26],[30.7,-26.4],[30.7,-26.7],[31.3,-27.3],[31.9,-27.2],[32.1,-26.7],[32.8,-26.7],[32.6,-27.5],[32.5,-28.3],[32.2,-28.8],[31.5,-29.3]],[[29,-29],[28.5,-28.6],[28.1,-28.9],[27.5,-29.2],[27,-29.9],[27.7,-30.6],[28.1,-30.5],[28.3,-30.2],[28.8,-30.1],[29,-29.7],[29.3,-29.3],[29,-29]]]},"id":"344"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[32.8,-9.2],[33.2,-9.7],[33.5,-10.5],[33.3,-10.8],[33.1,-11.6],[33.3,-12.4],[33,-12.8],[32.7,-13.7],[33.2,-14],[30.2,-14.8],[30.3,-15.5],[29.5,-15.6],[28.9,-16],[28.8,-16.4],[28.5,-16.5],[27.6,-17.3],[27,-17.9],[26.7,-18],[26.4,-17.8],[25.3,-17.7],[25.1,-17.7],[25.1,-17.6],[24.7,-17.4],[24,-17.3],[23.2,-17.5],[22.6,-16.9],[21.9,-16.1],[21.9,-12.9],[24,-12.9],[23.9,-12.6],[24.1,-12.2],[23.9,-11.7],[24,-11.2],[23.9,-10.9],[24.3,-11],[24.3,-11.3],[24.8,-11.2],[25.4,-11.3],[25.8,-11.8],[26.6,-11.9],[27.2,-11.6],[27.4,-12.1],[28.2,-12.3],[28.5,-12.7],[28.9,-13.2],[29.7,-13.3],[29.6,-12.2],[29.3,-12.4],[28.6,-12],[28.4,-11.8],[28.5,-10.8],[28.7,-9.6],[28.4,-9.2],[28.7,-8.5],[29,-8.4],[30.3,-8.2],[30.7,-8.3],[31.2,-8.6],[31.6,-8.8],[32.2,-8.9],[32.8,-9.2]]]},"id":"367"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.2,-22.3],[30.7,-22.2],[30.3,-22.3],[29.8,-22.1],[29.4,-22.1],[28.8,-21.6],[28,-21.5],[27.7,-20.9],[27.7,-20.5],[27.3,-20.4],[26.2,-19.3],[25.9,-18.7],[25.6,-18.5],[25.3,-17.7],[26.4,-17.8],[26.7,-18],[27,-17.9],[27.6,-17.3],[28.5,-16.5],[28.8,-16.4],[28.9,-16],[29.5,-15.6],[30.3,-15.5],[30.3,-15.9],[31.2,-15.9],[31.6,-16.1],[31.9,-16.3],[32.3,-16.4],[32.8,-16.7],[32.8,-18],[32.7,-18.7],[32.6,-19.4],[32.8,-19.7],[32.7,-20.3],[32.5,-20.4],[32.2,-21.1],[31.2,-22.3]]]},"id":"368"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[32.7,35.1],[32.8,35.1],[32.9,35.4],[33.7,35.4],[34.6,35.7],[33.9,35.2],[34,35.1],[34,35],[33,34.6],[32.5,34.7],[32.3,35.1],[32.7,35.1]]]},"id":"271"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[47.8,8],[46.9,8],[43.7,9.2],[43.3,9.5],[42.9,10],[42.6,10.6],[42.8,10.9],[43.1,11.5],[43.5,11.3],[43.7,10.9],[44.1,10.4],[44.6,10.4],[45.6,10.7],[46.6,10.8],[47.5,11.1],[48,11.2],[48.4,11.4],[48.9,11.4],[48.9,11.4],[49.3,11.4],[49.7,11.6],[50.3,11.7],[50.7,12],[51.1,12],[51.1,11.7],[51,11.2],[51,10.6],[50.8,10.3],[50.6,9.2],[50.1,8.1],[49.5,6.8],[48.6,5.3],[47.7,4.2],[46.6,2.9],[45.6,2],[44.1,1.1],[43.1,0.3],[42,-0.9],[41.8,-1.4],[41.6,-1.7],[41,-0.9],[41,2.8],[41.9,3.9],[42.1,4.2],[42.8,4.3],[43.7,5],[45,5],[47.8,8]]]},"id":"335"},
    +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-13.1,27.7],[-8.7,27.7],[-8.7,27.6],[-8.7,27.4],[-8.7,25.9],[-12,25.9],[-11.9,23.4],[-12.9,23.3],[-13.1,22.8],[-12.9,21.3],[-16.8,21.3],[-17.1,21],[-17,21.9],[-16.6,22.2],[-16.3,22.7],[-16.3,23],[-16,23.7],[-15.4,24.4],[-15.1,24.5],[-14.8,25.1],[-14.8,25.6],[-14.4,26.3],[-13.8,26.6],[-13.1,27.6],[-13.1,27.7],[-13.1,27.7]]]},"id":"885"}
    +]}
    diff --git a/mapbox-gl/examples/data/geojson/countries.geojson b/mapbox-gl/examples/data/geojson/countries.geojson
    new file mode 100644
    index 0000000000..9b7e0dbc59
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/countries.geojson
    @@ -0,0 +1,181 @@
    +{"type":"FeatureCollection","features":[
    +{"type":"Feature","id":"AFG","properties":{"name":"Afghanistan"},"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[62.230651,35.270664],[62.984662,35.404041],[63.193538,35.857166],[63.982896,36.007957],[64.546479,36.312073],[64.746105,37.111818],[65.588948,37.305217],[65.745631,37.661164],[66.217385,37.39379],[66.518607,37.362784],[67.075782,37.356144],[67.83,37.144994],[68.135562,37.023115],[68.859446,37.344336],[69.196273,37.151144],[69.518785,37.608997],[70.116578,37.588223],[70.270574,37.735165],[70.376304,38.138396],[70.806821,38.486282],[71.348131,38.258905],[71.239404,37.953265],[71.541918,37.905774],[71.448693,37.065645],[71.844638,36.738171],[72.193041,36.948288],[72.63689,37.047558],[73.260056,37.495257],[73.948696,37.421566],[74.980002,37.41999],[75.158028,37.133031],[74.575893,37.020841],[74.067552,36.836176],[72.920025,36.720007],[71.846292,36.509942],[71.262348,36.074388],[71.498768,35.650563],[71.613076,35.153203],[71.115019,34.733126],[71.156773,34.348911],[70.881803,33.988856],[69.930543,34.02012],[70.323594,33.358533],[69.687147,33.105499],[69.262522,32.501944],[69.317764,31.901412],[68.926677,31.620189],[68.556932,31.71331],[67.792689,31.58293],[67.683394,31.303154],[66.938891,31.304911],[66.381458,30.738899],[66.346473,29.887943],[65.046862,29.472181],[64.350419,29.560031],[64.148002,29.340819],[63.550261,29.468331],[62.549857,29.318572],[60.874248,29.829239],[61.781222,30.73585],[61.699314,31.379506],[60.941945,31.548075],[60.863655,32.18292],[60.536078,32.981269],[60.9637,33.528832],[60.52843,33.676446],[60.803193,34.404102],[61.210817,35.650072]]]}},
    +{"type":"Feature","id":"AGO","properties":{"name":"Angola"},"geometry":{"type":"MultiPolygon","coordinates":[[[[16.326528,-5.87747],[16.57318,-6.622645],[16.860191,-7.222298],[17.089996,-7.545689],[17.47297,-8.068551],[18.134222,-7.987678],[18.464176,-7.847014],[19.016752,-7.988246],[19.166613,-7.738184],[19.417502,-7.155429],[20.037723,-7.116361],[20.091622,-6.94309],[20.601823,-6.939318],[20.514748,-7.299606],[21.728111,-7.290872],[21.746456,-7.920085],[21.949131,-8.305901],[21.801801,-8.908707],[21.875182,-9.523708],[22.208753,-9.894796],[22.155268,-11.084801],[22.402798,-10.993075],[22.837345,-11.017622],[23.456791,-10.867863],[23.912215,-10.926826],[24.017894,-11.237298],[23.904154,-11.722282],[24.079905,-12.191297],[23.930922,-12.565848],[24.016137,-12.911046],[21.933886,-12.898437],[21.887843,-16.08031],[22.562478,-16.898451],[23.215048,-17.523116],[21.377176,-17.930636],[18.956187,-17.789095],[18.263309,-17.309951],[14.209707,-17.353101],[14.058501,-17.423381],[13.462362,-16.971212],[12.814081,-16.941343],[12.215461,-17.111668],[11.734199,-17.301889],[11.640096,-16.673142],[11.778537,-15.793816],[12.123581,-14.878316],[12.175619,-14.449144],[12.500095,-13.5477],[12.738479,-13.137906],[13.312914,-12.48363],[13.633721,-12.038645],[13.738728,-11.297863],[13.686379,-10.731076],[13.387328,-10.373578],[13.120988,-9.766897],[12.87537,-9.166934],[12.929061,-8.959091],[13.236433,-8.562629],[12.93304,-7.596539],[12.728298,-6.927122],[12.227347,-6.294448],[12.322432,-6.100092],[12.735171,-5.965682],[13.024869,-5.984389],[13.375597,-5.864241],[16.326528,-5.87747]]],[[[12.436688,-5.684304],[12.182337,-5.789931],[11.914963,-5.037987],[12.318608,-4.60623],[12.62076,-4.438023],[12.995517,-4.781103],[12.631612,-4.991271],[12.468004,-5.248362],[12.436688,-5.684304]]]]}},
    +{"type":"Feature","id":"ALB","properties":{"name":"Albania"},"geometry":{"type":"Polygon","coordinates":[[[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855404]]]}},
    +{"type":"Feature","id":"ARE","properties":{"name":"United Arab Emirates"},"geometry":{"type":"Polygon","coordinates":[[[51.579519,24.245497],[51.757441,24.294073],[51.794389,24.019826],[52.577081,24.177439],[53.404007,24.151317],[54.008001,24.121758],[54.693024,24.797892],[55.439025,25.439145],[56.070821,26.055464],[56.261042,25.714606],[56.396847,24.924732],[55.886233,24.920831],[55.804119,24.269604],[55.981214,24.130543],[55.528632,23.933604],[55.525841,23.524869],[55.234489,23.110993],[55.208341,22.70833],[55.006803,22.496948],[52.000733,23.001154],[51.617708,24.014219],[51.579519,24.245497]]]}},
    +{"type":"Feature","id":"ARG","properties":{"name":"Argentina"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-65.5,-55.2],[-66.45,-55.25],[-66.95992,-54.89681],[-67.56244,-54.87001],[-68.63335,-54.8695],[-68.63401,-52.63637],[-68.25,-53.1],[-67.75,-53.85],[-66.45,-54.45],[-65.05,-54.7],[-65.5,-55.2]]],[[[-64.964892,-22.075862],[-64.377021,-22.798091],[-63.986838,-21.993644],[-62.846468,-22.034985],[-62.685057,-22.249029],[-60.846565,-23.880713],[-60.028966,-24.032796],[-58.807128,-24.771459],[-57.777217,-25.16234],[-57.63366,-25.603657],[-58.618174,-27.123719],[-57.60976,-27.395899],[-56.486702,-27.548499],[-55.695846,-27.387837],[-54.788795,-26.621786],[-54.625291,-25.739255],[-54.13005,-25.547639],[-53.628349,-26.124865],[-53.648735,-26.923473],[-54.490725,-27.474757],[-55.162286,-27.881915],[-56.2909,-28.852761],[-57.625133,-30.216295],[-57.874937,-31.016556],[-58.14244,-32.044504],[-58.132648,-33.040567],[-58.349611,-33.263189],[-58.427074,-33.909454],[-58.495442,-34.43149],[-57.22583,-35.288027],[-57.362359,-35.97739],[-56.737487,-36.413126],[-56.788285,-36.901572],[-57.749157,-38.183871],[-59.231857,-38.72022],[-61.237445,-38.928425],[-62.335957,-38.827707],[-62.125763,-39.424105],[-62.330531,-40.172586],[-62.145994,-40.676897],[-62.745803,-41.028761],[-63.770495,-41.166789],[-64.73209,-40.802677],[-65.118035,-41.064315],[-64.978561,-42.058001],[-64.303408,-42.359016],[-63.755948,-42.043687],[-63.458059,-42.563138],[-64.378804,-42.873558],[-65.181804,-43.495381],[-65.328823,-44.501366],[-65.565269,-45.036786],[-66.509966,-45.039628],[-67.293794,-45.551896],[-67.580546,-46.301773],[-66.597066,-47.033925],[-65.641027,-47.236135],[-65.985088,-48.133289],[-67.166179,-48.697337],[-67.816088,-49.869669],[-68.728745,-50.264218],[-69.138539,-50.73251],[-68.815561,-51.771104],[-68.149995,-52.349983],[-68.571545,-52.299444],[-69.498362,-52.142761],[-71.914804,-52.009022],[-72.329404,-51.425956],[-72.309974,-50.67701],[-72.975747,-50.74145],[-73.328051,-50.378785],[-73.415436,-49.318436],[-72.648247,-48.878618],[-72.331161,-48.244238],[-72.447355,-47.738533],[-71.917258,-46.884838],[-71.552009,-45.560733],[-71.659316,-44.973689],[-71.222779,-44.784243],[-71.329801,-44.407522],[-71.793623,-44.207172],[-71.464056,-43.787611],[-71.915424,-43.408565],[-72.148898,-42.254888],[-71.746804,-42.051386],[-71.915734,-40.832339],[-71.680761,-39.808164],[-71.413517,-38.916022],[-70.814664,-38.552995],[-71.118625,-37.576827],[-71.121881,-36.658124],[-70.364769,-36.005089],[-70.388049,-35.169688],[-69.817309,-34.193571],[-69.814777,-33.273886],[-70.074399,-33.09121],[-70.535069,-31.36501],[-69.919008,-30.336339],[-70.01355,-29.367923],[-69.65613,-28.459141],[-69.001235,-27.521214],[-68.295542,-26.89934],[-68.5948,-26.506909],[-68.386001,-26.185016],[-68.417653,-24.518555],[-67.328443,-24.025303],[-66.985234,-22.986349],[-67.106674,-22.735925],[-66.273339,-21.83231],[-64.964892,-22.075862]]]]}},
    +{"type":"Feature","id":"ARM","properties":{"name":"Armenia"},"geometry":{"type":"Polygon","coordinates":[[[43.582746,41.092143],[44.97248,41.248129],[45.179496,40.985354],[45.560351,40.81229],[45.359175,40.561504],[45.891907,40.218476],[45.610012,39.899994],[46.034534,39.628021],[46.483499,39.464155],[46.50572,38.770605],[46.143623,38.741201],[45.735379,39.319719],[45.739978,39.473999],[45.298145,39.471751],[45.001987,39.740004],[44.79399,39.713003],[44.400009,40.005],[43.656436,40.253564],[43.752658,40.740201],[43.582746,41.092143]]]}},
    +{"type":"Feature","id":"ATA","properties":{"name":"Antarctica"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-59.572095,-80.040179],[-59.865849,-80.549657],[-60.159656,-81.000327],[-62.255393,-80.863178],[-64.488125,-80.921934],[-65.741666,-80.588827],[-65.741666,-80.549657],[-66.290031,-80.255773],[-64.037688,-80.294944],[-61.883246,-80.39287],[-61.138976,-79.981371],[-60.610119,-79.628679],[-59.572095,-80.040179]]],[[[-159.208184,-79.497059],[-161.127601,-79.634209],[-162.439847,-79.281465],[-163.027408,-78.928774],[-163.066604,-78.869966],[-163.712896,-78.595667],[-163.712896,-78.595667],[-163.105801,-78.223338],[-161.245113,-78.380176],[-160.246208,-78.693645],[-159.482405,-79.046338],[-159.208184,-79.497059]]],[[[-45.154758,-78.04707],[-43.920828,-78.478103],[-43.48995,-79.08556],[-43.372438,-79.516645],[-43.333267,-80.026123],[-44.880537,-80.339644],[-46.506174,-80.594357],[-48.386421,-80.829485],[-50.482107,-81.025442],[-52.851988,-80.966685],[-54.164259,-80.633528],[-53.987991,-80.222028],[-51.853134,-79.94773],[-50.991326,-79.614623],[-50.364595,-79.183487],[-49.914131,-78.811209],[-49.306959,-78.458569],[-48.660616,-78.047018],[-48.660616,-78.047019],[-48.151396,-78.04707],[-46.662857,-77.831476],[-45.154758,-78.04707]]],[[[-121.211511,-73.50099],[-119.918851,-73.657725],[-118.724143,-73.481353],[-119.292119,-73.834097],[-120.232217,-74.08881],[-121.62283,-74.010468],[-122.621735,-73.657778],[-122.621735,-73.657777],[-122.406245,-73.324619],[-121.211511,-73.50099]]],[[[-125.559566,-73.481353],[-124.031882,-73.873268],[-124.619469,-73.834097],[-125.912181,-73.736118],[-127.28313,-73.461769],[-127.28313,-73.461768],[-126.558472,-73.246226],[-125.559566,-73.481353]]],[[[-98.98155,-71.933334],[-97.884743,-72.070535],[-96.787937,-71.952971],[-96.20035,-72.521205],[-96.983765,-72.442864],[-98.198083,-72.482035],[-99.432013,-72.442864],[-100.783455,-72.50162],[-101.801868,-72.305663],[-102.330725,-71.894164],[-102.330725,-71.894164],[-101.703967,-71.717792],[-100.430919,-71.854993],[-98.98155,-71.933334]]],[[[-68.451346,-70.955823],[-68.333834,-71.406493],[-68.510128,-71.798407],[-68.784297,-72.170736],[-69.959471,-72.307885],[-71.075889,-72.503842],[-72.388134,-72.484257],[-71.8985,-72.092343],[-73.073622,-72.229492],[-74.19004,-72.366693],[-74.953895,-72.072757],[-75.012625,-71.661258],[-73.915819,-71.269345],[-73.915819,-71.269344],[-73.230331,-71.15178],[-72.074717,-71.190951],[-71.780962,-70.681473],[-71.72218,-70.309196],[-71.741791,-69.505782],[-71.173815,-69.035475],[-70.253252,-68.87874],[-69.724447,-69.251017],[-69.489422,-69.623346],[-69.058518,-70.074016],[-68.725541,-70.505153],[-68.451346,-70.955823]]],[[[-58.614143,-64.152467],[-59.045073,-64.36801],[-59.789342,-64.211223],[-60.611928,-64.309202],[-61.297416,-64.54433],[-62.0221,-64.799094],[-62.51176,-65.09303],[-62.648858,-65.484942],[-62.590128,-65.857219],[-62.120079,-66.190326],[-62.805567,-66.425505],[-63.74569,-66.503847],[-64.294106,-66.837004],[-64.881693,-67.150474],[-65.508425,-67.58161],[-65.665082,-67.953887],[-65.312545,-68.365335],[-64.783715,-68.678908],[-63.961103,-68.913984],[-63.1973,-69.227556],[-62.785955,-69.619419],[-62.570516,-69.991747],[-62.276736,-70.383661],[-61.806661,-70.716768],[-61.512906,-71.089045],[-61.375809,-72.010074],[-61.081977,-72.382351],[-61.003661,-72.774265],[-60.690269,-73.166179],[-60.827367,-73.695242],[-61.375809,-74.106742],[-61.96337,-74.439848],[-63.295201,-74.576997],[-63.74569,-74.92974],[-64.352836,-75.262847],[-65.860987,-75.635124],[-67.192818,-75.79191],[-68.446282,-76.007452],[-69.797724,-76.222995],[-70.600724,-76.634494],[-72.206776,-76.673665],[-73.969536,-76.634494],[-75.555977,-76.712887],[-77.24037,-76.712887],[-76.926979,-77.104802],[-75.399294,-77.28107],[-74.282876,-77.55542],[-73.656119,-77.908112],[-74.772536,-78.221633],[-76.4961,-78.123654],[-77.925858,-78.378419],[-77.984666,-78.789918],[-78.023785,-79.181833],[-76.848637,-79.514939],[-76.633224,-79.887216],[-75.360097,-80.259545],[-73.244852,-80.416331],[-71.442946,-80.69063],[-70.013163,-81.004151],[-68.191646,-81.317672],[-65.704279,-81.474458],[-63.25603,-81.748757],[-61.552026,-82.042692],[-59.691416,-82.37585],[-58.712121,-82.846106],[-58.222487,-83.218434],[-57.008117,-82.865691],[-55.362894,-82.571755],[-53.619771,-82.258235],[-51.543644,-82.003521],[-49.76135,-81.729171],[-47.273931,-81.709586],[-44.825708,-81.846735],[-42.808363,-82.081915],[-42.16202,-81.65083],[-40.771433,-81.356894],[-38.244818,-81.337309],[-36.26667,-81.121715],[-34.386397,-80.906172],[-32.310296,-80.769023],[-30.097098,-80.592651],[-28.549802,-80.337938],[-29.254901,-79.985195],[-29.685805,-79.632503],[-29.685805,-79.260226],[-31.624808,-79.299397],[-33.681324,-79.456132],[-35.639912,-79.456132],[-35.914107,-79.083855],[-35.77701,-78.339248],[-35.326546,-78.123654],[-33.896763,-77.888526],[-32.212369,-77.65345],[-30.998051,-77.359515],[-29.783732,-77.065579],[-28.882779,-76.673665],[-27.511752,-76.497345],[-26.160336,-76.360144],[-25.474822,-76.281803],[-23.927552,-76.24258],[-22.458598,-76.105431],[-21.224694,-75.909474],[-20.010375,-75.674346],[-18.913543,-75.439218],[-17.522982,-75.125698],[-16.641589,-74.79254],[-15.701491,-74.498604],[-15.40771,-74.106742],[-16.46532,-73.871614],[-16.112784,-73.460114],[-15.446855,-73.146542],[-14.408805,-72.950585],[-13.311973,-72.715457],[-12.293508,-72.401936],[-11.510067,-72.010074],[-11.020433,-71.539767],[-10.295774,-71.265416],[-9.101015,-71.324224],[-8.611381,-71.65733],[-7.416622,-71.696501],[-7.377451,-71.324224],[-6.868232,-70.93231],[-5.790985,-71.030289],[-5.536375,-71.402617],[-4.341667,-71.461373],[-3.048981,-71.285053],[-1.795492,-71.167438],[-0.659489,-71.226246],[-0.228637,-71.637745],[0.868195,-71.304639],[1.886686,-71.128267],[3.022638,-70.991118],[4.139055,-70.853917],[5.157546,-70.618789],[6.273912,-70.462055],[7.13572,-70.246512],[7.742866,-69.893769],[8.48711,-70.148534],[9.525135,-70.011333],[10.249845,-70.48164],[10.817821,-70.834332],[11.953824,-70.638375],[12.404287,-70.246512],[13.422778,-69.972162],[14.734998,-70.030918],[15.126757,-70.403247],[15.949342,-70.030918],[17.026589,-69.913354],[18.201711,-69.874183],[19.259373,-69.893769],[20.375739,-70.011333],[21.452985,-70.07014],[21.923034,-70.403247],[22.569403,-70.697182],[23.666184,-70.520811],[24.841357,-70.48164],[25.977309,-70.48164],[27.093726,-70.462055],[28.09258,-70.324854],[29.150242,-70.20729],[30.031583,-69.93294],[30.971733,-69.75662],[31.990172,-69.658641],[32.754053,-69.384291],[33.302443,-68.835642],[33.870419,-68.502588],[34.908495,-68.659271],[35.300202,-69.012014],[36.16201,-69.247142],[37.200035,-69.168748],[37.905108,-69.52144],[38.649404,-69.776205],[39.667894,-69.541077],[40.020431,-69.109941],[40.921358,-68.933621],[41.959434,-68.600514],[42.938702,-68.463313],[44.113876,-68.267408],[44.897291,-68.051866],[45.719928,-67.816738],[46.503343,-67.601196],[47.44344,-67.718759],[48.344419,-67.366068],[48.990736,-67.091718],[49.930885,-67.111303],[50.753471,-66.876175],[50.949325,-66.523484],[51.791547,-66.249133],[52.614133,-66.053176],[53.613038,-65.89639],[54.53355,-65.818049],[55.414943,-65.876805],[56.355041,-65.974783],[57.158093,-66.249133],[57.255968,-66.680218],[58.137361,-67.013324],[58.744508,-67.287675],[59.939318,-67.405239],[60.605221,-67.679589],[61.427806,-67.953887],[62.387489,-68.012695],[63.19049,-67.816738],[64.052349,-67.405239],[64.992447,-67.620729],[65.971715,-67.738345],[66.911864,-67.855909],[67.891133,-67.934302],[68.890038,-67.934302],[69.712624,-68.972791],[69.673453,-69.227556],[69.555941,-69.678226],[68.596258,-69.93294],[67.81274,-70.305268],[67.949889,-70.697182],[69.066307,-70.677545],[68.929157,-71.069459],[68.419989,-71.441788],[67.949889,-71.853287],[68.71377,-72.166808],[69.869307,-72.264787],[71.024895,-72.088415],[71.573285,-71.696501],[71.906288,-71.324224],[72.454627,-71.010703],[73.08141,-70.716768],[73.33602,-70.364024],[73.864877,-69.874183],[74.491557,-69.776205],[75.62756,-69.737034],[76.626465,-69.619419],[77.644904,-69.462684],[78.134539,-69.07077],[78.428371,-68.698441],[79.113859,-68.326216],[80.093127,-68.071503],[80.93535,-67.875546],[81.483792,-67.542388],[82.051767,-67.366068],[82.776426,-67.209282],[83.775331,-67.30726],[84.676206,-67.209282],[85.655527,-67.091718],[86.752359,-67.150474],[87.477017,-66.876175],[87.986289,-66.209911],[88.358411,-66.484261],[88.828408,-66.954568],[89.67063,-67.150474],[90.630365,-67.228867],[91.5901,-67.111303],[92.608539,-67.189696],[93.548637,-67.209282],[94.17542,-67.111303],[95.017591,-67.170111],[95.781472,-67.385653],[96.682399,-67.248504],[97.759646,-67.248504],[98.68021,-67.111303],[99.718182,-67.248504],[100.384188,-66.915346],[100.893356,-66.58224],[101.578896,-66.30789],[102.832411,-65.563284],[103.478676,-65.700485],[104.242557,-65.974783],[104.90846,-66.327527],[106.181561,-66.934931],[107.160881,-66.954568],[108.081393,-66.954568],[109.15864,-66.837004],[110.235835,-66.699804],[111.058472,-66.425505],[111.74396,-66.13157],[112.860378,-66.092347],[113.604673,-65.876805],[114.388088,-66.072762],[114.897308,-66.386283],[115.602381,-66.699804],[116.699161,-66.660633],[117.384701,-66.915346],[118.57946,-67.170111],[119.832924,-67.268089],[120.871,-67.189696],[121.654415,-66.876175],[122.320369,-66.562654],[123.221296,-66.484261],[124.122274,-66.621462],[125.160247,-66.719389],[126.100396,-66.562654],[127.001427,-66.562654],[127.882768,-66.660633],[128.80328,-66.758611],[129.704259,-66.58224],[130.781454,-66.425505],[131.799945,-66.386283],[132.935896,-66.386283],[133.85646,-66.288304],[134.757387,-66.209963],[135.031582,-65.72007],[135.070753,-65.308571],[135.697485,-65.582869],[135.873805,-66.033591],[136.206705,-66.44509],[136.618049,-66.778197],[137.460271,-66.954568],[138.596223,-66.895761],[139.908442,-66.876175],[140.809421,-66.817367],[142.121692,-66.817367],[143.061842,-66.797782],[144.374061,-66.837004],[145.490427,-66.915346],[146.195552,-67.228867],[145.999699,-67.601196],[146.646067,-67.895131],[147.723263,-68.130259],[148.839629,-68.385024],[150.132314,-68.561292],[151.483705,-68.71813],[152.502247,-68.874813],[153.638199,-68.894502],[154.284567,-68.561292],[155.165857,-68.835642],[155.92979,-69.149215],[156.811132,-69.384291],[158.025528,-69.482269],[159.181013,-69.599833],[159.670699,-69.991747],[160.80665,-70.226875],[161.570479,-70.579618],[162.686897,-70.736353],[163.842434,-70.716768],[164.919681,-70.775524],[166.11444,-70.755938],[167.309095,-70.834332],[168.425616,-70.971481],[169.463589,-71.20666],[170.501665,-71.402617],[171.20679,-71.696501],[171.089227,-72.088415],[170.560422,-72.441159],[170.109958,-72.891829],[169.75737,-73.24452],[169.287321,-73.65602],[167.975101,-73.812806],[167.387489,-74.165498],[166.094803,-74.38104],[165.644391,-74.772954],[164.958851,-75.145283],[164.234193,-75.458804],[163.822797,-75.870303],[163.568239,-76.24258],[163.47026,-76.693302],[163.489897,-77.065579],[164.057873,-77.457442],[164.273363,-77.82977],[164.743464,-78.182514],[166.604126,-78.319611],[166.995781,-78.750748],[165.193876,-78.907483],[163.666217,-79.123025],[161.766385,-79.162248],[160.924162,-79.730482],[160.747894,-80.200737],[160.316964,-80.573066],[159.788211,-80.945395],[161.120016,-81.278501],[161.629287,-81.690001],[162.490992,-82.062278],[163.705336,-82.395435],[165.095949,-82.708956],[166.604126,-83.022477],[168.895665,-83.335998],[169.404782,-83.825891],[172.283934,-84.041433],[172.477049,-84.117914],[173.224083,-84.41371],[175.985672,-84.158997],[178.277212,-84.472518],[180,-84.71338],[-179.942499,-84.721443],[-179.058677,-84.139412],[-177.256772,-84.452933],[-177.140807,-84.417941],[-176.084673,-84.099259],[-175.947235,-84.110449],[-175.829882,-84.117914],[-174.382503,-84.534323],[-173.116559,-84.117914],[-172.889106,-84.061019],[-169.951223,-83.884647],[-168.999989,-84.117914],[-168.530199,-84.23739],[-167.022099,-84.570497],[-164.182144,-84.82521],[-161.929775,-85.138731],[-158.07138,-85.37391],[-155.192253,-85.09956],[-150.942099,-85.295517],[-148.533073,-85.609038],[-145.888918,-85.315102],[-143.107718,-85.040752],[-142.892279,-84.570497],[-146.829068,-84.531274],[-150.060732,-84.296146],[-150.902928,-83.904232],[-153.586201,-83.68869],[-153.409907,-83.23802],[-153.037759,-82.82652],[-152.665637,-82.454192],[-152.861517,-82.042692],[-154.526299,-81.768394],[-155.29018,-81.41565],[-156.83745,-81.102129],[-154.408787,-81.160937],[-152.097662,-81.004151],[-150.648293,-81.337309],[-148.865998,-81.043373],[-147.22075,-80.671045],[-146.417749,-80.337938],[-146.770286,-79.926439],[-148.062947,-79.652089],[-149.531901,-79.358205],[-151.588416,-79.299397],[-153.390322,-79.162248],[-155.329376,-79.064269],[-155.975668,-78.69194],[-157.268302,-78.378419],[-158.051768,-78.025676],[-158.365134,-76.889207],[-157.875474,-76.987238],[-156.974573,-77.300759],[-155.329376,-77.202728],[-153.742832,-77.065579],[-152.920247,-77.496664],[-151.33378,-77.398737],[-150.00195,-77.183143],[-148.748486,-76.908845],[-147.612483,-76.575738],[-146.104409,-76.47776],[-146.143528,-76.105431],[-146.496091,-75.733154],[-146.20231,-75.380411],[-144.909624,-75.204039],[-144.322037,-75.537197],[-142.794353,-75.34124],[-141.638764,-75.086475],[-140.209007,-75.06689],[-138.85759,-74.968911],[-137.5062,-74.733783],[-136.428901,-74.518241],[-135.214583,-74.302699],[-134.431194,-74.361455],[-133.745654,-74.439848],[-132.257168,-74.302699],[-130.925311,-74.479019],[-129.554284,-74.459433],[-128.242038,-74.322284],[-126.890622,-74.420263],[-125.402082,-74.518241],[-124.011496,-74.479019],[-122.562152,-74.498604],[-121.073613,-74.518241],[-119.70256,-74.479019],[-118.684145,-74.185083],[-117.469801,-74.028348],[-116.216312,-74.243891],[-115.021552,-74.067519],[-113.944331,-73.714828],[-113.297988,-74.028348],[-112.945452,-74.38104],[-112.299083,-74.714198],[-111.261059,-74.420263],[-110.066325,-74.79254],[-108.714909,-74.910103],[-107.559346,-75.184454],[-106.149148,-75.125698],[-104.876074,-74.949326],[-103.367949,-74.988497],[-102.016507,-75.125698],[-100.645531,-75.302018],[-100.1167,-74.870933],[-100.763043,-74.537826],[-101.252703,-74.185083],[-102.545337,-74.106742],[-103.113313,-73.734413],[-103.328752,-73.362084],[-103.681289,-72.61753],[-102.917485,-72.754679],[-101.60524,-72.813436],[-100.312528,-72.754679],[-99.13738,-72.911414],[-98.118889,-73.20535],[-97.688037,-73.558041],[-96.336595,-73.616849],[-95.043961,-73.4797],[-93.672907,-73.283743],[-92.439003,-73.166179],[-91.420564,-73.401307],[-90.088733,-73.322914],[-89.226951,-72.558722],[-88.423951,-73.009393],[-87.268337,-73.185764],[-86.014822,-73.087786],[-85.192236,-73.4797],[-83.879991,-73.518871],[-82.665646,-73.636434],[-81.470913,-73.851977],[-80.687447,-73.4797],[-80.295791,-73.126956],[-79.296886,-73.518871],[-77.925858,-73.420892],[-76.907367,-73.636434],[-76.221879,-73.969541],[-74.890049,-73.871614],[-73.852024,-73.65602],[-72.833533,-73.401307],[-71.619215,-73.264157],[-70.209042,-73.146542],[-68.935916,-73.009393],[-67.956622,-72.79385],[-67.369061,-72.480329],[-67.134036,-72.049244],[-67.251548,-71.637745],[-67.56494,-71.245831],[-67.917477,-70.853917],[-68.230843,-70.462055],[-68.485452,-70.109311],[-68.544209,-69.717397],[-68.446282,-69.325535],[-67.976233,-68.953206],[-67.5845,-68.541707],[-67.427843,-68.149844],[-67.62367,-67.718759],[-67.741183,-67.326845],[-67.251548,-66.876175],[-66.703184,-66.58224],[-66.056815,-66.209963],[-65.371327,-65.89639],[-64.568276,-65.602506],[-64.176542,-65.171423],[-63.628152,-64.897073],[-63.001394,-64.642308],[-62.041686,-64.583552],[-61.414928,-64.270031],[-60.709855,-64.074074],[-59.887269,-63.95651],[-59.162585,-63.701745],[-58.594557,-63.388224],[-57.811143,-63.27066],[-57.223582,-63.525425],[-57.59573,-63.858532],[-58.614143,-64.152467]]]]}},
    +{"type":"Feature","id":"ATF","properties":{"name":"French Southern and Antarctic Lands"},"geometry":{"type":"Polygon","coordinates":[[[68.935,-48.625],[69.58,-48.94],[70.525,-49.065],[70.56,-49.255],[70.28,-49.71],[68.745,-49.775],[68.72,-49.2425],[68.8675,-48.83],[68.935,-48.625]]]}},
    +{"type":"Feature","id":"AUS","properties":{"name":"Australia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[145.397978,-40.792549],[146.364121,-41.137695],[146.908584,-41.000546],[147.689259,-40.808258],[148.289068,-40.875438],[148.359865,-42.062445],[148.017301,-42.407024],[147.914052,-43.211522],[147.564564,-42.937689],[146.870343,-43.634597],[146.663327,-43.580854],[146.048378,-43.549745],[145.43193,-42.693776],[145.29509,-42.03361],[144.718071,-41.162552],[144.743755,-40.703975],[145.397978,-40.792549]]],[[[143.561811,-13.763656],[143.922099,-14.548311],[144.563714,-14.171176],[144.894908,-14.594458],[145.374724,-14.984976],[145.271991,-15.428205],[145.48526,-16.285672],[145.637033,-16.784918],[145.888904,-16.906926],[146.160309,-17.761655],[146.063674,-18.280073],[146.387478,-18.958274],[147.471082,-19.480723],[148.177602,-19.955939],[148.848414,-20.39121],[148.717465,-20.633469],[149.28942,-21.260511],[149.678337,-22.342512],[150.077382,-22.122784],[150.482939,-22.556142],[150.727265,-22.402405],[150.899554,-23.462237],[151.609175,-24.076256],[152.07354,-24.457887],[152.855197,-25.267501],[153.136162,-26.071173],[153.161949,-26.641319],[153.092909,-27.2603],[153.569469,-28.110067],[153.512108,-28.995077],[153.339095,-29.458202],[153.069241,-30.35024],[153.089602,-30.923642],[152.891578,-31.640446],[152.450002,-32.550003],[151.709117,-33.041342],[151.343972,-33.816023],[151.010555,-34.31036],[150.714139,-35.17346],[150.32822,-35.671879],[150.075212,-36.420206],[149.946124,-37.109052],[149.997284,-37.425261],[149.423882,-37.772681],[148.304622,-37.809061],[147.381733,-38.219217],[146.922123,-38.606532],[146.317922,-39.035757],[145.489652,-38.593768],[144.876976,-38.417448],[145.032212,-37.896188],[144.485682,-38.085324],[143.609974,-38.809465],[142.745427,-38.538268],[142.17833,-38.380034],[141.606582,-38.308514],[140.638579,-38.019333],[139.992158,-37.402936],[139.806588,-36.643603],[139.574148,-36.138362],[139.082808,-35.732754],[138.120748,-35.612296],[138.449462,-35.127261],[138.207564,-34.384723],[137.71917,-35.076825],[136.829406,-35.260535],[137.352371,-34.707339],[137.503886,-34.130268],[137.890116,-33.640479],[137.810328,-32.900007],[136.996837,-33.752771],[136.372069,-34.094766],[135.989043,-34.890118],[135.208213,-34.47867],[135.239218,-33.947953],[134.613417,-33.222778],[134.085904,-32.848072],[134.273903,-32.617234],[132.990777,-32.011224],[132.288081,-31.982647],[131.326331,-31.495803],[129.535794,-31.590423],[128.240938,-31.948489],[127.102867,-32.282267],[126.148714,-32.215966],[125.088623,-32.728751],[124.221648,-32.959487],[124.028947,-33.483847],[123.659667,-33.890179],[122.811036,-33.914467],[122.183064,-34.003402],[121.299191,-33.821036],[120.580268,-33.930177],[119.893695,-33.976065],[119.298899,-34.509366],[119.007341,-34.464149],[118.505718,-34.746819],[118.024972,-35.064733],[117.295507,-35.025459],[116.625109,-35.025097],[115.564347,-34.386428],[115.026809,-34.196517],[115.048616,-33.623425],[115.545123,-33.487258],[115.714674,-33.259572],[115.679379,-32.900369],[115.801645,-32.205062],[115.689611,-31.612437],[115.160909,-30.601594],[114.997043,-30.030725],[115.040038,-29.461095],[114.641974,-28.810231],[114.616498,-28.516399],[114.173579,-28.118077],[114.048884,-27.334765],[113.477498,-26.543134],[113.338953,-26.116545],[113.778358,-26.549025],[113.440962,-25.621278],[113.936901,-25.911235],[114.232852,-26.298446],[114.216161,-25.786281],[113.721255,-24.998939],[113.625344,-24.683971],[113.393523,-24.384764],[113.502044,-23.80635],[113.706993,-23.560215],[113.843418,-23.059987],[113.736552,-22.475475],[114.149756,-21.755881],[114.225307,-22.517488],[114.647762,-21.82952],[115.460167,-21.495173],[115.947373,-21.068688],[116.711615,-20.701682],[117.166316,-20.623599],[117.441545,-20.746899],[118.229559,-20.374208],[118.836085,-20.263311],[118.987807,-20.044203],[119.252494,-19.952942],[119.805225,-19.976506],[120.85622,-19.683708],[121.399856,-19.239756],[121.655138,-18.705318],[122.241665,-18.197649],[122.286624,-17.798603],[122.312772,-17.254967],[123.012574,-16.4052],[123.433789,-17.268558],[123.859345,-17.069035],[123.503242,-16.596506],[123.817073,-16.111316],[124.258287,-16.327944],[124.379726,-15.56706],[124.926153,-15.0751],[125.167275,-14.680396],[125.670087,-14.51007],[125.685796,-14.230656],[126.125149,-14.347341],[126.142823,-14.095987],[126.582589,-13.952791],[127.065867,-13.817968],[127.804633,-14.276906],[128.35969,-14.86917],[128.985543,-14.875991],[129.621473,-14.969784],[129.4096,-14.42067],[129.888641,-13.618703],[130.339466,-13.357376],[130.183506,-13.10752],[130.617795,-12.536392],[131.223495,-12.183649],[131.735091,-12.302453],[132.575298,-12.114041],[132.557212,-11.603012],[131.824698,-11.273782],[132.357224,-11.128519],[133.019561,-11.376411],[133.550846,-11.786515],[134.393068,-12.042365],[134.678632,-11.941183],[135.298491,-12.248606],[135.882693,-11.962267],[136.258381,-12.049342],[136.492475,-11.857209],[136.95162,-12.351959],[136.685125,-12.887223],[136.305407,-13.29123],[135.961758,-13.324509],[136.077617,-13.724278],[135.783836,-14.223989],[135.428664,-14.715432],[135.500184,-14.997741],[136.295175,-15.550265],[137.06536,-15.870762],[137.580471,-16.215082],[138.303217,-16.807604],[138.585164,-16.806622],[139.108543,-17.062679],[139.260575,-17.371601],[140.215245,-17.710805],[140.875463,-17.369069],[141.07111,-16.832047],[141.274095,-16.38887],[141.398222,-15.840532],[141.702183,-15.044921],[141.56338,-14.561333],[141.63552,-14.270395],[141.519869,-13.698078],[141.65092,-12.944688],[141.842691,-12.741548],[141.68699,-12.407614],[141.928629,-11.877466],[142.118488,-11.328042],[142.143706,-11.042737],[142.51526,-10.668186],[142.79731,-11.157355],[142.866763,-11.784707],[143.115947,-11.90563],[143.158632,-12.325656],[143.522124,-12.834358],[143.597158,-13.400422],[143.561811,-13.763656]]]]}},
    +{"type":"Feature","id":"AUT","properties":{"name":"Austria"},"geometry":{"type":"Polygon","coordinates":[[[16.979667,48.123497],[16.903754,47.714866],[16.340584,47.712902],[16.534268,47.496171],[16.202298,46.852386],[16.011664,46.683611],[15.137092,46.658703],[14.632472,46.431817],[13.806475,46.509306],[12.376485,46.767559],[12.153088,47.115393],[11.164828,46.941579],[11.048556,46.751359],[10.442701,46.893546],[9.932448,46.920728],[9.47997,47.10281],[9.632932,47.347601],[9.594226,47.525058],[9.896068,47.580197],[10.402084,47.302488],[10.544504,47.566399],[11.426414,47.523766],[12.141357,47.703083],[12.62076,47.672388],[12.932627,47.467646],[13.025851,47.637584],[12.884103,48.289146],[13.243357,48.416115],[13.595946,48.877172],[14.338898,48.555305],[14.901447,48.964402],[15.253416,49.039074],[16.029647,48.733899],[16.499283,48.785808],[16.960288,48.596982],[16.879983,48.470013],[16.979667,48.123497]]]}},
    +{"type":"Feature","id":"AZE","properties":{"name":"Azerbaijan"},"geometry":{"type":"MultiPolygon","coordinates":[[[[45.001987,39.740004],[45.298145,39.471751],[45.739978,39.473999],[45.735379,39.319719],[46.143623,38.741201],[45.457722,38.874139],[44.952688,39.335765],[44.79399,39.713003],[45.001987,39.740004]]],[[[47.373315,41.219732],[47.815666,41.151416],[47.987283,41.405819],[48.584353,41.80887],[49.110264,41.282287],[49.618915,40.572924],[50.08483,40.526157],[50.392821,40.256561],[49.569202,40.176101],[49.395259,39.399482],[49.223228,39.049219],[48.856532,38.815486],[48.883249,38.320245],[48.634375,38.270378],[48.010744,38.794015],[48.355529,39.288765],[48.060095,39.582235],[47.685079,39.508364],[46.50572,38.770605],[46.483499,39.464155],[46.034534,39.628021],[45.610012,39.899994],[45.891907,40.218476],[45.359175,40.561504],[45.560351,40.81229],[45.179496,40.985354],[44.97248,41.248129],[45.217426,41.411452],[45.962601,41.123873],[46.501637,41.064445],[46.637908,41.181673],[46.145432,41.722802],[46.404951,41.860675],[46.686071,41.827137],[47.373315,41.219732]]]]}},
    +{"type":"Feature","id":"BDI","properties":{"name":"Burundi"},"geometry":{"type":"Polygon","coordinates":[[[29.339998,-4.499983],[29.276384,-3.293907],[29.024926,-2.839258],[29.632176,-2.917858],[29.938359,-2.348487],[30.469696,-2.413858],[30.527677,-2.807632],[30.743013,-3.034285],[30.752263,-3.35933],[30.50556,-3.568567],[30.116333,-4.090138],[29.753512,-4.452389],[29.339998,-4.499983]]]}},
    +{"type":"Feature","id":"BEL","properties":{"name":"Belgium"},"geometry":{"type":"Polygon","coordinates":[[[3.314971,51.345781],[4.047071,51.267259],[4.973991,51.475024],[5.606976,51.037298],[6.156658,50.803721],[6.043073,50.128052],[5.782417,50.090328],[5.674052,49.529484],[4.799222,49.985373],[4.286023,49.907497],[3.588184,50.378992],[3.123252,50.780363],[2.658422,50.796848],[2.513573,51.148506],[3.314971,51.345781]]]}},
    +{"type":"Feature","id":"BEN","properties":{"name":"Benin"},"geometry":{"type":"Polygon","coordinates":[[[2.691702,6.258817],[1.865241,6.142158],[1.618951,6.832038],[1.664478,9.12859],[1.463043,9.334624],[1.425061,9.825395],[1.077795,10.175607],[0.772336,10.470808],[0.899563,10.997339],[1.24347,11.110511],[1.447178,11.547719],[1.935986,11.64115],[2.154474,11.94015],[2.490164,12.233052],[2.848643,12.235636],[3.61118,11.660167],[3.572216,11.327939],[3.797112,10.734746],[3.60007,10.332186],[3.705438,10.06321],[3.220352,9.444153],[2.912308,9.137608],[2.723793,8.506845],[2.749063,7.870734],[2.691702,6.258817]]]}},
    +{"type":"Feature","id":"BFA","properties":{"name":"Burkina Faso"},"geometry":{"type":"Polygon","coordinates":[[[-2.827496,9.642461],[-3.511899,9.900326],[-3.980449,9.862344],[-4.330247,9.610835],[-4.779884,9.821985],[-4.954653,10.152714],[-5.404342,10.370737],[-5.470565,10.95127],[-5.197843,11.375146],[-5.220942,11.713859],[-4.427166,12.542646],[-4.280405,13.228444],[-4.006391,13.472485],[-3.522803,13.337662],[-3.103707,13.541267],[-2.967694,13.79815],[-2.191825,14.246418],[-2.001035,14.559008],[-1.066363,14.973815],[-0.515854,15.116158],[-0.266257,14.924309],[0.374892,14.928908],[0.295646,14.444235],[0.429928,13.988733],[0.993046,13.33575],[1.024103,12.851826],[2.177108,12.625018],[2.154474,11.94015],[1.935986,11.64115],[1.447178,11.547719],[1.24347,11.110511],[0.899563,10.997339],[0.023803,11.018682],[-0.438702,11.098341],[-0.761576,10.93693],[-1.203358,11.009819],[-2.940409,10.96269],[-2.963896,10.395335],[-2.827496,9.642461]]]}},
    +{"type":"Feature","id":"BGD","properties":{"name":"Bangladesh"},"geometry":{"type":"Polygon","coordinates":[[[92.672721,22.041239],[92.652257,21.324048],[92.303234,21.475485],[92.368554,20.670883],[92.082886,21.192195],[92.025215,21.70157],[91.834891,22.182936],[91.417087,22.765019],[90.496006,22.805017],[90.586957,22.392794],[90.272971,21.836368],[89.847467,22.039146],[89.70205,21.857116],[89.418863,21.966179],[89.031961,22.055708],[88.876312,22.879146],[88.52977,23.631142],[88.69994,24.233715],[88.084422,24.501657],[88.306373,24.866079],[88.931554,25.238692],[88.209789,25.768066],[88.563049,26.446526],[89.355094,26.014407],[89.832481,25.965082],[89.920693,25.26975],[90.872211,25.132601],[91.799596,25.147432],[92.376202,24.976693],[91.915093,24.130414],[91.46773,24.072639],[91.158963,23.503527],[91.706475,22.985264],[91.869928,23.624346],[92.146035,23.627499],[92.672721,22.041239]]]}},
    +{"type":"Feature","id":"BGR","properties":{"name":"Bulgaria"},"geometry":{"type":"Polygon","coordinates":[[[22.65715,44.234923],[22.944832,43.823785],[23.332302,43.897011],[24.100679,43.741051],[25.569272,43.688445],[26.065159,43.943494],[27.2424,44.175986],[27.970107,43.812468],[28.558081,43.707462],[28.039095,43.293172],[27.673898,42.577892],[27.99672,42.007359],[27.135739,42.141485],[26.117042,41.826905],[26.106138,41.328899],[25.197201,41.234486],[24.492645,41.583896],[23.692074,41.309081],[22.952377,41.337994],[22.881374,41.999297],[22.380526,42.32026],[22.545012,42.461362],[22.436595,42.580321],[22.604801,42.898519],[22.986019,43.211161],[22.500157,43.642814],[22.410446,44.008063],[22.65715,44.234923]]]}},
    +{"type":"Feature","id":"BHS","properties":{"name":"The Bahamas"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-77.53466,23.75975],[-77.78,23.71],[-78.03405,24.28615],[-78.40848,24.57564],[-78.19087,25.2103],[-77.89,25.17],[-77.54,24.34],[-77.53466,23.75975]]],[[[-77.82,26.58],[-78.91,26.42],[-78.98,26.79],[-78.51,26.87],[-77.85,26.84],[-77.82,26.58]]],[[[-77,26.59],[-77.17255,25.87918],[-77.35641,26.00735],[-77.34,26.53],[-77.78802,26.92516],[-77.79,27.04],[-77,26.59]]]]}},
    +{"type":"Feature","id":"BIH","properties":{"name":"Bosnia and Herzegovina"},"geometry":{"type":"Polygon","coordinates":[[[19.005486,44.860234],[19.36803,44.863],[19.11761,44.42307],[19.59976,44.03847],[19.454,43.5681],[19.21852,43.52384],[19.03165,43.43253],[18.70648,43.20011],[18.56,42.65],[17.674922,43.028563],[17.297373,43.446341],[16.916156,43.667722],[16.456443,44.04124],[16.23966,44.351143],[15.750026,44.818712],[15.959367,45.233777],[16.318157,45.004127],[16.534939,45.211608],[17.002146,45.233777],[17.861783,45.06774],[18.553214,45.08159],[19.005486,44.860234]]]}},
    +{"type":"Feature","id":"BLR","properties":{"name":"Belarus"},"geometry":{"type":"Polygon","coordinates":[[[23.484128,53.912498],[24.450684,53.905702],[25.536354,54.282423],[25.768433,54.846963],[26.588279,55.167176],[26.494331,55.615107],[27.10246,55.783314],[28.176709,56.16913],[29.229513,55.918344],[29.371572,55.670091],[29.896294,55.789463],[30.873909,55.550976],[30.971836,55.081548],[30.757534,54.811771],[31.384472,54.157056],[31.791424,53.974639],[31.731273,53.794029],[32.405599,53.618045],[32.693643,53.351421],[32.304519,53.132726],[31.497644,53.167427],[31.305201,53.073996],[31.540018,52.742052],[31.785998,52.101678],[30.927549,52.042353],[30.619454,51.822806],[30.555117,51.319503],[30.157364,51.416138],[29.254938,51.368234],[28.992835,51.602044],[28.617613,51.427714],[28.241615,51.572227],[27.454066,51.592303],[26.337959,51.832289],[25.327788,51.910656],[24.553106,51.888461],[24.005078,51.617444],[23.527071,51.578454],[23.508002,52.023647],[23.199494,52.486977],[23.799199,52.691099],[23.804935,53.089731],[23.527536,53.470122],[23.484128,53.912498]]]}},
    +{"type":"Feature","id":"BLZ","properties":{"name":"Belize"},"geometry":{"type":"Polygon","coordinates":[[[-89.14308,17.808319],[-89.150909,17.955468],[-89.029857,18.001511],[-88.848344,17.883198],[-88.490123,18.486831],[-88.300031,18.499982],[-88.296336,18.353273],[-88.106813,18.348674],[-88.123479,18.076675],[-88.285355,17.644143],[-88.197867,17.489475],[-88.302641,17.131694],[-88.239518,17.036066],[-88.355428,16.530774],[-88.551825,16.265467],[-88.732434,16.233635],[-88.930613,15.887273],[-89.229122,15.886938],[-89.150806,17.015577],[-89.14308,17.808319]]]}},
    +{"type":"Feature","id":"BMU","properties":{"name":"Bermuda"},"geometry":{"type":"Polygon","coordinates":[[[-64.7799734332998,32.3072000581802],[-64.7873319183061,32.3039237143428],[-64.7946942710173,32.3032682700388],[-64.8094297981283,32.3098175728414],[-64.8167896352437,32.3058845718466],[-64.8101968029642,32.3022833180511],[-64.7962291465484,32.2934409732427],[-64.7815086336978,32.2868973114514],[-64.7997025513437,32.2796896417328],[-64.8066707691087,32.2747767569465],[-64.8225587873683,32.2669111289395],[-64.8287548840306,32.2669075473817],[-64.8306732143498,32.2583944840235],[-64.8399924854972,32.254782282336],[-64.8566090462354,32.2547740387514],[-64.8682296789446,32.2616393614322],[-64.8628241459563,32.2724481933959],[-64.8748651338951,32.2757120264753],[-64.8717752856644,32.2819371582026],[-64.8671422127295,32.2930760547989],[-64.8559068764437,32.2960321186471],[-64.8597429072279,32.3015842021933],[-64.8439233486717,32.3140553852543],[-64.8350242329311,32.3242161760006],[-64.8338690593672,32.3294587561557],[-64.8520298651164,32.3110911879954],[-64.8635922932573,32.3048469433363],[-64.8686668994079,32.30910745083],[-64.8721354593415,32.3041908606301],[-64.8779667328485,32.3038632800462],[-64.8780046844321,32.2907757831692],[-64.8849776658292,32.2819261366004],[-64.8783230004629,32.2613001418681],[-64.863194968877,32.2465799485801],[-64.8519819555722,32.2485519134663],[-64.842311980074,32.2492123317296],[-64.8388242605209,32.2475773472534],[-64.8334002575532,32.2462714714698],[-64.8256389530584,32.2472637398594],[-64.8205697556026,32.2531698880328],[-64.8105087275579,32.2561208974156],[-64.7900177727338,32.2659446936992],[-64.7745415970416,32.2718413023427],[-64.7644742436426,32.2855931353214],[-64.7551803442276,32.2908326702531],[-64.7423982971436,32.2996734994024],[-64.7206991797682,32.3137542201258],[-64.7117851247134,32.3176823360806],[-64.6962778813133,32.3275029115532],[-64.6768921127452,32.3324095397555],[-64.6567136927777,32.3451776458469],[-64.6532168823499,32.3494356627941],[-64.6605720384429,32.3589423487763],[-64.65125819471,32.3615600906466],[-64.6462011670816,32.36975169749],[-64.6613227512832,32.3763135008721],[-64.6690666074397,32.388444543924],[-64.6834270548595,32.3854968316788],[-64.6954617672714,32.3763221285869],[-64.70438689565,32.3704254760469],[-64.7117569982798,32.368132600249],[-64.7061764744404,32.3600110593559],[-64.700531552697,32.3590601356818],[-64.6940348033967,32.3640708659835],[-64.6895164826082,32.3633598579866],[-64.6864150099255,32.3547797587266],[-64.6824635995504,32.3540628176846],[-64.6835876652835,32.3626447677968],[-64.6801998697415,32.3631199096979],[-64.6672170444687,32.3597751617473],[-64.6598811264978,32.3497625771755],[-64.6737331235384,32.3390281851635],[-64.6887090648183,32.3342439408053],[-64.706732854446,32.3429010723036],[-64.7149301576112,32.3552188753513],[-64.7185967666669,32.3552239212394],[-64.7214189847314,32.3518830231342],[-64.7270616067222,32.3466461715475],[-64.734962460882,32.3442819830499],[-64.7383521549094,32.3407216514918],[-64.7411729976333,32.3311790864627],[-64.7423019216485,32.323311561213],[-64.7462482354281,32.318538611581],[-64.7566773739613,32.3130509130175],[-64.768738200563,32.3088369816572],[-64.7799734332998,32.3072000581802]]]}},
    +{"type":"Feature","id":"BOL","properties":{"name":"Bolivia"},"geometry":{"type":"Polygon","coordinates":[[[-62.846468,-22.034985],[-63.986838,-21.993644],[-64.377021,-22.798091],[-64.964892,-22.075862],[-66.273339,-21.83231],[-67.106674,-22.735925],[-67.82818,-22.872919],[-68.219913,-21.494347],[-68.757167,-20.372658],[-68.442225,-19.405068],[-68.966818,-18.981683],[-69.100247,-18.260125],[-69.590424,-17.580012],[-68.959635,-16.500698],[-69.389764,-15.660129],[-69.160347,-15.323974],[-69.339535,-14.953195],[-68.948887,-14.453639],[-68.929224,-13.602684],[-68.88008,-12.899729],[-68.66508,-12.5613],[-69.529678,-10.951734],[-68.786158,-11.03638],[-68.271254,-11.014521],[-68.048192,-10.712059],[-67.173801,-10.306812],[-66.646908,-9.931331],[-65.338435,-9.761988],[-65.444837,-10.511451],[-65.321899,-10.895872],[-65.402281,-11.56627],[-64.316353,-12.461978],[-63.196499,-12.627033],[-62.80306,-13.000653],[-62.127081,-13.198781],[-61.713204,-13.489202],[-61.084121,-13.479384],[-60.503304,-13.775955],[-60.459198,-14.354007],[-60.264326,-14.645979],[-60.251149,-15.077219],[-60.542966,-15.09391],[-60.15839,-16.258284],[-58.24122,-16.299573],[-58.388058,-16.877109],[-58.280804,-17.27171],[-57.734558,-17.552468],[-57.498371,-18.174188],[-57.676009,-18.96184],[-57.949997,-19.400004],[-57.853802,-19.969995],[-58.166392,-20.176701],[-58.183471,-19.868399],[-59.115042,-19.356906],[-60.043565,-19.342747],[-61.786326,-19.633737],[-62.265961,-20.513735],[-62.291179,-21.051635],[-62.685057,-22.249029],[-62.846468,-22.034985]]]}},
    +{"type":"Feature","id":"BRA","properties":{"name":"Brazil"},"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.2909,-28.852761],[-55.162286,-27.881915],[-54.490725,-27.474757],[-53.648735,-26.923473],[-53.628349,-26.124865],[-54.13005,-25.547639],[-54.625291,-25.739255],[-54.428946,-25.162185],[-54.293476,-24.5708],[-54.29296,-24.021014],[-54.652834,-23.839578],[-55.027902,-24.001274],[-55.400747,-23.956935],[-55.517639,-23.571998],[-55.610683,-22.655619],[-55.797958,-22.35693],[-56.473317,-22.0863],[-56.88151,-22.282154],[-57.937156,-22.090176],[-57.870674,-20.732688],[-58.166392,-20.176701],[-57.853802,-19.969995],[-57.949997,-19.400004],[-57.676009,-18.96184],[-57.498371,-18.174188],[-57.734558,-17.552468],[-58.280804,-17.27171],[-58.388058,-16.877109],[-58.24122,-16.299573],[-60.15839,-16.258284],[-60.542966,-15.09391],[-60.251149,-15.077219],[-60.264326,-14.645979],[-60.459198,-14.354007],[-60.503304,-13.775955],[-61.084121,-13.479384],[-61.713204,-13.489202],[-62.127081,-13.198781],[-62.80306,-13.000653],[-63.196499,-12.627033],[-64.316353,-12.461978],[-65.402281,-11.56627],[-65.321899,-10.895872],[-65.444837,-10.511451],[-65.338435,-9.761988],[-66.646908,-9.931331],[-67.173801,-10.306812],[-68.048192,-10.712059],[-68.271254,-11.014521],[-68.786158,-11.03638],[-69.529678,-10.951734],[-70.093752,-11.123972],[-70.548686,-11.009147],[-70.481894,-9.490118],[-71.302412,-10.079436],[-72.184891,-10.053598],[-72.563033,-9.520194],[-73.226713,-9.462213],[-73.015383,-9.032833],[-73.571059,-8.424447],[-73.987235,-7.52383],[-73.723401,-7.340999],[-73.724487,-6.918595],[-73.120027,-6.629931],[-73.219711,-6.089189],[-72.964507,-5.741251],[-72.891928,-5.274561],[-71.748406,-4.593983],[-70.928843,-4.401591],[-70.794769,-4.251265],[-69.893635,-4.298187],[-69.444102,-1.556287],[-69.420486,-1.122619],[-69.577065,-0.549992],[-70.020656,-0.185156],[-70.015566,0.541414],[-69.452396,0.706159],[-69.252434,0.602651],[-69.218638,0.985677],[-69.804597,1.089081],[-69.816973,1.714805],[-67.868565,1.692455],[-67.53781,2.037163],[-67.259998,1.719999],[-67.065048,1.130112],[-66.876326,1.253361],[-66.325765,0.724452],[-65.548267,0.789254],[-65.354713,1.095282],[-64.611012,1.328731],[-64.199306,1.492855],[-64.083085,1.916369],[-63.368788,2.2009],[-63.422867,2.411068],[-64.269999,2.497006],[-64.408828,3.126786],[-64.368494,3.79721],[-64.816064,4.056445],[-64.628659,4.148481],[-63.888343,4.02053],[-63.093198,3.770571],[-62.804533,4.006965],[-62.08543,4.162124],[-60.966893,4.536468],[-60.601179,4.918098],[-60.733574,5.200277],[-60.213683,5.244486],[-59.980959,5.014061],[-60.111002,4.574967],[-59.767406,4.423503],[-59.53804,3.958803],[-59.815413,3.606499],[-59.974525,2.755233],[-59.718546,2.24963],[-59.646044,1.786894],[-59.030862,1.317698],[-58.540013,1.268088],[-58.429477,1.463942],[-58.11345,1.507195],[-57.660971,1.682585],[-57.335823,1.948538],[-56.782704,1.863711],[-56.539386,1.899523],[-55.995698,1.817667],[-55.9056,2.021996],[-56.073342,2.220795],[-55.973322,2.510364],[-55.569755,2.421506],[-55.097587,2.523748],[-54.524754,2.311849],[-54.088063,2.105557],[-53.778521,2.376703],[-53.554839,2.334897],[-53.418465,2.053389],[-52.939657,2.124858],[-52.556425,2.504705],[-52.249338,3.241094],[-51.657797,4.156232],[-51.317146,4.203491],[-51.069771,3.650398],[-50.508875,1.901564],[-49.974076,1.736483],[-49.947101,1.04619],[-50.699251,0.222984],[-50.388211,-0.078445],[-48.620567,-0.235489],[-48.584497,-1.237805],[-47.824956,-0.581618],[-46.566584,-0.941028],[-44.905703,-1.55174],[-44.417619,-2.13775],[-44.581589,-2.691308],[-43.418791,-2.38311],[-41.472657,-2.912018],[-39.978665,-2.873054],[-38.500383,-3.700652],[-37.223252,-4.820946],[-36.452937,-5.109404],[-35.597796,-5.149504],[-35.235389,-5.464937],[-34.89603,-6.738193],[-34.729993,-7.343221],[-35.128212,-8.996401],[-35.636967,-9.649282],[-37.046519,-11.040721],[-37.683612,-12.171195],[-38.423877,-13.038119],[-38.673887,-13.057652],[-38.953276,-13.79337],[-38.882298,-15.667054],[-39.161092,-17.208407],[-39.267339,-17.867746],[-39.583521,-18.262296],[-39.760823,-19.599113],[-40.774741,-20.904512],[-40.944756,-21.937317],[-41.754164,-22.370676],[-41.988284,-22.97007],[-43.074704,-22.967693],[-44.647812,-23.351959],[-45.352136,-23.796842],[-46.472093,-24.088969],[-47.648972,-24.885199],[-48.495458,-25.877025],[-48.641005,-26.623698],[-48.474736,-27.175912],[-48.66152,-28.186135],[-48.888457,-28.674115],[-49.587329,-29.224469],[-50.696874,-30.984465],[-51.576226,-31.777698],[-52.256081,-32.24537],[-52.7121,-33.196578],[-53.373662,-33.768378],[-53.650544,-33.202004],[-53.209589,-32.727666],[-53.787952,-32.047243],[-54.572452,-31.494511],[-55.60151,-30.853879],[-55.973245,-30.883076],[-56.976026,-30.109686],[-57.625133,-30.216295]]]}},
    +{"type":"Feature","id":"BRN","properties":{"name":"Brunei"},"geometry":{"type":"Polygon","coordinates":[[[114.204017,4.525874],[114.599961,4.900011],[115.45071,5.44773],[115.4057,4.955228],[115.347461,4.316636],[114.869557,4.348314],[114.659596,4.007637],[114.204017,4.525874]]]}},
    +{"type":"Feature","id":"BTN","properties":{"name":"Bhutan"},"geometry":{"type":"Polygon","coordinates":[[[91.696657,27.771742],[92.103712,27.452614],[92.033484,26.83831],[91.217513,26.808648],[90.373275,26.875724],[89.744528,26.719403],[88.835643,27.098966],[88.814248,27.299316],[89.47581,28.042759],[90.015829,28.296439],[90.730514,28.064954],[91.258854,28.040614],[91.696657,27.771742]]]}},
    +{"type":"Feature","id":"BWA","properties":{"name":"Botswana"},"geometry":{"type":"Polygon","coordinates":[[[25.649163,-18.536026],[25.850391,-18.714413],[26.164791,-19.293086],[27.296505,-20.39152],[27.724747,-20.499059],[27.727228,-20.851802],[28.02137,-21.485975],[28.794656,-21.639454],[29.432188,-22.091313],[28.017236,-22.827754],[27.11941,-23.574323],[26.786407,-24.240691],[26.485753,-24.616327],[25.941652,-24.696373],[25.765849,-25.174845],[25.664666,-25.486816],[25.025171,-25.71967],[24.211267,-25.670216],[23.73357,-25.390129],[23.312097,-25.26869],[22.824271,-25.500459],[22.579532,-25.979448],[22.105969,-26.280256],[21.605896,-26.726534],[20.889609,-26.828543],[20.66647,-26.477453],[20.758609,-25.868136],[20.165726,-24.917962],[19.895768,-24.76779],[19.895458,-21.849157],[20.881134,-21.814327],[20.910641,-18.252219],[21.65504,-18.219146],[23.196858,-17.869038],[23.579006,-18.281261],[24.217365,-17.889347],[24.520705,-17.887125],[25.084443,-17.661816],[25.264226,-17.73654],[25.649163,-18.536026]]]}},
    +{"type":"Feature","id":"CAF","properties":{"name":"Central African Republic"},"geometry":{"type":"Polygon","coordinates":[[[15.27946,7.421925],[16.106232,7.497088],[16.290562,7.754307],[16.456185,7.734774],[16.705988,7.508328],[17.96493,7.890914],[18.389555,8.281304],[18.911022,8.630895],[18.81201,8.982915],[19.094008,9.074847],[20.059685,9.012706],[21.000868,9.475985],[21.723822,10.567056],[22.231129,10.971889],[22.864165,11.142395],[22.977544,10.714463],[23.554304,10.089255],[23.55725,9.681218],[23.394779,9.265068],[23.459013,8.954286],[23.805813,8.666319],[24.567369,8.229188],[25.114932,7.825104],[25.124131,7.500085],[25.796648,6.979316],[26.213418,6.546603],[26.465909,5.946717],[27.213409,5.550953],[27.374226,5.233944],[27.044065,5.127853],[26.402761,5.150875],[25.650455,5.256088],[25.278798,5.170408],[25.128833,4.927245],[24.805029,4.897247],[24.410531,5.108784],[23.297214,4.609693],[22.84148,4.710126],[22.704124,4.633051],[22.405124,4.02916],[21.659123,4.224342],[20.927591,4.322786],[20.290679,4.691678],[19.467784,5.031528],[18.932312,4.709506],[18.542982,4.201785],[18.453065,3.504386],[17.8099,3.560196],[17.133042,3.728197],[16.537058,3.198255],[16.012852,2.26764],[15.907381,2.557389],[15.862732,3.013537],[15.405396,3.335301],[15.03622,3.851367],[14.950953,4.210389],[14.478372,4.732605],[14.558936,5.030598],[14.459407,5.451761],[14.53656,6.226959],[14.776545,6.408498],[15.27946,7.421925]]]}},
    +{"type":"Feature","id":"CAN","properties":{"name":"Canada"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-63.6645,46.55001],[-62.9393,46.41587],[-62.01208,46.44314],[-62.50391,46.03339],[-62.87433,45.96818],[-64.1428,46.39265],[-64.39261,46.72747],[-64.01486,47.03601],[-63.6645,46.55001]]],[[[-61.806305,49.10506],[-62.29318,49.08717],[-63.58926,49.40069],[-64.51912,49.87304],[-64.17322,49.95718],[-62.85829,49.70641],[-61.835585,49.28855],[-61.806305,49.10506]]],[[[-123.510002,48.510011],[-124.012891,48.370846],[-125.655013,48.825005],[-125.954994,49.179996],[-126.850004,49.53],[-127.029993,49.814996],[-128.059336,49.994959],[-128.444584,50.539138],[-128.358414,50.770648],[-127.308581,50.552574],[-126.695001,50.400903],[-125.755007,50.295018],[-125.415002,49.950001],[-124.920768,49.475275],[-123.922509,49.062484],[-123.510002,48.510011]]],[[[-56.134036,50.68701],[-56.795882,49.812309],[-56.143105,50.150117],[-55.471492,49.935815],[-55.822401,49.587129],[-54.935143,49.313011],[-54.473775,49.556691],[-53.476549,49.249139],[-53.786014,48.516781],[-53.086134,48.687804],[-52.958648,48.157164],[-52.648099,47.535548],[-53.069158,46.655499],[-53.521456,46.618292],[-54.178936,46.807066],[-53.961869,47.625207],[-54.240482,47.752279],[-55.400773,46.884994],[-55.997481,46.91972],[-55.291219,47.389562],[-56.250799,47.632545],[-57.325229,47.572807],[-59.266015,47.603348],[-59.419494,47.899454],[-58.796586,48.251525],[-59.231625,48.523188],[-58.391805,49.125581],[-57.35869,50.718274],[-56.73865,51.287438],[-55.870977,51.632094],[-55.406974,51.588273],[-55.600218,51.317075],[-56.134036,50.68701]]],[[[-132.710008,54.040009],[-132.710009,54.040009],[-132.710008,54.040009],[-132.710008,54.040009],[-131.74999,54.120004],[-132.04948,52.984621],[-131.179043,52.180433],[-131.57783,52.182371],[-132.180428,52.639707],[-132.549992,53.100015],[-133.054611,53.411469],[-133.239664,53.85108],[-133.180004,54.169975],[-132.710008,54.040009]]],[[[-79.26582,62.158675],[-79.65752,61.63308],[-80.09956,61.7181],[-80.36215,62.01649],[-80.315395,62.085565],[-79.92939,62.3856],[-79.52002,62.36371],[-79.26582,62.158675]]],[[[-81.89825,62.7108],[-83.06857,62.15922],[-83.77462,62.18231],[-83.99367,62.4528],[-83.25048,62.91409],[-81.87699,62.90458],[-81.89825,62.7108]]],[[[-85.161308,65.657285],[-84.975764,65.217518],[-84.464012,65.371772],[-83.882626,65.109618],[-82.787577,64.766693],[-81.642014,64.455136],[-81.55344,63.979609],[-80.817361,64.057486],[-80.103451,63.725981],[-80.99102,63.411246],[-82.547178,63.651722],[-83.108798,64.101876],[-84.100417,63.569712],[-85.523405,63.052379],[-85.866769,63.637253],[-87.221983,63.541238],[-86.35276,64.035833],[-86.224886,64.822917],[-85.883848,65.738778],[-85.161308,65.657285]]],[[[-75.86588,67.14886],[-76.98687,67.09873],[-77.2364,67.58809],[-76.81166,68.14856],[-75.89521,68.28721],[-75.1145,68.01036],[-75.10333,67.58202],[-75.21597,67.44425],[-75.86588,67.14886]]],[[[-95.647681,69.10769],[-96.269521,68.75704],[-97.617401,69.06003],[-98.431801,68.9507],[-99.797401,69.40003],[-98.917401,69.71003],[-98.218261,70.14354],[-97.157401,69.86003],[-96.557401,69.68003],[-96.257401,69.49003],[-95.647681,69.10769]]],[[[-90.5471,69.49766],[-90.55151,68.47499],[-89.21515,69.25873],[-88.01966,68.61508],[-88.31749,67.87338],[-87.35017,67.19872],[-86.30607,67.92146],[-85.57664,68.78456],[-85.52197,69.88211],[-84.10081,69.80539],[-82.62258,69.65826],[-81.28043,69.16202],[-81.2202,68.66567],[-81.96436,68.13253],[-81.25928,67.59716],[-81.38653,67.11078],[-83.34456,66.41154],[-84.73542,66.2573],[-85.76943,66.55833],[-86.0676,66.05625],[-87.03143,65.21297],[-87.32324,64.77563],[-88.48296,64.09897],[-89.91444,64.03273],[-90.70398,63.61017],[-90.77004,62.96021],[-91.93342,62.83508],[-93.15698,62.02469],[-94.24153,60.89865],[-94.62931,60.11021],[-94.6846,58.94882],[-93.21502,58.78212],[-92.76462,57.84571],[-92.29703,57.08709],[-90.89769,57.28468],[-89.03953,56.85172],[-88.03978,56.47162],[-87.32421,55.99914],[-86.07121,55.72383],[-85.01181,55.3026],[-83.36055,55.24489],[-82.27285,55.14832],[-82.4362,54.28227],[-82.12502,53.27703],[-81.40075,52.15788],[-79.91289,51.20842],[-79.14301,51.53393],[-78.60191,52.56208],[-79.12421,54.14145],[-79.82958,54.66772],[-78.22874,55.13645],[-77.0956,55.83741],[-76.54137,56.53423],[-76.62319,57.20263],[-77.30226,58.05209],[-78.51688,58.80458],[-77.33676,59.85261],[-77.77272,60.75788],[-78.10687,62.31964],[-77.41067,62.55053],[-75.69621,62.2784],[-74.6682,62.18111],[-73.83988,62.4438],[-72.90853,62.10507],[-71.67708,61.52535],[-71.37369,61.13717],[-69.59042,61.06141],[-69.62033,60.22125],[-69.2879,58.95736],[-68.37455,58.80106],[-67.64976,58.21206],[-66.20178,58.76731],[-65.24517,59.87071],[-64.58352,60.33558],[-63.80475,59.4426],[-62.50236,58.16708],[-61.39655,56.96745],[-61.79866,56.33945],[-60.46853,55.77548],[-59.56962,55.20407],[-57.97508,54.94549],[-57.3332,54.6265],[-56.93689,53.78032],[-56.15811,53.64749],[-55.75632,53.27036],[-55.68338,52.14664],[-56.40916,51.7707],[-57.12691,51.41972],[-58.77482,51.0643],[-60.03309,50.24277],[-61.72366,50.08046],[-63.86251,50.29099],[-65.36331,50.2982],[-66.39905,50.22897],[-67.23631,49.51156],[-68.51114,49.06836],[-69.95362,47.74488],[-71.10458,46.82171],[-70.25522,46.98606],[-68.65,48.3],[-66.55243,49.1331],[-65.05626,49.23278],[-64.17099,48.74248],[-65.11545,48.07085],[-64.79854,46.99297],[-64.47219,46.23849],[-63.17329,45.73902],[-61.52072,45.88377],[-60.51815,47.00793],[-60.4486,46.28264],[-59.80287,45.9204],[-61.03988,45.26525],[-63.25471,44.67014],[-64.24656,44.26553],[-65.36406,43.54523],[-66.1234,43.61867],[-66.16173,44.46512],[-64.42549,45.29204],[-66.02605,45.25931],[-67.13741,45.13753],[-67.79134,45.70281],[-67.79046,47.06636],[-68.23444,47.35486],[-68.905,47.185],[-69.237216,47.447781],[-69.99997,46.69307],[-70.305,45.915],[-70.66,45.46],[-71.08482,45.30524],[-71.405,45.255],[-71.50506,45.0082],[-73.34783,45.00738],[-74.867,45.00048],[-75.31821,44.81645],[-76.375,44.09631],[-76.5,44.018459],[-76.820034,43.628784],[-77.737885,43.629056],[-78.72028,43.625089],[-79.171674,43.466339],[-79.01,43.27],[-78.92,42.965],[-78.939362,42.863611],[-80.247448,42.3662],[-81.277747,42.209026],[-82.439278,41.675105],[-82.690089,41.675105],[-83.02981,41.832796],[-83.142,41.975681],[-83.12,42.08],[-82.9,42.43],[-82.43,42.98],[-82.137642,43.571088],[-82.337763,44.44],[-82.550925,45.347517],[-83.592851,45.816894],[-83.469551,45.994686],[-83.616131,46.116927],[-83.890765,46.116927],[-84.091851,46.275419],[-84.14212,46.512226],[-84.3367,46.40877],[-84.6049,46.4396],[-84.543749,46.538684],[-84.779238,46.637102],[-84.87608,46.900083],[-85.652363,47.220219],[-86.461991,47.553338],[-87.439793,47.94],[-88.378114,48.302918],[-89.272917,48.019808],[-89.6,48.01],[-90.83,48.27],[-91.64,48.14],[-92.61,48.45],[-93.63087,48.60926],[-94.32914,48.67074],[-94.64,48.84],[-94.81758,49.38905],[-95.15609,49.38425],[-95.15907,49],[-97.22872,49.0007],[-100.65,49],[-104.04826,48.99986],[-107.05,49],[-110.05,49],[-113,49],[-116.04818,49],[-117.03121,49],[-120,49],[-122.84,49],[-122.97421,49.002538],[-124.91024,49.98456],[-125.62461,50.41656],[-127.43561,50.83061],[-127.99276,51.71583],[-127.85032,52.32961],[-129.12979,52.75538],[-129.30523,53.56159],[-130.51497,54.28757],[-130.53611,54.80278],[-129.98,55.285],[-130.00778,55.91583],[-131.70781,56.55212],[-132.73042,57.69289],[-133.35556,58.41028],[-134.27111,58.86111],[-134.945,59.27056],[-135.47583,59.78778],[-136.47972,59.46389],[-137.4525,58.905],[-138.34089,59.56211],[-139.039,60],[-140.013,60.27682],[-140.99778,60.30639],[-140.9925,66.00003],[-140.986,69.712],[-139.12052,69.47102],[-137.54636,68.99002],[-136.50358,68.89804],[-135.62576,69.31512],[-134.41464,69.62743],[-132.92925,69.50534],[-131.43136,69.94451],[-129.79471,70.19369],[-129.10773,69.77927],[-128.36156,70.01286],[-128.13817,70.48384],[-127.44712,70.37721],[-125.75632,69.48058],[-124.42483,70.1584],[-124.28968,69.39969],[-123.06108,69.56372],[-122.6835,69.85553],[-121.47226,69.79778],[-119.94288,69.37786],[-117.60268,69.01128],[-116.22643,68.84151],[-115.2469,68.90591],[-113.89794,68.3989],[-115.30489,67.90261],[-113.49727,67.68815],[-110.798,67.80612],[-109.94619,67.98104],[-108.8802,67.38144],[-107.79239,67.88736],[-108.81299,68.31164],[-108.16721,68.65392],[-106.95,68.7],[-106.15,68.8],[-105.34282,68.56122],[-104.33791,68.018],[-103.22115,68.09775],[-101.45433,67.64689],[-99.90195,67.80566],[-98.4432,67.78165],[-98.5586,68.40394],[-97.66948,68.57864],[-96.11991,68.23939],[-96.12588,67.29338],[-95.48943,68.0907],[-94.685,68.06383],[-94.23282,69.06903],[-95.30408,69.68571],[-96.47131,70.08976],[-96.39115,71.19482],[-95.2088,71.92053],[-93.88997,71.76015],[-92.87818,71.31869],[-91.51964,70.19129],[-92.40692,69.69997],[-90.5471,69.49766]]],[[[-114.16717,73.12145],[-114.66634,72.65277],[-112.44102,72.9554],[-111.05039,72.4504],[-109.92035,72.96113],[-109.00654,72.63335],[-108.18835,71.65089],[-107.68599,72.06548],[-108.39639,73.08953],[-107.51645,73.23598],[-106.52259,73.07601],[-105.40246,72.67259],[-104.77484,71.6984],[-104.46476,70.99297],[-102.78537,70.49776],[-100.98078,70.02432],[-101.08929,69.58447],[-102.73116,69.50402],[-102.09329,69.11962],[-102.43024,68.75282],[-104.24,68.91],[-105.96,69.18],[-107.12254,69.11922],[-109,68.78],[-111.534149,68.630059],[-113.3132,68.53554],[-113.85496,69.00744],[-115.22,69.28],[-116.10794,69.16821],[-117.34,69.96],[-116.67473,70.06655],[-115.13112,70.2373],[-113.72141,70.19237],[-112.4161,70.36638],[-114.35,70.6],[-116.48684,70.52045],[-117.9048,70.54056],[-118.43238,70.9092],[-116.11311,71.30918],[-117.65568,71.2952],[-119.40199,71.55859],[-118.56267,72.30785],[-117.86642,72.70594],[-115.18909,73.31459],[-114.16717,73.12145]]],[[[-104.5,73.42],[-105.38,72.76],[-106.94,73.46],[-106.6,73.6],[-105.26,73.64],[-104.5,73.42]]],[[[-76.34,73.102685],[-76.251404,72.826385],[-77.314438,72.855545],[-78.39167,72.876656],[-79.486252,72.742203],[-79.775833,72.802902],[-80.876099,73.333183],[-80.833885,73.693184],[-80.353058,73.75972],[-78.064438,73.651932],[-76.34,73.102685]]],[[[-86.562179,73.157447],[-85.774371,72.534126],[-84.850112,73.340278],[-82.31559,73.750951],[-80.600088,72.716544],[-80.748942,72.061907],[-78.770639,72.352173],[-77.824624,72.749617],[-75.605845,72.243678],[-74.228616,71.767144],[-74.099141,71.33084],[-72.242226,71.556925],[-71.200015,70.920013],[-68.786054,70.525024],[-67.91497,70.121948],[-66.969033,69.186087],[-68.805123,68.720198],[-66.449866,68.067163],[-64.862314,67.847539],[-63.424934,66.928473],[-61.851981,66.862121],[-62.163177,66.160251],[-63.918444,64.998669],[-65.14886,65.426033],[-66.721219,66.388041],[-68.015016,66.262726],[-68.141287,65.689789],[-67.089646,65.108455],[-65.73208,64.648406],[-65.320168,64.382737],[-64.669406,63.392927],[-65.013804,62.674185],[-66.275045,62.945099],[-68.783186,63.74567],[-67.369681,62.883966],[-66.328297,62.280075],[-66.165568,61.930897],[-68.877367,62.330149],[-71.023437,62.910708],[-72.235379,63.397836],[-71.886278,63.679989],[-73.378306,64.193963],[-74.834419,64.679076],[-74.818503,64.389093],[-77.70998,64.229542],[-78.555949,64.572906],[-77.897281,65.309192],[-76.018274,65.326969],[-73.959795,65.454765],[-74.293883,65.811771],[-73.944912,66.310578],[-72.651167,67.284576],[-72.92606,67.726926],[-73.311618,68.069437],[-74.843307,68.554627],[-76.869101,68.894736],[-76.228649,69.147769],[-77.28737,69.76954],[-78.168634,69.826488],[-78.957242,70.16688],[-79.492455,69.871808],[-81.305471,69.743185],[-84.944706,69.966634],[-87.060003,70.260001],[-88.681713,70.410741],[-89.51342,70.762038],[-88.467721,71.218186],[-89.888151,71.222552],[-90.20516,72.235074],[-89.436577,73.129464],[-88.408242,73.537889],[-85.826151,73.803816],[-86.562179,73.157447]]],[[[-100.35642,73.84389],[-99.16387,73.63339],[-97.38,73.76],[-97.12,73.47],[-98.05359,72.99052],[-96.54,72.56],[-96.72,71.66],[-98.35966,71.27285],[-99.32286,71.35639],[-100.01482,71.73827],[-102.5,72.51],[-102.48,72.83],[-100.43836,72.70588],[-101.54,73.36],[-100.35642,73.84389]]],[[[-93.196296,72.771992],[-94.269047,72.024596],[-95.409856,72.061881],[-96.033745,72.940277],[-96.018268,73.43743],[-95.495793,73.862417],[-94.503658,74.134907],[-92.420012,74.100025],[-90.509793,73.856732],[-92.003965,72.966244],[-93.196296,72.771992]]],[[[-120.46,71.383602],[-123.09219,70.90164],[-123.62,71.34],[-125.928949,71.868688],[-125.5,72.292261],[-124.80729,73.02256],[-123.94,73.68],[-124.91775,74.29275],[-121.53788,74.44893],[-120.10978,74.24135],[-117.55564,74.18577],[-116.58442,73.89607],[-115.51081,73.47519],[-116.76794,73.22292],[-119.22,72.52],[-120.46,71.82],[-120.46,71.383602]]],[[[-93.612756,74.979997],[-94.156909,74.592347],[-95.608681,74.666864],[-96.820932,74.927623],[-96.288587,75.377828],[-94.85082,75.647218],[-93.977747,75.29649],[-93.612756,74.979997]]],[[[-98.5,76.72],[-97.735585,76.25656],[-97.704415,75.74344],[-98.16,75],[-99.80874,74.89744],[-100.88366,75.05736],[-100.86292,75.64075],[-102.50209,75.5638],[-102.56552,76.3366],[-101.48973,76.30537],[-99.98349,76.64634],[-98.57699,76.58859],[-98.5,76.72]]],[[[-108.21141,76.20168],[-107.81943,75.84552],[-106.92893,76.01282],[-105.881,75.9694],[-105.70498,75.47951],[-106.31347,75.00527],[-109.7,74.85],[-112.22307,74.41696],[-113.74381,74.39427],[-113.87135,74.72029],[-111.79421,75.1625],[-116.31221,75.04343],[-117.7104,75.2222],[-116.34602,76.19903],[-115.40487,76.47887],[-112.59056,76.14134],[-110.81422,75.54919],[-109.0671,75.47321],[-110.49726,76.42982],[-109.5811,76.79417],[-108.54859,76.67832],[-108.21141,76.20168]]],[[[-94.684086,77.097878],[-93.573921,76.776296],[-91.605023,76.778518],[-90.741846,76.449597],[-90.969661,76.074013],[-89.822238,75.847774],[-89.187083,75.610166],[-87.838276,75.566189],[-86.379192,75.482421],[-84.789625,75.699204],[-82.753445,75.784315],[-81.128531,75.713983],[-80.057511,75.336849],[-79.833933,74.923127],[-80.457771,74.657304],[-81.948843,74.442459],[-83.228894,74.564028],[-86.097452,74.410032],[-88.15035,74.392307],[-89.764722,74.515555],[-92.422441,74.837758],[-92.768285,75.38682],[-92.889906,75.882655],[-93.893824,76.319244],[-95.962457,76.441381],[-97.121379,76.751078],[-96.745123,77.161389],[-94.684086,77.097878]]],[[[-116.198587,77.645287],[-116.335813,76.876962],[-117.106051,76.530032],[-118.040412,76.481172],[-119.899318,76.053213],[-121.499995,75.900019],[-122.854924,76.116543],[-122.854925,76.116543],[-121.157535,76.864508],[-119.103939,77.51222],[-117.570131,77.498319],[-116.198587,77.645287]]],[[[-93.840003,77.519997],[-94.295608,77.491343],[-96.169654,77.555111],[-96.436304,77.834629],[-94.422577,77.820005],[-93.720656,77.634331],[-93.840003,77.519997]]],[[[-110.186938,77.697015],[-112.051191,77.409229],[-113.534279,77.732207],[-112.724587,78.05105],[-111.264443,78.152956],[-109.854452,77.996325],[-110.186938,77.697015]]],[[[-109.663146,78.601973],[-110.881314,78.40692],[-112.542091,78.407902],[-112.525891,78.550555],[-111.50001,78.849994],[-110.963661,78.804441],[-109.663146,78.601973]]],[[[-95.830295,78.056941],[-97.309843,77.850597],[-98.124289,78.082857],[-98.552868,78.458105],[-98.631984,78.87193],[-97.337231,78.831984],[-96.754399,78.765813],[-95.559278,78.418315],[-95.830295,78.056941]]],[[[-100.060192,78.324754],[-99.670939,77.907545],[-101.30394,78.018985],[-102.949809,78.343229],[-105.176133,78.380332],[-104.210429,78.67742],[-105.41958,78.918336],[-105.492289,79.301594],[-103.529282,79.165349],[-100.825158,78.800462],[-100.060192,78.324754]]],[[[-87.02,79.66],[-85.81435,79.3369],[-87.18756,79.0393],[-89.03535,78.28723],[-90.80436,78.21533],[-92.87669,78.34333],[-93.95116,78.75099],[-93.93574,79.11373],[-93.14524,79.3801],[-94.974,79.37248],[-96.07614,79.70502],[-96.70972,80.15777],[-96.01644,80.60233],[-95.32345,80.90729],[-94.29843,80.97727],[-94.73542,81.20646],[-92.40984,81.25739],[-91.13289,80.72345],[-89.45,80.509322],[-87.81,80.32],[-87.02,79.66]]],[[[-68.5,83.106322],[-65.82735,83.02801],[-63.68,82.9],[-61.85,82.6286],[-61.89388,82.36165],[-64.334,81.92775],[-66.75342,81.72527],[-67.65755,81.50141],[-65.48031,81.50657],[-67.84,80.9],[-69.4697,80.61683],[-71.18,79.8],[-73.2428,79.63415],[-73.88,79.430162],[-76.90773,79.32309],[-75.52924,79.19766],[-76.22046,79.01907],[-75.39345,78.52581],[-76.34354,78.18296],[-77.88851,77.89991],[-78.36269,77.50859],[-79.75951,77.20968],[-79.61965,76.98336],[-77.91089,77.022045],[-77.88911,76.777955],[-80.56125,76.17812],[-83.17439,76.45403],[-86.11184,76.29901],[-87.6,76.42],[-89.49068,76.47239],[-89.6161,76.95213],[-87.76739,77.17833],[-88.26,77.9],[-87.65,77.970222],[-84.97634,77.53873],[-86.34,78.18],[-87.96192,78.37181],[-87.15198,78.75867],[-85.37868,78.9969],[-85.09495,79.34543],[-86.50734,79.73624],[-86.93179,80.25145],[-84.19844,80.20836],[-83.408696,80.1],[-81.84823,80.46442],[-84.1,80.58],[-87.59895,80.51627],[-89.36663,80.85569],[-90.2,81.26],[-91.36786,81.5531],[-91.58702,81.89429],[-90.1,82.085],[-88.93227,82.11751],[-86.97024,82.27961],[-85.5,82.652273],[-84.260005,82.6],[-83.18,82.32],[-82.42,82.86],[-81.1,83.02],[-79.30664,83.13056],[-76.25,83.172059],[-75.71878,83.06404],[-72.83153,83.23324],[-70.665765,83.169781],[-68.5,83.106322]]]]}},
    +{"type":"Feature","id":"CHE","properties":{"name":"Switzerland"},"geometry":{"type":"Polygon","coordinates":[[[9.594226,47.525058],[9.632932,47.347601],[9.47997,47.10281],[9.932448,46.920728],[10.442701,46.893546],[10.363378,46.483571],[9.922837,46.314899],[9.182882,46.440215],[8.966306,46.036932],[8.489952,46.005151],[8.31663,46.163642],[7.755992,45.82449],[7.273851,45.776948],[6.843593,45.991147],[6.5001,46.429673],[6.022609,46.27299],[6.037389,46.725779],[6.768714,47.287708],[6.736571,47.541801],[7.192202,47.449766],[7.466759,47.620582],[8.317301,47.61358],[8.522612,47.830828],[9.594226,47.525058]]]}},
    +{"type":"Feature","id":"CHL","properties":{"name":"Chile"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-68.63401,-52.63637],[-68.63335,-54.8695],[-67.56244,-54.87001],[-66.95992,-54.89681],[-67.29103,-55.30124],[-68.14863,-55.61183],[-68.639991,-55.580018],[-69.2321,-55.49906],[-69.95809,-55.19843],[-71.00568,-55.05383],[-72.2639,-54.49514],[-73.2852,-53.95752],[-74.66253,-52.83749],[-73.8381,-53.04743],[-72.43418,-53.7154],[-71.10773,-54.07433],[-70.59178,-53.61583],[-70.26748,-52.93123],[-69.34565,-52.5183],[-68.63401,-52.63637]]],[[[-68.219913,-21.494347],[-67.82818,-22.872919],[-67.106674,-22.735925],[-66.985234,-22.986349],[-67.328443,-24.025303],[-68.417653,-24.518555],[-68.386001,-26.185016],[-68.5948,-26.506909],[-68.295542,-26.89934],[-69.001235,-27.521214],[-69.65613,-28.459141],[-70.01355,-29.367923],[-69.919008,-30.336339],[-70.535069,-31.36501],[-70.074399,-33.09121],[-69.814777,-33.273886],[-69.817309,-34.193571],[-70.388049,-35.169688],[-70.364769,-36.005089],[-71.121881,-36.658124],[-71.118625,-37.576827],[-70.814664,-38.552995],[-71.413517,-38.916022],[-71.680761,-39.808164],[-71.915734,-40.832339],[-71.746804,-42.051386],[-72.148898,-42.254888],[-71.915424,-43.408565],[-71.464056,-43.787611],[-71.793623,-44.207172],[-71.329801,-44.407522],[-71.222779,-44.784243],[-71.659316,-44.973689],[-71.552009,-45.560733],[-71.917258,-46.884838],[-72.447355,-47.738533],[-72.331161,-48.244238],[-72.648247,-48.878618],[-73.415436,-49.318436],[-73.328051,-50.378785],[-72.975747,-50.74145],[-72.309974,-50.67701],[-72.329404,-51.425956],[-71.914804,-52.009022],[-69.498362,-52.142761],[-68.571545,-52.299444],[-69.461284,-52.291951],[-69.94278,-52.537931],[-70.845102,-52.899201],[-71.006332,-53.833252],[-71.429795,-53.856455],[-72.557943,-53.53141],[-73.702757,-52.835069],[-73.702757,-52.83507],[-74.946763,-52.262754],[-75.260026,-51.629355],[-74.976632,-51.043396],[-75.479754,-50.378372],[-75.608015,-48.673773],[-75.18277,-47.711919],[-74.126581,-46.939253],[-75.644395,-46.647643],[-74.692154,-45.763976],[-74.351709,-44.103044],[-73.240356,-44.454961],[-72.717804,-42.383356],[-73.3889,-42.117532],[-73.701336,-43.365776],[-74.331943,-43.224958],[-74.017957,-41.794813],[-73.677099,-39.942213],[-73.217593,-39.258689],[-73.505559,-38.282883],[-73.588061,-37.156285],[-73.166717,-37.12378],[-72.553137,-35.50884],[-71.861732,-33.909093],[-71.43845,-32.418899],[-71.668721,-30.920645],[-71.370083,-30.095682],[-71.489894,-28.861442],[-70.905124,-27.64038],[-70.724954,-25.705924],[-70.403966,-23.628997],[-70.091246,-21.393319],[-70.16442,-19.756468],[-70.372572,-18.347975],[-69.858444,-18.092694],[-69.590424,-17.580012],[-69.100247,-18.260125],[-68.966818,-18.981683],[-68.442225,-19.405068],[-68.757167,-20.372658],[-68.219913,-21.494347]]]]}},
    +{"type":"Feature","id":"CHN","properties":{"name":"China"},"geometry":{"type":"MultiPolygon","coordinates":[[[[110.339188,18.678395],[109.47521,18.197701],[108.655208,18.507682],[108.626217,19.367888],[109.119056,19.821039],[110.211599,20.101254],[110.786551,20.077534],[111.010051,19.69593],[110.570647,19.255879],[110.339188,18.678395]]],[[[127.657407,49.76027],[129.397818,49.4406],[130.582293,48.729687],[130.987282,47.790132],[132.506672,47.78897],[133.373596,48.183442],[135.026311,48.47823],[134.500814,47.57844],[134.112362,47.212467],[133.769644,46.116927],[133.097127,45.144066],[131.883454,45.321162],[131.025212,44.967953],[131.288555,44.11152],[131.144688,42.92999],[130.633866,42.903015],[130.640016,42.395009],[129.994267,42.985387],[129.596669,42.424982],[128.052215,41.994285],[128.208433,41.466772],[127.343783,41.503152],[126.869083,41.816569],[126.182045,41.107336],[125.079942,40.569824],[124.265625,39.928493],[122.86757,39.637788],[122.131388,39.170452],[121.054554,38.897471],[121.585995,39.360854],[121.376757,39.750261],[122.168595,40.422443],[121.640359,40.94639],[120.768629,40.593388],[119.639602,39.898056],[119.023464,39.252333],[118.042749,39.204274],[117.532702,38.737636],[118.059699,38.061476],[118.87815,37.897325],[118.911636,37.448464],[119.702802,37.156389],[120.823457,37.870428],[121.711259,37.481123],[122.357937,37.454484],[122.519995,36.930614],[121.104164,36.651329],[120.637009,36.11144],[119.664562,35.609791],[119.151208,34.909859],[120.227525,34.360332],[120.620369,33.376723],[121.229014,32.460319],[121.908146,31.692174],[121.891919,30.949352],[121.264257,30.676267],[121.503519,30.142915],[122.092114,29.83252],[121.938428,29.018022],[121.684439,28.225513],[121.125661,28.135673],[120.395473,27.053207],[119.585497,25.740781],[118.656871,24.547391],[117.281606,23.624501],[115.890735,22.782873],[114.763827,22.668074],[114.152547,22.22376],[113.80678,22.54834],[113.241078,22.051367],[111.843592,21.550494],[110.785466,21.397144],[110.444039,20.341033],[109.889861,20.282457],[109.627655,21.008227],[109.864488,21.395051],[108.522813,21.715212],[108.05018,21.55238],[107.04342,21.811899],[106.567273,22.218205],[106.725403,22.794268],[105.811247,22.976892],[105.329209,23.352063],[104.476858,22.81915],[103.504515,22.703757],[102.706992,22.708795],[102.170436,22.464753],[101.652018,22.318199],[101.80312,21.174367],[101.270026,21.201652],[101.180005,21.436573],[101.150033,21.849984],[100.416538,21.558839],[99.983489,21.742937],[99.240899,22.118314],[99.531992,22.949039],[98.898749,23.142722],[98.660262,24.063286],[97.60472,23.897405],[97.724609,25.083637],[98.671838,25.918703],[98.712094,26.743536],[98.68269,27.508812],[98.246231,27.747221],[97.911988,28.335945],[97.327114,28.261583],[96.248833,28.411031],[96.586591,28.83098],[96.117679,29.452802],[95.404802,29.031717],[94.56599,29.277438],[93.413348,28.640629],[92.503119,27.896876],[91.696657,27.771742],[91.258854,28.040614],[90.730514,28.064954],[90.015829,28.296439],[89.47581,28.042759],[88.814248,27.299316],[88.730326,28.086865],[88.120441,27.876542],[86.954517,27.974262],[85.82332,28.203576],[85.011638,28.642774],[84.23458,28.839894],[83.898993,29.320226],[83.337115,29.463732],[82.327513,30.115268],[81.525804,30.422717],[81.111256,30.183481],[79.721367,30.882715],[78.738894,31.515906],[78.458446,32.618164],[79.176129,32.48378],[79.208892,32.994395],[78.811086,33.506198],[78.912269,34.321936],[77.837451,35.49401],[76.192848,35.898403],[75.896897,36.666806],[75.158028,37.133031],[74.980002,37.41999],[74.829986,37.990007],[74.864816,38.378846],[74.257514,38.606507],[73.928852,38.505815],[73.675379,39.431237],[73.960013,39.660008],[73.822244,39.893973],[74.776862,40.366425],[75.467828,40.562072],[76.526368,40.427946],[76.904484,41.066486],[78.187197,41.185316],[78.543661,41.582243],[80.11943,42.123941],[80.25999,42.349999],[80.18015,42.920068],[80.866206,43.180362],[79.966106,44.917517],[81.947071,45.317027],[82.458926,45.53965],[83.180484,47.330031],[85.16429,47.000956],[85.720484,47.452969],[85.768233,48.455751],[86.598776,48.549182],[87.35997,49.214981],[87.751264,49.297198],[88.013832,48.599463],[88.854298,48.069082],[90.280826,47.693549],[90.970809,46.888146],[90.585768,45.719716],[90.94554,45.286073],[92.133891,45.115076],[93.480734,44.975472],[94.688929,44.352332],[95.306875,44.241331],[95.762455,43.319449],[96.349396,42.725635],[97.451757,42.74889],[99.515817,42.524691],[100.845866,42.663804],[101.83304,42.514873],[103.312278,41.907468],[104.522282,41.908347],[104.964994,41.59741],[106.129316,42.134328],[107.744773,42.481516],[109.243596,42.519446],[110.412103,42.871234],[111.129682,43.406834],[111.829588,43.743118],[111.667737,44.073176],[111.348377,44.457442],[111.873306,45.102079],[112.436062,45.011646],[113.463907,44.808893],[114.460332,45.339817],[115.985096,45.727235],[116.717868,46.388202],[117.421701,46.672733],[118.874326,46.805412],[119.66327,46.69268],[119.772824,47.048059],[118.866574,47.74706],[118.064143,48.06673],[117.295507,47.697709],[116.308953,47.85341],[115.742837,47.726545],[115.485282,48.135383],[116.191802,49.134598],[116.678801,49.888531],[117.879244,49.510983],[119.288461,50.142883],[119.279366,50.582908],[120.18205,51.643566],[120.738191,51.964115],[120.725789,52.516226],[120.177089,52.753886],[121.003085,53.251401],[122.245748,53.431726],[123.571507,53.458804],[125.068211,53.161045],[125.946349,52.792799],[126.564399,51.784255],[126.939157,51.353894],[127.287456,50.739797],[127.657407,49.76027]]]]}},
    +{"type":"Feature","id":"CIV","properties":{"name":"Ivory Coast"},"geometry":{"type":"Polygon","coordinates":[[[-2.856125,4.994476],[-3.311084,4.984296],[-4.00882,5.179813],[-4.649917,5.168264],[-5.834496,4.993701],[-6.528769,4.705088],[-7.518941,4.338288],[-7.712159,4.364566],[-7.635368,5.188159],[-7.539715,5.313345],[-7.570153,5.707352],[-7.993693,6.12619],[-8.311348,6.193033],[-8.60288,6.467564],[-8.385452,6.911801],[-8.485446,7.395208],[-8.439298,7.686043],[-8.280703,7.68718],[-8.221792,8.123329],[-8.299049,8.316444],[-8.203499,8.455453],[-7.8321,8.575704],[-8.079114,9.376224],[-8.309616,9.789532],[-8.229337,10.12902],[-8.029944,10.206535],[-7.89959,10.297382],[-7.622759,10.147236],[-6.850507,10.138994],[-6.666461,10.430811],[-6.493965,10.411303],[-6.205223,10.524061],[-6.050452,10.096361],[-5.816926,10.222555],[-5.404342,10.370737],[-4.954653,10.152714],[-4.779884,9.821985],[-4.330247,9.610835],[-3.980449,9.862344],[-3.511899,9.900326],[-2.827496,9.642461],[-2.56219,8.219628],[-2.983585,7.379705],[-3.24437,6.250472],[-2.810701,5.389051],[-2.856125,4.994476]]]}},
    +{"type":"Feature","id":"CMR","properties":{"name":"Cameroon"},"geometry":{"type":"Polygon","coordinates":[[[13.075822,2.267097],[12.951334,2.321616],[12.35938,2.192812],[11.751665,2.326758],[11.276449,2.261051],[9.649158,2.283866],[9.795196,3.073404],[9.404367,3.734527],[8.948116,3.904129],[8.744924,4.352215],[8.488816,4.495617],[8.500288,4.771983],[8.757533,5.479666],[9.233163,6.444491],[9.522706,6.453482],[10.118277,7.03877],[10.497375,7.055358],[11.058788,6.644427],[11.745774,6.981383],[11.839309,7.397042],[12.063946,7.799808],[12.218872,8.305824],[12.753672,8.717763],[12.955468,9.417772],[13.1676,9.640626],[13.308676,10.160362],[13.57295,10.798566],[14.415379,11.572369],[14.468192,11.904752],[14.577178,12.085361],[14.181336,12.483657],[14.213531,12.802035],[14.495787,12.859396],[14.893386,12.219048],[14.960152,11.555574],[14.923565,10.891325],[15.467873,9.982337],[14.909354,9.992129],[14.627201,9.920919],[14.171466,10.021378],[13.954218,9.549495],[14.544467,8.965861],[14.979996,8.796104],[15.120866,8.38215],[15.436092,7.692812],[15.27946,7.421925],[14.776545,6.408498],[14.53656,6.226959],[14.459407,5.451761],[14.558936,5.030598],[14.478372,4.732605],[14.950953,4.210389],[15.03622,3.851367],[15.405396,3.335301],[15.862732,3.013537],[15.907381,2.557389],[16.012852,2.26764],[15.940919,1.727673],[15.146342,1.964015],[14.337813,2.227875],[13.075822,2.267097]]]}},
    +{"type":"Feature","id":"COD","properties":{"name":"Democratic Republic of the Congo"},"geometry":{"type":"Polygon","coordinates":[[[30.83386,3.509166],[30.773347,2.339883],[31.174149,2.204465],[30.85267,1.849396],[30.468508,1.583805],[30.086154,1.062313],[29.875779,0.59738],[29.819503,-0.20531],[29.587838,-0.587406],[29.579466,-1.341313],[29.291887,-1.620056],[29.254835,-2.21511],[29.117479,-2.292211],[29.024926,-2.839258],[29.276384,-3.293907],[29.339998,-4.499983],[29.519987,-5.419979],[29.419993,-5.939999],[29.620032,-6.520015],[30.199997,-7.079981],[30.740015,-8.340007],[30.346086,-8.238257],[29.002912,-8.407032],[28.734867,-8.526559],[28.449871,-9.164918],[28.673682,-9.605925],[28.49607,-10.789884],[28.372253,-11.793647],[28.642417,-11.971569],[29.341548,-12.360744],[29.616001,-12.178895],[29.699614,-13.257227],[28.934286,-13.248958],[28.523562,-12.698604],[28.155109,-12.272481],[27.388799,-12.132747],[27.16442,-11.608748],[26.553088,-11.92444],[25.75231,-11.784965],[25.418118,-11.330936],[24.78317,-11.238694],[24.314516,-11.262826],[24.257155,-10.951993],[23.912215,-10.926826],[23.456791,-10.867863],[22.837345,-11.017622],[22.402798,-10.993075],[22.155268,-11.084801],[22.208753,-9.894796],[21.875182,-9.523708],[21.801801,-8.908707],[21.949131,-8.305901],[21.746456,-7.920085],[21.728111,-7.290872],[20.514748,-7.299606],[20.601823,-6.939318],[20.091622,-6.94309],[20.037723,-7.116361],[19.417502,-7.155429],[19.166613,-7.738184],[19.016752,-7.988246],[18.464176,-7.847014],[18.134222,-7.987678],[17.47297,-8.068551],[17.089996,-7.545689],[16.860191,-7.222298],[16.57318,-6.622645],[16.326528,-5.87747],[13.375597,-5.864241],[13.024869,-5.984389],[12.735171,-5.965682],[12.322432,-6.100092],[12.182337,-5.789931],[12.436688,-5.684304],[12.468004,-5.248362],[12.631612,-4.991271],[12.995517,-4.781103],[13.25824,-4.882957],[13.600235,-4.500138],[14.144956,-4.510009],[14.209035,-4.793092],[14.582604,-4.970239],[15.170992,-4.343507],[15.75354,-3.855165],[16.00629,-3.535133],[15.972803,-2.712392],[16.407092,-1.740927],[16.865307,-1.225816],[17.523716,-0.74383],[17.638645,-0.424832],[17.663553,-0.058084],[17.82654,0.288923],[17.774192,0.855659],[17.898835,1.741832],[18.094276,2.365722],[18.393792,2.900443],[18.453065,3.504386],[18.542982,4.201785],[18.932312,4.709506],[19.467784,5.031528],[20.290679,4.691678],[20.927591,4.322786],[21.659123,4.224342],[22.405124,4.02916],[22.704124,4.633051],[22.84148,4.710126],[23.297214,4.609693],[24.410531,5.108784],[24.805029,4.897247],[25.128833,4.927245],[25.278798,5.170408],[25.650455,5.256088],[26.402761,5.150875],[27.044065,5.127853],[27.374226,5.233944],[27.979977,4.408413],[28.428994,4.287155],[28.696678,4.455077],[29.159078,4.389267],[29.715995,4.600805],[29.9535,4.173699],[30.83386,3.509166]]]}},
    +{"type":"Feature","id":"COG","properties":{"name":"Republic of the Congo"},"geometry":{"type":"Polygon","coordinates":[[[12.995517,-4.781103],[12.62076,-4.438023],[12.318608,-4.60623],[11.914963,-5.037987],[11.093773,-3.978827],[11.855122,-3.426871],[11.478039,-2.765619],[11.820964,-2.514161],[12.495703,-2.391688],[12.575284,-1.948511],[13.109619,-2.42874],[13.992407,-2.470805],[14.29921,-1.998276],[14.425456,-1.333407],[14.316418,-0.552627],[13.843321,0.038758],[14.276266,1.19693],[14.026669,1.395677],[13.282631,1.314184],[13.003114,1.830896],[13.075822,2.267097],[14.337813,2.227875],[15.146342,1.964015],[15.940919,1.727673],[16.012852,2.26764],[16.537058,3.198255],[17.133042,3.728197],[17.8099,3.560196],[18.453065,3.504386],[18.393792,2.900443],[18.094276,2.365722],[17.898835,1.741832],[17.774192,0.855659],[17.82654,0.288923],[17.663553,-0.058084],[17.638645,-0.424832],[17.523716,-0.74383],[16.865307,-1.225816],[16.407092,-1.740927],[15.972803,-2.712392],[16.00629,-3.535133],[15.75354,-3.855165],[15.170992,-4.343507],[14.582604,-4.970239],[14.209035,-4.793092],[14.144956,-4.510009],[13.600235,-4.500138],[13.25824,-4.882957],[12.995517,-4.781103]]]}},
    +{"type":"Feature","id":"COL","properties":{"name":"Colombia"},"geometry":{"type":"Polygon","coordinates":[[[-75.373223,-0.152032],[-75.801466,0.084801],[-76.292314,0.416047],[-76.57638,0.256936],[-77.424984,0.395687],[-77.668613,0.825893],[-77.855061,0.809925],[-78.855259,1.380924],[-78.990935,1.69137],[-78.617831,1.766404],[-78.662118,2.267355],[-78.42761,2.629556],[-77.931543,2.696606],[-77.510431,3.325017],[-77.12769,3.849636],[-77.496272,4.087606],[-77.307601,4.667984],[-77.533221,5.582812],[-77.318815,5.845354],[-77.476661,6.691116],[-77.881571,7.223771],[-77.753414,7.70984],[-77.431108,7.638061],[-77.242566,7.935278],[-77.474723,8.524286],[-77.353361,8.670505],[-76.836674,8.638749],[-76.086384,9.336821],[-75.6746,9.443248],[-75.664704,9.774003],[-75.480426,10.61899],[-74.906895,11.083045],[-74.276753,11.102036],[-74.197223,11.310473],[-73.414764,11.227015],[-72.627835,11.731972],[-72.238195,11.95555],[-71.75409,12.437303],[-71.399822,12.376041],[-71.137461,12.112982],[-71.331584,11.776284],[-71.973922,11.608672],[-72.227575,11.108702],[-72.614658,10.821975],[-72.905286,10.450344],[-73.027604,9.73677],[-73.304952,9.152],[-72.78873,9.085027],[-72.660495,8.625288],[-72.439862,8.405275],[-72.360901,8.002638],[-72.479679,7.632506],[-72.444487,7.423785],[-72.198352,7.340431],[-71.960176,6.991615],[-70.674234,7.087785],[-70.093313,6.960376],[-69.38948,6.099861],[-68.985319,6.206805],[-68.265052,6.153268],[-67.695087,6.267318],[-67.34144,6.095468],[-67.521532,5.55687],[-67.744697,5.221129],[-67.823012,4.503937],[-67.621836,3.839482],[-67.337564,3.542342],[-67.303173,3.318454],[-67.809938,2.820655],[-67.447092,2.600281],[-67.181294,2.250638],[-66.876326,1.253361],[-67.065048,1.130112],[-67.259998,1.719999],[-67.53781,2.037163],[-67.868565,1.692455],[-69.816973,1.714805],[-69.804597,1.089081],[-69.218638,0.985677],[-69.252434,0.602651],[-69.452396,0.706159],[-70.015566,0.541414],[-70.020656,-0.185156],[-69.577065,-0.549992],[-69.420486,-1.122619],[-69.444102,-1.556287],[-69.893635,-4.298187],[-70.394044,-3.766591],[-70.692682,-3.742872],[-70.047709,-2.725156],[-70.813476,-2.256865],[-71.413646,-2.342802],[-71.774761,-2.16979],[-72.325787,-2.434218],[-73.070392,-2.308954],[-73.659504,-1.260491],[-74.122395,-1.002833],[-74.441601,-0.53082],[-75.106625,-0.057205],[-75.373223,-0.152032]]]}},
    +{"type":"Feature","id":"CRI","properties":{"name":"Costa Rica"},"geometry":{"type":"Polygon","coordinates":[[[-82.965783,8.225028],[-83.508437,8.446927],[-83.711474,8.656836],[-83.596313,8.830443],[-83.632642,9.051386],[-83.909886,9.290803],[-84.303402,9.487354],[-84.647644,9.615537],[-84.713351,9.908052],[-84.97566,10.086723],[-84.911375,9.795992],[-85.110923,9.55704],[-85.339488,9.834542],[-85.660787,9.933347],[-85.797445,10.134886],[-85.791709,10.439337],[-85.659314,10.754331],[-85.941725,10.895278],[-85.71254,11.088445],[-85.561852,11.217119],[-84.903003,10.952303],[-84.673069,11.082657],[-84.355931,10.999226],[-84.190179,10.79345],[-83.895054,10.726839],[-83.655612,10.938764],[-83.40232,10.395438],[-83.015677,9.992982],[-82.546196,9.566135],[-82.932891,9.476812],[-82.927155,9.07433],[-82.719183,8.925709],[-82.868657,8.807266],[-82.829771,8.626295],[-82.913176,8.423517],[-82.965783,8.225028]]]}},
    +{"type":"Feature","id":"CUB","properties":{"name":"Cuba"},"geometry":{"type":"Polygon","coordinates":[[[-82.268151,23.188611],[-81.404457,23.117271],[-80.618769,23.10598],[-79.679524,22.765303],[-79.281486,22.399202],[-78.347434,22.512166],[-77.993296,22.277194],[-77.146422,21.657851],[-76.523825,21.20682],[-76.19462,21.220565],[-75.598222,21.016624],[-75.67106,20.735091],[-74.933896,20.693905],[-74.178025,20.284628],[-74.296648,20.050379],[-74.961595,19.923435],[-75.63468,19.873774],[-76.323656,19.952891],[-77.755481,19.855481],[-77.085108,20.413354],[-77.492655,20.673105],[-78.137292,20.739949],[-78.482827,21.028613],[-78.719867,21.598114],[-79.285,21.559175],[-80.217475,21.827324],[-80.517535,22.037079],[-81.820943,22.192057],[-82.169992,22.387109],[-81.795002,22.636965],[-82.775898,22.68815],[-83.494459,22.168518],[-83.9088,22.154565],[-84.052151,21.910575],[-84.54703,21.801228],[-84.974911,21.896028],[-84.447062,22.20495],[-84.230357,22.565755],[-83.77824,22.788118],[-83.267548,22.983042],[-82.510436,23.078747],[-82.268151,23.188611]]]}},
    +{"type":"Feature","id":"TRNC","properties":{"name":"Northern Cyprus"},"geometry":{"type":"Polygon","coordinates":[[[32.73178,35.140026],[32.802474,35.145504],[32.946961,35.386703],[33.667227,35.373216],[34.576474,35.671596],[33.900804,35.245756],[33.973617,35.058506],[33.86644,35.093595],[33.675392,35.017863],[33.525685,35.038688],[33.475817,35.000345],[33.455922,35.101424],[33.383833,35.162712],[33.190977,35.173125],[32.919572,35.087833],[32.73178,35.140026]]]}},
    +{"type":"Feature","id":"CYP","properties":{"name":"Cyprus"},"geometry":{"type":"Polygon","coordinates":[[[33.973617,35.058506],[34.004881,34.978098],[32.979827,34.571869],[32.490296,34.701655],[32.256667,35.103232],[32.73178,35.140026],[32.919572,35.087833],[33.190977,35.173125],[33.383833,35.162712],[33.455922,35.101424],[33.475817,35.000345],[33.525685,35.038688],[33.675392,35.017863],[33.86644,35.093595],[33.973617,35.058506]]]}},
    +{"type":"Feature","id":"CZE","properties":{"name":"Czech Republic"},"geometry":{"type":"Polygon","coordinates":[[[16.960288,48.596982],[16.499283,48.785808],[16.029647,48.733899],[15.253416,49.039074],[14.901447,48.964402],[14.338898,48.555305],[13.595946,48.877172],[13.031329,49.307068],[12.521024,49.547415],[12.415191,49.969121],[12.240111,50.266338],[12.966837,50.484076],[13.338132,50.733234],[14.056228,50.926918],[14.307013,51.117268],[14.570718,51.002339],[15.016996,51.106674],[15.490972,50.78473],[16.238627,50.697733],[16.176253,50.422607],[16.719476,50.215747],[16.868769,50.473974],[17.554567,50.362146],[17.649445,50.049038],[18.392914,49.988629],[18.853144,49.49623],[18.554971,49.495015],[18.399994,49.315001],[18.170498,49.271515],[18.104973,49.043983],[17.913512,48.996493],[17.886485,48.903475],[17.545007,48.800019],[17.101985,48.816969],[16.960288,48.596982]]]}},
    +{"type":"Feature","id":"DEU","properties":{"name":"Germany"},"geometry":{"type":"Polygon","coordinates":[[[9.921906,54.983104],[9.93958,54.596642],[10.950112,54.363607],[10.939467,54.008693],[11.956252,54.196486],[12.51844,54.470371],[13.647467,54.075511],[14.119686,53.757029],[14.353315,53.248171],[14.074521,52.981263],[14.4376,52.62485],[14.685026,52.089947],[14.607098,51.745188],[15.016996,51.106674],[14.570718,51.002339],[14.307013,51.117268],[14.056228,50.926918],[13.338132,50.733234],[12.966837,50.484076],[12.240111,50.266338],[12.415191,49.969121],[12.521024,49.547415],[13.031329,49.307068],[13.595946,48.877172],[13.243357,48.416115],[12.884103,48.289146],[13.025851,47.637584],[12.932627,47.467646],[12.62076,47.672388],[12.141357,47.703083],[11.426414,47.523766],[10.544504,47.566399],[10.402084,47.302488],[9.896068,47.580197],[9.594226,47.525058],[8.522612,47.830828],[8.317301,47.61358],[7.466759,47.620582],[7.593676,48.333019],[8.099279,49.017784],[6.65823,49.201958],[6.18632,49.463803],[6.242751,49.902226],[6.043073,50.128052],[6.156658,50.803721],[5.988658,51.851616],[6.589397,51.852029],[6.84287,52.22844],[7.092053,53.144043],[6.90514,53.482162],[7.100425,53.693932],[7.936239,53.748296],[8.121706,53.527792],[8.800734,54.020786],[8.572118,54.395646],[8.526229,54.962744],[9.282049,54.830865],[9.921906,54.983104]]]}},
    +{"type":"Feature","id":"DJI","properties":{"name":"Djibouti"},"geometry":{"type":"Polygon","coordinates":[[[43.081226,12.699639],[43.317852,12.390148],[43.286381,11.974928],[42.715874,11.735641],[43.145305,11.46204],[42.776852,10.926879],[42.55493,11.10511],[42.31414,11.0342],[41.75557,11.05091],[41.73959,11.35511],[41.66176,11.6312],[42,12.1],[42.35156,12.54223],[42.779642,12.455416],[43.081226,12.699639]]]}},
    +{"type":"Feature","id":"DNK","properties":{"name":"Denmark"},"geometry":{"type":"MultiPolygon","coordinates":[[[[12.690006,55.609991],[12.089991,54.800015],[11.043543,55.364864],[10.903914,55.779955],[12.370904,56.111407],[12.690006,55.609991]]],[[[10.912182,56.458621],[10.667804,56.081383],[10.369993,56.190007],[9.649985,55.469999],[9.921906,54.983104],[9.282049,54.830865],[8.526229,54.962744],[8.120311,55.517723],[8.089977,56.540012],[8.256582,56.809969],[8.543438,57.110003],[9.424469,57.172066],[9.775559,57.447941],[10.580006,57.730017],[10.546106,57.215733],[10.25,56.890016],[10.369993,56.609982],[10.912182,56.458621]]]]}},
    +{"type":"Feature","id":"DOM","properties":{"name":"Dominican Republic"},"geometry":{"type":"Polygon","coordinates":[[[-71.712361,19.714456],[-71.587304,19.884911],[-70.806706,19.880286],[-70.214365,19.622885],[-69.950815,19.648],[-69.76925,19.293267],[-69.222126,19.313214],[-69.254346,19.015196],[-68.809412,18.979074],[-68.317943,18.612198],[-68.689316,18.205142],[-69.164946,18.422648],[-69.623988,18.380713],[-69.952934,18.428307],[-70.133233,18.245915],[-70.517137,18.184291],[-70.669298,18.426886],[-70.99995,18.283329],[-71.40021,17.598564],[-71.657662,17.757573],[-71.708305,18.044997],[-71.687738,18.31666],[-71.945112,18.6169],[-71.701303,18.785417],[-71.624873,19.169838],[-71.712361,19.714456]]]}},
    +{"type":"Feature","id":"DZA","properties":{"name":"Algeria"},"geometry":{"type":"Polygon","coordinates":[[[11.999506,23.471668],[8.572893,21.565661],[5.677566,19.601207],[4.267419,19.155265],[3.158133,19.057364],[3.146661,19.693579],[2.683588,19.85623],[2.060991,20.142233],[1.823228,20.610809],[-1.550055,22.792666],[-4.923337,24.974574],[-8.6844,27.395744],[-8.665124,27.589479],[-8.66559,27.656426],[-8.674116,28.841289],[-7.059228,29.579228],[-6.060632,29.7317],[-5.242129,30.000443],[-4.859646,30.501188],[-3.690441,30.896952],[-3.647498,31.637294],[-3.06898,31.724498],[-2.616605,32.094346],[-1.307899,32.262889],[-1.124551,32.651522],[-1.388049,32.864015],[-1.733455,33.919713],[-1.792986,34.527919],[-2.169914,35.168396],[-1.208603,35.714849],[-0.127454,35.888662],[0.503877,36.301273],[1.466919,36.605647],[3.161699,36.783905],[4.815758,36.865037],[5.32012,36.716519],[6.26182,37.110655],[7.330385,37.118381],[7.737078,36.885708],[8.420964,36.946427],[8.217824,36.433177],[8.376368,35.479876],[8.140981,34.655146],[7.524482,34.097376],[7.612642,33.344115],[8.430473,32.748337],[8.439103,32.506285],[9.055603,32.102692],[9.48214,30.307556],[9.805634,29.424638],[9.859998,28.95999],[9.683885,28.144174],[9.756128,27.688259],[9.629056,27.140953],[9.716286,26.512206],[9.319411,26.094325],[9.910693,25.365455],[9.948261,24.936954],[10.303847,24.379313],[10.771364,24.562532],[11.560669,24.097909],[11.999506,23.471668]]]}},
    +{"type":"Feature","id":"ECU","properties":{"name":"Ecuador"},"geometry":{"type":"Polygon","coordinates":[[[-80.302561,-3.404856],[-79.770293,-2.657512],[-79.986559,-2.220794],[-80.368784,-2.685159],[-80.967765,-2.246943],[-80.764806,-1.965048],[-80.933659,-1.057455],[-80.58337,-0.906663],[-80.399325,-0.283703],[-80.020898,0.36034],[-80.09061,0.768429],[-79.542762,0.982938],[-78.855259,1.380924],[-77.855061,0.809925],[-77.668613,0.825893],[-77.424984,0.395687],[-76.57638,0.256936],[-76.292314,0.416047],[-75.801466,0.084801],[-75.373223,-0.152032],[-75.233723,-0.911417],[-75.544996,-1.56161],[-76.635394,-2.608678],[-77.837905,-3.003021],[-78.450684,-3.873097],[-78.639897,-4.547784],[-79.205289,-4.959129],[-79.624979,-4.454198],[-80.028908,-4.346091],[-80.442242,-4.425724],[-80.469295,-4.059287],[-80.184015,-3.821162],[-80.302561,-3.404856]]]}},
    +{"type":"Feature","id":"EGY","properties":{"name":"Egypt"},"geometry":{"type":"Polygon","coordinates":[[[34.9226,29.50133],[34.64174,29.09942],[34.42655,28.34399],[34.15451,27.8233],[33.92136,27.6487],[33.58811,27.97136],[33.13676,28.41765],[32.42323,29.85108],[32.32046,29.76043],[32.73482,28.70523],[33.34876,27.69989],[34.10455,26.14227],[34.47387,25.59856],[34.79507,25.03375],[35.69241,23.92671],[35.49372,23.75237],[35.52598,23.10244],[36.69069,22.20485],[36.86623,22],[32.9,22],[29.02,22],[25,22],[25,25.6825],[25,29.238655],[24.70007,30.04419],[24.95762,30.6616],[24.80287,31.08929],[25.16482,31.56915],[26.49533,31.58568],[27.45762,31.32126],[28.45048,31.02577],[28.91353,30.87005],[29.68342,31.18686],[30.09503,31.4734],[30.97693,31.55586],[31.68796,31.4296],[31.96041,30.9336],[32.19247,31.26034],[32.99392,31.02407],[33.7734,30.96746],[34.26544,31.21936],[34.9226,29.50133]]]}},
    +{"type":"Feature","id":"ERI","properties":{"name":"Eritrea"},"geometry":{"type":"Polygon","coordinates":[[[42.35156,12.54223],[42.00975,12.86582],[41.59856,13.45209],[41.155194,13.77332],[40.8966,14.11864],[40.026219,14.519579],[39.34061,14.53155],[39.0994,14.74064],[38.51295,14.50547],[37.90607,14.95943],[37.59377,14.2131],[36.42951,14.42211],[36.323189,14.822481],[36.75386,16.291874],[36.85253,16.95655],[37.16747,17.26314],[37.904,17.42754],[38.41009,17.998307],[38.990623,16.840626],[39.26611,15.922723],[39.814294,15.435647],[41.179275,14.49108],[41.734952,13.921037],[42.276831,13.343992],[42.589576,13.000421],[43.081226,12.699639],[42.779642,12.455416],[42.35156,12.54223]]]}},
    +{"type":"Feature","id":"ESP","properties":{"name":"Spain"},"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.984433,42.592775],[-9.392884,43.026625],[-7.97819,43.748338],[-6.754492,43.567909],[-5.411886,43.57424],[-4.347843,43.403449],[-3.517532,43.455901],[-1.901351,43.422802],[-1.502771,43.034014],[0.338047,42.579546],[0.701591,42.795734],[1.826793,42.343385],[2.985999,42.473015],[3.039484,41.89212],[2.091842,41.226089],[0.810525,41.014732],[0.721331,40.678318],[0.106692,40.123934],[-0.278711,39.309978],[0.111291,38.738514],[-0.467124,38.292366],[-0.683389,37.642354],[-1.438382,37.443064],[-2.146453,36.674144],[-3.415781,36.6589],[-4.368901,36.677839],[-4.995219,36.324708],[-5.37716,35.94685],[-5.866432,36.029817],[-6.236694,36.367677],[-6.520191,36.942913],[-7.453726,37.097788],[-7.537105,37.428904],[-7.166508,37.803894],[-7.029281,38.075764],[-7.374092,38.373059],[-7.098037,39.030073],[-7.498632,39.629571],[-7.066592,39.711892],[-7.026413,40.184524],[-6.86402,40.330872],[-6.851127,41.111083],[-6.389088,41.381815],[-6.668606,41.883387],[-7.251309,41.918346],[-7.422513,41.792075],[-8.013175,41.790886],[-8.263857,42.280469],[-8.671946,42.134689],[-9.034818,41.880571]]]}},
    +{"type":"Feature","id":"EST","properties":{"name":"Estonia"},"geometry":{"type":"Polygon","coordinates":[[[24.312863,57.793424],[24.428928,58.383413],[24.061198,58.257375],[23.42656,58.612753],[23.339795,59.18724],[24.604214,59.465854],[25.864189,59.61109],[26.949136,59.445803],[27.981114,59.475388],[28.131699,59.300825],[27.420166,58.724581],[27.716686,57.791899],[27.288185,57.474528],[26.463532,57.476389],[25.60281,57.847529],[25.164594,57.970157],[24.312863,57.793424]]]}},
    +{"type":"Feature","id":"ETH","properties":{"name":"Ethiopia"},"geometry":{"type":"Polygon","coordinates":[[[37.90607,14.95943],[38.51295,14.50547],[39.0994,14.74064],[39.34061,14.53155],[40.02625,14.51959],[40.8966,14.11864],[41.1552,13.77333],[41.59856,13.45209],[42.00975,12.86582],[42.35156,12.54223],[42,12.1],[41.66176,11.6312],[41.73959,11.35511],[41.75557,11.05091],[42.31414,11.0342],[42.55493,11.10511],[42.776852,10.926879],[42.55876,10.57258],[42.92812,10.02194],[43.29699,9.54048],[43.67875,9.18358],[46.94834,7.99688],[47.78942,8.003],[44.9636,5.00162],[43.66087,4.95755],[42.76967,4.25259],[42.12861,4.23413],[41.855083,3.918912],[41.1718,3.91909],[40.76848,4.25702],[39.85494,3.83879],[39.559384,3.42206],[38.89251,3.50074],[38.67114,3.61607],[38.43697,3.58851],[38.120915,3.598605],[36.855093,4.447864],[36.159079,4.447864],[35.817448,4.776966],[35.817448,5.338232],[35.298007,5.506],[34.70702,6.59422],[34.25032,6.82607],[34.0751,7.22595],[33.56829,7.71334],[32.95418,7.78497],[33.2948,8.35458],[33.8255,8.37916],[33.97498,8.68456],[33.96162,9.58358],[34.25745,10.63009],[34.73115,10.91017],[34.83163,11.31896],[35.26049,12.08286],[35.86363,12.57828],[36.27022,13.56333],[36.42951,14.42211],[37.59377,14.2131],[37.90607,14.95943]]]}},
    +{"type":"Feature","id":"FIN","properties":{"name":"Finland"},"geometry":{"type":"Polygon","coordinates":[[[28.59193,69.064777],[28.445944,68.364613],[29.977426,67.698297],[29.054589,66.944286],[30.21765,65.80598],[29.54443,64.948672],[30.444685,64.204453],[30.035872,63.552814],[31.516092,62.867687],[31.139991,62.357693],[30.211107,61.780028],[28.069998,60.503517],[26.255173,60.423961],[24.496624,60.057316],[22.869695,59.846373],[22.290764,60.391921],[21.322244,60.72017],[21.544866,61.705329],[21.059211,62.607393],[21.536029,63.189735],[22.442744,63.81781],[24.730512,64.902344],[25.398068,65.111427],[25.294043,65.534346],[23.903379,66.006927],[23.56588,66.396051],[23.539473,67.936009],[21.978535,68.616846],[20.645593,69.106247],[21.244936,69.370443],[22.356238,68.841741],[23.66205,68.891247],[24.735679,68.649557],[25.689213,69.092114],[26.179622,69.825299],[27.732292,70.164193],[29.015573,69.766491],[28.59193,69.064777]]]}},
    +{"type":"Feature","id":"FJI","properties":{"name":"Fiji"},"geometry":{"type":"MultiPolygon","coordinates":[[[[178.3736,-17.33992],[178.71806,-17.62846],[178.55271,-18.15059],[177.93266,-18.28799],[177.38146,-18.16432],[177.28504,-17.72465],[177.67087,-17.38114],[178.12557,-17.50481],[178.3736,-17.33992]]],[[[179.364143,-16.801354],[178.725059,-17.012042],[178.596839,-16.63915],[179.096609,-16.433984],[179.413509,-16.379054],[180,-16.067133],[180,-16.555217],[179.364143,-16.801354]]],[[[-179.917369,-16.501783],[-180,-16.555217],[-180,-16.067133],[-179.79332,-16.020882],[-179.917369,-16.501783]]]]}},
    +{"type":"Feature","id":"FLK","properties":{"name":"Falkland Islands"},"geometry":{"type":"Polygon","coordinates":[[[-61.2,-51.85],[-60,-51.25],[-59.15,-51.5],[-58.55,-51.1],[-57.75,-51.55],[-58.05,-51.9],[-59.4,-52.2],[-59.85,-51.85],[-60.7,-52.3],[-61.2,-51.85]]]}},
    +{"type":"Feature","id":"FRA","properties":{"name":"France"},"geometry":{"type":"MultiPolygon","coordinates":[[[[9.560016,42.152492],[9.229752,41.380007],[8.775723,41.583612],[8.544213,42.256517],[8.746009,42.628122],[9.390001,43.009985],[9.560016,42.152492]]],[[[3.588184,50.378992],[4.286023,49.907497],[4.799222,49.985373],[5.674052,49.529484],[5.897759,49.442667],[6.18632,49.463803],[6.65823,49.201958],[8.099279,49.017784],[7.593676,48.333019],[7.466759,47.620582],[7.192202,47.449766],[6.736571,47.541801],[6.768714,47.287708],[6.037389,46.725779],[6.022609,46.27299],[6.5001,46.429673],[6.843593,45.991147],[6.802355,45.70858],[7.096652,45.333099],[6.749955,45.028518],[7.007562,44.254767],[7.549596,44.127901],[7.435185,43.693845],[6.529245,43.128892],[4.556963,43.399651],[3.100411,43.075201],[2.985999,42.473015],[1.826793,42.343385],[0.701591,42.795734],[0.338047,42.579546],[-1.502771,43.034014],[-1.901351,43.422802],[-1.384225,44.02261],[-1.193798,46.014918],[-2.225724,47.064363],[-2.963276,47.570327],[-4.491555,47.954954],[-4.59235,48.68416],[-3.295814,48.901692],[-1.616511,48.644421],[-1.933494,49.776342],[-0.989469,49.347376],[1.338761,50.127173],[1.639001,50.946606],[2.513573,51.148506],[2.658422,50.796848],[3.123252,50.780363],[3.588184,50.378992]]]]}},
    +{"type":"Feature","id":"GAB","properties":{"name":"Gabon"},"geometry":{"type":"Polygon","coordinates":[[[11.093773,-3.978827],[10.066135,-2.969483],[9.405245,-2.144313],[8.797996,-1.111301],[8.830087,-0.779074],[9.04842,-0.459351],[9.291351,0.268666],[9.492889,1.01012],[9.830284,1.067894],[11.285079,1.057662],[11.276449,2.261051],[11.751665,2.326758],[12.35938,2.192812],[12.951334,2.321616],[13.075822,2.267097],[13.003114,1.830896],[13.282631,1.314184],[14.026669,1.395677],[14.276266,1.19693],[13.843321,0.038758],[14.316418,-0.552627],[14.425456,-1.333407],[14.29921,-1.998276],[13.992407,-2.470805],[13.109619,-2.42874],[12.575284,-1.948511],[12.495703,-2.391688],[11.820964,-2.514161],[11.478039,-2.765619],[11.855122,-3.426871],[11.093773,-3.978827]]]}},
    +{"type":"Feature","id":"GBR","properties":{"name":"United Kingdom"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-5.661949,54.554603],[-6.197885,53.867565],[-6.95373,54.073702],[-7.572168,54.059956],[-7.366031,54.595841],[-7.572168,55.131622],[-6.733847,55.17286],[-5.661949,54.554603]]],[[[-3.005005,58.635],[-4.073828,57.553025],[-3.055002,57.690019],[-1.959281,57.6848],[-2.219988,56.870017],[-3.119003,55.973793],[-2.085009,55.909998],[-2.005676,55.804903],[-1.114991,54.624986],[-0.430485,54.464376],[0.184981,53.325014],[0.469977,52.929999],[1.681531,52.73952],[1.559988,52.099998],[1.050562,51.806761],[1.449865,51.289428],[0.550334,50.765739],[-0.787517,50.774989],[-2.489998,50.500019],[-2.956274,50.69688],[-3.617448,50.228356],[-4.542508,50.341837],[-5.245023,49.96],[-5.776567,50.159678],[-4.30999,51.210001],[-3.414851,51.426009],[-3.422719,51.426848],[-4.984367,51.593466],[-5.267296,51.9914],[-4.222347,52.301356],[-4.770013,52.840005],[-4.579999,53.495004],[-3.093831,53.404547],[-3.09208,53.404441],[-2.945009,53.985],[-3.614701,54.600937],[-3.630005,54.615013],[-4.844169,54.790971],[-5.082527,55.061601],[-4.719112,55.508473],[-5.047981,55.783986],[-5.586398,55.311146],[-5.644999,56.275015],[-6.149981,56.78501],[-5.786825,57.818848],[-5.009999,58.630013],[-4.211495,58.550845],[-3.005005,58.635]]]]}},
    +{"type":"Feature","id":"GEO","properties":{"name":"Georgia"},"geometry":{"type":"Polygon","coordinates":[[[41.554084,41.535656],[41.703171,41.962943],[41.45347,42.645123],[40.875469,43.013628],[40.321394,43.128634],[39.955009,43.434998],[40.076965,43.553104],[40.922185,43.382159],[42.394395,43.220308],[43.756017,42.740828],[43.9312,42.554974],[44.537623,42.711993],[45.470279,42.502781],[45.77641,42.092444],[46.404951,41.860675],[46.145432,41.722802],[46.637908,41.181673],[46.501637,41.064445],[45.962601,41.123873],[45.217426,41.411452],[44.97248,41.248129],[43.582746,41.092143],[42.619549,41.583173],[41.554084,41.535656]]]}},
    +{"type":"Feature","id":"GHA","properties":{"name":"Ghana"},"geometry":{"type":"Polygon","coordinates":[[[1.060122,5.928837],[-0.507638,5.343473],[-1.063625,5.000548],[-1.964707,4.710462],[-2.856125,4.994476],[-2.810701,5.389051],[-3.24437,6.250472],[-2.983585,7.379705],[-2.56219,8.219628],[-2.827496,9.642461],[-2.963896,10.395335],[-2.940409,10.96269],[-1.203358,11.009819],[-0.761576,10.93693],[-0.438702,11.098341],[0.023803,11.018682],[-0.049785,10.706918],[0.36758,10.191213],[0.365901,9.465004],[0.461192,8.677223],[0.712029,8.312465],[0.490957,7.411744],[0.570384,6.914359],[0.836931,6.279979],[1.060122,5.928837]]]}},
    +{"type":"Feature","id":"GIN","properties":{"name":"Guinea"},"geometry":{"type":"Polygon","coordinates":[[[-8.439298,7.686043],[-8.722124,7.711674],[-8.926065,7.309037],[-9.208786,7.313921],[-9.403348,7.526905],[-9.33728,7.928534],[-9.755342,8.541055],[-10.016567,8.428504],[-10.230094,8.406206],[-10.505477,8.348896],[-10.494315,8.715541],[-10.65477,8.977178],[-10.622395,9.26791],[-10.839152,9.688246],[-11.117481,10.045873],[-11.917277,10.046984],[-12.150338,9.858572],[-12.425929,9.835834],[-12.596719,9.620188],[-12.711958,9.342712],[-13.24655,8.903049],[-13.685154,9.494744],[-14.074045,9.886167],[-14.330076,10.01572],[-14.579699,10.214467],[-14.693232,10.656301],[-14.839554,10.876572],[-15.130311,11.040412],[-14.685687,11.527824],[-14.382192,11.509272],[-14.121406,11.677117],[-13.9008,11.678719],[-13.743161,11.811269],[-13.828272,12.142644],[-13.718744,12.247186],[-13.700476,12.586183],[-13.217818,12.575874],[-12.499051,12.33209],[-12.278599,12.35444],[-12.203565,12.465648],[-11.658301,12.386583],[-11.513943,12.442988],[-11.456169,12.076834],[-11.297574,12.077971],[-11.036556,12.211245],[-10.87083,12.177887],[-10.593224,11.923975],[-10.165214,11.844084],[-9.890993,12.060479],[-9.567912,12.194243],[-9.327616,12.334286],[-9.127474,12.30806],[-8.905265,12.088358],[-8.786099,11.812561],[-8.376305,11.393646],[-8.581305,11.136246],[-8.620321,10.810891],[-8.407311,10.909257],[-8.282357,10.792597],[-8.335377,10.494812],[-8.029944,10.206535],[-8.229337,10.12902],[-8.309616,9.789532],[-8.079114,9.376224],[-7.8321,8.575704],[-8.203499,8.455453],[-8.299049,8.316444],[-8.221792,8.123329],[-8.280703,7.68718],[-8.439298,7.686043]]]}},
    +{"type":"Feature","id":"GMB","properties":{"name":"Gambia"},"geometry":{"type":"Polygon","coordinates":[[[-16.841525,13.151394],[-16.713729,13.594959],[-15.624596,13.623587],[-15.39877,13.860369],[-15.081735,13.876492],[-14.687031,13.630357],[-14.376714,13.62568],[-14.046992,13.794068],[-13.844963,13.505042],[-14.277702,13.280585],[-14.712197,13.298207],[-15.141163,13.509512],[-15.511813,13.27857],[-15.691001,13.270353],[-15.931296,13.130284],[-16.841525,13.151394]]]}},
    +{"type":"Feature","id":"GNB","properties":{"name":"Guinea Bissau"},"geometry":{"type":"Polygon","coordinates":[[[-15.130311,11.040412],[-15.66418,11.458474],[-16.085214,11.524594],[-16.314787,11.806515],[-16.308947,11.958702],[-16.613838,12.170911],[-16.677452,12.384852],[-16.147717,12.547762],[-15.816574,12.515567],[-15.548477,12.62817],[-13.700476,12.586183],[-13.718744,12.247186],[-13.828272,12.142644],[-13.743161,11.811269],[-13.9008,11.678719],[-14.121406,11.677117],[-14.382192,11.509272],[-14.685687,11.527824],[-15.130311,11.040412]]]}},
    +{"type":"Feature","id":"GNQ","properties":{"name":"Equatorial Guinea"},"geometry":{"type":"Polygon","coordinates":[[[9.492889,1.01012],[9.305613,1.160911],[9.649158,2.283866],[11.276449,2.261051],[11.285079,1.057662],[9.830284,1.067894],[9.492889,1.01012]]]}},
    +{"type":"Feature","id":"GRC","properties":{"name":"Greece"},"geometry":{"type":"MultiPolygon","coordinates":[[[[23.69998,35.705004],[24.246665,35.368022],[25.025015,35.424996],[25.769208,35.354018],[25.745023,35.179998],[26.290003,35.29999],[26.164998,35.004995],[24.724982,34.919988],[24.735007,35.084991],[23.514978,35.279992],[23.69998,35.705004]]],[[[26.604196,41.562115],[26.294602,40.936261],[26.056942,40.824123],[25.447677,40.852545],[24.925848,40.947062],[23.714811,40.687129],[24.407999,40.124993],[23.899968,39.962006],[23.342999,39.960998],[22.813988,40.476005],[22.626299,40.256561],[22.849748,39.659311],[23.350027,39.190011],[22.973099,38.970903],[23.530016,38.510001],[24.025025,38.219993],[24.040011,37.655015],[23.115003,37.920011],[23.409972,37.409991],[22.774972,37.30501],[23.154225,36.422506],[22.490028,36.41],[21.670026,36.844986],[21.295011,37.644989],[21.120034,38.310323],[20.730032,38.769985],[20.217712,39.340235],[20.150016,39.624998],[20.615,40.110007],[20.674997,40.435],[20.99999,40.580004],[21.02004,40.842727],[21.674161,40.931275],[22.055378,41.149866],[22.597308,41.130487],[22.76177,41.3048],[22.952377,41.337994],[23.692074,41.309081],[24.492645,41.583896],[25.197201,41.234486],[26.106138,41.328899],[26.117042,41.826905],[26.604196,41.562115]]]]}},
    +{"type":"Feature","id":"GRL","properties":{"name":"Greenland"},"geometry":{"type":"Polygon","coordinates":[[[-46.76379,82.62796],[-43.40644,83.22516],[-39.89753,83.18018],[-38.62214,83.54905],[-35.08787,83.64513],[-27.10046,83.51966],[-20.84539,82.72669],[-22.69182,82.34165],[-26.51753,82.29765],[-31.9,82.2],[-31.39646,82.02154],[-27.85666,82.13178],[-24.84448,81.78697],[-22.90328,82.09317],[-22.07175,81.73449],[-23.16961,81.15271],[-20.62363,81.52462],[-15.76818,81.91245],[-12.77018,81.71885],[-12.20855,81.29154],[-16.28533,80.58004],[-16.85,80.35],[-20.04624,80.17708],[-17.73035,80.12912],[-18.9,79.4],[-19.70499,78.75128],[-19.67353,77.63859],[-18.47285,76.98565],[-20.03503,76.94434],[-21.67944,76.62795],[-19.83407,76.09808],[-19.59896,75.24838],[-20.66818,75.15585],[-19.37281,74.29561],[-21.59422,74.22382],[-20.43454,73.81713],[-20.76234,73.46436],[-22.17221,73.30955],[-23.56593,73.30663],[-22.31311,72.62928],[-22.29954,72.18409],[-24.27834,72.59788],[-24.79296,72.3302],[-23.44296,72.08016],[-22.13281,71.46898],[-21.75356,70.66369],[-23.53603,70.471],[-24.30702,70.85649],[-25.54341,71.43094],[-25.20135,70.75226],[-26.36276,70.22646],[-23.72742,70.18401],[-22.34902,70.12946],[-25.02927,69.2588],[-27.74737,68.47046],[-30.67371,68.12503],[-31.77665,68.12078],[-32.81105,67.73547],[-34.20196,66.67974],[-36.35284,65.9789],[-37.04378,65.93768],[-38.37505,65.69213],[-39.81222,65.45848],[-40.66899,64.83997],[-40.68281,64.13902],[-41.1887,63.48246],[-42.81938,62.68233],[-42.41666,61.90093],[-42.86619,61.07404],[-43.3784,60.09772],[-44.7875,60.03676],[-46.26364,60.85328],[-48.26294,60.85843],[-49.23308,61.40681],[-49.90039,62.38336],[-51.63325,63.62691],[-52.14014,64.27842],[-52.27659,65.1767],[-53.66166,66.09957],[-53.30161,66.8365],[-53.96911,67.18899],[-52.9804,68.35759],[-51.47536,68.72958],[-51.08041,69.14781],[-50.87122,69.9291],[-52.013585,69.574925],[-52.55792,69.42616],[-53.45629,69.283625],[-54.68336,69.61003],[-54.75001,70.28932],[-54.35884,70.821315],[-53.431315,70.835755],[-51.39014,70.56978],[-53.10937,71.20485],[-54.00422,71.54719],[-55,71.406537],[-55.83468,71.65444],[-54.71819,72.58625],[-55.32634,72.95861],[-56.12003,73.64977],[-57.32363,74.71026],[-58.59679,75.09861],[-58.58516,75.51727],[-61.26861,76.10238],[-63.39165,76.1752],[-66.06427,76.13486],[-68.50438,76.06141],[-69.66485,76.37975],[-71.40257,77.00857],[-68.77671,77.32312],[-66.76397,77.37595],[-71.04293,77.63595],[-73.297,78.04419],[-73.15938,78.43271],[-69.37345,78.91388],[-65.7107,79.39436],[-65.3239,79.75814],[-68.02298,80.11721],[-67.15129,80.51582],[-63.68925,81.21396],[-62.23444,81.3211],[-62.65116,81.77042],[-60.28249,82.03363],[-57.20744,82.19074],[-54.13442,82.19962],[-53.04328,81.88833],[-50.39061,82.43883],[-48.00386,82.06481],[-46.59984,81.985945],[-44.523,81.6607],[-46.9007,82.19979],[-46.76379,82.62796]]]}},
    +{"type":"Feature","id":"GTM","properties":{"name":"Guatemala"},"geometry":{"type":"Polygon","coordinates":[[[-90.095555,13.735338],[-90.608624,13.909771],[-91.23241,13.927832],[-91.689747,14.126218],[-92.22775,14.538829],[-92.20323,14.830103],[-92.087216,15.064585],[-92.229249,15.251447],[-91.74796,16.066565],[-90.464473,16.069562],[-90.438867,16.41011],[-90.600847,16.470778],[-90.711822,16.687483],[-91.08167,16.918477],[-91.453921,17.252177],[-91.002269,17.254658],[-91.00152,17.817595],[-90.067934,17.819326],[-89.14308,17.808319],[-89.150806,17.015577],[-89.229122,15.886938],[-88.930613,15.887273],[-88.604586,15.70638],[-88.518364,15.855389],[-88.225023,15.727722],[-88.68068,15.346247],[-89.154811,15.066419],[-89.22522,14.874286],[-89.145535,14.678019],[-89.353326,14.424133],[-89.587343,14.362586],[-89.534219,14.244816],[-89.721934,14.134228],[-90.064678,13.88197],[-90.095555,13.735338]]]}},
    +{"type":"Feature","id":"GUF","properties":{"name":"French Guiana"},"geometry":{"type":"Polygon","coordinates":[[[-52.556425,2.504705],[-52.939657,2.124858],[-53.418465,2.053389],[-53.554839,2.334897],[-53.778521,2.376703],[-54.088063,2.105557],[-54.524754,2.311849],[-54.27123,2.738748],[-54.184284,3.194172],[-54.011504,3.62257],[-54.399542,4.212611],[-54.478633,4.896756],[-53.958045,5.756548],[-53.618453,5.646529],[-52.882141,5.409851],[-51.823343,4.565768],[-51.657797,4.156232],[-52.249338,3.241094],[-52.556425,2.504705]]]}},
    +{"type":"Feature","id":"GUY","properties":{"name":"Guyana"},"geometry":{"type":"Polygon","coordinates":[[[-59.758285,8.367035],[-59.101684,7.999202],[-58.482962,7.347691],[-58.454876,6.832787],[-58.078103,6.809094],[-57.542219,6.321268],[-57.147436,5.97315],[-57.307246,5.073567],[-57.914289,4.812626],[-57.86021,4.576801],[-58.044694,4.060864],[-57.601569,3.334655],[-57.281433,3.333492],[-57.150098,2.768927],[-56.539386,1.899523],[-56.782704,1.863711],[-57.335823,1.948538],[-57.660971,1.682585],[-58.11345,1.507195],[-58.429477,1.463942],[-58.540013,1.268088],[-59.030862,1.317698],[-59.646044,1.786894],[-59.718546,2.24963],[-59.974525,2.755233],[-59.815413,3.606499],[-59.53804,3.958803],[-59.767406,4.423503],[-60.111002,4.574967],[-59.980959,5.014061],[-60.213683,5.244486],[-60.733574,5.200277],[-61.410303,5.959068],[-61.139415,6.234297],[-61.159336,6.696077],[-60.543999,6.856584],[-60.295668,7.043911],[-60.637973,7.415],[-60.550588,7.779603],[-59.758285,8.367035]]]}},
    +{"type":"Feature","id":"HND","properties":{"name":"Honduras"},"geometry":{"type":"Polygon","coordinates":[[[-87.316654,12.984686],[-87.489409,13.297535],[-87.793111,13.38448],[-87.723503,13.78505],[-87.859515,13.893312],[-88.065343,13.964626],[-88.503998,13.845486],[-88.541231,13.980155],[-88.843073,14.140507],[-89.058512,14.340029],[-89.353326,14.424133],[-89.145535,14.678019],[-89.22522,14.874286],[-89.154811,15.066419],[-88.68068,15.346247],[-88.225023,15.727722],[-88.121153,15.688655],[-87.901813,15.864458],[-87.61568,15.878799],[-87.522921,15.797279],[-87.367762,15.84694],[-86.903191,15.756713],[-86.440946,15.782835],[-86.119234,15.893449],[-86.001954,16.005406],[-85.683317,15.953652],[-85.444004,15.885749],[-85.182444,15.909158],[-84.983722,15.995923],[-84.52698,15.857224],[-84.368256,15.835158],[-84.063055,15.648244],[-83.773977,15.424072],[-83.410381,15.270903],[-83.147219,14.995829],[-83.489989,15.016267],[-83.628585,14.880074],[-83.975721,14.749436],[-84.228342,14.748764],[-84.449336,14.621614],[-84.649582,14.666805],[-84.820037,14.819587],[-84.924501,14.790493],[-85.052787,14.551541],[-85.148751,14.560197],[-85.165365,14.35437],[-85.514413,14.079012],[-85.698665,13.960078],[-85.801295,13.836055],[-86.096264,14.038187],[-86.312142,13.771356],[-86.520708,13.778487],[-86.755087,13.754845],[-86.733822,13.263093],[-86.880557,13.254204],[-87.005769,13.025794],[-87.316654,12.984686]]]}},
    +{"type":"Feature","id":"HRV","properties":{"name":"Croatia"},"geometry":{"type":"Polygon","coordinates":[[[18.829838,45.908878],[19.072769,45.521511],[19.390476,45.236516],[19.005486,44.860234],[18.553214,45.08159],[17.861783,45.06774],[17.002146,45.233777],[16.534939,45.211608],[16.318157,45.004127],[15.959367,45.233777],[15.750026,44.818712],[16.23966,44.351143],[16.456443,44.04124],[16.916156,43.667722],[17.297373,43.446341],[17.674922,43.028563],[18.56,42.65],[18.450016,42.479991],[17.50997,42.849995],[16.930006,43.209998],[16.015385,43.507215],[15.174454,44.243191],[15.37625,44.317915],[14.920309,44.738484],[14.901602,45.07606],[14.258748,45.233777],[13.952255,44.802124],[13.656976,45.136935],[13.679403,45.484149],[13.71506,45.500324],[14.411968,45.466166],[14.595109,45.634941],[14.935244,45.471695],[15.327675,45.452316],[15.323954,45.731783],[15.67153,45.834154],[15.768733,46.238108],[16.564808,46.503751],[16.882515,46.380632],[17.630066,45.951769],[18.456062,45.759481],[18.829838,45.908878]]]}},
    +{"type":"Feature","id":"HTI","properties":{"name":"Haiti"},"geometry":{"type":"Polygon","coordinates":[[[-73.189791,19.915684],[-72.579673,19.871501],[-71.712361,19.714456],[-71.624873,19.169838],[-71.701303,18.785417],[-71.945112,18.6169],[-71.687738,18.31666],[-71.708305,18.044997],[-72.372476,18.214961],[-72.844411,18.145611],[-73.454555,18.217906],[-73.922433,18.030993],[-74.458034,18.34255],[-74.369925,18.664908],[-73.449542,18.526053],[-72.694937,18.445799],[-72.334882,18.668422],[-72.79165,19.101625],[-72.784105,19.483591],[-73.415022,19.639551],[-73.189791,19.915684]]]}},
    +{"type":"Feature","id":"HUN","properties":{"name":"Hungary"},"geometry":{"type":"Polygon","coordinates":[[[16.202298,46.852386],[16.534268,47.496171],[16.340584,47.712902],[16.903754,47.714866],[16.979667,48.123497],[17.488473,47.867466],[17.857133,47.758429],[18.696513,47.880954],[18.777025,48.081768],[19.174365,48.111379],[19.661364,48.266615],[19.769471,48.202691],[20.239054,48.327567],[20.473562,48.56285],[20.801294,48.623854],[21.872236,48.319971],[22.085608,48.422264],[22.64082,48.15024],[22.710531,47.882194],[22.099768,47.672439],[21.626515,46.994238],[21.021952,46.316088],[20.220192,46.127469],[19.596045,46.17173],[18.829838,45.908878],[18.456062,45.759481],[17.630066,45.951769],[16.882515,46.380632],[16.564808,46.503751],[16.370505,46.841327],[16.202298,46.852386]]]}},
    +{"type":"Feature","id":"IDN","properties":{"name":"Indonesia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[120.715609,-10.239581],[120.295014,-10.25865],[118.967808,-9.557969],[119.90031,-9.36134],[120.425756,-9.665921],[120.775502,-9.969675],[120.715609,-10.239581]]],[[[124.43595,-10.140001],[123.579982,-10.359987],[123.459989,-10.239995],[123.550009,-9.900016],[123.980009,-9.290027],[124.968682,-8.89279],[125.07002,-9.089987],[125.08852,-9.393173],[124.43595,-10.140001]]],[[[117.900018,-8.095681],[118.260616,-8.362383],[118.87846,-8.280683],[119.126507,-8.705825],[117.970402,-8.906639],[117.277731,-9.040895],[116.740141,-9.032937],[117.083737,-8.457158],[117.632024,-8.449303],[117.900018,-8.095681]]],[[[122.903537,-8.094234],[122.756983,-8.649808],[121.254491,-8.933666],[119.924391,-8.810418],[119.920929,-8.444859],[120.715092,-8.236965],[121.341669,-8.53674],[122.007365,-8.46062],[122.903537,-8.094234]]],[[[108.623479,-6.777674],[110.539227,-6.877358],[110.759576,-6.465186],[112.614811,-6.946036],[112.978768,-7.594213],[114.478935,-7.776528],[115.705527,-8.370807],[114.564511,-8.751817],[113.464734,-8.348947],[112.559672,-8.376181],[111.522061,-8.302129],[110.58615,-8.122605],[109.427667,-7.740664],[108.693655,-7.6416],[108.277763,-7.766657],[106.454102,-7.3549],[106.280624,-6.9249],[105.365486,-6.851416],[106.051646,-5.895919],[107.265009,-5.954985],[108.072091,-6.345762],[108.486846,-6.421985],[108.623479,-6.777674]]],[[[134.724624,-6.214401],[134.210134,-6.895238],[134.112776,-6.142467],[134.290336,-5.783058],[134.499625,-5.445042],[134.727002,-5.737582],[134.724624,-6.214401]]],[[[127.249215,-3.459065],[126.874923,-3.790983],[126.183802,-3.607376],[125.989034,-3.177273],[127.000651,-3.129318],[127.249215,-3.459065]]],[[[130.471344,-3.093764],[130.834836,-3.858472],[129.990547,-3.446301],[129.155249,-3.362637],[128.590684,-3.428679],[127.898891,-3.393436],[128.135879,-2.84365],[129.370998,-2.802154],[130.471344,-3.093764]]],[[[134.143368,-1.151867],[134.422627,-2.769185],[135.457603,-3.367753],[136.293314,-2.307042],[137.440738,-1.703513],[138.329727,-1.702686],[139.184921,-2.051296],[139.926684,-2.409052],[141.00021,-2.600151],[141.017057,-5.859022],[141.033852,-9.117893],[140.143415,-8.297168],[139.127767,-8.096043],[138.881477,-8.380935],[137.614474,-8.411683],[138.039099,-7.597882],[138.668621,-7.320225],[138.407914,-6.232849],[137.92784,-5.393366],[135.98925,-4.546544],[135.164598,-4.462931],[133.66288,-3.538853],[133.367705,-4.024819],[132.983956,-4.112979],[132.756941,-3.746283],[132.753789,-3.311787],[131.989804,-2.820551],[133.066845,-2.460418],[133.780031,-2.479848],[133.696212,-2.214542],[132.232373,-2.212526],[131.836222,-1.617162],[130.94284,-1.432522],[130.519558,-0.93772],[131.867538,-0.695461],[132.380116,-0.369538],[133.985548,-0.78021],[134.143368,-1.151867]]],[[[125.240501,1.419836],[124.437035,0.427881],[123.685505,0.235593],[122.723083,0.431137],[121.056725,0.381217],[120.183083,0.237247],[120.04087,-0.519658],[120.935905,-1.408906],[121.475821,-0.955962],[123.340565,-0.615673],[123.258399,-1.076213],[122.822715,-0.930951],[122.38853,-1.516858],[121.508274,-1.904483],[122.454572,-3.186058],[122.271896,-3.5295],[123.170963,-4.683693],[123.162333,-5.340604],[122.628515,-5.634591],[122.236394,-5.282933],[122.719569,-4.464172],[121.738234,-4.851331],[121.489463,-4.574553],[121.619171,-4.188478],[120.898182,-3.602105],[120.972389,-2.627643],[120.305453,-2.931604],[120.390047,-4.097579],[120.430717,-5.528241],[119.796543,-5.6734],[119.366906,-5.379878],[119.653606,-4.459417],[119.498835,-3.494412],[119.078344,-3.487022],[118.767769,-2.801999],[119.180974,-2.147104],[119.323394,-1.353147],[119.825999,0.154254],[120.035702,0.566477],[120.885779,1.309223],[121.666817,1.013944],[122.927567,0.875192],[124.077522,0.917102],[125.065989,1.643259],[125.240501,1.419836]]],[[[128.688249,1.132386],[128.635952,0.258486],[128.12017,0.356413],[127.968034,-0.252077],[128.379999,-0.780004],[128.100016,-0.899996],[127.696475,-0.266598],[127.39949,1.011722],[127.600512,1.810691],[127.932378,2.174596],[128.004156,1.628531],[128.594559,1.540811],[128.688249,1.132386]]],[[[117.875627,1.827641],[118.996747,0.902219],[117.811858,0.784242],[117.478339,0.102475],[117.521644,-0.803723],[116.560048,-1.487661],[116.533797,-2.483517],[116.148084,-4.012726],[116.000858,-3.657037],[114.864803,-4.106984],[114.468652,-3.495704],[113.755672,-3.43917],[113.256994,-3.118776],[112.068126,-3.478392],[111.703291,-2.994442],[111.04824,-3.049426],[110.223846,-2.934032],[110.070936,-1.592874],[109.571948,-1.314907],[109.091874,-0.459507],[108.952658,0.415375],[109.069136,1.341934],[109.66326,2.006467],[109.830227,1.338136],[110.514061,0.773131],[111.159138,0.976478],[111.797548,0.904441],[112.380252,1.410121],[112.859809,1.49779],[113.80585,1.217549],[114.621355,1.430688],[115.134037,2.821482],[115.519078,3.169238],[115.865517,4.306559],[117.015214,4.306094],[117.882035,4.137551],[117.313232,3.234428],[118.04833,2.28769],[117.875627,1.827641]]],[[[105.817655,-5.852356],[104.710384,-5.873285],[103.868213,-5.037315],[102.584261,-4.220259],[102.156173,-3.614146],[101.399113,-2.799777],[100.902503,-2.050262],[100.141981,-0.650348],[99.26374,0.183142],[98.970011,1.042882],[98.601351,1.823507],[97.699598,2.453184],[97.176942,3.308791],[96.424017,3.86886],[95.380876,4.970782],[95.293026,5.479821],[95.936863,5.439513],[97.484882,5.246321],[98.369169,4.26837],[99.142559,3.59035],[99.693998,3.174329],[100.641434,2.099381],[101.658012,2.083697],[102.498271,1.3987],[103.07684,0.561361],[103.838396,0.104542],[103.437645,-0.711946],[104.010789,-1.059212],[104.369991,-1.084843],[104.53949,-1.782372],[104.887893,-2.340425],[105.622111,-2.428844],[106.108593,-3.061777],[105.857446,-4.305525],[105.817655,-5.852356]]]]}},
    +{"type":"Feature","id":"IND","properties":{"name":"India"},"geometry":{"type":"Polygon","coordinates":[[[77.837451,35.49401],[78.912269,34.321936],[78.811086,33.506198],[79.208892,32.994395],[79.176129,32.48378],[78.458446,32.618164],[78.738894,31.515906],[79.721367,30.882715],[81.111256,30.183481],[80.476721,29.729865],[80.088425,28.79447],[81.057203,28.416095],[81.999987,27.925479],[83.304249,27.364506],[84.675018,27.234901],[85.251779,26.726198],[86.024393,26.630985],[87.227472,26.397898],[88.060238,26.414615],[88.174804,26.810405],[88.043133,27.445819],[88.120441,27.876542],[88.730326,28.086865],[88.814248,27.299316],[88.835643,27.098966],[89.744528,26.719403],[90.373275,26.875724],[91.217513,26.808648],[92.033484,26.83831],[92.103712,27.452614],[91.696657,27.771742],[92.503119,27.896876],[93.413348,28.640629],[94.56599,29.277438],[95.404802,29.031717],[96.117679,29.452802],[96.586591,28.83098],[96.248833,28.411031],[97.327114,28.261583],[97.402561,27.882536],[97.051989,27.699059],[97.133999,27.083774],[96.419366,27.264589],[95.124768,26.573572],[95.155153,26.001307],[94.603249,25.162495],[94.552658,24.675238],[94.106742,23.850741],[93.325188,24.078556],[93.286327,23.043658],[93.060294,22.703111],[93.166128,22.27846],[92.672721,22.041239],[92.146035,23.627499],[91.869928,23.624346],[91.706475,22.985264],[91.158963,23.503527],[91.46773,24.072639],[91.915093,24.130414],[92.376202,24.976693],[91.799596,25.147432],[90.872211,25.132601],[89.920693,25.26975],[89.832481,25.965082],[89.355094,26.014407],[88.563049,26.446526],[88.209789,25.768066],[88.931554,25.238692],[88.306373,24.866079],[88.084422,24.501657],[88.69994,24.233715],[88.52977,23.631142],[88.876312,22.879146],[89.031961,22.055708],[88.888766,21.690588],[88.208497,21.703172],[86.975704,21.495562],[87.033169,20.743308],[86.499351,20.151638],[85.060266,19.478579],[83.941006,18.30201],[83.189217,17.671221],[82.192792,17.016636],[82.191242,16.556664],[81.692719,16.310219],[80.791999,15.951972],[80.324896,15.899185],[80.025069,15.136415],[80.233274,13.835771],[80.286294,13.006261],[79.862547,12.056215],[79.857999,10.357275],[79.340512,10.308854],[78.885345,9.546136],[79.18972,9.216544],[78.277941,8.933047],[77.941165,8.252959],[77.539898,7.965535],[76.592979,8.899276],[76.130061,10.29963],[75.746467,11.308251],[75.396101,11.781245],[74.864816,12.741936],[74.616717,13.992583],[74.443859,14.617222],[73.534199,15.990652],[73.119909,17.92857],[72.820909,19.208234],[72.824475,20.419503],[72.630533,21.356009],[71.175273,20.757441],[70.470459,20.877331],[69.16413,22.089298],[69.644928,22.450775],[69.349597,22.84318],[68.176645,23.691965],[68.842599,24.359134],[71.04324,24.356524],[70.844699,25.215102],[70.282873,25.722229],[70.168927,26.491872],[69.514393,26.940966],[70.616496,27.989196],[71.777666,27.91318],[72.823752,28.961592],[73.450638,29.976413],[74.42138,30.979815],[74.405929,31.692639],[75.258642,32.271105],[74.451559,32.7649],[74.104294,33.441473],[73.749948,34.317699],[74.240203,34.748887],[75.757061,34.504923],[76.871722,34.653544],[77.837451,35.49401]]]}},
    +{"type":"Feature","id":"IRL","properties":{"name":"Ireland"},"geometry":{"type":"Polygon","coordinates":[[[-6.197885,53.867565],[-6.032985,53.153164],[-6.788857,52.260118],[-8.561617,51.669301],[-9.977086,51.820455],[-9.166283,52.864629],[-9.688525,53.881363],[-8.327987,54.664519],[-7.572168,55.131622],[-7.366031,54.595841],[-7.572168,54.059956],[-6.95373,54.073702],[-6.197885,53.867565]]]}},
    +{"type":"Feature","id":"IRN","properties":{"name":"Iran"},"geometry":{"type":"Polygon","coordinates":[[[53.921598,37.198918],[54.800304,37.392421],[55.511578,37.964117],[56.180375,37.935127],[56.619366,38.121394],[57.330434,38.029229],[58.436154,37.522309],[59.234762,37.412988],[60.377638,36.527383],[61.123071,36.491597],[61.210817,35.650072],[60.803193,34.404102],[60.52843,33.676446],[60.9637,33.528832],[60.536078,32.981269],[60.863655,32.18292],[60.941945,31.548075],[61.699314,31.379506],[61.781222,30.73585],[60.874248,29.829239],[61.369309,29.303276],[61.771868,28.699334],[62.72783,28.259645],[62.755426,27.378923],[63.233898,27.217047],[63.316632,26.756532],[61.874187,26.239975],[61.497363,25.078237],[59.616134,25.380157],[58.525761,25.609962],[57.397251,25.739902],[56.970766,26.966106],[56.492139,27.143305],[55.72371,26.964633],[54.71509,26.480658],[53.493097,26.812369],[52.483598,27.580849],[51.520763,27.86569],[50.852948,28.814521],[50.115009,30.147773],[49.57685,29.985715],[48.941333,30.31709],[48.567971,29.926778],[48.014568,30.452457],[48.004698,30.985137],[47.685286,30.984853],[47.849204,31.709176],[47.334661,32.469155],[46.109362,33.017287],[45.416691,33.967798],[45.64846,34.748138],[46.151788,35.093259],[46.07634,35.677383],[45.420618,35.977546],[44.77267,37.17045],[44.225756,37.971584],[44.421403,38.281281],[44.109225,39.428136],[44.79399,39.713003],[44.952688,39.335765],[45.457722,38.874139],[46.143623,38.741201],[46.50572,38.770605],[47.685079,39.508364],[48.060095,39.582235],[48.355529,39.288765],[48.010744,38.794015],[48.634375,38.270378],[48.883249,38.320245],[49.199612,37.582874],[50.147771,37.374567],[50.842354,36.872814],[52.264025,36.700422],[53.82579,36.965031],[53.921598,37.198918]]]}},
    +{"type":"Feature","id":"IRQ","properties":{"name":"Iraq"},"geometry":{"type":"Polygon","coordinates":[[[45.420618,35.977546],[46.07634,35.677383],[46.151788,35.093259],[45.64846,34.748138],[45.416691,33.967798],[46.109362,33.017287],[47.334661,32.469155],[47.849204,31.709176],[47.685286,30.984853],[48.004698,30.985137],[48.014568,30.452457],[48.567971,29.926778],[47.974519,29.975819],[47.302622,30.05907],[46.568713,29.099025],[44.709499,29.178891],[41.889981,31.190009],[40.399994,31.889992],[39.195468,32.161009],[38.792341,33.378686],[41.006159,34.419372],[41.383965,35.628317],[41.289707,36.358815],[41.837064,36.605854],[42.349591,37.229873],[42.779126,37.385264],[43.942259,37.256228],[44.293452,37.001514],[44.772699,37.170445],[45.420618,35.977546]]]}},
    +{"type":"Feature","id":"ISL","properties":{"name":"Iceland"},"geometry":{"type":"Polygon","coordinates":[[[-14.508695,66.455892],[-14.739637,65.808748],[-13.609732,65.126671],[-14.909834,64.364082],[-17.794438,63.678749],[-18.656246,63.496383],[-19.972755,63.643635],[-22.762972,63.960179],[-21.778484,64.402116],[-23.955044,64.89113],[-22.184403,65.084968],[-22.227423,65.378594],[-24.326184,65.611189],[-23.650515,66.262519],[-22.134922,66.410469],[-20.576284,65.732112],[-19.056842,66.276601],[-17.798624,65.993853],[-16.167819,66.526792],[-14.508695,66.455892]]]}},
    +{"type":"Feature","id":"ISR","properties":{"name":"Israel"},"geometry":{"type":"Polygon","coordinates":[[[35.719918,32.709192],[35.545665,32.393992],[35.18393,32.532511],[34.974641,31.866582],[35.225892,31.754341],[34.970507,31.616778],[34.927408,31.353435],[35.397561,31.489086],[35.420918,31.100066],[34.922603,29.501326],[34.265433,31.219361],[34.556372,31.548824],[34.488107,31.605539],[34.752587,32.072926],[34.955417,32.827376],[35.098457,33.080539],[35.126053,33.0909],[35.460709,33.08904],[35.552797,33.264275],[35.821101,33.277426],[35.836397,32.868123],[35.700798,32.716014],[35.719918,32.709192]]]}},
    +{"type":"Feature","id":"ITA","properties":{"name":"Italy"},"geometry":{"type":"MultiPolygon","coordinates":[[[[15.520376,38.231155],[15.160243,37.444046],[15.309898,37.134219],[15.099988,36.619987],[14.335229,36.996631],[13.826733,37.104531],[12.431004,37.61295],[12.570944,38.126381],[13.741156,38.034966],[14.761249,38.143874],[15.520376,38.231155]]],[[[9.210012,41.209991],[9.809975,40.500009],[9.669519,39.177376],[9.214818,39.240473],[8.806936,38.906618],[8.428302,39.171847],[8.388253,40.378311],[8.159998,40.950007],[8.709991,40.899984],[9.210012,41.209991]]],[[[12.376485,46.767559],[13.806475,46.509306],[13.69811,46.016778],[13.93763,45.591016],[13.141606,45.736692],[12.328581,45.381778],[12.383875,44.885374],[12.261453,44.600482],[12.589237,44.091366],[13.526906,43.587727],[14.029821,42.761008],[15.14257,41.95514],[15.926191,41.961315],[16.169897,41.740295],[15.889346,41.541082],[16.785002,41.179606],[17.519169,40.877143],[18.376687,40.355625],[18.480247,40.168866],[18.293385,39.810774],[17.73838,40.277671],[16.869596,40.442235],[16.448743,39.795401],[17.17149,39.4247],[17.052841,38.902871],[16.635088,38.843572],[16.100961,37.985899],[15.684087,37.908849],[15.687963,38.214593],[15.891981,38.750942],[16.109332,38.964547],[15.718814,39.544072],[15.413613,40.048357],[14.998496,40.172949],[14.703268,40.60455],[14.060672,40.786348],[13.627985,41.188287],[12.888082,41.25309],[12.106683,41.704535],[11.191906,42.355425],[10.511948,42.931463],[10.200029,43.920007],[9.702488,44.036279],[8.888946,44.366336],[8.428561,44.231228],[7.850767,43.767148],[7.435185,43.693845],[7.549596,44.127901],[7.007562,44.254767],[6.749955,45.028518],[7.096652,45.333099],[6.802355,45.70858],[6.843593,45.991147],[7.273851,45.776948],[7.755992,45.82449],[8.31663,46.163642],[8.489952,46.005151],[8.966306,46.036932],[9.182882,46.440215],[9.922837,46.314899],[10.363378,46.483571],[10.442701,46.893546],[11.048556,46.751359],[11.164828,46.941579],[12.153088,47.115393],[12.376485,46.767559]]]]}},
    +{"type":"Feature","id":"JAM","properties":{"name":"Jamaica"},"geometry":{"type":"Polygon","coordinates":[[[-77.569601,18.490525],[-76.896619,18.400867],[-76.365359,18.160701],[-76.199659,17.886867],[-76.902561,17.868238],[-77.206341,17.701116],[-77.766023,17.861597],[-78.337719,18.225968],[-78.217727,18.454533],[-77.797365,18.524218],[-77.569601,18.490525]]]}},
    +{"type":"Feature","id":"JOR","properties":{"name":"Jordan"},"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.719918,32.709192],[36.834062,32.312938],[38.792341,33.378686],[39.195468,32.161009],[39.004886,32.010217],[37.002166,31.508413],[37.998849,30.5085],[37.66812,30.338665],[37.503582,30.003776],[36.740528,29.865283],[36.501214,29.505254],[36.068941,29.197495],[34.956037,29.356555],[34.922603,29.501326],[35.420918,31.100066],[35.397561,31.489086],[35.545252,31.782505],[35.545665,32.393992]]]}},
    +{"type":"Feature","id":"JPN","properties":{"name":"Japan"},"geometry":{"type":"MultiPolygon","coordinates":[[[[134.638428,34.149234],[134.766379,33.806335],[134.203416,33.201178],[133.79295,33.521985],[133.280268,33.28957],[133.014858,32.704567],[132.363115,32.989382],[132.371176,33.463642],[132.924373,34.060299],[133.492968,33.944621],[133.904106,34.364931],[134.638428,34.149234]]],[[[140.976388,37.142074],[140.59977,36.343983],[140.774074,35.842877],[140.253279,35.138114],[138.975528,34.6676],[137.217599,34.606286],[135.792983,33.464805],[135.120983,33.849071],[135.079435,34.596545],[133.340316,34.375938],[132.156771,33.904933],[130.986145,33.885761],[132.000036,33.149992],[131.33279,31.450355],[130.686318,31.029579],[130.20242,31.418238],[130.447676,32.319475],[129.814692,32.61031],[129.408463,33.296056],[130.353935,33.604151],[130.878451,34.232743],[131.884229,34.749714],[132.617673,35.433393],[134.608301,35.731618],[135.677538,35.527134],[136.723831,37.304984],[137.390612,36.827391],[138.857602,37.827485],[139.426405,38.215962],[140.05479,39.438807],[139.883379,40.563312],[140.305783,41.195005],[141.368973,41.37856],[141.914263,39.991616],[141.884601,39.180865],[140.959489,38.174001],[140.976388,37.142074]]],[[[143.910162,44.1741],[144.613427,43.960883],[145.320825,44.384733],[145.543137,43.262088],[144.059662,42.988358],[143.18385,41.995215],[141.611491,42.678791],[141.067286,41.584594],[139.955106,41.569556],[139.817544,42.563759],[140.312087,43.333273],[141.380549,43.388825],[141.671952,44.772125],[141.967645,45.551483],[143.14287,44.510358],[143.910162,44.1741]]]]}},
    +{"type":"Feature","id":"KAZ","properties":{"name":"Kazakhstan"},"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[70.388965,42.081308],[69.070027,41.384244],[68.632483,40.668681],[68.259896,40.662325],[67.985856,41.135991],[66.714047,41.168444],[66.510649,41.987644],[66.023392,41.994646],[66.098012,42.99766],[64.900824,43.728081],[63.185787,43.650075],[62.0133,43.504477],[61.05832,44.405817],[60.239972,44.784037],[58.689989,45.500014],[58.503127,45.586804],[55.928917,44.995858],[55.968191,41.308642],[55.455251,41.259859],[54.755345,42.043971],[54.079418,42.324109],[52.944293,42.116034],[52.50246,41.783316],[52.446339,42.027151],[52.692112,42.443895],[52.501426,42.792298],[51.342427,43.132975],[50.891292,44.031034],[50.339129,44.284016],[50.305643,44.609836],[51.278503,44.514854],[51.316899,45.245998],[52.16739,45.408391],[53.040876,45.259047],[53.220866,46.234646],[53.042737,46.853006],[52.042023,46.804637],[51.191945,47.048705],[50.034083,46.60899],[49.10116,46.39933],[48.593241,46.561034],[48.694734,47.075628],[48.057253,47.743753],[47.315231,47.715847],[46.466446,48.394152],[47.043672,49.152039],[46.751596,49.356006],[47.54948,50.454698],[48.577841,49.87476],[48.702382,50.605128],[50.766648,51.692762],[52.328724,51.718652],[54.532878,51.02624],[55.716941,50.621717],[56.777961,51.043551],[58.363291,51.063653],[59.642282,50.545442],[59.932807,50.842194],[61.337424,50.79907],[61.588003,51.272659],[59.967534,51.96042],[60.927269,52.447548],[60.739993,52.719986],[61.699986,52.979996],[60.978066,53.664993],[61.436591,54.006265],[65.178534,54.354228],[65.666876,54.601267],[68.1691,54.970392],[69.068167,55.38525],[70.865267,55.169734],[71.180131,54.133285],[72.22415,54.376655],[73.508516,54.035617],[73.425679,53.48981],[74.384845,53.546861],[76.8911,54.490524],[76.525179,54.177003],[77.800916,53.404415],[80.03556,50.864751],[80.568447,51.388336],[81.945986,50.812196],[83.383004,51.069183],[83.935115,50.889246],[84.416377,50.3114],[85.11556,50.117303],[85.54127,49.692859],[86.829357,49.826675],[87.35997,49.214981],[86.598776,48.549182],[85.768233,48.455751],[85.720484,47.452969],[85.16429,47.000956],[83.180484,47.330031],[82.458926,45.53965],[81.947071,45.317027],[79.966106,44.917517],[80.866206,43.180362],[80.18015,42.920068],[80.25999,42.349999],[79.643645,42.496683],[79.142177,42.856092],[77.658392,42.960686],[76.000354,42.988022],[75.636965,42.8779],[74.212866,43.298339],[73.645304,43.091272],[73.489758,42.500894],[71.844638,42.845395],[71.186281,42.704293],[70.962315,42.266154]]]}},
    +{"type":"Feature","id":"KEN","properties":{"name":"Kenya"},"geometry":{"type":"Polygon","coordinates":[[[40.993,-0.85829],[41.58513,-1.68325],[40.88477,-2.08255],[40.63785,-2.49979],[40.26304,-2.57309],[40.12119,-3.27768],[39.80006,-3.68116],[39.60489,-4.34653],[39.20222,-4.67677],[37.7669,-3.67712],[37.69869,-3.09699],[34.07262,-1.05982],[33.903711,-0.95],[33.893569,0.109814],[34.18,0.515],[34.6721,1.17694],[35.03599,1.90584],[34.59607,3.05374],[34.47913,3.5556],[34.005,4.249885],[34.620196,4.847123],[35.298007,5.506],[35.817448,5.338232],[35.817448,4.776966],[36.159079,4.447864],[36.855093,4.447864],[38.120915,3.598605],[38.43697,3.58851],[38.67114,3.61607],[38.89251,3.50074],[39.559384,3.42206],[39.85494,3.83879],[40.76848,4.25702],[41.1718,3.91909],[41.855083,3.918912],[40.98105,2.78452],[40.993,-0.85829]]]}},
    +{"type":"Feature","id":"KGZ","properties":{"name":"Kyrgyzstan"},"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[71.186281,42.704293],[71.844638,42.845395],[73.489758,42.500894],[73.645304,43.091272],[74.212866,43.298339],[75.636965,42.8779],[76.000354,42.988022],[77.658392,42.960686],[79.142177,42.856092],[79.643645,42.496683],[80.25999,42.349999],[80.11943,42.123941],[78.543661,41.582243],[78.187197,41.185316],[76.904484,41.066486],[76.526368,40.427946],[75.467828,40.562072],[74.776862,40.366425],[73.822244,39.893973],[73.960013,39.660008],[73.675379,39.431237],[71.784694,39.279463],[70.549162,39.604198],[69.464887,39.526683],[69.55961,40.103211],[70.648019,39.935754],[71.014198,40.244366],[71.774875,40.145844],[73.055417,40.866033],[71.870115,41.3929],[71.157859,41.143587],[70.420022,41.519998],[71.259248,42.167711],[70.962315,42.266154]]]}},
    +{"type":"Feature","id":"KHM","properties":{"name":"Cambodia"},"geometry":{"type":"Polygon","coordinates":[[[103.49728,10.632555],[103.09069,11.153661],[102.584932,12.186595],[102.348099,13.394247],[102.988422,14.225721],[104.281418,14.416743],[105.218777,14.273212],[106.043946,13.881091],[106.496373,14.570584],[107.382727,14.202441],[107.614548,13.535531],[107.491403,12.337206],[105.810524,11.567615],[106.24967,10.961812],[105.199915,10.88931],[104.334335,10.486544],[103.49728,10.632555]]]}},
    +{"type":"Feature","id":"KOR","properties":{"name":"South Korea"},"geometry":{"type":"Polygon","coordinates":[[[128.349716,38.612243],[129.21292,37.432392],[129.46045,36.784189],[129.468304,35.632141],[129.091377,35.082484],[128.18585,34.890377],[127.386519,34.475674],[126.485748,34.390046],[126.37392,34.93456],[126.559231,35.684541],[126.117398,36.725485],[126.860143,36.893924],[126.174759,37.749686],[126.237339,37.840378],[126.68372,37.804773],[127.073309,38.256115],[127.780035,38.304536],[128.205746,38.370397],[128.349716,38.612243]]]}},
    +{"type":"Feature","id":"CS-KM","properties":{"name":"Kosovo"},"geometry":{"type":"Polygon","coordinates":[[[20.76216,42.05186],[20.71731,41.84711],[20.59023,41.85541],[20.52295,42.21787],[20.28374,42.32025],[20.0707,42.58863],[20.25758,42.81275],[20.49679,42.88469],[20.63508,43.21671],[20.81448,43.27205],[20.95651,43.13094],[21.143395,43.068685],[21.27421,42.90959],[21.43866,42.86255],[21.63302,42.67717],[21.77505,42.6827],[21.66292,42.43922],[21.54332,42.32025],[21.576636,42.245224],[21.3527,42.2068],[20.76216,42.05186]]]}},
    +{"type":"Feature","id":"KWT","properties":{"name":"Kuwait"},"geometry":{"type":"Polygon","coordinates":[[[47.974519,29.975819],[48.183189,29.534477],[48.093943,29.306299],[48.416094,28.552004],[47.708851,28.526063],[47.459822,29.002519],[46.568713,29.099025],[47.302622,30.05907],[47.974519,29.975819]]]}},
    +{"type":"Feature","id":"LAO","properties":{"name":"Laos"},"geometry":{"type":"Polygon","coordinates":[[[105.218777,14.273212],[105.544338,14.723934],[105.589039,15.570316],[104.779321,16.441865],[104.716947,17.428859],[103.956477,18.240954],[103.200192,18.309632],[102.998706,17.961695],[102.413005,17.932782],[102.113592,18.109102],[101.059548,17.512497],[101.035931,18.408928],[101.282015,19.462585],[100.606294,19.508344],[100.548881,20.109238],[100.115988,20.41785],[100.329101,20.786122],[101.180005,21.436573],[101.270026,21.201652],[101.80312,21.174367],[101.652018,22.318199],[102.170436,22.464753],[102.754896,21.675137],[103.203861,20.766562],[104.435,20.758733],[104.822574,19.886642],[104.183388,19.624668],[103.896532,19.265181],[105.094598,18.666975],[105.925762,17.485315],[106.556008,16.604284],[107.312706,15.908538],[107.564525,15.202173],[107.382727,14.202441],[106.496373,14.570584],[106.043946,13.881091],[105.218777,14.273212]]]}},
    +{"type":"Feature","id":"LBN","properties":{"name":"Lebanon"},"geometry":{"type":"Polygon","coordinates":[[[35.821101,33.277426],[35.552797,33.264275],[35.460709,33.08904],[35.126053,33.0909],[35.482207,33.90545],[35.979592,34.610058],[35.998403,34.644914],[36.448194,34.593935],[36.61175,34.201789],[36.06646,33.824912],[35.821101,33.277426]]]}},
    +{"type":"Feature","id":"LBR","properties":{"name":"Liberia"},"geometry":{"type":"Polygon","coordinates":[[[-7.712159,4.364566],[-7.974107,4.355755],[-9.004794,4.832419],[-9.91342,5.593561],[-10.765384,6.140711],[-11.438779,6.785917],[-11.199802,7.105846],[-11.146704,7.396706],[-10.695595,7.939464],[-10.230094,8.406206],[-10.016567,8.428504],[-9.755342,8.541055],[-9.33728,7.928534],[-9.403348,7.526905],[-9.208786,7.313921],[-8.926065,7.309037],[-8.722124,7.711674],[-8.439298,7.686043],[-8.485446,7.395208],[-8.385452,6.911801],[-8.60288,6.467564],[-8.311348,6.193033],[-7.993693,6.12619],[-7.570153,5.707352],[-7.539715,5.313345],[-7.635368,5.188159],[-7.712159,4.364566]]]}},
    +{"type":"Feature","id":"LBY","properties":{"name":"Libya"},"geometry":{"type":"Polygon","coordinates":[[[14.8513,22.86295],[14.143871,22.491289],[13.581425,23.040506],[11.999506,23.471668],[11.560669,24.097909],[10.771364,24.562532],[10.303847,24.379313],[9.948261,24.936954],[9.910693,25.365455],[9.319411,26.094325],[9.716286,26.512206],[9.629056,27.140953],[9.756128,27.688259],[9.683885,28.144174],[9.859998,28.95999],[9.805634,29.424638],[9.48214,30.307556],[9.970017,30.539325],[10.056575,30.961831],[9.950225,31.37607],[10.636901,31.761421],[10.94479,32.081815],[11.432253,32.368903],[11.488787,33.136996],[12.66331,32.79278],[13.08326,32.87882],[13.91868,32.71196],[15.24563,32.26508],[15.71394,31.37626],[16.61162,31.18218],[18.02109,30.76357],[19.08641,30.26639],[19.57404,30.52582],[20.05335,30.98576],[19.82033,31.75179],[20.13397,32.2382],[20.85452,32.7068],[21.54298,32.8432],[22.89576,32.63858],[23.2368,32.19149],[23.60913,32.18726],[23.9275,32.01667],[24.92114,31.89936],[25.16482,31.56915],[24.80287,31.08929],[24.95762,30.6616],[24.70007,30.04419],[25,29.238655],[25,25.6825],[25,22],[25,20.00304],[23.85,20],[23.83766,19.58047],[19.84926,21.49509],[15.86085,23.40972],[14.8513,22.86295]]]}},
    +{"type":"Feature","id":"LKA","properties":{"name":"Sri Lanka"},"geometry":{"type":"Polygon","coordinates":[[[81.787959,7.523055],[81.637322,6.481775],[81.21802,6.197141],[80.348357,5.96837],[79.872469,6.763463],[79.695167,8.200843],[80.147801,9.824078],[80.838818,9.268427],[81.304319,8.564206],[81.787959,7.523055]]]}},
    +{"type":"Feature","id":"LSO","properties":{"name":"Lesotho"},"geometry":{"type":"Polygon","coordinates":[[[28.978263,-28.955597],[29.325166,-29.257387],[29.018415,-29.743766],[28.8484,-30.070051],[28.291069,-30.226217],[28.107205,-30.545732],[27.749397,-30.645106],[26.999262,-29.875954],[27.532511,-29.242711],[28.074338,-28.851469],[28.5417,-28.647502],[28.978263,-28.955597]]]}},
    +{"type":"Feature","id":"LTU","properties":{"name":"Lithuania"},"geometry":{"type":"Polygon","coordinates":[[[22.731099,54.327537],[22.651052,54.582741],[22.757764,54.856574],[22.315724,55.015299],[21.268449,55.190482],[21.0558,56.031076],[22.201157,56.337802],[23.878264,56.273671],[24.860684,56.372528],[25.000934,56.164531],[25.533047,56.100297],[26.494331,55.615107],[26.588279,55.167176],[25.768433,54.846963],[25.536354,54.282423],[24.450684,53.905702],[23.484128,53.912498],[23.243987,54.220567],[22.731099,54.327537]]]}},
    +{"type":"Feature","id":"LUX","properties":{"name":"Luxembourg"},"geometry":{"type":"Polygon","coordinates":[[[6.043073,50.128052],[6.242751,49.902226],[6.18632,49.463803],[5.897759,49.442667],[5.674052,49.529484],[5.782417,50.090328],[6.043073,50.128052]]]}},
    +{"type":"Feature","id":"LVA","properties":{"name":"Latvia"},"geometry":{"type":"Polygon","coordinates":[[[21.0558,56.031076],[21.090424,56.783873],[21.581866,57.411871],[22.524341,57.753374],[23.318453,57.006236],[24.12073,57.025693],[24.312863,57.793424],[25.164594,57.970157],[25.60281,57.847529],[26.463532,57.476389],[27.288185,57.474528],[27.770016,57.244258],[27.855282,56.759326],[28.176709,56.16913],[27.10246,55.783314],[26.494331,55.615107],[25.533047,56.100297],[25.000934,56.164531],[24.860684,56.372528],[23.878264,56.273671],[22.201157,56.337802],[21.0558,56.031076]]]}},
    +{"type":"Feature","id":"MAR","properties":{"name":"Morocco"},"geometry":{"type":"Polygon","coordinates":[[[-5.193863,35.755182],[-4.591006,35.330712],[-3.640057,35.399855],[-2.604306,35.179093],[-2.169914,35.168396],[-1.792986,34.527919],[-1.733455,33.919713],[-1.388049,32.864015],[-1.124551,32.651522],[-1.307899,32.262889],[-2.616605,32.094346],[-3.06898,31.724498],[-3.647498,31.637294],[-3.690441,30.896952],[-4.859646,30.501188],[-5.242129,30.000443],[-6.060632,29.7317],[-7.059228,29.579228],[-8.674116,28.841289],[-8.66559,27.656426],[-8.817809,27.656426],[-8.817828,27.656426],[-8.794884,27.120696],[-9.413037,27.088476],[-9.735343,26.860945],[-10.189424,26.860945],[-10.551263,26.990808],[-11.392555,26.883424],[-11.71822,26.104092],[-12.030759,26.030866],[-12.500963,24.770116],[-13.89111,23.691009],[-14.221168,22.310163],[-14.630833,21.86094],[-14.750955,21.5006],[-17.002962,21.420734],[-17.020428,21.42231],[-16.973248,21.885745],[-16.589137,22.158234],[-16.261922,22.67934],[-16.326414,23.017768],[-15.982611,23.723358],[-15.426004,24.359134],[-15.089332,24.520261],[-14.824645,25.103533],[-14.800926,25.636265],[-14.43994,26.254418],[-13.773805,26.618892],[-13.139942,27.640148],[-13.121613,27.654148],[-12.618837,28.038186],[-11.688919,28.148644],[-10.900957,28.832142],[-10.399592,29.098586],[-9.564811,29.933574],[-9.814718,31.177736],[-9.434793,32.038096],[-9.300693,32.564679],[-8.657476,33.240245],[-7.654178,33.697065],[-6.912544,34.110476],[-6.244342,35.145865],[-5.929994,35.759988],[-5.193863,35.755182]]]}},
    +{"type":"Feature","id":"MDA","properties":{"name":"Moldova"},"geometry":{"type":"Polygon","coordinates":[[[26.619337,48.220726],[26.857824,48.368211],[27.522537,48.467119],[28.259547,48.155562],[28.670891,48.118149],[29.122698,47.849095],[29.050868,47.510227],[29.415135,47.346645],[29.559674,46.928583],[29.908852,46.674361],[29.83821,46.525326],[30.024659,46.423937],[29.759972,46.349988],[29.170654,46.379262],[29.072107,46.517678],[28.862972,46.437889],[28.933717,46.25883],[28.659987,45.939987],[28.485269,45.596907],[28.233554,45.488283],[28.054443,45.944586],[28.160018,46.371563],[28.12803,46.810476],[27.551166,47.405117],[27.233873,47.826771],[26.924176,48.123264],[26.619337,48.220726]]]}},
    +{"type":"Feature","id":"MDG","properties":{"name":"Madagascar"},"geometry":{"type":"Polygon","coordinates":[[[49.543519,-12.469833],[49.808981,-12.895285],[50.056511,-13.555761],[50.217431,-14.758789],[50.476537,-15.226512],[50.377111,-15.706069],[50.200275,-16.000263],[49.860606,-15.414253],[49.672607,-15.710204],[49.863344,-16.451037],[49.774564,-16.875042],[49.498612,-17.106036],[49.435619,-17.953064],[49.041792,-19.118781],[48.548541,-20.496888],[47.930749,-22.391501],[47.547723,-23.781959],[47.095761,-24.94163],[46.282478,-25.178463],[45.409508,-25.601434],[44.833574,-25.346101],[44.03972,-24.988345],[43.763768,-24.460677],[43.697778,-23.574116],[43.345654,-22.776904],[43.254187,-22.057413],[43.433298,-21.336475],[43.893683,-21.163307],[43.89637,-20.830459],[44.374325,-20.072366],[44.464397,-19.435454],[44.232422,-18.961995],[44.042976,-18.331387],[43.963084,-17.409945],[44.312469,-16.850496],[44.446517,-16.216219],[44.944937,-16.179374],[45.502732,-15.974373],[45.872994,-15.793454],[46.312243,-15.780018],[46.882183,-15.210182],[47.70513,-14.594303],[48.005215,-14.091233],[47.869047,-13.663869],[48.293828,-13.784068],[48.84506,-13.089175],[48.863509,-12.487868],[49.194651,-12.040557],[49.543519,-12.469833]]]}},
    +{"type":"Feature","id":"MEX","properties":{"name":"Mexico"},"geometry":{"type":"Polygon","coordinates":[[[-97.140008,25.869997],[-97.528072,24.992144],[-97.702946,24.272343],[-97.776042,22.93258],[-97.872367,22.444212],[-97.699044,21.898689],[-97.38896,21.411019],[-97.189333,20.635433],[-96.525576,19.890931],[-96.292127,19.320371],[-95.900885,18.828024],[-94.839063,18.562717],[-94.42573,18.144371],[-93.548651,18.423837],[-92.786114,18.524839],[-92.037348,18.704569],[-91.407903,18.876083],[-90.77187,19.28412],[-90.53359,19.867418],[-90.451476,20.707522],[-90.278618,20.999855],[-89.601321,21.261726],[-88.543866,21.493675],[-87.658417,21.458846],[-87.05189,21.543543],[-86.811982,21.331515],[-86.845908,20.849865],[-87.383291,20.255405],[-87.621054,19.646553],[-87.43675,19.472403],[-87.58656,19.04013],[-87.837191,18.259816],[-88.090664,18.516648],[-88.300031,18.499982],[-88.490123,18.486831],[-88.848344,17.883198],[-89.029857,18.001511],[-89.150909,17.955468],[-89.14308,17.808319],[-90.067934,17.819326],[-91.00152,17.817595],[-91.002269,17.254658],[-91.453921,17.252177],[-91.08167,16.918477],[-90.711822,16.687483],[-90.600847,16.470778],[-90.438867,16.41011],[-90.464473,16.069562],[-91.74796,16.066565],[-92.229249,15.251447],[-92.087216,15.064585],[-92.20323,14.830103],[-92.22775,14.538829],[-93.359464,15.61543],[-93.875169,15.940164],[-94.691656,16.200975],[-95.250227,16.128318],[-96.053382,15.752088],[-96.557434,15.653515],[-97.263592,15.917065],[-98.01303,16.107312],[-98.947676,16.566043],[-99.697397,16.706164],[-100.829499,17.171071],[-101.666089,17.649026],[-101.918528,17.91609],[-102.478132,17.975751],[-103.50099,18.292295],[-103.917527,18.748572],[-104.99201,19.316134],[-105.493038,19.946767],[-105.731396,20.434102],[-105.397773,20.531719],[-105.500661,20.816895],[-105.270752,21.076285],[-105.265817,21.422104],[-105.603161,21.871146],[-105.693414,22.26908],[-106.028716,22.773752],[-106.90998,23.767774],[-107.915449,24.548915],[-108.401905,25.172314],[-109.260199,25.580609],[-109.444089,25.824884],[-109.291644,26.442934],[-109.801458,26.676176],[-110.391732,27.162115],[-110.641019,27.859876],[-111.178919,27.941241],[-111.759607,28.467953],[-112.228235,28.954409],[-112.271824,29.266844],[-112.809594,30.021114],[-113.163811,30.786881],[-113.148669,31.170966],[-113.871881,31.567608],[-114.205737,31.524045],[-114.776451,31.799532],[-114.9367,31.393485],[-114.771232,30.913617],[-114.673899,30.162681],[-114.330974,29.750432],[-113.588875,29.061611],[-113.424053,28.826174],[-113.271969,28.754783],[-113.140039,28.411289],[-112.962298,28.42519],[-112.761587,27.780217],[-112.457911,27.525814],[-112.244952,27.171727],[-111.616489,26.662817],[-111.284675,25.73259],[-110.987819,25.294606],[-110.710007,24.826004],[-110.655049,24.298595],[-110.172856,24.265548],[-109.771847,23.811183],[-109.409104,23.364672],[-109.433392,23.185588],[-109.854219,22.818272],[-110.031392,22.823078],[-110.295071,23.430973],[-110.949501,24.000964],[-111.670568,24.484423],[-112.182036,24.738413],[-112.148989,25.470125],[-112.300711,26.012004],[-112.777297,26.32196],[-113.464671,26.768186],[-113.59673,26.63946],[-113.848937,26.900064],[-114.465747,27.14209],[-115.055142,27.722727],[-114.982253,27.7982],[-114.570366,27.741485],[-114.199329,28.115003],[-114.162018,28.566112],[-114.931842,29.279479],[-115.518654,29.556362],[-115.887365,30.180794],[-116.25835,30.836464],[-116.721526,31.635744],[-117.12776,32.53534],[-115.99135,32.61239],[-114.72139,32.72083],[-114.815,32.52528],[-113.30498,32.03914],[-111.02361,31.33472],[-109.035,31.34194],[-108.24194,31.34222],[-108.24,31.754854],[-106.50759,31.75452],[-106.1429,31.39995],[-105.63159,31.08383],[-105.03737,30.64402],[-104.70575,30.12173],[-104.45697,29.57196],[-103.94,29.27],[-103.11,28.97],[-102.48,29.76],[-101.6624,29.7793],[-100.9576,29.38071],[-100.45584,28.69612],[-100.11,28.11],[-99.52,27.54],[-99.3,26.84],[-99.02,26.37],[-98.24,26.06],[-97.53,25.84],[-97.140008,25.869997]]]}},
    +{"type":"Feature","id":"MKD","properties":{"name":"Macedonia"},"geometry":{"type":"Polygon","coordinates":[[[20.59023,41.85541],[20.71731,41.84711],[20.76216,42.05186],[21.3527,42.2068],[21.576636,42.245224],[21.91708,42.30364],[22.380526,42.32026],[22.881374,41.999297],[22.952377,41.337994],[22.76177,41.3048],[22.597308,41.130487],[22.055378,41.149866],[21.674161,40.931275],[21.02004,40.842727],[20.60518,41.08622],[20.46315,41.51509],[20.59023,41.85541]]]}},
    +{"type":"Feature","id":"MLI","properties":{"name":"Mali"},"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-11.834208,14.799097],[-11.666078,15.388208],[-11.349095,15.411256],[-10.650791,15.132746],[-10.086846,15.330486],[-9.700255,15.264107],[-9.550238,15.486497],[-5.537744,15.50169],[-5.315277,16.201854],[-5.488523,16.325102],[-5.971129,20.640833],[-6.453787,24.956591],[-4.923337,24.974574],[-1.550055,22.792666],[1.823228,20.610809],[2.060991,20.142233],[2.683588,19.85623],[3.146661,19.693579],[3.158133,19.057364],[4.267419,19.155265],[4.27021,16.852227],[3.723422,16.184284],[3.638259,15.56812],[2.749993,15.409525],[1.385528,15.323561],[1.015783,14.968182],[0.374892,14.928908],[-0.266257,14.924309],[-0.515854,15.116158],[-1.066363,14.973815],[-2.001035,14.559008],[-2.191825,14.246418],[-2.967694,13.79815],[-3.103707,13.541267],[-3.522803,13.337662],[-4.006391,13.472485],[-4.280405,13.228444],[-4.427166,12.542646],[-5.220942,11.713859],[-5.197843,11.375146],[-5.470565,10.95127],[-5.404342,10.370737],[-5.816926,10.222555],[-6.050452,10.096361],[-6.205223,10.524061],[-6.493965,10.411303],[-6.666461,10.430811],[-6.850507,10.138994],[-7.622759,10.147236],[-7.89959,10.297382],[-8.029944,10.206535],[-8.335377,10.494812],[-8.282357,10.792597],[-8.407311,10.909257],[-8.620321,10.810891],[-8.581305,11.136246],[-8.376305,11.393646],[-8.786099,11.812561],[-8.905265,12.088358],[-9.127474,12.30806],[-9.327616,12.334286],[-9.567912,12.194243],[-9.890993,12.060479],[-10.165214,11.844084],[-10.593224,11.923975],[-10.87083,12.177887],[-11.036556,12.211245],[-11.297574,12.077971],[-11.456169,12.076834],[-11.513943,12.442988],[-11.467899,12.754519],[-11.553398,13.141214],[-11.927716,13.422075],[-12.124887,13.994727],[-12.17075,14.616834]]]}},
    +{"type":"Feature","id":"MMR","properties":{"name":"Myanmar"},"geometry":{"type":"Polygon","coordinates":[[[99.543309,20.186598],[98.959676,19.752981],[98.253724,19.708203],[97.797783,18.62708],[97.375896,18.445438],[97.859123,17.567946],[98.493761,16.837836],[98.903348,16.177824],[98.537376,15.308497],[98.192074,15.123703],[98.430819,14.622028],[99.097755,13.827503],[99.212012,13.269294],[99.196354,12.804748],[99.587286,11.892763],[99.038121,10.960546],[98.553551,9.93296],[98.457174,10.675266],[98.764546,11.441292],[98.428339,12.032987],[98.509574,13.122378],[98.103604,13.64046],[97.777732,14.837286],[97.597072,16.100568],[97.16454,16.928734],[96.505769,16.427241],[95.369352,15.71439],[94.808405,15.803454],[94.188804,16.037936],[94.533486,17.27724],[94.324817,18.213514],[93.540988,19.366493],[93.663255,19.726962],[93.078278,19.855145],[92.368554,20.670883],[92.303234,21.475485],[92.652257,21.324048],[92.672721,22.041239],[93.166128,22.27846],[93.060294,22.703111],[93.286327,23.043658],[93.325188,24.078556],[94.106742,23.850741],[94.552658,24.675238],[94.603249,25.162495],[95.155153,26.001307],[95.124768,26.573572],[96.419366,27.264589],[97.133999,27.083774],[97.051989,27.699059],[97.402561,27.882536],[97.327114,28.261583],[97.911988,28.335945],[98.246231,27.747221],[98.68269,27.508812],[98.712094,26.743536],[98.671838,25.918703],[97.724609,25.083637],[97.60472,23.897405],[98.660262,24.063286],[98.898749,23.142722],[99.531992,22.949039],[99.240899,22.118314],[99.983489,21.742937],[100.416538,21.558839],[101.150033,21.849984],[101.180005,21.436573],[100.329101,20.786122],[100.115988,20.41785],[99.543309,20.186598]]]}},
    +{"type":"Feature","id":"MNE","properties":{"name":"Montenegro"},"geometry":{"type":"Polygon","coordinates":[[[19.801613,42.500093],[19.738051,42.688247],[19.30449,42.19574],[19.37177,41.87755],[19.16246,41.95502],[18.88214,42.28151],[18.45,42.48],[18.56,42.65],[18.70648,43.20011],[19.03165,43.43253],[19.21852,43.52384],[19.48389,43.35229],[19.63,43.21378],[19.95857,43.10604],[20.3398,42.89852],[20.25758,42.81275],[20.0707,42.58863],[19.801613,42.500093]]]}},
    +{"type":"Feature","id":"MNG","properties":{"name":"Mongolia"},"geometry":{"type":"Polygon","coordinates":[[[87.751264,49.297198],[88.805567,49.470521],[90.713667,50.331812],[92.234712,50.802171],[93.104219,50.49529],[94.147566,50.480537],[94.815949,50.013433],[95.814028,49.977467],[97.259728,49.726061],[98.231762,50.422401],[97.82574,51.010995],[98.861491,52.047366],[99.981732,51.634006],[100.88948,51.516856],[102.065223,51.259921],[102.255909,50.510561],[103.676545,50.089966],[104.621552,50.275329],[105.886591,50.406019],[106.888804,50.274296],[107.868176,49.793705],[108.475167,49.282548],[109.402449,49.292961],[110.662011,49.130128],[111.581231,49.377968],[112.89774,49.543565],[114.362456,50.248303],[114.96211,50.140247],[115.485695,49.805177],[116.678801,49.888531],[116.191802,49.134598],[115.485282,48.135383],[115.742837,47.726545],[116.308953,47.85341],[117.295507,47.697709],[118.064143,48.06673],[118.866574,47.74706],[119.772824,47.048059],[119.66327,46.69268],[118.874326,46.805412],[117.421701,46.672733],[116.717868,46.388202],[115.985096,45.727235],[114.460332,45.339817],[113.463907,44.808893],[112.436062,45.011646],[111.873306,45.102079],[111.348377,44.457442],[111.667737,44.073176],[111.829588,43.743118],[111.129682,43.406834],[110.412103,42.871234],[109.243596,42.519446],[107.744773,42.481516],[106.129316,42.134328],[104.964994,41.59741],[104.522282,41.908347],[103.312278,41.907468],[101.83304,42.514873],[100.845866,42.663804],[99.515817,42.524691],[97.451757,42.74889],[96.349396,42.725635],[95.762455,43.319449],[95.306875,44.241331],[94.688929,44.352332],[93.480734,44.975472],[92.133891,45.115076],[90.94554,45.286073],[90.585768,45.719716],[90.970809,46.888146],[90.280826,47.693549],[88.854298,48.069082],[88.013832,48.599463],[87.751264,49.297198]]]}},
    +{"type":"Feature","id":"MOZ","properties":{"name":"Mozambique"},"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[35.312398,-11.439146],[36.514082,-11.720938],[36.775151,-11.594537],[37.471284,-11.568751],[37.827645,-11.268769],[38.427557,-11.285202],[39.52103,-10.896854],[40.316589,-10.317096],[40.478387,-10.765441],[40.437253,-11.761711],[40.560811,-12.639177],[40.59962,-14.201975],[40.775475,-14.691764],[40.477251,-15.406294],[40.089264,-16.100774],[39.452559,-16.720891],[38.538351,-17.101023],[37.411133,-17.586368],[36.281279,-18.659688],[35.896497,-18.84226],[35.1984,-19.552811],[34.786383,-19.784012],[34.701893,-20.497043],[35.176127,-21.254361],[35.373428,-21.840837],[35.385848,-22.14],[35.562546,-22.09],[35.533935,-23.070788],[35.371774,-23.535359],[35.60747,-23.706563],[35.458746,-24.12261],[35.040735,-24.478351],[34.215824,-24.816314],[33.01321,-25.357573],[32.574632,-25.727318],[32.660363,-26.148584],[32.915955,-26.215867],[32.83012,-26.742192],[32.071665,-26.73382],[31.985779,-26.29178],[31.837778,-25.843332],[31.752408,-25.484284],[31.930589,-24.369417],[31.670398,-23.658969],[31.191409,-22.25151],[32.244988,-21.116489],[32.508693,-20.395292],[32.659743,-20.30429],[32.772708,-19.715592],[32.611994,-19.419383],[32.654886,-18.67209],[32.849861,-17.979057],[32.847639,-16.713398],[32.328239,-16.392074],[31.852041,-16.319417],[31.636498,-16.07199],[31.173064,-15.860944],[30.338955,-15.880839],[30.274256,-15.507787],[30.179481,-14.796099],[33.214025,-13.97186],[33.7897,-14.451831],[34.064825,-14.35995],[34.459633,-14.61301],[34.517666,-15.013709],[34.307291,-15.478641],[34.381292,-16.18356],[35.03381,-16.8013],[35.339063,-16.10744],[35.771905,-15.896859],[35.686845,-14.611046],[35.267956,-13.887834],[34.907151,-13.565425],[34.559989,-13.579998],[34.280006,-12.280025],[34.559989,-11.52002]]]}},
    +{"type":"Feature","id":"MRT","properties":{"name":"Mauritania"},"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-12.830658,15.303692],[-13.435738,16.039383],[-14.099521,16.304302],[-14.577348,16.598264],[-15.135737,16.587282],[-15.623666,16.369337],[-16.12069,16.455663],[-16.463098,16.135036],[-16.549708,16.673892],[-16.270552,17.166963],[-16.146347,18.108482],[-16.256883,19.096716],[-16.377651,19.593817],[-16.277838,20.092521],[-16.536324,20.567866],[-17.063423,20.999752],[-16.845194,21.333323],[-12.929102,21.327071],[-13.118754,22.77122],[-12.874222,23.284832],[-11.937224,23.374594],[-11.969419,25.933353],[-8.687294,25.881056],[-8.6844,27.395744],[-4.923337,24.974574],[-6.453787,24.956591],[-5.971129,20.640833],[-5.488523,16.325102],[-5.315277,16.201854],[-5.537744,15.50169],[-9.550238,15.486497],[-9.700255,15.264107],[-10.086846,15.330486],[-10.650791,15.132746],[-11.349095,15.411256],[-11.666078,15.388208],[-11.834208,14.799097],[-12.17075,14.616834]]]}},
    +{"type":"Feature","id":"MWI","properties":{"name":"Malawi"},"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[34.280006,-12.280025],[34.559989,-13.579998],[34.907151,-13.565425],[35.267956,-13.887834],[35.686845,-14.611046],[35.771905,-15.896859],[35.339063,-16.10744],[35.03381,-16.8013],[34.381292,-16.18356],[34.307291,-15.478641],[34.517666,-15.013709],[34.459633,-14.61301],[34.064825,-14.35995],[33.7897,-14.451831],[33.214025,-13.97186],[32.688165,-13.712858],[32.991764,-12.783871],[33.306422,-12.435778],[33.114289,-11.607198],[33.31531,-10.79655],[33.485688,-10.525559],[33.231388,-9.676722],[32.759375,-9.230599],[33.739729,-9.417151],[33.940838,-9.693674],[34.280006,-10.16],[34.559989,-11.52002]]]}},
    +{"type":"Feature","id":"MYS","properties":{"name":"Malaysia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[101.075516,6.204867],[101.154219,5.691384],[101.814282,5.810808],[102.141187,6.221636],[102.371147,6.128205],[102.961705,5.524495],[103.381215,4.855001],[103.438575,4.181606],[103.332122,3.726698],[103.429429,3.382869],[103.502448,2.791019],[103.854674,2.515454],[104.247932,1.631141],[104.228811,1.293048],[103.519707,1.226334],[102.573615,1.967115],[101.390638,2.760814],[101.27354,3.270292],[100.695435,3.93914],[100.557408,4.76728],[100.196706,5.312493],[100.30626,6.040562],[100.085757,6.464489],[100.259596,6.642825],[101.075516,6.204867]]],[[[118.618321,4.478202],[117.882035,4.137551],[117.015214,4.306094],[115.865517,4.306559],[115.519078,3.169238],[115.134037,2.821482],[114.621355,1.430688],[113.80585,1.217549],[112.859809,1.49779],[112.380252,1.410121],[111.797548,0.904441],[111.159138,0.976478],[110.514061,0.773131],[109.830227,1.338136],[109.66326,2.006467],[110.396135,1.663775],[111.168853,1.850637],[111.370081,2.697303],[111.796928,2.885897],[112.995615,3.102395],[113.712935,3.893509],[114.204017,4.525874],[114.659596,4.007637],[114.869557,4.348314],[115.347461,4.316636],[115.4057,4.955228],[115.45071,5.44773],[116.220741,6.143191],[116.725103,6.924771],[117.129626,6.928053],[117.643393,6.422166],[117.689075,5.98749],[118.347691,5.708696],[119.181904,5.407836],[119.110694,5.016128],[118.439727,4.966519],[118.618321,4.478202]]]]}},
    +{"type":"Feature","id":"NAM","properties":{"name":"Namibia"},"geometry":{"type":"Polygon","coordinates":[[[16.344977,-28.576705],[15.601818,-27.821247],[15.210472,-27.090956],[14.989711,-26.117372],[14.743214,-25.39292],[14.408144,-23.853014],[14.385717,-22.656653],[14.257714,-22.111208],[13.868642,-21.699037],[13.352498,-20.872834],[12.826845,-19.673166],[12.608564,-19.045349],[11.794919,-18.069129],[11.734199,-17.301889],[12.215461,-17.111668],[12.814081,-16.941343],[13.462362,-16.971212],[14.058501,-17.423381],[14.209707,-17.353101],[18.263309,-17.309951],[18.956187,-17.789095],[21.377176,-17.930636],[23.215048,-17.523116],[24.033862,-17.295843],[24.682349,-17.353411],[25.07695,-17.578823],[25.084443,-17.661816],[24.520705,-17.887125],[24.217365,-17.889347],[23.579006,-18.281261],[23.196858,-17.869038],[21.65504,-18.219146],[20.910641,-18.252219],[20.881134,-21.814327],[19.895458,-21.849157],[19.895768,-24.76779],[19.894734,-28.461105],[19.002127,-28.972443],[18.464899,-29.045462],[17.836152,-28.856378],[17.387497,-28.783514],[17.218929,-28.355943],[16.824017,-28.082162],[16.344977,-28.576705]]]}},
    +{"type":"Feature","id":"NCL","properties":{"name":"New Caledonia"},"geometry":{"type":"Polygon","coordinates":[[[165.77999,-21.080005],[166.599991,-21.700019],[167.120011,-22.159991],[166.740035,-22.399976],[166.189732,-22.129708],[165.474375,-21.679607],[164.829815,-21.14982],[164.167995,-20.444747],[164.029606,-20.105646],[164.459967,-20.120012],[165.020036,-20.459991],[165.460009,-20.800022],[165.77999,-21.080005]]]}},
    +{"type":"Feature","id":"NER","properties":{"name":"Niger"},"geometry":{"type":"Polygon","coordinates":[[[2.154474,11.94015],[2.177108,12.625018],[1.024103,12.851826],[0.993046,13.33575],[0.429928,13.988733],[0.295646,14.444235],[0.374892,14.928908],[1.015783,14.968182],[1.385528,15.323561],[2.749993,15.409525],[3.638259,15.56812],[3.723422,16.184284],[4.27021,16.852227],[4.267419,19.155265],[5.677566,19.601207],[8.572893,21.565661],[11.999506,23.471668],[13.581425,23.040506],[14.143871,22.491289],[14.8513,22.86295],[15.096888,21.308519],[15.471077,21.048457],[15.487148,20.730415],[15.903247,20.387619],[15.685741,19.95718],[15.300441,17.92795],[15.247731,16.627306],[13.972202,15.684366],[13.540394,14.367134],[13.956699,13.996691],[13.954477,13.353449],[14.595781,13.330427],[14.495787,12.859396],[14.213531,12.802035],[14.181336,12.483657],[13.995353,12.461565],[13.318702,13.556356],[13.083987,13.596147],[12.302071,13.037189],[11.527803,13.32898],[10.989593,13.387323],[10.701032,13.246918],[10.114814,13.277252],[9.524928,12.851102],[9.014933,12.826659],[7.804671,13.343527],[7.330747,13.098038],[6.820442,13.115091],[6.445426,13.492768],[5.443058,13.865924],[4.368344,13.747482],[4.107946,13.531216],[3.967283,12.956109],[3.680634,12.552903],[3.61118,11.660167],[2.848643,12.235636],[2.490164,12.233052],[2.154474,11.94015]]]}},
    +{"type":"Feature","id":"NGA","properties":{"name":"Nigeria"},"geometry":{"type":"Polygon","coordinates":[[[8.500288,4.771983],[7.462108,4.412108],[7.082596,4.464689],[6.698072,4.240594],[5.898173,4.262453],[5.362805,4.887971],[5.033574,5.611802],[4.325607,6.270651],[3.57418,6.2583],[2.691702,6.258817],[2.749063,7.870734],[2.723793,8.506845],[2.912308,9.137608],[3.220352,9.444153],[3.705438,10.06321],[3.60007,10.332186],[3.797112,10.734746],[3.572216,11.327939],[3.61118,11.660167],[3.680634,12.552903],[3.967283,12.956109],[4.107946,13.531216],[4.368344,13.747482],[5.443058,13.865924],[6.445426,13.492768],[6.820442,13.115091],[7.330747,13.098038],[7.804671,13.343527],[9.014933,12.826659],[9.524928,12.851102],[10.114814,13.277252],[10.701032,13.246918],[10.989593,13.387323],[11.527803,13.32898],[12.302071,13.037189],[13.083987,13.596147],[13.318702,13.556356],[13.995353,12.461565],[14.181336,12.483657],[14.577178,12.085361],[14.468192,11.904752],[14.415379,11.572369],[13.57295,10.798566],[13.308676,10.160362],[13.1676,9.640626],[12.955468,9.417772],[12.753672,8.717763],[12.218872,8.305824],[12.063946,7.799808],[11.839309,7.397042],[11.745774,6.981383],[11.058788,6.644427],[10.497375,7.055358],[10.118277,7.03877],[9.522706,6.453482],[9.233163,6.444491],[8.757533,5.479666],[8.500288,4.771983]]]}},
    +{"type":"Feature","id":"NIC","properties":{"name":"Nicaragua"},"geometry":{"type":"Polygon","coordinates":[[[-85.71254,11.088445],[-86.058488,11.403439],[-86.52585,11.806877],[-86.745992,12.143962],[-87.167516,12.458258],[-87.668493,12.90991],[-87.557467,13.064552],[-87.392386,12.914018],[-87.316654,12.984686],[-87.005769,13.025794],[-86.880557,13.254204],[-86.733822,13.263093],[-86.755087,13.754845],[-86.520708,13.778487],[-86.312142,13.771356],[-86.096264,14.038187],[-85.801295,13.836055],[-85.698665,13.960078],[-85.514413,14.079012],[-85.165365,14.35437],[-85.148751,14.560197],[-85.052787,14.551541],[-84.924501,14.790493],[-84.820037,14.819587],[-84.649582,14.666805],[-84.449336,14.621614],[-84.228342,14.748764],[-83.975721,14.749436],[-83.628585,14.880074],[-83.489989,15.016267],[-83.147219,14.995829],[-83.233234,14.899866],[-83.284162,14.676624],[-83.182126,14.310703],[-83.4125,13.970078],[-83.519832,13.567699],[-83.552207,13.127054],[-83.498515,12.869292],[-83.473323,12.419087],[-83.626104,12.32085],[-83.719613,11.893124],[-83.650858,11.629032],[-83.85547,11.373311],[-83.808936,11.103044],[-83.655612,10.938764],[-83.895054,10.726839],[-84.190179,10.79345],[-84.355931,10.999226],[-84.673069,11.082657],[-84.903003,10.952303],[-85.561852,11.217119],[-85.71254,11.088445]]]}},
    +{"type":"Feature","id":"NLD","properties":{"name":"Netherlands"},"geometry":{"type":"Polygon","coordinates":[[[6.074183,53.510403],[6.90514,53.482162],[7.092053,53.144043],[6.84287,52.22844],[6.589397,51.852029],[5.988658,51.851616],[6.156658,50.803721],[5.606976,51.037298],[4.973991,51.475024],[4.047071,51.267259],[3.314971,51.345755],[3.830289,51.620545],[4.705997,53.091798],[6.074183,53.510403]]]}},
    +{"type":"Feature","id":"NOR","properties":{"name":"Norway"},"geometry":{"type":"MultiPolygon","coordinates":[[[[28.165547,71.185474],[31.293418,70.453788],[30.005435,70.186259],[31.101079,69.55808],[29.399581,69.156916],[28.59193,69.064777],[29.015573,69.766491],[27.732292,70.164193],[26.179622,69.825299],[25.689213,69.092114],[24.735679,68.649557],[23.66205,68.891247],[22.356238,68.841741],[21.244936,69.370443],[20.645593,69.106247],[20.025269,69.065139],[19.87856,68.407194],[17.993868,68.567391],[17.729182,68.010552],[16.768879,68.013937],[16.108712,67.302456],[15.108411,66.193867],[13.55569,64.787028],[13.919905,64.445421],[13.571916,64.049114],[12.579935,64.066219],[11.930569,63.128318],[11.992064,61.800362],[12.631147,61.293572],[12.300366,60.117933],[11.468272,59.432393],[11.027369,58.856149],[10.356557,59.469807],[8.382,58.313288],[7.048748,58.078884],[5.665835,58.588155],[5.308234,59.663232],[4.992078,61.970998],[5.9129,62.614473],[8.553411,63.454008],[10.527709,64.486038],[12.358347,65.879726],[14.761146,67.810642],[16.435927,68.563205],[19.184028,69.817444],[21.378416,70.255169],[23.023742,70.202072],[24.546543,71.030497],[26.37005,70.986262],[28.165547,71.185474]]],[[[24.72412,77.85385],[22.49032,77.44493],[20.72601,77.67704],[21.41611,77.93504],[20.8119,78.25463],[22.88426,78.45494],[23.28134,78.07954],[24.72412,77.85385]]],[[[18.25183,79.70175],[21.54383,78.95611],[19.02737,78.5626],[18.47172,77.82669],[17.59441,77.63796],[17.1182,76.80941],[15.91315,76.77045],[13.76259,77.38035],[14.66956,77.73565],[13.1706,78.02493],[11.22231,78.8693],[10.44453,79.65239],[13.17077,80.01046],[13.71852,79.66039],[15.14282,79.67431],[15.52255,80.01608],[16.99085,80.05086],[18.25183,79.70175]]],[[[25.447625,80.40734],[27.407506,80.056406],[25.924651,79.517834],[23.024466,79.400012],[20.075188,79.566823],[19.897266,79.842362],[18.462264,79.85988],[17.368015,80.318896],[20.455992,80.598156],[21.907945,80.357679],[22.919253,80.657144],[25.447625,80.40734]]]]}},
    +{"type":"Feature","id":"NPL","properties":{"name":"Nepal"},"geometry":{"type":"Polygon","coordinates":[[[88.120441,27.876542],[88.043133,27.445819],[88.174804,26.810405],[88.060238,26.414615],[87.227472,26.397898],[86.024393,26.630985],[85.251779,26.726198],[84.675018,27.234901],[83.304249,27.364506],[81.999987,27.925479],[81.057203,28.416095],[80.088425,28.79447],[80.476721,29.729865],[81.111256,30.183481],[81.525804,30.422717],[82.327513,30.115268],[83.337115,29.463732],[83.898993,29.320226],[84.23458,28.839894],[85.011638,28.642774],[85.82332,28.203576],[86.954517,27.974262],[88.120441,27.876542]]]}},
    +{"type":"Feature","id":"NZL","properties":{"name":"New Zealand"},"geometry":{"type":"MultiPolygon","coordinates":[[[[173.020375,-40.919052],[173.247234,-41.331999],[173.958405,-40.926701],[174.247587,-41.349155],[174.248517,-41.770008],[173.876447,-42.233184],[173.22274,-42.970038],[172.711246,-43.372288],[173.080113,-43.853344],[172.308584,-43.865694],[171.452925,-44.242519],[171.185138,-44.897104],[170.616697,-45.908929],[169.831422,-46.355775],[169.332331,-46.641235],[168.411354,-46.619945],[167.763745,-46.290197],[166.676886,-46.219917],[166.509144,-45.852705],[167.046424,-45.110941],[168.303763,-44.123973],[168.949409,-43.935819],[169.667815,-43.555326],[170.52492,-43.031688],[171.12509,-42.512754],[171.569714,-41.767424],[171.948709,-41.514417],[172.097227,-40.956104],[172.79858,-40.493962],[173.020375,-40.919052]]],[[[174.612009,-36.156397],[175.336616,-37.209098],[175.357596,-36.526194],[175.808887,-36.798942],[175.95849,-37.555382],[176.763195,-37.881253],[177.438813,-37.961248],[178.010354,-37.579825],[178.517094,-37.695373],[178.274731,-38.582813],[177.97046,-39.166343],[177.206993,-39.145776],[176.939981,-39.449736],[177.032946,-39.879943],[176.885824,-40.065978],[176.508017,-40.604808],[176.01244,-41.289624],[175.239567,-41.688308],[175.067898,-41.425895],[174.650973,-41.281821],[175.22763,-40.459236],[174.900157,-39.908933],[173.824047,-39.508854],[173.852262,-39.146602],[174.574802,-38.797683],[174.743474,-38.027808],[174.697017,-37.381129],[174.292028,-36.711092],[174.319004,-36.534824],[173.840997,-36.121981],[173.054171,-35.237125],[172.636005,-34.529107],[173.007042,-34.450662],[173.551298,-35.006183],[174.32939,-35.265496],[174.612009,-36.156397]]]]}},
    +{"type":"Feature","id":"OMN","properties":{"name":"Oman"},"geometry":{"type":"MultiPolygon","coordinates":[[[[58.861141,21.114035],[58.487986,20.428986],[58.034318,20.481437],[57.826373,20.243002],[57.665762,19.736005],[57.7887,19.06757],[57.694391,18.94471],[57.234264,18.947991],[56.609651,18.574267],[56.512189,18.087113],[56.283521,17.876067],[55.661492,17.884128],[55.269939,17.632309],[55.2749,17.228354],[54.791002,16.950697],[54.239253,17.044981],[53.570508,16.707663],[53.108573,16.651051],[52.782184,17.349742],[52.00001,19.000003],[54.999982,19.999994],[55.666659,22.000001],[55.208341,22.70833],[55.234489,23.110993],[55.525841,23.524869],[55.528632,23.933604],[55.981214,24.130543],[55.804119,24.269604],[55.886233,24.920831],[56.396847,24.924732],[56.84514,24.241673],[57.403453,23.878594],[58.136948,23.747931],[58.729211,23.565668],[59.180502,22.992395],[59.450098,22.660271],[59.80806,22.533612],[59.806148,22.310525],[59.442191,21.714541],[59.282408,21.433886],[58.861141,21.114035]]],[[[56.391421,25.895991],[56.261042,25.714606],[56.070821,26.055464],[56.362017,26.395934],[56.485679,26.309118],[56.391421,25.895991]]]]}},
    +{"type":"Feature","id":"PAK","properties":{"name":"Pakistan"},"geometry":{"type":"Polygon","coordinates":[[[75.158028,37.133031],[75.896897,36.666806],[76.192848,35.898403],[77.837451,35.49401],[76.871722,34.653544],[75.757061,34.504923],[74.240203,34.748887],[73.749948,34.317699],[74.104294,33.441473],[74.451559,32.7649],[75.258642,32.271105],[74.405929,31.692639],[74.42138,30.979815],[73.450638,29.976413],[72.823752,28.961592],[71.777666,27.91318],[70.616496,27.989196],[69.514393,26.940966],[70.168927,26.491872],[70.282873,25.722229],[70.844699,25.215102],[71.04324,24.356524],[68.842599,24.359134],[68.176645,23.691965],[67.443667,23.944844],[67.145442,24.663611],[66.372828,25.425141],[64.530408,25.237039],[62.905701,25.218409],[61.497363,25.078237],[61.874187,26.239975],[63.316632,26.756532],[63.233898,27.217047],[62.755426,27.378923],[62.72783,28.259645],[61.771868,28.699334],[61.369309,29.303276],[60.874248,29.829239],[62.549857,29.318572],[63.550261,29.468331],[64.148002,29.340819],[64.350419,29.560031],[65.046862,29.472181],[66.346473,29.887943],[66.381458,30.738899],[66.938891,31.304911],[67.683394,31.303154],[67.792689,31.58293],[68.556932,31.71331],[68.926677,31.620189],[69.317764,31.901412],[69.262522,32.501944],[69.687147,33.105499],[70.323594,33.358533],[69.930543,34.02012],[70.881803,33.988856],[71.156773,34.348911],[71.115019,34.733126],[71.613076,35.153203],[71.498768,35.650563],[71.262348,36.074388],[71.846292,36.509942],[72.920025,36.720007],[74.067552,36.836176],[74.575893,37.020841],[75.158028,37.133031]]]}},
    +{"type":"Feature","id":"PAN","properties":{"name":"Panama"},"geometry":{"type":"Polygon","coordinates":[[[-77.881571,7.223771],[-78.214936,7.512255],[-78.429161,8.052041],[-78.182096,8.319182],[-78.435465,8.387705],[-78.622121,8.718124],[-79.120307,8.996092],[-79.557877,8.932375],[-79.760578,8.584515],[-80.164481,8.333316],[-80.382659,8.298409],[-80.480689,8.090308],[-80.00369,7.547524],[-80.276671,7.419754],[-80.421158,7.271572],[-80.886401,7.220541],[-81.059543,7.817921],[-81.189716,7.647906],[-81.519515,7.70661],[-81.721311,8.108963],[-82.131441,8.175393],[-82.390934,8.292362],[-82.820081,8.290864],[-82.850958,8.073823],[-82.965783,8.225028],[-82.913176,8.423517],[-82.829771,8.626295],[-82.868657,8.807266],[-82.719183,8.925709],[-82.927155,9.07433],[-82.932891,9.476812],[-82.546196,9.566135],[-82.187123,9.207449],[-82.207586,8.995575],[-81.808567,8.950617],[-81.714154,9.031955],[-81.439287,8.786234],[-80.947302,8.858504],[-80.521901,9.111072],[-79.9146,9.312765],[-79.573303,9.61161],[-79.021192,9.552931],[-79.05845,9.454565],[-78.500888,9.420459],[-78.055928,9.24773],[-77.729514,8.946844],[-77.353361,8.670505],[-77.474723,8.524286],[-77.242566,7.935278],[-77.431108,7.638061],[-77.753414,7.70984],[-77.881571,7.223771]]]}},
    +{"type":"Feature","id":"PER","properties":{"name":"Peru"},"geometry":{"type":"Polygon","coordinates":[[[-69.590424,-17.580012],[-69.858444,-18.092694],[-70.372572,-18.347975],[-71.37525,-17.773799],[-71.462041,-17.363488],[-73.44453,-16.359363],[-75.237883,-15.265683],[-76.009205,-14.649286],[-76.423469,-13.823187],[-76.259242,-13.535039],[-77.106192,-12.222716],[-78.092153,-10.377712],[-79.036953,-8.386568],[-79.44592,-7.930833],[-79.760578,-7.194341],[-80.537482,-6.541668],[-81.249996,-6.136834],[-80.926347,-5.690557],[-81.410943,-4.736765],[-81.09967,-4.036394],[-80.302561,-3.404856],[-80.184015,-3.821162],[-80.469295,-4.059287],[-80.442242,-4.425724],[-80.028908,-4.346091],[-79.624979,-4.454198],[-79.205289,-4.959129],[-78.639897,-4.547784],[-78.450684,-3.873097],[-77.837905,-3.003021],[-76.635394,-2.608678],[-75.544996,-1.56161],[-75.233723,-0.911417],[-75.373223,-0.152032],[-75.106625,-0.057205],[-74.441601,-0.53082],[-74.122395,-1.002833],[-73.659504,-1.260491],[-73.070392,-2.308954],[-72.325787,-2.434218],[-71.774761,-2.16979],[-71.413646,-2.342802],[-70.813476,-2.256865],[-70.047709,-2.725156],[-70.692682,-3.742872],[-70.394044,-3.766591],[-69.893635,-4.298187],[-70.794769,-4.251265],[-70.928843,-4.401591],[-71.748406,-4.593983],[-72.891928,-5.274561],[-72.964507,-5.741251],[-73.219711,-6.089189],[-73.120027,-6.629931],[-73.724487,-6.918595],[-73.723401,-7.340999],[-73.987235,-7.52383],[-73.571059,-8.424447],[-73.015383,-9.032833],[-73.226713,-9.462213],[-72.563033,-9.520194],[-72.184891,-10.053598],[-71.302412,-10.079436],[-70.481894,-9.490118],[-70.548686,-11.009147],[-70.093752,-11.123972],[-69.529678,-10.951734],[-68.66508,-12.5613],[-68.88008,-12.899729],[-68.929224,-13.602684],[-68.948887,-14.453639],[-69.339535,-14.953195],[-69.160347,-15.323974],[-69.389764,-15.660129],[-68.959635,-16.500698],[-69.590424,-17.580012]]]}},
    +{"type":"Feature","id":"PHL","properties":{"name":"Philippines"},"geometry":{"type":"MultiPolygon","coordinates":[[[[126.376814,8.414706],[126.478513,7.750354],[126.537424,7.189381],[126.196773,6.274294],[125.831421,7.293715],[125.363852,6.786485],[125.683161,6.049657],[125.396512,5.581003],[124.219788,6.161355],[123.93872,6.885136],[124.243662,7.36061],[123.610212,7.833527],[123.296071,7.418876],[122.825506,7.457375],[122.085499,6.899424],[121.919928,7.192119],[122.312359,8.034962],[122.942398,8.316237],[123.487688,8.69301],[123.841154,8.240324],[124.60147,8.514158],[124.764612,8.960409],[125.471391,8.986997],[125.412118,9.760335],[126.222714,9.286074],[126.306637,8.782487],[126.376814,8.414706]]],[[[123.982438,10.278779],[123.623183,9.950091],[123.309921,9.318269],[122.995883,9.022189],[122.380055,9.713361],[122.586089,9.981045],[122.837081,10.261157],[122.947411,10.881868],[123.49885,10.940624],[123.337774,10.267384],[124.077936,11.232726],[123.982438,10.278779]]],[[[118.504581,9.316383],[117.174275,8.3675],[117.664477,9.066889],[118.386914,9.6845],[118.987342,10.376292],[119.511496,11.369668],[119.689677,10.554291],[119.029458,10.003653],[118.504581,9.316383]]],[[[121.883548,11.891755],[122.483821,11.582187],[123.120217,11.58366],[123.100838,11.165934],[122.637714,10.741308],[122.00261,10.441017],[121.967367,10.905691],[122.03837,11.415841],[121.883548,11.891755]]],[[[125.502552,12.162695],[125.783465,11.046122],[125.011884,11.311455],[125.032761,10.975816],[125.277449,10.358722],[124.801819,10.134679],[124.760168,10.837995],[124.459101,10.88993],[124.302522,11.495371],[124.891013,11.415583],[124.87799,11.79419],[124.266762,12.557761],[125.227116,12.535721],[125.502552,12.162695]]],[[[121.527394,13.06959],[121.26219,12.20556],[120.833896,12.704496],[120.323436,13.466413],[121.180128,13.429697],[121.527394,13.06959]]],[[[121.321308,18.504065],[121.937601,18.218552],[122.246006,18.47895],[122.336957,18.224883],[122.174279,17.810283],[122.515654,17.093505],[122.252311,16.262444],[121.662786,15.931018],[121.50507,15.124814],[121.728829,14.328376],[122.258925,14.218202],[122.701276,14.336541],[123.950295,13.782131],[123.855107,13.237771],[124.181289,12.997527],[124.077419,12.536677],[123.298035,13.027526],[122.928652,13.55292],[122.671355,13.185836],[122.03465,13.784482],[121.126385,13.636687],[120.628637,13.857656],[120.679384,14.271016],[120.991819,14.525393],[120.693336,14.756671],[120.564145,14.396279],[120.070429,14.970869],[119.920929,15.406347],[119.883773,16.363704],[120.286488,16.034629],[120.390047,17.599081],[120.715867,18.505227],[121.321308,18.504065]]]]}},
    +{"type":"Feature","id":"PNG","properties":{"name":"Papua New Guinea"},"geometry":{"type":"MultiPolygon","coordinates":[[[[155.880026,-6.819997],[155.599991,-6.919991],[155.166994,-6.535931],[154.729192,-5.900828],[154.514114,-5.139118],[154.652504,-5.042431],[154.759991,-5.339984],[155.062918,-5.566792],[155.547746,-6.200655],[156.019965,-6.540014],[155.880026,-6.819997]]],[[[151.982796,-5.478063],[151.459107,-5.56028],[151.30139,-5.840728],[150.754447,-6.083763],[150.241197,-6.317754],[149.709963,-6.316513],[148.890065,-6.02604],[148.318937,-5.747142],[148.401826,-5.437756],[149.298412,-5.583742],[149.845562,-5.505503],[149.99625,-5.026101],[150.139756,-5.001348],[150.236908,-5.53222],[150.807467,-5.455842],[151.089672,-5.113693],[151.647881,-4.757074],[151.537862,-4.167807],[152.136792,-4.14879],[152.338743,-4.312966],[152.318693,-4.867661],[151.982796,-5.478063]]],[[[147.191874,-7.388024],[148.084636,-8.044108],[148.734105,-9.104664],[149.306835,-9.071436],[149.266631,-9.514406],[150.038728,-9.684318],[149.738798,-9.872937],[150.801628,-10.293687],[150.690575,-10.582713],[150.028393,-10.652476],[149.78231,-10.393267],[148.923138,-10.280923],[147.913018,-10.130441],[147.135443,-9.492444],[146.567881,-8.942555],[146.048481,-8.067414],[144.744168,-7.630128],[143.897088,-7.91533],[143.286376,-8.245491],[143.413913,-8.983069],[142.628431,-9.326821],[142.068259,-9.159596],[141.033852,-9.117893],[141.017057,-5.859022],[141.00021,-2.600151],[142.735247,-3.289153],[144.583971,-3.861418],[145.27318,-4.373738],[145.829786,-4.876498],[145.981922,-5.465609],[147.648073,-6.083659],[147.891108,-6.614015],[146.970905,-6.721657],[147.191874,-7.388024]]],[[[153.140038,-4.499983],[152.827292,-4.766427],[152.638673,-4.176127],[152.406026,-3.789743],[151.953237,-3.462062],[151.384279,-3.035422],[150.66205,-2.741486],[150.939965,-2.500002],[151.479984,-2.779985],[151.820015,-2.999972],[152.239989,-3.240009],[152.640017,-3.659983],[153.019994,-3.980015],[153.140038,-4.499983]]]]}},
    +{"type":"Feature","id":"POL","properties":{"name":"Poland"},"geometry":{"type":"Polygon","coordinates":[[[15.016996,51.106674],[14.607098,51.745188],[14.685026,52.089947],[14.4376,52.62485],[14.074521,52.981263],[14.353315,53.248171],[14.119686,53.757029],[14.8029,54.050706],[16.363477,54.513159],[17.622832,54.851536],[18.620859,54.682606],[18.696255,54.438719],[19.66064,54.426084],[20.892245,54.312525],[22.731099,54.327537],[23.243987,54.220567],[23.484128,53.912498],[23.527536,53.470122],[23.804935,53.089731],[23.799199,52.691099],[23.199494,52.486977],[23.508002,52.023647],[23.527071,51.578454],[24.029986,50.705407],[23.922757,50.424881],[23.426508,50.308506],[22.51845,49.476774],[22.776419,49.027395],[22.558138,49.085738],[21.607808,49.470107],[20.887955,49.328772],[20.415839,49.431453],[19.825023,49.217125],[19.320713,49.571574],[18.909575,49.435846],[18.853144,49.49623],[18.392914,49.988629],[17.649445,50.049038],[17.554567,50.362146],[16.868769,50.473974],[16.719476,50.215747],[16.176253,50.422607],[16.238627,50.697733],[15.490972,50.78473],[15.016996,51.106674]]]}},
    +{"type":"Feature","id":"PRI","properties":{"name":"Puerto Rico"},"geometry":{"type":"Polygon","coordinates":[[[-66.282434,18.514762],[-65.771303,18.426679],[-65.591004,18.228035],[-65.847164,17.975906],[-66.599934,17.981823],[-67.184162,17.946553],[-67.242428,18.37446],[-67.100679,18.520601],[-66.282434,18.514762]]]}},
    +{"type":"Feature","id":"PRK","properties":{"name":"North Korea"},"geometry":{"type":"Polygon","coordinates":[[[130.640016,42.395009],[130.780007,42.220007],[130.400031,42.280004],[129.965949,41.941368],[129.667362,41.601104],[129.705189,40.882828],[129.188115,40.661808],[129.0104,40.485436],[128.633368,40.189847],[127.967414,40.025413],[127.533436,39.75685],[127.50212,39.323931],[127.385434,39.213472],[127.783343,39.050898],[128.349716,38.612243],[128.205746,38.370397],[127.780035,38.304536],[127.073309,38.256115],[126.68372,37.804773],[126.237339,37.840378],[126.174759,37.749686],[125.689104,37.94001],[125.568439,37.752089],[125.27533,37.669071],[125.240087,37.857224],[124.981033,37.948821],[124.712161,38.108346],[124.985994,38.548474],[125.221949,38.665857],[125.132859,38.848559],[125.38659,39.387958],[125.321116,39.551385],[124.737482,39.660344],[124.265625,39.928493],[125.079942,40.569824],[126.182045,41.107336],[126.869083,41.816569],[127.343783,41.503152],[128.208433,41.466772],[128.052215,41.994285],[129.596669,42.424982],[129.994267,42.985387],[130.640016,42.395009]]]}},
    +{"type":"Feature","id":"PRT","properties":{"name":"Portugal"},"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.671946,42.134689],[-8.263857,42.280469],[-8.013175,41.790886],[-7.422513,41.792075],[-7.251309,41.918346],[-6.668606,41.883387],[-6.389088,41.381815],[-6.851127,41.111083],[-6.86402,40.330872],[-7.026413,40.184524],[-7.066592,39.711892],[-7.498632,39.629571],[-7.098037,39.030073],[-7.374092,38.373059],[-7.029281,38.075764],[-7.166508,37.803894],[-7.537105,37.428904],[-7.453726,37.097788],[-7.855613,36.838269],[-8.382816,36.97888],[-8.898857,36.868809],[-8.746101,37.651346],[-8.839998,38.266243],[-9.287464,38.358486],[-9.526571,38.737429],[-9.446989,39.392066],[-9.048305,39.755093],[-8.977353,40.159306],[-8.768684,40.760639],[-8.790853,41.184334],[-8.990789,41.543459],[-9.034818,41.880571]]]}},
    +{"type":"Feature","id":"PRY","properties":{"name":"Paraguay"},"geometry":{"type":"Polygon","coordinates":[[[-62.685057,-22.249029],[-62.291179,-21.051635],[-62.265961,-20.513735],[-61.786326,-19.633737],[-60.043565,-19.342747],[-59.115042,-19.356906],[-58.183471,-19.868399],[-58.166392,-20.176701],[-57.870674,-20.732688],[-57.937156,-22.090176],[-56.88151,-22.282154],[-56.473317,-22.0863],[-55.797958,-22.35693],[-55.610683,-22.655619],[-55.517639,-23.571998],[-55.400747,-23.956935],[-55.027902,-24.001274],[-54.652834,-23.839578],[-54.29296,-24.021014],[-54.293476,-24.5708],[-54.428946,-25.162185],[-54.625291,-25.739255],[-54.788795,-26.621786],[-55.695846,-27.387837],[-56.486702,-27.548499],[-57.60976,-27.395899],[-58.618174,-27.123719],[-57.63366,-25.603657],[-57.777217,-25.16234],[-58.807128,-24.771459],[-60.028966,-24.032796],[-60.846565,-23.880713],[-62.685057,-22.249029]]]}},
    +{"type":"Feature","id":"QAT","properties":{"name":"Qatar"},"geometry":{"type":"Polygon","coordinates":[[[50.810108,24.754743],[50.743911,25.482424],[51.013352,26.006992],[51.286462,26.114582],[51.589079,25.801113],[51.6067,25.21567],[51.389608,24.627386],[51.112415,24.556331],[50.810108,24.754743]]]}},
    +{"type":"Feature","id":"ROU","properties":{"name":"Romania"},"geometry":{"type":"Polygon","coordinates":[[[22.710531,47.882194],[23.142236,48.096341],[23.760958,47.985598],[24.402056,47.981878],[24.866317,47.737526],[25.207743,47.891056],[25.945941,47.987149],[26.19745,48.220881],[26.619337,48.220726],[26.924176,48.123264],[27.233873,47.826771],[27.551166,47.405117],[28.12803,46.810476],[28.160018,46.371563],[28.054443,45.944586],[28.233554,45.488283],[28.679779,45.304031],[29.149725,45.464925],[29.603289,45.293308],[29.626543,45.035391],[29.141612,44.82021],[28.837858,44.913874],[28.558081,43.707462],[27.970107,43.812468],[27.2424,44.175986],[26.065159,43.943494],[25.569272,43.688445],[24.100679,43.741051],[23.332302,43.897011],[22.944832,43.823785],[22.65715,44.234923],[22.474008,44.409228],[22.705726,44.578003],[22.459022,44.702517],[22.145088,44.478422],[21.562023,44.768947],[21.483526,45.18117],[20.874313,45.416375],[20.762175,45.734573],[20.220192,46.127469],[21.021952,46.316088],[21.626515,46.994238],[22.099768,47.672439],[22.710531,47.882194]]]}},
    +{"type":"Feature","id":"RUS","properties":{"name":"Russia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[143.648007,50.7476],[144.654148,48.976391],[143.173928,49.306551],[142.558668,47.861575],[143.533492,46.836728],[143.505277,46.137908],[142.747701,46.740765],[142.09203,45.966755],[141.906925,46.805929],[142.018443,47.780133],[141.904445,48.859189],[142.1358,49.615163],[142.179983,50.952342],[141.594076,51.935435],[141.682546,53.301966],[142.606934,53.762145],[142.209749,54.225476],[142.654786,54.365881],[142.914616,53.704578],[143.260848,52.74076],[143.235268,51.75666],[143.648007,50.7476]]],[[[22.731099,54.327537],[20.892245,54.312525],[19.66064,54.426084],[19.888481,54.86616],[21.268449,55.190482],[22.315724,55.015299],[22.757764,54.856574],[22.651052,54.582741],[22.731099,54.327537]]],[[[-175.01425,66.58435],[-174.33983,66.33556],[-174.57182,67.06219],[-171.85731,66.91308],[-169.89958,65.97724],[-170.89107,65.54139],[-172.53025,65.43791],[-172.555,64.46079],[-172.95533,64.25269],[-173.89184,64.2826],[-174.65392,64.63125],[-175.98353,64.92288],[-176.20716,65.35667],[-177.22266,65.52024],[-178.35993,65.39052],[-178.90332,65.74044],[-178.68611,66.11211],[-179.88377,65.87456],[-179.43268,65.40411],[-180,64.979709],[-180,68.963636],[-177.55,68.2],[-174.92825,67.20589],[-175.01425,66.58435]]],[[[180,70.832199],[178.903425,70.78114],[178.7253,71.0988],[180,71.515714],[180,70.832199]]],[[[-178.69378,70.89302],[-180,70.832199],[-180,71.515714],[-179.871875,71.55762],[-179.02433,71.55553],[-177.577945,71.26948],[-177.663575,71.13277],[-178.69378,70.89302]]],[[[143.60385,73.21244],[142.08763,73.20544],[140.038155,73.31692],[139.86312,73.36983],[140.81171,73.76506],[142.06207,73.85758],[143.48283,73.47525],[143.60385,73.21244]]],[[[150.73167,75.08406],[149.575925,74.68892],[147.977465,74.778355],[146.11919,75.17298],[146.358485,75.49682],[148.22223,75.345845],[150.73167,75.08406]]],[[[145.086285,75.562625],[144.3,74.82],[140.61381,74.84768],[138.95544,74.61148],[136.97439,75.26167],[137.51176,75.94917],[138.831075,76.13676],[141.471615,76.09289],[145.086285,75.562625]]],[[[57.535693,70.720464],[56.944979,70.632743],[53.677375,70.762658],[53.412017,71.206662],[51.601895,71.474759],[51.455754,72.014881],[52.478275,72.229442],[52.444169,72.774731],[54.427614,73.627548],[53.50829,73.749814],[55.902459,74.627486],[55.631933,75.081412],[57.868644,75.60939],[61.170044,76.251883],[64.498368,76.439055],[66.210977,76.809782],[68.15706,76.939697],[68.852211,76.544811],[68.180573,76.233642],[64.637326,75.737755],[61.583508,75.260885],[58.477082,74.309056],[56.986786,73.333044],[55.419336,72.371268],[55.622838,71.540595],[57.535693,70.720464]]],[[[106.97013,76.97419],[107.24,76.48],[108.1538,76.72335],[111.07726,76.71],[113.33151,76.22224],[114.13417,75.84764],[113.88539,75.32779],[112.77918,75.03186],[110.15125,74.47673],[109.4,74.18],[110.64,74.04],[112.11919,73.78774],[113.01954,73.97693],[113.52958,73.33505],[113.96881,73.59488],[115.56782,73.75285],[118.77633,73.58772],[119.02,73.12],[123.20066,72.97122],[123.25777,73.73503],[125.38,73.56],[126.97644,73.56549],[128.59126,73.03871],[129.05157,72.39872],[128.46,71.98],[129.71599,71.19304],[131.28858,70.78699],[132.2535,71.8363],[133.85766,71.38642],[135.56193,71.65525],[137.49755,71.34763],[138.23409,71.62803],[139.86983,71.48783],[139.14791,72.41619],[140.46817,72.84941],[149.5,72.2],[150.35118,71.60643],[152.9689,70.84222],[157.00688,71.03141],[158.99779,70.86672],[159.83031,70.45324],[159.70866,69.72198],[160.94053,69.43728],[162.27907,69.64204],[164.05248,69.66823],[165.94037,69.47199],[167.83567,69.58269],[169.57763,68.6938],[170.81688,69.01363],[170.0082,69.65276],[170.45345,70.09703],[173.64391,69.81743],[175.72403,69.87725],[178.6,69.4],[180,68.963636],[180,64.979709],[179.99281,64.97433],[178.7072,64.53493],[177.41128,64.60821],[178.313,64.07593],[178.90825,63.25197],[179.37034,62.98262],[179.48636,62.56894],[179.22825,62.3041],[177.3643,62.5219],[174.56929,61.76915],[173.68013,61.65261],[172.15,60.95],[170.6985,60.33618],[170.33085,59.88177],[168.90046,60.57355],[166.29498,59.78855],[165.84,60.16],[164.87674,59.7316],[163.53929,59.86871],[163.21711,59.21101],[162.01733,58.24328],[162.05297,57.83912],[163.19191,57.61503],[163.05794,56.15924],[162.12958,56.12219],[161.70146,55.28568],[162.11749,54.85514],[160.36877,54.34433],[160.02173,53.20257],[158.53094,52.95868],[158.23118,51.94269],[156.78979,51.01105],[156.42,51.7],[155.99182,53.15895],[155.43366,55.38103],[155.91442,56.76792],[156.75815,57.3647],[156.81035,57.83204],[158.36433,58.05575],[160.15064,59.31477],[161.87204,60.343],[163.66969,61.1409],[164.47355,62.55061],[163.25842,62.46627],[162.65791,61.6425],[160.12148,60.54423],[159.30232,61.77396],[156.72068,61.43442],[154.21806,59.75818],[155.04375,59.14495],[152.81185,58.88385],[151.26573,58.78089],[151.33815,59.50396],[149.78371,59.65573],[148.54481,59.16448],[145.48722,59.33637],[142.19782,59.03998],[138.95848,57.08805],[135.12619,54.72959],[136.70171,54.60355],[137.19342,53.97732],[138.1647,53.75501],[138.80463,54.25455],[139.90151,54.18968],[141.34531,53.08957],[141.37923,52.23877],[140.59742,51.23967],[140.51308,50.04553],[140.06193,48.44671],[138.55472,46.99965],[138.21971,46.30795],[136.86232,45.1435],[135.51535,43.989],[134.86939,43.39821],[133.53687,42.81147],[132.90627,42.79849],[132.27807,43.28456],[130.93587,42.55274],[130.78,42.22],[130.64,42.395],[130.633866,42.903015],[131.144688,42.92999],[131.288555,44.11152],[131.02519,44.96796],[131.883454,45.321162],[133.09712,45.14409],[133.769644,46.116927],[134.11235,47.21248],[134.50081,47.57845],[135.026311,48.47823],[133.373596,48.183442],[132.50669,47.78896],[130.98726,47.79013],[130.582293,48.729687],[129.397818,49.4406],[127.6574,49.76027],[127.287456,50.739797],[126.939157,51.353894],[126.564399,51.784255],[125.946349,52.792799],[125.068211,53.161045],[123.57147,53.4588],[122.245748,53.431726],[121.003085,53.251401],[120.177089,52.753886],[120.725789,52.516226],[120.7382,51.96411],[120.18208,51.64355],[119.27939,50.58292],[119.288461,50.142883],[117.879244,49.510983],[116.678801,49.888531],[115.485695,49.805177],[114.96211,50.140247],[114.362456,50.248303],[112.89774,49.543565],[111.581231,49.377968],[110.662011,49.130128],[109.402449,49.292961],[108.475167,49.282548],[107.868176,49.793705],[106.888804,50.274296],[105.886591,50.406019],[104.62158,50.27532],[103.676545,50.089966],[102.25589,50.51056],[102.06521,51.25991],[100.88948,51.516856],[99.981732,51.634006],[98.861491,52.047366],[97.82574,51.010995],[98.231762,50.422401],[97.25976,49.72605],[95.81402,49.97746],[94.815949,50.013433],[94.147566,50.480537],[93.10421,50.49529],[92.234712,50.802171],[90.713667,50.331812],[88.805567,49.470521],[87.751264,49.297198],[87.35997,49.214981],[86.829357,49.826675],[85.54127,49.692859],[85.11556,50.117303],[84.416377,50.3114],[83.935115,50.889246],[83.383004,51.069183],[81.945986,50.812196],[80.568447,51.388336],[80.03556,50.864751],[77.800916,53.404415],[76.525179,54.177003],[76.8911,54.490524],[74.38482,53.54685],[73.425679,53.48981],[73.508516,54.035617],[72.22415,54.376655],[71.180131,54.133285],[70.865267,55.169734],[69.068167,55.38525],[68.1691,54.970392],[65.66687,54.60125],[65.178534,54.354228],[61.4366,54.00625],[60.978066,53.664993],[61.699986,52.979996],[60.739993,52.719986],[60.927269,52.447548],[59.967534,51.96042],[61.588003,51.272659],[61.337424,50.79907],[59.932807,50.842194],[59.642282,50.545442],[58.36332,51.06364],[56.77798,51.04355],[55.71694,50.62171],[54.532878,51.02624],[52.328724,51.718652],[50.766648,51.692762],[48.702382,50.605128],[48.577841,49.87476],[47.54948,50.454698],[46.751596,49.356006],[47.043672,49.152039],[46.466446,48.394152],[47.31524,47.71585],[48.05725,47.74377],[48.694734,47.075628],[48.59325,46.56104],[49.10116,46.39933],[48.64541,45.80629],[47.67591,45.64149],[46.68201,44.6092],[47.59094,43.66016],[47.49252,42.98658],[48.58437,41.80888],[47.987283,41.405819],[47.815666,41.151416],[47.373315,41.219732],[46.686071,41.827137],[46.404951,41.860675],[45.7764,42.09244],[45.470279,42.502781],[44.537623,42.711993],[43.93121,42.55496],[43.75599,42.74083],[42.3944,43.2203],[40.92219,43.38215],[40.076965,43.553104],[39.955009,43.434998],[38.68,44.28],[37.53912,44.65721],[36.67546,45.24469],[37.40317,45.40451],[38.23295,46.24087],[37.67372,46.63657],[39.14767,47.04475],[39.1212,47.26336],[38.223538,47.10219],[38.255112,47.5464],[38.77057,47.82562],[39.738278,47.898937],[39.89562,48.23241],[39.67465,48.78382],[40.080789,49.30743],[40.06904,49.60105],[38.594988,49.926462],[38.010631,49.915662],[37.39346,50.383953],[36.626168,50.225591],[35.356116,50.577197],[35.37791,50.77394],[35.022183,51.207572],[34.224816,51.255993],[34.141978,51.566413],[34.391731,51.768882],[33.7527,52.335075],[32.715761,52.238465],[32.412058,52.288695],[32.15944,52.06125],[31.78597,52.10168],[31.540018,52.742052],[31.305201,53.073996],[31.49764,53.16743],[32.304519,53.132726],[32.693643,53.351421],[32.405599,53.618045],[31.731273,53.794029],[31.791424,53.974639],[31.384472,54.157056],[30.757534,54.811771],[30.971836,55.081548],[30.873909,55.550976],[29.896294,55.789463],[29.371572,55.670091],[29.229513,55.918344],[28.176709,56.16913],[27.855282,56.759326],[27.770016,57.244258],[27.288185,57.474528],[27.716686,57.791899],[27.42015,58.72457],[28.131699,59.300825],[27.98112,59.47537],[29.1177,60.02805],[28.07,60.50352],[30.211107,61.780028],[31.139991,62.357693],[31.516092,62.867687],[30.035872,63.552814],[30.444685,64.204453],[29.54443,64.948672],[30.21765,65.80598],[29.054589,66.944286],[29.977426,67.698297],[28.445944,68.364613],[28.59193,69.064777],[29.39955,69.15692],[31.10108,69.55811],[32.13272,69.90595],[33.77547,69.30142],[36.51396,69.06342],[40.29234,67.9324],[41.05987,67.45713],[41.12595,66.79158],[40.01583,66.26618],[38.38295,65.99953],[33.91871,66.75961],[33.18444,66.63253],[34.81477,65.90015],[34.878574,65.436213],[34.94391,64.41437],[36.23129,64.10945],[37.01273,63.84983],[37.14197,64.33471],[36.539579,64.76446],[37.17604,65.14322],[39.59345,64.52079],[40.4356,64.76446],[39.7626,65.49682],[42.09309,66.47623],[43.01604,66.41858],[43.94975,66.06908],[44.53226,66.75634],[43.69839,67.35245],[44.18795,67.95051],[43.45282,68.57079],[46.25,68.25],[46.82134,67.68997],[45.55517,67.56652],[45.56202,67.01005],[46.34915,66.66767],[47.89416,66.88455],[48.13876,67.52238],[50.22766,67.99867],[53.71743,68.85738],[54.47171,68.80815],[53.48582,68.20131],[54.72628,68.09702],[55.44268,68.43866],[57.31702,68.46628],[58.802,68.88082],[59.94142,68.27844],[61.07784,68.94069],[60.03,69.52],[60.55,69.85],[63.504,69.54739],[64.888115,69.234835],[68.51216,68.09233],[69.18068,68.61563],[68.16444,69.14436],[68.13522,69.35649],[66.93008,69.45461],[67.25976,69.92873],[66.72492,70.70889],[66.69466,71.02897],[68.54006,71.9345],[69.19636,72.84336],[69.94,73.04],[72.58754,72.77629],[72.79603,72.22006],[71.84811,71.40898],[72.47011,71.09019],[72.79188,70.39114],[72.5647,69.02085],[73.66787,68.4079],[73.2387,67.7404],[71.28,66.32],[72.42301,66.17267],[72.82077,66.53267],[73.92099,66.78946],[74.18651,67.28429],[75.052,67.76047],[74.46926,68.32899],[74.93584,68.98918],[73.84236,69.07146],[73.60187,69.62763],[74.3998,70.63175],[73.1011,71.44717],[74.89082,72.12119],[74.65926,72.83227],[75.15801,72.85497],[75.68351,72.30056],[75.28898,71.33556],[76.35911,71.15287],[75.90313,71.87401],[77.57665,72.26717],[79.65202,72.32011],[81.5,71.75],[80.61071,72.58285],[80.51109,73.6482],[82.25,73.85],[84.65526,73.80591],[86.8223,73.93688],[86.00956,74.45967],[87.16682,75.11643],[88.31571,75.14393],[90.26,75.64],[92.90058,75.77333],[93.23421,76.0472],[95.86,76.14],[96.67821,75.91548],[98.92254,76.44689],[100.75967,76.43028],[101.03532,76.86189],[101.99084,77.28754],[104.3516,77.69792],[106.06664,77.37389],[104.705,77.1274],[106.97013,76.97419]]],[[[105.07547,78.30689],[99.43814,77.921],[101.2649,79.23399],[102.08635,79.34641],[102.837815,79.28129],[105.37243,78.71334],[105.07547,78.30689]]],[[[51.136187,80.54728],[49.793685,80.415428],[48.894411,80.339567],[48.754937,80.175468],[47.586119,80.010181],[46.502826,80.247247],[47.072455,80.559424],[44.846958,80.58981],[46.799139,80.771918],[48.318477,80.78401],[48.522806,80.514569],[49.09719,80.753986],[50.039768,80.918885],[51.522933,80.699726],[51.136187,80.54728]]],[[[99.93976,78.88094],[97.75794,78.7562],[94.97259,79.044745],[93.31288,79.4265],[92.5454,80.14379],[91.18107,80.34146],[93.77766,81.0246],[95.940895,81.2504],[97.88385,80.746975],[100.186655,79.780135],[99.93976,78.88094]]]]}},
    +{"type":"Feature","id":"RWA","properties":{"name":"Rwanda"},"geometry":{"type":"Polygon","coordinates":[[[30.419105,-1.134659],[30.816135,-1.698914],[30.758309,-2.28725],[30.469696,-2.413858],[29.938359,-2.348487],[29.632176,-2.917858],[29.024926,-2.839258],[29.117479,-2.292211],[29.254835,-2.21511],[29.291887,-1.620056],[29.579466,-1.341313],[29.821519,-1.443322],[30.419105,-1.134659]]]}},
    +{"type":"Feature","id":"ESH","properties":{"name":"Western Sahara"},"geometry":{"type":"Polygon","coordinates":[[[-8.794884,27.120696],[-8.817828,27.656426],[-8.66559,27.656426],[-8.665124,27.589479],[-8.6844,27.395744],[-8.687294,25.881056],[-11.969419,25.933353],[-11.937224,23.374594],[-12.874222,23.284832],[-13.118754,22.77122],[-12.929102,21.327071],[-16.845194,21.333323],[-17.063423,20.999752],[-17.020428,21.42231],[-17.002962,21.420734],[-14.750955,21.5006],[-14.630833,21.86094],[-14.221168,22.310163],[-13.89111,23.691009],[-12.500963,24.770116],[-12.030759,26.030866],[-11.71822,26.104092],[-11.392555,26.883424],[-10.551263,26.990808],[-10.189424,26.860945],[-9.735343,26.860945],[-9.413037,27.088476],[-8.794884,27.120696]]]}},
    +{"type":"Feature","id":"SAU","properties":{"name":"Saudi Arabia"},"geometry":{"type":"Polygon","coordinates":[[[42.779332,16.347891],[42.649573,16.774635],[42.347989,17.075806],[42.270888,17.474722],[41.754382,17.833046],[41.221391,18.6716],[40.939341,19.486485],[40.247652,20.174635],[39.801685,20.338862],[39.139399,21.291905],[39.023696,21.986875],[39.066329,22.579656],[38.492772,23.688451],[38.02386,24.078686],[37.483635,24.285495],[37.154818,24.858483],[37.209491,25.084542],[36.931627,25.602959],[36.639604,25.826228],[36.249137,26.570136],[35.640182,27.37652],[35.130187,28.063352],[34.632336,28.058546],[34.787779,28.607427],[34.83222,28.957483],[34.956037,29.356555],[36.068941,29.197495],[36.501214,29.505254],[36.740528,29.865283],[37.503582,30.003776],[37.66812,30.338665],[37.998849,30.5085],[37.002166,31.508413],[39.004886,32.010217],[39.195468,32.161009],[40.399994,31.889992],[41.889981,31.190009],[44.709499,29.178891],[46.568713,29.099025],[47.459822,29.002519],[47.708851,28.526063],[48.416094,28.552004],[48.807595,27.689628],[49.299554,27.461218],[49.470914,27.109999],[50.152422,26.689663],[50.212935,26.277027],[50.113303,25.943972],[50.239859,25.60805],[50.527387,25.327808],[50.660557,24.999896],[50.810108,24.754743],[51.112415,24.556331],[51.389608,24.627386],[51.579519,24.245497],[51.617708,24.014219],[52.000733,23.001154],[55.006803,22.496948],[55.208341,22.70833],[55.666659,22.000001],[54.999982,19.999994],[52.00001,19.000003],[49.116672,18.616668],[48.183344,18.166669],[47.466695,17.116682],[47.000005,16.949999],[46.749994,17.283338],[46.366659,17.233315],[45.399999,17.333335],[45.216651,17.433329],[44.062613,17.410359],[43.791519,17.319977],[43.380794,17.579987],[43.115798,17.08844],[43.218375,16.66689],[42.779332,16.347891]]]}},
    +{"type":"Feature","id":"SDN","properties":{"name":"Sudan"},"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.824963,9.484061],[33.842131,9.981915],[33.721959,10.325262],[33.206938,10.720112],[33.086766,11.441141],[33.206938,12.179338],[32.743419,12.248008],[32.67475,12.024832],[32.073892,11.97333],[32.314235,11.681484],[32.400072,11.080626],[31.850716,10.531271],[31.352862,9.810241],[30.837841,9.707237],[29.996639,10.290927],[29.618957,10.084919],[29.515953,9.793074],[29.000932,9.604232],[28.966597,9.398224],[27.97089,9.398224],[27.833551,9.604232],[27.112521,9.638567],[26.752006,9.466893],[26.477328,9.55273],[25.962307,10.136421],[25.790633,10.411099],[25.069604,10.27376],[24.794926,9.810241],[24.537415,8.917538],[24.194068,8.728696],[23.88698,8.61973],[23.805813,8.666319],[23.459013,8.954286],[23.394779,9.265068],[23.55725,9.681218],[23.554304,10.089255],[22.977544,10.714463],[22.864165,11.142395],[22.87622,11.38461],[22.50869,11.67936],[22.49762,12.26024],[22.28801,12.64605],[21.93681,12.58818],[22.03759,12.95546],[22.29658,13.37232],[22.18329,13.78648],[22.51202,14.09318],[22.30351,14.32682],[22.56795,14.94429],[23.02459,15.68072],[23.88689,15.61084],[23.83766,19.58047],[23.85,20],[25,20.00304],[25,22],[29.02,22],[32.9,22],[36.86623,22],[37.18872,21.01885],[36.96941,20.83744],[37.1147,19.80796],[37.48179,18.61409],[37.86276,18.36786],[38.41009,17.998307],[37.904,17.42754],[37.16747,17.26314],[36.85253,16.95655],[36.75389,16.29186],[36.32322,14.82249],[36.42951,14.42211],[36.27022,13.56333],[35.86363,12.57828],[35.26049,12.08286],[34.83163,11.31896],[34.73115,10.91017],[34.25745,10.63009],[33.96162,9.58358],[33.963393,9.464285]]]}},
    +{"type":"Feature","id":"SDS","properties":{"name":"South Sudan"},"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.97498,8.68456],[33.8255,8.37916],[33.2948,8.35458],[32.95418,7.78497],[33.56829,7.71334],[34.0751,7.22595],[34.25032,6.82607],[34.70702,6.59422],[35.298007,5.506],[34.620196,4.847123],[34.005,4.249885],[33.39,3.79],[32.68642,3.79232],[31.88145,3.55827],[31.24556,3.7819],[30.83385,3.50917],[29.95349,4.1737],[29.715995,4.600805],[29.159078,4.389267],[28.696678,4.455077],[28.428994,4.287155],[27.979977,4.408413],[27.374226,5.233944],[27.213409,5.550953],[26.465909,5.946717],[26.213418,6.546603],[25.796648,6.979316],[25.124131,7.500085],[25.114932,7.825104],[24.567369,8.229188],[23.88698,8.61973],[24.194068,8.728696],[24.537415,8.917538],[24.794926,9.810241],[25.069604,10.27376],[25.790633,10.411099],[25.962307,10.136421],[26.477328,9.55273],[26.752006,9.466893],[27.112521,9.638567],[27.833551,9.604232],[27.97089,9.398224],[28.966597,9.398224],[29.000932,9.604232],[29.515953,9.793074],[29.618957,10.084919],[29.996639,10.290927],[30.837841,9.707237],[31.352862,9.810241],[31.850716,10.531271],[32.400072,11.080626],[32.314235,11.681484],[32.073892,11.97333],[32.67475,12.024832],[32.743419,12.248008],[33.206938,12.179338],[33.086766,11.441141],[33.206938,10.720112],[33.721959,10.325262],[33.842131,9.981915],[33.824963,9.484061],[33.963393,9.464285]]]}},
    +{"type":"Feature","id":"SEN","properties":{"name":"Senegal"},"geometry":{"type":"Polygon","coordinates":[[[-16.713729,13.594959],[-17.126107,14.373516],[-17.625043,14.729541],[-17.185173,14.919477],[-16.700706,15.621527],[-16.463098,16.135036],[-16.12069,16.455663],[-15.623666,16.369337],[-15.135737,16.587282],[-14.577348,16.598264],[-14.099521,16.304302],[-13.435738,16.039383],[-12.830658,15.303692],[-12.17075,14.616834],[-12.124887,13.994727],[-11.927716,13.422075],[-11.553398,13.141214],[-11.467899,12.754519],[-11.513943,12.442988],[-11.658301,12.386583],[-12.203565,12.465648],[-12.278599,12.35444],[-12.499051,12.33209],[-13.217818,12.575874],[-13.700476,12.586183],[-15.548477,12.62817],[-15.816574,12.515567],[-16.147717,12.547762],[-16.677452,12.384852],[-16.841525,13.151394],[-15.931296,13.130284],[-15.691001,13.270353],[-15.511813,13.27857],[-15.141163,13.509512],[-14.712197,13.298207],[-14.277702,13.280585],[-13.844963,13.505042],[-14.046992,13.794068],[-14.376714,13.62568],[-14.687031,13.630357],[-15.081735,13.876492],[-15.39877,13.860369],[-15.624596,13.623587],[-16.713729,13.594959]]]}},
    +{"type":"Feature","id":"SLB","properties":{"name":"Solomon Islands"},"geometry":{"type":"MultiPolygon","coordinates":[[[[162.119025,-10.482719],[162.398646,-10.826367],[161.700032,-10.820011],[161.319797,-10.204751],[161.917383,-10.446701],[162.119025,-10.482719]]],[[[160.852229,-9.872937],[160.462588,-9.89521],[159.849447,-9.794027],[159.640003,-9.63998],[159.702945,-9.24295],[160.362956,-9.400304],[160.688518,-9.610162],[160.852229,-9.872937]]],[[[161.679982,-9.599982],[161.529397,-9.784312],[160.788253,-8.917543],[160.579997,-8.320009],[160.920028,-8.320009],[161.280006,-9.120011],[161.679982,-9.599982]]],[[[159.875027,-8.33732],[159.917402,-8.53829],[159.133677,-8.114181],[158.586114,-7.754824],[158.21115,-7.421872],[158.359978,-7.320018],[158.820001,-7.560003],[159.640003,-8.020027],[159.875027,-8.33732]]],[[[157.538426,-7.34782],[157.33942,-7.404767],[156.90203,-7.176874],[156.491358,-6.765943],[156.542828,-6.599338],[157.14,-7.021638],[157.538426,-7.34782]]]]}},
    +{"type":"Feature","id":"SLE","properties":{"name":"Sierra Leone"},"geometry":{"type":"Polygon","coordinates":[[[-11.438779,6.785917],[-11.708195,6.860098],[-12.428099,7.262942],[-12.949049,7.798646],[-13.124025,8.163946],[-13.24655,8.903049],[-12.711958,9.342712],[-12.596719,9.620188],[-12.425929,9.835834],[-12.150338,9.858572],[-11.917277,10.046984],[-11.117481,10.045873],[-10.839152,9.688246],[-10.622395,9.26791],[-10.65477,8.977178],[-10.494315,8.715541],[-10.505477,8.348896],[-10.230094,8.406206],[-10.695595,7.939464],[-11.146704,7.396706],[-11.199802,7.105846],[-11.438779,6.785917]]]}},
    +{"type":"Feature","id":"SLV","properties":{"name":"El Salvador"},"geometry":{"type":"Polygon","coordinates":[[[-87.793111,13.38448],[-87.904112,13.149017],[-88.483302,13.163951],[-88.843228,13.259734],[-89.256743,13.458533],[-89.812394,13.520622],[-90.095555,13.735338],[-90.064678,13.88197],[-89.721934,14.134228],[-89.534219,14.244816],[-89.587343,14.362586],[-89.353326,14.424133],[-89.058512,14.340029],[-88.843073,14.140507],[-88.541231,13.980155],[-88.503998,13.845486],[-88.065343,13.964626],[-87.859515,13.893312],[-87.723503,13.78505],[-87.793111,13.38448]]]}},
    +{"type":"Feature","id":"SOL","properties":{"name":"Somaliland"},"geometry":{"type":"Polygon","coordinates":[[[48.93813,9.451749],[48.486736,8.837626],[47.78942,8.003],[46.948328,7.996877],[43.67875,9.18358],[43.296975,9.540477],[42.92812,10.02194],[42.55876,10.57258],[42.776852,10.926879],[43.145305,11.46204],[43.47066,11.27771],[43.666668,10.864169],[44.117804,10.445538],[44.614259,10.442205],[45.556941,10.698029],[46.645401,10.816549],[47.525658,11.127228],[48.021596,11.193064],[48.378784,11.375482],[48.948206,11.410622],[48.942005,11.394266],[48.938491,10.982327],[48.938233,9.9735],[48.93813,9.451749]]]}},
    +{"type":"Feature","id":"SOM","properties":{"name":"Somalia"},"geometry":{"type":"Polygon","coordinates":[[[49.72862,11.5789],[50.25878,11.67957],[50.73202,12.0219],[51.1112,12.02464],[51.13387,11.74815],[51.04153,11.16651],[51.04531,10.6409],[50.83418,10.27972],[50.55239,9.19874],[50.07092,8.08173],[49.4527,6.80466],[48.59455,5.33911],[47.74079,4.2194],[46.56476,2.85529],[45.56399,2.04576],[44.06815,1.05283],[43.13597,0.2922],[42.04157,-0.91916],[41.81095,-1.44647],[41.58513,-1.68325],[40.993,-0.85829],[40.98105,2.78452],[41.855083,3.918912],[42.12861,4.23413],[42.76967,4.25259],[43.66087,4.95755],[44.9636,5.00162],[47.78942,8.003],[48.486736,8.837626],[48.93813,9.451749],[48.938233,9.9735],[48.938491,10.982327],[48.942005,11.394266],[48.948205,11.410617],[49.26776,11.43033],[49.72862,11.5789]]]}},
    +{"type":"Feature","id":"SRB","properties":{"name":"Republic of Serbia"},"geometry":{"type":"Polygon","coordinates":[[[20.874313,45.416375],[21.483526,45.18117],[21.562023,44.768947],[22.145088,44.478422],[22.459022,44.702517],[22.705726,44.578003],[22.474008,44.409228],[22.65715,44.234923],[22.410446,44.008063],[22.500157,43.642814],[22.986019,43.211161],[22.604801,42.898519],[22.436595,42.580321],[22.545012,42.461362],[22.380526,42.32026],[21.91708,42.30364],[21.576636,42.245224],[21.54332,42.32025],[21.66292,42.43922],[21.77505,42.6827],[21.63302,42.67717],[21.43866,42.86255],[21.27421,42.90959],[21.143395,43.068685],[20.95651,43.13094],[20.81448,43.27205],[20.63508,43.21671],[20.49679,42.88469],[20.25758,42.81275],[20.3398,42.89852],[19.95857,43.10604],[19.63,43.21378],[19.48389,43.35229],[19.21852,43.52384],[19.454,43.5681],[19.59976,44.03847],[19.11761,44.42307],[19.36803,44.863],[19.00548,44.86023],[19.390476,45.236516],[19.072769,45.521511],[18.82982,45.90888],[19.596045,46.17173],[20.220192,46.127469],[20.762175,45.734573],[20.874313,45.416375]]]}},
    +{"type":"Feature","id":"SUR","properties":{"name":"Suriname"},"geometry":{"type":"Polygon","coordinates":[[[-57.147436,5.97315],[-55.949318,5.772878],[-55.84178,5.953125],[-55.03325,6.025291],[-53.958045,5.756548],[-54.478633,4.896756],[-54.399542,4.212611],[-54.006931,3.620038],[-54.181726,3.18978],[-54.269705,2.732392],[-54.524754,2.311849],[-55.097587,2.523748],[-55.569755,2.421506],[-55.973322,2.510364],[-56.073342,2.220795],[-55.9056,2.021996],[-55.995698,1.817667],[-56.539386,1.899523],[-57.150098,2.768927],[-57.281433,3.333492],[-57.601569,3.334655],[-58.044694,4.060864],[-57.86021,4.576801],[-57.914289,4.812626],[-57.307246,5.073567],[-57.147436,5.97315]]]}},
    +{"type":"Feature","id":"SVK","properties":{"name":"Slovakia"},"geometry":{"type":"Polygon","coordinates":[[[18.853144,49.49623],[18.909575,49.435846],[19.320713,49.571574],[19.825023,49.217125],[20.415839,49.431453],[20.887955,49.328772],[21.607808,49.470107],[22.558138,49.085738],[22.280842,48.825392],[22.085608,48.422264],[21.872236,48.319971],[20.801294,48.623854],[20.473562,48.56285],[20.239054,48.327567],[19.769471,48.202691],[19.661364,48.266615],[19.174365,48.111379],[18.777025,48.081768],[18.696513,47.880954],[17.857133,47.758429],[17.488473,47.867466],[16.979667,48.123497],[16.879983,48.470013],[16.960288,48.596982],[17.101985,48.816969],[17.545007,48.800019],[17.886485,48.903475],[17.913512,48.996493],[18.104973,49.043983],[18.170498,49.271515],[18.399994,49.315001],[18.554971,49.495015],[18.853144,49.49623]]]}},
    +{"type":"Feature","id":"SVN","properties":{"name":"Slovenia"},"geometry":{"type":"Polygon","coordinates":[[[13.806475,46.509306],[14.632472,46.431817],[15.137092,46.658703],[16.011664,46.683611],[16.202298,46.852386],[16.370505,46.841327],[16.564808,46.503751],[15.768733,46.238108],[15.67153,45.834154],[15.323954,45.731783],[15.327675,45.452316],[14.935244,45.471695],[14.595109,45.634941],[14.411968,45.466166],[13.71506,45.500324],[13.93763,45.591016],[13.69811,46.016778],[13.806475,46.509306]]]}},
    +{"type":"Feature","id":"SWE","properties":{"name":"Sweden"},"geometry":{"type":"Polygon","coordinates":[[[22.183173,65.723741],[21.213517,65.026005],[21.369631,64.413588],[19.778876,63.609554],[17.847779,62.7494],[17.119555,61.341166],[17.831346,60.636583],[18.787722,60.081914],[17.869225,58.953766],[16.829185,58.719827],[16.44771,57.041118],[15.879786,56.104302],[14.666681,56.200885],[14.100721,55.407781],[12.942911,55.361737],[12.625101,56.30708],[11.787942,57.441817],[11.027369,58.856149],[11.468272,59.432393],[12.300366,60.117933],[12.631147,61.293572],[11.992064,61.800362],[11.930569,63.128318],[12.579935,64.066219],[13.571916,64.049114],[13.919905,64.445421],[13.55569,64.787028],[15.108411,66.193867],[16.108712,67.302456],[16.768879,68.013937],[17.729182,68.010552],[17.993868,68.567391],[19.87856,68.407194],[20.025269,69.065139],[20.645593,69.106247],[21.978535,68.616846],[23.539473,67.936009],[23.56588,66.396051],[23.903379,66.006927],[22.183173,65.723741]]]}},
    +{"type":"Feature","id":"SWZ","properties":{"name":"Swaziland"},"geometry":{"type":"Polygon","coordinates":[[[32.071665,-26.73382],[31.86806,-27.177927],[31.282773,-27.285879],[30.685962,-26.743845],[30.676609,-26.398078],[30.949667,-26.022649],[31.04408,-25.731452],[31.333158,-25.660191],[31.837778,-25.843332],[31.985779,-26.29178],[32.071665,-26.73382]]]}},
    +{"type":"Feature","id":"SYR","properties":{"name":"Syria"},"geometry":{"type":"Polygon","coordinates":[[[38.792341,33.378686],[36.834062,32.312938],[35.719918,32.709192],[35.700798,32.716014],[35.836397,32.868123],[35.821101,33.277426],[36.06646,33.824912],[36.61175,34.201789],[36.448194,34.593935],[35.998403,34.644914],[35.905023,35.410009],[36.149763,35.821535],[36.41755,36.040617],[36.685389,36.259699],[36.739494,36.81752],[37.066761,36.623036],[38.167727,36.90121],[38.699891,36.712927],[39.52258,36.716054],[40.673259,37.091276],[41.212089,37.074352],[42.349591,37.229873],[41.837064,36.605854],[41.289707,36.358815],[41.383965,35.628317],[41.006159,34.419372],[38.792341,33.378686]]]}},
    +{"type":"Feature","id":"TCD","properties":{"name":"Chad"},"geometry":{"type":"Polygon","coordinates":[[[14.495787,12.859396],[14.595781,13.330427],[13.954477,13.353449],[13.956699,13.996691],[13.540394,14.367134],[13.97217,15.68437],[15.247731,16.627306],[15.300441,17.92795],[15.685741,19.95718],[15.903247,20.387619],[15.487148,20.730415],[15.47106,21.04845],[15.096888,21.308519],[14.8513,22.86295],[15.86085,23.40972],[19.84926,21.49509],[23.83766,19.58047],[23.88689,15.61084],[23.02459,15.68072],[22.56795,14.94429],[22.30351,14.32682],[22.51202,14.09318],[22.18329,13.78648],[22.29658,13.37232],[22.03759,12.95546],[21.93681,12.58818],[22.28801,12.64605],[22.49762,12.26024],[22.50869,11.67936],[22.87622,11.38461],[22.864165,11.142395],[22.231129,10.971889],[21.723822,10.567056],[21.000868,9.475985],[20.059685,9.012706],[19.094008,9.074847],[18.81201,8.982915],[18.911022,8.630895],[18.389555,8.281304],[17.96493,7.890914],[16.705988,7.508328],[16.456185,7.734774],[16.290562,7.754307],[16.106232,7.497088],[15.27946,7.421925],[15.436092,7.692812],[15.120866,8.38215],[14.979996,8.796104],[14.544467,8.965861],[13.954218,9.549495],[14.171466,10.021378],[14.627201,9.920919],[14.909354,9.992129],[15.467873,9.982337],[14.923565,10.891325],[14.960152,11.555574],[14.89336,12.21905],[14.495787,12.859396]]]}},
    +{"type":"Feature","id":"TGO","properties":{"name":"Togo"},"geometry":{"type":"Polygon","coordinates":[[[1.865241,6.142158],[1.060122,5.928837],[0.836931,6.279979],[0.570384,6.914359],[0.490957,7.411744],[0.712029,8.312465],[0.461192,8.677223],[0.365901,9.465004],[0.36758,10.191213],[-0.049785,10.706918],[0.023803,11.018682],[0.899563,10.997339],[0.772336,10.470808],[1.077795,10.175607],[1.425061,9.825395],[1.463043,9.334624],[1.664478,9.12859],[1.618951,6.832038],[1.865241,6.142158]]]}},
    +{"type":"Feature","id":"THA","properties":{"name":"Thailand"},"geometry":{"type":"Polygon","coordinates":[[[102.584932,12.186595],[101.687158,12.64574],[100.83181,12.627085],[100.978467,13.412722],[100.097797,13.406856],[100.018733,12.307001],[99.478921,10.846367],[99.153772,9.963061],[99.222399,9.239255],[99.873832,9.207862],[100.279647,8.295153],[100.459274,7.429573],[101.017328,6.856869],[101.623079,6.740622],[102.141187,6.221636],[101.814282,5.810808],[101.154219,5.691384],[101.075516,6.204867],[100.259596,6.642825],[100.085757,6.464489],[99.690691,6.848213],[99.519642,7.343454],[98.988253,7.907993],[98.503786,8.382305],[98.339662,7.794512],[98.150009,8.350007],[98.25915,8.973923],[98.553551,9.93296],[99.038121,10.960546],[99.587286,11.892763],[99.196354,12.804748],[99.212012,13.269294],[99.097755,13.827503],[98.430819,14.622028],[98.192074,15.123703],[98.537376,15.308497],[98.903348,16.177824],[98.493761,16.837836],[97.859123,17.567946],[97.375896,18.445438],[97.797783,18.62708],[98.253724,19.708203],[98.959676,19.752981],[99.543309,20.186598],[100.115988,20.41785],[100.548881,20.109238],[100.606294,19.508344],[101.282015,19.462585],[101.035931,18.408928],[101.059548,17.512497],[102.113592,18.109102],[102.413005,17.932782],[102.998706,17.961695],[103.200192,18.309632],[103.956477,18.240954],[104.716947,17.428859],[104.779321,16.441865],[105.589039,15.570316],[105.544338,14.723934],[105.218777,14.273212],[104.281418,14.416743],[102.988422,14.225721],[102.348099,13.394247],[102.584932,12.186595]]]}},
    +{"type":"Feature","id":"TJK","properties":{"name":"Tajikistan"},"geometry":{"type":"Polygon","coordinates":[[[71.014198,40.244366],[70.648019,39.935754],[69.55961,40.103211],[69.464887,39.526683],[70.549162,39.604198],[71.784694,39.279463],[73.675379,39.431237],[73.928852,38.505815],[74.257514,38.606507],[74.864816,38.378846],[74.829986,37.990007],[74.980002,37.41999],[73.948696,37.421566],[73.260056,37.495257],[72.63689,37.047558],[72.193041,36.948288],[71.844638,36.738171],[71.448693,37.065645],[71.541918,37.905774],[71.239404,37.953265],[71.348131,38.258905],[70.806821,38.486282],[70.376304,38.138396],[70.270574,37.735165],[70.116578,37.588223],[69.518785,37.608997],[69.196273,37.151144],[68.859446,37.344336],[68.135562,37.023115],[67.83,37.144994],[68.392033,38.157025],[68.176025,38.901553],[67.44222,39.140144],[67.701429,39.580478],[68.536416,39.533453],[69.011633,40.086158],[69.329495,40.727824],[70.666622,40.960213],[70.45816,40.496495],[70.601407,40.218527],[71.014198,40.244366]]]}},
    +{"type":"Feature","id":"TKM","properties":{"name":"Turkmenistan"},"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[61.123071,36.491597],[60.377638,36.527383],[59.234762,37.412988],[58.436154,37.522309],[57.330434,38.029229],[56.619366,38.121394],[56.180375,37.935127],[55.511578,37.964117],[54.800304,37.392421],[53.921598,37.198918],[53.735511,37.906136],[53.880929,38.952093],[53.101028,39.290574],[53.357808,39.975286],[52.693973,40.033629],[52.915251,40.876523],[53.858139,40.631034],[54.736845,40.951015],[54.008311,41.551211],[53.721713,42.123191],[52.91675,41.868117],[52.814689,41.135371],[52.50246,41.783316],[52.944293,42.116034],[54.079418,42.324109],[54.755345,42.043971],[55.455251,41.259859],[55.968191,41.308642],[57.096391,41.32231],[56.932215,41.826026],[57.78653,42.170553],[58.629011,42.751551],[59.976422,42.223082],[60.083341,41.425146],[60.465953,41.220327],[61.547179,41.26637],[61.882714,41.084857],[62.37426,40.053886],[63.518015,39.363257],[64.170223,38.892407],[65.215999,38.402695],[66.54615,37.974685],[66.518607,37.362784],[66.217385,37.39379],[65.745631,37.661164],[65.588948,37.305217],[64.746105,37.111818],[64.546479,36.312073],[63.982896,36.007957],[63.193538,35.857166],[62.984662,35.404041],[62.230651,35.270664],[61.210817,35.650072]]]}},
    +{"type":"Feature","id":"TLS","properties":{"name":"East Timor"},"geometry":{"type":"Polygon","coordinates":[[[124.968682,-8.89279],[125.086246,-8.656887],[125.947072,-8.432095],[126.644704,-8.398247],[126.957243,-8.273345],[127.335928,-8.397317],[126.967992,-8.668256],[125.925885,-9.106007],[125.08852,-9.393173],[125.07002,-9.089987],[124.968682,-8.89279]]]}},
    +{"type":"Feature","id":"TTO","properties":{"name":"Trinidad and Tobago"},"geometry":{"type":"Polygon","coordinates":[[[-61.68,10.76],[-61.105,10.89],[-60.895,10.855],[-60.935,10.11],[-61.77,10],[-61.95,10.09],[-61.66,10.365],[-61.68,10.76]]]}},
    +{"type":"Feature","id":"TUN","properties":{"name":"Tunisia"},"geometry":{"type":"Polygon","coordinates":[[[9.48214,30.307556],[9.055603,32.102692],[8.439103,32.506285],[8.430473,32.748337],[7.612642,33.344115],[7.524482,34.097376],[8.140981,34.655146],[8.376368,35.479876],[8.217824,36.433177],[8.420964,36.946427],[9.509994,37.349994],[10.210002,37.230002],[10.18065,36.724038],[11.028867,37.092103],[11.100026,36.899996],[10.600005,36.41],[10.593287,35.947444],[10.939519,35.698984],[10.807847,34.833507],[10.149593,34.330773],[10.339659,33.785742],[10.856836,33.76874],[11.108501,33.293343],[11.488787,33.136996],[11.432253,32.368903],[10.94479,32.081815],[10.636901,31.761421],[9.950225,31.37607],[10.056575,30.961831],[9.970017,30.539325],[9.48214,30.307556]]]}},
    +{"type":"Feature","id":"TUR","properties":{"name":"Turkey"},"geometry":{"type":"MultiPolygon","coordinates":[[[[36.913127,41.335358],[38.347665,40.948586],[39.512607,41.102763],[40.373433,41.013673],[41.554084,41.535656],[42.619549,41.583173],[43.582746,41.092143],[43.752658,40.740201],[43.656436,40.253564],[44.400009,40.005],[44.79399,39.713003],[44.109225,39.428136],[44.421403,38.281281],[44.225756,37.971584],[44.772699,37.170445],[44.293452,37.001514],[43.942259,37.256228],[42.779126,37.385264],[42.349591,37.229873],[41.212089,37.074352],[40.673259,37.091276],[39.52258,36.716054],[38.699891,36.712927],[38.167727,36.90121],[37.066761,36.623036],[36.739494,36.81752],[36.685389,36.259699],[36.41755,36.040617],[36.149763,35.821535],[35.782085,36.274995],[36.160822,36.650606],[35.550936,36.565443],[34.714553,36.795532],[34.026895,36.21996],[32.509158,36.107564],[31.699595,36.644275],[30.621625,36.677865],[30.391096,36.262981],[29.699976,36.144357],[28.732903,36.676831],[27.641187,36.658822],[27.048768,37.653361],[26.318218,38.208133],[26.8047,38.98576],[26.170785,39.463612],[27.28002,40.420014],[28.819978,40.460011],[29.240004,41.219991],[31.145934,41.087622],[32.347979,41.736264],[33.513283,42.01896],[35.167704,42.040225],[36.913127,41.335358]]],[[[27.192377,40.690566],[26.358009,40.151994],[26.043351,40.617754],[26.056942,40.824123],[26.294602,40.936261],[26.604196,41.562115],[26.117042,41.826905],[27.135739,42.141485],[27.99672,42.007359],[28.115525,41.622886],[28.988443,41.299934],[28.806438,41.054962],[27.619017,40.999823],[27.192377,40.690566]]]]}},
    +{"type":"Feature","id":"TWN","properties":{"name":"Taiwan"},"geometry":{"type":"Polygon","coordinates":[[[121.777818,24.394274],[121.175632,22.790857],[120.74708,21.970571],[120.220083,22.814861],[120.106189,23.556263],[120.69468,24.538451],[121.495044,25.295459],[121.951244,24.997596],[121.777818,24.394274]]]}},
    +{"type":"Feature","id":"TZA","properties":{"name":"United Republic of Tanzania"},"geometry":{"type":"Polygon","coordinates":[[[33.903711,-0.95],[34.07262,-1.05982],[37.69869,-3.09699],[37.7669,-3.67712],[39.20222,-4.67677],[38.74054,-5.90895],[38.79977,-6.47566],[39.44,-6.84],[39.47,-7.1],[39.19469,-7.7039],[39.25203,-8.00781],[39.18652,-8.48551],[39.53574,-9.11237],[39.9496,-10.0984],[40.31659,-10.3171],[39.521,-10.89688],[38.427557,-11.285202],[37.82764,-11.26879],[37.47129,-11.56876],[36.775151,-11.594537],[36.514082,-11.720938],[35.312398,-11.439146],[34.559989,-11.52002],[34.28,-10.16],[33.940838,-9.693674],[33.73972,-9.41715],[32.759375,-9.230599],[32.191865,-8.930359],[31.556348,-8.762049],[31.157751,-8.594579],[30.74,-8.34],[30.2,-7.08],[29.62,-6.52],[29.419993,-5.939999],[29.519987,-5.419979],[29.339998,-4.499983],[29.753512,-4.452389],[30.11632,-4.09012],[30.50554,-3.56858],[30.75224,-3.35931],[30.74301,-3.03431],[30.52766,-2.80762],[30.46967,-2.41383],[30.758309,-2.28725],[30.816135,-1.698914],[30.419105,-1.134659],[30.76986,-1.01455],[31.86617,-1.02736],[33.903711,-0.95]]]}},
    +{"type":"Feature","id":"UGA","properties":{"name":"Uganda"},"geometry":{"type":"Polygon","coordinates":[[[31.86617,-1.02736],[30.76986,-1.01455],[30.419105,-1.134659],[29.821519,-1.443322],[29.579466,-1.341313],[29.587838,-0.587406],[29.8195,-0.2053],[29.875779,0.59738],[30.086154,1.062313],[30.468508,1.583805],[30.85267,1.849396],[31.174149,2.204465],[30.77332,2.33989],[30.83385,3.50917],[31.24556,3.7819],[31.88145,3.55827],[32.68642,3.79232],[33.39,3.79],[34.005,4.249885],[34.47913,3.5556],[34.59607,3.05374],[35.03599,1.90584],[34.6721,1.17694],[34.18,0.515],[33.893569,0.109814],[33.903711,-0.95],[31.86617,-1.02736]]]}},
    +{"type":"Feature","id":"UKR","properties":{"name":"Ukraine"},"geometry":{"type":"Polygon","coordinates":[[[31.785998,52.101678],[32.159412,52.061267],[32.412058,52.288695],[32.715761,52.238465],[33.7527,52.335075],[34.391731,51.768882],[34.141978,51.566413],[34.224816,51.255993],[35.022183,51.207572],[35.377924,50.773955],[35.356116,50.577197],[36.626168,50.225591],[37.39346,50.383953],[38.010631,49.915662],[38.594988,49.926462],[40.069058,49.601055],[40.080789,49.30743],[39.674664,48.783818],[39.895632,48.232405],[39.738278,47.898937],[38.770585,47.825608],[38.255112,47.5464],[38.223538,47.10219],[37.425137,47.022221],[36.759855,46.6987],[35.823685,46.645964],[34.962342,46.273197],[35.020788,45.651219],[35.510009,45.409993],[36.529998,45.46999],[36.334713,45.113216],[35.239999,44.939996],[33.882511,44.361479],[33.326421,44.564877],[33.546924,45.034771],[32.454174,45.327466],[32.630804,45.519186],[33.588162,45.851569],[33.298567,46.080598],[31.74414,46.333348],[31.675307,46.706245],[30.748749,46.5831],[30.377609,46.03241],[29.603289,45.293308],[29.149725,45.464925],[28.679779,45.304031],[28.233554,45.488283],[28.485269,45.596907],[28.659987,45.939987],[28.933717,46.25883],[28.862972,46.437889],[29.072107,46.517678],[29.170654,46.379262],[29.759972,46.349988],[30.024659,46.423937],[29.83821,46.525326],[29.908852,46.674361],[29.559674,46.928583],[29.415135,47.346645],[29.050868,47.510227],[29.122698,47.849095],[28.670891,48.118149],[28.259547,48.155562],[27.522537,48.467119],[26.857824,48.368211],[26.619337,48.220726],[26.19745,48.220881],[25.945941,47.987149],[25.207743,47.891056],[24.866317,47.737526],[24.402056,47.981878],[23.760958,47.985598],[23.142236,48.096341],[22.710531,47.882194],[22.64082,48.15024],[22.085608,48.422264],[22.280842,48.825392],[22.558138,49.085738],[22.776419,49.027395],[22.51845,49.476774],[23.426508,50.308506],[23.922757,50.424881],[24.029986,50.705407],[23.527071,51.578454],[24.005078,51.617444],[24.553106,51.888461],[25.327788,51.910656],[26.337959,51.832289],[27.454066,51.592303],[28.241615,51.572227],[28.617613,51.427714],[28.992835,51.602044],[29.254938,51.368234],[30.157364,51.416138],[30.555117,51.319503],[30.619454,51.822806],[30.927549,52.042353],[31.785998,52.101678]]]}},
    +{"type":"Feature","id":"URY","properties":{"name":"Uruguay"},"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.976026,-30.109686],[-55.973245,-30.883076],[-55.60151,-30.853879],[-54.572452,-31.494511],[-53.787952,-32.047243],[-53.209589,-32.727666],[-53.650544,-33.202004],[-53.373662,-33.768378],[-53.806426,-34.396815],[-54.935866,-34.952647],[-55.67409,-34.752659],[-56.215297,-34.859836],[-57.139685,-34.430456],[-57.817861,-34.462547],[-58.427074,-33.909454],[-58.349611,-33.263189],[-58.132648,-33.040567],[-58.14244,-32.044504],[-57.874937,-31.016556],[-57.625133,-30.216295]]]}},
    +{"type":"Feature","id":"USA","properties":{"name":"United States of America"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347,19.70294],[-156.02368,19.81422],[-155.85008,19.97729],[-155.91907,20.17395],[-155.86108,20.26721],[-155.78505,20.2487],[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.45328],[-155.22217,19.23972],[-155.54211,19.08348]]],[[[-156.07926,20.64397],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71055,20.92676],[-156.61258,21.01249],[-156.25711,20.91745],[-155.99566,20.76404],[-156.07926,20.64397]]],[[[-156.75824,21.17684],[-156.78933,21.06873],[-157.32521,21.09777],[-157.25027,21.21958],[-156.75824,21.17684]]],[[[-157.65283,21.32217],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283,21.32217]]],[[[-159.34512,21.982],[-159.46372,21.88299],[-159.80051,22.06533],[-159.74877,22.1382],[-159.5962,22.23618],[-159.36569,22.21494],[-159.34512,21.982]]],[[[-94.81758,49.38905],[-94.64,48.84],[-94.32914,48.67074],[-93.63087,48.60926],[-92.61,48.45],[-91.64,48.14],[-90.83,48.27],[-89.6,48.01],[-89.272917,48.019808],[-88.378114,48.302918],[-87.439793,47.94],[-86.461991,47.553338],[-85.652363,47.220219],[-84.87608,46.900083],[-84.779238,46.637102],[-84.543749,46.538684],[-84.6049,46.4396],[-84.3367,46.40877],[-84.14212,46.512226],[-84.091851,46.275419],[-83.890765,46.116927],[-83.616131,46.116927],[-83.469551,45.994686],[-83.592851,45.816894],[-82.550925,45.347517],[-82.337763,44.44],[-82.137642,43.571088],[-82.43,42.98],[-82.9,42.43],[-83.12,42.08],[-83.142,41.975681],[-83.02981,41.832796],[-82.690089,41.675105],[-82.439278,41.675105],[-81.277747,42.209026],[-80.247448,42.3662],[-78.939362,42.863611],[-78.92,42.965],[-79.01,43.27],[-79.171674,43.466339],[-78.72028,43.625089],[-77.737885,43.629056],[-76.820034,43.628784],[-76.5,44.018459],[-76.375,44.09631],[-75.31821,44.81645],[-74.867,45.00048],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.255],[-71.08482,45.30524],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.905,47.185],[-68.23444,47.35486],[-67.79046,47.06636],[-67.79134,45.70281],[-67.13741,45.13753],[-66.96466,44.8097],[-68.03252,44.3252],[-69.06,43.98],[-70.11617,43.68405],[-70.645476,43.090238],[-70.81489,42.8653],[-70.825,42.335],[-70.495,41.805],[-70.08,41.78],[-70.185,42.145],[-69.88497,41.92283],[-69.96503,41.63717],[-70.64,41.475],[-71.12039,41.49445],[-71.86,41.32],[-72.295,41.27],[-72.87643,41.22065],[-73.71,40.931102],[-72.24126,41.11948],[-71.945,40.93],[-73.345,40.63],[-73.982,40.628],[-73.952325,40.75075],[-74.25671,40.47351],[-73.96244,40.42763],[-74.17838,39.70926],[-74.90604,38.93954],[-74.98041,39.1964],[-75.20002,39.24845],[-75.52805,39.4985],[-75.32,38.96],[-75.071835,38.782032],[-75.05673,38.40412],[-75.37747,38.01551],[-75.94023,37.21689],[-76.03127,37.2566],[-75.72205,37.93705],[-76.23287,38.319215],[-76.35,39.15],[-76.542725,38.717615],[-76.32933,38.08326],[-76.989998,38.239992],[-76.30162,37.917945],[-76.25874,36.9664],[-75.9718,36.89726],[-75.86804,36.55125],[-75.72749,35.55074],[-76.36318,34.80854],[-77.397635,34.51201],[-78.05496,33.92547],[-78.55435,33.86133],[-79.06067,33.49395],[-79.20357,33.15839],[-80.301325,32.509355],[-80.86498,32.0333],[-81.33629,31.44049],[-81.49042,30.72999],[-81.31371,30.03552],[-80.98,29.18],[-80.535585,28.47213],[-80.53,28.04],[-80.056539,26.88],[-80.088015,26.205765],[-80.13156,25.816775],[-80.38103,25.20616],[-80.68,25.08],[-81.17213,25.20126],[-81.33,25.64],[-81.71,25.87],[-82.24,26.73],[-82.70515,27.49504],[-82.85526,27.88624],[-82.65,28.55],[-82.93,29.1],[-83.70959,29.93656],[-84.1,30.09],[-85.10882,29.63615],[-85.28784,29.68612],[-85.7731,30.15261],[-86.4,30.4],[-87.53036,30.27433],[-88.41782,30.3849],[-89.18049,30.31598],[-89.593831,30.159994],[-89.413735,29.89419],[-89.43,29.48864],[-89.21767,29.29108],[-89.40823,29.15961],[-89.77928,29.30714],[-90.15463,29.11743],[-90.880225,29.148535],[-91.626785,29.677],[-92.49906,29.5523],[-93.22637,29.78375],[-93.84842,29.71363],[-94.69,29.48],[-95.60026,28.73863],[-96.59404,28.30748],[-97.14,27.83],[-97.37,27.38],[-97.38,26.69],[-97.33,26.21],[-97.14,25.87],[-97.53,25.84],[-98.24,26.06],[-99.02,26.37],[-99.3,26.84],[-99.52,27.54],[-100.11,28.11],[-100.45584,28.69612],[-100.9576,29.38071],[-101.6624,29.7793],[-102.48,29.76],[-103.11,28.97],[-103.94,29.27],[-104.45697,29.57196],[-104.70575,30.12173],[-105.03737,30.64402],[-105.63159,31.08383],[-106.1429,31.39995],[-106.50759,31.75452],[-108.24,31.754854],[-108.24194,31.34222],[-109.035,31.34194],[-111.02361,31.33472],[-113.30498,32.03914],[-114.815,32.52528],[-114.72139,32.72083],[-115.99135,32.61239],[-117.12776,32.53534],[-117.295938,33.046225],[-117.944,33.621236],[-118.410602,33.740909],[-118.519895,34.027782],[-119.081,34.078],[-119.438841,34.348477],[-120.36778,34.44711],[-120.62286,34.60855],[-120.74433,35.15686],[-121.71457,36.16153],[-122.54747,37.55176],[-122.51201,37.78339],[-122.95319,38.11371],[-123.7272,38.95166],[-123.86517,39.76699],[-124.39807,40.3132],[-124.17886,41.14202],[-124.2137,41.99964],[-124.53284,42.76599],[-124.14214,43.70838],[-124.020535,44.615895],[-123.89893,45.52341],[-124.079635,46.86475],[-124.39567,47.72017],[-124.68721,48.184433],[-124.566101,48.379715],[-123.12,48.04],[-122.58736,47.096],[-122.34,47.36],[-122.5,48.18],[-122.84,49],[-120,49],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05,49],[-107.05,49],[-104.04826,48.99986],[-100.65,49],[-97.22872,49.0007],[-95.15907,49],[-95.15609,49.38425],[-94.81758,49.38905]]],[[[-153.006314,57.115842],[-154.00509,56.734677],[-154.516403,56.992749],[-154.670993,57.461196],[-153.76278,57.816575],[-153.228729,57.968968],[-152.564791,57.901427],[-152.141147,57.591059],[-153.006314,57.115842]]],[[[-165.579164,59.909987],[-166.19277,59.754441],[-166.848337,59.941406],[-167.455277,60.213069],[-166.467792,60.38417],[-165.67443,60.293607],[-165.579164,59.909987]]],[[[-171.731657,63.782515],[-171.114434,63.592191],[-170.491112,63.694975],[-169.682505,63.431116],[-168.689439,63.297506],[-168.771941,63.188598],[-169.52944,62.976931],[-170.290556,63.194438],[-170.671386,63.375822],[-171.553063,63.317789],[-171.791111,63.405846],[-171.731657,63.782515]]],[[[-155.06779,71.147776],[-154.344165,70.696409],[-153.900006,70.889989],[-152.210006,70.829992],[-152.270002,70.600006],[-150.739992,70.430017],[-149.720003,70.53001],[-147.613362,70.214035],[-145.68999,70.12001],[-144.920011,69.989992],[-143.589446,70.152514],[-142.07251,69.851938],[-140.985988,69.711998],[-140.985988,69.711998],[-140.992499,66.000029],[-140.99777,60.306397],[-140.012998,60.276838],[-139.039,60.000007],[-138.34089,59.56211],[-137.4525,58.905],[-136.47972,59.46389],[-135.47583,59.78778],[-134.945,59.27056],[-134.27111,58.86111],[-133.355549,58.410285],[-132.73042,57.69289],[-131.70781,56.55212],[-130.00778,55.91583],[-129.979994,55.284998],[-130.53611,54.802753],[-131.085818,55.178906],[-131.967211,55.497776],[-132.250011,56.369996],[-133.539181,57.178887],[-134.078063,58.123068],[-135.038211,58.187715],[-136.628062,58.212209],[-137.800006,58.499995],[-139.867787,59.537762],[-140.825274,59.727517],[-142.574444,60.084447],[-143.958881,59.99918],[-145.925557,60.45861],[-147.114374,60.884656],[-148.224306,60.672989],[-148.018066,59.978329],[-148.570823,59.914173],[-149.727858,59.705658],[-150.608243,59.368211],[-151.716393,59.155821],[-151.859433,59.744984],[-151.409719,60.725803],[-150.346941,61.033588],[-150.621111,61.284425],[-151.895839,60.727198],[-152.57833,60.061657],[-154.019172,59.350279],[-153.287511,58.864728],[-154.232492,58.146374],[-155.307491,57.727795],[-156.308335,57.422774],[-156.556097,56.979985],[-158.117217,56.463608],[-158.433321,55.994154],[-159.603327,55.566686],[-160.28972,55.643581],[-161.223048,55.364735],[-162.237766,55.024187],[-163.069447,54.689737],[-164.785569,54.404173],[-164.942226,54.572225],[-163.84834,55.039431],[-162.870001,55.348043],[-161.804175,55.894986],[-160.563605,56.008055],[-160.07056,56.418055],[-158.684443,57.016675],[-158.461097,57.216921],[-157.72277,57.570001],[-157.550274,58.328326],[-157.041675,58.918885],[-158.194731,58.615802],[-158.517218,58.787781],[-159.058606,58.424186],[-159.711667,58.93139],[-159.981289,58.572549],[-160.355271,59.071123],[-161.355003,58.670838],[-161.968894,58.671665],[-162.054987,59.266925],[-161.874171,59.633621],[-162.518059,59.989724],[-163.818341,59.798056],[-164.662218,60.267484],[-165.346388,60.507496],[-165.350832,61.073895],[-166.121379,61.500019],[-165.734452,62.074997],[-164.919179,62.633076],[-164.562508,63.146378],[-163.753332,63.219449],[-163.067224,63.059459],[-162.260555,63.541936],[-161.53445,63.455817],[-160.772507,63.766108],[-160.958335,64.222799],[-161.518068,64.402788],[-160.777778,64.788604],[-161.391926,64.777235],[-162.45305,64.559445],[-162.757786,64.338605],[-163.546394,64.55916],[-164.96083,64.446945],[-166.425288,64.686672],[-166.845004,65.088896],[-168.11056,65.669997],[-166.705271,66.088318],[-164.47471,66.57666],[-163.652512,66.57666],[-163.788602,66.077207],[-161.677774,66.11612],[-162.489715,66.735565],[-163.719717,67.116395],[-164.430991,67.616338],[-165.390287,68.042772],[-166.764441,68.358877],[-166.204707,68.883031],[-164.430811,68.915535],[-163.168614,69.371115],[-162.930566,69.858062],[-161.908897,70.33333],[-160.934797,70.44769],[-159.039176,70.891642],[-158.119723,70.824721],[-156.580825,71.357764],[-155.06779,71.147776]]]]}},
    +{"type":"Feature","id":"UZB","properties":{"name":"Uzbekistan"},"geometry":{"type":"Polygon","coordinates":[[[66.518607,37.362784],[66.54615,37.974685],[65.215999,38.402695],[64.170223,38.892407],[63.518015,39.363257],[62.37426,40.053886],[61.882714,41.084857],[61.547179,41.26637],[60.465953,41.220327],[60.083341,41.425146],[59.976422,42.223082],[58.629011,42.751551],[57.78653,42.170553],[56.932215,41.826026],[57.096391,41.32231],[55.968191,41.308642],[55.928917,44.995858],[58.503127,45.586804],[58.689989,45.500014],[60.239972,44.784037],[61.05832,44.405817],[62.0133,43.504477],[63.185787,43.650075],[64.900824,43.728081],[66.098012,42.99766],[66.023392,41.994646],[66.510649,41.987644],[66.714047,41.168444],[67.985856,41.135991],[68.259896,40.662325],[68.632483,40.668681],[69.070027,41.384244],[70.388965,42.081308],[70.962315,42.266154],[71.259248,42.167711],[70.420022,41.519998],[71.157859,41.143587],[71.870115,41.3929],[73.055417,40.866033],[71.774875,40.145844],[71.014198,40.244366],[70.601407,40.218527],[70.45816,40.496495],[70.666622,40.960213],[69.329495,40.727824],[69.011633,40.086158],[68.536416,39.533453],[67.701429,39.580478],[67.44222,39.140144],[68.176025,38.901553],[68.392033,38.157025],[67.83,37.144994],[67.075782,37.356144],[66.518607,37.362784]]]}},
    +{"type":"Feature","id":"VEN","properties":{"name":"Venezuela"},"geometry":{"type":"Polygon","coordinates":[[[-71.331584,11.776284],[-71.360006,11.539994],[-71.94705,11.423282],[-71.620868,10.96946],[-71.633064,10.446494],[-72.074174,9.865651],[-71.695644,9.072263],[-71.264559,9.137195],[-71.039999,9.859993],[-71.350084,10.211935],[-71.400623,10.968969],[-70.155299,11.375482],[-70.293843,11.846822],[-69.943245,12.162307],[-69.5843,11.459611],[-68.882999,11.443385],[-68.233271,10.885744],[-68.194127,10.554653],[-67.296249,10.545868],[-66.227864,10.648627],[-65.655238,10.200799],[-64.890452,10.077215],[-64.329479,10.389599],[-64.318007,10.641418],[-63.079322,10.701724],[-61.880946,10.715625],[-62.730119,10.420269],[-62.388512,9.948204],[-61.588767,9.873067],[-60.830597,9.38134],[-60.671252,8.580174],[-60.150096,8.602757],[-59.758285,8.367035],[-60.550588,7.779603],[-60.637973,7.415],[-60.295668,7.043911],[-60.543999,6.856584],[-61.159336,6.696077],[-61.139415,6.234297],[-61.410303,5.959068],[-60.733574,5.200277],[-60.601179,4.918098],[-60.966893,4.536468],[-62.08543,4.162124],[-62.804533,4.006965],[-63.093198,3.770571],[-63.888343,4.02053],[-64.628659,4.148481],[-64.816064,4.056445],[-64.368494,3.79721],[-64.408828,3.126786],[-64.269999,2.497006],[-63.422867,2.411068],[-63.368788,2.2009],[-64.083085,1.916369],[-64.199306,1.492855],[-64.611012,1.328731],[-65.354713,1.095282],[-65.548267,0.789254],[-66.325765,0.724452],[-66.876326,1.253361],[-67.181294,2.250638],[-67.447092,2.600281],[-67.809938,2.820655],[-67.303173,3.318454],[-67.337564,3.542342],[-67.621836,3.839482],[-67.823012,4.503937],[-67.744697,5.221129],[-67.521532,5.55687],[-67.34144,6.095468],[-67.695087,6.267318],[-68.265052,6.153268],[-68.985319,6.206805],[-69.38948,6.099861],[-70.093313,6.960376],[-70.674234,7.087785],[-71.960176,6.991615],[-72.198352,7.340431],[-72.444487,7.423785],[-72.479679,7.632506],[-72.360901,8.002638],[-72.439862,8.405275],[-72.660495,8.625288],[-72.78873,9.085027],[-73.304952,9.152],[-73.027604,9.73677],[-72.905286,10.450344],[-72.614658,10.821975],[-72.227575,11.108702],[-71.973922,11.608672],[-71.331584,11.776284]]]}},
    +{"type":"Feature","id":"VNM","properties":{"name":"Vietnam"},"geometry":{"type":"Polygon","coordinates":[[[108.05018,21.55238],[106.715068,20.696851],[105.881682,19.75205],[105.662006,19.058165],[106.426817,18.004121],[107.361954,16.697457],[108.269495,16.079742],[108.877107,15.276691],[109.33527,13.426028],[109.200136,11.666859],[108.36613,11.008321],[107.220929,10.364484],[106.405113,9.53084],[105.158264,8.59976],[104.795185,9.241038],[105.076202,9.918491],[104.334335,10.486544],[105.199915,10.88931],[106.24967,10.961812],[105.810524,11.567615],[107.491403,12.337206],[107.614548,13.535531],[107.382727,14.202441],[107.564525,15.202173],[107.312706,15.908538],[106.556008,16.604284],[105.925762,17.485315],[105.094598,18.666975],[103.896532,19.265181],[104.183388,19.624668],[104.822574,19.886642],[104.435,20.758733],[103.203861,20.766562],[102.754896,21.675137],[102.170436,22.464753],[102.706992,22.708795],[103.504515,22.703757],[104.476858,22.81915],[105.329209,23.352063],[105.811247,22.976892],[106.725403,22.794268],[106.567273,22.218205],[107.04342,21.811899],[108.05018,21.55238]]]}},
    +{"type":"Feature","id":"VUT","properties":{"name":"Vanuatu"},"geometry":{"type":"MultiPolygon","coordinates":[[[[167.844877,-16.466333],[167.515181,-16.59785],[167.180008,-16.159995],[167.216801,-15.891846],[167.844877,-16.466333]]],[[[167.107712,-14.93392],[167.270028,-15.740021],[167.001207,-15.614602],[166.793158,-15.668811],[166.649859,-15.392704],[166.629137,-14.626497],[167.107712,-14.93392]]]]}},
    +{"type":"Feature","id":"PSE","properties":{"name":"West Bank"},"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.545252,31.782505],[35.397561,31.489086],[34.927408,31.353435],[34.970507,31.616778],[35.225892,31.754341],[34.974641,31.866582],[35.18393,32.532511],[35.545665,32.393992]]]}},
    +{"type":"Feature","id":"YEM","properties":{"name":"Yemen"},"geometry":{"type":"Polygon","coordinates":[[[53.108573,16.651051],[52.385206,16.382411],[52.191729,15.938433],[52.168165,15.59742],[51.172515,15.17525],[49.574576,14.708767],[48.679231,14.003202],[48.238947,13.94809],[47.938914,14.007233],[47.354454,13.59222],[46.717076,13.399699],[45.877593,13.347764],[45.62505,13.290946],[45.406459,13.026905],[45.144356,12.953938],[44.989533,12.699587],[44.494576,12.721653],[44.175113,12.58595],[43.482959,12.6368],[43.222871,13.22095],[43.251448,13.767584],[43.087944,14.06263],[42.892245,14.802249],[42.604873,15.213335],[42.805015,15.261963],[42.702438,15.718886],[42.823671,15.911742],[42.779332,16.347891],[43.218375,16.66689],[43.115798,17.08844],[43.380794,17.579987],[43.791519,17.319977],[44.062613,17.410359],[45.216651,17.433329],[45.399999,17.333335],[46.366659,17.233315],[46.749994,17.283338],[47.000005,16.949999],[47.466695,17.116682],[48.183344,18.166669],[49.116672,18.616668],[52.00001,19.000003],[52.782184,17.349742],[53.108573,16.651051]]]}},
    +{"type":"Feature","id":"ZAF","properties":{"name":"South Africa"},"geometry":{"type":"Polygon","coordinates":[[[31.521001,-29.257387],[31.325561,-29.401978],[30.901763,-29.909957],[30.622813,-30.423776],[30.055716,-31.140269],[28.925553,-32.172041],[28.219756,-32.771953],[27.464608,-33.226964],[26.419452,-33.61495],[25.909664,-33.66704],[25.780628,-33.944646],[25.172862,-33.796851],[24.677853,-33.987176],[23.594043,-33.794474],[22.988189,-33.916431],[22.574157,-33.864083],[21.542799,-34.258839],[20.689053,-34.417175],[20.071261,-34.795137],[19.616405,-34.819166],[19.193278,-34.462599],[18.855315,-34.444306],[18.424643,-33.997873],[18.377411,-34.136521],[18.244499,-33.867752],[18.25008,-33.281431],[17.92519,-32.611291],[18.24791,-32.429131],[18.221762,-31.661633],[17.566918,-30.725721],[17.064416,-29.878641],[17.062918,-29.875954],[16.344977,-28.576705],[16.824017,-28.082162],[17.218929,-28.355943],[17.387497,-28.783514],[17.836152,-28.856378],[18.464899,-29.045462],[19.002127,-28.972443],[19.894734,-28.461105],[19.895768,-24.76779],[20.165726,-24.917962],[20.758609,-25.868136],[20.66647,-26.477453],[20.889609,-26.828543],[21.605896,-26.726534],[22.105969,-26.280256],[22.579532,-25.979448],[22.824271,-25.500459],[23.312097,-25.26869],[23.73357,-25.390129],[24.211267,-25.670216],[25.025171,-25.71967],[25.664666,-25.486816],[25.765849,-25.174845],[25.941652,-24.696373],[26.485753,-24.616327],[26.786407,-24.240691],[27.11941,-23.574323],[28.017236,-22.827754],[29.432188,-22.091313],[29.839037,-22.102216],[30.322883,-22.271612],[30.659865,-22.151567],[31.191409,-22.25151],[31.670398,-23.658969],[31.930589,-24.369417],[31.752408,-25.484284],[31.837778,-25.843332],[31.333158,-25.660191],[31.04408,-25.731452],[30.949667,-26.022649],[30.676609,-26.398078],[30.685962,-26.743845],[31.282773,-27.285879],[31.86806,-27.177927],[32.071665,-26.73382],[32.83012,-26.742192],[32.580265,-27.470158],[32.462133,-28.301011],[32.203389,-28.752405],[31.521001,-29.257387]],[[28.978263,-28.955597],[28.5417,-28.647502],[28.074338,-28.851469],[27.532511,-29.242711],[26.999262,-29.875954],[27.749397,-30.645106],[28.107205,-30.545732],[28.291069,-30.226217],[28.8484,-30.070051],[29.018415,-29.743766],[29.325166,-29.257387],[28.978263,-28.955597]]]}},
    +{"type":"Feature","id":"ZMB","properties":{"name":"Zambia"},"geometry":{"type":"Polygon","coordinates":[[[32.759375,-9.230599],[33.231388,-9.676722],[33.485688,-10.525559],[33.31531,-10.79655],[33.114289,-11.607198],[33.306422,-12.435778],[32.991764,-12.783871],[32.688165,-13.712858],[33.214025,-13.97186],[30.179481,-14.796099],[30.274256,-15.507787],[29.516834,-15.644678],[28.947463,-16.043051],[28.825869,-16.389749],[28.467906,-16.4684],[27.598243,-17.290831],[27.044427,-17.938026],[26.706773,-17.961229],[26.381935,-17.846042],[25.264226,-17.73654],[25.084443,-17.661816],[25.07695,-17.578823],[24.682349,-17.353411],[24.033862,-17.295843],[23.215048,-17.523116],[22.562478,-16.898451],[21.887843,-16.08031],[21.933886,-12.898437],[24.016137,-12.911046],[23.930922,-12.565848],[24.079905,-12.191297],[23.904154,-11.722282],[24.017894,-11.237298],[23.912215,-10.926826],[24.257155,-10.951993],[24.314516,-11.262826],[24.78317,-11.238694],[25.418118,-11.330936],[25.75231,-11.784965],[26.553088,-11.92444],[27.16442,-11.608748],[27.388799,-12.132747],[28.155109,-12.272481],[28.523562,-12.698604],[28.934286,-13.248958],[29.699614,-13.257227],[29.616001,-12.178895],[29.341548,-12.360744],[28.642417,-11.971569],[28.372253,-11.793647],[28.49607,-10.789884],[28.673682,-9.605925],[28.449871,-9.164918],[28.734867,-8.526559],[29.002912,-8.407032],[30.346086,-8.238257],[30.740015,-8.340007],[31.157751,-8.594579],[31.556348,-8.762049],[32.191865,-8.930359],[32.759375,-9.230599]]]}},
    +{"type":"Feature","id":"ZWE","properties":{"name":"Zimbabwe"},"geometry":{"type":"Polygon","coordinates":[[[31.191409,-22.25151],[30.659865,-22.151567],[30.322883,-22.271612],[29.839037,-22.102216],[29.432188,-22.091313],[28.794656,-21.639454],[28.02137,-21.485975],[27.727228,-20.851802],[27.724747,-20.499059],[27.296505,-20.39152],[26.164791,-19.293086],[25.850391,-18.714413],[25.649163,-18.536026],[25.264226,-17.73654],[26.381935,-17.846042],[26.706773,-17.961229],[27.044427,-17.938026],[27.598243,-17.290831],[28.467906,-16.4684],[28.825869,-16.389749],[28.947463,-16.043051],[29.516834,-15.644678],[30.274256,-15.507787],[30.338955,-15.880839],[31.173064,-15.860944],[31.636498,-16.07199],[31.852041,-16.319417],[32.328239,-16.392074],[32.847639,-16.713398],[32.849861,-17.979057],[32.654886,-18.67209],[32.611994,-19.419383],[32.772708,-19.715592],[32.659743,-20.30429],[32.508693,-20.395292],[32.244988,-21.116489],[31.191409,-22.25151]]]}}
    +]}
    diff --git a/mapbox-gl/examples/data/geojson/line-samples.geojson b/mapbox-gl/examples/data/geojson/line-samples.geojson
    new file mode 100644
    index 0000000000..21ebbe2bf8
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/line-samples.geojson
    @@ -0,0 +1,16 @@
    +{
    +"type": "FeatureCollection",
    +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
    +                                                                                
    +"features": [
    +{ "type": "Feature", "properties": { "name": "Roussel" }, "geometry": { "type": "LineString", "coordinates": [ [ -74.317812426119204, 48.287712285170407 ], [ -73.965445435101188, 48.287712285170407 ] ] } },
    +{ "type": "Feature", "properties": { "name": "de la Grande-Décharge Sud" }, "geometry": { "type": "LineString", "coordinates": [ [ -72.890274359943689, 47.66777849648031 ], [ -73.03935270229745, 47.552045722357249 ], [ -73.03483517677158, 47.743778832463107 ], [ -72.750231068641639, 47.749854072477497 ], [ -72.754748594167509, 47.552045722357249 ] ] } },
    +{ "type": "Feature", "properties": { "name": "du Saguenay Ouest" }, "geometry": { "type": "LineString", "coordinates": [ [ -73.007730023616347, 48.07082744644633 ], [ -72.894791885469544, 47.889388912080449 ], [ -72.727643441012276, 48.082900678850329 ], [ -72.560494996555022, 47.895447137861773 ] ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-Anicet" }, "geometry": { "type": "LineString", "coordinates": [ [ -72.985142395986983, 48.338787334581873 ], [ -72.980624870461128, 48.161307640513321 ], [ -72.885756834417805, 48.164320903012829 ], [ -72.894791885469544, 48.338787334581873 ] ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-Remy-en-Bouzemont-Saint-Genest-et-Isson" }, "geometry": { "type": "LineString", "coordinates": [ [ -73.459482576203527, 47.53679865861892 ], [ -73.454965050677686, 47.719470781160837 ] ] } },
    +{ "type": "Feature", "properties": { "name": "Sainte-Geneviève" }, "geometry": { "type": "LineString", "coordinates": [ [ -73.716981531178234, 47.889388912080449 ], [ -73.423342371996569, 48.091953743979651 ], [ -73.242641350961676, 47.883329977544491 ], [ -73.685358852497131, 47.862118125007399 ] ] } },
    +{ "type": "Feature", "properties": { "name": "de Lombard" }, "geometry": { "type": "LineString", "coordinates": [ [ -73.631148546186679, 48.323770568268507 ], [ -73.626631020660795, 48.185408784389622 ], [ -73.55435061224685, 48.188420630879783 ], [ -73.55435061224685, 48.320766684325278 ], [ -73.473035152781165, 48.317762623483489 ], [ -73.482070203832905, 48.19745510840756 ], [ -73.400754744367205, 48.206487993065075 ], [ -73.396237218841335, 48.323770568268507 ], [ -73.301369182798027, 48.317762623483489 ], [ -73.319439284901506, 48.191432300378096 ] ] } },
    +{ "type": "Feature", "properties": { "name": "de la Grande-Baie Sud" }, "geometry": { "type": "LineString", "coordinates": [ [ -74.439785615317746, 47.564240180362376 ], [ -74.227461915601779, 47.716431476953346 ], [ -74.055795945618627, 47.552045722357249 ], [ -73.879612450109633, 47.710352336655504 ] ] } },
    +{ "type": "Feature", "properties": { "name": "de Tadoussac" }, "geometry": { "type": "LineString", "coordinates": [ [ -74.313294900593348, 48.091953743979651 ], [ -74.313294900593348, 47.880300244488787 ], [ -73.974480486152942, 47.877270334238752 ], [ -73.978998011678812, 48.076864416783366 ] ] } }
    +]
    +}
    diff --git a/mapbox-gl/examples/data/geojson/point-samples.geojson b/mapbox-gl/examples/data/geojson/point-samples.geojson
    new file mode 100644
    index 0000000000..cdc8d4e071
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/point-samples.geojson
    @@ -0,0 +1,16 @@
    +{
    +"type": "FeatureCollection",
    +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
    +                                                                                
    +"features": [
    +{ "type": "Feature", "properties": { "name": "Saguenay (Arrondissement Latterière)" }, "geometry": { "type": "Point", "coordinates": [ -75.849253579389796, 47.6434349837781 ] } },
    +{ "type": "Feature", "properties": { "name": "Canton Tremblay" }, "geometry": { "type": "Point", "coordinates": [ -75.840218528338056, 47.971115165183342 ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-Félix-d'Otis" }, "geometry": { "type": "Point", "coordinates": [ -75.849253579389796, 48.278693733790902 ] } },
    +{ "type": "Feature", "properties": { "name": "La Baie" }, "geometry": { "type": "Point", "coordinates": [ -74.792152606335762, 47.649521925935176 ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-David-de-Falardeau" }, "geometry": { "type": "Point", "coordinates": [ -74.801187657387501, 47.977163824275436 ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-Honoré-de-Chicoutimi" }, "geometry": { "type": "Point", "coordinates": [ -74.792152606335762, 48.284706278302295 ] } },
    +{ "type": "Feature", "properties": { "name": "Alma" }, "geometry": { "type": "Point", "coordinates": [ -75.298115465233423, 47.6434349837781 ] } },
    +{ "type": "Feature", "properties": { "name": "Jonquière" }, "geometry": { "type": "Point", "coordinates": [ -75.298115465233423, 47.971115165183342 ] } },
    +{ "type": "Feature", "properties": { "name": "Chicoutimi" }, "geometry": { "type": "Point", "coordinates": [ -75.289080414181669, 48.284706278302295 ] } }
    +]
    +}
    diff --git a/mapbox-gl/examples/data/geojson/polygon-samples.geojson b/mapbox-gl/examples/data/geojson/polygon-samples.geojson
    new file mode 100644
    index 0000000000..eef9eee79b
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/polygon-samples.geojson
    @@ -0,0 +1,16 @@
    +{
    +"type": "FeatureCollection",
    +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
    +                                                                                
    +"features": [
    +{ "type": "Feature", "properties": { "name": "Parc de la Colline" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -72.357206347890767, 47.72858763003908 ], [ -71.86027854004486, 47.527648291638172 ], [ -72.37075892446839, 47.539848426151735 ], [ -72.357206347890767, 47.72858763003908 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Centre Paul-Étienne Simard" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -72.357206347890767, 48.013440900213297 ], [ -72.239750684218109, 48.013440900213297 ], [ -72.253303260795718, 47.856056000888501 ], [ -72.027426984502114, 47.856056000888501 ], [ -72.036462035553868, 48.013440900213297 ], [ -71.905453795303586, 48.01646283861713 ], [ -71.891901218725963, 47.801464984333364 ], [ -72.361723873416651, 47.810567474765456 ], [ -72.357206347890767, 48.013440900213297 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Loisirs Rivière du Moulin" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -72.194575428959382, 48.33278115872843 ], [ -72.018391933450374, 48.33278115872843 ], [ -71.846725963467236, 48.251628525276693 ], [ -71.950629050562299, 48.107038644740094 ], [ -72.203610480011122, 48.107038644740094 ], [ -72.397864077623623, 48.221539261269051 ], [ -72.194575428959382, 48.33278115872843 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "L'Étoile-du-Nord" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -71.589227008492543, 47.649521925935176 ], [ -71.525981651130337, 47.734664642855655 ], [ -71.48532392139748, 47.649521925935169 ], [ -71.295587849310877, 47.637347332276697 ], [ -71.462736293768117, 47.585573652777313 ], [ -71.390455885354172, 47.475766052599219 ], [ -71.535016702182091, 47.552045722357242 ], [ -71.702165146639345, 47.491030857179695 ], [ -71.616332161647762, 47.591667334264848 ], [ -71.787998131630914, 47.655608158761908 ], [ -71.589227008492543, 47.649521925935176 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Loisirs Lavoie et St-Jean-Baptiste" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -71.729270299794578, 48.010418784700107 ], [ -71.291070323784993, 48.004374022337799 ], [ -71.291070323784993, 47.777183877693901 ], [ -71.729270299794578, 47.786290622064854 ], [ -71.729270299794578, 48.010418784700107 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Loisirs Diamant" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -71.693130095587605, 48.341790157179155 ], [ -71.286552798259123, 48.344792802893032 ], [ -71.449183717190522, 48.224548983994914 ], [ -71.277517747207369, 48.070827446446337 ], [ -71.751857927423927, 48.085918544287573 ], [ -71.507911549026844, 48.21551928490868 ], [ -71.693130095587605, 48.341790157179155 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Sydenham" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -71.051641470913779, 47.710352336655504 ], [ -70.911598179611758, 47.710352336655504 ], [ -70.925150756189367, 47.619080121567436 ], [ -70.712827056473373, 47.616034965734443 ], [ -70.721862107525112, 47.448278226184989 ], [ -70.857387873301292, 47.448278226184989 ], [ -70.852870347775408, 47.552045722357249 ], [ -71.056158996439635, 47.552045722357249 ], [ -71.051641470913779, 47.710352336655504 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Saint-Luc" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -71.110369302750115, 47.798430466372736 ], [ -70.902563128560018, 47.983211774835986 ], [ -70.699274479895777, 47.789325849015306 ], [ -71.110369302750115, 47.798430466372736 ] ] ] } },
    +{ "type": "Feature", "properties": { "name": "Loisirs du Fjord du Saguenay" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -70.988396113551573, 48.32977780546792 ], [ -70.812212618042579, 48.32977780546792 ], [ -70.807695092516681, 48.209498600656133 ], [ -70.631511597007702, 48.209498600656147 ], [ -70.636029122533571, 48.079882636349602 ], [ -71.146509506957088, 48.082900678850329 ], [ -71.151027032482972, 48.212509031269981 ], [ -70.983878588025689, 48.209498600656133 ], [ -70.988396113551573, 48.32977780546792 ] ] ] } }
    +]
    +}
    diff --git a/mapbox-gl/examples/data/geojson/switzerland.geojson b/mapbox-gl/examples/data/geojson/switzerland.geojson
    new file mode 100644
    index 0000000000..eea0851bfd
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geojson/switzerland.geojson
    @@ -0,0 +1,4 @@
    +{"type":"FeatureCollection","features":[
    +{"type":"Feature","id":"CHE","properties":{"name":"Switzerland"},"geometry":{"type":"Polygon","coordinates":[[[9.594226,47.525058],[9.632932,47.347601],[9.47997,47.10281],[9.932448,46.920728],[10.442701,46.893546],[10.363378,46.483571],[9.922837,46.314899],[9.182882,46.440215],[8.966306,46.036932],[8.489952,46.005151],[8.31663,46.163642],[7.755992,45.82449],[7.273851,45.776948],[6.843593,45.991147],[6.5001,46.429673],[6.022609,46.27299],[6.037389,46.725779],[6.768714,47.287708],[6.736571,47.541801],[7.192202,47.449766],[7.466759,47.620582],[8.317301,47.61358],[8.522612,47.830828],[9.594226,47.525058]]]}},
    +{"type":"Feature","id":"LSNE","properties":{"name":"Lausanne"},"geometry":{"type":"Point","coordinates":[6.6339863,46.5193823]}}
    +]}
    diff --git a/mapbox-gl/examples/data/geolocation-orientation.json b/mapbox-gl/examples/data/geolocation-orientation.json
    new file mode 100644
    index 0000000000..0bb5d900f3
    --- /dev/null
    +++ b/mapbox-gl/examples/data/geolocation-orientation.json
    @@ -0,0 +1,729 @@
    +{
    +  "data": [{
    +    "coords": {
    +      "speed": 1.7330950498580933,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 8,
    +      "altitude": 238,
    +      "longitude": 5.868668798362713,
    +      "heading": 67.5,
    +      "latitude": 45.64444874417562
    +    },
    +    "timestamp": 1394788264972
    +  }, {
    +    "coords": {
    +      "speed": 1.9535436630249023,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 8,
    +      "altitude": 238,
    +      "longitude": 5.868715401744348,
    +      "heading": 69.609375,
    +      "latitude": 45.64446391542036
    +    },
    +    "timestamp": 1394788266115
    +  }, {
    +    "coords": {
    +      "speed": 2.1882569789886475,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 8,
    +      "altitude": 238,
    +      "longitude": 5.868768962105614,
    +      "heading": 67.5,
    +      "latitude": 45.644484995906836
    +    },
    +    "timestamp": 1394788267107
    +  }, {
    +    "coords": {
    +      "speed": 2.4942498207092285,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 237,
    +      "longitude": 5.868825791409117,
    +      "heading": 68.5546875,
    +      "latitude": 45.64450435810316
    +    },
    +    "timestamp": 1394788267959
    +  }, {
    +    "coords": {
    +      "speed": 2.7581217288970947,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 237,
    +      "longitude": 5.868881698703271,
    +      "heading": 69.609375,
    +      "latitude": 45.64452149909515
    +    },
    +    "timestamp": 1394788268964
    +  }, {
    +    "coords": {
    +      "speed": 3.3746347427368164,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.868938528006774,
    +      "heading": 70.3125,
    +      "latitude": 45.644536712249405
    +    },
    +    "timestamp": 1394788270116
    +  }, {
    +    "coords": {
    +      "speed": 3.597411870956421,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.868992004549009,
    +      "heading": 74.8828125,
    +      "latitude": 45.644547943999655
    +    },
    +    "timestamp": 1394788271158
    +  }, {
    +    "coords": {
    +      "speed": 3.6382505893707275,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.869038775568706,
    +      "heading": 73.828125,
    +      "latitude": 45.64456005584974
    +    },
    +    "timestamp": 1394788271893
    +  }, {
    +    "coords": {
    +      "speed": 3.65671443939209,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.869091162463528,
    +      "heading": 73.4765625,
    +      "latitude": 45.644572335337884
    +    },
    +    "timestamp": 1394788272903
    +  }, {
    +    "coords": {
    +      "speed": 3.7153592109680176,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.869144219910604,
    +      "heading": 73.125,
    +      "latitude": 45.64458671030182
    +    },
    +    "timestamp": 1394788273914
    +  }, {
    +    "coords": {
    +      "speed": 3.8041043281555176,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 236,
    +      "longitude": 5.869205072527629,
    +      "heading": 72.421875,
    +      "latitude": 45.64460313883204
    +    },
    +    "timestamp": 1394788274901
    +  }, {
    +    "coords": {
    +      "speed": 3.9588162899017334,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 236,
    +      "longitude": 5.869268858810765,
    +      "heading": 72.421875,
    +      "latitude": 45.64461990263838
    +    },
    +    "timestamp": 1394788276140
    +  }, {
    +    "coords": {
    +      "speed": 4.152309417724609,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 235,
    +      "longitude": 5.869351252918941,
    +      "heading": 78.046875,
    +      "latitude": 45.64466122542102
    +    },
    +    "timestamp": 1394788276948
    +  }, {
    +    "coords": {
    +      "speed": 4.49971866607666,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 236,
    +      "longitude": 5.869433479389054,
    +      "heading": 79.8046875,
    +      "latitude": 45.64467040360499
    +    },
    +    "timestamp": 1394788277892
    +  }, {
    +    "coords": {
    +      "speed": 4.824056148529053,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.869504055013758,
    +      "heading": 91.40625,
    +      "latitude": 45.64466089014489
    +    },
    +    "timestamp": 1394788279211
    +  }, {
    +    "coords": {
    +      "speed": 5.269814491271973,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.869575049733621,
    +      "heading": 91.40625,
    +      "latitude": 45.64465967476893
    +    },
    +    "timestamp": 1394788279898
    +  }, {
    +    "coords": {
    +      "speed": 5.4861016273498535,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.86963213049422,
    +      "heading": 95.2734375,
    +      "latitude": 45.64465091568012
    +    },
    +    "timestamp": 1394788280935
    +  }, {
    +    "coords": {
    +      "speed": 5.380503177642822,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.869714859878523,
    +      "heading": 75.5859375,
    +      "latitude": 45.64468792178262
    +    },
    +    "timestamp": 1394788281930
    +  }, {
    +    "coords": {
    +      "speed": 5.276519775390625,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 234,
    +      "longitude": 5.869746124377353,
    +      "heading": 55.1953125,
    +      "latitude": 45.64467706721801
    +    },
    +    "timestamp": 1394788282909
    +  }, {
    +    "coords": {
    +      "speed": 5.212399482727051,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 232,
    +      "longitude": 5.8697939850444625,
    +      "heading": 49.5703125,
    +      "latitude": 45.64467899505574
    +    },
    +    "timestamp": 1394788284221
    +  }, {
    +    "coords": {
    +      "speed": 5.174651622772217,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 232,
    +      "longitude": 5.869789123540623,
    +      "heading": 18.984375,
    +      "latitude": 45.64469378911484
    +    },
    +    "timestamp": 1394788284924
    +  }, {
    +    "coords": {
    +      "speed": 5.211904525756836,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 232,
    +      "longitude": 5.869806222623093,
    +      "heading": 10.1953125,
    +      "latitude": 45.64473896757294
    +    },
    +    "timestamp": 1394788286251
    +  }, {
    +    "coords": {
    +      "speed": 5.254780292510986,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 233,
    +      "longitude": 5.86982952431391,
    +      "heading": 18.6328125,
    +      "latitude": 45.64478381075491
    +    },
    +    "timestamp": 1394788286927
    +  }, {
    +    "coords": {
    +      "speed": 5.329030513763428,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 232,
    +      "longitude": 5.869875792419417,
    +      "heading": 33.75,
    +      "latitude": 45.644830078860416
    +    },
    +    "timestamp": 1394788288221
    +  }, {
    +    "coords": {
    +      "speed": 5.384955883026123,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 232,
    +      "longitude": 5.869927508761985,
    +      "heading": 46.7578125,
    +      "latitude": 45.64486025371183
    +    },
    +    "timestamp": 1394788288935
    +  }, {
    +    "coords": {
    +      "speed": 5.309582233428955,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 232,
    +      "longitude": 5.869972854858143,
    +      "heading": 47.109375,
    +      "latitude": 45.644890596201314
    +    },
    +    "timestamp": 1394788290178
    +  }, {
    +    "coords": {
    +      "speed": 5.250724792480469,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.870029265066488,
    +      "heading": 46.40625,
    +      "latitude": 45.644932673355235
    +    },
    +    "timestamp": 1394788290890
    +  }, {
    +    "coords": {
    +      "speed": 5.3057990074157715,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.870077712466819,
    +      "heading": 39.375,
    +      "latitude": 45.644970224281444
    +    },
    +    "timestamp": 1394788291884
    +  }, {
    +    "coords": {
    +      "speed": 5.431822299957275,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.870133116846783,
    +      "heading": 43.59375,
    +      "latitude": 45.6450097449549
    +    },
    +    "timestamp": 1394788292885
    +  }, {
    +    "coords": {
    +      "speed": 5.542125225067139,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.870186509569986,
    +      "heading": 43.59375,
    +      "latitude": 45.645047421609654
    +    },
    +    "timestamp": 1394788294100
    +  }, {
    +    "coords": {
    +      "speed": 5.647174835205078,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.870246104901535,
    +      "heading": 42.890625,
    +      "latitude": 45.645093647805645
    +    },
    +    "timestamp": 1394788295157
    +  }, {
    +    "coords": {
    +      "speed": 5.735793590545654,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.870298156520231,
    +      "heading": 42.5390625,
    +      "latitude": 45.64514368776758
    +    },
    +    "timestamp": 1394788296124
    +  }, {
    +    "coords": {
    +      "speed": 5.809989929199219,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.870346436282499,
    +      "heading": 43.59375,
    +      "latitude": 45.64519154843469
    +    },
    +    "timestamp": 1394788296960
    +  }, {
    +    "coords": {
    +      "speed": 5.877871036529541,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 228,
    +      "longitude": 5.87034755932109,
    +      "heading": 42.75193405151367,
    +      "latitude": 45.645270362475216
    +    },
    +    "timestamp": 1394788298177
    +  }, {
    +    "coords": {
    +      "speed": 5.937166690826416,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 228,
    +      "longitude": 5.870402806867787,
    +      "heading": 42.75193405151367,
    +      "latitude": 45.645312142096095
    +    },
    +    "timestamp": 1394788298898
    +  }, {
    +    "coords": {
    +      "speed": 6.071393966674805,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 229,
    +      "longitude": 5.870464520921814,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64535851937182
    +    },
    +    "timestamp": 1394788299897
    +  }, {
    +    "coords": {
    +      "speed": 6.329115390777588,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.8705368384107715,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.645412389093565
    +    },
    +    "timestamp": 1394788300957
    +  }, {
    +    "coords": {
    +      "speed": 6.581554889678955,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 229,
    +      "longitude": 5.870600162706978,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64545955929912
    +    },
    +    "timestamp": 1394788302211
    +  }, {
    +    "coords": {
    +      "speed": 6.605470180511475,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.870657211053185,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64550205482465
    +    },
    +    "timestamp": 1394788302917
    +  }, {
    +    "coords": {
    +      "speed": 6.623170375823975,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 229,
    +      "longitude": 5.870713613403495,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64554406917767
    +    },
    +    "timestamp": 1394788303929
    +  }, {
    +    "coords": {
    +      "speed": 6.645580768585205,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 229,
    +      "longitude": 5.870773011629353,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64558831489415
    +    },
    +    "timestamp": 1394788304902
    +  }, {
    +    "coords": {
    +      "speed": 6.663600444793701,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 4,
    +      "altitude": 229,
    +      "longitude": 5.87083890910435,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.645637401898654
    +    },
    +    "timestamp": 1394788306035
    +  }, {
    +    "coords": {
    +      "speed": 6.664675712585449,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 229,
    +      "longitude": 5.870890033475007,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64567548463474
    +    },
    +    "timestamp": 1394788307080
    +  }, {
    +    "coords": {
    +      "speed": 6.6489081382751465,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 228,
    +      "longitude": 5.870943189474929,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.645715080460064
    +    },
    +    "timestamp": 1394788308211
    +  }, {
    +    "coords": {
    +      "speed": 6.551820755004883,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 228,
    +      "longitude": 5.871005613698799,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64576158014743
    +    },
    +    "timestamp": 1394788308904
    +  }, {
    +    "coords": {
    +      "speed": 6.467689514160156,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 229,
    +      "longitude": 5.871058030061249,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64580062501799
    +    },
    +    "timestamp": 1394788310161
    +  }, {
    +    "coords": {
    +      "speed": 6.3997955322265625,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 229,
    +      "longitude": 5.871062579208228,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64580401381376
    +    },
    +    "timestamp": 1394788310957
    +  }, {
    +    "coords": {
    +      "speed": 5.799798488616943,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.8710817079554545,
    +      "heading": 43.183074951171875,
    +      "latitude": 45.64581826277647
    +    },
    +    "timestamp": 1394788312036
    +  }, {
    +    "coords": {
    +      "speed": 4.424941062927246,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 230,
    +      "longitude": 5.871121835629857,
    +      "heading": 175.4296875,
    +      "latitude": 45.645828271551544
    +    },
    +    "timestamp": 1394788312951
    +  }, {
    +    "coords": {
    +      "speed": 4.3496222496032715,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.8710026017471595,
    +      "heading": 176.484375,
    +      "latitude": 45.645752236602775
    +    },
    +    "timestamp": 1394788315227
    +  }, {
    +    "coords": {
    +      "speed": 5.076380252838135,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 232,
    +      "longitude": 5.871189236646398,
    +      "heading": 176.1328125,
    +      "latitude": 45.64553692475487
    +    },
    +    "timestamp": 1394788316970
    +  }, {
    +    "coords": {
    +      "speed": 5.102786064147949,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.871200384577616,
    +      "heading": 171.2109375,
    +      "latitude": 45.64548554368843
    +    },
    +    "timestamp": 1394788317965
    +  }, {
    +    "coords": {
    +      "speed": 4.705626964569092,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.871210945775612,
    +      "heading": 164.1796875,
    +      "latitude": 45.645453105723156
    +    },
    +    "timestamp": 1394788318956
    +  }, {
    +    "coords": {
    +      "speed": 4.378190040588379,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 231,
    +      "longitude": 5.87124749087344,
    +      "heading": 126.2109375,
    +      "latitude": 45.645433282522156
    +    },
    +    "timestamp": 1394788320197
    +  }, {
    +    "coords": {
    +      "speed": 4.208680152893066,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 233,
    +      "longitude": 5.871283365419014,
    +      "heading": 125.859375,
    +      "latitude": 45.6454103999265
    +    },
    +    "timestamp": 1394788320894
    +  }, {
    +    "coords": {
    +      "speed": 4.072604179382324,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 233,
    +      "longitude": 5.871314043184622,
    +      "heading": 103.359375,
    +      "latitude": 45.645410819021656
    +    },
    +    "timestamp": 1394788322169
    +  }, {
    +    "coords": {
    +      "speed": 3.7680623531341553,
    +      "accuracy": 5,
    +      "altitudeAccuracy": 6,
    +      "altitude": 234,
    +      "longitude": 5.871355114510163,
    +      "heading": 92.4609375,
    +      "latitude": 45.645418111277415
    +    },
    +    "timestamp": 1394788322898
    +  }, {
    +    "coords": {
    +      "speed": 3.537794351577759,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 234,
    +      "longitude": 5.871393922721847,
    +      "heading": 92.4609375,
    +      "latitude": 45.64541693781097
    +    },
    +    "timestamp": 1394788323968
    +  }, {
    +    "coords": {
    +      "speed": 3.3741507530212402,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 234,
    +      "longitude": 5.8714455552453835,
    +      "heading": 75.5859375,
    +      "latitude": 45.645444011358215
    +    },
    +    "timestamp": 1394788324896
    +  }, {
    +    "coords": {
    +      "speed": 3.3729660511016846,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.87150791660498,
    +      "heading": 70.3125,
    +      "latitude": 45.64547209073384
    +    },
    +    "timestamp": 1394788325971
    +  }, {
    +    "coords": {
    +      "speed": 3.463883876800537,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.871554352348551,
    +      "heading": 70.3125,
    +      "latitude": 45.64548374157925
    +    },
    +    "timestamp": 1394788327122
    +  }, {
    +    "coords": {
    +      "speed": 3.5247886180877686,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.871567260479435,
    +      "heading": 67.1484375,
    +      "latitude": 45.645496733529164
    +    },
    +    "timestamp": 1394788328164
    +  }, {
    +    "coords": {
    +      "speed": 3.455146551132202,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 6,
    +      "altitude": 235,
    +      "longitude": 5.871608583262071,
    +      "heading": 68.90625,
    +      "latitude": 45.64550293613751
    +    },
    +    "timestamp": 1394788328985
    +  }, {
    +    "coords": {
    +      "speed": 3.382997989654541,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 8,
    +      "altitude": 236,
    +      "longitude": 5.871640518313154,
    +      "heading": 78.75,
    +      "latitude": 45.6454965658911
    +    },
    +    "timestamp": 1394788329900
    +  }, {
    +    "coords": {
    +      "speed": 3.242330312728882,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 8,
    +      "altitude": 236,
    +      "longitude": 5.871667759498462,
    +      "heading": 92.4609375,
    +      "latitude": 45.64548562750746
    +    },
    +    "timestamp": 1394788331120
    +  }, {
    +    "coords": {
    +      "speed": 3.074465274810791,
    +      "accuracy": 10,
    +      "altitudeAccuracy": 8,
    +      "altitude": 236,
    +      "longitude": 5.871691312646374,
    +      "heading": 110.0390625,
    +      "latitude": 45.645468402696444
    +    },
    +    "timestamp": 1394788332219
    +  }]
    +}
    diff --git a/mapbox-gl/examples/data/geolocation_marker.png b/mapbox-gl/examples/data/geolocation_marker.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..62586406475770d280cbb3735e2c6e926cf84971
    GIT binary patch
    literal 829
    zcmV-D1H$}?P)<h;3K|Lk000e1NJLTq000&M000&U1^@s6#I$TX00004b3#c}2nYxW
    zd<bNS00009a7bBm0004E0004E0beK3Qvd(}8FWQhbW?9;ba!ELWdL_~cP?peYja~^
    zaAhuUa%Y?FJQ@H10=h{=K~y-6wbjpS({LEa@psa!TNp_lY;_A*e+-7ZI3}WqJ9_XS
    zcql^W@St}QJoyhq)I*P+#2t26L3$W?8H|AeDX5o;FhPe{X@{LKn`pBwZ5|JPGj<U>
    zp$~i@2|TZS^9?*-O+?T%O#_moRQdozB?ajGsTk-0ZIwvfcK`uIMC2f-D<G{h0Q9RE
    zv$L}!0E>%@TMFD&*#Y)cI_kxO0u2RafKlL-<2cimN~Kt<)x1`#)eu#!R;y8~)x1ij
    zQgj?=8aSnTGRiQLuS=;_Wq>hY!gbvn&1UnH9H>^*kyg|>_(+<~<|o&6ZvYdjH>0c+
    z>a#8N)(Ow^9>sASiHOwvJ-M^|Mb18bFUEs6Vmx>wXP>^8JIi0B=I@D!NF2wJ=XsA5
    zKB}yAvf2T_a$WcQ+}zw_P1B6|weMVe{t>Slu=z707KjBle@48n|Ki!&7DosBm>wO}
    zbGh7wbUOWdb#?Wd+W%MsY`}6HC%eAB{@k|h@%gpy+<x(yZtB_9liZpb#`pb=$;rv<
    z^?JPl1j&!<FPF<VZQCBN2NCyHs$HP~_g1RZg9zKU$IIpNO|=8YKAg+t&H+5EZ1Q8L
    z(>45Kr^Ca_CIHIVhf{ztG&FP?;MLclYc#2(M;Qi?(gCedC}d6390}Vo?;7Eu;dhOY
    zupMKX=18GX$O4-F&#wP09gtF~)Cj|H%QSS(WX(e(XR;=yp%aGTR;g5K0HUJ?%J%m5
    z2Y^e*tV6>|k22aoM^_iS)oS%E!2OG392rP;jUE|DasT2N0Ls_}+WT-mpI`8Of5Wl*
    zC|=HW4HqxxaI8Lj-`~jR^9u=_%w8=B0xZk=I5RVIZF+Q2Kc4C5Wi?=@Eq`6<lPf2<
    zH8qTgw2Q^!-KC|a4d54lMrdz_5cF_t_GHla>}b+@b(sDJzP{T|T4;%800000NkvXX
    Hu0mjfcPoHH
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/examples/data/geolocation_marker_heading.png b/mapbox-gl/examples/data/geolocation_marker_heading.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..5edfadccce934226dcbc7c30e4b774b07cc6991c
    GIT binary patch
    literal 1673
    zcmV;426p+0P)<h;3K|Lk000e1NJLTq000&M001Qj1^@s6Mp_v-00004b3#c}2nYxW
    zd<bNS00009a7bBm0004E0004E0beK3Qvd(}8FWQhbW?9;ba!ELWdL_~cP?peYja~^
    zaAhuUa%Y?FJQ@H11_nt)K~zYIm6va5Th|r9fA8s!Bgt=$Y)6V+FDgr_)v&H%wQNGL
    z@fPZRX&@fV4rvn_Mn)aFh0$~r;xGHqvaGP7?c2Zv1ZK#Ef$0XF>(V6D@%)Fb#LzC9
    zow}1F$+BffmSjzTp6)*AKIhq1X>#D8tNZT#-TTfv=iGA@Aq2HVQ53~!niN2o6!A3x
    zYfb7SQ2~qmRb^jAsxE(PN=>lbtpT+GcG*S(i>9%aTDw;Fc1WlqL3Y3a)W_rT-^b(e
    zcY%7@XDp_e%=Sox5W7@Dt$-6~0-g?qLg$5)P$+aBcv?Pl%CX8`j$(cblL@TA0YC!|
    zrZzUl3Rc&50qV-Rdrggv1HeNd1N;vt1G`MJ*A#ofuJQ5lGmUO{`?XsS)N8jMsEuxS
    z`}p|y8NemSSmm7E=GbEbiyZ6*d~I!QU72idbs?G4)wgGb`u40?NaplRHn-Z=*4731
    z<k$m-y$zmu$L8JE0nTtZe4*au^t>^-qE-Y{1iUf1qSm{do^UvPLE5;k)~;2PZH<p+
    z2ONEUeJw9^1YZ5{%Plc+KfSa1#QiiMez_%H=m@;p*Vos=BM-4+@(|SoRn6dFF!-9?
    zZgaf(QFNaxz4=j8vD<BqU@-XF9<XY_Ej3Kwx_b3$`{3ELzX>g*)TwBepZ~zayFY3Z
    zmyRFeaJ^mmVm*)3rYb*dZ+muodwb%}ojWO@#G`23=eLJ|V~Ip!QV1b#E^is`E7nt~
    z<@Ho*+4yffOC%DLz%e;@$FB`L+o(pAcWi8I(3o7-_0r7D%xHgq|I5IOz>EF;{V&hV
    z%#4<Gy=06V8yg!0ypmxvzTGbH!-re9gb*URw)W}J(9kPDFVF*YOYH%AhlYk;Nv^GZ
    zDufW}^wuYP!TVfnet!OZI-RzjI(6!n{3t3wAP}ern46o+%5PW!iW3tPJ#M$V5C{bR
    z0_3PBsIn0hMqs$8hOySr(9jSLhkxSt`;U7(o?|YT%L|ap<yMo)<YFWe`D}1-@UQ7~
    zI!!gSw}5S`A+95L>i`;oBf$5Ehlek2Zf-6LlN9xeDC!k+m)P9gTpS)Az6d-g=Qc`~
    zRgte-C+&0a=FOWIdwP0)r6`K^t8|gyO~;vy<yc5=1Mr;3NoT8zU!Q2f*I-8oQNDfq
    z_8)qCdv8!pOj)2{kx&PqUB7<4>)g3>zf}~)`saVG(f{5eQ!80w*%G<{T|g{bVlJLz
    z<g;~}oK`woT&mCK>vB4sbCZ*k396Y+SAcH;n&0nlou8k7Pt!EtpXOHi<-~vY23$?~
    zW8V>e5%7}9WWMU?==f<Q5{UtsofJ7bI(k9VG~eQ8fww+hc`PWvTOY5mxLH8cG~ejx
    z=mqHn)}8#&+S+;?;GL-$+oi8PBlpR6slq!`F#sgPZh$R-)$Mja3-HeexyObYV$6{Y
    zD_~Ipbzoqi+2L?BXNo%aQu!wa-%I7m6m=X9NAtkIKr^7KfQUw;1tEl}v#8kA{Ri$h
    ziA`0gv#1CmL^K*L073;S)6>&ixm<3=Zc%vN>wIGH^Ij)*i$X4!TbZ7o-U2Er)$l8A
    zY;61+py!D8#9(8NWRw71rRvbdNF*`^aP4FZPigkYhCZd)xpuMz0Lj=%LK@X@ZcHQ+
    zBVMn!>mT>iycGTq<x1^>(GaVu@VCKd`0;ldSY2J6Iehr=&wvdeYcBZIcszdh^y$-Q
    z{LRj~m!3Juos}%f?Q%`%_YOJu`&mET-_|IVN}0=-FAps&EF`FAxB^rYvy1A{a&%;5
    z<WfGLPYNMKp{$G0-F5N$2g~BbjnBo28=s5UKUfx_yX&G*)`bv4<n#ID$jHbg;HYG{
    zBy*?u(*O@}w7a|e<kHg8`{Em9X=&;G?(XiB66%o*J7B4G^dF#_?OefNu%olHv$M6e
    zwY{mS$q$f9r6RFd?2Fmi+1V>suFO+CHS$ywd=Xj4DME-^^{|a<I;~@O%>dOSsVpG{
    zsU=LuzYs#~RGTKav4BMajkfu!pi3>A+7(O{XRR{Utj0pM?jXjMVdqX_E@S@>+${xS
    Tjt<V!00000NkvXXu0mjfk2^lp
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/examples/data/gml/topp-states-wfs.xml b/mapbox-gl/examples/data/gml/topp-states-wfs.xml
    new file mode 100644
    index 0000000000..29edac4510
    --- /dev/null
    +++ b/mapbox-gl/examples/data/gml/topp-states-wfs.xml
    @@ -0,0 +1,2 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<wfs:FeatureCollection numberOfFeatures="49" timeStamp="2013-05-17T09:43:05.282Z" xsi:schemaLocation="http://www.openplans.org/topp http://demo.opengeo.org/geoserver/wfs?service=WFS&amp;version=1.1.0&amp;request=DescribeFeatureType&amp;typeName=topp%3Astates http://www.opengis.net/wfs http://demo.opengeo.org/geoserver/schemas/wfs/1.1.0/wfs.xsd" xmlns:ogc="http://www.opengis.net/ogc" xmlns:osm="http://openstreemap.org" xmlns:wfs="http://www.opengis.net/wfs" xmlns:topp="http://www.openplans.org/topp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ne="http://naturalearthdata.com"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>24.955967 -124.73142200000001</gml:lowerCorner><gml:upperCorner>49.371735 -66.969849</gml:upperCorner></gml:Envelope></gml:boundedBy><gml:featureMembers><topp:states gml:id="states.1"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.986771000000005 -91.516129</gml:lowerCorner><gml:upperCorner>42.50936100000001 -87.507889</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>37.51099000000001 -88.071564 37.476273000000006 -88.087883 37.442852 -88.311707 37.40930899999999 -88.359177 37.420292 -88.419853 37.400757 -88.467644 37.296852 -88.511322 37.257782000000006 -88.501427 37.205669 -88.450699 37.156909999999996 -88.422516 37.098670999999996 -88.45047 37.072143999999994 -88.476799 37.06818 -88.4907 37.064769999999996 -88.517273 37.072815000000006 -88.559273 37.109047000000004 -88.61422 37.13540999999999 -88.68837 37.141182 -88.739113 37.152107 -88.746506 37.202194000000006 -88.863289 37.218407 -88.932503 37.22003599999999 -88.993172 37.185860000000005 -89.065033 37.112137000000004 -89.116821 37.093185000000005 -89.146347 37.064235999999994 -89.169548 37.025711 -89.174332 36.99844 -89.150246 36.988113 -89.12986 36.986771000000005 -89.193512 37.02897299999999 -89.210052 37.041732999999994 -89.237679 37.087124 -89.264053 37.091244 -89.284233 37.085384000000005 -89.303291 37.060908999999995 -89.3097 37.027733 -89.264244 37.008686 -89.262001 36.999207 -89.282768 37.009682 -89.310982 37.049212999999995 -89.38295 37.09908299999999 -89.37999 37.137203 -89.423798 37.165318 -89.440521 37.224266 -89.468216 37.253731 -89.465309 37.256001 -89.489594 37.276402000000004 -89.513885 37.304962 -89.513885 37.329441 -89.50058 37.339409 -89.468742 37.355717 -89.435738 37.411018 -89.427574 37.453186 -89.453621 37.491726 -89.494781 37.571957 -89.524971 37.615928999999994 -89.513367 37.650375 -89.51918 37.67984 -89.513374 37.694798000000006 -89.521523 37.706103999999996 -89.581436 37.745453 -89.666458 37.78397 -89.675858 37.804794 -89.691055 37.840992 -89.728447 37.905063999999996 -89.851715 37.905486999999994 -89.861046 37.891875999999996 -89.866814 37.875904000000006 -89.900551 37.878044 -89.937874 37.911884 -89.978912 37.963634 -89.958229 37.969318 -90.010811 37.993206 -90.041924 38.032272000000006 -90.119339 38.05395100000001 -90.134712 38.08890500000001 -90.207527 38.122169000000014 -90.254059 38.16681700000001 -90.289635 38.18871300000001 -90.336716 38.23429899999999 -90.364769 38.32355899999999 -90.369347 38.36533 -90.358688 38.39084600000001 -90.339607 38.427357 -90.301842 38.518688 -90.265785 38.532768000000004 -90.26123 38.562805 -90.240944 38.61027100000001 -90.183708 38.658772 -90.183578 38.70036300000001 -90.20224 38.72396499999999 -90.196571 38.773098000000005 -90.163399 38.785484 -90.135178 38.80051 -90.121727 38.830467 -90.113121 38.85303099999999 -90.132812 38.91450900000001 -90.243927 38.92471699999999 -90.278931 38.92490799999999 -90.31974 38.96233000000001 -90.413071 38.959179000000006 -90.469841 38.89160899999999 -90.530426 38.87132600000001 -90.570328 38.880795000000006 -90.627213 38.93525299999999 -90.668877 39.037791999999996 -90.70607 39.058178 -90.707588 39.09370000000001 -90.690399 39.14421100000001 -90.716736 39.195873000000006 -90.718193 39.22474700000001 -90.732338 39.24780999999999 -90.738083 39.29680300000001 -90.779343 39.35045199999999 -90.850494 39.40058500000001 -90.947891 39.444412 -91.036339 39.473984 -91.064384 39.52892700000001 -91.093613 39.552593 -91.156189 39.600021 -91.203247 39.68591699999999 -91.317665 39.724639999999994 -91.367088 39.76127199999999 -91.373421 39.80377200000001 -91.381714 39.86304899999999 -91.449188 39.885242000000005 -91.450989 39.90182899999999 -91.434052 39.92183700000001 -91.430389 39.94606400000001 -91.447243 40.005753 -91.487289 40.066711 -91.504005 40.134544000000005 -91.516129 40.200458999999995 -91.506546 40.25137699999999 -91.498932 40.309624000000014 -91.486694 40.371902000000006 -91.448593 40.386875 -91.418816 40.392360999999994 -91.385757 40.40298799999999 -91.372757 40.44725 -91.385399 40.50365400000001 -91.374794 40.52849599999999 -91.382103 40.54799299999999 -91.412872 40.572970999999995 -91.411118 40.60343900000001 -91.37561 40.639545 -91.262062 40.64381800000001 -91.214912 40.65631099999999 -91.162498 40.68214800000001 -91.129158 40.70540199999999 -91.119987 40.76154700000001 -91.092751 40.833729000000005 -91.088905 40.87958499999999 -91.04921 40.92392699999999 -90.983276 40.950503999999995 -90.960709 41.07036199999999 -90.954651 41.10435899999999 -90.957787 41.14437100000001 -90.990341 41.16582500000001 -91.018257 41.17625799999999 -91.05632 41.23152200000001 -91.101524 41.267818000000005 -91.102348 41.334895999999986 -91.07328 41.40137899999999 -91.055786 41.423508 -91.027489 41.431084 -91.000694 41.421234 -90.949654 41.44462200000001 -90.844139 41.449820999999986 -90.7799 41.450062 -90.708214 41.46231800000001 -90.658791 41.50958600000001 -90.6007 41.52597 -90.54084 41.527546 -90.454994 41.543578999999994 -90.434967 41.567272 -90.423004 41.586849 -90.348366 41.60279800000001 -90.339348 41.64909 -90.341133 41.722736 -90.326027 41.75646599999999 -90.304886 41.78173799999999 -90.25531 41.80613700000001 -90.195839 41.93077500000001 -90.154518 41.98396299999999 -90.14267 42.03342799999999 -90.150536 42.06104300000001 -90.168098 42.103745 -90.166649 42.12050199999999 -90.176086 42.12268800000001 -90.191574 42.15972099999999 -90.230934 42.19731899999999 -90.323601 42.21020899999999 -90.367729 42.24264500000001 -90.407173 42.263924 -90.417984 42.340633 -90.427681 42.360073 -90.441597 42.38878299999999 -90.491043 42.42183700000001 -90.563583 42.46055999999999 -90.605827 42.47564299999999 -90.648346 42.494698 -90.651772 42.50936100000001 -90.638329 42.508362000000005 -90.419975 42.504108 -89.923569 42.50345999999999 -89.834618 42.49749 -89.400497 42.497906 -89.359444 42.49086399999999 -88.939079 42.490905999999995 -88.764954 42.489655 -88.70652 42.49197000000001 -88.297897 42.48961299999999 -88.194702 42.48913200000001 -87.79731 42.314212999999995 -87.836945 42.15645599999999 -87.760239 42.059822 -87.670547 41.847331999999994 -87.612625 41.723591 -87.529861 41.46971500000001 -87.532646 41.30130399999999 -87.532448 41.173756 -87.531731 41.00993 -87.532021 40.74541099999999 -87.532669 40.494609999999994 -87.53717 40.48324600000001 -87.535675 40.16619499999999 -87.535339 39.887302000000005 -87.535774 39.609341 -87.535576 39.47744800000001 -87.538567 39.350525000000005 -87.540215 39.338268 -87.597664 39.30740399999999 -87.625237 39.297661000000005 -87.610619 39.281418 -87.615799 39.258162999999996 -87.606895 39.248752999999994 -87.584564 39.20846599999999 -87.588593 39.198128 -87.594208 39.196068 -87.607925 39.168507000000005 -87.644257 39.146679000000006 -87.670326 39.130652999999995 -87.659454 39.11346800000001 -87.662262 39.10394299999999 -87.631668 39.08897400000001 -87.630867 39.08460600000001 -87.612007 39.062434999999994 -87.58532 38.995743000000004 -87.581749 38.99408299999999 -87.591858 38.97707700000001 -87.547905 38.96370300000001 -87.53347 38.93191899999999 -87.530182 38.90486100000001 -87.5392 38.869811999999996 -87.559059 38.857890999999995 -87.550507 38.795559 -87.507889 38.77669900000001 -87.519028 38.769722 -87.508003 38.73663300000001 -87.508316 38.68597399999999 -87.543892 38.672169 -87.588478 38.642810999999995 -87.625191 38.622917 -87.628647 38.599209 -87.619827 38.593177999999995 -87.640594 38.573871999999994 -87.652855 38.54742400000001 -87.672943 38.51536899999999 -87.65139 38.50044299999999 -87.653534 38.50400500000001 -87.679909 38.48153300000001 -87.692818 38.466125000000005 -87.756096 38.45709600000001 -87.758659 38.44548 -87.738953 38.41796500000001 -87.748428 38.378124000000014 -87.784019 38.35252399999999 -87.834503 38.28609800000001 -87.850082 38.28536199999999 -87.863007 38.316788 -87.874039 38.315552 -87.883446 38.300658999999996 -87.888466 38.281048 -87.914108 38.302345 -87.913651 38.30477099999999 -87.925919 38.241085 -87.980019 38.234814 -87.986008 38.200714000000005 -87.977928 38.171131 -87.932289 38.15752800000001 -87.931992 38.13691299999999 -87.950569 38.131760000000014 -87.973503 38.10330200000001 -88.018547 38.09234599999999 -88.012329 38.09674799999999 -87.964867 38.073307 -87.975296 38.054084999999986 -88.034729 38.04512 -88.043091 38.03830300000001 -88.041473 38.03353100000001 -88.021698 38.00823600000001 -88.029213 37.975055999999995 -88.021706 37.956264000000004 -88.042511 37.934498000000005 -88.041771 37.929783 -88.064621 37.944 -88.078941 37.92366 -88.084 37.917591 -88.030441 37.905758000000006 -88.026588 37.896004000000005 -88.044868 37.90617 -88.100082 37.895306000000005 -88.101456 37.867808999999994 -88.075737 37.843745999999996 -88.034241 37.827522 -88.042137 37.831249 -88.089264 37.817612 -88.086029 37.805683 -88.035576 37.735400999999996 -88.072472 37.700745 -88.133636 37.660686 -88.15937 37.628479 -88.157631 37.583572000000004 -88.134171 37.51099000000001 -88.071564</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Illinois</topp:STATE_NAME><topp:STATE_FIPS>17</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IL</topp:STATE_ABBR><topp:LAND_KM>143986.61</topp:LAND_KM><topp:WATER_KM>1993.335</topp:WATER_KM><topp:PERSONS>1.1430602E7</topp:PERSONS><topp:FAMILIES>2924880.0</topp:FAMILIES><topp:HOUSHOLD>4202240.0</topp:HOUSHOLD><topp:MALE>5552233.0</topp:MALE><topp:FEMALE>5878369.0</topp:FEMALE><topp:WORKERS>4199206.0</topp:WORKERS><topp:DRVALONE>3741715.0</topp:DRVALONE><topp:CARPOOL>652603.0</topp:CARPOOL><topp:PUBTRANS>538071.0</topp:PUBTRANS><topp:EMPLOYED>5417967.0</topp:EMPLOYED><topp:UNEMPLOY>385040.0</topp:UNEMPLOY><topp:SERVICE>1360159.0</topp:SERVICE><topp:MANUAL>828906.0</topp:MANUAL><topp:P_MALE>0.486</topp:P_MALE><topp:P_FEMALE>0.514</topp:P_FEMALE><topp:SAMP_POP>1747776.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.2"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.78811999999999 -77.122627</gml:lowerCorner><gml:upperCorner>38.99343099999999 -76.911209</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.966556999999995 -77.008232 38.88998799999999 -76.911209 38.78811999999999 -77.045448 38.81391500000001 -77.035248 38.829364999999996 -77.045189 38.838413 -77.040405 38.86243099999999 -77.039078 38.886101 -77.067886 38.91560000000001 -77.078949 38.93206000000001 -77.122627 38.99343099999999 -77.042389 38.966556999999995 -77.008232</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>District of Columbia</topp:STATE_NAME><topp:STATE_FIPS>11</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DC</topp:STATE_ABBR><topp:LAND_KM>159.055</topp:LAND_KM><topp:WATER_KM>17.991</topp:WATER_KM><topp:PERSONS>606900.0</topp:PERSONS><topp:FAMILIES>122087.0</topp:FAMILIES><topp:HOUSHOLD>249634.0</topp:HOUSHOLD><topp:MALE>282970.0</topp:MALE><topp:FEMALE>323930.0</topp:FEMALE><topp:WORKERS>229975.0</topp:WORKERS><topp:DRVALONE>106694.0</topp:DRVALONE><topp:CARPOOL>36621.0</topp:CARPOOL><topp:PUBTRANS>111422.0</topp:PUBTRANS><topp:EMPLOYED>303994.0</topp:EMPLOYED><topp:UNEMPLOY>23442.0</topp:UNEMPLOY><topp:SERVICE>65498.0</topp:SERVICE><topp:MANUAL>22407.0</topp:MANUAL><topp:P_MALE>0.466</topp:P_MALE><topp:P_FEMALE>0.534</topp:P_FEMALE><topp:SAMP_POP>72696.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.3"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.44949 -75.791435</gml:lowerCorner><gml:upperCorner>39.84000800000001 -75.045998</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.55747600000001 -75.70742 38.649551 -75.71106 38.83017000000001 -75.724937 39.141548 -75.752922 39.24775299999999 -75.761658 39.295849000000004 -75.764664 39.38300699999999 -75.772697 39.72375500000001 -75.791435 39.72444200000001 -75.775269 39.77481800000001 -75.745934 39.820347 -75.695114 39.83819600000001 -75.644341 39.84000800000001 -75.583794 39.826435000000004 -75.470345 39.798869999999994 -75.42083 39.789658 -75.412117 39.778130000000004 -75.428009 39.763248000000004 -75.460754 39.74171799999999 -75.475128 39.71997099999999 -75.476334 39.71474499999999 -75.489639 39.61279300000001 -75.610725 39.566722999999996 -75.562996 39.46376799999999 -75.590187 39.36694 -75.515572 39.25763699999999 -75.402481 39.073036 -75.397728 39.01238599999999 -75.324852 38.945910999999995 -75.307899 38.808670000000006 -75.190941 38.799812 -75.083138 38.44949 -75.045998 38.449963 -75.068298 38.45045099999999 -75.093094 38.455208 -75.350204 38.463066 -75.69915 38.55747600000001 -75.70742</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Delaware</topp:STATE_NAME><topp:STATE_FIPS>10</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>DE</topp:STATE_ABBR><topp:LAND_KM>5062.456</topp:LAND_KM><topp:WATER_KM>1385.022</topp:WATER_KM><topp:PERSONS>666168.0</topp:PERSONS><topp:FAMILIES>175867.0</topp:FAMILIES><topp:HOUSHOLD>247497.0</topp:HOUSHOLD><topp:MALE>322968.0</topp:MALE><topp:FEMALE>343200.0</topp:FEMALE><topp:WORKERS>247566.0</topp:WORKERS><topp:DRVALONE>258087.0</topp:DRVALONE><topp:CARPOOL>42968.0</topp:CARPOOL><topp:PUBTRANS>8069.0</topp:PUBTRANS><topp:EMPLOYED>335147.0</topp:EMPLOYED><topp:UNEMPLOY>13945.0</topp:UNEMPLOY><topp:SERVICE>87973.0</topp:SERVICE><topp:MANUAL>44140.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>102776.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.4"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>37.204796 -82.647278</gml:lowerCorner><gml:upperCorner>40.63713799999999 -77.727753</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.480372999999986 -79.231903 38.437183000000005 -79.272598 38.41251 -79.317238 38.46202500000001 -79.48658 38.553687999999994 -79.536743 38.592239000000006 -79.642632 38.55006 -79.669655 38.52065999999999 -79.66584 38.500237 -79.692879 38.43011899999999 -79.684319 38.394566 -79.720261 38.351719 -79.733055 38.35387 -79.764229 38.31420499999999 -79.800552 38.29874799999999 -79.803001 38.28499600000001 -79.786736 38.268542999999994 -79.793846 38.250156000000004 -79.831375 38.17914200000001 -79.916382 38.16248300000001 -79.910561 38.121185 -79.935547 38.10318799999999 -79.928513 38.06724199999999 -79.957733 38.038498000000004 -79.966713 37.989746 -80.000717 37.955524 -80.055023 37.914536 -80.106705 37.891155 -80.118721 37.877106 -80.160217 37.860062 -80.172432 37.842845999999994 -80.171806 37.80224200000001 -80.223946 37.778735999999995 -80.220757 37.757110999999995 -80.254898 37.725928999999994 -80.250244 37.682548999999995 -80.303322 37.671379 -80.295914 37.652122000000006 -80.305077 37.640423 -80.301147 37.640579 -80.254639 37.624142000000006 -80.219147 37.596771000000004 -80.246613 37.566593 -80.31694 37.533276 -80.326126 37.528244 -80.308525 37.53613300000001 -80.280952 37.511024000000006 -80.288139 37.491051 -80.347733 37.475975000000005 -80.352379 37.465599 -80.388527 37.43478 -80.425598 37.422695000000004 -80.474983 37.433735 -80.487015 37.460471999999996 -80.488098 37.47492200000001 -80.50898 37.469086000000004 -80.542969 37.445930000000004 -80.597702 37.388256 -80.705414 37.392596999999995 -80.729942 37.387615 -80.746529 37.378960000000006 -80.747894 37.371292 -80.763199 37.386073999999994 -80.770226 37.391632 -80.799446 37.412941000000004 -80.799812 37.423339999999996 -80.850723 37.388577 -80.877556 37.350821999999994 -80.848618 37.339291 -80.855629 37.301249999999996 -80.93438 37.291672000000005 -80.968086 37.296356 -80.978729 37.306122 -80.98613 37.285942000000006 -81.025124 37.274806999999996 -81.140923 37.240097000000006 -81.223114 37.293591000000006 -81.31205 37.338837 -81.358971 37.311038999999994 -81.391121 37.282509000000005 -81.403519 37.254307 -81.475533 37.252735 -81.495705 37.234257 -81.505707 37.206238 -81.556824 37.204796 -81.666054 37.235321 -81.701897 37.250378 -81.738625 37.272144 -81.752022 37.287041 -81.792824 37.279427 -81.815544 37.285393 -81.83905 37.30691899999999 -81.858841 37.325344 -81.863976 37.34047700000001 -81.897316 37.371616 -81.926979 37.41540500000001 -81.920891 37.466476 -81.988358 37.48279599999999 -81.976578 37.492915999999994 -81.948151 37.506535 -81.935608 37.531063 -81.959732 37.543144 -81.97673 37.530411 -82.026505 37.551345999999995 -82.049286 37.525234 -82.055817 37.548203 -82.084541 37.557345999999995 -82.142654 37.565815 -82.146667 37.56979 -82.137619 37.590430999999995 -82.131927 37.593464 -82.159454 37.640564 -82.185692 37.623913 -82.205582 37.656673 -82.23851 37.668957000000006 -82.295761 37.744072 -82.329536 37.758326999999994 -82.319633 37.784302 -82.339981 37.811623 -82.405937 37.872260999999995 -82.421638 37.894760000000005 -82.437737 37.922169 -82.500336 37.942420999999996 -82.493523 37.95430399999999 -82.480301 37.975815 -82.475906 38.01557199999999 -82.524803 38.10987499999999 -82.59333 38.146243999999996 -82.646248 38.16935000000001 -82.647278 38.178009 -82.613884 38.19374099999999 -82.606766 38.23872800000001 -82.616348 38.245304000000004 -82.589233 38.255889999999994 -82.574699 38.292427 -82.58017 38.307728 -82.572418 38.368382 -82.598358 38.41243700000001 -82.586723 38.403819999999996 -82.575539 38.400429 -82.547668 38.40575000000001 -82.49511 38.430308999999994 -82.415016 38.42838699999999 -82.39489 38.441868 -82.329308 38.46514500000001 -82.314369 38.57999799999999 -82.2901 38.594807 -82.271027 38.58475100000001 -82.213791 38.59494799999999 -82.18438 38.63210699999999 -82.17379 38.67781099999999 -82.18911 38.71021999999999 -82.184105 38.778858000000014 -82.216881 38.804539000000005 -82.197853 38.838707 -82.146233 38.89931899999999 -82.13945 38.95201499999999 -82.101341 38.97711899999999 -82.085152 38.98898700000001 -82.05864 39.014061 -82.043022 39.01518200000001 -81.999817 38.992928000000006 -81.975327 38.991096 -81.937874 38.98419200000001 -81.927971 38.932143999999994 -81.89875 38.89466100000001 -81.931992 38.884365 -81.91539 38.87337099999999 -81.892838 38.885628 -81.866943 38.93780899999999 -81.841057 38.948387 -81.823921 38.92348100000001 -81.783371 38.93009900000001 -81.762444 38.96844899999999 -81.781876 39.01675 -81.775826 39.044028999999995 -81.813606 39.066338 -81.824417 39.07693900000001 -81.81971 39.077179 -81.786507 39.09464299999999 -81.753708 39.125797000000006 -81.74485 39.17567399999999 -81.759056 39.21319199999999 -81.723221 39.219944 -81.698051 39.26015100000001 -81.689674 39.27042 -81.667671 39.26584199999999 -81.572838 39.332581000000005 -81.557541 39.352633999999995 -81.540802 39.40678399999999 -81.465164 39.410954000000004 -81.448112 39.40594899999999 -81.434135 39.34561500000001 -81.376076 39.35356899999999 -81.338997 39.38699700000001 -81.28418 39.388397 -81.237785 39.40828300000001 -81.225113 39.415820999999994 -81.20047 39.437725 -81.180733 39.46770900000001 -81.117256 39.496376 -81.098412 39.532589 -81.037552 39.54406700000001 -81.032738 39.58172999999999 -80.983818 39.606865 -80.932785 39.60727700000001 -80.912766 39.62400400000001 -80.881287 39.66233399999999 -80.872925 39.680274999999995 -80.863594 39.70332300000001 -80.83297 39.71875800000001 -80.832481 39.73625899999999 -80.856636 39.759918 -80.870911 39.808926000000014 -80.81929 39.83959200000001 -80.826103 39.85664700000001 -80.798714 39.87227200000001 -80.791039 39.90482700000001 -80.812325 39.915829 -80.808029 39.91976500000001 -80.796211 39.913239000000004 -80.768318 39.92119199999999 -80.759079 39.94694100000001 -80.763252 39.98340200000001 -80.739082 40.03559100000001 -80.738434 40.15401800000001 -80.702263 40.16811000000001 -80.701088 40.194080000000014 -80.678757 40.245609 -80.650314 40.276432 -80.614891 40.306174999999996 -80.604721 40.37320700000001 -80.609451 40.38859600000001 -80.629448 40.39815899999999 -80.628052 40.48047299999999 -80.602036 40.50439800000001 -80.625458 40.539139000000006 -80.633644 40.56821400000001 -80.668823 40.58207300000001 -80.667931 40.61391800000001 -80.637543 40.61999900000001 -80.611755 40.615910000000014 -80.574623 40.63713799999999 -80.522209 40.478717999999986 -80.524567 40.40296599999999 -80.523773 40.16244900000001 -80.526253 40.022751 -80.525169 39.95834400000001 -80.524857 39.721126999999996 -80.524467 39.719761000000005 -80.429283 39.721588 -79.918488 39.72172900000001 -79.765358 39.72019599999999 -79.481209 39.19728900000001 -79.490089 39.21315799999999 -79.461418 39.21198699999999 -79.449509 39.269195999999994 -79.385078 39.291988 -79.346428 39.300438000000014 -79.295509 39.325142 -79.28006 39.34854100000001 -79.260406 39.39339799999999 -79.163261 39.41386399999999 -79.158371 39.416934999999995 -79.131645 39.44721200000001 -79.104324 39.46453500000001 -79.096962 39.47077899999999 -79.104843 39.47075699999999 -79.070877 39.48573300000001 -79.064659 39.483723 -79.049103 39.43843100000001 -78.970688 39.460364999999996 -78.955643 39.5257 -78.871071 39.56322900000001 -78.838371 39.566734 -78.806808 39.585609000000005 -78.82267 39.61533 -78.798729 39.63074499999999 -78.798409 39.644153999999986 -78.772964 39.62652600000001 -78.767876 39.62687700000001 -78.732643 39.621455999999995 -78.730759 39.60870399999999 -78.736511 39.601528 -78.774002 39.581703000000005 -78.761711 39.57655299999999 -78.732979 39.559483 -78.716576 39.536838999999986 -78.666687 39.53790699999999 -78.649406 39.52985799999999 -78.637344 39.53559100000001 -78.60463 39.520981000000006 -78.564453 39.52506600000001 -78.509048 39.519844000000006 -78.481544 39.533641999999986 -78.456078 39.548225 -78.446136 39.549316000000005 -78.421089 39.58074199999999 -78.462082 39.592613 -78.450897 39.58752100000001 -78.404266 39.62086099999999 -78.432159 39.61440300000001 -78.384903 39.631226 -78.377937 39.632278000000014 -78.357048 39.640499000000005 -78.34819 39.61831699999999 -78.273285 39.641075 -78.257996 39.65847400000001 -78.2295 39.67389700000001 -78.227867 39.67584600000001 -78.204575 39.69454999999999 -78.183243 39.67550700000001 -78.094612 39.622772 -78.026695 39.598873 -77.995483 39.61122900000001 -77.964516 39.585915 -77.945259 39.59184300000001 -77.935738 39.61491799999999 -77.947823 39.618122 -77.938889 39.59602699999999 -77.903534 39.600605 -77.891022 39.61647400000001 -77.888718 39.60207 -77.855804 39.605278 -77.84269 39.572643 -77.840149 39.565357000000006 -77.853241 39.56435400000001 -77.885452 39.557995000000005 -77.890198 39.545815000000005 -77.869843 39.51455300000001 -77.864914 39.531834 -77.844131 39.52551299999999 -77.835747 39.529156 -77.829208 39.51194000000001 -77.825546 39.50190699999999 -77.84816 39.493809 -77.82547 39.49801600000001 -77.771835 39.480728 -77.799782 39.459002999999996 -77.785393 39.46303900000001 -77.804474 39.450817 -77.796082 39.439919 -77.804977 39.43221700000001 -77.802559 39.42506399999999 -77.757271 39.40333899999999 -77.741119 39.396095 -77.737518 39.378376 -77.756508 39.36027100000001 -77.745735 39.338493 -77.754585 39.326717 -77.750374 39.31769600000001 -77.727753 39.28454199999999 -77.759743 39.24644900000001 -77.768517 39.19650300000001 -77.805733 39.14162099999999 -77.820328 39.13207600000001 -77.830963 39.265536999999995 -78.033607 39.39101400000001 -78.229782 39.42336700000001 -78.277153 39.456902000000014 -78.347816 39.38073 -78.350502 39.36158800000001 -78.365746 39.35085699999999 -78.3442 39.341358000000014 -78.341118 39.257439000000005 -78.413818 39.244850000000014 -78.399399 39.21204 -78.42334 39.19752500000001 -78.424339 39.17049 -78.402634 39.14852099999999 -78.43084 39.118931 -78.44825 39.111839 -78.485519 39.09357800000001 -78.501869 39.05702600000001 -78.536919 39.035037999999986 -78.564445 39.02338 -78.549469 39.01382799999999 -78.553474 38.967197 -78.598961 38.979603 -78.631111 38.95044300000001 -78.647232 38.92157399999999 -78.680489 38.90488099999999 -78.719246 38.93021400000001 -78.724403 38.92917299999999 -78.737991 38.911381000000006 -78.749512 38.88010800000001 -78.793312 38.83363299999999 -78.816116 38.76329000000001 -78.866814 38.84664900000001 -78.987701 38.799847 -79.033989 38.79051999999999 -79.055046 38.76194000000001 -79.056801 38.707153000000005 -79.087479 38.659088 -79.088791 38.66365099999999 -79.121307 38.65812700000001 -79.12767 38.480372999999986 -79.231903</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>West Virginia</topp:STATE_NAME><topp:STATE_FIPS>54</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>WV</topp:STATE_ABBR><topp:LAND_KM>62384.2</topp:LAND_KM><topp:WATER_KM>375.199</topp:WATER_KM><topp:PERSONS>1793477.0</topp:PERSONS><topp:FAMILIES>500259.0</topp:FAMILIES><topp:HOUSHOLD>688557.0</topp:HOUSHOLD><topp:MALE>861536.0</topp:MALE><topp:FEMALE>931941.0</topp:FEMALE><topp:WORKERS>661702.0</topp:WORKERS><topp:DRVALONE>493164.0</topp:DRVALONE><topp:CARPOOL>106918.0</topp:CARPOOL><topp:PUBTRANS>7237.0</topp:PUBTRANS><topp:EMPLOYED>671085.0</topp:EMPLOYED><topp:UNEMPLOY>71142.0</topp:UNEMPLOY><topp:SERVICE>205950.0</topp:SERVICE><topp:MANUAL>124172.0</topp:MANUAL><topp:P_MALE>0.48</topp:P_MALE><topp:P_FEMALE>0.52</topp:P_FEMALE><topp:SAMP_POP>317564.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.5"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>37.970130999999995 -79.490089</gml:lowerCorner><gml:upperCorner>39.725368 -75.045998</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.649551 -75.71106 38.55747600000001 -75.70742 38.463066 -75.69915 38.455208 -75.350204 38.45045099999999 -75.093094 38.36962500000001 -75.155243 38.273765999999995 -75.150986 38.201415999999995 -75.262863 38.06891999999999 -75.373428 38.01671200000001 -75.37278 37.996418000000006 -75.626434 37.970130999999995 -75.648216 37.979656000000006 -75.86573 38.09724800000001 -75.769554 38.17493400000001 -75.897797 38.231594 -75.838074 38.24004400000001 -75.861671 38.26360299999999 -75.794174 38.258872999999994 -75.894958 38.35723100000001 -75.872437 38.375462 -75.886826 38.282055000000014 -75.949928 38.282523999999995 -75.995285 38.32189600000001 -76.020805 38.25893400000001 -76.06546 38.436935000000005 -76.294273 38.47872899999999 -76.291985 38.54334299999999 -76.192215 38.59508099999999 -76.251053 38.571922 -76.031937 38.622005 -76.028061 38.591858 -76.046921 38.610783 -76.075996 38.707977 -76.124039 38.70909499999999 -76.174042 38.76279099999999 -76.223312 38.769886000000014 -76.26712 38.67937499999999 -76.337624 38.69902400000001 -76.350533 38.834 -76.272415 38.765254999999996 -76.195175 38.78860900000001 -76.165855 38.885456000000005 -76.114372 38.88954899999999 -76.075867 38.89801800000001 -76.102898 38.94813199999999 -76.095497 38.920715 -76.113869 38.973354 -76.199669 39.118595 -76.111282 39.092918 -76.221771 39.13082499999999 -76.238892 39.204853000000014 -76.218437 39.32129699999999 -76.112373 39.35836800000001 -76.037422 39.37913900000001 -75.849739 39.39455000000001 -75.978798 39.471180000000004 -75.952637 39.524021000000005 -75.974762 39.56992700000001 -76.03141 39.542362 -76.078506 39.401936000000006 -76.154526 39.374888999999996 -76.226662 39.393280000000004 -76.364029 39.231144 -76.39904 39.24261899999999 -76.531296 39.259354 -76.604027 39.23144500000001 -76.565132 39.19811999999999 -76.576981 39.180983999999995 -76.60733 39.15868800000001 -76.595139 39.19626600000001 -76.563919 39.11835500000001 -76.423935 38.908237000000014 -76.472038 38.758972 -76.549126 38.709633 -76.525253 38.522099 -76.508896 38.39128099999999 -76.385811 38.34602000000001 -76.405457 38.32049900000001 -76.421463 38.33565899999999 -76.471924 38.41013699999999 -76.520081 38.450424 -76.647255 38.21306200000001 -76.343781 38.045704 -76.33017 38.22263699999999 -76.577271 38.23428000000001 -76.760239 38.391346 -76.864182 38.29984999999999 -76.908577 38.331028 -76.97303 38.426852999999994 -77.002396 38.39066299999999 -77.220924 38.41359299999999 -77.255875 38.487099 -77.277756 38.64812499999999 -77.12999 38.67779899999999 -77.125114 38.703982999999994 -77.093147 38.71527900000001 -77.081879 38.71202099999999 -77.057121 38.71878100000001 -77.046471 38.78811999999999 -77.045448 38.88998799999999 -76.911209 38.966556999999995 -77.008232 38.99343099999999 -77.042389 38.93206000000001 -77.122627 38.96477899999999 -77.152046 38.97587999999999 -77.243729 39.02757299999999 -77.255989 39.062588000000005 -77.3246 39.068512 -77.346519 39.066776000000004 -77.433037 39.080837 -77.459694 39.103958000000006 -77.479248 39.116653000000014 -77.513046 39.157444 -77.516617 39.17693299999999 -77.478638 39.218632000000014 -77.461998 39.22905700000001 -77.464958 39.249911999999995 -77.494064 39.268939999999986 -77.542191 39.298393000000004 -77.568962 39.29971699999999 -77.616524 39.31868 -77.679588 39.31769600000001 -77.727753 39.326717 -77.750374 39.338493 -77.754585 39.36027100000001 -77.745735 39.378376 -77.756508 39.396095 -77.737518 39.40333899999999 -77.741119 39.42506399999999 -77.757271 39.43221700000001 -77.802559 39.439919 -77.804977 39.450817 -77.796082 39.46303900000001 -77.804474 39.459002999999996 -77.785393 39.480728 -77.799782 39.49801600000001 -77.771835 39.493809 -77.82547 39.50190699999999 -77.84816 39.51194000000001 -77.825546 39.529156 -77.829208 39.52551299999999 -77.835747 39.531834 -77.844131 39.51455300000001 -77.864914 39.545815000000005 -77.869843 39.557995000000005 -77.890198 39.56435400000001 -77.885452 39.565357000000006 -77.853241 39.572643 -77.840149 39.605278 -77.84269 39.60207 -77.855804 39.61647400000001 -77.888718 39.600605 -77.891022 39.59602699999999 -77.903534 39.618122 -77.938889 39.61491799999999 -77.947823 39.59184300000001 -77.935738 39.585915 -77.945259 39.61122900000001 -77.964516 39.598873 -77.995483 39.622772 -78.026695 39.67550700000001 -78.094612 39.69454999999999 -78.183243 39.67584600000001 -78.204575 39.67389700000001 -78.227867 39.65847400000001 -78.2295 39.641075 -78.257996 39.61831699999999 -78.273285 39.640499000000005 -78.34819 39.632278000000014 -78.357048 39.631226 -78.377937 39.61440300000001 -78.384903 39.62086099999999 -78.432159 39.58752100000001 -78.404266 39.592613 -78.450897 39.58074199999999 -78.462082 39.549316000000005 -78.421089 39.548225 -78.446136 39.533641999999986 -78.456078 39.519844000000006 -78.481544 39.52506600000001 -78.509048 39.520981000000006 -78.564453 39.53559100000001 -78.60463 39.52985799999999 -78.637344 39.53790699999999 -78.649406 39.536838999999986 -78.666687 39.559483 -78.716576 39.57655299999999 -78.732979 39.581703000000005 -78.761711 39.601528 -78.774002 39.60870399999999 -78.736511 39.621455999999995 -78.730759 39.62687700000001 -78.732643 39.62652600000001 -78.767876 39.644153999999986 -78.772964 39.63074499999999 -78.798409 39.61533 -78.798729 39.585609000000005 -78.82267 39.566734 -78.806808 39.56322900000001 -78.838371 39.5257 -78.871071 39.460364999999996 -78.955643 39.43843100000001 -78.970688 39.483723 -79.049103 39.48573300000001 -79.064659 39.47075699999999 -79.070877 39.47077899999999 -79.104843 39.46453500000001 -79.096962 39.44721200000001 -79.104324 39.416934999999995 -79.131645 39.41386399999999 -79.158371 39.39339799999999 -79.163261 39.34854100000001 -79.260406 39.325142 -79.28006 39.300438000000014 -79.295509 39.291988 -79.346428 39.269195999999994 -79.385078 39.21198699999999 -79.449509 39.21315799999999 -79.461418 39.19728900000001 -79.490089 39.72019599999999 -79.481209 39.719234 -79.396851 39.722252 -78.930428 39.723029999999994 -78.818016 39.723659999999995 -78.385048 39.724007 -78.334816 39.725368 -78.096222 39.719528 -77.476082 39.719978 -77.464722 39.72058100000001 -77.221344 39.72079099999999 -76.997108 39.72115299999999 -76.790794 39.72016099999999 -76.570145 39.72174799999999 -76.233444 39.72212200000001 -76.139549 39.72375500000001 -75.791435 39.38300699999999 -75.772697 39.295849000000004 -75.764664 39.24775299999999 -75.761658 39.141548 -75.752922 38.83017000000001 -75.724937 38.649551 -75.71106</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.90772200000001 -76.293129 38.967567 -76.29451 38.956661 -76.339066 38.941925 -76.314453 38.91208599999999 -76.322632 38.924057000000005 -76.342545 38.875889 -76.329712 38.85410300000001 -76.375565 38.95818700000001 -76.356796 39.040595999999994 -76.299812 38.97889699999999 -76.248146 38.92357999999999 -76.246735 38.94923 -76.273514 38.90772200000001 -76.293129</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.449963 -75.068298 38.44949 -75.045998 38.322945000000004 -75.087692 38.449963 -75.068298</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.02758800000001 -75.270721 38.037903 -75.244461 38.094177 -75.209755 38.204845000000006 -75.164742 38.320201999999995 -75.094398 38.124187000000006 -75.17318 38.028526 -75.242584 38.02758800000001 -75.270721</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Maryland</topp:STATE_NAME><topp:STATE_FIPS>24</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>MD</topp:STATE_ABBR><topp:LAND_KM>25316.345</topp:LAND_KM><topp:WATER_KM>6188.794</topp:WATER_KM><topp:PERSONS>4781468.0</topp:PERSONS><topp:FAMILIES>1245814.0</topp:FAMILIES><topp:HOUSHOLD>1748991.0</topp:HOUSHOLD><topp:MALE>2318671.0</topp:MALE><topp:FEMALE>2462797.0</topp:FEMALE><topp:WORKERS>1783061.0</topp:WORKERS><topp:DRVALONE>1732837.0</topp:DRVALONE><topp:CARPOOL>376449.0</topp:CARPOOL><topp:PUBTRANS>202169.0</topp:PUBTRANS><topp:EMPLOYED>2481342.0</topp:EMPLOYED><topp:UNEMPLOY>111536.0</topp:UNEMPLOY><topp:SERVICE>586994.0</topp:SERVICE><topp:MANUAL>260308.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>684773.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.6"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.988972000000004 -109.055199</gml:lowerCorner><gml:upperCorner>41.00341 -102.036758</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>37.641459999999995 -102.043999 37.386261000000005 -102.041557 36.988972000000004 -102.036758 36.998504999999994 -102.997223 36.999741 -103.077377 36.994445999999996 -103.993111 36.993183 -105.145615 36.992580000000004 -105.212532 36.994541 -105.712891 36.99227500000001 -105.991425 36.991493000000006 -106.471588 36.989491 -106.860657 36.999072999999996 -106.889778 36.997519999999994 -107.410217 36.998772 -107.471855 36.999474000000006 -108.371834 36.996643000000006 -109.047821 37.630829000000006 -109.044937 37.887428 -109.042542 38.15294299999999 -109.042801 38.24493000000001 -109.055199 38.494663 -109.053284 39.36098899999999 -109.050751 39.51819599999999 -109.052864 39.65741 -109.051888 40.210544999999996 -109.050591 40.665329000000014 -109.045479 40.99847399999999 -109.047638 41.00341 -107.918037 41.000168 -107.303436 40.998489000000006 -106.864838 41.001316 -106.328545 41.000111000000004 -106.202896 40.996365 -105.278259 40.994305 -104.933968 41.00322700000001 -104.051201 40.999663999999996 -103.571823 41.000331999999986 -103.382469 40.99813499999999 -102.651802 41.000225 -102.620789 40.998076999999995 -102.047279 40.743134 -102.046532 40.69732300000001 -102.04557 40.431084 -102.047157 40.34265099999999 -102.047081 39.99892800000001 -102.051071 39.56869900000001 -102.048981 39.56280899999999 -102.04834 39.12675100000001 -102.047417 39.03699900000001 -102.048515 38.69253900000001 -102.047112 38.615486000000004 -102.047127 38.263329 -102.04509 38.253806999999995 -102.045601 37.734386 -102.043518 37.641459999999995 -102.043999</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Colorado</topp:STATE_NAME><topp:STATE_FIPS>08</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>CO</topp:STATE_ABBR><topp:LAND_KM>268659.501</topp:LAND_KM><topp:WATER_KM>960.364</topp:WATER_KM><topp:PERSONS>3294394.0</topp:PERSONS><topp:FAMILIES>854214.0</topp:FAMILIES><topp:HOUSHOLD>1282489.0</topp:HOUSHOLD><topp:MALE>1631295.0</topp:MALE><topp:FEMALE>1663099.0</topp:FEMALE><topp:WORKERS>1233023.0</topp:WORKERS><topp:DRVALONE>1216639.0</topp:DRVALONE><topp:CARPOOL>210274.0</topp:CARPOOL><topp:PUBTRANS>46983.0</topp:PUBTRANS><topp:EMPLOYED>1633281.0</topp:EMPLOYED><topp:UNEMPLOY>99438.0</topp:UNEMPLOY><topp:SERVICE>421079.0</topp:SERVICE><topp:MANUAL>181760.0</topp:MANUAL><topp:P_MALE>0.495</topp:P_MALE><topp:P_FEMALE>0.505</topp:P_FEMALE><topp:SAMP_POP>512677.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.7"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.496517 -89.568153</gml:lowerCorner><gml:upperCorner>39.14200600000001 -81.959732</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.655033 -86.510674 36.652058 -86.770538 36.650768 -87.068184 36.651264 -87.112701 36.649231 -87.346603 36.645168 -87.64064 36.64444 -87.693512 36.64147199999999 -87.853516 36.66937299999999 -87.870689 36.67963 -88.071312 36.582668 -88.041061 36.538146999999995 -88.035049 36.496517 -88.042732 36.498149999999995 -88.495979 36.499489 -88.512634 36.498985000000005 -88.810661 36.499847 -88.826302 36.499793999999994 -88.830315 36.502548000000004 -89.346596 36.502617 -89.414711 36.510563000000005 -89.418137 36.616187999999994 -89.373878 36.625702000000004 -89.363548 36.628849 -89.342323 36.622017 -89.322273 36.575249 -89.283424 36.569266999999996 -89.241615 36.581894000000005 -89.21006 36.631298 -89.200119 36.653003999999996 -89.177094 36.67157 -89.167831 36.713367000000005 -89.197495 36.72742100000001 -89.196297 36.760925 -89.1772 36.759040999999996 -89.151367 36.768032000000005 -89.125473 36.792412 -89.125839 36.80441999999999 -89.164375 36.829384000000005 -89.173462 36.843422000000004 -89.166496 36.86644 -89.129585 36.953869 -89.104965 36.977451 -89.107147 36.988113 -89.12986 36.99844 -89.150246 37.025711 -89.174332 37.064235999999994 -89.169548 37.093185000000005 -89.146347 37.112137000000004 -89.116821 37.185860000000005 -89.065033 37.22003599999999 -88.993172 37.218407 -88.932503 37.202194000000006 -88.863289 37.152107 -88.746506 37.141182 -88.739113 37.13540999999999 -88.68837 37.109047000000004 -88.61422 37.072815000000006 -88.559273 37.064769999999996 -88.517273 37.06818 -88.4907 37.072143999999994 -88.476799 37.098670999999996 -88.45047 37.156909999999996 -88.422516 37.205669 -88.450699 37.257782000000006 -88.501427 37.296852 -88.511322 37.400757 -88.467644 37.420292 -88.419853 37.40930899999999 -88.359177 37.442852 -88.311707 37.476273000000006 -88.087883 37.51099000000001 -88.071564 37.583572000000004 -88.134171 37.628479 -88.157631 37.660686 -88.15937 37.700745 -88.133636 37.735400999999996 -88.072472 37.805683 -88.035576 37.801308000000006 -88.011192 37.77618 -87.95871 37.799507000000006 -87.939583 37.809685 -87.920143 37.838570000000004 -87.910202 37.875179 -87.936821 37.904160000000005 -87.934456 37.919865 -87.921867 37.924553 -87.89901 37.890902999999994 -87.857162 37.878212000000005 -87.823624 37.898087000000004 -87.753761 37.894543 -87.72818 37.899711999999994 -87.709389 37.897007 -87.679703 37.836330000000004 -87.6847 37.828132999999994 -87.65168 37.843777 -87.607574 37.864868 -87.59362 37.890724000000006 -87.594704 37.923412 -87.627121 37.971115 -87.604309 37.91558499999999 -87.504791 37.936477999999994 -87.452278 37.93492500000001 -87.387543 37.89367300000001 -87.310555 37.870773 -87.272743 37.84907200000001 -87.226761 37.838593 -87.175781 37.82692 -87.158081 37.789688 -87.131882 37.784203000000005 -87.10643 37.807086999999996 -87.071312 37.907955 -87.036484 37.924713 -87.013161 37.930565 -86.989037 37.937988000000004 -86.93158 37.953644 -86.900085 37.986866000000006 -86.863281 37.991505000000004 -86.826317 37.978745 -86.802826 37.898303999999996 -86.753838 37.894566 -86.728889 37.91179700000001 -86.68914 37.913139 -86.668671 37.902514999999994 -86.660324 37.860584 -86.670685 37.847324 -86.665939 37.845943000000005 -86.645584 37.857918 -86.614799 37.921013 -86.598328 37.925606 -86.581802 37.921454999999995 -86.541107 37.927811000000005 -86.522758 37.942181000000005 -86.516922 37.987415 -86.530869 38.018631 -86.527855 38.046986000000004 -86.519112 38.051585999999986 -86.503136 38.05909700000001 -86.458389 38.075931999999995 -86.44249 38.08863400000001 -86.442543 38.11164500000001 -86.474358 38.12909300000001 -86.464867 38.129692000000006 -86.452545 38.10815400000001 -86.407204 38.12323000000001 -86.3937 38.13420500000001 -86.344063 38.143963000000014 -86.335442 38.155495 -86.343147 38.167957 -86.387123 38.194743999999986 -86.388329 38.19322600000001 -86.364372 38.177223 -86.341629 38.150238 -86.297699 38.07842299999999 -86.291466 38.05810500000001 -86.277725 38.04065299999999 -86.252182 38.01768899999999 -86.190651 38.01126500000001 -86.105019 37.966713 -86.05275 37.992878000000005 -86.031654 38.001693999999986 -86.006699 38.01176799999999 -85.958618 38.03397799999999 -85.930908 38.06480400000001 -85.914787 38.17993200000001 -85.912109 38.23849100000001 -85.852371 38.27622199999999 -85.839943 38.28611000000001 -85.806587 38.28232199999999 -85.786247 38.27024499999999 -85.746964 38.300883999999996 -85.681427 38.33768499999999 -85.654266 38.383621000000005 -85.643631 38.446606 -85.612679 38.47135499999999 -85.50724 38.518112 -85.466423 38.53695300000001 -85.432411 38.56141299999999 -85.417503 38.584778 -85.424446 38.694614 -85.45372 38.72478100000001 -85.446732 38.738358000000005 -85.418228 38.73694599999999 -85.335052 38.744316 -85.271439 38.69575499999999 -85.205208 38.69511399999999 -85.16098 38.714077 -85.119705 38.750361999999996 -85.068504 38.764229 -85.025124 38.78057899999999 -84.975662 38.793346000000014 -84.818832 38.83439999999999 -84.824478 38.866581 -84.787498 38.884322999999995 -84.788719 38.89712900000001 -84.803276 38.901981000000006 -84.859795 38.909369999999996 -84.875305 38.92754400000001 -84.875931 38.95457099999999 -84.846367 38.98271600000001 -84.834496 39.00577200000001 -84.844276 39.032837 -84.876343 39.050591 -84.890045 39.064987 -84.886757 39.10363000000001 -84.827911 39.10252800000001 -84.811531 39.106976 -84.789978 39.14200600000001 -84.742928 39.08956499999999 -84.667542 39.07487499999999 -84.622704 39.07020600000001 -84.593124 39.094134999999994 -84.515358 39.107304 -84.492111 39.111766999999986 -84.444977 39.084663000000006 -84.425743 39.04727600000001 -84.4198 39.035683000000006 -84.391373 39.03774999999999 -84.34584 39.01401100000001 -84.313377 38.94447299999999 -84.290199 38.91741200000001 -84.261589 38.87448900000001 -84.235359 38.812622000000005 -84.228767 38.78842900000001 -84.176819 38.765434 -84.088936 38.763664000000006 -84.053871 38.77757600000001 -83.962234 38.757889000000006 -83.912613 38.744845999999995 -83.857628 38.71180699999999 -83.837608 38.693771 -83.790543 38.650745 -83.770302 38.635479000000004 -83.712906 38.62085300000001 -83.678612 38.623806 -83.655838 38.63578799999999 -83.643272 38.664897999999994 -83.633324 38.677898 -83.618462 38.69603699999999 -83.526642 38.69006300000001 -83.50016 38.663700000000006 -83.453705 38.654922 -83.371513 38.631912 -83.330116 38.60648699999999 -83.320419 38.59624099999999 -83.306625 38.59656100000001 -83.290138 38.60918000000001 -83.27285 38.62409600000001 -83.24511 38.60976400000001 -83.182037 38.61926299999999 -83.14325 38.66475700000001 -83.111343 38.68565000000001 -83.060982 38.714436000000006 -83.027046 38.719565999999986 -82.972588 38.74633800000001 -82.92141 38.74269899999999 -82.890419 38.718929 -82.873299 38.683223999999996 -82.880119 38.65231700000001 -82.860138 38.60038 -82.853966 38.571583000000004 -82.827103 38.557209 -82.802475 38.552986000000004 -82.742058 38.539063 -82.695694 38.50206 -82.669876 38.472588 -82.613861 38.41243700000001 -82.586723 38.368382 -82.598358 38.307728 -82.572418 38.292427 -82.58017 38.255889999999994 -82.574699 38.245304000000004 -82.589233 38.23872800000001 -82.616348 38.19374099999999 -82.606766 38.178009 -82.613884 38.16935000000001 -82.647278 38.146243999999996 -82.646248 38.10987499999999 -82.59333 38.01557199999999 -82.524803 37.975815 -82.475906 37.95430399999999 -82.480301 37.942420999999996 -82.493523 37.922169 -82.500336 37.894760000000005 -82.437737 37.872260999999995 -82.421638 37.811623 -82.405937 37.784302 -82.339981 37.758326999999994 -82.319633 37.744072 -82.329536 37.668957000000006 -82.295761 37.656673 -82.23851 37.623913 -82.205582 37.640564 -82.185692 37.593464 -82.159454 37.590430999999995 -82.131927 37.56979 -82.137619 37.565815 -82.146667 37.557345999999995 -82.142654 37.548203 -82.084541 37.525234 -82.055817 37.551345999999995 -82.049286 37.530411 -82.026505 37.543144 -81.97673 37.531063 -81.959732 37.30475199999999 -82.289085 37.26040999999999 -82.353973 37.250595000000004 -82.406013 37.19927199999999 -82.550163 37.193813000000006 -82.568146 37.109916999999996 -82.719215 37.093018 -82.721497 37.075382000000005 -82.70929 37.065830000000005 -82.720177 37.033894000000004 -82.723717 37.005505 -82.81234 36.974491 -82.866676 36.932068 -82.860748 36.893600000000006 -82.878159 36.863986999999995 -82.95092 36.858704 -83.046745 36.850905999999995 -83.068062 36.779064000000005 -83.128326 36.75108 -83.124496 36.739971 -83.138618 36.734173 -83.203758 36.709446 -83.32148 36.68813299999999 -83.385948 36.672241 -83.404243 36.661747000000005 -83.460312 36.661395999999996 -83.530983 36.616894 -83.646889 36.598620999999994 -83.675262 36.584166999999994 -83.695694 36.591209000000006 -83.935677 36.592006999999995 -84.006821 36.595371 -84.254555 36.595417 -84.256844 36.605002999999996 -84.781929 36.605366000000004 -84.791115 36.620914 -84.998512 36.625553 -85.272537 36.626037999999994 -85.300133 36.618137000000004 -85.437408 36.626628999999994 -85.7855 36.63306 -85.980629 36.643246000000005 -86.199005 36.650890000000004 -86.415451 36.655033 -86.510674</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.498108 -89.533195 36.518738 -89.566986 36.541409 -89.568153 36.557743 -89.556137 36.564555999999996 -89.530365 36.559116 -89.493126 36.547775 -89.481674 36.525555 -89.471375 36.504695999999996 -89.481682 36.498547 -89.475822 36.498108 -89.533195</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Kentucky</topp:STATE_NAME><topp:STATE_FIPS>21</topp:STATE_FIPS><topp:SUB_REGION>E S Cen</topp:SUB_REGION><topp:STATE_ABBR>KY</topp:STATE_ABBR><topp:LAND_KM>103961.904</topp:LAND_KM><topp:WATER_KM>1772.542</topp:WATER_KM><topp:PERSONS>4551524.0</topp:PERSONS><topp:FAMILIES>1237346.0</topp:FAMILIES><topp:HOUSHOLD>1718663.0</topp:HOUSHOLD><topp:MALE>2195130.0</topp:MALE><topp:FEMALE>2356394.0</topp:FEMALE><topp:WORKERS>1656590.0</topp:WORKERS><topp:DRVALONE>1502949.0</topp:DRVALONE><topp:CARPOOL>273091.0</topp:CARPOOL><topp:PUBTRANS>48158.0</topp:PUBTRANS><topp:EMPLOYED>1970934.0</topp:EMPLOYED><topp:UNEMPLOY>148125.0</topp:UNEMPLOY><topp:SERVICE>556744.0</topp:SERVICE><topp:MANUAL>361621.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>646517.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.8"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.988853000000006 -102.051071</gml:lowerCorner><gml:upperCorner>40.00297900000001 -94.60099</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>37.001408 -95.071693 37.000538000000006 -95.406372 37.000938000000005 -95.525764 36.998035 -95.785492 37.000011 -95.957703 36.998264000000006 -96.005791 37.000538000000006 -96.518898 37.000134 -96.74839 36.999779000000004 -97.137375 36.996441000000004 -97.46508 36.998543 -97.803917 36.998645999999994 -98.104187 36.999035000000006 -98.346794 36.99834799999999 -98.539864 36.998042999999996 -98.999138 36.99453 -99.43708 36.995434 -99.544243 36.995388000000005 -99.998848 36.997623000000004 -100.088158 36.997806999999995 -100.633812 36.996635 -100.950142 36.99744 -101.071159 36.996666000000005 -101.553238 36.988853000000006 -102.024071 36.988972000000004 -102.036758 37.386261000000005 -102.041557 37.641459999999995 -102.043999 37.734386 -102.043518 38.253806999999995 -102.045601 38.263329 -102.04509 38.615486000000004 -102.047127 38.69253900000001 -102.047112 39.03699900000001 -102.048515 39.12675100000001 -102.047417 39.56280899999999 -102.04834 39.56869900000001 -102.048981 39.99892800000001 -102.051071 40.00100699999999 -101.406952 40.001823 -101.321709 40.00019499999999 -100.754425 39.999168 -100.734619 40.000580000000014 -100.190697 40.000473 -100.180496 40.00297900000001 -99.627472 39.99956499999999 -99.177834 39.998324999999994 -99.064384 39.998447 -98.720284 39.997116000000005 -98.504143 39.99842100000001 -98.26384 39.99843999999999 -97.929268 39.999718 -97.816269 39.997375000000005 -97.361595 39.996151 -96.907982 39.994473 -96.801117 39.994170999999994 -96.453743 39.99450300000001 -96.240311 39.995159 -96.000977 39.99348800000001 -95.780434 39.99259599999999 -95.329445 39.999409000000014 -95.308441 39.94210799999999 -95.240707 39.93817899999999 -95.207344 39.91018299999999 -95.19371 39.90805800000001 -95.150299 39.869868999999994 -95.100471 39.86654300000001 -95.062996 39.877849999999995 -95.033257 39.896984 -95.021523 39.90082899999999 -94.964775 39.89608799999999 -94.937996 39.849391999999995 -94.936264 39.83313799999999 -94.92363 39.828339 -94.898079 39.817406000000005 -94.88826 39.793780999999996 -94.899078 39.78278 -94.933022 39.77543299999999 -94.934868 39.757847 -94.921555 39.760684999999995 -94.876823 39.75412399999999 -94.870941 39.73931099999999 -94.877617 39.72676100000001 -94.905434 39.72703200000001 -94.930611 39.73650699999999 -94.952896 39.732044 -94.96154 39.68499399999999 -94.978325 39.66191900000001 -95.028046 39.62569400000001 -95.055771 39.586781 -95.053368 39.56069600000001 -95.108742 39.53285199999999 -95.101791 39.485332 -95.047356 39.46294399999999 -95.040268 39.43946500000001 -94.985962 39.411449000000005 -94.958252 39.381268000000006 -94.925507 39.380641999999995 -94.898041 39.34012200000001 -94.911102 39.32302899999999 -94.90744 39.286045 -94.880867 39.261765 -94.833237 39.21100200000001 -94.82058 39.19687999999999 -94.78981 39.17125300000001 -94.730293 39.17491899999999 -94.675278 39.158424 -94.646172 39.151646 -94.612419 39.141223999999994 -94.60099 39.112797 -94.607903 39.04466199999999 -94.609047 38.83710099999999 -94.612236 38.737213 -94.612915 38.47145800000001 -94.618484 38.39201399999999 -94.61882 38.05575200000001 -94.617096 38.03035399999999 -94.616501 37.679824999999994 -94.619057 37.65033 -94.618759 37.360714 -94.61853 37.327679 -94.618744 37.060085 -94.620438 36.996983 -94.620155 37.00071 -95.032509 37.001408 -95.071693</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Kansas</topp:STATE_NAME><topp:STATE_FIPS>20</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>KS</topp:STATE_ABBR><topp:LAND_KM>211921.641</topp:LAND_KM><topp:WATER_KM>1188.865</topp:WATER_KM><topp:PERSONS>2477574.0</topp:PERSONS><topp:FAMILIES>658600.0</topp:FAMILIES><topp:HOUSHOLD>944726.0</topp:HOUSHOLD><topp:MALE>1214645.0</topp:MALE><topp:FEMALE>1262929.0</topp:FEMALE><topp:WORKERS>907383.0</topp:WORKERS><topp:DRVALONE>928575.0</topp:DRVALONE><topp:CARPOOL>135598.0</topp:CARPOOL><topp:PUBTRANS>7585.0</topp:PUBTRANS><topp:EMPLOYED>1172214.0</topp:EMPLOYED><topp:UNEMPLOY>57772.0</topp:UNEMPLOY><topp:SERVICE>346339.0</topp:SERVICE><topp:MANUAL>166429.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>453411.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.9"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.541481000000005 -83.675262</gml:lowerCorner><gml:upperCorner>39.456902000000014 -75.242584</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.546059 -79.144325 36.54978199999999 -79.217064 36.547657 -79.5103 36.547889999999995 -79.717445 36.545024999999995 -80.024055 36.547134 -80.048096 36.551044000000005 -80.43531 36.557297000000005 -80.611053 36.563438000000005 -80.838158 36.565212 -80.903442 36.57286499999999 -81.345299 36.589648999999994 -81.669998 36.607555000000005 -81.652435 36.611481 -81.829056 36.613495 -81.918449 36.595837 -81.929459 36.595043000000004 -82.154327 36.593967000000006 -82.216805 36.591697999999994 -82.296997 36.591446000000005 -82.610962 36.590946 -82.849937 36.591198000000006 -82.986809 36.588001000000006 -83.211029 36.589848 -83.248489 36.60038 -83.275131 36.598755 -83.464302 36.598620999999994 -83.675262 36.616894 -83.646889 36.661395999999996 -83.530983 36.661747000000005 -83.460312 36.672241 -83.404243 36.68813299999999 -83.385948 36.709446 -83.32148 36.734173 -83.203758 36.739971 -83.138618 36.75108 -83.124496 36.779064000000005 -83.128326 36.850905999999995 -83.068062 36.858704 -83.046745 36.863986999999995 -82.95092 36.893600000000006 -82.878159 36.932068 -82.860748 36.974491 -82.866676 37.005505 -82.81234 37.033894000000004 -82.723717 37.065830000000005 -82.720177 37.075382000000005 -82.70929 37.093018 -82.721497 37.109916999999996 -82.719215 37.193813000000006 -82.568146 37.19927199999999 -82.550163 37.250595000000004 -82.406013 37.26040999999999 -82.353973 37.30475199999999 -82.289085 37.531063 -81.959732 37.506535 -81.935608 37.492915999999994 -81.948151 37.48279599999999 -81.976578 37.466476 -81.988358 37.41540500000001 -81.920891 37.371616 -81.926979 37.34047700000001 -81.897316 37.325344 -81.863976 37.30691899999999 -81.858841 37.285393 -81.83905 37.279427 -81.815544 37.287041 -81.792824 37.272144 -81.752022 37.250378 -81.738625 37.235321 -81.701897 37.204796 -81.666054 37.206238 -81.556824 37.234257 -81.505707 37.252735 -81.495705 37.254307 -81.475533 37.282509000000005 -81.403519 37.311038999999994 -81.391121 37.338837 -81.358971 37.293591000000006 -81.31205 37.240097000000006 -81.223114 37.274806999999996 -81.140923 37.285942000000006 -81.025124 37.306122 -80.98613 37.296356 -80.978729 37.291672000000005 -80.968086 37.301249999999996 -80.93438 37.339291 -80.855629 37.350821999999994 -80.848618 37.388577 -80.877556 37.423339999999996 -80.850723 37.412941000000004 -80.799812 37.391632 -80.799446 37.386073999999994 -80.770226 37.371292 -80.763199 37.378960000000006 -80.747894 37.387615 -80.746529 37.392596999999995 -80.729942 37.388256 -80.705414 37.445930000000004 -80.597702 37.469086000000004 -80.542969 37.47492200000001 -80.50898 37.460471999999996 -80.488098 37.433735 -80.487015 37.422695000000004 -80.474983 37.43478 -80.425598 37.465599 -80.388527 37.475975000000005 -80.352379 37.491051 -80.347733 37.511024000000006 -80.288139 37.53613300000001 -80.280952 37.528244 -80.308525 37.533276 -80.326126 37.566593 -80.31694 37.596771000000004 -80.246613 37.624142000000006 -80.219147 37.640579 -80.254639 37.640423 -80.301147 37.652122000000006 -80.305077 37.671379 -80.295914 37.682548999999995 -80.303322 37.725928999999994 -80.250244 37.757110999999995 -80.254898 37.778735999999995 -80.220757 37.80224200000001 -80.223946 37.842845999999994 -80.171806 37.860062 -80.172432 37.877106 -80.160217 37.891155 -80.118721 37.914536 -80.106705 37.955524 -80.055023 37.989746 -80.000717 38.038498000000004 -79.966713 38.06724199999999 -79.957733 38.10318799999999 -79.928513 38.121185 -79.935547 38.16248300000001 -79.910561 38.17914200000001 -79.916382 38.250156000000004 -79.831375 38.268542999999994 -79.793846 38.28499600000001 -79.786736 38.29874799999999 -79.803001 38.31420499999999 -79.800552 38.35387 -79.764229 38.351719 -79.733055 38.394566 -79.720261 38.43011899999999 -79.684319 38.500237 -79.692879 38.52065999999999 -79.66584 38.55006 -79.669655 38.592239000000006 -79.642632 38.553687999999994 -79.536743 38.46202500000001 -79.48658 38.41251 -79.317238 38.437183000000005 -79.272598 38.480372999999986 -79.231903 38.65812700000001 -79.12767 38.66365099999999 -79.121307 38.659088 -79.088791 38.707153000000005 -79.087479 38.76194000000001 -79.056801 38.79051999999999 -79.055046 38.799847 -79.033989 38.84664900000001 -78.987701 38.76329000000001 -78.866814 38.83363299999999 -78.816116 38.88010800000001 -78.793312 38.911381000000006 -78.749512 38.92917299999999 -78.737991 38.93021400000001 -78.724403 38.90488099999999 -78.719246 38.92157399999999 -78.680489 38.95044300000001 -78.647232 38.979603 -78.631111 38.967197 -78.598961 39.01382799999999 -78.553474 39.02338 -78.549469 39.035037999999986 -78.564445 39.05702600000001 -78.536919 39.09357800000001 -78.501869 39.111839 -78.485519 39.118931 -78.44825 39.14852099999999 -78.43084 39.17049 -78.402634 39.19752500000001 -78.424339 39.21204 -78.42334 39.244850000000014 -78.399399 39.257439000000005 -78.413818 39.341358000000014 -78.341118 39.35085699999999 -78.3442 39.36158800000001 -78.365746 39.38073 -78.350502 39.456902000000014 -78.347816 39.42336700000001 -78.277153 39.39101400000001 -78.229782 39.265536999999995 -78.033607 39.13207600000001 -77.830963 39.14162099999999 -77.820328 39.19650300000001 -77.805733 39.24644900000001 -77.768517 39.28454199999999 -77.759743 39.31769600000001 -77.727753 39.31868 -77.679588 39.29971699999999 -77.616524 39.298393000000004 -77.568962 39.268939999999986 -77.542191 39.249911999999995 -77.494064 39.22905700000001 -77.464958 39.218632000000014 -77.461998 39.17693299999999 -77.478638 39.157444 -77.516617 39.116653000000014 -77.513046 39.103958000000006 -77.479248 39.080837 -77.459694 39.066776000000004 -77.433037 39.068512 -77.346519 39.062588000000005 -77.3246 39.02757299999999 -77.255989 38.97587999999999 -77.243729 38.96477899999999 -77.152046 38.93206000000001 -77.122627 38.91560000000001 -77.078949 38.886101 -77.067886 38.86243099999999 -77.039078 38.838413 -77.040405 38.829364999999996 -77.045189 38.81391500000001 -77.035248 38.78811999999999 -77.045448 38.71878100000001 -77.046471 38.71202099999999 -77.057121 38.71527900000001 -77.081879 38.703982999999994 -77.093147 38.67779899999999 -77.125114 38.64812499999999 -77.12999 38.62270000000001 -77.197258 38.66076699999999 -77.194748 38.650723 -77.227592 38.50191100000001 -77.303619 38.436825 -77.338486 38.362671000000006 -77.289482 38.34398300000001 -77.321823 38.33137099999999 -77.2407 38.375350999999995 -77.054535 38.280272999999994 -76.999359 38.202473 -76.936462 38.12022400000001 -76.595604 38.074112000000014 -76.549034 38.02533 -76.558044 38.00317000000001 -76.573692 38.012744999999995 -76.524544 37.956951000000004 -76.367744 37.890029999999996 -76.259201 37.850178 -76.251923 37.798812999999996 -76.324539 37.719111999999996 -76.309944 37.70013 -76.357002 37.677814 -76.323059 37.622924999999995 -76.344872 37.656384 -76.507088 37.770115000000004 -76.58046 37.796349000000006 -76.631775 37.916675999999995 -76.771851 37.919506 -76.818504 37.798480999999995 -76.732353 37.774754 -76.681732 37.641907 -76.569496 37.551201000000006 -76.314644 37.52515 -76.34864 37.55257400000001 -76.512856 37.515202 -76.434181 37.515755 -76.355667 37.390190000000004 -76.254593 37.330321999999995 -76.275192 37.334571999999994 -76.300972 37.393547 -76.33902 37.457966 -76.446869 37.418892 -76.463936 37.412136000000004 -76.417084 37.373028000000005 -76.403755 37.377491000000006 -76.455551 37.293426999999994 -76.392738 37.255432 -76.461136 37.412200999999996 -76.653488 37.418491 -76.704681 37.371646999999996 -76.669983 37.291298 -76.595039 37.207297999999994 -76.424667 37.152393000000004 -76.412994 37.173030999999995 -76.396873 37.146427 -76.363785 37.177009999999996 -76.337318 37.122097 -76.285675 37.107708 -76.395691 37.074344999999994 -76.278931 37.020489 -76.293343 36.990414 -76.384552 36.96526 -76.426117 37.067642000000006 -76.53112 37.088367000000005 -76.515266 37.117771000000005 -76.564491 37.080173 -76.568443 37.13227500000001 -76.624916 37.178577000000004 -76.610031 37.225837999999996 -76.648094 37.232521000000006 -76.697151 37.193389999999994 -76.746101 37.240387 -76.795929 37.2439 -76.85717 37.322945000000004 -76.875488 37.259426000000005 -76.878426 37.23661 -76.94149 37.201054 -76.900864 37.207302 -76.797394 37.15066899999999 -76.72921 37.197987 -76.685966 37.147712999999996 -76.671539 37.054134000000005 -76.665642 37.024494000000004 -76.577827 36.994843 -76.613373 37.006195000000005 -76.555046 36.961723000000006 -76.48951 36.912186000000005 -76.517174 36.91908599999999 -76.482178 36.895568999999995 -76.486618 36.841801000000004 -76.560516 36.795615999999995 -76.561852 36.869473 -76.507195 36.901413000000005 -76.410805 36.913342 -76.348145 36.860187999999994 -76.341919 36.835926 -76.394089 36.826138 -76.401184 36.845844 -76.317436 36.828342000000006 -76.292702 36.942001000000005 -76.307617 36.962734 -76.284225 36.935069999999996 -76.202332 36.904442 -76.191666 36.931618 -76.118431 36.923134000000005 -75.995361 36.555874 -75.878166 36.556197999999995 -75.901985 36.599022000000005 -75.892853 36.721565 -75.950798 36.556652 -75.998665 36.556717000000006 -76.027168 36.603592000000006 -76.061859 36.55695299999999 -76.045959 36.557163 -76.127396 36.556056999999996 -76.330254 36.555813 -76.497559 36.555251999999996 -76.563583 36.554157000000004 -76.921631 36.554146 -76.924133 36.556286 -77.177353 36.553917 -77.320053 36.553439999999995 -77.763931 36.552944 -77.898857 36.552475 -78.051666 36.545531999999994 -78.321251 36.541481000000005 -78.458809 36.546074000000004 -78.737389 36.543533 -78.7967 36.546059 -79.144325</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.02758800000001 -75.270721 38.028526 -75.242584 37.962875 -75.298859 37.888783000000004 -75.33918 37.875652 -75.386078 37.90191299999999 -75.34481 37.900974000000005 -75.378571 37.918797 -75.346687 38.02758800000001 -75.270721</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>37.552181000000004 -75.867371 37.561554 -75.941101 37.58588399999999 -75.929558 37.580344999999994 -75.887276 37.592175 -75.905991 37.711792 -75.799721 37.789833 -75.7826 37.824516 -75.696083 37.858124000000004 -75.686707 37.930569000000006 -75.733978 37.941181 -75.658447 37.970130999999995 -75.648216 37.996418000000006 -75.626434 38.01671200000001 -75.37278 37.697131999999996 -75.61792 37.677193 -75.589905 37.589512 -75.699493 37.559753 -75.650269 37.558182 -75.727524 37.510540000000006 -75.7565 37.493472999999994 -75.705269 37.469044 -75.813026 37.426204999999996 -75.820488 37.408108 -75.790771 37.418148 -75.826675 37.36739300000001 -75.89711 37.14250199999999 -75.931389 37.126232 -75.970985 37.308780999999996 -76.018471 37.484641999999994 -75.934448 37.479350999999994 -75.965446 37.521832 -75.954704 37.556889 -75.930756 37.552181000000004 -75.867371</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Virginia</topp:STATE_NAME><topp:STATE_FIPS>51</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>VA</topp:STATE_ABBR><topp:LAND_KM>102537.328</topp:LAND_KM><topp:WATER_KM>4263.82</topp:WATER_KM><topp:PERSONS>6180651.0</topp:PERSONS><topp:FAMILIES>1627615.0</topp:FAMILIES><topp:HOUSHOLD>2289067.0</topp:HOUSHOLD><topp:MALE>3030948.0</topp:MALE><topp:FEMALE>3149703.0</topp:FEMALE><topp:WORKERS>2343200.0</topp:WORKERS><topp:DRVALONE>2278600.0</topp:DRVALONE><topp:CARPOOL>499251.0</topp:CARPOOL><topp:PUBTRANS>125792.0</topp:PUBTRANS><topp:EMPLOYED>3025109.0</topp:EMPLOYED><topp:UNEMPLOY>141926.0</topp:UNEMPLOY><topp:SERVICE>777181.0</topp:SERVICE><topp:MANUAL>420070.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>898089.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.10"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>35.989586 -95.767204</gml:lowerCorner><gml:upperCorner>40.609748999999994 -89.104965</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.953869 -89.104965 36.86644 -89.129585 36.843422000000004 -89.166496 36.829384000000005 -89.173462 36.80441999999999 -89.164375 36.792412 -89.125839 36.768032000000005 -89.125473 36.759040999999996 -89.151367 36.760925 -89.1772 36.72742100000001 -89.196297 36.713367000000005 -89.197495 36.67157 -89.167831 36.653003999999996 -89.177094 36.631298 -89.200119 36.581894000000005 -89.21006 36.569266999999996 -89.241615 36.575249 -89.283424 36.622017 -89.322273 36.628849 -89.342323 36.625702000000004 -89.363548 36.616187999999994 -89.373878 36.510563000000005 -89.418137 36.502617 -89.414711 36.456379 -89.448517 36.445953 -89.470833 36.465462 -89.491989 36.498547 -89.475822 36.504695999999996 -89.481682 36.525555 -89.471375 36.547775 -89.481674 36.559116 -89.493126 36.564555999999996 -89.530365 36.557743 -89.556137 36.541409 -89.568153 36.518738 -89.566986 36.498108 -89.533195 36.47180899999999 -89.516022 36.440960000000004 -89.545181 36.401058000000006 -89.520004 36.35593 -89.519333 36.345721999999995 -89.544556 36.354752000000005 -89.60569 36.334782000000004 -89.622795 36.308037 -89.606766 36.280865000000006 -89.542236 36.264538 -89.535378 36.257278 -89.541649 36.240898 -89.61808 36.25489399999999 -89.670586 36.25213599999999 -89.694542 36.240795000000006 -89.695656 36.220867 -89.676788 36.18374300000001 -89.618561 36.15201999999999 -89.589455 36.129791 -89.589424 36.099316 -89.667389 36.08297 -89.678169 36.025795 -89.688843 35.999877999999995 -89.721756 35.996838 -89.963203 35.991158 -90.283455 35.989586 -90.37896 36.091656 -90.315239 36.115905999999995 -90.284752 36.118763 -90.263702 36.137089 -90.23484 36.161148 -90.232224 36.172565000000006 -90.219223 36.196940999999995 -90.161308 36.21207 -90.131218 36.257996000000006 -90.109917 36.272273999999996 -90.066093 36.300472 -90.049751 36.325333 -90.067635 36.362606 -90.050201 36.382553 -90.052063 36.39738800000001 -90.080177 36.404915 -90.116829 36.422565000000006 -90.123833 36.453896 -90.117226 36.45741700000001 -90.137276 36.491814000000005 -90.150162 36.492751999999996 -90.224373 36.490962999999994 -90.581619 36.489204 -90.804314 36.487953000000005 -91.133827 36.491039 -91.411659 36.490376 -91.45285 36.490955 -91.688416 36.491371 -92.127487 36.491596 -92.146164 36.490855999999994 -92.522888 36.489918 -92.777466 36.489819 -92.852104 36.490616 -93.297142 36.490196 -93.328163 36.489891 -93.59626 36.489716 -93.857323 36.49095199999999 -94.080849 36.489338000000004 -94.617035 36.670471000000006 -94.62085 36.763538 -94.62146 36.996983 -94.620155 37.060085 -94.620438 37.327679 -94.618744 37.360714 -94.61853 37.65033 -94.618759 37.679824999999994 -94.619057 38.03035399999999 -94.616501 38.05575200000001 -94.617096 38.39201399999999 -94.61882 38.47145800000001 -94.618484 38.737213 -94.612915 38.83710099999999 -94.612236 39.04466199999999 -94.609047 39.112797 -94.607903 39.141223999999994 -94.60099 39.151646 -94.612419 39.158424 -94.646172 39.17491899999999 -94.675278 39.17125300000001 -94.730293 39.19687999999999 -94.78981 39.21100200000001 -94.82058 39.261765 -94.833237 39.286045 -94.880867 39.32302899999999 -94.90744 39.34012200000001 -94.911102 39.380641999999995 -94.898041 39.381268000000006 -94.925507 39.411449000000005 -94.958252 39.43946500000001 -94.985962 39.46294399999999 -95.040268 39.485332 -95.047356 39.53285199999999 -95.101791 39.56069600000001 -95.108742 39.586781 -95.053368 39.62569400000001 -95.055771 39.66191900000001 -95.028046 39.68499399999999 -94.978325 39.732044 -94.96154 39.73650699999999 -94.952896 39.72703200000001 -94.930611 39.72676100000001 -94.905434 39.73931099999999 -94.877617 39.75412399999999 -94.870941 39.760684999999995 -94.876823 39.757847 -94.921555 39.77543299999999 -94.934868 39.78278 -94.933022 39.793780999999996 -94.899078 39.817406000000005 -94.88826 39.828339 -94.898079 39.83313799999999 -94.92363 39.849391999999995 -94.936264 39.89608799999999 -94.937996 39.90082899999999 -94.964775 39.896984 -95.021523 39.877849999999995 -95.033257 39.86654300000001 -95.062996 39.869868999999994 -95.100471 39.90805800000001 -95.150299 39.91018299999999 -95.19371 39.93817899999999 -95.207344 39.94210799999999 -95.240707 39.999409000000014 -95.308441 40.02497500000001 -95.34481 40.028751 -95.370987 40.043750999999986 -95.390274 40.048111000000006 -95.413506 40.08037899999999 -95.403526 40.09536399999999 -95.384285 40.11541700000001 -95.392555 40.131744 -95.422218 40.17399599999999 -95.460693 40.213257 -95.466377 40.226856 -95.476562 40.266215999999986 -95.546875 40.309776 -95.595268 40.30910900000001 -95.646561 40.32234600000001 -95.645287 40.33141699999999 -95.617668 40.346497 -95.615936 40.358799000000005 -95.633919 40.396389 -95.636551 40.48533599999999 -95.695091 40.512203 -95.6847 40.53033099999999 -95.657791 40.558727000000005 -95.662674 40.565833999999995 -95.675423 40.56116900000001 -95.687141 40.52412799999999 -95.691795 40.53237200000001 -95.736763 40.549705999999986 -95.763138 40.589045999999996 -95.767204 40.58433500000001 -95.382294 40.581897999999995 -95.217171 40.57722899999999 -94.920364 40.57575600000001 -94.639633 40.57421500000001 -94.484993 40.57097200000001 -94.238159 40.57402400000001 -94.01783 40.57844499999999 -93.786079 40.58080699999999 -93.562691 40.58048199999999 -93.370056 40.58433500000001 -93.100731 40.58964499999999 -92.717621 40.59143800000001 -92.64624 40.599548 -92.361328 40.60006000000001 -92.192993 40.60823400000001 -91.946198 40.609748999999994 -91.741547 40.593399000000005 -91.716812 40.581165 -91.689796 40.55163999999999 -91.691917 40.53286399999999 -91.622375 40.50483299999999 -91.616699 40.484477999999996 -91.585869 40.463718 -91.579224 40.455399 -91.533051 40.44124600000001 -91.538689 40.43504300000001 -91.529449 40.41012599999999 -91.527534 40.40511699999999 -91.500221 40.390761999999995 -91.490158 40.39096799999999 -91.476883 40.371902000000006 -91.448593 40.309624000000014 -91.486694 40.25137699999999 -91.498932 40.200458999999995 -91.506546 40.134544000000005 -91.516129 40.066711 -91.504005 40.005753 -91.487289 39.94606400000001 -91.447243 39.92183700000001 -91.430389 39.90182899999999 -91.434052 39.885242000000005 -91.450989 39.86304899999999 -91.449188 39.80377200000001 -91.381714 39.76127199999999 -91.373421 39.724639999999994 -91.367088 39.68591699999999 -91.317665 39.600021 -91.203247 39.552593 -91.156189 39.52892700000001 -91.093613 39.473984 -91.064384 39.444412 -91.036339 39.40058500000001 -90.947891 39.35045199999999 -90.850494 39.29680300000001 -90.779343 39.24780999999999 -90.738083 39.22474700000001 -90.732338 39.195873000000006 -90.718193 39.14421100000001 -90.716736 39.09370000000001 -90.690399 39.058178 -90.707588 39.037791999999996 -90.70607 38.93525299999999 -90.668877 38.880795000000006 -90.627213 38.87132600000001 -90.570328 38.89160899999999 -90.530426 38.959179000000006 -90.469841 38.96233000000001 -90.413071 38.92490799999999 -90.31974 38.92471699999999 -90.278931 38.91450900000001 -90.243927 38.85303099999999 -90.132812 38.830467 -90.113121 38.80051 -90.121727 38.785484 -90.135178 38.773098000000005 -90.163399 38.72396499999999 -90.196571 38.70036300000001 -90.20224 38.658772 -90.183578 38.61027100000001 -90.183708 38.562805 -90.240944 38.532768000000004 -90.26123 38.518688 -90.265785 38.427357 -90.301842 38.39084600000001 -90.339607 38.36533 -90.358688 38.32355899999999 -90.369347 38.23429899999999 -90.364769 38.18871300000001 -90.336716 38.16681700000001 -90.289635 38.122169000000014 -90.254059 38.08890500000001 -90.207527 38.05395100000001 -90.134712 38.032272000000006 -90.119339 37.993206 -90.041924 37.969318 -90.010811 37.963634 -89.958229 37.911884 -89.978912 37.878044 -89.937874 37.875904000000006 -89.900551 37.891875999999996 -89.866814 37.905486999999994 -89.861046 37.905063999999996 -89.851715 37.840992 -89.728447 37.804794 -89.691055 37.78397 -89.675858 37.745453 -89.666458 37.706103999999996 -89.581436 37.694798000000006 -89.521523 37.67984 -89.513374 37.650375 -89.51918 37.615928999999994 -89.513367 37.571957 -89.524971 37.491726 -89.494781 37.453186 -89.453621 37.411018 -89.427574 37.355717 -89.435738 37.339409 -89.468742 37.329441 -89.50058 37.304962 -89.513885 37.276402000000004 -89.513885 37.256001 -89.489594 37.253731 -89.465309 37.224266 -89.468216 37.165318 -89.440521 37.137203 -89.423798 37.09908299999999 -89.37999 37.049212999999995 -89.38295 37.009682 -89.310982 36.999207 -89.282768 37.008686 -89.262001 37.027733 -89.264244 37.060908999999995 -89.3097 37.085384000000005 -89.303291 37.091244 -89.284233 37.087124 -89.264053 37.041732999999994 -89.237679 37.02897299999999 -89.210052 36.986771000000005 -89.193512 36.988113 -89.12986 36.977451 -89.107147 36.953869 -89.104965</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Missouri</topp:STATE_NAME><topp:STATE_FIPS>29</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>MO</topp:STATE_ABBR><topp:LAND_KM>178445.951</topp:LAND_KM><topp:WATER_KM>2100.115</topp:WATER_KM><topp:PERSONS>5117073.0</topp:PERSONS><topp:FAMILIES>1368334.0</topp:FAMILIES><topp:HOUSHOLD>1961206.0</topp:HOUSHOLD><topp:MALE>2464315.0</topp:MALE><topp:FEMALE>2652758.0</topp:FEMALE><topp:WORKERS>1861192.0</topp:WORKERS><topp:DRVALONE>1816079.0</topp:DRVALONE><topp:CARPOOL>312042.0</topp:CARPOOL><topp:PUBTRANS>47129.0</topp:PUBTRANS><topp:EMPLOYED>2367395.0</topp:EMPLOYED><topp:UNEMPLOY>155388.0</topp:UNEMPLOY><topp:SERVICE>659782.0</topp:SERVICE><topp:MANUAL>386746.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>864999.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.11"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>31.335534999999993 -114.820969</gml:lowerCorner><gml:upperCorner>37.003929 -109.045006</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>33.027668000000006 -114.519844 33.036743 -114.558304 33.026962 -114.609138 33.03352700000001 -114.633179 33.04437299999999 -114.644371 33.038883 -114.663162 33.095344999999995 -114.710564 33.122337 -114.708672 33.167213000000004 -114.67733 33.22456 -114.67926 33.239222999999996 -114.68692 33.267982 -114.676903 33.305676000000005 -114.734634 33.352385999999996 -114.702812 33.41103 -114.724144 33.41908599999999 -114.644302 33.439396 -114.629784 33.468571 -114.6203 33.486098999999996 -114.597298 33.509418 -114.586273 33.560047 -114.528633 33.580482 -114.539459 33.622112 -114.526382 33.665482 -114.524475 33.68271300000001 -114.535645 33.708347 -114.494888 33.743179 -114.509499 33.771694 -114.503769 33.826012000000006 -114.520332 33.84194599999999 -114.510933 33.86290700000001 -114.520172 33.925017999999994 -114.497398 33.95239599999999 -114.524841 33.965046 -114.517418 34.029827 -114.428192 34.078316 -114.423241 34.102638 -114.409378 34.141281000000006 -114.322014 34.171215000000004 -114.284584 34.186206999999996 -114.234993 34.266964 -114.149132 34.272605999999996 -114.124451 34.314533 -114.133347 34.336433 -114.152634 34.36519199999999 -114.181297 34.40547599999999 -114.257057 34.41205600000001 -114.282608 34.43574099999999 -114.302078 34.454860999999994 -114.331848 34.459666999999996 -114.375717 34.477074 -114.383072 34.536552 -114.376038 34.583714 -114.408951 34.598952999999995 -114.43351 34.610885999999994 -114.421478 34.709866000000005 -114.464844 34.744750999999994 -114.497009 34.74890499999999 -114.524757 34.759952999999996 -114.541245 34.831856 -114.56942 34.87553 -114.626465 34.919498000000004 -114.629677 34.943607 -114.620209 34.99764999999999 -114.631477 34.998913 -114.62027 35.041861999999995 -114.63298 35.076057000000006 -114.594833 35.118656 -114.635109 35.133906999999994 -114.625641 35.132560999999995 -114.581818 35.140068 -114.571457 35.174347 -114.560242 35.220184 -114.558784 35.304771 -114.58709 35.358383 -114.588783 35.450768 -114.644592 35.515761999999995 -114.67141 35.546645999999996 -114.648987 35.584843000000006 -114.652328 35.61135899999999 -114.639061 35.646595000000005 -114.653259 35.656409999999994 -114.667679 35.693111 -114.664284 35.732609 -114.688011 35.764717000000005 -114.681931 35.847458 -114.689056 35.870975 -114.661652 35.880489 -114.660789 35.911629000000005 -114.698463 35.987667 -114.735397 36.036777 -114.716858 36.058773 -114.728149 36.085983 -114.727333 36.105202000000006 -114.711945 36.141987 -114.620796 36.138355000000004 -114.598122 36.155108999999996 -114.529762 36.124729 -114.465805 36.121071 -114.443138 36.151009 -114.379997 36.137496999999996 -114.34343 36.111453999999995 -114.315292 36.087124 -114.303055 36.062248 -114.306786 36.018345 -114.232674 36.017269 -114.205971 36.041743999999994 -114.128227 36.121105 -114.106979 36.193993000000006 -114.044312 36.216038 -114.036598 36.84187300000001 -114.042915 36.996562999999995 -114.043137 36.99624300000001 -112.899216 36.998008999999996 -112.541763 36.995506000000006 -112.236511 37.001720000000006 -111.355453 37.002491000000006 -110.739372 37.003929 -110.483398 36.991749 -110.451546 36.992065 -109.996399 36.996643000000006 -109.047821 35.996655000000004 -109.047195 34.954612999999995 -109.045998 34.59174 -109.048012 33.783249 -109.049721 33.205101 -109.049904 32.77948000000001 -109.050728 32.441967000000005 -109.048882 31.343348000000006 -109.045006 31.337559 -110.451942 31.335534999999993 -111.07132 31.431438 -111.368866 32.04356 -113.328377 32.487114000000005 -114.820969 32.615993 -114.808601 32.72081 -114.72126 32.734966 -114.711906 32.741378999999995 -114.693253 32.726237999999995 -114.603157 32.735839999999996 -114.602737 32.737392 -114.571175 32.748783 -114.571426 32.74889 -114.559967 32.760707999999994 -114.560799 32.760704000000004 -114.542221 32.771187 -114.542404 32.771366 -114.529312 32.788002000000006 -114.534294 32.809867999999994 -114.525436 32.845378999999994 -114.460655 32.935867 -114.475662 32.977749 -114.467606 33.027668000000006 -114.519844</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Arizona</topp:STATE_NAME><topp:STATE_FIPS>04</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>AZ</topp:STATE_ABBR><topp:LAND_KM>294333.462</topp:LAND_KM><topp:WATER_KM>942.772</topp:WATER_KM><topp:PERSONS>3665228.0</topp:PERSONS><topp:FAMILIES>940106.0</topp:FAMILIES><topp:HOUSHOLD>1368843.0</topp:HOUSHOLD><topp:MALE>1810691.0</topp:MALE><topp:FEMALE>1854537.0</topp:FEMALE><topp:WORKERS>1358263.0</topp:WORKERS><topp:DRVALONE>1178320.0</topp:DRVALONE><topp:CARPOOL>239083.0</topp:CARPOOL><topp:PUBTRANS>32856.0</topp:PUBTRANS><topp:EMPLOYED>1603896.0</topp:EMPLOYED><topp:UNEMPLOY>123902.0</topp:UNEMPLOY><topp:SERVICE>455896.0</topp:SERVICE><topp:MANUAL>185109.0</topp:MANUAL><topp:P_MALE>0.494</topp:P_MALE><topp:P_FEMALE>0.506</topp:P_FEMALE><topp:SAMP_POP>468178.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.12"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>33.621021 -102.997223</gml:lowerCorner><gml:upperCorner>37.001408 -94.428337</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>34.929050000000004 -94.439102 34.735504000000006 -94.44574 34.508326999999994 -94.452408 34.19665500000001 -94.461479 33.939198000000005 -94.468376 33.631966000000006 -94.476486 33.623047 -94.50061 33.63081 -94.510559 33.621021 -94.525055 33.643009000000006 -94.51799 33.632694 -94.550194 33.635536 -94.562149 33.642830000000004 -94.562195 33.648247 -94.541931 33.661621 -94.545418 33.652157 -94.576462 33.655449000000004 -94.588387 33.662132 -94.585159 33.66301300000001 -94.565208 33.671913 -94.560722 33.670471000000006 -94.578506 33.678982000000005 -94.585106 33.665606999999994 -94.600945 33.683899 -94.631737 33.67010500000001 -94.638763 33.663737999999995 -94.658539 33.666061 -94.669426 33.671459 -94.667953 33.67765 -94.644325 33.692291 -94.655479 33.696537000000006 -94.668457 33.69028900000001 -94.690987 33.701267 -94.741653 33.707770999999994 -94.754478 33.719048 -94.742111 33.716797 -94.762718 33.736706 -94.749771 33.733665 -94.783157 33.742267999999996 -94.782028 33.752842 -94.764175 33.75326200000001 -94.783508 33.739582 -94.803223 33.749404999999996 -94.81916 33.749320999999995 -94.85788 33.774963 -94.881638 33.789597 -94.913879 33.803478 -94.908546 33.816196000000005 -94.918236 33.81580700000001 -94.940399 33.840824 -94.939888 33.848076000000006 -94.959908 33.866215 -94.968704 33.856182000000004 -94.989281 33.869946 -95.012772 33.866451 -95.037361 33.884445 -95.042862 33.896694 -95.063141 33.917648 -95.063477 33.888462000000004 -95.083603 33.89691500000001 -95.089714 33.918453 -95.082268 33.921738000000005 -95.09536 33.91228099999999 -95.119225 33.917145000000005 -95.126678 33.940867999999995 -95.127968 33.943546 -95.148315 33.964862999999994 -95.234039 33.936443 -95.251289 33.905022 -95.250992 33.897800000000004 -95.263618 33.91793800000001 -95.277351 33.886902000000006 -95.28643 33.886623 -95.301956 33.897114 -95.336227 33.870918 -95.33004 33.865753 -95.451607 33.886433 -95.468124 33.881718000000006 -95.498856 33.897735999999995 -95.512886 33.88574199999999 -95.544037 33.893158 -95.547493 33.897816000000006 -95.526733 33.906643 -95.519577 33.904033999999996 -95.546318 33.93607299999999 -95.562775 33.944553 -95.606071 33.936690999999996 -95.61483 33.920238 -95.612984 33.92010500000001 -95.633492 33.894825 -95.699707 33.903397 -95.746864 33.89344 -95.760696 33.87900500000001 -95.764252 33.85140199999999 -95.768517 33.864673999999994 -95.795479 33.843025 -95.825974 33.841038 -95.846558 33.89053 -95.933075 33.889973 -95.943069 33.86503999999999 -95.958763 33.857952 -95.977394 33.875378 -95.994209 33.87339 -96.002617 33.856978999999995 -96.001793 33.844207999999995 -96.014069 33.856021999999996 -96.026749 33.841278 -96.047974 33.844578 -96.091522 33.829257999999996 -96.109444 33.835589999999996 -96.148964 33.828982999999994 -96.169205 33.815792 -96.183128 33.808434000000005 -96.180725 33.823944 -96.154518 33.820319999999995 -96.141418 33.798229000000006 -96.161316 33.769356 -96.168816 33.758583 -96.187027 33.756691000000004 -96.212547 33.773388 -96.278076 33.761932 -96.28968 33.71405 -96.300789 33.701801 -96.316277 33.705528 -96.347588 33.74039500000001 -96.370819 33.788326 -96.419464 33.778130000000004 -96.487373 33.78809 -96.500748 33.815685 -96.510574 33.825419999999994 -96.562134 33.842957 -96.601196 33.862899999999996 -96.614166 33.896145000000004 -96.584488 33.913544 -96.666237 33.904324 -96.677704 33.847904 -96.693382 33.833870000000005 -96.711678 33.831738 -96.748825 33.869949000000005 -96.797592 33.871769 -96.814117 33.858031999999994 -96.844009 33.861678999999995 -96.861015 33.88400300000001 -96.878937 33.92459100000001 -96.882851 33.950027000000006 -96.898453 33.961772999999994 -96.929565 33.947849000000005 -96.936203 33.937321 -96.968185 33.944202000000004 -96.987862 33.876423 -96.987709 33.85051300000001 -97.005852 33.840560999999994 -97.025597 33.856728000000004 -97.0709 33.851101 -97.082176 33.837811 -97.078247 33.823448 -97.050026 33.807570999999996 -97.087669 33.742413 -97.083466 33.731669999999994 -97.0905 33.725933 -97.115562 33.728668 -97.152473 33.752769 -97.189163 33.819649 -97.208321 33.836158999999995 -97.195015 33.84779399999999 -97.168594 33.863147999999995 -97.164169 33.899204 -97.187767 33.905688999999995 -97.211334 33.894238 -97.246063 33.87297100000001 -97.250687 33.858729999999994 -97.263908 33.872574 -97.272278 33.89584000000001 -97.314087 33.870391999999995 -97.314957 33.861915999999994 -97.341805 33.831024 -97.363319 33.820708999999994 -97.410118 33.836212 -97.452736 33.890429999999995 -97.457062 33.902382 -97.462761 33.907707 -97.477531 33.916771 -97.518204 33.903904 -97.554588 33.902530999999996 -97.575668 33.917885 -97.592354 33.969437 -97.600182 33.988613 -97.671066 33.971546000000004 -97.704262 33.939293000000006 -97.729019 33.932097999999996 -97.756363 33.890457 -97.790207 33.857071000000005 -97.852547 33.855114 -97.869751 33.874022999999994 -97.909065 33.883480000000006 -97.954735 33.90250399999999 -97.976379 33.912052 -97.976128 33.932518 -97.950684 33.94865 -97.962997 33.959751 -97.947754 33.971160999999995 -97.950226 34.00128599999999 -97.982681 33.98698400000001 -98.023491 33.989799000000005 -98.055557 34.005314 -98.086205 34.06982000000001 -98.110687 34.134556 -98.094124 34.148987000000005 -98.114868 34.138431999999995 -98.136864 34.115368000000004 -98.172844 34.122871 -98.277 34.13942 -98.320488 34.142120000000006 -98.350403 34.11578 -98.384254 34.087231 -98.390953 34.082454999999996 -98.407135 34.065830000000005 -98.421341 34.05437499999999 -98.448189 34.066413999999995 -98.499519 34.105335 -98.557579 34.14193 -98.576332 34.151398 -98.607246 34.158435999999995 -98.625992 34.147037999999995 -98.66172 34.149998 -98.682213 34.130714 -98.705292 34.131962 -98.778534 34.145934999999994 -98.811066 34.16082 -98.891342 34.194565 -98.952507 34.209496 -98.996193 34.198921 -99.035217 34.208359 -99.07843 34.201469 -99.127945 34.21272999999999 -99.176155 34.223736 -99.190483 34.255646 -99.204597 34.305122 -99.196304 34.331993 -99.20549 34.368213999999995 -99.254105 34.398285 -99.267174 34.412707999999995 -99.323296 34.450194999999994 -99.364204 34.428993000000006 -99.392792 34.396744 -99.394157 34.369106 -99.409958 34.364704 -99.438377 34.383522 -99.479439 34.404067999999995 -99.502136 34.41518000000001 -99.553864 34.408913 -99.57785 34.384857 -99.58522 34.368556999999996 -99.601448 34.377444999999994 -99.684906 34.443993000000006 -99.777687 34.501778 -99.829933 34.518626999999995 -99.860573 34.548176 -99.8806 34.579109 -99.931908 34.579571 -99.94474 34.561863 -99.972099 34.562321 -99.996094 34.747185 -99.998878 35.030998 -99.996071 35.182182 -99.997185 35.424572 -99.994354 35.618809 -100.0 35.883793 -99.997726 36.057548999999995 -99.997154 36.492519 -100.001144 36.493877 -100.006866 36.48944899999999 -100.549416 36.489609 -100.956909 36.488022 -101.089668 36.492003999999994 -101.620316 36.492954 -102.03421 36.490207999999996 -102.165222 36.492344 -102.996918 36.998504999999994 -102.997223 36.988972000000004 -102.036758 36.988853000000006 -102.024071 36.996666000000005 -101.553238 36.99744 -101.071159 36.996635 -100.950142 36.997806999999995 -100.633812 36.997623000000004 -100.088158 36.995388000000005 -99.998848 36.995434 -99.544243 36.99453 -99.43708 36.998042999999996 -98.999138 36.99834799999999 -98.539864 36.999035000000006 -98.346794 36.998645999999994 -98.104187 36.998543 -97.803917 36.996441000000004 -97.46508 36.999779000000004 -97.137375 37.000134 -96.74839 37.000538000000006 -96.518898 36.998264000000006 -96.005791 37.000011 -95.957703 36.998035 -95.785492 37.000938000000005 -95.525764 37.000538000000006 -95.406372 37.001408 -95.071693 37.00071 -95.032509 36.996983 -94.620155 36.763538 -94.62146 36.670471000000006 -94.62085 36.489338000000004 -94.617035 36.478714 -94.607231 36.164444 -94.552895 36.106753999999995 -94.542198 35.760227 -94.485718 35.641003 -94.468269 35.400454999999994 -94.428337 34.929050000000004 -94.439102</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Oklahoma</topp:STATE_NAME><topp:STATE_FIPS>40</topp:STATE_FIPS><topp:SUB_REGION>W S Cen</topp:SUB_REGION><topp:STATE_ABBR>OK</topp:STATE_ABBR><topp:LAND_KM>177877.536</topp:LAND_KM><topp:WATER_KM>3170.998</topp:WATER_KM><topp:PERSONS>3145585.0</topp:PERSONS><topp:FAMILIES>855321.0</topp:FAMILIES><topp:HOUSHOLD>1206135.0</topp:HOUSHOLD><topp:MALE>1530819.0</topp:MALE><topp:FEMALE>1614766.0</topp:FEMALE><topp:WORKERS>1146497.0</topp:WORKERS><topp:DRVALONE>1078894.0</topp:DRVALONE><topp:CARPOOL>190902.0</topp:CARPOOL><topp:PUBTRANS>7817.0</topp:PUBTRANS><topp:EMPLOYED>1369138.0</topp:EMPLOYED><topp:UNEMPLOY>100931.0</topp:UNEMPLOY><topp:SERVICE>405802.0</topp:SERVICE><topp:MANUAL>205601.0</topp:MANUAL><topp:P_MALE>0.487</topp:P_MALE><topp:P_FEMALE>0.513</topp:P_FEMALE><topp:SAMP_POP>528722.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.13"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>33.881992 -84.323853</gml:lowerCorner><gml:upperCorner>36.589648999999994 -75.456978</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>34.989067000000006 -83.988548 34.988224 -84.11824 34.98900999999999 -84.323853 35.210544999999996 -84.291039 35.261604000000005 -84.225945 35.24099 -84.179726 35.245552 -84.101646 35.272575 -84.042778 35.292522000000005 -84.030861 35.32529100000001 -84.029205 35.37286 -84.00631 35.407623 -84.01265 35.45546 -83.954704 35.476479 -83.910011 35.510586 -83.881226 35.519062000000005 -83.8302 35.552605 -83.775879 35.564972 -83.672874 35.571738999999994 -83.613861 35.555176 -83.561035 35.559551 -83.505798 35.597279 -83.45829 35.625217000000006 -83.387093 35.653259000000006 -83.343025 35.65632600000001 -83.298416 35.691010000000006 -83.259125 35.700706 -83.253304 35.718216 -83.243851 35.728886 -83.185349 35.76268399999999 -83.143661 35.763808999999995 -83.118187 35.782578 -83.059959 35.773990999999995 -82.987007 35.791850999999994 -82.962753 35.872215 -82.906822 35.927868000000004 -82.91407 35.948359999999994 -82.895973 35.94742599999999 -82.85627 35.920872 -82.80867 35.956573000000006 -82.776443 35.987503000000004 -82.773598 35.999545999999995 -82.763229 36.051722999999996 -82.64389 36.054339999999996 -82.628044 36.042987999999994 -82.604401 36.022448999999995 -82.592232 36.003547999999995 -82.605804 35.963299000000006 -82.599304 35.956108 -82.554146 35.972542000000004 -82.506935 35.993176000000005 -82.475197 36.075317 -82.408424 36.098698 -82.373856 36.122150000000005 -82.311928 36.120377000000005 -82.262306 36.147014999999996 -82.207733 36.139622 -82.154053 36.096256 -82.11808 36.100139999999996 -82.077759 36.129711 -82.020454 36.263321000000005 -81.933113 36.290752 -81.911156 36.334655999999995 -81.830574 36.329342 -81.730492 36.333725 -81.709457 36.361858 -81.740379 36.391785 -81.741074 36.471779 -81.69828 36.519341 -81.702797 36.589648999999994 -81.669998 36.57286499999999 -81.345299 36.565212 -80.903442 36.563438000000005 -80.838158 36.557297000000005 -80.611053 36.551044000000005 -80.43531 36.547134 -80.048096 36.545024999999995 -80.024055 36.547889999999995 -79.717445 36.547657 -79.5103 36.54978199999999 -79.217064 36.546059 -79.144325 36.543533 -78.7967 36.546074000000004 -78.737389 36.541481000000005 -78.458809 36.545531999999994 -78.321251 36.552475 -78.051666 36.552944 -77.898857 36.553439999999995 -77.763931 36.553917 -77.320053 36.556286 -77.177353 36.554146 -76.924133 36.554157000000004 -76.921631 36.555251999999996 -76.563583 36.555813 -76.497559 36.556056999999996 -76.330254 36.557163 -76.127396 36.55695299999999 -76.045959 36.514374000000004 -76.033211 36.503567000000004 -76.091064 36.43621400000001 -75.976074 36.415119000000004 -75.969765 36.418915 -76.00161 36.365471 -75.951256 36.423244 -75.928123 36.350948 -75.924591 36.112815999999995 -75.800056 36.072818999999996 -75.798851 36.105667 -75.855164 36.244800999999995 -75.913765 36.25945299999999 -75.957512 36.294337999999996 -75.941933 36.31959500000001 -76.008972 36.193771 -75.957184 36.169726999999995 -75.981339 36.315239000000005 -76.183167 36.296608000000006 -76.218903 36.174419 -76.112709 36.14769 -76.141937 36.163360999999995 -76.234978 36.21423 -76.298927 36.110371 -76.275505 36.079792 -76.48053 36.058605 -76.420425 36.00716799999999 -76.52301 36.010132 -76.594002 36.065708 -76.649017 36.037116999999995 -76.633209 36.04961 -76.690155 36.156822000000005 -76.726509 36.294518 -76.688736 36.358329999999995 -76.776642 36.392444999999995 -76.92408 36.315166000000005 -76.741348 36.266132 -76.707497 36.233917000000005 -76.745064 36.144592 -76.760674 35.99297 -76.693764 35.936626000000004 -76.741127 35.94154399999999 -76.69722 35.977467000000004 -76.40947 35.932343 -76.371483 35.97687500000001 -76.213768 35.962914 -76.089638 35.920424999999994 -76.026047 35.75680199999999 -76.075912 35.683848999999995 -76.043068 35.696842000000004 -76.167305 35.663639 -76.104744 35.669094 -76.02121 35.892708 -75.987854 35.923519 -75.818054 35.869339 -75.748962 35.665172999999996 -75.72937 35.578686000000005 -75.779053 35.631268000000006 -75.891495 35.414604 -76.052979 35.341533999999996 -76.181618 35.416218 -76.498802 35.450199 -76.533752 35.504425 -76.458435 35.550865 -76.446526 35.57764400000001 -76.518944 35.541786 -76.492538 35.520336 -76.638199 35.43789700000001 -76.628769 35.411934 -76.705376 35.550186 -77.103767 35.43650100000001 -76.983185 35.350426 -76.694901 35.272919 -76.614517 35.312079999999995 -76.591209 35.303687999999994 -76.540977 35.248768 -76.507126 35.172520000000006 -76.640091 35.138560999999996 -76.605225 35.064350000000005 -76.624222 35.024086 -76.678009 34.98222 -76.849464 35.027428 -76.94149 35.07003400000001 -76.943245 35.092155000000005 -77.047882 35.087986 -77.104675 35.066036 -77.110588 35.052887 -77.00119 34.93647 -76.913109 34.906334 -76.647049 34.969997000000006 -76.670059 34.974636000000004 -76.529778 35.016659000000004 -76.44503 34.946247 -76.424057 34.943111 -76.364014 34.975956 -76.329407 34.948814 -76.314713 34.785061 -76.469902 34.719753 -76.625313 34.698916999999994 -77.050529 34.764331999999996 -77.148956 34.684914000000006 -77.129395 34.660636999999994 -77.156601 34.457008 -77.538635 34.420936999999995 -77.586761 34.43504299999999 -77.609726 34.412628 -77.602615 34.305046000000004 -77.750526 34.192738000000006 -77.864388 34.06918 -77.894402 34.062034999999995 -77.926758 34.189243000000005 -77.960732 33.992580000000004 -77.958527 33.914291000000006 -78.034805 33.881992 -78.57972 33.948673 -78.655716 34.304573000000005 -79.074501 34.619061 -79.447144 34.63409 -79.455971 34.800663 -79.667503 34.805256 -79.685959 34.807919 -79.919952 34.814762 -80.325279 34.815228000000005 -80.561546 34.816113 -80.800064 34.940639000000004 -80.785652 35.00201800000001 -80.840164 35.059734000000006 -80.894714 35.10124999999999 -80.927795 35.037200999999996 -81.039886 35.066483000000005 -81.065552 35.105408 -81.028442 35.132011000000006 -81.049042 35.151531000000006 -81.049301 35.163757000000004 -81.322823 35.162853 -81.362175 35.182472000000004 -81.765358 35.183116999999996 -81.87059 35.188282 -81.971443 35.193127000000004 -82.210175 35.195007000000004 -82.278328 35.184189 -82.32077 35.192673 -82.350861 35.182948999999994 -82.360123 35.18272399999999 -82.371376 35.208241 -82.38961 35.16955900000001 -82.43792 35.173500000000004 -82.466743 35.154560000000004 -82.524635 35.14949 -82.57003 35.119457 -82.654495 35.121464 -82.68605 35.097801000000004 -82.688034 35.091232000000005 -82.697357 35.085423000000006 -82.771347 35.055369999999996 -82.887695 35.024204 -83.007278 35.000277999999994 -83.106285 34.992023 -83.513008 34.989536 -83.549416 34.989391 -83.937996 34.989067000000006 -83.988548</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.556717000000006 -76.027168 36.556652 -75.998665 36.54253 -75.911919 36.47397599999999 -75.924805 36.478016 -75.97728 36.517925000000005 -75.976288 36.556717000000006 -76.027168</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>36.556197999999995 -75.901985 36.555874 -75.878166 36.22925600000001 -75.773155 35.788360999999995 -75.544968 36.049862000000005 -75.702736 36.050323000000006 -75.740868 36.225193000000004 -75.783173 36.556197999999995 -75.901985</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>35.670497999999995 -75.491219 35.768856 -75.533623 35.617397 -75.456978 35.227917000000005 -75.526299 35.189826999999994 -75.74929 35.234989 -75.691566 35.281357 -75.521484 35.564494999999994 -75.475418 35.670497999999995 -75.491219</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>35.069435 -76.017006 35.116184000000004 -75.975693 35.167282 -75.853935 35.192284 -75.763702 35.164021000000005 -75.811539 35.132492 -75.903946 35.069435 -76.002876 35.069435 -76.017006</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>34.58783 -76.544273 34.61066099999999 -76.555145 34.613922 -76.53775 34.697632 -76.484482 34.760684999999995 -76.432297 34.812870000000004 -76.37468 34.87701 -76.287704 34.693282999999994 -76.469261 34.58783 -76.544273</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>North Carolina</topp:STATE_NAME><topp:STATE_FIPS>37</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>NC</topp:STATE_ABBR><topp:LAND_KM>126177.635</topp:LAND_KM><topp:WATER_KM>10309.652</topp:WATER_KM><topp:PERSONS>6628629.0</topp:PERSONS><topp:FAMILIES>1812051.0</topp:FAMILIES><topp:HOUSHOLD>2517024.0</topp:HOUSHOLD><topp:MALE>3214286.0</topp:MALE><topp:FEMALE>3414343.0</topp:FEMALE><topp:WORKERS>2484061.0</topp:WORKERS><topp:DRVALONE>2527980.0</topp:DRVALONE><topp:CARPOOL>529828.0</topp:CARPOOL><topp:PUBTRANS>33902.0</topp:PUBTRANS><topp:EMPLOYED>3238414.0</topp:EMPLOYED><topp:UNEMPLOY>163081.0</topp:UNEMPLOY><topp:SERVICE>883563.0</topp:SERVICE><topp:MANUAL>701142.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>1013599.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.14"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>34.988682 -90.305351</gml:lowerCorner><gml:upperCorner>36.67963 -81.652435</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>35.45546 -83.954704 35.407623 -84.01265 35.37286 -84.00631 35.32529100000001 -84.029205 35.292522000000005 -84.030861 35.272575 -84.042778 35.245552 -84.101646 35.24099 -84.179726 35.261604000000005 -84.225945 35.210544999999996 -84.291039 34.98900999999999 -84.323853 34.988682 -84.618752 34.990680999999995 -84.771194 34.992756 -84.800476 34.99260700000001 -84.967911 34.992554 -84.972847 34.989075 -85.267761 34.989902 -85.359985 34.990047000000004 -85.467422 34.990086000000005 -85.608994 34.992301999999995 -85.869591 34.995377000000005 -86.303513 34.995186000000004 -86.313065 34.99699 -86.782372 34.998160999999996 -86.83342 35.00787699999999 -87.207573 35.007262999999995 -87.222748 35.010464 -87.607788 35.015949000000006 -87.986046 35.013458 -88.194923 35.004368 -88.193954 35.00374600000001 -88.351685 35.004954999999995 -88.383102 35.003094000000004 -88.784988 35.00235000000001 -88.812492 35.000145 -89.006134 35.000793 -89.198067 34.999714 -89.3423 35.000641 -89.646477 34.999168 -89.717186 35.000693999999996 -90.305351 35.048458 -90.291809 35.040897 -90.195709 35.077827 -90.169083 35.10864599999999 -90.178345 35.129611999999995 -90.164474 35.13653600000001 -90.14373 35.12505 -90.082924 35.147385 -90.064537 35.166916 -90.062431 35.191833 -90.073303 35.212738 -90.068962 35.254397999999995 -90.090103 35.263847 -90.105942 35.264056999999994 -90.152122 35.282566 -90.169746 35.30624400000001 -90.15699 35.314685999999995 -90.106346 35.345591999999996 -90.098701 35.365982 -90.105621 35.381508 -90.087135 35.406527999999994 -90.075478 35.418282000000005 -90.085159 35.41768999999999 -90.112244 35.4076 -90.132469 35.383044999999996 -90.140167 35.384254 -90.167816 35.423716999999996 -90.172676 35.442524000000006 -90.137276 35.473568 -90.101959 35.478207 -90.08223 35.472342999999995 -90.074844 35.426506 -90.073936 35.41341 -90.060295 35.417103 -90.046783 35.445454 -89.999565 35.51244 -90.041817 35.542846999999995 -90.040901 35.552414 -90.033051 35.561676000000006 -89.989586 35.532291 -89.962273 35.526900999999995 -89.947548 35.52923199999999 -89.931175 35.546059 -89.921661 35.578593999999995 -89.958031 35.603104 -89.957047 35.633335 -89.877441 35.629745 -89.863838 35.645222000000004 -89.849197 35.671062000000006 -89.857246 35.673306 -89.865181 35.655972000000006 -89.893402 35.676266 -89.929741 35.712486 -89.952034 35.734268 -89.951035 35.754836999999995 -89.909782 35.748192 -89.859871 35.758269999999996 -89.827042 35.774223000000006 -89.799904 35.805553 -89.790382 35.817420999999996 -89.759796 35.807036999999994 -89.735939 35.827515000000005 -89.700829 35.842037000000005 -89.701439 35.871418000000006 -89.757713 35.884102 -89.766273 35.896812 -89.762909 35.915012000000004 -89.737976 35.911427 -89.714684 35.885647000000006 -89.66465 35.894287000000006 -89.649338 35.913799 -89.645401 35.93782 -89.664192 35.966324 -89.713135 35.999877999999995 -89.721756 36.025795 -89.688843 36.08297 -89.678169 36.099316 -89.667389 36.129791 -89.589424 36.15201999999999 -89.589455 36.18374300000001 -89.618561 36.220867 -89.676788 36.240795000000006 -89.695656 36.25213599999999 -89.694542 36.25489399999999 -89.670586 36.240898 -89.61808 36.257278 -89.541649 36.264538 -89.535378 36.280865000000006 -89.542236 36.308037 -89.606766 36.334782000000004 -89.622795 36.354752000000005 -89.60569 36.345721999999995 -89.544556 36.35593 -89.519333 36.401058000000006 -89.520004 36.440960000000004 -89.545181 36.47180899999999 -89.516022 36.498108 -89.533195 36.498547 -89.475822 36.465462 -89.491989 36.445953 -89.470833 36.456379 -89.448517 36.502617 -89.414711 36.502548000000004 -89.346596 36.499793999999994 -88.830315 36.499847 -88.826302 36.498985000000005 -88.810661 36.499489 -88.512634 36.498149999999995 -88.495979 36.496517 -88.042732 36.538146999999995 -88.035049 36.582668 -88.041061 36.67963 -88.071312 36.66937299999999 -87.870689 36.64147199999999 -87.853516 36.64444 -87.693512 36.645168 -87.64064 36.649231 -87.346603 36.651264 -87.112701 36.650768 -87.068184 36.652058 -86.770538 36.655033 -86.510674 36.650890000000004 -86.415451 36.643246000000005 -86.199005 36.63306 -85.980629 36.626628999999994 -85.7855 36.618137000000004 -85.437408 36.626037999999994 -85.300133 36.625553 -85.272537 36.620914 -84.998512 36.605366000000004 -84.791115 36.605002999999996 -84.781929 36.595417 -84.256844 36.595371 -84.254555 36.592006999999995 -84.006821 36.591209000000006 -83.935677 36.584166999999994 -83.695694 36.598620999999994 -83.675262 36.598755 -83.464302 36.60038 -83.275131 36.589848 -83.248489 36.588001000000006 -83.211029 36.591198000000006 -82.986809 36.590946 -82.849937 36.591446000000005 -82.610962 36.591697999999994 -82.296997 36.593967000000006 -82.216805 36.595043000000004 -82.154327 36.595837 -81.929459 36.613495 -81.918449 36.611481 -81.829056 36.607555000000005 -81.652435 36.589648999999994 -81.669998 36.519341 -81.702797 36.471779 -81.69828 36.391785 -81.741074 36.361858 -81.740379 36.333725 -81.709457 36.329342 -81.730492 36.334655999999995 -81.830574 36.290752 -81.911156 36.263321000000005 -81.933113 36.129711 -82.020454 36.100139999999996 -82.077759 36.096256 -82.11808 36.139622 -82.154053 36.147014999999996 -82.207733 36.120377000000005 -82.262306 36.122150000000005 -82.311928 36.098698 -82.373856 36.075317 -82.408424 35.993176000000005 -82.475197 35.972542000000004 -82.506935 35.956108 -82.554146 35.963299000000006 -82.599304 36.003547999999995 -82.605804 36.022448999999995 -82.592232 36.042987999999994 -82.604401 36.054339999999996 -82.628044 36.051722999999996 -82.64389 35.999545999999995 -82.763229 35.987503000000004 -82.773598 35.956573000000006 -82.776443 35.920872 -82.80867 35.94742599999999 -82.85627 35.948359999999994 -82.895973 35.927868000000004 -82.91407 35.872215 -82.906822 35.791850999999994 -82.962753 35.773990999999995 -82.987007 35.782578 -83.059959 35.763808999999995 -83.118187 35.76268399999999 -83.143661 35.728886 -83.185349 35.718216 -83.243851 35.700706 -83.253304 35.691010000000006 -83.259125 35.65632600000001 -83.298416 35.653259000000006 -83.343025 35.625217000000006 -83.387093 35.597279 -83.45829 35.559551 -83.505798 35.555176 -83.561035 35.571738999999994 -83.613861 35.564972 -83.672874 35.552605 -83.775879 35.519062000000005 -83.8302 35.510586 -83.881226 35.476479 -83.910011 35.45546 -83.954704</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Tennessee</topp:STATE_NAME><topp:STATE_FIPS>47</topp:STATE_FIPS><topp:SUB_REGION>E S Cen</topp:SUB_REGION><topp:STATE_ABBR>TN</topp:STATE_ABBR><topp:LAND_KM>105823.567</topp:LAND_KM><topp:WATER_KM>2311.556</topp:WATER_KM><topp:PERSONS>4829958.0</topp:PERSONS><topp:FAMILIES>1334052.0</topp:FAMILIES><topp:HOUSHOLD>1835272.0</topp:HOUSHOLD><topp:MALE>2326184.0</topp:MALE><topp:FEMALE>2503774.0</topp:FEMALE><topp:WORKERS>1777371.0</topp:WORKERS><topp:DRVALONE>1747015.0</topp:DRVALONE><topp:CARPOOL>321170.0</topp:CARPOOL><topp:PUBTRANS>29542.0</topp:PUBTRANS><topp:EMPLOYED>2230659.0</topp:EMPLOYED><topp:UNEMPLOY>152128.0</topp:UNEMPLOY><topp:SERVICE>596400.0</topp:SERVICE><topp:MANUAL>456189.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>736654.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.15"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>25.845197999999996 -106.649513</gml:lowerCorner><gml:upperCorner>36.493877 -93.507217</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>31.393817999999996 -105.99836 31.478127999999998 -106.212753 31.733762999999996 -106.383041 31.786198 -106.538971 31.817728000000002 -106.614441 31.844634999999997 -106.615578 31.895101999999994 -106.643532 31.913998000000007 -106.633202 31.972117999999995 -106.632057 31.980227999999997 -106.649513 32.00098800000001 -106.623077 32.000645000000006 -106.377846 32.001553 -106.002708 32.004268999999994 -104.921799 32.003151 -104.850563 32.007278 -104.018814 32.005889999999994 -103.980896 32.00610399999999 -103.728973 32.004154 -103.332092 32.001900000000006 -103.057968 32.084995000000006 -103.055191 32.515429999999995 -103.059547 32.95353299999999 -103.048836 33.377728000000005 -103.042603 33.565742 -103.038239 33.826088 -103.032761 34.307743 -103.029144 34.745266 -103.022156 34.964718000000005 -103.02475 35.17720799999999 -103.02565 35.623604 -103.021797 35.742287000000005 -103.022118 36.056026 -103.02356 36.491566000000006 -103.026802 36.492344 -102.996918 36.490207999999996 -102.165222 36.492954 -102.03421 36.492003999999994 -101.620316 36.488022 -101.089668 36.489609 -100.956909 36.48944899999999 -100.549416 36.493877 -100.006866 36.492519 -100.001144 36.057548999999995 -99.997154 35.883793 -99.997726 35.618809 -100.0 35.424572 -99.994354 35.182182 -99.997185 35.030998 -99.996071 34.747185 -99.998878 34.562321 -99.996094 34.561863 -99.972099 34.579571 -99.94474 34.579109 -99.931908 34.548176 -99.8806 34.518626999999995 -99.860573 34.501778 -99.829933 34.443993000000006 -99.777687 34.377444999999994 -99.684906 34.368556999999996 -99.601448 34.384857 -99.58522 34.408913 -99.57785 34.41518000000001 -99.553864 34.404067999999995 -99.502136 34.383522 -99.479439 34.364704 -99.438377 34.369106 -99.409958 34.396744 -99.394157 34.428993000000006 -99.392792 34.450194999999994 -99.364204 34.412707999999995 -99.323296 34.398285 -99.267174 34.368213999999995 -99.254105 34.331993 -99.20549 34.305122 -99.196304 34.255646 -99.204597 34.223736 -99.190483 34.21272999999999 -99.176155 34.201469 -99.127945 34.208359 -99.07843 34.198921 -99.035217 34.209496 -98.996193 34.194565 -98.952507 34.16082 -98.891342 34.145934999999994 -98.811066 34.131962 -98.778534 34.130714 -98.705292 34.149998 -98.682213 34.147037999999995 -98.66172 34.158435999999995 -98.625992 34.151398 -98.607246 34.14193 -98.576332 34.105335 -98.557579 34.066413999999995 -98.499519 34.05437499999999 -98.448189 34.065830000000005 -98.421341 34.082454999999996 -98.407135 34.087231 -98.390953 34.11578 -98.384254 34.142120000000006 -98.350403 34.13942 -98.320488 34.122871 -98.277 34.115368000000004 -98.172844 34.138431999999995 -98.136864 34.148987000000005 -98.114868 34.134556 -98.094124 34.06982000000001 -98.110687 34.005314 -98.086205 33.989799000000005 -98.055557 33.98698400000001 -98.023491 34.00128599999999 -97.982681 33.971160999999995 -97.950226 33.959751 -97.947754 33.94865 -97.962997 33.932518 -97.950684 33.912052 -97.976128 33.90250399999999 -97.976379 33.883480000000006 -97.954735 33.874022999999994 -97.909065 33.855114 -97.869751 33.857071000000005 -97.852547 33.890457 -97.790207 33.932097999999996 -97.756363 33.939293000000006 -97.729019 33.971546000000004 -97.704262 33.988613 -97.671066 33.969437 -97.600182 33.917885 -97.592354 33.902530999999996 -97.575668 33.903904 -97.554588 33.916771 -97.518204 33.907707 -97.477531 33.902382 -97.462761 33.890429999999995 -97.457062 33.836212 -97.452736 33.820708999999994 -97.410118 33.831024 -97.363319 33.861915999999994 -97.341805 33.870391999999995 -97.314957 33.89584000000001 -97.314087 33.872574 -97.272278 33.858729999999994 -97.263908 33.87297100000001 -97.250687 33.894238 -97.246063 33.905688999999995 -97.211334 33.899204 -97.187767 33.863147999999995 -97.164169 33.84779399999999 -97.168594 33.836158999999995 -97.195015 33.819649 -97.208321 33.752769 -97.189163 33.728668 -97.152473 33.725933 -97.115562 33.731669999999994 -97.0905 33.742413 -97.083466 33.807570999999996 -97.087669 33.823448 -97.050026 33.837811 -97.078247 33.851101 -97.082176 33.856728000000004 -97.0709 33.840560999999994 -97.025597 33.85051300000001 -97.005852 33.876423 -96.987709 33.944202000000004 -96.987862 33.937321 -96.968185 33.947849000000005 -96.936203 33.961772999999994 -96.929565 33.950027000000006 -96.898453 33.92459100000001 -96.882851 33.88400300000001 -96.878937 33.861678999999995 -96.861015 33.858031999999994 -96.844009 33.871769 -96.814117 33.869949000000005 -96.797592 33.831738 -96.748825 33.833870000000005 -96.711678 33.847904 -96.693382 33.904324 -96.677704 33.913544 -96.666237 33.896145000000004 -96.584488 33.862899999999996 -96.614166 33.842957 -96.601196 33.825419999999994 -96.562134 33.815685 -96.510574 33.78809 -96.500748 33.778130000000004 -96.487373 33.788326 -96.419464 33.74039500000001 -96.370819 33.705528 -96.347588 33.701801 -96.316277 33.71405 -96.300789 33.761932 -96.28968 33.773388 -96.278076 33.756691000000004 -96.212547 33.758583 -96.187027 33.769356 -96.168816 33.798229000000006 -96.161316 33.820319999999995 -96.141418 33.823944 -96.154518 33.808434000000005 -96.180725 33.815792 -96.183128 33.828982999999994 -96.169205 33.835589999999996 -96.148964 33.829257999999996 -96.109444 33.844578 -96.091522 33.841278 -96.047974 33.856021999999996 -96.026749 33.844207999999995 -96.014069 33.856978999999995 -96.001793 33.87339 -96.002617 33.875378 -95.994209 33.857952 -95.977394 33.86503999999999 -95.958763 33.889973 -95.943069 33.89053 -95.933075 33.841038 -95.846558 33.843025 -95.825974 33.864673999999994 -95.795479 33.85140199999999 -95.768517 33.87900500000001 -95.764252 33.89344 -95.760696 33.903397 -95.746864 33.894825 -95.699707 33.92010500000001 -95.633492 33.920238 -95.612984 33.936690999999996 -95.61483 33.944553 -95.606071 33.93607299999999 -95.562775 33.904033999999996 -95.546318 33.906643 -95.519577 33.897816000000006 -95.526733 33.893158 -95.547493 33.88574199999999 -95.544037 33.897735999999995 -95.512886 33.881718000000006 -95.498856 33.886433 -95.468124 33.865753 -95.451607 33.870918 -95.33004 33.897114 -95.336227 33.886623 -95.301956 33.886902000000006 -95.28643 33.91793800000001 -95.277351 33.897800000000004 -95.263618 33.905022 -95.250992 33.936443 -95.251289 33.964862999999994 -95.234039 33.943546 -95.148315 33.940867999999995 -95.127968 33.917145000000005 -95.126678 33.91228099999999 -95.119225 33.921738000000005 -95.09536 33.918453 -95.082268 33.89691500000001 -95.089714 33.888462000000004 -95.083603 33.917648 -95.063477 33.896694 -95.063141 33.884445 -95.042862 33.866451 -95.037361 33.869946 -95.012772 33.856182000000004 -94.989281 33.866215 -94.968704 33.848076000000006 -94.959908 33.840824 -94.939888 33.81580700000001 -94.940399 33.816196000000005 -94.918236 33.803478 -94.908546 33.789597 -94.913879 33.774963 -94.881638 33.749320999999995 -94.85788 33.749404999999996 -94.81916 33.739582 -94.803223 33.75326200000001 -94.783508 33.752842 -94.764175 33.742267999999996 -94.782028 33.733665 -94.783157 33.736706 -94.749771 33.716797 -94.762718 33.719048 -94.742111 33.707770999999994 -94.754478 33.701267 -94.741653 33.69028900000001 -94.690987 33.696537000000006 -94.668457 33.692291 -94.655479 33.67765 -94.644325 33.671459 -94.667953 33.666061 -94.669426 33.663737999999995 -94.658539 33.67010500000001 -94.638763 33.683899 -94.631737 33.665606999999994 -94.600945 33.678982000000005 -94.585106 33.670471000000006 -94.578506 33.671913 -94.560722 33.66301300000001 -94.565208 33.662132 -94.585159 33.655449000000004 -94.588387 33.652157 -94.576462 33.661621 -94.545418 33.648247 -94.541931 33.642830000000004 -94.562195 33.635536 -94.562149 33.632694 -94.550194 33.643009000000006 -94.51799 33.621021 -94.525055 33.63081 -94.510559 33.623047 -94.50061 33.631966000000006 -94.476486 33.636444 -94.435913 33.616844 -94.436333 33.604347000000004 -94.451553 33.596503999999996 -94.443329 33.59714099999999 -94.428467 33.573486 -94.40657 33.57495900000001 -94.393417 33.593327 -94.379112 33.590042 -94.370628 33.572661999999994 -94.372307 33.560303000000005 -94.395264 33.547684000000004 -94.370758 33.57313499999999 -94.328751 33.556934 -94.302383 33.579853 -94.29882 33.589332999999996 -94.278984 33.584605999999994 -94.272079 33.561736999999994 -94.274544 33.592422 -94.237236 33.585719999999995 -94.223038 33.561535000000006 -94.235367 33.557987 -94.210884 33.58507899999999 -94.205345 33.593773 -94.159515 33.567085000000006 -94.155167 33.572998 -94.098701 33.583954000000006 -94.086655 33.577213 -94.061432 33.555912000000006 -94.035927 33.270325 -94.036507 33.023289000000005 -94.03875 32.882346999999996 -94.041603 32.694812999999996 -94.040199 32.389224999999996 -94.035233 32.199448000000004 -94.034767 31.994512999999998 -94.035065 31.989134000000007 -94.009888 31.977942 -94.004395 31.946158999999994 -93.977211 31.923164 -93.969986 31.909456000000006 -93.93573 31.909701999999996 -93.917923 31.892593000000005 -93.923462 31.894454999999994 -93.899261 31.870065999999994 -93.892525 31.87142 -93.881264 31.850112999999993 -93.877403 31.817272000000003 -93.864822 31.802017000000006 -93.834328 31.774637 -93.822067 31.753281 -93.831161 31.730351999999996 -93.80999 31.712350999999998 -93.814949 31.707565000000002 -93.808769 31.711394999999996 -93.792267 31.674566 -93.811844 31.653767000000002 -93.806427 31.647965999999997 -93.814728 31.618092000000004 -93.819588 31.615189 -93.835579 31.590182999999996 -93.832619 31.577110000000005 -93.816322 31.559062999999995 -93.810516 31.533734999999993 -93.780128 31.530724000000006 -93.763306 31.537717999999998 -93.747543 31.521877000000003 -93.731659 31.520568999999995 -93.705795 31.495402999999996 -93.718994 31.490556999999995 -93.750435 31.485500000000002 -93.751244 31.459474999999998 -93.726784 31.461457999999993 -93.698418 31.446251000000004 -93.701927 31.438131 -93.687004 31.427735999999996 -93.696129 31.415921999999995 -93.694443 31.406130000000005 -93.687492 31.398329000000004 -93.664017 31.372394999999997 -93.661072 31.373827000000006 -93.634857 31.328385999999995 -93.67704 31.312679000000003 -93.681587 31.286671 -93.656128 31.290262 -93.645592 31.273903000000004 -93.630829 31.275805000000005 -93.616455 31.270032999999998 -93.611877 31.242188 -93.611 31.229687 -93.590546 31.199066000000002 -93.602921 31.180199 -93.593941 31.17214 -93.576942 31.190928999999997 -93.550591 31.185773999999995 -93.528923 31.178076000000004 -93.526932 31.176339999999996 -93.537018 31.162943 -93.528328 31.159166 -93.544189 31.132441 -93.537506 31.125924999999995 -93.528091 31.116072000000003 -93.535088 31.109342999999996 -93.556679 31.100536000000005 -93.559982 31.094751000000002 -93.543121 31.082373000000004 -93.544106 31.074670999999995 -93.516998 31.056979999999996 -93.525742 31.038908000000006 -93.507217 31.014140999999995 -93.547119 31.018062999999998 -93.564941 31.012923999999998 -93.567894 30.997270999999998 -93.570847 30.991690000000006 -93.560951 30.976177000000007 -93.572456 30.970190000000002 -93.548676 30.956884000000002 -93.537338 30.960731999999993 -93.532188 30.935820000000007 -93.52562 30.926970999999995 -93.529984 30.924885000000003 -93.549622 30.905333999999996 -93.546516 30.901932000000002 -93.564476 30.886234 -93.568497 30.871880000000004 -93.560844 30.860282999999995 -93.552803 30.845147999999995 -93.566444 30.842342000000002 -93.555641 30.828344 -93.550682 30.802040000000005 -93.581871 30.772183999999996 -93.585175 30.745789000000002 -93.618454 30.732010000000002 -93.607651 30.732547999999994 -93.61779 30.710329 -93.612411 30.686802 -93.617607 30.672859000000003 -93.659988 30.639691999999997 -93.677971 30.640040999999997 -93.692879 30.623424999999997 -93.684586 30.615795000000006 -93.692696 30.597831999999997 -93.671585 30.598834999999994 -93.69342 30.587379 -93.717812 30.568152999999995 -93.71788 30.545517000000004 -93.735306 30.522858 -93.70546 30.505114000000006 -93.714638 30.496240999999998 -93.707275 30.488628000000006 -93.714851 30.470046999999994 -93.697975 30.462513 -93.703423 30.442633 -93.696571 30.43298 -93.721535 30.408822999999998 -93.742561 30.381788 -93.754944 30.367411000000004 -93.747833 30.354145000000003 -93.759338 30.340872000000005 -93.759178 30.304916000000006 -93.729774 30.297387999999998 -93.699211 30.239372000000003 -93.707359 30.220305999999994 -93.714844 30.180860999999993 -93.704361 30.175675999999996 -93.696167 30.150807999999998 -93.699661 30.148231999999993 -93.683144 30.141253000000006 -93.685959 30.141226000000003 -93.698639 30.117929000000004 -93.696922 30.114739999999998 -93.708382 30.095669 -93.715858 30.060519999999997 -93.712479 30.005964000000006 -93.760201 29.990654000000006 -93.857277 29.964602 -93.856331 29.818363000000005 -93.951767 29.674570000000003 -93.834961 29.674076 -94.065407 29.5599 -94.356995 29.551969999999997 -94.377007 29.432905000000005 -94.682518 29.363992999999994 -94.766548 29.383261000000005 -94.785248 29.475111 -94.681915 29.533051999999998 -94.572693 29.517522999999997 -94.501282 29.556780000000003 -94.469795 29.545147 -94.510811 29.553984 -94.533699 29.578998999999996 -94.564438 29.538556999999997 -94.788086 29.658516000000006 -94.706421 29.754568000000006 -94.700279 29.792986 -94.735725 29.759856999999997 -94.829414 29.668538999999996 -94.887161 29.682209 -94.932587 29.803979999999996 -95.088264 29.711578000000003 -95.040398 29.679700999999994 -94.989334 29.559264999999996 -95.014122 29.500333999999995 -94.911156 29.460527 -94.982811 29.464681999999996 -94.943756 29.424234 -94.952507 29.420113 -94.913445 29.447823 -94.916992 29.399323999999993 -94.891136 29.370931999999996 -94.815353 29.393831000000006 -94.891472 29.308774999999997 -94.898788 29.325922000000006 -94.951134 29.195876999999996 -95.066368 29.200030999999996 -95.160522 29.117548 -95.16478 29.105222999999995 -95.197342 28.978392 -95.248405 28.803241999999997 -95.526581 28.726954000000006 -95.683029 28.752681999999993 -95.671318 28.738871000000003 -95.786354 28.690454000000003 -95.937309 28.622673000000006 -95.956146 28.718987 -95.702148 28.488386000000006 -96.206581 28.596424 -95.991646 28.653132999999997 -95.983749 28.571320999999998 -96.237587 28.597116 -96.239029 28.611231000000004 -96.157471 28.634859000000006 -96.240456 28.762671999999995 -96.151062 28.686719999999994 -96.212173 28.661724000000007 -96.285973 28.708980999999994 -96.270378 28.634089000000003 -96.326157 28.617981 -96.364159 28.670252000000005 -96.391777 28.726028 -96.392731 28.712013 -96.427086 28.755035000000007 -96.449677 28.697248000000002 -96.432259 28.719493999999997 -96.403397 28.638664000000006 -96.418785 28.610088000000005 -96.375397 28.556944 -96.491203 28.596991000000003 -96.437157 28.655933000000005 -96.454384 28.598055000000002 -96.483269 28.608181000000002 -96.511894 28.649541999999997 -96.511734 28.636267000000004 -96.570396 28.691840999999997 -96.570557 28.808173999999994 -96.572212 28.690689000000006 -96.576485 28.71736 -96.591499 28.714141999999995 -96.646515 28.679074999999997 -96.660011 28.623633999999996 -96.606705 28.558941000000004 -96.610344 28.574099000000004 -96.566704 28.506221999999994 -96.48658 28.469627000000003 -96.563194 28.460826999999995 -96.518501 28.499454 -96.476501 28.434059000000005 -96.390724 28.306263 -96.661308 28.340197000000003 -96.702362 28.395885000000007 -96.703812 28.403458 -96.740768 28.477507000000003 -96.787094 28.449640000000002 -96.823875 28.446254999999994 -96.788338 28.410911999999996 -96.759102 28.391630000000006 -96.77536 28.404996999999995 -96.853493 28.352469999999997 -96.788231 28.312859000000003 -96.78627 28.271372 -96.793335 28.229349 -96.777931 28.211447000000007 -96.803688 28.114355000000003 -96.950905 28.256798000000003 -96.91272 28.210751000000002 -96.975304 28.186770999999993 -96.941071 28.115046000000007 -96.975105 28.137398000000005 -97.033615 28.199797000000004 -97.023567 28.130426 -97.131836 28.161810000000003 -97.135414 28.159459999999996 -97.167992 28.116381000000004 -97.157059 28.064724 -97.260284 28.048653 -97.241234 28.025931999999997 -97.270294 28.04052 -97.236214 28.054266 -97.123077 28.107749999999996 -97.026405 28.020236999999995 -97.023804 27.915385999999998 -97.114624 27.812219999999996 -97.195465 27.822318999999993 -97.247025 27.831109999999995 -97.213341 27.871145 -97.283485 27.839954000000006 -97.361046 27.873177999999996 -97.345619 27.852962000000005 -97.479355 27.875468999999995 -97.496681 27.863625999999996 -97.521698 27.843243 -97.499535 27.820282000000006 -97.479813 27.831427000000005 -97.388542 27.770840000000007 -97.396561 27.712225000000004 -97.317795 27.715328 -97.34951 27.690634000000003 -97.320015 27.6408 -97.353363 27.633185999999995 -97.399216 27.631439 -97.347504 27.707863000000003 -97.309212 27.688830999999993 -97.249794 27.562320999999997 -97.331459 27.321023999999994 -97.412262 27.319667999999993 -97.500435 27.439215000000004 -97.507538 27.344100999999995 -97.528381 27.300134999999997 -97.600113 27.419666000000007 -97.750076 27.294372999999993 -97.680008 27.287719999999993 -97.784744 27.230206999999993 -97.548157 27.265133000000006 -97.427216 27.081541 -97.503502 26.996508000000006 -97.478996 26.977857999999998 -97.568565 26.846052 -97.558052 26.793780999999996 -97.495575 26.600984999999994 -97.451698 26.518225 -97.425858 26.476805 -97.474709 26.385059 -97.421188 26.35906 -97.368698 26.182449000000005 -97.353363 26.068315999999996 -97.25312 26.002274999999997 -97.276321 26.009068 -97.213097 25.954569000000006 -97.172226 25.965124000000003 -97.307144 25.938663000000005 -97.304436 25.917021000000005 -97.380989 25.845361999999994 -97.385643 25.845197999999996 -97.434349 25.933231000000006 -97.590088 25.954172 -97.574936 25.962002 -97.612923 26.023444999999995 -97.647972 26.060141 -97.867432 26.059394999999995 -98.04007 26.034626000000003 -98.076347 26.065758000000002 -98.083214 26.055375999999995 -98.200691 26.098104000000006 -98.291946 26.120895000000004 -98.271355 26.132808999999995 -98.292274 26.111647000000005 -98.327934 26.158680000000004 -98.347191 26.156031 -98.384521 26.220911 -98.453392 26.201544 -98.488518 26.260453999999996 -98.599968 26.242056000000005 -98.677917 26.375071000000005 -98.819832 26.360328999999993 -98.908897 26.395308999999997 -98.93927 26.419531000000006 -99.106728 26.488339999999994 -99.101471 26.545728999999994 -99.168678 26.579888999999994 -99.165817 26.857360999999997 -99.285522 26.94663 -99.390518 26.995549999999994 -99.392715 27.028648000000004 -99.455063 27.199197999999996 -99.437157 27.269884000000005 -99.465271 27.318652999999998 -99.543587 27.490754999999993 -99.490494 27.504284 -99.526741 27.612627000000003 -99.549187 27.661558 -99.714493 27.780107 -99.815727 27.797686 -99.874733 27.986880999999997 -99.941856 28.003460000000004 -99.993309 28.154281999999995 -100.096924 28.201935000000006 -100.214073 28.241456999999997 -100.223465 28.280354000000003 -100.29792 28.320359999999994 -100.292892 28.394182 -100.35157 28.478651 -100.37677 28.500811 -100.345802 28.544190999999998 -100.419533 28.589732999999995 -100.403175 28.660988000000003 -100.49791 28.894222 -100.58979 28.922349999999994 -100.647224 29.080072 -100.66877 29.166571000000005 -100.768608 29.242502 -100.796989 29.373255 -101.009056 29.473552999999995 -101.06736 29.526473999999993 -101.261429 29.628749999999997 -101.254585 29.580910000000003 -101.308929 29.652431000000007 -101.305862 29.657162 -101.368401 29.745434000000003 -101.4161 29.769904999999994 -101.401276 29.760586000000004 -101.448425 29.788691 -101.470467 29.763018000000002 -101.538345 29.810119999999998 -101.543953 29.765150000000006 -101.58149 29.756960000000007 -101.639671 29.787166999999997 -101.759094 29.779999000000004 -101.805206 29.814125000000004 -101.819099 29.788501999999994 -101.924225 29.818772999999993 -101.97332 29.784571 -102.063995 29.880116 -102.324333 29.845288999999994 -102.367561 29.767945999999995 -102.384796 29.785455999999996 -102.503098 29.749499999999998 -102.551949 29.778248000000005 -102.5765 29.732338 -102.637611 29.744225 -102.676361 29.530146000000002 -102.804726 29.411844000000002 -102.822205 29.353370999999996 -102.883011 29.269203000000005 -102.908325 29.229035999999994 -102.866173 29.190862999999993 -102.988098 28.978682000000006 -103.153465 29.007453999999996 -103.266586 28.986373999999998 -103.28035 29.050338999999994 -103.335518 29.032107999999994 -103.37545 29.072134000000005 -103.474075 29.146646000000004 -103.526237 29.190631999999994 -103.720314 29.230349000000004 -103.739853 29.229794999999996 -103.782158 29.281239999999997 -103.767761 29.267259999999993 -103.786995 29.328119 -104.045631 29.400715000000005 -104.164383 29.484041000000005 -104.204735 29.550611000000004 -104.377594 29.679466000000005 -104.535248 29.807935999999998 -104.57756 29.909283000000002 -104.67437 30.057302000000007 -104.696495 30.148964000000007 -104.674759 30.238489 -104.702614 30.35047 -104.813957 30.376447999999996 -104.806473 30.392263 -104.852997 30.570556999999994 -104.890678 30.641324999999995 -104.986931 30.684334000000007 -104.997543 30.687870000000004 -105.060562 30.812085999999994 -105.21434 30.797652999999997 -105.258186 30.831948999999994 -105.287598 30.816507 -105.313782 30.853081000000003 -105.390312 30.902510000000007 -105.409065 30.998284999999996 -105.554382 31.086427999999998 -105.603218 31.170779999999993 -105.76973 31.393817999999996 -105.99836</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>29.257571999999996 -94.913429 29.342450999999997 -94.76738 29.319490000000002 -94.748405 29.096958 -95.105415 29.257571999999996 -94.913429</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.345845999999995 -96.397881 28.066322 -96.834625 28.172161000000003 -96.803841 28.183535000000006 -96.738907 28.318245000000005 -96.532135 28.325832000000005 -96.463051 28.391439000000005 -96.422554 28.345845999999995 -96.397881</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.045933000000005 -96.939964 28.131405 -96.872421 28.101840999999993 -96.837425 28.049402 -96.853806 27.840953999999996 -97.049606 27.914381000000006 -97.024429 27.984526000000002 -96.949387 28.000853000000006 -96.973 28.045933000000005 -96.939964</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>27.283717999999993 -97.359116 27.210453 -97.379379 27.284643000000003 -97.376091 27.440819000000005 -97.335503 27.581132999999994 -97.248672 27.651748999999995 -97.258682 27.612064000000004 -97.203583 27.707459999999998 -97.170181 27.811273999999997 -97.075294 27.819215999999997 -97.113037 27.830472999999998 -97.053566 27.574006999999995 -97.223717 27.283717999999993 -97.359116</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>26.601022999999998 -97.301132 26.706646000000006 -97.358025 26.820375 -97.381332 26.921988 -97.395309 27.111227 -97.400909 27.201651 -97.388786 27.204448999999997 -97.378532 27.097244000000003 -97.386925 26.949022 -97.381332 26.802663999999993 -97.358025 26.600669999999994 -97.295837 26.418142000000003 -97.232109 26.259241000000003 -97.194992 26.077720999999997 -97.171799 26.071920000000006 -97.179337 26.250541999999996 -97.208916 26.348551 -97.226311 26.419303999999997 -97.251251 26.478904999999997 -97.26725 26.539963 -97.278549 26.601022999999998 -97.301132</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Texas</topp:STATE_NAME><topp:STATE_FIPS>48</topp:STATE_FIPS><topp:SUB_REGION>W S Cen</topp:SUB_REGION><topp:STATE_ABBR>TX</topp:STATE_ABBR><topp:LAND_KM>688219.07</topp:LAND_KM><topp:WATER_KM>17337.549</topp:WATER_KM><topp:PERSONS>1.712202E7</topp:PERSONS><topp:FAMILIES>4377106.0</topp:FAMILIES><topp:HOUSHOLD>6115966.0</topp:HOUSHOLD><topp:MALE>8433346.00000001</topp:MALE><topp:FEMALE>8688674.0</topp:FEMALE><topp:WORKERS>6192585.0</topp:WORKERS><topp:DRVALONE>5860490.0</topp:DRVALONE><topp:CARPOOL>1142908.0</topp:CARPOOL><topp:PUBTRANS>168814.0</topp:PUBTRANS><topp:EMPLOYED>7687338.0</topp:EMPLOYED><topp:UNEMPLOY>590269.0</topp:UNEMPLOY><topp:SERVICE>2139266.0</topp:SERVICE><topp:MANUAL>1042397.0</topp:MANUAL><topp:P_MALE>0.493</topp:P_MALE><topp:P_FEMALE>0.507</topp:P_FEMALE><topp:SAMP_POP>2487642.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.16"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>31.343348000000006 -109.050728</gml:lowerCorner><gml:upperCorner>36.999741 -102.996918</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>32.441967000000005 -109.048882 32.77948000000001 -109.050728 33.205101 -109.049904 33.783249 -109.049721 34.59174 -109.048012 34.954612999999995 -109.045998 35.996655000000004 -109.047195 36.996643000000006 -109.047821 36.999474000000006 -108.371834 36.998772 -107.471855 36.997519999999994 -107.410217 36.999072999999996 -106.889778 36.989491 -106.860657 36.991493000000006 -106.471588 36.99227500000001 -105.991425 36.994541 -105.712891 36.992580000000004 -105.212532 36.993183 -105.145615 36.994445999999996 -103.993111 36.999741 -103.077377 36.998504999999994 -102.997223 36.492344 -102.996918 36.491566000000006 -103.026802 36.056026 -103.02356 35.742287000000005 -103.022118 35.623604 -103.021797 35.17720799999999 -103.02565 34.964718000000005 -103.02475 34.745266 -103.022156 34.307743 -103.029144 33.826088 -103.032761 33.565742 -103.038239 33.377728000000005 -103.042603 32.95353299999999 -103.048836 32.515429999999995 -103.059547 32.084995000000006 -103.055191 32.001900000000006 -103.057968 32.004154 -103.332092 32.00610399999999 -103.728973 32.005889999999994 -103.980896 32.007278 -104.018814 32.003151 -104.850563 32.004268999999994 -104.921799 32.001553 -106.002708 32.000645000000006 -106.377846 32.00098800000001 -106.623077 31.980227999999997 -106.649513 31.972117999999995 -106.632057 31.913998000000007 -106.633202 31.895101999999994 -106.643532 31.844634999999997 -106.615578 31.817728000000002 -106.614441 31.786198 -106.538971 31.784981000000002 -107.282997 31.786804000000004 -108.20266 31.343742000000006 -108.21006 31.343348000000006 -109.045006 32.441967000000005 -109.048882</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>New Mexico</topp:STATE_NAME><topp:STATE_FIPS>35</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>NM</topp:STATE_ABBR><topp:LAND_KM>304472.805</topp:LAND_KM><topp:WATER_KM>586.054</topp:WATER_KM><topp:PERSONS>1379559.0</topp:PERSONS><topp:FAMILIES>358259.0</topp:FAMILIES><topp:HOUSHOLD>497680.0</topp:HOUSHOLD><topp:MALE>677870.0</topp:MALE><topp:FEMALE>701689.0</topp:FEMALE><topp:WORKERS>491847.0</topp:WORKERS><topp:DRVALONE>433150.0</topp:DRVALONE><topp:CARPOOL>87085.0</topp:CARPOOL><topp:PUBTRANS>6019.0</topp:PUBTRANS><topp:EMPLOYED>576213.0</topp:EMPLOYED><topp:UNEMPLOY>49368.0</topp:UNEMPLOY><topp:SERVICE>169914.0</topp:SERVICE><topp:MANUAL>68673.0</topp:MANUAL><topp:P_MALE>0.491</topp:P_MALE><topp:P_FEMALE>0.509</topp:P_FEMALE><topp:SAMP_POP>237629.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.17"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>30.233400000000003 -88.472923</gml:lowerCorner><gml:upperCorner>35.015949000000006 -84.894089</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>31.980545000000006 -85.070137 31.907264999999995 -85.115219 31.854723000000007 -85.135635 31.783651000000006 -85.13163 31.778689999999997 -85.130234 31.731401000000005 -85.115356 31.708405999999997 -85.118744 31.684076000000005 -85.111275 31.621098000000003 -85.059357 31.55422 -85.042793 31.519488999999993 -85.04277 31.475917999999993 -85.066383 31.440490999999994 -85.061478 31.367059999999995 -85.088013 31.332159000000004 -85.082634 31.302903999999998 -85.081299 31.271259 -85.103432 31.226895999999996 -85.093674 31.196742999999998 -85.103027 31.172031000000004 -85.093437 31.162183999999996 -85.068733 31.126531999999997 -85.038139 31.07992 -85.017067 31.001068000000004 -85.001678 31.000820000000004 -85.485077 31.000816 -85.486664 30.993146999999993 -86.031883 30.995045000000005 -86.181534 30.991358000000005 -86.383972 30.997911000000002 -86.701897 30.998011000000005 -86.779404 31.002975000000006 -87.163147 31.002449 -87.598595 30.954178 -87.589882 30.876717 -87.625725 30.848110000000005 -87.615929 30.785506999999996 -87.541916 30.748301999999995 -87.526619 30.705612000000002 -87.460159 30.692618999999993 -87.418915 30.667823999999996 -87.398666 30.619905000000003 -87.393303 30.556298999999996 -87.422089 30.531194999999997 -87.445374 30.481504 -87.418839 30.440163 -87.405273 30.359522 -87.466461 30.319053999999994 -87.587585 30.278214000000006 -87.593422 30.233728 -87.795341 30.233400000000003 -88.002563 30.272436 -87.779953 30.299221000000003 -87.757515 30.421098999999998 -87.903473 30.620993 -87.913391 30.744000999999997 -88.019791 30.336957999999996 -88.135681 30.404094999999998 -88.32032 30.393353000000005 -88.401405 30.736266999999998 -88.417229 31.000511000000003 -88.429184 31.120699000000002 -88.434547 31.435450000000003 -88.450783 31.702084999999997 -88.465073 31.888723 -88.472923 32.22761199999999 -88.437691 32.309082000000004 -88.425758 32.58033399999999 -88.393799 32.924628999999996 -88.348007 32.98737 -88.339432 33.288773000000006 -88.304794 33.538685 -88.274551 33.742615 -88.248161 34.059017 -88.202606 34.090343000000004 -88.19931 34.32404699999999 -88.167572 34.465176 -88.151215 34.58040200000001 -88.13636 34.895542000000006 -88.090431 34.899849 -88.108849 34.93022499999999 -88.143066 35.004368 -88.193954 35.013458 -88.194923 35.015949000000006 -87.986046 35.010464 -87.607788 35.007262999999995 -87.222748 35.00787699999999 -87.207573 34.998160999999996 -86.83342 34.99699 -86.782372 34.995186000000004 -86.313065 34.995377000000005 -86.303513 34.992301999999995 -85.869591 34.990086000000005 -85.608994 34.862244000000004 -85.583092 34.622406 -85.534729 34.584602000000004 -85.525871 34.52293 -85.511894 34.29007 -85.460327 34.086822999999995 -85.416611 33.959728 -85.395782 33.905308000000005 -85.384056 33.654816 -85.335335 33.490482 -85.305031 33.425762000000006 -85.293877 33.129115999999996 -85.233902 33.120018 -85.233559 32.871685 -85.180779 32.807316 -85.162361 32.776939 -85.128151 32.756184000000005 -85.133362 32.744251000000006 -85.124947 32.734257 -85.11393 32.689837999999995 -85.107796 32.676018 -85.090538 32.645775 -85.103867 32.628315 -85.086388 32.602867 -85.084213 32.581115999999994 -85.070839 32.518787 -84.99572 32.454693000000006 -84.989883 32.429306 -84.965454 32.396666999999994 -84.971031 32.386948000000004 -84.984558 32.371357 -84.971733 32.346931 -85.002159 32.32944500000001 -85.005402 32.292987999999994 -84.921143 32.268585 -84.894676 32.259026000000006 -84.894089 32.249404999999996 -84.905434 32.247237999999996 -84.923752 32.228404999999995 -84.916077 32.217827 -84.928444 32.212097 -84.975815 32.191776000000004 -84.960457 32.178726 -85.007896 32.166122 -85.024452 32.126484000000005 -85.053383 32.09074 -85.046257 32.069489000000004 -85.056725 32.049853999999996 -85.06208 32.017219999999995 -85.05677 31.980545000000006 -85.070137</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Alabama</topp:STATE_NAME><topp:STATE_FIPS>01</topp:STATE_FIPS><topp:SUB_REGION>E S Cen</topp:SUB_REGION><topp:STATE_ABBR>AL</topp:STATE_ABBR><topp:LAND_KM>131443.119</topp:LAND_KM><topp:WATER_KM>4332.268</topp:WATER_KM><topp:PERSONS>4040587.0</topp:PERSONS><topp:FAMILIES>1103835.0</topp:FAMILIES><topp:HOUSHOLD>1506790.0</topp:HOUSHOLD><topp:MALE>1936162.0</topp:MALE><topp:FEMALE>2104425.0</topp:FEMALE><topp:WORKERS>1455669.0</topp:WORKERS><topp:DRVALONE>1374395.0</topp:DRVALONE><topp:CARPOOL>267191.0</topp:CARPOOL><topp:PUBTRANS>13279.0</topp:PUBTRANS><topp:EMPLOYED>1741794.0</topp:EMPLOYED><topp:UNEMPLOY>128587.0</topp:UNEMPLOY><topp:SERVICE>474797.0</topp:SERVICE><topp:MANUAL>360499.0</topp:MANUAL><topp:P_MALE>0.479</topp:P_MALE><topp:P_FEMALE>0.521</topp:P_FEMALE><topp:SAMP_POP>630032.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.18"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>30.194738 -91.643562</gml:lowerCorner><gml:upperCorner>35.004954999999995 -88.090431</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>31.435450000000003 -88.450783 31.120699000000002 -88.434547 31.000511000000003 -88.429184 30.736266999999998 -88.417229 30.393353000000005 -88.401405 30.352686000000006 -88.399216 30.325875999999994 -88.464226 30.38055 -88.577744 30.342123 -88.683243 30.430079000000006 -88.873756 30.416602999999995 -88.930191 30.398089999999996 -88.885696 30.314644 -89.276077 30.372190000000003 -89.273483 30.380228000000002 -89.334999 30.365088999999998 -89.357483 30.318657 -89.318497 30.256288999999995 -89.415565 30.200768999999994 -89.438072 30.194738 -89.57383 30.241224000000003 -89.609947 30.247632999999993 -89.606255 30.256769000000006 -89.621605 30.290160999999998 -89.625885 30.295635000000004 -89.639114 30.311649000000003 -89.637024 30.323566 -89.61853 30.343235000000007 -89.62014 30.355114 -89.633392 30.355097 -89.646629 30.378872 -89.65461 30.399880999999993 -89.675308 30.44516 -89.6754 30.462525999999997 -89.683388 30.467995000000002 -89.694534 30.478054 -89.695618 30.481223999999997 -89.71312 30.495846 -89.718994 30.497646000000003 -89.732254 30.515429999999995 -89.755112 30.551056000000003 -89.772728 30.553753 -89.790779 30.624081000000004 -89.820236 30.649266999999995 -89.805466 30.651055999999997 -89.819283 30.670685000000006 -89.829453 30.666062999999994 -89.845375 30.675693999999993 -89.83532 30.679331000000005 -89.841713 30.700361 -89.84127 30.703592 -89.83065 30.712236000000004 -89.843979 30.729174 -89.835541 30.732866 -89.823326 30.742459999999994 -89.825493 30.74798 -89.813278 30.789536999999996 -89.824615 30.811511999999993 -89.81086 30.812466 -89.796494 30.828918 -89.796021 30.830344999999994 -89.774727 30.861393000000007 -89.78495 30.861897999999997 -89.765778 30.900285999999994 -89.763237 30.902608999999998 -89.746727 30.909921999999995 -89.745682 30.920857999999996 -89.75798 30.925923999999995 -89.740402 30.949663 -89.748466 30.965215999999998 -89.739449 30.975308999999996 -89.719749 30.984431999999998 -89.726707 31.001340999999996 -89.723549 31.007261 -89.732643 31.007002 -89.833481 31.007191000000006 -90.259285 31.005211000000003 -90.343567 31.002150999999998 -90.542557 31.001527999999993 -90.560158 31.001368 -90.829613 31.000240000000005 -91.056908 31.000283999999994 -91.1791 31.001186000000004 -91.632179 31.011781999999997 -91.627655 31.032471 -91.57267 31.057982999999993 -91.551964 31.081958999999998 -91.564857 31.127517999999995 -91.619995 31.178307000000004 -91.59137 31.213755000000006 -91.600845 31.240928999999994 -91.636879 31.270943000000003 -91.643562 31.277518999999998 -91.634094 31.270296000000002 -91.556801 31.282898000000003 -91.51696 31.298687 -91.502357 31.323285999999996 -91.505638 31.346815000000007 -91.542671 31.368703999999994 -91.544289 31.382580000000004 -91.534454 31.388059999999996 -91.559258 31.421235999999993 -91.567612 31.43293 -91.552353 31.434166000000005 -91.534683 31.409069000000002 -91.53405 31.391921999999994 -91.522896 31.375079999999997 -91.494591 31.377611 -91.480721 31.403899999999993 -91.465805 31.420205999999993 -91.473175 31.452915000000004 -91.507782 31.52552 -91.506363 31.534713999999994 -91.502838 31.542699999999996 -91.464355 31.562579999999997 -91.423462 31.586186999999995 -91.404137 31.602427000000006 -91.415268 31.604192999999995 -91.497032 31.62178 -91.508759 31.643704999999997 -91.50769 31.650729999999996 -91.49765 31.626801 -91.45726 31.625364000000005 -91.408287 31.654439999999994 -91.389069 31.716517999999994 -91.387833 31.751576999999997 -91.36496 31.753310999999997 -91.370384 31.758379000000005 -91.339798 31.749723000000003 -91.282509 31.759600000000006 -91.263878 31.773764 -91.262573 31.763458 -91.336143 31.770703999999995 -91.36702 31.79567 -91.347122 31.843317 -91.334732 31.862983999999997 -91.30336 31.861332000000004 -91.292534 31.827371999999997 -91.276688 31.818658999999997 -91.250618 31.835226000000006 -91.241272 31.865425000000002 -91.26458 31.878511000000003 -91.24437 31.914288999999997 -91.201431 31.988291000000004 -91.163033 31.991827 -91.108002 32.021057 -91.07238 32.031994 -91.073135 32.043655 -91.086884 32.06610499999999 -91.143013 32.085674 -91.145584 32.088245 -91.125618 32.056843 -91.089302 32.058552000000006 -91.06501 32.093993999999995 -91.073814 32.10767 -91.041084 32.134060000000005 -91.011284 32.169262 -91.00383 32.169899 -91.024414 32.184532000000004 -91.054474 32.158671999999996 -91.045311 32.134392000000005 -91.054047 32.148579 -91.083008 32.134991 -91.108711 32.144783000000004 -91.15654 32.171988999999996 -91.166847 32.205864000000005 -91.157265 32.225605 -91.115952 32.21488600000001 -91.100571 32.232388 -91.087914 32.232742 -91.062462 32.249438999999995 -91.041649 32.223404 -90.977219 32.269065999999995 -90.971016 32.287132 -90.983818 32.297653 -90.980766 32.303698999999995 -90.971657 32.297405 -90.929245 32.305317 -90.916374 32.37965 -90.875351 32.381355 -90.885155 32.345898000000005 -90.914925 32.356396000000004 -90.984566 32.366234000000006 -90.998863 32.397082999999995 -91.009209 32.419017999999994 -90.969986 32.440037000000004 -90.965485 32.452965000000006 -90.98745 32.442417000000006 -91.026909 32.446999000000005 -91.05851 32.498569 -91.117683 32.524067 -91.118744 32.548637 -91.088783 32.496956 -91.037201 32.490814 -91.019684 32.495281000000006 -90.99205 32.511641999999995 -90.995071 32.542159999999996 -91.063087 32.561611 -91.073265 32.576389000000006 -91.058868 32.582672 -91.032326 32.615466999999995 -90.998657 32.625977000000006 -90.996132 32.642673 -91.007881 32.641502 -91.027466 32.614352999999994 -91.04763 32.606387999999995 -91.063812 32.597527 -91.110992 32.644852 -91.146263 32.663174 -91.139557 32.723518 -91.059029 32.749397 -91.09771 32.751125 -91.136467 32.76268 -91.156342 32.841212999999996 -91.142151 32.877162999999996 -91.079529 32.953711999999996 -91.075592 32.987621000000004 -91.092674 32.987350000000006 -91.111816 32.975224 -91.132355 32.937466 -91.129936 32.91778600000001 -91.137177 32.905022 -91.169701 32.914162000000005 -91.198311 32.935852 -91.20916 33.000294 -91.161865 33.013039000000006 -91.162132 33.021709 -91.160675 33.040431999999996 -91.156685 33.04727200000001 -91.123985 33.065571000000006 -91.11779 33.090652000000006 -91.14679 33.113224 -91.190536 33.140465000000006 -91.195503 33.150288 -91.177628 33.131069 -91.12133 33.145084 -91.095856 33.161602 -91.086319 33.225697 -91.092003 33.245780999999994 -91.054543 33.281921 -91.040428 33.293575000000004 -91.053833 33.29235799999999 -91.076324 33.249210000000005 -91.102905 33.268505000000005 -91.122505 33.322384 -91.141747 33.359402 -91.130424 33.393406 -91.106941 33.410179 -91.07869 33.431797 -91.061516 33.460010999999994 -91.060982 33.466206 -91.073761 33.462856 -91.0858 33.414897999999994 -91.099129 33.388878000000005 -91.137634 33.39183 -91.184982 33.414299 -91.204163 33.422112 -91.19883 33.443123 -91.130516 33.452831 -91.119713 33.469673 -91.118958 33.493190999999996 -91.128799 33.51178 -91.164978 33.504368 -91.174179 33.466644 -91.17173 33.447388000000004 -91.18177 33.443443 -91.232849 33.459453999999994 -91.22715 33.473395999999994 -91.207642 33.512012 -91.180405 33.52334999999999 -91.182846 33.538506 -91.204102 33.539276 -91.213486 33.55635100000001 -91.227631 33.590481 -91.22673 33.57468 -91.187805 33.57724399999999 -91.168022 33.616161000000005 -91.15065 33.637032000000005 -91.154404 33.669945 -91.205307 33.690723000000006 -91.215141 33.708965000000006 -91.211678 33.718315000000004 -91.1633 33.677527999999995 -91.121048 33.662586000000005 -91.083771 33.683216 -91.037834 33.70549 -91.038826 33.719357 -91.056213 33.70823300000001 -91.104568 33.71244 -91.128944 33.723225 -91.138 33.771820000000005 -91.142891 33.780086999999995 -91.136986 33.776439999999994 -91.105415 33.786525999999995 -91.066391 33.769554 -91.043404 33.76397299999999 -91.018433 33.77154899999999 -90.995377 33.78533899999999 -90.984039 33.798897 -90.990372 33.816586 -91.028854 33.843525 -91.054817 33.867081 -91.061371 33.936306 -91.018463 33.974644 -91.075706 33.994468999999995 -91.088852 34.006096 -91.069695 33.985699 -91.031059 33.990528 -91.009544 33.968372 -91.000259 33.960815 -90.986816 33.967461 -90.96479 33.978874000000005 -90.961128 33.994606000000005 -90.975273 34.010998 -90.973366 34.031288 -90.95047 34.040710000000004 -90.886345 34.10095200000001 -90.866333 34.102749 -90.906311 34.125941999999995 -90.942245 34.155804 -90.953346 34.185649999999995 -90.928917 34.147544999999994 -90.846611 34.148658999999995 -90.828865 34.166172 -90.80751 34.190544 -90.822922 34.20483 -90.921486 34.23467599999999 -90.933708 34.250195000000005 -90.928436 34.219162 -90.863411 34.229534 -90.83136 34.277339999999995 -90.823837 34.299347 -90.806419 34.299957000000006 -90.792526 34.278976 -90.758255 34.317719 -90.747757 34.363913999999994 -90.761856 34.37216600000001 -90.75531 34.377871999999996 -90.687485 34.3634 -90.681137 34.320145 -90.689377 34.31797400000001 -90.679337 34.330006 -90.657814 34.36591 -90.657242 34.40459799999999 -90.60379 34.432998999999995 -90.579124 34.453945000000004 -90.574402 34.496506 -90.59005 34.520222000000004 -90.580345 34.532509000000005 -90.565681 34.543327000000005 -90.537148 34.555649 -90.530617 34.604744 -90.577614 34.627815 -90.58799 34.645611 -90.5793 34.700287 -90.561058 34.685947 -90.539062 34.65180599999999 -90.547546 34.636894 -90.538963 34.638065 -90.508812 34.672039 -90.466225 34.704254000000006 -90.469978 34.702068 -90.513565 34.713252999999995 -90.533279 34.790336999999994 -90.547745 34.807323 -90.52726 34.805603000000005 -90.516045 34.789833 -90.501282 34.765784999999994 -90.498734 34.748371000000006 -90.516968 34.729855 -90.504417 34.726833 -90.485924 34.741198999999995 -90.451431 34.76075 -90.448868 34.79966400000001 -90.466705 34.82521800000001 -90.451904 34.857727 -90.474716 34.88092399999999 -90.470528 34.88618099999999 -90.438087 34.872643 -90.427841 34.835353999999995 -90.433548 34.832268 -90.42231 34.841038 -90.403931 34.860577000000006 -90.341423 34.850266000000005 -90.322823 34.851776 -90.301552 34.864959999999996 -90.299446 34.88269 -90.296272 34.896511000000004 -90.266708 34.920731 -90.242844 34.938903999999994 -90.241898 34.94976 -90.248169 34.978481 -90.299507 35.000693999999996 -90.305351 34.999168 -89.717186 35.000641 -89.646477 34.999714 -89.3423 35.000793 -89.198067 35.000145 -89.006134 35.00235000000001 -88.812492 35.003094000000004 -88.784988 35.004954999999995 -88.383102 35.00374600000001 -88.351685 35.004368 -88.193954 34.93022499999999 -88.143066 34.899849 -88.108849 34.895542000000006 -88.090431 34.58040200000001 -88.13636 34.465176 -88.151215 34.32404699999999 -88.167572 34.090343000000004 -88.19931 34.059017 -88.202606 33.742615 -88.248161 33.538685 -88.274551 33.288773000000006 -88.304794 32.98737 -88.339432 32.924628999999996 -88.348007 32.58033399999999 -88.393799 32.309082000000004 -88.425758 32.22761199999999 -88.437691 31.888723 -88.472923 31.702084999999997 -88.465073 31.435450000000003 -88.450783</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Mississippi</topp:STATE_NAME><topp:STATE_FIPS>28</topp:STATE_FIPS><topp:SUB_REGION>E S Cen</topp:SUB_REGION><topp:STATE_ABBR>MS</topp:STATE_ABBR><topp:LAND_KM>121506.43</topp:LAND_KM><topp:WATER_KM>3598.337</topp:WATER_KM><topp:PERSONS>2573216.0</topp:PERSONS><topp:FAMILIES>674378.0</topp:FAMILIES><topp:HOUSHOLD>911374.0</topp:HOUSHOLD><topp:MALE>1230617.0</topp:MALE><topp:FEMALE>1342599.0</topp:FEMALE><topp:WORKERS>890741.0</topp:WORKERS><topp:DRVALONE>777422.0</topp:DRVALONE><topp:CARPOOL>184019.0</topp:CARPOOL><topp:PUBTRANS>8020.0</topp:PUBTRANS><topp:EMPLOYED>1028773.0</topp:EMPLOYED><topp:UNEMPLOY>94712.0</topp:UNEMPLOY><topp:SERVICE>294070.0</topp:SERVICE><topp:MANUAL>222644.0</topp:MANUAL><topp:P_MALE>0.478</topp:P_MALE><topp:P_FEMALE>0.522</topp:P_FEMALE><topp:SAMP_POP>399625.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.19"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>30.361057000000002 -85.608994</gml:lowerCorner><gml:upperCorner>35.000277999999994 -80.89492</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>31.778689999999997 -85.130234 31.783651000000006 -85.13163 31.854723000000007 -85.135635 31.907264999999995 -85.115219 31.980545000000006 -85.070137 32.017219999999995 -85.05677 32.049853999999996 -85.06208 32.069489000000004 -85.056725 32.09074 -85.046257 32.126484000000005 -85.053383 32.166122 -85.024452 32.178726 -85.007896 32.191776000000004 -84.960457 32.212097 -84.975815 32.217827 -84.928444 32.228404999999995 -84.916077 32.247237999999996 -84.923752 32.249404999999996 -84.905434 32.259026000000006 -84.894089 32.268585 -84.894676 32.292987999999994 -84.921143 32.32944500000001 -85.005402 32.346931 -85.002159 32.371357 -84.971733 32.386948000000004 -84.984558 32.396666999999994 -84.971031 32.429306 -84.965454 32.454693000000006 -84.989883 32.518787 -84.99572 32.581115999999994 -85.070839 32.602867 -85.084213 32.628315 -85.086388 32.645775 -85.103867 32.676018 -85.090538 32.689837999999995 -85.107796 32.734257 -85.11393 32.744251000000006 -85.124947 32.756184000000005 -85.133362 32.776939 -85.128151 32.807316 -85.162361 32.871685 -85.180779 33.120018 -85.233559 33.129115999999996 -85.233902 33.425762000000006 -85.293877 33.490482 -85.305031 33.654816 -85.335335 33.905308000000005 -85.384056 33.959728 -85.395782 34.086822999999995 -85.416611 34.29007 -85.460327 34.52293 -85.511894 34.584602000000004 -85.525871 34.622406 -85.534729 34.862244000000004 -85.583092 34.990086000000005 -85.608994 34.990047000000004 -85.467422 34.989902 -85.359985 34.989075 -85.267761 34.992554 -84.972847 34.99260700000001 -84.967911 34.992756 -84.800476 34.990680999999995 -84.771194 34.988682 -84.618752 34.98900999999999 -84.323853 34.988224 -84.11824 34.989067000000006 -83.988548 34.989391 -83.937996 34.989536 -83.549416 34.992023 -83.513008 35.000277999999994 -83.106285 34.98407400000001 -83.100578 34.95459700000001 -83.115669 34.960803999999996 -83.121529 34.954285 -83.127441 34.936035000000004 -83.113228 34.940701000000004 -83.125763 34.930386 -83.137688 34.932190000000006 -83.155151 34.917561000000006 -83.158478 34.910557 -83.182304 34.884079 -83.203636 34.88909099999999 -83.219551 34.873729999999995 -83.232925 34.877990999999994 -83.243195 34.862292999999994 -83.235809 34.850075000000004 -83.250427 34.839619 -83.250542 34.839138000000005 -83.267891 34.814987 -83.270233 34.80543900000001 -83.302872 34.787150999999994 -83.322807 34.75214 -83.32354 34.727287000000004 -83.350449 34.709553 -83.3508 34.677597000000006 -83.339905 34.662848999999994 -83.299011 34.613224 -83.237106 34.598839 -83.165001 34.576733000000004 -83.159073 34.567768 -83.137489 34.534915999999996 -83.108246 34.518871000000004 -83.079147 34.489959999999996 -83.055122 34.470726 -83.00808 34.476395 -82.982094 34.477881999999994 -82.90519 34.457438999999994 -82.868179 34.412571 -82.844231 34.370941 -82.836578 34.339794 -82.808723 34.290409 -82.783012 34.28085299999999 -82.764313 34.233265 -82.758171 34.205444 -82.742737 34.169685 -82.735924 34.108242000000004 -82.6605 34.034519 -82.603088 34.013301999999996 -82.596283 33.968784 -82.573753 33.959166999999994 -82.576759 33.930908 -82.517685 33.878101 -82.456779 33.86013 -82.424675 33.854015000000004 -82.390747 33.83588399999999 -82.36586 33.835266000000004 -82.351463 33.803787 -82.311272 33.782520000000005 -82.305664 33.761466999999996 -82.266373 33.690208 -82.235054 33.680603000000005 -82.214363 33.623706999999996 -82.192345 33.615082 -82.16729 33.593773 -82.139137 33.594643000000005 -82.116562 33.57373 -82.065842 33.547371 -82.038704 33.538596999999996 -82.020302 33.520354999999995 -81.996696 33.490601 -81.981079 33.470882 -81.936546 33.451195 -81.916801 33.436004999999994 -81.927437 33.415302 -81.913727 33.408024 -81.940498 33.376419 -81.925972 33.377007000000006 -81.945229 33.350296 -81.936874 33.349354000000005 -81.911575 33.332054 -81.912254 33.335094 -81.893646 33.30669399999999 -81.877037 33.315524999999994 -81.865707 33.308201 -81.840271 33.296947 -81.860878 33.269279 -81.826736 33.273163 -81.840004 33.260468 -81.83696 33.243354999999994 -81.854126 33.22629499999999 -81.810928 33.207779 -81.801727 33.21722 -81.779999 33.213734 -81.770042 33.195232000000004 -81.759758 33.169685 -81.763573 33.144547 -81.74054 33.122787 -81.704445 33.087723 -81.61113 33.070637000000005 -81.595993 33.060660999999996 -81.560204 33.043530000000004 -81.547829 33.043774 -81.52977 33.012637999999995 -81.508919 33.004658000000006 -81.492905 32.959518 -81.49807 32.95526099999999 -81.51001 32.947063 -81.510406 32.897411000000005 -81.477219 32.89756800000001 -81.465202 32.875930999999994 -81.483376 32.871239 -81.45871 32.844414 -81.45549 32.84151799999999 -81.432503 32.831608 -81.424133 32.82014100000001 -81.430481 32.80933 -81.421005 32.785988 -81.430412 32.756561000000005 -81.416245 32.749645 -81.42321 32.741637999999995 -81.407799 32.701397 -81.420708 32.686531 -81.407364 32.650642000000005 -81.399651 32.625434999999996 -81.412277 32.595707000000004 -81.385078 32.588188 -81.36644 32.581801999999996 -81.366875 32.58335099999999 -81.351662 32.57118199999999 -81.340576 32.567108000000005 -81.299156 32.55463399999999 -81.274475 32.533749 -81.268158 32.520432 -81.236412 32.464375000000004 -81.195328 32.420128000000005 -81.200096 32.386639 -81.178421 32.37159 -81.179291 32.338547000000005 -81.157646 32.348274 -81.142052 32.332424 -81.132057 32.276447000000005 -81.124214 32.257515 -81.148338 32.224247000000005 -81.147858 32.190392 -81.114883 32.117408999999995 -81.119278 32.105239999999995 -81.105042 32.005779000000004 -80.89492 31.947711999999996 -80.973724 31.890095000000002 -80.971046 31.864124000000004 -81.140022 31.905677999999995 -81.180168 31.899907999999996 -81.207428 31.919683000000006 -81.197472 31.928220999999994 -81.204079 31.903204000000002 -81.239853 31.949219 -81.284462 31.89448 -81.245117 31.920439000000002 -81.206657 31.892927 -81.220276 31.897458999999998 -81.18042 31.853300000000004 -81.14164 31.823138999999998 -81.039185 31.777321 -81.061218 31.726851999999994 -81.137001 31.799593 -81.174187 31.784626000000003 -81.196541 31.735581999999994 -81.175354 31.799449999999993 -81.289902 31.645847000000003 -81.135117 31.599676000000002 -81.18737 31.639954000000003 -81.240898 31.556662000000003 -81.239326 31.504929000000004 -81.194954 31.466673 -81.208755 31.337370000000007 -81.313248 31.344328000000004 -81.365761 31.311252999999994 -81.410522 31.263861000000006 -81.394951 31.296920999999998 -81.388992 31.275588999999997 -81.301018 31.242464 -81.310432 31.148714 -81.381187 31.130898000000002 -81.528564 31.086316999999994 -81.525436 31.043591000000006 -81.475517 31.076525000000004 -81.535606 30.985579 -81.491035 30.961884999999995 -81.52874 30.944665999999998 -81.485008 30.864543999999995 -81.529449 30.757278 -81.498566 30.721216 -81.528778 30.706340999999995 -81.536774 30.724650999999994 -81.601349 30.716085000000007 -81.60495 30.731538999999998 -81.628159 30.728846000000004 -81.642654 30.745133999999993 -81.716942 30.763655 -81.736847 30.769397999999995 -81.757881 30.761351000000005 -81.776878 30.787851000000003 -81.803368 30.798851 -81.873169 30.828075 -81.899307 30.813242000000002 -81.907684 30.824016999999998 -81.94397 30.820198000000005 -81.953194 30.795773999999994 -81.961716 30.778118000000006 -81.981644 30.791086000000007 -82.015701 30.764022999999995 -82.01313 30.757299000000003 -82.032043 30.650677 -82.045074 30.598455 -82.013817 30.570755000000005 -82.005966 30.477481999999995 -82.022675 30.442642000000006 -82.035286 30.433831999999995 -82.046265 30.378668000000005 -82.038521 30.363557999999998 -82.052933 30.361057000000002 -82.164803 30.368397 -82.180397 30.42362 -82.205727 30.489782000000005 -82.199348 30.502739000000005 -82.219528 30.531214000000006 -82.238556 30.566845 -82.221199 30.577084 -82.412247 30.582468000000006 -82.463287 30.588835000000003 -82.583107 30.595502999999994 -82.696503 30.621129999999994 -83.132576 30.633077999999998 -83.301445 30.634096 -83.31073 30.650582999999997 -83.609177 30.660292 -83.737961 30.675341000000003 -84.000839 30.678381 -84.075668 30.690224 -84.281761 30.693897000000007 -84.374542 30.712473000000003 -84.863083 30.714312000000007 -84.864662 30.743697999999995 -84.888367 30.753899000000004 -84.915421 30.775897999999998 -84.926971 30.802435000000003 -84.93264 30.846733 -84.927002 30.894769999999994 -84.937851 30.926907 -84.969086 30.963477999999995 -84.97364 30.979134000000002 -85.000214 31.001068000000004 -85.001678 31.07992 -85.017067 31.126531999999997 -85.038139 31.162183999999996 -85.068733 31.172031000000004 -85.093437 31.196742999999998 -85.103027 31.226895999999996 -85.093674 31.271259 -85.103432 31.302903999999998 -85.081299 31.332159000000004 -85.082634 31.367059999999995 -85.088013 31.440490999999994 -85.061478 31.475917999999993 -85.066383 31.519488999999993 -85.04277 31.55422 -85.042793 31.621098000000003 -85.059357 31.684076000000005 -85.111275 31.708405999999997 -85.118744 31.731401000000005 -85.115356 31.778689999999997 -85.130234</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>30.903648000000004 -81.485237 30.970366 -81.416588 30.938568000000004 -81.403236 30.716189999999997 -81.455902 30.736647000000005 -81.479645 30.860022999999998 -81.47007 30.881500000000003 -81.503487 30.903648000000004 -81.485237</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Georgia</topp:STATE_NAME><topp:STATE_FIPS>13</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>GA</topp:STATE_ABBR><topp:LAND_KM>148574.888</topp:LAND_KM><topp:WATER_KM>3934.991</topp:WATER_KM><topp:PERSONS>6457339.0</topp:PERSONS><topp:FAMILIES>1707476.0</topp:FAMILIES><topp:HOUSHOLD>2359351.0</topp:HOUSHOLD><topp:MALE>3134562.0</topp:MALE><topp:FEMALE>3322777.0</topp:FEMALE><topp:WORKERS>2346478.0</topp:WORKERS><topp:DRVALONE>2372513.0</topp:DRVALONE><topp:CARPOOL>465734.0</topp:CARPOOL><topp:PUBTRANS>86587.0</topp:PUBTRANS><topp:EMPLOYED>3081555.0</topp:EMPLOYED><topp:UNEMPLOY>187390.0</topp:UNEMPLOY><topp:SERVICE>802419.0</topp:SERVICE><topp:MANUAL>536597.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>935083.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.20"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>32.067963000000006 -83.3508</gml:lowerCorner><gml:upperCorner>35.208241 -78.57972</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>33.195232000000004 -81.759758 33.213734 -81.770042 33.21722 -81.779999 33.207779 -81.801727 33.22629499999999 -81.810928 33.243354999999994 -81.854126 33.260468 -81.83696 33.273163 -81.840004 33.269279 -81.826736 33.296947 -81.860878 33.308201 -81.840271 33.315524999999994 -81.865707 33.30669399999999 -81.877037 33.335094 -81.893646 33.332054 -81.912254 33.349354000000005 -81.911575 33.350296 -81.936874 33.377007000000006 -81.945229 33.376419 -81.925972 33.408024 -81.940498 33.415302 -81.913727 33.436004999999994 -81.927437 33.451195 -81.916801 33.470882 -81.936546 33.490601 -81.981079 33.520354999999995 -81.996696 33.538596999999996 -82.020302 33.547371 -82.038704 33.57373 -82.065842 33.594643000000005 -82.116562 33.593773 -82.139137 33.615082 -82.16729 33.623706999999996 -82.192345 33.680603000000005 -82.214363 33.690208 -82.235054 33.761466999999996 -82.266373 33.782520000000005 -82.305664 33.803787 -82.311272 33.835266000000004 -82.351463 33.83588399999999 -82.36586 33.854015000000004 -82.390747 33.86013 -82.424675 33.878101 -82.456779 33.930908 -82.517685 33.959166999999994 -82.576759 33.968784 -82.573753 34.013301999999996 -82.596283 34.034519 -82.603088 34.108242000000004 -82.6605 34.169685 -82.735924 34.205444 -82.742737 34.233265 -82.758171 34.28085299999999 -82.764313 34.290409 -82.783012 34.339794 -82.808723 34.370941 -82.836578 34.412571 -82.844231 34.457438999999994 -82.868179 34.477881999999994 -82.90519 34.476395 -82.982094 34.470726 -83.00808 34.489959999999996 -83.055122 34.518871000000004 -83.079147 34.534915999999996 -83.108246 34.567768 -83.137489 34.576733000000004 -83.159073 34.598839 -83.165001 34.613224 -83.237106 34.662848999999994 -83.299011 34.677597000000006 -83.339905 34.709553 -83.3508 34.727287000000004 -83.350449 34.75214 -83.32354 34.787150999999994 -83.322807 34.80543900000001 -83.302872 34.814987 -83.270233 34.839138000000005 -83.267891 34.839619 -83.250542 34.850075000000004 -83.250427 34.862292999999994 -83.235809 34.877990999999994 -83.243195 34.873729999999995 -83.232925 34.88909099999999 -83.219551 34.884079 -83.203636 34.910557 -83.182304 34.917561000000006 -83.158478 34.932190000000006 -83.155151 34.930386 -83.137688 34.940701000000004 -83.125763 34.936035000000004 -83.113228 34.954285 -83.127441 34.960803999999996 -83.121529 34.95459700000001 -83.115669 34.98407400000001 -83.100578 35.000277999999994 -83.106285 35.024204 -83.007278 35.055369999999996 -82.887695 35.085423000000006 -82.771347 35.091232000000005 -82.697357 35.097801000000004 -82.688034 35.121464 -82.68605 35.119457 -82.654495 35.14949 -82.57003 35.154560000000004 -82.524635 35.173500000000004 -82.466743 35.16955900000001 -82.43792 35.208241 -82.38961 35.18272399999999 -82.371376 35.182948999999994 -82.360123 35.192673 -82.350861 35.184189 -82.32077 35.195007000000004 -82.278328 35.193127000000004 -82.210175 35.188282 -81.971443 35.183116999999996 -81.87059 35.182472000000004 -81.765358 35.162853 -81.362175 35.163757000000004 -81.322823 35.151531000000006 -81.049301 35.132011000000006 -81.049042 35.105408 -81.028442 35.066483000000005 -81.065552 35.037200999999996 -81.039886 35.10124999999999 -80.927795 35.059734000000006 -80.894714 35.00201800000001 -80.840164 34.940639000000004 -80.785652 34.816113 -80.800064 34.815228000000005 -80.561546 34.814762 -80.325279 34.807919 -79.919952 34.805256 -79.685959 34.800663 -79.667503 34.63409 -79.455971 34.619061 -79.447144 34.304573000000005 -79.074501 33.948673 -78.655716 33.881992 -78.57972 33.865536000000006 -78.622856 33.853359 -78.586197 33.71619 -78.855133 33.57246000000001 -79.000938 33.430611 -79.121101 33.317088999999996 -79.150383 33.342265999999995 -79.158348 33.296870999999996 -79.271072 33.183521 -79.202141 33.141338000000005 -79.22979 33.154827 -79.348976 33.138794000000004 -79.298058 33.10453 -79.288101 33.013699 -79.410835 33.015842000000006 -79.582565 32.980796999999995 -79.617439 32.92493399999999 -79.587852 32.899071000000006 -79.607018 32.79406 -79.752518 32.790531 -79.907677 32.929783 -79.800056 32.859215000000006 -79.907333 32.914047 -79.930443 32.904236 -79.962311 32.81062300000001 -79.948174 32.733852 -79.891846 32.67724200000001 -79.896584 32.605605999999995 -79.996803 32.561417000000006 -80.210983 32.503883 -80.290833 32.511768000000004 -80.347351 32.65522 -80.391045 32.668980000000005 -80.416168 32.504772 -80.399979 32.510245999999995 -80.480881 32.557655 -80.550568 32.509677999999994 -80.539055 32.518692 -80.646584 32.498272 -80.64373 32.43084 -80.486244 32.37331399999999 -80.44297 32.318489 -80.460564 32.272621 -80.626198 32.285465 -80.678185 32.360016 -80.742134 32.473118 -80.799553 32.504864 -80.784958 32.516093999999995 -80.830688 32.532509000000005 -80.867348 32.400516999999994 -80.822037 32.247921000000005 -80.780731 32.067963000000006 -80.893082 32.105239999999995 -81.105042 32.117408999999995 -81.119278 32.190392 -81.114883 32.224247000000005 -81.147858 32.257515 -81.148338 32.276447000000005 -81.124214 32.332424 -81.132057 32.348274 -81.142052 32.338547000000005 -81.157646 32.37159 -81.179291 32.386639 -81.178421 32.420128000000005 -81.200096 32.464375000000004 -81.195328 32.520432 -81.236412 32.533749 -81.268158 32.55463399999999 -81.274475 32.567108000000005 -81.299156 32.57118199999999 -81.340576 32.58335099999999 -81.351662 32.581801999999996 -81.366875 32.588188 -81.36644 32.595707000000004 -81.385078 32.625434999999996 -81.412277 32.650642000000005 -81.399651 32.686531 -81.407364 32.701397 -81.420708 32.741637999999995 -81.407799 32.749645 -81.42321 32.756561000000005 -81.416245 32.785988 -81.430412 32.80933 -81.421005 32.82014100000001 -81.430481 32.831608 -81.424133 32.84151799999999 -81.432503 32.844414 -81.45549 32.871239 -81.45871 32.875930999999994 -81.483376 32.89756800000001 -81.465202 32.897411000000005 -81.477219 32.947063 -81.510406 32.95526099999999 -81.51001 32.959518 -81.49807 33.004658000000006 -81.492905 33.012637999999995 -81.508919 33.043774 -81.52977 33.043530000000004 -81.547829 33.060660999999996 -81.560204 33.070637000000005 -81.595993 33.087723 -81.61113 33.122787 -81.704445 33.144547 -81.74054 33.169685 -81.763573 33.195232000000004 -81.759758</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>32.258446000000006 -80.767975 32.272003 -80.719254 32.21991 -80.666786 32.104488 -80.819435 32.258446000000006 -80.767975</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>South Carolina</topp:STATE_NAME><topp:STATE_FIPS>45</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>SC</topp:STATE_ABBR><topp:LAND_KM>77987.823</topp:LAND_KM><topp:WATER_KM>4910.636</topp:WATER_KM><topp:PERSONS>3486703.0</topp:PERSONS><topp:FAMILIES>928206.0</topp:FAMILIES><topp:HOUSHOLD>1258044.0</topp:HOUSHOLD><topp:MALE>1688510.0</topp:MALE><topp:FEMALE>1798193.0</topp:FEMALE><topp:WORKERS>1268959.0</topp:WORKERS><topp:DRVALONE>1234700.0</topp:DRVALONE><topp:CARPOOL>277209.0</topp:CARPOOL><topp:PUBTRANS>18092.0</topp:PUBTRANS><topp:EMPLOYED>1603425.0</topp:EMPLOYED><topp:UNEMPLOY>94673.0</topp:UNEMPLOY><topp:SERVICE>455692.0</topp:SERVICE><topp:MANUAL>337824.0</topp:MANUAL><topp:P_MALE>0.484</topp:P_MALE><topp:P_FEMALE>0.516</topp:P_FEMALE><topp:SAMP_POP>506464.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.21"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>33.010025 -94.617035</gml:lowerCorner><gml:upperCorner>36.492751999999996 -89.645401</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>34.19665500000001 -94.461479 34.508326999999994 -94.452408 34.735504000000006 -94.44574 34.929050000000004 -94.439102 35.400454999999994 -94.428337 35.641003 -94.468269 35.760227 -94.485718 36.106753999999995 -94.542198 36.164444 -94.552895 36.478714 -94.607231 36.489338000000004 -94.617035 36.49095199999999 -94.080849 36.489716 -93.857323 36.489891 -93.59626 36.490196 -93.328163 36.490616 -93.297142 36.489819 -92.852104 36.489918 -92.777466 36.490855999999994 -92.522888 36.491596 -92.146164 36.491371 -92.127487 36.490955 -91.688416 36.490376 -91.45285 36.491039 -91.411659 36.487953000000005 -91.133827 36.489204 -90.804314 36.490962999999994 -90.581619 36.492751999999996 -90.224373 36.491814000000005 -90.150162 36.45741700000001 -90.137276 36.453896 -90.117226 36.422565000000006 -90.123833 36.404915 -90.116829 36.39738800000001 -90.080177 36.382553 -90.052063 36.362606 -90.050201 36.325333 -90.067635 36.300472 -90.049751 36.272273999999996 -90.066093 36.257996000000006 -90.109917 36.21207 -90.131218 36.196940999999995 -90.161308 36.172565000000006 -90.219223 36.161148 -90.232224 36.137089 -90.23484 36.118763 -90.263702 36.115905999999995 -90.284752 36.091656 -90.315239 35.989586 -90.37896 35.991158 -90.283455 35.996838 -89.963203 35.999877999999995 -89.721756 35.966324 -89.713135 35.93782 -89.664192 35.913799 -89.645401 35.894287000000006 -89.649338 35.885647000000006 -89.66465 35.911427 -89.714684 35.915012000000004 -89.737976 35.896812 -89.762909 35.884102 -89.766273 35.871418000000006 -89.757713 35.842037000000005 -89.701439 35.827515000000005 -89.700829 35.807036999999994 -89.735939 35.817420999999996 -89.759796 35.805553 -89.790382 35.774223000000006 -89.799904 35.758269999999996 -89.827042 35.748192 -89.859871 35.754836999999995 -89.909782 35.734268 -89.951035 35.712486 -89.952034 35.676266 -89.929741 35.655972000000006 -89.893402 35.673306 -89.865181 35.671062000000006 -89.857246 35.645222000000004 -89.849197 35.629745 -89.863838 35.633335 -89.877441 35.603104 -89.957047 35.578593999999995 -89.958031 35.546059 -89.921661 35.52923199999999 -89.931175 35.526900999999995 -89.947548 35.532291 -89.962273 35.561676000000006 -89.989586 35.552414 -90.033051 35.542846999999995 -90.040901 35.51244 -90.041817 35.445454 -89.999565 35.417103 -90.046783 35.41341 -90.060295 35.426506 -90.073936 35.472342999999995 -90.074844 35.478207 -90.08223 35.473568 -90.101959 35.442524000000006 -90.137276 35.423716999999996 -90.172676 35.384254 -90.167816 35.383044999999996 -90.140167 35.4076 -90.132469 35.41768999999999 -90.112244 35.418282000000005 -90.085159 35.406527999999994 -90.075478 35.381508 -90.087135 35.365982 -90.105621 35.345591999999996 -90.098701 35.314685999999995 -90.106346 35.30624400000001 -90.15699 35.282566 -90.169746 35.264056999999994 -90.152122 35.263847 -90.105942 35.254397999999995 -90.090103 35.212738 -90.068962 35.191833 -90.073303 35.166916 -90.062431 35.147385 -90.064537 35.12505 -90.082924 35.13653600000001 -90.14373 35.129611999999995 -90.164474 35.10864599999999 -90.178345 35.077827 -90.169083 35.040897 -90.195709 35.048458 -90.291809 35.000693999999996 -90.305351 34.978481 -90.299507 34.94976 -90.248169 34.938903999999994 -90.241898 34.920731 -90.242844 34.896511000000004 -90.266708 34.88269 -90.296272 34.864959999999996 -90.299446 34.851776 -90.301552 34.850266000000005 -90.322823 34.860577000000006 -90.341423 34.841038 -90.403931 34.832268 -90.42231 34.835353999999995 -90.433548 34.872643 -90.427841 34.88618099999999 -90.438087 34.88092399999999 -90.470528 34.857727 -90.474716 34.82521800000001 -90.451904 34.79966400000001 -90.466705 34.76075 -90.448868 34.741198999999995 -90.451431 34.726833 -90.485924 34.729855 -90.504417 34.748371000000006 -90.516968 34.765784999999994 -90.498734 34.789833 -90.501282 34.805603000000005 -90.516045 34.807323 -90.52726 34.790336999999994 -90.547745 34.713252999999995 -90.533279 34.702068 -90.513565 34.704254000000006 -90.469978 34.672039 -90.466225 34.638065 -90.508812 34.636894 -90.538963 34.65180599999999 -90.547546 34.685947 -90.539062 34.700287 -90.561058 34.645611 -90.5793 34.627815 -90.58799 34.604744 -90.577614 34.555649 -90.530617 34.543327000000005 -90.537148 34.532509000000005 -90.565681 34.520222000000004 -90.580345 34.496506 -90.59005 34.453945000000004 -90.574402 34.432998999999995 -90.579124 34.40459799999999 -90.60379 34.36591 -90.657242 34.330006 -90.657814 34.31797400000001 -90.679337 34.320145 -90.689377 34.3634 -90.681137 34.377871999999996 -90.687485 34.37216600000001 -90.75531 34.363913999999994 -90.761856 34.317719 -90.747757 34.278976 -90.758255 34.299957000000006 -90.792526 34.299347 -90.806419 34.277339999999995 -90.823837 34.229534 -90.83136 34.219162 -90.863411 34.250195000000005 -90.928436 34.23467599999999 -90.933708 34.20483 -90.921486 34.190544 -90.822922 34.166172 -90.80751 34.148658999999995 -90.828865 34.147544999999994 -90.846611 34.185649999999995 -90.928917 34.155804 -90.953346 34.125941999999995 -90.942245 34.102749 -90.906311 34.10095200000001 -90.866333 34.040710000000004 -90.886345 34.031288 -90.95047 34.010998 -90.973366 33.994606000000005 -90.975273 33.978874000000005 -90.961128 33.967461 -90.96479 33.960815 -90.986816 33.968372 -91.000259 33.990528 -91.009544 33.985699 -91.031059 34.006096 -91.069695 33.994468999999995 -91.088852 33.974644 -91.075706 33.936306 -91.018463 33.867081 -91.061371 33.843525 -91.054817 33.816586 -91.028854 33.798897 -90.990372 33.78533899999999 -90.984039 33.77154899999999 -90.995377 33.76397299999999 -91.018433 33.769554 -91.043404 33.786525999999995 -91.066391 33.776439999999994 -91.105415 33.780086999999995 -91.136986 33.771820000000005 -91.142891 33.723225 -91.138 33.71244 -91.128944 33.70823300000001 -91.104568 33.719357 -91.056213 33.70549 -91.038826 33.683216 -91.037834 33.662586000000005 -91.083771 33.677527999999995 -91.121048 33.718315000000004 -91.1633 33.708965000000006 -91.211678 33.690723000000006 -91.215141 33.669945 -91.205307 33.637032000000005 -91.154404 33.616161000000005 -91.15065 33.57724399999999 -91.168022 33.57468 -91.187805 33.590481 -91.22673 33.55635100000001 -91.227631 33.539276 -91.213486 33.538506 -91.204102 33.52334999999999 -91.182846 33.512012 -91.180405 33.473395999999994 -91.207642 33.459453999999994 -91.22715 33.443443 -91.232849 33.447388000000004 -91.18177 33.466644 -91.17173 33.504368 -91.174179 33.51178 -91.164978 33.493190999999996 -91.128799 33.469673 -91.118958 33.452831 -91.119713 33.443123 -91.130516 33.422112 -91.19883 33.414299 -91.204163 33.39183 -91.184982 33.388878000000005 -91.137634 33.414897999999994 -91.099129 33.462856 -91.0858 33.466206 -91.073761 33.460010999999994 -91.060982 33.431797 -91.061516 33.410179 -91.07869 33.393406 -91.106941 33.359402 -91.130424 33.322384 -91.141747 33.268505000000005 -91.122505 33.249210000000005 -91.102905 33.29235799999999 -91.076324 33.293575000000004 -91.053833 33.281921 -91.040428 33.245780999999994 -91.054543 33.225697 -91.092003 33.161602 -91.086319 33.145084 -91.095856 33.131069 -91.12133 33.150288 -91.177628 33.140465000000006 -91.195503 33.113224 -91.190536 33.090652000000006 -91.14679 33.065571000000006 -91.11779 33.04727200000001 -91.123985 33.040431999999996 -91.156685 33.021709 -91.160675 33.013039000000006 -91.162132 33.013476999999995 -91.254616 33.013419999999996 -91.427528 33.013874 -91.454353 33.010025 -92.063309 33.016701 -92.717079 33.018135 -92.978828 33.019238 -93.232376 33.021393 -93.478897 33.021152 -93.511742 33.022594 -93.809753 33.023289000000005 -94.03875 33.270325 -94.036507 33.555912000000006 -94.035927 33.577213 -94.061432 33.583954000000006 -94.086655 33.572998 -94.098701 33.567085000000006 -94.155167 33.593773 -94.159515 33.58507899999999 -94.205345 33.557987 -94.210884 33.561535000000006 -94.235367 33.585719999999995 -94.223038 33.592422 -94.237236 33.561736999999994 -94.274544 33.584605999999994 -94.272079 33.589332999999996 -94.278984 33.579853 -94.29882 33.556934 -94.302383 33.57313499999999 -94.328751 33.547684000000004 -94.370758 33.560303000000005 -94.395264 33.572661999999994 -94.372307 33.590042 -94.370628 33.593327 -94.379112 33.57495900000001 -94.393417 33.573486 -94.40657 33.59714099999999 -94.428467 33.596503999999996 -94.443329 33.604347000000004 -94.451553 33.616844 -94.436333 33.636444 -94.435913 33.631966000000006 -94.476486 33.939198000000005 -94.468376 34.19665500000001 -94.461479</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Arkansas</topp:STATE_NAME><topp:STATE_FIPS>05</topp:STATE_FIPS><topp:SUB_REGION>W S Cen</topp:SUB_REGION><topp:STATE_ABBR>AR</topp:STATE_ABBR><topp:LAND_KM>134875.075</topp:LAND_KM><topp:WATER_KM>2867.302</topp:WATER_KM><topp:PERSONS>2350725.0</topp:PERSONS><topp:FAMILIES>651555.0</topp:FAMILIES><topp:HOUSHOLD>891179.0</topp:HOUSHOLD><topp:MALE>1133076.0</topp:MALE><topp:FEMALE>1217649.0</topp:FEMALE><topp:WORKERS>849722.0</topp:WORKERS><topp:DRVALONE>764635.0</topp:DRVALONE><topp:CARPOOL>153025.0</topp:CARPOOL><topp:PUBTRANS>5096.0</topp:PUBTRANS><topp:EMPLOYED>994289.0</topp:EMPLOYED><topp:UNEMPLOY>72079.0</topp:UNEMPLOY><topp:SERVICE>298794.0</topp:SERVICE><topp:MANUAL>210650.0</topp:MANUAL><topp:P_MALE>0.482</topp:P_MALE><topp:P_FEMALE>0.518</topp:P_FEMALE><topp:SAMP_POP>414226.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.22"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>28.939419 -94.041603</gml:lowerCorner><gml:upperCorner>33.023289000000005 -89.021759</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>30.239372000000003 -93.707359 30.297387999999998 -93.699211 30.304916000000006 -93.729774 30.340872000000005 -93.759178 30.354145000000003 -93.759338 30.367411000000004 -93.747833 30.381788 -93.754944 30.408822999999998 -93.742561 30.43298 -93.721535 30.442633 -93.696571 30.462513 -93.703423 30.470046999999994 -93.697975 30.488628000000006 -93.714851 30.496240999999998 -93.707275 30.505114000000006 -93.714638 30.522858 -93.70546 30.545517000000004 -93.735306 30.568152999999995 -93.71788 30.587379 -93.717812 30.598834999999994 -93.69342 30.597831999999997 -93.671585 30.615795000000006 -93.692696 30.623424999999997 -93.684586 30.640040999999997 -93.692879 30.639691999999997 -93.677971 30.672859000000003 -93.659988 30.686802 -93.617607 30.710329 -93.612411 30.732547999999994 -93.61779 30.732010000000002 -93.607651 30.745789000000002 -93.618454 30.772183999999996 -93.585175 30.802040000000005 -93.581871 30.828344 -93.550682 30.842342000000002 -93.555641 30.845147999999995 -93.566444 30.860282999999995 -93.552803 30.871880000000004 -93.560844 30.886234 -93.568497 30.901932000000002 -93.564476 30.905333999999996 -93.546516 30.924885000000003 -93.549622 30.926970999999995 -93.529984 30.935820000000007 -93.52562 30.960731999999993 -93.532188 30.956884000000002 -93.537338 30.970190000000002 -93.548676 30.976177000000007 -93.572456 30.991690000000006 -93.560951 30.997270999999998 -93.570847 31.012923999999998 -93.567894 31.018062999999998 -93.564941 31.014140999999995 -93.547119 31.038908000000006 -93.507217 31.056979999999996 -93.525742 31.074670999999995 -93.516998 31.082373000000004 -93.544106 31.094751000000002 -93.543121 31.100536000000005 -93.559982 31.109342999999996 -93.556679 31.116072000000003 -93.535088 31.125924999999995 -93.528091 31.132441 -93.537506 31.159166 -93.544189 31.162943 -93.528328 31.176339999999996 -93.537018 31.178076000000004 -93.526932 31.185773999999995 -93.528923 31.190928999999997 -93.550591 31.17214 -93.576942 31.180199 -93.593941 31.199066000000002 -93.602921 31.229687 -93.590546 31.242188 -93.611 31.270032999999998 -93.611877 31.275805000000005 -93.616455 31.273903000000004 -93.630829 31.290262 -93.645592 31.286671 -93.656128 31.312679000000003 -93.681587 31.328385999999995 -93.67704 31.373827000000006 -93.634857 31.372394999999997 -93.661072 31.398329000000004 -93.664017 31.406130000000005 -93.687492 31.415921999999995 -93.694443 31.427735999999996 -93.696129 31.438131 -93.687004 31.446251000000004 -93.701927 31.461457999999993 -93.698418 31.459474999999998 -93.726784 31.485500000000002 -93.751244 31.490556999999995 -93.750435 31.495402999999996 -93.718994 31.520568999999995 -93.705795 31.521877000000003 -93.731659 31.537717999999998 -93.747543 31.530724000000006 -93.763306 31.533734999999993 -93.780128 31.559062999999995 -93.810516 31.577110000000005 -93.816322 31.590182999999996 -93.832619 31.615189 -93.835579 31.618092000000004 -93.819588 31.647965999999997 -93.814728 31.653767000000002 -93.806427 31.674566 -93.811844 31.711394999999996 -93.792267 31.707565000000002 -93.808769 31.712350999999998 -93.814949 31.730351999999996 -93.80999 31.753281 -93.831161 31.774637 -93.822067 31.802017000000006 -93.834328 31.817272000000003 -93.864822 31.850112999999993 -93.877403 31.87142 -93.881264 31.870065999999994 -93.892525 31.894454999999994 -93.899261 31.892593000000005 -93.923462 31.909701999999996 -93.917923 31.909456000000006 -93.93573 31.923164 -93.969986 31.946158999999994 -93.977211 31.977942 -94.004395 31.989134000000007 -94.009888 31.994512999999998 -94.035065 32.199448000000004 -94.034767 32.389224999999996 -94.035233 32.694812999999996 -94.040199 32.882346999999996 -94.041603 33.023289000000005 -94.03875 33.022594 -93.809753 33.021152 -93.511742 33.021393 -93.478897 33.019238 -93.232376 33.018135 -92.978828 33.016701 -92.717079 33.010025 -92.063309 33.013874 -91.454353 33.013419999999996 -91.427528 33.013476999999995 -91.254616 33.013039000000006 -91.162132 33.000294 -91.161865 32.935852 -91.20916 32.914162000000005 -91.198311 32.905022 -91.169701 32.91778600000001 -91.137177 32.937466 -91.129936 32.975224 -91.132355 32.987350000000006 -91.111816 32.987621000000004 -91.092674 32.953711999999996 -91.075592 32.877162999999996 -91.079529 32.841212999999996 -91.142151 32.76268 -91.156342 32.751125 -91.136467 32.749397 -91.09771 32.723518 -91.059029 32.663174 -91.139557 32.644852 -91.146263 32.597527 -91.110992 32.606387999999995 -91.063812 32.614352999999994 -91.04763 32.641502 -91.027466 32.642673 -91.007881 32.625977000000006 -90.996132 32.615466999999995 -90.998657 32.582672 -91.032326 32.576389000000006 -91.058868 32.561611 -91.073265 32.542159999999996 -91.063087 32.511641999999995 -90.995071 32.495281000000006 -90.99205 32.490814 -91.019684 32.496956 -91.037201 32.548637 -91.088783 32.524067 -91.118744 32.498569 -91.117683 32.446999000000005 -91.05851 32.442417000000006 -91.026909 32.452965000000006 -90.98745 32.440037000000004 -90.965485 32.419017999999994 -90.969986 32.397082999999995 -91.009209 32.366234000000006 -90.998863 32.356396000000004 -90.984566 32.345898000000005 -90.914925 32.381355 -90.885155 32.37965 -90.875351 32.305317 -90.916374 32.297405 -90.929245 32.303698999999995 -90.971657 32.297653 -90.980766 32.287132 -90.983818 32.269065999999995 -90.971016 32.223404 -90.977219 32.249438999999995 -91.041649 32.232742 -91.062462 32.232388 -91.087914 32.21488600000001 -91.100571 32.225605 -91.115952 32.205864000000005 -91.157265 32.171988999999996 -91.166847 32.144783000000004 -91.15654 32.134991 -91.108711 32.148579 -91.083008 32.134392000000005 -91.054047 32.158671999999996 -91.045311 32.184532000000004 -91.054474 32.169899 -91.024414 32.169262 -91.00383 32.134060000000005 -91.011284 32.10767 -91.041084 32.093993999999995 -91.073814 32.058552000000006 -91.06501 32.056843 -91.089302 32.088245 -91.125618 32.085674 -91.145584 32.06610499999999 -91.143013 32.043655 -91.086884 32.031994 -91.073135 32.021057 -91.07238 31.991827 -91.108002 31.988291000000004 -91.163033 31.914288999999997 -91.201431 31.878511000000003 -91.24437 31.865425000000002 -91.26458 31.835226000000006 -91.241272 31.818658999999997 -91.250618 31.827371999999997 -91.276688 31.861332000000004 -91.292534 31.862983999999997 -91.30336 31.843317 -91.334732 31.79567 -91.347122 31.770703999999995 -91.36702 31.763458 -91.336143 31.773764 -91.262573 31.759600000000006 -91.263878 31.749723000000003 -91.282509 31.758379000000005 -91.339798 31.753310999999997 -91.370384 31.751576999999997 -91.36496 31.716517999999994 -91.387833 31.654439999999994 -91.389069 31.625364000000005 -91.408287 31.626801 -91.45726 31.650729999999996 -91.49765 31.643704999999997 -91.50769 31.62178 -91.508759 31.604192999999995 -91.497032 31.602427000000006 -91.415268 31.586186999999995 -91.404137 31.562579999999997 -91.423462 31.542699999999996 -91.464355 31.534713999999994 -91.502838 31.52552 -91.506363 31.452915000000004 -91.507782 31.420205999999993 -91.473175 31.403899999999993 -91.465805 31.377611 -91.480721 31.375079999999997 -91.494591 31.391921999999994 -91.522896 31.409069000000002 -91.53405 31.434166000000005 -91.534683 31.43293 -91.552353 31.421235999999993 -91.567612 31.388059999999996 -91.559258 31.382580000000004 -91.534454 31.368703999999994 -91.544289 31.346815000000007 -91.542671 31.323285999999996 -91.505638 31.298687 -91.502357 31.282898000000003 -91.51696 31.270296000000002 -91.556801 31.277518999999998 -91.634094 31.270943000000003 -91.643562 31.240928999999994 -91.636879 31.213755000000006 -91.600845 31.178307000000004 -91.59137 31.127517999999995 -91.619995 31.081958999999998 -91.564857 31.057982999999993 -91.551964 31.032471 -91.57267 31.011781999999997 -91.627655 31.001186000000004 -91.632179 31.000283999999994 -91.1791 31.000240000000005 -91.056908 31.001368 -90.829613 31.001527999999993 -90.560158 31.002150999999998 -90.542557 31.005211000000003 -90.343567 31.007191000000006 -90.259285 31.007002 -89.833481 31.007261 -89.732643 31.001340999999996 -89.723549 30.984431999999998 -89.726707 30.975308999999996 -89.719749 30.965215999999998 -89.739449 30.949663 -89.748466 30.925923999999995 -89.740402 30.920857999999996 -89.75798 30.909921999999995 -89.745682 30.902608999999998 -89.746727 30.900285999999994 -89.763237 30.861897999999997 -89.765778 30.861393000000007 -89.78495 30.830344999999994 -89.774727 30.828918 -89.796021 30.812466 -89.796494 30.811511999999993 -89.81086 30.789536999999996 -89.824615 30.74798 -89.813278 30.742459999999994 -89.825493 30.732866 -89.823326 30.729174 -89.835541 30.712236000000004 -89.843979 30.703592 -89.83065 30.700361 -89.84127 30.679331000000005 -89.841713 30.675693999999993 -89.83532 30.666062999999994 -89.845375 30.670685000000006 -89.829453 30.651055999999997 -89.819283 30.649266999999995 -89.805466 30.624081000000004 -89.820236 30.553753 -89.790779 30.551056000000003 -89.772728 30.515429999999995 -89.755112 30.497646000000003 -89.732254 30.495846 -89.718994 30.481223999999997 -89.71312 30.478054 -89.695618 30.467995000000002 -89.694534 30.462525999999997 -89.683388 30.44516 -89.6754 30.399880999999993 -89.675308 30.378872 -89.65461 30.355097 -89.646629 30.355114 -89.633392 30.343235000000007 -89.62014 30.323566 -89.61853 30.311649000000003 -89.637024 30.295635000000004 -89.639114 30.290160999999998 -89.625885 30.256769000000006 -89.621605 30.247632999999993 -89.606255 30.241224000000003 -89.609947 30.194738 -89.57383 30.180817000000005 -89.7285 30.230897999999996 -89.759155 30.269656999999995 -89.943466 30.368784000000005 -90.075485 30.380756000000005 -90.23967 30.303650000000005 -90.308945 30.185677999999996 -90.424446 30.091879000000006 -90.395485 30.061852000000002 -90.275902 30.041409 -90.111473 30.053462999999994 -89.990479 30.155893000000006 -89.891159 30.105172999999994 -89.798203 30.158731000000003 -89.740028 30.120897 -89.724869 30.163185 -89.669144 30.122229000000004 -89.649338 30.055026999999995 -89.716782 30.010485000000003 -89.8489 29.951088 -89.820786 29.969201999999996 -89.714958 29.897323999999998 -89.712143 29.875474999999994 -89.627357 29.897953 -89.585808 30.008758999999998 -89.574326 30.044205000000005 -89.435806 29.985530999999995 -89.453575 29.951083999999994 -89.377747 29.940074999999993 -89.431 29.845741000000004 -89.402153 29.827894 -89.421104 29.796570000000003 -89.363991 29.782731999999996 -89.417374 29.830743999999996 -89.482185 29.754524000000004 -89.540443 29.766693000000004 -89.650528 29.710655000000003 -89.592819 29.697537999999994 -89.611778 29.664406 -89.511887 29.635962000000006 -89.479134 29.664947999999995 -89.597893 29.702751000000006 -89.676643 29.693869000000007 -89.695267 29.626320000000007 -89.635201 29.645964000000006 -89.728256 29.637164999999996 -89.748482 29.605813999999995 -89.723145 29.610033 -89.771713 29.471469999999997 -89.544571 29.401235999999997 -89.537018 29.397722 -89.384468 29.340675000000005 -89.337006 29.350447000000003 -89.264725 29.29759 -89.262505 29.348831000000004 -89.193352 29.290667999999997 -89.12989 29.211662000000004 -89.120178 29.223191999999997 -89.033218 29.163218999999998 -89.098228 29.146894000000003 -89.021759 29.085052000000005 -89.057709 29.135084000000006 -89.126495 29.082790000000003 -89.111359 29.05699 -89.153976 29.016448999999994 -89.144348 29.120942999999997 -89.241112 29.059235 -89.257446 28.939419 -89.394844 29.147987 -89.263237 29.179964 -89.319038 29.104266999999993 -89.339684 29.100149000000002 -89.388611 29.146038000000004 -89.393082 29.216097000000005 -89.467018 29.255514000000005 -89.459061 29.234772000000007 -89.493141 29.279352000000003 -89.61953 29.331536999999997 -89.610657 29.322316999999998 -89.794876 29.374066999999997 -89.753639 29.420692000000003 -89.821678 29.477407 -89.817459 29.472459999999998 -89.966667 29.503325000000004 -89.971855 29.493719 -90.006622 29.553633000000005 -90.113007 29.533593999999994 -90.137215 29.595093000000006 -90.151718 29.544514000000007 -90.208069 29.495723999999996 -90.174057 29.446938000000003 -90.036011 29.428061999999997 -90.055252 29.373985000000005 -90.029869 29.351208 -90.05587 29.308646999999993 -90.032928 29.321500999999998 -90.111565 29.214346000000006 -90.077805 29.223448000000005 -90.043037 29.176212000000007 -90.077728 29.098444 -90.227684 29.184421999999998 -90.263878 29.254514999999998 -90.242836 29.274933000000004 -90.27874 29.245307999999994 -90.284019 29.312726999999995 -90.347565 29.272121 -90.396347 29.325767999999997 -90.406746 29.352219000000005 -90.450432 29.303717000000006 -90.476387 29.304811 -90.611008 29.260701999999995 -90.583046 29.222738000000007 -90.621178 29.254065999999995 -90.649963 29.162341999999995 -90.638031 29.181691999999998 -90.683769 29.140046999999996 -90.676704 29.126712999999995 -90.782806 29.160397000000003 -90.772598 29.182114 -90.839287 29.137184000000005 -90.882545 29.181684000000004 -90.920319 29.220781000000002 -90.814201 29.256578000000005 -90.818459 29.267443 -90.897255 29.343298000000004 -90.936432 29.359800000000007 -91.078285 29.313914999999994 -91.102089 29.405705999999995 -91.213631 29.489372000000003 -91.262619 29.552368 -91.432915 29.531470999999996 -91.547752 29.641918000000004 -91.548378 29.643753000000004 -91.643433 29.768929999999997 -91.615654 29.725629999999995 -91.863136 29.765747000000005 -91.881058 29.808189 -91.846512 29.786683999999994 -91.823814 29.838835000000003 -91.827385 29.841680999999994 -91.966965 29.805848999999995 -91.973206 29.73053 -92.137024 29.773314999999997 -92.129974 29.762908999999993 -92.19957 29.606800000000007 -92.059631 29.541351000000006 -92.297241 29.588403999999997 -92.607285 29.788773000000006 -93.233505 29.758573999999996 -93.721832 29.725645 -93.801659 29.809765 -93.899734 29.850303999999994 -93.79129 30.005964000000006 -93.760201 30.060519999999997 -93.712479 30.095669 -93.715858 30.114739999999998 -93.708382 30.117929000000004 -93.696922 30.141226000000003 -93.698639 30.141253000000006 -93.685959 30.148231999999993 -93.683144 30.150807999999998 -93.699661 30.175675999999996 -93.696167 30.180860999999993 -93.704361 30.220305999999994 -93.714844 30.239372000000003 -93.707359</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>29.596264000000005 -92.016243 29.650718999999995 -91.902428 29.578401999999997 -91.76973 29.577053000000006 -91.7015 29.534039000000007 -91.764847 29.494297000000003 -91.757996 29.486866000000006 -91.848969 29.596264000000005 -92.016243</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>29.341691999999995 -91.341209 29.316238 -91.300385 29.381181999999995 -91.226448 29.297392000000002 -91.189171 29.323502000000005 -91.161461 29.283457 -91.17112 29.284537999999998 -91.18792 29.307766 -91.200356 29.273453000000003 -91.194954 29.266313999999994 -91.152489 29.245047 -91.163559 29.259704999999997 -91.13459 29.226777999999996 -91.128204 29.253806999999995 -91.276054 29.341691999999995 -91.341209</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>29.258872999999994 -90.934479 29.241874999999993 -90.970749 29.272836999999996 -90.953529 29.276128999999997 -90.981285 29.21973 -90.980286 29.226169999999996 -90.944885 29.185575 -90.961929 29.184093000000004 -91.003143 29.224581 -90.994469 29.211037000000005 -91.04435 29.275409999999994 -91.033005 29.252899 -91.067589 29.190692999999996 -91.055458 29.226864000000006 -91.122459 29.293295 -91.127411 29.297325 -91.007271 29.323582000000002 -90.998856 29.258872999999994 -90.934479</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Louisiana</topp:STATE_NAME><topp:STATE_FIPS>22</topp:STATE_FIPS><topp:SUB_REGION>W S Cen</topp:SUB_REGION><topp:STATE_ABBR>LA</topp:STATE_ABBR><topp:LAND_KM>112836.008</topp:LAND_KM><topp:WATER_KM>19978.72</topp:WATER_KM><topp:PERSONS>4219973.0</topp:PERSONS><topp:FAMILIES>1089882.0</topp:FAMILIES><topp:HOUSHOLD>1499269.0</topp:HOUSHOLD><topp:MALE>2031386.0</topp:MALE><topp:FEMALE>2188587.0</topp:FEMALE><topp:WORKERS>1468894.0</topp:WORKERS><topp:DRVALONE>1239339.0</topp:DRVALONE><topp:CARPOOL>247348.0</topp:CARPOOL><topp:PUBTRANS>49252.0</topp:PUBTRANS><topp:EMPLOYED>1641614.0</topp:EMPLOYED><topp:UNEMPLOY>175303.0</topp:UNEMPLOY><topp:SERVICE>480026.0</topp:SERVICE><topp:MANUAL>243755.0</topp:MANUAL><topp:P_MALE>0.481</topp:P_MALE><topp:P_FEMALE>0.519</topp:P_FEMALE><topp:SAMP_POP>628817.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.23"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>24.955967 -87.625725</gml:lowerCorner><gml:upperCorner>31.002975000000006 -80.051147</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.784925 -80.785889 28.736064999999996 -80.762642 28.785916999999998 -80.832329 28.785431000000003 -80.850929 28.398709999999994 -80.747414 27.869877000000002 -80.49247 27.825405000000003 -80.509583 27.845419000000007 -80.482079 27.703277999999997 -80.401604 27.555349000000007 -80.357605 27.264324000000002 -80.239868 27.185357999999994 -80.190323 27.214741000000004 -80.22422 27.241014000000007 -80.289864 27.247938000000005 -80.327019 27.212528000000006 -80.290649 27.202517 -80.221504 27.108739999999997 -80.148201 26.973709 -80.090599 26.973502999999994 -80.114845 26.796858 -80.051147 26.326020999999997 -80.08493 25.977166999999994 -80.128014 25.759654999999995 -80.193329 25.613369000000006 -80.301682 25.489627999999996 -80.330215 25.400810000000007 -80.3022 25.249505999999997 -80.417038 25.191820000000007 -80.421028 25.211920000000006 -80.552094 25.151897000000005 -80.694038 25.185230000000004 -80.85701 25.130098000000004 -80.976082 25.133785000000003 -81.119217 25.164288 -81.14843 25.268479999999997 -81.183983 25.320368000000002 -81.14077 25.214028999999996 -81.011765 25.202136999999993 -80.951233 25.246325999999996 -80.915482 25.322063 -80.974487 25.396433000000002 -81.143524 25.680716000000004 -81.258575 25.710038999999995 -81.20015 25.802723 -81.256409 25.914282 -81.530937 25.923204 -81.718483 25.999798 -81.705887 26.110792000000004 -81.794907 26.284103 -81.814049 26.331834999999998 -81.849541 26.439195999999995 -81.864403 26.467205000000007 -81.942207 26.517027 -81.968307 26.534626000000003 -81.928963 26.642636999999993 -81.884468 26.709919 -81.774109 26.663417999999993 -81.897293 26.550365 -81.944641 26.524323999999993 -82.02166 26.715442999999993 -82.083847 26.766623999999993 -82.068459 26.866366999999997 -82.051956 26.921446000000003 -82.098061 26.962719000000007 -81.991631 27.031345 -81.979385 27.031341999999995 -81.993042 26.976213 -82.012878 26.936725999999993 -82.153397 27.004071999999994 -82.257645 27.024220999999997 -82.282364 26.997569999999996 -82.257767 26.913353 -82.176682 26.789783 -82.154015 26.849542999999997 -82.290024 26.948556999999994 -82.355614 26.998177999999996 -82.398354 26.947598999999997 -82.368851 26.903291999999993 -82.343811 26.946956999999998 -82.380219 27.236664000000005 -82.507111 27.259951 -82.531311 27.210125000000005 -82.514664 27.273954000000003 -82.570137 27.331478000000004 -82.532921 27.386359999999996 -82.565742 27.473528 -82.686104 27.503142999999994 -82.639069 27.493270999999993 -82.666008 27.524114999999995 -82.681885 27.477749000000003 -82.488449 27.511673000000002 -82.514381 27.522543 -82.427361 27.512009000000006 -82.575752 27.536323999999993 -82.638947 27.552398999999994 -82.569786 27.581925999999996 -82.554443 27.554879999999997 -82.626556 27.607794 -82.540222 27.643703000000002 -82.552681 27.791323000000006 -82.404564 27.905918 -82.399002 27.939856000000006 -82.461182 27.821644000000006 -82.483315 27.830928999999998 -82.51078 27.935429 -82.539177 28.028550999999993 -82.645744 28.006866000000002 -82.650864 28.045873999999998 -82.698517 28.010217999999995 -82.672897 27.975055999999995 -82.700844 27.966285999999997 -82.64492 27.940264 -82.725662 27.878159999999994 -82.564445 27.848225 -82.623833 27.715418 -82.644798 27.705280000000002 -82.678505 27.829351000000003 -82.794014 27.717466 -82.72834 27.685559999999995 -82.74118 27.850340000000003 -82.84446 28.172736999999998 -82.77932 28.428231999999994 -82.673653 28.692483999999993 -82.636253 28.808122999999995 -82.681557 28.814018000000004 -82.636696 28.884451 -82.636795 29.008409999999998 -82.755493 29.154886000000005 -82.802322 29.179143999999994 -83.036903 29.265411 -83.074081 29.299412000000004 -83.142357 29.327944000000002 -83.109146 29.343441 -83.174782 29.433701999999997 -83.234474 29.519644 -83.380173 29.669376 -83.405113 29.737100999999996 -83.550583 29.910746000000003 -83.654312 30.077274000000003 -83.971855 30.099740999999995 -84.074738 30.081518000000003 -84.147964 30.107906 -84.233177 30.069419999999994 -84.354027 29.977183999999994 -84.360519 29.991586999999996 -84.43779 29.959339 -84.433205 29.946934 -84.339043 29.909959999999998 -84.347015 29.929419999999993 -84.464638 29.746649000000005 -84.858124 29.777760999999998 -84.928604 29.719708999999995 -84.988373 29.701345000000003 -85.214844 29.682806 -85.36496 29.776556 -85.409683 29.862859999999998 -85.414131 29.875206000000006 -85.393074 29.794155000000003 -85.402626 29.691041999999996 -85.358124 29.701498 -85.306671 29.809586999999993 -85.300606 29.923595000000006 -85.384872 30.092388 -85.628571 30.098090999999997 -85.568604 30.021659999999997 -85.471436 30.031349000000006 -85.415092 30.045551000000003 -85.432465 30.024067000000002 -85.382774 30.036083000000005 -85.382736 30.058364999999995 -85.395752 30.050961 -85.464394 30.131484999999998 -85.529793 30.178572000000003 -85.7099 30.251490000000004 -85.66613 30.250951999999998 -85.60025 30.310806 -85.56955 30.264861999999994 -85.717201 30.296729999999997 -85.752777 30.280158999999998 -85.850487 30.232613 -85.830009 30.228745000000004 -85.757988 30.128639000000007 -85.726463 30.274229000000005 -85.987076 30.387459000000007 -86.387512 30.409773 -86.505363 30.405812999999995 -86.388161 30.399688999999995 -86.240089 30.428329000000005 -86.241623 30.385603000000003 -86.1147 30.426264000000003 -86.122993 30.487657999999996 -86.219666 30.461968999999996 -86.387932 30.501040000000003 -86.45269 30.423451999999997 -86.610283 30.417765000000003 -86.79039 30.355022000000005 -87.193413 30.463367000000005 -86.932549 30.514238000000006 -87.014435 30.590237000000002 -86.986229 30.587272999999996 -87.019615 30.450367 -87.069305 30.564471999999995 -87.124565 30.557541 -87.171585 30.464836000000005 -87.160133 30.357185 -87.273926 30.323470999999998 -87.424103 30.40204 -87.409279 30.431301000000005 -87.346832 30.456971999999993 -87.34742 30.481504 -87.418839 30.531194999999997 -87.445374 30.556298999999996 -87.422089 30.619905000000003 -87.393303 30.667823999999996 -87.398666 30.692618999999993 -87.418915 30.705612000000002 -87.460159 30.748301999999995 -87.526619 30.785506999999996 -87.541916 30.848110000000005 -87.615929 30.876717 -87.625725 30.954178 -87.589882 31.002449 -87.598595 31.002975000000006 -87.163147 30.998011000000005 -86.779404 30.997911000000002 -86.701897 30.991358000000005 -86.383972 30.995045000000005 -86.181534 30.993146999999993 -86.031883 31.000816 -85.486664 31.000820000000004 -85.485077 31.001068000000004 -85.001678 30.979134000000002 -85.000214 30.963477999999995 -84.97364 30.926907 -84.969086 30.894769999999994 -84.937851 30.846733 -84.927002 30.802435000000003 -84.93264 30.775897999999998 -84.926971 30.753899000000004 -84.915421 30.743697999999995 -84.888367 30.714312000000007 -84.864662 30.712473000000003 -84.863083 30.693897000000007 -84.374542 30.690224 -84.281761 30.678381 -84.075668 30.675341000000003 -84.000839 30.660292 -83.737961 30.650582999999997 -83.609177 30.634096 -83.31073 30.633077999999998 -83.301445 30.621129999999994 -83.132576 30.595502999999994 -82.696503 30.588835000000003 -82.583107 30.582468000000006 -82.463287 30.577084 -82.412247 30.566845 -82.221199 30.531214000000006 -82.238556 30.502739000000005 -82.219528 30.489782000000005 -82.199348 30.42362 -82.205727 30.368397 -82.180397 30.361057000000002 -82.164803 30.363557999999998 -82.052933 30.378668000000005 -82.038521 30.433831999999995 -82.046265 30.442642000000006 -82.035286 30.477481999999995 -82.022675 30.570755000000005 -82.005966 30.598455 -82.013817 30.650677 -82.045074 30.757299000000003 -82.032043 30.764022999999995 -82.01313 30.791086000000007 -82.015701 30.778118000000006 -81.981644 30.795773999999994 -81.961716 30.820198000000005 -81.953194 30.824016999999998 -81.94397 30.813242000000002 -81.907684 30.828075 -81.899307 30.798851 -81.873169 30.787851000000003 -81.803368 30.761351000000005 -81.776878 30.769397999999995 -81.757881 30.763655 -81.736847 30.745133999999993 -81.716942 30.728846000000004 -81.642654 30.731538999999998 -81.628159 30.716085000000007 -81.60495 30.724650999999994 -81.601349 30.706340999999995 -81.536774 30.721216 -81.528778 30.598367999999994 -81.49897 30.555975000000004 -81.518303 30.454525000000004 -81.457718 30.380301000000003 -81.480988 30.356947000000005 -81.443451 30.246539999999996 -81.432961 29.912806000000003 -81.302963 29.828992999999997 -81.316696 29.737694000000005 -81.243996 29.668774 -81.233978 29.429985000000002 -81.1101 29.351543000000007 -81.097275 28.784925 -80.785889</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>30.399535999999998 -86.833923 30.403769999999994 -86.791595 30.414351999999994 -86.620872 30.406591000000006 -86.534805 30.400948 -86.52282 30.393187999999995 -86.532692 30.402359000000004 -86.738686 30.390366 -86.793007 30.386838999999995 -86.834625 30.321231999999995 -87.242378 30.321231999999995 -87.267075 30.332520000000002 -87.290352 30.339573 -87.286118 30.334635000000006 -87.202171 30.351566000000005 -87.139381 30.369202 -87.047676 30.381195000000005 -86.929161 30.399535999999998 -86.833923</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.788576000000006 -80.736061 28.821799999999996 -80.763557 28.895123999999996 -80.817406 29.013130000000004 -80.891304 29.071560000000005 -80.916512 29.061249000000004 -80.899323 28.991361999999995 -80.862663 28.788576000000006 -80.736061</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.784098 -80.726128 28.589879999999994 -80.579918 28.463184 -80.526176 28.408979000000002 -80.587883 28.412378000000004 -80.621857 28.548378 -80.577675 28.603649000000004 -80.599358 28.618378000000007 -80.660271 28.588076 -80.692108 28.618686999999994 -80.78067 28.687444999999997 -80.786156 28.706346999999994 -80.735222 28.736614000000003 -80.754295 28.657073999999994 -80.641426 28.784098 -80.726128</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>28.581490000000002 -80.68885 28.600616000000002 -80.654022 28.573291999999995 -80.609421 28.427130000000005 -80.662956 28.301556000000005 -80.667336 28.144653000000005 -80.605797 28.385118000000006 -80.721268 28.581490000000002 -80.68885</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>26.585724 -82.102127 26.497208 -82.067139 26.493585999999993 -82.097641 26.642279000000002 -82.135895 26.683082999999996 -82.183495 26.693342 -82.128838 26.585724 -82.102127</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>25.354546 -80.24968 25.157832999999997 -80.355888 24.955967 -80.588036 25.211134 -80.353287 25.267585999999994 -80.330521 25.296104 -80.361824 25.354546 -80.24968</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Florida</topp:STATE_NAME><topp:STATE_FIPS>12</topp:STATE_FIPS><topp:SUB_REGION>S Atl</topp:SUB_REGION><topp:STATE_ABBR>FL</topp:STATE_ABBR><topp:LAND_KM>139852.123</topp:LAND_KM><topp:WATER_KM>30456.797</topp:WATER_KM><topp:PERSONS>1.2937926E7</topp:PERSONS><topp:FAMILIES>3511825.0</topp:FAMILIES><topp:HOUSHOLD>5134869.0</topp:HOUSHOLD><topp:MALE>6261719.0</topp:MALE><topp:FEMALE>6676207.0</topp:FEMALE><topp:WORKERS>4943568.0</topp:WORKERS><topp:DRVALONE>4468021.0</topp:DRVALONE><topp:CARPOOL>818546.0</topp:CARPOOL><topp:PUBTRANS>116352.0</topp:PUBTRANS><topp:EMPLOYED>5810467.0</topp:EMPLOYED><topp:UNEMPLOY>356769.0</topp:UNEMPLOY><topp:SERVICE>1683987.0</topp:SERVICE><topp:MANUAL>675050.0</topp:MANUAL><topp:P_MALE>0.484</topp:P_MALE><topp:P_FEMALE>0.516</topp:P_FEMALE><topp:SAMP_POP>1603894.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.24"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>41.69744499999999 -90.40802</gml:lowerCorner><gml:upperCorner>48.173923 -82.419922</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>48.173923 -88.497459 48.033287 -88.625252 47.960361000000006 -88.901466 47.850761000000006 -89.028534 47.82418100000001 -89.139793 47.844719 -89.192825 47.88396499999999 -89.201698 47.93933899999999 -89.156013 48.173923 -88.497459</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>47.290257 -88.500565 47.355976 -88.43779 47.447922000000005 -88.211288 47.47089 -87.78801 47.416045999999994 -87.704269 47.39312000000001 -87.737396 47.35809699999999 -87.916931 47.200829 -88.222168 46.988167000000004 -88.41272 47.111546000000004 -88.470543 47.134838 -88.594139 47.243668000000014 -88.595512 47.290257 -88.500565</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>45.96948599999999 -85.859734 45.95799600000001 -85.914841 45.91820899999999 -85.916992 45.96423300000001 -86.067764 45.946957 -86.259178 45.905708000000004 -86.315498 45.834419 -86.343658 45.762767999999994 -86.458138 45.748980999999986 -86.529251 45.72411299999999 -86.521873 45.710194 -86.575989 45.62125 -86.629654 45.65006600000001 -86.684921 45.692532 -86.696785 45.81390400000001 -86.584595 45.82609600000001 -86.76133 45.71480600000001 -86.901489 45.69627800000001 -87.123627 45.55482900000001 -87.260582 45.42396500000001 -87.332108 45.16274999999999 -87.583755 45.108517000000006 -87.592407 45.140690000000006 -87.672707 45.176624000000004 -87.729561 45.199093000000005 -87.736092 45.211693 -87.721519 45.236794 -87.719559 45.247108 -87.705032 45.27222800000001 -87.704361 45.34819400000001 -87.645248 45.36188100000001 -87.64357 45.39129600000001 -87.689484 45.352924 -87.759926 45.358349000000004 -87.827896 45.346176000000014 -87.841171 45.370193 -87.861984 45.37210099999999 -87.868423 45.36211399999999 -87.873863 45.365883 -87.883499 45.406147000000004 -87.849419 45.44512900000001 -87.860153 45.46649199999999 -87.813499 45.4991 -87.789268 45.54455899999999 -87.805023 45.568625999999995 -87.828484 45.568554000000006 -87.786194 45.60042200000001 -87.774956 45.613235 -87.775925 45.654487999999986 -87.819817 45.66542799999999 -87.816933 45.67595299999999 -87.780823 45.68413899999999 -87.777351 45.701362999999986 -87.801033 45.71143000000001 -87.80143 45.72245799999999 -87.842239 45.75074000000001 -87.873505 45.766491 -87.969055 45.79508999999999 -87.989944 45.786156000000005 -88.051514 45.79157599999999 -88.088608 45.819447 -88.129822 45.834923 -88.121658 45.87368799999999 -88.065292 45.891850000000005 -88.095634 45.92066199999999 -88.093719 45.926334 -88.111259 45.93634 -88.150307 45.953564 -88.180061 45.947948 -88.214859 45.96710200000001 -88.257034 45.961989999999986 -88.299019 45.966758999999996 -88.32119 45.99463299999999 -88.369804 45.98346699999999 -88.403389 46.000805000000014 -88.454185 45.999194999999986 -88.483681 46.013003999999995 -88.49395 46.018654 -88.51548 46.01934399999999 -88.548225 46.00900300000001 -88.575226 46.015559999999994 -88.597404 45.99416400000001 -88.615372 45.99343099999999 -88.643539 46.02018699999999 -88.677254 46.018967 -88.703476 46.02962500000001 -88.72628 46.02119099999999 -88.772888 46.032658 -88.777351 46.036404000000005 -88.793686 46.026848 -88.804268 46.073646999999994 -88.925064 46.100437 -88.985168 46.145691 -89.09967 46.30407700000001 -89.924973 46.34048100000001 -90.111488 46.365207999999996 -90.115005 46.39395099999999 -90.141624 46.442432 -90.161217 46.506348 -90.211349 46.50884199999999 -90.258224 46.52253300000001 -90.269608 46.525104999999996 -90.300003 46.54435000000001 -90.302216 46.55161699999999 -90.31353 46.53971100000001 -90.385345 46.56866500000001 -90.40802 46.678687999999994 -90.018692 46.76899 -89.886086 46.824768000000006 -89.791084 46.850266000000005 -89.386574 46.923439 -89.214455 46.996669999999995 -89.125053 46.997169000000014 -88.994743 47.03099399999999 -88.929558 47.104625999999996 -88.884705 47.225887 -88.629379 47.13118700000001 -88.617981 47.10657900000001 -88.511093 47.03266099999999 -88.512871 46.99080699999999 -88.44104 46.92837499999999 -88.445839 46.855221 -88.476395 46.799465 -88.446487 46.945965 -88.177704 46.90103099999999 -88.189064 46.91194200000001 -88.03656 46.90984 -87.900528 46.836929 -87.663635 46.50805700000001 -87.371399 46.50153700000001 -87.110535 46.53635800000001 -87.006256 46.44442000000001 -86.871231 46.486694 -86.759338 46.42231799999999 -86.638062 46.56113400000001 -86.462227 46.67308399999999 -86.147942 46.65529599999999 -86.096573 46.694828 -85.857376 46.67416800000001 -85.503716 46.75676300000001 -85.22998 46.770927 -84.954681 46.69431700000001 -85.026886 46.549004 -85.01889 46.50555800000001 -85.051567 46.476425000000006 -85.016556 46.48782299999999 -84.931244 46.444035000000014 -84.803589 46.482922 -84.629761 46.407906 -84.572617 46.480637 -84.415924 46.488646999999986 -84.311577 46.248703000000006 -84.181618 46.207291 -84.273102 46.17142899999999 -84.247002 46.17608999999999 -84.119713 46.12892500000001 -84.029564 46.09445199999999 -84.061966 46.02596700000001 -83.989494 46.005882000000014 -83.901947 45.96021999999999 -83.906456 45.978519000000006 -84.113258 45.99917199999999 -84.354454 45.978329 -84.501595 46.038216000000006 -84.616798 46.03590399999999 -84.688972 45.85566700000001 -84.731682 45.890625 -84.851044 46.024742 -85.061562 46.10004799999999 -85.378151 46.101916999999986 -85.509445 45.972881 -85.655281 45.96948599999999 -85.859734</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>46.01401100000001 -83.854675 45.988392000000005 -83.801102 46.02731700000001 -83.756416 46.036170999999996 -83.673592 46.071774000000005 -83.680313 46.08408700000001 -83.732445 46.103950999999995 -83.649887 46.08849699999999 -83.5895 46.01176799999999 -83.533997 45.987526 -83.473198 45.92569399999999 -83.516167 45.91748000000001 -83.579819 45.953575 -83.629707 45.936745 -83.804878 45.99742900000001 -83.852806 45.970833 -83.885887 46.01401100000001 -83.854675</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.765465000000006 -86.834801 41.90740600000001 -86.617569 42.126408 -86.49881 42.24938599999999 -86.374252 42.42229499999999 -86.28495 42.77480299999999 -86.217812 43.12102899999999 -86.273781 43.47515899999999 -86.463127 43.66318100000001 -86.541222 43.772659000000004 -86.447731 43.766636000000005 -86.404266 43.781452 -86.434021 43.82011800000001 -86.428734 43.95017999999999 -86.459465 43.94558699999999 -86.438065 44.053616000000005 -86.518517 44.183201 -86.386337 44.351226999999994 -86.271866 44.52227400000001 -86.237946 44.700733000000014 -86.25853 44.734443999999996 -86.108391 44.777930999999995 -86.082825 44.850616 -86.09787 44.89826199999999 -86.06736 44.98597699999999 -85.79567 45.196529 -85.61013 45.18056100000001 -85.56543 44.95836299999999 -85.652924 44.778435 -85.637962 44.763161 -85.526009 44.86053799999999 -85.451279 45.01060100000001 -85.384796 45.21159 -85.390167 45.273537000000005 -85.373177 45.32037700000001 -85.305405 45.370216 -85.092804 45.37316899999999 -84.98584 45.40988899999999 -84.921623 45.464642 -85.081757 45.569771 -85.120384 45.630177 -85.077957 45.683704000000006 -84.983353 45.73773600000001 -84.971977 45.780293 -84.724136 45.653625000000005 -84.46524 45.665595999999994 -84.321434 45.63089400000001 -84.205544 45.57133099999999 -84.135216 45.498737000000006 -84.105896 45.49176 -83.92289 45.409435 -83.782814 45.41238000000001 -83.712326 45.34948700000001 -83.592377 45.36078599999999 -83.49585 45.328922000000006 -83.489616 45.27289200000001 -83.394043 45.25716800000001 -83.420784 45.213627 -83.39872 45.09860599999999 -83.312737 45.052761000000004 -83.444466 45.01111599999999 -83.433998 44.997871 -83.464928 44.92628500000001 -83.429382 44.860634000000005 -83.319756 44.703171 -83.280846 44.515446 -83.320068 44.335117 -83.356995 44.261257 -83.529175 44.17010099999999 -83.56826 44.07047700000001 -83.598427 43.99714700000001 -83.704819 43.96282199999999 -83.873627 43.916977 -83.918388 43.698261 -83.938141 43.59962100000001 -83.699196 43.60739899999999 -83.654648 43.72592499999999 -83.530945 43.70282399999999 -83.494286 43.745723999999996 -83.466446 43.844437 -83.367203 43.94044500000001 -83.326065 44.06994599999999 -82.940208 44.03355400000001 -82.806038 43.97249600000001 -82.727966 43.787848999999994 -82.618561 43.69454999999999 -82.605812 43.172225999999995 -82.503899 42.97243499999999 -82.419922 42.898651 -82.472038 42.76286300000001 -82.473328 42.634018 -82.518272 42.63169500000001 -82.645966 42.66935000000001 -82.634102 42.681194000000005 -82.729889 42.635761 -82.820488 42.61289199999999 -82.802444 42.495720000000006 -82.888222 42.458031000000005 -82.874992 42.36300299999999 -82.929474 42.292666999999994 -83.107666 42.115706999999986 -83.193947 42.03393600000001 -83.19014 41.725081999999986 -83.482758 41.71699100000001 -83.764015 41.71594200000001 -83.868698 41.707989 -84.359261 41.7071 -84.384445 41.69744499999999 -84.790421 41.760909999999996 -84.788521 41.76182600000001 -84.82605 41.76281700000001 -85.193169 41.763531 -85.297234 41.76257699999999 -85.65947 41.763485 -85.799232 41.764579999999995 -86.068298 41.764815999999996 -86.234558 41.76549499999999 -86.525162 41.765465000000006 -86.834801</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Michigan</topp:STATE_NAME><topp:STATE_FIPS>26</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>MI</topp:STATE_ABBR><topp:LAND_KM>147135.821</topp:LAND_KM><topp:WATER_KM>12547.912</topp:WATER_KM><topp:PERSONS>9295297.0</topp:PERSONS><topp:FAMILIES>2439171.0</topp:FAMILIES><topp:HOUSHOLD>3419331.0</topp:HOUSHOLD><topp:MALE>4512781.0</topp:MALE><topp:FEMALE>4782516.0</topp:FEMALE><topp:WORKERS>3388716.0</topp:WORKERS><topp:DRVALONE>3327685.0</topp:DRVALONE><topp:CARPOOL>429086.0</topp:CARPOOL><topp:PUBTRANS>66653.0</topp:PUBTRANS><topp:EMPLOYED>4166196.0</topp:EMPLOYED><topp:UNEMPLOY>374341.0</topp:UNEMPLOY><topp:SERVICE>1138730.0</topp:SERVICE><topp:MANUAL>717162.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>1735861.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.25"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>44.35372899999999 -116.0625</gml:lowerCorner><gml:upperCorner>49.0 -104.04258</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>44.70223999999999 -111.474632 44.691494000000006 -111.480011 44.67010099999999 -111.4599 44.652634000000006 -111.457474 44.64078900000001 -111.469376 44.637767999999994 -111.506897 44.616051 -111.500954 44.593277 -111.513733 44.55126999999999 -111.492111 44.55002200000001 -111.462036 44.538002000000006 -111.458534 44.536224000000004 -111.481781 44.52877799999999 -111.489449 44.55294799999999 -111.566437 44.543071999999995 -111.604454 44.550835000000006 -111.684067 44.53384399999999 -111.716202 44.51890900000001 -111.766121 44.51854700000001 -111.791809 44.504065999999995 -111.807037 44.55635100000001 -111.871696 44.549812 -111.939575 44.529762000000005 -111.977005 44.53513000000001 -112.022797 44.52293 -112.02626 44.52869799999999 -112.058548 44.518317999999994 -112.099075 44.52833899999999 -112.123367 44.53153599999999 -112.19883 44.53858199999999 -112.216934 44.55957799999999 -112.229568 44.560058999999995 -112.255844 44.54178999999999 -112.281509 44.525188000000014 -112.341675 44.49726899999999 -112.339745 44.44936000000001 -112.366753 44.449375 -112.419922 44.46892500000001 -112.457687 44.463089 -112.501007 44.477588999999995 -112.53849 44.48089200000001 -112.652351 44.49702500000001 -112.713486 44.484409 -112.732872 44.47400999999999 -112.779022 44.458099000000004 -112.795387 44.42117300000001 -112.825851 44.39490900000001 -112.817871 44.364292000000006 -112.816559 44.35372899999999 -112.843437 44.370068 -112.869942 44.39294100000001 -112.886467 44.407279999999986 -112.937439 44.43562700000001 -112.984406 44.43780100000001 -113.011169 44.45270199999999 -113.005814 44.48186100000001 -113.019463 44.510695999999996 -113.006866 44.53304299999999 -113.036972 44.556377 -113.038811 44.582764 -113.082184 44.624371 -113.053436 44.67560599999999 -113.072289 44.69599500000001 -113.098099 44.71525199999999 -113.100845 44.737457000000006 -113.126572 44.761517 -113.137413 44.811916 -113.239471 44.810562000000004 -113.256287 44.780304 -113.31781 44.779076 -113.33976 44.80764400000001 -113.349152 44.833775 -113.420502 44.851315 -113.444695 44.930744000000004 -113.495308 44.939648000000005 -113.486465 44.940848999999986 -113.462532 44.949596000000014 -113.447884 44.998267999999996 -113.440147 45.027523 -113.45797 45.04342299999999 -113.454552 45.05839499999999 -113.48542 45.071293 -113.489273 45.082137999999986 -113.519722 45.107910000000004 -113.509338 45.10762399999999 -113.551384 45.117785999999995 -113.573486 45.13462100000001 -113.570694 45.14981800000001 -113.593208 45.18106800000001 -113.600037 45.20686699999999 -113.644699 45.26236 -113.689224 45.27786599999999 -113.687813 45.321608999999995 -113.738182 45.382464999999996 -113.74041 45.410252000000014 -113.774124 45.44571300000001 -113.78476 45.477787000000006 -113.768265 45.50713300000001 -113.7714 45.51694499999999 -113.780029 45.51498799999999 -113.832809 45.583809 -113.802849 45.600716000000006 -113.821579 45.60964200000001 -113.85112 45.61357100000001 -113.902397 45.63733300000001 -113.901291 45.655204999999995 -113.922623 45.671291 -113.925789 45.67945900000001 -113.963234 45.69745599999999 -113.970238 45.68641299999999 -114.00856 45.672459 -114.018967 45.652592 -114.010078 45.64085399999999 -114.01712 45.625225 -114.055603 45.58645999999999 -114.082054 45.57120900000001 -114.117226 45.550465 -114.131134 45.544005999999996 -114.171753 45.52799999999999 -114.193893 45.53537399999999 -114.241081 45.503029 -114.246964 45.48594299999999 -114.261322 45.45750799999999 -114.325516 45.46346700000001 -114.349327 45.485825000000006 -114.370537 45.49909199999999 -114.418129 45.52771799999999 -114.432632 45.547932 -114.461784 45.54673399999999 -114.495667 45.558277000000004 -114.526466 45.548823999999996 -114.559998 45.59648100000001 -114.540031 45.62435500000001 -114.563751 45.652477000000005 -114.500816 45.67414099999999 -114.509781 45.694484999999986 -114.496635 45.72308000000001 -114.53405 45.746082 -114.541031 45.762482000000006 -114.562614 45.81014999999999 -114.516449 45.84276600000001 -114.498238 45.83955 -114.472878 45.85270299999999 -114.442307 45.84653499999999 -114.406601 45.870968000000005 -114.391914 45.910731999999996 -114.412605 45.921558000000005 -114.428535 45.95406 -114.404366 45.972054000000014 -114.411522 45.98988700000001 -114.483528 46.00984600000001 -114.473602 46.02349100000001 -114.493393 46.050895999999995 -114.464828 46.08231000000001 -114.455101 46.107437000000004 -114.47644 46.11622199999999 -114.505638 46.136143000000004 -114.518013 46.157497000000006 -114.508682 46.15534199999999 -114.466087 46.169048000000004 -114.439949 46.22033300000001 -114.438622 46.24386200000001 -114.471901 46.25304 -114.472862 46.28478999999999 -114.430862 46.39299 -114.408859 46.39962399999999 -114.39608 46.428257 -114.383087 46.487449999999995 -114.409775 46.506203 -114.359528 46.51746700000001 -114.349174 46.58795900000001 -114.342377 46.622917 -114.323769 46.654304999999994 -114.33374 46.661674000000005 -114.383072 46.645793999999995 -114.44059 46.62365299999999 -114.483772 46.637969999999996 -114.539444 46.62912800000001 -114.609879 46.66090399999999 -114.643791 46.67100099999999 -114.644089 46.687186999999994 -114.624977 46.734802 -114.672935 46.73384100000001 -114.697479 46.695212999999995 -114.747154 46.70312100000001 -114.781967 46.755798 -114.776878 46.76661300000001 -114.793076 46.77562 -114.839836 46.79712699999999 -114.865646 46.799515000000014 -114.901367 46.85252800000001 -114.947449 46.89096799999999 -114.939606 46.90724599999999 -114.923164 46.92529300000001 -114.963768 46.958889 -115.00061 46.96308099999999 -115.036369 46.97343799999999 -115.054672 47.02660399999999 -115.080368 47.06362899999999 -115.134537 47.09182000000001 -115.147713 47.09764899999999 -115.171524 47.12410399999999 -115.192101 47.17962600000001 -115.295258 47.24522400000001 -115.324249 47.25509600000001 -115.342682 47.263667999999996 -115.407227 47.27444800000001 -115.425659 47.28171900000001 -115.500946 47.292057 -115.522079 47.334686000000005 -115.554535 47.370075000000014 -115.598549 47.38011599999999 -115.637794 47.399238999999994 -115.665489 47.42254600000001 -115.749336 47.43403599999999 -115.749115 47.44537399999999 -115.731491 47.44924900000001 -115.655098 47.457863 -115.642197 47.475303999999994 -115.639153 47.489609 -115.69178 47.520962 -115.700531 47.53376 -115.741837 47.59079 -115.691093 47.616150000000005 -115.697289 47.63995 -115.73307 47.69562500000001 -115.732666 47.70980499999999 -115.774727 47.744911 -115.789536 47.75635500000001 -115.835739 47.805256000000014 -115.84832 47.827526000000006 -115.868805 47.841148000000004 -115.902916 47.867199 -115.936836 47.925217 -115.997925 47.965014999999994 -116.024307 47.976269 -116.052483 48.208549000000005 -116.054497 48.498726000000005 -116.055679 48.99999600000001 -116.0625 49.0 -114.728333 49.0 -114.0625 48.99999600000001 -112.1875 49.0 -111.281876 49.0 -110.75 48.99999600000001 -109.5 48.99999600000001 -108.25 48.99999600000001 -107.1875 48.99999600000001 -106.125 48.99999600000001 -105.0625 49.0 -104.0625 48.64579800000001 -104.051826 48.390995000000004 -104.05162 48.00006099999999 -104.047935 47.400002 -104.046822 47.33381700000001 -104.045441 46.64293699999999 -104.046951 46.54253 -104.046219 46.280876000000006 -104.047348 45.942993 -104.048416 45.88305299999999 -104.049026 45.212886999999995 -104.043358 44.997817999999995 -104.04258 44.997349000000014 -104.059349 45.001091 -105.04126 44.999832 -105.084465 44.99722700000001 -106.020576 44.996173999999996 -106.258644 44.99981299999999 -107.893715 45.000159999999994 -108.258568 44.99764300000001 -108.624573 44.99958000000001 -109.798653 45.00285299999999 -109.994789 44.99868799999999 -110.392006 44.99234799999999 -110.428894 44.995766 -111.05265 44.66456199999999 -111.050842 44.47339600000001 -111.050789 44.486198 -111.093857 44.500832 -111.128143 44.52797699999999 -111.133583 44.54526100000001 -111.169464 44.564926000000014 -111.177986 44.57324600000001 -111.218727 44.60263800000001 -111.233452 44.61805699999999 -111.219017 44.62698399999999 -111.22319 44.64228800000001 -111.269882 44.673878 -111.269424 44.683013999999986 -111.294884 44.70526899999999 -111.31469 44.72793999999999 -111.318436 44.72625400000001 -111.34919 44.74516299999999 -111.371521 44.73777000000001 -111.384171 44.708946 -111.394295 44.713257 -111.442841 44.70223999999999 -111.474632</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Montana</topp:STATE_NAME><topp:STATE_FIPS>30</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>MT</topp:STATE_ABBR><topp:LAND_KM>376990.894</topp:LAND_KM><topp:WATER_KM>3858.589</topp:WATER_KM><topp:PERSONS>799065.0</topp:PERSONS><topp:FAMILIES>211666.0</topp:FAMILIES><topp:HOUSHOLD>306163.0</topp:HOUSHOLD><topp:MALE>395769.0</topp:MALE><topp:FEMALE>403296.0</topp:FEMALE><topp:WORKERS>293243.0</topp:WORKERS><topp:DRVALONE>250373.0</topp:DRVALONE><topp:CARPOOL>41442.0</topp:CARPOOL><topp:PUBTRANS>2050.0</topp:PUBTRANS><topp:EMPLOYED>350723.0</topp:EMPLOYED><topp:UNEMPLOY>26217.0</topp:UNEMPLOY><topp:SERVICE>123090.0</topp:SERVICE><topp:MANUAL>43619.0</topp:MANUAL><topp:P_MALE>0.495</topp:P_MALE><topp:P_FEMALE>0.505</topp:P_FEMALE><topp:SAMP_POP>150091.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.26"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>43.09096099999999 -71.08799</gml:lowerCorner><gml:upperCorner>47.45329699999999 -66.969849</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>44.074073999999996 -69.777786 43.99992399999999 -69.860435 43.756001 -69.792038 43.727901 -69.830902 43.74424400000001 -69.852295 43.842262000000005 -69.846664 43.876633 -69.887299 43.790649 -69.903641 43.768764000000004 -69.973412 43.786125 -70.000008 43.84565699999999 -69.987877 43.84551999999999 -70.026909 43.78972999999999 -70.157135 43.68571499999999 -70.236305 43.577156 -70.222748 43.534823999999986 -70.342117 43.430217999999996 -70.366432 43.349383999999986 -70.457481 43.335632000000004 -70.539444 43.09096099999999 -70.666176 43.12178399999999 -70.819168 43.159088 -70.831047 43.235138000000006 -70.813705 43.280936999999994 -70.901581 43.301987 -70.906296 43.366299 -70.970192 43.39610300000001 -70.979591 43.438046000000014 -70.961975 43.47013100000001 -70.971283 43.51630800000001 -70.95977 43.53191000000001 -70.96476 43.54887400000001 -70.950111 43.564064 -70.957016 43.571751000000006 -70.974365 43.791088 -70.984932 44.282082 -71.009079 44.66848400000001 -71.029205 45.301422 -71.08799 45.338818 -70.959862 45.22539499999999 -70.87693 45.278088 -70.843361 45.354629999999986 -70.813148 45.39067800000001 -70.829613 45.42512500000001 -70.797447 45.391918000000004 -70.635414 45.51290900000001 -70.72039 45.660618 -70.55275 45.72200000000001 -70.396866 45.79026400000001 -70.416695 45.89896400000001 -70.254448 45.94458 -70.247948 45.96874199999999 -70.310776 46.05311599999999 -70.280502 46.06662 -70.305328 46.13739799999999 -70.229805 46.190212 -70.283974 46.33480499999999 -70.191536 46.426083000000006 -70.047089 46.57056399999999 -70.014626 46.691329999999994 -69.985458 47.45329699999999 -69.230789 47.421992999999986 -69.047478 47.257328 -69.037231 47.18222 -68.895386 47.29692499999999 -68.515182 47.285056999999995 -68.391769 47.35733400000001 -68.335327 47.35210799999999 -68.231323 47.060959 -67.79155 45.94700599999999 -67.78083 45.91652300000001 -67.756157 45.87841800000001 -67.795113 45.827740000000006 -67.759911 45.794448999999986 -67.803596 45.67805100000001 -67.803978 45.65922499999999 -67.753502 45.68123600000001 -67.718582 45.605132999999995 -67.615692 45.592495000000014 -67.439857 45.50348700000001 -67.416641 45.485748 -67.504662 45.37578199999999 -67.419113 45.28020900000001 -67.478508 45.18951000000001 -67.439995 45.12217699999999 -67.346169 45.18270899999999 -67.274658 45.15618900000001 -67.166473 45.121914000000004 -67.15123 44.959216999999995 -67.065933 44.904503000000005 -67.147278 44.828575 -66.969849 44.78054399999999 -67.008293 44.65370200000001 -67.200935 44.65344200000001 -67.309036 44.691322000000014 -67.389076 44.598254999999995 -67.571556 44.54016100000001 -67.6194 44.553932 -67.811775 44.535999000000004 -67.859116 44.452319999999986 -67.900597 44.471149 -67.968895 44.50524899999999 -67.963989 44.484734 -67.987076 44.38487599999999 -68.016945 44.381294 -68.074928 44.47515899999999 -68.13681 44.49056999999999 -68.246155 44.431309 -68.364304 44.465228999999994 -68.429108 44.398971999999986 -68.552719 44.28975700000001 -68.530609 44.25980799999999 -68.559959 44.34625199999999 -68.740837 44.327354000000014 -68.813377 44.413914000000005 -68.814293 44.507209999999986 -68.741875 44.552246 -68.745804 44.608833000000004 -68.824074 44.664017 -68.824333 44.610896999999994 -68.86113 44.56958 -68.808426 44.494518 -68.812202 44.430256000000014 -68.959702 44.271033999999986 -68.98555 44.24401499999999 -69.022003 44.068985 -69.074974 43.94670500000001 -69.21965 43.94210799999999 -69.294159 44.01588799999999 -69.346962 44.025047 -69.394997 43.887077000000005 -69.483742 43.844779999999986 -69.589836 43.852141999999986 -69.664963 43.98017100000001 -69.655754 44.033535 -69.613441 43.93790100000001 -69.721146 43.893294999999995 -69.749039 43.784392999999994 -69.725182 43.76161999999999 -69.75087 43.79118700000001 -69.778183 44.026791 -69.800522 44.04765699999999 -69.767265 44.074073999999996 -69.777786</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>44.377174 -68.388458 44.39887200000001 -68.350792 44.42877999999999 -68.355988 44.43748500000001 -68.23925 44.33441500000001 -68.165314 44.289951 -68.305244 44.224998 -68.321251 44.27072100000001 -68.403427 44.377174 -68.388458</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Maine</topp:STATE_NAME><topp:STATE_FIPS>23</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>ME</topp:STATE_ABBR><topp:LAND_KM>79939.207</topp:LAND_KM><topp:WATER_KM>10236.779</topp:WATER_KM><topp:PERSONS>1227928.0</topp:PERSONS><topp:FAMILIES>328685.0</topp:FAMILIES><topp:HOUSHOLD>465312.0</topp:HOUSHOLD><topp:MALE>597850.0</topp:MALE><topp:FEMALE>630078.0</topp:FEMALE><topp:WORKERS>456299.0</topp:WORKERS><topp:DRVALONE>424101.0</topp:DRVALONE><topp:CARPOOL>79763.0</topp:CARPOOL><topp:PUBTRANS>5084.0</topp:PUBTRANS><topp:EMPLOYED>571842.0</topp:EMPLOYED><topp:UNEMPLOY>40722.0</topp:UNEMPLOY><topp:SERVICE>172830.0</topp:SERVICE><topp:MANUAL>94926.0</topp:MANUAL><topp:P_MALE>0.487</topp:P_MALE><topp:P_FEMALE>0.513</topp:P_FEMALE><topp:SAMP_POP>274322.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.27"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>45.930859 -104.0625</gml:lowerCorner><gml:upperCorner>49.0 -96.551582</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>45.938297000000006 -98.730057 45.939583 -99.006447 45.94278 -99.716949 45.943561999999986 -99.875389 45.940392 -100.514 45.94250500000001 -102.00235 45.94166200000001 -102.945953 45.941113 -102.994377 45.942993 -104.048416 46.280876000000006 -104.047348 46.54253 -104.046219 46.64293699999999 -104.046951 47.33381700000001 -104.045441 47.400002 -104.046822 48.00006099999999 -104.047935 48.390995000000004 -104.05162 48.64579800000001 -104.051826 49.0 -104.0625 48.99999600000001 -102.9375 49.0 -102.02182 49.0 -101.5 48.99999600000001 -100.1875 49.0 -99.53315 49.0 -99.0 48.99999600000001 -97.9375 49.0 -97.229095 48.931842999999986 -97.216026 48.873772 -97.175385 48.835995 -97.17086 48.815552 -97.180077 48.810383 -97.164368 48.80152899999999 -97.173599 48.781184999999994 -97.147171 48.76355699999999 -97.138901 48.75566900000001 -97.147552 48.74723399999999 -97.132156 48.72625400000001 -97.13446 48.70859899999999 -97.109756 48.69525899999999 -97.116394 48.674544999999995 -97.096825 48.629963000000004 -97.107285 48.62980999999999 -97.127098 48.62078500000001 -97.122612 48.61403999999999 -97.144371 48.58692199999999 -97.140465 48.58365599999999 -97.157845 48.57287199999999 -97.151779 48.56227899999999 -97.167595 48.549553 -97.146271 48.545094000000006 -97.160088 48.53841399999999 -97.15519 48.53466399999999 -97.139038 48.517967 -97.14798 48.51732999999999 -97.134247 48.43812600000001 -97.143265 48.437118999999996 -97.119286 48.41612599999999 -97.122253 48.41962799999999 -97.151299 48.41000700000001 -97.149475 48.40790200000001 -97.128777 48.38822200000001 -97.15847 48.38442599999999 -97.134857 48.37247099999999 -97.133438 48.36323200000001 -97.150047 48.36150699999999 -97.130775 48.326008 -97.136787 48.319942 -97.112244 48.310986000000014 -97.132286 48.303635000000014 -97.114403 48.29489899999999 -97.113373 48.29305600000001 -97.130165 48.28616299999999 -97.112335 48.277893000000006 -97.111366 48.2645 -97.136307 48.25918999999999 -97.123436 48.233540000000005 -97.127205 48.22806499999999 -97.108887 48.22177099999999 -97.139404 48.20762300000001 -97.11055 48.20375799999999 -97.130478 48.19507999999999 -97.136925 48.175242999999995 -97.135941 48.16778600000001 -97.137093 48.15924100000001 -97.115715 48.148415 -97.136162 48.142791999999986 -97.120567 48.116386000000006 -97.121521 48.100989999999996 -97.098679 48.07036199999999 -97.092369 48.048182999999995 -97.066719 47.95494500000001 -97.047699 47.917912 -97.014977 47.875590999999986 -97.02021 47.87021999999999 -96.999985 47.82805300000001 -96.976875 47.809685 -96.983536 47.794464000000005 -96.957474 47.763531 -96.931656 47.71411900000001 -96.923302 47.67394999999999 -96.889069 47.61528000000001 -96.872978 47.60117700000001 -96.85186 47.56300400000001 -96.858307 47.54459399999999 -96.848831 47.52138099999999 -96.860329 47.50064499999999 -96.851257 47.46156300000001 -96.866325 47.436779 -96.855469 47.41311300000001 -96.86689 47.408962 -96.849648 47.38414399999999 -96.83947 47.36098100000001 -96.850273 47.34227000000001 -96.838104 47.314629 -96.84639 47.29391100000001 -96.837357 47.25686999999999 -96.849266 47.240486000000004 -96.836708 47.170094000000006 -96.826134 47.151917 -96.838806 47.092636 -96.818794 47.07886500000001 -96.826607 47.03396599999999 -96.82225 47.010265000000004 -96.834938 47.00347099999999 -96.824173 46.969814000000014 -96.816414 46.96967699999999 -96.793068 46.95587900000001 -96.801529 46.948238 -96.789352 46.93222 -96.787567 46.936297999999994 -96.762711 46.92281700000001 -96.756554 46.86738600000001 -96.777702 46.844898 -96.767891 46.81206900000001 -96.796837 46.762348 -96.780022 46.70708099999999 -96.781197 46.678841000000006 -96.793335 46.62980999999999 -96.789886 46.62414899999999 -96.783958 46.600021 -96.770683 46.58865700000001 -96.75087 46.48947100000001 -96.73996 46.46875800000001 -96.714539 46.42720800000001 -96.709328 46.41225800000001 -96.687874 46.359474000000006 -96.651749 46.350853 -96.61451 46.336365 -96.601723 46.23872399999999 -96.597832 46.215453999999994 -96.586105 46.191959 -96.58754 46.17721599999999 -96.570816 46.09557000000001 -96.551582 46.02132 -96.575867 45.947722999999996 -96.561455 45.934151000000014 -96.566574 45.936543 -97.232941 45.930859 -97.97834 45.931534 -98.014328 45.938297000000006 -98.730057</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>North Dakota</topp:STATE_NAME><topp:STATE_FIPS>38</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>ND</topp:STATE_ABBR><topp:LAND_KM>178695.213</topp:LAND_KM><topp:WATER_KM>4427.798</topp:WATER_KM><topp:PERSONS>638800.0</topp:PERSONS><topp:FAMILIES>166270.0</topp:FAMILIES><topp:HOUSHOLD>240878.0</topp:HOUSHOLD><topp:MALE>318201.0</topp:MALE><topp:FEMALE>320599.0</topp:FEMALE><topp:WORKERS>237047.0</topp:WORKERS><topp:DRVALONE>209546.0</topp:DRVALONE><topp:CARPOOL>31332.0</topp:CARPOOL><topp:PUBTRANS>1638.0</topp:PUBTRANS><topp:EMPLOYED>287558.0</topp:EMPLOYED><topp:UNEMPLOY>16083.0</topp:UNEMPLOY><topp:SERVICE>104938.0</topp:SERVICE><topp:MANUAL>33936.0</topp:MANUAL><topp:P_MALE>0.498</topp:P_MALE><topp:P_FEMALE>0.502</topp:P_FEMALE><topp:SAMP_POP>155209.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.28"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>42.48846399999999 -104.060547</gml:lowerCorner><gml:upperCorner>45.943561999999986 -96.439087</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>42.99532300000001 -102.787933 42.99937399999999 -103.005409 42.99865 -103.500984 43.003094000000004 -104.05571 43.47916000000001 -104.05867 43.503738 -104.057426 43.85292799999999 -104.05899 44.14584400000001 -104.059242 44.181842999999986 -104.060547 44.57436799999999 -104.058975 44.997349000000014 -104.059349 44.997817999999995 -104.04258 45.212886999999995 -104.043358 45.88305299999999 -104.049026 45.942993 -104.048416 45.941113 -102.994377 45.94166200000001 -102.945953 45.94250500000001 -102.00235 45.940392 -100.514 45.943561999999986 -99.875389 45.94278 -99.716949 45.939583 -99.006447 45.938297000000006 -98.730057 45.931534 -98.014328 45.930859 -97.97834 45.936543 -97.232941 45.934151000000014 -96.566574 45.817893999999995 -96.587608 45.80830399999999 -96.604263 45.73901000000001 -96.657043 45.65072599999999 -96.832443 45.609161 -96.854637 45.58412899999999 -96.842735 45.517517 -96.768898 45.45823300000001 -96.737686 45.410675 -96.692825 45.39656099999999 -96.604744 45.375168 -96.532211 45.32854499999999 -96.477257 45.29888500000001 -96.457268 45.27522999999999 -96.454163 44.97203099999999 -96.45575 44.80138400000001 -96.454887 44.62884500000001 -96.45639 44.53837999999999 -96.454781 44.199093000000005 -96.457077 43.84877 -96.456284 43.49974399999999 -96.460136 43.499874000000005 -96.597992 43.481944999999996 -96.583473 43.435562000000004 -96.588791 43.40074899999999 -96.557388 43.38424699999999 -96.524734 43.356987000000004 -96.522575 43.30767800000001 -96.540245 43.29009199999999 -96.578812 43.263630000000006 -96.570404 43.25328099999999 -96.55925 43.23965100000001 -96.566673 43.225505999999996 -96.558289 43.217926000000006 -96.486931 43.20909900000001 -96.472801 43.12632400000001 -96.451195 43.087886999999995 -96.460495 43.07559599999999 -96.461784 43.06189699999999 -96.479263 43.05152100000001 -96.519699 43.012061999999986 -96.498711 42.986469 -96.516838 42.952393 -96.514626 42.91387599999999 -96.543953 42.89691500000001 -96.537201 42.846667999999994 -96.555901 42.83435399999999 -96.572815 42.835387999999995 -96.587334 42.799564000000004 -96.600563 42.77683999999999 -96.632668 42.74860799999999 -96.640396 42.708359 -96.626228 42.668518000000006 -96.562729 42.66241099999999 -96.540855 42.629761 -96.512535 42.58048600000001 -96.48819 42.573891 -96.500633 42.56403399999999 -96.489029 42.517135999999994 -96.479935 42.48924600000001 -96.439087 42.48846399999999 -96.494392 42.52050399999999 -96.546906 42.518291000000005 -96.584442 42.507239999999996 -96.605156 42.522696999999994 -96.628983 42.550735 -96.63636 42.61230499999999 -96.713745 42.621910000000014 -96.714958 42.641166999999996 -96.694283 42.657718999999986 -96.698746 42.66859400000001 -96.722343 42.67002099999999 -96.799026 42.681343 -96.81012 42.70408599999999 -96.809822 42.73170099999999 -96.907913 42.721149 -96.970451 42.72730999999999 -96.977547 42.75206800000001 -96.969681 42.75831600000001 -96.979271 42.759544000000005 -97.014816 42.77392599999999 -97.130142 42.798621999999995 -97.161095 42.81257600000001 -97.211502 42.841206 -97.224113 42.85183000000001 -97.242859 42.850018000000006 -97.271126 42.861774 -97.311081 42.867435 -97.388969 42.85044500000001 -97.456924 42.857158999999996 -97.482819 42.860138000000006 -97.505791 42.847992000000005 -97.570312 42.86128600000001 -97.634628 42.836838 -97.68541 42.85800900000001 -97.724907 42.84616500000001 -97.771843 42.849597999999986 -97.796684 42.866589000000005 -97.818298 42.855807999999996 -97.888313 42.83127200000001 -97.889595 42.79232400000001 -97.929131 42.77368899999999 -97.963211 42.76681099999999 -97.994797 42.769192000000004 -98.032791 42.80836099999999 -98.121468 42.82022499999999 -98.122765 42.83579599999999 -98.144516 42.839573 -98.167473 42.881798 -98.309982 42.920139000000006 -98.390846 42.937163999999996 -98.457085 42.991783 -98.497292 42.99239 -99.253593 42.992332000000005 -99.532402 42.99108899999999 -100.197739 42.98683500000001 -101.231308 42.989891 -102.086273 42.99532300000001 -102.787933</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>South Dakota</topp:STATE_NAME><topp:STATE_FIPS>46</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>SD</topp:STATE_ABBR><topp:LAND_KM>196575.21</topp:LAND_KM><topp:WATER_KM>3169.429</topp:WATER_KM><topp:PERSONS>696004.0</topp:PERSONS><topp:FAMILIES>180306.0</topp:FAMILIES><topp:HOUSHOLD>259034.0</topp:HOUSHOLD><topp:MALE>342498.0</topp:MALE><topp:FEMALE>353506.0</topp:FEMALE><topp:WORKERS>250825.0</topp:WORKERS><topp:DRVALONE>233478.0</topp:DRVALONE><topp:CARPOOL>32610.0</topp:CARPOOL><topp:PUBTRANS>971.0</topp:PUBTRANS><topp:EMPLOYED>321891.0</topp:EMPLOYED><topp:UNEMPLOY>13983.0</topp:UNEMPLOY><topp:SERVICE>119594.0</topp:SERVICE><topp:MANUAL>41921.0</topp:MANUAL><topp:P_MALE>0.492</topp:P_MALE><topp:P_FEMALE>0.508</topp:P_FEMALE><topp:SAMP_POP>162746.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.29"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>40.994305 -111.05265</gml:lowerCorner><gml:upperCorner>45.00285299999999 -104.051201</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.69824600000001 -104.053108 41.564246999999995 -104.054993 41.38810699999999 -104.053505 41.00322700000001 -104.051201 40.994305 -104.933968 40.996365 -105.278259 41.000111000000004 -106.202896 41.001316 -106.328545 40.998489000000006 -106.864838 41.000168 -107.303436 41.00341 -107.918037 40.99847399999999 -109.047638 40.997646 -110.001457 40.99794 -110.062477 40.996635 -111.050285 41.25847999999999 -111.050911 41.57864799999999 -111.050323 41.996264999999994 -111.047951 42.503322999999995 -111.046028 43.01996199999999 -111.048447 43.284813000000014 -111.04673 43.51560599999999 -111.045998 43.982631999999995 -111.049629 44.47339600000001 -111.050789 44.66456199999999 -111.050842 44.995766 -111.05265 44.99234799999999 -110.428894 44.99868799999999 -110.392006 45.00285299999999 -109.994789 44.99958000000001 -109.798653 44.99764300000001 -108.624573 45.000159999999994 -108.258568 44.99981299999999 -107.893715 44.996173999999996 -106.258644 44.99722700000001 -106.020576 44.999832 -105.084465 45.001091 -105.04126 44.997349000000014 -104.059349 44.57436799999999 -104.058975 44.181842999999986 -104.060547 44.14584400000001 -104.059242 43.85292799999999 -104.05899 43.503738 -104.057426 43.47916000000001 -104.05867 43.003094000000004 -104.05571 42.61470399999999 -104.055725 41.99985100000001 -104.053009 41.69824600000001 -104.053108</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Wyoming</topp:STATE_NAME><topp:STATE_FIPS>56</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>WY</topp:STATE_ABBR><topp:LAND_KM>251500.801</topp:LAND_KM><topp:WATER_KM>1848.149</topp:WATER_KM><topp:PERSONS>453588.0</topp:PERSONS><topp:FAMILIES>119825.0</topp:FAMILIES><topp:HOUSHOLD>168839.0</topp:HOUSHOLD><topp:MALE>227007.0</topp:MALE><topp:FEMALE>226581.0</topp:FEMALE><topp:WORKERS>164561.0</topp:WORKERS><topp:DRVALONE>153679.0</topp:DRVALONE><topp:CARPOOL>28109.0</topp:CARPOOL><topp:PUBTRANS>2963.0</topp:PUBTRANS><topp:EMPLOYED>207868.0</topp:EMPLOYED><topp:UNEMPLOY>13112.0</topp:UNEMPLOY><topp:SERVICE>71419.0</topp:SERVICE><topp:MANUAL>29157.0</topp:MANUAL><topp:P_MALE>0.5</topp:P_MALE><topp:P_FEMALE>0.5</topp:P_FEMALE><topp:SAMP_POP>83202.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.30"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>42.48913200000001 -92.885185</gml:lowerCorner><gml:upperCorner>46.952549000000005 -86.967598</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>44.96162799999999 -87.748451 44.927334 -87.83989 44.873356 -87.830917 44.720482000000004 -87.985687 44.677273000000014 -87.983078 44.639126000000005 -88.013184 44.595821 -87.975655 44.622242 -88.012917 44.57145700000001 -88.040314 44.535503000000006 -87.966125 44.53914599999999 -87.926308 44.61691300000001 -87.868683 44.64405400000001 -87.76413 44.68929299999999 -87.723724 44.833054000000004 -87.614365 44.85134099999999 -87.552689 44.82302899999999 -87.551575 44.89110199999999 -87.433647 44.81156899999999 -87.367363 44.79472000000001 -87.314369 44.676918 -87.372978 44.53394700000001 -87.473442 44.327854 -87.537407 44.175758 -87.517242 44.097836 -87.644287 43.89390900000001 -87.726036 43.67318 -87.702644 43.546299000000005 -87.785957 43.458713999999986 -87.802872 43.358588999999995 -87.875244 43.19720799999999 -87.889748 43.075863 -87.859985 43.02576099999999 -87.891899 42.96457699999999 -87.836357 42.841544999999996 -87.819771 42.77752699999999 -87.756729 42.66662199999999 -87.791435 42.48913200000001 -87.79731 42.48961299999999 -88.194702 42.49197000000001 -88.297897 42.489655 -88.70652 42.490905999999995 -88.764954 42.49086399999999 -88.939079 42.497906 -89.359444 42.49749 -89.400497 42.50345999999999 -89.834618 42.504108 -89.923569 42.508362000000005 -90.419975 42.50936100000001 -90.638329 42.528560999999996 -90.62558 42.555713999999995 -90.639091 42.571392 -90.664253 42.637932000000006 -90.694664 42.657005 -90.745483 42.678246 -90.892418 42.68068299999999 -90.919281 42.707066 -90.999054 42.744923 -91.06604 42.78337500000001 -91.081902 42.871452000000005 -91.0933 42.925906999999995 -91.138992 43.00133099999999 -91.152084 43.081199999999995 -91.159622 43.08290500000001 -91.168442 43.147594 -91.161224 43.25791899999999 -91.068924 43.280703999999986 -91.066299 43.313320000000004 -91.078369 43.353970000000004 -91.176918 43.37053700000001 -91.198112 43.424076000000014 -91.210785 43.46471 -91.235771 43.500834999999995 -91.223434 43.54874000000001 -91.240425 43.598918999999995 -91.232857 43.67735300000001 -91.258255 43.72242700000001 -91.258781 43.78810899999999 -91.250969 43.84722500000001 -91.291809 43.947227 -91.373215 43.985657 -91.425758 44.03425200000001 -91.528275 44.03499199999999 -91.569016 44.04085900000001 -91.601639 44.066933000000006 -91.652084 44.13726399999999 -91.753067 44.19122300000001 -91.848587 44.25753 -91.888535 44.288376 -91.922188 44.317554 -91.922592 44.339146 -91.938705 44.364520999999996 -91.972221 44.41562300000001 -92.091164 44.43842699999999 -92.205963 44.456249000000014 -92.248924 44.49221399999999 -92.296509 44.540524000000005 -92.320297 44.55286799999999 -92.340691 44.57519099999999 -92.509026 44.61032499999999 -92.60878 44.642685 -92.630173 44.713627 -92.736946 44.746193000000005 -92.805382 44.835403000000014 -92.760826 44.862267 -92.764061 44.899529 -92.771667 44.91503499999999 -92.753723 44.935688 -92.749565 45.001037999999994 -92.766922 45.022152000000006 -92.762787 45.065642999999994 -92.796555 45.11303699999999 -92.745216 45.15645599999999 -92.744728 45.186645999999996 -92.762375 45.212410000000006 -92.755211 45.297638000000006 -92.746384 45.31823700000001 -92.707176 45.363113 -92.684662 45.39550399999999 -92.648544 45.439491000000004 -92.644768 45.45526100000001 -92.65461 45.47009299999999 -92.685211 45.54728299999999 -92.727943 45.56430399999999 -92.761963 45.56344200000001 -92.834824 45.57887600000001 -92.876617 45.64499699999999 -92.885185 45.71060600000001 -92.85981 45.73093399999999 -92.833427 45.763386 -92.7789 45.83734899999999 -92.748558 45.84502800000001 -92.733894 45.891006000000004 -92.706039 45.915752 -92.666008 45.95132100000001 -92.552475 45.98263499999999 -92.523781 45.981250999999986 -92.462151 46.02555799999999 -92.424805 46.01630399999999 -92.364769 46.022651999999994 -92.346031 46.05693400000001 -92.327179 46.07328799999999 -92.289177 46.15665799999999 -92.28875 46.416042000000004 -92.28849 46.65884399999999 -92.287079 46.646930999999995 -92.208961 46.74268699999999 -92.095779 46.68386100000001 -92.003967 46.680195 -91.921272 46.756924 -91.555588 46.952549000000005 -90.861542 46.92030299999999 -90.7743 46.88319000000001 -90.77726 46.58555999999999 -90.926056 46.645756000000006 -90.73053 46.587582 -90.540695 46.56866500000001 -90.40802 46.53971100000001 -90.385345 46.55161699999999 -90.31353 46.54435000000001 -90.302216 46.525104999999996 -90.300003 46.52253300000001 -90.269608 46.50884199999999 -90.258224 46.506348 -90.211349 46.442432 -90.161217 46.39395099999999 -90.141624 46.365207999999996 -90.115005 46.34048100000001 -90.111488 46.30407700000001 -89.924973 46.145691 -89.09967 46.100437 -88.985168 46.073646999999994 -88.925064 46.026848 -88.804268 46.036404000000005 -88.793686 46.032658 -88.777351 46.02119099999999 -88.772888 46.02962500000001 -88.72628 46.018967 -88.703476 46.02018699999999 -88.677254 45.99343099999999 -88.643539 45.99416400000001 -88.615372 46.015559999999994 -88.597404 46.00900300000001 -88.575226 46.01934399999999 -88.548225 46.018654 -88.51548 46.013003999999995 -88.49395 45.999194999999986 -88.483681 46.000805000000014 -88.454185 45.98346699999999 -88.403389 45.99463299999999 -88.369804 45.966758999999996 -88.32119 45.961989999999986 -88.299019 45.96710200000001 -88.257034 45.947948 -88.214859 45.953564 -88.180061 45.93634 -88.150307 45.926334 -88.111259 45.92066199999999 -88.093719 45.891850000000005 -88.095634 45.87368799999999 -88.065292 45.834923 -88.121658 45.819447 -88.129822 45.79157599999999 -88.088608 45.786156000000005 -88.051514 45.79508999999999 -87.989944 45.766491 -87.969055 45.75074000000001 -87.873505 45.72245799999999 -87.842239 45.71143000000001 -87.80143 45.701362999999986 -87.801033 45.68413899999999 -87.777351 45.67595299999999 -87.780823 45.66542799999999 -87.816933 45.654487999999986 -87.819817 45.613235 -87.775925 45.60042200000001 -87.774956 45.568554000000006 -87.786194 45.568625999999995 -87.828484 45.54455899999999 -87.805023 45.4991 -87.789268 45.46649199999999 -87.813499 45.44512900000001 -87.860153 45.406147000000004 -87.849419 45.365883 -87.883499 45.36211399999999 -87.873863 45.37210099999999 -87.868423 45.370193 -87.861984 45.346176000000014 -87.841171 45.358349000000004 -87.827896 45.352924 -87.759926 45.39129600000001 -87.689484 45.36188100000001 -87.64357 45.34819400000001 -87.645248 45.27222800000001 -87.704361 45.247108 -87.705032 45.236794 -87.719559 45.211693 -87.721519 45.199093000000005 -87.736092 45.176624000000004 -87.729561 45.140690000000006 -87.672707 45.10907 -87.66478 45.09465399999999 -87.581169 45.05681999999999 -87.618416 44.992008 -87.620232 44.96162799999999 -87.748451</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>45.29042100000001 -87.034409 45.29867200000001 -86.986137 45.24028799999999 -86.967598 45.218422000000004 -86.995621 45.249031 -87.044998 45.149981999999994 -87.025337 45.14731599999999 -87.079765 45.09551999999999 -87.044792 45.092186 -87.08757 45.053291 -87.083794 45.06477000000001 -87.11245 44.982811 -87.178589 44.93332699999999 -87.168686 44.87324100000001 -87.205551 44.798775000000006 -87.311028 44.83774600000001 -87.378639 44.911204999999995 -87.405319 45.015221 -87.342056 45.052628 -87.283379 45.17507599999999 -87.230804 45.15498400000001 -87.177803 45.29647800000001 -87.065948 45.29042100000001 -87.034409</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Wisconsin</topp:STATE_NAME><topp:STATE_FIPS>55</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>WI</topp:STATE_ABBR><topp:LAND_KM>139556.572</topp:LAND_KM><topp:WATER_KM>8223.295</topp:WATER_KM><topp:PERSONS>4796441.0</topp:PERSONS><topp:FAMILIES>1249223.0</topp:FAMILIES><topp:HOUSHOLD>1789141.0</topp:HOUSHOLD><topp:MALE>2345442.0</topp:MALE><topp:FEMALE>2450999.0</topp:FEMALE><topp:WORKERS>1763163.0</topp:WORKERS><topp:DRVALONE>1710612.0</topp:DRVALONE><topp:CARPOOL>264651.0</topp:CARPOOL><topp:PUBTRANS>57559.0</topp:PUBTRANS><topp:EMPLOYED>2335941.0</topp:EMPLOYED><topp:UNEMPLOY>129191.0</topp:UNEMPLOY><topp:SERVICE>688168.0</topp:SERVICE><topp:MANUAL>425748.0</topp:MANUAL><topp:P_MALE>0.489</topp:P_MALE><topp:P_FEMALE>0.511</topp:P_FEMALE><topp:SAMP_POP>1180518.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.31"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>41.994698 -117.23590899999999</gml:lowerCorner><gml:upperCorner>49.0 -111.045998</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>43.67914999999999 -117.025307 43.75381899999999 -117.02280400000001 43.80025900000001 -117.036125 43.831683999999996 -117.026634 43.83988600000001 -117.009514 43.853088000000014 -117.01522800000001 43.859466999999995 -116.984779 43.87358499999999 -116.977158 43.904556000000014 -116.97715 43.92869200000001 -116.958725 43.96331000000001 -116.966965 44.014317000000005 -116.932602 44.07400899999999 -116.975822 44.090411999999986 -116.962448 44.093140000000005 -116.945892 44.14642699999999 -116.90126000000001 44.17741799999999 -116.912056 44.197956000000005 -116.980873 44.225295999999986 -116.975128 44.24717699999999 -116.99170699999999 44.24945099999999 -117.02935 44.23167000000001 -117.05102500000001 44.24396100000001 -117.080383 44.26719299999999 -117.099556 44.26992000000001 -117.111687 44.25074799999999 -117.142273 44.25344799999999 -117.169716 44.28483600000001 -117.212563 44.300781 -117.21644599999999 44.33955399999999 -117.200592 44.39009899999999 -117.23590899999999 44.427971000000014 -117.21620899999999 44.47310300000001 -117.22339600000001 44.48590100000001 -117.20295 44.511921 -117.18637799999999 44.534771000000006 -117.14415 44.559402000000006 -117.14292900000001 44.57263900000001 -117.129494 44.689452999999986 -117.07834600000001 44.697674000000006 -117.065506 44.749233000000004 -117.038567 44.77615399999999 -116.950493 44.82906 -116.908623 44.848674999999986 -116.89636999999999 44.868729 -116.86608100000001 44.920265 -116.834404 44.954971 -116.846565 44.97275500000001 -116.83040600000001 44.97186300000001 -116.84716800000001 44.980087 -116.854897 45.00016400000001 -116.847107 45.017067 -116.853523 45.04987700000001 -116.80632 45.075874 -116.786224 45.099602000000004 -116.777107 45.10642200000001 -116.760284 45.137428 -116.735603 45.26247000000001 -116.687836 45.33552900000001 -116.671288 45.45998 -116.564796 45.493763 -116.553528 45.56617399999999 -116.477577 45.60637299999999 -116.46944400000001 45.66460799999999 -116.513939 45.710846000000004 -116.527298 45.74754300000001 -116.559654 45.78075000000001 -116.653419 45.819289999999995 -116.702202 45.819885 -116.772728 45.845989 -116.790283 45.90372099999999 -116.855492 45.980639999999994 -116.897217 45.995299999999986 -116.91815199999999 46.065811 -116.956741 46.078632 -116.98622900000001 46.097397 -116.960655 46.165604 -116.928444 46.197674000000006 -116.96650700000001 46.249427999999995 -116.97174100000001 46.30256700000001 -117.00065599999999 46.335544999999996 -117.026985 46.348816 -117.06319400000001 46.388690999999994 -117.04348 46.42809700000001 -117.037567 46.53671600000001 -117.040932 47.11942300000001 -117.039963 47.258601999999996 -117.041382 47.361541999999986 -117.040779 47.971191000000005 -117.036438 48.046284000000014 -117.037834 48.83815000000001 -117.028046 49.0 -117.030975 48.99999600000001 -116.0625 48.498726000000005 -116.055679 48.208549000000005 -116.054497 47.976269 -116.052483 47.965014999999994 -116.024307 47.925217 -115.997925 47.867199 -115.936836 47.841148000000004 -115.902916 47.827526000000006 -115.868805 47.805256000000014 -115.84832 47.75635500000001 -115.835739 47.744911 -115.789536 47.70980499999999 -115.774727 47.69562500000001 -115.732666 47.63995 -115.73307 47.616150000000005 -115.697289 47.59079 -115.691093 47.53376 -115.741837 47.520962 -115.700531 47.489609 -115.69178 47.475303999999994 -115.639153 47.457863 -115.642197 47.44924900000001 -115.655098 47.44537399999999 -115.731491 47.43403599999999 -115.749115 47.42254600000001 -115.749336 47.399238999999994 -115.665489 47.38011599999999 -115.637794 47.370075000000014 -115.598549 47.334686000000005 -115.554535 47.292057 -115.522079 47.28171900000001 -115.500946 47.27444800000001 -115.425659 47.263667999999996 -115.407227 47.25509600000001 -115.342682 47.24522400000001 -115.324249 47.17962600000001 -115.295258 47.12410399999999 -115.192101 47.09764899999999 -115.171524 47.09182000000001 -115.147713 47.06362899999999 -115.134537 47.02660399999999 -115.080368 46.97343799999999 -115.054672 46.96308099999999 -115.036369 46.958889 -115.00061 46.92529300000001 -114.963768 46.90724599999999 -114.923164 46.89096799999999 -114.939606 46.85252800000001 -114.947449 46.799515000000014 -114.901367 46.79712699999999 -114.865646 46.77562 -114.839836 46.76661300000001 -114.793076 46.755798 -114.776878 46.70312100000001 -114.781967 46.695212999999995 -114.747154 46.73384100000001 -114.697479 46.734802 -114.672935 46.687186999999994 -114.624977 46.67100099999999 -114.644089 46.66090399999999 -114.643791 46.62912800000001 -114.609879 46.637969999999996 -114.539444 46.62365299999999 -114.483772 46.645793999999995 -114.44059 46.661674000000005 -114.383072 46.654304999999994 -114.33374 46.622917 -114.323769 46.58795900000001 -114.342377 46.51746700000001 -114.349174 46.506203 -114.359528 46.487449999999995 -114.409775 46.428257 -114.383087 46.39962399999999 -114.39608 46.39299 -114.408859 46.28478999999999 -114.430862 46.25304 -114.472862 46.24386200000001 -114.471901 46.22033300000001 -114.438622 46.169048000000004 -114.439949 46.15534199999999 -114.466087 46.157497000000006 -114.508682 46.136143000000004 -114.518013 46.11622199999999 -114.505638 46.107437000000004 -114.47644 46.08231000000001 -114.455101 46.050895999999995 -114.464828 46.02349100000001 -114.493393 46.00984600000001 -114.473602 45.98988700000001 -114.483528 45.972054000000014 -114.411522 45.95406 -114.404366 45.921558000000005 -114.428535 45.910731999999996 -114.412605 45.870968000000005 -114.391914 45.84653499999999 -114.406601 45.85270299999999 -114.442307 45.83955 -114.472878 45.84276600000001 -114.498238 45.81014999999999 -114.516449 45.762482000000006 -114.562614 45.746082 -114.541031 45.72308000000001 -114.53405 45.694484999999986 -114.496635 45.67414099999999 -114.509781 45.652477000000005 -114.500816 45.62435500000001 -114.563751 45.59648100000001 -114.540031 45.548823999999996 -114.559998 45.558277000000004 -114.526466 45.54673399999999 -114.495667 45.547932 -114.461784 45.52771799999999 -114.432632 45.49909199999999 -114.418129 45.485825000000006 -114.370537 45.46346700000001 -114.349327 45.45750799999999 -114.325516 45.48594299999999 -114.261322 45.503029 -114.246964 45.53537399999999 -114.241081 45.52799999999999 -114.193893 45.544005999999996 -114.171753 45.550465 -114.131134 45.57120900000001 -114.117226 45.58645999999999 -114.082054 45.625225 -114.055603 45.64085399999999 -114.01712 45.652592 -114.010078 45.672459 -114.018967 45.68641299999999 -114.00856 45.69745599999999 -113.970238 45.67945900000001 -113.963234 45.671291 -113.925789 45.655204999999995 -113.922623 45.63733300000001 -113.901291 45.61357100000001 -113.902397 45.60964200000001 -113.85112 45.600716000000006 -113.821579 45.583809 -113.802849 45.51498799999999 -113.832809 45.51694499999999 -113.780029 45.50713300000001 -113.7714 45.477787000000006 -113.768265 45.44571300000001 -113.78476 45.410252000000014 -113.774124 45.382464999999996 -113.74041 45.321608999999995 -113.738182 45.27786599999999 -113.687813 45.26236 -113.689224 45.20686699999999 -113.644699 45.18106800000001 -113.600037 45.14981800000001 -113.593208 45.13462100000001 -113.570694 45.117785999999995 -113.573486 45.10762399999999 -113.551384 45.107910000000004 -113.509338 45.082137999999986 -113.519722 45.071293 -113.489273 45.05839499999999 -113.48542 45.04342299999999 -113.454552 45.027523 -113.45797 44.998267999999996 -113.440147 44.949596000000014 -113.447884 44.940848999999986 -113.462532 44.939648000000005 -113.486465 44.930744000000004 -113.495308 44.851315 -113.444695 44.833775 -113.420502 44.80764400000001 -113.349152 44.779076 -113.33976 44.780304 -113.31781 44.810562000000004 -113.256287 44.811916 -113.239471 44.761517 -113.137413 44.737457000000006 -113.126572 44.71525199999999 -113.100845 44.69599500000001 -113.098099 44.67560599999999 -113.072289 44.624371 -113.053436 44.582764 -113.082184 44.556377 -113.038811 44.53304299999999 -113.036972 44.510695999999996 -113.006866 44.48186100000001 -113.019463 44.45270199999999 -113.005814 44.43780100000001 -113.011169 44.43562700000001 -112.984406 44.407279999999986 -112.937439 44.39294100000001 -112.886467 44.370068 -112.869942 44.35372899999999 -112.843437 44.364292000000006 -112.816559 44.39490900000001 -112.817871 44.42117300000001 -112.825851 44.458099000000004 -112.795387 44.47400999999999 -112.779022 44.484409 -112.732872 44.49702500000001 -112.713486 44.48089200000001 -112.652351 44.477588999999995 -112.53849 44.463089 -112.501007 44.46892500000001 -112.457687 44.449375 -112.419922 44.44936000000001 -112.366753 44.49726899999999 -112.339745 44.525188000000014 -112.341675 44.54178999999999 -112.281509 44.560058999999995 -112.255844 44.55957799999999 -112.229568 44.53858199999999 -112.216934 44.53153599999999 -112.19883 44.52833899999999 -112.123367 44.518317999999994 -112.099075 44.52869799999999 -112.058548 44.52293 -112.02626 44.53513000000001 -112.022797 44.529762000000005 -111.977005 44.549812 -111.939575 44.55635100000001 -111.871696 44.504065999999995 -111.807037 44.51854700000001 -111.791809 44.51890900000001 -111.766121 44.53384399999999 -111.716202 44.550835000000006 -111.684067 44.543071999999995 -111.604454 44.55294799999999 -111.566437 44.52877799999999 -111.489449 44.536224000000004 -111.481781 44.538002000000006 -111.458534 44.55002200000001 -111.462036 44.55126999999999 -111.492111 44.593277 -111.513733 44.616051 -111.500954 44.637767999999994 -111.506897 44.64078900000001 -111.469376 44.652634000000006 -111.457474 44.67010099999999 -111.4599 44.691494000000006 -111.480011 44.70223999999999 -111.474632 44.713257 -111.442841 44.708946 -111.394295 44.73777000000001 -111.384171 44.74516299999999 -111.371521 44.72625400000001 -111.34919 44.72793999999999 -111.318436 44.70526899999999 -111.31469 44.683013999999986 -111.294884 44.673878 -111.269424 44.64228800000001 -111.269882 44.62698399999999 -111.22319 44.61805699999999 -111.219017 44.60263800000001 -111.233452 44.57324600000001 -111.218727 44.564926000000014 -111.177986 44.54526100000001 -111.169464 44.52797699999999 -111.133583 44.500832 -111.128143 44.486198 -111.093857 44.47339600000001 -111.050789 43.982631999999995 -111.049629 43.51560599999999 -111.045998 43.284813000000014 -111.04673 43.01996199999999 -111.048447 42.503322999999995 -111.046028 41.996264999999994 -111.047951 42.00023999999999 -111.49382 42.002376999999996 -112.099724 41.99913000000001 -112.146324 42.001228 -112.988754 41.995476 -114.038223 41.99601000000001 -114.268616 41.99659700000001 -115.023979 41.994698 -115.946617 41.99490399999999 -116.991356 41.99490399999999 -117.01790600000001 43.67914999999999 -117.025307</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Idaho</topp:STATE_NAME><topp:STATE_FIPS>16</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>ID</topp:STATE_ABBR><topp:LAND_KM>214324.978</topp:LAND_KM><topp:WATER_KM>2131.144</topp:WATER_KM><topp:PERSONS>1006749.0</topp:PERSONS><topp:FAMILIES>263194.0</topp:FAMILIES><topp:HOUSHOLD>360723.0</topp:HOUSHOLD><topp:MALE>500956.0</topp:MALE><topp:FEMALE>505793.0</topp:FEMALE><topp:WORKERS>359136.0</topp:WORKERS><topp:DRVALONE>329881.0</topp:DRVALONE><topp:CARPOOL>52958.0</topp:CARPOOL><topp:PUBTRANS>8572.0</topp:PUBTRANS><topp:EMPLOYED>443703.0</topp:EMPLOYED><topp:UNEMPLOY>29070.0</topp:UNEMPLOY><topp:SERVICE>144809.0</topp:SERVICE><topp:MANUAL>68391.0</topp:MANUAL><topp:P_MALE>0.498</topp:P_MALE><topp:P_FEMALE>0.502</topp:P_FEMALE><topp:SAMP_POP>201595.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.32"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>42.725769000000014 -73.436432</gml:lowerCorner><gml:upperCorner>45.013306 -71.505844</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>42.745979000000005 -73.258507 42.747401999999994 -73.269722 42.803471 -73.296616 42.83702500000001 -73.280029 42.94021599999999 -73.276451 43.310779999999994 -73.250519 43.512764000000004 -73.238838 43.559315 -73.260429 43.57496599999999 -73.291847 43.593121 -73.282181 43.619586999999996 -73.294548 43.624649000000005 -73.303978 43.61493300000001 -73.364128 43.56907699999999 -73.388557 43.582413 -73.418762 43.63204999999999 -73.423401 43.71421799999999 -73.371429 43.756496 -73.357109 43.778366000000005 -73.359436 43.80444700000001 -73.385178 43.885918000000004 -73.375557 43.914749 -73.405769 43.98813999999999 -73.417839 44.01816600000001 -73.408684 44.045624000000004 -73.436432 44.06384299999999 -73.435646 44.10655600000001 -73.409187 44.136173000000014 -73.408295 44.172054 -73.382492 44.20119500000001 -73.377762 44.26008999999999 -73.305756 44.36734000000001 -73.330215 44.405483000000004 -73.300423 44.432804000000004 -73.293747 44.54428100000001 -73.334877 44.55392499999999 -73.348236 44.57912099999999 -73.371719 44.61976200000001 -73.382248 44.634303999999986 -73.37056 44.66123200000001 -73.37352 44.68032500000001 -73.358574 44.72419400000001 -73.373581 44.799251999999996 -73.327209 44.81907699999999 -73.369476 44.847893 -73.382729 44.93256400000001 -73.336838 44.981933999999995 -73.351181 45.0061 -73.345146 45.00844599999999 -73.188972 45.005325 -72.547668 45.007294 -71.902328 45.013306 -71.505844 44.976517 -71.541397 44.94364899999999 -71.517448 44.89962399999999 -71.506836 44.81597099999999 -71.575569 44.77914799999999 -71.583969 44.74166099999999 -71.631599 44.67781099999999 -71.608147 44.65054699999999 -71.589218 44.637394 -71.568497 44.59653499999999 -71.554573 44.57887600000001 -71.537262 44.551146999999986 -71.592758 44.53881799999999 -71.591911 44.52574899999999 -71.575714 44.49448000000001 -71.58709 44.47444899999999 -71.614693 44.476673000000005 -71.637024 44.469116000000014 -71.648178 44.440079 -71.656868 44.421284000000014 -71.677353 44.39819 -71.767036 44.38411300000001 -71.798195 44.350300000000004 -71.821663 44.34413900000001 -71.835281 44.336051999999995 -71.928825 44.325726 -71.939369 44.32748799999999 -71.994896 44.299374 -72.035957 44.26143300000001 -72.060028 44.234318 -72.044853 44.18211400000001 -72.059746 44.156372000000005 -72.045189 44.12068199999999 -72.035385 44.10038800000001 -72.04998 44.096035 -72.032913 44.083309000000014 -72.035194 44.03197499999999 -72.077385 44.008858000000004 -72.08567 43.98916199999999 -72.110374 43.976448000000005 -72.113274 43.95792399999999 -72.092178 43.939098 -72.11367 43.90914900000001 -72.122116 43.878849 -72.170555 43.801620000000014 -72.185303 43.764565000000005 -72.206558 43.75062199999999 -72.219589 43.735229000000004 -72.260521 43.69846000000001 -72.304504 43.597294000000005 -72.333549 43.572304 -72.373962 43.517483 -72.395462 43.48455799999999 -72.38298 43.41008400000001 -72.396713 43.37705199999999 -72.412605 43.350933 -72.398094 43.32333 -72.410698 43.307308000000006 -72.402885 43.232178000000005 -72.436066 43.155945 -72.452866 43.11619200000001 -72.438072 43.078959999999995 -72.443932 43.046425 -72.462219 42.99952300000001 -72.457626 42.97606300000001 -72.473808 42.96550400000001 -72.50473 42.95159100000001 -72.520683 42.912532999999996 -72.525276 42.86056099999999 -72.553894 42.80765199999999 -72.539383 42.789176999999995 -72.513535 42.76865000000001 -72.507736 42.761505 -72.47979 42.746758 -72.462639 42.725769000000014 -72.456238 42.73728199999999 -72.923454 42.74031400000001 -73.020149 42.745979000000005 -73.258507</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Vermont</topp:STATE_NAME><topp:STATE_FIPS>50</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>VT</topp:STATE_ABBR><topp:LAND_KM>23955.748</topp:LAND_KM><topp:WATER_KM>947.04</topp:WATER_KM><topp:PERSONS>562758.0</topp:PERSONS><topp:FAMILIES>144895.0</topp:FAMILIES><topp:HOUSHOLD>210650.0</topp:HOUSHOLD><topp:MALE>275492.0</topp:MALE><topp:FEMALE>287266.0</topp:FEMALE><topp:WORKERS>209604.0</topp:WORKERS><topp:DRVALONE>199808.0</topp:DRVALONE><topp:CARPOOL>35731.0</topp:CARPOOL><topp:PUBTRANS>2033.0</topp:PUBTRANS><topp:EMPLOYED>283146.0</topp:EMPLOYED><topp:UNEMPLOY>17600.0</topp:UNEMPLOY><topp:SERVICE>84759.0</topp:SERVICE><topp:MANUAL>38046.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>146370.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.33"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>43.49814599999999 -97.229095</gml:lowerCorner><gml:upperCorner>49.371735 -89.530586</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>43.49960300000001 -91.730217 43.499187000000006 -92.07737 43.49949599999999 -92.452995 43.500294 -92.557831 43.501316 -93.027016 43.501495000000006 -93.054184 43.50053399999999 -93.500618 43.500809000000004 -93.653481 43.50034299999999 -93.973717 43.49899300000001 -94.246544 43.49814599999999 -94.454987 43.50007199999999 -94.859573 43.499413000000004 -94.920197 43.500370000000004 -95.396278 43.49957699999999 -95.464493 43.498977999999994 -95.866615 43.49856600000001 -96.060738 43.49974399999999 -96.460136 43.84877 -96.456284 44.199093000000005 -96.457077 44.53837999999999 -96.454781 44.62884500000001 -96.45639 44.80138400000001 -96.454887 44.97203099999999 -96.45575 45.27522999999999 -96.454163 45.29888500000001 -96.457268 45.32854499999999 -96.477257 45.375168 -96.532211 45.39656099999999 -96.604744 45.410675 -96.692825 45.45823300000001 -96.737686 45.517517 -96.768898 45.58412899999999 -96.842735 45.609161 -96.854637 45.65072599999999 -96.832443 45.73901000000001 -96.657043 45.80830399999999 -96.604263 45.817893999999995 -96.587608 45.934151000000014 -96.566574 45.947722999999996 -96.561455 46.02132 -96.575867 46.09557000000001 -96.551582 46.17721599999999 -96.570816 46.191959 -96.58754 46.215453999999994 -96.586105 46.23872399999999 -96.597832 46.336365 -96.601723 46.350853 -96.61451 46.359474000000006 -96.651749 46.41225800000001 -96.687874 46.42720800000001 -96.709328 46.46875800000001 -96.714539 46.48947100000001 -96.73996 46.58865700000001 -96.75087 46.600021 -96.770683 46.62414899999999 -96.783958 46.62980999999999 -96.789886 46.678841000000006 -96.793335 46.70708099999999 -96.781197 46.762348 -96.780022 46.81206900000001 -96.796837 46.844898 -96.767891 46.86738600000001 -96.777702 46.92281700000001 -96.756554 46.936297999999994 -96.762711 46.93222 -96.787567 46.948238 -96.789352 46.95587900000001 -96.801529 46.96967699999999 -96.793068 46.969814000000014 -96.816414 47.00347099999999 -96.824173 47.010265000000004 -96.834938 47.03396599999999 -96.82225 47.07886500000001 -96.826607 47.092636 -96.818794 47.151917 -96.838806 47.170094000000006 -96.826134 47.240486000000004 -96.836708 47.25686999999999 -96.849266 47.29391100000001 -96.837357 47.314629 -96.84639 47.34227000000001 -96.838104 47.36098100000001 -96.850273 47.38414399999999 -96.83947 47.408962 -96.849648 47.41311300000001 -96.86689 47.436779 -96.855469 47.46156300000001 -96.866325 47.50064499999999 -96.851257 47.52138099999999 -96.860329 47.54459399999999 -96.848831 47.56300400000001 -96.858307 47.60117700000001 -96.85186 47.61528000000001 -96.872978 47.67394999999999 -96.889069 47.71411900000001 -96.923302 47.763531 -96.931656 47.794464000000005 -96.957474 47.809685 -96.983536 47.82805300000001 -96.976875 47.87021999999999 -96.999985 47.875590999999986 -97.02021 47.917912 -97.014977 47.95494500000001 -97.047699 48.048182999999995 -97.066719 48.07036199999999 -97.092369 48.100989999999996 -97.098679 48.116386000000006 -97.121521 48.142791999999986 -97.120567 48.148415 -97.136162 48.15924100000001 -97.115715 48.16778600000001 -97.137093 48.175242999999995 -97.135941 48.19507999999999 -97.136925 48.20375799999999 -97.130478 48.20762300000001 -97.11055 48.22177099999999 -97.139404 48.22806499999999 -97.108887 48.233540000000005 -97.127205 48.25918999999999 -97.123436 48.2645 -97.136307 48.277893000000006 -97.111366 48.28616299999999 -97.112335 48.29305600000001 -97.130165 48.29489899999999 -97.113373 48.303635000000014 -97.114403 48.310986000000014 -97.132286 48.319942 -97.112244 48.326008 -97.136787 48.36150699999999 -97.130775 48.36323200000001 -97.150047 48.37247099999999 -97.133438 48.38442599999999 -97.134857 48.38822200000001 -97.15847 48.40790200000001 -97.128777 48.41000700000001 -97.149475 48.41962799999999 -97.151299 48.41612599999999 -97.122253 48.437118999999996 -97.119286 48.43812600000001 -97.143265 48.51732999999999 -97.134247 48.517967 -97.14798 48.53466399999999 -97.139038 48.53841399999999 -97.15519 48.545094000000006 -97.160088 48.549553 -97.146271 48.56227899999999 -97.167595 48.57287199999999 -97.151779 48.58365599999999 -97.157845 48.58692199999999 -97.140465 48.61403999999999 -97.144371 48.62078500000001 -97.122612 48.62980999999999 -97.127098 48.629963000000004 -97.107285 48.674544999999995 -97.096825 48.69525899999999 -97.116394 48.70859899999999 -97.109756 48.72625400000001 -97.13446 48.74723399999999 -97.132156 48.75566900000001 -97.147552 48.76355699999999 -97.138901 48.781184999999994 -97.147171 48.80152899999999 -97.173599 48.810383 -97.164368 48.815552 -97.180077 48.835995 -97.17086 48.873772 -97.175385 48.931842999999986 -97.216026 49.0 -97.229095 49.0 -96.406616 49.0 -95.276421 49.00000399999999 -95.157516 49.371735 -95.151627 49.33081100000001 -94.83181 48.87717799999999 -94.681038 48.777637 -94.694221 48.71370300000001 -94.570107 48.710815 -94.430435 48.70774499999999 -94.292145 48.652023000000014 -94.230637 48.62478300000001 -93.843735 48.525454999999994 -93.812515 48.511639 -93.780937 48.53432799999999 -93.513985 48.54957999999999 -93.465187 48.59277 -93.457619 48.63722999999999 -93.304092 48.62666300000001 -93.091301 48.62844100000001 -92.946785 48.54030599999999 -92.728851 48.54044300000001 -92.64167 48.502917999999994 -92.626228 48.494815999999986 -92.698669 48.460464 -92.70649 48.440166000000005 -92.497375 48.402264 -92.456192 48.357594000000006 -92.473167 48.22087500000001 -92.369965 48.244438 -92.276772 48.29840899999999 -92.300125 48.352417 -92.275986 48.36685600000001 -92.125824 48.35561000000001 -92.035049 48.25049999999999 -91.979401 48.206249000000014 -91.788689 48.19687999999999 -91.711815 48.11494099999999 -91.703606 48.10456500000001 -91.568657 48.043678 -91.571442 48.08140599999999 -91.239334 48.195449999999994 -91.027039 48.254311 -90.864388 48.08855399999999 -90.743256 48.12181100000001 -90.567352 48.092861 -90.556732 48.112885000000006 -90.14518 48.08619300000001 -90.026611 48.02367000000001 -89.986931 47.99261899999999 -89.900299 48.0266 -89.749222 48.00176999999999 -89.530586 47.99267599999999 -89.625557 47.95950300000001 -89.636284 47.82467299999999 -89.99958 47.71004500000001 -90.509514 47.461151 -91.021317 47.12500800000001 -91.468483 46.92715100000001 -91.800789 46.791957999999994 -92.088303 46.668262 -92.214432 46.66663399999999 -92.302956 46.65884399999999 -92.287079 46.416042000000004 -92.28849 46.15665799999999 -92.28875 46.07328799999999 -92.289177 46.05693400000001 -92.327179 46.022651999999994 -92.346031 46.01630399999999 -92.364769 46.02555799999999 -92.424805 45.981250999999986 -92.462151 45.98263499999999 -92.523781 45.95132100000001 -92.552475 45.915752 -92.666008 45.891006000000004 -92.706039 45.84502800000001 -92.733894 45.83734899999999 -92.748558 45.763386 -92.7789 45.73093399999999 -92.833427 45.71060600000001 -92.85981 45.64499699999999 -92.885185 45.57887600000001 -92.876617 45.56344200000001 -92.834824 45.56430399999999 -92.761963 45.54728299999999 -92.727943 45.47009299999999 -92.685211 45.45526100000001 -92.65461 45.439491000000004 -92.644768 45.39550399999999 -92.648544 45.363113 -92.684662 45.31823700000001 -92.707176 45.297638000000006 -92.746384 45.212410000000006 -92.755211 45.186645999999996 -92.762375 45.15645599999999 -92.744728 45.11303699999999 -92.745216 45.065642999999994 -92.796555 45.022152000000006 -92.762787 45.001037999999994 -92.766922 44.935688 -92.749565 44.91503499999999 -92.753723 44.899529 -92.771667 44.862267 -92.764061 44.835403000000014 -92.760826 44.746193000000005 -92.805382 44.713627 -92.736946 44.642685 -92.630173 44.61032499999999 -92.60878 44.57519099999999 -92.509026 44.55286799999999 -92.340691 44.540524000000005 -92.320297 44.49221399999999 -92.296509 44.456249000000014 -92.248924 44.43842699999999 -92.205963 44.41562300000001 -92.091164 44.364520999999996 -91.972221 44.339146 -91.938705 44.317554 -91.922592 44.288376 -91.922188 44.25753 -91.888535 44.19122300000001 -91.848587 44.13726399999999 -91.753067 44.066933000000006 -91.652084 44.04085900000001 -91.601639 44.03499199999999 -91.569016 44.03425200000001 -91.528275 43.985657 -91.425758 43.947227 -91.373215 43.84722500000001 -91.291809 43.78810899999999 -91.250969 43.72242700000001 -91.258781 43.67735300000001 -91.258255 43.598918999999995 -91.232857 43.54874000000001 -91.240425 43.500834999999995 -91.223434 43.50065599999999 -91.610954 43.49960300000001 -91.730217</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Minnesota</topp:STATE_NAME><topp:STATE_FIPS>27</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>MN</topp:STATE_ABBR><topp:LAND_KM>206207.099</topp:LAND_KM><topp:WATER_KM>12845.411</topp:WATER_KM><topp:PERSONS>4375099.0</topp:PERSONS><topp:FAMILIES>1130683.0</topp:FAMILIES><topp:HOUSHOLD>1647853.0</topp:HOUSHOLD><topp:MALE>2145183.0</topp:MALE><topp:FEMALE>2229916.0</topp:FEMALE><topp:WORKERS>1605702.0</topp:WORKERS><topp:DRVALONE>1593019.0</topp:DRVALONE><topp:CARPOOL>246546.0</topp:CARPOOL><topp:PUBTRANS>77722.0</topp:PUBTRANS><topp:EMPLOYED>2192417.0</topp:EMPLOYED><topp:UNEMPLOY>118919.0</topp:UNEMPLOY><topp:SERVICE>606584.0</topp:SERVICE><topp:MANUAL>303141.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>1043304.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.34"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>41.98778899999999 -124.55839499999999</gml:lowerCorner><gml:upperCorner>46.23625899999999 -116.46944400000001</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.99445700000001 -121.440407 42.0009 -122.28357700000001 42.00234599999999 -123.22094 41.99799300000001 -123.512032 41.99311399999999 -123.81796299999999 41.997814000000005 -124.205246 42.09884299999999 -124.351036 42.246059 -124.41384099999999 42.42977500000001 -124.4366 42.553196000000014 -124.390549 42.62286800000001 -124.39986400000001 42.832626000000005 -124.55839499999999 42.955624 -124.484123 43.26175699999999 -124.38554400000001 43.300365 -124.40484599999999 43.459270000000004 -124.27276599999999 43.605166999999994 -124.224777 43.85727700000001 -124.157097 44.269672000000014 -124.117088 44.66229999999999 -124.053177 44.81490299999999 -124.074341 45.03627399999999 -124.006348 45.293139999999994 -123.955376 45.48526000000001 -123.97931700000001 45.508140999999995 -123.93543199999999 45.47422399999999 -123.89086900000001 45.499256 -123.858269 45.568703 -123.95217099999999 45.703007000000014 -123.93483 45.775654 -123.97538 45.871212000000014 -123.955025 45.942093 -123.995255 46.012493000000006 -123.919937 46.20288099999999 -123.97608199999999 46.11161799999999 -123.792847 46.1446 -123.775833 46.193821000000014 -123.819725 46.210109999999986 -123.76016200000001 46.170063 -123.715912 46.174667 -123.668999 46.23625899999999 -123.515785 46.14432099999999 -123.36232 46.144904999999994 -123.303482 46.144188000000014 -123.247566 46.170174 -123.211205 46.18375399999999 -123.174965 46.17947799999999 -123.117325 46.155902999999995 -123.04937000000001 46.110648999999995 -122.97294600000001 46.07949400000001 -122.898537 46.02734799999999 -122.874199 45.944053999999994 -122.80652599999999 45.904236 -122.805008 45.86805000000001 -122.78286 45.85061300000001 -122.78330199999999 45.80050700000001 -122.786797 45.76073099999999 -122.76307700000001 45.727847999999994 -122.771339 45.64955900000001 -122.759331 45.63120699999999 -122.69511399999999 45.606990999999994 -122.650002 45.594978 -122.564224 45.56493800000001 -122.435951 45.566329999999994 -122.355255 45.543251 -122.30194900000001 45.548271 -122.243721 45.590664000000004 -122.08084099999999 45.617985000000004 -121.998817 45.63593700000001 -121.97146599999999 45.642189 -121.925629 45.677017000000006 -121.887093 45.70084399999999 -121.809853 45.689876999999996 -121.757507 45.688953 -121.705231 45.719727000000006 -121.52787000000001 45.69512599999999 -121.441368 45.69076200000001 -121.420845 45.69984400000001 -121.366631 45.696799999999996 -121.318794 45.67849699999999 -121.27520799999999 45.66580200000001 -121.213089 45.657444 -121.20212599999999 45.61340000000001 -121.190872 45.600674 -121.173134 45.607215999999994 -121.124023 45.64676700000001 -121.07235 45.65299999999999 -121.032303 45.645309 -120.9673 45.65047100000001 -120.947395 45.63563199999999 -120.906761 45.665339999999986 -120.86024499999999 45.71066300000001 -120.695824 45.732765 -120.657234 45.743763 -120.622589 45.74107000000001 -120.568916 45.69578200000001 -120.49799300000001 45.68942999999999 -120.442223 45.71673200000001 -120.282478 45.719933 -120.206291 45.76141000000001 -120.154755 45.78035 -120.067497 45.81128699999999 -119.993172 45.83184399999999 -119.86859100000001 45.841755000000006 -119.832413 45.85268400000001 -119.677307 45.89955499999999 -119.620979 45.913459999999986 -119.58815799999999 45.89934500000001 -119.511086 45.914412999999996 -119.43772899999999 45.917755 -119.378311 45.932807999999994 -119.301636 45.92249699999999 -119.17761999999999 45.92585399999999 -119.13913 45.966419 -119.031105 45.999202999999994 -118.981018 46.001778 -117.991478 46.00001900000001 -117.981628 46.00040100000001 -117.60180700000001 45.999966 -117.48065199999999 45.995299999999986 -116.91815199999999 45.980639999999994 -116.897217 45.90372099999999 -116.855492 45.845989 -116.790283 45.819885 -116.772728 45.819289999999995 -116.702202 45.78075000000001 -116.653419 45.74754300000001 -116.559654 45.710846000000004 -116.527298 45.66460799999999 -116.513939 45.60637299999999 -116.46944400000001 45.56617399999999 -116.477577 45.493763 -116.553528 45.45998 -116.564796 45.33552900000001 -116.671288 45.26247000000001 -116.687836 45.137428 -116.735603 45.10642200000001 -116.760284 45.099602000000004 -116.777107 45.075874 -116.786224 45.04987700000001 -116.80632 45.017067 -116.853523 45.00016400000001 -116.847107 44.980087 -116.854897 44.97186300000001 -116.84716800000001 44.97275500000001 -116.83040600000001 44.954971 -116.846565 44.920265 -116.834404 44.868729 -116.86608100000001 44.848674999999986 -116.89636999999999 44.82906 -116.908623 44.77615399999999 -116.950493 44.749233000000004 -117.038567 44.697674000000006 -117.065506 44.689452999999986 -117.07834600000001 44.57263900000001 -117.129494 44.559402000000006 -117.14292900000001 44.534771000000006 -117.14415 44.511921 -117.18637799999999 44.48590100000001 -117.20295 44.47310300000001 -117.22339600000001 44.427971000000014 -117.21620899999999 44.39009899999999 -117.23590899999999 44.33955399999999 -117.200592 44.300781 -117.21644599999999 44.28483600000001 -117.212563 44.25344799999999 -117.169716 44.25074799999999 -117.142273 44.26992000000001 -117.111687 44.26719299999999 -117.099556 44.24396100000001 -117.080383 44.23167000000001 -117.05102500000001 44.24945099999999 -117.02935 44.24717699999999 -116.99170699999999 44.225295999999986 -116.975128 44.197956000000005 -116.980873 44.17741799999999 -116.912056 44.14642699999999 -116.90126000000001 44.093140000000005 -116.945892 44.090411999999986 -116.962448 44.07400899999999 -116.975822 44.014317000000005 -116.932602 43.96331000000001 -116.966965 43.92869200000001 -116.958725 43.904556000000014 -116.97715 43.87358499999999 -116.977158 43.859466999999995 -116.984779 43.853088000000014 -117.01522800000001 43.83988600000001 -117.009514 43.831683999999996 -117.026634 43.80025900000001 -117.036125 43.75381899999999 -117.02280400000001 43.67914999999999 -117.025307 41.99490399999999 -117.01790600000001 41.99674200000001 -118.184326 41.98924299999999 -119.309906 41.98895999999999 -119.350655 41.989315000000005 -119.992401 41.98778899999999 -120.87082699999999 41.99445700000001 -121.440407</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Oregon</topp:STATE_NAME><topp:STATE_FIPS>41</topp:STATE_FIPS><topp:SUB_REGION>Pacific</topp:SUB_REGION><topp:STATE_ABBR>OR</topp:STATE_ABBR><topp:LAND_KM>248646.672</topp:LAND_KM><topp:WATER_KM>6172.331</topp:WATER_KM><topp:PERSONS>2842321.0</topp:PERSONS><topp:FAMILIES>750844.0</topp:FAMILIES><topp:HOUSHOLD>1103313.0</topp:HOUSHOLD><topp:MALE>1397073.0</topp:MALE><topp:FEMALE>1445248.0</topp:FEMALE><topp:WORKERS>1063797.0</topp:WORKERS><topp:DRVALONE>949326.0</topp:DRVALONE><topp:CARPOOL>165256.0</topp:CARPOOL><topp:PUBTRANS>43435.0</topp:PUBTRANS><topp:EMPLOYED>1319960.0</topp:EMPLOYED><topp:UNEMPLOY>87183.0</topp:UNEMPLOY><topp:SERVICE>380532.0</topp:SERVICE><topp:MANUAL>206287.0</topp:MANUAL><topp:P_MALE>0.492</topp:P_MALE><topp:P_FEMALE>0.508</topp:P_FEMALE><topp:SAMP_POP>452821.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.35"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>42.69851299999999 -72.553894</gml:lowerCorner><gml:upperCorner>45.301422 -70.734642</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>42.720383 -72.280388 42.725769000000014 -72.456238 42.746758 -72.462639 42.761505 -72.47979 42.76865000000001 -72.507736 42.789176999999995 -72.513535 42.80765199999999 -72.539383 42.86056099999999 -72.553894 42.912532999999996 -72.525276 42.95159100000001 -72.520683 42.96550400000001 -72.50473 42.97606300000001 -72.473808 42.99952300000001 -72.457626 43.046425 -72.462219 43.078959999999995 -72.443932 43.11619200000001 -72.438072 43.155945 -72.452866 43.232178000000005 -72.436066 43.307308000000006 -72.402885 43.32333 -72.410698 43.350933 -72.398094 43.37705199999999 -72.412605 43.41008400000001 -72.396713 43.48455799999999 -72.38298 43.517483 -72.395462 43.572304 -72.373962 43.597294000000005 -72.333549 43.69846000000001 -72.304504 43.735229000000004 -72.260521 43.75062199999999 -72.219589 43.764565000000005 -72.206558 43.801620000000014 -72.185303 43.878849 -72.170555 43.90914900000001 -72.122116 43.939098 -72.11367 43.95792399999999 -72.092178 43.976448000000005 -72.113274 43.98916199999999 -72.110374 44.008858000000004 -72.08567 44.03197499999999 -72.077385 44.083309000000014 -72.035194 44.096035 -72.032913 44.10038800000001 -72.04998 44.12068199999999 -72.035385 44.156372000000005 -72.045189 44.18211400000001 -72.059746 44.234318 -72.044853 44.26143300000001 -72.060028 44.299374 -72.035957 44.32748799999999 -71.994896 44.325726 -71.939369 44.336051999999995 -71.928825 44.34413900000001 -71.835281 44.350300000000004 -71.821663 44.38411300000001 -71.798195 44.39819 -71.767036 44.421284000000014 -71.677353 44.440079 -71.656868 44.469116000000014 -71.648178 44.476673000000005 -71.637024 44.47444899999999 -71.614693 44.49448000000001 -71.58709 44.52574899999999 -71.575714 44.53881799999999 -71.591911 44.551146999999986 -71.592758 44.57887600000001 -71.537262 44.59653499999999 -71.554573 44.637394 -71.568497 44.65054699999999 -71.589218 44.67781099999999 -71.608147 44.74166099999999 -71.631599 44.77914799999999 -71.583969 44.81597099999999 -71.575569 44.89962399999999 -71.506836 44.94364899999999 -71.517448 44.976517 -71.541397 45.013306 -71.505844 45.059844999999996 -71.503471 45.11694700000001 -71.430885 45.202758999999986 -71.403 45.23603800000001 -71.447037 45.23488599999999 -71.386856 45.29344900000001 -71.297714 45.237922999999995 -71.153572 45.301422 -71.08799 44.66848400000001 -71.029205 44.282082 -71.009079 43.791088 -70.984932 43.571751000000006 -70.974365 43.564064 -70.957016 43.54887400000001 -70.950111 43.53191000000001 -70.96476 43.51630800000001 -70.95977 43.47013100000001 -70.971283 43.438046000000014 -70.961975 43.39610300000001 -70.979591 43.366299 -70.970192 43.301987 -70.906296 43.280936999999994 -70.901581 43.235138000000006 -70.813705 43.159088 -70.831047 43.12178399999999 -70.819168 43.127621000000005 -70.885246 43.10144 -70.87513 43.083935 -70.905914 43.058796 -70.886993 43.089653 -70.810928 43.058673999999996 -70.734642 42.866974 -70.814384 42.863338 -70.850243 42.886787 -70.898613 42.88505900000001 -70.921837 42.85108199999999 -71.025925 42.80422999999999 -71.066063 42.81819200000001 -71.121101 42.80722800000001 -71.181557 42.73867000000001 -71.186844 42.743464999999986 -71.240974 42.725978999999995 -71.252907 42.69851299999999 -71.287689 42.705291999999986 -71.901421 42.707122999999996 -71.930695 42.720383 -72.280388</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>New Hampshire</topp:STATE_NAME><topp:STATE_FIPS>33</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>NH</topp:STATE_ABBR><topp:LAND_KM>23230.674</topp:LAND_KM><topp:WATER_KM>875.265</topp:WATER_KM><topp:PERSONS>1109252.0</topp:PERSONS><topp:FAMILIES>292601.0</topp:FAMILIES><topp:HOUSHOLD>411186.0</topp:HOUSHOLD><topp:MALE>543544.0</topp:MALE><topp:FEMALE>565708.0</topp:FEMALE><topp:WORKERS>415538.0</topp:WORKERS><topp:DRVALONE>443317.0</topp:DRVALONE><topp:CARPOOL>69904.0</topp:CARPOOL><topp:PUBTRANS>4067.0</topp:PUBTRANS><topp:EMPLOYED>574237.0</topp:EMPLOYED><topp:UNEMPLOY>38108.0</topp:UNEMPLOY><topp:SERVICE>148720.0</topp:SERVICE><topp:MANUAL>78699.0</topp:MANUAL><topp:P_MALE>0.49</topp:P_MALE><topp:P_FEMALE>0.51</topp:P_FEMALE><topp:SAMP_POP>193338.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.36"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>40.371902000000006 -96.640396</gml:lowerCorner><gml:upperCorner>43.501495000000006 -90.14267</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>40.70540199999999 -91.119987 40.68214800000001 -91.129158 40.65631099999999 -91.162498 40.64381800000001 -91.214912 40.639545 -91.262062 40.60343900000001 -91.37561 40.572970999999995 -91.411118 40.54799299999999 -91.412872 40.52849599999999 -91.382103 40.50365400000001 -91.374794 40.44725 -91.385399 40.40298799999999 -91.372757 40.392360999999994 -91.385757 40.386875 -91.418816 40.371902000000006 -91.448593 40.39096799999999 -91.476883 40.390761999999995 -91.490158 40.40511699999999 -91.500221 40.41012599999999 -91.527534 40.43504300000001 -91.529449 40.44124600000001 -91.538689 40.455399 -91.533051 40.463718 -91.579224 40.484477999999996 -91.585869 40.50483299999999 -91.616699 40.53286399999999 -91.622375 40.55163999999999 -91.691917 40.581165 -91.689796 40.593399000000005 -91.716812 40.609748999999994 -91.741547 40.60823400000001 -91.946198 40.60006000000001 -92.192993 40.599548 -92.361328 40.59143800000001 -92.64624 40.58964499999999 -92.717621 40.58433500000001 -93.100731 40.58048199999999 -93.370056 40.58080699999999 -93.562691 40.57844499999999 -93.786079 40.57402400000001 -94.01783 40.57097200000001 -94.238159 40.57421500000001 -94.484993 40.57575600000001 -94.639633 40.57722899999999 -94.920364 40.581897999999995 -95.217171 40.58433500000001 -95.382294 40.589045999999996 -95.767204 40.620903 -95.757271 40.64311599999999 -95.767723 40.730434 -95.876335 40.792598999999996 -95.851509 40.848331 -95.846153 40.870299999999986 -95.834114 40.901107999999994 -95.836258 40.97425799999999 -95.837318 41.002650999999986 -95.860611 41.03500399999999 -95.859253 41.06587200000001 -95.878517 41.10918799999999 -95.857986 41.16420400000001 -95.876396 41.166866 -95.859512 41.18053800000001 -95.85891 41.194064999999995 -95.91581 41.207854999999995 -95.921959 41.225246 -95.91069 41.302059000000014 -95.929939 41.308471999999995 -95.910912 41.286865000000006 -95.897301 41.30139199999999 -95.888817 41.34008 -95.942604 41.39481000000001 -95.939766 41.46238700000001 -95.934776 41.47239300000001 -95.952896 41.48195999999999 -96.006607 41.492999999999995 -96.013161 41.51152400000001 -95.996399 41.528110999999996 -95.993675 41.53667100000001 -96.004303 41.52434199999999 -96.049881 41.537529000000006 -96.085548 41.563152 -96.091644 41.576008 -96.080544 41.59901400000001 -96.111015 41.65468999999999 -96.099014 41.68410499999999 -96.119972 41.69492299999999 -96.12191 41.70499799999999 -96.085266 41.73157499999999 -96.09948 41.75298699999999 -96.09903 41.791481000000005 -96.076126 41.86263299999999 -96.13533 41.90416300000001 -96.159676 41.92491899999999 -96.145576 41.96626699999999 -96.147034 41.98069799999999 -96.184921 41.99662799999999 -96.202545 42.001270000000005 -96.235794 42.02844999999999 -96.238426 42.04890800000001 -96.265182 42.12346299999999 -96.284821 42.168194 -96.35186 42.214049999999986 -96.363205 42.22953000000001 -96.337402 42.26031499999999 -96.332352 42.28208900000001 -96.342575 42.29803100000001 -96.368393 42.32879600000001 -96.389473 42.34928500000001 -96.423866 42.38092399999999 -96.411453 42.414783 -96.41732 42.441799 -96.397583 42.46740700000001 -96.395767 42.48924600000001 -96.439087 42.517135999999994 -96.479935 42.56403399999999 -96.489029 42.573891 -96.500633 42.58048600000001 -96.48819 42.629761 -96.512535 42.66241099999999 -96.540855 42.668518000000006 -96.562729 42.708359 -96.626228 42.74860799999999 -96.640396 42.77683999999999 -96.632668 42.799564000000004 -96.600563 42.835387999999995 -96.587334 42.83435399999999 -96.572815 42.846667999999994 -96.555901 42.89691500000001 -96.537201 42.91387599999999 -96.543953 42.952393 -96.514626 42.986469 -96.516838 43.012061999999986 -96.498711 43.05152100000001 -96.519699 43.06189699999999 -96.479263 43.07559599999999 -96.461784 43.087886999999995 -96.460495 43.12632400000001 -96.451195 43.20909900000001 -96.472801 43.217926000000006 -96.486931 43.225505999999996 -96.558289 43.23965100000001 -96.566673 43.25328099999999 -96.55925 43.263630000000006 -96.570404 43.29009199999999 -96.578812 43.30767800000001 -96.540245 43.356987000000004 -96.522575 43.38424699999999 -96.524734 43.40074899999999 -96.557388 43.435562000000004 -96.588791 43.481944999999996 -96.583473 43.499874000000005 -96.597992 43.49974399999999 -96.460136 43.49856600000001 -96.060738 43.498977999999994 -95.866615 43.49957699999999 -95.464493 43.500370000000004 -95.396278 43.499413000000004 -94.920197 43.50007199999999 -94.859573 43.49814599999999 -94.454987 43.49899300000001 -94.246544 43.50034299999999 -93.973717 43.500809000000004 -93.653481 43.50053399999999 -93.500618 43.501495000000006 -93.054184 43.501316 -93.027016 43.500294 -92.557831 43.49949599999999 -92.452995 43.499187000000006 -92.07737 43.49960300000001 -91.730217 43.50065599999999 -91.610954 43.500834999999995 -91.223434 43.46471 -91.235771 43.424076000000014 -91.210785 43.37053700000001 -91.198112 43.353970000000004 -91.176918 43.313320000000004 -91.078369 43.280703999999986 -91.066299 43.25791899999999 -91.068924 43.147594 -91.161224 43.08290500000001 -91.168442 43.081199999999995 -91.159622 43.00133099999999 -91.152084 42.925906999999995 -91.138992 42.871452000000005 -91.0933 42.78337500000001 -91.081902 42.744923 -91.06604 42.707066 -90.999054 42.68068299999999 -90.919281 42.678246 -90.892418 42.657005 -90.745483 42.637932000000006 -90.694664 42.571392 -90.664253 42.555713999999995 -90.639091 42.528560999999996 -90.62558 42.50936100000001 -90.638329 42.494698 -90.651772 42.47564299999999 -90.648346 42.46055999999999 -90.605827 42.42183700000001 -90.563583 42.38878299999999 -90.491043 42.360073 -90.441597 42.340633 -90.427681 42.263924 -90.417984 42.24264500000001 -90.407173 42.21020899999999 -90.367729 42.19731899999999 -90.323601 42.15972099999999 -90.230934 42.12268800000001 -90.191574 42.12050199999999 -90.176086 42.103745 -90.166649 42.06104300000001 -90.168098 42.03342799999999 -90.150536 41.98396299999999 -90.14267 41.93077500000001 -90.154518 41.80613700000001 -90.195839 41.78173799999999 -90.25531 41.75646599999999 -90.304886 41.722736 -90.326027 41.64909 -90.341133 41.60279800000001 -90.339348 41.586849 -90.348366 41.567272 -90.423004 41.543578999999994 -90.434967 41.527546 -90.454994 41.52597 -90.54084 41.50958600000001 -90.6007 41.46231800000001 -90.658791 41.450062 -90.708214 41.449820999999986 -90.7799 41.44462200000001 -90.844139 41.421234 -90.949654 41.431084 -91.000694 41.423508 -91.027489 41.40137899999999 -91.055786 41.334895999999986 -91.07328 41.267818000000005 -91.102348 41.23152200000001 -91.101524 41.17625799999999 -91.05632 41.16582500000001 -91.018257 41.14437100000001 -90.990341 41.10435899999999 -90.957787 41.07036199999999 -90.954651 40.950503999999995 -90.960709 40.92392699999999 -90.983276 40.87958499999999 -91.04921 40.833729000000005 -91.088905 40.76154700000001 -91.092751 40.70540199999999 -91.119987</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Iowa</topp:STATE_NAME><topp:STATE_FIPS>19</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>IA</topp:STATE_ABBR><topp:LAND_KM>144715.989</topp:LAND_KM><topp:WATER_KM>1037.659</topp:WATER_KM><topp:PERSONS>2776755.0</topp:PERSONS><topp:FAMILIES>740819.0</topp:FAMILIES><topp:HOUSHOLD>1064325.0</topp:HOUSHOLD><topp:MALE>1344802.0</topp:MALE><topp:FEMALE>1431953.0</topp:FEMALE><topp:WORKERS>1013757.0</topp:WORKERS><topp:DRVALONE>970933.0</topp:DRVALONE><topp:CARPOOL>156930.0</topp:CARPOOL><topp:PUBTRANS>16373.0</topp:PUBTRANS><topp:EMPLOYED>1340242.0</topp:EMPLOYED><topp:UNEMPLOY>63641.0</topp:UNEMPLOY><topp:SERVICE>429860.0</topp:SERVICE><topp:MANUAL>217271.0</topp:MANUAL><topp:P_MALE>0.484</topp:P_MALE><topp:P_FEMALE>0.516</topp:P_FEMALE><topp:SAMP_POP>579075.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.37"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>41.238155000000006 -73.499275</gml:lowerCorner><gml:upperCorner>42.886787 -69.91832</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.77209500000001 -71.319832 41.784324999999995 -71.340302 41.813061000000005 -71.345985 41.85780299999999 -71.335045 41.87568300000001 -71.342995 41.89593099999999 -71.333588 41.88834 -71.384453 41.979164 -71.382904 42.01361499999999 -71.379143 42.00915499999999 -71.497925 42.004177 -71.798317 42.01787899999999 -71.802826 42.02570299999999 -72.095451 42.026306000000005 -72.136826 42.030669999999986 -72.508041 42.03002900000001 -72.571693 42.021511000000004 -72.582375 42.022705 -72.608292 42.030440999999996 -72.609993 42.03375199999999 -72.756355 42.002071 -72.768036 41.99708899999999 -72.818138 42.033412999999996 -72.81691 42.03591499999999 -73.006546 42.03621699999999 -73.046082 42.047337 -73.484665 42.07737 -73.499275 42.504673 -73.351273 42.745979000000005 -73.258507 42.74031400000001 -73.020149 42.73728199999999 -72.923454 42.725769000000014 -72.456238 42.720383 -72.280388 42.707122999999996 -71.930695 42.705291999999986 -71.901421 42.69851299999999 -71.287689 42.725978999999995 -71.252907 42.743464999999986 -71.240974 42.73867000000001 -71.186844 42.80722800000001 -71.181557 42.81819200000001 -71.121101 42.80422999999999 -71.066063 42.85108199999999 -71.025925 42.88505900000001 -70.921837 42.886787 -70.898613 42.863338 -70.850243 42.866974 -70.814384 42.66342900000001 -70.740204 42.646209999999996 -70.593712 42.58254600000001 -70.633965 42.54634100000001 -70.813637 42.447970999999995 -70.894112 42.432297000000005 -70.961128 42.285529999999994 -71.034668 42.23441700000001 -70.923714 42.26566700000001 -70.893181 42.26040599999999 -70.825172 42.248538999999994 -70.775108 42.153065 -70.686554 41.968086 -70.619225 41.930847 -70.540863 41.805656 -70.538231 41.743515 -70.424042 41.721554 -70.274368 41.711704 -70.34166 41.712463000000014 -70.205795 41.781409999999994 -70.019753 41.856243000000006 -70.000984 42.00209000000001 -70.101028 42.06001699999999 -70.255676 42.07239200000001 -70.13562 42.026196 -70.051003 41.903988 -69.964706 41.76754399999999 -69.91832 41.67138299999999 -69.954964 41.612460999999996 -70.398148 41.56964500000001 -70.433449 41.53969599999999 -70.637665 41.55601899999999 -70.665413 41.73553100000001 -70.620285 41.626587 -70.839951 41.63380799999999 -70.892647 41.52001999999999 -71.001701 41.49295799999999 -71.117645 41.65517 -71.141724 41.67839799999999 -71.199318 41.707592000000005 -71.229485 41.749641 -71.267136 41.77209500000001 -71.319832</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.429550000000006 -70.604858 41.46445499999999 -70.568222 41.41727399999999 -70.55336 41.41017199999999 -70.576385 41.39854399999999 -70.515686 41.341442 -70.486671 41.33404200000001 -70.739204 41.29804999999999 -70.770241 41.348488 -70.844444 41.352406 -70.783051 41.32486700000001 -70.771454 41.382057 -70.752296 41.454826 -70.668015 41.47455199999999 -70.606369 41.429550000000006 -70.604858</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.31181000000001 -70.032257 41.32465400000001 -70.00705 41.33708999999999 -70.027161 41.29672600000001 -70.088173 41.349597999999986 -70.035027 41.391842 -70.049805 41.294769 -69.966522 41.251693999999986 -69.968987 41.238155000000006 -70.103645 41.270081000000005 -70.213806 41.29396399999999 -70.207634 41.27750800000001 -70.098412 41.31181000000001 -70.032257</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Massachusetts</topp:STATE_NAME><topp:STATE_FIPS>25</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>MA</topp:STATE_ABBR><topp:LAND_KM>20300.349</topp:LAND_KM><topp:WATER_KM>2606.738</topp:WATER_KM><topp:PERSONS>6016425.0</topp:PERSONS><topp:FAMILIES>1514746.0</topp:FAMILIES><topp:HOUSHOLD>2247110.0</topp:HOUSHOLD><topp:MALE>2888745.0</topp:MALE><topp:FEMALE>3127680.0</topp:FEMALE><topp:WORKERS>2268095.0</topp:WORKERS><topp:DRVALONE>2148065.0</topp:DRVALONE><topp:CARPOOL>318026.0</topp:CARPOOL><topp:PUBTRANS>247381.0</topp:PUBTRANS><topp:EMPLOYED>3027950.0</topp:EMPLOYED><topp:UNEMPLOY>218000.0</topp:UNEMPLOY><topp:SERVICE>718354.0</topp:SERVICE><topp:MANUAL>345333.0</topp:MANUAL><topp:P_MALE>0.48</topp:P_MALE><topp:P_FEMALE>0.52</topp:P_FEMALE><topp:SAMP_POP>808423.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.38"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>39.99259599999999 -104.055725</gml:lowerCorner><gml:upperCorner>43.003094000000004 -95.308441</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>40.00100699999999 -101.406952 39.99892800000001 -102.051071 40.34265099999999 -102.047081 40.431084 -102.047157 40.69732300000001 -102.04557 40.743134 -102.046532 40.998076999999995 -102.047279 41.000225 -102.620789 40.99813499999999 -102.651802 41.000331999999986 -103.382469 40.999663999999996 -103.571823 41.00322700000001 -104.051201 41.38810699999999 -104.053505 41.564246999999995 -104.054993 41.69824600000001 -104.053108 41.99985100000001 -104.053009 42.61470399999999 -104.055725 43.003094000000004 -104.05571 42.99865 -103.500984 42.99937399999999 -103.005409 42.99532300000001 -102.787933 42.989891 -102.086273 42.98683500000001 -101.231308 42.99108899999999 -100.197739 42.992332000000005 -99.532402 42.99239 -99.253593 42.991783 -98.497292 42.937163999999996 -98.457085 42.920139000000006 -98.390846 42.881798 -98.309982 42.839573 -98.167473 42.83579599999999 -98.144516 42.82022499999999 -98.122765 42.80836099999999 -98.121468 42.769192000000004 -98.032791 42.76681099999999 -97.994797 42.77368899999999 -97.963211 42.79232400000001 -97.929131 42.83127200000001 -97.889595 42.855807999999996 -97.888313 42.866589000000005 -97.818298 42.849597999999986 -97.796684 42.84616500000001 -97.771843 42.85800900000001 -97.724907 42.836838 -97.68541 42.86128600000001 -97.634628 42.847992000000005 -97.570312 42.860138000000006 -97.505791 42.857158999999996 -97.482819 42.85044500000001 -97.456924 42.867435 -97.388969 42.861774 -97.311081 42.850018000000006 -97.271126 42.85183000000001 -97.242859 42.841206 -97.224113 42.81257600000001 -97.211502 42.798621999999995 -97.161095 42.77392599999999 -97.130142 42.759544000000005 -97.014816 42.75831600000001 -96.979271 42.75206800000001 -96.969681 42.72730999999999 -96.977547 42.721149 -96.970451 42.73170099999999 -96.907913 42.70408599999999 -96.809822 42.681343 -96.81012 42.67002099999999 -96.799026 42.66859400000001 -96.722343 42.657718999999986 -96.698746 42.641166999999996 -96.694283 42.621910000000014 -96.714958 42.61230499999999 -96.713745 42.550735 -96.63636 42.522696999999994 -96.628983 42.507239999999996 -96.605156 42.518291000000005 -96.584442 42.52050399999999 -96.546906 42.48846399999999 -96.494392 42.48924600000001 -96.439087 42.46740700000001 -96.395767 42.441799 -96.397583 42.414783 -96.41732 42.38092399999999 -96.411453 42.34928500000001 -96.423866 42.32879600000001 -96.389473 42.29803100000001 -96.368393 42.28208900000001 -96.342575 42.26031499999999 -96.332352 42.22953000000001 -96.337402 42.214049999999986 -96.363205 42.168194 -96.35186 42.12346299999999 -96.284821 42.04890800000001 -96.265182 42.02844999999999 -96.238426 42.001270000000005 -96.235794 41.99662799999999 -96.202545 41.98069799999999 -96.184921 41.96626699999999 -96.147034 41.92491899999999 -96.145576 41.90416300000001 -96.159676 41.86263299999999 -96.13533 41.791481000000005 -96.076126 41.75298699999999 -96.09903 41.73157499999999 -96.09948 41.70499799999999 -96.085266 41.69492299999999 -96.12191 41.68410499999999 -96.119972 41.65468999999999 -96.099014 41.59901400000001 -96.111015 41.576008 -96.080544 41.563152 -96.091644 41.537529000000006 -96.085548 41.52434199999999 -96.049881 41.53667100000001 -96.004303 41.528110999999996 -95.993675 41.51152400000001 -95.996399 41.492999999999995 -96.013161 41.48195999999999 -96.006607 41.47239300000001 -95.952896 41.46238700000001 -95.934776 41.39481000000001 -95.939766 41.34008 -95.942604 41.30139199999999 -95.888817 41.286865000000006 -95.897301 41.308471999999995 -95.910912 41.302059000000014 -95.929939 41.225246 -95.91069 41.207854999999995 -95.921959 41.194064999999995 -95.91581 41.18053800000001 -95.85891 41.166866 -95.859512 41.16420400000001 -95.876396 41.10918799999999 -95.857986 41.06587200000001 -95.878517 41.03500399999999 -95.859253 41.002650999999986 -95.860611 40.97425799999999 -95.837318 40.901107999999994 -95.836258 40.870299999999986 -95.834114 40.848331 -95.846153 40.792598999999996 -95.851509 40.730434 -95.876335 40.64311599999999 -95.767723 40.620903 -95.757271 40.589045999999996 -95.767204 40.549705999999986 -95.763138 40.53237200000001 -95.736763 40.52412799999999 -95.691795 40.56116900000001 -95.687141 40.565833999999995 -95.675423 40.558727000000005 -95.662674 40.53033099999999 -95.657791 40.512203 -95.6847 40.48533599999999 -95.695091 40.396389 -95.636551 40.358799000000005 -95.633919 40.346497 -95.615936 40.33141699999999 -95.617668 40.32234600000001 -95.645287 40.30910900000001 -95.646561 40.309776 -95.595268 40.266215999999986 -95.546875 40.226856 -95.476562 40.213257 -95.466377 40.17399599999999 -95.460693 40.131744 -95.422218 40.11541700000001 -95.392555 40.09536399999999 -95.384285 40.08037899999999 -95.403526 40.048111000000006 -95.413506 40.043750999999986 -95.390274 40.028751 -95.370987 40.02497500000001 -95.34481 39.999409000000014 -95.308441 39.99259599999999 -95.329445 39.99348800000001 -95.780434 39.995159 -96.000977 39.99450300000001 -96.240311 39.994170999999994 -96.453743 39.994473 -96.801117 39.996151 -96.907982 39.997375000000005 -97.361595 39.999718 -97.816269 39.99843999999999 -97.929268 39.99842100000001 -98.26384 39.997116000000005 -98.504143 39.998447 -98.720284 39.998324999999994 -99.064384 39.99956499999999 -99.177834 40.00297900000001 -99.627472 40.000473 -100.180496 40.000580000000014 -100.190697 39.999168 -100.734619 40.00019499999999 -100.754425 40.001823 -101.321709 40.00100699999999 -101.406952</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Nebraska</topp:STATE_NAME><topp:STATE_FIPS>31</topp:STATE_FIPS><topp:SUB_REGION>W N Cen</topp:SUB_REGION><topp:STATE_ABBR>NE</topp:STATE_ABBR><topp:LAND_KM>199113.203</topp:LAND_KM><topp:WATER_KM>1244.957</topp:WATER_KM><topp:PERSONS>1578385.0</topp:PERSONS><topp:FAMILIES>415427.0</topp:FAMILIES><topp:HOUSHOLD>602363.0</topp:HOUSHOLD><topp:MALE>769439.0</topp:MALE><topp:FEMALE>808946.0</topp:FEMALE><topp:WORKERS>571600.0</topp:WORKERS><topp:DRVALONE>590216.0</topp:DRVALONE><topp:CARPOOL>86820.0</topp:CARPOOL><topp:PUBTRANS>8988.0</topp:PUBTRANS><topp:EMPLOYED>772813.0</topp:EMPLOYED><topp:UNEMPLOY>29326.0</topp:UNEMPLOY><topp:SERVICE>249094.0</topp:SERVICE><topp:MANUAL>108841.0</topp:MANUAL><topp:P_MALE>0.487</topp:P_MALE><topp:P_FEMALE>0.513</topp:P_FEMALE><topp:SAMP_POP>328831.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.39"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>40.505898 -79.763466</gml:lowerCorner><gml:upperCorner>45.0061 -71.870476</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>42.267269 -79.763466 42.41930400000001 -79.444252 42.493404 -79.355118 42.574557999999996 -79.142471 42.69918799999999 -79.043991 42.792686 -78.859444 42.974174000000005 -78.93679 43.022301 -78.883034 43.06657000000001 -78.925835 43.09054900000001 -79.061348 43.14468400000001 -79.039558 43.26816199999999 -79.062469 43.371937 -78.464905 43.36551299999999 -77.992271 43.335109999999986 -77.745277 43.24148600000001 -77.575989 43.27565000000001 -77.377602 43.27852999999999 -76.914841 43.342667000000006 -76.737152 43.323375999999996 -76.718796 43.414085 -76.619957 43.500652 -76.454994 43.554084999999986 -76.223114 43.633129 -76.184921 43.68263200000001 -76.206017 43.83506399999999 -76.240341 43.91243 -76.194069 43.93214800000001 -76.129417 44.013172 -76.134872 44.06554399999999 -76.201889 44.04196200000001 -76.297226 44.09830099999999 -76.363213 44.390209 -75.848351 44.51747499999999 -75.758972 44.81057000000001 -75.329201 44.948578 -74.968819 44.99286699999999 -74.736481 44.990803 -74.021935 45.0061 -73.345146 44.981933999999995 -73.351181 44.93256400000001 -73.336838 44.847893 -73.382729 44.81907699999999 -73.369476 44.799251999999996 -73.327209 44.72419400000001 -73.373581 44.68032500000001 -73.358574 44.66123200000001 -73.37352 44.634303999999986 -73.37056 44.61976200000001 -73.382248 44.57912099999999 -73.371719 44.55392499999999 -73.348236 44.54428100000001 -73.334877 44.432804000000004 -73.293747 44.405483000000004 -73.300423 44.36734000000001 -73.330215 44.26008999999999 -73.305756 44.20119500000001 -73.377762 44.172054 -73.382492 44.136173000000014 -73.408295 44.10655600000001 -73.409187 44.06384299999999 -73.435646 44.045624000000004 -73.436432 44.01816600000001 -73.408684 43.98813999999999 -73.417839 43.914749 -73.405769 43.885918000000004 -73.375557 43.80444700000001 -73.385178 43.778366000000005 -73.359436 43.756496 -73.357109 43.71421799999999 -73.371429 43.63204999999999 -73.423401 43.582413 -73.418762 43.56907699999999 -73.388557 43.61493300000001 -73.364128 43.624649000000005 -73.303978 43.619586999999996 -73.294548 43.593121 -73.282181 43.57496599999999 -73.291847 43.559315 -73.260429 43.512764000000004 -73.238838 43.310779999999994 -73.250519 42.94021599999999 -73.276451 42.83702500000001 -73.280029 42.803471 -73.296616 42.747401999999994 -73.269722 42.745979000000005 -73.258507 42.504673 -73.351273 42.07737 -73.499275 42.047337 -73.484665 41.665592000000004 -73.517578 41.522651999999994 -73.530823 41.365204000000006 -73.544724 41.293526000000014 -73.55069 41.21065899999999 -73.478554 41.100258 -73.725662 41.012519999999995 -73.654152 40.99829500000001 -73.65358 40.88658899999999 -73.780464 40.832233 -73.796768 40.802704000000006 -73.92028 40.70389900000001 -74.006599 40.737629 -74.006676 40.79738599999999 -73.977478 40.885941 -73.922813 40.92721599999999 -73.909386 40.96077299999999 -73.896568 40.99843200000001 -73.897118 41.123515999999995 -74.213448 41.137530999999996 -74.242767 41.19575499999999 -74.372383 41.35047900000001 -74.700455 41.374966 -74.705666 41.401543000000004 -74.740829 41.421966999999995 -74.740433 41.43005400000001 -74.755219 41.429824999999994 -74.79319 41.447063000000014 -74.864456 41.44458 -74.895668 41.461803 -74.898949 41.484261000000004 -74.932953 41.48351299999999 -74.972176 41.539467 -75.015274 41.56571199999999 -75.025475 41.604389 -75.070251 41.612990999999994 -75.072838 41.637226 -75.051712 41.714747999999986 -75.065788 41.72662 -75.057358 41.770172 -75.061707 41.778954 -75.097542 41.79711900000001 -75.097214 41.81406000000001 -75.080231 41.83689899999999 -75.118164 41.84909400000001 -75.125137 41.85570100000001 -75.148666 41.867751999999996 -75.171669 41.868786 -75.254898 41.947517000000005 -75.284073 41.96118899999999 -75.324448 41.992760000000004 -75.346039 41.998271999999986 -75.383194 41.996284 -75.48011 41.999424000000005 -76.105186 42.000580000000014 -76.14537 42.002937 -76.564247 42.00246000000001 -76.928711 42.00290699999999 -76.968887 41.998760000000004 -77.613136 41.997265 -77.745293 41.99813499999999 -78.204529 41.99935500000001 -78.305351 41.999786 -78.918777 42.00109900000001 -79.059723 42.00053 -79.612595 42.003052 -79.761887 42.267269 -79.763466</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>40.594482 -73.752632 40.557545000000005 -73.928009 40.618134 -73.761818 40.636829000000006 -73.765358 40.65249600000001 -73.846481 40.63102699999999 -73.916153 40.59016399999999 -73.880234 40.581154 -74.004456 40.63923299999999 -74.028137 40.739281000000005 -73.956024 40.79701600000001 -73.899437 40.78874999999999 -73.754341 40.844898 -73.749901 40.90304599999999 -73.598701 40.87964600000001 -73.478523 40.922459 -73.431084 40.90094400000001 -73.214966 40.95129800000001 -73.141426 40.968334 -73.021721 40.98118600000001 -72.632034 41.149235000000004 -72.317505 41.142437 -72.281624 41.11010400000001 -72.354752 41.02593999999999 -72.416817 40.966080000000005 -72.551437 40.905201000000005 -72.605278 40.92004800000001 -72.476578 41.02391800000001 -72.293556 41.03527500000001 -72.203827 41.000473 -72.077492 41.08476999999999 -71.924187 41.07440600000001 -71.870476 41.030472 -71.919189 40.814941000000005 -72.521629 40.661224000000004 -73.422958 40.594482 -73.752632</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>40.626282 -73.293495 40.63297700000001 -73.29158 40.63297700000001 -73.240875 40.67506800000001 -73.051468 40.737246999999996 -72.877357 40.76403400000001 -72.782654 40.76786000000001 -72.757782 40.75829300000001 -72.764481 40.69994 -72.956764 40.67124200000001 -73.03138 40.625324000000006 -73.249489 40.626282 -73.293495</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>40.505898 -74.23735 40.537800000000004 -74.23735 40.624393 -74.16671 40.649460000000005 -74.07328 40.60160400000001 -74.059608 40.544636 -74.123413 40.510456000000005 -74.194054 40.505898 -74.23735</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>New York</topp:STATE_NAME><topp:STATE_FIPS>36</topp:STATE_FIPS><topp:SUB_REGION>Mid Atl</topp:SUB_REGION><topp:STATE_ABBR>NY</topp:STATE_ABBR><topp:LAND_KM>120316.171</topp:LAND_KM><topp:WATER_KM>6262.71</topp:WATER_KM><topp:PERSONS>1.8235907E7</topp:PERSONS><topp:FAMILIES>4548344.0</topp:FAMILIES><topp:HOUSHOLD>6746555.0</topp:HOUSHOLD><topp:MALE>8739138.0</topp:MALE><topp:FEMALE>9496769.0</topp:FEMALE><topp:WORKERS>6763332.0</topp:WORKERS><topp:DRVALONE>4467866.0</topp:DRVALONE><topp:CARPOOL>881693.0</topp:CARPOOL><topp:PUBTRANS>2113133.0</topp:PUBTRANS><topp:EMPLOYED>8498119.0</topp:EMPLOYED><topp:UNEMPLOY>636280.0</topp:UNEMPLOY><topp:SERVICE>2104413.0</topp:SERVICE><topp:MANUAL>1035767.0</topp:MANUAL><topp:P_MALE>0.479</topp:P_MALE><topp:P_FEMALE>0.521</topp:P_FEMALE><topp:SAMP_POP>2564485.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.40"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>39.719234 -80.526253</gml:lowerCorner><gml:upperCorner>42.267269 -74.700455</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>39.719528 -77.476082 39.725368 -78.096222 39.724007 -78.334816 39.723659999999995 -78.385048 39.723029999999994 -78.818016 39.722252 -78.930428 39.719234 -79.396851 39.72019599999999 -79.481209 39.72172900000001 -79.765358 39.721588 -79.918488 39.719761000000005 -80.429283 39.721126999999996 -80.524467 39.95834400000001 -80.524857 40.022751 -80.525169 40.16244900000001 -80.526253 40.40296599999999 -80.523773 40.478717999999986 -80.524567 40.63713799999999 -80.522209 40.854107 -80.520515 40.897209000000004 -80.521133 41.12957 -80.52314 41.48923500000001 -80.520203 41.495048999999995 -80.524132 41.850719 -80.52285 41.986816000000005 -80.520798 42.267269 -79.763466 42.003052 -79.761887 42.00053 -79.612595 42.00109900000001 -79.059723 41.999786 -78.918777 41.99935500000001 -78.305351 41.99813499999999 -78.204529 41.997265 -77.745293 41.998760000000004 -77.613136 42.00290699999999 -76.968887 42.00246000000001 -76.928711 42.002937 -76.564247 42.000580000000014 -76.14537 41.999424000000005 -76.105186 41.996284 -75.48011 41.998271999999986 -75.383194 41.992760000000004 -75.346039 41.96118899999999 -75.324448 41.947517000000005 -75.284073 41.868786 -75.254898 41.867751999999996 -75.171669 41.85570100000001 -75.148666 41.84909400000001 -75.125137 41.83689899999999 -75.118164 41.81406000000001 -75.080231 41.79711900000001 -75.097214 41.778954 -75.097542 41.770172 -75.061707 41.72662 -75.057358 41.714747999999986 -75.065788 41.637226 -75.051712 41.612990999999994 -75.072838 41.604389 -75.070251 41.56571199999999 -75.025475 41.539467 -75.015274 41.48351299999999 -74.972176 41.484261000000004 -74.932953 41.461803 -74.898949 41.44458 -74.895668 41.447063000000014 -74.864456 41.429824999999994 -74.79319 41.43005400000001 -74.755219 41.421966999999995 -74.740433 41.401543000000004 -74.740829 41.374966 -74.705666 41.35047900000001 -74.700455 41.311871 -74.792053 41.29512 -74.794533 41.282612 -74.825584 41.22672299999999 -74.866798 41.20667599999999 -74.863274 41.141009999999994 -74.915154 41.111759000000006 -74.950378 41.09928500000001 -74.984978 41.08166499999999 -74.989265 41.082581000000005 -74.966675 41.062388999999996 -75.001534 41.028107000000006 -75.035637 41.01061999999999 -75.070358 41.00015300000001 -75.116997 40.97743199999999 -75.139679 40.962841 -75.135902 40.90325200000001 -75.080116 40.884528999999986 -75.074066 40.871948 -75.05687 40.85557600000001 -75.055 40.83918800000001 -75.099922 40.821293 -75.089592 40.791561 -75.100922 40.772609999999986 -75.130699 40.774711999999994 -75.170692 40.747906 -75.194023 40.723759 -75.188133 40.68596299999999 -75.205734 40.669692999999995 -75.184441 40.65063900000001 -75.209297 40.634106 -75.198318 40.614643 -75.200768 40.58366799999999 -75.193893 40.570583 -75.197952 40.55669800000001 -75.182663 40.564696999999995 -75.124908 40.545254 -75.08017 40.52090100000001 -75.064064 40.45615000000001 -75.070503 40.42006699999999 -75.057838 40.401218 -75.021652 40.40851599999999 -75.000862 40.40434300000001 -74.973228 40.34536700000001 -74.950577 40.33366799999999 -74.932892 40.31392700000001 -74.921539 40.29948400000001 -74.88105 40.248344 -74.843155 40.177616 -74.73922 40.14919699999999 -74.725876 40.12424899999999 -74.746689 40.116051 -74.829414 40.077946 -74.872314 40.05790300000001 -74.956589 40.033962 -74.983871 40.00752299999999 -75.046059 39.98527899999999 -75.068428 39.97561999999999 -75.084969 39.97657799999999 -75.111343 39.95580699999999 -75.140244 39.934628000000004 -75.147537 39.89677399999999 -75.136185 39.88148899999999 -75.14328 39.87729300000001 -75.185982 39.850291999999996 -75.247368 39.845425000000006 -75.254112 39.84840399999999 -75.346298 39.798869999999994 -75.42083 39.826435000000004 -75.470345 39.84000800000001 -75.583794 39.83819600000001 -75.644341 39.820347 -75.695114 39.77481800000001 -75.745934 39.72444200000001 -75.775269 39.72375500000001 -75.791435 39.72212200000001 -76.139549 39.72174799999999 -76.233444 39.72016099999999 -76.570145 39.72115299999999 -76.790794 39.72079099999999 -76.997108 39.72058100000001 -77.221344 39.719978 -77.464722 39.719528 -77.476082</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Pennsylvania</topp:STATE_NAME><topp:STATE_FIPS>42</topp:STATE_FIPS><topp:SUB_REGION>Mid Atl</topp:SUB_REGION><topp:STATE_ABBR>PA</topp:STATE_ABBR><topp:LAND_KM>116082.737</topp:LAND_KM><topp:WATER_KM>1274.584</topp:WATER_KM><topp:PERSONS>1.1881643E7</topp:PERSONS><topp:FAMILIES>3155989.0</topp:FAMILIES><topp:HOUSHOLD>4495966.0</topp:HOUSHOLD><topp:MALE>5694265.0</topp:MALE><topp:FEMALE>6187378.0</topp:FEMALE><topp:WORKERS>4416669.0</topp:WORKERS><topp:DRVALONE>3818385.0</topp:DRVALONE><topp:CARPOOL>689656.0</topp:CARPOOL><topp:PUBTRANS>343724.0</topp:PUBTRANS><topp:EMPLOYED>5434532.0</topp:EMPLOYED><topp:UNEMPLOY>344795.0</topp:UNEMPLOY><topp:SERVICE>1426851.0</topp:SERVICE><topp:MANUAL>916786.0</topp:MANUAL><topp:P_MALE>0.479</topp:P_MALE><topp:P_FEMALE>0.521</topp:P_FEMALE><topp:SAMP_POP>2147891.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.41"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>40.99829500000001 -73.725662</gml:lowerCorner><gml:upperCorner>42.047337 -71.788734</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.522651999999994 -73.530823 41.665592000000004 -73.517578 42.047337 -73.484665 42.03621699999999 -73.046082 42.03591499999999 -73.006546 42.033412999999996 -72.81691 41.99708899999999 -72.818138 42.002071 -72.768036 42.03375199999999 -72.756355 42.030440999999996 -72.609993 42.022705 -72.608292 42.021511000000004 -72.582375 42.03002900000001 -72.571693 42.030669999999986 -72.508041 42.026306000000005 -72.136826 42.02570299999999 -72.095451 42.01787899999999 -71.802826 42.004177 -71.798317 41.72150400000001 -71.788734 41.641659000000004 -71.793091 41.601208000000014 -71.79068 41.415729999999996 -71.80323 41.40375499999999 -71.846481 41.34186199999999 -71.837357 41.325249000000014 -71.848259 41.32266999999999 -71.867165 41.281048 -72.281891 41.28954300000001 -72.326828 41.35825 -72.378883 41.27800400000001 -72.378624 41.26360299999999 -72.52771 41.26996600000001 -72.907188 41.160942000000006 -73.104866 40.99829500000001 -73.65358 41.012519999999995 -73.654152 41.100258 -73.725662 41.21065899999999 -73.478554 41.293526000000014 -73.55069 41.365204000000006 -73.544724 41.522651999999994 -73.530823</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Connecticut</topp:STATE_NAME><topp:STATE_FIPS>09</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>CT</topp:STATE_ABBR><topp:LAND_KM>12549.571</topp:LAND_KM><topp:WATER_KM>748.495</topp:WATER_KM><topp:PERSONS>3287116.0</topp:PERSONS><topp:FAMILIES>864493.0</topp:FAMILIES><topp:HOUSHOLD>1230479.0</topp:HOUSHOLD><topp:MALE>1592873.0</topp:MALE><topp:FEMALE>1694243.0</topp:FEMALE><topp:WORKERS>1248972.0</topp:WORKERS><topp:DRVALONE>1300825.0</topp:DRVALONE><topp:CARPOOL>186944.0</topp:CARPOOL><topp:PUBTRANS>65805.0</topp:PUBTRANS><topp:EMPLOYED>1692874.0</topp:EMPLOYED><topp:UNEMPLOY>95819.0</topp:UNEMPLOY><topp:SERVICE>402039.0</topp:SERVICE><topp:MANUAL>195403.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>446558.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.42"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>41.32266999999999 -71.867165</gml:lowerCorner><gml:upperCorner>42.01361499999999 -71.117645</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.601208000000014 -71.79068 41.641659000000004 -71.793091 41.72150400000001 -71.788734 42.004177 -71.798317 42.00915499999999 -71.497925 42.01361499999999 -71.379143 41.979164 -71.382904 41.88834 -71.384453 41.89593099999999 -71.333588 41.87568300000001 -71.342995 41.85780299999999 -71.335045 41.813061000000005 -71.345985 41.784324999999995 -71.340302 41.77209500000001 -71.319832 41.749641 -71.267136 41.707592000000005 -71.229485 41.67944700000001 -71.284508 41.74124900000001 -71.367889 41.761055 -71.394081 41.703190000000006 -71.369514 41.65211099999999 -71.419746 41.48658800000001 -71.427818 41.39198300000001 -71.490387 41.32716400000001 -71.722755 41.32266999999999 -71.867165 41.325249000000014 -71.848259 41.34186199999999 -71.837357 41.40375499999999 -71.846481 41.415729999999996 -71.80323 41.601208000000014 -71.79068</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.67839799999999 -71.199318 41.65517 -71.141724 41.49295799999999 -71.117645 41.46321499999999 -71.200447 41.67839799999999 -71.199318</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>41.62116599999999 -71.269676 41.63553999999999 -71.219955 41.47474700000001 -71.239182 41.483517000000006 -71.288513 41.44575499999999 -71.350029 41.62116599999999 -71.269676</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Rhode Island</topp:STATE_NAME><topp:STATE_FIPS>44</topp:STATE_FIPS><topp:SUB_REGION>N Eng</topp:SUB_REGION><topp:STATE_ABBR>RI</topp:STATE_ABBR><topp:LAND_KM>2706.52</topp:LAND_KM><topp:WATER_KM>538.056</topp:WATER_KM><topp:PERSONS>1003464.0</topp:PERSONS><topp:FAMILIES>258886.0</topp:FAMILIES><topp:HOUSHOLD>377977.0</topp:HOUSHOLD><topp:MALE>481496.0</topp:MALE><topp:FEMALE>521968.0</topp:FEMALE><topp:WORKERS>377122.0</topp:WORKERS><topp:DRVALONE>375766.0</topp:DRVALONE><topp:CARPOOL>58141.0</topp:CARPOOL><topp:PUBTRANS>12187.0</topp:PUBTRANS><topp:EMPLOYED>487913.0</topp:EMPLOYED><topp:UNEMPLOY>34690.0</topp:UNEMPLOY><topp:SERVICE>131213.0</topp:SERVICE><topp:MANUAL>75612.0</topp:MANUAL><topp:P_MALE>0.48</topp:P_MALE><topp:P_FEMALE>0.52</topp:P_FEMALE><topp:SAMP_POP>128740.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.43"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.95657 -75.570587</gml:lowerCorner><gml:upperCorner>41.35047900000001 -73.896568</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>39.71474499999999 -75.489639 39.71997099999999 -75.476334 39.74171799999999 -75.475128 39.763248000000004 -75.460754 39.778130000000004 -75.428009 39.789658 -75.412117 39.798869999999994 -75.42083 39.84840399999999 -75.346298 39.845425000000006 -75.254112 39.850291999999996 -75.247368 39.87729300000001 -75.185982 39.88148899999999 -75.14328 39.89677399999999 -75.136185 39.934628000000004 -75.147537 39.95580699999999 -75.140244 39.97657799999999 -75.111343 39.97561999999999 -75.084969 39.98527899999999 -75.068428 40.00752299999999 -75.046059 40.033962 -74.983871 40.05790300000001 -74.956589 40.077946 -74.872314 40.116051 -74.829414 40.12424899999999 -74.746689 40.14919699999999 -74.725876 40.177616 -74.73922 40.248344 -74.843155 40.29948400000001 -74.88105 40.31392700000001 -74.921539 40.33366799999999 -74.932892 40.34536700000001 -74.950577 40.40434300000001 -74.973228 40.40851599999999 -75.000862 40.401218 -75.021652 40.42006699999999 -75.057838 40.45615000000001 -75.070503 40.52090100000001 -75.064064 40.545254 -75.08017 40.564696999999995 -75.124908 40.55669800000001 -75.182663 40.570583 -75.197952 40.58366799999999 -75.193893 40.614643 -75.200768 40.634106 -75.198318 40.65063900000001 -75.209297 40.669692999999995 -75.184441 40.68596299999999 -75.205734 40.723759 -75.188133 40.747906 -75.194023 40.774711999999994 -75.170692 40.772609999999986 -75.130699 40.791561 -75.100922 40.821293 -75.089592 40.83918800000001 -75.099922 40.85557600000001 -75.055 40.871948 -75.05687 40.884528999999986 -75.074066 40.90325200000001 -75.080116 40.962841 -75.135902 40.97743199999999 -75.139679 41.00015300000001 -75.116997 41.01061999999999 -75.070358 41.028107000000006 -75.035637 41.062388999999996 -75.001534 41.082581000000005 -74.966675 41.08166499999999 -74.989265 41.09928500000001 -74.984978 41.111759000000006 -74.950378 41.141009999999994 -74.915154 41.20667599999999 -74.863274 41.22672299999999 -74.866798 41.282612 -74.825584 41.29512 -74.794533 41.311871 -74.792053 41.35047900000001 -74.700455 41.19575499999999 -74.372383 41.137530999999996 -74.242767 41.123515999999995 -74.213448 40.99843200000001 -73.897118 40.96077299999999 -73.896568 40.92721599999999 -73.909386 40.885941 -73.922813 40.79738599999999 -73.977478 40.737629 -74.006676 40.70389900000001 -74.006599 40.64696900000001 -74.129471 40.705524 -74.115944 40.675377 -74.14727 40.591083999999995 -74.208664 40.51419799999999 -74.27932 40.46364199999999 -74.269325 40.443478 -74.224915 40.45135099999999 -74.122299 40.32350500000001 -73.978859 40.10172700000001 -74.03965 40.115971 -74.091843 40.088066 -74.08419 40.091251 -74.034714 40.05673999999999 -74.050209 40.051445 -74.122597 40.042156000000006 -74.077751 39.87848700000001 -74.159645 39.718154999999996 -74.171844 39.62392800000001 -74.238091 39.571968 -74.323753 39.52350999999999 -74.329262 39.54250300000001 -74.412796 39.50251 -74.401527 39.42663999999999 -74.460823 39.38095899999999 -74.447906 39.287136000000004 -74.65863 39.28151700000001 -74.622925 39.25071299999999 -74.624985 39.02626000000001 -74.802681 38.989731000000006 -74.879639 38.95657 -74.876686 38.971625999999986 -74.968506 39.11374699999999 -74.890587 39.170525 -74.917038 39.19825 -75.014786 39.18457799999999 -75.120331 39.37485899999999 -75.416031 39.49040199999999 -75.553116 39.56645599999999 -75.517044 39.61762200000001 -75.570587 39.71474499999999 -75.489639</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>New Jersey</topp:STATE_NAME><topp:STATE_FIPS>34</topp:STATE_FIPS><topp:SUB_REGION>Mid Atl</topp:SUB_REGION><topp:STATE_ABBR>NJ</topp:STATE_ABBR><topp:LAND_KM>21208.368</topp:LAND_KM><topp:WATER_KM>1128.595</topp:WATER_KM><topp:PERSONS>7484736.0</topp:PERSONS><topp:FAMILIES>1962314.0</topp:FAMILIES><topp:HOUSHOLD>2687478.0</topp:HOUSHOLD><topp:MALE>3622220.0</topp:MALE><topp:FEMALE>3862516.0</topp:FEMALE><topp:WORKERS>2817265.0</topp:WORKERS><topp:DRVALONE>2723918.0</topp:DRVALONE><topp:CARPOOL>451438.0</topp:CARPOOL><topp:PUBTRANS>266063.0</topp:PUBTRANS><topp:EMPLOYED>3741297.0</topp:EMPLOYED><topp:UNEMPLOY>218598.0</topp:UNEMPLOY><topp:SERVICE>846794.0</topp:SERVICE><topp:MANUAL>460013.0</topp:MANUAL><topp:P_MALE>0.484</topp:P_MALE><topp:P_FEMALE>0.516</topp:P_FEMALE><topp:SAMP_POP>1052748.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.44"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>37.77618 -88.101456</gml:lowerCorner><gml:upperCorner>41.76549499999999 -84.787498</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.177223 -86.341629 38.19322600000001 -86.364372 38.194743999999986 -86.388329 38.167957 -86.387123 38.155495 -86.343147 38.143963000000014 -86.335442 38.13420500000001 -86.344063 38.12323000000001 -86.3937 38.10815400000001 -86.407204 38.129692000000006 -86.452545 38.12909300000001 -86.464867 38.11164500000001 -86.474358 38.08863400000001 -86.442543 38.075931999999995 -86.44249 38.05909700000001 -86.458389 38.051585999999986 -86.503136 38.046986000000004 -86.519112 38.018631 -86.527855 37.987415 -86.530869 37.942181000000005 -86.516922 37.927811000000005 -86.522758 37.921454999999995 -86.541107 37.925606 -86.581802 37.921013 -86.598328 37.857918 -86.614799 37.845943000000005 -86.645584 37.847324 -86.665939 37.860584 -86.670685 37.902514999999994 -86.660324 37.913139 -86.668671 37.91179700000001 -86.68914 37.894566 -86.728889 37.898303999999996 -86.753838 37.978745 -86.802826 37.991505000000004 -86.826317 37.986866000000006 -86.863281 37.953644 -86.900085 37.937988000000004 -86.93158 37.930565 -86.989037 37.924713 -87.013161 37.907955 -87.036484 37.807086999999996 -87.071312 37.784203000000005 -87.10643 37.789688 -87.131882 37.82692 -87.158081 37.838593 -87.175781 37.84907200000001 -87.226761 37.870773 -87.272743 37.89367300000001 -87.310555 37.93492500000001 -87.387543 37.936477999999994 -87.452278 37.91558499999999 -87.504791 37.971115 -87.604309 37.923412 -87.627121 37.890724000000006 -87.594704 37.864868 -87.59362 37.843777 -87.607574 37.828132999999994 -87.65168 37.836330000000004 -87.6847 37.897007 -87.679703 37.899711999999994 -87.709389 37.894543 -87.72818 37.898087000000004 -87.753761 37.878212000000005 -87.823624 37.890902999999994 -87.857162 37.924553 -87.89901 37.919865 -87.921867 37.904160000000005 -87.934456 37.875179 -87.936821 37.838570000000004 -87.910202 37.809685 -87.920143 37.799507000000006 -87.939583 37.77618 -87.95871 37.801308000000006 -88.011192 37.805683 -88.035576 37.817612 -88.086029 37.831249 -88.089264 37.827522 -88.042137 37.843745999999996 -88.034241 37.867808999999994 -88.075737 37.895306000000005 -88.101456 37.90617 -88.100082 37.896004000000005 -88.044868 37.905758000000006 -88.026588 37.917591 -88.030441 37.92366 -88.084 37.944 -88.078941 37.929783 -88.064621 37.934498000000005 -88.041771 37.956264000000004 -88.042511 37.975055999999995 -88.021706 38.00823600000001 -88.029213 38.03353100000001 -88.021698 38.03830300000001 -88.041473 38.04512 -88.043091 38.054084999999986 -88.034729 38.073307 -87.975296 38.09674799999999 -87.964867 38.09234599999999 -88.012329 38.10330200000001 -88.018547 38.131760000000014 -87.973503 38.13691299999999 -87.950569 38.15752800000001 -87.931992 38.171131 -87.932289 38.200714000000005 -87.977928 38.234814 -87.986008 38.241085 -87.980019 38.30477099999999 -87.925919 38.302345 -87.913651 38.281048 -87.914108 38.300658999999996 -87.888466 38.315552 -87.883446 38.316788 -87.874039 38.28536199999999 -87.863007 38.28609800000001 -87.850082 38.35252399999999 -87.834503 38.378124000000014 -87.784019 38.41796500000001 -87.748428 38.44548 -87.738953 38.45709600000001 -87.758659 38.466125000000005 -87.756096 38.48153300000001 -87.692818 38.50400500000001 -87.679909 38.50044299999999 -87.653534 38.51536899999999 -87.65139 38.54742400000001 -87.672943 38.573871999999994 -87.652855 38.593177999999995 -87.640594 38.599209 -87.619827 38.622917 -87.628647 38.642810999999995 -87.625191 38.672169 -87.588478 38.68597399999999 -87.543892 38.73663300000001 -87.508316 38.769722 -87.508003 38.77669900000001 -87.519028 38.795559 -87.507889 38.857890999999995 -87.550507 38.869811999999996 -87.559059 38.90486100000001 -87.5392 38.93191899999999 -87.530182 38.96370300000001 -87.53347 38.97707700000001 -87.547905 38.99408299999999 -87.591858 38.995743000000004 -87.581749 39.062434999999994 -87.58532 39.08460600000001 -87.612007 39.08897400000001 -87.630867 39.10394299999999 -87.631668 39.11346800000001 -87.662262 39.130652999999995 -87.659454 39.146679000000006 -87.670326 39.168507000000005 -87.644257 39.196068 -87.607925 39.198128 -87.594208 39.20846599999999 -87.588593 39.248752999999994 -87.584564 39.258162999999996 -87.606895 39.281418 -87.615799 39.297661000000005 -87.610619 39.30740399999999 -87.625237 39.338268 -87.597664 39.350525000000005 -87.540215 39.47744800000001 -87.538567 39.609341 -87.535576 39.887302000000005 -87.535774 40.16619499999999 -87.535339 40.48324600000001 -87.535675 40.494609999999994 -87.53717 40.74541099999999 -87.532669 41.00993 -87.532021 41.173756 -87.531731 41.30130399999999 -87.532448 41.46971500000001 -87.532646 41.723591 -87.529861 41.67158900000001 -87.463669 41.676331000000005 -87.419266 41.65807699999999 -87.441933 41.63415499999999 -87.394707 41.62615199999999 -87.233818 41.716465 -86.942429 41.765465000000006 -86.834801 41.76549499999999 -86.525162 41.764815999999996 -86.234558 41.764579999999995 -86.068298 41.763485 -85.799232 41.76257699999999 -85.65947 41.763531 -85.297234 41.76281700000001 -85.193169 41.76182600000001 -84.82605 41.760909999999996 -84.788521 41.69744499999999 -84.790421 41.530440999999996 -84.791412 41.42784900000001 -84.791939 41.28376800000001 -84.791016 41.253082000000006 -84.791626 40.988293 -84.790565 40.937659999999994 -84.791061 40.728813 -84.793098 40.58869200000001 -84.793289 40.353004 -84.794594 40.31945400000001 -84.795425 40.01394300000001 -84.803604 39.91711799999999 -84.80619 39.73325 -84.808739 39.563998999999995 -84.811081 39.51311100000001 -84.81115 39.31227899999999 -84.812073 39.302975 -84.812119 39.10252800000001 -84.811531 39.10363000000001 -84.827911 39.064987 -84.886757 39.050591 -84.890045 39.032837 -84.876343 39.00577200000001 -84.844276 38.98271600000001 -84.834496 38.95457099999999 -84.846367 38.92754400000001 -84.875931 38.909369999999996 -84.875305 38.901981000000006 -84.859795 38.89712900000001 -84.803276 38.884322999999995 -84.788719 38.866581 -84.787498 38.83439999999999 -84.824478 38.793346000000014 -84.818832 38.78057899999999 -84.975662 38.764229 -85.025124 38.750361999999996 -85.068504 38.714077 -85.119705 38.69511399999999 -85.16098 38.69575499999999 -85.205208 38.744316 -85.271439 38.73694599999999 -85.335052 38.738358000000005 -85.418228 38.72478100000001 -85.446732 38.694614 -85.45372 38.584778 -85.424446 38.56141299999999 -85.417503 38.53695300000001 -85.432411 38.518112 -85.466423 38.47135499999999 -85.50724 38.446606 -85.612679 38.383621000000005 -85.643631 38.33768499999999 -85.654266 38.300883999999996 -85.681427 38.27024499999999 -85.746964 38.28232199999999 -85.786247 38.28611000000001 -85.806587 38.27622199999999 -85.839943 38.23849100000001 -85.852371 38.17993200000001 -85.912109 38.06480400000001 -85.914787 38.03397799999999 -85.930908 38.01176799999999 -85.958618 38.001693999999986 -86.006699 37.992878000000005 -86.031654 37.966713 -86.05275 38.01126500000001 -86.105019 38.01768899999999 -86.190651 38.04065299999999 -86.252182 38.05810500000001 -86.277725 38.07842299999999 -86.291466 38.150238 -86.297699 38.177223 -86.341629</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Indiana</topp:STATE_NAME><topp:STATE_FIPS>18</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>IN</topp:STATE_ABBR><topp:LAND_KM>92903.666</topp:LAND_KM><topp:WATER_KM>882.327</topp:WATER_KM><topp:PERSONS>5544159.0</topp:PERSONS><topp:FAMILIES>1480351.0</topp:FAMILIES><topp:HOUSHOLD>2065355.0</topp:HOUSHOLD><topp:MALE>2688281.0</topp:MALE><topp:FEMALE>2855878.0</topp:FEMALE><topp:WORKERS>2023242.0</topp:WORKERS><topp:DRVALONE>2040232.0</topp:DRVALONE><topp:CARPOOL>332386.0</topp:CARPOOL><topp:PUBTRANS>34117.0</topp:PUBTRANS><topp:EMPLOYED>2628695.0</topp:EMPLOYED><topp:UNEMPLOY>160143.0</topp:UNEMPLOY><topp:SERVICE>746029.0</topp:SERVICE><topp:MANUAL>518252.0</topp:MANUAL><topp:P_MALE>0.485</topp:P_MALE><topp:P_FEMALE>0.515</topp:P_FEMALE><topp:SAMP_POP>891110.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.45"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>34.998913 -119.995277</gml:lowerCorner><gml:upperCorner>41.99674200000001 -114.036598</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.411883999999986 -119.151466 38.527195000000006 -119.31783300000001 38.703002999999995 -119.57468399999999 38.92234400000001 -119.888329 38.99419800000001 -119.99423999999999 39.063582999999994 -119.994133 39.106224 -119.99352300000001 39.158806 -119.994507 39.31163799999999 -119.99427800000001 39.443591999999995 -119.99498 39.720702999999986 -119.995132 41.177670000000006 -119.995277 41.989315000000005 -119.992401 41.98895999999999 -119.350655 41.98924299999999 -119.309906 41.99674200000001 -118.184326 41.99490399999999 -117.01790600000001 41.99490399999999 -116.991356 41.994698 -115.946617 41.99659700000001 -115.023979 41.99601000000001 -114.268616 41.995476 -114.038223 40.997761 -114.037308 40.111110999999994 -114.03727 39.90884 -114.039009 39.53874200000001 -114.039276 38.67904300000001 -114.043449 38.571140000000014 -114.044273 38.13769099999999 -114.046463 37.598507 -114.046448 36.996562999999995 -114.043137 36.84187300000001 -114.042915 36.216038 -114.036598 36.193993000000006 -114.044312 36.121105 -114.106979 36.041743999999994 -114.128227 36.017269 -114.205971 36.018345 -114.232674 36.062248 -114.306786 36.087124 -114.303055 36.111453999999995 -114.315292 36.137496999999996 -114.34343 36.151009 -114.379997 36.121071 -114.443138 36.124729 -114.465805 36.155108999999996 -114.529762 36.138355000000004 -114.598122 36.141987 -114.620796 36.105202000000006 -114.711945 36.085983 -114.727333 36.058773 -114.728149 36.036777 -114.716858 35.987667 -114.735397 35.911629000000005 -114.698463 35.880489 -114.660789 35.870975 -114.661652 35.847458 -114.689056 35.764717000000005 -114.681931 35.732609 -114.688011 35.693111 -114.664284 35.656409999999994 -114.667679 35.646595000000005 -114.653259 35.61135899999999 -114.639061 35.584843000000006 -114.652328 35.546645999999996 -114.648987 35.515761999999995 -114.67141 35.450768 -114.644592 35.358383 -114.588783 35.304771 -114.58709 35.220184 -114.558784 35.174347 -114.560242 35.140068 -114.571457 35.132560999999995 -114.581818 35.133906999999994 -114.625641 35.118656 -114.635109 35.076057000000006 -114.594833 35.041861999999995 -114.63298 34.998913 -114.62027 35.795722999999995 -115.625359 36.001259000000005 -115.884918 36.959655999999995 -117.15952300000001 37.457367000000005 -117.837761 37.886745000000005 -118.416466 38.411883999999986 -119.151466</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Nevada</topp:STATE_NAME><topp:STATE_FIPS>32</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>NV</topp:STATE_ABBR><topp:LAND_KM>284397.191</topp:LAND_KM><topp:WATER_KM>1971.049</topp:WATER_KM><topp:PERSONS>1201833.0</topp:PERSONS><topp:FAMILIES>307400.0</topp:FAMILIES><topp:HOUSHOLD>466297.0</topp:HOUSHOLD><topp:MALE>611880.0</topp:MALE><topp:FEMALE>589953.0</topp:FEMALE><topp:WORKERS>475643.0</topp:WORKERS><topp:DRVALONE>443571.0</topp:DRVALONE><topp:CARPOOL>93635.0</topp:CARPOOL><topp:PUBTRANS>16096.0</topp:PUBTRANS><topp:EMPLOYED>607437.0</topp:EMPLOYED><topp:UNEMPLOY>40083.0</topp:UNEMPLOY><topp:SERVICE>222792.0</topp:SERVICE><topp:MANUAL>67743.0</topp:MANUAL><topp:P_MALE>0.509</topp:P_MALE><topp:P_FEMALE>0.491</topp:P_FEMALE><topp:SAMP_POP>159997.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.46"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>36.991749 -114.046463</gml:lowerCorner><gml:upperCorner>42.002376999999996 -109.042542</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.13769099999999 -114.046463 38.571140000000014 -114.044273 38.67904300000001 -114.043449 39.53874200000001 -114.039276 39.90884 -114.039009 40.111110999999994 -114.03727 40.997761 -114.037308 41.995476 -114.038223 42.001228 -112.988754 41.99913000000001 -112.146324 42.002376999999996 -112.099724 42.00023999999999 -111.49382 41.996264999999994 -111.047951 41.57864799999999 -111.050323 41.25847999999999 -111.050911 40.996635 -111.050285 40.99794 -110.062477 40.997646 -110.001457 40.99847399999999 -109.047638 40.665329000000014 -109.045479 40.210544999999996 -109.050591 39.65741 -109.051888 39.51819599999999 -109.052864 39.36098899999999 -109.050751 38.494663 -109.053284 38.24493000000001 -109.055199 38.15294299999999 -109.042801 37.887428 -109.042542 37.630829000000006 -109.044937 36.996643000000006 -109.047821 36.992065 -109.996399 36.991749 -110.451546 37.003929 -110.483398 37.002491000000006 -110.739372 37.001720000000006 -111.355453 36.995506000000006 -112.236511 36.998008999999996 -112.541763 36.99624300000001 -112.899216 36.996562999999995 -114.043137 37.598507 -114.046448 38.13769099999999 -114.046463</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Utah</topp:STATE_NAME><topp:STATE_FIPS>49</topp:STATE_FIPS><topp:SUB_REGION>Mtn</topp:SUB_REGION><topp:STATE_ABBR>UT</topp:STATE_ABBR><topp:LAND_KM>212815.546</topp:LAND_KM><topp:WATER_KM>7086.152</topp:WATER_KM><topp:PERSONS>1722850.0</topp:PERSONS><topp:FAMILIES>410862.0</topp:FAMILIES><topp:HOUSHOLD>537273.0</topp:HOUSHOLD><topp:MALE>855759.0</topp:MALE><topp:FEMALE>867091.0</topp:FEMALE><topp:WORKERS>564185.0</topp:WORKERS><topp:DRVALONE>541226.0</topp:DRVALONE><topp:CARPOOL>111197.0</topp:CARPOOL><topp:PUBTRANS>16971.0</topp:PUBTRANS><topp:EMPLOYED>736059.0</topp:EMPLOYED><topp:UNEMPLOY>41389.0</topp:UNEMPLOY><topp:SERVICE>196289.0</topp:SERVICE><topp:MANUAL>102232.0</topp:MANUAL><topp:P_MALE>0.497</topp:P_MALE><topp:P_FEMALE>0.503</topp:P_FEMALE><topp:SAMP_POP>304592.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.47"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>32.535725 -124.391472</gml:lowerCorner><gml:upperCorner>42.00234599999999 -114.124451</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.16936899999999 -121.664154 38.066856 -121.781296 38.072990000000004 -121.90169499999999 38.13958400000001 -121.983475 38.071158999999994 -122.23116300000001 38.15950000000001 -122.271919 38.206017 -122.314674 38.19366500000001 -122.337822 38.159392999999994 -122.28427099999999 38.097565 -122.27169 38.161418999999995 -122.397377 38.113888 -122.428116 38.113495 -122.48784599999999 38.15075300000001 -122.527557 38.08553699999999 -122.473457 38.018730000000005 -122.505363 37.983031999999994 -122.440697 37.931843 -122.48893699999999 37.834293 -122.457176 37.822177999999994 -122.514641 37.906994 -122.665298 37.894465999999994 -122.690628 38.00774799999999 -122.821091 38.030697 -122.920074 37.990829000000005 -122.95549 37.994536999999994 -123.00962100000001 38.15334300000001 -122.938164 38.29731000000001 -122.993538 38.294224000000014 -123.047684 38.433685 -123.12043 38.547421000000014 -123.29682199999999 38.75774799999999 -123.52276599999999 38.92486199999999 -123.720772 39.041900999999996 -123.68232 39.34791200000001 -123.812592 39.55199400000001 -123.753525 39.687229 -123.78240199999999 39.82652300000001 -123.836975 39.99871400000001 -124.0065 40.10051300000001 -124.093414 40.25257099999999 -124.34414699999999 40.327698 -124.334946 40.435383 -124.391472 40.978363 -124.108269 41.128986 -124.148521 41.313987999999995 -124.070419 41.45832100000001 -124.05677 41.727355999999986 -124.143021 41.77691999999999 -124.241905 41.84849199999999 -124.206306 41.997814000000005 -124.205246 41.99311399999999 -123.81796299999999 41.99799300000001 -123.512032 42.00234599999999 -123.22094 42.0009 -122.28357700000001 41.99445700000001 -121.440407 41.98778899999999 -120.87082699999999 41.989315000000005 -119.992401 41.177670000000006 -119.995277 39.720702999999986 -119.995132 39.443591999999995 -119.99498 39.31163799999999 -119.99427800000001 39.158806 -119.994507 39.106224 -119.99352300000001 39.063582999999994 -119.994133 38.99419800000001 -119.99423999999999 38.92234400000001 -119.888329 38.703002999999995 -119.57468399999999 38.527195000000006 -119.31783300000001 38.411883999999986 -119.151466 37.886745000000005 -118.416466 37.457367000000005 -117.837761 36.959655999999995 -117.15952300000001 36.001259000000005 -115.884918 35.795722999999995 -115.625359 34.998913 -114.62027 34.99764999999999 -114.631477 34.943607 -114.620209 34.919498000000004 -114.629677 34.87553 -114.626465 34.831856 -114.56942 34.759952999999996 -114.541245 34.74890499999999 -114.524757 34.744750999999994 -114.497009 34.709866000000005 -114.464844 34.610885999999994 -114.421478 34.598952999999995 -114.43351 34.583714 -114.408951 34.536552 -114.376038 34.477074 -114.383072 34.459666999999996 -114.375717 34.454860999999994 -114.331848 34.43574099999999 -114.302078 34.41205600000001 -114.282608 34.40547599999999 -114.257057 34.36519199999999 -114.181297 34.336433 -114.152634 34.314533 -114.133347 34.272605999999996 -114.124451 34.266964 -114.149132 34.186206999999996 -114.234993 34.171215000000004 -114.284584 34.141281000000006 -114.322014 34.102638 -114.409378 34.078316 -114.423241 34.029827 -114.428192 33.965046 -114.517418 33.95239599999999 -114.524841 33.925017999999994 -114.497398 33.86290700000001 -114.520172 33.84194599999999 -114.510933 33.826012000000006 -114.520332 33.771694 -114.503769 33.743179 -114.509499 33.708347 -114.494888 33.68271300000001 -114.535645 33.665482 -114.524475 33.622112 -114.526382 33.580482 -114.539459 33.560047 -114.528633 33.509418 -114.586273 33.486098999999996 -114.597298 33.468571 -114.6203 33.439396 -114.629784 33.41908599999999 -114.644302 33.41103 -114.724144 33.352385999999996 -114.702812 33.305676000000005 -114.734634 33.267982 -114.676903 33.239222999999996 -114.68692 33.22456 -114.67926 33.167213000000004 -114.67733 33.122337 -114.708672 33.095344999999995 -114.710564 33.038883 -114.663162 33.04437299999999 -114.644371 33.03352700000001 -114.633179 33.026962 -114.609138 33.036743 -114.558304 33.027668000000006 -114.519844 32.977749 -114.467606 32.935867 -114.475662 32.845378999999994 -114.460655 32.809867999999994 -114.525436 32.788002000000006 -114.534294 32.771366 -114.529312 32.771187 -114.542404 32.760704000000004 -114.542221 32.760707999999994 -114.560799 32.74889 -114.559967 32.748783 -114.571426 32.737392 -114.571175 32.735839999999996 -114.602737 32.726237999999995 -114.603157 32.741378999999995 -114.693253 32.734966 -114.711906 32.72081 -114.72126 32.61940800000001 -116.10612499999999 32.535725 -117.127235 32.718391 -117.198944 32.602818 -117.119743 32.678878999999995 -117.123665 32.738884 -117.197906 32.680042 -117.247337 32.851173 -117.284454 32.888126 -117.253998 33.111442999999994 -117.327568 33.234055 -117.40927099999999 33.394504999999995 -117.596451 33.747547 -118.10582 33.773906999999994 -118.245712 33.703888000000006 -118.285988 33.738434 -118.40418199999999 33.77543300000001 -118.42804699999999 33.812309 -118.387268 33.882954 -118.411201 34.037243000000004 -118.54093900000001 34.018249999999995 -118.787193 34.04007300000001 -118.93843100000001 34.146336000000005 -119.215393 34.238094000000004 -119.265823 34.374866 -119.482056 34.416439 -119.605331 34.404799999999994 -119.868462 34.46167 -120.010521 34.471912 -120.139183 34.442513000000005 -120.455208 34.521389 -120.508408 34.57235300000001 -120.640289 34.704041000000004 -120.60062400000001 34.75992599999999 -120.630669 34.855637 -120.607155 34.903831 -120.66494 34.972660000000005 -120.643333 35.074841000000006 -120.61576099999999 35.140052999999995 -120.637405 35.209278 -120.86032900000001 35.259429999999995 -120.88258400000001 35.364563000000004 -120.848984 35.427792 -120.874199 35.456608 -120.990929 35.629352999999995 -121.145531 35.663567 -121.269226 35.801067 -121.328041 35.879887 -121.444496 36.181171000000006 -121.688751 36.306979999999996 -121.88121000000001 36.582813 -121.954216 36.640468999999996 -121.91035500000001 36.607754 -121.866318 36.648262 -121.807503 36.819035 -121.76033000000001 36.85037199999999 -121.790649 36.962147 -121.882469 36.947556000000006 -122.06025700000001 37.000918999999996 -122.17236299999999 37.106834000000006 -122.273552 37.239182 -122.413551 37.35247 -122.38816800000001 37.479541999999995 -122.440376 37.522964 -122.504593 37.700321 -122.497124 37.783012 -122.497131 37.808696999999995 -122.399849 37.725291999999996 -122.34539000000001 37.702518 -122.36525 37.609852000000004 -122.358589 37.452602 -122.088234 37.460781 -121.974266 37.497375000000005 -122.091949 37.735271 -122.198654 37.77853399999999 -122.31133299999999 37.89183800000001 -122.306473 37.90942 -122.37041500000001 37.973522 -122.378601 38.01487399999999 -122.294441 38.057232 -121.99955 38.023574999999994 -121.697891 38.08618200000001 -121.65668500000001 38.09421900000001 -121.57582099999999 38.063747000000006 -121.56848099999999 38.06355300000001 -121.54641000000001 38.113879999999995 -121.57177 38.13744399999999 -121.55308500000001 38.09654599999999 -121.65851599999999 38.16936899999999 -121.664154</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>34.075226 -119.866859 34.021339 -119.666962 34.055779 -119.571632 34.034588 -119.522141 34.006493000000006 -119.538422 33.965278999999995 -119.711578 33.968410000000006 -119.84631300000001 34.004665 -119.88810000000001 34.03187200000001 -119.873024 34.059177000000005 -119.926727 34.075226 -119.866859</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>33.92416 -120.16642 34.010887 -120.237579 34.04110300000001 -120.045837 33.94775799999999 -119.962425 33.89481000000001 -120.108215 33.92416 -120.16642</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>33.480793000000006 -118.593864 33.410984 -118.36148800000001 33.334419 -118.29368600000001 33.30746499999999 -118.30313100000001 33.324757000000005 -118.454475 33.41952499999999 -118.48043100000001 33.434456 -118.555519 33.480793000000006 -118.593864</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>32.819157000000004 -118.350052 32.806076000000004 -118.419197 32.892039999999994 -118.51076499999999 33.020988 -118.598602 33.035938 -118.570572 32.987350000000006 -118.540672 32.819157000000004 -118.350052</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>California</topp:STATE_NAME><topp:STATE_FIPS>06</topp:STATE_FIPS><topp:SUB_REGION>Pacific</topp:SUB_REGION><topp:STATE_ABBR>CA</topp:STATE_ABBR><topp:LAND_KM>403970.143</topp:LAND_KM><topp:WATER_KM>20023.368</topp:WATER_KM><topp:PERSONS>2.9760021E7</topp:PERSONS><topp:FAMILIES>7139394.0</topp:FAMILIES><topp:HOUSHOLD>1.0381206E7</topp:HOUSHOLD><topp:MALE>1.4897627E7</topp:MALE><topp:FEMALE>1.4862394E7</topp:FEMALE><topp:WORKERS>1.1306576E7</topp:WORKERS><topp:DRVALONE>9982242.0</topp:DRVALONE><topp:CARPOOL>2036025.0</topp:CARPOOL><topp:PUBTRANS>685797.0</topp:PUBTRANS><topp:EMPLOYED>1.3996309E7</topp:EMPLOYED><topp:UNEMPLOY>996502.0</topp:UNEMPLOY><topp:SERVICE>3664771.0</topp:SERVICE><topp:MANUAL>1798201.0</topp:MANUAL><topp:P_MALE>0.501</topp:P_MALE><topp:P_FEMALE>0.499</topp:P_FEMALE><topp:SAMP_POP>3792553.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.48"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>38.400429 -84.812119</gml:lowerCorner><gml:upperCorner>41.986816000000005 -80.520203</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>38.60918000000001 -83.27285 38.59656100000001 -83.290138 38.59624099999999 -83.306625 38.60648699999999 -83.320419 38.631912 -83.330116 38.654922 -83.371513 38.663700000000006 -83.453705 38.69006300000001 -83.50016 38.69603699999999 -83.526642 38.677898 -83.618462 38.664897999999994 -83.633324 38.63578799999999 -83.643272 38.623806 -83.655838 38.62085300000001 -83.678612 38.635479000000004 -83.712906 38.650745 -83.770302 38.693771 -83.790543 38.71180699999999 -83.837608 38.744845999999995 -83.857628 38.757889000000006 -83.912613 38.77757600000001 -83.962234 38.763664000000006 -84.053871 38.765434 -84.088936 38.78842900000001 -84.176819 38.812622000000005 -84.228767 38.87448900000001 -84.235359 38.91741200000001 -84.261589 38.94447299999999 -84.290199 39.01401100000001 -84.313377 39.03774999999999 -84.34584 39.035683000000006 -84.391373 39.04727600000001 -84.4198 39.084663000000006 -84.425743 39.111766999999986 -84.444977 39.107304 -84.492111 39.094134999999994 -84.515358 39.07020600000001 -84.593124 39.07487499999999 -84.622704 39.08956499999999 -84.667542 39.14200600000001 -84.742928 39.106976 -84.789978 39.10252800000001 -84.811531 39.302975 -84.812119 39.31227899999999 -84.812073 39.51311100000001 -84.81115 39.563998999999995 -84.811081 39.73325 -84.808739 39.91711799999999 -84.80619 40.01394300000001 -84.803604 40.31945400000001 -84.795425 40.353004 -84.794594 40.58869200000001 -84.793289 40.728813 -84.793098 40.937659999999994 -84.791061 40.988293 -84.790565 41.253082000000006 -84.791626 41.28376800000001 -84.791016 41.42784900000001 -84.791939 41.530440999999996 -84.791412 41.69744499999999 -84.790421 41.7071 -84.384445 41.707989 -84.359261 41.71594200000001 -83.868698 41.71699100000001 -83.764015 41.725081999999986 -83.482758 41.62604099999999 -83.153824 41.53814299999999 -83.003517 41.537598 -82.795921 41.50736599999999 -82.784798 41.45605900000001 -83.07048 41.429417 -82.909019 41.450474000000014 -82.717041 41.391289 -82.548943 41.43145799999999 -82.341499 41.51526999999999 -82.015739 41.50187700000001 -81.962044 41.49110400000001 -81.738647 41.63166799999999 -81.478424 41.72423900000001 -81.362434 41.85020399999999 -80.999954 41.986816000000005 -80.520798 41.850719 -80.52285 41.495048999999995 -80.524132 41.48923500000001 -80.520203 41.12957 -80.52314 40.897209000000004 -80.521133 40.854107 -80.520515 40.63713799999999 -80.522209 40.615910000000014 -80.574623 40.61999900000001 -80.611755 40.61391800000001 -80.637543 40.58207300000001 -80.667931 40.56821400000001 -80.668823 40.539139000000006 -80.633644 40.50439800000001 -80.625458 40.48047299999999 -80.602036 40.39815899999999 -80.628052 40.38859600000001 -80.629448 40.37320700000001 -80.609451 40.306174999999996 -80.604721 40.276432 -80.614891 40.245609 -80.650314 40.194080000000014 -80.678757 40.16811000000001 -80.701088 40.15401800000001 -80.702263 40.03559100000001 -80.738434 39.98340200000001 -80.739082 39.94694100000001 -80.763252 39.92119199999999 -80.759079 39.913239000000004 -80.768318 39.91976500000001 -80.796211 39.915829 -80.808029 39.90482700000001 -80.812325 39.87227200000001 -80.791039 39.85664700000001 -80.798714 39.83959200000001 -80.826103 39.808926000000014 -80.81929 39.759918 -80.870911 39.73625899999999 -80.856636 39.71875800000001 -80.832481 39.70332300000001 -80.83297 39.680274999999995 -80.863594 39.66233399999999 -80.872925 39.62400400000001 -80.881287 39.60727700000001 -80.912766 39.606865 -80.932785 39.58172999999999 -80.983818 39.54406700000001 -81.032738 39.532589 -81.037552 39.496376 -81.098412 39.46770900000001 -81.117256 39.437725 -81.180733 39.415820999999994 -81.20047 39.40828300000001 -81.225113 39.388397 -81.237785 39.38699700000001 -81.28418 39.35356899999999 -81.338997 39.34561500000001 -81.376076 39.40594899999999 -81.434135 39.410954000000004 -81.448112 39.40678399999999 -81.465164 39.352633999999995 -81.540802 39.332581000000005 -81.557541 39.26584199999999 -81.572838 39.27042 -81.667671 39.26015100000001 -81.689674 39.219944 -81.698051 39.21319199999999 -81.723221 39.17567399999999 -81.759056 39.125797000000006 -81.74485 39.09464299999999 -81.753708 39.077179 -81.786507 39.07693900000001 -81.81971 39.066338 -81.824417 39.044028999999995 -81.813606 39.01675 -81.775826 38.96844899999999 -81.781876 38.93009900000001 -81.762444 38.92348100000001 -81.783371 38.948387 -81.823921 38.93780899999999 -81.841057 38.885628 -81.866943 38.87337099999999 -81.892838 38.884365 -81.91539 38.89466100000001 -81.931992 38.932143999999994 -81.89875 38.98419200000001 -81.927971 38.991096 -81.937874 38.992928000000006 -81.975327 39.01518200000001 -81.999817 39.014061 -82.043022 38.98898700000001 -82.05864 38.97711899999999 -82.085152 38.95201499999999 -82.101341 38.89931899999999 -82.13945 38.838707 -82.146233 38.804539000000005 -82.197853 38.778858000000014 -82.216881 38.71021999999999 -82.184105 38.67781099999999 -82.18911 38.63210699999999 -82.17379 38.59494799999999 -82.18438 38.58475100000001 -82.213791 38.594807 -82.271027 38.57999799999999 -82.2901 38.46514500000001 -82.314369 38.441868 -82.329308 38.42838699999999 -82.39489 38.430308999999994 -82.415016 38.40575000000001 -82.49511 38.400429 -82.547668 38.403819999999996 -82.575539 38.41243700000001 -82.586723 38.472588 -82.613861 38.50206 -82.669876 38.539063 -82.695694 38.552986000000004 -82.742058 38.557209 -82.802475 38.571583000000004 -82.827103 38.60038 -82.853966 38.65231700000001 -82.860138 38.683223999999996 -82.880119 38.718929 -82.873299 38.74269899999999 -82.890419 38.74633800000001 -82.92141 38.719565999999986 -82.972588 38.714436000000006 -83.027046 38.68565000000001 -83.060982 38.66475700000001 -83.111343 38.61926299999999 -83.14325 38.60976400000001 -83.182037 38.62409600000001 -83.24511 38.60918000000001 -83.27285</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Ohio</topp:STATE_NAME><topp:STATE_FIPS>39</topp:STATE_FIPS><topp:SUB_REGION>E N Cen</topp:SUB_REGION><topp:STATE_ABBR>OH</topp:STATE_ABBR><topp:LAND_KM>105012.073</topp:LAND_KM><topp:WATER_KM>2001.617</topp:WATER_KM><topp:PERSONS>9980887.0</topp:PERSONS><topp:FAMILIES>2673875.0</topp:FAMILIES><topp:HOUSHOLD>3748665.0</topp:HOUSHOLD><topp:MALE>4816445.0</topp:MALE><topp:FEMALE>5164442.0</topp:FEMALE><topp:WORKERS>3638837.0</topp:WORKERS><topp:DRVALONE>3581182.0</topp:DRVALONE><topp:CARPOOL>477421.0</topp:CARPOOL><topp:PUBTRANS>98938.0</topp:PUBTRANS><topp:EMPLOYED>4524383.0</topp:EMPLOYED><topp:UNEMPLOY>324867.0</topp:UNEMPLOY><topp:SERVICE>1206763.0</topp:SERVICE><topp:MANUAL>830481.0</topp:MANUAL><topp:P_MALE>0.483</topp:P_MALE><topp:P_FEMALE>0.517</topp:P_FEMALE><topp:SAMP_POP>1727359.0</topp:SAMP_POP></topp:states><topp:states gml:id="states.49"><gml:boundedBy><gml:Envelope srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:lowerCorner>45.543251 -124.73142200000001</gml:lowerCorner><gml:upperCorner>49.00000399999999 -116.91815199999999</gml:upperCorner></gml:Envelope></gml:boundedBy><topp:the_geom><gml:MultiSurface srsDimension="2" srsName="urn:x-ogc:def:crs:EPSG:4326"><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>48.22539499999999 -122.40074899999999 48.228542000000004 -122.461586 48.12867399999999 -122.453156 48.060280000000006 -122.36007699999999 48.13415499999999 -122.513245 48.21064000000001 -122.540802 48.25397100000001 -122.507858 48.24677299999999 -122.403137 48.289897999999994 -122.37705199999999 48.414421000000004 -122.563087 48.413071 -122.665749 48.49450300000001 -122.698128 48.51899700000001 -122.60689500000001 48.458575999999994 -122.52195 48.462368 -122.472557 48.559616000000005 -122.504021 48.59956700000001 -122.428268 48.63874100000001 -122.486519 48.711895 -122.52527599999999 48.75809100000001 -122.51557199999999 48.80318500000001 -122.696114 48.910156 -122.75295299999999 48.95089300000001 -122.821129 48.955974999999995 -122.74265299999999 48.999911999999995 -122.76383200000001 48.99997300000001 -120.855827 49.0 -118.842453 49.00000399999999 -118.19923399999999 48.99999600000001 -117.4375 49.0 -117.030975 48.83815000000001 -117.028046 48.046284000000014 -117.037834 47.971191000000005 -117.036438 47.361541999999986 -117.040779 47.258601999999996 -117.041382 47.11942300000001 -117.039963 46.53671600000001 -117.040932 46.42809700000001 -117.037567 46.388690999999994 -117.04348 46.348816 -117.06319400000001 46.335544999999996 -117.026985 46.30256700000001 -117.00065599999999 46.249427999999995 -116.97174100000001 46.197674000000006 -116.96650700000001 46.165604 -116.928444 46.097397 -116.960655 46.078632 -116.98622900000001 46.065811 -116.956741 45.995299999999986 -116.91815199999999 45.999966 -117.48065199999999 46.00040100000001 -117.60180700000001 46.00001900000001 -117.981628 46.001778 -117.991478 45.999202999999994 -118.981018 45.966419 -119.031105 45.92585399999999 -119.13913 45.92249699999999 -119.17761999999999 45.932807999999994 -119.301636 45.917755 -119.378311 45.914412999999996 -119.43772899999999 45.89934500000001 -119.511086 45.913459999999986 -119.58815799999999 45.89955499999999 -119.620979 45.85268400000001 -119.677307 45.841755000000006 -119.832413 45.83184399999999 -119.86859100000001 45.81128699999999 -119.993172 45.78035 -120.067497 45.76141000000001 -120.154755 45.719933 -120.206291 45.71673200000001 -120.282478 45.68942999999999 -120.442223 45.69578200000001 -120.49799300000001 45.74107000000001 -120.568916 45.743763 -120.622589 45.732765 -120.657234 45.71066300000001 -120.695824 45.665339999999986 -120.86024499999999 45.63563199999999 -120.906761 45.65047100000001 -120.947395 45.645309 -120.9673 45.65299999999999 -121.032303 45.64676700000001 -121.07235 45.607215999999994 -121.124023 45.600674 -121.173134 45.61340000000001 -121.190872 45.657444 -121.20212599999999 45.66580200000001 -121.213089 45.67849699999999 -121.27520799999999 45.696799999999996 -121.318794 45.69984400000001 -121.366631 45.69076200000001 -121.420845 45.69512599999999 -121.441368 45.719727000000006 -121.52787000000001 45.688953 -121.705231 45.689876999999996 -121.757507 45.70084399999999 -121.809853 45.677017000000006 -121.887093 45.642189 -121.925629 45.63593700000001 -121.97146599999999 45.617985000000004 -121.998817 45.590664000000004 -122.08084099999999 45.548271 -122.243721 45.543251 -122.30194900000001 45.566329999999994 -122.355255 45.56493800000001 -122.435951 45.594978 -122.564224 45.606990999999994 -122.650002 45.63120699999999 -122.69511399999999 45.64955900000001 -122.759331 45.727847999999994 -122.771339 45.76073099999999 -122.76307700000001 45.80050700000001 -122.786797 45.85061300000001 -122.78330199999999 45.86805000000001 -122.78286 45.904236 -122.805008 45.944053999999994 -122.80652599999999 46.02734799999999 -122.874199 46.07949400000001 -122.898537 46.110648999999995 -122.97294600000001 46.155902999999995 -123.04937000000001 46.17947799999999 -123.117325 46.18375399999999 -123.174965 46.170174 -123.211205 46.144188000000014 -123.247566 46.144904999999994 -123.303482 46.275192000000004 -123.469528 46.258835000000005 -123.61882800000001 46.285595 -123.724205 46.240612 -123.884514 46.310452 -123.992065 46.267437 -124.077843 46.639927 -124.064232 46.583721 -124.021767 46.383858000000004 -124.01173399999999 46.404517999999996 -123.840187 46.481292999999994 -123.939423 46.511257 -123.892296 46.61740499999999 -123.956436 46.67324099999999 -123.925194 46.718468 -123.839691 46.74516700000001 -123.89426399999999 46.716038 -124.041878 46.729206000000005 -124.089767 46.78965400000001 -124.100784 46.900172999999995 -124.137543 46.90833699999999 -124.104477 46.87433200000001 -124.103455 46.82395199999999 -124.02752699999999 46.88744 -124.045647 46.96414899999999 -123.81137799999999 46.976574 -123.994583 47.03122300000001 -124.033112 47.042866000000004 -124.111076 46.929801999999995 -124.160751 47.167174999999986 -124.19144399999999 47.27526499999999 -124.230133 47.349434 -124.31813 47.527107 -124.347778 47.63896199999999 -124.372299 47.80845600000001 -124.482719 47.873940000000005 -124.605362 48.150204 -124.73142200000001 48.232212000000004 -124.703857 48.37777299999999 -124.71582000000001 48.35748699999999 -124.562202 48.159355000000005 -123.98989900000001 48.11121700000001 -123.395561 48.14891800000001 -123.121933 48.09436400000001 -122.920311 48.066981999999996 -122.923561 48.13332 -122.839828 48.14417599999999 -122.767601 48.08550600000001 -122.80165099999999 47.91734299999999 -122.660294 47.86461600000001 -122.652321 47.80917400000001 -122.744598 47.80273399999999 -122.788528 47.85726199999999 -122.808243 47.827515000000005 -122.857529 47.67270300000001 -122.898094 47.605659 -122.981476 47.456458999999995 -123.11264800000001 47.34873200000001 -123.152794 47.35320999999999 -123.009209 47.438648 -122.831993 47.356235999999996 -123.034943 47.37175400000001 -123.11142000000001 47.516121 -123.02507 47.614791999999994 -122.91570300000001 47.66087300000001 -122.751678 47.75708399999999 -122.721794 47.85019299999999 -122.60990100000001 47.936375 -122.611954 47.90964500000001 -122.530632 47.75516099999999 -122.472343 47.69715099999999 -122.620255 47.571372999999994 -122.58521300000001 47.583687 -122.55401599999999 47.52291500000001 -122.541458 47.507397 -122.50322 47.39854399999999 -122.55720500000001 47.37410700000001 -122.542885 47.33411000000001 -122.587013 47.283512 -122.551918 47.25156799999999 -122.57929200000001 47.293578999999994 -122.61030600000001 47.27075199999999 -122.60567499999999 47.29226700000001 -122.69850199999999 47.39873499999999 -122.62751 47.398762000000005 -122.636192 47.341633 -122.740303 47.26633799999999 -122.768463 47.22331199999999 -122.718559 47.162678 -122.759995 47.23500799999999 -122.82386 47.33754300000001 -122.772087 47.36092400000001 -122.800934 47.29941600000001 -122.87912 47.20816400000001 -123.11417399999999 47.090239999999994 -123.07994099999999 47.100956 -123.03009 47.048145000000005 -122.921898 47.12604099999999 -122.788803 47.08262300000001 -122.726944 47.09850700000001 -122.698837 47.180240999999995 -122.590569 47.28763599999999 -122.529526 47.31645599999999 -122.545349 47.25965099999999 -122.422859 47.277901000000014 -122.391609 47.30130399999999 -122.440369 47.31902299999999 -122.419601 47.34450100000001 -122.324142 47.390293000000014 -122.31850399999999 47.510422000000005 -122.391396 47.59558899999999 -122.38098099999999 47.66435999999999 -122.413574 47.77435700000001 -122.39325 47.95039700000001 -122.30167399999999 47.969296000000014 -122.22887399999999 48.007622 -122.215744 48.128322999999995 -122.367073 48.22539499999999 -122.40074899999999</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>48.443974 -122.96669 48.479603 -123.093941 48.52202199999999 -123.158424 48.56274400000001 -123.16860199999999 48.62382500000001 -123.13975500000001 48.608554999999996 -123.102425 48.55765500000001 -123.01080300000001 48.533896999999996 -123.007408 48.52711099999999 -122.96669 48.51353800000001 -123.020981 48.489783999999986 -123.017593 48.443974 -122.96669</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember><gml:surfaceMember><gml:Polygon srsDimension="2"><gml:exterior><gml:LinearRing srsDimension="2"><gml:posList>48.276825 -122.731903 48.396953999999994 -122.66433 48.40496400000001 -122.603104 48.32122000000001 -122.524475 48.28368800000001 -122.52737400000001 48.296527999999995 -122.622231 48.225594 -122.730751 48.206501 -122.60965 48.077042000000006 -122.544937 48.09425400000001 -122.49494899999999 48.03233 -122.378738 47.964068999999995 -122.354149 47.904731999999996 -122.385712 47.91824 -122.441536 47.987694000000005 -122.47036 47.967715999999996 -122.543701 48.031616000000014 -122.607361 48.181366 -122.694275 48.218998 -122.767494 48.276825 -122.731903</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember></gml:MultiSurface></topp:the_geom><topp:STATE_NAME>Washington</topp:STATE_NAME><topp:STATE_FIPS>53</topp:STATE_FIPS><topp:SUB_REGION>Pacific</topp:SUB_REGION><topp:STATE_ABBR>WA</topp:STATE_ABBR><topp:LAND_KM>172447.205</topp:LAND_KM><topp:WATER_KM>12226.63</topp:WATER_KM><topp:PERSONS>4866692.0</topp:PERSONS><topp:FAMILIES>1264934.0</topp:FAMILIES><topp:HOUSHOLD>1872431.0</topp:HOUSHOLD><topp:MALE>2413747.0</topp:MALE><topp:FEMALE>2452945.0</topp:FEMALE><topp:WORKERS>1830031.0</topp:WORKERS><topp:DRVALONE>1700872.0</topp:DRVALONE><topp:CARPOOL>282240.0</topp:CARPOOL><topp:PUBTRANS>104403.0</topp:PUBTRANS><topp:EMPLOYED>2293961.0</topp:EMPLOYED><topp:UNEMPLOY>139216.0</topp:UNEMPLOY><topp:SERVICE>637487.0</topp:SERVICE><topp:MANUAL>302635.0</topp:MANUAL><topp:P_MALE>0.496</topp:P_MALE><topp:P_FEMALE>0.504</topp:P_FEMALE><topp:SAMP_POP>736744.0</topp:SAMP_POP></topp:states></gml:featureMembers></wfs:FeatureCollection>
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/data/gpx/fells_loop.gpx b/mapbox-gl/examples/data/gpx/fells_loop.gpx
    new file mode 100644
    index 0000000000..fdfb0d43ce
    --- /dev/null
    +++ b/mapbox-gl/examples/data/gpx/fells_loop.gpx
    @@ -0,0 +1,1077 @@
    +<?xml version="1.0"?>
    +<gpx
    + version="1.0"
    + creator="ExpertGPS 1.1 - http://www.topografix.com"
    + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    + xmlns="http://www.topografix.com/GPX/1/0"
    + xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
    +<time>2002-02-27T17:18:33Z</time>
    +<bounds minlat="42.401051" minlon="-71.126602" maxlat="42.468655" maxlon="-71.102973"/>
    +<wpt lat="42.438878" lon="-71.119277">
    + <ele>44.586548</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5066</name>
    + <desc><![CDATA[5066]]></desc>
    + <sym>Crossing</sym>
    + <type><![CDATA[Crossing]]></type>
    +</wpt>
    +<wpt lat="42.439227" lon="-71.119689">
    + <ele>57.607200</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>5067</name>
    + <desc><![CDATA[5067]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.438917" lon="-71.116146">
    + <ele>44.826904</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>5096</name>
    + <desc><![CDATA[5096]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.443904" lon="-71.122044">
    + <ele>50.594727</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5142</name>
    + <desc><![CDATA[5142]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.447298" lon="-71.121447">
    + <ele>127.711200</ele>
    + <time>2001-06-02T03:26:58Z</time>
    + <name>5156</name>
    + <desc><![CDATA[5156]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.454873" lon="-71.125094">
    + <ele>96.926400</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>5224</name>
    + <desc><![CDATA[5224]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.459079" lon="-71.124988">
    + <ele>82.600800</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>5229</name>
    + <desc><![CDATA[5229]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.456979" lon="-71.124474">
    + <ele>82.905600</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>5237</name>
    + <desc><![CDATA[5237]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.454401" lon="-71.120990">
    + <ele>66.696655</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5254</name>
    + <desc><![CDATA[5254]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.451442" lon="-71.121746">
    + <ele>74.627442</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5258</name>
    + <desc><![CDATA[5258]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.454404" lon="-71.120660">
    + <ele>65.254761</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5264</name>
    + <desc><![CDATA[5264]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.457761" lon="-71.121045">
    + <ele>77.419200</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>526708</name>
    + <desc><![CDATA[526708]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.457089" lon="-71.120313">
    + <ele>74.676000</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>526750</name>
    + <desc><![CDATA[526750]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.456592" lon="-71.119676">
    + <ele>78.713135</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>527614</name>
    + <desc><![CDATA[527614]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.456252" lon="-71.119356">
    + <ele>78.713135</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>527631</name>
    + <desc><![CDATA[527631]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.458148" lon="-71.119135">
    + <ele>68.275200</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>5278</name>
    + <desc><![CDATA[5278]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.459377" lon="-71.117693">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5289</name>
    + <desc><![CDATA[5289]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.464183" lon="-71.119828">
    + <ele>52.997925</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5374FIRE</name>
    + <desc><![CDATA[5374FIRE]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.465650" lon="-71.119399">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>5376</name>
    + <desc><![CDATA[5376]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.439018" lon="-71.114456">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6006</name>
    + <desc><![CDATA[600698]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.438594" lon="-71.114803">
    + <ele>46.028564</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6006BLUE</name>
    + <desc><![CDATA[6006BLUE]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.436757" lon="-71.113223">
    + <ele>37.616943</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6014MEADOW</name>
    + <desc><![CDATA[6014MEADOW]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.441754" lon="-71.113220">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6029</name>
    + <desc><![CDATA[6029]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.436243" lon="-71.109075">
    + <ele>50.292000</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6053</name>
    + <desc><![CDATA[6053]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.439250" lon="-71.107500">
    + <ele>25.603200</ele>
    + <time>2001-06-02T03:26:57Z</time>
    + <name>6066</name>
    + <desc><![CDATA[6066]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.439764" lon="-71.107582">
    + <ele>34.442400</ele>
    + <time>2001-06-02T03:26:57Z</time>
    + <name>6067</name>
    + <desc><![CDATA[6067]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.434766" lon="-71.105874">
    + <ele>30.480000</ele>
    + <time>2001-06-02T03:26:57Z</time>
    + <name>6071</name>
    + <desc><![CDATA[6071]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.433304" lon="-71.106599">
    + <ele>15.240000</ele>
    + <time>2001-06-02T03:26:56Z</time>
    + <name>6073</name>
    + <desc><![CDATA[6073]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.437338" lon="-71.104772">
    + <ele>37.795200</ele>
    + <time>2001-06-02T03:26:57Z</time>
    + <name>6084</name>
    + <desc><![CDATA[6084]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.442196" lon="-71.110975">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6130</name>
    + <desc><![CDATA[6130]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.442981" lon="-71.111441">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:26:58Z</time>
    + <name>6131</name>
    + <desc><![CDATA[6131]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.444773" lon="-71.108882">
    + <ele>62.788800</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6153</name>
    + <desc><![CDATA[6153]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.443592" lon="-71.106301">
    + <ele>55.473600</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6171</name>
    + <desc><![CDATA[6171]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.447804" lon="-71.106624">
    + <ele>62.484000</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6176</name>
    + <desc><![CDATA[6176]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.448448" lon="-71.106158">
    + <ele>62.179200</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6177</name>
    + <desc><![CDATA[6177]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.453415" lon="-71.106783">
    + <ele>69.799200</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6272</name>
    + <desc><![CDATA[6272]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.453434" lon="-71.107253">
    + <ele>73.152000</ele>
    + <time>2001-06-02T03:26:56Z</time>
    + <name>6272</name>
    + <desc><![CDATA[6272]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.458298" lon="-71.106771">
    + <ele>70.104000</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6278</name>
    + <desc><![CDATA[6278]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.451430" lon="-71.105413">
    + <ele>57.564209</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6280</name>
    + <desc><![CDATA[6280]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.453845" lon="-71.105206">
    + <ele>66.696655</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6283</name>
    + <desc><![CDATA[6283]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.459986" lon="-71.106170">
    + <ele>72.945191</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6289</name>
    + <desc><![CDATA[6289]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.457616" lon="-71.105116">
    + <ele>72.847200</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6297</name>
    + <desc><![CDATA[6297]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.467110" lon="-71.113574">
    + <ele>53.644800</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>6328</name>
    + <desc><![CDATA[6328]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.464202" lon="-71.109863">
    + <ele>43.891200</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>6354</name>
    + <desc><![CDATA[6354]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.466459" lon="-71.110067">
    + <ele>48.768000</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>635722</name>
    + <desc><![CDATA[635722]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.466557" lon="-71.109410">
    + <ele>49.072800</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>635783</name>
    + <desc><![CDATA[635783]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.463495" lon="-71.107117">
    + <ele>62.484000</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>6373</name>
    + <desc><![CDATA[6373]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.401051" lon="-71.110241">
    + <ele>3.962400</ele>
    + <time>2001-06-02T03:26:56Z</time>
    + <name>6634</name>
    + <desc><![CDATA[6634]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.432621" lon="-71.106532">
    + <ele>13.411200</ele>
    + <time>2001-06-02T03:26:56Z</time>
    + <name>6979</name>
    + <desc><![CDATA[6979]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.431033" lon="-71.107883">
    + <ele>34.012085</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6997</name>
    + <desc><![CDATA[6997]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</wpt>
    +<wpt lat="42.465687" lon="-71.107360">
    + <ele>87.782400</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>BEAR HILL</name>
    + <cmt>BEAR HILL TOWER</cmt>
    + <desc><![CDATA[Bear Hill Tower]]></desc>
    + <sym>Tall Tower</sym>
    + <type><![CDATA[Tower]]></type>
    +</wpt>
    +<wpt lat="42.430950" lon="-71.107628">
    + <ele>23.469600</ele>
    + <time>2001-06-02T00:18:15Z</time>
    + <name>BELLEVUE</name>
    + <cmt>BELLEVUE</cmt>
    + <desc><![CDATA[Bellevue Parking Lot]]></desc>
    + <sym>Parking Area</sym>
    + <type><![CDATA[Parking]]></type>
    +</wpt>
    +<wpt lat="42.438666" lon="-71.114079">
    + <ele>43.384766</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6016</name>
    + <desc><![CDATA[Bike Loop Connector]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.456469" lon="-71.124651">
    + <ele>89.916000</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>5236BRIDGE</name>
    + <desc><![CDATA[Bridge]]></desc>
    + <sym>Bridge</sym>
    + <type><![CDATA[Bridge]]></type>
    +</wpt>
    +<wpt lat="42.465759" lon="-71.119815">
    + <ele>55.473600</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5376BRIDGE</name>
    + <desc><![CDATA[Bridge]]></desc>
    + <sym>Bridge</sym>
    + <type><![CDATA[Bridge]]></type>
    +</wpt>
    +<wpt lat="42.442993" lon="-71.105878">
    + <ele>52.730400</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6181CROSS</name>
    + <desc><![CDATA[Crossing]]></desc>
    + <sym>Crossing</sym>
    + <type><![CDATA[Crossing]]></type>
    +</wpt>
    +<wpt lat="42.435472" lon="-71.109664">
    + <ele>45.110400</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6042CROSS</name>
    + <desc><![CDATA[Crossing]]></desc>
    + <sym>Crossing</sym>
    + <type><![CDATA[Crossing]]></type>
    +</wpt>
    +<wpt lat="42.458516" lon="-71.103646">
    + <name>DARKHOLLPO</name>
    + <desc><![CDATA[Dark Hollow Pond]]></desc>
    + <sym>Fishing Area</sym>
    +</wpt>
    +<wpt lat="42.443109" lon="-71.112675">
    + <ele>56.083200</ele>
    + <time>2001-06-02T03:26:57Z</time>
    + <name>6121DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.449866" lon="-71.119298">
    + <ele>117.043200</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>5179DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.459629" lon="-71.116524">
    + <ele>69.494400</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5299DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.465485" lon="-71.119148">
    + <ele>56.997600</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>5376DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.462776" lon="-71.109986">
    + <ele>46.939200</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>6353DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.446793" lon="-71.108784">
    + <ele>61.264800</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6155DEAD</name>
    + <desc><![CDATA[Dead End]]></desc>
    + <sym>Danger Area</sym>
    + <type><![CDATA[Dead End]]></type>
    +</wpt>
    +<wpt lat="42.451204" lon="-71.126602">
    + <ele>110.947200</ele>
    + <time>2001-06-02T03:26:59Z</time>
    + <name>GATE14</name>
    + <desc><![CDATA[Gate 14]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.458499" lon="-71.122078">
    + <ele>77.724000</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>GATE16</name>
    + <desc><![CDATA[Gate 16]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.459376" lon="-71.119238">
    + <ele>65.836800</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>GATE17</name>
    + <desc><![CDATA[Gate 17]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.466353" lon="-71.119240">
    + <ele>57.302400</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>GATE19</name>
    + <desc><![CDATA[Gate 19]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.468655" lon="-71.107697">
    + <ele>49.377600</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>GATE21</name>
    + <desc><![CDATA[Gate 21]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.456718" lon="-71.102973">
    + <ele>81.076800</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>GATE24</name>
    + <desc><![CDATA[Gate 24]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.430847" lon="-71.107690">
    + <ele>21.515015</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>GATE5</name>
    + <desc><![CDATA[Gate 5]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Truck Stop]]></type>
    +</wpt>
    +<wpt lat="42.431240" lon="-71.109236">
    + <ele>26.561890</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>GATE6</name>
    + <desc><![CDATA[Gate 6]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Trail Head]]></type>
    +</wpt>
    +<wpt lat="42.439502" lon="-71.106556">
    + <ele>32.004000</ele>
    + <time>2001-06-02T00:18:16Z</time>
    + <name>6077LOGS</name>
    + <desc><![CDATA[Log Crossing]]></desc>
    + <sym>Amusement Park</sym>
    + <type><![CDATA[Obstacle]]></type>
    +</wpt>
    +<wpt lat="42.449765" lon="-71.122320">
    + <ele>119.809082</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5148NANEPA</name>
    + <desc><![CDATA[Nanepashemet Road Crossing]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Trail Head]]></type>
    +</wpt>
    +<wpt lat="42.457388" lon="-71.119845">
    + <ele>73.761600</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>5267OBSTAC</name>
    + <desc><![CDATA[Obstacle]]></desc>
    + <sym>Amusement Park</sym>
    + <type><![CDATA[Obstacle]]></type>
    +</wpt>
    +<wpt lat="42.434980" lon="-71.109942">
    + <ele>45.307495</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>PANTHRCAVE</name>
    + <desc><![CDATA[Panther Cave]]></desc>
    + <sym>Tunnel</sym>
    + <type><![CDATA[Tunnel]]></type>
    +</wpt>
    +<wpt lat="42.453256" lon="-71.121211">
    + <ele>77.992066</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5252PURPLE</name>
    + <desc><![CDATA[Purple Rock Hill]]></desc>
    + <sym>Summit</sym>
    + <type><![CDATA[Summit]]></type>
    +</wpt>
    +<wpt lat="42.457734" lon="-71.117481">
    + <ele>67.970400</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5287WATER</name>
    + <desc><![CDATA[Reservoir]]></desc>
    + <sym>Swimming Area</sym>
    + <type><![CDATA[Reservoir]]></type>
    +</wpt>
    +<wpt lat="42.459278" lon="-71.124574">
    + <ele>81.076800</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>5239ROAD</name>
    + <desc><![CDATA[Road]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.458782" lon="-71.118991">
    + <ele>67.360800</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5278ROAD</name>
    + <desc><![CDATA[Road]]></desc>
    + <sym>Truck Stop</sym>
    + <type><![CDATA[Road]]></type>
    +</wpt>
    +<wpt lat="42.439993" lon="-71.120925">
    + <ele>53.949600</ele>
    + <time>2001-06-02T00:18:14Z</time>
    + <name>5058ROAD</name>
    + <cmt>ROAD CROSSING</cmt>
    + <desc><![CDATA[Road Crossing]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Road Crossing]]></type>
    +</wpt>
    +<wpt lat="42.453415" lon="-71.106782">
    + <ele>69.799200</ele>
    + <time>2001-06-02T00:18:13Z</time>
    + <name>SHEEPFOLD</name>
    + <desc><![CDATA[Sheepfold Parking Lot]]></desc>
    + <sym>Parking Area</sym>
    + <type><![CDATA[Parking]]></type>
    +</wpt>
    +<wpt lat="42.455956" lon="-71.107483">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>SOAPBOX</name>
    + <desc><![CDATA[Soap Box Derby Track]]></desc>
    + <sym>Cemetery</sym>
    + <type><![CDATA[Intersection]]></type>
    +</wpt>
    +<wpt lat="42.465913" lon="-71.119328">
    + <ele>64.533692</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5376STREAM</name>
    + <desc><![CDATA[Stream Crossing]]></desc>
    + <sym>Bridge</sym>
    + <type><![CDATA[Bridge]]></type>
    +</wpt>
    +<wpt lat="42.445359" lon="-71.122845">
    + <ele>61.649902</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5144SUMMIT</name>
    + <desc><![CDATA[Summit]]></desc>
    + <sym>Summit</sym>
    + <type><![CDATA[Summit]]></type>
    +</wpt>
    +<wpt lat="42.441727" lon="-71.121676">
    + <ele>67.360800</ele>
    + <time>2001-06-02T00:18:16Z</time>
    + <name>5150TANK</name>
    + <cmt>WATER TANK</cmt>
    + <desc><![CDATA[Water Tank]]></desc>
    + <sym>Museum</sym>
    + <type><![CDATA[Water Tank]]></type>
    +</wpt>
    +<rte>
    + <name>BELLEVUE</name>
    + <desc><![CDATA[Bike Loop Bellevue]]></desc>
    + <number>1</number>
    +<rtept lat="42.430950" lon="-71.107628">
    + <ele>23.469600</ele>
    + <time>2001-06-02T00:18:15Z</time>
    + <name>BELLEVUE</name>
    + <cmt>BELLEVUE</cmt>
    + <desc><![CDATA[Bellevue Parking Lot]]></desc>
    + <sym>Parking Area</sym>
    + <type><![CDATA[Parking]]></type>
    +</rtept>
    +<rtept lat="42.431240" lon="-71.109236">
    + <ele>26.561890</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>GATE6</name>
    + <desc><![CDATA[Gate 6]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Trail Head]]></type>
    +</rtept>
    +<rtept lat="42.434980" lon="-71.109942">
    + <ele>45.307495</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>PANTHRCAVE</name>
    + <desc><![CDATA[Panther Cave]]></desc>
    + <sym>Tunnel</sym>
    + <type><![CDATA[Tunnel]]></type>
    +</rtept>
    +<rtept lat="42.436757" lon="-71.113223">
    + <ele>37.616943</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6014MEADOW</name>
    + <desc><![CDATA[6014MEADOW]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.439018" lon="-71.114456">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6006</name>
    + <desc><![CDATA[600698]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.438594" lon="-71.114803">
    + <ele>46.028564</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6006BLUE</name>
    + <desc><![CDATA[6006BLUE]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.438917" lon="-71.116146">
    + <ele>44.826904</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>5096</name>
    + <desc><![CDATA[5096]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.438878" lon="-71.119277">
    + <ele>44.586548</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5066</name>
    + <desc><![CDATA[5066]]></desc>
    + <sym>Crossing</sym>
    + <type><![CDATA[Crossing]]></type>
    +</rtept>
    +<rtept lat="42.439227" lon="-71.119689">
    + <ele>57.607200</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>5067</name>
    + <desc><![CDATA[5067]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.439993" lon="-71.120925">
    + <ele>53.949600</ele>
    + <time>2001-06-02T00:18:14Z</time>
    + <name>5058ROAD</name>
    + <cmt>ROAD CROSSING</cmt>
    + <desc><![CDATA[Road Crossing]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Road Crossing]]></type>
    +</rtept>
    +<rtept lat="42.441727" lon="-71.121676">
    + <ele>67.360800</ele>
    + <time>2001-06-02T00:18:16Z</time>
    + <name>5150TANK</name>
    + <cmt>WATER TANK</cmt>
    + <desc><![CDATA[Water Tank]]></desc>
    + <sym>Museum</sym>
    + <type><![CDATA[Water Tank]]></type>
    +</rtept>
    +<rtept lat="42.443904" lon="-71.122044">
    + <ele>50.594727</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5142</name>
    + <desc><![CDATA[5142]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.445359" lon="-71.122845">
    + <ele>61.649902</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5144SUMMIT</name>
    + <desc><![CDATA[Summit]]></desc>
    + <sym>Summit</sym>
    + <type><![CDATA[Summit]]></type>
    +</rtept>
    +<rtept lat="42.447298" lon="-71.121447">
    + <ele>127.711200</ele>
    + <time>2001-06-02T03:26:58Z</time>
    + <name>5156</name>
    + <desc><![CDATA[5156]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.449765" lon="-71.122320">
    + <ele>119.809082</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5148NANEPA</name>
    + <desc><![CDATA[Nanepashemet Road Crossing]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Trail Head]]></type>
    +</rtept>
    +<rtept lat="42.451442" lon="-71.121746">
    + <ele>74.627442</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5258</name>
    + <desc><![CDATA[5258]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.453256" lon="-71.121211">
    + <ele>77.992066</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5252PURPLE</name>
    + <desc><![CDATA[Purple Rock Hill]]></desc>
    + <sym>Summit</sym>
    + <type><![CDATA[Summit]]></type>
    +</rtept>
    +<rtept lat="42.456252" lon="-71.119356">
    + <ele>78.713135</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>527631</name>
    + <desc><![CDATA[527631]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.456592" lon="-71.119676">
    + <ele>78.713135</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>527614</name>
    + <desc><![CDATA[527614]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.457388" lon="-71.119845">
    + <ele>73.761600</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>5267OBSTAC</name>
    + <desc><![CDATA[Obstacle]]></desc>
    + <sym>Amusement Park</sym>
    + <type><![CDATA[Obstacle]]></type>
    +</rtept>
    +<rtept lat="42.458148" lon="-71.119135">
    + <ele>68.275200</ele>
    + <time>2001-06-02T03:27:00Z</time>
    + <name>5278</name>
    + <desc><![CDATA[5278]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.459377" lon="-71.117693">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:27:01Z</time>
    + <name>5289</name>
    + <desc><![CDATA[5289]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.464183" lon="-71.119828">
    + <ele>52.997925</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>5374FIRE</name>
    + <desc><![CDATA[5374FIRE]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.465650" lon="-71.119399">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>5376</name>
    + <desc><![CDATA[5376]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.465913" lon="-71.119328">
    + <ele>64.533692</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>5376STREAM</name>
    + <desc><![CDATA[Stream Crossing]]></desc>
    + <sym>Bridge</sym>
    + <type><![CDATA[Bridge]]></type>
    +</rtept>
    +<rtept lat="42.467110" lon="-71.113574">
    + <ele>53.644800</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>6328</name>
    + <desc><![CDATA[6328]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.466459" lon="-71.110067">
    + <ele>48.768000</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>635722</name>
    + <desc><![CDATA[635722]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.466557" lon="-71.109410">
    + <ele>49.072800</ele>
    + <time>2001-06-02T03:27:02Z</time>
    + <name>635783</name>
    + <desc><![CDATA[635783]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.463495" lon="-71.107117">
    + <ele>62.484000</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>6373</name>
    + <desc><![CDATA[6373]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.465687" lon="-71.107360">
    + <ele>87.782400</ele>
    + <time>2001-06-02T03:27:03Z</time>
    + <name>BEAR HILL</name>
    + <cmt>BEAR HILL TOWER</cmt>
    + <desc><![CDATA[Bear Hill Tower]]></desc>
    + <sym>Tall Tower</sym>
    + <type><![CDATA[Tower]]></type>
    +</rtept>
    +<rtept lat="42.459986" lon="-71.106170">
    + <ele>72.945191</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6289</name>
    + <desc><![CDATA[6289]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.457616" lon="-71.105116">
    + <ele>72.847200</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6297</name>
    + <desc><![CDATA[6297]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.453845" lon="-71.105206">
    + <ele>66.696655</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6283</name>
    + <desc><![CDATA[6283]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.451430" lon="-71.105413">
    + <ele>57.564209</ele>
    + <time>2001-11-16T23:03:38Z</time>
    + <name>6280</name>
    + <desc><![CDATA[6280]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.448448" lon="-71.106158">
    + <ele>62.179200</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6177</name>
    + <desc><![CDATA[6177]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.447804" lon="-71.106624">
    + <ele>62.484000</ele>
    + <time>2001-06-02T03:27:04Z</time>
    + <name>6176</name>
    + <desc><![CDATA[6176]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.444773" lon="-71.108882">
    + <ele>62.788800</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6153</name>
    + <desc><![CDATA[6153]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.443592" lon="-71.106301">
    + <ele>55.473600</ele>
    + <time>2001-06-02T03:27:05Z</time>
    + <name>6171</name>
    + <desc><![CDATA[6171]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.442981" lon="-71.111441">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:26:58Z</time>
    + <name>6131</name>
    + <desc><![CDATA[6131]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.442196" lon="-71.110975">
    + <ele>64.008000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6130</name>
    + <desc><![CDATA[6130]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.441754" lon="-71.113220">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6029</name>
    + <desc><![CDATA[6029]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.439018" lon="-71.114456">
    + <ele>56.388000</ele>
    + <time>2001-06-02T03:26:55Z</time>
    + <name>6006</name>
    + <desc><![CDATA[600698]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Intersection]]></type>
    +</rtept>
    +<rtept lat="42.436757" lon="-71.113223">
    + <ele>37.616943</ele>
    + <time>2001-11-28T21:05:28Z</time>
    + <name>6014MEADOW</name>
    + <desc><![CDATA[6014MEADOW]]></desc>
    + <sym>Dot</sym>
    + <type><![CDATA[Dot]]></type>
    +</rtept>
    +<rtept lat="42.434980" lon="-71.109942">
    + <ele>45.307495</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>PANTHRCAVE</name>
    + <desc><![CDATA[Panther Cave]]></desc>
    + <sym>Tunnel</sym>
    + <type><![CDATA[Tunnel]]></type>
    +</rtept>
    +<rtept lat="42.431240" lon="-71.109236">
    + <ele>26.561890</ele>
    + <time>2001-11-07T23:53:41Z</time>
    + <name>GATE6</name>
    + <desc><![CDATA[Gate 6]]></desc>
    + <sym>Trailhead</sym>
    + <type><![CDATA[Trail Head]]></type>
    +</rtept>
    +<rtept lat="42.430950" lon="-71.107628">
    + <ele>23.469600</ele>
    + <time>2001-06-02T00:18:15Z</time>
    + <name>BELLEVUE</name>
    + <cmt>BELLEVUE</cmt>
    + <desc><![CDATA[Bellevue Parking Lot]]></desc>
    + <sym>Parking Area</sym>
    + <type><![CDATA[Parking]]></type>
    +</rtept>
    +</rte>
    +</gpx>
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/data/icon.png b/mapbox-gl/examples/data/icon.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..ed886623d54cc7972175302fad216a541dd349c3
    GIT binary patch
    literal 2532
    zcmV<A2^;o_P)<h;3K|Lk000e1NJLTq001BW001xu1^@s6aIAQ50000PbVXQnQ*UN;
    zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU*hDk(0RCwB?SZQoj*A@QmTW0sz3}$f*
    zablKG+@zsUrKEzURjV`zXhPHoD}WNIw29Ixg;ZpfnjfJ_X&MPy(kc{!A~h{bA#Otx
    z5P?vHfW|Ba+*0haV`F<f;~CHL=B+*Vy_vBG&v@fX$MgK|a=x>l`<P`}$c?cF>s>Ic
    zA2Um+7X$7oqFa)IzWEUk6VNryGP~1lhNA9aZgG8N(O=v9FJU<VV-M8d#lZG>r%jwG
    zCn})F6VQ8Ouv7&w%#kw}1V9uay1alSLQlk@wRJ@e4|}3#*~j|}0FdM!W{IbKch}B9
    zXBwG{SD@eQf!-E}rCGKlBL_f-ivolS7le=xV#p7{<3pykEoD_mvwK#4dY&(n?8S!l
    zjCcC@8E8*KX>LXO*j4Cf!?1MIIfyZg)D%{~KhmA$a6eEDHo<~e5%8&3yVlXf;w(<m
    z6cg&@WChe~?NAzTKs((@`z%5%L2%1pHn~Pbf`^DdyJQ$`38?>ShnY|zl)K3>@JR4i
    z4^`wfz`XAJxXq170@|5QFqv=>Q%;PpV8ZAfk!#T~V00y6Mluis1kC4>^q4Z0W*Z#<
    zF4<$o(=fYIw!N8`82jB2Tq5a+l|0);L89u~5RpK3&_tLbgBepHSt1EY=f(_A-UQrE
    zmZ8H`3|o4^Eji33!2wW&f`KyZf8zsu^WFzauE~=`@s$;ZiCLjc3;h6a49w9qSHCO`
    z%3P$d+WP!D%&MJ<4_|l(bv4rm2f&?}B7)JG7BO+08ifD^o)0)EdlLp6GJD#+`RBKv
    zdI{hC%7ga4pf7+OYc`;=B4p3;-#f-mDBLfyyeUac{%0V7>?~=45Yh&Yk_isIyyQpt
    z<kd}>Gxf`GyIg4LxKgm~diPDrH6nZ-4|e{119q+X9Rfaoe!oq&)T~&#xMkSu`p`c<
    z_hG!U<QaHfZtQqrJtmE-!o|+(_R^2O_zclx9PxAl9la5}yYh9+uB}7e<f+*F>{{DY
    zg{Htz0Q#$4uvNKs>8ku}W!V_~vVIk1SSNq|Wb0ZSXg+QO{=tKf;L)#rC%<;Vtoe9y
    z-eM;u=gcr90NVu2PNCck?eQ><TsUb<xa0U<d&#cG{V<u6o0rX7h`Yw!VL$KuVjq&3
    z6wY3|fD@OR?RO2Q4m#%K=h@DOVVGcm2Lnf2oAARezaR!_$2{4S-degEv+u5RB+EM4
    z`h9=G+n;P5c$NqhLm&;Xzi>+=(56YDiFj)MW28+1^JmO~qGbxU<>qBdOa=2f=4QWe
    z91MWt`9eMtxZQZRtE--Z^-sQNuUYYpMfvsIG(m5m<h+~e08{83OW>)ln7)feK^7^2
    zxWDF0`2MVKmCUm9);*157tfTOe|h1`Tbz&$?Szi%9rc~QaRCiYpW|ZqI+ksDmHLvx
    z#^CUaTk-YU`wHG|KJpPZe!k6qe)H2U1=)}dTLWTW0@0{Ow02&@o^ywhRMUOxSCZKG
    z@1yu{$K`_U$1a`0KU+`Q&zh-cql)ECTG9thr!nYL6>Y2?KM4;_pJV&M!PT4K=j~N?
    zyt#J^{&09F#`uD0dA)&By(7zO3s&YewS4YEY&^KlIXssKSSbzU?8BUX&Z7UrsTi(A
    z+Y$8masQ-y@=W;Pa~xz;=`GMqO{faBmDTn$H(@@jX8r0`0FDMMQxycs#dsBD*<Ol%
    z<CIuFJh7J;ivv^_P50(w|7=?;{BCc-e0Q=3$F7{?MK)K0oKjCoDccAec>vTZwaa38
    zjau$=bYfVrlN~L5|7Zi=Kh{vx%X7jkdhcgNn?lZr@w%^55DF%>V*$vbjI^S2joi!w
    zuzHpATDe(M<}U-Q8HZF`56leZ0#hiew&8ZAwxpU#owWhicrV6;Dv>raFuBbHcBM}P
    zy53|8=EDLdj*0h7LDag6=(R4pIkCji1K{@wi?r#l45I3*Gog_wd(;?=c;;N<-A1!d
    z1F~??WXhU^pn~ZP)pgS_d0H)!(Ozho%&7ZOQIg;tLu#)qM~dl)W_n=sCdnlFXN68E
    zi-!y)nUFmGKu>oRy@_7i^FtHL5uipx7)4ji)lc#xp{LOv>wwD0=OhbSSDXv{j{*RZ
    z?`=<qwmr5>4h2`h&7fy=boWHi9qk^y4`hpxorqk-#x2?rVO)=0W*+Itz+&WPTXdFQ
    zA$Z*|Qskuyd9G?ugzBSXPH`^~bCbGZ9(p_wjb4S$R=1}9*kle!M3F~#9n!<`QwVsu
    zNgU$nxhL~b;uqABkZIZ#(5`LNmRlsMel?P2UTI^=T40LiT9ak?&4twFw9$iIOa_yl
    z<yvvNWAJD)6%~4CVsrAhC&DEGFu%M@i^LjPknV4MqszG;uoBHgY35Guw=W|+lw~Sv
    z7sI{e2X72o4I>M7Se9(9Hpl@4Kfh!O&NV7uNZ9b5PtIP!NRvh)Mc$wIb#mj$jv>o&
    z^ofSUT377PRQl+KD?3u;v@jg$?W>sJXpaQoZmyp1Xkk8SU5U>KMMk-6m7b1;QRbL}
    z4aXGyRiBldlX@*|nrUria>I$tumMDu?C#VR^*K|d%c@_p56YQ>6*C26`7&;+o`WXo
    z&5Q!=QujN__f8)g<U>mF(X-;??OHhU0XZg^Pj(KqK?p_t!PX83N(skKI+XP?%^hdq
    zkzX6)OU@u;jORHu7Ht-|DLj&MxSTUZUjv)7yOZ&VJPc|}XNp3vp299OY5>v4cPA)>
    zmgssSCIoXJ`uu@qZ&H2Cx%3oroiR&mk3Y@EdYgywEo%tJ4tX!=smyYdX_jpcx0p>X
    z*$)_8BRo<(^xi`urzlYW)A1Xj+`V0#REFCJz;pRH-`_Lgo>zES*&w{62_B+3;yywE
    zOiM?msqJs@%P$Gl0SHz8+XrA@Sc#!n<$o*F(e-=FOMuEKNd;sG0XLBgpm&yNLOFfo
    zxFxcBu_|D2>G`_b2awBb#E4Ds|5Upf-ez(Q1nK{v3=j}YwwX6uy0J)@=ue4v67vd=
    z6EQ;!sd!H53$f6FrHFj{FT5?5d72UL3_k0BevVh3T)9}_v7S<pX}r;*p6OVk9=OW;
    zbKV|nv`_DUR{w9(MZXmnW1jmON94%@7)Lah8j5Pq@|lat(Po;LV>dI0uJ1OxQvaj5
    zCZZM&fJh|}X#!u+B@IghC0a~QbQ?jNrReVTrZd~JCgksVtn)$>>9=-R-*#CsRIGTu
    um$D>g8M7h)`DH`Tl67m3SZw@XfB^usYL{3O78I=j0000<MNUMnLSTZVkJv8&
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/examples/data/igc/Clement-Latour.igc b/mapbox-gl/examples/data/igc/Clement-Latour.igc
    new file mode 100644
    index 0000000000..d0eb152483
    --- /dev/null
    +++ b/mapbox-gl/examples/data/igc/Clement-Latour.igc
    @@ -0,0 +1,15585 @@
    +AXGD123 6030 SN07172 SW3.32
    +HFDTE190411
    +HOPLTPILOT: Clement Latour
    +HOGTYGLIDERTYPE: R10.2
    +HOGIDGLIDERID: None
    +HODTM100GPSDATUM: WGS-84
    +HOCIDCOMPETITIONID: 
    +HOCCLCOMPETITION CLASS: None
    +HOSITSite: Marlens
    +B0853524556201N00651065EA0200502041
    +B0853544556201N00651065EA0200502041
    +B0853564556201N00651065EA0200402041
    +B0853584556201N00651065EA0200402041
    +B0854004556201N00651065EA0200402041
    +B0854024556201N00651065EA0200402041
    +B0854044556201N00651065EA0200402041
    +B0854064556201N00651065EA0200402041
    +B0854084556201N00651065EA0200402041
    +B0854104556201N00651065EA0200402041
    +B0854124556201N00651065EA0200402041
    +B0854144556201N00651065EA0200402041
    +B0854164556201N00651064EA0200402041
    +B0854184556201N00651064EA0200402041
    +B0854204556201N00651065EA0200402041
    +B0854224556201N00651065EA0200502041
    +B0854244556200N00651064EA0200402041
    +B0854264556198N00651062EA0200302041
    +B0854284556194N00651060EA0199902039
    +B0854304556188N00651058EA0199802038
    +B0854324556182N00651058EA0199602036
    +B0854344556174N00651057EA0199202034
    +B0854364556165N00651059EA0199302032
    +B0854384556157N00651061EA0199302031
    +B0854404556147N00651061EA0199502031
    +B0854424556138N00651060EA0200102031
    +B0854444556136N00651051EA0200702035
    +B0854464556139N00651038EA0201102040
    +B0854484556143N00651023EA0201602046
    +B0854504556142N00651008EA0202402053
    +B0854524556141N00650995EA0203102061
    +B0854544556141N00650984EA0203902070
    +B0854564556138N00650976EA0204502077
    +B0854584556132N00650971EA0205202085
    +B0855004556130N00650958EA0205502092
    +B0855024556141N00650943EA0205902097
    +B0855044556156N00650946EA0206502102
    +B0855064556161N00650961EA0206802108
    +B0855084556150N00650973EA0207402114
    +B0855104556141N00650966EA0208102121
    +B0855124556140N00650949EA0208602126
    +B0855144556149N00650938EA0209202132
    +B0855164556163N00650942EA0209502137
    +B0855184556172N00650956EA0210102142
    +B0855204556167N00650969EA0210402146
    +B0855224556155N00650968EA0211202151
    +B0855244556147N00650959EA0212102158
    +B0855264556143N00650948EA0212602164
    +B0855284556141N00650932EA0212902168
    +B0855304556149N00650914EA0213402172
    +B0855324556164N00650915EA0213802177
    +B0855344556174N00650928EA0214502182
    +B0855364556175N00650943EA0215002188
    +B0855384556167N00650953EA0215202192
    +B0855404556154N00650950EA0215802196
    +B0855424556148N00650933EA0216402200
    +B0855444556154N00650914EA0216502205
    +B0855464556171N00650911EA0216802208
    +B0855484556184N00650926EA0217502214
    +B0855504556186N00650944EA0218202219
    +B0855524556178N00650956EA0218802226
    +B0855544556166N00650955EA0219102232
    +B0855564556156N00650941EA0219502236
    +B0855584556157N00650918EA0219902239
    +B0856004556171N00650905EA0220702245
    +B0856024556183N00650912EA0220902247
    +B0856044556182N00650927EA0221202252
    +B0856064556169N00650930EA0221802256
    +B0856084556157N00650915EA0222402262
    +B0856104556153N00650893EA0223102267
    +B0856124556159N00650876EA0223402273
    +B0856144556173N00650872EA0223502276
    +B0856164556185N00650881EA0223802279
    +B0856184556188N00650896EA0224202282
    +B0856204556181N00650907EA0224702286
    +B0856224556171N00650911EA0225302291
    +B0856244556162N00650905EA0225802297
    +B0856264556157N00650886EA0226102301
    +B0856284556164N00650867EA0226302305
    +B0856304556179N00650864EA0226902309
    +B0856324556189N00650870EA0227502315
    +B0856344556195N00650878EA0228002321
    +B0856364556197N00650888EA0228202323
    +B0856384556190N00650899EA0228302326
    +B0856404556177N00650899EA0228402327
    +B0856424556166N00650883EA0228702330
    +B0856444556164N00650863EA0229202333
    +B0856464556170N00650844EA0229802337
    +B0856484556181N00650835EA0230102341
    +B0856504556194N00650834EA0230502344
    +B0856524556203N00650845EA0230402346
    +B0856544556192N00650853EA0230602347
    +B0856564556180N00650843EA0231202351
    +B0856584556177N00650825EA0231802356
    +B0857004556176N00650809EA0232202361
    +B0857024556177N00650793EA0232102364
    +B0857044556186N00650779EA0231902365
    +B0857064556200N00650781EA0232002365
    +B0857084556208N00650792EA0232502367
    +B0857104556213N00650800EA0232802370
    +B0857124556218N00650809EA0232902371
    +B0857144556213N00650822EA0233402373
    +B0857164556205N00650832EA0234202377
    +B0857184556198N00650840EA0234802383
    +B0857204556192N00650844EA0235002386
    +B0857224556182N00650835EA0235102389
    +B0857244556180N00650815EA0235302392
    +B0857264556190N00650803EA0235802395
    +B0857284556202N00650801EA0236602402
    +B0857304556209N00650806EA0237002406
    +B0857324556218N00650815EA0237302410
    +B0857344556221N00650827EA0237802415
    +B0857364556212N00650836EA0238202419
    +B0857384556199N00650833EA0238702425
    +B0857404556190N00650819EA0238902429
    +B0857424556191N00650800EA0239102432
    +B0857444556201N00650789EA0239202434
    +B0857464556215N00650789EA0239502437
    +B0857484556223N00650796EA0239902440
    +B0857504556226N00650807EA0240202443
    +B0857524556225N00650821EA0240502446
    +B0857544556219N00650833EA0241002449
    +B0857564556208N00650833EA0241202453
    +B0857584556198N00650825EA0241402455
    +B0858004556189N00650814EA0241802458
    +B0858024556180N00650805EA0242302462
    +B0858044556171N00650795EA0242702466
    +B0858064556166N00650782EA0243102470
    +B0858084556162N00650766EA0243402474
    +B0858104556154N00650757EA0243502477
    +B0858124556141N00650753EA0243602478
    +B0858144556131N00650748EA0243802481
    +B0858164556121N00650740EA0243902482
    +B0858184556108N00650731EA0244202483
    +B0858204556096N00650722EA0244702486
    +B0858224556088N00650710EA0244702488
    +B0858244556080N00650696EA0244102488
    +B0858264556070N00650684EA0243902485
    +B0858284556060N00650677EA0243402482
    +B0858304556050N00650671EA0242602477
    +B0858324556039N00650660EA0241802470
    +B0858344556026N00650647EA0241702466
    +B0858364556016N00650633EA0242002463
    +B0858384556006N00650620EA0242002464
    +B0858404555997N00650609EA0241602461
    +B0858424555988N00650598EA0241202456
    +B0858444555977N00650587EA0240802453
    +B0858464555965N00650578EA0240502449
    +B0858484555954N00650568EA0240102445
    +B0858504555941N00650556EA0239902442
    +B0858524555929N00650544EA0240202440
    +B0858544555919N00650532EA0240502440
    +B0858564555910N00650520EA0240502441
    +B0858584555900N00650508EA0240402440
    +B0859004555888N00650495EA0240402440
    +B0859024555876N00650482EA0240302439
    +B0859044555864N00650468EA0240202439
    +B0859064555852N00650455EA0240202439
    +B0859084555840N00650442EA0240202439
    +B0859104555829N00650429EA0240002438
    +B0859124555817N00650417EA0239802436
    +B0859144555805N00650402EA0239802435
    +B0859164555794N00650388EA0240002434
    +B0859184555785N00650375EA0239902434
    +B0859204555775N00650365EA0239402432
    +B0859224555762N00650354EA0239202430
    +B0859244555751N00650339EA0239202430
    +B0859264555742N00650324EA0239102429
    +B0859284555733N00650308EA0238702427
    +B0859304555723N00650293EA0238602425
    +B0859324555714N00650279EA0238302423
    +B0859344555706N00650265EA0238002421
    +B0859364555701N00650258EA0237902419
    +B0859384555685N00650234EA0237802417
    +B0859404555674N00650218EA0237602415
    +B0859424555662N00650205EA0237302414
    +B0859444555649N00650193EA0237102412
    +B0859464555636N00650180EA0236902410
    +B0859484555624N00650166EA0237002409
    +B0859504555612N00650151EA0237602409
    +B0859524555604N00650135EA0238302412
    +B0859544555595N00650121EA0238702417
    +B0859564555586N00650110EA0238802419
    +B0859584555575N00650104EA0239002422
    +B0900004555563N00650095EA0239502426
    +B0900024555551N00650082EA0240102431
    +B0900044555540N00650074EA0240402435
    +B0900064555529N00650080EA0240302439
    +B0900084555526N00650091EA0240202442
    +B0900104555537N00650095EA0240102444
    +B0900124555549N00650076EA0240102445
    +B0900144555551N00650049EA0240802448
    +B0900164555548N00650028EA0241402455
    +B0900184555546N00650010EA0241602457
    +B0900204555541N00649989EA0242102460
    +B0900224555530N00649978EA0242402465
    +B0900244555520N00649983EA0242702468
    +B0900264555518N00649996EA0243402473
    +B0900284555528N00650000EA0243902479
    +B0900304555541N00649985EA0244602485
    +B0900324555545N00649961EA0245202491
    +B0900344555534N00649946EA0245502496
    +B0900364555522N00649952EA0246202500
    +B0900384555521N00649967EA0247202508
    +B0900404555531N00649969EA0247902517
    +B0900424555542N00649959EA0248602524
    +B0900444555548N00649938EA0249402532
    +B0900464555541N00649918EA0249902540
    +B0900484555528N00649915EA0250602547
    +B0900504555522N00649928EA0251402554
    +B0900524555525N00649937EA0252302564
    +B0900544555535N00649936EA0253002570
    +B0900564555546N00649928EA0253702578
    +B0900584555552N00649908EA0254202584
    +B0901004555548N00649885EA0254802590
    +B0901024555536N00649876EA0255502596
    +B0901044555527N00649886EA0256202602
    +B0901064555526N00649900EA0257002611
    +B0901084555534N00649903EA0257602617
    +B0901104555545N00649894EA0258302623
    +B0901124555550N00649874EA0258902630
    +B0901144555543N00649855EA0259502636
    +B0901164555530N00649850EA0260302642
    +B0901184555522N00649861EA0261002649
    +B0901204555522N00649873EA0261802657
    +B0901224555529N00649879EA0262402663
    +B0901244555540N00649872EA0263102670
    +B0901264555545N00649853EA0263702677
    +B0901284555539N00649836EA0264402684
    +B0901304555525N00649830EA0265002691
    +B0901324555514N00649837EA0265702698
    +B0901344555511N00649847EA0266202704
    +B0901364555519N00649853EA0266502708
    +B0901384555531N00649846EA0267002712
    +B0901404555538N00649827EA0267602717
    +B0901424555534N00649806EA0268202722
    +B0901444555522N00649795EA0268902729
    +B0901464555508N00649797EA0269502735
    +B0901484555503N00649807EA0269802740
    +B0901504555510N00649814EA0270102745
    +B0901524555522N00649807EA0270602749
    +B0901544555527N00649788EA0271202754
    +B0901564555524N00649768EA0271902761
    +B0901584555517N00649752EA0272402767
    +B0902004555504N00649748EA0272702772
    +B0902024555492N00649755EA0273202777
    +B0902044555490N00649767EA0273602782
    +B0902064555498N00649774EA0274002786
    +B0902084555508N00649761EA0274402789
    +B0902104555510N00649738EA0275002794
    +B0902124555502N00649717EA0275902800
    +B0902144555494N00649700EA0276402806
    +B0902164555485N00649681EA0276802810
    +B0902184555475N00649659EA0277402815
    +B0902204555466N00649638EA0278102822
    +B0902224555458N00649618EA0278602829
    +B0902244555451N00649600EA0278702833
    +B0902264555442N00649582EA0278502836
    +B0902284555434N00649563EA0278402836
    +B0902304555427N00649545EA0278102834
    +B0902324555420N00649526EA0277902830
    +B0902344555412N00649505EA0278002829
    +B0902364555405N00649488EA0278302831
    +B0902384555399N00649472EA0278202829
    +B0902404555393N00649457EA0278002827
    +B0902424555385N00649441EA0277502824
    +B0902444555378N00649425EA0277102820
    +B0902464555370N00649408EA0276802816
    +B0902484555362N00649391EA0276502812
    +B0902504555356N00649374EA0276202808
    +B0902524555348N00649357EA0275702804
    +B0902544555340N00649341EA0275102799
    +B0902564555332N00649322EA0274702794
    +B0902584555326N00649302EA0274402791
    +B0903004555319N00649283EA0274002787
    +B0903024555312N00649264EA0273502781
    +B0903044555307N00649243EA0273202778
    +B0903064555303N00649224EA0273002775
    +B0903084555298N00649207EA0272602771
    +B0903104555294N00649187EA0271802766
    +B0903124555291N00649165EA0271302760
    +B0903144555287N00649145EA0270802755
    +B0903164555283N00649124EA0270102749
    +B0903184555281N00649104EA0269602743
    +B0903204555279N00649085EA0269002737
    +B0903224555276N00649070EA0268402730
    +B0903244555273N00649054EA0267702723
    +B0903264555269N00649034EA0267202716
    +B0903284555267N00649015EA0267202711
    +B0903304555266N00648998EA0267002709
    +B0903324555263N00648982EA0266502704
    +B0903344555258N00648964EA0266202700
    +B0903364555253N00648946EA0266202698
    +B0903384555247N00648929EA0266002697
    +B0903404555242N00648911EA0266102697
    +B0903424555237N00648893EA0266902697
    +B0903444555231N00648875EA0267902700
    +B0903464555227N00648857EA0268502706
    +B0903484555225N00648837EA0268902711
    +B0903504555221N00648816EA0269802717
    +B0903524555218N00648795EA0270802725
    +B0903544555218N00648779EA0271302732
    +B0903564555215N00648761EA0271602737
    +B0903584555211N00648742EA0272302745
    +B0904004555209N00648727EA0273202754
    +B0904024555207N00648712EA0273802763
    +B0904044555204N00648699EA0274202770
    +B0904064555199N00648685EA0274502776
    +B0904084555193N00648669EA0274902781
    +B0904104555186N00648654EA0275402786
    +B0904124555179N00648642EA0275602791
    +B0904144555172N00648630EA0275902794
    +B0904164555166N00648615EA0276102797
    +B0904184555161N00648600EA0276202799
    +B0904204555155N00648586EA0276102799
    +B0904224555148N00648571EA0276102800
    +B0904244555141N00648557EA0275902799
    +B0904264555135N00648543EA0275802798
    +B0904284555130N00648529EA0275602797
    +B0904304555125N00648515EA0275302795
    +B0904324555118N00648502EA0275302794
    +B0904344555111N00648489EA0275402794
    +B0904364555104N00648477EA0275202793
    +B0904384555096N00648462EA0275102792
    +B0904404555090N00648445EA0275102792
    +B0904424555084N00648428EA0274902790
    +B0904444555078N00648411EA0274602788
    +B0904464555071N00648395EA0274202785
    +B0904484555064N00648380EA0274002782
    +B0904504555057N00648363EA0273702779
    +B0904524555051N00648344EA0274002777
    +B0904544555046N00648327EA0274302777
    +B0904564555040N00648314EA0274302779
    +B0904584555035N00648301EA0274302779
    +B0905004555029N00648287EA0274202779
    +B0905024555021N00648272EA0274002777
    +B0905044555013N00648257EA0273502775
    +B0905064555004N00648243EA0273102771
    +B0905084554995N00648227EA0272302765
    +B0905104554984N00648211EA0271902760
    +B0905124554974N00648196EA0271502756
    +B0905144554963N00648181EA0271002752
    +B0905164554954N00648165EA0270302746
    +B0905184554943N00648147EA0269402739
    +B0905204554931N00648130EA0268602733
    +B0905224554919N00648114EA0268002726
    +B0905244554908N00648096EA0267302718
    +B0905264554896N00648078EA0266602711
    +B0905284554885N00648061EA0266002705
    +B0905304554873N00648043EA0265402698
    +B0905324554861N00648026EA0264902693
    +B0905344554849N00648009EA0264302686
    +B0905364554836N00647992EA0263502680
    +B0905384554823N00647975EA0263002674
    +B0905404554808N00647962EA0262402669
    +B0905424554794N00647950EA0262202665
    +B0905444554780N00647938EA0262002662
    +B0905464554766N00647928EA0261902659
    +B0905484554751N00647918EA0261602656
    +B0905504554738N00647907EA0261602654
    +B0905524554725N00647897EA0261202652
    +B0905544554713N00647887EA0260702647
    +B0905564554700N00647874EA0260302643
    +B0905584554688N00647857EA0260002640
    +B0906004554678N00647841EA0259702637
    +B0906024554668N00647826EA0259102633
    +B0906044554657N00647808EA0258602628
    +B0906064554646N00647788EA0258202623
    +B0906084554637N00647767EA0257802619
    +B0906104554628N00647746EA0257302615
    +B0906124554617N00647727EA0257002611
    +B0906144554607N00647708EA0256702608
    +B0906164554598N00647689EA0256302604
    +B0906184554587N00647669EA0256102602
    +B0906204554577N00647650EA0256002600
    +B0906224554567N00647631EA0255702597
    +B0906244554557N00647611EA0255602595
    +B0906264554547N00647590EA0255602594
    +B0906284554538N00647572EA0255502593
    +B0906304554528N00647552EA0255202591
    +B0906324554520N00647532EA0254902588
    +B0906344554512N00647512EA0254502585
    +B0906364554503N00647494EA0254102581
    +B0906384554495N00647476EA0253902578
    +B0906404554487N00647461EA0253702576
    +B0906424554479N00647444EA0253302573
    +B0906444554470N00647426EA0253102570
    +B0906464554463N00647410EA0253002569
    +B0906484554455N00647395EA0252702566
    +B0906504554447N00647379EA0252302562
    +B0906524554439N00647359EA0251902559
    +B0906544554434N00647339EA0251702557
    +B0906564554430N00647320EA0251702555
    +B0906584554424N00647302EA0251502554
    +B0907004554419N00647284EA0251302552
    +B0907024554416N00647264EA0250902549
    +B0907044554415N00647246EA0250702547
    +B0907064554412N00647227EA0250402544
    +B0907084554407N00647208EA0250302542
    +B0907104554402N00647190EA0250102541
    +B0907124554396N00647175EA0249802539
    +B0907144554389N00647159EA0249602535
    +B0907164554382N00647143EA0249402533
    +B0907184554376N00647129EA0249002531
    +B0907204554370N00647115EA0248402528
    +B0907224554362N00647100EA0248102524
    +B0907244554353N00647085EA0247902521
    +B0907264554346N00647071EA0247602518
    +B0907284554338N00647056EA0247302515
    +B0907304554329N00647041EA0247002512
    +B0907324554321N00647026EA0246702509
    +B0907344554313N00647011EA0246402505
    +B0907364554304N00646995EA0246002502
    +B0907384554295N00646981EA0245702498
    +B0907404554286N00646966EA0245502495
    +B0907424554277N00646950EA0245202493
    +B0907444554268N00646934EA0245102491
    +B0907464554258N00646919EA0245002490
    +B0907484554248N00646904EA0244902489
    +B0907504554239N00646888EA0244702488
    +B0907524554229N00646873EA0244602487
    +B0907544554219N00646859EA0244602486
    +B0907564554208N00646844EA0244502485
    +B0907584554197N00646829EA0244502485
    +B0908004554187N00646815EA0244402485
    +B0908024554177N00646799EA0244402484
    +B0908044554167N00646782EA0244402484
    +B0908064554158N00646764EA0244602485
    +B0908084554150N00646747EA0244602486
    +B0908104554142N00646732EA0244502486
    +B0908124554136N00646716EA0244302485
    +B0908144554130N00646700EA0243902482
    +B0908164554122N00646682EA0243702479
    +B0908184554114N00646665EA0243702478
    +B0908204554106N00646649EA0243602477
    +B0908224554099N00646632EA0243302474
    +B0908244554091N00646614EA0243102472
    +B0908264554084N00646597EA0243002471
    +B0908284554078N00646579EA0242602468
    +B0908304554070N00646559EA0242202465
    +B0908324554062N00646541EA0242202464
    +B0908344554054N00646526EA0242002462
    +B0908364554045N00646510EA0241602458
    +B0908384554035N00646494EA0241302455
    +B0908404554025N00646479EA0241202453
    +B0908424554015N00646465EA0240902451
    +B0908444554005N00646450EA0240602448
    +B0908464553995N00646435EA0240402445
    +B0908484553985N00646421EA0240202443
    +B0908504553976N00646407EA0239802440
    +B0908524553965N00646392EA0239302436
    +B0908544553954N00646377EA0239002432
    +B0908564553945N00646363EA0238902430
    +B0908584553935N00646347EA0238602428
    +B0909004553925N00646331EA0238402426
    +B0909024553917N00646315EA0238302424
    +B0909044553908N00646300EA0238202422
    +B0909064553899N00646285EA0237802419
    +B0909084553891N00646268EA0237502417
    +B0909104553882N00646252EA0237302415
    +B0909124553873N00646236EA0237002412
    +B0909144553865N00646219EA0236702409
    +B0909164553855N00646203EA0236502406
    +B0909184553846N00646188EA0236202404
    +B0909204553838N00646171EA0235902401
    +B0909224553830N00646153EA0235602397
    +B0909244553822N00646135EA0235402395
    +B0909264553814N00646120EA0235202393
    +B0909284553805N00646105EA0234802390
    +B0909304553796N00646089EA0234602387
    +B0909324553787N00646073EA0234402386
    +B0909344553779N00646058EA0234102383
    +B0909364553771N00646043EA0233802380
    +B0909384553762N00646027EA0233702378
    +B0909404553754N00646013EA0233602377
    +B0909424553746N00645999EA0233102374
    +B0909444553736N00645984EA0232802370
    +B0909464553728N00645968EA0232802369
    +B0909484553720N00645953EA0232602367
    +B0909504553712N00645937EA0232302364
    +B0909524553704N00645919EA0232202363
    +B0909544553697N00645902EA0232202362
    +B0909564553690N00645886EA0231902360
    +B0909584553682N00645870EA0231602357
    +B0910004553674N00645853EA0231402355
    +B0910024553665N00645839EA0231202353
    +B0910044553655N00645825EA0230902350
    +B0910064553646N00645810EA0230702347
    +B0910084553637N00645796EA0230402345
    +B0910104553627N00645781EA0230002342
    +B0910124553618N00645766EA0229702339
    +B0910144553609N00645751EA0229502336
    +B0910164553601N00645735EA0229302334
    +B0910184553594N00645718EA0229102332
    +B0910204553587N00645702EA0228802329
    +B0910224553579N00645686EA0228402327
    +B0910244553571N00645671EA0228202324
    +B0910264553563N00645656EA0228102322
    +B0910284553554N00645642EA0227902320
    +B0910304553545N00645627EA0227702318
    +B0910324553537N00645612EA0227602316
    +B0910344553528N00645598EA0227302314
    +B0910364553518N00645584EA0227202312
    +B0910384553509N00645570EA0226902310
    +B0910404553500N00645556EA0226702308
    +B0910424553490N00645542EA0226402305
    +B0910444553481N00645527EA0226002302
    +B0910464553472N00645512EA0225802299
    +B0910484553461N00645498EA0225602297
    +B0910504553452N00645485EA0225402295
    +B0910524553442N00645471EA0225202293
    +B0910544553431N00645458EA0225002291
    +B0910564553421N00645445EA0224902289
    +B0910584553410N00645432EA0224702288
    +B0911004553400N00645419EA0224502286
    +B0911024553390N00645406EA0224302284
    +B0911044553381N00645392EA0224102282
    +B0911064553371N00645378EA0223902280
    +B0911084553361N00645364EA0223602278
    +B0911104553351N00645350EA0223302275
    +B0911124553341N00645335EA0223102272
    +B0911144553332N00645320EA0222902270
    +B0911164553324N00645306EA0222602268
    +B0911184553317N00645291EA0222202264
    +B0911204553308N00645276EA0221902261
    +B0911224553300N00645260EA0221702258
    +B0911244553293N00645246EA0221202254
    +B0911264553285N00645231EA0220902250
    +B0911284553279N00645215EA0221202248
    +B0911304553274N00645203EA0221402248
    +B0911324553268N00645193EA0221402249
    +B0911344553261N00645183EA0221402250
    +B0911364553256N00645171EA0221302250
    +B0911384553249N00645157EA0221402250
    +B0911404553242N00645142EA0221702252
    +B0911424553237N00645126EA0222202256
    +B0911444553233N00645110EA0222602260
    +B0911464553227N00645094EA0223002265
    +B0911484553216N00645082EA0223202268
    +B0911504553203N00645085EA0223502272
    +B0911524553197N00645101EA0223802275
    +B0911544553206N00645112EA0224202280
    +B0911564553216N00645107EA0224502284
    +B0911584553220N00645090EA0224802287
    +B0912004553214N00645071EA0225102290
    +B0912024553200N00645061EA0225502293
    +B0912044553186N00645065EA0225702296
    +B0912064553179N00645079EA0226002299
    +B0912084553182N00645094EA0226302302
    +B0912104553192N00645100EA0226402304
    +B0912124553203N00645091EA0226602306
    +B0912144553207N00645075EA0226802308
    +B0912164553203N00645058EA0227002310
    +B0912184553192N00645047EA0227102312
    +B0912204553178N00645047EA0227302313
    +B0912224553168N00645057EA0227602315
    +B0912244553168N00645074EA0227802317
    +B0912264553175N00645087EA0228002320
    +B0912284553185N00645090EA0228202322
    +B0912304553194N00645085EA0228402324
    +B0912324553199N00645070EA0228802327
    +B0912344553197N00645051EA0229102330
    +B0912364553185N00645040EA0229102332
    +B0912384553172N00645046EA0229102333
    +B0912404553169N00645062EA0229202334
    +B0912424553177N00645072EA0229402336
    +B0912444553187N00645072EA0229802338
    +B0912464553196N00645066EA0230302343
    +B0912484553204N00645058EA0230602347
    +B0912504553210N00645052EA0230802350
    +B0912524553214N00645035EA0230802351
    +B0912544553208N00645014EA0231202352
    +B0912564553197N00645005EA0231602355
    +B0912584553186N00645012EA0231802358
    +B0913004553183N00645025EA0231902360
    +B0913024553188N00645037EA0232002361
    +B0913044553196N00645043EA0232402365
    +B0913064553206N00645041EA0232702367
    +B0913084553216N00645029EA0233102371
    +B0913104553219N00645010EA0233602375
    +B0913124553214N00644990EA0234102380
    +B0913144553203N00644979EA0234402384
    +B0913164553192N00644985EA0234702388
    +B0913184553191N00645000EA0235102392
    +B0913204553199N00645008EA0235502396
    +B0913224553210N00645004EA0235802399
    +B0913244553217N00644987EA0236202402
    +B0913264553212N00644965EA0236702407
    +B0913284553199N00644956EA0237102411
    +B0913304553189N00644962EA0237302415
    +B0913324553187N00644977EA0237602418
    +B0913344553196N00644983EA0237902420
    +B0913364553208N00644975EA0238302424
    +B0913384553213N00644958EA0238702429
    +B0913404553212N00644939EA0239302433
    +B0913424553202N00644926EA0239802438
    +B0913444553189N00644925EA0240202443
    +B0913464553182N00644934EA0240302446
    +B0913484553183N00644948EA0240502449
    +B0913504553193N00644953EA0240802452
    +B0913524553203N00644944EA0241002454
    +B0913544553210N00644927EA0241302456
    +B0913564553209N00644907EA0241802459
    +B0913584553200N00644895EA0242302463
    +B0914004553189N00644892EA0243002468
    +B0914024553180N00644894EA0243502475
    +B0914044553175N00644904EA0243702479
    +B0914064553179N00644916EA0243902483
    +B0914084553190N00644916EA0243902485
    +B0914104553200N00644902EA0244002486
    +B0914124553203N00644883EA0244602489
    +B0914144553201N00644863EA0245002494
    +B0914164553192N00644850EA0245002496
    +B0914184553180N00644851EA0245102498
    +B0914204553172N00644860EA0245202499
    +B0914224553173N00644873EA0245702501
    +B0914244553182N00644879EA0246202504
    +B0914264553194N00644870EA0246602508
    +B0914284553199N00644852EA0246902511
    +B0914304553195N00644834EA0246902513
    +B0914324553184N00644828EA0246602513
    +B0914344553173N00644837EA0246602513
    +B0914364553169N00644850EA0247002515
    +B0914384553172N00644860EA0247302518
    +B0914404553178N00644866EA0247502520
    +B0914424553186N00644869EA0247702522
    +B0914444553196N00644866EA0247902523
    +B0914464553207N00644858EA0248402526
    +B0914484553216N00644850EA0249002531
    +B0914504553223N00644837EA0249302535
    +B0914524553229N00644818EA0249502538
    +B0914544553225N00644793EA0249702541
    +B0914564553213N00644785EA0249602542
    +B0914584553206N00644797EA0249802542
    +B0915004553206N00644811EA0250302546
    +B0915024553207N00644820EA0250802551
    +B0915044553210N00644826EA0251102555
    +B0915064553221N00644825EA0251002557
    +B0915084553232N00644814EA0251002557
    +B0915104553235N00644795EA0251002556
    +B0915124553231N00644775EA0251402557
    +B0915144553223N00644760EA0251802560
    +B0915164553213N00644751EA0252002562
    +B0915184553203N00644754EA0251902563
    +B0915204553197N00644765EA0251902563
    +B0915224553197N00644778EA0252102565
    +B0915244553205N00644785EA0252302566
    +B0915264553218N00644781EA0252402568
    +B0915284553224N00644758EA0252402567
    +B0915304553220N00644736EA0252702568
    +B0915324553208N00644729EA0252902570
    +B0915344553198N00644729EA0253002572
    +B0915364553191N00644734EA0252902573
    +B0915384553187N00644745EA0252802572
    +B0915404553193N00644757EA0252802572
    +B0915424553205N00644759EA0252902572
    +B0915444553216N00644746EA0252902572
    +B0915464553217N00644722EA0252902572
    +B0915484553210N00644707EA0253202574
    +B0915504553199N00644702EA0253202575
    +B0915524553189N00644699EA0253102574
    +B0915544553178N00644698EA0252602572
    +B0915564553164N00644699EA0252602570
    +B0915584553151N00644699EA0252702571
    +B0916004553140N00644701EA0252602570
    +B0916024553129N00644703EA0252402568
    +B0916044553117N00644703EA0252002565
    +B0916064553106N00644705EA0251602562
    +B0916084553093N00644706EA0251302558
    +B0916104553081N00644705EA0251202556
    +B0916124553070N00644702EA0250802553
    +B0916144553058N00644699EA0250502550
    +B0916164553046N00644696EA0250302547
    +B0916184553034N00644694EA0250202545
    +B0916204553022N00644693EA0250002543
    +B0916224553009N00644689EA0250002542
    +B0916244552997N00644682EA0250302542
    +B0916264552986N00644677EA0250802544
    +B0916284552976N00644672EA0251302549
    +B0916304552967N00644665EA0251202550
    +B0916324552955N00644659EA0250902551
    +B0916344552942N00644655EA0251002551
    +B0916364552930N00644650EA0251502554
    +B0916384552918N00644646EA0251802557
    +B0916404552905N00644641EA0252102560
    +B0916424552892N00644637EA0252202563
    +B0916444552880N00644635EA0252302564
    +B0916464552866N00644633EA0252502566
    +B0916484552853N00644631EA0252502567
    +B0916504552841N00644629EA0252602568
    +B0916524552829N00644626EA0252702569
    +B0916544552817N00644623EA0252602569
    +B0916564552804N00644620EA0252802570
    +B0916584552790N00644619EA0253202572
    +B0917004552778N00644618EA0253502576
    +B0917024552767N00644621EA0253602578
    +B0917044552756N00644629EA0253602578
    +B0917064552748N00644638EA0253502578
    +B0917084552736N00644641EA0253302578
    +B0917104552722N00644638EA0252702576
    +B0917124552708N00644629EA0252002572
    +B0917144552695N00644619EA0251402565
    +B0917164552681N00644607EA0251002560
    +B0917184552670N00644595EA0250702557
    +B0917204552660N00644583EA0250302552
    +B0917224552649N00644570EA0249602546
    +B0917244552638N00644555EA0249302541
    +B0917264552627N00644542EA0248902537
    +B0917284552617N00644529EA0248502532
    +B0917304552604N00644515EA0248202527
    +B0917324552591N00644502EA0248302524
    +B0917344552579N00644491EA0248602523
    +B0917364552569N00644478EA0249002524
    +B0917384552559N00644464EA0249502528
    +B0917404552549N00644449EA0250102534
    +B0917424552541N00644434EA0250602541
    +B0917444552534N00644418EA0251102547
    +B0917464552525N00644405EA0251702552
    +B0917484552516N00644394EA0252202558
    +B0917504552507N00644386EA0252202562
    +B0917524552494N00644387EA0251802565
    +B0917544552487N00644399EA0251802566
    +B0917564552496N00644407EA0252202567
    +B0917584552508N00644397EA0252602570
    +B0918004552514N00644383EA0253102575
    +B0918024552516N00644367EA0253402578
    +B0918044552516N00644350EA0253702581
    +B0918064552512N00644333EA0254002584
    +B0918084552506N00644317EA0254402587
    +B0918104552500N00644302EA0254702590
    +B0918124552493N00644287EA0254702592
    +B0918144552483N00644274EA0254602592
    +B0918164552471N00644260EA0254702592
    +B0918184552458N00644247EA0254902593
    +B0918204552445N00644232EA0255102595
    +B0918224552433N00644218EA0255202596
    +B0918244552419N00644205EA0255002596
    +B0918264552405N00644190EA0255002595
    +B0918284552393N00644174EA0255002595
    +B0918304552381N00644159EA0254802593
    +B0918324552367N00644145EA0254602592
    +B0918344552352N00644131EA0254302589
    +B0918364552338N00644117EA0254102587
    +B0918384552326N00644103EA0254102586
    +B0918404552314N00644090EA0253802585
    +B0918424552301N00644079EA0253302581
    +B0918444552288N00644066EA0252802576
    +B0918464552276N00644051EA0252202571
    +B0918484552265N00644037EA0251602564
    +B0918504552253N00644023EA0250902558
    +B0918524552240N00644010EA0250402552
    +B0918544552228N00643998EA0250002546
    +B0918564552216N00643986EA0249502541
    +B0918584552204N00643973EA0249002536
    +B0919004552191N00643961EA0248702532
    +B0919024552179N00643951EA0248302528
    +B0919044552167N00643939EA0247602523
    +B0919064552154N00643927EA0247002516
    +B0919084552139N00643916EA0246602512
    +B0919104552128N00643903EA0246202508
    +B0919124552117N00643890EA0245502502
    +B0919144552104N00643878EA0245002496
    +B0919164552091N00643867EA0244802491
    +B0919184552080N00643855EA0244302487
    +B0919204552068N00643844EA0243702482
    +B0919224552055N00643834EA0243002476
    +B0919244552042N00643823EA0242602471
    +B0919264552029N00643812EA0242302468
    +B0919284552016N00643801EA0241902464
    +B0919304552003N00643789EA0241602460
    +B0919324551990N00643779EA0241402458
    +B0919344551978N00643769EA0240902455
    +B0919364551965N00643761EA0240602451
    +B0919384551951N00643760EA0240602448
    +B0919404551938N00643760EA0240402446
    +B0919424551925N00643755EA0239802442
    +B0919444551911N00643750EA0239602438
    +B0919464551898N00643743EA0239402435
    +B0919484551887N00643730EA0239202432
    +B0919504551875N00643716EA0238902430
    +B0919524551863N00643702EA0238902429
    +B0919544551852N00643687EA0238902428
    +B0919564551840N00643674EA0238602427
    +B0919584551828N00643660EA0238402425
    +B0920004551815N00643647EA0238402424
    +B0920024551803N00643634EA0238502424
    +B0920044551791N00643622EA0238502424
    +B0920064551778N00643608EA0238402423
    +B0920084551766N00643594EA0238402424
    +B0920104551753N00643584EA0238502425
    +B0920124551741N00643573EA0238402425
    +B0920144551729N00643563EA0238202423
    +B0920164551715N00643554EA0238002421
    +B0920184551701N00643548EA0238002421
    +B0920204551688N00643544EA0237902420
    +B0920224551673N00643540EA0237502418
    +B0920244551658N00643535EA0237302415
    +B0920264551644N00643530EA0237202414
    +B0920284551630N00643522EA0237102413
    +B0920304551616N00643512EA0237002413
    +B0920324551603N00643502EA0236902412
    +B0920344551590N00643492EA0236902411
    +B0920364551578N00643482EA0236702410
    +B0920384551565N00643472EA0236502408
    +B0920404551552N00643463EA0236402406
    +B0920424551539N00643453EA0236202405
    +B0920444551527N00643444EA0236002403
    +B0920464551516N00643434EA0235602400
    +B0920484551504N00643423EA0235402397
    +B0920504551491N00643413EA0235302395
    +B0920524551477N00643406EA0235102393
    +B0920544551465N00643400EA0234902392
    +B0920564551452N00643394EA0234702389
    +B0920584551438N00643387EA0234602388
    +B0921004551426N00643379EA0234502387
    +B0921024551414N00643370EA0234302385
    +B0921044551401N00643359EA0234002382
    +B0921064551389N00643348EA0233902381
    +B0921084551377N00643336EA0233802380
    +B0921104551366N00643324EA0233602377
    +B0921124551354N00643311EA0233302375
    +B0921144551342N00643300EA0233102373
    +B0921164551330N00643289EA0232702371
    +B0921184551318N00643278EA0232402367
    +B0921204551306N00643265EA0232202364
    +B0921224551294N00643255EA0232202363
    +B0921244551283N00643246EA0232002362
    +B0921264551271N00643235EA0231802359
    +B0921284551258N00643223EA0231602357
    +B0921304551246N00643212EA0231402356
    +B0921324551235N00643200EA0231202354
    +B0921344551224N00643187EA0230902351
    +B0921364551212N00643175EA0230602348
    +B0921384551199N00643165EA0230402346
    +B0921404551185N00643156EA0230202344
    +B0921424551172N00643148EA0229902341
    +B0921444551159N00643139EA0229702339
    +B0921464551146N00643129EA0229502337
    +B0921484551133N00643118EA0229202334
    +B0921504551120N00643108EA0229002332
    +B0921524551108N00643099EA0228802330
    +B0921544551096N00643089EA0228502327
    +B0921564551084N00643076EA0228202324
    +B0921584551072N00643064EA0227802321
    +B0922004551060N00643051EA0227602318
    +B0922024551048N00643039EA0227402316
    +B0922044551036N00643027EA0227402315
    +B0922064551026N00643016EA0227202313
    +B0922084551015N00643004EA0226802310
    +B0922104551004N00642992EA0226602308
    +B0922124550993N00642982EA0226302305
    +B0922144550981N00642973EA0226002302
    +B0922164550968N00642965EA0225702299
    +B0922184550955N00642956EA0225402296
    +B0922204550943N00642946EA0225102293
    +B0922224550931N00642936EA0224802290
    +B0922244550919N00642927EA0224602287
    +B0922264550907N00642917EA0224302285
    +B0922284550896N00642906EA0224002281
    +B0922304550885N00642896EA0223702278
    +B0922324550872N00642886EA0223302275
    +B0922344550861N00642876EA0222902271
    +B0922364550848N00642866EA0222502267
    +B0922384550835N00642859EA0222102263
    +B0922404550822N00642852EA0221902260
    +B0922424550809N00642845EA0221502257
    +B0922444550798N00642836EA0220902252
    +B0922464550786N00642827EA0220302246
    +B0922484550773N00642819EA0219602239
    +B0922504550761N00642808EA0219302234
    +B0922524550750N00642797EA0219102231
    +B0922544550738N00642787EA0219002229
    +B0922564550727N00642774EA0218902227
    +B0922584550716N00642761EA0218802226
    +B0923004550705N00642751EA0218602226
    +B0923024550693N00642741EA0218502224
    +B0923044550681N00642731EA0218502223
    +B0923064550668N00642725EA0218502224
    +B0923084550656N00642719EA0218502224
    +B0923104550644N00642712EA0218402223
    +B0923124550632N00642701EA0218202222
    +B0923144550620N00642690EA0218202221
    +B0923164550607N00642679EA0217802219
    +B0923184550594N00642668EA0217702217
    +B0923204550582N00642656EA0217502215
    +B0923224550569N00642644EA0217402214
    +B0923244550556N00642633EA0217202212
    +B0923264550544N00642622EA0217002211
    +B0923284550533N00642609EA0216902209
    +B0923304550522N00642598EA0216802208
    +B0923324550511N00642587EA0216602206
    +B0923344550500N00642573EA0216402204
    +B0923364550490N00642558EA0216102202
    +B0923384550481N00642545EA0215702199
    +B0923404550472N00642531EA0215002195
    +B0923424550463N00642517EA0214302189
    +B0923444550451N00642503EA0213702183
    +B0923464550441N00642492EA0213102177
    +B0923484550430N00642482EA0212402169
    +B0923504550418N00642471EA0211702161
    +B0923524550404N00642463EA0211002155
    +B0923544550389N00642458EA0210502149
    +B0923564550373N00642452EA0210102143
    +B0923584550359N00642445EA0209902139
    +B0924004550345N00642438EA0209402135
    +B0924024550331N00642431EA0208802130
    +B0924044550317N00642421EA0208302124
    +B0924064550304N00642410EA0208002120
    +B0924084550290N00642400EA0207702117
    +B0924104550278N00642391EA0207402113
    +B0924124550264N00642383EA0207102109
    +B0924144550251N00642374EA0206902107
    +B0924164550238N00642366EA0206402103
    +B0924184550223N00642356EA0205802098
    +B0924204550209N00642347EA0205602094
    +B0924224550195N00642337EA0205502092
    +B0924244550183N00642329EA0205102089
    +B0924264550169N00642322EA0204602085
    +B0924284550154N00642313EA0204602083
    +B0924304550141N00642304EA0204502082
    +B0924324550127N00642295EA0204202079
    +B0924344550112N00642285EA0204002076
    +B0924364550098N00642275EA0203802075
    +B0924384550084N00642266EA0203702073
    +B0924404550070N00642256EA0203702073
    +B0924424550055N00642251EA0203802074
    +B0924444550042N00642248EA0203602074
    +B0924464550028N00642243EA0203502073
    +B0924484550014N00642238EA0203702073
    +B0924504550001N00642233EA0203702074
    +B0924524549988N00642228EA0203602074
    +B0924544549974N00642224EA0203502073
    +B0924564549961N00642220EA0203502073
    +B0924584549948N00642217EA0203502074
    +B0925004549935N00642214EA0203502074
    +B0925024549921N00642211EA0203602074
    +B0925044549909N00642206EA0203702075
    +B0925064549896N00642201EA0203702075
    +B0925084549884N00642197EA0203502075
    +B0925104549872N00642194EA0203402074
    +B0925124549858N00642191EA0203402074
    +B0925144549845N00642187EA0203402074
    +B0925164549832N00642184EA0203202073
    +B0925184549818N00642181EA0203202072
    +B0925204549804N00642177EA0203302072
    +B0925224549789N00642173EA0203402073
    +B0925244549775N00642169EA0203502074
    +B0925264549761N00642166EA0203602076
    +B0925284549747N00642162EA0203702077
    +B0925304549733N00642157EA0203902078
    +B0925324549720N00642152EA0204002079
    +B0925344549706N00642148EA0204302081
    +B0925364549692N00642144EA0204702085
    +B0925384549678N00642139EA0205102088
    +B0925404549665N00642134EA0205402092
    +B0925424549653N00642127EA0205902096
    +B0925444549642N00642120EA0206102100
    +B0925464549630N00642115EA0206402102
    +B0925484549618N00642108EA0206702106
    +B0925504549606N00642101EA0206902109
    +B0925524549595N00642100EA0207002111
    +B0925544549588N00642108EA0207002112
    +B0925564549591N00642117EA0207002112
    +B0925584549601N00642117EA0207002113
    +B0926004549611N00642109EA0207402116
    +B0926024549620N00642101EA0207702119
    +B0926044549628N00642096EA0207902121
    +B0926064549636N00642092EA0208202123
    +B0926084549643N00642089EA0208602126
    +B0926104549650N00642086EA0209002130
    +B0926124549656N00642083EA0209502135
    +B0926144549662N00642082EA0209902139
    +B0926164549668N00642082EA0210302143
    +B0926184549674N00642082EA0210602147
    +B0926204549680N00642088EA0210702148
    +B0926224549677N00642104EA0210902150
    +B0926244549665N00642111EA0211102151
    +B0926264549649N00642100EA0211602154
    +B0926284549638N00642086EA0211902158
    +B0926304549627N00642074EA0212202161
    +B0926324549614N00642062EA0212702165
    +B0926344549602N00642057EA0212902168
    +B0926364549592N00642065EA0212502170
    +B0926384549589N00642076EA0212502171
    +B0926404549597N00642080EA0212702171
    +B0926424549608N00642076EA0213002172
    +B0926444549615N00642070EA0213202174
    +B0926464549622N00642066EA0213602177
    +B0926484549629N00642062EA0214102181
    +B0926504549635N00642057EA0214502185
    +B0926524549641N00642052EA0215002189
    +B0926544549648N00642049EA0215602195
    +B0926564549653N00642050EA0216002200
    +B0926584549655N00642059EA0216402204
    +B0927004549650N00642072EA0216902208
    +B0927024549634N00642072EA0217202212
    +B0927044549621N00642054EA0217402214
    +B0927064549620N00642032EA0217602217
    +B0927084549632N00642030EA0217902220
    +B0927104549640N00642043EA0218702225
    +B0927124549645N00642051EA0219402232
    +B0927144549651N00642053EA0220102240
    +B0927164549655N00642058EA0220502246
    +B0927184549651N00642069EA0220702250
    +B0927204549633N00642070EA0221002252
    +B0927224549618N00642050EA0221802258
    +B0927244549616N00642029EA0222202263
    +B0927264549622N00642015EA0222802268
    +B0927284549629N00642008EA0223602275
    +B0927304549636N00642003EA0224402283
    +B0927324549640N00641998EA0224802290
    +B0927344549646N00641999EA0225202295
    +B0927364549648N00642012EA0225602299
    +B0927384549637N00642023EA0225802302
    +B0927404549620N00642018EA0226102304
    +B0927424549607N00641997EA0226702307
    +B0927444549606N00641976EA0227202312
    +B0927464549612N00641962EA0227902318
    +B0927484549619N00641955EA0228702326
    +B0927504549626N00641949EA0229502333
    +B0927524549632N00641944EA0230302342
    +B0927544549639N00641938EA0231002349
    +B0927564549647N00641937EA0231502356
    +B0927584549651N00641951EA0231802360
    +B0928004549645N00641967EA0232202365
    +B0928024549629N00641967EA0232702368
    +B0928044549618N00641948EA0233502374
    +B0928064549616N00641930EA0234702382
    +B0928084549620N00641921EA0235602392
    +B0928104549625N00641916EA0236202401
    +B0928124549630N00641909EA0236402407
    +B0928144549637N00641903EA0236602412
    +B0928164549647N00641910EA0237102416
    +B0928184549652N00641928EA0237702420
    +B0928204549646N00641946EA0237802424
    +B0928224549632N00641960EA0238102426
    +B0928244549616N00641965EA0238602430
    +B0928264549602N00641956EA0239002433
    +B0928284549594N00641939EA0239402437
    +B0928304549598N00641925EA0240002442
    +B0928324549607N00641922EA0240502446
    +B0928344549614N00641919EA0240802450
    +B0928364549620N00641911EA0241302454
    +B0928384549628N00641904EA0242102460
    +B0928404549635N00641902EA0242002464
    +B0928424549642N00641903EA0241602467
    +B0928444549645N00641915EA0240802466
    +B0928464549638N00641934EA0240102463
    +B0928484549622N00641939EA0239702455
    +B0928504549606N00641929EA0240002451
    +B0928524549595N00641920EA0240302451
    +B0928544549584N00641916EA0240702453
    +B0928564549571N00641911EA0241002455
    +B0928584549559N00641904EA0241402459
    +B0929004549546N00641899EA0241902463
    +B0929024549533N00641894EA0242302466
    +B0929044549522N00641890EA0242902470
    +B0929064549516N00641894EA0243402474
    +B0929084549512N00641901EA0243702478
    +B0929104549510N00641910EA0243602481
    +B0929124549511N00641920EA0243402481
    +B0929144549517N00641931EA0243702483
    +B0929164549525N00641933EA0244402486
    +B0929184549529N00641928EA0245102492
    +B0929204549532N00641923EA0245602496
    +B0929224549535N00641919EA0245902500
    +B0929244549537N00641911EA0246302504
    +B0929264549536N00641901EA0246402506
    +B0929284549528N00641891EA0246402508
    +B0929304549516N00641886EA0246702510
    +B0929324549507N00641881EA0246702512
    +B0929344549497N00641876EA0246402512
    +B0929364549482N00641880EA0246802511
    +B0929384549474N00641896EA0247302513
    +B0929404549476N00641910EA0247602515
    +B0929424549482N00641916EA0247502516
    +B0929444549488N00641915EA0247602517
    +B0929464549493N00641912EA0248102521
    +B0929484549497N00641912EA0247802521
    +B0929504549500N00641910EA0247302521
    +B0929524549506N00641904EA0247202519
    +B0929544549511N00641900EA0246902517
    +B0929564549516N00641897EA0246102513
    +B0929584549521N00641893EA0245602508
    +B0930004549526N00641884EA0245202504
    +B0930024549522N00641868EA0244602498
    +B0930044549511N00641855EA0244202492
    +B0930064549498N00641844EA0244302488
    +B0930084549486N00641834EA0244702486
    +B0930104549478N00641829EA0244602485
    +B0930124549466N00641826EA0244102482
    +B0930144549455N00641817EA0244102479
    +B0930164549446N00641806EA0244202479
    +B0930184549439N00641794EA0243902478
    +B0930204549428N00641782EA0243602475
    +B0930224549416N00641768EA0244402475
    +B0930244549407N00641753EA0245102477
    +B0930264549398N00641741EA0245102479
    +B0930284549383N00641737EA0245102479
    +B0930304549371N00641739EA0245602483
    +B0930324549362N00641741EA0245802487
    +B0930344549355N00641745EA0245202488
    +B0930364549349N00641760EA0244902489
    +B0930384549353N00641773EA0245002490
    +B0930404549365N00641771EA0245002490
    +B0930424549372N00641753EA0244902491
    +B0930444549371N00641733EA0245002492
    +B0930464549365N00641716EA0245002493
    +B0930484549356N00641702EA0244802492
    +B0930504549347N00641688EA0244902491
    +B0930524549338N00641675EA0244702490
    +B0930544549331N00641661EA0244302488
    +B0930564549325N00641645EA0243702485
    +B0930584549316N00641631EA0243102479
    +B0931004549305N00641619EA0242602474
    +B0931024549296N00641607EA0242502471
    +B0931044549286N00641596EA0242402469
    +B0931064549275N00641587EA0242402468
    +B0931084549264N00641577EA0242402467
    +B0931104549253N00641564EA0242402466
    +B0931124549242N00641551EA0242702467
    +B0931144549230N00641542EA0242802468
    +B0931164549220N00641532EA0242502467
    +B0931184549210N00641516EA0242502467
    +B0931204549199N00641499EA0242702467
    +B0931224549190N00641483EA0242902469
    +B0931244549181N00641470EA0242802469
    +B0931264549170N00641453EA0243202470
    +B0931284549160N00641437EA0243502473
    +B0931304549152N00641421EA0243602475
    +B0931324549145N00641405EA0243602476
    +B0931344549137N00641393EA0243402476
    +B0931364549129N00641383EA0243302474
    +B0931384549120N00641371EA0243202473
    +B0931404549113N00641358EA0243502474
    +B0931424549104N00641346EA0243802475
    +B0931444549095N00641335EA0244102478
    +B0931464549086N00641324EA0244302482
    +B0931484549076N00641312EA0244402483
    +B0931504549064N00641306EA0244302484
    +B0931524549053N00641303EA0243902483
    +B0931544549041N00641298EA0243502481
    +B0931564549029N00641287EA0243202478
    +B0931584549016N00641272EA0242902474
    +B0932004549004N00641253EA0242702471
    +B0932024548990N00641238EA0242502469
    +B0932044548977N00641228EA0242302467
    +B0932064548965N00641218EA0242002464
    +B0932084548952N00641210EA0241602461
    +B0932104548939N00641198EA0241302457
    +B0932124548928N00641182EA0241002454
    +B0932144548919N00641165EA0240702452
    +B0932164548909N00641149EA0240502448
    +B0932184548898N00641135EA0240702447
    +B0932204548889N00641124EA0241202446
    +B0932224548880N00641116EA0241502449
    +B0932244548869N00641108EA0241702452
    +B0932264548858N00641109EA0241602453
    +B0932284548851N00641122EA0241702455
    +B0932304548854N00641131EA0242302459
    +B0932324548862N00641132EA0242602464
    +B0932344548870N00641128EA0242702466
    +B0932364548876N00641119EA0242502468
    +B0932384548881N00641100EA0242202467
    +B0932404548877N00641077EA0242102466
    +B0932424548865N00641062EA0242502466
    +B0932444548854N00641060EA0242702466
    +B0932464548845N00641064EA0242602466
    +B0932484548837N00641071EA0242602466
    +B0932504548834N00641085EA0242702467
    +B0932524548841N00641095EA0243002470
    +B0932544548849N00641101EA0243402474
    +B0932564548854N00641107EA0243702478
    +B0932584548858N00641112EA0243702480
    +B0933004548865N00641111EA0243602480
    +B0933024548875N00641099EA0243802480
    +B0933044548877N00641076EA0243902482
    +B0933064548870N00641057EA0243902483
    +B0933084548862N00641043EA0243902483
    +B0933104548850N00641036EA0243702482
    +B0933124548838N00641043EA0243402481
    +B0933144548833N00641058EA0243602480
    +B0933164548839N00641068EA0243802481
    +B0933184548847N00641074EA0243802481
    +B0933204548856N00641078EA0243802481
    +B0933224548865N00641084EA0243902482
    +B0933244548874N00641090EA0244202484
    +B0933264548880N00641095EA0244402487
    +B0933284548888N00641097EA0244502489
    +B0933304548897N00641101EA0244502489
    +B0933324548906N00641110EA0244902491
    +B0933344548914N00641120EA0245402494
    +B0933364548918N00641130EA0245702498
    +B0933384548922N00641138EA0245702500
    +B0933404548931N00641138EA0245702502
    +B0933424548945N00641125EA0245802503
    +B0933444548950N00641104EA0246102505
    +B0933464548944N00641090EA0246502506
    +B0933484548933N00641088EA0247002510
    +B0933504548928N00641094EA0247202514
    +B0933524548928N00641102EA0247502517
    +B0933544548933N00641110EA0248002520
    +B0933564548939N00641115EA0248502525
    +B0933584548946N00641115EA0248802529
    +B0934004548953N00641119EA0248902532
    +B0934024548960N00641126EA0248902534
    +B0934044548969N00641117EA0248802535
    +B0934064548970N00641094EA0248802534
    +B0934084548957N00641074EA0249102534
    +B0934104548945N00641068EA0250002537
    +B0934124548936N00641067EA0250802543
    +B0934144548929N00641065EA0251202548
    +B0934164548923N00641070EA0251002551
    +B0934184548927N00641078EA0251002553
    +B0934204548937N00641068EA0251002554
    +B0934224548945N00641054EA0251502559
    +B0934244548950N00641045EA0251702562
    +B0934264548953N00641030EA0251602564
    +B0934284548954N00641008EA0251902565
    +B0934304548947N00640993EA0252402568
    +B0934324548937N00640993EA0252602571
    +B0934344548932N00641002EA0253102574
    +B0934364548931N00641014EA0253902580
    +B0934384548934N00641022EA0254302585
    +B0934404548941N00641024EA0254402587
    +B0934424548951N00641013EA0254302589
    +B0934444548954N00640992EA0254502589
    +B0934464548947N00640976EA0255202592
    +B0934484548938N00640977EA0255902595
    +B0934504548929N00640985EA0256702603
    +B0934524548926N00640991EA0256902608
    +B0934544548928N00640996EA0256802611
    +B0934564548939N00640993EA0256702613
    +B0934584548949N00640974EA0256702614
    +B0935004548948N00640949EA0257002615
    +B0935024548938N00640931EA0257902619
    +B0935044548929N00640922EA0258702626
    +B0935064548923N00640914EA0258902630
    +B0935084548915N00640902EA0258602632
    +B0935104548904N00640896EA0258302632
    +B0935124548897N00640903EA0258402633
    +B0935144548899N00640912EA0258602634
    +B0935164548909N00640913EA0258802635
    +B0935184548919N00640904EA0259102636
    +B0935204548926N00640887EA0259402639
    +B0935224548925N00640865EA0259802642
    +B0935244548916N00640850EA0260202646
    +B0935264548906N00640848EA0260402649
    +B0935284548902N00640857EA0260802652
    +B0935304548909N00640863EA0261002655
    +B0935324548922N00640855EA0261202658
    +B0935344548928N00640837EA0261502661
    +B0935364548927N00640818EA0261702663
    +B0935384548922N00640802EA0262102666
    +B0935404548911N00640792EA0262502670
    +B0935424548900N00640785EA0262802673
    +B0935444548891N00640776EA0263002675
    +B0935464548883N00640766EA0263302678
    +B0935484548874N00640757EA0263602681
    +B0935504548865N00640755EA0263602684
    +B0935524548857N00640764EA0263602685
    +B0935544548850N00640773EA0263702686
    +B0935564548840N00640770EA0263702685
    +B0935584548829N00640762EA0263902686
    +B0936004548818N00640756EA0264402688
    +B0936024548807N00640749EA0264902691
    +B0936044548796N00640743EA0265202695
    +B0936064548786N00640736EA0265502699
    +B0936084548777N00640727EA0265602701
    +B0936104548768N00640717EA0265502702
    +B0936124548758N00640708EA0265502702
    +B0936144548749N00640698EA0265602703
    +B0936164548740N00640685EA0265902703
    +B0936184548733N00640669EA0266602705
    +B0936204548731N00640656EA0267502709
    +B0936224548727N00640648EA0268202715
    +B0936244548721N00640640EA0268602721
    +B0936264548715N00640630EA0268602725
    +B0936284548706N00640620EA0268902728
    +B0936304548696N00640608EA0269502734
    +B0936324548687N00640597EA0270202740
    +B0936344548678N00640587EA0270802747
    +B0936364548669N00640577EA0271402754
    +B0936384548660N00640567EA0272002760
    +B0936404548652N00640559EA0272302766
    +B0936424548643N00640552EA0272402770
    +B0936444548633N00640544EA0272402772
    +B0936464548622N00640534EA0273002775
    +B0936484548612N00640525EA0273702780
    +B0936504548603N00640514EA0274302786
    +B0936524548595N00640498EA0274902791
    +B0936544548586N00640485EA0275402797
    +B0936564548576N00640476EA0275902802
    +B0936584548565N00640464EA0276602808
    +B0937004548554N00640454EA0277002814
    +B0937024548543N00640448EA0277102818
    +B0937044548530N00640439EA0277102820
    +B0937064548518N00640426EA0277302822
    +B0937084548508N00640417EA0277302824
    +B0937104548496N00640412EA0277002824
    +B0937124548484N00640403EA0276802822
    +B0937144548473N00640392EA0276802822
    +B0937164548463N00640383EA0276902821
    +B0937184548453N00640372EA0277002822
    +B0937204548445N00640361EA0277302823
    +B0937224548436N00640351EA0277402825
    +B0937244548427N00640341EA0277302824
    +B0937264548417N00640327EA0277202823
    +B0937284548408N00640312EA0277302823
    +B0937304548400N00640300EA0277102823
    +B0937324548391N00640289EA0277002821
    +B0937344548383N00640276EA0277002821
    +B0937364548375N00640264EA0276802820
    +B0937384548366N00640254EA0276702818
    +B0937404548359N00640241EA0276602817
    +B0937424548351N00640227EA0276502815
    +B0937444548343N00640214EA0276602814
    +B0937464548334N00640200EA0276802815
    +B0937484548327N00640184EA0277002817
    +B0937504548319N00640169EA0277202819
    +B0937524548310N00640156EA0277202819
    +B0937544548303N00640138EA0277502821
    +B0937564548297N00640121EA0277802823
    +B0937584548292N00640107EA0278002825
    +B0938004548284N00640091EA0278202826
    +B0938024548276N00640075EA0278802829
    +B0938044548269N00640061EA0279202834
    +B0938064548264N00640048EA0279202836
    +B0938084548258N00640032EA0279102837
    +B0938104548252N00640014EA0279402839
    +B0938124548247N00639999EA0279702842
    +B0938144548243N00639985EA0279802843
    +B0938164548237N00639969EA0279902844
    +B0938184548230N00639953EA0280202846
    +B0938204548225N00639938EA0280402849
    +B0938224548219N00639923EA0280502850
    +B0938244548213N00639906EA0280602851
    +B0938264548210N00639888EA0280802853
    +B0938284548206N00639872EA0280802854
    +B0938304548202N00639856EA0280802854
    +B0938324548198N00639840EA0280802854
    +B0938344548194N00639824EA0280702853
    +B0938364548190N00639809EA0280702853
    +B0938384548185N00639794EA0280602852
    +B0938404548180N00639779EA0280302851
    +B0938424548175N00639764EA0280302849
    +B0938444548165N00639760EA0279902848
    +B0938464548150N00639762EA0279602845
    +B0938484548143N00639746EA0278602840
    +B0938504548149N00639719EA0278802836
    +B0938524548154N00639702EA0279102835
    +B0938544548152N00639690EA0278702833
    +B0938564548150N00639671EA0278002828
    +B0938584548149N00639650EA0278102827
    +B0939004548148N00639633EA0278102827
    +B0939024548145N00639616EA0277602823
    +B0939044548142N00639594EA0277202820
    +B0939064548142N00639572EA0277102818
    +B0939084548146N00639552EA0276602814
    +B0939104548154N00639532EA0276402810
    +B0939124548161N00639512EA0276402808
    +B0939144548165N00639494EA0276302807
    +B0939164548166N00639474EA0275902804
    +B0939184548165N00639453EA0275702801
    +B0939204548164N00639433EA0275802800
    +B0939224548164N00639415EA0275602799
    +B0939244548162N00639397EA0275202796
    +B0939264548159N00639378EA0274902793
    +B0939284548155N00639361EA0274802792
    +B0939304548152N00639344EA0274502789
    +B0939324548148N00639329EA0274102786
    +B0939344548142N00639312EA0273702782
    +B0939364548137N00639294EA0273402779
    +B0939384548132N00639276EA0273102775
    +B0939404548127N00639258EA0272902773
    +B0939424548124N00639240EA0272902772
    +B0939444548120N00639224EA0272702771
    +B0939464548114N00639207EA0272802770
    +B0939484548109N00639192EA0272902771
    +B0939504548102N00639178EA0272702770
    +B0939524548096N00639163EA0272502768
    +B0939544548090N00639148EA0272302767
    +B0939564548083N00639135EA0272202766
    +B0939584548077N00639121EA0271902764
    +B0940004548070N00639106EA0271702762
    +B0940024548063N00639092EA0271702761
    +B0940044548056N00639078EA0271602760
    +B0940064548049N00639063EA0271402758
    +B0940084548041N00639048EA0271402757
    +B0940104548032N00639034EA0271302757
    +B0940124548025N00639021EA0271002755
    +B0940144548017N00639005EA0270702752
    +B0940164548010N00638990EA0270702751
    +B0940184548003N00638977EA0270402750
    +B0940204547996N00638964EA0270202747
    +B0940224547989N00638950EA0270102746
    +B0940244547983N00638937EA0270102745
    +B0940264547977N00638923EA0269902744
    +B0940284547970N00638908EA0269702742
    +B0940304547964N00638893EA0269502740
    +B0940324547958N00638877EA0269202738
    +B0940344547952N00638861EA0268802734
    +B0940364547947N00638843EA0268502731
    +B0940384547942N00638827EA0268302729
    +B0940404547937N00638811EA0268002726
    +B0940424547932N00638794EA0267602722
    +B0940444547927N00638777EA0267302718
    +B0940464547921N00638761EA0266902715
    +B0940484547915N00638744EA0266502711
    +B0940504547909N00638727EA0266002707
    +B0940524547901N00638709EA0265702703
    +B0940544547893N00638693EA0265402700
    +B0940564547886N00638678EA0265202696
    +B0940584547880N00638660EA0265002693
    +B0941004547876N00638643EA0264902691
    +B0941024547874N00638626EA0264702689
    +B0941044547873N00638609EA0264302685
    +B0941064547874N00638588EA0264302683
    +B0941084547873N00638571EA0264602683
    +B0941104547869N00638558EA0264402682
    +B0941124547862N00638544EA0264002680
    +B0941144547856N00638528EA0263702678
    +B0941164547850N00638511EA0263702678
    +B0941184547846N00638496EA0263602677
    +B0941204547842N00638480EA0263702677
    +B0941224547839N00638466EA0263702678
    +B0941244547837N00638453EA0263302676
    +B0941264547833N00638436EA0262902674
    +B0941284547830N00638420EA0263102673
    +B0941304547826N00638409EA0263002674
    +B0941324547820N00638400EA0262702672
    +B0941344547813N00638387EA0262502670
    +B0941364547805N00638374EA0262602669
    +B0941384547798N00638363EA0262402668
    +B0941404547789N00638351EA0262002666
    +B0941424547783N00638336EA0261602663
    +B0941444547778N00638322EA0261402661
    +B0941464547772N00638309EA0261102658
    +B0941484547767N00638294EA0260902654
    +B0941504547763N00638277EA0260802653
    +B0941524547759N00638260EA0260602651
    +B0941544547753N00638246EA0260302649
    +B0941564547748N00638230EA0260002646
    +B0941584547744N00638215EA0259802643
    +B0942004547741N00638200EA0259602640
    +B0942024547737N00638184EA0259502639
    +B0942044547734N00638170EA0259302637
    +B0942064547731N00638154EA0259302636
    +B0942084547728N00638137EA0259402637
    +B0942104547725N00638122EA0259402637
    +B0942124547722N00638105EA0259202635
    +B0942144547718N00638089EA0259202635
    +B0942164547712N00638075EA0259102634
    +B0942184547706N00638061EA0259102634
    +B0942204547700N00638048EA0259302635
    +B0942224547693N00638036EA0259402636
    +B0942244547687N00638024EA0259402636
    +B0942264547682N00638012EA0259502637
    +B0942284547677N00638000EA0259402637
    +B0942304547673N00637986EA0259402637
    +B0942324547669N00637974EA0259502638
    +B0942344547664N00637964EA0259502638
    +B0942364547660N00637952EA0259402638
    +B0942384547653N00637939EA0259502638
    +B0942404547645N00637926EA0259702640
    +B0942424547637N00637914EA0259802641
    +B0942444547631N00637901EA0259902642
    +B0942464547625N00637889EA0259702642
    +B0942484547619N00637876EA0259602641
    +B0942504547612N00637862EA0259602641
    +B0942524547607N00637848EA0259602641
    +B0942544547602N00637834EA0259502641
    +B0942564547595N00637821EA0259502640
    +B0942584547588N00637810EA0259502640
    +B0943004547582N00637798EA0259302639
    +B0943024547576N00637783EA0259202637
    +B0943044547570N00637769EA0259102636
    +B0943064547565N00637756EA0259102636
    +B0943084547560N00637742EA0259002635
    +B0943104547555N00637725EA0259202636
    +B0943124547551N00637709EA0259402637
    +B0943144547546N00637696EA0259502637
    +B0943164547541N00637680EA0259602639
    +B0943184547537N00637666EA0259902641
    +B0943204547534N00637655EA0259802642
    +B0943224547529N00637642EA0259402641
    +B0943244547523N00637629EA0259202639
    +B0943264547517N00637616EA0259102638
    +B0943284547513N00637601EA0259002636
    +B0943304547509N00637586EA0258902635
    +B0943324547504N00637574EA0258902635
    +B0943344547499N00637562EA0258702633
    +B0943364547494N00637549EA0258102630
    +B0943384547486N00637537EA0257502626
    +B0943404547478N00637524EA0257202622
    +B0943424547472N00637511EA0256902619
    +B0943444547464N00637501EA0256302614
    +B0943464547456N00637490EA0256002609
    +B0943484547448N00637478EA0255702605
    +B0943504547439N00637470EA0255002600
    +B0943524547430N00637461EA0254102595
    +B0943544547421N00637447EA0253602588
    +B0943564547413N00637432EA0253402584
    +B0943584547406N00637419EA0253202581
    +B0944004547401N00637406EA0252902577
    +B0944024547395N00637392EA0252802574
    +B0944044547390N00637374EA0253202571
    +B0944064547390N00637356EA0253902571
    +B0944084547388N00637342EA0254202573
    +B0944104547385N00637329EA0254502576
    +B0944124547381N00637315EA0254602578
    +B0944144547374N00637300EA0254802580
    +B0944164547367N00637284EA0255202584
    +B0944184547361N00637269EA0255602588
    +B0944204547356N00637256EA0256002593
    +B0944224547351N00637243EA0256302597
    +B0944244547345N00637230EA0256602601
    +B0944264547340N00637218EA0256902604
    +B0944284547335N00637207EA0256802606
    +B0944304547328N00637194EA0256702607
    +B0944324547320N00637179EA0257002609
    +B0944344547313N00637165EA0257402612
    +B0944364547307N00637155EA0257502615
    +B0944384547299N00637145EA0257202616
    +B0944404547291N00637132EA0257002615
    +B0944424547283N00637117EA0257002616
    +B0944444547277N00637104EA0256802614
    +B0944464547270N00637091EA0256302612
    +B0944484547262N00637075EA0256202610
    +B0944504547255N00637058EA0256402609
    +B0944524547250N00637042EA0256502611
    +B0944544547244N00637026EA0256602611
    +B0944564547237N00637008EA0256702612
    +B0944584547229N00636991EA0257002613
    +B0945004547222N00636976EA0257202615
    +B0945024547216N00636961EA0257802617
    +B0945044547212N00636946EA0259002621
    +B0945064547209N00636935EA0259602628
    +B0945084547204N00636922EA0260102633
    +B0945104547196N00636908EA0261002639
    +B0945124547188N00636897EA0262002647
    +B0945144547178N00636888EA0263002657
    +B0945164547167N00636881EA0263902668
    +B0945184547155N00636876EA0264902680
    +B0945204547143N00636872EA0265502689
    +B0945224547129N00636866EA0266002696
    +B0945244547116N00636857EA0266902705
    +B0945264547105N00636847EA0268002715
    +B0945284547095N00636842EA0268502724
    +B0945304547083N00636837EA0268602731
    +B0945324547072N00636828EA0268602735
    +B0945344547061N00636820EA0268402739
    +B0945364547050N00636811EA0267902740
    +B0945384547040N00636798EA0267702737
    +B0945404547030N00636784EA0267602735
    +B0945424547019N00636772EA0267602734
    +B0945444547009N00636759EA0267302731
    +B0945464546998N00636745EA0267002728
    +B0945484546988N00636730EA0266702724
    +B0945504546979N00636714EA0266402720
    +B0945524546970N00636697EA0266202717
    +B0945544546962N00636680EA0266002714
    +B0945564546954N00636662EA0265702710
    +B0945584546947N00636643EA0265602708
    +B0946004546940N00636626EA0265302706
    +B0946024546934N00636610EA0264702702
    +B0946044546926N00636594EA0264102697
    +B0946064546917N00636578EA0263902693
    +B0946084546909N00636564EA0263602690
    +B0946104546902N00636550EA0263102686
    +B0946124546895N00636536EA0262702681
    +B0946144546886N00636522EA0262702677
    +B0946164546877N00636507EA0262702675
    +B0946184546871N00636492EA0262502673
    +B0946204546865N00636476EA0262302670
    +B0946224546859N00636459EA0262302669
    +B0946244546852N00636444EA0262302669
    +B0946264546844N00636430EA0262302668
    +B0946284546836N00636414EA0262302668
    +B0946304546828N00636397EA0262702668
    +B0946324546820N00636383EA0263102670
    +B0946344546812N00636371EA0263302672
    +B0946364546801N00636359EA0263602674
    +B0946384546791N00636349EA0264002677
    +B0946404546781N00636340EA0264202681
    +B0946424546771N00636329EA0264002682
    +B0946444546761N00636317EA0264102682
    +B0946464546752N00636302EA0264302684
    +B0946484546745N00636288EA0264402686
    +B0946504546738N00636274EA0264502687
    +B0946524546731N00636260EA0264502688
    +B0946544546723N00636244EA0264602689
    +B0946564546716N00636228EA0264702690
    +B0946584546709N00636212EA0264802691
    +B0947004546701N00636198EA0265102693
    +B0947024546694N00636185EA0265402696
    +B0947044546689N00636172EA0265402697
    +B0947064546682N00636158EA0265302697
    +B0947084546675N00636144EA0265302697
    +B0947104546670N00636129EA0265302697
    +B0947124546665N00636114EA0265002696
    +B0947144546660N00636096EA0265002695
    +B0947164546655N00636078EA0265202695
    +B0947184546650N00636064EA0265202695
    +B0947204546644N00636050EA0265002694
    +B0947224546637N00636031EA0264902694
    +B0947244546631N00636013EA0265102695
    +B0947264546627N00635998EA0265002696
    +B0947284546623N00635981EA0264802694
    +B0947304546616N00635962EA0264802693
    +B0947324546610N00635945EA0264902694
    +B0947344546604N00635929EA0264702693
    +B0947364546598N00635911EA0264302690
    +B0947384546591N00635892EA0264202688
    +B0947404546586N00635873EA0264102687
    +B0947424546582N00635855EA0263702685
    +B0947444546576N00635838EA0263402681
    +B0947464546569N00635819EA0263302680
    +B0947484546564N00635803EA0263302679
    +B0947504546557N00635787EA0263202677
    +B0947524546551N00635772EA0263302677
    +B0947544546545N00635757EA0263602678
    +B0947564546538N00635745EA0264102680
    +B0947584546531N00635733EA0264502684
    +B0948004546524N00635722EA0265102690
    +B0948024546516N00635714EA0265502695
    +B0948044546508N00635707EA0265902699
    +B0948064546500N00635699EA0266202702
    +B0948084546491N00635689EA0266602706
    +B0948104546481N00635680EA0267102711
    +B0948124546471N00635677EA0267502716
    +B0948144546462N00635675EA0267602719
    +B0948164546451N00635675EA0267702721
    +B0948184546439N00635672EA0268002723
    +B0948204546427N00635667EA0268502727
    +B0948224546415N00635664EA0268702731
    +B0948244546402N00635662EA0269002733
    +B0948264546389N00635657EA0269202736
    +B0948284546377N00635649EA0269402738
    +B0948304546366N00635639EA0269502739
    +B0948324546356N00635625EA0269602740
    +B0948344546348N00635607EA0269602741
    +B0948364546341N00635589EA0269702742
    +B0948384546335N00635573EA0269702742
    +B0948404546330N00635555EA0269702742
    +B0948424546329N00635537EA0269702742
    +B0948444546328N00635518EA0269702742
    +B0948464546327N00635499EA0269802742
    +B0948484546326N00635480EA0270002743
    +B0948504546323N00635461EA0269902744
    +B0948524546317N00635445EA0269702744
    +B0948544546311N00635429EA0269102742
    +B0948564546306N00635410EA0269002740
    +B0948584546301N00635392EA0268902739
    +B0949004546297N00635377EA0268602737
    +B0949024546293N00635360EA0268202733
    +B0949044546291N00635340EA0268102731
    +B0949064546289N00635322EA0268002730
    +B0949084546286N00635306EA0267602727
    +B0949104546282N00635288EA0267102722
    +B0949124546279N00635268EA0267002719
    +B0949144546276N00635250EA0266802717
    +B0949164546273N00635233EA0266502713
    +B0949184546269N00635215EA0266102710
    +B0949204546263N00635197EA0266002707
    +B0949224546258N00635181EA0265702705
    +B0949244546252N00635166EA0265402702
    +B0949264546246N00635149EA0265202699
    +B0949284546241N00635131EA0264902696
    +B0949304546236N00635114EA0264602693
    +B0949324546231N00635097EA0264202689
    +B0949344546226N00635079EA0263902686
    +B0949364546222N00635062EA0263502682
    +B0949384546217N00635046EA0263002678
    +B0949404546212N00635028EA0262502673
    +B0949424546208N00635010EA0262102668
    +B0949444546203N00634992EA0261702664
    +B0949464546198N00634975EA0261302660
    +B0949484546191N00634956EA0260902656
    +B0949504546186N00634938EA0260802654
    +B0949524546181N00634923EA0260302650
    +B0949544546176N00634905EA0259902645
    +B0949564546171N00634887EA0259802642
    +B0949584546165N00634871EA0259602641
    +B0950004546159N00634857EA0259202637
    +B0950024546153N00634840EA0258802633
    +B0950044546147N00634823EA0258702631
    +B0950064546141N00634808EA0258502629
    +B0950084546136N00634792EA0258202626
    +B0950104546130N00634775EA0257902623
    +B0950124546125N00634760EA0257602621
    +B0950144546118N00634745EA0257302618
    +B0950164546112N00634728EA0257002614
    +B0950184546105N00634711EA0257002613
    +B0950204546099N00634695EA0256802611
    +B0950224546092N00634679EA0256602610
    +B0950244546087N00634663EA0256502609
    +B0950264546082N00634647EA0256202607
    +B0950284546076N00634631EA0256002604
    +B0950304546068N00634615EA0256002603
    +B0950324546060N00634598EA0256202602
    +B0950344546053N00634581EA0256102602
    +B0950364546047N00634563EA0255802600
    +B0950384546040N00634542EA0255502597
    +B0950404546034N00634522EA0255402596
    +B0950424546029N00634504EA0255302596
    +B0950444546025N00634485EA0255002593
    +B0950464546020N00634465EA0255002591
    +B0950484546016N00634448EA0255102591
    +B0950504546013N00634432EA0254902590
    +B0950524546009N00634414EA0254802588
    +B0950544546005N00634396EA0255002588
    +B0950564546001N00634382EA0254902588
    +B0950584545996N00634370EA0254802586
    +B0951004545991N00634357EA0254502584
    +B0951024545986N00634343EA0254302582
    +B0951044545980N00634329EA0254302581
    +B0951064545975N00634314EA0254502581
    +B0951084545970N00634299EA0254802583
    +B0951104545965N00634285EA0255002586
    +B0951124545960N00634272EA0255202587
    +B0951144545953N00634257EA0255602591
    +B0951164545947N00634243EA0256202595
    +B0951184545941N00634229EA0256602600
    +B0951204545936N00634217EA0256802604
    +B0951224545929N00634206EA0256602606
    +B0951244545923N00634192EA0256302607
    +B0951264545918N00634176EA0255902605
    +B0951284545912N00634159EA0255702603
    +B0951304545906N00634143EA0255502601
    +B0951324545902N00634127EA0255302599
    +B0951344545897N00634109EA0255202597
    +B0951364545893N00634090EA0255602596
    +B0951384545893N00634071EA0256202597
    +B0951404545893N00634053EA0256902599
    +B0951424545889N00634035EA0257602605
    +B0951444545886N00634018EA0258402612
    +B0951464545885N00634003EA0258902620
    +B0951484545884N00633991EA0258902625
    +B0951504545882N00633978EA0258802628
    +B0951524545879N00633963EA0258602629
    +B0951544545875N00633946EA0258502627
    +B0951564545871N00633928EA0258502627
    +B0951584545867N00633910EA0258702628
    +B0952004545864N00633893EA0258702629
    +B0952024545860N00633878EA0258602628
    +B0952044545855N00633862EA0258302627
    +B0952064545850N00633845EA0258102625
    +B0952084545846N00633828EA0258002624
    +B0952104545842N00633812EA0257702622
    +B0952124545837N00633797EA0257502619
    +B0952144545833N00633780EA0257102616
    +B0952164545829N00633764EA0256802613
    +B0952184545826N00633747EA0256602611
    +B0952204545822N00633730EA0256502609
    +B0952224545816N00633713EA0256502607
    +B0952244545812N00633696EA0256502608
    +B0952264545808N00633680EA0256402607
    +B0952284545804N00633663EA0256302606
    +B0952304545799N00633646EA0256602606
    +B0952324545795N00633630EA0256802607
    +B0952344545790N00633615EA0256902608
    +B0952364545785N00633598EA0256902609
    +B0952384545779N00633581EA0256902609
    +B0952404545774N00633564EA0256702609
    +B0952424545769N00633545EA0256502607
    +B0952444545763N00633526EA0256702607
    +B0952464545757N00633509EA0256702608
    +B0952484545752N00633495EA0256702608
    +B0952504545745N00633480EA0256802608
    +B0952524545740N00633464EA0257102609
    +B0952544545737N00633450EA0257302611
    +B0952564545732N00633434EA0257302611
    +B0952584545725N00633415EA0257202611
    +B0953004545718N00633398EA0257102612
    +B0953024545713N00633383EA0256502611
    +B0953044545706N00633367EA0256002608
    +B0953064545697N00633348EA0255902606
    +B0953084545690N00633332EA0255802605
    +B0953104545683N00633316EA0255502602
    +B0953124545675N00633297EA0255502600
    +B0953144545669N00633280EA0255602600
    +B0953164545665N00633264EA0255302598
    +B0953184545658N00633247EA0255102595
    +B0953204545649N00633233EA0255102594
    +B0953224545641N00633220EA0255102594
    +B0953244545635N00633203EA0255102594
    +B0953264545629N00633187EA0255002594
    +B0953284545623N00633173EA0254702592
    +B0953304545615N00633158EA0254502590
    +B0953324545607N00633144EA0254402589
    +B0953344545602N00633130EA0254002587
    +B0953364545595N00633117EA0253502583
    +B0953384545589N00633102EA0253002578
    +B0953404545582N00633086EA0252502573
    +B0953424545577N00633070EA0252202570
    +B0953444545573N00633055EA0251902566
    +B0953464545569N00633040EA0251602563
    +B0953484545565N00633026EA0251202559
    +B0953504545560N00633014EA0250802555
    +B0953524545555N00633001EA0250402551
    +B0953544545550N00632988EA0250002547
    +B0953564545545N00632977EA0249602544
    +B0953584545540N00632966EA0249102539
    +B0954004545534N00632954EA0248602534
    +B0954024545527N00632944EA0248202529
    +B0954044545521N00632932EA0247802523
    +B0954064545514N00632919EA0247302519
    +B0954084545507N00632905EA0247102515
    +B0954104545500N00632891EA0247002513
    +B0954124545494N00632877EA0246902512
    +B0954144545489N00632862EA0246802511
    +B0954164545484N00632847EA0246702509
    +B0954184545480N00632831EA0246502508
    +B0954204545475N00632816EA0246202505
    +B0954224545469N00632804EA0245902503
    +B0954244545462N00632791EA0245502500
    +B0954264545455N00632778EA0245302497
    +B0954284545449N00632766EA0245102495
    +B0954304545442N00632753EA0244802492
    +B0954324545436N00632740EA0244502489
    +B0954344545430N00632725EA0244402487
    +B0954364545425N00632710EA0244102485
    +B0954384545419N00632696EA0243902483
    +B0954404545413N00632680EA0243802481
    +B0954424545407N00632666EA0243602479
    +B0954444545401N00632653EA0243302477
    +B0954464545395N00632638EA0243102474
    +B0954484545388N00632622EA0242802471
    +B0954504545382N00632606EA0242702470
    +B0954524545376N00632591EA0242502468
    +B0954544545371N00632576EA0242202465
    +B0954564545365N00632560EA0242002462
    +B0954584545360N00632544EA0241702460
    +B0955004545355N00632529EA0241402458
    +B0955024545349N00632514EA0241002454
    +B0955044545344N00632499EA0240702451
    +B0955064545338N00632483EA0240402447
    +B0955084545332N00632469EA0240102444
    +B0955104545326N00632454EA0239802441
    +B0955124545321N00632439EA0239402437
    +B0955144545316N00632424EA0239102435
    +B0955164545311N00632409EA0238802432
    +B0955184545308N00632393EA0238702430
    +B0955204545305N00632379EA0238402427
    +B0955224545303N00632364EA0238102424
    +B0955244545301N00632348EA0237902421
    +B0955264545299N00632333EA0237702420
    +B0955284545298N00632319EA0237202417
    +B0955304545295N00632304EA0236702412
    +B0955324545289N00632290EA0236502410
    +B0955344545283N00632279EA0236302408
    +B0955364545278N00632267EA0235902404
    +B0955384545271N00632254EA0235602401
    +B0955404545264N00632242EA0235302398
    +B0955424545259N00632228EA0235002395
    +B0955444545253N00632214EA0234702392
    +B0955464545246N00632199EA0234702390
    +B0955484545241N00632184EA0234602389
    +B0955504545237N00632170EA0234302388
    +B0955524545231N00632158EA0233702385
    +B0955544545224N00632144EA0233202380
    +B0955564545219N00632127EA0233002377
    +B0955584545214N00632111EA0232902376
    +B0956004545210N00632097EA0232802374
    +B0956024545207N00632081EA0232802373
    +B0956044545202N00632066EA0233002373
    +B0956064545198N00632053EA0232802373
    +B0956084545194N00632038EA0232602371
    +B0956104545188N00632023EA0232502369
    +B0956124545182N00632010EA0232402369
    +B0956144545177N00631996EA0232202367
    +B0956164545172N00631982EA0231802364
    +B0956184545166N00631969EA0231402359
    +B0956204545161N00631954EA0231002355
    +B0956224545156N00631939EA0230902352
    +B0956244545153N00631925EA0231102351
    +B0956264545150N00631912EA0231202351
    +B0956284545147N00631897EA0231102350
    +B0956304545144N00631881EA0231202351
    +B0956324545141N00631865EA0231302351
    +B0956344545138N00631849EA0231302352
    +B0956364545134N00631833EA0231402352
    +B0956384545132N00631817EA0231702355
    +B0956404545130N00631803EA0231802357
    +B0956424545127N00631789EA0231802357
    +B0956444545123N00631775EA0231902358
    +B0956464545119N00631759EA0232302360
    +B0956484545114N00631745EA0232402363
    +B0956504545110N00631732EA0232102363
    +B0956524545106N00631718EA0231902362
    +B0956544545103N00631703EA0231602359
    +B0956564545099N00631687EA0231402356
    +B0956584545094N00631671EA0231302355
    +B0957004545091N00631656EA0231102354
    +B0957024545088N00631641EA0230902352
    +B0957044545085N00631626EA0230902351
    +B0957064545082N00631613EA0230802350
    +B0957084545079N00631599EA0230502347
    +B0957104545077N00631583EA0230902346
    +B0957124545075N00631569EA0231102346
    +B0957144545073N00631556EA0231302348
    +B0957164545072N00631543EA0231402350
    +B0957184545070N00631527EA0231602352
    +B0957204545068N00631512EA0231902355
    +B0957224545066N00631496EA0232102357
    +B0957244545063N00631481EA0232202359
    +B0957264545060N00631465EA0232502362
    +B0957284545058N00631449EA0232802365
    +B0957304545056N00631435EA0232902367
    +B0957324545052N00631422EA0233002369
    +B0957344545048N00631407EA0233202370
    +B0957364545045N00631392EA0233302372
    +B0957384545042N00631375EA0233502373
    +B0957404545040N00631357EA0234002376
    +B0957424545037N00631341EA0234602380
    +B0957444545033N00631326EA0235102385
    +B0957464545030N00631312EA0235602390
    +B0957484545027N00631297EA0236102395
    +B0957504545023N00631281EA0236802402
    +B0957524545021N00631266EA0237302408
    +B0957544545020N00631252EA0237502413
    +B0957564545020N00631236EA0237802416
    +B0957584545018N00631219EA0237902418
    +B0958004545015N00631202EA0238002419
    +B0958024545011N00631183EA0238002420
    +B0958044545009N00631165EA0238002421
    +B0958064545007N00631150EA0237902420
    +B0958084545006N00631134EA0237902420
    +B0958104545007N00631118EA0237702420
    +B0958124545004N00631104EA0237702419
    +B0958144545002N00631090EA0237902420
    +B0958164545001N00631077EA0237902421
    +B0958184544999N00631063EA0238002421
    +B0958204544997N00631048EA0238102422
    +B0958224544995N00631033EA0238402424
    +B0958244544992N00631018EA0238702427
    +B0958264544988N00631003EA0239502431
    +B0958284544986N00630989EA0240402437
    +B0958304544985N00630978EA0241202445
    +B0958324544983N00630970EA0241802452
    +B0958344544976N00630965EA0242202458
    +B0958364544966N00630975EA0242802464
    +B0958384544966N00630991EA0243402471
    +B0958404544978N00631001EA0244102478
    +B0958424544991N00630994EA0244902486
    +B0958444544995N00630978EA0245802496
    +B0958464544994N00630966EA0246602505
    +B0958484544990N00630957EA0247002511
    +B0958504544981N00630954EA0247302517
    +B0958524544973N00630968EA0247602521
    +B0958544544977N00630985EA0248202525
    +B0958564544991N00630991EA0248902531
    +B0958584545004N00630984EA0249702538
    +B0959004545010N00630969EA0250302544
    +B0959024545011N00630952EA0251002551
    +B0959044545011N00630939EA0251602558
    +B0959064545010N00630927EA0251902563
    +B0959084545007N00630914EA0252202566
    +B0959104545002N00630899EA0252502569
    +B0959124545000N00630885EA0252602572
    +B0959144544998N00630871EA0252602573
    +B0959164544995N00630855EA0252602573
    +B0959184544992N00630838EA0252702574
    +B0959204544990N00630824EA0252602574
    +B0959224544985N00630809EA0252302572
    +B0959244544980N00630795EA0252302571
    +B0959264544975N00630782EA0252302571
    +B0959284544970N00630770EA0252302570
    +B0959304544964N00630758EA0252302570
    +B0959324544957N00630746EA0252402570
    +B0959344544951N00630731EA0252202570
    +B0959364544948N00630716EA0252002568
    +B0959384544944N00630703EA0251702565
    +B0959404544942N00630690EA0251302562
    +B0959424544940N00630675EA0250802557
    +B0959444544937N00630660EA0250302552
    +B0959464544931N00630645EA0249902547
    +B0959484544925N00630630EA0249302542
    +B0959504544919N00630614EA0248802536
    +B0959524544912N00630599EA0248402532
    +B0959544544906N00630584EA0248102527
    +B0959564544902N00630567EA0248102524
    +B0959584544897N00630552EA0248202523
    +B1000004544891N00630540EA0248302524
    +B1000024544887N00630527EA0248302524
    +B1000044544883N00630512EA0248302524
    +B1000064544879N00630495EA0248802525
    +B1000084544875N00630480EA0249502527
    +B1000104544872N00630470EA0249802531
    +B1000124544869N00630457EA0250202534
    +B1000144544865N00630444EA0250902540
    +B1000164544861N00630432EA0251202545
    +B1000184544858N00630421EA0250902549
    +B1000204544854N00630405EA0250902551
    +B1000224544852N00630388EA0251302556
    +B1000244544848N00630375EA0251502559
    +B1000264544845N00630362EA0251602560
    +B1000284544841N00630348EA0251502560
    +B1000304544836N00630331EA0251402560
    +B1000324544830N00630313EA0251602560
    +B1000344544825N00630294EA0252302562
    +B1000364544821N00630280EA0252702565
    +B1000384544817N00630270EA0252802567
    +B1000404544813N00630258EA0252902568
    +B1000424544810N00630242EA0253002570
    +B1000444544808N00630226EA0253102571
    +B1000464544806N00630210EA0253002571
    +B1000484544804N00630194EA0252902571
    +B1000504544799N00630178EA0252802570
    +B1000524544794N00630164EA0252602569
    +B1000544544789N00630148EA0252302566
    +B1000564544785N00630130EA0252202565
    +B1000584544781N00630113EA0252102564
    +B1001004544776N00630097EA0251802562
    +B1001024544771N00630081EA0251402559
    +B1001044544765N00630066EA0250902555
    +B1001064544759N00630049EA0250602551
    +B1001084544754N00630032EA0250402548
    +B1001104544749N00630015EA0250202546
    +B1001124544745N00629999EA0249902543
    +B1001144544740N00629982EA0249702541
    +B1001164544735N00629964EA0249502539
    +B1001184544731N00629947EA0249302537
    +B1001204544728N00629930EA0249202535
    +B1001224544724N00629913EA0249102534
    +B1001244544720N00629895EA0249102534
    +B1001264544716N00629879EA0249002533
    +B1001284544712N00629863EA0248602531
    +B1001304544708N00629845EA0248702531
    +B1001324544705N00629828EA0248802531
    +B1001344544703N00629815EA0248702530
    +B1001364544701N00629800EA0248802530
    +B1001384544699N00629782EA0249402530
    +B1001404544697N00629765EA0250302533
    +B1001424544695N00629748EA0251002538
    +B1001444544693N00629731EA0251402543
    +B1001464544689N00629713EA0251902550
    +B1001484544687N00629694EA0252402555
    +B1001504544686N00629675EA0252602559
    +B1001524544685N00629654EA0252802562
    +B1001544544683N00629634EA0253002566
    +B1001564544681N00629613EA0253602569
    +B1001584544682N00629590EA0254402574
    +B1002004544684N00629570EA0254902580
    +B1002024544684N00629553EA0255102584
    +B1002044544683N00629534EA0255402587
    +B1002064544679N00629515EA0255902592
    +B1002084544675N00629496EA0256502599
    +B1002104544669N00629480EA0256502603
    +B1002124544664N00629463EA0256402606
    +B1002144544661N00629445EA0256502607
    +B1002164544659N00629430EA0256702610
    +B1002184544655N00629417EA0256702611
    +B1002204544652N00629404EA0256302611
    +B1002224544647N00629388EA0256002609
    +B1002244544642N00629371EA0255902607
    +B1002264544637N00629355EA0255702606
    +B1002284544633N00629340EA0255602604
    +B1002304544627N00629324EA0255502602
    +B1002324544621N00629309EA0255502601
    +B1002344544616N00629295EA0255502601
    +B1002364544612N00629282EA0255202599
    +B1002384544606N00629268EA0254902597
    +B1002404544601N00629251EA0254902595
    +B1002424544598N00629235EA0255002595
    +B1002444544594N00629222EA0254902594
    +B1002464544590N00629208EA0254702592
    +B1002484544584N00629193EA0254602591
    +B1002504544579N00629179EA0254702591
    +B1002524544574N00629165EA0254702591
    +B1002544544569N00629150EA0254602590
    +B1002564544565N00629136EA0254602590
    +B1002584544560N00629122EA0254502589
    +B1003004544555N00629107EA0254502589
    +B1003024544551N00629093EA0254402588
    +B1003044544546N00629078EA0254302587
    +B1003064544541N00629063EA0254202586
    +B1003084544537N00629049EA0254002585
    +B1003104544532N00629035EA0253802583
    +B1003124544528N00629020EA0253602581
    +B1003144544523N00629005EA0253402579
    +B1003164544518N00628990EA0253302577
    +B1003184544513N00628975EA0253102575
    +B1003204544508N00628960EA0252802572
    +B1003224544503N00628944EA0252402569
    +B1003244544498N00628928EA0252002566
    +B1003264544494N00628912EA0251302561
    +B1003284544490N00628894EA0250502555
    +B1003304544486N00628876EA0250102550
    +B1003324544485N00628860EA0249302544
    +B1003344544484N00628844EA0248202537
    +B1003364544481N00628826EA0247502529
    +B1003384544479N00628810EA0246902522
    +B1003404544478N00628794EA0246102514
    +B1003424544476N00628777EA0245602507
    +B1003444544472N00628760EA0245502502
    +B1003464544466N00628746EA0245202498
    +B1003484544460N00628732EA0244802493
    +B1003504544453N00628717EA0244302489
    +B1003524544446N00628700EA0244002485
    +B1003544544442N00628683EA0243502480
    +B1003564544439N00628666EA0242902475
    +B1003584544434N00628648EA0242502470
    +B1004004544430N00628630EA0242202466
    +B1004024544427N00628612EA0241802462
    +B1004044544422N00628594EA0241302457
    +B1004064544417N00628575EA0240902454
    +B1004084544412N00628557EA0240602451
    +B1004104544407N00628540EA0240202446
    +B1004124544403N00628522EA0239702441
    +B1004144544398N00628502EA0239602438
    +B1004164544394N00628484EA0239402436
    +B1004184544390N00628467EA0238902432
    +B1004204544386N00628450EA0238202427
    +B1004224544383N00628429EA0237702422
    +B1004244544378N00628410EA0237602419
    +B1004264544375N00628393EA0237202416
    +B1004284544371N00628375EA0236602411
    +B1004304544365N00628357EA0236202406
    +B1004324544359N00628338EA0236002404
    +B1004344544354N00628321EA0235702401
    +B1004364544349N00628303EA0235302396
    +B1004384544344N00628285EA0234902393
    +B1004404544339N00628265EA0234702390
    +B1004424544335N00628247EA0234202386
    +B1004444544330N00628228EA0233802382
    +B1004464544326N00628208EA0233402378
    +B1004484544323N00628189EA0233002374
    +B1004504544320N00628169EA0232602370
    +B1004524544317N00628150EA0232202366
    +B1004544544314N00628129EA0231802362
    +B1004564544311N00628109EA0231502359
    +B1004584544308N00628089EA0231202356
    +B1005004544306N00628069EA0230802352
    +B1005024544303N00628049EA0230402348
    +B1005044544300N00628029EA0230002344
    +B1005064544297N00628007EA0229702340
    +B1005084544294N00627987EA0229402337
    +B1005104544290N00627967EA0229102333
    +B1005124544285N00627947EA0228602329
    +B1005144544281N00627927EA0228302326
    +B1005164544277N00627907EA0228002323
    +B1005184544273N00627887EA0227702319
    +B1005204544269N00627867EA0227402317
    +B1005224544265N00627847EA0227102313
    +B1005244544261N00627827EA0226902310
    +B1005264544258N00627806EA0226702308
    +B1005284544255N00627787EA0226402306
    +B1005304544252N00627767EA0226102303
    +B1005324544249N00627746EA0225802300
    +B1005344544247N00627724EA0225602298
    +B1005364544245N00627704EA0225402296
    +B1005384544243N00627683EA0225102293
    +B1005404544241N00627661EA0224902290
    +B1005424544240N00627639EA0224802288
    +B1005444544239N00627618EA0224602286
    +B1005464544238N00627598EA0224302284
    +B1005484544237N00627577EA0224002281
    +B1005504544236N00627555EA0223802279
    +B1005524544235N00627534EA0223702277
    +B1005544544233N00627513EA0223502275
    +B1005564544231N00627491EA0223402273
    +B1005584544230N00627470EA0223202272
    +B1006004544227N00627449EA0222902270
    +B1006024544223N00627429EA0222702268
    +B1006044544219N00627408EA0222602266
    +B1006064544216N00627387EA0222402265
    +B1006084544211N00627366EA0222302263
    +B1006104544207N00627345EA0222102262
    +B1006124544202N00627324EA0222102261
    +B1006144544198N00627303EA0221902260
    +B1006164544193N00627283EA0221702258
    +B1006184544188N00627263EA0221502256
    +B1006204544182N00627242EA0221402255
    +B1006224544179N00627221EA0221202253
    +B1006244544174N00627200EA0221002251
    +B1006264544169N00627179EA0220802249
    +B1006284544165N00627158EA0220602247
    +B1006304544159N00627138EA0220402245
    +B1006324544153N00627119EA0220202243
    +B1006344544148N00627098EA0219802240
    +B1006364544144N00627077EA0219702238
    +B1006384544141N00627055EA0219402236
    +B1006404544141N00627032EA0219102233
    +B1006424544141N00627009EA0219002231
    +B1006444544141N00626986EA0218702229
    +B1006464544141N00626964EA0218502226
    +B1006484544141N00626942EA0218302224
    +B1006504544141N00626919EA0218102222
    +B1006524544141N00626897EA0217902220
    +B1006544544140N00626874EA0217702218
    +B1006564544140N00626852EA0217502216
    +B1006584544140N00626829EA0217302214
    +B1007004544139N00626807EA0217102212
    +B1007024544138N00626785EA0216902210
    +B1007044544138N00626763EA0216702208
    +B1007064544139N00626740EA0216502205
    +B1007084544139N00626717EA0216302203
    +B1007104544140N00626695EA0216102202
    +B1007124544140N00626672EA0215902200
    +B1007144544139N00626650EA0215802197
    +B1007164544138N00626628EA0215602196
    +B1007184544138N00626605EA0215402193
    +B1007204544138N00626583EA0215102191
    +B1007224544137N00626561EA0214902189
    +B1007244544137N00626538EA0214702187
    +B1007264544137N00626516EA0214502184
    +B1007284544136N00626494EA0214202182
    +B1007304544135N00626471EA0214102180
    +B1007324544134N00626449EA0213802177
    +B1007344544133N00626427EA0213602175
    +B1007364544132N00626405EA0213402173
    +B1007384544131N00626383EA0213102170
    +B1007404544129N00626361EA0212902168
    +B1007424544128N00626340EA0212702166
    +B1007444544126N00626318EA0212502164
    +B1007464544124N00626297EA0212302161
    +B1007484544121N00626276EA0212002159
    +B1007504544119N00626254EA0211802157
    +B1007524544117N00626233EA0211602154
    +B1007544544115N00626211EA0211302152
    +B1007564544113N00626190EA0211002150
    +B1007584544111N00626169EA0210702147
    +B1008004544109N00626147EA0210402144
    +B1008024544106N00626126EA0210102141
    +B1008044544103N00626106EA0209902138
    +B1008064544100N00626085EA0209602135
    +B1008084544097N00626064EA0209302132
    +B1008104544093N00626043EA0209002129
    +B1008124544090N00626022EA0208702126
    +B1008144544087N00626001EA0208502123
    +B1008164544085N00625980EA0208302121
    +B1008184544082N00625958EA0208002118
    +B1008204544080N00625938EA0207702116
    +B1008224544077N00625917EA0207502113
    +B1008244544075N00625896EA0207302111
    +B1008264544072N00625875EA0207002109
    +B1008284544069N00625854EA0206802106
    +B1008304544068N00625833EA0206602104
    +B1008324544066N00625812EA0206302101
    +B1008344544064N00625791EA0206102099
    +B1008364544062N00625770EA0205902097
    +B1008384544061N00625749EA0205702095
    +B1008404544059N00625728EA0205502093
    +B1008424544057N00625708EA0205202091
    +B1008444544055N00625687EA0205002088
    +B1008464544053N00625667EA0204802086
    +B1008484544050N00625647EA0204602084
    +B1008504544047N00625626EA0204402082
    +B1008524544044N00625607EA0204202080
    +B1008544544040N00625587EA0203902077
    +B1008564544037N00625567EA0203702074
    +B1008584544034N00625547EA0203302072
    +B1009004544031N00625528EA0203102070
    +B1009024544027N00625508EA0202902068
    +B1009044544023N00625488EA0202802065
    +B1009064544019N00625469EA0202602063
    +B1009084544014N00625450EA0202302061
    +B1009104544010N00625430EA0202102059
    +B1009124544006N00625410EA0202002057
    +B1009144544002N00625391EA0201702055
    +B1009164543997N00625372EA0201502053
    +B1009184543991N00625353EA0201302050
    +B1009204543985N00625335EA0201102049
    +B1009224543978N00625317EA0200902047
    +B1009244543972N00625299EA0200602044
    +B1009264543967N00625279EA0200402042
    +B1009284543962N00625260EA0200302040
    +B1009304543959N00625240EA0200102038
    +B1009324543956N00625221EA0199902036
    +B1009344543953N00625201EA0199602034
    +B1009364543950N00625180EA0199402032
    +B1009384543948N00625161EA0199202030
    +B1009404543946N00625141EA0199002028
    +B1009424543944N00625122EA0198802025
    +B1009444543942N00625102EA0198502023
    +B1009464543940N00625082EA0198302022
    +B1009484543938N00625062EA0198102020
    +B1009504543936N00625042EA0197802017
    +B1009524543934N00625022EA0197502014
    +B1009544543931N00625002EA0197402012
    +B1009564543927N00624982EA0197202010
    +B1009584543924N00624962EA0196902008
    +B1010004543920N00624942EA0196702006
    +B1010024543916N00624923EA0196502004
    +B1010044543913N00624904EA0196402002
    +B1010064543908N00624885EA0196202000
    +B1010084543904N00624866EA0196001998
    +B1010104543901N00624847EA0195701995
    +B1010124543897N00624827EA0195501992
    +B1010144543893N00624808EA0195301990
    +B1010164543890N00624790EA0195101988
    +B1010184543886N00624772EA0194701985
    +B1010204543883N00624753EA0194601983
    +B1010224543879N00624734EA0194401981
    +B1010244543875N00624716EA0194201979
    +B1010264543871N00624698EA0194001976
    +B1010284543867N00624679EA0193901975
    +B1010304543862N00624661EA0193701973
    +B1010324543858N00624643EA0193501972
    +B1010344543854N00624625EA0193401970
    +B1010364543849N00624607EA0193201968
    +B1010384543845N00624589EA0193001967
    +B1010404543840N00624571EA0192901965
    +B1010424543836N00624553EA0192701963
    +B1010444543833N00624534EA0192501961
    +B1010464543829N00624516EA0192301959
    +B1010484543825N00624498EA0192001957
    +B1010504543822N00624480EA0191801955
    +B1010524543818N00624462EA0191601953
    +B1010544543815N00624444EA0191401951
    +B1010564543811N00624426EA0191201949
    +B1010584543806N00624408EA0191001947
    +B1011004543802N00624390EA0190901945
    +B1011024543798N00624373EA0190601943
    +B1011044543793N00624355EA0190401941
    +B1011064543789N00624337EA0190201938
    +B1011084543784N00624319EA0190001936
    +B1011104543780N00624301EA0189801934
    +B1011124543776N00624283EA0189501932
    +B1011144543772N00624266EA0189201930
    +B1011164543768N00624248EA0189001927
    +B1011184543762N00624231EA0188801925
    +B1011204543755N00624215EA0188701923
    +B1011224543750N00624197EA0188401921
    +B1011244543745N00624180EA0188201919
    +B1011264543739N00624163EA0188001917
    +B1011284543734N00624145EA0187801914
    +B1011304543730N00624128EA0187501912
    +B1011324543725N00624110EA0187301910
    +B1011344543721N00624092EA0187101907
    +B1011364543717N00624074EA0186901905
    +B1011384543712N00624056EA0186601903
    +B1011404543708N00624038EA0186401900
    +B1011424543703N00624020EA0186201898
    +B1011444543698N00624003EA0185901895
    +B1011464543693N00623984EA0185701893
    +B1011484543688N00623966EA0185501891
    +B1011504543684N00623948EA0185301889
    +B1011524543679N00623930EA0185001887
    +B1011544543675N00623912EA0184801884
    +B1011564543670N00623894EA0184601882
    +B1011584543665N00623876EA0184401880
    +B1012004543660N00623858EA0184201877
    +B1012024543654N00623840EA0183901875
    +B1012044543650N00623822EA0183701873
    +B1012064543645N00623804EA0183501871
    +B1012084543640N00623786EA0183301868
    +B1012104543635N00623768EA0183001866
    +B1012124543631N00623750EA0182801864
    +B1012144543626N00623732EA0182601862
    +B1012164543621N00623714EA0182401860
    +B1012184543616N00623697EA0182201858
    +B1012204543612N00623679EA0182001856
    +B1012224543607N00623661EA0181801854
    +B1012244543603N00623643EA0181601852
    +B1012264543599N00623626EA0181401850
    +B1012284543594N00623608EA0181201848
    +B1012304543589N00623591EA0181001846
    +B1012324543585N00623573EA0180801844
    +B1012344543582N00623555EA0180601842
    +B1012364543578N00623536EA0180401840
    +B1012384543575N00623518EA0180201838
    +B1012404543571N00623499EA0180101836
    +B1012424543568N00623481EA0179901834
    +B1012444543565N00623462EA0179701833
    +B1012464543562N00623444EA0179401830
    +B1012484543558N00623426EA0179301828
    +B1012504543554N00623408EA0179101827
    +B1012524543551N00623390EA0178901825
    +B1012544543547N00623372EA0178701822
    +B1012564543544N00623354EA0178501821
    +B1012584543540N00623336EA0178301819
    +B1013004543536N00623319EA0178101817
    +B1013024543532N00623301EA0178001815
    +B1013044543528N00623283EA0177801813
    +B1013064543523N00623265EA0177701812
    +B1013084543519N00623247EA0177501810
    +B1013104543514N00623229EA0177401808
    +B1013124543509N00623212EA0177201807
    +B1013144543504N00623195EA0177001805
    +B1013164543499N00623177EA0176701803
    +B1013184543493N00623160EA0176501801
    +B1013204543489N00623142EA0176301798
    +B1013224543484N00623125EA0176001796
    +B1013244543479N00623108EA0175901794
    +B1013264543474N00623091EA0175601791
    +B1013284543469N00623074EA0175401789
    +B1013304543464N00623057EA0175101786
    +B1013324543459N00623039EA0174901784
    +B1013344543454N00623022EA0174701782
    +B1013364543450N00623005EA0174501779
    +B1013384543445N00622988EA0174301777
    +B1013404543441N00622970EA0174001775
    +B1013424543437N00622953EA0173801772
    +B1013444543433N00622935EA0173601770
    +B1013464543428N00622918EA0173301768
    +B1013484543424N00622901EA0173101765
    +B1013504543420N00622883EA0172901763
    +B1013524543416N00622866EA0172701761
    +B1013544543413N00622848EA0172501758
    +B1013564543409N00622831EA0172301756
    +B1013584543406N00622814EA0172101754
    +B1014004543402N00622796EA0171901752
    +B1014024543398N00622779EA0171701750
    +B1014044543395N00622761EA0171501748
    +B1014064543392N00622744EA0171301746
    +B1014084543388N00622727EA0171001744
    +B1014104543384N00622709EA0170801742
    +B1014124543380N00622691EA0170601740
    +B1014144543376N00622673EA0170501738
    +B1014164543373N00622656EA0170201736
    +B1014184543369N00622638EA0170001734
    +B1014204543366N00622621EA0169801732
    +B1014224543363N00622603EA0169601730
    +B1014244543359N00622585EA0169401728
    +B1014264543356N00622568EA0169101725
    +B1014284543353N00622550EA0168901723
    +B1014304543350N00622532EA0168601720
    +B1014324543347N00622515EA0168501718
    +B1014344543344N00622497EA0168201716
    +B1014364543340N00622479EA0168001714
    +B1014384543338N00622461EA0167701711
    +B1014404543335N00622444EA0167501708
    +B1014424543332N00622426EA0167201706
    +B1014444543330N00622408EA0167001703
    +B1014464543327N00622390EA0166701701
    +B1014484543324N00622372EA0166501699
    +B1014504543322N00622355EA0166301696
    +B1014524543320N00622337EA0166001694
    +B1014544543317N00622319EA0165801691
    +B1014564543314N00622302EA0165601689
    +B1014584543311N00622285EA0165301687
    +B1015004543307N00622267EA0165201684
    +B1015024543304N00622250EA0164901682
    +B1015044543300N00622234EA0164701680
    +B1015064543296N00622217EA0164401677
    +B1015084543292N00622201EA0164101674
    +B1015104543288N00622184EA0163801671
    +B1015124543284N00622168EA0163601669
    +B1015144543280N00622151EA0163301666
    +B1015164543275N00622135EA0163001663
    +B1015184543271N00622119EA0162801660
    +B1015204543266N00622103EA0162401657
    +B1015224543262N00622086EA0162101655
    +B1015244543258N00622069EA0162301654
    +B1015264543253N00622055EA0162201654
    +B1015284543247N00622042EA0161901651
    +B1015304543242N00622028EA0161601649
    +B1015324543237N00622014EA0161501648
    +B1015344543232N00622000EA0161201646
    +B1015364543228N00621986EA0160701642
    +B1015384543228N00621970EA0160501639
    +B1015404543227N00621954EA0160201637
    +B1015424543225N00621939EA0159801633
    +B1015444543224N00621923EA0159501630
    +B1015464543223N00621907EA0159201627
    +B1015484543221N00621890EA0158901624
    +B1015504543219N00621874EA0158701621
    +B1015524543217N00621856EA0158501619
    +B1015544543215N00621839EA0158401618
    +B1015564543212N00621822EA0158201616
    +B1015584543210N00621805EA0158001614
    +B1016004543208N00621788EA0157801612
    +B1016024543205N00621773EA0157301609
    +B1016044543200N00621756EA0156901605
    +B1016064543196N00621737EA0157001603
    +B1016084543191N00621721EA0156901602
    +B1016104543186N00621708EA0156401599
    +B1016124543181N00621691EA0156001595
    +B1016144543176N00621673EA0155801592
    +B1016164543171N00621654EA0155801591
    +B1016184543168N00621637EA0155901591
    +B1016204543168N00621620EA0156001591
    +B1016224543167N00621607EA0156001593
    +B1016244543166N00621594EA0156301594
    +B1016264543167N00621581EA0156601596
    +B1016284543169N00621570EA0157001600
    +B1016304543169N00621559EA0157101602
    +B1016324543168N00621548EA0157201604
    +B1016344543170N00621535EA0157501606
    +B1016364543176N00621523EA0157701608
    +B1016384543184N00621515EA0157801610
    +B1016404543192N00621507EA0157801611
    +B1016424543196N00621495EA0157601611
    +B1016444543188N00621488EA0157601611
    +B1016464543178N00621496EA0157601611
    +B1016484543176N00621515EA0157901611
    +B1016504543176N00621530EA0158301615
    +B1016524543174N00621542EA0158501617
    +B1016544543171N00621555EA0158701619
    +B1016564543165N00621565EA0158801621
    +B1016584543154N00621562EA0159201623
    +B1017004543151N00621546EA0159301626
    +B1017024543157N00621530EA0159401627
    +B1017044543170N00621523EA0159501628
    +B1017064543182N00621531EA0159701629
    +B1017084543183N00621548EA0159801630
    +B1017104543174N00621561EA0160101633
    +B1017124543164N00621567EA0160401636
    +B1017144543154N00621567EA0160801639
    +B1017164543146N00621562EA0160901642
    +B1017184543142N00621552EA0160701643
    +B1017204543148N00621534EA0160501642
    +B1017224543162N00621526EA0160501641
    +B1017244543175N00621534EA0160701641
    +B1017264543181N00621548EA0161101644
    +B1017284543186N00621561EA0161401647
    +B1017304543192N00621573EA0161701650
    +B1017324543193N00621588EA0161801651
    +B1017344543183N00621594EA0161801653
    +B1017364543175N00621585EA0161901654
    +B1017384543176N00621570EA0162001655
    +B1017404543187N00621560EA0162501657
    +B1017424543198N00621557EA0163001661
    +B1017444543206N00621554EA0163201664
    +B1017464543211N00621547EA0162701665
    +B1017484543204N00621532EA0162401666
    +B1017504543190N00621529EA0162601665
    +B1017524543183N00621544EA0162701665
    +B1017544543189N00621559EA0162901664
    +B1017564543202N00621563EA0163301667
    +B1017584543211N00621554EA0163301668
    +B1018004543213N00621541EA0163401669
    +B1018024543209N00621529EA0163301669
    +B1018044543201N00621518EA0163301669
    +B1018064543193N00621506EA0163501670
    +B1018084543189N00621493EA0163401669
    +B1018104543186N00621478EA0163101668
    +B1018124543181N00621461EA0163001666
    +B1018144543176N00621444EA0163101666
    +B1018164543171N00621429EA0163201667
    +B1018184543165N00621414EA0163201667
    +B1018204543159N00621399EA0163201667
    +B1018224543153N00621385EA0163201667
    +B1018244543147N00621374EA0162901666
    +B1018264543141N00621362EA0162401663
    +B1018284543135N00621347EA0162201660
    +B1018304543129N00621331EA0162101658
    +B1018324543126N00621316EA0161801656
    +B1018344543121N00621303EA0161401653
    +B1018364543114N00621288EA0161101650
    +B1018384543108N00621273EA0160901648
    +B1018404543102N00621260EA0160701645
    +B1018424543096N00621246EA0160301641
    +B1018444543090N00621232EA0160201639
    +B1018464543085N00621218EA0159901636
    +B1018484543080N00621203EA0159601633
    +B1018504543075N00621187EA0159401631
    +B1018524543069N00621173EA0159301629
    +B1018544543063N00621160EA0159001627
    +B1018564543057N00621147EA0158601623
    +B1018584543051N00621133EA0158301619
    +B1019004543044N00621118EA0158101617
    +B1019024543038N00621103EA0157901615
    +B1019044543034N00621089EA0157401611
    +B1019064543028N00621073EA0156901607
    +B1019084543020N00621054EA0157301605
    +B1019104543015N00621037EA0158001605
    +B1019124543012N00621024EA0158401608
    +B1019144543007N00621011EA0158701612
    +B1019164543002N00620998EA0159401617
    +B1019184542996N00620985EA0159901623
    +B1019204542988N00620978EA0159901626
    +B1019224542977N00620986EA0159701628
    +B1019244542976N00621006EA0159801630
    +B1019264542988N00621013EA0160101632
    +B1019284543000N00621003EA0160401635
    +B1019304543006N00620985EA0161101639
    +B1019324543008N00620968EA0161701647
    +B1019344543008N00620956EA0161801650
    +B1019364543003N00620947EA0161601652
    +B1019384542992N00620942EA0161301652
    +B1019404542982N00620954EA0160901651
    +B1019424542983N00620972EA0160701647
    +B1019444542996N00620978EA0160501644
    +B1019464543006N00620966EA0160501641
    +B1019484543006N00620948EA0160501641
    +B1019504542999N00620932EA0160601640
    +B1019524542990N00620921EA0160801641
    +B1019544542982N00620912EA0160601642
    +B1019564542975N00620905EA0160001640
    +B1019584542965N00620899EA0159501636
    +B1020004542955N00620889EA0159101632
    +B1020024542946N00620878EA0158801627
    +B1020044542935N00620867EA0158901624
    +B1020064542924N00620859EA0159101623
    +B1020084542916N00620852EA0159101622
    +B1020104542906N00620848EA0158601619
    +B1020124542895N00620842EA0158501617
    +B1020144542886N00620834EA0158401616
    +B1020164542878N00620827EA0158201615
    +B1020184542870N00620820EA0157901612
    +B1020204542861N00620811EA0157801610
    +B1020224542853N00620801EA0157801609
    +B1020244542844N00620791EA0157901610
    +B1020264542836N00620783EA0158101611
    +B1020284542827N00620775EA0158301613
    +B1020304542819N00620765EA0158601616
    +B1020324542811N00620755EA0158901618
    +B1020344542802N00620746EA0159401622
    +B1020364542794N00620738EA0160001627
    +B1020384542787N00620729EA0160201631
    +B1020404542778N00620727EA0159901634
    +B1020424542771N00620739EA0159501636
    +B1020444542778N00620754EA0159201636
    +B1020464542795N00620753EA0159701637
    +B1020484542807N00620749EA0160201640
    +B1020504542816N00620749EA0160401641
    +B1020524542827N00620749EA0160901645
    +B1020544542838N00620749EA0161401649
    +B1020564542845N00620758EA0161201652
    +B1020584542843N00620773EA0161001652
    +B1021004542831N00620776EA0161301653
    +B1021024542822N00620766EA0161801655
    +B1021044542818N00620754EA0162201659
    +B1021064542813N00620744EA0162101661
    +B1021084542804N00620733EA0162401663
    +B1021104542795N00620720EA0163001667
    +B1021124542788N00620711EA0163201670
    +B1021144542781N00620703EA0163201671
    +B1021164542771N00620694EA0163401673
    +B1021184542763N00620682EA0163901676
    +B1021204542758N00620669EA0164001679
    +B1021224542752N00620658EA0163801680
    +B1021244542742N00620651EA0163701679
    +B1021264542733N00620662EA0163901679
    +B1021284542738N00620677EA0164401680
    +B1021304542749N00620681EA0164901683
    +B1021324542759N00620682EA0165101686
    +B1021344542766N00620690EA0165101686
    +B1021364542764N00620703EA0165301687
    +B1021384542753N00620700EA0165201687
    +B1021404542745N00620685EA0165501689
    +B1021424542743N00620667EA0165901692
    +B1021454542740N00620644EA0166401697
    +B1021474542736N00620633EA0166501699
    +B1021494542728N00620631EA0166401700
    +B1021514542721N00620644EA0166501699
    +B1021534542724N00620659EA0167101701
    +B1021554542731N00620666EA0167601706
    +B1021574542741N00620665EA0167701708
    +B1021594542753N00620662EA0168101712
    +B1022014542763N00620661EA0168301716
    +B1022034542772N00620662EA0168401717
    +B1022054542782N00620666EA0168401718
    +B1022074542786N00620677EA0168301718
    +B1022094542780N00620688EA0168201717
    +B1022114542769N00620683EA0168501718
    +B1022134542763N00620666EA0169101720
    +B1022154542759N00620652EA0169401724
    +B1022174542754N00620641EA0169501725
    +B1022194542748N00620626EA0169701727
    +B1022214542742N00620615EA0170101731
    +B1022234542736N00620608EA0170101732
    +B1022254542730N00620601EA0170101733
    +B1022274542725N00620590EA0170401735
    +B1022294542720N00620579EA0171001738
    +B1022314542715N00620570EA0171301743
    +B1022334542710N00620565EA0171301746
    +B1022354542701N00620572EA0171101746
    +B1022374542699N00620592EA0171201746
    +B1022394542711N00620604EA0171701747
    +B1022414542725N00620602EA0172301751
    +B1022434542735N00620603EA0172601755
    +B1022454542746N00620607EA0172501756
    +B1022474542754N00620616EA0172401756
    +B1022494542751N00620632EA0172501755
    +B1022514542740N00620635EA0172801757
    +B1022534542733N00620623EA0173001760
    +B1022554542730N00620609EA0173201763
    +B1022574542725N00620600EA0173301764
    +B1022594542715N00620600EA0173401766
    +B1023014542709N00620613EA0173401767
    +B1023034542712N00620630EA0173601768
    +B1023054542722N00620637EA0173801770
    +B1023074542734N00620629EA0173801772
    +B1023094542741N00620611EA0173901773
    +B1023114542738N00620595EA0174201776
    +B1023134542732N00620582EA0174601779
    +B1023154542725N00620571EA0174901782
    +B1023174542716N00620563EA0175401787
    +B1023194542707N00620564EA0175401789
    +B1023214542701N00620578EA0175001791
    +B1023234542707N00620593EA0174901790
    +B1023254542719N00620598EA0175101792
    +B1023274542730N00620596EA0175301792
    +B1023294542740N00620593EA0175401792
    +B1023314542751N00620593EA0175601794
    +B1023334542761N00620594EA0175901796
    +B1023354542771N00620597EA0176101798
    +B1023374542781N00620601EA0176301800
    +B1023394542790N00620604EA0176501801
    +B1023414542797N00620610EA0176601803
    +B1023434542801N00620621EA0176701804
    +B1023454542793N00620633EA0176901804
    +B1023474542783N00620628EA0177101806
    +B1023494542778N00620612EA0177501809
    +B1023514542774N00620598EA0177901813
    +B1023534542770N00620587EA0178001815
    +B1023554542764N00620578EA0178301817
    +B1023574542758N00620568EA0178701821
    +B1023594542750N00620568EA0178901823
    +B1024014542744N00620583EA0179101826
    +B1024034542750N00620596EA0179401827
    +B1024054542764N00620595EA0179801831
    +B1024074542776N00620589EA0180201835
    +B1024094542786N00620585EA0180201837
    +B1024114542796N00620595EA0180101838
    +B1024134542795N00620612EA0180101839
    +B1024154542785N00620613EA0180001839
    +B1024174542775N00620597EA0180401841
    +B1024194542769N00620582EA0180901845
    +B1024214542763N00620572EA0181201848
    +B1024234542757N00620560EA0181601852
    +B1024254542751N00620548EA0182201857
    +B1024274542746N00620537EA0182801863
    +B1024294542740N00620529EA0182801867
    +B1024314542730N00620532EA0182601870
    +B1024334542724N00620549EA0182701870
    +B1024354542732N00620561EA0183201871
    +B1024374542744N00620553EA0183501873
    +B1024394542749N00620531EA0184201876
    +B1024414542744N00620514EA0184701881
    +B1024434542735N00620509EA0184601883
    +B1024454542725N00620507EA0184301882
    +B1024474542714N00620503EA0184001879
    +B1024494542703N00620497EA0183801877
    +B1024514542692N00620494EA0183501874
    +B1024534542681N00620492EA0183401871
    +B1024554542671N00620489EA0183601871
    +B1024574542662N00620489EA0183801872
    +B1024594542653N00620492EA0183801873
    +B1025014542644N00620494EA0183801873
    +B1025034542635N00620492EA0183801873
    +B1025054542625N00620488EA0183901874
    +B1025074542615N00620484EA0184201875
    +B1025094542604N00620479EA0185101878
    +B1025114542597N00620475EA0185801883
    +B1025134542589N00620473EA0186101887
    +B1025154542580N00620483EA0186501891
    +B1025174542580N00620503EA0186601895
    +B1025194542592N00620510EA0186401897
    +B1025214542606N00620499EA0186901901
    +B1025234542611N00620485EA0187501906
    +B1025254542610N00620471EA0188101912
    +B1025274542602N00620457EA0188601918
    +B1025294542590N00620457EA0189001923
    +B1025314542584N00620471EA0189401928
    +B1025334542582N00620488EA0190101935
    +B1025354542591N00620502EA0190301939
    +B1025374542607N00620504EA0190601942
    +B1025394542616N00620492EA0191201946
    +B1025414542610N00620477EA0191801951
    +B1025434542597N00620472EA0192601957
    +B1025454542586N00620469EA0193101964
    +B1025474542578N00620463EA0193301968
    +B1025494542568N00620456EA0193701971
    +B1025514542558N00620449EA0194301977
    +B1025534542550N00620442EA0194701982
    +B1025554542541N00620436EA0194901984
    +B1025574542531N00620429EA0195001986
    +B1025594542521N00620420EA0195201987
    +B1026014542511N00620411EA0195301988
    +B1026034542501N00620401EA0195401990
    +B1026054542491N00620391EA0195601992
    +B1026074542482N00620381EA0195701993
    +B1026094542472N00620372EA0195601993
    +B1026114542463N00620363EA0195401992
    +B1026134542453N00620353EA0195101989
    +B1026154542442N00620344EA0195001988
    +B1026174542431N00620336EA0194801986
    +B1026194542421N00620326EA0194701984
    +B1026214542409N00620315EA0194801984
    +B1026234542399N00620303EA0194701984
    +B1026254542388N00620293EA0194801984
    +B1026274542376N00620287EA0194901985
    +B1026294542365N00620283EA0195101987
    +B1026314542354N00620280EA0195201988
    +B1026334542343N00620275EA0195201988
    +B1026354542331N00620269EA0195301988
    +B1026374542319N00620260EA0195401989
    +B1026394542308N00620249EA0195501991
    +B1026414542297N00620237EA0195601992
    +B1026434542286N00620229EA0195601993
    +B1026454542275N00620224EA0195701993
    +B1026474542264N00620218EA0195401992
    +B1026494542254N00620213EA0194901990
    +B1026514542239N00620208EA0195201989
    +B1026534542226N00620204EA0195501990
    +B1026554542217N00620199EA0195601990
    +B1026574542208N00620192EA0195701991
    +B1026594542199N00620185EA0196001994
    +B1027014542191N00620175EA0196201996
    +B1027034542182N00620166EA0196501998
    +B1027054542173N00620158EA0196702001
    +B1027074542164N00620151EA0196702002
    +B1027094542155N00620145EA0196602001
    +B1027114542146N00620139EA0196602001
    +B1027134542138N00620131EA0196502002
    +B1027154542132N00620123EA0196202001
    +B1027174542123N00620115EA0196302000
    +B1027194542113N00620107EA0196502000
    +B1027214542106N00620100EA0196502001
    +B1027234542098N00620094EA0196201999
    +B1027254542090N00620088EA0196201998
    +B1027274542080N00620084EA0196001997
    +B1027294542070N00620079EA0195801995
    +B1027314542060N00620072EA0195501993
    +B1027334542050N00620064EA0195301990
    +B1027354542040N00620057EA0195101989
    +B1027374542032N00620049EA0194701986
    +B1027394542022N00620043EA0194201981
    +B1027414542012N00620036EA0193701976
    +B1027434542002N00620027EA0193301973
    +B1027454541994N00620019EA0192801967
    +B1027474541984N00620011EA0192301962
    +B1027494541974N00620003EA0192001958
    +B1027514541964N00619995EA0191601955
    +B1027534541955N00619989EA0191201950
    +B1027554541945N00619982EA0190801946
    +B1027574541936N00619974EA0190401942
    +B1027594541927N00619968EA0190101938
    +B1028014541916N00619961EA0190201936
    +B1028034541904N00619952EA0190901935
    +B1028054541894N00619945EA0191401936
    +B1028074541885N00619941EA0191601940
    +B1028094541876N00619936EA0191601940
    +B1028114541866N00619930EA0191601941
    +B1028134541855N00619925EA0191701942
    +B1028154541844N00619922EA0191601943
    +B1028174541833N00619919EA0191401943
    +B1028194541823N00619912EA0191101941
    +B1028214541813N00619905EA0190601939
    +B1028234541803N00619899EA0190201934
    +B1028254541792N00619891EA0190201933
    +B1028274541782N00619885EA0190301932
    +B1028294541773N00619878EA0190301932
    +B1028314541763N00619872EA0190201931
    +B1028334541751N00619865EA0190601932
    +B1028354541740N00619859EA0190901935
    +B1028374541731N00619852EA0191101938
    +B1028394541722N00619843EA0191401941
    +B1028414541713N00619831EA0192201945
    +B1028434541705N00619822EA0192901951
    +B1028454541699N00619813EA0193201956
    +B1028474541693N00619805EA0193401959
    +B1028494541686N00619796EA0193801963
    +B1028514541679N00619788EA0194301969
    +B1028534541674N00619780EA0194601974
    +B1028554541667N00619777EA0194601977
    +B1028574541658N00619789EA0194801979
    +B1028594541658N00619807EA0195001983
    +B1029014541667N00619817EA0195301986
    +B1029034541676N00619824EA0195701990
    +B1029054541685N00619831EA0196001994
    +B1029074541696N00619827EA0196301997
    +B1029094541704N00619812EA0196702002
    +B1029114541705N00619795EA0197102006
    +B1029134541701N00619781EA0197302009
    +B1029154541693N00619771EA0197702012
    +B1029174541683N00619767EA0198202016
    +B1029194541673N00619767EA0198802022
    +B1029214541665N00619773EA0199402029
    +B1029234541661N00619785EA0199702033
    +B1029254541664N00619797EA0199602037
    +B1029274541673N00619804EA0199602039
    +B1029294541689N00619802EA0200102041
    +B1029314541698N00619789EA0200602045
    +B1029334541696N00619772EA0201002048
    +B1029354541690N00619758EA0201702054
    +B1029374541683N00619752EA0202202059
    +B1029394541675N00619750EA0202602063
    +B1029414541667N00619748EA0203102068
    +B1029434541660N00619758EA0203402072
    +B1029454541662N00619771EA0203602075
    +B1029474541672N00619777EA0203802077
    +B1029494541686N00619772EA0204302079
    +B1029514541694N00619754EA0204902083
    +B1029534541693N00619734EA0205402089
    +B1029554541690N00619720EA0205702093
    +B1029574541685N00619709EA0205802096
    +B1029594541678N00619700EA0205802097
    +B1030014541668N00619699EA0205802097
    +B1030034541659N00619713EA0205602096
    +B1030054541662N00619733EA0206102096
    +B1030074541669N00619746EA0207002099
    +B1030094541673N00619755EA0207702105
    +B1030114541676N00619765EA0208002109
    +B1030134541685N00619774EA0208302112
    +B1030154541698N00619768EA0208702117
    +B1030174541704N00619748EA0209302123
    +B1030194541698N00619730EA0209902128
    +B1030214541689N00619721EA0210602136
    +B1030234541681N00619723EA0211002143
    +B1030254541675N00619733EA0211402147
    +B1030274541676N00619749EA0211502151
    +B1030294541689N00619754EA0211902154
    +B1030314541702N00619744EA0212502159
    +B1030334541704N00619723EA0213002164
    +B1030354541700N00619702EA0213602171
    +B1030374541693N00619686EA0214002176
    +B1030394541685N00619676EA0214202180
    +B1030414541676N00619671EA0214402182
    +B1030434541665N00619667EA0214702185
    +B1030454541654N00619664EA0215002189
    +B1030474541643N00619662EA0215302192
    +B1030494541632N00619658EA0215502194
    +B1030514541621N00619655EA0215702196
    +B1030534541609N00619654EA0215802198
    +B1030554541597N00619653EA0215902199
    +B1030574541586N00619649EA0216102200
    +B1030594541576N00619645EA0216002201
    +B1031014541566N00619643EA0215802200
    +B1031034541557N00619640EA0215402198
    +B1031054541548N00619633EA0215102195
    +B1031074541539N00619623EA0214902192
    +B1031094541530N00619614EA0214602190
    +B1031114541520N00619606EA0214302186
    +B1031134541510N00619598EA0214002183
    +B1031154541499N00619591EA0213902180
    +B1031174541488N00619586EA0213602178
    +B1031194541478N00619580EA0213302175
    +B1031214541468N00619572EA0213002171
    +B1031234541460N00619560EA0212602167
    +B1031254541453N00619547EA0212102163
    +B1031274541444N00619535EA0211802159
    +B1031294541435N00619522EA0211602156
    +B1031314541428N00619509EA0211202152
    +B1031334541421N00619495EA0210802148
    +B1031354541414N00619480EA0210402145
    +B1031374541407N00619464EA0210102141
    +B1031394541401N00619449EA0209702138
    +B1031414541395N00619435EA0209302134
    +B1031434541388N00619419EA0208802129
    +B1031454541381N00619403EA0208602126
    +B1031474541375N00619389EA0208402123
    +B1031494541367N00619376EA0207802119
    +B1031514541358N00619363EA0207302114
    +B1031534541348N00619351EA0206802110
    +B1031554541337N00619341EA0206302105
    +B1031574541325N00619332EA0206002101
    +B1031594541314N00619322EA0205602097
    +B1032014541304N00619312EA0205202092
    +B1032034541293N00619300EA0204902088
    +B1032054541283N00619287EA0204802085
    +B1032074541273N00619276EA0204802084
    +B1032094541263N00619266EA0204702084
    +B1032114541253N00619255EA0204702084
    +B1032134541245N00619243EA0204602083
    +B1032154541237N00619231EA0204402082
    +B1032174541228N00619220EA0204002079
    +B1032194541219N00619207EA0203602075
    +B1032214541211N00619192EA0203102070
    +B1032234541203N00619177EA0202602065
    +B1032254541196N00619161EA0201902060
    +B1032274541189N00619146EA0201302053
    +B1032294541181N00619131EA0200802047
    +B1032314541172N00619115EA0200502042
    +B1032334541164N00619101EA0200102039
    +B1032354541155N00619088EA0199702035
    +B1032374541146N00619074EA0199302031
    +B1032394541136N00619059EA0199202028
    +B1032414541128N00619044EA0199202027
    +B1032434541121N00619029EA0199002024
    +B1032454541114N00619011EA0198902023
    +B1032474541108N00618993EA0198902023
    +B1032494541101N00618977EA0198602021
    +B1032514541094N00618962EA0198302018
    +B1032534541085N00618945EA0197902015
    +B1032554541077N00618929EA0197502012
    +B1032574541069N00618914EA0197102008
    +B1032594541061N00618897EA0196602003
    +B1033014541053N00618881EA0196302000
    +B1033034541045N00618865EA0195901996
    +B1033054541038N00618849EA0195501992
    +B1033074541031N00618831EA0195301988
    +B1033094541025N00618814EA0195101986
    +B1033114541018N00618797EA0194901984
    +B1033134541011N00618779EA0194601981
    +B1033154541005N00618761EA0194501980
    +B1033174541000N00618744EA0194301979
    +B1033194540995N00618726EA0194101977
    +B1033214540989N00618709EA0193801975
    +B1033234540983N00618692EA0193601973
    +B1033254540978N00618675EA0193501971
    +B1033274540972N00618657EA0193301970
    +B1033294540966N00618641EA0193201968
    +B1033314540961N00618624EA0192901966
    +B1033334540955N00618608EA0192601963
    +B1033354540949N00618591EA0192401961
    +B1033374540942N00618574EA0192201960
    +B1033394540936N00618558EA0192001958
    +B1033414540930N00618540EA0192001956
    +B1033434540925N00618523EA0191901955
    +B1033454540922N00618506EA0191601953
    +B1033474540919N00618490EA0191401951
    +B1033494540913N00618473EA0191201948
    +B1033514540908N00618456EA0191001947
    +B1033534540902N00618440EA0190901945
    +B1033554540896N00618424EA0190601943
    +B1033574540889N00618407EA0190501941
    +B1033594540884N00618391EA0190301940
    +B1034014540879N00618375EA0190101938
    +B1034034540874N00618358EA0189801935
    +B1034054540870N00618340EA0189601933
    +B1034074540864N00618323EA0189501931
    +B1034094540859N00618307EA0189301929
    +B1034114540853N00618290EA0189101928
    +B1034134540848N00618274EA0188901926
    +B1034154540842N00618258EA0188601923
    +B1034174540837N00618241EA0188501920
    +B1034194540831N00618224EA0188301919
    +B1034214540825N00618208EA0188001916
    +B1034234540820N00618191EA0187701913
    +B1034254540814N00618174EA0187401910
    +B1034274540808N00618156EA0187201908
    +B1034294540802N00618139EA0187001906
    +B1034314540796N00618122EA0186601903
    +B1034334540790N00618105EA0186501900
    +B1034354540784N00618088EA0186101898
    +B1034374540779N00618071EA0185901896
    +B1034394540773N00618054EA0185701893
    +B1034414540767N00618037EA0185301890
    +B1034434540762N00618020EA0185001887
    +B1034454540756N00618003EA0184701884
    +B1034474540750N00617986EA0184401880
    +B1034494540743N00617970EA0184201877
    +B1034514540737N00617953EA0183901875
    +B1034534540731N00617936EA0183601872
    +B1034554540725N00617919EA0183301869
    +B1034574540719N00617902EA0183001866
    +B1034594540713N00617885EA0182701863
    +B1035014540706N00617868EA0182401860
    +B1035034540700N00617851EA0182101857
    +B1035054540694N00617834EA0181801854
    +B1035074540688N00617818EA0181601851
    +B1035094540682N00617800EA0181301848
    +B1035114540676N00617783EA0181001845
    +B1035134540671N00617765EA0180501841
    +B1035154540665N00617747EA0180201838
    +B1035174540660N00617730EA0179901834
    +B1035194540654N00617713EA0179401831
    +B1035214540649N00617698EA0178901827
    +B1035234540644N00617682EA0178301821
    +B1035254540639N00617664EA0177801815
    +B1035274540634N00617648EA0177501811
    +B1035294540630N00617631EA0177101806
    +B1035314540625N00617613EA0177101803
    +B1035334540619N00617596EA0177301802
    +B1035354540615N00617580EA0177501803
    +B1035374540610N00617564EA0177701805
    +B1035394540604N00617548EA0178101808
    +B1035414540599N00617531EA0178501813
    +B1035434540596N00617516EA0178601815
    +B1035454540592N00617499EA0178801817
    +B1035474540586N00617481EA0179201821
    +B1035494540581N00617461EA0179701825
    +B1035514540578N00617442EA0180301830
    +B1035534540575N00617423EA0181101837
    +B1035554540571N00617409EA0181801845
    +B1035574540566N00617396EA0182001850
    +B1035594540553N00617391EA0182101855
    +B1036014540540N00617400EA0182501859
    +B1036034540538N00617416EA0182801863
    +B1036054540546N00617427EA0183401869
    +B1036074540552N00617433EA0184001875
    +B1036094540560N00617427EA0184301880
    +B1036114540564N00617409EA0184701884
    +B1036134540559N00617391EA0185201888
    +B1036154540548N00617381EA0185701893
    +B1036174540536N00617382EA0185701896
    +B1036194540527N00617396EA0185601897
    +B1036214540530N00617412EA0185801897
    +B1036234540541N00617419EA0186201899
    +B1036254540552N00617413EA0186801904
    +B1036274540558N00617401EA0187201908
    +B1036294540561N00617385EA0187601912
    +B1036314540563N00617369EA0188001916
    +B1036334540565N00617354EA0188301919
    +B1036354540566N00617339EA0188501922
    +B1036374540569N00617327EA0188001923
    +B1036394540572N00617314EA0187301923
    +B1036414540574N00617295EA0186901920
    +B1036434540574N00617277EA0186601915
    +B1036454540572N00617258EA0186301911
    +B1036474540569N00617239EA0186101907
    +B1036494540567N00617220EA0186501905
    +B1036514540563N00617201EA0187001904
    +B1036534540562N00617188EA0187301905
    +B1036554540563N00617174EA0187501908
    +B1036574540564N00617161EA0187701911
    +B1036594540564N00617150EA0187601911
    +B1037014540560N00617136EA0187701912
    +B1037034540555N00617117EA0188501915
    +B1037054540553N00617100EA0189101920
    +B1037074540549N00617087EA0189501925
    +B1037094540539N00617082EA0190001929
    +B1037114540531N00617089EA0190401935
    +B1037134540532N00617103EA0190501939
    +B1037154540544N00617107EA0191001943
    +B1037174540556N00617095EA0191301948
    +B1037194540563N00617079EA0191701952
    +B1037214540569N00617060EA0191901955
    +B1037234540565N00617042EA0191701957
    +B1037254540556N00617030EA0191701957
    +B1037274540544N00617030EA0191801957
    +B1037294540536N00617042EA0192001957
    +B1037314540535N00617058EA0192401959
    +B1037334540538N00617072EA0193001964
    +B1037354540540N00617083EA0193601969
    +B1037374540542N00617095EA0194501976
    +B1037394540544N00617106EA0195201984
    +B1037414540546N00617115EA0195301990
    +B1037434540559N00617118EA0195101990
    +B1037454540571N00617107EA0195301993
    +B1037474540569N00617088EA0195501994
    +B1037494540562N00617072EA0196501997
    +B1037514540558N00617057EA0197302003
    +B1037534540553N00617046EA0197602008
    +B1037554540543N00617041EA0197702012
    +B1037574540533N00617052EA0197902014
    +B1037594540538N00617068EA0198602018
    +B1038014540553N00617065EA0199102023
    +B1038034540560N00617043EA0199802028
    +B1038054540552N00617026EA0200402035
    +B1038074540541N00617026EA0200602039
    +B1038094540535N00617039EA0200902043
    +B1038114540543N00617051EA0201302047
    +B1038134540557N00617047EA0201902051
    +B1038154540563N00617025EA0202502056
    +B1038174540557N00617006EA0203102062
    +B1038194540547N00617004EA0203302066
    +B1038214540541N00617016EA0203502069
    +B1038234540547N00617030EA0203902072
    +B1038254540561N00617025EA0204602077
    +B1038274540571N00617011EA0205402083
    +B1038294540579N00616999EA0206002090
    +B1038314540582N00616984EA0206102095
    +B1038334540576N00616968EA0206002099
    +B1038354540564N00616969EA0206002102
    +B1038374540557N00616983EA0206202102
    +B1038394540560N00617000EA0206502104
    +B1038414540570N00617009EA0207202108
    +B1038434540580N00617008EA0207602113
    +B1038454540588N00616997EA0207902116
    +B1038474540588N00616979EA0208202119
    +B1038494540579N00616968EA0208502122
    +B1038514540568N00616971EA0208702125
    +B1038534540565N00616988EA0209102128
    +B1038554540573N00617000EA0209302130
    +B1038574540586N00616997EA0209602133
    +B1038594540595N00616984EA0210002136
    +B1039014540596N00616966EA0210402141
    +B1039034540590N00616953EA0210702144
    +B1039054540581N00616948EA0211102148
    +B1039074540572N00616956EA0211402150
    +B1039094540568N00616971EA0211702153
    +B1039114540575N00616980EA0212002156
    +B1039134540588N00616975EA0212202159
    +B1039154540594N00616955EA0212502162
    +B1039174540592N00616937EA0212802166
    +B1039194540587N00616923EA0213102170
    +B1039214540579N00616911EA0213302172
    +B1039234540570N00616900EA0213502174
    +B1039254540560N00616890EA0213602176
    +B1039274540552N00616881EA0213502176
    +B1039294540541N00616872EA0213502176
    +B1039314540532N00616863EA0213602176
    +B1039334540524N00616853EA0213402176
    +B1039354540517N00616844EA0213102174
    +B1039374540508N00616833EA0212602170
    +B1039394540500N00616822EA0212102165
    +B1039414540489N00616812EA0211802161
    +B1039434540479N00616803EA0211402157
    +B1039454540469N00616793EA0210902153
    +B1039474540458N00616785EA0210502148
    +B1039494540446N00616778EA0210202144
    +B1039514540435N00616770EA0210002141
    +B1039534540425N00616762EA0209602138
    +B1039554540414N00616751EA0209202133
    +B1039574540402N00616739EA0209002130
    +B1039594540390N00616731EA0208802128
    +B1040014540379N00616723EA0208302124
    +B1040034540368N00616712EA0207802119
    +B1040054540356N00616699EA0207502116
    +B1040074540346N00616686EA0207102112
    +B1040094540338N00616672EA0206602108
    +B1040114540329N00616659EA0206202103
    +B1040134540318N00616647EA0205802099
    +B1040154540307N00616636EA0205602096
    +B1040174540296N00616626EA0205302093
    +B1040194540286N00616616EA0205002090
    +B1040214540275N00616606EA0204502085
    +B1040234540264N00616594EA0204202082
    +B1040254540254N00616583EA0203902079
    +B1040274540245N00616571EA0203502075
    +B1040294540235N00616560EA0203102071
    +B1040314540224N00616549EA0202702067
    +B1040334540215N00616537EA0202402063
    +B1040354540205N00616525EA0202002059
    +B1040374540195N00616514EA0201702056
    +B1040394540185N00616503EA0201402052
    +B1040414540175N00616493EA0201102049
    +B1040434540165N00616483EA0200702046
    +B1040454540155N00616474EA0200402043
    +B1040474540145N00616464EA0200002039
    +B1040494540135N00616452EA0199702036
    +B1040514540126N00616440EA0199502033
    +B1040534540116N00616428EA0199202030
    +B1040554540106N00616417EA0198902027
    +B1040574540096N00616405EA0198602024
    +B1040594540086N00616395EA0198402022
    +B1041014540076N00616384EA0198002019
    +B1041034540065N00616373EA0197702015
    +B1041054540055N00616363EA0197402012
    +B1041074540044N00616352EA0197002009
    +B1041094540034N00616342EA0196602006
    +B1041114540023N00616332EA0196402002
    +B1041134540012N00616323EA0196102000
    +B1041154540002N00616313EA0195701996
    +B1041174539992N00616302EA0195401992
    +B1041194539982N00616290EA0195101989
    +B1041214539972N00616279EA0194901987
    +B1041234539962N00616268EA0194601983
    +B1041254539952N00616258EA0194301980
    +B1041274539942N00616248EA0194001977
    +B1041294539932N00616237EA0193801975
    +B1041314539922N00616226EA0193501972
    +B1041334539911N00616216EA0193201969
    +B1041354539901N00616206EA0192901966
    +B1041374539890N00616197EA0192601963
    +B1041394539880N00616187EA0192301960
    +B1041414539869N00616178EA0192001957
    +B1041434539859N00616168EA0191801954
    +B1041454539849N00616158EA0191501952
    +B1041474539839N00616149EA0191101948
    +B1041494539828N00616140EA0190901946
    +B1041514539818N00616131EA0190501943
    +B1041534539807N00616123EA0190201940
    +B1041554539797N00616115EA0189901937
    +B1041574539786N00616107EA0189601934
    +B1041594539776N00616098EA0189401931
    +B1042014539765N00616090EA0189101928
    +B1042034539755N00616082EA0188801925
    +B1042054539744N00616073EA0188501922
    +B1042074539734N00616065EA0188201919
    +B1042094539723N00616057EA0187901917
    +B1042114539712N00616049EA0187701914
    +B1042134539701N00616042EA0187401911
    +B1042154539691N00616035EA0187101908
    +B1042174539680N00616027EA0186901906
    +B1042194539669N00616019EA0186601903
    +B1042214539658N00616012EA0186401900
    +B1042234539648N00616004EA0186101898
    +B1042254539636N00615998EA0185701894
    +B1042274539625N00615991EA0185401892
    +B1042294539615N00615983EA0185201889
    +B1042314539604N00615975EA0184901885
    +B1042334539593N00615967EA0184601882
    +B1042354539583N00615959EA0184401880
    +B1042374539572N00615952EA0184301879
    +B1042394539562N00615945EA0183901876
    +B1042414539551N00615939EA0183601873
    +B1042434539539N00615931EA0183401870
    +B1042454539527N00615923EA0183301869
    +B1042474539516N00615917EA0183301869
    +B1042494539505N00615910EA0183301868
    +B1042514539494N00615903EA0183401869
    +B1042534539483N00615896EA0183501870
    +B1042554539473N00615890EA0183601871
    +B1042574539461N00615884EA0183701872
    +B1042594539450N00615879EA0183701873
    +B1043014539439N00615874EA0183801873
    +B1043034539429N00615870EA0183501872
    +B1043054539418N00615868EA0183201870
    +B1043074539405N00615863EA0183101868
    +B1043094539394N00615859EA0183201867
    +B1043114539385N00615857EA0183601869
    +B1043134539377N00615854EA0184101873
    +B1043154539369N00615851EA0184401877
    +B1043174539363N00615852EA0184301879
    +B1043194539356N00615857EA0184301879
    +B1043214539346N00615860EA0184401880
    +B1043234539335N00615859EA0184601882
    +B1043254539326N00615860EA0184801884
    +B1043274539317N00615860EA0184901886
    +B1043294539308N00615859EA0185001887
    +B1043314539299N00615858EA0185001888
    +B1043334539289N00615861EA0185001888
    +B1043354539279N00615862EA0184901888
    +B1043374539267N00615863EA0184801887
    +B1043394539256N00615864EA0184801886
    +B1043414539244N00615863EA0184801886
    +B1043434539234N00615860EA0184701886
    +B1043454539225N00615858EA0184401885
    +B1043474539213N00615855EA0184201882
    +B1043494539202N00615850EA0184101881
    +B1043514539191N00615847EA0183601878
    +B1043534539181N00615845EA0183301874
    +B1043554539170N00615842EA0183201873
    +B1043574539161N00615841EA0182901870
    +B1043594539153N00615840EA0182501867
    +B1044014539143N00615838EA0182201862
    +B1044034539132N00615837EA0182001859
    +B1044054539121N00615837EA0181901857
    +B1044074539110N00615835EA0182101857
    +B1044094539100N00615831EA0182401858
    +B1044114539091N00615827EA0182701861
    +B1044134539081N00615824EA0183001863
    +B1044154539072N00615819EA0183301866
    +B1044174539062N00615814EA0183501869
    +B1044194539052N00615810EA0183501870
    +B1044214539042N00615807EA0183601872
    +B1044234539032N00615802EA0183601872
    +B1044254539023N00615796EA0183401871
    +B1044274539011N00615788EA0183401871
    +B1044294539000N00615781EA0183501871
    +B1044314538990N00615777EA0183401871
    +B1044334538980N00615772EA0183101869
    +B1044354538970N00615766EA0182701866
    +B1044374538961N00615757EA0182501863
    +B1044394538952N00615747EA0182701861
    +B1044414538947N00615736EA0183001861
    +B1044434538941N00615725EA0183101863
    +B1044454538933N00615714EA0183401865
    +B1044474538927N00615703EA0183801869
    +B1044494538921N00615692EA0183901870
    +B1044514538913N00615680EA0183801870
    +B1044534538906N00615672EA0183801871
    +B1044554538898N00615668EA0183701871
    +B1044574538890N00615663EA0183301870
    +B1044594538882N00615654EA0183001867
    +B1045014538872N00615645EA0182801865
    +B1045034538863N00615636EA0182601863
    +B1045054538854N00615627EA0182301860
    +B1045074538845N00615616EA0182001857
    +B1045094538836N00615606EA0181801855
    +B1045114538827N00615597EA0181601853
    +B1045134538818N00615588EA0181101849
    +B1045154538808N00615578EA0180701845
    +B1045174538797N00615569EA0180201841
    +B1045194538787N00615560EA0179701836
    +B1045214538776N00615551EA0179201830
    +B1045234538766N00615541EA0178701826
    +B1045254538755N00615533EA0178401822
    +B1045274538744N00615526EA0178101818
    +B1045294538732N00615519EA0177801815
    +B1045314538721N00615511EA0177701813
    +B1045334538710N00615505EA0177401811
    +B1045354538699N00615499EA0177101808
    +B1045374538688N00615492EA0176901805
    +B1045394538676N00615487EA0176701803
    +B1045414538665N00615481EA0176401800
    +B1045434538653N00615474EA0176101797
    +B1045454538642N00615469EA0175801794
    +B1045474538630N00615465EA0175401791
    +B1045494538617N00615461EA0175101787
    +B1045514538606N00615456EA0174901785
    +B1045534538594N00615451EA0174601782
    +B1045554538582N00615445EA0174401780
    +B1045574538571N00615439EA0174201778
    +B1045594538560N00615431EA0174001776
    +B1046014538548N00615426EA0173901774
    +B1046034538536N00615421EA0173901774
    +B1046054538525N00615414EA0173801773
    +B1046074538513N00615409EA0173501771
    +B1046094538500N00615405EA0173401769
    +B1046114538488N00615400EA0173301768
    +B1046134538476N00615397EA0173101767
    +B1046154538464N00615393EA0173001765
    +B1046174538452N00615389EA0173001765
    +B1046194538440N00615386EA0172801763
    +B1046214538428N00615383EA0172701762
    +B1046234538417N00615380EA0172601761
    +B1046254538405N00615376EA0172401760
    +B1046274538393N00615374EA0172301758
    +B1046294538381N00615374EA0172101757
    +B1046314538369N00615373EA0171801754
    +B1046334538356N00615370EA0171601752
    +B1046354538344N00615367EA0171601751
    +B1046374538333N00615366EA0171401749
    +B1046394538321N00615365EA0171201747
    +B1046414538308N00615366EA0171201745
    +B1046434538295N00615365EA0171501745
    +B1046454538284N00615364EA0171701746
    +B1046474538273N00615362EA0172001749
    +B1046494538261N00615358EA0172501753
    +B1046514538250N00615354EA0173101758
    +B1046534538241N00615351EA0173401763
    +B1046554538236N00615360EA0173401766
    +B1046574538237N00615373EA0173401769
    +B1046594538248N00615376EA0173701771
    +B1047014538257N00615367EA0174301774
    +B1047034538264N00615352EA0174801778
    +B1047054538270N00615339EA0175101783
    +B1047074538279N00615333EA0175201785
    +B1047094538286N00615340EA0175201786
    +B1047114538281N00615351EA0175201787
    +B1047134538268N00615352EA0175501788
    +B1047154538257N00615343EA0176101792
    +B1047174538248N00615334EA0176601798
    +B1047194538242N00615322EA0176801801
    +B1047214538237N00615304EA0177201804
    +B1047234538230N00615290EA0177401808
    +B1047254538221N00615293EA0177501809
    +B1047274538218N00615306EA0177701811
    +B1047294538224N00615315EA0178201815
    +B1047314538234N00615309EA0178501819
    +B1047334538240N00615293EA0178701821
    +B1047354538238N00615272EA0179001824
    +B1047374538228N00615260EA0179001827
    +B1047394538215N00615267EA0179201828
    +B1047414538214N00615282EA0179401830
    +B1047434538224N00615290EA0179801832
    +B1047454538234N00615287EA0180201837
    +B1047474538242N00615280EA0180601840
    +B1047494538250N00615273EA0180801844
    +B1047514538257N00615268EA0180801846
    +B1047534538266N00615266EA0180801846
    +B1047554538274N00615274EA0180801846
    +B1047574538273N00615288EA0181001846
    +B1047594538265N00615298EA0181301848
    +B1048014538254N00615301EA0181601851
    +B1048034538243N00615302EA0182201855
    +B1048054538233N00615301EA0182601859
    +B1048074538224N00615302EA0182601863
    +B1048094538214N00615303EA0182401864
    +B1048114538204N00615301EA0182301863
    +B1048134538194N00615298EA0182201862
    +B1048154538185N00615294EA0182201861
    +B1048174538177N00615290EA0182301861
    +B1048194538168N00615285EA0182501862
    +B1048214538158N00615281EA0182801864
    +B1048234538148N00615278EA0183001866
    +B1048254538137N00615274EA0183101867
    +B1048274538125N00615271EA0182901867
    +B1048294538113N00615267EA0182701866
    +B1048314538102N00615262EA0182601864
    +B1048334538092N00615257EA0182701864
    +B1048354538082N00615251EA0182901865
    +B1048374538072N00615246EA0183201867
    +B1048394538061N00615240EA0183601870
    +B1048414538051N00615234EA0183901874
    +B1048434538041N00615232EA0183701875
    +B1048454538030N00615233EA0183201875
    +B1048474538017N00615234EA0182901872
    +B1048494538005N00615235EA0182801870
    +B1048514537995N00615237EA0182501867
    +B1048534537984N00615238EA0182101863
    +B1048554537972N00615238EA0181801860
    +B1048574537961N00615238EA0181601857
    +B1048594537951N00615239EA0181401854
    +B1049014537938N00615240EA0181401852
    +B1049034537925N00615241EA0181401851
    +B1049054537915N00615245EA0181301850
    +B1049074537903N00615248EA0181301849
    +B1049094537892N00615251EA0181401849
    +B1049114537881N00615253EA0181701851
    +B1049134537872N00615252EA0182401854
    +B1049154537865N00615251EA0182901859
    +B1049174537857N00615252EA0183201863
    +B1049194537848N00615254EA0183401866
    +B1049214537839N00615248EA0183601868
    +B1049234537830N00615239EA0183901871
    +B1049254537821N00615229EA0184001873
    +B1049274537813N00615218EA0184201874
    +B1049294537805N00615205EA0184301876
    +B1049314537799N00615193EA0184101876
    +B1049334537792N00615184EA0183601876
    +B1049354537783N00615177EA0183201873
    +B1049374537771N00615171EA0183101871
    +B1049394537760N00615165EA0183101870
    +B1049414537751N00615159EA0182801868
    +B1049434537742N00615155EA0182801865
    +B1049454537732N00615149EA0183301864
    +B1049474537725N00615142EA0183401864
    +B1049494537717N00615134EA0183201864
    +B1049514537707N00615129EA0183301863
    +B1049534537697N00615130EA0183901865
    +B1049554537688N00615130EA0184601872
    +B1049574537680N00615129EA0185101878
    +B1049594537672N00615129EA0185701884
    +B1050014537665N00615136EA0186001889
    +B1050034537663N00615153EA0186401894
    +B1050054537673N00615163EA0186401897
    +B1050074537685N00615154EA0186301899
    +B1050094537690N00615138EA0186401899
    +B1050114537686N00615123EA0186601901
    +B1050134537678N00615113EA0187101904
    +B1050154537670N00615110EA0187801909
    +B1050174537662N00615108EA0188101914
    +B1050194537653N00615108EA0188201917
    +B1050214537644N00615118EA0188301918
    +B1050234537643N00615135EA0188601920
    +B1050254537653N00615142EA0188901922
    +B1050274537663N00615131EA0188901925
    +B1050294537666N00615110EA0189201927
    +B1050314537661N00615091EA0190001930
    +B1050334537654N00615084EA0190601936
    +B1050354537646N00615081EA0190901942
    +B1050374537637N00615083EA0191101945
    +B1050394537631N00615097EA0191101946
    +B1050414537634N00615117EA0191301947
    +B1050434537647N00615118EA0191701950
    +B1050454537654N00615101EA0191901952
    +B1050474537652N00615080EA0192501955
    +B1050494537647N00615064EA0193101962
    +B1050514537643N00615054EA0193601968
    +B1050534537636N00615046EA0193801972
    +B1050554537625N00615048EA0193601974
    +B1050574537618N00615063EA0193401974
    +B1050594537623N00615078EA0193301973
    +B1051014537636N00615080EA0193501973
    +B1051034537645N00615070EA0193901976
    +B1051054537650N00615056EA0194201979
    +B1051074537652N00615040EA0194701982
    +B1051094537651N00615024EA0195201987
    +B1051114537651N00615009EA0195201990
    +B1051134537648N00614996EA0194901992
    +B1051154537641N00614983EA0194701991
    +B1051174537634N00614968EA0194501989
    +B1051194537628N00614953EA0194101986
    +B1051214537621N00614937EA0193501982
    +B1051234537614N00614919EA0193201978
    +B1051254537608N00614902EA0193001975
    +B1051274537601N00614887EA0192601971
    +B1051294537595N00614871EA0192301967
    +B1051314537588N00614857EA0192701964
    +B1051334537580N00614846EA0193101968
    +B1051354537575N00614835EA0193001969
    +B1051374537569N00614821EA0192701968
    +B1051394537562N00614803EA0193001969
    +B1051414537558N00614786EA0193401972
    +B1051434537556N00614772EA0193601974
    +B1051454537551N00614759EA0193501975
    +B1051474537545N00614745EA0193801976
    +B1051494537540N00614732EA0194001978
    +B1051514537534N00614720EA0194201980
    +B1051534537528N00614709EA0194401982
    +B1051554537523N00614697EA0194401984
    +B1051574537517N00614687EA0194201983
    +B1051594537510N00614677EA0193701982
    +B1052014537502N00614664EA0193501979
    +B1052034537494N00614650EA0193401978
    +B1052054537486N00614638EA0192901975
    +B1052074537477N00614626EA0192601970
    +B1052094537468N00614612EA0192401968
    +B1052114537461N00614598EA0192401966
    +B1052134537456N00614584EA0192301965
    +B1052154537451N00614570EA0192201963
    +B1052174537446N00614558EA0192501962
    +B1052194537438N00614548EA0192901963
    +B1052214537431N00614538EA0193301967
    +B1052234537427N00614528EA0193401968
    +B1052254537421N00614519EA0193401968
    +B1052274537416N00614510EA0193501969
    +B1052294537410N00614500EA0193801971
    +B1052314537403N00614489EA0194401974
    +B1052334537397N00614479EA0195101979
    +B1052354537391N00614471EA0195501984
    +B1052374537383N00614466EA0195601987
    +B1052394537374N00614474EA0195701990
    +B1052414537372N00614487EA0195901992
    +B1052434537376N00614498EA0196101994
    +B1052454537387N00614505EA0196501996
    +B1052474537403N00614500EA0197302001
    +B1052494537413N00614490EA0198202007
    +B1052514537417N00614478EA0198602014
    +B1052534537414N00614463EA0198802018
    +B1052554537402N00614457EA0199002023
    +B1052574537394N00614465EA0199202026
    +B1052594537392N00614480EA0199502029
    +B1053014537392N00614492EA0200102035
    +B1053034537390N00614500EA0200302039
    +B1053054537392N00614512EA0200102042
    +B1053074537404N00614524EA0200302044
    +B1053094537418N00614523EA0200602047
    +B1053114537428N00614513EA0200902049
    +B1053134537436N00614500EA0201302052
    +B1053154537440N00614484EA0201702055
    +B1053174537439N00614470EA0202202060
    +B1053194537433N00614460EA0202602063
    +B1053214537425N00614453EA0203102069
    +B1053234537418N00614448EA0203302073
    +B1053254537411N00614452EA0203102075
    +B1053274537408N00614467EA0203102074
    +B1053294537416N00614479EA0203302074
    +B1053314537427N00614485EA0203702077
    +B1053334537440N00614480EA0203902079
    +B1053354537450N00614464EA0204102080
    +B1053374537452N00614446EA0204702083
    +B1053394537449N00614432EA0205202088
    +B1053414537442N00614421EA0205502092
    +B1053434537434N00614417EA0205702095
    +B1053454537427N00614426EA0205502096
    +B1053474537427N00614442EA0205502096
    +B1053494537436N00614451EA0205702096
    +B1053514537450N00614454EA0206302098
    +B1053534537461N00614449EA0206602102
    +B1053554537469N00614435EA0207002105
    +B1053574537471N00614416EA0207302108
    +B1053594537463N00614402EA0207602111
    +B1054014537456N00614392EA0208102117
    +B1054034537456N00614376EA0208002120
    +B1054054537462N00614358EA0208102121
    +B1054074537474N00614345EA0208402123
    +B1054094537487N00614343EA0208902126
    +B1054114537497N00614348EA0209602131
    +B1054134537503N00614358EA0210002135
    +B1054154537507N00614369EA0210302139
    +B1054174537509N00614379EA0210502143
    +B1054194537506N00614388EA0210402145
    +B1054214537494N00614394EA0210602146
    +B1054234537485N00614387EA0211002148
    +B1054254537478N00614379EA0211002150
    +B1054274537471N00614373EA0211102149
    +B1054294537464N00614366EA0211102150
    +B1054314537456N00614357EA0211102150
    +B1054334537449N00614350EA0211002149
    +B1054354537439N00614342EA0210802147
    +B1054374537430N00614332EA0210702146
    +B1054394537421N00614321EA0210702146
    +B1054414537413N00614311EA0210602145
    +B1054434537406N00614298EA0210502143
    +B1054454537399N00614286EA0210402142
    +B1054474537390N00614278EA0210102140
    +B1054494537382N00614269EA0209802137
    +B1054514537372N00614259EA0209702135
    +B1054534537362N00614248EA0209802134
    +B1054554537353N00614238EA0209602133
    +B1054574537345N00614230EA0209102131
    +B1054594537336N00614219EA0208602127
    +B1055014537326N00614207EA0208202123
    +B1055034537317N00614195EA0207902119
    +B1055054537307N00614183EA0207502115
    +B1055074537296N00614172EA0207202112
    +B1055094537286N00614161EA0206902109
    +B1055114537275N00614152EA0206502106
    +B1055134537264N00614143EA0206202102
    +B1055154537253N00614132EA0206002099
    +B1055174537242N00614121EA0205702097
    +B1055194537232N00614111EA0205402093
    +B1055214537222N00614100EA0205202091
    +B1055234537212N00614088EA0205102089
    +B1055254537204N00614076EA0204902088
    +B1055274537195N00614063EA0204802086
    +B1055294537185N00614049EA0204702085
    +B1055314537176N00614038EA0204602084
    +B1055334537166N00614027EA0204502083
    +B1055354537156N00614016EA0204302081
    +B1055374537146N00614004EA0204402081
    +B1055394537135N00613992EA0204602082
    +B1055414537127N00613982EA0204602083
    +B1055434537118N00613972EA0204402082
    +B1055454537108N00613962EA0204102080
    +B1055474537098N00613951EA0203802077
    +B1055494537088N00613940EA0203502074
    +B1055514537076N00613930EA0203202071
    +B1055534537066N00613922EA0203002069
    +B1055554537055N00613914EA0202702066
    +B1055574537046N00613904EA0202402063
    +B1055594537036N00613893EA0202502062
    +B1056014537028N00613882EA0203202062
    +B1056034537020N00613871EA0203802064
    +B1056054537013N00613860EA0204302069
    +B1056074537008N00613851EA0204502073
    +B1056094537001N00613843EA0204402074
    +B1056114536992N00613834EA0204402075
    +B1056134536985N00613823EA0204402076
    +B1056154536978N00613809EA0204302076
    +B1056174536970N00613796EA0204302076
    +B1056194536963N00613783EA0204402077
    +B1056214536957N00613770EA0204402077
    +B1056234536952N00613757EA0204302077
    +B1056254536946N00613743EA0204402078
    +B1056274536940N00613728EA0204602079
    +B1056294536934N00613715EA0204702081
    +B1056314536928N00613704EA0204602081
    +B1056334536921N00613693EA0204202080
    +B1056354536915N00613679EA0204102078
    +B1056374536910N00613666EA0204202078
    +B1056394536905N00613654EA0204202078
    +B1056414536899N00613644EA0204002076
    +B1056434536892N00613633EA0204102076
    +B1056454536884N00613624EA0204302077
    +B1056474536876N00613615EA0204602079
    +B1056494536869N00613606EA0205002083
    +B1056514536862N00613596EA0205302086
    +B1056534536855N00613586EA0205702090
    +B1056554536849N00613575EA0205902093
    +B1056574536842N00613563EA0206102095
    +B1056594536835N00613551EA0206402097
    +B1057014536829N00613541EA0206602101
    +B1057034536823N00613533EA0206602103
    +B1057054536815N00613522EA0206202103
    +B1057074536807N00613511EA0206002101
    +B1057094536799N00613499EA0205702098
    +B1057114536791N00613486EA0205402095
    +B1057134536783N00613473EA0205202093
    +B1057154536775N00613460EA0204902090
    +B1057174536768N00613447EA0204502086
    +B1057194536760N00613432EA0204202082
    +B1057214536753N00613416EA0204202080
    +B1057234536747N00613401EA0204102080
    +B1057254536740N00613387EA0204102079
    +B1057274536732N00613374EA0204202079
    +B1057294536725N00613361EA0204302080
    +B1057314536719N00613345EA0204702082
    +B1057334536715N00613329EA0205202086
    +B1057354536710N00613316EA0205302088
    +B1057374536704N00613302EA0205102090
    +B1057394536698N00613287EA0204702089
    +B1057414536692N00613275EA0204602087
    +B1057434536685N00613265EA0204402085
    +B1057454536679N00613253EA0204002082
    +B1057474536672N00613239EA0203902079
    +B1057494536666N00613224EA0203902077
    +B1057514536661N00613208EA0203902077
    +B1057534536654N00613192EA0204202078
    +B1057554536647N00613175EA0204802080
    +B1057574536641N00613160EA0205502085
    +B1057594536633N00613147EA0206302091
    +B1058014536623N00613141EA0207002098
    +B1058034536613N00613151EA0207402104
    +B1058054536610N00613168EA0207202108
    +B1058074536620N00613179EA0206602110
    +B1058094536634N00613172EA0206502112
    +B1058114536641N00613150EA0206802113
    +B1058134536640N00613124EA0207702117
    +B1058154536639N00613105EA0208802125
    +B1058174536637N00613091EA0209502132
    +B1058194536632N00613081EA0209902137
    +B1058214536621N00613084EA0210402141
    +B1058234536615N00613101EA0211202147
    +B1058254536617N00613111EA0212002155
    +B1058274536627N00613112EA0212502160
    +B1058294536637N00613103EA0213102167
    +B1058314536639N00613085EA0213602172
    +B1058334536631N00613070EA0214502179
    +B1058354536622N00613070EA0215102186
    +B1058374536614N00613077EA0215802193
    +B1058394536609N00613087EA0216302200
    +B1058414536608N00613099EA0216602204
    +B1058434536613N00613114EA0216502208
    +B1058454536624N00613122EA0216602209
    +B1058474536638N00613118EA0216602209
    +B1058494536648N00613102EA0216802209
    +B1058514536653N00613079EA0217302213
    +B1058534536657N00613059EA0217902217
    +B1058554536659N00613041EA0218402221
    +B1058574536661N00613020EA0219302227
    +B1058594536661N00613004EA0220102234
    +B1059014536659N00612994EA0220402239
    +B1059034536654N00612984EA0220802243
    +B1059054536645N00612978EA0221202248
    +B1059074536635N00612986EA0221602252
    +B1059094536632N00613001EA0222202257
    +B1059114536639N00613009EA0222602262
    +B1059134536652N00612995EA0223102267
    +B1059154536655N00612973EA0224002273
    +B1059174536648N00612958EA0224502279
    +B1059194536638N00612953EA0225102286
    +B1059214536628N00612953EA0225402291
    +B1059234536623N00612964EA0225802295
    +B1059254536627N00612976EA0226302300
    +B1059274536638N00612975EA0226702304
    +B1059294536647N00612957EA0227302309
    +B1059314536647N00612935EA0227702313
    +B1059334536641N00612917EA0228202318
    +B1059354536631N00612908EA0228702323
    +B1059374536622N00612908EA0229102326
    +B1059394536619N00612919EA0229202330
    +B1059414536630N00612927EA0229402331
    +B1059434536643N00612922EA0230002336
    +B1059454536650N00612906EA0230302340
    +B1059474536654N00612887EA0230702343
    +B1059494536655N00612869EA0230802346
    +B1059514536655N00612851EA0230902348
    +B1059534536652N00612833EA0231102350
    +B1059554536649N00612817EA0231102351
    +B1059574536645N00612804EA0230802351
    +B1059594536639N00612792EA0230602350
    +B1100014536635N00612777EA0230302347
    +B1100034536631N00612762EA0230002344
    +B1100054536626N00612747EA0229502341
    +B1100074536619N00612732EA0229302338
    +B1100094536614N00612718EA0228702334
    +B1100114536607N00612704EA0228102328
    +B1100134536600N00612689EA0227702323
    +B1100154536593N00612674EA0227202318
    +B1100174536587N00612659EA0226702312
    +B1100194536580N00612643EA0226302307
    +B1100214536575N00612625EA0226102303
    +B1100234536570N00612608EA0226102301
    +B1100254536563N00612595EA0226102301
    +B1100274536558N00612582EA0226002299
    +B1100294536551N00612569EA0226302300
    +B1100314536544N00612558EA0226802301
    +B1100334536538N00612550EA0227102305
    +B1100354536531N00612543EA0227302307
    +B1100374536522N00612535EA0227902311
    +B1100394536514N00612528EA0228502316
    +B1100414536506N00612521EA0228902321
    +B1100434536499N00612513EA0229302326
    +B1100454536491N00612507EA0229902332
    +B1100474536482N00612508EA0230202336
    +B1100494536476N00612520EA0230202340
    +B1100514536479N00612539EA0230402343
    +B1100534536491N00612543EA0230602346
    +B1100554536504N00612533EA0230902349
    +B1100574536512N00612518EA0231602354
    +B1100594536516N00612498EA0232202360
    +B1101014536516N00612481EA0232602365
    +B1101034536513N00612465EA0232902368
    +B1101054536504N00612455EA0232902371
    +B1101074536493N00612451EA0232902372
    +B1101094536483N00612448EA0232902372
    +B1101114536474N00612445EA0233002372
    +B1101134536466N00612438EA0233102373
    +B1101154536459N00612430EA0233402375
    +B1101174536453N00612422EA0233502377
    +B1101194536447N00612411EA0233402377
    +B1101214536442N00612397EA0233502377
    +B1101234536435N00612384EA0233902379
    +B1101254536429N00612372EA0234002382
    +B1101274536423N00612363EA0233502382
    +B1101294536415N00612352EA0233002380
    +B1101314536408N00612339EA0232902378
    +B1101334536401N00612326EA0232702376
    +B1101354536394N00612315EA0232402372
    +B1101374536387N00612303EA0232102369
    +B1101394536380N00612291EA0232202367
    +B1101414536373N00612278EA0232702366
    +B1101434536367N00612268EA0233202368
    +B1101454536360N00612260EA0233502372
    +B1101474536353N00612252EA0233602373
    +B1101494536345N00612241EA0233602374
    +B1101514536336N00612229EA0234102376
    +B1101534536329N00612217EA0234702381
    +B1101554536324N00612204EA0235002386
    +B1101574536319N00612191EA0235102388
    +B1101594536312N00612180EA0235302390
    +B1102014536305N00612169EA0235402392
    +B1102034536298N00612158EA0235202392
    +B1102054536289N00612146EA0234802391
    +B1102074536280N00612136EA0234602388
    +B1102094536271N00612124EA0234502387
    +B1102114536264N00612110EA0234302385
    +B1102134536256N00612094EA0234202384
    +B1102154536249N00612080EA0234302384
    +B1102174536242N00612067EA0234302384
    +B1102194536236N00612054EA0234102383
    +B1102214536228N00612042EA0233802381
    +B1102234536220N00612029EA0233702379
    +B1102254536211N00612015EA0233702378
    +B1102274536203N00612002EA0233702378
    +B1102294536196N00611989EA0233702378
    +B1102314536188N00611976EA0233702378
    +B1102334536181N00611963EA0233502377
    +B1102354536174N00611950EA0233302374
    +B1102374536166N00611935EA0233002372
    +B1102394536158N00611921EA0232902370
    +B1102414536149N00611907EA0232702368
    +B1102434536139N00611894EA0232502367
    +B1102454536129N00611882EA0232302365
    +B1102474536118N00611869EA0232102363
    +B1102494536108N00611855EA0232002361
    +B1102514536099N00611840EA0231702359
    +B1102534536090N00611824EA0231502356
    +B1102554536081N00611806EA0231302354
    +B1102574536073N00611790EA0231002351
    +B1102594536066N00611772EA0230602348
    +B1103014536058N00611754EA0230502345
    +B1103034536051N00611737EA0230402344
    +B1103054536045N00611720EA0230202342
    +B1103074536038N00611704EA0230002340
    +B1103094536030N00611686EA0229902338
    +B1103114536022N00611670EA0229902338
    +B1103134536014N00611654EA0229802337
    +B1103154536006N00611637EA0229602335
    +B1103174536000N00611619EA0229402334
    +B1103194535993N00611601EA0229202332
    +B1103214535986N00611582EA0228802329
    +B1103234535980N00611562EA0228702326
    +B1103254535974N00611541EA0228602325
    +B1103274535969N00611521EA0228502324
    +B1103294535964N00611501EA0228202322
    +B1103314535959N00611481EA0227902319
    +B1103334535952N00611460EA0227802317
    +B1103354535947N00611440EA0227602315
    +B1103374535941N00611421EA0227502313
    +B1103394535935N00611401EA0227402312
    +B1103414535929N00611382EA0227202311
    +B1103434535924N00611363EA0227102310
    +B1103454535918N00611344EA0227002309
    +B1103474535912N00611324EA0227102308
    +B1103494535905N00611305EA0227302309
    +B1103514535900N00611288EA0227302310
    +B1103534535894N00611272EA0227102309
    +B1103554535888N00611255EA0226902308
    +B1103574535882N00611238EA0226802306
    +B1103594535876N00611220EA0226702305
    +B1104014535870N00611202EA0226502303
    +B1104034535865N00611183EA0226302302
    +B1104054535860N00611165EA0226202300
    +B1104074535855N00611147EA0226002298
    +B1104094535851N00611129EA0225802297
    +B1104114535847N00611110EA0225602295
    +B1104134535842N00611093EA0225402293
    +B1104154535837N00611074EA0225202291
    +B1104174535832N00611056EA0224902288
    +B1104194535828N00611038EA0224602285
    +B1104214535824N00611020EA0224202281
    +B1104234535819N00611001EA0224002278
    +B1104254535815N00610982EA0223902277
    +B1104274535812N00610964EA0223602275
    +B1104294535809N00610947EA0223202272
    +B1104314535805N00610927EA0223002269
    +B1104334535802N00610909EA0222902267
    +B1104354535799N00610892EA0222602265
    +B1104374535794N00610874EA0222502262
    +B1104394535790N00610856EA0222502261
    +B1104414535787N00610840EA0222502261
    +B1104434535785N00610825EA0222202260
    +B1104454535781N00610810EA0221702257
    +B1104474535775N00610793EA0221502254
    +B1104494535769N00610775EA0221602252
    +B1104514535765N00610758EA0221702253
    +B1104534535762N00610743EA0221602253
    +B1104554535758N00610727EA0221702253
    +B1104574535756N00610711EA0221702253
    +B1104594535754N00610697EA0221602253
    +B1105014535750N00610680EA0221602253
    +B1105034535746N00610663EA0221702253
    +B1105054535741N00610648EA0221602254
    +B1105074535735N00610635EA0221602253
    +B1105094535729N00610620EA0221502252
    +B1105114535724N00610603EA0221502252
    +B1105134535718N00610588EA0221602253
    +B1105154535713N00610572EA0221602253
    +B1105174535707N00610554EA0221602253
    +B1105194535701N00610536EA0221802254
    +B1105214535694N00610518EA0222002256
    +B1105234535687N00610502EA0222002257
    +B1105254535680N00610485EA0222002257
    +B1105274535673N00610467EA0222102258
    +B1105294535667N00610450EA0222302259
    +B1105314535661N00610433EA0222502261
    +B1105334535654N00610416EA0222802263
    +B1105354535646N00610399EA0223302267
    +B1105374535638N00610385EA0223602271
    +B1105394535630N00610371EA0223802273
    +B1105414535622N00610358EA0223902275
    +B1105434535616N00610343EA0223802275
    +B1105454535611N00610327EA0223702275
    +B1105474535606N00610312EA0223602274
    +B1105494535601N00610295EA0223302271
    +B1105514535596N00610277EA0223202269
    +B1105534535591N00610259EA0223002268
    +B1105554535588N00610240EA0222902266
    +B1105574535585N00610221EA0222602264
    +B1105594535581N00610204EA0222302262
    +B1106014535577N00610187EA0222002258
    +B1106034535573N00610170EA0221502254
    +B1106054535568N00610153EA0221102250
    +B1106074535563N00610135EA0220602245
    +B1106094535559N00610117EA0220302241
    +B1106114535554N00610100EA0219902237
    +B1106134535550N00610082EA0219402232
    +B1106154535546N00610064EA0219102228
    +B1106174535543N00610046EA0218802225
    +B1106194535538N00610028EA0218502222
    +B1106214535533N00610011EA0218102218
    +B1106234535526N00609994EA0217902215
    +B1106254535519N00609978EA0217702213
    +B1106274535514N00609960EA0217502211
    +B1106294535507N00609943EA0217202208
    +B1106314535499N00609927EA0216802205
    +B1106334535493N00609907EA0216402201
    +B1106354535488N00609885EA0216202198
    +B1106374535483N00609863EA0216002196
    +B1106394535480N00609842EA0215802194
    +B1106414535477N00609821EA0215402191
    +B1106434535475N00609800EA0215202188
    +B1106454535472N00609780EA0214902185
    +B1106474535468N00609760EA0214302181
    +B1106494535464N00609739EA0214002177
    +B1106514535461N00609716EA0213902174
    +B1106534535457N00609695EA0213602172
    +B1106554535455N00609675EA0213002169
    +B1106574535455N00609653EA0212502163
    +B1106594535455N00609630EA0212202160
    +B1107014535457N00609608EA0211902156
    +B1107034535457N00609586EA0211502153
    +B1107054535456N00609563EA0211202149
    +B1107074535456N00609540EA0210802146
    +B1107094535457N00609517EA0210502142
    +B1107114535456N00609493EA0210102139
    +B1107134535456N00609469EA0210002136
    +B1107154535457N00609445EA0209902135
    +B1107174535457N00609423EA0209602133
    +B1107194535459N00609400EA0209202129
    +B1107214535462N00609376EA0208902126
    +B1107234535465N00609352EA0208802124
    +B1107254535468N00609330EA0208602122
    +B1107274535471N00609308EA0208402119
    +B1107294535474N00609285EA0208202117
    +B1107314535477N00609261EA0208102116
    +B1107334535478N00609238EA0207902115
    +B1107354535479N00609216EA0207702113
    +B1107374535480N00609193EA0207402110
    +B1107394535479N00609170EA0207202108
    +B1107414535479N00609148EA0207102107
    +B1107434535477N00609126EA0206802105
    +B1107454535475N00609104EA0206602102
    +B1107474535475N00609081EA0206402100
    +B1107494535477N00609061EA0206302098
    +B1107514535477N00609043EA0205802095
    +B1107534535477N00609023EA0205302091
    +B1107554535474N00609002EA0205002087
    +B1107574535472N00608982EA0204802085
    +B1107594535470N00608963EA0204502082
    +B1108014535468N00608942EA0204302079
    +B1108034535465N00608922EA0204002076
    +B1108054535463N00608902EA0203702073
    +B1108074535463N00608881EA0203502071
    +B1108094535461N00608861EA0203302069
    +B1108114535458N00608842EA0203102067
    +B1108134535457N00608823EA0202902064
    +B1108154535455N00608803EA0202702062
    +B1108174535453N00608783EA0202402060
    +B1108194535451N00608764EA0202102058
    +B1108214535449N00608744EA0201902055
    +B1108234535447N00608724EA0201702053
    +B1108254535445N00608705EA0201502051
    +B1108274535444N00608686EA0201202048
    +B1108294535442N00608665EA0200902045
    +B1108314535440N00608644EA0200802044
    +B1108334535438N00608626EA0200602042
    +B1108354535437N00608606EA0200302039
    +B1108374535436N00608585EA0200202037
    +B1108394535435N00608565EA0200102035
    +B1108414535434N00608546EA0199902034
    +B1108434535433N00608527EA0199602031
    +B1108454535433N00608507EA0199502030
    +B1108474535433N00608488EA0199402028
    +B1108494535433N00608469EA0199202026
    +B1108514535434N00608450EA0199002025
    +B1108534535434N00608431EA0198802024
    +B1108554535432N00608412EA0198602022
    +B1108574535429N00608393EA0198402020
    +B1108594535427N00608374EA0198202018
    +B1109014535424N00608356EA0197902015
    +B1109034535419N00608338EA0197602012
    +B1109054535413N00608320EA0197302009
    +B1109074535406N00608303EA0197102006
    +B1109094535401N00608284EA0196702002
    +B1109114535395N00608265EA0196502000
    +B1109134535389N00608247EA0196301998
    +B1109154535383N00608230EA0195801994
    +B1109174535378N00608211EA0195401990
    +B1109194535372N00608190EA0195201987
    +B1109214535367N00608170EA0195101984
    +B1109234535364N00608150EA0194801982
    +B1109254535360N00608129EA0194601979
    +B1109274535355N00608107EA0194401978
    +B1109294535351N00608088EA0194401977
    +B1109314535347N00608068EA0194101975
    +B1109334535345N00608048EA0194001973
    +B1109354535345N00608027EA0193801972
    +B1109374535346N00608007EA0193601970
    +B1109394535348N00607987EA0193201967
    +B1109414535349N00607966EA0192901963
    +B1109434535352N00607945EA0192601961
    +B1109454535354N00607925EA0192501959
    +B1109474535356N00607906EA0192301957
    +B1109494535357N00607886EA0192101955
    +B1109514535358N00607867EA0191801952
    +B1109534535359N00607847EA0191401949
    +B1109554535361N00607827EA0191101945
    +B1109574535362N00607807EA0190701941
    +B1109594535362N00607787EA0190301938
    +B1110014535364N00607767EA0190001934
    +B1110034535364N00607748EA0189701932
    +B1110054535365N00607728EA0189501929
    +B1110074535365N00607709EA0189201926
    +B1110094535365N00607689EA0189001924
    +B1110114535364N00607669EA0188901922
    +B1110134535365N00607651EA0188601920
    +B1110154535366N00607633EA0188201916
    +B1110174535367N00607614EA0188001914
    +B1110194535368N00607595EA0187801912
    +B1110214535369N00607578EA0187401909
    +B1110234535370N00607559EA0187101905
    +B1110254535372N00607539EA0186901902
    +B1110274535372N00607521EA0186601900
    +B1110294535372N00607503EA0186301897
    +B1110314535372N00607485EA0186001894
    +B1110334535372N00607467EA0185701891
    +B1110354535373N00607448EA0185401888
    +B1110374535373N00607430EA0185001885
    +B1110394535372N00607412EA0184601881
    +B1110414535370N00607393EA0184101876
    +B1110434535368N00607374EA0183701872
    +B1110454535367N00607355EA0183401868
    +B1110474535366N00607338EA0183001865
    +B1110494535365N00607320EA0182701861
    +B1110514535365N00607301EA0182401857
    +B1110534535367N00607283EA0182201855
    +B1110554535368N00607266EA0182001853
    +B1110574535370N00607249EA0181701850
    +B1110594535372N00607232EA0181401847
    +B1111014535375N00607215EA0181201845
    +B1111034535377N00607198EA0180901842
    +B1111054535379N00607181EA0180601840
    +B1111074535382N00607164EA0180301837
    +B1111094535386N00607147EA0180001834
    +B1111114535388N00607130EA0179701831
    +B1111134535389N00607113EA0179301827
    +B1111154535391N00607095EA0179001823
    +B1111174535395N00607078EA0178801821
    +B1111194535399N00607063EA0178701819
    +B1111214535402N00607050EA0178401816
    +B1111234535405N00607035EA0178001813
    +B1111254535409N00607019EA0177601809
    +B1111274535410N00607003EA0177201806
    +B1111294535412N00606986EA0176901802
    +B1111314535416N00606971EA0176401798
    +B1111334535420N00606955EA0176001793
    +B1111354535423N00606937EA0175601790
    +B1111374535427N00606920EA0175301786
    +B1111394535432N00606904EA0174901782
    +B1111414535438N00606888EA0174601779
    +B1111434535444N00606873EA0174301776
    +B1111454535450N00606857EA0173901772
    +B1111474535455N00606840EA0173601769
    +B1111494535460N00606824EA0173401766
    +B1111514535466N00606809EA0173101764
    +B1111534535470N00606793EA0172801761
    +B1111554535475N00606776EA0172501758
    +B1111574535480N00606761EA0172101755
    +B1111594535486N00606747EA0171601750
    +B1112014535494N00606733EA0171101746
    +B1112034535503N00606720EA0170701741
    +B1112054535513N00606710EA0170301738
    +B1112074535524N00606705EA0169901733
    +B1112094535535N00606699EA0169401728
    +B1112114535547N00606689EA0169101724
    +B1112134535557N00606677EA0168801721
    +B1112154535567N00606666EA0168501717
    +B1112174535578N00606657EA0168301715
    +B1112194535588N00606652EA0167901711
    +B1112214535599N00606656EA0167401706
    +B1112234535608N00606665EA0167001702
    +B1112254535615N00606675EA0166501698
    +B1112274535623N00606687EA0166101693
    +B1112294535631N00606700EA0166201689
    +B1112314535639N00606711EA0166401688
    +B1112334535645N00606721EA0166301688
    +B1112354535653N00606728EA0166001686
    +B1112374535662N00606736EA0165501683
    +B1112394535672N00606745EA0165201680
    +B1112414535682N00606755EA0165201679
    +B1112434535691N00606765EA0165301679
    +B1112454535700N00606773EA0165401679
    +B1112474535710N00606783EA0165601680
    +B1112494535719N00606794EA0166001683
    +B1112514535728N00606805EA0166801687
    +B1112534535737N00606814EA0167401694
    +B1112554535745N00606822EA0168101700
    +B1112574535753N00606827EA0168801707
    +B1112594535760N00606825EA0168901713
    +B1113014535760N00606810EA0169001718
    +B1113034535750N00606799EA0169401723
    +B1113054535736N00606807EA0169901727
    +B1113074535732N00606826EA0170801735
    +B1113094535733N00606843EA0171601744
    +B1113114535738N00606857EA0172201751
    +B1113134535746N00606869EA0172901757
    +B1113154535757N00606877EA0173601764
    +B1113174535767N00606872EA0174301771
    +B1113194535768N00606861EA0174801777
    +B1113214535761N00606850EA0175501784
    +B1113234535751N00606846EA0176101791
    +B1113254535741N00606859EA0176501797
    +B1113274535737N00606882EA0177301803
    +B1113294535744N00606899EA0178101810
    +B1113314535755N00606906EA0178701816
    +B1113334535766N00606904EA0179201823
    +B1113354535772N00606893EA0179501828
    +B1113374535771N00606879EA0180001833
    +B1113394535763N00606872EA0180401838
    +B1113414535753N00606870EA0181001843
    +B1113434535742N00606875EA0181501847
    +B1113454535736N00606894EA0182101852
    +B1113474535738N00606914EA0183001858
    +B1113494535748N00606926EA0183801866
    +B1113514535756N00606933EA0184301874
    +B1113534535760N00606946EA0184401879
    +B1113554535753N00606962EA0184501884
    +B1113574535741N00606966EA0184501886
    +B1113594535731N00606956EA0184301887
    +B1114014535722N00606943EA0184201885
    +B1114034535712N00606935EA0184001883
    +B1114054535702N00606926EA0183701879
    +B1114074535699N00606906EA0183301875
    +B1114094535706N00606887EA0183601872
    +B1114114535717N00606875EA0184401873
    +B1114134535725N00606866EA0185201876
    +B1114154535730N00606856EA0185601880
    +B1114174535735N00606846EA0186001885
    +B1114194535743N00606844EA0186601890
    +B1114214535751N00606849EA0187201897
    +B1114234535754N00606859EA0187301901
    +B1114254535752N00606878EA0187401904
    +B1114274535742N00606894EA0188101909
    +B1114294535730N00606895EA0188701914
    +B1114314535723N00606881EA0189301920
    +B1114334535726N00606865EA0189701926
    +B1114354535737N00606860EA0189801930
    +B1114374535750N00606870EA0190301933
    +B1114394535757N00606884EA0191101939
    +B1114414535761N00606896EA0191601945
    +B1114434535767N00606910EA0192201950
    +B1114454535766N00606928EA0193101956
    +B1114474535755N00606937EA0193501963
    +B1114494535745N00606927EA0194101968
    +B1114514535746N00606908EA0195001976
    +B1114534535757N00606903EA0195801984
    +B1114554535768N00606914EA0196501992
    +B1114574535769N00606933EA0197301999
    +B1114594535760N00606944EA0197802007
    +B1115014535749N00606941EA0198202011
    +B1115034535739N00606933EA0198702015
    +B1115054535732N00606920EA0199302021
    +B1115074535728N00606904EA0199902027
    +B1115094535731N00606892EA0200502033
    +B1115114535737N00606887EA0200702038
    +B1115134535745N00606887EA0201002041
    +B1115154535755N00606895EA0201502045
    +B1115174535756N00606911EA0202002050
    +B1115194535749N00606925EA0202802056
    +B1115214535739N00606933EA0203602064
    +B1115234535730N00606940EA0204002070
    +B1115254535718N00606938EA0204302075
    +B1115274535708N00606923EA0204702080
    +B1115294535705N00606906EA0205102084
    +B1115314535708N00606891EA0205202086
    +B1115334535717N00606878EA0205302088
    +B1115354535731N00606877EA0205402088
    +B1115374535744N00606883EA0205802091
    +B1115394535751N00606894EA0206002093
    +B1115414535754N00606911EA0206102094
    +B1115434535751N00606932EA0207002097
    +B1115454535745N00606949EA0208002103
    +B1115474535742N00606961EA0208502109
    +B1115494535739N00606970EA0208602114
    +B1115514535733N00606978EA0208302116
    +B1115534535723N00606983EA0208402117
    +B1115554535713N00606981EA0208302117
    +B1115574535703N00606973EA0208302116
    +B1115594535693N00606964EA0208802117
    +B1116014535686N00606955EA0209302120
    +B1116034535679N00606946EA0209602124
    +B1116054535672N00606936EA0209802127
    +B1116074535668N00606918EA0209702128
    +B1116094535676N00606902EA0209902129
    +B1116114535688N00606900EA0210202131
    +B1116134535696N00606910EA0210402134
    +B1116154535699N00606926EA0210902137
    +B1116174535696N00606941EA0211602142
    +B1116194535690N00606954EA0211902148
    +B1116214535682N00606961EA0211902151
    +B1116234535667N00606958EA0211802153
    +B1116254535655N00606947EA0212202155
    +B1116274535651N00606932EA0212402157
    +B1116294535655N00606920EA0212502159
    +B1116314535663N00606916EA0212402158
    +B1116334535673N00606922EA0212702158
    +B1116354535683N00606926EA0213102161
    +B1116374535691N00606925EA0213302164
    +B1116394535700N00606922EA0213202166
    +B1116414535710N00606916EA0213502167
    +B1116434535719N00606908EA0214002171
    +B1116454535725N00606900EA0214302175
    +B1116474535730N00606890EA0214402177
    +B1116494535734N00606877EA0214802180
    +B1116514535735N00606862EA0215202184
    +B1116534535738N00606853EA0215402188
    +B1116554535747N00606848EA0215302190
    +B1116574535758N00606856EA0215502191
    +B1116594535761N00606873EA0215702193
    +B1117014535758N00606889EA0216002196
    +B1117034535749N00606898EA0216402199
    +B1117054535738N00606896EA0216902202
    +B1117074535730N00606885EA0217202206
    +B1117094535729N00606870EA0217602209
    +B1117114535733N00606859EA0217902213
    +B1117134535741N00606853EA0218202216
    +B1117154535751N00606855EA0218402219
    +B1117174535761N00606863EA0218502221
    +B1117194535767N00606881EA0218502222
    +B1117214535762N00606900EA0218702223
    +B1117234535752N00606912EA0219102226
    +B1117254535741N00606921EA0219702231
    +B1117274535732N00606928EA0219902235
    +B1117294535723N00606932EA0219802236
    +B1117314535712N00606933EA0219702237
    +B1117334535701N00606926EA0219802236
    +B1117354535693N00606913EA0220102238
    +B1117374535689N00606900EA0220402241
    +B1117394535688N00606888EA0220602242
    +B1117414535685N00606875EA0220802244
    +B1117434535682N00606862EA0221002246
    +B1117454535682N00606849EA0221102248
    +B1117474535687N00606838EA0221302249
    +B1117494535694N00606830EA0221402251
    +B1117514535702N00606825EA0221402251
    +B1117534535711N00606820EA0221302251
    +B1117554535722N00606821EA0221402251
    +B1117574535732N00606831EA0221502252
    +B1117594535741N00606842EA0221602253
    +B1118014535749N00606854EA0221602254
    +B1118034535756N00606867EA0221602254
    +B1118054535759N00606883EA0221702254
    +B1118074535753N00606900EA0222102255
    +B1118094535746N00606910EA0222302257
    +B1118114535741N00606920EA0222202256
    +B1118134535736N00606929EA0222102256
    +B1118154535727N00606931EA0221702254
    +B1118174535715N00606927EA0221602252
    +B1118194535703N00606922EA0221802252
    +B1118214535691N00606919EA0222002254
    +B1118234535680N00606916EA0222202256
    +B1118254535671N00606910EA0222302257
    +B1118274535661N00606903EA0222402258
    +B1118294535651N00606896EA0222702261
    +B1118314535641N00606889EA0222802263
    +B1118334535631N00606882EA0222902264
    +B1118354535620N00606874EA0222902264
    +B1118374535612N00606864EA0223002265
    +B1118394535606N00606851EA0222902265
    +B1118414535600N00606836EA0223002265
    +B1118434535595N00606821EA0223002265
    +B1118454535590N00606804EA0223002265
    +B1118474535585N00606787EA0222802264
    +B1118494535582N00606770EA0222502263
    +B1118514535578N00606751EA0222102260
    +B1118534535574N00606731EA0221802256
    +B1118554535569N00606711EA0221502253
    +B1118574535565N00606691EA0221302250
    +B1118594535562N00606670EA0220902247
    +B1119014535557N00606650EA0220602244
    +B1119034535551N00606631EA0220402242
    +B1119054535546N00606613EA0220002238
    +B1119074535539N00606594EA0219602234
    +B1119094535532N00606574EA0219102230
    +B1119114535523N00606556EA0218902227
    +B1119134535513N00606540EA0218502223
    +B1119154535503N00606523EA0218002218
    +B1119174535495N00606504EA0217502213
    +B1119194535486N00606486EA0217102209
    +B1119214535476N00606469EA0216502203
    +B1119234535466N00606451EA0216002197
    +B1119254535455N00606432EA0215902193
    +B1119274535447N00606415EA0215802191
    +B1119294535438N00606400EA0215502188
    +B1119314535428N00606383EA0215202184
    +B1119334535417N00606367EA0215002183
    +B1119354535406N00606354EA0214702181
    +B1119374535395N00606340EA0214502178
    +B1119394535385N00606325EA0214202175
    +B1119414535374N00606309EA0213902172
    +B1119434535363N00606292EA0213702169
    +B1119454535353N00606275EA0213502168
    +B1119474535344N00606259EA0213302166
    +B1119494535334N00606242EA0213102163
    +B1119514535325N00606226EA0212802161
    +B1119534535316N00606207EA0212602159
    +B1119554535308N00606188EA0212602158
    +B1119574535300N00606170EA0212402157
    +B1119594535292N00606152EA0212202155
    +B1120014535283N00606133EA0212202154
    +B1120034535274N00606116EA0212002153
    +B1120054535266N00606100EA0211802151
    +B1120074535257N00606082EA0211602149
    +B1120094535247N00606064EA0211502148
    +B1120114535237N00606047EA0211302147
    +B1120134535229N00606030EA0211102145
    +B1120154535220N00606012EA0210902142
    +B1120174535210N00605995EA0210802141
    +B1120194535200N00605979EA0210602140
    +B1120214535191N00605962EA0210502138
    +B1120234535182N00605946EA0210202137
    +B1120254535172N00605929EA0210002136
    +B1120274535162N00605914EA0209602134
    +B1120294535152N00605898EA0209502133
    +B1120314535142N00605883EA0209302131
    +B1120334535132N00605868EA0209002129
    +B1120354535122N00605852EA0208902127
    +B1120374535112N00605837EA0208802126
    +B1120394535102N00605823EA0208602124
    +B1120414535092N00605807EA0208402122
    +B1120434535082N00605791EA0208202120
    +B1120454535073N00605775EA0208102118
    +B1120474535064N00605759EA0207902116
    +B1120494535055N00605743EA0207702114
    +B1120514535045N00605727EA0207402111
    +B1120534535036N00605710EA0207202109
    +B1120554535028N00605693EA0207102107
    +B1120574535019N00605676EA0206902105
    +B1120594535011N00605659EA0206602103
    +B1121014535002N00605643EA0206402100
    +B1121034534993N00605626EA0206102097
    +B1121054534984N00605609EA0205802094
    +B1121074534975N00605592EA0205402091
    +B1121094534966N00605574EA0205602088
    +B1121114534957N00605556EA0205002085
    +B1121134534947N00605539EA0204802083
    +B1121154534938N00605522EA0204602080
    +B1121174534930N00605504EA0204302078
    +B1121194534920N00605488EA0204102075
    +B1121214534911N00605472EA0203802073
    +B1121234534901N00605456EA0203602071
    +B1121254534890N00605442EA0203302069
    +B1121274534878N00605428EA0203202066
    +B1121294534868N00605414EA0202902064
    +B1121314534856N00605401EA0202702062
    +B1121334534844N00605388EA0202602060
    +B1121354534833N00605375EA0202402059
    +B1121374534822N00605361EA0202202057
    +B1121394534811N00605347EA0202002055
    +B1121414534800N00605334EA0201902053
    +B1121434534789N00605322EA0201702052
    +B1121454534778N00605310EA0201402050
    +B1121474534768N00605294EA0201502048
    +B1121494534758N00605278EA0201402048
    +B1121514534749N00605263EA0201402047
    +B1121534534738N00605250EA0201202046
    +B1121554534727N00605238EA0201102045
    +B1121574534715N00605227EA0201102045
    +B1121594534705N00605215EA0201102044
    +B1122014534694N00605202EA0200902042
    +B1122034534683N00605188EA0200902041
    +B1122054534673N00605175EA0200702041
    +B1122074534663N00605161EA0200502039
    +B1122094534653N00605147EA0200302037
    +B1122114534644N00605131EA0200102035
    +B1122134534635N00605115EA0199802033
    +B1122154534626N00605098EA0199602031
    +B1122174534617N00605080EA0199402029
    +B1122194534608N00605063EA0199302028
    +B1122214534599N00605046EA0199302027
    +B1122234534591N00605030EA0199202026
    +B1122254534581N00605014EA0199002025
    +B1122274534571N00605000EA0199002024
    +B1122294534561N00604986EA0198802022
    +B1122314534551N00604971EA0198702021
    +B1122334534541N00604956EA0198602019
    +B1122354534531N00604942EA0198402018
    +B1122374534521N00604928EA0198402017
    +B1122394534511N00604914EA0198302016
    +B1122414534501N00604900EA0198202015
    +B1122434534490N00604886EA0198102014
    +B1122454534479N00604873EA0198002013
    +B1122474534469N00604860EA0197802011
    +B1122494534459N00604847EA0197602009
    +B1122514534448N00604835EA0197302007
    +B1122534534437N00604823EA0197102005
    +B1122554534426N00604811EA0196902003
    +B1122574534416N00604799EA0196702000
    +B1122594534405N00604786EA0196601998
    +B1123014534395N00604773EA0196301996
    +B1123034534385N00604760EA0196001994
    +B1123054534375N00604748EA0195701991
    +B1123074534364N00604734EA0195401987
    +B1123094534354N00604721EA0195201985
    +B1123114534344N00604708EA0195001983
    +B1123134534336N00604694EA0194801981
    +B1123154534327N00604680EA0194601979
    +B1123174534318N00604667EA0194401977
    +B1123194534308N00604658EA0194101974
    +B1123214534298N00604647EA0193801971
    +B1123234534287N00604635EA0193601969
    +B1123254534277N00604623EA0193401967
    +B1123274534268N00604609EA0193301965
    +B1123294534259N00604594EA0193101963
    +B1123314534249N00604581EA0192901961
    +B1123334534239N00604568EA0192701958
    +B1123354534228N00604556EA0192501957
    +B1123374534218N00604544EA0192301955
    +B1123394534208N00604534EA0191901952
    +B1123414534198N00604523EA0191601948
    +B1123434534187N00604509EA0191301946
    +B1123454534178N00604493EA0191101943
    +B1123474534170N00604478EA0190901941
    +B1123494534160N00604464EA0190801939
    +B1123514534152N00604451EA0190501937
    +B1123534534143N00604438EA0190301934
    +B1123554534133N00604427EA0190101932
    +B1123574534123N00604415EA0189901931
    +B1123594534114N00604404EA0189701929
    +B1124014534105N00604394EA0189501926
    +B1124034534096N00604383EA0189501924
    +B1124054534085N00604373EA0189801924
    +B1124074534076N00604366EA0189901925
    +B1124094534070N00604359EA0189701924
    +B1124114534062N00604352EA0189701923
    +B1124134534052N00604344EA0190001925
    +B1124154534045N00604332EA0190401928
    +B1124174534040N00604321EA0190601932
    +B1124194534033N00604311EA0190501933
    +B1124214534023N00604301EA0190101933
    +B1124234534013N00604290EA0190101932
    +B1124254534004N00604281EA0190401934
    +B1124274533996N00604273EA0190801938
    +B1124294533990N00604265EA0191001941
    +B1124314533984N00604255EA0191201943
    +B1124334533976N00604246EA0191401945
    +B1124354533967N00604242EA0191601947
    +B1124374533960N00604253EA0191601948
    +B1124394533962N00604268EA0191701949
    +B1124414533972N00604275EA0192001951
    +B1124434533981N00604269EA0192101953
    +B1124454533987N00604255EA0192401955
    +B1124474533989N00604241EA0193001959
    +B1124494533988N00604229EA0193301962
    +B1124514533985N00604217EA0193601964
    +B1124534533977N00604208EA0194001967
    +B1124554533968N00604219EA0194301971
    +B1124574533971N00604236EA0194401974
    +B1124594533984N00604235EA0194401975
    +B1125014533994N00604223EA0194801978
    +B1125034533997N00604209EA0195101981
    +B1125054533996N00604193EA0195501984
    +B1125074533991N00604176EA0196201989
    +B1125094533984N00604163EA0196801996
    +B1125114533976N00604153EA0197302001
    +B1125134533966N00604147EA0197702006
    +B1125154533954N00604157EA0198102009
    +B1125174533954N00604173EA0198502013
    +B1125194533965N00604181EA0199102017
    +B1125214533975N00604181EA0199802023
    +B1125234533981N00604170EA0200202028
    +B1125254533983N00604155EA0200902034
    +B1125274533979N00604139EA0201302040
    +B1125294533969N00604131EA0201502044
    +B1125314533958N00604131EA0201902048
    +B1125334533950N00604139EA0202302054
    +B1125354533951N00604151EA0202602057
    +B1125374533958N00604159EA0203102062
    +B1125394533968N00604159EA0203602067
    +B1125414533975N00604147EA0204102072
    +B1125434533978N00604132EA0204602078
    +B1125454533975N00604118EA0204702082
    +B1125474533969N00604104EA0204902084
    +B1125494533959N00604095EA0205202087
    +B1125514533949N00604094EA0205402089
    +B1125534533941N00604100EA0205502091
    +B1125554533938N00604109EA0205602093
    +B1125574533940N00604120EA0205802095
    +B1125594533944N00604127EA0205902096
    +B1126014533951N00604133EA0206302098
    +B1126034533957N00604141EA0206902102
    +B1126054533960N00604149EA0207202106
    +B1126074533963N00604155EA0207102108
    +B1126094533971N00604157EA0206702109
    +B1126114533982N00604150EA0206602109
    +B1126134533987N00604132EA0206602107
    +B1126154533986N00604114EA0206902107
    +B1126174533983N00604098EA0207102108
    +B1126194533977N00604084EA0207202109
    +B1126214533967N00604074EA0207102108
    +B1126234533956N00604069EA0207302110
    +B1126254533946N00604068EA0207402111
    +B1126274533936N00604071EA0207402110
    +B1126294533925N00604074EA0207302109
    +B1126314533914N00604061EA0207102108
    +B1126334533913N00604041EA0207102106
    +B1126354533921N00604028EA0207002104
    +B1126374533931N00604022EA0207002103
    +B1126394533941N00604024EA0206902102
    +B1126414533948N00604035EA0206802100
    +B1126434533952N00604048EA0206902100
    +B1126454533956N00604060EA0206802100
    +B1126474533958N00604071EA0206802099
    +B1126494533960N00604083EA0207002100
    +B1126514533961N00604093EA0207102101
    +B1126534533963N00604102EA0207302103
    +B1126554533964N00604111EA0207302104
    +B1126574533966N00604121EA0207302104
    +B1126594533967N00604130EA0207102103
    +B1127014533964N00604139EA0206802102
    +B1127034533956N00604147EA0206602099
    +B1127054533943N00604148EA0206502097
    +B1127074533930N00604143EA0206402096
    +B1127094533917N00604135EA0206302094
    +B1127114533903N00604127EA0206202092
    +B1127134533890N00604118EA0206002091
    +B1127154533877N00604108EA0205702088
    +B1127174533864N00604099EA0205402084
    +B1127194533849N00604088EA0205202082
    +B1127214533835N00604079EA0205102080
    +B1127234533821N00604069EA0204902079
    +B1127254533808N00604059EA0204702076
    +B1127274533794N00604048EA0204502074
    +B1127294533781N00604038EA0204102072
    +B1127314533768N00604027EA0203602068
    +B1127334533754N00604017EA0203302064
    +B1127354533741N00604006EA0203002062
    +B1127374533729N00603995EA0202602058
    +B1127394533716N00603985EA0202102053
    +B1127414533703N00603973EA0201802049
    +B1127434533691N00603963EA0201602046
    +B1127454533679N00603952EA0201302044
    +B1127474533668N00603941EA0201002041
    +B1127494533656N00603931EA0200702038
    +B1127514533644N00603921EA0200602036
    +B1127534533633N00603911EA0200302034
    +B1127554533622N00603902EA0200102031
    +B1127574533610N00603892EA0199802029
    +B1127594533599N00603883EA0199502027
    +B1128014533587N00603874EA0199102023
    +B1128034533576N00603866EA0198802020
    +B1128054533565N00603856EA0198602018
    +B1128074533555N00603844EA0198202014
    +B1128094533544N00603836EA0197702010
    +B1128114533532N00603828EA0197202006
    +B1128134533521N00603819EA0196902002
    +B1128154533509N00603809EA0196701999
    +B1128174533498N00603800EA0196401996
    +B1128194533486N00603791EA0196001993
    +B1128214533474N00603781EA0195701989
    +B1128234533461N00603771EA0195601987
    +B1128254533449N00603761EA0195701986
    +B1128274533438N00603749EA0195601986
    +B1128294533427N00603738EA0195501984
    +B1128314533414N00603729EA0195501984
    +B1128334533402N00603720EA0195501984
    +B1128354533390N00603711EA0195401984
    +B1128374533380N00603703EA0195501984
    +B1128394533370N00603694EA0195401985
    +B1128414533362N00603686EA0195101984
    +B1128434533352N00603676EA0194801981
    +B1128454533341N00603666EA0194601979
    +B1128474533331N00603657EA0194401977
    +B1128494533322N00603649EA0194201974
    +B1128514533312N00603640EA0194201973
    +B1128534533303N00603633EA0194301973
    +B1128554533295N00603625EA0194401974
    +B1128574533287N00603617EA0194501975
    +B1128594533280N00603609EA0194401974
    +B1129014533272N00603601EA0194401974
    +B1129034533264N00603595EA0194301974
    +B1129054533257N00603591EA0194101973
    +B1129074533249N00603588EA0194001972
    +B1129094533242N00603585EA0194001971
    +B1129114533235N00603583EA0193701970
    +B1129134533226N00603580EA0193501968
    +B1129154533218N00603578EA0193301966
    +B1129174533208N00603577EA0193301965
    +B1129194533200N00603575EA0193301964
    +B1129214533191N00603572EA0193101963
    +B1129234533181N00603568EA0193001962
    +B1129254533172N00603563EA0193101962
    +B1129274533164N00603558EA0193301963
    +B1129294533155N00603553EA0193801965
    +B1129314533147N00603548EA0194401969
    +B1129334533141N00603540EA0194901974
    +B1129354533133N00603536EA0195401979
    +B1129374533125N00603548EA0195901984
    +B1129394533130N00603566EA0196401989
    +B1129414533144N00603568EA0196801994
    +B1129434533154N00603554EA0197201999
    +B1129454533152N00603537EA0197702004
    +B1129474533143N00603530EA0198002010
    +B1129494533132N00603534EA0198602015
    +B1129514533122N00603538EA0199202021
    +B1129534533116N00603546EA0199702026
    +B1129554533116N00603564EA0200202031
    +B1129574533126N00603573EA0200702035
    +B1129594533139N00603567EA0201002039
    +B1130014533146N00603552EA0201302042
    +B1130034533146N00603537EA0201702046
    +B1130054533138N00603528EA0202102050
    +B1130074533126N00603533EA0202402052
    +B1130094533120N00603549EA0202902055
    +B1130114533125N00603564EA0203602060
    +B1130134533134N00603572EA0204202066
    +B1130154533144N00603572EA0204302070
    +B1130174533154N00603562EA0204602074
    +B1130194533156N00603545EA0204902077
    +B1130214533151N00603531EA0205202080
    +B1130234533142N00603526EA0205602084
    +B1130254533133N00603530EA0206002087
    +B1130274533129N00603546EA0206402091
    +B1130294533130N00603559EA0206902097
    +B1130314533134N00603569EA0207102101
    +B1130334533142N00603571EA0206902104
    +B1130354533154N00603560EA0207002105
    +B1130374533158N00603544EA0207302108
    +B1130394533156N00603527EA0207602109
    +B1130414533151N00603513EA0207902112
    +B1130434533141N00603505EA0208102114
    +B1130454533130N00603500EA0208602118
    +B1130474533120N00603500EA0209102123
    +B1130494533110N00603504EA0209502127
    +B1130514533103N00603513EA0209802131
    +B1130534533104N00603525EA0210002134
    +B1130554533109N00603534EA0210402137
    +B1130574533117N00603534EA0210702141
    +B1130594533124N00603522EA0210802144
    +B1131014533126N00603501EA0211102146
    +B1131034533121N00603482EA0211402149
    +B1131054533107N00603474EA0211702152
    +B1131074533097N00603481EA0211902154
    +B1131094533093N00603494EA0212102156
    +B1131114533093N00603506EA0212602160
    +B1131134533097N00603513EA0212802163
    +B1131154533105N00603513EA0212702165
    +B1131174533113N00603499EA0212502165
    +B1131194533115N00603477EA0212502163
    +B1131214533111N00603457EA0212802164
    +B1131234533102N00603443EA0213202167
    +B1131254533093N00603434EA0213402169
    +B1131274533083N00603429EA0213402170
    +B1131294533072N00603431EA0213302169
    +B1131314533065N00603444EA0213202168
    +B1131334533067N00603456EA0213302168
    +B1131354533073N00603461EA0213402169
    +B1131374533079N00603462EA0213502170
    +B1131394533086N00603460EA0213202169
    +B1131414533093N00603454EA0212802167
    +B1131434533100N00603435EA0212902166
    +B1131454533103N00603416EA0213102167
    +B1131474533099N00603399EA0213102166
    +B1131494533092N00603382EA0213002165
    +B1131514533084N00603367EA0212802163
    +B1131534533075N00603353EA0212502160
    +B1131554533065N00603342EA0212102157
    +B1131574533054N00603331EA0211302152
    +B1131594533043N00603317EA0210602145
    +B1132014533033N00603303EA0210102139
    +B1132034533023N00603290EA0209602133
    +B1132054533013N00603276EA0209102127
    +B1132074533003N00603261EA0208702122
    +B1132094532993N00603246EA0208402118
    +B1132114532984N00603232EA0207902113
    +B1132134532974N00603217EA0207402107
    +B1132154532965N00603200EA0206902102
    +B1132174532956N00603181EA0206502098
    +B1132194532948N00603164EA0206102094
    +B1132214532939N00603148EA0205602089
    +B1132234532930N00603130EA0205302085
    +B1132254532920N00603113EA0205102081
    +B1132274532911N00603098EA0204802078
    +B1132294532901N00603082EA0204302074
    +B1132314532891N00603068EA0203902070
    +B1132334532880N00603053EA0203802067
    +B1132354532870N00603037EA0203402063
    +B1132374532860N00603021EA0203102060
    +B1132394532850N00603005EA0202702056
    +B1132414532841N00602988EA0202302052
    +B1132434532832N00602970EA0202002048
    +B1132454532823N00602953EA0201602044
    +B1132474532814N00602936EA0201202040
    +B1132494532806N00602918EA0200802036
    +B1132514532797N00602900EA0200402032
    +B1132534532789N00602882EA0200002029
    +B1132554532783N00602861EA0199602025
    +B1132574532778N00602841EA0199402023
    +B1132594532772N00602822EA0198802019
    +B1133014532765N00602804EA0198202015
    +B1133034532756N00602785EA0197702010
    +B1133054532747N00602769EA0197402006
    +B1133074532736N00602754EA0196902002
    +B1133094532726N00602741EA0196401998
    +B1133114532715N00602729EA0196001993
    +B1133134532704N00602716EA0195601988
    +B1133154532693N00602702EA0195201984
    +B1133174532683N00602688EA0194801980
    +B1133194532674N00602673EA0194301975
    +B1133214532664N00602659EA0193801970
    +B1133234532654N00602644EA0193301965
    +B1133254532644N00602629EA0192901961
    +B1133274532636N00602614EA0192601958
    +B1133294532628N00602598EA0192201954
    +B1133314532619N00602583EA0191901950
    +B1133334532610N00602568EA0191601947
    +B1133354532601N00602554EA0191201944
    +B1133374532591N00602540EA0190901940
    +B1133394532581N00602526EA0190601937
    +B1133414532571N00602513EA0190301934
    +B1133434532561N00602499EA0190001931
    +B1133454532550N00602485EA0189801928
    +B1133474532540N00602472EA0189501926
    +B1133494532530N00602458EA0189201923
    +B1133514532520N00602443EA0188901920
    +B1133534532510N00602429EA0188501916
    +B1133554532498N00602416EA0188301914
    +B1133574532489N00602402EA0187901911
    +B1133594532479N00602389EA0187701907
    +B1134014532468N00602375EA0187501905
    +B1134034532459N00602361EA0187401904
    +B1134054532449N00602347EA0187201902
    +B1134074532440N00602333EA0187001900
    +B1134094532430N00602319EA0186801898
    +B1134114532420N00602306EA0186701896
    +B1134134532410N00602293EA0186401894
    +B1134154532400N00602280EA0186201892
    +B1134174532390N00602267EA0186001891
    +B1134194532380N00602254EA0185901889
    +B1134214532370N00602241EA0185801887
    +B1134234532359N00602227EA0185601885
    +B1134254532349N00602213EA0185401883
    +B1134274532339N00602200EA0185201881
    +B1134294532329N00602186EA0185001879
    +B1134314532319N00602173EA0184801877
    +B1134334532309N00602160EA0184601875
    +B1134354532298N00602148EA0184401873
    +B1134374532288N00602136EA0184201871
    +B1134394532277N00602123EA0184001869
    +B1134414532266N00602111EA0183801867
    +B1134434532256N00602098EA0183601865
    +B1134454532245N00602086EA0183401863
    +B1134474532235N00602074EA0183201861
    +B1134494532224N00602061EA0183001859
    +B1134514532214N00602049EA0182801857
    +B1134534532204N00602036EA0182501855
    +B1134554532193N00602024EA0182401852
    +B1134574532183N00602011EA0182201851
    +B1134594532172N00601999EA0182001849
    +B1135014532162N00601986EA0181801847
    +B1135034532152N00601973EA0181501845
    +B1135054532141N00601961EA0181301843
    +B1135074532131N00601950EA0181101841
    +B1135094532122N00601938EA0181001839
    +B1135114532112N00601928EA0181201839
    +B1135134532104N00601921EA0181201839
    +B1135154532097N00601915EA0181101838
    +B1135174532090N00601908EA0181001837
    +B1135194532082N00601900EA0181201838
    +B1135214532074N00601897EA0181401839
    +B1135234532067N00601893EA0181401841
    +B1135254532062N00601888EA0181101840
    +B1135274532055N00601881EA0180801839
    +B1135294532044N00601875EA0181001839
    +B1135314532034N00601872EA0181101840
    +B1135334532025N00601868EA0180901840
    +B1135354532018N00601862EA0180501838
    +B1135374532010N00601853EA0180201835
    +B1135394532002N00601845EA0179901832
    +B1135414531994N00601837EA0179601828
    +B1135434531985N00601827EA0179501826
    +B1135454531976N00601818EA0179401825
    +B1135474531967N00601809EA0179301824
    +B1135494531959N00601799EA0179101822
    +B1135514531951N00601789EA0178801820
    +B1135534531942N00601779EA0178501817
    +B1135554531934N00601768EA0178001813
    +B1135574531925N00601755EA0177501809
    +B1135594531916N00601743EA0177001804
    +B1136014531907N00601730EA0176401798
    +B1136034531898N00601718EA0175801792
    +B1136054531887N00601706EA0175101786
    +B1136074531876N00601695EA0174601779
    +B1136094531865N00601684EA0174301774
    +B1136114531856N00601673EA0173801769
    +B1136134531846N00601663EA0173301764
    +B1136154531834N00601654EA0172801759
    +B1136174531823N00601645EA0172301754
    +B1136194531813N00601637EA0171601748
    +B1136214531801N00601631EA0170801741
    +B1136234531788N00601629EA0170201734
    +B1136254531775N00601626EA0169701728
    +B1136274531763N00601624EA0169401724
    +B1136294531752N00601623EA0168801718
    +B1136314531741N00601622EA0168301712
    +B1136334531729N00601622EA0167801707
    +B1136354531717N00601624EA0167301702
    +B1136374531704N00601627EA0166701697
    +B1136394531691N00601631EA0166101691
    +B1136414531677N00601632EA0165401685
    +B1136434531664N00601632EA0164701678
    +B1136454531649N00601633EA0164101670
    +B1136474531635N00601635EA0163701665
    +B1136494531621N00601637EA0163401660
    +B1136514531608N00601640EA0163101656
    +B1136534531595N00601641EA0162801654
    +B1136554531582N00601643EA0162801652
    +B1136574531570N00601648EA0162801651
    +B1136594531560N00601651EA0162601650
    +B1137014531547N00601654EA0162401648
    +B1137034531533N00601658EA0162301647
    +B1137054531522N00601661EA0161901645
    +B1137074531511N00601664EA0161201642
    +B1137094531498N00601669EA0160801637
    +B1137114531484N00601674EA0160501634
    +B1137134531472N00601677EA0159901629
    +B1137154531459N00601681EA0159201623
    +B1137174531444N00601687EA0158701618
    +B1137194531430N00601689EA0158401614
    +B1137214531417N00601686EA0157801608
    +B1137234531403N00601682EA0157301603
    +B1137254531387N00601677EA0157101598
    +B1137274531374N00601669EA0156701595
    +B1137294531360N00601660EA0156301591
    +B1137314531347N00601650EA0156101587
    +B1137334531334N00601639EA0155801585
    +B1137354531323N00601627EA0155501582
    +B1137374531313N00601617EA0155001578
    +B1137394531303N00601605EA0154601574
    +B1137414531293N00601593EA0154301571
    +B1137434531283N00601584EA0153701566
    +B1137454531272N00601575EA0153101560
    +B1137474531260N00601565EA0152601555
    +B1137494531248N00601555EA0152201550
    +B1137514531236N00601546EA0152001547
    +B1137534531226N00601535EA0151701544
    +B1137554531215N00601525EA0151401540
    +B1137574531203N00601515EA0151301538
    +B1137594531192N00601505EA0151001536
    +B1138014531181N00601494EA0150701533
    +B1138034531170N00601484EA0150501530
    +B1138054531158N00601475EA0150301529
    +B1138074531147N00601465EA0150001527
    +B1138094531136N00601456EA0149801524
    +B1138114531124N00601447EA0149501521
    +B1138134531114N00601436EA0149201518
    +B1138154531103N00601425EA0148801515
    +B1138174531091N00601415EA0148601512
    +B1138194531079N00601403EA0148401510
    +B1138214531068N00601393EA0148101508
    +B1138234531056N00601383EA0147801504
    +B1138254531045N00601371EA0147501502
    +B1138274531033N00601359EA0147301499
    +B1138294531022N00601348EA0146901496
    +B1138314531010N00601336EA0146701493
    +B1138334530998N00601325EA0146501491
    +B1138354530986N00601315EA0146201488
    +B1138374530975N00601304EA0145901486
    +B1138394530963N00601293EA0145601482
    +B1138414530950N00601283EA0145301480
    +B1138434530937N00601273EA0145101477
    +B1138454530924N00601263EA0144801474
    +B1138474530912N00601253EA0144501471
    +B1138494530900N00601241EA0144201468
    +B1138514530888N00601229EA0144001466
    +B1138534530876N00601216EA0143801464
    +B1138554530865N00601202EA0143601462
    +B1138574530854N00601189EA0143301459
    +B1138594530843N00601177EA0143001455
    +B1139014530831N00601165EA0142701453
    +B1139034530818N00601154EA0142501450
    +B1139054530805N00601146EA0142201447
    +B1139074530791N00601137EA0142001444
    +B1139094530778N00601127EA0141801443
    +B1139114530766N00601118EA0141401440
    +B1139134530754N00601108EA0141201437
    +B1139154530740N00601098EA0141101436
    +B1139174530728N00601089EA0140901434
    +B1139194530716N00601079EA0140501431
    +B1139214530703N00601069EA0140201428
    +B1139234530690N00601060EA0140001425
    +B1139254530677N00601050EA0139901424
    +B1139274530664N00601040EA0139701422
    +B1139294530652N00601031EA0139501420
    +B1139314530639N00601020EA0139201417
    +B1139334530627N00601009EA0139001415
    +B1139354530614N00600999EA0138901413
    +B1139374530602N00600989EA0138701411
    +B1139394530590N00600979EA0138501409
    +B1139414530577N00600970EA0138201406
    +B1139434530565N00600958EA0138001404
    +B1139454530554N00600947EA0137601402
    +B1139474530542N00600936EA0137301399
    +B1139494530530N00600925EA0136901395
    +B1139514530518N00600913EA0136601391
    +B1139534530504N00600903EA0136401388
    +B1139554530491N00600893EA0136101386
    +B1139574530479N00600883EA0135801383
    +B1139594530467N00600872EA0135701380
    +B1140014530456N00600861EA0135501378
    +B1140034530445N00600850EA0135201376
    +B1140054530436N00600838EA0135001374
    +B1140074530425N00600827EA0134901373
    +B1140094530415N00600817EA0134601371
    +B1140114530404N00600807EA0134401368
    +B1140134530392N00600795EA0134401367
    +B1140154530381N00600784EA0134401367
    +B1140174530371N00600773EA0134101365
    +B1140194530361N00600763EA0133901363
    +B1140214530350N00600753EA0133701361
    +B1140234530339N00600743EA0133501358
    +B1140254530328N00600734EA0133301357
    +B1140274530317N00600724EA0133101355
    +B1140294530307N00600715EA0132901352
    +B1140314530296N00600707EA0132701350
    +B1140334530285N00600699EA0132501348
    +B1140354530274N00600691EA0132201346
    +B1140374530263N00600683EA0131901343
    +B1140394530252N00600675EA0131801341
    +B1140414530240N00600670EA0131601339
    +B1140434530228N00600666EA0131201336
    +B1140454530216N00600663EA0131001334
    +B1140474530203N00600660EA0130801332
    +B1140494530191N00600655EA0130601329
    +B1140514530180N00600650EA0130401327
    +B1140534530168N00600644EA0130101324
    +B1140554530157N00600636EA0129901322
    +B1140574530147N00600629EA0129601319
    +B1140594530137N00600620EA0129401317
    +B1141014530127N00600611EA0129101314
    +B1141034530117N00600602EA0128901312
    +B1141054530108N00600592EA0128701310
    +B1141074530097N00600584EA0128501307
    +B1141094530087N00600575EA0128301306
    +B1141114530077N00600568EA0128101304
    +B1141134530066N00600560EA0127801302
    +B1141154530056N00600552EA0127701300
    +B1141174530046N00600543EA0127601298
    +B1141194530036N00600535EA0127501297
    +B1141214530026N00600527EA0127201295
    +B1141234530015N00600520EA0127001292
    +B1141254530005N00600511EA0126801290
    +B1141274529995N00600502EA0126601288
    +B1141294529985N00600494EA0126501287
    +B1141314529976N00600485EA0126201285
    +B1141334529967N00600476EA0126001283
    +B1141354529958N00600466EA0125901281
    +B1141374529949N00600457EA0125701279
    +B1141394529940N00600447EA0125501277
    +B1141414529931N00600438EA0125301275
    +B1141434529923N00600428EA0125101273
    +B1141454529914N00600418EA0124901271
    +B1141474529905N00600409EA0124601268
    +B1141494529896N00600399EA0124301265
    +B1141514529887N00600390EA0124101263
    +B1141534529878N00600381EA0123801260
    +B1141554529869N00600372EA0123501257
    +B1141574529859N00600364EA0123201254
    +B1141594529849N00600356EA0122901251
    +B1142014529839N00600348EA0122601248
    +B1142034529830N00600341EA0122301246
    +B1142054529820N00600333EA0122001242
    +B1142074529810N00600325EA0121701239
    +B1142094529800N00600317EA0121401236
    +B1142114529791N00600309EA0121101233
    +B1142134529782N00600301EA0120801230
    +B1142154529773N00600292EA0120601227
    +B1142174529765N00600283EA0120201225
    +B1142194529756N00600275EA0119801222
    +B1142214529747N00600266EA0119501218
    +B1142234529738N00600258EA0119301215
    +B1142254529729N00600250EA0119001212
    +B1142274529719N00600243EA0118601208
    +B1142294529710N00600234EA0118201204
    +B1142314529701N00600224EA0117901200
    +B1142334529693N00600214EA0117601197
    +B1142354529684N00600204EA0117201194
    +B1142374529676N00600194EA0117001191
    +B1142394529667N00600183EA0116601188
    +B1142414529659N00600173EA0116201184
    +B1142434529651N00600163EA0115801180
    +B1142454529642N00600152EA0115401176
    +B1142474529633N00600142EA0115201172
    +B1142494529624N00600133EA0114801169
    +B1142514529615N00600123EA0114401165
    +B1142534529606N00600112EA0114001161
    +B1142554529596N00600102EA0113701158
    +B1142574529587N00600091EA0113401154
    +B1142594529578N00600080EA0113101151
    +B1143014529569N00600071EA0112901149
    +B1143034529560N00600062EA0112501145
    +B1143054529551N00600052EA0112101142
    +B1143074529542N00600041EA0111801138
    +B1143094529533N00600031EA0111401135
    +B1143114529524N00600021EA0111101132
    +B1143134529514N00600012EA0110901129
    +B1143154529505N00600004EA0110801127
    +B1143174529496N00559995EA0110401125
    +B1143194529486N00559986EA0110201122
    +B1143214529477N00559978EA0110101121
    +B1143234529468N00559971EA0109901119
    +B1143254529458N00559963EA0109601116
    +B1143274529448N00559956EA0109401114
    +B1143294529439N00559949EA0109301112
    +B1143314529429N00559942EA0109001110
    +B1143334529419N00559936EA0108801107
    +B1143354529408N00559928EA0108601105
    +B1143374529397N00559921EA0108601104
    +B1143394529386N00559914EA0108501104
    +B1143414529376N00559908EA0108501103
    +B1143434529367N00559901EA0108501103
    +B1143454529359N00559895EA0108501103
    +B1143474529350N00559889EA0108401103
    +B1143494529342N00559883EA0108201102
    +B1143514529333N00559878EA0108101101
    +B1143534529324N00559874EA0108001099
    +B1143554529314N00559868EA0108001098
    +B1143574529305N00559862EA0107901098
    +B1143594529297N00559856EA0107701097
    +B1144014529289N00559849EA0107601095
    +B1144034529280N00559841EA0107401094
    +B1144054529271N00559833EA0107301093
    +B1144074529261N00559824EA0107201091
    +B1144094529252N00559817EA0107201091
    +B1144114529242N00559809EA0107101090
    +B1144134529233N00559801EA0107001089
    +B1144154529223N00559793EA0106901088
    +B1144174529212N00559784EA0106801087
    +B1144194529202N00559774EA0106801087
    +B1144214529192N00559766EA0106801087
    +B1144234529182N00559757EA0106701086
    +B1144254529172N00559749EA0106601085
    +B1144274529163N00559740EA0106501085
    +B1144294529153N00559731EA0106501084
    +B1144314529144N00559721EA0106501083
    +B1144334529135N00559712EA0106401083
    +B1144354529126N00559704EA0106201082
    +B1144374529117N00559695EA0105901080
    +B1144394529108N00559686EA0105801078
    +B1144414529099N00559677EA0105701077
    +B1144434529089N00559668EA0105501075
    +B1144454529079N00559658EA0105301074
    +B1144474529068N00559650EA0105301073
    +B1144494529059N00559641EA0105201072
    +B1144514529050N00559632EA0105001070
    +B1144534529039N00559624EA0105001070
    +B1144554529029N00559616EA0105001069
    +B1144574529019N00559607EA0104901069
    +B1144594529009N00559599EA0104801068
    +B1145014528999N00559591EA0104601066
    +B1145034528989N00559582EA0104501065
    +B1145054528978N00559573EA0104401064
    +B1145074528969N00559564EA0104301063
    +B1145094528959N00559557EA0104201062
    +B1145114528949N00559548EA0104001060
    +B1145134528940N00559540EA0104001059
    +B1145154528929N00559532EA0103901059
    +B1145174528918N00559525EA0103901058
    +B1145194528908N00559516EA0104001058
    +B1145214528898N00559509EA0103901058
    +B1145234528889N00559500EA0103701057
    +B1145254528879N00559490EA0103701056
    +B1145274528869N00559481EA0103701056
    +B1145294528861N00559471EA0103601056
    +B1145314528852N00559462EA0103401054
    +B1145334528842N00559451EA0103601054
    +B1145354528832N00559442EA0103601055
    +B1145374528823N00559433EA0103401053
    +B1145394528813N00559421EA0103501053
    +B1145414528804N00559413EA0103501054
    +B1145434528795N00559407EA0103201053
    +B1145454528785N00559399EA0103001051
    +B1145474528776N00559390EA0102901050
    +B1145494528768N00559381EA0102801048
    +B1145514528759N00559372EA0102901047
    +B1145534528750N00559365EA0103001048
    +B1145554528743N00559357EA0102801047
    +B1145574528736N00559346EA0102801046
    +B1145594528727N00559336EA0102901046
    +B1146014528718N00559327EA0103001048
    +B1146034528711N00559318EA0103001048
    +B1146054528702N00559309EA0103201049
    +B1146074528694N00559304EA0103301051
    +B1146094528686N00559300EA0103401053
    +B1146114528677N00559298EA0103401053
    +B1146134528667N00559296EA0103501054
    +B1146154528658N00559294EA0103601055
    +B1146174528649N00559292EA0103601056
    +B1146194528639N00559290EA0103701056
    +B1146214528630N00559288EA0103701057
    +B1146234528621N00559288EA0103501056
    +B1146254528611N00559287EA0103201054
    +B1146274528601N00559284EA0103001052
    +B1146294528592N00559281EA0102501049
    +B1146314528583N00559277EA0101901044
    +B1146334528574N00559272EA0101501039
    +B1146354528562N00559267EA0101401035
    +B1146374528550N00559265EA0101201033
    +B1146394528539N00559265EA0100801029
    +B1146414528527N00559264EA0100301024
    +B1146434528514N00559262EA0100201021
    +B1146454528502N00559260EA0099901019
    +B1146474528491N00559258EA0099501016
    +B1146494528479N00559253EA0099101011
    +B1146514528467N00559247EA0099001007
    +B1146534528455N00559241EA0099101006
    +B1146554528446N00559235EA0098901005
    +B1146574528437N00559230EA0098501002
    +B1146594528426N00559223EA0098200999
    +B1147014528416N00559214EA0098200998
    +B1147034528405N00559206EA0098200997
    +B1147054528395N00559198EA0098100997
    +B1147074528385N00559189EA0098100996
    +B1147094528376N00559181EA0098200997
    +B1147114528367N00559174EA0098100997
    +B1147134528358N00559165EA0098000996
    +B1147154528349N00559155EA0098000996
    +B1147174528340N00559148EA0097900996
    +B1147194528332N00559140EA0097700994
    +B1147214528323N00559130EA0097700994
    +B1147234528313N00559122EA0097800995
    +B1147254528306N00559115EA0097700994
    +B1147274528298N00559107EA0097600993
    +B1147294528291N00559099EA0097500992
    +B1147314528283N00559091EA0097500992
    +B1147334528273N00559084EA0097400991
    +B1147354528264N00559077EA0097300990
    +B1147374528253N00559070EA0097100989
    +B1147394528242N00559062EA0097100988
    +B1147414528231N00559055EA0097100988
    +B1147434528221N00559048EA0097000988
    +B1147454528211N00559041EA0096800986
    +B1147474528200N00559033EA0096700985
    +B1147494528190N00559025EA0096700985
    +B1147514528181N00559017EA0096500983
    +B1147534528172N00559006EA0096500982
    +B1147554528162N00558998EA0096700982
    +B1147574528152N00558992EA0096600982
    +B1147594528143N00558983EA0096400981
    +B1148014528133N00558974EA0096500981
    +B1148034528123N00558966EA0096500981
    +B1148054528114N00558957EA0096300980
    +B1148074528104N00558947EA0096000978
    +B1148094528094N00558936EA0095700975
    +B1148114528085N00558926EA0095200972
    +B1148134528074N00558914EA0095000968
    +B1148154528064N00558904EA0094800966
    +B1148174528055N00558895EA0094300963
    +B1148194528044N00558885EA0094100959
    +B1148214528032N00558875EA0094000957
    +B1148234528022N00558866EA0093600955
    +B1148254528011N00558857EA0093200950
    +B1148274527999N00558845EA0093300948
    +B1148294527988N00558835EA0093300947
    +B1148314527978N00558827EA0093200947
    +B1148334527967N00558818EA0093200946
    +B1148354527956N00558810EA0093200946
    +B1148374527945N00558802EA0093100946
    +B1148394527935N00558794EA0093100946
    +B1148414527926N00558785EA0093000945
    +B1148434527917N00558774EA0093100945
    +B1148454527910N00558764EA0093000945
    +B1148474527902N00558755EA0092800944
    +B1148494527893N00558746EA0092700943
    +B1148514527883N00558740EA0092500942
    +B1148534527874N00558733EA0092200939
    +B1148554527864N00558725EA0092100937
    +B1148574527853N00558716EA0092000935
    +B1148594527843N00558707EA0092000935
    +B1149014527833N00558699EA0092000935
    +B1149034527824N00558689EA0092100936
    +B1149054527815N00558679EA0092100936
    +B1149074527806N00558669EA0092100937
    +B1149094527797N00558660EA0091800935
    +B1149114527788N00558648EA0091600933
    +B1149134527778N00558636EA0091800932
    +B1149154527770N00558627EA0091900933
    +B1149174527763N00558618EA0091800933
    +B1149194527755N00558607EA0091700932
    +B1149214527746N00558597EA0091600931
    +B1149234527738N00558587EA0091400930
    +B1149254527730N00558576EA0091100927
    +B1149274527720N00558564EA0090900925
    +B1149294527710N00558552EA0090800923
    +B1149314527700N00558541EA0090700923
    +B1149334527690N00558529EA0090700922
    +B1149354527680N00558515EA0090700921
    +B1149374527672N00558500EA0090900922
    +B1149394527665N00558488EA0091000924
    +B1149414527656N00558477EA0091200925
    +B1149434527649N00558465EA0091300927
    +B1149454527642N00558452EA0091400929
    +B1149474527636N00558439EA0091500929
    +B1149494527629N00558425EA0091500930
    +B1149514527622N00558411EA0091500930
    +B1149534527615N00558397EA0091400930
    +B1149554527609N00558382EA0091300929
    +B1149574527601N00558368EA0091300929
    +B1149594527592N00558355EA0091100928
    +B1150014527582N00558341EA0090800926
    +B1150034527574N00558325EA0090700924
    +B1150054527566N00558310EA0090600924
    +B1150074527558N00558296EA0090400922
    +B1150094527551N00558280EA0090100920
    +B1150114527543N00558262EA0090000918
    +B1150134527536N00558245EA0090000917
    +B1150154527531N00558228EA0089900916
    +B1150174527526N00558211EA0089800915
    +B1150194527521N00558194EA0089700914
    +B1150214527515N00558178EA0089600913
    +B1150234527510N00558162EA0089400911
    +B1150254527504N00558145EA0089100909
    +B1150274527496N00558128EA0089000907
    +B1150294527488N00558111EA0089000906
    +B1150314527483N00558094EA0089000906
    +B1150334527480N00558078EA0089100907
    +B1150354527475N00558061EA0089100907
    +B1150374527469N00558046EA0089000906
    +B1150394527461N00558032EA0088800905
    +B1150414527450N00558019EA0089000905
    +B1150434527436N00558010EA0089200906
    +B1150454527424N00558001EA0089200907
    +B1150474527412N00557995EA0089100907
    +B1150494527398N00557991EA0089200907
    +B1150514527384N00557989EA0089500909
    +B1150534527371N00557985EA0089600911
    +B1150554527361N00557990EA0089600911
    +B1150574527356N00558004EA0089600912
    +B1150594527362N00558010EA0089600912
    +B1151014527370N00557999EA0089700912
    +B1151034527371N00557977EA0089900913
    +B1151054527366N00557957EA0090100915
    +B1151074527356N00557944EA0090300917
    +B1151094527344N00557942EA0090400919
    +B1151114527334N00557947EA0090500920
    +B1151134527330N00557961EA0090400920
    +B1151154527335N00557972EA0090400920
    +B1151174527342N00557975EA0090700922
    +B1151194527349N00557975EA0090700923
    +B1151214527358N00557970EA0090700923
    +B1151234527367N00557959EA0091000924
    +B1151254527374N00557942EA0091400926
    +B1151274527375N00557923EA0091800930
    +B1151294527372N00557907EA0091900934
    +B1151314527371N00557889EA0091700935
    +B1151334527370N00557869EA0091700936
    +B1151354527361N00557854EA0091400936
    +B1151374527347N00557856EA0091200934
    +B1151394527338N00557866EA0091100933
    +B1151414527338N00557879EA0091000931
    +B1151434527344N00557889EA0091300931
    +B1151454527354N00557893EA0091600932
    +B1151474527365N00557891EA0091900936
    +B1151494527374N00557887EA0092100938
    +B1151514527383N00557885EA0092300940
    +B1151534527391N00557883EA0092400942
    +B1151554527398N00557883EA0092400943
    +B1151574527407N00557879EA0092300943
    +B1151594527412N00557862EA0092200943
    +B1152014527410N00557845EA0092200943
    +B1152034527401N00557834EA0092200943
    +B1152054527391N00557834EA0092200942
    +B1152074527384N00557845EA0092200942
    +B1152094527382N00557857EA0092400943
    +B1152114527386N00557864EA0092500944
    +B1152134527391N00557868EA0092500945
    +B1152154527397N00557871EA0092500944
    +B1152174527404N00557873EA0092500944
    +B1152194527413N00557874EA0092700944
    +B1152214527422N00557874EA0092800946
    +B1152234527432N00557875EA0093100947
    +B1152254527442N00557877EA0093400950
    +B1152274527451N00557879EA0093700953
    +B1152294527459N00557882EA0094100957
    +B1152314527466N00557884EA0094600961
    +B1152334527473N00557885EA0094800964
    +B1152354527480N00557879EA0094900967
    +B1152374527483N00557862EA0094900969
    +B1152394527477N00557847EA0095000970
    +B1152414527462N00557846EA0095200971
    +B1152434527457N00557857EA0095300972
    +B1152454527458N00557868EA0095700974
    +B1152474527463N00557873EA0095900977
    +B1152494527470N00557873EA0095800978
    +B1152514527477N00557865EA0095900978
    +B1152534527480N00557847EA0096000978
    +B1152554527476N00557829EA0096400981
    +B1152574527470N00557818EA0096700985
    +B1152594527461N00557814EA0096800987
    +B1153014527453N00557817EA0096900988
    +B1153034527449N00557826EA0097000989
    +B1153054527450N00557836EA0097200990
    +B1153074527455N00557843EA0097500992
    +B1153094527462N00557843EA0097900995
    +B1153114527469N00557845EA0098300999
    +B1153134527475N00557850EA0098501002
    +B1153154527480N00557854EA0098501005
    +B1153174527488N00557850EA0098201006
    +B1153194527492N00557833EA0098101006
    +B1153214527487N00557816EA0098101006
    +B1153234527476N00557811EA0098201005
    +B1153254527465N00557819EA0098301004
    +B1153274527456N00557828EA0098701006
    +B1153294527449N00557835EA0099101010
    +B1153314527445N00557844EA0099501013
    +B1153334527444N00557854EA0099901017
    +B1153354527443N00557865EA0100301021
    +B1153374527442N00557875EA0100601024
    +B1153394527450N00557878EA0100801027
    +B1153414527456N00557866EA0101301029
    +B1153434527455N00557848EA0101601033
    +B1153454527443N00557838EA0101801035
    +B1153474527431N00557846EA0102201039
    +B1153494527430N00557861EA0102501041
    +B1153514527437N00557869EA0103001047
    +B1153534527444N00557862EA0103301051
    +B1153554527445N00557848EA0103701055
    +B1153574527439N00557835EA0104001059
    +B1153594527428N00557830EA0104201062
    +B1154014527416N00557840EA0104601065
    +B1154034527409N00557853EA0104701067
    +B1154054527409N00557866EA0104901069
    +B1154074527418N00557869EA0105301071
    +B1154094527424N00557862EA0105901075
    +B1154114527429N00557858EA0106401081
    +B1154134527434N00557855EA0106701085
    +B1154154527437N00557845EA0106801088
    +B1154174527438N00557831EA0107301091
    +B1154194527435N00557819EA0107501095
    +B1154214527425N00557811EA0107701097
    +B1154234527411N00557811EA0107901099
    +B1154254527401N00557823EA0108001101
    +B1154274527398N00557839EA0108201102
    +B1154294527402N00557849EA0108401104
    +B1154314527408N00557850EA0108401105
    +B1154334527415N00557843EA0108501105
    +B1154354527418N00557832EA0108901107
    +B1154374527417N00557819EA0109201110
    +B1154394527415N00557807EA0109101111
    +B1154414527410N00557795EA0108701111
    +B1154434527395N00557785EA0108901112
    +B1154454527380N00557789EA0109201112
    +B1154474527375N00557803EA0109001112
    +B1154494527374N00557816EA0108901109
    +B1154514527378N00557829EA0108901108
    +B1154534527387N00557835EA0109101109
    +B1154554527396N00557834EA0109401111
    +B1154574527403N00557831EA0109601113
    +B1154594527410N00557827EA0109801115
    +B1155014527418N00557823EA0110301118
    +B1155034527426N00557820EA0110701123
    +B1155054527431N00557816EA0110701126
    +B1155074527435N00557809EA0110201128
    +B1155094527434N00557791EA0109801129
    +B1155114527426N00557776EA0109901128
    +B1155134527413N00557773EA0110101128
    +B1155154527404N00557782EA0110301129
    +B1155174527402N00557796EA0110501130
    +B1155194527404N00557809EA0110801131
    +B1155214527411N00557816EA0110801133
    +B1155234527418N00557816EA0110501133
    +B1155254527422N00557802EA0110101130
    +B1155274527419N00557785EA0109901128
    +B1155294527409N00557774EA0109801126
    +B1155314527398N00557771EA0110001126
    +B1155334527387N00557773EA0110301128
    +B1155354527378N00557777EA0110401129
    +B1155374527369N00557782EA0110301130
    +B1155394527364N00557796EA0110301130
    +B1155414527364N00557811EA0110301129
    +B1155434527372N00557820EA0110301128
    +B1155454527381N00557819EA0110201128
    +B1155474527388N00557810EA0110301129
    +B1155494527392N00557801EA0110101128
    +B1155514527394N00557790EA0109601126
    +B1155534527392N00557775EA0109201123
    +B1155554527385N00557759EA0109101120
    +B1155574527373N00557748EA0109201120
    +B1155594527362N00557740EA0109101119
    +B1156014527352N00557731EA0109101118
    +B1156034527342N00557722EA0109101117
    +B1156054527333N00557715EA0109101117
    +B1156074527324N00557709EA0109201117
    +B1156094527314N00557708EA0109401118
    +B1156114527305N00557706EA0109701120
    +B1156134527298N00557706EA0109901122
    +B1156154527293N00557719EA0110001123
    +B1156174527298N00557734EA0110201124
    +B1156194527311N00557735EA0110401127
    +B1156214527317N00557724EA0110401128
    +B1156234527317N00557707EA0110401129
    +B1156254527312N00557691EA0110601130
    +B1156274527304N00557683EA0110701130
    +B1156294527293N00557684EA0111001132
    +B1156314527283N00557687EA0111201134
    +B1156334527276N00557691EA0111301136
    +B1156354527273N00557704EA0111501137
    +B1156374527276N00557715EA0111701139
    +B1156394527286N00557714EA0111401140
    +B1156414527295N00557699EA0111701140
    +B1156434527295N00557682EA0112001141
    +B1156454527288N00557671EA0112101142
    +B1156474527278N00557666EA0112201144
    +B1156494527269N00557672EA0112201144
    +B1156514527264N00557684EA0112401144
    +B1156534527263N00557696EA0112601146
    +B1156554527265N00557702EA0112201146
    +B1156574527272N00557706EA0111701144
    +B1156594527281N00557704EA0111401141
    +B1157014527289N00557689EA0111101137
    +B1157034527289N00557669EA0111301134
    +B1157054527285N00557654EA0111501134
    +B1157074527278N00557645EA0111601135
    +B1157094527268N00557638EA0111701136
    +B1157114527256N00557635EA0112001139
    +B1157134527245N00557635EA0112201141
    +B1157154527236N00557636EA0112201142
    +B1157174527228N00557642EA0112101142
    +B1157194527227N00557651EA0111901141
    +B1157214527233N00557655EA0111701140
    +B1157234527241N00557649EA0111601139
    +B1157254527247N00557635EA0111601138
    +B1157274527248N00557617EA0111701137
    +B1157294527241N00557597EA0111901138
    +B1157314527231N00557586EA0112101139
    +B1157334527221N00557581EA0112001139
    +B1157354527211N00557582EA0111701138
    +B1157374527207N00557593EA0111701137
    +B1157394527210N00557602EA0111801138
    +B1157414527217N00557601EA0111801138
    +B1157434527224N00557587EA0111801137
    +B1157454527225N00557568EA0111901138
    +B1157474527221N00557551EA0111801137
    +B1157494527212N00557540EA0111601136
    +B1157514527200N00557537EA0111301134
    +B1157534527192N00557546EA0111201132
    +B1157554527188N00557557EA0111301132
    +B1157574527181N00557566EA0111201131
    +B1157594527172N00557572EA0111101130
    +B1158014527163N00557579EA0110901128
    +B1158034527155N00557588EA0110801127
    +B1158054527147N00557594EA0110601125
    +B1158074527137N00557599EA0110601124
    +B1158094527128N00557604EA0110601123
    +B1158114527120N00557611EA0110301122
    +B1158134527111N00557617EA0110001119
    +B1158154527101N00557624EA0109701116
    +B1158174527092N00557633EA0109901115
    +B1158194527083N00557642EA0110101115
    +B1158214527075N00557649EA0110101116
    +B1158234527068N00557657EA0110201116
    +B1158254527059N00557664EA0110601118
    +B1158274527052N00557668EA0110801121
    +B1158294527047N00557673EA0110801122
    +B1158314527046N00557681EA0110901123
    +B1158334527052N00557688EA0110901124
    +B1158354527063N00557684EA0110801125
    +B1158374527072N00557669EA0110701124
    +B1158394527073N00557649EA0110801124
    +B1158414527068N00557633EA0110901125
    +B1158434527058N00557623EA0111001126
    +B1158454527048N00557620EA0111201128
    +B1158474527040N00557620EA0111201129
    +B1158494527034N00557626EA0111001128
    +B1158514527027N00557636EA0111101128
    +B1158534527019N00557644EA0111301129
    +B1158554527011N00557649EA0111501132
    +B1158574527003N00557652EA0111701134
    +B1158594526997N00557656EA0111901136
    +B1159014526995N00557663EA0111801137
    +B1159034526997N00557673EA0112001138
    +B1159054527005N00557680EA0112201139
    +B1159074527015N00557677EA0112301140
    +B1159094527023N00557666EA0112101141
    +B1159114527027N00557644EA0112201141
    +B1159134527024N00557625EA0112201142
    +B1159154527015N00557614EA0112301141
    +B1159174527004N00557612EA0112401143
    +B1159194526994N00557614EA0112701145
    +B1159214526984N00557615EA0112801147
    +B1159234526975N00557613EA0112901148
    +B1159254526966N00557612EA0112901149
    +B1159274526958N00557614EA0113001150
    +B1159294526951N00557617EA0112901150
    +B1159314526947N00557624EA0112801150
    +B1159334526944N00557634EA0112801149
    +B1159354526938N00557644EA0112801148
    +B1159374526932N00557652EA0112801148
    +B1159394526925N00557659EA0112701147
    +B1159414526918N00557667EA0112601146
    +B1159434526911N00557676EA0112401145
    +B1159454526904N00557683EA0112301144
    +B1159474526895N00557691EA0112201143
    +B1159494526887N00557698EA0112201143
    +B1159514526878N00557704EA0112101142
    +B1159534526869N00557710EA0112001141
    +B1159554526860N00557716EA0111801140
    +B1159574526851N00557722EA0111501137
    +B1159594526841N00557727EA0111101135
    +B1200014526832N00557731EA0110701131
    +B1200034526821N00557733EA0110401127
    +B1200054526810N00557737EA0110201124
    +B1200074526799N00557741EA0110001122
    +B1200094526789N00557742EA0109601119
    +B1200114526777N00557744EA0109301115
    +B1200134526765N00557746EA0109201113
    +B1200154526753N00557748EA0108901111
    +B1200174526743N00557753EA0108601108
    +B1200194526731N00557758EA0108301104
    +B1200214526720N00557763EA0108101102
    +B1200234526708N00557767EA0107601099
    +B1200254526696N00557770EA0107301095
    +B1200274526684N00557773EA0107001092
    +B1200294526673N00557776EA0106601088
    +B1200314526661N00557778EA0106001084
    +B1200334526647N00557782EA0105801080
    +B1200354526634N00557783EA0105501077
    +B1200374526621N00557783EA0105101073
    +B1200394526607N00557783EA0105501069
    +B1200414526593N00557778EA0106101067
    +B1200434526583N00557775EA0106301067
    +B1200454526574N00557779EA0106501068
    +B1200474526569N00557794EA0106801070
    +B1200494526575N00557802EA0107101073
    +B1200514526584N00557796EA0107501079
    +B1200534526587N00557782EA0107801085
    +B1200554526581N00557767EA0107901090
    +B1200574526569N00557760EA0108001094
    +B1200594526556N00557771EA0108201097
    +B1201014526553N00557786EA0108401100
    +B1201044526559N00557794EA0108901106
    +B1201064526566N00557793EA0109201109
    +B1201084526573N00557789EA0109601113
    +B1201104526580N00557782EA0110101117
    +B1201124526583N00557769EA0110201119
    +B1201144526577N00557754EA0110201121
    +B1201164526565N00557754EA0110301123
    +B1201184526556N00557770EA0110701125
    +B1201204526559N00557786EA0111201128
    +B1201224526567N00557787EA0111601132
    +B1201244526574N00557780EA0112001137
    +B1201264526579N00557769EA0112201140
    +B1201284526578N00557755EA0111901142
    +B1201304526566N00557744EA0111701142
    +B1201324526554N00557754EA0111801143
    +B1201344526551N00557772EA0112401144
    +B1201364526555N00557782EA0112901147
    +B1201384526563N00557783EA0113101150
    +B1201404526573N00557781EA0113401152
    +B1201424526583N00557779EA0113801156
    +B1201444526592N00557773EA0113801158
    +B1201464526595N00557756EA0113701160
    +B1201484526590N00557734EA0113401162
    +B1201504526576N00557727EA0113001160
    +B1201524526564N00557737EA0112901160
    +B1201544526561N00557751EA0112701159
    +B1201564526568N00557763EA0112701157
    +B1201584526578N00557763EA0112601156
    +B1202004526584N00557751EA0112601153
    +B1202024526586N00557736EA0112401150
    +B1202044526584N00557717EA0112101146
    +B1202064526577N00557700EA0111801143
    +B1202084526567N00557690EA0111301140
    +B1202104526553N00557686EA0110801135
    +B1202124526539N00557683EA0110901133
    +B1202144526526N00557681EA0111101132
    +B1202164526514N00557681EA0111301134
    +B1202184526502N00557680EA0111801136
    +B1202204526492N00557678EA0112301140
    +B1202224526484N00557678EA0112201143
    +B1202244526480N00557688EA0112001145
    +B1202264526486N00557700EA0112001147
    +B1202284526498N00557697EA0112301148
    +B1202304526508N00557693EA0112601150
    +B1202324526514N00557700EA0112601151
    +B1202344526511N00557715EA0112901152
    +B1202364526498N00557721EA0113101153
    +B1202384526487N00557710EA0113301154
    +B1202404526483N00557691EA0113501156
    +B1202424526490N00557677EA0113601158
    +B1202444526501N00557676EA0113901160
    +B1202464526506N00557684EA0114101161
    +B1202484526504N00557696EA0114201163
    +B1202504526493N00557706EA0114501165
    +B1202524526478N00557700EA0114701167
    +B1202544526470N00557685EA0114901169
    +B1202564526470N00557667EA0115201172
    +B1202584526477N00557654EA0115401176
    +B1203004526486N00557652EA0115401178
    +B1203024526494N00557661EA0115601179
    +B1203044526489N00557673EA0115801180
    +B1203064526477N00557678EA0116301182
    +B1203084526464N00557677EA0116501186
    +B1203104526453N00557669EA0116401187
    +B1203124526447N00557654EA0116201186
    +B1203144526451N00557632EA0116401186
    +B1203164526460N00557621EA0116601186
    +B1203184526470N00557620EA0116901187
    +B1203204526477N00557627EA0117001188
    +B1203224526476N00557638EA0117301191
    +B1203244526468N00557648EA0117901194
    +B1203264526460N00557654EA0118301198
    +B1203284526455N00557662EA0118301201
    +B1203304526457N00557672EA0118401203
    +B1203324526468N00557667EA0118701206
    +B1203344526473N00557651EA0118801209
    +B1203364526472N00557631EA0119001211
    +B1203384526466N00557615EA0119101212
    +B1203404526451N00557613EA0119601216
    +B1203424526441N00557621EA0120001220
    +B1203444526437N00557632EA0120301223
    +B1203464526437N00557641EA0120701226
    +B1203484526443N00557648EA0121001229
    +B1203504526452N00557641EA0121101233
    +B1203524526454N00557624EA0121301235
    +B1203544526448N00557603EA0121701238
    +B1203564526433N00557602EA0122001240
    +B1203584526423N00557612EA0122201242
    +B1204004526422N00557624EA0122701245
    +B1204024526429N00557628EA0123101249
    +B1204044526436N00557619EA0123401253
    +B1204064526439N00557604EA0123601256
    +B1204084526436N00557587EA0123801259
    +B1204104526428N00557572EA0123901261
    +B1204124526414N00557568EA0124101262
    +B1204144526403N00557576EA0124301265
    +B1204164526401N00557589EA0124501267
    +B1204184526406N00557598EA0124901269
    +B1204204526415N00557600EA0125301273
    +B1204224526423N00557597EA0125501275
    +B1204244526429N00557590EA0125601276
    +B1204264526430N00557573EA0125501278
    +B1204284526425N00557556EA0125501279
    +B1204304526414N00557551EA0125601279
    +B1204324526401N00557555EA0125901282
    +B1204344526393N00557566EA0126301285
    +B1204364526387N00557576EA0126701289
    +B1204384526380N00557586EA0127101293
    +B1204404526375N00557597EA0127501296
    +B1204424526377N00557606EA0127401299
    +B1204444526386N00557605EA0127301300
    +B1204464526391N00557590EA0127401301
    +B1204484526386N00557571EA0127701302
    +B1204504526376N00557558EA0128101305
    +B1204524526363N00557551EA0128401309
    +B1204544526351N00557552EA0128401311
    +B1204564526340N00557560EA0128401311
    +B1204584526333N00557570EA0128701312
    +B1205004526328N00557581EA0128601312
    +B1205024526325N00557593EA0128301311
    +B1205044526328N00557604EA0127801309
    +B1205064526336N00557601EA0127701306
    +B1205084526340N00557583EA0128201304
    +B1205104526335N00557567EA0128301304
    +B1205124526327N00557555EA0128301305
    +B1205144526318N00557547EA0128001303
    +B1205164526306N00557541EA0127801301
    +B1205184526293N00557541EA0127801300
    +B1205204526280N00557542EA0127701300
    +B1205224526267N00557542EA0127601299
    +B1205244526253N00557542EA0127401297
    +B1205264526241N00557540EA0127101295
    +B1205284526228N00557538EA0126701291
    +B1205304526215N00557537EA0126401288
    +B1205324526202N00557536EA0126301286
    +B1205344526190N00557536EA0126001284
    +B1205364526178N00557537EA0125601280
    +B1205384526165N00557537EA0125401277
    +B1205404526151N00557537EA0125201274
    +B1205424526139N00557538EA0125001272
    +B1205444526127N00557541EA0124901270
    +B1205464526114N00557542EA0124801268
    +B1205484526102N00557541EA0124401266
    +B1205504526091N00557543EA0123801263
    +B1205524526079N00557548EA0123401260
    +B1205544526066N00557555EA0123101257
    +B1205564526055N00557561EA0122601254
    +B1205584526044N00557567EA0121801249
    +B1206004526031N00557574EA0121301243
    +B1206024526017N00557581EA0120901238
    +B1206044526003N00557587EA0120401234
    +B1206064525991N00557596EA0119901228
    +B1206084525977N00557606EA0119501223
    +B1206104525964N00557615EA0119201219
    +B1206124525950N00557624EA0118901216
    +B1206144525937N00557633EA0118601212
    +B1206164525925N00557641EA0118401209
    +B1206184525912N00557648EA0118001206
    +B1206204525899N00557655EA0117701203
    +B1206224525887N00557661EA0117401199
    +B1206244525874N00557666EA0117101196
    +B1206264525861N00557671EA0117001193
    +B1206284525848N00557676EA0116901192
    +B1206304525838N00557681EA0116501189
    +B1206324525825N00557685EA0116101185
    +B1206344525812N00557687EA0115801183
    +B1206364525799N00557689EA0115501180
    +B1206384525788N00557693EA0115101176
    +B1206404525776N00557696EA0114701173
    +B1206424525764N00557700EA0114301168
    +B1206444525750N00557704EA0114201164
    +B1206464525737N00557704EA0114301161
    +B1206484525726N00557705EA0114101160
    +B1206504525716N00557707EA0113801157
    +B1206524525703N00557710EA0113701155
    +B1206544525691N00557713EA0113601155
    +B1206564525680N00557716EA0113401153
    +B1206584525668N00557718EA0113101150
    +B1207004525655N00557720EA0112701147
    +B1207024525641N00557721EA0112401144
    +B1207044525626N00557720EA0112401142
    +B1207064525612N00557719EA0112501141
    +B1207084525599N00557718EA0112701142
    +B1207104525587N00557716EA0112701143
    +B1207124525576N00557714EA0112501143
    +B1207144525564N00557713EA0112201142
    +B1207164525551N00557712EA0112001140
    +B1207184525538N00557710EA0111901139
    +B1207204525527N00557708EA0111601137
    +B1207224525515N00557707EA0111101135
    +B1207244525502N00557706EA0110801132
    +B1207264525490N00557706EA0110801130
    +B1207284525479N00557707EA0110901130
    +B1207304525469N00557707EA0111201131
    +B1207324525460N00557707EA0111101131
    +B1207344525451N00557709EA0110701129
    +B1207364525441N00557710EA0110301126
    +B1207384525430N00557711EA0110001122
    +B1207404525419N00557712EA0109501119
    +B1207424525407N00557712EA0108901114
    +B1207444525395N00557712EA0108701109
    +B1207464525383N00557709EA0108601107
    +B1207484525374N00557703EA0108601106
    +B1207504525364N00557696EA0108601105
    +B1207524525354N00557689EA0109301105
    +B1207544525346N00557684EA0109801107
    +B1207564525342N00557680EA0109901109
    +B1207584525335N00557682EA0109901111
    +B1208004525336N00557695EA0110001112
    +B1208024525349N00557702EA0110201114
    +B1208044525363N00557690EA0110601116
    +B1208064525364N00557670EA0110801120
    +B1208084525356N00557654EA0111201126
    +B1208104525347N00557651EA0111601132
    +B1208124525341N00557658EA0111801136
    +B1208144525339N00557670EA0112001140
    +B1208164525347N00557678EA0112101141
    +B1208184525362N00557669EA0112601144
    +B1208204525369N00557653EA0112601146
    +B1208224525369N00557633EA0112601147
    +B1208244525364N00557618EA0112801148
    +B1208264525354N00557616EA0113001149
    +B1208284525343N00557620EA0113701153
    +B1208304525335N00557622EA0114401159
    +B1208324525328N00557621EA0114801164
    +B1208344525324N00557628EA0114701168
    +B1208364525330N00557641EA0114801171
    +B1208384525343N00557637EA0115001173
    +B1208404525350N00557617EA0115401175
    +B1208424525347N00557600EA0115801179
    +B1208444525339N00557597EA0116001181
    +B1208464525330N00557606EA0116501186
    +B1208484525330N00557617EA0116501189
    +B1208504525340N00557624EA0116501192
    +B1208524525353N00557614EA0116701193
    +B1208544525359N00557596EA0117201195
    +B1208564525354N00557581EA0117601198
    +B1208584525344N00557576EA0117901201
    +B1209004525335N00557580EA0118201204
    +B1209024525333N00557594EA0118201206
    +B1209044525342N00557603EA0118201207
    +B1209064525355N00557598EA0118301207
    +B1209084525362N00557582EA0118301207
    +B1209104525359N00557563EA0118901209
    +B1209124525354N00557551EA0119701214
    +B1209144525350N00557541EA0119901219
    +B1209164525343N00557531EA0120101221
    +B1209184525330N00557530EA0120401224
    +B1209204525324N00557543EA0120601226
    +B1209224525330N00557556EA0121001228
    +B1209244525341N00557555EA0121401232
    +B1209264525349N00557542EA0121701236
    +B1209284525348N00557525EA0122101240
    +B1209304525338N00557514EA0122501244
    +B1209324525327N00557519EA0122701248
    +B1209344525324N00557533EA0122901251
    +B1209364525332N00557542EA0123001253
    +B1209384525343N00557538EA0123101255
    +B1209404525348N00557521EA0123501257
    +B1209424525346N00557502EA0124201262
    +B1209444525341N00557487EA0124701268
    +B1209464525331N00557481EA0125301274
    +B1209484525321N00557482EA0125601279
    +B1209504525315N00557492EA0125501282
    +B1209524525321N00557505EA0125501285
    +B1209544525331N00557499EA0125601286
    +B1209564525336N00557478EA0126201288
    +B1209584525333N00557460EA0126701292
    +B1210004525324N00557449EA0127201297
    +B1210024525316N00557440EA0127501300
    +B1210044525307N00557437EA0127501303
    +B1210064525299N00557448EA0127601304
    +B1210084525302N00557461EA0128001307
    +B1210104525312N00557457EA0128201309
    +B1210124525319N00557442EA0128801312
    +B1210144525319N00557425EA0129201316
    +B1210164525314N00557412EA0129401318
    +B1210184525305N00557405EA0129601320
    +B1210204525297N00557413EA0129701321
    +B1210224525298N00557426EA0129801322
    +B1210244525307N00557431EA0130201324
    +B1210264525316N00557421EA0130501326
    +B1210284525319N00557403EA0130701328
    +B1210304525312N00557388EA0130901331
    +B1210324525302N00557382EA0131101334
    +B1210344525294N00557385EA0131201336
    +B1210364525291N00557397EA0131301338
    +B1210384525297N00557407EA0131401339
    +B1210404525307N00557404EA0131501341
    +B1210424525313N00557390EA0131701343
    +B1210444525315N00557374EA0131801345
    +B1210464525313N00557360EA0132001347
    +B1210484525308N00557347EA0132101348
    +B1210504525300N00557339EA0132201349
    +B1210524525291N00557339EA0132301350
    +B1210544525284N00557349EA0132301351
    +B1210564525287N00557363EA0132501351
    +B1210584525296N00557362EA0132701352
    +B1211004525302N00557346EA0132801353
    +B1211024525300N00557329EA0132801354
    +B1211044525292N00557315EA0132901355
    +B1211064525281N00557309EA0132901355
    +B1211084525272N00557314EA0132701355
    +B1211104525271N00557329EA0132501353
    +B1211124525282N00557330EA0132401352
    +B1211144525291N00557317EA0132401352
    +B1211164525295N00557301EA0132401352
    +B1211184525296N00557282EA0132401352
    +B1211204525292N00557264EA0132401352
    +B1211224525284N00557251EA0132201351
    +B1211244525274N00557243EA0131801348
    +B1211264525261N00557238EA0131501345
    +B1211284525248N00557231EA0131401342
    +B1211304525235N00557224EA0131401341
    +B1211324525224N00557219EA0131401341
    +B1211344525214N00557211EA0131101339
    +B1211364525203N00557200EA0130901337
    +B1211384525192N00557188EA0131001336
    +B1211404525182N00557177EA0131001336
    +B1211424525173N00557168EA0131001337
    +B1211444525164N00557162EA0130901337
    +B1211464525155N00557154EA0130801335
    +B1211484525145N00557146EA0130601333
    +B1211504525133N00557137EA0130501332
    +B1211524525123N00557127EA0130401331
    +B1211544525112N00557115EA0130201330
    +B1211564525100N00557104EA0130101328
    +B1211584525088N00557094EA0129801327
    +B1212004525076N00557084EA0129601325
    +B1212024525063N00557074EA0129401323
    +B1212044525051N00557063EA0129101321
    +B1212064525040N00557054EA0128601317
    +B1212084525027N00557043EA0128001312
    +B1212104525015N00557031EA0127701308
    +B1212124525003N00557022EA0127401305
    +B1212144524991N00557015EA0127001300
    +B1212164524977N00557010EA0126501295
    +B1212184524964N00557007EA0126201291
    +B1212204524950N00557005EA0125701287
    +B1212224524936N00557004EA0125301282
    +B1212244524922N00557003EA0124901277
    +B1212264524911N00557000EA0124301272
    +B1212284524899N00557000EA0123801266
    +B1212304524886N00557001EA0123501260
    +B1212324524875N00557001EA0123501256
    +B1212344524865N00557000EA0123401253
    +B1212364524853N00557001EA0123401253
    +B1212384524841N00557002EA0123501254
    +B1212404524830N00557004EA0123501255
    +B1212424524817N00557006EA0123601256
    +B1212444524805N00557007EA0123901258
    +B1212464524794N00557009EA0124301261
    +B1212484524784N00557010EA0124601264
    +B1212504524777N00557019EA0124701267
    +B1212524524778N00557036EA0125101269
    +B1212544524787N00557044EA0125401272
    +B1212564524798N00557033EA0125601274
    +B1212584524797N00557012EA0125701278
    +B1213004524784N00557007EA0125901281
    +B1213024524775N00557019EA0126401285
    +B1213044524777N00557034EA0126801288
    +B1213064524786N00557040EA0127201293
    +B1213084524796N00557037EA0127501297
    +B1213104524801N00557024EA0127601301
    +B1213124524799N00557006EA0127601303
    +B1213144524787N00557000EA0127901305
    +B1213164524777N00557009EA0128201308
    +B1213184524778N00557022EA0128601310
    +B1213204524788N00557031EA0129101315
    +B1213224524798N00557029EA0129401319
    +B1213244524806N00557017EA0129801322
    +B1213264524805N00556999EA0130101326
    +B1213284524796N00556987EA0130301328
    +B1213304524784N00556992EA0130501330
    +B1213324524781N00557006EA0130901331
    +B1213344524791N00557018EA0131401337
    +B1213364524800N00557019EA0131901342
    +B1213384524807N00557012EA0132101345
    +B1213404524805N00556993EA0132201348
    +B1213424524793N00556982EA0132701352
    +B1213444524783N00556990EA0132901355
    +B1213464524785N00557006EA0133201358
    +B1213484524796N00557012EA0133601362
    +B1213504524805N00557001EA0134001364
    +B1213524524807N00556982EA0134501368
    +B1213544524801N00556963EA0134701372
    +B1213564524787N00556959EA0135001375
    +B1213584524779N00556972EA0135101377
    +B1214004524784N00556985EA0135301380
    +B1214024524795N00556986EA0135701382
    +B1214044524802N00556973EA0136201386
    +B1214064524801N00556956EA0136501390
    +B1214084524793N00556942EA0136801394
    +B1214104524782N00556939EA0136901396
    +B1214124524773N00556950EA0137001398
    +B1214144524776N00556964EA0137201400
    +B1214164524787N00556968EA0137701402
    +B1214184524798N00556963EA0138301407
    +B1214204524807N00556956EA0138701412
    +B1214224524814N00556946EA0138801415
    +B1214244524815N00556927EA0138701417
    +B1214264524807N00556909EA0139101419
    +B1214284524793N00556911EA0139401421
    +B1214304524787N00556921EA0139501423
    +B1214324524792N00556933EA0139801425
    +B1214344524802N00556933EA0140101428
    +B1214364524809N00556920EA0140201431
    +B1214384524811N00556903EA0140401431
    +B1214404524805N00556886EA0140601433
    +B1214424524793N00556881EA0140701435
    +B1214444524784N00556890EA0140901436
    +B1214464524783N00556903EA0141101439
    +B1214484524789N00556913EA0141301441
    +B1214504524800N00556915EA0141601443
    +B1214524524811N00556909EA0141901446
    +B1214544524818N00556896EA0141901448
    +B1214564524818N00556877EA0141701449
    +B1214584524810N00556862EA0141701449
    +B1215004524797N00556864EA0141901449
    +B1215024524792N00556876EA0142101450
    +B1215044524797N00556888EA0142301451
    +B1215064524808N00556891EA0142501453
    +B1215084524819N00556885EA0142601454
    +B1215104524826N00556876EA0142501454
    +B1215124524829N00556862EA0142201453
    +B1215144524827N00556845EA0141901451
    +B1215164524815N00556837EA0141701448
    +B1215184524803N00556841EA0141701447
    +B1215204524798N00556850EA0141801446
    +B1215224524798N00556860EA0141901447
    +B1215244524800N00556870EA0142001447
    +B1215264524803N00556881EA0142101449
    +B1215284524804N00556891EA0142101449
    +B1215304524806N00556898EA0142001448
    +B1215324524816N00556898EA0142201448
    +B1215344524824N00556885EA0142101448
    +B1215364524824N00556870EA0141701447
    +B1215384524818N00556855EA0141401444
    +B1215404524809N00556840EA0141501443
    +B1215424524803N00556823EA0141701443
    +B1215444524798N00556807EA0141801445
    +B1215464524793N00556789EA0141901446
    +B1215484524787N00556771EA0142201447
    +B1215504524783N00556755EA0142401450
    +B1215524524779N00556741EA0142201451
    +B1215544524773N00556725EA0141901451
    +B1215564524766N00556709EA0141801450
    +B1215584524759N00556693EA0141601448
    +B1216004524751N00556679EA0141101445
    +B1216024524744N00556664EA0140601440
    +B1216044524737N00556648EA0140101435
    +B1216064524731N00556632EA0139701431
    +B1216084524725N00556617EA0139201425
    +B1216104524717N00556601EA0138801420
    +B1216124524710N00556584EA0138501416
    +B1216144524703N00556567EA0138301413
    +B1216164524696N00556550EA0137901409
    +B1216184524690N00556532EA0137601405
    +B1216204524682N00556516EA0137501403
    +B1216224524676N00556502EA0137401402
    +B1216244524669N00556487EA0137401401
    +B1216264524663N00556471EA0137601401
    +B1216284524659N00556455EA0137601402
    +B1216304524654N00556440EA0137501401
    +B1216324524647N00556424EA0137401400
    +B1216344524640N00556407EA0137401400
    +B1216364524634N00556392EA0137301400
    +B1216384524628N00556380EA0137101399
    +B1216404524621N00556367EA0136701397
    +B1216424524613N00556354EA0136201393
    +B1216444524606N00556340EA0135801388
    +B1216464524600N00556325EA0135601385
    +B1216484524592N00556311EA0135501382
    +B1216504524585N00556296EA0135501381
    +B1216524524578N00556281EA0135801381
    +B1216544524572N00556267EA0136001382
    +B1216564524567N00556253EA0136101384
    +B1216584524563N00556238EA0136201385
    +B1217004524560N00556221EA0136501387
    +B1217024524558N00556205EA0137101391
    +B1217044524554N00556192EA0137501395
    +B1217064524545N00556190EA0137701399
    +B1217084524537N00556203EA0138101403
    +B1217104524538N00556217EA0138501407
    +B1217124524548N00556220EA0138701411
    +B1217144524558N00556205EA0138901414
    +B1217164524560N00556187EA0139201417
    +B1217184524558N00556170EA0139701422
    +B1217204524556N00556155EA0140301427
    +B1217224524549N00556144EA0140801432
    +B1217244524540N00556138EA0141101437
    +B1217264524531N00556148EA0141101440
    +B1217284524533N00556164EA0141401444
    +B1217304524545N00556164EA0141901447
    +B1217324524554N00556148EA0142501452
    +B1217344524553N00556133EA0142901456
    +B1217364524546N00556123EA0143201459
    +B1217384524536N00556128EA0143401463
    +B1217404524536N00556142EA0143601465
    +B1217424524546N00556150EA0144101467
    +B1217444524558N00556143EA0144501470
    +B1217464524563N00556129EA0144801473
    +B1217484524566N00556114EA0145101476
    +B1217504524565N00556102EA0145101478
    +B1217524524561N00556090EA0145201478
    +B1217544524551N00556082EA0145601480
    +B1217564524540N00556085EA0146201482
    +B1217584524531N00556091EA0146801487
    +B1218004524525N00556098EA0147101492
    +B1218024524526N00556112EA0147101496
    +B1218044524537N00556122EA0147401499
    +B1218064524549N00556114EA0147701502
    +B1218084524554N00556095EA0148101506
    +B1218104524548N00556080EA0148701510
    +B1218124524537N00556078EA0149201515
    +B1218144524529N00556088EA0149501519
    +B1218164524528N00556103EA0149801523
    +B1218184524536N00556113EA0150001526
    +B1218204524548N00556106EA0150201529
    +B1218224524555N00556088EA0150801532
    +B1218244524551N00556071EA0151301538
    +B1218264524543N00556059EA0152001545
    +B1218284524536N00556049EA0152701552
    +B1218304524528N00556043EA0153201558
    +B1218324524519N00556050EA0153501563
    +B1218344524518N00556064EA0153901568
    +B1218364524529N00556075EA0154401572
    +B1218384524540N00556064EA0154801576
    +B1218404524544N00556045EA0155501581
    +B1218424524540N00556030EA0156201589
    +B1218444524534N00556019EA0156801595
    +B1218464524526N00556013EA0156901600
    +B1218484524516N00556022EA0157101604
    +B1218504524517N00556039EA0157301608
    +B1218524524529N00556045EA0157701612
    +B1218544524544N00556037EA0158301615
    +B1218564524553N00556021EA0159101621
    +B1218584524554N00556004EA0159501627
    +B1219004524550N00555991EA0159701632
    +B1219024524542N00555987EA0159701634
    +B1219044524535N00555999EA0159801636
    +B1219064524540N00556016EA0160201638
    +B1219084524556N00556020EA0160701640
    +B1219104524573N00556015EA0161501646
    +B1219124524586N00556007EA0162301653
    +B1219144524592N00555995EA0162701658
    +B1219164524588N00555980EA0163001662
    +B1219184524579N00555980EA0163401666
    +B1219204524575N00555997EA0163701669
    +B1219224524587N00556013EA0164101672
    +B1219244524603N00556012EA0164801676
    +B1219264524613N00556004EA0165001681
    +B1219284524619N00555994EA0164901683
    +B1219304524618N00555980EA0164701683
    +B1219324524610N00555971EA0164701682
    +B1219344524597N00555974EA0165401684
    +B1219364524587N00555979EA0166301688
    +B1219384524581N00555981EA0166701694
    +B1219404524577N00555983EA0166701697
    +B1219424524576N00555998EA0167001698
    +B1219444524585N00556015EA0167701702
    +B1219464524599N00556014EA0168201705
    +B1219484524606N00556002EA0168301709
    +B1219504524601N00555987EA0168901714
    +B1219524524593N00555980EA0169601721
    +B1219544524586N00555987EA0169901726
    +B1219564524583N00556000EA0170101730
    +B1219584524586N00556015EA0170301733
    +B1220004524596N00556026EA0170901736
    +B1220024524608N00556023EA0171501742
    +B1220044524613N00556013EA0171901746
    +B1220064524610N00556001EA0172501753
    +B1220084524600N00556000EA0172801758
    +B1220104524591N00556012EA0173101762
    +B1220124524590N00556031EA0173701766
    +B1220144524600N00556043EA0174101770
    +B1220164524611N00556042EA0174501775
    +B1220184524617N00556031EA0174801778
    +B1220204524614N00556018EA0175201783
    +B1220224524603N00556016EA0175401786
    +B1220244524593N00556027EA0175901790
    +B1220264524586N00556043EA0176501795
    +B1220284524586N00556060EA0176801799
    +B1220304524592N00556075EA0177101802
    +B1220324524603N00556079EA0177201805
    +B1220344524611N00556068EA0177501808
    +B1220364524612N00556054EA0178001811
    +B1220384524603N00556047EA0178401815
    +B1220404524592N00556048EA0179001821
    +B1220424524582N00556052EA0179201825
    +B1220444524574N00556063EA0179201827
    +B1220464524572N00556083EA0179301829
    +B1220484524581N00556097EA0179501831
    +B1220504524593N00556093EA0179601832
    +B1220524524599N00556081EA0179901834
    +B1220544524598N00556068EA0180301837
    +B1220564524594N00556057EA0180601841
    +B1220584524588N00556048EA0180701843
    +B1221004524582N00556037EA0181001844
    +B1221024524578N00556025EA0181301848
    +B1221044524575N00556013EA0181801851
    +B1221064524573N00556000EA0182201856
    +B1221084524569N00555989EA0182401859
    +B1221104524562N00555981EA0182601861
    +B1221124524553N00555983EA0182901863
    +B1221144524548N00555994EA0183101865
    +B1221164524551N00556008EA0183101867
    +B1221184524560N00556018EA0183101867
    +B1221204524573N00556019EA0183101867
    +B1221224524584N00556013EA0183201867
    +B1221244524591N00556001EA0183301868
    +B1221264524596N00555987EA0183501869
    +B1221284524600N00555976EA0183701871
    +B1221304524601N00555965EA0183601871
    +B1221324524602N00555952EA0183501870
    +B1221344524608N00555936EA0183801870
    +B1221364524613N00555919EA0184201872
    +B1221384524615N00555907EA0184601876
    +B1221404524617N00555896EA0184601878
    +B1221424524620N00555881EA0184701879
    +B1221444524623N00555865EA0184801880
    +B1221464524626N00555850EA0184901881
    +B1221484524628N00555834EA0185101883
    +B1221504524631N00555818EA0185301886
    +B1221524524633N00555801EA0185401887
    +B1221544524634N00555785EA0185301888
    +B1221564524635N00555769EA0185201887
    +B1221584524634N00555752EA0185101886
    +B1222004524633N00555734EA0185001885
    +B1222024524633N00555715EA0185101885
    +B1222044524631N00555697EA0185301886
    +B1222064524629N00555683EA0185301887
    +B1222084524628N00555670EA0185101886
    +B1222104524625N00555654EA0185001885
    +B1222124524621N00555640EA0185201885
    +B1222144524617N00555627EA0185801887
    +B1222164524614N00555616EA0186301890
    +B1222184524611N00555605EA0186501894
    +B1222204524609N00555592EA0186601896
    +B1222224524605N00555575EA0186701897
    +B1222244524600N00555557EA0187301901
    +B1222264524594N00555541EA0187801906
    +B1222284524590N00555526EA0188001910
    +B1222304524584N00555510EA0187801912
    +B1222324524576N00555492EA0187801912
    +B1222344524567N00555472EA0188401916
    +B1222364524560N00555455EA0188701921
    +B1222384524554N00555442EA0188601923
    +B1222404524546N00555429EA0188501923
    +B1222424524537N00555416EA0188401922
    +B1222444524529N00555403EA0188301920
    +B1222464524521N00555390EA0188201919
    +B1222484524512N00555377EA0188201918
    +B1222504524504N00555366EA0188101918
    +B1222524524495N00555354EA0188101917
    +B1222544524485N00555342EA0188301917
    +B1222564524476N00555329EA0188601919
    +B1222584524466N00555316EA0189001921
    +B1223004524455N00555307EA0189601925
    +B1223024524444N00555308EA0190901930
    +B1223044524439N00555303EA0191601938
    +B1223064524434N00555296EA0192201945
    +B1223084524424N00555302EA0192901952
    +B1223104524423N00555317EA0193501958
    +B1223124524439N00555319EA0194301966
    +B1223144524451N00555307EA0195501976
    +B1223164524449N00555293EA0196001986
    +B1223184524441N00555283EA0196701994
    +B1223204524432N00555278EA0197302002
    +B1223224524424N00555290EA0197902009
    +B1223244524427N00555307EA0198602016
    +B1223264524440N00555311EA0199602023
    +B1223284524449N00555297EA0200302031
    +B1223304524449N00555283EA0200802038
    +B1223324524446N00555270EA0201102042
    +B1223344524436N00555262EA0201702047
    +B1223364524427N00555269EA0202402053
    +B1223384524426N00555283EA0203002060
    +B1223404524435N00555291EA0203602066
    +B1223424524448N00555286EA0204502074
    +B1223444524453N00555272EA0205102081
    +B1223464524451N00555256EA0205602088
    +B1223484524447N00555243EA0206102094
    +B1223504524440N00555235EA0206502099
    +B1223524524431N00555238EA0207302105
    +B1223544524427N00555252EA0207802110
    +B1223564524436N00555267EA0208502116
    +B1223584524449N00555264EA0209202124
    +B1224004524454N00555246EA0209602129
    +B1224024524452N00555229EA0210402137
    +B1224044524446N00555222EA0211002143
    +B1224064524437N00555223EA0211602149
    +B1224084524435N00555236EA0212002155
    +B1224104524440N00555251EA0212702160
    +B1224124524452N00555256EA0213402166
    +B1224144524464N00555244EA0213902171
    +B1224164524470N00555229EA0214402178
    +B1224184524467N00555215EA0214902184
    +B1224204524461N00555207EA0215402188
    +B1224224524453N00555208EA0215802193
    +B1224244524448N00555220EA0216102197
    +B1224264524455N00555235EA0216702201
    +B1224284524469N00555240EA0217402206
    +B1224304524483N00555234EA0218002213
    +B1224324524493N00555224EA0218402219
    +B1224344524497N00555211EA0218602223
    +B1224364524495N00555196EA0218802226
    +B1224384524487N00555188EA0219102228
    +B1224404524479N00555180EA0219902233
    +B1224424524473N00555171EA0220902239
    +B1224444524468N00555164EA0221402246
    +B1224464524461N00555155EA0221802251
    +B1224484524452N00555144EA0222102255
    +B1224504524444N00555136EA0222302259
    +B1224524524434N00555130EA0222502261
    +B1224544524423N00555123EA0222702263
    +B1224564524413N00555117EA0222702265
    +B1224584524403N00555113EA0222702266
    +B1225004524392N00555109EA0222902267
    +B1225024524382N00555103EA0223002268
    +B1225044524371N00555097EA0223202270
    +B1225064524361N00555091EA0223402271
    +B1225084524350N00555085EA0223502273
    +B1225104524340N00555078EA0223602274
    +B1225124524330N00555073EA0223702276
    +B1225144524320N00555066EA0223802277
    +B1225164524310N00555059EA0223902278
    +B1225184524299N00555052EA0224102279
    +B1225204524288N00555045EA0224302282
    +B1225224524279N00555037EA0224402283
    +B1225244524269N00555028EA0224502284
    +B1225264524258N00555018EA0224602285
    +B1225284524248N00555007EA0224702287
    +B1225304524238N00554995EA0224602287
    +B1225324524226N00554984EA0224302286
    +B1225344524215N00554974EA0224102284
    +B1225364524204N00554966EA0223602281
    +B1225384524193N00554958EA0223202277
    +B1225404524181N00554949EA0223002274
    +B1225424524170N00554940EA0222902271
    +B1225444524158N00554933EA0222602269
    +B1225464524148N00554925EA0222502267
    +B1225484524138N00554917EA0222402265
    +B1225504524128N00554909EA0222202263
    +B1225524524117N00554900EA0222302262
    +B1225544524107N00554892EA0222302262
    +B1225564524097N00554885EA0222502263
    +B1225584524087N00554876EA0222502263
    +B1226004524077N00554867EA0222502263
    +B1226024524066N00554858EA0222402263
    +B1226044524055N00554850EA0222402263
    +B1226064524044N00554841EA0222202262
    +B1226084524032N00554833EA0222202261
    +B1226104524022N00554826EA0222002260
    +B1226124524011N00554820EA0221602257
    +B1226144523999N00554813EA0221502255
    +B1226164523988N00554805EA0221502254
    +B1226184523978N00554799EA0221402253
    +B1226204523967N00554793EA0221202251
    +B1226224523955N00554786EA0221102249
    +B1226244523944N00554778EA0221402248
    +B1226264523936N00554775EA0221602249
    +B1226284523928N00554770EA0221802250
    +B1226304523919N00554762EA0222102253
    +B1226324523909N00554755EA0222502257
    +B1226344523901N00554750EA0223002261
    +B1226364523895N00554745EA0223102264
    +B1226384523887N00554739EA0223202266
    +B1226404523878N00554730EA0223502268
    +B1226424523870N00554722EA0223802272
    +B1226444523861N00554714EA0224002274
    +B1226464523852N00554706EA0224202277
    +B1226484523843N00554696EA0224502280
    +B1226504523836N00554684EA0224702283
    +B1226524523828N00554675EA0224902285
    +B1226544523819N00554666EA0224902286
    +B1226564523811N00554658EA0225002288
    +B1226584523803N00554651EA0225102288
    +B1227004523795N00554646EA0225002289
    +B1227024523786N00554643EA0224902288
    +B1227044523776N00554640EA0224902287
    +B1227064523765N00554636EA0225002288
    +B1227084523756N00554634EA0225102289
    +B1227104523746N00554635EA0224902288
    +B1227124523734N00554634EA0224802287
    +B1227144523722N00554633EA0224902287
    +B1227164523713N00554631EA0224902288
    +B1227184523703N00554626EA0224902287
    +B1227204523693N00554623EA0225102288
    +B1227224523684N00554621EA0225302290
    +B1227244523674N00554618EA0225202290
    +B1227264523663N00554616EA0225102289
    +B1227284523651N00554614EA0225002288
    +B1227304523639N00554609EA0224902287
    +B1227324523628N00554606EA0224902287
    +B1227344523616N00554602EA0224802286
    +B1227364523603N00554597EA0224802285
    +B1227384523589N00554594EA0225002286
    +B1227404523576N00554591EA0225002286
    +B1227424523562N00554587EA0225202287
    +B1227444523548N00554584EA0225302289
    +B1227464523536N00554581EA0225302289
    +B1227484523522N00554576EA0225302289
    +B1227504523509N00554569EA0225502290
    +B1227524523497N00554563EA0225702292
    +B1227544523485N00554556EA0225702293
    +B1227564523473N00554551EA0225702293
    +B1227584523460N00554546EA0225702293
    +B1228004523447N00554541EA0225802294
    +B1228024523434N00554535EA0225802294
    +B1228044523421N00554529EA0225802294
    +B1228064523405N00554524EA0225902295
    +B1228084523392N00554520EA0226002296
    +B1228104523379N00554516EA0226002296
    +B1228124523365N00554511EA0226202297
    +B1228144523352N00554507EA0226302299
    +B1228164523339N00554505EA0226402300
    +B1228184523327N00554502EA0226302301
    +B1228204523314N00554499EA0226102300
    +B1228224523300N00554496EA0226102300
    +B1228244523287N00554493EA0226002299
    +B1228264523274N00554489EA0225902298
    +B1228284523260N00554485EA0226002298
    +B1228304523246N00554482EA0225902298
    +B1228324523232N00554478EA0225902298
    +B1228344523219N00554476EA0225902297
    +B1228364523204N00554473EA0225902297
    +B1228384523191N00554468EA0225902297
    +B1228404523177N00554463EA0226002297
    +B1228424523164N00554459EA0225902297
    +B1228444523152N00554455EA0225802297
    +B1228464523139N00554450EA0225802296
    +B1228484523125N00554446EA0225802297
    +B1228504523112N00554442EA0225902297
    +B1228524523099N00554440EA0225802297
    +B1228544523085N00554437EA0225802296
    +B1228564523072N00554435EA0225802297
    +B1228584523059N00554432EA0225902297
    +B1229004523046N00554430EA0225802297
    +B1229024523033N00554429EA0225702296
    +B1229044523020N00554425EA0225702295
    +B1229064523007N00554421EA0225902296
    +B1229084522995N00554418EA0226302298
    +B1229104522983N00554416EA0226502300
    +B1229124522972N00554413EA0226502302
    +B1229144522959N00554408EA0226502302
    +B1229164522945N00554404EA0226602303
    +B1229184522932N00554402EA0226702304
    +B1229204522918N00554398EA0226702305
    +B1229224522904N00554395EA0226702305
    +B1229244522893N00554392EA0226402304
    +B1229264522880N00554386EA0226202302
    +B1229284522867N00554380EA0226602302
    +B1229304522856N00554375EA0226902303
    +B1229324522845N00554371EA0226902303
    +B1229344522830N00554366EA0227002304
    +B1229364522816N00554361EA0227202306
    +B1229384522802N00554357EA0227302308
    +B1229404522788N00554356EA0227302309
    +B1229424522774N00554356EA0227302309
    +B1229444522761N00554354EA0227202309
    +B1229464522748N00554353EA0226802308
    +B1229484522733N00554350EA0226402305
    +B1229504522719N00554345EA0226202303
    +B1229524522706N00554339EA0225902300
    +B1229544522694N00554331EA0225702297
    +B1229564522681N00554324EA0225502294
    +B1229584522667N00554317EA0225302292
    +B1230004522654N00554310EA0225002290
    +B1230024522640N00554305EA0224602286
    +B1230044522626N00554299EA0224302283
    +B1230064522613N00554293EA0224202280
    +B1230084522599N00554287EA0224202279
    +B1230104522586N00554284EA0224302279
    +B1230124522573N00554281EA0224302279
    +B1230144522561N00554275EA0224202278
    +B1230164522550N00554267EA0224002277
    +B1230184522537N00554260EA0223802275
    +B1230204522524N00554255EA0223602274
    +B1230224522511N00554253EA0223302272
    +B1230244522498N00554249EA0223002269
    +B1230264522484N00554246EA0222802266
    +B1230284522470N00554243EA0222602264
    +B1230304522457N00554238EA0222602263
    +B1230324522445N00554234EA0222402261
    +B1230344522432N00554230EA0222102259
    +B1230364522419N00554226EA0221902257
    +B1230384522405N00554221EA0221802255
    +B1230404522392N00554217EA0221702254
    +B1230424522380N00554212EA0221602253
    +B1230444522366N00554207EA0221502252
    +B1230464522353N00554202EA0221502251
    +B1230484522341N00554197EA0221502251
    +B1230504522329N00554192EA0221302250
    +B1230524522316N00554187EA0221202248
    +B1230544522303N00554182EA0221102248
    +B1230564522291N00554177EA0220902246
    +B1230584522278N00554174EA0220702245
    +B1231004522266N00554171EA0220702243
    +B1231024522253N00554166EA0220702243
    +B1231044522240N00554162EA0220602243
    +B1231064522228N00554158EA0220602242
    +B1231084522216N00554154EA0220702242
    +B1231104522204N00554151EA0220802244
    +B1231124522192N00554148EA0220702244
    +B1231144522180N00554144EA0220402243
    +B1231164522166N00554140EA0220302241
    +B1231184522153N00554137EA0220302241
    +B1231204522141N00554132EA0220402241
    +B1231224522129N00554126EA0220502242
    +B1231244522117N00554122EA0220502242
    +B1231264522105N00554118EA0220402241
    +B1231284522092N00554114EA0220402241
    +B1231304522079N00554110EA0220602241
    +B1231324522067N00554106EA0221102243
    +B1231344522058N00554100EA0221202245
    +B1231364522050N00554091EA0221102245
    +B1231384522038N00554084EA0221202245
    +B1231404522027N00554077EA0221402247
    +B1231424522018N00554071EA0221302248
    +B1231444522010N00554065EA0221002248
    +B1231464522000N00554057EA0220602246
    +B1231484521988N00554050EA0220302244
    +B1231504521976N00554043EA0220202242
    +B1231524521965N00554035EA0220102240
    +B1231544521954N00554026EA0220102239
    +B1231564521943N00554018EA0220402239
    +B1231584521934N00554012EA0220802241
    +B1232004521926N00554008EA0221202244
    +B1232024521917N00554002EA0221502247
    +B1232044521908N00553995EA0221702250
    +B1232064521899N00553989EA0222202254
    +B1232084521891N00553984EA0222502258
    +B1232104521883N00553979EA0222802261
    +B1232124521873N00553975EA0223102264
    +B1232144521862N00553971EA0223502268
    +B1232164521851N00553965EA0223902273
    +B1232184521841N00553957EA0224402277
    +B1232204521832N00553952EA0224702281
    +B1232224521823N00553947EA0224902284
    +B1232244521813N00553939EA0225102287
    +B1232264521802N00553933EA0225402289
    +B1232284521792N00553926EA0226002293
    +B1232304521782N00553916EA0226402297
    +B1232324521772N00553909EA0226902302
    +B1232344521761N00553903EA0227502307
    +B1232364521753N00553894EA0228302314
    +B1232384521746N00553883EA0229302321
    +B1232404521740N00553873EA0230302331
    +B1232424521732N00553865EA0231102340
    +B1232444521724N00553858EA0231902349
    +B1232464521716N00553850EA0232702358
    +B1232484521707N00553844EA0233302366
    +B1232504521698N00553836EA0233702372
    +B1232524521689N00553829EA0234102378
    +B1232544521679N00553821EA0234502382
    +B1232564521669N00553811EA0235302388
    +B1232584521661N00553800EA0236002395
    +B1233004521653N00553790EA0236102400
    +B1233024521643N00553780EA0236302403
    +B1233044521634N00553771EA0236602407
    +B1233064521626N00553763EA0236502409
    +B1233084521617N00553753EA0236502409
    +B1233104521607N00553741EA0237102411
    +B1233124521599N00553731EA0237702415
    +B1233144521592N00553724EA0237902418
    +B1233164521583N00553716EA0238202420
    +B1233184521573N00553708EA0238802424
    +B1233204521563N00553698EA0239302429
    +B1233224521554N00553688EA0239602433
    +B1233244521543N00553678EA0240002437
    +B1233264521534N00553667EA0240202440
    +B1233284521524N00553655EA0240302443
    +B1233304521514N00553641EA0240902446
    +B1233324521509N00553623EA0241502450
    +B1233344521503N00553609EA0241802454
    +B1233364521495N00553597EA0242202458
    +B1233384521490N00553582EA0242602462
    +B1233404521487N00553567EA0242702465
    +B1233424521483N00553550EA0242802467
    +B1233444521475N00553535EA0242902469
    +B1233464521469N00553520EA0243002470
    +B1233484521464N00553502EA0243102471
    +B1233504521458N00553486EA0243302473
    +B1233524521450N00553471EA0243502475
    +B1233544521443N00553457EA0243402476
    +B1233564521433N00553444EA0243102476
    +B1233584521421N00553430EA0243002475
    +B1234004521408N00553416EA0243402475
    +B1234024521399N00553404EA0243402477
    +B1234044521391N00553393EA0243002476
    +B1234064521382N00553380EA0243002475
    +B1234084521372N00553369EA0243102476
    +B1234104521362N00553364EA0242902475
    +B1234124521349N00553360EA0243202474
    +B1234144521336N00553360EA0243502476
    +B1234164521325N00553365EA0243502477
    +B1234184521314N00553369EA0243102477
    +B1234204521301N00553369EA0242802474
    +B1234224521289N00553364EA0242402470
    +B1234244521279N00553359EA0242002466
    +B1234264521266N00553355EA0241602461
    +B1234284521254N00553350EA0241502458
    +B1234304521242N00553345EA0241502457
    +B1234324521231N00553341EA0241302456
    +B1234344521221N00553336EA0240802453
    +B1234364521211N00553330EA0240202449
    +B1234384521199N00553324EA0239802444
    +B1234404521189N00553320EA0239302440
    +B1234424521179N00553318EA0238602434
    +B1234444521168N00553316EA0238002426
    +B1234464521156N00553312EA0237602420
    +B1234484521144N00553306EA0237502416
    +B1234504521135N00553300EA0237002411
    +B1234524521125N00553292EA0236402405
    +B1234544521114N00553284EA0236102400
    +B1234564521102N00553281EA0235802396
    +B1234584521092N00553278EA0235502393
    +B1235004521082N00553273EA0235002389
    +B1235024521071N00553267EA0234702384
    +B1235044521060N00553263EA0234402382
    +B1235064521050N00553261EA0234002379
    +B1235084521040N00553260EA0233402374
    +B1235104521028N00553257EA0233002369
    +B1235124521017N00553252EA0232702366
    +B1235144521009N00553247EA0232302362
    +B1235164520999N00553241EA0231802357
    +B1235184520989N00553234EA0231502353
    +B1235204520978N00553229EA0231502350
    +B1235224520969N00553224EA0231302349
    +B1235244520960N00553219EA0231002345
    +B1235264520948N00553213EA0230802343
    +B1235284520938N00553210EA0230802343
    +B1235304520928N00553206EA0230602341
    +B1235324520918N00553201EA0230502340
    +B1235344520908N00553196EA0230302339
    +B1235364520899N00553191EA0229902337
    +B1235384520890N00553184EA0229502333
    +B1235404520879N00553178EA0229102329
    +B1235424520869N00553173EA0228702326
    +B1235444520861N00553167EA0228002322
    +B1235464520852N00553161EA0227202315
    +B1235484520841N00553155EA0226802310
    +B1235504520832N00553148EA0226602307
    +B1235524520824N00553143EA0226002301
    +B1235544520814N00553135EA0225702297
    +B1235564520804N00553130EA0225402293
    +B1235584520795N00553127EA0225202290
    +B1236004520786N00553120EA0224902286
    +B1236024520777N00553113EA0225102285
    +B1236044520769N00553106EA0225102284
    +B1236064520760N00553099EA0225102284
    +B1236084520749N00553091EA0225102285
    +B1236104520740N00553083EA0225302286
    +B1236124520731N00553075EA0225102286
    +B1236144520721N00553067EA0224802285
    +B1236164520712N00553058EA0224602283
    +B1236184520704N00553050EA0224302280
    +B1236204520695N00553041EA0224202279
    +B1236224520686N00553034EA0224302279
    +B1236244520678N00553027EA0224202278
    +B1236264520670N00553020EA0223802276
    +B1236284520659N00553014EA0223702274
    +B1236304520648N00553007EA0223902274
    +B1236324520639N00552999EA0224002275
    +B1236344520629N00552991EA0224202277
    +B1236364520619N00552983EA0224602279
    +B1236384520611N00552975EA0224902282
    +B1236404520603N00552969EA0225002284
    +B1236424520594N00552965EA0224602285
    +B1236444520585N00552961EA0224102283
    +B1236464520576N00552956EA0223702279
    +B1236484520567N00552950EA0223102274
    +B1236504520555N00552944EA0222802269
    +B1236524520543N00552936EA0223002265
    +B1236544520534N00552927EA0223202264
    +B1236564520525N00552921EA0223402264
    +B1236584520515N00552915EA0223302265
    +B1237004520503N00552909EA0223202264
    +B1237024520491N00552904EA0223302265
    +B1237044520480N00552898EA0223302266
    +B1237064520470N00552892EA0223302266
    +B1237084520459N00552885EA0223502267
    +B1237104520448N00552877EA0223902269
    +B1237124520440N00552869EA0224202273
    +B1237144520433N00552862EA0224002275
    +B1237164520424N00552856EA0223702276
    +B1237184520415N00552850EA0223502274
    +B1237204520405N00552842EA0223502274
    +B1237224520396N00552834EA0223602275
    +B1237244520387N00552825EA0223902276
    +B1237264520377N00552816EA0224402278
    +B1237284520369N00552807EA0224802282
    +B1237304520362N00552796EA0225202286
    +B1237324520354N00552783EA0225902291
    +B1237344520344N00552774EA0226702297
    +B1237364520336N00552768EA0227002303
    +B1237384520328N00552765EA0227402307
    +B1237404520319N00552759EA0228002312
    +B1237424520310N00552752EA0228702319
    +B1237444520301N00552748EA0229202325
    +B1237464520292N00552743EA0229902331
    +B1237484520283N00552740EA0230402338
    +B1237504520275N00552736EA0230802343
    +B1237524520265N00552731EA0231202347
    +B1237544520253N00552725EA0231802353
    +B1237564520243N00552720EA0232202359
    +B1237584520233N00552719EA0232502363
    +B1238004520223N00552717EA0232802366
    +B1238024520214N00552711EA0233102369
    +B1238044520205N00552704EA0233402372
    +B1238064520197N00552694EA0233602374
    +B1238084520189N00552684EA0233902378
    +B1238104520180N00552675EA0234102380
    +B1238124520170N00552668EA0234102382
    +B1238144520160N00552659EA0234002382
    +B1238164520147N00552650EA0234102382
    +B1238184520135N00552642EA0234602383
    +B1238204520123N00552636EA0235002386
    +B1238224520112N00552630EA0235302389
    +B1238244520101N00552621EA0236002394
    +B1238264520091N00552613EA0236702399
    +B1238284520081N00552606EA0237302406
    +B1238304520072N00552599EA0237802411
    +B1238324520063N00552593EA0238102416
    +B1238344520054N00552587EA0238002419
    +B1238364520045N00552581EA0237702421
    +B1238384520034N00552573EA0237602421
    +B1238404520025N00552569EA0237502420
    +B1238424520018N00552565EA0237202418
    +B1238444520009N00552560EA0237002415
    +B1238464519998N00552551EA0237002413
    +B1238484519989N00552542EA0237302413
    +B1238504519982N00552534EA0237402413
    +B1238524519974N00552527EA0237502413
    +B1238544519965N00552519EA0237702414
    +B1238564519957N00552512EA0237902416
    +B1238584519949N00552507EA0237902418
    +B1239004519939N00552503EA0237802417
    +B1239024519928N00552496EA0238702419
    +B1239044519920N00552489EA0239802423
    +B1239064519912N00552484EA0240302430
    +B1239084519903N00552480EA0240502433
    +B1239104519893N00552475EA0240502436
    +B1239124519882N00552469EA0240402437
    +B1239144519873N00552462EA0240202437
    +B1239164519863N00552455EA0239902436
    +B1239184519855N00552446EA0239702433
    +B1239204519845N00552437EA0239602432
    +B1239224519835N00552429EA0239602432
    +B1239244519825N00552422EA0239402431
    +B1239264519815N00552413EA0239102429
    +B1239284519804N00552404EA0238802427
    +B1239304519793N00552395EA0238602425
    +B1239324519785N00552387EA0238002422
    +B1239344519775N00552378EA0237302418
    +B1239364519763N00552369EA0237002412
    +B1239384519752N00552360EA0236702409
    +B1239404519742N00552352EA0236302405
    +B1239424519733N00552345EA0235802400
    +B1239444519722N00552337EA0235402396
    +B1239464519712N00552328EA0235302393
    +B1239484519703N00552320EA0235402391
    +B1239504519693N00552313EA0235202390
    +B1239524519680N00552307EA0234902387
    +B1239544519666N00552300EA0234702385
    +B1239564519654N00552293EA0234502384
    +B1239584519644N00552285EA0234502382
    +B1240004519632N00552275EA0234802382
    +B1240024519621N00552268EA0235002383
    +B1240044519610N00552264EA0234902383
    +B1240064519598N00552257EA0234502381
    +B1240084519586N00552247EA0234302380
    +B1240104519574N00552239EA0234002377
    +B1240124519562N00552233EA0233502374
    +B1240144519549N00552226EA0233002369
    +B1240164519537N00552218EA0232502364
    +B1240184519524N00552208EA0232102359
    +B1240204519513N00552199EA0231802356
    +B1240224519504N00552191EA0231502353
    +B1240244519493N00552183EA0231002348
    +B1240264519481N00552174EA0230502343
    +B1240284519469N00552164EA0230202339
    +B1240304519459N00552154EA0230302337
    +B1240324519449N00552146EA0230502335
    +B1240344519440N00552139EA0230202335
    +B1240364519431N00552134EA0229802332
    +B1240384519420N00552126EA0229402329
    +B1240404519409N00552117EA0229302328
    +B1240424519399N00552109EA0229002325
    +B1240444519388N00552100EA0228802323
    +B1240464519377N00552090EA0228902322
    +B1240484519367N00552081EA0228802322
    +B1240504519357N00552071EA0228402319
    +B1240524519345N00552059EA0228102317
    +B1240544519334N00552050EA0228202317
    +B1240564519325N00552040EA0228202317
    +B1240584519317N00552031EA0228002316
    +B1241004519307N00552022EA0227802314
    +B1241024519297N00552013EA0227702313
    +B1241044519288N00552005EA0227502312
    +B1241064519278N00551997EA0227102309
    +B1241084519268N00551989EA0226902306
    +B1241104519258N00551982EA0226602304
    +B1241124519248N00551975EA0226402301
    +B1241144519237N00551967EA0226102299
    +B1241164519228N00551959EA0225702296
    +B1241184519218N00551952EA0225202292
    +B1241204519207N00551943EA0224702287
    +B1241224519196N00551933EA0224602285
    +B1241244519186N00551927EA0224402283
    +B1241264519176N00551922EA0224002280
    +B1241284519166N00551915EA0223502275
    +B1241304519154N00551910EA0223302271
    +B1241324519143N00551904EA0223002269
    +B1241344519134N00551897EA0222702265
    +B1241364519124N00551889EA0222402262
    +B1241384519114N00551880EA0222402259
    +B1241404519105N00551872EA0222302258
    +B1241424519095N00551863EA0222202257
    +B1241444519086N00551853EA0222302256
    +B1241464519076N00551844EA0222402257
    +B1241484519069N00551835EA0222502258
    +B1241504519060N00551826EA0222502258
    +B1241524519051N00551815EA0222802260
    +B1241544519043N00551804EA0223402263
    +B1241564519036N00551794EA0223602267
    +B1241584519028N00551786EA0223502269
    +B1242004519020N00551777EA0223502270
    +B1242024519011N00551768EA0223702271
    +B1242044519002N00551759EA0224002274
    +B1242064518993N00551749EA0224102276
    +B1242084518984N00551740EA0224002277
    +B1242104518977N00551732EA0223702277
    +B1242124518968N00551724EA0223402275
    +B1242144518959N00551716EA0223202272
    +B1242164518950N00551710EA0223102271
    +B1242184518941N00551702EA0223102270
    +B1242204518933N00551691EA0223202270
    +B1242224518925N00551684EA0223202270
    +B1242244518918N00551675EA0223002268
    +B1242264518909N00551665EA0223102268
    +B1242284518898N00551654EA0223602268
    +B1242304518890N00551644EA0224202271
    +B1242324518882N00551634EA0224902275
    +B1242344518873N00551624EA0225702282
    +B1242364518864N00551614EA0226302291
    +B1242384518854N00551606EA0226802298
    +B1242404518845N00551598EA0227002303
    +B1242424518836N00551588EA0227102306
    +B1242444518824N00551579EA0227402310
    +B1242464518812N00551570EA0228002315
    +B1242484518804N00551559EA0228802320
    +B1242504518798N00551549EA0229402327
    +B1242524518792N00551538EA0229802331
    +B1242544518784N00551527EA0230302337
    +B1242564518776N00551518EA0230502342
    +B1242584518765N00551509EA0230602345
    +B1243004518754N00551499EA0230802348
    +B1243024518744N00551492EA0230502350
    +B1243044518735N00551487EA0229902351
    +B1243064518723N00551476EA0230102351
    +B1243084518714N00551464EA0230702353
    +B1243104518708N00551455EA0230802354
    +B1243124518701N00551445EA0231202356
    +B1243144518695N00551434EA0231702359
    +B1243164518690N00551421EA0232502364
    +B1243184518685N00551408EA0233202370
    +B1243204518679N00551395EA0233902377
    +B1243224518671N00551388EA0234002383
    +B1243244518658N00551394EA0234102386
    +B1243264518651N00551412EA0234502389
    +B1243284518653N00551430EA0235302393
    +B1243304518665N00551435EA0236002398
    +B1243324518676N00551424EA0236802405
    +B1243344518679N00551408EA0237402412
    +B1243364518672N00551395EA0237502416
    +B1243384518659N00551396EA0237402418
    +B1243404518648N00551414EA0237302420
    +B1243424518649N00551434EA0237702421
    +B1243444518662N00551444EA0238502425
    +B1243464518674N00551444EA0239302432
    +B1243484518682N00551443EA0239702437
    +B1243504518691N00551440EA0240102441
    +B1243524518698N00551426EA0240402444
    +B1243544518700N00551409EA0240802447
    +B1243564518696N00551396EA0241002451
    +B1243584518690N00551387EA0241202453
    +B1244004518683N00551376EA0241102454
    +B1244024518676N00551365EA0241202453
    +B1244044518668N00551354EA0241402454
    +B1244064518663N00551343EA0241402456
    +B1244084518657N00551331EA0241402456
    +B1244104518652N00551321EA0241102455
    +B1244124518648N00551309EA0240602452
    +B1244144518641N00551297EA0240302449
    +B1244164518635N00551283EA0240202446
    +B1244184518630N00551270EA0240002445
    +B1244204518624N00551259EA0239802442
    +B1244224518619N00551247EA0239502440
    +B1244244518614N00551235EA0239302437
    +B1244264518608N00551222EA0239102434
    +B1244284518603N00551211EA0238802432
    +B1244304518597N00551199EA0238602429
    +B1244324518592N00551185EA0238502427
    +B1244344518588N00551171EA0238402426
    +B1244364518583N00551160EA0238102424
    +B1244384518577N00551148EA0237702420
    +B1244404518571N00551135EA0237402417
    +B1244424518565N00551123EA0237302415
    +B1244444518559N00551110EA0237202413
    +B1244464518553N00551096EA0237102412
    +B1244484518547N00551083EA0237002411
    +B1244504518541N00551071EA0236702409
    +B1244524518536N00551060EA0236402406
    +B1244544518531N00551048EA0236002403
    +B1244564518526N00551036EA0235602399
    +B1244584518522N00551023EA0235202395
    +B1245004518518N00551009EA0234702390
    +B1245024518514N00550995EA0234302386
    +B1245044518509N00550981EA0233902382
    +B1245064518505N00550967EA0233502378
    +B1245084518501N00550953EA0233102373
    +B1245104518496N00550940EA0232802369
    +B1245124518490N00550927EA0232402365
    +B1245144518484N00550917EA0232002362
    +B1245164518477N00550906EA0231502357
    +B1245184518472N00550893EA0231302354
    +B1245204518467N00550881EA0230902351
    +B1245224518462N00550870EA0230502347
    +B1245244518457N00550857EA0230202343
    +B1245264518451N00550845EA0229902340
    +B1245284518446N00550833EA0229502336
    +B1245304518441N00550820EA0229402334
    +B1245324518435N00550809EA0229102332
    +B1245344518430N00550799EA0228802329
    +B1245364518425N00550788EA0228402325
    +B1245384518418N00550776EA0228102322
    +B1245404518412N00550765EA0227802318
    +B1245424518406N00550756EA0227402315
    +B1245444518399N00550746EA0227002311
    +B1245464518393N00550735EA0226602307
    +B1245484518386N00550723EA0226302304
    +B1245504518380N00550713EA0226102301
    +B1245524518374N00550702EA0225802298
    +B1245544518368N00550692EA0225702296
    +B1245564518362N00550684EA0225402294
    +B1245584518356N00550676EA0225002291
    +B1246004518350N00550665EA0224502287
    +B1246024518343N00550656EA0224202283
    +B1246044518336N00550647EA0223902281
    +B1246064518330N00550638EA0223502277
    +B1246084518324N00550628EA0223002272
    +B1246104518318N00550617EA0222502267
    +B1246124518313N00550605EA0222102263
    +B1246144518307N00550595EA0221602258
    +B1246164518300N00550584EA0221202253
    +B1246184518293N00550570EA0221002249
    +B1246204518288N00550556EA0221002246
    +B1246224518283N00550543EA0220902246
    +B1246244518278N00550529EA0220802244
    +B1246264518273N00550513EA0220702242
    +B1246284518269N00550498EA0220602242
    +B1246304518265N00550482EA0220502241
    +B1246324518261N00550466EA0220502241
    +B1246344518257N00550451EA0220602241
    +B1246364518253N00550437EA0220602241
    +B1246384518249N00550422EA0220602242
    +B1246404518245N00550408EA0220402242
    +B1246424518240N00550395EA0220502242
    +B1246444518236N00550381EA0220502242
    +B1246464518234N00550366EA0220402242
    +B1246484518232N00550353EA0220302241
    +B1246504518229N00550339EA0220202240
    +B1246524518226N00550324EA0220302240
    +B1246544518222N00550311EA0220302241
    +B1246564518219N00550300EA0220202241
    +B1246584518214N00550288EA0220102239
    +B1247004518210N00550277EA0219902238
    +B1247024518205N00550265EA0219902237
    +B1247044518200N00550255EA0219702236
    +B1247064518196N00550245EA0219502234
    +B1247084518190N00550235EA0219102232
    +B1247104518182N00550226EA0219002230
    +B1247124518174N00550218EA0218902229
    +B1247144518168N00550211EA0218702227
    +B1247164518161N00550203EA0218502224
    +B1247184518154N00550193EA0218202221
    +B1247204518147N00550184EA0218102219
    +B1247224518139N00550175EA0218102218
    +B1247244518132N00550165EA0218102218
    +B1247264518127N00550154EA0218102219
    +B1247284518123N00550146EA0218002219
    +B1247304518117N00550137EA0217802217
    +B1247324518112N00550128EA0217702215
    +B1247344518105N00550119EA0217502213
    +B1247364518099N00550108EA0217302211
    +B1247384518094N00550098EA0217102209
    +B1247404518088N00550088EA0216902207
    +B1247424518082N00550078EA0216802205
    +B1247444518075N00550067EA0217202204
    +B1247464518068N00550059EA0217502205
    +B1247484518063N00550051EA0217702209
    +B1247504518057N00550043EA0217702211
    +B1247524518052N00550036EA0217402211
    +B1247544518045N00550027EA0217002209
    +B1247564518037N00550017EA0216902207
    +B1247584518029N00550007EA0216702206
    +B1248004518021N00549997EA0216602204
    +B1248024518014N00549989EA0216502203
    +B1248044518007N00549986EA0216402201
    +B1248064517998N00549984EA0216802201
    +B1248084517991N00549981EA0217302202
    +B1248104517985N00549979EA0218002205
    +B1248124517979N00549978EA0218702210
    +B1248144517974N00549976EA0218902215
    +B1248164517968N00549972EA0219102218
    +B1248184517959N00549968EA0219502222
    +B1248204517951N00549962EA0220102228
    +B1248224517943N00549959EA0220102233
    +B1248244517933N00549963EA0219802236
    +B1248264517923N00549965EA0219302237
    +B1248284517912N00549962EA0219302235
    +B1248304517905N00549957EA0219002233
    +B1248324517899N00549953EA0218602231
    +B1248344517890N00549948EA0218102226
    +B1248364517880N00549943EA0217902223
    +B1248384517870N00549939EA0217702220
    +B1248404517861N00549933EA0217402217
    +B1248424517853N00549925EA0217102213
    +B1248444517845N00549916EA0217002211
    +B1248464517837N00549907EA0216702209
    +B1248484517829N00549898EA0216402206
    +B1248504517820N00549891EA0216002203
    +B1248524517810N00549883EA0215802199
    +B1248544517801N00549876EA0215402195
    +B1248564517790N00549873EA0214902191
    +B1248584517780N00549868EA0214402186
    +B1249004517769N00549861EA0214002181
    +B1249024517759N00549855EA0213702178
    +B1249044517748N00549850EA0213402174
    +B1249064517738N00549844EA0213102171
    +B1249084517728N00549837EA0212802168
    +B1249104517720N00549831EA0212202164
    +B1249124517711N00549825EA0211502158
    +B1249144517700N00549819EA0210902152
    +B1249164517690N00549813EA0210502147
    +B1249184517681N00549807EA0210102143
    +B1249204517672N00549803EA0209702138
    +B1249224517663N00549797EA0209102133
    +B1249244517655N00549790EA0208602127
    +B1249264517645N00549783EA0208102122
    +B1249284517636N00549776EA0207702117
    +B1249304517626N00549770EA0207202113
    +B1249324517617N00549764EA0206702107
    +B1249344517607N00549756EA0206402103
    +B1249364517597N00549749EA0206002099
    +B1249384517589N00549740EA0205602095
    +B1249404517580N00549731EA0205102090
    +B1249424517569N00549722EA0204902086
    +B1249444517559N00549712EA0204802084
    +B1249464517550N00549702EA0204702083
    +B1249484517541N00549694EA0204602082
    +B1249504517531N00549685EA0204602081
    +B1249524517521N00549677EA0204502080
    +B1249544517511N00549670EA0204402080
    +B1249564517501N00549661EA0204302079
    +B1249584517492N00549653EA0204202077
    +B1250004517482N00549644EA0204002076
    +B1250024517472N00549635EA0204002075
    +B1250044517463N00549626EA0203902075
    +B1250064517453N00549618EA0203802074
    +B1250084517443N00549610EA0203702073
    +B1250104517433N00549602EA0203602072
    +B1250124517423N00549594EA0203402071
    +B1250144517413N00549585EA0203302070
    +B1250164517402N00549576EA0203302069
    +B1250184517392N00549567EA0203202069
    +B1250204517382N00549557EA0203202068
    +B1250224517371N00549548EA0203202068
    +B1250244517361N00549540EA0203202068
    +B1250264517351N00549533EA0203102067
    +B1250284517341N00549525EA0203002067
    +B1250304517331N00549517EA0203002066
    +B1250324517321N00549510EA0202902066
    +B1250344517310N00549502EA0202702064
    +B1250364517300N00549492EA0202702063
    +B1250384517290N00549483EA0202602063
    +B1250404517280N00549474EA0202302061
    +B1250424517270N00549465EA0202102059
    +B1250444517258N00549457EA0202002057
    +B1250464517247N00549450EA0201802056
    +B1250484517238N00549442EA0201402053
    +B1250504517227N00549432EA0201102050
    +B1250524517216N00549422EA0201102048
    +B1250544517206N00549415EA0200902047
    +B1250564517197N00549407EA0200402044
    +B1250584517188N00549396EA0200202040
    +B1251004517179N00549386EA0200002038
    +B1251024517170N00549379EA0199602035
    +B1251044517159N00549372EA0199202031
    +B1251064517150N00549360EA0198902028
    +B1251084517142N00549350EA0198502025
    +B1251104517134N00549340EA0197902021
    +B1251124517125N00549329EA0197602016
    +B1251144517116N00549319EA0197402012
    +B1251164517108N00549312EA0196902008
    +B1251184517097N00549305EA0196402002
    +B1251204517086N00549297EA0196201999
    +B1251224517077N00549290EA0195801995
    +B1251244517068N00549284EA0195101989
    +B1251264517058N00549277EA0194501983
    +B1251284517048N00549271EA0194101979
    +B1251304517040N00549267EA0193601974
    +B1251324517030N00549261EA0192701968
    +B1251344517018N00549253EA0192201961
    +B1251364517009N00549244EA0192001957
    +B1251384517002N00549236EA0191301952
    +B1251404516993N00549226EA0190601945
    +B1251424516984N00549215EA0190301940
    +B1251444516975N00549207EA0190101937
    +B1251464516967N00549201EA0189701933
    +B1251484516958N00549194EA0189301928
    +B1251504516948N00549187EA0188901924
    +B1251524516939N00549179EA0188601921
    +B1251544516929N00549172EA0188101917
    +B1251564516919N00549167EA0187501912
    +B1251584516909N00549158EA0186801906
    +B1252004516900N00549145EA0186501902
    +B1252024516891N00549136EA0185901898
    +B1252044516883N00549126EA0185101891
    +B1252064516873N00549115EA0184501885
    +B1252084516863N00549104EA0184201881
    +B1252104516854N00549093EA0184001877
    +B1252124516846N00549083EA0183901874
    +B1252144516836N00549073EA0183701873
    +B1252164516828N00549062EA0183601871
    +B1252184516821N00549052EA0183301869
    +B1252204516813N00549043EA0182901865
    +B1252224516804N00549034EA0182601862
    +B1252244516795N00549024EA0182501860
    +B1252264516786N00549016EA0182401858
    +B1252284516778N00549008EA0182101856
    +B1252304516769N00549000EA0181601853
    +B1252324516760N00548991EA0181101848
    +B1252344516751N00548982EA0180701845
    +B1252364516743N00548974EA0180301840
    +B1252384516733N00548967EA0180101836
    +B1252404516724N00548958EA0179901834
    +B1252424516715N00548952EA0179601831
    +B1252444516705N00548944EA0179401827
    +B1252464516695N00548935EA0179501826
    +B1252484516687N00548928EA0179701825
    +B1252504516678N00548921EA0179801826
    +B1252524516670N00548913EA0179901827
    +B1252544516661N00548905EA0180301829
    +B1252564516652N00548900EA0180701832
    +B1252584516643N00548893EA0181101837
    +B1253004516635N00548885EA0181501842
    +B1253024516625N00548879EA0181901846
    +B1253044516616N00548873EA0182101850
    +B1253064516608N00548864EA0182001852
    +B1253084516599N00548853EA0182101854
    +B1253104516589N00548843EA0182501857
    +B1253124516583N00548833EA0182901860
    +B1253144516578N00548821EA0183401865
    +B1253164516571N00548811EA0184101870
    +B1253184516564N00548801EA0184901877
    +B1253204516556N00548792EA0185201884
    +B1253224516549N00548785EA0185301889
    +B1253244516540N00548777EA0185301892
    +B1253264516530N00548768EA0186101896
    +B1253284516521N00548761EA0187001901
    +B1253304516517N00548751EA0187501907
    +B1253324516511N00548743EA0187901911
    +B1253344516503N00548734EA0188501917
    +B1253364516497N00548723EA0189101923
    +B1253384516491N00548714EA0189601928
    +B1253404516484N00548705EA0190201934
    +B1253424516479N00548693EA0190401939
    +B1253444516476N00548681EA0190201942
    +B1253464516471N00548671EA0189701944
    +B1253484516464N00548663EA0189501943
    +B1253504516457N00548654EA0189201940
    +B1253524516450N00548645EA0189501937
    +B1253544516445N00548635EA0189801938
    +B1253564516439N00548628EA0189801938
    +B1253584516432N00548622EA0189701938
    +B1254004516425N00548615EA0189401936
    +B1254024516416N00548607EA0188901933
    +B1254044516408N00548598EA0188601929
    +B1254064516400N00548589EA0188501926
    +B1254084516391N00548580EA0188501924
    +B1254104516383N00548571EA0188501923
    +B1254124516375N00548564EA0188401923
    +B1254144516367N00548558EA0188201921
    +B1254164516357N00548550EA0188301920
    +B1254184516349N00548543EA0188901919
    +B1254204516343N00548537EA0189401922
    +B1254224516337N00548532EA0189601925
    +B1254244516330N00548525EA0189701926
    +B1254264516322N00548518EA0190001929
    +B1254284516314N00548514EA0190401933
    +B1254304516307N00548509EA0190501937
    +B1254324516300N00548499EA0190601939
    +B1254344516292N00548488EA0191101942
    +B1254364516283N00548480EA0191701946
    +B1254384516277N00548474EA0192001951
    +B1254404516271N00548467EA0191701954
    +B1254424516263N00548459EA0191401956
    +B1254444516253N00548449EA0191201956
    +B1254464516244N00548434EA0191201955
    +B1254484516235N00548417EA0191601956
    +B1254504516226N00548406EA0192101958
    +B1254524516217N00548399EA0192001960
    +B1254544516207N00548389EA0191501960
    +B1254564516196N00548377EA0191501959
    +B1254584516186N00548365EA0191501959
    +B1255004516175N00548358EA0191401957
    +B1255024516163N00548352EA0191601956
    +B1255044516153N00548342EA0191801957
    +B1255064516145N00548331EA0192001958
    +B1255084516136N00548322EA0192301960
    +B1255104516127N00548313EA0192601963
    +B1255124516120N00548303EA0192501964
    +B1255144516111N00548293EA0192301964
    +B1255164516099N00548282EA0192601964
    +B1255184516089N00548272EA0192901967
    +B1255204516080N00548263EA0192901968
    +B1255224516070N00548254EA0192901968
    +B1255244516060N00548245EA0192901967
    +B1255264516050N00548236EA0192901966
    +B1255284516040N00548224EA0193101967
    +B1255304516031N00548211EA0193601968
    +B1255324516024N00548201EA0194201971
    +B1255344516018N00548192EA0194501975
    +B1255364516013N00548184EA0194301977
    +B1255384516005N00548175EA0194101977
    +B1255404515996N00548166EA0193901976
    +B1255424515988N00548158EA0193701975
    +B1255444515981N00548150EA0193401972
    +B1255464515973N00548141EA0193101969
    +B1255484515965N00548132EA0192901966
    +B1255504515956N00548122EA0192601963
    +B1255524515948N00548113EA0192401961
    +B1255544515938N00548104EA0192201958
    +B1255564515929N00548096EA0192001956
    +B1255584515920N00548087EA0191801954
    +B1256004515910N00548076EA0191601952
    +B1256024515901N00548066EA0191401950
    +B1256044515893N00548056EA0191101947
    +B1256064515883N00548046EA0191001945
    +B1256084515874N00548039EA0190701943
    +B1256104515865N00548030EA0190201940
    +B1256124515855N00548020EA0189901936
    +B1256144515845N00548011EA0189701934
    +B1256164515835N00548002EA0189301931
    +B1256184515824N00547994EA0189101927
    +B1256204515812N00547986EA0189301925
    +B1256224515802N00547977EA0189901925
    +B1256244515794N00547966EA0190501926
    +B1256264515786N00547955EA0191401931
    +B1256284515777N00547944EA0192301938
    +B1256304515770N00547936EA0193001947
    +B1256324515763N00547939EA0193301954
    +B1256344515757N00547956EA0193801961
    +B1256364515762N00547971EA0194301967
    +B1256384515773N00547971EA0194901975
    +B1256404515779N00547955EA0195401981
    +B1256424515770N00547940EA0196101989
    +B1256444515758N00547942EA0196401996
    +B1256464515752N00547957EA0196502001
    +B1256484515755N00547971EA0196902005
    +B1256504515769N00547975EA0197302009
    +B1256524515778N00547961EA0197802014
    +B1256544515774N00547943EA0198402019
    +B1256564515764N00547938EA0198602024
    +B1256584515754N00547946EA0198702027
    +B1257004515752N00547964EA0198902030
    +B1257024515761N00547976EA0199202032
    +B1257044515773N00547976EA0199702035
    +B1257064515782N00547964EA0200202038
    +B1257084515786N00547949EA0200602043
    +B1257104515784N00547935EA0200802046
    +B1257124515775N00547927EA0201002048
    +B1257144515764N00547934EA0201002050
    +B1257164515761N00547949EA0201002051
    +B1257184515769N00547964EA0201202052
    +B1257204515783N00547967EA0201502053
    +B1257224515793N00547959EA0201802055
    +B1257244515797N00547946EA0202002057
    +B1257264515798N00547930EA0202302060
    +B1257284515794N00547919EA0202402062
    +B1257304515787N00547909EA0202702063
    +B1257324515779N00547899EA0203002066
    +B1257344515770N00547889EA0203602070
    +B1257364515763N00547882EA0203802074
    +B1257384515755N00547876EA0203902076
    +B1257404515746N00547869EA0204102078
    +B1257424515737N00547860EA0204102079
    +B1257444515729N00547850EA0204002079
    +B1257464515719N00547842EA0203802078
    +B1257484515709N00547832EA0203802077
    +B1257504515698N00547821EA0203902077
    +B1257524515688N00547811EA0204002078
    +B1257544515679N00547801EA0204102078
    +B1257564515671N00547789EA0204102079
    +B1257584515663N00547778EA0204102079
    +B1258004515654N00547768EA0204302079
    +B1258024515647N00547758EA0204402080
    +B1258044515641N00547748EA0204402080
    +B1258064515634N00547739EA0204402081
    +B1258084515627N00547732EA0204302080
    +B1258104515619N00547725EA0204002079
    +B1258124515611N00547717EA0203702077
    +B1258144515603N00547710EA0203402073
    +B1258164515593N00547700EA0203302070
    +B1258184515584N00547690EA0203202069
    +B1258204515576N00547680EA0202902068
    +B1258224515569N00547669EA0202402065
    +B1258244515560N00547658EA0202202061
    +B1258264515552N00547647EA0202102059
    +B1258284515544N00547636EA0201802057
    +B1258304515537N00547624EA0201702054
    +B1258324515530N00547612EA0201702053
    +B1258344515524N00547602EA0201702053
    +B1258364515515N00547593EA0201502052
    +B1258384515506N00547583EA0201502051
    +B1258404515497N00547573EA0201602052
    +B1258424515490N00547561EA0201702053
    +B1258444515484N00547549EA0201702053
    +B1258464515475N00547538EA0201902054
    +B1258484515467N00547526EA0202002055
    +B1258504515460N00547515EA0202002056
    +B1258524515453N00547504EA0202002056
    +B1258544515445N00547493EA0201902056
    +B1258564515437N00547483EA0201902055
    +B1258584515430N00547473EA0201902055
    +B1259004515422N00547464EA0201802055
    +B1259024515414N00547454EA0201602054
    +B1259044515407N00547444EA0201402052
    +B1259064515398N00547435EA0201102049
    +B1259084515387N00547428EA0201102048
    +B1259104515379N00547420EA0201102048
    +B1259124515372N00547412EA0200502046
    +B1259144515361N00547404EA0200502043
    +B1259164515351N00547393EA0200502043
    +B1259184515342N00547384EA0200402042
    +B1259204515332N00547378EA0200202040
    +B1259224515322N00547369EA0200502039
    +B1259244515312N00547359EA0201202040
    +B1259264515305N00547351EA0202002042
    +B1259284515298N00547344EA0202402047
    +B1259304515292N00547335EA0202502050
    +B1259324515284N00547325EA0202602052
    +B1259344515278N00547313EA0202802054
    +B1259364515272N00547300EA0203102057
    +B1259384515265N00547288EA0203402061
    +B1259404515258N00547276EA0203902065
    +B1259424515251N00547265EA0204802071
    +B1259444515247N00547255EA0205302077
    +B1259464515245N00547244EA0205602081
    +B1259484515241N00547233EA0205602085
    +B1259504515236N00547223EA0205602087
    +B1259524515231N00547216EA0205602088
    +B1259544515225N00547209EA0205602087
    +B1259564515221N00547201EA0206102089
    +B1259584515218N00547193EA0206402093
    +B1300004515214N00547185EA0206502095
    +B1300024515208N00547177EA0206602096
    +B1300044515201N00547170EA0206902099
    +B1300064515194N00547164EA0206702101
    +B1300084515186N00547158EA0206702101
    +B1300104515178N00547150EA0207202103
    +B1300124515173N00547148EA0207402105
    +B1300144515166N00547148EA0207502107
    +B1300164515157N00547141EA0207502108
    +B1300184515149N00547139EA0207402110
    +B1300204515143N00547141EA0206702110
    +B1300224515137N00547142EA0206202107
    +B1300244515130N00547139EA0205702103
    +B1300264515124N00547136EA0205002099
    +B1300284515117N00547130EA0204102092
    +B1300304515110N00547122EA0203402083
    +B1300324515105N00547113EA0202802076
    +B1300344515100N00547104EA0202202069
    +B1300364515094N00547099EA0201502062
    +B1300384515087N00547091EA0201102055
    +B1300404515081N00547078EA0201002050
    +B1300424515073N00547069EA0200902046
    +B1300444515064N00547062EA0200802045
    +B1300464515056N00547052EA0200702043
    +B1300484515047N00547042EA0200602042
    +B1300504515039N00547032EA0200502041
    +B1300524515033N00547020EA0200602041
    +B1300544515028N00547006EA0200602041
    +B1300564515022N00546994EA0200402041
    +B1300584515014N00546982EA0200402039
    +B1301004515004N00546967EA0200802040
    +B1301024514996N00546954EA0201002041
    +B1301044514989N00546943EA0200702041
    +B1301064514979N00546932EA0200602040
    +B1301084514968N00546921EA0201002041
    +B1301104514959N00546910EA0201202044
    +B1301124514950N00546901EA0201402045
    +B1301144514940N00546892EA0201902048
    +B1301164514932N00546883EA0202302052
    +B1301184514924N00546874EA0202602055
    +B1301204514915N00546864EA0203302060
    +B1301224514906N00546851EA0204402066
    +B1301244514900N00546838EA0204702072
    +B1301264514895N00546829EA0204602076
    +B1301284514887N00546821EA0204702078
    +B1301304514878N00546813EA0205002082
    +B1301324514873N00546803EA0205102085
    +B1301344514868N00546794EA0205102087
    +B1301364514861N00546785EA0204902088
    +B1301384514854N00546774EA0204702087
    +B1301404514845N00546762EA0204402085
    +B1301424514836N00546751EA0204502085
    +B1301444514828N00546742EA0204102083
    +B1301464514820N00546734EA0203402080
    +B1301484514809N00546726EA0202902075
    +B1301504514798N00546718EA0202802073
    +B1301524514789N00546711EA0202602071
    +B1301544514780N00546704EA0202302067
    +B1301564514769N00546696EA0202802064
    +B1301584514759N00546688EA0204002063
    +B1302004514753N00546683EA0204302065
    +B1302024514747N00546678EA0204202066
    +B1302044514738N00546671EA0204302067
    +B1302064514730N00546662EA0204602069
    +B1302084514723N00546653EA0204702072
    +B1302104514717N00546647EA0204702074
    +B1302124514712N00546637EA0204702075
    +B1302144514707N00546625EA0204702075
    +B1302164514700N00546613EA0204702076
    +B1302184514693N00546601EA0204702076
    +B1302204514688N00546589EA0204602076
    +B1302224514682N00546579EA0204602076
    +B1302244514676N00546571EA0204402076
    +B1302264514670N00546562EA0204102074
    +B1302284514664N00546552EA0204102073
    +B1302304514658N00546543EA0203902072
    +B1302324514653N00546534EA0203702071
    +B1302344514647N00546524EA0203502069
    +B1302364514641N00546515EA0203402067
    +B1302384514636N00546506EA0203202066
    +B1302404514630N00546499EA0203002065
    +B1302424514625N00546492EA0202902063
    +B1302444514619N00546486EA0202802063
    +B1302464514614N00546481EA0202702062
    +B1302484514607N00546475EA0202502060
    +B1302504514602N00546468EA0202402059
    +B1302524514597N00546459EA0202402058
    +B1302544514590N00546453EA0202402058
    +B1302564514583N00546447EA0202902058
    +B1302584514577N00546439EA0203402061
    +B1303004514572N00546427EA0203702065
    +B1303024514565N00546415EA0204102068
    +B1303044514558N00546401EA0204702073
    +B1303064514553N00546388EA0205102079
    +B1303084514547N00546377EA0205402083
    +B1303104514541N00546366EA0205502086
    +B1303124514535N00546356EA0205502088
    +B1303144514530N00546345EA0205302088
    +B1303164514523N00546335EA0205102087
    +B1303184514515N00546327EA0205102086
    +B1303204514508N00546319EA0204902085
    +B1303224514502N00546311EA0204602083
    +B1303244514494N00546304EA0204402081
    +B1303264514487N00546297EA0204202079
    +B1303284514481N00546290EA0204002076
    +B1303304514474N00546283EA0203602073
    +B1303324514468N00546275EA0203302070
    +B1303344514460N00546267EA0203002067
    +B1303364514454N00546260EA0202602064
    +B1303384514448N00546253EA0202302060
    +B1303404514441N00546245EA0201902056
    +B1303424514435N00546237EA0201602053
    +B1303444514429N00546230EA0201202049
    +B1303464514422N00546224EA0200902045
    +B1303484514416N00546219EA0200502042
    +B1303504514409N00546212EA0200202038
    +B1303524514403N00546205EA0199802035
    +B1303544514396N00546198EA0199402031
    +B1303564514389N00546190EA0199002027
    +B1303584514381N00546182EA0198802023
    +B1304004514374N00546173EA0198802021
    +B1304024514368N00546164EA0199202020
    +B1304044514363N00546155EA0199402021
    +B1304064514359N00546148EA0199402022
    +B1304084514353N00546141EA0199102021
    +B1304104514348N00546133EA0198802019
    +B1304124514342N00546123EA0198502016
    +B1304144514337N00546113EA0198402015
    +B1304164514332N00546106EA0198102013
    +B1304184514326N00546098EA0197802010
    +B1304204514320N00546089EA0197702008
    +B1304224514315N00546081EA0197402006
    +B1304244514311N00546072EA0197002003
    +B1304264514305N00546061EA0197002001
    +B1304284514297N00546051EA0197202001
    +B1304304514289N00546042EA0197302001
    +B1304324514283N00546032EA0197202001
    +B1304344514274N00546021EA0197102000
    +B1304364514266N00546009EA0197102001
    +B1304384514260N00546000EA0196902000
    +B1304404514254N00545990EA0196801999
    +B1304424514246N00545977EA0197101999
    +B1304444514239N00545963EA0197602000
    +B1304464514234N00545952EA0197602001
    +B1304484514228N00545942EA0197502000
    +B1304504514223N00545931EA0197402001
    +B1304524514218N00545921EA0197302001
    +B1304544514213N00545910EA0197202001
    +B1304564514207N00545899EA0197102000
    +B1304584514202N00545886EA0197102001
    +B1305004514198N00545875EA0197002000
    +B1305024514193N00545864EA0196901999
    +B1305044514187N00545854EA0196901999
    +B1305064514181N00545844EA0196801999
    +B1305084514176N00545836EA0196601998
    +B1305104514170N00545827EA0196501997
    +B1305124514165N00545817EA0196301996
    +B1305144514160N00545810EA0196101994
    +B1305164514153N00545802EA0195901992
    +B1305184514147N00545794EA0195901992
    +B1305204514141N00545788EA0195801991
    +B1305224514135N00545780EA0195601990
    +B1305244514129N00545772EA0195501988
    +B1305264514123N00545763EA0195501987
    +B1305284514118N00545754EA0195401987
    +B1305304514113N00545745EA0195201986
    +B1305324514107N00545737EA0195101985
    +B1305344514100N00545730EA0195001984
    +B1305364514095N00545722EA0194801983
    +B1305384514088N00545715EA0194501981
    +B1305404514081N00545709EA0194501979
    +B1305424514074N00545707EA0194301978
    +B1305444514069N00545719EA0193901976
    +B1305464514069N00545740EA0193901974
    +B1305484514073N00545757EA0193801974
    +B1305504514079N00545773EA0193501972
    +B1305524514087N00545792EA0193301969
    +B1305544514096N00545810EA0193301968
    +B1305564514104N00545826EA0193101967
    +B1305584514113N00545842EA0192801965
    +B1306004514121N00545861EA0192901963
    +B1306024514129N00545878EA0193001963
    +B1306044514137N00545894EA0192801962
    +B1306064514147N00545910EA0192601960
    +B1306084514158N00545927EA0192601959
    +B1306104514168N00545943EA0192601959
    +B1306124514180N00545957EA0192401958
    +B1306144514191N00545972EA0192201956
    +B1306164514202N00545988EA0192101955
    +B1306184514213N00546003EA0192101954
    +B1306204514223N00546018EA0191901953
    +B1306224514234N00546034EA0191801952
    +B1306244514244N00546050EA0191801951
    +B1306264514254N00546065EA0191801951
    +B1306284514263N00546080EA0191701951
    +B1306304514270N00546094EA0191601951
    +B1306324514275N00546104EA0191501949
    +B1306344514280N00546116EA0191601949
    +B1306364514285N00546128EA0191801950
    +B1306384514290N00546139EA0191801951
    +B1306404514297N00546151EA0191801951
    +B1306424514305N00546165EA0191801951
    +B1306444514312N00546178EA0191601951
    +B1306464514320N00546190EA0191301949
    +B1306484514327N00546203EA0191101947
    +B1306504514336N00546215EA0191101947
    +B1306524514345N00546225EA0191101946
    +B1306544514353N00546236EA0190701945
    +B1306564514362N00546248EA0190501942
    +B1306584514370N00546261EA0190101940
    +B1307004514377N00546275EA0189901936
    +B1307024514385N00546290EA0189801934
    +B1307044514394N00546306EA0189801933
    +B1307064514402N00546321EA0190201932
    +B1307084514410N00546336EA0190601934
    +B1307104514417N00546350EA0190901936
    +B1307124514425N00546364EA0191401940
    +B1307144514432N00546380EA0191601943
    +B1307164514437N00546395EA0190901945
    +B1307184514444N00546413EA0190101945
    +B1307204514453N00546431EA0189701942
    +B1307224514461N00546447EA0189401939
    +B1307244514467N00546462EA0189001934
    +B1307264514475N00546476EA0188501929
    +B1307284514484N00546492EA0188201924
    +B1307304514493N00546506EA0187801921
    +B1307324514503N00546520EA0187501916
    +B1307344514513N00546533EA0187201913
    +B1307364514523N00546546EA0187001910
    +B1307384514533N00546559EA0186801907
    +B1307404514542N00546572EA0186601905
    +B1307424514550N00546586EA0186401902
    +B1307444514557N00546599EA0186301900
    +B1307464514565N00546610EA0186201899
    +B1307484514573N00546618EA0186001898
    +B1307504514581N00546627EA0185701895
    +B1307524514590N00546636EA0185501893
    +B1307544514597N00546646EA0185101890
    +B1307564514604N00546657EA0184801887
    +B1307584514614N00546667EA0184601884
    +B1308004514624N00546679EA0184501882
    +B1308024514634N00546692EA0184601880
    +B1308044514644N00546706EA0185601880
    +B1308064514654N00546720EA0186501881
    +B1308084514661N00546733EA0186901885
    +B1308104514668N00546744EA0187101889
    +B1308124514673N00546757EA0187601893
    +B1308144514678N00546773EA0188401899
    +B1308164514683N00546787EA0189301906
    +B1308184514690N00546800EA0189901915
    +B1308204514696N00546811EA0190201921
    +B1308224514703N00546823EA0190301927
    +B1308244514713N00546837EA0190501930
    +B1308264514723N00546853EA0191001935
    +B1308284514731N00546869EA0190901939
    +B1308304514740N00546883EA0190501941
    +B1308324514749N00546896EA0189801942
    +B1308344514759N00546910EA0189401940
    +B1308364514770N00546923EA0189101936
    +B1308384514780N00546934EA0188801931
    +B1308404514790N00546946EA0188401927
    +B1308424514799N00546959EA0188101923
    +B1308444514808N00546972EA0187801919
    +B1308464514816N00546986EA0187501916
    +B1308484514823N00547000EA0187301913
    +B1308504514830N00547011EA0186901909
    +B1308524514837N00547023EA0186401904
    +B1308544514843N00547038EA0186401901
    +B1308564514850N00547051EA0186501898
    +B1308584514856N00547058EA0186401897
    +B1309004514863N00547067EA0186101894
    +B1309024514870N00547077EA0185901892
    +B1309044514878N00547087EA0185701890
    +B1309064514888N00547095EA0185501888
    +B1309084514897N00547105EA0185201886
    +B1309104514905N00547115EA0184801883
    +B1309124514913N00547126EA0184401879
    +B1309144514923N00547138EA0184701876
    +B1309164514932N00547150EA0185301874
    +B1309184514940N00547162EA0185501874
    +B1309204514948N00547174EA0185601876
    +B1309224514958N00547186EA0185801880
    +B1309244514967N00547197EA0186001883
    +B1309264514975N00547208EA0186301887
    +B1309284514983N00547219EA0186801891
    +B1309304514993N00547232EA0187501896
    +B1309324515003N00547245EA0188501902
    +B1309344515010N00547255EA0189201910
    +B1309364515014N00547267EA0189701916
    +B1309384515010N00547278EA0189801923
    +B1309404514998N00547280EA0190101928
    +B1309424514989N00547268EA0190601933
    +B1309444514994N00547254EA0191201939
    +B1309464515008N00547250EA0192101946
    +B1309484515020N00547252EA0193201956
    +B1309504515030N00547253EA0193901965
    +B1309524515039N00547256EA0194201972
    +B1309544515047N00547274EA0194901979
    +B1309564515045N00547295EA0195501986
    +B1309584515033N00547299EA0196001992
    +B1310004515026N00547287EA0196501999
    +B1310024515029N00547272EA0197002005
    +B1310044515042N00547267EA0197902010
    +B1310064515055N00547273EA0198802018
    +B1310084515065N00547282EA0199602028
    +B1310104515073N00547290EA0200002034
    +B1310124515075N00547308EA0200002039
    +B1310144515067N00547325EA0200102043
    +B1310164515053N00547327EA0200602046
    +B1310184515045N00547311EA0201102049
    +B1310204515046N00547295EA0201802054
    +B1310224515049N00547284EA0202602060
    +B1310244515055N00547274EA0203602067
    +B1310264515063N00547267EA0204402075
    +B1310284515074N00547267EA0205302082
    +B1310304515082N00547277EA0205902090
    +B1310324515088N00547294EA0206202096
    +B1310344515087N00547316EA0206202100
    +B1310364515075N00547324EA0206202103
    +B1310384515064N00547313EA0206402105
    +B1310404515066N00547298EA0206802106
    +B1310424515076N00547291EA0207402110
    +B1310444515086N00547288EA0208202117
    +B1310464515097N00547286EA0208902123
    +B1310484515109N00547287EA0209902131
    +B1310504515120N00547290EA0210802141
    +B1310524515130N00547293EA0211502149
    +B1310544515138N00547288EA0211502156
    +B1310564515132N00547275EA0211802161
    +B1310584515120N00547274EA0212602165
    +B1311004515113N00547289EA0213602171
    +B1311024515115N00547307EA0214602179
    +B1311044515127N00547316EA0215302186
    +B1311064515138N00547312EA0215902194
    +B1311084515142N00547301EA0216302199
    +B1311104515140N00547290EA0216802204
    +B1311124515130N00547289EA0217202208
    +B1311144515119N00547303EA0218402213
    +B1311164515116N00547324EA0219502221
    +B1311184515120N00547343EA0220402230
    +B1311204515131N00547359EA0221102240
    +B1311224515145N00547362EA0221802248
    +B1311244515152N00547350EA0222402256
    +B1311264515146N00547337EA0222902263
    +B1311284515132N00547336EA0223702270
    +B1311304515123N00547351EA0224702278
    +B1311324515123N00547372EA0225502287
    +B1311344515133N00547389EA0226102295
    +B1311364515148N00547395EA0226802302
    +B1311384515158N00547387EA0227502309
    +B1311404515155N00547374EA0228102316
    +B1311424515143N00547370EA0228802322
    +B1311444515132N00547381EA0229602330
    +B1311464515130N00547401EA0230202338
    +B1311484515140N00547418EA0230702345
    +B1311504515153N00547424EA0230902350
    +B1311524515167N00547420EA0231602355
    +B1311544515176N00547410EA0232402362
    +B1311564515178N00547398EA0233102370
    +B1311584515178N00547387EA0234002378
    +B1312004515178N00547379EA0234702386
    +B1312024515172N00547380EA0235002393
    +B1312044515165N00547394EA0235302398
    +B1312064515167N00547413EA0235802404
    +B1312084515178N00547430EA0236402408
    +B1312104515194N00547437EA0236902414
    +B1312124515207N00547431EA0237402419
    +B1312144515214N00547419EA0238102425
    +B1312164515215N00547409EA0238802431
    +B1312184515210N00547404EA0239402437
    +B1312204515205N00547403EA0239802442
    +B1312224515200N00547411EA0240002446
    +B1312244515198N00547428EA0240202448
    +B1312264515204N00547448EA0240502451
    +B1312284515216N00547464EA0241002455
    +B1312304515231N00547474EA0241702460
    +B1312324515244N00547479EA0242102465
    +B1312344515258N00547483EA0242202469
    +B1312364515272N00547488EA0242102471
    +B1312384515286N00547499EA0242102470
    +B1312404515298N00547511EA0241602469
    +B1312424515312N00547520EA0241302466
    +B1312444515328N00547530EA0240902461
    +B1312464515341N00547543EA0240302455
    +B1312484515354N00547556EA0239702449
    +B1312504515369N00547568EA0239302443
    +B1312524515382N00547582EA0238802438
    +B1312544515394N00547595EA0238402433
    +B1312564515408N00547607EA0238102429
    +B1312584515421N00547619EA0237802425
    +B1313004515432N00547634EA0237502422
    +B1313024515443N00547649EA0237402419
    +B1313044515454N00547665EA0237302418
    +B1313064515465N00547679EA0237202416
    +B1313084515476N00547693EA0236902413
    +B1313104515489N00547706EA0236602411
    +B1313124515501N00547719EA0236402408
    +B1313144515513N00547732EA0236002405
    +B1313164515524N00547745EA0235602401
    +B1313184515537N00547758EA0235102397
    +B1313204515549N00547770EA0234602391
    +B1313224515561N00547781EA0234002386
    +B1313244515574N00547791EA0233502380
    +B1313264515587N00547802EA0233102375
    +B1313284515600N00547815EA0232702371
    +B1313304515611N00547829EA0232302367
    +B1313324515622N00547842EA0232102363
    +B1313344515633N00547853EA0231802361
    +B1313364515643N00547863EA0231402357
    +B1313384515653N00547874EA0231002353
    +B1313404515664N00547884EA0230702349
    +B1313424515675N00547895EA0230302346
    +B1313444515685N00547906EA0229902342
    +B1313464515695N00547917EA0229402338
    +B1313484515707N00547928EA0229102334
    +B1313504515718N00547940EA0228902331
    +B1313524515729N00547951EA0228502327
    +B1313544515740N00547961EA0228102323
    +B1313564515752N00547971EA0227702319
    +B1313594515763N00547982EA0227302315
    +B1314014515775N00547994EA0227002312
    +B1314034515787N00548006EA0227102310
    +B1314054515798N00548019EA0227302309
    +B1314074515807N00548030EA0227002308
    +B1314094515817N00548040EA0226302304
    +B1314114515828N00548053EA0226002301
    +B1314134515840N00548066EA0226002299
    +B1314154515850N00548077EA0225602296
    +B1314174515860N00548090EA0225202292
    +B1314194515871N00548105EA0224902289
    +B1314214515884N00548117EA0224602286
    +B1314234515895N00548129EA0224202282
    +B1314254515906N00548141EA0223602278
    +B1314274515916N00548152EA0223002273
    +B1314294515927N00548163EA0222602268
    +B1314314515938N00548173EA0222102264
    +B1314334515949N00548184EA0221502258
    +B1314354515959N00548195EA0221002252
    +B1314374515971N00548206EA0220802247
    +B1314394515981N00548219EA0220702244
    +B1314414515989N00548231EA0220402242
    +B1314434515998N00548243EA0220002237
    +B1314454516007N00548257EA0219902235
    +B1314474516017N00548270EA0219902234
    +B1314494516027N00548282EA0219702232
    +B1314514516037N00548295EA0219602231
    +B1314534516047N00548306EA0219402230
    +B1314554516055N00548316EA0218902228
    +B1314574516064N00548329EA0218302224
    +B1314594516074N00548342EA0217902219
    +B1315014516084N00548353EA0217702217
    +B1315034516094N00548363EA0217302213
    +B1315054516105N00548373EA0217002210
    +B1315074516116N00548383EA0216602206
    +B1315094516128N00548393EA0216202202
    +B1315114516140N00548401EA0215702198
    +B1315134516152N00548411EA0215302194
    +B1315154516163N00548423EA0215102191
    +B1315174516175N00548434EA0214802188
    +B1315194516185N00548446EA0214402185
    +B1315214516195N00548458EA0214002181
    +B1315234516204N00548468EA0213402176
    +B1315254516215N00548480EA0213002171
    +B1315274516226N00548492EA0212602167
    +B1315294516237N00548501EA0211902162
    +B1315314516250N00548509EA0211402155
    +B1315334516264N00548516EA0211302151
    +B1315354516275N00548527EA0211002148
    +B1315374516284N00548538EA0210702144
    +B1315394516295N00548548EA0210502142
    +B1315414516305N00548557EA0210302140
    +B1315434516314N00548567EA0209802137
    +B1315454516324N00548577EA0209302132
    +B1315474516336N00548588EA0209102130
    +B1315494516346N00548598EA0209002128
    +B1315514516356N00548607EA0208802126
    +B1315534516367N00548616EA0208602124
    +B1315554516379N00548625EA0208702123
    +B1315574516390N00548633EA0208502123
    +B1315594516401N00548641EA0208202121
    +B1316014516414N00548650EA0208202120
    +B1316034516428N00548660EA0208302119
    +B1316054516439N00548672EA0208202119
    +B1316074516450N00548682EA0208102118
    +B1316094516463N00548691EA0208102118
    +B1316114516475N00548700EA0208002118
    +B1316134516486N00548709EA0207802116
    +B1316154516498N00548719EA0207802115
    +B1316174516510N00548729EA0207902116
    +B1316194516522N00548739EA0207902117
    +B1316214516533N00548749EA0208102117
    +B1316234516546N00548759EA0208602119
    +B1316254516558N00548769EA0208902121
    +B1316274516568N00548779EA0209302123
    +B1316294516580N00548789EA0210002128
    +B1316314516591N00548799EA0210502134
    +B1316334516603N00548811EA0210802138
    +B1316354516617N00548820EA0211302142
    +B1316374516630N00548828EA0211902148
    +B1316394516640N00548838EA0212202153
    +B1316414516650N00548847EA0212402157
    +B1316434516661N00548855EA0212402159
    +B1316454516672N00548862EA0212402160
    +B1316474516684N00548872EA0212502160
    +B1316494516696N00548881EA0212602161
    +B1316514516709N00548888EA0212602162
    +B1316534516721N00548895EA0212502162
    +B1316554516733N00548903EA0212402161
    +B1316574516745N00548911EA0212302160
    +B1316594516757N00548921EA0212402160
    +B1317014516768N00548931EA0212602161
    +B1317034516780N00548942EA0212802163
    +B1317054516791N00548954EA0213202166
    +B1317074516802N00548966EA0213402169
    +B1317094516814N00548977EA0213602171
    +B1317114516827N00548987EA0213802173
    +B1317134516839N00548997EA0213802175
    +B1317154516851N00549007EA0213602175
    +B1317174516864N00549018EA0213502174
    +B1317194516877N00549031EA0213502174
    +B1317214516889N00549042EA0213202173
    +B1317234516900N00549055EA0212902170
    +B1317254516912N00549072EA0212402166
    +B1317274516923N00549087EA0212102162
    +B1317294516933N00549099EA0212002160
    +B1317314516942N00549110EA0211402156
    +B1317334516952N00549122EA0210702152
    +B1317354516964N00549131EA0210002146
    +B1317374516975N00549139EA0209202138
    +B1317394516987N00549148EA0208402129
    +B1317414516999N00549158EA0207602121
    +B1317434517012N00549167EA0207002114
    +B1317454517024N00549176EA0206302107
    +B1317474517038N00549183EA0205702099
    +B1317494517054N00549191EA0205402092
    +B1317514517069N00549200EA0205602087
    +B1317534517082N00549208EA0205502083
    +B1317554517091N00549217EA0205102080
    +B1317574517102N00549227EA0204902077
    +B1317594517112N00549238EA0204702075
    +B1318014517122N00549248EA0204402073
    +B1318034517134N00549258EA0204002070
    +B1318054517146N00549268EA0203702067
    +B1318074517158N00549278EA0203402064
    +B1318094517170N00549287EA0203302062
    +B1318114517180N00549297EA0202902060
    +B1318134517189N00549307EA0202602057
    +B1318154517199N00549316EA0202402055
    +B1318174517210N00549324EA0202202053
    +B1318194517220N00549332EA0201902051
    +B1318214517230N00549340EA0201602048
    +B1318234517240N00549350EA0201502046
    +B1318254517250N00549359EA0201202044
    +B1318274517260N00549367EA0200802041
    +B1318294517270N00549375EA0200302037
    +B1318314517281N00549383EA0200102034
    +B1318334517293N00549390EA0200002031
    +B1318354517301N00549396EA0199602029
    +B1318374517311N00549400EA0199002025
    +B1318394517324N00549407EA0198902021
    +B1318414517337N00549417EA0199202018
    +B1318434517348N00549424EA0199002017
    +B1318454517357N00549431EA0198602014
    +B1318474517368N00549441EA0198302011
    +B1318494517378N00549452EA0198102010
    +B1318514517387N00549461EA0197802008
    +B1318534517396N00549471EA0197202003
    +B1318554517406N00549483EA0196901999
    +B1318574517417N00549493EA0196801997
    +B1318594517427N00549503EA0196701996
    +B1319014517437N00549513EA0196501994
    +B1319034517448N00549522EA0196301993
    +B1319054517459N00549531EA0196201992
    +B1319074517470N00549541EA0196201991
    +B1319094517482N00549550EA0196101991
    +B1319114517492N00549560EA0195701989
    +B1319134517502N00549570EA0195201986
    +B1319154517514N00549581EA0195001983
    +B1319174517526N00549592EA0194901981
    +B1319194517538N00549600EA0194801980
    +B1319214517549N00549607EA0194501977
    +B1319234517561N00549618EA0194601976
    +B1319254517571N00549630EA0194701977
    +B1319274517580N00549641EA0194601977
    +B1319294517590N00549652EA0194501976
    +B1319314517599N00549664EA0194401975
    +B1319334517609N00549677EA0194401975
    +B1319354517618N00549691EA0194301975
    +B1319374517627N00549703EA0194001973
    +B1319394517637N00549714EA0193701970
    +B1319414517647N00549726EA0193501968
    +B1319434517657N00549738EA0193301966
    +B1319454517666N00549750EA0193401965
    +B1319474517675N00549762EA0193401965
    +B1319494517682N00549775EA0193201964
    +B1319514517690N00549786EA0192901962
    +B1319534517698N00549798EA0192601960
    +B1319554517707N00549810EA0192301957
    +B1319574517715N00549823EA0192201954
    +B1319594517722N00549837EA0192101953
    +B1320014517728N00549851EA0192001952
    +B1320034517734N00549864EA0191701950
    +B1320054517741N00549880EA0191701948
    +B1320074517750N00549896EA0191701948
    +B1320094517758N00549912EA0191901948
    +B1320114517766N00549928EA0192101950
    +B1320134517774N00549944EA0192301952
    +B1320154517781N00549958EA0192501955
    +B1320174517787N00549972EA0192801957
    +B1320194517795N00549981EA0192701959
    +B1320214517808N00549978EA0192601959
    +B1320234517818N00549964EA0192801960
    +B1320254517816N00549950EA0192801960
    +B1320274517806N00549944EA0192901960
    +B1320294517797N00549954EA0192901961
    +B1320314517794N00549972EA0193001960
    +B1320334517797N00549991EA0193201962
    +B1320354517802N00550009EA0193401963
    +B1320374517806N00550026EA0193601966
    +B1320394517812N00550042EA0194001969
    +B1320414517818N00550056EA0194401973
    +B1320434517822N00550069EA0194801977
    +B1320454517824N00550083EA0195101980
    +B1320474517819N00550095EA0195301983
    +B1320494517808N00550094EA0195601986
    +B1320514517802N00550080EA0195801989
    +B1320534517808N00550063EA0195901991
    +B1320554517823N00550063EA0196101993
    +B1320574517834N00550080EA0196601996
    +B1320594517838N00550096EA0197102001
    +B1321014517844N00550109EA0197702007
    +B1321034517849N00550123EA0198102012
    +B1321054517850N00550137EA0198702017
    +B1321074517843N00550148EA0199002021
    +B1321094517832N00550148EA0199202024
    +B1321114517825N00550138EA0199302026
    +B1321134517826N00550121EA0199402028
    +B1321154517838N00550110EA0199702030
    +B1321174517852N00550116EA0200102032
    +B1321194517862N00550131EA0200702035
    +B1321214517868N00550145EA0201302041
    +B1321234517876N00550155EA0201702046
    +B1321254517885N00550163EA0201902050
    +B1321274517894N00550172EA0202002052
    +B1321294517902N00550183EA0202002053
    +B1321314517910N00550194EA0201902053
    +B1321334517918N00550207EA0202002053
    +B1321354517926N00550220EA0201902053
    +B1321374517933N00550235EA0201902053
    +B1321394517940N00550250EA0202102054
    +B1321414517947N00550263EA0202202055
    +B1321434517954N00550277EA0202002055
    +B1321454517961N00550291EA0202002054
    +B1321474517970N00550306EA0202102054
    +B1321494517978N00550318EA0202202055
    +B1321514517984N00550330EA0202102055
    +B1321534517991N00550342EA0202002054
    +B1321554517999N00550354EA0201902052
    +B1321574518007N00550365EA0201902052
    +B1321594518017N00550377EA0201802052
    +B1322014518027N00550388EA0201702051
    +B1322034518036N00550400EA0201402049
    +B1322054518045N00550413EA0201102047
    +B1322074518055N00550427EA0201102045
    +B1322094518064N00550441EA0201102044
    +B1322114518072N00550454EA0201002044
    +B1322134518081N00550467EA0200902043
    +B1322154518090N00550481EA0201002043
    +B1322174518098N00550496EA0201102044
    +B1322194518105N00550510EA0201002044
    +B1322214518113N00550523EA0200702043
    +B1322234518121N00550538EA0200502040
    +B1322254518128N00550553EA0200302039
    +B1322274518135N00550566EA0200002037
    +B1322294518143N00550579EA0199502033
    +B1322314518151N00550593EA0199102028
    +B1322334518160N00550606EA0198802025
    +B1322354518169N00550618EA0198502021
    +B1322374518178N00550632EA0198202018
    +B1322394518186N00550645EA0197802015
    +B1322414518194N00550658EA0197502011
    +B1322434518203N00550671EA0197302008
    +B1322454518213N00550683EA0197002006
    +B1322474518221N00550695EA0196602002
    +B1322494518230N00550707EA0196201998
    +B1322514518240N00550720EA0195801994
    +B1322534518249N00550734EA0195601991
    +B1322554518257N00550747EA0195401989
    +B1322574518264N00550761EA0195101986
    +B1322594518271N00550775EA0194601982
    +B1323014518280N00550790EA0194201978
    +B1323034518288N00550804EA0194001975
    +B1323054518296N00550818EA0193601972
    +B1323074518304N00550831EA0193101968
    +B1323094518312N00550845EA0192701963
    +B1323114518321N00550861EA0192401959
    +B1323134518329N00550875EA0192101956
    +B1323154518337N00550888EA0191901953
    +B1323174518346N00550901EA0191501951
    +B1323194518354N00550916EA0191001946
    +B1323214518362N00550932EA0190601942
    +B1323234518368N00550950EA0190201938
    +B1323254518374N00550967EA0190001934
    +B1323274518379N00550983EA0189601931
    +B1323294518384N00550999EA0189301928
    +B1323314518389N00551015EA0189101925
    +B1323334518394N00551031EA0188901923
    +B1323354518402N00551044EA0188501920
    +B1323374518411N00551055EA0188101916
    +B1323394518424N00551062EA0187901912
    +B1323414518437N00551064EA0187701910
    +B1323434518450N00551062EA0187501908
    +B1323454518463N00551062EA0187501907
    +B1323474518475N00551060EA0187701907
    +B1323494518487N00551058EA0187701908
    +B1323514518499N00551058EA0187801908
    +B1323534518512N00551057EA0187901909
    +B1323554518524N00551055EA0187901911
    +B1323574518535N00551053EA0187601911
    +B1323594518546N00551050EA0187201910
    +B1324014518559N00551045EA0187101908
    +B1324034518572N00551042EA0187001907
    +B1324054518585N00551039EA0187101907
    +B1324074518599N00551034EA0187201907
    +B1324094518613N00551029EA0187601909
    +B1324114518625N00551023EA0187901911
    +B1324134518635N00551015EA0187901912
    +B1324154518645N00551007EA0187801913
    +B1324174518655N00551004EA0187601913
    +B1324194518666N00550995EA0187501912
    +B1324214518671N00550979EA0187401911
    +B1324234518659N00550964EA0187301911
    +B1324254518651N00550964EA0187301910
    +B1324274518644N00550970EA0187101908
    +B1324294518635N00550976EA0187001906
    +B1324314518627N00550984EA0187001905
    +B1324334518620N00550993EA0186901905
    +B1324354518614N00550997EA0186501903
    +B1324374518605N00551002EA0186201900
    +B1324394518596N00551009EA0186001898
    +B1324414518588N00551015EA0185901897
    +B1324434518581N00551021EA0185701894
    +B1324454518572N00551027EA0185601892
    +B1324474518563N00551033EA0185501890
    +B1324494518555N00551038EA0185401889
    +B1324514518546N00551043EA0185401888
    +B1324534518537N00551050EA0185401888
    +B1324554518528N00551056EA0185401888
    +B1324574518519N00551061EA0185201887
    +B1324594518510N00551067EA0184901885
    +B1325014518500N00551074EA0184701883
    +B1325034518491N00551082EA0184601882
    +B1325054518481N00551089EA0184401880
    +B1325074518471N00551095EA0184301878
    +B1325094518460N00551100EA0184301877
    +B1325114518450N00551105EA0184401877
    +B1325134518440N00551110EA0184601878
    +B1325154518431N00551112EA0184901880
    +B1325174518421N00551110EA0185601883
    +B1325194518413N00551109EA0185801887
    +B1325214518406N00551113EA0185401889
    +B1325234518399N00551119EA0185201888
    +B1325254518390N00551125EA0185201887
    +B1325274518382N00551131EA0185101887
    +B1325294518375N00551137EA0184901885
    +B1325314518365N00551129EA0185101884
    +B1325334518356N00551115EA0186101884
    +B1325354518349N00551103EA0186901887
    +B1325374518343N00551094EA0187401893
    +B1325394518336N00551085EA0187901900
    +B1325414518328N00551074EA0188401906
    +B1325434518321N00551066EA0188901912
    +B1325454518312N00551064EA0188901917
    +B1325474518305N00551075EA0188901921
    +B1325494518305N00551092EA0189201924
    +B1325514518315N00551106EA0190001928
    +B1325534518329N00551110EA0190701935
    +B1325554518345N00551105EA0191601942
    +B1325574518357N00551091EA0192401950
    +B1325594518365N00551073EA0193101959
    +B1326014518370N00551056EA0193401966
    +B1326034518369N00551041EA0193401972
    +B1326054518358N00551031EA0193301976
    +B1326074518347N00551041EA0193601978
    +B1326094518350N00551058EA0193901981
    +B1326114518364N00551067EA0194501983
    +B1326134518381N00551064EA0194701985
    +B1326154518392N00551051EA0194401986
    +B1326174518390N00551035EA0194001987
    +B1326194518378N00551029EA0193901985
    +B1326214518366N00551036EA0194101984
    +B1326234518361N00551050EA0194701984
    +B1326254518361N00551063EA0195701986
    +B1326274518359N00551072EA0196501992
    +B1326294518354N00551083EA0197301999
    +B1326314518350N00551098EA0198202007
    +B1326334518358N00551111EA0198902014
    +B1326354518371N00551106EA0199602021
    +B1326374518371N00551087EA0200602029
    +B1326394518360N00551086EA0201502037
    +B1326414518357N00551107EA0202402045
    +B1326434518366N00551119EA0203702057
    +B1326454518378N00551118EA0204402066
    +B1326474518386N00551101EA0205302076
    +B1326494518380N00551086EA0206002085
    +B1326514518367N00551088EA0207302094
    +B1326534518362N00551102EA0208402107
    +B1326554518362N00551115EA0209302120
    +B1326574518366N00551125EA0209902130
    +B1326594518380N00551129EA0210602139
    +B1327014518395N00551119EA0211802147
    +B1327034518399N00551104EA0212602155
    +B1327054518383N00551092EA0213602166
    +B1327074518371N00551099EA0214502176
    +B1327094518370N00551115EA0215302185
    +B1327114518379N00551125EA0216002193
    +B1327134518394N00551118EA0216802201
    +B1327154518400N00551100EA0218102210
    +B1327174518395N00551086EA0219002221
    +B1327194518383N00551081EA0219902230
    +B1327214518375N00551087EA0220702241
    +B1327234518375N00551103EA0221402248
    +B1327254518385N00551116EA0222402257
    +B1327274518400N00551112EA0223202265
    +B1327294518408N00551096EA0224302275
    +B1327314518404N00551078EA0225102286
    +B1327334518392N00551075EA0225902293
    +B1327354518387N00551089EA0226702301
    +B1327374518394N00551105EA0227302309
    +B1327394518408N00551101EA0228202315
    +B1327414518415N00551079EA0229302325
    +B1327434518406N00551066EA0230202335
    +B1327454518396N00551068EA0231102344
    +B1327474518393N00551080EA0231802352
    +B1327494518400N00551092EA0232402358
    +B1327514518413N00551090EA0233202367
    +B1327534518422N00551073EA0234102375
    +B1327554518425N00551052EA0235302385
    +B1327574518424N00551035EA0236002395
    +B1327594518419N00551022EA0236302403
    +B1328014518406N00551027EA0236502410
    +B1328034518400N00551046EA0237202416
    +B1328054518405N00551063EA0238302423
    +B1328074518412N00551071EA0238802430
    +B1328094518422N00551076EA0239002434
    +B1328114518436N00551077EA0239502437
    +B1328134518448N00551065EA0240102441
    +B1328154518444N00551047EA0240802447
    +B1328174518432N00551041EA0241702453
    +B1328194518423N00551050EA0242502461
    +B1328214518421N00551067EA0242802468
    +B1328234518427N00551081EA0243302472
    +B1328254518436N00551093EA0243902478
    +B1328274518445N00551108EA0244602485
    +B1328294518452N00551122EA0245502492
    +B1328314518461N00551136EA0246302500
    +B1328334518471N00551150EA0247102508
    +B1328354518481N00551164EA0247802515
    +B1328374518488N00551181EA0248302522
    +B1328394518494N00551200EA0248802527
    +B1328414518500N00551219EA0249302533
    +B1328434518508N00551238EA0249502537
    +B1328454518516N00551258EA0249502539
    +B1328474518524N00551279EA0249402540
    +B1328494518533N00551298EA0249402540
    +B1328514518541N00551315EA0249302540
    +B1328534518548N00551331EA0249102539
    +B1328554518557N00551346EA0248802536
    +B1328574518566N00551359EA0248602533
    +B1328594518577N00551371EA0248502531
    +B1329014518587N00551383EA0248302529
    +B1329034518599N00551395EA0248102526
    +B1329054518611N00551407EA0247902524
    +B1329074518620N00551421EA0247802523
    +B1329094518629N00551435EA0247402520
    +B1329114518639N00551449EA0246902516
    +B1329134518650N00551464EA0246502511
    +B1329154518662N00551479EA0246202507
    +B1329174518674N00551492EA0246102504
    +B1329194518687N00551505EA0246102502
    +B1329214518699N00551516EA0246402502
    +B1329234518710N00551527EA0246202501
    +B1329254518722N00551538EA0246002499
    +B1329274518735N00551550EA0246102499
    +B1329294518747N00551561EA0246202499
    +B1329314518759N00551571EA0246202499
    +B1329334518771N00551581EA0246302500
    +B1329354518782N00551592EA0246302500
    +B1329374518795N00551604EA0246402500
    +B1329394518807N00551615EA0246602502
    +B1329414518817N00551625EA0246702503
    +B1329434518827N00551633EA0246402503
    +B1329454518839N00551641EA0246202502
    +B1329474518850N00551649EA0246302501
    +B1329494518861N00551657EA0246202501
    +B1329514518872N00551664EA0246102501
    +B1329534518883N00551672EA0245802499
    +B1329554518894N00551679EA0245602496
    +B1329574518906N00551685EA0245402494
    +B1329594518917N00551693EA0245102492
    +B1330014518928N00551700EA0244802489
    +B1330034518940N00551708EA0244602485
    +B1330054518952N00551718EA0244502484
    +B1330074518963N00551726EA0244302483
    +B1330094518973N00551734EA0244002480
    +B1330114518984N00551741EA0243702477
    +B1330134518994N00551750EA0243302474
    +B1330154519005N00551757EA0242902470
    +B1330174519017N00551765EA0242602466
    +B1330194519028N00551774EA0242502464
    +B1330214519039N00551780EA0242102461
    +B1330234519049N00551789EA0241802457
    +B1330254519060N00551800EA0241902455
    +B1330274519069N00551807EA0241802455
    +B1330294519078N00551814EA0241602453
    +B1330314519089N00551823EA0241602452
    +B1330334519099N00551830EA0241502452
    +B1330354519109N00551836EA0241002450
    +B1330374519118N00551845EA0240402447
    +B1330394519130N00551855EA0240202444
    +B1330414519142N00551865EA0240102442
    +B1330434519151N00551874EA0239502438
    +B1330454519161N00551882EA0238702433
    +B1330474519173N00551891EA0238002426
    +B1330494519186N00551899EA0237702421
    +B1330514519198N00551906EA0237302417
    +B1330534519210N00551913EA0236602410
    +B1330554519222N00551921EA0236002404
    +B1330574519236N00551929EA0235602398
    +B1330594519248N00551935EA0234902393
    +B1331014519260N00551942EA0234302386
    +B1331034519273N00551948EA0234002380
    +B1331054519285N00551955EA0233902375
    +B1331074519295N00551963EA0233802373
    +B1331094519305N00551970EA0233302369
    +B1331114519316N00551979EA0232902365
    +B1331134519327N00551988EA0232802361
    +B1331154519337N00551996EA0232802360
    +B1331174519346N00552004EA0232502358
    +B1331194519357N00552012EA0232302355
    +B1331214519368N00552021EA0232302354
    +B1331234519378N00552030EA0232202354
    +B1331254519387N00552038EA0232002352
    +B1331274519397N00552047EA0231802350
    +B1331294519408N00552054EA0231902350
    +B1331314519418N00552063EA0231702349
    +B1331334519427N00552072EA0231502348
    +B1331354519437N00552081EA0231402347
    +B1331374519447N00552090EA0231302346
    +B1331394519456N00552100EA0231102344
    +B1331414519466N00552109EA0231002343
    +B1331434519477N00552117EA0230902342
    +B1331454519487N00552123EA0230802341
    +B1331474519497N00552130EA0230702340
    +B1331494519508N00552137EA0230602339
    +B1331514519518N00552144EA0230602339
    +B1331534519528N00552151EA0230402338
    +B1331554519538N00552158EA0230202336
    +B1331574519548N00552166EA0230102334
    +B1331594519559N00552174EA0230002333
    +B1332014519568N00552183EA0229702332
    +B1332034519578N00552192EA0229302329
    +B1332054519589N00552201EA0229002326
    +B1332074519599N00552206EA0228902324
    +B1332094519608N00552210EA0228602322
    +B1332114519618N00552215EA0228002319
    +B1332134519629N00552221EA0227802316
    +B1332154519640N00552226EA0227702314
    +B1332174519647N00552233EA0227202311
    +B1332194519656N00552240EA0226602307
    +B1332214519667N00552248EA0226502304
    +B1332234519676N00552254EA0226202301
    +B1332254519686N00552258EA0225702297
    +B1332274519696N00552264EA0225502293
    +B1332294519706N00552271EA0225402291
    +B1332314519714N00552277EA0225102288
    +B1332334519721N00552285EA0224602284
    +B1332354519730N00552292EA0224302280
    +B1332374519739N00552297EA0224102278
    +B1332394519747N00552302EA0223802275
    +B1332414519756N00552306EA0223502272
    +B1332434519766N00552311EA0223202269
    +B1332454519775N00552317EA0223002266
    +B1332474519784N00552322EA0222702264
    +B1332494519794N00552327EA0222402261
    +B1332514519804N00552331EA0222202258
    +B1332534519814N00552337EA0222202257
    +B1332554519824N00552344EA0222402256
    +B1332574519833N00552352EA0222802256
    +B1332594519842N00552358EA0223302259
    +B1333014519850N00552365EA0223502262
    +B1333034519858N00552372EA0223602264
    +B1333054519868N00552377EA0223902267
    +B1333074519877N00552384EA0224202270
    +B1333094519885N00552392EA0224402273
    +B1333114519893N00552399EA0224602276
    +B1333134519903N00552405EA0224902278
    +B1333154519913N00552408EA0225202281
    +B1333174519924N00552410EA0225402285
    +B1333194519934N00552413EA0225402287
    +B1333214519944N00552418EA0225302288
    +B1333234519956N00552421EA0225402288
    +B1333254519968N00552424EA0225502289
    +B1333274519978N00552429EA0225802291
    +B1333294519987N00552433EA0226002294
    +B1333314519996N00552437EA0226202296
    +B1333334520004N00552443EA0226402298
    +B1333354520012N00552449EA0226702301
    +B1333374520020N00552456EA0226902304
    +B1333394520027N00552463EA0227002306
    +B1333414520036N00552473EA0227002307
    +B1333434520044N00552482EA0227002307
    +B1333454520053N00552491EA0227202307
    +B1333474520061N00552501EA0227402309
    +B1333494520069N00552511EA0227502310
    +B1333514520078N00552521EA0227602312
    +B1333534520087N00552535EA0228002314
    +B1333554520094N00552548EA0228702317
    +B1333574520101N00552562EA0229102322
    +B1333594520106N00552574EA0229402326
    +B1334014520113N00552584EA0229502329
    +B1334034520124N00552590EA0230002332
    +B1334054520133N00552598EA0230302336
    +B1334074520142N00552607EA0230502340
    +B1334094520153N00552612EA0230802342
    +B1334114520164N00552616EA0231202346
    +B1334134520175N00552621EA0231602351
    +B1334154520186N00552626EA0232002354
    +B1334174520197N00552631EA0232502359
    +B1334194520208N00552634EA0232802363
    +B1334214520219N00552636EA0233002366
    +B1334234520231N00552637EA0233202369
    +B1334254520242N00552635EA0233302371
    +B1334274520253N00552635EA0233302372
    +B1334294520265N00552635EA0233302372
    +B1334314520276N00552634EA0233402373
    +B1334334520288N00552633EA0233402373
    +B1334354520299N00552630EA0233302373
    +B1334374520311N00552627EA0233302373
    +B1334394520322N00552623EA0233202372
    +B1334414520333N00552617EA0233102371
    +B1334434520343N00552610EA0233002370
    +B1334454520353N00552604EA0232802369
    +B1334474520364N00552599EA0232602367
    +B1334494520376N00552597EA0232502365
    +B1334514520388N00552598EA0232402364
    +B1334534520400N00552600EA0232302363
    +B1334554520412N00552601EA0232002361
    +B1334574520424N00552603EA0231902359
    +B1334594520436N00552602EA0231802357
    +B1335014520448N00552601EA0231602356
    +B1335034520459N00552600EA0231402354
    +B1335054520470N00552599EA0231302352
    +B1335074520481N00552600EA0231102350
    +B1335094520492N00552601EA0230802348
    +B1335114520503N00552604EA0230502345
    +B1335134520515N00552606EA0230502343
    +B1335154520527N00552607EA0230502342
    +B1335174520537N00552609EA0230402342
    +B1335194520548N00552610EA0230402341
    +B1335214520560N00552610EA0230602342
    +B1335234520570N00552610EA0230702343
    +B1335254520580N00552609EA0230702343
    +B1335274520592N00552609EA0230802344
    +B1335294520603N00552611EA0230902346
    +B1335314520612N00552611EA0230602346
    +B1335334520623N00552610EA0230302345
    +B1335354520636N00552608EA0230402344
    +B1335374520647N00552607EA0230502345
    +B1335394520657N00552606EA0230202344
    +B1335414520667N00552606EA0230102342
    +B1335434520679N00552606EA0230302341
    +B1335454520690N00552606EA0230602342
    +B1335474520702N00552606EA0230702344
    +B1335494520714N00552605EA0231002346
    +B1335514520726N00552604EA0231202349
    +B1335534520738N00552604EA0231402350
    +B1335554520751N00552604EA0231602352
    +B1335574520765N00552605EA0231802355
    +B1335594520777N00552607EA0232002357
    +B1336014520790N00552609EA0232202358
    +B1336034520803N00552612EA0232302360
    +B1336054520817N00552615EA0232402361
    +B1336074520831N00552617EA0232602362
    +B1336094520846N00552618EA0233102364
    +B1336114520857N00552617EA0233202367
    +B1336134520866N00552618EA0232902368
    +B1336154520878N00552617EA0232702367
    +B1336174520890N00552615EA0232402364
    +B1336194520901N00552613EA0232002361
    +B1336214520911N00552613EA0231502356
    +B1336234520923N00552615EA0231202353
    +B1336254520932N00552619EA0231002350
    +B1336274520940N00552623EA0230502346
    +B1336294520949N00552626EA0230102341
    +B1336314520961N00552627EA0230002338
    +B1336334520969N00552631EA0229802336
    +B1336354520976N00552636EA0229202331
    +B1336374520986N00552637EA0228902327
    +B1336394520998N00552639EA0229102324
    +B1336414521010N00552640EA0229302323
    +B1336434521019N00552639EA0229302322
    +B1336454521029N00552638EA0229202322
    +B1336474521040N00552638EA0229302324
    +B1336494521049N00552638EA0229102323
    +B1336514521059N00552638EA0228902321
    +B1336534521071N00552637EA0229002321
    +B1336554521082N00552639EA0229102322
    +B1336574521090N00552641EA0228902322
    +B1336594521101N00552644EA0228702320
    +B1337014521114N00552648EA0228802320
    +B1337034521125N00552650EA0228902322
    +B1337054521136N00552650EA0228902322
    +B1337074521149N00552650EA0229102322
    +B1337094521161N00552650EA0229302323
    +B1337114521172N00552650EA0229302324
    +B1337134521184N00552650EA0229402325
    +B1337154521198N00552650EA0229702327
    +B1337174521210N00552650EA0229702329
    +B1337194521221N00552650EA0229602329
    +B1337214521233N00552648EA0229502328
    +B1337234521247N00552646EA0229802329
    +B1337254521259N00552646EA0230102332
    +B1337274521270N00552645EA0230502335
    +B1337294521281N00552645EA0230802339
    +B1337314521290N00552649EA0230902342
    +B1337334521299N00552651EA0230902345
    +B1337354521310N00552651EA0231002345
    +B1337374521321N00552653EA0231202347
    +B1337394521332N00552655EA0231402349
    +B1337414521342N00552656EA0231502350
    +B1337434521354N00552657EA0231502351
    +B1337454521367N00552658EA0231602351
    +B1337474521379N00552656EA0231802353
    +B1337494521390N00552654EA0231802354
    +B1337514521401N00552652EA0231702353
    +B1337534521412N00552651EA0231702354
    +B1337554521423N00552651EA0231702353
    +B1337574521435N00552653EA0231902354
    +B1337594521446N00552653EA0232202355
    +B1338014521456N00552654EA0232302357
    +B1338034521466N00552657EA0232302357
    +B1338054521478N00552661EA0232602358
    +B1338074521490N00552666EA0233002360
    +B1338094521501N00552668EA0233302363
    +B1338114521512N00552669EA0233802366
    +B1338134521523N00552671EA0234202371
    +B1338154521533N00552671EA0234702375
    +B1338174521543N00552670EA0235402380
    +B1338194521552N00552669EA0236002387
    +B1338214521561N00552668EA0236602394
    +B1338234521569N00552663EA0237002400
    +B1338254521575N00552653EA0237302405
    +B1338274521569N00552636EA0237302409
    +B1338294521556N00552626EA0237402412
    +B1338314521542N00552631EA0237702415
    +B1338334521535N00552645EA0237902418
    +B1338354521536N00552662EA0238602422
    +B1338374521543N00552675EA0239202428
    +B1338394521552N00552685EA0239802435
    +B1338414521561N00552694EA0240402441
    +B1338434521571N00552701EA0240902446
    +B1338454521582N00552706EA0241302450
    +B1338474521593N00552711EA0241602454
    +B1338494521604N00552717EA0241602457
    +B1338514521615N00552722EA0241502458
    +B1338534521626N00552730EA0241402458
    +B1338554521637N00552740EA0241102456
    +B1338574521649N00552747EA0240802453
    +B1338594521661N00552754EA0240802451
    +B1339014521674N00552760EA0241002450
    +B1339034521686N00552766EA0241202450
    +B1339054521696N00552769EA0240902450
    +B1339074521706N00552770EA0240502447
    +B1339094521717N00552772EA0240102443
    +B1339114521728N00552773EA0239902439
    +B1339134521740N00552774EA0240202437
    +B1339154521749N00552775EA0240302436
    +B1339174521757N00552777EA0240202435
    +B1339194521767N00552778EA0240302434
    +B1339214521778N00552779EA0240402435
    +B1339234521788N00552782EA0240402436
    +B1339254521797N00552785EA0240202436
    +B1339274521808N00552785EA0240102435
    +B1339294521818N00552787EA0239802433
    +B1339314521829N00552788EA0239702432
    +B1339334521839N00552791EA0239602430
    +B1339354521848N00552795EA0239502430
    +B1339374521859N00552797EA0239902430
    +B1339394521870N00552799EA0240402431
    +B1339414521880N00552802EA0240802435
    +B1339434521889N00552804EA0241102440
    +B1339454521898N00552806EA0241102442
    +B1339474521907N00552809EA0241102443
    +B1339494521919N00552811EA0241402444
    +B1339514521929N00552812EA0241202446
    +B1339534521938N00552814EA0241002447
    +B1339554521950N00552816EA0241202446
    +B1339574521963N00552817EA0241702448
    +B1339594521973N00552818EA0242102451
    +B1340014521982N00552819EA0242302453
    +B1340034521993N00552818EA0243002457
    +B1340054522003N00552819EA0243602462
    +B1340074522013N00552820EA0243802465
    +B1340094522023N00552820EA0243802467
    +B1340114522035N00552819EA0244102469
    +B1340134522046N00552818EA0244502472
    +B1340154522056N00552819EA0244902476
    +B1340174522065N00552821EA0245402480
    +B1340194522075N00552820EA0245902485
    +B1340214522083N00552818EA0246502493
    +B1340234522093N00552817EA0246902497
    +B1340254522100N00552816EA0247202500
    +B1340274522110N00552815EA0247702504
    +B1340294522119N00552814EA0247902508
    +B1340314522129N00552812EA0248302511
    +B1340334522140N00552811EA0248902515
    +B1340354522151N00552814EA0249802521
    +B1340374522160N00552818EA0250202528
    +B1340394522169N00552822EA0250402533
    +B1340414522178N00552827EA0250402536
    +B1340434522190N00552833EA0250602538
    +B1340454522202N00552840EA0250802540
    +B1340474522213N00552848EA0251202544
    +B1340494522224N00552856EA0251302546
    +B1340514522235N00552865EA0251302548
    +B1340534522246N00552873EA0251302548
    +B1340554522257N00552879EA0251202548
    +B1340574522268N00552884EA0250802547
    +B1340594522279N00552890EA0250302544
    +B1341014522290N00552894EA0249702540
    +B1341034522301N00552899EA0249102534
    +B1341054522313N00552903EA0248702529
    +B1341074522325N00552906EA0248502526
    +B1341094522337N00552910EA0248102523
    +B1341114522348N00552914EA0247802519
    +B1341134522360N00552920EA0247602516
    +B1341154522371N00552926EA0247402514
    +B1341174522381N00552932EA0247102511
    +B1341194522393N00552942EA0246902507
    +B1341214522405N00552954EA0247502505
    +B1341234522416N00552961EA0248002505
    +B1341254522426N00552968EA0248202508
    +B1341274522438N00552976EA0248602512
    +B1341294522450N00552982EA0249402517
    +B1341314522461N00552989EA0249902524
    +B1341334522471N00552997EA0250502530
    +B1341354522481N00553003EA0251102537
    +B1341374522492N00553012EA0251602543
    +B1341394522502N00553024EA0252202550
    +B1341414522512N00553034EA0252402555
    +B1341434522522N00553044EA0252402559
    +B1341454522533N00553053EA0252602561
    +B1341474522545N00553060EA0252802564
    +B1341494522556N00553066EA0253002566
    +B1341514522565N00553074EA0253102568
    +B1341534522576N00553081EA0253202570
    +B1341554522588N00553088EA0253302570
    +B1341574522601N00553095EA0253502572
    +B1341594522613N00553105EA0253802575
    +B1342014522625N00553115EA0253802577
    +B1342034522637N00553125EA0253702578
    +B1342054522650N00553137EA0253502577
    +B1342074522661N00553152EA0253402575
    +B1342094522673N00553166EA0253202574
    +B1342114522683N00553180EA0252802572
    +B1342134522694N00553196EA0252402568
    +B1342154522706N00553207EA0251902564
    +B1342174522718N00553218EA0251202558
    +B1342194522730N00553230EA0250602552
    +B1342214522744N00553240EA0250202547
    +B1342234522757N00553250EA0249702542
    +B1342254522771N00553259EA0249102536
    +B1342274522785N00553270EA0248402529
    +B1342294522798N00553279EA0248002524
    +B1342314522811N00553284EA0247702520
    +B1342334522824N00553288EA0247502516
    +B1342354522837N00553293EA0247202513
    +B1342374522850N00553299EA0246902510
    +B1342394522863N00553305EA0246702507
    +B1342414522874N00553312EA0246402505
    +B1342434522887N00553316EA0246002501
    +B1342454522900N00553321EA0245802498
    +B1342474522912N00553327EA0245502496
    +B1342494522924N00553333EA0245102492
    +B1342514522937N00553338EA0245002489
    +B1342534522950N00553344EA0245102487
    +B1342554522961N00553351EA0244902486
    +B1342574522973N00553359EA0244702483
    +B1342594522985N00553366EA0244602482
    +B1343014522998N00553372EA0244502481
    +B1343034523011N00553376EA0244202479
    +B1343054523023N00553380EA0243902478
    +B1343074523036N00553386EA0243702477
    +B1343094523047N00553393EA0243602476
    +B1343114523060N00553399EA0243402475
    +B1343134523072N00553404EA0243402474
    +B1343154523085N00553408EA0243202472
    +B1343174523098N00553411EA0243202471
    +B1343194523111N00553413EA0243102471
    +B1343214523124N00553415EA0242902470
    +B1343234523137N00553419EA0242702467
    +B1343254523150N00553422EA0242502465
    +B1343274523163N00553426EA0242502464
    +B1343294523175N00553430EA0242202463
    +B1343314523187N00553435EA0241902460
    +B1343334523200N00553441EA0241702458
    +B1343354523211N00553449EA0241502456
    +B1343374523222N00553455EA0241002453
    +B1343394523234N00553460EA0240502449
    +B1343414523246N00553467EA0240302446
    +B1343434523257N00553476EA0240002443
    +B1343454523268N00553479EA0239502439
    +B1343474523281N00553480EA0239302436
    +B1343494523292N00553485EA0239002433
    +B1343514523302N00553490EA0238602429
    +B1343534523314N00553494EA0238402426
    +B1343554523326N00553498EA0238202424
    +B1343574523337N00553503EA0237902421
    +B1343594523348N00553511EA0237802418
    +B1344014523358N00553520EA0237602414
    +B1344034523369N00553529EA0237102410
    +B1344054523379N00553537EA0236902408
    +B1344074523389N00553546EA0236602406
    +B1344094523399N00553556EA0236402403
    +B1344114523410N00553564EA0236202401
    +B1344134523421N00553572EA0236002399
    +B1344154523432N00553578EA0235702396
    +B1344174523443N00553583EA0235502394
    +B1344194523455N00553588EA0235402392
    +B1344214523466N00553592EA0235202390
    +B1344234523478N00553595EA0235102388
    +B1344254523490N00553600EA0235002387
    +B1344274523501N00553606EA0234902385
    +B1344294523512N00553613EA0234602383
    +B1344314523523N00553620EA0234602382
    +B1344334523534N00553627EA0234602381
    +B1344354523544N00553633EA0234402379
    +B1344374523555N00553640EA0234102377
    +B1344394523566N00553645EA0234002375
    +B1344414523577N00553650EA0233702373
    +B1344434523588N00553655EA0233402371
    +B1344454523599N00553660EA0233302369
    +B1344474523611N00553665EA0233202368
    +B1344494523621N00553668EA0232802365
    +B1344514523633N00553672EA0232402362
    +B1344534523644N00553677EA0232202359
    +B1344554523657N00553681EA0232102356
    +B1344574523669N00553683EA0232002355
    +B1344594523679N00553686EA0231702353
    +B1345014523691N00553689EA0231502350
    +B1345034523704N00553691EA0231402349
    +B1345054523715N00553694EA0231202348
    +B1345074523726N00553698EA0230902345
    +B1345094523737N00553704EA0230702343
    +B1345114523748N00553711EA0230802342
    +B1345134523757N00553719EA0230502340
    +B1345154523767N00553727EA0230102337
    +B1345174523777N00553737EA0230002335
    +B1345194523787N00553746EA0229802333
    +B1345214523799N00553751EA0229402330
    +B1345234523811N00553755EA0229202327
    +B1345254523823N00553760EA0229002324
    +B1345274523835N00553764EA0228802322
    +B1345294523847N00553768EA0228302319
    +B1345314523860N00553771EA0228102316
    +B1345334523872N00553775EA0227902314
    +B1345354523884N00553779EA0227602311
    +B1345374523896N00553783EA0227302308
    +B1345394523908N00553787EA0227002305
    +B1345414523920N00553791EA0226702302
    +B1345434523932N00553795EA0226302299
    +B1345454523945N00553798EA0226002296
    +B1345474523957N00553801EA0225702293
    +B1345494523970N00553804EA0225502290
    +B1345514523982N00553807EA0225302288
    +B1345534523994N00553809EA0225002286
    +B1345554524006N00553812EA0224802283
    +B1345574524018N00553816EA0224702281
    +B1345594524030N00553818EA0224502280
    +B1346014524041N00553821EA0224302279
    +B1346034524052N00553825EA0224002276
    +B1346054524064N00553829EA0223802273
    +B1346074524076N00553832EA0223702272
    +B1346094524087N00553835EA0223402270
    +B1346114524099N00553838EA0223302267
    +B1346134524111N00553841EA0223102266
    +B1346154524123N00553844EA0222902264
    +B1346174524135N00553849EA0222602262
    +B1346194524146N00553853EA0222502260
    +B1346214524158N00553858EA0222402259
    +B1346234524170N00553862EA0222302258
    +B1346254524181N00553867EA0222102257
    +B1346274524192N00553871EA0222002256
    +B1346294524204N00553876EA0221902255
    +B1346314524216N00553880EA0221802254
    +B1346334524227N00553885EA0221602252
    +B1346354524239N00553889EA0221402251
    +B1346374524251N00553894EA0221102248
    +B1346394524263N00553899EA0220802245
    +B1346414524275N00553903EA0220602243
    +B1346434524286N00553907EA0220402240
    +B1346454524297N00553911EA0220102238
    +B1346474524307N00553915EA0219602234
    +B1346494524318N00553919EA0219502231
    +B1346514524329N00553924EA0219402229
    +B1346534524339N00553928EA0219002227
    +B1346554524350N00553933EA0218702224
    +B1346574524362N00553938EA0218602222
    +B1346594524374N00553940EA0218602222
    +B1347014524385N00553942EA0218502220
    +B1347034524397N00553946EA0218602219
    +B1347054524409N00553950EA0218502220
    +B1347074524420N00553951EA0218302219
    +B1347094524430N00553952EA0218002217
    +B1347114524441N00553955EA0217902215
    +B1347134524451N00553957EA0217602213
    +B1347154524461N00553957EA0217202210
    +B1347174524471N00553960EA0217002207
    +B1347194524480N00553963EA0216602204
    +B1347214524487N00553967EA0216102200
    +B1347234524496N00553973EA0215602195
    +B1347254524505N00553977EA0215302192
    +B1347274524513N00553981EA0214802187
    +B1347294524521N00553986EA0214102182
    +B1347314524530N00553989EA0213402175
    +B1347334524540N00553991EA0212702168
    +B1347354524548N00553993EA0212002161
    +B1347374524558N00553996EA0211302153
    +B1347394524568N00554000EA0210702146
    +B1347414524578N00554005EA0210202140
    +B1347434524589N00554009EA0209802135
    +B1347454524600N00554011EA0209402130
    +B1347474524608N00554014EA0208902126
    +B1347494524618N00554017EA0208302121
    +B1347514524629N00554019EA0208002116
    +B1347534524639N00554022EA0207802113
    +B1347554524647N00554025EA0207202109
    +B1347574524656N00554027EA0206502103
    +B1347594524667N00554032EA0206302098
    +B1348014524677N00554036EA0206302095
    +B1348034524686N00554040EA0206002093
    +B1348054524696N00554044EA0205502088
    +B1348074524708N00554046EA0205302085
    +B1348094524719N00554046EA0204902082
    +B1348114524730N00554047EA0204502078
    +B1348134524742N00554048EA0204302075
    +B1348154524755N00554050EA0204202073
    +B1348174524766N00554050EA0204002072
    +B1348194524778N00554052EA0203602069
    +B1348214524790N00554055EA0203402067
    +B1348234524802N00554057EA0203302066
    +B1348254524813N00554057EA0202902063
    +B1348274524824N00554060EA0202502059
    +B1348294524836N00554063EA0202302056
    +B1348314524848N00554065EA0202102054
    +B1348334524859N00554068EA0201802052
    +B1348354524870N00554069EA0201802050
    +B1348374524882N00554071EA0202302049
    +B1348394524893N00554073EA0202802049
    +B1348414524903N00554076EA0202802051
    +B1348434524913N00554079EA0202502051
    +B1348454524925N00554082EA0202402050
    +B1348474524936N00554086EA0202302050
    +B1348494524946N00554090EA0202302050
    +B1348514524957N00554094EA0202502050
    +B1348534524967N00554102EA0203102052
    +B1348554524975N00554111EA0203802055
    +B1348574524983N00554119EA0204402061
    +B1348594524993N00554121EA0204902067
    +B1349014525000N00554106EA0205202074
    +B1349034524991N00554090EA0205602080
    +B1349054524978N00554092EA0206002086
    +B1349074524971N00554107EA0206502091
    +B1349094524975N00554124EA0207202098
    +B1349114524985N00554130EA0207902106
    +B1349134524996N00554124EA0208602114
    +B1349154525000N00554109EA0209002121
    +B1349174524995N00554094EA0209402127
    +B1349194524985N00554090EA0209402131
    +B1349214524974N00554099EA0209802134
    +B1349234524972N00554117EA0210302137
    +B1349254524976N00554134EA0211102144
    +B1349274524980N00554148EA0211702152
    +B1349294524988N00554157EA0212002155
    +B1349314525001N00554157EA0212502160
    +B1349334525008N00554143EA0212602163
    +B1349354525002N00554123EA0213102166
    +B1349374524988N00554119EA0213602170
    +B1349394524978N00554130EA0214002174
    +B1349414524977N00554149EA0214502178
    +B1349434524985N00554165EA0214902183
    +B1349454524998N00554169EA0215402188
    +B1349474525008N00554163EA0215802192
    +B1349494525015N00554149EA0216002196
    +B1349514525014N00554131EA0216602199
    +B1349534525004N00554120EA0217302204
    +B1349554524994N00554128EA0217702208
    +B1349574524992N00554146EA0218302212
    +B1349594525002N00554156EA0218602216
    +B1350014525014N00554150EA0218802220
    +B1350034525020N00554133EA0219202225
    +B1350054525015N00554115EA0219802229
    +B1350074525004N00554106EA0220502236
    +B1350094524994N00554111EA0221002241
    +B1350114524987N00554124EA0221302246
    +B1350134524984N00554139EA0221702250
    +B1350154524989N00554155EA0222102255
    +B1350174525000N00554159EA0222402259
    +B1350194525011N00554152EA0222802262
    +B1350214525018N00554138EA0223102266
    +B1350234525019N00554119EA0223802270
    +B1350254525012N00554101EA0224502276
    +B1350274525007N00554089EA0225102283
    +B1350294525002N00554082EA0225002288
    +B1350314524990N00554086EA0224802291
    +B1350334524980N00554100EA0224902293
    +B1350354524979N00554118EA0225202295
    +B1350374524987N00554131EA0225802298
    +B1350394524996N00554134EA0226202302
    +B1350414525007N00554132EA0226502305
    +B1350434525017N00554123EA0226802307
    +B1350454525024N00554108EA0226902309
    +B1350474525023N00554088EA0227002311
    +B1350494525013N00554077EA0227002312
    +B1350514525004N00554079EA0226802310
    +B1350534524996N00554085EA0226302306
    +B1350554524990N00554100EA0226202302
    +B1350574524994N00554115EA0226502300
    +B1350594525001N00554124EA0226802300
    +B1351014525010N00554129EA0227202303
    +B1351034525019N00554133EA0227302305
    +B1351054525027N00554140EA0227502307
    +B1351074525036N00554149EA0227902309
    +B1351094525046N00554154EA0228702314
    +B1351114525055N00554156EA0229302320
    +B1351134525063N00554159EA0229502324
    +B1351154525074N00554154EA0229402327
    +B1351174525076N00554139EA0229102329
    +B1351194525067N00554127EA0229202330
    +B1351214525056N00554131EA0229002331
    +B1351234525047N00554143EA0229002330
    +B1351254525045N00554161EA0229102330
    +B1351274525051N00554180EA0229302330
    +B1351294525061N00554194EA0229802333
    +B1351314525073N00554199EA0230202337
    +B1351334525084N00554202EA0230802343
    +B1351354525092N00554209EA0231202348
    +B1351374525100N00554211EA0231302352
    +B1351394525104N00554202EA0231402354
    +B1351414525099N00554189EA0231902355
    +B1351434525087N00554187EA0232202358
    +B1351454525075N00554200EA0232402360
    +B1351474525068N00554219EA0232602362
    +B1351494525069N00554241EA0233002365
    +B1351514525076N00554257EA0233202368
    +B1351534525088N00554265EA0233302369
    +B1351554525101N00554260EA0233802371
    +B1351574525104N00554246EA0234402373
    +B1351594525093N00554235EA0234802378
    +B1352014525080N00554243EA0234902380
    +B1352034525075N00554264EA0234902382
    +B1352054525078N00554285EA0235102383
    +B1352074525091N00554298EA0235602389
    +B1352094525104N00554296EA0236202393
    +B1352114525112N00554283EA0236702399
    +B1352134525113N00554268EA0237402405
    +B1352154525103N00554261EA0237602410
    +B1352174525091N00554268EA0237902414
    +B1352194525085N00554288EA0238002417
    +B1352214525090N00554307EA0238202418
    +B1352234525105N00554313EA0238902422
    +B1352254525118N00554312EA0239602429
    +B1352274525128N00554306EA0240402437
    +B1352294525134N00554298EA0241102445
    +B1352314525133N00554287EA0241502451
    +B1352334525121N00554279EA0242002456
    +B1352354525110N00554291EA0242402460
    +B1352374525109N00554312EA0243002464
    +B1352394525119N00554326EA0243702470
    +B1352414525129N00554333EA0244302476
    +B1352434525139N00554337EA0244902482
    +B1352454525152N00554343EA0245502488
    +B1352474525166N00554349EA0246102493
    +B1352494525180N00554352EA0246702499
    +B1352514525193N00554352EA0247202505
    +B1352534525205N00554355EA0247602510
    +B1352554525217N00554358EA0248002514
    +B1352574525228N00554363EA0248602520
    +B1352594525234N00554375EA0248602524
    +B1353014525240N00554393EA0249002527
    +B1353034525248N00554410EA0249502533
    +B1353054525257N00554421EA0249702537
    +B1353074525267N00554433EA0249802540
    +B1353094525277N00554447EA0250202543
    +B1353114525288N00554459EA0250502547
    +B1353134525297N00554469EA0250602549
    +B1353154525307N00554480EA0250602550
    +B1353174525318N00554490EA0250402549
    +B1353194525329N00554500EA0250302548
    +B1353214525340N00554508EA0250102546
    +B1353234525353N00554513EA0249902544
    +B1353254525366N00554519EA0250002543
    +B1353274525379N00554527EA0250102543
    +B1353294525392N00554534EA0250002543
    +B1353314525406N00554537EA0249802542
    +B1353334525420N00554546EA0249902541
    +B1353354525433N00554556EA0249902541
    +B1353374525445N00554562EA0249802541
    +B1353394525459N00554567EA0249702539
    +B1353414525473N00554574EA0249702539
    +B1353434525486N00554581EA0249602538
    +B1353454525500N00554587EA0249502537
    +B1353474525515N00554594EA0249402537
    +B1353494525529N00554600EA0249402536
    +B1353514525544N00554604EA0249302535
    +B1353534525558N00554609EA0249202534
    +B1353554525573N00554612EA0249102533
    +B1353574525587N00554615EA0248802532
    +B1353594525600N00554618EA0248602530
    +B1354014525613N00554620EA0248402527
    +B1354034525625N00554620EA0248202525
    +B1354054525635N00554619EA0247902523
    +B1354074525645N00554619EA0247502520
    +B1354094525656N00554618EA0247102515
    +B1354114525668N00554618EA0246802512
    +B1354134525680N00554620EA0246502509
    +B1354154525693N00554620EA0246202506
    +B1354174525705N00554618EA0246002502
    +B1354194525719N00554616EA0245902500
    +B1354214525732N00554614EA0245902499
    +B1354234525745N00554612EA0246002499
    +B1354254525758N00554609EA0246302500
    +B1354274525771N00554608EA0246802502
    +B1354294525783N00554610EA0247202506
    +B1354314525794N00554612EA0247402509
    +B1354334525805N00554614EA0247702511
    +B1354354525817N00554619EA0247902513
    +B1354374525828N00554619EA0248102516
    +B1354394525841N00554621EA0248402519
    +B1354414525853N00554628EA0248702522
    +B1354434525865N00554637EA0248802525
    +B1354454525878N00554645EA0248902526
    +B1354474525891N00554652EA0249002527
    +B1354494525905N00554660EA0249102528
    +B1354514525916N00554664EA0248802528
    +B1354534525927N00554667EA0248202527
    +B1354554525939N00554671EA0247802524
    +B1354574525952N00554675EA0247702522
    +B1354594525963N00554677EA0247502522
    +B1355014525973N00554680EA0247202519
    +B1355034525984N00554683EA0247002516
    +B1355054525997N00554686EA0246702513
    +B1355074526009N00554689EA0246502510
    +B1355094526021N00554694EA0246202507
    +B1355114526034N00554700EA0246002505
    +B1355134526046N00554706EA0245802503
    +B1355154526058N00554711EA0245502500
    +B1355174526071N00554719EA0245302497
    +B1355194526083N00554725EA0245202495
    +B1355214526095N00554728EA0244802492
    +B1355234526108N00554732EA0244602489
    +B1355254526120N00554739EA0244202486
    +B1355274526132N00554744EA0243902483
    +B1355294526143N00554749EA0243502479
    +B1355314526154N00554756EA0242902474
    +B1355334526164N00554764EA0242302469
    +B1355354526175N00554772EA0241902463
    +B1355374526186N00554782EA0241602460
    +B1355394526197N00554790EA0241202455
    +B1355414526209N00554796EA0241102451
    +B1355434526223N00554803EA0241602449
    +B1355454526234N00554809EA0241902448
    +B1355474526242N00554816EA0241702448
    +B1355494526251N00554818EA0241302446
    +B1355514526261N00554817EA0241402445
    +B1355534526272N00554820EA0241502446
    +B1355554526283N00554823EA0241402447
    +B1355574526295N00554825EA0241302446
    +B1355594526309N00554827EA0241502446
    +B1356014526323N00554825EA0241802448
    +B1356034526334N00554824EA0241802450
    +B1356054526346N00554824EA0241602450
    +B1356074526358N00554823EA0241502449
    +B1356094526372N00554824EA0241502449
    +B1356114526386N00554828EA0241302449
    +B1356134526399N00554831EA0241102448
    +B1356154526413N00554832EA0241002446
    +B1356174526426N00554831EA0240802445
    +B1356194526439N00554834EA0240602443
    +B1356214526453N00554839EA0240502441
    +B1356234526468N00554844EA0240402440
    +B1356254526482N00554850EA0240202439
    +B1356274526495N00554856EA0239702436
    +B1356294526510N00554862EA0239202432
    +B1356314526523N00554867EA0238802427
    +B1356334526537N00554871EA0238302423
    +B1356354526551N00554876EA0238002419
    +B1356374526564N00554879EA0237602415
    +B1356394526577N00554881EA0237102411
    +B1356414526590N00554882EA0236802407
    +B1356434526603N00554883EA0236402403
    +B1356454526616N00554883EA0236002399
    +B1356474526630N00554882EA0235602396
    +B1356494526643N00554881EA0235202392
    +B1356514526657N00554882EA0234802388
    +B1356534526671N00554882EA0234602384
    +B1356554526685N00554884EA0234302382
    +B1356574526698N00554887EA0234002378
    +B1356594526712N00554890EA0233602374
    +B1357014526727N00554893EA0233202371
    +B1357034526742N00554897EA0233002368
    +B1357054526756N00554901EA0232702365
    +B1357074526770N00554906EA0232302361
    +B1357094526784N00554910EA0232002358
    +B1357114526798N00554915EA0231702355
    +B1357134526812N00554921EA0231402352
    +B1357154526826N00554926EA0231102349
    +B1357174526840N00554931EA0230902346
    +B1357194526854N00554936EA0230702344
    +B1357214526868N00554941EA0230402342
    +B1357234526882N00554945EA0230202339
    +B1357254526896N00554950EA0229902337
    +B1357274526910N00554955EA0229702334
    +B1357294526925N00554960EA0229502331
    +B1357314526939N00554965EA0229402330
    +B1357334526951N00554971EA0229202328
    +B1357354526964N00554978EA0228802325
    +B1357374526978N00554985EA0228502322
    +B1357394526992N00554991EA0228202319
    +B1357414527005N00554997EA0227802315
    +B1357434527019N00555005EA0227502312
    +B1357454527032N00555012EA0227102309
    +B1357474527045N00555018EA0226602305
    +B1357494527058N00555024EA0226202300
    +B1357514527071N00555030EA0225702295
    +B1357534527084N00555037EA0225202290
    +B1357554527097N00555044EA0224702285
    +B1357574527111N00555053EA0224402280
    +B1357594527124N00555061EA0224302277
    +B1358014527136N00555071EA0224102275
    +B1358034527149N00555080EA0223902273
    +B1358054527161N00555088EA0223702272
    +B1358074527173N00555098EA0223602270
    +B1358094527186N00555107EA0223602269
    +B1358114527197N00555113EA0223402269
    +B1358134527209N00555119EA0223102267
    +B1358154527222N00555126EA0223002265
    +B1358174527235N00555135EA0222902265
    +B1358194527245N00555144EA0222402263
    +B1358214527257N00555153EA0222002260
    +B1358234527271N00555161EA0221902258
    +B1358254527283N00555172EA0221902257
    +B1358274527293N00555181EA0221702255
    +B1358294527306N00555189EA0221402252
    +B1358314527318N00555197EA0221302250
    +B1358334527331N00555203EA0221002248
    +B1358354527344N00555208EA0220902246
    +B1358374527357N00555212EA0220702245
    +B1358394527370N00555216EA0220402242
    +B1358414527384N00555220EA0220202240
    +B1358434527397N00555223EA0220102237
    +B1358454527412N00555228EA0220002236
    +B1358474527426N00555232EA0219902235
    +B1358494527439N00555236EA0219802234
    +B1358514527453N00555239EA0219602232
    +B1358534527467N00555242EA0219402230
    +B1358554527481N00555248EA0219202229
    +B1358574527494N00555255EA0219002227
    +B1358594527507N00555261EA0218702224
    +B1359014527520N00555268EA0218402222
    +B1359034527533N00555274EA0218202219
    +B1359054527546N00555280EA0217902216
    +B1359074527560N00555286EA0217502213
    +B1359094527573N00555291EA0217202209
    +B1359114527587N00555294EA0216902206
    +B1359134527600N00555297EA0216602203
    +B1359154527614N00555302EA0216302200
    +B1359174527628N00555306EA0215902196
    +B1359194527641N00555310EA0215502193
    +B1359214527655N00555314EA0215202189
    +B1359234527668N00555317EA0214802185
    +B1359254527682N00555321EA0214602182
    +B1359274527695N00555326EA0214202178
    +B1359294527708N00555330EA0213802174
    +B1359314527721N00555335EA0213602171
    +B1359334527734N00555341EA0213302168
    +B1359354527747N00555347EA0212902164
    +B1359374527760N00555353EA0212702161
    +B1359394527773N00555360EA0212502158
    +B1359414527786N00555367EA0212202156
    +B1359434527799N00555374EA0211802152
    +B1359454527812N00555381EA0211602150
    +B1359474527825N00555387EA0211402148
    +B1359494527837N00555392EA0211202146
    +B1359514527848N00555400EA0211002144
    +B1359534527859N00555407EA0210702142
    +B1359554527869N00555413EA0210502139
    +B1359574527879N00555419EA0210202137
    +B1359594527890N00555427EA0210102135
    +B1400014527900N00555434EA0210102134
    +B1400034527910N00555438EA0209902133
    +B1400054527920N00555442EA0209802132
    +B1400074527930N00555448EA0209702131
    +B1400094527940N00555453EA0209702131
    +B1400114527949N00555457EA0209402130
    +B1400134527960N00555464EA0209002128
    +B1400154527972N00555473EA0209002126
    +B1400174527984N00555481EA0208802125
    +B1400194527994N00555489EA0208402122
    +B1400214528007N00555496EA0207902117
    +B1400234528020N00555501EA0207702115
    +B1400254528032N00555505EA0207302112
    +B1400274528045N00555509EA0206902107
    +B1400294528058N00555516EA0206702104
    +B1400314528071N00555523EA0206702102
    +B1400334528082N00555528EA0206502100
    +B1400354528094N00555532EA0206102097
    +B1400374528107N00555535EA0205902094
    +B1400394528119N00555538EA0205702092
    +B1400414528131N00555544EA0205502090
    +B1400434528143N00555550EA0205402088
    +B1400454528154N00555556EA0205202087
    +B1400474528165N00555563EA0205002085
    +B1400494528176N00555572EA0204802083
    +B1400514528186N00555582EA0204402081
    +B1400534528195N00555591EA0204002077
    +B1400554528207N00555603EA0204002074
    +B1400574528217N00555617EA0204202073
    +B1400594528225N00555628EA0204202073
    +B1401014528234N00555638EA0204002072
    +B1401034528242N00555650EA0203902070
    +B1401054528251N00555662EA0203802068
    +B1401074528260N00555674EA0203702068
    +B1401094528269N00555686EA0203402066
    +B1401114528278N00555698EA0203202064
    +B1401134528289N00555711EA0203202063
    +B1401154528297N00555726EA0203002062
    +B1401174528305N00555741EA0202702060
    +B1401194528313N00555758EA0202402057
    +B1401214528321N00555774EA0202202055
    +B1401234528330N00555789EA0201902053
    +B1401254528338N00555804EA0201602049
    +B1401274528347N00555819EA0201502047
    +B1401294528356N00555833EA0201402045
    +B1401314528365N00555847EA0201302044
    +B1401334528374N00555861EA0201302043
    +B1401354528383N00555875EA0201302043
    +B1401374528392N00555888EA0201202043
    +B1401394528402N00555901EA0201002042
    +B1401414528412N00555914EA0200902041
    +B1401434528422N00555927EA0200902040
    +B1401454528433N00555939EA0200902041
    +B1401474528443N00555952EA0200902041
    +B1401494528453N00555964EA0200702040
    +B1401514528465N00555976EA0200602039
    +B1401534528476N00555988EA0200402037
    +B1401554528487N00556000EA0200102035
    +B1401574528498N00556014EA0200002033
    +B1401594528508N00556027EA0199802032
    +B1402014528518N00556039EA0199402029
    +B1402034528529N00556051EA0199102026
    +B1402054528540N00556063EA0199002024
    +B1402074528550N00556073EA0198602022
    +B1402094528561N00556083EA0198202018
    +B1402114528573N00556092EA0197902015
    +B1402134528585N00556101EA0197602012
    +B1402154528595N00556112EA0197202009
    +B1402174528605N00556123EA0196602004
    +B1402194528616N00556136EA0196301999
    +B1402214528627N00556149EA0196001996
    +B1402234528637N00556161EA0195601992
    +B1402254528647N00556173EA0195001987
    +B1402274528657N00556188EA0194701982
    +B1402294528668N00556202EA0194401979
    +B1402314528678N00556215EA0194001975
    +B1402334528689N00556229EA0193601971
    +B1402354528700N00556243EA0193301968
    +B1402374528710N00556257EA0193001965
    +B1402394528720N00556272EA0192501961
    +B1402414528730N00556287EA0192201956
    +B1402434528741N00556301EA0191901953
    +B1402454528750N00556315EA0191501949
    +B1402474528759N00556329EA0191001944
    +B1402494528771N00556344EA0190601940
    +B1402514528781N00556359EA0190401938
    +B1402534528790N00556373EA0190001934
    +B1402554528800N00556388EA0189601930
    +B1402574528811N00556404EA0189301927
    +B1402594528821N00556421EA0189101924
    +B1403014528832N00556436EA0188901922
    +B1403034528842N00556450EA0188801920
    +B1403054528852N00556466EA0188501917
    +B1403074528862N00556480EA0188301915
    +B1403094528873N00556494EA0188101912
    +B1403114528882N00556508EA0187801911
    +B1403134528892N00556522EA0187601908
    +B1403154528903N00556534EA0187301905
    +B1403174528912N00556548EA0187101903
    +B1403194528921N00556562EA0186801900
    +B1403214528930N00556575EA0186501897
    +B1403234528940N00556589EA0186201894
    +B1403254528949N00556603EA0185901891
    +B1403274528958N00556617EA0185601888
    +B1403294528966N00556632EA0185201884
    +B1403314528975N00556647EA0184801880
    +B1403334528984N00556661EA0184401876
    +B1403354528992N00556676EA0184001872
    +B1403374529002N00556690EA0183601868
    +B1403394529011N00556705EA0183301864
    +B1403414529021N00556721EA0183101861
    +B1403434529029N00556736EA0182701859
    +B1403454529038N00556750EA0182301855
    +B1403474529048N00556766EA0182001851
    +B1403494529057N00556782EA0181701848
    +B1403514529067N00556796EA0181301844
    +B1403534529077N00556810EA0180901840
    +B1403554529088N00556825EA0180601836
    +B1403574529098N00556840EA0180301833
    +B1403594529108N00556854EA0180001830
    +B1404014529119N00556867EA0179701827
    +B1404034529129N00556880EA0179401824
    +B1404054529139N00556893EA0179001821
    +B1404074529149N00556908EA0178701817
    +B1404094529159N00556923EA0178401814
    +B1404114529169N00556937EA0178001811
    +B1404134529179N00556951EA0177701807
    +B1404154529189N00556966EA0177401804
    +B1404174529199N00556981EA0177101801
    +B1404194529208N00556996EA0176701798
    +B1404214529219N00557011EA0176401794
    +B1404234529229N00557026EA0176101791
    +B1404254529238N00557042EA0175801788
    +B1404274529248N00557058EA0175501784
    +B1404294529258N00557074EA0175201781
    +B1404314529269N00557090EA0174901778
    +B1404334529279N00557105EA0174701776
    +B1404354529288N00557120EA0174401773
    +B1404374529299N00557135EA0174101769
    +B1404394529309N00557150EA0173801767
    +B1404414529319N00557164EA0173601765
    +B1404434529329N00557178EA0173301762
    +B1404454529339N00557193EA0173101759
    +B1404484529350N00557207EA0173001758
    +B1404504529359N00557219EA0172701756
    +B1404524529369N00557233EA0172401754
    +B1404544529379N00557247EA0172301751
    +B1404564529390N00557261EA0172201750
    +B1404584529399N00557272EA0172001749
    +B1405004529409N00557284EA0171601746
    +B1405024529420N00557297EA0171401744
    +B1405044529431N00557309EA0171201742
    +B1405064529441N00557321EA0171001740
    +B1405084529451N00557333EA0170701737
    +B1405104529461N00557346EA0170401733
    +B1405124529471N00557359EA0170101731
    +B1405144529481N00557372EA0170001729
    +B1405164529490N00557385EA0169801727
    +B1405184529500N00557397EA0169501724
    +B1405204529509N00557410EA0169301721
    +B1405224529519N00557423EA0169201720
    +B1405244529528N00557436EA0168901717
    +B1405264529538N00557448EA0168501714
    +B1405284529548N00557463EA0168201710
    +B1405304529557N00557478EA0167901708
    +B1405324529567N00557492EA0167501704
    +B1405344529576N00557508EA0167201701
    +B1405364529585N00557523EA0167001698
    +B1405384529595N00557538EA0166801696
    +B1405404529604N00557551EA0166701694
    +B1405424529612N00557564EA0166501692
    +B1405444529621N00557578EA0166301690
    +B1405464529629N00557592EA0166001688
    +B1405484529638N00557606EA0165801685
    +B1405504529645N00557620EA0165601683
    +B1405524529654N00557635EA0165401681
    +B1405544529662N00557650EA0165201679
    +B1405564529669N00557666EA0165101677
    +B1405584529677N00557679EA0164801675
    +B1406004529686N00557693EA0164601673
    +B1406024529695N00557706EA0164401670
    +B1406044529704N00557717EA0164201668
    +B1406064529713N00557730EA0163901666
    +B1406084529722N00557743EA0163701663
    +B1406104529732N00557755EA0163601662
    +B1406124529740N00557768EA0163501661
    +B1406144529748N00557780EA0163301658
    +B1406164529757N00557793EA0163101656
    +B1406184529766N00557806EA0162901655
    +B1406204529774N00557817EA0162701653
    +B1406224529783N00557829EA0162401650
    +B1406244529792N00557841EA0162301648
    +B1406264529801N00557851EA0162101647
    +B1406284529810N00557861EA0161801644
    +B1406304529819N00557872EA0161601642
    +B1406324529829N00557883EA0161501640
    +B1406344529837N00557894EA0161201638
    +B1406364529846N00557905EA0160901635
    +B1406384529855N00557917EA0160701633
    +B1406404529864N00557929EA0160401631
    +B1406424529872N00557941EA0160101628
    +B1406444529882N00557952EA0159901625
    +B1406464529892N00557964EA0159701622
    +B1406484529900N00557976EA0159401620
    +B1406504529910N00557988EA0159001617
    +B1406524529920N00557999EA0158801614
    +B1406544529929N00558011EA0158601612
    +B1406564529939N00558022EA0158401609
    +B1406584529948N00558033EA0158101607
    +B1407004529958N00558045EA0157901605
    +B1407024529967N00558056EA0157701603
    +B1407044529976N00558068EA0157401600
    +B1407064529986N00558079EA0157101597
    +B1407084529996N00558090EA0156901594
    +B1407104530005N00558102EA0156601591
    +B1407124530014N00558114EA0156301588
    +B1407144530024N00558127EA0156101586
    +B1407164530033N00558140EA0155901584
    +B1407184530042N00558154EA0155701582
    +B1407204530051N00558167EA0155501580
    +B1407224530059N00558180EA0155201577
    +B1407244530068N00558193EA0155001575
    +B1407264530077N00558206EA0154801573
    +B1407284530086N00558218EA0154601571
    +B1407304530096N00558230EA0154301568
    +B1407324530105N00558242EA0154101565
    +B1407344530114N00558255EA0153901564
    +B1407364530123N00558267EA0153701561
    +B1407384530132N00558280EA0153401559
    +B1407404530141N00558294EA0153301557
    +B1407424530150N00558306EA0153101555
    +B1407444530158N00558319EA0152901553
    +B1407464530167N00558332EA0152701551
    +B1407484530176N00558344EA0152501549
    +B1407504530184N00558356EA0152201547
    +B1407524530193N00558369EA0152001544
    +B1407544530202N00558382EA0151701542
    +B1407564530210N00558395EA0151501539
    +B1407584530219N00558407EA0151301537
    +B1408004530227N00558420EA0151101535
    +B1408024530236N00558433EA0151001534
    +B1408044530244N00558445EA0150801532
    +B1408064530253N00558457EA0150601530
    +B1408084530261N00558470EA0150401528
    +B1408104530269N00558482EA0150201526
    +B1408124530277N00558494EA0150001523
    +B1408144530285N00558506EA0149701521
    +B1408164530294N00558518EA0149601519
    +B1408184530302N00558529EA0149401517
    +B1408204530311N00558541EA0149101515
    +B1408224530320N00558552EA0148901512
    +B1408244530329N00558564EA0148701510
    +B1408264530338N00558575EA0148501509
    +B1408284530347N00558587EA0148301507
    +B1408304530356N00558598EA0148101504
    +B1408324530365N00558610EA0147901503
    +B1408344530373N00558622EA0147801501
    +B1408364530382N00558633EA0147501499
    +B1408384530391N00558645EA0147401497
    +B1408404530400N00558657EA0147201495
    +B1408424530408N00558669EA0147001493
    +B1408444530418N00558681EA0146801491
    +B1408464530426N00558693EA0146601489
    +B1408484530435N00558704EA0146501488
    +B1408504530443N00558717EA0146301486
    +B1408524530452N00558729EA0146101484
    +B1408544530461N00558740EA0145901482
    +B1408564530470N00558752EA0145701480
    +B1408584530478N00558764EA0145501478
    +B1409004530487N00558776EA0145301476
    +B1409024530496N00558787EA0145101474
    +B1409044530505N00558798EA0144901472
    +B1409064530514N00558809EA0144701470
    +B1409084530523N00558821EA0144501468
    +B1409104530531N00558832EA0144201465
    +B1409124530540N00558844EA0144001463
    +B1409144530549N00558856EA0143801461
    +B1409164530557N00558868EA0143501458
    +B1409184530566N00558880EA0143201455
    +B1409204530574N00558893EA0143001453
    +B1409224530583N00558905EA0142801451
    +B1409244530591N00558917EA0142601448
    +B1409264530600N00558928EA0142401446
    +B1409284530609N00558940EA0142201444
    +B1409304530617N00558951EA0141901441
    +B1409324530626N00558963EA0141601439
    +B1409344530635N00558975EA0141401436
    +B1409364530643N00558987EA0141201434
    +B1409384530651N00558999EA0140901431
    +B1409404530660N00559010EA0140601428
    +B1409424530669N00559022EA0140401426
    +B1409444530677N00559033EA0140101423
    +B1409464530686N00559044EA0139701420
    +B1409484530695N00559056EA0139401417
    +B1409504530703N00559067EA0139101414
    +B1409524530712N00559079EA0138701411
    +B1409544530721N00559090EA0138401407
    +B1409564530730N00559102EA0138101404
    +B1409584530739N00559113EA0137801401
    +B1410004530747N00559124EA0137501398
    +B1410024530756N00559137EA0137201395
    +B1410044530764N00559149EA0136901392
    +B1410064530773N00559161EA0136601389
    +B1410084530781N00559173EA0136401386
    +B1410104530789N00559185EA0136101383
    +B1410124530798N00559197EA0135801380
    +B1410144530806N00559209EA0135401377
    +B1410164530815N00559220EA0135101374
    +B1410184530823N00559232EA0134801371
    +B1410204530831N00559244EA0134501367
    +B1410224530840N00559256EA0134201364
    +B1410244530848N00559268EA0133901361
    +B1410264530856N00559281EA0133701359
    +B1410284530864N00559293EA0133401356
    +B1410304530871N00559306EA0133101353
    +B1410324530879N00559319EA0132901350
    +B1410344530886N00559332EA0132601348
    +B1410364530894N00559345EA0132401345
    +B1410384530902N00559358EA0132101342
    +B1410404530909N00559371EA0131901340
    +B1410424530917N00559384EA0131701337
    +B1410444530924N00559397EA0131401334
    +B1410464530932N00559410EA0131201332
    +B1410484530939N00559423EA0131001330
    +B1410504530947N00559435EA0130801328
    +B1410524530955N00559448EA0130701326
    +B1410544530963N00559460EA0130401324
    +B1410564530970N00559473EA0130301322
    +B1410584530977N00559485EA0130001320
    +B1411004530984N00559498EA0129801317
    +B1411024530992N00559511EA0129601315
    +B1411044530999N00559523EA0129401314
    +B1411064531006N00559535EA0129101311
    +B1411084531014N00559548EA0129001309
    +B1411104531021N00559561EA0128801308
    +B1411124531028N00559574EA0128601305
    +B1411144531034N00559587EA0128301302
    +B1411164531041N00559602EA0128101300
    +B1411184531047N00559615EA0127801298
    +B1411204531054N00559628EA0127501294
    +B1411224531061N00559642EA0127201291
    +B1411244531068N00559655EA0126901289
    +B1411264531074N00559668EA0126601286
    +B1411284531080N00559682EA0126301283
    +B1411304531087N00559695EA0126001280
    +B1411324531093N00559709EA0125701277
    +B1411344531100N00559723EA0125401273
    +B1411364531107N00559737EA0125201271
    +B1411384531114N00559750EA0124901268
    +B1411404531119N00559764EA0124701266
    +B1411424531126N00559779EA0124601264
    +B1411444531132N00559793EA0124501263
    +B1411464531138N00559806EA0124301262
    +B1411484531144N00559820EA0124101260
    +B1411504531151N00559834EA0124001259
    +B1411524531157N00559848EA0123701257
    +B1411544531163N00559861EA0123401254
    +B1411564531170N00559874EA0123001251
    +B1411584531177N00559885EA0122801248
    +B1412004531183N00559898EA0122601245
    +B1412024531189N00559912EA0122501243
    +B1412044531193N00559927EA0122201242
    +B1412064531197N00559940EA0121601239
    +B1412084531203N00559949EA0121201234
    +B1412104531210N00559959EA0121001231
    +B1412124531215N00559970EA0120601229
    +B1412144531221N00559979EA0120201224
    +B1412164531228N00559988EA0119901220
    +B1412184531234N00559998EA0119601218
    +B1412204531240N00600008EA0119101214
    +B1412224531246N00600019EA0118801209
    +B1412244531253N00600031EA0118501206
    +B1412264531259N00600042EA0118201203
    +B1412284531265N00600052EA0117801199
    +B1412304531271N00600064EA0117501195
    +B1412324531277N00600075EA0117201192
    +B1412344531284N00600085EA0116801188
    +B1412364531290N00600094EA0116401184
    +B1412384531298N00600104EA0116001180
    +B1412404531305N00600114EA0115701176
    +B1412424531312N00600123EA0115301172
    +B1412444531319N00600133EA0115101169
    +B1412464531327N00600143EA0114901166
    +B1412484531334N00600153EA0114601163
    +B1412504531342N00600161EA0114401161
    +B1412524531350N00600169EA0114501160
    +B1412544531357N00600177EA0114401160
    +B1412564531365N00600185EA0114401160
    +B1412584531371N00600194EA0114301159
    +B1413004531377N00600203EA0114001157
    +B1413024531384N00600214EA0113801155
    +B1413044531389N00600227EA0113701154
    +B1413064531396N00600239EA0113601152
    +B1413084531402N00600251EA0113401151
    +B1413104531409N00600262EA0113301149
    +B1413124531416N00600271EA0113101148
    +B1413144531423N00600280EA0112901146
    +B1413164531430N00600290EA0112601144
    +B1413184531438N00600300EA0112401141
    +B1413204531445N00600309EA0112101138
    +B1413224531453N00600320EA0112101136
    +B1413244531461N00600331EA0112001135
    +B1413264531469N00600342EA0111901135
    +B1413284531476N00600353EA0111701133
    +B1413304531485N00600363EA0111601132
    +B1413324531492N00600373EA0111501131
    +B1413344531499N00600383EA0111301129
    +B1413364531507N00600391EA0111001127
    +B1413384531513N00600400EA0110801125
    +B1413404531519N00600410EA0110401122
    +B1413424531525N00600419EA0109901118
    +B1413444531532N00600429EA0109501113
    +B1413464531539N00600440EA0109301110
    +B1413484531545N00600452EA0109001108
    +B1413504531551N00600465EA0108701104
    +B1413524531559N00600476EA0108401101
    +B1413544531565N00600488EA0108201099
    +B1413564531571N00600502EA0108001096
    +B1413584531577N00600517EA0107701094
    +B1414004531584N00600531EA0107501091
    +B1414024531590N00600546EA0107301089
    +B1414044531596N00600560EA0107101087
    +B1414064531602N00600575EA0106901085
    +B1414084531609N00600590EA0106801083
    +B1414104531616N00600606EA0106701082
    +B1414124531623N00600622EA0106701081
    +B1414144531629N00600638EA0106601081
    +B1414164531636N00600653EA0106401080
    +B1414184531642N00600668EA0106301078
    +B1414204531650N00600684EA0106201077
    +B1414224531656N00600701EA0106101077
    +B1414244531661N00600716EA0106001076
    +B1414264531668N00600731EA0105701073
    +B1414284531675N00600747EA0105501071
    +B1414304531682N00600760EA0105401069
    +B1414324531689N00600773EA0105101068
    +B1414344531693N00600787EA0105001066
    +B1414364531698N00600802EA0104701063
    +B1414384531703N00600815EA0104501060
    +B1414404531709N00600829EA0104201058
    +B1414424531715N00600842EA0104001055
    +B1414444531723N00600853EA0103901053
    +B1414464531729N00600864EA0103701052
    +B1414484531735N00600877EA0103501050
    +B1414504531742N00600890EA0103601049
    +B1414524531746N00600902EA0103601050
    +B1414544531748N00600914EA0103301049
    +B1414564531753N00600926EA0103201047
    +B1414584531758N00600939EA0103301047
    +B1415004531760N00600952EA0103001047
    +B1415024531765N00600964EA0102801045
    +B1415044531770N00600978EA0102901045
    +B1415064531774N00600993EA0102901045
    +B1415084531778N00601004EA0102901045
    +B1415104531784N00601015EA0102801044
    +B1415124531789N00601026EA0102901044
    +B1415144531795N00601038EA0103001045
    +B1415164531800N00601050EA0103001045
    +B1415184531805N00601064EA0102901044
    +B1415204531810N00601077EA0103001044
    +B1415224531814N00601090EA0103101044
    +B1415244531818N00601102EA0103101044
    +B1415264531824N00601114EA0103101044
    +B1415284531828N00601124EA0103201045
    +B1415304531833N00601131EA0103301045
    +B1415324531838N00601140EA0103501046
    +B1415344531843N00601150EA0103701049
    +B1415364531847N00601160EA0103901050
    +B1415384531852N00601168EA0104001051
    +B1415404531857N00601176EA0104201053
    +B1415424531863N00601184EA0104401055
    +B1415444531868N00601193EA0104601056
    +B1415464531874N00601201EA0104601058
    +B1415484531879N00601208EA0104301058
    +B1415504531885N00601215EA0104001057
    +B1415524531892N00601222EA0103801055
    +B1415544531899N00601226EA0103801053
    +B1415564531906N00601229EA0103901053
    +B1415584531914N00601233EA0103801053
    +B1416004531921N00601234EA0103801051
    +B1416024531930N00601234EA0104001052
    +B1416044531939N00601235EA0104401053
    +B1416064531947N00601236EA0104701056
    +B1416084531954N00601237EA0104901059
    +B1416104531959N00601237EA0105001060
    +B1416124531965N00601237EA0105001061
    +B1416144531971N00601237EA0105001061
    +B1416164531977N00601236EA0105301063
    +B1416184531983N00601235EA0105601066
    +B1416204531989N00601234EA0105901069
    +B1416224531994N00601235EA0106101071
    +B1416244532000N00601234EA0106401074
    +B1416264532006N00601233EA0106801079
    +B1416284532010N00601232EA0107301083
    +B1416304532014N00601233EA0107501086
    +B1416324532018N00601232EA0107701089
    +B1416344532021N00601230EA0107901091
    +B1416364532025N00601230EA0108201094
    +B1416384532026N00601227EA0108401095
    +B1416404532025N00601221EA0108601097
    +B1416424532013N00601217EA0108801099
    +B1416444531997N00601226EA0109101101
    +B1416464531988N00601245EA0109201103
    +B1416484531988N00601267EA0109501105
    +B1416504531994N00601285EA0109801109
    +B1416524532002N00601290EA0110001112
    +B1416544532007N00601288EA0110301113
    +B1416564532010N00601283EA0110601116
    +B1416584532011N00601278EA0111101121
    +B1417004532012N00601277EA0111401125
    +B1417024532015N00601278EA0111501128
    +B1417044532015N00601276EA0111501130
    +B1417064532010N00601271EA0111501132
    +B1417084531996N00601274EA0111701134
    +B1417104531981N00601285EA0112101137
    +B1417124531972N00601305EA0112301140
    +B1417144531970N00601329EA0112701143
    +B1417164531976N00601345EA0113101146
    +B1417184531982N00601347EA0113401150
    +B1417204531985N00601347EA0113501152
    +B1417224531988N00601348EA0113201153
    +B1417244531992N00601347EA0113201152
    +B1417264531997N00601343EA0113501154
    +B1417284532000N00601342EA0113601156
    +B1417304532002N00601341EA0113601156
    +B1417324532004N00601337EA0113801157
    +B1417344532007N00601334EA0113901159
    +B1417364532010N00601331EA0113901159
    +B1417384532012N00601326EA0113901158
    +B1417404532016N00601320EA0114001159
    +B1417424532021N00601314EA0114501161
    +B1417444532024N00601310EA0114901164
    +B1417464532026N00601309EA0114901166
    +B1417484532027N00601308EA0114901167
    +B1417504532030N00601303EA0115401169
    +B1417524532032N00601299EA0116001173
    +B1417544532033N00601298EA0116201177
    +B1417564532035N00601298EA0116201179
    +B1417584532038N00601297EA0116301180
    +B1418004532040N00601294EA0116501181
    +B1418024532042N00601292EA0116501183
    +B1418044532045N00601290EA0116401184
    +B1418064532048N00601288EA0116501183
    +B1418084532051N00601285EA0116601185
    +B1418104532054N00601283EA0116801186
    +B1418124532058N00601280EA0117201188
    +B1418144532063N00601278EA0117701192
    +B1418164532065N00601274EA0118001195
    +B1418184532061N00601267EA0118101198
    +B1418204532048N00601266EA0118301200
    +B1418224532036N00601278EA0118601202
    +B1418244532031N00601301EA0118801205
    +B1418264532032N00601324EA0119301210
    +B1418284532039N00601337EA0119701214
    +B1418304532045N00601337EA0120001218
    +B1418324532046N00601331EA0120101221
    +B1418344532042N00601324EA0120201223
    +B1418364532031N00601321EA0120201224
    +B1418384532016N00601330EA0120401225
    +B1418404532006N00601349EA0120601227
    +B1418424532002N00601371EA0121001230
    +B1418444532004N00601390EA0121501234
    +B1418464532009N00601403EA0121701236
    +B1418484532017N00601411EA0122001239
    +B1418504532023N00601413EA0122801243
    +B1418524532026N00601409EA0123201248
    +B1418544532027N00601406EA0123101251
    +B1418564532031N00601404EA0123001253
    +B1418584532035N00601400EA0122901254
    +B1419004532031N00601391EA0122501252
    +B1419024532018N00601389EA0122301250
    +B1419044532002N00601401EA0122301249
    +B1419064531993N00601423EA0122301247
    +B1419084531992N00601447EA0122401246
    +B1419104531997N00601464EA0122701247
    +B1419124532003N00601471EA0122901248
    +B1419144532007N00601473EA0122801249
    +B1419164532011N00601473EA0122601247
    +B1419184532016N00601471EA0123001247
    +B1419204532021N00601471EA0123401250
    +B1419224532024N00601471EA0123501253
    +B1419244532026N00601471EA0123601253
    +B1419264532030N00601469EA0123901255
    +B1419284532033N00601467EA0124101259
    +B1419304532034N00601466EA0124101260
    +B1419324532037N00601464EA0124201260
    +B1419344532041N00601462EA0124401262
    +B1419364532045N00601460EA0124401264
    +B1419384532049N00601458EA0124501264
    +B1419404532055N00601455EA0124801266
    +B1419424532061N00601453EA0125101269
    +B1419444532067N00601449EA0125401272
    +B1419464532072N00601446EA0125601275
    +B1419484532076N00601444EA0125401277
    +B1419504532080N00601441EA0125101277
    +B1419524532084N00601438EA0125001274
    +B1419544532090N00601437EA0125201275
    +B1419564532095N00601435EA0125501277
    +B1419584532098N00601433EA0125801279
    +B1420004532103N00601430EA0126401282
    +B1420024532107N00601427EA0126901285
    +B1420044532111N00601427EA0126801288
    +B1420064532115N00601426EA0126601289
    +B1420084532123N00601422EA0126801290
    +B1420104532131N00601420EA0127101292
    +B1420124532137N00601419EA0127301294
    +B1420144532142N00601419EA0127401295
    +B1420164532148N00601417EA0127801297
    +B1420184532152N00601415EA0128301301
    +B1420204532156N00601414EA0128401304
    +B1420224532159N00601414EA0128401305
    +B1420244532158N00601410EA0128601305
    +B1420264532152N00601401EA0129201307
    +B1420284532140N00601403EA0129501310
    +B1420304532128N00601414EA0129801313
    +B1420324532117N00601432EA0129901317
    +B1420344532114N00601455EA0129801318
    +B1420364532123N00601471EA0130001319
    +B1420384532134N00601474EA0130501323
    +B1420404532138N00601473EA0131201328
    +B1420424532141N00601472EA0131701334
    +B1420444532145N00601472EA0132201339
    +B1420464532149N00601471EA0132701345
    +B1420484532148N00601465EA0132801348
    +B1420504532137N00601459EA0133201352
    +B1420524532124N00601467EA0133501356
    +B1420544532114N00601489EA0133901359
    +B1420564532114N00601514EA0134701364
    +B1420584532123N00601530EA0135501371
    +B1421004532131N00601535EA0136101379
    +B1421024532134N00601537EA0136401385
    +B1421044532138N00601536EA0136801389
    +B1421064532138N00601530EA0137101394
    +B1421084532130N00601526EA0137301398
    +B1421104532117N00601529EA0137601402
    +B1421124532104N00601544EA0138001405
    +B1421144532098N00601570EA0138301408
    +B1421164532100N00601594EA0138801412
    +B1421184532107N00601608EA0139201416
    +B1421204532114N00601610EA0139601420
    +B1421224532118N00601609EA0140001423
    +B1421244532122N00601609EA0139701425
    +B1421264532127N00601608EA0138901426
    +B1421284532132N00601604EA0138301424
    +B1421304532134N00601598EA0138101420
    +B1421324532136N00601599EA0137801416
    +B1421344532141N00601601EA0137201411
    +B1421364532148N00601598EA0137101407
    +B1421384532152N00601594EA0136901405
    +B1421404532156N00601590EA0136601402
    +B1421424532160N00601588EA0136301398
    +B1421444532165N00601585EA0136301395
    +B1421464532170N00601581EA0136301393
    +B1421484532175N00601578EA0136401392
    +B1421504532180N00601576EA0136601392
    +B1421524532185N00601572EA0136901393
    +B1421544532190N00601568EA0137001394
    +B1421564532193N00601564EA0136401393
    +B1421584532194N00601558EA0135601391
    +B1422004532197N00601550EA0135401388
    +B1422024532200N00601544EA0135401386
    +B1422044532205N00601539EA0135401385
    +B1422064532210N00601537EA0135401384
    +B1422084532216N00601538EA0135301383
    +B1422104532221N00601536EA0135201382
    +B1422124532226N00601531EA0135301381
    +B1422144532232N00601526EA0135601381
    +B1422164532237N00601524EA0135901383
    +B1422184532243N00601522EA0136001384
    +B1422204532249N00601518EA0135701383
    +B1422224532257N00601516EA0135001381
    +B1422244532265N00601513EA0134501377
    +B1422264532273N00601510EA0134301374
    +B1422284532282N00601510EA0134201372
    +B1422304532291N00601510EA0134201371
    +B1422324532301N00601513EA0134101370
    +B1422344532311N00601514EA0134001368
    +B1422364532321N00601516EA0134101367
    +B1422384532331N00601517EA0134301367
    +B1422404532340N00601518EA0134601369
    +B1422424532347N00601517EA0134901371
    +B1422444532353N00601514EA0135301374
    +B1422464532358N00601510EA0135701378
    +B1422484532364N00601506EA0135901381
    +B1422504532371N00601503EA0136101384
    +B1422524532376N00601500EA0136101386
    +B1422544532377N00601494EA0135701387
    +B1422564532373N00601489EA0135401386
    +B1422584532365N00601490EA0135201384
    +B1423004532355N00601498EA0135301381
    +B1423024532345N00601510EA0135601382
    +B1423044532336N00601522EA0135901384
    +B1423064532328N00601532EA0136101386
    +B1423084532319N00601544EA0136101386
    +B1423104532309N00601554EA0136101386
    +B1423124532300N00601553EA0136101385
    +B1423144532300N00601546EA0136201385
    +B1423164532305N00601541EA0136301386
    +B1423184532313N00601540EA0136201385
    +B1423204532321N00601537EA0136101384
    +B1423224532329N00601535EA0135801382
    +B1423244532336N00601532EA0135401380
    +B1423264532341N00601527EA0135101377
    +B1423284532341N00601519EA0134801374
    +B1423304532338N00601510EA0134501370
    +B1423324532335N00601501EA0134301367
    +B1423344532332N00601492EA0134201365
    +B1423364532328N00601485EA0133901362
    +B1423384532322N00601477EA0133601359
    +B1423404532316N00601466EA0133601357
    +B1423424532314N00601456EA0133401356
    +B1423444532312N00601446EA0133001353
    +B1423464532309N00601437EA0132601350
    +B1423484532307N00601427EA0132301346
    +B1423504532306N00601416EA0132001343
    +B1423524532303N00601406EA0131801340
    +B1423544532301N00601397EA0131501338
    +B1423564532300N00601386EA0131301334
    +B1423584532299N00601374EA0131101332
    +B1424004532297N00601363EA0130901330
    +B1424024532297N00601351EA0130601327
    +B1424044532298N00601338EA0130501325
    +B1424064532300N00601326EA0130401324
    +B1424084532304N00601316EA0130201322
    +B1424104532307N00601305EA0130201321
    +B1424124532310N00601293EA0130301321
    +B1424144532313N00601283EA0130601323
    +B1424164532316N00601274EA0130801325
    +B1424184532317N00601267EA0130801327
    +B1424204532319N00601261EA0130901327
    +B1424224532321N00601255EA0131001328
    +B1424244532324N00601248EA0130901328
    +B1424264532328N00601242EA0131001328
    +B1424284532331N00601235EA0131101329
    +B1424304532333N00601228EA0131101330
    +B1424324532336N00601222EA0131001330
    +B1424344532339N00601217EA0130901329
    +B1424364532343N00601211EA0130801328
    +B1424384532347N00601203EA0130701327
    +B1424404532350N00601196EA0130601326
    +B1424424532355N00601191EA0130601326
    +B1424444532359N00601187EA0130301325
    +B1424464532360N00601178EA0130201323
    +B1424484532355N00601169EA0130101323
    +B1424504532351N00601162EA0129901321
    +B1424524532354N00601157EA0129401319
    +B1424544532364N00601160EA0128901316
    +B1424564532374N00601174EA0128701312
    +B1424584532380N00601192EA0128501309
    +B1425004532385N00601212EA0128301307
    +B1425024532390N00601232EA0128301305
    +B1425044532394N00601252EA0128401304
    +B1425064532399N00601271EA0128501304
    +B1425084532404N00601289EA0128801306
    +B1425104532408N00601307EA0128701307
    +B1425124532412N00601322EA0128201308
    +B1425144532418N00601338EA0127801307
    +B1425164532425N00601356EA0127901306
    +B1425184532430N00601373EA0127801305
    +B1425204532434N00601391EA0127801303
    +B1425224532440N00601408EA0127701302
    +B1425244532446N00601424EA0127701301
    +B1425264532453N00601440EA0127801301
    +B1425284532458N00601456EA0128001302
    +B1425304532464N00601471EA0128201304
    +B1425324532470N00601486EA0128301305
    +B1425344532475N00601500EA0128501307
    +B1425364532480N00601515EA0128601308
    +B1425384532487N00601527EA0128701308
    +B1425404532494N00601536EA0128701308
    +B1425424532501N00601536EA0128601308
    +B1425444532505N00601523EA0128601307
    +B1425464532499N00601515EA0128601306
    +B1425484532486N00601516EA0128801306
    +B1425504532474N00601521EA0129001309
    +B1425524532463N00601524EA0129101310
    +B1425544532453N00601525EA0129301311
    +B1425564532442N00601526EA0129401314
    +B1425584532433N00601526EA0129501315
    +B1426004532425N00601522EA0129501315
    +B1426024532420N00601514EA0129501315
    +B1426044532417N00601504EA0129501314
    +B1426064532415N00601493EA0129401314
    +B1426084532412N00601482EA0129401314
    +B1426104532409N00601473EA0129301314
    +B1426124532406N00601462EA0129201313
    +B1426144532403N00601452EA0129101312
    +B1426164532399N00601442EA0129101311
    +B1426184532395N00601431EA0129201311
    +B1426204532394N00601420EA0129301312
    +B1426224532393N00601411EA0129401313
    +B1426244532391N00601403EA0129301313
    +B1426264532387N00601396EA0129101312
    +B1426284532383N00601386EA0128901310
    +B1426304532380N00601375EA0128801308
    +B1426324532377N00601364EA0128801307
    +B1426344532374N00601353EA0128801307
    +B1426364532372N00601344EA0128801307
    +B1426384532372N00601334EA0128901308
    +B1426404532372N00601326EA0129001310
    +B1426424532373N00601318EA0129001310
    +B1426444532373N00601309EA0129201311
    +B1426464532374N00601298EA0129601312
    +B1426484532376N00601288EA0129901316
    +B1426504532377N00601280EA0130101318
    +B1426524532379N00601271EA0130001319
    +B1426544532389N00601269EA0130001319
    +B1426564532397N00601282EA0130101319
    +B1426584532395N00601301EA0130101320
    +B1427004532386N00601316EA0130401321
    +B1427024532376N00601323EA0130701323
    +B1427044532366N00601321EA0131001327
    +B1427064532357N00601317EA0131201330
    +B1427084532351N00601311EA0131201332
    +B1427104532346N00601303EA0131101332
    +B1427124532344N00601294EA0131101333
    +B1427144532346N00601289EA0131101332
    +B1427164532352N00601290EA0131101332
    +B1427184532359N00601303EA0131201331
    +B1427204532360N00601321EA0131201332
    +B1427224532357N00601338EA0131101332
    +B1427244532354N00601355EA0131201331
    +B1427264532348N00601372EA0131401332
    +B1427284532339N00601385EA0131601333
    +B1427304532330N00601396EA0131701335
    +B1427324532320N00601408EA0132101337
    +B1427344532310N00601419EA0132701341
    +B1427364532302N00601431EA0133001346
    +B1427384532293N00601441EA0132801349
    +B1427404532280N00601440EA0132701351
    +B1427424532269N00601431EA0133001352
    +B1427444532266N00601420EA0133301355
    +B1427464532269N00601412EA0133601358
    +B1427484532275N00601409EA0133701360
    +B1427504532285N00601414EA0133901361
    +B1427524532291N00601432EA0134101362
    +B1427544532287N00601454EA0134701365
    +B1427564532277N00601466EA0135101369
    +B1427584532266N00601469EA0135301371
    +B1428004532255N00601464EA0135601374
    +B1428024532251N00601452EA0135901376
    +B1428044532255N00601443EA0136201380
    +B1428064532264N00601445EA0136201382
    +B1428084532273N00601457EA0136301383
    +B1428104532276N00601479EA0136601385
    +B1428124532268N00601495EA0136901388
    +B1428144532256N00601502EA0137201391
    +B1428164532244N00601503EA0137301392
    +B1428184532232N00601500EA0137501393
    +B1428204532224N00601487EA0137901395
    +B1428224532227N00601477EA0138101398
    +B1428244532235N00601478EA0138201401
    +B1428264532242N00601489EA0138101402
    +B1428284532247N00601510EA0138201402
    +B1428304532243N00601532EA0138501404
    +B1428324532231N00601545EA0138801405
    +B1428344532219N00601550EA0138801406
    +B1428364532207N00601551EA0138801405
    +B1428384532193N00601550EA0139001404
    +B1428404532184N00601538EA0139301404
    +B1428424532184N00601527EA0139601406
    +B1428444532187N00601520EA0139701409
    +B1428464532189N00601515EA0139501410
    +B1428484532192N00601511EA0139101410
    +B1428504532203N00601516EA0138901408
    +B1428524532214N00601524EA0138901408
    +B1428544532223N00601532EA0138801408
    +B1428564532231N00601539EA0138701406
    +B1428584532240N00601545EA0138501404
    +B1429004532250N00601550EA0138201402
    +B1429024532260N00601554EA0138001400
    +B1429044532271N00601554EA0138401398
    +B1429064532279N00601554EA0138701398
    +B1429084532286N00601556EA0138801399
    +B1429104532293N00601557EA0138701398
    +B1429124532302N00601556EA0138901398
    +B1429144532311N00601559EA0139401399
    +B1429164532319N00601560EA0139601403
    +B1429184532324N00601559EA0139901406
    +B1429204532330N00601556EA0140301410
    +B1429224532335N00601552EA0140301413
    +B1429244532336N00601545EA0139901415
    +B1429264532325N00601534EA0139601415
    +B1429284532309N00601534EA0139501414
    +B1429304532296N00601553EA0139301414
    +B1429324532297N00601575EA0139301413
    +B1429344532305N00601586EA0139501413
    +B1429364532313N00601586EA0139501414
    +B1429384532319N00601586EA0139401413
    +B1429404532327N00601587EA0139301412
    +B1429424532333N00601585EA0139001411
    +B1429444532341N00601583EA0138701409
    +B1429464532349N00601582EA0138601406
    +B1429484532359N00601584EA0138801404
    +B1429504532367N00601587EA0139301404
    +B1429524532374N00601590EA0139501405
    +B1429544532380N00601591EA0139401405
    +B1429564532387N00601591EA0139101404
    +B1429584532396N00601591EA0138801401
    +B1430004532406N00601594EA0138901401
    +B1430024532415N00601597EA0138901400
    +B1430044532422N00601598EA0138701399
    +B1430064532430N00601600EA0138301397
    +B1430084532439N00601601EA0137701393
    +B1430104532448N00601600EA0137601390
    +B1430124532457N00601600EA0137701388
    +B1430144532466N00601603EA0137601387
    +B1430164532477N00601603EA0137601386
    +B1430184532488N00601605EA0138001385
    +B1430204532498N00601608EA0138001386
    +B1430224532504N00601610EA0137601385
    +B1430244532512N00601610EA0137401383
    +B1430264532522N00601610EA0137501383
    +B1430284532530N00601610EA0137701385
    +B1430304532540N00601612EA0137601385
    +B1430324532549N00601615EA0137301385
    +B1430344532557N00601617EA0136701383
    +B1430364532565N00601619EA0136301380
    +B1430384532574N00601620EA0135801376
    +B1430404532582N00601617EA0135701372
    +B1430424532589N00601614EA0135701371
    +B1430444532597N00601613EA0135601369
    +B1430464532607N00601614EA0135701369
    +B1430484532615N00601615EA0135601370
    +B1430504532622N00601618EA0135301368
    +B1430524532631N00601623EA0135001366
    +B1430544532640N00601632EA0135001365
    +B1430564532648N00601645EA0134901364
    +B1430584532655N00601659EA0134601362
    +B1431004532662N00601672EA0134101359
    +B1431024532670N00601690EA0133901356
    +B1431044532677N00601709EA0133701354
    +B1431064532682N00601730EA0133301351
    +B1431084532687N00601751EA0133101347
    +B1431104532693N00601773EA0132901345
    +B1431124532697N00601793EA0132501342
    +B1431144532703N00601815EA0132201338
    +B1431164532708N00601837EA0132101336
    +B1431184532713N00601858EA0132001334
    +B1431204532719N00601873EA0131501332
    +B1431224532727N00601891EA0130901328
    +B1431244532734N00601912EA0130701324
    +B1431264532736N00601934EA0130401321
    +B1431284532736N00601951EA0129601317
    +B1431304532738N00601970EA0128801312
    +B1431324532741N00601991EA0128201305
    +B1431344532742N00602010EA0127401299
    +B1431364532744N00602028EA0126301291
    +B1431384532749N00602050EA0125901284
    +B1431404532754N00602073EA0125701277
    +B1431424532757N00602091EA0125301273
    +B1431444532762N00602109EA0125201268
    +B1431464532768N00602128EA0125601264
    +B1431484532771N00602148EA0126101262
    +B1431504532773N00602164EA0126201263
    +B1431524532776N00602181EA0126501265
    +B1431544532778N00602198EA0126501267
    +B1431564532779N00602214EA0126201267
    +B1431584532781N00602231EA0125701266
    +B1432004532784N00602251EA0125501263
    +B1432024532787N00602270EA0125201261
    +B1432044532789N00602289EA0124801258
    +B1432064532790N00602310EA0124601255
    +B1432084532792N00602331EA0124501254
    +B1432104532793N00602349EA0124101252
    +B1432124532794N00602369EA0123701249
    +B1432144532794N00602389EA0123501246
    +B1432164532797N00602408EA0123201244
    +B1432184532798N00602428EA0122801242
    +B1432204532799N00602449EA0122501238
    +B1432224532801N00602470EA0122201235
    +B1432244532801N00602491EA0122001234
    +B1432264532802N00602511EA0121801231
    +B1432284532805N00602531EA0121601229
    +B1432304532807N00602551EA0121301226
    +B1432324532809N00602571EA0120901223
    +B1432344532812N00602590EA0120801221
    +B1432364532814N00602610EA0120701220
    +B1432384532817N00602628EA0120501218
    +B1432404532820N00602645EA0120201216
    +B1432424532822N00602663EA0120201214
    +B1432444532824N00602681EA0120101214
    +B1432464532826N00602698EA0119801212
    +B1432484532829N00602716EA0119501209
    +B1432504532833N00602734EA0119301207
    +B1432524532838N00602751EA0119201206
    +B1432544532843N00602767EA0119101204
    +B1432564532852N00602791EA0119101203
    +B1432584532855N00602799EA0119201203
    +B1433004532861N00602813EA0119201204
    +B1433024532865N00602827EA0119201204
    +B1433044532870N00602839EA0119001203
    +B1433064532876N00602850EA0118901202
    +B1433084532884N00602859EA0118801201
    +B1433104532894N00602867EA0119001201
    +B1433124532904N00602873EA0119101202
    +B1433144532914N00602879EA0119401204
    +B1433164532922N00602885EA0119501206
    +B1433184532929N00602892EA0119601208
    +B1433204532937N00602897EA0119401208
    +B1433224532944N00602899EA0119001208
    +B1433244532953N00602900EA0118901206
    +B1433264532963N00602902EA0119001204
    +B1433284532972N00602904EA0119301205
    +B1433304532981N00602904EA0119701207
    +B1433324532989N00602904EA0120301211
    +B1433344532995N00602908EA0120801217
    +B1433364532999N00602907EA0120801221
    +B1433384533000N00602896EA0120701224
    +B1433404532990N00602884EA0121001226
    +B1433424532975N00602891EA0121501230
    +B1433444532966N00602910EA0122001234
    +B1433464532967N00602928EA0122401239
    +B1433484532974N00602940EA0122701243
    +B1433504532982N00602939EA0122801244
    +B1433524532984N00602927EA0122801245
    +B1433544532976N00602917EA0123101246
    +B1433564532957N00602918EA0123501251
    +B1433584532951N00602924EA0123801252
    +B1434004532942N00602941EA0124101256
    +B1434024532939N00602961EA0124501259
    +B1434044532943N00602977EA0124901263
    +B1434064532951N00602985EA0125401268
    +B1434084532958N00602983EA0125601271
    +B1434104532958N00602970EA0125701274
    +B1434124532948N00602961EA0126001276
    +B1434144532934N00602968EA0126401280
    +B1434164532924N00602986EA0126801283
    +B1434184532923N00603006EA0127201286
    +B1434204532929N00603021EA0127401289
    +B1434224532938N00603024EA0127601291
    +B1434244532942N00603016EA0127601292
    +B1434264532934N00603005EA0127701294
    +B1434284532919N00603006EA0128101296
    +B1434304532908N00603021EA0128601299
    +B1434324532902N00603039EA0129001302
    +B1434344532903N00603057EA0129401307
    +B1434364532908N00603072EA0129801312
    +B1434384532917N00603078EA0129901315
    +B1434404532925N00603074EA0129901318
    +B1434424532922N00603055EA0129701319
    +B1434444532908N00603053EA0129701319
    +B1434464532900N00603059EA0130001320
    +B1434484532888N00603072EA0130301323
    +B1434504532879N00603087EA0130901327
    +B1434524532869N00603111EA0131501335
    +B1434544532866N00603117EA0131701337
    +B1434564532861N00603131EA0131601340
    +B1434584532871N00603152EA0131901344
    +B1435004532877N00603146EA0132201346
    +B1435024532878N00603140EA0132601348
    +B1435044532864N00603125EA0132801351
    +B1435064532856N00603125EA0133101352
    +B1435084532835N00603142EA0133501356
    +B1435104532830N00603160EA0133801359
    +B1435124532832N00603177EA0134001361
    +B1435144532840N00603185EA0134101361
    +B1435164532846N00603182EA0134201363
    +B1435184532844N00603169EA0134401365
    +B1435204532832N00603160EA0134901368
    +B1435224532824N00603161EA0135201369
    +B1435244532808N00603168EA0135701374
    +B1435264532787N00603167EA0135801379
    +B1435284532780N00603153EA0135801381
    +B1435304532785N00603146EA0136101383
    +B1435324532789N00603149EA0136401384
    +B1435344532796N00603172EA0136901390
    +B1435364532799N00603183EA0137401395
    +B1435384532801N00603193EA0137601397
    +B1435404532803N00603200EA0137701397
    +B1435424532807N00603221EA0138101401
    +B1435444532812N00603234EA0138701406
    +B1435464532818N00603243EA0139301412
    +B1435484532821N00603246EA0139701414
    +B1435504532828N00603236EA0139701419
    +B1435524532821N00603221EA0140001422
    +B1435544532806N00603217EA0140401426
    +B1435564532791N00603224EA0140701429
    +B1435584532779N00603228EA0140701431
    +B1436004532766N00603218EA0140401431
    +B1436024532761N00603204EA0140401431
    +B1436044532764N00603197EA0139901429
    +B1436064532771N00603198EA0139501425
    +B1436084532778N00603205EA0139101420
    +B1436104532782N00603209EA0138901417
    +B1436124532794N00603222EA0138901414
    +B1436144532800N00603233EA0139001414
    +B1436164532806N00603244EA0139301415
    +B1436184532812N00603254EA0139701417
    +B1436204532817N00603261EA0140101420
    +B1436224532823N00603268EA0140401423
    +B1436244532829N00603276EA0140701426
    +B1436264532835N00603284EA0140801428
    +B1436284532841N00603290EA0141001430
    +B1436304532847N00603297EA0141201432
    +B1436324532854N00603304EA0141301433
    +B1436344532862N00603312EA0141501435
    +B1436364532869N00603320EA0141701437
    +B1436384532876N00603325EA0141901439
    +B1436404532883N00603330EA0142001441
    +B1436424532890N00603336EA0142301444
    +B1436444532897N00603341EA0142601447
    +B1436464532904N00603346EA0143001451
    +B1436484532911N00603349EA0143101453
    +B1436504532917N00603352EA0143201455
    +B1436524532925N00603354EA0143201456
    +B1436544532933N00603357EA0143401457
    +B1436564532940N00603357EA0143401457
    +B1436584532947N00603355EA0143501458
    +B1437004532950N00603347EA0143301457
    +B1437024532946N00603333EA0142901456
    +B1437044532934N00603326EA0143001456
    +B1437064532922N00603325EA0143201457
    +B1437084532911N00603322EA0143201457
    +B1437104532900N00603318EA0143301457
    +B1437124532889N00603317EA0143201457
    +B1437144532878N00603319EA0143101455
    +B1437164532866N00603320EA0143201456
    +B1437184532853N00603319EA0143601458
    +B1437204532842N00603318EA0144201461
    +B1437224532830N00603316EA0144701466
    +B1437244532821N00603310EA0145101471
    +B1437264532822N00603300EA0145301474
    +B1437284532829N00603296EA0145701478
    +B1437304532832N00603296EA0146101481
    +B1437324532837N00603299EA0146301484
    +B1437344532845N00603311EA0146401488
    +B1437364532851N00603323EA0146901492
    +B1437384532856N00603334EA0147101494
    +B1437404532862N00603342EA0147001495
    +B1437424532866N00603346EA0146801495
    +B1437444532876N00603354EA0146701492
    +B1437464532882N00603349EA0146401490
    +B1437484532879N00603333EA0146101487
    +B1437504532867N00603320EA0146501485
    +B1437524532856N00603315EA0147201487
    +B1437544532848N00603312EA0147101488
    +B1437564532838N00603308EA0147001487
    +B1437584532831N00603305EA0147201488
    +B1438004532818N00603302EA0147301490
    +B1438024532800N00603296EA0147101490
    +B1438044532791N00603286EA0147101490
    +B1438064532792N00603273EA0147001489
    +B1438084532802N00603273EA0146901489
    +B1438104532810N00603286EA0147301491
    +B1438124532814N00603295EA0147701494
    +B1438144532817N00603301EA0147701495
    +B1438164532823N00603310EA0147901497
    +B1438184532828N00603319EA0148301500
    +B1438204532833N00603326EA0148701504
    +B1438224532839N00603331EA0148801506
    +B1438244532847N00603337EA0149101510
    +B1438264532853N00603345EA0149601514
    +B1438284532859N00603353EA0150201520
    +B1438304532862N00603359EA0150501524
    +B1438324532871N00603363EA0150801527
    +B1438344532879N00603367EA0151301532
    +B1438364532886N00603367EA0151301535
    +B1438384532893N00603364EA0150801537
    +B1438404532901N00603366EA0150501536
    +B1438424532908N00603371EA0150401534
    +B1438444532915N00603375EA0150401534
    +B1438464532921N00603377EA0150101531
    +B1438484532928N00603377EA0149801528
    +B1438504532937N00603375EA0149701526
    +B1438524532945N00603375EA0149801525
    +B1438544532952N00603373EA0149901525
    +B1438564532961N00603370EA0150001525
    +B1438584532969N00603369EA0150501526
    +B1439004532977N00603370EA0150701528
    +B1439024532985N00603371EA0150801529
    +B1439044532992N00603369EA0151001531
    +B1439064532999N00603365EA0150601531
    +B1439084533006N00603360EA0150001530
    +B1439104533014N00603355EA0149601527
    +B1439124533022N00603351EA0149301523
    +B1439144533030N00603345EA0149301521
    +B1439164533039N00603343EA0149801520
    +B1439184533044N00603345EA0150201520
    +B1439204533049N00603344EA0150101519
    +B1439224533057N00603340EA0150201520
    +B1439244533064N00603339EA0150501522
    +B1439264533070N00603336EA0150601523
    +B1439284533078N00603331EA0150701525
    +B1439304533085N00603325EA0150901527
    +B1439324533093N00603320EA0151201531
    +B1439344533101N00603315EA0151501534
    +B1439364533109N00603315EA0151701536
    +B1439384533116N00603321EA0151801538
    +B1439404533123N00603329EA0151801538
    +B1439424533130N00603334EA0151601537
    +B1439444533138N00603339EA0151301535
    +B1439464533146N00603347EA0151101533
    +B1439484533154N00603356EA0150901531
    +B1439504533161N00603364EA0150601528
    +B1439524533168N00603373EA0150201524
    +B1439544533176N00603382EA0149801521
    +B1439564533185N00603391EA0149601518
    +B1439584533194N00603399EA0149401515
    +B1440004533203N00603406EA0149101512
    +B1440024533212N00603414EA0148901510
    +B1440044533221N00603423EA0148801509
    +B1440064533229N00603431EA0148601507
    +B1440084533237N00603440EA0148401505
    +B1440104533245N00603449EA0148101502
    +B1440124533254N00603458EA0147901500
    +B1440144533262N00603466EA0147801498
    +B1440164533270N00603474EA0147501495
    +B1440184533279N00603483EA0147201493
    +B1440204533287N00603492EA0147001491
    +B1440224533295N00603500EA0146801488
    +B1440244533304N00603506EA0146401485
    +B1440264533313N00603512EA0146201483
    +B1440284533322N00603519EA0146001481
    +B1440304533330N00603527EA0145801479
    +B1440324533338N00603538EA0145801478
    +B1440344533343N00603550EA0145701477
    +B1440364533350N00603562EA0145601476
    +B1440384533357N00603574EA0145401475
    +B1440404533364N00603587EA0145601475
    +B1440424533369N00603601EA0145701476
    +B1440444533372N00603615EA0145401475
    +B1440464533379N00603628EA0144901473
    +B1440484533386N00603641EA0144701471
    +B1440504533394N00603655EA0144701471
    +B1440524533401N00603668EA0144901471
    +B1440544533409N00603679EA0145101473
    +B1440564533416N00603691EA0145001473
    +B1440584533424N00603704EA0144801471
    +B1441004533433N00603718EA0144701470
    +B1441024533442N00603731EA0144601469
    +B1441044533451N00603744EA0144401467
    +B1441064533461N00603756EA0144201465
    +B1441084533472N00603768EA0144301465
    +B1441104533483N00603778EA0144601466
    +B1441124533493N00603786EA0145101469
    +B1441144533501N00603796EA0145401472
    +B1441164533510N00603807EA0145401473
    +B1441184533521N00603818EA0145801476
    +B1441204533529N00603830EA0146201479
    +B1441224533537N00603844EA0146401481
    +B1441244533547N00603858EA0146601483
    +B1441264533558N00603873EA0147201487
    +B1441284533568N00603878EA0147701492
    +B1441304533571N00603871EA0148101496
    +B1441324533568N00603862EA0148501502
    +B1441344533562N00603856EA0149001508
    +B1441364533558N00603850EA0149801515
    +B1441384533559N00603843EA0150301520
    +B1441404533565N00603836EA0150601524
    +B1441424533577N00603835EA0151001529
    +B1441444533587N00603854EA0151101532
    +B1441464533579N00603878EA0151401536
    +B1441484533565N00603879EA0151801540
    +B1441504533556N00603877EA0152601546
    +B1441524533550N00603875EA0153001551
    +B1441544533546N00603868EA0153201554
    +B1441564533549N00603856EA0153601557
    +B1441584533562N00603857EA0154101561
    +B1442004533572N00603874EA0154501564
    +B1442024533571N00603895EA0154801567
    +B1442044533559N00603907EA0155001569
    +B1442064533546N00603906EA0155501574
    +B1442084533540N00603897EA0156001579
    +B1442104533539N00603888EA0156601586
    +B1442124533543N00603883EA0156901590
    +B1442144533553N00603886EA0157001592
    +B1442164533565N00603900EA0157201593
    +B1442184533567N00603922EA0157501596
    +B1442204533559N00603940EA0157701598
    +B1442224533546N00603947EA0158101601
    +B1442244533537N00603944EA0158401604
    +B1442264533533N00603937EA0158801609
    +B1442284533533N00603930EA0159201613
    +B1442304533540N00603927EA0159601617
    +B1442324533550N00603935EA0159601618
    +B1442344533556N00603958EA0159501618
    +B1442364533550N00603981EA0159501618
    +B1442384533536N00603988EA0159601617
    +B1442404533525N00603985EA0159901619
    +B1442424533516N00603982EA0160301623
    +B1442444533508N00603980EA0160701627
    +B1442464533499N00603977EA0161301632
    +B1442484533490N00603975EA0162101639
    +B1442504533484N00603974EA0162701645
    +B1442524533479N00603970EA0162401648
    +B1442544533486N00603964EA0162201649
    +B1442564533497N00603965EA0162101650
    +B1442584533508N00603978EA0162301649
    +B1443004533514N00603997EA0162601651
    +B1443024533509N00604019EA0162901655
    +B1443044533496N00604030EA0163201658
    +B1443064533481N00604032EA0163801662
    +B1443084533471N00604031EA0164601668
    +B1443104533464N00604029EA0164901672
    +B1443124533458N00604025EA0164601674
    +B1443144533461N00604013EA0164001673
    +B1443164533471N00604008EA0163601673
    +B1443184533480N00604008EA0163401670
    +B1443204533490N00604009EA0163001663
    +B1443224533500N00604011EA0162701659
    +B1443244533510N00604012EA0162701657
    +B1443264533519N00604012EA0162801656
    +B1443284533526N00604012EA0162801655
    +B1443304533535N00604012EA0162801653
    +B1443324533543N00604010EA0162701652
    +B1443344533550N00604007EA0162701651
    +B1443364533558N00604005EA0162601650
    +B1443384533565N00604002EA0162401648
    +B1443404533572N00603999EA0161901644
    +B1443424533580N00603998EA0161701641
    +B1443444533588N00603996EA0161401638
    +B1443464533595N00603995EA0161001634
    +B1443484533603N00603993EA0160801631
    +B1443504533611N00603994EA0160501628
    +B1443524533620N00603993EA0160201625
    +B1443544533629N00603990EA0160001622
    +B1443564533637N00603989EA0159801620
    +B1443584533645N00603987EA0159501617
    +B1444004533654N00603985EA0159201614
    +B1444024533662N00603983EA0158801611
    +B1444044533672N00603981EA0158501608
    +B1444064533682N00603978EA0158401606
    +B1444084533690N00603974EA0158301604
    +B1444104533698N00603971EA0158001601
    +B1444124533708N00603966EA0157501597
    +B1444144533717N00603959EA0157001594
    +B1444164533726N00603953EA0156601590
    +B1444184533735N00603945EA0156101585
    +B1444204533746N00603937EA0155801580
    +B1444224533755N00603930EA0155701578
    +B1444244533764N00603925EA0155701576
    +B1444264533773N00603921EA0156001577
    +B1444284533780N00603917EA0156301579
    +B1444304533785N00603912EA0156401580
    +B1444324533792N00603905EA0156601583
    +B1444344533798N00603899EA0157201587
    +B1444364533806N00603896EA0157901593
    +B1444384533810N00603894EA0158001596
    +B1444404533816N00603888EA0158101599
    +B1444424533824N00603882EA0158501604
    +B1444444533830N00603878EA0158801607
    +B1444464533836N00603874EA0158701608
    +B1444484533845N00603870EA0158801609
    +B1444504533854N00603867EA0158901610
    +B1444524533863N00603863EA0158901610
    +B1444544533872N00603860EA0159001611
    +B1444564533883N00603859EA0159401613
    +B1444584533892N00603858EA0159601616
    +B1445004533901N00603857EA0159701617
    +B1445024533912N00603855EA0159801619
    +B1445044533924N00603856EA0160201621
    +B1445064533934N00603858EA0160501625
    +B1445084533945N00603857EA0160801628
    +B1445104533957N00603859EA0161501633
    +B1445124533965N00603866EA0161901637
    +B1445144533973N00603876EA0162001640
    +B1445164533983N00603889EA0162301643
    +B1445184533991N00603902EA0163001648
    +B1445204533999N00603911EA0163501653
    +B1445224534008N00603922EA0163901658
    +B1445244534017N00603925EA0164301663
    +B1445264534018N00603917EA0164301667
    +B1445284534008N00603911EA0164501673
    +B1445304533998N00603916EA0165001678
    +B1445324533989N00603919EA0165301681
    +B1445344533980N00603917EA0165801685
    +B1445364533972N00603911EA0166301690
    +B1445384533967N00603905EA0166801695
    +B1445404533965N00603896EA0167201699
    +B1445424533972N00603886EA0167501702
    +B1445444533985N00603888EA0167901706
    +B1445464533994N00603905EA0168001708
    +B1445484533992N00603929EA0167901710
    +B1445504533980N00603941EA0168101710
    +B1445524533967N00603941EA0168701714
    +B1445544533958N00603939EA0169401721
    +B1445564533947N00603940EA0170001727
    +B1445584533936N00603939EA0170601733
    +B1446004533930N00603933EA0171201739
    +B1446024533930N00603924EA0171601744
    +B1446044533940N00603922EA0171901748
    +B1446064533951N00603935EA0172301752
    +B1446084533954N00603956EA0172701755
    +B1446104533944N00603972EA0173201760
    +B1446124533930N00603976EA0173901764
    +B1446144533922N00603971EA0174401770
    +B1446164533921N00603963EA0174601773
    +B1446184533929N00603957EA0174901775
    +B1446204533941N00603963EA0175201778
    +B1446224533950N00603982EA0175501782
    +B1446244533952N00604004EA0176101787
    +B1446264533946N00604024EA0176701793
    +B1446284533937N00604040EA0177401800
    +B1446304533927N00604048EA0178101808
    +B1446324533916N00604044EA0178601813
    +B1446344533911N00604035EA0179101818
    +B1446364533918N00604027EA0179301822
    +B1446384533930N00604032EA0179601826
    +B1446404533940N00604048EA0180001830
    +B1446424533946N00604066EA0180601836
    +B1446444533950N00604084EA0181101841
    +B1446464533951N00604106EA0181101845
    +B1446484533942N00604129EA0181101847
    +B1446504533925N00604141EA0181101848
    +B1446524533909N00604137EA0181601849
    +B1446544533898N00604129EA0182401854
    +B1446564533888N00604121EA0183001859
    +B1446584533879N00604113EA0183401864
    +B1447004533878N00604102EA0183901867
    +B1447024533888N00604102EA0184501873
    +B1447044533896N00604113EA0185101878
    +B1447064533899N00604131EA0185301882
    +B1447084533894N00604151EA0185601885
    +B1447104533880N00604160EA0186001889
    +B1447124533867N00604157EA0186601895
    +B1447144533859N00604147EA0187101900
    +B1447164533856N00604138EA0187801907
    +B1447184533858N00604132EA0188301912
    +B1447204533866N00604131EA0188701916
    +B1447224533877N00604140EA0189201921
    +B1447244533883N00604156EA0189601926
    +B1447264533880N00604175EA0190001930
    +B1447284533871N00604192EA0190601935
    +B1447304533857N00604198EA0191101940
    +B1447324533844N00604195EA0191601945
    +B1447344533834N00604186EA0192201950
    +B1447364533834N00604173EA0192701955
    +B1447384533840N00604170EA0193301961
    +B1447404533850N00604176EA0193601965
    +B1447424533858N00604188EA0194001970
    +B1447444533861N00604205EA0194401975
    +B1447464533859N00604224EA0194601978
    +B1447484533853N00604246EA0195001983
    +B1447504533838N00604258EA0195601987
    +B1447524533823N00604255EA0196001991
    +B1447544533814N00604244EA0196501996
    +B1447564533811N00604232EA0196901999
    +B1447584533814N00604222EA0197502004
    +B1448004533822N00604223EA0197802008
    +B1448024533831N00604233EA0198102012
    +B1448044533836N00604249EA0198402016
    +B1448064533834N00604269EA0198602018
    +B1448084533820N00604284EA0198702020
    +B1448104533803N00604286EA0199202023
    +B1448124533791N00604282EA0199702028
    +B1448144533781N00604276EA0200202033
    +B1448164533770N00604272EA0200902039
    +B1448184533761N00604269EA0201402044
    +B1448204533752N00604261EA0201802049
    +B1448224533749N00604249EA0201902052
    +B1448244533757N00604245EA0201902053
    +B1448264533766N00604255EA0202102055
    +B1448284533769N00604272EA0202202056
    +B1448304533765N00604293EA0202502059
    +B1448324533755N00604308EA0203002062
    +B1448344533744N00604321EA0203602067
    +B1448364533738N00604337EA0203902070
    +B1448384533736N00604353EA0204202073
    +B1448404533740N00604366EA0204502076
    +B1448424533747N00604370EA0204702078
    +B1448444533753N00604360EA0204602079
    +B1448464533751N00604344EA0204802081
    +B1448484533743N00604333EA0205402085
    +B1448504533733N00604325EA0205802089
    +B1448524533724N00604318EA0206202093
    +B1448544533714N00604314EA0206302095
    +B1448564533699N00604321EA0206502097
    +B1448584533690N00604338EA0206702099
    +B1449004533693N00604353EA0206902101
    +B1449024533701N00604356EA0207202104
    +B1449044533704N00604343EA0207402107
    +B1449064533701N00604330EA0207702110
    +B1449084533692N00604320EA0207802111
    +B1449104533679N00604314EA0207802112
    +B1449124533664N00604319EA0208002114
    +B1449144533653N00604330EA0208202115
    +B1449164533644N00604341EA0208402118
    +B1449184533640N00604354EA0208602119
    +B1449204533639N00604368EA0208702121
    +B1449224533644N00604377EA0208802121
    +B1449244533653N00604372EA0208902122
    +B1449264533657N00604357EA0209102123
    +B1449284533652N00604341EA0209202124
    +B1449304533640N00604330EA0209102124
    +B1449324533623N00604330EA0209102124
    +B1449344533608N00604338EA0209102124
    +B1449364533596N00604348EA0209402126
    +B1449384533585N00604358EA0209602128
    +B1449404533578N00604369EA0209502129
    +B1449424533575N00604384EA0209302128
    +B1449444533577N00604397EA0209002126
    +B1449464533582N00604405EA0208702123
    +B1449484533589N00604408EA0208702122
    +B1449504533596N00604408EA0208702122
    +B1449524533602N00604409EA0208602120
    +B1449544533608N00604415EA0208302117
    +B1449564533613N00604423EA0208002113
    +B1449584533619N00604436EA0207702110
    +B1450004533623N00604450EA0207402107
    +B1450024533625N00604464EA0206902103
    +B1450044533626N00604480EA0206402098
    +B1450064533627N00604499EA0206002094
    +B1450084533627N00604515EA0205702091
    +B1450104533627N00604528EA0205402087
    +B1450124533629N00604543EA0204902082
    +B1450144533631N00604558EA0204702079
    +B1450164533631N00604572EA0204602078
    +B1450184533631N00604586EA0204302074
    +B1450204533632N00604601EA0204202072
    +B1450224533633N00604615EA0204102071
    +B1450244533633N00604629EA0204102071
    +B1450264533634N00604641EA0204002069
    +B1450284533635N00604656EA0203802068
    +B1450304533636N00604670EA0204002068
    +B1450324533635N00604684EA0204002068
    +B1450344533634N00604698EA0203902067
    +B1450364533635N00604712EA0203802067
    +B1450384533635N00604727EA0203702066
    +B1450404533634N00604741EA0203702066
    +B1450424533632N00604756EA0203702065
    +B1450444533630N00604771EA0203502065
    +B1450464533628N00604785EA0203402063
    +B1450484533627N00604799EA0203202062
    +B1450504533626N00604814EA0203002060
    +B1450524533626N00604829EA0202802058
    +B1450544533627N00604843EA0202702057
    +B1450564533627N00604857EA0202602055
    +B1450584533627N00604871EA0202302053
    +B1451004533627N00604885EA0202002050
    +B1451024533628N00604900EA0201802048
    +B1451044533630N00604915EA0201602046
    +B1451064533631N00604931EA0201502045
    +B1451084533633N00604946EA0201302043
    +B1451104533634N00604961EA0201102041
    +B1451124533635N00604976EA0200902038
    +B1451144533636N00604992EA0200602036
    +B1451164533638N00605006EA0200302033
    +B1451184533640N00605021EA0200002030
    +B1451204533643N00605037EA0199702026
    +B1451224533646N00605052EA0199502024
    +B1451244533649N00605067EA0199302022
    +B1451264533652N00605081EA0198902019
    +B1451284533656N00605096EA0198702016
    +B1451304533660N00605111EA0198702015
    +B1451324533662N00605124EA0198502014
    +B1451344533665N00605139EA0198302012
    +B1451364533667N00605154EA0198202011
    +B1451384533669N00605167EA0198102010
    +B1451404533673N00605180EA0197902008
    +B1451424533676N00605194EA0197802006
    +B1451444533679N00605208EA0198002007
    +B1451464533680N00605220EA0197902006
    +B1451484533681N00605232EA0197502004
    +B1451504533683N00605247EA0197302001
    +B1451524533687N00605261EA0197202001
    +B1451544533690N00605274EA0197102000
    +B1451564533695N00605286EA0197001998
    +B1451584533700N00605298EA0196801997
    +B1452004533704N00605310EA0196701997
    +B1452024533710N00605321EA0196601996
    +B1452044533716N00605331EA0196601995
    +B1452064533722N00605339EA0196501995
    +B1452084533729N00605346EA0196301993
    +B1452104533736N00605353EA0196101991
    +B1452124533743N00605362EA0195901989
    +B1452144533749N00605371EA0195801987
    +B1452164533755N00605382EA0195501985
    +B1452184533761N00605392EA0195301982
    +B1452204533768N00605402EA0195201980
    +B1452224533775N00605410EA0194901978
    +B1452244533783N00605418EA0194501975
    +B1452264533791N00605426EA0194201972
    +B1452284533799N00605432EA0194001969
    +B1452304533806N00605439EA0193601966
    +B1452324533813N00605447EA0193401963
    +B1452344533820N00605455EA0193101960
    +B1452364533827N00605462EA0192801957
    +B1452384533835N00605470EA0192601955
    +B1452404533842N00605477EA0192601954
    +B1452424533849N00605483EA0192501953
    +B1452444533857N00605489EA0192301951
    +B1452464533865N00605495EA0192301950
    +B1452484533872N00605502EA0192201949
    +B1452504533879N00605510EA0192201950
    +B1452524533885N00605518EA0192201950
    +B1452544533893N00605527EA0192201950
    +B1452564533900N00605536EA0192101950
    +B1452584533906N00605547EA0192101949
    +B1453004533911N00605559EA0192201949
    +B1453024533917N00605571EA0192301951
    +B1453044533921N00605581EA0192401952
    +B1453064533926N00605592EA0192401952
    +B1453084533932N00605603EA0192301951
    +B1453104533939N00605613EA0192201951
    +B1453124533943N00605624EA0192001949
    +B1453144533948N00605635EA0191701946
    +B1453164533954N00605647EA0191401943
    +B1453184533959N00605659EA0191201941
    +B1453204533963N00605669EA0191001939
    +B1453224533967N00605682EA0190501935
    +B1453244533974N00605693EA0190101931
    +B1453264533980N00605704EA0189701927
    +B1453284533985N00605717EA0189501924
    +B1453304533991N00605728EA0189201921
    +B1453324533997N00605738EA0188801917
    +B1453344534004N00605748EA0188501914
    +B1453364534011N00605757EA0188301911
    +B1453384534020N00605765EA0188101909
    +B1453404534027N00605773EA0187901906
    +B1453424534035N00605781EA0187401903
    +B1453444534044N00605789EA0187101899
    +B1453464534054N00605797EA0186901897
    +B1453484534063N00605804EA0186601894
    +B1453504534072N00605809EA0186201891
    +B1453524534082N00605816EA0185901887
    +B1453544534092N00605823EA0185701884
    +B1453564534102N00605830EA0185501882
    +B1453584534111N00605836EA0185301880
    +B1454004534121N00605843EA0185201878
    +B1454024534131N00605849EA0184901876
    +B1454044534142N00605855EA0185001875
    +B1454064534151N00605860EA0185301877
    +B1454084534159N00605865EA0185201876
    +B1454104534167N00605867EA0184801875
    +B1454124534178N00605873EA0184301871
    +B1454144534190N00605879EA0184101869
    +B1454164534199N00605884EA0183801866
    +B1454184534209N00605890EA0183401862
    +B1454204534219N00605897EA0182901857
    +B1454224534229N00605903EA0182601854
    +B1454244534239N00605909EA0182201850
    +B1454264534248N00605915EA0181801845
    +B1454284534258N00605921EA0181301841
    +B1454304534268N00605928EA0181101839
    +B1454324534278N00605935EA0180701835
    +B1454344534288N00605944EA0180201830
    +B1454364534300N00605954EA0180001826
    +B1454384534310N00605962EA0179901824
    +B1454404534320N00605969EA0179401819
    +B1454424534331N00605978EA0179001815
    +B1454444534344N00605987EA0179101813
    +B1454464534354N00605994EA0179201813
    +B1454484534364N00605999EA0178801810
    +B1454504534375N00606005EA0178401806
    +B1454524534386N00606013EA0178301804
    +B1454544534397N00606021EA0178101803
    +B1454564534408N00606028EA0177801801
    +B1454584534418N00606036EA0177601799
    +B1455004534428N00606043EA0177301797
    +B1455024534440N00606048EA0177101794
    +B1455044534452N00606055EA0177101792
    +B1455064534463N00606064EA0177201792
    +B1455084534473N00606071EA0177201793
    +B1455104534483N00606078EA0177201793
    +B1455124534494N00606084EA0177301794
    +B1455144534505N00606089EA0177501794
    +B1455164534517N00606091EA0177301794
    +B1455184534529N00606096EA0177401795
    +B1455204534540N00606103EA0177501795
    +B1455224534552N00606110EA0177401795
    +B1455244534565N00606119EA0177401796
    +B1455264534577N00606128EA0177601797
    +B1455284534589N00606134EA0177801799
    +B1455304534601N00606140EA0178101802
    +B1455324534613N00606147EA0178201804
    +B1455344534625N00606155EA0178401805
    +B1455364534636N00606162EA0178801809
    +B1455384534646N00606169EA0179001811
    +B1455404534657N00606178EA0179301815
    +B1455424534669N00606188EA0180101819
    +B1455444534678N00606196EA0181101826
    +B1455464534685N00606202EA0181801833
    +B1455484534694N00606205EA0182301839
    +B1455504534703N00606195EA0182901847
    +B1455524534707N00606183EA0183501854
    +B1455544534705N00606170EA0183901859
    +B1455564534698N00606158EA0184101863
    +B1455584534685N00606154EA0184301867
    +B1456004534672N00606161EA0184801872
    +B1456024534663N00606174EA0185801878
    +B1456044534659N00606189EA0186601887
    +B1456064534659N00606203EA0187401896
    +B1456084534661N00606218EA0188101903
    +B1456104534666N00606232EA0188801911
    +B1456124534678N00606236EA0189601920
    +B1456144534686N00606227EA0189901926
    +B1456164534684N00606209EA0190101932
    +B1456184534670N00606203EA0190501937
    +B1456204534656N00606215EA0191301944
    +B1456224534644N00606227EA0192501955
    +B1456244534635N00606236EA0193401965
    +B1456264534631N00606249EA0193801973
    +B1456284534633N00606267EA0194201979
    +B1456304534643N00606278EA0195001985
    +B1456324534653N00606276EA0195901993
    +B1456344534659N00606265EA0196501999
    +B1456364534655N00606251EA0197102005
    +B1456384534643N00606248EA0197802011
    +B1456404534632N00606255EA0198702018
    +B1456424534624N00606269EA0199302024
    +B1456444534620N00606285EA0200002030
    +B1456464534625N00606301EA0200602036
    +B1456484534638N00606306EA0201302042
    +B1456504534646N00606296EA0202102048
    +B1456524534645N00606280EA0202802055
    +B1456544534636N00606272EA0203502062
    +B1456564534624N00606271EA0204102069
    +B1456584534613N00606279EA0204902076
    +B1457004534606N00606294EA0205702084
    +B1457024534609N00606309EA0206602092
    +B1457044534619N00606317EA0207502102
    +B1457064534627N00606310EA0208102110
    +B1457084534627N00606293EA0208802117
    +B1457104534617N00606284EA0209402125
    +B1457124534603N00606286EA0210202133
    +B1457144534593N00606297EA0211102141
    +B1457164534587N00606313EA0211802149
    +B1457184534586N00606329EA0212602156
    +B1457204534594N00606341EA0213202163
    +B1457224534604N00606341EA0214002171
    +B1457244534612N00606332EA0215002179
    +B1457264534611N00606317EA0216002188
    +B1457284534600N00606309EA0216802198
    +B1457304534587N00606318EA0217602206
    +B1457324534581N00606335EA0218302215
    +B1457344534583N00606354EA0219302226
    +B1457364534590N00606366EA0220402237
    +B1457384534600N00606368EA0221302247
    +B1457404534608N00606362EA0222202257
    +B1457424534609N00606349EA0222802265
    +B1457444534600N00606337EA0223502274
    +B1457464534589N00606335EA0224302282
    +B1457484534576N00606347EA0225002290
    +B1457504534570N00606365EA0226202300
    +B1457524534573N00606381EA0227202309
    +B1457544534583N00606389EA0228102318
    +B1457564534592N00606384EA0228902327
    +B1457584534592N00606370EA0229602334
    +B1458004534584N00606359EA0230302342
    +B1458024534571N00606360EA0231102349
    +B1458044534558N00606370EA0232102359
    +B1458064534551N00606386EA0233102369
    +B1458084534551N00606403EA0234002378
    +B1458104534560N00606415EA0234902387
    +B1458124534570N00606416EA0235702395
    +B1458144534576N00606406EA0236402403
    +B1458164534575N00606391EA0237102410
    +B1458184534566N00606381EA0237802417
    +B1458204534556N00606376EA0238602425
    +B1458224534543N00606378EA0239302432
    +B1458244534532N00606395EA0239902439
    +B1458264534532N00606414EA0240702446
    +B1458284534541N00606421EA0241602453
    +B1458304534549N00606414EA0242302460
    +B1458324534549N00606401EA0242802466
    +B1458344534540N00606388EA0243502474
    +B1458364534526N00606387EA0244302483
    +B1458384534511N00606393EA0245302492
    +B1458404534501N00606408EA0246202502
    +B1458424534497N00606424EA0247102510
    +B1458444534503N00606438EA0247902519
    +B1458464534511N00606443EA0248902528
    +B1458484534519N00606436EA0249602537
    +B1458504534522N00606423EA0250402545
    +B1458524534516N00606410EA0251202553
    +B1458544534502N00606406EA0252002561
    +B1458564534488N00606413EA0252802569
    +B1458584534480N00606426EA0253702578
    +B1459004534478N00606440EA0254402586
    +B1459024534480N00606453EA0255402595
    +B1459044534488N00606458EA0256102603
    +B1459064534497N00606456EA0256902612
    +B1459084534504N00606451EA0257802620
    +B1459104534508N00606441EA0258402628
    +B1459124534508N00606426EA0258902634
    +B1459144534499N00606410EA0259502639
    +B1459164534484N00606408EA0260102645
    +B1459184534471N00606417EA0260702650
    +B1459204534463N00606431EA0261302655
    +B1459224534459N00606447EA0262102662
    +B1459244534458N00606462EA0262802669
    +B1459264534457N00606479EA0263602676
    +B1459284534453N00606495EA0264502684
    +B1459304534446N00606509EA0265202691
    +B1459324534440N00606526EA0265702697
    +B1459344534431N00606543EA0266302703
    +B1459364534423N00606560EA0266802709
    +B1459384534415N00606575EA0267302714
    +B1459404534407N00606589EA0267502718
    +B1459424534400N00606603EA0267402720
    +B1459444534390N00606619EA0267502720
    +B1459464534381N00606636EA0267802722
    +B1459484534372N00606650EA0267802722
    +B1459504534361N00606664EA0267502721
    +B1459524534349N00606678EA0267502720
    +B1459544534337N00606694EA0267702721
    +B1459564534326N00606708EA0267802722
    +B1459584534313N00606721EA0267802722
    +B1500004534302N00606735EA0267802722
    +B1500024534292N00606747EA0267402720
    +B1500044534281N00606762EA0267002717
    +B1500064534271N00606781EA0266902716
    +B1500084534264N00606797EA0266702715
    +B1500104534259N00606813EA0266202711
    +B1500124534257N00606832EA0265602706
    +B1500144534260N00606850EA0265102701
    +B1500164534263N00606864EA0264802697
    +B1500184534266N00606877EA0264302691
    +B1500204534271N00606891EA0263802684
    +B1500224534276N00606903EA0263202678
    +B1500244534282N00606915EA0262602671
    +B1500264534289N00606930EA0262102664
    +B1500284534295N00606945EA0261802659
    +B1500304534301N00606960EA0261502656
    +B1500324534307N00606974EA0261202652
    +B1500344534315N00606989EA0261102649
    +B1500364534324N00607001EA0261102648
    +B1500384534331N00607008EA0260702645
    +B1500404534335N00607016EA0259502641
    +B1500424534341N00607028EA0258402635
    +B1500444534353N00607046EA0258002628
    +B1500464534362N00607062EA0258202625
    +B1500484534369N00607072EA0258202624
    +B1500504534377N00607081EA0257902620
    +B1500524534386N00607092EA0257702618
    +B1500544534395N00607103EA0257602616
    +B1500564534404N00607114EA0257402613
    +B1500584534414N00607123EA0257202611
    +B1501004534425N00607130EA0256902609
    +B1501024534436N00607133EA0256702606
    +B1501044534448N00607135EA0256302602
    +B1501064534461N00607136EA0256002598
    +B1501084534474N00607140EA0255602595
    +B1501104534488N00607144EA0255302591
    +B1501124534501N00607148EA0255002588
    +B1501144534514N00607152EA0254702585
    +B1501164534528N00607155EA0254502582
    +B1501184534541N00607158EA0254302580
    +B1501204534555N00607160EA0254102578
    +B1501224534568N00607162EA0254002577
    +B1501244534582N00607163EA0253802575
    +B1501264534596N00607165EA0253702574
    +B1501284534610N00607166EA0253602573
    +B1501304534623N00607167EA0253502572
    +B1501324534637N00607168EA0253402571
    +B1501344534651N00607168EA0253302569
    +B1501364534665N00607168EA0253102568
    +B1501384534678N00607167EA0253002566
    +B1501404534691N00607165EA0252802565
    +B1501424534704N00607161EA0252702563
    +B1501444534718N00607158EA0252402561
    +B1501464534731N00607153EA0252202559
    +B1501484534744N00607148EA0252102557
    +B1501504534757N00607143EA0251902555
    +B1501524534771N00607139EA0251702554
    +B1501544534784N00607135EA0251602552
    +B1501564534798N00607132EA0251402550
    +B1501584534811N00607129EA0251202548
    +B1502004534824N00607127EA0250902546
    +B1502024534838N00607124EA0250702544
    +B1502044534852N00607122EA0250602542
    +B1502064534865N00607121EA0250602542
    +B1502084534878N00607122EA0250502541
    +B1502104534891N00607121EA0250202539
    +B1502124534905N00607120EA0249902536
    +B1502144534920N00607121EA0249702534
    +B1502164534933N00607122EA0249502532
    +B1502184534947N00607125EA0249202530
    +B1502204534962N00607132EA0249102528
    +B1502224534975N00607141EA0249102527
    +B1502244534988N00607150EA0249102527
    +B1502264535000N00607158EA0248902526
    +B1502284535012N00607166EA0248602524
    +B1502304535024N00607174EA0248302520
    +B1502324535036N00607186EA0247902516
    +B1502344535047N00607198EA0247602513
    +B1502364535057N00607211EA0247202509
    +B1502384535067N00607224EA0246802505
    +B1502404535076N00607237EA0246402501
    +B1502424535085N00607250EA0245902496
    +B1502444535094N00607264EA0245202489
    +B1502464535105N00607278EA0245002485
    +B1502484535114N00607293EA0244802481
    +B1502504535122N00607307EA0244502477
    +B1502524535130N00607322EA0244202473
    +B1502544535140N00607337EA0243902471
    +B1502564535147N00607351EA0243702468
    +B1502584535154N00607365EA0243302464
    +B1503004535162N00607379EA0242902460
    +B1503024535170N00607394EA0242602458
    +B1503044535176N00607411EA0242302455
    +B1503064535183N00607426EA0242102453
    +B1503084535190N00607441EA0241802450
    +B1503104535197N00607455EA0241502447
    +B1503124535204N00607469EA0241302446
    +B1503144535210N00607484EA0241102443
    +B1503164535216N00607499EA0240802440
    +B1503184535222N00607514EA0240502438
    +B1503204535228N00607530EA0240302436
    +B1503224535232N00607547EA0240102433
    +B1503244535238N00607564EA0239902432
    +B1503264535245N00607580EA0239702430
    +B1503284535251N00607595EA0239602429
    +B1503304535258N00607610EA0239402427
    +B1503324535266N00607624EA0239202425
    +B1503344535273N00607640EA0238902422
    +B1503364535281N00607656EA0238702420
    +B1503384535288N00607673EA0238702419
    +B1503404535295N00607688EA0238802419
    +B1503424535302N00607702EA0238702418
    +B1503444535309N00607718EA0238402416
    +B1503464535317N00607735EA0238302416
    +B1503484535325N00607750EA0238302415
    +B1503504535333N00607765EA0238102414
    +B1503524535341N00607780EA0237902412
    +B1503544535350N00607796EA0237702410
    +B1503564535358N00607810EA0237602409
    +B1503584535365N00607822EA0237402407
    +B1504004535373N00607836EA0237002404
    +B1504024535381N00607850EA0236802402
    +B1504044535388N00607863EA0236602399
    +B1504064535395N00607875EA0236202396
    +B1504084535403N00607888EA0235902393
    +B1504104535411N00607900EA0235702391
    +B1504124535419N00607912EA0235402388
    +B1504144535427N00607926EA0235102385
    +B1504164535435N00607940EA0234902383
    +B1504184535444N00607953EA0234602380
    +B1504204535452N00607967EA0234302377
    +B1504224535460N00607982EA0233902373
    +B1504244535467N00607996EA0233702371
    +B1504264535473N00608010EA0233402368
    +B1504284535480N00608026EA0232902364
    +B1504304535486N00608043EA0232802361
    +B1504324535492N00608059EA0232502359
    +B1504344535498N00608075EA0232202355
    +B1504364535505N00608092EA0231902353
    +B1504384535512N00608107EA0231802351
    +B1504404535518N00608121EA0231602349
    +B1504424535525N00608136EA0231302346
    +B1504444535533N00608150EA0231002344
    +B1504464535540N00608165EA0230702340
    +B1504484535547N00608183EA0230402337
    +B1504504535553N00608200EA0230302336
    +B1504524535559N00608215EA0230002333
    +B1504544535565N00608232EA0229702330
    +B1504564535573N00608249EA0229302327
    +B1504584535580N00608265EA0229302325
    +B1505004535587N00608281EA0229102324
    +B1505024535593N00608296EA0229002322
    +B1505044535600N00608312EA0228702320
    +B1505064535606N00608327EA0228602318
    +B1505084535613N00608341EA0228302316
    +B1505104535620N00608356EA0228002313
    +B1505124535627N00608371EA0227702310
    +B1505144535634N00608386EA0227502308
    +B1505164535639N00608402EA0227302305
    +B1505184535644N00608417EA0226902302
    +B1505204535651N00608433EA0226602299
    +B1505224535658N00608448EA0226502298
    +B1505244535663N00608462EA0226102294
    +B1505264535671N00608476EA0225702290
    +B1505284535679N00608492EA0225402288
    +B1505304535685N00608508EA0225402286
    +B1505324535690N00608523EA0225302285
    +B1505344535694N00608539EA0225002282
    +B1505364535701N00608554EA0224702279
    +B1505384535708N00608567EA0224602278
    +B1505404535713N00608582EA0224502277
    +B1505424535718N00608596EA0224202274
    +B1505444535725N00608611EA0224002272
    +B1505464535731N00608625EA0223902271
    +B1505484535736N00608638EA0223602269
    +B1505504535743N00608652EA0223202265
    +B1505524535750N00608667EA0223002263
    +B1505544535757N00608679EA0222802262
    +B1505564535763N00608692EA0222502258
    +B1505584535771N00608705EA0222202256
    +B1506004535777N00608719EA0221902253
    +B1506024535782N00608733EA0221602250
    +B1506044535787N00608748EA0221302247
    +B1506064535792N00608763EA0221002244
    +B1506084535798N00608776EA0220702240
    +B1506104535802N00608790EA0220302236
    +B1506124535807N00608805EA0220002233
    +B1506144535811N00608820EA0219802230
    +B1506164535812N00608835EA0219502227
    +B1506184535814N00608851EA0219202223
    +B1506204535818N00608866EA0219002221
    +B1506224535822N00608883EA0219102220
    +B1506244535824N00608899EA0219202221
    +B1506264535826N00608910EA0219102221
    +B1506284535828N00608919EA0218702220
    +B1506304535830N00608933EA0218602219
    +B1506324535831N00608946EA0218902220
    +B1506344535831N00608957EA0218902219
    +B1506364535834N00608968EA0218502217
    +B1506384535838N00608981EA0218502216
    +B1506404535842N00608995EA0218802216
    +B1506424535845N00609008EA0219202218
    +B1506444535849N00609019EA0219402222
    +B1506464535852N00609031EA0219602224
    +B1506484535858N00609041EA0219802226
    +B1506504535864N00609052EA0220102229
    +B1506524535870N00609063EA0220402232
    +B1506544535876N00609075EA0220702235
    +B1506564535882N00609086EA0221002239
    +B1506584535889N00609095EA0221302242
    +B1507004535896N00609104EA0221402244
    +B1507024535904N00609113EA0221602246
    +B1507044535910N00609123EA0222002250
    +B1507064535917N00609132EA0222202252
    +B1507084535924N00609141EA0222402255
    +B1507104535932N00609148EA0222902259
    +B1507124535938N00609156EA0223402264
    +B1507144535944N00609163EA0224202271
    +B1507164535949N00609172EA0225102279
    +B1507184535956N00609181EA0226002288
    +B1507204535962N00609188EA0226702296
    +B1507224535969N00609193EA0227402304
    +B1507244535976N00609202EA0228202312
    +B1507264535981N00609210EA0229302322
    +B1507284535987N00609212EA0230102331
    +B1507304535993N00609214EA0230402339
    +B1507324536000N00609222EA0230902345
    +B1507344536007N00609231EA0231802353
    +B1507364536013N00609237EA0232602362
    +B1507384536019N00609243EA0233002368
    +B1507404536024N00609251EA0233002372
    +B1507424536031N00609259EA0233002374
    +B1507444536038N00609268EA0233102374
    +B1507464536046N00609278EA0233002373
    +B1507484536054N00609286EA0233102373
    +B1507504536063N00609294EA0233202374
    +B1507524536070N00609304EA0233202373
    +B1507544536077N00609314EA0233202373
    +B1507564536084N00609325EA0233102372
    +B1507584536091N00609337EA0233102371
    +B1508004536098N00609348EA0233002370
    +B1508024536105N00609359EA0232802368
    +B1508044536113N00609369EA0232602366
    +B1508064536122N00609380EA0232502365
    +B1508084536130N00609391EA0232502365
    +B1508104536137N00609402EA0232502364
    +B1508124536144N00609414EA0232302362
    +B1508144536151N00609426EA0232102361
    +B1508164536156N00609439EA0231902359
    +B1508184536161N00609452EA0231602355
    +B1508204536168N00609466EA0231302353
    +B1508224536174N00609480EA0231302351
    +B1508244536180N00609494EA0231302350
    +B1508264536186N00609507EA0231102348
    +B1508284536192N00609520EA0231002347
    +B1508304536197N00609535EA0230902346
    +B1508324536202N00609549EA0230802344
    +B1508344536207N00609565EA0230702343
    +B1508364536211N00609581EA0230802343
    +B1508384536214N00609596EA0230802342
    +B1508404536216N00609612EA0230602341
    +B1508424536219N00609628EA0230502340
    +B1508444536221N00609644EA0230502339
    +B1508464536223N00609660EA0230402338
    +B1508484536226N00609674EA0230302337
    +B1508504536229N00609689EA0230202337
    +B1508524536229N00609706EA0230102336
    +B1508544536226N00609723EA0230002335
    +B1508564536221N00609738EA0229902334
    +B1508584536216N00609754EA0229802333
    +B1509004536209N00609769EA0229602332
    +B1509024536202N00609785EA0229602331
    +B1509044536194N00609801EA0229502331
    +B1509064536187N00609817EA0229502330
    +B1509084536180N00609833EA0229402330
    +B1509104536173N00609849EA0229302329
    +B1509124536166N00609865EA0229302329
    +B1509144536159N00609882EA0229302329
    +B1509164536152N00609897EA0229202329
    +B1509184536146N00609913EA0229102328
    +B1509204536140N00609929EA0228902326
    +B1509224536133N00609946EA0229002326
    +B1509244536126N00609963EA0229002326
    +B1509264536118N00609978EA0229002326
    +B1509284536111N00609995EA0228902325
    +B1509304536103N00610011EA0228902325
    +B1509324536096N00610027EA0229002326
    +B1509344536087N00610043EA0229002327
    +B1509364536080N00610058EA0229102327
    +B1509384536073N00610073EA0229102327
    +B1509404536067N00610090EA0229102327
    +B1509424536059N00610106EA0229102328
    +B1509444536052N00610123EA0229302329
    +B1509464536045N00610138EA0229502331
    +B1509484536038N00610153EA0229602332
    +B1509504536031N00610169EA0229802334
    +B1509524536024N00610183EA0229802335
    +B1509544536016N00610197EA0229802335
    +B1509564536008N00610212EA0229802335
    +B1509584535999N00610226EA0229802335
    +B1510004535990N00610241EA0229702334
    +B1510024535981N00610258EA0229602333
    +B1510044535971N00610274EA0229702333
    +B1510064535962N00610290EA0229802335
    +B1510084535953N00610305EA0229902336
    +B1510104535945N00610320EA0230102337
    +B1510124535936N00610335EA0230302339
    +B1510144535926N00610348EA0230502341
    +B1510164535917N00610360EA0230602343
    +B1510184535907N00610371EA0230802345
    +B1510204535897N00610382EA0231002346
    +B1510224535887N00610393EA0231402349
    +B1510244535876N00610402EA0231802353
    +B1510264535866N00610410EA0232302358
    +B1510284535855N00610419EA0233002364
    +B1510304535844N00610425EA0233802371
    +B1510324535835N00610432EA0234502378
    +B1510344535830N00610447EA0234902384
    +B1510364535832N00610465EA0235602390
    +B1510384535843N00610470EA0236302397
    +B1510404535850N00610458EA0237002404
    +B1510424535847N00610444EA0237802411
    +B1510444535835N00610435EA0238302418
    +B1510464535820N00610439EA0238902426
    +B1510484535809N00610453EA0239802434
    +B1510504535808N00610469EA0240402442
    +B1510524535813N00610484EA0241102448
    +B1510544535821N00610495EA0241802456
    +B1510564535830N00610493EA0242402462
    +B1510584535835N00610482EA0243102469
    +B1511004535831N00610468EA0243902477
    +B1511024535818N00610463EA0244502484
    +B1511044535805N00610471EA0245402493
    +B1511064535798N00610488EA0246002501
    +B1511084535799N00610506EA0246502507
    +B1511104535804N00610521EA0247002512
    +B1511124535813N00610529EA0247602517
    +B1511144535824N00610527EA0248302523
    +B1511164535829N00610518EA0249002530
    +B1511184535828N00610505EA0249802538
    +B1511204535820N00610492EA0250602546
    +B1511224535806N00610490EA0251202552
    +B1511244535792N00610497EA0252302561
    +B1511264535785N00610513EA0253102570
    +B1511284535783N00610530EA0253402577
    +B1511304535785N00610549EA0253802582
    +B1511324535793N00610565EA0254102585
    +B1511344535805N00610570EA0254602589
    +B1511364535814N00610568EA0255202595
    +B1511384535819N00610559EA0256102601
    +B1511404535818N00610548EA0256902608
    +B1511424535813N00610538EA0257602615
    +B1511444535802N00610531EA0258202622
    +B1511464535786N00610534EA0258902628
    +B1511484535776N00610551EA0259702635
    +B1511504535775N00610573EA0260302642
    +B1511524535783N00610589EA0260602648
    +B1511544535793N00610599EA0261002651
    +B1511564535805N00610598EA0261702657
    +B1511584535809N00610589EA0262302663
    +B1512004535803N00610576EA0262602667
    +B1512024535787N00610573EA0263102672
    +B1512044535772N00610588EA0263902679
    +B1512064535763N00610607EA0264802687
    +B1512084535758N00610622EA0265202693
    +B1512104535755N00610638EA0265002697
    +B1512124535751N00610659EA0264902699
    +B1512144535746N00610681EA0265102698
    +B1512164535742N00610703EA0265202700
    +B1512184535739N00610723EA0265402701
    +B1512204535735N00610740EA0265602702
    +B1512224535730N00610757EA0265602702
    +B1512244535724N00610776EA0265802703
    +B1512264535717N00610794EA0265702702
    +B1512284535710N00610812EA0265402700
    +B1512304535703N00610831EA0265102698
    +B1512324535699N00610853EA0264602693
    +B1512344535697N00610875EA0264202688
    +B1512364535693N00610895EA0263502682
    +B1512384535688N00610914EA0262902675
    +B1512404535688N00610934EA0262302668
    +B1512424535693N00610954EA0261902662
    +B1512444535698N00610973EA0261502657
    +B1512464535702N00610991EA0260702650
    +B1512484535706N00611008EA0259802642
    +B1512504535713N00611028EA0259002634
    +B1512524535717N00611048EA0258402627
    +B1512544535722N00611065EA0257702619
    +B1512564535728N00611079EA0256902611
    +B1512584535736N00611095EA0256002602
    +B1513004535744N00611111EA0255302594
    +B1513024535752N00611128EA0254702587
    +B1513044535760N00611144EA0254102580
    +B1513064535768N00611161EA0253702575
    +B1513084535774N00611177EA0253202569
    +B1513104535779N00611193EA0252302562
    +B1513124535788N00611210EA0251502555
    +B1513144535797N00611228EA0251202549
    +B1513164535806N00611244EA0251302544
    +B1513184535812N00611258EA0251202541
    +B1513204535818N00611273EA0250902537
    +B1513224535826N00611289EA0250602535
    +B1513244535833N00611305EA0250402533
    +B1513264535841N00611320EA0250202531
    +B1513284535848N00611334EA0250002530
    +B1513304535855N00611348EA0249802528
    +B1513324535862N00611362EA0249302524
    +B1513344535871N00611378EA0249002521
    +B1513364535879N00611395EA0248802519
    +B1513384535888N00611411EA0248402515
    +B1513404535897N00611427EA0248102512
    +B1513424535906N00611444EA0248102511
    +B1513444535914N00611459EA0248002510
    +B1513464535921N00611473EA0247702507
    +B1513484535930N00611488EA0247302503
    +B1513504535940N00611504EA0247102501
    +B1513524535948N00611520EA0247302500
    +B1513544535955N00611533EA0247502501
    +B1513564535960N00611546EA0247502501
    +B1513584535965N00611558EA0247402501
    +B1514004535971N00611571EA0247302500
    +B1514024535976N00611584EA0247202500
    +B1514044535982N00611595EA0247102499
    +B1514064535988N00611608EA0247002499
    +B1514084535994N00611622EA0246902498
    +B1514104536000N00611635EA0246802497
    +B1514124536007N00611649EA0246602496
    +B1514144536013N00611663EA0246502495
    +B1514164536019N00611677EA0246402494
    +B1514184536025N00611691EA0246102491
    +B1514204536032N00611705EA0245702488
    +B1514224536039N00611721EA0245302484
    +B1514244536045N00611736EA0244802480
    +B1514264536053N00611751EA0244502477
    +B1514284536060N00611764EA0244302474
    +B1514304536067N00611777EA0243902471
    +B1514324536075N00611789EA0243502467
    +B1514344536083N00611803EA0243202463
    +B1514364536091N00611816EA0243002461
    +B1514384536098N00611827EA0242702459
    +B1514404536105N00611840EA0242302456
    +B1514424536112N00611855EA0242002453
    +B1514444536119N00611868EA0241702451
    +B1514464536126N00611879EA0241502448
    +B1514484536134N00611889EA0241102445
    +B1514504536142N00611898EA0240802442
    +B1514524536148N00611907EA0240302438
    +B1514544536154N00611919EA0239802433
    +B1514564536161N00611933EA0239602429
    +B1514584536168N00611943EA0239402427
    +B1515004536174N00611952EA0238902423
    +B1515024536182N00611964EA0238602419
    +B1515044536189N00611975EA0238502417
    +B1515064536195N00611986EA0238302415
    +B1515084536201N00611998EA0237902411
    +B1515104536207N00612011EA0237502408
    +B1515124536214N00612021EA0237202405
    +B1515144536221N00612028EA0236702401
    +B1515164536228N00612038EA0236302397
    +B1515184536234N00612048EA0236102395
    +B1515204536240N00612057EA0235702391
    +B1515224536246N00612068EA0235202387
    +B1515244536253N00612078EA0234902384
    +B1515264536259N00612087EA0234702381
    +B1515284536264N00612096EA0234402378
    +B1515304536271N00612106EA0234102375
    +B1515324536278N00612116EA0233902373
    +B1515344536285N00612125EA0233602370
    +B1515364536291N00612135EA0233302367
    +B1515384536298N00612146EA0233002365
    +B1515404536305N00612157EA0232802363
    +B1515424536312N00612168EA0232602361
    +B1515444536318N00612179EA0232402359
    +B1515464536324N00612191EA0232202356
    +B1515484536331N00612204EA0232002354
    +B1515504536336N00612218EA0231702352
    +B1515524536342N00612231EA0231402349
    +B1515544536349N00612245EA0231202347
    +B1515564536355N00612260EA0231102346
    +B1515584536362N00612273EA0231102345
    +B1516004536368N00612287EA0231002344
    +B1516024536374N00612302EA0230802342
    +B1516044536380N00612315EA0230502339
    +B1516064536387N00612331EA0230302337
    +B1516084536394N00612346EA0230402337
    +B1516104536400N00612357EA0230202335
    +B1516124536406N00612370EA0229802332
    +B1516144536413N00612383EA0229802332
    +B1516164536420N00612395EA0229902332
    +B1516184536425N00612407EA0229702331
    +B1516204536432N00612418EA0229402328
    +B1516224536440N00612428EA0229302328
    +B1516244536446N00612439EA0229302327
    +B1516264536452N00612451EA0229002325
    +B1516284536459N00612465EA0228902323
    +B1516304536464N00612479EA0228802323
    +B1516324536468N00612494EA0228702322
    +B1516344536473N00612507EA0228502320
    +B1516364536478N00612521EA0228302318
    +B1516384536485N00612535EA0228202316
    +B1516404536491N00612547EA0228102316
    +B1516424536498N00612559EA0228102315
    +B1516444536505N00612570EA0227902314
    +B1516464536511N00612582EA0227702312
    +B1516484536518N00612594EA0227502310
    +B1516504536525N00612607EA0227202307
    +B1516524536533N00612621EA0227102306
    +B1516544536540N00612633EA0226902304
    +B1516564536548N00612644EA0226802302
    +B1516584536555N00612657EA0226702301
    +B1517004536562N00612669EA0226602301
    +B1517024536569N00612681EA0226502299
    +B1517044536576N00612695EA0226402298
    +B1517064536583N00612708EA0226402298
    +B1517084536589N00612722EA0226302298
    +B1517104536595N00612735EA0226002296
    +B1517124536602N00612747EA0225502293
    +B1517144536610N00612759EA0225302291
    +B1517164536615N00612774EA0225102288
    +B1517184536621N00612788EA0224802285
    +B1517204536628N00612801EA0224502281
    +B1517224536634N00612815EA0224102278
    +B1517244536640N00612827EA0223702274
    +B1517264536646N00612840EA0223202268
    +B1517284536652N00612854EA0222702264
    +B1517304536658N00612867EA0222302259
    +B1517324536664N00612879EA0221802254
    +B1517344536671N00612892EA0221302249
    +B1517364536677N00612904EA0220902245
    +B1517384536683N00612914EA0220402240
    +B1517404536689N00612926EA0219802234
    +B1517424536695N00612940EA0219402229
    +B1517444536701N00612954EA0219202225
    +B1517464536705N00612968EA0218902222
    +B1517484536710N00612981EA0218602219
    +B1517504536716N00612993EA0218502218
    +B1517524536722N00613005EA0218402216
    +B1517544536727N00613018EA0218302215
    +B1517564536732N00613032EA0218202214
    +B1517584536737N00613045EA0218102214
    +B1518004536742N00613058EA0218102213
    +B1518024536748N00613072EA0218102214
    +B1518044536756N00613084EA0218102214
    +B1518064536763N00613097EA0218302215
    +B1518084536771N00613109EA0218202215
    +B1518104536778N00613121EA0218202215
    +B1518124536785N00613133EA0218202215
    +B1518144536793N00613145EA0218102214
    +B1518164536802N00613157EA0218102215
    +B1518184536811N00613171EA0218102215
    +B1518204536819N00613184EA0218202215
    +B1518224536827N00613196EA0218202215
    +B1518244536835N00613208EA0217902213
    +B1518264536844N00613220EA0217902212
    +B1518284536854N00613230EA0217902213
    +B1518304536863N00613240EA0217902212
    +B1518324536871N00613252EA0217702211
    +B1518344536880N00613263EA0217602210
    +B1518364536888N00613275EA0217502209
    +B1518384536897N00613287EA0217302208
    +B1518404536906N00613300EA0217302207
    +B1518424536913N00613314EA0217302207
    +B1518444536921N00613327EA0217102205
    +B1518464536930N00613341EA0217002204
    +B1518484536938N00613353EA0217102204
    +B1518504536947N00613365EA0217102204
    +B1518524536955N00613376EA0217002203
    +B1518544536964N00613389EA0216902202
    +B1518564536973N00613402EA0216902202
    +B1518584536982N00613416EA0216802201
    +B1519004536990N00613429EA0216802202
    +B1519024536999N00613441EA0216802201
    +B1519044537007N00613455EA0216702200
    +B1519064537016N00613468EA0216602200
    +B1519084537025N00613479EA0216702200
    +B1519104537034N00613492EA0216602199
    +B1519124537043N00613504EA0216402198
    +B1519144537052N00613517EA0216302197
    +B1519164537060N00613530EA0216202196
    +B1519184537069N00613543EA0216102194
    +B1519204537078N00613558EA0215902192
    +B1519224537087N00613572EA0215802191
    +B1519244537095N00613584EA0215602190
    +B1519264537104N00613597EA0215302187
    +B1519284537112N00613611EA0215002184
    +B1519304537120N00613623EA0214802182
    +B1519324537128N00613636EA0214502178
    +B1519344537137N00613649EA0214102175
    +B1519364537145N00613664EA0213902173
    +B1519384537153N00613677EA0213702171
    +B1519404537162N00613690EA0213502167
    +B1519434537174N00613709EA0213202165
    +B1519454537183N00613722EA0212802162
    +B1519474537191N00613736EA0212502159
    +B1519494537200N00613749EA0212402157
    +B1519514537207N00613761EA0212202154
    +B1519534537215N00613772EA0211802151
    +B1519554537224N00613783EA0211302147
    +B1519574537234N00613794EA0211202144
    +B1519594537242N00613804EA0211102142
    +B1520014537249N00613816EA0210502138
    +B1520034537258N00613829EA0210102135
    +B1520054537267N00613840EA0209902133
    +B1520074537276N00613850EA0209602129
    +B1520094537287N00613860EA0209302125
    +B1520114537297N00613870EA0209102123
    +B1520134537306N00613878EA0208902120
    +B1520154537315N00613887EA0208502117
    +B1520174537326N00613898EA0208002113
    +B1520194537337N00613909EA0207802111
    +B1520214537347N00613919EA0207702110
    +B1520234537357N00613927EA0207502108
    +B1520254537366N00613936EA0207302105
    +B1520274537377N00613947EA0207102103
    +B1520294537387N00613956EA0206902101
    +B1520314537397N00613964EA0206602098
    +B1520334537408N00613971EA0206202095
    +B1520354537419N00613979EA0206002093
    +B1520374537431N00613987EA0206002092
    +B1520394537441N00613997EA0205802091
    +B1520414537453N00614005EA0205802090
    +B1520434537464N00614014EA0205702090
    +B1520454537476N00614022EA0205602088
    +B1520474537488N00614030EA0205502088
    +B1520494537501N00614036EA0205402087
    +B1520514537513N00614043EA0205402086
    +B1520534537526N00614050EA0205302086
    +B1520554537538N00614056EA0205402086
    +B1520574537551N00614062EA0205602088
    +B1520594537562N00614068EA0205802090
    +B1521014537573N00614074EA0206102092
    +B1521034537584N00614081EA0206402095
    +B1521054537595N00614088EA0206702098
    +B1521074537605N00614094EA0207002101
    +B1521094537616N00614101EA0207202103
    +B1521114537627N00614108EA0207402105
    +B1521134537637N00614112EA0207702107
    +B1521154537647N00614115EA0207802108
    +B1521174537656N00614116EA0208002109
    +B1521194537667N00614116EA0208302110
    +B1521214537677N00614116EA0208602112
    +B1521234537686N00614114EA0209202117
    +B1521254537692N00614108EA0209802122
    +B1521274537690N00614098EA0210502128
    +B1521294537680N00614102EA0211002134
    +B1521314537671N00614121EA0211402139
    +B1521334537671N00614147EA0211902144
    +B1521354537686N00614162EA0212402150
    +B1521374537700N00614161EA0212902156
    +B1521394537708N00614154EA0213702163
    +B1521414537714N00614147EA0214602172
    +B1521434537719N00614142EA0215102179
    +B1521454537721N00614136EA0215402184
    +B1521474537716N00614129EA0215602188
    +B1521494537705N00614133EA0215702191
    +B1521514537697N00614152EA0215902194
    +B1521534537698N00614175EA0216302196
    +B1521554537708N00614186EA0216602199
    +B1521574537724N00614183EA0217102202
    +B1521594537733N00614176EA0217702207
    +B1522014537739N00614171EA0218002210
    +B1522034537745N00614164EA0218302213
    +B1522054537749N00614154EA0218702216
    +B1522074537748N00614144EA0218902219
    +B1522094537740N00614136EA0218902220
    +B1522114537728N00614140EA0219202223
    +B1522134537722N00614156EA0219402226
    +B1522154537723N00614177EA0219502228
    +B1522174537731N00614191EA0219602229
    +B1522194537743N00614198EA0219702229
    +B1522214537755N00614194EA0220102231
    +B1522234537761N00614185EA0220402234
    +B1522254537762N00614176EA0220602236
    +B1522274537763N00614166EA0220902238
    +B1522294537765N00614155EA0221102240
    +B1522314537766N00614145EA0221502243
    +B1522334537766N00614136EA0222202248
    +B1522354537766N00614127EA0222702254
    +B1522374537767N00614118EA0223302260
    +B1522394537764N00614111EA0223902266
    +B1522414537753N00614113EA0224202272
    +B1522434537742N00614132EA0224702278
    +B1522454537739N00614155EA0225302283
    +B1522474537748N00614166EA0225702288
    +B1522494537763N00614162EA0226202293
    +B1522514537763N00614148EA0226602297
    +B1522534537756N00614142EA0227202303
    +B1522554537747N00614142EA0227802310
    +B1522574537738N00614154EA0228402316
    +B1522594537730N00614173EA0228902323
    +B1523014537730N00614193EA0229402328
    +B1523034537739N00614208EA0230002333
    +B1523054537751N00614212EA0230502339
    +B1523074537758N00614205EA0231002343
    +B1523094537758N00614194EA0231402348
    +B1523114537752N00614186EA0232102354
    +B1523134537743N00614186EA0232902361
    +B1523154537733N00614197EA0233602368
    +B1523174537726N00614217EA0234002373
    +B1523194537727N00614239EA0234602379
    +B1523214537738N00614248EA0235202385
    +B1523234537749N00614243EA0235602391
    +B1523254537754N00614232EA0236002394
    +B1523274537756N00614223EA0236602400
    +B1523294537755N00614215EA0237402408
    +B1523314537749N00614207EA0238202416
    +B1523334537740N00614201EA0238802422
    +B1523354537724N00614206EA0238902427
    +B1523374537709N00614227EA0239602433
    +B1523394537704N00614251EA0240502439
    +B1523414537711N00614263EA0240902444
    +B1523434537724N00614269EA0241202449
    +B1523454537736N00614269EA0241602453
    +B1523474537745N00614258EA0242202458
    +B1523494537750N00614247EA0243402464
    +B1523514537747N00614239EA0243902471
    +B1523534537744N00614234EA0243702474
    +B1523554537740N00614221EA0243102475
    +B1523574537728N00614212EA0243002473
    +B1523594537717N00614220EA0242802472
    +B1524014537708N00614235EA0242402469
    +B1524034537702N00614253EA0242202465
    +B1524054537698N00614273EA0242702465
    +B1524074537696N00614293EA0243502466
    +B1524094537693N00614308EA0244302471
    +B1524114537691N00614324EA0244702476
    +B1524134537694N00614342EA0245002479
    +B1524154537703N00614358EA0245402484
    +B1524174537714N00614360EA0245802488
    +B1524194537720N00614350EA0246202493
    +B1524214537718N00614339EA0246902498
    +B1524234537708N00614337EA0247402504
    +B1524254537696N00614347EA0247802510
    +B1524274537689N00614365EA0248602517
    +B1524294537690N00614384EA0248902523
    +B1524314537695N00614403EA0249102527
    +B1524334537706N00614416EA0249502531
    +B1524354537719N00614419EA0249902535
    +B1524374537723N00614416EA0250202536
    +B1524394537731N00614402EA0250902543
    +B1524414537729N00614392EA0251602550
    +B1524434537723N00614388EA0252002555
    +B1524454537710N00614396EA0252402559
    +B1524474537700N00614413EA0253202567
    +B1524494537700N00614433EA0253702573
    +B1524514537704N00614442EA0253902575
    +B1524534537721N00614455EA0254202581
    +B1524554537730N00614447EA0254502584
    +B1524574537734N00614437EA0255302591
    +B1524594537734N00614428EA0255602595
    +B1525014537728N00614421EA0255802598
    +B1525034537716N00614419EA0256002600
    +B1525054537710N00614423EA0256402601
    +B1525074537699N00614448EA0257002608
    +B1525094537695N00614464EA0257802615
    +B1525114537694N00614481EA0258202619
    +B1525134537696N00614501EA0258202623
    +B1525154537707N00614516EA0258202624
    +B1525174537719N00614514EA0258002624
    +B1525194537724N00614501EA0258002623
    +B1525214537720N00614490EA0258602626
    +B1525234537714N00614482EA0259502631
    +B1525254537708N00614477EA0260202637
    +B1525274537704N00614470EA0260502640
    +B1525294537697N00614462EA0260702643
    +B1525314537686N00614461EA0261302647
    +B1525334537678N00614473EA0261902651
    +B1525354537677N00614489EA0262302656
    +B1525374537684N00614508EA0263002663
    +B1525394537688N00614528EA0263502669
    +B1525414537689N00614547EA0263702673
    +B1525434537692N00614565EA0263602675
    +B1525454537695N00614585EA0263202676
    +B1525474537699N00614606EA0262802674
    +B1525494537705N00614627EA0262602671
    +B1525514537711N00614643EA0262502669
    +B1525534537717N00614658EA0262202666
    +B1525554537722N00614674EA0261702662
    +B1525574537730N00614686EA0261202656
    +B1525594537740N00614698EA0260702651
    +B1526014537750N00614709EA0260402648
    +B1526034537760N00614717EA0260102644
    +B1526054537770N00614727EA0259702639
    +B1526074537781N00614737EA0259302635
    +B1526094537792N00614745EA0259102632
    +B1526114537804N00614752EA0258902631
    +B1526134537815N00614758EA0258702627
    +B1526154537826N00614760EA0258402625
    +B1526174537837N00614762EA0258202623
    +B1526194537847N00614764EA0257902620
    +B1526214537859N00614766EA0257502617
    +B1526234537871N00614767EA0257502615
    +B1526254537883N00614769EA0257502615
    +B1526274537895N00614771EA0257302614
    +B1526294537906N00614775EA0257302614
    +B1526314537915N00614779EA0257202613
    +B1526334537925N00614784EA0256802610
    +B1526354537935N00614788EA0256602609
    +B1526374537945N00614791EA0256402606
    +B1526394537954N00614795EA0256002603
    +B1526414537965N00614799EA0255602599
    +B1526434537975N00614801EA0255402597
    +B1526454537985N00614802EA0255102594
    +B1526474537995N00614804EA0254702590
    +B1526494538006N00614805EA0254402587
    +B1526514538017N00614806EA0254202584
    +B1526534538027N00614807EA0253902580
    +B1526554538038N00614810EA0253502577
    +B1526574538050N00614813EA0253302574
    +B1526594538060N00614817EA0253202572
    +B1527014538070N00614821EA0252802569
    +B1527034538081N00614824EA0252402565
    +B1527054538093N00614826EA0252102561
    +B1527074538105N00614828EA0251902559
    +B1527094538116N00614830EA0251702557
    +B1527114538128N00614831EA0251402554
    +B1527134538139N00614830EA0251002550
    +B1527154538150N00614829EA0250702548
    +B1527174538160N00614829EA0250402544
    +B1527194538170N00614829EA0250002540
    +B1527214538180N00614830EA0249602536
    +B1527234538190N00614831EA0249302533
    +B1527254538200N00614833EA0248902529
    +B1527274538212N00614833EA0248502525
    +B1527294538224N00614835EA0248202521
    +B1527314538237N00614837EA0248102519
    +B1527334538248N00614839EA0248002518
    +B1527354538260N00614841EA0247902516
    +B1527374538271N00614845EA0247702515
    +B1527394538281N00614847EA0247402512
    +B1527414538291N00614849EA0247002509
    +B1527434538303N00614853EA0246802507
    +B1527454538313N00614858EA0246502504
    +B1527474538323N00614863EA0246102500
    +B1527494538334N00614868EA0245902498
    +B1527514538345N00614872EA0245702495
    +B1527534538356N00614874EA0245302491
    +B1527554538368N00614876EA0244902488
    +B1527574538380N00614880EA0244802486
    +B1527594538391N00614883EA0244602483
    +B1528014538403N00614886EA0244302480
    +B1528034538414N00614889EA0244002477
    +B1528054538424N00614890EA0243702474
    +B1528074538436N00614892EA0243202470
    +B1528094538448N00614892EA0242902467
    +B1528114538460N00614893EA0242602464
    +B1528134538472N00614896EA0242202460
    +B1528154538484N00614899EA0241802456
    +B1528174538495N00614906EA0241502453
    +B1528194538506N00614913EA0241202450
    +B1528214538519N00614921EA0241002447
    +B1528234538530N00614926EA0240802446
    +B1528254538541N00614929EA0240602444
    +B1528274538551N00614932EA0240202440
    +B1528294538563N00614935EA0239802436
    +B1528314538574N00614936EA0239502433
    +B1528334538584N00614937EA0239002429
    +B1528354538593N00614934EA0238402424
    +B1528374538604N00614933EA0237902418
    +B1528394538616N00614935EA0237702415
    +B1528414538627N00614937EA0237402412
    +B1528434538637N00614938EA0236802407
    +B1528454538647N00614937EA0236202401
    +B1528474538658N00614939EA0235702395
    +B1528494538670N00614942EA0235302390
    +B1528514538682N00614946EA0235002386
    +B1528534538694N00614949EA0234902384
    +B1528554538704N00614951EA0234702381
    +B1528574538716N00614951EA0234302377
    +B1528594538728N00614949EA0234002375
    +B1529014538739N00614945EA0233902373
    +B1529034538749N00614942EA0233802372
    +B1529054538759N00614939EA0233602370
    +B1529074538769N00614940EA0233202366
    +B1529094538780N00614940EA0232902363
    +B1529114538791N00614936EA0232602361
    +B1529134538803N00614936EA0232502360
    +B1529154538814N00614937EA0232502360
    +B1529174538825N00614938EA0232602360
    +B1529194538834N00614938EA0232602359
    +B1529214538844N00614936EA0232402358
    +B1529234538854N00614932EA0232502358
    +B1529254538865N00614930EA0232702359
    +B1529274538873N00614931EA0232902361
    +B1529294538881N00614931EA0233202364
    +B1529314538887N00614930EA0233302366
    +B1529334538893N00614928EA0233302367
    +B1529354538900N00614926EA0233302367
    +B1529374538909N00614926EA0233202367
    +B1529394538919N00614930EA0233202366
    +B1529414538930N00614937EA0233102366
    +B1529434538941N00614944EA0233002365
    +B1529454538954N00614950EA0232902365
    +B1529474538966N00614958EA0233002365
    +B1529494538977N00614964EA0233002365
    +B1529514538988N00614971EA0232702364
    +B1529534539001N00614977EA0232702363
    +B1529554539014N00614981EA0232802364
    +B1529574539025N00614985EA0232802364
    +B1529594539037N00614990EA0232802364
    +B1530014539049N00614995EA0232902365
    +B1530034539059N00615002EA0233002366
    +B1530054539068N00615007EA0232702365
    +B1530074539079N00615012EA0232302363
    +B1530094539090N00615019EA0232002360
    +B1530114539102N00615027EA0231902359
    +B1530134539113N00615035EA0231802358
    +B1530154539124N00615041EA0231802357
    +B1530174539134N00615048EA0231702356
    +B1530194539145N00615055EA0231402353
    +B1530214539156N00615062EA0231102350
    +B1530234539167N00615070EA0230902348
    +B1530254539177N00615077EA0230602345
    +B1530274539188N00615086EA0230202341
    +B1530294539198N00615098EA0229802336
    +B1530314539208N00615107EA0229302332
    +B1530334539219N00615116EA0228902327
    +B1530354539230N00615125EA0228502323
    +B1530374539241N00615134EA0228102318
    +B1530394539252N00615143EA0227702315
    +B1530414539262N00615152EA0227402311
    +B1530434539273N00615160EA0227002307
    +B1530454539284N00615168EA0226602303
    +B1530474539296N00615178EA0226402300
    +B1530494539306N00615187EA0226202297
    +B1530514539316N00615197EA0225802293
    +B1530534539328N00615207EA0225402289
    +B1530554539341N00615215EA0225302287
    +B1530574539352N00615220EA0225102284
    +B1530594539363N00615227EA0224702281
    +B1531014539376N00615235EA0224602279
    +B1531034539387N00615247EA0224502278
    +B1531054539398N00615257EA0224402276
    +B1531074539409N00615268EA0224002273
    +B1531094539420N00615279EA0223602269
    +B1531114539432N00615288EA0223302266
    +B1531134539443N00615298EA0222902262
    +B1531154539454N00615309EA0222502258
    +B1531174539466N00615319EA0222202255
    +B1531194539477N00615330EA0221902251
    +B1531214539488N00615341EA0221502248
    +B1531234539500N00615352EA0221202244
    +B1531254539512N00615364EA0220802240
    +B1531274539524N00615375EA0220602238
    +B1531294539535N00615387EA0220602237
    +B1531314539547N00615399EA0220402236
    +B1531334539558N00615410EA0220302234
    +B1531354539571N00615421EA0220102233
    +B1531374539583N00615433EA0220102233
    +B1531394539594N00615446EA0220202233
    +B1531414539605N00615459EA0220102233
    +B1531434539616N00615472EA0220002231
    +B1531454539628N00615486EA0219902231
    +B1531474539638N00615499EA0220002231
    +B1531494539648N00615511EA0219902230
    +B1531514539660N00615524EA0219802229
    +B1531534539671N00615536EA0219702229
    +B1531554539681N00615546EA0219402227
    +B1531574539693N00615557EA0219202225
    +B1531594539706N00615569EA0219302225
    +B1532014539718N00615580EA0219402225
    +B1532034539728N00615590EA0219202223
    +B1532054539740N00615603EA0218802220
    +B1532074539752N00615616EA0218802220
    +B1532094539763N00615626EA0218702219
    +B1532114539774N00615636EA0218402217
    +B1532134539785N00615646EA0218202215
    +B1532154539797N00615657EA0218102214
    +B1532174539808N00615667EA0218002213
    +B1532194539819N00615677EA0217702210
    +B1532214539831N00615689EA0217402207
    +B1532234539842N00615701EA0217202205
    +B1532254539853N00615712EA0217002203
    +B1532274539864N00615723EA0216602200
    +B1532294539875N00615733EA0216102195
    +B1532314539888N00615743EA0215702191
    +B1532334539901N00615753EA0215302187
    +B1532354539914N00615765EA0214902183
    +B1532374539927N00615779EA0214702180
    +B1532394539940N00615791EA0214602178
    +B1532414539951N00615803EA0214202175
    +B1532434539963N00615815EA0213902171
    +B1532454539975N00615825EA0213602169
    +B1532474539985N00615835EA0213302166
    +B1532494539993N00615846EA0212702161
    +B1532514540003N00615856EA0212002155
    +B1532534540013N00615868EA0211602151
    +B1532554540022N00615878EA0211202146
    +B1532574540030N00615888EA0210502140
    +B1532594540040N00615899EA0210002134
    +B1533014540051N00615911EA0209502129
    +B1533034540060N00615924EA0209302125
    +B1533054540067N00615934EA0208802121
    +B1533074540076N00615944EA0208202115
    +B1533094540086N00615953EA0207902111
    +B1533114540096N00615961EA0207502107
    +B1533134540107N00615969EA0207102103
    +B1533154540118N00615977EA0206702099
    +B1533174540128N00615984EA0206502097
    +B1533194540139N00615992EA0206302094
    +B1533214540149N00616002EA0206002091
    +B1533234540159N00616013EA0205802088
    +B1533254540170N00616024EA0205602086
    +B1533274540180N00616033EA0205302083
    +B1533294540190N00616043EA0205002080
    +B1533314540201N00616053EA0204702077
    +B1533334540211N00616062EA0204502075
    +B1533354540222N00616071EA0204302072
    +B1533374540233N00616080EA0204002070
    +B1533394540243N00616090EA0203802068
    +B1533414540253N00616099EA0203502065
    +B1533434540263N00616109EA0203102062
    +B1533454540274N00616117EA0203102060
    +B1533474540285N00616126EA0202902058
    +B1533494540296N00616137EA0202702056
    +B1533514540307N00616148EA0202702056
    +B1533534540319N00616159EA0203002056
    +B1533554540330N00616169EA0203202058
    +B1533574540340N00616176EA0203202059
    +B1533594540350N00616183EA0203102058
    +B1534014540360N00616194EA0203002058
    +B1534034540370N00616204EA0203002058
    +B1534054540380N00616213EA0202902057
    +B1534074540390N00616222EA0202802056
    +B1534094540400N00616232EA0202802056
    +B1534114540409N00616242EA0202702056
    +B1534134540418N00616251EA0202502054
    +B1534154540428N00616261EA0202502055
    +B1534174540438N00616270EA0202502055
    +B1534194540447N00616279EA0202402054
    +B1534214540457N00616289EA0202302053
    +B1534234540464N00616304EA0202202051
    +B1534254540470N00616321EA0202202051
    +B1534274540475N00616338EA0202302051
    +B1534294540479N00616355EA0202202051
    +B1534314540482N00616373EA0202202050
    +B1534334540485N00616392EA0202202051
    +B1534354540490N00616409EA0202502052
    +B1534374540494N00616423EA0202902055
    +B1534394540497N00616436EA0203402059
    +B1534414540503N00616446EA0203602062
    +B1534434540513N00616442EA0204002066
    +B1534454540517N00616436EA0204402068
    +B1534474540512N00616411EA0204802074
    +B1534494540499N00616409EA0205202079
    +B1534514540489N00616419EA0205502084
    +B1534534540486N00616433EA0205702087
    +B1534554540493N00616447EA0205902090
    +B1534574540506N00616450EA0206402094
    +B1534594540517N00616449EA0207302101
    +B1535014540525N00616447EA0207802107
    +B1535034540532N00616438EA0208002111
    +B1535054540531N00616419EA0208102113
    +B1535074540521N00616405EA0208402116
    +B1535094540509N00616404EA0208702119
    +B1535114540500N00616415EA0209002122
    +B1535134540498N00616429EA0209302126
    +B1535154540502N00616442EA0209702129
    +B1535174540512N00616449EA0210202135
    +B1535194540522N00616450EA0210902141
    +B1535214540532N00616447EA0211402147
    +B1535234540541N00616445EA0211802151
    +B1535254540548N00616442EA0211902154
    +B1535274540554N00616427EA0211902156
    +B1535294540552N00616408EA0212302160
    +B1535314540541N00616400EA0212502162
    +B1535334540528N00616405EA0212702164
    +B1535354540521N00616416EA0213202167
    +B1535374540521N00616430EA0213702172
    +B1535394540527N00616440EA0214202177
    +B1535414540537N00616437EA0214202179
    +B1535434540544N00616422EA0214402181
    +B1535454540542N00616406EA0214802185
    +B1535474540534N00616395EA0215102187
    +B1535494540524N00616388EA0215302190
    +B1535514540512N00616386EA0215502191
    +B1535534540501N00616392EA0215802193
    +B1535554540493N00616403EA0216102196
    +B1535574540491N00616419EA0216602200
    +B1535594540493N00616435EA0217202205
    +B1536014540495N00616447EA0217902212
    +B1536034540498N00616458EA0218402218
    +B1536054540503N00616464EA0218502222
    +B1536074540516N00616459EA0218802225
    +B1536094540522N00616443EA0219202229
    +B1536114540518N00616428EA0219602232
    +B1536134540507N00616421EA0220102237
    +B1536154540497N00616427EA0220702241
    +B1536174540494N00616434EA0221202244
    +B1536194540493N00616449EA0221802250
    +B1536214540498N00616461EA0222102255
    +B1536234540508N00616463EA0222202258
    +B1536254540519N00616456EA0222502261
    +B1536274540526N00616444EA0222802264
    +B1536294540530N00616427EA0223202265
    +B1536314540530N00616408EA0223602269
    +B1536334540521N00616395EA0224102274
    +B1536354540511N00616386EA0224502279
    +B1536374540499N00616389EA0224702282
    +B1536394540492N00616404EA0225002284
    +B1536414540492N00616421EA0225602287
    +B1536434540500N00616433EA0226102292
    +B1536454540507N00616438EA0226502296
    +B1536474540515N00616437EA0226702299
    +B1536494540525N00616425EA0226902302
    +B1536514540527N00616407EA0227302306
    +B1536534540522N00616391EA0227602310
    +B1536554540512N00616379EA0228002314
    +B1536574540500N00616378EA0228302317
    +B1536594540490N00616388EA0228602320
    +B1537014540487N00616404EA0229002324
    +B1537034540492N00616416EA0229102327
    +B1537054540500N00616423EA0229302329
    +B1537074540508N00616429EA0229802332
    +B1537094540523N00616427EA0230502338
    +B1537114540528N00616424EA0231302341
    +B1537134540537N00616420EA0231902348
    +B1537154540552N00616411EA0232502357
    +B1537174540555N00616401EA0232902360
    +B1537194540553N00616383EA0233202367
    +B1537214540541N00616377EA0233602371
    +B1537234540530N00616390EA0234002375
    +B1537254540531N00616408EA0234702381
    +B1537274540541N00616413EA0235402386
    +B1537294540553N00616401EA0235802392
    +B1537314540554N00616382EA0236202396
    +B1537334540542N00616373EA0236502401
    +B1537354540529N00616380EA0237102408
    +B1537374540522N00616393EA0237502413
    +B1537394540523N00616409EA0238102417
    +B1537414540533N00616420EA0238902424
    +B1537434540544N00616420EA0239702431
    +B1537454540554N00616414EA0240402438
    +B1537474540559N00616405EA0240902445
    +B1537494540557N00616390EA0241302451
    +B1537514540549N00616377EA0241902457
    +B1537534540535N00616377EA0242202462
    +B1537554540525N00616389EA0242702467
    +B1537574540523N00616406EA0243402472
    +B1537594540530N00616416EA0244002478
    +B1538014540541N00616415EA0244702484
    +B1538034540549N00616408EA0245302490
    +B1538054540551N00616395EA0245902496
    +B1538074540546N00616380EA0246502502
    +B1538094540536N00616369EA0247102508
    +B1538114540523N00616371EA0247402513
    +B1538134540514N00616385EA0248002519
    +B1538154540513N00616400EA0248602525
    +B1538174540520N00616411EA0249202530
    +B1538194540530N00616413EA0249802535
    +B1538214540539N00616409EA0250302541
    +B1538234540545N00616401EA0251002547
    +B1538254540548N00616390EA0251602554
    +B1538274540547N00616376EA0252002559
    +B1538294540537N00616362EA0252602564
    +B1538314540522N00616359EA0253102570
    +B1538334540513N00616371EA0253602574
    +B1538354540513N00616387EA0254102579
    +B1538374540521N00616397EA0254602583
    +B1538394540530N00616399EA0255102587
    +B1538414540538N00616392EA0255602592
    +B1538434540538N00616377EA0256102595
    +B1538454540525N00616366EA0256702600
    +B1538474540514N00616368EA0257102605
    +B1538494540507N00616381EA0257602610
    +B1538514540505N00616396EA0258002615
    +B1538534540509N00616409EA0258402619
    +B1538554540517N00616417EA0258902624
    +B1538574540528N00616417EA0259202627
    +B1538594540538N00616406EA0259702631
    +B1539014540541N00616392EA0260302637
    +B1539034540536N00616377EA0260802641
    +B1539054540524N00616371EA0261402647
    +B1539074540512N00616375EA0261902652
    +B1539094540505N00616388EA0262402657
    +B1539114540505N00616404EA0262802662
    +B1539134540513N00616416EA0263402668
    +B1539154540525N00616419EA0263902673
    +B1539174540536N00616416EA0264402678
    +B1539194540544N00616406EA0264902683
    +B1539214540544N00616389EA0265202688
    +B1539234540535N00616376EA0265802692
    +B1539254540522N00616377EA0266202697
    +B1539274540514N00616393EA0266502700
    +B1539294540517N00616414EA0267102706
    +B1539314540522N00616430EA0267802714
    +B1539334540526N00616444EA0268102719
    +B1539354540528N00616457EA0268502723
    +B1539374540533N00616469EA0268802726
    +B1539394540539N00616480EA0269302730
    +B1539414540545N00616492EA0269802735
    +B1539434540552N00616502EA0270402740
    +B1539454540557N00616514EA0270802746
    +B1539474540561N00616528EA0271202750
    +B1539494540565N00616543EA0271702755
    +B1539514540569N00616556EA0271902759
    +B1539534540573N00616570EA0271802762
    +B1539554540576N00616587EA0271702763
    +B1539574540581N00616603EA0271502762
    +B1539594540587N00616619EA0271502760
    +B1540014540594N00616635EA0271602761
    +B1540034540598N00616651EA0271702762
    +B1540054540602N00616666EA0271602762
    +B1540074540607N00616680EA0271202761
    +B1540094540612N00616695EA0270602759
    +B1540114540620N00616710EA0270002755
    +B1540134540629N00616725EA0269602749
    +B1540154540638N00616742EA0269302745
    +B1540174540648N00616756EA0269002741
    +B1540194540660N00616768EA0268802737
    +B1540214540672N00616780EA0268602734
    +B1540234540684N00616790EA0268102731
    +B1540254540697N00616801EA0267602726
    +B1540274540710N00616814EA0267402722
    +B1540294540724N00616827EA0267402720
    +B1540314540736N00616838EA0267202718
    +B1540334540747N00616848EA0267002716
    +B1540354540759N00616858EA0266702713
    +B1540374540770N00616868EA0266502710
    +B1540394540782N00616878EA0266302707
    +B1540414540793N00616888EA0266102705
    +B1540434540804N00616898EA0265802702
    +B1540454540815N00616909EA0265602699
    +B1540474540826N00616919EA0265302697
    +B1540494540837N00616929EA0265102694
    +B1540514540848N00616939EA0264802691
    +B1540534540859N00616950EA0264502688
    +B1540554540871N00616961EA0264302686
    +B1540574540882N00616973EA0264102683
    +B1540594540893N00616985EA0264002682
    +B1541014540904N00616997EA0263902681
    +B1541034540914N00617007EA0263802680
    +B1541054540925N00617018EA0263502678
    +B1541074540936N00617030EA0263302675
    +B1541094540947N00617042EA0263102673
    +B1541114540958N00617053EA0262802671
    +B1541134540968N00617064EA0262502668
    +B1541154540977N00617077EA0262202665
    +B1541174540987N00617089EA0261902663
    +B1541194540996N00617100EA0261502659
    +B1541214541006N00617113EA0261102655
    +B1541234541017N00617126EA0260902651
    +B1541254541027N00617140EA0260702649
    +B1541274541035N00617155EA0260402646
    +B1541294541044N00617169EA0260202642
    +B1541314541055N00617183EA0260002641
    +B1541334541063N00617199EA0259902640
    +B1541354541072N00617214EA0259802639
    +B1541374541085N00617236EA0259602635
    +B1541394541089N00617244EA0259502635
    +B1541414541096N00617259EA0259502634
    +B1541434541102N00617272EA0259102632
    +B1541454541109N00617287EA0258602629
    +B1541474541116N00617302EA0258302626
    +B1541494541126N00617325EA0257902620
    +B1541514541132N00617341EA0257502616
    +B1541534541139N00617359EA0257202612
    +B1541554541141N00617367EA0257002611
    +B1541574541146N00617382EA0256602608
    +B1541594541154N00617408EA0256202602
    +B1542014541157N00617418EA0256002600
    +B1542034541162N00617435EA0255902599
    +B1542054541170N00617461EA0255702595
    +B1542074541173N00617470EA0255602594
    +B1542094541179N00617487EA0255602594
    +B1542114541188N00617512EA0255402591
    +B1542134541194N00617530EA0255202589
    +B1542154541197N00617538EA0255002588
    +B1542174541206N00617564EA0254802585
    +B1542194541212N00617580EA0254502582
    +B1542214541219N00617597EA0254302579
    +B1542234541226N00617613EA0254302578
    +B1542254541233N00617627EA0254202577
    +B1542274541237N00617635EA0253902576
    +B1542294541248N00617659EA0253702573
    +B1542314541252N00617668EA0253702572
    +B1542334541264N00617690EA0253602571
    +B1542354541273N00617705EA0253402569
    +B1542374541282N00617719EA0253302568
    +B1542394541286N00617726EA0253102568
    +B1542414541300N00617748EA0252802565
    +B1542434541309N00617763EA0252602563
    +B1542454541320N00617777EA0252602562
    +B1542474541329N00617790EA0252402560
    +B1542494541339N00617804EA0252202558
    +B1542514541345N00617812EA0252202557
    +B1542534541359N00617833EA0252202557
    +B1542554541367N00617847EA0252002556
    +B1542574541372N00617861EA0251802554
    +B1542594541386N00617877EA0251702553
    +B1543014541394N00617891EA0251502552
    +B1543034541398N00617899EA0251202550
    +B1543054541410N00617923EA0251002547
    +B1543074541419N00617938EA0250802545
    +B1543094541426N00617953EA0250502542
    +B1543114541435N00617968EA0250202539
    +B1543134541443N00617984EA0250002537
    +B1543154541450N00617999EA0249702535
    +B1543174541457N00618015EA0249402531
    +B1543194541465N00618031EA0249102528
    +B1543214541472N00618047EA0248802525
    +B1543234541478N00618063EA0248402521
    +B1543254541485N00618080EA0248102518
    +B1543274541492N00618099EA0247902515
    +B1543294541499N00618115EA0247602513
    +B1543314541506N00618131EA0247302510
    +B1543334541512N00618149EA0247002507
    +B1543354541520N00618166EA0246802505
    +B1543374541526N00618183EA0246602503
    +B1543394541533N00618199EA0246402500
    +B1543414541540N00618216EA0246202498
    +B1543434541547N00618233EA0246002496
    +B1543454541554N00618249EA0245802494
    +B1543474541562N00618265EA0245602493
    +B1543494541569N00618281EA0245502491
    +B1543514541577N00618297EA0245202488
    +B1543534541585N00618313EA0244902485
    +B1543554541593N00618328EA0244702483
    +B1543574541601N00618343EA0244402481
    +B1543594541609N00618358EA0244202478
    +B1544014541617N00618373EA0244102477
    +B1544034541626N00618387EA0244002476
    +B1544054541634N00618399EA0243902475
    +B1544074541643N00618413EA0243802473
    +B1544094541651N00618428EA0243702473
    +B1544114541658N00618442EA0243602472
    +B1544134541665N00618457EA0243402470
    +B1544154541673N00618473EA0243302469
    +B1544174541680N00618490EA0243302469
    +B1544194541686N00618505EA0243302468
    +B1544214541692N00618522EA0243102467
    +B1544234541699N00618539EA0243002465
    +B1544254541706N00618556EA0242902465
    +B1544274541712N00618572EA0242802464
    +B1544294541719N00618588EA0242602462
    +B1544314541726N00618605EA0242502461
    +B1544334541733N00618621EA0242302460
    +B1544354541740N00618637EA0242202458
    +B1544374541746N00618654EA0242002456
    +B1544394541753N00618670EA0241802454
    +B1544414541760N00618686EA0241602452
    +B1544434541767N00618702EA0241302450
    +B1544454541774N00618717EA0241102448
    +B1544474541780N00618733EA0240802445
    +B1544494541787N00618749EA0240602442
    +B1544514541794N00618765EA0240302439
    +B1544534541801N00618781EA0240002437
    +B1544554541808N00618798EA0239802434
    +B1544574541815N00618813EA0239702432
    +B1544594541822N00618828EA0239402430
    +B1545014541829N00618844EA0239102428
    +B1545034541835N00618861EA0238902425
    +B1545054541842N00618878EA0238702423
    +B1545074541848N00618895EA0238402420
    +B1545094541853N00618912EA0238202418
    +B1545114541860N00618928EA0238002416
    +B1545134541866N00618944EA0237702414
    +B1545154541871N00618959EA0237402411
    +B1545174541878N00618975EA0237002407
    +B1545194541884N00618991EA0236702404
    +B1545214541889N00619007EA0236502401
    +B1545234541896N00619021EA0236202398
    +B1545254541902N00619036EA0236002395
    +B1545274541909N00619051EA0235802394
    +B1545294541914N00619064EA0235502391
    +B1545314541920N00619078EA0235102387
    +B1545334541926N00619093EA0234702384
    +B1545354541931N00619109EA0234502382
    +B1545374541934N00619124EA0234302379
    +B1545394541939N00619139EA0233902375
    +B1545414541944N00619154EA0233602373
    +B1545434541949N00619168EA0233402369
    +B1545454541954N00619182EA0233002365
    +B1545474541960N00619197EA0232602362
    +B1545494541966N00619212EA0232402359
    +B1545514541972N00619224EA0232102356
    +B1545534541978N00619236EA0231702353
    +B1545554541986N00619248EA0231502351
    +B1545574541993N00619258EA0231302349
    +B1545594542001N00619268EA0231102347
    +B1546014542009N00619278EA0230902345
    +B1546034542017N00619289EA0230802344
    +B1546054542024N00619300EA0230702342
    +B1546074542031N00619313EA0230502341
    +B1546094542038N00619325EA0230402339
    +B1546114542045N00619336EA0230302338
    +B1546134542051N00619349EA0230202337
    +B1546154542057N00619363EA0230202336
    +B1546174542064N00619376EA0230002335
    +B1546194542070N00619389EA0229802333
    +B1546214542076N00619401EA0229502331
    +B1546234542083N00619413EA0229302329
    +B1546254542090N00619425EA0229102327
    +B1546274542096N00619438EA0229002325
    +B1546294542103N00619449EA0228802324
    +B1546314542110N00619460EA0228702322
    +B1546334542118N00619471EA0228602321
    +B1546354542125N00619479EA0228502321
    +B1546374542133N00619487EA0228402319
    +B1546394542141N00619496EA0228202317
    +B1546414542149N00619504EA0228102315
    +B1546434542156N00619511EA0227802314
    +B1546454542164N00619518EA0227502311
    +B1546474542171N00619527EA0227202308
    +B1546494542179N00619534EA0227002305
    +B1546514542186N00619544EA0226702302
    +B1546534542193N00619554EA0226402299
    +B1546554542200N00619564EA0226102296
    +B1546574542207N00619575EA0225802293
    +B1546594542215N00619585EA0225602291
    +B1547014542222N00619595EA0225302288
    +B1547034542230N00619605EA0225102286
    +B1547054542237N00619615EA0224902284
    +B1547074542244N00619625EA0224702281
    +B1547094542251N00619635EA0224502279
    +B1547114542260N00619644EA0224302278
    +B1547134542268N00619654EA0224302277
    +B1547154542275N00619663EA0224102276
    +B1547174542284N00619672EA0223902274
    +B1547194542293N00619681EA0223702272
    +B1547214542301N00619689EA0223502270
    +B1547234542309N00619698EA0223302268
    +B1547254542317N00619710EA0223202266
    +B1547274542325N00619720EA0223102265
    +B1547294542332N00619729EA0223002264
    +B1547314542340N00619739EA0222702261
    +B1547334542349N00619749EA0222602260
    +B1547354542357N00619759EA0222402259
    +B1547374542365N00619768EA0222302257
    +B1547394542374N00619777EA0222002254
    +B1547414542382N00619785EA0221802252
    +B1547434542391N00619794EA0221602250
    +B1547454542400N00619802EA0221402248
    +B1547474542409N00619809EA0221302246
    +B1547494542414N00619814EA0221102245
    +B1547514542426N00619827EA0220902243
    +B1547534542434N00619836EA0220702241
    +B1547554542442N00619845EA0220502238
    +B1547574542450N00619855EA0220202236
    +B1547594542457N00619865EA0220102234
    +B1548014542465N00619875EA0219702232
    +B1548034542473N00619885EA0219602230
    +B1548054542481N00619895EA0219402228
    +B1548074542489N00619905EA0219102225
    +B1548094542497N00619915EA0218802222
    +B1548114542505N00619926EA0218502219
    +B1548134542513N00619936EA0218502218
    +B1548154542520N00619945EA0218402217
    +B1548174542527N00619955EA0218202215
    +B1548194542534N00619966EA0217902212
    +B1548214542541N00619977EA0217702211
    +B1548234542548N00619989EA0217402208
    +B1548254542555N00620001EA0217202206
    +B1548274542562N00620014EA0217002204
    +B1548294542569N00620025EA0216802202
    +B1548314542577N00620035EA0216502199
    +B1548334542585N00620044EA0216302197
    +B1548354542593N00620052EA0216202195
    +B1548374542601N00620059EA0216102194
    +B1548394542610N00620063EA0215902192
    +B1548414542618N00620069EA0215602190
    +B1548434542626N00620077EA0215502189
    +B1548454542634N00620085EA0215402187
    +B1548474542642N00620093EA0215302185
    +B1548494542651N00620100EA0215102183
    +B1548514542660N00620106EA0214902182
    +B1548534542668N00620112EA0214602180
    +B1548554542676N00620119EA0214502178
    +B1548574542684N00620128EA0214202176
    +B1548594542692N00620137EA0214002174
    +B1549014542700N00620145EA0213802172
    +B1549034542708N00620154EA0213602170
    +B1549054542716N00620163EA0213302167
    +B1549074542724N00620172EA0213002164
    +B1549094542732N00620181EA0212802162
    +B1549114542740N00620189EA0212502159
    +B1549134542748N00620197EA0212302157
    +B1549154542756N00620205EA0212002154
    +B1549174542764N00620213EA0211802152
    +B1549194542773N00620221EA0211602149
    +B1549214542781N00620230EA0211302147
    +B1549234542789N00620238EA0211102144
    +B1549254542798N00620246EA0210902143
    +B1549274542806N00620255EA0210802141
    +B1549294542814N00620262EA0210602139
    +B1549314542822N00620271EA0210402137
    +B1549334542831N00620279EA0210202136
    +B1549354542839N00620288EA0210002133
    +B1549374542847N00620297EA0209702130
    +B1549394542856N00620307EA0209502128
    +B1549414542865N00620318EA0209502127
    +B1549434542874N00620327EA0209402126
    +B1549454542882N00620336EA0209102123
    +B1549474542891N00620345EA0209002122
    +B1549494542899N00620354EA0209302123
    +B1549514542906N00620361EA0209202123
    +B1549534542913N00620368EA0208902121
    +B1549554542922N00620375EA0208702120
    +B1549574542929N00620382EA0208902121
    +B1549594542935N00620390EA0209002122
    +B1550014542941N00620398EA0209202124
    +B1550034542948N00620404EA0209402127
    +B1550054542955N00620411EA0209702129
    +B1550074542962N00620419EA0210202134
    +B1550094542968N00620424EA0210702139
    +B1550114542974N00620429EA0211202144
    +B1550134542980N00620434EA0211902151
    +B1550154542986N00620439EA0212602158
    +B1550174542991N00620444EA0213002164
    +B1550194542993N00620436EA0213102169
    +B1550214542987N00620425EA0213502173
    +B1550234542972N00620429EA0213802176
    +B1550254542958N00620450EA0214002180
    +B1550274542958N00620474EA0214902186
    +B1550294542963N00620487EA0215902194
    +B1550314542969N00620493EA0216702201
    +B1550334542976N00620499EA0217402209
    +B1550354542984N00620500EA0217902215
    +B1550374542987N00620493EA0218002220
    +B1550394542979N00620487EA0218302223
    +B1550414542965N00620490EA0218602224
    +B1550434542953N00620501EA0219002228
    +B1550454542945N00620517EA0219602233
    +B1550474542938N00620535EA0220402240
    +B1550494542936N00620553EA0221102246
    +B1550514542939N00620568EA0221702252
    +B1550534542947N00620573EA0222302257
    +B1550554542950N00620566EA0222602263
    +B1550574542942N00620558EA0222902268
    +B1550594542927N00620559EA0223702273
    +B1551014542913N00620568EA0224402281
    +B1551034542903N00620579EA0224902287
    +B1551054542895N00620596EA0225302292
    +B1551074542894N00620614EA0225602296
    +B1551094542902N00620620EA0225802299
    +B1551114542906N00620610EA0226202302
    +B1551134542899N00620597EA0226802306
    +B1551154542888N00620590EA0227502314
    +B1551174542880N00620586EA0228002318
    +B1551194542872N00620582EA0228402323
    +B1551214542861N00620581EA0228902327
    +B1551234542849N00620593EA0229202331
    +B1551254542844N00620614EA0229502334
    +B1551274542848N00620630EA0229902338
    +B1551294542859N00620628EA0229902340
    +B1551314542862N00620615EA0230202344
    +B1551334542856N00620603EA0231002349
    +B1551354542848N00620598EA0231302351
    +B1551374542842N00620590EA0231402353
    +B1551394542835N00620585EA0231602355
    +B1551414542823N00620586EA0231702356
    +B1551434542810N00620600EA0231802357
    +B1551454542803N00620619EA0232002357
    +B1551474542805N00620638EA0232302359
    +B1551494542815N00620644EA0232302360
    +B1551514542825N00620641EA0232302361
    +B1551534542834N00620641EA0232502362
    +B1551554542843N00620641EA0232402362
    +B1551574542850N00620639EA0231802361
    +B1551594542858N00620637EA0230802359
    +B1552014542869N00620635EA0230402356
    +B1552034542877N00620633EA0230502354
    +B1552054542881N00620632EA0230202350
    +B1552074542887N00620628EA0229402344
    +B1552094542896N00620624EA0229102339
    +B1552114542904N00620624EA0229102336
    +B1552134542911N00620624EA0228802332
    +B1552154542919N00620622EA0228302326
    +B1552174542929N00620621EA0228002322
    +B1552194542938N00620622EA0227902320
    +B1552214542946N00620624EA0227602316
    +B1552234542956N00620626EA0227202312
    +B1552254542966N00620627EA0227002309
    +B1552274542975N00620629EA0226902307
    +B1552294542985N00620631EA0226702304
    +B1552314542994N00620634EA0226202300
    +B1552334543004N00620637EA0225802295
    +B1552354543013N00620640EA0225502292
    +B1552374543022N00620644EA0225202288
    +B1552394543032N00620648EA0224702283
    +B1552414543042N00620652EA0224202279
    +B1552434543051N00620656EA0223902275
    +B1552454543061N00620658EA0223502271
    +B1552474543070N00620661EA0223102266
    +B1552494543080N00620663EA0222702262
    +B1552514543091N00620665EA0222402259
    +B1552534543101N00620668EA0221902255
    +B1552554543111N00620672EA0221502251
    +B1552574543121N00620675EA0221102246
    +B1552594543131N00620678EA0220702242
    +B1553014543142N00620680EA0220302238
    +B1553034543152N00620682EA0219902234
    +B1553054543162N00620686EA0219602231
    +B1553074543172N00620690EA0219302228
    +B1553094543182N00620694EA0219002225
    +B1553114543192N00620698EA0218702221
    +B1553134543202N00620703EA0218502219
    +B1553154543212N00620709EA0218202216
    +B1553174543222N00620716EA0217902214
    +B1553194543233N00620722EA0217802212
    +B1553214543242N00620729EA0217502209
    +B1553234543251N00620737EA0217202207
    +B1553254543260N00620743EA0216802203
    +B1553274543270N00620749EA0216402199
    +B1553294543280N00620756EA0216402198
    +B1553314543289N00620761EA0216202196
    +B1553334543298N00620766EA0216002193
    +B1553354543308N00620770EA0215802191
    +B1553374543317N00620774EA0215502189
    +B1553394543326N00620778EA0215202186
    +B1553414543336N00620781EA0215102184
    +B1553434543346N00620783EA0215002183
    +B1553454543354N00620784EA0214802182
    +B1553474543361N00620784EA0214602179
    +B1553494543370N00620788EA0214302176
    +B1553514543381N00620793EA0214102175
    +B1553534543392N00620799EA0214602174
    +B1553554543400N00620805EA0215202175
    +B1553574543409N00620811EA0215502179
    +B1553594543418N00620816EA0215802183
    +B1554014543428N00620821EA0216302188
    +B1554034543437N00620826EA0216602193
    +B1554054543445N00620828EA0216402196
    +B1554074543453N00620824EA0216202197
    +B1554094543463N00620822EA0216302198
    +B1554114543472N00620823EA0216102198
    +B1554134543482N00620822EA0215802197
    +B1554154543493N00620824EA0215602195
    +B1554174543504N00620826EA0215402193
    +B1554194543515N00620827EA0215302192
    +B1554214543527N00620828EA0215302191
    +B1554234543539N00620828EA0215202190
    +B1554254543550N00620830EA0215102189
    +B1554274543561N00620834EA0215002188
    +B1554294543573N00620838EA0214902187
    +B1554314543584N00620843EA0214902187
    +B1554334543595N00620847EA0214902187
    +B1554354543606N00620850EA0214702185
    +B1554374543618N00620852EA0214702184
    +B1554394543630N00620854EA0214702184
    +B1554414543641N00620856EA0214702184
    +B1554434543652N00620859EA0214702184
    +B1554454543663N00620862EA0214702184
    +B1554474543674N00620864EA0214802184
    +B1554494543685N00620866EA0214802185
    +B1554514543696N00620868EA0214802185
    +B1554534543708N00620871EA0214802185
    +B1554554543720N00620874EA0214802185
    +B1554574543732N00620878EA0214902185
    +B1554594543744N00620882EA0215002186
    +B1555014543756N00620887EA0215202188
    +B1555034543768N00620893EA0215302189
    +B1555054543781N00620898EA0215302189
    +B1555074543794N00620902EA0215402190
    +B1555094543806N00620905EA0215402190
    +B1555114543819N00620909EA0215402190
    +B1555134543832N00620913EA0215302190
    +B1555154543845N00620916EA0215302190
    +B1555174543858N00620919EA0215302190
    +B1555194543870N00620921EA0215202189
    +B1555214543883N00620923EA0215102188
    +B1555234543896N00620926EA0215002187
    +B1555254543908N00620930EA0214902186
    +B1555274543921N00620933EA0214802184
    +B1555294543933N00620936EA0214602183
    +B1555314543944N00620941EA0214402181
    +B1555334543956N00620947EA0214002177
    +B1555354543968N00620952EA0213702174
    +B1555374543981N00620957EA0213502171
    +B1555394543993N00620963EA0213402170
    +B1555414544004N00620970EA0213102167
    +B1555434544016N00620979EA0212702163
    +B1555454544028N00620988EA0212602161
    +B1555474544040N00620995EA0212302159
    +B1555494544052N00621001EA0211902156
    +B1555514544065N00621006EA0211802154
    +B1555534544078N00621011EA0211902153
    +B1555554544090N00621016EA0212202153
    +B1555574544101N00621022EA0212302154
    +B1555594544113N00621026EA0212302155
    +B1556014544126N00621031EA0212402156
    +B1556034544138N00621034EA0212502157
    +B1556054544150N00621036EA0212502158
    +B1556074544162N00621038EA0212402157
    +B1556094544175N00621040EA0212202156
    +B1556114544188N00621043EA0212202156
    +B1556134544199N00621046EA0211902153
    +B1556154544212N00621050EA0211502150
    +B1556174544226N00621056EA0211302148
    +B1556194544238N00621063EA0211302147
    +B1556214544252N00621068EA0211102145
    +B1556234544266N00621073EA0211102145
    +B1556254544280N00621077EA0211402145
    +B1556274544292N00621081EA0211502146
    +B1556294544303N00621086EA0211202146
    +B1556314544316N00621090EA0210902144
    +B1556334544330N00621093EA0210802143
    +B1556354544342N00621098EA0210602142
    +B1556374544355N00621102EA0210302139
    +B1556394544370N00621108EA0210002136
    +B1556414544383N00621114EA0209802134
    +B1556434544396N00621121EA0209502131
    +B1556454544409N00621128EA0209102127
    +B1556474544424N00621134EA0208702123
    +B1556494544438N00621140EA0208702121
    +B1556514544451N00621144EA0208402119
    +B1556534544463N00621147EA0207902115
    +B1556554544478N00621151EA0207502110
    +B1556574544492N00621155EA0207302108
    +B1556594544506N00621159EA0207002105
    +B1557014544520N00621164EA0206702103
    +B1557034544535N00621169EA0206702101
    +B1557054544549N00621175EA0206502100
    +B1557074544562N00621182EA0206202097
    +B1557094544576N00621187EA0205902094
    +B1557114544591N00621192EA0205802092
    +B1557134544604N00621199EA0205602090
    +B1557154544618N00621205EA0205402088
    +B1557174544632N00621211EA0205402088
    +B1557194544644N00621218EA0205202086
    +B1557214544657N00621225EA0205002084
    +B1557234544671N00621231EA0204702081
    +B1557254544684N00621238EA0204702081
    +B1557274544696N00621244EA0204702080
    +B1557294544709N00621251EA0204502078
    +B1557314544723N00621258EA0204402077
    +B1557334544735N00621266EA0204302077
    +B1557354544747N00621274EA0204102074
    +B1557374544760N00621282EA0203802072
    +B1557394544774N00621289EA0203802071
    +B1557414544786N00621297EA0203702070
    +B1557434544799N00621306EA0203502068
    +B1557454544812N00621315EA0203302066
    +B1557474544825N00621324EA0203102065
    +B1557494544837N00621332EA0203002063
    +B1557514544850N00621341EA0202702060
    +B1557534544863N00621350EA0202502059
    +B1557554544877N00621357EA0202502057
    +B1557574544890N00621366EA0202402056
    +B1557594544905N00621373EA0202202055
    +B1558014544919N00621380EA0202202054
    +B1558034544932N00621387EA0202102054
    +B1558054544946N00621393EA0202002052
    +B1558074544959N00621399EA0201802051
    +B1558094544973N00621405EA0201702050
    +B1558114544986N00621412EA0201602049
    +B1558134544999N00621419EA0201502048
    +B1558154545012N00621429EA0201302046
    +B1558174545024N00621439EA0201202045
    +B1558194545038N00621447EA0201002043
    +B1558214545051N00621455EA0201002043
    +B1558234545064N00621461EA0200902042
    +B1558254545077N00621466EA0200802041
    +B1558274545090N00621472EA0200702040
    +B1558294545103N00621478EA0200602039
    +B1558314545116N00621483EA0200602038
    +B1558334545130N00621487EA0200502037
    +B1558354545142N00621491EA0200402036
    +B1558374545155N00621495EA0200302035
    +B1558394545168N00621499EA0200102033
    +B1558414545182N00621503EA0199902031
    +B1558434545195N00621506EA0199602029
    +B1558454545208N00621509EA0199402026
    +B1558474545222N00621512EA0199102023
    +B1558494545235N00621516EA0198902021
    +B1558514545247N00621521EA0198602018
    +B1558534545260N00621527EA0198402016
    +B1558554545273N00621534EA0198402015
    +B1558574545284N00621538EA0198102013
    +B1558594545295N00621540EA0197602010
    +B1559014545308N00621541EA0197202006
    +B1559034545321N00621542EA0197002005
    +B1559054545333N00621543EA0196802002
    +B1559074545346N00621543EA0196702000
    +B1559094545358N00621544EA0196701999
    +B1559114545369N00621544EA0196601998
    +B1559134545379N00621543EA0196301996
    +B1559154545390N00621541EA0196201995
    +B1559174545401N00621538EA0196301995
    +B1559194545411N00621535EA0196201994
    +B1559214545421N00621532EA0195901992
    +B1559234545433N00621531EA0195801991
    +B1559254545444N00621533EA0195601990
    +B1559274545456N00621536EA0195401988
    +B1559294545467N00621539EA0195201985
    +B1559314545479N00621543EA0195101983
    +B1559334545491N00621546EA0194901982
    +B1559354545502N00621550EA0194801980
    +B1559374545513N00621555EA0194801980
    +B1559394545523N00621560EA0194701979
    +B1559414545534N00621564EA0194501978
    +B1559434545546N00621568EA0194501977
    +B1559454545557N00621572EA0194601977
    +B1559474545569N00621576EA0194501977
    +B1559494545580N00621580EA0194401976
    +B1559514545593N00621583EA0194301975
    +B1559534545604N00621585EA0194201974
    +B1559554545616N00621587EA0194201974
    +B1559574545628N00621590EA0194201973
    +B1559594545639N00621593EA0194201973
    +B1600014545651N00621596EA0194101972
    +B1600034545662N00621599EA0193901971
    +B1600054545673N00621603EA0193701969
    +B1600074545685N00621606EA0193601968
    +B1600094545696N00621609EA0193501967
    +B1600114545708N00621613EA0193401965
    +B1600134545720N00621617EA0193301964
    +B1600154545732N00621622EA0193201963
    +B1600174545743N00621624EA0193101962
    +B1600194545754N00621628EA0192901961
    +B1600214545766N00621632EA0192801958
    +B1600234545778N00621639EA0192601957
    +B1600254545788N00621647EA0192501956
    +B1600274545799N00621653EA0192301954
    +B1600294545810N00621660EA0192101952
    +B1600314545821N00621666EA0192001950
    +B1600334545832N00621671EA0191801949
    +B1600354545844N00621677EA0191601947
    +B1600374545855N00621683EA0191501945
    +B1600394545865N00621688EA0191301944
    +B1600414545876N00621694EA0191101942
    +B1600434545887N00621699EA0190901940
    +B1600454545898N00621703EA0190801938
    +B1600474545908N00621707EA0190501936
    +B1600494545918N00621712EA0190201933
    +B1600514545928N00621716EA0189901930
    +B1600534545938N00621722EA0189501926
    +B1600554545948N00621727EA0189101922
    +B1600574545958N00621734EA0188801919
    +B1600594545967N00621740EA0188501916
    +B1601014545977N00621743EA0188201912
    +B1601034545987N00621745EA0187801909
    +B1601054545997N00621747EA0187501906
    +B1601074546008N00621748EA0187301903
    +B1601094546018N00621749EA0187101901
    +B1601114546028N00621752EA0186901898
    +B1601134546037N00621757EA0186401894
    +B1601154546048N00621764EA0185901890
    +B1601174546058N00621769EA0185801888
    +B1601194546067N00621773EA0185701886
    +B1601214546077N00621780EA0185501883
    +B1601234546087N00621788EA0185301881
    +B1601254546096N00621795EA0185101880
    +B1601274546106N00621802EA0185101879
    +B1601294546115N00621806EA0184901878
    +B1601314546123N00621810EA0184401876
    +B1601334546134N00621815EA0183801872
    +B1601354546146N00621821EA0183501868
    +B1601374546158N00621826EA0183501867
    +B1601394546169N00621829EA0183301866
    +B1601414546180N00621832EA0183101863
    +B1601434546190N00621834EA0183001862
    +B1601454546200N00621835EA0182901860
    +B1601474546211N00621840EA0182601857
    +B1601494546223N00621844EA0182501856
    +B1601514546234N00621849EA0182501855
    +B1601534546244N00621853EA0182501855
    +B1601554546253N00621856EA0182301853
    +B1601574546265N00621858EA0182201852
    +B1601594546276N00621860EA0182301853
    +B1602014546285N00621861EA0182301852
    +B1602034546294N00621863EA0182101851
    +B1602054546303N00621866EA0181901848
    +B1602074546314N00621871EA0181701847
    +B1602094546323N00621875EA0181501844
    +B1602114546333N00621878EA0181201841
    +B1602134546344N00621882EA0181001839
    +B1602154546355N00621887EA0180901838
    +B1602174546366N00621891EA0180701836
    +B1602194546376N00621896EA0180501834
    +B1602214546388N00621901EA0180501833
    +B1602234546398N00621906EA0180401833
    +B1602254546407N00621913EA0180301831
    +B1602274546417N00621918EA0180001829
    +B1602294546428N00621923EA0179901827
    +B1602314546438N00621929EA0180001827
    +B1602334546447N00621934EA0179801826
    +B1602354546457N00621938EA0179601824
    +B1602374546466N00621940EA0179601824
    +B1602394546474N00621944EA0179601823
    +B1602414546484N00621949EA0179401822
    +B1602434546494N00621955EA0179501822
    +B1602454546503N00621960EA0179601823
    +B1602474546513N00621967EA0179701825
    +B1602494546522N00621973EA0180101827
    +B1602514546530N00621977EA0180401831
    +B1602534546538N00621982EA0180801835
    +B1602554546545N00621985EA0181001838
    +B1602574546548N00621978EA0181001840
    +B1602594546545N00621966EA0181101841
    +B1603014546535N00621961EA0181301842
    +B1603034546521N00621969EA0181501844
    +B1603054546514N00621988EA0181601845
    +B1603074546515N00622008EA0181701846
    +B1603094546522N00622021EA0181801847
    +B1603114546530N00622028EA0182001848
    +B1603134546538N00622030EA0182001849
    +B1603154546547N00622031EA0182301851
    +B1603174546557N00622034EA0182901854
    +B1603194546565N00622036EA0183301859
    +B1603214546571N00622039EA0183601862
    +B1603234546577N00622038EA0183601864
    +B1603254546577N00622028EA0183601865
    +B1603274546568N00622020EA0183701865
    +B1603294546553N00622026EA0183701866
    +B1603314546542N00622044EA0183701867
    +B1603334546539N00622066EA0183701867
    +B1603354546545N00622084EA0183801868
    +B1603374546553N00622096EA0184001869
    +B1603394546562N00622106EA0184201871
    +B1603414546571N00622115EA0184401872
    +B1603434546581N00622123EA0184601875
    +B1603454546590N00622128EA0184901877
    +B1603474546600N00622133EA0185201880
    +B1603494546610N00622137EA0185601884
    +B1603514546618N00622142EA0186001888
    +B1603534546624N00622145EA0186201892
    +B1603554546630N00622146EA0186301894
    +B1603574546632N00622140EA0186401895
    +B1603594546626N00622133EA0186501897
    +B1604014546612N00622133EA0186601897
    +B1604034546599N00622147EA0186501896
    +B1604054546591N00622168EA0186601897
    +B1604074546591N00622190EA0186801898
    +B1604094546597N00622207EA0187101901
    +B1604114546604N00622220EA0187401904
    +B1604134546612N00622231EA0187701907
    +B1604154546621N00622239EA0188001909
    +B1604174546630N00622243EA0188201912
    +B1604194546640N00622247EA0188501914
    +B1604214546651N00622251EA0188901918
    +B1604234546661N00622254EA0189301922
    +B1604254546671N00622252EA0189801926
    +B1604274546679N00622249EA0190301932
    +B1604294546687N00622250EA0190601935
    +B1604314546696N00622251EA0190901939
    +B1604334546705N00622251EA0191401944
    +B1604354546713N00622249EA0191901949
    +B1604374546719N00622247EA0192501955
    +B1604394546726N00622248EA0193201962
    +B1604414546733N00622251EA0193901969
    +B1604434546740N00622251EA0194701977
    +B1604454546745N00622251EA0195301984
    +B1604474546747N00622243EA0195701990
    +B1604494546740N00622229EA0196101995
    +B1604514546725N00622227EA0196802001
    +B1604534546713N00622241EA0197302006
    +B1604554546709N00622262EA0197902011
    +B1604574546714N00622276EA0198502017
    +B1604594546723N00622276EA0199002023
    +B1605014546728N00622266EA0199802029
    +B1605034546721N00622253EA0200402035
    +B1605054546707N00622250EA0200902041
    +B1605074546692N00622261EA0201402046
    +B1605094546686N00622283EA0202002052
    +B1605114546689N00622301EA0202702058
    +B1605134546696N00622311EA0203302065
    +B1605154546704N00622315EA0204002072
    +B1605174546711N00622314EA0204802080
    +B1605194546714N00622307EA0205402087
    +B1605214546714N00622297EA0206202094
    +B1605234546706N00622286EA0206802101
    +B1605254546693N00622285EA0207302108
    +B1605274546679N00622295EA0207802113
    +B1605294546672N00622314EA0208202118
    +B1605314546674N00622334EA0208902124
    +B1605334546681N00622345EA0209602131
    +B1605354546688N00622348EA0210202137
    +B1605374546693N00622346EA0210902144
    +B1605394546695N00622337EA0211402149
    +B1605414546688N00622326EA0212002155
    +B1605434546678N00622319EA0212702161
    +B1605454546665N00622323EA0213102167
    +B1605474546652N00622339EA0213602172
    +B1605494546646N00622358EA0214202178
    +B1605514546646N00622376EA0214702184
    +B1605534546649N00622391EA0215102188
    +B1605554546656N00622400EA0215302191
    +B1605574546666N00622403EA0215602194
    +B1605594546676N00622399EA0216302199
    +B1606014546681N00622394EA0217102206
    +B1606034546686N00622392EA0217802213
    +B1606054546690N00622390EA0218202219
    +B1606074546691N00622384EA0218502223
    +B1606094546685N00622374EA0218702226
    +B1606114546672N00622365EA0219002229
    +B1606134546656N00622370EA0219502233
    +B1606154546646N00622387EA0219902236
    +B1606174546645N00622406EA0220302241
    +B1606194546651N00622420EA0220802245
    +B1606214546660N00622424EA0221302250
    +B1606234546667N00622421EA0222002256
    +B1606254546668N00622413EA0222402262
    +B1606274546663N00622406EA0222602266
    +B1606294546652N00622398EA0222802268
    +B1606314546635N00622397EA0223302272
    +B1606334546620N00622410EA0223602275
    +B1606354546613N00622429EA0224002279
    +B1606374546611N00622447EA0224402283
    +B1606394546615N00622462EA0224702286
    +B1606414546624N00622473EA0225002289
    +B1606434546634N00622479EA0225602293
    +B1606454546642N00622481EA0226302299
    +B1606474546649N00622481EA0226902306
    +B1606494546654N00622482EA0227502312
    +B1606514546660N00622482EA0227802317
    +B1606534546665N00622478EA0227602321
    +B1606554546664N00622465EA0227002322
    +B1606574546654N00622450EA0226902320
    +B1606594546639N00622449EA0227102320
    +B1607014546626N00622462EA0227102318
    +B1607034546620N00622480EA0227102316
    +B1607054546619N00622499EA0227202315
    +B1607074546623N00622516EA0227502317
    +B1607094546630N00622530EA0228002320
    +B1607114546637N00622542EA0228402325
    +B1607134546642N00622555EA0228902329
    +B1607154546649N00622567EA0229402334
    +B1607174546656N00622570EA0230002339
    +B1607194546662N00622563EA0230302343
    +B1607214546660N00622549EA0230502346
    +B1607234546648N00622542EA0230702348
    +B1607254546633N00622547EA0231002352
    +B1607274546622N00622564EA0231302354
    +B1607294546620N00622584EA0231602357
    +B1607314546624N00622601EA0232002360
    +B1607334546631N00622612EA0232402364
    +B1607354546638N00622618EA0232802368
    +B1607374546645N00622613EA0233102372
    +B1607394546647N00622603EA0233302373
    +B1607414546644N00622592EA0233602376
    +B1607434546633N00622585EA0233702378
    +B1607454546618N00622587EA0233802380
    +B1607474546606N00622601EA0234202382
    +B1607494546602N00622621EA0234502385
    +B1607514546604N00622637EA0234702388
    +B1607534546610N00622650EA0235002391
    +B1607554546616N00622661EA0235402394
    +B1607574546621N00622669EA0235702397
    +B1607594546627N00622674EA0235802399
    +B1608014546634N00622675EA0236002401
    +B1608034546642N00622674EA0236102402
    +B1608054546650N00622673EA0236102402
    +B1608074546657N00622672EA0236202402
    +B1608094546664N00622671EA0236202402
    +B1608114546672N00622668EA0236302403
    +B1608134546679N00622664EA0236702405
    +B1608154546685N00622663EA0237202409
    +B1608174546692N00622662EA0237702414
    +B1608194546699N00622661EA0238002417
    +B1608214546707N00622660EA0238302421
    +B1608234546714N00622657EA0238702425
    +B1608254546717N00622649EA0238802428
    +B1608274546712N00622639EA0238802430
    +B1608294546700N00622635EA0238702431
    +B1608314546684N00622642EA0238902432
    +B1608334546673N00622659EA0239302433
    +B1608354546669N00622679EA0239702437
    +B1608374546669N00622697EA0240102442
    +B1608394546672N00622712EA0240502446
    +B1608414546677N00622723EA0240902449
    +B1608434546683N00622731EA0241102452
    +B1608454546690N00622737EA0241402454
    +B1608474546698N00622738EA0241502455
    +B1608494546707N00622741EA0241502456
    +B1608514546716N00622746EA0241502456
    +B1608534546725N00622750EA0241302455
    +B1608554546736N00622751EA0241302454
    +B1608574546748N00622749EA0241602455
    +B1608594546758N00622748EA0242302456
    +B1609014546769N00622748EA0242802460
    +B1609034546780N00622743EA0243202464
    +B1609054546791N00622739EA0243902470
    +B1609074546802N00622738EA0244602477
    +B1609094546813N00622736EA0245002482
    +B1609114546825N00622733EA0245602488
    +B1609134546837N00622732EA0246402495
    +B1609154546850N00622733EA0247502504
    +B1609174546862N00622733EA0248402514
    +B1609194546874N00622732EA0249302523
    +B1609214546886N00622729EA0250102533
    +B1609234546897N00622725EA0250702541
    +B1609254546908N00622723EA0251202547
    +B1609274546920N00622719EA0251402552
    +B1609294546933N00622711EA0251702556
    +B1609314546944N00622704EA0252002559
    +B1609334546956N00622698EA0252302563
    +B1609354546966N00622694EA0252802568
    +B1609374546975N00622692EA0253102572
    +B1609394546984N00622688EA0253202574
    +B1609414546993N00622681EA0253302575
    +B1609434547002N00622677EA0253302576
    +B1609454547010N00622674EA0253302577
    +B1609474547017N00622670EA0253202576
    +B1609494547025N00622665EA0253002574
    +B1609514547033N00622661EA0252702572
    +B1609534547044N00622656EA0252302568
    +B1609554547054N00622652EA0251902564
    +B1609574547064N00622649EA0251602560
    +B1609594547075N00622647EA0251202555
    +B1610014547086N00622647EA0250702550
    +B1610034547097N00622645EA0250402546
    +B1610054547109N00622641EA0250402543
    +B1610074547119N00622637EA0250402542
    +B1610094547129N00622633EA0250202540
    +B1610114547140N00622627EA0250502540
    +B1610134547150N00622620EA0251102541
    +B1610154547158N00622615EA0251502545
    +B1610174547167N00622611EA0251702547
    +B1610194547177N00622609EA0251802549
    +B1610214547187N00622610EA0251802550
    +B1610234547198N00622610EA0251902552
    +B1610254547210N00622611EA0252202554
    +B1610274547221N00622612EA0252702558
    +B1610294547231N00622612EA0253202563
    +B1610314547240N00622609EA0253602568
    +B1610334547249N00622607EA0254202574
    +B1610354547257N00622604EA0254502579
    +B1610374547266N00622603EA0254402583
    +B1610394547276N00622602EA0254402585
    +B1610414547288N00622600EA0254602587
    +B1610434547297N00622598EA0254802590
    +B1610454547307N00622598EA0255002591
    +B1610474547315N00622594EA0255202594
    +B1610494547324N00622591EA0255602597
    +B1610514547331N00622589EA0255802600
    +B1610534547340N00622587EA0256202603
    +B1610554547350N00622587EA0256902608
    +B1610574547359N00622590EA0257602614
    +B1610594547368N00622592EA0258002618
    +B1611014547377N00622595EA0258402623
    +B1611034547386N00622600EA0258702627
    +B1611054547396N00622605EA0258902629
    +B1611074547405N00622608EA0259202632
    +B1611094547414N00622608EA0259402635
    +B1611114547424N00622609EA0259702638
    +B1611134547434N00622611EA0260002641
    +B1611154547444N00622612EA0260502645
    +B1611174547453N00622612EA0261002650
    +B1611194547461N00622613EA0261202653
    +B1611214547469N00622612EA0261002655
    +B1611234547480N00622610EA0260802655
    +B1611254547491N00622612EA0260902657
    +B1611274547502N00622615EA0261402659
    +B1611294547512N00622616EA0262002664
    +B1611314547523N00622618EA0262602670
    +B1611334547533N00622621EA0263302676
    +B1611354547544N00622625EA0263702681
    +B1611374547556N00622630EA0263802684
    +B1611394547568N00622637EA0263902686
    +B1611414547578N00622647EA0264302688
    +B1611434547587N00622657EA0264602691
    +B1611454547596N00622665EA0264802693
    +B1611474547603N00622675EA0265002695
    +B1611494547607N00622690EA0265102696
    +B1611514547612N00622704EA0265302698
    +B1611534547616N00622718EA0265402699
    +B1611554547619N00622733EA0265602700
    +B1611574547623N00622748EA0265702701
    +B1611594547628N00622763EA0266002703
    +B1612014547633N00622777EA0266202706
    +B1612034547638N00622790EA0266202707
    +B1612054547645N00622805EA0265902706
    +B1612074547650N00622820EA0265802705
    +B1612094547655N00622834EA0265602703
    +B1612114547660N00622850EA0265002700
    +B1612134547666N00622868EA0264302695
    +B1612154547670N00622885EA0263702688
    +B1612174547677N00622901EA0263302683
    +B1612194547687N00622912EA0262802677
    +B1612214547699N00622919EA0262302672
    +B1612234547712N00622926EA0261702666
    +B1612254547725N00622934EA0261102659
    +B1612274547738N00622942EA0260502653
    +B1612294547751N00622950EA0259902646
    +B1612314547764N00622955EA0259402641
    +B1612334547776N00622959EA0258802634
    +B1612354547790N00622965EA0258202627
    +B1612374547803N00622972EA0258002622
    +B1612394547814N00622981EA0257702618
    +B1612414547823N00622989EA0257302613
    +B1612434547834N00622995EA0256802608
    +B1612454547845N00623001EA0256502604
    +B1612474547857N00623006EA0256402601
    +B1612494547866N00623010EA0256902599
    +B1612514547872N00623011EA0256702597
    +B1612534547879N00623008EA0256002594
    +B1612554547890N00623009EA0255302589
    +B1612574547901N00623014EA0255302587
    +B1612594547911N00623018EA0255502587
    +B1613014547922N00623024EA0255702589
    +B1613034547931N00623033EA0256202592
    +B1613054547940N00623039EA0256902598
    +B1613074547948N00623043EA0257502605
    +B1613094547957N00623039EA0258002612
    +B1613114547963N00623025EA0258302617
    +B1613134547963N00623007EA0258502620
    +B1613154547955N00622992EA0258902624
    +B1613174547941N00622992EA0259402628
    +B1613194547931N00623004EA0259802634
    +B1613214547928N00623019EA0260202638
    +B1613234547931N00623034EA0260702644
    +B1613254547936N00623047EA0261202649
    +B1613274547941N00623061EA0261702655
    +B1613294547946N00623073EA0262102659
    +B1613314547951N00623087EA0262402663
    +B1613334547956N00623101EA0262702666
    +B1613354547961N00623115EA0263002669
    +B1613374547967N00623129EA0263402672
    +B1613394547972N00623143EA0263702676
    +B1613414547977N00623158EA0263902678
    +B1613434547982N00623173EA0264002680
    +B1613454547987N00623188EA0264102681
    +B1613474547992N00623203EA0264102681
    +B1613494547998N00623218EA0264002680
    +B1613514548004N00623236EA0263902680
    +B1613534548010N00623250EA0263702679
    +B1613554548015N00623266EA0263102676
    +B1613574548021N00623284EA0262502672
    +B1613594548029N00623299EA0262102668
    +B1614014548036N00623313EA0261702663
    +B1614034548042N00623331EA0261202658
    +B1614054548048N00623349EA0261002654
    +B1614074548053N00623365EA0260602651
    +B1614094548060N00623383EA0259802645
    +B1614114548068N00623402EA0259302640
    +B1614134548074N00623419EA0259202636
    +B1614154548080N00623435EA0258902633
    +B1614174548086N00623453EA0258502629
    +B1614194548092N00623470EA0258302626
    +B1614214548098N00623487EA0258102623
    +B1614234548103N00623504EA0257802621
    +B1614254548108N00623522EA0257702619
    +B1614274548114N00623539EA0257602618
    +B1614294548119N00623555EA0257402616
    +B1614314548125N00623573EA0257402614
    +B1614334548131N00623590EA0257302613
    +B1614354548137N00623607EA0257202613
    +B1614374548142N00623625EA0256902611
    +B1614394548149N00623643EA0256602608
    +B1614414548156N00623661EA0256502606
    +B1614434548162N00623678EA0256302604
    +B1614454548169N00623696EA0256002601
    +B1614474548175N00623715EA0255802599
    +B1614494548181N00623733EA0255602597
    +B1614514548187N00623751EA0255302595
    +B1614534548193N00623769EA0255102592
    +B1614554548200N00623787EA0254902590
    +B1614574548206N00623804EA0254702588
    +B1614594548212N00623822EA0254302584
    +B1615014548219N00623840EA0254102582
    +B1615034548226N00623858EA0254002580
    +B1615054548232N00623875EA0253802578
    +B1615074548238N00623893EA0253602576
    +B1615094548245N00623911EA0253402575
    +B1615114548251N00623928EA0253202573
    +B1615134548258N00623946EA0253002570
    +B1615154548263N00623965EA0252702567
    +B1615174548267N00623984EA0252402564
    +B1615194548271N00624003EA0252102561
    +B1615214548275N00624021EA0251902558
    +B1615234548278N00624040EA0251702556
    +B1615254548279N00624059EA0251602555
    +B1615274548281N00624077EA0251502553
    +B1615294548283N00624098EA0251102550
    +B1615314548285N00624120EA0250902548
    +B1615334548288N00624140EA0250902547
    +B1615354548291N00624159EA0250802546
    +B1615374548292N00624179EA0250602544
    +B1615394548295N00624198EA0250402543
    +B1615414548299N00624217EA0250302542
    +B1615434548303N00624236EA0250202540
    +B1615454548306N00624256EA0250102539
    +B1615474548310N00624274EA0249802538
    +B1615494548316N00624292EA0249702536
    +B1615514548321N00624310EA0249402533
    +B1615534548327N00624328EA0249102530
    +B1615554548332N00624347EA0249002529
    +B1615574548337N00624364EA0248802527
    +B1615594548343N00624382EA0248602524
    +B1616014548348N00624401EA0248402522
    +B1616034548352N00624419EA0248202520
    +B1616054548357N00624437EA0247902518
    +B1616074548361N00624455EA0248002517
    +B1616094548365N00624472EA0248102517
    +B1616114548369N00624486EA0247902515
    +B1616134548374N00624501EA0247702514
    +B1616154548377N00624516EA0247702513
    +B1616174548380N00624531EA0247302511
    +B1616194548386N00624547EA0246902507
    +B1616214548391N00624563EA0246702505
    +B1616234548396N00624579EA0246402503
    +B1616254548402N00624593EA0246102500
    +B1616274548408N00624610EA0245702496
    +B1616294548413N00624627EA0245602494
    +B1616314548418N00624644EA0245402492
    +B1616334548423N00624660EA0245202490
    +B1616354548429N00624676EA0245102489
    +B1616374548435N00624691EA0244902487
    +B1616394548442N00624706EA0244602484
    +B1616414548449N00624721EA0244402483
    +B1616434548456N00624735EA0244202480
    +B1616454548465N00624748EA0243802476
    +B1616474548473N00624762EA0243502473
    +B1616494548481N00624774EA0243202471
    +B1616514548490N00624786EA0242802467
    +B1616534548497N00624801EA0242402463
    +B1616554548504N00624817EA0242002459
    +B1616574548511N00624832EA0241902457
    +B1616594548517N00624847EA0241702455
    +B1617014548523N00624861EA0241502453
    +B1617034548530N00624874EA0241302451
    +B1617054548537N00624887EA0241102448
    +B1617074548543N00624901EA0240902446
    +B1617094548550N00624914EA0240602443
    +B1617114548557N00624927EA0240302440
    +B1617134548563N00624942EA0240002437
    +B1617154548571N00624956EA0239602433
    +B1617174548579N00624969EA0239302430
    +B1617194548587N00624981EA0239102428
    +B1617214548595N00624994EA0238802425
    +B1617234548602N00625006EA0238502422
    +B1617254548610N00625019EA0237902418
    +B1617274548619N00625031EA0237502413
    +B1617294548628N00625043EA0237102409
    +B1617314548637N00625055EA0236602405
    +B1617334548646N00625069EA0236202400
    +B1617354548653N00625083EA0235902397
    +B1617374548660N00625098EA0235502393
    +B1617394548668N00625112EA0235102388
    +B1617414548675N00625128EA0234902385
    +B1617434548681N00625144EA0234802383
    +B1617454548687N00625159EA0234802382
    +B1617474548693N00625172EA0234502379
    +B1617494548700N00625184EA0234202377
    +B1617514548708N00625196EA0234002375
    +B1617534548715N00625207EA0233702372
    +B1617554548723N00625219EA0233402369
    +B1617574548731N00625230EA0233102366
    +B1617594548738N00625241EA0232802363
    +B1618014548746N00625253EA0232602361
    +B1618034548755N00625263EA0232402358
    +B1618054548763N00625274EA0232602358
    +B1618074548769N00625288EA0232602359
    +B1618094548775N00625299EA0232302358
    +B1618114548783N00625313EA0232102355
    +B1618134548790N00625329EA0232002355
    +B1618154548795N00625342EA0231902354
    +B1618174548802N00625354EA0231602351
    +B1618194548811N00625367EA0231302348
    +B1618214548819N00625378EA0231102346
    +B1618234548826N00625388EA0230902344
    +B1618254548835N00625398EA0230702342
    +B1618274548845N00625408EA0230602341
    +B1618294548853N00625417EA0230402339
    +B1618314548862N00625426EA0230102336
    +B1618334548871N00625437EA0229902334
    +B1618354548880N00625447EA0229902334
    +B1618374548888N00625458EA0229802333
    +B1618394548897N00625467EA0229602331
    +B1618414548907N00625477EA0229302328
    +B1618434548916N00625487EA0229202327
    +B1618454548925N00625497EA0229102326
    +B1618474548933N00625508EA0228902324
    +B1618494548941N00625519EA0228802323
    +B1618514548949N00625529EA0228502321
    +B1618534548957N00625539EA0228202318
    +B1618554548966N00625549EA0227902315
    +B1618574548974N00625562EA0227702312
    +B1618594548982N00625574EA0227502310
    +B1619014548991N00625584EA0227402308
    +B1619034548999N00625595EA0227202306
    +B1619054549007N00625605EA0227002304
    +B1619074549015N00625614EA0226702302
    +B1619094549023N00625625EA0226402299
    +B1619114549031N00625634EA0226202297
    +B1619134549040N00625643EA0226002295
    +B1619154549048N00625652EA0225802292
    +B1619174549057N00625660EA0225402288
    +B1619194549066N00625668EA0225102286
    +B1619214549075N00625677EA0225002284
    +B1619234549083N00625687EA0224802282
    +B1619254549091N00625699EA0224602280
    +B1619274549100N00625712EA0224402278
    +B1619294549108N00625723EA0224302276
    +B1619314549117N00625734EA0224002274
    +B1619334549126N00625744EA0224002273
    +B1619354549134N00625753EA0223802272
    +B1619374549143N00625762EA0223802271
    +B1619394549152N00625771EA0223702271
    +B1619414549161N00625779EA0223702270
    +B1619434549170N00625787EA0223702270
    +B1619454549178N00625794EA0223702270
    +B1619474549186N00625801EA0223802271
    +B1619494549194N00625809EA0224002272
    +B1619514549202N00625817EA0224002272
    +B1619534549209N00625825EA0224302274
    +B1619554549216N00625832EA0224502277
    +B1619574549222N00625838EA0224502279
    +B1619594549230N00625844EA0224302279
    +B1620014549239N00625849EA0224102277
    +B1620034549248N00625856EA0223902275
    +B1620054549256N00625864EA0223702273
    +B1620074549266N00625873EA0223502270
    +B1620094549275N00625882EA0223302268
    +B1620114549284N00625892EA0223102266
    +B1620134549294N00625901EA0222902264
    +B1620154549303N00625910EA0222802262
    +B1620174549310N00625921EA0222602260
    +B1620194549318N00625931EA0222102257
    +B1620214549328N00625938EA0221602252
    +B1620234549337N00625947EA0221302249
    +B1620254549346N00625957EA0221002245
    +B1620274549355N00625965EA0220702242
    +B1620294549365N00625975EA0220502239
    +B1620314549373N00625986EA0220302237
    +B1620334549382N00625997EA0220102234
    +B1620354549391N00626008EA0220002233
    +B1620374549400N00626018EA0219802231
    +B1620394549409N00626026EA0219602229
    +B1620414549418N00626037EA0219402226
    +B1620434549429N00626047EA0219302225
    +B1620454549438N00626054EA0219302225
    +B1620474549447N00626061EA0219002223
    +B1620494549456N00626068EA0218802221
    +B1620514549465N00626075EA0218602218
    +B1620534549474N00626081EA0218302215
    +B1620554549484N00626089EA0218002212
    +B1620574549492N00626095EA0218102211
    +B1620594549499N00626101EA0218002210
    +B1621014549506N00626110EA0217602208
    +B1621034549513N00626120EA0217302205
    +B1621054549521N00626129EA0217302204
    +B1621074549528N00626139EA0217302204
    +B1621094549535N00626149EA0217102202
    +B1621114549542N00626158EA0216902200
    +B1621134549550N00626166EA0216502197
    +B1621154549559N00626174EA0216202194
    +B1621174549568N00626180EA0216102192
    +B1621194549575N00626185EA0215802190
    +B1621214549583N00626189EA0215502187
    +B1621234549592N00626193EA0215202184
    +B1621254549601N00626200EA0215002182
    +B1621274549608N00626207EA0214702179
    +B1621294549616N00626212EA0214102175
    +B1621314549624N00626218EA0213602170
    +B1621334549632N00626224EA0213302167
    +B1621354549639N00626228EA0212902163
    +B1621374549647N00626230EA0212402158
    +B1621394549655N00626231EA0211902153
    +B1621414549663N00626233EA0211402148
    +B1621434549670N00626230EA0210902143
    +B1621454549677N00626224EA0210502138
    +B1621474549685N00626218EA0210402134
    +B1621494549692N00626214EA0210302132
    +B1621514549697N00626211EA0210502131
    +B1621534549701N00626207EA0210702134
    +B1621554549704N00626204EA0210702135
    +B1621574549709N00626201EA0210702135
    +B1621594549714N00626200EA0210702136
    +B1622014549720N00626200EA0210902138
    +B1622034549726N00626203EA0211002139
    +B1622054549733N00626206EA0211202142
    +B1622074549739N00626211EA0211402144
    +B1622094549746N00626216EA0211402145
    +B1622114549752N00626222EA0211602147
    +B1622134549758N00626229EA0211702148
    +B1622154549764N00626235EA0211602149
    +B1622174549770N00626241EA0211602149
    +B1622194549777N00626247EA0211702149
    +B1622214549784N00626254EA0211802150
    +B1622234549791N00626261EA0211902150
    +B1622254549798N00626268EA0211902151
    +B1622274549805N00626275EA0212002152
    +B1622294549812N00626282EA0212202154
    +B1622314549819N00626289EA0212402156
    +B1622334549825N00626298EA0212702158
    +B1622354549831N00626307EA0213002161
    +B1622374549837N00626317EA0213302164
    +B1622394549843N00626328EA0213602167
    +B1622414549849N00626338EA0213802169
    +B1622434549855N00626348EA0214002172
    +B1622454549862N00626358EA0214302174
    +B1622474549867N00626369EA0214602177
    +B1622494549873N00626379EA0215102182
    +B1622514549879N00626390EA0215502185
    +B1622534549884N00626403EA0215802189
    +B1622554549891N00626414EA0216102192
    +B1622574549898N00626418EA0216502196
    +B1622594549905N00626411EA0216602199
    +B1623014549903N00626396EA0216902202
    +B1623034549892N00626390EA0217302206
    +B1623054549878N00626392EA0217402208
    +B1623074549867N00626392EA0217602209
    +B1623094549857N00626384EA0217602210
    +B1623114549852N00626374EA0217802211
    +B1623134549854N00626366EA0217702212
    +B1623154549864N00626368EA0217602212
    +B1623174549873N00626381EA0217702212
    +B1623194549879N00626398EA0217902214
    +B1623214549884N00626412EA0218102216
    +B1623234549889N00626425EA0218202217
    +B1623254549894N00626439EA0218602219
    +B1623274549898N00626455EA0219102223
    +B1623294549902N00626470EA0219602227
    +B1623314549909N00626480EA0219702230
    +B1623334549919N00626479EA0219802232
    +B1623354549920N00626468EA0219702232
    +B1623374549911N00626459EA0219502233
    +B1623394549899N00626454EA0219502233
    +B1623414549891N00626444EA0219502231
    +B1623434549889N00626431EA0219702232
    +B1623454549892N00626424EA0219702232
    +B1623474549897N00626420EA0219602231
    +B1623494549907N00626424EA0219302228
    +B1623514549915N00626443EA0219102226
    +B1623534549914N00626465EA0219202226
    +B1623554549911N00626483EA0219202226
    +B1623574549911N00626502EA0219002224
    +B1623594549913N00626523EA0218802222
    +B1624014549916N00626544EA0219102222
    +B1624034549919N00626563EA0219602224
    +B1624054549922N00626580EA0219902226
    +B1624074549926N00626598EA0220002228
    +B1624094549933N00626614EA0220202230
    +B1624114549941N00626622EA0220402232
    +B1624134549952N00626619EA0220302232
    +B1624154549952N00626608EA0220402234
    +B1624174549942N00626601EA0220502235
    +B1624194549931N00626598EA0220602238
    +B1624214549922N00626593EA0220802239
    +B1624234549916N00626587EA0221102241
    +B1624254549910N00626581EA0221202243
    +B1624274549904N00626574EA0221302244
    +B1624294549899N00626565EA0221502246
    +B1624314549896N00626556EA0221802249
    +B1624334549892N00626548EA0222002251
    +B1624354549888N00626539EA0222102253
    +B1624374549889N00626531EA0221902253
    +B1624394549901N00626533EA0221602252
    +B1624414549907N00626555EA0221602251
    +B1624434549906N00626578EA0222102253
    +B1624454549905N00626596EA0222502255
    +B1624474549907N00626612EA0222702258
    +B1624494549910N00626627EA0222802259
    +B1624514549917N00626641EA0222702259
    +B1624534549928N00626642EA0222602258
    +B1624554549933N00626632EA0222502257
    +B1624574549926N00626620EA0222602257
    +B1624594549916N00626613EA0223102259
    +B1625014549907N00626607EA0223402263
    +B1625034549900N00626599EA0223602266
    +B1625054549893N00626592EA0223702267
    +B1625074549886N00626583EA0224002270
    +B1625094549880N00626575EA0224202272
    +B1625114549875N00626566EA0224302273
    +B1625134549870N00626556EA0224302274
    +B1625154549867N00626547EA0224302275
    +B1625174549870N00626538EA0224202274
    +B1625194549880N00626541EA0224202274
    +B1625214549887N00626563EA0224502274
    +B1625234549887N00626585EA0225102278
    +B1625254549889N00626600EA0225302281
    +B1625274549893N00626616EA0225502283
    +B1625294549896N00626633EA0225802286
    +B1625314549901N00626646EA0226002290
    +B1625334549910N00626649EA0226002292
    +B1625354549916N00626642EA0226102293
    +B1625374549915N00626631EA0226302296
    +B1625394549906N00626622EA0226502298
    +B1625414549897N00626615EA0226802300
    +B1625434549890N00626606EA0227102303
    +B1625454549883N00626597EA0227402307
    +B1625474549875N00626590EA0227702310
    +B1625494549870N00626582EA0227702311
    +B1625514549869N00626575EA0227602311
    +B1625534549878N00626576EA0227502310
    +B1625554549887N00626589EA0227502310
    +B1625574549894N00626606EA0227702311
    +B1625594549901N00626618EA0227802312
    +B1626014549909N00626628EA0228002313
    +B1626034549917N00626637EA0228202315
    +B1626054549925N00626642EA0228302316
    +B1626074549934N00626647EA0228402317
    +B1626094549943N00626652EA0228502318
    +B1626114549950N00626659EA0228402318
    +B1626134549958N00626667EA0228302317
    +B1626154549966N00626674EA0228202316
    +B1626174549975N00626684EA0228102314
    +B1626194549984N00626693EA0228202314
    +B1626214549994N00626702EA0228202314
    +B1626234550003N00626710EA0228202314
    +B1626254550012N00626718EA0228102313
    +B1626274550022N00626725EA0228102312
    +B1626294550032N00626732EA0228202313
    +B1626314550041N00626737EA0228402314
    +B1626334550049N00626740EA0228502315
    +B1626354550057N00626743EA0228502316
    +B1626374550066N00626746EA0228402316
    +B1626394550075N00626750EA0228102314
    +B1626414550084N00626754EA0227902312
    +B1626434550093N00626758EA0227602309
    +B1626454550103N00626761EA0227302306
    +B1626474550113N00626764EA0226902302
    +B1626494550123N00626764EA0226702299
    +B1626514550134N00626765EA0226602297
    +B1626534550144N00626769EA0226602297
    +B1626554550152N00626772EA0226502296
    +B1626574550162N00626776EA0226102293
    +B1626594550174N00626780EA0225902291
    +B1627014550184N00626784EA0225902290
    +B1627034550193N00626789EA0225702289
    +B1627054550202N00626795EA0225402287
    +B1627074550213N00626798EA0225102284
    +B1627094550223N00626800EA0224902282
    +B1627114550232N00626800EA0224602279
    +B1627134550242N00626802EA0224302276
    +B1627154550252N00626805EA0224002273
    +B1627174550261N00626806EA0223602269
    +B1627194550271N00626807EA0223202265
    +B1627214550281N00626807EA0222802261
    +B1627234550290N00626809EA0222302256
    +B1627254550301N00626814EA0221702250
    +B1627274550311N00626822EA0221302246
    +B1627294550320N00626832EA0221102242
    +B1627314550328N00626843EA0220702239
    +B1627334550336N00626852EA0220402235
    +B1627354550345N00626858EA0220102232
    +B1627374550352N00626865EA0219702229
    +B1627394550357N00626878EA0219302225
    +B1627414550356N00626895EA0218902221
    +B1627434550351N00626913EA0218702218
    +B1627454550343N00626929EA0218602216
    +B1627474550334N00626943EA0218502214
    +B1627494550324N00626956EA0218402213
    +B1627514550312N00626964EA0218302211
    +B1627534550299N00626971EA0218202210
    +B1627554550287N00626976EA0218102209
    +B1627574550275N00626982EA0218002209
    +B1627594550263N00626987EA0217802207
    +B1628014550251N00626989EA0217602205
    +B1628034550239N00626986EA0217402204
    +B1628054550229N00626982EA0217302203
    +B1628074550219N00626976EA0217102202
    +B1628094550210N00626968EA0217102201
    +B1628114550201N00626960EA0217302201
    +B1628134550193N00626952EA0217702204
    +B1628154550187N00626945EA0218002207
    +B1628174550179N00626939EA0218202210
    +B1628194550170N00626934EA0218402212
    +B1628214550163N00626928EA0218602214
    +B1628234550161N00626919EA0218602216
    +B1628254550170N00626915EA0218602216
    +B1628274550179N00626927EA0218402216
    +B1628294550185N00626941EA0218402216
    +B1628314550192N00626948EA0218202216
    +B1628334550199N00626946EA0217802214
    +B1628354550201N00626933EA0217502211
    +B1628374550194N00626921EA0217302209
    +B1628394550184N00626913EA0217202207
    +B1628414550172N00626906EA0217202206
    +B1628434550162N00626898EA0217302207
    +B1628454550152N00626888EA0217202206
    +B1628474550142N00626878EA0217002204
    +B1628494550134N00626868EA0216902203
    +B1628514550128N00626857EA0216802203
    +B1628534550122N00626847EA0216702202
    +B1628554550117N00626837EA0216502201
    +B1628574550111N00626825EA0216402199
    +B1628594550105N00626813EA0216302198
    +B1629014550099N00626802EA0216302198
    +B1629034550094N00626792EA0216402199
    +B1629054550088N00626783EA0216602201
    +B1629074550081N00626776EA0216802203
    +B1629094550075N00626771EA0216902204
    +B1629114550069N00626763EA0217102206
    +B1629134550062N00626756EA0217302207
    +B1629154550054N00626750EA0217402210
    +B1629174550046N00626744EA0217502211
    +B1629194550039N00626738EA0217702212
    +B1629214550032N00626729EA0218002214
    +B1629234550025N00626722EA0218402218
    +B1629254550019N00626716EA0218802222
    +B1629274550017N00626707EA0219202226
    +B1629294550017N00626699EA0219502230
    +B1629314550019N00626692EA0219702233
    +B1629334550028N00626691EA0219702235
    +B1629354550040N00626704EA0220102237
    +B1629374550042N00626722EA0220502241
    +B1629394550042N00626739EA0220602243
    +B1629414550050N00626750EA0220402244
    +B1629434550061N00626747EA0220202243
    +B1629454550063N00626734EA0220102243
    +B1629474550050N00626725EA0220102243
    +B1629494550037N00626724EA0220402244
    +B1629514550027N00626724EA0220802247
    +B1629534550018N00626722EA0220902248
    +B1629554550010N00626719EA0220902248
    +B1629574550002N00626715EA0221002248
    +B1629594549994N00626711EA0221202250
    +B1630014549986N00626708EA0221402251
    +B1630034549977N00626707EA0221502253
    +B1630054549967N00626705EA0221602254
    +B1630074549958N00626702EA0221702255
    +B1630094549949N00626698EA0221902256
    +B1630114549941N00626691EA0222002257
    +B1630134549932N00626684EA0222202259
    +B1630154549924N00626676EA0222702262
    +B1630174549917N00626668EA0223002266
    +B1630194549912N00626661EA0223202267
    +B1630214549911N00626652EA0223102268
    +B1630234549920N00626648EA0223102269
    +B1630254549930N00626660EA0223102269
    +B1630274549937N00626675EA0223202271
    +B1630294549944N00626686EA0223402272
    +B1630314549951N00626695EA0223502272
    +B1630334549958N00626702EA0223402272
    +B1630354549966N00626708EA0223402271
    +B1630374549974N00626714EA0223402271
    +B1630394549982N00626718EA0223402270
    +B1630414549990N00626722EA0223402271
    +B1630434549998N00626726EA0223402270
    +B1630454550006N00626729EA0223302270
    +B1630474550013N00626731EA0223102268
    +B1630494550017N00626727EA0222802265
    +B1630514550014N00626715EA0222502262
    +B1630534550004N00626708EA0222502260
    +B1630554549992N00626708EA0222702260
    +B1630574549980N00626710EA0222702260
    +B1630594549970N00626709EA0222802261
    +B1631014549960N00626707EA0222902263
    +B1631034549951N00626705EA0222802263
    +B1631054549943N00626700EA0222802263
    +B1631074549935N00626692EA0222802264
    +B1631094549930N00626683EA0222702263
    +B1631114549925N00626672EA0222602262
    +B1631134549921N00626662EA0222502262
    +B1631154549917N00626651EA0222502261
    +B1631174549912N00626640EA0222502261
    +B1631194549908N00626629EA0222702262
    +B1631214549904N00626619EA0222602262
    +B1631234549901N00626607EA0222602261
    +B1631254549897N00626594EA0222502260
    +B1631274549893N00626579EA0222502260
    +B1631294549887N00626566EA0222602261
    +B1631314549882N00626554EA0222702262
    +B1631334549877N00626540EA0222802263
    +B1631354549872N00626528EA0223002265
    +B1631374549865N00626518EA0223202266
    +B1631394549855N00626510EA0223302268
    +B1631414549849N00626499EA0223402269
    +B1631434549853N00626491EA0223602271
    +B1631454549859N00626492EA0223802273
    +B1631474549866N00626500EA0223902275
    +B1631494549869N00626514EA0224102276
    +B1631514549871N00626531EA0224302279
    +B1631534549874N00626546EA0224602281
    +B1631554549882N00626548EA0224702283
    +B1631574549888N00626542EA0225102286
    +B1631594549889N00626531EA0225502291
    +B1632014549890N00626520EA0225902295
    +B1632034549894N00626513EA0226102297
    +B1632054549890N00626503EA0226002299
    +B1632074549877N00626503EA0225802301
    +B1632094549865N00626520EA0225702301
    +B1632114549867N00626542EA0225602300
    +B1632134549871N00626563EA0226102303
    +B1632154549875N00626578EA0226602307
    +B1632174549880N00626591EA0226802309
    +B1632194549885N00626603EA0226902309
    +B1632214549893N00626606EA0226702309
    +B1632234549897N00626597EA0226502306
    +B1632254549896N00626584EA0226502305
    +B1632274549887N00626573EA0226602305
    +B1632294549880N00626563EA0226702305
    +B1632314549876N00626551EA0226702305
    +B1632334549872N00626540EA0226902305
    +B1632354549865N00626531EA0227002306
    +B1632374549860N00626521EA0227002306
    +B1632394549854N00626511EA0227202307
    +B1632414549846N00626504EA0227102307
    +B1632434549837N00626499EA0226802306
    +B1632454549830N00626489EA0226202303
    +B1632474549829N00626477EA0225802299
    +B1632494549839N00626474EA0225602296
    +B1632514549847N00626482EA0225502293
    +B1632534549853N00626497EA0225602293
    +B1632554549858N00626513EA0225702293
    +B1632574549862N00626530EA0226002294
    +B1632594549865N00626544EA0226402297
    +B1633014549869N00626554EA0226702299
    +B1633034549875N00626561EA0226802301
    +B1633054549881N00626555EA0226702301
    +B1633074549879N00626544EA0226902302
    +B1633094549870N00626537EA0227102304
    +B1633114549859N00626533EA0227402306
    +B1633134549850N00626526EA0227702310
    +B1633154549844N00626518EA0227602311
    +B1633174549842N00626508EA0227202311
    +B1633194549851N00626508EA0226902308
    +B1633214549860N00626523EA0226802307
    +B1633234549863N00626540EA0226902307
    +B1633254549865N00626555EA0226802305
    +B1633274549870N00626569EA0226702303
    +B1633294549875N00626583EA0226702303
    +B1633314549882N00626597EA0226802303
    +B1633334549889N00626605EA0227002304
    +B1633354549895N00626600EA0227002304
    +B1633374549895N00626588EA0227002305
    +B1633394549893N00626577EA0227102305
    +B1633414549891N00626566EA0227102305
    +B1633434549887N00626555EA0227102305
    +B1633454549883N00626543EA0227202306
    +B1633474549878N00626532EA0227102307
    +B1633494549872N00626522EA0227002306
    +B1633514549861N00626514EA0227002306
    +B1633534549850N00626508EA0227002305
    +B1633554549840N00626502EA0226902305
    +B1633574549829N00626495EA0226802303
    +B1633594549824N00626485EA0226502301
    +B1634014549824N00626474EA0226302299
    +B1634034549830N00626469EA0226302298
    +B1634054549839N00626477EA0226302298
    +B1634074549845N00626490EA0226202297
    +B1634094549850N00626507EA0226202297
    +B1634114549855N00626523EA0226102296
    +B1634134549860N00626538EA0226102296
    +B1634154549865N00626554EA0226002295
    +B1634174549870N00626568EA0225902295
    +B1634194549876N00626582EA0226002295
    +B1634214549882N00626596EA0226302296
    +B1634234549886N00626609EA0226702300
    +B1634254549890N00626619EA0226802302
    +B1634274549897N00626625EA0226702302
    +B1634294549906N00626621EA0226402301
    +B1634314549906N00626607EA0226402300
    +B1634334549896N00626596EA0226502300
    +B1634354549885N00626590EA0226802302
    +B1634374549874N00626586EA0227002303
    +B1634394549864N00626581EA0226902303
    +B1634414549857N00626572EA0226802303
    +B1634434549856N00626561EA0226802302
    +B1634454549863N00626559EA0226702301
    +B1634474549871N00626569EA0226702301
    +B1634494549877N00626585EA0226902303
    +B1634514549880N00626601EA0227102305
    +B1634534549884N00626616EA0227402308
    +B1634554549890N00626631EA0227702311
    +B1634574549895N00626645EA0228002314
    +B1634594549902N00626654EA0228002316
    +B1635014549909N00626651EA0227702316
    +B1635034549909N00626637EA0227602316
    +B1635054549899N00626627EA0227702316
    +B1635074549887N00626624EA0228102318
    +B1635094549876N00626623EA0228102319
    +B1635114549868N00626619EA0227802319
    +B1635134549863N00626609EA0227702317
    +B1635154549864N00626601EA0227802316
    +B1635174549870N00626599EA0227802316
    +B1635194549879N00626604EA0227902316
    +B1635214549888N00626612EA0228102317
    +B1635234549895N00626620EA0228302319
    +B1635254549902N00626623EA0228302320
    +B1635274549907N00626619EA0228302319
    +B1635294549910N00626609EA0228302319
    +B1635314549913N00626599EA0228202319
    +B1635334549916N00626590EA0228202318
    +B1635354549918N00626579EA0228102316
    +B1635374549921N00626568EA0228002316
    +B1635394549925N00626556EA0228002315
    +B1635414549927N00626544EA0227802314
    +B1635434549925N00626532EA0227702313
    +B1635454549913N00626525EA0227302311
    +B1635474549898N00626529EA0227102310
    +B1635494549887N00626543EA0227002308
    +B1635514549881N00626562EA0226802306
    +B1635534549880N00626583EA0226802305
    +B1635554549884N00626603EA0226902305
    +B1635574549890N00626619EA0227102307
    +B1635594549897N00626631EA0227302308
    +B1636014549905N00626640EA0227302309
    +B1636034549913N00626648EA0227402310
    +B1636054549921N00626657EA0227502311
    +B1636074549928N00626665EA0227702313
    +B1636094549934N00626672EA0227702314
    +B1636114549940N00626678EA0227602313
    +B1636134549947N00626690EA0227502312
    +B1636154549953N00626709EA0227402311
    +B1636174549955N00626730EA0227302310
    +B1636194549957N00626752EA0227202308
    +B1636214549957N00626776EA0226902306
    +B1636234549956N00626802EA0226802304
    +B1636254549956N00626828EA0226702302
    +B1636274549955N00626854EA0226802302
    +B1636294549954N00626880EA0226802302
    +B1636314549952N00626907EA0226802301
    +B1636334549949N00626935EA0226602300
    +B1636354549946N00626961EA0226202298
    +B1636374549941N00626987EA0225402295
    +B1636394549934N00627014EA0225002292
    +B1636414549929N00627035EA0224602287
    +B1636434549926N00627056EA0223502282
    +B1636454549923N00627081EA0222402275
    +B1636474549920N00627105EA0221702268
    +B1636494549919N00627126EA0220802259
    +B1636514549917N00627147EA0219802247
    +B1636534549918N00627169EA0219002237
    +B1636554549920N00627192EA0218202228
    +B1636574549920N00627213EA0217402219
    +B1636594549921N00627233EA0216802211
    +B1637014549923N00627252EA0216102203
    +B1637034549925N00627271EA0215302193
    +B1637054549927N00627292EA0214802186
    +B1637074549930N00627309EA0214502179
    +B1637094549933N00627325EA0213802171
    +B1637114549937N00627345EA0213402164
    +B1637134549941N00627365EA0213102159
    +B1637154549944N00627384EA0212602154
    +B1637174549946N00627403EA0212102150
    +B1637194549947N00627420EA0211602145
    +B1637214549947N00627439EA0211102139
    +B1637234549947N00627458EA0211002135
    +B1637254549947N00627474EA0210802133
    +B1637274549948N00627490EA0210502129
    +B1637294549949N00627509EA0210202127
    +B1637314549950N00627526EA0210202126
    +B1637334549954N00627542EA0210002125
    +B1637354549957N00627558EA0209602122
    +B1637374549961N00627576EA0209402120
    +B1637394549964N00627592EA0209102117
    +B1637414549967N00627609EA0208702114
    +B1637434549970N00627626EA0208302111
    +B1637454549973N00627641EA0208002108
    +B1637474549977N00627656EA0207502103
    +B1637494549982N00627671EA0207202100
    +B1637514549986N00627685EA0206902097
    +B1637534549989N00627699EA0206502093
    +B1637554549992N00627715EA0206002089
    +B1637574549997N00627732EA0205702086
    +B1637594550000N00627748EA0205502083
    +B1638014550002N00627765EA0205102080
    +B1638034550007N00627780EA0204802076
    +B1638054550011N00627796EA0204302072
    +B1638074550014N00627813EA0204002068
    +B1638094550018N00627830EA0203902065
    +B1638114550022N00627844EA0203702063
    +B1638134550024N00627858EA0203402060
    +B1638154550027N00627873EA0203102057
    +B1638174550032N00627887EA0202902055
    +B1638194550036N00627899EA0202502052
    +B1638214550041N00627913EA0202102048
    +B1638234550045N00627929EA0201902046
    +B1638254550047N00627945EA0201802044
    +B1638274550050N00627959EA0201502042
    +B1638294550054N00627974EA0201102038
    +B1638314550058N00627990EA0201002037
    +B1638334550062N00628005EA0201002036
    +B1638354550063N00628021EA0200702034
    +B1638374550066N00628037EA0200402032
    +B1638394550071N00628054EA0200402031
    +B1638414550073N00628069EA0200302030
    +B1638434550074N00628085EA0199902028
    +B1638454550076N00628102EA0199702026
    +B1638474550079N00628118EA0199702025
    +B1638494550080N00628132EA0199702024
    +B1638514550083N00628147EA0199402021
    +B1638534550086N00628163EA0199102019
    +B1638554550087N00628181EA0198902017
    +B1638574550089N00628199EA0198502013
    +B1638594550091N00628217EA0198202011
    +B1639014550093N00628234EA0198002009
    +B1639034550095N00628250EA0197802006
    +B1639054550096N00628267EA0197402003
    +B1639074550098N00628285EA0197202000
    +B1639094550101N00628302EA0197001998
    +B1639114550103N00628318EA0196701996
    +B1639134550106N00628336EA0196501993
    +B1639154550108N00628354EA0196301991
    +B1639174550110N00628370EA0196001988
    +B1639194550111N00628388EA0195601984
    +B1639214550113N00628407EA0195201981
    +B1639234550115N00628425EA0195001978
    +B1639254550118N00628443EA0194701976
    +B1639274550120N00628461EA0194401973
    +B1639294550120N00628481EA0194301971
    +B1639314550121N00628500EA0194101969
    +B1639334550123N00628519EA0193901967
    +B1639354550125N00628539EA0193701965
    +B1639374550129N00628557EA0193801965
    +B1639394550134N00628573EA0193901966
    +B1639414550139N00628589EA0194001968
    +B1639434550145N00628604EA0194101969
    +B1639454550151N00628618EA0194301970
    +B1639474550156N00628633EA0194401972
    +B1639494550161N00628647EA0194601974
    +B1639514550168N00628657EA0194601975
    +B1639534550178N00628653EA0194301975
    +B1639554550184N00628642EA0194301974
    +B1639574550182N00628630EA0194501975
    +B1639594550172N00628622EA0194501976
    +B1640014550160N00628626EA0194601977
    +B1640034550151N00628638EA0194701978
    +B1640054550146N00628656EA0194901980
    +B1640074550147N00628674EA0195001981
    +B1640094550154N00628689EA0195101982
    +B1640114550165N00628695EA0195201983
    +B1640134550175N00628689EA0195201984
    +B1640154550179N00628678EA0195201984
    +B1640174550174N00628666EA0195301984
    +B1640194550162N00628664EA0195301984
    +B1640214550150N00628675EA0195301984
    +B1640234550143N00628691EA0195501985
    +B1640254550138N00628708EA0195501985
    +B1640274550134N00628727EA0195501985
    +B1640294550130N00628749EA0195801987
    +B1640314550128N00628768EA0196001989
    +B1640334550127N00628783EA0196001989
    +B1640354550125N00628801EA0196001990
    +B1640374550122N00628817EA0196301992
    +B1640394550117N00628829EA0196301994
    +B1640414550113N00628841EA0196401994
    +B1640434550108N00628855EA0196401995
    +B1640454550105N00628868EA0196701997
    +B1640474550104N00628882EA0196801998
    +B1640494550113N00628891EA0196701998
    +B1640514550126N00628890EA0196701998
    +B1640534550134N00628877EA0196801999
    +B1640554550132N00628862EA0196902000
    +B1640574550122N00628854EA0196902000
    +B1640594550111N00628860EA0197002000
    +B1641014550104N00628872EA0197302002
    +B1641034550102N00628885EA0197502005
    +B1641054550102N00628897EA0197502006
    +B1641074550105N00628911EA0197402005
    +B1641094550116N00628919EA0197402004
    +B1641114550129N00628916EA0197502004
    +B1641134550138N00628904EA0197602006
    +B1641154550140N00628889EA0197702007
    +B1641174550135N00628876EA0197702007
    +B1641194550123N00628873EA0197702007
    +B1641214550112N00628882EA0197802009
    +B1641234550109N00628898EA0198002010
    +B1641254550112N00628913EA0198002011
    +B1641274550122N00628923EA0198102011
    +B1641294550135N00628921EA0198202012
    +B1641314550144N00628909EA0198202013
    +B1641334550146N00628894EA0198402014
    +B1641354550138N00628882EA0198602016
    +B1641374550127N00628883EA0198802018
    +B1641394550120N00628895EA0199002020
    +B1641414550121N00628911EA0199002022
    +B1641434550130N00628923EA0199002022
    +B1641454550144N00628925EA0199102023
    +B1641474550155N00628917EA0199202024
    +B1641494550160N00628903EA0199402026
    +B1641514550160N00628889EA0199502027
    +B1641534550156N00628875EA0199502028
    +B1641554550147N00628866EA0199502029
    +B1641574550134N00628868EA0199602029
    +B1641594550123N00628876EA0199702029
    +B1642014550114N00628888EA0200002032
    +B1642034550110N00628901EA0200302035
    +B1642054550107N00628913EA0200402036
    +B1642074550107N00628927EA0200302036
    +B1642094550114N00628942EA0200202036
    +B1642114550129N00628946EA0200202036
    +B1642134550139N00628937EA0200202036
    +B1642154550143N00628923EA0200302036
    +B1642174550139N00628910EA0200502038
    +B1642194550129N00628902EA0200702040
    +B1642214550119N00628900EA0200902042
    +B1642234550109N00628901EA0201102045
    +B1642254550100N00628904EA0201302047
    +B1642274550092N00628908EA0201202048
    +B1642294550086N00628919EA0201102047
    +B1642314550084N00628935EA0201102047
    +B1642334550094N00628948EA0200902045
    +B1642354550105N00628951EA0200802043
    +B1642374550116N00628943EA0200702041
    +B1642394550120N00628928EA0200702041
    +B1642414550113N00628916EA0200802041
    +B1642434550104N00628910EA0201002043
    +B1642454550096N00628908EA0201102044
    +B1642474550086N00628910EA0201102044
    +B1642494550078N00628920EA0201202044
    +B1642514550074N00628934EA0201102044
    +B1642534550073N00628949EA0201002043
    +B1642554550071N00628966EA0200902042
    +B1642574550069N00628984EA0201002043
    +B1642594550067N00628999EA0200902043
    +B1643014550064N00629015EA0200602041
    +B1643034550062N00629032EA0200502039
    +B1643054550060N00629049EA0200402039
    +B1643074550057N00629065EA0200202036
    +B1643094550053N00629082EA0200102034
    +B1643114550049N00629099EA0200102034
    +B1643134550045N00629113EA0200002033
    +B1643154550041N00629128EA0199702030
    +B1643174550038N00629145EA0199502028
    +B1643194550036N00629160EA0199402027
    +B1643214550034N00629175EA0199202025
    +B1643234550032N00629191EA0199102023
    +B1643254550030N00629208EA0199002022
    +B1643274550027N00629225EA0199102022
    +B1643294550024N00629241EA0199102022
    +B1643314550020N00629257EA0199002021
    +B1643334550016N00629274EA0199202022
    +B1643354550014N00629290EA0199702025
    +B1643374550012N00629303EA0200002028
    +B1643394550008N00629314EA0200202031
    +B1643414550002N00629320EA0200102032
    +B1643434549993N00629310EA0199802030
    +B1643454549990N00629292EA0199702029
    +B1643474549997N00629276EA0199702029
    +B1643494550010N00629272EA0199802029
    +B1643514550022N00629280EA0199802029
    +B1643534550030N00629294EA0200002030
    +B1643554550034N00629310EA0200402033
    +B1643574550037N00629324EA0200702037
    +B1643594550040N00629337EA0200902039
    +B1644014550041N00629352EA0201102041
    +B1644034550042N00629369EA0201402044
    +B1644054550042N00629383EA0201702047
    +B1644074550048N00629394EA0201702049
    +B1644094550061N00629396EA0201702050
    +B1644114550070N00629386EA0201802051
    +B1644134550073N00629370EA0201702051
    +B1644154550068N00629356EA0201802051
    +B1644174550058N00629352EA0201802051
    +B1644194550047N00629359EA0201902052
    +B1644214550040N00629371EA0202302054
    +B1644234550035N00629385EA0202702058
    +B1644254550030N00629398EA0203002061
    +B1644274550026N00629410EA0203302064
    +B1644294550021N00629422EA0203602067
    +B1644314550016N00629432EA0204002071
    +B1644334550011N00629442EA0204302074
    +B1644354550007N00629454EA0204702078
    +B1644374550004N00629464EA0205002082
    +B1644394550003N00629475EA0205202085
    +B1644414550007N00629488EA0205302086
    +B1644434550019N00629494EA0205502088
    +B1644454550030N00629486EA0205602089
    +B1644474550035N00629472EA0205802091
    +B1644494550031N00629457EA0205902092
    +B1644514550024N00629446EA0206202095
    +B1644534550016N00629438EA0206502098
    +B1644554550007N00629434EA0206802101
    +B1644574549996N00629434EA0207202104
    +B1644594549986N00629434EA0207602108
    +B1645014549976N00629435EA0207902112
    +B1645034549967N00629439EA0208302116
    +B1645054549961N00629454EA0208502118
    +B1645074549962N00629470EA0208602120
    +B1645094549971N00629483EA0208802122
    +B1645114549984N00629487EA0209102125
    +B1645134549993N00629477EA0209202127
    +B1645154549992N00629463EA0209402128
    +B1645174549982N00629455EA0209602130
    +B1645194549972N00629458EA0209802132
    +B1645214549963N00629464EA0210002134
    +B1645234549957N00629474EA0210102135
    +B1645254549953N00629488EA0210102135
    +B1645274549957N00629504EA0210202136
    +B1645294549969N00629516EA0210402138
    +B1645314549983N00629515EA0210602140
    +B1645334549991N00629504EA0210702141
    +B1645354549992N00629490EA0210802143
    +B1645374549988N00629480EA0210902143
    +B1645394549981N00629473EA0211002145
    +B1645414549974N00629471EA0211102145
    +B1645434549965N00629472EA0211202146
    +B1645454549956N00629476EA0211202147
    +B1645474549948N00629482EA0211102146
    +B1645494549940N00629492EA0210802144
    +B1645514549935N00629508EA0210902143
    +B1645534549930N00629528EA0211302144
    +B1645554549927N00629545EA0211402146
    +B1645574549925N00629560EA0211102145
    +B1645594549923N00629578EA0210702143
    +B1646014549919N00629597EA0210502141
    +B1646034549916N00629614EA0210502141
    +B1646054549912N00629630EA0210302139
    +B1646074549908N00629649EA0210102136
    +B1646094549905N00629669EA0210202136
    +B1646114549902N00629687EA0210102135
    +B1646134549899N00629706EA0209902133
    +B1646154549896N00629724EA0209902132
    +B1646174549894N00629743EA0209802131
    +B1646194549892N00629763EA0209802130
    +B1646214549888N00629783EA0209902131
    +B1646234549885N00629802EA0210102132
    +B1646254549880N00629817EA0210002133
    +B1646274549875N00629833EA0209802132
    +B1646294549871N00629853EA0209602130
    +B1646314549866N00629870EA0209502129
    +B1646334549860N00629885EA0209202127
    +B1646354549855N00629901EA0208702123
    +B1646374549850N00629919EA0208502120
    +B1646394549847N00629936EA0208002116
    +B1646414549843N00629953EA0207602111
    +B1646434549839N00629971EA0207402107
    +B1646454549834N00629987EA0207202105
    +B1646474549830N00630002EA0206802101
    +B1646494549828N00630020EA0206402097
    +B1646514549824N00630037EA0206002093
    +B1646534549821N00630054EA0205702090
    +B1646554549818N00630072EA0205402086
    +B1646574549815N00630089EA0205102083
    +B1646594549813N00630107EA0204802079
    +B1647014549810N00630125EA0204302075
    +B1647034549807N00630143EA0204102072
    +B1647054549804N00630160EA0203802069
    +B1647074549800N00630177EA0203402066
    +B1647094549797N00630194EA0203102062
    +B1647114549794N00630211EA0202902060
    +B1647134549791N00630227EA0202702058
    +B1647154549789N00630243EA0202402054
    +B1647174549786N00630260EA0202202052
    +B1647194549784N00630277EA0202102051
    +B1647214549782N00630293EA0202002049
    +B1647234549779N00630310EA0201902048
    +B1647254549776N00630326EA0201902048
    +B1647274549772N00630343EA0201802047
    +B1647294549769N00630359EA0201602045
    +B1647314549766N00630376EA0201402044
    +B1647334549763N00630394EA0201402043
    +B1647354549760N00630410EA0201202042
    +B1647374549756N00630427EA0201102040
    +B1647394549751N00630444EA0201002039
    +B1647414549746N00630461EA0201002039
    +B1647434549742N00630476EA0200902038
    +B1647454549738N00630492EA0200802037
    +B1647474549734N00630509EA0200602035
    +B1647494549731N00630527EA0200502035
    +B1647514549728N00630545EA0200502034
    +B1647534549725N00630561EA0200402033
    +B1647554549720N00630579EA0200202031
    +B1647574549715N00630597EA0200102031
    +B1647594549712N00630614EA0200102030
    +B1648014549708N00630632EA0200002029
    +B1648034549704N00630649EA0200102029
    +B1648054549701N00630665EA0200002029
    +B1648074549698N00630681EA0199802027
    +B1648094549695N00630700EA0199502025
    +B1648114549692N00630718EA0199502025
    +B1648134549689N00630734EA0199502024
    +B1648154549686N00630751EA0199302022
    +B1648174549683N00630768EA0199202021
    +B1648194549681N00630786EA0199102020
    +B1648214549680N00630804EA0199002019
    +B1648234549676N00630822EA0198802017
    +B1648254549673N00630841EA0198702016
    +B1648274549670N00630860EA0198602015
    +B1648294549666N00630879EA0198602014
    +B1648314549663N00630896EA0198302012
    +B1648334549659N00630914EA0197802008
    +B1648354549655N00630932EA0197202004
    +B1648374549651N00630950EA0196801999
    +B1648394549646N00630969EA0196401994
    +B1648414549641N00630987EA0196001990
    +B1648434549636N00631005EA0195801987
    +B1648454549633N00631023EA0195501984
    +B1648474549630N00631042EA0195101981
    +B1648494549626N00631061EA0194801977
    +B1648514549621N00631079EA0194401974
    +B1648534549616N00631096EA0194001970
    +B1648554549611N00631113EA0193501964
    +B1648574549607N00631132EA0192901959
    +B1648594549603N00631150EA0192401954
    +B1649014549599N00631167EA0191901948
    +B1649034549595N00631185EA0191301942
    +B1649054549591N00631204EA0190801937
    +B1649074549588N00631223EA0190401932
    +B1649094549584N00631241EA0190001928
    +B1649114549579N00631259EA0189701924
    +B1649134549574N00631277EA0189201920
    +B1649154549568N00631294EA0188901916
    +B1649174549561N00631309EA0188501912
    +B1649194549555N00631326EA0188101908
    +B1649214549549N00631343EA0187601904
    +B1649234549543N00631361EA0187301900
    +B1649254549538N00631379EA0186901896
    +B1649274549533N00631397EA0186601892
    +B1649294549531N00631416EA0186201889
    +B1649314549528N00631434EA0185801885
    +B1649334549525N00631453EA0185401881
    +B1649354549522N00631473EA0185201878
    +B1649374549519N00631489EA0185001875
    +B1649394549516N00631505EA0184601871
    +B1649414549513N00631524EA0184401869
    +B1649434549511N00631542EA0184301867
    +B1649454549509N00631558EA0183901864
    +B1649474549506N00631576EA0183601861
    +B1649494549504N00631594EA0183301859
    +B1649514549501N00631611EA0183101856
    +B1649534549497N00631627EA0182701853
    +B1649554549493N00631645EA0182301849
    +B1649574549489N00631662EA0182001846
    +B1649594549486N00631679EA0181801844
    +B1650014549482N00631695EA0181401840
    +B1650034549478N00631712EA0181001836
    +B1650054549475N00631731EA0180801834
    +B1650074549472N00631748EA0180701832
    +B1650094549469N00631763EA0180301829
    +B1650114549466N00631780EA0180001826
    +B1650134549462N00631797EA0179901824
    +B1650154549459N00631813EA0179701822
    +B1650174549455N00631830EA0179401819
    +B1650194549450N00631847EA0179101817
    +B1650214549445N00631863EA0178701813
    +B1650234549439N00631880EA0178301809
    +B1650254549432N00631898EA0178001805
    +B1650274549426N00631916EA0178001802
    +B1650294549421N00631930EA0177801801
    +B1650314549415N00631945EA0177401797
    +B1650334549408N00631961EA0177101795
    +B1650354549401N00631977EA0177001793
    +B1650374549395N00631991EA0176701790
    +B1650394549389N00632008EA0176301787
    +B1650414549383N00632024EA0176201785
    +B1650434549378N00632040EA0176001784
    +B1650454549372N00632056EA0175701781
    +B1650474549367N00632073EA0175601780
    +B1650494549362N00632089EA0175401778
    +B1650514549356N00632105EA0175201776
    +B1650534549350N00632121EA0175001774
    +B1650554549345N00632138EA0174801772
    +B1650574549339N00632153EA0174501769
    +B1650594549334N00632169EA0174101766
    +B1651014549329N00632186EA0173801762
    +B1651034549323N00632202EA0173501759
    +B1651054549318N00632219EA0173201757
    +B1651074549312N00632235EA0172901754
    +B1651094549307N00632252EA0172501750
    +B1651114549302N00632270EA0172201746
    +B1651134549297N00632289EA0172001743
    +B1651154549292N00632308EA0171801742
    +B1651174549287N00632326EA0171801741
    +B1651194549282N00632343EA0171701740
    +B1651214549276N00632361EA0171501738
    +B1651234549271N00632379EA0171501737
    +B1651254549267N00632397EA0171401737
    +B1651274549262N00632414EA0171301735
    +B1651294549258N00632433EA0171101734
    +B1651314549254N00632451EA0171001733
    +B1651334549250N00632468EA0170801731
    +B1651354549246N00632486EA0170601729
    +B1651374549241N00632503EA0170401728
    +B1651394549237N00632521EA0170301726
    +B1651414549232N00632539EA0170101724
    +B1651434549228N00632556EA0169901722
    +B1651454549224N00632574EA0169701720
    +B1651474549220N00632592EA0169501718
    +B1651494549216N00632610EA0169301716
    +B1651514549211N00632628EA0169001714
    +B1651534549207N00632646EA0168901712
    +B1651554549203N00632663EA0168501709
    +B1651574549199N00632681EA0168201706
    +B1651594549195N00632698EA0168001703
    +B1652014549191N00632716EA0167801701
    +B1652034549186N00632734EA0167501698
    +B1652054549180N00632751EA0167301696
    +B1652074549175N00632767EA0167101693
    +B1652094549170N00632785EA0166701690
    +B1652114549164N00632802EA0166501688
    +B1652134549158N00632818EA0166301686
    +B1652154549152N00632834EA0166101683
    +B1652174549146N00632851EA0165801681
    +B1652194549140N00632868EA0165501678
    +B1652214549135N00632885EA0165301675
    +B1652234549130N00632903EA0165001673
    +B1652254549125N00632921EA0164701670
    +B1652274549120N00632938EA0164301666
    +B1652294549115N00632957EA0164001662
    +B1652314549111N00632976EA0163801660
    +B1652334549106N00632995EA0163701658
    +B1652354549102N00633014EA0163301656
    +B1652374549099N00633035EA0163101653
    +B1652394549094N00633056EA0163001652
    +B1652414549090N00633076EA0162901650
    +B1652434549085N00633096EA0162701649
    +B1652454549080N00633115EA0162601647
    +B1652474549074N00633133EA0162301645
    +B1652494549067N00633151EA0162101642
    +B1652514549061N00633170EA0161801640
    +B1652534549056N00633188EA0161401637
    +B1652554549050N00633206EA0161101633
    +B1652574549044N00633225EA0160801630
    +B1652594549037N00633242EA0160401627
    +B1653014549028N00633259EA0160101623
    +B1653034549020N00633277EA0160001621
    +B1653054549011N00633292EA0159801619
    +B1653074549003N00633306EA0159501616
    +B1653094548994N00633323EA0159301614
    +B1653114548986N00633341EA0159301613
    +B1653134548980N00633358EA0159201611
    +B1653154548975N00633377EA0158901610
    +B1653174548968N00633398EA0159001610
    +B1653194548961N00633415EA0159401612
    +B1653214548954N00633428EA0159601615
    +B1653234548947N00633440EA0159601615
    +B1653254548940N00633454EA0159301615
    +B1653274548931N00633469EA0159101613
    +B1653294548923N00633483EA0159001612
    +B1653314548917N00633500EA0158701610
    +B1653334548915N00633520EA0158501608
    +B1653354548916N00633542EA0158201605
    +B1653374548917N00633564EA0158001603
    +B1653394548919N00633586EA0157801600
    +B1653414548921N00633609EA0157701599
    +B1653434548922N00633630EA0157601598
    +B1653454548924N00633651EA0157501596
    +B1653474548927N00633675EA0157501595
    +B1653494548929N00633697EA0157901596
    +B1653514548930N00633714EA0158201598
    +B1653534548932N00633730EA0158301599
    +B1653554548939N00633735EA0158201600
    +B1653574548947N00633731EA0158301602
    +B1653594548946N00633720EA0158401603
    +B1654014548938N00633709EA0158501604
    +B1654034548925N00633706EA0158701606
    +B1654054548914N00633714EA0158601607
    +B1654074548903N00633730EA0158601607
    +B1654094548897N00633749EA0158901609
    +B1654114548896N00633765EA0159301613
    +B1654134548898N00633778EA0159701617
    +B1654154548901N00633790EA0160101621
    +B1654174548904N00633800EA0160301623
    +B1654194548912N00633798EA0160201625
    +B1654214548914N00633786EA0160401627
    +B1654234548902N00633777EA0160501628
    +B1654254548885N00633780EA0160901631
    +B1654274548874N00633791EA0161401636
    +B1654294548870N00633807EA0161601639
    +B1654314548875N00633821EA0162001642
    +B1654334548882N00633822EA0162101645
    +B1654354548886N00633811EA0162301647
    +B1654374548883N00633797EA0162701650
    +B1654394548870N00633789EA0163001654
    +B1654414548856N00633793EA0163401657
    +B1654434548845N00633808EA0163601660
    +B1654454548842N00633824EA0163901664
    +B1654474548848N00633834EA0164101666
    +B1654494548855N00633835EA0164401669
    +B1654514548859N00633828EA0164701672
    +B1654534548859N00633818EA0165001675
    +B1654554548856N00633807EA0165301679
    +B1654574548851N00633799EA0165301681
    +B1654594548843N00633791EA0165401682
    +B1655014548838N00633781EA0165301682
    +B1655034548844N00633774EA0165301682
    +B1655054548852N00633781EA0165501682
    +B1655074548857N00633795EA0165501682
    +B1655094548858N00633813EA0165501681
    +B1655114548860N00633832EA0165501681
    +B1655134548865N00633847EA0165501680
    +B1655154548871N00633862EA0165501680
    +B1655174548875N00633879EA0165701682
    +B1655194548879N00633893EA0166001684
    +B1655214548885N00633905EA0166301686
    +B1655234548891N00633915EA0166501689
    +B1655254548897N00633926EA0166801692
    +B1655274548901N00633938EA0167001695
    +B1655294548907N00633950EA0167101697
    +B1655314548917N00633949EA0167301698
    +B1655334548924N00633937EA0167701701
    +B1655354548920N00633924EA0168001705
    +B1655374548908N00633920EA0168301707
    +B1655394548896N00633930EA0168701710
    +B1655414548891N00633947EA0169101714
    +B1655434548892N00633962EA0169401718
    +B1655454548899N00633974EA0169701721
    +B1655474548910N00633974EA0170101725
    +B1655494548916N00633964EA0170501729
    +B1655514548917N00633951EA0170801733
    +B1655534548913N00633939EA0171201737
    +B1655554548902N00633934EA0171301740
    +B1655574548890N00633941EA0171401742
    +B1655594548881N00633956EA0171901746
    +B1656014548879N00633972EA0172401750
    +B1656034548885N00633983EA0172701753
    +B1656054548895N00633992EA0173001756
    +B1656074548906N00633996EA0173401760
    +B1656094548912N00633987EA0173701764
    +B1656114548912N00633971EA0174001767
    +B1656134548902N00633962EA0174301770
    +B1656154548890N00633970EA0174601773
    +B1656174548880N00633983EA0175401778
    +B1656194548877N00633998EA0176001784
    +B1656214548879N00634013EA0176301788
    +B1656234548886N00634028EA0176401791
    +B1656254548900N00634035EA0176901795
    +B1656274548909N00634029EA0177301800
    +B1656294548909N00634017EA0177801805
    +B1656314548900N00634008EA0178201809
    +B1656334548888N00634012EA0178601814
    +B1656354548882N00634027EA0179201819
    +B1656374548882N00634044EA0179501824
    +B1656394548887N00634060EA0179901828
    +B1656414548897N00634067EA0180401832
    +B1656434548906N00634063EA0180701836
    +B1656454548909N00634051EA0180901839
    +B1656474548904N00634039EA0181201842
    +B1656494548893N00634035EA0181601845
    +B1656514548880N00634040EA0182101850
    +B1656534548871N00634052EA0182601855
    +B1656554548868N00634069EA0183001859
    +B1656574548868N00634088EA0183401863
    +B1656594548876N00634101EA0183701866
    +B1657014548888N00634101EA0183801869
    +B1657034548896N00634090EA0184101872
    +B1657054548894N00634076EA0184401875
    +B1657074548884N00634066EA0184701878
    +B1657094548871N00634069EA0185101882
    +B1657114548861N00634081EA0185501886
    +B1657134548854N00634097EA0185801889
    +B1657154548852N00634115EA0186001892
    +B1657174548857N00634129EA0186101893
    +B1657194548868N00634137EA0185801892
    +B1657214548878N00634134EA0185501889
    +B1657234548886N00634121EA0185401887
    +B1657254548888N00634107EA0185601887
    +B1657274548886N00634093EA0186001889
    +B1657294548881N00634079EA0186701893
    +B1657314548877N00634067EA0187201899
    +B1657334548871N00634056EA0187401902
    +B1657354548859N00634052EA0187501904
    +B1657374548845N00634058EA0187801907
    +B1657394548840N00634071EA0188001910
    +B1657414548846N00634083EA0188401914
    +B1657434548855N00634082EA0188901919
    +B1657454548862N00634070EA0189301924
    +B1657474548866N00634057EA0189801930
    +B1657494548864N00634044EA0190201934
    +B1657514548855N00634031EA0190601938
    +B1657534548840N00634030EA0190901942
    +B1657554548829N00634042EA0191201945
    +B1657574548828N00634055EA0191301946
    +B1657594548836N00634064EA0191501947
    +B1658014548845N00634065EA0191901950
    +B1658034548851N00634057EA0192201953
    +B1658054548853N00634043EA0192501955
    +B1658074548847N00634029EA0192801957
    +B1658094548836N00634020EA0193101960
    +B1658114548823N00634018EA0193501964
    +B1658134548812N00634025EA0193901967
    +B1658154548805N00634037EA0194101970
    +B1658174548806N00634051EA0194401973
    +B1658194548814N00634060EA0194801977
    +B1658214548822N00634054EA0195201981
    +B1658234548825N00634042EA0195401984
    +B1658254548822N00634028EA0195701986
    +B1658274548812N00634016EA0196001989
    +B1658294548799N00634012EA0196301992
    +B1658314548786N00634015EA0196601995
    +B1658334548777N00634025EA0196901998
    +B1658354548773N00634040EA0197202001
    +B1658374548776N00634052EA0197502004
    +B1658394548784N00634056EA0197802007
    +B1658414548790N00634051EA0198102010
    +B1658434548793N00634038EA0198202012
    +B1658454548789N00634024EA0198502014
    +B1658474548776N00634013EA0198802016
    +B1658494548762N00634012EA0199102020
    +B1658514548750N00634018EA0199502024
    +B1658534548742N00634030EA0199702027
    +B1658554548740N00634043EA0200002029
    +B1658574548744N00634054EA0200102030
    +B1658594548751N00634057EA0200402033
    +B1659014548759N00634049EA0200602035
    +B1659034548762N00634035EA0200802037
    +B1659054548760N00634020EA0201202040
    +B1659074548752N00634008EA0201502042
    +B1659094548736N00634004EA0201902046
    +B1659114548722N00634009EA0202302051
    +B1659134548713N00634020EA0202502056
    +B1659154548709N00634034EA0202702058
    +B1659174548709N00634048EA0202802060
    +B1659194548719N00634058EA0202902062
    +B1659214548730N00634060EA0203402065
    +B1659234548735N00634053EA0203602069
    +B1659254548736N00634039EA0203802071
    +B1659274548730N00634022EA0204102074
    +B1659294548714N00634010EA0204702077
    +B1659314548700N00634012EA0204902081
    +B1659334548689N00634023EA0205302084
    +B1659354548684N00634038EA0205502088
    +B1659374548686N00634052EA0205702090
    +B1659394548694N00634058EA0206002093
    +B1659414548701N00634056EA0206302096
    +B1659434548707N00634048EA0206702100
    +B1659454548710N00634036EA0207102104
    +B1659474548712N00634025EA0207502108
    +B1659494548715N00634013EA0207902112
    +B1659514548715N00634000EA0208302115
    +B1659534548709N00633989EA0208402119
    +B1659554548694N00633988EA0208602122
    +B1659574548683N00633998EA0208902125
    +B1659594548678N00634013EA0209402128
    +B1700014548681N00634026EA0209702131
    +B1700034548687N00634034EA0210102135
    +B1700054548696N00634038EA0210602139
    +B1700074548705N00634035EA0211002144
    +B1700094548711N00634026EA0211402148
    +B1700114548712N00634013EA0211802152
    +B1700134548708N00634000EA0212102156
    +B1700154548700N00633993EA0212402159
    +B1700174548687N00633995EA0212902163
    +B1700194548676N00634006EA0213202167
    +B1700214548667N00634019EA0213502170
    +B1700234548661N00634033EA0213902173
    +B1700254548660N00634050EA0214402177
    +B1700274548663N00634065EA0214902182
    +B1700294548667N00634075EA0215402187
    +B1700314548674N00634079EA0215702191
    +B1700334548682N00634071EA0216002195
    +B1700354548684N00634060EA0216402199
    +B1700374548680N00634048EA0216702202
    +B1700394548668N00634044EA0217102205
    +B1700414548657N00634049EA0217502209
    +B1700434548648N00634062EA0217902214
    +B1700454548644N00634077EA0218302218
    +B1700474548642N00634091EA0218702222
    +B1700494548642N00634101EA0218902225
    +B1700514548647N00634106EA0218802226
    +B1700534548653N00634106EA0218902226
    +B1700554548658N00634100EA0219202227
    +B1700574548661N00634089EA0219602229
    +B1700594548660N00634078EA0220002233
    +B1701014548653N00634068EA0220302236
    +B1701034548642N00634063EA0220602239
    +B1701054548629N00634066EA0220802241
    +B1701074548618N00634075EA0221402245
    +B1701094548608N00634088EA0221902250
    +B1701114548601N00634100EA0222302255
    +B1701134548598N00634111EA0222402258
    +B1701154548602N00634121EA0222502259
    +B1701174548612N00634121EA0222802262
    +B1701194548618N00634113EA0223302265
    +B1701214548620N00634100EA0223802270
    +B1701234548617N00634087EA0224402275
    +B1701254548607N00634078EA0224802280
    +B1701274548593N00634083EA0225202285
    +B1701294548587N00634099EA0225502290
    +B1701314548591N00634112EA0225802294
    +B1701334548600N00634118EA0226302299
    +B1701354548609N00634117EA0226902305
    +B1701374548617N00634112EA0227402311
    +B1701394548621N00634103EA0227902316
    +B1701414548621N00634091EA0228402321
    +B1701434548614N00634081EA0228802325
    +B1701454548599N00634079EA0229102329
    +B1701474548587N00634089EA0229502332
    +B1701494548581N00634104EA0229802336
    +B1701514548581N00634120EA0230102339
    +B1701534548585N00634135EA0230702344
    +B1701554548594N00634141EA0231302350
    +B1701574548604N00634136EA0232002355
    +B1701594548611N00634127EA0232802363
    +B1702014548614N00634116EA0233402370
    +B1702034548609N00634102EA0233802376
    +B1702054548596N00634094EA0234102381
    +B1702074548582N00634101EA0234402384
    +B1702094548574N00634117EA0234902389
    +B1702114548572N00634135EA0235602394
    +B1702134548573N00634151EA0236202400
    +B1702154548578N00634164EA0236902407
    +B1702174548585N00634172EA0237502414
    +B1702194548592N00634174EA0238002418
    +B1702214548600N00634167EA0238602424
    +B1702234548601N00634150EA0239102429
    +B1702254548592N00634140EA0239602434
    +B1702274548579N00634144EA0240102440
    +B1702294548570N00634156EA0240702445
    +B1702314548564N00634172EA0241402452
    +B1702334548562N00634189EA0242002458
    +B1702354548566N00634202EA0242602465
    +B1702374548574N00634204EA0243002469
    +B1702394548582N00634198EA0243502473
    +B1702414548586N00634190EA0244102479
    +B1702434548583N00634178EA0244502484
    +B1702454548574N00634167EA0245002489
    +B1702474548563N00634163EA0245402494
    +B1702494548551N00634167EA0245802498
    +B1702514548539N00634176EA0246402504
    +B1702534548533N00634190EA0246902508
    +B1702554548535N00634205EA0247402513
    +B1702574548540N00634216EA0248002519
    +B1702594548545N00634222EA0248602525
    +B1703014548552N00634219EA0249102531
    +B1703034548556N00634207EA0249702537
    +B1703054548552N00634192EA0250302543
    +B1703074548538N00634187EA0250802548
    +B1703094548524N00634193EA0251402555
    +B1703114548515N00634209EA0251902561
    +B1703134548512N00634226EA0252702568
    +B1703154548512N00634238EA0253402575
    +B1703174548519N00634242EA0253802580
    +B1703194548526N00634235EA0254402585
    +B1703214548527N00634221EA0255202591
    +B1703234548519N00634208EA0255702597
    +B1703254548508N00634202EA0256402603
    +B1703274548494N00634205EA0256902610
    +B1703294548483N00634216EA0257302614
    +B1703314548477N00634234EA0257902619
    +B1703334548476N00634251EA0258402625
    +B1703354548478N00634266EA0258702629
    +B1703374548480N00634281EA0258902633
    +B1703394548483N00634297EA0259102635
    +B1703414548485N00634313EA0259402638
    +B1703434548487N00634329EA0259502639
    +B1703454548489N00634345EA0259602640
    +B1703474548490N00634362EA0259702641
    +B1703494548490N00634378EA0259602641
    +B1703514548491N00634394EA0259302640
    +B1703534548491N00634414EA0259502639
    +B1703554548489N00634434EA0259902639
    +B1703574548488N00634452EA0260202642
    +B1703594548486N00634468EA0260602644
    +B1704014548483N00634487EA0261002647
    +B1704034548477N00634503EA0261402651
    +B1704054548473N00634518EA0261502654
    +B1704074548471N00634535EA0261702655
    +B1704094548472N00634551EA0261902658
    +B1704114548473N00634567EA0262002660
    +B1704134548474N00634583EA0262002661
    +B1704154548476N00634601EA0262202663
    +B1704174548476N00634617EA0262402665
    +B1704194548477N00634632EA0262402665
    +B1704214548476N00634650EA0262302664
    +B1704234548476N00634669EA0262402665
    +B1704254548476N00634687EA0262402666
    +B1704274548477N00634704EA0262402666
    +B1704294548478N00634723EA0262402666
    +B1704314548479N00634741EA0262202665
    +B1704334548481N00634758EA0261902663
    +B1704354548483N00634778EA0261802662
    +B1704374548486N00634796EA0261602660
    +B1704394548490N00634812EA0261202657
    +B1704414548492N00634831EA0261002653
    +B1704434548494N00634852EA0261002652
    +B1704454548495N00634870EA0260802650
    +B1704474548496N00634888EA0260402647
    +B1704494548500N00634908EA0260102643
    +B1704514548505N00634927EA0260102641
    +B1704534548510N00634945EA0260202640
    +B1704554548516N00634962EA0260702641
    +B1704574548523N00634977EA0261202644
    +B1704594548529N00634992EA0261502648
    +B1705014548536N00635007EA0261802650
    +B1705034548545N00635025EA0262002654
    +B1705054548553N00635042EA0262402658
    +B1705074548561N00635058EA0262902662
    +B1705094548570N00635072EA0263202666
    +B1705114548577N00635086EA0263602671
    +B1705134548584N00635101EA0264102675
    +B1705154548592N00635116EA0264702681
    +B1705174548600N00635131EA0265302687
    +B1705194548607N00635147EA0265902694
    +B1705214548614N00635163EA0266402699
    +B1705234548622N00635180EA0266802704
    +B1705254548631N00635197EA0267502710
    +B1705274548641N00635212EA0268102717
    +B1705294548650N00635226EA0268602723
    +B1705314548658N00635239EA0268802729
    +B1705334548668N00635251EA0268802733
    +B1705354548679N00635265EA0268902735
    +B1705374548691N00635278EA0269102737
    +B1705394548701N00635289EA0269002738
    +B1705414548712N00635303EA0269002738
    +B1705434548722N00635317EA0269202739
    +B1705454548729N00635331EA0269102740
    +B1705474548733N00635346EA0268802739
    +B1705494548737N00635364EA0268502736
    +B1705514548740N00635384EA0268502734
    +B1705534548740N00635403EA0268602733
    +B1705554548740N00635420EA0268302731
    +B1705574548741N00635437EA0268102729
    +B1705594548741N00635455EA0268002727
    +B1706014548741N00635472EA0268102726
    +B1706034548742N00635486EA0268102726
    +B1706054548745N00635499EA0267802725
    +B1706074548748N00635514EA0267602722
    +B1706094548751N00635530EA0267502721
    +B1706114548757N00635542EA0267102718
    +B1706134548765N00635554EA0266702713
    +B1706154548773N00635566EA0266402709
    +B1706174548780N00635579EA0266102706
    +B1706194548788N00635592EA0265902703
    +B1706214548796N00635605EA0265602700
    +B1706234548801N00635620EA0265302696
    +B1706254548805N00635636EA0265002693
    +B1706274548809N00635652EA0264702690
    +B1706294548812N00635668EA0264402687
    +B1706314548814N00635685EA0264102684
    +B1706334548816N00635701EA0263702680
    +B1706354548819N00635718EA0263402677
    +B1706374548821N00635735EA0263202674
    +B1706394548824N00635751EA0263002672
    +B1706414548826N00635767EA0262802669
    +B1706434548828N00635783EA0262602667
    +B1706454548830N00635800EA0262402665
    +B1706474548831N00635817EA0262102663
    +B1706494548833N00635834EA0261902661
    +B1706514548836N00635850EA0261802659
    +B1706534548838N00635866EA0261602657
    +B1706554548841N00635881EA0261402655
    +B1706574548845N00635896EA0261102652
    +B1706594548847N00635912EA0261002650
    +B1707014548849N00635927EA0260802648
    +B1707034548850N00635942EA0260402645
    +B1707054548850N00635959EA0260102641
    +B1707074548851N00635975EA0259902639
    +B1707094548854N00635991EA0259702637
    +B1707114548857N00636006EA0259602636
    +B1707134548862N00636021EA0259302633
    +B1707154548867N00636035EA0259002631
    +B1707174548872N00636050EA0258802628
    +B1707194548877N00636065EA0258702627
    +B1707214548882N00636080EA0258502625
    +B1707234548888N00636095EA0258402623
    +B1707254548893N00636111EA0258302622
    +B1707274548898N00636126EA0258202621
    +B1707294548903N00636141EA0257902619
    +B1707314548908N00636156EA0257702616
    +B1707334548914N00636172EA0257602615
    +B1707354548919N00636188EA0257502614
    +B1707374548924N00636203EA0257402613
    +B1707394548929N00636218EA0257202611
    +B1707414548934N00636234EA0257002609
    +B1707434548939N00636249EA0256802607
    +B1707454548944N00636264EA0256602605
    +B1707474548949N00636280EA0256402603
    +B1707494548954N00636296EA0256202601
    +B1707514548960N00636311EA0256002599
    +B1707534548965N00636326EA0255702597
    +B1707554548969N00636342EA0255502594
    +B1707574548973N00636359EA0255302592
    +B1707594548977N00636374EA0255202591
    +B1708014548980N00636390EA0255002589
    +B1708034548983N00636406EA0254802587
    +B1708054548986N00636422EA0254602585
    +B1708074548989N00636438EA0254502583
    +B1708094548993N00636454EA0254302582
    +B1708114548996N00636470EA0254202580
    +B1708134549000N00636486EA0254102579
    +B1708154549003N00636503EA0253902577
    +B1708174549006N00636519EA0253702575
    +B1708194549009N00636536EA0253502573
    +B1708214549012N00636553EA0253402572
    +B1708234549015N00636570EA0253202570
    +B1708254549019N00636586EA0253102569
    +B1708274549023N00636602EA0252902567
    +B1708294549027N00636618EA0252802566
    +B1708314549031N00636635EA0252602564
    +B1708334549035N00636651EA0252402562
    +B1708354549039N00636667EA0252302561
    +B1708374549043N00636683EA0252102559
    +B1708394549046N00636698EA0251802557
    +B1708414549049N00636715EA0251602554
    +B1708434549053N00636733EA0251602552
    +B1708454549055N00636750EA0251402551
    +B1708474549057N00636766EA0251102549
    +B1708494549058N00636783EA0250902547
    +B1708514549058N00636801EA0250802545
    +B1708534549058N00636817EA0250602543
    +B1708554549058N00636833EA0250302540
    +B1708574549059N00636849EA0250102537
    +B1708594549062N00636866EA0249902535
    +B1709014549064N00636882EA0249802533
    +B1709034549068N00636898EA0249602531
    +B1709054549071N00636914EA0249402530
    +B1709074549075N00636930EA0249302528
    +B1709094549078N00636946EA0249002526
    +B1709114549081N00636962EA0248802523
    +B1709134549084N00636979EA0248602522
    +B1709154549087N00636995EA0248502520
    +B1709174549090N00637012EA0248202518
    +B1709194549092N00637029EA0248002516
    +B1709214549094N00637046EA0247902514
    +B1709234549097N00637062EA0247702513
    +B1709254549100N00637079EA0247502511
    +B1709274549103N00637096EA0247302508
    +B1709294549106N00637113EA0247102506
    +B1709314549110N00637129EA0246802504
    +B1709334549114N00637145EA0246602502
    +B1709354549119N00637162EA0246402499
    +B1709374549123N00637179EA0246202497
    +B1709394549127N00637195EA0245902495
    +B1709414549131N00637213EA0245702493
    +B1709434549134N00637230EA0245502490
    +B1709454549136N00637248EA0245202488
    +B1709474549138N00637265EA0244902485
    +B1709494549139N00637283EA0244602483
    +B1709514549138N00637301EA0244402480
    +B1709534549139N00637318EA0244202478
    +B1709554549140N00637336EA0243902475
    +B1709574549142N00637353EA0243702473
    +B1709594549146N00637370EA0243502470
    +B1710014549150N00637387EA0243202468
    +B1710034549154N00637404EA0243002466
    +B1710054549158N00637420EA0242802463
    +B1710074549162N00637437EA0242602462
    +B1710094549167N00637453EA0242402459
    +B1710114549171N00637470EA0242102456
    +B1710134549176N00637487EA0241802454
    +B1710154549181N00637503EA0241602451
    +B1710174549186N00637520EA0241402449
    +B1710194549192N00637536EA0241102447
    +B1710214549196N00637552EA0240902444
    +B1710234549202N00637568EA0240602441
    +B1710254549207N00637585EA0240302438
    +B1710274549211N00637602EA0240002436
    +B1710294549215N00637619EA0239802433
    +B1710314549219N00637635EA0239502430
    +B1710334549224N00637652EA0239302428
    +B1710354549228N00637669EA0239002425
    +B1710374549232N00637686EA0238802423
    +B1710394549236N00637702EA0238602421
    +B1710414549240N00637719EA0238402419
    +B1710434549244N00637735EA0238202417
    +B1710454549247N00637751EA0237902414
    +B1710474549251N00637767EA0237602411
    +B1710494549255N00637783EA0237402409
    +B1710514549258N00637799EA0237102407
    +B1710534549262N00637815EA0236802403
    +B1710554549266N00637832EA0236602401
    +B1710574549271N00637847EA0236402399
    +B1710594549275N00637862EA0236002396
    +B1711014549279N00637879EA0235602392
    +B1711034549284N00637895EA0235302389
    +B1711054549288N00637911EA0235002385
    +B1711074549294N00637927EA0234702382
    +B1711094549299N00637943EA0234502379
    +B1711114549303N00637959EA0234202376
    +B1711134549307N00637975EA0233802373
    +B1711154549310N00637992EA0233502370
    +B1711174549314N00638008EA0233202366
    +B1711194549318N00638025EA0232802363
    +B1711214549321N00638041EA0232502360
    +B1711234549325N00638058EA0232302357
    +B1711254549328N00638074EA0232002355
    +B1711274549330N00638090EA0231602351
    +B1711294549335N00638107EA0231302348
    +B1711314549339N00638125EA0230902344
    +B1711334549342N00638142EA0230702342
    +B1711354549346N00638159EA0230402339
    +B1711374549349N00638177EA0230102335
    +B1711394549353N00638194EA0229902333
    +B1711414549356N00638211EA0229702331
    +B1711434549358N00638229EA0229402328
    +B1711454549361N00638247EA0229102325
    +B1711474549364N00638264EA0229002323
    +B1711494549367N00638282EA0228802321
    +B1711514549369N00638299EA0228502319
    +B1711534549372N00638317EA0228202316
    +B1711554549375N00638335EA0228002314
    +B1711574549378N00638353EA0227802312
    +B1711594549380N00638371EA0227602310
    +B1712014549382N00638389EA0227402308
    +B1712034549382N00638408EA0227302306
    +B1712054549383N00638427EA0227202305
    +B1712074549384N00638444EA0227002303
    +B1712094549385N00638463EA0226702301
    +B1712114549386N00638482EA0226502298
    +B1712134549388N00638501EA0226402297
    +B1712154549389N00638519EA0226202295
    +B1712174549392N00638537EA0225902292
    +B1712194549395N00638555EA0225602290
    +B1712214549399N00638573EA0225402288
    +B1712234549401N00638591EA0225202285
    +B1712254549404N00638609EA0224902283
    +B1712274549406N00638626EA0224602280
    +B1712294549408N00638644EA0224202277
    +B1712314549410N00638662EA0224002274
    +B1712334549411N00638681EA0223802271
    +B1712354549412N00638699EA0223602269
    +B1712374549413N00638717EA0223402267
    +B1712394549415N00638735EA0223202265
    +B1712414549416N00638752EA0223002263
    +B1712434549418N00638770EA0222802261
    +B1712454549420N00638788EA0222602259
    +B1712474549423N00638807EA0222402258
    +B1712494549425N00638824EA0222302256
    +B1712514549428N00638842EA0222102254
    +B1712534549431N00638860EA0222002252
    +B1712554549432N00638878EA0221802251
    +B1712574549434N00638896EA0221602249
    +B1712594549436N00638914EA0221402247
    +B1713014549439N00638933EA0221202245
    +B1713034549442N00638951EA0221102244
    +B1713054549444N00638969EA0220802242
    +B1713074549447N00638987EA0220502239
    +B1713094549450N00639005EA0220302236
    +B1713114549452N00639025EA0220302235
    +B1713134549455N00639041EA0220202234
    +B1713154549458N00639057EA0220102232
    +B1713174549461N00639073EA0220002232
    +B1713194549464N00639089EA0219902231
    +B1713214549467N00639104EA0219702229
    +B1713234549470N00639120EA0219502227
    +B1713254549473N00639136EA0219302226
    +B1713274549476N00639151EA0219002223
    +B1713294549479N00639168EA0218802221
    +B1713314549482N00639185EA0218802220
    +B1713334549484N00639202EA0218802219
    +B1713354549486N00639219EA0218602218
    +B1713374549488N00639237EA0218402216
    +B1713394549490N00639255EA0218302214
    +B1713414549491N00639272EA0218202213
    +B1713434549493N00639289EA0218002212
    +B1713454549495N00639305EA0217702209
    +B1713474549498N00639322EA0217502207
    +B1713494549501N00639338EA0217402206
    +B1713514549504N00639354EA0217202204
    +B1713534549507N00639369EA0217102203
    +B1713554549511N00639385EA0217002202
    +B1713574549513N00639401EA0216802200
    +B1713594549516N00639417EA0216602198
    +B1714014549519N00639434EA0216502197
    +B1714034549522N00639451EA0216502197
    +B1714054549526N00639467EA0216402196
    +B1714074549529N00639483EA0216202194
    +B1714094549533N00639500EA0216102193
    +B1714114549537N00639515EA0215902192
    +B1714134549541N00639530EA0215702190
    +B1714154549545N00639547EA0215702189
    +B1714174549549N00639564EA0215702188
    +B1714194549552N00639580EA0215502187
    +B1714214549555N00639597EA0215302185
    +B1714234549559N00639616EA0215302184
    +B1714254549562N00639634EA0215202184
    +B1714274549564N00639650EA0215002182
    +B1714294549567N00639668EA0214702180
    +B1714314549571N00639685EA0214602178
    +B1714334549575N00639702EA0214402177
    +B1714354549579N00639718EA0214202174
    +B1714374549583N00639735EA0214002172
    +B1714394549588N00639751EA0213802170
    +B1714414549594N00639767EA0213602169
    +B1714434549599N00639782EA0213302166
    +B1714454549604N00639798EA0213002163
    +B1714474549610N00639814EA0212802160
    +B1714494549615N00639830EA0212502158
    +B1714514549620N00639845EA0212202155
    +B1714534549625N00639861EA0212002152
    +B1714554549630N00639876EA0211802150
    +B1714574549634N00639891EA0211402147
    +B1714594549638N00639907EA0211002143
    +B1715014549643N00639923EA0210802140
    +B1715034549648N00639938EA0210602138
    +B1715054549652N00639953EA0210402135
    +B1715074549657N00639968EA0210202133
    +B1715094549661N00639984EA0210102131
    +B1715114549667N00639999EA0209902130
    +B1715134549672N00640013EA0209602128
    +B1715154549678N00640027EA0209202124
    +B1715174549685N00640043EA0209002122
    +B1715194549691N00640057EA0208902120
    +B1715214549697N00640071EA0208602117
    +B1715234549703N00640086EA0208402114
    +B1715254549710N00640101EA0208302113
    +B1715274549716N00640115EA0208102112
    +B1715294549723N00640130EA0207902110
    +B1715314549728N00640145EA0207802108
    +B1715334549734N00640160EA0207602106
    +B1715354549739N00640176EA0207402104
    +B1715374549744N00640192EA0207302102
    +B1715394549748N00640209EA0207202101
    +B1715414549752N00640225EA0207002100
    +B1715434549755N00640242EA0206902099
    +B1715454549759N00640259EA0206802098
    +B1715474549762N00640277EA0206702097
    +B1715494549764N00640295EA0206702096
    +B1715514549766N00640313EA0206602095
    +B1715534549767N00640331EA0206402095
    +B1715554549767N00640349EA0206302093
    +B1715574549768N00640367EA0206202091
    +B1715594549769N00640386EA0206102091
    +B1716014549769N00640403EA0205902090
    +B1716034549770N00640421EA0205702088
    +B1716054549771N00640440EA0205602087
    +B1716074549772N00640458EA0205602086
    +B1716094549774N00640476EA0205302084
    +B1716114549775N00640494EA0205002082
    +B1716134549778N00640512EA0204902080
    +B1716154549781N00640529EA0204702078
    +B1716174549783N00640547EA0204502076
    +B1716194549787N00640564EA0204302074
    +B1716214549793N00640580EA0204202073
    +B1716234549799N00640595EA0204002071
    +B1716254549806N00640610EA0203802069
    +B1716274549814N00640625EA0203602067
    +B1716294549821N00640639EA0203402065
    +B1716314549827N00640654EA0203102062
    +B1716334549833N00640670EA0202902060
    +B1716354549839N00640687EA0202702058
    +B1716374549845N00640703EA0202502056
    +B1716394549851N00640719EA0202202053
    +B1716414549858N00640735EA0201902051
    +B1716434549865N00640750EA0201802048
    +B1716454549872N00640765EA0201502046
    +B1716474549880N00640780EA0201202043
    +B1716494549888N00640794EA0200902040
    +B1716514549897N00640808EA0200702038
    +B1716534549906N00640821EA0200402035
    +B1716554549916N00640833EA0200102032
    +B1716574549926N00640845EA0200002030
    +B1716594549936N00640857EA0199802029
    +B1717014549946N00640868EA0199402026
    +B1717034549956N00640879EA0199102023
    +B1717054549965N00640893EA0199002020
    +B1717074549973N00640906EA0198802019
    +B1717094549982N00640919EA0198502017
    +B1717114549991N00640932EA0198302014
    +B1717134550000N00640944EA0198202012
    +B1717154550010N00640956EA0198302011
    +B1717174550019N00640969EA0198402012
    +B1717194550027N00640982EA0198402013
    +B1717214550033N00640993EA0198302013
    +B1717234550040N00641003EA0198102012
    +B1717254550048N00641015EA0197902010
    +B1717274550055N00641026EA0197702008
    +B1717294550063N00641037EA0197502006
    +B1717314550071N00641048EA0197302004
    +B1717334550079N00641060EA0197202003
    +B1717354550085N00641071EA0197002001
    +B1717374550093N00641081EA0196801999
    +B1717394550102N00641092EA0196801998
    +B1717414550110N00641102EA0196601997
    +B1717434550118N00641112EA0196501996
    +B1717454550127N00641120EA0196601995
    +B1717474550135N00641128EA0196501995
    +B1717494550143N00641135EA0196401994
    +B1717514550152N00641139EA0196201992
    +B1717534550159N00641131EA0195901990
    +B1717554550161N00641116EA0195601988
    +B1717574550154N00641102EA0195501986
    +B1717594550144N00641095EA0195401984
    +B1718014550134N00641090EA0195301983
    +B1718034550126N00641082EA0195201981
    +B1718054550117N00641073EA0195101981
    +B1718074550108N00641067EA0194901980
    +B1718094550098N00641059EA0194701978
    +B1718114550089N00641049EA0194601977
    +B1718134550081N00641039EA0194601977
    +B1718154550074N00641029EA0194401975
    +B1718174550066N00641019EA0194101972
    +B1718194550061N00641005EA0194001970
    +B1718214550059N00640990EA0193801968
    +B1718234550058N00640975EA0193501965
    +B1718254550060N00640959EA0193201962
    +B1718274550061N00640943EA0193101959
    +B1718294550065N00640928EA0193001958
    +B1718314550070N00640915EA0193001958
    +B1718334550073N00640902EA0192901957
    +B1718354550074N00640889EA0192901957
    +B1718374550075N00640877EA0192901957
    +B1718394550073N00640864EA0192801956
    +B1718414550069N00640851EA0192501954
    +B1718434550067N00640837EA0192301952
    +B1718454550065N00640823EA0192101949
    +B1718474550062N00640809EA0191801946
    +B1718494550060N00640793EA0191501943
    +B1718514550058N00640777EA0191501941
    +B1718534550055N00640760EA0191601941
    +B1718554550053N00640743EA0191601941
    +B1718574550050N00640729EA0191501941
    +B1718594550046N00640715EA0191201940
    +B1719014550042N00640702EA0190801937
    +B1719034550038N00640688EA0190401934
    +B1719054550034N00640672EA0190101930
    +B1719074550031N00640656EA0189701926
    +B1719094550027N00640641EA0189501923
    +B1719114550024N00640626EA0189201919
    +B1719134550021N00640610EA0189001917
    +B1719154550020N00640593EA0189001915
    +B1719174550020N00640578EA0189101915
    +B1719194550021N00640564EA0189101915
    +B1719214550023N00640549EA0189001915
    +B1719234550025N00640534EA0189001915
    +B1719254550026N00640519EA0188901914
    +B1719274550028N00640504EA0188701912
    +B1719294550030N00640487EA0188501911
    +B1719314550031N00640472EA0188301909
    +B1719334550032N00640456EA0188001907
    +B1719354550033N00640440EA0187901905
    +B1719374550033N00640424EA0187701903
    +B1719394550034N00640408EA0187401900
    +B1719414550035N00640391EA0187301898
    +B1719434550036N00640376EA0187101897
    +B1719454550038N00640362EA0186601895
    +B1719474550039N00640346EA0186101891
    +B1719494550040N00640328EA0185801887
    +B1719514550040N00640311EA0185501884
    +B1719534550040N00640295EA0185101880
    +B1719554550041N00640278EA0184801876
    +B1719574550041N00640261EA0184301873
    +B1719594550042N00640245EA0184001869
    +B1720014550041N00640229EA0183601865
    +B1720034550040N00640213EA0183101861
    +B1720054550039N00640198EA0182601856
    +B1720074550036N00640181EA0182301852
    +B1720094550034N00640165EA0181901848
    +B1720114550033N00640151EA0181401843
    +B1720134550033N00640134EA0181001839
    +B1720154550032N00640117EA0180701835
    +B1720174550032N00640102EA0180301832
    +B1720194550034N00640087EA0179801827
    +B1720214550036N00640070EA0179401823
    +B1720234550038N00640054EA0179101819
    +B1720254550040N00640039EA0178601815
    +B1720274550043N00640022EA0178101809
    +B1720294550045N00640005EA0177901806
    +B1720314550046N00639989EA0177501802
    +B1720334550046N00639973EA0177101798
    +B1720354550047N00639956EA0177001794
    +B1720374550049N00639939EA0176901792
    +B1720394550051N00639923EA0176801791
    +B1720414550054N00639907EA0176701790
    +B1720434550057N00639892EA0176501789
    +B1720454550060N00639876EA0176301788
    +B1720474550062N00639859EA0176001785
    +B1720494550066N00639843EA0175701783
    +B1720514550069N00639827EA0175301779
    +B1720534550072N00639810EA0175101776
    +B1720554550075N00639793EA0174901775
    +B1720574550078N00639778EA0174601772
    +B1720594550081N00639762EA0174401769
    +B1721014550084N00639744EA0174301767
    +B1721034550088N00639728EA0174201766
    +B1721054550091N00639712EA0174201766
    +B1721074550095N00639697EA0174201766
    +B1721094550100N00639682EA0174301767
    +B1721114550105N00639668EA0174201767
    +B1721134550110N00639654EA0174101766
    +B1721154550116N00639639EA0174101765
    +B1721174550121N00639626EA0174101766
    +B1721194550126N00639612EA0173901765
    +B1721214550131N00639597EA0173601764
    +B1721234550136N00639582EA0173501762
    +B1721254550143N00639568EA0173401761
    +B1721274550151N00639555EA0173401760
    +B1721294550158N00639543EA0173301760
    +B1721314550166N00639531EA0173301759
    +B1721334550175N00639521EA0173401759
    +B1721354550183N00639511EA0173601761
    +B1721374550190N00639503EA0173501761
    +B1721394550197N00639495EA0173301761
    +B1721414550204N00639484EA0172901759
    +B1721434550211N00639471EA0172601756
    +B1721454550218N00639457EA0172401753
    +B1721474550225N00639444EA0172301752
    +B1721494550231N00639431EA0172201751
    +B1721514550238N00639418EA0172101749
    +B1721534550244N00639405EA0171901747
    +B1721554550250N00639393EA0171801746
    +B1721574550255N00639380EA0171601744
    +B1721594550260N00639366EA0171501742
    +B1722014550263N00639351EA0171401740
    +B1722034550267N00639336EA0171201738
    +B1722054550270N00639320EA0171001737
    +B1722074550273N00639305EA0170901735
    +B1722094550276N00639289EA0170601733
    +B1722114550280N00639274EA0170301730
    +B1722134550286N00639259EA0170101728
    +B1722154550293N00639246EA0170101726
    +B1722174550301N00639236EA0169901725
    +B1722194550309N00639228EA0169701723
    +B1722214550319N00639221EA0169401719
    +B1722234550330N00639215EA0169301718
    +B1722254550339N00639210EA0169101717
    +B1722274550349N00639205EA0169001715
    +B1722294550360N00639200EA0169101714
    +B1722314550370N00639197EA0169101715
    +B1722334550379N00639196EA0169001714
    +B1722354550389N00639192EA0168901713
    +B1722374550399N00639187EA0168901712
    +B1722394550411N00639188EA0168901712
    +B1722414550421N00639190EA0169201713
    +B1722434550429N00639191EA0169301714
    +B1722454550435N00639198EA0169101715
    +B1722474550439N00639211EA0169101715
    +B1722494550444N00639224EA0169201715
    +B1722514550449N00639236EA0169101715
    +B1722534550454N00639251EA0169101714
    +B1722554550457N00639267EA0169101714
    +B1722574550460N00639283EA0169201715
    +B1722594550464N00639295EA0169201715
    +B1723014550468N00639304EA0169001715
    +B1723034550475N00639311EA0169001714
    +B1723054550482N00639318EA0169201715
    +B1723074550488N00639325EA0169101715
    +B1723094550494N00639333EA0169001714
    +B1723114550500N00639341EA0168801713
    +B1723134550507N00639350EA0168801712
    +B1723154550514N00639360EA0168901712
    +B1723174550519N00639369EA0168801712
    +B1723194550524N00639376EA0168701711
    +B1723214550531N00639382EA0168501710
    +B1723234550538N00639388EA0168401709
    +B1723254550544N00639393EA0168301708
    +B1723274550551N00639396EA0168201707
    +B1723294550559N00639399EA0168201706
    +B1723314550566N00639400EA0168101705
    +B1723334550573N00639398EA0167901703
    +B1723354550581N00639394EA0167801702
    +B1723374550589N00639390EA0167801702
    +B1723394550596N00639385EA0167601702
    +B1723414550603N00639380EA0167401700
    +B1723434550611N00639373EA0167201699
    +B1723454550618N00639366EA0167201699
    +B1723474550624N00639359EA0167001698
    +B1723494550634N00639346EA0166801697
    +B1723524550641N00639337EA0166901697
    +B1723544550648N00639330EA0166701696
    +B1723564550655N00639323EA0166601694
    +B1723584550664N00639316EA0166501694
    +B1724004550671N00639310EA0166501694
    +B1724024550677N00639302EA0166401693
    +B1724044550684N00639294EA0166301692
    +B1724064550691N00639289EA0166201691
    +B1724084550697N00639282EA0166101690
    +B1724104550703N00639274EA0166001689
    +B1724124550709N00639266EA0165901688
    +B1724144550716N00639259EA0165701687
    +B1724164550722N00639252EA0165501685
    +B1724184550729N00639243EA0165401683
    +B1724204550736N00639233EA0165401682
    +B1724224550742N00639225EA0165401683
    +B1724244550748N00639218EA0165401683
    +B1724264550754N00639210EA0165201681
    +B1724284550762N00639203EA0165101680
    +B1724304550768N00639196EA0164901678
    +B1724324550774N00639188EA0164601676
    +B1724344550780N00639182EA0164301672
    +B1724364550787N00639175EA0163901669
    +B1724384550793N00639168EA0163601666
    +B1724404550800N00639160EA0163401663
    +B1724424550806N00639151EA0163201661
    +B1724444550812N00639144EA0162901658
    +B1724464550817N00639135EA0162501654
    +B1724484550823N00639125EA0162201651
    +B1724504550829N00639116EA0162001649
    +B1724524550833N00639108EA0161701645
    +B1724544550839N00639098EA0161401642
    +B1724564550846N00639090EA0161101640
    +B1724584550851N00639081EA0160801638
    +B1725004550855N00639071EA0160401635
    +B1725024550861N00639061EA0160101632
    +B1725044550865N00639051EA0159801628
    +B1725064550870N00639040EA0159501625
    +B1725084550874N00639028EA0159101622
    +B1725104550877N00639017EA0158801618
    +B1725124550881N00639005EA0158201614
    +B1725144550885N00638992EA0157601610
    +B1725164550888N00638977EA0157101605
    +B1725184550889N00638961EA0156501599
    +B1725204550890N00638944EA0156101594
    +B1725224550892N00638929EA0155701589
    +B1725244550893N00638913EA0155401585
    +B1725264550894N00638898EA0155101580
    +B1725284550895N00638882EA0154701575
    +B1725304550895N00638865EA0154401572
    +B1725324550893N00638848EA0154101568
    +B1725344550892N00638833EA0153801565
    +B1725364550892N00638818EA0153501561
    +B1725384550893N00638803EA0153201558
    +B1725404550892N00638787EA0152901555
    +B1725424550892N00638771EA0152601552
    +B1725444550892N00638755EA0152501550
    +B1725464550892N00638739EA0152201547
    +B1725484550892N00638723EA0151901544
    +B1725504550893N00638706EA0151601541
    +B1725524550894N00638690EA0151601539
    +B1725544550895N00638674EA0151301537
    +B1725564550898N00638659EA0151101535
    +B1725584550902N00638645EA0151001533
    +B1726004550906N00638633EA0150901532
    +B1726024550911N00638621EA0150601530
    +B1726044550916N00638609EA0150301527
    +B1726064550921N00638598EA0150001524
    +B1726084550926N00638586EA0149601521
    +B1726104550931N00638572EA0149401519
    +B1726124550935N00638559EA0149101518
    +B1726144550939N00638546EA0148901517
    +B1726164550942N00638533EA0148601514
    +B1726184550945N00638518EA0148401512
    +B1726204550949N00638505EA0148201510
    +B1726224550952N00638494EA0148001507
    +B1726244550956N00638482EA0147501504
    +B1726264550961N00638469EA0147201500
    +B1726284550966N00638455EA0147101497
    +B1726304550970N00638443EA0147001495
    +B1726324550973N00638430EA0146701493
    +B1726344550978N00638417EA0146501490
    +B1726364550983N00638404EA0146401488
    +B1726384550987N00638393EA0146201486
    +B1726404550992N00638381EA0146001483
    +B1726424550998N00638370EA0145801480
    +B1726444551004N00638361EA0145601478
    +B1726464551010N00638351EA0145301476
    +B1726484551016N00638340EA0145001473
    +B1726504551022N00638330EA0144801471
    +B1726524551028N00638320EA0144601468
    +B1726544551034N00638311EA0144301466
    +B1726564551040N00638300EA0143901462
    +B1726584551046N00638288EA0143901461
    +B1727004551052N00638277EA0143701460
    +B1727024551058N00638266EA0143501457
    +B1727044551065N00638254EA0143301457
    +B1727064551071N00638243EA0143101455
    +B1727084551076N00638232EA0142901453
    +B1727104551081N00638220EA0142601450
    +B1727124551087N00638208EA0142301448
    +B1727144551093N00638197EA0142001445
    +B1727164551099N00638185EA0141701442
    +B1727184551105N00638174EA0141501438
    +B1727204551112N00638163EA0141201436
    +B1727224551118N00638151EA0141001434
    +B1727244551124N00638140EA0140601431
    +B1727264551130N00638127EA0140301428
    +B1727284551136N00638115EA0140101426
    +B1727304551142N00638102EA0139901425
    +B1727324551147N00638090EA0139601424
    +B1727344551153N00638077EA0139301422
    +B1727364551159N00638064EA0139001422
    +B1727384551164N00638050EA0138701421
    +B1727404551169N00638037EA0138301419
    +B1727424551176N00638025EA0138001417
    +B1727444551181N00638012EA0137701415
    +B1727464551188N00638000EA0137301411
    +B1727484551194N00637990EA0137001406
    +B1727504551200N00637979EA0136501401
    +B1727524551207N00637967EA0136201396
    +B1727544551215N00637955EA0136001393
    +B1727564551222N00637945EA0135801391
    +B1727584551229N00637935EA0135401388
    +B1728004551237N00637924EA0135201385
    +B1728024551245N00637914EA0135101383
    +B1728044551251N00637905EA0134801380
    +B1728064551259N00637896EA0134601378
    +B1728084551267N00637886EA0134401376
    +B1728104551274N00637876EA0134001373
    +B1728124551282N00637866EA0133701369
    +B1728144551290N00637856EA0133501366
    +B1728164551298N00637847EA0133301363
    +B1728184551306N00637837EA0132901359
    +B1728204551315N00637826EA0132701356
    +B1728224551323N00637816EA0132501353
    +B1728244551331N00637807EA0132101349
    +B1728264551339N00637796EA0131901346
    +B1728284551347N00637784EA0131801344
    +B1728304551353N00637773EA0131601342
    +B1728324551361N00637763EA0131301339
    +B1728344551369N00637753EA0131001337
    +B1728364551377N00637745EA0130901335
    +B1728384551385N00637737EA0130601333
    +B1728404551393N00637727EA0130401330
    +B1728424551400N00637717EA0130101327
    +B1728444551408N00637706EA0129801324
    +B1728464551417N00637696EA0129701321
    +B1728484551425N00637689EA0129501319
    +B1728504551433N00637681EA0129201317
    +B1728524551442N00637672EA0128901313
    +B1728544551450N00637662EA0128701311
    +B1728564551458N00637651EA0128401309
    +B1728584551466N00637639EA0128201307
    +B1729004551473N00637628EA0128001306
    +B1729024551481N00637617EA0127701304
    +B1729044551490N00637609EA0127401300
    +B1729064551499N00637603EA0127201297
    +B1729084551508N00637599EA0127001294
    +B1729104551517N00637594EA0126701291
    +B1729124551526N00637587EA0126401287
    +B1729144551534N00637579EA0126101285
    +B1729164551542N00637570EA0125801282
    +B1729184551551N00637561EA0125401278
    +B1729204551560N00637551EA0125201275
    +B1729224551569N00637542EA0125101273
    +B1729244551577N00637536EA0124801270
    +B1729264551587N00637529EA0124701267
    +B1729284551596N00637525EA0124501266
    +B1729304551605N00637520EA0124101264
    +B1729324551616N00637516EA0124001261
    +B1729344551626N00637511EA0124101261
    +B1729364551634N00637506EA0124101260
    +B1729384551643N00637500EA0124001258
    +B1729404551653N00637497EA0123901257
    +B1729424551662N00637496EA0123801255
    +B1729444551671N00637497EA0123701254
    +B1729464551680N00637499EA0123701253
    +B1729484551688N00637499EA0123501252
    +B1729504551697N00637499EA0123301250
    +B1729524551707N00637500EA0123101248
    +B1729544551716N00637502EA0123001246
    +B1729564551724N00637505EA0122701244
    +B1729584551732N00637498EA0122001241
    +B1730004551733N00637477EA0121501237
    +B1730024551727N00637458EA0121901234
    +B1730044551723N00637448EA0121401232
    +B1730064551715N00637431EA0120601226
    +B1730084551703N00637413EA0120701223
    +B1730104551695N00637401EA0120801221
    +B1730124551690N00637388EA0120301217
    +B1730144551683N00637371EA0119801213
    +B1730164551674N00637354EA0119901212
    +B1730184551667N00637340EA0119701210
    +B1730204551664N00637325EA0119201208
    +B1730224551672N00637314EA0118601204
    +B1730244551685N00637317EA0117801198
    +B1730264551675N00637333EA0116901191
    +B1730284551659N00637320EA0116501184
    +B1730304551659N00637301EA0116801181
    +B1730324551660N00637287EA0116601180
    +B1730344551653N00637272EA0116001175
    +B1730364551642N00637257EA0115801173
    +B1730384551632N00637247EA0115901173
    +B1730404551623N00637238EA0115401170
    +B1730424551612N00637226EA0115201167
    +B1730444551602N00637213EA0115301166
    +B1730464551600N00637199EA0114801163
    +B1730484551605N00637178EA0114401161
    +B1730504551614N00637165EA0114401161
    +B1730524551620N00637156EA0114101159
    +B1730544551626N00637144EA0113501157
    +B1730564551634N00637131EA0113501155
    +B1730584551642N00637122EA0113501154
    +B1731004551648N00637114EA0113201151
    +B1731024551657N00637112EA0113001148
    +B1731044551667N00637124EA0112301143
    +B1731064551657N00637145EA0111801137
    +B1731084551640N00637149EA0111901133
    +B1731104551631N00637154EA0111401130
    +B1731124551633N00637169EA0110701124
    +B1731144551644N00637176EA0110501119
    +B1731164551653N00637178EA0110501117
    +B1731184551661N00637185EA0110001114
    +B1731204551667N00637196EA0110101112
    +B1731224551672N00637205EA0109701111
    +B1731244551675N00637214EA0108701108
    +B1731264551680N00637224EA0108701104
    +B1731284551687N00637234EA0109001102
    +B1731304551692N00637241EA0109001101
    +B1731324551693N00637243EA0109001100
    +B1731344551694N00637244EA0109001100
    +B1731364551694N00637244EA0109101100
    +B1731384551694N00637244EA0109001100
    +B1731404551694N00637244EA0109001100
    +B1731424551694N00637244EA0109001100
    +B1731444551694N00637244EA0109101100
    +B1731464551694N00637244EA0109001100
    +B1731484551694N00637244EA0109001100
    +B1731504551694N00637244EA0109001100
    +B1731524551694N00637244EA0109001100
    +B1731544551694N00637244EA0109001100
    +B1731564551694N00637244EA0109001100
    +B1731584551694N00637244EA0109001100
    +B1732004551694N00637244EA0109001100
    +B1732024551694N00637244EA0109001100
    +B1732044551694N00637244EA0109001100
    +B1732064551694N00637244EA0109001100
    +B1732084551694N00637244EA0109001100
    +B1732104551694N00637244EA0109001100
    +B1732124551694N00637244EA0109001100
    +B1732144551694N00637244EA0109001100
    +B1732164551694N00637244EA0109001100
    +B1732184551694N00637244EA0109001100
    +B1732204551694N00637244EA0109001100
    +B1732224551694N00637244EA0109001100
    +B1732244551694N00637244EA0109001100
    +B1732264551694N00637244EA0109001100
    +B1732284551694N00637244EA0109001100
    +B1732304551694N00637244EA0109001100
    +B1732324551694N00637244EA0109001100
    +B1732344551694N00637244EA0109001100
    +B1732364551694N00637244EA0109001098
    +B1732384551694N00637244EA0109001098
    +B1732404551694N00637244EA0109001099
    +B1732424551694N00637244EA0109001100
    +B1732444551694N00637244EA0109001100
    +B1732464551694N00637244EA0109001101
    +B1732484551694N00637244EA0109001101
    +B1732504551694N00637244EA0109001101
    +B1732524551694N00637244EA0109001101
    +B1732544551694N00637244EA0109001101
    +B1732564551694N00637244EA0109001101
    +B1732584551694N00637244EA0109001101
    +B1733004551694N00637244EA0109001101
    +B1733024551694N00637244EA0109001101
    +B1733044551694N00637244EA0109001101
    +B1733064551694N00637244EA0109001101
    +B1733084551694N00637245EA0109001101
    diff --git a/mapbox-gl/examples/data/igc/Damien-de-Baenst.igc b/mapbox-gl/examples/data/igc/Damien-de-Baenst.igc
    new file mode 100644
    index 0000000000..d6fa2fe39d
    --- /dev/null
    +++ b/mapbox-gl/examples/data/igc/Damien-de-Baenst.igc
    @@ -0,0 +1,3319 @@
    +ABRA00326
    +HFDTE190411
    +HFFXA100
    +HFPLTPILOT:Damien de Baesnt
    +HFGTYGLIDERTYPE:Ozone R10.2       
    +HFGIDGLIDERID:1111              
    +HFDTM100GPSDATUM:WGS84
    +HFGPSGPS:FURUNO GH-80
    +HFRFWFIRMWAREVERSION:2.27
    +HFRHWHARDWAREVERSION:1.00
    +HFFTYFRTYPE:BRAUNIGER,COMPEO
    +I013638TAS
    +B0844164556194N00651071EA0203502037000
    +B0844264556194N00651071EA0203502038000
    +B0844364556194N00651071EA0203502039000
    +B0844464556194N00651070EA0203602040000
    +B0844564556193N00651071EA0203502040000
    +B0845064556193N00651071EA0203602041000
    +B0845164556193N00651071EA0203502042000
    +B0845264556193N00651070EA0203602042000
    +B0845364556193N00651071EA0203502041000
    +B0845464556194N00651071EA0203502041000
    +B0845564556194N00651071EA0203602040000
    +B0846064556195N00651071EA0203602039000
    +B0846164556195N00651072EA0203602039000
    +B0846264556195N00651072EA0203602039000
    +B0846364556195N00651072EA0203602040000
    +B0846464556195N00651072EA0203502040001
    +B0846564556195N00651071EA0203502040001
    +B0847064556195N00651071EA0203502039000
    +B0847164556194N00651071EA0203502038000
    +B0847264556193N00651071EA0203502036000
    +B0847364556193N00651071EA0203502033000
    +B0847464556193N00651071EA0203502033000
    +B0847564556194N00651070EA0203502033000
    +B0848064556194N00651070EA0203502033000
    +B0848164556194N00651070EA0203502034000
    +B0848264556194N00651070EA0203502035000
    +B0848364556194N00651070EA0203502036000
    +B0848464556184N00651068EA0202802036003
    +B0848564556147N00651045EA0203302035000
    +B0849064556120N00651002EA0203902037000
    +B0849174556106N00650925EA0204402041000
    +B0849274556084N00650863EA0203502042000
    +B0849374556056N00650807EA0203502042000
    +B0849474556028N00650751EA0205202043000
    +B0849574555997N00650710EA0206902048000
    +B0850074555959N00650698EA0206402056000
    +B0850174555995N00650719EA0206502061000
    +B0850274556007N00650753EA0207002065000
    +B0850374555968N00650704EA0208202071000
    +B0850474555923N00650661EA0206802074000
    +B0850574555877N00650603EA0207802076000
    +B0851074555827N00650572EA0209402080000
    +B0851174555840N00650598EA0209502086000
    +B0851274555894N00650558EA0210402091000
    +B0851374555916N00650579EA0210202098000
    +B0851484555864N00650522EA0210902103000
    +B0851584555808N00650494EA0211802108000
    +B0852084555781N00650531EA0213102115000
    +B0852184555750N00650535EA0212702121000
    +B0852284555774N00650500EA0213302126000
    +B0852384555725N00650478EA0213902130000
    +B0852484555694N00650496EA0215702137000
    +B0852584555702N00650445EA0215902144000
    +B0853084555664N00650396EA0217802153000
    +B0853184555661N00650439EA0221002165000
    +B0853284555615N00650432EA0223102184000
    +B0853384555639N00650429EA0225202204000
    +B0853484555599N00650406EA0226002222000
    +B0853584555629N00650372EA0227502237000
    +B0854084555661N00650403EA0227202252000
    +B0854184555696N00650447EA0225602257000
    +B0854284555733N00650486EA0224502258000
    +B0854384555779N00650521EA0223602255000
    +B0854484555811N00650573EA0222902249000
    +B0854584555839N00650630EA0222502243000
    +B0855084555858N00650693EA0221502237000
    +B0855184555884N00650741EA0220602230000
    +B0855284555906N00650786EA0219702223047
    +B0855384555924N00650838EA0219002214041
    +B0855484555949N00650883EA0217602205042
    +B0855584555968N00650908EA0217202199038
    +B0856084555985N00650944EA0216602192042
    +B0856184555995N00650972EA0218202188027
    +B0856284555993N00650995EA0217802187041
    +B0856384556001N00651028EA0217602185041
    +B0856484556012N00651058EA0216302183041
    +B0856584556025N00651094EA0214802177046
    +B0857084556041N00651125EA0214702170045
    +B0857184556066N00651153EA0214002163041
    +B0857284556092N00651182EA0214202157041
    +B0857384556133N00651191EA0215702155038
    +B0857484556139N00651216EA0217502161045
    +B0857584556148N00651179EA0220102172045
    +B0858084556123N00651139EA0222202185044
    +B0858184556119N00651134EA0224702203045
    +B0858284556145N00651090EA0228202224042
    +B0858384556123N00651077EA0230102247038
    +B0858484556136N00651065EA0232702271048
    +B0858584556123N00651015EA0236202298042
    +B0859084556107N00651035EA0239002326030
    +B0859184556099N00650985EA0242602354052
    +B0859294556053N00650995EA0245402386038
    +B0859394556068N00650969EA0248602415032
    +B0859494556014N00650971EA0250302444040
    +B0859594556014N00650890EA0250202467046
    +B0900094556012N00650932EA0253002488045
    +B0900194556004N00650877EA0252802504042
    +B0900294556038N00650897EA0257102520043
    +B0900394556007N00650870EA0259602544042
    +B0900494556015N00650893EA0263102571046
    +B0900594555992N00650859EA0266002597048
    +B0901094555972N00650895EA0268102623046
    +B0901194555977N00650853EA0270702646038
    +B0901294555973N00650904EA0272702673044
    +B0901394555923N00650867EA0273402695042
    +B0901494555899N00650786EA0274602712044
    +B0901594555871N00650704EA0275402727041
    +B0902094555840N00650618EA0275502738047
    +B0902194555809N00650533EA0274202743044
    +B0902294555784N00650449EA0272402742045
    +B0902394555764N00650371EA0272002739044
    +B0902494555747N00650293EA0272302734038
    +B0902594555729N00650215EA0273302733036
    +B0903094555714N00650135EA0274302737045
    +B0903194555681N00650062EA0274102742045
    +B0903294555637N00649978EA0272802743044
    +B0903394555599N00649881EA0272702738044
    +B0903494555576N00649799EA0275402742038
    +B0903594555585N00649806EA0279202752047
    +B0904094555601N00649797EA0282102771040
    +B0904194555583N00649764EA0284802794044
    +B0904294555575N00649796EA0287502820038
    +B0904394555571N00649728EA0289802844051
    +B0904494555569N00649759EA0292402869042
    +B0904594555554N00649715EA0294802893051
    +B0905094555502N00649727EA0296002916035
    +B0905194555428N00649711EA0295902932044
    +B0905294555362N00649670EA0295302942047
    +B0905394555322N00649577EA0293902947042
    +B0905494555297N00649476EA0291402943046
    +B0905594555267N00649374EA0289102931048
    +B0906094555242N00649277EA0286402915043
    +B0906194555213N00649195EA0283302894038
    +B0906294555187N00649115EA0281002872040
    +B0906394555149N00649030EA0280202850044
    +B0906494555115N00648943EA0279402834048
    +B0906594555080N00648866EA0278102820044
    +B0907094555041N00648772EA0277902806042
    +B0907204555008N00648670EA0278102798044
    +B0907304554951N00648603EA0280302797037
    +B0907404554910N00648557EA0281002803042
    +B0907504554876N00648454EA0278902802061
    +B0908004554827N00648342EA0276102793065
    +B0908104554778N00648226EA0274202779061
    +B0908204554725N00648116EA0272702765058
    +B0908304554658N00648001EA0270002747061
    +B0908404554595N00647879EA0267402726062
    +B0908504554535N00647774EA0265702705057
    +B0909004554480N00647669EA0263502684058
    +B0909104554424N00647568EA0261102663057
    +B0909204554371N00647442EA0259202641060
    +B0909304554316N00647324EA0256702620061
    +B0909404554268N00647221EA0255602601057
    +B0909504554220N00647115EA0254502585057
    +B0910004554173N00647009EA0253202570055
    +B0910104554120N00646906EA0251202554056
    +B0910204554071N00646799EA0249702537058
    +B0910304554018N00646700EA0248402522056
    +B0910404553972N00646589EA0246902506058
    +B0910504553924N00646479EA0245202491061
    +B0911004553875N00646370EA0244002476060
    +B0911104553827N00646265EA0242002461059
    +B0911204553777N00646154EA0240002443060
    +B0911304553730N00646048EA0237102424066
    +B0911404553677N00645931EA0234602402066
    +B0911504553626N00645814EA0232702379067
    +B0912004553572N00645703EA0230402358067
    +B0912104553517N00645593EA0228102336067
    +B0912204553460N00645486EA0226302314065
    +B0912304553406N00645393EA0224702294060
    +B0912404553349N00645304EA0223002275057
    +B0912504553299N00645239EA0222902261044
    +B0913004553251N00645165EA0221602248042
    +B0913104553216N00645081EA0222102238039
    +B0913204553182N00645000EA0223102234037
    +B0913304553142N00645006EA0224302236036
    +B0913404553177N00645023EA0225402242040
    +B0913504553172N00645035EA0227202250051
    +B0914004553178N00645007EA0229702260036
    +B0914104553137N00644991EA0231402277040
    +B0914204553176N00644962EA0233402292036
    +B0914304553178N00644893EA0235102311048
    +B0914414553153N00644941EA0237402333043
    +B0914514553184N00644943EA0239402354040
    +B0915014553219N00644878EA0242702378039
    +B0915114553188N00644839EA0243702401042
    +B0915214553163N00644895EA0245802420036
    +B0915314553188N00644920EA0246202438041
    +B0915414553223N00644863EA0249302454043
    +B0915514553184N00644863EA0251602475041
    +B0916014553222N00644851EA0254002496037
    +B0916114553214N00644772EA0256302519035
    +B0916214553166N00644726EA0256802539042
    +B0916314553100N00644689EA0256202552058
    +B0916414553035N00644646EA0254402555058
    +B0916514552959N00644604EA0255402554047
    +B0917014552887N00644569EA0256302556034
    +B0917114552878N00644488EA0257402563042
    +B0917214552863N00644504EA0258402571040
    +B0917314552857N00644430EA0258602579048
    +B0917414552819N00644439EA0258602585050
    +B0917514552732N00644399EA0257402585055
    +B0918014552651N00644360EA0254602577052
    +B0918114552575N00644352EA0252502562054
    +B0918214552524N00644375EA0255302555038
    +B0918314552504N00644332EA0256302558047
    +B0918414552509N00644358EA0257902564035
    +B0918514552447N00644356EA0259702574039
    +B0919014552451N00644356EA0261702586044
    +B0919114552426N00644259EA0263302603042
    +B0919214552377N00644220EA0265402621039
    +B0919314552380N00644262EA0266502636042
    +B0919414552403N00644193EA0267402650041
    +B0919514552346N00644122EA0268202662044
    +B0920014552282N00644059EA0267202670047
    +B0920114552210N00643999EA0262702666064
    +B0920214552131N00643912EA0259502649062
    +B0920314552057N00643833EA0257402628061
    +B0920414551982N00643763EA0255002607059
    +B0920514551906N00643697EA0252702584059
    +B0921014551832N00643622EA0251302562058
    +B0921114551759N00643536EA0250002543059
    +B0921214551684N00643456EA0248902526060
    +B0921314551608N00643379EA0247402512060
    +B0921414551529N00643303EA0245802496058
    +B0921514551447N00643238EA0243502480062
    +B0922014551366N00643175EA0241402461060
    +B0922114551283N00643119EA0239502441060
    +B0922214551203N00643060EA0237402422058
    +B0922324551119N00642981EA0235402399058
    +B0922424551041N00642907EA0233302379060
    +B0922524550959N00642858EA0231002358060
    +B0923024550879N00642814EA0228802337060
    +B0923124550805N00642760EA0225902315058
    +B0923224550729N00642706EA0224202291055
    +B0923324550677N00642660EA0224102275041
    +B0923424550637N00642614EA0224202264038
    +B0923524550633N00642546EA0224402258044
    +B0924024550666N00642504EA0223902255042
    +B0924124550651N00642567EA0224002251042
    +B0924224550602N00642526EA0224102249039
    +B0924324550566N00642458EA0223302247040
    +B0924424550541N00642378EA0224402246043
    +B0924524550537N00642406EA0224602248043
    +B0925024550477N00642362EA0225902251041
    +B0925124550467N00642374EA0227002257055
    +B0925224550431N00642320EA0228202264037
    +B0925324550420N00642371EA0228602270032
    +B0925424550353N00642348EA0229402279037
    +B0925524550311N00642259EA0230502289043
    +B0926024550279N00642168EA0231602299047
    +B0926124550288N00642118EA0232802311048
    +B0926224550254N00642175EA0234102322046
    +B0926324550202N00642129EA0235402333037
    +B0926424550184N00642044EA0236102343034
    +B0926524550163N00641960EA0235902350041
    +B0927024550152N00641871EA0236802356042
    +B0927124550135N00641805EA0238802365037
    +B0927224550154N00641779EA0239702377039
    +B0927324550110N00641802EA0240902387044
    +B0927424550121N00641771EA0243102401044
    +B0927524550074N00641787EA0245102415042
    +B0928024550083N00641766EA0247602433041
    +B0928124550040N00641746EA0250002453046
    +B0928224550036N00641772EA0252502473037
    +B0928324549996N00641728EA0253502493039
    +B0928424550019N00641715EA0255902514048
    +B0928524550003N00641683EA0258302535048
    +B0929024549978N00641715EA0260802557044
    +B0929124549989N00641690EA0263102579044
    +B0929224549952N00641675EA0265002601046
    +B0929324549964N00641695EA0266802622043
    +B0929424549903N00641708EA0267202640042
    +B0929524549840N00641685EA0267702654042
    +B0930024549776N00641655EA0267802665042
    +B0930134549713N00641613EA0267602671040
    +B0930234549648N00641578EA0266102671045
    +B0930334549586N00641551EA0265102667042
    +B0930434549523N00641520EA0264902662044
    +B0930534549465N00641496EA0265902659038
    +B0931034549398N00641467EA0266902662043
    +B0931134549329N00641429EA0269902672043
    +B0931234549253N00641403EA0271402685043
    +B0931334549184N00641372EA0271702698044
    +B0931434549115N00641338EA0271402706049
    +B0931534549047N00641292EA0270102707044
    +B0932034548989N00641228EA0269202704043
    +B0932134548931N00641152EA0269502701043
    +B0932234548860N00641102EA0268802699043
    +B0932334548802N00641031EA0268502696043
    +B0932434548746N00640961EA0268702693046
    +B0932534548687N00640908EA0269202693039
    +B0933034548617N00640840EA0268302691053
    +B0933134548546N00640765EA0266902685054
    +B0933234548488N00640679EA0266202679050
    +B0933334548441N00640602EA0265702672042
    +B0933434548392N00640537EA0265602667045
    +B0933534548334N00640469EA0263902661055
    +B0934034548266N00640403EA0262902650061
    +B0934134548209N00640378EA0265302645038
    +B0934234548151N00640360EA0264602647051
    +B0934334548083N00640321EA0263002645056
    +B0934434548032N00640232EA0261502638057
    +B0934534547992N00640133EA0260302628059
    +B0935034547953N00640047EA0258102616057
    +B0935134547920N00639950EA0257002602058
    +B0935234547887N00639852EA0255702589057
    +B0935334547863N00639744EA0254002575056
    +B0935434547845N00639628EA0252302560060
    +B0935534547844N00639522EA0254102550043
    +B0936034547832N00639443EA0256802552045
    +B0936134547809N00639369EA0257102559042
    +B0936234547791N00639287EA0257202564046
    +B0936334547767N00639201EA0256002565048
    +B0936434547735N00639111EA0252302558057
    +B0936534547713N00638988EA0249502543061
    +B0937034547695N00638872EA0247002524057
    +B0937134547682N00638757EA0244602502059
    +B0937234547673N00638650EA0242102480058
    +B0937334547668N00638544EA0240502458046
    +B0937434547665N00638440EA0239002438056
    +B0937534547665N00638335EA0239102423049
    +B0938034547661N00638257EA0240402416040
    +B0938144547652N00638167EA0239402411039
    +B0938244547631N00638089EA0238302405045
    +B0938344547618N00638012EA0238702399044
    +B0938444547603N00637933EA0238602396047
    +B0938544547584N00637862EA0238602394044
    +B0939044547569N00637804EA0238502393038
    +B0939144547554N00637738EA0239102391043
    +B0939244547536N00637672EA0240802395039
    +B0939344547519N00637616EA0241102402043
    +B0939444547502N00637552EA0239402403045
    +B0939544547472N00637479EA0236002395060
    +B0940044547431N00637404EA0234102379059
    +B0940144547394N00637342EA0233702365053
    +B0940244547352N00637289EA0232802355042
    +B0940344547318N00637236EA0231102344052
    +B0940444547300N00637170EA0232102335040
    +B0940544547258N00637146EA0234602334034
    +B0941044547236N00637114EA0238202346042
    +B0941144547264N00637148EA0240302363039
    +B0941244547223N00637134EA0243802384035
    +B0941344547206N00637070EA0247602409041
    +B0941444547186N00637000EA0251002439039
    +B0941544547154N00636909EA0251102464050
    +B0942044547124N00636805EA0250602481054
    +B0942144547080N00636719EA0250602492055
    +B0942244547030N00636627EA0248502495056
    +B0942344546971N00636542EA0246702490058
    +B0942444546920N00636449EA0245002480057
    +B0942544546873N00636354EA0245002470044
    +B0943044546833N00636264EA0246202466049
    +B0943144546795N00636202EA0249902472040
    +B0943244546748N00636155EA0251302485041
    +B0943344546706N00636089EA0251702495036
    +B0943444546671N00636024EA0250902502044
    +B0943544546641N00635946EA0249102503038
    +B0944044546610N00635869EA0247102497046
    +B0944144546584N00635786EA0247402490047
    +B0944244546564N00635703EA0249802488043
    +B0944344546541N00635627EA0250802494047
    +B0944444546518N00635543EA0253002503040
    +B0944544546490N00635463EA0253402513046
    +B0945044546466N00635381EA0253302521048
    +B0945144546441N00635301EA0252802526045
    +B0945244546422N00635225EA0253002529042
    +B0945344546400N00635146EA0254002530038
    +B0945444546383N00635073EA0256202537044
    +B0945544546361N00634998EA0256702547044
    +B0946054546337N00634908EA0255702554045
    +B0946154546318N00634818EA0256002556043
    +B0946254546295N00634735EA0255602558045
    +B0946354546281N00634647EA0256402559045
    +B0946454546259N00634566EA0257102562042
    +B0946554546232N00634487EA0256702564039
    +B0947054546207N00634421EA0259202570043
    +B0947154546176N00634349EA0259702578048
    +B0947254546133N00634273EA0260902587044
    +B0947354546096N00634197EA0261302596043
    +B0947454546059N00634129EA0260202600041
    +B0947554546031N00634044EA0259802601045
    +B0948054546006N00633951EA0261702604048
    +B0948154545974N00633884EA0262702611038
    +B0948254545938N00633807EA0263002618048
    +B0948354545898N00633726EA0265302625048
    +B0948454545849N00633687EA0264102634050
    +B0948554545806N00633610EA0262302633050
    +B0949054545781N00633514EA0261502629058
    +B0949154545756N00633416EA0262702627052
    +B0949254545729N00633334EA0263002629053
    +B0949354545704N00633240EA0261402629057
    +B0949454545675N00633143EA0259102621055
    +B0949554545641N00633047EA0257502609063
    +B0950054545602N00632965EA0255502596058
    +B0950154545566N00632890EA0253402580054
    +B0950254545542N00632801EA0251102563056
    +B0950354545519N00632702EA0248702543059
    +B0950454545492N00632603EA0246402523056
    +B0950554545448N00632506EA0243802500061
    +B0951054545410N00632407EA0242002478057
    +B0951154545383N00632302EA0240302457058
    +B0951254545354N00632197EA0238302437060
    +B0951354545326N00632094EA0236302417060
    +B0951454545297N00631992EA0234502397057
    +B0951554545270N00631892EA0231702376058
    +B0952054545241N00631796EA0229502355055
    +B0952154545219N00631698EA0227102332059
    +B0952254545187N00631613EA0225202310057
    +B0952354545154N00631522EA0222902288056
    +B0952454545108N00631438EA0219602264059
    +B0952554545067N00631341EA0217502238057
    +B0953054545055N00631252EA0218802218040
    +B0953154545034N00631207EA0220702212036
    +B0953254545021N00631265EA0222902214039
    +B0953354545017N00631245EA0225802223042
    +B0953454545015N00631268EA0228102239047
    +B0953554545035N00631269EA0230502256043
    +B0954064544999N00631240EA0232502276037
    +B0954164545039N00631260EA0233502294044
    +B0954264545011N00631258EA0235202313047
    +B0954364545039N00631286EA0237502330046
    +B0954464545045N00631255EA0239402349047
    +B0954564545044N00631311EA0241802369043
    +B0955064545064N00631294EA0244202390048
    +B0955164545041N00631293EA0246802412043
    +B0955264545073N00631274EA0248702434049
    +B0955364545055N00631275EA0251402457044
    +B0955464545075N00631313EA0253802480046
    +B0955564545060N00631253EA0255602504041
    +B0956064545060N00631170EA0256602524046
    +B0956164545059N00631097EA0257902542043
    +B0956264545052N00631020EA0257702557046
    +B0956364545044N00630941EA0258002566046
    +B0956464545035N00630852EA0259002574047
    +B0956564545027N00630778EA0260802584037
    +B0957064545019N00630698EA0258902591046
    +B0957164545006N00630610EA0257302590048
    +B0957264544996N00630516EA0257102585042
    +B0957364544989N00630423EA0255402579040
    +B0957464544979N00630347EA0255202571046
    +B0957564544966N00630268EA0256702568046
    +B0958064544937N00630218EA0256602568040
    +B0958164544889N00630234EA0258802571040
    +B0958264544851N00630215EA0261002581038
    +B0958364544820N00630159EA0260402591044
    +B0958464544790N00630103EA0260002597040
    +B0958564544757N00630040EA0258102596041
    +B0959064544727N00629965EA0255802589043
    +B0959164544699N00629887EA0254202578044
    +B0959264544671N00629820EA0253902567038
    +B0959364544649N00629750EA0255102559036
    +B0959464544636N00629693EA0257402560038
    +B0959564544629N00629625EA0256602563046
    +B1000064544661N00629540EA0259102567047
    +B1000164544700N00629439EA0261502579045
    +B1000264544722N00629325EA0261802592047
    +B1000364544732N00629214EA0262502602030
    +B1000464544731N00629111EA0261702608044
    +B1000564544708N00629022EA0259802610044
    +B1001064544670N00628946EA0256902603058
    +B1001164544631N00628853EA0254202590059
    +B1001264544582N00628767EA0252102574060
    +B1001364544541N00628679EA0251602557048
    +B1001474544501N00628592EA0250702542051
    +B1001574544467N00628505EA0249502529052
    +B1002074544435N00628409EA0248302516055
    +B1002174544406N00628307EA0246802503055
    +B1002274544379N00628202EA0244302488056
    +B1002374544359N00628082EA0242002470056
    +B1002474544345N00627960EA0240102452056
    +B1002574544335N00627837EA0238102433057
    +B1003074544326N00627709EA0236702415056
    +B1003174544313N00627585EA0235302398059
    +B1003274544290N00627465EA0233902381059
    +B1003374544269N00627346EA0232602365051
    +B1003474544247N00627240EA0231602352049
    +B1003574544226N00627136EA0230502339048
    +B1004074544208N00627031EA0229402328047
    +B1004174544195N00626925EA0228202316047
    +B1004274544176N00626822EA0227102305048
    +B1004374544152N00626721EA0226102293047
    +B1004474544130N00626617EA0224802281048
    +B1004574544110N00626510EA0223702270047
    +B1005074544092N00626401EA0222302258047
    +B1005174544080N00626290EA0220502245048
    +B1005274544069N00626180EA0218602230047
    +B1005374544061N00626069EA0217102215047
    +B1005474544051N00625960EA0215702199047
    +B1005574544044N00625850EA0214202184049
    +B1006074544036N00625744EA0212702169047
    +B1006174544028N00625641EA0211102154047
    +B1006274544019N00625537EA0209602138047
    +B1006374544007N00625433EA0208202123047
    +B1006474543995N00625329EA0207002109047
    +B1006574543984N00625225EA0205902095049
    +B1007074543974N00625121EA0204702083046
    +B1007174543955N00625023EA0203402070047
    +B1007274543938N00624923EA0202202057047
    +B1007374543922N00624824EA0201202045047
    +B1007474543911N00624725EA0200302034049
    +B1007574543894N00624628EA0199202024047
    +B1008074543871N00624533EA0198102013048
    +B1008174543848N00624440EA0196802001047
    +B1008274543827N00624348EA0195501989048
    +B1008374543810N00624256EA0194301977046
    +B1008474543794N00624162EA0193201965046
    +B1008574543779N00624066EA0192101953047
    +B1009074543760N00623970EA0191101942046
    +B1009174543739N00623874EA0190101931047
    +B1009274543716N00623782EA0189101920045
    +B1009384543687N00623682EA0188001908047
    +B1009484543664N00623590EA0186901897047
    +B1009584543641N00623498EA0185601887048
    +B1010084543601N00623399EA0184201875057
    +B1010184543561N00623310EA0183301863043
    +B1010284543541N00623220EA0182001851048
    +B1010384543526N00623127EA0180801839048
    +B1010484543511N00623025EA0179401827052
    +B1010584543490N00622923EA0178101814053
    +B1011084543470N00622820EA0176701801053
    +B1011184543453N00622717EA0175201787053
    +B1011284543430N00622614EA0173801773053
    +B1011384543409N00622510EA0172201758053
    +B1011484543389N00622406EA0170701744053
    +B1011584543370N00622302EA0169101729053
    +B1012084543348N00622201EA0167801714053
    +B1012184543326N00622100EA0166501699053
    +B1012284543301N00622002EA0164901685053
    +B1012384543280N00621912EA0165001672035
    +B1012484543279N00621841EA0165401666036
    +B1012584543297N00621860EA0165001661043
    +B1013084543262N00621827EA0165401658037
    +B1013184543292N00621823EA0164801656037
    +B1013284543255N00621815EA0164101652039
    +B1013384543232N00621736EA0163201649039
    +B1013484543186N00621678EA0161401642040
    +B1013584543156N00621609EA0160701632032
    +B1014084543127N00621576EA0162801627041
    +B1014184543159N00621585EA0164501628040
    +B1014284543132N00621566EA0165401633039
    +B1014384543140N00621629EA0165401640037
    +B1014484543160N00621584EA0165901644035
    +B1014584543132N00621522EA0167001649042
    +B1015084543120N00621556EA0168701657032
    +B1015184543154N00621528EA0170301666040
    +B1015284543119N00621527EA0171201678036
    +B1015384543142N00621581EA0172501689037
    +B1015484543169N00621541EA0173301701041
    +B1015584543126N00621524EA0174201711042
    +B1016084543121N00621593EA0175401722037
    +B1016184543153N00621569EA0175201731042
    +B1016284543155N00621497EA0175301738039
    +B1016384543155N00621420EA0175701744035
    +B1016484543183N00621445EA0176301750035
    +B1016584543165N00621426EA0176601756043
    +B1017084543208N00621466EA0177101760037
    +B1017184543184N00621524EA0177901765039
    +B1017294543189N00621478EA0178501771038
    +B1017394543238N00621506EA0179501778041
    +B1017494543226N00621547EA0179901786043
    +B1017594543245N00621502EA0181301793039
    +B1018094543255N00621535EA0182801802043
    +B1018194543220N00621493EA0183601811039
    +B1018294543191N00621428EA0184001819045
    +B1018394543159N00621362EA0183601824041
    +B1018494543125N00621299EA0181601825045
    +B1018594543096N00621231EA0180201821043
    +B1019094543052N00621148EA0177701811053
    +B1019194543007N00621060EA0176001797058
    +B1019294542959N00620976EA0174101783056
    +B1019394542903N00620899EA0172201767047
    +B1019494542857N00620842EA0171201750036
    +B1019594542830N00620787EA0173101739033
    +B1020094542816N00620810EA0173301735033
    +B1020194542871N00620802EA0174101736038
    +B1020294542855N00620741EA0174801739041
    +B1020394542817N00620716EA0174701742041
    +B1020494542782N00620681EA0174601745026
    +B1020594542738N00620638EA0174801745040
    +B1021094542704N00620585EA0178101749036
    +B1021194542687N00620624EA0179701761036
    +B1021294542732N00620634EA0181101774038
    +B1021394542715N00620657EA0182601789034
    +B1021494542720N00620595EA0184501804038
    +B1021594542723N00620627EA0186701821040
    +B1022094542699N00620575EA0188301839044
    +B1022194542708N00620610EA0190601856044
    +B1022294542714N00620590EA0192201874048
    +B1022394542693N00620567EA0193601890045
    +B1022494542701N00620601EA0194601906037
    +B1022594542651N00620579EA0195401920044
    +B1023094542681N00620541EA0196001932039
    +B1023194542670N00620580EA0196501942041
    +B1023294542610N00620558EA0196701951039
    +B1023394542552N00620521EA0194701955045
    +B1023494542494N00620482EA0193201952041
    +B1023594542437N00620444EA0192201945044
    +B1024094542384N00620394EA0192901938041
    +B1024194542334N00620340EA0193601936048
    +B1024294542285N00620294EA0193301935050
    +B1024394542230N00620253EA0193201933043
    +B1024494542188N00620203EA0194501933034
    +B1024594542148N00620171EA0195201937047
    +B1025094542101N00620135EA0194901941044
    +B1025204542051N00620090EA0193901941048
    +B1025304542009N00620057EA0193201939043
    +B1025404541959N00620029EA0191701934042
    +B1025504541907N00620007EA0190101925049
    +B1026004541861N00619979EA0190401917036
    +B1026104541813N00619954EA0191001913040
    +B1026204541765N00619928EA0190501910038
    +B1026304541715N00619887EA0190201907038
    +B1026404541669N00619851EA0191301905031
    +B1026504541627N00619806EA0191101907038
    +B1027004541589N00619757EA0191901908042
    +B1027104541578N00619700EA0195401915041
    +B1027204541578N00619720EA0198901932047
    +B1027304541585N00619726EA0201901953051
    +B1027404541597N00619719EA0205401978046
    +B1027504541578N00619683EA0208402005040
    +B1028004541609N00619704EA0210902033040
    +B1028104541587N00619680EA0213502061044
    +B1028204541596N00619718EA0216402087035
    +B1028304541549N00619700EA0218402114037
    +B1028404541576N00619671EA0219602138041
    +B1028504541618N00619666EA0222202161037
    +B1029004541585N00619609EA0223202183045
    +B1029104541547N00619546EA0222202199042
    +B1029204541515N00619471EA0219702203051
    +B1029304541480N00619388EA0217602198062
    +B1029404541444N00619290EA0216102188058
    +B1029504541420N00619186EA0214902176057
    +B1030004541395N00619084EA0212502163056
    +B1030104541363N00618972EA0210302146058
    +B1030204541333N00618890EA0209402130045
    +B1030304541306N00618814EA0208002117046
    +B1030404541279N00618723EA0205502103051
    +B1030504541271N00618634EA0203902087041
    +B1031004541239N00618560EA0202502071051
    +B1031104541208N00618485EA0201302056049
    +B1031204541182N00618406EA0200002041046
    +B1031304541159N00618325EA0199102026042
    +B1031404541138N00618245EA0197902013047
    +B1031504541105N00618176EA0196802000044
    +B1032004541070N00618108EA0195001987056
    +B1032104541033N00618019EA0193501972056
    +B1032204540994N00617933EA0191701956058
    +B1032304540946N00617850EA0189901938055
    +B1032404540902N00617765EA0187901920056
    +B1032504540857N00617678EA0186201902055
    +B1033004540812N00617589EA0184301884056
    +B1033114540758N00617500EA0181701863057
    +B1033214540709N00617432EA0180201844053
    +B1033314540670N00617376EA0179801828042
    +B1033414540625N00617311EA0180401817038
    +B1033514540581N00617238EA0181201814043
    +B1034014540552N00617158EA0180401811043
    +B1034114540533N00617082EA0182001811039
    +B1034214540493N00617088EA0183301816037
    +B1034314540506N00617129EA0184701823035
    +B1034414540518N00617073EA0186601835039
    +B1034514540482N00617086EA0188301849030
    +B1035014540505N00617136EA0190101863039
    +B1035114540516N00617085EA0192801880038
    +B1035214540486N00617110EA0195401901041
    +B1035314540518N00617152EA0198601926034
    +B1035414540551N00617094EA0199201950037
    +B1035514540513N00617033EA0201001968037
    +B1036014540505N00617064EA0204201987042
    +B1036114540491N00617029EA0207202011048
    +B1036214540520N00617033EA0210302037041
    +B1036314540489N00617024EA0213002064041
    +B1036414540530N00617038EA0215402090044
    +B1036514540535N00616968EA0216402117036
    +B1037014540477N00616977EA0217402135039
    +B1037114540459N00616936EA0218402151042
    +B1037214540457N00616964EA0219602166045
    +B1037314540416N00616897EA0218702176045
    +B1037414540372N00616831EA0216402177054
    +B1037514540317N00616745EA0214302169060
    +B1038014540263N00616658EA0212302157057
    +B1038114540203N00616580EA0210102142057
    +B1038214540150N00616495EA0207302123065
    +B1038314540089N00616404EA0205002102065
    +B1038414540022N00616322EA0202502079064
    +B1038514539954N00616242EA0200302056063
    +B1039014539890N00616162EA0198002033061
    +B1039114539830N00616097EA0196402011055
    +B1039214539771N00616037EA0194401990055
    +B1039314539711N00615981EA0192801970051
    +B1039414539666N00615942EA0191701954042
    +B1039514539621N00615900EA0190501938043
    +B1040014539573N00615876EA0189901924040
    +B1040114539526N00615848EA0191301916032
    +B1040214539501N00615864EA0190601914036
    +B1040314539546N00615838EA0191801913032
    +B1040424539562N00615861EA0191601915040
    +B1040524539550N00615815EA0192401919048
    +B1041024539546N00615859EA0192901923038
    +B1041124539526N00615823EA0192901927039
    +B1041224539556N00615848EA0193801931042
    +B1041324539547N00615813EA0193501934043
    +B1041424539557N00615851EA0193801936039
    +B1041524539511N00615868EA0192901937040
    +B1042024539458N00615844EA0193101935038
    +B1042124539421N00615797EA0193701936042
    +B1042224539433N00615816EA0194001937044
    +B1042324539388N00615829EA0195201941041
    +B1042424539392N00615862EA0195301946037
    +B1042524539403N00615788EA0195601949034
    +B1043024539357N00615746EA0195401952041
    +B1043124539301N00615740EA0192801949045
    +B1043224539236N00615751EA0190501938052
    +B1043324539173N00615758EA0189501924048
    +B1043424539124N00615764EA0190501916037
    +B1043524539077N00615773EA0189001911042
    +B1044024539019N00615778EA0188601903040
    +B1044124538962N00615772EA0187801896039
    +B1044224538918N00615741EA0188901893041
    +B1044324538904N00615659EA0189901893035
    +B1044424538868N00615630EA0190101896041
    +B1044524538852N00615675EA0189901898038
    +B1045024538894N00615711EA0189901899044
    +B1045124538878N00615745EA0189701901039
    +B1045224538874N00615675EA0190101902045
    +B1045324538877N00615705EA0190801904037
    +B1045424538831N00615724EA0190901907041
    +B1045524538794N00615668EA0191001908038
    +B1046024538843N00615666EA0190101907039
    +B1046124538865N00615722EA0189401905042
    +B1046224538877N00615784EA0188601900036
    +B1046324538850N00615787EA0188301896043
    +B1046424538895N00615772EA0188401891041
    +B1046524538935N00615800EA0189201891043
    +B1047024538914N00615741EA0191101895040
    +B1047124538901N00615791EA0191501902039
    +B1047224538935N00615760EA0191801905042
    +B1047324538907N00615700EA0193301912032
    +B1047424538882N00615734EA0192601918040
    +B1047524538920N00615762EA0192801923036
    +B1048024538938N00615688EA0193901927035
    +B1048124538913N00615674EA0194901933043
    +B1048224538949N00615662EA0196201942041
    +B1048334538918N00615623EA0197001952042
    +B1048434538942N00615649EA0197701960036
    +B1048534538952N00615573EA0198201967037
    +B1049034538905N00615535EA0196701971042
    +B1049134538836N00615492EA0193501964058
    +B1049234538763N00615447EA0190601949054
    +B1049334538691N00615412EA0188901932057
    +B1049434538619N00615382EA0187901915056
    +B1049534538542N00615355EA0186401900061
    +B1050034538469N00615336EA0184501883055
    +B1050134538392N00615329EA0183501867053
    +B1050234538315N00615324EA0182201853056
    +B1050334538238N00615323EA0181901839046
    +B1050434538183N00615317EA0183001834036
    +B1050534538126N00615314EA0182301831044
    +B1051034538070N00615306EA0183601829039
    +B1051134538013N00615305EA0181801826042
    +B1051234537950N00615315EA0181401821041
    +B1051334537893N00615309EA0181301817043
    +B1051434537845N00615286EA0183401817031
    +B1051534537800N00615254EA0183001821040
    +B1052034537748N00615218EA0183301824041
    +B1052134537703N00615166EA0184601827037
    +B1052234537669N00615103EA0187401834039
    +B1052334537642N00615039EA0187901844037
    +B1052434537620N00614968EA0188101853037
    +B1052534537591N00614898EA0186801859040
    +B1053034537555N00614827EA0185501860044
    +B1053134537522N00614755EA0187101860035
    +B1053234537489N00614693EA0187101863039
    +B1053334537450N00614632EA0185701864043
    +B1053434537409N00614575EA0183701859037
    +B1053534537365N00614512EA0184601851029
    +B1054034537332N00614485EA0185201850040
    +B1054134537355N00614537EA0188201853046
    +B1054234537353N00614516EA0190401863039
    +B1054334537347N00614536EA0192701880043
    +B1054434537379N00614500EA0194601898043
    +B1054534537353N00614526EA0196201917041
    +B1055034537378N00614523EA0198001936041
    +B1055134537380N00614481EA0199801953039
    +B1055234537399N00614505EA0200601973045
    +B1055334537365N00614458EA0201401985041
    +B1055434537324N00614405EA0201601997039
    +B1055534537273N00614338EA0200702003042
    +B1056034537225N00614287EA0199002003045
    +B1056144537171N00614228EA0197401995042
    +B1056244537126N00614168EA0196801986045
    +B1056344537095N00614108EA0198601981040
    +B1056444537058N00614046EA0199601984042
    +B1056544537024N00613980EA0199601987038
    +B1057044536995N00613922EA0198901989043
    +B1057144536963N00613860EA0198401987042
    +B1057244536930N00613802EA0199401986039
    +B1057344536898N00613745EA0199501988043
    +B1057444536870N00613687EA0200501989034
    +B1057544536846N00613623EA0200501991038
    +B1058044536823N00613553EA0202101997044
    +B1058144536794N00613487EA0203102004038
    +B1058244536764N00613423EA0201702010043
    +B1058344536727N00613357EA0201802011041
    +B1058444536699N00613296EA0202602013038
    +B1058544536672N00613243EA0204902019034
    +B1059044536642N00613221EA0206802031043
    +B1059144536670N00613252EA0209002045042
    +B1059244536656N00613199EA0210802064044
    +B1059344536646N00613249EA0212102081040
    +B1059444536686N00613251EA0213502098040
    +B1059544536681N00613177EA0216002114040
    +B1100044536661N00613116EA0219202134037
    +B1100144536632N00613049EA0221802158040
    +B1100244536604N00612961EA0219802174045
    +B1100344536575N00612887EA0217902180036
    +B1100444536541N00612814EA0215902176044
    +B1100544536510N00612736EA0214602168043
    +B1101044536480N00612656EA0215302160036
    +B1101144536457N00612598EA0219102162034
    +B1101244536450N00612631EA0221702174041
    +B1101344536480N00612572EA0225802193039
    +B1101444536448N00612562EA0229702220037
    +B1101544536478N00612606EA0231302249035
    +B1102044536514N00612547EA0233802272028
    +B1102144536534N00612498EA0236902299052
    +B1102244536516N00612499EA0242002327034
    +B1102344536550N00612492EA0244202359043
    +B1102444536552N00612469EA0246702391055
    +B1102544536531N00612459EA0250202423043
    +B1103044536545N00612466EA0253502453039
    +B1103144536561N00612397EA0255702483045
    +B1103244536558N00612417EA0258702511034
    +B1103344536532N00612337EA0260602539042
    +B1103454536502N00612239EA0259202561041
    +B1103554536476N00612143EA0258502570043
    +B1104054536449N00612051EA0258702574053
    +B1104154536425N00611960EA0259102579039
    +B1104254536383N00611881EA0257602581056
    +B1104354536324N00611801EA0256402576057
    +B1104454536272N00611719EA0254702569057
    +B1104554536228N00611627EA0254802561045
    +B1105054536190N00611525EA0253402554054
    +B1105154536152N00611420EA0252402545053
    +B1105254536111N00611321EA0252102536048
    +B1105354536074N00611228EA0251802530049
    +B1105454536038N00611122EA0250202523056
    +B1105554536002N00611015EA0248602512057
    +B1106054535965N00610906EA0247402499053
    +B1106154535938N00610799EA0246402488056
    +B1106254535907N00610690EA0245402476054
    +B1106354535882N00610610EA0247002469041
    +B1106454535922N00610596EA0247102468048
    +B1106554535881N00610577EA0248002469041
    +B1107054535860N00610495EA0247202470042
    +B1107154535851N00610407EA0247802470043
    +B1107254535835N00610306EA0248002473042
    +B1107354535852N00610220EA0249402476048
    +B1107454535836N00610222EA0250402481043
    +B1107554535793N00610140EA0251202490044
    +B1108054535771N00610043EA0251802498041
    +B1108154535752N00609942EA0251602504042
    +B1108254535736N00609853EA0249802507036
    +B1108354535718N00609754EA0248002503044
    +B1108454535702N00609660EA0245702494043
    +B1108554535700N00609560EA0242602479046
    +B1109054535695N00609447EA0239802459057
    +B1109154535682N00609314EA0236702437063
    +B1109254535640N00609200EA0234202412059
    +B1109354535619N00609071EA0231802388058
    +B1109454535614N00608929EA0229702363057
    +B1109554535626N00608794EA0227502339058
    +B1110054535637N00608657EA0225502315054
    +B1110154535648N00608522EA0223502292057
    +B1110254535661N00608389EA0221602269059
    +B1110354535672N00608259EA0219702248058
    +B1110454535667N00608125EA0217302227054
    +B1110554535670N00607998EA0215502206059
    +B1111054535664N00607874EA0213802187054
    +B1111154535667N00607748EA0211302166058
    +B1111264535671N00607611EA0209202143060
    +B1111364535684N00607486EA0206502121060
    +B1111464535691N00607360EA0205002098059
    +B1111564535683N00607266EA0204302080043
    +B1112064535684N00607178EA0202402063043
    +B1112164535690N00607090EA0199102044053
    +B1112264535700N00606994EA0196902022047
    +B1112364535726N00606957EA0198102005037
    +B1112464535755N00607008EA0199701999043
    +B1112564535715N00606994EA0199101996028
    +B1113064535689N00606935EA0200901992031
    +B1113164535698N00606942EA0203301998048
    +B1113264535665N00606926EA0205302010037
    +B1113364535679N00606978EA0205902023048
    +B1113464535631N00606982EA0205702034042
    +B1113564535638N00606925EA0205302041037
    +B1114064535658N00606871EA0205802044034
    +B1114164535680N00606833EA0207102051036
    +B1114264535668N00606858EA0209602060046
    +B1114364535694N00606841EA0212102073043
    +B1114464535658N00606852EA0214002090041
    +B1114564535687N00606824EA0216402109036
    +B1115064535668N00606867EA0218002129042
    +B1115164535664N00606819EA0219302145038
    +B1115264535696N00606859EA0220602164039
    +B1115364535730N00606910EA0222902182035
    +B1115464535700N00606932EA0223802199040
    +B1115564535724N00606892EA0226102213035
    +B1116064535709N00606926EA0227702231039
    +B1116164535731N00606882EA0229902249034
    +B1116264535718N00606929EA0231702268038
    +B1116364535723N00606872EA0232502285042
    +B1116464535727N00606920EA0233702300044
    +B1116564535723N00606872EA0234002312042
    +B1117064535749N00606906EA0236502324046
    +B1117164535707N00606904EA0236902338040
    +B1117264535747N00606863EA0237602348039
    +B1117364535743N00606907EA0238802358047
    +B1117464535734N00606854EA0240402370039
    +B1117564535740N00606896EA0241502382046
    +B1118064535696N00606846EA0240802393047
    +B1118164535668N00606749EA0239502397044
    +B1118264535640N00606654EA0238702396043
    +B1118364535610N00606552EA0236402391044
    +B1118464535551N00606475EA0234902382045
    +B1118574535484N00606385EA0233002367056
    +B1119074535415N00606310EA0231502352059
    +B1119174535346N00606227EA0230502338060
    +B1119274535277N00606139EA0229302325058
    +B1119374535213N00606051EA0227702310058
    +B1119474535143N00605977EA0226102296058
    +B1119574535077N00605889EA0224802281058
    +B1120074535017N00605795EA0223302266058
    +B1120174534958N00605699EA0221702251058
    +B1120274534899N00605605EA0220002236059
    +B1120374534838N00605509EA0218402220058
    +B1120474534772N00605421EA0217002204058
    +B1120574534710N00605326EA0215802189058
    +B1121074534646N00605234EA0214902175058
    +B1121174534583N00605140EA0214002162058
    +B1121274534523N00605043EA0212802150058
    +B1121374534459N00604954EA0211402137057
    +B1121474534396N00604861EA0210002124058
    +B1121574534334N00604767EA0208502111057
    +B1122074534280N00604691EA0207802098045
    +B1122174534235N00604618EA0207202087044
    +B1122274534192N00604537EA0206002077043
    +B1122374534146N00604457EA0204502066044
    +B1122474534098N00604385EA0203502054044
    +B1122574534044N00604327EA0202802044045
    +B1123074533989N00604267EA0202702035042
    +B1123174533944N00604202EA0203302030034
    +B1123274533969N00604164EA0205002030040
    +B1123374533940N00604186EA0206202036037
    +B1123474533955N00604135EA0206702043040
    +B1123574533983N00604176EA0208902052035
    +B1124074533936N00604161EA0210402063045
    +B1124174533953N00604160EA0212302076036
    +B1124274533916N00604131EA0212602089040
    +B1124374533959N00604100EA0214202101042
    +B1124474533937N00604104EA0215502113045
    +B1124574533954N00604098EA0216802127040
    +B1125074533919N00604039EA0217202139039
    +B1125174533891N00603953EA0216402149042
    +B1125274533862N00603958EA0213202150048
    +B1125374533902N00604017EA0212402142050
    +B1125474533929N00604082EA0212002134048
    +B1125574533949N00604148EA0213802130037
    +B1126074533982N00604169EA0213402132042
    +B1126174533973N00604087EA0213302131041
    +B1126274533918N00604058EA0213202130048
    +B1126374533951N00604042EA0213402130039
    +B1126484533912N00604085EA0213602131040
    +B1126584533848N00604084EA0212602131042
    +B1127084533778N00604050EA0211102126044
    +B1127184533719N00603990EA0209102117046
    +B1127284533662N00603930EA0207602105043
    +B1127384533608N00603873EA0205902091045
    +B1127484533551N00603827EA0204402076042
    +B1127584533491N00603793EA0202602061045
    +B1128084533430N00603750EA0202202046040
    +B1128184533376N00603707EA0201602035043
    +B1128284533324N00603658EA0201002025044
    +B1128384533276N00603621EA0201702017033
    +B1128484533244N00603628EA0202402016037
    +B1128584533287N00603645EA0203802019040
    +B1129084533284N00603565EA0204402023035
    +B1129184533241N00603517EA0203702027042
    +B1129284533189N00603502EA0202802027033
    +B1129384533136N00603499EA0202702025040
    +B1129484533083N00603520EA0203702024037
    +B1129584533083N00603576EA0202702025046
    +B1130084533134N00603610EA0204302027044
    +B1130184533170N00603567EA0205302032039
    +B1130284533145N00603511EA0206802038037
    +B1130384533099N00603522EA0208702048034
    +B1130484533114N00603564EA0210102060038
    +B1130584533138N00603507EA0211002073043
    +B1131084533095N00603455EA0212402085039
    +B1131184533077N00603497EA0212802096039
    +B1131284533111N00603510EA0213402106029
    +B1131384533143N00603435EA0213102113039
    +B1131484533107N00603361EA0213002118041
    +B1131584533063N00603296EA0209802117045
    +B1132084533021N00603192EA0206702105056
    +B1132184532968N00603086EA0203702087062
    +B1132284532922N00602971EA0201302066060
    +B1132384532876N00602857EA0198902043059
    +B1132484532814N00602764EA0196402019058
    +B1132584532746N00602684EA0193801995057
    +B1133084532683N00602603EA0190601970056
    +B1133184532630N00602511EA0189501944046
    +B1133284532588N00602446EA0188301923041
    +B1133384532542N00602385EA0187901906043
    +B1133484532492N00602325EA0187301893044
    +B1133584532447N00602260EA0186601882043
    +B1134084532405N00602189EA0185801872042
    +B1134184532363N00602117EA0184901862042
    +B1134294532312N00602051EA0184101852043
    +B1134394532261N00602001EA0182901843045
    +B1134494532211N00601941EA0182001833043
    +B1134594532161N00601886EA0180701824042
    +B1135094532112N00601857EA0179901814043
    +B1135194532078N00601826EA0179701806040
    +B1135294532054N00601786EA0179201799041
    +B1135394532036N00601718EA0178701794045
    +B1135494532064N00601677EA0177001788039
    +B1135594532027N00601698EA0175101776037
    +B1136094531982N00601669EA0173301763045
    +B1136194531931N00601620EA0171101748042
    +B1136294531867N00601565EA0167801729056
    +B1136394531796N00601520EA0164901707056
    +B1136494531729N00601454EA0162701682058
    +B1136594531666N00601381EA0160901660057
    +B1137094531607N00601321EA0159901640047
    +B1137194531559N00601265EA0159201624043
    +B1137294531516N00601199EA0158401610043
    +B1137394531469N00601142EA0157101598042
    +B1137494531425N00601077EA0155701584044
    +B1137594531385N00600999EA0154901571047
    +B1138094531338N00600930EA0153901559045
    +B1138194531289N00600860EA0152501546046
    +B1138294531247N00600787EA0150901533046
    +B1138394531203N00600713EA0149601519048
    +B1138494531158N00600641EA0148201506042
    +B1138594531110N00600583EA0146701495046
    +B1139094531063N00600521EA0145301482047
    +B1139194531014N00600450EA0143001466059
    +B1139294530957N00600368EA0140901447059
    +B1139394530897N00600298EA0138901428058
    +B1139494530838N00600225EA0137001408057
    +B1139594530785N00600155EA0135401389057
    +B1140094530729N00600080EA0133701371058
    +B1140194530669N00600014EA0132001353058
    +B1140294530609N00559952EA0130301339056
    +B1140394530548N00559889EA0128801321057
    +B1140494530492N00559822EA0127601303053
    +B1140594530445N00559754EA0126301286052
    +B1141094530397N00559688EA0124901271051
    +B1141194530345N00559628EA0123201255053
    +B1141294530293N00559569EA0121601239051
    +B1141394530240N00559508EA0120301224052
    +B1141494530179N00559458EA0118501209053
    +B1141594530118N00559406EA0117001193054
    +B1142094530063N00559344EA0115401177050
    +B1142204530002N00559281EA0114301161053
    +B1142304529945N00559229EA0113301148051
    +B1142404529886N00559179EA0112001135052
    +B1142504529825N00559129EA0110701122052
    +B1143004529768N00559076EA0109401109050
    +B1143104529714N00559019EA0108101096052
    +B1143204529658N00558962EA0106801083051
    +B1143304529602N00558904EA0105101069049
    +B1143404529535N00558863EA0103801054052
    +B1143504529477N00558814EA0102001039051
    +B1144004529421N00558765EA0099701022052
    +B1144104529367N00558729EA0097501004040
    +B1144204529314N00558680EA0094400982046
    +B1144304529258N00558634EA0091600959050
    +B1144404529200N00558584EA0088800933045
    +B1144504529141N00558540EA0085900906046
    +B1145004529081N00558486EA0084600881048
    +B1145104529024N00558440EA0084300863034
    +B1145204528974N00558406EA0082900850040
    +B1145304528923N00558360EA0081600835040
    +B1145404528868N00558318EA0079900820038
    +B1145504528815N00558267EA0078500804038
    +B1146004528763N00558210EA0077900791038
    +B1146104528711N00558155EA0077200780040
    +B1146204528676N00558096EA0077600773033
    +B1146304528678N00558124EA0077500769038
    +B1146404528617N00558125EA0078100766035
    +B1146504528619N00558086EA0078300766035
    +B1147004528618N00558136EA0078600767037
    +B1147104528564N00558120EA0078900768031
    +B1147204528570N00558088EA0079300770035
    +B1147304528564N00558145EA0080500775037
    +B1147404528519N00558117EA0080800781031
    +B1147504528533N00558108EA0081700785036
    +B1148004528488N00558144EA0082600794035
    +B1148104528468N00558089EA0083200800037
    +B1148204528486N00558101EA0083900805031
    +B1148304528444N00558148EA0084400813037
    +B1148404528417N00558096EA0085800819034
    +B1148504528405N00558134EA0086600826040
    +B1149004528370N00558088EA0087700834036
    +B1149104528384N00558078EA0088700841039
    +B1149204528335N00558089EA0089800853035
    +B1149304528336N00558042EA0090300862037
    +B1149404528327N00558092EA0091900873041
    +B1149504528281N00558062EA0091800883037
    +B1150014528308N00558033EA0092500889041
    +B1150114528317N00558001EA0094900898037
    +B1150214528268N00558002EA0096400911036
    +B1150314528278N00558043EA0098000926042
    +B1150414528291N00557997EA0098700941036
    +B1150514528273N00557930EA0099300953039
    +B1151014528286N00557938EA0101100966037
    +B1151114528243N00557923EA0102800979034
    +B1151214528262N00557933EA0104800995040
    +B1151314528218N00557926EA0107101011031
    +B1151414528245N00557895EA0107501027039
    +B1151514528244N00557951EA0108701041032
    +B1152014528201N00557933EA0110801055035
    +B1152114528226N00557905EA0111001068038
    +B1152214528225N00557963EA0112301079037
    +B1152314528192N00557941EA0113901091036
    +B1152414528221N00557977EA0114501101045
    +B1152514528186N00557993EA0116101114042
    +B1153014528217N00557981EA0116401125044
    +B1153114528202N00558050EA0117201135047
    +B1153214528178N00558007EA0118301143038
    +B1153314528211N00558036EA0118401152046
    +B1153414528175N00558063EA0119001159042
    +B1153514528151N00558011EA0119701165039
    +B1154014528173N00558029EA0120401172043
    +B1154114528136N00558017EA0120901178043
    +B1154214528111N00557964EA0120201183038
    +B1154314528075N00557915EA0118901183040
    +B1154414528039N00557868EA0116801179042
    +B1154514527990N00557836EA0113201167054
    +B1155014527920N00557822EA0110101147055
    +B1155114527846N00557802EA0109601125044
    +B1155214527816N00557753EA0110501110033
    +B1155314527837N00557747EA0110001102042
    +B1155414527797N00557785EA0109701096035
    +B1155514527743N00557757EA0111001091035
    +B1156014527734N00557719EA0111501092043
    +B1156114527723N00557778EA0112701096036
    +B1156214527684N00557744EA0113301102039
    +B1156314527676N00557679EA0113501109039
    +B1156414527677N00557603EA0115201115032
    +B1156514527679N00557636EA0115401123042
    +B1157014527662N00557573EA0117101130039
    +B1157114527665N00557610EA0117401137033
    +B1157214527630N00557553EA0119701146034
    +B1157314527593N00557523EA0120501159037
    +B1157424527625N00557529EA0122001173037
    +B1157524527619N00557463EA0124001189040
    +B1158024527631N00557490EA0125701205037
    +B1158124527664N00557437EA0126901222040
    +B1158224527628N00557397EA0126501233038
    +B1158324527574N00557397EA0128101242041
    +B1158424527570N00557331EA0129101252043
    +B1158524527586N00557345EA0128801262042
    +B1159024527544N00557305EA0129601268035
    +B1159124527542N00557215EA0129501274044
    +B1159224527548N00557118EA0126201274056
    +B1159324527553N00557000EA0125001263053
    +B1159424527567N00556915EA0126401258037
    +B1159524527570N00556933EA0127901257035
    +B1200024527574N00556858EA0129301261038
    +B1200124527613N00556867EA0131001270037
    +B1200224527587N00556855EA0132301282045
    +B1200324527622N00556845EA0134501295045
    +B1200424527624N00556811EA0136601308038
    +B1200524527655N00556835EA0138201324042
    +B1201024527625N00556811EA0140001340038
    +B1201124527660N00556804EA0142001356039
    +B1201224527623N00556798EA0144301373041
    +B1201324527652N00556765EA0146501392039
    +B1201424527634N00556778EA0148501414047
    +B1201524527661N00556787EA0150801435037
    +B1202024527641N00556762EA0152801457036
    +B1202124527662N00556783EA0154601479041
    +B1202224527629N00556754EA0156001500039
    +B1202324527656N00556754EA0157401518045
    +B1202424527620N00556708EA0158801535041
    +B1202524527597N00556622EA0160501551037
    +B1203024527558N00556567EA0160801566045
    +B1203124527518N00556517EA0159301575043
    +B1203224527479N00556466EA0158901578033
    +B1203324527436N00556414EA0159401580038
    +B1203424527391N00556383EA0160201584040
    +B1203524527419N00556423EA0159401587043
    +B1204024527469N00556456EA0159201588041
    +B1204124527512N00556502EA0157601587040
    +B1204224527553N00556546EA0157101582039
    +B1204324527597N00556589EA0157901576038
    +B1204424527640N00556621EA0160701575040
    +B1204524527624N00556599EA0163501583036
    +B1205034527629N00556608EA0165701600046
    +B1205134527647N00556575EA0168401618046
    +B1205234527624N00556565EA0171301640040
    +B1205334527655N00556571EA0174601665043
    +B1205434527659N00556547EA0178401694050
    +B1205534527638N00556548EA0182301727050
    +B1206034527644N00556556EA0185501760051
    +B1206134527663N00556534EA0189201793047
    +B1206234527643N00556508EA0192301827040
    +B1206334527672N00556486EA0195401858044
    +B1206434527634N00556465EA0198101890044
    +B1206534527656N00556457EA0201801922042
    +B1207034527634N00556420EA0203501952042
    +B1207134527631N00556458EA0205301980037
    +B1207234527575N00556443EA0203802001042
    +B1207334527517N00556435EA0202602011042
    +B1207434527453N00556430EA0200502011053
    +B1207534527382N00556415EA0198602005056
    +B1208034527311N00556406EA0196401994059
    +B1208134527240N00556395EA0194101980056
    +B1208234527169N00556398EA0192301963057
    +B1208334527103N00556404EA0190801946059
    +B1208434527036N00556407EA0189601931055
    +B1208534526967N00556413EA0188201916056
    +B1209034526901N00556426EA0187101902057
    +B1209134526834N00556445EA0186101889048
    +B1209234526768N00556449EA0184301875047
    +B1209334526706N00556451EA0185501863044
    +B1209434526652N00556448EA0188001861045
    +B1209534526634N00556488EA0188801866040
    +B1210034526682N00556473EA0191001873039
    +B1210134526660N00556433EA0192901888039
    +B1210234526607N00556422EA0194601904045
    +B1210334526556N00556406EA0195101919041
    +B1210434526497N00556398EA0195401931044
    +B1210534526431N00556390EA0196301938045
    +B1211034526372N00556374EA0197901948050
    +B1211134526305N00556353EA0198901960052
    +B1211234526234N00556335EA0199701969046
    +B1211334526169N00556313EA0200001978052
    +B1211434526103N00556282EA0200901986057
    +B1211534526035N00556252EA0200601994055
    +B1212034525963N00556220EA0200901997044
    +B1212134525899N00556181EA0201002000060
    +B1212234525844N00556141EA0203102006050
    +B1212334525801N00556111EA0204502015037
    +B1212434525756N00556086EA0207302027031
    +B1212544525709N00556058EA0207002044045
    +B1213044525643N00556019EA0205702051057
    +B1213144525573N00555984EA0203902049044
    +B1213244525506N00555970EA0203702045050
    +B1213344525447N00555935EA0202402040057
    +B1213444525384N00555906EA0202902035038
    +B1213544525334N00555873EA0202002031043
    +B1214044525280N00555840EA0200802024047
    +B1214144525226N00555824EA0200802017039
    +B1214244525174N00555795EA0199802012044
    +B1214344525122N00555755EA0198902005047
    +B1214444525061N00555723EA0198201998041
    +B1214544525004N00555698EA0199201993041
    +B1215044524948N00555654EA0201801996044
    +B1215144524897N00555620EA0203702005044
    +B1215244524846N00555583EA0205202018042
    +B1215344524787N00555536EA0205502029045
    +B1215444524741N00555489EA0206102038040
    +B1215544524687N00555459EA0207802048051
    +B1216044524638N00555411EA0209702061040
    +B1216144524586N00555364EA0209102072042
    +B1216244524519N00555315EA0208302075052
    +B1216344524455N00555267EA0208102075046
    +B1216444524396N00555228EA0212502083036
    +B1216544524356N00555176EA0213502099041
    +B1217044524307N00555125EA0214102112046
    +B1217144524259N00555081EA0214002122051
    +B1217244524198N00555029EA0215202129045
    +B1217344524132N00554977EA0215602136040
    +B1217444524071N00554924EA0215202140053
    +B1217544524012N00554874EA0216402145050
    +B1218044523950N00554815EA0217002150051
    +B1218144523888N00554770EA0217802157047
    +B1218244523829N00554731EA0218602163047
    +B1218344523772N00554696EA0218402170046
    +B1218444523700N00554653EA0216502169056
    +B1218544523626N00554599EA0217402167058
    +B1219044523551N00554560EA0218302169057
    +B1219144523478N00554530EA0220302175051
    +B1219244523420N00554505EA0221302185047
    +B1219344523359N00554498EA0220402192050
    +B1219444523300N00554503EA0220202195041
    +B1219544523237N00554479EA0220302196051
    +B1220044523167N00554449EA0220802198050
    +B1220144523095N00554424EA0219602198055
    +B1220244523010N00554392EA0219002195057
    +B1220344522929N00554354EA0218102191055
    +B1220454522842N00554321EA0217302184060
    +B1220554522762N00554298EA0216202176057
    +B1221054522681N00554272EA0216102170057
    +B1221154522598N00554249EA0215302163055
    +B1221254522518N00554218EA0215102157055
    +B1221354522444N00554194EA0214002151052
    +B1221454522369N00554181EA0213002144057
    +B1221554522292N00554153EA0212002136057
    +B1222054522216N00554124EA0211202128054
    +B1222154522138N00554109EA0211202120061
    +B1222254522073N00554103EA0212702117048
    +B1222354522021N00554093EA0214402121030
    +B1222454521976N00554048EA0215402128045
    +B1222554521929N00554012EA0217802139043
    +B1223054521880N00553981EA0219102153040
    +B1223154521827N00553944EA0219802167039
    +B1223254521771N00553912EA0219302175040
    +B1223354521717N00553878EA0218802179042
    +B1223454521664N00553827EA0219502181045
    +B1223554521615N00553773EA0220802186033
    +B1224054521571N00553742EA0221802193043
    +B1224154521536N00553706EA0225002204040
    +B1224254521494N00553676EA0228802225044
    +B1224354521443N00553645EA0230902249038
    +B1224454521400N00553598EA0232402269038
    +B1224554521354N00553554EA0235302290037
    +B1225054521305N00553507EA0237102313045
    +B1225154521248N00553446EA0237602332052
    +B1225254521192N00553398EA0235602343051
    +B1225354521131N00553345EA0233502343053
    +B1225454521071N00553299EA0230302335059
    +B1225554521013N00553254EA0228402321055
    +B1226054520966N00553200EA0229202308052
    +B1226154520921N00553164EA0227602300046
    +B1226254520882N00553121EA0227802290037
    +B1226354520843N00553081EA0228702286048
    +B1226454520795N00553045EA0228602285045
    +B1226554520750N00553018EA0228702285042
    +B1227054520707N00552985EA0227402282047
    +B1227154520655N00552947EA0227302277046
    +B1227254520606N00552910EA0228802276042
    +B1227354520560N00552864EA0229002279048
    +B1227454520512N00552817EA0229502283047
    +B1227554520462N00552780EA0229802285040
    +B1228054520411N00552735EA0229502287044
    +B1228164520363N00552690EA0231602290044
    +B1228264520322N00552656EA0233602300036
    +B1228364520270N00552618EA0235602313048
    +B1228464520218N00552587EA0238902332044
    +B1228564520184N00552541EA0240902353032
    +B1229064520140N00552508EA0240802370041
    +B1229164520081N00552484EA0240502381046
    +B1229264520030N00552439EA0241902388041
    +B1229364519980N00552394EA0242502398044
    +B1229464519923N00552350EA0244102408048
    +B1229564519874N00552310EA0246302420047
    +B1230064519818N00552274EA0246602434042
    +B1230164519762N00552239EA0245102441046
    +B1230264519706N00552208EA0244102441045
    +B1230364519651N00552167EA0241502436043
    +B1230464519598N00552116EA0240502426046
    +B1230564519546N00552063EA0239102416043
    +B1231064519484N00552023EA0236202402054
    +B1231164519419N00551987EA0235702386049
    +B1231264519358N00551956EA0235802376047
    +B1231364519306N00551923EA0234302365048
    +B1231464519260N00551892EA0234302356050
    +B1231564519214N00551853EA0234302350038
    +B1232064519168N00551821EA0232802345046
    +B1232164519106N00551782EA0231302336052
    +B1232264519044N00551748EA0231102326052
    +B1232364518983N00551719EA0231002321055
    +B1232464518920N00551684EA0230502316055
    +B1232564518867N00551644EA0231602312036
    +B1233064518818N00551601EA0232202314034
    +B1233164518762N00551567EA0232202315045
    +B1233264518708N00551535EA0231702316042
    +B1233364518654N00551503EA0231702316038
    +B1233464518621N00551457EA0232102316040
    +B1233564518603N00551408EA0233902319040
    +B1234064518591N00551350EA0235502326034
    +B1234164518576N00551278EA0236302335048
    +B1234264518555N00551211EA0237102345043
    +B1234364518537N00551135EA0235402351046
    +B1234464518515N00551055EA0234202350044
    +B1234564518488N00550976EA0233302346044
    +B1235064518465N00550903EA0232502340044
    +B1235164518442N00550818EA0231102332055
    +B1235264518419N00550732EA0230002324052
    +B1235364518401N00550649EA0227602313050
    +B1235464518379N00550561EA0224902296057
    +B1235564518362N00550474EA0222602277055
    +B1236064518345N00550384EA0220602257051
    +B1236174518322N00550286EA0218602234048
    +B1236274518302N00550193EA0218102216056
    +B1236374518290N00550128EA0218002204038
    +B1236474518277N00550057EA0216702193042
    +B1236574518255N00549997EA0215002181045
    +B1237074518234N00549943EA0214802170040
    +B1237174518217N00549886EA0213302161041
    +B1237274518201N00549822EA0212302149040
    +B1237374518237N00549762EA0212502139046
    +B1237474518284N00549716EA0212302134040
    +B1237574518323N00549757EA0211202128044
    +B1238074518284N00549832EA0209702120038
    +B1238174518222N00549828EA0209302111044
    +B1238274518170N00549833EA0208502104040
    +B1238374518118N00549820EA0208102097040
    +B1238474518069N00549800EA0207202089043
    +B1238574518042N00549799EA0207002083041
    +B1239074518017N00549787EA0209102081039
    +B1239174518060N00549789EA0210802085042
    +B1239274518047N00549814EA0212902093034
    +B1239374518094N00549809EA0214602108038
    +B1239474518088N00549869EA0216502122029
    +B1239574518097N00549841EA0218602140042
    +B1240074518106N00549890EA0221402158035
    +B1240174518146N00549867EA0222802179040
    +B1240274518150N00549940EA0223102197041
    +B1240374518121N00549929EA0224202208032
    +B1240474518160N00549900EA0224302221044
    +B1240574518167N00549969EA0226002232041
    +B1241074518166N00549933EA0226602244037
    +B1241174518223N00549975EA0225202249042
    +B1241274518209N00550061EA0226102252042
    +B1241374518174N00550110EA0227302258037
    +B1241474518136N00550110EA0228102264039
    +B1241574518097N00550076EA0228602271043
    +B1242074518066N00550021EA0230402278039
    +B1242174518042N00550052EA0232402288044
    +B1242274518072N00550030EA0234402303043
    +B1242374518039N00550009EA0234102317039
    +B1242474518013N00549975EA0236602329039
    +B1242574518030N00550007EA0238802346044
    +B1243074517991N00550006EA0240102362039
    +B1243174517966N00549964EA0242402379041
    +B1243274517943N00549911EA0245102399045
    +B1243374517918N00549866EA0246902420041
    +B1243474517897N00549811EA0245102436050
    +B1243584517883N00549726EA0242502439058
    +B1244084517882N00549650EA0240902435053
    +B1244184517877N00549565EA0238602425058
    +B1244284517880N00549485EA0239902414046
    +B1244384517892N00549428EA0241202410039
    +B1244484517882N00549385EA0242602412043
    +B1244584517865N00549321EA0242802416042
    +B1245084517843N00549245EA0240302417049
    +B1245184517826N00549167EA0238602411056
    +B1245284517810N00549076EA0237202400055
    +B1245384517791N00548986EA0235802389061
    +B1245484517773N00548897EA0235102377056
    +B1245584517756N00548804EA0234002367058
    +B1246084517742N00548708EA0232302356059
    +B1246184517727N00548615EA0230602342062
    +B1246284517714N00548517EA0230002329059
    +B1246384517700N00548422EA0230202319058
    +B1246484517699N00548343EA0232702317033
    +B1246584517673N00548360EA0233302320042
    +B1247084517726N00548394EA0233702325044
    +B1247184517742N00548328EA0234502332040
    +B1247284517731N00548247EA0233602335052
    +B1247384517718N00548155EA0233002334048
    +B1247484517699N00548070EA0232202331054
    +B1247584517680N00547982EA0233002329051
    +B1248084517657N00547896EA0233102330054
    +B1248184517631N00547808EA0233002330050
    +B1248284517608N00547739EA0235302332035
    +B1248384517603N00547677EA0238402344036
    +B1248484517580N00547637EA0239902359037
    +B1248584517604N00547668EA0242702377038
    +B1249084517634N00547594EA0244702397040
    +B1249184517622N00547520EA0248602420036
    +B1249284517594N00547474EA0252302450039
    +B1249384517616N00547494EA0255602479042
    +B1249484517582N00547480EA0258202509038
    +B1249584517552N00547511EA0261102538041
    +B1250084517588N00547511EA0264302568043
    +B1250184517561N00547468EA0265702596045
    +B1250284517519N00547423EA0268002617040
    +B1250384517489N00547384EA0270302640039
    +B1250484517460N00547335EA0271102662048
    +B1250584517428N00547264EA0272902680058
    +B1251084517407N00547189EA0274602699045
    +B1251184517383N00547120EA0275002715046
    +B1251284517361N00547051EA0275302727048
    +B1251384517338N00546978EA0276602738045
    +B1251494517307N00546886EA0276202749045
    +B1251594517267N00546786EA0274502750054
    +B1252094517225N00546690EA0272602745054
    +B1252194517178N00546600EA0270402735057
    +B1252294517127N00546515EA0268302721061
    +B1252394517076N00546434EA0266202705060
    +B1252494517027N00546357EA0264202688059
    +B1252594516983N00546272EA0262002669056
    +B1253094516939N00546187EA0260402649057
    +B1253194516891N00546105EA0259102631058
    +B1253294516839N00546033EA0257802615058
    +B1253394516787N00545957EA0256202599059
    +B1253494516735N00545886EA0254602583059
    +B1253594516680N00545819EA0252702566060
    +B1254094516626N00545747EA0250802549059
    +B1254194516568N00545684EA0248702531056
    +B1254294516510N00545620EA0245902511057
    +B1254394516450N00545563EA0243102488058
    +B1254494516393N00545511EA0240202463056
    +B1254594516338N00545466EA0237002437056
    +B1255094516283N00545423EA0234402409055
    +B1255194516235N00545392EA0231602382057
    +B1255294516186N00545372EA0229702356056
    +B1255394516134N00545353EA0227902332056
    +B1255494516075N00545343EA0226302310058
    +B1255594516024N00545337EA0222302287054
    +B1256094515966N00545334EA0218702259050
    +B1256194515911N00545333EA0215202228051
    +B1256294515861N00545346EA0211102195054
    +B1256394515807N00545365EA0206902160056
    +B1256494515745N00545362EA0205302125050
    +B1256594515684N00545364EA0204502098052
    +B1257094515635N00545362EA0204702079046
    +B1257194515595N00545369EA0204502067041
    +B1257294515558N00545376EA0204702059049
    +B1257394515525N00545386EA0202602051039
    +B1257494515554N00545332EA0200002040039
    +B1257594515589N00545410EA0197602023041
    +B1258094515613N00545495EA0196402005039
    +B1258194515633N00545562EA0196101991037
    +B1258294515612N00545529EA0196801980041
    +B1258394515655N00545546EA0195801975038
    +B1258494515682N00545605EA0196301968035
    +B1258594515680N00545658EA0197801968044
    +B1259094515691N00545636EA0199101972046
    +B1259194515677N00545615EA0200701979040
    +B1259304515714N00545642EA0202101989043
    +B1259404515699N00545615EA0203702001048
    +B1259504515704N00545652EA0206102015041
    +B1300004515722N00545607EA0207802032042
    +B1300104515720N00545639EA0210102050038
    +B1300204515726N00545586EA0211702069045
    +B1300304515726N00545613EA0213602088039
    +B1300404515742N00545565EA0214902105042
    +B1300504515762N00545626EA0215302119044
    +B1301004515725N00545628EA0216602133040
    +B1301104515754N00545584EA0217202143042
    +B1301204515792N00545615EA0216402152043
    +B1301304515834N00545651EA0215702155039
    +B1301404515871N00545685EA0214802155042
    +B1301504515915N00545737EA0215002153048
    +B1302004515969N00545787EA0215102152043
    +B1302104516021N00545843EA0215002151042
    +B1302204516073N00545901EA0216202151043
    +B1302304516119N00545966EA0218002155044
    +B1302404516102N00545994EA0217902161044
    +B1302504516140N00545951EA0219502169042
    +B1303004516196N00545999EA0219202177041
    +B1303104516240N00546060EA0217402179044
    +B1303204516286N00546119EA0215102175042
    +B1303304516333N00546196EA0213102165049
    +B1303404516384N00546272EA0210802150050
    +B1303504516448N00546324EA0211202136036
    +B1304004516503N00546373EA0213402130036
    +B1304104516528N00546432EA0215702135049
    +B1304204516548N00546408EA0218302146041
    +B1304304516538N00546425EA0220202161046
    +B1304404516599N00546429EA0222302178043
    +B1304504516659N00546462EA0222902195041
    +B1305004516716N00546510EA0220302204045
    +B1305104516777N00546562EA0218402203041
    +B1305204516840N00546601EA0216202195042
    +B1305304516905N00546649EA0214102183045
    +B1305404516979N00546686EA0212102167041
    +B1305504517056N00546725EA0210202150045
    +B1306004517145N00546762EA0210202134036
    +B1306104517224N00546788EA0211102124039
    +B1306204517283N00546829EA0210002119041
    +B1306304517263N00546845EA0210002113042
    +B1306404517273N00546785EA0210402108044
    +B1306504517307N00546713EA0210902108039
    +B1307004517289N00546716EA0211102108038
    +B1307114517343N00546788EA0211902111038
    +B1307214517361N00546758EA0212902114033
    +B1307314517364N00546812EA0214402120036
    +B1307414517422N00546786EA0215302130040
    +B1307514517406N00546758EA0216602140036
    +B1308014517416N00546836EA0217902150035
    +B1308114517484N00546846EA0219302163037
    +B1308214517461N00546833EA0221002176037
    +B1308314517453N00546829EA0222502189043
    +B1308414517472N00546891EA0224702205043
    +B1308514517492N00546886EA0226702221044
    +B1309014517471N00546924EA0228402239041
    +B1309114517493N00546883EA0230202256041
    +B1309214517490N00546934EA0231502272043
    +B1309314517444N00546938EA0232902288042
    +B1309414517392N00546950EA0233302301043
    +B1309514517350N00546963EA0233802312037
    +B1310014517311N00546961EA0232702319041
    +B1310114517274N00546953EA0232302321042
    +B1310214517236N00546966EA0233602322043
    +B1310314517210N00546967EA0235702329042
    +B1310414517256N00547008EA0238202340038
    +B1310514517273N00547001EA0240002353043
    +B1311014517286N00547075EA0243102372046
    +B1311114517324N00547067EA0244702392040
    +B1311214517327N00547095EA0247302412039
    +B1311314517381N00547120EA0249402435045
    +B1311414517383N00547113EA0251902455041
    +B1311514517445N00547150EA0253602478039
    +B1312014517475N00547251EA0256602501045
    +B1312114517502N00547234EA0258102520037
    +B1312214517503N00547204EA0260802540043
    +B1312314517534N00547277EA0263402566044
    +B1312414517538N00547379EA0266002592043
    +B1312514517547N00547483EA0269502620037
    +B1313014517557N00547583EA0270502646054
    +B1313114517571N00547694EA0270202665052
    +B1313214517587N00547806EA0270002680054
    +B1313314517592N00547925EA0267902684054
    +B1313414517591N00548048EA0264802678057
    +B1313514517601N00548175EA0261602664058
    +B1314014517617N00548274EA0260402648058
    +B1314114517628N00548379EA0260102633051
    +B1314214517642N00548472EA0258602620040
    +B1314314517659N00548558EA0257202607045
    +B1314414517673N00548651EA0256302596045
    +B1314514517689N00548739EA0255002584045
    +B1315024517706N00548845EA0253602571047
    +B1315124517720N00548942EA0252402559048
    +B1315224517722N00549031EA0251102548043
    +B1315324517721N00549114EA0249302534044
    +B1315424517724N00549196EA0247402518040
    +B1315524517747N00549279EA0247402503046
    +B1316024517762N00549352EA0248902495042
    +B1316124517772N00549422EA0249902494039
    +B1316224517783N00549491EA0250502497040
    +B1316324517791N00549570EA0251602501057
    +B1316424517805N00549650EA0253902509045
    +B1316524517830N00549707EA0254402519047
    +B1317024517841N00549787EA0253802526043
    +B1317124517850N00549876EA0253202530049
    +B1317224517864N00549979EA0252902531054
    +B1317324517892N00550087EA0253202531057
    +B1317424517920N00550197EA0252002530056
    +B1317524517962N00550313EA0250502522055
    +B1318024518002N00550430EA0249402513057
    +B1318124518037N00550555EA0247502502055
    +B1318224518082N00550672EA0246402490054
    +B1318324518128N00550786EA0245002477053
    +B1318424518182N00550891EA0242802463059
    +B1318524518235N00550984EA0240402445057
    +B1319024518288N00551085EA0238302425059
    +B1319124518357N00551157EA0237402407055
    +B1319224518420N00551233EA0237402393050
    +B1319324518472N00551295EA0240002389039
    +B1319424518522N00551361EA0242402396052
    +B1319524518570N00551426EA0245102409049
    +B1320024518617N00551498EA0246702425048
    +B1320124518671N00551559EA0247502441046
    +B1320224518738N00551598EA0247702450055
    +B1320324518811N00551641EA0247702458060
    +B1320424518889N00551682EA0248702464057
    +B1320524518958N00551719EA0249502472052
    +B1321024519020N00551751EA0251202482042
    +B1321124519070N00551759EA0250602491047
    +B1321224519118N00551774EA0249002493040
    +B1321324519163N00551819EA0247802488044
    +B1321434519210N00551882EA0245802482056
    +B1321534519277N00551936EA0244702470059
    +B1322034519338N00552000EA0243102459053
    +B1322134519398N00552058EA0241502445053
    +B1322234519451N00552122EA0239902431055
    +B1322334519507N00552181EA0238402417051
    +B1322434519553N00552223EA0236102401041
    +B1322534519599N00552270EA0233702382046
    +B1323034519657N00552326EA0231502361054
    +B1323134519711N00552367EA0229402339049
    +B1323234519781N00552410EA0229702323053
    +B1323334519840N00552450EA0229202313045
    +B1323434519891N00552500EA0231402307055
    +B1323534519942N00552560EA0232802310050
    +B1324034519985N00552609EA0235302320041
    +B1324134520031N00552653EA0238302335040
    +B1324234520092N00552706EA0240802355048
    +B1324334520153N00552763EA0243502377044
    +B1324434520206N00552810EA0244902398044
    +B1324544520265N00552870EA0246802419046
    +B1325044520321N00552921EA0245602433055
    +B1325144520377N00552995EA0243702435057
    +B1325244520436N00553060EA0243002432057
    +B1325344520497N00553121EA0241502427055
    +B1325444520554N00553175EA0240302420047
    +B1325544520613N00553224EA0238502408042
    +B1326044520671N00553276EA0236202395049
    +B1326144520736N00553336EA0234202377056
    +B1326244520808N00553406EA0232502360059
    +B1326344520876N00553476EA0230602343055
    +B1326444520940N00553547EA0228902325053
    +B1326544521006N00553586EA0226902307054
    +B1327044521064N00553628EA0224202286053
    +B1327144521115N00553664EA0223402266041
    +B1327244521160N00553682EA0223202252045
    +B1327344521211N00553695EA0223602244041
    +B1327444521262N00553727EA0224802242033
    +B1327544521307N00553781EA0225302243044
    +B1328044521354N00553831EA0227702248036
    +B1328144521393N00553870EA0226902255038
    +B1328244521443N00553928EA0226502257047
    +B1328344521476N00553991EA0227002259042
    +B1328444521469N00554034EA0225902261040
    +B1328544521486N00553977EA0225602259040
    +B1329044521548N00553955EA0225502259045
    +B1329144521612N00553965EA0224502256044
    +B1329244521674N00553984EA0224002250037
    +B1329344521729N00554021EA0225102247048
    +B1329444521782N00554048EA0224902248041
    +B1329544521836N00554059EA0224002245043
    +B1330044521891N00554060EA0223802242045
    +B1330144521951N00554094EA0221802235057
    +B1330244522022N00554129EA0220402225056
    +B1330344522094N00554164EA0219402214057
    +B1330444522158N00554224EA0217502202058
    +B1330544522228N00554264EA0215802187058
    +B1331044522301N00554291EA0214202172055
    +B1331144522374N00554315EA0212802156057
    +B1331244522449N00554344EA0211502142057
    +B1331344522525N00554365EA0210202127060
    +B1331444522596N00554387EA0208802114056
    +B1331544522665N00554403EA0207302100055
    +B1332044522734N00554420EA0205602085057
    +B1332144522801N00554441EA0204202071054
    +B1332244522856N00554452EA0205002061047
    +B1332344522905N00554461EA0205502058043
    +B1332444522953N00554473EA0204902055043
    +B1332554523006N00554481EA0203902050041
    +B1333054523062N00554493EA0203502044042
    +B1333154523115N00554494EA0203302040043
    +B1333254523166N00554497EA0204402038037
    +B1333354523177N00554527EA0205802043041
    +B1333454523177N00554471EA0207602050043
    +B1333554523164N00554475EA0209602061042
    +B1334054523199N00554419EA0211202076038
    +B1334154523217N00554462EA0213302091039
    +B1334254523196N00554414EA0214802107039
    +B1334354523244N00554415EA0217102125040
    +B1334454523226N00554424EA0219802145045
    +B1334554523245N00554420EA0222702166046
    +B1335054523223N00554366EA0225102190042
    +B1335154523222N00554383EA0226802212043
    +B1335254523234N00554318EA0228302233040
    +B1335354523272N00554355EA0229102251040
    +B1335454523276N00554420EA0230002265037
    +B1335554523251N00554380EA0231402279044
    +B1336054523267N00554414EA0232402292044
    +B1336154523244N00554472EA0233402305039
    +B1336254523222N00554438EA0233602316043
    +B1336354523275N00554404EA0234502324040
    +B1336454523327N00554411EA0236002334043
    +B1336554523306N00554397EA0237602347049
    +B1337054523364N00554349EA0239202360043
    +B1337154523427N00554333EA0237902371041
    +B1337254523490N00554336EA0235202371053
    +B1337354523564N00554343EA0233402362054
    +B1337454523635N00554351EA0231502350056
    +B1337554523704N00554354EA0230002337056
    +B1338054523774N00554357EA0228802323053
    +B1338154523842N00554360EA0227602310053
    +B1338254523916N00554356EA0226002296055
    +B1338354523992N00554342EA0224202281054
    +B1338454524063N00554339EA0222802266053
    +B1338554524136N00554340EA0221602251056
    +B1339054524213N00554322EA0220502237057
    +B1339154524286N00554307EA0220102225056
    +B1339254524343N00554303EA0219902216047
    +B1339354524395N00554291EA0219802209042
    +B1339454524447N00554289EA0219202203043
    +B1339554524504N00554282EA0218402197042
    +B1340054524563N00554280EA0217702190044
    +B1340154524620N00554267EA0216302183044
    +B1340254524678N00554252EA0214502172051
    +B1340354524738N00554268EA0214902162035
    +B1340464524795N00554239EA0216102155038
    +B1340564524839N00554263EA0218902161037
    +B1341064524810N00554289EA0222202174042
    +B1341164524837N00554319EA0224602193050
    +B1341264524808N00554314EA0227402216042
    +B1341364524833N00554324EA0230502242043
    +B1341464524784N00554334EA0232902266041
    +B1341564524805N00554282EA0235002291042
    +B1342064524826N00554317EA0236102314044
    +B1342164524772N00554327EA0239002334040
    +B1342264524794N00554289EA0241002354041
    +B1342364524766N00554332EA0242902374030
    +B1342464524749N00554273EA0242602391035
    +B1342564524789N00554238EA0243302404041
    +B1343064524841N00554238EA0245502417043
    +B1343164524903N00554240EA0247702433042
    +B1343264524960N00554241EA0249002450042
    +B1343364525016N00554242EA0249202464050
    +B1343464525090N00554255EA0252002476050
    +B1343564525164N00554262EA0253802493058
    +B1344064525235N00554280EA0256402512057
    +B1344164525310N00554296EA0258202531055
    +B1344264525391N00554328EA0259902550051
    +B1344364525461N00554376EA0260202565057
    +B1344464525523N00554427EA0260402577055
    +B1344564525573N00554489EA0261502587053
    +B1345064525587N00554516EA0258602594080
    +B1345174525578N00554517EV0251002593103
    +B1345264525520N00554541EA0247402585078
    +B1345364525580N00554517EA0246702561047
    +B1345464525652N00554526EA0245002529054
    +B1345564525719N00554564EA0243802501055
    +B1346064525788N00554621EA0245802482058
    +B1346164525857N00554692EA0247002474051
    +B1346264525925N00554744EA0246602470056
    +B1346364525990N00554789EA0245502466052
    +B1346464526061N00554821EA0244302458056
    +B1346564526132N00554844EA0242802450058
    +B1347064526199N00554866EA0240502439057
    +B1347164526268N00554898EA0238502423060
    +B1347264526337N00554903EA0236602407063
    +B1347364526395N00554919EA0234002387054
    +B1347464526461N00554936EA0232602367053
    +B1347564526532N00554948EA0231702349050
    +B1348064526588N00554941EA0231002336038
    +B1348164526644N00554951EA0227802321043
    +B1348274526726N00554964EA0224002296056
    +B1348374526807N00554988EA0221602271054
    +B1348474526879N00555041EA0218402246056
    +B1348574526952N00555083EA0216702221052
    +B1349074527024N00555119EA0216402201051
    +B1349174527094N00555155EA0216202186052
    +B1349274527163N00555193EA0214902175055
    +B1349374527237N00555231EA0213202162057
    +B1349474527306N00555266EA0211802148053
    +B1349574527376N00555297EA0210702134056
    +B1350074527442N00555323EA0209402122052
    +B1350174527514N00555343EA0206902106055
    +B1350274527589N00555371EA0205702089056
    +B1350374527659N00555393EA0204302074057
    +B1350474527728N00555404EA0203202059054
    +B1350574527797N00555418EA0202402047051
    +B1351074527865N00555432EA0201902035046
    +B1351174527927N00555451EA0202002027050
    +B1351274527985N00555464EA0203102024037
    +B1351374528010N00555427EA0202202023041
    +B1351474528062N00555438EA0201902020036
    +B1351574528098N00555517EA0200802017044
    +B1352074528105N00555623EA0199202010041
    +B1352174528132N00555705EA0197502000045
    +B1352274528184N00555744EA0196701989040
    +B1352374528207N00555712EA0195401978044
    +B1352474528186N00555637EA0193801965055
    +B1352574528160N00555555EA0192101952054
    +B1353074528122N00555479EA0190701937055
    +B1353174528071N00555420EA0190101923046
    +B1353274528013N00555415EA0189601913039
    +B1353374527948N00555420EA0190301906034
    +B1353474527944N00555396EA0190901903037
    +B1353574527979N00555441EA0191501905034
    +B1354074527957N00555419EA0191401905038
    +B1354174527896N00555433EA0192701907035
    +B1354274527873N00555398EA0194001914042
    +B1354374527856N00555437EA0195201923038
    +B1354474527844N00555386EA0196001932039
    +B1354574527872N00555410EA0197901942044
    +B1355074527844N00555384EA0199501954041
    +B1355174527874N00555390EA0200201967044
    +B1355274527842N00555398EA0202001979042
    +B1355374527874N00555408EA0202701991043
    +B1355474527833N00555439EA0204502004040
    +B1355574527846N00555399EA0205102016039
    +B1356074527880N00555455EA0206802028043
    +B1356184527847N00555456EA0208502044043
    +B1356284527880N00555494EA0210502059043
    +B1356384527841N00555494EA0212702075040
    +B1356484527870N00555509EA0214102092046
    +B1356584527829N00555507EA0216002110038
    +B1357084527861N00555521EA0217502127039
    +B1357184527827N00555529EA0219002144042
    +B1357284527865N00555501EA0220202159036
    +B1357384527846N00555462EA0219002170042
    +B1357484527791N00555499EA0220102177039
    +B1357584527745N00555483EA0219502183041
    +B1358084527792N00555463EA0218402183041
    +B1358184527844N00555514EA0218102181045
    +B1358284527877N00555605EA0218402181040
    +B1358384527916N00555681EA0217902180042
    +B1358484527962N00555746EA0216602177043
    +B1358584528007N00555816EA0214902169042
    +B1359084528054N00555899EA0211402156062
    +B1359184528113N00555992EA0209102137058
    +B1359284528176N00556079EA0206102115059
    +B1359384528239N00556172EA0203602090059
    +B1359484528296N00556261EA0201402067056
    +B1359584528356N00556336EA0198802043056
    +B1400084528417N00556414EA0196702018059
    +B1400184528483N00556489EA0195101996057
    +B1400284528543N00556559EA0194201976039
    +B1400384528597N00556612EA0192501960045
    +B1400484528654N00556666EA0191501947048
    +B1400584528714N00556706EA0190201935044
    +B1401084528777N00556740EA0189001922048
    +B1401184528839N00556758EA0187501910047
    +B1401284528897N00556783EA0186001896046
    +B1401384528955N00556812EA0184501882046
    +B1401484529011N00556853EA0182401866043
    +B1401584529064N00556905EA0181001849044
    +B1402084529113N00556974EA0179101831046
    +B1402184529160N00557055EA0177001813046
    +B1402284529214N00557133EA0175601801046
    +B1402384529265N00557210EA0174601784046
    +B1402484529314N00557289EA0173501769047
    +B1402584529361N00557368EA0172101755046
    +B1403084529405N00557445EA0170801741044
    +B1403184529447N00557515EA0169301729047
    +B1403284529496N00557583EA0168401714046
    +B1403384529548N00557645EA0166601701047
    +B1403484529599N00557715EA0164801685047
    +B1403594529653N00557794EA0164101667045
    +B1404094529703N00557856EA0163101654046
    +B1404194529752N00557915EA0161901642045
    +B1404294529797N00557981EA0160501629047
    +B1404394529844N00558041EA0159201617046
    +B1404494529893N00558102EA0157601604046
    +B1404594529959N00558148EA0155701591055
    +B1405094530021N00558214EA0154101575054
    +B1405194530078N00558288EA0152301558056
    +B1405294530128N00558377EA0150201540057
    +B1405394530175N00558467EA0148401522058
    +B1405494530228N00558546EA0146301503057
    +B1405594530282N00558620EA0144501483054
    +B1406094530334N00558687EA0142701464051
    +B1406194530385N00558755EA0140801444053
    +B1406294530432N00558825EA0138801428054
    +B1406394530468N00558916EA0137201410054
    +B1406494530501N00559002EA0135301391053
    +B1406594530548N00559069EA0133701371055
    +B1407094530595N00559136EA0132501352055
    +B1407194530634N00559211EA0130701335052
    +B1407294530676N00559283EA0129101318052
    +B1407394530717N00559360EA0127401301052
    +B1407494530759N00559438EA0125601284055
    +B1407594530804N00559510EA0124101267054
    +B1408094530849N00559580EA0122401250054
    +B1408194530894N00559651EA0121101233055
    +B1408294530935N00559723EA0119501217054
    +B1408394530977N00559796EA0117901201052
    +B1408494531019N00559871EA0115901184053
    +B1408594531067N00559937EA0115001168053
    +B1409094531108N00600013EA0113101153056
    +B1409194531147N00600088EA0111901137049
    +B1409294531181N00600159EA0109901121052
    +B1409394531216N00600220EA0108101103050
    +B1409494531257N00600276EA0106101086056
    +B1409594531305N00600341EA0103601067057
    +B1410094531349N00600407EA0102101047054
    +B1410194531388N00600471EA0100201028057
    +B1410294531428N00600537EA0098001009055
    +B1410394531468N00600608EA0095600989054
    +B1410494531516N00600678EA0094000967054
    +B1410594531550N00600726EA0092200948048
    +B1411094531592N00600774EA0090000929054
    +B1411194531634N00600839EA0088100909051
    +B1411294531679N00600899EA0086900889046
    +B1411394531718N00600961EA0085000873050
    +B1411494531754N00601023EA0083800856049
    +B1412004531794N00601077EA0083600841038
    +B1412104531832N00601099EA0083300831035
    +B1412204531875N00601103EA0083300824037
    +B1412304531921N00601088EA0083300818040
    +B1412404531953N00601074EA0085200817034
    +B1412504531984N00601061EA0086600821038
    +B1413004532013N00601060EA0088000828034
    +B1413104531989N00601054EA0088700837035
    +B1413204531934N00601080EA0089600847036
    +B1413304531932N00601074EA0092500857029
    +B1413404531929N00601067EA0095300872039
    +B1413504531909N00601137EA0098100895038
    +B1414004531926N00601143EA0101200914038
    +B1414104531885N00601193EA0104300946039
    +B1414204531907N00601225EA0107400973038
    +B1414304531865N00601251EA0110201004040
    +B1414404531888N00601298EA0112601033037
    +B1414504531866N00601275EA0113601057038
    +B1415004531814N00601298EA0115301081037
    +B1415104531843N00601295EA0116501100040
    +B1415204531888N00601331EA0116201116039
    +B1415304531925N00601353EA0115601125041
    +B1415404531951N00601343EA0116201129038
    +B1415504531983N00601328EA0117901135034
    +B1416004531992N00601325EA0118501142038
    +B1416104531932N00601358EA0120101153038
    +B1416204531944N00601357EA0121601165044
    +B1416304531917N00601404EA0123101179042
    +B1416404531936N00601415EA0123501192042
    +B1416504531890N00601473EA0125601206041
    +B1417004531886N00601518EA0127101219049
    +B1417104531882N00601508EA0128501229042
    +B1417204531876N00601578EA0130401247044
    +B1417304531871N00601559EA0132101259038
    +B1417404531907N00601547EA0133001277041
    +B1417504531924N00601538EA0135601289041
    +B1418004531901N00601605EA0138101312040
    +B1418104531930N00601607EA0141401329034
    +B1418204531907N00601599EA0143301355033
    +B1418304531908N00601674EA0145101380043
    +B1418404531940N00601665EA0146001399036
    +B1418504531954N00601628EA0146401413042
    +B1419004531991N00601609EA0147801429040
    +B1419104532024N00601596EA0149601443041
    +B1419204532059N00601579EA0150601458040
    +B1419314532104N00601576EA0150201470044
    +B1419414532139N00601571EA0150301477044
    +B1419514532176N00601563EA0150101481041
    +B1420014532214N00601559EA0150101483041
    +B1420114532256N00601554EA0150301484039
    +B1420214532296N00601550EA0150601486043
    +B1420314532338N00601539EA0151701489038
    +B1420414532384N00601528EA0152501495041
    +B1420514532430N00601524EA0153501502039
    +B1421014532474N00601516EA0155101510038
    +B1421114532503N00601486EA0156401521032
    +B1421214532492N00601429EA0156501531035
    +B1421314532438N00601467EA0155901536039
    +B1421414532391N00601510EA0155701539044
    +B1421514532358N00601457EA0155501540036
    +B1422014532338N00601400EA0152601536038
    +B1422114532369N00601386EA0149901524044
    +B1422214532434N00601396EA0148201508043
    +B1422314532499N00601410EA0147401493040
    +B1422414532548N00601406EA0145401479038
    +B1422514532514N00601451EA0144001464042
    +B1423014532460N00601458EA0143601451036
    +B1423114532409N00601467EA0140401437041
    +B1423214532363N00601443EA0137801416039
    +B1423314532315N00601410EA0136501396044
    +B1423414532276N00601373EA0135501380040
    +B1423514532242N00601327EA0134201365039
    +B1424014532191N00601292EA0134001352037
    +B1424114532159N00601261EA0135801346039
    +B1424214532162N00601312EA0134501344038
    +B1424314532097N00601386EA0136201342035
    +B1424414532079N00601392EA0137701345049
    +B1424514532036N00601423EA0139701353036
    +B1425014532006N00601464EA0141301365048
    +B1425114531978N00601525EA0143501379035
    +B1425214531943N00601512EA0144801393036
    +B1425314531919N00601536EA0146201410051
    +B1425414531897N00601579EA0148701426042
    +B1425514531892N00601558EA0150401440037
    +B1426014531855N00601594EA0151801461043
    +B1426114531851N00601626EA0152601476045
    +B1426214531799N00601629EA0153601492040
    +B1426314531820N00601611EA0155801501043
    +B1426414531802N00601654EA0157601519042
    +B1426514531792N00601620EA0159601536045
    +B1427024531776N00601670EA0161101558040
    +B1427124531782N00601666EA0163501573042
    +B1427224531742N00601700EA0165301591034
    +B1427324531753N00601730EA0167101609046
    +B1427424531724N00601728EA0168901627039
    +B1427524531725N00601770EA0170601647047
    +B1428024531704N00601738EA0172601665039
    +B1428124531721N00601783EA0173401685042
    +B1428224531688N00601772EA0174401700040
    +B1428324531721N00601765EA0173601713042
    +B1428424531766N00601790EA0173001715040
    +B1428524531817N00601836EA0172701715053
    +B1429024531869N00601894EA0171801713058
    +B1429124531920N00601967EA0170801709062
    +B1429224531968N00602043EA0169801702055
    +B1429324532021N00602113EA0168201693057
    +B1429424532072N00602185EA0166701681058
    +B1429524532131N00602243EA0164201666060
    +B1430024532183N00602311EA0161701649060
    +B1430124532230N00602381EA0159301629060
    +B1430224532280N00602449EA0157601609059
    +B1430324532329N00602521EA0156001589058
    +B1430424532377N00602601EA0153801570059
    +B1430524532426N00602667EA0150601548056
    +B1431024532473N00602726EA0147001522057
    +B1431124532521N00602783EA0143301492059
    +B1431224532573N00602829EA0140101460060
    +B1431324532630N00602885EA0137501430059
    +B1431424532686N00602942EA0135301402056
    +B1431524532733N00602996EA0135301379052
    +B1432024532769N00603042EA0135901365040
    +B1432124532796N00603105EA0136601357040
    +B1432224532824N00603161EA0136801356041
    +B1432324532858N00603206EA0137201357038
    +B1432424532900N00603237EA0137901359042
    +B1432524532945N00603248EA0138001361038
    +B1433024532989N00603239EA0138201363039
    +B1433124533028N00603234EA0139701365038
    +B1433224532984N00603234EA0139801369037
    +B1433324532982N00603213EA0140201374034
    +B1433424533024N00603227EA0141801380038
    +B1433524533038N00603217EA0144601389044
    +B1434024533043N00603281EA0147201405038
    +B1434124533062N00603272EA0149201421042
    +B1434224533010N00603314EA0152001442044
    +B1434324533005N00603285EA0154201459047
    +B1434424532982N00603340EA0156301486044
    +B1434534532985N00603365EA0157701509046
    +B1435034532958N00603370EA0159701527038
    +B1435134532994N00603393EA0160801551036
    +B1435234533034N00603405EA0160901568040
    +B1435334533075N00603401EA0163001580035
    +B1435434533098N00603404EA0165501593037
    +B1435534533059N00603393EA0166801615042
    +B1436034533058N00603471EA0169301634036
    +B1436134533081N00603492EA0172801653039
    +B1436234533042N00603484EA0176601684044
    +B1436334533056N00603498EA0180101709044
    +B1436434533019N00603468EA0183101743045
    +B1436534533011N00603521EA0186301777042
    +B1437034533000N00603494EA0189001802034
    +B1437134532984N00603557EA0192001837047
    +B1437234532977N00603568EA0195401866042
    +B1437334532938N00603579EA0198501898040
    +B1437434532951N00603618EA0201701929048
    +B1437534532910N00603618EA0205301963029
    +B1438034532930N00603650EA0208001995030
    +B1438134532896N00603716EA0210502030036
    +B1438234532893N00603675EA0213902058036
    +B1438334532908N00603732EA0216402092043
    +B1438434532862N00603736EA0218902122036
    +B1438534532881N00603705EA0221402150036
    +B1439034532859N00603741EA0223902176047
    +B1439134532869N00603706EA0224602199046
    +B1439234532825N00603731EA0227302218039
    +B1439334532836N00603678EA0227302237040
    +B1439434532867N00603711EA0226402247039
    +B1439534532892N00603773EA0225702249041
    +B1440034532928N00603825EA0224602248041
    +B1440134532961N00603877EA0223202244036
    +B1440234532989N00603934EA0220202234053
    +B1440334533015N00604030EA0217602216062
    +B1440434533041N00604119EA0216002196058
    +B1440534533064N00604210EA0214202178059
    +B1441034533095N00604303EA0212302160057
    +B1441134533126N00604394EA0209702139054
    +B1441234533156N00604482EA0207202117057
    +B1441334533186N00604569EA0204602094057
    +B1441434533202N00604660EA0201602070052
    +B1441534533208N00604749EA0198102041052
    +B1442034533216N00604841EA0195002011054
    +B1442134533226N00604936EA0192601982052
    +B1442244533241N00605048EA0191001952049
    +B1442344533253N00605146EA0189501930049
    +B1442444533278N00605231EA0189101913042
    +B1442544533311N00605294EA0189501903039
    +B1443044533345N00605348EA0188401895042
    +B1443144533385N00605403EA0188301887038
    +B1443244533430N00605443EA0186901879044
    +B1443344533475N00605480EA0187101872034
    +B1443444533507N00605519EA0188101870038
    +B1443544533543N00605538EA0189701873037
    +B1444044533584N00605557EA0191001879040
    +B1444144533614N00605596EA0192501889040
    +B1444244533573N00605610EA0194401901041
    +B1444344533578N00605556EA0195701917041
    +B1444444533602N00605598EA0196601931041
    +B1444544533557N00605657EA0198001943038
    +B1445044533536N00605606EA0199401957040
    +B1445144533568N00605587EA0200701970044
    +B1445244533584N00605605EA0200901980038
    +B1445344533530N00605610EA0201601990040
    +B1445444533542N00605572EA0202201998039
    +B1445544533574N00605572EA0204202006039
    +B1446044533536N00605540EA0206002019040
    +B1446144533518N00605619EA0208202035036
    +B1446244533553N00605655EA0209502052041
    +B1446344533587N00605643EA0211402068040
    +B1446444533565N00605689EA0212902085039
    +B1446544533563N00605650EA0215202102037
    +B1447044533533N00605688EA0216902120042
    +B1447144533548N00605658EA0219102138040
    +B1447244533522N00605707EA0220502158046
    +B1447344533530N00605658EA0222902176045
    +B1447444533513N00605716EA0225002195040
    +B1447544533520N00605686EA0226902215042
    +B1448044533523N00605747EA0229102235039
    +B1448144533489N00605718EA0230802255041
    +B1448244533520N00605686EA0232702274036
    +B1448344533506N00605738EA0233802292047
    +B1448444533514N00605692EA0235402308037
    +B1448544533538N00605737EA0237702325040
    +B1449044533496N00605729EA0238102342045
    +B1449144533534N00605704EA0240502357046
    +B1449244533528N00605776EA0243302377041
    +B1449344533489N00605736EA0245202398042
    +B1449444533520N00605675EA0247902420036
    +B1449554533523N00605713EA0250302445043
    +B1450054533540N00605688EA0252902470040
    +B1450154533517N00605699EA0254402491047
    +B1450254533558N00605691EA0256702512035
    +B1450354533547N00605732EA0258402532039
    +B1450454533551N00605674EA0259802552044
    +B1450554533602N00605680EA0262302571044
    +B1451054533651N00605717EA0263302591043
    +B1451154533695N00605761EA0264102607036
    +B1451254533737N00605797EA0263002616046
    +B1451354533788N00605831EA0262502620040
    +B1451454533834N00605879EA0260202616043
    +B1451554533879N00605927EA0258802607047
    +B1452054533916N00605983EA0257702597047
    +B1452154533947N00606035EA0257002587039
    +B1452254533977N00606095EA0256902578046
    +B1452354534004N00606150EA0257002573046
    +B1452454534025N00606206EA0257002570040
    +B1452554534047N00606259EA0256302566045
    +B1453054534069N00606312EA0256102563041
    +B1453154534098N00606361EA0251902553045
    +B1453254534139N00606433EA0248302532051
    +B1453354534181N00606515EA0245402507054
    +B1453454534230N00606607EA0244402484051
    +B1453554534278N00606699EA0244002466061
    +B1454054534311N00606789EA0246702459035
    +B1454154534338N00606828EA0247802464038
    +B1454254534366N00606794EA0248302468037
    +B1454354534348N00606753EA0247902470042
    +B1454454534385N00606774EA0247702471043
    +B1454554534427N00606807EA0247702472044
    +B1455054534466N00606856EA0244602466046
    +B1455154534509N00606888EA0242102452042
    +B1455254534550N00606917EA0240902436042
    +B1455354534592N00606951EA0239302421042
    +B1455454534634N00606980EA0236802403043
    +B1455554534681N00607010EA0234202384041
    +B1456054534731N00607038EA0232702363041
    +B1456154534778N00607066EA0230902344047
    +B1456254534813N00607111EA0229502326041
    +B1456354534843N00607182EA0228802311057
    +B1456454534874N00607271EA0227302298051
    +B1456554534901N00607361EA0225402284055
    +B1457054534933N00607461EA0223502266051
    +B1457154534969N00607559EA0221602247054
    +B1457254534997N00607656EA0220102230056
    +B1457354535019N00607753EA0218302213055
    +B1457464535049N00607859EA0216702194055
    +B1457564535079N00607958EA0214702177057
    +B1458064535109N00608055EA0212702158056
    +B1458164535139N00608151EA0210402139057
    +B1458264535162N00608250EA0208202118058
    +B1458364535189N00608346EA0206302098058
    +B1458464535213N00608443EA0204502078059
    +B1458564535231N00608547EA0203202059055
    +B1459064535256N00608642EA0202602045056
    +B1459164535280N00608728EA0201702034059
    +B1459264535297N00608828EA0202002024050
    +B1459364535317N00608932EA0202802021055
    +B1459464535335N00609021EA0204802025039
    +B1459564535363N00609087EA0205202032036
    +B1500064535400N00609139EA0204502035040
    +B1500164535404N00609223EA0203602034039
    +B1500264535406N00609313EA0202302029041
    +B1500364535405N00609401EA0201202022038
    +B1500464535403N00609486EA0200002013042
    +B1500564535416N00609570EA0199502004039
    +B1501064535446N00609648EA0198701996041
    +B1501164535476N00609727EA0197901988040
    +B1501264535488N00609806EA0196901979038
    +B1501364535494N00609896EA0196001970040
    +B1501464535494N00609992EA0194601960045
    +B1501564535487N00610091EA0193801950040
    +B1502064535480N00610189EA0191901938040
    +B1502164535483N00610286EA0191201925044
    +B1502264535490N00610379EA0193601923037
    +B1502364535448N00610402EA0194101925039
    +B1502464535475N00610400EA0197501932040
    +B1502564535439N00610435EA0198801945046
    +B1503064535451N00610409EA0201701962039
    +B1503164535428N00610448EA0204201982036
    +B1503264535418N00610418EA0205302001042
    +B1503364535430N00610477EA0206602019038
    +B1503464535401N00610454EA0207602034038
    +B1503564535431N00610460EA0208002046047
    +B1504064535426N00610536EA0209802058045
    +B1504164535429N00610497EA0211702070039
    +B1504264535442N00610536EA0215402087042
    +B1504364535411N00610505EA0218302112044
    +B1504464535425N00610546EA0222202140047
    +B1504564535405N00610519EA0225502171046
    +B1505064535384N00610584EA0229202204034
    +B1505164535369N00610541EA0231902235045
    +B1505264535360N00610605EA0234602266041
    +B1505374535343N00610572EA0237302296044
    +B1505474535321N00610628EA0239902327047
    +B1505574535330N00610608EA0243102354040
    +B1506074535300N00610669EA0244102383040
    +B1506174535266N00610608EA0245902404038
    +B1506274535273N00610641EA0247902424043
    +B1506374535216N00610622EA0248902444043
    +B1506474535230N00610600EA0249402458044
    +B1506574535206N00610697EA0250702471040
    +B1507074535178N00610783EA0247902476039
    +B1507174535147N00610878EA0245102471041
    +B1507274535119N00610978EA0243302459048
    +B1507374535088N00611073EA0241202444042
    +B1507474535058N00611161EA0238102425043
    +B1507574535025N00611256EA0235502403045
    +B1508074534998N00611349EA0233202380038
    +B1508174534966N00611446EA0230802356040
    +B1508274534937N00611542EA0229102334045
    +B1508374534923N00611638EA0227302313041
    +B1508474534926N00611733EA0225402294041
    +B1508574534932N00611827EA0223502274042
    +B1509074534944N00611918EA0221802254045
    +B1509174534969N00611999EA0220602238044
    +B1509274535023N00612049EA0218502221055
    +B1509374535084N00612097EA0216702202055
    +B1509474535144N00612143EA0214902184057
    +B1509574535206N00612186EA0213102166057
    +B1510074535266N00612228EA0211302147058
    +B1510174535297N00612313EA0209402129053
    +B1510274535291N00612431EA0207102109051
    +B1510374535250N00612554EA0204402087058
    +B1510474535203N00612677EA0202002064059
    +B1510574535160N00612804EA0199602041059
    +B1511074535104N00612925EA0197402016056
    +B1511174535049N00613044EA0195301993055
    +B1511274535006N00613156EA0192901971057
    +B1511374534967N00613281EA0191601948056
    +B1511474534924N00613390EA0191501931053
    +B1511574534891N00613480EA0191601921045
    +B1512074534863N00613575EA0190501912043
    +B1512174534835N00613665EA0189501903042
    +B1512274534805N00613756EA0188101893040
    +B1512374534768N00613847EA0187401883044
    +B1512474534736N00613935EA0186301874042
    +B1512584534704N00614037EA0184801862048
    +B1513084534674N00614132EA0183801851042
    +B1513184534646N00614229EA0183001840046
    +B1513284534620N00614328EA0182001830040
    +B1513384534594N00614423EA0180301819045
    +B1513484534567N00614522EA0178601807044
    +B1513584534546N00614618EA0176801794043
    +B1514084534514N00614712EA0175101779044
    +B1514184534493N00614808EA0173401763042
    +B1514284534481N00614901EA0172101747043
    +B1514384534476N00614993EA0170901732043
    +B1514484534462N00615086EA0170001718045
    +B1514584534448N00615181EA0169201706045
    +B1515084534431N00615273EA0168001694043
    +B1515184534411N00615363EA0166601682043
    +B1515284534385N00615452EA0165701670044
    +B1515384534357N00615538EA0164401658044
    +B1515484534334N00615638EA0162801645053
    +B1515584534314N00615747EA0161601632055
    +B1516084534290N00615849EA0160401619053
    +B1516184534267N00615957EA0158701605054
    +B1516284534241N00616067EA0157401591056
    +B1516384534221N00616186EA0156501578053
    +B1516484534207N00616298EA0155001566055
    +B1516584534190N00616407EA0153401552054
    +B1517084534178N00616512EA0151601537053
    +B1517184534170N00616620EA0149701520053
    +B1517284534171N00616727EA0148001503056
    +B1517384534177N00616837EA0146401486055
    +B1517484534169N00616949EA0144901469055
    +B1517584534160N00617061EA0143401453053
    +B1518084534149N00617170EA0141801437054
    +B1518184534145N00617281EA0140201421053
    +B1518284534145N00617394EA0138901405051
    +B1518384534146N00617502EA0137401389042
    +B1518484534143N00617616EA0137701376038
    +B1518584534139N00617728EA0138101369041
    +B1519084534160N00617815EA0139001367034
    +B1519184534189N00617821EA0139801369040
    +B1519284534136N00617830EA0140801375036
    +B1519384534105N00617927EA0141401382037
    +B1519484534119N00618029EA0141801389038
    +B1519584534161N00618087EA0141501393041
    +B1520084534210N00618082EA0141901397038
    +B1520184534246N00618124EA0142501400038
    +B1520284534232N00618222EA0142301403045
    +B1520384534209N00618314EA0141401404039
    +B1520484534198N00618400EA0139701399037
    +B1520594534200N00618498EA0138701390039
    +B1521094534192N00618588EA0137701380042
    +B1521194534173N00618676EA0137001371042
    +B1521294534137N00618762EA0137601364042
    +B1521394534106N00618755EA0138301363044
    +B1521494534133N00618766EA0140001366035
    +B1521594534113N00618824EA0141301373043
    +B1522094534136N00618817EA0142801383041
    +B1522194534102N00618864EA0144001394040
    +B1522294534127N00618843EA0144901405038
    +B1522394534118N00618901EA0146101418044
    +B1522494534129N00618871EA0147901430038
    +B1522594534114N00618930EA0149101444038
    +B1523094534122N00618898EA0150301456040
    +B1523194534112N00618973EA0151301467031
    +B1523294534079N00618975EA0152201479046
    +B1523394534108N00619018EA0153901491036
    +B1523494534069N00619092EA0154301502041
    +B1523594534069N00619062EA0155001511040
    +B1524094534108N00619120EA0155601520036
    +B1524194534093N00619219EA0155401526042
    +B1524294534050N00619299EA0154701528045
    +B1524394534007N00619371EA0155101529040
    +B1524494533962N00619412EA0155601531041
    +B1524594533993N00619411EA0156401534040
    +B1525094533999N00619490EA0157201540039
    +B1525194533946N00619517EA0157601546041
    +B1525294533897N00619554EA0157001550033
    +B1525394533923N00619612EA0155301548043
    +B1525494533964N00619588EA0155801544033
    +B1525594534008N00619578EA0156001544042
    +B1526094534058N00619577EA0157001545037
    +B1526194534114N00619587EA0157501549038
    +B1526294534170N00619582EA0158301554037
    +B1526394534225N00619574EA0158601559039
    +B1526494534273N00619554EA0159801565038
    +B1526594534312N00619547EA0162201575050
    +B1527094534290N00619550EA0164901589039
    +B1527194534293N00619579EA0167801608049
    +B1527294534298N00619600EA0170701632046
    +B1527394534306N00619593EA0174201658049
    +B1527494534287N00619588EA0178301688046
    +B1527594534291N00619634EA0181901721043
    +B1528094534293N00619616EA0185801756037
    +B1528194534265N00619661EA0189501792052
    +B1528294534286N00619648EA0193401829043
    +B1528404534258N00619655EA0197901871041
    +B1528504534271N00619687EA0201801910042
    +B1529004534277N00619665EA0206001950040
    +B1529104534248N00619696EA0209801991048
    +B1529204534280N00619697EA0213902030041
    +B1529304534245N00619700EA0217902071044
    +B1529404534277N00619712EA0222102111037
    +B1529504534240N00619714EA0225602151048
    +B1530004534281N00619716EA0229402190045
    +B1530104534253N00619785EA0232702227046
    +B1530204534262N00619745EA0235702263042
    +B1530304534263N00619806EA0238402297041
    +B1530404534258N00619763EA0240602328040
    +B1530504534275N00619822EA0242602356043
    +B1531004534254N00619805EA0243302379046
    +B1531104534288N00619851EA0244402397045
    +B1531204534266N00619922EA0245802413039
    +B1531304534264N00619886EA0245302425046
    +B1531404534302N00619948EA0245802433039
    +B1531504534321N00620028EA0244702437042
    +B1532004534339N00620112EA0243302435044
    +B1532104534353N00620199EA0241702429046
    +B1532204534375N00620282EA0240602421044
    +B1532304534402N00620359EA0239502412044
    +B1532404534426N00620437EA0238602403043
    +B1532504534449N00620517EA0237502393045
    +B1533004534473N00620598EA0236502383044
    +B1533104534492N00620691EA0233902370056
    +B1533204534518N00620801EA0233002354058
    +B1533304534543N00620897EA0235002345035
    +B1533404534520N00620929EA0235902345046
    +B1533504534557N00620931EA0238602349045
    +B1534004534525N00620935EA0240402362048
    +B1534104534558N00620958EA0243402378043
    +B1534204534525N00620985EA0245502398043
    +B1534304534562N00621017EA0247702418040
    +B1534404534533N00621037EA0249002437044
    +B1534504534576N00621059EA0250002454041
    +B1535004534611N00621126EA0249002468043
    +B1535104534642N00621196EA0247202471046
    +B1535204534672N00621274EA0246402468043
    +B1535304534702N00621350EA0245002462043
    +B1535404534726N00621432EA0243802453046
    +B1535504534752N00621506EA0242402444047
    +B1536004534771N00621590EA0240802433044
    +B1536104534793N00621667EA0238902419043
    +B1536214534816N00621754EA0237502404043
    +B1536314534844N00621843EA0235002387057
    +B1536414534878N00621945EA0233702369053
    +B1536514534917N00622043EA0233402354047
    +B1537014534958N00622130EA0233102343046
    +B1537114534990N00622219EA0233102336045
    +B1537214535021N00622298EA0235102335039
    +B1537314534993N00622308EA0237102341045
    +B1537414535022N00622336EA0238702351043
    +B1537514534992N00622341EA0240702363043
    +B1538014535001N00622390EA0242502378045
    +B1538114534982N00622362EA0244102394044
    +B1538214535022N00622406EA0244802408040
    +B1538314535058N00622472EA0244202418045
    +B1538414535097N00622533EA0243402422044
    +B1538514535137N00622583EA0243302423041
    +B1539014535179N00622626EA0241402421037
    +B1539114535221N00622674EA0238002411046
    +B1539214535270N00622727EA0235602395045
    +B1539314535319N00622783EA0234502378042
    +B1539414535366N00622845EA0233002362044
    +B1539514535408N00622906EA0231102347046
    +B1540014535458N00622954EA0230602331036
    +B1540114535504N00622996EA0230902321044
    +B1540214535547N00623034EA0231702316037
    +B1540314535601N00623068EA0231402313040
    +B1540414535656N00623086EA0231702311038
    +B1540514535699N00623086EA0231302311044
    +B1541014535748N00623089EA0232202310046
    +B1541114535788N00623093EA0233202313040
    +B1541214535776N00623056EA0234402319040
    +B1541314535748N00623138EA0235202326040
    +B1541414535791N00623186EA0235702334042
    +B1541514535838N00623183EA0235402340044
    +B1542014535883N00623177EA0236202345041
    +B1542114535933N00623184EA0236202350046
    +B1542214535980N00623200EA0235302352043
    +B1542314536029N00623215EA0234902349041
    +B1542414536077N00623202EA0235102347047
    +B1542514536123N00623172EA0236802348040
    +B1543014536142N00623125EA0236702353036
    +B1543114536094N00623147EA0235202354040
    +B1543214536128N00623205EA0234602350045
    +B1543314536177N00623201EA0233702346042
    +B1543414536234N00623201EA0232402340044
    +B1543514536296N00623220EA0231702332047
    +B1544014536361N00623250EA0231302325042
    +B1544124536431N00623284EA0228902315058
    +B1544224536505N00623298EA0227402301055
    +B1544324536580N00623326EA0226402288057
    +B1544424536653N00623360EA0225402276058
    +B1544524536719N00623378EA0224302263047
    +B1545024536770N00623411EA0223202253056
    +B1545124536845N00623449EA0221602241052
    +B1545224536922N00623485EA0219202227057
    +B1545324536999N00623528EA0217102209058
    +B1545424537076N00623574EA0214302189058
    +B1545524537147N00623613EA0210702165054
    +B1546024537213N00623641EA0210102140038
    +B1546124537262N00623646EA0211102127035
    +B1546224537225N00623663EA0211802119039
    +B1546324537252N00623736EA0212502118036
    +B1546424537246N00623700EA0212602117039
    +B1546524537258N00623671EA0212502118039
    +B1547024537222N00623730EA0213502117041
    +B1547124537233N00623732EA0213802121039
    +B1547224537187N00623774EA0214302123040
    +B1547324537209N00623833EA0215902130046
    +B1547424537179N00623891EA0217502139036
    +B1547524537202N00623908EA0218802151047
    +B1548024537189N00623979EA0220202163043
    +B1548124537173N00623969EA0220102174041
    +B1548224537185N00624054EA0221802185034
    +B1548324537238N00624064EA0219002191048
    +B1548424537314N00624089EA0216302186061
    +B1548524537385N00624112EA0213502173045
    +B1549024537452N00624120EA0210202153056
    +B1549124537525N00624151EA0207302129055
    +B1549224537596N00624188EA0204402104055
    +B1549324537665N00624239EA0200702076056
    +B1549424537731N00624278EA0196402044054
    +B1549524537793N00624290EA0195602011051
    +B1550024537851N00624301EA0195501989050
    +B1550124537907N00624316EA0194501972048
    +B1550224537969N00624342EA0193801958052
    +B1550324538033N00624375EA0191601946048
    +B1550424538097N00624392EA0187701926054
    +B1550524538169N00624417EA0184301900060
    +B1551024538246N00624445EA0181001871063
    +B1551124538324N00624474EA0178101842060
    +B1551224538399N00624504EA0174801813061
    +B1551324538482N00624550EA0171901783061
    +B1551424538567N00624594EA0168801753062
    +B1551524538650N00624635EA0165901723060
    +B1552034538728N00624671EA0164201692052
    +B1552134538797N00624709EA0162301668055
    +B1552234538872N00624743EA0159801644060
    +B1552334538943N00624781EA0157801620058
    +B1552434539011N00624794EA0155901598055
    +B1552534539076N00624809EA0154501578055
    +B1553034539140N00624851EA0153401559056
    +B1553134539204N00624894EA0152201543058
    +B1553234539270N00624943EA0150901528057
    +B1553334539335N00624983EA0149701513055
    +B1553434539399N00625016EA0148301500054
    +B1553534539463N00625053EA0147301488054
    +B1554034539526N00625101EA0146201475054
    +B1554134539594N00625141EA0145001463055
    +B1554234539663N00625169EA0143801451055
    +B1554334539731N00625203EA0142401438054
    +B1554434539796N00625250EA0141201426055
    +B1554534539861N00625290EA0139801412054
    +B1555034539929N00625323EA0138601399055
    +B1555134539996N00625354EA0137201386055
    +B1555234540063N00625388EA0135701373055
    +B1555334540131N00625421EA0133801358054
    +B1555434540198N00625462EA0132001342056
    +B1555534540263N00625495EA0130401326056
    +B1556034540333N00625537EA0129701310051
    +B1556134540402N00625582EA0128101296046
    +B1556234540461N00625615EA0127501284041
    +B1556334540520N00625652EA0127101274040
    +B1556434540577N00625715EA0127501267039
    +B1556534540615N00625802EA0127201263038
    +B1557034540621N00625912EA0126801259040
    +B1557134540611N00626010EA0127601257039
    +B1557234540581N00626081EA0127601256040
    +B1557334540548N00626150EA0127901257041
    +B1557434540506N00626206EA0128601260036
    +B1557534540467N00626262EA0130001264034
    +B1558034540449N00626261EA0130501270039
    +B1558134540497N00626264EA0131401279040
    +B1558234540479N00626247EA0133301287030
    +B1558334540449N00626311EA0134601299045
    +B1558434540454N00626282EA0137201311047
    +B1558534540441N00626300EA0139801329036
    +B1559034540453N00626334EA0142001349043
    +B1559134540448N00626298EA0143301369041
    +B1559234540469N00626374EA0144001386038
    +B1559334540432N00626395EA0145701398040
    +B1559434540462N00626424EA0146001412038
    +B1559534540440N00626440EA0146801424039
    +B1600044540481N00626425EA0147601435038
    +B1600144540525N00626407EA0147501443044
    +B1600244540578N00626404EA0148201450041
    +B1600344540635N00626406EA0149201457042
    +B1600444540687N00626393EA0148901463042
    +B1600544540739N00626369EA0150201468034
    +B1601044540730N00626365EA0151201474039
    +B1601144540696N00626407EA0151701483039
    +B1601244540734N00626389EA0151901490040
    +B1601344540796N00626398EA0153701499036
    +B1601444540770N00626413EA0155301509039
    +B1601544540772N00626399EA0156701521041
    +B1602044540773N00626461EA0158301534036
    +B1602144540760N00626436EA0159501546040
    +B1602244540784N00626493EA0160401560041
    +B1602344540745N00626500EA0161801573044
    +B1602444540760N00626479EA0163801586044
    +B1602544540759N00626556EA0165201601034
    +B1603044540721N00626553EA0166601615040
    +B1603144540753N00626549EA0169001631043
    +B1603244540728N00626602EA0170201648040
    +B1603344540705N00626583EA0172601662039
    +B1603444540737N00626626EA0174401683037
    +B1603544540701N00626636EA0176401700040
    +B1604044540728N00626655EA0178301721047
    +B1604144540716N00626645EA0180501737041
    +B1604244540729N00626693EA0181601758041
    +B1604344540717N00626668EA0182901775043
    +B1604444540732N00626726EA0183801791042
    +B1604544540689N00626783EA0185101805036
    +B1605044540664N00626874EA0187001819037
    +B1605144540659N00626870EA0188901835032
    +B1605244540665N00626942EA0190701852038
    +B1605344540669N00626921EA0192001868039
    +B1605444540660N00626993EA0193401885042
    +B1605544540643N00626986EA0195401901044
    +B1606044540640N00627058EA0197401920036
    +B1606144540663N00627058EA0198801938042
    +B1606244540636N00627121EA0199901956039
    +B1606344540658N00627103EA0201001969034
    +B1606444540656N00627177EA0201501981041
    +B1606544540632N00627161EA0202901991040
    +B1607044540666N00627184EA0203302003041
    +B1607144540637N00627206EA0204702011036
    +B1607244540646N00627177EA0205802020046
    +B1607354540644N00627233EA0207302034044
    +B1607454540686N00627237EA0208402047037
    +B1607554540733N00627258EA0209902060038
    +B1608054540774N00627261EA0211402074042
    +B1608154540744N00627282EA0213002090035
    +B1608254540781N00627350EA0214102104036
    +B1608354540827N00627380EA0215502117038
    +B1608454540868N00627422EA0217702132038
    +B1608554540906N00627455EA0219102149035
    +B1609054540883N00627526EA0218802162045
    +B1609154540890N00627497EA0219902169041
    +B1609254540928N00627520EA0219202178042
    +B1609354540968N00627569EA0219002181041
    +B1609454540980N00627654EA0219702183044
    +B1609554540956N00627710EA0224002193040
    +B1610054540947N00627683EA0226702210047
    +B1610154540910N00627724EA0229802236043
    +B1610254540907N00627703EA0230002254042
    +B1610354540936N00627752EA0233402274040
    +B1610454540894N00627795EA0238302301037
    +B1610554540885N00627780EA0240202323042
    +B1611054540889N00627850EA0244502361036
    +B1611154540856N00627848EA0247902391034
    +B1611254540860N00627903EA0250402427041
    +B1611354540829N00627884EA0253502452035
    +B1611454540840N00627911EA0255702480031
    +B1611554540795N00627953EA0257402510034
    +B1612054540782N00627903EA0258602532040
    +B1612154540770N00627845EA0260302551037
    +B1612254540756N00627794EA0260202568037
    +B1612354540785N00627761EA0259902577040
    +B1612454540830N00627793EA0260002584041
    +B1612554540856N00627848EA0262102592041
    +B1613054540827N00627885EA0264102605047
    +B1613154540839N00627867EA0267602620042
    +B1613254540859N00627916EA0269202643037
    +B1613354540903N00627934EA0271102664044
    +B1613454540951N00627961EA0272202683041
    +B1613554541001N00627985EA0274702699036
    +B1614054541051N00628010EA0278202721040
    +B1614154541105N00628038EA0280802748044
    +B1614254541166N00628076EA0284002772032
    +B1614354541215N00628084EA0285002797041
    +B1614454541266N00628113EA0284302814042
    +B1614554541317N00628144EA0284002822041
    +B1615054541364N00628172EA0282402826045
    +B1615164541422N00628209EA0280202821044
    +B1615264541481N00628245EA0277802810044
    +B1615364541538N00628284EA0275702795046
    +B1615464541594N00628327EA0274202779044
    +B1615564541654N00628357EA0273402766044
    +B1616064541717N00628388EA0271702752054
    +B1616164541794N00628440EA0270402737061
    +B1616264541872N00628491EA0269402724058
    +B1616364541952N00628538EA0268502711055
    +B1616464542032N00628583EA0266402698054
    +B1616564542117N00628623EA0263402680061
    +B1617064542194N00628617EA0262202661045
    +B1617164542263N00628641EA0260702645055
    +B1617264542337N00628695EA0258502627061
    +B1617364542413N00628746EA0256602608054
    +B1617464542493N00628796EA0254802589055
    +B1617564542568N00628851EA0253402572055
    +B1618064542645N00628904EA0252102556055
    +B1618164542715N00628962EA0250302540059
    +B1618264542787N00629023EA0248602523057
    +B1618364542857N00629082EA0247202507056
    +B1618464542926N00629144EA0245802492057
    +B1618564542999N00629205EA0244402477058
    +B1619064543070N00629269EA0243202463057
    +B1619164543135N00629342EA0242102449056
    +B1619264543202N00629415EA0240802436057
    +B1619364543266N00629490EA0239602423057
    +B1619464543334N00629561EA0238302410057
    +B1619564543401N00629629EA0236802396055
    +B1620064543468N00629697EA0235202382056
    +B1620164543533N00629764EA0233102366057
    +B1620264543589N00629828EA0233202350045
    +B1620364543615N00629886EA0232002341044
    +B1620464543653N00629948EA0231002331042
    +B1620564543692N00630015EA0230902321038
    +B1621064543731N00630061EA0229402314047
    +B1621164543770N00630107EA0228102304044
    +B1621264543815N00630122EA0225802292053
    +B1621364543868N00630138EA0223502275056
    +B1621464543915N00630213EA0221602258054
    +B1621564543951N00630307EA0218902239056
    +B1622064543983N00630421EA0216602219055
    +B1622164544013N00630539EA0213602198058
    +B1622264544038N00630667EA0210302174057
    +B1622364544055N00630795EA0208102148058
    +B1622464544071N00630923EA0205302123058
    +B1622564544097N00631040EA0203302097058
    +B1623064544124N00631149EA0200802073056
    +B1623164544142N00631272EA0198302048054
    +B1623274544162N00631402EA0196202021054
    +B1623374544186N00631513EA0193901998052
    +B1623474544212N00631639EA0191601974056
    +B1623574544235N00631766EA0189001951057
    +B1624074544260N00631887EA0186101926057
    +B1624174544286N00632017EA0183101900055
    +B1624274544300N00632157EA0180501872055
    +B1624374544320N00632261EA0181701850036
    +B1624474544359N00632318EA0182001836036
    +B1624574544411N00632345EA0184001830034
    +B1625074544434N00632324EA0185001832040
    +B1625174544398N00632376EA0186101836032
    +B1625274544434N00632444EA0187301843037
    +B1625374544485N00632453EA0188401853032
    +B1625474544500N00632405EA0188501861039
    +B1625574544537N00632391EA0189701869038
    +B1626074544530N00632467EA0190501877038
    +B1626174544539N00632437EA0191501885036
    +B1626274544576N00632510EA0192401893039
    +B1626374544541N00632540EA0192801902039
    +B1626474544533N00632487EA0193501911039
    +B1626574544554N00632446EA0194501918038
    +B1627074544575N00632494EA0195701927038
    +B1627174544550N00632467EA0197301936035
    +B1627274544577N00632492EA0198201948039
    +B1627374544546N00632510EA0200101958037
    +B1627474544573N00632519EA0200501971038
    +B1627574544556N00632590EA0202101981036
    +B1628074544541N00632546EA0203201993035
    +B1628174544569N00632561EA0204102004040
    +B1628274544546N00632618EA0205302016038
    +B1628374544540N00632565EA0206302028040
    +B1628474544536N00632508EA0206702039042
    +B1628574544492N00632566EA0206202045036
    +B1629074544523N00632629EA0206902048035
    +B1629174544552N00632679EA0208302055042
    +B1629274544522N00632663EA0209802064038
    +B1629374544553N00632678EA0211602076043
    +B1629474544518N00632676EA0212902088039
    +B1629574544543N00632664EA0214302100040
    +B1630074544524N00632711EA0216102115039
    +B1630174544532N00632669EA0217402131043
    +B1630274544549N00632727EA0218502144039
    +B1630374544500N00632733EA0220502158036
    +B1630474544529N00632711EA0221402172040
    +B1630574544527N00632779EA0222202184039
    +B1631084544477N00632849EA0225002199035
    +B1631184544503N00632886EA0227302217034
    +B1631284544478N00632847EA0228702236041
    +B1631384544430N00632907EA0230402253037
    +B1631484544455N00632965EA0232602271036
    +B1631584544448N00632937EA0234002291039
    +B1632084544411N00633008EA0236102309035
    +B1632184544447N00633049EA0237602328040
    +B1632284544459N00633007EA0239402346038
    +B1632384544403N00633038EA0240302363040
    +B1632484544404N00633127EA0241202377042
    +B1632584544441N00633121EA0242302390037
    +B1633084544459N00633088EA0243602401040
    +B1633184544451N00633149EA0244502414040
    +B1633284544438N00633109EA0245602425038
    +B1633384544474N00633139EA0246702437045
    +B1633484544438N00633140EA0247602448038
    +B1633584544460N00633097EA0248202457039
    +B1634084544498N00633153EA0248802465039
    +B1634184544472N00633202EA0249602472039
    +B1634284544478N00633149EA0250702481037
    +B1634384544513N00633188EA0251302489037
    +B1634484544546N00633242EA0251602497038
    +B1634584544590N00633208EA0252102504038
    +B1635084544603N00633139EA0252602509041
    +B1635184544597N00633070EA0253002515037
    +B1635284544552N00633073EA0252102518038
    +B1635384544553N00633149EA0251502517042
    +B1635484544599N00633201EA0251602516043
    +B1635584544651N00633252EA0252202516046
    +B1636084544706N00633302EA0253002519044
    +B1636184544759N00633360EA0253202522047
    +B1636284544813N00633416EA0253402525043
    +B1636384544867N00633476EA0251502525044
    +B1636484544927N00633525EA0249402517042
    +B1636584544989N00633559EA0247902506044
    +B1637084545051N00633596EA0246702494045
    +B1637184545103N00633636EA0245502482044
    +B1637284545152N00633691EA0244402469045
    +B1637384545198N00633746EA0244102458042
    +B1637484545241N00633805EA0243602450043
    +B1637584545290N00633850EA0243102443044
    +B1638084545337N00633907EA0242602437041
    +B1638184545384N00633952EA0241802430044
    +B1638284545433N00633997EA0241602424043
    +B1638394545489N00634054EA0241702419042
    +B1638494545538N00634106EA0241202416043
    +B1638594545590N00634143EA0242402415046
    +B1639094545644N00634179EA0243202418043
    +B1639194545699N00634217EA0243502421043
    +B1639294545755N00634261EA0242402424045
    +B1639394545805N00634322EA0241802422041
    +B1639494545858N00634370EA0240302417045
    +B1639594545907N00634420EA0239602409043
    +B1640094545954N00634471EA0239102402041
    +B1640194546001N00634510EA0238202395045
    +B1640294546050N00634553EA0237502388043
    +B1640394546100N00634595EA0236802381044
    +B1640494546151N00634642EA0236002374043
    +B1640594546202N00634682EA0234702366045
    +B1641094546258N00634704EA0233302356044
    +B1641194546314N00634727EA0232002344046
    +B1641294546371N00634748EA0230702332043
    +B1641394546430N00634765EA0229102319044
    +B1641494546493N00634778EA0227402305053
    +B1641594546565N00634791EA0226302291053
    +B1642094546634N00634802EA0226102278043
    +B1642194546692N00634817EA0225802270041
    +B1642294546747N00634838EA0225002262044
    +B1642394546806N00634874EA0224202255047
    +B1642494546862N00634902EA0223102246044
    +B1642594546914N00634915EA0221902237042
    +B1643094546968N00634926EA0221202228045
    +B1643194547022N00634923EA0220502219042
    +B1643294547080N00634935EA0220102211044
    +B1643394547135N00634958EA0220302204040
    +B1643494547185N00634970EA0221902203036
    +B1643594547205N00634940EA0222402206036
    +B1644094547159N00634967EA0222902209041
    +B1644194547171N00635054EA0223902214036
    +B1644294547216N00635060EA0224702222038
    +B1644394547224N00635014EA0225202229040
    +B1644494547179N00635001EA0226402236038
    +B1644594547193N00634972EA0226802244038
    +B1645094547218N00635038EA0226702251042
    +B1645194547175N00635051EA0227502255043
    +B1645294547191N00635013EA0228502261040
    +B1645394547200N00635085EA0228502267040
    +B1645494547165N00635073EA0229202271042
    +B1645594547184N00635034EA0229602276040
    +B1646094547210N00635081EA0230202281040
    +B1646194547160N00635119EA0230402286036
    +B1646304547143N00635069EA0231202290038
    +B1646404547162N00635113EA0231302295040
    +B1646504547122N00635095EA0231602298037
    +B1647004547146N00635046EA0232702302041
    +B1647104547155N00635105EA0232902309042
    +B1647204547124N00635066EA0234102314035
    +B1647304547153N00635087EA0234702321046
    +B1647404547116N00635088EA0235902329039
    +B1647504547145N00635048EA0236102336037
    +B1648004547198N00635084EA0236202341037
    +B1648104547243N00635090EA0236902347037
    +B1648204547235N00635038EA0236602351043
    +B1648304547180N00635044EA0236402354039
    +B1648404547167N00635119EA0235402353040
    +B1648504547226N00635129EA0235202352041
    +B1649004547282N00635120EA0235002351044
    +B1649104547336N00635105EA0234902349044
    +B1649204547389N00635098EA0235402349044
    +B1649304547436N00635098EA0235002349042
    +B1649404547490N00635102EA0235002347041
    +B1649504547540N00635090EA0235402347042
    +B1650004547592N00635071EA0235102347043
    +B1650104547638N00635041EA0235202347042
    +B1650204547686N00635022EA0234502346043
    +B1650304547736N00634996EA0233702342044
    +B1650404547784N00634974EA0232602337043
    +B1650504547835N00634950EA0232002330042
    +B1651004547885N00634923EA0231502323046
    +B1651104547927N00634890EA0232602320039
    +B1651204547971N00634849EA0232002319042
    +B1651304548021N00634818EA0232002317047
    +B1651404548075N00634801EA0232302316036
    +B1651504548129N00634807EA0231802316041
    +B1652004548184N00634795EA0230502313043
    +B1652104548237N00634772EA0229502306044
    +B1652204548282N00634735EA0229102299038
    +B1652304548325N00634699EA0229002291041
    +B1652404548361N00634675EA0230202290034
    +B1652504548368N00634629EA0230602292038
    +B1653004548337N00634582EA0230802295037
    +B1653104548286N00634627EA0231202297036
    +B1653204548279N00634711EA0232602302033
    +B1653304548320N00634764EA0233102309037
    +B1653404548331N00634729EA0232802314040
    +B1653504548279N00634744EA0233102318038
    +B1654004548248N00634820EA0233102321037
    +B1654114548217N00634799EA0232202322040
    +B1654214548261N00634762EA0231702320044
    +B1654314548307N00634732EA0231902319044
    +B1654414548348N00634702EA0231502317040
    +B1654514548386N00634667EA0231202315040
    +B1655014548423N00634626EA0231202312045
    +B1655114548453N00634594EA0233002313036
    +B1655214548441N00634557EA0234702318040
    +B1655314548416N00634639EA0235402327038
    +B1655414548453N00634689EA0237602337038
    +B1655514548456N00634624EA0237302348039
    +B1656014548399N00634611EA0239602359040
    +B1656114548391N00634701EA0240702371039
    +B1656214548423N00634733EA0242402385036
    +B1656314548422N00634673EA0241002394038
    +B1656414548365N00634650EA0242202399032
    +B1656514548331N00634647EA0242702406043
    +B1657014548358N00634680EA0244802416036
    +B1657114548317N00634709EA0245302425041
    +B1657214548341N00634688EA0246602434034
    +B1657314548304N00634737EA0246802443045
    +B1657414548288N00634691EA0246902449037
    +B1657514548312N00634651EA0246102453046
    +B1658014548332N00634687EA0246702456037
    +B1658114548294N00634718EA0246202457043
    +B1658214548320N00634691EA0245702457034
    +B1658314548367N00634716EA0244602453040
    +B1658414548400N00634769EA0245902450039
    +B1658514548356N00634791EA0246202451043
    +B1659014548386N00634778EA0247602454041
    +B1659114548400N00634830EA0246702458038
    +B1659214548347N00634862EA0247202458043
    +B1659314548367N00634840EA0246802461044
    +B1659414548403N00634893EA0246702461035
    +B1659514548406N00634972EA0248002463042
    +B1700014548397N00634955EA0249502469037
    +B1700114548382N00635035EA0251002477035
    +B1700214548364N00635009EA0253002488035
    +B1700314548357N00635064EA0254202501042
    +B1700414548364N00635038EA0255802514039
    +B1700514548346N00635092EA0257602529044
    +B1701014548357N00635062EA0259402545042
    +B1701114548333N00635108EA0261302561040
    +B1701214548357N00635092EA0263302579043
    +B1701314548328N00635131EA0265202597039
    +B1701424548372N00635130EA0267402618040
    +B1701524548417N00635200EA0269002638043
    +B1702024548457N00635281EA0270302658045
    +B1702124548497N00635353EA0270302673045
    +B1702224548534N00635449EA0269602681054
    +B1702324548564N00635560EA0268502683058
    +B1702424548588N00635671EA0267402681056
    +B1702524548610N00635781EA0266002675058
    +B1703024548635N00635885EA0265002667055
    +B1703124548661N00635992EA0264502659055
    +B1703224548694N00636084EA0263602651045
    +B1703324548717N00636173EA0261302640056
    +B1703424548738N00636272EA0259602627062
    +B1703524548766N00636372EA0257902613057
    +B1704024548787N00636475EA0256502598057
    +B1704124548808N00636574EA0254902583058
    +B1704224548830N00636671EA0253402568057
    +B1704324548862N00636767EA0252002552056
    +B1704424548882N00636864EA0250902538055
    +B1704524548907N00636961EA0249702525057
    +B1705024548928N00637062EA0248602512057
    +B1705124548951N00637161EA0247402500055
    +B1705224548973N00637262EA0246102489057
    +B1705324548994N00637364EA0245002477057
    +B1705424549018N00637464EA0243902465057
    +B1705524549042N00637565EA0242602453057
    +B1706024549069N00637667EA0241102441056
    +B1706124549091N00637770EA0239802428057
    +B1706224549113N00637872EA0238302414056
    +B1706324549136N00637974EA0236902399056
    +B1706424549160N00638075EA0235702385055
    +B1706524549185N00638175EA0234702372055
    +B1707024549206N00638273EA0233702360055
    +B1707124549228N00638373EA0232502348056
    +B1707224549250N00638471EA0231702338056
    +B1707324549270N00638568EA0230902327054
    +B1707424549293N00638665EA0230102317049
    +B1707524549313N00638750EA0229302308046
    +B1708024549328N00638838EA0228602300046
    +B1708124549340N00638926EA0227702292047
    +B1708224549353N00639014EA0226602284048
    +B1708324549361N00639114EA0225202274054
    +B1708424549374N00639217EA0224002263054
    +B1708524549393N00639317EA0222702252053
    +B1709024549409N00639422EA0221602240053
    +B1709124549423N00639528EA0220702229051
    +B1709224549439N00639633EA0219502218052
    +B1709324549461N00639737EA0218602207052
    +B1709424549482N00639845EA0217202194052
    +B1709534549506N00639950EA0215802183055
    +B1710034549532N00640057EA0214902171052
    +B1710134549549N00640163EA0213702160055
    +B1710234549563N00640270EA0212402148055
    +B1710334549580N00640367EA0211802137049
    +B1710434549598N00640461EA0211202128048
    +B1710534549621N00640555EA0210802119046
    +B1711034549645N00640647EA0210102112047
    +B1711134549674N00640730EA0210002105042
    +B1711234549697N00640804EA0209902101040
    +B1711334549713N00640877EA0209402098038
    +B1711434549741N00640943EA0208402093044
    +B1711534549779N00641000EA0207902088042
    +B1712034549823N00641046EA0207002081043
    +B1712134549874N00641073EA0206002074040
    +B1712234549929N00641088EA0205502067045
    +B1712334549984N00641103EA0205502061043
    +B1712434550039N00641126EA0204802056042
    +B1712534550088N00641170EA0204802052040
    +B1713034550128N00641233EA0204302049043
    +B1713134550178N00641283EA0203202043041
    +B1713234550231N00641319EA0202202036041
    +B1713334550293N00641338EA0201202028044
    +B1713434550351N00641353EA0200802020042
    +B1713534550406N00641381EA0200602013039
    +B1714034550452N00641447EA0200502008042
    +B1714134550466N00641542EA0199902004041
    +B1714234550465N00641647EA0199402000044
    +B1714334550467N00641756EA0199201996043
    +B1714434550478N00641859EA0199301993042
    +B1714534550504N00641948EA0199001990042
    +B1715034550542N00642020EA0198401987045
    +B1715134550584N00642086EA0197101983043
    +B1715234550628N00642145EA0195001974043
    +B1715334550681N00642202EA0192801959050
    +B1715434550740N00642251EA0192701945036
    +B1715534550793N00642288EA0191501935044
    +B1716034550840N00642329EA0191001923037
    +B1716134550842N00642321EA0190301915042
    +B1716234550829N00642399EA0189201907043
    +B1716334550861N00642482EA0187801897042
    +B1716434550884N00642575EA0186201886042
    +B1716534550893N00642680EA0183401872053
    +B1717034550908N00642805EA0180601853056
    +B1717134550924N00642930EA0177701830063
    +B1717234550940N00643065EA0174801805062
    +B1717334550945N00643204EA0171601777061
    +B1717434550948N00643343EA0168401748060
    +B1717544550948N00643492EA0165701716059
    +B1718044550949N00643629EA0163201687061
    +B1718144550957N00643750EA0162401662040
    +B1718244550965N00643841EA0161501643041
    +B1718344550985N00643923EA0160601628044
    +B1718444551009N00643998EA0159501615050
    +B1718544551038N00644083EA0158901603050
    +B1719044551075N00644164EA0157501592053
    +B1719144551118N00644235EA0156501579050
    +B1719244551164N00644299EA0155001567046
    +B1719344551207N00644352EA0155101556035
    +B1719444551234N00644401EA0155301551036
    +B1719544551269N00644455EA0155001546039
    +B1720044551303N00644510EA0154501542040
    +B1720144551349N00644548EA0153801536041
    +B1720244551393N00644576EA0154101531038
    +B1720344551437N00644610EA0154101529036
    +B1720444551479N00644604EA0154301527032
    +B1720544551472N00644544EA0154201528037
    +B1721044551496N00644545EA0153101526039
    +B1721144551447N00644576EA0152501523039
    +B1721244551401N00644546EA0150801518042
    +B1721344551438N00644516EA0149501510042
    +B1721444551482N00644489EA0149101500043
    +B1721544551512N00644455EA0149201494044
    +B1722044551483N00644488EA0150601491030
    +B1722144551515N00644488EA0150301492039
    +B1722244551485N00644435EA0150501492035
    +B1722344551454N00644491EA0151801493029
    +B1722444551476N00644507EA0152601499036
    +B1722544551434N00644500EA0153401505037
    +B1723044551442N00644558EA0154501512037
    +B1723144551438N00644515EA0154701519039
    +B1723244551389N00644542EA0155301526036
    +B1723344551412N00644603EA0156101533033
    +B1723444551432N00644580EA0155501538041
    +B1723544551386N00644604EA0155801541032
    +B1724044551402N00644661EA0155401544038
    +B1724144551444N00644662EA0155201544040
    +B1724244551475N00644668EA0155001543033
    +B1724344551495N00644730EA0154401541036
    +B1724444551516N00644689EA0155101539035
    +B1724544551536N00644676EA0155801541040
    +B1725044551499N00644701EA0156601543035
    +B1725144551496N00644659EA0156801547034
    +B1725244551498N00644713EA0156701549038
    +B1725354551463N00644676EA0157201551035
    +B1725454551470N00644629EA0157201554039
    +B1725554551488N00644673EA0156201554037
    +B1726054551436N00644668EA0155601551038
    +B1726154551424N00644613EA0154601546039
    +B1726254551457N00644573EA0154101540040
    +B1726354551488N00644528EA0153801535041
    +B1726454551505N00644467EA0153501531040
    +B1726554551533N00644421EA0151901526041
    +B1727054551566N00644373EA0149801515042
    +B1727154551615N00644322EA0148001501055
    +B1727254551663N00644275EA0146401486052
    +B1727354551727N00644255EA0144601470051
    +B1727454551790N00644239EA0143501453051
    +B1727554551830N00644228EA0142601439039
    +B1728054551869N00644211EA0141201428045
    +B1728154551912N00644200EA0139801416052
    +B1728254551963N00644178EA0138501403050
    +B1728354552012N00644146EA0137101389048
    +B1728454552054N00644095EA0136601377045
    +B1728554552101N00644068EA0136301367045
    +B1729054552141N00644053EA0136301360040
    +B1729154552180N00644021EA0136701356037
    +B1729254552174N00643997EA0137201355041
    +B1729354552129N00644032EA0137201355041
    +B1729454552153N00644007EA0137401356036
    +B1729554552200N00644014EA0137501359041
    +B1730054552241N00643985EA0138701363041
    +B1730154552280N00643975EA0138401366038
    +B1730254552325N00643966EA0138101367037
    +B1730354552376N00643955EA0138401368040
    +B1730454552425N00643934EA0139001369038
    +B1730554552471N00643907EA0138801370037
    +B1731054552518N00643879EA0138801370040
    +B1731154552558N00643846EA0138801370038
    +B1731254552590N00643808EA0138701370033
    +B1731354552624N00643774EA0139101370033
    +B1731454552639N00643734EA0139901374040
    +B1731554552586N00643762EA0139401376043
    +B1732054552605N00643737EA0139101376041
    +B1732154552650N00643728EA0139201375040
    +B1732254552688N00643710EA0139501375038
    +B1732354552728N00643689EA0139601376039
    +B1732454552763N00643662EA0139801377042
    +B1732554552803N00643642EA0139801378037
    +B1733064552854N00643617EA0140301379037
    +B1733164552902N00643599EA0140401381039
    +B1733264552960N00643590EA0140501382037
    +B1733364553015N00643580EA0140201383038
    +B1733464553071N00643568EA0139201380043
    +B1733564553131N00643574EA0139401377038
    +B1734064553181N00643584EA0138901374044
    +B1734164553235N00643593EA0138301371039
    +B1734264553293N00643591EA0137801367048
    +B1734364553353N00643601EA0137601363039
    +B1734464553408N00643610EA0137801361037
    +B1734564553465N00643611EA0137301358044
    +B1735064553518N00643608EA0136801355038
    +B1735164553575N00643615EA0136301352045
    +B1735264553634N00643603EA0136501349038
    +B1735364553702N00643632EA0136201347040
    +B1735464553774N00643674EA0134801342045
    +B1735564553833N00643701EA0134401336040
    +B1736064553891N00643719EA0134101331037
    +B1736164553951N00643738EA0133301326043
    +B1736264554013N00643764EA0133301322036
    +B1736364554075N00643782EA0132501317043
    +B1736464554139N00643786EA0131701311037
    +B1736564554201N00643814EA0130001303035
    +B1737064554268N00643844EA0128001291042
    +B1737164554340N00643859EA0126201276046
    +B1737264554411N00643870EA0124201261041
    +B1737364554482N00643882EA0122301244037
    +B1737464554550N00643910EA0120301226045
    +B1737564554619N00643915EA0118001207044
    +B1738064554675N00643854EA0116701189043
    +B1738164554706N00643785EA0115101171042
    +B1738264554704N00643719EA0114001155042
    +B1738364554716N00643648EA0112701139037
    +B1738464554720N00643586EA0111901126044
    +B1738564554698N00643549EA0111201115043
    +B1739064554675N00643515EA0111001106037
    +B1739164554646N00643487EA0110401099044
    +B1739264554613N00643454EA0110001092034
    +B1739364554593N00643430EA0110101088037
    +B1739464554573N00643413EA0109701085039
    +B1739564554543N00643400EA0110101081038
    +B1740064554513N00643385EA0110601080035
    +B1740164554485N00643369EA0111001081034
    +B1740264554513N00643366EA0110901083036
    +B1740364554510N00643428EA0110801084038
    +B1740464554478N00643400EA0109901083040
    +B1740574554439N00643367EA0109101079038
    +B1741074554418N00643315EA0107301072038
    +B1741174554423N00643257EA0106601061033
    +B1741274554472N00643275EA0106301054036
    +B1741374554458N00643328EA0105301048033
    +B1741474554429N00643287EA0103801039042
    +B1741574554416N00643236EA0102501029037
    +B1742074554411N00643190EA0100601018038
    +B1742174554466N00643227EA0099601003036
    +B1742274554500N00643298EA0098500990035
    +B1742374554505N00643363EA0098200980035
    +B1742474554491N00643316EA0096200969036
    +B1742574554504N00643244EA0094700956040
    +B1743074554534N00643189EA0093400943038
    +B1743174554557N00643137EA0092100929036
    +B1743274554581N00643084EA0091200916044
    +B1743374554606N00643034EA0090200904034
    +B1743474554633N00642986EA0090900894038
    +B1743574554619N00642995EA0091500891041
    +B1744074554657N00643053EA0092000891030
    +B1744174554678N00643029EA0092400892036
    +B1744274554647N00643070EA0093100896033
    +B1744374554678N00643125EA0094000901031
    +B1744474554703N00643105EA0094100905038
    +B1744574554672N00643129EA0094500909033
    +B1745074554680N00643128EA0094400912033
    +B1745174554713N00643204EA0094200914041
    +B1745274554685N00643212EA0093700914044
    +B1745374554692N00643163EA0094000913036
    +B1745474554739N00643165EA0093500912037
    +B1745574554733N00643235EA0092900909038
    +B1746074554710N00643256EA0092100905036
    +B1746174554735N00643214EA0091700900037
    +B1746274554793N00643257EA0091000894038
    +B1746374554787N00643263EA0090300889037
    +B1746474554825N00643213EA0088700881036
    +B1746574554862N00643161EA0087200870035
    +B1747074554912N00643115EA0086900859036
    +B1747174554934N00643073EA0087200853038
    +B1747274554932N00643033EA0087000849040
    +B1747374554937N00642991EA0087700847034
    +B1747474554922N00643052EA0087500846040
    +B1747574554986N00643097EA0087600847038
    +B1748074555037N00643065EA0087300847037
    +B1748174555021N00643025EA0087000846038
    +B1748274555017N00642975EA0086600844038
    +B1748374555026N00642931EA0086500842037
    +B1748484555038N00642887EA0087200841035
    +B1748584555024N00642947EA0087300841039
    +B1749084555076N00642986EA0087900842034
    +B1749184555124N00642954EA0087100843031
    +B1749284555136N00642891EA0087400843037
    +B1749384555112N00642903EA0086700841038
    +B1749484555147N00642965EA0087000841032
    +B1749584555137N00643003EA0086600840039
    +B1750084555128N00642971EA0087200840032
    +B1750184555167N00643005EA0087000841033
    +B1750284555147N00643051EA0086300840036
    +B1750384555128N00643016EA0085200836036
    +B1750484555110N00642958EA0084900830041
    +B1750584555096N00642907EA0084900826040
    +B1751084555088N00642868EA0084800824038
    +B1751184555092N00642816EA0084500821040
    +B1751284555077N00642772EA0084100818038
    +B1751384555063N00642722EA0083100814037
    +B1751484555053N00642675EA0081900808039
    +B1751584555041N00642624EA0081600800036
    +B1752084555023N00642582EA0080500793042
    +B1752184555027N00642525EA0080000785037
    +B1752284555028N00642471EA0079300777040
    +B1752384555039N00642410EA0078800771041
    +B1752484555046N00642346EA0078700765038
    +B1752584555028N00642289EA0077800760043
    +B1753084554983N00642269EA0076800753043
    +B1753184554936N00642304EA0076000745037
    +B1753284554914N00642384EA0074800737040
    +B1753384554927N00642482EA0073600728041
    +B1753484554964N00642576EA0072000718005
    +B1753584555033N00642625EA0071800707002
    +B1754084555093N00642588EA0070400697000
    +B1754184555113N00642503EA0069400686001
    +B1754284555110N00642430EA0068500676000
    +B1754384555080N00642396EA0067600665000
    +B1754484555043N00642363EA0066800656000
    +B1754584555012N00642323EA0065800646000
    +B1755084554984N00642342EA0064300636000
    +B1755184555015N00642412EA0063300625000
    +B1755284554990N00642377EA0062000615002
    +B1755384554962N00642364EA0061600606000
    +B1755484554962N00642363EA0061600601000
    +B1755584554963N00642364EA0061600596000
    +B1756084554963N00642365EA0061600591000
    +B1756184554964N00642366EA0061600587000
    +B1756294554965N00642366EA0061600584000
    diff --git a/mapbox-gl/examples/data/igc/Sylvain-Dhonneur.igc b/mapbox-gl/examples/data/igc/Sylvain-Dhonneur.igc
    new file mode 100644
    index 0000000000..524f7e2325
    --- /dev/null
    +++ b/mapbox-gl/examples/data/igc/Sylvain-Dhonneur.igc
    @@ -0,0 +1,16374 @@
    +AXGD111 6030, SN07178, SW3.29
    +HFDTE190411
    +HOPLTPILOT: Sylvain Dhonneur
    +HOGTYGLIDERTYPE: R10
    +HOGTYGLIDERID: None
    +HOCIDCOMPETITIONID: 
    +HODTM100GPSDATUM: WGS-84
    +HOCCLCOMPETITION CLASS: None
    +HOSITSite: None
    +B0851014556248N00651105EA0000002045
    +B0851034556248N00651105EA0000002045
    +B0851054556248N00651105EA0000002045
    +B0851074556248N00651105EA0000002045
    +B0851094556248N00651105EA0000002045
    +B0851114556248N00651105EA0000002045
    +B0851134556248N00651105EA0000002045
    +B0851154556248N00651105EA0000002045
    +B0851174556248N00651105EA0000002045
    +B0851194556248N00651105EA0000002045
    +B0851214556248N00651105EA0000002045
    +B0851234556248N00651105EA0000002045
    +B0851254556248N00651105EA0000002045
    +B0851274556248N00651105EA0000002045
    +B0851294556248N00651105EA0000002045
    +B0851314556248N00651105EA0000002045
    +B0851334556248N00651105EA0000002045
    +B0851354556251N00651107EA0000002045
    +B0851374556254N00651109EA0000002045
    +B0851394556258N00651111EA0000002045
    +B0851414556265N00651116EA0000002045
    +B0851434556275N00651124EA0000002044
    +B0851454556282N00651135EA0000002041
    +B0851474556289N00651146EA0000002039
    +B0851494556295N00651156EA0000002036
    +B0851514556299N00651169EA0000002032
    +B0851534556302N00651185EA0000002028
    +B0851554556305N00651199EA0000002025
    +B0851574556307N00651211EA0000002023
    +B0851594556306N00651224EA0000002021
    +B0852024556305N00651235EA0000002020
    +B0852044556306N00651241EA0000002021
    +B0852064556310N00651247EA0000002022
    +B0852084556311N00651254EA0000002024
    +B0852104556307N00651258EA0000002027
    +B0852124556304N00651261EA0000002031
    +B0852144556303N00651267EA0000002035
    +B0852164556300N00651274EA0000002037
    +B0852184556296N00651281EA0000002039
    +B0852204556290N00651286EA0000002040
    +B0852224556282N00651290EA0000002041
    +B0852244556274N00651291EA0000002043
    +B0852264556270N00651293EA0000002045
    +B0852284556264N00651297EA0000002046
    +B0852304556256N00651300EA0000002049
    +B0852324556250N00651301EA0000002052
    +B0852344556243N00651301EA0000002057
    +B0852364556234N00651303EA0000002061
    +B0852384556224N00651305EA0000002067
    +B0852404556214N00651303EA0000002072
    +B0852424556204N00651298EA0000002077
    +B0852444556192N00651294EA0000002081
    +B0852464556179N00651294EA0000002084
    +B0852484556171N00651300EA0000002086
    +B0852504556170N00651308EA0000002089
    +B0852524556173N00651313EA0000002091
    +B0852544556181N00651312EA0000002092
    +B0852564556189N00651302EA0000002094
    +B0852584556192N00651284EA0000002094
    +B0853004556193N00651265EA0000002095
    +B0853024556193N00651243EA0000002095
    +B0853044556184N00651214EA0000002096
    +B0853064556175N00651201EA0000002099
    +B0853084556165N00651190EA0000002102
    +B0853104556157N00651178EA0000002106
    +B0853124556149N00651163EA0000002111
    +B0853144556144N00651150EA0000002117
    +B0853164556136N00651140EA0000002122
    +B0853184556125N00651133EA0000002128
    +B0853204556115N00651140EA0000002137
    +B0853224556111N00651149EA0000002144
    +B0853254556108N00651158EA0000002151
    +B0853274556111N00651165EA0000002157
    +B0853294556118N00651164EA0000002162
    +B0853314556126N00651154EA0000002165
    +B0853334556128N00651132EA0000002167
    +B0853354556124N00651110EA0000002172
    +B0853374556122N00651094EA0000002179
    +B0853394556121N00651083EA0000002186
    +B0853414556118N00651070EA0000002192
    +B0853434556110N00651056EA0000002197
    +B0853454556098N00651058EA0000002204
    +B0853474556088N00651067EA0000002213
    +B0853494556085N00651076EA0000002220
    +B0853514556086N00651085EA0000002227
    +B0853534556086N00651085EA0000002235
    +B0853554556099N00651084EA0000002241
    +B0853574556103N00651068EA0000002246
    +B0853594556104N00651049EA0000002254
    +B0854014556099N00651031EA0000002261
    +B0854034556088N00651018EA0000002270
    +B0854054556076N00651010EA0000002278
    +B0854074556065N00651011EA0000002284
    +B0854094556056N00651016EA0000002291
    +B0854114556049N00651024EA0000002299
    +B0854134556047N00651031EA0000002306
    +B0854154556050N00651035EA0000002311
    +B0854174556057N00651032EA0000002315
    +B0854194556065N00651019EA0000002319
    +B0854214556070N00650999EA0000002324
    +B0854234556070N00650978EA0000002330
    +B0854254556066N00650962EA0000002337
    +B0854274556060N00650946EA0000002344
    +B0854294556052N00650928EA0000002350
    +B0854314556038N00650920EA0000002356
    +B0854334556026N00650914EA0000002360
    +B0854354556014N00650910EA0000002363
    +B0854374556003N00650911EA0000002365
    +B0854394555996N00650918EA0000002368
    +B0854414555995N00650926EA0000002372
    +B0854434555999N00650931EA0000002377
    +B0854454556008N00650931EA0000002385
    +B0854474556013N00650929EA0000002390
    +B0854494556019N00650927EA0000002394
    +B0854514556026N00650921EA0000002397
    +B0854534556034N00650915EA0000002401
    +B0854554556040N00650914EA0000002406
    +B0854574556046N00650917EA0000002411
    +B0854594556052N00650920EA0000002416
    +B0855014556058N00650920EA0000002420
    +B0855034556064N00650923EA0000002424
    +B0855064556069N00650931EA0000002427
    +B0855084556074N00650938EA0000002430
    +B0855104556079N00650943EA0000002435
    +B0855124556085N00650947EA0000002441
    +B0855144556092N00650947EA0000002446
    +B0855164556100N00650941EA0000002450
    +B0855184556104N00650930EA0000002453
    +B0855204556107N00650917EA0000002456
    +B0855224556108N00650901EA0000002457
    +B0855244556102N00650887EA0000002458
    +B0855264556089N00650881EA0000002459
    +B0855284556078N00650885EA0000002460
    +B0855304556071N00650893EA0000002460
    +B0855324556065N00650903EA0000002459
    +B0855344556060N00650915EA0000002459
    +B0855364556055N00650927EA0000002459
    +B0855384556053N00650938EA0000002461
    +B0855404556050N00650950EA0000002465
    +B0855424556046N00650962EA0000002469
    +B0855444556040N00650971EA0000002474
    +B0855464556032N00650980EA0000002479
    +B0855484556023N00650991EA0000002483
    +B0855504556015N00651002EA0000002488
    +B0855524556011N00651015EA0000002492
    +B0855544556013N00651027EA0000002496
    +B0855564556019N00651029EA0000002500
    +B0855584556026N00651024EA0000002504
    +B0856004556032N00651016EA0000002508
    +B0856024556035N00651003EA0000002512
    +B0856044556034N00650987EA0000002515
    +B0856064556030N00650971EA0000002519
    +B0856084556023N00650959EA0000002523
    +B0856104556014N00650947EA0000002527
    +B0856124556003N00650936EA0000002530
    +B0856144555991N00650929EA0000002533
    +B0856164555979N00650924EA0000002536
    +B0856184555967N00650920EA0000002539
    +B0856204555956N00650922EA0000002541
    +B0856224555946N00650928EA0000002542
    +B0856244555941N00650939EA0000002543
    +B0856264555943N00650955EA0000002543
    +B0856284555948N00650961EA0000002544
    +B0856304555954N00650962EA0000002545
    +B0856324555962N00650959EA0000002546
    +B0856344555967N00650953EA0000002548
    +B0856364555971N00650943EA0000002550
    +B0856384555976N00650932EA0000002551
    +B0856404555978N00650923EA0000002553
    +B0856424555980N00650915EA0000002555
    +B0856454555983N00650906EA0000002556
    +B0856474555986N00650895EA0000002557
    +B0856494555985N00650882EA0000002557
    +B0856514555984N00650868EA0000002558
    +B0856534555983N00650853EA0000002560
    +B0856554555981N00650839EA0000002562
    +B0856574555981N00650827EA0000002565
    +B0856594555983N00650815EA0000002568
    +B0857014555983N00650800EA0000002569
    +B0857034555979N00650786EA0000002568
    +B0857054555968N00650778EA0000002566
    +B0857074555959N00650774EA0000002564
    +B0857094555949N00650770EA0000002562
    +B0857114555938N00650765EA0000002560
    +B0857134555927N00650758EA0000002557
    +B0857154555917N00650751EA0000002554
    +B0857174555907N00650743EA0000002551
    +B0857194555897N00650733EA0000002547
    +B0857214555887N00650721EA0000002543
    +B0857234555876N00650709EA0000002539
    +B0857254555867N00650697EA0000002537
    +B0857274555860N00650688EA0000002536
    +B0857294555852N00650681EA0000002536
    +B0857314555844N00650676EA0000002536
    +B0857334555835N00650669EA0000002535
    +B0857354555824N00650658EA0000002530
    +B0857374555814N00650646EA0000002529
    +B0857394555805N00650635EA0000002525
    +B0857414555796N00650623EA0000002520
    +B0857434555785N00650610EA0000002517
    +B0857454555775N00650599EA0000002514
    +B0857474555766N00650588EA0000002509
    +B0857494555756N00650575EA0000002504
    +B0857514555746N00650561EA0000002500
    +B0857534555737N00650546EA0000002497
    +B0857554555728N00650531EA0000002493
    +B0857574555718N00650516EA0000002490
    +B0857594555708N00650501EA0000002486
    +B0858014555699N00650485EA0000002483
    +B0858034555684N00650461EA0000002478
    +B0858054555675N00650445EA0000002476
    +B0858074555666N00650430EA0000002473
    +B0858094555656N00650416EA0000002469
    +B0858114555645N00650400EA0000002464
    +B0858134555634N00650385EA0000002459
    +B0858154555622N00650372EA0000002455
    +B0858174555609N00650359EA0000002450
    +B0858194555596N00650346EA0000002447
    +B0858214555585N00650333EA0000002444
    +B0858244555574N00650319EA0000002440
    +B0858264555563N00650304EA0000002436
    +B0858284555554N00650289EA0000002433
    +B0858304555545N00650273EA0000002429
    +B0858324555537N00650256EA0000002425
    +B0858344555531N00650236EA0000002421
    +B0858364555524N00650216EA0000002419
    +B0858384555517N00650196EA0000002418
    +B0858404555511N00650178EA0000002419
    +B0858424555507N00650160EA0000002419
    +B0858444555502N00650142EA0000002419
    +B0858464555498N00650124EA0000002420
    +B0858484555492N00650107EA0000002420
    +B0858504555487N00650089EA0000002419
    +B0858524555483N00650070EA0000002418
    +B0858544555479N00650050EA0000002418
    +B0858564555475N00650031EA0000002419
    +B0858584555471N00650012EA0000002421
    +B0859004555466N00649995EA0000002424
    +B0859024555462N00649982EA0000002426
    +B0859044555458N00649968EA0000002427
    +B0859064555453N00649950EA0000002425
    +B0859084555448N00649930EA0000002426
    +B0859104555442N00649911EA0000002427
    +B0859124555437N00649895EA0000002428
    +B0859144555434N00649881EA0000002427
    +B0859164555431N00649862EA0000002424
    +B0859184555427N00649842EA0000002424
    +B0859204555423N00649825EA0000002425
    +B0859224555419N00649812EA0000002429
    +B0859244555416N00649799EA0000002430
    +B0859264555413N00649784EA0000002430
    +B0859284555412N00649765EA0000002430
    +B0859304555420N00649750EA0000002429
    +B0859324555431N00649745EA0000002427
    +B0859344555440N00649752EA0000002424
    +B0859364555438N00649764EA0000002424
    +B0859384555423N00649765EA0000002425
    +B0859404555411N00649757EA0000002428
    +B0859424555403N00649745EA0000002431
    +B0859444555397N00649734EA0000002432
    +B0859464555390N00649719EA0000002432
    +B0859484555382N00649704EA0000002433
    +B0859504555377N00649689EA0000002435
    +B0859524555373N00649676EA0000002436
    +B0859544555365N00649660EA0000002437
    +B0859564555360N00649644EA0000002440
    +B0859594555355N00649633EA0000002441
    +B0900014555348N00649621EA0000002442
    +B0900034555341N00649606EA0000002443
    +B0900054555334N00649593EA0000002446
    +B0900074555327N00649583EA0000002448
    +B0900094555319N00649573EA0000002449
    +B0900114555310N00649561EA0000002449
    +B0900134555303N00649549EA0000002451
    +B0900154555296N00649538EA0000002451
    +B0900174555289N00649526EA0000002449
    +B0900194555281N00649512EA0000002446
    +B0900214555273N00649497EA0000002443
    +B0900234555266N00649483EA0000002439
    +B0900254555259N00649467EA0000002436
    +B0900274555254N00649451EA0000002434
    +B0900294555249N00649436EA0000002433
    +B0900314555245N00649421EA0000002431
    +B0900334555240N00649404EA0000002430
    +B0900354555235N00649389EA0000002429
    +B0900374555232N00649374EA0000002428
    +B0900394555227N00649358EA0000002425
    +B0900414555222N00649341EA0000002423
    +B0900434555217N00649324EA0000002420
    +B0900454555212N00649309EA0000002416
    +B0900474555208N00649296EA0000002412
    +B0900494555202N00649281EA0000002408
    +B0900514555194N00649265EA0000002406
    +B0900534555190N00649252EA0000002405
    +B0900554555187N00649237EA0000002405
    +B0900574555182N00649219EA0000002406
    +B0900594555179N00649203EA0000002410
    +B0901014555176N00649188EA0000002414
    +B0901034555171N00649170EA0000002420
    +B0901054555167N00649152EA0000002426
    +B0901074555165N00649134EA0000002433
    +B0901094555165N00649121EA0000002440
    +B0901114555163N00649111EA0000002445
    +B0901134555161N00649102EA0000002448
    +B0901154555153N00649083EA0000002449
    +B0901174555146N00649071EA0000002453
    +B0901194555136N00649060EA0000002455
    +B0901214555126N00649051EA0000002458
    +B0901234555118N00649048EA0000002461
    +B0901254555112N00649054EA0000002464
    +B0901274555111N00649061EA0000002466
    +B0901294555115N00649068EA0000002468
    +B0901324555121N00649073EA0000002470
    +B0901344555127N00649078EA0000002470
    +B0901364555134N00649084EA0000002470
    +B0901384555141N00649091EA0000002470
    +B0901404555147N00649098EA0000002471
    +B0901424555152N00649107EA0000002471
    +B0901444555157N00649115EA0000002472
    +B0901464555161N00649122EA0000002474
    +B0901484555166N00649128EA0000002475
    +B0901504555170N00649136EA0000002477
    +B0901524555173N00649145EA0000002479
    +B0901544555176N00649153EA0000002480
    +B0901564555177N00649161EA0000002483
    +B0901584555177N00649161EA0000002484
    +B0902004555175N00649182EA0000002485
    +B0902024555171N00649192EA0000002487
    +B0902044555164N00649200EA0000002488
    +B0902064555155N00649201EA0000002489
    +B0902084555144N00649192EA0000002489
    +B0902104555136N00649174EA0000002488
    +B0902124555133N00649155EA0000002489
    +B0902144555134N00649136EA0000002490
    +B0902164555135N00649115EA0000002491
    +B0902184555136N00649094EA0000002491
    +B0902204555137N00649074EA0000002491
    +B0902224555139N00649054EA0000002490
    +B0902244555140N00649034EA0000002489
    +B0902264555140N00649019EA0000002490
    +B0902284555141N00649001EA0000002489
    +B0902304555142N00648981EA0000002489
    +B0902324555142N00648961EA0000002490
    +B0902344555144N00648941EA0000002492
    +B0902364555146N00648921EA0000002495
    +B0902384555147N00648901EA0000002498
    +B0902404555149N00648881EA0000002500
    +B0902424555156N00648862EA0000002501
    +B0902444555168N00648850EA0000002502
    +B0902464555182N00648850EA0000002504
    +B0902484555191N00648857EA0000002506
    +B0902504555185N00648863EA0000002511
    +B0902524555178N00648857EA0000002516
    +B0902544555176N00648839EA0000002522
    +B0902564555182N00648818EA0000002526
    +B0902584555196N00648817EA0000002532
    +B0903004555207N00648827EA0000002537
    +B0903024555210N00648836EA0000002543
    +B0903044555204N00648840EA0000002550
    +B0903064555196N00648837EA0000002557
    +B0903084555191N00648825EA0000002563
    +B0903114555191N00648808EA0000002571
    +B0903134555197N00648794EA0000002578
    +B0903154555207N00648782EA0000002586
    +B0903174555219N00648776EA0000002594
    +B0903194555232N00648779EA0000002602
    +B0903214555242N00648788EA0000002610
    +B0903234555247N00648799EA0000002618
    +B0903254555244N00648807EA0000002625
    +B0903274555237N00648812EA0000002632
    +B0903294555231N00648813EA0000002640
    +B0903314555227N00648810EA0000002649
    +B0903334555223N00648802EA0000002656
    +B0903354555222N00648785EA0000002662
    +B0903374555228N00648767EA0000002669
    +B0903394555240N00648758EA0000002677
    +B0903414555255N00648763EA0000002687
    +B0903434555264N00648774EA0000002698
    +B0903454555267N00648785EA0000002708
    +B0903474555259N00648787EA0000002718
    +B0903494555253N00648780EA0000002728
    +B0903514555252N00648769EA0000002739
    +B0903534555252N00648755EA0000002748
    +B0903554555250N00648743EA0000002759
    +B0903574555248N00648731EA0000002770
    +B0903594555245N00648719EA0000002780
    +B0904014555240N00648707EA0000002789
    +B0904034555235N00648695EA0000002797
    +B0904054555229N00648684EA0000002804
    +B0904074555222N00648673EA0000002810
    +B0904094555215N00648661EA0000002814
    +B0904114555207N00648648EA0000002816
    +B0904134555199N00648635EA0000002819
    +B0904154555193N00648625EA0000002823
    +B0904174555187N00648616EA0000002826
    +B0904194555179N00648605EA0000002828
    +B0904214555173N00648593EA0000002830
    +B0904234555167N00648583EA0000002832
    +B0904254555161N00648572EA0000002832
    +B0904274555152N00648552EA0000002830
    +B0904294555146N00648543EA0000002829
    +B0904314555139N00648538EA0000002827
    +B0904334555130N00648531EA0000002824
    +B0904354555124N00648518EA0000002816
    +B0904374555119N00648505EA0000002815
    +B0904394555114N00648494EA0000002815
    +B0904414555109N00648484EA0000002812
    +B0904434555103N00648471EA0000002811
    +B0904454555098N00648457EA0000002810
    +B0904474555092N00648443EA0000002807
    +B0904504555085N00648427EA0000002804
    +B0904524555079N00648411EA0000002802
    +B0904544555074N00648394EA0000002800
    +B0904564555068N00648380EA0000002797
    +B0904584555061N00648366EA0000002794
    +B0905004555055N00648351EA0000002791
    +B0905024555050N00648335EA0000002790
    +B0905044555045N00648321EA0000002790
    +B0905064555039N00648309EA0000002789
    +B0905084555034N00648295EA0000002788
    +B0905104555028N00648280EA0000002787
    +B0905124555021N00648267EA0000002786
    +B0905144555014N00648255EA0000002783
    +B0905164555005N00648241EA0000002779
    +B0905184554996N00648227EA0000002773
    +B0905204554987N00648212EA0000002766
    +B0905224554978N00648197EA0000002760
    +B0905244554968N00648181EA0000002754
    +B0905264554958N00648166EA0000002749
    +B0905284554948N00648150EA0000002743
    +B0905304554939N00648133EA0000002736
    +B0905324554928N00648116EA0000002730
    +B0905344554918N00648100EA0000002723
    +B0905364554907N00648083EA0000002715
    +B0905384554895N00648063EA0000002707
    +B0905404554884N00648044EA0000002701
    +B0905424554873N00648030EA0000002695
    +B0905444554859N00648018EA0000002688
    +B0905464554845N00648004EA0000002683
    +B0905484554832N00647991EA0000002678
    +B0905504554819N00647978EA0000002671
    +B0905524554804N00647964EA0000002665
    +B0905544554790N00647950EA0000002661
    +B0905564554777N00647936EA0000002658
    +B0905584554765N00647923EA0000002655
    +B0906004554752N00647910EA0000002653
    +B0906024554740N00647896EA0000002651
    +B0906044554729N00647881EA0000002648
    +B0906064554718N00647867EA0000002645
    +B0906084554700N00647848EA0000002638
    +B0906104554688N00647834EA0000002634
    +B0906124554678N00647818EA0000002630
    +B0906144554667N00647804EA0000002626
    +B0906164554655N00647792EA0000002622
    +B0906184554643N00647778EA0000002617
    +B0906204554631N00647761EA0000002614
    +B0906224554619N00647745EA0000002610
    +B0906244554608N00647729EA0000002607
    +B0906264554596N00647713EA0000002604
    +B0906284554585N00647699EA0000002602
    +B0906314554572N00647686EA0000002600
    +B0906334554560N00647674EA0000002597
    +B0906354554549N00647659EA0000002593
    +B0906374554537N00647644EA0000002590
    +B0906394554525N00647627EA0000002587
    +B0906414554514N00647608EA0000002584
    +B0906434554504N00647590EA0000002582
    +B0906454554495N00647570EA0000002579
    +B0906474554486N00647550EA0000002577
    +B0906494554479N00647532EA0000002573
    +B0906514554471N00647512EA0000002568
    +B0906534554464N00647489EA0000002564
    +B0906554554457N00647467EA0000002561
    +B0906574554449N00647446EA0000002557
    +B0906594554442N00647423EA0000002554
    +B0907014554435N00647401EA0000002552
    +B0907034554428N00647380EA0000002548
    +B0907054554421N00647356EA0000002546
    +B0907074554414N00647335EA0000002544
    +B0907094554408N00647315EA0000002541
    +B0907114554403N00647295EA0000002538
    +B0907134554398N00647274EA0000002536
    +B0907154554392N00647255EA0000002534
    +B0907174554385N00647238EA0000002532
    +B0907194554378N00647221EA0000002530
    +B0907214554371N00647205EA0000002528
    +B0907234554366N00647190EA0000002526
    +B0907254554360N00647173EA0000002522
    +B0907274554355N00647156EA0000002519
    +B0907294554347N00647139EA0000002515
    +B0907314554339N00647121EA0000002511
    +B0907334554331N00647103EA0000002508
    +B0907354554322N00647087EA0000002506
    +B0907374554312N00647072EA0000002504
    +B0907394554303N00647057EA0000002501
    +B0907414554295N00647042EA0000002499
    +B0907434554288N00647025EA0000002496
    +B0907454554281N00647008EA0000002492
    +B0907474554273N00646992EA0000002489
    +B0907494554261N00646968EA0000002482
    +B0907514554252N00646951EA0000002478
    +B0907534554242N00646933EA0000002474
    +B0907554554233N00646914EA0000002471
    +B0907574554223N00646895EA0000002467
    +B0907594554213N00646877EA0000002465
    +B0908014554201N00646860EA0000002463
    +B0908034554190N00646844EA0000002462
    +B0908054554181N00646830EA0000002462
    +B0908074554172N00646815EA0000002460
    +B0908104554161N00646799EA0000002458
    +B0908124554152N00646782EA0000002458
    +B0908144554144N00646766EA0000002459
    +B0908164554135N00646749EA0000002458
    +B0908184554127N00646731EA0000002458
    +B0908204554120N00646713EA0000002457
    +B0908224554114N00646695EA0000002456
    +B0908244554107N00646677EA0000002453
    +B0908264554099N00646659EA0000002452
    +B0908284554092N00646642EA0000002451
    +B0908304554084N00646626EA0000002449
    +B0908324554076N00646610EA0000002447
    +B0908344554069N00646594EA0000002445
    +B0908364554062N00646577EA0000002443
    +B0908384554062N00646577EA0000002441
    +B0908404554048N00646543EA0000002439
    +B0908424554040N00646527EA0000002437
    +B0908444554032N00646511EA0000002434
    +B0908464554023N00646496EA0000002431
    +B0908484554015N00646480EA0000002429
    +B0908504554007N00646464EA0000002427
    +B0908524553999N00646448EA0000002424
    +B0908544553991N00646432EA0000002420
    +B0908564553982N00646415EA0000002417
    +B0908584553973N00646398EA0000002414
    +B0909004553964N00646383EA0000002411
    +B0909024553954N00646368EA0000002408
    +B0909044553945N00646351EA0000002404
    +B0909064553936N00646333EA0000002402
    +B0909084553928N00646319EA0000002401
    +B0909104553920N00646304EA0000002397
    +B0909124553911N00646288EA0000002394
    +B0909144553902N00646272EA0000002393
    +B0909164553894N00646257EA0000002391
    +B0909184553886N00646241EA0000002387
    +B0909204553877N00646225EA0000002385
    +B0909224553868N00646209EA0000002382
    +B0909244553859N00646195EA0000002379
    +B0909264553849N00646180EA0000002376
    +B0909284553838N00646165EA0000002373
    +B0909304553824N00646143EA0000002369
    +B0909324553814N00646128EA0000002366
    +B0909344553805N00646114EA0000002364
    +B0909364553796N00646100EA0000002361
    +B0909384553787N00646085EA0000002357
    +B0909404553778N00646068EA0000002355
    +B0909424553770N00646053EA0000002353
    +B0909444553762N00646039EA0000002350
    +B0909464553753N00646023EA0000002346
    +B0909484553744N00646008EA0000002344
    +B0909514553735N00645995EA0000002342
    +B0909534553727N00645980EA0000002339
    +B0909554553717N00645965EA0000002337
    +B0909574553708N00645951EA0000002336
    +B0909594553699N00645938EA0000002334
    +B0910014553689N00645924EA0000002332
    +B0910034553680N00645910EA0000002331
    +B0910054553671N00645897EA0000002329
    +B0910074553662N00645883EA0000002327
    +B0910094553653N00645869EA0000002325
    +B0910114553643N00645856EA0000002323
    +B0910134553633N00645844EA0000002320
    +B0910154553622N00645831EA0000002318
    +B0910174553612N00645819EA0000002315
    +B0910194553602N00645806EA0000002313
    +B0910214553592N00645792EA0000002311
    +B0910234553583N00645777EA0000002308
    +B0910254553575N00645762EA0000002306
    +B0910274553567N00645747EA0000002303
    +B0910294553559N00645731EA0000002301
    +B0910314553550N00645715EA0000002298
    +B0910334553542N00645699EA0000002296
    +B0910354553534N00645683EA0000002294
    +B0910374553526N00645668EA0000002292
    +B0910394553518N00645653EA0000002289
    +B0910414553510N00645637EA0000002286
    +B0910434553504N00645619EA0000002283
    +B0910454553500N00645601EA0000002281
    +B0910474553496N00645583EA0000002278
    +B0910494553490N00645565EA0000002275
    +B0910514553484N00645548EA0000002274
    +B0910534553478N00645531EA0000002272
    +B0910554553472N00645514EA0000002269
    +B0910574553468N00645495EA0000002266
    +B0910594553465N00645477EA0000002265
    +B0911014553463N00645459EA0000002263
    +B0911034553459N00645441EA0000002260
    +B0911054553454N00645423EA0000002258
    +B0911074553447N00645406EA0000002257
    +B0911094553440N00645391EA0000002256
    +B0911114553430N00645367EA0000002253
    +B0911134553422N00645352EA0000002251
    +B0911154553413N00645339EA0000002248
    +B0911174553405N00645324EA0000002245
    +B0911194553396N00645307EA0000002242
    +B0911214553386N00645294EA0000002240
    +B0911234553378N00645285EA0000002239
    +B0911254553370N00645274EA0000002235
    +B0911274553357N00645261EA0000002230
    +B0911294553343N00645248EA0000002228
    +B0911324553331N00645239EA0000002225
    +B0911344553319N00645228EA0000002219
    +B0911364553304N00645214EA0000002216
    +B0911384553294N00645205EA0000002215
    +B0911404553286N00645197EA0000002215
    +B0911424553276N00645187EA0000002215
    +B0911444553264N00645177EA0000002216
    +B0911464553253N00645167EA0000002219
    +B0911484553243N00645156EA0000002223
    +B0911504553233N00645144EA0000002228
    +B0911524553224N00645131EA0000002234
    +B0911544553218N00645118EA0000002240
    +B0911564553212N00645106EA0000002245
    +B0911584553204N00645094EA0000002249
    +B0912004553194N00645086EA0000002252
    +B0912024553188N00645087EA0000002255
    +B0912044553176N00645102EA0000002258
    +B0912064553176N00645118EA0000002260
    +B0912084553179N00645129EA0000002263
    +B0912104553186N00645135EA0000002264
    +B0912124553195N00645136EA0000002265
    +B0912144553201N00645125EA0000002267
    +B0912164553204N00645113EA0000002270
    +B0912184553205N00645100EA0000002274
    +B0912204553204N00645084EA0000002278
    +B0912224553198N00645069EA0000002282
    +B0912244553191N00645058EA0000002286
    +B0912264553180N00645057EA0000002289
    +B0912284553171N00645065EA0000002291
    +B0912304553169N00645080EA0000002292
    +B0912324553174N00645095EA0000002292
    +B0912344553184N00645102EA0000002296
    +B0912364553193N00645097EA0000002298
    +B0912384553201N00645090EA0000002301
    +B0912404553206N00645079EA0000002306
    +B0912424553206N00645064EA0000002308
    +B0912444553204N00645048EA0000002312
    +B0912464553198N00645033EA0000002316
    +B0912484553187N00645025EA0000002319
    +B0912504553177N00645028EA0000002322
    +B0912524553167N00645042EA0000002325
    +B0912544553166N00645055EA0000002326
    +B0912564553173N00645066EA0000002326
    +B0912584553184N00645070EA0000002327
    +B0913004553193N00645071EA0000002329
    +B0913024553201N00645068EA0000002331
    +B0913044553209N00645060EA0000002332
    +B0913064553215N00645049EA0000002333
    +B0913084553218N00645033EA0000002334
    +B0913104553216N00645015EA0000002336
    +B0913134553212N00645000EA0000002338
    +B0913154553205N00644987EA0000002341
    +B0913174553198N00644976EA0000002345
    +B0913194553189N00644969EA0000002349
    +B0913214553180N00644971EA0000002352
    +B0913234553173N00644980EA0000002354
    +B0913254553174N00644991EA0000002356
    +B0913274553179N00644998EA0000002358
    +B0913294553186N00645001EA0000002361
    +B0913314553194N00644998EA0000002364
    +B0913334553201N00644989EA0000002367
    +B0913354553207N00644976EA0000002370
    +B0913374553209N00644961EA0000002374
    +B0913394553206N00644944EA0000002376
    +B0913414553197N00644932EA0000002380
    +B0913434553186N00644934EA0000002385
    +B0913454553180N00644945EA0000002389
    +B0913474553182N00644956EA0000002393
    +B0913494553190N00644960EA0000002396
    +B0913514553199N00644954EA0000002399
    +B0913534553206N00644942EA0000002402
    +B0913554553209N00644924EA0000002405
    +B0913574553205N00644906EA0000002408
    +B0913594553196N00644897EA0000002412
    +B0914014553188N00644899EA0000002417
    +B0914034553180N00644905EA0000002421
    +B0914054553172N00644913EA0000002427
    +B0914074553167N00644922EA0000002431
    +B0914094553170N00644933EA0000002434
    +B0914114553180N00644937EA0000002436
    +B0914134553191N00644930EA0000002437
    +B0914154553200N00644919EA0000002441
    +B0914174553207N00644905EA0000002445
    +B0914194553210N00644889EA0000002450
    +B0914214553212N00644872EA0000002454
    +B0914234553208N00644856EA0000002458
    +B0914254553198N00644848EA0000002461
    +B0914274553189N00644853EA0000002464
    +B0914294553185N00644864EA0000002468
    +B0914314553187N00644874EA0000002472
    +B0914334553200N00644878EA0000002477
    +B0914354553209N00644868EA0000002480
    +B0914374553214N00644850EA0000002484
    +B0914394553214N00644833EA0000002486
    +B0914414553206N00644822EA0000002490
    +B0914434553195N00644817EA0000002492
    +B0914454553186N00644818EA0000002495
    +B0914474553178N00644821EA0000002496
    +B0914504553172N00644830EA0000002496
    +B0914524553170N00644844EA0000002498
    +B0914544553175N00644854EA0000002502
    +B0914564553184N00644858EA0000002504
    +B0914584553194N00644860EA0000002505
    +B0915004553205N00644858EA0000002507
    +B0915024553215N00644852EA0000002509
    +B0915044553225N00644840EA0000002511
    +B0915064553230N00644824EA0000002513
    +B0915084553231N00644805EA0000002514
    +B0915104553226N00644787EA0000002516
    +B0915124553216N00644777EA0000002517
    +B0915144553204N00644780EA0000002518
    +B0915164553195N00644788EA0000002521
    +B0915184553188N00644796EA0000002525
    +B0915204553183N00644801EA0000002530
    +B0915224553179N00644807EA0000002534
    +B0915244553179N00644816EA0000002537
    +B0915264553183N00644826EA0000002538
    +B0915284553189N00644832EA0000002539
    +B0915304553199N00644829EA0000002540
    +B0915324553206N00644818EA0000002543
    +B0915344553211N00644802EA0000002543
    +B0915364553215N00644780EA0000002542
    +B0915384553214N00644760EA0000002544
    +B0915404553210N00644744EA0000002546
    +B0915424553202N00644734EA0000002546
    +B0915444553192N00644730EA0000002547
    +B0915464553185N00644733EA0000002547
    +B0915484553179N00644741EA0000002546
    +B0915504553176N00644755EA0000002546
    +B0915524553175N00644769EA0000002547
    +B0915544553176N00644781EA0000002548
    +B0915564553178N00644794EA0000002549
    +B0915584553179N00644808EA0000002551
    +B0916004553177N00644818EA0000002553
    +B0916024553175N00644828EA0000002557
    +B0916044553172N00644836EA0000002562
    +B0916064553170N00644842EA0000002566
    +B0916084553172N00644850EA0000002569
    +B0916104553180N00644856EA0000002572
    +B0916124553194N00644850EA0000002577
    +B0916144553202N00644842EA0000002582
    +B0916164553206N00644827EA0000002586
    +B0916184553207N00644810EA0000002590
    +B0916204553203N00644796EA0000002593
    +B0916224553194N00644786EA0000002597
    +B0916244553184N00644781EA0000002601
    +B0916264553174N00644779EA0000002605
    +B0916284553165N00644778EA0000002608
    +B0916314553154N00644771EA0000002611
    +B0916334553143N00644762EA0000002612
    +B0916354553130N00644753EA0000002615
    +B0916374553119N00644749EA0000002619
    +B0916394553110N00644747EA0000002621
    +B0916414553100N00644747EA0000002621
    +B0916434553086N00644746EA0000002622
    +B0916454553073N00644743EA0000002626
    +B0916474553061N00644738EA0000002627
    +B0916494553049N00644734EA0000002627
    +B0916514553036N00644728EA0000002625
    +B0916534553023N00644722EA0000002625
    +B0916554553010N00644716EA0000002624
    +B0916574552997N00644707EA0000002624
    +B0916594552986N00644696EA0000002626
    +B0917014552975N00644688EA0000002628
    +B0917034552962N00644681EA0000002631
    +B0917054552950N00644675EA0000002635
    +B0917074552940N00644676EA0000002639
    +B0917094552930N00644680EA0000002641
    +B0917114552918N00644683EA0000002640
    +B0917134552905N00644677EA0000002639
    +B0917154552894N00644660EA0000002636
    +B0917174552888N00644641EA0000002633
    +B0917194552881N00644622EA0000002630
    +B0917214552871N00644605EA0000002628
    +B0917234552860N00644591EA0000002627
    +B0917254552846N00644580EA0000002627
    +B0917274552832N00644568EA0000002627
    +B0917294552820N00644555EA0000002626
    +B0917314552805N00644544EA0000002623
    +B0917334552789N00644533EA0000002621
    +B0917354552775N00644522EA0000002621
    +B0917374552762N00644511EA0000002618
    +B0917394552755N00644506EA0000002614
    +B0917414552732N00644491EA0000002611
    +B0917434552714N00644481EA0000002607
    +B0917454552696N00644471EA0000002602
    +B0917474552679N00644459EA0000002598
    +B0917494552665N00644447EA0000002595
    +B0917514552646N00644436EA0000002590
    +B0917534552632N00644427EA0000002588
    +B0917554552623N00644420EA0000002587
    +B0917574552613N00644415EA0000002583
    +B0917594552600N00644405EA0000002580
    +B0918014552588N00644395EA0000002580
    +B0918034552581N00644390EA0000002583
    +B0918054552573N00644386EA0000002584
    +B0918074552561N00644382EA0000002585
    +B0918104552549N00644377EA0000002588
    +B0918124552539N00644368EA0000002593
    +B0918144552530N00644358EA0000002599
    +B0918164552524N00644341EA0000002603
    +B0918184552525N00644323EA0000002608
    +B0918204552533N00644320EA0000002611
    +B0918224552539N00644333EA0000002614
    +B0918244552533N00644345EA0000002617
    +B0918264552521N00644345EA0000002621
    +B0918284552511N00644333EA0000002624
    +B0918304552508N00644314EA0000002626
    +B0918324552516N00644302EA0000002628
    +B0918344552526N00644311EA0000002631
    +B0918364552528N00644325EA0000002634
    +B0918384552520N00644333EA0000002637
    +B0918404552509N00644333EA0000002640
    +B0918424552497N00644325EA0000002641
    +B0918444552487N00644311EA0000002643
    +B0918464552479N00644300EA0000002645
    +B0918484552471N00644290EA0000002647
    +B0918504552462N00644276EA0000002648
    +B0918524552452N00644261EA0000002648
    +B0918544552442N00644245EA0000002651
    +B0918564552435N00644231EA0000002655
    +B0918584552426N00644220EA0000002657
    +B0919004552415N00644212EA0000002658
    +B0919024552404N00644209EA0000002660
    +B0919044552395N00644212EA0000002660
    +B0919064552386N00644218EA0000002661
    +B0919084552376N00644223EA0000002664
    +B0919104552368N00644229EA0000002666
    +B0919124552361N00644234EA0000002667
    +B0919144552352N00644239EA0000002669
    +B0919164552343N00644244EA0000002673
    +B0919184552337N00644250EA0000002674
    +B0919204552331N00644256EA0000002674
    +B0919224552325N00644262EA0000002674
    +B0919244552316N00644266EA0000002672
    +B0919264552303N00644264EA0000002670
    +B0919284552293N00644255EA0000002670
    +B0919304552278N00644243EA0000002667
    +B0919324552265N00644234EA0000002662
    +B0919344552252N00644223EA0000002656
    +B0919364552240N00644212EA0000002648
    +B0919384552225N00644199EA0000002639
    +B0919404552209N00644184EA0000002633
    +B0919424552195N00644171EA0000002628
    +B0919444552181N00644157EA0000002621
    +B0919464552167N00644143EA0000002616
    +B0919484552153N00644128EA0000002609
    +B0919514552138N00644112EA0000002603
    +B0919534552123N00644097EA0000002598
    +B0919554552109N00644083EA0000002592
    +B0919574552096N00644068EA0000002586
    +B0919594552082N00644054EA0000002579
    +B0920014552068N00644040EA0000002573
    +B0920034552055N00644026EA0000002567
    +B0920054552042N00644012EA0000002561
    +B0920074552029N00643997EA0000002556
    +B0920094552017N00643980EA0000002552
    +B0920114552004N00643965EA0000002547
    +B0920134551991N00643950EA0000002541
    +B0920154551978N00643934EA0000002535
    +B0920174551964N00643917EA0000002528
    +B0920194551950N00643900EA0000002521
    +B0920214551935N00643881EA0000002514
    +B0920234551919N00643863EA0000002508
    +B0920254551904N00643846EA0000002503
    +B0920274551888N00643829EA0000002497
    +B0920294551872N00643812EA0000002492
    +B0920314551856N00643795EA0000002487
    +B0920334551839N00643779EA0000002482
    +B0920354551823N00643763EA0000002479
    +B0920374551807N00643747EA0000002475
    +B0920394551790N00643731EA0000002472
    +B0920414551773N00643715EA0000002469
    +B0920434551757N00643700EA0000002465
    +B0920454551739N00643685EA0000002461
    +B0920474551722N00643670EA0000002458
    +B0920494551704N00643655EA0000002454
    +B0920514551687N00643640EA0000002451
    +B0920534551671N00643624EA0000002446
    +B0920554551653N00643608EA0000002442
    +B0920574551635N00643593EA0000002439
    +B0920594551619N00643577EA0000002438
    +B0921014551602N00643562EA0000002437
    +B0921034551586N00643547EA0000002434
    +B0921054551571N00643530EA0000002430
    +B0921074551554N00643514EA0000002426
    +B0921094551536N00643500EA0000002422
    +B0921114551509N00643482EA0000002419
    +B0921134551493N00643469EA0000002416
    +B0921154551476N00643458EA0000002413
    +B0921174551459N00643445EA0000002409
    +B0921194551442N00643432EA0000002405
    +B0921214551426N00643419EA0000002402
    +B0921234551410N00643406EA0000002399
    +B0921254551394N00643392EA0000002395
    +B0921274551379N00643378EA0000002392
    +B0921294551364N00643363EA0000002388
    +B0921324551349N00643349EA0000002385
    +B0921344551335N00643335EA0000002384
    +B0921364551323N00643322EA0000002382
    +B0921384551311N00643310EA0000002378
    +B0921404551296N00643298EA0000002375
    +B0921424551283N00643286EA0000002374
    +B0921444551271N00643273EA0000002371
    +B0921464551257N00643261EA0000002368
    +B0921484551244N00643248EA0000002365
    +B0921504551231N00643235EA0000002363
    +B0921524551218N00643223EA0000002360
    +B0921544551205N00643210EA0000002357
    +B0921564551192N00643197EA0000002355
    +B0921584551179N00643184EA0000002352
    +B0922004551165N00643173EA0000002349
    +B0922024551151N00643163EA0000002346
    +B0922044551137N00643151EA0000002344
    +B0922064551123N00643140EA0000002341
    +B0922084551110N00643127EA0000002338
    +B0922104551097N00643113EA0000002336
    +B0922124551084N00643099EA0000002333
    +B0922144551072N00643086EA0000002330
    +B0922164551060N00643071EA0000002327
    +B0922184551047N00643058EA0000002324
    +B0922204551034N00643045EA0000002322
    +B0922224551022N00643032EA0000002319
    +B0922244551009N00643020EA0000002316
    +B0922264550996N00643009EA0000002314
    +B0922284550982N00642999EA0000002312
    +B0922304550970N00642989EA0000002309
    +B0922324550957N00642978EA0000002305
    +B0922344550943N00642966EA0000002301
    +B0922364550931N00642952EA0000002299
    +B0922384550919N00642938EA0000002295
    +B0922404550908N00642923EA0000002292
    +B0922424550896N00642908EA0000002289
    +B0922444550886N00642894EA0000002286
    +B0922464550876N00642878EA0000002281
    +B0922484550865N00642862EA0000002277
    +B0922504550853N00642846EA0000002273
    +B0922524550838N00642824EA0000002267
    +B0922544550828N00642808EA0000002262
    +B0922564550818N00642791EA0000002258
    +B0922584550808N00642777EA0000002254
    +B0923004550797N00642765EA0000002248
    +B0923024550784N00642752EA0000002242
    +B0923044550773N00642740EA0000002239
    +B0923064550764N00642732EA0000002237
    +B0923084550754N00642723EA0000002233
    +B0923114550742N00642713EA0000002231
    +B0923134550729N00642704EA0000002231
    +B0923154550718N00642695EA0000002230
    +B0923174550707N00642683EA0000002231
    +B0923194550698N00642675EA0000002233
    +B0923214550690N00642668EA0000002233
    +B0923234550682N00642661EA0000002231
    +B0923254550673N00642650EA0000002229
    +B0923274550663N00642639EA0000002230
    +B0923294550653N00642631EA0000002232
    +B0923314550645N00642623EA0000002233
    +B0923334550638N00642613EA0000002233
    +B0923354550631N00642603EA0000002234
    +B0923374550623N00642594EA0000002234
    +B0923394550616N00642584EA0000002234
    +B0923414550609N00642575EA0000002232
    +B0923434550601N00642565EA0000002229
    +B0923454550593N00642552EA0000002225
    +B0923474550587N00642537EA0000002221
    +B0923494550580N00642522EA0000002219
    +B0923514550575N00642508EA0000002217
    +B0923534550570N00642493EA0000002216
    +B0923554550564N00642478EA0000002217
    +B0923574550558N00642464EA0000002217
    +B0923594550553N00642451EA0000002217
    +B0924014550549N00642437EA0000002215
    +B0924034550543N00642420EA0000002213
    +B0924054550537N00642403EA0000002212
    +B0924074550531N00642388EA0000002214
    +B0924094550526N00642374EA0000002218
    +B0924114550520N00642360EA0000002221
    +B0924134550514N00642346EA0000002224
    +B0924154550508N00642333EA0000002227
    +B0924174550505N00642320EA0000002232
    +B0924194550507N00642311EA0000002235
    +B0924214550516N00642311EA0000002238
    +B0924234550520N00642324EA0000002240
    +B0924254550512N00642340EA0000002241
    +B0924274550496N00642341EA0000002242
    +B0924294550483N00642327EA0000002244
    +B0924314550475N00642313EA0000002248
    +B0924334550463N00642296EA0000002251
    +B0924354550456N00642282EA0000002251
    +B0924374550447N00642268EA0000002251
    +B0924394550434N00642257EA0000002251
    +B0924414550422N00642246EA0000002250
    +B0924434550410N00642231EA0000002251
    +B0924454550398N00642219EA0000002252
    +B0924474550388N00642209EA0000002256
    +B0924504550379N00642200EA0000002259
    +B0924524550370N00642188EA0000002262
    +B0924544550362N00642178EA0000002266
    +B0924564550354N00642169EA0000002271
    +B0924584550346N00642158EA0000002276
    +B0925004550342N00642145EA0000002280
    +B0925024550343N00642132EA0000002284
    +B0925044550352N00642127EA0000002287
    +B0925064550360N00642133EA0000002291
    +B0925084550362N00642145EA0000002295
    +B0925104550356N00642157EA0000002299
    +B0925124550344N00642161EA0000002304
    +B0925144550333N00642156EA0000002310
    +B0925164550326N00642146EA0000002315
    +B0925184550322N00642133EA0000002319
    +B0925204550324N00642119EA0000002322
    +B0925224550332N00642114EA0000002325
    +B0925244550340N00642120EA0000002328
    +B0925264550342N00642135EA0000002331
    +B0925284550337N00642147EA0000002334
    +B0925304550325N00642153EA0000002337
    +B0925324550313N00642148EA0000002341
    +B0925344550305N00642138EA0000002346
    +B0925364550299N00642128EA0000002350
    +B0925384550295N00642115EA0000002353
    +B0925404550296N00642101EA0000002356
    +B0925424550302N00642091EA0000002358
    +B0925444550310N00642091EA0000002361
    +B0925464550315N00642101EA0000002364
    +B0925484550314N00642115EA0000002366
    +B0925504550306N00642126EA0000002369
    +B0925524550293N00642128EA0000002372
    +B0925544550283N00642119EA0000002375
    +B0925564550277N00642107EA0000002379
    +B0925584550272N00642097EA0000002383
    +B0926004550268N00642087EA0000002385
    +B0926024550266N00642073EA0000002387
    +B0926044550270N00642061EA0000002389
    +B0926064550279N00642059EA0000002391
    +B0926084550285N00642075EA0000002393
    +B0926104550280N00642087EA0000002394
    +B0926124550268N00642089EA0000002396
    +B0926144550259N00642078EA0000002399
    +B0926164550252N00642068EA0000002402
    +B0926184550243N00642058EA0000002405
    +B0926204550237N00642047EA0000002408
    +B0926224550231N00642036EA0000002410
    +B0926244550225N00642025EA0000002412
    +B0926264550217N00642013EA0000002414
    +B0926294550209N00642001EA0000002416
    +B0926314550203N00641990EA0000002419
    +B0926334550195N00641982EA0000002422
    +B0926354550186N00641973EA0000002422
    +B0926374550179N00641963EA0000002421
    +B0926394550177N00641953EA0000002420
    +B0926414550180N00641944EA0000002418
    +B0926434550189N00641945EA0000002416
    +B0926454550196N00641957EA0000002415
    +B0926474550197N00641975EA0000002414
    +B0926494550191N00641992EA0000002414
    +B0926514550179N00642006EA0000002413
    +B0926534550166N00642007EA0000002412
    +B0926554550157N00641997EA0000002410
    +B0926574550149N00641986EA0000002410
    +B0926594550141N00641974EA0000002410
    +B0927014550135N00641961EA0000002410
    +B0927034550127N00641948EA0000002411
    +B0927054550120N00641935EA0000002412
    +B0927074550114N00641921EA0000002414
    +B0927094550110N00641907EA0000002415
    +B0927114550106N00641893EA0000002417
    +B0927134550103N00641880EA0000002419
    +B0927154550099N00641865EA0000002422
    +B0927174550099N00641850EA0000002425
    +B0927194550097N00641834EA0000002429
    +B0927214550096N00641818EA0000002433
    +B0927234550096N00641804EA0000002439
    +B0927254550097N00641794EA0000002446
    +B0927274550102N00641787EA0000002452
    +B0927294550110N00641797EA0000002458
    +B0927314550114N00641814EA0000002464
    +B0927334550108N00641827EA0000002472
    +B0927354550096N00641829EA0000002479
    +B0927374550083N00641821EA0000002485
    +B0927394550075N00641807EA0000002492
    +B0927414550070N00641793EA0000002501
    +B0927434550066N00641774EA0000002511
    +B0927454550068N00641762EA0000002517
    +B0927474550073N00641758EA0000002522
    +B0927494550079N00641764EA0000002527
    +B0927514550082N00641777EA0000002531
    +B0927534550077N00641792EA0000002535
    +B0927554550064N00641801EA0000002539
    +B0927574550054N00641799EA0000002546
    +B0928004550043N00641797EA0000002551
    +B0928024550031N00641792EA0000002555
    +B0928044550023N00641777EA0000002558
    +B0928064550022N00641757EA0000002561
    +B0928084550029N00641744EA0000002564
    +B0928104550038N00641746EA0000002569
    +B0928124550041N00641759EA0000002574
    +B0928144550037N00641772EA0000002579
    +B0928164550027N00641784EA0000002584
    +B0928184550014N00641788EA0000002589
    +B0928204550002N00641781EA0000002593
    +B0928224549993N00641767EA0000002597
    +B0928244549990N00641748EA0000002601
    +B0928264549994N00641733EA0000002606
    +B0928284550002N00641727EA0000002610
    +B0928304550009N00641732EA0000002613
    +B0928324550012N00641747EA0000002617
    +B0928344550006N00641760EA0000002621
    +B0928364549995N00641768EA0000002627
    +B0928384549983N00641769EA0000002631
    +B0928404549971N00641763EA0000002634
    +B0928424549961N00641753EA0000002637
    +B0928444549955N00641735EA0000002640
    +B0928464549955N00641718EA0000002643
    +B0928484549959N00641708EA0000002647
    +B0928504549965N00641704EA0000002650
    +B0928524549972N00641706EA0000002653
    +B0928544549975N00641717EA0000002655
    +B0928564549971N00641731EA0000002657
    +B0928584549960N00641738EA0000002659
    +B0929004549947N00641738EA0000002662
    +B0929024549934N00641735EA0000002664
    +B0929044549922N00641731EA0000002666
    +B0929064549908N00641727EA0000002668
    +B0929084549895N00641724EA0000002671
    +B0929104549883N00641719EA0000002673
    +B0929124549872N00641716EA0000002676
    +B0929144549860N00641714EA0000002677
    +B0929164549848N00641712EA0000002676
    +B0929184549831N00641707EA0000002675
    +B0929204549819N00641702EA0000002674
    +B0929224549805N00641696EA0000002673
    +B0929244549793N00641691EA0000002672
    +B0929264549780N00641687EA0000002671
    +B0929284549767N00641684EA0000002669
    +B0929304549753N00641681EA0000002666
    +B0929324549740N00641674EA0000002663
    +B0929344549728N00641666EA0000002661
    +B0929374549716N00641662EA0000002660
    +B0929394549702N00641658EA0000002657
    +B0929414549687N00641652EA0000002654
    +B0929434549674N00641647EA0000002652
    +B0929454549661N00641644EA0000002650
    +B0929474549648N00641639EA0000002647
    +B0929494549636N00641635EA0000002645
    +B0929514549623N00641631EA0000002641
    +B0929534549610N00641628EA0000002639
    +B0929554549598N00641624EA0000002637
    +B0929574549587N00641618EA0000002635
    +B0929594549576N00641614EA0000002633
    +B0930014549564N00641610EA0000002632
    +B0930034549551N00641604EA0000002632
    +B0930054549540N00641600EA0000002633
    +B0930074549528N00641596EA0000002632
    +B0930094549514N00641588EA0000002630
    +B0930114549500N00641579EA0000002631
    +B0930134549486N00641573EA0000002630
    +B0930154549471N00641565EA0000002628
    +B0930174549454N00641556EA0000002627
    +B0930194549438N00641547EA0000002628
    +B0930214549422N00641537EA0000002629
    +B0930234549407N00641525EA0000002630
    +B0930254549394N00641513EA0000002633
    +B0930274549385N00641505EA0000002638
    +B0930294549376N00641498EA0000002642
    +B0930314549365N00641493EA0000002646
    +B0930334549353N00641486EA0000002651
    +B0930354549342N00641477EA0000002656
    +B0930374549330N00641467EA0000002659
    +B0930394549319N00641458EA0000002662
    +B0930414549307N00641448EA0000002664
    +B0930434549295N00641435EA0000002667
    +B0930454549283N00641424EA0000002671
    +B0930474549271N00641415EA0000002676
    +B0930494549258N00641407EA0000002680
    +B0930514549244N00641400EA0000002683
    +B0930534549231N00641387EA0000002685
    +B0930554549221N00641375EA0000002687
    +B0930574549210N00641365EA0000002687
    +B0930594549192N00641345EA0000002690
    +B0931014549180N00641335EA0000002693
    +B0931034549170N00641327EA0000002697
    +B0931054549159N00641319EA0000002699
    +B0931074549149N00641311EA0000002700
    +B0931094549137N00641301EA0000002700
    +B0931114549125N00641290EA0000002697
    +B0931134549113N00641275EA0000002697
    +B0931154549104N00641261EA0000002697
    +B0931184549094N00641249EA0000002696
    +B0931204549083N00641238EA0000002696
    +B0931224549074N00641227EA0000002697
    +B0931244549066N00641218EA0000002700
    +B0931264549056N00641208EA0000002701
    +B0931284549046N00641196EA0000002705
    +B0931304549037N00641183EA0000002710
    +B0931324549030N00641171EA0000002716
    +B0931344549019N00641163EA0000002720
    +B0931364549007N00641159EA0000002725
    +B0931384548996N00641156EA0000002728
    +B0931404548984N00641151EA0000002731
    +B0931424548972N00641141EA0000002733
    +B0931444548961N00641131EA0000002734
    +B0931464548949N00641122EA0000002734
    +B0931484548936N00641112EA0000002734
    +B0931504548925N00641102EA0000002735
    +B0931524548913N00641093EA0000002735
    +B0931544548900N00641082EA0000002734
    +B0931564548886N00641069EA0000002735
    +B0931584548872N00641056EA0000002737
    +B0932004548862N00641047EA0000002741
    +B0932024548853N00641042EA0000002742
    +B0932044548843N00641035EA0000002742
    +B0932064548828N00641021EA0000002743
    +B0932084548816N00641008EA0000002745
    +B0932104548809N00641003EA0000002749
    +B0932124548800N00640997EA0000002751
    +B0932144548786N00640988EA0000002754
    +B0932164548775N00640977EA0000002759
    +B0932184548766N00640967EA0000002761
    +B0932204548755N00640958EA0000002762
    +B0932224548744N00640947EA0000002764
    +B0932244548735N00640936EA0000002765
    +B0932264548725N00640925EA0000002764
    +B0932284548712N00640912EA0000002762
    +B0932304548700N00640896EA0000002763
    +B0932324548690N00640883EA0000002763
    +B0932344548680N00640870EA0000002762
    +B0932364548665N00640848EA0000002761
    +B0932384548655N00640833EA0000002760
    +B0932404548646N00640820EA0000002760
    +B0932424548637N00640807EA0000002758
    +B0932444548629N00640793EA0000002756
    +B0932464548620N00640778EA0000002753
    +B0932484548610N00640764EA0000002751
    +B0932504548601N00640749EA0000002748
    +B0932524548592N00640733EA0000002746
    +B0932544548584N00640719EA0000002745
    +B0932574548575N00640707EA0000002743
    +B0932594548565N00640692EA0000002742
    +B0933014548556N00640676EA0000002741
    +B0933034548547N00640661EA0000002740
    +B0933054548536N00640647EA0000002738
    +B0933074548525N00640631EA0000002737
    +B0933094548514N00640617EA0000002738
    +B0933114548503N00640605EA0000002740
    +B0933134548493N00640592EA0000002741
    +B0933154548482N00640579EA0000002740
    +B0933174548471N00640564EA0000002740
    +B0933194548462N00640549EA0000002740
    +B0933214548454N00640534EA0000002738
    +B0933234548445N00640518EA0000002737
    +B0933254548437N00640501EA0000002736
    +B0933274548428N00640487EA0000002735
    +B0933294548418N00640473EA0000002733
    +B0933314548409N00640458EA0000002733
    +B0933334548402N00640447EA0000002734
    +B0933354548394N00640438EA0000002732
    +B0933374548385N00640426EA0000002729
    +B0933394548376N00640410EA0000002728
    +B0933414548367N00640398EA0000002727
    +B0933434548359N00640386EA0000002726
    +B0933454548351N00640373EA0000002725
    +B0933474548344N00640361EA0000002723
    +B0933494548335N00640348EA0000002722
    +B0933514548326N00640335EA0000002722
    +B0933534548317N00640324EA0000002724
    +B0933554548311N00640318EA0000002727
    +B0933574548302N00640314EA0000002730
    +B0933594548293N00640307EA0000002734
    +B0934014548285N00640301EA0000002738
    +B0934034548277N00640295EA0000002740
    +B0934054548269N00640289EA0000002742
    +B0934074548260N00640283EA0000002745
    +B0934094548252N00640279EA0000002747
    +B0934114548243N00640274EA0000002747
    +B0934134548235N00640267EA0000002748
    +B0934154548226N00640260EA0000002749
    +B0934174548213N00640248EA0000002751
    +B0934194548203N00640242EA0000002754
    +B0934214548196N00640240EA0000002756
    +B0934234548188N00640237EA0000002757
    +B0934254548179N00640233EA0000002757
    +B0934274548168N00640227EA0000002753
    +B0934294548158N00640218EA0000002750
    +B0934314548150N00640205EA0000002745
    +B0934334548140N00640191EA0000002740
    +B0934354548130N00640174EA0000002733
    +B0934384548119N00640154EA0000002728
    +B0934404548111N00640136EA0000002724
    +B0934424548103N00640124EA0000002721
    +B0934444548094N00640114EA0000002715
    +B0934464548083N00640101EA0000002711
    +B0934484548074N00640087EA0000002709
    +B0934504548067N00640072EA0000002705
    +B0934524548059N00640055EA0000002701
    +B0934544548051N00640036EA0000002699
    +B0934564548044N00640022EA0000002698
    +B0934584548034N00640011EA0000002694
    +B0935004548023N00639998EA0000002690
    +B0935024548017N00639989EA0000002685
    +B0935044547999N00639962EA0000002683
    +B0935064547995N00639954EA0000002684
    +B0935084547984N00639943EA0000002682
    +B0935104547974N00639932EA0000002680
    +B0935124547966N00639919EA0000002681
    +B0935144547960N00639905EA0000002682
    +B0935164547956N00639890EA0000002681
    +B0935184547953N00639870EA0000002679
    +B0935204547948N00639848EA0000002677
    +B0935224547945N00639827EA0000002677
    +B0935244547942N00639808EA0000002676
    +B0935264547939N00639787EA0000002672
    +B0935284547936N00639765EA0000002669
    +B0935304547934N00639743EA0000002667
    +B0935324547933N00639723EA0000002664
    +B0935344547932N00639701EA0000002660
    +B0935364547933N00639678EA0000002656
    +B0935384547933N00639656EA0000002654
    +B0935404547934N00639635EA0000002650
    +B0935424547935N00639613EA0000002647
    +B0935444547937N00639590EA0000002645
    +B0935464547936N00639570EA0000002643
    +B0935484547936N00639551EA0000002642
    +B0935504547936N00639533EA0000002641
    +B0935524547932N00639519EA0000002640
    +B0935544547926N00639502EA0000002639
    +B0935564547923N00639483EA0000002637
    +B0935584547916N00639449EA0000002633
    +B0936004547914N00639424EA0000002633
    +B0936024547913N00639398EA0000002634
    +B0936044547912N00639377EA0000002638
    +B0936064547908N00639365EA0000002643
    +B0936084547899N00639352EA0000002646
    +B0936104547891N00639333EA0000002649
    +B0936124547883N00639314EA0000002651
    +B0936144547873N00639295EA0000002651
    +B0936164547864N00639275EA0000002652
    +B0936194547856N00639253EA0000002652
    +B0936214547849N00639232EA0000002652
    +B0936234547842N00639211EA0000002652
    +B0936254547837N00639188EA0000002650
    +B0936274547832N00639165EA0000002650
    +B0936294547827N00639143EA0000002650
    +B0936314547822N00639121EA0000002648
    +B0936334547818N00639097EA0000002646
    +B0936354547816N00639073EA0000002645
    +B0936374547816N00639051EA0000002645
    +B0936394547816N00639041EA0000002642
    +B0936414547814N00639010EA0000002639
    +B0936434547814N00638988EA0000002636
    +B0936454547811N00638971EA0000002635
    +B0936474547805N00638960EA0000002633
    +B0936494547801N00638944EA0000002628
    +B0936514547799N00638921EA0000002622
    +B0936534547798N00638896EA0000002618
    +B0936554547796N00638876EA0000002615
    +B0936574547793N00638857EA0000002611
    +B0936594547790N00638839EA0000002609
    +B0937014547789N00638822EA0000002605
    +B0937034547788N00638803EA0000002600
    +B0937054547789N00638781EA0000002597
    +B0937074547793N00638763EA0000002595
    +B0937094547795N00638748EA0000002590
    +B0937114547797N00638731EA0000002585
    +B0937134547801N00638711EA0000002580
    +B0937154547804N00638691EA0000002577
    +B0937174547805N00638672EA0000002574
    +B0937194547805N00638654EA0000002570
    +B0937214547806N00638636EA0000002568
    +B0937234547806N00638618EA0000002567
    +B0937254547804N00638602EA0000002566
    +B0937274547801N00638584EA0000002563
    +B0937294547799N00638564EA0000002562
    +B0937314547798N00638543EA0000002560
    +B0937334547796N00638524EA0000002558
    +B0937354547794N00638506EA0000002556
    +B0937374547794N00638488EA0000002555
    +B0937394547791N00638462EA0000002555
    +B0937414547791N00638449EA0000002556
    +B0937434547790N00638432EA0000002556
    +B0937454547789N00638412EA0000002558
    +B0937474547792N00638394EA0000002561
    +B0937494547793N00638378EA0000002563
    +B0937514547791N00638360EA0000002563
    +B0937534547790N00638341EA0000002565
    +B0937554547790N00638323EA0000002567
    +B0937584547789N00638306EA0000002568
    +B0938004547789N00638287EA0000002568
    +B0938024547788N00638266EA0000002569
    +B0938044547785N00638250EA0000002572
    +B0938064547781N00638235EA0000002573
    +B0938084547776N00638220EA0000002575
    +B0938104547771N00638205EA0000002578
    +B0938124547766N00638190EA0000002579
    +B0938144547761N00638174EA0000002579
    +B0938164547757N00638166EA0000002580
    +B0938184547748N00638141EA0000002581
    +B0938204547745N00638126EA0000002582
    +B0938224547741N00638110EA0000002581
    +B0938244547734N00638095EA0000002580
    +B0938264547729N00638080EA0000002578
    +B0938284547724N00638065EA0000002576
    +B0938304547720N00638048EA0000002573
    +B0938324547717N00638031EA0000002572
    +B0938344547712N00638014EA0000002572
    +B0938364547708N00637997EA0000002574
    +B0938384547703N00637981EA0000002576
    +B0938404547697N00637965EA0000002577
    +B0938424547689N00637950EA0000002578
    +B0938444547682N00637937EA0000002578
    +B0938464547675N00637923EA0000002576
    +B0938484547667N00637904EA0000002574
    +B0938504547660N00637884EA0000002574
    +B0938524547655N00637864EA0000002576
    +B0938544547650N00637846EA0000002577
    +B0938564547645N00637828EA0000002578
    +B0938584547639N00637810EA0000002577
    +B0939004547632N00637790EA0000002577
    +B0939024547626N00637777EA0000002580
    +B0939044547623N00637772EA0000002583
    +B0939064547619N00637764EA0000002584
    +B0939084547614N00637750EA0000002587
    +B0939104547610N00637738EA0000002592
    +B0939124547608N00637730EA0000002598
    +B0939144547607N00637720EA0000002603
    +B0939164547607N00637706EA0000002607
    +B0939184547605N00637692EA0000002611
    +B0939204547602N00637671EA0000002616
    +B0939224547599N00637657EA0000002618
    +B0939244547596N00637641EA0000002619
    +B0939264547593N00637625EA0000002618
    +B0939284547590N00637608EA0000002616
    +B0939304547586N00637591EA0000002613
    +B0939324547582N00637572EA0000002611
    +B0939344547580N00637554EA0000002609
    +B0939364547576N00637540EA0000002607
    +B0939394547570N00637526EA0000002603
    +B0939414547564N00637509EA0000002599
    +B0939434547561N00637491EA0000002598
    +B0939454547556N00637476EA0000002596
    +B0939474547550N00637461EA0000002592
    +B0939494547544N00637445EA0000002588
    +B0939514547539N00637428EA0000002584
    +B0939534547534N00637412EA0000002582
    +B0939554547528N00637396EA0000002578
    +B0939574547523N00637381EA0000002573
    +B0939594547519N00637362EA0000002568
    +B0940014547516N00637343EA0000002565
    +B0940034547512N00637327EA0000002562
    +B0940054547508N00637310EA0000002558
    +B0940074547505N00637294EA0000002555
    +B0940094547502N00637281EA0000002552
    +B0940114547496N00637266EA0000002547
    +B0940134547491N00637250EA0000002545
    +B0940154547484N00637234EA0000002542
    +B0940174547475N00637219EA0000002539
    +B0940194547467N00637203EA0000002537
    +B0940214547460N00637188EA0000002536
    +B0940234547454N00637174EA0000002533
    +B0940254547445N00637160EA0000002529
    +B0940274547436N00637145EA0000002526
    +B0940294547426N00637131EA0000002525
    +B0940314547417N00637118EA0000002522
    +B0940334547407N00637104EA0000002518
    +B0940354547397N00637088EA0000002516
    +B0940374547386N00637074EA0000002514
    +B0940394547377N00637061EA0000002512
    +B0940414547369N00637047EA0000002509
    +B0940434547359N00637034EA0000002504
    +B0940454547348N00637022EA0000002500
    +B0940474547337N00637011EA0000002495
    +B0940494547326N00637000EA0000002491
    +B0940514547314N00636990EA0000002488
    +B0940534547304N00636980EA0000002486
    +B0940554547294N00636971EA0000002485
    +B0940574547283N00636962EA0000002485
    +B0940594547275N00636953EA0000002487
    +B0941014547260N00636941EA0000002486
    +B0941034547250N00636932EA0000002486
    +B0941054547242N00636923EA0000002485
    +B0941074547235N00636913EA0000002483
    +B0941094547225N00636900EA0000002482
    +B0941114547215N00636889EA0000002483
    +B0941134547206N00636879EA0000002482
    +B0941154547197N00636868EA0000002483
    +B0941174547186N00636859EA0000002485
    +B0941204547178N00636849EA0000002485
    +B0941224547170N00636836EA0000002486
    +B0941244547162N00636824EA0000002488
    +B0941264547155N00636812EA0000002489
    +B0941284547147N00636799EA0000002490
    +B0941304547140N00636785EA0000002490
    +B0941324547133N00636770EA0000002491
    +B0941344547126N00636755EA0000002491
    +B0941364547117N00636743EA0000002491
    +B0941384547107N00636731EA0000002491
    +B0941404547099N00636719EA0000002489
    +B0941424547090N00636707EA0000002486
    +B0941444547080N00636692EA0000002482
    +B0941464547070N00636673EA0000002479
    +B0941484547062N00636657EA0000002477
    +B0941504547058N00636649EA0000002473
    +B0941524547045N00636623EA0000002467
    +B0941544547036N00636605EA0000002464
    +B0941564547026N00636590EA0000002460
    +B0941584547016N00636574EA0000002456
    +B0942004547008N00636555EA0000002452
    +B0942024547000N00636536EA0000002450
    +B0942044546992N00636518EA0000002446
    +B0942064546985N00636499EA0000002442
    +B0942084546977N00636479EA0000002439
    +B0942104546968N00636461EA0000002437
    +B0942124546959N00636444EA0000002436
    +B0942144546949N00636427EA0000002434
    +B0942164546939N00636410EA0000002433
    +B0942184546928N00636393EA0000002433
    +B0942204546918N00636377EA0000002433
    +B0942224546907N00636361EA0000002432
    +B0942244546897N00636346EA0000002433
    +B0942264546887N00636333EA0000002436
    +B0942284546879N00636321EA0000002440
    +B0942304546873N00636311EA0000002446
    +B0942324546865N00636305EA0000002450
    +B0942344546858N00636295EA0000002455
    +B0942364546853N00636281EA0000002461
    +B0942384546846N00636271EA0000002469
    +B0942404546838N00636264EA0000002475
    +B0942424546826N00636252EA0000002483
    +B0942444546816N00636242EA0000002487
    +B0942464546808N00636232EA0000002490
    +B0942484546799N00636219EA0000002492
    +B0942504546791N00636203EA0000002496
    +B0942524546784N00636188EA0000002501
    +B0942544546778N00636176EA0000002507
    +B0942564546772N00636164EA0000002510
    +B0942584546766N00636150EA0000002513
    +B0943014546759N00636136EA0000002517
    +B0943034546751N00636125EA0000002519
    +B0943054546743N00636111EA0000002520
    +B0943074546734N00636096EA0000002523
    +B0943094546726N00636088EA0000002526
    +B0943114546719N00636083EA0000002528
    +B0943134546713N00636075EA0000002527
    +B0943154546704N00636062EA0000002526
    +B0943174546696N00636049EA0000002526
    +B0943194546689N00636037EA0000002525
    +B0943214546683N00636024EA0000002523
    +B0943234546676N00636009EA0000002520
    +B0943254546668N00635993EA0000002517
    +B0943274546660N00635978EA0000002515
    +B0943294546654N00635965EA0000002513
    +B0943314546648N00635950EA0000002509
    +B0943334546642N00635935EA0000002507
    +B0943354546636N00635921EA0000002506
    +B0943374546631N00635906EA0000002502
    +B0943394546624N00635889EA0000002497
    +B0943414546618N00635873EA0000002495
    +B0943434546615N00635860EA0000002494
    +B0943454546610N00635848EA0000002492
    +B0943474546605N00635834EA0000002490
    +B0943494546599N00635820EA0000002490
    +B0943514546594N00635808EA0000002487
    +B0943534546587N00635792EA0000002484
    +B0943554546578N00635774EA0000002483
    +B0943574546570N00635757EA0000002483
    +B0943594546564N00635738EA0000002482
    +B0944014546556N00635718EA0000002483
    +B0944034546548N00635700EA0000002485
    +B0944054546542N00635687EA0000002490
    +B0944074546536N00635678EA0000002494
    +B0944094546530N00635667EA0000002496
    +B0944114546523N00635654EA0000002498
    +B0944134546517N00635641EA0000002500
    +B0944154546511N00635629EA0000002502
    +B0944174546503N00635615EA0000002503
    +B0944194546498N00635599EA0000002506
    +B0944214546492N00635588EA0000002509
    +B0944234546483N00635569EA0000002509
    +B0944254546477N00635554EA0000002510
    +B0944274546470N00635541EA0000002510
    +B0944294546464N00635527EA0000002509
    +B0944314546459N00635509EA0000002508
    +B0944334546453N00635491EA0000002508
    +B0944354546448N00635472EA0000002509
    +B0944374546444N00635455EA0000002511
    +B0944404546440N00635441EA0000002511
    +B0944424546435N00635427EA0000002510
    +B0944444546430N00635411EA0000002508
    +B0944464546422N00635392EA0000002506
    +B0944484546413N00635373EA0000002507
    +B0944504546406N00635357EA0000002505
    +B0944524546398N00635339EA0000002503
    +B0944544546391N00635320EA0000002500
    +B0944564546384N00635303EA0000002499
    +B0944584546378N00635287EA0000002496
    +B0945004546371N00635269EA0000002492
    +B0945024546363N00635251EA0000002490
    +B0945044546354N00635235EA0000002490
    +B0945064546347N00635222EA0000002491
    +B0945084546340N00635208EA0000002491
    +B0945104546332N00635192EA0000002493
    +B0945124546324N00635177EA0000002495
    +B0945144546317N00635164EA0000002496
    +B0945164546309N00635149EA0000002495
    +B0945184546301N00635134EA0000002495
    +B0945204546294N00635121EA0000002494
    +B0945224546287N00635108EA0000002493
    +B0945244546282N00635094EA0000002490
    +B0945264546275N00635080EA0000002488
    +B0945284546268N00635065EA0000002485
    +B0945304546259N00635048EA0000002482
    +B0945324546251N00635031EA0000002480
    +B0945344546244N00635015EA0000002478
    +B0945364546236N00634999EA0000002475
    +B0945384546227N00634982EA0000002472
    +B0945404546219N00634965EA0000002468
    +B0945424546210N00634949EA0000002464
    +B0945444546200N00634933EA0000002461
    +B0945464546191N00634917EA0000002459
    +B0945484546182N00634903EA0000002456
    +B0945504546173N00634888EA0000002453
    +B0945524546165N00634872EA0000002450
    +B0945544546156N00634856EA0000002447
    +B0945564546148N00634839EA0000002445
    +B0945584546141N00634823EA0000002442
    +B0946004546129N00634799EA0000002439
    +B0946024546121N00634783EA0000002437
    +B0946044546112N00634768EA0000002434
    +B0946064546103N00634754EA0000002433
    +B0946084546093N00634739EA0000002431
    +B0946104546083N00634726EA0000002430
    +B0946124546073N00634713EA0000002428
    +B0946144546064N00634701EA0000002428
    +B0946164546055N00634690EA0000002426
    +B0946194546045N00634678EA0000002423
    +B0946214546034N00634662EA0000002421
    +B0946234546025N00634647EA0000002421
    +B0946254546015N00634633EA0000002419
    +B0946274546005N00634617EA0000002418
    +B0946294545995N00634601EA0000002418
    +B0946314545985N00634585EA0000002420
    +B0946334545976N00634572EA0000002424
    +B0946354545969N00634561EA0000002432
    +B0946374545963N00634551EA0000002438
    +B0946394545957N00634538EA0000002445
    +B0946414545955N00634521EA0000002450
    +B0946434545960N00634505EA0000002456
    +B0946454545970N00634499EA0000002461
    +B0946474545980N00634510EA0000002466
    +B0946494545983N00634525EA0000002471
    +B0946514545974N00634533EA0000002477
    +B0946534545961N00634523EA0000002485
    +B0946554545954N00634510EA0000002492
    +B0946574545949N00634496EA0000002498
    +B0946594545946N00634480EA0000002503
    +B0947014545941N00634464EA0000002508
    +B0947034545937N00634451EA0000002515
    +B0947054545933N00634440EA0000002519
    +B0947074545927N00634428EA0000002522
    +B0947094545920N00634415EA0000002523
    +B0947114545914N00634403EA0000002523
    +B0947134545909N00634391EA0000002523
    +B0947154545903N00634375EA0000002520
    +B0947174545897N00634357EA0000002514
    +B0947194545889N00634338EA0000002506
    +B0947214545882N00634316EA0000002501
    +B0947234545874N00634296EA0000002498
    +B0947254545867N00634276EA0000002495
    +B0947274545860N00634256EA0000002491
    +B0947294545853N00634237EA0000002489
    +B0947314545845N00634219EA0000002487
    +B0947334545840N00634200EA0000002484
    +B0947354545835N00634180EA0000002482
    +B0947374545830N00634161EA0000002480
    +B0947394545825N00634142EA0000002479
    +B0947414545818N00634112EA0000002477
    +B0947434545812N00634089EA0000002478
    +B0947454545808N00634078EA0000002482
    +B0947474545804N00634073EA0000002489
    +B0947494545797N00634065EA0000002496
    +B0947514545790N00634052EA0000002503
    +B0947534545787N00634042EA0000002511
    +B0947554545784N00634033EA0000002517
    +B0947574545778N00634022EA0000002522
    +B0948004545769N00634013EA0000002530
    +B0948024545764N00634008EA0000002538
    +B0948044545759N00634000EA0000002543
    +B0948064545753N00633988EA0000002549
    +B0948084545748N00633975EA0000002557
    +B0948104545746N00633962EA0000002562
    +B0948124545744N00633949EA0000002566
    +B0948144545741N00633936EA0000002568
    +B0948164545738N00633923EA0000002568
    +B0948184545736N00633909EA0000002567
    +B0948204545734N00633894EA0000002563
    +B0948224545731N00633877EA0000002560
    +B0948244545728N00633859EA0000002557
    +B0948264545725N00633840EA0000002552
    +B0948284545721N00633819EA0000002550
    +B0948304545719N00633809EA0000002548
    +B0948324545714N00633782EA0000002545
    +B0948344545711N00633762EA0000002543
    +B0948364545708N00633742EA0000002542
    +B0948384545706N00633722EA0000002540
    +B0948404545703N00633701EA0000002538
    +B0948424545700N00633679EA0000002537
    +B0948444545697N00633658EA0000002537
    +B0948464545695N00633636EA0000002535
    +B0948484545692N00633614EA0000002533
    +B0948504545688N00633592EA0000002532
    +B0948524545682N00633570EA0000002532
    +B0948544545677N00633552EA0000002533
    +B0948564545671N00633544EA0000002538
    +B0948584545664N00633535EA0000002539
    +B0949004545655N00633523EA0000002541
    +B0949024545648N00633509EA0000002545
    +B0949044545641N00633498EA0000002550
    +B0949064545634N00633489EA0000002554
    +B0949084545626N00633482EA0000002558
    +B0949104545620N00633476EA0000002561
    +B0949124545613N00633470EA0000002562
    +B0949144545604N00633461EA0000002561
    +B0949164545596N00633448EA0000002560
    +B0949184545589N00633437EA0000002562
    +B0949204545582N00633429EA0000002562
    +B0949224545571N00633413EA0000002559
    +B0949244545562N00633399EA0000002558
    +B0949264545554N00633383EA0000002556
    +B0949284545549N00633363EA0000002555
    +B0949304545546N00633344EA0000002556
    +B0949324545543N00633323EA0000002559
    +B0949344545542N00633305EA0000002564
    +B0949364545538N00633295EA0000002570
    +B0949384545530N00633283EA0000002574
    +B0949414545522N00633268EA0000002579
    +B0949434545515N00633254EA0000002585
    +B0949454545509N00633242EA0000002590
    +B0949474545501N00633230EA0000002593
    +B0949494545493N00633217EA0000002596
    +B0949514545485N00633200EA0000002596
    +B0949534545479N00633184EA0000002598
    +B0949554545472N00633170EA0000002598
    +B0949574545464N00633157EA0000002597
    +B0949594545456N00633142EA0000002595
    +B0950014545447N00633125EA0000002594
    +B0950034545440N00633110EA0000002594
    +B0950054545433N00633095EA0000002591
    +B0950074545429N00633088EA0000002587
    +B0950094545416N00633063EA0000002583
    +B0950114545413N00633054EA0000002580
    +B0950134545403N00633029EA0000002576
    +B0950154545397N00633011EA0000002571
    +B0950174545390N00632993EA0000002567
    +B0950194545384N00632974EA0000002564
    +B0950214545380N00632956EA0000002563
    +B0950234545376N00632941EA0000002561
    +B0950254545371N00632925EA0000002558
    +B0950274545366N00632906EA0000002556
    +B0950294545362N00632890EA0000002553
    +B0950314545359N00632873EA0000002549
    +B0950334545355N00632854EA0000002544
    +B0950354545351N00632835EA0000002541
    +B0950374545347N00632818EA0000002536
    +B0950394545342N00632801EA0000002531
    +B0950414545337N00632783EA0000002525
    +B0950434545330N00632766EA0000002520
    +B0950454545324N00632749EA0000002515
    +B0950474545319N00632731EA0000002511
    +B0950494545314N00632714EA0000002507
    +B0950514545309N00632696EA0000002501
    +B0950534545304N00632677EA0000002497
    +B0950554545299N00632660EA0000002493
    +B0950574545294N00632643EA0000002488
    +B0950594545290N00632625EA0000002484
    +B0951014545285N00632608EA0000002481
    +B0951034545278N00632583EA0000002474
    +B0951054545272N00632566EA0000002469
    +B0951074545267N00632547EA0000002465
    +B0951094545263N00632529EA0000002461
    +B0951114545259N00632511EA0000002457
    +B0951134545255N00632493EA0000002453
    +B0951154545250N00632475EA0000002449
    +B0951174545245N00632457EA0000002445
    +B0951194545239N00632440EA0000002442
    +B0951224545233N00632424EA0000002439
    +B0951244545228N00632406EA0000002435
    +B0951264545222N00632384EA0000002429
    +B0951284545217N00632362EA0000002425
    +B0951304545212N00632341EA0000002422
    +B0951324545207N00632323EA0000002416
    +B0951344545201N00632302EA0000002411
    +B0951364545195N00632281EA0000002407
    +B0951384545191N00632262EA0000002405
    +B0951404545188N00632250EA0000002405
    +B0951424545186N00632237EA0000002402
    +B0951444545184N00632230EA0000002401
    +B0951464545178N00632208EA0000002402
    +B0951484545173N00632195EA0000002400
    +B0951504545167N00632179EA0000002397
    +B0951524545167N00632179EA0000002396
    +B0951544545156N00632149EA0000002394
    +B0951564545151N00632133EA0000002391
    +B0951584545145N00632117EA0000002390
    +B0952004545140N00632102EA0000002390
    +B0952024545137N00632086EA0000002389
    +B0952044545133N00632070EA0000002387
    +B0952064545126N00632054EA0000002387
    +B0952084545120N00632038EA0000002387
    +B0952104545115N00632021EA0000002387
    +B0952124545112N00632003EA0000002388
    +B0952144545107N00631985EA0000002389
    +B0952164545102N00631969EA0000002389
    +B0952184545099N00631951EA0000002390
    +B0952204545095N00631935EA0000002393
    +B0952224545088N00631921EA0000002396
    +B0952244545081N00631910EA0000002397
    +B0952264545073N00631897EA0000002398
    +B0952284545066N00631882EA0000002401
    +B0952304545060N00631868EA0000002403
    +B0952324545054N00631855EA0000002404
    +B0952344545047N00631839EA0000002405
    +B0952364545039N00631823EA0000002408
    +B0952384545030N00631807EA0000002410
    +B0952404545022N00631789EA0000002412
    +B0952424545015N00631769EA0000002415
    +B0952444545007N00631740EA0000002420
    +B0952464545000N00631723EA0000002422
    +B0952484544993N00631704EA0000002425
    +B0952504544988N00631684EA0000002428
    +B0952524544985N00631665EA0000002432
    +B0952544544980N00631645EA0000002435
    +B0952564544975N00631626EA0000002440
    +B0952584544972N00631609EA0000002447
    +B0953014544969N00631594EA0000002452
    +B0953034544965N00631578EA0000002457
    +B0953054544962N00631561EA0000002464
    +B0953074544961N00631548EA0000002470
    +B0953094544958N00631538EA0000002475
    +B0953114544953N00631530EA0000002478
    +B0953134544946N00631524EA0000002480
    +B0953154544939N00631518EA0000002481
    +B0953174544933N00631510EA0000002483
    +B0953194544927N00631503EA0000002485
    +B0953214544924N00631500EA0000002486
    +B0953234544916N00631486EA0000002486
    +B0953254544912N00631475EA0000002486
    +B0953274544908N00631464EA0000002485
    +B0953294544904N00631451EA0000002483
    +B0953314544899N00631438EA0000002480
    +B0953334544896N00631421EA0000002478
    +B0953354544894N00631405EA0000002478
    +B0953374544890N00631392EA0000002476
    +B0953394544886N00631376EA0000002474
    +B0953414544882N00631360EA0000002472
    +B0953434544878N00631345EA0000002470
    +B0953454544876N00631332EA0000002468
    +B0953474544872N00631317EA0000002463
    +B0953494544868N00631302EA0000002460
    +B0953514544864N00631287EA0000002456
    +B0953534544860N00631271EA0000002452
    +B0953554544856N00631255EA0000002448
    +B0953574544853N00631240EA0000002443
    +B0953594544850N00631225EA0000002439
    +B0954014544847N00631210EA0000002435
    +B0954034544845N00631196EA0000002432
    +B0954054544842N00631181EA0000002430
    +B0954074544841N00631166EA0000002429
    +B0954094544840N00631151EA0000002426
    +B0954114544838N00631134EA0000002424
    +B0954134544837N00631117EA0000002423
    +B0954154544837N00631102EA0000002425
    +B0954174544835N00631088EA0000002425
    +B0954194544834N00631073EA0000002424
    +B0954214544834N00631058EA0000002423
    +B0954234544834N00631038EA0000002418
    +B0954254544833N00631022EA0000002414
    +B0954274544832N00631006EA0000002413
    +B0954294544832N00630992EA0000002412
    +B0954314544831N00630980EA0000002413
    +B0954334544830N00630968EA0000002414
    +B0954354544828N00630956EA0000002416
    +B0954374544825N00630943EA0000002417
    +B0954394544822N00630929EA0000002418
    +B0954424544819N00630914EA0000002419
    +B0954444544818N00630898EA0000002419
    +B0954464544818N00630884EA0000002419
    +B0954484544817N00630870EA0000002417
    +B0954504544815N00630854EA0000002413
    +B0954524544812N00630837EA0000002409
    +B0954544544809N00630819EA0000002406
    +B0954564544807N00630803EA0000002402
    +B0954584544806N00630795EA0000002398
    +B0955004544803N00630768EA0000002395
    +B0955024544801N00630751EA0000002392
    +B0955044544800N00630737EA0000002388
    +B0955064544798N00630719EA0000002383
    +B0955084544798N00630699EA0000002380
    +B0955104544797N00630681EA0000002380
    +B0955124544796N00630666EA0000002382
    +B0955144544794N00630644EA0000002385
    +B0955174544791N00630630EA0000002388
    +B0955194544789N00630616EA0000002391
    +B0955214544787N00630604EA0000002393
    +B0955234544784N00630593EA0000002393
    +B0955254544781N00630579EA0000002392
    +B0955274544777N00630563EA0000002388
    +B0955294544772N00630548EA0000002385
    +B0955314544771N00630540EA0000002382
    +B0955334544772N00630516EA0000002378
    +B0955354544772N00630498EA0000002375
    +B0955374544772N00630481EA0000002371
    +B0955394544773N00630462EA0000002367
    +B0955414544772N00630443EA0000002365
    +B0955434544771N00630424EA0000002362
    +B0955454544771N00630405EA0000002360
    +B0955474544772N00630385EA0000002359
    +B0955494544773N00630370EA0000002361
    +B0955514544774N00630359EA0000002363
    +B0955534544774N00630346EA0000002361
    +B0955554544776N00630333EA0000002362
    +B0955574544781N00630323EA0000002362
    +B0955594544789N00630315EA0000002363
    +B0956014544795N00630306EA0000002363
    +B0956034544802N00630296EA0000002364
    +B0956054544808N00630285EA0000002365
    +B0956074544815N00630276EA0000002365
    +B0956094544825N00630266EA0000002367
    +B0956114544835N00630257EA0000002370
    +B0956134544844N00630248EA0000002375
    +B0956154544850N00630240EA0000002380
    +B0956174544858N00630230EA0000002385
    +B0956194544872N00630229EA0000002390
    +B0956214544884N00630239EA0000002397
    +B0956234544891N00630254EA0000002403
    +B0956254544889N00630275EA0000002414
    +B0956274544879N00630278EA0000002422
    +B0956294544872N00630273EA0000002431
    +B0956314544870N00630265EA0000002438
    +B0956334544870N00630252EA0000002443
    +B0956354544876N00630240EA0000002448
    +B0956374544887N00630233EA0000002454
    +B0956394544898N00630237EA0000002460
    +B0956424544908N00630250EA0000002465
    +B0956444544910N00630266EA0000002469
    +B0956464544904N00630273EA0000002475
    +B0956484544897N00630270EA0000002481
    +B0956504544892N00630264EA0000002488
    +B0956524544889N00630257EA0000002494
    +B0956544544884N00630247EA0000002498
    +B0956564544884N00630234EA0000002503
    +B0956584544892N00630225EA0000002507
    +B0957004544905N00630221EA0000002512
    +B0957024544916N00630226EA0000002517
    +B0957044544922N00630239EA0000002521
    +B0957064544919N00630253EA0000002526
    +B0957084544913N00630250EA0000002530
    +B0957104544908N00630241EA0000002536
    +B0957124544907N00630231EA0000002542
    +B0957144544910N00630218EA0000002547
    +B0957164544919N00630208EA0000002552
    +B0957184544930N00630209EA0000002556
    +B0957204544937N00630221EA0000002560
    +B0957224544939N00630236EA0000002563
    +B0957244544935N00630248EA0000002566
    +B0957264544927N00630252EA0000002570
    +B0957284544921N00630252EA0000002574
    +B0957304544915N00630248EA0000002578
    +B0957324544911N00630238EA0000002580
    +B0957344544911N00630223EA0000002582
    +B0957364544917N00630211EA0000002584
    +B0957384544929N00630204EA0000002584
    +B0957404544941N00630207EA0000002585
    +B0957424544949N00630219EA0000002585
    +B0957444544949N00630236EA0000002581
    +B0957464544944N00630246EA0000002583
    +B0957484544934N00630243EA0000002585
    +B0957504544927N00630236EA0000002587
    +B0957524544919N00630233EA0000002588
    +B0957544544911N00630230EA0000002589
    +B0957564544903N00630225EA0000002592
    +B0957584544896N00630224EA0000002596
    +B0958004544882N00630221EA0000002603
    +B0958024544873N00630218EA0000002609
    +B0958044544865N00630216EA0000002616
    +B0958064544858N00630216EA0000002622
    +B0958084544849N00630214EA0000002627
    +B0958104544840N00630209EA0000002631
    +B0958124544833N00630200EA0000002633
    +B0958144544826N00630188EA0000002633
    +B0958164544818N00630175EA0000002631
    +B0958194544813N00630165EA0000002630
    +B0958214544809N00630153EA0000002627
    +B0958234544804N00630142EA0000002625
    +B0958254544799N00630133EA0000002626
    +B0958274544794N00630123EA0000002627
    +B0958294544789N00630113EA0000002629
    +B0958314544784N00630107EA0000002629
    +B0958334544779N00630101EA0000002628
    +B0958354544774N00630092EA0000002625
    +B0958374544768N00630080EA0000002621
    +B0958394544762N00630067EA0000002617
    +B0958414544756N00630054EA0000002615
    +B0958434544751N00630041EA0000002612
    +B0958454544748N00630034EA0000002606
    +B0958474544742N00630010EA0000002600
    +B0958494544742N00630010EA0000002596
    +B0958514544734N00629981EA0000002593
    +B0958534544730N00629966EA0000002587
    +B0958554544724N00629950EA0000002582
    +B0958574544718N00629934EA0000002578
    +B0958594544713N00629919EA0000002576
    +B0959014544707N00629906EA0000002572
    +B0959034544702N00629892EA0000002569
    +B0959054544697N00629877EA0000002567
    +B0959074544691N00629863EA0000002565
    +B0959094544686N00629850EA0000002563
    +B0959114544680N00629837EA0000002561
    +B0959134544673N00629823EA0000002560
    +B0959154544667N00629811EA0000002558
    +B0959174544660N00629798EA0000002556
    +B0959194544653N00629785EA0000002554
    +B0959214544645N00629771EA0000002553
    +B0959234544639N00629761EA0000002556
    +B0959254544633N00629754EA0000002560
    +B0959274544626N00629747EA0000002566
    +B0959294544619N00629740EA0000002573
    +B0959314544613N00629731EA0000002576
    +B0959334544607N00629722EA0000002581
    +B0959354544604N00629715EA0000002585
    +B0959374544601N00629712EA0000002588
    +B0959394544596N00629703EA0000002587
    +B0959414544593N00629681EA0000002583
    +B0959434544591N00629668EA0000002579
    +B0959454544588N00629653EA0000002578
    +B0959474544584N00629638EA0000002578
    +B0959494544583N00629626EA0000002582
    +B0959514544584N00629614EA0000002589
    +B0959534544583N00629599EA0000002597
    +B0959564544582N00629579EA0000002603
    +B0959584544583N00629558EA0000002610
    +B1000004544582N00629538EA0000002616
    +B1000024544580N00629522EA0000002620
    +B1000044544578N00629504EA0000002624
    +B1000064544575N00629484EA0000002626
    +B1000084544568N00629468EA0000002629
    +B1000104544562N00629454EA0000002632
    +B1000124544556N00629438EA0000002633
    +B1000144544553N00629416EA0000002633
    +B1000164544549N00629400EA0000002635
    +B1000184544546N00629385EA0000002635
    +B1000204544542N00629366EA0000002636
    +B1000224544539N00629357EA0000002638
    +B1000244544539N00629357EA0000002639
    +B1000264544524N00629312EA0000002639
    +B1000284544519N00629291EA0000002639
    +B1000304544516N00629273EA0000002639
    +B1000324544513N00629256EA0000002638
    +B1000344544511N00629238EA0000002635
    +B1000364544507N00629219EA0000002632
    +B1000384544503N00629199EA0000002630
    +B1000404544500N00629180EA0000002629
    +B1000424544496N00629163EA0000002629
    +B1000444544493N00629147EA0000002628
    +B1000464544490N00629130EA0000002626
    +B1000484544486N00629114EA0000002625
    +B1000504544482N00629098EA0000002623
    +B1000524544478N00629082EA0000002621
    +B1000544544474N00629065EA0000002617
    +B1000564544470N00629049EA0000002613
    +B1000584544464N00629031EA0000002607
    +B1001004544456N00629015EA0000002601
    +B1001024544447N00628998EA0000002594
    +B1001044544440N00628978EA0000002587
    +B1001064544434N00628958EA0000002580
    +B1001084544430N00628938EA0000002575
    +B1001104544429N00628922EA0000002571
    +B1001124544426N00628904EA0000002566
    +B1001144544419N00628875EA0000002560
    +B1001164544415N00628858EA0000002556
    +B1001184544413N00628841EA0000002551
    +B1001204544409N00628821EA0000002544
    +B1001224544403N00628801EA0000002538
    +B1001244544398N00628780EA0000002533
    +B1001264544393N00628759EA0000002529
    +B1001284544390N00628740EA0000002527
    +B1001304544387N00628720EA0000002524
    +B1001334544385N00628699EA0000002521
    +B1001354544381N00628678EA0000002518
    +B1001374544377N00628658EA0000002515
    +B1001394544372N00628637EA0000002510
    +B1001414544369N00628616EA0000002507
    +B1001434544366N00628595EA0000002504
    +B1001454544363N00628573EA0000002500
    +B1001474544361N00628551EA0000002497
    +B1001494544359N00628529EA0000002495
    +B1001514544356N00628509EA0000002491
    +B1001534544355N00628487EA0000002487
    +B1001554544354N00628466EA0000002484
    +B1001574544352N00628445EA0000002481
    +B1001594544351N00628434EA0000002478
    +B1002014544347N00628402EA0000002474
    +B1002034544344N00628380EA0000002471
    +B1002054544340N00628358EA0000002468
    +B1002074544336N00628337EA0000002465
    +B1002094544331N00628316EA0000002461
    +B1002114544327N00628295EA0000002458
    +B1002134544322N00628274EA0000002454
    +B1002154544318N00628253EA0000002451
    +B1002174544314N00628232EA0000002447
    +B1002194544310N00628210EA0000002443
    +B1002214544306N00628189EA0000002439
    +B1002234544302N00628166EA0000002436
    +B1002254544299N00628144EA0000002433
    +B1002274544296N00628121EA0000002429
    +B1002294544293N00628099EA0000002425
    +B1002314544290N00628077EA0000002421
    +B1002334544286N00628055EA0000002417
    +B1002354544284N00628033EA0000002413
    +B1002374544280N00628010EA0000002410
    +B1002394544277N00627988EA0000002407
    +B1002414544275N00627965EA0000002403
    +B1002434544273N00627943EA0000002400
    +B1002454544271N00627920EA0000002397
    +B1002474544269N00627897EA0000002393
    +B1002494544266N00627874EA0000002390
    +B1002514544264N00627852EA0000002388
    +B1002534544261N00627829EA0000002385
    +B1002554544256N00627795EA0000002382
    +B1002574544253N00627773EA0000002380
    +B1002594544250N00627750EA0000002378
    +B1003014544247N00627728EA0000002375
    +B1003034544244N00627705EA0000002372
    +B1003054544242N00627682EA0000002370
    +B1003074544240N00627660EA0000002367
    +B1003094544236N00627636EA0000002364
    +B1003114544234N00627613EA0000002362
    +B1003144544232N00627591EA0000002360
    +B1003164544230N00627570EA0000002357
    +B1003184544229N00627547EA0000002354
    +B1003204544228N00627524EA0000002352
    +B1003224544226N00627502EA0000002350
    +B1003244544224N00627480EA0000002348
    +B1003264544223N00627457EA0000002345
    +B1003284544221N00627434EA0000002343
    +B1003304544218N00627412EA0000002341
    +B1003324544216N00627390EA0000002339
    +B1003344544213N00627367EA0000002337
    +B1003364544211N00627356EA0000002335
    +B1003384544206N00627321EA0000002333
    +B1003404544204N00627298EA0000002332
    +B1003424544200N00627276EA0000002329
    +B1003444544197N00627254EA0000002327
    +B1003464544194N00627231EA0000002325
    +B1003484544191N00627208EA0000002323
    +B1003504544189N00627185EA0000002320
    +B1003524544186N00627162EA0000002318
    +B1003544544183N00627139EA0000002316
    +B1003564544180N00627116EA0000002313
    +B1003584544178N00627093EA0000002311
    +B1004004544175N00627070EA0000002308
    +B1004024544172N00627048EA0000002305
    +B1004044544170N00627025EA0000002302
    +B1004064544167N00627002EA0000002300
    +B1004084544164N00626979EA0000002297
    +B1004104544161N00626956EA0000002294
    +B1004124544157N00626934EA0000002292
    +B1004144544154N00626912EA0000002289
    +B1004164544150N00626889EA0000002287
    +B1004184544145N00626868EA0000002284
    +B1004204544141N00626846EA0000002281
    +B1004224544136N00626824EA0000002279
    +B1004244544132N00626802EA0000002276
    +B1004264544128N00626780EA0000002273
    +B1004284544123N00626758EA0000002270
    +B1004304544120N00626735EA0000002267
    +B1004324544116N00626712EA0000002264
    +B1004344544114N00626688EA0000002261
    +B1004364544110N00626652EA0000002256
    +B1004384544108N00626628EA0000002254
    +B1004404544106N00626604EA0000002252
    +B1004424544104N00626580EA0000002249
    +B1004444544102N00626557EA0000002246
    +B1004464544100N00626533EA0000002244
    +B1004484544098N00626509EA0000002241
    +B1004504544095N00626485EA0000002238
    +B1004524544093N00626461EA0000002235
    +B1004554544092N00626437EA0000002233
    +B1004574544089N00626413EA0000002230
    +B1004594544088N00626390EA0000002226
    +B1005014544086N00626366EA0000002223
    +B1005034544083N00626342EA0000002220
    +B1005054544081N00626319EA0000002217
    +B1005074544079N00626295EA0000002214
    +B1005094544076N00626272EA0000002211
    +B1005114544074N00626248EA0000002208
    +B1005134544073N00626236EA0000002204
    +B1005154544070N00626201EA0000002201
    +B1005174544069N00626177EA0000002198
    +B1005194544067N00626154EA0000002195
    +B1005214544065N00626130EA0000002191
    +B1005234544063N00626107EA0000002188
    +B1005254544061N00626083EA0000002185
    +B1005274544059N00626060EA0000002182
    +B1005294544058N00626037EA0000002179
    +B1005314544056N00626013EA0000002176
    +B1005334544054N00625990EA0000002173
    +B1005354544053N00625966EA0000002170
    +B1005374544051N00625943EA0000002167
    +B1005394544049N00625920EA0000002164
    +B1005414544048N00625896EA0000002161
    +B1005434544047N00625873EA0000002158
    +B1005454544045N00625850EA0000002156
    +B1005474544043N00625827EA0000002153
    +B1005494544041N00625805EA0000002150
    +B1005514544039N00625783EA0000002146
    +B1005534544037N00625760EA0000002143
    +B1005554544036N00625738EA0000002140
    +B1005574544034N00625716EA0000002137
    +B1005594544032N00625694EA0000002133
    +B1006014544031N00625672EA0000002130
    +B1006034544029N00625650EA0000002127
    +B1006054544027N00625628EA0000002124
    +B1006074544025N00625606EA0000002120
    +B1006094544024N00625584EA0000002117
    +B1006114544021N00625562EA0000002114
    +B1006134544020N00625540EA0000002111
    +B1006154544018N00625517EA0000002108
    +B1006174544016N00625484EA0000002104
    +B1006194544014N00625462EA0000002101
    +B1006214544012N00625440EA0000002098
    +B1006234544010N00625417EA0000002095
    +B1006254544009N00625394EA0000002092
    +B1006274544008N00625372EA0000002089
    +B1006294544006N00625350EA0000002086
    +B1006314544005N00625327EA0000002083
    +B1006334544003N00625305EA0000002081
    +B1006364544002N00625282EA0000002078
    +B1006384544000N00625260EA0000002075
    +B1006404543999N00625237EA0000002072
    +B1006424543997N00625215EA0000002069
    +B1006444543996N00625193EA0000002066
    +B1006464543995N00625171EA0000002063
    +B1006484543994N00625148EA0000002060
    +B1006504543993N00625126EA0000002057
    +B1006524543992N00625104EA0000002054
    +B1006544543992N00625081EA0000002051
    +B1006564543991N00625059EA0000002049
    +B1006584543990N00625037EA0000002046
    +B1007004543989N00625014EA0000002043
    +B1007024543988N00624992EA0000002041
    +B1007044543987N00624970EA0000002038
    +B1007064543986N00624959EA0000002036
    +B1007084543984N00624927EA0000002034
    +B1007104543983N00624906EA0000002031
    +B1007124543981N00624885EA0000002027
    +B1007144543979N00624863EA0000002025
    +B1007164543976N00624842EA0000002023
    +B1007184543972N00624822EA0000002020
    +B1007204543969N00624801EA0000002016
    +B1007224543965N00624779EA0000002013
    +B1007244543961N00624759EA0000002011
    +B1007264543957N00624738EA0000002007
    +B1007284543954N00624717EA0000002004
    +B1007304543949N00624695EA0000002001
    +B1007324543945N00624675EA0000001998
    +B1007344543941N00624654EA0000001995
    +B1007364543937N00624633EA0000001992
    +B1007384543934N00624612EA0000001989
    +B1007404543931N00624590EA0000001986
    +B1007424543928N00624570EA0000001983
    +B1007444543926N00624550EA0000001980
    +B1007464543924N00624529EA0000001977
    +B1007484543922N00624509EA0000001974
    +B1007504543920N00624489EA0000001971
    +B1007524543918N00624468EA0000001968
    +B1007544543916N00624448EA0000001966
    +B1007564543914N00624428EA0000001962
    +B1007584543910N00624397EA0000001958
    +B1008004543908N00624377EA0000001955
    +B1008024543905N00624357EA0000001952
    +B1008044543902N00624337EA0000001949
    +B1008064543900N00624317EA0000001946
    +B1008084543897N00624296EA0000001943
    +B1008104543894N00624276EA0000001940
    +B1008124543891N00624256EA0000001937
    +B1008154543888N00624235EA0000001933
    +B1008174543885N00624214EA0000001930
    +B1008194543882N00624194EA0000001928
    +B1008214543879N00624173EA0000001925
    +B1008234543877N00624152EA0000001922
    +B1008254543874N00624132EA0000001920
    +B1008274543872N00624111EA0000001917
    +B1008294543869N00624091EA0000001914
    +B1008314543867N00624070EA0000001912
    +B1008334543865N00624049EA0000001910
    +B1008354543862N00624028EA0000001908
    +B1008374543859N00624008EA0000001905
    +B1008394543857N00623987EA0000001903
    +B1008414543854N00623966EA0000001900
    +B1008434543851N00623945EA0000001897
    +B1008454543848N00623924EA0000001894
    +B1008474543845N00623903EA0000001891
    +B1008494543842N00623882EA0000001888
    +B1008514543839N00623862EA0000001885
    +B1008534543836N00623841EA0000001883
    +B1008554543833N00623820EA0000001880
    +B1008574543829N00623799EA0000001877
    +B1008594543826N00623779EA0000001874
    +B1009014543822N00623758EA0000001871
    +B1009034543819N00623737EA0000001869
    +B1009054543815N00623717EA0000001867
    +B1009074543810N00623697EA0000001864
    +B1009094543805N00623677EA0000001861
    +B1009114543800N00623656EA0000001858
    +B1009134543794N00623637EA0000001856
    +B1009154543789N00623617EA0000001853
    +B1009174543783N00623597EA0000001850
    +B1009194543777N00623577EA0000001847
    +B1009214543771N00623557EA0000001844
    +B1009234543765N00623537EA0000001842
    +B1009254543759N00623518EA0000001840
    +B1009274543753N00623498EA0000001836
    +B1009294543748N00623478EA0000001833
    +B1009314543743N00623458EA0000001830
    +B1009334543737N00623438EA0000001828
    +B1009354543729N00623409EA0000001824
    +B1009374543724N00623389EA0000001821
    +B1009394543719N00623370EA0000001818
    +B1009414543712N00623351EA0000001815
    +B1009434543706N00623332EA0000001812
    +B1009454543698N00623313EA0000001810
    +B1009474543691N00623294EA0000001807
    +B1009494543683N00623276EA0000001805
    +B1009514543676N00623258EA0000001802
    +B1009534543668N00623239EA0000001800
    +B1009564543661N00623221EA0000001797
    +B1009584543653N00623203EA0000001794
    +B1010004543646N00623185EA0000001792
    +B1010024543638N00623167EA0000001789
    +B1010044543630N00623150EA0000001786
    +B1010064543623N00623131EA0000001783
    +B1010084543616N00623113EA0000001781
    +B1010104543608N00623094EA0000001778
    +B1010124543601N00623076EA0000001776
    +B1010144543594N00623058EA0000001773
    +B1010164543586N00623040EA0000001770
    +B1010184543579N00623021EA0000001767
    +B1010204543576N00623012EA0000001765
    +B1010224543566N00622984EA0000001762
    +B1010244543560N00622965EA0000001759
    +B1010264543554N00622946EA0000001756
    +B1010284543547N00622927EA0000001753
    +B1010304543540N00622909EA0000001751
    +B1010324543533N00622891EA0000001748
    +B1010344543526N00622873EA0000001745
    +B1010364543518N00622855EA0000001743
    +B1010384543511N00622837EA0000001740
    +B1010404543504N00622820EA0000001737
    +B1010424543497N00622802EA0000001734
    +B1010444543489N00622784EA0000001732
    +B1010464543481N00622766EA0000001729
    +B1010484543473N00622749EA0000001727
    +B1010504543465N00622731EA0000001724
    +B1010524543458N00622712EA0000001721
    +B1010544543450N00622694EA0000001718
    +B1010564543442N00622675EA0000001715
    +B1010584543436N00622656EA0000001712
    +B1011004543428N00622638EA0000001709
    +B1011024543421N00622619EA0000001706
    +B1011044543414N00622601EA0000001703
    +B1011064543407N00622582EA0000001700
    +B1011084543400N00622563EA0000001696
    +B1011104543393N00622544EA0000001693
    +B1011124543383N00622516EA0000001688
    +B1011144543377N00622497EA0000001685
    +B1011164543371N00622478EA0000001682
    +B1011184543365N00622459EA0000001679
    +B1011204543359N00622440EA0000001676
    +B1011224543354N00622421EA0000001672
    +B1011244543348N00622402EA0000001669
    +B1011264543342N00622382EA0000001666
    +B1011284543336N00622364EA0000001663
    +B1011304543330N00622344EA0000001660
    +B1011324543325N00622325EA0000001657
    +B1011344543319N00622307EA0000001654
    +B1011374543313N00622288EA0000001651
    +B1011394543307N00622269EA0000001648
    +B1011414543301N00622250EA0000001645
    +B1011434543295N00622231EA0000001642
    +B1011454543289N00622213EA0000001639
    +B1011474543282N00622194EA0000001636
    +B1011494543277N00622176EA0000001633
    +B1011514543271N00622158EA0000001630
    +B1011534543264N00622141EA0000001626
    +B1011554543258N00622123EA0000001623
    +B1011574543251N00622106EA0000001620
    +B1011594543244N00622089EA0000001617
    +B1012014543240N00622081EA0000001615
    +B1012034543231N00622055EA0000001613
    +B1012054543227N00622038EA0000001610
    +B1012074543223N00622020EA0000001607
    +B1012094543220N00622001EA0000001604
    +B1012114543219N00621981EA0000001601
    +B1012134543218N00621961EA0000001599
    +B1012154543216N00621938EA0000001597
    +B1012174543213N00621919EA0000001596
    +B1012194543208N00621907EA0000001594
    +B1012214543203N00621893EA0000001590
    +B1012234543200N00621875EA0000001586
    +B1012254543196N00621860EA0000001582
    +B1012274543190N00621846EA0000001579
    +B1012294543186N00621831EA0000001576
    +B1012314543182N00621816EA0000001573
    +B1012334543176N00621800EA0000001571
    +B1012354543171N00621789EA0000001569
    +B1012374543164N00621776EA0000001566
    +B1012394543159N00621761EA0000001564
    +B1012414543153N00621749EA0000001563
    +B1012434543146N00621735EA0000001559
    +B1012454543138N00621720EA0000001557
    +B1012474543129N00621707EA0000001556
    +B1012494543121N00621694EA0000001555
    +B1012514543114N00621681EA0000001555
    +B1012534543105N00621665EA0000001553
    +B1012554543100N00621648EA0000001551
    +B1012574543098N00621630EA0000001550
    +B1012594543098N00621614EA0000001547
    +B1013014543096N00621601EA0000001543
    +B1013034543095N00621585EA0000001539
    +B1013054543096N00621567EA0000001536
    +B1013074543098N00621549EA0000001534
    +B1013094543097N00621536EA0000001535
    +B1013114543097N00621521EA0000001534
    +B1013134543100N00621504EA0000001534
    +B1013154543107N00621490EA0000001535
    +B1013184543114N00621476EA0000001536
    +B1013204543120N00621461EA0000001536
    +B1013224543125N00621445EA0000001537
    +B1013244543128N00621431EA0000001538
    +B1013264543130N00621418EA0000001539
    +B1013284543130N00621405EA0000001539
    +B1013304543128N00621392EA0000001539
    +B1013324543124N00621380EA0000001540
    +B1013344543122N00621369EA0000001540
    +B1013364543120N00621357EA0000001539
    +B1013384543118N00621344EA0000001539
    +B1013404543117N00621332EA0000001538
    +B1013424543113N00621320EA0000001536
    +B1013444543110N00621307EA0000001535
    +B1013464543110N00621295EA0000001534
    +B1013484543111N00621282EA0000001532
    +B1013504543111N00621267EA0000001529
    +B1013524543111N00621251EA0000001527
    +B1013544543110N00621236EA0000001524
    +B1013564543106N00621222EA0000001521
    +B1013584543099N00621209EA0000001519
    +B1014004543092N00621197EA0000001518
    +B1014024543085N00621185EA0000001517
    +B1014044543077N00621175EA0000001517
    +B1014064543068N00621165EA0000001518
    +B1014084543061N00621157EA0000001519
    +B1014104543054N00621150EA0000001518
    +B1014124543046N00621141EA0000001515
    +B1014144543038N00621130EA0000001514
    +B1014164543030N00621119EA0000001513
    +B1014184543021N00621110EA0000001512
    +B1014204543013N00621099EA0000001511
    +B1014224543004N00621084EA0000001511
    +B1014244542997N00621069EA0000001512
    +B1014264542990N00621058EA0000001515
    +B1014284542984N00621051EA0000001518
    +B1014304542979N00621044EA0000001522
    +B1014324542971N00621033EA0000001525
    +B1014344542964N00621029EA0000001526
    +B1014364542955N00621030EA0000001527
    +B1014384542947N00621037EA0000001527
    +B1014404542946N00621052EA0000001526
    +B1014424542954N00621062EA0000001525
    +B1014444542968N00621061EA0000001524
    +B1014464542977N00621051EA0000001525
    +B1014484542981N00621038EA0000001526
    +B1014504542981N00621024EA0000001526
    +B1014524542979N00621011EA0000001529
    +B1014544542975N00621001EA0000001531
    +B1014564542971N00620993EA0000001531
    +B1014594542965N00620983EA0000001530
    +B1015014542959N00620973EA0000001528
    +B1015034542951N00620964EA0000001525
    +B1015054542943N00620954EA0000001522
    +B1015074542935N00620944EA0000001520
    +B1015094542927N00620935EA0000001518
    +B1015114542917N00620927EA0000001517
    +B1015134542908N00620921EA0000001516
    +B1015154542899N00620915EA0000001515
    +B1015174542888N00620910EA0000001514
    +B1015194542878N00620904EA0000001515
    +B1015214542868N00620898EA0000001516
    +B1015234542858N00620893EA0000001515
    +B1015254542849N00620890EA0000001513
    +B1015274542838N00620886EA0000001512
    +B1015294542827N00620882EA0000001513
    +B1015314542822N00620881EA0000001514
    +B1015334542816N00620880EA0000001514
    +B1015354542807N00620877EA0000001514
    +B1015374542800N00620872EA0000001516
    +B1015394542795N00620870EA0000001520
    +B1015414542790N00620875EA0000001523
    +B1015434542789N00620884EA0000001525
    +B1015454542792N00620895EA0000001526
    +B1015474542800N00620901EA0000001528
    +B1015494542811N00620897EA0000001529
    +B1015514542821N00620886EA0000001529
    +B1015534542824N00620869EA0000001530
    +B1015554542820N00620852EA0000001530
    +B1015574542813N00620843EA0000001531
    +B1015594542806N00620838EA0000001533
    +B1016014542800N00620836EA0000001536
    +B1016034542793N00620836EA0000001537
    +B1016054542784N00620835EA0000001537
    +B1016074542774N00620832EA0000001537
    +B1016094542767N00620829EA0000001536
    +B1016114542758N00620827EA0000001534
    +B1016134542741N00620825EA0000001531
    +B1016154542731N00620823EA0000001529
    +B1016174542720N00620821EA0000001526
    +B1016194542709N00620817EA0000001524
    +B1016214542699N00620812EA0000001523
    +B1016234542689N00620805EA0000001522
    +B1016254542681N00620798EA0000001523
    +B1016274542677N00620795EA0000001526
    +B1016294542670N00620795EA0000001527
    +B1016314542665N00620801EA0000001529
    +B1016334542667N00620812EA0000001531
    +B1016354542678N00620815EA0000001533
    +B1016384542689N00620810EA0000001536
    +B1016404542693N00620794EA0000001539
    +B1016424542693N00620775EA0000001544
    +B1016444542689N00620760EA0000001548
    +B1016464542682N00620754EA0000001552
    +B1016484542676N00620760EA0000001556
    +B1016504542677N00620771EA0000001559
    +B1016524542685N00620775EA0000001563
    +B1016544542696N00620766EA0000001567
    +B1016564542703N00620750EA0000001572
    +B1016584542701N00620733EA0000001576
    +B1017004542696N00620720EA0000001580
    +B1017024542689N00620713EA0000001584
    +B1017044542684N00620720EA0000001588
    +B1017064542681N00620737EA0000001596
    +B1017084542686N00620744EA0000001598
    +B1017104542694N00620750EA0000001600
    +B1017124542705N00620748EA0000001602
    +B1017144542717N00620738EA0000001606
    +B1017164542724N00620722EA0000001609
    +B1017184542718N00620704EA0000001611
    +B1017204542708N00620692EA0000001614
    +B1017224542700N00620689EA0000001620
    +B1017244542692N00620685EA0000001623
    +B1017264542683N00620682EA0000001627
    +B1017284542676N00620686EA0000001632
    +B1017304542674N00620694EA0000001636
    +B1017324542673N00620704EA0000001637
    +B1017344542674N00620717EA0000001639
    +B1017364542684N00620725EA0000001640
    +B1017384542696N00620723EA0000001641
    +B1017404542703N00620710EA0000001642
    +B1017424542703N00620691EA0000001644
    +B1017444542698N00620679EA0000001646
    +B1017464542691N00620671EA0000001649
    +B1017484542684N00620663EA0000001652
    +B1017504542676N00620657EA0000001656
    +B1017524542669N00620653EA0000001661
    +B1017544542661N00620657EA0000001666
    +B1017564542658N00620667EA0000001668
    +B1017584542661N00620679EA0000001672
    +B1018004542669N00620684EA0000001676
    +B1018024542679N00620683EA0000001677
    +B1018044542689N00620677EA0000001678
    +B1018064542696N00620659EA0000001679
    +B1018084542692N00620641EA0000001680
    +B1018104542684N00620631EA0000001685
    +B1018124542677N00620626EA0000001688
    +B1018144542671N00620618EA0000001690
    +B1018164542663N00620615EA0000001691
    +B1018194542656N00620620EA0000001691
    +B1018214542653N00620631EA0000001691
    +B1018234542656N00620643EA0000001691
    +B1018254542665N00620644EA0000001692
    +B1018274542675N00620633EA0000001694
    +B1018294542678N00620618EA0000001698
    +B1018314542675N00620605EA0000001703
    +B1018334542668N00620597EA0000001706
    +B1018354542661N00620593EA0000001709
    +B1018374542653N00620597EA0000001711
    +B1018394542648N00620607EA0000001712
    +B1018414542647N00620618EA0000001713
    +B1018434542652N00620630EA0000001714
    +B1018454542662N00620635EA0000001714
    +B1018474542672N00620629EA0000001715
    +B1018494542678N00620615EA0000001716
    +B1018514542675N00620601EA0000001717
    +B1018534542668N00620591EA0000001719
    +B1018554542662N00620583EA0000001721
    +B1018574542656N00620575EA0000001723
    +B1018594542648N00620567EA0000001725
    +B1019014542640N00620564EA0000001729
    +B1019034542634N00620570EA0000001733
    +B1019054542629N00620582EA0000001737
    +B1019074542629N00620597EA0000001739
    +B1019094542635N00620606EA0000001741
    +B1019114542644N00620609EA0000001741
    +B1019134542653N00620606EA0000001740
    +B1019154542659N00620592EA0000001739
    +B1019174542658N00620575EA0000001738
    +B1019194542651N00620561EA0000001737
    +B1019214542642N00620551EA0000001737
    +B1019234542635N00620543EA0000001739
    +B1019254542629N00620537EA0000001742
    +B1019274542623N00620531EA0000001745
    +B1019294542619N00620525EA0000001748
    +B1019314542608N00620519EA0000001750
    +B1019334542599N00620523EA0000001749
    +B1019354542595N00620532EA0000001748
    +B1019374542596N00620544EA0000001746
    +B1019394542605N00620553EA0000001744
    +B1019414542616N00620553EA0000001742
    +B1019434542628N00620549EA0000001739
    +B1019454542640N00620549EA0000001736
    +B1019474542651N00620552EA0000001734
    +B1019494542662N00620557EA0000001732
    +B1019514542673N00620564EA0000001732
    +B1019534542680N00620570EA0000001733
    +B1019564542684N00620576EA0000001735
    +B1019584542689N00620583EA0000001736
    +B1020004542696N00620589EA0000001739
    +B1020024542700N00620598EA0000001743
    +B1020044542699N00620608EA0000001746
    +B1020064542693N00620616EA0000001749
    +B1020084542684N00620610EA0000001751
    +B1020104542678N00620594EA0000001753
    +B1020124542681N00620588EA0000001755
    +B1020144542688N00620595EA0000001757
    +B1020164542688N00620606EA0000001760
    +B1020184542681N00620611EA0000001763
    +B1020204542676N00620610EA0000001767
    +B1020224542667N00620593EA0000001769
    +B1020244542670N00620577EA0000001772
    +B1020264542679N00620569EA0000001774
    +B1020284542688N00620572EA0000001777
    +B1020304542693N00620580EA0000001781
    +B1020324542696N00620589EA0000001785
    +B1020344542693N00620601EA0000001788
    +B1020364542684N00620606EA0000001791
    +B1020384542673N00620598EA0000001795
    +B1020404542667N00620584EA0000001798
    +B1020424542670N00620572EA0000001801
    +B1020444542679N00620562EA0000001804
    +B1020464542690N00620562EA0000001805
    +B1020484542699N00620567EA0000001807
    +B1020504542707N00620572EA0000001811
    +B1020524542714N00620573EA0000001816
    +B1020544542720N00620575EA0000001820
    +B1020564542724N00620582EA0000001824
    +B1020584542723N00620592EA0000001827
    +B1021004542714N00620593EA0000001830
    +B1021024542704N00620583EA0000001832
    +B1021044542700N00620568EA0000001835
    +B1021064542706N00620558EA0000001837
    +B1021084542717N00620562EA0000001840
    +B1021104542722N00620569EA0000001842
    +B1021124542709N00620567EA0000001847
    +B1021144542700N00620557EA0000001850
    +B1021164542696N00620543EA0000001852
    +B1021184542701N00620531EA0000001855
    +B1021204542714N00620531EA0000001856
    +B1021224542725N00620538EA0000001859
    +B1021244542733N00620546EA0000001863
    +B1021264542738N00620550EA0000001869
    +B1021284542742N00620557EA0000001873
    +B1021304542746N00620568EA0000001878
    +B1021324542747N00620578EA0000001883
    +B1021344542741N00620585EA0000001888
    +B1021374542731N00620583EA0000001893
    +B1021394542725N00620571EA0000001898
    +B1021414542726N00620555EA0000001903
    +B1021434542736N00620549EA0000001909
    +B1021454542746N00620550EA0000001915
    +B1021474542754N00620556EA0000001921
    +B1021494542756N00620567EA0000001925
    +B1021514542750N00620579EA0000001927
    +B1021534542739N00620578EA0000001929
    +B1021554542731N00620564EA0000001932
    +B1021574542730N00620555EA0000001934
    +B1021594542737N00620535EA0000001937
    +B1022014542747N00620535EA0000001940
    +B1022034542753N00620543EA0000001942
    +B1022054542754N00620554EA0000001943
    +B1022074542748N00620565EA0000001943
    +B1022094542742N00620573EA0000001944
    +B1022114542737N00620577EA0000001946
    +B1022134542729N00620581EA0000001946
    +B1022154542720N00620577EA0000001947
    +B1022174542714N00620562EA0000001948
    +B1022194542716N00620545EA0000001949
    +B1022214542725N00620544EA0000001952
    +B1022234542735N00620552EA0000001954
    +B1022254542735N00620560EA0000001958
    +B1022274542728N00620566EA0000001961
    +B1022294542718N00620566EA0000001962
    +B1022314542707N00620557EA0000001964
    +B1022334542702N00620542EA0000001967
    +B1022354542707N00620535EA0000001969
    +B1022374542716N00620539EA0000001972
    +B1022394542722N00620549EA0000001976
    +B1022414542721N00620558EA0000001980
    +B1022434542716N00620569EA0000001982
    +B1022454542708N00620576EA0000001983
    +B1022474542697N00620573EA0000001985
    +B1022494542691N00620561EA0000001988
    +B1022514542690N00620547EA0000001991
    +B1022534542703N00620535EA0000001995
    +B1022554542713N00620539EA0000001997
    +B1022574542716N00620551EA0000002001
    +B1022594542713N00620564EA0000002004
    +B1023014542706N00620574EA0000002006
    +B1023034542695N00620576EA0000002008
    +B1023054542686N00620569EA0000002011
    +B1023074542679N00620556EA0000002014
    +B1023094542679N00620541EA0000002017
    +B1023114542683N00620530EA0000002021
    +B1023134542691N00620523EA0000002025
    +B1023164542701N00620521EA0000002029
    +B1023184542708N00620529EA0000002032
    +B1023204542709N00620544EA0000002035
    +B1023224542703N00620556EA0000002039
    +B1023244542693N00620558EA0000002042
    +B1023264542684N00620548EA0000002044
    +B1023284542682N00620532EA0000002046
    +B1023304542689N00620525EA0000002049
    +B1023324542698N00620530EA0000002052
    +B1023344542701N00620536EA0000002055
    +B1023364542699N00620557EA0000002057
    +B1023384542689N00620565EA0000002060
    +B1023404542679N00620562EA0000002062
    +B1023424542673N00620551EA0000002063
    +B1023444542673N00620536EA0000002064
    +B1023464542682N00620529EA0000002065
    +B1023484542692N00620535EA0000002066
    +B1023504542696N00620548EA0000002067
    +B1023524542691N00620559EA0000002069
    +B1023544542682N00620565EA0000002070
    +B1023564542671N00620563EA0000002070
    +B1023584542662N00620554EA0000002070
    +B1024004542653N00620547EA0000002071
    +B1024024542647N00620546EA0000002074
    +B1024044542640N00620551EA0000002078
    +B1024064542632N00620558EA0000002082
    +B1024084542629N00620569EA0000002087
    +B1024104542629N00620584EA0000002090
    +B1024124542634N00620596EA0000002094
    +B1024144542642N00620599EA0000002096
    +B1024164542649N00620591EA0000002098
    +B1024184542648N00620580EA0000002100
    +B1024204542643N00620568EA0000002101
    +B1024224542636N00620560EA0000002104
    +B1024244542629N00620554EA0000002107
    +B1024264542620N00620549EA0000002112
    +B1024284542607N00620543EA0000002119
    +B1024304542599N00620536EA0000002122
    +B1024324542590N00620529EA0000002124
    +B1024344542580N00620522EA0000002124
    +B1024364542568N00620513EA0000002123
    +B1024384542557N00620503EA0000002120
    +B1024404542546N00620494EA0000002117
    +B1024424542536N00620486EA0000002114
    +B1024444542525N00620476EA0000002110
    +B1024464542514N00620466EA0000002106
    +B1024484542504N00620457EA0000002103
    +B1024504542494N00620447EA0000002099
    +B1024534542483N00620439EA0000002095
    +B1024554542471N00620430EA0000002091
    +B1024574542460N00620421EA0000002087
    +B1024594542449N00620410EA0000002084
    +B1025014542440N00620401EA0000002082
    +B1025034542430N00620391EA0000002080
    +B1025054542419N00620380EA0000002077
    +B1025074542408N00620372EA0000002076
    +B1025094542396N00620366EA0000002075
    +B1025114542391N00620363EA0000002076
    +B1025134542377N00620351EA0000002077
    +B1025154542366N00620340EA0000002078
    +B1025174542355N00620329EA0000002080
    +B1025194542345N00620319EA0000002081
    +B1025214542335N00620308EA0000002081
    +B1025234542323N00620297EA0000002082
    +B1025254542312N00620289EA0000002083
    +B1025274542302N00620281EA0000002083
    +B1025294542292N00620274EA0000002081
    +B1025314542282N00620265EA0000002079
    +B1025334542272N00620254EA0000002079
    +B1025354542263N00620245EA0000002079
    +B1025374542254N00620237EA0000002079
    +B1025394542246N00620228EA0000002079
    +B1025414542237N00620218EA0000002080
    +B1025434542227N00620209EA0000002083
    +B1025454542219N00620200EA0000002084
    +B1025474542211N00620191EA0000002085
    +B1025494542201N00620181EA0000002088
    +B1025514542192N00620174EA0000002092
    +B1025534542184N00620171EA0000002097
    +B1025554542178N00620168EA0000002100
    +B1025574542171N00620164EA0000002102
    +B1025594542164N00620157EA0000002102
    +B1026014542161N00620148EA0000002102
    +B1026034542164N00620133EA0000002103
    +B1026054542168N00620119EA0000002105
    +B1026074542174N00620110EA0000002105
    +B1026094542194N00620107EA0000002105
    +B1026114542204N00620121EA0000002107
    +B1026134542202N00620135EA0000002110
    +B1026154542189N00620144EA0000002114
    +B1026174542180N00620140EA0000002120
    +B1026194542175N00620129EA0000002125
    +B1026214542175N00620117EA0000002128
    +B1026234542182N00620106EA0000002129
    +B1026254542195N00620103EA0000002130
    +B1026274542206N00620114EA0000002131
    +B1026294542204N00620128EA0000002132
    +B1026324542195N00620133EA0000002134
    +B1026344542186N00620130EA0000002136
    +B1026364542179N00620126EA0000002140
    +B1026384542173N00620122EA0000002141
    +B1026404542165N00620116EA0000002143
    +B1026424542158N00620109EA0000002145
    +B1026444542152N00620104EA0000002147
    +B1026464542145N00620100EA0000002148
    +B1026484542140N00620098EA0000002149
    +B1026504542125N00620090EA0000002150
    +B1026524542117N00620085EA0000002150
    +B1026544542107N00620078EA0000002149
    +B1026564542096N00620070EA0000002148
    +B1026584542086N00620064EA0000002147
    +B1027004542076N00620058EA0000002145
    +B1027024542064N00620049EA0000002141
    +B1027044542051N00620040EA0000002138
    +B1027064542040N00620030EA0000002130
    +B1027084542029N00620022EA0000002126
    +B1027104542018N00620015EA0000002121
    +B1027124542006N00620007EA0000002117
    +B1027144541994N00620000EA0000002113
    +B1027164541981N00619993EA0000002108
    +B1027184541968N00619990EA0000002102
    +B1027204541954N00619985EA0000002097
    +B1027224541941N00619975EA0000002091
    +B1027244541929N00619965EA0000002086
    +B1027264541916N00619957EA0000002082
    +B1027284541904N00619948EA0000002079
    +B1027304541891N00619940EA0000002075
    +B1027324541878N00619933EA0000002071
    +B1027344541865N00619925EA0000002069
    +B1027364541853N00619916EA0000002067
    +B1027384541839N00619907EA0000002064
    +B1027404541825N00619896EA0000002061
    +B1027424541813N00619885EA0000002060
    +B1027444541800N00619876EA0000002058
    +B1027464541786N00619867EA0000002055
    +B1027484541765N00619853EA0000002052
    +B1027504541751N00619844EA0000002050
    +B1027524541738N00619833EA0000002047
    +B1027544541724N00619822EA0000002045
    +B1027564541711N00619812EA0000002044
    +B1027584541697N00619800EA0000002043
    +B1028004541684N00619788EA0000002043
    +B1028024541674N00619779EA0000002045
    +B1028044541666N00619773EA0000002047
    +B1028064541657N00619766EA0000002049
    +B1028084541648N00619758EA0000002052
    +B1028104541641N00619751EA0000002058
    +B1028134541633N00619746EA0000002066
    +B1028154541626N00619742EA0000002071
    +B1028174541618N00619738EA0000002076
    +B1028194541610N00619735EA0000002082
    +B1028214541604N00619729EA0000002089
    +B1028234541598N00619720EA0000002096
    +B1028254541595N00619716EA0000002105
    +B1028274541585N00619710EA0000002113
    +B1028294541576N00619711EA0000002119
    +B1028314541568N00619720EA0000002126
    +B1028334541566N00619734EA0000002134
    +B1028354541570N00619745EA0000002143
    +B1028374541577N00619753EA0000002152
    +B1028394541586N00619759EA0000002161
    +B1028414541595N00619758EA0000002168
    +B1028434541602N00619749EA0000002174
    +B1028454541608N00619738EA0000002177
    +B1028474541614N00619727EA0000002180
    +B1028494541617N00619712EA0000002184
    +B1028514541618N00619696EA0000002188
    +B1028534541615N00619682EA0000002192
    +B1028554541608N00619671EA0000002195
    +B1028574541598N00619664EA0000002198
    +B1028594541590N00619660EA0000002200
    +B1029014541581N00619657EA0000002200
    +B1029034541572N00619653EA0000002202
    +B1029054541564N00619648EA0000002204
    +B1029074541556N00619644EA0000002204
    +B1029094541547N00619640EA0000002203
    +B1029114541537N00619633EA0000002203
    +B1029134541528N00619627EA0000002204
    +B1029154541520N00619623EA0000002203
    +B1029174541511N00619618EA0000002200
    +B1029194541501N00619611EA0000002195
    +B1029214541490N00619604EA0000002190
    +B1029234541479N00619593EA0000002184
    +B1029254541467N00619579EA0000002179
    +B1029274541457N00619568EA0000002176
    +B1029294541441N00619554EA0000002167
    +B1029314541429N00619543EA0000002163
    +B1029334541418N00619532EA0000002160
    +B1029354541408N00619523EA0000002157
    +B1029374541396N00619513EA0000002153
    +B1029394541382N00619505EA0000002151
    +B1029414541371N00619498EA0000002149
    +B1029434541358N00619491EA0000002146
    +B1029454541345N00619483EA0000002141
    +B1029474541333N00619474EA0000002138
    +B1029494541323N00619463EA0000002136
    +B1029514541312N00619451EA0000002132
    +B1029544541301N00619438EA0000002130
    +B1029564541289N00619427EA0000002128
    +B1029584541278N00619415EA0000002125
    +B1030004541267N00619400EA0000002123
    +B1030024541257N00619386EA0000002123
    +B1030044541249N00619374EA0000002122
    +B1030064541240N00619362EA0000002120
    +B1030084541231N00619347EA0000002117
    +B1030104541221N00619333EA0000002117
    +B1030124541213N00619320EA0000002117
    +B1030144541205N00619306EA0000002114
    +B1030164541197N00619288EA0000002110
    +B1030184541188N00619269EA0000002106
    +B1030204541183N00619260EA0000002103
    +B1030224541169N00619236EA0000002097
    +B1030244541158N00619222EA0000002091
    +B1030264541146N00619206EA0000002086
    +B1030284541135N00619190EA0000002082
    +B1030304541127N00619176EA0000002077
    +B1030324541118N00619159EA0000002070
    +B1030344541109N00619139EA0000002066
    +B1030364541100N00619121EA0000002063
    +B1030384541092N00619103EA0000002060
    +B1030404541084N00619084EA0000002059
    +B1030424541077N00619067EA0000002057
    +B1030444541069N00619049EA0000002053
    +B1030464541061N00619030EA0000002050
    +B1030484541053N00619012EA0000002048
    +B1030504541046N00618993EA0000002044
    +B1030524541038N00618975EA0000002039
    +B1030544541029N00618958EA0000002033
    +B1030564541019N00618940EA0000002028
    +B1030584541010N00618923EA0000002024
    +B1031004541002N00618907EA0000002020
    +B1031024540993N00618889EA0000002016
    +B1031044540984N00618870EA0000002013
    +B1031064540976N00618852EA0000002011
    +B1031084540969N00618833EA0000002008
    +B1031104540958N00618805EA0000002004
    +B1031124540949N00618788EA0000002002
    +B1031144540941N00618771EA0000001998
    +B1031164540933N00618752EA0000001993
    +B1031184540924N00618734EA0000001989
    +B1031204540915N00618717EA0000001986
    +B1031224540908N00618699EA0000001983
    +B1031244540900N00618681EA0000001979
    +B1031264540892N00618663EA0000001977
    +B1031284540885N00618644EA0000001974
    +B1031304540877N00618627EA0000001971
    +B1031334540869N00618609EA0000001968
    +B1031354540861N00618590EA0000001965
    +B1031374540853N00618573EA0000001962
    +B1031394540844N00618556EA0000001959
    +B1031414540835N00618538EA0000001956
    +B1031434540829N00618519EA0000001954
    +B1031454540821N00618501EA0000001951
    +B1031474540814N00618483EA0000001948
    +B1031494540808N00618465EA0000001946
    +B1031514540801N00618446EA0000001944
    +B1031534540795N00618428EA0000001942
    +B1031554540787N00618410EA0000001940
    +B1031574540780N00618392EA0000001937
    +B1031594540774N00618373EA0000001934
    +B1032014540768N00618354EA0000001931
    +B1032034540762N00618334EA0000001929
    +B1032054540757N00618315EA0000001925
    +B1032074540751N00618295EA0000001922
    +B1032094540745N00618276EA0000001919
    +B1032114540739N00618256EA0000001916
    +B1032134540734N00618237EA0000001913
    +B1032154540728N00618218EA0000001909
    +B1032174540721N00618200EA0000001905
    +B1032194540715N00618179EA0000001900
    +B1032214540709N00618156EA0000001895
    +B1032234540703N00618134EA0000001891
    +B1032254540698N00618112EA0000001887
    +B1032274540694N00618089EA0000001882
    +B1032294540690N00618066EA0000001877
    +B1032314540686N00618043EA0000001872
    +B1032334540683N00618020EA0000001868
    +B1032354540679N00617997EA0000001865
    +B1032374540675N00617974EA0000001862
    +B1032394540672N00617952EA0000001858
    +B1032414540668N00617930EA0000001855
    +B1032434540664N00617908EA0000001851
    +B1032454540660N00617886EA0000001847
    +B1032474540657N00617862EA0000001843
    +B1032494540653N00617840EA0000001840
    +B1032514540649N00617817EA0000001836
    +B1032534540644N00617783EA0000001831
    +B1032554540641N00617760EA0000001828
    +B1032574540638N00617739EA0000001825
    +B1032594540636N00617717EA0000001824
    +B1033014540635N00617701EA0000001823
    +B1033034540634N00617689EA0000001821
    +B1033054540630N00617675EA0000001817
    +B1033074540627N00617660EA0000001817
    +B1033094540624N00617647EA0000001817
    +B1033114540620N00617633EA0000001815
    +B1033144540616N00617615EA0000001813
    +B1033164540613N00617596EA0000001812
    +B1033184540609N00617576EA0000001810
    +B1033204540606N00617556EA0000001807
    +B1033224540603N00617536EA0000001805
    +B1033244540599N00617514EA0000001802
    +B1033264540598N00617494EA0000001800
    +B1033284540600N00617479EA0000001800
    +B1033304540600N00617462EA0000001798
    +B1033324540600N00617441EA0000001798
    +B1033344540600N00617421EA0000001800
    +B1033364540598N00617407EA0000001801
    +B1033384540596N00617392EA0000001800
    +B1033404540594N00617375EA0000001798
    +B1033424540592N00617355EA0000001797
    +B1033444540593N00617335EA0000001798
    +B1033464540594N00617318EA0000001799
    +B1033484540596N00617302EA0000001798
    +B1033504540598N00617282EA0000001799
    +B1033524540597N00617261EA0000001800
    +B1033544540594N00617243EA0000001802
    +B1033564540592N00617227EA0000001803
    +B1033584540590N00617209EA0000001802
    +B1034004540589N00617193EA0000001801
    +B1034024540587N00617178EA0000001799
    +B1034044540585N00617162EA0000001796
    +B1034064540584N00617147EA0000001795
    +B1034084540581N00617134EA0000001793
    +B1034104540576N00617120EA0000001793
    +B1034124540571N00617107EA0000001794
    +B1034144540569N00617094EA0000001795
    +B1034164540564N00617080EA0000001797
    +B1034184540558N00617066EA0000001800
    +B1034204540554N00617054EA0000001803
    +B1034224540551N00617042EA0000001804
    +B1034244540544N00617031EA0000001805
    +B1034264540533N00617032EA0000001804
    +B1034284540525N00617038EA0000001801
    +B1034304540520N00617047EA0000001799
    +B1034324540516N00617059EA0000001797
    +B1034344540512N00617070EA0000001797
    +B1034364540505N00617083EA0000001801
    +B1034384540501N00617090EA0000001805
    +B1034404540496N00617096EA0000001809
    +B1034424540491N00617103EA0000001812
    +B1034444540488N00617111EA0000001815
    +B1034464540488N00617122EA0000001817
    +B1034484540494N00617130EA0000001819
    +B1034504540505N00617132EA0000001822
    +B1034524540514N00617121EA0000001826
    +B1034554540516N00617104EA0000001831
    +B1034574540513N00617089EA0000001836
    +B1034594540507N00617075EA0000001841
    +B1035014540499N00617067EA0000001845
    +B1035034540490N00617063EA0000001849
    +B1035054540483N00617061EA0000001851
    +B1035074540477N00617066EA0000001853
    +B1035094540473N00617075EA0000001855
    +B1035114540472N00617086EA0000001858
    +B1035134540476N00617097EA0000001863
    +B1035154540483N00617104EA0000001868
    +B1035174540489N00617109EA0000001875
    +B1035194540494N00617115EA0000001882
    +B1035214540499N00617122EA0000001886
    +B1035234540503N00617133EA0000001893
    +B1035254540503N00617145EA0000001900
    +B1035274540499N00617156EA0000001907
    +B1035294540493N00617164EA0000001913
    +B1035314540486N00617164EA0000001917
    +B1035334540477N00617155EA0000001920
    +B1035354540476N00617136EA0000001921
    +B1035374540485N00617121EA0000001924
    +B1035394540496N00617118EA0000001927
    +B1035414540507N00617118EA0000001932
    +B1035434540517N00617120EA0000001936
    +B1035454540527N00617124EA0000001942
    +B1035474540536N00617123EA0000001950
    +B1035494540543N00617116EA0000001956
    +B1035514540551N00617107EA0000001960
    +B1035534540560N00617099EA0000001962
    +B1035554540566N00617088EA0000001962
    +B1035574540569N00617072EA0000001964
    +B1035594540567N00617056EA0000001965
    +B1036014540559N00617045EA0000001966
    +B1036034540547N00617044EA0000001967
    +B1036054540538N00617048EA0000001969
    +B1036074540532N00617051EA0000001973
    +B1036094540525N00617056EA0000001976
    +B1036114540517N00617062EA0000001980
    +B1036134540508N00617069EA0000001985
    +B1036154540504N00617074EA0000001992
    +B1036174540494N00617076EA0000002000
    +B1036194540485N00617070EA0000002004
    +B1036214540480N00617057EA0000002006
    +B1036234540482N00617040EA0000002008
    +B1036254540491N00617028EA0000002009
    +B1036274540502N00617021EA0000002010
    +B1036294540510N00617019EA0000002013
    +B1036314540517N00617020EA0000002017
    +B1036344540521N00617025EA0000002020
    +B1036364540523N00617034EA0000002024
    +B1036384540524N00617044EA0000002027
    +B1036404540522N00617056EA0000002030
    +B1036424540518N00617066EA0000002035
    +B1036444540509N00617070EA0000002040
    +B1036464540498N00617065EA0000002045
    +B1036484540490N00617053EA0000002051
    +B1036504540489N00617037EA0000002056
    +B1036524540495N00617025EA0000002060
    +B1036544540506N00617023EA0000002063
    +B1036564540516N00617026EA0000002067
    +B1036584540525N00617029EA0000002070
    +B1037004540537N00617034EA0000002070
    +B1037024540543N00617040EA0000002074
    +B1037044540548N00617044EA0000002079
    +B1037064540552N00617052EA0000002083
    +B1037084540555N00617065EA0000002085
    +B1037104540549N00617076EA0000002088
    +B1037124540539N00617076EA0000002091
    +B1037144540530N00617070EA0000002094
    +B1037164540526N00617053EA0000002097
    +B1037184540529N00617035EA0000002100
    +B1037204540538N00617023EA0000002104
    +B1037224540549N00617017EA0000002109
    +B1037244540558N00617025EA0000002113
    +B1037264540557N00617038EA0000002116
    +B1037284540548N00617045EA0000002120
    +B1037304540538N00617042EA0000002123
    +B1037324540531N00617034EA0000002126
    +B1037344540525N00617020EA0000002130
    +B1037364540526N00617003EA0000002134
    +B1037384540534N00616991EA0000002138
    +B1037404540546N00616986EA0000002141
    +B1037424540554N00616995EA0000002145
    +B1037444540551N00617010EA0000002149
    +B1037464540540N00617014EA0000002154
    +B1037484540530N00617004EA0000002158
    +B1037504540523N00616984EA0000002165
    +B1037524540527N00616969EA0000002169
    +B1037544540536N00616956EA0000002173
    +B1037564540547N00616951EA0000002178
    +B1037584540554N00616958EA0000002182
    +B1038004540555N00616969EA0000002185
    +B1038024540548N00616980EA0000002187
    +B1038044540537N00616984EA0000002191
    +B1038064540529N00616983EA0000002195
    +B1038094540519N00616983EA0000002199
    +B1038114540509N00616983EA0000002204
    +B1038134540501N00616983EA0000002209
    +B1038154540493N00616982EA0000002212
    +B1038174540483N00616983EA0000002213
    +B1038194540472N00616980EA0000002215
    +B1038214540463N00616973EA0000002218
    +B1038234540455N00616963EA0000002219
    +B1038254540445N00616952EA0000002220
    +B1038274540436N00616940EA0000002220
    +B1038294540428N00616928EA0000002220
    +B1038314540419N00616917EA0000002220
    +B1038334540411N00616905EA0000002219
    +B1038354540403N00616893EA0000002216
    +B1038374540393N00616879EA0000002213
    +B1038394540383N00616865EA0000002211
    +B1038414540375N00616853EA0000002208
    +B1038434540366N00616843EA0000002204
    +B1038454540357N00616830EA0000002199
    +B1038474540348N00616816EA0000002196
    +B1038494540339N00616805EA0000002190
    +B1038514540328N00616792EA0000002184
    +B1038534540319N00616773EA0000002180
    +B1038554540310N00616756EA0000002177
    +B1038574540301N00616741EA0000002172
    +B1038594540292N00616724EA0000002167
    +B1039014540283N00616707EA0000002163
    +B1039034540272N00616693EA0000002160
    +B1039054540262N00616679EA0000002155
    +B1039074540251N00616665EA0000002151
    +B1039094540239N00616652EA0000002147
    +B1039114540228N00616639EA0000002143
    +B1039134540217N00616627EA0000002139
    +B1039154540205N00616614EA0000002134
    +B1039174540194N00616600EA0000002130
    +B1039194540183N00616586EA0000002127
    +B1039214540173N00616572EA0000002123
    +B1039234540158N00616550EA0000002117
    +B1039254540147N00616536EA0000002113
    +B1039274540136N00616521EA0000002109
    +B1039294540126N00616508EA0000002106
    +B1039314540115N00616494EA0000002101
    +B1039334540103N00616478EA0000002094
    +B1039354540089N00616462EA0000002089
    +B1039374540076N00616447EA0000002085
    +B1039394540064N00616431EA0000002081
    +B1039414540052N00616416EA0000002075
    +B1039434540039N00616400EA0000002070
    +B1039454540026N00616384EA0000002065
    +B1039484540013N00616369EA0000002060
    +B1039504540000N00616353EA0000002055
    +B1039524539988N00616337EA0000002050
    +B1039544539975N00616322EA0000002045
    +B1039564539963N00616306EA0000002040
    +B1039584539950N00616290EA0000002035
    +B1040004539937N00616274EA0000002031
    +B1040024539925N00616259EA0000002026
    +B1040044539912N00616244EA0000002022
    +B1040064539899N00616230EA0000002017
    +B1040084539885N00616216EA0000002012
    +B1040104539871N00616202EA0000002007
    +B1040124539858N00616188EA0000002003
    +B1040144539845N00616175EA0000001998
    +B1040164539831N00616161EA0000001993
    +B1040184539817N00616148EA0000001988
    +B1040204539804N00616135EA0000001983
    +B1040224539790N00616122EA0000001978
    +B1040244539776N00616109EA0000001973
    +B1040264539763N00616097EA0000001968
    +B1040284539749N00616084EA0000001963
    +B1040304539735N00616071EA0000001959
    +B1040324539722N00616058EA0000001954
    +B1040344539709N00616045EA0000001949
    +B1040364539696N00616031EA0000001944
    +B1040384539682N00616018EA0000001939
    +B1040404539669N00616004EA0000001934
    +B1040424539656N00615991EA0000001929
    +B1040444539643N00615977EA0000001925
    +B1040464539631N00615964EA0000001921
    +B1040484539621N00615950EA0000001919
    +B1040504539614N00615937EA0000001916
    +B1040524539605N00615925EA0000001914
    +B1040544539596N00615915EA0000001914
    +B1040564539589N00615907EA0000001913
    +B1040584539580N00615899EA0000001912
    +B1041004539571N00615892EA0000001913
    +B1041024539566N00615889EA0000001915
    +B1041044539548N00615880EA0000001918
    +B1041064539540N00615876EA0000001920
    +B1041084539533N00615872EA0000001921
    +B1041104539526N00615868EA0000001921
    +B1041124539520N00615861EA0000001920
    +B1041144539513N00615856EA0000001919
    +B1041164539504N00615853EA0000001917
    +B1041184539494N00615852EA0000001914
    +B1041204539484N00615849EA0000001911
    +B1041224539475N00615846EA0000001910
    +B1041244539465N00615844EA0000001908
    +B1041264539455N00615839EA0000001907
    +B1041294539444N00615835EA0000001906
    +B1041314539433N00615833EA0000001906
    +B1041334539422N00615831EA0000001906
    +B1041354539410N00615826EA0000001908
    +B1041374539399N00615822EA0000001910
    +B1041394539389N00615818EA0000001911
    +B1041414539380N00615811EA0000001911
    +B1041434539371N00615803EA0000001911
    +B1041454539361N00615801EA0000001911
    +B1041474539352N00615799EA0000001910
    +B1041494539343N00615796EA0000001909
    +B1041514539333N00615793EA0000001909
    +B1041534539324N00615791EA0000001907
    +B1041554539313N00615788EA0000001905
    +B1041574539303N00615785EA0000001902
    +B1041594539294N00615783EA0000001898
    +B1042014539283N00615783EA0000001893
    +B1042034539272N00615783EA0000001889
    +B1042054539261N00615785EA0000001885
    +B1042074539250N00615786EA0000001881
    +B1042094539239N00615789EA0000001876
    +B1042114539228N00615791EA0000001871
    +B1042134539217N00615793EA0000001867
    +B1042154539206N00615796EA0000001863
    +B1042174539196N00615797EA0000001859
    +B1042194539188N00615798EA0000001856
    +B1042214539178N00615797EA0000001852
    +B1042234539169N00615796EA0000001849
    +B1042254539163N00615800EA0000001847
    +B1042274539159N00615804EA0000001848
    +B1042294539153N00615804EA0000001850
    +B1042314539148N00615806EA0000001851
    +B1042334539141N00615807EA0000001852
    +B1042354539132N00615803EA0000001854
    +B1042374539126N00615793EA0000001856
    +B1042394539124N00615779EA0000001859
    +B1042414539134N00615758EA0000001862
    +B1042434539148N00615755EA0000001863
    +B1042454539157N00615762EA0000001865
    +B1042474539160N00615772EA0000001868
    +B1042494539158N00615781EA0000001870
    +B1042514539152N00615785EA0000001871
    +B1042534539146N00615787EA0000001873
    +B1042554539141N00615790EA0000001875
    +B1042574539134N00615793EA0000001877
    +B1042594539125N00615794EA0000001877
    +B1043014539115N00615796EA0000001875
    +B1043034539107N00615800EA0000001873
    +B1043064539098N00615803EA0000001870
    +B1043084539087N00615804EA0000001866
    +B1043104539076N00615804EA0000001864
    +B1043124539065N00615802EA0000001862
    +B1043144539054N00615801EA0000001859
    +B1043164539043N00615801EA0000001857
    +B1043184539031N00615800EA0000001854
    +B1043204539020N00615797EA0000001851
    +B1043224539008N00615792EA0000001850
    +B1043244538997N00615787EA0000001848
    +B1043264538986N00615781EA0000001847
    +B1043284538975N00615774EA0000001847
    +B1043304538965N00615767EA0000001848
    +B1043324538956N00615760EA0000001847
    +B1043344538947N00615751EA0000001847
    +B1043364538939N00615743EA0000001848
    +B1043384538934N00615735EA0000001850
    +B1043404538928N00615724EA0000001852
    +B1043424538921N00615718EA0000001854
    +B1043444538915N00615717EA0000001857
    +B1043464538908N00615719EA0000001859
    +B1043484538902N00615723EA0000001863
    +B1043504538897N00615733EA0000001867
    +B1043524538895N00615746EA0000001870
    +B1043544538902N00615761EA0000001873
    +B1043564538913N00615765EA0000001875
    +B1043584538923N00615759EA0000001877
    +B1044004538929N00615744EA0000001878
    +B1044024538928N00615727EA0000001881
    +B1044044538924N00615716EA0000001884
    +B1044064538919N00615706EA0000001887
    +B1044084538914N00615695EA0000001890
    +B1044104538910N00615685EA0000001892
    +B1044124538905N00615678EA0000001896
    +B1044144538900N00615671EA0000001900
    +B1044164538894N00615669EA0000001904
    +B1044184538887N00615672EA0000001905
    +B1044204538880N00615690EA0000001907
    +B1044224538882N00615704EA0000001909
    +B1044244538889N00615714EA0000001910
    +B1044264538899N00615718EA0000001910
    +B1044284538908N00615712EA0000001910
    +B1044304538916N00615698EA0000001910
    +B1044324538917N00615681EA0000001911
    +B1044344538914N00615667EA0000001913
    +B1044364538909N00615655EA0000001915
    +B1044384538903N00615647EA0000001916
    +B1044404538895N00615642EA0000001917
    +B1044424538887N00615636EA0000001919
    +B1044454538880N00615639EA0000001920
    +B1044474538872N00615645EA0000001920
    +B1044494538864N00615649EA0000001922
    +B1044514538859N00615654EA0000001922
    +B1044534538856N00615661EA0000001920
    +B1044554538855N00615670EA0000001918
    +B1044574538859N00615679EA0000001917
    +B1044594538864N00615685EA0000001916
    +B1045014538867N00615692EA0000001914
    +B1045034538866N00615703EA0000001913
    +B1045054538862N00615712EA0000001912
    +B1045074538858N00615718EA0000001913
    +B1045094538853N00615725EA0000001914
    +B1045114538847N00615732EA0000001915
    +B1045134538842N00615740EA0000001916
    +B1045154538836N00615746EA0000001918
    +B1045174538827N00615747EA0000001919
    +B1045194538817N00615737EA0000001920
    +B1045214538813N00615720EA0000001920
    +B1045234538818N00615704EA0000001920
    +B1045254538828N00615699EA0000001920
    +B1045274538834N00615705EA0000001921
    +B1045294538829N00615714EA0000001922
    +B1045314538820N00615717EA0000001925
    +B1045334538811N00615712EA0000001927
    +B1045354538804N00615698EA0000001928
    +B1045374538804N00615682EA0000001929
    +B1045394538813N00615672EA0000001931
    +B1045414538824N00615675EA0000001932
    +B1045434538828N00615685EA0000001934
    +B1045454538826N00615694EA0000001936
    +B1045474538818N00615700EA0000001937
    +B1045494538809N00615700EA0000001939
    +B1045514538801N00615691EA0000001940
    +B1045534538796N00615676EA0000001941
    +B1045554538796N00615660EA0000001942
    +B1045574538804N00615648EA0000001941
    +B1045594538816N00615645EA0000001940
    +B1046014538827N00615657EA0000001939
    +B1046034538831N00615666EA0000001940
    +B1046054538835N00615675EA0000001941
    +B1046074538839N00615682EA0000001943
    +B1046094538842N00615688EA0000001944
    +B1046114538841N00615699EA0000001944
    +B1046134538834N00615708EA0000001943
    +B1046154538822N00615707EA0000001940
    +B1046174538811N00615700EA0000001938
    +B1046194538799N00615694EA0000001936
    +B1046214538788N00615690EA0000001936
    +B1046234538777N00615684EA0000001936
    +B1046264538766N00615676EA0000001935
    +B1046284538753N00615667EA0000001935
    +B1046304538742N00615660EA0000001936
    +B1046324538733N00615654EA0000001935
    +B1046344538723N00615650EA0000001934
    +B1046364538711N00615645EA0000001927
    +B1046384538698N00615639EA0000001924
    +B1046404538686N00615634EA0000001921
    +B1046424538675N00615629EA0000001916
    +B1046444538662N00615621EA0000001910
    +B1046464538649N00615614EA0000001904
    +B1046484538635N00615605EA0000001900
    +B1046504538622N00615598EA0000001896
    +B1046524538609N00615591EA0000001891
    +B1046544538595N00615582EA0000001888
    +B1046564538582N00615574EA0000001886
    +B1046584538570N00615567EA0000001883
    +B1047004538557N00615559EA0000001880
    +B1047024538544N00615551EA0000001876
    +B1047044538532N00615543EA0000001873
    +B1047064538519N00615535EA0000001870
    +B1047084538507N00615526EA0000001866
    +B1047104538494N00615517EA0000001863
    +B1047124538482N00615507EA0000001860
    +B1047144538469N00615497EA0000001857
    +B1047164538457N00615487EA0000001853
    +B1047184538444N00615478EA0000001850
    +B1047204538432N00615468EA0000001847
    +B1047224538419N00615459EA0000001844
    +B1047244538407N00615449EA0000001841
    +B1047264538394N00615439EA0000001838
    +B1047284538381N00615428EA0000001837
    +B1047304538370N00615417EA0000001837
    +B1047324538361N00615406EA0000001839
    +B1047344538351N00615396EA0000001842
    +B1047364538342N00615385EA0000001845
    +B1047384538333N00615375EA0000001848
    +B1047404538325N00615365EA0000001850
    +B1047424538316N00615354EA0000001850
    +B1047444538303N00615338EA0000001851
    +B1047464538293N00615328EA0000001852
    +B1047484538282N00615319EA0000001854
    +B1047504538273N00615308EA0000001857
    +B1047524538266N00615300EA0000001861
    +B1047544538258N00615294EA0000001862
    +B1047564538248N00615288EA0000001865
    +B1047584538239N00615283EA0000001867
    +B1048004538228N00615284EA0000001869
    +B1048024538216N00615285EA0000001873
    +B1048054538206N00615283EA0000001878
    +B1048074538199N00615282EA0000001880
    +B1048094538190N00615281EA0000001882
    +B1048114538180N00615278EA0000001884
    +B1048134538173N00615274EA0000001885
    +B1048154538165N00615272EA0000001885
    +B1048174538155N00615268EA0000001883
    +B1048194538144N00615265EA0000001881
    +B1048214538132N00615263EA0000001878
    +B1048234538120N00615261EA0000001876
    +B1048254538108N00615258EA0000001875
    +B1048274538108N00615258EA0000001875
    +B1048294538088N00615254EA0000001876
    +B1048314538079N00615254EA0000001878
    +B1048334538069N00615252EA0000001882
    +B1048354538061N00615250EA0000001885
    +B1048374538051N00615249EA0000001886
    +B1048394538040N00615247EA0000001887
    +B1048414538029N00615248EA0000001887
    +B1048434538017N00615249EA0000001884
    +B1048454538007N00615252EA0000001879
    +B1048474537997N00615255EA0000001874
    +B1048494537986N00615258EA0000001869
    +B1048514537976N00615264EA0000001863
    +B1048534537965N00615269EA0000001858
    +B1048554537953N00615274EA0000001854
    +B1048574537942N00615279EA0000001850
    +B1048594537930N00615284EA0000001848
    +B1049014537918N00615286EA0000001845
    +B1049034537905N00615286EA0000001844
    +B1049054537892N00615285EA0000001843
    +B1049074537880N00615282EA0000001842
    +B1049094537868N00615276EA0000001842
    +B1049114537857N00615268EA0000001843
    +B1049134537850N00615261EA0000001845
    +B1049154537844N00615258EA0000001848
    +B1049174537836N00615251EA0000001849
    +B1049194537824N00615239EA0000001855
    +B1049214537815N00615240EA0000001859
    +B1049234537808N00615244EA0000001862
    +B1049254537803N00615252EA0000001864
    +B1049274537803N00615262EA0000001866
    +B1049294537810N00615271EA0000001866
    +B1049314537820N00615267EA0000001868
    +B1049334537827N00615255EA0000001870
    +B1049354537830N00615242EA0000001873
    +B1049374537828N00615230EA0000001874
    +B1049394537822N00615219EA0000001874
    +B1049424537813N00615208EA0000001874
    +B1049444537804N00615198EA0000001875
    +B1049464537796N00615192EA0000001874
    +B1049484537786N00615189EA0000001872
    +B1049504537776N00615185EA0000001870
    +B1049524537767N00615182EA0000001870
    +B1049544537758N00615182EA0000001868
    +B1049564537748N00615182EA0000001866
    +B1049584537738N00615178EA0000001864
    +B1050004537728N00615173EA0000001864
    +B1050024537720N00615169EA0000001865
    +B1050044537711N00615165EA0000001863
    +B1050064537701N00615156EA0000001862
    +B1050084537701N00615156EA0000001862
    +B1050104537687N00615137EA0000001862
    +B1050124537681N00615126EA0000001862
    +B1050144537674N00615113EA0000001864
    +B1050164537668N00615102EA0000001867
    +B1050184537662N00615092EA0000001873
    +B1050204537657N00615086EA0000001878
    +B1050224537651N00615082EA0000001881
    +B1050244537643N00615079EA0000001885
    +B1050264537636N00615081EA0000001888
    +B1050284537631N00615092EA0000001889
    +B1050304537630N00615104EA0000001890
    +B1050324537633N00615116EA0000001888
    +B1050344537642N00615128EA0000001889
    +B1050364537650N00615128EA0000001892
    +B1050384537657N00615122EA0000001894
    +B1050404537665N00615113EA0000001898
    +B1050424537669N00615097EA0000001900
    +B1050444537667N00615080EA0000001903
    +B1050464537659N00615069EA0000001907
    +B1050484537651N00615063EA0000001911
    +B1050504537643N00615065EA0000001915
    +B1050524537636N00615074EA0000001918
    +B1050544537632N00615086EA0000001921
    +B1050564537634N00615097EA0000001924
    +B1050584537641N00615102EA0000001925
    +B1051004537655N00615093EA0000001926
    +B1051024537661N00615079EA0000001928
    +B1051044537661N00615061EA0000001931
    +B1051064537657N00615050EA0000001934
    +B1051084537654N00615040EA0000001937
    +B1051104537650N00615028EA0000001939
    +B1051124537647N00615017EA0000001943
    +B1051144537644N00615005EA0000001945
    +B1051164537642N00614994EA0000001946
    +B1051184537638N00614980EA0000001945
    +B1051204537633N00614966EA0000001943
    +B1051234537629N00614950EA0000001940
    +B1051254537625N00614934EA0000001937
    +B1051274537619N00614918EA0000001934
    +B1051294537613N00614903EA0000001931
    +B1051314537606N00614889EA0000001928
    +B1051334537599N00614875EA0000001925
    +B1051354537592N00614861EA0000001923
    +B1051374537584N00614846EA0000001922
    +B1051394537576N00614832EA0000001923
    +B1051414537571N00614819EA0000001924
    +B1051434537564N00614806EA0000001924
    +B1051454537557N00614793EA0000001926
    +B1051474537551N00614781EA0000001928
    +B1051494537545N00614767EA0000001929
    +B1051514537540N00614753EA0000001931
    +B1051534537534N00614741EA0000001934
    +B1051554537530N00614729EA0000001935
    +B1051574537525N00614715EA0000001935
    +B1051594537520N00614701EA0000001935
    +B1052014537513N00614688EA0000001936
    +B1052034537507N00614677EA0000001935
    +B1052054537500N00614665EA0000001933
    +B1052074537491N00614653EA0000001929
    +B1052094537481N00614641EA0000001926
    +B1052114537472N00614628EA0000001923
    +B1052134537464N00614617EA0000001921
    +B1052154537456N00614605EA0000001918
    +B1052174537448N00614593EA0000001917
    +B1052194537439N00614579EA0000001916
    +B1052214537432N00614569EA0000001917
    +B1052234537425N00614559EA0000001915
    +B1052254537416N00614549EA0000001916
    +B1052274537409N00614539EA0000001919
    +B1052294537404N00614528EA0000001921
    +B1052314537397N00614515EA0000001924
    +B1052334537393N00614508EA0000001928
    +B1052354537389N00614505EA0000001937
    +B1052374537379N00614497EA0000001949
    +B1052394537375N00614504EA0000001956
    +B1052414537374N00614519EA0000001963
    +B1052434537379N00614531EA0000001969
    +B1052454537390N00614535EA0000001973
    +B1052474537402N00614531EA0000001977
    +B1052494537409N00614521EA0000001982
    +B1052514537411N00614505EA0000001989
    +B1052534537410N00614492EA0000001997
    +B1052554537405N00614487EA0000002004
    +B1052584537398N00614490EA0000002009
    +B1053004537395N00614503EA0000002015
    +B1053024537399N00614514EA0000002020
    +B1053044537410N00614519EA0000002024
    +B1053064537424N00614516EA0000002028
    +B1053084537433N00614501EA0000002032
    +B1053104537434N00614485EA0000002036
    +B1053124537431N00614472EA0000002042
    +B1053144537425N00614465EA0000002047
    +B1053164537418N00614464EA0000002053
    +B1053184537413N00614473EA0000002056
    +B1053204537413N00614485EA0000002059
    +B1053224537418N00614498EA0000002062
    +B1053244537430N00614501EA0000002065
    +B1053264537442N00614494EA0000002068
    +B1053284537448N00614481EA0000002072
    +B1053304537451N00614465EA0000002076
    +B1053324537450N00614452EA0000002081
    +B1053344537446N00614439EA0000002086
    +B1053364537437N00614432EA0000002090
    +B1053384537430N00614436EA0000002092
    +B1053404537427N00614447EA0000002094
    +B1053424537432N00614459EA0000002096
    +B1053444537441N00614468EA0000002098
    +B1053464537454N00614468EA0000002099
    +B1053484537467N00614456EA0000002100
    +B1053504537469N00614438EA0000002102
    +B1053524537462N00614424EA0000002107
    +B1053544537458N00614414EA0000002113
    +B1053564537457N00614402EA0000002116
    +B1053584537461N00614384EA0000002120
    +B1054004537472N00614370EA0000002126
    +B1054024537484N00614368EA0000002132
    +B1054044537494N00614373EA0000002138
    +B1054064537497N00614384EA0000002142
    +B1054084537495N00614394EA0000002148
    +B1054104537490N00614399EA0000002153
    +B1054124537479N00614395EA0000002156
    +B1054144537471N00614383EA0000002161
    +B1054164537476N00614358EA0000002165
    +B1054184537489N00614349EA0000002168
    +B1054204537500N00614356EA0000002172
    +B1054224537499N00614367EA0000002176
    +B1054244537488N00614373EA0000002179
    +B1054264537479N00614360EA0000002184
    +B1054284537480N00614340EA0000002186
    +B1054304537492N00614327EA0000002188
    +B1054324537505N00614331EA0000002190
    +B1054354537507N00614343EA0000002191
    +B1054374537499N00614350EA0000002193
    +B1054394537490N00614345EA0000002195
    +B1054414537484N00614332EA0000002197
    +B1054434537484N00614312EA0000002191
    +B1054454537497N00614301EA0000002189
    +B1054474537510N00614312EA0000002188
    +B1054494537507N00614327EA0000002187
    +B1054514537495N00614329EA0000002187
    +B1054534537486N00614326EA0000002189
    +B1054554537478N00614325EA0000002188
    +B1054574537468N00614320EA0000002186
    +B1054594537457N00614313EA0000002186
    +B1055014537449N00614307EA0000002186
    +B1055034537439N00614300EA0000002185
    +B1055054537428N00614293EA0000002183
    +B1055074537416N00614286EA0000002181
    +B1055094537403N00614277EA0000002178
    +B1055114537389N00614268EA0000002176
    +B1055134537377N00614262EA0000002175
    +B1055154537364N00614257EA0000002172
    +B1055174537351N00614247EA0000002170
    +B1055194537341N00614242EA0000002169
    +B1055214537332N00614237EA0000002166
    +B1055234537321N00614228EA0000002162
    +B1055254537307N00614215EA0000002158
    +B1055274537297N00614201EA0000002154
    +B1055294537288N00614188EA0000002149
    +B1055314537276N00614176EA0000002144
    +B1055334537264N00614161EA0000002139
    +B1055354537253N00614148EA0000002135
    +B1055374537242N00614136EA0000002130
    +B1055394537230N00614122EA0000002126
    +B1055414537218N00614109EA0000002122
    +B1055434537207N00614097EA0000002120
    +B1055454537199N00614086EA0000002120
    +B1055474537190N00614077EA0000002119
    +B1055494537178N00614067EA0000002118
    +B1055514537167N00614056EA0000002118
    +B1055534537159N00614045EA0000002120
    +B1055554537147N00614030EA0000002124
    +B1055574537138N00614019EA0000002126
    +B1055594537128N00614009EA0000002127
    +B1056014537117N00613997EA0000002126
    +B1056034537107N00613983EA0000002125
    +B1056054537097N00613969EA0000002125
    +B1056074537088N00613954EA0000002124
    +B1056094537079N00613939EA0000002123
    +B1056114537067N00613925EA0000002121
    +B1056144537056N00613910EA0000002120
    +B1056164537046N00613897EA0000002120
    +B1056184537038N00613886EA0000002120
    +B1056204537031N00613872EA0000002122
    +B1056224537024N00613860EA0000002128
    +B1056244537016N00613849EA0000002133
    +B1056264537009N00613838EA0000002137
    +B1056284537005N00613825EA0000002141
    +B1056304537002N00613811EA0000002143
    +B1056324536997N00613798EA0000002144
    +B1056344536989N00613785EA0000002145
    +B1056364536982N00613773EA0000002145
    +B1056384536977N00613760EA0000002145
    +B1056404536971N00613746EA0000002144
    +B1056424536965N00613731EA0000002144
    +B1056444536959N00613715EA0000002144
    +B1056464536953N00613701EA0000002145
    +B1056484536947N00613687EA0000002144
    +B1056504536940N00613674EA0000002142
    +B1056524536933N00613661EA0000002139
    +B1056544536925N00613647EA0000002136
    +B1056564536917N00613635EA0000002134
    +B1056584536908N00613625EA0000002132
    +B1057004536900N00613615EA0000002131
    +B1057024536892N00613607EA0000002132
    +B1057044536884N00613601EA0000002135
    +B1057064536878N00613592EA0000002137
    +B1057084536870N00613582EA0000002141
    +B1057104536864N00613574EA0000002146
    +B1057124536858N00613565EA0000002149
    +B1057144536852N00613557EA0000002152
    +B1057164536845N00613546EA0000002156
    +B1057184536839N00613535EA0000002161
    +B1057204536837N00613524EA0000002165
    +B1057224536836N00613510EA0000002167
    +B1057244536835N00613495EA0000002168
    +B1057264536831N00613482EA0000002168
    +B1057284536825N00613470EA0000002166
    +B1057304536817N00613457EA0000002163
    +B1057324536810N00613442EA0000002161
    +B1057344536799N00613423EA0000002159
    +B1057364536792N00613412EA0000002156
    +B1057384536785N00613400EA0000002153
    +B1057404536779N00613388EA0000002151
    +B1057424536771N00613374EA0000002149
    +B1057444536763N00613361EA0000002147
    +B1057464536756N00613346EA0000002145
    +B1057484536749N00613332EA0000002145
    +B1057504536743N00613319EA0000002145
    +B1057524536738N00613306EA0000002147
    +B1057554536732N00613293EA0000002148
    +B1057574536727N00613280EA0000002147
    +B1057594536720N00613266EA0000002146
    +B1058014536712N00613250EA0000002145
    +B1058034536705N00613235EA0000002144
    +B1058054536698N00613221EA0000002144
    +B1058074536692N00613210EA0000002145
    +B1058094536685N00613200EA0000002144
    +B1058114536678N00613186EA0000002145
    +B1058134536669N00613173EA0000002147
    +B1058154536662N00613162EA0000002149
    +B1058174536656N00613148EA0000002149
    +B1058194536648N00613131EA0000002151
    +B1058214536640N00613115EA0000002155
    +B1058234536636N00613104EA0000002162
    +B1058254536631N00613094EA0000002169
    +B1058274536624N00613083EA0000002175
    +B1058294536617N00613083EA0000002183
    +B1058314536610N00613088EA0000002190
    +B1058334536605N00613094EA0000002197
    +B1058354536603N00613106EA0000002202
    +B1058374536607N00613117EA0000002206
    +B1058394536614N00613127EA0000002208
    +B1058414536625N00613131EA0000002211
    +B1058434536637N00613126EA0000002214
    +B1058454536645N00613115EA0000002215
    +B1058474536649N00613099EA0000002215
    +B1058494536649N00613077EA0000002217
    +B1058514536647N00613058EA0000002221
    +B1058534536645N00613049EA0000002227
    +B1058554536644N00613040EA0000002232
    +B1058574536640N00613024EA0000002238
    +B1058594536635N00613008EA0000002247
    +B1059014536629N00613002EA0000002254
    +B1059034536622N00613000EA0000002259
    +B1059054536611N00612999EA0000002266
    +B1059074536604N00613010EA0000002273
    +B1059094536605N00613021EA0000002279
    +B1059114536616N00613030EA0000002288
    +B1059134536621N00613028EA0000002292
    +B1059154536629N00613021EA0000002300
    +B1059174536638N00613002EA0000002310
    +B1059194536641N00612985EA0000002316
    +B1059214536643N00612965EA0000002323
    +B1059234536642N00612947EA0000002330
    +B1059254536639N00612932EA0000002335
    +B1059274536635N00612918EA0000002339
    +B1059294536628N00612906EA0000002341
    +B1059314536618N00612901EA0000002341
    +B1059344536610N00612905EA0000002337
    +B1059364536603N00612910EA0000002333
    +B1059384536595N00612913EA0000002327
    +B1059404536585N00612911EA0000002322
    +B1059424536577N00612897EA0000002317
    +B1059444536579N00612879EA0000002312
    +B1059464536590N00612866EA0000002305
    +B1059484536604N00612866EA0000002299
    +B1059504536614N00612875EA0000002295
    +B1059524536624N00612883EA0000002293
    +B1059544536634N00612889EA0000002293
    +B1059564536645N00612896EA0000002294
    +B1059584536656N00612900EA0000002296
    +B1100004536666N00612903EA0000002300
    +B1100024536675N00612906EA0000002304
    +B1100044536684N00612909EA0000002307
    +B1100064536696N00612909EA0000002311
    +B1100084536706N00612904EA0000002315
    +B1100104536716N00612894EA0000002319
    +B1100124536723N00612882EA0000002323
    +B1100144536723N00612869EA0000002327
    +B1100164536720N00612856EA0000002328
    +B1100184536716N00612839EA0000002331
    +B1100204536711N00612825EA0000002335
    +B1100224536707N00612816EA0000002339
    +B1100244536703N00612806EA0000002341
    +B1100264536697N00612795EA0000002344
    +B1100284536693N00612784EA0000002346
    +B1100304536688N00612773EA0000002346
    +B1100324536683N00612764EA0000002346
    +B1100344536678N00612754EA0000002344
    +B1100364536672N00612743EA0000002342
    +B1100384536666N00612730EA0000002338
    +B1100404536660N00612717EA0000002333
    +B1100424536653N00612705EA0000002329
    +B1100444536647N00612694EA0000002325
    +B1100464536641N00612681EA0000002320
    +B1100484536634N00612670EA0000002315
    +B1100504536626N00612658EA0000002308
    +B1100524536617N00612645EA0000002302
    +B1100544536607N00612632EA0000002295
    +B1100564536596N00612617EA0000002289
    +B1100584536580N00612595EA0000002282
    +B1101004536571N00612583EA0000002278
    +B1101024536561N00612570EA0000002276
    +B1101044536555N00612560EA0000002276
    +B1101064536548N00612550EA0000002278
    +B1101084536540N00612539EA0000002283
    +B1101104536535N00612532EA0000002292
    +B1101124536529N00612523EA0000002299
    +B1101154536524N00612512EA0000002307
    +B1101174536520N00612503EA0000002315
    +B1101194536517N00612495EA0000002320
    +B1101214536512N00612488EA0000002324
    +B1101234536506N00612480EA0000002326
    +B1101254536500N00612471EA0000002329
    +B1101274536494N00612463EA0000002330
    +B1101294536487N00612454EA0000002330
    +B1101314536480N00612442EA0000002329
    +B1101334536474N00612430EA0000002331
    +B1101354536468N00612420EA0000002334
    +B1101374536465N00612411EA0000002335
    +B1101394536462N00612400EA0000002335
    +B1101414536456N00612389EA0000002334
    +B1101434536450N00612378EA0000002334
    +B1101454536443N00612366EA0000002334
    +B1101474536435N00612355EA0000002333
    +B1101494536428N00612343EA0000002332
    +B1101514536420N00612331EA0000002330
    +B1101534536412N00612319EA0000002328
    +B1101554536404N00612306EA0000002325
    +B1101574536395N00612293EA0000002322
    +B1101594536385N00612280EA0000002317
    +B1102014536374N00612264EA0000002315
    +B1102034536364N00612250EA0000002315
    +B1102054536357N00612239EA0000002318
    +B1102074536352N00612228EA0000002324
    +B1102094536346N00612218EA0000002330
    +B1102114536339N00612207EA0000002335
    +B1102134536332N00612196EA0000002340
    +B1102154536328N00612184EA0000002345
    +B1102174536326N00612170EA0000002348
    +B1102194536322N00612158EA0000002350
    +B1102214536315N00612147EA0000002351
    +B1102234536308N00612136EA0000002351
    +B1102254536301N00612124EA0000002349
    +B1102274536293N00612112EA0000002349
    +B1102294536286N00612100EA0000002349
    +B1102314536276N00612080EA0000002347
    +B1102334536268N00612070EA0000002347
    +B1102354536261N00612060EA0000002346
    +B1102374536257N00612054EA0000002345
    +B1102394536245N00612037EA0000002344
    +B1102414536236N00612027EA0000002343
    +B1102434536227N00612015EA0000002343
    +B1102454536219N00612003EA0000002345
    +B1102474536212N00611993EA0000002345
    +B1102494536204N00611985EA0000002344
    +B1102514536194N00611974EA0000002340
    +B1102534536184N00611960EA0000002336
    +B1102564536174N00611945EA0000002333
    +B1102584536164N00611930EA0000002330
    +B1103004536156N00611913EA0000002327
    +B1103024536147N00611896EA0000002325
    +B1103044536140N00611884EA0000002325
    +B1103064536135N00611874EA0000002322
    +B1103084536127N00611862EA0000002319
    +B1103104536117N00611848EA0000002318
    +B1103124536109N00611836EA0000002316
    +B1103144536100N00611821EA0000002313
    +B1103164536092N00611804EA0000002312
    +B1103184536084N00611788EA0000002310
    +B1103204536075N00611771EA0000002305
    +B1103224536065N00611751EA0000002303
    +B1103244536055N00611733EA0000002300
    +B1103264536045N00611715EA0000002296
    +B1103284536036N00611696EA0000002292
    +B1103304536026N00611675EA0000002290
    +B1103324536018N00611656EA0000002288
    +B1103344536011N00611634EA0000002285
    +B1103364536003N00611611EA0000002282
    +B1103384535996N00611587EA0000002281
    +B1103404535989N00611565EA0000002281
    +B1103424535982N00611545EA0000002282
    +B1103444535977N00611525EA0000002284
    +B1103464535971N00611509EA0000002284
    +B1103484535963N00611493EA0000002283
    +B1103504535953N00611474EA0000002279
    +B1103524535945N00611451EA0000002278
    +B1103544535937N00611430EA0000002277
    +B1103564535930N00611411EA0000002274
    +B1103584535921N00611388EA0000002269
    +B1104004535911N00611365EA0000002267
    +B1104024535903N00611345EA0000002265
    +B1104044535893N00611328EA0000002263
    +B1104064535883N00611312EA0000002263
    +B1104084535876N00611299EA0000002261
    +B1104104535867N00611284EA0000002259
    +B1104124535857N00611269EA0000002258
    +B1104144535843N00611245EA0000002254
    +B1104164535833N00611227EA0000002253
    +B1104184535825N00611210EA0000002253
    +B1104204535819N00611192EA0000002251
    +B1104224535813N00611173EA0000002248
    +B1104244535806N00611153EA0000002247
    +B1104264535801N00611134EA0000002244
    +B1104284535796N00611115EA0000002241
    +B1104304535789N00611095EA0000002239
    +B1104324535783N00611076EA0000002237
    +B1104344535778N00611057EA0000002235
    +B1104374535772N00611038EA0000002233
    +B1104394535768N00611018EA0000002230
    +B1104414535763N00610998EA0000002226
    +B1104434535759N00610976EA0000002223
    +B1104454535754N00610955EA0000002220
    +B1104474535750N00610935EA0000002216
    +B1104494535744N00610913EA0000002212
    +B1104514535738N00610893EA0000002209
    +B1104534535734N00610874EA0000002207
    +B1104554535731N00610855EA0000002203
    +B1104574535726N00610833EA0000002200
    +B1104594535721N00610812EA0000002199
    +B1105014535721N00610812EA0000002200
    +B1105034535717N00610778EA0000002203
    +B1105054535715N00610765EA0000002208
    +B1105074535712N00610749EA0000002214
    +B1105094535708N00610734EA0000002218
    +B1105114535705N00610719EA0000002221
    +B1105134535702N00610701EA0000002222
    +B1105154535698N00610682EA0000002224
    +B1105174535695N00610664EA0000002227
    +B1105194535691N00610647EA0000002228
    +B1105214535687N00610629EA0000002232
    +B1105234535682N00610611EA0000002235
    +B1105254535678N00610596EA0000002238
    +B1105274535674N00610580EA0000002238
    +B1105294535669N00610562EA0000002238
    +B1105314535663N00610544EA0000002239
    +B1105334535656N00610526EA0000002240
    +B1105354535651N00610508EA0000002240
    +B1105374535646N00610489EA0000002241
    +B1105394535641N00610471EA0000002242
    +B1105414535636N00610453EA0000002243
    +B1105434535631N00610434EA0000002243
    +B1105454535627N00610413EA0000002244
    +B1105474535624N00610391EA0000002246
    +B1105494535620N00610371EA0000002249
    +B1105514535617N00610347EA0000002251
    +B1105534535615N00610329EA0000002250
    +B1105554535613N00610311EA0000002251
    +B1105574535611N00610297EA0000002251
    +B1105594535608N00610282EA0000002249
    +B1106014535604N00610263EA0000002246
    +B1106034535602N00610246EA0000002246
    +B1106054535600N00610231EA0000002243
    +B1106074535597N00610215EA0000002240
    +B1106094535593N00610197EA0000002235
    +B1106124535590N00610178EA0000002232
    +B1106144535586N00610162EA0000002228
    +B1106164535581N00610148EA0000002223
    +B1106184535576N00610131EA0000002218
    +B1106204535571N00610115EA0000002214
    +B1106224535568N00610099EA0000002209
    +B1106244535568N00610083EA0000002204
    +B1106264535567N00610066EA0000002198
    +B1106284535563N00610049EA0000002194
    +B1106304535558N00610031EA0000002190
    +B1106324535555N00610023EA0000002188
    +B1106344535549N00610000EA0000002185
    +B1106364535545N00609984EA0000002180
    +B1106384535540N00609966EA0000002177
    +B1106404535537N00609949EA0000002175
    +B1106424535532N00609934EA0000002172
    +B1106444535527N00609917EA0000002167
    +B1106464535519N00609899EA0000002165
    +B1106484535513N00609882EA0000002163
    +B1106504535508N00609865EA0000002160
    +B1106524535503N00609845EA0000002158
    +B1106544535499N00609825EA0000002158
    +B1106564535495N00609808EA0000002156
    +B1106584535491N00609790EA0000002155
    +B1107004535487N00609770EA0000002154
    +B1107024535486N00609752EA0000002154
    +B1107044535484N00609735EA0000002153
    +B1107064535482N00609715EA0000002150
    +B1107084535480N00609692EA0000002149
    +B1107104535478N00609672EA0000002149
    +B1107124535477N00609653EA0000002147
    +B1107144535475N00609632EA0000002144
    +B1107164535473N00609608EA0000002143
    +B1107184535473N00609588EA0000002142
    +B1107204535473N00609568EA0000002138
    +B1107224535472N00609545EA0000002135
    +B1107244535471N00609513EA0000002130
    +B1107264535471N00609492EA0000002125
    +B1107284535470N00609470EA0000002121
    +B1107304535470N00609447EA0000002118
    +B1107324535471N00609425EA0000002115
    +B1107344535471N00609404EA0000002113
    +B1107364535471N00609382EA0000002110
    +B1107384535470N00609360EA0000002107
    +B1107404535471N00609339EA0000002104
    +B1107424535471N00609317EA0000002101
    +B1107444535470N00609295EA0000002099
    +B1107464535469N00609273EA0000002098
    +B1107494535467N00609253EA0000002096
    +B1107514535466N00609231EA0000002095
    +B1107534535465N00609209EA0000002094
    +B1107554535464N00609188EA0000002093
    +B1107574535462N00609167EA0000002091
    +B1107594535461N00609146EA0000002088
    +B1108014535461N00609125EA0000002086
    +B1108034535459N00609104EA0000002084
    +B1108054535459N00609084EA0000002084
    +B1108074535459N00609065EA0000002084
    +B1108094535459N00609048EA0000002082
    +B1108114535459N00609031EA0000002078
    +B1108134535460N00609022EA0000002076
    +B1108154535462N00608991EA0000002071
    +B1108174535463N00608972EA0000002069
    +B1108194535463N00608955EA0000002066
    +B1108214535464N00608936EA0000002062
    +B1108234535466N00608917EA0000002060
    +B1108254535468N00608898EA0000002057
    +B1108274535469N00608880EA0000002055
    +B1108294535470N00608863EA0000002053
    +B1108314535471N00608845EA0000002051
    +B1108334535470N00608827EA0000002048
    +B1108354535469N00608808EA0000002046
    +B1108374535468N00608789EA0000002044
    +B1108394535467N00608771EA0000002042
    +B1108414535466N00608753EA0000002040
    +B1108434535464N00608734EA0000002038
    +B1108454535463N00608716EA0000002035
    +B1108474535461N00608697EA0000002032
    +B1108494535460N00608679EA0000002030
    +B1108514535457N00608661EA0000002029
    +B1108534535454N00608644EA0000002026
    +B1108554535451N00608625EA0000002023
    +B1108574535449N00608606EA0000002022
    +B1108594535447N00608589EA0000002020
    +B1109014535444N00608571EA0000002018
    +B1109034535442N00608552EA0000002016
    +B1109054535439N00608525EA0000002014
    +B1109074535436N00608506EA0000002012
    +B1109094535433N00608489EA0000002010
    +B1109114535431N00608471EA0000002007
    +B1109134535429N00608452EA0000002006
    +B1109154535428N00608434EA0000002005
    +B1109174535427N00608416EA0000002003
    +B1109194535426N00608399EA0000002001
    +B1109214535425N00608380EA0000002000
    +B1109234535426N00608362EA0000001999
    +B1109254535426N00608345EA0000001997
    +B1109284535425N00608327EA0000001996
    +B1109304535425N00608309EA0000001994
    +B1109324535425N00608291EA0000001993
    +B1109344535424N00608274EA0000001992
    +B1109364535424N00608256EA0000001990
    +B1109384535424N00608238EA0000001988
    +B1109404535423N00608219EA0000001985
    +B1109424535423N00608197EA0000001982
    +B1109444535423N00608176EA0000001980
    +B1109464535422N00608156EA0000001977
    +B1109484535423N00608135EA0000001973
    +B1109504535424N00608113EA0000001970
    +B1109524535425N00608091EA0000001968
    +B1109544535426N00608070EA0000001965
    +B1109564535428N00608048EA0000001962
    +B1109584535431N00608026EA0000001960
    +B1110004535434N00608004EA0000001957
    +B1110024535436N00607984EA0000001954
    +B1110044535439N00607962EA0000001949
    +B1110064535442N00607940EA0000001946
    +B1110084535446N00607919EA0000001943
    +B1110104535448N00607899EA0000001940
    +B1110124535450N00607879EA0000001936
    +B1110144535453N00607857EA0000001932
    +B1110164535455N00607835EA0000001929
    +B1110184535456N00607814EA0000001925
    +B1110204535457N00607792EA0000001920
    +B1110224535458N00607768EA0000001917
    +B1110244535458N00607746EA0000001915
    +B1110264535458N00607724EA0000001913
    +B1110284535460N00607702EA0000001909
    +B1110304535463N00607680EA0000001906
    +B1110324535467N00607658EA0000001905
    +B1110344535469N00607639EA0000001903
    +B1110364535471N00607619EA0000001899
    +B1110384535474N00607598EA0000001895
    +B1110404535479N00607567EA0000001891
    +B1110424535482N00607549EA0000001887
    +B1110444535485N00607529EA0000001882
    +B1110464535488N00607509EA0000001879
    +B1110484535491N00607490EA0000001875
    +B1110504535494N00607472EA0000001871
    +B1110524535497N00607454EA0000001866
    +B1110544535500N00607435EA0000001862
    +B1110564535504N00607418EA0000001857
    +B1110584535509N00607399EA0000001852
    +B1111004535513N00607379EA0000001849
    +B1111024535518N00607363EA0000001847
    +B1111044535522N00607346EA0000001844
    +B1111074535527N00607326EA0000001841
    +B1111094535532N00607306EA0000001840
    +B1111114535535N00607289EA0000001839
    +B1111134535539N00607271EA0000001836
    +B1111154535544N00607251EA0000001834
    +B1111174535549N00607232EA0000001834
    +B1111194535554N00607215EA0000001832
    +B1111214535559N00607200EA0000001828
    +B1111234535563N00607182EA0000001827
    +B1111254535566N00607167EA0000001826
    +B1111274535568N00607153EA0000001823
    +B1111294535571N00607136EA0000001820
    +B1111314535574N00607120EA0000001819
    +B1111334535576N00607107EA0000001816
    +B1111354535579N00607091EA0000001812
    +B1111374535582N00607074EA0000001810
    +B1111394535585N00607060EA0000001808
    +B1111414535588N00607048EA0000001804
    +B1111434535591N00607033EA0000001799
    +B1111454535593N00607015EA0000001794
    +B1111474535596N00606994EA0000001790
    +B1111494535599N00606978EA0000001788
    +B1111514535601N00606962EA0000001785
    +B1111534535604N00606943EA0000001782
    +B1111554535609N00606925EA0000001780
    +B1111574535615N00606909EA0000001778
    +B1111594535621N00606893EA0000001775
    +B1112014535626N00606876EA0000001773
    +B1112034535632N00606859EA0000001771
    +B1112054535639N00606844EA0000001771
    +B1112074535643N00606829EA0000001770
    +B1112094535648N00606814EA0000001767
    +B1112114535654N00606798EA0000001766
    +B1112134535659N00606784EA0000001765
    +B1112154535666N00606773EA0000001765
    +B1112174535673N00606763EA0000001765
    +B1112194535679N00606752EA0000001767
    +B1112214535683N00606741EA0000001771
    +B1112234535685N00606733EA0000001771
    +B1112254535689N00606721EA0000001773
    +B1112274535696N00606718EA0000001777
    +B1112294535702N00606720EA0000001779
    +B1112314535710N00606724EA0000001779
    +B1112334535717N00606734EA0000001780
    +B1112354535720N00606749EA0000001781
    +B1112374535719N00606762EA0000001783
    +B1112394535715N00606773EA0000001788
    +B1112414535710N00606784EA0000001794
    +B1112434535702N00606792EA0000001800
    +B1112464535693N00606791EA0000001805
    +B1112484535686N00606783EA0000001808
    +B1112504535685N00606770EA0000001812
    +B1112524535690N00606760EA0000001816
    +B1112544535696N00606758EA0000001819
    +B1112564535704N00606761EA0000001821
    +B1112584535712N00606770EA0000001824
    +B1113004535715N00606785EA0000001827
    +B1113024535713N00606799EA0000001832
    +B1113044535710N00606809EA0000001840
    +B1113064535704N00606817EA0000001846
    +B1113084535695N00606822EA0000001852
    +B1113104535687N00606820EA0000001856
    +B1113124535681N00606815EA0000001859
    +B1113144535678N00606807EA0000001861
    +B1113164535679N00606796EA0000001862
    +B1113184535690N00606788EA0000001863
    +B1113204535699N00606791EA0000001864
    +B1113224535706N00606799EA0000001865
    +B1113244535714N00606806EA0000001866
    +B1113264535723N00606813EA0000001869
    +B1113284535729N00606819EA0000001875
    +B1113304535733N00606823EA0000001880
    +B1113324535739N00606833EA0000001885
    +B1113344535738N00606848EA0000001890
    +B1113364535727N00606862EA0000001895
    +B1113384535715N00606862EA0000001901
    +B1113404535707N00606852EA0000001907
    +B1113424535706N00606836EA0000001913
    +B1113444535714N00606829EA0000001919
    +B1113464535722N00606829EA0000001925
    +B1113484535728N00606840EA0000001932
    +B1113504535727N00606855EA0000001936
    +B1113524535719N00606869EA0000001941
    +B1113544535705N00606873EA0000001946
    +B1113564535693N00606866EA0000001952
    +B1113584535686N00606854EA0000001957
    +B1114004535689N00606839EA0000001962
    +B1114024535695N00606832EA0000001964
    +B1114044535704N00606835EA0000001967
    +B1114064535707N00606851EA0000001969
    +B1114084535698N00606866EA0000001973
    +B1114104535686N00606868EA0000001978
    +B1114124535677N00606862EA0000001980
    +B1114144535670N00606855EA0000001981
    +B1114164535666N00606844EA0000001981
    +B1114184535666N00606831EA0000001980
    +B1114204535675N00606820EA0000001980
    +B1114224535687N00606821EA0000001979
    +B1114254535694N00606828EA0000001980
    +B1114274535699N00606835EA0000001982
    +B1114294535703N00606842EA0000001981
    +B1114314535710N00606853EA0000001982
    +B1114334535720N00606864EA0000001984
    +B1114354535728N00606872EA0000001988
    +B1114374535735N00606881EA0000001995
    +B1114394535739N00606893EA0000002003
    +B1114414535736N00606908EA0000002008
    +B1114434535730N00606924EA0000002014
    +B1114454535720N00606933EA0000002021
    +B1114474535708N00606934EA0000002027
    +B1114494535700N00606927EA0000002034
    +B1114514535699N00606916EA0000002040
    +B1114534535702N00606907EA0000002046
    +B1114554535707N00606901EA0000002055
    +B1114574535711N00606898EA0000002062
    +B1114594535718N00606898EA0000002069
    +B1115014535726N00606906EA0000002074
    +B1115034535730N00606923EA0000002079
    +B1115054535727N00606939EA0000002085
    +B1115074535721N00606951EA0000002093
    +B1115094535712N00606956EA0000002101
    +B1115114535702N00606958EA0000002108
    +B1115134535693N00606957EA0000002114
    +B1115154535685N00606950EA0000002120
    +B1115174535683N00606939EA0000002125
    +B1115194535685N00606929EA0000002131
    +B1115214535692N00606924EA0000002135
    +B1115234535698N00606920EA0000002140
    +B1115254535707N00606917EA0000002144
    +B1115274535717N00606915EA0000002150
    +B1115294535726N00606917EA0000002156
    +B1115314535731N00606927EA0000002162
    +B1115334535732N00606941EA0000002169
    +B1115354535732N00606956EA0000002176
    +B1115374535728N00606972EA0000002182
    +B1115394535720N00606982EA0000002187
    +B1115414535708N00606984EA0000002189
    +B1115434535703N00606975EA0000002189
    +B1115454535702N00606964EA0000002192
    +B1115474535703N00606954EA0000002195
    +B1115494535704N00606944EA0000002199
    +B1115514535707N00606933EA0000002203
    +B1115534535712N00606923EA0000002207
    +B1115554535718N00606914EA0000002213
    +B1115574535724N00606912EA0000002217
    +B1115594535732N00606911EA0000002220
    +B1116014535743N00606913EA0000002226
    +B1116034535747N00606923EA0000002232
    +B1116064535749N00606937EA0000002238
    +B1116084535747N00606948EA0000002243
    +B1116104535740N00606956EA0000002248
    +B1116124535731N00606960EA0000002251
    +B1116144535720N00606961EA0000002252
    +B1116164535711N00606952EA0000002253
    +B1116184535707N00606938EA0000002256
    +B1116204535707N00606925EA0000002258
    +B1116224535708N00606912EA0000002260
    +B1116244535712N00606900EA0000002261
    +B1116264535718N00606891EA0000002261
    +B1116284535727N00606886EA0000002261
    +B1116304535732N00606885EA0000002262
    +B1116324535747N00606889EA0000002265
    +B1116344535755N00606893EA0000002269
    +B1116364535763N00606894EA0000002276
    +B1116384535771N00606894EA0000002283
    +B1116404535777N00606892EA0000002290
    +B1116424535783N00606893EA0000002295
    +B1116444535789N00606900EA0000002298
    +B1116464535793N00606913EA0000002300
    +B1116484535788N00606929EA0000002301
    +B1116504535776N00606941EA0000002304
    +B1116524535765N00606940EA0000002308
    +B1116544535756N00606935EA0000002313
    +B1116564535748N00606929EA0000002317
    +B1116584535741N00606920EA0000002321
    +B1117004535736N00606910EA0000002325
    +B1117024535733N00606898EA0000002329
    +B1117044535733N00606886EA0000002334
    +B1117064535736N00606876EA0000002338
    +B1117084535744N00606870EA0000002341
    +B1117104535753N00606876EA0000002345
    +B1117124535758N00606889EA0000002349
    +B1117144535758N00606903EA0000002353
    +B1117164535751N00606914EA0000002357
    +B1117184535740N00606918EA0000002362
    +B1117204535730N00606914EA0000002367
    +B1117224535721N00606900EA0000002371
    +B1117244535720N00606886EA0000002373
    +B1117264535725N00606873EA0000002376
    +B1117284535731N00606865EA0000002378
    +B1117304535737N00606858EA0000002380
    +B1117324535744N00606849EA0000002382
    +B1117344535751N00606844EA0000002383
    +B1117364535760N00606846EA0000002384
    +B1117384535768N00606852EA0000002386
    +B1117404535770N00606864EA0000002388
    +B1117424535765N00606878EA0000002390
    +B1117444535753N00606884EA0000002394
    +B1117474535742N00606882EA0000002398
    +B1117494535734N00606874EA0000002400
    +B1117514535730N00606862EA0000002402
    +B1117534535732N00606847EA0000002403
    +B1117554535736N00606835EA0000002404
    +B1117574535742N00606822EA0000002406
    +B1117594535747N00606810EA0000002409
    +B1118014535752N00606797EA0000002412
    +B1118034535757N00606785EA0000002415
    +B1118054535761N00606775EA0000002418
    +B1118074535766N00606765EA0000002421
    +B1118094535770N00606754EA0000002423
    +B1118114535775N00606745EA0000002425
    +B1118134535782N00606739EA0000002426
    +B1118154535792N00606742EA0000002428
    +B1118174535798N00606751EA0000002432
    +B1118194535800N00606762EA0000002435
    +B1118214535797N00606774EA0000002438
    +B1118234535792N00606784EA0000002442
    +B1118254535784N00606788EA0000002444
    +B1118274535774N00606790EA0000002446
    +B1118294535762N00606786EA0000002448
    +B1118314535754N00606774EA0000002449
    +B1118334535751N00606759EA0000002450
    +B1118354535754N00606745EA0000002452
    +B1118374535758N00606732EA0000002452
    +B1118394535763N00606721EA0000002453
    +B1118414535767N00606709EA0000002453
    +B1118434535770N00606697EA0000002453
    +B1118454535774N00606683EA0000002452
    +B1118474535775N00606668EA0000002451
    +B1118494535774N00606653EA0000002451
    +B1118514535773N00606638EA0000002450
    +B1118534535774N00606621EA0000002449
    +B1118554535775N00606605EA0000002448
    +B1118574535775N00606590EA0000002447
    +B1118594535774N00606575EA0000002444
    +B1119014535777N00606548EA0000002441
    +B1119034535779N00606531EA0000002441
    +B1119054535780N00606516EA0000002438
    +B1119074535781N00606500EA0000002436
    +B1119094535780N00606484EA0000002435
    +B1119114535780N00606468EA0000002434
    +B1119134535780N00606453EA0000002434
    +B1119154535779N00606439EA0000002435
    +B1119174535776N00606426EA0000002434
    +B1119194535772N00606413EA0000002433
    +B1119214535768N00606399EA0000002433
    +B1119234535763N00606386EA0000002431
    +B1119254535759N00606372EA0000002429
    +B1119284535756N00606353EA0000002426
    +B1119304535752N00606335EA0000002425
    +B1119324535747N00606320EA0000002422
    +B1119344535742N00606303EA0000002419
    +B1119364535736N00606286EA0000002417
    +B1119384535731N00606270EA0000002414
    +B1119404535726N00606253EA0000002412
    +B1119424535721N00606235EA0000002409
    +B1119444535714N00606219EA0000002408
    +B1119464535710N00606202EA0000002407
    +B1119484535704N00606185EA0000002404
    +B1119504535697N00606166EA0000002401
    +B1119524535688N00606145EA0000002399
    +B1119544535681N00606123EA0000002398
    +B1119564535675N00606102EA0000002395
    +B1119584535667N00606083EA0000002393
    +B1120004535659N00606066EA0000002391
    +B1120024535651N00606047EA0000002387
    +B1120044535643N00606027EA0000002381
    +B1120064535633N00606006EA0000002378
    +B1120084535623N00605989EA0000002375
    +B1120104535612N00605974EA0000002372
    +B1120124535600N00605958EA0000002367
    +B1120144535586N00605943EA0000002365
    +B1120164535573N00605932EA0000002364
    +B1120184535562N00605918EA0000002362
    +B1120204535552N00605901EA0000002358
    +B1120224535539N00605886EA0000002354
    +B1120244535525N00605873EA0000002352
    +B1120264535514N00605857EA0000002348
    +B1120284535504N00605841EA0000002343
    +B1120304535491N00605827EA0000002339
    +B1120324535478N00605812EA0000002336
    +B1120344535467N00605797EA0000002332
    +B1120364535454N00605784EA0000002328
    +B1120384535441N00605770EA0000002325
    +B1120404535428N00605756EA0000002322
    +B1120424535409N00605736EA0000002316
    +B1120444535396N00605723EA0000002312
    +B1120464535384N00605709EA0000002308
    +B1120484535371N00605697EA0000002304
    +B1120504535358N00605685EA0000002300
    +B1120524535346N00605671EA0000002296
    +B1120544535335N00605656EA0000002292
    +B1120564535324N00605642EA0000002288
    +B1120584535313N00605626EA0000002283
    +B1121004535301N00605609EA0000002279
    +B1121024535289N00605593EA0000002277
    +B1121044535279N00605577EA0000002274
    +B1121074535267N00605561EA0000002271
    +B1121094535255N00605545EA0000002269
    +B1121114535243N00605530EA0000002267
    +B1121134535232N00605514EA0000002265
    +B1121154535220N00605499EA0000002264
    +B1121174535208N00605483EA0000002264
    +B1121194535197N00605467EA0000002262
    +B1121214535187N00605449EA0000002260
    +B1121234535176N00605431EA0000002259
    +B1121254535164N00605414EA0000002259
    +B1121274535152N00605398EA0000002258
    +B1121294535141N00605381EA0000002257
    +B1121314535128N00605364EA0000002258
    +B1121334535115N00605349EA0000002258
    +B1121354535103N00605333EA0000002257
    +B1121374535090N00605317EA0000002256
    +B1121394535075N00605302EA0000002256
    +B1121414535062N00605287EA0000002256
    +B1121434535050N00605271EA0000002255
    +B1121454535036N00605255EA0000002252
    +B1121474535022N00605240EA0000002252
    +B1121494535009N00605224EA0000002251
    +B1121514534996N00605209EA0000002248
    +B1121534534982N00605194EA0000002245
    +B1121554534969N00605179EA0000002242
    +B1121574534955N00605164EA0000002240
    +B1121594534942N00605151EA0000002238
    +B1122014534929N00605137EA0000002235
    +B1122034534915N00605123EA0000002231
    +B1122054534901N00605108EA0000002228
    +B1122074534888N00605093EA0000002226
    +B1122094534874N00605079EA0000002223
    +B1122114534861N00605065EA0000002220
    +B1122134534847N00605050EA0000002217
    +B1122154534834N00605033EA0000002214
    +B1122174534821N00605019EA0000002212
    +B1122194534807N00605005EA0000002208
    +B1122214534793N00604990EA0000002205
    +B1122234534779N00604976EA0000002203
    +B1122254534759N00604956EA0000002198
    +B1122274534745N00604942EA0000002196
    +B1122294534731N00604928EA0000002193
    +B1122314534718N00604914EA0000002190
    +B1122334534704N00604901EA0000002187
    +B1122354534691N00604887EA0000002184
    +B1122374534677N00604873EA0000002182
    +B1122394534664N00604859EA0000002179
    +B1122414534651N00604845EA0000002177
    +B1122434534637N00604831EA0000002174
    +B1122454534624N00604818EA0000002172
    +B1122484534611N00604805EA0000002169
    +B1122504534598N00604792EA0000002166
    +B1122524534584N00604778EA0000002164
    +B1122544534571N00604764EA0000002161
    +B1122564534558N00604750EA0000002158
    +B1122584534545N00604736EA0000002155
    +B1123004534531N00604723EA0000002152
    +B1123024534518N00604709EA0000002149
    +B1123044534505N00604696EA0000002146
    +B1123064534492N00604683EA0000002143
    +B1123084534478N00604671EA0000002140
    +B1123104534465N00604658EA0000002136
    +B1123124534452N00604645EA0000002133
    +B1123144534445N00604638EA0000002131
    +B1123164534426N00604619EA0000002128
    +B1123184534413N00604607EA0000002125
    +B1123204534400N00604594EA0000002123
    +B1123224534386N00604582EA0000002120
    +B1123244534373N00604570EA0000002118
    +B1123264534359N00604558EA0000002115
    +B1123284534346N00604545EA0000002113
    +B1123304534333N00604532EA0000002110
    +B1123324534320N00604519EA0000002108
    +B1123344534307N00604507EA0000002105
    +B1123364534294N00604495EA0000002102
    +B1123384534280N00604483EA0000002099
    +B1123404534266N00604471EA0000002096
    +B1123424534252N00604460EA0000002093
    +B1123444534239N00604448EA0000002091
    +B1123464534226N00604437EA0000002089
    +B1123484534213N00604426EA0000002086
    +B1123504534200N00604414EA0000002083
    +B1123524534187N00604402EA0000002079
    +B1123544534174N00604388EA0000002076
    +B1123564534162N00604376EA0000002072
    +B1123584534149N00604363EA0000002070
    +B1124004534137N00604350EA0000002067
    +B1124024534125N00604337EA0000002065
    +B1124044534113N00604326EA0000002063
    +B1124064534094N00604309EA0000002060
    +B1124084534080N00604299EA0000002059
    +B1124104534067N00604288EA0000002057
    +B1124124534052N00604277EA0000002055
    +B1124144534039N00604267EA0000002054
    +B1124164534025N00604256EA0000002052
    +B1124184534012N00604246EA0000002050
    +B1124204533997N00604238EA0000002048
    +B1124224533986N00604233EA0000002048
    +B1124244533979N00604228EA0000002049
    +B1124264533970N00604221EA0000002050
    +B1124294533959N00604212EA0000002051
    +B1124314533949N00604204EA0000002053
    +B1124334533939N00604197EA0000002059
    +B1124354533930N00604187EA0000002061
    +B1124374533924N00604171EA0000002064
    +B1124394533918N00604158EA0000002066
    +B1124414533912N00604146EA0000002066
    +B1124434533908N00604129EA0000002067
    +B1124454533911N00604111EA0000002069
    +B1124474533921N00604101EA0000002070
    +B1124494533932N00604100EA0000002071
    +B1124514533942N00604104EA0000002072
    +B1124534533951N00604109EA0000002074
    +B1124554533958N00604112EA0000002075
    +B1124574533966N00604116EA0000002077
    +B1124594533972N00604122EA0000002081
    +B1125014533975N00604130EA0000002086
    +B1125034533978N00604136EA0000002090
    +B1125054533979N00604145EA0000002092
    +B1125074533974N00604156EA0000002097
    +B1125094533962N00604158EA0000002101
    +B1125114533953N00604148EA0000002105
    +B1125134533952N00604128EA0000002109
    +B1125154533961N00604114EA0000002111
    +B1125174533973N00604116EA0000002114
    +B1125194533979N00604122EA0000002118
    +B1125214533977N00604133EA0000002124
    +B1125234533973N00604138EA0000002128
    +B1125254533967N00604141EA0000002131
    +B1125274533955N00604139EA0000002133
    +B1125294533944N00604126EA0000002136
    +B1125314533943N00604107EA0000002140
    +B1125334533952N00604093EA0000002143
    +B1125354533964N00604091EA0000002147
    +B1125374533970N00604099EA0000002151
    +B1125394533970N00604107EA0000002154
    +B1125414533963N00604113EA0000002157
    +B1125434533950N00604109EA0000002159
    +B1125454533936N00604089EA0000002162
    +B1125474533937N00604069EA0000002164
    +B1125494533944N00604053EA0000002167
    +B1125514533954N00604050EA0000002168
    +B1125534533962N00604058EA0000002170
    +B1125554533962N00604068EA0000002173
    +B1125574533958N00604075EA0000002177
    +B1125594533955N00604082EA0000002180
    +B1126014533955N00604089EA0000002183
    +B1126034533953N00604095EA0000002184
    +B1126054533947N00604100EA0000002184
    +B1126074533937N00604104EA0000002184
    +B1126104533926N00604097EA0000002185
    +B1126124533916N00604082EA0000002185
    +B1126144533914N00604063EA0000002183
    +B1126164533921N00604051EA0000002181
    +B1126184533932N00604052EA0000002179
    +B1126204533936N00604058EA0000002177
    +B1126224533938N00604065EA0000002176
    +B1126244533938N00604072EA0000002177
    +B1126264533937N00604079EA0000002178
    +B1126284533936N00604088EA0000002180
    +B1126304533929N00604092EA0000002181
    +B1126324533920N00604092EA0000002182
    +B1126344533908N00604083EA0000002181
    +B1126364533901N00604067EA0000002180
    +B1126384533903N00604049EA0000002178
    +B1126404533913N00604038EA0000002175
    +B1126424533923N00604033EA0000002173
    +B1126444533931N00604033EA0000002172
    +B1126464533936N00604034EA0000002171
    +B1126484533941N00604038EA0000002170
    +B1126504533945N00604045EA0000002169
    +B1126524533944N00604053EA0000002169
    +B1126544533938N00604059EA0000002168
    +B1126564533929N00604059EA0000002167
    +B1126584533919N00604056EA0000002165
    +B1127004533907N00604051EA0000002163
    +B1127024533894N00604046EA0000002161
    +B1127044533883N00604041EA0000002161
    +B1127064533873N00604035EA0000002160
    +B1127084533861N00604027EA0000002158
    +B1127104533850N00604020EA0000002153
    +B1127124533837N00604013EA0000002149
    +B1127144533825N00604002EA0000002144
    +B1127164533812N00603992EA0000002139
    +B1127184533799N00603984EA0000002135
    +B1127204533786N00603975EA0000002133
    +B1127224533773N00603968EA0000002130
    +B1127244533762N00603961EA0000002127
    +B1127264533743N00603947EA0000002121
    +B1127284533730N00603939EA0000002118
    +B1127304533717N00603929EA0000002115
    +B1127324533706N00603920EA0000002112
    +B1127344533694N00603913EA0000002108
    +B1127364533681N00603905EA0000002104
    +B1127384533668N00603896EA0000002102
    +B1127404533657N00603888EA0000002100
    +B1127424533646N00603881EA0000002096
    +B1127444533634N00603872EA0000002093
    +B1127474533622N00603862EA0000002091
    +B1127494533612N00603853EA0000002087
    +B1127514533601N00603844EA0000002082
    +B1127534533588N00603834EA0000002079
    +B1127554533577N00603825EA0000002076
    +B1127574533566N00603816EA0000002073
    +B1127594533555N00603807EA0000002069
    +B1128014533544N00603799EA0000002066
    +B1128034533534N00603792EA0000002062
    +B1128054533523N00603785EA0000002057
    +B1128074533510N00603779EA0000002054
    +B1128094533498N00603771EA0000002051
    +B1128114533488N00603764EA0000002048
    +B1128134533476N00603759EA0000002046
    +B1128154533465N00603752EA0000002044
    +B1128174533454N00603745EA0000002043
    +B1128194533441N00603739EA0000002042
    +B1128214533429N00603733EA0000002042
    +B1128234533418N00603727EA0000002041
    +B1128254533407N00603720EA0000002040
    +B1128274533395N00603711EA0000002041
    +B1128294533384N00603702EA0000002040
    +B1128314533374N00603694EA0000002038
    +B1128334533362N00603685EA0000002037
    +B1128354533351N00603676EA0000002037
    +B1128374533341N00603669EA0000002035
    +B1128394533331N00603661EA0000002032
    +B1128414533321N00603653EA0000002032
    +B1128434533312N00603646EA0000002032
    +B1128454533305N00603640EA0000002031
    +B1128474533296N00603633EA0000002031
    +B1128494533287N00603623EA0000002033
    +B1128514533278N00603613EA0000002036
    +B1128534533271N00603607EA0000002041
    +B1128554533266N00603607EA0000002045
    +B1128574533262N00603611EA0000002047
    +B1128594533264N00603622EA0000002048
    +B1129014533271N00603633EA0000002050
    +B1129034533286N00603637EA0000002054
    +B1129054533298N00603628EA0000002056
    +B1129074533306N00603614EA0000002059
    +B1129094533309N00603596EA0000002060
    +B1129114533306N00603578EA0000002062
    +B1129134533301N00603566EA0000002063
    +B1129154533294N00603556EA0000002064
    +B1129174533288N00603547EA0000002063
    +B1129194533281N00603540EA0000002062
    +B1129214533273N00603533EA0000002061
    +B1129234533265N00603527EA0000002060
    +B1129264533257N00603523EA0000002058
    +B1129284533247N00603520EA0000002056
    +B1129304533237N00603515EA0000002055
    +B1129324533228N00603509EA0000002053
    +B1129344533219N00603503EA0000002051
    +B1129364533210N00603499EA0000002050
    +B1129384533201N00603497EA0000002049
    +B1129404533194N00603497EA0000002046
    +B1129424533185N00603501EA0000002043
    +B1129444533175N00603510EA0000002042
    +B1129464533165N00603517EA0000002043
    +B1129484533159N00603518EA0000002045
    +B1129504533154N00603519EA0000002045
    +B1129524533145N00603520EA0000002046
    +B1129544533137N00603523EA0000002048
    +B1129564533132N00603532EA0000002050
    +B1129584533127N00603542EA0000002053
    +B1130004533124N00603554EA0000002056
    +B1130024533126N00603566EA0000002062
    +B1130044533133N00603576EA0000002068
    +B1130064533142N00603582EA0000002073
    +B1130084533155N00603576EA0000002077
    +B1130104533162N00603562EA0000002082
    +B1130124533161N00603548EA0000002087
    +B1130144533155N00603539EA0000002092
    +B1130164533146N00603538EA0000002097
    +B1130184533138N00603545EA0000002104
    +B1130204533135N00603555EA0000002110
    +B1130224533137N00603566EA0000002115
    +B1130244533142N00603575EA0000002118
    +B1130264533151N00603577EA0000002121
    +B1130284533162N00603565EA0000002123
    +B1130304533164N00603549EA0000002126
    +B1130324533160N00603535EA0000002129
    +B1130344533152N00603526EA0000002134
    +B1130364533145N00603521EA0000002138
    +B1130384533137N00603516EA0000002141
    +B1130404533127N00603512EA0000002146
    +B1130424533119N00603521EA0000002149
    +B1130444533114N00603539EA0000002158
    +B1130464533118N00603545EA0000002164
    +B1130484533127N00603548EA0000002167
    +B1130504533138N00603542EA0000002169
    +B1130524533144N00603527EA0000002171
    +B1130544533146N00603509EA0000002172
    +B1130564533141N00603493EA0000002175
    +B1130584533131N00603485EA0000002179
    +B1131004533122N00603481EA0000002184
    +B1131024533115N00603478EA0000002187
    +B1131054533106N00603479EA0000002189
    +B1131074533100N00603489EA0000002191
    +B1131094533102N00603499EA0000002194
    +B1131114533110N00603503EA0000002196
    +B1131134533120N00603493EA0000002197
    +B1131154533125N00603474EA0000002198
    +B1131174533120N00603454EA0000002199
    +B1131194533110N00603440EA0000002201
    +B1131214533099N00603431EA0000002205
    +B1131234533092N00603422EA0000002206
    +B1131254533083N00603414EA0000002206
    +B1131274533072N00603414EA0000002205
    +B1131294533066N00603422EA0000002202
    +B1131314533063N00603430EA0000002198
    +B1131334533062N00603439EA0000002195
    +B1131354533066N00603447EA0000002193
    +B1131374533072N00603452EA0000002193
    +B1131394533076N00603455EA0000002193
    +B1131414533080N00603459EA0000002191
    +B1131434533087N00603459EA0000002189
    +B1131454533096N00603446EA0000002186
    +B1131474533101N00603429EA0000002185
    +B1131494533100N00603409EA0000002184
    +B1131514533096N00603391EA0000002183
    +B1131534533086N00603379EA0000002182
    +B1131554533075N00603370EA0000002180
    +B1131574533063N00603359EA0000002177
    +B1131594533051N00603348EA0000002173
    +B1132014533038N00603337EA0000002168
    +B1132034533027N00603324EA0000002162
    +B1132054533017N00603311EA0000002155
    +B1132074533006N00603300EA0000002149
    +B1132094532996N00603285EA0000002143
    +B1132114532986N00603269EA0000002139
    +B1132134532977N00603254EA0000002135
    +B1132154532967N00603238EA0000002130
    +B1132174532957N00603221EA0000002124
    +B1132194532937N00603199EA0000002116
    +B1132214532923N00603187EA0000002112
    +B1132234532910N00603175EA0000002106
    +B1132254532897N00603160EA0000002101
    +B1132274532883N00603146EA0000002097
    +B1132294532871N00603133EA0000002093
    +B1132314532858N00603120EA0000002087
    +B1132334532844N00603105EA0000002083
    +B1132354532832N00603091EA0000002079
    +B1132374532819N00603077EA0000002075
    +B1132394532806N00603064EA0000002070
    +B1132414532792N00603050EA0000002065
    +B1132444532779N00603037EA0000002059
    +B1132464532766N00603021EA0000002054
    +B1132484532754N00603008EA0000002049
    +B1132504532741N00602995EA0000002043
    +B1132524532727N00602980EA0000002038
    +B1132544532714N00602966EA0000002034
    +B1132564532701N00602953EA0000002030
    +B1132584532688N00602939EA0000002026
    +B1133004532675N00602925EA0000002023
    +B1133024532661N00602913EA0000002019
    +B1133044532648N00602899EA0000002015
    +B1133064532635N00602884EA0000002011
    +B1133084532622N00602871EA0000002007
    +B1133104532609N00602857EA0000002003
    +B1133124532596N00602846EA0000001998
    +B1133144532583N00602834EA0000001994
    +B1133164532569N00602821EA0000001990
    +B1133184532557N00602807EA0000001986
    +B1133204532546N00602790EA0000001983
    +B1133224532537N00602772EA0000001980
    +B1133244532526N00602756EA0000001975
    +B1133264532515N00602739EA0000001972
    +B1133284532504N00602723EA0000001968
    +B1133304532492N00602709EA0000001964
    +B1133324532481N00602693EA0000001960
    +B1133344532470N00602676EA0000001956
    +B1133364532459N00602661EA0000001953
    +B1133384532448N00602644EA0000001949
    +B1133404532437N00602628EA0000001946
    +B1133424532426N00602612EA0000001943
    +B1133444532414N00602596EA0000001939
    +B1133464532403N00602580EA0000001936
    +B1133484532391N00602565EA0000001933
    +B1133504532379N00602551EA0000001930
    +B1133524532367N00602536EA0000001928
    +B1133544532355N00602521EA0000001925
    +B1133564532343N00602508EA0000001923
    +B1133584532330N00602495EA0000001920
    +B1134004532311N00602476EA0000001916
    +B1134024532298N00602464EA0000001913
    +B1134044532286N00602451EA0000001910
    +B1134064532274N00602437EA0000001907
    +B1134084532262N00602421EA0000001904
    +B1134104532251N00602406EA0000001901
    +B1134124532239N00602390EA0000001897
    +B1134144532228N00602374EA0000001894
    +B1134164532217N00602357EA0000001891
    +B1134184532206N00602341EA0000001888
    +B1134204532195N00602326EA0000001885
    +B1134234532183N00602311EA0000001882
    +B1134254532172N00602295EA0000001879
    +B1134274532160N00602281EA0000001876
    +B1134294532147N00602268EA0000001873
    +B1134314532135N00602254EA0000001870
    +B1134334532123N00602240EA0000001867
    +B1134354532111N00602225EA0000001865
    +B1134374532099N00602211EA0000001862
    +B1134394532087N00602197EA0000001860
    +B1134414532074N00602183EA0000001857
    +B1134434532062N00602169EA0000001854
    +B1134454532049N00602156EA0000001852
    +B1134474532036N00602144EA0000001850
    +B1134494532023N00602132EA0000001847
    +B1134514532010N00602120EA0000001845
    +B1134534531997N00602108EA0000001843
    +B1134554531984N00602096EA0000001841
    +B1134574531972N00602085EA0000001839
    +B1134594531959N00602075EA0000001837
    +B1135014531946N00602064EA0000001834
    +B1135034531932N00602054EA0000001831
    +B1135054531919N00602043EA0000001827
    +B1135074531908N00602030EA0000001825
    +B1135094531901N00602017EA0000001821
    +B1135114531893N00602004EA0000001818
    +B1135134531889N00601988EA0000001815
    +B1135154531887N00601972EA0000001812
    +B1135174531882N00601957EA0000001810
    +B1135194531878N00601942EA0000001809
    +B1135214531874N00601929EA0000001806
    +B1135234531868N00601917EA0000001803
    +B1135254531858N00601907EA0000001802
    +B1135274531849N00601899EA0000001799
    +B1135294531841N00601892EA0000001796
    +B1135314531831N00601886EA0000001794
    +B1135334531820N00601877EA0000001790
    +B1135364531803N00601870EA0000001789
    +B1135384531792N00601864EA0000001789
    +B1135404531782N00601861EA0000001788
    +B1135424531772N00601858EA0000001786
    +B1135444531761N00601855EA0000001782
    +B1135464531747N00601852EA0000001778
    +B1135484531735N00601850EA0000001777
    +B1135504531723N00601848EA0000001775
    +B1135524531712N00601849EA0000001771
    +B1135544531700N00601849EA0000001767
    +B1135564531686N00601848EA0000001763
    +B1135584531666N00601846EA0000001761
    +B1136004531654N00601845EA0000001759
    +B1136024531642N00601842EA0000001758
    +B1136044531630N00601838EA0000001757
    +B1136064531618N00601833EA0000001754
    +B1136084531605N00601824EA0000001751
    +B1136104531593N00601816EA0000001750
    +B1136124531580N00601809EA0000001748
    +B1136144531568N00601801EA0000001747
    +B1136164531555N00601790EA0000001745
    +B1136184531543N00601779EA0000001743
    +B1136214531530N00601769EA0000001740
    +B1136234531517N00601757EA0000001738
    +B1136254531504N00601746EA0000001737
    +B1136274531493N00601737EA0000001734
    +B1136294531482N00601728EA0000001730
    +B1136314531469N00601719EA0000001728
    +B1136334531457N00601714EA0000001727
    +B1136354531445N00601710EA0000001725
    +B1136374531433N00601706EA0000001722
    +B1136394531421N00601702EA0000001721
    +B1136414531410N00601695EA0000001720
    +B1136434531401N00601689EA0000001718
    +B1136454531392N00601683EA0000001715
    +B1136474531381N00601676EA0000001711
    +B1136494531375N00601672EA0000001709
    +B1136514531358N00601664EA0000001706
    +B1136534531348N00601658EA0000001703
    +B1136554531337N00601653EA0000001699
    +B1136574531325N00601646EA0000001696
    +B1136594531314N00601637EA0000001694
    +B1137014531304N00601629EA0000001692
    +B1137034531294N00601621EA0000001689
    +B1137054531283N00601613EA0000001687
    +B1137074531273N00601604EA0000001683
    +B1137094531261N00601595EA0000001679
    +B1137114531247N00601585EA0000001676
    +B1137134531235N00601577EA0000001673
    +B1137154531223N00601568EA0000001669
    +B1137174531210N00601559EA0000001664
    +B1137194531197N00601550EA0000001661
    +B1137214531185N00601540EA0000001657
    +B1137234531174N00601531EA0000001653
    +B1137254531162N00601522EA0000001649
    +B1137274531150N00601513EA0000001647
    +B1137294531139N00601505EA0000001644
    +B1137314531127N00601495EA0000001641
    +B1137334531116N00601485EA0000001638
    +B1137354531104N00601476EA0000001635
    +B1137374531093N00601467EA0000001632
    +B1137394531080N00601459EA0000001628
    +B1137414531061N00601446EA0000001624
    +B1137434531048N00601437EA0000001622
    +B1137454531036N00601430EA0000001620
    +B1137474531023N00601422EA0000001616
    +B1137494531010N00601414EA0000001613
    +B1137514530996N00601405EA0000001612
    +B1137534530985N00601395EA0000001611
    +B1137554530975N00601385EA0000001608
    +B1137574530964N00601374EA0000001605
    +B1137594530953N00601364EA0000001604
    +B1138024530943N00601354EA0000001602
    +B1138044530932N00601343EA0000001599
    +B1138064530921N00601332EA0000001596
    +B1138084530910N00601322EA0000001595
    +B1138104530899N00601312EA0000001592
    +B1138124530888N00601301EA0000001589
    +B1138144530878N00601290EA0000001585
    +B1138164530866N00601279EA0000001582
    +B1138184530854N00601269EA0000001579
    +B1138204530843N00601258EA0000001576
    +B1138224530832N00601248EA0000001573
    +B1138244530820N00601237EA0000001570
    +B1138264530808N00601227EA0000001568
    +B1138284530797N00601217EA0000001565
    +B1138304530785N00601207EA0000001561
    +B1138324530773N00601197EA0000001558
    +B1138344530761N00601187EA0000001554
    +B1138364530749N00601177EA0000001552
    +B1138384530739N00601167EA0000001550
    +B1138404530727N00601157EA0000001547
    +B1138424530715N00601146EA0000001543
    +B1138444530703N00601134EA0000001540
    +B1138464530692N00601122EA0000001537
    +B1138484530680N00601111EA0000001534
    +B1138504530668N00601099EA0000001532
    +B1138524530658N00601086EA0000001530
    +B1138544530647N00601073EA0000001527
    +B1138564530636N00601059EA0000001523
    +B1138584530624N00601045EA0000001521
    +B1139004530612N00601032EA0000001519
    +B1139024530600N00601021EA0000001516
    +B1139044530587N00601009EA0000001513
    +B1139064530574N00600999EA0000001511
    +B1139084530560N00600991EA0000001508
    +B1139104530546N00600982EA0000001505
    +B1139124530532N00600972EA0000001504
    +B1139144530520N00600964EA0000001503
    +B1139164530508N00600957EA0000001501
    +B1139184530495N00600949EA0000001497
    +B1139204530475N00600938EA0000001494
    +B1139224530462N00600932EA0000001491
    +B1139244530449N00600926EA0000001487
    +B1139264530435N00600919EA0000001484
    +B1139284530422N00600912EA0000001482
    +B1139304530409N00600905EA0000001479
    +B1139324530396N00600898EA0000001475
    +B1139344530383N00600890EA0000001472
    +B1139364530370N00600882EA0000001470
    +B1139384530357N00600874EA0000001467
    +B1139404530344N00600865EA0000001464
    +B1139434530331N00600855EA0000001461
    +B1139454530319N00600844EA0000001459
    +B1139474530307N00600834EA0000001456
    +B1139494530294N00600824EA0000001453
    +B1139514530281N00600814EA0000001450
    +B1139534530268N00600806EA0000001448
    +B1139554530255N00600798EA0000001445
    +B1139574530241N00600790EA0000001443
    +B1139594530227N00600783EA0000001440
    +B1140014530213N00600777EA0000001437
    +B1140034530199N00600770EA0000001435
    +B1140054530185N00600762EA0000001431
    +B1140074530172N00600753EA0000001428
    +B1140094530158N00600745EA0000001426
    +B1140114530144N00600737EA0000001423
    +B1140134530131N00600729EA0000001420
    +B1140154530117N00600721EA0000001416
    +B1140174530103N00600713EA0000001414
    +B1140194530089N00600705EA0000001411
    +B1140214530075N00600698EA0000001408
    +B1140234530061N00600690EA0000001405
    +B1140254530047N00600683EA0000001403
    +B1140274530033N00600676EA0000001401
    +B1140294530019N00600670EA0000001398
    +B1140314530005N00600662EA0000001396
    +B1140334529991N00600654EA0000001394
    +B1140354529977N00600646EA0000001392
    +B1140374529963N00600637EA0000001389
    +B1140394529949N00600630EA0000001387
    +B1140414529935N00600622EA0000001385
    +B1140434529922N00600614EA0000001383
    +B1140454529909N00600605EA0000001380
    +B1140474529895N00600596EA0000001378
    +B1140494529883N00600586EA0000001375
    +B1140514529871N00600575EA0000001373
    +B1140534529860N00600564EA0000001371
    +B1140554529849N00600553EA0000001368
    +B1140574529838N00600543EA0000001365
    +B1140594529826N00600533EA0000001363
    +B1141014529809N00600519EA0000001358
    +B1141034529798N00600509EA0000001355
    +B1141054529786N00600500EA0000001352
    +B1141074529774N00600491EA0000001349
    +B1141094529763N00600483EA0000001346
    +B1141114529751N00600473EA0000001343
    +B1141134529741N00600463EA0000001341
    +B1141154529730N00600454EA0000001337
    +B1141174529719N00600444EA0000001334
    +B1141194529708N00600434EA0000001331
    +B1141224529697N00600425EA0000001328
    +B1141244529685N00600416EA0000001325
    +B1141264529674N00600407EA0000001321
    +B1141284529661N00600399EA0000001318
    +B1141304529650N00600390EA0000001315
    +B1141324529639N00600380EA0000001312
    +B1141344529628N00600371EA0000001308
    +B1141364529616N00600362EA0000001305
    +B1141384529605N00600353EA0000001303
    +B1141404529595N00600345EA0000001300
    +B1141424529583N00600336EA0000001297
    +B1141444529573N00600327EA0000001294
    +B1141464529562N00600318EA0000001292
    +B1141484529551N00600310EA0000001290
    +B1141504529540N00600301EA0000001287
    +B1141524529530N00600293EA0000001285
    +B1141544529519N00600285EA0000001282
    +B1141564529508N00600275EA0000001279
    +B1141584529497N00600264EA0000001276
    +B1142004529487N00600256EA0000001274
    +B1142024529477N00600247EA0000001271
    +B1142044529466N00600238EA0000001268
    +B1142064529456N00600229EA0000001265
    +B1142084529446N00600221EA0000001263
    +B1142104529436N00600212EA0000001260
    +B1142124529426N00600204EA0000001257
    +B1142144529415N00600196EA0000001255
    +B1142164529405N00600188EA0000001252
    +B1142184529395N00600180EA0000001249
    +B1142204529384N00600172EA0000001246
    +B1142224529373N00600164EA0000001244
    +B1142244529363N00600156EA0000001242
    +B1142264529353N00600148EA0000001239
    +B1142284529342N00600139EA0000001236
    +B1142304529332N00600131EA0000001234
    +B1142324529321N00600122EA0000001231
    +B1142344529311N00600113EA0000001228
    +B1142364529301N00600102EA0000001226
    +B1142384529290N00600092EA0000001224
    +B1142404529274N00600078EA0000001221
    +B1142424529263N00600070EA0000001219
    +B1142444529252N00600061EA0000001217
    +B1142464529241N00600053EA0000001215
    +B1142484529230N00600045EA0000001212
    +B1142504529218N00600037EA0000001210
    +B1142524529208N00600028EA0000001209
    +B1142544529197N00600019EA0000001207
    +B1142564529186N00600011EA0000001205
    +B1142584529175N00600001EA0000001204
    +B1143014529164N00559993EA0000001202
    +B1143034529153N00559985EA0000001201
    +B1143054529143N00559976EA0000001199
    +B1143074529132N00559967EA0000001197
    +B1143094529122N00559960EA0000001195
    +B1143114529110N00559953EA0000001193
    +B1143134529099N00559946EA0000001191
    +B1143154529087N00559939EA0000001188
    +B1143174529075N00559932EA0000001187
    +B1143194529064N00559926EA0000001184
    +B1143214529052N00559921EA0000001182
    +B1143234529040N00559915EA0000001179
    +B1143254529028N00559909EA0000001177
    +B1143274529016N00559902EA0000001174
    +B1143294529005N00559896EA0000001172
    +B1143314528993N00559888EA0000001170
    +B1143334528982N00559881EA0000001167
    +B1143354528971N00559872EA0000001165
    +B1143374528961N00559864EA0000001162
    +B1143394528950N00559856EA0000001160
    +B1143414528939N00559848EA0000001158
    +B1143434528927N00559841EA0000001156
    +B1143454528916N00559834EA0000001153
    +B1143474528905N00559827EA0000001150
    +B1143494528893N00559821EA0000001148
    +B1143514528883N00559813EA0000001146
    +B1143534528872N00559806EA0000001143
    +B1143554528860N00559800EA0000001141
    +B1143574528848N00559795EA0000001140
    +B1143594528837N00559790EA0000001138
    +B1144014528825N00559784EA0000001136
    +B1144034528813N00559777EA0000001135
    +B1144054528802N00559771EA0000001134
    +B1144074528791N00559764EA0000001132
    +B1144094528780N00559757EA0000001130
    +B1144114528769N00559749EA0000001129
    +B1144134528758N00559742EA0000001128
    +B1144154528746N00559735EA0000001126
    +B1144174528734N00559728EA0000001125
    +B1144194528723N00559722EA0000001124
    +B1144214528712N00559716EA0000001121
    +B1144234528696N00559707EA0000001117
    +B1144254528687N00559700EA0000001113
    +B1144274528676N00559694EA0000001108
    +B1144294528665N00559688EA0000001103
    +B1144314528653N00559682EA0000001100
    +B1144334528642N00559676EA0000001097
    +B1144354528631N00559670EA0000001094
    +B1144374528621N00559663EA0000001091
    +B1144404528611N00559654EA0000001088
    +B1144424528601N00559644EA0000001086
    +B1144444528591N00559635EA0000001083
    +B1144464528580N00559627EA0000001080
    +B1144484528568N00559618EA0000001078
    +B1144504528556N00559610EA0000001077
    +B1144524528544N00559603EA0000001075
    +B1144544528531N00559595EA0000001073
    +B1144564528518N00559586EA0000001072
    +B1144584528506N00559578EA0000001071
    +B1145004528495N00559568EA0000001069
    +B1145024528482N00559560EA0000001067
    +B1145044528469N00559552EA0000001067
    +B1145064528458N00559542EA0000001065
    +B1145084528447N00559531EA0000001064
    +B1145104528436N00559521EA0000001063
    +B1145124528425N00559510EA0000001063
    +B1145144528416N00559498EA0000001061
    +B1145164528406N00559486EA0000001060
    +B1145184528395N00559475EA0000001059
    +B1145204528384N00559464EA0000001059
    +B1145224528373N00559453EA0000001058
    +B1145244528362N00559442EA0000001057
    +B1145264528350N00559431EA0000001056
    +B1145284528338N00559421EA0000001056
    +B1145304528326N00559412EA0000001057
    +B1145324528316N00559404EA0000001058
    +B1145344528305N00559398EA0000001057
    +B1145364528295N00559389EA0000001058
    +B1145384528284N00559382EA0000001060
    +B1145404528273N00559376EA0000001061
    +B1145424528262N00559371EA0000001063
    +B1145444528253N00559364EA0000001064
    +B1145464528247N00559356EA0000001065
    +B1145484528244N00559348EA0000001065
    +B1145504528250N00559343EA0000001062
    +B1145524528258N00559349EA0000001061
    +B1145544528262N00559365EA0000001060
    +B1145564528259N00559384EA0000001059
    +B1145584528247N00559402EA0000001059
    +B1146004528226N00559411EA0000001061
    +B1146024528213N00559410EA0000001062
    +B1146044528202N00559406EA0000001062
    +B1146064528192N00559399EA0000001061
    +B1146084528181N00559390EA0000001060
    +B1146104528172N00559381EA0000001059
    +B1146124528163N00559371EA0000001057
    +B1146144528157N00559361EA0000001056
    +B1146164528149N00559352EA0000001054
    +B1146184528142N00559342EA0000001052
    +B1146214528134N00559332EA0000001050
    +B1146234528127N00559322EA0000001048
    +B1146254528119N00559312EA0000001047
    +B1146274528111N00559302EA0000001046
    +B1146294528104N00559293EA0000001045
    +B1146314528097N00559284EA0000001042
    +B1146334528090N00559273EA0000001039
    +B1146354528083N00559263EA0000001038
    +B1146374528077N00559254EA0000001037
    +B1146394528071N00559245EA0000001036
    +B1146414528065N00559236EA0000001036
    +B1146434528058N00559228EA0000001035
    +B1146454528052N00559219EA0000001034
    +B1146474528045N00559209EA0000001033
    +B1146494528036N00559200EA0000001033
    +B1146514528028N00559191EA0000001032
    +B1146534528021N00559182EA0000001031
    +B1146554528014N00559172EA0000001030
    +B1146574528006N00559162EA0000001030
    +B1146594527999N00559153EA0000001028
    +B1147014527990N00559142EA0000001028
    +B1147034527981N00559131EA0000001028
    +B1147054527973N00559121EA0000001030
    +B1147074527964N00559110EA0000001031
    +B1147094527956N00559099EA0000001032
    +B1147114527947N00559089EA0000001035
    +B1147134527939N00559079EA0000001036
    +B1147154527933N00559069EA0000001037
    +B1147174527928N00559058EA0000001038
    +B1147194527923N00559046EA0000001039
    +B1147214527920N00559036EA0000001041
    +B1147234527922N00559026EA0000001042
    +B1147254527928N00559022EA0000001043
    +B1147274527936N00559026EA0000001043
    +B1147294527940N00559040EA0000001043
    +B1147314527936N00559061EA0000001045
    +B1147334527922N00559071EA0000001047
    +B1147354527910N00559071EA0000001049
    +B1147374527899N00559066EA0000001050
    +B1147394527892N00559056EA0000001051
    +B1147414527888N00559038EA0000001055
    +B1147434527894N00559029EA0000001056
    +B1147454527901N00559028EA0000001056
    +B1147474527908N00559034EA0000001056
    +B1147494527911N00559049EA0000001056
    +B1147514527906N00559067EA0000001057
    +B1147534527895N00559077EA0000001058
    +B1147554527883N00559079EA0000001059
    +B1147574527871N00559076EA0000001061
    +B1147594527861N00559070EA0000001062
    +B1148024527853N00559060EA0000001062
    +B1148044527846N00559049EA0000001063
    +B1148064527843N00559038EA0000001064
    +B1148084527840N00559026EA0000001065
    +B1148104527841N00559015EA0000001065
    +B1148124527847N00559010EA0000001065
    +B1148144527855N00559012EA0000001064
    +B1148164527860N00559022EA0000001063
    +B1148184527857N00559037EA0000001062
    +B1148204527847N00559048EA0000001062
    +B1148224527834N00559048EA0000001062
    +B1148244527823N00559041EA0000001061
    +B1148264527814N00559030EA0000001061
    +B1148284527805N00559018EA0000001060
    +B1148304527797N00559005EA0000001059
    +B1148324527790N00558991EA0000001058
    +B1148344527782N00558977EA0000001057
    +B1148364527774N00558963EA0000001057
    +B1148384527769N00558951EA0000001058
    +B1148404527763N00558938EA0000001058
    +B1148424527757N00558924EA0000001057
    +B1148444527753N00558910EA0000001057
    +B1148464527748N00558897EA0000001057
    +B1148484527743N00558885EA0000001057
    +B1148504527739N00558871EA0000001056
    +B1148524527735N00558855EA0000001056
    +B1148544527730N00558841EA0000001057
    +B1148564527726N00558826EA0000001057
    +B1148584527720N00558810EA0000001057
    +B1149004527714N00558796EA0000001058
    +B1149024527709N00558782EA0000001059
    +B1149044527704N00558767EA0000001059
    +B1149064527698N00558751EA0000001058
    +B1149084527692N00558735EA0000001058
    +B1149104527686N00558720EA0000001059
    +B1149124527681N00558703EA0000001059
    +B1149144527676N00558687EA0000001059
    +B1149164527671N00558671EA0000001058
    +B1149184527667N00558654EA0000001058
    +B1149204527660N00558633EA0000001059
    +B1149224527656N00558619EA0000001059
    +B1149244527652N00558605EA0000001060
    +B1149264527647N00558590EA0000001060
    +B1149284527643N00558574EA0000001059
    +B1149304527638N00558557EA0000001058
    +B1149324527632N00558542EA0000001057
    +B1149344527627N00558529EA0000001057
    +B1149364527621N00558514EA0000001057
    +B1149394527617N00558496EA0000001055
    +B1149414527615N00558480EA0000001054
    +B1149434527613N00558464EA0000001053
    +B1149454527610N00558446EA0000001052
    +B1149474527609N00558427EA0000001052
    +B1149494527607N00558412EA0000001052
    +B1149514527604N00558396EA0000001050
    +B1149534527600N00558380EA0000001050
    +B1149554527594N00558365EA0000001049
    +B1149574527589N00558348EA0000001049
    +B1149594527583N00558332EA0000001048
    +B1150014527577N00558315EA0000001047
    +B1150034527573N00558297EA0000001046
    +B1150054527569N00558280EA0000001046
    +B1150074527565N00558264EA0000001046
    +B1150094527561N00558248EA0000001047
    +B1150114527560N00558233EA0000001047
    +B1150134527558N00558218EA0000001046
    +B1150154527555N00558201EA0000001047
    +B1150174527553N00558186EA0000001046
    +B1150194527549N00558169EA0000001044
    +B1150214527545N00558151EA0000001041
    +B1150234527542N00558135EA0000001039
    +B1150254527540N00558119EA0000001036
    +B1150274527537N00558103EA0000001034
    +B1150294527533N00558085EA0000001031
    +B1150314527532N00558068EA0000001031
    +B1150334527531N00558053EA0000001031
    +B1150354527531N00558039EA0000001029
    +B1150374527531N00558024EA0000001027
    +B1150394527532N00558011EA0000001024
    +B1150414527534N00557998EA0000001021
    +B1150434527536N00557984EA0000001018
    +B1150454527535N00557970EA0000001015
    +B1150474527534N00557957EA0000001011
    +B1150494527533N00557942EA0000001007
    +B1150514527535N00557927EA0000001004
    +B1150534527537N00557914EA0000001001
    +B1150554527535N00557901EA0000000998
    +B1150574527533N00557895EA0000000993
    +B1150594527517N00557882EA0000000992
    +B1151014527508N00557882EA0000000993
    +B1151034527499N00557882EA0000000996
    +B1151054527487N00557879EA0000001000
    +B1151074527479N00557871EA0000001007
    +B1151094527476N00557860EA0000001011
    +B1151114527473N00557848EA0000001013
    +B1151134527473N00557835EA0000001014
    +B1151154527482N00557829EA0000001013
    +B1151184527492N00557829EA0000001016
    +B1151204527497N00557834EA0000001021
    +B1151224527496N00557844EA0000001027
    +B1151244527492N00557853EA0000001033
    +B1151264527486N00557861EA0000001037
    +B1151284527477N00557866EA0000001043
    +B1151304527469N00557862EA0000001047
    +B1151324527464N00557849EA0000001052
    +B1151344527467N00557831EA0000001054
    +B1151364527476N00557822EA0000001056
    +B1151384527484N00557820EA0000001059
    +B1151404527490N00557825EA0000001064
    +B1151424527491N00557836EA0000001068
    +B1151444527483N00557846EA0000001072
    +B1151464527470N00557843EA0000001076
    +B1151484527463N00557831EA0000001081
    +B1151504527461N00557815EA0000001085
    +B1151524527465N00557799EA0000001088
    +B1151544527473N00557787EA0000001092
    +B1151564527482N00557790EA0000001096
    +B1151584527484N00557800EA0000001100
    +B1152004527472N00557812EA0000001106
    +B1152024527460N00557809EA0000001112
    +B1152044527453N00557797EA0000001114
    +B1152064527448N00557782EA0000001117
    +B1152084527450N00557767EA0000001122
    +B1152104527457N00557769EA0000001125
    +B1152124527462N00557782EA0000001128
    +B1152144527451N00557797EA0000001132
    +B1152164527438N00557794EA0000001136
    +B1152184527429N00557780EA0000001138
    +B1152204527426N00557766EA0000001139
    +B1152224527434N00557754EA0000001138
    +B1152244527444N00557751EA0000001139
    +B1152264527451N00557756EA0000001142
    +B1152284527455N00557762EA0000001143
    +B1152304527460N00557769EA0000001145
    +B1152324527466N00557776EA0000001149
    +B1152344527473N00557781EA0000001154
    +B1152364527480N00557786EA0000001158
    +B1152384527489N00557800EA0000001161
    +B1152404527491N00557815EA0000001162
    +B1152424527480N00557828EA0000001162
    +B1152444527466N00557828EA0000001162
    +B1152464527459N00557820EA0000001164
    +B1152484527460N00557810EA0000001169
    +B1152504527465N00557798EA0000001174
    +B1152524527474N00557787EA0000001177
    +B1152544527484N00557779EA0000001181
    +B1152574527494N00557779EA0000001186
    +B1152594527502N00557784EA0000001189
    +B1153014527508N00557794EA0000001191
    +B1153034527514N00557810EA0000001190
    +B1153054527516N00557828EA0000001188
    +B1153074527508N00557843EA0000001183
    +B1153094527494N00557850EA0000001178
    +B1153114527480N00557845EA0000001173
    +B1153134527471N00557833EA0000001169
    +B1153154527469N00557818EA0000001167
    +B1153174527470N00557805EA0000001167
    +B1153194527474N00557795EA0000001168
    +B1153214527481N00557788EA0000001166
    +B1153234527488N00557782EA0000001163
    +B1153254527491N00557773EA0000001161
    +B1153274527490N00557766EA0000001158
    +B1153294527485N00557761EA0000001157
    +B1153314527481N00557752EA0000001156
    +B1153334527477N00557740EA0000001155
    +B1153354527473N00557729EA0000001155
    +B1153374527470N00557716EA0000001156
    +B1153394527467N00557704EA0000001157
    +B1153414527465N00557693EA0000001158
    +B1153434527461N00557678EA0000001158
    +B1153454527456N00557665EA0000001160
    +B1153474527452N00557656EA0000001162
    +B1153494527448N00557643EA0000001163
    +B1153514527448N00557627EA0000001164
    +B1153534527456N00557617EA0000001165
    +B1153554527465N00557618EA0000001167
    +B1153574527468N00557627EA0000001169
    +B1153594527461N00557633EA0000001171
    +B1154014527452N00557630EA0000001174
    +B1154034527436N00557625EA0000001179
    +B1154054527434N00557606EA0000001180
    +B1154074527441N00557591EA0000001181
    +B1154094527451N00557591EA0000001183
    +B1154114527456N00557601EA0000001186
    +B1154134527455N00557614EA0000001190
    +B1154154527453N00557631EA0000001195
    +B1154174527453N00557640EA0000001196
    +B1154194527452N00557652EA0000001198
    +B1154214527448N00557661EA0000001200
    +B1154234527441N00557664EA0000001201
    +B1154254527430N00557661EA0000001202
    +B1154274527421N00557653EA0000001203
    +B1154294527413N00557643EA0000001203
    +B1154314527409N00557628EA0000001203
    +B1154334527411N00557611EA0000001204
    +B1154364527419N00557605EA0000001203
    +B1154384527428N00557610EA0000001203
    +B1154404527431N00557620EA0000001202
    +B1154424527429N00557634EA0000001204
    +B1154444527425N00557646EA0000001206
    +B1154464527422N00557657EA0000001207
    +B1154484527419N00557668EA0000001207
    +B1154504527416N00557679EA0000001207
    +B1154524527416N00557691EA0000001207
    +B1154544527416N00557703EA0000001206
    +B1154564527413N00557717EA0000001208
    +B1154584527410N00557730EA0000001210
    +B1155004527408N00557740EA0000001213
    +B1155024527407N00557750EA0000001214
    +B1155044527406N00557761EA0000001216
    +B1155064527407N00557771EA0000001217
    +B1155084527410N00557782EA0000001217
    +B1155104527412N00557798EA0000001216
    +B1155124527403N00557811EA0000001215
    +B1155144527393N00557811EA0000001217
    +B1155164527383N00557808EA0000001218
    +B1155184527372N00557805EA0000001219
    +B1155204527362N00557797EA0000001219
    +B1155224527358N00557784EA0000001220
    +B1155244527360N00557772EA0000001220
    +B1155264527369N00557769EA0000001221
    +B1155284527376N00557776EA0000001221
    +B1155304527370N00557787EA0000001220
    +B1155324527357N00557790EA0000001221
    +B1155344527346N00557784EA0000001222
    +B1155364527339N00557776EA0000001222
    +B1155384527334N00557766EA0000001221
    +B1155404527330N00557755EA0000001219
    +B1155424527319N00557750EA0000001213
    +B1155444527311N00557743EA0000001213
    +B1155464527301N00557728EA0000001213
    +B1155484527295N00557717EA0000001214
    +B1155504527292N00557713EA0000001214
    +B1155524527280N00557697EA0000001213
    +B1155544527274N00557684EA0000001213
    +B1155564527269N00557670EA0000001213
    +B1155584527261N00557658EA0000001214
    +B1156004527254N00557647EA0000001215
    +B1156024527246N00557637EA0000001216
    +B1156044527238N00557627EA0000001216
    +B1156064527230N00557615EA0000001215
    +B1156084527223N00557602EA0000001213
    +B1156104527215N00557591EA0000001210
    +B1156124527205N00557579EA0000001207
    +B1156154527196N00557566EA0000001205
    +B1156174527188N00557554EA0000001201
    +B1156194527180N00557539EA0000001197
    +B1156214527170N00557525EA0000001193
    +B1156234527159N00557513EA0000001191
    +B1156254527149N00557502EA0000001188
    +B1156274527140N00557490EA0000001183
    +B1156294527130N00557477EA0000001179
    +B1156314527120N00557464EA0000001176
    +B1156334527113N00557451EA0000001173
    +B1156354527107N00557436EA0000001168
    +B1156374527109N00557417EA0000001163
    +B1156394527119N00557408EA0000001158
    +B1156414527130N00557410EA0000001153
    +B1156434527138N00557418EA0000001149
    +B1156454527146N00557426EA0000001145
    +B1156474527153N00557436EA0000001141
    +B1156494527160N00557447EA0000001136
    +B1156514527169N00557459EA0000001131
    +B1156534527178N00557472EA0000001127
    +B1156554527184N00557486EA0000001124
    +B1156574527190N00557498EA0000001119
    +B1156594527198N00557511EA0000001114
    +B1157014527207N00557526EA0000001108
    +B1157034527213N00557541EA0000001104
    +B1157054527219N00557553EA0000001101
    +B1157074527223N00557562EA0000001099
    +B1157094527226N00557570EA0000001098
    +B1157114527234N00557579EA0000001097
    +B1157134527240N00557589EA0000001097
    +B1157154527245N00557598EA0000001098
    +B1157174527248N00557607EA0000001098
    +B1157194527248N00557619EA0000001097
    +B1157214527248N00557633EA0000001097
    +B1157234527250N00557639EA0000001098
    +B1157254527257N00557640EA0000001099
    +B1157274527264N00557646EA0000001102
    +B1157294527266N00557655EA0000001102
    +B1157314527265N00557665EA0000001101
    +B1157334527263N00557670EA0000001101
    +B1157354527248N00557678EA0000001100
    +B1157374527234N00557674EA0000001100
    +B1157394527222N00557663EA0000001101
    +B1157414527215N00557648EA0000001101
    +B1157434527216N00557628EA0000001100
    +B1157454527224N00557613EA0000001101
    +B1157474527231N00557602EA0000001102
    +B1157494527237N00557592EA0000001103
    +B1157514527243N00557583EA0000001104
    +B1157544527246N00557571EA0000001104
    +B1157564527245N00557552EA0000001104
    +B1157584527242N00557536EA0000001105
    +B1158004527234N00557529EA0000001105
    +B1158024527224N00557526EA0000001102
    +B1158044527214N00557531EA0000001099
    +B1158064527205N00557538EA0000001097
    +B1158084527195N00557542EA0000001096
    +B1158104527186N00557549EA0000001094
    +B1158124527178N00557555EA0000001093
    +B1158144527172N00557560EA0000001091
    +B1158164527165N00557568EA0000001088
    +B1158184527157N00557577EA0000001086
    +B1158204527147N00557585EA0000001085
    +B1158224527140N00557592EA0000001084
    +B1158244527135N00557599EA0000001081
    +B1158264527127N00557607EA0000001079
    +B1158284527118N00557613EA0000001077
    +B1158304527111N00557623EA0000001073
    +B1158324527101N00557635EA0000001070
    +B1158344527090N00557646EA0000001069
    +B1158364527081N00557656EA0000001069
    +B1158384527073N00557665EA0000001071
    +B1158404527068N00557671EA0000001071
    +B1158424527064N00557678EA0000001071
    +B1158444527059N00557684EA0000001071
    +B1158464527055N00557690EA0000001073
    +B1158484527053N00557695EA0000001074
    +B1158504527053N00557702EA0000001075
    +B1158524527058N00557708EA0000001075
    +B1158544527067N00557710EA0000001074
    +B1158564527077N00557706EA0000001073
    +B1158584527085N00557688EA0000001073
    +B1159004527087N00557670EA0000001073
    +B1159024527081N00557659EA0000001073
    +B1159044527073N00557657EA0000001072
    +B1159064527064N00557660EA0000001072
    +B1159084527057N00557660EA0000001073
    +B1159104527049N00557658EA0000001072
    +B1159124527036N00557660EA0000001071
    +B1159144527034N00557663EA0000001070
    +B1159164527029N00557672EA0000001069
    +B1159184527019N00557686EA0000001069
    +B1159204527014N00557695EA0000001070
    +B1159224527010N00557703EA0000001069
    +B1159244527004N00557714EA0000001068
    +B1159264526997N00557722EA0000001068
    +B1159284526991N00557730EA0000001068
    +B1159304526985N00557740EA0000001067
    +B1159324526978N00557749EA0000001068
    +B1159354526975N00557756EA0000001069
    +B1159374526970N00557763EA0000001069
    +B1159394526964N00557770EA0000001068
    +B1159414526956N00557774EA0000001068
    +B1159434526949N00557780EA0000001067
    +B1159454526942N00557789EA0000001067
    +B1159474526935N00557796EA0000001067
    +B1159494526928N00557803EA0000001066
    +B1159514526920N00557810EA0000001063
    +B1159534526911N00557814EA0000001061
    +B1159554526903N00557820EA0000001059
    +B1159574526896N00557827EA0000001056
    +B1159594526888N00557834EA0000001053
    +B1200014526882N00557840EA0000001051
    +B1200034526875N00557847EA0000001048
    +B1200054526865N00557855EA0000001046
    +B1200074526855N00557859EA0000001045
    +B1200094526846N00557861EA0000001045
    +B1200114526835N00557863EA0000001044
    +B1200134526823N00557865EA0000001044
    +B1200154526812N00557866EA0000001044
    +B1200174526803N00557866EA0000001044
    +B1200194526792N00557867EA0000001042
    +B1200214526778N00557866EA0000001040
    +B1200234526768N00557867EA0000001038
    +B1200254526757N00557869EA0000001036
    +B1200274526744N00557868EA0000001033
    +B1200294526733N00557868EA0000001031
    +B1200314526723N00557870EA0000001029
    +B1200334526712N00557869EA0000001027
    +B1200354526700N00557870EA0000001025
    +B1200374526689N00557871EA0000001023
    +B1200394526678N00557872EA0000001021
    +B1200414526666N00557872EA0000001018
    +B1200434526655N00557869EA0000001017
    +B1200454526645N00557863EA0000001016
    +B1200474526635N00557858EA0000001016
    +B1200494526625N00557856EA0000001018
    +B1200514526611N00557852EA0000001024
    +B1200534526603N00557841EA0000001027
    +B1200554526600N00557826EA0000001031
    +B1200574526605N00557815EA0000001035
    +B1200594526614N00557814EA0000001039
    +B1201014526622N00557815EA0000001045
    +B1201034526630N00557820EA0000001049
    +B1201054526635N00557830EA0000001053
    +B1201074526633N00557837EA0000001056
    +B1201094526627N00557844EA0000001058
    +B1201124526620N00557849EA0000001060
    +B1201144526610N00557847EA0000001063
    +B1201164526601N00557836EA0000001065
    +B1201184526596N00557822EA0000001070
    +B1201204526592N00557811EA0000001075
    +B1201224526588N00557802EA0000001080
    +B1201244526586N00557791EA0000001084
    +B1201264526592N00557782EA0000001087
    +B1201284526602N00557782EA0000001090
    +B1201304526607N00557795EA0000001090
    +B1201324526596N00557807EA0000001093
    +B1201344526582N00557802EA0000001095
    +B1201364526576N00557794EA0000001098
    +B1201384526569N00557787EA0000001101
    +B1201404526563N00557776EA0000001103
    +B1201424526561N00557769EA0000001105
    +B1201444526568N00557753EA0000001107
    +B1201464526577N00557755EA0000001109
    +B1201484526584N00557762EA0000001112
    +B1201504526589N00557767EA0000001114
    +B1201524526594N00557769EA0000001116
    +B1201544526602N00557766EA0000001116
    +B1201564526607N00557757EA0000001114
    +B1201584526602N00557743EA0000001114
    +B1202004526593N00557729EA0000001111
    +B1202024526583N00557718EA0000001108
    +B1202044526572N00557716EA0000001104
    +B1202064526560N00557722EA0000001101
    +B1202084526550N00557729EA0000001102
    +B1202104526542N00557734EA0000001101
    +B1202124526533N00557738EA0000001099
    +B1202144526523N00557740EA0000001100
    +B1202164526516N00557742EA0000001102
    +B1202184526509N00557739EA0000001101
    +B1202204526501N00557732EA0000001101
    +B1202224526495N00557718EA0000001102
    +B1202244526502N00557700EA0000001101
    +B1202264526513N00557694EA0000001102
    +B1202284526522N00557706EA0000001105
    +B1202304526520N00557711EA0000001106
    +B1202324526500N00557714EA0000001110
    +B1202344526490N00557703EA0000001112
    +B1202364526483N00557691EA0000001115
    +B1202384526476N00557681EA0000001115
    +B1202404526469N00557673EA0000001115
    +B1202424526460N00557672EA0000001114
    +B1202444526451N00557676EA0000001113
    +B1202464526442N00557678EA0000001111
    +B1202484526434N00557682EA0000001108
    +B1202504526425N00557688EA0000001104
    +B1202534526415N00557693EA0000001101
    +B1202554526406N00557699EA0000001097
    +B1202574526398N00557704EA0000001092
    +B1202594526388N00557709EA0000001088
    +B1203014526378N00557715EA0000001085
    +B1203034526368N00557719EA0000001081
    +B1203054526357N00557724EA0000001076
    +B1203074526346N00557729EA0000001073
    +B1203094526336N00557733EA0000001069
    +B1203114526325N00557736EA0000001063
    +B1203134526313N00557740EA0000001056
    +B1203154526301N00557746EA0000001049
    +B1203174526290N00557752EA0000001041
    +B1203194526279N00557760EA0000001033
    +B1203214526268N00557769EA0000001026
    +B1203234526257N00557778EA0000001020
    +B1203254526245N00557786EA0000001015
    +B1203274526235N00557793EA0000001010
    +B1203294526223N00557799EA0000001005
    +B1203314526211N00557805EA0000001002
    +B1203334526200N00557811EA0000000999
    +B1203354526190N00557816EA0000000998
    +B1203374526181N00557818EA0000000999
    +B1203394526172N00557819EA0000001000
    +B1203414526159N00557819EA0000001001
    +B1203434526146N00557818EA0000001001
    +B1203454526133N00557818EA0000001002
    +B1203474526122N00557819EA0000001002
    +B1203494526110N00557819EA0000001002
    +B1203514526097N00557818EA0000001000
    +B1203534526084N00557815EA0000000998
    +B1203554526071N00557815EA0000000997
    +B1203574526059N00557814EA0000000995
    +B1203594526047N00557812EA0000000993
    +B1204014526034N00557811EA0000000992
    +B1204034526022N00557813EA0000000991
    +B1204054526010N00557815EA0000000990
    +B1204074525990N00557819EA0000000987
    +B1204094525978N00557823EA0000000986
    +B1204114525972N00557824EA0000000985
    +B1204134525960N00557827EA0000000982
    +B1204154525943N00557834EA0000000978
    +B1204174525932N00557840EA0000000976
    +B1204194525923N00557847EA0000000973
    +B1204214525913N00557854EA0000000969
    +B1204234525903N00557863EA0000000966
    +B1204254525894N00557872EA0000000964
    +B1204274525886N00557881EA0000000962
    +B1204294525879N00557890EA0000000961
    +B1204324525871N00557900EA0000000959
    +B1204344525861N00557909EA0000000958
    +B1204364525855N00557918EA0000000959
    +B1204384525851N00557925EA0000000962
    +B1204404525845N00557931EA0000000965
    +B1204424525838N00557933EA0000000967
    +B1204444525830N00557935EA0000000968
    +B1204464525821N00557938EA0000000968
    +B1204484525814N00557945EA0000000967
    +B1204504525810N00557948EA0000000965
    +B1204524525794N00557947EA0000000963
    +B1204544525782N00557946EA0000000960
    +B1204564525771N00557945EA0000000958
    +B1204584525760N00557943EA0000000956
    +B1205004525748N00557942EA0000000956
    +B1205024525737N00557940EA0000000957
    +B1205044525726N00557938EA0000000958
    +B1205064525715N00557935EA0000000958
    +B1205084525704N00557932EA0000000958
    +B1205104525692N00557929EA0000000958
    +B1205124525682N00557927EA0000000957
    +B1205144525671N00557925EA0000000955
    +B1205164525658N00557923EA0000000953
    +B1205184525647N00557920EA0000000952
    +B1205204525635N00557919EA0000000950
    +B1205224525622N00557917EA0000000947
    +B1205244525610N00557916EA0000000946
    +B1205264525599N00557914EA0000000944
    +B1205284525586N00557912EA0000000942
    +B1205304525573N00557908EA0000000939
    +B1205324525560N00557905EA0000000937
    +B1205344525546N00557903EA0000000936
    +B1205364525531N00557903EA0000000936
    +B1205384525518N00557904EA0000000937
    +B1205404525507N00557903EA0000000936
    +B1205424525494N00557904EA0000000935
    +B1205444525475N00557908EA0000000935
    +B1205464525469N00557908EA0000000936
    +B1205484525458N00557905EA0000000937
    +B1205504525443N00557899EA0000000938
    +B1205524525432N00557893EA0000000937
    +B1205544525419N00557889EA0000000935
    +B1205564525408N00557886EA0000000934
    +B1205584525398N00557880EA0000000933
    +B1206004525385N00557875EA0000000930
    +B1206024525370N00557870EA0000000928
    +B1206044525357N00557864EA0000000927
    +B1206074525345N00557858EA0000000927
    +B1206094525333N00557850EA0000000928
    +B1206114525321N00557843EA0000000930
    +B1206134525312N00557837EA0000000933
    +B1206154525306N00557829EA0000000937
    +B1206174525299N00557823EA0000000940
    +B1206194525291N00557820EA0000000944
    +B1206214525285N00557825EA0000000948
    +B1206234525284N00557833EA0000000951
    +B1206254525291N00557840EA0000000954
    +B1206274525300N00557834EA0000000957
    +B1206294525308N00557820EA0000000960
    +B1206314525308N00557801EA0000000964
    +B1206334525303N00557790EA0000000967
    +B1206354525297N00557793EA0000000971
    +B1206374525298N00557802EA0000000974
    +B1206394525304N00557805EA0000000976
    +B1206414525312N00557797EA0000000978
    +B1206434525316N00557783EA0000000982
    +B1206454525313N00557769EA0000000986
    +B1206474525308N00557767EA0000000990
    +B1206494525307N00557776EA0000000994
    +B1206514525314N00557780EA0000000997
    +B1206534525322N00557772EA0000000999
    +B1206554525326N00557756EA0000001002
    +B1206574525322N00557739EA0000001005
    +B1206594525315N00557737EA0000001008
    +B1207014525309N00557744EA0000001011
    +B1207034525304N00557751EA0000001015
    +B1207054525298N00557756EA0000001018
    +B1207074525294N00557762EA0000001021
    +B1207094525295N00557771EA0000001023
    +B1207114525302N00557778EA0000001026
    +B1207134525311N00557775EA0000001028
    +B1207154525318N00557762EA0000001030
    +B1207174525315N00557745EA0000001033
    +B1207194525304N00557733EA0000001037
    +B1207214525298N00557739EA0000001040
    +B1207234525297N00557749EA0000001042
    +B1207254525302N00557757EA0000001043
    +B1207274525312N00557758EA0000001045
    +B1207294525322N00557749EA0000001046
    +B1207314525328N00557735EA0000001047
    +B1207334525327N00557716EA0000001049
    +B1207354525323N00557702EA0000001052
    +B1207374525316N00557698EA0000001055
    +B1207394525311N00557704EA0000001058
    +B1207414525311N00557714EA0000001060
    +B1207434525316N00557722EA0000001062
    +B1207464525325N00557723EA0000001064
    +B1207484525335N00557703EA0000001064
    +B1207504525333N00557681EA0000001070
    +B1207524525327N00557669EA0000001075
    +B1207544525320N00557666EA0000001079
    +B1207564525312N00557669EA0000001081
    +B1207584525307N00557683EA0000001084
    +B1208004525313N00557694EA0000001088
    +B1208024525320N00557698EA0000001092
    +B1208044525329N00557697EA0000001094
    +B1208064525338N00557690EA0000001096
    +B1208084525347N00557669EA0000001098
    +B1208104525345N00557651EA0000001100
    +B1208124525338N00557646EA0000001103
    +B1208144525330N00557649EA0000001110
    +B1208164525326N00557656EA0000001113
    +B1208184525325N00557665EA0000001116
    +B1208204525330N00557675EA0000001119
    +B1208224525338N00557680EA0000001122
    +B1208244525348N00557675EA0000001124
    +B1208264525357N00557661EA0000001126
    +B1208284525362N00557645EA0000001128
    +B1208304525363N00557627EA0000001129
    +B1208324525357N00557610EA0000001130
    +B1208344525349N00557602EA0000001131
    +B1208364525340N00557602EA0000001132
    +B1208384525333N00557603EA0000001135
    +B1208404525327N00557608EA0000001138
    +B1208424525324N00557618EA0000001140
    +B1208444525325N00557628EA0000001143
    +B1208464525331N00557632EA0000001146
    +B1208484525342N00557632EA0000001148
    +B1208504525352N00557622EA0000001149
    +B1208524525357N00557607EA0000001151
    +B1208544525354N00557589EA0000001153
    +B1208564525347N00557573EA0000001155
    +B1208584525341N00557567EA0000001159
    +B1209004525332N00557565EA0000001162
    +B1209024525323N00557568EA0000001165
    +B1209044525319N00557584EA0000001171
    +B1209064525325N00557594EA0000001174
    +B1209084525334N00557596EA0000001177
    +B1209104525342N00557589EA0000001179
    +B1209124525349N00557573EA0000001181
    +B1209144525352N00557555EA0000001183
    +B1209164525351N00557539EA0000001187
    +B1209184525347N00557528EA0000001192
    +B1209204525343N00557516EA0000001197
    +B1209234525335N00557506EA0000001201
    +B1209254525326N00557504EA0000001205
    +B1209274525319N00557508EA0000001209
    +B1209294525314N00557516EA0000001212
    +B1209314525314N00557525EA0000001216
    +B1209334525318N00557533EA0000001219
    +B1209354525325N00557534EA0000001222
    +B1209374525333N00557528EA0000001224
    +B1209394525338N00557516EA0000001227
    +B1209414525342N00557503EA0000001230
    +B1209434525344N00557488EA0000001234
    +B1209454525344N00557474EA0000001239
    +B1209474525341N00557464EA0000001243
    +B1209494525337N00557455EA0000001246
    +B1209514525329N00557453EA0000001248
    +B1209534525324N00557461EA0000001251
    +B1209554525322N00557471EA0000001255
    +B1209574525323N00557480EA0000001259
    +B1209594525328N00557485EA0000001264
    +B1210014525336N00557483EA0000001268
    +B1210034525344N00557473EA0000001273
    +B1210054525347N00557460EA0000001277
    +B1210074525344N00557447EA0000001281
    +B1210094525341N00557433EA0000001284
    +B1210114525337N00557421EA0000001286
    +B1210134525329N00557416EA0000001289
    +B1210154525319N00557418EA0000001292
    +B1210174525312N00557423EA0000001296
    +B1210194525305N00557429EA0000001299
    +B1210214525300N00557439EA0000001302
    +B1210234525300N00557449EA0000001304
    +B1210254525309N00557451EA0000001306
    +B1210274525317N00557442EA0000001308
    +B1210294525320N00557426EA0000001311
    +B1210314525320N00557410EA0000001314
    +B1210334525316N00557397EA0000001318
    +B1210354525306N00557381EA0000001322
    +B1210374525303N00557377EA0000001323
    +B1210394525290N00557376EA0000001326
    +B1210414525285N00557382EA0000001328
    +B1210434525283N00557391EA0000001324
    +B1210454525287N00557399EA0000001325
    +B1210474525296N00557399EA0000001326
    +B1210494525304N00557388EA0000001326
    +B1210514525308N00557371EA0000001327
    +B1210534525307N00557355EA0000001331
    +B1210554525302N00557344EA0000001334
    +B1210584525293N00557334EA0000001336
    +B1211004525283N00557331EA0000001337
    +B1211024525275N00557338EA0000001339
    +B1211044525275N00557345EA0000001340
    +B1211064525282N00557346EA0000001342
    +B1211084525289N00557335EA0000001344
    +B1211104525294N00557318EA0000001345
    +B1211124525296N00557301EA0000001346
    +B1211144525294N00557285EA0000001347
    +B1211164525289N00557272EA0000001348
    +B1211184525280N00557264EA0000001347
    +B1211204525270N00557265EA0000001345
    +B1211224525263N00557272EA0000001343
    +B1211244525262N00557283EA0000001341
    +B1211264525269N00557288EA0000001339
    +B1211284525277N00557285EA0000001337
    +B1211304525283N00557274EA0000001335
    +B1211324525286N00557257EA0000001333
    +B1211344525280N00557240EA0000001332
    +B1211364525274N00557226EA0000001331
    +B1211384525267N00557210EA0000001328
    +B1211404525260N00557194EA0000001326
    +B1211424525255N00557176EA0000001323
    +B1211444525249N00557157EA0000001320
    +B1211464525241N00557142EA0000001319
    +B1211484525233N00557128EA0000001317
    +B1211504525223N00557115EA0000001315
    +B1211524525212N00557107EA0000001312
    +B1211544525201N00557101EA0000001307
    +B1211564525187N00557097EA0000001301
    +B1211584525174N00557095EA0000001296
    +B1212004525161N00557093EA0000001292
    +B1212024525147N00557092EA0000001286
    +B1212044525131N00557090EA0000001282
    +B1212064525116N00557086EA0000001276
    +B1212084525099N00557082EA0000001272
    +B1212104525082N00557079EA0000001268
    +B1212124525058N00557073EA0000001263
    +B1212144525042N00557068EA0000001259
    +B1212164525025N00557064EA0000001255
    +B1212184525010N00557058EA0000001251
    +B1212204524994N00557051EA0000001246
    +B1212224524978N00557045EA0000001241
    +B1212244524963N00557038EA0000001237
    +B1212264524948N00557034EA0000001231
    +B1212284524933N00557032EA0000001226
    +B1212304524918N00557029EA0000001221
    +B1212324524904N00557028EA0000001215
    +B1212344524888N00557027EA0000001211
    +B1212374524873N00557025EA0000001207
    +B1212394524862N00557024EA0000001205
    +B1212414524850N00557021EA0000001202
    +B1212434524836N00557019EA0000001201
    +B1212454524825N00557019EA0000001201
    +B1212474524819N00557021EA0000001204
    +B1212494524812N00557020EA0000001205
    +B1212514524803N00557021EA0000001208
    +B1212534524796N00557022EA0000001213
    +B1212554524791N00557027EA0000001216
    +B1212574524786N00557035EA0000001220
    +B1212594524785N00557046EA0000001224
    +B1213014524785N00557046EA0000001228
    +B1213034524796N00557062EA0000001231
    +B1213054524805N00557058EA0000001235
    +B1213074524813N00557049EA0000001239
    +B1213094524814N00557036EA0000001242
    +B1213114524808N00557024EA0000001245
    +B1213134524801N00557016EA0000001249
    +B1213154524794N00557009EA0000001252
    +B1213174524788N00557003EA0000001255
    +B1213194524779N00557003EA0000001257
    +B1213214524771N00557009EA0000001259
    +B1213234524768N00557021EA0000001261
    +B1213254524770N00557031EA0000001264
    +B1213274524776N00557037EA0000001266
    +B1213294524783N00557038EA0000001267
    +B1213314524792N00557035EA0000001269
    +B1213334524800N00557030EA0000001273
    +B1213354524807N00557024EA0000001276
    +B1213374524812N00557014EA0000001278
    +B1213394524812N00556999EA0000001278
    +B1213414524805N00556985EA0000001281
    +B1213434524796N00556983EA0000001282
    +B1213454524790N00556991EA0000001284
    +B1213474524788N00557002EA0000001288
    +B1213494524792N00557011EA0000001291
    +B1213514524799N00557014EA0000001293
    +B1213534524811N00557007EA0000001295
    +B1213554524816N00556995EA0000001296
    +B1213574524816N00556980EA0000001296
    +B1213594524812N00556966EA0000001299
    +B1214014524805N00556957EA0000001300
    +B1214034524796N00556953EA0000001302
    +B1214054524787N00556952EA0000001306
    +B1214074524779N00556950EA0000001310
    +B1214094524772N00556951EA0000001312
    +B1214114524765N00556956EA0000001314
    +B1214134524762N00556967EA0000001316
    +B1214154524766N00556978EA0000001317
    +B1214184524776N00556981EA0000001318
    +B1214204524786N00556976EA0000001319
    +B1214224524792N00556964EA0000001321
    +B1214244524795N00556951EA0000001321
    +B1214264524794N00556934EA0000001322
    +B1214284524789N00556918EA0000001325
    +B1214304524782N00556908EA0000001328
    +B1214324524774N00556902EA0000001330
    +B1214344524766N00556902EA0000001330
    +B1214364524757N00556908EA0000001331
    +B1214384524753N00556917EA0000001334
    +B1214404524752N00556921EA0000001334
    +B1214424524753N00556935EA0000001334
    +B1214444524757N00556946EA0000001334
    +B1214464524766N00556950EA0000001333
    +B1214484524776N00556944EA0000001332
    +B1214504524782N00556927EA0000001331
    +B1214524524778N00556910EA0000001331
    +B1214544524770N00556899EA0000001334
    +B1214564524761N00556891EA0000001333
    +B1214584524751N00556884EA0000001334
    +B1215004524740N00556880EA0000001335
    +B1215024524729N00556877EA0000001336
    +B1215044524719N00556871EA0000001336
    +B1215064524708N00556867EA0000001334
    +B1215084524696N00556861EA0000001331
    +B1215104524685N00556853EA0000001329
    +B1215124524674N00556847EA0000001326
    +B1215144524662N00556842EA0000001323
    +B1215164524649N00556838EA0000001319
    +B1215184524635N00556834EA0000001317
    +B1215204524622N00556829EA0000001315
    +B1215224524610N00556823EA0000001312
    +B1215244524596N00556817EA0000001309
    +B1215264524582N00556810EA0000001306
    +B1215284524570N00556803EA0000001303
    +B1215304524551N00556795EA0000001298
    +B1215324524538N00556791EA0000001293
    +B1215344524525N00556785EA0000001289
    +B1215364524512N00556780EA0000001284
    +B1215384524499N00556775EA0000001278
    +B1215404524486N00556770EA0000001272
    +B1215424524473N00556767EA0000001267
    +B1215444524461N00556762EA0000001260
    +B1215464524448N00556756EA0000001253
    +B1215484524435N00556752EA0000001247
    +B1215504524422N00556748EA0000001241
    +B1215524524408N00556741EA0000001235
    +B1215554524395N00556735EA0000001230
    +B1215574524382N00556728EA0000001224
    +B1215594524367N00556718EA0000001217
    +B1216014524351N00556710EA0000001210
    +B1216034524337N00556706EA0000001205
    +B1216054524324N00556701EA0000001200
    +B1216074524310N00556694EA0000001195
    +B1216094524297N00556687EA0000001191
    +B1216114524284N00556680EA0000001187
    +B1216134524270N00556673EA0000001182
    +B1216154524255N00556666EA0000001178
    +B1216174524242N00556658EA0000001175
    +B1216194524229N00556651EA0000001172
    +B1216214524214N00556645EA0000001168
    +B1216234524199N00556639EA0000001164
    +B1216254524184N00556634EA0000001160
    +B1216274524169N00556628EA0000001156
    +B1216294524155N00556624EA0000001153
    +B1216314524142N00556619EA0000001152
    +B1216334524130N00556616EA0000001151
    +B1216354524120N00556616EA0000001151
    +B1216374524112N00556614EA0000001152
    +B1216394524105N00556612EA0000001154
    +B1216414524099N00556611EA0000001155
    +B1216434524092N00556608EA0000001156
    +B1216454524082N00556602EA0000001155
    +B1216474524075N00556598EA0000001156
    +B1216494524068N00556598EA0000001158
    +B1216514524061N00556599EA0000001160
    +B1216534524055N00556600EA0000001164
    +B1216554524050N00556600EA0000001168
    +B1216574524043N00556597EA0000001171
    +B1216594524038N00556586EA0000001173
    +B1217014524039N00556571EA0000001175
    +B1217034524050N00556564EA0000001177
    +B1217054524065N00556566EA0000001180
    +B1217074524074N00556575EA0000001184
    +B1217094524069N00556588EA0000001190
    +B1217114524061N00556585EA0000001194
    +B1217134524057N00556574EA0000001199
    +B1217154524059N00556560EA0000001203
    +B1217174524068N00556549EA0000001207
    +B1217194524079N00556548EA0000001211
    +B1217214524088N00556554EA0000001215
    +B1217234524088N00556563EA0000001219
    +B1217254524081N00556565EA0000001222
    +B1217274524076N00556553EA0000001226
    +B1217294524078N00556540EA0000001231
    +B1217314524085N00556533EA0000001235
    +B1217344524094N00556531EA0000001241
    +B1217364524103N00556537EA0000001246
    +B1217384524106N00556550EA0000001251
    +B1217404524100N00556559EA0000001255
    +B1217424524094N00556557EA0000001260
    +B1217444524089N00556545EA0000001264
    +B1217464524087N00556532EA0000001269
    +B1217484524091N00556521EA0000001274
    +B1217504524101N00556515EA0000001278
    +B1217524524111N00556517EA0000001282
    +B1217544524118N00556528EA0000001286
    +B1217564524118N00556541EA0000001289
    +B1217584524110N00556546EA0000001292
    +B1218004524101N00556540EA0000001296
    +B1218024524097N00556526EA0000001299
    +B1218044524099N00556513EA0000001302
    +B1218064524107N00556505EA0000001306
    +B1218084524117N00556507EA0000001310
    +B1218104524126N00556517EA0000001313
    +B1218124524125N00556529EA0000001317
    +B1218144524118N00556533EA0000001321
    +B1218164524109N00556528EA0000001323
    +B1218184524105N00556515EA0000001326
    +B1218204524108N00556502EA0000001327
    +B1218224524116N00556495EA0000001330
    +B1218244524125N00556493EA0000001334
    +B1218264524134N00556490EA0000001338
    +B1218284524144N00556492EA0000001341
    +B1218304524149N00556501EA0000001344
    +B1218324524147N00556507EA0000001347
    +B1218344524140N00556502EA0000001349
    +B1218364524135N00556490EA0000001351
    +B1218384524134N00556477EA0000001354
    +B1218404524136N00556463EA0000001356
    +B1218424524139N00556449EA0000001354
    +B1218444524151N00556444EA0000001355
    +B1218464524161N00556451EA0000001355
    +B1218484524161N00556465EA0000001356
    +B1218504524147N00556468EA0000001355
    +B1218524524138N00556460EA0000001357
    +B1218544524134N00556446EA0000001360
    +B1218564524134N00556433EA0000001362
    +B1218584524134N00556420EA0000001364
    +B1219004524131N00556408EA0000001365
    +B1219024524130N00556395EA0000001367
    +B1219044524137N00556382EA0000001368
    +B1219064524148N00556379EA0000001369
    +B1219084524157N00556383EA0000001370
    +B1219104524158N00556392EA0000001370
    +B1219124524148N00556394EA0000001369
    +B1219154524139N00556385EA0000001369
    +B1219174524136N00556372EA0000001371
    +B1219194524133N00556361EA0000001374
    +B1219214524131N00556348EA0000001377
    +B1219234524132N00556334EA0000001378
    +B1219254524140N00556326EA0000001379
    +B1219274524150N00556328EA0000001380
    +B1219294524155N00556338EA0000001381
    +B1219314524152N00556346EA0000001381
    +B1219334524143N00556344EA0000001382
    +B1219354524136N00556331EA0000001382
    +B1219374524135N00556315EA0000001383
    +B1219394524137N00556300EA0000001385
    +B1219414524139N00556283EA0000001386
    +B1219434524140N00556266EA0000001388
    +B1219454524140N00556249EA0000001389
    +B1219474524142N00556233EA0000001389
    +B1219494524146N00556219EA0000001389
    +B1219514524151N00556205EA0000001387
    +B1219534524155N00556188EA0000001384
    +B1219554524161N00556170EA0000001381
    +B1219574524169N00556154EA0000001379
    +B1219594524176N00556139EA0000001379
    +B1220014524183N00556125EA0000001380
    +B1220034524189N00556110EA0000001381
    +B1220054524196N00556096EA0000001383
    +B1220074524202N00556084EA0000001384
    +B1220094524210N00556076EA0000001385
    +B1220114524223N00556076EA0000001383
    +B1220134524235N00556089EA0000001382
    +B1220154524235N00556104EA0000001380
    +B1220174524225N00556108EA0000001378
    +B1220194524218N00556096EA0000001376
    +B1220214524218N00556080EA0000001376
    +B1220234524224N00556069EA0000001376
    +B1220254524231N00556059EA0000001373
    +B1220274524236N00556046EA0000001372
    +B1220294524243N00556024EA0000001369
    +B1220314524248N00556008EA0000001366
    +B1220334524255N00555991EA0000001362
    +B1220354524261N00555974EA0000001359
    +B1220374524266N00555957EA0000001356
    +B1220394524273N00555939EA0000001352
    +B1220414524280N00555921EA0000001351
    +B1220434524285N00555905EA0000001349
    +B1220454524289N00555888EA0000001346
    +B1220474524293N00555870EA0000001344
    +B1220494524295N00555850EA0000001343
    +B1220514524296N00555832EA0000001343
    +B1220544524295N00555816EA0000001341
    +B1220564524291N00555802EA0000001338
    +B1220584524285N00555788EA0000001338
    +B1221004524280N00555778EA0000001337
    +B1221024524273N00555770EA0000001334
    +B1221044524265N00555759EA0000001330
    +B1221064524257N00555748EA0000001328
    +B1221084524249N00555737EA0000001326
    +B1221104524241N00555728EA0000001323
    +B1221124524233N00555719EA0000001320
    +B1221144524224N00555710EA0000001318
    +B1221164524213N00555700EA0000001317
    +B1221184524203N00555691EA0000001318
    +B1221204524196N00555682EA0000001321
    +B1221224524190N00555672EA0000001323
    +B1221244524183N00555666EA0000001325
    +B1221264524178N00555666EA0000001325
    +B1221284524171N00555670EA0000001324
    +B1221304524166N00555676EA0000001324
    +B1221324524162N00555684EA0000001323
    +B1221344524154N00555688EA0000001322
    +B1221364524143N00555683EA0000001322
    +B1221384524133N00555675EA0000001324
    +B1221404524126N00555667EA0000001327
    +B1221424524120N00555657EA0000001328
    +B1221444524117N00555643EA0000001328
    +B1221464524124N00555629EA0000001329
    +B1221484524135N00555628EA0000001329
    +B1221504524140N00555643EA0000001330
    +B1221524524135N00555653EA0000001331
    +B1221544524124N00555649EA0000001332
    +B1221564524117N00555636EA0000001332
    +B1221584524115N00555618EA0000001333
    +B1222004524112N00555602EA0000001332
    +B1222024524109N00555585EA0000001330
    +B1222044524107N00555565EA0000001329
    +B1222064524105N00555547EA0000001328
    +B1222084524102N00555530EA0000001328
    +B1222104524099N00555502EA0000001328
    +B1222124524097N00555486EA0000001329
    +B1222144524092N00555471EA0000001329
    +B1222164524087N00555455EA0000001329
    +B1222184524084N00555439EA0000001331
    +B1222204524081N00555425EA0000001333
    +B1222224524077N00555414EA0000001338
    +B1222244524071N00555409EA0000001339
    +B1222264524064N00555413EA0000001340
    +B1222284524064N00555425EA0000001341
    +B1222314524073N00555434EA0000001342
    +B1222334524084N00555435EA0000001344
    +B1222354524094N00555430EA0000001346
    +B1222374524103N00555417EA0000001346
    +B1222394524108N00555398EA0000001348
    +B1222414524106N00555382EA0000001351
    +B1222434524101N00555373EA0000001355
    +B1222454524095N00555364EA0000001357
    +B1222474524089N00555363EA0000001358
    +B1222494524085N00555370EA0000001359
    +B1222514524087N00555381EA0000001360
    +B1222534524098N00555383EA0000001361
    +B1222554524110N00555376EA0000001362
    +B1222574524119N00555361EA0000001362
    +B1222594524121N00555342EA0000001362
    +B1223014524115N00555328EA0000001362
    +B1223034524108N00555320EA0000001363
    +B1223054524099N00555315EA0000001362
    +B1223074524088N00555314EA0000001362
    +B1223094524080N00555311EA0000001362
    +B1223114524074N00555304EA0000001364
    +B1223134524068N00555299EA0000001366
    +B1223154524061N00555298EA0000001367
    +B1223174524056N00555303EA0000001368
    +B1223194524058N00555312EA0000001369
    +B1223214524065N00555318EA0000001370
    +B1223234524076N00555318EA0000001370
    +B1223254524086N00555319EA0000001370
    +B1223274524095N00555325EA0000001370
    +B1223294524100N00555336EA0000001371
    +B1223314524097N00555346EA0000001371
    +B1223334524090N00555347EA0000001372
    +B1223354524083N00555339EA0000001374
    +B1223374524079N00555328EA0000001376
    +B1223394524079N00555315EA0000001378
    +B1223414524079N00555302EA0000001379
    +B1223434524075N00555292EA0000001379
    +B1223454524066N00555287EA0000001379
    +B1223474524062N00555294EA0000001380
    +B1223494524061N00555304EA0000001380
    +B1223514524062N00555315EA0000001379
    +B1223534524057N00555324EA0000001380
    +B1223554524048N00555326EA0000001382
    +B1223574524042N00555315EA0000001383
    +B1223594524044N00555298EA0000001384
    +B1224014524057N00555288EA0000001384
    +B1224034524071N00555292EA0000001384
    +B1224054524077N00555305EA0000001383
    +B1224074524072N00555316EA0000001385
    +B1224104524065N00555320EA0000001390
    +B1224124524060N00555326EA0000001392
    +B1224144524055N00555334EA0000001397
    +B1224164524049N00555339EA0000001402
    +B1224184524045N00555338EA0000001405
    +B1224204524040N00555331EA0000001407
    +B1224224524040N00555321EA0000001409
    +B1224244524047N00555310EA0000001411
    +B1224264524057N00555310EA0000001413
    +B1224284524062N00555320EA0000001414
    +B1224304524059N00555328EA0000001416
    +B1224324524054N00555330EA0000001419
    +B1224344524051N00555325EA0000001422
    +B1224364524051N00555315EA0000001424
    +B1224384524057N00555305EA0000001427
    +B1224404524067N00555300EA0000001428
    +B1224424524079N00555310EA0000001430
    +B1224444524083N00555325EA0000001431
    +B1224464524080N00555334EA0000001432
    +B1224484524073N00555333EA0000001433
    +B1224504524068N00555331EA0000001436
    +B1224524524063N00555329EA0000001438
    +B1224544524058N00555325EA0000001440
    +B1224564524055N00555316EA0000001442
    +B1224584524056N00555306EA0000001443
    +B1225004524062N00555295EA0000001444
    +B1225024524071N00555289EA0000001445
    +B1225044524081N00555296EA0000001446
    +B1225064524086N00555307EA0000001447
    +B1225084524082N00555316EA0000001447
    +B1225104524074N00555315EA0000001448
    +B1225124524066N00555313EA0000001448
    +B1225144524058N00555308EA0000001447
    +B1225164524050N00555299EA0000001446
    +B1225184524042N00555290EA0000001446
    +B1225204524034N00555285EA0000001447
    +B1225224524025N00555283EA0000001448
    +B1225244524010N00555282EA0000001447
    +B1225264523999N00555276EA0000001445
    +B1225284523989N00555270EA0000001442
    +B1225304523978N00555263EA0000001438
    +B1225324523967N00555254EA0000001434
    +B1225344523957N00555245EA0000001431
    +B1225364523948N00555237EA0000001428
    +B1225384523939N00555227EA0000001425
    +B1225404523928N00555217EA0000001421
    +B1225424523918N00555207EA0000001418
    +B1225454523909N00555197EA0000001417
    +B1225474523901N00555186EA0000001415
    +B1225494523891N00555173EA0000001412
    +B1225514523884N00555161EA0000001411
    +B1225534523877N00555150EA0000001410
    +B1225554523868N00555141EA0000001410
    +B1225574523858N00555133EA0000001411
    +B1225594523849N00555127EA0000001413
    +B1226014523840N00555122EA0000001416
    +B1226034523834N00555117EA0000001419
    +B1226054523827N00555112EA0000001421
    +B1226074523819N00555112EA0000001422
    +B1226094523814N00555120EA0000001423
    +B1226114523816N00555131EA0000001423
    +B1226134523823N00555138EA0000001424
    +B1226154523833N00555133EA0000001424
    +B1226174523841N00555118EA0000001424
    +B1226194523842N00555102EA0000001426
    +B1226214523839N00555089EA0000001427
    +B1226234523832N00555079EA0000001429
    +B1226254523825N00555071EA0000001431
    +B1226274523817N00555066EA0000001432
    +B1226294523810N00555064EA0000001435
    +B1226314523803N00555068EA0000001437
    +B1226334523799N00555076EA0000001440
    +B1226354523801N00555085EA0000001442
    +B1226374523808N00555091EA0000001443
    +B1226394523820N00555084EA0000001445
    +B1226414523823N00555067EA0000001445
    +B1226434523820N00555049EA0000001446
    +B1226454523814N00555038EA0000001448
    +B1226474523805N00555034EA0000001449
    +B1226494523796N00555032EA0000001452
    +B1226514523788N00555030EA0000001456
    +B1226534523780N00555029EA0000001459
    +B1226554523773N00555028EA0000001461
    +B1226574523767N00555032EA0000001463
    +B1226594523767N00555045EA0000001465
    +B1227014523775N00555053EA0000001466
    +B1227034523788N00555045EA0000001469
    +B1227054523794N00555029EA0000001471
    +B1227074523792N00555013EA0000001473
    +B1227094523784N00555007EA0000001475
    +B1227114523778N00555009EA0000001477
    +B1227134523775N00555018EA0000001479
    +B1227154523779N00555027EA0000001480
    +B1227174523787N00555032EA0000001482
    +B1227194523797N00555027EA0000001484
    +B1227214523802N00555012EA0000001485
    +B1227244523801N00554994EA0000001486
    +B1227264523794N00554983EA0000001487
    +B1227284523785N00554980EA0000001489
    +B1227304523776N00554978EA0000001492
    +B1227324523767N00554979EA0000001495
    +B1227344523760N00554987EA0000001499
    +B1227364523760N00554999EA0000001502
    +B1227384523766N00555012EA0000001505
    +B1227404523778N00555018EA0000001508
    +B1227424523789N00555010EA0000001511
    +B1227444523797N00554994EA0000001514
    +B1227464523795N00554979EA0000001517
    +B1227484523788N00554970EA0000001519
    +B1227504523780N00554964EA0000001522
    +B1227524523771N00554961EA0000001525
    +B1227544523764N00554956EA0000001529
    +B1227564523758N00554953EA0000001533
    +B1227584523751N00554959EA0000001536
    +B1228004523747N00554971EA0000001538
    +B1228024523750N00554984EA0000001540
    +B1228044523759N00554996EA0000001540
    +B1228064523771N00555000EA0000001541
    +B1228084523783N00554992EA0000001541
    +B1228104523790N00554977EA0000001543
    +B1228124523789N00554963EA0000001544
    +B1228144523783N00554956EA0000001547
    +B1228164523777N00554959EA0000001549
    +B1228184523772N00554967EA0000001550
    +B1228204523773N00554978EA0000001550
    +B1228224523779N00554990EA0000001551
    +B1228244523789N00554993EA0000001551
    +B1228264523797N00554985EA0000001551
    +B1228284523796N00554973EA0000001552
    +B1228304523791N00554963EA0000001555
    +B1228324523784N00554958EA0000001557
    +B1228344523778N00554958EA0000001558
    +B1228364523774N00554965EA0000001560
    +B1228384523776N00554976EA0000001562
    +B1228404523788N00554982EA0000001565
    +B1228424523798N00554980EA0000001568
    +B1228444523807N00554972EA0000001570
    +B1228464523812N00554958EA0000001573
    +B1228484523810N00554943EA0000001575
    +B1228504523803N00554935EA0000001576
    +B1228524523794N00554941EA0000001579
    +B1228544523791N00554953EA0000001582
    +B1228564523794N00554965EA0000001585
    +B1228584523804N00554972EA0000001588
    +B1229004523817N00554971EA0000001591
    +B1229034523827N00554962EA0000001594
    +B1229054523829N00554946EA0000001596
    +B1229074523826N00554938EA0000001598
    +B1229094523821N00554941EA0000001601
    +B1229114523820N00554952EA0000001603
    +B1229134523826N00554964EA0000001606
    +B1229154523834N00554971EA0000001609
    +B1229174523844N00554967EA0000001611
    +B1229194523849N00554955EA0000001613
    +B1229214523846N00554939EA0000001616
    +B1229234523838N00554938EA0000001619
    +B1229254523831N00554946EA0000001623
    +B1229274523825N00554959EA0000001626
    +B1229294523826N00554974EA0000001629
    +B1229314523832N00554982EA0000001632
    +B1229334523841N00554981EA0000001634
    +B1229354523846N00554969EA0000001637
    +B1229374523842N00554955EA0000001639
    +B1229394523835N00554948EA0000001642
    +B1229414523826N00554948EA0000001645
    +B1229434523820N00554954EA0000001647
    +B1229454523821N00554966EA0000001650
    +B1229474523829N00554975EA0000001652
    +B1229494523840N00554973EA0000001655
    +B1229514523849N00554960EA0000001657
    +B1229534523848N00554940EA0000001659
    +B1229554523842N00554929EA0000001661
    +B1229574523836N00554936EA0000001665
    +B1229594523834N00554946EA0000001670
    +B1230014523837N00554957EA0000001673
    +B1230034523845N00554967EA0000001677
    +B1230054523855N00554965EA0000001679
    +B1230074523864N00554952EA0000001682
    +B1230094523864N00554936EA0000001684
    +B1230114523858N00554927EA0000001686
    +B1230134523850N00554932EA0000001689
    +B1230154523848N00554937EA0000001694
    +B1230174523854N00554961EA0000001698
    +B1230194523865N00554966EA0000001700
    +B1230214523877N00554959EA0000001702
    +B1230234523883N00554946EA0000001703
    +B1230254523881N00554933EA0000001704
    +B1230274523874N00554926EA0000001707
    +B1230294523867N00554920EA0000001711
    +B1230314523860N00554915EA0000001716
    +B1230334523851N00554915EA0000001720
    +B1230354523843N00554921EA0000001724
    +B1230374523841N00554931EA0000001729
    +B1230394523843N00554942EA0000001732
    +B1230424523850N00554952EA0000001734
    +B1230444523861N00554954EA0000001734
    +B1230464523871N00554944EA0000001735
    +B1230484523876N00554929EA0000001735
    +B1230504523871N00554913EA0000001736
    +B1230524523864N00554906EA0000001738
    +B1230544523856N00554900EA0000001739
    +B1230564523847N00554893EA0000001741
    +B1230584523837N00554890EA0000001744
    +B1231004523830N00554888EA0000001749
    +B1231024523823N00554883EA0000001753
    +B1231044523816N00554881EA0000001757
    +B1231064523810N00554887EA0000001759
    +B1231084523806N00554899EA0000001762
    +B1231104523809N00554911EA0000001763
    +B1231124523818N00554920EA0000001764
    +B1231144523828N00554920EA0000001765
    +B1231164523832N00554907EA0000001766
    +B1231184523830N00554893EA0000001769
    +B1231204523823N00554888EA0000001772
    +B1231224523815N00554891EA0000001775
    +B1231244523809N00554900EA0000001776
    +B1231264523806N00554912EA0000001777
    +B1231284523809N00554924EA0000001776
    +B1231304523817N00554932EA0000001774
    +B1231324523827N00554930EA0000001773
    +B1231344523834N00554922EA0000001771
    +B1231364523834N00554908EA0000001769
    +B1231384523831N00554896EA0000001768
    +B1231404523824N00554889EA0000001769
    +B1231424523817N00554881EA0000001771
    +B1231444523811N00554870EA0000001773
    +B1231464523806N00554860EA0000001777
    +B1231484523799N00554856EA0000001782
    +B1231504523791N00554851EA0000001787
    +B1231524523785N00554839EA0000001791
    +B1231544523786N00554823EA0000001795
    +B1231564523793N00554811EA0000001801
    +B1231584523799N00554821EA0000001805
    +B1232004523801N00554835EA0000001810
    +B1232024523803N00554846EA0000001814
    +B1232044523802N00554857EA0000001817
    +B1232064523793N00554863EA0000001819
    +B1232084523787N00554856EA0000001820
    +B1232104523785N00554844EA0000001821
    +B1232124523794N00554841EA0000001822
    +B1232144523803N00554849EA0000001824
    +B1232164523808N00554862EA0000001827
    +B1232194523805N00554874EA0000001829
    +B1232214523796N00554876EA0000001831
    +B1232234523791N00554864EA0000001833
    +B1232254523793N00554850EA0000001835
    +B1232274523800N00554846EA0000001838
    +B1232294523811N00554851EA0000001840
    +B1232314523818N00554860EA0000001844
    +B1232334523819N00554872EA0000001846
    +B1232354523812N00554879EA0000001848
    +B1232374523804N00554875EA0000001850
    +B1232394523802N00554863EA0000001851
    +B1232414523802N00554863EA0000001854
    +B1232434523819N00554859EA0000001856
    +B1232454523823N00554870EA0000001858
    +B1232474523819N00554883EA0000001860
    +B1232494523812N00554887EA0000001862
    +B1232514523805N00554878EA0000001863
    +B1232534523806N00554864EA0000001864
    +B1232554523811N00554857EA0000001866
    +B1232574523817N00554864EA0000001868
    +B1232594523815N00554876EA0000001870
    +B1233014523806N00554881EA0000001871
    +B1233034523799N00554874EA0000001872
    +B1233054523800N00554863EA0000001872
    +B1233074523809N00554856EA0000001875
    +B1233094523816N00554850EA0000001879
    +B1233114523823N00554844EA0000001882
    +B1233134523832N00554843EA0000001884
    +B1233154523841N00554850EA0000001886
    +B1233174523842N00554863EA0000001888
    +B1233194523836N00554873EA0000001889
    +B1233214523827N00554869EA0000001891
    +B1233234523820N00554860EA0000001894
    +B1233254523815N00554847EA0000001896
    +B1233274523816N00554834EA0000001898
    +B1233294523822N00554826EA0000001900
    +B1233314523829N00554828EA0000001902
    +B1233334523830N00554848EA0000001905
    +B1233354523821N00554858EA0000001907
    +B1233374523812N00554855EA0000001909
    +B1233394523805N00554846EA0000001913
    +B1233414523796N00554832EA0000001915
    +B1233434523802N00554821EA0000001919
    +B1233454523814N00554822EA0000001922
    +B1233474523821N00554839EA0000001925
    +B1233494523817N00554859EA0000001929
    +B1233514523804N00554864EA0000001931
    +B1233534523794N00554851EA0000001933
    +B1233564523794N00554837EA0000001936
    +B1233584523806N00554833EA0000001941
    +B1234004523817N00554839EA0000001946
    +B1234024523823N00554854EA0000001951
    +B1234044523816N00554867EA0000001954
    +B1234064523803N00554868EA0000001957
    +B1234084523796N00554857EA0000001959
    +B1234104523800N00554842EA0000001961
    +B1234124523809N00554837EA0000001966
    +B1234144523818N00554845EA0000001970
    +B1234164523823N00554863EA0000001974
    +B1234184523818N00554877EA0000001977
    +B1234204523807N00554881EA0000001979
    +B1234224523807N00554881EA0000001981
    +B1234244523798N00554856EA0000001983
    +B1234264523807N00554849EA0000001986
    +B1234284523816N00554859EA0000001988
    +B1234304523819N00554881EA0000001993
    +B1234324523813N00554892EA0000001996
    +B1234344523804N00554892EA0000001997
    +B1234364523795N00554888EA0000001996
    +B1234384523788N00554880EA0000001999
    +B1234404523785N00554868EA0000002001
    +B1234424523786N00554855EA0000002004
    +B1234444523787N00554844EA0000002009
    +B1234464523784N00554836EA0000002015
    +B1234484523778N00554829EA0000002019
    +B1234504523770N00554827EA0000002023
    +B1234524523761N00554833EA0000002029
    +B1234544523756N00554845EA0000002033
    +B1234564523753N00554862EA0000002038
    +B1234584523754N00554879EA0000002043
    +B1235004523761N00554892EA0000002046
    +B1235024523771N00554896EA0000002051
    +B1235044523779N00554889EA0000002056
    +B1235064523782N00554877EA0000002060
    +B1235084523780N00554866EA0000002065
    +B1235104523776N00554858EA0000002073
    +B1235124523765N00554850EA0000002079
    +B1235144523756N00554848EA0000002085
    +B1235164523749N00554857EA0000002090
    +B1235184523745N00554870EA0000002095
    +B1235204523741N00554885EA0000002099
    +B1235224523743N00554902EA0000002102
    +B1235244523752N00554915EA0000002106
    +B1235264523764N00554914EA0000002109
    +B1235284523771N00554906EA0000002113
    +B1235304523775N00554894EA0000002120
    +B1235324523777N00554884EA0000002127
    +B1235354523776N00554875EA0000002133
    +B1235374523773N00554866EA0000002138
    +B1235394523766N00554858EA0000002142
    +B1235414523757N00554853EA0000002147
    +B1235434523748N00554857EA0000002152
    +B1235454523741N00554867EA0000002157
    +B1235474523739N00554882EA0000002161
    +B1235494523742N00554898EA0000002164
    +B1235514523750N00554909EA0000002168
    +B1235534523759N00554910EA0000002172
    +B1235554523766N00554904EA0000002176
    +B1235574523767N00554894EA0000002182
    +B1235594523765N00554884EA0000002186
    +B1236014523762N00554875EA0000002191
    +B1236034523758N00554866EA0000002196
    +B1236054523749N00554860EA0000002201
    +B1236074523740N00554863EA0000002206
    +B1236094523732N00554873EA0000002212
    +B1236114523727N00554886EA0000002218
    +B1236134523727N00554901EA0000002223
    +B1236154523732N00554916EA0000002228
    +B1236174523741N00554925EA0000002232
    +B1236194523751N00554926EA0000002237
    +B1236214523757N00554920EA0000002241
    +B1236234523759N00554911EA0000002244
    +B1236254523759N00554901EA0000002246
    +B1236274523754N00554891EA0000002250
    +B1236294523747N00554884EA0000002254
    +B1236314523739N00554882EA0000002258
    +B1236334523729N00554884EA0000002263
    +B1236354523720N00554884EA0000002268
    +B1236374523711N00554880EA0000002271
    +B1236394523703N00554877EA0000002274
    +B1236414523695N00554874EA0000002277
    +B1236434523687N00554871EA0000002278
    +B1236454523678N00554865EA0000002278
    +B1236474523666N00554859EA0000002279
    +B1236494523655N00554854EA0000002281
    +B1236514523638N00554848EA0000002281
    +B1236534523626N00554843EA0000002283
    +B1236554523615N00554838EA0000002286
    +B1236574523606N00554834EA0000002288
    +B1236594523596N00554830EA0000002288
    +B1237014523585N00554825EA0000002290
    +B1237034523574N00554820EA0000002292
    +B1237054523563N00554817EA0000002295
    +B1237074523552N00554812EA0000002296
    +B1237094523541N00554807EA0000002298
    +B1237114523530N00554803EA0000002300
    +B1237144523518N00554802EA0000002300
    +B1237164523504N00554800EA0000002301
    +B1237184523492N00554797EA0000002302
    +B1237204523482N00554792EA0000002302
    +B1237224523471N00554789EA0000002300
    +B1237244523458N00554784EA0000002298
    +B1237264523446N00554779EA0000002298
    +B1237284523434N00554775EA0000002297
    +B1237304523423N00554769EA0000002296
    +B1237324523410N00554762EA0000002293
    +B1237344523396N00554756EA0000002291
    +B1237364523383N00554748EA0000002290
    +B1237384523371N00554740EA0000002289
    +B1237404523357N00554732EA0000002288
    +B1237424523343N00554724EA0000002288
    +B1237444523331N00554716EA0000002288
    +B1237464523318N00554710EA0000002287
    +B1237484523305N00554702EA0000002285
    +B1237504523294N00554695EA0000002285
    +B1237524523285N00554690EA0000002286
    +B1237544523275N00554684EA0000002285
    +B1237564523265N00554677EA0000002286
    +B1237584523255N00554671EA0000002287
    +B1238004523245N00554668EA0000002287
    +B1238024523234N00554662EA0000002286
    +B1238044523222N00554656EA0000002285
    +B1238064523210N00554651EA0000002284
    +B1238084523198N00554644EA0000002282
    +B1238104523184N00554636EA0000002281
    +B1238124523170N00554630EA0000002278
    +B1238144523156N00554623EA0000002274
    +B1238164523140N00554614EA0000002271
    +B1238184523125N00554611EA0000002269
    +B1238204523111N00554605EA0000002265
    +B1238224523096N00554596EA0000002263
    +B1238244523082N00554589EA0000002260
    +B1238264523069N00554582EA0000002256
    +B1238284523055N00554574EA0000002251
    +B1238304523035N00554564EA0000002245
    +B1238324523022N00554559EA0000002243
    +B1238344523008N00554553EA0000002239
    +B1238364522992N00554544EA0000002234
    +B1238384522977N00554534EA0000002231
    +B1238404522963N00554527EA0000002227
    +B1238424522948N00554521EA0000002222
    +B1238444522932N00554513EA0000002215
    +B1238464522915N00554506EA0000002210
    +B1238484522899N00554498EA0000002206
    +B1238504522884N00554489EA0000002204
    +B1238534522868N00554480EA0000002203
    +B1238554522853N00554471EA0000002203
    +B1238574522838N00554463EA0000002205
    +B1238594522827N00554457EA0000002208
    +B1239014522819N00554451EA0000002210
    +B1239034522811N00554446EA0000002210
    +B1239054522801N00554440EA0000002210
    +B1239074522790N00554434EA0000002211
    +B1239094522778N00554429EA0000002213
    +B1239114522765N00554425EA0000002216
    +B1239134522752N00554420EA0000002219
    +B1239154522741N00554415EA0000002223
    +B1239174522731N00554409EA0000002226
    +B1239194522731N00554409EA0000002228
    +B1239214522707N00554396EA0000002229
    +B1239234522695N00554390EA0000002231
    +B1239254522683N00554385EA0000002233
    +B1239274522670N00554380EA0000002234
    +B1239294522657N00554376EA0000002234
    +B1239314522643N00554371EA0000002233
    +B1239334522630N00554368EA0000002232
    +B1239354522618N00554362EA0000002231
    +B1239374522606N00554357EA0000002229
    +B1239394522592N00554349EA0000002226
    +B1239414522580N00554342EA0000002225
    +B1239434522569N00554335EA0000002224
    +B1239454522556N00554330EA0000002223
    +B1239474522542N00554326EA0000002221
    +B1239494522527N00554322EA0000002221
    +B1239514522514N00554320EA0000002221
    +B1239534522500N00554318EA0000002220
    +B1239554522485N00554315EA0000002219
    +B1239574522470N00554310EA0000002219
    +B1239594522457N00554306EA0000002220
    +B1240014522444N00554302EA0000002220
    +B1240034522429N00554295EA0000002219
    +B1240054522416N00554288EA0000002220
    +B1240074522404N00554281EA0000002221
    +B1240094522389N00554274EA0000002222
    +B1240114522370N00554263EA0000002226
    +B1240134522357N00554258EA0000002228
    +B1240154522344N00554255EA0000002228
    +B1240174522332N00554251EA0000002227
    +B1240194522319N00554247EA0000002226
    +B1240214522306N00554244EA0000002226
    +B1240234522293N00554239EA0000002227
    +B1240254522282N00554235EA0000002231
    +B1240274522269N00554231EA0000002234
    +B1240294522258N00554227EA0000002239
    +B1240314522249N00554223EA0000002243
    +B1240344522240N00554218EA0000002246
    +B1240364522231N00554212EA0000002247
    +B1240384522222N00554206EA0000002249
    +B1240404522214N00554199EA0000002251
    +B1240424522204N00554193EA0000002253
    +B1240444522194N00554186EA0000002255
    +B1240464522185N00554182EA0000002256
    +B1240484522176N00554177EA0000002256
    +B1240504522165N00554171EA0000002254
    +B1240524522152N00554164EA0000002251
    +B1240544522139N00554156EA0000002250
    +B1240564522133N00554153EA0000002247
    +B1240584522115N00554148EA0000002243
    +B1241004522115N00554148EA0000002237
    +B1241024522090N00554137EA0000002231
    +B1241044522076N00554133EA0000002227
    +B1241064522063N00554128EA0000002223
    +B1241084522051N00554123EA0000002219
    +B1241104522037N00554118EA0000002214
    +B1241124522023N00554108EA0000002210
    +B1241144522011N00554102EA0000002208
    +B1241164522005N00554105EA0000002207
    +B1241184522002N00554104EA0000002209
    +B1241204521995N00554100EA0000002210
    +B1241224521986N00554095EA0000002213
    +B1241244521979N00554089EA0000002218
    +B1241264521972N00554084EA0000002220
    +B1241284521963N00554078EA0000002222
    +B1241304521953N00554072EA0000002225
    +B1241324521945N00554069EA0000002228
    +B1241344521935N00554063EA0000002230
    +B1241364521927N00554056EA0000002231
    +B1241384521917N00554050EA0000002231
    +B1241404521905N00554041EA0000002230
    +B1241424521895N00554033EA0000002231
    +B1241444521887N00554028EA0000002232
    +B1241464521876N00554022EA0000002232
    +B1241484521864N00554012EA0000002233
    +B1241504521851N00553999EA0000002241
    +B1241524521845N00553992EA0000002249
    +B1241544521838N00553983EA0000002258
    +B1241564521833N00553975EA0000002265
    +B1241584521829N00553966EA0000002271
    +B1242004521823N00553957EA0000002278
    +B1242024521818N00553949EA0000002284
    +B1242044521812N00553940EA0000002291
    +B1242064521808N00553934EA0000002297
    +B1242084521802N00553928EA0000002301
    +B1242104521796N00553919EA0000002304
    +B1242124521789N00553911EA0000002306
    +B1242154521782N00553900EA0000002308
    +B1242174521775N00553887EA0000002310
    +B1242194521767N00553875EA0000002314
    +B1242214521761N00553865EA0000002319
    +B1242234521754N00553856EA0000002322
    +B1242254521745N00553844EA0000002327
    +B1242274521738N00553836EA0000002334
    +B1242294521732N00553832EA0000002339
    +B1242314521723N00553828EA0000002342
    +B1242334521718N00553825EA0000002345
    +B1242354521704N00553819EA0000002348
    +B1242374521695N00553814EA0000002349
    +B1242394521685N00553806EA0000002350
    +B1242414521672N00553796EA0000002348
    +B1242434521658N00553785EA0000002348
    +B1242454521645N00553775EA0000002350
    +B1242474521635N00553768EA0000002352
    +B1242494521624N00553760EA0000002352
    +B1242514521612N00553750EA0000002354
    +B1242534521601N00553741EA0000002357
    +B1242554521591N00553731EA0000002360
    +B1242574521582N00553719EA0000002365
    +B1242594521573N00553709EA0000002370
    +B1243014521563N00553699EA0000002377
    +B1243034521552N00553687EA0000002384
    +B1243054521544N00553682EA0000002393
    +B1243074521538N00553686EA0000002403
    +B1243094521532N00553689EA0000002409
    +B1243114521524N00553684EA0000002414
    +B1243134521517N00553675EA0000002419
    +B1243154521510N00553667EA0000002422
    +B1243174521501N00553662EA0000002424
    +B1243194521489N00553654EA0000002424
    +B1243214521477N00553643EA0000002426
    +B1243234521466N00553632EA0000002425
    +B1243254521454N00553620EA0000002423
    +B1243274521441N00553606EA0000002423
    +B1243294521428N00553595EA0000002425
    +B1243314521410N00553581EA0000002426
    +B1243334521397N00553573EA0000002424
    +B1243354521381N00553564EA0000002424
    +B1243374521368N00553558EA0000002426
    +B1243394521359N00553553EA0000002428
    +B1243414521349N00553546EA0000002427
    +B1243434521336N00553537EA0000002425
    +B1243454521326N00553531EA0000002424
    +B1243474521318N00553526EA0000002421
    +B1243494521309N00553522EA0000002418
    +B1243514521298N00553515EA0000002411
    +B1243544521287N00553507EA0000002403
    +B1243564521274N00553501EA0000002397
    +B1243584521262N00553493EA0000002393
    +B1244004521251N00553484EA0000002389
    +B1244024521239N00553476EA0000002385
    +B1244044521228N00553466EA0000002381
    +B1244064521216N00553456EA0000002377
    +B1244084521204N00553447EA0000002374
    +B1244104521193N00553439EA0000002371
    +B1244124521183N00553428EA0000002366
    +B1244144521170N00553418EA0000002360
    +B1244164521157N00553407EA0000002356
    +B1244184521144N00553399EA0000002353
    +B1244204521132N00553393EA0000002349
    +B1244224521121N00553386EA0000002344
    +B1244244521108N00553379EA0000002338
    +B1244264521095N00553372EA0000002333
    +B1244284521084N00553365EA0000002328
    +B1244304521072N00553357EA0000002321
    +B1244324521060N00553348EA0000002316
    +B1244344521048N00553340EA0000002311
    +B1244364521036N00553331EA0000002307
    +B1244384521025N00553322EA0000002301
    +B1244404521013N00553314EA0000002296
    +B1244424521001N00553305EA0000002292
    +B1244444520989N00553297EA0000002288
    +B1244464520978N00553289EA0000002284
    +B1244484520965N00553282EA0000002280
    +B1244504520952N00553274EA0000002277
    +B1244524520940N00553266EA0000002273
    +B1244544520928N00553258EA0000002271
    +B1244564520915N00553250EA0000002269
    +B1244584520904N00553242EA0000002268
    +B1245004520894N00553233EA0000002266
    +B1245024520882N00553221EA0000002264
    +B1245044520870N00553208EA0000002264
    +B1245064520859N00553195EA0000002264
    +B1245084520848N00553184EA0000002263
    +B1245104520831N00553169EA0000002260
    +B1245124520819N00553160EA0000002257
    +B1245144520807N00553153EA0000002255
    +B1245164520794N00553149EA0000002253
    +B1245184520783N00553144EA0000002250
    +B1245204520772N00553138EA0000002247
    +B1245224520762N00553130EA0000002242
    +B1245244520751N00553122EA0000002237
    +B1245264520741N00553116EA0000002233
    +B1245284520730N00553107EA0000002228
    +B1245304520720N00553096EA0000002223
    +B1245324520709N00553086EA0000002219
    +B1245354520699N00553075EA0000002215
    +B1245374520688N00553063EA0000002211
    +B1245394520677N00553054EA0000002208
    +B1245414520666N00553045EA0000002205
    +B1245434520656N00553034EA0000002201
    +B1245454520643N00553025EA0000002195
    +B1245474520630N00553018EA0000002192
    +B1245494520620N00553010EA0000002189
    +B1245514520612N00553001EA0000002186
    +B1245534520602N00552993EA0000002182
    +B1245554520591N00552985EA0000002180
    +B1245574520581N00552978EA0000002178
    +B1245594520581N00552978EA0000002175
    +B1246014520560N00552961EA0000002173
    +B1246034520548N00552953EA0000002173
    +B1246054520536N00552945EA0000002173
    +B1246074520525N00552938EA0000002175
    +B1246094520512N00552932EA0000002177
    +B1246114520503N00552926EA0000002181
    +B1246134520499N00552919EA0000002184
    +B1246154520492N00552913EA0000002187
    +B1246174520484N00552905EA0000002191
    +B1246194520478N00552897EA0000002197
    +B1246214520472N00552890EA0000002204
    +B1246234520464N00552882EA0000002208
    +B1246254520452N00552872EA0000002212
    +B1246274520440N00552864EA0000002218
    +B1246294520430N00552859EA0000002223
    +B1246314520419N00552851EA0000002225
    +B1246334520405N00552842EA0000002225
    +B1246354520394N00552831EA0000002223
    +B1246374520386N00552819EA0000002222
    +B1246394520377N00552811EA0000002220
    +B1246414520367N00552803EA0000002217
    +B1246434520357N00552792EA0000002215
    +B1246454520345N00552784EA0000002214
    +B1246474520333N00552777EA0000002214
    +B1246494520326N00552774EA0000002216
    +B1246514520300N00552760EA0000002222
    +B1246534520288N00552755EA0000002228
    +B1246554520277N00552752EA0000002235
    +B1246574520269N00552744EA0000002245
    +B1246594520260N00552735EA0000002256
    +B1247014520251N00552726EA0000002267
    +B1247034520242N00552718EA0000002279
    +B1247054520236N00552712EA0000002291
    +B1247074520228N00552706EA0000002301
    +B1247094520219N00552699EA0000002309
    +B1247114520210N00552689EA0000002316
    +B1247144520201N00552681EA0000002322
    +B1247164520192N00552673EA0000002327
    +B1247184520181N00552666EA0000002330
    +B1247204520169N00552659EA0000002330
    +B1247224520158N00552653EA0000002331
    +B1247244520151N00552648EA0000002333
    +B1247264520146N00552641EA0000002334
    +B1247284520137N00552636EA0000002333
    +B1247304520128N00552628EA0000002332
    +B1247324520120N00552620EA0000002333
    +B1247344520111N00552614EA0000002332
    +B1247364520101N00552608EA0000002330
    +B1247384520090N00552600EA0000002326
    +B1247404520079N00552592EA0000002321
    +B1247424520067N00552583EA0000002315
    +B1247444520055N00552573EA0000002309
    +B1247464520047N00552565EA0000002305
    +B1247484520040N00552558EA0000002301
    +B1247504520029N00552549EA0000002297
    +B1247524520018N00552538EA0000002294
    +B1247544520009N00552528EA0000002293
    +B1247564520001N00552516EA0000002295
    +B1247584519993N00552504EA0000002299
    +B1248004519986N00552492EA0000002306
    +B1248024519980N00552478EA0000002310
    +B1248044519973N00552466EA0000002316
    +B1248064519964N00552455EA0000002321
    +B1248084519955N00552445EA0000002327
    +B1248104519945N00552433EA0000002331
    +B1248124519934N00552422EA0000002337
    +B1248144519923N00552411EA0000002345
    +B1248164519915N00552405EA0000002353
    +B1248184519907N00552398EA0000002359
    +B1248204519897N00552390EA0000002364
    +B1248224519886N00552382EA0000002372
    +B1248244519879N00552373EA0000002381
    +B1248264519873N00552365EA0000002389
    +B1248284519866N00552358EA0000002396
    +B1248304519851N00552349EA0000002407
    +B1248324519843N00552343EA0000002414
    +B1248344519832N00552336EA0000002420
    +B1248364519819N00552332EA0000002424
    +B1248384519807N00552328EA0000002429
    +B1248404519798N00552324EA0000002433
    +B1248424519787N00552321EA0000002436
    +B1248444519776N00552317EA0000002436
    +B1248464519765N00552309EA0000002433
    +B1248484519756N00552299EA0000002429
    +B1248504519748N00552294EA0000002424
    +B1248524519738N00552291EA0000002417
    +B1248554519726N00552285EA0000002411
    +B1248574519716N00552279EA0000002407
    +B1248594519707N00552275EA0000002402
    +B1249014519696N00552268EA0000002395
    +B1249034519682N00552262EA0000002391
    +B1249054519671N00552257EA0000002388
    +B1249074519664N00552255EA0000002385
    +B1249094519653N00552251EA0000002380
    +B1249114519641N00552243EA0000002377
    +B1249134519629N00552236EA0000002375
    +B1249154519618N00552229EA0000002370
    +B1249174519605N00552220EA0000002367
    +B1249194519594N00552212EA0000002365
    +B1249214519583N00552205EA0000002364
    +B1249234519571N00552197EA0000002360
    +B1249254519558N00552189EA0000002357
    +B1249274519545N00552181EA0000002355
    +B1249294519534N00552174EA0000002354
    +B1249314519523N00552166EA0000002351
    +B1249334519512N00552158EA0000002349
    +B1249354519500N00552150EA0000002348
    +B1249374519489N00552142EA0000002345
    +B1249394519478N00552134EA0000002342
    +B1249414519466N00552125EA0000002337
    +B1249434519455N00552116EA0000002334
    +B1249454519444N00552105EA0000002331
    +B1249474519433N00552095EA0000002328
    +B1249494519421N00552085EA0000002324
    +B1249514519408N00552076EA0000002322
    +B1249534519396N00552069EA0000002319
    +B1249554519383N00552063EA0000002317
    +B1249574519369N00552056EA0000002314
    +B1249594519356N00552051EA0000002312
    +B1250014519343N00552046EA0000002308
    +B1250034519330N00552040EA0000002305
    +B1250054519316N00552033EA0000002302
    +B1250074519303N00552026EA0000002300
    +B1250094519291N00552017EA0000002297
    +B1250114519273N00552004EA0000002292
    +B1250134519260N00551996EA0000002288
    +B1250154519248N00551988EA0000002283
    +B1250174519234N00551980EA0000002279
    +B1250194519221N00551970EA0000002275
    +B1250214519208N00551962EA0000002274
    +B1250234519196N00551954EA0000002272
    +B1250254519183N00551944EA0000002270
    +B1250274519171N00551934EA0000002267
    +B1250294519158N00551923EA0000002263
    +B1250314519143N00551911EA0000002260
    +B1250334519130N00551899EA0000002258
    +B1250364519118N00551890EA0000002258
    +B1250384519105N00551879EA0000002258
    +B1250404519093N00551867EA0000002259
    +B1250424519082N00551854EA0000002261
    +B1250444519070N00551843EA0000002261
    +B1250464519056N00551832EA0000002261
    +B1250484519042N00551823EA0000002262
    +B1250504519029N00551813EA0000002263
    +B1250524519015N00551802EA0000002263
    +B1250544519002N00551791EA0000002265
    +B1250564518990N00551779EA0000002268
    +B1250584518980N00551766EA0000002271
    +B1251004518971N00551754EA0000002277
    +B1251024518962N00551742EA0000002281
    +B1251044518953N00551731EA0000002287
    +B1251064518945N00551722EA0000002293
    +B1251084518937N00551715EA0000002299
    +B1251104518928N00551708EA0000002304
    +B1251124518919N00551700EA0000002310
    +B1251144518911N00551693EA0000002317
    +B1251164518904N00551684EA0000002323
    +B1251184518897N00551675EA0000002328
    +B1251204518889N00551667EA0000002334
    +B1251224518882N00551659EA0000002339
    +B1251244518874N00551649EA0000002342
    +B1251264518864N00551639EA0000002346
    +B1251284518854N00551629EA0000002350
    +B1251304518845N00551618EA0000002353
    +B1251324518835N00551608EA0000002356
    +B1251344518825N00551598EA0000002361
    +B1251364518816N00551592EA0000002367
    +B1251384518811N00551590EA0000002371
    +B1251404518802N00551586EA0000002373
    +B1251424518793N00551580EA0000002374
    +B1251444518783N00551573EA0000002374
    +B1251464518771N00551566EA0000002371
    +B1251484518761N00551561EA0000002371
    +B1251504518752N00551558EA0000002368
    +B1251524518739N00551551EA0000002363
    +B1251544518730N00551546EA0000002362
    +B1251564518723N00551539EA0000002360
    +B1251584518716N00551533EA0000002356
    +B1252004518706N00551525EA0000002353
    +B1252024518696N00551518EA0000002351
    +B1252044518688N00551513EA0000002348
    +B1252064518679N00551507EA0000002344
    +B1252084518667N00551499EA0000002338
    +B1252104518655N00551489EA0000002335
    +B1252124518644N00551479EA0000002334
    +B1252144518634N00551470EA0000002334
    +B1252174518623N00551465EA0000002335
    +B1252194518614N00551462EA0000002334
    +B1252214518603N00551461EA0000002331
    +B1252234518591N00551459EA0000002329
    +B1252254518580N00551457EA0000002329
    +B1252274518569N00551455EA0000002330
    +B1252294518558N00551451EA0000002332
    +B1252314518550N00551448EA0000002337
    +B1252334518544N00551448EA0000002340
    +B1252354518535N00551446EA0000002345
    +B1252374518528N00551444EA0000002351
    +B1252394518528N00551444EA0000002356
    +B1252414518515N00551451EA0000002362
    +B1252434518509N00551452EA0000002369
    +B1252454518506N00551444EA0000002374
    +B1252474518510N00551434EA0000002380
    +B1252494518522N00551432EA0000002386
    +B1252514518534N00551441EA0000002391
    +B1252534518537N00551460EA0000002397
    +B1252554518531N00551476EA0000002402
    +B1252574518522N00551480EA0000002407
    +B1252594518515N00551469EA0000002412
    +B1253014518515N00551454EA0000002418
    +B1253034518525N00551447EA0000002423
    +B1253054518536N00551456EA0000002429
    +B1253074518542N00551472EA0000002435
    +B1253094518538N00551489EA0000002439
    +B1253114518528N00551497EA0000002443
    +B1253134518520N00551491EA0000002447
    +B1253154518518N00551480EA0000002451
    +B1253174518528N00551473EA0000002457
    +B1253194518539N00551479EA0000002461
    +B1253214518544N00551494EA0000002466
    +B1253234518540N00551510EA0000002469
    +B1253254518532N00551516EA0000002473
    +B1253274518523N00551509EA0000002476
    +B1253294518513N00551499EA0000002477
    +B1253314518497N00551479EA0000002477
    +B1253334518488N00551462EA0000002476
    +B1253354518476N00551443EA0000002467
    +B1253374518465N00551433EA0000002464
    +B1253394518456N00551421EA0000002460
    +B1253414518447N00551410EA0000002456
    +B1253434518436N00551401EA0000002451
    +B1253454518426N00551389EA0000002448
    +B1253474518418N00551378EA0000002446
    +B1253494518410N00551367EA0000002443
    +B1253514518402N00551356EA0000002441
    +B1253544518393N00551343EA0000002439
    +B1253564518385N00551332EA0000002438
    +B1253584518377N00551321EA0000002439
    +B1254004518369N00551308EA0000002438
    +B1254024518360N00551295EA0000002438
    +B1254044518351N00551281EA0000002439
    +B1254064518344N00551267EA0000002439
    +B1254084518336N00551253EA0000002438
    +B1254104518325N00551239EA0000002438
    +B1254124518315N00551225EA0000002438
    +B1254144518306N00551212EA0000002437
    +B1254164518295N00551198EA0000002436
    +B1254184518285N00551184EA0000002436
    +B1254204518275N00551171EA0000002435
    +B1254224518266N00551158EA0000002433
    +B1254244518256N00551143EA0000002431
    +B1254264518246N00551129EA0000002429
    +B1254284518236N00551115EA0000002429
    +B1254304518229N00551101EA0000002427
    +B1254324518221N00551087EA0000002425
    +B1254344518212N00551074EA0000002424
    +B1254364518205N00551060EA0000002423
    +B1254384518198N00551046EA0000002422
    +B1254404518191N00551033EA0000002421
    +B1254424518183N00551020EA0000002420
    +B1254444518177N00551006EA0000002419
    +B1254464518169N00550993EA0000002417
    +B1254484518161N00550978EA0000002413
    +B1254504518151N00550962EA0000002410
    +B1254524518142N00550948EA0000002408
    +B1254544518133N00550935EA0000002404
    +B1254564518123N00550920EA0000002399
    +B1254584518112N00550905EA0000002395
    +B1255004518102N00550890EA0000002392
    +B1255024518091N00550877EA0000002388
    +B1255044518081N00550864EA0000002384
    +B1255064518071N00550850EA0000002379
    +B1255084518059N00550835EA0000002372
    +B1255104518043N00550813EA0000002365
    +B1255124518035N00550797EA0000002362
    +B1255144518029N00550783EA0000002360
    +B1255164518027N00550771EA0000002358
    +B1255184518023N00550758EA0000002354
    +B1255204518017N00550743EA0000002351
    +B1255224518011N00550729EA0000002350
    +B1255244518005N00550716EA0000002348
    +B1255264517999N00550702EA0000002344
    +B1255284517992N00550687EA0000002342
    +B1255304517986N00550673EA0000002340
    +B1255324517981N00550660EA0000002337
    +B1255354517975N00550647EA0000002333
    +B1255374517968N00550632EA0000002328
    +B1255394517961N00550616EA0000002322
    +B1255414517953N00550599EA0000002318
    +B1255434517946N00550583EA0000002314
    +B1255454517939N00550567EA0000002309
    +B1255474517932N00550549EA0000002303
    +B1255494517924N00550531EA0000002298
    +B1255514517917N00550514EA0000002295
    +B1255534517910N00550497EA0000002290
    +B1255554517904N00550481EA0000002285
    +B1255574517895N00550467EA0000002282
    +B1255594517889N00550456EA0000002280
    +B1256014517884N00550448EA0000002275
    +B1256034517877N00550437EA0000002270
    +B1256054517870N00550422EA0000002267
    +B1256074517866N00550412EA0000002264
    +B1256094517862N00550402EA0000002258
    +B1256114517856N00550386EA0000002253
    +B1256134517852N00550369EA0000002250
    +B1256154517850N00550358EA0000002248
    +B1256174517847N00550349EA0000002243
    +B1256194517841N00550335EA0000002238
    +B1256214517832N00550322EA0000002236
    +B1256234517824N00550312EA0000002232
    +B1256254517815N00550302EA0000002227
    +B1256274517806N00550290EA0000002223
    +B1256294517798N00550279EA0000002220
    +B1256314517790N00550270EA0000002218
    +B1256334517780N00550260EA0000002216
    +B1256354517769N00550249EA0000002213
    +B1256374517759N00550237EA0000002210
    +B1256394517750N00550226EA0000002209
    +B1256414517741N00550215EA0000002206
    +B1256434517729N00550203EA0000002203
    +B1256454517720N00550191EA0000002199
    +B1256474517711N00550179EA0000002197
    +B1256494517702N00550167EA0000002193
    +B1256514517685N00550148EA0000002185
    +B1256534517673N00550135EA0000002181
    +B1256554517662N00550123EA0000002177
    +B1256574517650N00550111EA0000002172
    +B1256594517638N00550097EA0000002167
    +B1257014517627N00550083EA0000002164
    +B1257034517616N00550070EA0000002161
    +B1257054517604N00550059EA0000002157
    +B1257074517592N00550047EA0000002153
    +B1257094517580N00550033EA0000002148
    +B1257114517567N00550020EA0000002144
    +B1257134517556N00550006EA0000002141
    +B1257164517544N00549992EA0000002137
    +B1257184517531N00549978EA0000002133
    +B1257204517520N00549966EA0000002130
    +B1257224517508N00549953EA0000002127
    +B1257244517496N00549941EA0000002123
    +B1257264517485N00549928EA0000002118
    +B1257284517474N00549913EA0000002113
    +B1257304517463N00549899EA0000002108
    +B1257324517452N00549886EA0000002102
    +B1257344517440N00549872EA0000002096
    +B1257364517428N00549858EA0000002091
    +B1257384517422N00549851EA0000002085
    +B1257404517404N00549831EA0000002080
    +B1257424517392N00549817EA0000002075
    +B1257444517380N00549803EA0000002068
    +B1257464517367N00549789EA0000002062
    +B1257484517354N00549774EA0000002057
    +B1257504517342N00549759EA0000002053
    +B1257524517330N00549745EA0000002050
    +B1257544517318N00549731EA0000002047
    +B1257564517306N00549716EA0000002044
    +B1257584517295N00549701EA0000002042
    +B1258004517283N00549687EA0000002041
    +B1258024517271N00549675EA0000002039
    +B1258044517257N00549664EA0000002036
    +B1258064517246N00549651EA0000002032
    +B1258084517235N00549636EA0000002027
    +B1258104517222N00549623EA0000002022
    +B1258124517209N00549609EA0000002019
    +B1258144517198N00549595EA0000002016
    +B1258164517185N00549582EA0000002012
    +B1258184517172N00549570EA0000002008
    +B1258204517160N00549556EA0000002004
    +B1258224517149N00549544EA0000002001
    +B1258244517139N00549538EA0000002000
    +B1258264517131N00549532EA0000001996
    +B1258284517122N00549524EA0000001993
    +B1258304517112N00549517EA0000001992
    +B1258324517096N00549510EA0000001987
    +B1258344517086N00549503EA0000001983
    +B1258364517076N00549495EA0000001980
    +B1258384517066N00549487EA0000001977
    +B1258404517057N00549479EA0000001973
    +B1258424517047N00549469EA0000001969
    +B1258444517038N00549460EA0000001966
    +B1258464517029N00549452EA0000001962
    +B1258484517020N00549441EA0000001957
    +B1258504517010N00549429EA0000001950
    +B1258524516998N00549415EA0000001945
    +B1258544516989N00549399EA0000001942
    +B1258574516980N00549384EA0000001937
    +B1258594516972N00549369EA0000001933
    +B1259014516965N00549356EA0000001931
    +B1259034516958N00549346EA0000001927
    +B1259054516949N00549339EA0000001923
    +B1259074516939N00549332EA0000001919
    +B1259094516927N00549325EA0000001914
    +B1259114516915N00549319EA0000001912
    +B1259134516904N00549312EA0000001908
    +B1259154516898N00549307EA0000001902
    +B1259174516877N00549291EA0000001898
    +B1259194516864N00549279EA0000001895
    +B1259214516858N00549273EA0000001892
    +B1259234516840N00549252EA0000001887
    +B1259254516826N00549239EA0000001883
    +B1259274516814N00549227EA0000001880
    +B1259294516807N00549215EA0000001879
    +B1259314516799N00549202EA0000001875
    +B1259334516788N00549189EA0000001871
    +B1259354516775N00549179EA0000001868
    +B1259374516762N00549172EA0000001863
    +B1259394516750N00549162EA0000001858
    +B1259414516738N00549153EA0000001853
    +B1259434516729N00549147EA0000001850
    +B1259454516725N00549148EA0000001847
    +B1259474516719N00549147EA0000001843
    +B1259494516710N00549145EA0000001841
    +B1259514516703N00549140EA0000001839
    +B1259534516699N00549131EA0000001836
    +B1259554516692N00549124EA0000001832
    +B1259574516684N00549118EA0000001830
    +B1259594516678N00549112EA0000001831
    +B1300014516672N00549106EA0000001829
    +B1300034516664N00549098EA0000001828
    +B1300054516655N00549090EA0000001827
    +B1300074516646N00549083EA0000001828
    +B1300094516639N00549075EA0000001830
    +B1300114516631N00549068EA0000001830
    +B1300134516618N00549059EA0000001831
    +B1300154516612N00549053EA0000001830
    +B1300174516603N00549046EA0000001828
    +B1300194516593N00549039EA0000001827
    +B1300214516584N00549032EA0000001827
    +B1300234516577N00549021EA0000001826
    +B1300254516570N00549009EA0000001827
    +B1300274516563N00548999EA0000001829
    +B1300294516556N00548992EA0000001832
    +B1300314516550N00548989EA0000001835
    +B1300334516543N00548982EA0000001839
    +B1300354516537N00548975EA0000001845
    +B1300384516534N00548971EA0000001853
    +B1300404516531N00548964EA0000001860
    +B1300424516531N00548953EA0000001867
    +B1300444516540N00548939EA0000001873
    +B1300464516554N00548936EA0000001881
    +B1300484516565N00548945EA0000001887
    +B1300504516569N00548965EA0000001896
    +B1300524516564N00548978EA0000001905
    +B1300544516556N00548975EA0000001913
    +B1300564516552N00548968EA0000001922
    +B1300584516551N00548960EA0000001928
    +B1301004516552N00548949EA0000001935
    +B1301024516560N00548936EA0000001941
    +B1301044516576N00548935EA0000001946
    +B1301064516585N00548943EA0000001951
    +B1301084516583N00548957EA0000001956
    +B1301104516576N00548962EA0000001961
    +B1301124516573N00548959EA0000001968
    +B1301144516569N00548961EA0000001972
    +B1301164516567N00548950EA0000001976
    +B1301184516563N00548940EA0000001982
    +B1301204516559N00548934EA0000001990
    +B1301224516554N00548928EA0000001995
    +B1301244516547N00548922EA0000002000
    +B1301264516538N00548915EA0000002007
    +B1301284516535N00548911EA0000002016
    +B1301304516535N00548908EA0000002022
    +B1301324516532N00548902EA0000002027
    +B1301344516525N00548891EA0000002030
    +B1301364516522N00548882EA0000002033
    +B1301384516518N00548873EA0000002034
    +B1301404516511N00548864EA0000002039
    +B1301424516506N00548856EA0000002044
    +B1301444516502N00548849EA0000002048
    +B1301464516496N00548842EA0000002050
    +B1301484516489N00548831EA0000002049
    +B1301504516480N00548822EA0000002048
    +B1301524516471N00548814EA0000002046
    +B1301544516459N00548799EA0000002041
    +B1301564516450N00548787EA0000002039
    +B1301584516442N00548775EA0000002039
    +B1302004516434N00548763EA0000002041
    +B1302024516422N00548753EA0000002044
    +B1302044516412N00548746EA0000002048
    +B1302064516408N00548739EA0000002052
    +B1302084516403N00548730EA0000002055
    +B1302104516395N00548717EA0000002059
    +B1302124516388N00548703EA0000002065
    +B1302144516383N00548693EA0000002070
    +B1302174516376N00548680EA0000002074
    +B1302194516367N00548664EA0000002080
    +B1302214516359N00548650EA0000002087
    +B1302234516351N00548638EA0000002091
    +B1302254516341N00548624EA0000002094
    +B1302274516331N00548610EA0000002096
    +B1302294516320N00548597EA0000002096
    +B1302314516310N00548585EA0000002095
    +B1302334516301N00548575EA0000002093
    +B1302354516291N00548565EA0000002090
    +B1302374516281N00548555EA0000002086
    +B1302394516272N00548544EA0000002084
    +B1302414516264N00548534EA0000002082
    +B1302434516258N00548525EA0000002079
    +B1302454516250N00548516EA0000002074
    +B1302474516242N00548506EA0000002071
    +B1302494516234N00548498EA0000002067
    +B1302514516224N00548489EA0000002063
    +B1302534516215N00548478EA0000002059
    +B1302554516207N00548469EA0000002055
    +B1302574516199N00548458EA0000002050
    +B1302594516188N00548445EA0000002044
    +B1303014516178N00548431EA0000002040
    +B1303034516169N00548417EA0000002038
    +B1303054516161N00548405EA0000002034
    +B1303074516151N00548395EA0000002029
    +B1303094516141N00548383EA0000002026
    +B1303114516133N00548372EA0000002025
    +B1303134516126N00548364EA0000002022
    +B1303154516118N00548355EA0000002018
    +B1303174516109N00548344EA0000002013
    +B1303194516099N00548335EA0000002008
    +B1303214516089N00548328EA0000002002
    +B1303234516078N00548320EA0000001997
    +B1303254516068N00548311EA0000001991
    +B1303274516058N00548300EA0000001985
    +B1303294516049N00548290EA0000001980
    +B1303314516038N00548281EA0000001973
    +B1303334516022N00548261EA0000001963
    +B1303354516012N00548247EA0000001958
    +B1303374516002N00548232EA0000001954
    +B1303394515993N00548217EA0000001951
    +B1303414515986N00548201EA0000001947
    +B1303434515982N00548187EA0000001944
    +B1303454515979N00548171EA0000001940
    +B1303474515974N00548158EA0000001938
    +B1303494515967N00548148EA0000001935
    +B1303514515960N00548137EA0000001932
    +B1303534515951N00548125EA0000001929
    +B1303554515941N00548117EA0000001928
    +B1303584515932N00548110EA0000001927
    +B1304004515924N00548102EA0000001924
    +B1304024515914N00548094EA0000001919
    +B1304044515904N00548086EA0000001915
    +B1304064515895N00548078EA0000001911
    +B1304084515884N00548071EA0000001905
    +B1304104515873N00548058EA0000001898
    +B1304124515861N00548041EA0000001893
    +B1304144515851N00548027EA0000001889
    +B1304164515840N00548014EA0000001882
    +B1304184515828N00548000EA0000001877
    +B1304204515817N00547985EA0000001872
    +B1304224515811N00547972EA0000001870
    +B1304244515804N00547960EA0000001867
    +B1304264515795N00547946EA0000001864
    +B1304284515785N00547929EA0000001862
    +B1304304515777N00547913EA0000001862
    +B1304324515772N00547900EA0000001865
    +B1304344515765N00547888EA0000001870
    +B1304364515759N00547877EA0000001879
    +B1304384515753N00547869EA0000001886
    +B1304404515746N00547863EA0000001889
    +B1304424515738N00547856EA0000001892
    +B1304444515730N00547851EA0000001895
    +B1304464515724N00547846EA0000001897
    +B1304484515717N00547840EA0000001897
    +B1304504515710N00547835EA0000001895
    +B1304524515702N00547829EA0000001893
    +B1304544515694N00547824EA0000001892
    +B1304564515686N00547819EA0000001889
    +B1304584515676N00547811EA0000001885
    +B1305004515665N00547803EA0000001882
    +B1305024515653N00547795EA0000001879
    +B1305044515643N00547785EA0000001875
    +B1305064515634N00547778EA0000001870
    +B1305084515623N00547772EA0000001865
    +B1305104515613N00547764EA0000001861
    +B1305124515603N00547758EA0000001856
    +B1305144515589N00547745EA0000001850
    +B1305164515583N00547734EA0000001848
    +B1305184515578N00547724EA0000001849
    +B1305204515574N00547716EA0000001852
    +B1305224515571N00547707EA0000001857
    +B1305244515565N00547698EA0000001863
    +B1305264515558N00547690EA0000001870
    +B1305284515551N00547683EA0000001874
    +B1305304515540N00547676EA0000001879
    +B1305324515532N00547669EA0000001886
    +B1305344515528N00547669EA0000001892
    +B1305364515522N00547669EA0000001897
    +B1305394515513N00547665EA0000001899
    +B1305414515505N00547660EA0000001900
    +B1305434515498N00547654EA0000001900
    +B1305454515491N00547649EA0000001899
    +B1305474515483N00547644EA0000001897
    +B1305494515474N00547639EA0000001895
    +B1305514515464N00547633EA0000001895
    +B1305534515456N00547626EA0000001893
    +B1305554515447N00547620EA0000001890
    +B1305574515437N00547615EA0000001885
    +B1305594515427N00547607EA0000001879
    +B1306014515417N00547599EA0000001872
    +B1306034515406N00547591EA0000001865
    +B1306054515395N00547581EA0000001858
    +B1306074515385N00547571EA0000001853
    +B1306094515374N00547564EA0000001847
    +B1306114515362N00547557EA0000001842
    +B1306134515351N00547551EA0000001838
    +B1306154515339N00547548EA0000001835
    +B1306174515327N00547547EA0000001833
    +B1306194515316N00547546EA0000001833
    +B1306214515308N00547543EA0000001833
    +B1306234515299N00547539EA0000001831
    +B1306254515288N00547533EA0000001829
    +B1306274515277N00547525EA0000001829
    +B1306294515268N00547519EA0000001830
    +B1306314515258N00547513EA0000001829
    +B1306334515245N00547506EA0000001828
    +B1306354515232N00547498EA0000001830
    +B1306374515221N00547489EA0000001830
    +B1306394515209N00547480EA0000001830
    +B1306414515198N00547472EA0000001829
    +B1306434515187N00547464EA0000001828
    +B1306454515176N00547458EA0000001829
    +B1306474515167N00547452EA0000001828
    +B1306494515159N00547445EA0000001827
    +B1306514515149N00547436EA0000001824
    +B1306534515139N00547427EA0000001823
    +B1306554515127N00547411EA0000001819
    +B1306574515117N00547400EA0000001816
    +B1306594515105N00547389EA0000001815
    +B1307014515096N00547378EA0000001814
    +B1307034515087N00547368EA0000001812
    +B1307054515076N00547359EA0000001808
    +B1307074515065N00547350EA0000001804
    +B1307094515053N00547340EA0000001802
    +B1307114515044N00547330EA0000001802
    +B1307134515044N00547327EA0000001804
    +B1307154515044N00547325EA0000001803
    +B1307184515038N00547316EA0000001801
    +B1307204515031N00547304EA0000001803
    +B1307224515024N00547295EA0000001802
    +B1307244515014N00547283EA0000001802
    +B1307264515004N00547270EA0000001804
    +B1307284514996N00547260EA0000001807
    +B1307304514990N00547252EA0000001813
    +B1307324514983N00547241EA0000001816
    +B1307344514975N00547230EA0000001822
    +B1307364514968N00547220EA0000001828
    +B1307384514961N00547210EA0000001832
    +B1307404514957N00547204EA0000001835
    +B1307424514943N00547188EA0000001838
    +B1307444514936N00547180EA0000001838
    +B1307464514929N00547172EA0000001838
    +B1307484514921N00547161EA0000001836
    +B1307504514914N00547151EA0000001836
    +B1307524514907N00547143EA0000001836
    +B1307544514899N00547133EA0000001834
    +B1307564514890N00547122EA0000001833
    +B1307584514881N00547110EA0000001832
    +B1308004514873N00547098EA0000001832
    +B1308024514865N00547086EA0000001833
    +B1308044514857N00547072EA0000001834
    +B1308064514850N00547060EA0000001835
    +B1308084514843N00547048EA0000001835
    +B1308104514834N00547036EA0000001833
    +B1308124514824N00547023EA0000001831
    +B1308144514816N00547011EA0000001829
    +B1308164514809N00546998EA0000001826
    +B1308184514800N00546986EA0000001823
    +B1308204514793N00546976EA0000001820
    +B1308224514785N00546966EA0000001815
    +B1308244514778N00546953EA0000001809
    +B1308264514768N00546940EA0000001802
    +B1308284514758N00546924EA0000001797
    +B1308304514745N00546904EA0000001793
    +B1308324514737N00546891EA0000001788
    +B1308344514729N00546875EA0000001783
    +B1308364514722N00546858EA0000001780
    +B1308384514714N00546846EA0000001778
    +B1308404514705N00546839EA0000001777
    +B1308424514696N00546831EA0000001777
    +B1308444514686N00546823EA0000001777
    +B1308464514678N00546814EA0000001778
    +B1308484514672N00546809EA0000001780
    +B1308504514663N00546803EA0000001782
    +B1308524514655N00546794EA0000001786
    +B1308554514648N00546785EA0000001792
    +B1308574514641N00546777EA0000001796
    +B1308594514635N00546768EA0000001800
    +B1309014514628N00546757EA0000001805
    +B1309034514621N00546745EA0000001810
    +B1309054514614N00546736EA0000001815
    +B1309074514607N00546726EA0000001818
    +B1309094514599N00546714EA0000001821
    +B1309114514592N00546701EA0000001824
    +B1309134514585N00546690EA0000001826
    +B1309154514577N00546681EA0000001827
    +B1309174514568N00546671EA0000001826
    +B1309194514558N00546658EA0000001825
    +B1309214514553N00546652EA0000001827
    +B1309234514545N00546635EA0000001828
    +B1309254514540N00546628EA0000001827
    +B1309274514534N00546619EA0000001825
    +B1309294514528N00546606EA0000001826
    +B1309314514521N00546595EA0000001826
    +B1309334514514N00546586EA0000001825
    +B1309354514507N00546575EA0000001826
    +B1309374514501N00546564EA0000001828
    +B1309394514494N00546553EA0000001830
    +B1309414514487N00546541EA0000001830
    +B1309434514478N00546527EA0000001831
    +B1309454514469N00546510EA0000001833
    +B1309474514464N00546496EA0000001836
    +B1309494514460N00546483EA0000001839
    +B1309514514453N00546466EA0000001841
    +B1309534514446N00546450EA0000001843
    +B1309554514439N00546436EA0000001845
    +B1309574514431N00546425EA0000001846
    +B1309594514425N00546412EA0000001847
    +B1310014514420N00546399EA0000001850
    +B1310034514415N00546385EA0000001852
    +B1310054514411N00546370EA0000001853
    +B1310074514404N00546356EA0000001855
    +B1310094514398N00546343EA0000001856
    +B1310114514387N00546321EA0000001859
    +B1310134514381N00546308EA0000001862
    +B1310154514376N00546297EA0000001865
    +B1310174514372N00546286EA0000001866
    +B1310194514366N00546272EA0000001867
    +B1310214514359N00546260EA0000001868
    +B1310234514353N00546250EA0000001867
    +B1310254514346N00546238EA0000001865
    +B1310274514337N00546224EA0000001863
    +B1310294514329N00546212EA0000001862
    +B1310314514322N00546201EA0000001858
    +B1310334514313N00546188EA0000001854
    +B1310364514304N00546173EA0000001852
    +B1310384514295N00546158EA0000001851
    +B1310404514287N00546146EA0000001849
    +B1310424514279N00546134EA0000001847
    +B1310444514273N00546120EA0000001847
    +B1310464514266N00546109EA0000001845
    +B1310484514256N00546099EA0000001843
    +B1310504514249N00546086EA0000001843
    +B1310524514243N00546074EA0000001842
    +B1310544514236N00546064EA0000001839
    +B1310564514229N00546051EA0000001838
    +B1310584514222N00546037EA0000001837
    +B1311004514215N00546025EA0000001836
    +B1311024514208N00546010EA0000001834
    +B1311044514202N00545994EA0000001832
    +B1311064514196N00545980EA0000001830
    +B1311084514190N00545964EA0000001827
    +B1311104514184N00545949EA0000001823
    +B1311124514178N00545934EA0000001820
    +B1311144514172N00545921EA0000001816
    +B1311164514165N00545908EA0000001811
    +B1311184514158N00545893EA0000001806
    +B1311204514150N00545880EA0000001802
    +B1311224514142N00545867EA0000001798
    +B1311244514135N00545854EA0000001794
    +B1311264514126N00545842EA0000001788
    +B1311284514117N00545829EA0000001784
    +B1311304514108N00545816EA0000001780
    +B1311324514103N00545807EA0000001779
    +B1311344514098N00545800EA0000001775
    +B1311364514091N00545790EA0000001772
    +B1311384514084N00545780EA0000001771
    +B1311404514078N00545770EA0000001768
    +B1311424514070N00545763EA0000001766
    +B1311444514061N00545767EA0000001763
    +B1311464514054N00545781EA0000001760
    +B1311484514052N00545795EA0000001758
    +B1311504514056N00545810EA0000001754
    +B1311524514064N00545832EA0000001748
    +B1311544514070N00545849EA0000001744
    +B1311564514076N00545867EA0000001740
    +B1311584514084N00545885EA0000001736
    +B1312004514091N00545902EA0000001734
    +B1312024514099N00545916EA0000001730
    +B1312044514108N00545932EA0000001726
    +B1312064514117N00545947EA0000001724
    +B1312084514126N00545962EA0000001721
    +B1312104514136N00545978EA0000001716
    +B1312124514146N00545995EA0000001713
    +B1312144514156N00546013EA0000001710
    +B1312174514165N00546028EA0000001707
    +B1312194514174N00546045EA0000001703
    +B1312214514184N00546061EA0000001700
    +B1312234514193N00546076EA0000001699
    +B1312254514202N00546089EA0000001696
    +B1312274514211N00546103EA0000001692
    +B1312294514221N00546118EA0000001691
    +B1312314514230N00546132EA0000001690
    +B1312334514238N00546145EA0000001689
    +B1312354514246N00546159EA0000001688
    +B1312374514253N00546171EA0000001687
    +B1312394514262N00546184EA0000001683
    +B1312414514272N00546198EA0000001682
    +B1312434514279N00546213EA0000001682
    +B1312454514288N00546225EA0000001683
    +B1312474514297N00546232EA0000001684
    +B1312494514309N00546244EA0000001685
    +B1312514514320N00546259EA0000001687
    +B1312534514329N00546273EA0000001691
    +B1312554514337N00546285EA0000001692
    +B1312574514345N00546295EA0000001692
    +B1312594514351N00546306EA0000001691
    +B1313014514358N00546318EA0000001688
    +B1313034514366N00546329EA0000001685
    +B1313054514374N00546339EA0000001683
    +B1313074514382N00546351EA0000001680
    +B1313094514389N00546364EA0000001678
    +B1313114514397N00546376EA0000001676
    +B1313134514405N00546387EA0000001675
    +B1313154514412N00546396EA0000001675
    +B1313174514417N00546401EA0000001676
    +B1313194514424N00546406EA0000001675
    +B1313214514432N00546417EA0000001675
    +B1313234514441N00546429EA0000001675
    +B1313254514449N00546440EA0000001676
    +B1313274514457N00546453EA0000001677
    +B1313294514466N00546467EA0000001678
    +B1313314514473N00546480EA0000001681
    +B1313334514482N00546496EA0000001684
    +B1313354514492N00546507EA0000001685
    +B1313374514502N00546517EA0000001688
    +B1313394514510N00546522EA0000001691
    +B1313414514516N00546528EA0000001693
    +B1313434514523N00546539EA0000001694
    +B1313454514529N00546550EA0000001695
    +B1313474514535N00546559EA0000001696
    +B1313494514541N00546568EA0000001696
    +B1313514514550N00546579EA0000001695
    +B1313534514559N00546590EA0000001695
    +B1313554514567N00546600EA0000001697
    +B1313584514573N00546609EA0000001698
    +B1314004514583N00546621EA0000001698
    +B1314024514592N00546634EA0000001699
    +B1314044514600N00546641EA0000001702
    +B1314064514604N00546640EA0000001710
    +B1314084514612N00546648EA0000001716
    +B1314104514621N00546657EA0000001723
    +B1314124514627N00546664EA0000001733
    +B1314144514628N00546671EA0000001740
    +B1314164514626N00546680EA0000001746
    +B1314184514619N00546686EA0000001753
    +B1314204514614N00546683EA0000001759
    +B1314224514608N00546653EA0000001766
    +B1314244514619N00546642EA0000001774
    +B1314264514632N00546643EA0000001783
    +B1314284514639N00546649EA0000001792
    +B1314304514644N00546659EA0000001798
    +B1314324514646N00546672EA0000001804
    +B1314344514643N00546685EA0000001812
    +B1314364514635N00546687EA0000001817
    +B1314384514627N00546673EA0000001823
    +B1314404514627N00546656EA0000001828
    +B1314424514638N00546648EA0000001833
    +B1314444514650N00546651EA0000001839
    +B1314464514656N00546658EA0000001846
    +B1314484514662N00546666EA0000001851
    +B1314504514664N00546678EA0000001854
    +B1314524514665N00546689EA0000001858
    +B1314544514670N00546700EA0000001861
    +B1314564514680N00546708EA0000001864
    +B1314584514687N00546716EA0000001867
    +B1315004514694N00546724EA0000001868
    +B1315024514705N00546735EA0000001869
    +B1315044514714N00546745EA0000001871
    +B1315064514720N00546752EA0000001875
    +B1315084514728N00546758EA0000001877
    +B1315104514737N00546767EA0000001881
    +B1315124514751N00546783EA0000001889
    +B1315144514759N00546791EA0000001896
    +B1315164514766N00546798EA0000001901
    +B1315184514774N00546806EA0000001907
    +B1315204514777N00546817EA0000001915
    +B1315224514773N00546826EA0000001922
    +B1315244514759N00546826EA0000001929
    +B1315264514750N00546814EA0000001935
    +B1315284514750N00546797EA0000001942
    +B1315304514762N00546786EA0000001949
    +B1315324514774N00546788EA0000001956
    +B1315344514783N00546797EA0000001966
    +B1315374514787N00546806EA0000001975
    +B1315394514787N00546818EA0000001982
    +B1315414514784N00546831EA0000001988
    +B1315434514774N00546837EA0000001993
    +B1315454514764N00546829EA0000001998
    +B1315474514763N00546810EA0000002004
    +B1315494514773N00546798EA0000002011
    +B1315514514785N00546799EA0000002018
    +B1315534514799N00546814EA0000002030
    +B1315554514806N00546819EA0000002036
    +B1315574514811N00546829EA0000002041
    +B1316004514808N00546847EA0000002045
    +B1316024514799N00546851EA0000002047
    +B1316044514790N00546838EA0000002047
    +B1316064514791N00546820EA0000002049
    +B1316084514804N00546817EA0000002051
    +B1316104514815N00546827EA0000002056
    +B1316124514823N00546836EA0000002060
    +B1316144514831N00546841EA0000002062
    +B1316164514837N00546849EA0000002062
    +B1316184514844N00546862EA0000002060
    +B1316204514850N00546870EA0000002061
    +B1316224514856N00546873EA0000002062
    +B1316244514859N00546876EA0000002062
    +B1316264514867N00546884EA0000002062
    +B1316284514877N00546895EA0000002062
    +B1316304514886N00546904EA0000002064
    +B1316324514893N00546913EA0000002066
    +B1316344514901N00546924EA0000002067
    +B1316364514910N00546932EA0000002071
    +B1316384514916N00546940EA0000002075
    +B1316404514922N00546947EA0000002079
    +B1316424514928N00546953EA0000002084
    +B1316444514936N00546959EA0000002091
    +B1316464514943N00546962EA0000002099
    +B1316484514949N00546963EA0000002105
    +B1316504514956N00546967EA0000002111
    +B1316524514963N00546972EA0000002118
    +B1316544514969N00546974EA0000002127
    +B1316564514974N00546977EA0000002133
    +B1316584514979N00546979EA0000002138
    +B1317004514985N00546986EA0000002142
    +B1317024514990N00546995EA0000002146
    +B1317044514994N00547000EA0000002149
    +B1317064515000N00547004EA0000002151
    +B1317084515007N00547012EA0000002150
    +B1317104515015N00547019EA0000002148
    +B1317124515025N00547026EA0000002143
    +B1317144515035N00547036EA0000002138
    +B1317164515053N00547050EA0000002134
    +B1317184515067N00547060EA0000002133
    +B1317204515080N00547071EA0000002135
    +B1317224515091N00547082EA0000002138
    +B1317244515100N00547093EA0000002142
    +B1317264515109N00547103EA0000002144
    +B1317284515117N00547115EA0000002144
    +B1317304515126N00547128EA0000002144
    +B1317324515135N00547139EA0000002142
    +B1317344515143N00547151EA0000002139
    +B1317364515149N00547163EA0000002135
    +B1317394515155N00547175EA0000002130
    +B1317414515163N00547189EA0000002128
    +B1317434515173N00547196EA0000002126
    +B1317454515183N00547200EA0000002124
    +B1317474515193N00547207EA0000002123
    +B1317494515203N00547214EA0000002123
    +B1317514515212N00547222EA0000002121
    +B1317534515221N00547232EA0000002119
    +B1317554515231N00547243EA0000002118
    +B1317574515241N00547256EA0000002117
    +B1317594515250N00547269EA0000002118
    +B1318014515258N00547281EA0000002117
    +B1318034515266N00547295EA0000002115
    +B1318054515276N00547310EA0000002115
    +B1318074515284N00547320EA0000002115
    +B1318094515292N00547331EA0000002113
    +B1318114515300N00547345EA0000002110
    +B1318134515309N00547358EA0000002107
    +B1318154515317N00547371EA0000002105
    +B1318174515324N00547384EA0000002105
    +B1318194515328N00547394EA0000002105
    +B1318214515334N00547406EA0000002103
    +B1318234515341N00547420EA0000002101
    +B1318254515347N00547434EA0000002100
    +B1318274515354N00547447EA0000002097
    +B1318294515363N00547461EA0000002094
    +B1318314515372N00547475EA0000002092
    +B1318334515381N00547489EA0000002088
    +B1318354515389N00547504EA0000002085
    +B1318374515399N00547518EA0000002081
    +B1318394515408N00547532EA0000002077
    +B1318414515417N00547546EA0000002073
    +B1318434515428N00547560EA0000002068
    +B1318454515437N00547575EA0000002062
    +B1318474515447N00547590EA0000002056
    +B1318494515458N00547603EA0000002050
    +B1318514515468N00547617EA0000002043
    +B1318534515479N00547631EA0000002036
    +B1318554515497N00547647EA0000002027
    +B1318574515508N00547656EA0000002020
    +B1318594515521N00547668EA0000002015
    +B1319014515533N00547682EA0000002010
    +B1319034515545N00547693EA0000002008
    +B1319054515555N00547704EA0000002005
    +B1319074515566N00547716EA0000002002
    +B1319094515577N00547729EA0000002001
    +B1319114515585N00547742EA0000001999
    +B1319134515594N00547756EA0000001997
    +B1319154515606N00547770EA0000001995
    +B1319174515619N00547784EA0000001994
    +B1319204515630N00547798EA0000001993
    +B1319224515643N00547813EA0000001993
    +B1319244515657N00547829EA0000001994
    +B1319264515669N00547843EA0000001995
    +B1319284515680N00547857EA0000001995
    +B1319304515693N00547873EA0000001996
    +B1319324515704N00547888EA0000001999
    +B1319344515714N00547901EA0000001999
    +B1319364515722N00547915EA0000001997
    +B1319384515732N00547927EA0000001998
    +B1319404515741N00547939EA0000001997
    +B1319424515750N00547952EA0000001994
    +B1319444515755N00547959EA0000001991
    +B1319464515771N00547981EA0000001990
    +B1319484515780N00547996EA0000001986
    +B1319504515789N00548012EA0000001982
    +B1319524515800N00548026EA0000001980
    +B1319544515809N00548041EA0000001979
    +B1319564515817N00548056EA0000001975
    +B1319584515826N00548072EA0000001970
    +B1320004515837N00548088EA0000001968
    +B1320024515847N00548103EA0000001964
    +B1320044515858N00548117EA0000001959
    +B1320064515869N00548133EA0000001955
    +B1320084515877N00548147EA0000001953
    +B1320104515884N00548155EA0000001952
    +B1320124515891N00548163EA0000001948
    +B1320144515901N00548175EA0000001945
    +B1320164515912N00548188EA0000001943
    +B1320184515922N00548204EA0000001942
    +B1320204515932N00548217EA0000001944
    +B1320224515941N00548228EA0000001943
    +B1320244515950N00548242EA0000001941
    +B1320264515960N00548257EA0000001938
    +B1320284515970N00548271EA0000001935
    +B1320304515981N00548285EA0000001931
    +B1320324515992N00548300EA0000001928
    +B1320344516004N00548315EA0000001927
    +B1320364516020N00548336EA0000001923
    +B1320384516031N00548350EA0000001920
    +B1320404516042N00548364EA0000001918
    +B1320424516052N00548376EA0000001915
    +B1320444516062N00548387EA0000001911
    +B1320464516075N00548398EA0000001907
    +B1320484516086N00548408EA0000001904
    +B1320504516096N00548418EA0000001900
    +B1320524516108N00548428EA0000001895
    +B1320544516120N00548439EA0000001892
    +B1320564516131N00548451EA0000001891
    +B1320584516138N00548457EA0000001890
    +B1321014516146N00548466EA0000001887
    +B1321034516155N00548474EA0000001887
    +B1321054516163N00548481EA0000001888
    +B1321074516171N00548490EA0000001887
    +B1321094516182N00548502EA0000001886
    +B1321114516194N00548513EA0000001887
    +B1321134516203N00548522EA0000001888
    +B1321154516213N00548529EA0000001887
    +B1321174516224N00548538EA0000001885
    +B1321194516235N00548549EA0000001883
    +B1321214516241N00548554EA0000001880
    +B1321234516257N00548569EA0000001877
    +B1321254516257N00548569EA0000001874
    +B1321274516279N00548591EA0000001871
    +B1321294516291N00548602EA0000001868
    +B1321314516302N00548611EA0000001866
    +B1321334516315N00548620EA0000001862
    +B1321354516329N00548633EA0000001859
    +B1321374516342N00548647EA0000001857
    +B1321394516352N00548655EA0000001856
    +B1321414516359N00548660EA0000001858
    +B1321434516367N00548668EA0000001863
    +B1321454516376N00548673EA0000001869
    +B1321474516382N00548676EA0000001876
    +B1321494516387N00548679EA0000001882
    +B1321514516397N00548683EA0000001889
    +B1321534516404N00548689EA0000001898
    +B1321554516407N00548697EA0000001907
    +B1321574516408N00548703EA0000001915
    +B1321594516409N00548711EA0000001923
    +B1322014516417N00548719EA0000001930
    +B1322034516431N00548714EA0000001934
    +B1322054516445N00548706EA0000001939
    +B1322074516455N00548698EA0000001943
    +B1322094516464N00548688EA0000001946
    +B1322114516475N00548681EA0000001948
    +B1322134516484N00548681EA0000001950
    +B1322154516492N00548684EA0000001951
    +B1322174516506N00548693EA0000001949
    +B1322194516517N00548698EA0000001946
    +B1322214516527N00548704EA0000001942
    +B1322234516539N00548711EA0000001938
    +B1322254516550N00548718EA0000001936
    +B1322274516562N00548724EA0000001936
    +B1322294516575N00548729EA0000001934
    +B1322314516589N00548735EA0000001933
    +B1322334516603N00548742EA0000001932
    +B1322354516616N00548751EA0000001932
    +B1322374516626N00548757EA0000001933
    +B1322394516636N00548766EA0000001934
    +B1322424516648N00548774EA0000001936
    +B1322444516657N00548780EA0000001939
    +B1322464516666N00548786EA0000001941
    +B1322484516675N00548795EA0000001944
    +B1322504516685N00548806EA0000001947
    +B1322524516693N00548814EA0000001952
    +B1322544516701N00548820EA0000001958
    +B1322564516707N00548829EA0000001964
    +B1322584516709N00548835EA0000001971
    +B1323004516704N00548854EA0000001978
    +B1323024516704N00548854EA0000001985
    +B1323044516689N00548844EA0000001993
    +B1323064516685N00548829EA0000002002
    +B1323084516687N00548817EA0000002011
    +B1323104516694N00548811EA0000002018
    +B1323124516702N00548814EA0000002025
    +B1323144516706N00548825EA0000002031
    +B1323164516703N00548838EA0000002037
    +B1323184516695N00548841EA0000002045
    +B1323204516684N00548838EA0000002050
    +B1323224516673N00548837EA0000002055
    +B1323244516663N00548829EA0000002061
    +B1323264516659N00548820EA0000002065
    +B1323284516661N00548812EA0000002069
    +B1323304516670N00548814EA0000002072
    +B1323324516680N00548822EA0000002074
    +B1323344516691N00548830EA0000002075
    +B1323364516703N00548841EA0000002077
    +B1323384516715N00548853EA0000002080
    +B1323404516725N00548863EA0000002084
    +B1323424516735N00548872EA0000002090
    +B1323444516744N00548879EA0000002098
    +B1323464516750N00548889EA0000002106
    +B1323484516751N00548903EA0000002113
    +B1323504516748N00548918EA0000002120
    +B1323524516739N00548923EA0000002126
    +B1323544516726N00548907EA0000002136
    +B1323564516726N00548895EA0000002142
    +B1323584516734N00548887EA0000002148
    +B1324004516744N00548884EA0000002155
    +B1324024516754N00548885EA0000002163
    +B1324044516764N00548885EA0000002171
    +B1324064516772N00548887EA0000002179
    +B1324084516781N00548895EA0000002186
    +B1324104516787N00548910EA0000002192
    +B1324124516786N00548925EA0000002198
    +B1324144516779N00548931EA0000002202
    +B1324174516772N00548923EA0000002206
    +B1324194516772N00548910EA0000002211
    +B1324214516781N00548908EA0000002217
    +B1324234516791N00548911EA0000002224
    +B1324254516802N00548916EA0000002230
    +B1324274516814N00548921EA0000002237
    +B1324294516826N00548923EA0000002245
    +B1324314516839N00548927EA0000002252
    +B1324334516854N00548927EA0000002256
    +B1324354516861N00548930EA0000002262
    +B1324374516882N00548940EA0000002268
    +B1324394516896N00548949EA0000002272
    +B1324414516907N00548959EA0000002275
    +B1324434516920N00548967EA0000002277
    +B1324454516935N00548975EA0000002279
    +B1324474516947N00548986EA0000002280
    +B1324494516959N00548998EA0000002280
    +B1324514516971N00549008EA0000002279
    +B1324534516984N00549018EA0000002277
    +B1324554516998N00549028EA0000002277
    +B1324574517011N00549039EA0000002277
    +B1324594517024N00549051EA0000002275
    +B1325014517040N00549061EA0000002273
    +B1325034517055N00549071EA0000002273
    +B1325054517066N00549081EA0000002275
    +B1325074517076N00549091EA0000002274
    +B1325094517087N00549102EA0000002273
    +B1325114517101N00549115EA0000002271
    +B1325134517115N00549128EA0000002271
    +B1325154517127N00549137EA0000002272
    +B1325174517139N00549146EA0000002270
    +B1325194517154N00549158EA0000002266
    +B1325214517168N00549171EA0000002265
    +B1325234517182N00549186EA0000002264
    +B1325254517195N00549200EA0000002265
    +B1325274517207N00549213EA0000002264
    +B1325294517223N00549234EA0000002265
    +B1325314517231N00549244EA0000002266
    +B1325334517240N00549253EA0000002266
    +B1325354517249N00549263EA0000002264
    +B1325374517259N00549275EA0000002258
    +B1325394517269N00549289EA0000002251
    +B1325414517279N00549304EA0000002244
    +B1325434517288N00549319EA0000002237
    +B1325454517298N00549336EA0000002229
    +B1325474517308N00549353EA0000002222
    +B1325494517314N00549369EA0000002217
    +B1325514517319N00549383EA0000002210
    +B1325544517327N00549398EA0000002202
    +B1325564517337N00549411EA0000002196
    +B1325584517345N00549423EA0000002190
    +B1326004517353N00549436EA0000002183
    +B1326024517364N00549445EA0000002177
    +B1326044517375N00549448EA0000002173
    +B1326064517379N00549455EA0000002172
    +B1326084517384N00549462EA0000002172
    +B1326104517394N00549470EA0000002171
    +B1326124517400N00549475EA0000002171
    +B1326144517412N00549487EA0000002172
    +B1326164517416N00549495EA0000002170
    +B1326184517421N00549506EA0000002167
    +B1326204517429N00549517EA0000002163
    +B1326224517439N00549524EA0000002160
    +B1326244517449N00549528EA0000002155
    +B1326264517460N00549532EA0000002149
    +B1326284517472N00549534EA0000002141
    +B1326304517486N00549537EA0000002134
    +B1326324517500N00549543EA0000002128
    +B1326344517513N00549550EA0000002123
    +B1326364517527N00549558EA0000002119
    +B1326384517540N00549568EA0000002117
    +B1326404517552N00549579EA0000002115
    +B1326424517564N00549592EA0000002113
    +B1326444517575N00549609EA0000002109
    +B1326464517584N00549627EA0000002107
    +B1326484517591N00549643EA0000002105
    +B1326504517598N00549656EA0000002102
    +B1326524517607N00549674EA0000002098
    +B1326544517616N00549696EA0000002094
    +B1326564517625N00549717EA0000002091
    +B1326584517635N00549735EA0000002088
    +B1327004517644N00549754EA0000002086
    +B1327024517654N00549775EA0000002083
    +B1327044517664N00549795EA0000002081
    +B1327064517675N00549813EA0000002079
    +B1327084517686N00549831EA0000002077
    +B1327104517702N00549858EA0000002072
    +B1327124517714N00549877EA0000002068
    +B1327144517727N00549896EA0000002063
    +B1327164517738N00549916EA0000002060
    +B1327184517750N00549936EA0000002057
    +B1327204517763N00549956EA0000002054
    +B1327224517778N00549971EA0000002053
    +B1327244517791N00549981EA0000002053
    +B1327264517800N00549992EA0000002052
    +B1327284517810N00550010EA0000002051
    +B1327304517817N00550028EA0000002050
    +B1327324517823N00550043EA0000002051
    +B1327354517830N00550058EA0000002051
    +B1327374517839N00550077EA0000002050
    +B1327394517849N00550096EA0000002051
    +B1327414517858N00550115EA0000002052
    +B1327434517868N00550133EA0000002054
    +B1327454517877N00550149EA0000002055
    +B1327474517887N00550165EA0000002056
    +B1327494517895N00550185EA0000002056
    +B1327514517904N00550206EA0000002055
    +B1327534517912N00550225EA0000002054
    +B1327554517919N00550244EA0000002052
    +B1327574517927N00550264EA0000002048
    +B1327594517935N00550285EA0000002043
    +B1328014517943N00550306EA0000002032
    +B1328034517952N00550326EA0000002029
    +B1328054517959N00550341EA0000002026
    +B1328074517965N00550354EA0000002023
    +B1328094517970N00550367EA0000002018
    +B1328114517980N00550383EA0000002013
    +B1328134517991N00550397EA0000002009
    +B1328154518001N00550412EA0000002004
    +B1328174518010N00550429EA0000001997
    +B1328194518020N00550450EA0000001989
    +B1328214518031N00550473EA0000001983
    +B1328234518041N00550493EA0000001977
    +B1328254518052N00550512EA0000001970
    +B1328274518063N00550531EA0000001962
    +B1328294518072N00550551EA0000001954
    +B1328314518083N00550570EA0000001948
    +B1328334518091N00550584EA0000001942
    +B1328354518100N00550598EA0000001935
    +B1328374518112N00550613EA0000001927
    +B1328394518125N00550628EA0000001922
    +B1328414518136N00550643EA0000001916
    +B1328434518147N00550657EA0000001909
    +B1328454518160N00550674EA0000001903
    +B1328474518171N00550690EA0000001899
    +B1328494518180N00550704EA0000001895
    +B1328514518193N00550726EA0000001885
    +B1328534518202N00550742EA0000001880
    +B1328554518209N00550758EA0000001876
    +B1328574518217N00550773EA0000001870
    +B1328594518224N00550791EA0000001865
    +B1329014518230N00550810EA0000001861
    +B1329034518235N00550826EA0000001857
    +B1329054518241N00550843EA0000001852
    +B1329074518247N00550861EA0000001847
    +B1329094518253N00550879EA0000001844
    +B1329114518260N00550894EA0000001841
    +B1329134518265N00550909EA0000001839
    +B1329164518269N00550924EA0000001837
    +B1329184518274N00550940EA0000001833
    +B1329204518279N00550957EA0000001830
    +B1329224518284N00550975EA0000001829
    +B1329244518289N00550992EA0000001829
    +B1329264518293N00551008EA0000001830
    +B1329284518298N00551023EA0000001833
    +B1329304518303N00551039EA0000001836
    +B1329324518308N00551054EA0000001839
    +B1329344518311N00551068EA0000001843
    +B1329364518313N00551079EA0000001846
    +B1329384518315N00551091EA0000001847
    +B1329404518317N00551105EA0000001849
    +B1329424518319N00551118EA0000001853
    +B1329444518321N00551126EA0000001858
    +B1329464518321N00551136EA0000001862
    +B1329484518320N00551146EA0000001868
    +B1329504518314N00551150EA0000001872
    +B1329524518307N00551143EA0000001876
    +B1329544518304N00551125EA0000001881
    +B1329564518314N00551112EA0000001884
    +B1329584518329N00551116EA0000001888
    +B1330004518333N00551132EA0000001891
    +B1330024518332N00551144EA0000001899
    +B1330044518330N00551155EA0000001905
    +B1330064518328N00551168EA0000001910
    +B1330084518328N00551177EA0000001915
    +B1330104518328N00551182EA0000001919
    +B1330124518322N00551188EA0000001920
    +B1330144518314N00551192EA0000001924
    +B1330164518305N00551184EA0000001925
    +B1330184518302N00551165EA0000001927
    +B1330204518309N00551152EA0000001929
    +B1330224518321N00551154EA0000001931
    +B1330244518326N00551167EA0000001934
    +B1330264518325N00551177EA0000001938
    +B1330284518326N00551187EA0000001940
    +B1330304518319N00551199EA0000001941
    +B1330324518308N00551196EA0000001939
    +B1330344518301N00551177EA0000001937
    +B1330364518303N00551159EA0000001934
    +B1330384518310N00551150EA0000001934
    +B1330404518318N00551158EA0000001933
    +B1330424518320N00551168EA0000001935
    +B1330444518324N00551176EA0000001935
    +B1330464518330N00551189EA0000001935
    +B1330484518336N00551200EA0000001936
    +B1330504518342N00551209EA0000001937
    +B1330524518347N00551220EA0000001936
    +B1330554518352N00551231EA0000001937
    +B1330574518357N00551239EA0000001939
    +B1330594518362N00551247EA0000001939
    +B1331014518368N00551256EA0000001940
    +B1331034518374N00551264EA0000001941
    +B1331054518380N00551272EA0000001942
    +B1331074518386N00551279EA0000001945
    +B1331094518393N00551286EA0000001946
    +B1331114518399N00551295EA0000001947
    +B1331134518406N00551303EA0000001947
    +B1331154518414N00551311EA0000001946
    +B1331174518421N00551320EA0000001945
    +B1331194518429N00551329EA0000001945
    +B1331214518436N00551338EA0000001944
    +B1331234518443N00551349EA0000001943
    +B1331254518450N00551360EA0000001941
    +B1331274518458N00551371EA0000001940
    +B1331294518466N00551382EA0000001938
    +B1331314518475N00551391EA0000001938
    +B1331334518482N00551398EA0000001938
    +B1331354518490N00551406EA0000001938
    +B1331374518498N00551412EA0000001939
    +B1331394518505N00551419EA0000001941
    +B1331414518511N00551425EA0000001943
    +B1331434518518N00551432EA0000001946
    +B1331454518525N00551440EA0000001949
    +B1331474518531N00551449EA0000001953
    +B1331494518533N00551460EA0000001957
    +B1331514518532N00551473EA0000001961
    +B1331534518524N00551481EA0000001965
    +B1331554518513N00551476EA0000001969
    +B1331574518507N00551462EA0000001973
    +B1331594518510N00551451EA0000001976
    +B1332014518522N00551450EA0000001980
    +B1332034518532N00551459EA0000001983
    +B1332054518539N00551466EA0000001987
    +B1332074518548N00551472EA0000001991
    +B1332094518564N00551486EA0000001996
    +B1332114518575N00551493EA0000002000
    +B1332134518584N00551502EA0000002004
    +B1332154518595N00551509EA0000002007
    +B1332174518607N00551514EA0000002012
    +B1332194518618N00551517EA0000002017
    +B1332214518629N00551521EA0000002024
    +B1332234518639N00551524EA0000002030
    +B1332254518649N00551526EA0000002037
    +B1332274518656N00551527EA0000002042
    +B1332294518663N00551529EA0000002046
    +B1332314518668N00551537EA0000002050
    +B1332344518664N00551550EA0000002053
    +B1332364518654N00551555EA0000002056
    +B1332384518646N00551547EA0000002060
    +B1332404518648N00551530EA0000002063
    +B1332424518658N00551522EA0000002068
    +B1332444518669N00551524EA0000002073
    +B1332464518675N00551531EA0000002078
    +B1332484518677N00551540EA0000002080
    +B1332504518676N00551553EA0000002083
    +B1332524518663N00551561EA0000002086
    +B1332544518652N00551555EA0000002088
    +B1332564518649N00551537EA0000002091
    +B1332584518654N00551527EA0000002094
    +B1333004518663N00551528EA0000002096
    +B1333024518673N00551532EA0000002099
    +B1333044518684N00551538EA0000002103
    +B1333064518694N00551545EA0000002108
    +B1333084518701N00551552EA0000002113
    +B1333104518705N00551562EA0000002118
    +B1333124518702N00551578EA0000002123
    +B1333144518692N00551588EA0000002128
    +B1333164518682N00551584EA0000002132
    +B1333184518677N00551569EA0000002137
    +B1333204518680N00551557EA0000002142
    +B1333224518687N00551556EA0000002147
    +B1333244518689N00551570EA0000002152
    +B1333264518686N00551586EA0000002156
    +B1333284518679N00551600EA0000002160
    +B1333304518675N00551603EA0000002166
    +B1333324518670N00551584EA0000002169
    +B1333344518677N00551571EA0000002171
    +B1333364518685N00551570EA0000002175
    +B1333384518694N00551590EA0000002180
    +B1333404518698N00551601EA0000002185
    +B1333424518705N00551613EA0000002190
    +B1333444518707N00551631EA0000002196
    +B1333464518699N00551646EA0000002200
    +B1333484518687N00551646EA0000002203
    +B1333504518682N00551622EA0000002205
    +B1333524518690N00551615EA0000002209
    +B1333544518700N00551621EA0000002214
    +B1333564518707N00551627EA0000002222
    +B1333584518714N00551630EA0000002229
    +B1334004518721N00551634EA0000002235
    +B1334024518729N00551635EA0000002241
    +B1334044518734N00551629EA0000002245
    +B1334064518733N00551617EA0000002249
    +B1334084518724N00551609EA0000002253
    +B1334104518713N00551605EA0000002257
    +B1334124518702N00551608EA0000002260
    +B1334154518693N00551619EA0000002265
    +B1334174518689N00551634EA0000002270
    +B1334194518689N00551648EA0000002275
    +B1334214518692N00551662EA0000002279
    +B1334234518698N00551672EA0000002282
    +B1334254518706N00551671EA0000002285
    +B1334274518714N00551660EA0000002287
    +B1334294518717N00551647EA0000002290
    +B1334314518712N00551636EA0000002294
    +B1334334518705N00551631EA0000002300
    +B1334354518696N00551631EA0000002305
    +B1334374518685N00551636EA0000002309
    +B1334394518678N00551649EA0000002313
    +B1334414518677N00551664EA0000002317
    +B1334434518681N00551677EA0000002320
    +B1334454518688N00551689EA0000002322
    +B1334474518697N00551698EA0000002323
    +B1334494518706N00551700EA0000002323
    +B1334514518715N00551699EA0000002320
    +B1334534518725N00551696EA0000002322
    +B1334554518733N00551700EA0000002326
    +B1334574518741N00551706EA0000002328
    +B1334594518750N00551712EA0000002332
    +B1335014518759N00551715EA0000002337
    +B1335034518765N00551717EA0000002339
    +B1335054518772N00551721EA0000002341
    +B1335074518780N00551724EA0000002341
    +B1335094518790N00551729EA0000002338
    +B1335114518799N00551735EA0000002336
    +B1335134518809N00551741EA0000002334
    +B1335154518820N00551746EA0000002333
    +B1335174518830N00551750EA0000002332
    +B1335194518839N00551756EA0000002332
    +B1335214518848N00551761EA0000002331
    +B1335234518859N00551767EA0000002329
    +B1335254518872N00551775EA0000002327
    +B1335274518885N00551782EA0000002326
    +B1335294518896N00551788EA0000002324
    +B1335314518913N00551797EA0000002319
    +B1335334518927N00551804EA0000002316
    +B1335354518939N00551811EA0000002314
    +B1335374518951N00551817EA0000002312
    +B1335394518963N00551823EA0000002309
    +B1335414518976N00551830EA0000002305
    +B1335434518988N00551837EA0000002303
    +B1335454519001N00551844EA0000002302
    +B1335474519013N00551851EA0000002299
    +B1335494519024N00551857EA0000002295
    +B1335514519035N00551864EA0000002290
    +B1335534519049N00551870EA0000002284
    +B1335564519063N00551874EA0000002280
    +B1335584519075N00551881EA0000002275
    +B1336004519087N00551887EA0000002269
    +B1336024519100N00551893EA0000002263
    +B1336044519114N00551904EA0000002258
    +B1336064519126N00551914EA0000002256
    +B1336084519137N00551920EA0000002254
    +B1336104519150N00551926EA0000002251
    +B1336124519162N00551933EA0000002249
    +B1336144519175N00551938EA0000002247
    +B1336164519188N00551946EA0000002244
    +B1336184519200N00551954EA0000002243
    +B1336204519213N00551960EA0000002240
    +B1336224519226N00551965EA0000002236
    +B1336244519240N00551971EA0000002234
    +B1336264519253N00551978EA0000002232
    +B1336284519264N00551985EA0000002229
    +B1336304519275N00551992EA0000002225
    +B1336324519289N00551998EA0000002221
    +B1336344519302N00552005EA0000002219
    +B1336364519314N00552013EA0000002217
    +B1336384519326N00552019EA0000002213
    +B1336404519339N00552027EA0000002210
    +B1336424519351N00552036EA0000002207
    +B1336444519363N00552043EA0000002206
    +B1336464519375N00552049EA0000002203
    +B1336484519387N00552057EA0000002200
    +B1336504519401N00552064EA0000002198
    +B1336524519412N00552072EA0000002197
    +B1336544519424N00552080EA0000002194
    +B1336564519436N00552089EA0000002190
    +B1336584519449N00552098EA0000002187
    +B1337004519461N00552108EA0000002185
    +B1337024519473N00552117EA0000002182
    +B1337044519484N00552127EA0000002178
    +B1337064519495N00552136EA0000002172
    +B1337084519508N00552146EA0000002167
    +B1337104519527N00552163EA0000002161
    +B1337124519537N00552172EA0000002158
    +B1337144519549N00552181EA0000002155
    +B1337164519561N00552190EA0000002152
    +B1337184519574N00552199EA0000002150
    +B1337204519586N00552208EA0000002150
    +B1337224519597N00552217EA0000002151
    +B1337244519608N00552227EA0000002152
    +B1337264519620N00552238EA0000002153
    +B1337284519632N00552248EA0000002155
    +B1337304519643N00552258EA0000002156
    +B1337324519654N00552267EA0000002157
    +B1337354519667N00552278EA0000002155
    +B1337374519680N00552290EA0000002155
    +B1337394519691N00552300EA0000002154
    +B1337414519701N00552309EA0000002152
    +B1337434519711N00552317EA0000002151
    +B1337454519721N00552322EA0000002151
    +B1337474519728N00552326EA0000002153
    +B1337494519734N00552331EA0000002152
    +B1337514519741N00552340EA0000002153
    +B1337534519745N00552351EA0000002156
    +B1337554519750N00552358EA0000002158
    +B1337574519757N00552362EA0000002159
    +B1337594519764N00552369EA0000002162
    +B1338014519772N00552375EA0000002164
    +B1338034519782N00552380EA0000002166
    +B1338054519792N00552387EA0000002168
    +B1338074519800N00552395EA0000002169
    +B1338094519811N00552401EA0000002170
    +B1338114519823N00552409EA0000002169
    +B1338134519835N00552417EA0000002171
    +B1338154519846N00552421EA0000002172
    +B1338174519857N00552426EA0000002175
    +B1338194519864N00552432EA0000002179
    +B1338214519869N00552437EA0000002180
    +B1338234519877N00552443EA0000002183
    +B1338254519884N00552449EA0000002188
    +B1338274519891N00552454EA0000002191
    +B1338294519899N00552460EA0000002193
    +B1338314519907N00552468EA0000002196
    +B1338334519912N00552483EA0000002198
    +B1338354519914N00552503EA0000002201
    +B1338374519905N00552519EA0000002204
    +B1338394519896N00552521EA0000002207
    +B1338414519891N00552508EA0000002209
    +B1338434519895N00552495EA0000002210
    +B1338454519908N00552494EA0000002217
    +B1338474519918N00552503EA0000002223
    +B1338494519932N00552517EA0000002229
    +B1338514519941N00552524EA0000002232
    +B1338534519952N00552533EA0000002235
    +B1338554519962N00552541EA0000002240
    +B1338574519967N00552545EA0000002245
    +B1338594519971N00552549EA0000002249
    +B1339014519975N00552560EA0000002252
    +B1339034519974N00552575EA0000002255
    +B1339054519961N00552579EA0000002248
    +B1339074519953N00552561EA0000002248
    +B1339094519957N00552547EA0000002249
    +B1339124519964N00552540EA0000002252
    +B1339144519970N00552535EA0000002255
    +B1339164519978N00552534EA0000002255
    +B1339184519989N00552537EA0000002258
    +B1339204519998N00552542EA0000002259
    +B1339224520007N00552546EA0000002260
    +B1339244520017N00552554EA0000002261
    +B1339264520026N00552561EA0000002262
    +B1339284520037N00552568EA0000002263
    +B1339304520050N00552579EA0000002262
    +B1339324520061N00552588EA0000002263
    +B1339344520069N00552595EA0000002264
    +B1339364520079N00552603EA0000002265
    +B1339384520088N00552611EA0000002267
    +B1339404520096N00552616EA0000002269
    +B1339424520107N00552623EA0000002271
    +B1339444520119N00552630EA0000002275
    +B1339464520130N00552636EA0000002279
    +B1339484520139N00552642EA0000002284
    +B1339504520146N00552651EA0000002290
    +B1339524520147N00552663EA0000002295
    +B1339544520141N00552672EA0000002299
    +B1339564520132N00552671EA0000002303
    +B1339584520123N00552659EA0000002307
    +B1340004520123N00552644EA0000002312
    +B1340024520131N00552637EA0000002318
    +B1340044520140N00552635EA0000002323
    +B1340064520149N00552639EA0000002328
    +B1340084520157N00552651EA0000002332
    +B1340104520158N00552666EA0000002336
    +B1340124520153N00552677EA0000002340
    +B1340144520145N00552677EA0000002342
    +B1340164520141N00552664EA0000002343
    +B1340184520148N00552652EA0000002345
    +B1340204520158N00552651EA0000002348
    +B1340224520168N00552651EA0000002350
    +B1340244520180N00552647EA0000002350
    +B1340264520193N00552650EA0000002350
    +B1340284520210N00552657EA0000002350
    +B1340304520223N00552663EA0000002350
    +B1340324520236N00552670EA0000002351
    +B1340344520247N00552675EA0000002352
    +B1340364520257N00552681EA0000002353
    +B1340384520269N00552689EA0000002352
    +B1340404520281N00552698EA0000002350
    +B1340424520296N00552706EA0000002348
    +B1340444520311N00552714EA0000002346
    +B1340464520325N00552722EA0000002344
    +B1340484520339N00552731EA0000002343
    +B1340504520355N00552741EA0000002340
    +B1340534520371N00552750EA0000002339
    +B1340554520384N00552761EA0000002339
    +B1340574520394N00552774EA0000002339
    +B1340594520407N00552785EA0000002337
    +B1341014520420N00552797EA0000002335
    +B1341034520433N00552808EA0000002335
    +B1341054520445N00552818EA0000002334
    +B1341074520456N00552828EA0000002333
    +B1341094520468N00552840EA0000002330
    +B1341114520480N00552855EA0000002326
    +B1341134520492N00552869EA0000002324
    +B1341154520504N00552883EA0000002321
    +B1341174520516N00552898EA0000002318
    +B1341194520529N00552912EA0000002315
    +B1341214520542N00552924EA0000002313
    +B1341234520555N00552939EA0000002311
    +B1341254520567N00552954EA0000002309
    +B1341274520580N00552969EA0000002306
    +B1341294520593N00552982EA0000002305
    +B1341314520606N00552994EA0000002303
    +B1341334520621N00553007EA0000002301
    +B1341354520635N00553020EA0000002301
    +B1341374520647N00553030EA0000002300
    +B1341394520662N00553039EA0000002299
    +B1341414520677N00553050EA0000002298
    +B1341434520690N00553061EA0000002298
    +B1341454520703N00553073EA0000002297
    +B1341474520716N00553088EA0000002295
    +B1341494520726N00553103EA0000002295
    +B1341514520737N00553116EA0000002294
    +B1341534520750N00553127EA0000002293
    +B1341554520762N00553140EA0000002290
    +B1341574520775N00553151EA0000002288
    +B1341594520788N00553162EA0000002287
    +B1342014520800N00553176EA0000002286
    +B1342034520811N00553187EA0000002285
    +B1342054520824N00553197EA0000002283
    +B1342074520845N00553216EA0000002279
    +B1342094520857N00553227EA0000002277
    +B1342114520870N00553238EA0000002275
    +B1342134520883N00553249EA0000002272
    +B1342154520896N00553261EA0000002270
    +B1342174520910N00553272EA0000002268
    +B1342194520923N00553279EA0000002266
    +B1342214520936N00553286EA0000002263
    +B1342234520947N00553297EA0000002260
    +B1342254520959N00553308EA0000002257
    +B1342274520970N00553318EA0000002254
    +B1342294520979N00553331EA0000002249
    +B1342324520990N00553342EA0000002244
    +B1342344521002N00553352EA0000002241
    +B1342364521013N00553364EA0000002239
    +B1342384521024N00553374EA0000002236
    +B1342404521036N00553383EA0000002234
    +B1342424521047N00553393EA0000002230
    +B1342444521058N00553402EA0000002226
    +B1342464521072N00553411EA0000002223
    +B1342484521084N00553422EA0000002222
    +B1342504521095N00553431EA0000002219
    +B1342524521108N00553440EA0000002215
    +B1342544521114N00553445EA0000002214
    +B1342564521130N00553458EA0000002212
    +B1342584521140N00553464EA0000002209
    +B1343004521154N00553474EA0000002206
    +B1343024521168N00553483EA0000002204
    +B1343044521180N00553491EA0000002203
    +B1343064521190N00553501EA0000002200
    +B1343084521201N00553512EA0000002195
    +B1343104521213N00553521EA0000002192
    +B1343124521225N00553529EA0000002188
    +B1343144521237N00553537EA0000002183
    +B1343164521250N00553545EA0000002180
    +B1343184521261N00553553EA0000002179
    +B1343204521273N00553560EA0000002176
    +B1343224521285N00553569EA0000002173
    +B1343244521297N00553581EA0000002170
    +B1343264521310N00553593EA0000002169
    +B1343284521322N00553604EA0000002168
    +B1343304521334N00553612EA0000002170
    +B1343324521344N00553620EA0000002174
    +B1343344521354N00553626EA0000002180
    +B1343364521363N00553634EA0000002183
    +B1343384521367N00553647EA0000002186
    +B1343404521361N00553659EA0000002188
    +B1343424521352N00553660EA0000002191
    +B1343444521346N00553644EA0000002197
    +B1343464521346N00553629EA0000002203
    +B1343484521354N00553612EA0000002209
    +B1343504521368N00553611EA0000002213
    +B1343524521379N00553623EA0000002216
    +B1343544521383N00553637EA0000002219
    +B1343564521380N00553651EA0000002223
    +B1343584521375N00553661EA0000002229
    +B1344004521367N00553665EA0000002234
    +B1344024521359N00553660EA0000002240
    +B1344044521354N00553648EA0000002245
    +B1344064521354N00553630EA0000002250
    +B1344084521365N00553621EA0000002255
    +B1344104521378N00553628EA0000002260
    +B1344134521383N00553644EA0000002265
    +B1344154521381N00553658EA0000002270
    +B1344174521378N00553668EA0000002277
    +B1344194521373N00553678EA0000002283
    +B1344214521366N00553683EA0000002289
    +B1344234521360N00553679EA0000002294
    +B1344254521356N00553666EA0000002298
    +B1344274521362N00553645EA0000002297
    +B1344294521373N00553641EA0000002301
    +B1344314521378N00553643EA0000002305
    +B1344334521393N00553656EA0000002309
    +B1344354521393N00553656EA0000002313
    +B1344374521412N00553673EA0000002317
    +B1344394521423N00553678EA0000002322
    +B1344414521432N00553684EA0000002326
    +B1344434521441N00553689EA0000002331
    +B1344454521451N00553694EA0000002336
    +B1344474521461N00553697EA0000002341
    +B1344494521471N00553701EA0000002346
    +B1344514521484N00553706EA0000002352
    +B1344534521497N00553711EA0000002359
    +B1344554521508N00553716EA0000002368
    +B1344574521518N00553722EA0000002379
    +B1344594521528N00553725EA0000002387
    +B1345014521540N00553730EA0000002393
    +B1345034521551N00553738EA0000002402
    +B1345054521557N00553747EA0000002410
    +B1345074521562N00553753EA0000002416
    +B1345094521569N00553758EA0000002420
    +B1345114521576N00553761EA0000002422
    +B1345134521585N00553763EA0000002423
    +B1345154521594N00553767EA0000002426
    +B1345174521604N00553769EA0000002428
    +B1345194521613N00553773EA0000002429
    +B1345214521623N00553778EA0000002430
    +B1345234521634N00553784EA0000002430
    +B1345254521645N00553792EA0000002431
    +B1345274521661N00553806EA0000002432
    +B1345294521671N00553812EA0000002432
    +B1345314521682N00553819EA0000002431
    +B1345334521693N00553827EA0000002430
    +B1345354521704N00553836EA0000002428
    +B1345374521715N00553844EA0000002426
    +B1345394521728N00553854EA0000002422
    +B1345414521743N00553863EA0000002420
    +B1345434521758N00553872EA0000002420
    +B1345454521771N00553881EA0000002418
    +B1345474521784N00553888EA0000002418
    +B1345504521796N00553893EA0000002417
    +B1345524521809N00553899EA0000002415
    +B1345544521824N00553905EA0000002413
    +B1345564521839N00553912EA0000002413
    +B1345584521853N00553918EA0000002413
    +B1346004521866N00553922EA0000002411
    +B1346024521881N00553929EA0000002409
    +B1346044521895N00553936EA0000002408
    +B1346064521909N00553944EA0000002407
    +B1346084521916N00553949EA0000002404
    +B1346104521938N00553963EA0000002400
    +B1346124521953N00553972EA0000002398
    +B1346144521969N00553977EA0000002394
    +B1346164521969N00553977EA0000002391
    +B1346184521999N00553993EA0000002387
    +B1346204522014N00554001EA0000002382
    +B1346224522030N00554009EA0000002378
    +B1346244522046N00554017EA0000002375
    +B1346264522060N00554024EA0000002372
    +B1346284522075N00554031EA0000002368
    +B1346304522090N00554039EA0000002365
    +B1346324522105N00554047EA0000002363
    +B1346344522119N00554053EA0000002361
    +B1346364522135N00554058EA0000002357
    +B1346384522151N00554062EA0000002356
    +B1346404522165N00554067EA0000002354
    +B1346424522180N00554071EA0000002351
    +B1346444522196N00554075EA0000002347
    +B1346464522211N00554080EA0000002345
    +B1346484522226N00554083EA0000002342
    +B1346504522242N00554085EA0000002338
    +B1346524522258N00554084EA0000002335
    +B1346544522272N00554084EA0000002333
    +B1346564522286N00554085EA0000002331
    +B1346584522300N00554085EA0000002328
    +B1347004522316N00554084EA0000002325
    +B1347024522331N00554083EA0000002324
    +B1347044522344N00554082EA0000002322
    +B1347064522365N00554082EA0000002317
    +B1347084522381N00554081EA0000002315
    +B1347104522396N00554080EA0000002314
    +B1347124522410N00554080EA0000002312
    +B1347144522425N00554080EA0000002309
    +B1347164522441N00554081EA0000002307
    +B1347184522457N00554082EA0000002306
    +B1347204522472N00554083EA0000002304
    +B1347224522488N00554086EA0000002301
    +B1347244522503N00554092EA0000002300
    +B1347264522517N00554098EA0000002299
    +B1347284522530N00554103EA0000002297
    +B1347314522543N00554109EA0000002294
    +B1347334522558N00554114EA0000002292
    +B1347354522571N00554118EA0000002289
    +B1347374522585N00554121EA0000002287
    +B1347394522598N00554125EA0000002285
    +B1347414522612N00554128EA0000002282
    +B1347434522627N00554132EA0000002279
    +B1347454522641N00554136EA0000002277
    +B1347474522654N00554140EA0000002274
    +B1347494522669N00554144EA0000002271
    +B1347514522683N00554148EA0000002270
    +B1347534522695N00554152EA0000002268
    +B1347554522709N00554156EA0000002264
    +B1347574522723N00554162EA0000002260
    +B1347594522737N00554169EA0000002257
    +B1348014522751N00554176EA0000002254
    +B1348034522765N00554183EA0000002251
    +B1348054522779N00554190EA0000002248
    +B1348074522793N00554196EA0000002246
    +B1348094522806N00554201EA0000002244
    +B1348114522819N00554206EA0000002240
    +B1348134522833N00554212EA0000002236
    +B1348154522847N00554216EA0000002234
    +B1348174522860N00554219EA0000002231
    +B1348194522873N00554224EA0000002227
    +B1348214522887N00554229EA0000002224
    +B1348234522901N00554235EA0000002222
    +B1348254522914N00554240EA0000002220
    +B1348274522925N00554244EA0000002219
    +B1348294522937N00554247EA0000002215
    +B1348314522951N00554251EA0000002212
    +B1348334522965N00554255EA0000002210
    +B1348354522976N00554256EA0000002210
    +B1348374522987N00554259EA0000002207
    +B1348394522998N00554261EA0000002204
    +B1348414523011N00554264EA0000002202
    +B1348434523022N00554267EA0000002202
    +B1348454523031N00554269EA0000002199
    +B1348474523049N00554273EA0000002191
    +B1348494523061N00554279EA0000002188
    +B1348514523073N00554283EA0000002185
    +B1348534523084N00554287EA0000002179
    +B1348554523096N00554290EA0000002173
    +B1348574523111N00554291EA0000002169
    +B1348594523124N00554294EA0000002167
    +B1349014523137N00554297EA0000002163
    +B1349034523152N00554300EA0000002161
    +B1349054523166N00554301EA0000002160
    +B1349074523179N00554303EA0000002161
    +B1349094523191N00554305EA0000002163
    +B1349124523201N00554306EA0000002165
    +B1349144523211N00554309EA0000002166
    +B1349164523222N00554312EA0000002169
    +B1349184523232N00554314EA0000002172
    +B1349204523242N00554318EA0000002174
    +B1349224523252N00554321EA0000002175
    +B1349244523264N00554322EA0000002175
    +B1349264523275N00554322EA0000002175
    +B1349284523286N00554321EA0000002175
    +B1349304523298N00554321EA0000002176
    +B1349324523310N00554322EA0000002179
    +B1349344523319N00554323EA0000002181
    +B1349364523329N00554324EA0000002181
    +B1349384523341N00554325EA0000002179
    +B1349404523356N00554325EA0000002178
    +B1349424523369N00554327EA0000002179
    +B1349444523381N00554331EA0000002177
    +B1349464523395N00554333EA0000002174
    +B1349484523409N00554338EA0000002173
    +B1349504523421N00554342EA0000002171
    +B1349524523432N00554343EA0000002168
    +B1349544523444N00554347EA0000002163
    +B1349564523459N00554353EA0000002157
    +B1349584523475N00554356EA0000002153
    +B1350004523488N00554361EA0000002150
    +B1350024523500N00554365EA0000002145
    +B1350044523514N00554365EA0000002139
    +B1350064523528N00554367EA0000002135
    +B1350084523541N00554370EA0000002132
    +B1350104523554N00554373EA0000002127
    +B1350124523568N00554376EA0000002122
    +B1350144523582N00554377EA0000002117
    +B1350164523596N00554376EA0000002112
    +B1350184523610N00554374EA0000002109
    +B1350204523623N00554372EA0000002106
    +B1350224523634N00554369EA0000002103
    +B1350244523647N00554366EA0000002098
    +B1350264523661N00554363EA0000002092
    +B1350284523682N00554360EA0000002088
    +B1350304523695N00554358EA0000002085
    +B1350324523708N00554357EA0000002080
    +B1350344523722N00554355EA0000002076
    +B1350364523735N00554353EA0000002073
    +B1350384523748N00554351EA0000002068
    +B1350404523761N00554347EA0000002064
    +B1350424523774N00554344EA0000002060
    +B1350444523787N00554339EA0000002056
    +B1350464523800N00554335EA0000002052
    +B1350484523813N00554330EA0000002050
    +B1350504523824N00554326EA0000002047
    +B1350534523837N00554322EA0000002042
    +B1350554523851N00554319EA0000002039
    +B1350574523864N00554316EA0000002036
    +B1350594523877N00554313EA0000002035
    +B1351014523889N00554310EA0000002032
    +B1351034523902N00554306EA0000002028
    +B1351054523915N00554303EA0000002025
    +B1351074523929N00554299EA0000002023
    +B1351094523942N00554296EA0000002021
    +B1351114523948N00554294EA0000002019
    +B1351134523968N00554292EA0000002016
    +B1351154523981N00554289EA0000002013
    +B1351174523981N00554289EA0000002011
    +B1351194524007N00554283EA0000002009
    +B1351214524021N00554279EA0000002007
    +B1351234524034N00554276EA0000002005
    +B1351254524048N00554274EA0000002003
    +B1351274524062N00554271EA0000002001
    +B1351294524076N00554268EA0000001998
    +B1351314524090N00554265EA0000001996
    +B1351334524103N00554262EA0000001994
    +B1351354524117N00554259EA0000001992
    +B1351374524131N00554257EA0000001990
    +B1351394524144N00554253EA0000001988
    +B1351414524158N00554250EA0000001986
    +B1351434524171N00554247EA0000001983
    +B1351454524184N00554243EA0000001979
    +B1351474524198N00554240EA0000001975
    +B1351494524212N00554238EA0000001972
    +B1351514524226N00554236EA0000001970
    +B1351534524241N00554235EA0000001967
    +B1351554524255N00554233EA0000001965
    +B1351574524269N00554229EA0000001965
    +B1351594524281N00554227EA0000001963
    +B1352014524295N00554224EA0000001960
    +B1352034524308N00554220EA0000001958
    +B1352054524321N00554217EA0000001956
    +B1352074524334N00554214EA0000001952
    +B1352094524355N00554209EA0000001947
    +B1352114524369N00554206EA0000001943
    +B1352134524381N00554198EA0000001940
    +B1352154524393N00554190EA0000001936
    +B1352174524405N00554184EA0000001932
    +B1352194524417N00554179EA0000001929
    +B1352214524429N00554176EA0000001927
    +B1352234524440N00554174EA0000001926
    +B1352254524451N00554171EA0000001923
    +B1352274524461N00554167EA0000001919
    +B1352294524474N00554164EA0000001916
    +B1352314524486N00554160EA0000001915
    +B1352344524497N00554156EA0000001913
    +B1352364524507N00554151EA0000001909
    +B1352384524519N00554147EA0000001906
    +B1352404524531N00554144EA0000001904
    +B1352424524542N00554142EA0000001901
    +B1352444524554N00554139EA0000001898
    +B1352464524567N00554136EA0000001894
    +B1352484524581N00554135EA0000001892
    +B1352504524593N00554136EA0000001891
    +B1352524524602N00554136EA0000001894
    +B1352544524613N00554137EA0000001899
    +B1352564524624N00554139EA0000001905
    +B1352584524624N00554139EA0000001914
    +B1353004524639N00554143EA0000001919
    +B1353024524647N00554142EA0000001925
    +B1353044524656N00554140EA0000001932
    +B1353064524663N00554137EA0000001938
    +B1353084524671N00554134EA0000001943
    +B1353104524681N00554132EA0000001949
    +B1353124524689N00554130EA0000001955
    +B1353144524694N00554124EA0000001960
    +B1353164524701N00554117EA0000001966
    +B1353184524709N00554112EA0000001971
    +B1353204524717N00554108EA0000001976
    +B1353224524727N00554104EA0000001981
    +B1353244524738N00554101EA0000001986
    +B1353264524748N00554098EA0000001989
    +B1353284524758N00554095EA0000001993
    +B1353304524767N00554091EA0000001997
    +B1353324524777N00554089EA0000002000
    +B1353344524786N00554088EA0000002002
    +B1353364524798N00554085EA0000002002
    +B1353384524810N00554084EA0000002003
    +B1353404524818N00554076EA0000002002
    +B1353424524822N00554063EA0000002001
    +B1353444524822N00554050EA0000002000
    +B1353464524814N00554041EA0000001999
    +B1353484524802N00554043EA0000001997
    +B1353504524794N00554067EA0000001995
    +B1353524524793N00554080EA0000001995
    +B1353544524790N00554090EA0000001995
    +B1353564524786N00554101EA0000001994
    +B1353584524779N00554112EA0000001994
    +B1354004524773N00554122EA0000001995
    +B1354024524766N00554133EA0000001997
    +B1354044524759N00554145EA0000002001
    +B1354064524753N00554156EA0000002006
    +B1354084524747N00554162EA0000002013
    +B1354104524738N00554163EA0000002020
    +B1354134524732N00554152EA0000002028
    +B1354154524736N00554137EA0000002034
    +B1354174524751N00554141EA0000002043
    +B1354194524755N00554163EA0000002051
    +B1354214524750N00554179EA0000002063
    +B1354234524743N00554187EA0000002073
    +B1354254524735N00554186EA0000002081
    +B1354274524731N00554172EA0000002088
    +B1354294524737N00554160EA0000002095
    +B1354314524751N00554165EA0000002101
    +B1354334524757N00554182EA0000002107
    +B1354354524749N00554202EA0000002116
    +B1354374524736N00554210EA0000002125
    +B1354394524732N00554209EA0000002131
    +B1354414524726N00554195EA0000002138
    +B1354434524730N00554184EA0000002145
    +B1354454524740N00554178EA0000002151
    +B1354474524753N00554184EA0000002156
    +B1354494524760N00554202EA0000002161
    +B1354514524761N00554220EA0000002166
    +B1354534524755N00554234EA0000002171
    +B1354554524745N00554238EA0000002175
    +B1354574524738N00554228EA0000002179
    +B1354594524740N00554216EA0000002183
    +B1355014524754N00554214EA0000002188
    +B1355034524765N00554226EA0000002192
    +B1355054524768N00554247EA0000002196
    +B1355074524764N00554265EA0000002202
    +B1355094524756N00554276EA0000002205
    +B1355114524745N00554274EA0000002207
    +B1355134524743N00554260EA0000002209
    +B1355154524752N00554253EA0000002213
    +B1355174524762N00554255EA0000002219
    +B1355194524770N00554259EA0000002226
    +B1355214524779N00554265EA0000002231
    +B1355234524787N00554278EA0000002233
    +B1355254524787N00554301EA0000002236
    +B1355274524777N00554314EA0000002239
    +B1355294524764N00554299EA0000002242
    +B1355314524769N00554286EA0000002244
    +B1355334524779N00554285EA0000002247
    +B1355354524788N00554293EA0000002248
    +B1355374524794N00554305EA0000002251
    +B1355394524797N00554319EA0000002253
    +B1355414524793N00554332EA0000002255
    +B1355434524784N00554336EA0000002256
    +B1355454524775N00554329EA0000002257
    +B1355474524774N00554315EA0000002257
    +B1355494524781N00554305EA0000002257
    +B1355524524790N00554300EA0000002258
    +B1355544524798N00554297EA0000002259
    +B1355564524809N00554297EA0000002261
    +B1355584524820N00554297EA0000002263
    +B1356004524830N00554299EA0000002268
    +B1356024524840N00554299EA0000002272
    +B1356044524850N00554301EA0000002277
    +B1356064524860N00554309EA0000002281
    +B1356084524870N00554319EA0000002286
    +B1356104524882N00554323EA0000002290
    +B1356124524895N00554320EA0000002293
    +B1356144524906N00554316EA0000002297
    +B1356164524914N00554309EA0000002299
    +B1356184524922N00554298EA0000002301
    +B1356204524926N00554286EA0000002302
    +B1356224524924N00554274EA0000002304
    +B1356244524917N00554265EA0000002304
    +B1356264524909N00554260EA0000002305
    +B1356284524901N00554256EA0000002305
    +B1356304524894N00554247EA0000002304
    +B1356324524890N00554232EA0000002303
    +B1356344524893N00554221EA0000002303
    +B1356364524899N00554215EA0000002304
    +B1356384524908N00554210EA0000002306
    +B1356404524917N00554207EA0000002308
    +B1356424524927N00554203EA0000002311
    +B1356444524936N00554199EA0000002315
    +B1356464524946N00554196EA0000002318
    +B1356484524957N00554195EA0000002323
    +B1356504524968N00554192EA0000002328
    +B1356524524979N00554190EA0000002334
    +B1356544524991N00554192EA0000002338
    +B1356564525003N00554192EA0000002341
    +B1356584525014N00554191EA0000002345
    +B1357004525026N00554193EA0000002349
    +B1357024525037N00554194EA0000002353
    +B1357044525046N00554189EA0000002358
    +B1357064525052N00554178EA0000002361
    +B1357084525050N00554162EA0000002365
    +B1357104525044N00554157EA0000002366
    +B1357124525036N00554156EA0000002368
    +B1357144525028N00554162EA0000002369
    +B1357164525024N00554175EA0000002369
    +B1357184525023N00554191EA0000002369
    +B1357204525027N00554207EA0000002372
    +B1357224525037N00554218EA0000002374
    +B1357244525050N00554221EA0000002377
    +B1357264525062N00554221EA0000002380
    +B1357294525075N00554222EA0000002383
    +B1357314525089N00554222EA0000002387
    +B1357334525101N00554221EA0000002392
    +B1357354525114N00554220EA0000002395
    +B1357374525127N00554222EA0000002399
    +B1357394525141N00554223EA0000002405
    +B1357414525153N00554222EA0000002411
    +B1357434525164N00554221EA0000002416
    +B1357454525178N00554220EA0000002420
    +B1357474525185N00554219EA0000002426
    +B1357494525206N00554216EA0000002432
    +B1357514525218N00554217EA0000002437
    +B1357534525218N00554217EA0000002442
    +B1357554525239N00554220EA0000002445
    +B1357574525252N00554220EA0000002446
    +B1357594525266N00554224EA0000002446
    +B1358014525278N00554226EA0000002445
    +B1358034525290N00554229EA0000002443
    +B1358054525303N00554235EA0000002441
    +B1358074525316N00554240EA0000002441
    +B1358094525328N00554243EA0000002441
    +B1358114525338N00554247EA0000002438
    +B1358134525350N00554253EA0000002435
    +B1358154525364N00554258EA0000002431
    +B1358174525377N00554262EA0000002428
    +B1358194525390N00554266EA0000002424
    +B1358214525404N00554271EA0000002421
    +B1358234525417N00554277EA0000002418
    +B1358254525429N00554282EA0000002416
    +B1358274525442N00554288EA0000002412
    +B1358294525455N00554294EA0000002410
    +B1358314525467N00554300EA0000002408
    +B1358334525479N00554306EA0000002405
    +B1358354525491N00554313EA0000002401
    +B1358374525505N00554318EA0000002399
    +B1358394525517N00554320EA0000002397
    +B1358414525530N00554325EA0000002394
    +B1358434525542N00554328EA0000002391
    +B1358454525559N00554332EA0000002385
    +B1358474525572N00554336EA0000002379
    +B1358494525586N00554342EA0000002375
    +B1358514525600N00554346EA0000002373
    +B1358534525614N00554352EA0000002371
    +B1358554525627N00554361EA0000002370
    +B1358574525640N00554367EA0000002370
    +B1358594525650N00554370EA0000002371
    +B1359014525657N00554375EA0000002371
    +B1359034525667N00554380EA0000002368
    +B1359054525680N00554386EA0000002365
    +B1359074525694N00554392EA0000002364
    +B1359104525706N00554398EA0000002364
    +B1359124525716N00554404EA0000002363
    +B1359144525729N00554409EA0000002360
    +B1359164525742N00554412EA0000002359
    +B1359184525754N00554416EA0000002359
    +B1359204525765N00554423EA0000002358
    +B1359224525778N00554431EA0000002358
    +B1359244525791N00554439EA0000002360
    +B1359264525802N00554447EA0000002363
    +B1359284525814N00554454EA0000002364
    +B1359304525827N00554459EA0000002366
    +B1359324525840N00554465EA0000002368
    +B1359344525840N00554465EA0000002368
    +B1359364525866N00554480EA0000002368
    +B1359384525881N00554488EA0000002368
    +B1359404525894N00554495EA0000002369
    +B1359424525907N00554502EA0000002369
    +B1359444525921N00554506EA0000002371
    +B1359464525933N00554512EA0000002374
    +B1359484525943N00554521EA0000002374
    +B1359504525955N00554527EA0000002373
    +B1359524525967N00554533EA0000002374
    +B1359544525978N00554542EA0000002376
    +B1359564525991N00554549EA0000002379
    +B1359584526004N00554557EA0000002384
    +B1400004526014N00554565EA0000002390
    +B1400024526021N00554570EA0000002395
    +B1400044526028N00554575EA0000002398
    +B1400064526033N00554589EA0000002401
    +B1400084526031N00554608EA0000002403
    +B1400104526019N00554617EA0000002405
    +B1400124526010N00554610EA0000002408
    +B1400144526007N00554601EA0000002413
    +B1400164526008N00554593EA0000002415
    +B1400184526013N00554587EA0000002417
    +B1400204526021N00554584EA0000002419
    +B1400224526032N00554592EA0000002421
    +B1400244526037N00554608EA0000002422
    +B1400264526027N00554630EA0000002425
    +B1400284526017N00554627EA0000002426
    +B1400304526016N00554612EA0000002428
    +B1400324526025N00554604EA0000002429
    +B1400344526036N00554607EA0000002430
    +B1400364526047N00554611EA0000002433
    +B1400384526057N00554612EA0000002434
    +B1400404526068N00554616EA0000002433
    +B1400424526080N00554624EA0000002431
    +B1400454526093N00554630EA0000002427
    +B1400474526106N00554634EA0000002422
    +B1400494526121N00554640EA0000002418
    +B1400514526135N00554648EA0000002415
    +B1400534526147N00554654EA0000002413
    +B1400554526160N00554656EA0000002410
    +B1400574526175N00554660EA0000002407
    +B1400594526190N00554664EA0000002404
    +B1401014526204N00554666EA0000002402
    +B1401034526217N00554669EA0000002401
    +B1401054526230N00554670EA0000002401
    +B1401074526243N00554670EA0000002401
    +B1401094526257N00554671EA0000002400
    +B1401114526272N00554672EA0000002399
    +B1401134526285N00554672EA0000002400
    +B1401154526298N00554674EA0000002400
    +B1401174526313N00554676EA0000002401
    +B1401194526327N00554676EA0000002402
    +B1401214526341N00554675EA0000002404
    +B1401234526354N00554674EA0000002408
    +B1401254526367N00554672EA0000002411
    +B1401274526382N00554672EA0000002414
    +B1401294526396N00554673EA0000002419
    +B1401314526404N00554673EA0000002424
    +B1401334526412N00554672EA0000002428
    +B1401354526422N00554672EA0000002430
    +B1401374526433N00554672EA0000002431
    +B1401394526445N00554674EA0000002431
    +B1401414526459N00554674EA0000002430
    +B1401434526475N00554674EA0000002429
    +B1401454526491N00554677EA0000002429
    +B1401474526505N00554681EA0000002430
    +B1401494526519N00554684EA0000002430
    +B1401514526534N00554684EA0000002430
    +B1401534526549N00554685EA0000002429
    +B1401554526565N00554689EA0000002429
    +B1401574526579N00554692EA0000002429
    +B1401594526594N00554695EA0000002427
    +B1402014526619N00554706EA0000002425
    +B1402034526633N00554713EA0000002425
    +B1402054526646N00554717EA0000002425
    +B1402074526658N00554721EA0000002425
    +B1402094526670N00554723EA0000002424
    +B1402114526682N00554725EA0000002422
    +B1402134526696N00554727EA0000002418
    +B1402154526709N00554733EA0000002414
    +B1402174526722N00554737EA0000002410
    +B1402194526737N00554740EA0000002406
    +B1402214526753N00554749EA0000002401
    +B1402234526768N00554756EA0000002397
    +B1402264526784N00554759EA0000002392
    +B1402284526800N00554764EA0000002386
    +B1402304526816N00554773EA0000002380
    +B1402324526834N00554778EA0000002375
    +B1402344526849N00554784EA0000002370
    +B1402364526864N00554791EA0000002364
    +B1402384526880N00554797EA0000002359
    +B1402404526896N00554805EA0000002354
    +B1402424526908N00554816EA0000002350
    +B1402444526921N00554825EA0000002344
    +B1402464526936N00554833EA0000002338
    +B1402484526951N00554843EA0000002333
    +B1402504526951N00554843EA0000002328
    +B1402524526978N00554861EA0000002322
    +B1402544526992N00554871EA0000002317
    +B1402564527005N00554880EA0000002314
    +B1402584527018N00554886EA0000002311
    +B1403004527031N00554893EA0000002307
    +B1403024527045N00554900EA0000002302
    +B1403044527061N00554908EA0000002296
    +B1403064527078N00554915EA0000002291
    +B1403084527095N00554921EA0000002288
    +B1403104527111N00554928EA0000002285
    +B1403124527127N00554936EA0000002280
    +B1403144527144N00554944EA0000002276
    +B1403164527161N00554953EA0000002273
    +B1403184527178N00554962EA0000002269
    +B1403204527195N00554970EA0000002266
    +B1403224527212N00554979EA0000002263
    +B1403244527229N00554988EA0000002260
    +B1403264527246N00554996EA0000002258
    +B1403284527262N00555006EA0000002256
    +B1403304527278N00555017EA0000002253
    +B1403324527294N00555027EA0000002249
    +B1403344527310N00555036EA0000002245
    +B1403364527326N00555049EA0000002241
    +B1403384527341N00555060EA0000002237
    +B1403404527358N00555073EA0000002234
    +B1403424527381N00555094EA0000002229
    +B1403444527396N00555107EA0000002225
    +B1403464527412N00555121EA0000002221
    +B1403484527428N00555135EA0000002217
    +B1403504527443N00555148EA0000002215
    +B1403524527458N00555159EA0000002212
    +B1403544527474N00555172EA0000002207
    +B1403564527490N00555185EA0000002203
    +B1403584527507N00555197EA0000002201
    +B1404004527523N00555209EA0000002198
    +B1404024527538N00555219EA0000002195
    +B1404044527554N00555230EA0000002190
    +B1404074527571N00555241EA0000002186
    +B1404094527587N00555251EA0000002184
    +B1404114527603N00555263EA0000002181
    +B1404134527617N00555276EA0000002177
    +B1404154527632N00555289EA0000002174
    +B1404174527648N00555300EA0000002171
    +B1404194527664N00555309EA0000002167
    +B1404214527680N00555318EA0000002164
    +B1404234527696N00555327EA0000002160
    +B1404254527713N00555333EA0000002157
    +B1404274527730N00555338EA0000002153
    +B1404294527746N00555343EA0000002148
    +B1404314527746N00555343EA0000002144
    +B1404334527781N00555350EA0000002140
    +B1404354527798N00555354EA0000002136
    +B1404374527814N00555362EA0000002132
    +B1404394527831N00555370EA0000002127
    +B1404414527848N00555377EA0000002122
    +B1404434527864N00555387EA0000002118
    +B1404454527881N00555396EA0000002114
    +B1404474527898N00555402EA0000002110
    +B1404494527915N00555407EA0000002105
    +B1404514527931N00555411EA0000002101
    +B1404534527948N00555412EA0000002096
    +B1404554527959N00555413EA0000002092
    +B1404574527965N00555412EA0000002088
    +B1404594527973N00555409EA0000002084
    +B1405014527986N00555404EA0000002079
    +B1405034527995N00555400EA0000002075
    +B1405054528002N00555398EA0000002073
    +B1405074528011N00555395EA0000002070
    +B1405094528023N00555392EA0000002069
    +B1405114528034N00555394EA0000002068
    +B1405134528044N00555398EA0000002067
    +B1405154528055N00555403EA0000002064
    +B1405174528066N00555412EA0000002064
    +B1405194528075N00555422EA0000002064
    +B1405214528084N00555433EA0000002063
    +B1405234528099N00555450EA0000002061
    +B1405254528108N00555459EA0000002060
    +B1405274528118N00555469EA0000002059
    +B1405294528128N00555480EA0000002058
    +B1405314528138N00555491EA0000002057
    +B1405334528146N00555503EA0000002056
    +B1405354528155N00555515EA0000002053
    +B1405374528167N00555528EA0000002049
    +B1405394528180N00555541EA0000002048
    +B1405414528191N00555553EA0000002046
    +B1405434528202N00555566EA0000002043
    +B1405454528214N00555578EA0000002040
    +B1405484528226N00555589EA0000002037
    +B1405504528237N00555599EA0000002034
    +B1405524528249N00555610EA0000002029
    +B1405544528262N00555621EA0000002026
    +B1405564528274N00555631EA0000002023
    +B1405584528285N00555642EA0000002020
    +B1406004528296N00555652EA0000002016
    +B1406024528308N00555664EA0000002010
    +B1406044528320N00555677EA0000002006
    +B1406064528330N00555690EA0000002003
    +B1406084528340N00555702EA0000001999
    +B1406104528351N00555714EA0000001993
    +B1406124528362N00555726EA0000001988
    +B1406144528373N00555740EA0000001985
    +B1406164528383N00555754EA0000001983
    +B1406184528393N00555767EA0000001981
    +B1406204528405N00555779EA0000001980
    +B1406224528416N00555791EA0000001979
    +B1406244528426N00555801EA0000001978
    +B1406264528437N00555813EA0000001976
    +B1406284528449N00555824EA0000001975
    +B1406304528459N00555835EA0000001973
    +B1406324528471N00555847EA0000001970
    +B1406344528482N00555859EA0000001968
    +B1406364528494N00555869EA0000001966
    +B1406384528505N00555881EA0000001963
    +B1406404528516N00555894EA0000001962
    +B1406424528527N00555907EA0000001961
    +B1406444528538N00555921EA0000001961
    +B1406464528549N00555934EA0000001961
    +B1406484528558N00555947EA0000001961
    +B1406504528566N00555961EA0000001961
    +B1406524528575N00555973EA0000001961
    +B1406544528584N00555984EA0000001961
    +B1406564528592N00555996EA0000001960
    +B1406584528600N00556007EA0000001958
    +B1407004528609N00556019EA0000001956
    +B1407024528622N00556036EA0000001953
    +B1407044528631N00556046EA0000001951
    +B1407064528641N00556057EA0000001950
    +B1407084528651N00556067EA0000001949
    +B1407104528661N00556078EA0000001948
    +B1407124528670N00556088EA0000001947
    +B1407144528679N00556100EA0000001945
    +B1407164528687N00556114EA0000001943
    +B1407184528694N00556129EA0000001940
    +B1407204528700N00556145EA0000001938
    +B1407224528705N00556163EA0000001934
    +B1407244528710N00556181EA0000001930
    +B1407274528718N00556196EA0000001926
    +B1407294528726N00556211EA0000001922
    +B1407314528735N00556225EA0000001917
    +B1407334528744N00556240EA0000001913
    +B1407354528753N00556255EA0000001910
    +B1407374528762N00556269EA0000001907
    +B1407394528772N00556282EA0000001903
    +B1407414528781N00556296EA0000001900
    +B1407434528791N00556310EA0000001897
    +B1407454528799N00556324EA0000001894
    +B1407474528809N00556338EA0000001890
    +B1407494528818N00556353EA0000001887
    +B1407514528826N00556367EA0000001884
    +B1407534528835N00556381EA0000001880
    +B1407554528844N00556396EA0000001877
    +B1407574528852N00556410EA0000001874
    +B1407594528860N00556424EA0000001870
    +B1408014528869N00556438EA0000001865
    +B1408034528878N00556451EA0000001861
    +B1408054528886N00556467EA0000001857
    +B1408074528895N00556482EA0000001855
    +B1408094528904N00556494EA0000001852
    +B1408114528914N00556507EA0000001849
    +B1408134528924N00556520EA0000001847
    +B1408154528934N00556532EA0000001845
    +B1408174528944N00556543EA0000001843
    +B1408194528954N00556552EA0000001841
    +B1408214528965N00556559EA0000001840
    +B1408234528975N00556567EA0000001838
    +B1408254528987N00556573EA0000001835
    +B1408274528998N00556578EA0000001834
    +B1408294529010N00556582EA0000001832
    +B1408314529021N00556586EA0000001830
    +B1408334529033N00556589EA0000001829
    +B1408354529045N00556592EA0000001828
    +B1408374529056N00556597EA0000001826
    +B1408394529068N00556603EA0000001823
    +B1408414529082N00556620EA0000001818
    +B1408434529092N00556632EA0000001815
    +B1408454529103N00556643EA0000001812
    +B1408474529114N00556652EA0000001809
    +B1408494529124N00556661EA0000001807
    +B1408514529135N00556669EA0000001805
    +B1408534529146N00556679EA0000001802
    +B1408554529156N00556689EA0000001799
    +B1408574529167N00556699EA0000001796
    +B1408594529177N00556708EA0000001794
    +B1409014529187N00556717EA0000001792
    +B1409044529198N00556726EA0000001789
    +B1409064529208N00556735EA0000001787
    +B1409084529219N00556743EA0000001784
    +B1409104529230N00556750EA0000001780
    +B1409124529241N00556757EA0000001777
    +B1409144529252N00556765EA0000001774
    +B1409164529263N00556772EA0000001771
    +B1409184529274N00556779EA0000001768
    +B1409204529285N00556786EA0000001766
    +B1409224529295N00556795EA0000001763
    +B1409244529306N00556803EA0000001760
    +B1409264529316N00556812EA0000001758
    +B1409284529326N00556823EA0000001755
    +B1409304529337N00556833EA0000001752
    +B1409324529348N00556843EA0000001750
    +B1409344529358N00556853EA0000001747
    +B1409364529370N00556863EA0000001745
    +B1409384529381N00556872EA0000001742
    +B1409404529391N00556883EA0000001740
    +B1409424529402N00556894EA0000001737
    +B1409444529413N00556904EA0000001734
    +B1409464529423N00556915EA0000001730
    +B1409484529433N00556929EA0000001727
    +B1409504529442N00556942EA0000001724
    +B1409524529452N00556955EA0000001721
    +B1409544529461N00556969EA0000001718
    +B1409564529470N00556983EA0000001716
    +B1409584529480N00556996EA0000001713
    +B1410004529490N00557008EA0000001711
    +B1410024529500N00557021EA0000001708
    +B1410044529510N00557034EA0000001706
    +B1410064529520N00557047EA0000001704
    +B1410084529529N00557061EA0000001701
    +B1410104529537N00557075EA0000001700
    +B1410124529545N00557089EA0000001699
    +B1410144529554N00557102EA0000001697
    +B1410164529563N00557116EA0000001695
    +B1410184529570N00557130EA0000001694
    +B1410204529582N00557150EA0000001692
    +B1410224529590N00557163EA0000001691
    +B1410244529599N00557174EA0000001690
    +B1410264529607N00557186EA0000001688
    +B1410284529616N00557198EA0000001685
    +B1410304529625N00557210EA0000001682
    +B1410324529634N00557222EA0000001680
    +B1410344529642N00557234EA0000001677
    +B1410364529651N00557246EA0000001674
    +B1410384529660N00557258EA0000001671
    +B1410404529668N00557269EA0000001669
    +B1410424529676N00557281EA0000001666
    +B1410454529684N00557293EA0000001664
    +B1410474529693N00557304EA0000001662
    +B1410494529701N00557316EA0000001660
    +B1410514529709N00557328EA0000001658
    +B1410534529717N00557341EA0000001655
    +B1410554529724N00557354EA0000001653
    +B1410574529732N00557366EA0000001650
    +B1410594529741N00557378EA0000001647
    +B1411014529750N00557390EA0000001644
    +B1411034529758N00557404EA0000001641
    +B1411054529768N00557419EA0000001636
    +B1411074529777N00557434EA0000001633
    +B1411094529785N00557448EA0000001630
    +B1411114529794N00557462EA0000001626
    +B1411134529804N00557476EA0000001621
    +B1411154529814N00557489EA0000001618
    +B1411174529823N00557502EA0000001614
    +B1411194529834N00557514EA0000001610
    +B1411214529845N00557526EA0000001606
    +B1411234529856N00557537EA0000001603
    +B1411254529866N00557549EA0000001600
    +B1411274529876N00557562EA0000001597
    +B1411294529885N00557575EA0000001594
    +B1411314529895N00557590EA0000001591
    +B1411334529904N00557605EA0000001589
    +B1411354529913N00557619EA0000001586
    +B1411374529922N00557633EA0000001583
    +B1411394529932N00557646EA0000001580
    +B1411414529941N00557660EA0000001577
    +B1411434529951N00557672EA0000001573
    +B1411454529961N00557685EA0000001570
    +B1411474529971N00557699EA0000001567
    +B1411494529980N00557713EA0000001564
    +B1411514529989N00557726EA0000001561
    +B1411534529998N00557739EA0000001557
    +B1411554530008N00557753EA0000001554
    +B1411574530022N00557772EA0000001550
    +B1411594530032N00557784EA0000001547
    +B1412014530041N00557796EA0000001544
    +B1412034530051N00557808EA0000001542
    +B1412054530061N00557818EA0000001539
    +B1412074530071N00557829EA0000001536
    +B1412094530082N00557841EA0000001533
    +B1412114530092N00557853EA0000001531
    +B1412134530102N00557864EA0000001528
    +B1412154530113N00557874EA0000001525
    +B1412174530123N00557886EA0000001522
    +B1412194530133N00557898EA0000001521
    +B1412224530143N00557909EA0000001518
    +B1412244530153N00557919EA0000001515
    +B1412264530163N00557931EA0000001512
    +B1412284530173N00557942EA0000001510
    +B1412304530183N00557952EA0000001507
    +B1412324530192N00557963EA0000001504
    +B1412344530202N00557974EA0000001502
    +B1412364530212N00557985EA0000001499
    +B1412384530222N00557996EA0000001497
    +B1412404530232N00558007EA0000001494
    +B1412424530242N00558018EA0000001491
    +B1412444530251N00558030EA0000001489
    +B1412464530261N00558042EA0000001486
    +B1412484530271N00558054EA0000001484
    +B1412504530280N00558067EA0000001481
    +B1412524530289N00558080EA0000001478
    +B1412544530298N00558092EA0000001475
    +B1412564530307N00558105EA0000001473
    +B1412584530317N00558118EA0000001470
    +B1413004530326N00558131EA0000001467
    +B1413024530335N00558143EA0000001465
    +B1413044530345N00558155EA0000001462
    +B1413064530354N00558168EA0000001459
    +B1413084530363N00558180EA0000001457
    +B1413104530373N00558192EA0000001455
    +B1413124530383N00558204EA0000001452
    +B1413144530392N00558216EA0000001449
    +B1413164530402N00558228EA0000001447
    +B1413184530412N00558240EA0000001444
    +B1413204530421N00558252EA0000001441
    +B1413224530431N00558264EA0000001439
    +B1413244530441N00558277EA0000001436
    +B1413264530450N00558290EA0000001434
    +B1413284530460N00558302EA0000001431
    +B1413304530469N00558316EA0000001429
    +B1413324530477N00558329EA0000001426
    +B1413344530485N00558343EA0000001424
    +B1413364530498N00558365EA0000001419
    +B1413384530506N00558378EA0000001416
    +B1413404530514N00558391EA0000001413
    +B1413424530523N00558405EA0000001410
    +B1413444530531N00558419EA0000001407
    +B1413464530539N00558433EA0000001404
    +B1413484530547N00558447EA0000001401
    +B1413504530555N00558461EA0000001399
    +B1413524530564N00558474EA0000001396
    +B1413544530572N00558488EA0000001393
    +B1413564530581N00558502EA0000001390
    +B1413584530589N00558515EA0000001388
    +B1414004530597N00558528EA0000001385
    +B1414034530605N00558541EA0000001382
    +B1414054530614N00558554EA0000001379
    +B1414074530622N00558567EA0000001377
    +B1414094530630N00558581EA0000001374
    +B1414114530638N00558595EA0000001372
    +B1414134530646N00558608EA0000001370
    +B1414154530653N00558621EA0000001368
    +B1414174530661N00558634EA0000001365
    +B1414194530669N00558647EA0000001363
    +B1414214530676N00558660EA0000001360
    +B1414234530683N00558674EA0000001357
    +B1414254530689N00558688EA0000001354
    +B1414274530696N00558702EA0000001351
    +B1414294530703N00558716EA0000001348
    +B1414314530710N00558729EA0000001345
    +B1414334530717N00558743EA0000001340
    +B1414354530725N00558757EA0000001337
    +B1414374530732N00558770EA0000001334
    +B1414394530740N00558783EA0000001331
    +B1414414530748N00558795EA0000001328
    +B1414434530757N00558806EA0000001325
    +B1414454530766N00558819EA0000001322
    +B1414474530774N00558831EA0000001319
    +B1414494530782N00558844EA0000001316
    +B1414514530790N00558858EA0000001312
    +B1414534530800N00558873EA0000001309
    +B1414554530809N00558888EA0000001307
    +B1414574530817N00558903EA0000001305
    +B1414594530826N00558917EA0000001301
    +B1415014530835N00558932EA0000001298
    +B1415034530844N00558947EA0000001296
    +B1415054530852N00558961EA0000001294
    +B1415074530860N00558976EA0000001292
    +B1415094530868N00558990EA0000001290
    +B1415114530876N00559003EA0000001287
    +B1415134530883N00559018EA0000001284
    +B1415154530895N00559040EA0000001281
    +B1415174530902N00559054EA0000001279
    +B1415194530909N00559069EA0000001277
    +B1415214530916N00559083EA0000001275
    +B1415234530923N00559097EA0000001272
    +B1415254530930N00559111EA0000001270
    +B1415274530938N00559123EA0000001267
    +B1415294530946N00559136EA0000001264
    +B1415314530955N00559150EA0000001261
    +B1415334530963N00559163EA0000001259
    +B1415354530971N00559177EA0000001256
    +B1415374530980N00559191EA0000001253
    +B1415404530989N00559205EA0000001252
    +B1415424530996N00559219EA0000001251
    +B1415444531002N00559232EA0000001248
    +B1415464531012N00559242EA0000001245
    +B1415484531022N00559252EA0000001243
    +B1415504531030N00559263EA0000001240
    +B1415524531037N00559275EA0000001236
    +B1415544531045N00559289EA0000001232
    +B1415564531052N00559304EA0000001229
    +B1415584531058N00559317EA0000001226
    +B1416004531064N00559330EA0000001221
    +B1416024531071N00559346EA0000001217
    +B1416044531077N00559362EA0000001212
    +B1416064531084N00559378EA0000001209
    +B1416084531090N00559392EA0000001205
    +B1416104531096N00559407EA0000001201
    +B1416124531103N00559422EA0000001197
    +B1416144531109N00559438EA0000001193
    +B1416164531114N00559455EA0000001187
    +B1416184531119N00559472EA0000001183
    +B1416204531125N00559488EA0000001179
    +B1416224531131N00559503EA0000001175
    +B1416244531138N00559519EA0000001171
    +B1416264531143N00559536EA0000001167
    +B1416284531149N00559553EA0000001164
    +B1416304531154N00559570EA0000001160
    +B1416324531160N00559586EA0000001158
    +B1416344531166N00559600EA0000001154
    +B1416364531172N00559615EA0000001151
    +B1416384531179N00559633EA0000001149
    +B1416404531186N00559651EA0000001147
    +B1416424531194N00559670EA0000001146
    +B1416444531201N00559687EA0000001147
    +B1416464531207N00559700EA0000001145
    +B1416484531215N00559713EA0000001142
    +B1416504531223N00559729EA0000001141
    +B1416524531229N00559744EA0000001141
    +B1416544531235N00559758EA0000001138
    +B1416564531246N00559782EA0000001133
    +B1416584531253N00559799EA0000001130
    +B1417004531259N00559815EA0000001126
    +B1417024531267N00559831EA0000001123
    +B1417044531273N00559846EA0000001119
    +B1417064531280N00559863EA0000001115
    +B1417084531287N00559880EA0000001110
    +B1417104531294N00559898EA0000001107
    +B1417124531301N00559915EA0000001104
    +B1417144531308N00559930EA0000001101
    +B1417164531315N00559946EA0000001098
    +B1417184531322N00559963EA0000001095
    +B1417214531329N00559979EA0000001093
    +B1417234531334N00559995EA0000001090
    +B1417254531341N00600011EA0000001085
    +B1417274531349N00600028EA0000001081
    +B1417294531355N00600044EA0000001078
    +B1417314531361N00600059EA0000001074
    +B1417334531367N00600078EA0000001068
    +B1417354531374N00600097EA0000001064
    +B1417374531381N00600114EA0000001062
    +B1417394531387N00600131EA0000001059
    +B1417414531394N00600148EA0000001054
    +B1417434531403N00600165EA0000001050
    +B1417454531407N00600174EA0000001048
    +B1417474531417N00600199EA0000001045
    +B1417494531424N00600214EA0000001041
    +B1417514531431N00600232EA0000001038
    +B1417534531438N00600250EA0000001035
    +B1417554531445N00600268EA0000001033
    +B1417574531451N00600283EA0000001033
    +B1417594531456N00600299EA0000001030
    +B1418014531463N00600316EA0000001027
    +B1418034531470N00600335EA0000001026
    +B1418054531476N00600350EA0000001025
    +B1418074531483N00600364EA0000001023
    +B1418094531489N00600380EA0000001022
    +B1418114531495N00600394EA0000001021
    +B1418134531502N00600407EA0000001021
    +B1418154531508N00600420EA0000001019
    +B1418174531514N00600433EA0000001017
    +B1418194531522N00600446EA0000001014
    +B1418214531529N00600461EA0000001011
    +B1418234531535N00600475EA0000001008
    +B1418254531542N00600491EA0000001004
    +B1418274531550N00600507EA0000001001
    +B1418294531556N00600524EA0000000999
    +B1418314531563N00600539EA0000000997
    +B1418334531570N00600554EA0000000995
    +B1418354531579N00600575EA0000000989
    +B1418374531587N00600588EA0000000984
    +B1418394531594N00600601EA0000000980
    +B1418414531601N00600614EA0000000975
    +B1418434531608N00600626EA0000000970
    +B1418454531616N00600641EA0000000965
    +B1418474531622N00600656EA0000000962
    +B1418494531628N00600671EA0000000957
    +B1418514531635N00600687EA0000000953
    +B1418534531643N00600702EA0000000949
    +B1418554531650N00600716EA0000000946
    +B1418574531657N00600730EA0000000943
    +B1419004531666N00600745EA0000000940
    +B1419024531673N00600760EA0000000939
    +B1419044531680N00600774EA0000000938
    +B1419064531687N00600788EA0000000937
    +B1419084531694N00600803EA0000000936
    +B1419104531701N00600819EA0000000936
    +B1419124531707N00600832EA0000000937
    +B1419144531714N00600845EA0000000936
    +B1419164531721N00600858EA0000000934
    +B1419184531731N00600870EA0000000933
    +B1419204531741N00600885EA0000000933
    +B1419224531747N00600899EA0000000934
    +B1419244531753N00600913EA0000000934
    +B1419264531760N00600929EA0000000932
    +B1419284531767N00600946EA0000000932
    +B1419304531772N00600962EA0000000931
    +B1419324531780N00600974EA0000000929
    +B1419344531787N00600987EA0000000928
    +B1419364531795N00601002EA0000000926
    +B1419384531802N00601015EA0000000925
    +B1419404531809N00601030EA0000000922
    +B1419424531815N00601046EA0000000921
    +B1419444531821N00601062EA0000000919
    +B1419464531828N00601078EA0000000917
    +B1419484531834N00601094EA0000000916
    +B1419504531841N00601108EA0000000914
    +B1419524531848N00601121EA0000000914
    +B1419544531853N00601130EA0000000914
    +B1419564531859N00601139EA0000000913
    +B1419584531866N00601148EA0000000912
    +B1420004531872N00601154EA0000000912
    +B1420024531878N00601160EA0000000913
    +B1420044531885N00601164EA0000000913
    +B1420064531891N00601169EA0000000914
    +B1420084531897N00601172EA0000000914
    +B1420104531904N00601173EA0000000914
    +B1420124531912N00601172EA0000000914
    +B1420144531921N00601170EA0000000914
    +B1420164531928N00601168EA0000000915
    +B1420184531934N00601165EA0000000915
    +B1420204531938N00601163EA0000000917
    +B1420224531943N00601160EA0000000917
    +B1420244531949N00601154EA0000000918
    +B1420264531954N00601148EA0000000919
    +B1420284531959N00601145EA0000000920
    +B1420304531965N00601141EA0000000922
    +B1420324531972N00601137EA0000000924
    +B1420344531978N00601133EA0000000927
    +B1420364531983N00601132EA0000000930
    +B1420384531991N00601131EA0000000933
    +B1420414531998N00601131EA0000000937
    +B1420434532002N00601128EA0000000940
    +B1420454532008N00601125EA0000000944
    +B1420474532016N00601123EA0000000949
    +B1420494532022N00601121EA0000000954
    +B1420514532029N00601119EA0000000959
    +B1420534532037N00601120EA0000000963
    +B1420554532045N00601118EA0000000968
    +B1420574532051N00601114EA0000000974
    +B1420594532057N00601111EA0000000978
    +B1421014532064N00601109EA0000000984
    +B1421034532069N00601106EA0000000990
    +B1421054532075N00601104EA0000000996
    +B1421074532081N00601102EA0000001001
    +B1421094532089N00601098EA0000001005
    +B1421114532097N00601095EA0000001010
    +B1421134532101N00601091EA0000001014
    +B1421154532105N00601086EA0000001017
    +B1421174532112N00601083EA0000001019
    +B1421194532119N00601080EA0000001022
    +B1421214532125N00601078EA0000001026
    +B1421234532131N00601075EA0000001030
    +B1421254532136N00601071EA0000001034
    +B1421274532142N00601067EA0000001037
    +B1421294532150N00601064EA0000001040
    +B1421314532156N00601060EA0000001044
    +B1421334532160N00601056EA0000001046
    +B1421354532167N00601051EA0000001048
    +B1421374532176N00601048EA0000001050
    +B1421394532182N00601046EA0000001054
    +B1421414532188N00601042EA0000001058
    +B1421434532195N00601040EA0000001063
    +B1421454532202N00601039EA0000001068
    +B1421474532209N00601037EA0000001074
    +B1421494532214N00601036EA0000001079
    +B1421514532220N00601034EA0000001083
    +B1421534532228N00601034EA0000001087
    +B1421554532236N00601034EA0000001091
    +B1421574532248N00601037EA0000001094
    +B1421594532258N00601040EA0000001098
    +B1422014532267N00601039EA0000001104
    +B1422034532272N00601035EA0000001111
    +B1422054532276N00601034EA0000001117
    +B1422074532280N00601034EA0000001122
    +B1422094532283N00601030EA0000001126
    +B1422114532283N00601023EA0000001129
    +B1422134532280N00601014EA0000001131
    +B1422154532268N00601007EA0000001132
    +B1422174532252N00601012EA0000001134
    +B1422204532242N00601032EA0000001135
    +B1422224532242N00601052EA0000001135
    +B1422244532244N00601066EA0000001138
    +B1422264532250N00601078EA0000001143
    +B1422284532257N00601088EA0000001148
    +B1422304532262N00601099EA0000001155
    +B1422324532268N00601109EA0000001162
    +B1422344532274N00601115EA0000001169
    +B1422364532278N00601116EA0000001175
    +B1422384532283N00601114EA0000001182
    +B1422404532284N00601110EA0000001188
    +B1422424532280N00601102EA0000001194
    +B1422444532271N00601098EA0000001198
    +B1422464532259N00601102EA0000001203
    +B1422484532250N00601114EA0000001209
    +B1422504532247N00601133EA0000001214
    +B1422524532250N00601149EA0000001220
    +B1422544532256N00601157EA0000001225
    +B1422564532262N00601164EA0000001224
    +B1422584532265N00601159EA0000001227
    +B1423004532260N00601152EA0000001229
    +B1423024532251N00601149EA0000001234
    +B1423044532241N00601153EA0000001239
    +B1423064532233N00601166EA0000001244
    +B1423084532228N00601186EA0000001250
    +B1423104532227N00601206EA0000001255
    +B1423124532229N00601222EA0000001257
    +B1423144532232N00601232EA0000001258
    +B1423164532237N00601235EA0000001255
    +B1423184532241N00601231EA0000001252
    +B1423204532238N00601222EA0000001248
    +B1423224532227N00601220EA0000001248
    +B1423244532214N00601231EA0000001249
    +B1423264532203N00601246EA0000001253
    +B1423284532194N00601258EA0000001254
    +B1423304532185N00601273EA0000001255
    +B1423324532180N00601292EA0000001256
    +B1423344532182N00601306EA0000001254
    +B1423364532191N00601314EA0000001252
    +B1423384532194N00601309EA0000001249
    +B1423404532198N00601302EA0000001245
    +B1423424532203N00601298EA0000001242
    +B1423444532208N00601293EA0000001241
    +B1423464532210N00601288EA0000001241
    +B1423484532209N00601283EA0000001242
    +B1423504532211N00601278EA0000001242
    +B1423524532214N00601275EA0000001244
    +B1423544532215N00601272EA0000001244
    +B1423564532217N00601267EA0000001245
    +B1423584532218N00601264EA0000001243
    +B1424014532222N00601263EA0000001242
    +B1424034532228N00601265EA0000001240
    +B1424054532235N00601268EA0000001240
    +B1424074532241N00601268EA0000001240
    +B1424094532247N00601269EA0000001240
    +B1424114532254N00601269EA0000001240
    +B1424134532259N00601268EA0000001239
    +B1424154532265N00601265EA0000001238
    +B1424174532270N00601260EA0000001237
    +B1424194532275N00601255EA0000001237
    +B1424214532279N00601249EA0000001235
    +B1424234532283N00601243EA0000001234
    +B1424254532287N00601236EA0000001233
    +B1424274532291N00601229EA0000001233
    +B1424294532296N00601224EA0000001231
    +B1424314532301N00601219EA0000001231
    +B1424334532305N00601215EA0000001230
    +B1424354532310N00601212EA0000001228
    +B1424374532314N00601207EA0000001225
    +B1424394532318N00601201EA0000001224
    +B1424414532321N00601195EA0000001222
    +B1424434532326N00601187EA0000001221
    +B1424454532331N00601181EA0000001220
    +B1424474532335N00601176EA0000001220
    +B1424494532339N00601171EA0000001219
    +B1424514532345N00601167EA0000001217
    +B1424534532351N00601163EA0000001216
    +B1424554532356N00601158EA0000001217
    +B1424574532360N00601152EA0000001216
    +B1424594532364N00601147EA0000001216
    +B1425014532368N00601142EA0000001215
    +B1425034532371N00601136EA0000001213
    +B1425054532375N00601131EA0000001212
    +B1425074532380N00601127EA0000001212
    +B1425094532384N00601127EA0000001213
    +B1425114532388N00601128EA0000001214
    +B1425134532393N00601126EA0000001216
    +B1425154532396N00601122EA0000001219
    +B1425174532401N00601121EA0000001222
    +B1425194532407N00601123EA0000001225
    +B1425214532414N00601132EA0000001227
    +B1425234532418N00601151EA0000001229
    +B1425254532411N00601172EA0000001232
    +B1425274532399N00601179EA0000001232
    +B1425294532389N00601169EA0000001233
    +B1425314532391N00601159EA0000001234
    +B1425334532400N00601160EA0000001235
    +B1425354532409N00601168EA0000001239
    +B1425374532414N00601178EA0000001243
    +B1425394532416N00601193EA0000001245
    +B1425424532413N00601212EA0000001248
    +B1425444532402N00601221EA0000001250
    +B1425464532391N00601219EA0000001251
    +B1425484532386N00601207EA0000001250
    +B1425504532389N00601194EA0000001252
    +B1425524532398N00601194EA0000001254
    +B1425544532405N00601206EA0000001257
    +B1425564532404N00601223EA0000001260
    +B1425584532396N00601234EA0000001262
    +B1426004532385N00601235EA0000001264
    +B1426024532380N00601224EA0000001266
    +B1426044532386N00601218EA0000001268
    +B1426064532394N00601228EA0000001270
    +B1426084532397N00601245EA0000001273
    +B1426104532397N00601260EA0000001277
    +B1426124532394N00601275EA0000001279
    +B1426144532386N00601287EA0000001281
    +B1426164532375N00601291EA0000001283
    +B1426184532366N00601284EA0000001283
    +B1426204532365N00601274EA0000001284
    +B1426224532372N00601274EA0000001284
    +B1426244532381N00601284EA0000001286
    +B1426264532388N00601293EA0000001287
    +B1426284532396N00601299EA0000001289
    +B1426304532402N00601311EA0000001288
    +B1426324532406N00601328EA0000001288
    +B1426344532403N00601344EA0000001289
    +B1426364532396N00601357EA0000001290
    +B1426384532387N00601368EA0000001291
    +B1426404532377N00601377EA0000001291
    +B1426424532368N00601383EA0000001292
    +B1426444532358N00601386EA0000001291
    +B1426464532348N00601380EA0000001290
    +B1426484532345N00601369EA0000001290
    +B1426504532347N00601359EA0000001289
    +B1426524532349N00601351EA0000001290
    +B1426544532350N00601339EA0000001293
    +B1426564532351N00601331EA0000001295
    +B1426584532352N00601324EA0000001298
    +B1427004532354N00601317EA0000001302
    +B1427024532356N00601311EA0000001306
    +B1427044532358N00601307EA0000001309
    +B1427064532360N00601302EA0000001311
    +B1427084532367N00601299EA0000001312
    +B1427104532374N00601304EA0000001313
    +B1427124532378N00601318EA0000001313
    +B1427144532377N00601335EA0000001313
    +B1427164532368N00601350EA0000001312
    +B1427194532358N00601360EA0000001312
    +B1427214532346N00601367EA0000001312
    +B1427234532332N00601376EA0000001313
    +B1427254532320N00601384EA0000001314
    +B1427274532309N00601391EA0000001317
    +B1427294532300N00601398EA0000001321
    +B1427314532290N00601398EA0000001324
    +B1427334532280N00601390EA0000001327
    +B1427354532278N00601382EA0000001329
    +B1427374532284N00601382EA0000001332
    +B1427394532290N00601399EA0000001334
    +B1427414532290N00601417EA0000001337
    +B1427434532285N00601429EA0000001340
    +B1427454532276N00601438EA0000001343
    +B1427474532264N00601443EA0000001346
    +B1427494532254N00601437EA0000001349
    +B1427514532251N00601427EA0000001353
    +B1427534532256N00601420EA0000001357
    +B1427554532264N00601420EA0000001361
    +B1427574532272N00601432EA0000001363
    +B1427594532275N00601452EA0000001367
    +B1428014532269N00601469EA0000001373
    +B1428034532259N00601478EA0000001377
    +B1428054532247N00601482EA0000001379
    +B1428074532237N00601478EA0000001381
    +B1428094532232N00601466EA0000001384
    +B1428114532237N00601461EA0000001389
    +B1428134532244N00601463EA0000001393
    +B1428154532252N00601472EA0000001394
    +B1428174532256N00601488EA0000001396
    +B1428194532256N00601504EA0000001399
    +B1428214532246N00601516EA0000001401
    +B1428234532236N00601523EA0000001403
    +B1428254532221N00601526EA0000001400
    +B1428274532211N00601517EA0000001402
    +B1428294532208N00601506EA0000001404
    +B1428314532211N00601499EA0000001408
    +B1428334532219N00601497EA0000001414
    +B1428354532224N00601497EA0000001415
    +B1428374532231N00601505EA0000001416
    +B1428394532234N00601526EA0000001415
    +B1428414532225N00601548EA0000001414
    +B1428434532210N00601556EA0000001413
    +B1428454532198N00601557EA0000001414
    +B1428474532186N00601558EA0000001416
    +B1428494532175N00601558EA0000001420
    +B1428514532165N00601558EA0000001423
    +B1428534532155N00601556EA0000001425
    +B1428554532148N00601546EA0000001426
    +B1428584532151N00601537EA0000001426
    +B1429004532160N00601542EA0000001424
    +B1429024532165N00601560EA0000001422
    +B1429044532157N00601580EA0000001420
    +B1429064532141N00601589EA0000001418
    +B1429084532125N00601592EA0000001418
    +B1429104532114N00601595EA0000001420
    +B1429124532104N00601596EA0000001420
    +B1429144532093N00601596EA0000001422
    +B1429164532082N00601598EA0000001424
    +B1429184532072N00601596EA0000001427
    +B1429204532065N00601588EA0000001429
    +B1429224532065N00601578EA0000001434
    +B1429244532074N00601577EA0000001437
    +B1429264532080N00601586EA0000001439
    +B1429284532080N00601606EA0000001440
    +B1429304532068N00601622EA0000001441
    +B1429324532054N00601623EA0000001443
    +B1429344532048N00601613EA0000001445
    +B1429364532052N00601604EA0000001450
    +B1429384532059N00601600EA0000001454
    +B1429404532064N00601597EA0000001458
    +B1429424532070N00601599EA0000001462
    +B1429444532077N00601611EA0000001466
    +B1429464532074N00601629EA0000001468
    +B1429484532062N00601639EA0000001471
    +B1429504532049N00601636EA0000001473
    +B1429524532042N00601626EA0000001474
    +B1429544532041N00601614EA0000001476
    +B1429564532048N00601607EA0000001479
    +B1429584532057N00601605EA0000001482
    +B1430004532064N00601613EA0000001487
    +B1430024532067N00601627EA0000001490
    +B1430044532064N00601644EA0000001493
    +B1430064532053N00601655EA0000001495
    +B1430084532034N00601650EA0000001498
    +B1430104532032N00601638EA0000001501
    +B1430124532035N00601631EA0000001505
    +B1430144532040N00601630EA0000001508
    +B1430164532047N00601635EA0000001511
    +B1430184532051N00601651EA0000001515
    +B1430204532042N00601669EA0000001518
    +B1430224532029N00601676EA0000001521
    +B1430244532018N00601671EA0000001524
    +B1430264532012N00601660EA0000001527
    +B1430284532014N00601651EA0000001529
    +B1430304532020N00601647EA0000001533
    +B1430334532027N00601648EA0000001538
    +B1430354532033N00601661EA0000001544
    +B1430374532033N00601681EA0000001552
    +B1430394532026N00601699EA0000001563
    +B1430414532012N00601707EA0000001569
    +B1430434532000N00601702EA0000001573
    +B1430454531994N00601688EA0000001577
    +B1430474531999N00601681EA0000001582
    +B1430494532007N00601684EA0000001588
    +B1430514532012N00601692EA0000001598
    +B1430534532015N00601699EA0000001606
    +B1430554532020N00601705EA0000001614
    +B1430574532024N00601720EA0000001619
    +B1430594532021N00601741EA0000001625
    +B1431014532009N00601753EA0000001632
    +B1431034531997N00601748EA0000001638
    +B1431054531994N00601736EA0000001645
    +B1431074531996N00601730EA0000001654
    +B1431094532002N00601729EA0000001664
    +B1431114532007N00601731EA0000001673
    +B1431134532011N00601737EA0000001680
    +B1431154532017N00601751EA0000001686
    +B1431174532015N00601773EA0000001691
    +B1431194532004N00601784EA0000001695
    +B1431214531992N00601785EA0000001700
    +B1431234531984N00601776EA0000001705
    +B1431254531984N00601766EA0000001712
    +B1431274531991N00601761EA0000001718
    +B1431294531998N00601764EA0000001723
    +B1431314532002N00601777EA0000001727
    +B1431334532000N00601795EA0000001729
    +B1431354531990N00601805EA0000001731
    +B1431374531978N00601799EA0000001732
    +B1431394531971N00601787EA0000001735
    +B1431414531971N00601779EA0000001739
    +B1431434531974N00601773EA0000001741
    +B1431454531980N00601768EA0000001743
    +B1431474531991N00601778EA0000001745
    +B1431494531992N00601794EA0000001746
    +B1431514531984N00601810EA0000001746
    +B1431534531973N00601816EA0000001746
    +B1431554531965N00601811EA0000001747
    +B1431574531961N00601800EA0000001749
    +B1431594531957N00601790EA0000001755
    +B1432014531955N00601778EA0000001760
    +B1432034531956N00601765EA0000001766
    +B1432054531960N00601757EA0000001771
    +B1432074531965N00601759EA0000001776
    +B1432094531967N00601771EA0000001779
    +B1432124531962N00601785EA0000001782
    +B1432144531950N00601791EA0000001784
    +B1432164531939N00601787EA0000001786
    +B1432184531930N00601781EA0000001790
    +B1432204531923N00601776EA0000001793
    +B1432224531918N00601769EA0000001795
    +B1432244531918N00601761EA0000001797
    +B1432264531923N00601759EA0000001798
    +B1432284531929N00601764EA0000001799
    +B1432304531935N00601767EA0000001801
    +B1432324531939N00601763EA0000001802
    +B1432344531943N00601759EA0000001803
    +B1432364531947N00601758EA0000001803
    +B1432384531953N00601756EA0000001802
    +B1432404531958N00601752EA0000001802
    +B1432424531963N00601748EA0000001801
    +B1432444531969N00601744EA0000001799
    +B1432464531976N00601738EA0000001798
    +B1432484531982N00601733EA0000001797
    +B1432504531988N00601728EA0000001796
    +B1432524531995N00601725EA0000001794
    +B1432544532002N00601721EA0000001793
    +B1432564532009N00601720EA0000001793
    +B1432584532016N00601719EA0000001792
    +B1433004532024N00601718EA0000001791
    +B1433024532032N00601718EA0000001792
    +B1433044532039N00601719EA0000001793
    +B1433064532047N00601721EA0000001793
    +B1433084532055N00601723EA0000001795
    +B1433104532063N00601722EA0000001796
    +B1433124532070N00601722EA0000001796
    +B1433144532079N00601723EA0000001795
    +B1433164532088N00601723EA0000001795
    +B1433184532096N00601724EA0000001794
    +B1433204532105N00601727EA0000001793
    +B1433224532113N00601730EA0000001792
    +B1433244532122N00601733EA0000001791
    +B1433264532135N00601736EA0000001790
    +B1433284532144N00601738EA0000001788
    +B1433304532153N00601741EA0000001786
    +B1433324532162N00601744EA0000001785
    +B1433344532170N00601748EA0000001783
    +B1433364532178N00601754EA0000001782
    +B1433384532186N00601759EA0000001781
    +B1433404532194N00601766EA0000001779
    +B1433424532203N00601775EA0000001776
    +B1433444532214N00601786EA0000001773
    +B1433464532224N00601796EA0000001771
    +B1433484532233N00601806EA0000001768
    +B1433514532242N00601816EA0000001764
    +B1433534532252N00601828EA0000001761
    +B1433554532262N00601839EA0000001760
    +B1433574532271N00601850EA0000001757
    +B1433594532280N00601860EA0000001753
    +B1434014532290N00601872EA0000001750
    +B1434034532300N00601883EA0000001747
    +B1434054532309N00601894EA0000001744
    +B1434074532319N00601906EA0000001740
    +B1434094532329N00601919EA0000001736
    +B1434114532340N00601934EA0000001731
    +B1434134532352N00601949EA0000001726
    +B1434154532358N00601956EA0000001723
    +B1434174532374N00601978EA0000001718
    +B1434194532385N00601993EA0000001713
    +B1434214532396N00602008EA0000001708
    +B1434234532407N00602023EA0000001704
    +B1434254532418N00602038EA0000001699
    +B1434274532428N00602053EA0000001694
    +B1434294532439N00602069EA0000001688
    +B1434314532448N00602085EA0000001683
    +B1434334532459N00602101EA0000001678
    +B1434354532469N00602117EA0000001672
    +B1434374532479N00602133EA0000001666
    +B1434394532490N00602148EA0000001660
    +B1434414532499N00602163EA0000001653
    +B1434434532508N00602180EA0000001647
    +B1434454532517N00602196EA0000001640
    +B1434474532526N00602214EA0000001633
    +B1434494532535N00602231EA0000001626
    +B1434514532544N00602248EA0000001619
    +B1434534532552N00602266EA0000001613
    +B1434554532559N00602284EA0000001609
    +B1434574532562N00602299EA0000001606
    +B1434594532566N00602314EA0000001602
    +B1435014532572N00602332EA0000001599
    +B1435034532578N00602347EA0000001597
    +B1435054532583N00602361EA0000001599
    +B1435074532593N00602384EA0000001596
    +B1435094532599N00602400EA0000001595
    +B1435114532605N00602416EA0000001595
    +B1435134532611N00602431EA0000001593
    +B1435154532616N00602447EA0000001591
    +B1435174532622N00602462EA0000001589
    +B1435194532628N00602478EA0000001588
    +B1435214532633N00602494EA0000001585
    +B1435234532639N00602509EA0000001583
    +B1435254532644N00602525EA0000001580
    +B1435274532649N00602541EA0000001578
    +B1435304532654N00602555EA0000001576
    +B1435324532660N00602568EA0000001573
    +B1435344532667N00602580EA0000001569
    +B1435364532674N00602592EA0000001567
    +B1435384532679N00602605EA0000001564
    +B1435404532685N00602618EA0000001561
    +B1435424532691N00602632EA0000001557
    +B1435444532695N00602648EA0000001554
    +B1435464532701N00602663EA0000001551
    +B1435484532706N00602678EA0000001548
    +B1435504532711N00602694EA0000001544
    +B1435524532717N00602712EA0000001542
    +B1435544532722N00602729EA0000001541
    +B1435564532727N00602743EA0000001538
    +B1435584532731N00602759EA0000001535
    +B1436004532737N00602777EA0000001532
    +B1436024532742N00602793EA0000001531
    +B1436044532747N00602806EA0000001529
    +B1436064532752N00602821EA0000001525
    +B1436084532758N00602839EA0000001523
    +B1436104532763N00602855EA0000001521
    +B1436124532770N00602867EA0000001518
    +B1436144532778N00602879EA0000001513
    +B1436164532784N00602893EA0000001508
    +B1436184532792N00602905EA0000001503
    +B1436204532801N00602916EA0000001497
    +B1436224532809N00602928EA0000001493
    +B1436244532815N00602945EA0000001488
    +B1436264532820N00602961EA0000001483
    +B1436284532825N00602979EA0000001478
    +B1436304532827N00602999EA0000001474
    +B1436324532829N00603018EA0000001471
    +B1436344532832N00603036EA0000001467
    +B1436364532834N00603055EA0000001463
    +B1436384532837N00603074EA0000001460
    +B1436404532839N00603093EA0000001456
    +B1436424532842N00603112EA0000001452
    +B1436444532844N00603131EA0000001447
    +B1436464532847N00603160EA0000001441
    +B1436484532846N00603181EA0000001437
    +B1436504532845N00603203EA0000001435
    +B1436524532843N00603223EA0000001434
    +B1436544532843N00603242EA0000001434
    +B1436564532843N00603257EA0000001439
    +B1436584532845N00603268EA0000001443
    +B1437004532847N00603277EA0000001445
    +B1437024532850N00603284EA0000001447
    +B1437044532852N00603294EA0000001449
    +B1437064532855N00603305EA0000001452
    +B1437094532860N00603315EA0000001456
    +B1437114532864N00603323EA0000001461
    +B1437134532869N00603329EA0000001466
    +B1437154532875N00603332EA0000001469
    +B1437174532880N00603336EA0000001472
    +B1437194532885N00603336EA0000001473
    +B1437214532890N00603330EA0000001473
    +B1437234532887N00603316EA0000001472
    +B1437254532876N00603311EA0000001472
    +B1437274532863N00603319EA0000001474
    +B1437294532854N00603327EA0000001478
    +B1437314532843N00603332EA0000001480
    +B1437334532831N00603333EA0000001482
    +B1437354532822N00603327EA0000001484
    +B1437374532818N00603317EA0000001489
    +B1437394532822N00603311EA0000001492
    +B1437414532829N00603316EA0000001494
    +B1437434532834N00603324EA0000001498
    +B1437454532838N00603330EA0000001501
    +B1437474532844N00603336EA0000001502
    +B1437494532851N00603340EA0000001502
    +B1437514532858N00603342EA0000001502
    +B1437534532866N00603342EA0000001504
    +B1437554532873N00603342EA0000001505
    +B1437574532878N00603343EA0000001505
    +B1437594532886N00603346EA0000001505
    +B1438014532896N00603353EA0000001506
    +B1438034532906N00603359EA0000001507
    +B1438054532913N00603362EA0000001510
    +B1438074532922N00603365EA0000001511
    +B1438094532932N00603369EA0000001514
    +B1438114532940N00603371EA0000001515
    +B1438134532948N00603371EA0000001517
    +B1438154532953N00603371EA0000001520
    +B1438174532959N00603372EA0000001521
    +B1438194532967N00603370EA0000001521
    +B1438214532975N00603368EA0000001521
    +B1438234532986N00603366EA0000001519
    +B1438254532996N00603362EA0000001517
    +B1438274533009N00603355EA0000001515
    +B1438294533019N00603350EA0000001513
    +B1438314533020N00603348EA0000001514
    +B1438334533020N00603345EA0000001514
    +B1438354533022N00603336EA0000001513
    +B1438374533028N00603327EA0000001513
    +B1438394533034N00603325EA0000001513
    +B1438414533042N00603319EA0000001512
    +B1438434533050N00603314EA0000001510
    +B1438454533057N00603314EA0000001508
    +B1438484533064N00603311EA0000001505
    +B1438504533073N00603307EA0000001504
    +B1438524533079N00603302EA0000001503
    +B1438544533082N00603295EA0000001502
    +B1438564533088N00603287EA0000001501
    +B1438584533098N00603281EA0000001500
    +B1439004533106N00603279EA0000001501
    +B1439024533114N00603276EA0000001503
    +B1439044533123N00603272EA0000001506
    +B1439064533132N00603270EA0000001510
    +B1439084533139N00603270EA0000001514
    +B1439104533146N00603275EA0000001519
    +B1439124533150N00603278EA0000001530
    +B1439144533152N00603282EA0000001534
    +B1439164533153N00603292EA0000001534
    +B1439184533144N00603312EA0000001533
    +B1439204533131N00603307EA0000001530
    +B1439224533129N00603290EA0000001529
    +B1439244533140N00603284EA0000001530
    +B1439264533148N00603286EA0000001531
    +B1439284533153N00603291EA0000001529
    +B1439304533158N00603303EA0000001528
    +B1439324533161N00603316EA0000001528
    +B1439344533167N00603326EA0000001526
    +B1439364533173N00603336EA0000001524
    +B1439384533179N00603346EA0000001521
    +B1439404533187N00603354EA0000001517
    +B1439424533195N00603362EA0000001514
    +B1439444533204N00603371EA0000001511
    +B1439464533213N00603380EA0000001510
    +B1439484533221N00603388EA0000001507
    +B1439504533230N00603397EA0000001505
    +B1439524533238N00603407EA0000001503
    +B1439544533245N00603415EA0000001500
    +B1439564533253N00603424EA0000001497
    +B1439584533262N00603432EA0000001494
    +B1440004533269N00603439EA0000001491
    +B1440024533277N00603448EA0000001487
    +B1440044533289N00603462EA0000001483
    +B1440064533297N00603471EA0000001480
    +B1440084533306N00603481EA0000001476
    +B1440104533314N00603490EA0000001474
    +B1440124533322N00603497EA0000001471
    +B1440144533332N00603505EA0000001468
    +B1440164533341N00603513EA0000001465
    +B1440184533350N00603522EA0000001463
    +B1440204533357N00603533EA0000001463
    +B1440224533363N00603545EA0000001462
    +B1440244533370N00603558EA0000001460
    +B1440264533379N00603571EA0000001457
    +B1440294533388N00603583EA0000001455
    +B1440314533397N00603596EA0000001452
    +B1440334533406N00603609EA0000001451
    +B1440354533415N00603624EA0000001449
    +B1440374533423N00603639EA0000001447
    +B1440394533432N00603653EA0000001446
    +B1440414533440N00603667EA0000001445
    +B1440434533448N00603682EA0000001443
    +B1440454533457N00603696EA0000001441
    +B1440474533466N00603710EA0000001439
    +B1440494533476N00603725EA0000001437
    +B1440514533486N00603740EA0000001435
    +B1440534533496N00603754EA0000001434
    +B1440554533507N00603767EA0000001434
    +B1440574533519N00603780EA0000001434
    +B1440594533530N00603792EA0000001435
    +B1441014533541N00603803EA0000001437
    +B1441034533552N00603814EA0000001440
    +B1441054533561N00603824EA0000001445
    +B1441074533569N00603832EA0000001451
    +B1441094533578N00603840EA0000001457
    +B1441114533586N00603843EA0000001463
    +B1441134533590N00603842EA0000001469
    +B1441154533594N00603840EA0000001474
    +B1441174533595N00603831EA0000001479
    +B1441194533593N00603824EA0000001481
    +B1441214533585N00603818EA0000001481
    +B1441234533572N00603827EA0000001483
    +B1441254533565N00603836EA0000001489
    +B1441274533562N00603837EA0000001494
    +B1441294533558N00603830EA0000001499
    +B1441314533561N00603822EA0000001503
    +B1441334533568N00603817EA0000001507
    +B1441354533579N00603819EA0000001509
    +B1441374533591N00603834EA0000001512
    +B1441394533593N00603854EA0000001514
    +B1441414533579N00603870EA0000001517
    +B1441434533568N00603873EA0000001521
    +B1441454533563N00603862EA0000001530
    +B1441474533568N00603854EA0000001534
    +B1441494533578N00603858EA0000001539
    +B1441514533583N00603873EA0000001542
    +B1441534533577N00603893EA0000001546
    +B1441554533567N00603903EA0000001549
    +B1441574533557N00603900EA0000001551
    +B1441594533552N00603894EA0000001557
    +B1442014533550N00603888EA0000001560
    +B1442034533550N00603878EA0000001564
    +B1442064533559N00603873EA0000001567
    +B1442084533570N00603886EA0000001570
    +B1442104533573N00603907EA0000001572
    +B1442124533562N00603922EA0000001573
    +B1442144533548N00603925EA0000001576
    +B1442164533542N00603919EA0000001581
    +B1442184533541N00603913EA0000001587
    +B1442204533543N00603908EA0000001591
    +B1442224533547N00603905EA0000001593
    +B1442244533558N00603912EA0000001595
    +B1442264533566N00603932EA0000001596
    +B1442284533564N00603954EA0000001597
    +B1442304533559N00603962EA0000001599
    +B1442324533545N00603969EA0000001600
    +B1442344533539N00603965EA0000001604
    +B1442364533536N00603959EA0000001606
    +B1442384533539N00603954EA0000001608
    +B1442404533549N00603960EA0000001610
    +B1442424533560N00603972EA0000001611
    +B1442444533570N00603980EA0000001612
    +B1442464533579N00603981EA0000001613
    +B1442484533588N00603982EA0000001614
    +B1442504533597N00603982EA0000001616
    +B1442524533605N00603981EA0000001617
    +B1442544533612N00603977EA0000001617
    +B1442564533620N00603976EA0000001617
    +B1442584533627N00603975EA0000001618
    +B1443004533635N00603973EA0000001618
    +B1443024533643N00603971EA0000001620
    +B1443044533650N00603970EA0000001621
    +B1443064533656N00603969EA0000001622
    +B1443084533663N00603965EA0000001622
    +B1443104533672N00603962EA0000001621
    +B1443124533679N00603959EA0000001620
    +B1443144533686N00603954EA0000001618
    +B1443164533695N00603949EA0000001616
    +B1443184533705N00603946EA0000001615
    +B1443204533717N00603942EA0000001614
    +B1443224533727N00603939EA0000001612
    +B1443244533736N00603935EA0000001610
    +B1443264533745N00603932EA0000001608
    +B1443284533756N00603927EA0000001606
    +B1443304533768N00603922EA0000001605
    +B1443324533776N00603917EA0000001605
    +B1443344533783N00603910EA0000001605
    +B1443364533793N00603901EA0000001603
    +B1443384533805N00603892EA0000001602
    +B1443404533815N00603886EA0000001602
    +B1443424533822N00603879EA0000001602
    +B1443454533831N00603871EA0000001602
    +B1443474533840N00603866EA0000001604
    +B1443494533851N00603863EA0000001605
    +B1443514533861N00603860EA0000001608
    +B1443534533869N00603858EA0000001612
    +B1443554533875N00603856EA0000001617
    +B1443574533881N00603856EA0000001623
    +B1443594533886N00603856EA0000001628
    +B1444014533889N00603852EA0000001633
    +B1444034533890N00603843EA0000001636
    +B1444054533886N00603836EA0000001639
    +B1444074533879N00603834EA0000001642
    +B1444094533871N00603840EA0000001645
    +B1444114533863N00603854EA0000001646
    +B1444134533862N00603872EA0000001648
    +B1444154533868N00603885EA0000001650
    +B1444174533879N00603891EA0000001652
    +B1444194533886N00603890EA0000001656
    +B1444214533892N00603887EA0000001659
    +B1444234533898N00603884EA0000001660
    +B1444254533902N00603879EA0000001661
    +B1444274533900N00603872EA0000001660
    +B1444294533894N00603866EA0000001659
    +B1444314533884N00603872EA0000001657
    +B1444334533876N00603890EA0000001656
    +B1444354533876N00603910EA0000001655
    +B1444374533884N00603922EA0000001655
    +B1444394533892N00603920EA0000001655
    +B1444414533897N00603913EA0000001655
    +B1444434533905N00603910EA0000001657
    +B1444454533913N00603910EA0000001659
    +B1444474533921N00603912EA0000001660
    +B1444494533931N00603917EA0000001661
    +B1444514533942N00603925EA0000001664
    +B1444534533951N00603935EA0000001667
    +B1444554533960N00603945EA0000001671
    +B1444574533967N00603952EA0000001677
    +B1444594533974N00603957EA0000001687
    +B1445014533980N00603952EA0000001692
    +B1445034533980N00603943EA0000001698
    +B1445054533973N00603934EA0000001703
    +B1445074533965N00603932EA0000001710
    +B1445094533956N00603938EA0000001716
    +B1445114533949N00603951EA0000001723
    +B1445134533946N00603967EA0000001730
    +B1445154533949N00603978EA0000001735
    +B1445174533956N00603986EA0000001742
    +B1445194533962N00603991EA0000001747
    +B1445214533969N00603991EA0000001753
    +B1445244533974N00603987EA0000001758
    +B1445264533974N00603980EA0000001762
    +B1445284533972N00603973EA0000001767
    +B1445304533965N00603967EA0000001773
    +B1445324533954N00603970EA0000001779
    +B1445344533946N00603982EA0000001786
    +B1445364533943N00603999EA0000001794
    +B1445384533945N00604015EA0000001800
    +B1445404533950N00604029EA0000001806
    +B1445424533956N00604039EA0000001812
    +B1445444533963N00604039EA0000001817
    +B1445464533966N00604035EA0000001821
    +B1445484533966N00604029EA0000001825
    +B1445504533963N00604019EA0000001829
    +B1445524533958N00604014EA0000001834
    +B1445544533951N00604014EA0000001841
    +B1445564533941N00604021EA0000001848
    +B1445584533931N00604035EA0000001854
    +B1446004533926N00604054EA0000001860
    +B1446024533925N00604073EA0000001867
    +B1446044533929N00604088EA0000001874
    +B1446064533936N00604097EA0000001879
    +B1446084533942N00604098EA0000001884
    +B1446104533944N00604091EA0000001889
    +B1446124533943N00604084EA0000001894
    +B1446144533938N00604078EA0000001900
    +B1446164533930N00604076EA0000001906
    +B1446184533921N00604079EA0000001912
    +B1446204533910N00604087EA0000001917
    +B1446224533901N00604104EA0000001922
    +B1446244533895N00604125EA0000001927
    +B1446264533894N00604146EA0000001932
    +B1446284533898N00604163EA0000001936
    +B1446304533906N00604170EA0000001939
    +B1446324533912N00604169EA0000001943
    +B1446344533915N00604169EA0000001946
    +B1446364533920N00604164EA0000001950
    +B1446384533918N00604157EA0000001954
    +B1446404533915N00604150EA0000001958
    +B1446424533911N00604146EA0000001964
    +B1446444533906N00604142EA0000001969
    +B1446464533898N00604141EA0000001974
    +B1446484533888N00604145EA0000001980
    +B1446504533876N00604154EA0000001986
    +B1446524533867N00604176EA0000001991
    +B1446544533864N00604198EA0000001997
    +B1446564533866N00604215EA0000002003
    +B1446584533870N00604226EA0000002006
    +B1447014533875N00604233EA0000002009
    +B1447034533880N00604235EA0000002012
    +B1447054533884N00604235EA0000002014
    +B1447074533889N00604232EA0000002015
    +B1447094533896N00604228EA0000002017
    +B1447114533899N00604222EA0000002021
    +B1447134533898N00604215EA0000002026
    +B1447154533896N00604212EA0000002032
    +B1447174533891N00604209EA0000002036
    +B1447194533883N00604206EA0000002042
    +B1447214533873N00604207EA0000002049
    +B1447234533867N00604214EA0000002056
    +B1447254533853N00604240EA0000002061
    +B1447274533846N00604259EA0000002067
    +B1447294533844N00604278EA0000002072
    +B1447314533851N00604293EA0000002077
    +B1447334533858N00604300EA0000002081
    +B1447354533865N00604304EA0000002084
    +B1447374533871N00604305EA0000002086
    +B1447394533875N00604301EA0000002089
    +B1447414533878N00604295EA0000002093
    +B1447434533879N00604287EA0000002096
    +B1447454533875N00604278EA0000002097
    +B1447474533866N00604271EA0000002100
    +B1447494533854N00604272EA0000002104
    +B1447514533845N00604281EA0000002107
    +B1447534533836N00604295EA0000002108
    +B1447554533827N00604310EA0000002112
    +B1447574533822N00604327EA0000002115
    +B1447594533827N00604342EA0000002118
    +B1448014533833N00604351EA0000002121
    +B1448034533839N00604356EA0000002122
    +B1448054533847N00604358EA0000002125
    +B1448074533854N00604358EA0000002128
    +B1448094533859N00604355EA0000002132
    +B1448114533861N00604348EA0000002137
    +B1448134533860N00604337EA0000002140
    +B1448154533850N00604324EA0000002145
    +B1448174533842N00604320EA0000002148
    +B1448194533831N00604318EA0000002149
    +B1448214533815N00604334EA0000002147
    +B1448234533806N00604357EA0000002149
    +B1448254533803N00604375EA0000002152
    +B1448274533801N00604390EA0000002154
    +B1448294533802N00604405EA0000002155
    +B1448314533811N00604409EA0000002156
    +B1448334533818N00604408EA0000002159
    +B1448354533822N00604405EA0000002163
    +B1448374533824N00604400EA0000002167
    +B1448404533825N00604392EA0000002170
    +B1448424533824N00604381EA0000002172
    +B1448444533821N00604369EA0000002174
    +B1448464533815N00604362EA0000002175
    +B1448484533807N00604356EA0000002176
    +B1448504533796N00604352EA0000002175
    +B1448524533784N00604351EA0000002174
    +B1448544533770N00604350EA0000002171
    +B1448564533755N00604350EA0000002169
    +B1448584533739N00604352EA0000002168
    +B1449004533723N00604350EA0000002168
    +B1449024533709N00604348EA0000002167
    +B1449044533694N00604348EA0000002168
    +B1449064533680N00604349EA0000002169
    +B1449084533666N00604351EA0000002171
    +B1449104533654N00604359EA0000002172
    +B1449124533644N00604373EA0000002173
    +B1449144533639N00604387EA0000002173
    +B1449164533639N00604400EA0000002172
    +B1449184533644N00604411EA0000002170
    +B1449204533649N00604418EA0000002166
    +B1449224533657N00604418EA0000002161
    +B1449244533664N00604410EA0000002157
    +B1449264533669N00604403EA0000002156
    +B1449284533669N00604393EA0000002153
    +B1449304533667N00604379EA0000002151
    +B1449324533663N00604365EA0000002150
    +B1449344533654N00604354EA0000002152
    +B1449364533642N00604347EA0000002152
    +B1449384533629N00604341EA0000002154
    +B1449404533615N00604343EA0000002155
    +B1449424533604N00604352EA0000002155
    +B1449444533596N00604365EA0000002156
    +B1449464533594N00604380EA0000002156
    +B1449484533594N00604395EA0000002156
    +B1449504533592N00604409EA0000002154
    +B1449524533588N00604425EA0000002151
    +B1449544533588N00604443EA0000002147
    +B1449564533586N00604470EA0000002143
    +B1449584533583N00604485EA0000002141
    +B1450004533582N00604498EA0000002137
    +B1450024533580N00604513EA0000002133
    +B1450044533577N00604528EA0000002131
    +B1450064533576N00604541EA0000002128
    +B1450084533575N00604554EA0000002125
    +B1450104533573N00604568EA0000002123
    +B1450124533572N00604580EA0000002121
    +B1450144533570N00604593EA0000002119
    +B1450164533569N00604607EA0000002118
    +B1450184533568N00604619EA0000002117
    +B1450214533568N00604631EA0000002115
    +B1450234533567N00604643EA0000002113
    +B1450254533566N00604657EA0000002111
    +B1450274533566N00604669EA0000002110
    +B1450294533564N00604681EA0000002109
    +B1450314533563N00604696EA0000002107
    +B1450334533563N00604710EA0000002107
    +B1450354533562N00604723EA0000002106
    +B1450374533561N00604736EA0000002105
    +B1450394533561N00604749EA0000002103
    +B1450414533562N00604763EA0000002103
    +B1450434533564N00604775EA0000002102
    +B1450454533566N00604786EA0000002100
    +B1450474533568N00604799EA0000002100
    +B1450494533570N00604812EA0000002100
    +B1450514533571N00604824EA0000002098
    +B1450534533572N00604837EA0000002095
    +B1450554533573N00604851EA0000002093
    +B1450574533574N00604865EA0000002092
    +B1450594533576N00604876EA0000002090
    +B1451014533577N00604889EA0000002087
    +B1451034533577N00604903EA0000002085
    +B1451054533575N00604918EA0000002083
    +B1451074533573N00604933EA0000002081
    +B1451094533573N00604948EA0000002079
    +B1451114533573N00604963EA0000002078
    +B1451134533572N00604978EA0000002076
    +B1451154533572N00604993EA0000002074
    +B1451174533573N00605007EA0000002072
    +B1451194533573N00605021EA0000002069
    +B1451214533574N00605034EA0000002066
    +B1451234533575N00605048EA0000002062
    +B1451254533576N00605062EA0000002058
    +B1451274533578N00605074EA0000002054
    +B1451294533581N00605086EA0000002048
    +B1451314533585N00605101EA0000002043
    +B1451334533595N00605124EA0000002035
    +B1451354533601N00605137EA0000002032
    +B1451374533607N00605151EA0000002028
    +B1451394533612N00605166EA0000002025
    +B1451414533618N00605181EA0000002023
    +B1451434533624N00605193EA0000002022
    +B1451454533628N00605206EA0000002019
    +B1451474533634N00605219EA0000002016
    +B1451494533639N00605232EA0000002015
    +B1451514533644N00605245EA0000002013
    +B1451534533649N00605258EA0000002010
    +B1451554533656N00605272EA0000002007
    +B1451584533662N00605284EA0000002006
    +B1452004533670N00605297EA0000002004
    +B1452024533678N00605308EA0000002002
    +B1452044533687N00605319EA0000002000
    +B1452064533695N00605330EA0000001998
    +B1452084533702N00605341EA0000001996
    +B1452104533711N00605352EA0000001994
    +B1452124533719N00605361EA0000001993
    +B1452144533727N00605369EA0000001991
    +B1452164533735N00605378EA0000001988
    +B1452184533744N00605387EA0000001986
    +B1452204533752N00605397EA0000001984
    +B1452224533759N00605409EA0000001981
    +B1452244533768N00605419EA0000001978
    +B1452264533777N00605429EA0000001975
    +B1452284533786N00605441EA0000001972
    +B1452304533794N00605450EA0000001969
    +B1452324533803N00605459EA0000001965
    +B1452344533812N00605467EA0000001962
    +B1452364533821N00605476EA0000001961
    +B1452384533830N00605482EA0000001959
    +B1452404533839N00605490EA0000001958
    +B1452424533846N00605499EA0000001957
    +B1452444533855N00605506EA0000001954
    +B1452464533864N00605513EA0000001952
    +B1452484533873N00605523EA0000001950
    +B1452504533883N00605529EA0000001949
    +B1452524533895N00605531EA0000001947
    +B1452544533906N00605536EA0000001946
    +B1452564533917N00605543EA0000001945
    +B1452584533926N00605551EA0000001945
    +B1453004533934N00605561EA0000001944
    +B1453024533942N00605572EA0000001944
    +B1453044533951N00605580EA0000001943
    +B1453064533959N00605589EA0000001942
    +B1453084533967N00605598EA0000001939
    +B1453104533976N00605606EA0000001936
    +B1453124533985N00605616EA0000001931
    +B1453144534000N00605631EA0000001924
    +B1453164534010N00605639EA0000001920
    +B1453184534019N00605648EA0000001918
    +B1453204534028N00605656EA0000001916
    +B1453224534038N00605663EA0000001913
    +B1453244534048N00605671EA0000001910
    +B1453264534058N00605681EA0000001908
    +B1453284534068N00605691EA0000001906
    +B1453304534078N00605701EA0000001903
    +B1453324534088N00605711EA0000001901
    +B1453344534099N00605720EA0000001900
    +B1453364534109N00605729EA0000001899
    +B1453394534119N00605737EA0000001896
    +B1453414534130N00605747EA0000001893
    +B1453434534141N00605756EA0000001891
    +B1453454534151N00605764EA0000001890
    +B1453474534160N00605772EA0000001888
    +B1453494534171N00605782EA0000001885
    +B1453514534181N00605790EA0000001883
    +B1453534534190N00605796EA0000001881
    +B1453554534200N00605805EA0000001878
    +B1453574534211N00605815EA0000001874
    +B1453594534223N00605822EA0000001870
    +B1454014534234N00605830EA0000001865
    +B1454034534244N00605839EA0000001860
    +B1454054534255N00605846EA0000001855
    +B1454074534264N00605855EA0000001850
    +B1454094534273N00605865EA0000001845
    +B1454114534283N00605876EA0000001840
    +B1454134534293N00605888EA0000001836
    +B1454154534303N00605899EA0000001832
    +B1454174534314N00605910EA0000001828
    +B1454194534326N00605920EA0000001823
    +B1454214534339N00605929EA0000001819
    +B1454234534350N00605937EA0000001817
    +B1454254534360N00605945EA0000001814
    +B1454274534372N00605954EA0000001810
    +B1454294534384N00605964EA0000001805
    +B1454314534396N00605975EA0000001802
    +B1454334534407N00605985EA0000001798
    +B1454354534419N00605994EA0000001795
    +B1454374534432N00606002EA0000001791
    +B1454394534444N00606009EA0000001788
    +B1454414534456N00606016EA0000001786
    +B1454434534470N00606024EA0000001783
    +B1454454534483N00606034EA0000001781
    +B1454474534495N00606043EA0000001780
    +B1454494534507N00606052EA0000001780
    +B1454514534520N00606062EA0000001779
    +B1454534534536N00606077EA0000001781
    +B1454554534545N00606084EA0000001782
    +B1454574534555N00606093EA0000001783
    +B1454594534565N00606101EA0000001784
    +B1455014534576N00606107EA0000001785
    +B1455034534586N00606113EA0000001791
    +B1455054534596N00606121EA0000001793
    +B1455074534606N00606129EA0000001796
    +B1455094534616N00606135EA0000001799
    +B1455114534625N00606137EA0000001803
    +B1455134534632N00606141EA0000001806
    +B1455154534640N00606144EA0000001809
    +B1455184534647N00606147EA0000001813
    +B1455204534655N00606150EA0000001817
    +B1455224534664N00606153EA0000001821
    +B1455244534673N00606158EA0000001827
    +B1455264534679N00606161EA0000001832
    +B1455284534686N00606163EA0000001837
    +B1455304534695N00606167EA0000001842
    +B1455324534702N00606172EA0000001849
    +B1455344534709N00606179EA0000001855
    +B1455364534716N00606185EA0000001861
    +B1455384534723N00606185EA0000001865
    +B1455404534730N00606180EA0000001868
    +B1455424534733N00606171EA0000001871
    +B1455444534732N00606159EA0000001873
    +B1455464534727N00606148EA0000001876
    +B1455484534718N00606141EA0000001879
    +B1455504534707N00606139EA0000001881
    +B1455524534697N00606144EA0000001883
    +B1455544534691N00606156EA0000001885
    +B1455564534689N00606170EA0000001889
    +B1455584534690N00606184EA0000001895
    +B1456004534691N00606197EA0000001902
    +B1456024534696N00606208EA0000001908
    +B1456044534703N00606216EA0000001914
    +B1456064534710N00606220EA0000001919
    +B1456084534719N00606219EA0000001922
    +B1456104534726N00606211EA0000001925
    +B1456124534725N00606193EA0000001928
    +B1456154534714N00606182EA0000001932
    +B1456174534704N00606185EA0000001934
    +B1456194534695N00606187EA0000001935
    +B1456214534685N00606191EA0000001936
    +B1456234534674N00606199EA0000001938
    +B1456254534662N00606205EA0000001942
    +B1456274534652N00606211EA0000001948
    +B1456294534647N00606215EA0000001956
    +B1456314534635N00606230EA0000001964
    +B1456334534630N00606242EA0000001971
    +B1456354534628N00606257EA0000001978
    +B1456374534628N00606273EA0000001986
    +B1456394534635N00606285EA0000001993
    +B1456414534645N00606292EA0000002002
    +B1456434534656N00606287EA0000002009
    +B1456454534660N00606273EA0000002016
    +B1456474534655N00606259EA0000002022
    +B1456494534642N00606257EA0000002029
    +B1456514534632N00606264EA0000002037
    +B1456534534624N00606274EA0000002045
    +B1456554534620N00606288EA0000002054
    +B1456574534618N00606302EA0000002063
    +B1456594534620N00606314EA0000002072
    +B1457014534625N00606326EA0000002080
    +B1457034534631N00606335EA0000002087
    +B1457054534639N00606341EA0000002094
    +B1457074534648N00606343EA0000002102
    +B1457094534655N00606335EA0000002110
    +B1457114534655N00606324EA0000002117
    +B1457134534650N00606312EA0000002123
    +B1457154534641N00606302EA0000002130
    +B1457174534632N00606299EA0000002136
    +B1457194534622N00606300EA0000002142
    +B1457214534606N00606309EA0000002153
    +B1457234534598N00606319EA0000002162
    +B1457254534593N00606330EA0000002172
    +B1457274534589N00606345EA0000002181
    +B1457294534590N00606364EA0000002190
    +B1457314534600N00606373EA0000002200
    +B1457334534609N00606373EA0000002211
    +B1457354534614N00606365EA0000002220
    +B1457374534614N00606352EA0000002228
    +B1457394534611N00606343EA0000002235
    +B1457414534603N00606333EA0000002242
    +B1457434534592N00606328EA0000002251
    +B1457464534581N00606328EA0000002261
    +B1457484534569N00606328EA0000002272
    +B1457504534560N00606334EA0000002282
    +B1457524534553N00606346EA0000002291
    +B1457544534549N00606360EA0000002299
    +B1457564534547N00606374EA0000002306
    +B1457584534552N00606388EA0000002313
    +B1458004534561N00606396EA0000002323
    +B1458024534570N00606397EA0000002334
    +B1458044534576N00606397EA0000002344
    +B1458064534582N00606392EA0000002353
    +B1458084534584N00606383EA0000002359
    +B1458104534584N00606377EA0000002366
    +B1458124534572N00606363EA0000002372
    +B1458144534559N00606362EA0000002380
    +B1458164534546N00606364EA0000002390
    +B1458184534536N00606366EA0000002400
    +B1458204534528N00606369EA0000002407
    +B1458224534518N00606378EA0000002415
    +B1458244534513N00606395EA0000002424
    +B1458264534516N00606407EA0000002435
    +B1458284534519N00606418EA0000002446
    +B1458304534524N00606427EA0000002456
    +B1458324534530N00606433EA0000002466
    +B1458344534536N00606434EA0000002474
    +B1458364534543N00606431EA0000002482
    +B1458384534548N00606423EA0000002489
    +B1458404534549N00606412EA0000002496
    +B1458424534546N00606399EA0000002502
    +B1458444534536N00606389EA0000002508
    +B1458464534523N00606384EA0000002516
    +B1458484534507N00606387EA0000002524
    +B1458504534496N00606400EA0000002534
    +B1458524534488N00606413EA0000002544
    +B1458544534483N00606426EA0000002553
    +B1458564534482N00606440EA0000002561
    +B1458584534484N00606452EA0000002570
    +B1459004534497N00606454EA0000002583
    +B1459024534504N00606450EA0000002593
    +B1459044534508N00606443EA0000002601
    +B1459064534511N00606431EA0000002607
    +B1459084534508N00606415EA0000002614
    +B1459104534497N00606406EA0000002620
    +B1459124534482N00606409EA0000002627
    +B1459144534472N00606424EA0000002634
    +B1459164534469N00606439EA0000002641
    +B1459184534469N00606452EA0000002649
    +B1459204534470N00606465EA0000002656
    +B1459224534473N00606480EA0000002664
    +B1459254534474N00606494EA0000002671
    +B1459274534474N00606505EA0000002678
    +B1459294534476N00606518EA0000002682
    +B1459314534477N00606536EA0000002686
    +B1459334534479N00606553EA0000002692
    +B1459354534480N00606566EA0000002698
    +B1459374534482N00606580EA0000002702
    +B1459394534484N00606598EA0000002707
    +B1459414534484N00606614EA0000002714
    +B1459434534484N00606629EA0000002718
    +B1459454534485N00606646EA0000002721
    +B1459474534488N00606664EA0000002727
    +B1459494534489N00606683EA0000002734
    +B1459514534490N00606700EA0000002740
    +B1459534534492N00606717EA0000002746
    +B1459554534494N00606732EA0000002752
    +B1459574534497N00606746EA0000002758
    +B1459594534499N00606757EA0000002763
    +B1500014534502N00606771EA0000002766
    +B1500034534505N00606787EA0000002768
    +B1500054534507N00606801EA0000002767
    +B1500074534510N00606821EA0000002764
    +B1500094534513N00606841EA0000002764
    +B1500114534515N00606854EA0000002763
    +B1500134534519N00606865EA0000002760
    +B1500154534524N00606878EA0000002754
    +B1500174534527N00606894EA0000002750
    +B1500194534529N00606909EA0000002748
    +B1500214534532N00606922EA0000002744
    +B1500234534535N00606936EA0000002739
    +B1500254534539N00606953EA0000002734
    +B1500274534544N00606969EA0000002732
    +B1500294534550N00606985EA0000002728
    +B1500314534555N00607001EA0000002725
    +B1500334534562N00607017EA0000002724
    +B1500354534568N00607029EA0000002723
    +B1500374534575N00607043EA0000002720
    +B1500394534585N00607067EA0000002719
    +B1500414534591N00607080EA0000002720
    +B1500434534599N00607089EA0000002718
    +B1500454534611N00607094EA0000002717
    +B1500474534624N00607095EA0000002716
    +B1500494534636N00607094EA0000002714
    +B1500514534650N00607092EA0000002712
    +B1500534534663N00607089EA0000002712
    +B1500554534676N00607083EA0000002711
    +B1500574534687N00607076EA0000002708
    +B1500594534700N00607068EA0000002706
    +B1501014534714N00607061EA0000002704
    +B1501044534727N00607052EA0000002701
    +B1501064534743N00607044EA0000002697
    +B1501084534759N00607037EA0000002696
    +B1501104534774N00607030EA0000002695
    +B1501124534788N00607023EA0000002692
    +B1501144534804N00607017EA0000002688
    +B1501164534819N00607011EA0000002686
    +B1501184534833N00607004EA0000002685
    +B1501204534848N00606998EA0000002682
    +B1501224534864N00606994EA0000002680
    +B1501244534878N00606988EA0000002678
    +B1501264534892N00606980EA0000002676
    +B1501284534907N00606973EA0000002673
    +B1501304534921N00606967EA0000002670
    +B1501324534935N00606959EA0000002668
    +B1501344534949N00606951EA0000002664
    +B1501364534963N00606944EA0000002661
    +B1501384534977N00606934EA0000002658
    +B1501404534990N00606925EA0000002655
    +B1501424535003N00606917EA0000002653
    +B1501444535017N00606908EA0000002649
    +B1501464535034N00606900EA0000002644
    +B1501484535050N00606894EA0000002641
    +B1501504535063N00606888EA0000002640
    +B1501524535075N00606882EA0000002637
    +B1501544535088N00606874EA0000002633
    +B1501564535100N00606866EA0000002628
    +B1501584535112N00606859EA0000002623
    +B1502004535126N00606855EA0000002617
    +B1502024535140N00606853EA0000002613
    +B1502044535152N00606849EA0000002610
    +B1502064535164N00606843EA0000002608
    +B1502084535178N00606840EA0000002605
    +B1502104535191N00606835EA0000002603
    +B1502124535202N00606828EA0000002600
    +B1502144535215N00606823EA0000002595
    +B1502164535229N00606815EA0000002591
    +B1502184535241N00606806EA0000002588
    +B1502204535258N00606792EA0000002584
    +B1502224535270N00606778EA0000002582
    +B1502244535281N00606767EA0000002582
    +B1502264535291N00606757EA0000002583
    +B1502284535301N00606747EA0000002582
    +B1502304535312N00606737EA0000002583
    +B1502324535323N00606730EA0000002583
    +B1502344535333N00606724EA0000002582
    +B1502364535344N00606715EA0000002579
    +B1502384535357N00606706EA0000002577
    +B1502404535367N00606697EA0000002576
    +B1502424535377N00606686EA0000002574
    +B1502454535388N00606675EA0000002572
    +B1502474535399N00606665EA0000002571
    +B1502494535410N00606655EA0000002569
    +B1502514535420N00606646EA0000002567
    +B1502534535432N00606637EA0000002563
    +B1502554535445N00606629EA0000002560
    +B1502574535457N00606621EA0000002558
    +B1502594535468N00606611EA0000002557
    +B1503014535480N00606602EA0000002554
    +B1503034535493N00606593EA0000002550
    +B1503054535505N00606582EA0000002547
    +B1503074535517N00606573EA0000002545
    +B1503094535530N00606563EA0000002541
    +B1503114535542N00606553EA0000002538
    +B1503134535554N00606544EA0000002535
    +B1503154535566N00606536EA0000002531
    +B1503174535578N00606529EA0000002527
    +B1503194535591N00606521EA0000002523
    +B1503214535604N00606515EA0000002519
    +B1503234535617N00606507EA0000002516
    +B1503254535629N00606499EA0000002512
    +B1503274535641N00606492EA0000002509
    +B1503294535653N00606486EA0000002504
    +B1503314535666N00606478EA0000002499
    +B1503334535679N00606472EA0000002496
    +B1503354535691N00606467EA0000002492
    +B1503374535704N00606458EA0000002488
    +B1503394535718N00606450EA0000002486
    +B1503414535731N00606442EA0000002485
    +B1503434535742N00606434EA0000002482
    +B1503454535755N00606423EA0000002478
    +B1503474535768N00606413EA0000002476
    +B1503494535781N00606405EA0000002474
    +B1503514535794N00606397EA0000002470
    +B1503534535806N00606390EA0000002467
    +B1503554535819N00606384EA0000002464
    +B1503574535832N00606377EA0000002460
    +B1503594535847N00606373EA0000002458
    +B1504014535868N00606366EA0000002458
    +B1504034535880N00606359EA0000002459
    +B1504054535892N00606353EA0000002461
    +B1504074535904N00606345EA0000002463
    +B1504094535916N00606337EA0000002466
    +B1504114535929N00606333EA0000002467
    +B1504134535942N00606328EA0000002468
    +B1504154535954N00606322EA0000002468
    +B1504174535967N00606316EA0000002469
    +B1504194535980N00606311EA0000002470
    +B1504214535994N00606305EA0000002470
    +B1504234536008N00606301EA0000002470
    +B1504264536022N00606297EA0000002471
    +B1504284536034N00606292EA0000002472
    +B1504304536046N00606288EA0000002470
    +B1504324536059N00606284EA0000002467
    +B1504344536070N00606277EA0000002463
    +B1504364536081N00606270EA0000002457
    +B1504384536093N00606262EA0000002451
    +B1504404536104N00606251EA0000002446
    +B1504424536116N00606240EA0000002442
    +B1504444536128N00606232EA0000002438
    +B1504464536140N00606223EA0000002435
    +B1504484536152N00606214EA0000002433
    +B1504504536164N00606206EA0000002430
    +B1504524536176N00606200EA0000002427
    +B1504544536188N00606193EA0000002423
    +B1504564536200N00606188EA0000002421
    +B1504584536212N00606183EA0000002418
    +B1505004536224N00606177EA0000002414
    +B1505024536237N00606173EA0000002410
    +B1505044536251N00606170EA0000002406
    +B1505064536263N00606168EA0000002403
    +B1505084536274N00606164EA0000002401
    +B1505104536284N00606158EA0000002398
    +B1505124536295N00606153EA0000002395
    +B1505144536305N00606149EA0000002391
    +B1505164536315N00606143EA0000002386
    +B1505184536326N00606137EA0000002381
    +B1505204536336N00606131EA0000002376
    +B1505224536347N00606123EA0000002372
    +B1505244536357N00606117EA0000002369
    +B1505264536368N00606110EA0000002366
    +B1505284536381N00606103EA0000002363
    +B1505304536394N00606101EA0000002361
    +B1505324536403N00606099EA0000002360
    +B1505344536412N00606094EA0000002359
    +B1505364536425N00606091EA0000002357
    +B1505384536438N00606089EA0000002357
    +B1505404536453N00606085EA0000002359
    +B1505424536464N00606081EA0000002359
    +B1505444536475N00606077EA0000002360
    +B1505464536484N00606073EA0000002366
    +B1505484536495N00606073EA0000002366
    +B1505504536506N00606071EA0000002366
    +B1505524536518N00606069EA0000002366
    +B1505544536528N00606068EA0000002367
    +B1505564536539N00606066EA0000002366
    +B1505584536550N00606065EA0000002366
    +B1506004536562N00606064EA0000002366
    +B1506024536573N00606062EA0000002366
    +B1506054536585N00606062EA0000002367
    +B1506074536597N00606064EA0000002369
    +B1506094536608N00606065EA0000002369
    +B1506114536619N00606065EA0000002369
    +B1506134536631N00606063EA0000002369
    +B1506154536642N00606062EA0000002371
    +B1506174536652N00606062EA0000002371
    +B1506194536663N00606061EA0000002371
    +B1506214536675N00606059EA0000002371
    +B1506234536686N00606059EA0000002371
    +B1506254536697N00606059EA0000002370
    +B1506274536708N00606058EA0000002368
    +B1506294536719N00606056EA0000002367
    +B1506314536730N00606055EA0000002368
    +B1506334536741N00606053EA0000002369
    +B1506354536752N00606051EA0000002371
    +B1506374536763N00606052EA0000002373
    +B1506394536775N00606055EA0000002377
    +B1506414536784N00606058EA0000002381
    +B1506434536791N00606059EA0000002387
    +B1506454536798N00606058EA0000002392
    +B1506474536804N00606055EA0000002397
    +B1506494536811N00606053EA0000002404
    +B1506514536818N00606051EA0000002411
    +B1506534536824N00606050EA0000002418
    +B1506554536831N00606048EA0000002423
    +B1506574536840N00606047EA0000002428
    +B1506594536847N00606051EA0000002434
    +B1507014536851N00606060EA0000002439
    +B1507034536852N00606075EA0000002443
    +B1507054536847N00606092EA0000002447
    +B1507074536832N00606103EA0000002451
    +B1507094536818N00606098EA0000002455
    +B1507114536812N00606085EA0000002459
    +B1507134536817N00606074EA0000002464
    +B1507154536825N00606073EA0000002470
    +B1507174536834N00606077EA0000002479
    +B1507194536841N00606083EA0000002488
    +B1507214536854N00606089EA0000002496
    +B1507234536864N00606092EA0000002503
    +B1507254536872N00606095EA0000002508
    +B1507274536882N00606098EA0000002513
    +B1507294536892N00606101EA0000002518
    +B1507314536901N00606104EA0000002525
    +B1507334536908N00606106EA0000002532
    +B1507354536915N00606107EA0000002538
    +B1507374536925N00606106EA0000002545
    +B1507394536933N00606104EA0000002553
    +B1507414536939N00606104EA0000002558
    +B1507444536949N00606102EA0000002561
    +B1507464536962N00606104EA0000002563
    +B1507484536975N00606106EA0000002567
    +B1507504536985N00606104EA0000002572
    +B1507524536995N00606101EA0000002578
    +B1507544537003N00606103EA0000002586
    +B1507564537008N00606104EA0000002592
    +B1507584537015N00606102EA0000002598
    +B1508004537022N00606104EA0000002602
    +B1508024537028N00606106EA0000002606
    +B1508044537036N00606105EA0000002608
    +B1508064537044N00606104EA0000002610
    +B1508084537052N00606104EA0000002613
    +B1508104537061N00606102EA0000002616
    +B1508124537069N00606100EA0000002621
    +B1508144537076N00606097EA0000002627
    +B1508164537085N00606094EA0000002633
    +B1508184537093N00606091EA0000002639
    +B1508204537098N00606083EA0000002646
    +B1508224537103N00606071EA0000002651
    +B1508244537112N00606062EA0000002657
    +B1508264537120N00606054EA0000002663
    +B1508284537125N00606043EA0000002667
    +B1508304537131N00606033EA0000002670
    +B1508324537141N00606026EA0000002671
    +B1508344537149N00606017EA0000002671
    +B1508364537158N00606006EA0000002670
    +B1508384537172N00605997EA0000002668
    +B1508404537185N00605991EA0000002669
    +B1508424537196N00605987EA0000002670
    +B1508444537207N00605981EA0000002670
    +B1508464537220N00605976EA0000002668
    +B1508484537233N00605975EA0000002665
    +B1508504537246N00605975EA0000002662
    +B1508524537259N00605975EA0000002659
    +B1508544537272N00605976EA0000002657
    +B1508564537285N00605980EA0000002654
    +B1508584537307N00605980EA0000002647
    +B1509004537321N00605977EA0000002644
    +B1509024537335N00605977EA0000002641
    +B1509044537350N00605978EA0000002637
    +B1509064537365N00605979EA0000002635
    +B1509084537378N00605981EA0000002633
    +B1509104537391N00605984EA0000002631
    +B1509124537404N00605986EA0000002628
    +B1509144537418N00605991EA0000002626
    +B1509164537431N00605996EA0000002623
    +B1509184537445N00606000EA0000002621
    +B1509204537459N00606004EA0000002618
    +B1509234537472N00606009EA0000002616
    +B1509254537485N00606014EA0000002615
    +B1509274537498N00606020EA0000002615
    +B1509294537511N00606027EA0000002616
    +B1509314537524N00606034EA0000002616
    +B1509334537536N00606038EA0000002615
    +B1509354537549N00606039EA0000002613
    +B1509374537562N00606043EA0000002610
    +B1509394537576N00606048EA0000002607
    +B1509414537589N00606050EA0000002605
    +B1509434537601N00606054EA0000002603
    +B1509454537613N00606059EA0000002600
    +B1509474537626N00606062EA0000002597
    +B1509494537639N00606067EA0000002594
    +B1509514537652N00606071EA0000002591
    +B1509534537665N00606074EA0000002589
    +B1509554537677N00606078EA0000002587
    +B1509574537689N00606082EA0000002584
    +B1509594537702N00606087EA0000002580
    +B1510014537715N00606094EA0000002576
    +B1510034537728N00606100EA0000002573
    +B1510054537740N00606105EA0000002569
    +B1510074537753N00606112EA0000002564
    +B1510094537767N00606121EA0000002559
    +B1510114537779N00606128EA0000002554
    +B1510134537793N00606136EA0000002549
    +B1510154537806N00606143EA0000002544
    +B1510174537820N00606149EA0000002540
    +B1510194537831N00606155EA0000002537
    +B1510214537842N00606163EA0000002534
    +B1510234537854N00606169EA0000002530
    +B1510254537865N00606175EA0000002527
    +B1510274537875N00606183EA0000002523
    +B1510294537886N00606192EA0000002517
    +B1510314537897N00606201EA0000002511
    +B1510334537910N00606211EA0000002505
    +B1510354537921N00606221EA0000002499
    +B1510374537939N00606232EA0000002491
    +B1510394537951N00606241EA0000002485
    +B1510414537964N00606249EA0000002480
    +B1510434537975N00606255EA0000002475
    +B1510454537986N00606265EA0000002469
    +B1510474537999N00606276EA0000002463
    +B1510494538012N00606285EA0000002458
    +B1510514538023N00606294EA0000002455
    +B1510534538033N00606305EA0000002450
    +B1510554538045N00606315EA0000002446
    +B1510574538056N00606327EA0000002443
    +B1510594538066N00606338EA0000002440
    +B1511014538077N00606351EA0000002438
    +B1511044538087N00606364EA0000002435
    +B1511064538098N00606377EA0000002434
    +B1511084538108N00606389EA0000002433
    +B1511104538119N00606400EA0000002431
    +B1511124538129N00606414EA0000002429
    +B1511144538138N00606428EA0000002428
    +B1511164538148N00606440EA0000002426
    +B1511184538158N00606453EA0000002425
    +B1511204538169N00606465EA0000002424
    +B1511224538180N00606477EA0000002423
    +B1511244538190N00606489EA0000002422
    +B1511264538201N00606502EA0000002421
    +B1511284538212N00606516EA0000002419
    +B1511304538223N00606531EA0000002417
    +B1511324538233N00606545EA0000002417
    +B1511344538243N00606556EA0000002416
    +B1511364538253N00606567EA0000002415
    +B1511384538263N00606580EA0000002414
    +B1511404538273N00606593EA0000002414
    +B1511424538280N00606604EA0000002414
    +B1511444538288N00606614EA0000002413
    +B1511464538298N00606623EA0000002412
    +B1511484538307N00606632EA0000002411
    +B1511504538314N00606643EA0000002412
    +B1511524538322N00606652EA0000002414
    +B1511544538331N00606661EA0000002417
    +B1511564538339N00606670EA0000002421
    +B1511584538347N00606677EA0000002423
    +B1512004538357N00606684EA0000002425
    +B1512024538367N00606692EA0000002426
    +B1512044538377N00606701EA0000002426
    +B1512064538387N00606710EA0000002426
    +B1512084538397N00606719EA0000002426
    +B1512104538407N00606728EA0000002425
    +B1512124538418N00606736EA0000002423
    +B1512144538428N00606743EA0000002423
    +B1512164538439N00606748EA0000002421
    +B1512184538457N00606757EA0000002418
    +B1512204538468N00606764EA0000002417
    +B1512224538481N00606770EA0000002418
    +B1512244538494N00606775EA0000002420
    +B1512264538506N00606780EA0000002421
    +B1512284538518N00606785EA0000002422
    +B1512304538528N00606788EA0000002422
    +B1512324538541N00606795EA0000002419
    +B1512344538554N00606805EA0000002418
    +B1512364538563N00606812EA0000002416
    +B1512384538573N00606816EA0000002412
    +B1512404538584N00606821EA0000002408
    +B1512424538597N00606824EA0000002406
    +B1512454538608N00606827EA0000002402
    +B1512474538619N00606832EA0000002398
    +B1512494538631N00606838EA0000002394
    +B1512514538644N00606844EA0000002392
    +B1512534538655N00606852EA0000002391
    +B1512554538668N00606857EA0000002390
    +B1512574538680N00606863EA0000002390
    +B1512594538691N00606870EA0000002390
    +B1513014538703N00606876EA0000002388
    +B1513034538715N00606882EA0000002387
    +B1513054538728N00606891EA0000002388
    +B1513074538739N00606899EA0000002389
    +B1513094538751N00606906EA0000002389
    +B1513114538762N00606913EA0000002389
    +B1513134538772N00606923EA0000002390
    +B1513154538778N00606927EA0000002393
    +B1513174538784N00606931EA0000002393
    +B1513194538792N00606939EA0000002395
    +B1513214538799N00606941EA0000002399
    +B1513234538805N00606941EA0000002400
    +B1513254538814N00606942EA0000002402
    +B1513274538823N00606943EA0000002406
    +B1513294538831N00606943EA0000002411
    +B1513314538837N00606943EA0000002416
    +B1513334538844N00606944EA0000002419
    +B1513354538852N00606948EA0000002422
    +B1513374538860N00606951EA0000002426
    +B1513394538867N00606951EA0000002430
    +B1513414538872N00606952EA0000002433
    +B1513434538878N00606955EA0000002434
    +B1513454538885N00606959EA0000002435
    +B1513474538892N00606961EA0000002437
    +B1513494538898N00606963EA0000002438
    +B1513514538904N00606963EA0000002438
    +B1513534538910N00606965EA0000002436
    +B1513554538918N00606970EA0000002434
    +B1513574538927N00606976EA0000002432
    +B1513594538941N00606984EA0000002430
    +B1514014538950N00606991EA0000002428
    +B1514034538960N00606998EA0000002424
    +B1514054538971N00607005EA0000002421
    +B1514074538982N00607012EA0000002420
    +B1514094538993N00607019EA0000002418
    +B1514114539004N00607026EA0000002414
    +B1514134539015N00607033EA0000002410
    +B1514154539027N00607042EA0000002407
    +B1514174539038N00607052EA0000002406
    +B1514194539047N00607063EA0000002404
    +B1514214539057N00607073EA0000002403
    +B1514244539066N00607082EA0000002401
    +B1514264539076N00607092EA0000002399
    +B1514284539086N00607102EA0000002397
    +B1514304539097N00607111EA0000002396
    +B1514324539109N00607118EA0000002396
    +B1514344539119N00607124EA0000002398
    +B1514364539130N00607131EA0000002398
    +B1514384539141N00607138EA0000002399
    +B1514404539151N00607144EA0000002400
    +B1514424539161N00607148EA0000002399
    +B1514444539170N00607155EA0000002400
    +B1514464539176N00607164EA0000002400
    +B1514484539179N00607167EA0000002399
    +B1514504539191N00607175EA0000002396
    +B1514524539201N00607185EA0000002396
    +B1514544539211N00607193EA0000002398
    +B1514564539221N00607201EA0000002400
    +B1514584539229N00607211EA0000002403
    +B1515004539234N00607219EA0000002406
    +B1515024539240N00607226EA0000002409
    +B1515044539247N00607234EA0000002413
    +B1515064539254N00607238EA0000002419
    +B1515084539259N00607240EA0000002425
    +B1515104539265N00607243EA0000002430
    +B1515124539271N00607247EA0000002435
    +B1515144539278N00607253EA0000002438
    +B1515164539285N00607260EA0000002441
    +B1515184539292N00607266EA0000002443
    +B1515204539297N00607269EA0000002443
    +B1515224539305N00607276EA0000002442
    +B1515244539314N00607286EA0000002441
    +B1515264539322N00607293EA0000002439
    +B1515284539332N00607300EA0000002436
    +B1515304539343N00607309EA0000002432
    +B1515324539355N00607317EA0000002430
    +B1515344539366N00607326EA0000002428
    +B1515364539377N00607337EA0000002426
    +B1515384539389N00607346EA0000002424
    +B1515404539405N00607359EA0000002418
    +B1515424539416N00607368EA0000002414
    +B1515444539426N00607376EA0000002409
    +B1515464539437N00607384EA0000002404
    +B1515484539447N00607390EA0000002400
    +B1515504539458N00607396EA0000002396
    +B1515524539468N00607402EA0000002392
    +B1515544539479N00607407EA0000002387
    +B1515564539490N00607411EA0000002381
    +B1515584539502N00607416EA0000002376
    +B1516004539513N00607422EA0000002371
    +B1516024539524N00607428EA0000002366
    +B1516054539537N00607434EA0000002362
    +B1516074539549N00607439EA0000002357
    +B1516094539561N00607443EA0000002352
    +B1516114539574N00607448EA0000002347
    +B1516134539587N00607453EA0000002343
    +B1516154539599N00607459EA0000002339
    +B1516174539611N00607465EA0000002335
    +B1516194539624N00607471EA0000002331
    +B1516214539636N00607477EA0000002326
    +B1516234539649N00607482EA0000002323
    +B1516254539661N00607489EA0000002319
    +B1516274539673N00607495EA0000002315
    +B1516294539679N00607498EA0000002310
    +B1516314539699N00607509EA0000002303
    +B1516334539715N00607518EA0000002298
    +B1516354539731N00607526EA0000002293
    +B1516374539745N00607533EA0000002289
    +B1516394539760N00607541EA0000002283
    +B1516414539775N00607548EA0000002277
    +B1516434539791N00607554EA0000002273
    +B1516454539805N00607562EA0000002268
    +B1516474539819N00607569EA0000002262
    +B1516494539835N00607576EA0000002256
    +B1516514539851N00607584EA0000002250
    +B1516534539865N00607593EA0000002246
    +B1516554539879N00607602EA0000002242
    +B1516574539893N00607610EA0000002237
    +B1516594539908N00607618EA0000002233
    +B1517014539921N00607628EA0000002228
    +B1517034539935N00607636EA0000002224
    +B1517054539949N00607645EA0000002220
    +B1517074539962N00607655EA0000002217
    +B1517094539975N00607665EA0000002213
    +B1517114539989N00607676EA0000002208
    +B1517134540002N00607686EA0000002204
    +B1517154540016N00607696EA0000002199
    +B1517174540029N00607706EA0000002195
    +B1517194540043N00607715EA0000002190
    +B1517214540063N00607729EA0000002184
    +B1517234540077N00607739EA0000002179
    +B1517254540090N00607749EA0000002174
    +B1517274540104N00607758EA0000002170
    +B1517294540118N00607767EA0000002165
    +B1517314540131N00607778EA0000002160
    +B1517334540145N00607788EA0000002155
    +B1517354540158N00607798EA0000002150
    +B1517374540172N00607809EA0000002145
    +B1517394540185N00607819EA0000002140
    +B1517414540199N00607829EA0000002136
    +B1517434540212N00607838EA0000002132
    +B1517464540225N00607848EA0000002128
    +B1517484540238N00607858EA0000002123
    +B1517504540252N00607868EA0000002119
    +B1517524540265N00607877EA0000002115
    +B1517544540278N00607887EA0000002112
    +B1517564540290N00607897EA0000002108
    +B1517584540303N00607906EA0000002103
    +B1518004540315N00607916EA0000002099
    +B1518024540327N00607927EA0000002094
    +B1518044540340N00607939EA0000002089
    +B1518064540351N00607950EA0000002084
    +B1518084540364N00607962EA0000002079
    +B1518104540375N00607977EA0000002075
    +B1518124540385N00607993EA0000002071
    +B1518144540394N00608010EA0000002067
    +B1518164540404N00608027EA0000002062
    +B1518184540415N00608043EA0000002058
    +B1518204540426N00608058EA0000002053
    +B1518224540437N00608073EA0000002047
    +B1518244540448N00608087EA0000002042
    +B1518264540460N00608102EA0000002037
    +B1518284540472N00608117EA0000002032
    +B1518304540484N00608132EA0000002027
    +B1518324540495N00608147EA0000002021
    +B1518344540507N00608163EA0000002016
    +B1518364540517N00608181EA0000002011
    +B1518384540528N00608198EA0000002006
    +B1518404540539N00608216EA0000002001
    +B1518424540550N00608232EA0000001996
    +B1518444540562N00608248EA0000001991
    +B1518464540574N00608263EA0000001985
    +B1518484540585N00608278EA0000001980
    +B1518504540597N00608293EA0000001975
    +B1518524540608N00608308EA0000001970
    +B1518544540620N00608323EA0000001964
    +B1518564540632N00608338EA0000001958
    +B1518584540643N00608354EA0000001952
    +B1519004540661N00608378EA0000001946
    +B1519024540672N00608394EA0000001942
    +B1519044540684N00608410EA0000001938
    +B1519064540695N00608426EA0000001934
    +B1519084540706N00608441EA0000001930
    +B1519104540717N00608457EA0000001926
    +B1519124540729N00608473EA0000001922
    +B1519144540740N00608488EA0000001918
    +B1519164540751N00608503EA0000001915
    +B1519184540762N00608517EA0000001912
    +B1519204540773N00608531EA0000001909
    +B1519224540785N00608544EA0000001906
    +B1519244540796N00608558EA0000001902
    +B1519274540807N00608572EA0000001899
    +B1519294540817N00608586EA0000001895
    +B1519314540828N00608600EA0000001892
    +B1519334540839N00608614EA0000001888
    +B1519354540848N00608630EA0000001884
    +B1519374540858N00608645EA0000001879
    +B1519394540869N00608658EA0000001874
    +B1519414540879N00608672EA0000001869
    +B1519434540890N00608685EA0000001864
    +B1519454540901N00608697EA0000001860
    +B1519474540911N00608709EA0000001855
    +B1519494540922N00608722EA0000001850
    +B1519514540933N00608735EA0000001845
    +B1519534540944N00608750EA0000001841
    +B1519554540954N00608763EA0000001838
    +B1519574540962N00608774EA0000001835
    +B1519594540972N00608785EA0000001831
    +B1520014540982N00608798EA0000001827
    +B1520034540993N00608811EA0000001822
    +B1520054541002N00608824EA0000001819
    +B1520074541012N00608836EA0000001816
    +B1520094541022N00608847EA0000001813
    +B1520114541033N00608859EA0000001810
    +B1520134541043N00608872EA0000001808
    +B1520154541052N00608886EA0000001806
    +B1520174541062N00608898EA0000001802
    +B1520194541071N00608909EA0000001798
    +B1520214541079N00608921EA0000001793
    +B1520234541089N00608933EA0000001787
    +B1520254541099N00608946EA0000001782
    +B1520274541110N00608958EA0000001777
    +B1520294541122N00608970EA0000001772
    +B1520314541133N00608983EA0000001767
    +B1520334541145N00608994EA0000001764
    +B1520354541155N00609004EA0000001759
    +B1520374541165N00609015EA0000001753
    +B1520394541175N00609025EA0000001746
    +B1520414541192N00609040EA0000001739
    +B1520434541203N00609052EA0000001734
    +B1520454541213N00609064EA0000001729
    +B1520474541225N00609076EA0000001725
    +B1520494541236N00609088EA0000001721
    +B1520514541247N00609100EA0000001718
    +B1520534541258N00609111EA0000001717
    +B1520554541269N00609122EA0000001716
    +B1520574541276N00609134EA0000001717
    +B1520594541281N00609143EA0000001715
    +B1521014541289N00609157EA0000001712
    +B1521034541297N00609170EA0000001713
    +B1521064541305N00609177EA0000001712
    +B1521084541314N00609187EA0000001710
    +B1521104541323N00609199EA0000001709
    +B1521124541333N00609208EA0000001708
    +B1521144541342N00609215EA0000001708
    +B1521164541351N00609224EA0000001706
    +B1521184541362N00609233EA0000001704
    +B1521204541373N00609239EA0000001704
    +B1521224541383N00609246EA0000001704
    +B1521244541394N00609253EA0000001702
    +B1521264541405N00609259EA0000001701
    +B1521284541414N00609265EA0000001701
    +B1521304541423N00609272EA0000001700
    +B1521324541437N00609285EA0000001700
    +B1521344541447N00609290EA0000001699
    +B1521364541456N00609293EA0000001699
    +B1521384541464N00609299EA0000001698
    +B1521404541473N00609304EA0000001696
    +B1521424541483N00609308EA0000001693
    +B1521444541492N00609315EA0000001690
    +B1521464541500N00609322EA0000001686
    +B1521484541509N00609327EA0000001681
    +B1521504541519N00609332EA0000001677
    +B1521524541527N00609337EA0000001673
    +B1521544541537N00609342EA0000001669
    +B1521574541548N00609349EA0000001666
    +B1521594541558N00609357EA0000001663
    +B1522014541569N00609362EA0000001662
    +B1522034541579N00609369EA0000001662
    +B1522054541591N00609375EA0000001662
    +B1522074541602N00609381EA0000001662
    +B1522094541613N00609388EA0000001663
    +B1522114541624N00609394EA0000001663
    +B1522134541636N00609401EA0000001663
    +B1522154541649N00609408EA0000001664
    +B1522174541660N00609414EA0000001666
    +B1522194541672N00609421EA0000001668
    +B1522214541678N00609424EA0000001670
    +B1522234541695N00609434EA0000001673
    +B1522254541704N00609439EA0000001677
    +B1522274541712N00609444EA0000001682
    +B1522294541718N00609449EA0000001688
    +B1522314541726N00609453EA0000001695
    +B1522334541732N00609458EA0000001703
    +B1522354541735N00609460EA0000001712
    +B1522374541738N00609460EA0000001720
    +B1522394541743N00609459EA0000001727
    +B1522414541747N00609459EA0000001736
    +B1522434541750N00609458EA0000001744
    +B1522454541754N00609458EA0000001751
    +B1522474541758N00609457EA0000001757
    +B1522494541760N00609451EA0000001764
    +B1522514541759N00609442EA0000001769
    +B1522534541758N00609436EA0000001774
    +B1522554541755N00609427EA0000001777
    +B1522574541750N00609416EA0000001780
    +B1522594541747N00609404EA0000001781
    +B1523014541750N00609403EA0000001782
    +B1523034541752N00609413EA0000001783
    +B1523054541755N00609423EA0000001786
    +B1523074541756N00609429EA0000001790
    +B1523094541760N00609432EA0000001792
    +B1523114541765N00609437EA0000001795
    +B1523134541766N00609445EA0000001800
    +B1523154541766N00609450EA0000001803
    +B1523174541771N00609453EA0000001806
    +B1523194541772N00609454EA0000001810
    +B1523214541771N00609454EA0000001813
    +B1523234541776N00609455EA0000001816
    +B1523254541780N00609459EA0000001820
    +B1523274541781N00609463EA0000001826
    +B1523294541784N00609465EA0000001831
    +B1523314541789N00609468EA0000001836
    +B1523334541792N00609472EA0000001840
    +B1523354541796N00609475EA0000001842
    +B1523384541803N00609474EA0000001846
    +B1523404541808N00609473EA0000001850
    +B1523424541810N00609472EA0000001854
    +B1523444541813N00609467EA0000001858
    +B1523464541815N00609461EA0000001863
    +B1523484541815N00609456EA0000001867
    +B1523504541818N00609452EA0000001870
    +B1523524541821N00609448EA0000001873
    +B1523544541825N00609446EA0000001877
    +B1523564541828N00609445EA0000001880
    +B1523584541832N00609441EA0000001885
    +B1524004541837N00609439EA0000001889
    +B1524024541839N00609439EA0000001893
    +B1524044541846N00609438EA0000001895
    +B1524064541851N00609439EA0000001896
    +B1524084541857N00609440EA0000001897
    +B1524104541864N00609441EA0000001899
    +B1524124541871N00609440EA0000001902
    +B1524144541876N00609439EA0000001906
    +B1524164541881N00609438EA0000001910
    +B1524184541886N00609439EA0000001913
    +B1524204541890N00609441EA0000001917
    +B1524224541893N00609441EA0000001921
    +B1524244541896N00609442EA0000001925
    +B1524264541899N00609441EA0000001929
    +B1524284541901N00609442EA0000001934
    +B1524304541902N00609443EA0000001937
    +B1524324541904N00609446EA0000001939
    +B1524344541908N00609449EA0000001942
    +B1524364541911N00609452EA0000001945
    +B1524384541914N00609454EA0000001947
    +B1524404541918N00609459EA0000001948
    +B1524424541921N00609461EA0000001950
    +B1524444541924N00609462EA0000001950
    +B1524464541928N00609465EA0000001950
    +B1524484541933N00609467EA0000001952
    +B1524504541938N00609468EA0000001952
    +B1524524541944N00609469EA0000001952
    +B1524544541955N00609471EA0000001951
    +B1524564541962N00609472EA0000001952
    +B1524584541965N00609468EA0000001952
    +B1525004541965N00609458EA0000001951
    +B1525024541961N00609444EA0000001951
    +B1525044541947N00609435EA0000001951
    +B1525064541930N00609434EA0000001951
    +B1525084541915N00609438EA0000001952
    +B1525104541900N00609443EA0000001954
    +B1525124541887N00609448EA0000001956
    +B1525144541875N00609452EA0000001957
    +B1525174541863N00609455EA0000001956
    +B1525194541849N00609458EA0000001957
    +B1525214541836N00609462EA0000001958
    +B1525234541822N00609464EA0000001959
    +B1525254541808N00609466EA0000001961
    +B1525274541794N00609466EA0000001962
    +B1525294541780N00609466EA0000001962
    +B1525314541766N00609467EA0000001961
    +B1525334541753N00609466EA0000001961
    +B1525354541739N00609464EA0000001959
    +B1525374541725N00609470EA0000001956
    +B1525394541714N00609485EA0000001954
    +B1525414541713N00609496EA0000001954
    +B1525434541718N00609497EA0000001955
    +B1525454541723N00609497EA0000001957
    +B1525474541727N00609495EA0000001957
    +B1525494541730N00609494EA0000001956
    +B1525514541736N00609492EA0000001957
    +B1525534541741N00609488EA0000001959
    +B1525554541743N00609485EA0000001962
    +B1525574541746N00609481EA0000001965
    +B1525594541750N00609478EA0000001968
    +B1526014541753N00609477EA0000001970
    +B1526034541757N00609477EA0000001972
    +B1526054541762N00609477EA0000001972
    +B1526074541765N00609475EA0000001972
    +B1526094541769N00609476EA0000001972
    +B1526114541774N00609477EA0000001971
    +B1526134541779N00609480EA0000001972
    +B1526154541785N00609482EA0000001974
    +B1526174541791N00609483EA0000001977
    +B1526194541796N00609483EA0000001980
    +B1526214541801N00609481EA0000001982
    +B1526234541805N00609478EA0000001983
    +B1526254541811N00609476EA0000001983
    +B1526274541817N00609475EA0000001981
    +B1526294541827N00609476EA0000001977
    +B1526314541835N00609476EA0000001973
    +B1526334541844N00609477EA0000001969
    +B1526354541851N00609480EA0000001966
    +B1526374541858N00609483EA0000001963
    +B1526394541868N00609483EA0000001960
    +B1526414541874N00609484EA0000001957
    +B1526434541878N00609487EA0000001955
    +B1526454541886N00609488EA0000001952
    +B1526474541898N00609486EA0000001949
    +B1526494541909N00609484EA0000001948
    +B1526514541918N00609484EA0000001948
    +B1526534541927N00609486EA0000001947
    +B1526564541936N00609486EA0000001947
    +B1526584541946N00609487EA0000001946
    +B1527004541955N00609489EA0000001945
    +B1527024541963N00609491EA0000001944
    +B1527044541971N00609492EA0000001943
    +B1527064541978N00609494EA0000001943
    +B1527084541984N00609495EA0000001944
    +B1527104541989N00609495EA0000001947
    +B1527124541996N00609496EA0000001945
    +B1527144542004N00609498EA0000001945
    +B1527164542008N00609502EA0000001946
    +B1527184542011N00609505EA0000001945
    +B1527204542016N00609506EA0000001945
    +B1527224542023N00609504EA0000001946
    +B1527244542026N00609502EA0000001947
    +B1527264542029N00609502EA0000001946
    +B1527284542034N00609503EA0000001946
    +B1527304542042N00609505EA0000001947
    +B1527324542046N00609507EA0000001947
    +B1527344542050N00609508EA0000001947
    +B1527364542059N00609510EA0000001946
    +B1527384542068N00609511EA0000001948
    +B1527404542074N00609512EA0000001951
    +B1527424542078N00609513EA0000001952
    +B1527444542083N00609513EA0000001953
    +B1527464542089N00609513EA0000001955
    +B1527484542095N00609516EA0000001956
    +B1527504542101N00609517EA0000001955
    +B1527524542108N00609517EA0000001956
    +B1527544542114N00609519EA0000001957
    +B1527564542120N00609523EA0000001955
    +B1527584542128N00609524EA0000001954
    +B1528004542135N00609525EA0000001954
    +B1528024542140N00609527EA0000001954
    +B1528044542144N00609529EA0000001952
    +B1528064542151N00609528EA0000001949
    +B1528084542159N00609530EA0000001948
    +B1528104542168N00609535EA0000001943
    +B1528124542176N00609537EA0000001940
    +B1528144542183N00609539EA0000001937
    +B1528164542189N00609540EA0000001934
    +B1528184542196N00609539EA0000001930
    +B1528204542205N00609539EA0000001924
    +B1528224542213N00609538EA0000001919
    +B1528244542221N00609539EA0000001914
    +B1528264542230N00609539EA0000001908
    +B1528284542242N00609539EA0000001901
    +B1528304542254N00609540EA0000001895
    +B1528324542262N00609541EA0000001890
    +B1528354542271N00609542EA0000001885
    +B1528374542284N00609544EA0000001879
    +B1528394542296N00609545EA0000001874
    +B1528414542305N00609545EA0000001871
    +B1528434542313N00609547EA0000001868
    +B1528454542324N00609548EA0000001864
    +B1528474542335N00609550EA0000001861
    +B1528494542344N00609555EA0000001860
    +B1528514542353N00609559EA0000001859
    +B1528534542363N00609562EA0000001858
    +B1528554542372N00609567EA0000001858
    +B1528574542382N00609570EA0000001857
    +B1528594542392N00609572EA0000001857
    +B1529014542402N00609574EA0000001855
    +B1529034542413N00609577EA0000001853
    +B1529054542423N00609581EA0000001851
    +B1529074542432N00609585EA0000001849
    +B1529094542442N00609589EA0000001846
    +B1529114542452N00609595EA0000001842
    +B1529134542462N00609600EA0000001839
    +B1529154542471N00609605EA0000001836
    +B1529174542480N00609612EA0000001831
    +B1529194542491N00609618EA0000001826
    +B1529214542502N00609623EA0000001821
    +B1529234542512N00609626EA0000001817
    +B1529254542520N00609629EA0000001814
    +B1529274542529N00609631EA0000001812
    +B1529294542542N00609634EA0000001811
    +B1529314542553N00609636EA0000001811
    +B1529334542559N00609640EA0000001813
    +B1529354542564N00609644EA0000001818
    +B1529374542568N00609647EA0000001824
    +B1529394542571N00609647EA0000001830
    +B1529414542575N00609649EA0000001835
    +B1529434542579N00609652EA0000001839
    +B1529454542581N00609651EA0000001843
    +B1529474542582N00609645EA0000001846
    +B1529494542580N00609637EA0000001849
    +B1529514542568N00609627EA0000001854
    +B1529534542557N00609625EA0000001855
    +B1529554542543N00609631EA0000001856
    +B1529574542533N00609645EA0000001857
    +B1529594542531N00609661EA0000001857
    +B1530014542536N00609672EA0000001859
    +B1530034542543N00609684EA0000001865
    +B1530054542553N00609689EA0000001868
    +B1530074542562N00609694EA0000001869
    +B1530094542572N00609697EA0000001872
    +B1530114542581N00609699EA0000001877
    +B1530134542589N00609701EA0000001882
    +B1530164542596N00609702EA0000001886
    +B1530184542605N00609704EA0000001887
    +B1530204542616N00609708EA0000001889
    +B1530224542624N00609710EA0000001890
    +B1530244542631N00609711EA0000001890
    +B1530264542639N00609715EA0000001888
    +B1530284542648N00609720EA0000001886
    +B1530304542656N00609724EA0000001882
    +B1530324542664N00609731EA0000001878
    +B1530344542675N00609738EA0000001874
    +B1530364542686N00609745EA0000001871
    +B1530384542696N00609751EA0000001868
    +B1530404542706N00609758EA0000001863
    +B1530424542718N00609765EA0000001859
    +B1530444542729N00609773EA0000001856
    +B1530464542738N00609780EA0000001852
    +B1530484542748N00609787EA0000001848
    +B1530504542759N00609795EA0000001844
    +B1530524542770N00609801EA0000001842
    +B1530544542780N00609806EA0000001839
    +B1530564542790N00609813EA0000001836
    +B1530584542800N00609818EA0000001834
    +B1531004542810N00609823EA0000001831
    +B1531024542820N00609828EA0000001829
    +B1531044542830N00609834EA0000001825
    +B1531064542840N00609839EA0000001821
    +B1531084542850N00609843EA0000001817
    +B1531104542860N00609848EA0000001813
    +B1531124542870N00609854EA0000001809
    +B1531144542880N00609860EA0000001804
    +B1531164542891N00609865EA0000001801
    +B1531184542901N00609870EA0000001798
    +B1531204542911N00609875EA0000001794
    +B1531224542921N00609882EA0000001791
    +B1531244542931N00609889EA0000001788
    +B1531264542941N00609895EA0000001786
    +B1531284542950N00609901EA0000001785
    +B1531304542960N00609908EA0000001782
    +B1531324542976N00609918EA0000001779
    +B1531344542986N00609924EA0000001777
    +B1531364542997N00609931EA0000001774
    +B1531384543008N00609935EA0000001772
    +B1531404543018N00609940EA0000001770
    +B1531424543028N00609945EA0000001769
    +B1531444543037N00609948EA0000001770
    +B1531464543044N00609951EA0000001771
    +B1531484543050N00609957EA0000001771
    +B1531504543059N00609960EA0000001771
    +B1531524543069N00609963EA0000001770
    +B1531544543078N00609969EA0000001767
    +B1531574543088N00609975EA0000001764
    +B1531594543099N00609981EA0000001762
    +B1532014543110N00609984EA0000001759
    +B1532034543121N00609988EA0000001756
    +B1532054543133N00609994EA0000001752
    +B1532074543144N00609999EA0000001748
    +B1532094543154N00610003EA0000001745
    +B1532114543165N00610009EA0000001741
    +B1532134543176N00610014EA0000001738
    +B1532154543187N00610019EA0000001735
    +B1532174543198N00610025EA0000001733
    +B1532194543209N00610031EA0000001730
    +B1532214543220N00610038EA0000001727
    +B1532234543231N00610043EA0000001725
    +B1532254543241N00610048EA0000001722
    +B1532274543252N00610054EA0000001720
    +B1532294543263N00610060EA0000001717
    +B1532314543274N00610066EA0000001714
    +B1532334543284N00610072EA0000001712
    +B1532354543295N00610080EA0000001709
    +B1532374543305N00610088EA0000001706
    +B1532394543316N00610095EA0000001704
    +B1532414543327N00610101EA0000001702
    +B1532434543337N00610106EA0000001699
    +B1532454543349N00610112EA0000001697
    +B1532474543360N00610117EA0000001697
    +B1532494543370N00610123EA0000001695
    +B1532514543380N00610128EA0000001693
    +B1532534543392N00610133EA0000001691
    +B1532554543403N00610139EA0000001689
    +B1532574543414N00610144EA0000001687
    +B1532594543425N00610150EA0000001685
    +B1533014543437N00610157EA0000001683
    +B1533034543448N00610164EA0000001682
    +B1533054543459N00610170EA0000001680
    +B1533074543470N00610175EA0000001678
    +B1533094543482N00610182EA0000001675
    +B1533114543499N00610189EA0000001671
    +B1533134543511N00610195EA0000001669
    +B1533154543523N00610200EA0000001667
    +B1533174543536N00610205EA0000001666
    +B1533194543548N00610209EA0000001665
    +B1533214543559N00610214EA0000001663
    +B1533234543571N00610219EA0000001663
    +B1533254543582N00610225EA0000001661
    +B1533274543592N00610232EA0000001658
    +B1533294543603N00610237EA0000001653
    +B1533314543615N00610241EA0000001651
    +B1533334543627N00610246EA0000001649
    +B1533364543639N00610251EA0000001645
    +B1533384543652N00610253EA0000001641
    +B1533404543665N00610256EA0000001638
    +B1533424543676N00610260EA0000001634
    +B1533444543689N00610262EA0000001630
    +B1533464543701N00610262EA0000001627
    +B1533484543712N00610263EA0000001626
    +B1533504543722N00610265EA0000001624
    +B1533524543733N00610267EA0000001622
    +B1533544543745N00610270EA0000001620
    +B1533564543757N00610274EA0000001620
    +B1533584543768N00610279EA0000001622
    +B1534004543785N00610287EA0000001623
    +B1534024543796N00610292EA0000001626
    +B1534044543804N00610299EA0000001629
    +B1534064543810N00610305EA0000001631
    +B1534084543817N00610311EA0000001634
    +B1534104543823N00610316EA0000001638
    +B1534124543829N00610322EA0000001640
    +B1534144543835N00610327EA0000001643
    +B1534164543840N00610327EA0000001646
    +B1534184543844N00610324EA0000001649
    +B1534204543846N00610320EA0000001651
    +B1534234543845N00610314EA0000001655
    +B1534254543844N00610309EA0000001659
    +B1534274543845N00610304EA0000001661
    +B1534294543850N00610301EA0000001663
    +B1534314543856N00610305EA0000001667
    +B1534334543862N00610315EA0000001669
    +B1534354543864N00610332EA0000001671
    +B1534374543858N00610352EA0000001673
    +B1534394543845N00610360EA0000001676
    +B1534414543836N00610354EA0000001678
    +B1534434543836N00610344EA0000001681
    +B1534454543846N00610345EA0000001685
    +B1534474543854N00610359EA0000001690
    +B1534494543853N00610379EA0000001693
    +B1534514543845N00610396EA0000001698
    +B1534534543833N00610399EA0000001701
    +B1534554543825N00610387EA0000001704
    +B1534574543830N00610380EA0000001708
    +B1534594543839N00610384EA0000001712
    +B1535014543846N00610398EA0000001716
    +B1535034543848N00610416EA0000001719
    +B1535054543844N00610434EA0000001723
    +B1535074543831N00610443EA0000001726
    +B1535094543820N00610438EA0000001728
    +B1535114543821N00610427EA0000001731
    +B1535134543830N00610426EA0000001734
    +B1535154543838N00610436EA0000001738
    +B1535174543843N00610453EA0000001742
    +B1535194543843N00610472EA0000001746
    +B1535214543836N00610489EA0000001750
    +B1535234543826N00610494EA0000001755
    +B1535254543819N00610489EA0000001758
    +B1535274543818N00610479EA0000001762
    +B1535294543825N00610474EA0000001765
    +B1535314543834N00610481EA0000001769
    +B1535334543840N00610496EA0000001774
    +B1535354543839N00610524EA0000001782
    +B1535374543831N00610536EA0000001788
    +B1535394543822N00610536EA0000001793
    +B1535414543816N00610528EA0000001798
    +B1535434543817N00610518EA0000001802
    +B1535454543824N00610516EA0000001807
    +B1535474543833N00610524EA0000001813
    +B1535494543840N00610538EA0000001819
    +B1535514543843N00610558EA0000001824
    +B1535534543839N00610578EA0000001830
    +B1535554543830N00610583EA0000001835
    +B1535574543822N00610577EA0000001840
    +B1536004543820N00610567EA0000001846
    +B1536024543827N00610563EA0000001852
    +B1536044543837N00610569EA0000001860
    +B1536064543846N00610582EA0000001866
    +B1536084543851N00610600EA0000001873
    +B1536104543848N00610620EA0000001880
    +B1536124543841N00610631EA0000001889
    +B1536144543831N00610628EA0000001897
    +B1536164543828N00610618EA0000001904
    +B1536184543832N00610609EA0000001911
    +B1536204543844N00610609EA0000001918
    +B1536224543855N00610620EA0000001926
    +B1536244543860N00610639EA0000001934
    +B1536264543857N00610658EA0000001942
    +B1536284543847N00610664EA0000001950
    +B1536304543838N00610658EA0000001958
    +B1536324543836N00610647EA0000001966
    +B1536344543842N00610639EA0000001973
    +B1536364543853N00610641EA0000001980
    +B1536384543863N00610654EA0000001986
    +B1536404543865N00610675EA0000001992
    +B1536424543860N00610693EA0000001999
    +B1536444543852N00610695EA0000002006
    +B1536464543846N00610685EA0000002014
    +B1536484543847N00610675EA0000002022
    +B1536504543854N00610669EA0000002029
    +B1536524543863N00610672EA0000002037
    +B1536544543871N00610685EA0000002044
    +B1536564543873N00610702EA0000002051
    +B1536584543869N00610722EA0000002058
    +B1537004543859N00610734EA0000002064
    +B1537024543849N00610731EA0000002070
    +B1537044543845N00610719EA0000002078
    +B1537064543847N00610711EA0000002086
    +B1537084543850N00610704EA0000002094
    +B1537104543857N00610702EA0000002102
    +B1537124543865N00610709EA0000002110
    +B1537144543868N00610735EA0000002122
    +B1537164543861N00610754EA0000002129
    +B1537184543851N00610766EA0000002137
    +B1537204543842N00610768EA0000002145
    +B1537224543834N00610764EA0000002152
    +B1537244543830N00610755EA0000002160
    +B1537264543834N00610749EA0000002166
    +B1537284543843N00610754EA0000002173
    +B1537304543848N00610768EA0000002180
    +B1537324543848N00610789EA0000002187
    +B1537344543841N00610807EA0000002194
    +B1537374543832N00610817EA0000002202
    +B1537394543821N00610817EA0000002209
    +B1537414543812N00610816EA0000002216
    +B1537434543805N00610809EA0000002221
    +B1537454543806N00610796EA0000002228
    +B1537474543815N00610791EA0000002234
    +B1537494543824N00610800EA0000002243
    +B1537514543829N00610816EA0000002251
    +B1537534543828N00610835EA0000002259
    +B1537554543820N00610850EA0000002266
    +B1537574543806N00610851EA0000002271
    +B1537594543798N00610846EA0000002277
    +B1538014543795N00610841EA0000002281
    +B1538034543793N00610825EA0000002286
    +B1538054543800N00610817EA0000002292
    +B1538074543811N00610821EA0000002299
    +B1538094543817N00610835EA0000002306
    +B1538114543817N00610854EA0000002313
    +B1538134543812N00610873EA0000002321
    +B1538154543802N00610886EA0000002328
    +B1538174543792N00610887EA0000002334
    +B1538194543784N00610879EA0000002339
    +B1538214543783N00610868EA0000002343
    +B1538234543789N00610862EA0000002348
    +B1538254543799N00610868EA0000002355
    +B1538274543810N00610882EA0000002365
    +B1538294543813N00610897EA0000002374
    +B1538314543814N00610913EA0000002382
    +B1538334543810N00610931EA0000002389
    +B1538354543801N00610946EA0000002396
    +B1538374543789N00610946EA0000002401
    +B1538394543783N00610935EA0000002406
    +B1538414543785N00610924EA0000002412
    +B1538434543791N00610918EA0000002418
    +B1538454543800N00610919EA0000002427
    +B1538474543807N00610925EA0000002435
    +B1538494543814N00610938EA0000002443
    +B1538514543815N00610965EA0000002456
    +B1538534543807N00610979EA0000002465
    +B1538554543796N00610980EA0000002472
    +B1538574543790N00610973EA0000002478
    +B1538594543788N00610963EA0000002483
    +B1539014543792N00610954EA0000002489
    +B1539034543801N00610951EA0000002497
    +B1539054543810N00610955EA0000002505
    +B1539074543817N00610965EA0000002513
    +B1539094543822N00610982EA0000002520
    +B1539114543821N00610999EA0000002528
    +B1539134543814N00611012EA0000002535
    +B1539154543803N00611014EA0000002542
    +B1539184543796N00611005EA0000002548
    +B1539204543796N00610993EA0000002555
    +B1539224543802N00610985EA0000002560
    +B1539244543813N00610981EA0000002567
    +B1539264543824N00610986EA0000002574
    +B1539284543831N00610997EA0000002579
    +B1539304543837N00611013EA0000002584
    +B1539324543840N00611031EA0000002588
    +B1539344543843N00611044EA0000002592
    +B1539364543849N00611051EA0000002593
    +B1539384543858N00611056EA0000002593
    +B1539404543867N00611057EA0000002591
    +B1539424543877N00611058EA0000002590
    +B1539444543887N00611064EA0000002588
    +B1539464543898N00611071EA0000002587
    +B1539484543907N00611072EA0000002587
    +B1539504543914N00611073EA0000002589
    +B1539524543920N00611075EA0000002589
    +B1539544543927N00611078EA0000002588
    +B1539564543934N00611079EA0000002588
    +B1539584543940N00611081EA0000002587
    +B1540004543948N00611083EA0000002585
    +B1540024543955N00611085EA0000002583
    +B1540044543964N00611087EA0000002580
    +B1540064543972N00611088EA0000002578
    +B1540084543978N00611083EA0000002576
    +B1540104543984N00611076EA0000002574
    +B1540124543992N00611071EA0000002572
    +B1540144543999N00611065EA0000002571
    +B1540164544005N00611057EA0000002570
    +B1540184544012N00611049EA0000002568
    +B1540204544020N00611041EA0000002567
    +B1540224544026N00611032EA0000002566
    +B1540244544034N00611026EA0000002566
    +B1540264544043N00611021EA0000002566
    +B1540284544050N00611015EA0000002567
    +B1540304544058N00611009EA0000002567
    +B1540324544071N00611001EA0000002565
    +B1540344544078N00610995EA0000002563
    +B1540364544086N00610990EA0000002561
    +B1540384544097N00610986EA0000002561
    +B1540404544105N00610981EA0000002560
    +B1540424544113N00610977EA0000002559
    +B1540444544123N00610975EA0000002557
    +B1540464544132N00610972EA0000002555
    +B1540484544140N00610967EA0000002552
    +B1540504544151N00610962EA0000002547
    +B1540524544163N00610956EA0000002543
    +B1540544544175N00610949EA0000002541
    +B1540564544187N00610945EA0000002537
    +B1540594544200N00610941EA0000002534
    +B1541014544214N00610935EA0000002532
    +B1541034544228N00610929EA0000002531
    +B1541054544239N00610927EA0000002529
    +B1541074544251N00610921EA0000002526
    +B1541094544264N00610913EA0000002523
    +B1541114544278N00610907EA0000002522
    +B1541134544290N00610903EA0000002520
    +B1541154544304N00610899EA0000002517
    +B1541174544318N00610893EA0000002515
    +B1541194544331N00610887EA0000002515
    +B1541214544342N00610880EA0000002513
    +B1541234544354N00610873EA0000002511
    +B1541254544369N00610867EA0000002510
    +B1541274544381N00610860EA0000002510
    +B1541294544394N00610852EA0000002509
    +B1541314544407N00610846EA0000002509
    +B1541334544418N00610839EA0000002509
    +B1541354544429N00610830EA0000002509
    +B1541374544442N00610826EA0000002510
    +B1541394544454N00610823EA0000002511
    +B1541414544465N00610818EA0000002513
    +B1541434544477N00610811EA0000002514
    +B1541454544488N00610804EA0000002515
    +B1541474544499N00610798EA0000002516
    +B1541494544510N00610794EA0000002517
    +B1541514544522N00610789EA0000002518
    +B1541534544533N00610784EA0000002520
    +B1541554544544N00610783EA0000002520
    +B1541574544556N00610780EA0000002519
    +B1541594544568N00610777EA0000002519
    +B1542014544580N00610774EA0000002519
    +B1542034544591N00610771EA0000002518
    +B1542054544603N00610766EA0000002516
    +B1542074544616N00610763EA0000002514
    +B1542094544628N00610760EA0000002511
    +B1542114544640N00610756EA0000002510
    +B1542134544655N00610741EA0000002507
    +B1542154544666N00610733EA0000002503
    +B1542174544678N00610725EA0000002499
    +B1542194544687N00610713EA0000002496
    +B1542214544696N00610703EA0000002492
    +B1542234544706N00610697EA0000002487
    +B1542254544717N00610693EA0000002481
    +B1542274544727N00610687EA0000002476
    +B1542294544737N00610681EA0000002471
    +B1542314544747N00610676EA0000002466
    +B1542334544757N00610671EA0000002460
    +B1542354544767N00610667EA0000002456
    +B1542374544775N00610666EA0000002451
    +B1542404544784N00610665EA0000002444
    +B1542424544793N00610662EA0000002437
    +B1542444544801N00610659EA0000002433
    +B1542464544807N00610654EA0000002426
    +B1542484544813N00610645EA0000002418
    +B1542504544821N00610634EA0000002413
    +B1542524544828N00610623EA0000002408
    +B1542544544836N00610613EA0000002405
    +B1542564544845N00610605EA0000002403
    +B1542584544854N00610597EA0000002401
    +B1543004544861N00610589EA0000002399
    +B1543024544870N00610582EA0000002395
    +B1543044544880N00610574EA0000002393
    +B1543064544890N00610565EA0000002392
    +B1543084544900N00610558EA0000002390
    +B1543104544911N00610551EA0000002388
    +B1543124544922N00610544EA0000002388
    +B1543144544931N00610535EA0000002388
    +B1543164544940N00610528EA0000002387
    +B1543184544950N00610520EA0000002385
    +B1543204544960N00610513EA0000002385
    +B1543224544970N00610507EA0000002385
    +B1543244544979N00610499EA0000002383
    +B1543264544990N00610491EA0000002381
    +B1543284545001N00610483EA0000002381
    +B1543304545012N00610477EA0000002382
    +B1543324545022N00610470EA0000002384
    +B1543344545029N00610465EA0000002384
    +B1543364545038N00610462EA0000002383
    +B1543384545047N00610458EA0000002382
    +B1543404545056N00610455EA0000002381
    +B1543424545064N00610454EA0000002379
    +B1543444545075N00610451EA0000002379
    +B1543464545085N00610448EA0000002379
    +B1543484545094N00610446EA0000002378
    +B1543504545103N00610445EA0000002375
    +B1543524545115N00610444EA0000002372
    +B1543544545133N00610446EA0000002371
    +B1543564545142N00610449EA0000002370
    +B1543584545152N00610451EA0000002369
    +B1544004545164N00610447EA0000002369
    +B1544024545174N00610447EA0000002370
    +B1544044545182N00610451EA0000002372
    +B1544064545189N00610452EA0000002371
    +B1544084545195N00610450EA0000002369
    +B1544104545204N00610447EA0000002369
    +B1544124545213N00610442EA0000002370
    +B1544144545221N00610438EA0000002372
    +B1544164545230N00610437EA0000002375
    +B1544194545238N00610435EA0000002377
    +B1544214545245N00610429EA0000002377
    +B1544234545255N00610421EA0000002379
    +B1544254545262N00610413EA0000002380
    +B1544274545270N00610405EA0000002380
    +B1544294545277N00610396EA0000002378
    +B1544314545286N00610390EA0000002375
    +B1544334545297N00610386EA0000002372
    +B1544354545307N00610381EA0000002369
    +B1544374545316N00610376EA0000002365
    +B1544394545326N00610372EA0000002362
    +B1544414545336N00610367EA0000002359
    +B1544434545344N00610361EA0000002357
    +B1544454545354N00610357EA0000002354
    +B1544474545364N00610353EA0000002352
    +B1544494545373N00610348EA0000002348
    +B1544514545384N00610343EA0000002345
    +B1544534545395N00610340EA0000002343
    +B1544554545405N00610336EA0000002340
    +B1544574545414N00610333EA0000002336
    +B1544594545425N00610331EA0000002332
    +B1545014545436N00610329EA0000002328
    +B1545034545446N00610328EA0000002324
    +B1545054545456N00610328EA0000002319
    +B1545074545467N00610327EA0000002315
    +B1545094545476N00610327EA0000002312
    +B1545114545485N00610328EA0000002308
    +B1545134545495N00610328EA0000002302
    +B1545154545507N00610327EA0000002297
    +B1545174545519N00610328EA0000002293
    +B1545194545533N00610330EA0000002291
    +B1545214545545N00610331EA0000002292
    +B1545234545555N00610329EA0000002294
    +B1545254545559N00610328EA0000002299
    +B1545274545559N00610329EA0000002305
    +B1545294545561N00610326EA0000002310
    +B1545314545561N00610318EA0000002316
    +B1545334545551N00610307EA0000002327
    +B1545354545541N00610299EA0000002333
    +B1545374545524N00610298EA0000002339
    +B1545394545508N00610307EA0000002347
    +B1545414545497N00610323EA0000002355
    +B1545434545491N00610339EA0000002363
    +B1545454545492N00610352EA0000002369
    +B1545474545497N00610359EA0000002374
    +B1545494545501N00610362EA0000002381
    +B1545514545503N00610358EA0000002386
    +B1545534545502N00610349EA0000002392
    +B1545554545496N00610339EA0000002399
    +B1545584545486N00610332EA0000002407
    +B1546004545475N00610328EA0000002413
    +B1546024545460N00610332EA0000002417
    +B1546044545445N00610342EA0000002423
    +B1546064545435N00610357EA0000002428
    +B1546084545431N00610374EA0000002431
    +B1546104545433N00610389EA0000002434
    +B1546124545439N00610395EA0000002437
    +B1546144545444N00610396EA0000002441
    +B1546164545447N00610396EA0000002445
    +B1546184545449N00610395EA0000002450
    +B1546204545452N00610394EA0000002454
    +B1546224545455N00610391EA0000002460
    +B1546244545456N00610382EA0000002463
    +B1546264545453N00610372EA0000002465
    +B1546284545452N00610362EA0000002468
    +B1546304545454N00610356EA0000002473
    +B1546334545456N00610353EA0000002476
    +B1546354545460N00610352EA0000002478
    +B1546374545465N00610349EA0000002482
    +B1546394545470N00610347EA0000002486
    +B1546414545475N00610347EA0000002487
    +B1546434545481N00610347EA0000002488
    +B1546454545487N00610344EA0000002487
    +B1546474545494N00610339EA0000002484
    +B1546494545502N00610336EA0000002483
    +B1546514545509N00610335EA0000002483
    +B1546534545517N00610335EA0000002483
    +B1546554545525N00610334EA0000002482
    +B1546574545534N00610332EA0000002481
    +B1546594545545N00610331EA0000002479
    +B1547014545556N00610329EA0000002478
    +B1547034545568N00610327EA0000002476
    +B1547054545579N00610326EA0000002474
    +B1547074545591N00610326EA0000002472
    +B1547094545603N00610326EA0000002469
    +B1547114545615N00610327EA0000002466
    +B1547134545626N00610327EA0000002465
    +B1547154545637N00610328EA0000002463
    +B1547174545649N00610331EA0000002461
    +B1547194545662N00610334EA0000002460
    +B1547214545674N00610337EA0000002459
    +B1547234545686N00610338EA0000002459
    +B1547254545698N00610340EA0000002458
    +B1547274545711N00610345EA0000002455
    +B1547294545723N00610348EA0000002452
    +B1547314545736N00610349EA0000002449
    +B1547334545748N00610352EA0000002446
    +B1547354545760N00610354EA0000002442
    +B1547374545773N00610354EA0000002439
    +B1547394545786N00610356EA0000002435
    +B1547414545798N00610360EA0000002432
    +B1547434545811N00610364EA0000002428
    +B1547454545825N00610367EA0000002424
    +B1547474545845N00610369EA0000002419
    +B1547494545852N00610369EA0000002418
    +B1547514545872N00610370EA0000002413
    +B1547534545886N00610371EA0000002411
    +B1547554545898N00610374EA0000002409
    +B1547574545910N00610378EA0000002407
    +B1547594545923N00610381EA0000002404
    +B1548014545936N00610383EA0000002402
    +B1548034545948N00610388EA0000002399
    +B1548054545960N00610393EA0000002396
    +B1548074545973N00610397EA0000002394
    +B1548094545985N00610402EA0000002392
    +B1548114545996N00610406EA0000002391
    +B1548144546008N00610412EA0000002389
    +B1548164546020N00610418EA0000002388
    +B1548184546031N00610426EA0000002386
    +B1548204546042N00610434EA0000002385
    +B1548224546053N00610442EA0000002384
    +B1548244546064N00610452EA0000002384
    +B1548264546074N00610462EA0000002383
    +B1548284546085N00610471EA0000002382
    +B1548304546096N00610482EA0000002381
    +B1548324546106N00610493EA0000002379
    +B1548344546118N00610504EA0000002377
    +B1548364546129N00610515EA0000002376
    +B1548384546140N00610525EA0000002375
    +B1548404546151N00610535EA0000002374
    +B1548424546162N00610546EA0000002373
    +B1548444546173N00610557EA0000002371
    +B1548464546184N00610569EA0000002369
    +B1548484546195N00610581EA0000002368
    +B1548504546205N00610594EA0000002367
    +B1548524546215N00610605EA0000002365
    +B1548544546226N00610617EA0000002363
    +B1548564546237N00610629EA0000002361
    +B1548584546246N00610641EA0000002359
    +B1549004546256N00610654EA0000002357
    +B1549024546265N00610668EA0000002354
    +B1549044546273N00610682EA0000002351
    +B1549064546282N00610697EA0000002348
    +B1549084546292N00610713EA0000002345
    +B1549104546300N00610729EA0000002343
    +B1549124546309N00610745EA0000002340
    +B1549144546319N00610757EA0000002336
    +B1549164546330N00610771EA0000002334
    +B1549184546339N00610785EA0000002331
    +B1549204546349N00610797EA0000002327
    +B1549224546359N00610809EA0000002324
    +B1549244546369N00610821EA0000002320
    +B1549264546379N00610832EA0000002316
    +B1549284546395N00610849EA0000002309
    +B1549304546405N00610863EA0000002304
    +B1549324546416N00610877EA0000002299
    +B1549344546425N00610891EA0000002296
    +B1549364546434N00610905EA0000002291
    +B1549384546442N00610919EA0000002285
    +B1549404546451N00610933EA0000002280
    +B1549424546460N00610948EA0000002275
    +B1549444546468N00610962EA0000002270
    +B1549464546476N00610976EA0000002263
    +B1549484546485N00610991EA0000002257
    +B1549504546493N00611006EA0000002251
    +B1549524546502N00611019EA0000002247
    +B1549554546509N00611031EA0000002242
    +B1549574546515N00611045EA0000002236
    +B1549594546522N00611059EA0000002228
    +B1550014546529N00611077EA0000002221
    +B1550034546538N00611093EA0000002215
    +B1550054546547N00611107EA0000002211
    +B1550074546555N00611123EA0000002207
    +B1550094546561N00611138EA0000002203
    +B1550114546568N00611151EA0000002198
    +B1550134546575N00611164EA0000002194
    +B1550154546582N00611180EA0000002190
    +B1550174546589N00611197EA0000002186
    +B1550194546598N00611212EA0000002181
    +B1550214546607N00611228EA0000002177
    +B1550234546614N00611245EA0000002173
    +B1550254546622N00611260EA0000002168
    +B1550274546632N00611276EA0000002163
    +B1550294546639N00611295EA0000002159
    +B1550314546648N00611310EA0000002155
    +B1550334546658N00611326EA0000002151
    +B1550354546666N00611345EA0000002149
    +B1550374546674N00611364EA0000002147
    +B1550394546682N00611383EA0000002146
    +B1550414546689N00611402EA0000002146
    +B1550434546696N00611420EA0000002144
    +B1550454546705N00611438EA0000002142
    +B1550474546716N00611456EA0000002141
    +B1550494546726N00611473EA0000002142
    +B1550514546736N00611490EA0000002141
    +B1550534546746N00611504EA0000002142
    +B1550554546755N00611516EA0000002141
    +B1550574546765N00611528EA0000002139
    +B1550594546776N00611539EA0000002138
    +B1551014546787N00611548EA0000002138
    +B1551034546797N00611559EA0000002137
    +B1551054546807N00611571EA0000002137
    +B1551074546817N00611583EA0000002137
    +B1551094546832N00611600EA0000002136
    +B1551114546842N00611614EA0000002135
    +B1551134546853N00611627EA0000002134
    +B1551154546865N00611637EA0000002133
    +B1551174546876N00611647EA0000002131
    +B1551194546889N00611657EA0000002128
    +B1551214546902N00611667EA0000002126
    +B1551234546914N00611677EA0000002125
    +B1551254546925N00611688EA0000002122
    +B1551274546937N00611698EA0000002118
    +B1551294546949N00611709EA0000002115
    +B1551314546962N00611719EA0000002113
    +B1551344546973N00611728EA0000002109
    +B1551364546985N00611738EA0000002105
    +B1551384546997N00611748EA0000002101
    +B1551404547008N00611759EA0000002097
    +B1551424547019N00611771EA0000002095
    +B1551444547029N00611781EA0000002091
    +B1551464547040N00611792EA0000002087
    +B1551484547050N00611804EA0000002082
    +B1551504547060N00611816EA0000002078
    +B1551524547071N00611827EA0000002074
    +B1551544547081N00611839EA0000002069
    +B1551564547091N00611851EA0000002065
    +B1551584547100N00611862EA0000002060
    +B1552004547110N00611873EA0000002055
    +B1552024547120N00611884EA0000002051
    +B1552044547130N00611895EA0000002047
    +B1552064547141N00611904EA0000002044
    +B1552084547151N00611913EA0000002040
    +B1552104547161N00611923EA0000002036
    +B1552124547172N00611933EA0000002032
    +B1552144547182N00611943EA0000002028
    +B1552164547193N00611953EA0000002024
    +B1552184547203N00611964EA0000002020
    +B1552204547213N00611975EA0000002017
    +B1552224547223N00611986EA0000002014
    +B1552244547233N00611997EA0000002011
    +B1552264547243N00612008EA0000002009
    +B1552284547254N00612019EA0000002006
    +B1552304547264N00612031EA0000002005
    +B1552324547274N00612044EA0000002003
    +B1552344547284N00612056EA0000002000
    +B1552364547294N00612068EA0000001997
    +B1552384547304N00612081EA0000001993
    +B1552404547315N00612094EA0000001990
    +B1552424547325N00612108EA0000001987
    +B1552444547336N00612118EA0000001984
    +B1552464547346N00612129EA0000001982
    +B1552484547356N00612141EA0000001979
    +B1552504547371N00612163EA0000001974
    +B1552524547381N00612177EA0000001972
    +B1552544547390N00612189EA0000001971
    +B1552564547400N00612201EA0000001968
    +B1552584547411N00612214EA0000001966
    +B1553004547421N00612227EA0000001965
    +B1553024547430N00612239EA0000001962
    +B1553044547440N00612252EA0000001960
    +B1553064547450N00612266EA0000001958
    +B1553084547459N00612280EA0000001957
    +B1553104547468N00612294EA0000001956
    +B1553124547477N00612309EA0000001955
    +B1553154547484N00612323EA0000001954
    +B1553174547493N00612336EA0000001952
    +B1553194547502N00612351EA0000001951
    +B1553214547511N00612364EA0000001950
    +B1553234547520N00612375EA0000001949
    +B1553254547529N00612387EA0000001947
    +B1553274547539N00612398EA0000001945
    +B1553294547549N00612407EA0000001942
    +B1553314547559N00612417EA0000001940
    +B1553334547569N00612427EA0000001938
    +B1553354547579N00612436EA0000001935
    +B1553374547589N00612446EA0000001933
    +B1553394547594N00612451EA0000001930
    +B1553414547610N00612465EA0000001928
    +B1553434547620N00612476EA0000001926
    +B1553454547629N00612486EA0000001926
    +B1553474547637N00612498EA0000001923
    +B1553494547647N00612510EA0000001921
    +B1553514547657N00612521EA0000001920
    +B1553534547665N00612532EA0000001917
    +B1553554547673N00612542EA0000001914
    +B1553574547683N00612555EA0000001910
    +B1553594547693N00612568EA0000001907
    +B1554014547702N00612581EA0000001904
    +B1554034547711N00612595EA0000001900
    +B1554054547720N00612608EA0000001897
    +B1554074547729N00612620EA0000001894
    +B1554094547737N00612634EA0000001891
    +B1554114547746N00612648EA0000001888
    +B1554134547755N00612661EA0000001885
    +B1554154547763N00612674EA0000001881
    +B1554174547771N00612688EA0000001877
    +B1554194547779N00612701EA0000001874
    +B1554214547786N00612715EA0000001870
    +B1554234547794N00612731EA0000001866
    +B1554254547801N00612746EA0000001864
    +B1554274547808N00612761EA0000001861
    +B1554294547816N00612775EA0000001857
    +B1554314547827N00612797EA0000001851
    +B1554334547835N00612810EA0000001848
    +B1554354547842N00612823EA0000001844
    +B1554374547850N00612837EA0000001841
    +B1554394547857N00612850EA0000001838
    +B1554414547864N00612864EA0000001836
    +B1554434547871N00612876EA0000001834
    +B1554454547878N00612888EA0000001832
    +B1554474547885N00612901EA0000001829
    +B1554494547891N00612914EA0000001825
    +B1554514547898N00612928EA0000001822
    +B1554534547905N00612941EA0000001819
    +B1554564547911N00612954EA0000001816
    +B1554584547918N00612966EA0000001813
    +B1555004547926N00612978EA0000001810
    +B1555024547933N00612989EA0000001807
    +B1555044547941N00612998EA0000001803
    +B1555064547950N00613006EA0000001800
    +B1555084547958N00613015EA0000001796
    +B1555104547967N00613023EA0000001793
    +B1555124547975N00613032EA0000001788
    +B1555144547984N00613042EA0000001784
    +B1555164547992N00613054EA0000001782
    +B1555184547999N00613065EA0000001779
    +B1555204548006N00613077EA0000001775
    +B1555224548013N00613090EA0000001771
    +B1555244548020N00613103EA0000001768
    +B1555264548027N00613117EA0000001765
    +B1555284548034N00613130EA0000001762
    +B1555304548040N00613143EA0000001758
    +B1555324548047N00613157EA0000001755
    +B1555344548054N00613170EA0000001752
    +B1555364548061N00613183EA0000001748
    +B1555384548068N00613196EA0000001745
    +B1555404548075N00613210EA0000001742
    +B1555424548081N00613223EA0000001739
    +B1555444548088N00613237EA0000001736
    +B1555464548094N00613252EA0000001734
    +B1555484548101N00613265EA0000001732
    +B1555504548108N00613278EA0000001730
    +B1555524548115N00613292EA0000001727
    +B1555544548121N00613306EA0000001723
    +B1555564548128N00613320EA0000001720
    +B1555584548135N00613335EA0000001717
    +B1556004548141N00613350EA0000001714
    +B1556024548148N00613365EA0000001710
    +B1556044548154N00613380EA0000001707
    +B1556064548161N00613394EA0000001704
    +B1556084548167N00613409EA0000001701
    +B1556104548177N00613432EA0000001695
    +B1556124548184N00613448EA0000001692
    +B1556144548191N00613465EA0000001690
    +B1556164548197N00613480EA0000001687
    +B1556184548204N00613494EA0000001682
    +B1556204548212N00613509EA0000001677
    +B1556224548220N00613524EA0000001673
    +B1556244548228N00613538EA0000001668
    +B1556264548236N00613551EA0000001663
    +B1556284548244N00613568EA0000001658
    +B1556304548251N00613584EA0000001655
    +B1556324548259N00613600EA0000001652
    +B1556344548265N00613617EA0000001648
    +B1556374548273N00613634EA0000001644
    +B1556394548280N00613652EA0000001640
    +B1556414548288N00613669EA0000001637
    +B1556434548296N00613685EA0000001633
    +B1556454548303N00613700EA0000001629
    +B1556474548311N00613715EA0000001625
    +B1556494548319N00613732EA0000001621
    +B1556514548326N00613749EA0000001617
    +B1556534548334N00613766EA0000001613
    +B1556554548341N00613782EA0000001610
    +B1556574548349N00613798EA0000001606
    +B1556594548356N00613815EA0000001602
    +B1557014548364N00613832EA0000001598
    +B1557034548372N00613848EA0000001594
    +B1557054548380N00613865EA0000001590
    +B1557074548387N00613881EA0000001587
    +B1557094548395N00613898EA0000001582
    +B1557114548403N00613914EA0000001577
    +B1557134548412N00613930EA0000001572
    +B1557154548420N00613945EA0000001568
    +B1557174548429N00613959EA0000001562
    +B1557194548438N00613975EA0000001556
    +B1557214548448N00613993EA0000001552
    +B1557234548455N00614012EA0000001549
    +B1557254548457N00614027EA0000001545
    +B1557274548462N00614045EA0000001541
    +B1557294548467N00614063EA0000001539
    +B1557314548471N00614079EA0000001538
    +B1557334548476N00614093EA0000001534
    +B1557354548483N00614109EA0000001530
    +B1557374548489N00614127EA0000001529
    +B1557394548495N00614142EA0000001527
    +B1557414548502N00614157EA0000001525
    +B1557434548509N00614172EA0000001523
    +B1557454548517N00614186EA0000001521
    +B1557474548524N00614202EA0000001520
    +B1557494548531N00614218EA0000001518
    +B1557514548542N00614240EA0000001516
    +B1557534548549N00614255EA0000001515
    +B1557554548557N00614269EA0000001513
    +B1557574548564N00614284EA0000001510
    +B1557594548572N00614299EA0000001508
    +B1558014548581N00614315EA0000001506
    +B1558034548588N00614330EA0000001504
    +B1558054548596N00614345EA0000001502
    +B1558074548603N00614360EA0000001501
    +B1558094548610N00614376EA0000001500
    +B1558114548617N00614392EA0000001498
    +B1558134548623N00614409EA0000001495
    +B1558154548630N00614426EA0000001494
    +B1558184548636N00614442EA0000001494
    +B1558204548641N00614456EA0000001491
    +B1558224548648N00614472EA0000001489
    +B1558244548655N00614487EA0000001487
    +B1558264548662N00614500EA0000001485
    +B1558284548668N00614514EA0000001482
    +B1558304548676N00614528EA0000001480
    +B1558324548683N00614543EA0000001478
    +B1558344548691N00614558EA0000001476
    +B1558364548698N00614571EA0000001475
    +B1558384548706N00614584EA0000001473
    +B1558404548714N00614597EA0000001472
    +B1558424548721N00614609EA0000001471
    +B1558444548728N00614622EA0000001469
    +B1558464548735N00614636EA0000001468
    +B1558484548741N00614650EA0000001466
    +B1558504548748N00614663EA0000001465
    +B1558524548754N00614674EA0000001462
    +B1558544548762N00614685EA0000001458
    +B1558564548770N00614694EA0000001455
    +B1558584548778N00614703EA0000001451
    +B1559004548785N00614712EA0000001448
    +B1559024548793N00614722EA0000001445
    +B1559044548801N00614734EA0000001444
    +B1559064548808N00614746EA0000001442
    +B1559084548817N00614756EA0000001443
    +B1559104548822N00614764EA0000001445
    +B1559124548826N00614775EA0000001445
    +B1559144548830N00614787EA0000001444
    +B1559164548835N00614795EA0000001444
    +B1559184548839N00614803EA0000001445
    +B1559204548844N00614810EA0000001446
    +B1559224548851N00614816EA0000001448
    +B1559244548856N00614823EA0000001452
    +B1559264548860N00614828EA0000001454
    +B1559284548865N00614831EA0000001455
    +B1559304548875N00614837EA0000001456
    +B1559324548881N00614844EA0000001456
    +B1559344548890N00614851EA0000001456
    +B1559364548899N00614859EA0000001457
    +B1559384548906N00614868EA0000001459
    +B1559404548914N00614877EA0000001463
    +B1559424548922N00614884EA0000001467
    +B1559444548928N00614889EA0000001473
    +B1559464548933N00614894EA0000001477
    +B1559484548938N00614899EA0000001481
    +B1559504548944N00614906EA0000001483
    +B1559524548952N00614912EA0000001486
    +B1559544548959N00614917EA0000001489
    +B1559574548965N00614921EA0000001493
    +B1559594548970N00614927EA0000001496
    +B1600014548976N00614933EA0000001498
    +B1600034548983N00614939EA0000001499
    +B1600054548990N00614947EA0000001498
    +B1600074548998N00614954EA0000001497
    +B1600094549006N00614962EA0000001495
    +B1600114549015N00614970EA0000001494
    +B1600134549025N00614978EA0000001494
    +B1600154549033N00614987EA0000001496
    +B1600174549039N00614995EA0000001499
    +B1600194549047N00615004EA0000001502
    +B1600214549051N00615009EA0000001506
    +B1600234549059N00615018EA0000001509
    +B1600254549065N00615025EA0000001512
    +B1600274549075N00615033EA0000001515
    +B1600294549084N00615038EA0000001519
    +B1600314549090N00615042EA0000001527
    +B1600334549096N00615048EA0000001533
    +B1600354549102N00615056EA0000001538
    +B1600374549108N00615067EA0000001542
    +B1600394549114N00615073EA0000001546
    +B1600414549117N00615074EA0000001549
    +B1600434549121N00615076EA0000001551
    +B1600454549127N00615081EA0000001553
    +B1600474549135N00615085EA0000001557
    +B1600494549142N00615089EA0000001561
    +B1600514549151N00615093EA0000001565
    +B1600534549161N00615099EA0000001570
    +B1600554549168N00615101EA0000001574
    +B1600574549176N00615105EA0000001579
    +B1600594549185N00615109EA0000001584
    +B1601014549193N00615111EA0000001589
    +B1601034549199N00615113EA0000001593
    +B1601054549208N00615116EA0000001596
    +B1601074549218N00615118EA0000001601
    +B1601094549226N00615122EA0000001606
    +B1601114549233N00615125EA0000001609
    +B1601134549247N00615128EA0000001613
    +B1601154549255N00615132EA0000001615
    +B1601174549264N00615136EA0000001616
    +B1601194549273N00615141EA0000001619
    +B1601214549282N00615147EA0000001622
    +B1601234549289N00615152EA0000001625
    +B1601254549297N00615156EA0000001629
    +B1601274549305N00615161EA0000001632
    +B1601294549312N00615166EA0000001636
    +B1601314549319N00615170EA0000001638
    +B1601334549327N00615175EA0000001641
    +B1601364549336N00615181EA0000001645
    +B1601384549344N00615187EA0000001649
    +B1601404549352N00615193EA0000001653
    +B1601424549360N00615200EA0000001657
    +B1601444549368N00615207EA0000001659
    +B1601464549377N00615215EA0000001661
    +B1601484549386N00615224EA0000001663
    +B1601504549395N00615233EA0000001665
    +B1601524549405N00615243EA0000001668
    +B1601544549414N00615253EA0000001672
    +B1601564549422N00615262EA0000001675
    +B1601584549427N00615267EA0000001677
    +B1602004549442N00615282EA0000001681
    +B1602024549451N00615291EA0000001683
    +B1602044549460N00615300EA0000001683
    +B1602064549469N00615308EA0000001683
    +B1602084549480N00615317EA0000001684
    +B1602104549490N00615324EA0000001686
    +B1602124549499N00615331EA0000001689
    +B1602144549508N00615339EA0000001691
    +B1602164549517N00615349EA0000001692
    +B1602184549527N00615358EA0000001691
    +B1602204549538N00615366EA0000001693
    +B1602224549548N00615375EA0000001695
    +B1602244549559N00615384EA0000001698
    +B1602264549570N00615392EA0000001703
    +B1602284549579N00615402EA0000001707
    +B1602304549586N00615412EA0000001710
    +B1602324549594N00615421EA0000001712
    +B1602344549602N00615429EA0000001712
    +B1602364549612N00615435EA0000001709
    +B1602384549624N00615444EA0000001707
    +B1602404549634N00615453EA0000001706
    +B1602424549646N00615462EA0000001704
    +B1602444549657N00615473EA0000001704
    +B1602464549667N00615485EA0000001705
    +B1602484549676N00615497EA0000001704
    +B1602504549690N00615514EA0000001701
    +B1602524549700N00615522EA0000001698
    +B1602544549711N00615531EA0000001695
    +B1602564549721N00615543EA0000001693
    +B1602584549730N00615555EA0000001691
    +B1603004549741N00615567EA0000001687
    +B1603024549751N00615580EA0000001685
    +B1603044549761N00615594EA0000001684
    +B1603064549769N00615608EA0000001683
    +B1603084549779N00615622EA0000001682
    +B1603104549788N00615638EA0000001682
    +B1603124549796N00615655EA0000001682
    +B1603144549805N00615673EA0000001682
    +B1603174549814N00615690EA0000001684
    +B1603194549823N00615708EA0000001684
    +B1603214549831N00615725EA0000001685
    +B1603234549841N00615743EA0000001684
    +B1603254549850N00615763EA0000001686
    +B1603274549858N00615781EA0000001689
    +B1603294549864N00615798EA0000001692
    +B1603314549869N00615816EA0000001695
    +B1603334549875N00615835EA0000001700
    +B1603354549878N00615844EA0000001705
    +B1603374549885N00615869EA0000001710
    +B1603394549890N00615888EA0000001713
    +B1603414549896N00615905EA0000001716
    +B1603434549903N00615915EA0000001717
    +B1603454549911N00615922EA0000001717
    +B1603474549918N00615934EA0000001716
    +B1603494549925N00615955EA0000001714
    +B1603514549930N00615975EA0000001714
    +B1603534549933N00615994EA0000001713
    +B1603554549936N00616013EA0000001710
    +B1603574549940N00616034EA0000001709
    +B1603594549943N00616054EA0000001709
    +B1604014549945N00616072EA0000001710
    +B1604034549946N00616090EA0000001710
    +B1604054549949N00616108EA0000001711
    +B1604074549951N00616126EA0000001712
    +B1604094549954N00616145EA0000001712
    +B1604114549956N00616164EA0000001712
    +B1604134549959N00616182EA0000001712
    +B1604154549963N00616199EA0000001712
    +B1604174549968N00616217EA0000001712
    +B1604194549973N00616232EA0000001712
    +B1604214549979N00616245EA0000001712
    +B1604234549987N00616256EA0000001711
    +B1604254549995N00616268EA0000001710
    +B1604274550003N00616278EA0000001709
    +B1604294550017N00616288EA0000001706
    +B1604314550027N00616293EA0000001705
    +B1604334550038N00616298EA0000001704
    +B1604354550048N00616299EA0000001704
    +B1604374550060N00616299EA0000001703
    +B1604394550071N00616301EA0000001705
    +B1604414550083N00616303EA0000001706
    +B1604434550094N00616304EA0000001708
    +B1604454550104N00616307EA0000001709
    +B1604474550114N00616308EA0000001709
    +B1604494550127N00616309EA0000001708
    +B1604514550139N00616311EA0000001708
    +B1604534550151N00616314EA0000001708
    +B1604564550161N00616314EA0000001709
    +B1604584550173N00616314EA0000001709
    +B1605004550184N00616315EA0000001709
    +B1605024550196N00616315EA0000001708
    +B1605044550208N00616315EA0000001707
    +B1605064550219N00616311EA0000001705
    +B1605084550224N00616303EA0000001704
    +B1605104550221N00616293EA0000001702
    +B1605124550217N00616291EA0000001701
    +B1605144550203N00616295EA0000001702
    +B1605164550194N00616297EA0000001702
    +B1605184550185N00616299EA0000001703
    +B1605204550176N00616303EA0000001703
    +B1605224550166N00616305EA0000001704
    +B1605244550157N00616307EA0000001704
    +B1605264550147N00616309EA0000001703
    +B1605284550136N00616309EA0000001701
    +B1605304550125N00616311EA0000001700
    +B1605324550116N00616312EA0000001699
    +B1605344550106N00616310EA0000001697
    +B1605364550093N00616309EA0000001695
    +B1605384550082N00616308EA0000001695
    +B1605404550074N00616308EA0000001694
    +B1605424550063N00616306EA0000001692
    +B1605444550052N00616303EA0000001692
    +B1605464550042N00616301EA0000001694
    +B1605484550032N00616299EA0000001695
    +B1605504550022N00616296EA0000001696
    +B1605524550012N00616294EA0000001697
    +B1605544550002N00616291EA0000001697
    +B1605564549991N00616287EA0000001697
    +B1605584549980N00616283EA0000001697
    +B1606004549969N00616278EA0000001698
    +B1606024549957N00616275EA0000001700
    +B1606044549948N00616271EA0000001702
    +B1606064549941N00616265EA0000001703
    +B1606084549935N00616255EA0000001703
    +B1606104549932N00616236EA0000001703
    +B1606124549933N00616225EA0000001705
    +B1606144549933N00616217EA0000001706
    +B1606164549933N00616208EA0000001708
    +B1606184549934N00616199EA0000001711
    +B1606204549936N00616192EA0000001714
    +B1606224549941N00616187EA0000001716
    +B1606244549949N00616186EA0000001718
    +B1606264549956N00616201EA0000001720
    +B1606284549954N00616225EA0000001721
    +B1606304549943N00616234EA0000001722
    +B1606324549930N00616222EA0000001723
    +B1606354549927N00616208EA0000001726
    +B1606374549928N00616201EA0000001731
    +B1606394549926N00616198EA0000001735
    +B1606414549927N00616192EA0000001739
    +B1606434549933N00616190EA0000001743
    +B1606454549942N00616202EA0000001747
    +B1606474549946N00616225EA0000001750
    +B1606494549945N00616237EA0000001754
    +B1606514549935N00616249EA0000001758
    +B1606534549928N00616241EA0000001762
    +B1606554549934N00616241EA0000001765
    +B1606574549939N00616255EA0000001768
    +B1606594549940N00616278EA0000001771
    +B1607014549936N00616296EA0000001773
    +B1607034549931N00616299EA0000001776
    +B1607054549930N00616292EA0000001780
    +B1607074549929N00616288EA0000001784
    +B1607094549929N00616282EA0000001788
    +B1607114549932N00616279EA0000001791
    +B1607134549934N00616298EA0000001793
    +B1607154549929N00616321EA0000001796
    +B1607174549919N00616330EA0000001799
    +B1607194549903N00616301EA0000001806
    +B1607214549908N00616296EA0000001809
    +B1607234549916N00616306EA0000001813
    +B1607254549920N00616328EA0000001817
    +B1607274549911N00616346EA0000001823
    +B1607294549900N00616347EA0000001826
    +B1607314549895N00616332EA0000001831
    +B1607334549902N00616331EA0000001834
    +B1607354549911N00616343EA0000001839
    +B1607374549913N00616364EA0000001842
    +B1607394549903N00616386EA0000001846
    +B1607414549891N00616391EA0000001849
    +B1607434549884N00616391EA0000001853
    +B1607454549885N00616384EA0000001857
    +B1607474549890N00616385EA0000001861
    +B1607494549894N00616404EA0000001865
    +B1607514549887N00616432EA0000001872
    +B1607534549876N00616426EA0000001876
    +B1607554549872N00616414EA0000001880
    +B1607574549871N00616406EA0000001885
    +B1607594549873N00616420EA0000001889
    +B1608014549872N00616441EA0000001894
    +B1608034549865N00616454EA0000001899
    +B1608054549858N00616454EA0000001904
    +B1608074549857N00616444EA0000001909
    +B1608094549861N00616441EA0000001915
    +B1608114549868N00616448EA0000001921
    +B1608134549872N00616466EA0000001926
    +B1608164549870N00616488EA0000001931
    +B1608184549863N00616504EA0000001936
    +B1608204549857N00616504EA0000001940
    +B1608224549857N00616494EA0000001945
    +B1608244549863N00616492EA0000001951
    +B1608264549867N00616496EA0000001956
    +B1608284549871N00616522EA0000001960
    +B1608304549867N00616540EA0000001964
    +B1608324549857N00616539EA0000001969
    +B1608344549853N00616527EA0000001974
    +B1608364549854N00616519EA0000001980
    +B1608384549859N00616520EA0000001987
    +B1608404549866N00616531EA0000001992
    +B1608424549870N00616553EA0000001996
    +B1608444549866N00616576EA0000002001
    +B1608464549860N00616586EA0000002005
    +B1608484549858N00616582EA0000002011
    +B1608504549863N00616577EA0000002017
    +B1608524549870N00616579EA0000002024
    +B1608544549877N00616584EA0000002029
    +B1608564549884N00616597EA0000002034
    +B1608584549883N00616614EA0000002039
    +B1609004549875N00616620EA0000002045
    +B1609024549869N00616611EA0000002052
    +B1609044549868N00616599EA0000002059
    +B1609064549874N00616595EA0000002065
    +B1609084549880N00616611EA0000002071
    +B1609104549881N00616633EA0000002076
    +B1609124549877N00616648EA0000002081
    +B1609144549870N00616653EA0000002088
    +B1609164549862N00616653EA0000002095
    +B1609184549856N00616645EA0000002102
    +B1609204549856N00616633EA0000002108
    +B1609224549866N00616631EA0000002114
    +B1609244549875N00616647EA0000002117
    +B1609264549876N00616671EA0000002121
    +B1609284549867N00616689EA0000002127
    +B1609304549861N00616680EA0000002131
    +B1609324549858N00616672EA0000002136
    +B1609344549858N00616666EA0000002140
    +B1609364549864N00616663EA0000002143
    +B1609384549872N00616675EA0000002144
    +B1609404549874N00616701EA0000002146
    +B1609424549871N00616722EA0000002148
    +B1609444549860N00616730EA0000002151
    +B1609464549850N00616738EA0000002155
    +B1609484549841N00616745EA0000002161
    +B1609504549832N00616744EA0000002167
    +B1609524549828N00616733EA0000002173
    +B1609554549832N00616727EA0000002179
    +B1609574549838N00616736EA0000002184
    +B1609594549842N00616760EA0000002188
    +B1610014549839N00616782EA0000002192
    +B1610034549831N00616785EA0000002196
    +B1610054549825N00616777EA0000002202
    +B1610074549821N00616768EA0000002208
    +B1610094549823N00616757EA0000002212
    +B1610114549829N00616755EA0000002215
    +B1610134549836N00616770EA0000002219
    +B1610154549834N00616792EA0000002222
    +B1610174549826N00616805EA0000002224
    +B1610194549815N00616804EA0000002226
    +B1610214549807N00616799EA0000002229
    +B1610234549804N00616794EA0000002233
    +B1610254549806N00616788EA0000002236
    +B1610274549813N00616785EA0000002238
    +B1610294549821N00616797EA0000002240
    +B1610314549823N00616820EA0000002242
    +B1610334549818N00616837EA0000002244
    +B1610354549806N00616844EA0000002247
    +B1610374549797N00616844EA0000002252
    +B1610394549791N00616838EA0000002256
    +B1610414549790N00616831EA0000002260
    +B1610434549796N00616828EA0000002264
    +B1610454549805N00616834EA0000002267
    +B1610474549810N00616855EA0000002269
    +B1610494549809N00616879EA0000002271
    +B1610514549801N00616895EA0000002274
    +B1610534549791N00616904EA0000002278
    +B1610554549781N00616909EA0000002282
    +B1610574549772N00616908EA0000002284
    +B1610594549770N00616900EA0000002286
    +B1611014549775N00616896EA0000002288
    +B1611034549783N00616902EA0000002290
    +B1611054549789N00616922EA0000002292
    +B1611074549787N00616945EA0000002294
    +B1611094549775N00616969EA0000002298
    +B1611114549765N00616977EA0000002301
    +B1611134549756N00616979EA0000002303
    +B1611154549752N00616972EA0000002303
    +B1611174549754N00616965EA0000002303
    +B1611194549761N00616964EA0000002303
    +B1611214549771N00616979EA0000002303
    +B1611234549775N00617003EA0000002303
    +B1611254549771N00617025EA0000002302
    +B1611274549762N00617037EA0000002303
    +B1611294549754N00617040EA0000002304
    +B1611314549747N00617046EA0000002305
    +B1611334549739N00617047EA0000002305
    +B1611364549734N00617038EA0000002304
    +B1611384549733N00617028EA0000002304
    +B1611404549731N00617021EA0000002305
    +B1611424549729N00617015EA0000002307
    +B1611444549726N00617007EA0000002309
    +B1611464549722N00617001EA0000002313
    +B1611484549719N00616996EA0000002318
    +B1611504549717N00616990EA0000002323
    +B1611524549714N00616985EA0000002328
    +B1611544549711N00616977EA0000002332
    +B1611564549710N00616973EA0000002337
    +B1611584549705N00616969EA0000002342
    +B1612004549699N00616969EA0000002347
    +B1612024549690N00616972EA0000002350
    +B1612044549683N00616983EA0000002354
    +B1612064549676N00616996EA0000002357
    +B1612084549672N00617012EA0000002361
    +B1612104549675N00617030EA0000002365
    +B1612124549684N00617044EA0000002369
    +B1612144549694N00617047EA0000002375
    +B1612164549701N00617043EA0000002381
    +B1612184549704N00617039EA0000002387
    +B1612204549704N00617035EA0000002392
    +B1612224549700N00617032EA0000002397
    +B1612244549692N00617031EA0000002399
    +B1612264549682N00617034EA0000002402
    +B1612284549674N00617043EA0000002406
    +B1612304549671N00617059EA0000002409
    +B1612324549673N00617078EA0000002411
    +B1612344549682N00617091EA0000002414
    +B1612364549692N00617095EA0000002418
    +B1612384549701N00617093EA0000002423
    +B1612404549706N00617088EA0000002427
    +B1612424549708N00617083EA0000002431
    +B1612444549706N00617078EA0000002434
    +B1612464549698N00617077EA0000002435
    +B1612484549684N00617096EA0000002436
    +B1612504549680N00617116EA0000002437
    +B1612524549683N00617132EA0000002439
    +B1612544549691N00617146EA0000002441
    +B1612564549701N00617159EA0000002443
    +B1612584549711N00617164EA0000002445
    +B1613004549718N00617164EA0000002448
    +B1613024549724N00617163EA0000002449
    +B1613044549728N00617159EA0000002450
    +B1613064549727N00617153EA0000002450
    +B1613084549724N00617147EA0000002450
    +B1613104549722N00617140EA0000002449
    +B1613124549720N00617132EA0000002448
    +B1613154549718N00617124EA0000002446
    +B1613174549715N00617116EA0000002445
    +B1613194549713N00617108EA0000002443
    +B1613214549710N00617099EA0000002440
    +B1613234549707N00617090EA0000002438
    +B1613254549703N00617082EA0000002437
    +B1613274549701N00617074EA0000002437
    +B1613294549698N00617066EA0000002436
    +B1613314549695N00617057EA0000002436
    +B1613334549693N00617049EA0000002435
    +B1613354549691N00617041EA0000002433
    +B1613374549690N00617036EA0000002430
    +B1613394549686N00617022EA0000002430
    +B1613414549685N00617015EA0000002430
    +B1613434549683N00617005EA0000002431
    +B1613454549682N00616996EA0000002433
    +B1613474549682N00616990EA0000002438
    +B1613494549682N00616984EA0000002444
    +B1613514549683N00616976EA0000002450
    +B1613534549683N00616969EA0000002454
    +B1613554549683N00616961EA0000002458
    +B1613574549679N00616952EA0000002460
    +B1613594549671N00616948EA0000002461
    +B1614014549661N00616951EA0000002460
    +B1614034549651N00616961EA0000002460
    +B1614054549647N00616979EA0000002460
    +B1614074549649N00616996EA0000002462
    +B1614094549654N00617009EA0000002465
    +B1614114549661N00617021EA0000002466
    +B1614134549668N00617032EA0000002469
    +B1614154549675N00617044EA0000002474
    +B1614174549681N00617047EA0000002477
    +B1614194549688N00617044EA0000002479
    +B1614214549693N00617039EA0000002481
    +B1614234549691N00617030EA0000002482
    +B1614254549682N00617018EA0000002485
    +B1614274549670N00617019EA0000002486
    +B1614294549654N00617037EA0000002487
    +B1614314549650N00617053EA0000002487
    +B1614334549650N00617073EA0000002489
    +B1614354549653N00617088EA0000002491
    +B1614374549660N00617100EA0000002494
    +B1614394549667N00617115EA0000002498
    +B1614414549673N00617129EA0000002502
    +B1614434549680N00617140EA0000002504
    +B1614454549688N00617149EA0000002507
    +B1614474549694N00617152EA0000002510
    +B1614494549698N00617146EA0000002512
    +B1614524549696N00617138EA0000002513
    +B1614544549687N00617132EA0000002514
    +B1614564549674N00617136EA0000002515
    +B1614584549662N00617151EA0000002516
    +B1615004549659N00617173EA0000002517
    +B1615024549663N00617192EA0000002520
    +B1615044549671N00617208EA0000002523
    +B1615064549677N00617220EA0000002525
    +B1615084549685N00617231EA0000002525
    +B1615104549694N00617242EA0000002526
    +B1615124549702N00617253EA0000002529
    +B1615144549709N00617262EA0000002529
    +B1615164549713N00617265EA0000002528
    +B1615184549727N00617278EA0000002526
    +B1615204549734N00617290EA0000002524
    +B1615224549739N00617303EA0000002520
    +B1615244549745N00617317EA0000002515
    +B1615264549750N00617333EA0000002508
    +B1615284549753N00617350EA0000002503
    +B1615304549757N00617365EA0000002498
    +B1615324549761N00617379EA0000002493
    +B1615344549767N00617393EA0000002487
    +B1615364549774N00617409EA0000002480
    +B1615384549782N00617426EA0000002473
    +B1615404549788N00617445EA0000002465
    +B1615424549793N00617465EA0000002459
    +B1615444549797N00617485EA0000002453
    +B1615464549801N00617505EA0000002447
    +B1615484549806N00617526EA0000002441
    +B1615504549811N00617547EA0000002436
    +B1615524549815N00617570EA0000002430
    +B1615544549819N00617594EA0000002426
    +B1615564549823N00617617EA0000002422
    +B1615584549827N00617639EA0000002420
    +B1616004549831N00617660EA0000002415
    +B1616024549837N00617682EA0000002410
    +B1616044549846N00617716EA0000002402
    +B1616064549848N00617737EA0000002399
    +B1616084549851N00617756EA0000002395
    +B1616104549854N00617775EA0000002389
    +B1616124549855N00617798EA0000002383
    +B1616144549856N00617819EA0000002379
    +B1616164549858N00617836EA0000002376
    +B1616184549858N00617852EA0000002371
    +B1616204549859N00617872EA0000002366
    +B1616224549860N00617892EA0000002360
    +B1616244549862N00617911EA0000002353
    +B1616264549865N00617933EA0000002346
    +B1616284549868N00617955EA0000002341
    +B1616314549871N00617972EA0000002338
    +B1616334549874N00617988EA0000002336
    +B1616354549877N00618006EA0000002332
    +B1616374549881N00618024EA0000002329
    +B1616394549884N00618043EA0000002327
    +B1616414549887N00618061EA0000002324
    +B1616434549892N00618077EA0000002321
    +B1616454549895N00618095EA0000002318
    +B1616474549898N00618110EA0000002316
    +B1616494549901N00618125EA0000002312
    +B1616514549902N00618142EA0000002308
    +B1616534549904N00618160EA0000002304
    +B1616554549907N00618177EA0000002300
    +B1616574549909N00618195EA0000002297
    +B1616594549911N00618213EA0000002294
    +B1617014549914N00618231EA0000002291
    +B1617034549916N00618249EA0000002288
    +B1617054549919N00618266EA0000002285
    +B1617074549921N00618283EA0000002281
    +B1617094549922N00618303EA0000002278
    +B1617114549924N00618321EA0000002275
    +B1617134549926N00618339EA0000002272
    +B1617154549929N00618358EA0000002270
    +B1617174549932N00618377EA0000002267
    +B1617194549936N00618396EA0000002265
    +B1617214549938N00618415EA0000002262
    +B1617234549940N00618434EA0000002259
    +B1617254549943N00618453EA0000002256
    +B1617274549946N00618471EA0000002253
    +B1617294549948N00618488EA0000002249
    +B1617314549951N00618507EA0000002244
    +B1617334549953N00618529EA0000002240
    +B1617354549954N00618548EA0000002238
    +B1617374549954N00618563EA0000002237
    +B1617394549956N00618577EA0000002235
    +B1617414549959N00618590EA0000002231
    +B1617434549962N00618606EA0000002225
    +B1617454549967N00618630EA0000002219
    +B1617474549969N00618646EA0000002216
    +B1617494549971N00618663EA0000002211
    +B1617514549972N00618681EA0000002207
    +B1617534549974N00618699EA0000002204
    +B1617554549977N00618715EA0000002201
    +B1617574549979N00618732EA0000002197
    +B1617594549982N00618750EA0000002195
    +B1618014549986N00618767EA0000002194
    +B1618034549989N00618783EA0000002193
    +B1618054549992N00618798EA0000002191
    +B1618074549995N00618815EA0000002189
    +B1618094549997N00618832EA0000002187
    +B1618124549998N00618847EA0000002184
    +B1618144549999N00618863EA0000002180
    +B1618164550001N00618881EA0000002175
    +B1618184550002N00618902EA0000002171
    +B1618204550002N00618923EA0000002167
    +B1618224550003N00618943EA0000002163
    +B1618244550004N00618965EA0000002159
    +B1618264550005N00618988EA0000002157
    +B1618284550005N00619009EA0000002154
    +B1618304550005N00619031EA0000002151
    +B1618324550006N00619053EA0000002148
    +B1618344550006N00619074EA0000002146
    +B1618364550006N00619096EA0000002143
    +B1618384550006N00619118EA0000002140
    +B1618404550008N00619139EA0000002137
    +B1618424550009N00619161EA0000002134
    +B1618444550010N00619183EA0000002131
    +B1618464550012N00619204EA0000002128
    +B1618484550014N00619224EA0000002126
    +B1618504550017N00619244EA0000002122
    +B1618524550022N00619265EA0000002119
    +B1618544550027N00619285EA0000002117
    +B1618564550030N00619306EA0000002115
    +B1618584550031N00619325EA0000002114
    +B1619004550035N00619343EA0000002115
    +B1619024550036N00619358EA0000002118
    +B1619044550038N00619371EA0000002121
    +B1619064550040N00619382EA0000002122
    +B1619084550042N00619394EA0000002124
    +B1619104550044N00619406EA0000002124
    +B1619124550044N00619419EA0000002125
    +B1619144550045N00619434EA0000002126
    +B1619164550045N00619449EA0000002127
    +B1619184550046N00619463EA0000002127
    +B1619204550046N00619477EA0000002127
    +B1619224550046N00619492EA0000002128
    +B1619244550047N00619505EA0000002128
    +B1619264550051N00619526EA0000002127
    +B1619284550053N00619542EA0000002128
    +B1619304550054N00619556EA0000002129
    +B1619324550057N00619570EA0000002129
    +B1619344550059N00619585EA0000002128
    +B1619364550061N00619600EA0000002127
    +B1619384550062N00619614EA0000002126
    +B1619404550064N00619629EA0000002124
    +B1619424550067N00619644EA0000002122
    +B1619444550069N00619659EA0000002121
    +B1619464550072N00619673EA0000002119
    +B1619484550074N00619687EA0000002117
    +B1619504550076N00619702EA0000002114
    +B1619534550078N00619719EA0000002110
    +B1619554550082N00619737EA0000002107
    +B1619574550085N00619756EA0000002105
    +B1619594550088N00619773EA0000002101
    +B1620014550090N00619791EA0000002097
    +B1620034550093N00619811EA0000002093
    +B1620054550096N00619829EA0000002091
    +B1620074550098N00619846EA0000002087
    +B1620094550101N00619864EA0000002083
    +B1620114550105N00619882EA0000002079
    +B1620134550109N00619901EA0000002076
    +B1620154550111N00619921EA0000002074
    +B1620174550114N00619940EA0000002073
    +B1620194550117N00619959EA0000002072
    +B1620214550120N00619978EA0000002070
    +B1620234550123N00619998EA0000002069
    +B1620254550126N00620017EA0000002068
    +B1620274550128N00620036EA0000002068
    +B1620294550130N00620055EA0000002067
    +B1620314550131N00620073EA0000002065
    +B1620334550133N00620094EA0000002062
    +B1620354550134N00620115EA0000002060
    +B1620374550134N00620135EA0000002059
    +B1620394550134N00620154EA0000002056
    +B1620414550136N00620172EA0000002053
    +B1620434550138N00620193EA0000002048
    +B1620454550139N00620214EA0000002045
    +B1620474550140N00620233EA0000002042
    +B1620494550141N00620253EA0000002037
    +B1620514550142N00620273EA0000002033
    +B1620534550143N00620293EA0000002030
    +B1620554550145N00620311EA0000002027
    +B1620574550145N00620331EA0000002023
    +B1620594550145N00620352EA0000002020
    +B1621014550146N00620372EA0000002018
    +B1621034550146N00620392EA0000002016
    +B1621054550146N00620412EA0000002013
    +B1621074550149N00620442EA0000002009
    +B1621094550149N00620462EA0000002006
    +B1621114550148N00620481EA0000002004
    +B1621134550149N00620501EA0000002000
    +B1621154550149N00620521EA0000001997
    +B1621174550149N00620540EA0000001993
    +B1621194550150N00620559EA0000001989
    +B1621214550150N00620579EA0000001985
    +B1621234550150N00620598EA0000001982
    +B1621254550150N00620618EA0000001979
    +B1621274550150N00620637EA0000001976
    +B1621294550150N00620657EA0000001972
    +B1621324550151N00620676EA0000001968
    +B1621344550151N00620696EA0000001965
    +B1621364550152N00620714EA0000001962
    +B1621384550153N00620732EA0000001958
    +B1621404550153N00620752EA0000001954
    +B1621424550153N00620771EA0000001950
    +B1621444550154N00620789EA0000001946
    +B1621464550154N00620808EA0000001941
    +B1621484550155N00620827EA0000001937
    +B1621504550156N00620846EA0000001933
    +B1621524550156N00620864EA0000001929
    +B1621544550156N00620874EA0000001925
    +B1621564550156N00620874EA0000001921
    +B1621584550156N00620925EA0000001918
    +B1622004550155N00620946EA0000001915
    +B1622024550156N00620967EA0000001911
    +B1622044550156N00620988EA0000001908
    +B1622064550155N00621008EA0000001905
    +B1622084550155N00621030EA0000001901
    +B1622104550155N00621052EA0000001898
    +B1622124550155N00621074EA0000001894
    +B1622144550156N00621095EA0000001889
    +B1622164550157N00621117EA0000001884
    +B1622184550157N00621140EA0000001879
    +B1622204550158N00621163EA0000001875
    +B1622224550159N00621185EA0000001872
    +B1622244550159N00621207EA0000001868
    +B1622264550160N00621229EA0000001863
    +B1622284550160N00621251EA0000001860
    +B1622304550159N00621273EA0000001856
    +B1622324550160N00621293EA0000001852
    +B1622344550160N00621313EA0000001848
    +B1622364550160N00621335EA0000001842
    +B1622384550162N00621359EA0000001837
    +B1622404550162N00621383EA0000001833
    +B1622424550163N00621406EA0000001831
    +B1622444550163N00621427EA0000001830
    +B1622464550162N00621454EA0000001832
    +B1622484550162N00621473EA0000001834
    +B1622504550161N00621489EA0000001837
    +B1622524550160N00621504EA0000001840
    +B1622544550159N00621519EA0000001843
    +B1622564550156N00621535EA0000001845
    +B1622584550149N00621549EA0000001846
    +B1623004550138N00621559EA0000001847
    +B1623024550125N00621561EA0000001849
    +B1623044550116N00621550EA0000001851
    +B1623064550116N00621538EA0000001852
    +B1623084550121N00621536EA0000001855
    +B1623104550127N00621544EA0000001857
    +B1623134550131N00621558EA0000001858
    +B1623154550131N00621575EA0000001860
    +B1623174550128N00621592EA0000001862
    +B1623194550122N00621608EA0000001866
    +B1623214550115N00621623EA0000001869
    +B1623234550109N00621639EA0000001873
    +B1623254550105N00621655EA0000001876
    +B1623274550104N00621670EA0000001878
    +B1623294550105N00621683EA0000001879
    +B1623314550107N00621689EA0000001879
    +B1623334550117N00621700EA0000001880
    +B1623354550123N00621699EA0000001881
    +B1623374550127N00621694EA0000001883
    +B1623394550127N00621686EA0000001886
    +B1623414550124N00621677EA0000001889
    +B1623434550116N00621670EA0000001893
    +B1623454550103N00621669EA0000001896
    +B1623474550091N00621673EA0000001900
    +B1623494550081N00621684EA0000001904
    +B1623514550074N00621699EA0000001906
    +B1623534550070N00621715EA0000001909
    +B1623554550071N00621730EA0000001910
    +B1623574550076N00621741EA0000001910
    +B1623594550082N00621748EA0000001910
    +B1624014550089N00621749EA0000001911
    +B1624034550093N00621745EA0000001913
    +B1624054550095N00621737EA0000001915
    +B1624074550094N00621728EA0000001919
    +B1624094550089N00621721EA0000001922
    +B1624114550081N00621713EA0000001924
    +B1624134550069N00621711EA0000001928
    +B1624154550056N00621715EA0000001931
    +B1624174550046N00621726EA0000001934
    +B1624194550039N00621741EA0000001937
    +B1624214550036N00621758EA0000001939
    +B1624234550039N00621771EA0000001940
    +B1624254550044N00621782EA0000001940
    +B1624274550054N00621787EA0000001940
    +B1624294550060N00621780EA0000001940
    +B1624314550060N00621770EA0000001942
    +B1624334550054N00621760EA0000001945
    +B1624354550042N00621756EA0000001948
    +B1624374550028N00621758EA0000001952
    +B1624394550017N00621764EA0000001955
    +B1624414550007N00621774EA0000001958
    +B1624434549998N00621789EA0000001960
    +B1624454549994N00621805EA0000001962
    +B1624474549993N00621820EA0000001962
    +B1624494549994N00621834EA0000001962
    +B1624514550001N00621845EA0000001963
    +B1624544550010N00621846EA0000001963
    +B1624564550015N00621840EA0000001964
    +B1624584550016N00621830EA0000001965
    +B1625004550012N00621820EA0000001965
    +B1625024550002N00621814EA0000001965
    +B1625044549989N00621814EA0000001965
    +B1625064549978N00621824EA0000001965
    +B1625084549970N00621840EA0000001967
    +B1625104549966N00621849EA0000001970
    +B1625124549959N00621873EA0000001974
    +B1625144549956N00621886EA0000001975
    +B1625164549953N00621900EA0000001975
    +B1625184549955N00621918EA0000001976
    +B1625204549960N00621930EA0000001979
    +B1625224549966N00621935EA0000001981
    +B1625244549973N00621936EA0000001983
    +B1625264549978N00621932EA0000001985
    +B1625284549978N00621922EA0000001987
    +B1625304549974N00621913EA0000001990
    +B1625324549964N00621906EA0000001994
    +B1625344549951N00621904EA0000001998
    +B1625364549939N00621914EA0000002001
    +B1625384549930N00621929EA0000002006
    +B1625404549927N00621946EA0000002009
    +B1625424549928N00621961EA0000002010
    +B1625444549934N00621973EA0000002011
    +B1625464549942N00621976EA0000002012
    +B1625484549947N00621969EA0000002014
    +B1625504549947N00621958EA0000002018
    +B1625524549944N00621947EA0000002024
    +B1625544549937N00621939EA0000002028
    +B1625564549926N00621936EA0000002032
    +B1625584549914N00621941EA0000002036
    +B1626004549906N00621956EA0000002039
    +B1626024549903N00621973EA0000002042
    +B1626044549905N00621986EA0000002045
    +B1626064549915N00621993EA0000002049
    +B1626084549921N00621989EA0000002053
    +B1626104549924N00621980EA0000002058
    +B1626124549921N00621971EA0000002062
    +B1626144549915N00621961EA0000002066
    +B1626164549902N00621957EA0000002069
    +B1626184549889N00621966EA0000002073
    +B1626204549880N00621981EA0000002077
    +B1626224549879N00621997EA0000002080
    +B1626244549881N00622011EA0000002084
    +B1626264549887N00622019EA0000002086
    +B1626284549894N00622021EA0000002090
    +B1626314549900N00622017EA0000002094
    +B1626334549902N00622008EA0000002099
    +B1626354549902N00621999EA0000002102
    +B1626374549897N00621989EA0000002104
    +B1626394549883N00621983EA0000002106
    +B1626414549870N00621989EA0000002109
    +B1626434549859N00622006EA0000002110
    +B1626454549856N00622021EA0000002113
    +B1626474549856N00622035EA0000002115
    +B1626494549859N00622047EA0000002118
    +B1626514549866N00622054EA0000002120
    +B1626534549874N00622057EA0000002123
    +B1626554549881N00622056EA0000002127
    +B1626574549886N00622049EA0000002130
    +B1626594549887N00622038EA0000002134
    +B1627014549883N00622028EA0000002136
    +B1627034549873N00622021EA0000002138
    +B1627054549862N00622017EA0000002139
    +B1627074549847N00622024EA0000002139
    +B1627094549835N00622039EA0000002140
    +B1627114549829N00622057EA0000002142
    +B1627134549828N00622072EA0000002145
    +B1627154549829N00622086EA0000002147
    +B1627174549831N00622099EA0000002148
    +B1627194549834N00622112EA0000002150
    +B1627214549840N00622121EA0000002152
    +B1627234549848N00622122EA0000002156
    +B1627254549854N00622119EA0000002161
    +B1627274549857N00622112EA0000002165
    +B1627294549858N00622101EA0000002169
    +B1627314549852N00622091EA0000002173
    +B1627334549842N00622086EA0000002176
    +B1627354549830N00622084EA0000002179
    +B1627374549817N00622088EA0000002181
    +B1627394549805N00622098EA0000002183
    +B1627414549798N00622114EA0000002184
    +B1627434549797N00622139EA0000002185
    +B1627454549801N00622151EA0000002186
    +B1627474549807N00622159EA0000002189
    +B1627494549814N00622161EA0000002193
    +B1627514549819N00622160EA0000002197
    +B1627534549823N00622160EA0000002199
    +B1627554549829N00622156EA0000002200
    +B1627574549830N00622142EA0000002203
    +B1627594549824N00622130EA0000002206
    +B1628014549815N00622124EA0000002210
    +B1628034549803N00622124EA0000002212
    +B1628054549790N00622129EA0000002215
    +B1628074549781N00622143EA0000002216
    +B1628104549776N00622160EA0000002218
    +B1628124549778N00622175EA0000002220
    +B1628144549782N00622185EA0000002222
    +B1628164549788N00622193EA0000002224
    +B1628184549796N00622197EA0000002228
    +B1628204549803N00622195EA0000002232
    +B1628224549809N00622189EA0000002234
    +B1628244549811N00622184EA0000002237
    +B1628264549812N00622168EA0000002241
    +B1628284549805N00622158EA0000002244
    +B1628304549794N00622152EA0000002246
    +B1628324549781N00622156EA0000002247
    +B1628344549769N00622167EA0000002249
    +B1628364549762N00622182EA0000002251
    +B1628384549760N00622197EA0000002252
    +B1628404549763N00622212EA0000002254
    +B1628424549770N00622221EA0000002257
    +B1628444549777N00622225EA0000002262
    +B1628464549784N00622225EA0000002265
    +B1628484549791N00622223EA0000002269
    +B1628504549797N00622216EA0000002272
    +B1628524549798N00622204EA0000002275
    +B1628544549793N00622192EA0000002278
    +B1628564549783N00622185EA0000002280
    +B1628584549771N00622184EA0000002282
    +B1629004549758N00622190EA0000002283
    +B1629024549749N00622203EA0000002284
    +B1629044549745N00622220EA0000002284
    +B1629064549747N00622235EA0000002285
    +B1629084549753N00622243EA0000002287
    +B1629104549761N00622244EA0000002290
    +B1629124549767N00622243EA0000002292
    +B1629144549774N00622243EA0000002295
    +B1629164549780N00622242EA0000002298
    +B1629184549787N00622240EA0000002301
    +B1629204549793N00622240EA0000002305
    +B1629224549801N00622238EA0000002309
    +B1629244549807N00622232EA0000002311
    +B1629264549808N00622222EA0000002314
    +B1629284549804N00622212EA0000002316
    +B1629304549795N00622204EA0000002317
    +B1629324549784N00622202EA0000002319
    +B1629344549772N00622208EA0000002320
    +B1629364549764N00622221EA0000002321
    +B1629384549761N00622235EA0000002323
    +B1629404549764N00622248EA0000002325
    +B1629424549767N00622258EA0000002329
    +B1629444549772N00622266EA0000002331
    +B1629464549778N00622271EA0000002334
    +B1629494549784N00622272EA0000002337
    +B1629514549790N00622268EA0000002340
    +B1629534549794N00622260EA0000002343
    +B1629554549796N00622251EA0000002345
    +B1629574549794N00622239EA0000002346
    +B1629594549788N00622228EA0000002345
    +B1630014549778N00622220EA0000002345
    +B1630034549766N00622218EA0000002346
    +B1630054549754N00622225EA0000002346
    +B1630074549747N00622238EA0000002346
    +B1630094549744N00622253EA0000002346
    +B1630114549746N00622267EA0000002347
    +B1630134549752N00622276EA0000002350
    +B1630154549759N00622281EA0000002355
    +B1630174549766N00622281EA0000002360
    +B1630194549773N00622279EA0000002363
    +B1630214549778N00622274EA0000002365
    +B1630234549781N00622266EA0000002365
    +B1630254549780N00622254EA0000002364
    +B1630274549773N00622243EA0000002364
    +B1630294549762N00622238EA0000002364
    +B1630314549750N00622241EA0000002364
    +B1630334549740N00622253EA0000002363
    +B1630354549735N00622270EA0000002363
    +B1630374549735N00622284EA0000002364
    +B1630394549739N00622296EA0000002367
    +B1630414549744N00622303EA0000002369
    +B1630434549752N00622306EA0000002371
    +B1630454549760N00622308EA0000002373
    +B1630474549766N00622311EA0000002374
    +B1630494549773N00622313EA0000002373
    +B1630514549780N00622317EA0000002372
    +B1630534549786N00622328EA0000002370
    +B1630554549789N00622343EA0000002368
    +B1630574549791N00622359EA0000002367
    +B1630594549794N00622374EA0000002365
    +B1631014549796N00622391EA0000002363
    +B1631034549799N00622419EA0000002361
    +B1631054549800N00622436EA0000002360
    +B1631074549801N00622452EA0000002358
    +B1631094549802N00622469EA0000002358
    +B1631114549804N00622484EA0000002358
    +B1631134549805N00622498EA0000002357
    +B1631154549807N00622512EA0000002356
    +B1631174549809N00622528EA0000002353
    +B1631194549810N00622544EA0000002349
    +B1631214549813N00622559EA0000002345
    +B1631234549819N00622573EA0000002340
    +B1631254549825N00622587EA0000002336
    +B1631274549829N00622602EA0000002332
    +B1631304549833N00622616EA0000002327
    +B1631324549838N00622632EA0000002322
    +B1631344549841N00622649EA0000002318
    +B1631364549843N00622665EA0000002315
    +B1631384549847N00622682EA0000002312
    +B1631404549850N00622699EA0000002309
    +B1631424549854N00622715EA0000002306
    +B1631444549858N00622733EA0000002301
    +B1631464549863N00622753EA0000002298
    +B1631484549866N00622773EA0000002295
    +B1631504549870N00622793EA0000002292
    +B1631524549873N00622814EA0000002288
    +B1631544549877N00622834EA0000002285
    +B1631564549881N00622854EA0000002282
    +B1631584549884N00622874EA0000002278
    +B1632004549887N00622894EA0000002275
    +B1632024549891N00622914EA0000002272
    +B1632044549894N00622934EA0000002269
    +B1632064549897N00622955EA0000002266
    +B1632084549900N00622975EA0000002263
    +B1632104549904N00622994EA0000002260
    +B1632124549907N00623014EA0000002256
    +B1632144549910N00623034EA0000002252
    +B1632164549914N00623054EA0000002248
    +B1632184549917N00623074EA0000002245
    +B1632204549921N00623094EA0000002242
    +B1632224549925N00623113EA0000002238
    +B1632244549929N00623134EA0000002235
    +B1632264549933N00623153EA0000002233
    +B1632284549936N00623172EA0000002229
    +B1632304549939N00623192EA0000002225
    +B1632324549941N00623213EA0000002221
    +B1632344549943N00623233EA0000002217
    +B1632364549944N00623253EA0000002213
    +B1632384549946N00623274EA0000002208
    +B1632404549948N00623294EA0000002204
    +B1632424549950N00623315EA0000002200
    +B1632444549952N00623344EA0000002194
    +B1632464549954N00623365EA0000002190
    +B1632484549954N00623386EA0000002187
    +B1632504549955N00623408EA0000002184
    +B1632524549956N00623429EA0000002181
    +B1632544549958N00623449EA0000002179
    +B1632564549959N00623469EA0000002175
    +B1632584549961N00623489EA0000002172
    +B1633004549964N00623509EA0000002169
    +B1633024549965N00623530EA0000002166
    +B1633044549966N00623550EA0000002162
    +B1633064549968N00623571EA0000002159
    +B1633084549969N00623590EA0000002156
    +B1633114549969N00623610EA0000002152
    +B1633134549970N00623629EA0000002148
    +B1633154549970N00623648EA0000002144
    +B1633174549970N00623668EA0000002140
    +B1633194549971N00623688EA0000002136
    +B1633214549971N00623708EA0000002132
    +B1633234549971N00623728EA0000002130
    +B1633254549972N00623748EA0000002127
    +B1633274549972N00623767EA0000002124
    +B1633294549973N00623787EA0000002121
    +B1633314549973N00623807EA0000002119
    +B1633334549973N00623807EA0000002117
    +B1633354549972N00623848EA0000002114
    +B1633374549972N00623868EA0000002111
    +B1633394549973N00623888EA0000002108
    +B1633414549974N00623909EA0000002105
    +B1633434549975N00623930EA0000002102
    +B1633454549977N00623950EA0000002100
    +B1633474549978N00623971EA0000002097
    +B1633494549979N00623992EA0000002095
    +B1633514549980N00624013EA0000002092
    +B1633534549980N00624034EA0000002090
    +B1633554549980N00624055EA0000002087
    +B1633574549981N00624076EA0000002085
    +B1633594549981N00624097EA0000002082
    +B1634014549981N00624118EA0000002080
    +B1634034549982N00624139EA0000002077
    +B1634054549982N00624161EA0000002075
    +B1634074549982N00624182EA0000002072
    +B1634094549982N00624204EA0000002069
    +B1634114549981N00624225EA0000002067
    +B1634134549982N00624246EA0000002064
    +B1634154549983N00624267EA0000002062
    +B1634174549983N00624289EA0000002059
    +B1634194549983N00624310EA0000002057
    +B1634214549983N00624331EA0000002054
    +B1634234549983N00624353EA0000002052
    +B1634254549983N00624384EA0000002048
    +B1634274549984N00624406EA0000002045
    +B1634294549983N00624427EA0000002042
    +B1634314549983N00624448EA0000002040
    +B1634334549982N00624469EA0000002038
    +B1634354549982N00624490EA0000002036
    +B1634374549981N00624511EA0000002033
    +B1634394549981N00624532EA0000002030
    +B1634414549980N00624554EA0000002028
    +B1634434549980N00624574EA0000002026
    +B1634454549979N00624594EA0000002024
    +B1634474549978N00624615EA0000002021
    +B1634494549978N00624636EA0000002019
    +B1634524549978N00624655EA0000002017
    +B1634544549977N00624675EA0000002014
    +B1634564549976N00624695EA0000002011
    +B1634584549975N00624716EA0000002008
    +B1635004549973N00624737EA0000002005
    +B1635024549972N00624757EA0000002002
    +B1635044549971N00624779EA0000001999
    +B1635064549970N00624800EA0000001997
    +B1635084549969N00624820EA0000001994
    +B1635104549968N00624840EA0000001992
    +B1635124549968N00624861EA0000001989
    +B1635144549968N00624861EA0000001987
    +B1635164549966N00624897EA0000001986
    +B1635184549965N00624914EA0000001984
    +B1635204549964N00624932EA0000001982
    +B1635224549962N00624949EA0000001980
    +B1635244549961N00624966EA0000001978
    +B1635264549960N00624983EA0000001977
    +B1635284549959N00625001EA0000001975
    +B1635304549959N00625017EA0000001974
    +B1635324549959N00625034EA0000001972
    +B1635344549957N00625052EA0000001970
    +B1635364549955N00625069EA0000001968
    +B1635384549953N00625086EA0000001966
    +B1635404549949N00625104EA0000001964
    +B1635424549947N00625121EA0000001962
    +B1635444549944N00625138EA0000001960
    +B1635464549940N00625156EA0000001958
    +B1635484549937N00625173EA0000001956
    +B1635504549934N00625190EA0000001955
    +B1635524549931N00625208EA0000001954
    +B1635544549928N00625225EA0000001953
    +B1635564549925N00625242EA0000001952
    +B1635584549922N00625260EA0000001951
    +B1636004549919N00625277EA0000001950
    +B1636024549915N00625295EA0000001949
    +B1636044549911N00625321EA0000001947
    +B1636064549909N00625338EA0000001946
    +B1636084549907N00625355EA0000001944
    +B1636104549906N00625372EA0000001942
    +B1636124549904N00625390EA0000001940
    +B1636144549902N00625407EA0000001938
    +B1636164549900N00625425EA0000001936
    +B1636184549897N00625443EA0000001934
    +B1636204549894N00625461EA0000001932
    +B1636224549892N00625478EA0000001931
    +B1636244549889N00625495EA0000001930
    +B1636264549887N00625513EA0000001929
    +B1636284549884N00625530EA0000001929
    +B1636314549881N00625548EA0000001928
    +B1636334549875N00625573EA0000001928
    +B1636354549871N00625590EA0000001928
    +B1636374549868N00625606EA0000001928
    +B1636394549866N00625623EA0000001928
    +B1636414549864N00625639EA0000001929
    +B1636434549862N00625656EA0000001929
    +B1636454549862N00625672EA0000001930
    +B1636474549862N00625688EA0000001931
    +B1636494549862N00625704EA0000001933
    +B1636524549862N00625718EA0000001935
    +B1636544549861N00625732EA0000001937
    +B1636564549860N00625745EA0000001939
    +B1636584549859N00625757EA0000001940
    +B1637004549862N00625769EA0000001941
    +B1637024549865N00625774EA0000001941
    +B1637044549879N00625771EA0000001941
    +B1637064549883N00625761EA0000001941
    +B1637084549868N00625742EA0000001942
    +B1637104549858N00625737EA0000001944
    +B1637124549850N00625731EA0000001945
    +B1637144549841N00625724EA0000001946
    +B1637164549831N00625717EA0000001947
    +B1637184549821N00625709EA0000001950
    +B1637204549814N00625697EA0000001952
    +B1637224549812N00625686EA0000001952
    +B1637244549820N00625682EA0000001951
    +B1637264549829N00625687EA0000001950
    +B1637284549835N00625699EA0000001952
    +B1637304549839N00625708EA0000001954
    +B1637324549843N00625718EA0000001957
    +B1637344549848N00625729EA0000001959
    +B1637364549852N00625740EA0000001962
    +B1637384549857N00625746EA0000001964
    +B1637404549864N00625744EA0000001964
    +B1637424549868N00625733EA0000001964
    +B1637444549863N00625718EA0000001963
    +B1637464549848N00625716EA0000001964
    +B1637484549835N00625723EA0000001968
    +B1637504549826N00625726EA0000001969
    +B1637524549814N00625726EA0000001969
    +B1637544549802N00625717EA0000001969
    +B1637564549800N00625700EA0000001968
    +B1637584549807N00625695EA0000001967
    +B1638004549814N00625699EA0000001967
    +B1638024549820N00625706EA0000001970
    +B1638044549829N00625714EA0000001972
    +B1638064549833N00625724EA0000001973
    +B1638084549838N00625734EA0000001975
    +B1638104549842N00625743EA0000001977
    +B1638124549844N00625755EA0000001978
    +B1638144549845N00625768EA0000001980
    +B1638164549843N00625782EA0000001982
    +B1638184549838N00625795EA0000001983
    +B1638204549833N00625808EA0000001986
    +B1638224549831N00625821EA0000001987
    +B1638244549832N00625835EA0000001989
    +B1638264549836N00625846EA0000001989
    +B1638284549844N00625848EA0000001990
    +B1638314549850N00625842EA0000001989
    +B1638334549850N00625829EA0000001989
    +B1638354549846N00625817EA0000001989
    +B1638374549843N00625806EA0000001991
    +B1638394549843N00625800EA0000001993
    +B1638414549839N00625782EA0000001996
    +B1638434549834N00625772EA0000001999
    +B1638454549828N00625762EA0000002002
    +B1638474549823N00625749EA0000002004
    +B1638494549823N00625736EA0000002005
    +B1638514549828N00625731EA0000002005
    +B1638534549835N00625740EA0000002004
    +B1638554549837N00625755EA0000002003
    +B1638574549835N00625769EA0000002003
    +B1638594549831N00625784EA0000002003
    +B1639014549825N00625799EA0000002003
    +B1639034549817N00625812EA0000002005
    +B1639054549810N00625823EA0000002007
    +B1639074549806N00625836EA0000002009
    +B1639094549806N00625848EA0000002011
    +B1639114549811N00625858EA0000002013
    +B1639134549818N00625859EA0000002014
    +B1639154549824N00625852EA0000002015
    +B1639174549822N00625840EA0000002016
    +B1639194549818N00625829EA0000002019
    +B1639214549816N00625819EA0000002020
    +B1639234549816N00625807EA0000002021
    +B1639254549817N00625797EA0000002023
    +B1639274549817N00625787EA0000002023
    +B1639294549817N00625776EA0000002023
    +B1639314549822N00625768EA0000002022
    +B1639334549829N00625770EA0000002021
    +B1639354549831N00625784EA0000002020
    +B1639374549826N00625799EA0000002019
    +B1639394549817N00625811EA0000002020
    +B1639414549810N00625824EA0000002020
    +B1639434549802N00625838EA0000002020
    +B1639454549792N00625860EA0000002022
    +B1639474549787N00625876EA0000002023
    +B1639494549783N00625890EA0000002025
    +B1639514549779N00625904EA0000002026
    +B1639534549777N00625918EA0000002027
    +B1639554549775N00625933EA0000002029
    +B1639574549774N00625946EA0000002032
    +B1639594549774N00625958EA0000002035
    +B1640014549776N00625970EA0000002037
    +B1640034549781N00625978EA0000002039
    +B1640054549788N00625981EA0000002040
    +B1640074549793N00625975EA0000002040
    +B1640094549790N00625963EA0000002039
    +B1640124549779N00625957EA0000002038
    +B1640144549766N00625960EA0000002036
    +B1640164549758N00625973EA0000002035
    +B1640184549759N00625987EA0000002036
    +B1640204549762N00626001EA0000002036
    +B1640224549766N00626016EA0000002035
    +B1640244549771N00626031EA0000002035
    +B1640264549775N00626047EA0000002036
    +B1640284549778N00626063EA0000002038
    +B1640304549781N00626080EA0000002039
    +B1640324549781N00626080EA0000002041
    +B1640344549787N00626113EA0000002043
    +B1640364549791N00626128EA0000002046
    +B1640384549794N00626143EA0000002048
    +B1640404549799N00626157EA0000002050
    +B1640424549804N00626171EA0000002053
    +B1640444549809N00626185EA0000002056
    +B1640464549813N00626199EA0000002059
    +B1640484549819N00626212EA0000002062
    +B1640504549825N00626224EA0000002066
    +B1640524549832N00626236EA0000002069
    +B1640544549839N00626247EA0000002073
    +B1640564549845N00626259EA0000002077
    +B1640584549852N00626271EA0000002080
    +B1641004549859N00626281EA0000002084
    +B1641024549866N00626290EA0000002088
    +B1641044549873N00626300EA0000002092
    +B1641064549879N00626310EA0000002096
    +B1641084549887N00626316EA0000002100
    +B1641104549893N00626316EA0000002103
    +B1641124549897N00626309EA0000002106
    +B1641144549895N00626299EA0000002108
    +B1641164549883N00626297EA0000002112
    +B1641184549870N00626300EA0000002115
    +B1641204549859N00626299EA0000002118
    +B1641224549848N00626293EA0000002121
    +B1641244549833N00626286EA0000002128
    +B1641264549824N00626281EA0000002132
    +B1641284549817N00626274EA0000002135
    +B1641304549816N00626265EA0000002137
    +B1641324549822N00626263EA0000002139
    +B1641344549829N00626270EA0000002140
    +B1641364549833N00626284EA0000002143
    +B1641384549839N00626294EA0000002146
    +B1641404549844N00626308EA0000002148
    +B1641424549848N00626322EA0000002151
    +B1641444549855N00626333EA0000002154
    +B1641474549862N00626346EA0000002158
    +B1641494549869N00626359EA0000002163
    +B1641514549876N00626369EA0000002167
    +B1641534549882N00626380EA0000002172
    +B1641554549885N00626387EA0000002178
    +B1641574549896N00626401EA0000002184
    +B1641594549902N00626403EA0000002191
    +B1642014549906N00626399EA0000002198
    +B1642034549909N00626395EA0000002205
    +B1642054549910N00626386EA0000002210
    +B1642074549905N00626375EA0000002213
    +B1642094549891N00626372EA0000002216
    +B1642114549876N00626376EA0000002219
    +B1642134549863N00626376EA0000002221
    +B1642154549851N00626370EA0000002221
    +B1642174549844N00626358EA0000002214
    +B1642194549850N00626353EA0000002213
    +B1642214549858N00626359EA0000002213
    +B1642234549864N00626371EA0000002214
    +B1642254549869N00626381EA0000002216
    +B1642274549875N00626392EA0000002217
    +B1642294549881N00626405EA0000002219
    +B1642314549886N00626417EA0000002221
    +B1642334549891N00626426EA0000002223
    +B1642354549896N00626438EA0000002224
    +B1642374549900N00626453EA0000002225
    +B1642394549901N00626470EA0000002226
    +B1642414549903N00626487EA0000002227
    +B1642434549905N00626503EA0000002229
    +B1642454549908N00626516EA0000002232
    +B1642474549911N00626531EA0000002234
    +B1642494549913N00626547EA0000002236
    +B1642514549916N00626564EA0000002237
    +B1642534549920N00626580EA0000002239
    +B1642554549923N00626595EA0000002241
    +B1642574549927N00626608EA0000002243
    +B1642594549935N00626626EA0000002245
    +B1643014549940N00626638EA0000002247
    +B1643034549947N00626650EA0000002250
    +B1643054549955N00626661EA0000002253
    +B1643074549963N00626671EA0000002257
    +B1643094549970N00626680EA0000002260
    +B1643114549977N00626688EA0000002263
    +B1643134549983N00626693EA0000002266
    +B1643154549990N00626700EA0000002269
    +B1643174549996N00626708EA0000002271
    +B1643194550003N00626713EA0000002272
    +B1643214550008N00626710EA0000002273
    +B1643234550009N00626701EA0000002274
    +B1643254549998N00626696EA0000002274
    +B1643284549983N00626699EA0000002274
    +B1643304549970N00626701EA0000002276
    +B1643324549959N00626698EA0000002277
    +B1643344549949N00626692EA0000002278
    +B1643364549940N00626684EA0000002278
    +B1643384549932N00626676EA0000002280
    +B1643404549925N00626668EA0000002281
    +B1643424549918N00626658EA0000002283
    +B1643444549912N00626649EA0000002285
    +B1643464549907N00626641EA0000002287
    +B1643484549904N00626637EA0000002288
    +B1643504549894N00626624EA0000002289
    +B1643524549889N00626616EA0000002291
    +B1643544549884N00626608EA0000002293
    +B1643564549882N00626600EA0000002294
    +B1643584549884N00626594EA0000002294
    +B1644004549891N00626594EA0000002296
    +B1644024549897N00626610EA0000002297
    +B1644044549896N00626631EA0000002299
    +B1644064549897N00626647EA0000002300
    +B1644084549904N00626657EA0000002301
    +B1644104549912N00626666EA0000002303
    +B1644124549918N00626674EA0000002304
    +B1644144549926N00626681EA0000002304
    +B1644164549934N00626685EA0000002306
    +B1644184549941N00626690EA0000002307
    +B1644204549947N00626694EA0000002307
    +B1644224549954N00626696EA0000002307
    +B1644244549958N00626690EA0000002306
    +B1644264549953N00626678EA0000002305
    +B1644284549942N00626668EA0000002306
    +B1644304549932N00626662EA0000002308
    +B1644324549923N00626656EA0000002309
    +B1644344549915N00626651EA0000002309
    +B1644364549907N00626646EA0000002309
    +B1644384549899N00626638EA0000002307
    +B1644404549889N00626623EA0000002305
    +B1644424549884N00626614EA0000002305
    +B1644444549879N00626605EA0000002305
    +B1644464549876N00626595EA0000002307
    +B1644484549873N00626586EA0000002309
    +B1644504549871N00626580EA0000002311
    +B1644524549874N00626574EA0000002310
    +B1644544549882N00626582EA0000002310
    +B1644564549887N00626601EA0000002309
    +B1644584549889N00626620EA0000002311
    +B1645004549893N00626634EA0000002311
    +B1645024549899N00626647EA0000002313
    +B1645044549904N00626658EA0000002314
    +B1645074549910N00626667EA0000002314
    +B1645094549918N00626677EA0000002313
    +B1645114549926N00626687EA0000002312
    +B1645134549934N00626696EA0000002312
    +B1645154549942N00626705EA0000002311
    +B1645174549951N00626714EA0000002309
    +B1645194549960N00626723EA0000002307
    +B1645214549969N00626731EA0000002306
    +B1645234549978N00626739EA0000002305
    +B1645254549987N00626748EA0000002304
    +B1645274549996N00626758EA0000002302
    +B1645294550001N00626763EA0000002301
    +B1645314550014N00626780EA0000002300
    +B1645334550022N00626793EA0000002299
    +B1645354550029N00626807EA0000002297
    +B1645374550034N00626826EA0000002296
    +B1645394550036N00626847EA0000002294
    +B1645414550038N00626868EA0000002291
    +B1645434550041N00626889EA0000002289
    +B1645454550042N00626910EA0000002286
    +B1645474550043N00626929EA0000002283
    +B1645494550043N00626950EA0000002279
    +B1645514550043N00626973EA0000002274
    +B1645534550043N00626995EA0000002269
    +B1645554550043N00627018EA0000002262
    +B1645574550043N00627044EA0000002255
    +B1645594550042N00627071EA0000002247
    +B1646014550040N00627095EA0000002241
    +B1646034550037N00627119EA0000002233
    +B1646054550035N00627145EA0000002224
    +B1646074550032N00627169EA0000002216
    +B1646094550029N00627192EA0000002208
    +B1646114550025N00627216EA0000002194
    +B1646134550023N00627242EA0000002184
    +B1646154550020N00627269EA0000002176
    +B1646174550019N00627293EA0000002169
    +B1646194550021N00627315EA0000002163
    +B1646214550023N00627341EA0000002156
    +B1646234550024N00627359EA0000002149
    +B1646254550025N00627377EA0000002145
    +B1646274550026N00627393EA0000002140
    +B1646294550026N00627410EA0000002133
    +B1646314550026N00627430EA0000002125
    +B1646334550024N00627453EA0000002119
    +B1646354550021N00627474EA0000002114
    +B1646374550021N00627494EA0000002108
    +B1646394550021N00627516EA0000002102
    +B1646414550021N00627538EA0000002097
    +B1646434550023N00627559EA0000002094
    +B1646454550027N00627577EA0000002090
    +B1646484550030N00627597EA0000002085
    +B1646504550034N00627618EA0000002079
    +B1646524550038N00627639EA0000002075
    +B1646544550039N00627659EA0000002073
    +B1646564550040N00627679EA0000002068
    +B1646584550041N00627701EA0000002064
    +B1647004550040N00627723EA0000002062
    +B1647024550038N00627742EA0000002059
    +B1647044550038N00627761EA0000002055
    +B1647064550039N00627782EA0000002051
    +B1647084550042N00627803EA0000002048
    +B1647104550046N00627823EA0000002044
    +B1647124550050N00627844EA0000002039
    +B1647144550053N00627865EA0000002035
    +B1647164550058N00627884EA0000002032
    +B1647184550060N00627904EA0000002027
    +B1647204550063N00627925EA0000002022
    +B1647224550067N00627945EA0000002019
    +B1647244550070N00627964EA0000002016
    +B1647264550073N00627982EA0000002013
    +B1647284550076N00628002EA0000002008
    +B1647304550078N00628023EA0000002006
    +B1647324550080N00628042EA0000002003
    +B1647344550080N00628062EA0000001998
    +B1647364550081N00628084EA0000001994
    +B1647384550083N00628106EA0000001991
    +B1647404550086N00628126EA0000001988
    +B1647424550089N00628146EA0000001984
    +B1647444550093N00628167EA0000001980
    +B1647464550096N00628188EA0000001977
    +B1647484550099N00628209EA0000001975
    +B1647504550102N00628230EA0000001972
    +B1647524550105N00628252EA0000001969
    +B1647544550108N00628273EA0000001967
    +B1647564550111N00628295EA0000001965
    +B1647584550114N00628316EA0000001963
    +B1648004550116N00628339EA0000001960
    +B1648024550121N00628371EA0000001958
    +B1648044550123N00628393EA0000001958
    +B1648064550125N00628412EA0000001958
    +B1648084550128N00628431EA0000001957
    +B1648104550130N00628451EA0000001956
    +B1648124550132N00628471EA0000001957
    +B1648144550134N00628490EA0000001956
    +B1648164550136N00628507EA0000001956
    +B1648184550138N00628524EA0000001954
    +B1648204550142N00628542EA0000001951
    +B1648224550145N00628563EA0000001950
    +B1648244550148N00628581EA0000001949
    +B1648264550151N00628598EA0000001948
    +B1648294550154N00628617EA0000001948
    +B1648314550157N00628636EA0000001948
    +B1648334550161N00628653EA0000001949
    +B1648354550165N00628670EA0000001950
    +B1648374550166N00628686EA0000001950
    +B1648394550169N00628701EA0000001949
    +B1648414550172N00628716EA0000001948
    +B1648434550177N00628730EA0000001946
    +B1648454550181N00628745EA0000001943
    +B1648474550186N00628760EA0000001940
    +B1648494550191N00628776EA0000001937
    +B1648514550197N00628791EA0000001935
    +B1648534550201N00628807EA0000001933
    +B1648554550204N00628824EA0000001931
    +B1648574550207N00628840EA0000001930
    +B1648594550209N00628856EA0000001929
    +B1649014550210N00628872EA0000001929
    +B1649034550210N00628887EA0000001929
    +B1649054550209N00628903EA0000001931
    +B1649074550208N00628917EA0000001932
    +B1649094550206N00628930EA0000001933
    +B1649114550204N00628942EA0000001933
    +B1649134550202N00628955EA0000001933
    +B1649154550199N00628970EA0000001933
    +B1649174550197N00628983EA0000001934
    +B1649194550195N00628996EA0000001933
    +B1649214550191N00629010EA0000001934
    +B1649234550187N00629022EA0000001935
    +B1649254550184N00629034EA0000001935
    +B1649274550181N00629047EA0000001935
    +B1649294550177N00629060EA0000001936
    +B1649314550172N00629072EA0000001934
    +B1649334550168N00629086EA0000001932
    +B1649354550164N00629101EA0000001931
    +B1649374550161N00629112EA0000001929
    +B1649394550159N00629123EA0000001927
    +B1649414550153N00629142EA0000001926
    +B1649434550151N00629150EA0000001927
    +B1649454550148N00629160EA0000001927
    +B1649474550143N00629171EA0000001927
    +B1649494550134N00629173EA0000001929
    +B1649514550128N00629166EA0000001929
    +B1649534550127N00629152EA0000001929
    +B1649554550135N00629142EA0000001928
    +B1649574550149N00629147EA0000001928
    +B1649594550156N00629158EA0000001931
    +B1650014550160N00629170EA0000001933
    +B1650034550163N00629185EA0000001935
    +B1650054550163N00629198EA0000001938
    +B1650074550160N00629208EA0000001939
    +B1650104550157N00629217EA0000001940
    +B1650124550153N00629226EA0000001940
    +B1650144550148N00629236EA0000001939
    +B1650164550141N00629242EA0000001939
    +B1650184550136N00629247EA0000001939
    +B1650204550131N00629253EA0000001939
    +B1650224550125N00629262EA0000001938
    +B1650244550120N00629271EA0000001939
    +B1650264550117N00629278EA0000001938
    +B1650284550114N00629286EA0000001937
    +B1650304550111N00629296EA0000001935
    +B1650324550107N00629307EA0000001935
    +B1650344550104N00629317EA0000001935
    +B1650364550101N00629327EA0000001933
    +B1650384550098N00629337EA0000001933
    +B1650404550093N00629346EA0000001933
    +B1650424550088N00629355EA0000001933
    +B1650444550085N00629366EA0000001934
    +B1650464550081N00629378EA0000001935
    +B1650484550078N00629388EA0000001936
    +B1650504550075N00629398EA0000001938
    +B1650524550072N00629408EA0000001940
    +B1650544550069N00629418EA0000001943
    +B1650564550066N00629426EA0000001946
    +B1650584550061N00629431EA0000001948
    +B1651004550056N00629434EA0000001950
    +B1651024550051N00629428EA0000001951
    +B1651044550050N00629414EA0000001953
    +B1651064550055N00629401EA0000001955
    +B1651084550066N00629395EA0000001956
    +B1651104550079N00629398EA0000001957
    +B1651124550088N00629409EA0000001958
    +B1651144550092N00629424EA0000001960
    +B1651164550090N00629436EA0000001961
    +B1651184550087N00629446EA0000001963
    +B1651204550086N00629456EA0000001963
    +B1651224550081N00629472EA0000001964
    +B1651244550079N00629482EA0000001966
    +B1651264550079N00629493EA0000001966
    +B1651284550076N00629504EA0000001967
    +B1651304550072N00629513EA0000001968
    +B1651324550070N00629523EA0000001968
    +B1651344550067N00629536EA0000001966
    +B1651364550064N00629549EA0000001967
    +B1651384550063N00629563EA0000001966
    +B1651404550063N00629578EA0000001965
    +B1651424550063N00629593EA0000001965
    +B1651444550060N00629605EA0000001964
    +B1651464550057N00629619EA0000001963
    +B1651484550054N00629634EA0000001960
    +B1651514550051N00629650EA0000001957
    +B1651534550049N00629667EA0000001955
    +B1651554550049N00629683EA0000001952
    +B1651574550050N00629699EA0000001948
    +B1651594550051N00629717EA0000001946
    +B1652014550051N00629734EA0000001945
    +B1652034550051N00629749EA0000001943
    +B1652054550051N00629764EA0000001941
    +B1652074550050N00629780EA0000001940
    +B1652094550050N00629787EA0000001938
    +B1652114550049N00629802EA0000001937
    +B1652134550047N00629825EA0000001935
    +B1652154550047N00629841EA0000001934
    +B1652174550046N00629856EA0000001934
    +B1652194550045N00629871EA0000001934
    +B1652214550043N00629886EA0000001932
    +B1652234550040N00629901EA0000001930
    +B1652254550038N00629916EA0000001928
    +B1652274550036N00629930EA0000001926
    +B1652294550033N00629944EA0000001923
    +B1652314550031N00629960EA0000001920
    +B1652334550028N00629975EA0000001917
    +B1652354550024N00629991EA0000001913
    +B1652374550021N00630009EA0000001908
    +B1652394550017N00630028EA0000001904
    +B1652414550013N00630047EA0000001900
    +B1652434550010N00630067EA0000001898
    +B1652454550006N00630085EA0000001895
    +B1652474550002N00630103EA0000001892
    +B1652494550000N00630122EA0000001890
    +B1652514549998N00630141EA0000001888
    +B1652534549996N00630160EA0000001885
    +B1652554549993N00630179EA0000001882
    +B1652574549991N00630199EA0000001880
    +B1652594549989N00630218EA0000001878
    +B1653014549987N00630238EA0000001876
    +B1653034549984N00630268EA0000001873
    +B1653054549983N00630287EA0000001871
    +B1653074549980N00630307EA0000001868
    +B1653094549978N00630327EA0000001867
    +B1653114549975N00630345EA0000001866
    +B1653134549973N00630364EA0000001864
    +B1653154549970N00630385EA0000001862
    +B1653174549967N00630404EA0000001862
    +B1653194549963N00630422EA0000001861
    +B1653214549960N00630441EA0000001859
    +B1653234549957N00630461EA0000001857
    +B1653254549954N00630481EA0000001857
    +B1653274549952N00630501EA0000001857
    +B1653304549949N00630520EA0000001858
    +B1653324549947N00630536EA0000001859
    +B1653344549944N00630551EA0000001859
    +B1653364549942N00630567EA0000001859
    +B1653384549941N00630584EA0000001860
    +B1653404549940N00630599EA0000001860
    +B1653424549939N00630615EA0000001859
    +B1653444549937N00630632EA0000001860
    +B1653464549936N00630639EA0000001859
    +B1653484549934N00630663EA0000001857
    +B1653504549932N00630681EA0000001857
    +B1653524549931N00630698EA0000001859
    +B1653544549930N00630713EA0000001862
    +B1653564549931N00630728EA0000001865
    +B1653584549931N00630743EA0000001867
    +B1654004549930N00630756EA0000001871
    +B1654024549930N00630767EA0000001873
    +B1654044549929N00630779EA0000001875
    +B1654064549928N00630792EA0000001877
    +B1654084549922N00630801EA0000001879
    +B1654104549912N00630803EA0000001880
    +B1654124549903N00630791EA0000001880
    +B1654144549901N00630775EA0000001881
    +B1654164549908N00630765EA0000001883
    +B1654184549918N00630765EA0000001885
    +B1654204549926N00630772EA0000001887
    +B1654224549932N00630781EA0000001887
    +B1654244549936N00630794EA0000001889
    +B1654264549938N00630806EA0000001892
    +B1654284549939N00630818EA0000001893
    +B1654304549940N00630832EA0000001895
    +B1654324549942N00630843EA0000001898
    +B1654344549943N00630855EA0000001900
    +B1654364549944N00630869EA0000001901
    +B1654384549945N00630882EA0000001903
    +B1654404549944N00630894EA0000001906
    +B1654424549940N00630905EA0000001907
    +B1654444549928N00630913EA0000001908
    +B1654464549920N00630906EA0000001908
    +B1654484549917N00630891EA0000001907
    +B1654504549921N00630879EA0000001907
    +B1654524549929N00630878EA0000001907
    +B1654544549937N00630882EA0000001908
    +B1654564549942N00630892EA0000001909
    +B1654584549945N00630904EA0000001909
    +B1655004549947N00630917EA0000001911
    +B1655024549948N00630930EA0000001914
    +B1655044549949N00630942EA0000001918
    +B1655064549951N00630954EA0000001924
    +B1655084549953N00630967EA0000001929
    +B1655114549954N00630981EA0000001934
    +B1655134549953N00630994EA0000001939
    +B1655154549946N00631006EA0000001944
    +B1655174549936N00631011EA0000001949
    +B1655194549928N00631005EA0000001953
    +B1655214549925N00630991EA0000001957
    +B1655234549925N00630985EA0000001961
    +B1655254549935N00630975EA0000001963
    +B1655274549945N00630980EA0000001966
    +B1655294549949N00630992EA0000001971
    +B1655314549953N00631004EA0000001974
    +B1655334549955N00631019EA0000001976
    +B1655354549951N00631034EA0000001979
    +B1655374549941N00631045EA0000001981
    +B1655394549930N00631043EA0000001984
    +B1655414549924N00631032EA0000001987
    +B1655434549925N00631019EA0000001991
    +B1655454549931N00631013EA0000001994
    +B1655474549940N00631017EA0000001998
    +B1655494549945N00631032EA0000002002
    +B1655514549944N00631048EA0000002005
    +B1655534549936N00631061EA0000002007
    +B1655554549923N00631066EA0000002010
    +B1655574549911N00631060EA0000002014
    +B1655594549905N00631046EA0000002018
    +B1656014549906N00631034EA0000002021
    +B1656034549914N00631030EA0000002024
    +B1656054549921N00631036EA0000002026
    +B1656074549925N00631048EA0000002029
    +B1656094549926N00631061EA0000002032
    +B1656114549928N00631074EA0000002035
    +B1656134549928N00631089EA0000002039
    +B1656154549926N00631103EA0000002044
    +B1656174549923N00631113EA0000002048
    +B1656194549920N00631125EA0000002051
    +B1656214549913N00631137EA0000002055
    +B1656234549898N00631136EA0000002063
    +B1656254549892N00631126EA0000002068
    +B1656274549890N00631113EA0000002073
    +B1656294549895N00631103EA0000002076
    +B1656314549903N00631099EA0000002080
    +B1656334549910N00631104EA0000002083
    +B1656354549914N00631114EA0000002087
    +B1656374549919N00631126EA0000002092
    +B1656394549922N00631137EA0000002098
    +B1656414549922N00631151EA0000002102
    +B1656434549916N00631165EA0000002105
    +B1656454549904N00631172EA0000002109
    +B1656474549891N00631162EA0000002114
    +B1656504549887N00631149EA0000002118
    +B1656524549890N00631135EA0000002122
    +B1656544549900N00631128EA0000002127
    +B1656564549910N00631132EA0000002132
    +B1656584549916N00631140EA0000002139
    +B1657004549917N00631144EA0000002144
    +B1657024549922N00631160EA0000002149
    +B1657044549924N00631173EA0000002154
    +B1657064549921N00631188EA0000002158
    +B1657084549912N00631200EA0000002162
    +B1657104549898N00631204EA0000002166
    +B1657124549887N00631196EA0000002172
    +B1657144549884N00631181EA0000002177
    +B1657164549888N00631168EA0000002182
    +B1657184549898N00631164EA0000002188
    +B1657204549906N00631168EA0000002195
    +B1657224549911N00631177EA0000002202
    +B1657244549913N00631188EA0000002207
    +B1657264549912N00631202EA0000002211
    +B1657284549907N00631215EA0000002214
    +B1657304549899N00631228EA0000002217
    +B1657324549888N00631239EA0000002218
    +B1657344549876N00631236EA0000002221
    +B1657364549868N00631222EA0000002225
    +B1657384549869N00631209EA0000002228
    +B1657404549875N00631200EA0000002232
    +B1657424549884N00631201EA0000002236
    +B1657444549892N00631211EA0000002241
    +B1657464549894N00631227EA0000002246
    +B1657484549892N00631243EA0000002248
    +B1657504549886N00631257EA0000002249
    +B1657524549876N00631268EA0000002250
    +B1657544549864N00631275EA0000002253
    +B1657564549852N00631273EA0000002256
    +B1657584549842N00631265EA0000002260
    +B1658004549835N00631247EA0000002267
    +B1658024549834N00631234EA0000002271
    +B1658044549838N00631222EA0000002273
    +B1658064549845N00631212EA0000002274
    +B1658084549856N00631208EA0000002277
    +B1658104549863N00631216EA0000002280
    +B1658124549867N00631225EA0000002285
    +B1658144549868N00631235EA0000002289
    +B1658164549867N00631249EA0000002290
    +B1658184549861N00631265EA0000002293
    +B1658204549846N00631272EA0000002294
    +B1658224549833N00631265EA0000002297
    +B1658244549827N00631251EA0000002299
    +B1658264549826N00631238EA0000002300
    +B1658294549830N00631225EA0000002301
    +B1658314549839N00631218EA0000002302
    +B1658334549848N00631219EA0000002305
    +B1658354549853N00631222EA0000002307
    +B1658374549855N00631223EA0000002309
    +B1658394549862N00631227EA0000002308
    +B1658414549868N00631232EA0000002309
    +B1658434549872N00631242EA0000002310
    +B1658454549876N00631251EA0000002312
    +B1658474549882N00631258EA0000002313
    +B1658494549890N00631265EA0000002316
    +B1658514549897N00631271EA0000002320
    +B1658534549905N00631275EA0000002323
    +B1658554549913N00631281EA0000002325
    +B1658574549921N00631281EA0000002328
    +B1658594549929N00631274EA0000002331
    +B1659014549933N00631265EA0000002334
    +B1659034549935N00631254EA0000002337
    +B1659054549934N00631241EA0000002338
    +B1659074549927N00631230EA0000002337
    +B1659094549912N00631225EA0000002336
    +B1659114549900N00631230EA0000002335
    +B1659134549891N00631242EA0000002335
    +B1659154549885N00631258EA0000002334
    +B1659174549882N00631275EA0000002336
    +B1659194549884N00631290EA0000002337
    +B1659214549891N00631301EA0000002340
    +B1659234549899N00631308EA0000002344
    +B1659254549907N00631315EA0000002346
    +B1659274549915N00631320EA0000002348
    +B1659294549923N00631325EA0000002349
    +B1659314549930N00631327EA0000002349
    +B1659334549939N00631328EA0000002348
    +B1659354549949N00631330EA0000002348
    +B1659374549957N00631331EA0000002348
    +B1659394549970N00631331EA0000002349
    +B1659414549979N00631329EA0000002350
    +B1659434549987N00631327EA0000002351
    +B1659454549994N00631327EA0000002352
    +B1659474550003N00631326EA0000002352
    +B1659494550011N00631326EA0000002352
    +B1659514550017N00631327EA0000002352
    +B1659534550024N00631330EA0000002351
    +B1659554550029N00631337EA0000002349
    +B1659574550032N00631347EA0000002347
    +B1659594550031N00631360EA0000002345
    +B1700014550026N00631372EA0000002343
    +B1700034550022N00631386EA0000002340
    +B1700064550015N00631399EA0000002338
    +B1700084550005N00631409EA0000002335
    +B1700104549995N00631423EA0000002332
    +B1700124549985N00631436EA0000002330
    +B1700144549981N00631441EA0000002330
    +B1700164549968N00631458EA0000002329
    +B1700184549960N00631471EA0000002329
    +B1700204549953N00631483EA0000002329
    +B1700224549947N00631496EA0000002329
    +B1700244549941N00631510EA0000002328
    +B1700264549936N00631526EA0000002328
    +B1700284549931N00631541EA0000002328
    +B1700304549927N00631555EA0000002329
    +B1700324549924N00631571EA0000002327
    +B1700344549920N00631588EA0000002327
    +B1700364549916N00631603EA0000002326
    +B1700384549913N00631618EA0000002325
    +B1700404549910N00631635EA0000002324
    +B1700424549907N00631651EA0000002323
    +B1700444549905N00631667EA0000002322
    +B1700464549902N00631683EA0000002321
    +B1700484549901N00631699EA0000002320
    +B1700504549899N00631716EA0000002320
    +B1700524549896N00631733EA0000002319
    +B1700544549892N00631750EA0000002318
    +B1700564549887N00631767EA0000002317
    +B1700584549883N00631786EA0000002315
    +B1701004549878N00631806EA0000002314
    +B1701024549873N00631826EA0000002313
    +B1701044549869N00631845EA0000002312
    +B1701064549866N00631865EA0000002310
    +B1701084549861N00631885EA0000002310
    +B1701104549858N00631905EA0000002310
    +B1701124549855N00631924EA0000002309
    +B1701144549851N00631944EA0000002307
    +B1701164549848N00631966EA0000002307
    +B1701184549843N00631991EA0000002309
    +B1701204549840N00632006EA0000002308
    +B1701224549837N00632022EA0000002307
    +B1701244549833N00632037EA0000002307
    +B1701264549830N00632054EA0000002305
    +B1701284549827N00632073EA0000002303
    +B1701304549822N00632091EA0000002302
    +B1701324549818N00632109EA0000002299
    +B1701344549812N00632129EA0000002297
    +B1701364549807N00632149EA0000002296
    +B1701384549802N00632170EA0000002296
    +B1701404549798N00632190EA0000002295
    +B1701424549793N00632210EA0000002293
    +B1701454549789N00632231EA0000002292
    +B1701474549784N00632252EA0000002290
    +B1701494549779N00632273EA0000002288
    +B1701514549773N00632294EA0000002286
    +B1701534549767N00632313EA0000002285
    +B1701554549760N00632333EA0000002282
    +B1701574549753N00632353EA0000002279
    +B1701594549747N00632373EA0000002277
    +B1702014549742N00632394EA0000002275
    +B1702034549736N00632414EA0000002273
    +B1702054549729N00632434EA0000002270
    +B1702074549726N00632445EA0000002268
    +B1702094549716N00632476EA0000002267
    +B1702114549708N00632496EA0000002265
    +B1702134549701N00632516EA0000002263
    +B1702154549694N00632537EA0000002260
    +B1702174549687N00632558EA0000002260
    +B1702194549682N00632578EA0000002258
    +B1702214549677N00632598EA0000002255
    +B1702234549671N00632619EA0000002252
    +B1702254549666N00632640EA0000002252
    +B1702274549660N00632659EA0000002250
    +B1702294549652N00632679EA0000002248
    +B1702314549645N00632698EA0000002247
    +B1702334549639N00632717EA0000002245
    +B1702354549633N00632737EA0000002244
    +B1702374549627N00632756EA0000002243
    +B1702394549621N00632774EA0000002242
    +B1702414549615N00632793EA0000002240
    +B1702434549608N00632813EA0000002237
    +B1702454549600N00632833EA0000002235
    +B1702474549592N00632851EA0000002234
    +B1702494549583N00632870EA0000002232
    +B1702514549575N00632889EA0000002230
    +B1702534549567N00632908EA0000002229
    +B1702554549560N00632927EA0000002227
    +B1702574549554N00632947EA0000002225
    +B1702594549543N00632975EA0000002224
    +B1703014549536N00632993EA0000002222
    +B1703034549529N00633012EA0000002220
    +B1703054549522N00633032EA0000002219
    +B1703074549516N00633051EA0000002218
    +B1703094549510N00633070EA0000002214
    +B1703114549504N00633090EA0000002211
    +B1703134549498N00633110EA0000002208
    +B1703154549492N00633128EA0000002205
    +B1703174549486N00633146EA0000002200
    +B1703194549480N00633165EA0000002195
    +B1703214549474N00633185EA0000002192
    +B1703234549468N00633202EA0000002190
    +B1703264549461N00633219EA0000002186
    +B1703284549455N00633237EA0000002182
    +B1703304549449N00633255EA0000002179
    +B1703324549443N00633271EA0000002175
    +B1703344549437N00633289EA0000002171
    +B1703364549431N00633308EA0000002166
    +B1703384549425N00633328EA0000002161
    +B1703404549418N00633347EA0000002158
    +B1703424549411N00633367EA0000002156
    +B1703444549404N00633385EA0000002155
    +B1703464549396N00633403EA0000002154
    +B1703484549388N00633422EA0000002154
    +B1703504549381N00633441EA0000002154
    +B1703524549373N00633458EA0000002155
    +B1703544549365N00633475EA0000002156
    +B1703564549359N00633491EA0000002158
    +B1703584549353N00633503EA0000002160
    +B1704004549346N00633515EA0000002160
    +B1704024549339N00633528EA0000002162
    +B1704044549332N00633541EA0000002165
    +B1704064549325N00633553EA0000002169
    +B1704084549319N00633563EA0000002175
    +B1704104549314N00633570EA0000002178
    +B1704124549304N00633577EA0000002181
    +B1704144549293N00633575EA0000002185
    +B1704164549287N00633562EA0000002187
    +B1704184549285N00633547EA0000002190
    +B1704204549292N00633539EA0000002193
    +B1704224549303N00633544EA0000002197
    +B1704244549307N00633558EA0000002200
    +B1704264549305N00633574EA0000002204
    +B1704284549301N00633587EA0000002208
    +B1704304549294N00633600EA0000002212
    +B1704324549288N00633613EA0000002218
    +B1704344549284N00633625EA0000002225
    +B1704364549278N00633635EA0000002232
    +B1704384549269N00633651EA0000002241
    +B1704404549260N00633659EA0000002245
    +B1704424549250N00633660EA0000002250
    +B1704444549241N00633649EA0000002254
    +B1704464549238N00633634EA0000002259
    +B1704484549243N00633622EA0000002264
    +B1704504549255N00633625EA0000002268
    +B1704524549262N00633639EA0000002273
    +B1704544549260N00633653EA0000002279
    +B1704564549254N00633665EA0000002285
    +B1704584549248N00633676EA0000002290
    +B1705004549241N00633688EA0000002295
    +B1705024549232N00633697EA0000002301
    +B1705044549221N00633701EA0000002307
    +B1705074549211N00633697EA0000002312
    +B1705094549205N00633687EA0000002317
    +B1705114549204N00633673EA0000002321
    +B1705134549208N00633662EA0000002325
    +B1705154549216N00633659EA0000002329
    +B1705174549222N00633667EA0000002333
    +B1705194549224N00633680EA0000002338
    +B1705214549222N00633694EA0000002344
    +B1705234549216N00633706EA0000002349
    +B1705254549207N00633713EA0000002354
    +B1705274549198N00633714EA0000002359
    +B1705294549189N00633707EA0000002363
    +B1705314549184N00633695EA0000002366
    +B1705334549185N00633681EA0000002369
    +B1705354549191N00633672EA0000002372
    +B1705374549199N00633672EA0000002375
    +B1705394549206N00633681EA0000002378
    +B1705414549208N00633695EA0000002382
    +B1705434549204N00633708EA0000002386
    +B1705454549199N00633719EA0000002390
    +B1705474549192N00633728EA0000002394
    +B1705494549182N00633731EA0000002397
    +B1705514549172N00633726EA0000002400
    +B1705534549165N00633716EA0000002403
    +B1705554549162N00633702EA0000002404
    +B1705574549165N00633689EA0000002406
    +B1705594549170N00633678EA0000002407
    +B1706014549171N00633668EA0000002410
    +B1706034549169N00633655EA0000002412
    +B1706054549164N00633644EA0000002414
    +B1706074549153N00633638EA0000002416
    +B1706094549142N00633638EA0000002417
    +B1706114549132N00633641EA0000002419
    +B1706134549122N00633646EA0000002421
    +B1706154549112N00633650EA0000002422
    +B1706174549103N00633656EA0000002423
    +B1706194549091N00633670EA0000002421
    +B1706214549083N00633680EA0000002420
    +B1706234549075N00633689EA0000002419
    +B1706254549067N00633700EA0000002417
    +B1706274549059N00633712EA0000002415
    +B1706294549051N00633726EA0000002413
    +B1706314549042N00633740EA0000002411
    +B1706334549035N00633754EA0000002411
    +B1706354549027N00633768EA0000002410
    +B1706374549020N00633782EA0000002410
    +B1706394549013N00633797EA0000002410
    +B1706414549007N00633814EA0000002410
    +B1706434549000N00633832EA0000002409
    +B1706454548993N00633849EA0000002409
    +B1706484548986N00633863EA0000002411
    +B1706504548980N00633878EA0000002413
    +B1706524548972N00633891EA0000002416
    +B1706544548962N00633898EA0000002419
    +B1706564548950N00633898EA0000002421
    +B1706584548940N00633886EA0000002423
    +B1707004548940N00633870EA0000002423
    +B1707024548950N00633866EA0000002423
    +B1707044548957N00633877EA0000002425
    +B1707064548957N00633894EA0000002426
    +B1707084548951N00633910EA0000002429
    +B1707104548945N00633926EA0000002430
    +B1707124548939N00633944EA0000002432
    +B1707144548932N00633961EA0000002434
    +B1707164548926N00633977EA0000002437
    +B1707184548919N00633993EA0000002440
    +B1707204548912N00634009EA0000002443
    +B1707224548906N00634026EA0000002447
    +B1707244548901N00634042EA0000002453
    +B1707264548896N00634057EA0000002457
    +B1707284548889N00634072EA0000002461
    +B1707304548880N00634081EA0000002465
    +B1707324548869N00634084EA0000002468
    +B1707344548859N00634078EA0000002470
    +B1707364548854N00634064EA0000002472
    +B1707384548858N00634048EA0000002475
    +B1707404548869N00634044EA0000002477
    +B1707424548878N00634054EA0000002480
    +B1707444548879N00634072EA0000002483
    +B1707464548874N00634089EA0000002486
    +B1707484548868N00634104EA0000002489
    +B1707504548862N00634120EA0000002492
    +B1707524548856N00634135EA0000002496
    +B1707544548850N00634149EA0000002499
    +B1707564548844N00634163EA0000002502
    +B1707584548829N00634176EA0000002506
    +B1708004548816N00634171EA0000002508
    +B1708024548809N00634158EA0000002511
    +B1708044548811N00634144EA0000002512
    +B1708064548821N00634138EA0000002514
    +B1708084548830N00634146EA0000002515
    +B1708104548833N00634161EA0000002517
    +B1708124548829N00634177EA0000002520
    +B1708144548822N00634188EA0000002524
    +B1708164548817N00634199EA0000002528
    +B1708184548810N00634212EA0000002532
    +B1708204548802N00634224EA0000002536
    +B1708224548792N00634229EA0000002540
    +B1708254548783N00634224EA0000002543
    +B1708274548778N00634214EA0000002545
    +B1708294548781N00634200EA0000002548
    +B1708314548792N00634195EA0000002551
    +B1708334548801N00634206EA0000002554
    +B1708354548802N00634222EA0000002559
    +B1708374548797N00634238EA0000002564
    +B1708394548787N00634249EA0000002569
    +B1708414548775N00634254EA0000002575
    +B1708434548764N00634248EA0000002581
    +B1708454548758N00634236EA0000002586
    +B1708474548758N00634222EA0000002591
    +B1708494548767N00634216EA0000002595
    +B1708514548777N00634222EA0000002599
    +B1708534548781N00634237EA0000002604
    +B1708554548779N00634252EA0000002610
    +B1708574548774N00634266EA0000002616
    +B1708594548766N00634277EA0000002622
    +B1709014548755N00634284EA0000002628
    +B1709034548743N00634285EA0000002634
    +B1709054548734N00634276EA0000002639
    +B1709074548730N00634263EA0000002644
    +B1709094548734N00634252EA0000002648
    +B1709114548743N00634247EA0000002652
    +B1709134548751N00634255EA0000002656
    +B1709154548754N00634270EA0000002660
    +B1709174548752N00634284EA0000002666
    +B1709194548748N00634296EA0000002672
    +B1709214548743N00634308EA0000002678
    +B1709234548738N00634322EA0000002682
    +B1709254548735N00634337EA0000002687
    +B1709274548733N00634352EA0000002689
    +B1709294548728N00634368EA0000002692
    +B1709314548726N00634384EA0000002694
    +B1709334548724N00634399EA0000002697
    +B1709354548723N00634419EA0000002698
    +B1709374548723N00634442EA0000002698
    +B1709394548725N00634472EA0000002701
    +B1709414548725N00634491EA0000002702
    +B1709434548727N00634513EA0000002703
    +B1709454548730N00634532EA0000002704
    +B1709474548733N00634549EA0000002706
    +B1709494548738N00634566EA0000002706
    +B1709514548745N00634586EA0000002706
    +B1709534548751N00634606EA0000002707
    +B1709554548756N00634624EA0000002709
    +B1709574548762N00634641EA0000002709
    +B1709594548769N00634660EA0000002709
    +B1710014548778N00634680EA0000002708
    +B1710034548787N00634696EA0000002708
    +B1710064548795N00634713EA0000002707
    +B1710084548802N00634733EA0000002705
    +B1710104548810N00634753EA0000002703
    +B1710124548816N00634772EA0000002701
    +B1710144548824N00634790EA0000002700
    +B1710164548832N00634808EA0000002697
    +B1710184548841N00634827EA0000002694
    +B1710204548849N00634845EA0000002691
    +B1710224548856N00634862EA0000002688
    +B1710244548865N00634879EA0000002684
    +B1710264548873N00634897EA0000002680
    +B1710284548873N00634897EA0000002673
    +B1710304548890N00634932EA0000002670
    +B1710324548898N00634948EA0000002666
    +B1710344548906N00634966EA0000002663
    +B1710364548915N00634981EA0000002661
    +B1710384548924N00634996EA0000002659
    +B1710404548934N00635010EA0000002657
    +B1710424548944N00635023EA0000002654
    +B1710444548953N00635037EA0000002652
    +B1710464548962N00635052EA0000002650
    +B1710484548971N00635067EA0000002647
    +B1710504548981N00635081EA0000002645
    +B1710524548990N00635094EA0000002642
    +B1710544549001N00635106EA0000002639
    +B1710564549011N00635119EA0000002637
    +B1710584549021N00635133EA0000002634
    +B1711004549031N00635145EA0000002630
    +B1711024549042N00635159EA0000002627
    +B1711044549051N00635174EA0000002624
    +B1711064549060N00635187EA0000002622
    +B1711084549070N00635200EA0000002619
    +B1711104549080N00635214EA0000002615
    +B1711124549090N00635229EA0000002613
    +B1711144549099N00635243EA0000002611
    +B1711164549107N00635258EA0000002607
    +B1711184549118N00635285EA0000002604
    +B1711204549127N00635300EA0000002601
    +B1711224549135N00635315EA0000002598
    +B1711244549144N00635329EA0000002595
    +B1711264549154N00635343EA0000002591
    +B1711284549163N00635357EA0000002588
    +B1711304549171N00635373EA0000002584
    +B1711324549180N00635388EA0000002580
    +B1711344549189N00635405EA0000002577
    +B1711364549196N00635421EA0000002574
    +B1711384549205N00635436EA0000002571
    +B1711404549213N00635451EA0000002568
    +B1711424549220N00635469EA0000002564
    +B1711444549224N00635488EA0000002561
    +B1711474549228N00635507EA0000002558
    +B1711494549231N00635527EA0000002555
    +B1711514549233N00635547EA0000002552
    +B1711534549234N00635567EA0000002549
    +B1711554549236N00635587EA0000002546
    +B1711574549238N00635608EA0000002543
    +B1711594549240N00635628EA0000002540
    +B1712014549241N00635649EA0000002537
    +B1712034549243N00635669EA0000002534
    +B1712054549244N00635679EA0000002532
    +B1712074549248N00635708EA0000002530
    +B1712094549252N00635726EA0000002527
    +B1712114549255N00635744EA0000002524
    +B1712134549258N00635763EA0000002521
    +B1712154549262N00635782EA0000002518
    +B1712174549266N00635800EA0000002515
    +B1712194549269N00635819EA0000002512
    +B1712214549273N00635838EA0000002508
    +B1712234549277N00635857EA0000002506
    +B1712254549283N00635874EA0000002503
    +B1712274549290N00635890EA0000002500
    +B1712294549297N00635907EA0000002497
    +B1712314549304N00635923EA0000002495
    +B1712334549311N00635939EA0000002491
    +B1712354549318N00635955EA0000002487
    +B1712374549325N00635972EA0000002484
    +B1712394549333N00635986EA0000002481
    +B1712414549343N00635999EA0000002478
    +B1712434549352N00636012EA0000002475
    +B1712454549363N00636024EA0000002471
    +B1712474549373N00636036EA0000002468
    +B1712494549383N00636051EA0000002465
    +B1712514549392N00636066EA0000002463
    +B1712534549402N00636080EA0000002461
    +B1712554549412N00636094EA0000002458
    +B1712574549423N00636106EA0000002454
    +B1712594549440N00636126EA0000002451
    +B1713014549448N00636140EA0000002448
    +B1713034549456N00636154EA0000002445
    +B1713054549466N00636168EA0000002442
    +B1713074549476N00636181EA0000002440
    +B1713094549485N00636195EA0000002437
    +B1713114549493N00636210EA0000002434
    +B1713134549502N00636225EA0000002431
    +B1713154549510N00636241EA0000002428
    +B1713174549518N00636257EA0000002425
    +B1713194549527N00636271EA0000002421
    +B1713214549537N00636285EA0000002418
    +B1713234549546N00636299EA0000002414
    +B1713254549556N00636313EA0000002411
    +B1713284549565N00636326EA0000002407
    +B1713304549575N00636339EA0000002404
    +B1713324549584N00636355EA0000002401
    +B1713344549591N00636372EA0000002397
    +B1713364549598N00636389EA0000002394
    +B1713384549604N00636408EA0000002390
    +B1713404549609N00636427EA0000002387
    +B1713424549612N00636436EA0000002384
    +B1713444549622N00636462EA0000002381
    +B1713464549628N00636481EA0000002378
    +B1713484549635N00636499EA0000002375
    +B1713504549641N00636518EA0000002373
    +B1713524549645N00636538EA0000002370
    +B1713544549651N00636557EA0000002368
    +B1713564549657N00636575EA0000002365
    +B1713584549663N00636594EA0000002362
    +B1714004549669N00636613EA0000002360
    +B1714024549675N00636631EA0000002357
    +B1714044549680N00636651EA0000002354
    +B1714064549686N00636670EA0000002351
    +B1714084549693N00636688EA0000002348
    +B1714104549700N00636707EA0000002346
    +B1714124549706N00636726EA0000002343
    +B1714144549713N00636745EA0000002341
    +B1714164549719N00636763EA0000002339
    +B1714184549726N00636782EA0000002337
    +B1714204549733N00636801EA0000002334
    +B1714224549739N00636820EA0000002332
    +B1714244549746N00636839EA0000002329
    +B1714264549753N00636858EA0000002327
    +B1714284549760N00636876EA0000002325
    +B1714304549767N00636895EA0000002323
    +B1714324549774N00636913EA0000002321
    +B1714344549781N00636931EA0000002319
    +B1714364549789N00636948EA0000002317
    +B1714384549796N00636967EA0000002314
    +B1714404549807N00636994EA0000002311
    +B1714424549815N00637012EA0000002308
    +B1714444549823N00637029EA0000002305
    +B1714464549831N00637047EA0000002303
    +B1714484549840N00637065EA0000002300
    +B1714504549849N00637081EA0000002299
    +B1714524549857N00637098EA0000002296
    +B1714544549865N00637115EA0000002294
    +B1714564549873N00637132EA0000002292
    +B1714584549881N00637148EA0000002289
    +B1715004549888N00637165EA0000002287
    +B1715024549896N00637181EA0000002284
    +B1715044549903N00637199EA0000002281
    +B1715064549910N00637216EA0000002278
    +B1715094549917N00637233EA0000002274
    +B1715114549925N00637250EA0000002271
    +B1715134549933N00637267EA0000002268
    +B1715154549940N00637284EA0000002265
    +B1715174549949N00637300EA0000002262
    +B1715194549953N00637308EA0000002258
    +B1715214549966N00637332EA0000002255
    +B1715234549974N00637348EA0000002253
    +B1715254549983N00637365EA0000002250
    +B1715274549990N00637382EA0000002248
    +B1715294549999N00637397EA0000002246
    +B1715314550008N00637412EA0000002243
    +B1715334550016N00637428EA0000002241
    +B1715354550025N00637444EA0000002239
    +B1715374550033N00637460EA0000002237
    +B1715394550041N00637476EA0000002234
    +B1715414550048N00637494EA0000002232
    +B1715434550055N00637512EA0000002230
    +B1715454550062N00637529EA0000002229
    +B1715474550069N00637546EA0000002227
    +B1715494550075N00637563EA0000002225
    +B1715514550082N00637581EA0000002222
    +B1715534550087N00637599EA0000002220
    +B1715554550093N00637618EA0000002217
    +B1715574550098N00637637EA0000002215
    +B1715594550103N00637655EA0000002213
    +B1716014550109N00637673EA0000002211
    +B1716034550115N00637691EA0000002208
    +B1716054550122N00637710EA0000002206
    +B1716074550128N00637727EA0000002204
    +B1716094550134N00637746EA0000002200
    +B1716114550139N00637765EA0000002197
    +B1716134550146N00637783EA0000002194
    +B1716154550152N00637802EA0000002191
    +B1716174550158N00637820EA0000002188
    +B1716194550164N00637837EA0000002185
    +B1716214550172N00637864EA0000002180
    +B1716234550178N00637883EA0000002177
    +B1716254550183N00637902EA0000002175
    +B1716274550189N00637921EA0000002174
    +B1716294550194N00637939EA0000002172
    +B1716314550200N00637957EA0000002170
    +B1716334550206N00637975EA0000002168
    +B1716354550212N00637993EA0000002165
    +B1716374550219N00638012EA0000002163
    +B1716394550227N00638029EA0000002162
    +B1716414550234N00638046EA0000002160
    +B1716434550240N00638064EA0000002157
    +B1716454550246N00638083EA0000002156
    +B1716484550253N00638099EA0000002154
    +B1716504550260N00638115EA0000002152
    +B1716524550267N00638133EA0000002150
    +B1716544550275N00638150EA0000002148
    +B1716564550280N00638158EA0000002146
    +B1716584550292N00638182EA0000002142
    +B1717004550300N00638201EA0000002138
    +B1717024550306N00638220EA0000002136
    +B1717044550312N00638238EA0000002132
    +B1717064550319N00638257EA0000002128
    +B1717084550327N00638275EA0000002126
    +B1717104550334N00638290EA0000002123
    +B1717124550341N00638307EA0000002119
    +B1717144550348N00638324EA0000002114
    +B1717164550354N00638343EA0000002111
    +B1717184550359N00638362EA0000002109
    +B1717204550365N00638381EA0000002106
    +B1717224550369N00638400EA0000002102
    +B1717244550374N00638419EA0000002099
    +B1717264550379N00638437EA0000002096
    +B1717284550386N00638456EA0000002093
    +B1717304550392N00638473EA0000002091
    +B1717324550397N00638491EA0000002088
    +B1717344550402N00638508EA0000002085
    +B1717364550407N00638527EA0000002081
    +B1717384550413N00638545EA0000002078
    +B1717404550419N00638564EA0000002074
    +B1717424550424N00638582EA0000002072
    +B1717444550430N00638600EA0000002069
    +B1717464550435N00638619EA0000002066
    +B1717484550439N00638639EA0000002063
    +B1717504550441N00638659EA0000002061
    +B1717524550444N00638679EA0000002059
    +B1717544550446N00638698EA0000002056
    +B1717564550449N00638718EA0000002053
    +B1717584550452N00638739EA0000002049
    +B1718004550456N00638760EA0000002047
    +B1718024550460N00638790EA0000002043
    +B1718044550463N00638810EA0000002040
    +B1718064550464N00638832EA0000002037
    +B1718084550467N00638852EA0000002036
    +B1718104550469N00638872EA0000002034
    +B1718124550471N00638893EA0000002031
    +B1718144550473N00638916EA0000002030
    +B1718164550476N00638937EA0000002028
    +B1718184550478N00638959EA0000002025
    +B1718204550480N00638980EA0000002022
    +B1718224550482N00639002EA0000002019
    +B1718244550486N00639024EA0000002017
    +B1718264550490N00639045EA0000002015
    +B1718294550494N00639065EA0000002012
    +B1718314550498N00639087EA0000002009
    +B1718334550502N00639109EA0000002007
    +B1718354550505N00639131EA0000002006
    +B1718374550506N00639152EA0000002005
    +B1718394550508N00639170EA0000002004
    +B1718414550510N00639189EA0000002003
    +B1718434550513N00639208EA0000002002
    +B1718454550516N00639226EA0000002003
    +B1718474550518N00639241EA0000002004
    +B1718494550520N00639256EA0000002004
    +B1718514550521N00639270EA0000002004
    +B1718534550521N00639284EA0000002003
    +B1718554550520N00639299EA0000002002
    +B1718574550520N00639315EA0000002001
    +B1718594550519N00639330EA0000002000
    +B1719014550519N00639346EA0000001998
    +B1719034550518N00639364EA0000001996
    +B1719054550517N00639381EA0000001995
    +B1719074550516N00639398EA0000001993
    +B1719094550515N00639416EA0000001990
    +B1719114550514N00639435EA0000001989
    +B1719134550515N00639453EA0000001987
    +B1719154550516N00639471EA0000001985
    +B1719174550517N00639489EA0000001984
    +B1719194550518N00639506EA0000001983
    +B1719214550519N00639523EA0000001981
    +B1719234550520N00639542EA0000001981
    +B1719254550520N00639560EA0000001982
    +B1719274550520N00639575EA0000001983
    +B1719294550519N00639592EA0000001982
    +B1719314550518N00639611EA0000001983
    +B1719334550518N00639627EA0000001983
    +B1719354550518N00639643EA0000001981
    +B1719374550518N00639662EA0000001981
    +B1719394550519N00639680EA0000001981
    +B1719414550521N00639704EA0000001981
    +B1719434550522N00639722EA0000001980
    +B1719454550523N00639740EA0000001981
    +B1719474550525N00639755EA0000001982
    +B1719494550527N00639770EA0000001982
    +B1719514550529N00639787EA0000001983
    +B1719534550530N00639802EA0000001985
    +B1719554550531N00639815EA0000001987
    +B1719574550536N00639821EA0000001987
    +B1719594550542N00639820EA0000001988
    +B1720014550545N00639813EA0000001989
    +B1720034550544N00639801EA0000001989
    +B1720054550538N00639788EA0000001989
    +B1720074550523N00639781EA0000001990
    +B1720104550510N00639788EA0000001990
    +B1720124550501N00639801EA0000001991
    +B1720144550498N00639817EA0000001992
    +B1720164550498N00639832EA0000001994
    +B1720184550499N00639847EA0000001994
    +B1720204550501N00639861EA0000001995
    +B1720224550504N00639874EA0000001996
    +B1720244550509N00639886EA0000001997
    +B1720264550514N00639897EA0000001997
    +B1720284550518N00639910EA0000001998
    +B1720304550522N00639923EA0000001998
    +B1720324550526N00639936EA0000001997
    +B1720344550531N00639949EA0000001996
    +B1720364550537N00639960EA0000001996
    +B1720384550542N00639973EA0000001996
    +B1720404550547N00639985EA0000001995
    +B1720424550552N00639998EA0000001995
    +B1720444550558N00640010EA0000001995
    +B1720464550564N00640019EA0000001994
    +B1720484550571N00640029EA0000001993
    +B1720504550577N00640043EA0000001992
    +B1720524550582N00640056EA0000001992
    +B1720544550588N00640067EA0000001992
    +B1720564550594N00640079EA0000001991
    +B1720584550600N00640089EA0000001991
    +B1721004550607N00640100EA0000001990
    +B1721024550614N00640110EA0000001988
    +B1721044550622N00640121EA0000001987
    +B1721064550631N00640131EA0000001987
    +B1721084550638N00640142EA0000001986
    +B1721104550646N00640153EA0000001984
    +B1721124550656N00640163EA0000001983
    +B1721144550665N00640173EA0000001982
    +B1721164550673N00640184EA0000001980
    +B1721184550683N00640193EA0000001977
    +B1721204550697N00640207EA0000001976
    +B1721224550706N00640217EA0000001974
    +B1721244550715N00640227EA0000001972
    +B1721264550724N00640238EA0000001970
    +B1721284550732N00640252EA0000001969
    +B1721304550740N00640264EA0000001968
    +B1721324550748N00640276EA0000001966
    +B1721344550756N00640290EA0000001964
    +B1721364550765N00640301EA0000001963
    +B1721384550774N00640308EA0000001962
    +B1721404550783N00640316EA0000001960
    +B1721424550792N00640327EA0000001958
    +B1721444550800N00640338EA0000001958
    +B1721464550809N00640349EA0000001957
    +B1721494550817N00640361EA0000001956
    +B1721514550826N00640373EA0000001956
    +B1721534550833N00640386EA0000001956
    +B1721554550841N00640398EA0000001955
    +B1721574550850N00640409EA0000001955
    +B1721594550858N00640422EA0000001955
    +B1722014550866N00640436EA0000001956
    +B1722034550874N00640450EA0000001957
    +B1722054550881N00640464EA0000001957
    +B1722074550888N00640479EA0000001957
    +B1722094550888N00640479EA0000001956
    +B1722114550904N00640509EA0000001956
    +B1722134550910N00640525EA0000001957
    +B1722154550916N00640545EA0000001957
    +B1722174550922N00640563EA0000001958
    +B1722194550927N00640580EA0000001958
    +B1722214550932N00640599EA0000001957
    +B1722234550939N00640619EA0000001956
    +B1722254550946N00640637EA0000001955
    +B1722274550952N00640653EA0000001954
    +B1722294550958N00640669EA0000001953
    +B1722314550967N00640684EA0000001952
    +B1722334550977N00640698EA0000001949
    +B1722354550985N00640712EA0000001946
    +B1722374550994N00640728EA0000001942
    +B1722394551003N00640743EA0000001938
    +B1722414551012N00640759EA0000001935
    +B1722434551020N00640774EA0000001932
    +B1722454551028N00640788EA0000001928
    +B1722474551037N00640802EA0000001925
    +B1722494551045N00640813EA0000001921
    +B1722514551055N00640825EA0000001917
    +B1722534551064N00640838EA0000001913
    +B1722554551072N00640851EA0000001909
    +B1722574551080N00640865EA0000001906
    +B1722594551087N00640881EA0000001902
    +B1723014551099N00640903EA0000001897
    +B1723034551108N00640917EA0000001894
    +B1723054551116N00640932EA0000001891
    +B1723074551124N00640945EA0000001888
    +B1723094551133N00640959EA0000001884
    +B1723114551142N00640974EA0000001880
    +B1723134551150N00640988EA0000001877
    +B1723154551158N00641002EA0000001874
    +B1723174551167N00641016EA0000001870
    +B1723194551175N00641030EA0000001866
    +B1723214551184N00641043EA0000001863
    +B1723234551192N00641056EA0000001860
    +B1723254551202N00641069EA0000001856
    +B1723284551210N00641083EA0000001854
    +B1723304551219N00641098EA0000001851
    +B1723324551227N00641111EA0000001849
    +B1723344551236N00641124EA0000001847
    +B1723364551245N00641137EA0000001845
    +B1723384551254N00641150EA0000001843
    +B1723404551263N00641162EA0000001841
    +B1723424551273N00641175EA0000001839
    +B1723444551282N00641187EA0000001837
    +B1723464551291N00641200EA0000001834
    +B1723484551300N00641214EA0000001831
    +B1723504551309N00641227EA0000001828
    +B1723524551318N00641240EA0000001826
    +B1723544551327N00641253EA0000001823
    +B1723564551336N00641266EA0000001819
    +B1723584551346N00641280EA0000001817
    +B1724004551355N00641294EA0000001814
    +B1724024551364N00641307EA0000001812
    +B1724044551373N00641320EA0000001809
    +B1724064551383N00641332EA0000001807
    +B1724084551392N00641344EA0000001805
    +B1724104551401N00641357EA0000001803
    +B1724124551410N00641371EA0000001801
    +B1724144551418N00641386EA0000001799
    +B1724164551426N00641403EA0000001796
    +B1724184551435N00641421EA0000001793
    +B1724204551443N00641438EA0000001791
    +B1724224551450N00641455EA0000001789
    +B1724244551458N00641474EA0000001787
    +B1724264551466N00641493EA0000001786
    +B1724284551473N00641511EA0000001785
    +B1724304551480N00641530EA0000001784
    +B1724324551487N00641547EA0000001782
    +B1724344551494N00641565EA0000001781
    +B1724364551502N00641581EA0000001780
    +B1724384551509N00641596EA0000001778
    +B1724404551517N00641612EA0000001776
    +B1724424551530N00641641EA0000001773
    +B1724444551537N00641657EA0000001772
    +B1724464551545N00641673EA0000001771
    +B1724484551551N00641687EA0000001771
    +B1724504551558N00641700EA0000001770
    +B1724524551566N00641713EA0000001769
    +B1724544551574N00641729EA0000001766
    +B1724564551583N00641744EA0000001764
    +B1724584551592N00641758EA0000001762
    +B1725004551600N00641772EA0000001760
    +B1725024551609N00641787EA0000001757
    +B1725044551619N00641802EA0000001755
    +B1725064551628N00641818EA0000001753
    +B1725094551637N00641832EA0000001751
    +B1725114551647N00641846EA0000001749
    +B1725134551657N00641861EA0000001746
    +B1725154551667N00641876EA0000001746
    +B1725174551676N00641888EA0000001746
    +B1725194551685N00641898EA0000001745
    +B1725214551696N00641911EA0000001742
    +B1725234551705N00641926EA0000001741
    +B1725254551714N00641940EA0000001739
    +B1725274551724N00641953EA0000001736
    +B1725294551735N00641967EA0000001735
    +B1725314551745N00641982EA0000001734
    +B1725334551755N00641995EA0000001733
    +B1725354551765N00642009EA0000001732
    +B1725374551776N00642023EA0000001731
    +B1725394551786N00642036EA0000001731
    +B1725414551797N00642047EA0000001730
    +B1725434551807N00642058EA0000001728
    +B1725454551818N00642069EA0000001726
    +B1725474551830N00642079EA0000001723
    +B1725494551841N00642090EA0000001722
    +B1725514551851N00642101EA0000001721
    +B1725534551860N00642114EA0000001719
    +B1725554551871N00642126EA0000001718
    +B1725574551881N00642138EA0000001717
    +B1725594551890N00642149EA0000001716
    +B1726014551900N00642161EA0000001713
    +B1726034551911N00642174EA0000001710
    +B1726054551922N00642187EA0000001709
    +B1726074551933N00642199EA0000001706
    +B1726094551944N00642211EA0000001703
    +B1726114551955N00642224EA0000001700
    +B1726134551966N00642235EA0000001699
    +B1726154551976N00642248EA0000001697
    +B1726174551986N00642262EA0000001695
    +B1726194551997N00642273EA0000001693
    +B1726214552007N00642285EA0000001692
    +B1726234552021N00642307EA0000001689
    +B1726254552031N00642319EA0000001687
    +B1726274552041N00642331EA0000001685
    +B1726294552052N00642343EA0000001683
    +B1726314552063N00642354EA0000001681
    +B1726334552075N00642363EA0000001679
    +B1726354552086N00642373EA0000001678
    +B1726374552098N00642382EA0000001676
    +B1726394552110N00642391EA0000001675
    +B1726414552121N00642401EA0000001673
    +B1726434552133N00642412EA0000001670
    +B1726454552145N00642422EA0000001669
    +B1726474552156N00642432EA0000001668
    +B1726504552167N00642444EA0000001666
    +B1726524552179N00642456EA0000001664
    +B1726544552190N00642467EA0000001662
    +B1726564552202N00642476EA0000001661
    +B1726584552214N00642485EA0000001660
    +B1727004552225N00642492EA0000001658
    +B1727024552237N00642500EA0000001656
    +B1727044552249N00642510EA0000001654
    +B1727064552260N00642521EA0000001653
    +B1727084552271N00642532EA0000001653
    +B1727104552282N00642543EA0000001651
    +B1727124552282N00642543EA0000001649
    +B1727144552303N00642568EA0000001648
    +B1727164552313N00642581EA0000001647
    +B1727184552323N00642594EA0000001646
    +B1727204552333N00642609EA0000001644
    +B1727224552343N00642625EA0000001642
    +B1727244552354N00642639EA0000001641
    +B1727264552364N00642650EA0000001640
    +B1727284552375N00642661EA0000001637
    +B1727304552387N00642672EA0000001636
    +B1727324552399N00642682EA0000001635
    +B1727344552409N00642693EA0000001633
    +B1727364552420N00642704EA0000001632
    +B1727384552431N00642717EA0000001631
    +B1727404552440N00642730EA0000001631
    +B1727424552450N00642742EA0000001632
    +B1727444552459N00642754EA0000001633
    +B1727464552466N00642766EA0000001635
    +B1727484552473N00642777EA0000001635
    +B1727504552481N00642788EA0000001634
    +B1727524552489N00642800EA0000001635
    +B1727544552496N00642813EA0000001635
    +B1727564552504N00642825EA0000001636
    +B1727584552512N00642837EA0000001636
    +B1728004552519N00642848EA0000001636
    +B1728024552533N00642866EA0000001635
    +B1728044552542N00642877EA0000001635
    +B1728064552551N00642888EA0000001635
    +B1728084552560N00642899EA0000001636
    +B1728104552569N00642910EA0000001636
    +B1728124552577N00642923EA0000001637
    +B1728144552583N00642935EA0000001638
    +B1728164552589N00642948EA0000001638
    +B1728184552596N00642962EA0000001637
    +B1728204552603N00642977EA0000001636
    +B1728224552610N00642992EA0000001634
    +B1728244552619N00643005EA0000001633
    +B1728264552628N00643019EA0000001632
    +B1728284552638N00643030EA0000001631
    +B1728314552647N00643043EA0000001630
    +B1728334552657N00643056EA0000001629
    +B1728354552667N00643069EA0000001629
    +B1728374552676N00643082EA0000001629
    +B1728394552685N00643096EA0000001628
    +B1728414552694N00643109EA0000001628
    +B1728434552704N00643121EA0000001627
    +B1728454552714N00643132EA0000001627
    +B1728474552724N00643144EA0000001626
    +B1728494552733N00643155EA0000001626
    +B1728514552744N00643165EA0000001626
    +B1728534552754N00643178EA0000001627
    +B1728554552764N00643189EA0000001629
    +B1728574552772N00643199EA0000001629
    +B1728594552782N00643209EA0000001628
    +B1729014552793N00643220EA0000001628
    +B1729034552803N00643229EA0000001630
    +B1729054552813N00643239EA0000001631
    +B1729074552823N00643252EA0000001633
    +B1729094552833N00643265EA0000001634
    +B1729114552841N00643278EA0000001637
    +B1729134552846N00643290EA0000001640
    +B1729154552853N00643301EA0000001642
    +B1729174552858N00643310EA0000001644
    +B1729194552864N00643318EA0000001646
    +B1729214552870N00643328EA0000001647
    +B1729234552875N00643338EA0000001650
    +B1729254552880N00643346EA0000001652
    +B1729274552885N00643356EA0000001653
    +B1729294552890N00643367EA0000001654
    +B1729314552896N00643376EA0000001657
    +B1729334552901N00643386EA0000001660
    +B1729354552906N00643397EA0000001661
    +B1729374552911N00643410EA0000001663
    +B1729394552918N00643422EA0000001665
    +B1729414552924N00643434EA0000001667
    +B1729434552934N00643453EA0000001670
    +B1729454552941N00643466EA0000001673
    +B1729474552947N00643478EA0000001677
    +B1729494552953N00643489EA0000001680
    +B1729514552958N00643502EA0000001681
    +B1729534552964N00643516EA0000001682
    +B1729554552970N00643527EA0000001684
    +B1729574552975N00643532EA0000001684
    +B1729594552980N00643532EA0000001685
    +B1730014552982N00643529EA0000001685
    +B1730034552981N00643522EA0000001685
    +B1730054552974N00643516EA0000001685
    +B1730084552963N00643515EA0000001686
    +B1730104552951N00643521EA0000001686
    +B1730124552941N00643537EA0000001686
    +B1730144552936N00643555EA0000001687
    +B1730164552934N00643574EA0000001687
    +B1730184552933N00643593EA0000001688
    +B1730204552932N00643612EA0000001690
    +B1730224552932N00643631EA0000001692
    +B1730244552932N00643650EA0000001695
    +B1730264552932N00643669EA0000001698
    +B1730284552934N00643686EA0000001699
    +B1730304552940N00643700EA0000001701
    +B1730324552947N00643708EA0000001703
    +B1730344552951N00643706EA0000001706
    +B1730364552950N00643700EA0000001709
    +B1730384552946N00643695EA0000001712
    +B1730404552936N00643693EA0000001714
    +B1730424552923N00643700EA0000001714
    +B1730444552913N00643718EA0000001716
    +B1730464552909N00643739EA0000001717
    +B1730484552911N00643758EA0000001719
    +B1730504552917N00643773EA0000001722
    +B1730524552922N00643785EA0000001725
    +B1730544552928N00643793EA0000001728
    +B1730564552934N00643799EA0000001731
    +B1730584552940N00643793EA0000001733
    +B1731004552936N00643782EA0000001736
    +B1731024552928N00643779EA0000001739
    +B1731044552917N00643782EA0000001740
    +B1731064552904N00643794EA0000001741
    +B1731084552896N00643817EA0000001743
    +B1731104552896N00643839EA0000001745
    +B1731124552901N00643855EA0000001748
    +B1731144552907N00643866EA0000001751
    +B1731164552915N00643875EA0000001754
    +B1731184552922N00643879EA0000001755
    +B1731204552933N00643871EA0000001757
    +B1731224552935N00643864EA0000001759
    +B1731244552932N00643856EA0000001760
    +B1731264552922N00643851EA0000001761
    +B1731284552911N00643861EA0000001761
    +B1731304552903N00643881EA0000001761
    +B1731324552899N00643902EA0000001762
    +B1731344552901N00643920EA0000001765
    +B1731364552906N00643932EA0000001766
    +B1731384552911N00643940EA0000001766
    +B1731404552919N00643943EA0000001764
    +B1731424552929N00643944EA0000001763
    +B1731444552938N00643951EA0000001762
    +B1731474552948N00643960EA0000001763
    +B1731494552957N00643966EA0000001766
    +B1731514552965N00643970EA0000001769
    +B1731534552973N00643973EA0000001771
    +B1731554552979N00643971EA0000001772
    +B1731574552982N00643966EA0000001773
    +B1731594552984N00643962EA0000001772
    +B1732014552990N00643962EA0000001771
    +B1732034553002N00643970EA0000001770
    +B1732054553011N00643982EA0000001772
    +B1732074553019N00643992EA0000001771
    +B1732094553028N00644004EA0000001771
    +B1732114553037N00644017EA0000001773
    +B1732134553045N00644028EA0000001774
    +B1732154553055N00644038EA0000001773
    +B1732174553065N00644049EA0000001774
    +B1732194553074N00644057EA0000001775
    +B1732214553083N00644063EA0000001776
    +B1732234553093N00644070EA0000001776
    +B1732254553102N00644075EA0000001776
    +B1732274553110N00644079EA0000001776
    +B1732294553120N00644085EA0000001774
    +B1732314553131N00644093EA0000001773
    +B1732334553141N00644099EA0000001772
    +B1732354553152N00644103EA0000001769
    +B1732374553164N00644109EA0000001767
    +B1732394553175N00644115EA0000001765
    +B1732414553186N00644120EA0000001762
    +B1732434553198N00644126EA0000001759
    +B1732454553210N00644136EA0000001757
    +B1732474553221N00644145EA0000001756
    +B1732494553231N00644153EA0000001756
    +B1732514553241N00644162EA0000001755
    +B1732534553252N00644168EA0000001753
    +B1732554553262N00644172EA0000001751
    +B1732574553273N00644175EA0000001749
    +B1732594553285N00644175EA0000001746
    +B1733014553303N00644175EA0000001744
    +B1733034553314N00644177EA0000001743
    +B1733054553325N00644177EA0000001743
    +B1733074553335N00644177EA0000001742
    +B1733094553346N00644177EA0000001741
    +B1733114553356N00644178EA0000001739
    +B1733134553368N00644180EA0000001737
    +B1733154553380N00644185EA0000001735
    +B1733174553391N00644190EA0000001734
    +B1733194553403N00644193EA0000001734
    +B1733214553414N00644198EA0000001733
    +B1733234553424N00644201EA0000001732
    +B1733254553437N00644203EA0000001729
    +B1733284553448N00644209EA0000001728
    +B1733304553458N00644215EA0000001726
    +B1733324553469N00644222EA0000001723
    +B1733344553480N00644229EA0000001720
    +B1733364553491N00644235EA0000001717
    +B1733384553502N00644239EA0000001715
    +B1733404553514N00644240EA0000001713
    +B1733424553526N00644240EA0000001711
    +B1733444553539N00644242EA0000001709
    +B1733464553551N00644246EA0000001706
    +B1733484553563N00644247EA0000001704
    +B1733504553577N00644245EA0000001700
    +B1733524553590N00644246EA0000001697
    +B1733544553602N00644247EA0000001695
    +B1733564553615N00644246EA0000001692
    +B1733584553628N00644249EA0000001689
    +B1734004553641N00644251EA0000001687
    +B1734024553654N00644251EA0000001683
    +B1734044553668N00644251EA0000001680
    +B1734064553683N00644254EA0000001677
    +B1734084553693N00644256EA0000001675
    +B1734104553705N00644257EA0000001673
    +B1734124553719N00644261EA0000001670
    +B1734144553732N00644266EA0000001669
    +B1734164553743N00644268EA0000001668
    +B1734184553755N00644272EA0000001667
    +B1734204553767N00644276EA0000001666
    +B1734224553779N00644279EA0000001666
    +B1734244553791N00644280EA0000001667
    +B1734264553804N00644283EA0000001667
    +B1734284553816N00644284EA0000001668
    +B1734304553828N00644283EA0000001668
    +B1734324553842N00644284EA0000001669
    +B1734344553854N00644284EA0000001671
    +B1734364553867N00644283EA0000001672
    +B1734384553880N00644283EA0000001673
    +B1734404553899N00644289EA0000001675
    +B1734424553910N00644298EA0000001677
    +B1734444553920N00644305EA0000001678
    +B1734464553930N00644307EA0000001678
    +B1734484553942N00644306EA0000001676
    +B1734504553955N00644309EA0000001675
    +B1734524553968N00644312EA0000001673
    +B1734544553981N00644312EA0000001671
    +B1734564553994N00644312EA0000001669
    +B1734584554008N00644312EA0000001667
    +B1735004554021N00644311EA0000001666
    +B1735024554035N00644309EA0000001665
    +B1735044554049N00644309EA0000001664
    +B1735074554062N00644308EA0000001663
    +B1735094554075N00644306EA0000001662
    +B1735114554088N00644305EA0000001661
    +B1735134554101N00644304EA0000001660
    +B1735154554113N00644302EA0000001659
    +B1735174554125N00644301EA0000001658
    +B1735194554139N00644300EA0000001659
    +B1735214554152N00644297EA0000001660
    +B1735234554166N00644296EA0000001660
    +B1735254554180N00644296EA0000001659
    +B1735274554194N00644295EA0000001657
    +B1735294554208N00644294EA0000001657
    +B1735314554223N00644294EA0000001657
    +B1735334554236N00644293EA0000001659
    +B1735354554249N00644291EA0000001659
    +B1735374554263N00644290EA0000001659
    +B1735394554276N00644291EA0000001660
    +B1735414554289N00644291EA0000001661
    +B1735434554302N00644291EA0000001660
    +B1735454554316N00644291EA0000001660
    +B1735474554330N00644291EA0000001659
    +B1735494554343N00644291EA0000001658
    +B1735514554357N00644291EA0000001657
    +B1735534554371N00644292EA0000001654
    +B1735554554386N00644293EA0000001651
    +B1735574554400N00644294EA0000001649
    +B1735594554415N00644295EA0000001647
    +B1736014554429N00644296EA0000001645
    +B1736034554444N00644297EA0000001643
    +B1736054554458N00644297EA0000001641
    +B1736074554473N00644297EA0000001639
    +B1736094554488N00644297EA0000001638
    +B1736114554502N00644296EA0000001636
    +B1736134554516N00644295EA0000001633
    +B1736154554532N00644294EA0000001631
    +B1736174554546N00644295EA0000001628
    +B1736194554569N00644297EA0000001624
    +B1736214554584N00644298EA0000001621
    +B1736234554599N00644299EA0000001617
    +B1736254554614N00644302EA0000001614
    +B1736274554629N00644305EA0000001612
    +B1736294554644N00644306EA0000001608
    +B1736314554658N00644305EA0000001603
    +B1736334554674N00644307EA0000001599
    +B1736354554690N00644308EA0000001595
    +B1736374554705N00644308EA0000001591
    +B1736394554720N00644307EA0000001586
    +B1736414554736N00644308EA0000001581
    +B1736434554751N00644309EA0000001578
    +B1736454554765N00644310EA0000001575
    +B1736484554779N00644310EA0000001570
    +B1736504554795N00644312EA0000001566
    +B1736524554809N00644315EA0000001564
    +B1736544554823N00644317EA0000001560
    +B1736564554837N00644320EA0000001556
    +B1736584554852N00644323EA0000001552
    +B1737004554866N00644327EA0000001548
    +B1737024554881N00644332EA0000001543
    +B1737044554895N00644337EA0000001539
    +B1737064554909N00644342EA0000001535
    +B1737084554923N00644347EA0000001531
    +B1737104554938N00644351EA0000001527
    +B1737124554953N00644353EA0000001524
    +B1737144554968N00644356EA0000001521
    +B1737164554982N00644362EA0000001518
    +B1737184554995N00644370EA0000001517
    +B1737204555008N00644376EA0000001517
    +B1737224555021N00644379EA0000001517
    +B1737244555034N00644384EA0000001517
    +B1737264555046N00644389EA0000001515
    +B1737284555059N00644391EA0000001512
    +B1737304555073N00644390EA0000001511
    +B1737324555084N00644389EA0000001511
    +B1737344555094N00644389EA0000001511
    +B1737364555104N00644392EA0000001512
    +B1737384555114N00644396EA0000001514
    +B1737404555123N00644398EA0000001514
    +B1737424555133N00644401EA0000001513
    +B1737444555143N00644407EA0000001511
    +B1737464555153N00644414EA0000001510
    +B1737484555164N00644422EA0000001510
    +B1737504555174N00644430EA0000001511
    +B1737524555184N00644435EA0000001512
    +B1737544555194N00644440EA0000001513
    +B1737564555199N00644444EA0000001512
    +B1737584555212N00644459EA0000001512
    +B1738004555227N00644476EA0000001511
    +B1738024555235N00644490EA0000001509
    +B1738044555243N00644503EA0000001507
    +B1738064555253N00644513EA0000001504
    +B1738084555263N00644521EA0000001501
    +B1738104555273N00644529EA0000001498
    +B1738124555282N00644541EA0000001493
    +B1738144555291N00644552EA0000001489
    +B1738164555300N00644561EA0000001486
    +B1738184555308N00644571EA0000001482
    +B1738204555316N00644581EA0000001478
    +B1738224555325N00644592EA0000001476
    +B1738244555333N00644603EA0000001474
    +B1738264555341N00644613EA0000001471
    +B1738294555349N00644623EA0000001470
    +B1738314555356N00644633EA0000001469
    +B1738334555363N00644642EA0000001466
    +B1738354555370N00644653EA0000001463
    +B1738374555378N00644664EA0000001462
    +B1738394555384N00644675EA0000001459
    +B1738414555391N00644687EA0000001456
    +B1738434555399N00644699EA0000001454
    +B1738454555407N00644712EA0000001452
    +B1738474555415N00644724EA0000001448
    +B1738494555424N00644737EA0000001443
    +B1738514555433N00644752EA0000001438
    +B1738534555441N00644768EA0000001434
    +B1738554555449N00644783EA0000001430
    +B1738574555457N00644800EA0000001426
    +B1738594555463N00644818EA0000001423
    +B1739014555470N00644834EA0000001420
    +B1739034555476N00644851EA0000001417
    +B1739054555482N00644867EA0000001412
    +B1739074555490N00644883EA0000001408
    +B1739094555498N00644899EA0000001405
    +B1739114555506N00644915EA0000001402
    +B1739134555513N00644929EA0000001398
    +B1739154555522N00644944EA0000001393
    +B1739174555531N00644960EA0000001390
    +B1739194555536N00644975EA0000001387
    +B1739214555542N00644990EA0000001383
    +B1739234555549N00645006EA0000001378
    +B1739254555555N00645024EA0000001374
    +B1739274555560N00645042EA0000001370
    +B1739294555565N00645059EA0000001365
    +B1739314555569N00645077EA0000001361
    +B1739334555573N00645094EA0000001358
    +B1739354555578N00645108EA0000001353
    +B1739374555583N00645125EA0000001347
    +B1739394555587N00645143EA0000001344
    +B1739414555595N00645163EA0000001337
    +B1739434555600N00645178EA0000001332
    +B1739454555607N00645194EA0000001328
    +B1739474555613N00645208EA0000001324
    +B1739494555618N00645222EA0000001319
    +B1739514555624N00645237EA0000001314
    +B1739534555631N00645252EA0000001311
    +B1739554555634N00645265EA0000001308
    +B1739574555638N00645277EA0000001303
    +B1739594555643N00645290EA0000001298
    +B1740014555647N00645303EA0000001295
    +B1740034555651N00645315EA0000001290
    +B1740054555656N00645326EA0000001284
    +B1740084555661N00645343EA0000001279
    +B1740104555662N00645362EA0000001275
    +B1740124555665N00645378EA0000001272
    +B1740144555670N00645394EA0000001270
    +B1740164555673N00645409EA0000001268
    +B1740184555678N00645423EA0000001267
    +B1740204555683N00645438EA0000001266
    +B1740224555687N00645454EA0000001266
    +B1740244555691N00645468EA0000001264
    +B1740264555697N00645484EA0000001264
    +B1740284555702N00645497EA0000001266
    +B1740304555707N00645508EA0000001268
    +B1740324555710N00645517EA0000001270
    +B1740344555713N00645527EA0000001272
    +B1740364555716N00645536EA0000001275
    +B1740384555717N00645543EA0000001277
    +B1740404555719N00645552EA0000001278
    +B1740424555721N00645561EA0000001278
    +B1740444555725N00645564EA0000001279
    +B1740464555730N00645559EA0000001279
    +B1740484555733N00645550EA0000001278
    +B1740504555730N00645537EA0000001278
    +B1740524555721N00645523EA0000001279
    +B1740544555706N00645515EA0000001279
    +B1740564555691N00645522EA0000001280
    +B1740584555682N00645535EA0000001281
    +B1741004555680N00645548EA0000001282
    +B1741024555680N00645558EA0000001281
    +B1741044555682N00645570EA0000001280
    +B1741064555684N00645583EA0000001279
    +B1741084555687N00645595EA0000001280
    +B1741104555689N00645607EA0000001280
    +B1741124555690N00645620EA0000001280
    +B1741144555693N00645634EA0000001281
    +B1741164555695N00645647EA0000001282
    +B1741184555697N00645661EA0000001281
    +B1741204555701N00645683EA0000001281
    +B1741224555703N00645697EA0000001281
    +B1741244555704N00645711EA0000001280
    +B1741264555706N00645724EA0000001279
    +B1741284555709N00645739EA0000001277
    +B1741304555713N00645753EA0000001276
    +B1741324555716N00645766EA0000001275
    +B1741344555719N00645779EA0000001273
    +B1741364555724N00645792EA0000001271
    +B1741384555727N00645806EA0000001270
    +B1741404555729N00645820EA0000001268
    +B1741424555729N00645835EA0000001267
    +B1741444555731N00645849EA0000001266
    +B1741464555732N00645864EA0000001266
    +B1741494555733N00645880EA0000001267
    +B1741514555736N00645895EA0000001268
    +B1741534555738N00645910EA0000001268
    +B1741554555739N00645926EA0000001269
    +B1741574555742N00645940EA0000001270
    +B1741594555745N00645954EA0000001272
    +B1742014555748N00645967EA0000001275
    +B1742034555751N00645977EA0000001277
    +B1742054555752N00645988EA0000001279
    +B1742074555748N00646000EA0000001279
    +B1742094555737N00646007EA0000001279
    +B1742114555724N00645998EA0000001278
    +B1742134555719N00645981EA0000001278
    +B1742154555725N00645970EA0000001279
    +B1742174555736N00645968EA0000001282
    +B1742194555743N00645974EA0000001283
    +B1742214555746N00645987EA0000001284
    +B1742234555749N00646000EA0000001287
    +B1742254555752N00646009EA0000001289
    +B1742274555755N00646020EA0000001291
    +B1742294555758N00646029EA0000001294
    +B1742314555763N00646033EA0000001297
    +B1742334555769N00646033EA0000001299
    +B1742354555773N00646029EA0000001301
    +B1742374555777N00646021EA0000001302
    +B1742394555776N00646008EA0000001303
    +B1742414555771N00645997EA0000001304
    +B1742434555762N00645990EA0000001304
    +B1742454555751N00645990EA0000001304
    +B1742474555742N00645998EA0000001303
    +B1742494555738N00646011EA0000001304
    +B1742514555738N00646023EA0000001307
    +B1742534555739N00646034EA0000001310
    +B1742554555738N00646046EA0000001313
    +B1742574555738N00646057EA0000001316
    +B1742594555741N00646063EA0000001317
    +B1743014555749N00646060EA0000001321
    +B1743034555750N00646047EA0000001324
    +B1743054555746N00646034EA0000001327
    +B1743074555739N00646023EA0000001329
    +B1743094555727N00646017EA0000001330
    +B1743114555714N00646017EA0000001331
    +B1743134555703N00646023EA0000001333
    +B1743154555696N00646034EA0000001335
    +B1743174555693N00646045EA0000001336
    +B1743194555692N00646057EA0000001336
    +B1743214555694N00646068EA0000001335
    +B1743234555700N00646076EA0000001334
    +B1743254555706N00646084EA0000001333
    +B1743284555711N00646093EA0000001333
    +B1743304555715N00646103EA0000001331
    +B1743324555720N00646113EA0000001330
    +B1743344555725N00646122EA0000001329
    +B1743364555729N00646132EA0000001328
    +B1743384555734N00646143EA0000001326
    +B1743404555740N00646154EA0000001324
    +B1743424555745N00646165EA0000001322
    +B1743444555752N00646177EA0000001322
    +B1743464555757N00646185EA0000001322
    +B1743484555761N00646195EA0000001321
    +B1743504555766N00646205EA0000001321
    +B1743524555771N00646215EA0000001321
    +B1743544555773N00646226EA0000001322
    +B1743564555772N00646238EA0000001322
    +B1743584555771N00646251EA0000001321
    +B1744004555772N00646264EA0000001320
    +B1744024555774N00646278EA0000001318
    +B1744044555779N00646291EA0000001317
    +B1744064555786N00646300EA0000001316
    +B1744084555792N00646309EA0000001317
    +B1744104555794N00646320EA0000001318
    +B1744124555791N00646333EA0000001318
    +B1744144555786N00646346EA0000001318
    +B1744164555780N00646358EA0000001317
    +B1744184555773N00646370EA0000001317
    +B1744204555767N00646382EA0000001317
    +B1744224555762N00646394EA0000001317
    +B1744244555757N00646407EA0000001316
    +B1744264555751N00646420EA0000001315
    +B1744284555744N00646432EA0000001315
    +B1744304555738N00646443EA0000001313
    +B1744324555731N00646455EA0000001311
    +B1744344555724N00646467EA0000001310
    +B1744364555718N00646480EA0000001310
    +B1744384555711N00646490EA0000001308
    +B1744404555700N00646505EA0000001306
    +B1744424555695N00646518EA0000001304
    +B1744444555690N00646532EA0000001302
    +B1744464555687N00646548EA0000001300
    +B1744484555685N00646563EA0000001297
    +B1744504555683N00646578EA0000001294
    +B1744524555681N00646592EA0000001290
    +B1744544555681N00646606EA0000001287
    +B1744564555680N00646621EA0000001285
    +B1744584555679N00646635EA0000001283
    +B1745004555676N00646648EA0000001281
    +B1745024555672N00646661EA0000001280
    +B1745044555667N00646672EA0000001278
    +B1745074555662N00646683EA0000001277
    +B1745094555656N00646693EA0000001274
    +B1745114555649N00646704EA0000001271
    +B1745134555642N00646714EA0000001270
    +B1745154555635N00646723EA0000001267
    +B1745174555627N00646731EA0000001265
    +B1745194555617N00646737EA0000001262
    +B1745214555608N00646740EA0000001260
    +B1745234555600N00646741EA0000001257
    +B1745254555592N00646740EA0000001254
    +B1745274555583N00646738EA0000001251
    +B1745294555575N00646736EA0000001249
    +B1745314555567N00646734EA0000001248
    +B1745334555560N00646730EA0000001247
    +B1745354555552N00646727EA0000001246
    +B1745374555546N00646722EA0000001245
    +B1745394555540N00646717EA0000001244
    +B1745414555533N00646713EA0000001243
    +B1745434555526N00646710EA0000001242
    +B1745454555520N00646707EA0000001241
    +B1745474555512N00646704EA0000001239
    +B1745494555505N00646700EA0000001237
    +B1745514555498N00646695EA0000001235
    +B1745534555491N00646691EA0000001232
    +B1745554555484N00646688EA0000001230
    +B1745574555477N00646684EA0000001227
    +B1745594555469N00646680EA0000001225
    +B1746014555464N00646677EA0000001224
    +B1746034555458N00646672EA0000001222
    +B1746054555450N00646668EA0000001221
    +B1746074555444N00646665EA0000001220
    +B1746094555438N00646662EA0000001219
    +B1746114555431N00646659EA0000001218
    +B1746134555424N00646658EA0000001218
    +B1746154555414N00646658EA0000001216
    +B1746174555407N00646655EA0000001215
    +B1746194555401N00646653EA0000001216
    +B1746214555395N00646651EA0000001215
    +B1746234555389N00646649EA0000001213
    +B1746254555383N00646648EA0000001212
    +B1746274555375N00646648EA0000001211
    +B1746294555368N00646645EA0000001211
    +B1746314555363N00646643EA0000001212
    +B1746334555358N00646642EA0000001211
    +B1746354555351N00646641EA0000001211
    +B1746374555345N00646639EA0000001210
    +B1746394555340N00646636EA0000001211
    +B1746414555335N00646636EA0000001212
    +B1746444555330N00646636EA0000001212
    +B1746464555325N00646636EA0000001213
    +B1746484555320N00646636EA0000001213
    +B1746504555313N00646636EA0000001214
    +B1746524555308N00646636EA0000001214
    +B1746544555302N00646637EA0000001215
    +B1746564555296N00646639EA0000001215
    +B1746584555291N00646641EA0000001216
    +B1747004555285N00646640EA0000001216
    +B1747024555279N00646637EA0000001215
    +B1747044555273N00646635EA0000001216
    +B1747064555268N00646635EA0000001217
    +B1747084555261N00646634EA0000001218
    +B1747104555255N00646634EA0000001219
    +B1747124555249N00646635EA0000001220
    +B1747144555242N00646635EA0000001221
    +B1747164555234N00646635EA0000001221
    +B1747184555228N00646636EA0000001222
    +B1747204555223N00646638EA0000001223
    +B1747224555216N00646638EA0000001224
    +B1747244555208N00646639EA0000001226
    +B1747264555202N00646638EA0000001227
    +B1747284555194N00646637EA0000001228
    +B1747304555186N00646639EA0000001230
    +B1747324555179N00646642EA0000001232
    +B1747344555172N00646646EA0000001234
    +B1747364555165N00646645EA0000001236
    +B1747384555162N00646637EA0000001238
    +B1747404555166N00646627EA0000001240
    +B1747424555176N00646622EA0000001242
    +B1747444555188N00646625EA0000001243
    +B1747464555199N00646637EA0000001244
    +B1747484555196N00646656EA0000001244
    +B1747504555193N00646663EA0000001245
    +B1747524555181N00646672EA0000001245
    +B1747544555174N00646671EA0000001246
    +B1747564555166N00646676EA0000001250
    +B1747584555161N00646678EA0000001253
    +B1748004555156N00646676EA0000001256
    +B1748024555154N00646668EA0000001258
    +B1748044555161N00646660EA0000001260
    +B1748064555173N00646662EA0000001261
    +B1748084555178N00646677EA0000001263
    +B1748104555172N00646693EA0000001265
    +B1748124555164N00646701EA0000001267
    +B1748144555159N00646705EA0000001271
    +B1748164555155N00646709EA0000001274
    +B1748184555149N00646710EA0000001276
    +B1748204555146N00646705EA0000001277
    +B1748224555148N00646696EA0000001277
    +B1748254555159N00646689EA0000001277
    +B1748274555172N00646693EA0000001277
    +B1748294555180N00646708EA0000001278
    +B1748314555179N00646725EA0000001279
    +B1748334555173N00646735EA0000001280
    +B1748354555166N00646739EA0000001283
    +B1748374555160N00646739EA0000001284
    +B1748394555154N00646737EA0000001286
    +B1748414555148N00646738EA0000001290
    +B1748434555143N00646742EA0000001293
    +B1748454555138N00646740EA0000001296
    +B1748474555135N00646731EA0000001298
    +B1748494555140N00646722EA0000001300
    +B1748514555151N00646720EA0000001301
    +B1748534555156N00646731EA0000001303
    +B1748554555153N00646746EA0000001304
    +B1748574555145N00646754EA0000001307
    +B1748594555139N00646759EA0000001310
    +B1749014555133N00646762EA0000001313
    +B1749034555125N00646765EA0000001314
    +B1749054555120N00646762EA0000001316
    +B1749074555121N00646753EA0000001317
    +B1749094555131N00646747EA0000001318
    +B1749114555145N00646750EA0000001319
    +B1749134555156N00646761EA0000001321
    +B1749154555166N00646768EA0000001323
    +B1749174555176N00646766EA0000001325
    +B1749194555183N00646758EA0000001327
    +B1749214555182N00646749EA0000001328
    +B1749234555176N00646744EA0000001329
    +B1749254555168N00646748EA0000001330
    +B1749274555160N00646753EA0000001331
    +B1749294555151N00646758EA0000001331
    +B1749314555141N00646763EA0000001332
    +B1749334555133N00646769EA0000001334
    +B1749354555124N00646775EA0000001339
    +B1749374555116N00646779EA0000001341
    +B1749394555109N00646778EA0000001342
    +B1749414555104N00646771EA0000001343
    +B1749434555107N00646761EA0000001343
    +B1749454555118N00646756EA0000001342
    +B1749474555128N00646764EA0000001341
    +B1749494555128N00646783EA0000001341
    +B1749514555121N00646796EA0000001342
    +B1749534555115N00646798EA0000001343
    +B1749554555112N00646794EA0000001343
    +B1749574555111N00646787EA0000001342
    +B1749594555115N00646776EA0000001340
    +B1750024555125N00646769EA0000001339
    +B1750044555135N00646773EA0000001337
    +B1750064555133N00646790EA0000001335
    +B1750084555124N00646800EA0000001334
    +B1750104555119N00646805EA0000001335
    +B1750124555114N00646808EA0000001336
    +B1750144555106N00646808EA0000001336
    +B1750164555097N00646808EA0000001337
    +B1750184555087N00646808EA0000001338
    +B1750204555078N00646808EA0000001339
    +B1750224555068N00646808EA0000001341
    +B1750244555059N00646808EA0000001344
    +B1750264555050N00646809EA0000001347
    +B1750284555043N00646811EA0000001351
    +B1750304555036N00646808EA0000001354
    +B1750324555033N00646798EA0000001356
    +B1750344555037N00646787EA0000001358
    +B1750364555046N00646784EA0000001359
    +B1750384555050N00646796EA0000001360
    +B1750404555044N00646812EA0000001361
    +B1750424555034N00646820EA0000001362
    +B1750444555027N00646823EA0000001363
    +B1750464555019N00646824EA0000001364
    +B1750484555009N00646823EA0000001364
    +B1750504554999N00646823EA0000001364
    +B1750524554989N00646823EA0000001365
    +B1750544554978N00646824EA0000001366
    +B1750564554968N00646825EA0000001368
    +B1750584554958N00646828EA0000001369
    +B1751004554948N00646829EA0000001371
    +B1751024554942N00646822EA0000001372
    +B1751044554943N00646811EA0000001372
    +B1751064554951N00646807EA0000001373
    +B1751084554962N00646816EA0000001374
    +B1751104554977N00646828EA0000001376
    +B1751124554986N00646836EA0000001376
    +B1751144554995N00646841EA0000001376
    +B1751164555005N00646843EA0000001376
    +B1751184555014N00646846EA0000001376
    +B1751204555024N00646848EA0000001376
    +B1751224555033N00646848EA0000001376
    +B1751244555040N00646842EA0000001376
    +B1751264555041N00646832EA0000001376
    +B1751284555037N00646823EA0000001375
    +B1751304555028N00646819EA0000001374
    +B1751324555017N00646817EA0000001374
    +B1751344555008N00646819EA0000001375
    +B1751374554999N00646819EA0000001376
    +B1751394554989N00646819EA0000001376
    +B1751414554979N00646819EA0000001376
    +B1751434554968N00646820EA0000001376
    +B1751454554956N00646821EA0000001375
    +B1751474554945N00646823EA0000001376
    +B1751494554936N00646825EA0000001378
    +B1751514554927N00646826EA0000001379
    +B1751534554917N00646828EA0000001380
    +B1751554554906N00646829EA0000001379
    +B1751574554894N00646830EA0000001379
    +B1751594554883N00646830EA0000001380
    +B1752014554873N00646830EA0000001380
    +B1752034554862N00646831EA0000001380
    +B1752054554851N00646833EA0000001380
    +B1752074554840N00646833EA0000001380
    +B1752094554830N00646834EA0000001380
    +B1752114554818N00646835EA0000001380
    +B1752134554807N00646836EA0000001380
    +B1752154554796N00646836EA0000001380
    +B1752174554784N00646837EA0000001379
    +B1752194554773N00646838EA0000001379
    +B1752214554761N00646839EA0000001378
    +B1752234554749N00646841EA0000001376
    +B1752254554737N00646843EA0000001375
    +B1752274554727N00646845EA0000001374
    +B1752294554715N00646845EA0000001373
    +B1752314554701N00646845EA0000001371
    +B1752334554689N00646847EA0000001370
    +B1752354554678N00646849EA0000001370
    +B1752374554666N00646851EA0000001369
    +B1752394554653N00646856EA0000001368
    +B1752414554643N00646858EA0000001368
    +B1752434554632N00646859EA0000001368
    +B1752454554621N00646862EA0000001367
    +B1752474554605N00646866EA0000001367
    +B1752494554595N00646869EA0000001366
    +B1752514554584N00646872EA0000001365
    +B1752534554573N00646875EA0000001365
    +B1752554554563N00646877EA0000001365
    +B1752574554554N00646878EA0000001366
    +B1752594554544N00646881EA0000001366
    +B1753014554534N00646886EA0000001366
    +B1753034554524N00646889EA0000001366
    +B1753054554513N00646893EA0000001365
    +B1753074554502N00646897EA0000001365
    +B1753094554491N00646901EA0000001365
    +B1753114554481N00646905EA0000001365
    +B1753134554471N00646909EA0000001364
    +B1753154554460N00646912EA0000001364
    +B1753184554449N00646917EA0000001363
    +B1753204554438N00646922EA0000001362
    +B1753224554428N00646926EA0000001361
    +B1753244554418N00646931EA0000001359
    +B1753264554406N00646937EA0000001357
    +B1753284554395N00646943EA0000001357
    +B1753304554385N00646949EA0000001357
    +B1753324554375N00646955EA0000001356
    +B1753344554365N00646961EA0000001354
    +B1753364554354N00646969EA0000001352
    +B1753384554343N00646977EA0000001350
    +B1753404554331N00646984EA0000001349
    +B1753424554319N00646991EA0000001349
    +B1753444554310N00646997EA0000001349
    +B1753464554302N00647002EA0000001348
    +B1753484554293N00647008EA0000001346
    +B1753504554281N00647014EA0000001344
    +B1753524554272N00647020EA0000001344
    +B1753544554263N00647026EA0000001342
    +B1753564554253N00647032EA0000001340
    +B1753584554242N00647039EA0000001338
    +B1754004554231N00647046EA0000001336
    +B1754024554221N00647051EA0000001334
    +B1754044554209N00647055EA0000001332
    +B1754064554199N00647062EA0000001330
    +B1754084554189N00647071EA0000001327
    +B1754104554180N00647081EA0000001326
    +B1754124554171N00647091EA0000001324
    +B1754144554163N00647102EA0000001322
    +B1754164554154N00647112EA0000001320
    +B1754184554144N00647121EA0000001319
    +B1754204554135N00647131EA0000001319
    +B1754224554127N00647140EA0000001319
    +B1754244554119N00647150EA0000001319
    +B1754264554111N00647161EA0000001320
    +B1754284554103N00647173EA0000001323
    +B1754304554098N00647181EA0000001324
    +B1754324554094N00647192EA0000001326
    +B1754344554088N00647200EA0000001326
    +B1754364554082N00647206EA0000001326
    +B1754384554075N00647214EA0000001325
    +B1754404554069N00647225EA0000001323
    +B1754424554062N00647237EA0000001321
    +B1754444554056N00647250EA0000001319
    +B1754464554050N00647263EA0000001318
    +B1754484554044N00647276EA0000001316
    +B1754504554038N00647290EA0000001314
    +B1754524554030N00647303EA0000001312
    +B1754554554023N00647316EA0000001311
    +B1754574554015N00647329EA0000001310
    +B1754594554009N00647343EA0000001309
    +B1755014554003N00647358EA0000001308
    +B1755034553996N00647373EA0000001307
    +B1755054553990N00647389EA0000001305
    +B1755074553983N00647404EA0000001304
    +B1755094553976N00647417EA0000001303
    +B1755114553969N00647429EA0000001303
    +B1755134553962N00647441EA0000001303
    +B1755154553956N00647453EA0000001303
    +B1755174553951N00647464EA0000001302
    +B1755194553944N00647475EA0000001301
    +B1755214553936N00647485EA0000001299
    +B1755234553930N00647493EA0000001299
    +B1755254553926N00647500EA0000001300
    +B1755274553921N00647507EA0000001302
    +B1755294553915N00647514EA0000001304
    +B1755314553911N00647522EA0000001306
    +B1755334553906N00647527EA0000001308
    +B1755354553901N00647531EA0000001311
    +B1755374553897N00647528EA0000001313
    +B1755394553894N00647519EA0000001315
    +B1755414553896N00647508EA0000001316
    +B1755434553905N00647496EA0000001316
    +B1755454553920N00647492EA0000001316
    +B1755474553937N00647499EA0000001317
    +B1755494553945N00647513EA0000001318
    +B1755514553946N00647530EA0000001320
    +B1755534553946N00647546EA0000001323
    +B1755554553944N00647558EA0000001325
    +B1755574553938N00647564EA0000001326
    +B1755594553933N00647563EA0000001327
    +B1756014553932N00647554EA0000001328
    +B1756034553939N00647543EA0000001329
    +B1756054553953N00647540EA0000001329
    +B1756074553975N00647554EA0000001330
    +B1756094553978N00647572EA0000001331
    +B1756114553977N00647589EA0000001332
    +B1756134553976N00647605EA0000001333
    +B1756154553971N00647618EA0000001333
    +B1756174553964N00647628EA0000001334
    +B1756194553959N00647639EA0000001335
    +B1756214553956N00647650EA0000001336
    +B1756234553951N00647661EA0000001337
    +B1756254553947N00647676EA0000001337
    +B1756274553945N00647692EA0000001338
    +B1756294553945N00647706EA0000001339
    +B1756314553944N00647723EA0000001340
    +B1756334553942N00647742EA0000001339
    +B1756364553939N00647758EA0000001339
    +B1756384553937N00647774EA0000001339
    +B1756404553935N00647791EA0000001337
    +B1756424553932N00647809EA0000001336
    +B1756444553930N00647823EA0000001334
    +B1756464553926N00647838EA0000001331
    +B1756484553921N00647853EA0000001328
    +B1756504553916N00647867EA0000001326
    +B1756524553911N00647881EA0000001323
    +B1756544553907N00647898EA0000001319
    +B1756564553903N00647915EA0000001317
    +B1756584553899N00647930EA0000001314
    +B1757004553896N00647947EA0000001311
    +B1757024553893N00647965EA0000001307
    +B1757044553888N00647981EA0000001305
    +B1757064553885N00647998EA0000001301
    +B1757084553880N00648015EA0000001298
    +B1757104553874N00648029EA0000001294
    +B1757124553869N00648045EA0000001290
    +B1757144553865N00648060EA0000001286
    +B1757164553860N00648077EA0000001282
    +B1757184553856N00648095EA0000001278
    +B1757204553852N00648111EA0000001274
    +B1757224553845N00648127EA0000001269
    +B1757244553839N00648142EA0000001266
    +B1757264553833N00648155EA0000001262
    +B1757284553827N00648169EA0000001258
    +B1757304553822N00648187EA0000001255
    +B1757324553819N00648204EA0000001252
    +B1757344553817N00648221EA0000001250
    +B1757364553815N00648242EA0000001247
    +B1757384553814N00648264EA0000001244
    +B1757404553814N00648284EA0000001241
    +B1757424553814N00648304EA0000001237
    +B1757444553812N00648328EA0000001233
    +B1757464553812N00648349EA0000001230
    +B1757484553810N00648381EA0000001224
    +B1757504553808N00648403EA0000001219
    +B1757524553806N00648423EA0000001216
    +B1757544553803N00648441EA0000001212
    +B1757564553799N00648462EA0000001208
    +B1757584553796N00648482EA0000001204
    +B1758004553795N00648501EA0000001201
    +B1758024553793N00648522EA0000001196
    +B1758044553792N00648546EA0000001192
    +B1758064553792N00648568EA0000001190
    +B1758084553791N00648589EA0000001188
    +B1758104553791N00648609EA0000001187
    +B1758124553791N00648630EA0000001186
    +B1758144553789N00648651EA0000001184
    +B1758174553789N00648672EA0000001183
    +B1758194553789N00648692EA0000001181
    +B1758214553790N00648710EA0000001179
    +B1758234553791N00648731EA0000001177
    +B1758254553791N00648752EA0000001175
    +B1758274553793N00648772EA0000001173
    +B1758294553794N00648792EA0000001171
    +B1758314553795N00648812EA0000001169
    +B1758334553796N00648832EA0000001167
    +B1758354553797N00648852EA0000001165
    +B1758374553797N00648852EA0000001163
    +B1758394553794N00648890EA0000001161
    +B1758414553793N00648910EA0000001158
    +B1758434553790N00648928EA0000001156
    +B1758454553787N00648947EA0000001154
    +B1758474553783N00648966EA0000001151
    +B1758494553777N00648983EA0000001148
    +B1758514553773N00649000EA0000001145
    +B1758534553767N00649018EA0000001143
    +B1758554553762N00649034EA0000001141
    +B1758574553756N00649050EA0000001139
    +B1758594553750N00649063EA0000001136
    +B1759014553742N00649074EA0000001134
    +B1759034553736N00649088EA0000001132
    +B1759054553732N00649105EA0000001131
    +B1759074553728N00649121EA0000001130
    +B1759094553722N00649133EA0000001129
    +B1759114553716N00649145EA0000001129
    +B1759134553712N00649159EA0000001128
    +B1759154553705N00649170EA0000001124
    +B1759174553697N00649176EA0000001123
    +B1759194553692N00649185EA0000001121
    +B1759214553687N00649195EA0000001119
    +B1759234553675N00649195EA0000001116
    +B1759254553673N00649183EA0000001112
    +B1759274553682N00649178EA0000001105
    +B1759294553703N00649194EA0000001103
    +B1759314553711N00649204EA0000001099
    +B1759334553723N00649207EA0000001095
    +B1759354553734N00649204EA0000001092
    +B1759374553742N00649199EA0000001090
    +B1759394553752N00649196EA0000001088
    +B1759414553762N00649192EA0000001085
    +B1759434553767N00649185EA0000001083
    +B1759454553765N00649177EA0000001081
    +B1759474553759N00649174EA0000001078
    +B1759494553752N00649177EA0000001076
    +B1759514553744N00649184EA0000001074
    +B1759534553736N00649187EA0000001072
    +B1759554553727N00649186EA0000001069
    +B1759584553718N00649184EA0000001066
    +B1800004553711N00649183EA0000001064
    +B1800024553704N00649180EA0000001063
    +B1800044553697N00649174EA0000001061
    +B1800064553690N00649169EA0000001059
    +B1800084553684N00649167EA0000001057
    +B1800104553680N00649160EA0000001055
    +B1800124553682N00649150EA0000001053
    +B1800144553685N00649142EA0000001051
    +B1800164553687N00649133EA0000001049
    +B1800184553687N00649122EA0000001045
    +B1800204553689N00649108EA0000001042
    +B1800224553691N00649095EA0000001039
    +B1800244553690N00649081EA0000001035
    +B1800264553689N00649067EA0000001031
    +B1800284553689N00649054EA0000001029
    +B1800304553689N00649048EA0000001027
    +B1800324553688N00649046EA0000001026
    +B1800344553689N00649046EA0000001025
    +B1800364553689N00649046EA0000001025
    +B1800384553689N00649046EA0000001025
    +B1800404553689N00649046EA0000001025
    +B1800424553689N00649046EA0000001025
    +B1800444553689N00649046EA0000001025
    +B1800464553689N00649046EA0000001025
    +B1800484553689N00649046EA0000001025
    +B1800504553689N00649046EA0000001025
    +B1800524553689N00649046EA0000001025
    +B1800544553689N00649046EA0000001025
    +B1800564553689N00649046EA0000001025
    +B1800584553689N00649046EA0000001025
    +B1801004553689N00649046EA0000001025
    +B1801024553689N00649046EA0000001025
    +B1801044553689N00649046EA0000001025
    +B1801064553689N00649046EA0000001025
    +B1801084553689N00649046EA0000001025
    +B1801104553689N00649046EA0000001025
    +B1801124553689N00649046EA0000001025
    +B1801144553689N00649046EA0000001025
    +B1801164553689N00649046EA0000001025
    +B1801184553689N00649046EA0000001025
    +B1801204553689N00649046EA0000001025
    +B1801224553689N00649046EA0000001025
    +B1801244553689N00649046EA0000001025
    +B1801264553689N00649046EA0000001025
    +B1801284553689N00649046EA0000001025
    +B1801304553689N00649046EA0000001025
    +B1801324553689N00649046EA0000001025
    +B1801344553689N00649051EA0000001028
    +B1801374553689N00649050EA0000001027
    +B1801394553689N00649049EA0000001027
    +B1801414553689N00649048EA0000001026
    +B1801434553689N00649047EA0000001026
    +B1801454553689N00649046EA0000001025
    +B1801474553689N00649045EA0000001025
    +B1801494553689N00649044EA0000001025
    +B1801514553689N00649042EA0000001025
    +B1801534553689N00649041EA0000001025
    +B1801554553689N00649041EA0000001025
    +B1801574553689N00649041EA0000001025
    +B1801594553689N00649041EA0000001025
    +B1802014553689N00649041EA0000001025
    +B1802034553689N00649041EA0000001025
    +B1802054553689N00649041EA0000001025
    +B1802074553689N00649041EA0000001025
    diff --git a/mapbox-gl/examples/data/igc/Tom-Payne.igc b/mapbox-gl/examples/data/igc/Tom-Payne.igc
    new file mode 100644
    index 0000000000..1e06b053d9
    --- /dev/null
    +++ b/mapbox-gl/examples/data/igc/Tom-Payne.igc
    @@ -0,0 +1,9879 @@
    +AFLY05094
    +HFDTE190411
    +HFFXA100
    +HFPLTPILOT:Tom Payne         
    +HFGTYGLIDERTYPE:Axis Mercury      
    +HFGIDGLIDERID:                  
    +HFDTM100GPSDATUM:WGS84
    +HFGPSGPS:FURUNO GH-80
    +HFRFWFIRMWAREVERSION:1.22
    +HFRHWHARDWAREVERSION:1.00
    +HFFTYFRTYPE:FLYTEC,5020
    +I013638TAS
    +B0848484556256N00651095EA0205102039000
    +B0848514556256N00651095EA0205102039000
    +B0848544556255N00651096EA0205102039000
    +B0848574556255N00651096EA0205002039000
    +B0849004556255N00651096EA0205002039000
    +B0849034556254N00651097EA0205102039000
    +B0849064556254N00651097EA0205002040000
    +B0849094556254N00651097EA0205002040000
    +B0849124556253N00651096EA0205002040000
    +B0849154556254N00651096EA0205002040000
    +B0849184556254N00651096EA0205002041000
    +B0849214556254N00651097EA0205002041000
    +B0849244556254N00651097EA0205002041000
    +B0849274556254N00651097EA0205002042000
    +B0849304556254N00651098EA0205002042000
    +B0849334556253N00651098EA0205002042000
    +B0849364556253N00651097EA0205002043000
    +B0849394556253N00651097EA0205002043000
    +B0849424556254N00651098EA0204902043000
    +B0849454556254N00651097EA0204702043000
    +B0849484556257N00651102EA0204702043000
    +B0849514556263N00651112EA0204602043000
    +B0849544556269N00651124EA0204702043000
    +B0849574556275N00651136EA0204402043000
    +B0850004556285N00651145EA0204302042000
    +B0850034556295N00651154EA0204002042000
    +B0850064556305N00651165EA0203402042000
    +B0850094556318N00651177EA0203202041000
    +B0850124556327N00651189EA0203102040000
    +B0850154556332N00651208EA0202702039000
    +B0850194556336N00651233EA0202402037000
    +B0850224556336N00651249EA0202302036000
    +B0850254556334N00651265EA0201902035000
    +B0850284556327N00651279EA0201602033000
    +B0850314556318N00651293EA0200502031000
    +B0850344556308N00651313EA0201002028000
    +B0850374556302N00651328EA0201702026000
    +B0850404556295N00651338EA0201802024000
    +B0850434556287N00651353EA0203102023000
    +B0850464556281N00651362EA0203502023000
    +B0850494556275N00651368EA0202902023000
    +B0850534556259N00651373EA0203102024000
    +B0850564556243N00651365EA0202502024000
    +B0850594556226N00651359EA0202502023000
    +B0851024556211N00651354EA0202302023000
    +B0851054556194N00651345EA0203302023000
    +B0851084556179N00651335EA0203002023000
    +B0851114556163N00651329EA0202602023000
    +B0851144556146N00651327EA0202702023000
    +B0851174556129N00651326EA0203302023000
    +B0851204556114N00651326EA0203302024000
    +B0851234556099N00651335EA0203502024000
    +B0851264556092N00651350EA0203502025000
    +B0851294556093N00651366EA0204602026000
    +B0851324556094N00651378EA0205202027000
    +B0851354556099N00651389EA0205702029000
    +B0851384556112N00651389EA0206002031000
    +B0851414556124N00651373EA0206602034000
    +B0851444556122N00651347EA0207202037000
    +B0851474556108N00651329EA0207702040000
    +B0851504556091N00651329EA0208202043000
    +B0851534556082N00651343EA0208902047000
    +B0851564556085N00651357EA0209302050000
    +B0851594556094N00651364EA0209502054000
    +B0852024556106N00651357EA0210002058000
    +B0852054556114N00651341EA0210302062000
    +B0852084556115N00651317EA0210702066000
    +B0852114556104N00651295EA0211102070000
    +B0852144556087N00651292EA0211702074000
    +B0852174556074N00651301EA0211902078000
    +B0852214556070N00651323EA0212402083000
    +B0852244556080N00651325EA0212502087000
    +B0852274556093N00651314EA0212602091000
    +B0852304556095N00651291EA0212502095000
    +B0852334556085N00651269EA0212702098000
    +B0852364556066N00651256EA0213302102000
    +B0852394556049N00651261EA0213602105000
    +B0852424556044N00651276EA0213902109000
    +B0852454556050N00651289EA0214502112000
    +B0852484556057N00651297EA0214502115000
    +B0852514556070N00651294EA0214302119000
    +B0852544556079N00651277EA0214302121000
    +B0852574556081N00651252EA0213702124000
    +B0853004556078N00651225EA0213502125000
    +B0853034556062N00651201EA0213002126000
    +B0853064556040N00651195EA0213802126000
    +B0853094556028N00651202EA0213502127000
    +B0853124556025N00651216EA0213402128000
    +B0853154556031N00651226EA0213802128000
    +B0853184556040N00651228EA0213402129000
    +B0853214556052N00651227EA0213402129000
    +B0853244556065N00651223EA0213402129000
    +B0853274556079N00651215EA0213502129000
    +B0853304556094N00651206EA0213802130000
    +B0853334556108N00651196EA0214102130000
    +B0853364556122N00651184EA0214702131000
    +B0853394556132N00651165EA0215402132000
    +B0853424556139N00651143EA0216002134000
    +B0853464556147N00651114EA0217002138000
    +B0853494556149N00651092EA0217602141000
    +B0853524556146N00651068EA0218602144000
    +B0853554556132N00651059EA0219602149000
    +B0853584556123N00651069EA0220702153000
    +B0854014556125N00651082EA0221902157000
    +B0854044556133N00651087EA0222702162000
    +B0854074556146N00651081EA0224102169000
    +B0854104556151N00651063EA0225102177000
    +B0854134556145N00651037EA0226402186000
    +B0854164556133N00651019EA0227502194000
    +B0854194556118N00651012EA0229002204000
    +B0854224556106N00651018EA0229902213000
    +B0854254556106N00651031EA0231302221000
    +B0854284556118N00651027EA0232802231000
    +B0854314556124N00651003EA0234502242000
    +B0854344556124N00650980EA0235902253000
    +B0854374556116N00650959EA0236802265000
    +B0854404556098N00650953EA0237402275000
    +B0854434556085N00650969EA0238602286000
    +B0854464556088N00650984EA0240002297000
    +B0854494556099N00650990EA0241302307000
    +B0854524556112N00650982EA0242602318000
    +B0854554556116N00650967EA0243302330000
    +B0854584556104N00650951EA0243702341000
    +B0855014556084N00650951EA0244802351000
    +B0855044556075N00650967EA0245802361000
    +B0855074556078N00650986EA0247102372000
    +B0855104556086N00651001EA0248602382000
    +B0855134556097N00651003EA0249602394000
    +B0855164556105N00650993EA0250302403000
    +B0855194556101N00650977EA0250202414000
    +B0855224556085N00650965EA0250802424000
    +B0855254556066N00650970EA0252302433000
    +B0855284556055N00650988EA0253302443000
    +B0855314556056N00651009EA0254302453000
    +B0855354556069N00651006EA0254802465000
    +B0855384556070N00650986EA0255602474000
    +B0855414556055N00650973EA0256002482000
    +B0855444556037N00650974EA0257302491000
    +B0855474556026N00650991EA0258102499000
    +B0855504556030N00651009EA0259002507000
    +B0855534556037N00651011EA0259302513000
    +B0855564556042N00650997EA0259202521000
    +B0855594556037N00650977EA0259702529000
    +B0856024556026N00650960EA0260402536000
    +B0856054556012N00650942EA0261102544000
    +B0856094555998N00650917EA0261802553000
    +B0856124555986N00650899EA0262002560000
    +B0856154555969N00650897EA0261702566000
    +B0856184555952N00650910EA0261802572000
    +B0856214555944N00650934EA0262302577000
    +B0856244555950N00650949EA0262802582000
    +B0856274555962N00650952EA0263102586000
    +B0856304555972N00650939EA0263702591000
    +B0856334555974N00650917EA0264502597000
    +B0856364555970N00650896EA0264502602000
    +B0856394555959N00650878EA0264402607000
    +B0856424555944N00650862EA0264402611000
    +B0856454555928N00650847EA0264102614000
    +B0856484555917N00650829EA0264002617000
    +B0856514555908N00650807EA0263702619000
    +B0856544555897N00650784EA0263702621000
    +B0856574555885N00650766EA0263502622000
    +B0857004555872N00650750EA0263202623000
    +B0857034555859N00650733EA0263102624000
    +B0857064555842N00650721EA0263102625000
    +B0857094555829N00650709EA0262302625000
    +B0857124555816N00650690EA0261202624000
    +B0857154555804N00650668EA0260502623000
    +B0857184555791N00650647EA0259402621000
    +B0857214555778N00650620EA0258402617000
    +B0857244555766N00650594EA0258002614000
    +B0857274555752N00650572EA0256902610000
    +B0857304555736N00650548EA0256202605000
    +B0857334555720N00650521EA0255502601000
    +B0857374555701N00650486EA0255002595000
    +B0857404555687N00650459EA0254202590000
    +B0857434555673N00650431EA0253802585000
    +B0857464555659N00650403EA0253202579000
    +B0857494555646N00650373EA0252802574000
    +B0857524555634N00650344EA0252302569000
    +B0857554555622N00650314EA0251602564000
    +B0857584555610N00650284EA0251502558000
    +B0858014555599N00650255EA0251302553000
    +B0858044555586N00650224EA0251302549000
    +B0858074555573N00650192EA0252202545000
    +B0858104555563N00650165EA0252302543000
    +B0858134555548N00650140EA0252502541000
    +B0858164555536N00650114EA0252702539000
    +B0858194555532N00650087EA0252502537000
    +B0858224555527N00650056EA0251902536000
    +B0858254555521N00650019EA0251902534000
    +B0858284555515N00649983EA0251602532000
    +B0858314555509N00649947EA0251702530000
    +B0858344555505N00649913EA0251802528000
    +B0858374555502N00649876EA0252702526000
    +B0858404555501N00649842EA0253602526000
    +B0858434555499N00649809EA0254202526000
    +B0858464555491N00649782EA0254802527000
    +B0858494555479N00649765EA0255202529000
    +B0858524555465N00649768EA0256002531000
    +B0858554555463N00649780EA0256502533000
    +B0858584555473N00649788EA0256902537000
    +B0859024555492N00649777EA0257202540000
    +B0859054555503N00649752EA0258102544000
    +B0859084555509N00649728EA0258702548000
    +B0859114555511N00649700EA0259402553000
    +B0859144555508N00649673EA0260202557000
    +B0859174555507N00649645EA0260402562000
    +B0859204555496N00649622EA0260602567000
    +B0859234555482N00649607EA0261602572000
    +B0859264555467N00649597EA0262002577000
    +B0859294555453N00649601EA0262002581000
    +B0859324555450N00649615EA0262602585000
    +B0859354555461N00649621EA0262602590000
    +B0859384555477N00649605EA0262602593000
    +B0859414555479N00649577EA0263402597000
    +B0859444555474N00649554EA0263802601000
    +B0859474555466N00649529EA0264202606000
    +B0859504555456N00649504EA0264302610000
    +B0859534555444N00649480EA0264602613000
    +B0859564555431N00649459EA0264802617000
    +B0859594555420N00649439EA0264202620000
    +B0900024555408N00649417EA0263502622000
    +B0900054555394N00649395EA0262802623000
    +B0900084555379N00649368EA0262302623000
    +B0900114555366N00649339EA0262202623000
    +B0900144555356N00649312EA0261502622000
    +B0900174555345N00649283EA0260502621000
    +B0900204555330N00649251EA0260502618000
    +B0900234555316N00649222EA0260602617000
    +B0900264555305N00649192EA0260502615000
    +B0900304555291N00649151EA0260402613000
    +B0900334555279N00649121EA0260102611000
    +B0900364555267N00649090EA0259802609000
    +B0900394555256N00649059EA0259602608000
    +B0900424555246N00649026EA0259102605000
    +B0900454555234N00648994EA0259002603000
    +B0900484555224N00648967EA0260402601000
    +B0900514555220N00648953EA0261202602000
    +B0900544555215N00648930EA0262202602000
    +B0900574555212N00648909EA0263302605000
    +B0901004555208N00648889EA0263602607000
    +B0901034555203N00648864EA0264702611000
    +B0901064555198N00648841EA0265302615000
    +B0901094555190N00648820EA0265602619000
    +B0901124555185N00648802EA0265502623000
    +B0901154555185N00648779EA0266102626000
    +B0901184555184N00648759EA0266402630000
    +B0901214555182N00648737EA0266602634000
    +B0901244555184N00648711EA0267502637000
    +B0901274555189N00648686EA0268302641000
    +B0901304555198N00648661EA0269002646000
    +B0901334555198N00648633EA0269402650000
    +B0901364555186N00648618EA0270002655000
    +B0901394555172N00648624EA0270502660000
    +B0901424555169N00648641EA0270902664000
    +B0901454555177N00648656EA0271102668000
    +B0901484555193N00648658EA0271402672000
    +B0901514555204N00648642EA0272102677000
    +B0901544555206N00648619EA0272902682000
    +B0901574555204N00648598EA0273202686000
    +B0902004555198N00648577EA0274002691000
    +B0902034555189N00648565EA0274002697000
    +B0902074555174N00648573EA0274402703000
    +B0902104555175N00648593EA0275102707000
    +B0902134555184N00648605EA0275402712000
    +B0902164555200N00648604EA0275802716000
    +B0902194555214N00648592EA0276302720000
    +B0902224555224N00648571EA0276502725000
    +B0902254555226N00648545EA0277002729000
    +B0902284555224N00648522EA0277402733000
    +B0902314555220N00648500EA0277802737000
    +B0902344555213N00648476EA0278302742000
    +B0902374555207N00648454EA0278802747000
    +B0902404555199N00648435EA0279002751000
    +B0902434555190N00648416EA0279402756000
    +B0902464555182N00648395EA0279702760000
    +B0902494555175N00648374EA0279702764000
    +B0902524555167N00648354EA0280002768000
    +B0902554555159N00648337EA0279702771000
    +B0902584555148N00648321EA0279402774000
    +B0903014555135N00648304EA0279402776000
    +B0903044555123N00648289EA0278902778000
    +B0903074555108N00648271EA0278102779000
    +B0903104555091N00648249EA0278002779000
    +B0903134555078N00648227EA0277602778000
    +B0903164555066N00648205EA0277002778000
    +B0903194555053N00648184EA0276302776000
    +B0903224555036N00648164EA0275402774000
    +B0903254555021N00648137EA0275102771000
    +B0903284555007N00648110EA0274302768000
    +B0903314554988N00648084EA0272902764000
    +B0903354554964N00648049EA0272402758000
    +B0903384554949N00648022EA0271302752000
    +B0903414554933N00647995EA0270902747000
    +B0903444554918N00647971EA0270302741000
    +B0903474554900N00647941EA0269002735000
    +B0903504554877N00647910EA0268302729000
    +B0903534554855N00647880EA0267802723000
    +B0903564554834N00647849EA0267202716000
    +B0903594554812N00647817EA0266302710000
    +B0904024554792N00647782EA0265402703000
    +B0904054554770N00647745EA0265102697000
    +B0904084554748N00647712EA0265002691000
    +B0904114554726N00647682EA0264002685000
    +B0904144554702N00647650EA0263402679000
    +B0904174554681N00647619EA0263302673000
    +B0904204554666N00647585EA0262502667000
    +B0904234554651N00647547EA0261802661000
    +B0904264554635N00647509EA0261602655000
    +B0904294554620N00647475EA0260902649000
    +B0904324554605N00647439EA0260202643000
    +B0904354554589N00647402EA0259702637000
    +B0904384554573N00647365EA0259002631000
    +B0904414554558N00647326EA0258402624000
    +B0904444554543N00647289EA0257802618000
    +B0904474554527N00647253EA0257302612000
    +B0904504554513N00647218EA0256702606000
    +B0904534554499N00647185EA0255702600000
    +B0904574554480N00647137EA0255102592000
    +B0905004554466N00647104EA0254502586000
    +B0905034554452N00647072EA0253802579000
    +B0905064554438N00647039EA0253302573000
    +B0905094554425N00647005EA0253102567000
    +B0905124554412N00646971EA0253002562000
    +B0905154554397N00646941EA0253002557000
    +B0905184554383N00646915EA0252302553000
    +B0905214554368N00646884EA0251202548000
    +B0905244554355N00646851EA0251002543000
    +B0905274554342N00646821EA0250302538000
    +B0905304554328N00646789EA0249702533000
    +B0905334554314N00646757EA0249102528000
    +B0905364554301N00646724EA0248402523000
    +B0905394554287N00646692EA0247702518000
    +B0905424554273N00646658EA0247002512000
    +B0905454554260N00646623EA0246602506000
    +B0905484554246N00646589EA0246302501000
    +B0905514554235N00646556EA0246002496000
    +B0905544554223N00646524EA0245302490000
    +B0905574554208N00646490EA0244702485000
    +B0906004554192N00646458EA0244202480000
    +B0906034554178N00646425EA0243802474000
    +B0906064554164N00646393EA0243202469000
    +B0906094554150N00646359EA0242902464000
    +B0906124554136N00646326EA0242102459000
    +B0906164554117N00646283EA0241402451000
    +B0906194554103N00646250EA0241002446000
    +B0906224554091N00646216EA0240402440000
    +B0906254554076N00646184EA0239902435000
    +B0906284554061N00646152EA0239202429000
    +B0906314554045N00646121EA0238602424000
    +B0906344554031N00646089EA0238402419000
    +B0906374554015N00646059EA0237702413000
    +B0906404554000N00646028EA0237102408000
    +B0906434553985N00645996EA0236702403000
    +B0906464553971N00645963EA0236102397000
    +B0906494553958N00645930EA0235602392000
    +B0906524553943N00645899EA0235002387000
    +B0906554553928N00645867EA0234502382000
    +B0906584553912N00645835EA0234002377000
    +B0907014553897N00645803EA0233402372000
    +B0907044553883N00645770EA0233002367000
    +B0907074553870N00645737EA0232302362000
    +B0907104553856N00645703EA0231702356000
    +B0907134553843N00645668EA0231402351000
    +B0907164553832N00645635EA0231002346000
    +B0907194553823N00645602EA0230502341000
    +B0907224553812N00645568EA0230002336000
    +B0907254553801N00645535EA0229502331000
    +B0907284553790N00645503EA0229202325000
    +B0907314553779N00645473EA0228602320000
    +B0907354553762N00645431EA0228102313000
    +B0907384553748N00645402EA0227602308000
    +B0907414553733N00645373EA0227002303000
    +B0907444553718N00645345EA0226502298000
    +B0907474553705N00645314EA0225802292000
    +B0907504553691N00645284EA0225102287000
    +B0907534553675N00645257EA0224302281000
    +B0907564553655N00645234EA0223402275000
    +B0907594553633N00645213EA0222702268000
    +B0908024553611N00645197EA0222102262000
    +B0908054553587N00645182EA0221502256000
    +B0908084553566N00645165EA0220902250000
    +B0908114553543N00645148EA0220302244000
    +B0908144553519N00645135EA0219902239000
    +B0908174553495N00645125EA0219502233000
    +B0908204553469N00645117EA0218902227000
    +B0908234553444N00645109EA0218602221000
    +B0908264553421N00645103EA0218102215000
    +B0908294553396N00645098EA0217502209000
    +B0908324553371N00645093EA0216702204000
    +B0908354553348N00645087EA0216602199000
    +B0908384553326N00645081EA0215802194000
    +B0908414553301N00645075EA0215002188000
    +B0908444553275N00645065EA0214702183000
    +B0908474553250N00645053EA0214902177000
    +B0908514553227N00645039EA0215402172000
    +B0908544553213N00645033EA0215802169000
    +B0908574553196N00645032EA0216202167000
    +B0909004553178N00645030EA0216202166000
    +B0909034553158N00645025EA0216802165000
    +B0909064553140N00645015EA0217002164000
    +B0909094553121N00645002EA0217102165000
    +B0909124553104N00644984EA0217402165000
    +B0909154553089N00644965EA0217102165000
    +B0909184553075N00644950EA0216502165000
    +B0909214553060N00644938EA0215802164000
    +B0909244553045N00644936EA0215302162000
    +B0909274553042N00644952EA0215502161000
    +B0909304553044N00644966EA0215202160000
    +B0909334553048N00644984EA0215002158000
    +B0909364553051N00645001EA0215502157000
    +B0909394553055N00645017EA0215602156000
    +B0909424553058N00645032EA0216202156000
    +B0909454553060N00645048EA0216302157000
    +B0909484553062N00645066EA0216002157000
    +B0909514553072N00645084EA0215802157000
    +B0909544553081N00645098EA0215202156000
    +B0909574553092N00645114EA0215602156000
    +B0910004553096N00645125EA0215102155000
    +B0910034553104N00645134EA0214502154000
    +B0910064553114N00645146EA0214902153000
    +B0910094553120N00645154EA0214802152000
    +B0910134553131N00645163EA0214302151000
    +B0910164553142N00645167EA0214402150000
    +B0910194553153N00645173EA0213902149000
    +B0910224553167N00645178EA0214302148000
    +B0910254553178N00645179EA0214502147000
    +B0910284553189N00645181EA0215002147000
    +B0910314553200N00645182EA0215402147000
    +B0910344553211N00645183EA0215702148000
    +B0910374553223N00645183EA0216502149000
    +B0910404553232N00645185EA0216702151000
    +B0910434553240N00645201EA0216802153000
    +B0910464553237N00645222EA0216802155000
    +B0910494553226N00645235EA0215802155000
    +B0910524553208N00645234EA0214702155000
    +B0910554553198N00645214EA0213802153000
    +B0910584553203N00645190EA0213602151000
    +B0911014553215N00645174EA0214402149000
    +B0911044553227N00645165EA0215202147000
    +B0911074553239N00645156EA0215802147000
    +B0911104553248N00645136EA0216502147000
    +B0911134553240N00645110EA0217202148000
    +B0911164553221N00645104EA0218302149000
    +B0911194553210N00645118EA0218802151000
    +B0911224553218N00645130EA0219502154000
    +B0911254553228N00645119EA0220802158000
    +B0911284553230N00645096EA0221302162000
    +B0911314553215N00645075EA0222302166000
    +B0911344553197N00645073EA0223102172000
    +B0911384553186N00645096EA0223202179000
    +B0911414553194N00645117EA0223602184000
    +B0911444553209N00645115EA0224402189000
    +B0911474553214N00645095EA0224902195000
    +B0911504553205N00645072EA0225202200000
    +B0911534553186N00645071EA0226302205000
    +B0911564553175N00645090EA0227002211000
    +B0911594553179N00645111EA0227102216000
    +B0912024553194N00645117EA0227202221000
    +B0912054553204N00645104EA0227902227000
    +B0912084553203N00645080EA0228102232000
    +B0912114553192N00645059EA0228502237000
    +B0912144553175N00645057EA0228802242000
    +B0912174553164N00645074EA0229002247000
    +B0912204553166N00645098EA0229102251000
    +B0912234553179N00645114EA0229602255000
    +B0912264553193N00645110EA0230302259000
    +B0912294553203N00645099EA0231002264000
    +B0912324553210N00645089EA0231302270000
    +B0912354553213N00645067EA0231302274000
    +B0912384553204N00645040EA0232402279000
    +B0912414553189N00645026EA0232902285000
    +B0912444553174N00645031EA0233102290000
    +B0912474553165N00645050EA0233202294000
    +B0912504553172N00645067EA0233102298000
    +B0912534553188N00645075EA0234002301000
    +B0912564553199N00645071EA0234202305000
    +B0912594553210N00645055EA0234402309000
    +B0913024553214N00645030EA0235102312000
    +B0913054553205N00645005EA0235702316000
    +B0913084553190N00644991EA0236202321000
    +B0913114553177N00644998EA0236702325000
    +B0913144553176N00645021EA0237202330000
    +B0913184553191N00645035EA0237502336000
    +B0913214553207N00645027EA0238202340000
    +B0913244553219N00645017EA0238702345000
    +B0913274553227N00644995EA0238902349000
    +B0913304553224N00644965EA0239902353000
    +B0913334553212N00644947EA0241002358000
    +B0913364553197N00644949EA0241702363000
    +B0913394553192N00644966EA0242202368000
    +B0913424553201N00644972EA0242802373000
    +B0913454553213N00644960EA0243102378000
    +B0913484553215N00644933EA0243702383000
    +B0913514553204N00644916EA0244902388000
    +B0913544553192N00644920EA0245602394000
    +B0913574553194N00644933EA0246002399000
    +B0914004553207N00644929EA0246302405000
    +B0914034553213N00644904EA0246802410000
    +B0914064553206N00644880EA0247602416000
    +B0914094553193N00644876EA0247802422000
    +B0914124553188N00644892EA0248902427000
    +B0914154553197N00644899EA0249202433000
    +B0914184553208N00644881EA0249502439000
    +B0914214553203N00644854EA0250002443000
    +B0914244553191N00644848EA0250102448000
    +B0914274553180N00644859EA0250602452000
    +B0914304553179N00644875EA0251302457000
    +B0914334553190N00644877EA0251402462000
    +B0914364553201N00644857EA0252002467000
    +B0914394553198N00644829EA0252502472000
    +B0914424553185N00644818EA0252602477000
    +B0914454553173N00644823EA0252502481000
    +B0914494553174N00644845EA0253102486000
    +B0914524553184N00644853EA0253502490000
    +B0914554553198N00644862EA0253702494000
    +B0914584553212N00644864EA0254202498000
    +B0915014553224N00644847EA0254502503000
    +B0915044553227N00644821EA0254602507000
    +B0915074553219N00644794EA0254902511000
    +B0915104553205N00644777EA0255802515000
    +B0915134553190N00644766EA0255602520000
    +B0915164553174N00644761EA0255402524000
    +B0915194553157N00644758EA0255102527000
    +B0915224553139N00644756EA0254402529000
    +B0915254553118N00644754EA0253702530000
    +B0915284553097N00644752EA0253102530000
    +B0915314553075N00644750EA0252602529000
    +B0915344553055N00644746EA0252302528000
    +B0915374553034N00644742EA0251602526000
    +B0915404553008N00644736EA0252002524000
    +B0915434552987N00644727EA0252002523000
    +B0915464552965N00644717EA0251602521000
    +B0915494552943N00644703EA0252302520000
    +B0915524552924N00644692EA0253402519000
    +B0915554552907N00644684EA0254002520000
    +B0915584552889N00644687EA0254702522000
    +B0916014552886N00644702EA0255102524000
    +B0916044552897N00644699EA0255602526000
    +B0916074552900N00644675EA0256402529000
    +B0916104552889N00644650EA0257102532000
    +B0916134552871N00644643EA0257702535000
    +B0916174552857N00644657EA0258102541000
    +B0916204552863N00644665EA0258302544000
    +B0916234552873N00644650EA0258702548000
    +B0916264552871N00644623EA0259202553000
    +B0916294552858N00644600EA0259702557000
    +B0916324552838N00644591EA0259802561000
    +B0916354552820N00644592EA0260302566000
    +B0916384552805N00644598EA0260202570000
    +B0916414552788N00644603EA0260002573000
    +B0916444552766N00644596EA0259502576000
    +B0916474552741N00644583EA0259502578000
    +B0916504552718N00644572EA0259602580000
    +B0916534552700N00644558EA0259002581000
    +B0916564552682N00644539EA0257902582000
    +B0916594552659N00644519EA0256402581000
    +B0917024552633N00644498EA0256202579000
    +B0917054552611N00644478EA0255702576000
    +B0917084552590N00644456EA0254902573000
    +B0917114552566N00644434EA0254202570000
    +B0917144552541N00644416EA0254902567000
    +B0917174552521N00644402EA0256102564000
    +B0917204552503N00644391EA0256502564000
    +B0917234552484N00644389EA0257102563000
    +B0917264552469N00644397EA0257502564000
    +B0917294552471N00644410EA0257702565000
    +B0917324552482N00644404EA0258302566000
    +B0917354552490N00644382EA0259202568000
    +B0917384552489N00644356EA0259502570000
    +B0917414552475N00644334EA0259802572000
    +B0917454552453N00644336EA0260002576000
    +B0917484552452N00644348EA0260502577000
    +B0917514552464N00644345EA0260802580000
    +B0917544552474N00644323EA0261102583000
    +B0917574552471N00644294EA0261702586000
    +B0918004552457N00644272EA0261702589000
    +B0918034552436N00644255EA0261902592000
    +B0918064552414N00644238EA0261602594000
    +B0918094552386N00644217EA0260802595000
    +B0918124552358N00644195EA0261002596000
    +B0918154552334N00644174EA0260302596000
    +B0918184552308N00644150EA0259102595000
    +B0918214552282N00644128EA0258602594000
    +B0918244552259N00644107EA0257202591000
    +B0918274552233N00644085EA0255902588000
    +B0918304552207N00644062EA0255502583000
    +B0918334552183N00644043EA0255002579000
    +B0918364552160N00644024EA0253902574000
    +B0918394552135N00644006EA0252902569000
    +B0918424552109N00643988EA0252202564000
    +B0918454552086N00643968EA0251402558000
    +B0918484552063N00643947EA0250602552000
    +B0918514552039N00643924EA0249802545000
    +B0918544552014N00643901EA0249402539000
    +B0918574551992N00643879EA0248402533000
    +B0919004551968N00643857EA0247602526000
    +B0919034551944N00643837EA0246702519000
    +B0919074551911N00643811EA0246002510000
    +B0919104551886N00643789EA0245402503000
    +B0919134551861N00643767EA0244802496000
    +B0919164551836N00643744EA0244402489000
    +B0919194551811N00643722EA0244102483000
    +B0919224551786N00643701EA0244202477000
    +B0919254551763N00643682EA0243902472000
    +B0919284551739N00643662EA0243202467000
    +B0919314551714N00643639EA0242502462000
    +B0919344551689N00643616EA0242502457000
    +B0919374551665N00643596EA0242302452000
    +B0919404551640N00643578EA0241902448000
    +B0919434551616N00643560EA0241902444000
    +B0919464551593N00643541EA0241702440000
    +B0919494551571N00643523EA0241302436000
    +B0919524551547N00643504EA0241002433000
    +B0919554551524N00643487EA0240702429000
    +B0919584551502N00643470EA0240302425000
    +B0920014551479N00643452EA0240102422000
    +B0920044551456N00643434EA0239702418000
    +B0920074551433N00643415EA0239302415000
    +B0920104551409N00643397EA0239102411000
    +B0920134551385N00643380EA0238802408000
    +B0920164551362N00643365EA0238302404000
    +B0920194551338N00643349EA0238002401000
    +B0920224551315N00643333EA0237902397000
    +B0920264551287N00643308EA0237502393000
    +B0920294551265N00643290EA0237202389000
    +B0920324551242N00643273EA0236502386000
    +B0920354551218N00643255EA0235802383000
    +B0920384551194N00643234EA0235502380000
    +B0920414551171N00643213EA0235202376000
    +B0920444551148N00643192EA0234802373000
    +B0920474551125N00643173EA0234302369000
    +B0920504551102N00643154EA0233802365000
    +B0920534551079N00643137EA0233502362000
    +B0920564551057N00643120EA0233202358000
    +B0920594551034N00643104EA0232702354000
    +B0921024551011N00643086EA0232202350000
    +B0921054550987N00643069EA0231702346000
    +B0921084550964N00643050EA0231102342000
    +B0921114550940N00643032EA0230702338000
    +B0921144550917N00643015EA0230102333000
    +B0921174550893N00642997EA0229602329000
    +B0921204550870N00642980EA0229102325000
    +B0921234550848N00642962EA0228502320000
    +B0921264550825N00642946EA0227802315000
    +B0921294550803N00642930EA0226902310000
    +B0921324550781N00642914EA0226502305000
    +B0921354550760N00642901EA0226002299000
    +B0921384550736N00642890EA0225402294000
    +B0921414550714N00642880EA0225002289000
    +B0921444550692N00642869EA0224502283000
    +B0921484550666N00642856EA0224002277000
    +B0921514550647N00642846EA0223402271000
    +B0921544550629N00642837EA0223202266000
    +B0921574550609N00642827EA0222602261000
    +B0922004550587N00642815EA0221802256000
    +B0922034550564N00642799EA0221102250000
    +B0922064550541N00642783EA0220402245000
    +B0922094550517N00642769EA0219902239000
    +B0922124550495N00642754EA0219302234000
    +B0922154550471N00642738EA0218402228000
    +B0922184550448N00642720EA0217702222000
    +B0922214550424N00642705EA0216702216000
    +B0922244550398N00642693EA0215902210000
    +B0922274550373N00642683EA0215202203000
    +B0922304550349N00642674EA0214202196000
    +B0922334550323N00642663EA0213402189000
    +B0922364550298N00642652EA0212402182000
    +B0922394550272N00642639EA0211502175000
    +B0922424550247N00642626EA0210802167000
    +B0922454550222N00642614EA0210202160000
    +B0922484550197N00642602EA0209302152000
    +B0922514550171N00642590EA0208602145000
    +B0922544550146N00642576EA0208102137000
    +B0922574550121N00642563EA0207602130000
    +B0923004550096N00642552EA0207102123000
    +B0923034550072N00642541EA0206902116000
    +B0923074550042N00642525EA0206802108000
    +B0923104550019N00642515EA0206302102000
    +B0923134549996N00642502EA0205902096000
    +B0923164549973N00642488EA0205602091000
    +B0923194549951N00642474EA0205202086000
    +B0923224549927N00642460EA0204502081000
    +B0923254549901N00642449EA0204202075000
    +B0923284549876N00642438EA0203602070000
    +B0923314549850N00642426EA0203302065000
    +B0923344549826N00642415EA0203002060000
    +B0923374549803N00642404EA0202702056000
    +B0923404549780N00642391EA0202702051000
    +B0923434549757N00642379EA0202602047000
    +B0923464549734N00642367EA0202602044000
    +B0923494549713N00642353EA0202602040000
    +B0923524549693N00642342EA0202402037000
    +B0923554549672N00642332EA0201902035000
    +B0923584549649N00642319EA0202002032000
    +B0924014549628N00642306EA0201602029000
    +B0924044549606N00642293EA0201102026000
    +B0924074549584N00642280EA0201202024000
    +B0924104549565N00642268EA0201602021000
    +B0924134549549N00642259EA0201502020000
    +B0924164549532N00642250EA0201202018000
    +B0924194549514N00642241EA0201002017000
    +B0924234549491N00642227EA0201102015000
    +B0924264549474N00642220EA0201102013000
    +B0924294549457N00642212EA0201802012000
    +B0924324549443N00642201EA0202202012000
    +B0924354549426N00642191EA0202402011000
    +B0924384549409N00642185EA0202902011000
    +B0924414549393N00642183EA0203002011000
    +B0924444549375N00642178EA0203502012000
    +B0924474549360N00642168EA0204002013000
    +B0924504549343N00642160EA0203602014000
    +B0924534549325N00642150EA0203502015000
    +B0924564549307N00642141EA0203202016000
    +B0924594549288N00642130EA0203802017000
    +B0925024549272N00642121EA0204102018000
    +B0925054549255N00642111EA0204502020000
    +B0925084549239N00642099EA0205102021000
    +B0925114549224N00642088EA0205402023000
    +B0925144549208N00642084EA0205702025000
    +B0925174549196N00642088EA0206102027000
    +B0925204549188N00642098EA0206202030000
    +B0925234549185N00642109EA0206402031000
    +B0925264549192N00642117EA0206402034000
    +B0925294549203N00642117EA0206602036000
    +B0925324549214N00642118EA0206902040000
    +B0925354549225N00642117EA0206602043000
    +B0925384549237N00642118EA0206902045000
    +B0925414549251N00642122EA0207402048000
    +B0925444549262N00642125EA0207702050000
    +B0925474549272N00642133EA0208402053000
    +B0925514549285N00642144EA0209202056000
    +B0925544549296N00642141EA0209602060000
    +B0925574549302N00642118EA0210302063000
    +B0926004549294N00642094EA0210802067000
    +B0926034549278N00642090EA0211402070000
    +B0926064549268N00642101EA0212202074000
    +B0926094549268N00642113EA0212602077000
    +B0926124549278N00642111EA0213002081000
    +B0926154549282N00642088EA0213502086000
    +B0926184549273N00642065EA0214102092000
    +B0926214549258N00642054EA0214402097000
    +B0926244549245N00642061EA0214602102000
    +B0926274549243N00642075EA0215102105000
    +B0926304549251N00642078EA0215402109000
    +B0926334549260N00642064EA0215702113000
    +B0926364549258N00642038EA0216002117000
    +B0926394549245N00642016EA0216502122000
    +B0926424549230N00642002EA0216702126000
    +B0926454549212N00641997EA0216802129000
    +B0926484549195N00642005EA0217002133000
    +B0926514549193N00642019EA0217102136000
    +B0926544549202N00642027EA0217402139000
    +B0926574549211N00642034EA0217902142000
    +B0927004549220N00642039EA0218402145000
    +B0927034549229N00642034EA0218602148000
    +B0927064549237N00642014EA0218902152000
    +B0927094549234N00641987EA0219102155000
    +B0927124549221N00641963EA0219502158000
    +B0927154549202N00641950EA0219802162000
    +B0927194549184N00641956EA0220202166000
    +B0927224549182N00641968EA0221102169000
    +B0927254549184N00641979EA0221502171000
    +B0927284549192N00641979EA0221502174000
    +B0927314549202N00641963EA0222602179000
    +B0927344549204N00641937EA0223002184000
    +B0927374549195N00641907EA0223102188000
    +B0927404549178N00641886EA0224002193000
    +B0927434549161N00641875EA0225202198000
    +B0927464549146N00641879EA0225702203000
    +B0927494549139N00641892EA0226502209000
    +B0927524549140N00641902EA0227002211000
    +B0927554549147N00641906EA0227202216000
    +B0927584549157N00641893EA0227702222000
    +B0928014549163N00641868EA0228202227000
    +B0928044549157N00641840EA0228102233000
    +B0928074549144N00641811EA0229202238000
    +B0928104549129N00641811EA0229902243000
    +B0928134549123N00641817EA0230902247000
    +B0928164549120N00641823EA0231102251000
    +B0928194549125N00641832EA0231802255000
    +B0928224549133N00641827EA0231702262000
    +B0928254549137N00641804EA0231002268000
    +B0928284549125N00641775EA0231102273000
    +B0928314549106N00641763EA0232002277000
    +B0928344549097N00641769EA0232602281000
    +B0928374549093N00641776EA0232602283000
    +B0928404549093N00641787EA0232402286000
    +B0928434549099N00641796EA0232302288000
    +B0928464549107N00641801EA0231902291000
    +B0928494549115N00641806EA0231502295000
    +B0928534549128N00641815EA0231402298000
    +B0928564549136N00641817EA0231102299000
    +B0928594549147N00641822EA0231002301000
    +B0929024549157N00641825EA0231002302000
    +B0929054549166N00641827EA0230602302000
    +B0929084549174N00641831EA0230302303000
    +B0929114549185N00641837EA0229902303000
    +B0929144549200N00641848EA0230202303000
    +B0929174549215N00641856EA0230202302000
    +B0929204549227N00641863EA0230902302000
    +B0929234549236N00641870EA0230702303000
    +B0929264549246N00641876EA0230702303000
    +B0929294549258N00641876EA0230902303000
    +B0929324549269N00641876EA0231202303000
    +B0929354549283N00641876EA0232202304000
    +B0929384549294N00641876EA0233302306000
    +B0929414549302N00641880EA0233502308000
    +B0929444549306N00641894EA0233802311000
    +B0929474549296N00641908EA0233802314000
    +B0929504549279N00641903EA0234202316000
    +B0929534549268N00641881EA0234602319000
    +B0929564549270N00641855EA0234902321000
    +B0929594549282N00641840EA0235802324000
    +B0930024549293N00641832EA0236102327000
    +B0930054549303N00641827EA0235702331000
    +B0930084549314N00641824EA0235602333000
    +B0930114549325N00641824EA0234702336000
    +B0930144549340N00641830EA0234502337000
    +B0930184549360N00641840EA0235202338000
    +B0930214549374N00641845EA0234902339000
    +B0930244549391N00641851EA0235702340000
    +B0930274549403N00641854EA0236502341000
    +B0930304549413N00641862EA0236802343000
    +B0930334549412N00641878EA0236902345000
    +B0930364549396N00641887EA0236802347000
    +B0930394549381N00641868EA0236802348000
    +B0930424549379N00641842EA0237102350000
    +B0930454549389N00641822EA0237302351000
    +B0930484549403N00641816EA0238102353000
    +B0930514549410N00641822EA0238402355000
    +B0930544549411N00641835EA0238102356000
    +B0930574549415N00641847EA0237402358000
    +B0931004549425N00641855EA0237002359000
    +B0931034549436N00641860EA0237402360000
    +B0931064549447N00641866EA0238202362000
    +B0931094549454N00641871EA0238302363000
    +B0931124549467N00641870EA0238902365000
    +B0931154549477N00641849EA0239102368000
    +B0931184549471N00641822EA0239402370000
    +B0931214549453N00641810EA0240302373000
    +B0931244549440N00641818EA0240802377000
    +B0931274549438N00641830EA0241102379000
    +B0931304549451N00641833EA0241602382000
    +B0931334549462N00641814EA0241702386000
    +B0931364549457N00641786EA0242002389000
    +B0931394549443N00641763EA0242902394000
    +B0931434549421N00641764EA0243702397000
    +B0931464549414N00641775EA0244602401000
    +B0931494549416N00641788EA0245302405000
    +B0931524549422N00641796EA0245602408000
    +B0931554549433N00641789EA0245602413000
    +B0931584549434N00641763EA0245202418000
    +B0932014549421N00641737EA0245602421000
    +B0932044549404N00641721EA0246502425000
    +B0932074549387N00641715EA0247002429000
    +B0932104549373N00641720EA0247402434000
    +B0932134549366N00641731EA0248102438000
    +B0932164549364N00641744EA0248702441000
    +B0932194549367N00641755EA0248902444000
    +B0932224549376N00641759EA0248702449000
    +B0932254549385N00641749EA0248602454000
    +B0932284549389N00641723EA0249002458000
    +B0932314549378N00641693EA0249102461000
    +B0932344549360N00641681EA0248902465000
    +B0932374549345N00641687EA0248702467000
    +B0932404549341N00641701EA0248702469000
    +B0932434549347N00641707EA0248102470000
    +B0932464549360N00641708EA0249002471000
    +B0932494549368N00641707EA0249402473000
    +B0932524549379N00641705EA0249702475000
    +B0932554549389N00641707EA0250202478000
    +B0932584549398N00641708EA0250402479000
    +B0933014549406N00641713EA0250002481000
    +B0933044549416N00641719EA0250402483000
    +B0933074549429N00641718EA0251902486000
    +B0933114549440N00641710EA0252402489000
    +B0933144549450N00641707EA0253402494000
    +B0933174549456N00641690EA0254302498000
    +B0933204549451N00641658EA0254902503000
    +B0933234549434N00641639EA0256102509000
    +B0933264549417N00641639EA0257502515000
    +B0933294549407N00641644EA0258302522000
    +B0933324549407N00641652EA0259202525000
    +B0933354549416N00641645EA0260402532000
    +B0933384549414N00641618EA0260902540000
    +B0933414549399N00641597EA0262002547000
    +B0933444549383N00641588EA0263002555000
    +B0933474549370N00641592EA0263402563000
    +B0933504549368N00641609EA0264202571000
    +B0933534549375N00641618EA0265402578000
    +B0933564549382N00641602EA0266002586000
    +B0933594549379N00641571EA0266602593000
    +B0934024549368N00641546EA0267602601000
    +B0934054549352N00641538EA0267902609000
    +B0934084549334N00641543EA0268102616000
    +B0934114549328N00641561EA0268402623000
    +B0934144549338N00641569EA0269102630000
    +B0934174549346N00641552EA0269502636000
    +B0934204549341N00641527EA0269902642000
    +B0934234549325N00641511EA0270602649000
    +B0934264549308N00641496EA0271402655000
    +B0934294549288N00641479EA0272002661000
    +B0934324549265N00641464EA0272302667000
    +B0934354549243N00641452EA0272702673000
    +B0934384549222N00641438EA0272402679000
    +B0934414549202N00641422EA0272502684000
    +B0934444549179N00641414EA0272102688000
    +B0934474549159N00641395EA0272402691000
    +B0934504549142N00641376EA0272102694000
    +B0934544549118N00641350EA0272302697000
    +B0934574549097N00641332EA0272302700000
    +B0935004549079N00641314EA0272802702000
    +B0935034549065N00641291EA0272802705000
    +B0935064549051N00641265EA0272902708000
    +B0935094549034N00641242EA0273002710000
    +B0935124549016N00641218EA0273102712000
    +B0935154549005N00641193EA0273802714000
    +B0935184548992N00641174EA0273002716000
    +B0935214548975N00641153EA0273202718000
    +B0935244548963N00641133EA0273802719000
    +B0935274548948N00641111EA0274302721000
    +B0935304548934N00641087EA0274902723000
    +B0935334548917N00641066EA0274202725000
    +B0935364548895N00641042EA0273402726000
    +B0935394548872N00641015EA0272902727000
    +B0935424548848N00640987EA0272202726000
    +B0935454548827N00640958EA0271902724000
    +B0935484548807N00640929EA0271802723000
    +B0935514548788N00640896EA0272302721000
    +B0935544548771N00640865EA0273102721000
    +B0935574548754N00640833EA0273302721000
    +B0936004548736N00640802EA0273202722000
    +B0936034548718N00640771EA0273002722000
    +B0936064548698N00640743EA0272502722000
    +B0936094548679N00640714EA0272702722000
    +B0936134548662N00640679EA0272702722000
    +B0936164548650N00640654EA0272102721000
    +B0936194548635N00640624EA0272202721000
    +B0936224548619N00640594EA0272802720000
    +B0936254548603N00640567EA0273402721000
    +B0936284548588N00640541EA0274102722000
    +B0936314548573N00640515EA0275302724000
    +B0936344548558N00640492EA0275702727000
    +B0936374548541N00640467EA0275302730000
    +B0936404548526N00640440EA0275302731000
    +B0936434548509N00640437EA0274502732000
    +B0936464548490N00640430EA0274902733000
    +B0936494548481N00640416EA0274202733000
    +B0936524548467N00640396EA0273902733000
    +B0936554548451N00640376EA0274502733000
    +B0936584548438N00640357EA0275202734000
    +B0937014548428N00640340EA0276202736000
    +B0937044548418N00640326EA0275802738000
    +B0937074548405N00640308EA0275402739000
    +B0937104548391N00640290EA0275502740000
    +B0937134548377N00640270EA0274802741000
    +B0937164548361N00640246EA0274602741000
    +B0937194548346N00640220EA0275002741000
    +B0937224548333N00640197EA0275102742000
    +B0937254548319N00640174EA0275202742000
    +B0937284548304N00640152EA0275402743000
    +B0937314548290N00640129EA0275502743000
    +B0937344548277N00640106EA0275702744000
    +B0937374548264N00640085EA0276602745000
    +B0937414548248N00640058EA0276402747000
    +B0937444548235N00640037EA0276902748000
    +B0937474548223N00640017EA0276902749000
    +B0937504548211N00639995EA0277002751000
    +B0937534548198N00639972EA0277402752000
    +B0937564548187N00639951EA0277602754000
    +B0937594548173N00639923EA0276602755000
    +B0938024548158N00639893EA0277002756000
    +B0938054548144N00639869EA0276402756000
    +B0938084548129N00639843EA0275502756000
    +B0938114548115N00639814EA0275702755000
    +B0938144548103N00639788EA0275102754000
    +B0938174548091N00639758EA0274802753000
    +B0938204548079N00639729EA0274402751000
    +B0938234548069N00639698EA0273602749000
    +B0938264548058N00639666EA0273302747000
    +B0938294548052N00639634EA0273302744000
    +B0938324548043N00639605EA0273202742000
    +B0938354548032N00639574EA0273502740000
    +B0938384548023N00639541EA0274302739000
    +B0938414548014N00639515EA0274602739000
    +B0938444548005N00639493EA0273202738000
    +B0938474547991N00639459EA0272402737000
    +B0938504547978N00639423EA0274102735000
    +B0938534547972N00639405EA0275102734000
    +B0938564547969N00639385EA0275202735000
    +B0938594547962N00639358EA0275702736000
    +B0939034547948N00639322EA0276202738000
    +B0939064547933N00639296EA0276502740000
    +B0939094547917N00639269EA0276702742000
    +B0939124547899N00639244EA0276902744000
    +B0939154547884N00639219EA0276502746000
    +B0939184547866N00639191EA0275902747000
    +B0939214547849N00639163EA0275602747000
    +B0939244547832N00639136EA0275302747000
    +B0939274547816N00639109EA0275002746000
    +B0939304547799N00639081EA0274702745000
    +B0939334547783N00639052EA0274502744000
    +B0939364547767N00639022EA0274102743000
    +B0939394547753N00638991EA0273502741000
    +B0939424547742N00638960EA0273102739000
    +B0939454547737N00638935EA0273002738000
    +B0939484547727N00638912EA0271202735000
    +B0939514547713N00638880EA0270802732000
    +B0939544547699N00638850EA0270502728000
    +B0939574547688N00638821EA0269402725000
    +B0940004547677N00638789EA0269002721000
    +B0940034547667N00638760EA0268202717000
    +B0940064547656N00638728EA0267502712000
    +B0940094547645N00638695EA0266902708000
    +B0940124547633N00638664EA0266202703000
    +B0940154547621N00638632EA0265702697000
    +B0940184547610N00638600EA0264802692000
    +B0940214547600N00638569EA0264302686000
    +B0940254547585N00638527EA0263402679000
    +B0940284547573N00638496EA0263002673000
    +B0940314547562N00638467EA0262502667000
    +B0940344547550N00638436EA0261702661000
    +B0940374547537N00638405EA0260902655000
    +B0940404547525N00638375EA0260002648000
    +B0940434547514N00638342EA0259202642000
    +B0940464547505N00638312EA0258302635000
    +B0940494547495N00638280EA0257102628000
    +B0940524547486N00638247EA0256202620000
    +B0940554547475N00638215EA0255302613000
    +B0940584547465N00638182EA0254102605000
    +B0941014547454N00638149EA0253402596000
    +B0941044547444N00638118EA0252402588000
    +B0941074547433N00638085EA0251602580000
    +B0941104547424N00638052EA0251602572000
    +B0941134547416N00638022EA0250902564000
    +B0941164547409N00637988EA0250302557000
    +B0941194547405N00637954EA0249502550000
    +B0941224547399N00637920EA0248802543000
    +B0941254547394N00637886EA0248702536000
    +B0941284547392N00637856EA0248702530000
    +B0941314547389N00637829EA0247602524000
    +B0941344547383N00637797EA0247102517000
    +B0941374547375N00637768EA0246902512000
    +B0941404547369N00637741EA0246802506000
    +B0941444547362N00637707EA0247102499000
    +B0941474547358N00637681EA0247302495000
    +B0941504547353N00637655EA0247502492000
    +B0941534547348N00637630EA0247702489000
    +B0941564547341N00637603EA0247802486000
    +B0941594547334N00637580EA0248202484000
    +B0942024547327N00637558EA0248602483000
    +B0942054547320N00637537EA0248902482000
    +B0942084547312N00637511EA0249102482000
    +B0942114547306N00637486EA0249302482000
    +B0942144547300N00637461EA0249602482000
    +B0942174547292N00637438EA0249802482000
    +B0942204547281N00637416EA0249902483000
    +B0942234547271N00637392EA0249802484000
    +B0942264547260N00637369EA0250402484000
    +B0942294547247N00637347EA0251102485000
    +B0942324547235N00637323EA0251802487000
    +B0942354547225N00637301EA0252202489000
    +B0942384547214N00637278EA0252702492000
    +B0942414547205N00637253EA0252802495000
    +B0942444547194N00637227EA0253002497000
    +B0942474547181N00637203EA0252802500000
    +B0942504547166N00637180EA0253102502000
    +B0942534547152N00637155EA0253502504000
    +B0942564547140N00637133EA0253802506000
    +B0942594547129N00637109EA0253702508000
    +B0943034547116N00637075EA0253602511000
    +B0943064547110N00637049EA0253702512000
    +B0943094547102N00637022EA0253002513000
    +B0943124547094N00636993EA0253302514000
    +B0943154547083N00636968EA0252802515000
    +B0943184547072N00636939EA0252702515000
    +B0943214547064N00636912EA0252802515000
    +B0943244547054N00636885EA0252702515000
    +B0943274547041N00636860EA0252602515000
    +B0943304547028N00636835EA0252202515000
    +B0943334547015N00636809EA0252002514000
    +B0943364547003N00636782EA0252002514000
    +B0943394546992N00636756EA0251602513000
    +B0943424546980N00636733EA0251002512000
    +B0943454546967N00636708EA0250302511000
    +B0943484546957N00636687EA0249702509000
    +B0943514546946N00636667EA0248202507000
    +B0943544546931N00636638EA0247102503000
    +B0943574546915N00636607EA0246802499000
    +B0944004546901N00636578EA0246002495000
    +B0944034546886N00636548EA0245502490000
    +B0944064546872N00636519EA0244802486000
    +B0944094546858N00636489EA0243902481000
    +B0944124546844N00636458EA0244002476000
    +B0944154546829N00636433EA0244902472000
    +B0944184546817N00636417EA0244502468000
    +B0944214546800N00636396EA0244802465000
    +B0944254546780N00636364EA0246002462000
    +B0944284546768N00636342EA0245902461000
    +B0944314546754N00636312EA0246002460000
    +B0944344546742N00636282EA0246702460000
    +B0944374546734N00636253EA0247202460000
    +B0944404546727N00636226EA0247602461000
    +B0944434546720N00636196EA0248302462000
    +B0944464546716N00636168EA0249102464000
    +B0944494546707N00636144EA0249502466000
    +B0944524546693N00636121EA0249702469000
    +B0944554546679N00636101EA0250702471000
    +B0944584546665N00636083EA0250302474000
    +B0945014546649N00636059EA0249702476000
    +B0945044546638N00636031EA0250402478000
    +B0945074546631N00636006EA0249802480000
    +B0945104546619N00635978EA0248802481000
    +B0945134546608N00635947EA0248402481000
    +B0945164546597N00635918EA0247802480000
    +B0945194546587N00635886EA0246902479000
    +B0945224546580N00635851EA0247002477000
    +B0945254546575N00635819EA0247702476000
    +B0945284546573N00635796EA0248002475000
    +B0945314546572N00635776EA0248402475000
    +B0945344546569N00635755EA0248702475000
    +B0945374546563N00635730EA0249702475000
    +B0945404546563N00635712EA0250902477000
    +B0945434546561N00635697EA0250902479000
    +B0945464546555N00635676EA0252402481000
    +B0945494546550N00635662EA0253102485000
    +B0945524546542N00635644EA0253702488000
    +B0945554546530N00635621EA0254502492000
    +B0945584546520N00635594EA0255702496000
    +B0946014546509N00635566EA0256502502000
    +B0946054546494N00635535EA0257602510000
    +B0946084546488N00635514EA0258002516000
    +B0946114546477N00635487EA0257902522000
    +B0946144546467N00635466EA0259302528000
    +B0946174546459N00635454EA0258902534000
    +B0946204546447N00635434EA0258902539000
    +B0946234546438N00635418EA0258802543000
    +B0946264546428N00635399EA0257702547000
    +B0946294546415N00635373EA0256402549000
    +B0946324546401N00635344EA0255902550000
    +B0946354546389N00635315EA0255202550000
    +B0946384546377N00635285EA0254602549000
    +B0946414546367N00635254EA0254102547000
    +B0946444546359N00635226EA0254302546000
    +B0946474546353N00635202EA0254302544000
    +B0946504546347N00635176EA0254902543000
    +B0946534546339N00635153EA0255502542000
    +B0946564546330N00635130EA0255802543000
    +B0946594546322N00635101EA0255902543000
    +B0947024546314N00635069EA0256302544000
    +B0947054546306N00635039EA0256002544000
    +B0947084546294N00635011EA0256802545000
    +B0947114546286N00634992EA0256602547000
    +B0947144546274N00634968EA0256402547000
    +B0947174546260N00634940EA0257002548000
    +B0947204546246N00634912EA0256302550000
    +B0947234546232N00634884EA0255602550000
    +B0947264546218N00634856EA0255102551000
    +B0947294546205N00634828EA0254702550000
    +B0947334546192N00634790EA0254502549000
    +B0947364546182N00634762EA0254002548000
    +B0947394546172N00634735EA0253502546000
    +B0947424546161N00634706EA0252902544000
    +B0947454546149N00634676EA0252602542000
    +B0947484546138N00634646EA0252502539000
    +B0947514546129N00634616EA0251802537000
    +B0947544546117N00634581EA0251702534000
    +B0947574546106N00634548EA0252002532000
    +B0948004546094N00634514EA0251902529000
    +B0948034546083N00634479EA0253102528000
    +B0948064546073N00634453EA0254902528000
    +B0948094546067N00634438EA0255402529000
    +B0948124546058N00634417EA0254902530000
    +B0948154546045N00634388EA0255702531000
    +B0948184546037N00634364EA0256302533000
    +B0948214546025N00634337EA0256202535000
    +B0948244546012N00634311EA0257002538000
    +B0948274546002N00634286EA0256802540000
    +B0948304545991N00634261EA0255902542000
    +B0948334545977N00634233EA0255402542000
    +B0948364545964N00634206EA0255402543000
    +B0948394545953N00634185EA0255402543000
    +B0948424545944N00634165EA0254802543000
    +B0948454545933N00634139EA0255102543000
    +B0948484545924N00634114EA0254802543000
    +B0948514545913N00634085EA0255402543000
    +B0948544545905N00634056EA0256202543000
    +B0948574545898N00634026EA0257402544000
    +B0949004545892N00633998EA0258302546000
    +B0949034545885N00633976EA0258102549000
    +B0949074545876N00633947EA0257602552000
    +B0949104545867N00633925EA0257902553000
    +B0949134545858N00633907EA0258102555000
    +B0949164545846N00633890EA0257602557000
    +B0949194545836N00633870EA0258502558000
    +B0949224545833N00633851EA0258902560000
    +B0949254545826N00633833EA0259102562000
    +B0949284545818N00633812EA0259102564000
    +B0949314545810N00633788EA0258502565000
    +B0949344545798N00633762EA0258402566000
    +B0949374545787N00633734EA0258402567000
    +B0949404545777N00633706EA0258102568000
    +B0949434545766N00633676EA0257902568000
    +B0949464545754N00633646EA0257702568000
    +B0949494545743N00633616EA0257702567000
    +B0949524545732N00633586EA0257902567000
    +B0949554545720N00633557EA0257802567000
    +B0949584545708N00633527EA0257802567000
    +B0950014545693N00633498EA0258202567000
    +B0950044545679N00633469EA0258702567000
    +B0950074545666N00633440EA0258302568000
    +B0950104545654N00633410EA0258302568000
    +B0950134545642N00633381EA0258002568000
    +B0950164545628N00633351EA0258302568000
    +B0950194545617N00633326EA0258302568000
    +B0950234545604N00633284EA0257702568000
    +B0950264545594N00633253EA0258202568000
    +B0950294545585N00633226EA0258302568000
    +B0950324545574N00633199EA0257902568000
    +B0950354545563N00633172EA0258002568000
    +B0950384545553N00633148EA0257702568000
    +B0950414545542N00633125EA0257202568000
    +B0950444545529N00633101EA0257302568000
    +B0950474545517N00633080EA0257102568000
    +B0950504545504N00633058EA0256802567000
    +B0950534545493N00633033EA0255902566000
    +B0950564545481N00633007EA0255002564000
    +B0950594545470N00632977EA0254202562000
    +B0951024545458N00632948EA0254002559000
    +B0951054545448N00632923EA0253802556000
    +B0951084545441N00632900EA0253802553000
    +B0951114545437N00632880EA0253402550000
    +B0951144545431N00632860EA0253002547000
    +B0951174545424N00632842EA0252402545000
    +B0951204545415N00632821EA0251302541000
    +B0951234545403N00632800EA0251002537000
    +B0951264545393N00632780EA0250002533000
    +B0951294545382N00632758EA0249702529000
    +B0951324545372N00632739EA0248902524000
    +B0951354545362N00632717EA0248202519000
    +B0951384545354N00632697EA0247502514000
    +B0951414545344N00632673EA0245902509000
    +B0951454545331N00632630EA0246002501000
    +B0951484545323N00632603EA0245502495000
    +B0951514545314N00632574EA0244302489000
    +B0951544545305N00632544EA0243902483000
    +B0951574545297N00632515EA0243102477000
    +B0952004545287N00632485EA0242202471000
    +B0952034545277N00632455EA0241802464000
    +B0952064545269N00632426EA0241302458000
    +B0952094545258N00632397EA0240802452000
    +B0952124545247N00632371EA0240202446000
    +B0952154545236N00632344EA0239402439000
    +B0952184545227N00632315EA0239002433000
    +B0952214545218N00632292EA0238202427000
    +B0952244545208N00632265EA0237902421000
    +B0952274545200N00632240EA0238102416000
    +B0952304545192N00632221EA0237402411000
    +B0952334545183N00632198EA0236902405000
    +B0952364545175N00632173EA0237002400000
    +B0952394545167N00632150EA0236802396000
    +B0952424545161N00632126EA0236802392000
    +B0952454545154N00632103EA0236602388000
    +B0952484545148N00632082EA0236602385000
    +B0952514545142N00632061EA0236302381000
    +B0952544545132N00632040EA0236002378000
    +B0952574545122N00632021EA0236202375000
    +B0953004545113N00632001EA0236502373000
    +B0953034545104N00631980EA0237102371000
    +B0953064545097N00631962EA0237702370000
    +B0953094545091N00631943EA0237802370000
    +B0953134545084N00631918EA0238102370000
    +B0953164545077N00631901EA0238402371000
    +B0953194545071N00631884EA0238602371000
    +B0953224545067N00631866EA0239302372000
    +B0953254545063N00631852EA0239602374000
    +B0953284545054N00631836EA0240302375000
    +B0953314545047N00631821EA0241402378000
    +B0953344545039N00631807EA0241802380000
    +B0953374545032N00631789EA0242502384000
    +B0953404545026N00631769EA0243702388000
    +B0953434545022N00631753EA0244702392000
    +B0953464545016N00631736EA0245302397000
    +B0953494545009N00631717EA0246402403000
    +B0953524545002N00631699EA0247202409000
    +B0953554544994N00631677EA0248102415000
    +B0953584544988N00631658EA0249002422000
    +B0954014544981N00631640EA0249402429000
    +B0954044544976N00631619EA0250402436000
    +B0954074544972N00631601EA0250902442000
    +B0954104544967N00631579EA0251402449000
    +B0954134544961N00631554EA0253002456000
    +B0954164544957N00631532EA0253702463000
    +B0954194544950N00631512EA0254102470000
    +B0954224544945N00631494EA0253802477000
    +B0954254544938N00631474EA0253502483000
    +B0954284544932N00631451EA0253102488000
    +B0954314544925N00631423EA0253002492000
    +B0954344544919N00631395EA0253202495000
    +B0954374544913N00631368EA0253102498000
    +B0954404544908N00631341EA0253002501000
    +B0954444544902N00631305EA0252802504000
    +B0954474544895N00631280EA0252302506000
    +B0954504544887N00631257EA0252102507000
    +B0954534544879N00631235EA0251002508000
    +B0954564544872N00631210EA0250702507000
    +B0954594544867N00631187EA0250502506000
    +B0955024544862N00631163EA0250302505000
    +B0955054544856N00631139EA0250302504000
    +B0955084544851N00631115EA0249902503000
    +B0955114544846N00631087EA0249602502000
    +B0955144544844N00631059EA0248902500000
    +B0955174544842N00631032EA0248602498000
    +B0955204544837N00631007EA0248102495000
    +B0955234544833N00630983EA0248002493000
    +B0955264544830N00630958EA0248302491000
    +B0955294544827N00630934EA0248402489000
    +B0955324544825N00630908EA0248202487000
    +B0955354544822N00630881EA0247802486000
    +B0955384544819N00630853EA0247602484000
    +B0955414544814N00630827EA0246802482000
    +B0955444544809N00630798EA0246502480000
    +B0955474544803N00630771EA0245902478000
    +B0955504544797N00630743EA0245202475000
    +B0955534544790N00630713EA0244302473000
    +B0955564544785N00630678EA0243902469000
    +B0955594544784N00630648EA0244402466000
    +B0956024544782N00630622EA0244002463000
    +B0956064544776N00630582EA0244002459000
    +B0956094544770N00630555EA0243802456000
    +B0956124544765N00630525EA0242902452000
    +B0956154544761N00630493EA0242402449000
    +B0956184544758N00630462EA0241302445000
    +B0956214544759N00630425EA0240602441000
    +B0956244544766N00630388EA0240602436000
    +B0956274544775N00630358EA0241002432000
    +B0956304544784N00630332EA0240502429000
    +B0956334544796N00630304EA0241102425000
    +B0956364544808N00630287EA0242402423000
    +B0956394544817N00630273EA0242502422000
    +B0956424544828N00630254EA0243502422000
    +B0956454544838N00630238EA0243902422000
    +B0956484544851N00630221EA0244202423000
    +B0956514544864N00630203EA0244802424000
    +B0956544544872N00630184EA0245702426000
    +B0956574544876N00630163EA0246402428000
    +B0957004544879N00630146EA0246602431000
    +B0957034544889N00630129EA0246602434000
    +B0957064544898N00630110EA0246802437000
    +B0957094544906N00630092EA0246602439000
    +B0957124544921N00630085EA0246302442000
    +B0957154544934N00630100EA0246602443000
    +B0957184544933N00630120EA0246202445000
    +B0957214544926N00630144EA0246402446000
    +B0957244544918N00630166EA0246502447000
    +B0957274544909N00630188EA0246902449000
    +B0957304544898N00630207EA0247802450000
    +B0957334544886N00630221EA0248402452000
    +B0957374544867N00630226EA0249502456000
    +B0957404544858N00630216EA0251202460000
    +B0957434544857N00630204EA0252002463000
    +B0957464544866N00630188EA0253102469000
    +B0957494544879N00630183EA0254302475000
    +B0957524544890N00630196EA0255302482000
    +B0957554544890N00630217EA0256202489000
    +B0957584544879N00630230EA0257002496000
    +B0958014544866N00630227EA0258202503000
    +B0958044544864N00630213EA0259402510000
    +B0958074544872N00630199EA0260602518000
    +B0958104544885N00630197EA0261502526000
    +B0958134544893N00630212EA0262002534000
    +B0958164544887N00630230EA0262702542000
    +B0958194544873N00630233EA0263802550000
    +B0958224544861N00630225EA0264502558000
    +B0958254544852N00630210EA0264902566000
    +B0958284544845N00630193EA0264902574000
    +B0958314544837N00630173EA0264602581000
    +B0958344544830N00630153EA0264202586000
    +B0958374544824N00630133EA0264102591000
    +B0958404544818N00630112EA0264002596000
    +B0958434544811N00630091EA0264702600000
    +B0958464544804N00630075EA0264902604000
    +B0958494544798N00630055EA0264702608000
    +B0958524544792N00630032EA0265202612000
    +B0958554544786N00630014EA0264902616000
    +B0958584544778N00629994EA0264002619000
    +B0959014544768N00629973EA0263302621000
    +B0959044544757N00629950EA0262402622000
    +B0959074544748N00629923EA0261602621000
    +B0959104544739N00629895EA0261102620000
    +B0959144544728N00629858EA0260802618000
    +B0959174544719N00629830EA0260302616000
    +B0959204544710N00629801EA0259802614000
    +B0959234544702N00629773EA0259502611000
    +B0959264544694N00629746EA0259402609000
    +B0959294544687N00629718EA0259202606000
    +B0959324544678N00629687EA0261302604000
    +B0959354544676N00629670EA0262402604000
    +B0959384544671N00629648EA0263102605000
    +B0959414544666N00629627EA0264302607000
    +B0959444544663N00629607EA0263702610000
    +B0959474544658N00629580EA0264002612000
    +B0959504544656N00629559EA0264002614000
    +B0959534544656N00629535EA0263902616000
    +B0959564544653N00629508EA0264402618000
    +B0959594544650N00629484EA0265102620000
    +B1000024544645N00629456EA0265902623000
    +B1000054544640N00629427EA0266602626000
    +B1000084544633N00629400EA0267202629000
    +B1000114544627N00629374EA0268002633000
    +B1000144544618N00629352EA0268902637000
    +B1000174544610N00629331EA0269402641000
    +B1000204544603N00629308EA0269802645000
    +B1000234544593N00629289EA0270302650000
    +B1000264544581N00629271EA0270602654000
    +B1000294544568N00629252EA0270902658000
    +B1000324544556N00629233EA0270602662000
    +B1000354544543N00629216EA0270702666000
    +B1000384544534N00629200EA0271202669000
    +B1000414544527N00629183EA0271302672000
    +B1000444544517N00629164EA0272202675000
    +B1000484544502N00629148EA0272502680000
    +B1000514544491N00629138EA0272902684000
    +B1000544544481N00629128EA0273302689000
    +B1000574544472N00629114EA0273702693000
    +B1001004544464N00629100EA0274102697000
    +B1001034544455N00629086EA0274202701000
    +B1001064544446N00629072EA0274102704000
    +B1001094544439N00629056EA0273802707000
    +B1001124544432N00629038EA0273502709000
    +B1001154544425N00629020EA0273402711000
    +B1001184544418N00629002EA0272902713000
    +B1001214544412N00628981EA0272302714000
    +B1001244544408N00628955EA0271602714000
    +B1001274544405N00628925EA0271302713000
    +B1001304544398N00628893EA0272002713000
    +B1001334544391N00628870EA0272002713000
    +B1001364544383N00628850EA0271502712000
    +B1001394544371N00628829EA0271402712000
    +B1001424544360N00628807EA0271102711000
    +B1001454544351N00628782EA0270502710000
    +B1001484544342N00628754EA0270302708000
    +B1001514544335N00628726EA0269702707000
    +B1001544544329N00628697EA0269402704000
    +B1001574544320N00628671EA0269202702000
    +B1002004544311N00628645EA0268402699000
    +B1002034544301N00628615EA0267802696000
    +B1002074544291N00628573EA0267202691000
    +B1002104544286N00628541EA0266702688000
    +B1002134544281N00628509EA0266302684000
    +B1002164544277N00628478EA0265902681000
    +B1002194544273N00628447EA0265702677000
    +B1002224544268N00628416EA0265202673000
    +B1002254544261N00628385EA0264502669000
    +B1002284544255N00628356EA0264402665000
    +B1002314544248N00628325EA0263102661000
    +B1002344544241N00628288EA0262902656000
    +B1002374544234N00628253EA0262202652000
    +B1002404544226N00628216EA0261702647000
    +B1002434544219N00628179EA0261402643000
    +B1002464544213N00628144EA0260802638000
    +B1002494544205N00628108EA0260502634000
    +B1002524544197N00628072EA0260202629000
    +B1002554544189N00628037EA0259502625000
    +B1002584544181N00628000EA0259502620000
    +B1003014544175N00627964EA0259402616000
    +B1003044544168N00627930EA0258802613000
    +B1003074544159N00627896EA0258402609000
    +B1003104544149N00627862EA0257702605000
    +B1003134544140N00627827EA0257302601000
    +B1003164544132N00627793EA0256702597000
    +B1003194544123N00627759EA0256102592000
    +B1003234544111N00627712EA0255702586000
    +B1003264544102N00627677EA0255302582000
    +B1003294544092N00627643EA0254902578000
    +B1003324544083N00627609EA0254502573000
    +B1003354544072N00627577EA0254002569000
    +B1003384544062N00627543EA0253602565000
    +B1003414544054N00627508EA0253302560000
    +B1003444544049N00627472EA0253002556000
    +B1003474544045N00627435EA0252602552000
    +B1003504544043N00627397EA0252302549000
    +B1003534544039N00627360EA0252102545000
    +B1003564544036N00627324EA0251702542000
    +B1003594544031N00627286EA0251202538000
    +B1004024544026N00627250EA0250702535000
    +B1004054544020N00627215EA0250102531000
    +B1004084544015N00627179EA0249702527000
    +B1004114544009N00627142EA0249402523000
    +B1004144544004N00627106EA0249102519000
    +B1004174543999N00627070EA0248602515000
    +B1004204543994N00627032EA0248402511000
    +B1004234543989N00626996EA0248002507000
    +B1004264543985N00626960EA0247502503000
    +B1004294543980N00626923EA0247202499000
    +B1004324543974N00626888EA0246602495000
    +B1004354543968N00626851EA0246202491000
    +B1004394543959N00626802EA0245902485000
    +B1004424543953N00626766EA0245502481000
    +B1004454543947N00626729EA0245102477000
    +B1004484543942N00626694EA0244802473000
    +B1004514543938N00626660EA0244202469000
    +B1004544543934N00626622EA0243802464000
    +B1004574543930N00626585EA0243402460000
    +B1005004543927N00626547EA0243102456000
    +B1005034543923N00626508EA0242702452000
    +B1005064543920N00626471EA0242202448000
    +B1005094543918N00626430EA0241802443000
    +B1005124543917N00626391EA0241502439000
    +B1005154543914N00626353EA0240802434000
    +B1005184543908N00626314EA0240302430000
    +B1005214543901N00626276EA0239602425000
    +B1005244543895N00626237EA0239102420000
    +B1005274543890N00626199EA0238802416000
    +B1005304543886N00626160EA0238202411000
    +B1005334543881N00626121EA0237802405000
    +B1005364543877N00626083EA0237302400000
    +B1005394543872N00626043EA0236802395000
    +B1005424543868N00626003EA0236502390000
    +B1005454543864N00625964EA0236002385000
    +B1005484543860N00625924EA0235602381000
    +B1005514543856N00625883EA0235302376000
    +B1005554543851N00625830EA0234902370000
    +B1005584543846N00625791EA0234402365000
    +B1006014543841N00625751EA0234002361000
    +B1006044543835N00625712EA0233402357000
    +B1006074543829N00625673EA0232902352000
    +B1006104543824N00625633EA0232502348000
    +B1006134543819N00625594EA0232002343000
    +B1006164543814N00625553EA0231502339000
    +B1006194543809N00625514EA0231102334000
    +B1006224543805N00625474EA0230602330000
    +B1006254543800N00625434EA0230202326000
    +B1006284543796N00625394EA0229702322000
    +B1006314543791N00625354EA0229302317000
    +B1006344543786N00625314EA0228802313000
    +B1006374543780N00625275EA0228402309000
    +B1006404543775N00625236EA0227902305000
    +B1006434543768N00625197EA0227502300000
    +B1006464543762N00625158EA0227102296000
    +B1006494543756N00625119EA0226602292000
    +B1006524543749N00625081EA0226202288000
    +B1006554543742N00625044EA0225702284000
    +B1006584543736N00625006EA0225302279000
    +B1007014543730N00624968EA0224902275000
    +B1007044543725N00624929EA0224402271000
    +B1007074543719N00624891EA0224002267000
    +B1007114543711N00624841EA0223502262000
    +B1007144543706N00624803EA0223202258000
    +B1007174543700N00624765EA0222702254000
    +B1007204543694N00624727EA0222302249000
    +B1007234543688N00624689EA0221802245000
    +B1007264543683N00624651EA0221502241000
    +B1007294543678N00624613EA0221002237000
    +B1007324543673N00624575EA0220702233000
    +B1007354543667N00624537EA0220202229000
    +B1007384543661N00624499EA0219802224000
    +B1007414543653N00624461EA0219302220000
    +B1007444543645N00624424EA0218902216000
    +B1007474543637N00624387EA0218402212000
    +B1007504543629N00624349EA0217902208000
    +B1007534543621N00624311EA0217702204000
    +B1007564543614N00624275EA0217202200000
    +B1007594543607N00624237EA0216802195000
    +B1008024543600N00624200EA0216302191000
    +B1008054543594N00624163EA0215902187000
    +B1008084543587N00624126EA0215402183000
    +B1008114543581N00624089EA0215002178000
    +B1008144543573N00624052EA0214502174000
    +B1008174543566N00624016EA0214202170000
    +B1008204543558N00623981EA0213802166000
    +B1008244543548N00623933EA0213302160000
    +B1008274543541N00623897EA0212902156000
    +B1008304543534N00623861EA0212502152000
    +B1008334543526N00623826EA0212002148000
    +B1008364543519N00623790EA0211502144000
    +B1008394543511N00623754EA0211102140000
    +B1008424543503N00623719EA0210602135000
    +B1008454543495N00623683EA0210202131000
    +B1008484543486N00623648EA0209702126000
    +B1008514543477N00623612EA0209302122000
    +B1008544543468N00623577EA0208902117000
    +B1008574543460N00623542EA0208402113000
    +B1009004543451N00623507EA0208002108000
    +B1009034543440N00623473EA0207502104000
    +B1009064543429N00623439EA0207202099000
    +B1009094543418N00623407EA0206702095000
    +B1009124543406N00623374EA0206302090000
    +B1009154543395N00623341EA0205802086000
    +B1009184543384N00623308EA0205302082000
    +B1009214543373N00623274EA0204902078000
    +B1009244543362N00623241EA0204502073000
    +B1009274543351N00623208EA0204002069000
    +B1009304543340N00623175EA0203602065000
    +B1009334543329N00623142EA0203002060000
    +B1009364543318N00623110EA0202702055000
    +B1009404543303N00623066EA0202202049000
    +B1009434543292N00623034EA0201702044000
    +B1009464543281N00623001EA0201202039000
    +B1009494543270N00622968EA0200802034000
    +B1009524543260N00622935EA0200402029000
    +B1009554543250N00622903EA0200102025000
    +B1009584543240N00622870EA0199702020000
    +B1010014543230N00622836EA0199202015000
    +B1010044543219N00622802EA0198902010000
    +B1010074543207N00622770EA0198502006000
    +B1010104543196N00622737EA0198002002000
    +B1010134543185N00622704EA0197601997000
    +B1010164543175N00622671EA0197101993000
    +B1010194543164N00622638EA0196701989000
    +B1010224543155N00622606EA0196201985000
    +B1010254543147N00622572EA0195701980000
    +B1010284543138N00622539EA0195301976000
    +B1010314543131N00622505EA0194701972000
    +B1010344543125N00622471EA0194401968000
    +B1010374543119N00622437EA0194001963000
    +B1010404543115N00622403EA0193501959000
    +B1010434543111N00622369EA0193001955000
    +B1010464543108N00622335EA0192501950000
    +B1010494543106N00622302EA0192001946000
    +B1010524543104N00622268EA0191501942000
    +B1010554543102N00622235EA0190901937000
    +B1010594543100N00622190EA0190401931000
    +B1011024543098N00622157EA0189801926000
    +B1011054543096N00622123EA0189201921000
    +B1011084543095N00622090EA0188701917000
    +B1011114543095N00622058EA0188301912000
    +B1011144543098N00622026EA0187701907000
    +B1011174543101N00621995EA0187101902000
    +B1011204543105N00621964EA0186501897000
    +B1011234543110N00621934EA0186001892000
    +B1011264543113N00621904EA0185501887000
    +B1011294543114N00621873EA0184901882000
    +B1011324543116N00621842EA0184501877000
    +B1011354543117N00621813EA0184001872000
    +B1011384543118N00621785EA0183301867000
    +B1011414543121N00621756EA0183101862000
    +B1011444543123N00621731EA0182101857000
    +B1011474543126N00621703EA0181401851000
    +B1011504543129N00621676EA0180801846000
    +B1011534543133N00621649EA0180201840000
    +B1011564543140N00621620EA0180801835000
    +B1011594543147N00621603EA0181001831000
    +B1012024543154N00621583EA0181301827000
    +B1012054543163N00621565EA0182001824000
    +B1012084543169N00621545EA0182201823000
    +B1012114543171N00621526EA0182501822000
    +B1012144543177N00621508EA0182801821000
    +B1012174543186N00621491EA0183501821000
    +B1012214543198N00621465EA0184101822000
    +B1012244543203N00621443EA0185001824000
    +B1012274543206N00621423EA0185201826000
    +B1012304543200N00621406EA0185001828000
    +B1012334543186N00621404EA0184501830000
    +B1012364543180N00621425EA0184101830000
    +B1012394543188N00621445EA0184501831000
    +B1012424543199N00621452EA0185101832000
    +B1012454543213N00621453EA0185701833000
    +B1012484543227N00621455EA0186701835000
    +B1012514543240N00621455EA0187101837000
    +B1012544543254N00621452EA0187001840000
    +B1012574543270N00621450EA0186901843000
    +B1013004543286N00621447EA0186701845000
    +B1013034543300N00621442EA0186201847000
    +B1013064543315N00621437EA0185701848000
    +B1013094543331N00621435EA0185701849000
    +B1013124543343N00621430EA0185201849000
    +B1013154543355N00621416EA0184801849000
    +B1013184543366N00621397EA0184601849000
    +B1013214543372N00621371EA0184701848000
    +B1013244543377N00621346EA0184801848000
    +B1013274543376N00621322EA0184401847000
    +B1013304543371N00621300EA0184601846000
    +B1013334543364N00621280EA0184401845000
    +B1013364543356N00621260EA0184101844000
    +B1013394543347N00621239EA0183801843000
    +B1013434543334N00621207EA0183701841000
    +B1013464543323N00621189EA0183301840000
    +B1013494543311N00621171EA0182801838000
    +B1013524543299N00621152EA0182301836000
    +B1013554543287N00621134EA0181801834000
    +B1013584543275N00621116EA0181701831000
    +B1014014543264N00621099EA0181001829000
    +B1014044543251N00621083EA0180701826000
    +B1014074543237N00621069EA0180601824000
    +B1014104543224N00621055EA0180501822000
    +B1014134543211N00621042EA0179801819000
    +B1014164543198N00621028EA0179401815000
    +B1014194543184N00621014EA0179401812000
    +B1014224543170N00620999EA0179401809000
    +B1014254543157N00620984EA0179601806000
    +B1014284543145N00620971EA0179801804000
    +B1014314543135N00620956EA0179901802000
    +B1014344543125N00620942EA0179701801000
    +B1014374543115N00620929EA0179501800000
    +B1014404543101N00620916EA0180001798000
    +B1014434543088N00620904EA0180601798000
    +B1014464543076N00620897EA0180501798000
    +B1014494543062N00620905EA0180701798000
    +B1014524543059N00620922EA0180701798000
    +B1014554543067N00620937EA0180901798000
    +B1014594543084N00620928EA0180601798000
    +B1015024543088N00620906EA0180801797000
    +B1015054543079N00620885EA0181501798000
    +B1015084543070N00620872EA0181501798000
    +B1015114543056N00620862EA0181201798000
    +B1015144543044N00620850EA0181301799000
    +B1015174543031N00620837EA0181001799000
    +B1015204543017N00620828EA0180701798000
    +B1015234543003N00620824EA0180301798000
    +B1015264542988N00620820EA0179901797000
    +B1015294542972N00620811EA0179901797000
    +B1015324542959N00620802EA0180101796000
    +B1015354542947N00620794EA0179701795000
    +B1015384542933N00620785EA0179101794000
    +B1015414542918N00620776EA0178301793000
    +B1015444542901N00620769EA0177601791000
    +B1015474542883N00620759EA0177701788000
    +B1015504542869N00620746EA0177501786000
    +B1015534542853N00620733EA0178201784000
    +B1015564542841N00620723EA0178701783000
    +B1015594542827N00620713EA0178801782000
    +B1016024542813N00620699EA0179401782000
    +B1016054542801N00620686EA0179401783000
    +B1016084542790N00620672EA0179101783000
    +B1016114542778N00620659EA0178601783000
    +B1016144542765N00620645EA0178401783000
    +B1016184542750N00620628EA0178201782000
    +B1016214542738N00620615EA0178101781000
    +B1016244542723N00620600EA0178401781000
    +B1016274542710N00620589EA0178801781000
    +B1016304542697N00620579EA0179301781000
    +B1016334542686N00620564EA0180201782000
    +B1016364542675N00620549EA0181001784000
    +B1016394542662N00620544EA0181501786000
    +B1016424542652N00620559EA0182701789000
    +B1016454542649N00620572EA0183801792000
    +B1016484542651N00620587EA0183601795000
    +B1016514542660N00620605EA0183501799000
    +B1016544542675N00620600EA0182901803000
    +B1016574542680N00620578EA0183001805000
    +B1017004542675N00620555EA0184201808000
    +B1017034542668N00620534EA0185601812000
    +B1017064542654N00620522EA0186901817000
    +B1017094542645N00620535EA0188001822000
    +B1017124542658N00620544EA0188801827000
    +B1017154542668N00620524EA0189801832000
    +B1017184542662N00620503EA0191601838000
    +B1017214542650N00620501EA0192701845000
    +B1017244542641N00620516EA0193701852000
    +B1017274542648N00620535EA0194101859000
    +B1017304542665N00620526EA0195201866000
    +B1017334542669N00620500EA0197101875000
    +B1017364542664N00620484EA0198101884000
    +B1017394542652N00620474EA0199101894000
    +B1017424542641N00620486EA0200101903000
    +B1017454542646N00620505EA0201501913000
    +B1017484542662N00620510EA0202501923000
    +B1017514542674N00620494EA0204301933000
    +B1017554542674N00620468EA0205201947000
    +B1017584542663N00620452EA0206601957000
    +B1018014542650N00620457EA0207401967000
    +B1018044542646N00620474EA0208801977000
    +B1018074542657N00620486EA0210201988000
    +B1018104542671N00620484EA0211302000000
    +B1018134542681N00620465EA0212502011000
    +B1018164542681N00620442EA0214002023000
    +B1018194542668N00620426EA0215402035000
    +B1018224542652N00620415EA0216502047000
    +B1018254542636N00620405EA0217202059000
    +B1018284542621N00620393EA0217602070000
    +B1018314542606N00620382EA0217602081000
    +B1018344542591N00620369EA0218002091000
    +B1018374542578N00620360EA0218702100000
    +B1018404542567N00620351EA0219102109000
    +B1018434542553N00620344EA0220202117000
    +B1018464542543N00620337EA0220602126000
    +B1018494542532N00620327EA0219802133000
    +B1018524542517N00620314EA0219302140000
    +B1018554542498N00620298EA0218802145000
    +B1018584542479N00620282EA0218702149000
    +B1019014542461N00620268EA0218002153000
    +B1019044542440N00620254EA0217702155000
    +B1019074542420N00620240EA0217402157000
    +B1019104542399N00620227EA0217402158000
    +B1019134542379N00620216EA0217002159000
    +B1019164542359N00620202EA0217102160000
    +B1019194542340N00620184EA0217702160000
    +B1019224542321N00620171EA0218102161000
    +B1019254542301N00620162EA0217702163000
    +B1019294542273N00620146EA0217602163000
    +B1019324542255N00620133EA0217202164000
    +B1019354542235N00620122EA0216802164000
    +B1019384542219N00620114EA0216702163000
    +B1019414542202N00620103EA0215802162000
    +B1019444542186N00620090EA0216002161000
    +B1019474542172N00620078EA0216302160000
    +B1019504542160N00620072EA0216902160000
    +B1019534542150N00620065EA0217002160000
    +B1019564542136N00620056EA0217702161000
    +B1019594542125N00620048EA0218202163000
    +B1020024542111N00620038EA0218302164000
    +B1020054542095N00620030EA0218902166000
    +B1020084542080N00620023EA0218702168000
    +B1020114542063N00620014EA0218902170000
    +B1020144542048N00620003EA0218902171000
    +B1020174542032N00619994EA0218802173000
    +B1020204542015N00619985EA0219102173000
    +B1020234542000N00619980EA0219002174000
    +B1020264541984N00619973EA0218302175000
    +B1020294541966N00619964EA0217602175000
    +B1020324541949N00619955EA0216602174000
    +B1020354541930N00619943EA0215602172000
    +B1020384541911N00619930EA0215602170000
    +B1020414541896N00619922EA0215102167000
    +B1020444541878N00619912EA0214502165000
    +B1020474541861N00619903EA0214002162000
    +B1020514541837N00619889EA0213202157000
    +B1020544541819N00619876EA0213002154000
    +B1020574541802N00619862EA0212602150000
    +B1021004541784N00619847EA0212702146000
    +B1021034541770N00619834EA0212902143000
    +B1021064541755N00619821EA0212602141000
    +B1021094541739N00619808EA0212802138000
    +B1021124541722N00619791EA0212802136000
    +B1021154541704N00619777EA0213502135000
    +B1021184541690N00619765EA0215002135000
    +B1021214541679N00619754EA0215402136000
    +B1021244541664N00619744EA0216102137000
    +B1021274541650N00619751EA0216602139000
    +B1021304541648N00619772EA0217402142000
    +B1021334541660N00619785EA0218002145000
    +B1021364541675N00619776EA0218602148000
    +B1021394541681N00619752EA0219802151000
    +B1021424541680N00619727EA0220202155000
    +B1021454541673N00619704EA0220902159000
    +B1021484541665N00619688EA0221702164000
    +B1021514541653N00619683EA0221902169000
    +B1021544541643N00619695EA0222202174000
    +B1021574541646N00619716EA0222802179000
    +B1022004541662N00619722EA0223202183000
    +B1022034541677N00619707EA0223602188000
    +B1022064541679N00619683EA0224202193000
    +B1022094541670N00619665EA0225002197000
    +B1022124541660N00619652EA0225602202000
    +B1022154541647N00619651EA0225502207000
    +B1022184541640N00619666EA0225702212000
    +B1022214541648N00619682EA0226002216000
    +B1022244541663N00619677EA0225802220000
    +B1022274541666N00619651EA0226602223000
    +B1022314541654N00619633EA0227302228000
    +B1022344541642N00619623EA0226802232000
    +B1022374541626N00619612EA0226602235000
    +B1022404541608N00619595EA0225802237000
    +B1022434541590N00619573EA0225502238000
    +B1022464541573N00619552EA0225202239000
    +B1022494541555N00619529EA0224302239000
    +B1022524541537N00619503EA0224202239000
    +B1022554541521N00619479EA0223902239000
    +B1022584541504N00619453EA0223702238000
    +B1023014541486N00619428EA0223402237000
    +B1023044541469N00619404EA0223002236000
    +B1023074541452N00619379EA0222502234000
    +B1023104541435N00619355EA0221902233000
    +B1023134541418N00619329EA0221302231000
    +B1023164541402N00619302EA0220702229000
    +B1023194541386N00619277EA0220002226000
    +B1023224541369N00619251EA0219502223000
    +B1023254541352N00619225EA0218902220000
    +B1023284541335N00619200EA0218002216000
    +B1023314541318N00619173EA0217402212000
    +B1023344541302N00619144EA0216602208000
    +B1023374541285N00619115EA0216102204000
    +B1023404541269N00619085EA0215402199000
    +B1023434541252N00619056EA0214702194000
    +B1023464541235N00619028EA0214302189000
    +B1023504541217N00618991EA0213702181000
    +B1023534541203N00618961EA0213202176000
    +B1023564541191N00618929EA0213002171000
    +B1023594541180N00618898EA0212802166000
    +B1024024541169N00618867EA0212502160000
    +B1024054541157N00618836EA0211802155000
    +B1024084541144N00618804EA0211302150000
    +B1024114541132N00618775EA0210702145000
    +B1024144541117N00618745EA0209902139000
    +B1024174541105N00618713EA0209702134000
    +B1024204541093N00618684EA0209002129000
    +B1024234541081N00618652EA0208402124000
    +B1024264541069N00618620EA0208002118000
    +B1024294541058N00618590EA0207302113000
    +B1024324541046N00618559EA0207002108000
    +B1024354541034N00618528EA0206602103000
    +B1024384541024N00618497EA0206002098000
    +B1024414541013N00618465EA0205502093000
    +B1024444541002N00618434EA0205302088000
    +B1024474540991N00618404EA0204902083000
    +B1024504540980N00618373EA0204502079000
    +B1024534540968N00618343EA0204002074000
    +B1024564540955N00618312EA0203602070000
    +B1024594540942N00618283EA0203102066000
    +B1025024540929N00618255EA0202602061000
    +B1025064540912N00618215EA0202302055000
    +B1025094540899N00618185EA0201702051000
    +B1025124540886N00618157EA0201102046000
    +B1025154540873N00618127EA0200602041000
    +B1025184540861N00618098EA0200202037000
    +B1025214540849N00618068EA0199702032000
    +B1025244540837N00618038EA0199302027000
    +B1025274540825N00618007EA0199102023000
    +B1025304540815N00617976EA0198502018000
    +B1025334540804N00617945EA0198202013000
    +B1025364540794N00617913EA0197802009000
    +B1025394540785N00617881EA0197302005000
    +B1025424540776N00617849EA0196902000000
    +B1025454540767N00617816EA0196501996000
    +B1025484540758N00617784EA0196101991000
    +B1025514540748N00617752EA0195801987000
    +B1025544540739N00617721EA0195301982000
    +B1025574540729N00617690EA0194901978000
    +B1026004540720N00617658EA0194501973000
    +B1026034540711N00617627EA0193901969000
    +B1026064540702N00617595EA0193501964000
    +B1026094540693N00617562EA0193001959000
    +B1026124540684N00617531EA0192501954000
    +B1026154540675N00617499EA0191901950000
    +B1026184540666N00617466EA0191201945000
    +B1026224540653N00617421EA0190701938000
    +B1026254540644N00617386EA0190501933000
    +B1026284540638N00617353EA0190801929000
    +B1026314540634N00617320EA0190701925000
    +B1026344540629N00617290EA0191301922000
    +B1026374540623N00617268EA0191601920000
    +B1026404540616N00617245EA0191901918000
    +B1026434540611N00617223EA0192201917000
    +B1026464540609N00617202EA0191801916000
    +B1026494540604N00617177EA0191601915000
    +B1026524540600N00617152EA0191901914000
    +B1026554540596N00617129EA0191901913000
    +B1026584540594N00617104EA0192501912000
    +B1027014540593N00617077EA0193601912000
    +B1027044540592N00617051EA0194701914000
    +B1027074540585N00617028EA0195801916000
    +B1027104540570N00617021EA0196701920000
    +B1027134540561N00617033EA0197401924000
    +B1027164540568N00617045EA0197801928000
    +B1027194540582N00617036EA0198601933000
    +B1027224540596N00617015EA0200201938000
    +B1027254540598N00616995EA0201101944000
    +B1027284540591N00616979EA0201401950000
    +B1027314540577N00616974EA0202101956000
    +B1027344540567N00616984EA0203101963000
    +B1027374540569N00616996EA0204001966000
    +B1027404540582N00617002EA0204901975000
    +B1027434540595N00616989EA0206001982000
    +B1027464540599N00616965EA0206801991000
    +B1027494540594N00616950EA0206901999000
    +B1027534540577N00616938EA0207502009000
    +B1027564540567N00616940EA0208402015000
    +B1027594540564N00616950EA0208702020000
    +B1028024540572N00616963EA0209702028000
    +B1028054540584N00616961EA0210302036000
    +B1028084540596N00616947EA0210902043000
    +B1028114540600N00616929EA0211202050000
    +B1028144540597N00616909EA0211002057000
    +B1028174540589N00616888EA0211402063000
    +B1028204540580N00616870EA0211802068000
    +B1028234540571N00616853EA0211602073000
    +B1028264540558N00616835EA0211102078000
    +B1028294540543N00616819EA0210502081000
    +B1028324540525N00616806EA0209802084000
    +B1028354540506N00616790EA0209002085000
    +B1028384540486N00616771EA0208502086000
    +B1028414540465N00616752EA0207902085000
    +B1028444540445N00616732EA0207302084000
    +B1028474540424N00616713EA0206502081000
    +B1028504540403N00616692EA0206102079000
    +B1028534540382N00616673EA0205602076000
    +B1028564540361N00616653EA0204902072000
    +B1028594540340N00616632EA0204502069000
    +B1029024540320N00616612EA0203802065000
    +B1029054540300N00616592EA0203202060000
    +B1029084540280N00616574EA0202702056000
    +B1029114540260N00616556EA0202102051000
    +B1029154540233N00616533EA0201502044000
    +B1029184540213N00616515EA0201002039000
    +B1029214540193N00616498EA0200402035000
    +B1029244540173N00616480EA0199902030000
    +B1029274540153N00616463EA0199402025000
    +B1029304540132N00616448EA0198802019000
    +B1029334540112N00616433EA0198202014000
    +B1029364540091N00616419EA0197702009000
    +B1029394540072N00616406EA0197302004000
    +B1029424540053N00616392EA0196501999000
    +B1029454540033N00616379EA0196001993000
    +B1029484540013N00616366EA0195601988000
    +B1029514539994N00616353EA0195101983000
    +B1029544539974N00616339EA0194601978000
    +B1029574539955N00616325EA0194101973000
    +B1030004539936N00616311EA0193601967000
    +B1030034539916N00616297EA0193101962000
    +B1030064539896N00616283EA0192601957000
    +B1030094539877N00616269EA0192201952000
    +B1030124539857N00616257EA0191801947000
    +B1030154539837N00616245EA0191301942000
    +B1030184539817N00616234EA0191001937000
    +B1030214539797N00616224EA0190601933000
    +B1030244539776N00616215EA0190201928000
    +B1030274539755N00616205EA0189801923000
    +B1030314539728N00616191EA0189601918000
    +B1030344539707N00616180EA0189201913000
    +B1030374539686N00616169EA0188801909000
    +B1030404539666N00616159EA0188501905000
    +B1030434539646N00616148EA0188101901000
    +B1030464539626N00616139EA0187701897000
    +B1030494539605N00616129EA0187401893000
    +B1030524539585N00616119EA0187101889000
    +B1030554539565N00616109EA0186801885000
    +B1030584539544N00616099EA0186401882000
    +B1031014539524N00616090EA0186001878000
    +B1031044539504N00616081EA0185601874000
    +B1031074539483N00616073EA0185001870000
    +B1031104539462N00616065EA0184901866000
    +B1031134539443N00616058EA0184301862000
    +B1031164539425N00616052EA0184401859000
    +B1031194539409N00616045EA0183701855000
    +B1031224539390N00616036EA0183801852000
    +B1031254539374N00616025EA0184201849000
    +B1031284539362N00616015EA0184401847000
    +B1031314539350N00616004EA0184701845000
    +B1031344539339N00615993EA0184701844000
    +B1031374539327N00615981EA0184601843000
    +B1031404539314N00615972EA0184201842000
    +B1031434539300N00615964EA0183601840000
    +B1031474539280N00615953EA0183001838000
    +B1031504539263N00615947EA0182501835000
    +B1031534539247N00615939EA0182201832000
    +B1031564539232N00615931EA0181801830000
    +B1031594539216N00615924EA0181501827000
    +B1032024539198N00615918EA0181301824000
    +B1032054539183N00615911EA0181201821000
    +B1032084539167N00615905EA0180601818000
    +B1032114539151N00615900EA0180201815000
    +B1032144539135N00615897EA0179901812000
    +B1032174539119N00615892EA0180201809000
    +B1032204539106N00615886EA0180501807000
    +B1032234539093N00615880EA0181401806000
    +B1032264539082N00615877EA0182001806000
    +B1032294539071N00615878EA0182501806000
    +B1032324539059N00615882EA0183101807000
    +B1032354539056N00615896EA0183301808000
    +B1032384539070N00615909EA0184201810000
    +B1032414539087N00615903EA0184601812000
    +B1032444539099N00615880EA0185101815000
    +B1032474539097N00615856EA0185701818000
    +B1032504539088N00615837EA0186601821000
    +B1032534539079N00615825EA0186701825000
    +B1032564539065N00615820EA0186901829000
    +B1032594539052N00615821EA0186601832000
    +B1033024539040N00615836EA0186401836000
    +B1033054539030N00615848EA0186101838000
    +B1033084539016N00615852EA0185801841000
    +B1033124538998N00615845EA0185601843000
    +B1033154538986N00615832EA0185701845000
    +B1033184538972N00615822EA0185301845000
    +B1033214538959N00615812EA0184801846000
    +B1033244538944N00615799EA0184801846000
    +B1033274538932N00615783EA0185201845000
    +B1033304538922N00615767EA0186201846000
    +B1033334538914N00615753EA0187001848000
    +B1033364538903N00615747EA0187501850000
    +B1033394538895N00615761EA0188001852000
    +B1033424538901N00615776EA0189001855000
    +B1033454538914N00615775EA0189601858000
    +B1033484538927N00615762EA0190701862000
    +B1033514538933N00615740EA0191101866000
    +B1033544538929N00615718EA0192101871000
    +B1033574538918N00615709EA0192901875000
    +B1034004538908N00615717EA0193601880000
    +B1034034538909N00615734EA0194101885000
    +B1034064538921N00615744EA0195401891000
    +B1034094538935N00615733EA0196401897000
    +B1034124538944N00615711EA0197601904000
    +B1034154538942N00615687EA0198201911000
    +B1034184538930N00615678EA0199001919000
    +B1034214538925N00615688EA0199601924000
    +B1034244538932N00615704EA0200301932000
    +B1034274538948N00615703EA0201701940000
    +B1034304538958N00615681EA0202901949000
    +B1034334538961N00615654EA0203801958000
    +B1034364538954N00615633EA0204501967000
    +B1034394538941N00615628EA0204901976000
    +B1034424538934N00615642EA0205301984000
    +B1034454538939N00615657EA0205901991000
    +B1034484538954N00615665EA0207101998000
    +B1034524538974N00615656EA0207802009000
    +B1034554538987N00615637EA0209102017000
    +B1034584538990N00615613EA0209702025000
    +B1035014538981N00615594EA0210302033000
    +B1035044538966N00615593EA0210802041000
    +B1035074538961N00615608EA0211202048000
    +B1035104538972N00615622EA0212202055000
    +B1035134538988N00615618EA0213302062000
    +B1035164538998N00615599EA0214202070000
    +B1035194538998N00615574EA0214802077000
    +B1035224538987N00615556EA0215402085000
    +B1035254538972N00615553EA0216002092000
    +B1035284538966N00615567EA0216202098000
    +B1035314538973N00615584EA0216502105000
    +B1035344538989N00615586EA0217102111000
    +B1035374539004N00615576EA0218102118000
    +B1035404539015N00615559EA0219002125000
    +B1035434539024N00615538EA0219502132000
    +B1035464539020N00615514EA0220002139000
    +B1035494539004N00615506EA0220902146000
    +B1035524538994N00615516EA0221402152000
    +B1035554538996N00615535EA0222102159000
    +B1035584539009N00615539EA0222702166000
    +B1036014539019N00615518EA0223802173000
    +B1036044539018N00615494EA0224302179000
    +B1036074539010N00615476EA0224802185000
    +B1036104538996N00615469EA0225302191000
    +B1036134538984N00615478EA0225602197000
    +B1036164538982N00615495EA0225702203000
    +B1036194538990N00615509EA0225902208000
    +B1036224539007N00615511EA0226502213000
    +B1036264539025N00615488EA0227402221000
    +B1036294539035N00615466EA0228102226000
    +B1036324539041N00615442EA0228202232000
    +B1036354539035N00615417EA0228602237000
    +B1036384539023N00615403EA0228902242000
    +B1036414539010N00615395EA0229002247000
    +B1036444538994N00615388EA0229202251000
    +B1036474538977N00615383EA0229202256000
    +B1036504538960N00615379EA0228902259000
    +B1036534538942N00615376EA0228102262000
    +B1036564538922N00615372EA0227702263000
    +B1036594538903N00615368EA0227202264000
    +B1037024538883N00615365EA0226702264000
    +B1037054538864N00615364EA0226302264000
    +B1037084538845N00615363EA0225802263000
    +B1037114538825N00615362EA0225402261000
    +B1037144538806N00615360EA0224902259000
    +B1037174538787N00615358EA0224302257000
    +B1037204538768N00615356EA0224102255000
    +B1037234538750N00615353EA0223502252000
    +B1037264538731N00615351EA0223302250000
    +B1037294538712N00615347EA0222802247000
    +B1037324538693N00615344EA0222602244000
    +B1037354538674N00615341EA0222302241000
    +B1037384538656N00615338EA0221902238000
    +B1037414538637N00615336EA0221502235000
    +B1037444538618N00615333EA0221202232000
    +B1037484538594N00615329EA0220802228000
    +B1037514538575N00615326EA0220602225000
    +B1037544538557N00615323EA0220202222000
    +B1037574538538N00615319EA0219802218000
    +B1038004538520N00615316EA0219502215000
    +B1038034538502N00615312EA0219102212000
    +B1038064538484N00615308EA0218802209000
    +B1038094538466N00615304EA0218402205000
    +B1038124538448N00615300EA0218202202000
    +B1038154538432N00615298EA0218002199000
    +B1038184538418N00615296EA0217802196000
    +B1038214538402N00615292EA0217502193000
    +B1038244538386N00615290EA0217602190000
    +B1038274538371N00615286EA0217802188000
    +B1038304538357N00615287EA0217602186000
    +B1038334538342N00615287EA0217402184000
    +B1038364538325N00615284EA0217402182000
    +B1038394538307N00615279EA0217402180000
    +B1038424538289N00615271EA0217102179000
    +B1038454538269N00615266EA0216902177000
    +B1038484538250N00615261EA0216602175000
    +B1038514538231N00615255EA0216402173000
    +B1038544538209N00615248EA0216802171000
    +B1038574538188N00615242EA0217002170000
    +B1039004538167N00615234EA0217402169000
    +B1039044538141N00615224EA0216802169000
    +B1039074538122N00615221EA0216602168000
    +B1039104538106N00615221EA0217002167000
    +B1039134538094N00615218EA0216102166000
    +B1039164538077N00615210EA0215602164000
    +B1039194538058N00615201EA0215202162000
    +B1039224538040N00615195EA0214302160000
    +B1039254538021N00615187EA0213502157000
    +B1039284538005N00615177EA0213702154000
    +B1039314537991N00615166EA0213502151000
    +B1039344537974N00615153EA0214202148000
    +B1039374537959N00615143EA0214102147000
    +B1039404537943N00615132EA0213402145000
    +B1039434537925N00615120EA0212802143000
    +B1039464537909N00615104EA0213102140000
    +B1039494537895N00615089EA0213002138000
    +B1039524537879N00615074EA0212802136000
    +B1039554537865N00615058EA0212302134000
    +B1039584537849N00615041EA0211702132000
    +B1040014537832N00615025EA0211802129000
    +B1040044537817N00615009EA0211202127000
    +B1040074537802N00614989EA0210902124000
    +B1040104537787N00614968EA0210402121000
    +B1040134537772N00614947EA0210602118000
    +B1040164537758N00614928EA0210202115000
    +B1040194537743N00614905EA0210602113000
    +B1040224537729N00614885EA0211802111000
    +B1040264537715N00614866EA0212202111000
    +B1040294537704N00614852EA0212202111000
    +B1040324537696N00614832EA0212502111000
    +B1040354537688N00614815EA0212602112000
    +B1040384537679N00614796EA0212602113000
    +B1040414537672N00614777EA0212502114000
    +B1040444537663N00614759EA0212302114000
    +B1040474537655N00614741EA0211802114000
    +B1040504537646N00614720EA0211402113000
    +B1040534537638N00614699EA0212002113000
    +B1040564537630N00614681EA0212102113000
    +B1040594537620N00614659EA0212602113000
    +B1041024537610N00614639EA0213402114000
    +B1041054537602N00614622EA0213502115000
    +B1041084537593N00614605EA0213702117000
    +B1041114537584N00614587EA0213802118000
    +B1041144537575N00614567EA0213802120000
    +B1041174537566N00614548EA0213402121000
    +B1041204537556N00614528EA0213202122000
    +B1041234537545N00614512EA0212602122000
    +B1041264537533N00614496EA0212302121000
    +B1041294537521N00614479EA0212102121000
    +B1041324537508N00614461EA0211602120000
    +B1041354537493N00614444EA0211102118000
    +B1041384537479N00614426EA0210702116000
    +B1041414537464N00614408EA0210602114000
    +B1041444537450N00614391EA0210902113000
    +B1041474537435N00614374EA0210902111000
    +B1041514537415N00614355EA0210902110000
    +B1041544537401N00614341EA0210702108000
    +B1041574537388N00614326EA0210302107000
    +B1042004537376N00614311EA0209702105000
    +B1042034537361N00614294EA0208402103000
    +B1042064537345N00614274EA0208202100000
    +B1042094537330N00614256EA0207602096000
    +B1042124537313N00614238EA0207102092000
    +B1042154537296N00614220EA0206602089000
    +B1042184537278N00614202EA0206102085000
    +B1042214537261N00614183EA0205702081000
    +B1042244537244N00614164EA0205102077000
    +B1042274537227N00614145EA0204702072000
    +B1042304537213N00614127EA0204202068000
    +B1042334537199N00614110EA0203802064000
    +B1042364537182N00614092EA0204102060000
    +B1042394537167N00614076EA0204702056000
    +B1042424537157N00614060EA0205402054000
    +B1042454537146N00614043EA0205902053000
    +B1042484537136N00614029EA0205802052000
    +B1042514537124N00614015EA0206002052000
    +B1042544537114N00614001EA0206102052000
    +B1042574537105N00613987EA0206302051000
    +B1043004537096N00613970EA0206502051000
    +B1043034537088N00613956EA0206402052000
    +B1043064537078N00613939EA0206502052000
    +B1043094537068N00613922EA0206802052000
    +B1043134537056N00613901EA0206202053000
    +B1043164537045N00613884EA0205102053000
    +B1043194537031N00613866EA0204902051000
    +B1043224537019N00613852EA0204902050000
    +B1043254537009N00613834EA0204202049000
    +B1043284536998N00613813EA0204602047000
    +B1043314536990N00613799EA0204302047000
    +B1043344536979N00613784EA0203602045000
    +B1043374536967N00613766EA0203702043000
    +B1043404536957N00613751EA0203202042000
    +B1043434536945N00613737EA0202802039000
    +B1043464536932N00613727EA0202402037000
    +B1043494536918N00613715EA0203002035000
    +B1043524536906N00613704EA0203402033000
    +B1043554536894N00613689EA0203102032000
    +B1043584536879N00613672EA0203102031000
    +B1044014536865N00613657EA0203602030000
    +B1044044536855N00613643EA0204302030000
    +B1044074536847N00613628EA0204302030000
    +B1044104536839N00613609EA0204702031000
    +B1044134536833N00613590EA0205102032000
    +B1044164536826N00613569EA0205402034000
    +B1044194536820N00613549EA0205302036000
    +B1044224536812N00613525EA0205102038000
    +B1044254536804N00613502EA0204802039000
    +B1044284536796N00613481EA0204302039000
    +B1044314536787N00613462EA0203702039000
    +B1044354536774N00613434EA0203302038000
    +B1044384536764N00613414EA0203002037000
    +B1044414536754N00613395EA0202602035000
    +B1044444536745N00613376EA0202102034000
    +B1044474536735N00613356EA0201602031000
    +B1044504536725N00613335EA0201302029000
    +B1044534536716N00613314EA0201802026000
    +B1044564536707N00613296EA0202502025000
    +B1044594536697N00613282EA0203002024000
    +B1045024536686N00613267EA0203602024000
    +B1045054536673N00613253EA0204602025000
    +B1045084536659N00613246EA0205302027000
    +B1045114536649N00613254EA0205802029000
    +B1045144536647N00613270EA0206102032000
    +B1045174536658N00613282EA0206002035000
    +B1045204536674N00613276EA0206302037000
    +B1045234536684N00613253EA0207102039000
    +B1045264536688N00613228EA0208002042000
    +B1045294536690N00613200EA0209102046000
    +B1045324536687N00613176EA0209702050000
    +B1045354536675N00613163EA0210202055000
    +B1045384536666N00613172EA0210602059000
    +B1045414536671N00613189EA0211002064000
    +B1045444536688N00613188EA0211802068000
    +B1045474536705N00613173EA0213202073000
    +B1045504536718N00613154EA0214402080000
    +B1045534536726N00613129EA0215102086000
    +B1045564536720N00613107EA0215902093000
    +B1045594536708N00613097EA0217102100000
    +B1046024536700N00613102EA0217902105000
    +B1046064536708N00613119EA0218702116000
    +B1046094536726N00613120EA0220002124000
    +B1046124536740N00613101EA0221002133000
    +B1046154536741N00613074EA0221602142000
    +B1046184536734N00613056EA0222002150000
    +B1046214536723N00613055EA0222702157000
    +B1046244536722N00613071EA0223402165000
    +B1046274536730N00613084EA0224102172000
    +B1046304536746N00613090EA0224802180000
    +B1046334536766N00613080EA0225302187000
    +B1046364536778N00613056EA0226002194000
    +B1046394536776N00613032EA0225902201000
    +B1046424536766N00613024EA0226002207000
    +B1046454536757N00613031EA0226402211000
    +B1046484536757N00613045EA0226502216000
    +B1046514536770N00613055EA0227002222000
    +B1046544536785N00613043EA0227102227000
    +B1046574536789N00613019EA0227102232000
    +B1047004536781N00613002EA0227202236000
    +B1047034536768N00612988EA0226702239000
    +B1047064536754N00612971EA0226702242000
    +B1047094536745N00612954EA0226702244000
    +B1047124536736N00612939EA0226502246000
    +B1047154536726N00612922EA0226702247000
    +B1047184536716N00612904EA0226702249000
    +B1047214536706N00612882EA0227902251000
    +B1047244536697N00612865EA0228502253000
    +B1047274536687N00612848EA0229102256000
    +B1047304536679N00612829EA0230002259000
    +B1047334536673N00612810EA0230402263000
    +B1047364536667N00612789EA0229902267000
    +B1047394536659N00612768EA0229002270000
    +B1047434536644N00612742EA0227602270000
    +B1047464536631N00612720EA0227302270000
    +B1047494536620N00612700EA0226202269000
    +B1047524536607N00612675EA0225502267000
    +B1047554536596N00612651EA0224902264000
    +B1047584536584N00612627EA0224302261000
    +B1048014536573N00612603EA0224902259000
    +B1048044536564N00612581EA0225402257000
    +B1048074536556N00612561EA0225902257000
    +B1048104536547N00612540EA0226502256000
    +B1048134536541N00612521EA0226902257000
    +B1048164536532N00612502EA0227502258000
    +B1048194536527N00612485EA0227902259000
    +B1048224536519N00612463EA0228202261000
    +B1048254536512N00612444EA0228402263000
    +B1048284536504N00612426EA0228602265000
    +B1048314536493N00612409EA0229602267000
    +B1048344536485N00612402EA0230302269000
    +B1048374536477N00612395EA0230002272000
    +B1048404536470N00612380EA0229602274000
    +B1048434536460N00612363EA0229402276000
    +B1048464536450N00612345EA0229202278000
    +B1048494536441N00612330EA0228202278000
    +B1048524536430N00612312EA0227602278000
    +B1048554536417N00612290EA0227102277000
    +B1048584536403N00612270EA0227702276000
    +B1049014536392N00612252EA0227802275000
    +B1049044536380N00612236EA0228202274000
    +B1049074536369N00612222EA0228602275000
    +B1049104536360N00612210EA0228602275000
    +B1049134536352N00612193EA0228502276000
    +B1049174536337N00612168EA0228602276000
    +B1049204536321N00612147EA0230002277000
    +B1049234536311N00612129EA0230802280000
    +B1049264536301N00612109EA0231802282000
    +B1049294536291N00612095EA0233202287000
    +B1049324536286N00612086EA0233002290000
    +B1049354536275N00612073EA0233602294000
    +B1049384536267N00612063EA0234002299000
    +B1049414536258N00612049EA0233902303000
    +B1049444536249N00612035EA0234202306000
    +B1049474536240N00612020EA0233802310000
    +B1049504536229N00612002EA0233802313000
    +B1049534536220N00611985EA0233402315000
    +B1049564536209N00611966EA0233002317000
    +B1049594536199N00611948EA0233102318000
    +B1050024536190N00611932EA0233202319000
    +B1050054536181N00611917EA0233402319000
    +B1050084536172N00611901EA0233402320000
    +B1050114536162N00611885EA0233302321000
    +B1050144536152N00611871EA0233602322000
    +B1050174536143N00611854EA0234302323000
    +B1050204536137N00611837EA0234902324000
    +B1050234536132N00611821EA0235202326000
    +B1050264536124N00611804EA0235902328000
    +B1050294536117N00611787EA0236102331000
    +B1050324536110N00611767EA0235902334000
    +B1050354536100N00611747EA0235902336000
    +B1050384536089N00611727EA0235802338000
    +B1050414536080N00611704EA0235402339000
    +B1050444536072N00611681EA0234902340000
    +B1050474536063N00611658EA0234602341000
    +B1050514536050N00611630EA0234102340000
    +B1050544536039N00611608EA0234102340000
    +B1050574536029N00611585EA0234002339000
    +B1051004536018N00611562EA0233902339000
    +B1051034536007N00611538EA0234102338000
    +B1051064535998N00611515EA0234102338000
    +B1051094535989N00611491EA0234302338000
    +B1051124535979N00611467EA0235602338000
    +B1051154535971N00611452EA0236102339000
    +B1051184535963N00611434EA0236202341000
    +B1051214535953N00611414EA0236802343000
    +B1051244535945N00611389EA0236802345000
    +B1051274535940N00611362EA0236602347000
    +B1051304535934N00611336EA0236202348000
    +B1051334535926N00611310EA0236202349000
    +B1051364535919N00611285EA0235902349000
    +B1051394535910N00611259EA0235702349000
    +B1051424535902N00611233EA0235602349000
    +B1051454535894N00611206EA0235702349000
    +B1051484535888N00611180EA0236402350000
    +B1051514535884N00611154EA0236602351000
    +B1051544535880N00611128EA0236402352000
    +B1051574535877N00611102EA0235902353000
    +B1052004535872N00611073EA0235702353000
    +B1052034535867N00611045EA0235402353000
    +B1052074535865N00611005EA0235602353000
    +B1052104535860N00610977EA0235602353000
    +B1052134535856N00610949EA0235802353000
    +B1052164535851N00610921EA0235202353000
    +B1052194535845N00610891EA0235502352000
    +B1052224535840N00610865EA0235502352000
    +B1052254535835N00610835EA0235402352000
    +B1052284535830N00610808EA0235702352000
    +B1052314535825N00610782EA0235702352000
    +B1052344535820N00610757EA0236202352000
    +B1052374535815N00610734EA0236002352000
    +B1052404535809N00610710EA0236402353000
    +B1052434535805N00610691EA0235902353000
    +B1052464535798N00610665EA0236302353000
    +B1052494535792N00610642EA0236902354000
    +B1052524535786N00610619EA0236902355000
    +B1052554535778N00610597EA0237102356000
    +B1052584535774N00610576EA0237002357000
    +B1053014535769N00610551EA0237302358000
    +B1053044535763N00610528EA0238002360000
    +B1053074535756N00610508EA0238402362000
    +B1053104535747N00610487EA0239102364000
    +B1053134535735N00610467EA0240102366000
    +B1053164535723N00610447EA0240902370000
    +B1053194535713N00610423EA0242102374000
    +B1053224535703N00610402EA0243002379000
    +B1053254535688N00610384EA0243802385000
    +B1053284535670N00610384EA0244802391000
    +B1053314535664N00610395EA0245502396000
    +B1053344535677N00610396EA0246402401000
    +B1053374535686N00610373EA0247002408000
    +B1053414535675N00610339EA0247702416000
    +B1053444535660N00610320EA0248002422000
    +B1053474535648N00610294EA0248302428000
    +B1053504535644N00610265EA0248402433000
    +B1053534535644N00610235EA0248302438000
    +B1053564535647N00610204EA0248002442000
    +B1053594535652N00610172EA0247902445000
    +B1054024535657N00610143EA0247602447000
    +B1054054535654N00610116EA0247902450000
    +B1054084535651N00610095EA0248002453000
    +B1054114535644N00610075EA0247502455000
    +B1054144535637N00610054EA0246902456000
    +B1054174535634N00610027EA0246102457000
    +B1054204535630N00609998EA0245702457000
    +B1054234535626N00609970EA0244902456000
    +B1054264535622N00609939EA0244902454000
    +B1054294535618N00609911EA0244602453000
    +B1054324535611N00609879EA0244002451000
    +B1054354535602N00609842EA0244802450000
    +B1054384535594N00609814EA0244902449000
    +B1054414535586N00609787EA0244702448000
    +B1054444535579N00609758EA0244902447000
    +B1054474535572N00609728EA0244702447000
    +B1054504535567N00609696EA0244402446000
    +B1054534535563N00609665EA0244102445000
    +B1054564535557N00609633EA0243702444000
    +B1054594535550N00609601EA0243402443000
    +B1055024535542N00609569EA0243102441000
    +B1055064535531N00609530EA0242902439000
    +B1055094535522N00609502EA0242802437000
    +B1055124535513N00609473EA0242602435000
    +B1055154535504N00609446EA0242202434000
    +B1055184535493N00609420EA0241602432000
    +B1055214535483N00609392EA0241202430000
    +B1055244535474N00609365EA0240902428000
    +B1055274535465N00609338EA0240302425000
    +B1055304535456N00609309EA0240002423000
    +B1055334535448N00609280EA0239602420000
    +B1055364535440N00609251EA0239202417000
    +B1055394535432N00609222EA0238802414000
    +B1055424535424N00609192EA0238402411000
    +B1055454535417N00609163EA0238002408000
    +B1055484535409N00609133EA0237602404000
    +B1055514535401N00609104EA0237202400000
    +B1055544535393N00609074EA0236802396000
    +B1055574535385N00609045EA0236602393000
    +B1056004535376N00609015EA0236302389000
    +B1056034535365N00608987EA0236102387000
    +B1056064535355N00608960EA0236002383000
    +B1056094535344N00608932EA0235702380000
    +B1056124535333N00608904EA0235302377000
    +B1056154535322N00608877EA0235202374000
    +B1056184535311N00608851EA0234902370000
    +B1056214535299N00608826EA0234602367000
    +B1056254535283N00608791EA0234502362000
    +B1056284535272N00608764EA0234302359000
    +B1056314535263N00608735EA0233902356000
    +B1056344535253N00608706EA0233602354000
    +B1056374535243N00608679EA0233302351000
    +B1056404535232N00608652EA0233102347000
    +B1056434535221N00608624EA0232602344000
    +B1056464535211N00608597EA0232402342000
    +B1056494535200N00608570EA0232002339000
    +B1056524535190N00608543EA0231702336000
    +B1056554535180N00608516EA0231302333000
    +B1056584535170N00608489EA0231002330000
    +B1057014535160N00608463EA0230502327000
    +B1057044535151N00608436EA0229902324000
    +B1057074535139N00608404EA0228802320000
    +B1057104535123N00608367EA0228502316000
    +B1057134535109N00608334EA0228402312000
    +B1057164535098N00608301EA0227702308000
    +B1057194535085N00608265EA0227302304000
    +B1057224535071N00608230EA0227102300000
    +B1057254535058N00608197EA0226202295000
    +B1057284535044N00608162EA0225702291000
    +B1057314535030N00608128EA0225302286000
    +B1057344535018N00608093EA0224602281000
    +B1057374535004N00608057EA0224202276000
    +B1057404534990N00608023EA0223702272000
    +B1057444534971N00607977EA0223002265000
    +B1057474534957N00607942EA0222502259000
    +B1057504534943N00607907EA0222002254000
    +B1057534534928N00607873EA0221402249000
    +B1057564534912N00607839EA0220902243000
    +B1057594534896N00607807EA0220302238000
    +B1058024534880N00607775EA0219702232000
    +B1058054534863N00607744EA0219202227000
    +B1058084534846N00607711EA0218702221000
    +B1058114534829N00607678EA0218102215000
    +B1058144534813N00607645EA0217502210000
    +B1058174534797N00607611EA0217102204000
    +B1058204534781N00607577EA0216402198000
    +B1058234534765N00607543EA0215902193000
    +B1058264534749N00607510EA0215402187000
    +B1058294534732N00607477EA0214902182000
    +B1058324534715N00607445EA0214502177000
    +B1058354534698N00607412EA0213902171000
    +B1058384534680N00607381EA0213502166000
    +B1058414534662N00607350EA0213002161000
    +B1058444534643N00607319EA0212502156000
    +B1058474534625N00607289EA0211902151000
    +B1058504534608N00607256EA0211602145000
    +B1058534534592N00607222EA0211202140000
    +B1058564534576N00607188EA0210602135000
    +B1059004534554N00607143EA0210102129000
    +B1059034534538N00607110EA0209802124000
    +B1059064534521N00607077EA0209102119000
    +B1059094534505N00607043EA0208702113000
    +B1059124534488N00607011EA0208402109000
    +B1059154534472N00606979EA0207702104000
    +B1059184534455N00606948EA0207402099000
    +B1059214534438N00606918EA0206902094000
    +B1059244534422N00606888EA0206502089000
    +B1059274534406N00606857EA0206102085000
    +B1059304534391N00606826EA0205802080000
    +B1059334534376N00606795EA0205202076000
    +B1059364534360N00606765EA0204802071000
    +B1059394534344N00606734EA0204302067000
    +B1059424534328N00606704EA0203902062000
    +B1059454534312N00606674EA0203402058000
    +B1059484534295N00606645EA0203002053000
    +B1059514534279N00606615EA0202702049000
    +B1059544534262N00606586EA0202102045000
    +B1059574534246N00606557EA0201602040000
    +B1100004534229N00606528EA0201102036000
    +B1100034534213N00606499EA0200702031000
    +B1100064534196N00606470EA0200202027000
    +B1100094534180N00606441EA0199902022000
    +B1100124534165N00606411EA0199302018000
    +B1100164534145N00606371EA0198902012000
    +B1100194534130N00606340EA0198402008000
    +B1100224534115N00606310EA0197902004000
    +B1100254534101N00606279EA0197501999000
    +B1100284534087N00606249EA0197001995000
    +B1100314534073N00606218EA0196501990000
    +B1100344534059N00606188EA0196001986000
    +B1100374534045N00606158EA0195501981000
    +B1100404534031N00606128EA0195001977000
    +B1100434534016N00606099EA0194501972000
    +B1100464534001N00606069EA0194001967000
    +B1100494533986N00606041EA0193501962000
    +B1100524533970N00606013EA0192801957000
    +B1100554533954N00605985EA0192501952000
    +B1100584533939N00605958EA0191801947000
    +B1101014533922N00605930EA0191301942000
    +B1101044533905N00605903EA0191201937000
    +B1101074533888N00605878EA0190601932000
    +B1101104533871N00605852EA0190101927000
    +B1101134533854N00605825EA0189601922000
    +B1101164533837N00605800EA0189201918000
    +B1101194533821N00605772EA0188801913000
    +B1101224533807N00605744EA0189101908000
    +B1101254533792N00605714EA0190001904000
    +B1101284533781N00605690EA0190901902000
    +B1101314533775N00605673EA0191101901000
    +B1101344533776N00605653EA0191801901000
    +B1101384533790N00605641EA0192801903000
    +B1101414533804N00605643EA0193901905000
    +B1101444533816N00605656EA0195001908000
    +B1101474533818N00605677EA0196301912000
    +B1101504533809N00605691EA0197201916000
    +B1101534533793N00605691EA0197801921000
    +B1101564533785N00605673EA0198001926000
    +B1101594533793N00605657EA0199401931000
    +B1102024533805N00605657EA0200401937000
    +B1102054533814N00605671EA0200701944000
    +B1102084533806N00605687EA0201201950000
    +B1102114533791N00605686EA0201801956000
    +B1102144533783N00605669EA0202501962000
    +B1102174533781N00605645EA0203901968000
    +B1102204533785N00605628EA0204701975000
    +B1102234533796N00605623EA0205201982000
    +B1102264533800N00605638EA0205801988000
    +B1102294533792N00605651EA0206001995000
    +B1102324533775N00605654EA0206802001000
    +B1102354533760N00605642EA0207502007000
    +B1102384533755N00605621EA0208402013000
    +B1102414533760N00605610EA0209002019000
    +B1102444533766N00605620EA0209302025000
    +B1102474533758N00605637EA0209602032000
    +B1102504533739N00605640EA0210002038000
    +B1102534533722N00605633EA0210302045000
    +B1102564533712N00605614EA0210002050000
    +B1102594533711N00605594EA0210202056000
    +B1103024533717N00605581EA0209602060000
    +B1103054533727N00605570EA0210102064000
    +B1103084533732N00605579EA0209802066000
    +B1103114533728N00605596EA0209502069000
    +B1103144533709N00605601EA0209102071000
    +B1103174533692N00605576EA0209202073000
    +B1103214533682N00605552EA0208502074000
    +B1103244533680N00605520EA0207502073000
    +B1103274533680N00605480EA0206602072000
    +B1103304533681N00605438EA0206802070000
    +B1103334533686N00605405EA0207302069000
    +B1103364533688N00605377EA0207002068000
    +B1103394533686N00605349EA0207002067000
    +B1103424533682N00605324EA0207402066000
    +B1103454533675N00605299EA0208102066000
    +B1103484533665N00605276EA0208902066000
    +B1103514533656N00605260EA0209502068000
    +B1103544533643N00605254EA0209802070000
    +B1103574533633N00605262EA0210302071000
    +B1104004533640N00605275EA0210602073000
    +B1104034533653N00605274EA0210902076000
    +B1104064533663N00605256EA0211502078000
    +B1104094533666N00605233EA0212002081000
    +B1104124533660N00605210EA0212402084000
    +B1104154533646N00605202EA0212702088000
    +B1104184533633N00605206EA0213102091000
    +B1104214533631N00605220EA0213202094000
    +B1104244533640N00605230EA0213202097000
    +B1104274533653N00605221EA0213402099000
    +B1104304533659N00605198EA0214002102000
    +B1104334533657N00605172EA0214702105000
    +B1104364533651N00605148EA0215302107000
    +B1104394533643N00605125EA0215902111000
    +B1104424533637N00605105EA0215802115000
    +B1104454533639N00605079EA0216002118000
    +B1104494533657N00605063EA0215602122000
    +B1104524533670N00605071EA0215402125000
    +B1104554533671N00605087EA0216002127000
    +B1104584533665N00605095EA0216502128000
    +B1105014533654N00605100EA0217002131000
    +B1105044533640N00605091EA0217402134000
    +B1105074533628N00605074EA0217802138000
    +B1105104533618N00605053EA0217702141000
    +B1105134533607N00605029EA0217602143000
    +B1105164533594N00605008EA0217802146000
    +B1105194533582N00604987EA0218102149000
    +B1105224533572N00604969EA0218202151000
    +B1105254533562N00604949EA0217802154000
    +B1105284533549N00604928EA0217602155000
    +B1105314533537N00604905EA0217302157000
    +B1105344533526N00604881EA0216302158000
    +B1105374533514N00604851EA0216102158000
    +B1105404533503N00604824EA0215402157000
    +B1105434533493N00604793EA0214802156000
    +B1105464533484N00604761EA0214802154000
    +B1105494533475N00604734EA0214302153000
    +B1105524533463N00604705EA0214202151000
    +B1105554533450N00604678EA0214002149000
    +B1105584533437N00604654EA0213502147000
    +B1106014533423N00604628EA0213102145000
    +B1106044533412N00604601EA0212802142000
    +B1106074533399N00604574EA0212502140000
    +B1106114533381N00604540EA0212402137000
    +B1106144533367N00604515EA0211902135000
    +B1106174533354N00604488EA0211602132000
    +B1106204533341N00604460EA0211002130000
    +B1106234533326N00604431EA0210602127000
    +B1106264533314N00604402EA0210402123000
    +B1106294533302N00604372EA0210002120000
    +B1106324533292N00604342EA0209802117000
    +B1106354533282N00604313EA0209202114000
    +B1106384533270N00604284EA0209002110000
    +B1106414533260N00604256EA0208802107000
    +B1106444533251N00604226EA0208202104000
    +B1106474533241N00604194EA0208002100000
    +B1106504533233N00604165EA0208002097000
    +B1106534533224N00604138EA0207302094000
    +B1106564533214N00604106EA0207102091000
    +B1106594533205N00604076EA0206702087000
    +B1107024533196N00604046EA0206102084000
    +B1107054533186N00604015EA0206002080000
    +B1107084533178N00603985EA0205402077000
    +B1107114533167N00603953EA0205302073000
    +B1107144533157N00603925EA0205002069000
    +B1107174533148N00603899EA0204502066000
    +B1107204533137N00603870EA0204402062000
    +B1107234533125N00603842EA0204302059000
    +B1107264533115N00603815EA0204202056000
    +B1107294533105N00603789EA0203802053000
    +B1107334533093N00603756EA0203402049000
    +B1107364533085N00603731EA0203202046000
    +B1107394533081N00603706EA0202902043000
    +B1107424533076N00603682EA0202602040000
    +B1107454533072N00603658EA0202502038000
    +B1107484533071N00603635EA0202202035000
    +B1107514533065N00603615EA0202102033000
    +B1107544533059N00603594EA0202002030000
    +B1107574533064N00603576EA0202002028000
    +B1108004533072N00603559EA0202202027000
    +B1108034533078N00603541EA0202102025000
    +B1108064533086N00603522EA0201702023000
    +B1108094533096N00603504EA0201202022000
    +B1108124533109N00603488EA0200902020000
    +B1108154533124N00603493EA0200702017000
    +B1108184533136N00603504EA0200402015000
    +B1108214533151N00603514EA0200202013000
    +B1108244533168N00603524EA0200602011000
    +B1108274533184N00603529EA0200902010000
    +B1108304533199N00603531EA0201302009000
    +B1108334533213N00603540EA0201402008000
    +B1108364533220N00603558EA0201802008000
    +B1108394533220N00603578EA0201902008000
    +B1108424533215N00603596EA0201902008000
    +B1108454533204N00603610EA0202002008000
    +B1108494533183N00603618EA0202202009000
    +B1108524533168N00603610EA0202002009000
    +B1108554533157N00603593EA0202302009000
    +B1108584533156N00603576EA0202102009000
    +B1109014533165N00603561EA0201702009000
    +B1109044533181N00603560EA0201802009000
    +B1109074533196N00603564EA0202402009000
    +B1109104533208N00603567EA0202402010000
    +B1109134533217N00603581EA0202202011000
    +B1109164533212N00603598EA0202202011000
    +B1109194533199N00603606EA0202402012000
    +B1109224533185N00603606EA0202302012000
    +B1109254533170N00603602EA0202302012000
    +B1109284533157N00603591EA0202002012000
    +B1109314533151N00603574EA0201702012000
    +B1109344533159N00603557EA0201202011000
    +B1109374533176N00603552EA0201402010000
    +B1109404533190N00603562EA0201402009000
    +B1109434533200N00603579EA0201602009000
    +B1109464533211N00603592EA0201602009000
    +B1109494533224N00603597EA0201502009000
    +B1109524533237N00603602EA0201502009000
    +B1109554533248N00603611EA0201302008000
    +B1109584533259N00603625EA0200802008000
    +B1110014533271N00603643EA0201002007000
    +B1110044533281N00603661EA0201402006000
    +B1110084533293N00603683EA0201802006000
    +B1110114533297N00603700EA0201802006000
    +B1110144533292N00603716EA0201202006000
    +B1110174533279N00603719EA0200602005000
    +B1110204533265N00603708EA0200902003000
    +B1110234533254N00603696EA0201002003000
    +B1110264533246N00603681EA0200502002000
    +B1110294533237N00603656EA0199602000000
    +B1110324533229N00603630EA0199801999000
    +B1110354533221N00603608EA0199601997000
    +B1110384533212N00603583EA0199501996000
    +B1110414533202N00603561EA0200401995000
    +B1110444533197N00603545EA0200601995000
    +B1110474533204N00603524EA0201001995000
    +B1110504533218N00603521EA0201701995000
    +B1110534533228N00603531EA0201801997000
    +B1110564533229N00603547EA0201501998000
    +B1110594533221N00603560EA0201801999000
    +B1111024533211N00603558EA0202102000000
    +B1111054533202N00603547EA0202302001000
    +B1111084533202N00603525EA0202302002000
    +B1111114533213N00603506EA0202702003000
    +B1111144533228N00603501EA0202702004000
    +B1111174533244N00603505EA0203102006000
    +B1111204533250N00603519EA0203202007000
    +B1111234533246N00603535EA0203802009000
    +B1111264533237N00603542EA0203902010000
    +B1111304533222N00603533EA0204102013000
    +B1111334533216N00603514EA0204602014000
    +B1111364533215N00603491EA0205102017000
    +B1111394533220N00603469EA0205102019000
    +B1111424533234N00603454EA0205302021000
    +B1111454533249N00603453EA0205702023000
    +B1111484533259N00603465EA0206102026000
    +B1111514533261N00603480EA0206402029000
    +B1111544533253N00603487EA0206702030000
    +B1111574533242N00603482EA0207202033000
    +B1112004533234N00603464EA0207202036000
    +B1112034533236N00603439EA0207402039000
    +B1112064533247N00603420EA0207702041000
    +B1112094533263N00603414EA0207502044000
    +B1112124533274N00603424EA0207702046000
    +B1112154533276N00603440EA0207902048000
    +B1112184533276N00603459EA0207902050000
    +B1112214533276N00603478EA0208602053000
    +B1112244533276N00603492EA0208602055000
    +B1112274533267N00603499EA0208502057000
    +B1112304533256N00603488EA0208602059000
    +B1112334533252N00603465EA0209002060000
    +B1112364533257N00603438EA0209602062000
    +B1112394533270N00603422EA0210002065000
    +B1112424533287N00603418EA0210102067000
    +B1112454533300N00603428EA0210302070000
    +B1112484533304N00603444EA0210202073000
    +B1112514533300N00603457EA0210202075000
    +B1112554533285N00603458EA0210202078000
    +B1112584533273N00603449EA0210202080000
    +B1113014533262N00603435EA0210402082000
    +B1113044533256N00603412EA0210002083000
    +B1113074533259N00603384EA0209902084000
    +B1113104533270N00603359EA0210102085000
    +B1113134533284N00603344EA0210302086000
    +B1113164533298N00603334EA0210302087000
    +B1113194533314N00603330EA0210202088000
    +B1113224533326N00603335EA0209602088000
    +B1113254533329N00603352EA0209102088000
    +B1113284533324N00603367EA0209202087000
    +B1113314533315N00603377EA0209002087000
    +B1113344533304N00603382EA0209302086000
    +B1113374533292N00603379EA0208702086000
    +B1113404533278N00603370EA0208902085000
    +B1113434533266N00603359EA0208302085000
    +B1113464533253N00603341EA0208202084000
    +B1113494533242N00603324EA0207902082000
    +B1113524533231N00603305EA0207302081000
    +B1113554533217N00603281EA0207602079000
    +B1113584533206N00603259EA0207402077000
    +B1114014533194N00603237EA0207302076000
    +B1114044533181N00603217EA0207202074000
    +B1114074533169N00603197EA0206702073000
    +B1114114533152N00603169EA0206402070000
    +B1114144533139N00603150EA0206002068000
    +B1114174533125N00603135EA0205702066000
    +B1114204533112N00603118EA0204902064000
    +B1114234533096N00603098EA0204802061000
    +B1114264533084N00603078EA0204902059000
    +B1114294533070N00603060EA0204002056000
    +B1114324533057N00603038EA0204202054000
    +B1114354533047N00603020EA0203302051000
    +B1114384533032N00602999EA0202802047000
    +B1114414533019N00602980EA0202002044000
    +B1114444533005N00602959EA0201202040000
    +B1114474532991N00602939EA0200702036000
    +B1114504532976N00602917EA0199502031000
    +B1114534532960N00602891EA0199102026000
    +B1114564532946N00602866EA0198502021000
    +B1114594532931N00602839EA0197702015000
    +B1115024532915N00602814EA0197002010000
    +B1115054532901N00602788EA0196502004000
    +B1115084532886N00602761EA0194801998000
    +B1115114532865N00602730EA0194401992000
    +B1115144532847N00602703EA0193701985000
    +B1115174532829N00602677EA0192301979000
    +B1115204532810N00602649EA0191701971000
    +B1115234532792N00602621EA0190601964000
    +B1115264532773N00602594EA0189401957000
    +B1115294532754N00602566EA0188701949000
    +B1115334532730N00602531EA0187601938000
    +B1115364532712N00602504EA0186801930000
    +B1115394532694N00602476EA0186201922000
    +B1115424532676N00602447EA0185401914000
    +B1115454532658N00602418EA0184901906000
    +B1115484532640N00602391EA0184401898000
    +B1115514532622N00602360EA0183801891000
    +B1115544532604N00602330EA0183501884000
    +B1115574532586N00602301EA0183201877000
    +B1116004532569N00602272EA0182801870000
    +B1116034532552N00602244EA0182401864000
    +B1116064532535N00602216EA0182001858000
    +B1116094532518N00602189EA0181601852000
    +B1116124532501N00602162EA0181301846000
    +B1116154532485N00602138EA0181401841000
    +B1116184532472N00602118EA0181001836000
    +B1116214532458N00602095EA0180301831000
    +B1116244532443N00602070EA0180001826000
    +B1116274532429N00602047EA0179701821000
    +B1116304532417N00602026EA0179501817000
    +B1116334532405N00602006EA0179101813000
    +B1116364532393N00601983EA0178801809000
    +B1116394532382N00601962EA0178301805000
    +B1116424532369N00601940EA0178201801000
    +B1116454532357N00601919EA0177701797000
    +B1116484532344N00601899EA0177401793000
    +B1116514532332N00601879EA0177001790000
    +B1116554532315N00601854EA0176501785000
    +B1116584532302N00601834EA0176101781000
    +B1117014532289N00601813EA0175701777000
    +B1117044532275N00601793EA0175401773000
    +B1117074532262N00601774EA0174901769000
    +B1117104532248N00601756EA0174701766000
    +B1117134532234N00601738EA0174101762000
    +B1117164532219N00601720EA0173701758000
    +B1117194532204N00601704EA0173201754000
    +B1117224532189N00601688EA0172901750000
    +B1117254532173N00601672EA0172501746000
    +B1117284532158N00601655EA0172301742000
    +B1117314532144N00601639EA0172101739000
    +B1117344532130N00601621EA0171701735000
    +B1117374532116N00601602EA0171401731000
    +B1117404532103N00601583EA0171201728000
    +B1117434532089N00601564EA0171001725000
    +B1117464532075N00601546EA0170601721000
    +B1117494532060N00601529EA0170301718000
    +B1117524532046N00601513EA0170101715000
    +B1117554532031N00601498EA0169701712000
    +B1117584532017N00601483EA0169301708000
    +B1118014532001N00601469EA0169001705000
    +B1118044531985N00601454EA0168701702000
    +B1118074531970N00601439EA0168101698000
    +B1118104531955N00601423EA0167801695000
    +B1118144531936N00601401EA0167301690000
    +B1118174531922N00601383EA0166901686000
    +B1118204531907N00601366EA0166301683000
    +B1118234531893N00601349EA0166001679000
    +B1118264531878N00601334EA0165601675000
    +B1118294531863N00601318EA0165201671000
    +B1118324531849N00601302EA0164801667000
    +B1118354531834N00601286EA0164501663000
    +B1118384531820N00601269EA0164301660000
    +B1118414531805N00601254EA0163801656000
    +B1118444531791N00601237EA0163801653000
    +B1118474531778N00601222EA0163601650000
    +B1118504531765N00601207EA0163601647000
    +B1118534531752N00601192EA0163501644000
    +B1118564531739N00601177EA0163501642000
    +B1118594531727N00601160EA0163401639000
    +B1119024531715N00601143EA0163501637000
    +B1119054531705N00601125EA0163501636000
    +B1119084531694N00601106EA0163501634000
    +B1119114531683N00601088EA0163601633000
    +B1119144531672N00601069EA0163601631000
    +B1119174531660N00601051EA0163701630000
    +B1119204531649N00601033EA0163201629000
    +B1119234531637N00601013EA0163201628000
    +B1119274531620N00600988EA0163101626000
    +B1119304531606N00600969EA0163201625000
    +B1119334531593N00600952EA0163101624000
    +B1119364531577N00600936EA0163101623000
    +B1119394531562N00600919EA0163201622000
    +B1119424531545N00600904EA0163301621000
    +B1119454531533N00600887EA0163201620000
    +B1119484531520N00600869EA0163201620000
    +B1119514531508N00600851EA0162901619000
    +B1119544531496N00600831EA0162701618000
    +B1119574531484N00600809EA0162901617000
    +B1120004531473N00600791EA0162601617000
    +B1120034531462N00600771EA0162101616000
    +B1120064531450N00600755EA0162101615000
    +B1120094531439N00600742EA0161901614000
    +B1120124531428N00600728EA0161601613000
    +B1120154531415N00600715EA0161301612000
    +B1120184531402N00600701EA0161101611000
    +B1120214531391N00600686EA0160901609000
    +B1120244531380N00600671EA0160401608000
    +B1120274531367N00600655EA0160401606000
    +B1120304531354N00600639EA0160001605000
    +B1120334531340N00600623EA0159801603000
    +B1120364531326N00600610EA0159601601000
    +B1120394531312N00600596EA0159101599000
    +B1120424531297N00600582EA0158701596000
    +B1120464531278N00600562EA0158101593000
    +B1120494531263N00600548EA0157601590000
    +B1120524531247N00600534EA0157301587000
    +B1120554531233N00600521EA0157201584000
    +B1120584531220N00600504EA0156601581000
    +B1121014531207N00600485EA0156401577000
    +B1121044531194N00600467EA0156201574000
    +B1121074531179N00600448EA0155701571000
    +B1121104531165N00600430EA0155601568000
    +B1121134531151N00600412EA0155001565000
    +B1121164531136N00600392EA0155001562000
    +B1121194531121N00600375EA0154801558000
    +B1121224531106N00600357EA0154301555000
    +B1121254531090N00600339EA0154201552000
    +B1121284531073N00600323EA0154001549000
    +B1121314531058N00600304EA0153901546000
    +B1121344531043N00600286EA0153601543000
    +B1121374531028N00600268EA0153801541000
    +B1121404531014N00600253EA0153501538000
    +B1121434530998N00600237EA0153501536000
    +B1121464530983N00600224EA0153001534000
    +B1121494530967N00600212EA0152901531000
    +B1121524530952N00600201EA0152501529000
    +B1121554530937N00600189EA0152301526000
    +B1121584530922N00600177EA0152101524000
    +B1122014530908N00600165EA0151601522000
    +B1122054530890N00600147EA0151301518000
    +B1122084530877N00600132EA0150801515000
    +B1122114530864N00600116EA0150601513000
    +B1122144530851N00600102EA0150101510000
    +B1122174530836N00600087EA0149801507000
    +B1122204530821N00600075EA0149401504000
    +B1122234530806N00600063EA0149201501000
    +B1122264530791N00600052EA0148901498000
    +B1122294530776N00600041EA0148501495000
    +B1122324530760N00600029EA0148301492000
    +B1122354530745N00600018EA0147901489000
    +B1122384530730N00600005EA0147601486000
    +B1122414530716N00559990EA0147301483000
    +B1122444530701N00559976EA0147001480000
    +B1122474530687N00559963EA0146701477000
    +B1122504530672N00559952EA0146401473000
    +B1122534530657N00559939EA0146101470000
    +B1122564530643N00559927EA0145801467000
    +B1122594530628N00559916EA0145401464000
    +B1123024530613N00559905EA0145101461000
    +B1123054530598N00559893EA0144801458000
    +B1123084530583N00559882EA0144501455000
    +B1123114530568N00559871EA0144201452000
    +B1123144530553N00559860EA0143901449000
    +B1123174530538N00559849EA0143601446000
    +B1123204530524N00559838EA0143401443000
    +B1123244530504N00559823EA0143001439000
    +B1123274530490N00559813EA0142701435000
    +B1123304530475N00559801EA0142301432000
    +B1123334530461N00559790EA0142101429000
    +B1123364530447N00559779EA0141701426000
    +B1123394530432N00559768EA0141401423000
    +B1123424530419N00559757EA0141001420000
    +B1123454530405N00559745EA0140701417000
    +B1123484530392N00559734EA0140301413000
    +B1123514530378N00559722EA0140001410000
    +B1123544530365N00559711EA0139701407000
    +B1123574530351N00559700EA0139301404000
    +B1124004530337N00559688EA0139001400000
    +B1124034530324N00559677EA0138701397000
    +B1124064530310N00559667EA0138401394000
    +B1124094530296N00559657EA0138201391000
    +B1124124530282N00559647EA0137701388000
    +B1124154530268N00559637EA0137501384000
    +B1124184530253N00559628EA0137101381000
    +B1124214530238N00559618EA0136801378000
    +B1124244530223N00559608EA0136601375000
    +B1124274530208N00559599EA0136201372000
    +B1124304530193N00559589EA0136001369000
    +B1124334530179N00559579EA0135501366000
    +B1124364530164N00559568EA0135301362000
    +B1124394530150N00559558EA0135001359000
    +B1124434530132N00559544EA0134601355000
    +B1124464530118N00559533EA0134301352000
    +B1124494530105N00559523EA0133901349000
    +B1124524530090N00559512EA0133801345000
    +B1124554530076N00559502EA0133401342000
    +B1124584530062N00559493EA0133101339000
    +B1125014530046N00559485EA0132701336000
    +B1125044530031N00559476EA0132501333000
    +B1125074530016N00559468EA0132201329000
    +B1125104530000N00559461EA0131801326000
    +B1125134529984N00559454EA0131501323000
    +B1125164529968N00559447EA0131001320000
    +B1125194529952N00559440EA0130701317000
    +B1125224529937N00559432EA0130401314000
    +B1125254529921N00559424EA0130101310000
    +B1125284529906N00559416EA0129701307000
    +B1125314529890N00559409EA0129401304000
    +B1125344529875N00559401EA0129101301000
    +B1125374529860N00559394EA0128701298000
    +B1125404529844N00559386EA0128401294000
    +B1125434529829N00559377EA0128201291000
    +B1125464529815N00559368EA0127701288000
    +B1125494529800N00559359EA0127501285000
    +B1125524529785N00559350EA0127201282000
    +B1125554529771N00559341EA0126801279000
    +B1125584529757N00559331EA0126601275000
    +B1126024529741N00559317EA0125901271000
    +B1126054529728N00559304EA0125601268000
    +B1126084529717N00559294EA0125201264000
    +B1126114529703N00559281EA0124901261000
    +B1126144529690N00559269EA0124901257000
    +B1126174529677N00559259EA0124601255000
    +B1126204529664N00559248EA0124301252000
    +B1126234529650N00559238EA0124101249000
    +B1126264529636N00559230EA0123701246000
    +B1126294529621N00559221EA0123401243000
    +B1126324529607N00559212EA0122801241000
    +B1126364529587N00559197EA0122601237000
    +B1126394529573N00559188EA0122101234000
    +B1126424529558N00559177EA0121801231000
    +B1126454529544N00559167EA0121401227000
    +B1126484529529N00559156EA0121101224000
    +B1126514529515N00559145EA0120801221000
    +B1126544529500N00559136EA0120501218000
    +B1126574529485N00559128EA0120101214000
    +B1127004529469N00559120EA0119901211000
    +B1127034529452N00559113EA0119601208000
    +B1127064529436N00559105EA0119701205000
    +B1127094529420N00559097EA0119301202000
    +B1127124529404N00559088EA0119101199000
    +B1127154529388N00559080EA0118901196000
    +B1127184529372N00559070EA0118601194000
    +B1127214529356N00559062EA0118601191000
    +B1127244529341N00559053EA0118301189000
    +B1127274529325N00559043EA0118201186000
    +B1127304529310N00559035EA0117801184000
    +B1127334529294N00559025EA0117801181000
    +B1127364529279N00559016EA0117401179000
    +B1127394529263N00559007EA0117301177000
    +B1127424529248N00558997EA0117201175000
    +B1127454529234N00558987EA0116901172000
    +B1127484529219N00558976EA0116701170000
    +B1127524529201N00558962EA0116401167000
    +B1127554529187N00558950EA0116401165000
    +B1127584529174N00558939EA0116301163000
    +B1128014529161N00558928EA0116001161000
    +B1128044529147N00558916EA0115801158000
    +B1128074529135N00558906EA0115901156000
    +B1128104529123N00558894EA0115501154000
    +B1128134529111N00558882EA0115501152000
    +B1128164529099N00558872EA0115101151000
    +B1128194529086N00558861EA0115001149000
    +B1128224529073N00558851EA0114801148000
    +B1128254529061N00558840EA0114501146000
    +B1128284529049N00558830EA0114101144000
    +B1128314529036N00558820EA0113601142000
    +B1128344529022N00558811EA0113201140000
    +B1128374529008N00558803EA0112901137000
    +B1128404528993N00558796EA0112501134000
    +B1128434528978N00558790EA0112001131000
    +B1128464528963N00558785EA0111701129000
    +B1128494528947N00558780EA0111201126000
    +B1128524528930N00558776EA0110901122000
    +B1128554528914N00558771EA0110401119000
    +B1128584528898N00558764EA0110201116000
    +B1129014528882N00558758EA0109701112000
    +B1129044528866N00558751EA0109401109000
    +B1129084528844N00558744EA0109001104000
    +B1129114528828N00558738EA0108601101000
    +B1129144528811N00558733EA0108201097000
    +B1129174528794N00558727EA0108001094000
    +B1129204528777N00558721EA0107701090000
    +B1129234528760N00558714EA0107301087000
    +B1129264528743N00558708EA0107001083000
    +B1129294528726N00558701EA0106601080000
    +B1129324528709N00558694EA0106301076000
    +B1129354528693N00558688EA0106001073000
    +B1129384528676N00558682EA0105801069000
    +B1129414528659N00558676EA0105601066000
    +B1129444528642N00558669EA0105501063000
    +B1129474528625N00558662EA0105101060000
    +B1129504528607N00558655EA0104901057000
    +B1129534528589N00558648EA0104701054000
    +B1129564528573N00558641EA0104801052000
    +B1129594528558N00558633EA0104401049000
    +B1130024528546N00558620EA0104301047000
    +B1130054528533N00558609EA0104001045000
    +B1130084528520N00558598EA0104001042000
    +B1130114528505N00558590EA0104101041000
    +B1130144528491N00558583EA0104001039000
    +B1130174528477N00558575EA0104101037000
    +B1130204528464N00558567EA0103901036000
    +B1130244528447N00558554EA0104001034000
    +B1130274528434N00558545EA0103901033000
    +B1130304528420N00558535EA0104001032000
    +B1130334528407N00558525EA0104001031000
    +B1130364528392N00558515EA0104001030000
    +B1130394528379N00558505EA0103701029000
    +B1130424528364N00558496EA0103201028000
    +B1130454528350N00558486EA0102701027000
    +B1130484528334N00558477EA0102401025000
    +B1130514528318N00558468EA0101901023000
    +B1130544528301N00558461EA0101501021000
    +B1130574528284N00558453EA0101201018000
    +B1131004528266N00558446EA0100901016000
    +B1131034528249N00558440EA0100501013000
    +B1131064528230N00558434EA0100101010000
    +B1131094528212N00558427EA0099601007000
    +B1131124528194N00558418EA0099301004000
    +B1131154528176N00558408EA0099101001000
    +B1131184528160N00558395EA0099000998000
    +B1131214528145N00558382EA0098800995000
    +B1131244528128N00558371EA0098200992000
    +B1131274528111N00558358EA0097800989000
    +B1131304528092N00558346EA0097600985000
    +B1131334528073N00558333EA0097900983000
    +B1131364528058N00558318EA0098400980000
    +B1131394528048N00558300EA0099000979000
    +B1131424528040N00558282EA0099200978000
    +B1131464528042N00558260EA0099500977000
    +B1131494528049N00558253EA0099500977000
    +B1131524528060N00558257EA0099700978000
    +B1131554528067N00558270EA0099800978000
    +B1131584528069N00558291EA0099500978000
    +B1132014528064N00558315EA0099600978000
    +B1132044528052N00558336EA0099800978000
    +B1132074528038N00558351EA0100700979000
    +B1132104528025N00558362EA0101100980000
    +B1132134528009N00558366EA0101400981000
    +B1132164527995N00558356EA0102100983000
    +B1132194527990N00558339EA0102700985000
    +B1132224527995N00558329EA0103200986000
    +B1132254528004N00558331EA0103500988000
    +B1132284528013N00558347EA0103700991000
    +B1132314528014N00558371EA0103800994000
    +B1132344528000N00558386EA0103700997000
    +B1132374527986N00558375EA0104200999000
    +B1132404527979N00558356EA0104801002000
    +B1132434527974N00558336EA0105401005000
    +B1132464527978N00558318EA0106101008000
    +B1132494527986N00558309EA0106501012000
    +B1132524527998N00558315EA0106501016000
    +B1132554528001N00558335EA0106501020000
    +B1132584527992N00558353EA0106301023000
    +B1133014527976N00558350EA0106501025000
    +B1133044527965N00558338EA0106501028000
    +B1133074527956N00558323EA0106201030000
    +B1133104527946N00558302EA0107101032000
    +B1133134527939N00558284EA0107901034000
    +B1133164527938N00558265EA0108401037000
    +B1133204527950N00558257EA0109001042000
    +B1133234527954N00558272EA0109101045000
    +B1133264527946N00558293EA0108701048000
    +B1133294527929N00558303EA0109201051000
    +B1133324527911N00558297EA0109501054000
    +B1133354527897N00558287EA0110701057000
    +B1133384527891N00558270EA0110701060000
    +B1133414527893N00558250EA0111801064000
    +B1133444527903N00558243EA0112201068000
    +B1133474527915N00558252EA0112301072000
    +B1133504527913N00558273EA0112101076000
    +B1133534527898N00558281EA0112301079000
    +B1133564527880N00558276EA0113501083000
    +B1133594527867N00558266EA0114401087000
    +B1134024527858N00558247EA0114801091000
    +B1134054527860N00558227EA0115701096000
    +B1134084527870N00558221EA0116401100000
    +B1134114527877N00558234EA0116501105000
    +B1134144527869N00558254EA0117201110000
    +B1134174527853N00558258EA0118401115000
    +B1134204527837N00558251EA0119101120000
    +B1134234527825N00558234EA0119401125000
    +B1134264527825N00558214EA0119801131000
    +B1134294527836N00558209EA0120601136000
    +B1134324527842N00558224EA0121801142000
    +B1134354527835N00558240EA0122801149000
    +B1134384527820N00558250EA0123601155000
    +B1134414527803N00558244EA0123801162000
    +B1134444527791N00558224EA0124401168000
    +B1134474527787N00558205EA0124801175000
    +B1134504527789N00558185EA0125501181000
    +B1134534527796N00558173EA0126401187000
    +B1134564527807N00558172EA0127301194000
    +B1134594527813N00558187EA0128001201000
    +B1135034527800N00558207EA0128801210000
    +B1135064527786N00558205EA0129401217000
    +B1135094527776N00558188EA0130101223000
    +B1135124527776N00558168EA0131001230000
    +B1135154527784N00558158EA0131801237000
    +B1135184527795N00558164EA0132801244000
    +B1135214527799N00558182EA0133401252000
    +B1135244527790N00558197EA0133901259000
    +B1135274527777N00558200EA0134101266000
    +B1135304527763N00558190EA0134701272000
    +B1135334527756N00558171EA0135501279000
    +B1135364527756N00558152EA0136301285000
    +B1135394527762N00558139EA0136701292000
    +B1135424527772N00558136EA0137401298000
    +B1135454527782N00558134EA0138101304000
    +B1135484527793N00558143EA0138801311000
    +B1135514527793N00558164EA0139201317000
    +B1135544527781N00558180EA0139801324000
    +B1135574527765N00558181EA0140501330000
    +B1136004527755N00558169EA0141001336000
    +B1136034527752N00558157EA0140901342000
    +B1136064527760N00558139EA0141401348000
    +B1136094527772N00558132EA0142201353000
    +B1136124527783N00558138EA0142601359000
    +B1136154527789N00558157EA0143001364000
    +B1136184527784N00558177EA0143401369000
    +B1136214527769N00558186EA0143701374000
    +B1136244527755N00558180EA0144101379000
    +B1136274527750N00558165EA0144301384000
    +B1136314527753N00558142EA0145001391000
    +B1136344527762N00558136EA0145201396000
    +B1136374527773N00558145EA0145601401000
    +B1136404527775N00558165EA0146101406000
    +B1136434527770N00558185EA0146301411000
    +B1136464527755N00558202EA0146801415000
    +B1136494527738N00558208EA0147101420000
    +B1136524527720N00558205EA0147201424000
    +B1136554527702N00558200EA0147601429000
    +B1136584527686N00558195EA0147701433000
    +B1137014527671N00558187EA0147401436000
    +B1137044527654N00558177EA0147301439000
    +B1137074527635N00558166EA0147601441000
    +B1137104527614N00558159EA0148001444000
    +B1137134527594N00558153EA0148401446000
    +B1137164527577N00558152EA0147501449000
    +B1137194527556N00558148EA0146901450000
    +B1137224527536N00558142EA0146901451000
    +B1137254527519N00558138EA0146201451000
    +B1137284527500N00558135EA0145701451000
    +B1137314527481N00558132EA0145201450000
    +B1137344527463N00558129EA0144601449000
    +B1137374527444N00558127EA0144301447000
    +B1137404527426N00558128EA0143601445000
    +B1137434527407N00558126EA0143101442000
    +B1137464527389N00558125EA0142401439000
    +B1137494527370N00558121EA0142101436000
    +B1137534527344N00558111EA0141801431000
    +B1137564527323N00558102EA0141601428000
    +B1137594527302N00558091EA0141401425000
    +B1138024527281N00558077EA0140901422000
    +B1138054527259N00558068EA0140901418000
    +B1138084527237N00558059EA0140601415000
    +B1138114527216N00558053EA0140401412000
    +B1138144527195N00558051EA0140201409000
    +B1138174527174N00558050EA0139401406000
    +B1138204527152N00558048EA0138901402000
    +B1138234527130N00558043EA0138701400000
    +B1138264527110N00558037EA0137901396000
    +B1138294527089N00558030EA0136701393000
    +B1138324527066N00558023EA0136301388000
    +B1138354527044N00558020EA0135701384000
    +B1138384527022N00558018EA0135201379000
    +B1138414527000N00558013EA0135001374000
    +B1138444526979N00558010EA0134301369000
    +B1138474526958N00558008EA0134101364000
    +B1138504526939N00558004EA0133401360000
    +B1138534526918N00558000EA0133401355000
    +B1138564526899N00557997EA0133101351000
    +B1138594526880N00557994EA0132201347000
    +B1139024526861N00557992EA0131701342000
    +B1139054526843N00557989EA0131401338000
    +B1139084526825N00557981EA0131601334000
    +B1139124526804N00557973EA0131401329000
    +B1139154526785N00557966EA0131501326000
    +B1139184526766N00557962EA0131501323000
    +B1139214526749N00557958EA0131601321000
    +B1139244526732N00557953EA0131301318000
    +B1139274526715N00557947EA0131401316000
    +B1139304526699N00557941EA0131101313000
    +B1139334526681N00557937EA0131201312000
    +B1139364526665N00557934EA0130901310000
    +B1139394526646N00557932EA0130801308000
    +B1139424526629N00557929EA0130501306000
    +B1139454526608N00557924EA0130501304000
    +B1139484526588N00557922EA0130301302000
    +B1139514526568N00557919EA0129501300000
    +B1139544526547N00557915EA0129101298000
    +B1139574526526N00557909EA0128401296000
    +B1140004526502N00557900EA0128601293000
    +B1140034526483N00557888EA0129101291000
    +B1140064526465N00557875EA0129101289000
    +B1140094526449N00557862EA0129101288000
    +B1140124526432N00557852EA0129201287000
    +B1140154526415N00557844EA0128801286000
    +B1140184526400N00557841EA0127801284000
    +B1140214526382N00557837EA0127201282000
    +B1140244526363N00557832EA0125901279000
    +B1140274526340N00557824EA0125501275000
    +B1140304526320N00557816EA0124301272000
    +B1140334526300N00557810EA0124201267000
    +B1140374526277N00557800EA0124701261000
    +B1140404526264N00557796EA0124801258000
    +B1140434526250N00557795EA0124901255000
    +B1140464526233N00557788EA0125001252000
    +B1140494526219N00557780EA0125101250000
    +B1140524526204N00557774EA0125201248000
    +B1140554526187N00557768EA0126001247000
    +B1140584526172N00557763EA0126401247000
    +B1141014526155N00557757EA0127201247000
    +B1141044526141N00557749EA0127801248000
    +B1141074526128N00557750EA0127901249000
    +B1141104526119N00557766EA0128501251000
    +B1141134526125N00557782EA0128901253000
    +B1141164526140N00557785EA0129201255000
    +B1141194526153N00557772EA0129501257000
    +B1141224526155N00557751EA0130001260000
    +B1141254526146N00557735EA0130401263000
    +B1141284526133N00557727EA0130901266000
    +B1141314526120N00557723EA0131501269000
    +B1141344526105N00557718EA0131701273000
    +B1141374526089N00557724EA0131701276000
    +B1141404526082N00557741EA0131801279000
    +B1141434526089N00557755EA0131801282000
    +B1141464526102N00557756EA0131901285000
    +B1141494526110N00557735EA0132501287000
    +B1141524526107N00557713EA0133201290000
    +B1141554526094N00557696EA0133401293000
    +B1141584526077N00557696EA0133801296000
    +B1142014526065N00557707EA0133501299000
    +B1142054526067N00557731EA0133401302000
    +B1142084526079N00557731EA0133101304000
    +B1142114526088N00557713EA0132701305000
    +B1142144526082N00557687EA0132501305000
    +B1142174526070N00557669EA0132701306000
    +B1142204526053N00557664EA0132201307000
    +B1142234526036N00557665EA0132001306000
    +B1142264526019N00557670EA0131701306000
    +B1142294526004N00557677EA0131001305000
    +B1142324525984N00557684EA0130501304000
    +B1142354525965N00557689EA0130401302000
    +B1142384525947N00557691EA0129901300000
    +B1142414525929N00557687EA0129801299000
    +B1142444525913N00557683EA0129301297000
    +B1142474525896N00557677EA0129301294000
    +B1142504525881N00557671EA0128801292000
    +B1142534525863N00557666EA0128401290000
    +B1142564525844N00557660EA0128601287000
    +B1142594525826N00557657EA0128301285000
    +B1143024525808N00557650EA0128501283000
    +B1143054525790N00557644EA0127801281000
    +B1143084525768N00557637EA0128101279000
    +B1143114525748N00557630EA0128201278000
    +B1143144525731N00557625EA0127701276000
    +B1143174525710N00557617EA0127901275000
    +B1143204525691N00557612EA0128001273000
    +B1143234525670N00557607EA0127801272000
    +B1143274525643N00557601EA0128301271000
    +B1143304525626N00557594EA0128001270000
    +B1143334525609N00557589EA0127801269000
    +B1143364525592N00557585EA0127701268000
    +B1143394525576N00557578EA0127601267000
    +B1143424525559N00557573EA0127701266000
    +B1143454525542N00557572EA0127301265000
    +B1143484525523N00557574EA0126201263000
    +B1143514525501N00557576EA0125801261000
    +B1143544525483N00557576EA0125001258000
    +B1143574525462N00557575EA0124801255000
    +B1144004525444N00557572EA0124401252000
    +B1144034525426N00557568EA0123601249000
    +B1144064525405N00557562EA0123801246000
    +B1144094525386N00557555EA0124001243000
    +B1144124525366N00557551EA0124301240000
    +B1144154525345N00557549EA0124501238000
    +B1144184525324N00557547EA0124601237000
    +B1144214525301N00557544EA0124701236000
    +B1144244525280N00557540EA0124501235000
    +B1144274525259N00557537EA0123801234000
    +B1144304525238N00557529EA0123601233000
    +B1144334525219N00557523EA0122701231000
    +B1144364525199N00557514EA0122001229000
    +B1144394525177N00557508EA0121401227000
    +B1144424525156N00557506EA0120801222000
    +B1144464525130N00557503EA0120001219000
    +B1144494525111N00557498EA0119401215000
    +B1144524525092N00557496EA0118701211000
    +B1144554525071N00557495EA0118801207000
    +B1144584525052N00557494EA0118601203000
    +B1145014525035N00557487EA0118801200000
    +B1145044525023N00557481EA0118701197000
    +B1145074525006N00557472EA0118101194000
    +B1145104524986N00557466EA0118601191000
    +B1145134524971N00557458EA0118601189000
    +B1145164524956N00557454EA0118401187000
    +B1145194524941N00557452EA0117601185000
    +B1145224524924N00557448EA0117601183000
    +B1145254524908N00557442EA0117101180000
    +B1145284524889N00557434EA0116801178000
    +B1145314524869N00557426EA0116701176000
    +B1145344524851N00557422EA0116401173000
    +B1145374524832N00557412EA0116901171000
    +B1145404524816N00557397EA0117201169000
    +B1145434524803N00557383EA0117601168000
    +B1145464524793N00557368EA0117801167000
    +B1145494524782N00557353EA0117901166000
    +B1145524524768N00557340EA0117901166000
    +B1145554524752N00557333EA0117701166000
    +B1145584524735N00557328EA0117401165000
    +B1146014524720N00557320EA0117301165000
    +B1146044524706N00557311EA0117201164000
    +B1146074524693N00557305EA0116801163000
    +B1146114524674N00557298EA0116801162000
    +B1146144524660N00557295EA0116801161000
    +B1146174524647N00557290EA0116701160000
    +B1146204524635N00557285EA0116601159000
    +B1146234524622N00557280EA0116601158000
    +B1146264524610N00557274EA0116401157000
    +B1146294524598N00557266EA0116201156000
    +B1146324524587N00557261EA0116101155000
    +B1146354524576N00557260EA0115801154000
    +B1146384524564N00557259EA0115501153000
    +B1146414524548N00557258EA0115301151000
    +B1146444524531N00557255EA0115201149000
    +B1146474524513N00557251EA0115401148000
    +B1146504524495N00557246EA0115301147000
    +B1146534524477N00557240EA0115301146000
    +B1146564524458N00557235EA0114901145000
    +B1146594524440N00557231EA0114301143000
    +B1147024524422N00557230EA0113701141000
    +B1147054524400N00557227EA0113201139000
    +B1147084524378N00557222EA0112901136000
    +B1147114524357N00557218EA0112401133000
    +B1147144524335N00557212EA0112501130000
    +B1147174524315N00557206EA0112001128000
    +B1147204524293N00557203EA0111601125000
    +B1147234524270N00557198EA0111101122000
    +B1147264524248N00557190EA0111001119000
    +B1147304524222N00557174EA0111501115000
    +B1147334524207N00557162EA0112501114000
    +B1147364524194N00557153EA0113101113000
    +B1147394524182N00557158EA0113901113000
    +B1147424524182N00557171EA0114501115000
    +B1147454524195N00557171EA0115301116000
    +B1147484524205N00557151EA0115701119000
    +B1147514524198N00557127EA0116301122000
    +B1147544524182N00557114EA0117501125000
    +B1147574524169N00557112EA0117901129000
    +B1148004524162N00557123EA0118801133000
    +B1148034524168N00557130EA0119601136000
    +B1148064524179N00557121EA0120801142000
    +B1148094524183N00557097EA0121701148000
    +B1148124524175N00557075EA0122401155000
    +B1148154524162N00557068EA0123001161000
    +B1148184524154N00557080EA0123301167000
    +B1148214524162N00557091EA0124501173000
    +B1148244524172N00557088EA0125601180000
    +B1148274524180N00557071EA0126001187000
    +B1148304524181N00557044EA0127301194000
    +B1148334524175N00557023EA0127701201000
    +B1148364524162N00557013EA0128401208000
    +B1148394524152N00557016EA0128801214000
    +B1148424524152N00557030EA0129501220000
    +B1148454524163N00557039EA0130501227000
    +B1148484524176N00557031EA0131401234000
    +B1148514524185N00557012EA0132201241000
    +B1148544524183N00556989EA0133001248000
    +B1148574524175N00556972EA0133601255000
    +B1149014524159N00556968EA0134201265000
    +B1149044524156N00556979EA0134801270000
    +B1149074524166N00556988EA0135201277000
    +B1149104524181N00556981EA0135901284000
    +B1149134524190N00556962EA0136601291000
    +B1149164524191N00556940EA0137101297000
    +B1149194524182N00556923EA0137301302000
    +B1149224524168N00556923EA0138201308000
    +B1149254524159N00556932EA0138801315000
    +B1149284524160N00556946EA0139201321000
    +B1149314524173N00556950EA0139901327000
    +B1149344524185N00556937EA0140201333000
    +B1149374524188N00556915EA0140501339000
    +B1149404524178N00556899EA0140601345000
    +B1149434524164N00556892EA0140901350000
    +B1149464524149N00556890EA0140801355000
    +B1149494524130N00556883EA0141201359000
    +B1149524524114N00556873EA0141601364000
    +B1149554524099N00556865EA0141301368000
    +B1149584524083N00556854EA0141201371000
    +B1150014524065N00556847EA0140901374000
    +B1150044524045N00556841EA0140701376000
    +B1150074524025N00556835EA0140801378000
    +B1150104524007N00556831EA0140701380000
    +B1150134523990N00556827EA0140501382000
    +B1150164523972N00556823EA0140501383000
    +B1150194523953N00556816EA0140101384000
    +B1150234523926N00556800EA0139901385000
    +B1150264523908N00556785EA0139801385000
    +B1150294523889N00556773EA0139301385000
    +B1150324523869N00556759EA0138801384000
    +B1150354523850N00556746EA0138401383000
    +B1150384523831N00556734EA0137701381000
    +B1150414523810N00556722EA0137701379000
    +B1150444523791N00556714EA0137401377000
    +B1150474523771N00556707EA0136601375000
    +B1150504523751N00556701EA0135801372000
    +B1150534523731N00556695EA0135301369000
    +B1150564523710N00556693EA0134701366000
    +B1150594523689N00556693EA0134601364000
    +B1151024523668N00556691EA0134301361000
    +B1151054523646N00556686EA0134201358000
    +B1151084523627N00556678EA0133501355000
    +B1151114523607N00556666EA0133301351000
    +B1151144523588N00556657EA0132801348000
    +B1151174523567N00556646EA0132301344000
    +B1151204523545N00556636EA0131701339000
    +B1151234523524N00556628EA0131501335000
    +B1151264523502N00556620EA0131201331000
    +B1151294523481N00556611EA0130701327000
    +B1151324523460N00556605EA0130201323000
    +B1151354523438N00556600EA0129501318000
    +B1151394523408N00556594EA0128801312000
    +B1151424523386N00556590EA0128201307000
    +B1151454523363N00556584EA0127701302000
    +B1151484523341N00556577EA0127201297000
    +B1151514523319N00556571EA0126801293000
    +B1151544523297N00556564EA0126201288000
    +B1151574523276N00556556EA0125801283000
    +B1152004523256N00556547EA0125401278000
    +B1152034523238N00556537EA0124801274000
    +B1152064523220N00556525EA0124501270000
    +B1152094523203N00556514EA0124301265000
    +B1152124523187N00556505EA0123201261000
    +B1152154523167N00556495EA0122401257000
    +B1152184523148N00556485EA0122101252000
    +B1152214523130N00556474EA0121701247000
    +B1152244523113N00556466EA0121501242000
    +B1152274523099N00556458EA0120401237000
    +B1152304523079N00556447EA0120301232000
    +B1152334523061N00556437EA0120701227000
    +B1152364523047N00556427EA0120201223000
    +B1152394523030N00556413EA0120501219000
    +B1152424523015N00556398EA0120801215000
    +B1152454523000N00556383EA0120901213000
    +B1152484522985N00556367EA0121201211000
    +B1152514522971N00556351EA0121501209000
    +B1152544522956N00556335EA0122101208000
    +B1152574522944N00556319EA0122101208000
    +B1153014522926N00556295EA0122201208000
    +B1153044522914N00556278EA0122601208000
    +B1153074522904N00556262EA0122601208000
    +B1153104522891N00556246EA0122601209000
    +B1153134522878N00556233EA0122501209000
    +B1153164522864N00556224EA0122201209000
    +B1153194522847N00556215EA0122301209000
    +B1153224522830N00556204EA0122701209000
    +B1153254522813N00556194EA0123101210000
    +B1153284522800N00556186EA0123201211000
    +B1153314522787N00556175EA0122701212000
    +B1153344522773N00556163EA0122601212000
    +B1153374522757N00556154EA0121601212000
    +B1153404522733N00556143EA0121201211000
    +B1153434522707N00556133EA0121001209000
    +B1153464522682N00556124EA0120801208000
    +B1153494522658N00556113EA0121001206000
    +B1153524522636N00556105EA0120701205000
    +B1153554522614N00556096EA0120401203000
    +B1153584522592N00556088EA0119701201000
    +B1154014522571N00556080EA0119201199000
    +B1154044522549N00556075EA0118501196000
    +B1154074522527N00556068EA0117901193000
    +B1154104522505N00556061EA0117501189000
    +B1154134522484N00556055EA0116901185000
    +B1154174522456N00556048EA0116301180000
    +B1154204522436N00556040EA0115901176000
    +B1154234522415N00556032EA0115501172000
    +B1154264522394N00556025EA0115101168000
    +B1154294522373N00556019EA0114601163000
    +B1154324522352N00556012EA0114301159000
    +B1154354522331N00556005EA0113801155000
    +B1154384522309N00555995EA0113401151000
    +B1154414522288N00555987EA0113101146000
    +B1154444522267N00555979EA0112301142000
    +B1154474522244N00555972EA0111901137000
    +B1154504522223N00555966EA0111201133000
    +B1154534522202N00555959EA0110801128000
    +B1154564522181N00555951EA0110501123000
    +B1154594522162N00555940EA0109901119000
    +B1155024522143N00555930EA0109101114000
    +B1155054522125N00555920EA0108801108000
    +B1155084522106N00555912EA0108401103000
    +B1155114522086N00555904EA0108101098000
    +B1155144522065N00555897EA0107801094000
    +B1155174522045N00555890EA0107801089000
    +B1155204522026N00555884EA0107301085000
    +B1155234522007N00555875EA0108201082000
    +B1155264521992N00555866EA0109001079000
    +B1155294521977N00555857EA0110301078000
    +B1155324521966N00555845EA0111301079000
    +B1155364521950N00555833EA0112001081000
    +B1155394521936N00555824EA0112801083000
    +B1155424521923N00555811EA0113101086000
    +B1155454521910N00555796EA0113401089000
    +B1155484521897N00555783EA0114001092000
    +B1155514521885N00555767EA0114401095000
    +B1155544521872N00555753EA0115301099000
    +B1155574521860N00555740EA0115501103000
    +B1156004521846N00555724EA0115901107000
    +B1156034521833N00555710EA0116301111000
    +B1156064521820N00555698EA0115901114000
    +B1156094521803N00555684EA0115301117000
    +B1156124521786N00555668EA0115101119000
    +B1156154521770N00555651EA0115201120000
    +B1156184521755N00555636EA0114901122000
    +B1156214521740N00555620EA0115001122000
    +B1156244521725N00555605EA0115201123000
    +B1156274521710N00555588EA0115601124000
    +B1156304521697N00555574EA0116101126000
    +B1156334521683N00555562EA0116001127000
    +B1156364521667N00555550EA0116301129000
    +B1156394521651N00555537EA0116801131000
    +B1156424521638N00555526EA0116701132000
    +B1156454521622N00555515EA0116201134000
    +B1156484521607N00555506EA0116801135000
    +B1156514521596N00555501EA0116201137000
    +B1156544521579N00555495EA0116001137000
    +B1156574521561N00555489EA0115701138000
    +B1157004521543N00555479EA0115101138000
    +B1157044521518N00555463EA0115001138000
    +B1157074521501N00555452EA0116101138000
    +B1157104521486N00555439EA0116901139000
    +B1157134521470N00555428EA0117601141000
    +B1157164521454N00555419EA0118801143000
    +B1157194521442N00555414EA0118701146000
    +B1157224521426N00555408EA0118401148000
    +B1157254521410N00555402EA0118901150000
    +B1157284521395N00555396EA0118601153000
    +B1157314521378N00555387EA0118801155000
    +B1157344521364N00555377EA0118801157000
    +B1157374521347N00555367EA0118901159000
    +B1157404521332N00555356EA0119101161000
    +B1157434521318N00555346EA0118501162000
    +B1157464521303N00555333EA0118601164000
    +B1157494521289N00555323EA0118301164000
    +B1157524521273N00555318EA0117901165000
    +B1157554521256N00555317EA0117501165000
    +B1157584521238N00555317EA0116901164000
    +B1158014521219N00555315EA0116101163000
    +B1158044521201N00555311EA0115101161000
    +B1158074521182N00555307EA0114901158000
    +B1158104521167N00555305EA0114001155000
    +B1158134521149N00555301EA0114101152000
    +B1158164521136N00555299EA0113801149000
    +B1158194521121N00555295EA0114001146000
    +B1158224521108N00555289EA0114101144000
    +B1158254521093N00555289EA0115001142000
    +B1158284521079N00555286EA0115401141000
    +B1158324521056N00555281EA0115801140000
    +B1158354521041N00555278EA0116201140000
    +B1158384521026N00555275EA0116301140000
    +B1158414521014N00555269EA0116201141000
    +B1158444520999N00555264EA0116201141000
    +B1158474520984N00555261EA0116601142000
    +B1158504520971N00555256EA0116701142000
    +B1158534520956N00555251EA0117001143000
    +B1158564520941N00555248EA0117101144000
    +B1158594520928N00555243EA0117201145000
    +B1159024520913N00555239EA0117801146000
    +B1159054520900N00555236EA0118701147000
    +B1159084520885N00555231EA0119301150000
    +B1159114520871N00555229EA0119801152000
    +B1159144520857N00555226EA0120101155000
    +B1159174520843N00555223EA0120101158000
    +B1159204520827N00555221EA0120201161000
    +B1159234520813N00555218EA0119801164000
    +B1159264520799N00555217EA0119601167000
    +B1159294520786N00555217EA0119201168000
    +B1159324520772N00555212EA0119601170000
    +B1159354520760N00555208EA0119701172000
    +B1159384520747N00555205EA0119701173000
    +B1159414520734N00555204EA0119801175000
    +B1159444520721N00555202EA0119601176000
    +B1159474520708N00555202EA0119201177000
    +B1159504520693N00555200EA0118401178000
    +B1159544520668N00555188EA0118701177000
    +B1159574520652N00555179EA0118301176000
    +B1200004520634N00555170EA0117801175000
    +B1200034520616N00555162EA0117001174000
    +B1200064520596N00555157EA0116101171000
    +B1200094520576N00555151EA0115301168000
    +B1200124520555N00555143EA0114701164000
    +B1200154520538N00555132EA0114701161000
    +B1200184520522N00555121EA0115101157000
    +B1200214520508N00555116EA0115501155000
    +B1200244520492N00555109EA0115201153000
    +B1200274520476N00555097EA0114901151000
    +B1200304520460N00555087EA0115001149000
    +B1200334520444N00555080EA0114801147000
    +B1200364520427N00555075EA0114401146000
    +B1200394520407N00555070EA0114901144000
    +B1200424520391N00555063EA0115501143000
    +B1200454520376N00555057EA0116101143000
    +B1200484520363N00555046EA0116901143000
    +B1200514520351N00555032EA0117201144000
    +B1200544520338N00555016EA0117701145000
    +B1200574520325N00555009EA0117501147000
    +B1201004520312N00555007EA0117501148000
    +B1201034520297N00554996EA0117601149000
    +B1201064520283N00554978EA0118301151000
    +B1201094520271N00554964EA0118201153000
    +B1201124520258N00554948EA0118101155000
    +B1201164520241N00554932EA0118001157000
    +B1201194520228N00554922EA0118001159000
    +B1201224520217N00554912EA0116801159000
    +B1201254520204N00554905EA0116401159000
    +B1201284520192N00554895EA0115101159000
    +B1201314520176N00554889EA0114001157000
    +B1201344520157N00554886EA0112701154000
    +B1201374520138N00554884EA0112201150000
    +B1201404520122N00554877EA0111101146000
    +B1201434520100N00554874EA0110301141000
    +B1201464520080N00554874EA0109701136000
    +B1201494520058N00554877EA0108701130000
    +B1201524520034N00554881EA0108201124000
    +B1201554520012N00554884EA0107701119000
    +B1201584519989N00554884EA0107501114000
    +B1202014519968N00554882EA0107301108000
    +B1202044519947N00554878EA0107001103000
    +B1202074519925N00554874EA0106601098000
    +B1202104519904N00554872EA0106001093000
    +B1202134519884N00554867EA0105301089000
    +B1202164519864N00554862EA0104401084000
    +B1202194519842N00554857EA0103801078000
    +B1202224519819N00554853EA0103301073000
    +B1202254519796N00554847EA0102801067000
    +B1202284519776N00554842EA0102101062000
    +B1202324519752N00554834EA0102201054000
    +B1202354519741N00554826EA0102201049000
    +B1202384519728N00554817EA0102801045000
    +B1202414519714N00554811EA0103501041000
    +B1202444519701N00554806EA0104001039000
    +B1202474519687N00554800EA0103901036000
    +B1202504519671N00554795EA0104201035000
    +B1202534519658N00554790EA0104201034000
    +B1202564519644N00554783EA0104001033000
    +B1202594519630N00554776EA0103401033000
    +B1203024519614N00554764EA0102501031000
    +B1203054519598N00554750EA0102501029000
    +B1203084519581N00554739EA0102401027000
    +B1203114519565N00554729EA0103301026000
    +B1203144519554N00554718EA0103701025000
    +B1203174519542N00554703EA0103601025000
    +B1203204519531N00554688EA0104501025000
    +B1203234519522N00554675EA0104801025000
    +B1203264519510N00554660EA0105501026000
    +B1203294519499N00554647EA0105901028000
    +B1203324519488N00554638EA0106201030000
    +B1203354519477N00554630EA0106901032000
    +B1203384519466N00554619EA0107001034000
    +B1203414519453N00554609EA0107301037000
    +B1203444519439N00554601EA0107901039000
    +B1203474519425N00554596EA0107901042000
    +B1203504519412N00554589EA0107601044000
    +B1203534519396N00554581EA0107601046000
    +B1203564519381N00554575EA0106901048000
    +B1203594519363N00554566EA0106801049000
    +B1204034519339N00554556EA0106201050000
    +B1204064519323N00554546EA0106001050000
    +B1204094519307N00554537EA0105101049000
    +B1204124519286N00554532EA0104601048000
    +B1204154519268N00554527EA0103901047000
    +B1204184519248N00554524EA0103101045000
    +B1204214519229N00554522EA0102301042000
    +B1204244519208N00554517EA0101401039000
    +B1204274519185N00554512EA0101001035000
    +B1204304519164N00554508EA0101301032000
    +B1204334519146N00554503EA0101701029000
    +B1204364519131N00554496EA0102101027000
    +B1204394519118N00554485EA0101701025000
    +B1204424519104N00554472EA0101101022000
    +B1204454519091N00554459EA0101201020000
    +B1204484519079N00554445EA0101101018000
    +B1204514519066N00554432EA0100701015000
    +B1204544519053N00554419EA0100301013000
    +B1204574519040N00554404EA0100301010000
    +B1205004519025N00554389EA0101501008000
    +B1205034519017N00554374EA0102101007000
    +B1205064519007N00554356EA0103501007000
    +B1205094518998N00554339EA0104601008000
    +B1205124518988N00554320EA0105301009000
    +B1205154518987N00554292EA0105301011000
    +B1205184519001N00554276EA0105801014000
    +B1205214519016N00554280EA0106001017000
    +B1205244519023N00554295EA0107201020000
    +B1205274519023N00554311EA0108201023000
    +B1205314519007N00554324EA0109501029000
    +B1205344518993N00554319EA0111001034000
    +B1205374518983N00554302EA0111501040000
    +B1205404518983N00554276EA0111501046000
    +B1205434518998N00554258EA0111501051000
    +B1205464519011N00554254EA0112001056000
    +B1205494519019N00554268EA0112701061000
    +B1205524519018N00554284EA0113801067000
    +B1205554519007N00554290EA0114701073000
    +B1205584518999N00554274EA0115501079000
    +B1206014519004N00554249EA0115801085000
    +B1206044519019N00554235EA0116301091000
    +B1206074519032N00554237EA0116801097000
    +B1206104519039N00554255EA0117701103000
    +B1206134519033N00554272EA0119201109000
    +B1206164519023N00554274EA0120101117000
    +B1206194519016N00554257EA0120701124000
    +B1206224519023N00554234EA0121101131000
    +B1206254519039N00554223EA0122001138000
    +B1206284519051N00554227EA0122401145000
    +B1206314519062N00554242EA0123201152000
    +B1206344519063N00554261EA0123601159000
    +B1206374519051N00554271EA0123901165000
    +B1206404519035N00554274EA0125201172000
    +B1206434519022N00554268EA0126201179000
    +B1206464519018N00554248EA0127001186000
    +B1206494519029N00554232EA0128001193000
    +B1206524519043N00554230EA0129001201000
    +B1206554519053N00554242EA0129701209000
    +B1206584519050N00554260EA0130201216000
    +B1207014519036N00554264EA0131001224000
    +B1207044519023N00554257EA0131801231000
    +B1207074519020N00554239EA0132001239000
    +B1207104519030N00554218EA0132701246000
    +B1207134519047N00554215EA0133901253000
    +B1207164519057N00554225EA0134001261000
    +B1207194519062N00554247EA0134101268000
    +B1207234519047N00554264EA0134701276000
    +B1207264519035N00554264EA0135401283000
    +B1207294519022N00554256EA0136001289000
    +B1207324519013N00554248EA0136301295000
    +B1207354519003N00554234EA0135801301000
    +B1207384518992N00554218EA0135601307000
    +B1207414518980N00554193EA0135101311000
    +B1207444518966N00554163EA0135401315000
    +B1207474518955N00554139EA0135401318000
    +B1207504518944N00554112EA0134301320000
    +B1207534518931N00554083EA0134201322000
    +B1207564518919N00554057EA0133801323000
    +B1207594518907N00554029EA0133601323000
    +B1208024518896N00554002EA0133701323000
    +B1208054518884N00553975EA0133401323000
    +B1208084518874N00553945EA0133501323000
    +B1208114518864N00553914EA0133901323000
    +B1208144518855N00553884EA0135601324000
    +B1208174518849N00553864EA0136901327000
    +B1208204518841N00553848EA0137901331000
    +B1208234518829N00553850EA0138701335000
    +B1208264518828N00553866EA0139301339000
    +B1208294518839N00553875EA0140001344000
    +B1208324518848N00553865EA0141101349000
    +B1208354518853N00553848EA0141901354000
    +B1208384518848N00553828EA0143001360000
    +B1208414518836N00553821EA0143701367000
    +B1208444518827N00553828EA0144101374000
    +B1208474518824N00553845EA0144501380000
    +B1208504518834N00553856EA0144901386000
    +B1208544518848N00553845EA0145701394000
    +B1208574518849N00553827EA0146301401000
    +B1209004518844N00553806EA0146701407000
    +B1209034518833N00553795EA0146901413000
    +B1209064518818N00553788EA0146701419000
    +B1209094518804N00553772EA0146701424000
    +B1209124518791N00553754EA0146201428000
    +B1209154518777N00553731EA0145501431000
    +B1209184518761N00553707EA0145301433000
    +B1209214518745N00553684EA0144701434000
    +B1209244518729N00553660EA0144201435000
    +B1209274518713N00553637EA0144001435000
    +B1209304518701N00553615EA0143901435000
    +B1209334518689N00553593EA0143401434000
    +B1209364518674N00553569EA0143001434000
    +B1209394518658N00553546EA0142201432000
    +B1209424518641N00553523EA0141801430000
    +B1209454518625N00553501EA0141101428000
    +B1209484518607N00553481EA0140301425000
    +B1209514518588N00553462EA0139701421000
    +B1209544518570N00553445EA0139201418000
    +B1209574518552N00553428EA0138701414000
    +B1210004518534N00553411EA0138501410000
    +B1210034518517N00553395EA0138201406000
    +B1210064518499N00553380EA0137701402000
    +B1210094518480N00553364EA0137301398000
    +B1210134518455N00553344EA0136901393000
    +B1210164518439N00553330EA0136601390000
    +B1210194518424N00553314EA0135901386000
    +B1210224518407N00553299EA0135301382000
    +B1210254518390N00553285EA0134801377000
    +B1210284518375N00553272EA0134101373000
    +B1210314518359N00553257EA0133301368000
    +B1210344518343N00553242EA0132501363000
    +B1210374518326N00553228EA0131701357000
    +B1210404518310N00553214EA0130701352000
    +B1210434518295N00553198EA0129801345000
    +B1210464518280N00553180EA0129601339000
    +B1210494518266N00553160EA0129101333000
    +B1210524518251N00553136EA0129301327000
    +B1210554518241N00553113EA0131001322000
    +B1210584518236N00553100EA0131801320000
    +B1211014518230N00553089EA0132901318000
    +B1211044518223N00553083EA0133901318000
    +B1211074518215N00553088EA0135301320000
    +B1211104518215N00553105EA0136301322000
    +B1211134518230N00553117EA0136601324000
    +B1211164518246N00553105EA0137301327000
    +B1211194518256N00553089EA0138201331000
    +B1211224518259N00553068EA0139201335000
    +B1211254518259N00553050EA0140501340000
    +B1211284518255N00553045EA0140801343000
    +B1211314518252N00553060EA0141401349000
    +B1211344518265N00553077EA0142601355000
    +B1211374518280N00553078EA0143201362000
    +B1211404518293N00553064EA0144101369000
    +B1211434518298N00553042EA0145101376000
    +B1211464518300N00553022EA0145501383000
    +B1211504518290N00553008EA0145401392000
    +B1211534518284N00553020EA0145201397000
    +B1211564518291N00553043EA0145301402000
    +B1211594518310N00553051EA0146101407000
    +B1212024518326N00553043EA0146901412000
    +B1212054518336N00553020EA0147101418000
    +B1212084518338N00553004EA0147501422000
    +B1212114518333N00552991EA0147201426000
    +B1212144518324N00552977EA0147301431000
    +B1212174518312N00552965EA0147001435000
    +B1212204518300N00552949EA0147401438000
    +B1212234518289N00552931EA0147601442000
    +B1212264518278N00552913EA0147601444000
    +B1212294518268N00552896EA0147601447000
    +B1212324518259N00552876EA0147401449000
    +B1212354518248N00552855EA0147301451000
    +B1212384518238N00552833EA0146601452000
    +B1212414518229N00552808EA0146301453000
    +B1212444518224N00552785EA0146301453000
    +B1212474518220N00552763EA0145901453000
    +B1212504518218N00552738EA0146201453000
    +B1212534518215N00552716EA0145701453000
    +B1212564518209N00552692EA0145101452000
    +B1212594518202N00552670EA0145401452000
    +B1213024518193N00552652EA0145101451000
    +B1213054518183N00552633EA0145101450000
    +B1213084518173N00552615EA0144901449000
    +B1213114518161N00552596EA0144101448000
    +B1213144518147N00552578EA0143701446000
    +B1213184518128N00552558EA0142901443000
    +B1213214518113N00552542EA0142201441000
    +B1213244518098N00552527EA0141701437000
    +B1213274518082N00552513EA0141001434000
    +B1213304518066N00552497EA0140801430000
    +B1213334518052N00552484EA0140401427000
    +B1213364518039N00552469EA0139801423000
    +B1213394518028N00552449EA0139401419000
    +B1213424518019N00552430EA0138801415000
    +B1213454518009N00552409EA0138201410000
    +B1213484517999N00552387EA0137801406000
    +B1213514517990N00552361EA0137901402000
    +B1213544517979N00552339EA0137701398000
    +B1213574517965N00552320EA0137001394000
    +B1214004517950N00552300EA0136801391000
    +B1214034517936N00552283EA0135801386000
    +B1214064517921N00552264EA0135401382000
    +B1214094517906N00552249EA0134601377000
    +B1214124517889N00552235EA0134101373000
    +B1214154517874N00552222EA0133601368000
    +B1214184517859N00552208EA0132601362000
    +B1214214517843N00552192EA0132501357000
    +B1214244517827N00552181EA0131701352000
    +B1214274517811N00552170EA0131001347000
    +B1214304517793N00552158EA0130501341000
    +B1214334517777N00552148EA0129801336000
    +B1214364517762N00552135EA0129101330000
    +B1214404517740N00552117EA0128901323000
    +B1214434517723N00552104EA0128601317000
    +B1214464517706N00552090EA0128401313000
    +B1214494517690N00552078EA0128001308000
    +B1214524517675N00552067EA0127101304000
    +B1214554517657N00552054EA0126401298000
    +B1214584517643N00552041EA0126301293000
    +B1215014517627N00552027EA0125501288000
    +B1215044517612N00552013EA0125401283000
    +B1215074517599N00551999EA0124301278000
    +B1215104517584N00551982EA0124401273000
    +B1215134517570N00551969EA0123901268000
    +B1215164517555N00551953EA0123801263000
    +B1215194517542N00551937EA0123701259000
    +B1215224517528N00551920EA0123301255000
    +B1215254517514N00551903EA0123501251000
    +B1215284517502N00551888EA0123401248000
    +B1215314517487N00551872EA0123601245000
    +B1215344517475N00551856EA0124001243000
    +B1215374517466N00551841EA0124001241000
    +B1215404517454N00551825EA0124301240000
    +B1215434517444N00551811EA0124501239000
    +B1215464517433N00551795EA0125301239000
    +B1215494517424N00551781EA0126301239000
    +B1215524517418N00551765EA0126501240000
    +B1215554517407N00551752EA0127001242000
    +B1215584517397N00551751EA0126601244000
    +B1216024517379N00551749EA0126201246000
    +B1216054517368N00551743EA0126301247000
    +B1216084517355N00551733EA0126201247000
    +B1216114517347N00551722EA0126601248000
    +B1216144517338N00551711EA0127001249000
    +B1216174517332N00551697EA0128101251000
    +B1216204517328N00551681EA0128501253000
    +B1216234517319N00551669EA0129101256000
    +B1216264517309N00551666EA0129201259000
    +B1216294517301N00551656EA0128901262000
    +B1216324517293N00551642EA0128801264000
    +B1216354517284N00551627EA0129501266000
    +B1216384517276N00551616EA0129701268000
    +B1216414517268N00551602EA0129801270000
    +B1216444517260N00551588EA0130301273000
    +B1216474517254N00551574EA0129901275000
    +B1216504517244N00551553EA0130901277000
    +B1216534517239N00551532EA0132301280000
    +B1216564517235N00551514EA0133201284000
    +B1216594517227N00551518EA0133601288000
    +B1217024517233N00551538EA0134901292000
    +B1217054517247N00551550EA0136401297000
    +B1217084517261N00551551EA0137101304000
    +B1217114517274N00551535EA0138201310000
    +B1217144517278N00551512EA0139601317000
    +B1217174517272N00551500EA0140201324000
    +B1217204517267N00551514EA0141201330000
    +B1217234517276N00551533EA0142601339000
    +B1217264517290N00551536EA0143601347000
    +B1217304517310N00551521EA0144901359000
    +B1217334517317N00551503EA0145601368000
    +B1217364517318N00551480EA0146801378000
    +B1217394517310N00551476EA0147501384000
    +B1217424517304N00551492EA0148101393000
    +B1217454517312N00551513EA0149001402000
    +B1217484517329N00551521EA0150101411000
    +B1217514517344N00551514EA0151001421000
    +B1217544517351N00551496EA0151501430000
    +B1217574517348N00551480EA0152401438000
    +B1218004517340N00551485EA0153401444000
    +B1218034517339N00551503EA0153901454000
    +B1218064517350N00551520EA0154801464000
    +B1218094517366N00551514EA0155501472000
    +B1218124517377N00551497EA0156101481000
    +B1218154517381N00551479EA0156601489000
    +B1218184517376N00551465EA0156701497000
    +B1218214517363N00551470EA0157301504000
    +B1218244517362N00551490EA0158101511000
    +B1218274517373N00551507EA0158601517000
    +B1218304517392N00551509EA0158901524000
    +B1218334517408N00551495EA0159501530000
    +B1218364517414N00551477EA0159801536000
    +B1218394517412N00551459EA0160001542000
    +B1218424517405N00551447EA0159201546000
    +B1218454517394N00551425EA0158601550000
    +B1218484517382N00551398EA0158501553000
    +B1218514517371N00551371EA0158301556000
    +B1218544517357N00551346EA0157801558000
    +B1218574517341N00551320EA0157401559000
    +B1219014517319N00551291EA0157101560000
    +B1219044517304N00551268EA0157101560000
    +B1219074517290N00551245EA0156701560000
    +B1219104517273N00551220EA0156501560000
    +B1219134517256N00551197EA0156801559000
    +B1219164517240N00551177EA0156901559000
    +B1219194517224N00551155EA0156601559000
    +B1219224517209N00551132EA0156401559000
    +B1219254517197N00551109EA0155701558000
    +B1219284517184N00551084EA0154701556000
    +B1219314517169N00551061EA0153901554000
    +B1219344517153N00551039EA0152901550000
    +B1219374517139N00551017EA0152001546000
    +B1219404517124N00550999EA0151101542000
    +B1219434517112N00550979EA0150301537000
    +B1219464517102N00550959EA0149401531000
    +B1219494517089N00550940EA0148901526000
    +B1219524517077N00550921EA0147801520000
    +B1219554517063N00550902EA0146401513000
    +B1219584517048N00550881EA0145701506000
    +B1220014517034N00550863EA0144801498000
    +B1220044517021N00550843EA0143601491000
    +B1220074517009N00550823EA0143301483000
    +B1220104516998N00550801EA0143101476000
    +B1220134516990N00550778EA0142501469000
    +B1220164516978N00550756EA0142301462000
    +B1220194516967N00550733EA0142401456000
    +B1220224516958N00550709EA0142301451000
    +B1220264516949N00550673EA0142201444000
    +B1220294516939N00550647EA0142201440000
    +B1220324516929N00550620EA0142401437000
    +B1220354516923N00550594EA0143601434000
    +B1220384516921N00550578EA0144201433000
    +B1220414516918N00550562EA0144201432000
    +B1220444516914N00550548EA0144401431000
    +B1220474516906N00550535EA0144201431000
    +B1220504516898N00550518EA0144301431000
    +B1220534516890N00550503EA0143901431000
    +B1220564516880N00550485EA0143601430000
    +B1220594516871N00550462EA0143701429000
    +B1221024516869N00550435EA0144401429000
    +B1221054516871N00550412EA0144801429000
    +B1221084516866N00550389EA0145901430000
    +B1221114516861N00550368EA0147001433000
    +B1221144516858N00550349EA0148001435000
    +B1221174516856N00550331EA0148701439000
    +B1221204516853N00550314EA0148501443000
    +B1221234516850N00550291EA0148801446000
    +B1221264516846N00550269EA0148801450000
    +B1221294516840N00550248EA0149001453000
    +B1221324516833N00550227EA0149301456000
    +B1221354516827N00550208EA0149301459000
    +B1221384516819N00550190EA0149801462000
    +B1221414516812N00550175EA0149901465000
    +B1221444516805N00550160EA0149901468000
    +B1221484516795N00550137EA0149501471000
    +B1221514516787N00550116EA0148601472000
    +B1221544516777N00550090EA0147901472000
    +B1221574516773N00550068EA0148201472000
    +B1222004516771N00550050EA0147401472000
    +B1222034516764N00550029EA0146801470000
    +B1222064516756N00550004EA0146301469000
    +B1222094516745N00549979EA0146201467000
    +B1222124516733N00549956EA0145601465000
    +B1222154516722N00549930EA0145301463000
    +B1222184516711N00549908EA0145501460000
    +B1222214516702N00549890EA0144901458000
    +B1222244516694N00549867EA0143801455000
    +B1222274516686N00549844EA0143501452000
    +B1222304516679N00549825EA0142301448000
    +B1222334516670N00549803EA0141401444000
    +B1222364516660N00549779EA0140801439000
    +B1222394516650N00549755EA0140201434000
    +B1222424516639N00549730EA0139401429000
    +B1222454516628N00549705EA0138901423000
    +B1222484516618N00549680EA0138201417000
    +B1222514516608N00549652EA0137801412000
    +B1222544516598N00549624EA0137601406000
    +B1222574516589N00549597EA0137901401000
    +B1223004516579N00549575EA0137701397000
    +B1223034516569N00549550EA0137101393000
    +B1223064516558N00549523EA0136801389000
    +B1223104516545N00549488EA0136601384000
    +B1223134516536N00549460EA0136401380000
    +B1223164516527N00549431EA0136501377000
    +B1223194516519N00549405EA0136701374000
    +B1223224516509N00549381EA0136201372000
    +B1223254516496N00549353EA0135601369000
    +B1223284516484N00549328EA0136001366000
    +B1223314516474N00549306EA0135801364000
    +B1223344516460N00549283EA0135701361000
    +B1223374516450N00549261EA0135601359000
    +B1223404516439N00549238EA0135401357000
    +B1223434516430N00549215EA0136001355000
    +B1223464516425N00549200EA0136301354000
    +B1223494516418N00549189EA0135901353000
    +B1223524516408N00549176EA0135401352000
    +B1223554516393N00549162EA0136001351000
    +B1223584516379N00549154EA0137301351000
    +B1224014516368N00549145EA0138001352000
    +B1224044516362N00549129EA0139101354000
    +B1224074516358N00549112EA0140001357000
    +B1224104516351N00549095EA0140201361000
    +B1224134516341N00549077EA0140001364000
    +B1224164516330N00549059EA0140201367000
    +B1224194516320N00549044EA0139901370000
    +B1224224516308N00549027EA0140301372000
    +B1224254516298N00549014EA0140001375000
    +B1224294516282N00548994EA0139801377000
    +B1224324516271N00548979EA0139701378000
    +B1224354516258N00548965EA0139401379000
    +B1224384516245N00548951EA0138801379000
    +B1224414516231N00548938EA0138501379000
    +B1224444516220N00548926EA0138801378000
    +B1224474516213N00548912EA0138901378000
    +B1224504516208N00548896EA0139601379000
    +B1224534516202N00548883EA0139801379000
    +B1224564516193N00548866EA0140601380000
    +B1224594516188N00548852EA0141801382000
    +B1225024516181N00548837EA0142401385000
    +B1225054516173N00548820EA0143301389000
    +B1225084516165N00548803EA0143901393000
    +B1225114516155N00548785EA0144501397000
    +B1225144516145N00548770EA0144601402000
    +B1225174516135N00548754EA0144101405000
    +B1225204516125N00548734EA0144001408000
    +B1225234516117N00548717EA0143201411000
    +B1225264516104N00548696EA0142601412000
    +B1225294516095N00548677EA0142101412000
    +B1225324516084N00548660EA0141601412000
    +B1225354516071N00548644EA0141201411000
    +B1225384516059N00548626EA0141001410000
    +B1225414516047N00548611EA0140501409000
    +B1225444516035N00548595EA0139901407000
    +B1225474516021N00548578EA0139401405000
    +B1225504516007N00548557EA0139901402000
    +B1225534516001N00548539EA0140801401000
    +B1225564515997N00548530EA0140001400000
    +B1226004515982N00548511EA0139601398000
    +B1226034515969N00548496EA0139001396000
    +B1226064515955N00548479EA0138501393000
    +B1226094515942N00548461EA0138301391000
    +B1226124515930N00548443EA0138101388000
    +B1226154515918N00548426EA0138001386000
    +B1226184515906N00548409EA0138001384000
    +B1226214515895N00548395EA0138001383000
    +B1226244515883N00548381EA0137801381000
    +B1226274515870N00548367EA0137601379000
    +B1226304515859N00548350EA0138101378000
    +B1226334515849N00548334EA0138501377000
    +B1226364515840N00548320EA0138901377000
    +B1226394515829N00548304EA0139101377000
    +B1226424515817N00548289EA0139801378000
    +B1226454515804N00548274EA0140001379000
    +B1226484515791N00548259EA0139901380000
    +B1226514515776N00548242EA0139701381000
    +B1226544515759N00548224EA0139801381000
    +B1226574515744N00548208EA0139701382000
    +B1227004515728N00548190EA0139401383000
    +B1227034515712N00548174EA0139701383000
    +B1227064515699N00548159EA0139701383000
    +B1227094515685N00548146EA0139501383000
    +B1227124515668N00548134EA0139001383000
    +B1227154515653N00548121EA0139401383000
    +B1227184515640N00548106EA0140101383000
    +B1227224515627N00548091EA0139801384000
    +B1227254515615N00548076EA0139001384000
    +B1227284515605N00548059EA0138201383000
    +B1227314515592N00548048EA0138601382000
    +B1227344515584N00548041EA0138101381000
    +B1227374515572N00548027EA0137201379000
    +B1227404515560N00548012EA0137001377000
    +B1227434515547N00547997EA0136501375000
    +B1227464515534N00547979EA0136301373000
    +B1227494515521N00547962EA0135901370000
    +B1227524515507N00547943EA0135701368000
    +B1227554515493N00547924EA0135201365000
    +B1227584515478N00547905EA0135101362000
    +B1228014515463N00547886EA0134901360000
    +B1228044515448N00547866EA0135001357000
    +B1228074515433N00547850EA0134701355000
    +B1228104515417N00547831EA0135001353000
    +B1228134515407N00547812EA0135901352000
    +B1228164515400N00547799EA0136201351000
    +B1228194515390N00547786EA0136001351000
    +B1228224515379N00547771EA0135801350000
    +B1228254515367N00547758EA0136101350000
    +B1228284515358N00547746EA0135701350000
    +B1228314515346N00547730EA0135101349000
    +B1228344515335N00547716EA0134701348000
    +B1228374515322N00547699EA0134701347000
    +B1228414515306N00547681EA0134901346000
    +B1228444515295N00547669EA0134501345000
    +B1228474515283N00547656EA0134701343000
    +B1228504515272N00547644EA0135001343000
    +B1228534515261N00547633EA0135201342000
    +B1228564515251N00547622EA0135101342000
    +B1228594515240N00547610EA0136001342000
    +B1229024515230N00547598EA0136201343000
    +B1229054515218N00547586EA0137301344000
    +B1229084515209N00547576EA0138201346000
    +B1229114515198N00547569EA0138601349000
    +B1229144515184N00547559EA0139701352000
    +B1229174515172N00547551EA0140501356000
    +B1229204515162N00547540EA0141501361000
    +B1229234515151N00547528EA0141701365000
    +B1229264515138N00547511EA0141901370000
    +B1229294515127N00547494EA0142301374000
    +B1229324515116N00547477EA0142401379000
    +B1229354515107N00547460EA0142701383000
    +B1229384515098N00547445EA0142801386000
    +B1229414515087N00547426EA0143501390000
    +B1229444515078N00547413EA0143701394000
    +B1229474515068N00547401EA0143201397000
    +B1229504515057N00547384EA0143301400000
    +B1229534515048N00547368EA0144601403000
    +B1229564515044N00547356EA0145301407000
    +B1229594515036N00547342EA0146201411000
    +B1230024515027N00547328EA0147001415000
    +B1230054515018N00547315EA0148101421000
    +B1230084515012N00547306EA0149101425000
    +B1230114515004N00547298EA0149501429000
    +B1230144514995N00547290EA0149501437000
    +B1230184514978N00547274EA0149001444000
    +B1230214514965N00547260EA0149201449000
    +B1230244514957N00547249EA0148701453000
    +B1230274514944N00547233EA0148201455000
    +B1230304514933N00547219EA0147801458000
    +B1230334514919N00547203EA0146801459000
    +B1230364514904N00547185EA0146601459000
    +B1230394514891N00547167EA0146601459000
    +B1230424514880N00547150EA0146401458000
    +B1230454514869N00547134EA0146901458000
    +B1230484514861N00547122EA0146901459000
    +B1230514514850N00547107EA0146701459000
    +B1230544514838N00547093EA0146801459000
    +B1230574514827N00547077EA0146701459000
    +B1231004514814N00547058EA0147201459000
    +B1231034514802N00547042EA0147201459000
    +B1231064514789N00547023EA0147501460000
    +B1231094514773N00547005EA0148201460000
    +B1231124514759N00546990EA0149401462000
    +B1231154514747N00546973EA0150401465000
    +B1231184514738N00546957EA0151301468000
    +B1231214514730N00546940EA0152401472000
    +B1231244514724N00546927EA0153001477000
    +B1231274514716N00546913EA0153501482000
    +B1231304514709N00546900EA0154101487000
    +B1231334514701N00546884EA0154501492000
    +B1231364514689N00546871EA0154401497000
    +B1231394514676N00546854EA0154601501000
    +B1231424514663N00546842EA0153701505000
    +B1231454514646N00546827EA0153001508000
    +B1231484514632N00546807EA0152901509000
    +B1231514514623N00546783EA0153501510000
    +B1231554514610N00546756EA0154301513000
    +B1231584514601N00546742EA0154201515000
    +B1232014514588N00546724EA0153801517000
    +B1232044514576N00546703EA0153901518000
    +B1232074514567N00546687EA0154301519000
    +B1232104514557N00546673EA0155401521000
    +B1232134514548N00546657EA0156001524000
    +B1232164514537N00546637EA0156501527000
    +B1232194514524N00546619EA0156801530000
    +B1232224514514N00546604EA0158001534000
    +B1232254514506N00546595EA0157701537000
    +B1232284514494N00546580EA0157601540000
    +B1232314514481N00546563EA0158401544000
    +B1232344514471N00546547EA0158801547000
    +B1232374514461N00546529EA0159001551000
    +B1232404514450N00546510EA0159501554000
    +B1232434514440N00546495EA0159501558000
    +B1232464514428N00546478EA0159001560000
    +B1232494514414N00546460EA0159101562000
    +B1232524514405N00546439EA0159501564000
    +B1232554514396N00546420EA0159101566000
    +B1232584514387N00546398EA0159001568000
    +B1233014514380N00546382EA0159501569000
    +B1233044514375N00546369EA0159001570000
    +B1233074514366N00546351EA0158501571000
    +B1233104514355N00546333EA0158601571000
    +B1233134514346N00546315EA0158901572000
    +B1233164514339N00546295EA0158801572000
    +B1233194514330N00546272EA0159101573000
    +B1233224514323N00546250EA0159701574000
    +B1233264514315N00546221EA0160701576000
    +B1233294514309N00546203EA0161801579000
    +B1233324514300N00546182EA0162501582000
    +B1233354514288N00546161EA0163201586000
    +B1233384514275N00546135EA0164001590000
    +B1233414514267N00546112EA0165101595000
    +B1233444514256N00546087EA0165701601000
    +B1233474514246N00546063EA0166801606000
    +B1233504514238N00546040EA0167501612000
    +B1233534514229N00546016EA0168701619000
    +B1233564514222N00545995EA0169901626000
    +B1233594514217N00545976EA0170501633000
    +B1234024514211N00545955EA0170801640000
    +B1234054514202N00545931EA0171801647000
    +B1234084514195N00545908EA0172301654000
    +B1234114514188N00545883EA0172801661000
    +B1234144514181N00545858EA0173301668000
    +B1234174514174N00545830EA0173901675000
    +B1234204514166N00545802EA0174601681000
    +B1234234514158N00545777EA0175601688000
    +B1234264514148N00545762EA0175701694000
    +B1234294514137N00545768EA0175201700000
    +B1234324514137N00545790EA0175301705000
    +B1234354514145N00545810EA0175701710000
    +B1234384514151N00545825EA0175801715000
    +B1234414514160N00545843EA0175801719000
    +B1234444514171N00545860EA0176101723000
    +B1234474514182N00545882EA0176701727000
    +B1234504514192N00545902EA0177101731000
    +B1234534514198N00545924EA0177701735000
    +B1234564514206N00545946EA0178801739000
    +B1234594514215N00545962EA0179301744000
    +B1235024514225N00545983EA0180001748000
    +B1235054514237N00546004EA0181301753000
    +B1235084514245N00546015EA0181201759000
    +B1235124514257N00546038EA0181701766000
    +B1235154514267N00546052EA0182501772000
    +B1235184514279N00546068EA0183201777000
    +B1235214514290N00546087EA0184501784000
    +B1235244514298N00546103EA0185301790000
    +B1235274514303N00546120EA0186001797000
    +B1235304514309N00546135EA0186501804000
    +B1235334514317N00546149EA0186401810000
    +B1235364514327N00546164EA0186301816000
    +B1235394514338N00546180EA0186001821000
    +B1235424514350N00546199EA0185601825000
    +B1235454514364N00546220EA0185601828000
    +B1235484514376N00546241EA0185801831000
    +B1235514514388N00546263EA0185701833000
    +B1235544514400N00546286EA0185401835000
    +B1235574514413N00546312EA0185601837000
    +B1236004514426N00546335EA0185201838000
    +B1236034514438N00546357EA0184701839000
    +B1236064514451N00546382EA0185201840000
    +B1236094514462N00546405EA0185701841000
    +B1236124514474N00546432EA0186001842000
    +B1236154514488N00546457EA0187101844000
    +B1236184514500N00546473EA0187001846000
    +B1236214514510N00546491EA0186801848000
    +B1236244514516N00546510EA0186501850000
    +B1236274514525N00546531EA0186601852000
    +B1236304514533N00546547EA0186101853000
    +B1236334514543N00546567EA0185601854000
    +B1236364514556N00546588EA0185801854000
    +B1236404514571N00546609EA0184501853000
    +B1236434514588N00546629EA0185001852000
    +B1236464514598N00546649EA0185101852000
    +B1236494514612N00546671EA0186001851000
    +B1236524514624N00546691EA0187001852000
    +B1236554514634N00546713EA0187801853000
    +B1236584514646N00546736EA0188301856000
    +B1237014514659N00546757EA0188501858000
    +B1237044514668N00546778EA0188901860000
    +B1237074514675N00546795EA0188301863000
    +B1237104514684N00546812EA0187401864000
    +B1237134514696N00546833EA0186201864000
    +B1237164514712N00546856EA0185801863000
    +B1237194514727N00546879EA0185001861000
    +B1237224514743N00546904EA0184501859000
    +B1237254514758N00546929EA0184101857000
    +B1237284514774N00546954EA0183801854000
    +B1237314514792N00546977EA0183701851000
    +B1237344514809N00547002EA0185001849000
    +B1237374514820N00547018EA0185301848000
    +B1237404514831N00547033EA0184701847000
    +B1237434514845N00547057EA0185201846000
    +B1237464514857N00547080EA0185101846000
    +B1237494514870N00547103EA0184301845000
    +B1237524514885N00547129EA0184001844000
    +B1237554514901N00547155EA0183601843000
    +B1237584514919N00547182EA0183501841000
    +B1238014514931N00547207EA0183301840000
    +B1238054514946N00547238EA0182901838000
    +B1238084514959N00547264EA0182801837000
    +B1238114514972N00547281EA0183301835000
    +B1238144514987N00547295EA0182901834000
    +B1238174515002N00547307EA0183301833000
    +B1238204515014N00547318EA0182701832000
    +B1238234515028N00547329EA0182201831000
    +B1238264515041N00547340EA0180801829000
    +B1238294515059N00547354EA0180201826000
    +B1238324515072N00547369EA0180001823000
    +B1238354515090N00547388EA0180501820000
    +B1238384515104N00547406EA0181501818000
    +B1238414515115N00547423EA0181601816000
    +B1238444515128N00547443EA0182201815000
    +B1238474515142N00547459EA0182801815000
    +B1238504515157N00547476EA0183301816000
    +B1238534515175N00547492EA0184001817000
    +B1238564515194N00547510EA0184301819000
    +B1238594515211N00547528EA0184401820000
    +B1239024515230N00547544EA0184401822000
    +B1239054515248N00547560EA0184801824000
    +B1239084515266N00547575EA0184801826000
    +B1239114515283N00547592EA0185101828000
    +B1239144515300N00547610EA0185301830000
    +B1239174515317N00547628EA0185201832000
    +B1239204515334N00547646EA0184801834000
    +B1239234515353N00547663EA0184401835000
    +B1239264515371N00547680EA0183601836000
    +B1239304515395N00547702EA0183301837000
    +B1239334515413N00547719EA0182601836000
    +B1239364515431N00547738EA0182701835000
    +B1239394515448N00547756EA0182301834000
    +B1239424515464N00547776EA0182101832000
    +B1239454515481N00547794EA0182001831000
    +B1239484515498N00547814EA0182401829000
    +B1239514515511N00547831EA0182301828000
    +B1239544515526N00547852EA0181601827000
    +B1239574515543N00547869EA0181301825000
    +B1240004515559N00547886EA0180501823000
    +B1240034515578N00547902EA0180501821000
    +B1240064515594N00547919EA0179901818000
    +B1240094515612N00547938EA0179701816000
    +B1240124515628N00547955EA0179201813000
    +B1240154515645N00547974EA0178901811000
    +B1240184515662N00547993EA0179301808000
    +B1240214515677N00548008EA0179301806000
    +B1240244515693N00548024EA0179601805000
    +B1240274515710N00548040EA0179501803000
    +B1240304515727N00548057EA0180401802000
    +B1240334515742N00548068EA0180401802000
    +B1240364515760N00548078EA0180501802000
    +B1240394515778N00548089EA0180601802000
    +B1240424515794N00548102EA0180001801000
    +B1240454515809N00548118EA0179201801000
    +B1240484515826N00548136EA0178901799000
    +B1240524515849N00548157EA0178901797000
    +B1240554515865N00548168EA0179301796000
    +B1240584515879N00548182EA0179001795000
    +B1241014515895N00548199EA0178501794000
    +B1241044515910N00548219EA0177601792000
    +B1241074515928N00548239EA0177001790000
    +B1241104515944N00548259EA0176301787000
    +B1241134515960N00548280EA0175701783000
    +B1241164515978N00548300EA0175401780000
    +B1241194515995N00548320EA0174901776000
    +B1241224516012N00548342EA0174701773000
    +B1241254516027N00548362EA0174301769000
    +B1241284516044N00548383EA0173701765000
    +B1241314516060N00548404EA0173401762000
    +B1241344516077N00548423EA0172701758000
    +B1241374516095N00548441EA0172201754000
    +B1241404516112N00548459EA0171901750000
    +B1241434516127N00548474EA0172401746000
    +B1241464516140N00548490EA0173101743000
    +B1241494516151N00548506EA0173501742000
    +B1241524516161N00548524EA0173601741000
    +B1241554516172N00548541EA0173101739000
    +B1241584516188N00548562EA0172801738000
    +B1242014516203N00548581EA0172301736000
    +B1242044516219N00548599EA0171801734000
    +B1242074516235N00548616EA0171301732000
    +B1242114516256N00548638EA0170701728000
    +B1242144516272N00548653EA0170301725000
    +B1242174516289N00548668EA0170401722000
    +B1242204516307N00548681EA0171301720000
    +B1242234516324N00548691EA0172701719000
    +B1242264516339N00548701EA0173701719000
    +B1242294516354N00548717EA0174801721000
    +B1242324516367N00548736EA0175601724000
    +B1242354516384N00548749EA0176301727000
    +B1242384516397N00548749EA0175801730000
    +B1242414516413N00548752EA0175801733000
    +B1242444516428N00548759EA0175401735000
    +B1242474516442N00548769EA0175001737000
    +B1242504516457N00548781EA0175601738000
    +B1242534516469N00548795EA0175701740000
    +B1242564516481N00548810EA0176701742000
    +B1242594516495N00548822EA0177601744000
    +B1243024516508N00548836EA0178501748000
    +B1243054516526N00548840EA0179401751000
    +B1243084516534N00548827EA0180301756000
    +B1243114516528N00548811EA0181401761000
    +B1243144516519N00548812EA0181801765000
    +B1243174516510N00548823EA0182501771000
    +B1243204516506N00548842EA0183501777000
    +B1243234516513N00548859EA0184201783000
    +B1243264516529N00548865EA0185601789000
    +B1243294516545N00548864EA0186901797000
    +B1243324516556N00548857EA0187601804000
    +B1243354516556N00548841EA0188901812000
    +B1243384516548N00548839EA0189701816000
    +B1243424516540N00548858EA0190701827000
    +B1243454516542N00548883EA0191801837000
    +B1243484516554N00548904EA0192701846000
    +B1243514516573N00548909EA0193901856000
    +B1243544516584N00548896EA0195401866000
    +B1243574516579N00548888EA0196101871000
    +B1244004516568N00548885EA0197301882000
    +B1244034516559N00548903EA0198101894000
    +B1244064516563N00548928EA0198801904000
    +B1244094516578N00548947EA0199501914000
    +B1244124516596N00548946EA0200501924000
    +B1244154516605N00548931EA0201201933000
    +B1244184516598N00548919EA0202201942000
    +B1244214516586N00548926EA0203501952000
    +B1244244516585N00548949EA0204001961000
    +B1244274516599N00548972EA0204801970000
    +B1244304516618N00548980EA0205201978000
    +B1244334516631N00548968EA0205901987000
    +B1244364516631N00548955EA0206101992000
    +B1244394516618N00548951EA0206802000000
    +B1244424516609N00548969EA0207602008000
    +B1244454516615N00548995EA0208202015000
    +B1244484516630N00549012EA0208402022000
    +B1244514516648N00549019EA0208302029000
    +B1244544516666N00549026EA0208302034000
    +B1244574516683N00549034EA0208302040000
    +B1245004516702N00549044EA0208102044000
    +B1245034516721N00549050EA0208402048000
    +B1245064516736N00549057EA0208202052000
    +B1245094516754N00549062EA0207802055000
    +B1245134516784N00549070EA0207302058000
    +B1245164516807N00549079EA0207302060000
    +B1245194516827N00549090EA0206902061000
    +B1245224516848N00549101EA0205802061000
    +B1245254516871N00549113EA0205602061000
    +B1245284516891N00549127EA0205102060000
    +B1245314516912N00549137EA0204602058000
    +B1245344516935N00549146EA0203902056000
    +B1245374516959N00549163EA0203002054000
    +B1245404516983N00549184EA0201802050000
    +B1245434517003N00549205EA0201602047000
    +B1245464517022N00549218EA0200802043000
    +B1245494517041N00549229EA0200502038000
    +B1245524517054N00549243EA0199502034000
    +B1245554517073N00549253EA0198202029000
    +B1245584517093N00549262EA0198302024000
    +B1246014517110N00549270EA0197502019000
    +B1246044517131N00549279EA0197102014000
    +B1246074517150N00549290EA0196802009000
    +B1246104517169N00549301EA0196102004000
    +B1246134517188N00549312EA0195701999000
    +B1246164517207N00549322EA0195201994000
    +B1246194517227N00549334EA0194901989000
    +B1246224517246N00549344EA0194601984000
    +B1246254517265N00549354EA0194301979000
    +B1246284517284N00549366EA0194001975000
    +B1246324517309N00549382EA0193801969000
    +B1246354517329N00549392EA0193601965000
    +B1246384517347N00549403EA0193201962000
    +B1246414517366N00549414EA0192901958000
    +B1246444517384N00549425EA0192601954000
    +B1246474517403N00549435EA0192201951000
    +B1246504517421N00549446EA0191801947000
    +B1246534517440N00549458EA0191401943000
    +B1246564517460N00549469EA0191001939000
    +B1246594517480N00549482EA0190701936000
    +B1247024517500N00549493EA0190601932000
    +B1247054517519N00549504EA0190301929000
    +B1247084517539N00549516EA0189901925000
    +B1247114517559N00549527EA0189801922000
    +B1247144517579N00549538EA0189501919000
    +B1247174517599N00549552EA0189401916000
    +B1247204517618N00549568EA0189001913000
    +B1247234517637N00549586EA0188901910000
    +B1247264517656N00549599EA0188301907000
    +B1247294517674N00549605EA0188001903000
    +B1247324517688N00549613EA0187301900000
    +B1247354517705N00549623EA0187001896000
    +B1247384517722N00549633EA0187001892000
    +B1247414517738N00549643EA0186801889000
    +B1247444517754N00549655EA0186701886000
    +B1247474517770N00549667EA0186701883000
    +B1247504517787N00549667EA0186501880000
    +B1247544517809N00549677EA0186101877000
    +B1247574517821N00549697EA0186001874000
    +B1248004517832N00549718EA0185501871000
    +B1248034517845N00549740EA0185101868000
    +B1248064517857N00549762EA0184701866000
    +B1248094517870N00549788EA0184301862000
    +B1248124517883N00549814EA0184601860000
    +B1248154517896N00549837EA0184501857000
    +B1248184517909N00549865EA0184601855000
    +B1248214517918N00549892EA0184801854000
    +B1248244517926N00549918EA0184401852000
    +B1248274517934N00549940EA0183901850000
    +B1248304517944N00549963EA0183101848000
    +B1248334517955N00549985EA0182301845000
    +B1248364517966N00550009EA0181101842000
    +B1248394517979N00550037EA0181101838000
    +B1248424517992N00550059EA0181201834000
    +B1248454518005N00550081EA0181301831000
    +B1248484518019N00550104EA0182301828000
    +B1248514518032N00550121EA0182601827000
    +B1248544518043N00550140EA0183001827000
    +B1248574518056N00550157EA0183801827000
    +B1249004518070N00550169EA0184101828000
    +B1249034518086N00550176EA0184401829000
    +B1249064518102N00550172EA0184301831000
    +B1249094518115N00550152EA0184401832000
    +B1249124518114N00550131EA0184701833000
    +B1249164518104N00550122EA0184401835000
    +B1249194518096N00550129EA0184601835000
    +B1249224518097N00550149EA0185001836000
    +B1249254518103N00550168EA0185501838000
    +B1249284518111N00550191EA0185901839000
    +B1249314518118N00550209EA0186201841000
    +B1249344518125N00550226EA0185701843000
    +B1249374518133N00550248EA0185901845000
    +B1249404518139N00550268EA0186401846000
    +B1249434518145N00550287EA0186301848000
    +B1249464518154N00550308EA0186601850000
    +B1249494518163N00550324EA0186701852000
    +B1249524518172N00550343EA0186601853000
    +B1249554518182N00550361EA0186301854000
    +B1249584518191N00550382EA0186301855000
    +B1250014518199N00550406EA0186001855000
    +B1250044518205N00550433EA0186001856000
    +B1250074518212N00550456EA0185401856000
    +B1250104518220N00550483EA0184301855000
    +B1250134518225N00550512EA0183901853000
    +B1250164518226N00550540EA0182901851000
    +B1250194518225N00550567EA0182001848000
    +B1250224518223N00550597EA0181701844000
    +B1250254518220N00550622EA0180901841000
    +B1250284518220N00550654EA0180401837000
    +B1250324518223N00550699EA0180301832000
    +B1250354518227N00550731EA0179601828000
    +B1250384518230N00550765EA0179001824000
    +B1250414518234N00550797EA0178801820000
    +B1250444518238N00550829EA0178201816000
    +B1250474518241N00550861EA0178201812000
    +B1250504518244N00550885EA0178801809000
    +B1250534518248N00550903EA0178701806000
    +B1250564518250N00550924EA0178701803000
    +B1250594518251N00550943EA0179001800000
    +B1251024518252N00550961EA0179201799000
    +B1251054518253N00550979EA0179501797000
    +B1251084518254N00550996EA0179001797000
    +B1251114518255N00551015EA0178701795000
    +B1251144518259N00551035EA0179201794000
    +B1251174518263N00551054EA0179301794000
    +B1251204518263N00551073EA0179401793000
    +B1251234518262N00551093EA0179501793000
    +B1251264518264N00551113EA0180001793000
    +B1251294518269N00551131EA0180301793000
    +B1251324518272N00551151EA0180601794000
    +B1251354518279N00551173EA0181201795000
    +B1251384518286N00551193EA0181601796000
    +B1251414518295N00551214EA0182401798000
    +B1251444518304N00551233EA0183301800000
    +B1251474518311N00551255EA0184201803000
    +B1251504518315N00551275EA0184701807000
    +B1251534518310N00551292EA0184801810000
    +B1251564518296N00551297EA0185901814000
    +B1252004518282N00551293EA0186401819000
    +B1252034518270N00551292EA0187001823000
    +B1252064518259N00551285EA0188301829000
    +B1252094518248N00551277EA0189501834000
    +B1252124518235N00551285EA0190201841000
    +B1252154518235N00551303EA0190601847000
    +B1252184518251N00551314EA0191701853000
    +B1252214518265N00551308EA0193201860000
    +B1252244518275N00551293EA0194501868000
    +B1252274518279N00551273EA0196001876000
    +B1252304518270N00551260EA0197201885000
    +B1252334518258N00551272EA0198001894000
    +B1252364518258N00551292EA0198801903000
    +B1252394518269N00551306EA0199401911000
    +B1252424518285N00551305EA0200701920000
    +B1252454518297N00551294EA0202001929000
    +B1252484518302N00551278EA0203101939000
    +B1252514518294N00551262EA0204401949000
    +B1252544518279N00551266EA0205801959000
    +B1252574518272N00551287EA0207201969000
    +B1253004518276N00551309EA0207701980000
    +B1253034518290N00551314EA0208601990000
    +B1253064518301N00551300EA0209902000000
    +B1253094518303N00551282EA0210802011000
    +B1253124518291N00551268EA0212102021000
    +B1253154518274N00551273EA0213002032000
    +B1253194518270N00551300EA0213602045000
    +B1253224518283N00551310EA0214902055000
    +B1253254518294N00551307EA0215702066000
    +B1253284518300N00551291EA0216502075000
    +B1253314518293N00551273EA0217502085000
    +B1253344518277N00551269EA0218502095000
    +B1253374518265N00551282EA0218902104000
    +B1253404518268N00551299EA0219502114000
    +B1253434518279N00551306EA0220202122000
    +B1253464518291N00551308EA0220802131000
    +B1253494518306N00551309EA0221702140000
    +B1253524518319N00551315EA0222702148000
    +B1253554518333N00551322EA0223702157000
    +B1253584518345N00551326EA0224302166000
    +B1254014518360N00551322EA0225102174000
    +B1254044518371N00551313EA0225702183000
    +B1254074518382N00551298EA0226502191000
    +B1254104518391N00551281EA0227802199000
    +B1254134518400N00551265EA0229102208000
    +B1254164518401N00551245EA0229902217000
    +B1254194518397N00551224EA0230902226000
    +B1254224518387N00551205EA0232402235000
    +B1254254518371N00551201EA0233702244000
    +B1254284518362N00551218EA0234702254000
    +B1254314518368N00551236EA0236002264000
    +B1254344518382N00551247EA0237402275000
    +B1254374518394N00551253EA0238502285000
    +B1254414518407N00551255EA0239002299000
    +B1254444518419N00551257EA0239302309000
    +B1254474518431N00551265EA0239302319000
    +B1254504518446N00551274EA0239302327000
    +B1254534518461N00551284EA0239602334000
    +B1254564518476N00551294EA0239302341000
    +B1254594518492N00551302EA0239402347000
    +B1255024518507N00551309EA0239002353000
    +B1255054518526N00551316EA0238802357000
    +B1255084518546N00551318EA0239702361000
    +B1255114518563N00551326EA0240302365000
    +B1255144518582N00551337EA0241002370000
    +B1255174518602N00551346EA0241702375000
    +B1255204518620N00551358EA0242802380000
    +B1255234518636N00551371EA0242902385000
    +B1255264518654N00551386EA0242702390000
    +B1255294518673N00551401EA0243802394000
    +B1255324518689N00551414EA0244202399000
    +B1255354518706N00551432EA0244602404000
    +B1255384518720N00551449EA0243702408000
    +B1255414518739N00551472EA0243902412000
    +B1255444518756N00551495EA0244202415000
    +B1255474518772N00551517EA0244002418000
    +B1255504518788N00551542EA0244002420000
    +B1255534518803N00551564EA0244202422000
    +B1255564518816N00551582EA0243402424000
    +B1255594518832N00551603EA0243302425000
    +B1256024518850N00551621EA0243102426000
    +B1256054518870N00551638EA0243802427000
    +B1256094518898N00551655EA0245102428000
    +B1256124518916N00551666EA0245802430000
    +B1256154518935N00551684EA0246702433000
    +B1256184518951N00551700EA0247402436000
    +B1256214518967N00551718EA0247902439000
    +B1256244518981N00551736EA0248402443000
    +B1256274518992N00551757EA0248402447000
    +B1256304519004N00551782EA0248502450000
    +B1256334519014N00551808EA0248902454000
    +B1256364519022N00551828EA0248902457000
    +B1256394519034N00551847EA0248102460000
    +B1256424519048N00551870EA0247502461000
    +B1256454519066N00551890EA0247402462000
    +B1256484519084N00551910EA0247002463000
    +B1256514519106N00551927EA0246802462000
    +B1256544519128N00551941EA0246702462000
    +B1256574519149N00551954EA0246102462000
    +B1257004519168N00551970EA0245402461000
    +B1257034519189N00551986EA0245102460000
    +B1257064519209N00552001EA0244902459000
    +B1257094519228N00552018EA0244502457000
    +B1257124519248N00552035EA0244302455000
    +B1257154519268N00552052EA0244002454000
    +B1257184519286N00552069EA0243402452000
    +B1257214519306N00552086EA0242802449000
    +B1257244519328N00552101EA0242602447000
    +B1257274519347N00552114EA0242202444000
    +B1257304519367N00552129EA0241802441000
    +B1257344519394N00552151EA0241802437000
    +B1257374519413N00552167EA0241002434000
    +B1257404519434N00552184EA0240102431000
    +B1257434519457N00552201EA0239302427000
    +B1257464519479N00552218EA0238402423000
    +B1257494519500N00552233EA0237002418000
    +B1257524519522N00552248EA0235702412000
    +B1257554519545N00552265EA0234702405000
    +B1257584519568N00552283EA0234202398000
    +B1258014519590N00552301EA0233802391000
    +B1258044519612N00552317EA0234002385000
    +B1258074519630N00552333EA0233702380000
    +B1258104519649N00552349EA0233002374000
    +B1258134519670N00552367EA0232802369000
    +B1258164519690N00552383EA0231902364000
    +B1258194519713N00552400EA0231702358000
    +B1258224519736N00552413EA0232002353000
    +B1258254519761N00552424EA0232402349000
    +B1258284519784N00552436EA0233202345000
    +B1258314519802N00552449EA0233302343000
    +B1258344519823N00552462EA0233502341000
    +B1258374519844N00552476EA0233602340000
    +B1258404519861N00552489EA0233602339000
    +B1258434519878N00552500EA0233302338000
    +B1258464519899N00552513EA0234402338000
    +B1258494519912N00552526EA0235002338000
    +B1258524519923N00552538EA0235302338000
    +B1258564519942N00552553EA0236502340000
    +B1258594519955N00552565EA0237302342000
    +B1259024519968N00552581EA0237802345000
    +B1259054519982N00552595EA0238602348000
    +B1259084519994N00552607EA0239402352000
    +B1259114520007N00552618EA0239602356000
    +B1259144520023N00552627EA0240202361000
    +B1259174520038N00552637EA0240702365000
    +B1259204520055N00552648EA0242202370000
    +B1259234520068N00552657EA0241902376000
    +B1259264520084N00552668EA0242302380000
    +B1259294520097N00552682EA0242102385000
    +B1259324520112N00552694EA0241602389000
    +B1259354520128N00552704EA0241402392000
    +B1259384520143N00552716EA0240502394000
    +B1259414520159N00552729EA0240402395000
    +B1259444520173N00552744EA0240002396000
    +B1259474520188N00552761EA0240402397000
    +B1259504520202N00552779EA0240902398000
    +B1259534520218N00552796EA0241202399000
    +B1259564520235N00552809EA0242202400000
    +B1259594520250N00552821EA0242702402000
    +B1300024520267N00552829EA0242702405000
    +B1300054520283N00552839EA0242602407000
    +B1300084520302N00552848EA0242702410000
    +B1300114520320N00552857EA0242402412000
    +B1300144520339N00552870EA0242302413000
    +B1300174520356N00552883EA0241902415000
    +B1300204520371N00552895EA0240402415000
    +B1300234520392N00552903EA0239802415000
    +B1300274520419N00552916EA0239302413000
    +B1300304520438N00552931EA0239302411000
    +B1300334520454N00552944EA0238902409000
    +B1300364520472N00552961EA0238702407000
    +B1300394520489N00552976EA0238302405000
    +B1300424520507N00552992EA0237802402000
    +B1300454520526N00553007EA0237602400000
    +B1300484520545N00553022EA0237202397000
    +B1300514520565N00553037EA0237202395000
    +B1300544520583N00553049EA0236702392000
    +B1300574520603N00553061EA0236602389000
    +B1301004520623N00553074EA0236602387000
    +B1301034520641N00553087EA0236502384000
    +B1301064520657N00553101EA0236602382000
    +B1301094520674N00553121EA0235102380000
    +B1301124520696N00553143EA0235002377000
    +B1301154520715N00553162EA0234202374000
    +B1301184520736N00553181EA0233402370000
    +B1301214520759N00553197EA0233202367000
    +B1301244520780N00553211EA0232702363000
    +B1301274520801N00553228EA0232102359000
    +B1301304520825N00553242EA0231802355000
    +B1301334520846N00553257EA0231202351000
    +B1301364520869N00553273EA0230902347000
    +B1301394520889N00553291EA0230702343000
    +B1301424520907N00553307EA0230202338000
    +B1301464520934N00553326EA0229702333000
    +B1301494520953N00553340EA0229202329000
    +B1301524520972N00553355EA0228602324000
    +B1301554520992N00553373EA0228102320000
    +B1301584521011N00553386EA0228102315000
    +B1302014521028N00553397EA0227802311000
    +B1302044521044N00553409EA0226902307000
    +B1302074521063N00553425EA0226102302000
    +B1302104521083N00553440EA0225602297000
    +B1302134521103N00553456EA0225002292000
    +B1302164521123N00553472EA0224602287000
    +B1302194521143N00553486EA0224402282000
    +B1302224521161N00553502EA0223702278000
    +B1302254521177N00553520EA0223502273000
    +B1302284521193N00553537EA0223002269000
    +B1302314521213N00553551EA0223202265000
    +B1302344521230N00553562EA0222902262000
    +B1302374521248N00553574EA0222902259000
    +B1302404521265N00553585EA0223202256000
    +B1302434521282N00553594EA0223102253000
    +B1302464521301N00553600EA0223102251000
    +B1302494521321N00553605EA0223002249000
    +B1302524521342N00553608EA0223402247000
    +B1302554521362N00553611EA0223202245000
    +B1302584521383N00553622EA0224602244000
    +B1303014521402N00553627EA0226002245000
    +B1303044521419N00553631EA0227002246000
    +B1303084521443N00553647EA0229202250000
    +B1303114521460N00553645EA0230602254000
    +B1303144521476N00553648EA0231402259000
    +B1303174521488N00553654EA0231902265000
    +B1303204521501N00553662EA0232502270000
    +B1303234521516N00553668EA0233902277000
    +B1303264521528N00553673EA0234802284000
    +B1303294521541N00553678EA0235002290000
    +B1303324521559N00553685EA0235302297000
    +B1303354521578N00553689EA0236102303000
    +B1303384521597N00553694EA0236602309000
    +B1303414521615N00553700EA0237402316000
    +B1303444521633N00553707EA0237802322000
    +B1303474521653N00553713EA0238602328000
    +B1303504521671N00553719EA0239702335000
    +B1303534521689N00553723EA0240302342000
    +B1303564521706N00553729EA0240702348000
    +B1303594521725N00553737EA0241102355000
    +B1304024521745N00553746EA0242302361000
    +B1304054521762N00553755EA0242702368000
    +B1304084521779N00553763EA0242702374000
    +B1304114521798N00553768EA0243302380000
    +B1304144521819N00553773EA0243302385000
    +B1304174521842N00553779EA0243602390000
    +B1304204521863N00553785EA0243802395000
    +B1304234521881N00553792EA0243302400000
    +B1304264521901N00553798EA0243002403000
    +B1304294521920N00553802EA0243302406000
    +B1304324521939N00553804EA0243102409000
    +B1304354521958N00553811EA0243202411000
    +B1304384521977N00553818EA0243102413000
    +B1304414521998N00553828EA0243202414000
    +B1304444522018N00553838EA0243002416000
    +B1304484522045N00553852EA0242902417000
    +B1304514522066N00553864EA0242902418000
    +B1304544522087N00553876EA0242702419000
    +B1304574522108N00553888EA0242402420000
    +B1305004522129N00553900EA0242002420000
    +B1305034522151N00553911EA0241602419000
    +B1305064522172N00553924EA0241802419000
    +B1305094522191N00553935EA0241102418000
    +B1305124522212N00553945EA0240602417000
    +B1305154522232N00553951EA0239702415000
    +B1305184522252N00553959EA0239202412000
    +B1305214522270N00553969EA0237702409000
    +B1305244522296N00553984EA0236602405000
    +B1305274522324N00553997EA0236402400000
    +B1305304522349N00554011EA0235702396000
    +B1305334522374N00554026EA0235202391000
    +B1305364522399N00554040EA0234602386000
    +B1305394522425N00554053EA0234202381000
    +B1305424522450N00554066EA0233602376000
    +B1305454522475N00554078EA0232802370000
    +B1305484522499N00554090EA0232402365000
    +B1305514522524N00554101EA0231802360000
    +B1305544522547N00554110EA0231002354000
    +B1305574522572N00554118EA0230602348000
    +B1306004522597N00554126EA0230202343000
    +B1306034522621N00554135EA0229702337000
    +B1306064522646N00554145EA0229202332000
    +B1306104522679N00554160EA0228502324000
    +B1306134522705N00554170EA0228002319000
    +B1306164522730N00554180EA0227602313000
    +B1306194522755N00554190EA0227002308000
    +B1306224522780N00554201EA0226702303000
    +B1306254522804N00554213EA0226202298000
    +B1306284522828N00554225EA0225702292000
    +B1306314522851N00554234EA0225202287000
    +B1306344522874N00554243EA0224302282000
    +B1306374522898N00554252EA0224002277000
    +B1306404522920N00554263EA0223502271000
    +B1306434522942N00554276EA0222802266000
    +B1306464522963N00554288EA0222502261000
    +B1306494522981N00554298EA0221702256000
    +B1306524523000N00554306EA0220802250000
    +B1306554523020N00554312EA0220902245000
    +B1306584523038N00554320EA0220402240000
    +B1307014523058N00554332EA0220302235000
    +B1307044523077N00554341EA0220102231000
    +B1307074523095N00554349EA0219302226000
    +B1307104523114N00554356EA0219202222000
    +B1307134523130N00554362EA0218402218000
    +B1307164523147N00554369EA0217402213000
    +B1307194523168N00554376EA0217402208000
    +B1307224523187N00554383EA0217002203000
    +B1307254523209N00554391EA0217002198000
    +B1307294523234N00554401EA0217002193000
    +B1307324523250N00554409EA0217202190000
    +B1307354523265N00554414EA0217202187000
    +B1307384523280N00554422EA0217002184000
    +B1307414523295N00554427EA0217202182000
    +B1307444523310N00554431EA0217302180000
    +B1307474523324N00554436EA0216802179000
    +B1307504523340N00554439EA0216302177000
    +B1307534523357N00554443EA0216302174000
    +B1307564523374N00554447EA0216602172000
    +B1307594523389N00554450EA0216702171000
    +B1308024523406N00554454EA0217102169000
    +B1308054523421N00554457EA0217402168000
    +B1308084523436N00554462EA0217702168000
    +B1308114523448N00554468EA0218102168000
    +B1308144523459N00554471EA0217902169000
    +B1308174523470N00554476EA0217502169000
    +B1308204523483N00554481EA0217602169000
    +B1308234523496N00554486EA0217102169000
    +B1308264523514N00554498EA0216902168000
    +B1308294523531N00554510EA0217702168000
    +B1308324523545N00554516EA0217702168000
    +B1308354523562N00554524EA0217602168000
    +B1308384523583N00554533EA0218102168000
    +B1308414523599N00554542EA0218502169000
    +B1308444523614N00554548EA0218002170000
    +B1308484523640N00554565EA0217702171000
    +B1308514523659N00554579EA0217702171000
    +B1308544523678N00554593EA0217602171000
    +B1308574523697N00554606EA0217902171000
    +B1309004523716N00554619EA0217802172000
    +B1309034523734N00554634EA0217602172000
    +B1309064523753N00554650EA0217502172000
    +B1309094523771N00554664EA0217902172000
    +B1309124523785N00554676EA0217802172000
    +B1309154523801N00554686EA0217902173000
    +B1309184523817N00554694EA0217902173000
    +B1309214523832N00554704EA0217402173000
    +B1309244523848N00554716EA0217302173000
    +B1309274523866N00554731EA0217102173000
    +B1309304523884N00554747EA0217302173000
    +B1309334523903N00554763EA0217402172000
    +B1309364523922N00554779EA0217402172000
    +B1309394523940N00554797EA0217202171000
    +B1309424523957N00554816EA0217302171000
    +B1309454523971N00554830EA0217302171000
    +B1309484523985N00554842EA0216802170000
    +B1309514524000N00554854EA0216902170000
    +B1309544524015N00554864EA0217102169000
    +B1309574524028N00554875EA0217302169000
    +B1310004524043N00554888EA0217202169000
    +B1310034524059N00554904EA0217502169000
    +B1310064524076N00554919EA0217602169000
    +B1310104524099N00554941EA0218502170000
    +B1310134524109N00554954EA0218802171000
    +B1310164524121N00554965EA0218502172000
    +B1310194524135N00554972EA0218502173000
    +B1310224524147N00554979EA0217902174000
    +B1310254524163N00554992EA0217902174000
    +B1310284524179N00555005EA0218002174000
    +B1310314524196N00555020EA0218002174000
    +B1310344524214N00555036EA0219102175000
    +B1310374524229N00555050EA0219302176000
    +B1310404524246N00555065EA0219802177000
    +B1310434524262N00555077EA0220502179000
    +B1310464524277N00555086EA0220702181000
    +B1310494524292N00555096EA0221302184000
    +B1310524524307N00555103EA0221802187000
    +B1310554524324N00555104EA0222702190000
    +B1310584524337N00555091EA0223402194000
    +B1311014524341N00555068EA0223702198000
    +B1311044524331N00555053EA0223702202000
    +B1311074524321N00555064EA0223702205000
    +B1311104524329N00555082EA0225002209000
    +B1311134524339N00555089EA0225502213000
    +B1311164524352N00555095EA0225902217000
    +B1311194524369N00555099EA0226402221000
    +B1311224524388N00555102EA0227102226000
    +B1311254524405N00555103EA0227402230000
    +B1311284524425N00555106EA0228102235000
    +B1311314524445N00555108EA0229502239000
    +B1311344524461N00555107EA0229802245000
    +B1311374524475N00555106EA0230602250000
    +B1311404524490N00555110EA0231502256000
    +B1311434524504N00555113EA0232202262000
    +B1311464524519N00555111EA0232402268000
    +B1311504524537N00555112EA0232802275000
    +B1311534524550N00555118EA0232702280000
    +B1311564524565N00555123EA0232502285000
    +B1311594524582N00555130EA0232502289000
    +B1312024524597N00555135EA0232302292000
    +B1312054524612N00555139EA0232402295000
    +B1312084524626N00555145EA0232702298000
    +B1312114524639N00555150EA0232702300000
    +B1312144524651N00555159EA0233102303000
    +B1312174524663N00555166EA0233102305000
    +B1312204524674N00555175EA0233002308000
    +B1312234524687N00555185EA0233102310000
    +B1312264524700N00555193EA0233002312000
    +B1312294524713N00555204EA0232102313000
    +B1312324524730N00555223EA0231402314000
    +B1312354524747N00555243EA0231302314000
    +B1312384524763N00555259EA0230702313000
    +B1312414524780N00555272EA0230302312000
    +B1312444524798N00555287EA0230302311000
    +B1312474524816N00555302EA0230002309000
    +B1312504524834N00555321EA0230102307000
    +B1312534524853N00555338EA0230102306000
    +B1312564524874N00555353EA0230802304000
    +B1312594524893N00555362EA0231802304000
    +B1313024524905N00555368EA0231702305000
    +B1313054524918N00555375EA0231302305000
    +B1313084524932N00555380EA0231002306000
    +B1313114524947N00555389EA0230602305000
    +B1313144524963N00555397EA0230602304000
    +B1313184524983N00555409EA0230002303000
    +B1313214525000N00555416EA0229802302000
    +B1313244525016N00555422EA0229302300000
    +B1313274525033N00555428EA0229302299000
    +B1313304525047N00555436EA0228602297000
    +B1313334525064N00555448EA0228302295000
    +B1313364525080N00555457EA0227902292000
    +B1313394525096N00555467EA0227602290000
    +B1313424525111N00555477EA0227302287000
    +B1313454525126N00555488EA0226502285000
    +B1313484525144N00555505EA0225602281000
    +B1313514525161N00555525EA0225302277000
    +B1313544525177N00555542EA0224702273000
    +B1313574525193N00555562EA0224602270000
    +B1314004525207N00555582EA0224102266000
    +B1314034525223N00555603EA0224002263000
    +B1314064525238N00555622EA0223402259000
    +B1314094525254N00555642EA0222902255000
    +B1314124525273N00555661EA0222702252000
    +B1314154525290N00555677EA0221802248000
    +B1314184525309N00555696EA0221102244000
    +B1314214525328N00555720EA0221102240000
    +B1314244525344N00555742EA0220602236000
    +B1314274525362N00555764EA0220102231000
    +B1314304525381N00555784EA0219702227000
    +B1314344525409N00555806EA0219102221000
    +B1314374525431N00555821EA0219002216000
    +B1314404525448N00555835EA0219202213000
    +B1314434525463N00555847EA0219102209000
    +B1314464525479N00555859EA0218702206000
    +B1314494525496N00555869EA0218302203000
    +B1314524525513N00555877EA0218402200000
    +B1314554525530N00555886EA0218102197000
    +B1314584525547N00555893EA0218102194000
    +B1315014525563N00555900EA0217902192000
    +B1315044525582N00555907EA0217702189000
    +B1315074525599N00555912EA0217402187000
    +B1315104525617N00555917EA0217102184000
    +B1315134525634N00555921EA0217302181000
    +B1315164525650N00555928EA0216902179000
    +B1315194525667N00555935EA0217002177000
    +B1315224525682N00555942EA0216702175000
    +B1315254525698N00555949EA0216602172000
    +B1315284525714N00555956EA0216302172000
    +B1315314525730N00555962EA0215902169000
    +B1315344525746N00555968EA0215702167000
    +B1315374525762N00555976EA0215302164000
    +B1315404525779N00555983EA0215002162000
    +B1315434525796N00555990EA0214702160000
    +B1315464525813N00555997EA0214402158000
    +B1315494525829N00556004EA0214002155000
    +B1315524525845N00556011EA0213802152000
    +B1315564525864N00556013EA0213402148000
    +B1315594525879N00556010EA0213102145000
    +B1316024525892N00556003EA0212802142000
    +B1316054525906N00555992EA0212402139000
    +B1316084525919N00555981EA0212202136000
    +B1316114525931N00555969EA0212002133000
    +B1316144525943N00555956EA0211702130000
    +B1316174525954N00555941EA0211602128000
    +B1316204525961N00555926EA0211502125000
    +B1316234525969N00555909EA0211002123000
    +B1316264525980N00555894EA0211102120000
    +B1316294525991N00555882EA0210602118000
    +B1316324526005N00555869EA0210102115000
    +B1316354526019N00555856EA0209802113000
    +B1316384526034N00555843EA0209002110000
    +B1316414526050N00555832EA0208602107000
    +B1316444526065N00555821EA0208002103000
    +B1316474526081N00555809EA0207402100000
    +B1316504526096N00555796EA0206702096000
    +B1316534526112N00555782EA0206302092000
    +B1316564526127N00555770EA0205702087000
    +B1316594526144N00555758EA0205402083000
    +B1317024526158N00555746EA0204902079000
    +B1317054526171N00555732EA0204702074000
    +B1317084526184N00555718EA0204602070000
    +B1317124526198N00555702EA0204502066000
    +B1317154526210N00555691EA0204102064000
    +B1317184526221N00555680EA0203702061000
    +B1317214526232N00555668EA0203202057000
    +B1317244526243N00555656EA0202702053000
    +B1317274526255N00555642EA0202002049000
    +B1317304526268N00555628EA0201702044000
    +B1317334526280N00555617EA0201402040000
    +B1317364526292N00555606EA0201302035000
    +B1317394526303N00555594EA0201002031000
    +B1317424526315N00555583EA0200602027000
    +B1317454526325N00555571EA0199902023000
    +B1317484526337N00555557EA0199302019000
    +B1317514526347N00555542EA0198902014000
    +B1317544526358N00555528EA0197602009000
    +B1317574526373N00555511EA0198102004000
    +B1318004526380N00555503EA0197802001000
    +B1318034526389N00555493EA0197001997000
    +B1318064526399N00555480EA0197001992000
    +B1318094526407N00555469EA0195701988000
    +B1318124526420N00555456EA0195701982000
    +B1318154526431N00555443EA0195001978000
    +B1318184526447N00555428EA0194501973000
    +B1318214526465N00555415EA0195001968000
    +B1318244526480N00555407EA0195401965000
    +B1318274526493N00555398EA0195801962000
    +B1318304526506N00555388EA0196101960000
    +B1318334526519N00555381EA0196801959000
    +B1318374526535N00555378EA0197201959000
    +B1318404526544N00555372EA0197401959000
    +B1318434526549N00555358EA0197701960000
    +B1318464526546N00555342EA0198301961000
    +B1318494526533N00555332EA0198301962000
    +B1318524526516N00555339EA0198401963000
    +B1318554526507N00555361EA0198401964000
    +B1318584526513N00555382EA0198501965000
    +B1319014526526N00555387EA0199201966000
    +B1319044526538N00555387EA0199701968000
    +B1319074526547N00555379EA0199901970000
    +B1319104526551N00555364EA0200401972000
    +B1319134526548N00555345EA0200501975000
    +B1319164526537N00555334EA0200501977000
    +B1319194526521N00555337EA0200501978000
    +B1319224526510N00555358EA0200501980000
    +B1319254526511N00555384EA0200801981000
    +B1319284526521N00555401EA0201101983000
    +B1319314526536N00555405EA0201801985000
    +B1319344526547N00555399EA0202101987000
    +B1319374526558N00555388EA0202101989000
    +B1319404526572N00555374EA0202201991000
    +B1319434526587N00555364EA0202401993000
    +B1319464526605N00555355EA0202901996000
    +B1319494526623N00555348EA0203601998000
    +B1319524526639N00555344EA0204202001000
    +B1319554526654N00555339EA0204002005000
    +B1319594526675N00555319EA0203102008000
    +B1320024526693N00555300EA0203502009000
    +B1320054526708N00555283EA0203302011000
    +B1320084526725N00555266EA0203702013000
    +B1320114526741N00555254EA0204802015000
    +B1320144526750N00555246EA0205102018000
    +B1320174526758N00555235EA0205502020000
    +B1320204526768N00555227EA0205902023000
    +B1320234526778N00555219EA0206302026000
    +B1320264526783N00555207EA0206702030000
    +B1320294526775N00555195EA0207202033000
    +B1320324526760N00555196EA0207702036000
    +B1320354526751N00555216EA0208102040000
    +B1320384526754N00555240EA0208402043000
    +B1320414526767N00555251EA0208902046000
    +B1320444526778N00555245EA0209102050000
    +B1320474526782N00555230EA0209802053000
    +B1320504526776N00555217EA0210202056000
    +B1320534526764N00555212EA0210502059000
    +B1320564526749N00555217EA0211002062000
    +B1320594526738N00555235EA0211402066000
    +B1321024526735N00555260EA0211802070000
    +B1321054526744N00555281EA0212002073000
    +B1321084526759N00555287EA0212502078000
    +B1321114526773N00555277EA0212202081000
    +B1321144526793N00555264EA0212702085000
    +B1321174526814N00555255EA0213902089000
    +B1321204526828N00555247EA0214802094000
    +B1321234526837N00555236EA0215402099000
    +B1321264526838N00555218EA0216402105000
    +B1321294526826N00555207EA0217102111000
    +B1321334526807N00555219EA0218202119000
    +B1321364526804N00555243EA0218902125000
    +B1321394526812N00555266EA0219702131000
    +B1321424526826N00555271EA0220602137000
    +B1321454526834N00555262EA0221502144000
    +B1321484526834N00555247EA0222402150000
    +B1321514526823N00555234EA0223302157000
    +B1321544526807N00555236EA0223802163000
    +B1321574526796N00555253EA0224602170000
    +B1322004526799N00555276EA0225202177000
    +B1322034526809N00555294EA0226102183000
    +B1322064526821N00555298EA0226902191000
    +B1322094526831N00555289EA0227602198000
    +B1322124526829N00555272EA0228202205000
    +B1322154526817N00555260EA0229202212000
    +B1322184526801N00555261EA0229702219000
    +B1322214526789N00555277EA0230102226000
    +B1322244526795N00555301EA0230902233000
    +B1322274526809N00555313EA0231902240000
    +B1322304526821N00555308EA0232202247000
    +B1322334526828N00555292EA0233102255000
    +B1322364526828N00555275EA0233702262000
    +B1322394526818N00555261EA0234302270000
    +B1322424526802N00555259EA0234902277000
    +B1322454526785N00555270EA0235402283000
    +B1322484526774N00555292EA0236202290000
    +B1322514526771N00555314EA0236802297000
    +B1322544526771N00555339EA0238002304000
    +B1322574526774N00555360EA0238702311000
    +B1323004526785N00555377EA0239502318000
    +B1323034526799N00555379EA0240302325000
    +B1323064526809N00555370EA0241002333000
    +B1323104526823N00555356EA0241702343000
    +B1323134526835N00555349EA0241802350000
    +B1323164526852N00555345EA0242602357000
    +B1323194526867N00555344EA0243802364000
    +B1323224526882N00555343EA0244502371000
    +B1323254526897N00555340EA0245002378000
    +B1323284526912N00555338EA0245702385000
    +B1323314526928N00555336EA0246502392000
    +B1323344526943N00555335EA0246902399000
    +B1323374526960N00555331EA0247102406000
    +B1323404526976N00555331EA0247202412000
    +B1323434526993N00555333EA0247502418000
    +B1323464527010N00555335EA0247402423000
    +B1323494527028N00555335EA0247302428000
    +B1323524527044N00555332EA0246802432000
    +B1323554527066N00555330EA0245702435000
    +B1323584527092N00555329EA0245502437000
    +B1324014527115N00555330EA0245402438000
    +B1324044527137N00555335EA0245102440000
    +B1324074527159N00555340EA0244802440000
    +B1324104527181N00555347EA0244802441000
    +B1324134527202N00555354EA0245102441000
    +B1324164527219N00555360EA0244902441000
    +B1324194527238N00555362EA0244902441000
    +B1324224527257N00555365EA0245102441000
    +B1324254527274N00555367EA0245002441000
    +B1324284527292N00555370EA0245002441000
    +B1324324527317N00555376EA0244902441000
    +B1324354527335N00555378EA0245302441000
    +B1324384527353N00555380EA0245202441000
    +B1324414527372N00555384EA0245602441000
    +B1324444527389N00555386EA0245802442000
    +B1324474527407N00555386EA0246202443000
    +B1324504527425N00555388EA0246502444000
    +B1324534527443N00555391EA0246802446000
    +B1324564527461N00555393EA0246702447000
    +B1324594527481N00555395EA0246702449000
    +B1325024527501N00555394EA0246702450000
    +B1325054527521N00555393EA0246702451000
    +B1325084527541N00555392EA0246702452000
    +B1325114527560N00555391EA0246702453000
    +B1325144527579N00555389EA0246702454000
    +B1325174527598N00555389EA0246502454000
    +B1325204527617N00555392EA0246502455000
    +B1325234527636N00555394EA0246702456000
    +B1325264527654N00555399EA0246602456000
    +B1325294527673N00555406EA0246602457000
    +B1325324527692N00555414EA0246502457000
    +B1325354527711N00555418EA0246402458000
    +B1325384527730N00555420EA0246302458000
    +B1325414527750N00555422EA0245702458000
    +B1325444527770N00555424EA0245602457000
    +B1325474527789N00555427EA0245102456000
    +B1325504527809N00555431EA0245002455000
    +B1325534527828N00555436EA0244502454000
    +B1325564527847N00555442EA0244002453000
    +B1325594527867N00555448EA0243602452000
    +B1326024527887N00555454EA0243102450000
    +B1326064527913N00555466EA0242802446000
    +B1326094527932N00555475EA0242402444000
    +B1326124527951N00555485EA0241902441000
    +B1326154527970N00555495EA0241502437000
    +B1326184527988N00555508EA0241102433000
    +B1326214528006N00555523EA0240602430000
    +B1326244528024N00555539EA0240102426000
    +B1326274528043N00555554EA0239702421000
    +B1326304528061N00555570EA0239102417000
    +B1326334528081N00555585EA0238602413000
    +B1326364528100N00555600EA0238202408000
    +B1326394528119N00555616EA0237702404000
    +B1326424528139N00555634EA0237502400000
    +B1326454528157N00555652EA0236902395000
    +B1326484528176N00555672EA0236502391000
    +B1326514528195N00555690EA0236102387000
    +B1326544528213N00555710EA0235502383000
    +B1326574528233N00555728EA0234702379000
    +B1327004528252N00555746EA0234002374000
    +B1327034528271N00555765EA0234002370000
    +B1327064528290N00555782EA0233502365000
    +B1327094528309N00555800EA0233102360000
    +B1327124528329N00555821EA0232502356000
    +B1327154528350N00555842EA0232202351000
    +B1327184528373N00555860EA0231602346000
    +B1327224528403N00555887EA0230902340000
    +B1327254528424N00555907EA0230502335000
    +B1327284528445N00555925EA0230002330000
    +B1327314528465N00555941EA0229402325000
    +B1327344528486N00555957EA0228802320000
    +B1327374528507N00555972EA0228202314000
    +B1327404528527N00555988EA0227802309000
    +B1327434528546N00556005EA0227302304000
    +B1327464528565N00556021EA0226902298000
    +B1327494528584N00556034EA0226302293000
    +B1327524528603N00556048EA0225402288000
    +B1327554528623N00556062EA0224902286000
    +B1327584528642N00556078EA0224702282000
    +B1328014528661N00556097EA0224302278000
    +B1328044528679N00556117EA0224102273000
    +B1328074528697N00556137EA0223702269000
    +B1328104528714N00556158EA0223402264000
    +B1328134528732N00556179EA0223102260000
    +B1328164528749N00556200EA0222802255000
    +B1328194528767N00556219EA0222502251000
    +B1328224528784N00556239EA0222202247000
    +B1328254528801N00556260EA0221802243000
    +B1328284528818N00556281EA0221702239000
    +B1328314528835N00556303EA0221702236000
    +B1328344528852N00556323EA0221702232000
    +B1328374528870N00556339EA0221402229000
    +B1328404528888N00556356EA0220902226000
    +B1328444528912N00556382EA0220702222000
    +B1328474528928N00556404EA0220302220000
    +B1328504528944N00556425EA0219902217000
    +B1328534528961N00556446EA0219402214000
    +B1328564528979N00556465EA0219302211000
    +B1328594528996N00556483EA0218802208000
    +B1329024529013N00556502EA0218402204000
    +B1329054529028N00556522EA0218102201000
    +B1329084529044N00556542EA0217902198000
    +B1329114529060N00556560EA0217802195000
    +B1329144529076N00556577EA0217602192000
    +B1329174529093N00556593EA0217202188000
    +B1329204529110N00556607EA0216702185000
    +B1329234529129N00556621EA0216302182000
    +B1329264529146N00556635EA0215802178000
    +B1329294529163N00556651EA0215202174000
    +B1329324529179N00556668EA0214802171000
    +B1329354529194N00556687EA0214302167000
    +B1329384529209N00556706EA0213702165000
    +B1329414529223N00556727EA0213102161000
    +B1329444529237N00556746EA0212702157000
    +B1329474529251N00556764EA0212002155000
    +B1329504529265N00556784EA0211602151000
    +B1329534529282N00556802EA0210702147000
    +B1329564529301N00556824EA0210602146000
    +B1329594529315N00556845EA0210102142000
    +B1330034529334N00556877EA0209302138000
    +B1330064529349N00556900EA0209002134000
    +B1330094529366N00556919EA0208402129000
    +B1330124529383N00556938EA0208302124000
    +B1330154529400N00556953EA0207602119000
    +B1330184529419N00556971EA0207402114000
    +B1330214529436N00556989EA0207002109000
    +B1330244529453N00557007EA0206602104000
    +B1330274529470N00557026EA0206402099000
    +B1330304529486N00557046EA0205902094000
    +B1330334529503N00557067EA0205502089000
    +B1330364529520N00557088EA0205102084000
    +B1330394529537N00557108EA0204702080000
    +B1330424529554N00557130EA0204502075000
    +B1330454529571N00557153EA0204102070000
    +B1330484529587N00557176EA0204002066000
    +B1330514529604N00557198EA0203902062000
    +B1330544529620N00557220EA0203402058000
    +B1330574529637N00557243EA0203302054000
    +B1331004529654N00557264EA0203002051000
    +B1331034529670N00557285EA0202802047000
    +B1331064529686N00557306EA0202502044000
    +B1331094529702N00557328EA0202102041000
    +B1331124529718N00557350EA0201902038000
    +B1331154529734N00557371EA0201602034000
    +B1331194529755N00557400EA0201202030000
    +B1331224529771N00557421EA0200902027000
    +B1331254529787N00557442EA0200702024000
    +B1331284529802N00557463EA0200502021000
    +B1331314529817N00557484EA0200202018000
    +B1331344529832N00557504EA0199902015000
    +B1331374529847N00557523EA0199402012000
    +B1331404529863N00557543EA0199202009000
    +B1331434529877N00557563EA0198802006000
    +B1331464529892N00557583EA0198302003000
    +B1331494529908N00557603EA0197902000000
    +B1331524529924N00557621EA0197401996000
    +B1331554529940N00557641EA0197201993000
    +B1331584529956N00557659EA0196701989000
    +B1332014529973N00557679EA0196501986000
    +B1332044529989N00557698EA0196201982000
    +B1332074530004N00557718EA0195801979000
    +B1332104530019N00557738EA0195501975000
    +B1332134530034N00557757EA0195101972000
    +B1332164530051N00557777EA0194801968000
    +B1332194530067N00557795EA0194501965000
    +B1332224530084N00557813EA0193901962000
    +B1332254530100N00557833EA0193601958000
    +B1332284530116N00557852EA0193001954000
    +B1332314530131N00557873EA0192801950000
    +B1332344530145N00557894EA0192501947000
    +B1332384530166N00557923EA0192301942000
    +B1332414530180N00557944EA0192001938000
    +B1332444530194N00557964EA0191801935000
    +B1332474530209N00557983EA0191501932000
    +B1332504530224N00558002EA0191201928000
    +B1332534530240N00558021EA0190901925000
    +B1332564530255N00558040EA0190401922000
    +B1332594530270N00558060EA0190001919000
    +B1333024530285N00558080EA0189701915000
    +B1333054530300N00558100EA0189101911000
    +B1333084530315N00558121EA0188901908000
    +B1333114530331N00558141EA0188701904000
    +B1333144530345N00558161EA0188601901000
    +B1333174530360N00558181EA0188301898000
    +B1333204530374N00558201EA0188001895000
    +B1333234530389N00558221EA0187701891000
    +B1333264530404N00558240EA0187401888000
    +B1333294530419N00558260EA0187101885000
    +B1333324530434N00558280EA0186901883000
    +B1333354530448N00558300EA0186501880000
    +B1333384530463N00558320EA0186101877000
    +B1333414530478N00558340EA0185801874000
    +B1333444530492N00558361EA0185401871000
    +B1333474530506N00558381EA0185001867000
    +B1333504530520N00558402EA0184601864000
    +B1333534530534N00558422EA0184301861000
    +B1333574530553N00558450EA0183901856000
    +B1334004530567N00558470EA0183601852000
    +B1334034530582N00558490EA0183001848000
    +B1334064530596N00558509EA0182601844000
    +B1334094530611N00558529EA0182401840000
    +B1334124530625N00558549EA0182001836000
    +B1334154530639N00558569EA0181701833000
    +B1334184530654N00558590EA0181301829000
    +B1334214530668N00558610EA0181001825000
    +B1334244530682N00558630EA0180501822000
    +B1334274530697N00558650EA0180401818000
    +B1334304530712N00558670EA0179901814000
    +B1334334530726N00558690EA0179701811000
    +B1334364530741N00558710EA0179301807000
    +B1334394530755N00558730EA0179001804000
    +B1334424530770N00558750EA0178701800000
    +B1334454530785N00558768EA0178501797000
    +B1334484530800N00558787EA0178201794000
    +B1334514530815N00558805EA0177801791000
    +B1334544530830N00558823EA0177501787000
    +B1334574530845N00558841EA0177301784000
    +B1335004530859N00558858EA0177001781000
    +B1335034530874N00558876EA0176701778000
    +B1335064530888N00558893EA0176401775000
    +B1335094530902N00558911EA0176101771000
    +B1335124530917N00558928EA0175601768000
    +B1335164530936N00558951EA0175201763000
    +B1335194530951N00558968EA0175001760000
    +B1335224530965N00558985EA0174501757000
    +B1335254530979N00559002EA0174001753000
    +B1335284530993N00559019EA0173501749000
    +B1335314531007N00559038EA0173301746000
    +B1335344531021N00559057EA0173001742000
    +B1335374531034N00559076EA0172701739000
    +B1335404531048N00559096EA0172501735000
    +B1335434531061N00559115EA0172301732000
    +B1335464531074N00559135EA0171801728000
    +B1335494531087N00559156EA0171601725000
    +B1335524531100N00559176EA0171201722000
    +B1335554531113N00559196EA0171101719000
    +B1335584531127N00559216EA0170701716000
    +B1336014531141N00559236EA0170501713000
    +B1336044531155N00559255EA0170101710000
    +B1336074531169N00559274EA0169701707000
    +B1336104531183N00559293EA0169501704000
    +B1336134531198N00559312EA0169101701000
    +B1336164531212N00559330EA0168801698000
    +B1336194531227N00559348EA0168401695000
    +B1336224531242N00559366EA0167901692000
    +B1336254531257N00559384EA0167601689000
    +B1336284531272N00559402EA0167201685000
    +B1336324531292N00559425EA0166801681000
    +B1336354531306N00559443EA0166301677000
    +B1336384531321N00559462EA0165801673000
    +B1336414531335N00559479EA0165301669000
    +B1336444531349N00559497EA0164801666000
    +B1336474531364N00559515EA0164401662000
    +B1336504531378N00559533EA0163801658000
    +B1336534531393N00559551EA0163301653000
    +B1336564531407N00559569EA0162801649000
    +B1336594531421N00559587EA0162401645000
    +B1337024531434N00559605EA0161901641000
    +B1337054531448N00559624EA0161501637000
    +B1337084531461N00559643EA0161101632000
    +B1337114531474N00559662EA0160701628000
    +B1337144531487N00559681EA0160301624000
    +B1337174531500N00559700EA0159801620000
    +B1337204531512N00559718EA0159201616000
    +B1337234531526N00559737EA0158801612000
    +B1337264531539N00559755EA0158401607000
    +B1337294531552N00559774EA0158001603000
    +B1337324531564N00559793EA0157601599000
    +B1337354531576N00559811EA0157301595000
    +B1337384531588N00559830EA0156901590000
    +B1337414531600N00559849EA0156601586000
    +B1337444531611N00559867EA0156301582000
    +B1337484531627N00559893EA0156001577000
    +B1337514531639N00559913EA0155801573000
    +B1337544531651N00559933EA0155501569000
    +B1337574531662N00559953EA0155301566000
    +B1338004531674N00559973EA0155001562000
    +B1338034531685N00559993EA0154701559000
    +B1338064531696N00600014EA0154401555000
    +B1338094531708N00600036EA0154101552000
    +B1338124531719N00600058EA0153901549000
    +B1338154531729N00600080EA0153601546000
    +B1338184531739N00600104EA0153301543000
    +B1338214531748N00600128EA0153001540000
    +B1338244531757N00600152EA0152801537000
    +B1338274531765N00600174EA0152701534000
    +B1338304531774N00600197EA0152501531000
    +B1338334531782N00600220EA0152401529000
    +B1338364531790N00600243EA0152201526000
    +B1338394531799N00600266EA0152101524000
    +B1338424531807N00600290EA0151901521000
    +B1338454531815N00600314EA0151701519000
    +B1338484531823N00600339EA0151501517000
    +B1338514531832N00600364EA0151301514000
    +B1338544531841N00600389EA0150901512000
    +B1338574531849N00600414EA0150501510000
    +B1339004531858N00600439EA0150001507000
    +B1339044531869N00600473EA0149601503000
    +B1339074531876N00600496EA0149301500000
    +B1339104531885N00600519EA0148801497000
    +B1339134531893N00600542EA0148601494000
    +B1339164531901N00600564EA0148201491000
    +B1339194531909N00600588EA0148001488000
    +B1339224531918N00600611EA0147601484000
    +B1339254531927N00600634EA0147401481000
    +B1339284531935N00600655EA0147201478000
    +B1339314531941N00600677EA0146701475000
    +B1339344531948N00600696EA0146101472000
    +B1339374531955N00600718EA0145501468000
    +B1339404531964N00600738EA0145101465000
    +B1339434531975N00600756EA0144501461000
    +B1339464531986N00600776EA0144101457000
    +B1339494531995N00600797EA0143801453000
    +B1339524532005N00600819EA0143401449000
    +B1339554532015N00600839EA0143301446000
    +B1339584532025N00600856EA0142701442000
    +B1340014532035N00600873EA0142001438000
    +B1340044532046N00600892EA0141401434000
    +B1340074532055N00600912EA0141101430000
    +B1340104532066N00600931EA0140701426000
    +B1340134532076N00600951EA0140301422000
    +B1340164532085N00600970EA0139701418000
    +B1340194532096N00600991EA0139401413000
    +B1340234532109N00601019EA0138901408000
    +B1340264532120N00601041EA0139001404000
    +B1340294532128N00601061EA0138801400000
    +B1340324532138N00601081EA0138501396000
    +B1340354532146N00601101EA0138401393000
    +B1340384532153N00601127EA0138101390000
    +B1340414532159N00601152EA0138301387000
    +B1340444532162N00601175EA0137801385000
    +B1340474532168N00601201EA0137401382000
    +B1340504532175N00601223EA0137101380000
    +B1340534532183N00601243EA0136601377000
    +B1340564532190N00601265EA0136101374000
    +B1340594532198N00601291EA0135801370000
    +B1341024532206N00601314EA0135601367000
    +B1341054532217N00601335EA0135801364000
    +B1341084532229N00601353EA0136101362000
    +B1341114532240N00601369EA0136601360000
    +B1341144532251N00601385EA0137201359000
    +B1341174532263N00601398EA0137401359000
    +B1341204532274N00601405EA0136801359000
    +B1341234532288N00601413EA0135801358000
    +B1341264532302N00601428EA0135501356000
    +B1341294532314N00601447EA0135401354000
    +B1341324532325N00601465EA0135201353000
    +B1341354532339N00601485EA0135401351000
    +B1341384532352N00601504EA0135901350000
    +B1341414532366N00601518EA0136401349000
    +B1341444532379N00601525EA0136901349000
    +B1341484532394N00601521EA0137401350000
    +B1341514532398N00601511EA0137901350000
    +B1341544532396N00601502EA0138401351000
    +B1341574532387N00601497EA0139001352000
    +B1342004532374N00601507EA0139501354000
    +B1342034532367N00601534EA0139601356000
    +B1342064532373N00601564EA0139901359000
    +B1342094532387N00601578EA0140301361000
    +B1342124532398N00601580EA0140601364000
    +B1342154532409N00601575EA0140701367000
    +B1342184532414N00601565EA0141101368000
    +B1342214532415N00601555EA0141601370000
    +B1342244532416N00601545EA0141601372000
    +B1342274532409N00601536EA0142201375000
    +B1342304532396N00601538EA0142501378000
    +B1342334532384N00601553EA0142701382000
    +B1342364532377N00601583EA0142701385000
    +B1342394532383N00601613EA0142901387000
    +B1342424532396N00601628EA0143101390000
    +B1342454532407N00601628EA0143401393000
    +B1342484532414N00601619EA0143601394000
    +B1342514532419N00601607EA0144001396000
    +B1342544532424N00601597EA0144101398000
    +B1342574532426N00601586EA0144501399000
    +B1343004532422N00601577EA0144901401000
    +B1343034532409N00601578EA0145401405000
    +B1343064532397N00601594EA0145801408000
    +B1343104532395N00601635EA0145701413000
    +B1343134532407N00601659EA0145901416000
    +B1343164532421N00601663EA0146401420000
    +B1343194532429N00601656EA0146501423000
    +B1343224532435N00601646EA0146801425000
    +B1343254532439N00601634EA0146601427000
    +B1343284532440N00601621EA0147101430000
    +B1343314532444N00601611EA0147401432000
    +B1343344532445N00601597EA0147201435000
    +B1343374532441N00601586EA0147201437000
    +B1343404532432N00601583EA0146801438000
    +B1343434532418N00601597EA0147001441000
    +B1343464532413N00601626EA0147001443000
    +B1343494532421N00601657EA0147201445000
    +B1343524532436N00601671EA0147601447000
    +B1343554532445N00601666EA0147601449000
    +B1343584532442N00601655EA0147701451000
    +B1344014532434N00601650EA0147901452000
    +B1344044532428N00601644EA0147901453000
    +B1344074532424N00601635EA0148601454000
    +B1344104532427N00601626EA0148801456000
    +B1344134532441N00601619EA0149201458000
    +B1344164532456N00601629EA0149001460000
    +B1344194532467N00601652EA0148501462000
    +B1344224532464N00601680EA0147801463000
    +B1344254532449N00601694EA0148101463000
    +B1344284532436N00601704EA0148501464000
    +B1344314532422N00601708EA0149201465000
    +B1344344532413N00601707EA0149501465000
    +B1344384532407N00601698EA0150001466000
    +B1344414532409N00601687EA0150601467000
    +B1344444532419N00601678EA0150301470000
    +B1344474532435N00601678EA0150401472000
    +B1344504532452N00601686EA0150401474000
    +B1344534532467N00601703EA0150201476000
    +B1344564532481N00601723EA0149601478000
    +B1344594532494N00601750EA0149201478000
    +B1345024532507N00601781EA0148801479000
    +B1345054532520N00601812EA0149601479000
    +B1345084532528N00601832EA0149501479000
    +B1345114532538N00601852EA0148601479000
    +B1345144532553N00601871EA0148801479000
    +B1345174532565N00601884EA0148101478000
    +B1345204532579N00601900EA0147401477000
    +B1345234532596N00601924EA0147801475000
    +B1345264532606N00601948EA0148701475000
    +B1345294532610N00601965EA0147901474000
    +B1345324532618N00601989EA0147601473000
    +B1345354532627N00602020EA0147601472000
    +B1345384532633N00602051EA0147801471000
    +B1345414532646N00602076EA0146601469000
    +B1345444532662N00602104EA0146801467000
    +B1345474532673N00602128EA0146301466000
    +B1345504532684N00602149EA0145301464000
    +B1345534532695N00602172EA0144401461000
    +B1345574532705N00602204EA0143401456000
    +B1346004532717N00602232EA0141901451000
    +B1346034532732N00602257EA0141501446000
    +B1346064532744N00602280EA0140401440000
    +B1346094532758N00602307EA0139501434000
    +B1346124532770N00602333EA0138801428000
    +B1346154532781N00602361EA0137701422000
    +B1346184532792N00602392EA0136901416000
    +B1346214532802N00602422EA0136301409000
    +B1346244532810N00602452EA0135501402000
    +B1346274532819N00602483EA0134901395000
    +B1346304532827N00602512EA0135601389000
    +B1346334532833N00602535EA0135401384000
    +B1346364532843N00602560EA0134801379000
    +B1346394532853N00602588EA0135101374000
    +B1346424532860N00602615EA0135001369000
    +B1346454532867N00602643EA0134401365000
    +B1346484532873N00602670EA0134301360000
    +B1346514532877N00602694EA0133301356000
    +B1346544532884N00602723EA0132701351000
    +B1346574532890N00602753EA0132501347000
    +B1347004532894N00602782EA0132201342000
    +B1347034532897N00602812EA0131601338000
    +B1347064532902N00602842EA0131501333000
    +B1347094532905N00602870EA0131401329000
    +B1347124532905N00602897EA0131201325000
    +B1347164532903N00602929EA0131101320000
    +B1347194532901N00602953EA0130601316000
    +B1347224532901N00602976EA0130501313000
    +B1347254532903N00602998EA0130001310000
    +B1347284532906N00603019EA0130101306000
    +B1347314532911N00603038EA0129501303000
    +B1347344532918N00603057EA0129601300000
    +B1347374532927N00603071EA0129201297000
    +B1347404532937N00603085EA0128901295000
    +B1347434532949N00603095EA0128801292000
    +B1347464532963N00603105EA0128701289000
    +B1347494532976N00603114EA0129601287000
    +B1347524532985N00603124EA0129301286000
    +B1347554532995N00603134EA0129501284000
    +B1347584533005N00603140EA0130301283000
    +B1348014533015N00603141EA0130501283000
    +B1348044533026N00603139EA0131101284000
    +B1348074533035N00603135EA0131301284000
    +B1348104533045N00603129EA0131701286000
    +B1348134533054N00603129EA0131801287000
    +B1348164533065N00603126EA0131401288000
    +B1348194533077N00603124EA0131201289000
    +B1348224533087N00603122EA0130601289000
    +B1348254533100N00603122EA0131001289000
    +B1348284533113N00603123EA0131501290000
    +B1348314533124N00603120EA0131501291000
    +B1348354533135N00603110EA0132001292000
    +B1348384533140N00603106EA0132301293000
    +B1348414533147N00603102EA0132401294000
    +B1348444533155N00603100EA0133301295000
    +B1348474533162N00603101EA0133401297000
    +B1348504533172N00603097EA0134501299000
    +B1348534533178N00603092EA0135201301000
    +B1348564533184N00603083EA0135301303000
    +B1348594533192N00603077EA0135901307000
    +B1349024533201N00603079EA0136401311000
    +B1349054533212N00603080EA0136901316000
    +B1349084533220N00603072EA0137101320000
    +B1349114533218N00603060EA0136901324000
    +B1349144533203N00603060EA0137001328000
    +B1349174533192N00603080EA0137001331000
    +B1349204533196N00603105EA0137401333000
    +B1349234533204N00603119EA0137601336000
    +B1349264533214N00603126EA0137801339000
    +B1349294533225N00603126EA0137701341000
    +B1349324533237N00603124EA0137501343000
    +B1349354533251N00603124EA0137401345000
    +B1349384533265N00603120EA0137901347000
    +B1349414533278N00603116EA0138501349000
    +B1349444533288N00603106EA0139201352000
    +B1349474533289N00603091EA0139701355000
    +B1349504533277N00603080EA0140301358000
    +B1349534533261N00603086EA0140901361000
    +B1349564533254N00603109EA0141501365000
    +B1349594533263N00603123EA0141901368000
    +B1350024533274N00603114EA0142501372000
    +B1350064533266N00603092EA0143501377000
    +B1350094533250N00603090EA0144201382000
    +B1350124533239N00603107EA0145201387000
    +B1350154533239N00603129EA0145801392000
    +B1350184533247N00603145EA0146201398000
    +B1350214533260N00603144EA0146701403000
    +B1350244533265N00603127EA0147401409000
    +B1350274533253N00603112EA0148401415000
    +B1350304533237N00603113EA0149101420000
    +B1350334533222N00603130EA0150101426000
    +B1350364533219N00603152EA0150501432000
    +B1350394533231N00603167EA0151101438000
    +B1350424533242N00603162EA0152101445000
    +B1350454533245N00603148EA0152701451000
    +B1350484533236N00603132EA0153801458000
    +B1350514533221N00603125EA0154601465000
    +B1350544533202N00603126EA0155501472000
    +B1350574533188N00603142EA0156001480000
    +B1351004533189N00603161EA0155901487000
    +B1351034533200N00603174EA0157201494000
    +B1351064533209N00603176EA0158201500000
    +B1351094533214N00603164EA0159101508000
    +B1351124533207N00603151EA0159501516000
    +B1351154533189N00603149EA0160701525000
    +B1351184533175N00603164EA0161401533000
    +B1351214533170N00603185EA0161601541000
    +B1351244533176N00603205EA0161701548000
    +B1351274533189N00603212EA0162701555000
    +B1351304533198N00603208EA0163801561000
    +B1351334533205N00603200EA0164501566000
    +B1351364533199N00603185EA0165101574000
    +B1351404533177N00603195EA0165801584000
    +B1351434533167N00603215EA0167101591000
    +B1351464533167N00603230EA0167601599000
    +B1351494533175N00603242EA0168401606000
    +B1351524533184N00603243EA0169001612000
    +B1351554533189N00603230EA0169901620000
    +B1351584533183N00603216EA0170701628000
    +B1352014533168N00603214EA0171001636000
    +B1352044533152N00603229EA0171901643000
    +B1352074533144N00603249EA0172901650000
    +B1352104533145N00603267EA0173401658000
    +B1352134533153N00603279EA0174201666000
    +B1352164533165N00603281EA0175101674000
    +B1352194533174N00603271EA0175701682000
    +B1352224533170N00603254EA0176301689000
    +B1352254533155N00603251EA0176701697000
    +B1352284533142N00603269EA0177401703000
    +B1352314533142N00603293EA0178501710000
    +B1352344533149N00603313EA0180001717000
    +B1352374533160N00603321EA0180801725000
    +B1352404533168N00603309EA0181701733000
    +B1352434533161N00603295EA0182201741000
    +B1352464533146N00603292EA0182701749000
    +B1352494533132N00603307EA0183401757000
    +B1352524533126N00603330EA0184601764000
    +B1352554533131N00603350EA0185201772000
    +B1352584533144N00603350EA0186101780000
    +B1353014533151N00603337EA0187101788000
    +B1353044533147N00603321EA0187501796000
    +B1353074533133N00603314EA0188001803000
    +B1353104533119N00603326EA0188701810000
    +B1353134533112N00603347EA0189401817000
    +B1353164533112N00603369EA0190001824000
    +B1353204533123N00603388EA0190201834000
    +B1353234533134N00603388EA0190501840000
    +B1353264533141N00603372EA0190901846000
    +B1353294533140N00603353EA0191601853000
    +B1353324533134N00603335EA0192101859000
    +B1353354533119N00603326EA0192301865000
    +B1353384533106N00603336EA0192501870000
    +B1353414533098N00603358EA0193201875000
    +B1353444533095N00603378EA0194001881000
    +B1353474533101N00603390EA0193701886000
    +B1353504533112N00603395EA0193701891000
    +B1353534533122N00603383EA0193801895000
    +B1353564533124N00603363EA0193601899000
    +B1353594533120N00603342EA0193701902000
    +B1354024533106N00603327EA0193901905000
    +B1354054533090N00603323EA0193701908000
    +B1354084533071N00603335EA0193701910000
    +B1354114533065N00603359EA0193801911000
    +B1354144533073N00603376EA0194201913000
    +B1354174533082N00603387EA0193801915000
    +B1354204533095N00603402EA0193701916000
    +B1354234533105N00603418EA0194001918000
    +B1354264533114N00603430EA0193601919000
    +B1354294533127N00603439EA0193501920000
    +B1354324533142N00603448EA0193001920000
    +B1354354533158N00603459EA0192801920000
    +B1354384533175N00603470EA0192301919000
    +B1354414533191N00603483EA0192301918000
    +B1354454533211N00603501EA0192201917000
    +B1354484533228N00603516EA0192001916000
    +B1354514533245N00603530EA0191801915000
    +B1354544533262N00603542EA0191601914000
    +B1354574533280N00603553EA0191201913000
    +B1355004533298N00603564EA0191001912000
    +B1355034533316N00603576EA0190501910000
    +B1355064533336N00603588EA0190101908000
    +B1355094533355N00603600EA0189901906000
    +B1355124533372N00603612EA0189401904000
    +B1355154533392N00603623EA0189101901000
    +B1355184533412N00603635EA0188901899000
    +B1355214533431N00603649EA0188901896000
    +B1355244533447N00603663EA0189101894000
    +B1355274533462N00603674EA0188601892000
    +B1355304533480N00603682EA0187901890000
    +B1355334533499N00603691EA0187301887000
    +B1355364533517N00603701EA0186501884000
    +B1355394533533N00603714EA0186501880000
    +B1355424533548N00603729EA0186201877000
    +B1355454533566N00603746EA0186401874000
    +B1355484533585N00603759EA0186701871000
    +B1355514533599N00603771EA0186301869000
    +B1355544533617N00603780EA0185801866000
    +B1355574533635N00603791EA0185801864000
    +B1356004533654N00603801EA0185901862000
    +B1356044533674N00603807EA0186401859000
    +B1356074533680N00603806EA0186201859000
    +B1356104533689N00603806EA0185501857000
    +B1356134533701N00603813EA0185501856000
    +B1356164533712N00603820EA0184101854000
    +B1356194533731N00603833EA0183701851000
    +B1356224533749N00603845EA0183601849000
    +B1356254533768N00603858EA0182601846000
    +B1356284533791N00603874EA0183001843000
    +B1356314533807N00603888EA0183101840000
    +B1356344533825N00603903EA0182201837000
    +B1356374533842N00603920EA0182401835000
    +B1356404533856N00603933EA0181601832000
    +B1356434533874N00603949EA0181501829000
    +B1356464533887N00603963EA0181001826000
    +B1356494533903N00603976EA0180301823000
    +B1356524533921N00603985EA0180501819000
    +B1356554533936N00603993EA0180301816000
    +B1356584533954N00604000EA0181301814000
    +B1357014533968N00604001EA0182501813000
    +B1357044533980N00603999EA0183001812000
    +B1357074533993N00604012EA0183501813000
    +B1357104533991N00604043EA0184401815000
    +B1357144533977N00604058EA0184701817000
    +B1357174533960N00604057EA0185201819000
    +B1357204533955N00604041EA0186501822000
    +B1357234533959N00604030EA0187201824000
    +B1357264533972N00604026EA0188001829000
    +B1357294533981N00604042EA0188801833000
    +B1357324533971N00604059EA0189601838000
    +B1357354533956N00604063EA0190701843000
    +B1357384533944N00604057EA0191501849000
    +B1357414533939N00604043EA0191901855000
    +B1357444533944N00604030EA0191801861000
    +B1357474533958N00604027EA0191901866000
    +B1357504533975N00604034EA0192601871000
    +B1357534533990N00604047EA0193701876000
    +B1357564534003N00604064EA0194301882000
    +B1357594534011N00604087EA0194501888000
    +B1358024534005N00604110EA0194201893000
    +B1358054533988N00604115EA0193901897000
    +B1358084533978N00604102EA0194101900000
    +B1358114533980N00604086EA0194701904000
    +B1358154533987N00604063EA0195401908000
    +B1358184533993N00604048EA0195801912000
    +B1358214533999N00604037EA0195401916000
    +B1358244534013N00604031EA0194901918000
    +B1358274534028N00604037EA0195401921000
    +B1358304534040N00604045EA0195101923000
    +B1358334534055N00604056EA0195101925000
    +B1358364534067N00604067EA0195801927000
    +B1358394534078N00604075EA0195901930000
    +B1358424534090N00604084EA0197101933000
    +B1358454534100N00604089EA0197401936000
    +B1358484534112N00604094EA0197501940000
    +B1358514534124N00604098EA0197001943000
    +B1358544534138N00604099EA0195901944000
    +B1358574534154N00604102EA0194901945000
    +B1359004534173N00604106EA0194401944000
    +B1359034534190N00604115EA0193701943000
    +B1359064534209N00604125EA0192101941000
    +B1359094534233N00604140EA0191901937000
    +B1359124534254N00604154EA0191201934000
    +B1359154534278N00604169EA0190701930000
    +B1359184534299N00604182EA0190501926000
    +B1359214534319N00604196EA0189101922000
    +B1359244534342N00604212EA0188101917000
    +B1359274534363N00604228EA0187601912000
    +B1359304534383N00604240EA0186701906000
    +B1359344534414N00604254EA0185901898000
    +B1359374534438N00604264EA0185101892000
    +B1359404534461N00604277EA0184701885000
    +B1359434534483N00604287EA0183801879000
    +B1359464534505N00604297EA0183001873000
    +B1359494534529N00604306EA0182501866000
    +B1359524534551N00604315EA0182001860000
    +B1359554534573N00604325EA0181401854000
    +B1359584534595N00604335EA0180701848000
    +B1400014534616N00604346EA0179801841000
    +B1400044534638N00604357EA0179001834000
    +B1400074534661N00604369EA0178201828000
    +B1400104534684N00604381EA0177301821000
    +B1400134534707N00604392EA0176701814000
    +B1400164534730N00604403EA0175901807000
    +B1400194534753N00604415EA0175301800000
    +B1400224534777N00604426EA0174701793000
    +B1400254534800N00604436EA0174201786000
    +B1400284534824N00604446EA0173901780000
    +B1400314534846N00604453EA0173101773000
    +B1400344534869N00604461EA0172501767000
    +B1400374534891N00604470EA0171801760000
    +B1400404534913N00604479EA0171501754000
    +B1400434534935N00604488EA0171101748000
    +B1400464534957N00604492EA0170601742000
    +B1400504534988N00604498EA0170101734000
    +B1400534535011N00604504EA0170601729000
    +B1400564535029N00604511EA0171301725000
    +B1400594535044N00604519EA0171801723000
    +B1401024535058N00604526EA0171801721000
    +B1401054535073N00604527EA0171601719000
    +B1401084535090N00604528EA0171601717000
    +B1401114535105N00604529EA0171601716000
    +B1401144535121N00604531EA0171701715000
    +B1401174535139N00604532EA0171401714000
    +B1401204535162N00604534EA0172001713000
    +B1401234535181N00604543EA0172901713000
    +B1401264535197N00604548EA0173301713000
    +B1401294535214N00604551EA0173801714000
    +B1401324535229N00604554EA0173801715000
    +B1401354535244N00604551EA0173301716000
    +B1401384535262N00604549EA0173101717000
    +B1401414535282N00604556EA0173801717000
    +B1401444535298N00604565EA0173801718000
    +B1401474535316N00604576EA0173601718000
    +B1401504535333N00604584EA0173701719000
    +B1401534535350N00604591EA0173301719000
    +B1401564535369N00604598EA0173401719000
    +B1401594535387N00604603EA0173301720000
    +B1402024535406N00604611EA0173601720000
    +B1402054535422N00604618EA0173601721000
    +B1402084535439N00604627EA0173701721000
    +B1402124535462N00604634EA0173801722000
    +B1402154535482N00604639EA0175001723000
    +B1402184535498N00604642EA0176001725000
    +B1402214535516N00604644EA0176701728000
    +B1402244535535N00604650EA0178101732000
    +B1402274535548N00604667EA0178901736000
    +B1402304535549N00604693EA0179901741000
    +B1402334535539N00604703EA0181201746000
    +B1402364535532N00604700EA0182301749000
    +B1402394535533N00604684EA0183401756000
    +B1402424535547N00604676EA0184501764000
    +B1402454535563N00604688EA0185301772000
    +B1402484535567N00604713EA0186601780000
    +B1402514535559N00604726EA0187501789000
    +B1402544535552N00604719EA0188801795000
    +B1402574535556N00604705EA0189601804000
    +B1403004535572N00604701EA0190701814000
    +B1403034535587N00604717EA0192201824000
    +B1403064535595N00604737EA0193101834000
    +B1403094535587N00604760EA0194101844000
    +B1403124535576N00604760EA0195901853000
    +B1403154535577N00604747EA0197101860000
    +B1403184535588N00604738EA0198201872000
    +B1403214535601N00604747EA0199401883000
    +B1403244535602N00604772EA0200301894000
    +B1403274535594N00604797EA0201401906000
    +B1403304535579N00604803EA0202901917000
    +B1403334535576N00604792EA0204201925000
    +B1403364535584N00604779EA0205201938000
    +B1403394535602N00604777EA0206501951000
    +B1403424535614N00604796EA0207701964000
    +B1403454535611N00604819EA0208401976000
    +B1403484535597N00604835EA0209401991000
    +B1403524535580N00604824EA0210902003000
    +B1403554535585N00604808EA0212402015000
    +B1403584535598N00604807EA0214002027000
    +B1404014535611N00604821EA0215202039000
    +B1404044535612N00604846EA0216102051000
    +B1404074535599N00604862EA0217102062000
    +B1404104535582N00604863EA0218402074000
    +B1404134535575N00604850EA0219402086000
    +B1404164535581N00604833EA0220402097000
    +B1404194535595N00604824EA0221802109000
    +B1404224535610N00604833EA0223102121000
    +B1404254535617N00604853EA0223602132000
    +B1404284535608N00604874EA0225002143000
    +B1404314535595N00604873EA0226102154000
    +B1404344535592N00604857EA0227402165000
    +B1404374535603N00604842EA0228702177000
    +B1404404535618N00604847EA0228902188000
    +B1404434535626N00604872EA0229602198000
    +B1404464535619N00604893EA0230802208000
    +B1404494535607N00604901EA0231802219000
    +B1404524535595N00604894EA0233002229000
    +B1404554535595N00604875EA0234302240000
    +B1404584535605N00604861EA0235002251000
    +B1405014535619N00604860EA0235202261000
    +B1405044535633N00604878EA0235902271000
    +B1405074535632N00604902EA0236502280000
    +B1405104535620N00604914EA0237202289000
    +B1405134535604N00604914EA0238202298000
    +B1405164535590N00604908EA0239202307000
    +B1405194535578N00604897EA0240302316000
    +B1405224535573N00604878EA0241302325000
    +B1405264535583N00604855EA0241902337000
    +B1405294535599N00604852EA0243202346000
    +B1405324535611N00604866EA0243702354000
    +B1405354535614N00604891EA0244402363000
    +B1405384535606N00604905EA0244902371000
    +B1405414535591N00604908EA0245102378000
    +B1405444535578N00604898EA0245702386000
    +B1405474535577N00604876EA0246402393000
    +B1405504535587N00604860EA0247302400000
    +B1405534535602N00604857EA0248102408000
    +B1405564535618N00604858EA0248802415000
    +B1405594535633N00604860EA0249102423000
    +B1406024535647N00604861EA0249102430000
    +B1406054535662N00604864EA0249302436000
    +B1406084535678N00604871EA0249702442000
    +B1406114535692N00604879EA0249802447000
    +B1406144535707N00604890EA0250002452000
    +B1406174535724N00604901EA0250302457000
    +B1406204535742N00604909EA0250802461000
    +B1406234535757N00604917EA0250902466000
    +B1406264535777N00604922EA0251502470000
    +B1406294535793N00604928EA0252102474000
    +B1406324535810N00604937EA0252402479000
    +B1406354535829N00604948EA0253502483000
    +B1406384535848N00604958EA0254202488000
    +B1406414535866N00604970EA0255102494000
    +B1406444535882N00604979EA0255502499000
    +B1406474535895N00604985EA0254902504000
    +B1406504535912N00604995EA0254902509000
    +B1406544535933N00605004EA0254602513000
    +B1406574535951N00605013EA0254402516000
    +B1407004535968N00605022EA0253202517000
    +B1407034535992N00605036EA0252302517000
    +B1407064536012N00605050EA0252302517000
    +B1407094536031N00605061EA0251302516000
    +B1407124536051N00605073EA0250502515000
    +B1407154536070N00605086EA0249702512000
    +B1407184536088N00605099EA0249102509000
    +B1407214536106N00605114EA0248702506000
    +B1407244536121N00605132EA0248302502000
    +B1407274536138N00605148EA0247502499000
    +B1407304536154N00605167EA0247302495000
    +B1407334536168N00605185EA0246602491000
    +B1407364536185N00605202EA0245402486000
    +B1407394536204N00605215EA0244802481000
    +B1407424536222N00605220EA0244502476000
    +B1407454536239N00605225EA0244702472000
    +B1407484536253N00605231EA0244902468000
    +B1407514536265N00605240EA0245402465000
    +B1407544536276N00605249EA0245602462000
    +B1407574536288N00605259EA0245802460000
    +B1408004536301N00605271EA0246102459000
    +B1408034536314N00605284EA0246202458000
    +B1408064536327N00605298EA0246502457000
    +B1408094536342N00605306EA0246602456000
    +B1408124536356N00605308EA0246202456000
    +B1408154536368N00605312EA0245202456000
    +B1408184536384N00605316EA0244002454000
    +B1408224536411N00605320EA0243702450000
    +B1408254536431N00605323EA0243302447000
    +B1408284536451N00605329EA0243102444000
    +B1408314536472N00605337EA0243002442000
    +B1408344536491N00605348EA0242702439000
    +B1408374536512N00605358EA0242302436000
    +B1408404536537N00605368EA0241902433000
    +B1408434536561N00605375EA0242002430000
    +B1408464536584N00605380EA0241502428000
    +B1408494536607N00605383EA0241202425000
    +B1408524536630N00605386EA0240902422000
    +B1408554536652N00605388EA0240202419000
    +B1408584536675N00605392EA0239902416000
    +B1409014536697N00605397EA0239202412000
    +B1409044536721N00605401EA0238402408000
    +B1409074536746N00605407EA0238102404000
    +B1409104536770N00605415EA0237602400000
    +B1409134536793N00605424EA0237002396000
    +B1409164536817N00605433EA0236702392000
    +B1409194536840N00605445EA0236402387000
    +B1409224536862N00605457EA0235802383000
    +B1409254536884N00605470EA0235002378000
    +B1409284536907N00605485EA0234902374000
    +B1409314536930N00605497EA0234202369000
    +B1409344536955N00605509EA0233902365000
    +B1409384536987N00605526EA0233702359000
    +B1409414537009N00605541EA0233102355000
    +B1409444537032N00605555EA0232802350000
    +B1409474537055N00605569EA0232302346000
    +B1409504537078N00605582EA0232002342000
    +B1409534537100N00605595EA0231702338000
    +B1409564537121N00605608EA0231002334000
    +B1409594537145N00605621EA0230602329000
    +B1410024537163N00605632EA0230302325000
    +B1410054537179N00605642EA0229702320000
    +B1410084537199N00605655EA0229302316000
    +B1410114537221N00605671EA0229102312000
    +B1410144537242N00605691EA0229202308000
    +B1410174537262N00605713EA0229502304000
    +B1410204537280N00605733EA0229702301000
    +B1410234537299N00605752EA0230002299000
    +B1410264537318N00605768EA0229902298000
    +B1410294537337N00605785EA0229702296000
    +B1410324537356N00605801EA0229502294000
    +B1410354537374N00605820EA0229502293000
    +B1410384537392N00605842EA0229802291000
    +B1410414537409N00605865EA0229802290000
    +B1410444537426N00605888EA0229902289000
    +B1410474537442N00605911EA0230902289000
    +B1410504537451N00605928EA0230802290000
    +B1410534537463N00605944EA0231102290000
    +B1410564537476N00605959EA0231702291000
    +B1411004537489N00605980EA0231602292000
    +B1411034537502N00605997EA0231702294000
    +B1411064537515N00606014EA0231702295000
    +B1411094537526N00606033EA0231802296000
    +B1411124537532N00606051EA0231702297000
    +B1411154537542N00606071EA0231402298000
    +B1411184537553N00606087EA0231702298000
    +B1411214537565N00606102EA0232402299000
    +B1411244537574N00606116EA0232702301000
    +B1411274537586N00606134EA0232702302000
    +B1411304537599N00606148EA0233002304000
    +B1411334537614N00606161EA0233102305000
    +B1411364537630N00606174EA0233102307000
    +B1411394537646N00606187EA0233002308000
    +B1411424537663N00606200EA0233202309000
    +B1411454537678N00606213EA0233002310000
    +B1411484537693N00606221EA0233502311000
    +B1411514537707N00606224EA0233402312000
    +B1411544537723N00606223EA0233502313000
    +B1411574537736N00606224EA0233202314000
    +B1412004537749N00606226EA0232902314000
    +B1412034537762N00606232EA0233002315000
    +B1412064537775N00606238EA0233502315000
    +B1412094537788N00606242EA0233802316000
    +B1412124537799N00606245EA0233802318000
    +B1412154537811N00606249EA0233402319000
    +B1412184537826N00606253EA0233602320000
    +B1412214537842N00606258EA0233702320000
    +B1412254537861N00606263EA0233802322000
    +B1412284537879N00606263EA0233602322000
    +B1412314537897N00606264EA0233902323000
    +B1412344537916N00606266EA0233902324000
    +B1412374537937N00606269EA0234602325000
    +B1412404537954N00606271EA0235602327000
    +B1412434537969N00606269EA0236202329000
    +B1412464537983N00606255EA0236902332000
    +B1412494537986N00606230EA0237202335000
    +B1412524537973N00606215EA0237402338000
    +B1412554537958N00606226EA0238202341000
    +B1412584537959N00606247EA0239102344000
    +B1413014537970N00606261EA0240102348000
    +B1413044537984N00606256EA0240502352000
    +B1413074537996N00606237EA0241402357000
    +B1413104538001N00606213EA0241502362000
    +B1413134537991N00606192EA0241802367000
    +B1413164537975N00606194EA0241902372000
    +B1413194537967N00606212EA0242602376000
    +B1413224537972N00606233EA0244002381000
    +B1413254537979N00606245EA0244702387000
    +B1413284537990N00606252EA0245002392000
    +B1413314538003N00606247EA0245602398000
    +B1413344538013N00606231EA0246202404000
    +B1413374538012N00606210EA0246602409000
    +B1413404538000N00606197EA0247502414000
    +B1413434537987N00606201EA0248402420000
    +B1413464537982N00606214EA0249102426000
    +B1413494537985N00606227EA0249602432000
    +B1413524537993N00606239EA0250102438000
    +B1413564538009N00606238EA0250602446000
    +B1413594538018N00606220EA0251402452000
    +B1414024538013N00606201EA0251702458000
    +B1414054537998N00606195EA0252802463000
    +B1414084537990N00606206EA0253502469000
    +B1414114537986N00606223EA0253902474000
    +B1414144537991N00606238EA0254402480000
    +B1414174538004N00606247EA0255102486000
    +B1414204538018N00606248EA0255802492000
    +B1414234538032N00606242EA0256402498000
    +B1414264538042N00606225EA0256802504000
    +B1414294538042N00606204EA0257002510000
    +B1414324538029N00606192EA0256902515000
    +B1414354538015N00606201EA0257002520000
    +B1414384538015N00606222EA0257402524000
    +B1414414538026N00606234EA0258302529000
    +B1414444538041N00606239EA0258802534000
    +B1414474538056N00606242EA0259102539000
    +B1414504538071N00606242EA0259202544000
    +B1414534538088N00606243EA0259202549000
    +B1414564538105N00606243EA0259102553000
    +B1414594538123N00606243EA0259102556000
    +B1415024538141N00606240EA0258702559000
    +B1415054538160N00606238EA0258502561000
    +B1415084538177N00606234EA0258402563000
    +B1415114538195N00606232EA0257702564000
    +B1415144538214N00606234EA0257902565000
    +B1415174538231N00606239EA0257602565000
    +B1415214538255N00606247EA0257502565000
    +B1415244538273N00606255EA0257102565000
    +B1415274538292N00606261EA0257002564000
    +B1415304538311N00606265EA0256702564000
    +B1415334538331N00606271EA0256202563000
    +B1415364538350N00606282EA0256502561000
    +B1415394538366N00606290EA0256402561000
    +B1415424538383N00606300EA0255802559000
    +B1415454538399N00606309EA0255602558000
    +B1415484538416N00606312EA0254402556000
    +B1415514538437N00606313EA0253602553000
    +B1415544538460N00606312EA0253202550000
    +B1415574538483N00606313EA0252802547000
    +B1416004538505N00606316EA0252202544000
    +B1416034538525N00606324EA0251402540000
    +B1416064538548N00606333EA0251002536000
    +B1416094538572N00606340EA0250602533000
    +B1416124538595N00606345EA0249902529000
    +B1416154538617N00606353EA0249402524000
    +B1416184538640N00606360EA0248602520000
    +B1416214538665N00606372EA0248402515000
    +B1416244538688N00606383EA0248002511000
    +B1416274538711N00606394EA0247502506000
    +B1416304538736N00606403EA0246802501000
    +B1416334538761N00606413EA0246502497000
    +B1416364538786N00606423EA0246302492000
    +B1416394538810N00606436EA0246202488000
    +B1416434538840N00606452EA0246002482000
    +B1416464538862N00606464EA0245902479000
    +B1416494538885N00606478EA0245602475000
    +B1416524538907N00606491EA0245402471000
    +B1416554538930N00606504EA0245002468000
    +B1416584538953N00606517EA0244702465000
    +B1417014538975N00606528EA0244602461000
    +B1417044538996N00606539EA0243602458000
    +B1417074539020N00606553EA0243102454000
    +B1417104539043N00606567EA0242902450000
    +B1417134539065N00606580EA0242002446000
    +B1417164539089N00606595EA0241702442000
    +B1417194539113N00606608EA0241302437000
    +B1417224539137N00606620EA0240902433000
    +B1417254539160N00606632EA0240202429000
    +B1417284539184N00606648EA0240102424000
    +B1417314539208N00606663EA0239802420000
    +B1417344539232N00606679EA0239402416000
    +B1417374539255N00606695EA0239202411000
    +B1417404539278N00606712EA0238902408000
    +B1417434539301N00606730EA0238802404000
    +B1417464539324N00606746EA0238702401000
    +B1417494539348N00606761EA0238502397000
    +B1417524539371N00606775EA0238302394000
    +B1417554539394N00606788EA0237602391000
    +B1417584539417N00606801EA0236802388000
    +B1418014539440N00606815EA0236402384000
    +B1418054539471N00606833EA0235802379000
    +B1418084539494N00606849EA0235102375000
    +B1418114539515N00606865EA0234502370000
    +B1418144539537N00606881EA0233702366000
    +B1418174539560N00606897EA0233102361000
    +B1418204539582N00606913EA0232502356000
    +B1418234539605N00606930EA0231802351000
    +B1418264539628N00606945EA0231102346000
    +B1418294539652N00606960EA0230502340000
    +B1418324539675N00606976EA0229802335000
    +B1418354539698N00606991EA0229402329000
    +B1418384539721N00607006EA0228802324000
    +B1418414539743N00607021EA0228402318000
    +B1418444539765N00607036EA0227802313000
    +B1418474539788N00607052EA0227202308000
    +B1418504539810N00607068EA0226702302000
    +B1418534539831N00607083EA0226002297000
    +B1418564539854N00607099EA0225402291000
    +B1418594539877N00607115EA0224802285000
    +B1419024539899N00607131EA0224202280000
    +B1419054539921N00607148EA0223702274000
    +B1419084539943N00607165EA0223302268000
    +B1419114539964N00607181EA0222902263000
    +B1419144539986N00607197EA0222502258000
    +B1419174540008N00607212EA0222102252000
    +B1419204540029N00607228EA0221802247000
    +B1419244540060N00607250EA0221902241000
    +B1419274540081N00607265EA0221602236000
    +B1419304540102N00607280EA0221202232000
    +B1419334540125N00607293EA0221002228000
    +B1419364540147N00607306EA0220502224000
    +B1419394540168N00607320EA0219902220000
    +B1419424540190N00607336EA0219502216000
    +B1419454540210N00607353EA0219002211000
    +B1419484540231N00607371EA0218802207000
    +B1419514540252N00607390EA0218402203000
    +B1419544540274N00607407EA0218002199000
    +B1419574540296N00607426EA0217902195000
    +B1420004540317N00607444EA0217802192000
    +B1420034540337N00607462EA0217502188000
    +B1420064540357N00607481EA0217302185000
    +B1420094540377N00607497EA0217002182000
    +B1420124540396N00607515EA0216702179000
    +B1420154540414N00607533EA0216502175000
    +B1420184540430N00607552EA0216002172000
    +B1420214540447N00607573EA0215602169000
    +B1420244540464N00607593EA0215302165000
    +B1420274540480N00607614EA0214702162000
    +B1420304540498N00607633EA0213902158000
    +B1420334540515N00607655EA0213302154000
    +B1420364540532N00607676EA0212602149000
    +B1420394540549N00607702EA0212202144000
    +B1420434540571N00607733EA0212002138000
    +B1420464540587N00607757EA0211602134000
    +B1420494540603N00607778EA0211102130000
    +B1420524540618N00607801EA0210702125000
    +B1420554540633N00607822EA0210202121000
    +B1420584540647N00607844EA0209502116000
    +B1421014540661N00607865EA0208602111000
    +B1421044540676N00607888EA0207802106000
    +B1421074540692N00607911EA0207102100000
    +B1421104540707N00607934EA0206102094000
    +B1421134540723N00607959EA0205402088000
    +B1421164540740N00607983EA0204802081000
    +B1421194540756N00608005EA0203902075000
    +B1421224540774N00608028EA0203302068000
    +B1421254540790N00608051EA0202802062000
    +B1421284540805N00608071EA0201802055000
    +B1421314540822N00608093EA0201202048000
    +B1421344540836N00608115EA0200502042000
    +B1421374540851N00608137EA0199802035000
    +B1421404540866N00608158EA0199502028000
    +B1421434540881N00608177EA0198602022000
    +B1421464540897N00608198EA0198102015000
    +B1421494540910N00608220EA0197402009000
    +B1421524540922N00608242EA0196302002000
    +B1421554540936N00608268EA0195901996000
    +B1421584540950N00608291EA0195201987000
    +B1422024540970N00608321EA0194401980000
    +B1422054540985N00608342EA0193701974000
    +B1422084541001N00608365EA0193001967000
    +B1422114541017N00608385EA0192801960000
    +B1422144541031N00608408EA0192801955000
    +B1422174541045N00608428EA0192701949000
    +B1422204541061N00608448EA0192301944000
    +B1422234541077N00608467EA0192101940000
    +B1422264541092N00608485EA0191601935000
    +B1422294541107N00608505EA0191101931000
    +B1422324541122N00608526EA0190701927000
    +B1422354541136N00608547EA0190101922000
    +B1422384541149N00608567EA0189301918000
    +B1422414541159N00608588EA0188801913000
    +B1422444541168N00608608EA0188501908000
    +B1422474541176N00608628EA0187701904000
    +B1422504541188N00608654EA0187401899000
    +B1422534541198N00608681EA0187601894000
    +B1422564541208N00608704EA0187301890000
    +B1422594541218N00608728EA0187201886000
    +B1423024541229N00608752EA0186801883000
    +B1423054541241N00608777EA0186601879000
    +B1423084541254N00608803EA0186201876000
    +B1423114541268N00608826EA0185801872000
    +B1423144541281N00608851EA0185301868000
    +B1423184541297N00608884EA0184601863000
    +B1423214541309N00608909EA0184101859000
    +B1423244541323N00608934EA0183501854000
    +B1423274541336N00608960EA0182901850000
    +B1423304541350N00608987EA0182201845000
    +B1423334541364N00609015EA0181501840000
    +B1423364541377N00609044EA0180601834000
    +B1423394541391N00609071EA0180101829000
    +B1423424541402N00609094EA0179201823000
    +B1423454541414N00609117EA0178001817000
    +B1423484541426N00609140EA0177501810000
    +B1423514541434N00609160EA0177501803000
    +B1423544541438N00609181EA0177301798000
    +B1423574541446N00609198EA0177201792000
    +B1424004541452N00609216EA0177301788000
    +B1424034541458N00609235EA0176601784000
    +B1424064541466N00609258EA0176301779000
    +B1424094541474N00609284EA0176001775000
    +B1424124541484N00609312EA0176301771000
    +B1424154541497N00609335EA0177101768000
    +B1424184541505N00609354EA0177901766000
    +B1424214541512N00609374EA0179001766000
    +B1424244541511N00609399EA0180301767000
    +B1424274541502N00609424EA0181301769000
    +B1424304541489N00609445EA0182201772000
    +B1424334541469N00609455EA0183201776000
    +B1424364541450N00609450EA0184401781000
    +B1424394541441N00609436EA0185601786000
    +B1424424541442N00609425EA0186801790000
    +B1424464541450N00609434EA0188001797000
    +B1424494541449N00609457EA0189401806000
    +B1424524541437N00609477EA0190101815000
    +B1424554541416N00609491EA0191301824000
    +B1424584541396N00609495EA0192401834000
    +B1425014541380N00609482EA0193801843000
    +B1425044541373N00609467EA0194801853000
    +B1425074541376N00609452EA0195901861000
    +B1425104541382N00609450EA0196901867000
    +B1425134541387N00609458EA0197901875000
    +B1425164541387N00609479EA0199301887000
    +B1425194541377N00609499EA0200301899000
    +B1425224541358N00609511EA0201601910000
    +B1425254541342N00609506EA0203201921000
    +B1425284541333N00609493EA0204101933000
    +B1425314541334N00609478EA0204901942000
    +B1425344541340N00609473EA0206101948000
    +B1425374541348N00609475EA0207401955000
    +B1425404541351N00609488EA0208201967000
    +B1425434541350N00609511EA0209401981000
    +B1425464541335N00609532EA0210701994000
    +B1425494541315N00609543EA0212102007000
    +B1425524541295N00609537EA0213302020000
    +B1425554541288N00609520EA0214502032000
    +B1425584541291N00609507EA0216102043000
    +B1426014541295N00609506EA0216802050000
    +B1426044541300N00609519EA0218302062000
    +B1426084541293N00609547EA0219402081000
    +B1426114541276N00609566EA0220402094000
    +B1426144541254N00609574EA0221802107000
    +B1426174541234N00609566EA0223102120000
    +B1426204541222N00609548EA0224602133000
    +B1426234541218N00609532EA0225502146000
    +B1426264541223N00609525EA0226402152000
    +B1426294541227N00609530EA0227602159000
    +B1426324541234N00609534EA0228602166000
    +B1426354541240N00609536EA0230002174000
    +B1426384541245N00609533EA0231202183000
    +B1426414541250N00609529EA0232702192000
    +B1426444541253N00609524EA0233602202000
    +B1426474541250N00609508EA0234302218000
    +B1426504541233N00609490EA0235202234000
    +B1426534541211N00609482EA0236402249000
    +B1426564541191N00609492EA0237702263000
    +B1426594541180N00609511EA0238802275000
    +B1427024541184N00609523EA0239902286000
    +B1427054541188N00609524EA0241202292000
    +B1427084541193N00609522EA0242002299000
    +B1427114541198N00609523EA0243402307000
    +B1427144541202N00609523EA0243602316000
    +B1427174541209N00609521EA0244002324000
    +B1427204541216N00609517EA0244502332000
    +B1427234541222N00609513EA0244702340000
    +B1427264541229N00609511EA0245102348000
    +B1427294541235N00609514EA0244702355000
    +B1427324541242N00609516EA0245102362000
    +B1427354541250N00609516EA0244302371000
    +B1427384541265N00609513EA0245302382000
    +B1427424541281N00609516EA0246202394000
    +B1427454541292N00609518EA0246502402000
    +B1427484541307N00609523EA0246602410000
    +B1427514541323N00609528EA0247302416000
    +B1427544541335N00609534EA0248202423000
    +B1427574541346N00609536EA0248102429000
    +B1428004541360N00609537EA0248602434000
    +B1428034541372N00609537EA0248902439000
    +B1428064541385N00609539EA0249002444000
    +B1428094541398N00609541EA0249602448000
    +B1428124541409N00609547EA0249902453000
    +B1428154541422N00609550EA0250602457000
    +B1428184541433N00609547EA0251202462000
    +B1428214541445N00609548EA0252202467000
    +B1428244541455N00609547EA0253202473000
    +B1428274541465N00609551EA0253802479000
    +B1428304541478N00609556EA0254202484000
    +B1428334541492N00609561EA0255502490000
    +B1428364541502N00609566EA0256502497000
    +B1428394541514N00609571EA0257802503000
    +B1428424541526N00609572EA0258302510000
    +B1428454541542N00609571EA0259802518000
    +B1428484541555N00609569EA0260902526000
    +B1428514541570N00609567EA0261502534000
    +B1428544541586N00609572EA0262602542000
    +B1428574541598N00609576EA0262502550000
    +B1429004541614N00609580EA0263002557000
    +B1429034541627N00609585EA0264302564000
    +B1429064541639N00609588EA0264802572000
    +B1429094541653N00609590EA0265402579000
    +B1429124541667N00609589EA0265202586000
    +B1429154541683N00609583EA0264402592000
    +B1429184541704N00609578EA0264102596000
    +B1429214541726N00609574EA0263602600000
    +B1429254541757N00609565EA0263102604000
    +B1429284541780N00609557EA0262902606000
    +B1429314541803N00609552EA0262202607000
    +B1429344541827N00609547EA0261602607000
    +B1429374541850N00609541EA0261402607000
    +B1429404541873N00609537EA0260902606000
    +B1429434541894N00609536EA0260302605000
    +B1429464541916N00609535EA0259202603000
    +B1429494541940N00609534EA0258502600000
    +B1429524541960N00609534EA0258002597000
    +B1429554541979N00609535EA0256802594000
    +B1429584541999N00609540EA0255802589000
    +B1430014542020N00609540EA0255002584000
    +B1430044542040N00609539EA0254602579000
    +B1430074542058N00609536EA0254502574000
    +B1430104542075N00609534EA0253902569000
    +B1430134542094N00609534EA0253502564000
    +B1430164542114N00609533EA0253002559000
    +B1430194542134N00609532EA0252602555000
    +B1430224542153N00609534EA0252302550000
    +B1430254542173N00609536EA0252202546000
    +B1430284542192N00609536EA0252002541000
    +B1430314542212N00609538EA0251702537000
    +B1430344542233N00609540EA0251202534000
    +B1430374542255N00609543EA0250702530000
    +B1430414542286N00609546EA0250302524000
    +B1430444542309N00609549EA0249202520000
    +B1430474542333N00609550EA0248502514000
    +B1430504542355N00609552EA0247402509000
    +B1430534542381N00609554EA0245702503000
    +B1430564542406N00609554EA0244602497000
    +B1430594542427N00609552EA0243102489000
    +B1431024542450N00609547EA0241202480000
    +B1431054542476N00609538EA0240902471000
    +B1431084542494N00609527EA0240002462000
    +B1431114542515N00609513EA0238402453000
    +B1431144542537N00609495EA0237702443000
    +B1431174542557N00609479EA0236802434000
    +B1431204542578N00609463EA0235902425000
    +B1431234542597N00609450EA0235902416000
    +B1431264542613N00609438EA0235102408000
    +B1431294542632N00609424EA0234602400000
    +B1431324542650N00609410EA0234202392000
    +B1431354542668N00609396EA0233502384000
    +B1431384542687N00609384EA0232902377000
    +B1431414542701N00609377EA0232802370000
    +B1431444542712N00609369EA0231602364000
    +B1431474542726N00609366EA0231402357000
    +B1431504542738N00609363EA0230602350000
    +B1431534542752N00609361EA0230002344000
    +B1431564542765N00609361EA0229502337000
    +B1431594542776N00609360EA0229002331000
    +B1432034542795N00609353EA0227402322000
    +B1432064542816N00609344EA0226602315000
    +B1432094542834N00609337EA0226002307000
    +B1432124542851N00609329EA0224702300000
    +B1432154542871N00609320EA0223902292000
    +B1432184542888N00609309EA0223002284000
    +B1432214542905N00609295EA0221602275000
    +B1432244542922N00609280EA0220802267000
    +B1432274542939N00609267EA0219902258000
    +B1432304542956N00609255EA0218902249000
    +B1432334542974N00609242EA0218002240000
    +B1432364542990N00609230EA0217702232000
    +B1432394543005N00609220EA0216802223000
    +B1432424543022N00609212EA0216102215000
    +B1432454543037N00609203EA0215502207000
    +B1432484543052N00609193EA0214402199000
    +B1432514543068N00609184EA0213802191000
    +B1432544543083N00609177EA0213002183000
    +B1432574543099N00609171EA0212402175000
    +B1433004543117N00609166EA0212002168000
    +B1433034543135N00609161EA0211302160000
    +B1433064543154N00609157EA0210702153000
    +B1433094543171N00609152EA0210002146000
    +B1433124543189N00609148EA0209302139000
    +B1433154543208N00609142EA0208802132000
    +B1433184543226N00609139EA0208202125000
    +B1433214543245N00609136EA0207602119000
    +B1433254543271N00609133EA0206902110000
    +B1433284543289N00609130EA0206302103000
    +B1433314543307N00609126EA0205602096000
    +B1433344543325N00609123EA0204902089000
    +B1433374543343N00609122EA0204302083000
    +B1433404543362N00609122EA0203502076000
    +B1433434543382N00609123EA0202902069000
    +B1433464543402N00609126EA0202702063000
    +B1433494543423N00609130EA0202702057000
    +B1433524543443N00609136EA0203102052000
    +B1433554543462N00609142EA0203302048000
    +B1433584543479N00609145EA0203702045000
    +B1434014543493N00609149EA0203602042000
    +B1434044543512N00609150EA0203002039000
    +B1434074543533N00609152EA0203502037000
    +B1434104543550N00609152EA0203602035000
    +B1434134543564N00609153EA0203302034000
    +B1434164543579N00609155EA0202802032000
    +B1434194543592N00609156EA0202302030000
    +B1434224543611N00609153EA0201502027000
    +B1434254543633N00609151EA0201902024000
    +B1434284543649N00609153EA0201802022000
    +B1434314543667N00609156EA0201502020000
    +B1434344543688N00609157EA0201802018000
    +B1434374543709N00609159EA0202002016000
    +B1434404543731N00609164EA0202102015000
    +B1434434543753N00609167EA0202202014000
    +B1434474543782N00609169EA0201402013000
    +B1434504543806N00609167EA0201302011000
    +B1434534543826N00609165EA0201102009000
    +B1434564543847N00609164EA0200202007000
    +B1434594543868N00609166EA0200302005000
    +B1435024543888N00609170EA0199702003000
    +B1435054543912N00609174EA0199002000000
    +B1435084543935N00609178EA0199201998000
    +B1435114543955N00609181EA0198701995000
    +B1435144543977N00609183EA0198501993000
    +B1435174543995N00609185EA0197901990000
    +B1435204544015N00609186EA0197401987000
    +B1435234544035N00609187EA0197301984000
    +B1435264544053N00609189EA0196801981000
    +B1435294544074N00609194EA0196401978000
    +B1435324544094N00609199EA0196501975000
    +B1435354544113N00609204EA0195901972000
    +B1435384544134N00609206EA0195601969000
    +B1435414544154N00609209EA0195401965000
    +B1435444544172N00609211EA0194901962000
    +B1435474544191N00609215EA0194601959000
    +B1435504544210N00609217EA0194201956000
    +B1435534544229N00609221EA0193701952000
    +B1435564544247N00609225EA0193401949000
    +B1435594544265N00609229EA0192901945000
    +B1436024544285N00609234EA0192501941000
    +B1436054544303N00609239EA0192001937000
    +B1436094544328N00609244EA0191801932000
    +B1436124544345N00609248EA0191301928000
    +B1436154544363N00609252EA0190801924000
    +B1436184544382N00609256EA0190201920000
    +B1436214544401N00609259EA0189701915000
    +B1436244544419N00609260EA0189101911000
    +B1436274544438N00609260EA0188601907000
    +B1436304544457N00609262EA0188101902000
    +B1436334544475N00609264EA0187501897000
    +B1436364544494N00609266EA0186901893000
    +B1436394544514N00609268EA0186501888000
    +B1436424544532N00609272EA0186001883000
    +B1436454544551N00609274EA0185401878000
    +B1436484544570N00609276EA0185001873000
    +B1436514544590N00609278EA0184601869000
    +B1436544544609N00609282EA0184101864000
    +B1436574544628N00609285EA0183401860000
    +B1437004544647N00609289EA0182801856000
    +B1437034544665N00609293EA0182101851000
    +B1437064544684N00609298EA0181601847000
    +B1437094544702N00609303EA0181201842000
    +B1437124544720N00609309EA0180501837000
    +B1437154544740N00609315EA0180101832000
    +B1437184544760N00609322EA0179301827000
    +B1437214544781N00609327EA0178401821000
    +B1437244544802N00609333EA0177601815000
    +B1437284544831N00609340EA0176601807000
    +B1437314544853N00609344EA0175601800000
    +B1437344544873N00609349EA0174901794000
    +B1437374544891N00609356EA0174201787000
    +B1437404544908N00609363EA0173801781000
    +B1437434544923N00609370EA0173001775000
    +B1437464544940N00609380EA0172701768000
    +B1437494544956N00609392EA0172901763000
    +B1437524544969N00609403EA0174001758000
    +B1437554544976N00609414EA0174501754000
    +B1437584544984N00609423EA0174701751000
    +B1438014544994N00609434EA0175901750000
    +B1438044544999N00609443EA0176301749000
    +B1438074545006N00609450EA0176601750000
    +B1438104545012N00609447EA0177101750000
    +B1438134545013N00609436EA0177101750000
    +B1438164545004N00609419EA0177501752000
    +B1438194544986N00609410EA0178101753000
    +B1438224544963N00609416EA0178901755000
    +B1438254544947N00609437EA0179301758000
    +B1438284544943N00609462EA0180101761000
    +B1438314544948N00609474EA0180501763000
    +B1438344544953N00609473EA0180501765000
    +B1438374544952N00609458EA0180701768000
    +B1438404544936N00609442EA0181301771000
    +B1438434544914N00609443EA0182001774000
    +B1438464544897N00609461EA0182501778000
    +B1438494544893N00609485EA0182901781000
    +B1438524544898N00609499EA0183201783000
    +B1438564544907N00609505EA0183201785000
    +B1438594544916N00609508EA0183301788000
    +B1439024544929N00609512EA0182901791000
    +B1439054544946N00609514EA0182901794000
    +B1439084544962N00609520EA0183001796000
    +B1439114544980N00609530EA0182801798000
    +B1439144544998N00609541EA0182901799000
    +B1439174545013N00609552EA0183501801000
    +B1439204545025N00609563EA0183101803000
    +B1439234545043N00609574EA0182801804000
    +B1439264545060N00609587EA0183501806000
    +B1439294545074N00609599EA0184401809000
    +B1439324545085N00609607EA0185701812000
    +B1439354545094N00609612EA0186701815000
    +B1439384545100N00609603EA0187301818000
    +B1439414545094N00609586EA0187701823000
    +B1439444545075N00609577EA0188401828000
    +B1439474545055N00609588EA0189301833000
    +B1439504545046N00609611EA0190501839000
    +B1439534545046N00609631EA0191601845000
    +B1439564545053N00609645EA0192601851000
    +B1439594545061N00609647EA0193301856000
    +B1440024545068N00609637EA0194701860000
    +B1440054545062N00609621EA0195201869000
    +B1440084545045N00609612EA0195701877000
    +B1440114545027N00609624EA0196401885000
    +B1440144545019N00609647EA0197301893000
    +B1440174545020N00609667EA0198301900000
    +B1440204545028N00609679EA0199301908000
    +B1440234545037N00609681EA0200501913000
    +B1440264545044N00609677EA0201101918000
    +B1440294545049N00609662EA0202101928000
    +B1440334545041N00609634EA0203201941000
    +B1440364545025N00609625EA0204001951000
    +B1440394545008N00609636EA0204901961000
    +B1440424545001N00609657EA0205801970000
    +B1440454545006N00609673EA0207001979000
    +B1440484545013N00609674EA0208101984000
    +B1440514545021N00609665EA0208901994000
    +B1440544545024N00609647EA0209902004000
    +B1440574545017N00609627EA0210902015000
    +B1441004545001N00609623EA0211502025000
    +B1441034544992N00609640EA0212402034000
    +B1441064544994N00609660EA0213902044000
    +B1441094545001N00609671EA0214502054000
    +B1441124545013N00609674EA0215102063000
    +B1441154545023N00609663EA0215102072000
    +B1441184545023N00609642EA0214902080000
    +B1441214545015N00609624EA0214802087000
    +B1441244544999N00609616EA0214502092000
    +B1441274544979N00609615EA0215002098000
    +B1441304544959N00609617EA0215702103000
    +B1441334544939N00609623EA0217002108000
    +B1441364544930N00609639EA0218102114000
    +B1441394544930N00609658EA0219202120000
    +B1441424544942N00609659EA0219802127000
    +B1441454544945N00609638EA0220702134000
    +B1441484544932N00609626EA0221502141000
    +B1441514544917N00609632EA0222202148000
    +B1441544544914N00609648EA0223202155000
    +B1441574544923N00609655EA0224102162000
    +B1442004544932N00609643EA0224602169000
    +B1442044544929N00609616EA0224502179000
    +B1442074544913N00609603EA0225002185000
    +B1442104544897N00609607EA0225502191000
    +B1442134544883N00609624EA0225802197000
    +B1442164544870N00609643EA0226902203000
    +B1442194544857N00609661EA0228102209000
    +B1442224544854N00609681EA0229202216000
    +B1442254544862N00609691EA0229902223000
    +B1442284544872N00609682EA0230602230000
    +B1442314544871N00609660EA0231402237000
    +B1442344544863N00609645EA0232102244000
    +B1442374544847N00609640EA0232502251000
    +B1442404544834N00609654EA0233602258000
    +B1442434544833N00609671EA0234402265000
    +B1442464544838N00609685EA0235102273000
    +B1442494544848N00609689EA0235202280000
    +B1442524544858N00609677EA0234902287000
    +B1442554544856N00609651EA0235102292000
    +B1442584544847N00609628EA0235502298000
    +B1443014544831N00609614EA0235902303000
    +B1443044544814N00609613EA0236502308000
    +B1443074544797N00609621EA0236802313000
    +B1443104544783N00609637EA0237702319000
    +B1443134544771N00609652EA0238302324000
    +B1443164544764N00609672EA0239102330000
    +B1443194544769N00609687EA0240002336000
    +B1443224544779N00609686EA0240502342000
    +B1443254544783N00609666EA0241102349000
    +B1443284544776N00609642EA0241502355000
    +B1443314544758N00609628EA0242102361000
    +B1443344544740N00609630EA0242502367000
    +B1443374544729N00609646EA0243102373000
    +B1443404544728N00609662EA0243802378000
    +B1443444544735N00609680EA0244602386000
    +B1443474544744N00609688EA0245402392000
    +B1443504544756N00609685EA0245802398000
    +B1443534544765N00609668EA0246002404000
    +B1443564544761N00609644EA0246402409000
    +B1443594544745N00609629EA0246702415000
    +B1444024544726N00609625EA0247202419000
    +B1444054544710N00609633EA0247802424000
    +B1444084544705N00609654EA0248602429000
    +B1444114544710N00609670EA0249602434000
    +B1444144544721N00609675EA0249902440000
    +B1444174544734N00609672EA0249802446000
    +B1444204544749N00609669EA0249002451000
    +B1444234544770N00609663EA0248102454000
    +B1444264544791N00609659EA0247902456000
    +B1444294544810N00609657EA0247202458000
    +B1444324544833N00609654EA0246202458000
    +B1444354544855N00609651EA0245602458000
    +B1444384544877N00609649EA0244502456000
    +B1444414544899N00609649EA0244502454000
    +B1444444544919N00609653EA0244202452000
    +B1444474544939N00609658EA0243102449000
    +B1444504544963N00609663EA0242902446000
    +B1444534544985N00609668EA0242702443000
    +B1444564545006N00609673EA0243502440000
    +B1444594545021N00609681EA0244602439000
    +B1445024545032N00609687EA0245002439000
    +B1445054545046N00609694EA0245402439000
    +B1445084545064N00609702EA0244902439000
    +B1445124545095N00609713EA0245402439000
    +B1445154545117N00609716EA0245202439000
    +B1445184545139N00609718EA0245402439000
    +B1445214545158N00609720EA0245602440000
    +B1445244545178N00609725EA0244602440000
    +B1445274545198N00609732EA0245002441000
    +B1445304545214N00609734EA0244802441000
    +B1445334545232N00609736EA0244102440000
    +B1445364545252N00609736EA0243902440000
    +B1445394545270N00609735EA0243202438000
    +B1445424545290N00609733EA0242802437000
    +B1445454545308N00609732EA0242402435000
    +B1445484545328N00609732EA0241902432000
    +B1445514545348N00609730EA0241402430000
    +B1445544545366N00609729EA0240702427000
    +B1445574545386N00609727EA0240002423000
    +B1446004545406N00609723EA0239502419000
    +B1446034545424N00609721EA0238802416000
    +B1446064545444N00609720EA0237902411000
    +B1446094545465N00609719EA0237402406000
    +B1446124545486N00609718EA0236502401000
    +B1446154545506N00609718EA0236102396000
    +B1446184545525N00609717EA0235302391000
    +B1446214545545N00609714EA0234402386000
    +B1446244545566N00609712EA0234102380000
    +B1446274545585N00609711EA0233902375000
    +B1446314545609N00609711EA0233102367000
    +B1446344545630N00609712EA0232702362000
    +B1446374545649N00609713EA0232502356000
    +B1446404545668N00609714EA0231902351000
    +B1446434545688N00609717EA0231702346000
    +B1446464545706N00609721EA0231702342000
    +B1446494545725N00609725EA0231102337000
    +B1446524545744N00609728EA0231102333000
    +B1446554545762N00609728EA0231002329000
    +B1446584545780N00609725EA0231302325000
    +B1447014545795N00609723EA0232602323000
    +B1447044545802N00609715EA0233502322000
    +B1447074545811N00609714EA0234102323000
    +B1447104545820N00609725EA0234802323000
    +B1447134545818N00609749EA0235602325000
    +B1447164545802N00609765EA0236302327000
    +B1447194545784N00609760EA0236802329000
    +B1447224545773N00609741EA0237302332000
    +B1447254545777N00609724EA0238002335000
    +B1447284545788N00609720EA0238902339000
    +B1447314545799N00609729EA0239802344000
    +B1447344545804N00609748EA0240702349000
    +B1447374545796N00609766EA0241702354000
    +B1447404545782N00609778EA0242102360000
    +B1447434545765N00609772EA0242602365000
    +B1447464545760N00609754EA0243502370000
    +B1447494545767N00609745EA0244402375000
    +B1447524545778N00609750EA0244902381000
    +B1447554545784N00609766EA0245902388000
    +B1447584545784N00609785EA0246402394000
    +B1448014545772N00609801EA0247002401000
    +B1448044545756N00609797EA0247502408000
    +B1448084545750N00609770EA0248302416000
    +B1448114545759N00609759EA0249002422000
    +B1448144545771N00609760EA0249602428000
    +B1448174545786N00609768EA0250102435000
    +B1448204545804N00609778EA0250002441000
    +B1448234545827N00609791EA0250602447000
    +B1448264545846N00609806EA0251402452000
    +B1448294545861N00609820EA0251002458000
    +B1448324545880N00609838EA0250302463000
    +B1448354545900N00609855EA0249802466000
    +B1448384545919N00609873EA0249902469000
    +B1448414545934N00609892EA0249202472000
    +B1448444545951N00609913EA0247702473000
    +B1448474545973N00609936EA0247702474000
    +B1448504545993N00609957EA0247502474000
    +B1448534546014N00609976EA0247802474000
    +B1448564546034N00609989EA0247902473000
    +B1448594546051N00609999EA0247102473000
    +B1449024546070N00610014EA0247402472000
    +B1449054546082N00610023EA0247602471000
    +B1449084546095N00610030EA0246602471000
    +B1449114546114N00610045EA0246402469000
    +B1449144546133N00610058EA0245602467000
    +B1449174546155N00610073EA0244702465000
    +B1449204546178N00610091EA0245202462000
    +B1449234546197N00610108EA0245602460000
    +B1449264546214N00610126EA0245902458000
    +B1449294546233N00610145EA0245702457000
    +B1449324546252N00610167EA0245702456000
    +B1449364546277N00610193EA0245202454000
    +B1449394546296N00610213EA0244502453000
    +B1449424546315N00610231EA0243302450000
    +B1449454546335N00610252EA0242202447000
    +B1449484546355N00610272EA0241602443000
    +B1449514546376N00610293EA0240602439000
    +B1449544546396N00610316EA0240202434000
    +B1449574546415N00610339EA0239502430000
    +B1450004546434N00610362EA0238902425000
    +B1450034546453N00610385EA0238902420000
    +B1450064546470N00610406EA0238402415000
    +B1450094546488N00610429EA0238002410000
    +B1450124546506N00610450EA0238002406000
    +B1450154546523N00610472EA0237702401000
    +B1450184546540N00610498EA0237602397000
    +B1450214546553N00610526EA0237502393000
    +B1450244546563N00610555EA0236802389000
    +B1450274546575N00610588EA0236702385000
    +B1450304546587N00610618EA0236602381000
    +B1450334546599N00610647EA0236202378000
    +B1450364546612N00610675EA0235802374000
    +B1450394546624N00610704EA0235302370000
    +B1450424546636N00610735EA0235002367000
    +B1450454546649N00610762EA0235102363000
    +B1450484546660N00610788EA0234602360000
    +B1450514546672N00610812EA0233902357000
    +B1450544546686N00610835EA0233502354000
    +B1450574546697N00610857EA0232802351000
    +B1451004546709N00610880EA0232102347000
    +B1451044546724N00610911EA0230902342000
    +B1451074546736N00610932EA0230202337000
    +B1451104546746N00610958EA0229602332000
    +B1451134546757N00610987EA0229102327000
    +B1451164546771N00611014EA0229702322000
    +B1451194546778N00611037EA0230302319000
    +B1451224546783N00611057EA0230102316000
    +B1451254546795N00611080EA0229702312000
    +B1451284546806N00611105EA0230502310000
    +B1451314546816N00611122EA0230202308000
    +B1451344546828N00611139EA0229802306000
    +B1451374546845N00611157EA0229502303000
    +B1451404546861N00611177EA0229402301000
    +B1451434546877N00611198EA0229102299000
    +B1451464546892N00611219EA0228702297000
    +B1451494546908N00611241EA0228402294000
    +B1451524546924N00611262EA0227902292000
    +B1451554546940N00611283EA0227402289000
    +B1451584546957N00611307EA0227002287000
    +B1452014546974N00611330EA0226702284000
    +B1452044546993N00611352EA0226402281000
    +B1452074547011N00611373EA0226302278000
    +B1452104547027N00611395EA0226202275000
    +B1452134547043N00611417EA0224902273000
    +B1452164547063N00611440EA0224402269000
    +B1452194547082N00611465EA0223902266000
    +B1452234547109N00611496EA0223102261000
    +B1452264547130N00611518EA0222702257000
    +B1452294547150N00611536EA0222102253000
    +B1452324547170N00611555EA0221402248000
    +B1452354547191N00611575EA0220602244000
    +B1452384547211N00611595EA0219702238000
    +B1452414547231N00611615EA0219302233000
    +B1452444547250N00611638EA0218602227000
    +B1452474547269N00611660EA0218102222000
    +B1452504547286N00611682EA0217102216000
    +B1452534547305N00611704EA0216402210000
    +B1452564547324N00611726EA0215702204000
    +B1452594547343N00611748EA0215302198000
    +B1453024547360N00611770EA0214902192000
    +B1453054547379N00611787EA0214602186000
    +B1453084547396N00611799EA0214302180000
    +B1453114547415N00611813EA0213702175000
    +B1453144547435N00611832EA0213702169000
    +B1453174547452N00611849EA0213302164000
    +B1453204547469N00611867EA0213002160000
    +B1453234547485N00611886EA0212702155000
    +B1453264547499N00611904EA0212102151000
    +B1453294547515N00611925EA0211702146000
    +B1453324547530N00611947EA0211302141000
    +B1453354547544N00611967EA0210902137000
    +B1453384547560N00611987EA0210402132000
    +B1453414547575N00612007EA0209902128000
    +B1453454547595N00612035EA0209602121000
    +B1453484547609N00612056EA0209002117000
    +B1453514547624N00612077EA0208402112000
    +B1453544547639N00612099EA0207902107000
    +B1453574547653N00612120EA0207402102000
    +B1454004547668N00612141EA0206702097000
    +B1454034547684N00612163EA0206302091000
    +B1454064547700N00612184EA0205902086000
    +B1454094547715N00612205EA0205402081000
    +B1454124547730N00612229EA0205102076000
    +B1454154547744N00612253EA0205002072000
    +B1454184547758N00612276EA0204602067000
    +B1454214547772N00612299EA0204202063000
    +B1454244547786N00612322EA0204002058000
    +B1454274547801N00612345EA0203702054000
    +B1454304547814N00612367EA0203402050000
    +B1454334547828N00612389EA0203102046000
    +B1454364547841N00612410EA0202702042000
    +B1454394547854N00612433EA0202302038000
    +B1454424547867N00612455EA0201802035000
    +B1454454547879N00612478EA0201202030000
    +B1454484547892N00612503EA0200902026000
    +B1454514547904N00612527EA0200402022000
    +B1454544547916N00612552EA0200002017000
    +B1454574547928N00612576EA0199602013000
    +B1455004547940N00612599EA0198902008000
    +B1455044547957N00612629EA0198602002000
    +B1455074547969N00612650EA0197901997000
    +B1455104547982N00612671EA0197401992000
    +B1455134547995N00612692EA0196801987000
    +B1455164548007N00612713EA0196201982000
    +B1455194548019N00612736EA0195801977000
    +B1455224548031N00612759EA0195401971000
    +B1455254548041N00612783EA0194801966000
    +B1455284548052N00612808EA0194101961000
    +B1455314548063N00612833EA0193601956000
    +B1455344548075N00612858EA0193101950000
    +B1455374548087N00612882EA0192601945000
    +B1455404548099N00612905EA0192101940000
    +B1455434548113N00612928EA0191801934000
    +B1455464548126N00612950EA0191401929000
    +B1455494548138N00612972EA0190901924000
    +B1455524548151N00612995EA0190401920000
    +B1455554548163N00613020EA0190201915000
    +B1455584548174N00613045EA0189701910000
    +B1456014548185N00613069EA0189201906000
    +B1456044548196N00613095EA0188901901000
    +B1456074548207N00613120EA0188301897000
    +B1456104548219N00613145EA0187901893000
    +B1456134548231N00613169EA0187601888000
    +B1456164548243N00613195EA0187001884000
    +B1456204548258N00613229EA0186801878000
    +B1456234548269N00613255EA0186301874000
    +B1456264548280N00613281EA0185901870000
    +B1456294548292N00613307EA0185401866000
    +B1456324548304N00613333EA0185201862000
    +B1456354548315N00613358EA0184801858000
    +B1456384548326N00613384EA0184601854000
    +B1456414548337N00613409EA0184001850000
    +B1456444548348N00613434EA0183701846000
    +B1456474548360N00613458EA0183201842000
    +B1456504548372N00613480EA0182601838000
    +B1456534548385N00613503EA0182201834000
    +B1456564548398N00613525EA0181801830000
    +B1456594548410N00613547EA0181301825000
    +B1457024548421N00613572EA0180801821000
    +B1457054548431N00613597EA0180301817000
    +B1457084548441N00613623EA0179801812000
    +B1457114548450N00613649EA0179101808000
    +B1457144548460N00613674EA0178301803000
    +B1457174548471N00613700EA0177801798000
    +B1457204548483N00613724EA0177401793000
    +B1457234548494N00613747EA0176901788000
    +B1457264548505N00613770EA0176301783000
    +B1457294548516N00613794EA0175801778000
    +B1457324548526N00613817EA0175301773000
    +B1457364548539N00613851EA0174801766000
    +B1457394548548N00613877EA0174401761000
    +B1457424548556N00613903EA0174001756000
    +B1457454548564N00613930EA0173601751000
    +B1457484548573N00613957EA0173301747000
    +B1457514548582N00613982EA0172901742000
    +B1457544548592N00614008EA0172501737000
    +B1457574548603N00614031EA0172001733000
    +B1458004548614N00614055EA0171701729000
    +B1458034548624N00614078EA0171301724000
    +B1458064548635N00614102EA0170901720000
    +B1458094548648N00614124EA0170501716000
    +B1458124548658N00614147EA0170101712000
    +B1458154548670N00614170EA0170001707000
    +B1458184548681N00614192EA0169601703000
    +B1458214548692N00614216EA0169001699000
    +B1458244548705N00614239EA0168801695000
    +B1458274548717N00614262EA0168301691000
    +B1458304548728N00614285EA0167701687000
    +B1458334548739N00614310EA0167201682000
    +B1458364548751N00614333EA0166801678000
    +B1458394548762N00614356EA0166401674000
    +B1458424548773N00614380EA0166101669000
    +B1458454548785N00614403EA0165601665000
    +B1458484548796N00614426EA0165301661000
    +B1458514548808N00614450EA0164801657000
    +B1458554548823N00614482EA0164301652000
    +B1458584548836N00614507EA0164101647000
    +B1459014548848N00614530EA0163601643000
    +B1459044548860N00614555EA0163201639000
    +B1459074548873N00614581EA0162801635000
    +B1459104548885N00614606EA0162701631000
    +B1459134548895N00614631EA0162201628000
    +B1459164548907N00614656EA0161701624000
    +B1459194548920N00614680EA0161401620000
    +B1459224548932N00614703EA0160601616000
    +B1459254548945N00614727EA0160501612000
    +B1459284548955N00614750EA0160601609000
    +B1459314548963N00614774EA0160601605000
    +B1459344548974N00614797EA0161101603000
    +B1459374548982N00614824EA0162601602000
    +B1459404548987N00614847EA0163601602000
    +B1459434548993N00614866EA0164501604000
    +B1459464549000N00614876EA0165401606000
    +B1459494549001N00614867EA0166201609000
    +B1459524548984N00614862EA0167601613000
    +B1459554548968N00614883EA0168601618000
    +B1459584548963N00614910EA0169301622000
    +B1500014548969N00614927EA0170301627000
    +B1500044548973N00614925EA0171101630000
    +B1500074548964N00614915EA0172001636000
    +B1500104548942N00614924EA0172901643000
    +B1500134548927N00614952EA0173901651000
    +B1500174548929N00614988EA0175001661000
    +B1500204548935N00614992EA0176101667000
    +B1500234548930N00614983EA0177101674000
    +B1500264548911N00614985EA0178201684000
    +B1500294548893N00615010EA0179201694000
    +B1500324548890N00615041EA0180201703000
    +B1500354548894N00615058EA0181501713000
    +B1500384548901N00615060EA0182701718000
    +B1500414548901N00615050EA0183701724000
    +B1500444548891N00615038EA0185301737000
    +B1500474548873N00615039EA0186401749000
    +B1500504548851N00615058EA0187301761000
    +B1500534548839N00615091EA0188401772000
    +B1500564548840N00615117EA0189401783000
    +B1500594548844N00615128EA0190001792000
    +B1501024548852N00615124EA0190401800000
    +B1501054548861N00615112EA0191501811000
    +B1501084548869N00615103EA0192901820000
    +B1501114548869N00615086EA0194601831000
    +B1501144548856N00615071EA0196101842000
    +B1501174548833N00615072EA0197601853000
    +B1501204548818N00615094EA0199101864000
    +B1501234548813N00615117EA0200301876000
    +B1501264548819N00615117EA0201101882000
    +B1501294548814N00615098EA0202901895000
    +B1501324548795N00615088EA0204201908000
    +B1501354548776N00615104EA0205201921000
    +B1501384548774N00615126EA0206501933000
    +B1501414548777N00615126EA0207201940000
    +B1501444548777N00615109EA0208801952000
    +B1501474548767N00615088EA0209801966000
    +B1501504548748N00615071EA0210501980000
    +B1501534548726N00615076EA0210701993000
    +B1501564548708N00615097EA0210902004000
    +B1501594548706N00615120EA0211802014000
    +B1502024548712N00615126EA0213102021000
    +B1502064548715N00615125EA0213002029000
    +B1502094548723N00615120EA0212802037000
    +B1502124548734N00615122EA0214402048000
    +B1502154548739N00615125EA0215102053000
    +B1502184548750N00615125EA0215902063000
    +B1502214548757N00615118EA0216702072000
    +B1502244548755N00615101EA0216802082000
    +B1502274548737N00615086EA0217002091000
    +B1502304548713N00615088EA0216902100000
    +B1502334548696N00615108EA0217402107000
    +B1502364548693N00615125EA0217802113000
    +B1502394548700N00615132EA0218402117000
    +B1502424548707N00615133EA0219002120000
    +B1502454548715N00615134EA0219002124000
    +B1502484548727N00615135EA0219502131000
    +B1502514548737N00615139EA0219902137000
    +B1502544548744N00615142EA0220302140000
    +B1502574548750N00615141EA0220402144000
    +B1503004548756N00615137EA0219702147000
    +B1503034548768N00615132EA0219002153000
    +B1503064548783N00615131EA0218902158000
    +B1503094548799N00615129EA0218302161000
    +B1503124548816N00615134EA0218102163000
    +B1503154548831N00615137EA0217602165000
    +B1503184548846N00615143EA0216702166000
    +B1503214548864N00615154EA0216502165000
    +B1503244548881N00615168EA0216502165000
    +B1503274548897N00615181EA0216102164000
    +B1503304548914N00615195EA0215802163000
    +B1503334548931N00615210EA0215302161000
    +B1503374548955N00615227EA0215202159000
    +B1503404548970N00615233EA0214802158000
    +B1503434548985N00615238EA0214102155000
    +B1503464549002N00615247EA0213702152000
    +B1503494549019N00615256EA0213202149000
    +B1503524549037N00615267EA0212902146000
    +B1503554549055N00615277EA0212402142000
    +B1503584549074N00615288EA0211902138000
    +B1504014549094N00615297EA0211502134000
    +B1504044549113N00615306EA0211102130000
    +B1504074549132N00615315EA0210502127000
    +B1504104549152N00615324EA0210202123000
    +B1504134549171N00615333EA0209902119000
    +B1504164549191N00615342EA0209602116000
    +B1504194549211N00615348EA0209402112000
    +B1504224549230N00615356EA0209202109000
    +B1504254549248N00615363EA0208802106000
    +B1504284549267N00615368EA0208602102000
    +B1504314549286N00615374EA0208302099000
    +B1504344549304N00615381EA0208102096000
    +B1504374549321N00615388EA0207502093000
    +B1504404549339N00615396EA0207002089000
    +B1504434549357N00615405EA0206702086000
    +B1504464549374N00615414EA0206002082000
    +B1504494549391N00615424EA0205502078000
    +B1504534549415N00615441EA0205102073000
    +B1504564549432N00615455EA0204802069000
    +B1504594549450N00615465EA0204302065000
    +B1505024549467N00615473EA0203702060000
    +B1505054549485N00615481EA0203302056000
    +B1505084549503N00615487EA0202502052000
    +B1505114549520N00615494EA0201902047000
    +B1505144549538N00615498EA0201002042000
    +B1505174549557N00615503EA0200102037000
    +B1505204549575N00615507EA0199302031000
    +B1505234549596N00615511EA0198802025000
    +B1505264549618N00615517EA0199002020000
    +B1505294549638N00615523EA0198902015000
    +B1505324549659N00615529EA0198902011000
    +B1505354549680N00615535EA0199102007000
    +B1505384549700N00615543EA0199302004000
    +B1505414549720N00615551EA0199202001000
    +B1505444549741N00615559EA0199501998000
    +B1505474549760N00615569EA0199701996000
    +B1505504549777N00615580EA0199001995000
    +B1505534549797N00615589EA0198801993000
    +B1505564549814N00615596EA0198501991000
    +B1505594549832N00615601EA0197301988000
    +B1506024549851N00615602EA0196801985000
    +B1506054549869N00615602EA0196101981000
    +B1506084549891N00615603EA0196201978000
    +B1506114549910N00615603EA0195901974000
    +B1506154549935N00615604EA0195301969000
    +B1506184549955N00615603EA0195601966000
    +B1506214549975N00615607EA0196101963000
    +B1506244549996N00615610EA0196401961000
    +B1506274550015N00615615EA0196801959000
    +B1506304550035N00615621EA0196701958000
    +B1506334550056N00615628EA0196801957000
    +B1506364550076N00615633EA0197101956000
    +B1506394550095N00615645EA0197101956000
    +B1506424550114N00615652EA0197201956000
    +B1506454550132N00615653EA0197201956000
    +B1506484550152N00615652EA0197001956000
    +B1506514550171N00615653EA0196901956000
    +B1506544550192N00615651EA0196301955000
    +B1506574550212N00615646EA0196301955000
    +B1507004550231N00615639EA0195601954000
    +B1507034550250N00615629EA0195301952000
    +B1507064550268N00615620EA0195001951000
    +B1507094550287N00615616EA0194901949000
    +B1507124550305N00615610EA0194801947000
    +B1507154550324N00615606EA0195301946000
    +B1507184550341N00615602EA0195901945000
    +B1507214550354N00615604EA0196201945000
    +B1507244550365N00615600EA0195901945000
    +B1507274550375N00615590EA0196501946000
    +B1507304550383N00615585EA0196801946000
    +B1507344550396N00615580EA0196901947000
    +B1507374550406N00615574EA0197201948000
    +B1507404550417N00615566EA0197101949000
    +B1507434550429N00615561EA0197801950000
    +B1507464550435N00615557EA0198201951000
    +B1507494550440N00615554EA0198301952000
    +B1507524550448N00615545EA0198101954000
    +B1507554550458N00615537EA0198501955000
    +B1507584550467N00615533EA0199201957000
    +B1508014550472N00615526EA0200201958000
    +B1508044550475N00615521EA0200701960000
    +B1508074550477N00615512EA0201001962000
    +B1508104550479N00615505EA0201501965000
    +B1508134550484N00615503EA0201201967000
    +B1508164550492N00615497EA0201101970000
    +B1508194550501N00615491EA0201801974000
    +B1508224550510N00615486EA0202301978000
    +B1508254550519N00615478EA0202901982000
    +B1508284550529N00615472EA0203001986000
    +B1508314550542N00615464EA0203601990000
    +B1508344550554N00615456EA0204401994000
    +B1508374550566N00615450EA0204901999000
    +B1508404550577N00615446EA0205602003000
    +B1508434550586N00615441EA0206502008000
    +B1508464550596N00615439EA0207002013000
    +B1508494550605N00615436EA0207302018000
    +B1508524550615N00615430EA0207302023000
    +B1508554550631N00615423EA0207402027000
    +B1508584550647N00615415EA0207902031000
    +B1509024550669N00615406EA0207902036000
    +B1509054550686N00615396EA0208002040000
    +B1509084550704N00615388EA0208402043000
    +B1509114550722N00615385EA0209402046000
    +B1509144550738N00615385EA0210102050000
    +B1509174550754N00615384EA0210102054000
    +B1509204550772N00615383EA0209702058000
    +B1509234550793N00615382EA0210202061000
    +B1509264550813N00615385EA0211102064000
    +B1509294550830N00615390EA0211802068000
    +B1509324550843N00615393EA0212502072000
    +B1509354550856N00615393EA0213302076000
    +B1509384550868N00615394EA0213802081000
    +B1509414550882N00615399EA0214402085000
    +B1509444550894N00615420EA0215002090000
    +B1509474550904N00615446EA0215702095000
    +B1509504550911N00615476EA0216202100000
    +B1509534550906N00615504EA0216502105000
    +B1509564550894N00615523EA0216702110000
    +B1509594550881N00615525EA0216802114000
    +B1510024550870N00615520EA0216802119000
    +B1510054550857N00615508EA0217202123000
    +B1510084550846N00615498EA0217902127000
    +B1510114550839N00615493EA0218102130000
    +B1510144550840N00615484EA0218802132000
    +B1510174550848N00615479EA0219602135000
    +B1510204550858N00615485EA0219902141000
    +B1510234550869N00615504EA0220502147000
    +B1510264550870N00615533EA0221102152000
    +B1510294550863N00615558EA0221402157000
    +B1510324550849N00615572EA0221302162000
    +B1510354550832N00615575EA0220602167000
    +B1510384550808N00615573EA0221902170000
    +B1510414550792N00615574EA0221702175000
    +B1510444550773N00615572EA0222102178000
    +B1510484550751N00615572EA0222702183000
    +B1510514550737N00615572EA0223102187000
    +B1510544550726N00615573EA0224002191000
    +B1510574550715N00615574EA0224302195000
    +B1511004550703N00615573EA0225102199000
    +B1511034550691N00615574EA0225902204000
    +B1511064550680N00615572EA0226602209000
    +B1511094550675N00615564EA0227602211000
    +B1511124550679N00615554EA0229002215000
    +B1511154550690N00615550EA0229902222000
    +B1511184550703N00615563EA0230702230000
    +B1511214550710N00615589EA0231902237000
    +B1511244550707N00615618EA0232702245000
    +B1511274550695N00615641EA0233002253000
    +B1511304550680N00615649EA0233202260000
    +B1511334550670N00615642EA0233602266000
    +B1511364550668N00615629EA0234202272000
    +B1511394550674N00615618EA0234802278000
    +B1511424550686N00615614EA0235402285000
    +B1511454550700N00615621EA0236102291000
    +B1511484550711N00615634EA0236102297000
    +B1511514550719N00615664EA0236702303000
    +B1511544550714N00615693EA0237802309000
    +B1511574550702N00615715EA0238202315000
    +B1512004550691N00615737EA0238202320000
    +B1512034550676N00615745EA0237902326000
    +B1512064550667N00615739EA0238102329000
    +B1512094550667N00615727EA0237502332000
    +B1512124550671N00615710EA0237802336000
    +B1512154550678N00615695EA0238302340000
    +B1512194550691N00615672EA0238902344000
    +B1512224550700N00615658EA0239502348000
    +B1512254550711N00615644EA0239902352000
    +B1512284550722N00615630EA0240702356000
    +B1512314550734N00615617EA0241602360000
    +B1512344550749N00615610EA0243202365000
    +B1512374550764N00615614EA0244402371000
    +B1512404550779N00615637EA0246202378000
    +B1512434550785N00615665EA0247502385000
    +B1512464550777N00615693EA0248402394000
    +B1512494550763N00615704EA0249502402000
    +B1512524550756N00615695EA0250702408000
    +B1512554550756N00615683EA0251402415000
    +B1512584550767N00615672EA0252902425000
    +B1513014550784N00615675EA0254502436000
    +B1513044550800N00615687EA0256302448000
    +B1513074550816N00615702EA0257902460000
    +B1513104550829N00615721EA0259402472000
    +B1513134550833N00615745EA0260002485000
    +B1513164550823N00615767EA0261002497000
    +B1513194550809N00615772EA0262502509000
    +B1513224550802N00615770EA0263202515000
    +B1513254550800N00615757EA0263702524000
    +B1513284550802N00615739EA0264802537000
    +B1513314550811N00615720EA0266502550000
    +B1513344550818N00615704EA0267302561000
    +B1513374550829N00615699EA0267402573000
    +B1513404550842N00615717EA0268102583000
    +B1513434550853N00615736EA0268602593000
    +B1513464550862N00615758EA0268502603000
    +B1513494550869N00615788EA0268902611000
    +B1513524550879N00615813EA0269202619000
    +B1513554550888N00615832EA0268302626000
    +B1513584550901N00615855EA0267902631000
    +B1514014550914N00615875EA0267502636000
    +B1514044550933N00615902EA0267002640000
    +B1514084550947N00615922EA0266802642000
    +B1514114550960N00615942EA0266202644000
    +B1514144550974N00615965EA0265902645000
    +B1514174550987N00615986EA0265302645000
    +B1514204551004N00616008EA0264902645000
    +B1514234551018N00616034EA0264902645000
    +B1514264551029N00616059EA0264502644000
    +B1514294551042N00616086EA0263602643000
    +B1514324551056N00616121EA0262902641000
    +B1514354551071N00616156EA0262602638000
    +B1514384551084N00616192EA0262302636000
    +B1514414551098N00616226EA0261602633000
    +B1514444551112N00616261EA0260802629000
    +B1514474551128N00616296EA0260102625000
    +B1514504551144N00616330EA0259502621000
    +B1514534551161N00616363EA0259102616000
    +B1514564551176N00616395EA0258402612000
    +B1514594551193N00616431EA0257602607000
    +B1515024551209N00616467EA0257302601000
    +B1515054551226N00616498EA0256702597000
    +B1515084551241N00616530EA0256002591000
    +B1515114551256N00616565EA0256002586000
    +B1515144551270N00616594EA0255702581000
    +B1515174551279N00616622EA0255002577000
    +B1515204551292N00616655EA0254902572000
    +B1515244551309N00616696EA0254802567000
    +B1515274551324N00616726EA0254302562000
    +B1515304551339N00616758EA0254302558000
    +B1515334551352N00616792EA0254102555000
    +B1515364551366N00616826EA0253902551000
    +B1515394551379N00616858EA0253402548000
    +B1515424551394N00616890EA0252902544000
    +B1515454551409N00616920EA0252502541000
    +B1515484551426N00616949EA0252002537000
    +B1515514551443N00616978EA0251702533000
    +B1515544551460N00617005EA0251202529000
    +B1515574551478N00617031EA0250702525000
    +B1516004551499N00617056EA0250402521000
    +B1516034551520N00617079EA0250002517000
    +B1516064551542N00617102EA0249702513000
    +B1516094551565N00617124EA0249402509000
    +B1516124551586N00617145EA0249302506000
    +B1516154551607N00617165EA0249602502000
    +B1516184551625N00617180EA0249802500000
    +B1516214551641N00617196EA0249402497000
    +B1516244551657N00617216EA0249502495000
    +B1516274551674N00617235EA0249102493000
    +B1516304551691N00617252EA0249002491000
    +B1516334551707N00617268EA0248802489000
    +B1516364551723N00617285EA0248502488000
    +B1516404551743N00617309EA0248502485000
    +B1516434551758N00617324EA0248402483000
    +B1516464551772N00617340EA0248102482000
    +B1516494551785N00617360EA0247902480000
    +B1516524551795N00617384EA0247802478000
    +B1516554551803N00617410EA0247802476000
    +B1516584551811N00617436EA0248102475000
    +B1517014551820N00617461EA0247202473000
    +B1517044551832N00617495EA0247102471000
    +B1517074551842N00617528EA0247102469000
    +B1517104551851N00617562EA0246602467000
    +B1517134551862N00617596EA0246502465000
    +B1517164551871N00617629EA0246302463000
    +B1517194551878N00617662EA0246002461000
    +B1517224551882N00617697EA0245402459000
    +B1517254551886N00617734EA0245402456000
    +B1517284551890N00617769EA0245302454000
    +B1517314551895N00617804EA0244602452000
    +B1517344551902N00617840EA0244202449000
    +B1517374551911N00617875EA0244102447000
    +B1517404551921N00617910EA0243702444000
    +B1517434551932N00617945EA0243502441000
    +B1517464551941N00617977EA0243202439000
    +B1517494551951N00618011EA0242502436000
    +B1517524551960N00618047EA0242002434000
    +B1517554551969N00618083EA0241602431000
    +B1517584551977N00618120EA0241102427000
    +B1518024551988N00618168EA0240602423000
    +B1518054551997N00618205EA0239802419000
    +B1518084552007N00618241EA0239402415000
    +B1518114552016N00618278EA0238902410000
    +B1518144552025N00618313EA0238302406000
    +B1518174552034N00618349EA0238002402000
    +B1518204552042N00618385EA0237502397000
    +B1518234552051N00618419EA0236802392000
    +B1518264552061N00618455EA0236202387000
    +B1518294552071N00618492EA0235702382000
    +B1518324552081N00618528EA0235002377000
    +B1518354552091N00618563EA0234402372000
    +B1518384552101N00618596EA0233502367000
    +B1518414552111N00618631EA0233102361000
    +B1518444552121N00618665EA0232702356000
    +B1518474552129N00618697EA0232402351000
    +B1518504552135N00618729EA0231302346000
    +B1518534552144N00618764EA0231002340000
    +B1518564552154N00618797EA0230702335000
    +B1518594552164N00618831EA0230002330000
    +B1519024552175N00618864EA0229602326000
    +B1519054552184N00618899EA0228902321000
    +B1519084552192N00618935EA0228402316000
    +B1519114552203N00618971EA0228202311000
    +B1519144552212N00619006EA0228002306000
    +B1519174552222N00619038EA0227502302000
    +B1519204552230N00619072EA0227102297000
    +B1519244552241N00619117EA0226802291000
    +B1519274552249N00619148EA0226002287000
    +B1519304552258N00619181EA0225202282000
    +B1519334552266N00619215EA0224902277000
    +B1519364552274N00619248EA0224602272000
    +B1519394552282N00619280EA0224102267000
    +B1519424552290N00619313EA0223702263000
    +B1519454552298N00619346EA0223202258000
    +B1519484552307N00619378EA0222702253000
    +B1519514552315N00619411EA0222302248000
    +B1519544552324N00619445EA0222002243000
    +B1519574552332N00619479EA0221702239000
    +B1520004552341N00619512EA0221302234000
    +B1520034552349N00619546EA0220902230000
    +B1520064552358N00619578EA0220502225000
    +B1520094552367N00619611EA0220002221000
    +B1520124552375N00619644EA0219602216000
    +B1520154552384N00619678EA0219302212000
    +B1520184552392N00619711EA0219102208000
    +B1520214552399N00619744EA0218702204000
    +B1520244552406N00619778EA0218502200000
    +B1520274552414N00619812EA0218202196000
    +B1520304552423N00619846EA0217802192000
    +B1520334552431N00619879EA0217502189000
    +B1520364552439N00619912EA0217102185000
    +B1520404552450N00619954EA0216702180000
    +B1520434552458N00619987EA0216302177000
    +B1520464552466N00620021EA0216102173000
    +B1520494552474N00620056EA0215802170000
    +B1520524552482N00620091EA0215302167000
    +B1520554552488N00620127EA0215102163000
    +B1520584552495N00620161EA0214702159000
    +B1521014552503N00620195EA0214202156000
    +B1521044552511N00620230EA0213802152000
    +B1521074552519N00620264EA0213702149000
    +B1521104552527N00620298EA0212902145000
    +B1521134552536N00620333EA0212402142000
    +B1521164552544N00620367EA0212102138000
    +B1521194552553N00620402EA0211602134000
    +B1521224552563N00620436EA0211302130000
    +B1521254552573N00620468EA0210802127000
    +B1521284552583N00620502EA0210202123000
    +B1521314552594N00620535EA0209702119000
    +B1521344552604N00620568EA0209102115000
    +B1521374552614N00620602EA0208502110000
    +B1521404552624N00620637EA0208402106000
    +B1521434552633N00620671EA0208102102000
    +B1521464552641N00620706EA0207602097000
    +B1521494552648N00620741EA0207102093000
    +B1521524552656N00620777EA0206602089000
    +B1521564552667N00620823EA0206302083000
    +B1521594552675N00620858EA0205802079000
    +B1522024552682N00620893EA0205502074000
    +B1522054552689N00620928EA0205002070000
    +B1522084552696N00620964EA0204602066000
    +B1522114552703N00621000EA0204202061000
    +B1522144552711N00621034EA0203602057000
    +B1522174552720N00621070EA0203102053000
    +B1522204552728N00621106EA0202602049000
    +B1522234552736N00621142EA0202102045000
    +B1522264552745N00621178EA0201702041000
    +B1522294552754N00621213EA0201202037000
    +B1522324552762N00621247EA0200802033000
    +B1522354552768N00621280EA0200502028000
    +B1522384552774N00621312EA0199902024000
    +B1522414552781N00621345EA0199502020000
    +B1522444552788N00621378EA0199102015000
    +B1522474552796N00621410EA0198202010000
    +B1522504552804N00621445EA0197902006000
    +B1522534552812N00621479EA0197402001000
    +B1522564552820N00621512EA0197001996000
    +B1522594552827N00621545EA0196401991000
    +B1523024552834N00621579EA0195801986000
    +B1523054552841N00621613EA0195301981000
    +B1523084552847N00621647EA0194801976000
    +B1523124552854N00621694EA0194301970000
    +B1523154552860N00621729EA0193801965000
    +B1523184552866N00621764EA0193401960000
    +B1523214552872N00621800EA0192901955000
    +B1523244552880N00621836EA0192501950000
    +B1523274552887N00621871EA0192101945000
    +B1523304552894N00621906EA0191801940000
    +B1523334552902N00621940EA0191401936000
    +B1523364552910N00621973EA0191301931000
    +B1523394552917N00622004EA0190801927000
    +B1523424552923N00622037EA0190401922000
    +B1523454552931N00622069EA0189901918000
    +B1523484552942N00622101EA0190101913000
    +B1523514552950N00622129EA0189601909000
    +B1523544552959N00622160EA0189001905000
    +B1523574552967N00622191EA0188701901000
    +B1524004552974N00622225EA0187901897000
    +B1524034552981N00622258EA0187901893000
    +B1524064552987N00622288EA0186801889000
    +B1524094552994N00622323EA0186501884000
    +B1524124553002N00622354EA0186101879000
    +B1524154553009N00622384EA0185701875000
    +B1524184553016N00622412EA0186101871000
    +B1524214553023N00622438EA0186101867000
    +B1524244553027N00622465EA0186401865000
    +B1524274553031N00622491EA0186601862000
    +B1524314553034N00622524EA0187301860000
    +B1524344553032N00622549EA0187901860000
    +B1524374553030N00622577EA0189201860000
    +B1524404553026N00622602EA0190201862000
    +B1524434553030N00622624EA0190601864000
    +B1524464553042N00622635EA0191701867000
    +B1524494553050N00622631EA0193101869000
    +B1524524553054N00622620EA0194401872000
    +B1524554553050N00622605EA0195501877000
    +B1524584553035N00622600EA0196601884000
    +B1525014553018N00622610EA0197801892000
    +B1525044553007N00622626EA0198301900000
    +B1525074553003N00622648EA0199201907000
    +B1525104553007N00622664EA0200101916000
    +B1525134553014N00622665EA0201101921000
    +B1525164553016N00622652EA0202301929000
    +B1525194553006N00622637EA0203301939000
    +B1525224552991N00622630EA0204001948000
    +B1525254552976N00622641EA0204501957000
    +B1525284552967N00622660EA0205301965000
    +B1525314552965N00622680EA0205901974000
    +B1525344552972N00622694EA0206501982000
    +B1525374552981N00622690EA0207901990000
    +B1525404552984N00622681EA0208601995000
    +B1525434552988N00622667EA0209502003000
    +B1525464552989N00622653EA0210002012000
    +B1525494552985N00622633EA0211002021000
    +B1525524552972N00622622EA0211602029000
    +B1525554552955N00622625EA0211702038000
    +B1525584552943N00622643EA0211602045000
    +B1526014552942N00622665EA0212102052000
    +B1526044552951N00622677EA0212702058000
    +B1526074552961N00622678EA0212902064000
    +B1526114552971N00622661EA0213502072000
    +B1526144552974N00622646EA0214102077000
    +B1526174552974N00622628EA0214502082000
    +B1526204552968N00622611EA0214702087000
    +B1526234552954N00622602EA0214302091000
    +B1526264552941N00622614EA0213702095000
    +B1526294552939N00622637EA0214002098000
    +B1526324552945N00622656EA0214202101000
    +B1526354552954N00622670EA0214802105000
    +B1526384552962N00622679EA0215302108000
    +B1526414552967N00622692EA0215102111000
    +B1526444552969N00622715EA0216102115000
    +B1526474552966N00622734EA0217102119000
    +B1526504552959N00622753EA0217902124000
    +B1526534552945N00622771EA0218802129000
    +B1526564552926N00622773EA0219702135000
    +B1526594552910N00622767EA0220302141000
    +B1527024552896N00622749EA0221002146000
    +B1527054552887N00622732EA0221302152000
    +B1527084552887N00622715EA0221202157000
    +B1527114552894N00622712EA0221202160000
    +B1527144552901N00622726EA0221602165000
    +B1527174552905N00622743EA0222002169000
    +B1527204552910N00622759EA0222602173000
    +B1527234552918N00622762EA0223102176000
    +B1527264552926N00622759EA0224002180000
    +B1527294552934N00622759EA0224702183000
    +B1527324552941N00622763EA0225202186000
    +B1527354552943N00622776EA0225402191000
    +B1527384552938N00622792EA0225602197000
    +B1527414552926N00622807EA0226302203000
    +B1527444552910N00622819EA0227302209000
    +B1527484552892N00622810EA0227202216000
    +B1527514552885N00622787EA0227302221000
    +B1527544552888N00622765EA0227402226000
    +B1527574552899N00622762EA0227902230000
    +B1528004552907N00622768EA0227602234000
    +B1528034552912N00622782EA0227902238000
    +B1528064552913N00622800EA0228502241000
    +B1528094552913N00622810EA0228802243000
    +B1528124552911N00622825EA0229002247000
    +B1528154552905N00622845EA0228802250000
    +B1528184552897N00622869EA0229002253000
    +B1528214552893N00622902EA0229702256000
    +B1528244552893N00622931EA0230902260000
    +B1528274552891N00622961EA0231902264000
    +B1528304552879N00622993EA0234002269000
    +B1528334552862N00623012EA0235702276000
    +B1528364552844N00623018EA0236902284000
    +B1528394552831N00623005EA0237602291000
    +B1528424552830N00622985EA0238202299000
    +B1528454552839N00622971EA0239102307000
    +B1528484552853N00622974EA0240302315000
    +B1528514552861N00622990EA0241602324000
    +B1528544552865N00623013EA0242902333000
    +B1528574552856N00623041EA0244902343000
    +B1529004552839N00623061EA0246502354000
    +B1529034552819N00623079EA0247802365000
    +B1529064552803N00623080EA0248802377000
    +B1529094552790N00623072EA0249302389000
    +B1529124552783N00623051EA0250402400000
    +B1529154552792N00623036EA0252002411000
    +B1529184552802N00623041EA0253502422000
    +B1529214552805N00623058EA0254402433000
    +B1529254552810N00623087EA0256002448000
    +B1529284552812N00623110EA0257002460000
    +B1529314552820N00623129EA0257702471000
    +B1529344552828N00623149EA0258302481000
    +B1529374552833N00623172EA0258802491000
    +B1529404552837N00623197EA0259602501000
    +B1529434552844N00623221EA0260102510000
    +B1529464552849N00623249EA0260802519000
    +B1529494552853N00623276EA0261902528000
    +B1529524552857N00623300EA0263102537000
    +B1529554552860N00623322EA0263402546000
    +B1529584552865N00623347EA0265002555000
    +B1530014552869N00623367EA0265802565000
    +B1530044552875N00623394EA0266702574000
    +B1530074552889N00623419EA0267802583000
    +B1530104552901N00623445EA0267902592000
    +B1530134552909N00623479EA0268902601000
    +B1530164552914N00623512EA0269302609000
    +B1530194552919N00623545EA0269602618000
    +B1530224552925N00623577EA0269702626000
    +B1530254552929N00623607EA0268902633000
    +B1530284552933N00623639EA0268102638000
    +B1530314552940N00623673EA0267502642000
    +B1530344552946N00623705EA0267202645000
    +B1530374552954N00623736EA0266902647000
    +B1530404552962N00623767EA0266202649000
    +B1530434552969N00623800EA0265402649000
    +B1530464552975N00623836EA0265402649000
    +B1530494552981N00623869EA0264602648000
    +B1530524552988N00623904EA0264202647000
    +B1530554552997N00623939EA0264002645000
    +B1530584553007N00623972EA0263602643000
    +B1531024553020N00624017EA0263402640000
    +B1531054553030N00624050EA0262902638000
    +B1531084553041N00624082EA0262602635000
    +B1531114553054N00624111EA0262002633000
    +B1531144553065N00624141EA0261302630000
    +B1531174553078N00624171EA0260902626000
    +B1531204553089N00624201EA0260402623000
    +B1531234553101N00624231EA0259802619000
    +B1531264553113N00624265EA0259402615000
    +B1531294553125N00624299EA0259402612000
    +B1531324553134N00624331EA0259102608000
    +B1531354553143N00624364EA0258902605000
    +B1531384553151N00624398EA0258902602000
    +B1531414553159N00624429EA0258502599000
    +B1531444553168N00624462EA0257802596000
    +B1531474553176N00624494EA0257502593000
    +B1531504553185N00624523EA0256902589000
    +B1531534553194N00624554EA0256502586000
    +B1531564553205N00624582EA0256202582000
    +B1531594553214N00624609EA0255602579000
    +B1532024553223N00624638EA0255002575000
    +B1532054553232N00624667EA0254502571000
    +B1532084553241N00624697EA0254102566000
    +B1532114553250N00624727EA0253702562000
    +B1532144553258N00624757EA0253402558000
    +B1532184553269N00624796EA0253102553000
    +B1532214553276N00624826EA0252402549000
    +B1532244553285N00624857EA0252002544000
    +B1532274553294N00624888EA0251902540000
    +B1532304553303N00624919EA0251302536000
    +B1532334553312N00624953EA0250802531000
    +B1532364553319N00624986EA0250302526000
    +B1532394553327N00625019EA0249802522000
    +B1532424553335N00625051EA0249502518000
    +B1532454553344N00625082EA0249002513000
    +B1532484553352N00625114EA0248602509000
    +B1532514553361N00625147EA0248302504000
    +B1532544553368N00625179EA0247902500000
    +B1532574553375N00625212EA0247302496000
    +B1533004553380N00625245EA0246902492000
    +B1533034553386N00625278EA0246402488000
    +B1533064553391N00625310EA0245902484000
    +B1533094553395N00625342EA0245302480000
    +B1533124553401N00625375EA0245002476000
    +B1533154553408N00625406EA0244402471000
    +B1533184553416N00625437EA0244002467000
    +B1533214553426N00625468EA0243602462000
    +B1533244553435N00625498EA0243002457000
    +B1533274553444N00625529EA0242602453000
    +B1533304553453N00625560EA0242202448000
    +B1533344553464N00625602EA0241902442000
    +B1533374553472N00625632EA0241602437000
    +B1533404553480N00625661EA0241002433000
    +B1533434553487N00625693EA0240702428000
    +B1533464553492N00625725EA0240502424000
    +B1533494553496N00625756EA0240002419000
    +B1533524553500N00625788EA0239602415000
    +B1533554553504N00625820EA0239202411000
    +B1533584553508N00625853EA0238802406000
    +B1534014553511N00625885EA0238202402000
    +B1534044553514N00625917EA0237702397000
    +B1534074553517N00625950EA0237202393000
    +B1534104553518N00625983EA0236702388000
    +B1534134553519N00626016EA0236202384000
    +B1534164553521N00626050EA0235702379000
    +B1534194553523N00626084EA0235202375000
    +B1534224553525N00626117EA0234802371000
    +B1534254553528N00626151EA0234402366000
    +B1534284553531N00626184EA0233902362000
    +B1534314553533N00626217EA0233302358000
    +B1534344553537N00626249EA0232802353000
    +B1534374553541N00626283EA0232302349000
    +B1534404553545N00626316EA0232002344000
    +B1534434553550N00626349EA0231602340000
    +B1534464553556N00626381EA0231302335000
    +B1534504553562N00626424EA0231002329000
    +B1534534553566N00626455EA0230702325000
    +B1534564553570N00626487EA0230302321000
    +B1534594553575N00626516EA0230002316000
    +B1535024553579N00626545EA0229302312000
    +B1535054553583N00626576EA0228702307000
    +B1535084553587N00626608EA0228202303000
    +B1535114553591N00626639EA0227902298000
    +B1535144553595N00626670EA0227402294000
    +B1535174553598N00626702EA0227002289000
    +B1535204553601N00626733EA0226702285000
    +B1535234553603N00626764EA0225902280000
    +B1535264553604N00626795EA0225302276000
    +B1535294553605N00626828EA0224702272000
    +B1535324553606N00626861EA0224402267000
    +B1535354553608N00626894EA0224102263000
    +B1535384553609N00626927EA0223802258000
    +B1535414553609N00626960EA0223502254000
    +B1535444553610N00626993EA0223202250000
    +B1535474553611N00627025EA0222802246000
    +B1535504553612N00627058EA0222502242000
    +B1535534553614N00627091EA0222102238000
    +B1535564553617N00627123EA0221502234000
    +B1535594553619N00627156EA0221302230000
    +B1536024553622N00627188EA0221002226000
    +B1536064553625N00627229EA0220602221000
    +B1536094553628N00627259EA0220202218000
    +B1536124553630N00627290EA0219902214000
    +B1536154553632N00627321EA0219602210000
    +B1536184553635N00627352EA0219302206000
    +B1536214553638N00627383EA0219002203000
    +B1536244553641N00627414EA0218802199000
    +B1536274553645N00627444EA0218502196000
    +B1536304553648N00627475EA0218402193000
    +B1536334553652N00627505EA0218102190000
    +B1536364553656N00627535EA0217802187000
    +B1536394553661N00627564EA0217502184000
    +B1536424553666N00627594EA0217402181000
    +B1536454553672N00627623EA0217102178000
    +B1536484553678N00627652EA0216802175000
    +B1536514553685N00627682EA0216502172000
    +B1536544553692N00627711EA0216002169000
    +B1536574553700N00627740EA0215702166000
    +B1537004553708N00627769EA0215402163000
    +B1537034553716N00627798EA0215002160000
    +B1537064553724N00627827EA0214902157000
    +B1537094553731N00627856EA0214202154000
    +B1537124553738N00627885EA0214002151000
    +B1537154553745N00627915EA0213702148000
    +B1537184553752N00627945EA0213202145000
    +B1537224553760N00627986EA0213102140000
    +B1537254553766N00628016EA0212602137000
    +B1537284553773N00628047EA0212002134000
    +B1537314553779N00628079EA0211602130000
    +B1537344553785N00628111EA0211102126000
    +B1537374553791N00628143EA0210502122000
    +B1537404553797N00628177EA0210102118000
    +B1537434553802N00628210EA0209702114000
    +B1537464553807N00628244EA0209302110000
    +B1537494553813N00628278EA0209002106000
    +B1537524553820N00628312EA0208702102000
    +B1537554553827N00628346EA0208302098000
    +B1537584553835N00628380EA0208002094000
    +B1538014553842N00628415EA0207802090000
    +B1538044553850N00628450EA0207402086000
    +B1538074553858N00628484EA0207502083000
    +B1538104553864N00628513EA0208102080000
    +B1538134553869N00628536EA0208002078000
    +B1538164553873N00628562EA0208102076000
    +B1538194553879N00628585EA0207902074000
    +B1538224553886N00628609EA0207602072000
    +B1538254553894N00628631EA0207202070000
    +B1538284553902N00628654EA0207102069000
    +B1538314553907N00628676EA0206502067000
    +B1538344553910N00628701EA0206302064000
    +B1538384553916N00628732EA0206102062000
    +B1538414553921N00628754EA0206002060000
    +B1538444553927N00628775EA0206102058000
    +B1538474553935N00628793EA0205802056000
    +B1538504553945N00628810EA0205702054000
    +B1538534553954N00628826EA0205602052000
    +B1538564553964N00628843EA0205402051000
    +B1538594553973N00628859EA0205202049000
    +B1539024553983N00628878EA0205202048000
    +B1539054553992N00628897EA0205002046000
    +B1539084554002N00628916EA0205002045000
    +B1539114554012N00628934EA0204702044000
    +B1539144554024N00628952EA0204502042000
    +B1539174554035N00628968EA0204302041000
    +B1539204554048N00628984EA0203902039000
    +B1539234554060N00629002EA0203702037000
    +B1539264554073N00629020EA0203402035000
    +B1539294554085N00629039EA0203402033000
    +B1539324554096N00629059EA0203302031000
    +B1539354554107N00629079EA0203202030000
    +B1539384554120N00629098EA0203302028000
    +B1539414554133N00629115EA0203802027000
    +B1539444554144N00629131EA0203602026000
    +B1539474554155N00629150EA0203502025000
    +B1539504554167N00629165EA0203402025000
    +B1539544554184N00629179EA0203302024000
    +B1539574554197N00629187EA0203202023000
    +B1540004554211N00629195EA0203102023000
    +B1540034554226N00629198EA0202902022000
    +B1540064554241N00629199EA0202902022000
    +B1540094554255N00629200EA0202902021000
    +B1540124554268N00629202EA0202902020000
    +B1540154554281N00629203EA0202902020000
    +B1540184554295N00629200EA0203202019000
    +B1540214554307N00629199EA0203502019000
    +B1540244554320N00629196EA0203702019000
    +B1540274554332N00629198EA0204002020000
    +B1540304554344N00629204EA0204302021000
    +B1540334554357N00629214EA0204402022000
    +B1540364554370N00629225EA0204802023000
    +B1540394554382N00629234EA0205102024000
    +B1540424554395N00629243EA0205302026000
    +B1540454554408N00629250EA0205602028000
    +B1540484554421N00629253EA0206002029000
    +B1540514554433N00629255EA0206302031000
    +B1540544554441N00629248EA0206702033000
    +B1540574554442N00629234EA0207502035000
    +B1541004554439N00629224EA0207302036000
    +B1541034554429N00629210EA0207502039000
    +B1541064554413N00629212EA0207602041000
    +B1541094554404N00629232EA0208102044000
    +B1541124554407N00629255EA0208302046000
    +B1541154554417N00629274EA0208502049000
    +B1541194554437N00629283EA0209402053000
    +B1541224554447N00629281EA0210402056000
    +B1541254554453N00629272EA0211102060000
    +B1541284554445N00629261EA0211802064000
    +B1541314554430N00629271EA0212402069000
    +B1541344554427N00629296EA0212802074000
    +B1541374554435N00629317EA0213902078000
    +B1541404554447N00629330EA0214502083000
    +B1541434554459N00629329EA0214802089000
    +B1541464554462N00629311EA0215202094000
    +B1541494554454N00629298EA0216102099000
    +B1541524554439N00629307EA0216702104000
    +B1541554554432N00629332EA0217102109000
    +B1541584554441N00629356EA0218102115000
    +B1542014554455N00629361EA0218602121000
    +B1542044554461N00629347EA0218802127000
    +B1542074554460N00629328EA0219502132000
    +B1542104554447N00629321EA0219702138000
    +B1542134554433N00629338EA0220202143000
    +B1542164554435N00629365EA0220602148000
    +B1542194554448N00629375EA0221202153000
    +B1542224554454N00629363EA0221302158000
    +B1542254554448N00629346EA0222102163000
    +B1542284554434N00629350EA0222602168000
    +B1542314554423N00629370EA0222802174000
    +B1542344554423N00629397EA0223002179000
    +B1542374554433N00629418EA0223602184000
    +B1542404554445N00629422EA0223802189000
    +B1542434554453N00629408EA0223702193000
    +B1542464554455N00629389EA0224402198000
    +B1542504554442N00629377EA0225002204000
    +B1542534554427N00629387EA0226002208000
    +B1542564554423N00629411EA0226602213000
    +B1542594554434N00629425EA0226802217000
    +B1543024554444N00629419EA0227302222000
    +B1543054554447N00629406EA0228102225000
    +B1543084554441N00629394EA0229102230000
    +B1543114554429N00629389EA0229802235000
    +B1543144554414N00629401EA0230502241000
    +B1543174554408N00629424EA0231402247000
    +B1543204554417N00629442EA0231602253000
    +B1543234554431N00629443EA0232502259000
    +B1543264554438N00629431EA0233202265000
    +B1543294554436N00629416EA0234002271000
    +B1543324554422N00629407EA0234602277000
    +B1543354554407N00629407EA0235202283000
    +B1543384554394N00629418EA0235702289000
    +B1543414554387N00629438EA0235902295000
    +B1543444554392N00629460EA0236302301000
    +B1543474554405N00629469EA0237002307000
    +B1543504554415N00629460EA0237502313000
    +B1543534554411N00629444EA0238302319000
    +B1543564554399N00629441EA0238602325000
    +B1543594554388N00629455EA0238802330000
    +B1544024554383N00629477EA0239202336000
    +B1544054554387N00629498EA0239502341000
    +B1544084554399N00629510EA0239902346000
    +B1544114554409N00629504EA0240402351000
    +B1544144554411N00629490EA0241002355000
    +B1544184554397N00629476EA0241502362000
    +B1544214554383N00629482EA0241602366000
    +B1544244554371N00629498EA0241702371000
    +B1544274554373N00629521EA0241702374000
    +B1544304554384N00629530EA0242302378000
    +B1544334554393N00629522EA0242302382000
    +B1544364554400N00629506EA0242802385000
    +B1544394554401N00629492EA0242702388000
    +B1544424554395N00629474EA0243002391000
    +B1544454554381N00629470EA0243002394000
    +B1544484554366N00629480EA0242902396000
    +B1544514554359N00629503EA0242802398000
    +B1544544554364N00629526EA0242702400000
    +B1544574554376N00629540EA0243102402000
    +B1545004554388N00629547EA0243102404000
    +B1545034554403N00629548EA0243202406000
    +B1545064554415N00629539EA0243302408000
    +B1545094554419N00629523EA0243502410000
    +B1545124554418N00629506EA0243902412000
    +B1545154554409N00629492EA0244402414000
    +B1545184554395N00629488EA0244402416000
    +B1545214554380N00629491EA0244102418000
    +B1545244554367N00629506EA0244102420000
    +B1545274554364N00629530EA0243902421000
    +B1545304554373N00629550EA0243602422000
    +B1545334554388N00629552EA0243602422000
    +B1545364554398N00629538EA0243702423000
    +B1545404554398N00629513EA0243902424000
    +B1545434554393N00629497EA0244002424000
    +B1545464554386N00629484EA0243902425000
    +B1545494554375N00629473EA0243702426000
    +B1545524554359N00629473EA0243402426000
    +B1545554554344N00629485EA0243402426000
    +B1545584554335N00629506EA0243102426000
    +B1546014554335N00629532EA0242802425000
    +B1546044554344N00629554EA0243002424000
    +B1546074554358N00629571EA0243802424000
    +B1546104554370N00629584EA0244402424000
    +B1546134554382N00629596EA0244702425000
    +B1546164554396N00629598EA0244902426000
    +B1546194554406N00629587EA0245702428000
    +B1546224554410N00629572EA0246002430000
    +B1546254554406N00629558EA0245902432000
    +B1546284554392N00629556EA0245902433000
    +B1546314554379N00629571EA0246102435000
    +B1546344554374N00629596EA0246902437000
    +B1546374554378N00629618EA0247402439000
    +B1546404554390N00629632EA0247802442000
    +B1546434554404N00629631EA0248402445000
    +B1546464554410N00629619EA0248802447000
    +B1546494554405N00629607EA0248702449000
    +B1546524554393N00629600EA0248702452000
    +B1546554554378N00629607EA0248902455000
    +B1546584554366N00629626EA0249502458000
    +B1547024554366N00629662EA0250102462000
    +B1547054554376N00629684EA0250602465000
    +B1547084554388N00629694EA0250702469000
    +B1547114554401N00629695EA0250702472000
    +B1547144554409N00629683EA0250702474000
    +B1547174554407N00629667EA0250902477000
    +B1547204554397N00629659EA0250902479000
    +B1547234554382N00629659EA0250802481000
    +B1547264554366N00629668EA0251402483000
    +B1547294554354N00629683EA0252202485000
    +B1547324554343N00629695EA0252402488000
    +B1547354554330N00629698EA0252702491000
    +B1547384554319N00629689EA0253202493000
    +B1547414554318N00629672EA0254302497000
    +B1547444554322N00629658EA0255102501000
    +B1547474554331N00629648EA0255302505000
    +B1547504554348N00629651EA0255402509000
    +B1547534554359N00629666EA0255202513000
    +B1547564554364N00629693EA0255002516000
    +B1547594554355N00629716EA0255202518000
    +B1548024554340N00629721EA0255702521000
    +B1548054554330N00629710EA0256602524000
    +B1548084554324N00629699EA0257202527000
    +B1548114554322N00629686EA0257302530000
    +B1548144554322N00629670EA0257802534000
    +B1548174554331N00629658EA0257302537000
    +B1548204554346N00629655EA0257002540000
    +B1548234554359N00629671EA0256702542000
    +B1548274554357N00629705EA0256502544000
    +B1548304554343N00629717EA0256702546000
    +B1548334554328N00629715EA0257302547000
    +B1548364554315N00629708EA0257602549000
    +B1548394554305N00629696EA0257902551000
    +B1548424554298N00629683EA0258502553000
    +B1548454554295N00629670EA0259402555000
    +B1548484554293N00629657EA0260302559000
    +B1548514554293N00629643EA0260602562000
    +B1548544554301N00629632EA0260802566000
    +B1548574554314N00629637EA0260802570000
    +B1549004554325N00629655EA0260902573000
    +B1549034554327N00629679EA0260702576000
    +B1549064554320N00629705EA0261302579000
    +B1549094554308N00629720EA0262102582000
    +B1549124554293N00629725EA0262502586000
    +B1549154554278N00629718EA0263202590000
    +B1549184554270N00629705EA0264102594000
    +B1549214554266N00629687EA0264902599000
    +B1549244554269N00629673EA0265702604000
    +B1549274554279N00629667EA0265802609000
    +B1549304554293N00629672EA0265902613000
    +B1549334554300N00629691EA0266002618000
    +B1549364554302N00629719EA0266802622000
    +B1549394554295N00629741EA0267502626000
    +B1549424554284N00629760EA0268002631000
    +B1549454554269N00629771EA0268902636000
    +B1549484554253N00629779EA0269602641000
    +B1549514554238N00629777EA0270202646000
    +B1549544554228N00629766EA0270502651000
    +B1549574554226N00629750EA0271102656000
    +B1550004554226N00629737EA0271502660000
    +B1550034554232N00629727EA0272102665000
    +B1550074554247N00629736EA0272802673000
    +B1550104554256N00629757EA0273502678000
    +B1550134554251N00629787EA0274102684000
    +B1550164554241N00629809EA0274902689000
    +B1550194554230N00629831EA0275002695000
    +B1550224554218N00629855EA0275502700000
    +B1550254554209N00629876EA0275702705000
    +B1550284554201N00629898EA0276202710000
    +B1550314554193N00629918EA0275802715000
    +B1550344554183N00629941EA0275902719000
    +B1550374554174N00629963EA0276202723000
    +B1550404554165N00629985EA0276102726000
    +B1550434554154N00630006EA0276502729000
    +B1550464554144N00630025EA0276702732000
    +B1550494554134N00630049EA0276902735000
    +B1550524554126N00630073EA0276902738000
    +B1550554554117N00630095EA0276802741000
    +B1550584554108N00630117EA0276302743000
    +B1551014554099N00630139EA0276302744000
    +B1551044554090N00630159EA0276002745000
    +B1551074554078N00630181EA0275602745000
    +B1551104554067N00630202EA0276102746000
    +B1551134554059N00630220EA0275402746000
    +B1551164554052N00630244EA0274802745000
    +B1551194554046N00630270EA0274202744000
    +B1551224554039N00630295EA0273402743000
    +B1551264554031N00630328EA0272902740000
    +B1551294554024N00630352EA0271802737000
    +B1551324554016N00630377EA0271202733000
    +B1551354554006N00630404EA0270102729000
    +B1551384553997N00630434EA0269602724000
    +B1551414553989N00630462EA0269002720000
    +B1551444553981N00630489EA0268502715000
    +B1551474553975N00630517EA0268002710000
    +B1551504553971N00630544EA0267502706000
    +B1551534553965N00630572EA0267102701000
    +B1551564553959N00630599EA0267202696000
    +B1551594553953N00630621EA0267002692000
    +B1552024553949N00630644EA0266502688000
    +B1552054553943N00630668EA0266002684000
    +B1552084553938N00630693EA0265502680000
    +B1552114553934N00630718EA0265502676000
    +B1552144553929N00630740EA0265002672000
    +B1552174553923N00630765EA0265102668000
    +B1552204553917N00630785EA0265402665000
    +B1552234553912N00630805EA0265302662000
    +B1552264553907N00630828EA0265402660000
    +B1552294553901N00630850EA0265602658000
    +B1552324553896N00630872EA0265802656000
    +B1552354553892N00630895EA0265802655000
    +B1552384553888N00630919EA0266202654000
    +B1552414553883N00630941EA0266402654000
    +B1552454553875N00630971EA0266502654000
    +B1552484553870N00630995EA0266702654000
    +B1552514553866N00631019EA0267002655000
    +B1552544553861N00631042EA0267202656000
    +B1552574553856N00631066EA0267602657000
    +B1553004553848N00631089EA0268502658000
    +B1553034553841N00631103EA0269102661000
    +B1553064553835N00631118EA0269502663000
    +B1553094553826N00631128EA0269802666000
    +B1553124553815N00631128EA0270102668000
    +B1553154553806N00631116EA0270502671000
    +B1553184553805N00631098EA0270602674000
    +B1553214553812N00631080EA0271102676000
    +B1553244553824N00631068EA0271602679000
    +B1553274553838N00631064EA0271902682000
    +B1553304553851N00631072EA0272402685000
    +B1553334553858N00631089EA0272902689000
    +B1553364553859N00631106EA0273402692000
    +B1553394553856N00631123EA0273702696000
    +B1553424553848N00631134EA0273702699000
    +B1553454553835N00631132EA0273902703000
    +B1553484553825N00631122EA0274302706000
    +B1553514553821N00631105EA0274902710000
    +B1553544553822N00631087EA0275202714000
    +B1553574553831N00631069EA0275602717000
    +B1554004553845N00631063EA0276502721000
    +B1554034553856N00631071EA0276802725000
    +B1554064553864N00631089EA0277502729000
    +B1554094553863N00631108EA0278002734000
    +B1554134553852N00631122EA0278002740000
    +B1554164553840N00631118EA0278302744000
    +B1554194553834N00631103EA0278502747000
    +B1554224553834N00631083EA0278702751000
    +B1554254553842N00631066EA0278802754000
    +B1554284553855N00631054EA0279302758000
    +B1554314553869N00631057EA0279502761000
    +B1554344553878N00631076EA0279902764000
    +B1554374553879N00631095EA0280502768000
    +B1554404553875N00631110EA0280202771000
    +B1554434553869N00631130EA0280202773000
    +B1554464553865N00631151EA0280402776000
    +B1554494553861N00631173EA0280302778000
    +B1554524553857N00631196EA0280502780000
    +B1554554553853N00631216EA0280302781000
    +B1554584553848N00631239EA0280302783000
    +B1555014553844N00631261EA0280102784000
    +B1555044553841N00631286EA0280302785000
    +B1555074553839N00631312EA0280402786000
    +B1555104553837N00631340EA0280502787000
    +B1555134553837N00631366EA0280702789000
    +B1555164553837N00631391EA0280902790000
    +B1555194553836N00631414EA0281302791000
    +B1555224553833N00631438EA0281602793000
    +B1555254553833N00631460EA0281802795000
    +B1555284553830N00631483EA0281902796000
    +B1555314553827N00631507EA0282102798000
    +B1555354553820N00631539EA0282202800000
    +B1555384553817N00631564EA0282702802000
    +B1555414553814N00631585EA0282502803000
    +B1555444553811N00631609EA0282202805000
    +B1555474553808N00631632EA0281702806000
    +B1555504553805N00631657EA0281202806000
    +B1555534553803N00631681EA0280702805000
    +B1555564553801N00631705EA0280302804000
    +B1555594553796N00631727EA0279502803000
    +B1556024553792N00631751EA0279002801000
    +B1556054553787N00631776EA0278602799000
    +B1556084553781N00631798EA0278202796000
    +B1556114553777N00631822EA0277702794000
    +B1556144553773N00631845EA0277502791000
    +B1556174553768N00631868EA0276902789000
    +B1556204553764N00631892EA0276602786000
    +B1556234553760N00631915EA0276102783000
    +B1556264553756N00631938EA0275802780000
    +B1556294553752N00631962EA0275802776000
    +B1556324553747N00631985EA0275402773000
    +B1556354553743N00632008EA0275402770000
    +B1556384553739N00632031EA0275202767000
    +B1556414553735N00632055EA0275002765000
    +B1556444553731N00632076EA0274602762000
    +B1556474553726N00632101EA0274502759000
    +B1556504553722N00632126EA0274902757000
    +B1556534553720N00632150EA0275102755000
    +B1556574553717N00632186EA0275702753000
    +B1557004553716N00632212EA0276402752000
    +B1557034553713N00632235EA0277002753000
    +B1557064553706N00632255EA0277502754000
    +B1557094553696N00632272EA0277902755000
    +B1557124553682N00632276EA0277902757000
    +B1557154553669N00632266EA0278002758000
    +B1557184553666N00632247EA0278102759000
    +B1557214553673N00632228EA0278502761000
    +B1557244553687N00632218EA0278502763000
    +B1557274553703N00632217EA0278802765000
    +B1557304553717N00632229EA0279202767000
    +B1557334553725N00632249EA0279502769000
    +B1557364553725N00632272EA0279902771000
    +B1557394553717N00632288EA0280202774000
    +B1557424553704N00632296EA0280702776000
    +B1557454553691N00632296EA0280602779000
    +B1557484553680N00632284EA0280602781000
    +B1557514553678N00632263EA0280502782000
    +B1557544553686N00632245EA0280402783000
    +B1557574553702N00632245EA0280202784000
    +B1558004553716N00632265EA0280702785000
    +B1558034553720N00632287EA0281002786000
    +B1558064553717N00632309EA0281302788000
    +B1558094553711N00632326EA0281502789000
    +B1558124553705N00632345EA0281802791000
    +B1558154553699N00632366EA0282302792000
    +B1558184553694N00632387EA0283102795000
    +B1558224553690N00632416EA0283902799000
    +B1558254553691N00632439EA0284702803000
    +B1558284553701N00632459EA0285402807000
    +B1558314553719N00632464EA0286302811000
    +B1558344553734N00632454EA0287002816000
    +B1558374553739N00632438EA0287702822000
    +B1558404553731N00632427EA0288402827000
    +B1558434553719N00632433EA0288902833000
    +B1558464553710N00632448EA0289702839000
    +B1558494553707N00632469EA0290502845000
    +B1558524553706N00632492EA0291002851000
    +B1558554553706N00632514EA0290902857000
    +B1558584553706N00632537EA0290802862000
    +B1559014553706N00632560EA0291002867000
    +B1559044553705N00632583EA0290802871000
    +B1559074553707N00632606EA0291002874000
    +B1559104553710N00632627EA0291002877000
    +B1559134553712N00632652EA0291202880000
    +B1559164553717N00632678EA0291502883000
    +B1559194553722N00632703EA0291002886000
    +B1559224553726N00632733EA0290502888000
    +B1559254553727N00632760EA0289902889000
    +B1559284553725N00632786EA0288902889000
    +B1559314553724N00632816EA0288402888000
    +B1559344553721N00632844EA0287702886000
    +B1559374553717N00632873EA0287102884000
    +B1559404553712N00632902EA0286502881000
    +B1559434553708N00632932EA0285902878000
    +B1559464553704N00632964EA0285902875000
    +B1559494553700N00632992EA0285602872000
    +B1559534553692N00633029EA0284702868000
    +B1559564553686N00633057EA0284202864000
    +B1559594553679N00633085EA0283902861000
    +B1600024553673N00633113EA0283602858000
    +B1600054553668N00633141EA0283202854000
    +B1600084553664N00633170EA0283002851000
    +B1600114553660N00633201EA0282602847000
    +B1600144553656N00633231EA0282502844000
    +B1600174553652N00633260EA0282402841000
    +B1600204553647N00633290EA0282202838000
    +B1600234553643N00633319EA0282102836000
    +B1600264553637N00633347EA0281502833000
    +B1600294553631N00633376EA0281302830000
    +B1600324553624N00633404EA0280702827000
    +B1600354553618N00633433EA0280502824000
    +B1600384553614N00633459EA0280302821000
    +B1600414553612N00633484EA0279702818000
    +B1600444553610N00633511EA0279502815000
    +B1600474553609N00633539EA0279502812000
    +B1600504553609N00633567EA0279902809000
    +B1600534553608N00633591EA0279702807000
    +B1600564553606N00633618EA0279202804000
    +B1600594553605N00633646EA0278902802000
    +B1601024553605N00633673EA0278402799000
    +B1601054553604N00633700EA0278002796000
    +B1601094553603N00633737EA0277602792000
    +B1601124553602N00633765EA0277302789000
    +B1601154553601N00633792EA0277102786000
    +B1601184553601N00633821EA0276802783000
    +B1601214553600N00633850EA0276802781000
    +B1601244553599N00633880EA0276602778000
    +B1601274553599N00633911EA0276502775000
    +B1601304553599N00633940EA0276402773000
    +B1601334553598N00633968EA0275802771000
    +B1601364553598N00634000EA0275302768000
    +B1601394553598N00634032EA0275302765000
    +B1601424553599N00634061EA0274802762000
    +B1601454553600N00634093EA0274502759000
    +B1601484553602N00634122EA0274202756000
    +B1601514553604N00634151EA0274002753000
    +B1601544553607N00634180EA0273802750000
    +B1601574553609N00634209EA0273502748000
    +B1602004553611N00634238EA0273302745000
    +B1602034553614N00634266EA0273202742000
    +B1602064553617N00634294EA0273002739000
    +B1602094553620N00634321EA0272802737000
    +B1602124553624N00634348EA0272502734000
    +B1602154553626N00634376EA0272302732000
    +B1602184553628N00634403EA0272002729000
    +B1602214553628N00634431EA0271802727000
    +B1602244553629N00634457EA0271402723000
    +B1602284553627N00634495EA0271402721000
    +B1602314553625N00634521EA0271302719000
    +B1602344553622N00634548EA0270802716000
    +B1602374553618N00634575EA0270702714000
    +B1602404553616N00634601EA0270402711000
    +B1602434553614N00634629EA0270102709000
    +B1602464553612N00634657EA0269802706000
    +B1602494553611N00634685EA0269502704000
    +B1602524553609N00634713EA0269202701000
    +B1602554553608N00634742EA0269002698000
    +B1602584553608N00634771EA0268802696000
    +B1603014553608N00634800EA0268402693000
    +B1603044553609N00634829EA0268002691000
    +B1603074553609N00634858EA0267802688000
    +B1603104553610N00634888EA0267402686000
    +B1603134553610N00634915EA0267102683000
    +B1603164553610N00634943EA0266602680000
    +B1603194553610N00634972EA0266302677000
    +B1603224553611N00635001EA0265902674000
    +B1603254553611N00635030EA0265502671000
    +B1603284553611N00635059EA0265102667000
    +B1603314553610N00635088EA0264702664000
    +B1603344553610N00635117EA0264202660000
    +B1603374553610N00635146EA0263702657000
    +B1603404553610N00635175EA0263302653000
    +B1603444553607N00635213EA0263002648000
    +B1603474553605N00635243EA0262602644000
    +B1603504553602N00635270EA0262202640000
    +B1603534553599N00635299EA0261802637000
    +B1603564553597N00635327EA0261602633000
    +B1603594553595N00635354EA0261202629000
    +B1604024553593N00635382EA0260802625000
    +B1604054553590N00635409EA0260302621000
    +B1604084553588N00635437EA0260002618000
    +B1604114553586N00635465EA0259702614000
    +B1604144553584N00635494EA0259402610000
    +B1604174553583N00635522EA0259102607000
    +B1604204553583N00635550EA0258702603000
    +B1604234553581N00635578EA0258402599000
    +B1604264553579N00635607EA0258102596000
    +B1604294553577N00635636EA0257702592000
    +B1604324553576N00635665EA0257402589000
    +B1604354553575N00635693EA0257202585000
    +B1604384553574N00635722EA0257002582000
    +B1604414553574N00635751EA0256702579000
    +B1604444553573N00635779EA0256402576000
    +B1604474553573N00635808EA0256102573000
    +B1604504553573N00635837EA0256002570000
    +B1604534553572N00635866EA0255702567000
    +B1604564553570N00635894EA0255602564000
    +B1605004553567N00635933EA0255502561000
    +B1605034553565N00635962EA0255102558000
    +B1605064553562N00635991EA0254902556000
    +B1605094553560N00636019EA0254802553000
    +B1605124553557N00636049EA0254502551000
    +B1605154553554N00636078EA0254502549000
    +B1605184553551N00636107EA0254402547000
    +B1605214553548N00636136EA0254202545000
    +B1605244553546N00636164EA0253802543000
    +B1605274553545N00636194EA0253702541000
    +B1605304553544N00636223EA0253502538000
    +B1605334553545N00636251EA0253202536000
    +B1605364553545N00636280EA0252802534000
    +B1605394553546N00636309EA0252502532000
    +B1605424553546N00636337EA0252302530000
    +B1605454553545N00636365EA0252002528000
    +B1605484553545N00636393EA0251702525000
    +B1605514553545N00636422EA0251502523000
    +B1605544553546N00636450EA0251302520000
    +B1605574553546N00636479EA0251102518000
    +B1606004553545N00636508EA0251002516000
    +B1606034553543N00636537EA0250802513000
    +B1606064553542N00636565EA0250702511000
    +B1606094553541N00636593EA0250502509000
    +B1606124553539N00636619EA0250202507000
    +B1606154553538N00636647EA0250102505000
    +B1606184553537N00636673EA0249802502000
    +B1606214553536N00636701EA0249602500000
    +B1606244553535N00636727EA0249302498000
    +B1606284553533N00636763EA0249002495000
    +B1606314553531N00636790EA0248802493000
    +B1606344553528N00636817EA0248602491000
    +B1606374553525N00636844EA0248502489000
    +B1606404553522N00636870EA0248302486000
    +B1606434553520N00636897EA0248202484000
    +B1606464553518N00636923EA0248002483000
    +B1606494553515N00636950EA0247802481000
    +B1606524553513N00636977EA0247702479000
    +B1606554553511N00637003EA0247502477000
    +B1606584553510N00637030EA0247402475000
    +B1607014553508N00637057EA0247202474000
    +B1607044553507N00637084EA0247002472000
    +B1607074553507N00637110EA0246802470000
    +B1607104553507N00637137EA0246602469000
    +B1607134553507N00637164EA0246402467000
    +B1607164553507N00637191EA0246202465000
    +B1607194553507N00637217EA0246002463000
    +B1607224553507N00637244EA0245802462000
    +B1607254553507N00637270EA0245602460000
    +B1607284553507N00637297EA0245402459000
    +B1607314553506N00637324EA0245302457000
    +B1607344553505N00637351EA0245002455000
    +B1607374553505N00637378EA0244902453000
    +B1607404553505N00637404EA0244702452000
    +B1607434553505N00637431EA0244402450000
    +B1607474553505N00637467EA0244202447000
    +B1607504553506N00637494EA0243902445000
    +B1607534553507N00637521EA0243602443000
    +B1607564553508N00637547EA0243302441000
    +B1607594553508N00637574EA0242902439000
    +B1608024553508N00637601EA0242602436000
    +B1608054553507N00637628EA0242202433000
    +B1608084553504N00637656EA0241902431000
    +B1608114553500N00637682EA0241702428000
    +B1608144553496N00637709EA0241302425000
    +B1608174553492N00637735EA0241102422000
    +B1608204553487N00637762EA0240702419000
    +B1608234553483N00637788EA0240402417000
    +B1608264553478N00637815EA0240002414000
    +B1608294553473N00637842EA0239802411000
    +B1608324553469N00637869EA0239502408000
    +B1608354553464N00637896EA0239202405000
    +B1608384553460N00637923EA0238902402000
    +B1608414553456N00637950EA0238602399000
    +B1608444553453N00637977EA0238202396000
    +B1608474553450N00638004EA0237902393000
    +B1608504553448N00638031EA0237602390000
    +B1608534553445N00638058EA0237302387000
    +B1608564553443N00638085EA0236802384000
    +B1608594553441N00638112EA0236502381000
    +B1609024553439N00638140EA0236102377000
    +B1609064553436N00638176EA0235702373000
    +B1609094553435N00638203EA0235202369000
    +B1609124553433N00638231EA0234802366000
    +B1609154553432N00638258EA0234402362000
    +B1609184553431N00638286EA0234002358000
    +B1609214553430N00638313EA0233702355000
    +B1609244553429N00638341EA0233202351000
    +B1609274553428N00638369EA0232902347000
    +B1609304553427N00638396EA0232502344000
    +B1609334553425N00638424EA0232202340000
    +B1609364553424N00638451EA0231902336000
    +B1609394553424N00638479EA0231502333000
    +B1609424553424N00638507EA0231202330000
    +B1609454553425N00638535EA0230902326000
    +B1609484553426N00638563EA0230502323000
    +B1609514553426N00638591EA0230302321000
    +B1609544553427N00638619EA0230002318000
    +B1609574553427N00638648EA0229802315000
    +B1610004553427N00638676EA0229502313000
    +B1610034553427N00638704EA0229202310000
    +B1610064553427N00638732EA0229102307000
    +B1610094553427N00638759EA0228902305000
    +B1610124553427N00638787EA0228802303000
    +B1610154553428N00638815EA0228502300000
    +B1610184553428N00638843EA0228302298000
    +B1610214553428N00638871EA0228202296000
    +B1610244553428N00638899EA0228002293000
    +B1610284553428N00638936EA0227802290000
    +B1610314553428N00638964EA0227502288000
    +B1610344553429N00638993EA0227302286000
    +B1610374553428N00639021EA0227102284000
    +B1610404553428N00639049EA0226902282000
    +B1610434553427N00639077EA0226702280000
    +B1610464553427N00639105EA0226302278000
    +B1610494553427N00639133EA0226102275000
    +B1610524553427N00639160EA0225702273000
    +B1610554553427N00639189EA0225502271000
    +B1610584553426N00639216EA0225202268000
    +B1611014553425N00639243EA0225002266000
    +B1611044553424N00639271EA0224702263000
    +B1611074553424N00639298EA0224402261000
    +B1611104553423N00639326EA0224202258000
    +B1611134553422N00639353EA0223802256000
    +B1611164553422N00639381EA0223602253000
    +B1611194553421N00639409EA0223302251000
    +B1611224553421N00639436EA0223102248000
    +B1611254553420N00639464EA0222802245000
    +B1611284553420N00639492EA0222602243000
    +B1611314553420N00639519EA0222302240000
    +B1611344553422N00639546EA0222002238000
    +B1611374553423N00639573EA0221602235000
    +B1611404553425N00639600EA0221202232000
    +B1611434553426N00639627EA0220902229000
    +B1611464553427N00639654EA0220602227000
    +B1611504553426N00639690EA0220402223000
    +B1611534553425N00639718EA0220102220000
    +B1611564553424N00639745EA0219902217000
    +B1611594553423N00639773EA0219902214000
    +B1612024553423N00639800EA0219802211000
    +B1612054553421N00639827EA0219702209000
    +B1612084553419N00639854EA0219702206000
    +B1612114553417N00639882EA0219702204000
    +B1612144553415N00639910EA0219502202000
    +B1612174553413N00639938EA0219502200000
    +B1612204553412N00639964EA0219502198000
    +B1612234553411N00639991EA0219102196000
    +B1612264553409N00640019EA0218902194000
    +B1612294553407N00640047EA0218702192000
    +B1612324553405N00640075EA0218502190000
    +B1612354553404N00640103EA0218402188000
    +B1612384553403N00640132EA0218302186000
    +B1612414553403N00640161EA0218102184000
    +B1612444553404N00640190EA0218002182000
    +B1612474553405N00640218EA0218102181000
    +B1612504553405N00640246EA0218002179000
    +B1612534553404N00640275EA0218102178000
    +B1612564553403N00640303EA0217902177000
    +B1612594553400N00640333EA0217802175000
    +B1613024553398N00640362EA0217902174000
    +B1613054553397N00640390EA0217602173000
    +B1613084553397N00640420EA0217702172000
    +B1613114553398N00640450EA0217602171000
    +B1613144553398N00640480EA0217702169000
    +B1613184553397N00640519EA0217702168000
    +B1613214553397N00640548EA0217502167000
    +B1613244553395N00640578EA0217202166000
    +B1613274553393N00640607EA0217102164000
    +B1613304553389N00640637EA0217002163000
    +B1613334553385N00640669EA0217102162000
    +B1613364553381N00640699EA0216902161000
    +B1613394553380N00640730EA0216402159000
    +B1613424553378N00640762EA0216102158000
    +B1613454553379N00640794EA0215602156000
    +B1613484553379N00640825EA0215002154000
    +B1613514553378N00640856EA0214502151000
    +B1613544553377N00640887EA0214502148000
    +B1613574553377N00640914EA0214202146000
    +B1614004553377N00640943EA0213802143000
    +B1614034553377N00640975EA0213702140000
    +B1614064553377N00641006EA0213702138000
    +B1614094553376N00641036EA0213302136000
    +B1614124553374N00641067EA0213102133000
    +B1614154553373N00641098EA0212702130000
    +B1614184553371N00641129EA0212402128000
    +B1614214553371N00641158EA0212102125000
    +B1614244553370N00641186EA0211702122000
    +B1614274553371N00641215EA0211202119000
    +B1614304553372N00641243EA0210502116000
    +B1614334553375N00641272EA0209902113000
    +B1614364553378N00641300EA0209602109000
    +B1614394553381N00641327EA0209002105000
    +B1614424553384N00641355EA0208802101000
    +B1614454553387N00641382EA0208302097000
    +B1614494553392N00641420EA0207902092000
    +B1614524553397N00641446EA0207502088000
    +B1614554553400N00641472EA0207002084000
    +B1614584553402N00641499EA0206802080000
    +B1615014553404N00641523EA0206202077000
    +B1615044553405N00641550EA0205802073000
    +B1615074553407N00641576EA0205302069000
    +B1615104553408N00641602EA0205002065000
    +B1615134553409N00641629EA0204702061000
    +B1615164553409N00641657EA0204802057000
    +B1615194553412N00641684EA0204802054000
    +B1615224553416N00641711EA0204502051000
    +B1615254553419N00641738EA0204202048000
    +B1615284553422N00641766EA0204102045000
    +B1615314553425N00641792EA0203902043000
    +B1615344553429N00641821EA0204002040000
    +B1615374553433N00641846EA0204002038000
    +B1615404553437N00641872EA0204002036000
    +B1615434553440N00641898EA0204102034000
    +B1615464553444N00641924EA0204002033000
    +B1615494553448N00641949EA0203802032000
    +B1615524553453N00641975EA0203602031000
    +B1615554553458N00642000EA0203502029000
    +B1615584553462N00642025EA0203302028000
    +B1616014553465N00642052EA0203302028000
    +B1616044553469N00642077EA0203302027000
    +B1616074553471N00642104EA0203202026000
    +B1616104553473N00642131EA0203102025000
    +B1616134553476N00642157EA0202802024000
    +B1616164553481N00642185EA0202602024000
    +B1616194553484N00642212EA0202402023000
    +B1616234553488N00642247EA0202002021000
    +B1616264553492N00642274EA0201802020000
    +B1616294553497N00642300EA0201502018000
    +B1616324553500N00642327EA0201402016000
    +B1616354553503N00642355EA0201302015000
    +B1616384553505N00642383EA0201402013000
    +B1616414553505N00642411EA0201402011000
    +B1616444553506N00642438EA0201702010000
    +B1616474553507N00642465EA0202102009000
    +B1616504553507N00642489EA0202102008000
    +B1616534553507N00642514EA0202002008000
    +B1616564553507N00642539EA0201902008000
    +B1616594553506N00642564EA0201602007000
    +B1617024553506N00642588EA0201602006000
    +B1617054553505N00642614EA0201402006000
    +B1617084553504N00642638EA0201202005000
    +B1617114553503N00642664EA0201102004000
    +B1617144553503N00642689EA0201102003000
    +B1617174553502N00642713EA0200602002000
    +B1617204553502N00642739EA0200602000000
    +B1617234553502N00642764EA0200301999000
    +B1617264553501N00642788EA0200301998000
    +B1617294553500N00642813EA0200101996000
    +B1617324553500N00642837EA0200001995000
    +B1617364553499N00642868EA0199701993000
    +B1617394553499N00642891EA0199501992000
    +B1617424553498N00642914EA0199301990000
    +B1617454553497N00642937EA0199001988000
    +B1617484553495N00642960EA0198601986000
    +B1617514553493N00642984EA0198301984000
    +B1617544553490N00643008EA0198101982000
    +B1617574553488N00643032EA0197701979000
    +B1618004553484N00643057EA0197201977000
    +B1618034553481N00643084EA0197301974000
    +B1618064553476N00643109EA0197501972000
    +B1618094553472N00643133EA0197401970000
    +B1618124553469N00643158EA0197501968000
    +B1618154553467N00643183EA0197201966000
    +B1618184553464N00643209EA0197301964000
    +B1618214553462N00643234EA0197301963000
    +B1618244553459N00643260EA0197401961000
    +B1618274553456N00643285EA0197501960000
    +B1618304553452N00643311EA0197501959000
    +B1618334553449N00643338EA0197801958000
    +B1618364553445N00643363EA0197601958000
    +B1618394553442N00643390EA0197901957000
    +B1618424553437N00643414EA0197801957000
    +B1618454553433N00643440EA0197901957000
    +B1618484553428N00643465EA0197901958000
    +B1618514553423N00643490EA0197801958000
    +B1618554553416N00643522EA0197701959000
    +B1618584553410N00643547EA0197801959000
    +B1619014553403N00643571EA0197801960000
    +B1619044553396N00643595EA0197901960000
    +B1619074553388N00643619EA0198001961000
    +B1619104553380N00643642EA0197501961000
    +B1619134553373N00643666EA0197201961000
    +B1619164553365N00643690EA0196701960000
    +B1619194553358N00643715EA0196301959000
    +B1619224553352N00643741EA0195801958000
    +B1619254553344N00643768EA0195501956000
    +B1619284553335N00643793EA0195001954000
    +B1619314553326N00643818EA0194601951000
    +B1619344553316N00643843EA0194201949000
    +B1619374553305N00643868EA0193901947000
    +B1619404553294N00643892EA0193601944000
    +B1619434553284N00643917EA0193101942000
    +B1619464553273N00643942EA0192801939000
    +B1619494553262N00643968EA0192301936000
    +B1619524553252N00643997EA0192301933000
    +B1619554553243N00644028EA0192201930000
    +B1619584553236N00644060EA0192201927000
    +B1620014553232N00644092EA0192101924000
    +B1620044553229N00644125EA0191801922000
    +B1620074553229N00644157EA0191501919000
    +B1620104553231N00644190EA0191501916000
    +B1620144553238N00644230EA0191601913000
    +B1620174553243N00644258EA0191601911000
    +B1620204553250N00644285EA0191701909000
    +B1620234553259N00644311EA0191901908000
    +B1620264553269N00644336EA0192101907000
    +B1620294553280N00644361EA0192401906000
    +B1620324553291N00644383EA0192501906000
    +B1620354553302N00644404EA0192401905000
    +B1620384553314N00644422EA0192301905000
    +B1620414553326N00644433EA0192001905000
    +B1620444553339N00644437EA0191901905000
    +B1620474553349N00644434EA0191701904000
    +B1620504553353N00644422EA0191401904000
    +B1620534553347N00644412EA0191301903000
    +B1620564553333N00644408EA0191301902000
    +B1620594553317N00644412EA0191401901000
    +B1621024553300N00644417EA0191001901000
    +B1621054553282N00644421EA0191201901000
    +B1621084553263N00644425EA0191501901000
    +B1621114553245N00644429EA0191701901000
    +B1621144553228N00644436EA0191701901000
    +B1621174553211N00644442EA0191701901000
    +B1621204553196N00644444EA0191701901000
    +B1621234553183N00644446EA0191401901000
    +B1621264553167N00644446EA0191401901000
    +B1621294553152N00644449EA0191401900000
    +B1621334553131N00644450EA0191501900000
    +B1621364553116N00644450EA0191801900000
    +B1621394553103N00644449EA0191901900000
    +B1621424553090N00644448EA0192001900000
    +B1621454553077N00644448EA0192101900000
    +B1621484553064N00644449EA0192101901000
    +B1621514553051N00644451EA0191901901000
    +B1621544553036N00644455EA0191601901000
    +B1621574553021N00644458EA0191601901000
    +B1622004553008N00644460EA0191201901000
    +B1622034552994N00644461EA0191101900000
    +B1622064552978N00644462EA0191101900000
    +B1622094552964N00644464EA0191201899000
    +B1622124552949N00644465EA0191001898000
    +B1622154552936N00644462EA0190601898000
    +B1622184552921N00644459EA0190201897000
    +B1622214552904N00644459EA0190501895000
    +B1622244552888N00644460EA0190801895000
    +B1622274552873N00644459EA0191301894000
    +B1622304552855N00644460EA0192101894000
    +B1622334552839N00644464EA0192601895000
    +B1622364552823N00644466EA0192701896000
    +B1622394552813N00644460EA0192701897000
    +B1622424552812N00644451EA0192601898000
    +B1622454552817N00644447EA0192501898000
    +B1622484552827N00644450EA0192301899000
    +B1622514552841N00644460EA0192601900000
    +B1622554552859N00644479EA0193501902000
    +B1622584552870N00644492EA0193901904000
    +B1623014552879N00644492EA0194501905000
    +B1623044552874N00644483EA0195301907000
    +B1623074552858N00644487EA0195701910000
    +B1623104552841N00644509EA0195901913000
    +B1623134552834N00644543EA0196401916000
    +B1623164552843N00644571EA0196801920000
    +B1623194552855N00644582EA0197501923000
    +B1623224552862N00644579EA0198001925000
    +B1623254552857N00644571EA0198901928000
    +B1623284552844N00644575EA0199101933000
    +B1623314552827N00644591EA0199401938000
    +B1623344552809N00644607EA0199201942000
    +B1623374552793N00644609EA0199401946000
    +B1623404552781N00644606EA0199101950000
    +B1623434552770N00644598EA0198901954000
    +B1623464552760N00644590EA0197901956000
    +B1623494552748N00644577EA0197901957000
    +B1623524552736N00644568EA0197301958000
    +B1623554552722N00644557EA0197101959000
    +B1623584552708N00644549EA0197101959000
    +B1624014552693N00644542EA0196601959000
    +B1624044552679N00644530EA0197301958000
    +B1624074552662N00644528EA0197801958000
    +B1624104552644N00644528EA0198901959000
    +B1624134552633N00644525EA0198701959000
    +B1624164552630N00644519EA0199001960000
    +B1624194552635N00644520EA0198701960000
    +B1624234552644N00644541EA0198501961000
    +B1624264552639N00644572EA0198101962000
    +B1624294552622N00644598EA0198001962000
    +B1624324552600N00644615EA0198001962000
    +B1624354552581N00644621EA0198001962000
    +B1624384552568N00644617EA0198101963000
    +B1624414552566N00644610EA0198401963000
    +B1624444552572N00644610EA0197901963000
    +B1624474552580N00644609EA0197601963000
    +B1624504552589N00644608EA0197501964000
    +B1624534552597N00644605EA0197001964000
    +B1624564552606N00644602EA0196401963000
    +B1624594552617N00644596EA0196001962000
    +B1625024552626N00644593EA0196701962000
    +B1625054552626N00644586EA0196901961000
    +B1625084552624N00644577EA0197301961000
    +B1625114552624N00644570EA0196601961000
    +B1625144552628N00644562EA0196301960000
    +B1625174552634N00644556EA0196001960000
    +B1625204552643N00644551EA0195501958000
    +B1625234552652N00644551EA0195101956000
    +B1625264552662N00644551EA0195001954000
    +B1625294552672N00644550EA0195201952000
    +B1625324552680N00644547EA0196001951000
    +B1625354552685N00644545EA0196001951000
    +B1625384552686N00644538EA0196001950000
    +B1625424552687N00644529EA0196001950000
    +B1625454552685N00644520EA0195701949000
    +B1625484552681N00644511EA0195401949000
    +B1625514552677N00644501EA0195001948000
    +B1625544552676N00644492EA0195101947000
    +B1625574552678N00644484EA0195301946000
    +B1626004552682N00644478EA0195701946000
    +B1626034552685N00644473EA0195601945000
    +B1626064552691N00644472EA0195201944000
    +B1626094552699N00644470EA0194701944000
    +B1626124552709N00644469EA0194401942000
    +B1626154552719N00644470EA0194401940000
    +B1626184552730N00644470EA0194601939000
    +B1626214552738N00644473EA0194301938000
    +B1626244552749N00644475EA0194101936000
    +B1626274552758N00644475EA0194101935000
    +B1626304552767N00644474EA0193601934000
    +B1626334552779N00644473EA0193901932000
    +B1626364552788N00644477EA0194001931000
    +B1626394552799N00644477EA0194301930000
    +B1626424552808N00644473EA0194701929000
    +B1626454552809N00644461EA0195201929000
    +B1626484552795N00644458EA0195701929000
    +B1626514552778N00644475EA0196201930000
    +B1626544552773N00644507EA0196401931000
    +B1626574552781N00644534EA0196801933000
    +B1627014552796N00644544EA0197001935000
    +B1627044552801N00644538EA0197501937000
    +B1627074552800N00644532EA0197201938000
    +B1627104552796N00644523EA0197001939000
    +B1627134552797N00644516EA0196701940000
    +B1627164552804N00644511EA0196401941000
    +B1627194552812N00644510EA0196101942000
    +B1627224552824N00644507EA0195401943000
    +B1627254552837N00644503EA0195401943000
    +B1627284552849N00644499EA0195301943000
    +B1627314552861N00644494EA0195101943000
    +B1627344552874N00644491EA0195301943000
    +B1627374552887N00644489EA0195101942000
    +B1627404552900N00644487EA0195301942000
    +B1627434552912N00644485EA0195701942000
    +B1627464552921N00644485EA0196501942000
    +B1627494552928N00644485EA0196401942000
    +B1627524552936N00644481EA0196701943000
    +B1627564552947N00644478EA0196501943000
    +B1627594552958N00644475EA0196601944000
    +B1628024552969N00644474EA0196801945000
    +B1628054552982N00644475EA0197101946000
    +B1628084552994N00644477EA0197301947000
    +B1628114553005N00644478EA0197401948000
    +B1628144553014N00644479EA0197001949000
    +B1628174553025N00644479EA0196701950000
    +B1628204553036N00644479EA0196001950000
    +B1628234553051N00644478EA0195801950000
    +B1628264553064N00644475EA0195901950000
    +B1628294553078N00644474EA0195501949000
    +B1628324553094N00644474EA0195801949000
    +B1628354553108N00644475EA0196001948000
    +B1628384553123N00644474EA0196001948000
    +B1628414553135N00644475EA0195601947000
    +B1628444553151N00644475EA0195501947000
    +B1628474553167N00644476EA0195001946000
    +B1628514553188N00644477EA0194801944000
    +B1628544553203N00644478EA0194701943000
    +B1628574553220N00644480EA0194201942000
    +B1629004553237N00644485EA0194201940000
    +B1629034553253N00644492EA0194001938000
    +B1629064553271N00644500EA0193801937000
    +B1629094553289N00644509EA0193301935000
    +B1629124553308N00644517EA0192901933000
    +B1629154553326N00644528EA0192701930000
    +B1629184553343N00644539EA0192001928000
    +B1629214553362N00644553EA0192101925000
    +B1629244553377N00644566EA0191601923000
    +B1629274553395N00644579EA0191301920000
    +B1629304553414N00644591EA0191701917000
    +B1629334553430N00644603EA0191001915000
    +B1629364553449N00644616EA0191001913000
    +B1629394553464N00644632EA0190901910000
    +B1629424553480N00644646EA0190601908000
    +B1629454553496N00644658EA0190101906000
    +B1629484553514N00644669EA0189901903000
    +B1629514553532N00644681EA0189701901000
    +B1629544553550N00644693EA0189601899000
    +B1629574553570N00644707EA0190301896000
    +B1630004553586N00644721EA0190901895000
    +B1630034553601N00644731EA0191001894000
    +B1630064553614N00644738EA0191401894000
    +B1630104553628N00644743EA0191601894000
    +B1630134553640N00644750EA0192001894000
    +B1630164553651N00644757EA0192501895000
    +B1630194553663N00644753EA0193001896000
    +B1630224553665N00644742EA0193301897000
    +B1630254553655N00644735EA0193601899000
    +B1630284553639N00644748EA0193301901000
    +B1630314553630N00644777EA0193701902000
    +B1630344553634N00644804EA0194201904000
    +B1630374553645N00644821EA0194801906000
    +B1630404553658N00644829EA0195201909000
    +B1630434553671N00644827EA0195501912000
    +B1630464553681N00644822EA0195901915000
    +B1630494553685N00644813EA0195801916000
    +B1630524553679N00644802EA0195801918000
    +B1630554553664N00644806EA0196001921000
    +B1630584553654N00644831EA0195901923000
    +B1631014553656N00644862EA0196501926000
    +B1631044553667N00644879EA0196501928000
    +B1631074553682N00644888EA0196901930000
    +B1631104553697N00644895EA0197101932000
    +B1631134553711N00644902EA0197101934000
    +B1631164553724N00644909EA0197501936000
    +B1631194553738N00644916EA0197801938000
    +B1631224553749N00644913EA0198101940000
    +B1631254553751N00644903EA0198301942000
    +B1631284553742N00644900EA0198101944000
    +B1631314553728N00644907EA0198201947000
    +B1631354553709N00644913EA0198101950000
    +B1631384553701N00644906EA0198201951000
    +B1631414553708N00644899EA0197801953000
    +B1631444553722N00644898EA0197701954000
    +B1631474553738N00644903EA0197401955000
    +B1631504553755N00644911EA0197401956000
    +B1631534553771N00644924EA0197401957000
    +B1631564553788N00644941EA0197501957000
    +B1631594553805N00644957EA0197301958000
    +B1632024553822N00644976EA0197301958000
    +B1632054553836N00644998EA0197701959000
    +B1632084553850N00645019EA0197801959000
    +B1632114553865N00645035EA0197701960000
    +B1632144553878N00645058EA0197701960000
    +B1632174553884N00645090EA0197301960000
    +B1632204553883N00645125EA0196901960000
    +B1632234553880N00645162EA0196501960000
    +B1632264553873N00645196EA0196101959000
    +B1632294553864N00645230EA0196101958000
    +B1632324553857N00645263EA0196201956000
    +B1632354553853N00645294EA0195901955000
    +B1632384553847N00645325EA0195801954000
    +B1632414553841N00645354EA0195101952000
    +B1632444553833N00645385EA0195001950000
    +B1632474553825N00645411EA0195001948000
    +B1632514553817N00645441EA0193801945000
    +B1632544553811N00645469EA0193401943000
    +B1632574553806N00645495EA0192301939000
    +B1633004553798N00645525EA0191901935000
    +B1633034553788N00645550EA0192401932000
    +B1633064553778N00645575EA0193001929000
    +B1633094553776N00645603EA0193301927000
    +B1633124553787N00645622EA0193701925000
    +B1633154553797N00645621EA0194201924000
    +B1633184553799N00645607EA0194601924000
    +B1633214553790N00645595EA0195001924000
    +B1633244553775N00645602EA0195601925000
    +B1633274553762N00645623EA0195701926000
    +B1633304553761N00645653EA0195701928000
    +B1633334553770N00645673EA0195901929000
    +B1633364553782N00645675EA0196101931000
    +B1633394553783N00645664EA0196301932000
    +B1633424553771N00645660EA0196501934000
    +B1633454553756N00645675EA0196401935000
    +B1633484553747N00645702EA0196001937000
    +B1633514553737N00645731EA0195601937000
    +B1633544553724N00645759EA0195201938000
    +B1633574553710N00645786EA0195001938000
    +B1634004553697N00645811EA0193701937000
    +B1634034553682N00645837EA0192501935000
    +B1634064553665N00645863EA0191701933000
    +B1634094553651N00645885EA0190201929000
    +B1634134553629N00645916EA0189401923000
    +B1634164553614N00645936EA0188401917000
    +B1634194553595N00645959EA0186801911000
    +B1634224553575N00645978EA0186201904000
    +B1634254553556N00645999EA0184401897000
    +B1634284553534N00646025EA0183601888000
    +B1634314553514N00646047EA0182901880000
    +B1634344553495N00646068EA0182401872000
    +B1634374553478N00646090EA0182801865000
    +B1634404553467N00646104EA0182501858000
    +B1634434553451N00646117EA0182001852000
    +B1634464553435N00646129EA0181801846000
    +B1634494553420N00646142EA0181601841000
    +B1634524553403N00646156EA0182201836000
    +B1634554553389N00646168EA0182401832000
    +B1634584553374N00646177EA0182701829000
    +B1635014553359N00646182EA0183201827000
    +B1635044553345N00646175EA0183701826000
    +B1635074553338N00646158EA0183801825000
    +B1635104553338N00646139EA0183401824000
    +B1635134553348N00646129EA0182701823000
    +B1635164553358N00646139EA0182301821000
    +B1635194553359N00646163EA0182401819000
    +B1635224553352N00646183EA0183001818000
    +B1635254553340N00646199EA0183201817000
    +B1635284553325N00646214EA0184101817000
    +B1635314553311N00646232EA0184601818000
    +B1635344553301N00646256EA0185501819000
    +B1635374553301N00646277EA0185601821000
    +B1635404553311N00646292EA0185901823000
    +B1635444553326N00646292EA0185901826000
    +B1635474553335N00646280EA0185801828000
    +B1635504553334N00646262EA0185701830000
    +B1635534553320N00646245EA0185601831000
    +B1635564553301N00646238EA0186501832000
    +B1635594553286N00646240EA0186801834000
    +B1636024553273N00646249EA0187101835000
    +B1636054553267N00646266EA0187101837000
    +B1636084553268N00646288EA0187701839000
    +B1636114553278N00646298EA0188101841000
    +B1636144553287N00646288EA0188501843000
    +B1636174553285N00646270EA0188801846000
    +B1636204553271N00646264EA0189301849000
    +B1636234553258N00646272EA0189401852000
    +B1636264553252N00646293EA0190101854000
    +B1636294553258N00646309EA0190401858000
    +B1636324553270N00646313EA0190701861000
    +B1636354553279N00646303EA0190901864000
    +B1636384553277N00646285EA0191001867000
    +B1636414553262N00646274EA0191201870000
    +B1636444553246N00646283EA0191701873000
    +B1636474553239N00646303EA0192201876000
    +B1636504553242N00646323EA0192801880000
    +B1636534553253N00646330EA0193001883000
    +B1636564553262N00646321EA0193401887000
    +B1636594553260N00646304EA0193301890000
    +B1637024553248N00646291EA0193501893000
    +B1637054553230N00646291EA0193701896000
    +B1637084553215N00646301EA0193801899000
    +B1637124553200N00646321EA0193401902000
    +B1637154553201N00646343EA0192801904000
    +B1637184553211N00646360EA0192601905000
    +B1637214553227N00646359EA0192801905000
    +B1637244553234N00646345EA0193201906000
    +B1637274553231N00646328EA0193401907000
    +B1637304553220N00646313EA0193401909000
    +B1637334553202N00646313EA0193201909000
    +B1637364553189N00646327EA0193101910000
    +B1637394553179N00646342EA0192401910000
    +B1637424553165N00646359EA0192101910000
    +B1637454553152N00646377EA0191701909000
    +B1637484553140N00646395EA0191001908000
    +B1637514553128N00646416EA0190401906000
    +B1637544553118N00646439EA0189801904000
    +B1637574553107N00646463EA0189401901000
    +B1638004553098N00646486EA0188601898000
    +B1638034553088N00646513EA0187901894000
    +B1638064553079N00646541EA0187301891000
    +B1638094553071N00646571EA0187001886000
    +B1638124553064N00646595EA0186101882000
    +B1638154553053N00646624EA0184801877000
    +B1638184553038N00646656EA0184301872000
    +B1638214553023N00646685EA0183501866000
    +B1638244553006N00646716EA0183101861000
    +B1638274552991N00646746EA0182701855000
    +B1638314552970N00646785EA0182001848000
    +B1638344552953N00646816EA0181301842000
    +B1638374552938N00646845EA0180901836000
    +B1638404552922N00646874EA0180001830000
    +B1638434552906N00646903EA0179001824000
    +B1638464552890N00646934EA0178101818000
    +B1638494552877N00646967EA0177901811000
    +B1638524552866N00646997EA0177501805000
    +B1638554552852N00647027EA0177001799000
    +B1638584552839N00647057EA0176801793000
    +B1639014552827N00647086EA0176301788000
    +B1639044552814N00647115EA0176101782000
    +B1639074552802N00647146EA0175501777000
    +B1639104552788N00647179EA0175301772000
    +B1639134552774N00647210EA0175701768000
    +B1639164552762N00647236EA0176001764000
    +B1639194552750N00647260EA0175801761000
    +B1639224552738N00647282EA0175701758000
    +B1639254552727N00647304EA0175301755000
    +B1639284552713N00647327EA0175201752000
    +B1639314552701N00647349EA0174701750000
    +B1639344552688N00647373EA0174601747000
    +B1639374552676N00647397EA0174301744000
    +B1639404552664N00647422EA0174401742000
    +B1639434552654N00647447EA0174401739000
    +B1639464552645N00647474EA0174401737000
    +B1639494552637N00647500EA0174101735000
    +B1639534552626N00647537EA0174001733000
    +B1639564552619N00647564EA0173701731000
    +B1639594552612N00647593EA0173701729000
    +B1640024552606N00647620EA0173501727000
    +B1640054552603N00647649EA0173401726000
    +B1640084552602N00647676EA0173401724000
    +B1640114552603N00647702EA0173001723000
    +B1640144552606N00647728EA0172801721000
    +B1640174552613N00647752EA0172701720000
    +B1640204552620N00647777EA0172301718000
    +B1640234552625N00647803EA0172401716000
    +B1640264552632N00647827EA0172501715000
    +B1640294552640N00647847EA0172501714000
    +B1640324552644N00647870EA0172701713000
    +B1640354552645N00647895EA0172501712000
    +B1640384552645N00647922EA0172601712000
    +B1640414552645N00647948EA0172801711000
    +B1640444552646N00647973EA0172601711000
    +B1640474552647N00648001EA0172401711000
    +B1640504552650N00648029EA0172201710000
    +B1640534552655N00648057EA0172001709000
    +B1640564552659N00648083EA0171601708000
    +B1640594552667N00648109EA0171301707000
    +B1641024552675N00648136EA0171301705000
    +B1641054552682N00648161EA0171001704000
    +B1641094552692N00648196EA0170901702000
    +B1641124552698N00648221EA0170901701000
    +B1641154552702N00648248EA0170701699000
    +B1641184552703N00648276EA0170601698000
    +B1641214552704N00648306EA0170601697000
    +B1641244552705N00648336EA0170401695000
    +B1641274552709N00648367EA0169901694000
    +B1641304552713N00648398EA0169601692000
    +B1641334552717N00648429EA0169501690000
    +B1641364552722N00648459EA0169301688000
    +B1641394552726N00648490EA0169201686000
    +B1641424552729N00648520EA0169101684000
    +B1641454552731N00648549EA0168901683000
    +B1641484552734N00648577EA0169001681000
    +B1641514552737N00648601EA0169401680000
    +B1641544552743N00648622EA0169301679000
    +B1641574552753N00648637EA0169401678000
    +B1642004552765N00648639EA0169501678000
    +B1642034552774N00648631EA0169901678000
    +B1642064552776N00648616EA0170301678000
    +B1642094552768N00648605EA0170401678000
    +B1642124552755N00648604EA0170201678000
    +B1642154552740N00648617EA0169901679000
    +B1642184552728N00648639EA0169901679000
    +B1642214552721N00648666EA0170401679000
    +B1642244552721N00648687EA0170401679000
    +B1642284552725N00648717EA0170301680000
    +B1642314552725N00648740EA0170601681000
    +B1642344552728N00648764EA0170801681000
    +B1642374552733N00648784EA0171001682000
    +B1642404552743N00648795EA0170801683000
    +B1642434552755N00648791EA0170901683000
    +B1642464552761N00648777EA0171001684000
    +B1642494552756N00648760EA0171001684000
    +B1642524552741N00648751EA0171101685000
    +B1642554552726N00648759EA0171201686000
    +B1642584552719N00648782EA0171201686000
    +B1643014552721N00648804EA0171301687000
    +B1643044552728N00648824EA0171001688000
    +B1643074552735N00648846EA0170901689000
    +B1643104552743N00648870EA0170901689000
    +B1643134552749N00648894EA0171001690000
    +B1643164552756N00648918EA0171001690000
    +B1643194552764N00648940EA0171201691000
    +B1643224552771N00648958EA0171101692000
    +B1643254552778N00648979EA0171001692000
    +B1643284552784N00649001EA0171201693000
    +B1643314552790N00649025EA0171001693000
    +B1643344552797N00649050EA0170901693000
    +B1643374552803N00649074EA0170801694000
    +B1643404552810N00649099EA0170601694000
    +B1643434552815N00649126EA0170201693000
    +B1643464552818N00649153EA0170301693000
    +B1643504552819N00649189EA0169801692000
    +B1643534552819N00649218EA0169701690000
    +B1643564552822N00649247EA0169601689000
    +B1643594552829N00649275EA0169601688000
    +B1644024552835N00649302EA0169401687000
    +B1644054552841N00649333EA0169201686000
    +B1644084552848N00649359EA0169001684000
    +B1644114552856N00649385EA0168401683000
    +B1644144552864N00649411EA0168301681000
    +B1644174552871N00649434EA0167901679000
    +B1644204552881N00649458EA0167501677000
    +B1644234552892N00649480EA0167201675000
    +B1644264552903N00649503EA0167001672000
    +B1644294552913N00649525EA0166901670000
    +B1644324552924N00649545EA0166801667000
    +B1644354552937N00649563EA0166801665000
    +B1644384552950N00649581EA0166901664000
    +B1644414552964N00649599EA0166801662000
    +B1644444552977N00649617EA0166801660000
    +B1644474552991N00649635EA0167001659000
    +B1644504553005N00649655EA0167301658000
    +B1644534553018N00649675EA0167301658000
    +B1644564553029N00649694EA0167301657000
    +B1644594553042N00649715EA0167101657000
    +B1645024553054N00649738EA0167001656000
    +B1645064553070N00649770EA0166801655000
    +B1645094553081N00649798EA0166701654000
    +B1645124553091N00649825EA0167101653000
    +B1645154553101N00649848EA0167501653000
    +B1645184553112N00649865EA0167601653000
    +B1645214553125N00649863EA0167501653000
    +B1645244553132N00649851EA0167701653000
    +B1645274553131N00649840EA0167501653000
    +B1645304553123N00649830EA0167301653000
    +B1645334553114N00649825EA0167101653000
    +B1645364553109N00649814EA0166801653000
    +B1645394553114N00649802EA0166801652000
    +B1645424553129N00649801EA0166601652000
    +B1645454553145N00649819EA0166501651000
    +B1645484553155N00649847EA0166201650000
    +B1645514553161N00649878EA0165801649000
    +B1645544553167N00649910EA0165601648000
    +B1645574553173N00649940EA0165101646000
    +B1646004553179N00649972EA0164601644000
    +B1646034553187N00650003EA0164201642000
    +B1646064553197N00650032EA0163801639000
    +B1646094553207N00650061EA0163701636000
    +B1646124553217N00650089EA0163201634000
    +B1646154553227N00650119EA0162901631000
    +B1646184553238N00650147EA0162501628000
    +B1646214553247N00650176EA0162201625000
    +B1646254553260N00650214EA0161701621000
    +B1646284553268N00650242EA0161001618000
    +B1646314553279N00650270EA0160501614000
    +B1646344553289N00650296EA0160101610000
    +B1646374553301N00650321EA0159801606000
    +B1646404553314N00650345EA0159701603000
    +B1646434553327N00650370EA0159501599000
    +B1646464553340N00650394EA0159601596000
    +B1646494553353N00650419EA0159601594000
    +B1646524553367N00650441EA0159701591000
    +B1646554553381N00650462EA0159501589000
    +B1646584553394N00650485EA0159401587000
    +B1647014553406N00650507EA0159501586000
    +B1647044553418N00650531EA0159701584000
    +B1647074553429N00650553EA0159601583000
    +B1647104553439N00650577EA0159201582000
    +B1647134553450N00650600EA0158901580000
    +B1647164553463N00650622EA0158801578000
    +B1647194553474N00650646EA0158701577000
    +B1647224553487N00650668EA0158601575000
    +B1647254553499N00650689EA0158601574000
    +B1647284553511N00650711EA0158601573000
    +B1647314553524N00650735EA0158401572000
    +B1647344553535N00650763EA0158201570000
    +B1647374553546N00650792EA0158001569000
    +B1647414553563N00650831EA0157701567000
    +B1647444553577N00650859EA0157301565000
    +B1647474553589N00650888EA0157401564000
    +B1647504553597N00650917EA0157101562000
    +B1647534553605N00650949EA0156801560000
    +B1647564553613N00650981EA0156601558000
    +B1647594553625N00651010EA0156501556000
    +B1648024553637N00651037EA0156501555000
    +B1648054553651N00651060EA0156701553000
    +B1648084553664N00651084EA0156301552000
    +B1648114553677N00651111EA0156501550000
    +B1648144553690N00651134EA0156301549000
    +B1648174553703N00651160EA0155701548000
    +B1648204553718N00651186EA0155801546000
    +B1648234553731N00651210EA0155501545000
    +B1648264553747N00651234EA0155101543000
    +B1648294553761N00651257EA0155201541000
    +B1648324553777N00651278EA0154601540000
    +B1648354553792N00651301EA0154301538000
    +B1648384553807N00651325EA0153701535000
    +B1648414553822N00651351EA0153601533000
    +B1648444553837N00651376EA0153201530000
    +B1648474553852N00651402EA0152801528000
    +B1648504553869N00651424EA0152401525000
    +B1648534553886N00651447EA0152201522000
    +B1648574553907N00651477EA0152001518000
    +B1649004553923N00651498EA0151501516000
    +B1649034553940N00651519EA0151201513000
    +B1649064553958N00651540EA0150901510000
    +B1649094553976N00651561EA0150401507000
    +B1649124553994N00651583EA0150001504000
    +B1649154554012N00651606EA0149701500000
    +B1649184554030N00651629EA0149601497000
    +B1649214554045N00651653EA0149501494000
    +B1649244554058N00651676EA0149201491000
    +B1649274554073N00651699EA0149001488000
    +B1649304554090N00651716EA0148601486000
    +B1649334554110N00651731EA0148301483000
    +B1649364554130N00651746EA0148001480000
    +B1649394554150N00651760EA0148101477000
    +B1649424554169N00651771EA0147801474000
    +B1649454554189N00651784EA0147701472000
    +B1649484554206N00651800EA0147801470000
    +B1649514554220N00651820EA0147601468000
    +B1649544554234N00651841EA0147101466000
    +B1649574554252N00651859EA0146801463000
    +B1650004554272N00651871EA0147401461000
    +B1650034554289N00651882EA0147501460000
    +B1650064554304N00651896EA0147401458000
    +B1650094554318N00651912EA0147101457000
    +B1650124554334N00651930EA0147001456000
    +B1650164554357N00651950EA0146501455000
    +B1650194554374N00651960EA0146301453000
    +B1650224554394N00651968EA0146301452000
    +B1650254554414N00651977EA0146401451000
    +B1650284554434N00651987EA0146601450000
    +B1650314554453N00651998EA0146601449000
    +B1650344554474N00652009EA0146901449000
    +B1650374554495N00652019EA0147401449000
    +B1650404554513N00652022EA0147201449000
    +B1650434554533N00652025EA0146801449000
    +B1650464554555N00652034EA0146501449000
    +B1650494554578N00652044EA0146101448000
    +B1650524554600N00652051EA0146201447000
    +B1650554554621N00652058EA0145701446000
    +B1650584554642N00652068EA0145701445000
    +B1651014554663N00652079EA0145601444000
    +B1651044554685N00652087EA0145701443000
    +B1651074554706N00652094EA0145501442000
    +B1651104554729N00652103EA0145301441000
    +B1651134554751N00652115EA0145501440000
    +B1651164554769N00652129EA0145401439000
    +B1651194554786N00652141EA0144801438000
    +B1651224554807N00652149EA0144701436000
    +B1651254554825N00652154EA0144601435000
    +B1651284554844N00652161EA0144201434000
    +B1651324554869N00652166EA0144001432000
    +B1651354554887N00652168EA0143601430000
    +B1651384554906N00652167EA0143401429000
    +B1651414554925N00652166EA0143201427000
    +B1651444554945N00652164EA0143001425000
    +B1651474554964N00652164EA0142401423000
    +B1651504554985N00652164EA0142201420000
    +B1651534555004N00652162EA0141901418000
    +B1651564555021N00652158EA0141401415000
    +B1651594555039N00652153EA0141001413000
    +B1652024555058N00652149EA0140801410000
    +B1652054555077N00652150EA0140601407000
    +B1652084555098N00652157EA0140401404000
    +B1652114555117N00652167EA0140301402000
    +B1652144555136N00652178EA0140001399000
    +B1652174555156N00652185EA0139801396000
    +B1652204555175N00652194EA0139501394000
    +B1652234555193N00652209EA0139101391000
    +B1652264555212N00652222EA0138701388000
    +B1652294555231N00652231EA0138201386000
    +B1652324555253N00652239EA0138101383000
    +B1652354555274N00652245EA0138101380000
    +B1652384555294N00652251EA0137601377000
    +B1652414555314N00652253EA0137101374000
    +B1652444555334N00652254EA0136701371000
    +B1652474555354N00652252EA0136201368000
    +B1652514555379N00652245EA0135701363000
    +B1652544555397N00652239EA0135301360000
    +B1652574555415N00652230EA0134901356000
    +B1653004555432N00652221EA0134501352000
    +B1653034555449N00652215EA0133901348000
    +B1653064555468N00652210EA0133401344000
    +B1653094555487N00652205EA0133101340000
    +B1653124555505N00652199EA0132701336000
    +B1653154555524N00652192EA0132001332000
    +B1653184555541N00652184EA0131501327000
    +B1653214555559N00652173EA0130901323000
    +B1653244555575N00652161EA0130801318000
    +B1653274555591N00652149EA0130301314000
    +B1653304555607N00652136EA0130301310000
    +B1653334555621N00652122EA0130201306000
    +B1653364555635N00652107EA0130101302000
    +B1653394555646N00652092EA0130201298000
    +B1653424555656N00652076EA0130101296000
    +B1653454555664N00652059EA0130101293000
    +B1653484555671N00652043EA0130101291000
    +B1653514555680N00652026EA0130001289000
    +B1653544555689N00652011EA0130201287000
    +B1653574555697N00651999EA0130301286000
    +B1654004555706N00651986EA0130301284000
    +B1654034555713N00651972EA0130201283000
    +B1654064555720N00651958EA0130301282000
    +B1654104555725N00651935EA0130201281000
    +B1654134555721N00651916EA0130201280000
    +B1654164555710N00651905EA0129801279000
    +B1654194555695N00651902EA0129601277000
    +B1654224555680N00651908EA0129101276000
    +B1654254555667N00651920EA0128901274000
    +B1654284555656N00651939EA0128201272000
    +B1654314555647N00651961EA0127901270000
    +B1654344555638N00651981EA0127501268000
    +B1654374555630N00652003EA0127501266000
    +B1654404555626N00652025EA0127501263000
    +B1654434555625N00652050EA0127501261000
    +B1654464555627N00652074EA0127501259000
    +B1654494555634N00652098EA0127401258000
    +B1654524555646N00652121EA0127301256000
    +B1654554555660N00652143EA0127201254000
    +B1654584555674N00652165EA0127401253000
    +B1655014555688N00652185EA0127201252000
    +B1655044555703N00652207EA0127301251000
    +B1655074555718N00652225EA0127201250000
    +B1655104555733N00652244EA0127101249000
    +B1655134555747N00652263EA0127001248000
    +B1655164555762N00652281EA0126901247000
    +B1655194555777N00652300EA0127001246000
    +B1655224555792N00652318EA0126901245000
    +B1655254555808N00652333EA0126901244000
    +B1655284555827N00652340EA0126901244000
    +B1655324555851N00652340EA0127101244000
    +B1655354555866N00652330EA0127101244000
    +B1655384555871N00652311EA0127401244000
    +B1655414555871N00652294EA0127401244000
    +B1655444555865N00652278EA0127501245000
    +B1655474555856N00652270EA0127401245000
    +B1655504555844N00652272EA0127601245000
    +B1655534555836N00652284EA0127601246000
    +B1655564555833N00652303EA0127601246000
    +B1655594555837N00652324EA0127601247000
    +B1656024555848N00652343EA0127901247000
    +B1656054555864N00652352EA0127901248000
    +B1656084555879N00652352EA0128001249000
    +B1656114555893N00652344EA0127901250000
    +B1656144555905N00652331EA0127901250000
    +B1656174555916N00652316EA0128001250000
    +B1656204555923N00652300EA0128201251000
    +B1656234555926N00652283EA0128201251000
    +B1656264555926N00652265EA0128001251000
    +B1656294555922N00652249EA0127801251000
    +B1656324555913N00652240EA0127401251000
    +B1656354555902N00652234EA0127501250000
    +B1656384555890N00652234EA0127001249000
    +B1656414555876N00652237EA0126801248000
    +B1656444555862N00652240EA0126601247000
    +B1656484555843N00652235EA0126701245000
    +B1656514555828N00652237EA0126701244000
    +B1656544555815N00652243EA0126901244000
    +B1656574555805N00652255EA0127001243000
    +B1657004555802N00652275EA0127001243000
    +B1657034555807N00652298EA0127001243000
    +B1657064555819N00652318EA0127001243000
    +B1657094555835N00652329EA0127001243000
    +B1657124555853N00652326EA0127101244000
    +B1657154555867N00652311EA0127301244000
    +B1657184555875N00652292EA0127301244000
    +B1657214555879N00652271EA0127301244000
    +B1657244555876N00652252EA0127201244000
    +B1657274555869N00652237EA0126901244000
    +B1657304555857N00652225EA0126801244000
    +B1657334555846N00652218EA0126901244000
    +B1657364555833N00652217EA0126801243000
    +B1657394555820N00652224EA0126901243000
    +B1657424555808N00652232EA0126701242000
    +B1657454555795N00652242EA0126801242000
    +B1657484555783N00652254EA0127101242000
    +B1657514555774N00652266EA0127001242000
    +B1657544555765N00652279EA0127101242000
    +B1657574555754N00652289EA0127201242000
    +B1658004555742N00652296EA0127501242000
    +B1658034555730N00652301EA0127901243000
    +B1658074555716N00652305EA0127601243000
    +B1658104555705N00652301EA0127701244000
    +B1658134555696N00652295EA0127501244000
    +B1658164555690N00652283EA0127601245000
    +B1658194555689N00652268EA0127401245000
    +B1658224555697N00652252EA0127401245000
    +B1658254555711N00652242EA0127701246000
    +B1658284555727N00652240EA0127901246000
    +B1658314555742N00652249EA0128101247000
    +B1658344555751N00652265EA0128101248000
    +B1658374555752N00652283EA0128501249000
    +B1658404555749N00652300EA0128501250000
    +B1658434555742N00652318EA0128701251000
    +B1658464555737N00652335EA0128701252000
    +B1658494555733N00652356EA0128801252000
    +B1658524555730N00652378EA0128901253000
    +B1658554555739N00652400EA0129101254000
    +B1658584555754N00652415EA0129301255000
    +B1659014555771N00652414EA0129501256000
    +B1659044555776N00652400EA0129801257000
    +B1659074555771N00652383EA0130501259000
    +B1659104555763N00652373EA0130501261000
    +B1659134555751N00652372EA0130901262000
    +B1659164555742N00652385EA0130801264000
    +B1659194555741N00652408EA0130801266000
    +B1659224555750N00652432EA0131101268000
    +B1659264555771N00652449EA0131001270000
    +B1659294555787N00652445EA0131401272000
    +B1659324555797N00652432EA0132001274000
    +B1659354555801N00652414EA0132401276000
    +B1659384555793N00652397EA0132801278000
    +B1659414555782N00652401EA0133201280000
    +B1659444555776N00652416EA0133601283000
    +B1659474555783N00652437EA0133801285000
    +B1659504555796N00652451EA0134201288000
    +B1659534555810N00652451EA0134901291000
    +B1659564555819N00652438EA0135301295000
    +B1659594555821N00652417EA0135601298000
    +B1700024555813N00652403EA0136001302000
    +B1700054555803N00652404EA0136301305000
    +B1700084555798N00652418EA0136401309000
    +B1700114555806N00652438EA0136901312000
    +B1700144555822N00652449EA0137301315000
    +B1700174555837N00652448EA0138001319000
    +B1700204555848N00652436EA0138101322000
    +B1700234555854N00652418EA0138501326000
    +B1700264555851N00652400EA0138501329000
    +B1700294555841N00652389EA0138601332000
    +B1700324555830N00652388EA0138901335000
    +B1700354555822N00652399EA0138501337000
    +B1700384555819N00652418EA0138101339000
    +B1700414555828N00652440EA0138101341000
    +B1700444555844N00652451EA0138401343000
    +B1700474555859N00652446EA0138501344000
    +B1700514555874N00652427EA0138601347000
    +B1700544555875N00652408EA0138501348000
    +B1700574555873N00652393EA0138601349000
    +B1701004555866N00652380EA0138401350000
    +B1701034555855N00652377EA0138201351000
    +B1701064555846N00652388EA0137901352000
    +B1701094555847N00652408EA0137401352000
    +B1701124555859N00652426EA0137001351000
    +B1701154555878N00652427EA0136401351000
    +B1701184555894N00652411EA0136201350000
    +B1701214555902N00652390EA0136001348000
    +B1701244555904N00652367EA0136301347000
    +B1701274555903N00652348EA0136201346000
    +B1701304555899N00652327EA0136301345000
    +B1701334555892N00652309EA0136201344000
    +B1701364555883N00652288EA0136201343000
    +B1701394555874N00652271EA0136301342000
    +B1701424555867N00652256EA0135901341000
    +B1701454555858N00652239EA0135801341000
    +B1701484555854N00652220EA0135601340000
    +B1701514555850N00652200EA0135301339000
    +B1701544555849N00652177EA0135101338000
    +B1701574555847N00652155EA0134901337000
    +B1702004555841N00652135EA0134401336000
    +B1702034555840N00652114EA0134001334000
    +B1702064555838N00652093EA0133701332000
    +B1702104555835N00652065EA0133401330000
    +B1702134555837N00652046EA0133001328000
    +B1702164555836N00652026EA0132501325000
    +B1702194555834N00652008EA0132101322000
    +B1702224555832N00651989EA0131601320000
    +B1702254555828N00651974EA0131101316000
    +B1702284555818N00651960EA0130401313000
    +B1702314555804N00651950EA0130001309000
    +B1702344555790N00651942EA0129401306000
    +B1702374555773N00651937EA0129001302000
    +B1702404555757N00651936EA0128801298000
    +B1702434555740N00651935EA0128401294000
    +B1702464555724N00651934EA0128401290000
    +B1702494555708N00651928EA0128101286000
    +B1702524555693N00651923EA0128101283000
    +B1702554555678N00651919EA0128001280000
    +B1702584555662N00651917EA0127601276000
    +B1703014555646N00651915EA0127501273000
    +B1703044555630N00651912EA0127301271000
    +B1703074555614N00651911EA0127201268000
    +B1703104555600N00651911EA0127001265000
    +B1703134555585N00651913EA0126801262000
    +B1703164555569N00651915EA0126601260000
    +B1703194555553N00651921EA0126301257000
    +B1703224555537N00651927EA0126101256000
    +B1703254555521N00651937EA0125701256000
    +B1703294555504N00651960EA0125401253000
    +B1703324555494N00651981EA0125301250000
    +B1703354555487N00652002EA0125001248000
    +B1703384555482N00652024EA0124701246000
    +B1703414555481N00652049EA0124401243000
    +B1703444555484N00652074EA0124201240000
    +B1703474555488N00652097EA0124101238000
    +B1703504555495N00652120EA0123801236000
    +B1703534555505N00652140EA0123401233000
    +B1703564555516N00652160EA0123101230000
    +B1703594555528N00652176EA0123101228000
    +B1704024555541N00652190EA0122801226000
    +B1704054555555N00652203EA0122701223000
    +B1704084555571N00652215EA0122601221000
    +B1704114555587N00652225EA0122801219000
    +B1704144555602N00652233EA0122801217000
    +B1704174555617N00652237EA0123301215000
    +B1704204555629N00652236EA0123201214000
    +B1704234555644N00652230EA0123501213000
    +B1704264555657N00652225EA0123001212000
    +B1704294555673N00652220EA0123001211000
    +B1704324555687N00652217EA0122501210000
    +B1704354555702N00652218EA0122501209000
    +B1704384555716N00652219EA0122401208000
    +B1704414555728N00652219EA0122201207000
    +B1704454555742N00652214EA0122201205000
    +B1704484555752N00652208EA0121701204000
    +B1704514555761N00652203EA0121601203000
    +B1704544555769N00652195EA0121201202000
    +B1704574555777N00652186EA0120901200000
    +B1705004555782N00652172EA0120501198000
    +B1705034555782N00652156EA0120101196000
    +B1705064555777N00652140EA0119701194000
    +B1705094555764N00652130EA0119201191000
    +B1705124555747N00652133EA0118601188000
    +B1705154555730N00652144EA0118401185000
    +B1705184555719N00652161EA0117901182000
    +B1705214555718N00652184EA0117601178000
    +B1705244555728N00652196EA0117101175000
    +B1705274555737N00652189EA0117001171000
    +B1705304555742N00652178EA0116501169000
    +B1705334555739N00652162EA0116001165000
    +B1705364555726N00652152EA0115701161000
    +B1705394555711N00652144EA0115301157000
    +B1705424555695N00652139EA0115001154000
    +B1705454555679N00652137EA0114601150000
    +B1705484555663N00652143EA0114101146000
    +B1705514555653N00652164EA0114001142000
    +B1705544555657N00652184EA0113801139000
    +B1705574555669N00652193EA0114001135000
    +B1706014555682N00652194EA0113601132000
    +B1706044555693N00652189EA0113401129000
    +B1706074555702N00652182EA0113001126000
    +B1706104555710N00652172EA0112801124000
    +B1706134555715N00652159EA0112501121000
    +B1706164555709N00652142EA0112001118000
    +B1706194555693N00652141EA0111401116000
    +B1706224555677N00652153EA0111401113000
    +B1706254555662N00652151EA0110801110000
    +B1706284555653N00652133EA0110701106000
    +B1706314555649N00652115EA0110201103000
    +B1706344555643N00652093EA0109901100000
    +B1706374555638N00652074EA0109501097000
    +B1706404555629N00652057EA0109401094000
    +B1706434555619N00652044EA0108801091000
    +B1706464555606N00652034EA0108601087000
    +B1706494555596N00652023EA0108301084000
    +B1706524555590N00652013EA0108401081000
    +B1706554555587N00652011EA0108601080000
    +B1706584555586N00652010EA0108601078000
    +B1707014555587N00652011EA0108401077000
    +B1707044555587N00652011EA0108401076000
    +B1707074555587N00652010EA0108501076000
    +B1707104555588N00652008EA0108501076000
    +B1707144555588N00652006EA0108501075000
    +B1707174555588N00652006EA0108401074000
    +B1707204555588N00652006EA0108401072000
    +B1707234555589N00652005EA0108401072000
    +B1707264555589N00652004EA0108401072000
    +B1707294555589N00652004EA0108401071000
    +B1707324555589N00652005EA0108501069000
    +B1707354555588N00652005EA0108401067000
    +B1707384555588N00652005EA0108401065000
    +B1707414555588N00652006EA0108401063000
    +B1707444555588N00652006EA0108501062000
    +B1707474555588N00652007EA0108401060000
    +B1707504555588N00652007EA0108401059000
    +B1707534555588N00652007EA0108401058000
    +GAB890A77AFE5CE63979AF6B1BED7F07D
    +G62BB282E44D63A1149EF2F5E8AF6F2F1
    +GEC14381987B15F81003EDE1E01A47843
    +G60189641B00B00800019000000000000
    diff --git a/mapbox-gl/examples/data/igc/Ulrich-Prinz.igc b/mapbox-gl/examples/data/igc/Ulrich-Prinz.igc
    new file mode 100644
    index 0000000000..e9cc114fa6
    --- /dev/null
    +++ b/mapbox-gl/examples/data/igc/Ulrich-Prinz.igc
    @@ -0,0 +1,4611 @@
    +ABRA02676
    +HFDTE190411
    +HFFXA100
    +HFPLTPILOT:Ulrich Prinz      
    +HFGTYGLIDERTYPE:Boomerang 8       
    +HFGIDGLIDERID:www.up0.de        
    +HFDTM100GPSDATUM:WGS84
    +HFGPSGPS:FURUNO GH-80
    +HFRFWFIRMWAREVERSION:2.26
    +HFRHWHARDWAREVERSION:1.00
    +HFFTYFRTYPE:BRAUNIGER,COMPEO
    +HOCIDCOMPETITIONID: DHV-XC-2011
    +HOCCLCOMPETITION CLASS: Paraglider open
    +HOSITSite: Planpraz (Chamonix)
    +I013638TAS
    +B0851384556194N00651066EA0206702042000
    +B0851454556194N00651066EA0206702043000
    +B0851524556194N00651065EA0206702044000
    +B0851594556194N00651065EA0206702044000
    +B0852064556194N00651065EA0206702045000
    +B0852134556195N00651065EA0206702045000
    +B0852204556195N00651065EA0206702046000
    +B0852274556195N00651065EA0206702046000
    +B0852344556195N00651065EA0206702046000
    +B0852414556194N00651064EA0206702046000
    +B0852484556194N00651064EA0206802045000
    +B0852554556194N00651063EA0206802045000
    +B0853024556194N00651063EA0206802044000
    +B0853094556194N00651063EA0206802044000
    +B0853164556195N00651063EA0206702043000
    +B0853234556195N00651064EA0206802043000
    +B0853304556195N00651065EA0206802043000
    +B0853374556195N00651064EA0206802043000
    +B0853444556195N00651064EA0206702043000
    +B0853514556195N00651064EA0206802043000
    +B0853584556195N00651064EA0206802043000
    +B0854054556195N00651064EA0206802042000
    +B0854124556195N00651064EA0206702042000
    +B0854194556195N00651065EA0206802042000
    +B0854264556196N00651065EA0206802041000
    +B0854334556192N00651063EA0206502040000
    +B0854404556177N00651065EA0205802040000
    +B0854474556147N00651059EA0206202038000
    +B0854544556137N00651026EA0207902037000
    +B0855014556141N00650975EA0210202040000
    +B0855094556148N00650921EA0212502047000
    +B0855164556167N00650965EA0214502059000
    +B0855234556155N00650950EA0216302074000
    +B0855304556187N00650964EA0217702091000
    +B0855374556159N00650953EA0219702099000
    +B0855444556194N00650925EA0221302115000
    +B0855514556189N00650967EA0222302132000
    +B0855584556178N00650918EA0224302148000
    +B0856054556207N00650939EA0225202165000
    +B0856124556171N00650924EA0227102179000
    +B0856194556196N00650883EA0228402192000
    +B0856264556187N00650918EA0229902207000
    +B0856334556169N00650867EA0231402223000
    +B0856404556200N00650870EA0232702239000
    +B0856474556176N00650877EA0234302255000
    +B0856544556189N00650826EA0235602271000
    +B0857014556205N00650854EA0237202286000
    +B0857084556187N00650820EA0238802301000
    +B0857154556220N00650799EA0239702317000
    +B0857234556212N00650841EA0241802334000
    +B0857304556193N00650803EA0242902348000
    +B0857374556226N00650802EA0244602363000
    +B0857444556201N00650822EA0245802379000
    +B0857514556205N00650771EA0246302393000
    +B0857584556231N00650801EA0247702407000
    +B0858054556204N00650821EA0249102420000
    +B0858124556210N00650786EA0249502429000
    +B0858194556209N00650819EA0250402440000
    +B0858264556177N00650791EA0251202447000
    +B0858334556140N00650759EA0251802456000
    +B0858404556101N00650730EA0252702462000
    +B0858474556060N00650708EA0253102470000
    +B0858544556026N00650675EA0251002477000
    +B0859014555989N00650630EA0249602480000
    +B0859084555948N00650579EA0249602480000
    +B0859154555913N00650531EA0249002479000
    +B0859224555879N00650478EA0249202478000
    +B0859294555841N00650422EA0248902476000
    +B0859364555800N00650371EA0249202474000
    +B0859434555761N00650325EA0248202473000
    +B0859504555723N00650273EA0247302469000
    +B0859574555683N00650221EA0246002465000
    +B0900044555642N00650168EA0245302459000
    +B0900114555598N00650116EA0245702451000
    +B0900184555571N00650057EA0247902449000
    +B0900254555558N00650063EA0249102449000
    +B0900324555570N00650009EA0251002452000
    +B0900394555546N00649966EV0252202456000
    +B0900464555549N00649970EA0255402461000
    +B0900534555558N00649905EA0257602469000
    +B0901004555525N00649880EA0259502482000
    +B0901074555544N00649876EA0262002498000
    +B0901144555518N00649858EA0264002503000
    +B0901214555527N00649829EA0266602507000
    +B0901284555512N00649846EA0268802525000
    +B0901354555530N00649806EA0271002547000
    +B0901424555512N00649818EA0272902571000
    +B0901494555536N00649782EA0275102599000
    +B0901564555502N00649758EA0277102624000
    +B0902034555520N00649767EA0279002635000
    +B0902104555494N00649736EA0280702649000
    +B0902174555511N00649735EA0282202662000
    +B0902244555495N00649662EA0283802689000
    +B0902314555467N00649588EA0285902717000
    +B0902384555448N00649519EA0285302743000
    +B0902454555419N00649449EA0285402763000
    +B0902524555393N00649396EA0284002779000
    +B0902594555374N00649335EA0282102787000
    +B0903064555350N00649281EA0279902787000
    +B0903144555314N00649214EA0278202784000
    +B0903214555289N00649151EA0276302778000
    +B0903284555265N00649074EA0273402768000
    +B0903354555256N00648995EA0272302755000
    +B0903424555242N00648924EA0271902743000
    +B0903494555237N00648855EA0275202735000
    +B0903564555269N00648839EA0278002735000
    +B0904034555246N00648878EA0281102739000
    +B0904104555229N00648824EA0284402748000
    +B0904174555235N00648758EA0285702763000
    +B0904244555220N00648697EA0287302780000
    +B0904314555205N00648643EA0288202796000
    +B0904384555183N00648584EA0289302811000
    +B0904454555160N00648521EA0289902825000
    +B0904524555140N00648462EA0289202836000
    +B0904594555115N00648405EA0288702844000
    +B0905064555089N00648347EA0288102849000
    +B0905134555064N00648289EA0287502851000
    +B0905204555028N00648242EA0285502850000
    +B0905274554993N00648179EA0283002844000
    +B0905344554956N00648123EA0281002837000
    +B0905414554915N00648067EA0278602825000
    +B0905484554879N00648006EA0276802809000
    +B0905554554835N00647959EA0275202793000
    +B0906024554788N00647911EA0273702776000
    +B0906094554747N00647852EA0272402760000
    +B0906164554706N00647789EA0271202745000
    +B0906234554671N00647736EA0270102731000
    +B0906304554631N00647692EA0269002716000
    +B0906374554593N00647649EA0267902708000
    +B0906444554556N00647591EA0266602694000
    +B0906514554523N00647528EA0266102681000
    +B0906584554491N00647463EA0265502669000
    +B0907054554470N00647397EA0264302661000
    +B0907124554450N00647331EA0263402652000
    +B0907194554431N00647268EA0262802643000
    +B0907264554404N00647208EA0262402637000
    +B0907334554371N00647163EA0261702628000
    +B0907404554336N00647118EA0260102618000
    +B0907474554308N00647057EA0259202608000
    +B0907544554285N00646996EA0258702599000
    +B0908014554256N00646942EA0257302589000
    +B0908084554225N00646890EA0256402579000
    +B0908154554195N00646832EA0256102569000
    +B0908224554159N00646779EA0255902562000
    +B0908294554127N00646719EA0255602555000
    +B0908364554102N00646652EA0254902549000
    +B0908434554071N00646587EA0254002543000
    +B0908504554039N00646524EA0253202537000
    +B0908574554006N00646462EA0252202530000
    +B0909044553970N00646408EA0251302522000
    +B0909124553930N00646348EA0250102513000
    +B0909194553897N00646291EA0249202504000
    +B0909264553867N00646230EA0248302495000
    +B0909334553835N00646172EA0247202486000
    +B0909404553802N00646115EA0245902475000
    +B0909474553772N00646055EA0245002465000
    +B0909544553739N00646000EA0244202455000
    +B0910014553707N00645947EA0243202446000
    +B0910084553680N00645888EA0242202437000
    +B0910154553660N00645821EA0241502429000
    +B0910224553640N00645754EA0240502421000
    +B0910294553625N00645687EA0239802413000
    +B0910364553607N00645622EA0239002404000
    +B0910434553591N00645556EA0238302396000
    +B0910504553573N00645496EA0237702387000
    +B0910574553541N00645447EA0237102378000
    +B0911044553513N00645393EA0236402370000
    +B0911114553503N00645333EA0235702363000
    +B0911184553486N00645282EA0235102357000
    +B0911254553446N00645254EA0234502350000
    +B0911324553407N00645221EA0234102345000
    +B0911394553361N00645196EA0233402340000
    +B0911464553313N00645166EA0234002337000
    +B0911534553278N00645129EA0235002334000
    +B0912004553246N00645101EA0236102334000
    +B0912074553212N00645061EA0236702335000
    +B0912144553180N00645081EA0236802337000
    +B0912214553189N00645056EA0237202337000
    +B0912284553150N00645045EA0237402339000
    +B0912354553154N00645096EA0238102341000
    +B0912424553186N00645075EA0239002345000
    +B0912494553176N00645032EA0239102349000
    +B0912564553185N00645042EA0240002352000
    +B0913034553210N00645034EA0239702356000
    +B0913104553191N00644995EA0240602359000
    +B0913174553211N00645013EA0241602361000
    +B0913244553202N00644962EA0242702363000
    +B0913314553199N00644954EA0244002367000
    +B0913384553203N00644908EA0245202373000
    +B0913454553195N00644920EA0246302376000
    +B0913524553198N00644874EA0248502382000
    +B0913594553180N00644891EA0248802394000
    +B0914064553208N00644864EA0250402401000
    +B0914134553181N00644860EA0251202413000
    +B0914204553197N00644880EA0251802424000
    +B0914274553195N00644824EA0251902435000
    +B0914344553171N00644855EA0253002449000
    +B0914414553191N00644880EA0253302461000
    +B0914484553227N00644849EA0254402473000
    +B0914554553207N00644827EA0255702477000
    +B0915024553221N00644849EA0255702488000
    +B0915094553212N00644800EA0256502496000
    +B0915164553203N00644829EA0256802504000
    +B0915244553224N00644774EA0257502510000
    +B0915314553190N00644770EA0258302518000
    +B0915384553214N00644773EA0258302526000
    +B0915454553201N00644715EA0258902534000
    +B0915524553182N00644753EA0258502540000
    +B0915594553190N00644792EA0259402545000
    +B0916064553191N00644836EA0260802551000
    +B0916134553203N00644799EA0262002555000
    +B0916204553203N00644815EA0263202560000
    +B0916274553182N00644758EA0264502566000
    +B0916344553135N00644731EA0264802573000
    +B0916414553087N00644714EA0265002584000
    +B0916484553039N00644696EA0264002592000
    +B0916554552992N00644676EA0263702596000
    +B0917024552944N00644652EA0264802600000
    +B0917094552901N00644622EA0264202604000
    +B0917164552862N00644569EA0264602607000
    +B0917234552806N00644531EA0263202610000
    +B0917304552749N00644504EA0262702610000
    +B0917374552692N00644464EA0260502607000
    +B0917444552629N00644424EA0260002601000
    +B0917514552587N00644401EA0258302593000
    +B0917584552549N00644376EA0260102588000
    +B0918054552552N00644332EA0259302586000
    +B0918124552553N00644373EA0260102585000
    +B0918194552520N00644372EA0261102585000
    +B0918264552534N00644364EA0262002586000
    +B0918334552516N00644371EA0262802588000
    +B0918404552483N00644319EA0263802595000
    +B0918474552444N00644267EA0264602601000
    +B0918544552405N00644219EA0265102607000
    +B0919014552360N00644177EA0265202611000
    +B0919084552301N00644133EA0262702615000
    +B0919154552235N00644082EA0260402613000
    +B0919224552170N00644030EA0258202607000
    +B0919294552109N00643982EA0255602597000
    +B0919364552051N00643931EA0253702584000
    +B0919434551992N00643881EA0251302568000
    +B0919504551933N00643832EA0249002551000
    +B0919574551882N00643781EA0247702533000
    +B0920044551829N00643720EA0247002515000
    +B0920114551773N00643664EA0246202499000
    +B0920184551717N00643607EA0245702485000
    +B0920254551656N00643563EA0244702473000
    +B0920324551593N00643526EA0243702461000
    +B0920394551531N00643486EA0242702450000
    +B0920464551471N00643444EA0241502439000
    +B0920534551414N00643401EA0240802428000
    +B0921004551357N00643364EA0239802417000
    +B0921074551298N00643325EA0238802406000
    +B0921154551233N00643277EA0237602394000
    +B0921224551177N00643235EA0236702384000
    +B0921294551121N00643190EA0235602374000
    +B0921364551068N00643149EA0234802364000
    +B0921434551012N00643109EA0233602353000
    +B0921504550957N00643067EA0232402343000
    +B0921574550898N00643031EA0230802332000
    +B0922044550840N00643005EA0229402321000
    +B0922114550786N00642967EA0227702309000
    +B0922184550731N00642931EA0225902296000
    +B0922254550677N00642905EA0224602282000
    +B0922324550627N00642887EA0223102267000
    +B0922394550576N00642860EA0221302252000
    +B0922464550522N00642816EA0219602236000
    +B0922534550467N00642769EA0217902221000
    +B0923004550411N00642725EA0216002205000
    +B0923074550352N00642682EA0213602188000
    +B0923144550293N00642636EA0211402169000
    +B0923214550238N00642595EA0209702150000
    +B0923284550182N00642550EA0208102133000
    +B0923354550124N00642506EA0206802116000
    +B0923424550064N00642466EA0205902100000
    +B0923494550009N00642416EA0205002085000
    +B0923564549951N00642374EA0204302072000
    +B0924034549888N00642349EA0203802060000
    +B0924104549826N00642319EA0202602048000
    +B0924174549776N00642290EA0202602037000
    +B0924244549727N00642248EA0204302030000
    +B0924314549739N00642203EA0205702028000
    +B0924384549729N00642228EA0207102029000
    +B0924454549716N00642165EA0208802032000
    +B0924524549739N00642176EA0210502039000
    +B0924594549704N00642173EA0211702046000
    +B0925064549721N00642118EA0213902056000
    +B0925134549735N00642152EA0215502069000
    +B0925204549699N00642149EA0216902085000
    +B0925274549704N00642094EA0218702100000
    +B0925344549692N00642122EA0220402115000
    +B0925414549675N00642064EA0222002122000
    +B0925484549693N00642070EA0223002133000
    +B0925554549652N00642086EA0224702150000
    +B0926024549647N00642030EA0226102158000
    +B0926094549659N00642052EA0227002176000
    +B0926164549614N00642057EA0228602193000
    +B0926234549614N00642006EA0229202205000
    +B0926304549595N00642039EA0229902221000
    +B0926374549543N00642016EA0231602236000
    +B0926444549545N00641956EA0232102250000
    +B0926514549549N00641981EA0232802263000
    +B0926584549501N00641965EA0234502275000
    +B0927054549498N00641903EA0235202287000
    +B0927124549524N00641908EA0236102299000
    +B0927194549549N00641921EA0235902310000
    +B0927264549569N00641949EA0235702318000
    +B0927344549598N00641970EA0236602325000
    +B0927414549627N00641975EA0238502334000
    +B0927484549611N00642008EA0240602344000
    +B0927554549591N00641955EA0242502347000
    +B0928024549621N00641923EA0244602362000
    +B0928094549619N00641962EA0246102376000
    +B0928164549587N00641917EA0248702393000
    +B0928234549613N00641920EA0249702410000
    +B0928304549586N00641965EA0250802421000
    +B0928374549565N00641915EA0253302438000
    +B0928444549594N00641900EA0253802456000
    +B0928514549586N00641953EA0253702470000
    +B0928584549545N00641942EA0255102482000
    +B0929054549528N00641904EA0256402492000
    +B0929124549531N00641928EA0257002494000
    +B0929194549531N00641904EA0257502501000
    +B0929264549517N00641866EA0259002514000
    +B0929334549524N00641827EA0259002528000
    +B0929404549534N00641778EA0258402539000
    +B0929474549549N00641792EA0258102544000
    +B0929544549517N00641834EA0257302547000
    +B0930014549516N00641887EA0256202548000
    +B0930084549537N00641877EA0255002547000
    +B0930154549528N00641822EA0254202544000
    +B0930224549472N00641796EA0253402538000
    +B0930294549425N00641774EA0253702533000
    +B0930364549383N00641734EA0254802530000
    +B0930434549384N00641684EA0254502529000
    +B0930504549400N00641695EA0255902529000
    +B0930574549405N00641721EA0256502530000
    +B0931044549399N00641749EA0256802532000
    +B0931114549416N00641755EA0254202533000
    +B0931184549442N00641760EA0253302530000
    +B0931254549468N00641769EA0252902526000
    +B0931324549498N00641767EA0253302522000
    +B0931394549524N00641774EA0254602522000
    +B0931464549550N00641777EA0255602523000
    +B0931534549578N00641772EA0255802526000
    +B0932004549608N00641754EA0255602529000
    +B0932074549641N00641736EA0255302530000
    +B0932144549670N00641702EA0255302531000
    +B0932214549697N00641667EA0255002532000
    +B0932284549729N00641642EA0255302532000
    +B0932354549765N00641640EA0255502533000
    +B0932424549804N00641653EA0256002534000
    +B0932494549835N00641681EA0255302535000
    +B0932564549832N00641733EA0255402536000
    +B0933034549798N00641766EA0254902535000
    +B0933104549753N00641751EA0253702533000
    +B0933174549706N00641735EA0252902529000
    +B0933244549660N00641717EA0251902522000
    +B0933324549604N00641709EA0251002515000
    +B0933394549552N00641713EA0250102508000
    +B0933464549499N00641708EA0249902501000
    +B0933534549443N00641676EA0250602497000
    +B0934004549396N00641666EA0251302494000
    +B0934074549401N00641649EA0251402493000
    +B0934144549410N00641595EA0251302492000
    +B0934214549409N00641624EA0253002493000
    +B0934284549357N00641626EA0252202493000
    +B0934354549303N00641586EA0253502494000
    +B0934424549258N00641546EA0252502495000
    +B0934494549227N00641486EA0252802497000
    +B0934564549198N00641444EA0252402501000
    +B0935034549164N00641406EA0253202503000
    +B0935104549136N00641365EA0251902504000
    +B0935174549101N00641317EA0252102502000
    +B0935244549067N00641274EA0253402503000
    +B0935314549032N00641239EA0254202507000
    +B0935384548995N00641211EA0255502511000
    +B0935454548974N00641155EA0256902517000
    +B0935524548994N00641156EA0258202524000
    +B0935594548982N00641195EA0259402534000
    +B0936064548951N00641135EA0259602544000
    +B0936134548920N00641067EA0261702553000
    +B0936204548905N00641078EA0261002562000
    +B0936274548901N00641005EA0262102570000
    +B0936344548870N00640944EA0263102578000
    +B0936414548848N00640877EA0264602587000
    +B0936484548826N00640814EA0265302597000
    +B0936554548798N00640770EA0265102605000
    +B0937024548761N00640741EA0265702610000
    +B0937094548727N00640714EA0266302615000
    +B0937164548694N00640678EA0266502619000
    +B0937234548660N00640642EA0266202624000
    +B0937304548623N00640601EA0267002629000
    +B0937374548592N00640571EA0268502635000
    +B0937444548603N00640513EA0268602642000
    +B0937514548618N00640545EA0271002650000
    +B0937584548598N00640562EA0272002661000
    +B0938054548611N00640510EA0273602671000
    +B0938124548605N00640526EA0274802682000
    +B0938194548579N00640473EA0275402694000
    +B0938264548603N00640465EA0276802703000
    +B0938334548572N00640485EA0277702713000
    +B0938404548532N00640452EA0277302721000
    +B0938474548495N00640391EA0276402728000
    +B0938544548461N00640341EA0276002732000
    +B0939014548428N00640290EA0275802735000
    +B0939084548403N00640237EA0276102737000
    +B0939154548378N00640185EA0275602738000
    +B0939224548350N00640140EA0275402738000
    +B0939294548325N00640084EA0275502738000
    +B0939374548289N00640017EA0275902738000
    +B0939444548271N00639973EA0277102740000
    +B0939514548248N00639915EA0279102744000
    +B0939584548227N00639861EA0280402752000
    +B0940054548209N00639800EA0281202762000
    +B0940124548191N00639739EA0280402768000
    +B0940194548167N00639661EA0279202771000
    +B0940264548138N00639587EA0277602770000
    +B0940334548112N00639512EA0277102767000
    +B0940404548091N00639434EA0278502764000
    +B0940474548086N00639389EA0278902765000
    +B0940544548071N00639329EA0279202767000
    +B0941014548057N00639267EA0280002769000
    +B0941084548038N00639208EA0280002773000
    +B0941154548018N00639133EA0278702774000
    +B0941224547991N00639057EA0277702772000
    +B0941294547968N00638979EA0276902769000
    +B0941364547945N00638903EA0275502763000
    +B0941434547925N00638822EA0273902756000
    +B0941504547904N00638740EA0272502747000
    +B0941574547885N00638664EA0272502738000
    +B0942044547860N00638600EA0271702729000
    +B0942114547841N00638538EA0270702722000
    +B0942184547827N00638453EA0269702714000
    +B0942254547813N00638378EA0268102705000
    +B0942324547792N00638308EA0267202695000
    +B0942394547766N00638239EA0266202685000
    +B0942464547741N00638166EA0265302675000
    +B0942534547708N00638098EA0265002665000
    +B0943004547676N00638035EA0264702656000
    +B0943074547649N00637981EA0265102650000
    +B0943144547628N00637918EA0264302644000
    +B0943214547612N00637866EA0265102640000
    +B0943284547591N00637811EA0264802637000
    +B0943354547569N00637750EA0264102633000
    +B0943424547549N00637690EA0265302631000
    +B0943494547527N00637652EA0264602631000
    +B0943564547496N00637603EA0264202629000
    +B0944034547467N00637563EA0264002628000
    +B0944104547444N00637514EA0264302628000
    +B0944174547419N00637465EA0263302627000
    +B0944244547398N00637421EA0262802624000
    +B0944314547374N00637369EA0263302621000
    +B0944384547350N00637314EA0263202619000
    +B0944454547324N00637247EA0263702618000
    +B0944524547295N00637187EA0264602618000
    +B0944594547268N00637133EA0263602619000
    +B0945064547238N00637064EA0264802618000
    +B0945134547216N00637013EA0264602620000
    +B0945204547193N00636954EA0264702621000
    +B0945284547172N00636877EA0267302626000
    +B0945354547146N00636828EA0270302636000
    +B0945424547101N00636785EA0272702649000
    +B0945494547060N00636749EA0274502665000
    +B0945564547022N00636705EA0274902679000
    +B0946034546987N00636652EA0272202689000
    +B0946104546950N00636581EA0270002691000
    +B0946174546907N00636515EA0268502688000
    +B0946244546870N00636447EA0267802682000
    +B0946314546840N00636377EA0267802677000
    +B0946384546811N00636311EA0268102673000
    +B0946454546788N00636258EA0269202672000
    +B0946524546760N00636206EA0270702673000
    +B0946594546733N00636167EA0271002676000
    +B0947064546700N00636102EA0271302680000
    +B0947134546672N00636037EA0272602684000
    +B0947204546645N00635981EA0272202688000
    +B0947274546617N00635902EA0271302691000
    +B0947344546586N00635823EA0270002691000
    +B0947414546555N00635755EA0269402689000
    +B0947484546533N00635692EA0272102688000
    +B0947554546517N00635641EA0273802692000
    +B0948024546498N00635589EA0275702700000
    +B0948094546485N00635545EA0276802711000
    +B0948164546477N00635491EA0277002722000
    +B0948234546460N00635429EA0277002729000
    +B0948304546433N00635381EA0275502733000
    +B0948374546393N00635320EA0273602733000
    +B0948444546353N00635261EA0272302730000
    +B0948514546313N00635200EA0270902724000
    +B0948584546276N00635136EA0270402716000
    +B0949054546250N00635080EA0268902709000
    +B0949124546216N00635008EA0267302700000
    +B0949194546188N00634945EA0266702690000
    +B0949264546157N00634873EA0265102679000
    +B0949334546130N00634802EA0263602667000
    +B0949404546095N00634727EA0261902654000
    +B0949474546062N00634645EA0260102641000
    +B0949544546032N00634567EA0260002627000
    +B0950014546004N00634485EA0259502615000
    +B0950084545988N00634423EA0259702606000
    +B0950154545965N00634351EA0259402598000
    +B0950224545944N00634306EA0260502594000
    +B0950294545925N00634247EA0261202592000
    +B0950364545903N00634189EA0260302591000
    +B0950434545880N00634120EA0259602589000
    +B0950504545867N00634064EA0262002588000
    +B0950574545849N00634018EA0263202592000
    +B0951044545826N00633961EA0262902597000
    +B0951114545804N00633892EA0262902601000
    +B0951194545776N00633807EA0261702603000
    +B0951264545749N00633741EA0260902601000
    +B0951334545727N00633677EA0261502599000
    +B0951404545706N00633619EA0260702598000
    +B0951474545688N00633549EA0261702597000
    +B0951544545669N00633480EA0260702597000
    +B0952014545643N00633407EA0260502594000
    +B0952084545612N00633339EA0260802592000
    +B0952154545581N00633270EA0261302592000
    +B0952224545564N00633216EA0261202593000
    +B0952294545539N00633166EA0261502594000
    +B0952364545517N00633116EA0259002593000
    +B0952434545492N00633044EA0257602587000
    +B0952504545464N00632972EA0256602579000
    +B0952574545437N00632910EA0255102570000
    +B0953044545407N00632849EA0253602559000
    +B0953114545374N00632790EA0252302548000
    +B0953184545341N00632728EA0251002536000
    +B0953254545315N00632658EA0249402523000
    +B0953324545286N00632589EA0247002510000
    +B0953394545261N00632504EA0245002495000
    +B0953464545241N00632427EA0243602479000
    +B0953534545210N00632360EA0241802463000
    +B0954004545179N00632299EA0240102446000
    +B0954074545155N00632239EA0239302430000
    +B0954144545133N00632180EA0238602416000
    +B0954214545110N00632114EA0238602403000
    +B0954284545089N00632065EA0239402395000
    +B0954354545066N00632020EA0239802389000
    +B0954424545048N00631983EA0239702386000
    +B0954494545035N00631939EA0240202384000
    +B0954564545018N00631893EA0241702386000
    +B0955034545000N00631851EA0243302390000
    +B0955104544987N00631816EA0244702397000
    +B0955174544973N00631776EA0247002406000
    +B0955244544966N00631723EA0250202418000
    +B0955314544990N00631697EA0251902433000
    +B0955384544972N00631726EA0254202442000
    +B0955454544959N00631682EA0255202458000
    +B0955524544958N00631620EA0255302474000
    +B0955594544953N00631555EA0256402488000
    +B0956064544961N00631502EA0256602502000
    +B0956134544973N00631451EA0256402513000
    +B0956204544987N00631401EA0256102521000
    +B0956274544998N00631345EA0256002526000
    +B0956344545002N00631277EA0256302530000
    +B0956414545003N00631205EA0256202532000
    +B0956484544995N00631136EA0256602534000
    +B0956554544994N00631080EA0258002538000
    +B0957024544997N00631026EA0258802544000
    +B0957104544994N00630962EA0261002553000
    +B0957174544991N00630911EA0263002565000
    +B0957244544980N00630854EA0263702577000
    +B0957314544965N00630789EA0263802588000
    +B0957384544954N00630730EA0263902597000
    +B0957454544943N00630668EA0262302601000
    +B0957524544936N00630585EA0260502601000
    +B0957594544920N00630501EA0258802596000
    +B0958064544905N00630423EA0257102589000
    +B0958134544892N00630357EA0259402584000
    +B0958204544877N00630313EA0260902583000
    +B0958274544864N00630270EA0262602585000
    +B0958344544842N00630277EA0263402591000
    +B0958414544866N00630252EA0264702594000
    +B0958484544865N00630196EA0265602601000
    +B0958554544856N00630140EA0266402610000
    +B0959024544842N00630091EA0267202619000
    +B0959094544827N00630045EA0268402629000
    +B0959164544808N00629996EA0268202637000
    +B0959234544786N00629943EA0266702642000
    +B0959304544757N00629892EA0264302643000
    +B0959374544726N00629819EA0262902638000
    +B0959444544702N00629744EA0263202632000
    +B0959514544690N00629687EA0265402630000
    +B0959584544684N00629625EA0268202634000
    +B1000054544689N00629569EA0267802640000
    +B1000124544699N00629504EA0267702644000
    +B1000194544702N00629443EA0267802648000
    +B1000264544707N00629369EA0268502650000
    +B1000334544711N00629290EA0269102654000
    +B1000404544709N00629220EA0270302658000
    +B1000474544696N00629153EA0270702664000
    +B1000544544693N00629089EA0270702669000
    +B1001014544688N00629025EA0269402673000
    +B1001084544683N00628971EA0268302674000
    +B1001154544664N00628922EA0266202671000
    +B1001224544635N00628875EA0264602664000
    +B1001294544606N00628830EA0263302656000
    +B1001364544577N00628780EA0262202647000
    +B1001434544553N00628726EA0262102639000
    +B1001504544531N00628673EA0261702630000
    +B1001574544506N00628623EA0261202623000
    +B1002044544484N00628570EA0260602616000
    +B1002114544463N00628514EA0260302609000
    +B1002184544444N00628454EA0259702603000
    +B1002254544426N00628393EA0258902596000
    +B1002324544403N00628332EA0258202591000
    +B1002394544380N00628270EA0257302584000
    +B1002464544364N00628207EA0256302575000
    +B1002534544348N00628141EA0255202566000
    +B1003014544328N00628065EA0254302556000
    +B1003084544313N00627998EA0253402550000
    +B1003154544306N00627926EA0252602547000
    +B1003224544301N00627856EA0251602545000
    +B1003294544293N00627787EA0250402536000
    +B1003364544283N00627716EA0249902526000
    +B1003434544274N00627644EA0249102516000
    +B1003504544261N00627574EA0248202506000
    +B1003574544252N00627501EA0247302495000
    +B1004044544240N00627428EA0246702484000
    +B1004114544231N00627356EA0246102474000
    +B1004184544221N00627286EA0245302465000
    +B1004254544208N00627217EA0244602457000
    +B1004324544195N00627148EA0244102450000
    +B1004394544185N00627079EA0243402443000
    +B1004464544170N00627013EA0242602436000
    +B1004534544163N00626941EA0241602428000
    +B1005004544156N00626868EA0240702419000
    +B1005074544150N00626797EA0240002410000
    +B1005144544140N00626729EA0239002402000
    +B1005214544125N00626659EA0237702392000
    +B1005284544111N00626588EA0236302382000
    +B1005354544099N00626513EA0235402372000
    +B1005424544095N00626434EA0234502362000
    +B1005494544094N00626354EA0233202352000
    +B1005564544088N00626273EA0231802344000
    +B1006034544078N00626184EA0230602334000
    +B1006104544066N00626091EA0229602323000
    +B1006174544050N00626003EA0228502316000
    +B1006244544034N00625913EA0227502305000
    +B1006314544019N00625823EA0226302294000
    +B1006384544000N00625734EA0225002282000
    +B1006454543983N00625646EA0224002271000
    +B1006524543970N00625557EA0222602258000
    +B1006594543954N00625471EA0221702245000
    +B1007064543940N00625384EA0220502237000
    +B1007134543929N00625299EA0219202226000
    +B1007204543917N00625213EA0218102214000
    +B1007274543904N00625123EA0217002201000
    +B1007344543888N00625035EA0215702187000
    +B1007414543873N00624946EA0214702174000
    +B1007484543858N00624858EA0213702161000
    +B1007554543841N00624770EA0212702149000
    +B1008024543824N00624682EA0211702137000
    +B1008094543806N00624596EA0210602125000
    +B1008164543787N00624511EA0209302113000
    +B1008234543767N00624428EA0208302102000
    +B1008304543750N00624344EA0207302092000
    +B1008384543731N00624248EA0206302081000
    +B1008454543716N00624166EA0205302072000
    +B1008524543701N00624084EA0204302062000
    +B1008594543688N00624001EA0203302051000
    +B1009064543676N00623917EA0202202041000
    +B1009134543661N00623834EA0201302031000
    +B1009204543646N00623750EA0200402020000
    +B1009274543630N00623668EA0199402010000
    +B1009344543617N00623584EA0198402000000
    +B1009414543605N00623502EA0197501991000
    +B1009484543591N00623420EA0196601981000
    +B1009554543578N00623338EA0195701973000
    +B1010024543565N00623257EA0194701963000
    +B1010094543552N00623176EA0193601953000
    +B1010164543539N00623097EA0192501943000
    +B1010234543518N00623020EA0191501933000
    +B1010304543492N00622945EA0190501922000
    +B1010374543465N00622871EA0189601913000
    +B1010444543441N00622795EA0188501904000
    +B1010514543418N00622720EA0187401894000
    +B1010584543396N00622644EA0186301884000
    +B1011054543377N00622567EA0185101873000
    +B1011124543362N00622487EA0184201862000
    +B1011194543346N00622409EA0182701852000
    +B1011264543329N00622330EA0181401841000
    +B1011334543312N00622251EA0180301830000
    +B1011404543293N00622172EA0179201818000
    +B1011474543275N00622092EA0178001806000
    +B1011544543261N00622012EA0176901794000
    +B1012014543246N00621933EA0175701782000
    +B1012084543225N00621858EA0174201770000
    +B1012154543202N00621784EA0172701757000
    +B1012224543181N00621711EA0171501743000
    +B1012294543166N00621647EA0170701730000
    +B1012364543164N00621580EA0170301719000
    +B1012434543159N00621524EA0171301712000
    +B1012504543165N00621464EA0171301709000
    +B1012574543191N00621416EA0170301705000
    +B1013044543226N00621380EA0169301700000
    +B1013114543265N00621345EA0168301693000
    +B1013184543289N00621292EA0167601686000
    +B1013254543295N00621230EA0166701678000
    +B1013324543278N00621177EA0165701670000
    +B1013394543244N00621146EA0164901662000
    +B1013464543209N00621119EA0165401655000
    +B1013534543175N00621095EA0165801651000
    +B1014004543146N00621066EA0165401649000
    +B1014074543108N00621038EA0165501646000
    +B1014144543085N00620998EA0166501644000
    +B1014214543066N00620954EA0167401644000
    +B1014284543054N00620905EA0168901647000
    +B1014354543042N00620863EA0168301650000
    +B1014424543011N00620839EA0167801653000
    +B1014504542970N00620813EA0166801656000
    +B1014574542934N00620794EA0167501657000
    +B1015044542902N00620772EA0167201658000
    +B1015114542865N00620760EA0166801658000
    +B1015184542837N00620747EA0167801658000
    +B1015254542812N00620731EA0168401660000
    +B1015324542789N00620707EA0168501662000
    +B1015394542763N00620677EA0169301664000
    +B1015464542743N00620642EA0169701666000
    +B1015534542714N00620608EA0170901669000
    +B1016004542689N00620571EA0172301675000
    +B1016074542679N00620586EA0173601683000
    +B1016144542714N00620588EA0174201691000
    +B1016214542699N00620578EA0175001697000
    +B1016284542682N00620532EA0176401705000
    +B1016354542655N00620508EA0177101714000
    +B1016424542624N00620486EA0177101721000
    +B1016494542584N00620475EA0176001725000
    +B1016564542545N00620473EA0175701728000
    +B1017034542563N00620500EA0175601729000
    +B1017104542603N00620498EA0176001731000
    +B1017174542646N00620500EA0177801734000
    +B1017244542664N00620532EA0177301738000
    +B1017314542642N00620499EA0178201743000
    +B1017384542618N00620497EA0178701748000
    +B1017454542647N00620522EA0179001753000
    +B1017524542683N00620520EA0180401759000
    +B1017594542712N00620538EA0182401767000
    +B1018064542695N00620563EA0184701777000
    +B1018134542687N00620519EA0186401789000
    +B1018204542711N00620534EA0188701802000
    +B1018274542683N00620522EA0191001813000
    +B1018344542705N00620497EA0193601830000
    +B1018414542688N00620511EA0195901845000
    +B1018484542705N00620468EA0198601864000
    +B1018554542700N00620501EA0201401884000
    +B1019024542711N00620462EA0203701902000
    +B1019094542695N00620478EA0206501919000
    +B1019164542707N00620432EA0208501935000
    +B1019234542700N00620469EA0211701955000
    +B1019304542658N00620460EA0214701982000
    +B1019374542622N00620447EA0215602011000
    +B1019444542581N00620419EA0216102038000
    +B1019514542533N00620378EA0216102058000
    +B1019584542499N00620348EA0218002076000
    +B1020054542463N00620323EA0217102094000
    +B1020124542418N00620293EA0217602108000
    +B1020194542376N00620261EA0218502119000
    +B1020264542333N00620235EA0218202128000
    +B1020344542286N00620199EA0217702136000
    +B1020414542242N00620168EA0216702140000
    +B1020484542189N00620128EA0214902139000
    +B1020554542148N00620094EA0215102136000
    +B1021024542109N00620080EA0216302135000
    +B1021094542072N00620072EA0215902136000
    +B1021164542030N00620055EA0214902136000
    +B1021234541989N00620034EA0213502133000
    +B1021304541941N00619998EA0211602128000
    +B1021374541890N00619963EA0210302119000
    +B1021444541843N00619928EA0209302110000
    +B1021514541796N00619891EA0207902100000
    +B1021584541752N00619854EA0207602089000
    +B1022054541710N00619832EA0208102080000
    +B1022124541671N00619816EA0209402076000
    +B1022194541671N00619770EA0210102075000
    +B1022264541700N00619792EA0211402077000
    +B1022334541690N00619838EA0212602082000
    +B1022404541651N00619825EA0214802089000
    +B1022474541671N00619796EA0215602098000
    +B1022544541674N00619838EA0216802107000
    +B1023014541639N00619821EA0218202116000
    +B1023084541656N00619781EA0218802125000
    +B1023154541677N00619814EA0220202130000
    +B1023224541656N00619846EA0220702139000
    +B1023294541615N00619832EA0222002150000
    +B1023364541583N00619806EA0222302161000
    +B1023434541562N00619754EA0223502171000
    +B1023504541536N00619705EA0225002181000
    +B1023574541535N00619662EA0225202193000
    +B1024044541558N00619698EA0226902203000
    +B1024114541551N00619736EA0228302211000
    +B1024184541543N00619694EA0230002217000
    +B1024254541545N00619718EA0231502226000
    +B1024324541523N00619666EA0232702237000
    +B1024394541502N00619606EA0233402250000
    +B1024464541483N00619547EA0232502262000
    +B1024534541459N00619491EA0230802270000
    +B1025004541432N00619412EA0228302272000
    +B1025074541405N00619330EA0227002269000
    +B1025144541385N00619246EA0225602263000
    +B1025214541360N00619164EA0224402255000
    +B1025284541329N00619087EA0222902247000
    +B1025354541295N00619005EA0220802236000
    +B1025424541262N00618921EA0219502224000
    +B1025494541231N00618845EA0218102210000
    +B1025564541199N00618765EA0216902198000
    +B1026034541164N00618688EA0215702186000
    +B1026104541132N00618609EA0214502174000
    +B1026184541097N00618518EA0212902159000
    +B1026254541067N00618435EA0211502145000
    +B1026324541040N00618357EA0210502131000
    +B1026394541020N00618275EA0209402118000
    +B1026464541004N00618192EA0208102105000
    +B1026534540983N00618112EA0206702092000
    +B1027004540950N00618041EA0205802079000
    +B1027074540921N00617967EA0204702067000
    +B1027144540895N00617892EA0203502055000
    +B1027214540871N00617816EA0202502044000
    +B1027284540846N00617741EA0201402034000
    +B1027354540821N00617665EA0200402023000
    +B1027424540797N00617589EA0199502013000
    +B1027494540773N00617512EA0198602003000
    +B1027564540754N00617432EA0197501992000
    +B1028034540736N00617367EA0196301982000
    +B1028104540716N00617291EA0194401971000
    +B1028174540702N00617206EA0194501960000
    +B1028244540689N00617142EA0194501951000
    +B1028314540673N00617082EA0195401944000
    +B1028384540658N00617024EA0196401941000
    +B1028454540647N00616976EA0197901942000
    +B1028524540682N00616967EA0199101946000
    +B1028594540667N00616983EA0200301950000
    +B1029064540635N00616964EA0202101957000
    +B1029134540604N00616945EA0202201965000
    +B1029204540597N00616897EA0202401972000
    +B1029274540631N00616903EA0203501978000
    +B1029344540616N00616923EA0203301984000
    +B1029414540595N00616876EA0203801990000
    +B1029484540579N00616828EA0203001996000
    +B1029554540546N00616785EA0201601998000
    +B1030024540504N00616738EA0199901997000
    +B1030094540470N00616693EA0201201993000
    +B1030164540469N00616651EA0201601991000
    +B1030234540504N00616653EA0202501993000
    +B1030304540488N00616679EA0203201996000
    +B1030374540453N00616659EA0204302000000
    +B1030444540419N00616622EA0204602005000
    +B1030514540380N00616586EA0204202011000
    +B1030584540340N00616550EA0202602014000
    +B1031054540292N00616507EA0200602011000
    +B1031124540243N00616463EA0198902006000
    +B1031194540194N00616418EA0197301998000
    +B1031264540144N00616375EA0195901988000
    +B1031334540094N00616335EA0194301976000
    +B1031404540043N00616293EA0193001963000
    +B1031474539992N00616251EA0191501949000
    +B1031544539941N00616213EA0190001935000
    +B1032014539892N00616177EA0188701921000
    +B1032094539837N00616129EA0187601906000
    +B1032164539790N00616096EA0186701892000
    +B1032234539751N00616064EA0187201880000
    +B1032304539715N00616036EA0185901870000
    +B1032374539667N00616008EA0184201860000
    +B1032444539612N00615985EA0182901849000
    +B1032514539555N00615970EA0181701837000
    +B1032584539497N00615954EA0181901826000
    +B1033054539457N00615936EA0182101818000
    +B1033124539414N00615927EA0181001812000
    +B1033194539378N00615918EA0182401807000
    +B1033264539385N00615936EA0183201806000
    +B1033334539380N00615887EA0184201805000
    +B1033404539349N00615890EA0184601806000
    +B1033474539313N00615895EA0183801808000
    +B1033544539271N00615891EA0182501808000
    +B1034014539228N00615889EA0181601805000
    +B1034084539180N00615883EA0181001802000
    +B1034154539134N00615881EA0180801797000
    +B1034224539098N00615871EA0181801794000
    +B1034294539071N00615862EA0182101793000
    +B1034364539039N00615847EA0182101794000
    +B1034434539003N00615853EA0181801795000
    +B1034504538969N00615851EA0180901795000
    +B1034574538930N00615850EA0180401793000
    +B1035044538899N00615822EA0181201791000
    +B1035114538899N00615770EA0181901791000
    +B1035184538904N00615711EA0183301793000
    +B1035254538888N00615695EA0182801796000
    +B1035324538914N00615731EA0184101798000
    +B1035394538904N00615754EA0184701802000
    +B1035464538937N00615731EA0185901808000
    +B1035534538950N00615768EA0187301815000
    +B1036004538942N00615730EA0188801823000
    +B1036074538972N00615738EA0190501833000
    +B1036144538953N00615764EA0190901843000
    +B1036214538931N00615719EA0192201854000
    +B1036284538956N00615689EA0192901865000
    +B1036354538947N00615723EA0195001874000
    +B1036424538931N00615681EA0195501884000
    +B1036494538956N00615692EA0197001894000
    +B1036564538936N00615699EA0198301905000
    +B1037034538954N00615650EA0198701915000
    +B1037104538940N00615684EA0200001923000
    +B1037174538900N00615661EA0199201933000
    +B1037244538860N00615619EA0198801942000
    +B1037314538821N00615580EA0198001948000
    +B1037384538781N00615546EA0196401950000
    +B1037454538733N00615513EA0194501947000
    +B1037524538679N00615478EA0193101941000
    +B1038004538618N00615442EA0191601932000
    +B1038074538564N00615414EA0190101922000
    +B1038144538508N00615388EA0188901909000
    +B1038214538453N00615363EA0187401896000
    +B1038284538398N00615339EA0186601882000
    +B1038354538352N00615320EA0186601872000
    +B1038424538314N00615300EA0187801865000
    +B1038494538273N00615278EA0189601863000
    +B1038564538279N00615221EA0190801866000
    +B1039034538287N00615251EA0191501869000
    +B1039104538248N00615247EA0193401874000
    +B1039174538263N00615208EA0194801882000
    +B1039244538249N00615233EA0196401889000
    +B1039314538249N00615199EA0196701897000
    +B1039384538243N00615226EA0197901908000
    +B1039454538207N00615234EA0198701919000
    +B1039524538166N00615229EA0198901930000
    +B1039594538127N00615214EA0200401940000
    +B1040064538116N00615167EA0200901950000
    +B1040134538138N00615176EA0202301959000
    +B1040204538110N00615191EA0202901968000
    +B1040274538070N00615179EA0203901978000
    +B1040344538030N00615177EA0202001986000
    +B1040414537984N00615165EA0201701989000
    +B1040484537945N00615146EA0201501990000
    +B1040554537909N00615127EA0201401989000
    +B1041024537876N00615103EA0203401991000
    +B1041094537880N00615060EA0204701997000
    +B1041164537886N00615092EA0206702004000
    +B1041234537862N00615069EA0208402013000
    +B1041304537886N00615070EA0209702018000
    +B1041374537855N00615061EA0211602027000
    +B1041444537824N00615020EA0211202040000
    +B1041514537789N00614989EA0211402051000
    +B1041584537756N00614946EA0211502061000
    +B1042054537728N00614900EA0213402070000
    +B1042124537705N00614848EA0214402080000
    +B1042194537695N00614790EA0216602092000
    +B1042264537725N00614766EA0218502105000
    +B1042334537706N00614771EA0220902115000
    +B1042404537722N00614732EA0222502129000
    +B1042474537720N00614767EA0225002145000
    +B1042544537689N00614756EA0225802161000
    +B1043014537663N00614700EA0226902177000
    +B1043084537638N00614649EA0226802192000
    +B1043154537608N00614599EA0226302203000
    +B1043224537581N00614555EA0226002211000
    +B1043294537547N00614510EA0224302216000
    +B1043364537514N00614458EA0225102219000
    +B1043434537483N00614412EA0225502220000
    +B1043514537453N00614368EA0223602221000
    +B1043584537409N00614311EA0221402217000
    +B1044054537360N00614246EA0219802211000
    +B1044124537310N00614186EA0218102202000
    +B1044194537262N00614120EA0216502191000
    +B1044264537215N00614054EA0214402178000
    +B1044334537169N00613983EA0214102164000
    +B1044404537146N00613918EA0215402154000
    +B1044474537123N00613877EA0214902147000
    +B1044544537093N00613841EA0213402140000
    +B1045014537063N00613797EA0215102134000
    +B1045084537041N00613759EA0215102132000
    +B1045154537011N00613728EA0214202131000
    +B1045224536981N00613681EA0212502128000
    +B1045294536947N00613626EA0212802123000
    +B1045364536919N00613586EA0213802120000
    +B1045434536903N00613568EA0214902120000
    +B1045504536880N00613539EA0215402121000
    +B1045574536859N00613488EA0215402124000
    +B1046044536841N00613438EA0215502126000
    +B1046114536820N00613387EA0214602127000
    +B1046184536786N00613337EA0213102126000
    +B1046254536757N00613288EA0213002122000
    +B1046324536738N00613242EA0214302120000
    +B1046394536716N00613204EA0215502121000
    +B1046464536717N00613146EA0218102126000
    +B1046534536744N00613163EA0219002134000
    +B1047004536727N00613172EA0220602141000
    +B1047074536733N00613120EA0222902152000
    +B1047144536734N00613134EA0225002160000
    +B1047214536734N00613087EA0227002173000
    +B1047284536749N00613113EA0228502186000
    +B1047354536726N00613080EA0230802201000
    +B1047424536713N00613034EA0232802220000
    +B1047494536692N00612996EA0235302240000
    +B1047564536679N00613013EA0236402261000
    +B1048034536689N00612978EA0239102278000
    +B1048104536663N00612943EA0240302296000
    +B1048174536637N00612891EA0241602313000
    +B1048244536612N00612836EA0240702329000
    +B1048314536591N00612772EA0237802339000
    +B1048384536565N00612691EA0235902342000
    +B1048454536538N00612612EA0234702341000
    +B1048524536512N00612553EA0236802340000
    +B1048594536489N00612514EA0238302341000
    +B1049064536466N00612467EA0238402345000
    +B1049134536442N00612415EA0237302348000
    +B1049204536409N00612353EA0235202347000
    +B1049284536370N00612272EA0234902341000
    +B1049354536352N00612221EA0234702337000
    +B1049424536326N00612172EA0235002332000
    +B1049494536305N00612129EA0236702331000
    +B1049564536306N00612085EA0238902336000
    +B1050034536322N00612111EA0241302343000
    +B1050104536313N00612083EA0243102352000
    +B1050174536351N00612090EA0245002364000
    +B1050244536333N00612102EA0247002377000
    +B1050314536311N00612062EA0249402394000
    +B1050384536286N00612008EA0248502410000
    +B1050454536261N00611952EA0247002421000
    +B1050524536229N00611902EA0246702427000
    +B1050594536208N00611853EA0247102432000
    +B1051064536187N00611804EA0248302437000
    +B1051134536172N00611757EA0248502443000
    +B1051204536146N00611701EA0248202447000
    +B1051274536116N00611644EA0247902451000
    +B1051344536089N00611588EA0246402452000
    +B1051414536061N00611519EA0245102449000
    +B1051484536031N00611441EA0245802443000
    +B1051554536013N00611380EA0245702439000
    +B1052024535996N00611319EA0246202438000
    +B1052094535978N00611258EA0245902438000
    +B1052164535960N00611191EA0245102439000
    +B1052234535940N00611118EA0243902436000
    +B1052304535917N00611026EA0243102431000
    +B1052374535893N00610938EA0242202425000
    +B1052444535868N00610850EA0241602420000
    +B1052514535847N00610762EA0241202413000
    +B1052584535829N00610683EA0241402407000
    +B1053054535810N00610605EA0241002402000
    +B1053124535791N00610535EA0242302398000
    +B1053194535765N00610480EA0243602398000
    +B1053264535741N00610437EA0247002405000
    +B1053334535715N00610414EA0249002416000
    +B1053404535736N00610431EA0251802430000
    +B1053474535727N00610386EA0253602446000
    +B1053544535684N00610371EA0255202463000
    +B1054014535641N00610355EA0255102479000
    +B1054084535599N00610306EA0255502492000
    +B1054154535577N00610234EA0255202502000
    +B1054224535560N00610149EA0253602508000
    +B1054294535540N00610052EA0251702508000
    +B1054364535520N00609951EA0250502505000
    +B1054434535496N00609852EA0249202499000
    +B1054504535470N00609753EA0248202491000
    +B1054584535441N00609638EA0247402481000
    +B1055054535420N00609542EA0246202475000
    +B1055124535394N00609460EA0245002475000
    +B1055194535367N00609371EA0243502471000
    +B1055264535345N00609270EA0242202457000
    +B1055334535326N00609166EA0240802445000
    +B1055404535304N00609065EA0239902429000
    +B1055474535273N00608971EA0238702414000
    +B1055544535241N00608878EA0237602398000
    +B1056014535208N00608788EA0236202385000
    +B1056084535178N00608697EA0234802371000
    +B1056154535149N00608607EA0233702357000
    +B1056224535119N00608521EA0232502343000
    +B1056294535092N00608431EA0231402330000
    +B1056364535062N00608343EA0230202318000
    +B1056434535026N00608264EA0229102305000
    +B1056504534989N00608185EA0227902293000
    +B1056574534954N00608103EA0226702280000
    +B1057044534918N00608022EA0225302268000
    +B1057114534882N00607941EA0224102254000
    +B1057184534848N00607858EA0222702245000
    +B1057254534817N00607772EA0221302235000
    +B1057324534789N00607684EA0220102223000
    +B1057394534759N00607596EA0218902210000
    +B1057464534727N00607510EA0217702197000
    +B1057534534693N00607427EA0216302184000
    +B1058004534656N00607347EA0215002171000
    +B1058074534618N00607266EA0213902159000
    +B1058144534582N00607183EA0212802147000
    +B1058214534545N00607102EA0211602134000
    +B1058284534508N00607023EA0210602121000
    +B1058354534473N00606946EA0209602109000
    +B1058424534437N00606870EA0208602098000
    +B1058494534401N00606796EA0207702087000
    +B1058564534364N00606723EA0206602076000
    +B1059034534328N00606649EA0205702066000
    +B1059104534302N00606588EA0205002056000
    +B1059174534275N00606525EA0204102055000
    +B1059244534248N00606466EA0203202046000
    +B1059314534220N00606409EA0202502036000
    +B1059384534187N00606361EA0201802026000
    +B1059454534153N00606313EA0201002016000
    +B1059524534125N00606257EA0200202007000
    +B1059594534098N00606201EA0199301999000
    +B1100064534071N00606147EA0198301991000
    +B1100134534044N00606093EA0197301981000
    +B1100204534013N00606040EA0196501972000
    +B1100274533985N00605988EA0195401961000
    +B1100354533948N00605931EA0194601950000
    +B1100424533917N00605883EA0193901940000
    +B1100494533883N00605838EA0193201931000
    +B1100564533849N00605793EA0192501924000
    +B1101034533815N00605745EA0191901917000
    +B1101104533792N00605706EA0192801913000
    +B1101174533808N00605655EA0194301911000
    +B1101244533843N00605666EA0195901913000
    +B1101314533821N00605694EA0197101918000
    +B1101384533808N00605665EA0198001924000
    +B1101454533841N00605673EA0199701932000
    +B1101524533821N00605709EA0200001939000
    +B1101594533786N00605690EA0201001948000
    +B1102064533798N00605642EA0203301958000
    +B1102134533807N00605669EA0204201970000
    +B1102204533776N00605653EA0206301982000
    +B1102274533779N00605607EA0208801996000
    +B1102344533782N00605641EA0209902009000
    +B1102414533745N00605619EA0210502023000
    +B1102484533758N00605591EA0211702034000
    +B1102554533739N00605632EA0211802047000
    +B1103024533699N00605612EA0211902056000
    +B1103094533706N00605560EA0211102064000
    +B1103164533713N00605507EA0209802067000
    +B1103234533698N00605444EA0210202069000
    +B1103304533683N00605385EA0210002069000
    +B1103374533671N00605334EA0210702070000
    +B1103444533648N00605322EA0210402071000
    +B1103514533674N00605307EA0210202070000
    +B1103584533671N00605241EA0211502069000
    +B1104054533661N00605182EA0211402070000
    +B1104124533645N00605124EA0210702072000
    +B1104194533625N00605066EA0209102070000
    +B1104264533607N00605001EA0208102066000
    +B1104334533588N00604938EA0207502062000
    +B1104404533566N00604872EA0207002058000
    +B1104474533546N00604808EA0205802052000
    +B1104544533527N00604743EA0204602045000
    +B1105014533505N00604679EA0204202037000
    +B1105084533483N00604617EA0203302030000
    +B1105154533463N00604556EA0202502022000
    +B1105224533444N00604496EA0201602015000
    +B1105294533421N00604439EA0200902007000
    +B1105364533400N00604376EA0200201999000
    +B1105434533378N00604312EA0199601992000
    +B1105504533356N00604252EA0199001986000
    +B1105574533336N00604188EA0198501979000
    +B1106044533316N00604125EA0198201974000
    +B1106114533295N00604063EA0197801968000
    +B1106184533271N00604003EA0197101963000
    +B1106254533247N00603940EA0196401958000
    +B1106334533216N00603871EA0196301952000
    +B1106404533188N00603820EA0195501948000
    +B1106474533171N00603768EA0195101944000
    +B1106544533160N00603712EA0194001937000
    +B1107014533145N00603661EA0194201932000
    +B1107084533134N00603617EA0193601926000
    +B1107154533116N00603581EA0193401921000
    +B1107224533117N00603533EA0193701916000
    +B1107294533139N00603487EA0194201914000
    +B1107364533178N00603471EA0194401912000
    +B1107434533195N00603508EA0194801912000
    +B1107504533165N00603511EA0195501912000
    +B1107574533170N00603468EA0195901914000
    +B1108044533197N00603496EA0196001916000
    +B1108114533187N00603550EA0197301918000
    +B1108184533169N00603544EA0197701922000
    +B1108254533205N00603542EA0198601927000
    +B1108324533220N00603591EA0199301934000
    +B1108394533190N00603584EA0199901940000
    +B1108464533213N00603579EA0200301946000
    +B1108534533192N00603616EA0200501951000
    +B1109004533172N00603583EA0201201955000
    +B1109074533199N00603562EA0201101960000
    +B1109144533183N00603592EA0200901964000
    +B1109214533150N00603588EA0201201967000
    +B1109284533168N00603547EA0200601969000
    +B1109354533180N00603587EA0200701969000
    +B1109424533152N00603624EA0200401969000
    +B1109494533126N00603594EA0199101969000
    +B1109564533155N00603554EA0198201967000
    +B1110034533199N00603556EA0199701964000
    +B1110104533208N00603589EA0200301964000
    +B1110174533191N00603553EA0201901967000
    +B1110244533222N00603534EA0202401972000
    +B1110314533209N00603570EA0202801976000
    +B1110384533208N00603533EA0203701979000
    +B1110454533219N00603562EA0203701982000
    +B1110524533202N00603531EA0203901986000
    +B1110594533234N00603524EA0204501990000
    +B1111064533221N00603554EA0204701993000
    +B1111134533211N00603512EA0204501996000
    +B1111204533248N00603493EA0205001999000
    +B1111274533264N00603529EA0205202004000
    +B1111344533241N00603559EA0205802009000
    +B1111414533218N00603526EA0206002013000
    +B1111484533221N00603467EA0206602017000
    +B1111554533258N00603473EA0207102021000
    +B1112024533235N00603484EA0208702027000
    +B1112094533253N00603447EA0208902034000
    +B1112164533270N00603489EA0210002040000
    +B1112244533255N00603465EA0210602048000
    +B1112314533288N00603470EA0211002055000
    +B1112384533267N00603474EA0210602061000
    +B1112454533264N00603413EA0211502066000
    +B1112524533299N00603405EA0211902071000
    +B1112594533277N00603399EA0212202075000
    +B1113064533303N00603362EA0212202080000
    +B1113134533304N00603394EA0212502083000
    +B1113204533293N00603351EA0212402086000
    +B1113274533326N00603345EA0211802088000
    +B1113344533316N00603381EA0211802090000
    +B1113414533283N00603379EA0211402090000
    +B1113484533247N00603344EA0210802089000
    +B1113554533212N00603305EA0210302086000
    +B1114024533184N00603267EA0210002083000
    +B1114094533144N00603223EA0209602079000
    +B1114164533116N00603189EA0208502075000
    +B1114234533080N00603140EA0207102069000
    +B1114304533046N00603101EA0206402063000
    +B1114374533014N00603056EA0205102056000
    +B1114444532976N00603016EA0203602047000
    +B1114514532938N00602968EA0202102037000
    +B1114584532894N00602928EA0200702026000
    +B1115054532851N00602883EA0199202013000
    +B1115124532817N00602821EA0197401999000
    +B1115194532785N00602754EA0195101984000
    +B1115264532749N00602675EA0192901968000
    +B1115334532713N00602595EA0190801950000
    +B1115404532673N00602526EA0188701932000
    +B1115474532631N00602460EA0187201916000
    +B1115544532589N00602388EA0185901899000
    +B1116014532550N00602321EA0184801882000
    +B1116084532512N00602257EA0184001865000
    +B1116154532474N00602195EA0183101851000
    +B1116224532437N00602134EA0182001837000
    +B1116294532400N00602072EA0180901824000
    +B1116364532363N00602013EA0179901811000
    +B1116434532325N00601960EA0178901799000
    +B1116504532287N00601914EA0177801788000
    +B1116574532250N00601868EA0176801776000
    +B1117044532207N00601835EA0175801767000
    +B1117114532164N00601811EA0174801758000
    +B1117184532123N00601777EA0173801748000
    +B1117254532082N00601739EA0172801738000
    +B1117324532041N00601695EA0171701727000
    +B1117394532002N00601657EA0170901716000
    +B1117464531963N00601621EA0169901705000
    +B1117534531929N00601577EA0167801694000
    +B1118014531883N00601536EA0167201679000
    +B1118084531842N00601500EA0165601668000
    +B1118154531805N00601460EA0164301656000
    +B1118224531770N00601419EA0163201644000
    +B1118294531733N00601379EA0161601632000
    +B1118364531697N00601332EA0160001619000
    +B1118434531666N00601285EA0158701606000
    +B1118504531636N00601242EA0157301593000
    +B1118574531603N00601198EA0155701579000
    +B1119044531565N00601153EA0154901565000
    +B1119114531527N00601121EA0154101553000
    +B1119184531488N00601081EA0154101542000
    +B1119254531461N00601045EA0153801534000
    +B1119324531428N00601009EA0153901526000
    +B1119394531393N00600974EA0153601520000
    +B1119464531357N00600927EA0153201515000
    +B1119534531320N00600883EA0153101511000
    +B1120004531287N00600842EA0152901507000
    +B1120074531254N00600805EA0152301502000
    +B1120144531221N00600766EA0151401498000
    +B1120214531188N00600726EA0150401494000
    +B1120284531156N00600681EA0149801488000
    +B1120354531125N00600635EA0149101482000
    +B1120424531096N00600587EA0148401476000
    +B1120494531063N00600535EA0147001469000
    +B1120564531030N00600486EA0146201462000
    +B1121034530997N00600444EA0145501454000
    +B1121104530964N00600405EA0144701448000
    +B1121174530927N00600372EA0144001441000
    +B1121244530889N00600340EA0143301434000
    +B1121314530852N00600308EA0142601427000
    +B1121384530816N00600275EA0142001420000
    +B1121454530783N00600241EA0141401414000
    +B1121524530749N00600207EA0140601407000
    +B1121594530715N00600172EA0139801399000
    +B1122064530686N00600131EA0138901393000
    +B1122134530653N00600094EA0137901386000
    +B1122204530616N00600061EA0136901378000
    +B1122274530577N00600029EA0135901371000
    +B1122344530541N00559996EA0135201363000
    +B1122414530505N00559965EA0134401357000
    +B1122484530469N00559932EA0133501351000
    +B1122554530434N00559899EA0132601339000
    +B1123024530399N00559866EA0131701326000
    +B1123094530365N00559831EA0130701313000
    +B1123164530329N00559799EA0129801303000
    +B1123234530290N00559772EA0128801294000
    +B1123304530251N00559745EA0127801289000
    +B1123374530212N00559717EA0126801277000
    +B1123444530174N00559685EA0125901265000
    +B1123514530139N00559650EA0125001255000
    +B1123584530103N00559618EA0124001244000
    +B1124054530067N00559582EA0122901235000
    +B1124134530024N00559541EA0122101223000
    +B1124204529987N00559507EA0121001211000
    +B1124274529949N00559475EA0120201201000
    +B1124344529916N00559439EA0119401191000
    +B1124414529885N00559400EA0118501181000
    +B1124484529853N00559364EA0117101172000
    +B1124554529824N00559324EA0116001162000
    +B1125024529795N00559291EA0114701150000
    +B1125094529765N00559256EA0113501138000
    +B1125164529738N00559219EA0112801126000
    +B1125234529709N00559183EA0112001115000
    +B1125304529679N00559148EA0111201106000
    +B1125374529649N00559111EA0110301097000
    +B1125444529623N00559072EA0109501088000
    +B1125514529592N00559036EA0108701078000
    +B1125584529564N00558998EA0108201070000
    +B1126054529543N00558956EA0107601063000
    +B1126124529523N00558911EA0106801056000
    +B1126194529499N00558870EA0106101049000
    +B1126264529468N00558831EA0105201043000
    +B1126334529433N00558799EA0104401036000
    +B1126404529407N00558759EA0103601027000
    +B1126474529384N00558719EA0102301019000
    +B1126544529360N00558684EA0101901009000
    +B1127014529332N00558657EA0100701001000
    +B1127084529302N00558626EA0099700991000
    +B1127154529276N00558589EA0098600985000
    +B1127224529250N00558554EA0097900975000
    +B1127294529214N00558527EA0096900962000
    +B1127364529183N00558493EA0095900950000
    +B1127434529154N00558459EA0095100938000
    +B1127504529115N00558435EA0094300927000
    +B1127574529073N00558420EA0093800918000
    +B1128044529034N00558393EA0092800911000
    +B1128114528991N00558378EA0091700903000
    +B1128184528949N00558359EA0090900895000
    +B1128254528902N00558347EA0089900888000
    +B1128324528862N00558319EA0088500880000
    +B1128394528820N00558281EA0087500870000
    +B1128464528771N00558268EA0086800859000
    +B1128534528720N00558271EA0085800849000
    +B1129004528669N00558279EA0084700839000
    +B1129074528616N00558294EA0083500829000
    +B1129144528562N00558306EA0082400819000
    +B1129214528512N00558320EA0082800810000
    +B1129284528494N00558354EA0082500802000
    +B1129354528497N00558316EA0082500795000
    +B1129424528468N00558280EA0082400789000
    +B1129494528425N00558289EA0082000784000
    +B1129564528384N00558311EA0081100779000
    +B1130034528339N00558335EA0080000773000
    +B1130104528288N00558354EA0079300767000
    +B1130184528230N00558374EA0078500759000
    +B1130254528177N00558369EA0077300753000
    +B1130324528126N00558354EA0077600746000
    +B1130394528114N00558303EA0079000742000
    +B1130464528132N00558322EA0078500741000
    +B1130534528090N00558348EA0079800740000
    +B1131004528057N00558318EA0080600742000
    +B1131074528076N00558317EA0082200746000
    +B1131144528045N00558321EA0083600752000
    +B1131214528066N00558305EA0085200761000
    +B1131284528046N00558334EA0087100772000
    +B1131354528048N00558294EA0088800784000
    +B1131424528058N00558330EA0089300797000
    +B1131494528015N00558328EA0091700806000
    +B1131564528002N00558289EA0092900818000
    +B1132034528018N00558319EA0095400829000
    +B1132104527992N00558342EA0097100847000
    +B1132174527997N00558296EA0098900862000
    +B1132244527989N00558330EA0100900879000
    +B1132314527978N00558295EA0102500891000
    +B1132384528001N00558304EA0103200906000
    +B1132454527981N00558344EA0103900921000
    +B1132524527955N00558318EA0106100931000
    +B1132594527976N00558306EA0107000950000
    +B1133064527959N00558354EA0107700966000
    +B1133134527916N00558353EA0109100981000
    +B1133204527908N00558303EA0109600994000
    +B1133274527933N00558266EA0110701008000
    +B1133344527919N00558309EA0110601019000
    +B1133414527876N00558319EA0111601029000
    +B1133484527856N00558270EA0112201035000
    +B1133554527869N00558216EA0114101044000
    +B1134024527870N00558242EA0116001053000
    +B1134094527859N00558210EA0117501065000
    +B1134164527862N00558236EA0119001076000
    +B1134234527837N00558200EA0120801091000
    +B1134304527858N00558199EA0122601109000
    +B1134374527826N00558215EA0124501127000
    +B1134444527823N00558177EA0126501144000
    +B1134514527827N00558218EA0127801160000
    +B1134584527799N00558196EA0129201175000
    +B1135054527812N00558156EA0131601192000
    +B1135124527810N00558190EA0133201209000
    +B1135194527787N00558157EA0134901228000
    +B1135264527801N00558175EA0136501245000
    +B1135334527773N00558173EA0137701260000
    +B1135404527786N00558144EA0139301276000
    +B1135474527790N00558181EA0140601295000
    +B1135544527763N00558163EA0141801313000
    +B1136024527794N00558167EA0143301331000
    +B1136094527779N00558213EA0143601345000
    +B1136164527763N00558175EA0144101357000
    +B1136234527773N00558131EA0145101368000
    +B1136304527799N00558121EA0145701377000
    +B1136374527780N00558160EA0146001386000
    +B1136444527755N00558125EA0147101394000
    +B1136514527744N00558074EA0147101403000
    +B1136584527735N00558014EA0145701408000
    +B1137054527725N00557938EA0143601408000
    +B1137124527725N00557865EA0142401404000
    +B1137194527714N00557812EA0142501400000
    +B1137264527710N00557753EA0142401396000
    +B1137334527702N00557697EA0141501391000
    +B1137404527699N00557641EA0140701386000
    +B1137474527695N00557589EA0139801380000
    +B1137544527684N00557535EA0137901373000
    +B1138014527677N00557477EA0135801364000
    +B1138084527667N00557423EA0136601353000
    +B1138154527654N00557372EA0136801347000
    +B1138224527653N00557312EA0135801340000
    +B1138294527650N00557248EA0135701334000
    +B1138364527649N00557189EA0134501328000
    +B1138434527650N00557132EA0134801322000
    +B1138504527642N00557075EA0135201319000
    +B1138574527640N00557020EA0134801317000
    +B1139044527637N00556962EA0133701312000
    +B1139114527635N00556903EA0134501308000
    +B1139184527632N00556848EA0134901305000
    +B1139254527629N00556792EA0136301305000
    +B1139324527614N00556735EA0137501306000
    +B1139394527594N00556684EA0138901308000
    +B1139464527573N00556647EV0139601310000
    +B1139534527546N00556608EA0140701317000
    +B1140004527530N00556569EA0140901320000
    +B1140074527509N00556529EA0140701320000
    +B1140144527486N00556492EA0140701324000
    +B1140214527466N00556510EA0139301327000
    +B1140284527503N00556537EA0139601331000
    +B1140354527521N00556558EV0139401333000
    +B1140424527547N00556603EA0140201334000
    +B1140494527583N00556640EA0140401338000
    +B1140564527617N00556671EA0141901342000
    +B1141034527652N00556692EA0144101347000
    +B1141104527636N00556727EA0146101350000
    +B1141174527615N00556706EA0147701356000
    +B1141244527618N00556713EA0150301360000
    +B1141314527650N00556706EA0152201371000
    +B1141384527652N00556750EA0155001386000
    +B1141464527623N00556715EA0157301414000
    +B1141534527657N00556709EA0158701439000
    +B1142004527657N00556763EA0159501461000
    +B1142074527620N00556742EA0160301479000
    +B1142144527607N00556678EA0162201500000
    +B1142214527636N00556659EA0164301521000
    +B1142284527617N00556673EA0165601539000
    +B1142354527617N00556627EA0167301559000
    +B1142424527650N00556604EA0168801574000
    +B1142494527637N00556640EA0171201584000
    +B1142564527635N00556593EA0172901600000
    +B1143034527666N00556599EA0173901619000
    +B1143104527644N00556635EA0176301639000
    +B1143174527632N00556598EA0178701660000
    +B1143244527660N00556618EA0180401682000
    +B1143314527632N00556645EA0182401704000
    +B1143384527615N00556593EA0184501725000
    +B1143454527609N00556560EV0186801740000
    +B1143524527612N00556552EA0187901751000
    +B1143594527630N00556519EA0190201770000
    +B1144064527619N00556470EA0191201784000
    +B1144134527580N00556453EA0190401801000
    +B1144204527533N00556444EA0190801807000
    +B1144274527491N00556440EA0190601821000
    +B1144344527453N00556436EA0189701830000
    +B1144414527412N00556435EA0188901839000
    +B1144484527369N00556433EA0188101843000
    +B1144554527329N00556433EA0187001844000
    +B1145024527288N00556439EA0186101842000
    +B1145094527247N00556440EA0185301838000
    +B1145164527207N00556435EA0184301833000
    +B1145234527166N00556435EA0183301827000
    +B1145304527126N00556442EA0182001819000
    +B1145374527084N00556448EA0180901810000
    +B1145444527042N00556455EA0179501801000
    +B1145514527001N00556457EA0178501790000
    +B1145584526961N00556466EA0177801779000
    +B1146054526925N00556470EA0176701770000
    +B1146124526886N00556470EA0175001760000
    +B1146194526845N00556474EA0173601749000
    +B1146264526801N00556492EA0172401736000
    +B1146334526752N00556511EA0171801724000
    +B1146404526703N00556522EA0170701713000
    +B1146474526652N00556528EA0169701703000
    +B1146544526600N00556526EA0168401692000
    +B1147014526545N00556518EA0167901681000
    +B1147084526492N00556513EA0168501676000
    +B1147154526447N00556510EA0169501671000
    +B1147224526404N00556494EA0171501668000
    +B1147294526364N00556473EA0172801670000
    +B1147374526313N00556462EA0173701675000
    +B1147444526270N00556459EA0174801682000
    +B1147514526229N00556453EA0175501688000
    +B1147584526190N00556447EA0176201696000
    +B1148054526151N00556436EA0177001704000
    +B1148124526111N00556418EA0177401714000
    +B1148194526064N00556408EA0177401722000
    +B1148264526017N00556391EA0176801727000
    +B1148334525979N00556380EA0177101728000
    +B1148404525942N00556370EA0177401730000
    +B1148474525898N00556350EA0177301731000
    +B1148544525851N00556329EA0177301730000
    +B1149014525806N00556302EA0177501730000
    +B1149084525773N00556274EA0178001732000
    +B1149154525743N00556241EA0179001735000
    +B1149224525705N00556211EA0180501737000
    +B1149294525671N00556178EA0180601744000
    +B1149364525631N00556162EA0181201749000
    +B1149434525607N00556138EA0183501757000
    +B1149504525612N00556156EA0184501767000
    +B1149574525605N00556123EA0186901778000
    +B1150044525611N00556146EA0188201791000
    +B1150114525615N00556091EA0190901803000
    +B1150184525593N00556095EA0192901815000
    +B1150254525611N00556069EA0195501831000
    +B1150324525591N00556037EA0197401851000
    +B1150394525605N00556065EA0199701866000
    +B1150464525609N00556026EA0201901884000
    +B1150534525592N00556046EA0203801905000
    +B1151004525622N00556039EA0206801925000
    +B1151074525602N00556001EA0208201947000
    +B1151144525567N00555978EA0210101970000
    +B1151214525536N00555953EA0213001993000
    +B1151284525554N00555922EA0214502017000
    +B1151354525542N00555965EA0215902035000
    +B1151424525505N00555960EA0218002056000
    +B1151494525465N00555934EA0219202078000
    +B1151564525422N00555911EA0220402097000
    +B1152034525383N00555886EA0221502115000
    +B1152104525346N00555860EA0221102129000
    +B1152174525301N00555847EA0220302139000
    +B1152244525262N00555834EA0219502146000
    +B1152314525221N00555818EA0218802149000
    +B1152384525182N00555802EA0219302151000
    +B1152454525145N00555788EA0219902154000
    +B1152524525105N00555783EA0220602157000
    +B1152594525069N00555774EA0221402162000
    +B1153064525035N00555768EA0221202167000
    +B1153134524998N00555759EA0221502170000
    +B1153214524952N00555744EA0220802173000
    +B1153284524914N00555724EA0220302174000
    +B1153354524875N00555697EA0219302172000
    +B1153424524841N00555665EA0219102169000
    +B1153494524803N00555632EA0219202167000
    +B1153564524764N00555604EA0219602165000
    +B1154034524736N00555585EA0221002166000
    +B1154104524713N00555574EA0222802170000
    +B1154174524684N00555565EA0224302177000
    +B1154244524649N00555553EA0226402187000
    +B1154314524619N00555526EA0228402199000
    +B1154384524594N00555509EA0229302212000
    +B1154454524563N00555499EA0229502224000
    +B1154524524532N00555484EA0230402235000
    +B1154594524501N00555471EA0231002244000
    +B1155064524463N00555449EA0230202251000
    +B1155134524428N00555422EA0228802254000
    +B1155204524390N00555390EA0227302253000
    +B1155274524354N00555374EA0226002250000
    +B1155344524318N00555347EA0224102244000
    +B1155414524281N00555310EA0221502234000
    +B1155484524246N00555264EA0221002221000
    +B1155554524215N00555222EA0220102210000
    +B1156024524182N00555185EA0220202199000
    +B1156094524150N00555156EA0219402191000
    +B1156164524114N00555124EA0220302183000
    +B1156234524084N00555103EA0220302180000
    +B1156304524051N00555090EA0219602178000
    +B1156374524015N00555081EA0218402174000
    +B1156444523980N00555065EA0218602169000
    +B1156514523954N00555046EA0219002165000
    +B1156584523929N00555022EA0219702164000
    +B1157054523897N00554991EA0219902164000
    +B1157124523864N00554952EA0218702164000
    +B1157194523830N00554917EA0218902163000
    +B1157264523792N00554910EA0218902162000
    +B1157334523757N00554891EA0219902162000
    +B1157404523720N00554872EA0221302163000
    +B1157474523688N00554849EA0221802164000
    +B1157544523647N00554837EA0221802168000
    +B1158014523611N00554828EV0222802171000
    +B1158084523568N00554798EA0224102172000
    +B1158154523530N00554786EA0224302176000
    +B1158224523489N00554771EA0224202178000
    +B1158304523455N00554750EV0225002180000
    +B1158364523411N00554732EA0225402181000
    +B1158434523368N00554733EA0225902182000
    +B1158504523327N00554722EA0226902189000
    +B1158574523284N00554707EA0225902197000
    +B1159044523241N00554696EA0226202204000
    +B1159124523203N00554698EV0226102208000
    +B1159194523145N00554687EA0226502211000
    +B1159264523104N00554678EA0226302215000
    +B1159334523076N00554667EV0226802217000
    +B1159404523028N00554639EA0226302219000
    +B1159474522987N00554607EA0224902223000
    +B1159544522943N00554581EA0223502223000
    +B1200014522889N00554553EA0221302218000
    +B1200084522822N00554530EA0219902211000
    +B1200154522799N00554518EV0220302210000
    +B1200224522726N00554479EA0221102201000
    +B1200294522700N00554465EA0222402193000
    +B1200364522668N00554446EA0223402191000
    +B1200434522634N00554430EA0223902191000
    +B1200504522591N00554411EA0223902193000
    +B1200574522553N00554383EA0225402195000
    +B1201044522520N00554362EA0226802200000
    +B1201114522487N00554336EA0227902207000
    +B1201184522445N00554318EA0228302209000
    +B1201254522407N00554301EA0227702216000
    +B1201324522367N00554278EA0227102222000
    +B1201394522325N00554248EA0226702225000
    +B1201464522282N00554227EA0226002225000
    +B1201534522244N00554208EA0226402226000
    +B1202004522216N00554193EV0228002227000
    +B1202074522185N00554163EA0229602230000
    +B1202144522158N00554135EA0231402237000
    +B1202214522128N00554113EA0233002242000
    +B1202284522095N00554088EA0234602251000
    +B1202354522069N00554061EA0234502264000
    +B1202424522034N00554039EA0235502274000
    +B1202494521998N00554013EA0235402283000
    +B1202564521965N00553993EA0235402291000
    +B1203034521928N00553972EA0235502297000
    +B1203104521898N00553945EA0236102303000
    +B1203174521867N00553930EA0236202308000
    +B1203244521829N00553917EA0236402312000
    +B1203314521787N00553907EA0237402315000
    +B1203384521746N00553898EA0239502320000
    +B1203454521712N00553889EA0240602328000
    +B1203524521683N00553893EA0239802331000
    +B1203594521647N00553884EA0240002338000
    +B1204064521609N00553880EA0240902344000
    +B1204134521577N00553866EA0241502350000
    +B1204214521545N00553851EV0243502350000
    +B1204274521511N00553829EA0244902356000
    +B1204344521483N00553803EA0245902367000
    +B1204424521455N00553796EV0246902376000
    +B1204484521409N00553783EA0245602381000
    +B1204554521382N00553761EA0244902391000
    +B1205034521358N00553740EA0244502399000
    +B1205104521331N00553710EA0244602403000
    +B1205174521303N00553670EA0243302404000
    +B1205244521273N00553626EA0241302402000
    +B1205314521244N00553579EA0239702397000
    +B1205384521217N00553531EA0239002391000
    +B1205454521194N00553494EA0238102386000
    +B1205524521169N00553458EA0236702379000
    +B1205594521139N00553432EA0235302372000
    +B1206064521108N00553406EA0233102362000
    +B1206134521070N00553367EA0231202348000
    +B1206204521031N00553325EA0229702333000
    +B1206274520991N00553281EA0227802321000
    +B1206344520944N00553245EA0227102309000
    +B1206414520913N00553216EA0226502300000
    +B1206484520885N00553189EA0226702289000
    +B1206554520863N00553165EA0225902278000
    +B1207024520833N00553150EA0224802267000
    +B1207094520800N00553125EA0223202256000
    +B1207164520766N00553098EA0223402244000
    +B1207234520730N00553075EA0223102234000
    +B1207304520694N00553053EA0224102227000
    +B1207374520667N00553022EA0224802223000
    +B1207444520634N00552999EA0225302221000
    +B1207514520604N00552976EA0224502220000
    +B1207584520570N00552953EA0225302219000
    +B1208054520536N00552930EA0225502219000
    +B1208124520507N00552913EA0224202219000
    +B1208194520476N00552890EA0223802219000
    +B1208264520440N00552865EA0224002217000
    +B1208334520407N00552834EA0225702214000
    +B1208404520392N00552796EA0227002215000
    +B1208474520414N00552779EA0228802217000
    +B1208544520408N00552810EA0230302221000
    +B1209014520380N00552792EA0231902230000
    +B1209084520355N00552769EA0233002241000
    +B1209154520329N00552739EA0233502254000
    +B1209224520294N00552714EA0234602265000
    +B1209294520259N00552697EA0236102277000
    +B1209364520226N00552677EA0238202290000
    +B1209434520200N00552649EA0239502305000
    +B1209504520173N00552620EA0239602318000
    +B1209574520141N00552598EA0239702328000
    +B1210044520111N00552575EA0240702336000
    +B1210114520079N00552565EA0240302342000
    +B1210184520043N00552545EA0240602348000
    +B1210254520018N00552526EA0240002354000
    +B1210324519993N00552501EA0240402357000
    +B1210394519968N00552469EA0241102360000
    +B1210464519941N00552436EA0242102363000
    +B1210534519910N00552411EA0243802368000
    +B1211014519875N00552380EA0245302374000
    +B1211084519850N00552355EA0246202383000
    +B1211154519818N00552332EA0244702391000
    +B1211224519774N00552304EA0243402395000
    +B1211294519738N00552277EA0243002398000
    +B1211364519702N00552259EA0243002399000
    +B1211434519671N00552248EA0244402400000
    +B1211504519639N00552225EA0244002402000
    +B1211574519606N00552200EA0243802403000
    +B1212044519578N00552174EA0242002403000
    +B1212114519539N00552156EA0240702399000
    +B1212184519500N00552138EA0241102394000
    +B1212254519464N00552127EA0241202392000
    +B1212324519430N00552112EA0239702390000
    +B1212394519395N00552086EA0238202384000
    +B1212464519360N00552057EA0236402376000
    +B1212534519316N00552027EA0235802367000
    +B1213004519278N00552001EA0234902358000
    +B1213074519247N00551971EA0233002349000
    +B1213144519211N00551952EA0231602340000
    +B1213214519173N00551942EA0231702329000
    +B1213284519135N00551933EA0232302319000
    +B1213354519100N00551917EA0234102313000
    +B1213424519071N00551895EA0235502311000
    +B1213494519037N00551876EA0238002313000
    +B1213564519046N00551890EA0239902316000
    +B1214034519048N00551845EA0241902322000
    +B1214104519021N00551832EA0243802333000
    +B1214174519049N00551853EA0245802343000
    +B1214244519071N00551796EA0247702360000
    +B1214314519077N00551727EA0249102377000
    +B1214384519081N00551665EA0250702396000
    +B1214454519085N00551616EA0252102415000
    +B1214524519081N00551569EA0252002432000
    +B1214594519070N00551521EA0250802444000
    +B1215064519052N00551467EA0249702451000
    +B1215134519027N00551413EA0249602454000
    +B1215204519001N00551375EA0249302456000
    +B1215274518971N00551327EA0248802458000
    +B1215344518945N00551276EA0248702459000
    +B1215414518921N00551227EA0249002459000
    +B1215484518892N00551190EA0248402458000
    +B1215554518862N00551154EA0250002457000
    +B1216024518838N00551119EA0250102459000
    +B1216094518818N00551073EA0251202462000
    +B1216164518795N00551024EA0253302468000
    +B1216234518802N00550967EA0254702477000
    +B1216304518832N00550993EA0256002486000
    +B1216374518811N00551006EA0258102498000
    +B1216444518798N00550963EA0259502512000
    +B1216514518782N00550916EA0258802526000
    +B1216584518756N00550866EA0258002535000
    +B1217064518721N00550819EA0257202542000
    +B1217134518684N00550778EA0255702543000
    +B1217204518639N00550725EA0254202540000
    +B1217274518596N00550671EA0253302535000
    +B1217344518562N00550622EA0252802529000
    +B1217414518532N00550570EA0251402522000
    +B1217484518503N00550526EA0250202514000
    +B1217554518477N00550488EA0249502506000
    +B1218024518446N00550445EA0247302497000
    +B1218094518409N00550401EA0245902485000
    +B1218164518373N00550362EA0245002474000
    +B1218234518337N00550329EA0244102464000
    +B1218304518307N00550291EA0243402454000
    +B1218374518274N00550254EA0243002444000
    +B1218444518239N00550219EA0242202434000
    +B1218514518200N00550190EA0241602425000
    +B1218584518158N00550167EA0240802416000
    +B1219054518113N00550148EA0240102414000
    +B1219124518072N00550117EA0239502406000
    +B1219194518035N00550082EA0238902397000
    +B1219264517997N00550046EA0238302389000
    +B1219334517963N00550009EA0237302380000
    +B1219404517932N00549962EA0236902375000
    +B1219474517898N00549928EA0237102367000
    +B1219544517876N00549901EA0237102361000
    +B1220024517864N00549877EV0236202358000
    +B1220084517842N00549847EA0236202353000
    +B1220154517823N00549819EA0234702348000
    +B1220224517805N00549791EA0234102341000
    +B1220304517775N00549773EA0233402332000
    +B1220374517746N00549749EA0232402323000
    +B1220444517720N00549722EA0230802317000
    +B1220514517685N00549694EA0228402307000
    +B1220584517648N00549669EA0226602295000
    +B1221054517606N00549643EA0225902282000
    +B1221124517563N00549615EA0225102269000
    +B1221194517521N00549594EA0224402256000
    +B1221264517484N00549585EA0223402245000
    +B1221334517444N00549564EA0222202234000
    +B1221404517406N00549537EA0221102222000
    +B1221474517369N00549509EA0220202211000
    +B1221544517333N00549484EA0219502201000
    +B1222014517296N00549465EA0218302192000
    +B1222084517264N00549430EA0217502182000
    +B1222154517230N00549400EA0216202173000
    +B1222224517200N00549370EA0214902164000
    +B1222294517164N00549346EA0213502154000
    +B1222364517132N00549325EA0211502142000
    +B1222434517099N00549303EA0209202127000
    +B1222504517066N00549276EA0207302110000
    +B1222574517032N00549245EA0205802093000
    +B1223044516999N00549224EA0203502077000
    +B1223114516969N00549201EA0202102060000
    +B1223184516949N00549180EA0200002042000
    +B1223254516921N00549162EA0198602027000
    +B1223324516895N00549135EA0196002009000
    +B1223394516869N00549096EA0194101989000
    +B1223464516846N00549054EA0193201970000
    +B1223534516821N00549012EA0192701953000
    +B1224004516795N00548973EA0193201939000
    +B1224074516772N00548941EA0193501930000
    +B1224144516743N00548917EA0195401926000
    +B1224214516730N00548887EA0197501926000
    +B1224284516743N00548914EA0199401929000
    +B1224354516714N00548938EA0201401933000
    +B1224424516688N00548919EA0202601941000
    +B1224494516666N00548888EA0204201952000
    +B1224564516647N00548841EA0205301961000
    +B1225034516618N00548817EA0207101976000
    +B1225104516587N00548798EA0208301990000
    +B1225174516578N00548765EA0209602005000
    +B1225244516609N00548792EA0210502013000
    +B1225314516592N00548834EA0212002025000
    +B1225384516564N00548822EA0215002040000
    +B1225454516565N00548774EA0217802060000
    +B1225524516579N00548806EA0220702081000
    +B1225594516555N00548814EA0221602104000
    +B1226064516531N00548783EA0220802124000
    +B1226134516502N00548744EA0220202138000
    +B1226204516471N00548694EA0219102147000
    +B1226284516430N00548632EA0217902150000
    +B1226354516398N00548595EV0216702150000
    +B1226424516355N00548550EA0215202149000
    +B1226494516328N00548509EA0213802143000
    +B1226564516297N00548469EA0212302136000
    +B1227034516266N00548432EA0210002123000
    +B1227104516240N00548405EA0209302112000
    +B1227174516221N00548386EV0208702103000
    +B1227244516182N00548360EA0207102095000
    +B1227314516152N00548333EA0205702081000
    +B1227384516123N00548304EA0205102071000
    +B1227454516088N00548279EA0204502059000
    +B1227524516058N00548243EA0203002047000
    +B1227594516026N00548206EA0201602034000
    +B1228064515993N00548172EA0200002021000
    +B1228134515957N00548135EA0198802006000
    +B1228204515925N00548102EA0197801992000
    +B1228274515900N00548076EA0197601983000
    +B1228344515877N00548045EA0197001972000
    +B1228414515853N00548006EA0196901963000
    +B1228484515831N00547975EA0197301956000
    +B1228554515803N00547942EA0196701951000
    +B1229024515773N00547908EA0195801946000
    +B1229094515743N00547874EA0196301941000
    +B1229164515717N00547843EA0196101937000
    +B1229234515687N00547818EA0195101933000
    +B1229304515657N00547791EA0193901929000
    +B1229374515630N00547754EA0192701923000
    +B1229444515606N00547715EA0192701916000
    +B1229514515590N00547683EA0194701912000
    +B1229584515593N00547633EA0196701912000
    +B1230054515624N00547648EA0198001917000
    +B1230124515601N00547686EA0199601924000
    +B1230194515572N00547668EA0200201934000
    +B1230264515539N00547629EA0198901942000
    +B1230334515499N00547579EA0199101947000
    +B1230404515472N00547543EA0198401951000
    +B1230474515438N00547517EA0196801953000
    +B1230544515399N00547481EA0196101951000
    +B1231014515371N00547443EA0196501948000
    +B1231084515344N00547414EA0196701946000
    +B1231154515321N00547382EA0198601946000
    +B1231224515304N00547355EA0200001949000
    +B1231294515280N00547322EA0201601955000
    +B1231364515254N00547286EA0202401962000
    +B1231434515227N00547245EA0204801971000
    +B1231504515215N00547208EA0207501984000
    +B1231574515205N00547168EA0206901997000
    +B1232044515187N00547137EA0207502007000
    +B1232114515163N00547109EA0206502016000
    +B1232194515128N00547069EA0204702020000
    +B1232264515096N00547029EA0203402018000
    +B1232334515060N00546986EA0201302013000
    +B1232404515031N00546942EA0200002005000
    +B1232474514999N00546900EA0197801994000
    +B1232544514965N00546852EA0196401981000
    +B1233014514933N00546804EA0195901968000
    +B1233084514907N00546766EA0194701957000
    +B1233154514876N00546723EA0193601946000
    +B1233224514849N00546676EA0192701936000
    +B1233294514820N00546635EA0192101926000
    +B1233364514789N00546591EA0190501916000
    +B1233434514753N00546553EA0189301904000
    +B1233504514712N00546505EA0188401892000
    +B1233574514685N00546467EA0188701882000
    +B1234044514652N00546433EA0189901876000
    +B1234114514625N00546391EA0188801872000
    +B1234184514594N00546352EA0187201866000
    +B1234254514554N00546303EA0184901857000
    +B1234324514513N00546242EA0182901845000
    +B1234394514476N00546183EA0182501832000
    +B1234464514447N00546134EA0182101821000
    +B1234534514414N00546094EA0181601812000
    +B1235004514375N00546066EA0181201804000
    +B1235074514341N00546036EA0182001797000
    +B1235144514310N00546011EA0182001794000
    +B1235214514272N00545981EA0182801793000
    +B1235284514241N00545939EA0183601793000
    +B1235354514217N00545902EA0184701796000
    +B1235424514197N00545869EA0186001802000
    +B1235494514171N00545832EA0185301808000
    +B1235564514146N00545798EA0184301811000
    +B1236034514117N00545762EA0183301810000
    +B1236104514090N00545723EA0180901805000
    +B1236174514079N00545769EA0179001797000
    +B1236244514091N00545824EA0177601786000
    +B1236314514117N00545873EA0176601774000
    +B1236384514151N00545910EA0177101767000
    +B1236454514188N00545939EA0178901764000
    +B1236524514214N00545973EA0179401760000
    +B1236594514236N00546022EA0180401759000
    +B1237064514259N00546074EA0181301760000
    +B1237134514284N00546122EA0182401762000
    +B1237204514313N00546161EA0183001766000
    +B1237274514351N00546192EA0184201773000
    +B1237344514384N00546234EA0185401783000
    +B1237414514414N00546273EA0185401793000
    +B1237484514442N00546307EA0184001796000
    +B1237554514465N00546351EA0184401802000
    +B1238024514492N00546399EA0186101806000
    +B1238094514511N00546437EA0185101809000
    +B1238174514541N00546495EA0186601811000
    +B1238244514569N00546536EA0185401814000
    +B1238314514604N00546589EA0185001815000
    +B1238384514634N00546631EA0184501815000
    +B1238454514670N00546691EA0185501815000
    +B1238524514703N00546743EA0186601815000
    +B1238594514728N00546784EA0187801819000
    +B1239064514751N00546812EA0189201826000
    +B1239134514773N00546836EA0189301834000
    +B1239204514801N00546870EA0190001839000
    +B1239274514827N00546912EA0189301846000
    +B1239344514855N00546952EA0189201850000
    +B1239414514890N00546986EA0188501852000
    +B1239484514933N00547027EA0188801853000
    +B1239554514968N00547067EA0191101854000
    +B1240024514992N00547101EA0192801860000
    +B1240094515021N00547134EA0193201867000
    +B1240164515046N00547163EA0193801871000
    +B1240234515071N00547187EA0193901878000
    +B1240304515097N00547215EA0192801880000
    +B1240374515124N00547237EA0191101880000
    +B1240444515150N00547275EA0191301881000
    +B1240514515190N00547321EA0192801881000
    +B1240584515219N00547357EA0194301885000
    +B1241054515254N00547394EA0195601892000
    +B1241124515281N00547428EA0197301901000
    +B1241194515307N00547462EA0198301911000
    +B1241264515335N00547500EA0200501923000
    +B1241334515360N00547534EA0200001934000
    +B1241404515393N00547581EA0199101942000
    +B1241474515421N00547641EA0198701947000
    +B1241544515455N00547700EA0197401949000
    +B1242014515496N00547756EA0194801945000
    +B1242084515539N00547800EA0193001939000
    +B1242154515575N00547837EA0193701931000
    +B1242224515600N00547877EA0193201924000
    +B1242294515632N00547919EA0191801917000
    +B1242364515668N00547965EA0191601910000
    +B1242434515700N00547997EA0191401904000
    +B1242504515733N00548029EA0192201900000
    +B1242574515768N00548071EA0194101900000
    +B1243044515800N00548113EA0194001900000
    +B1243114515839N00548158EA0193701900000
    +B1243184515880N00548203EA0192801900000
    +B1243254515919N00548252EA0191701900000
    +B1243324515954N00548302EA0189501900000
    +B1243394515994N00548350EA0186901900000
    +B1243464516033N00548401EA0186001900000
    +B1243534516072N00548452EA0183801894000
    +B1244004516117N00548515EA0182001883000
    +B1244084516173N00548589EA0180501861000
    +B1244154516223N00548647EA0179101840000
    +B1244224516270N00548698EA0177601820000
    +B1244294516322N00548750EA0175801801000
    +B1244364516366N00548783EA0176301783000
    +B1244434516407N00548808EA0178701773000
    +B1244504516444N00548835EA0180701769000
    +B1244574516473N00548862EA0182601772000
    +B1245044516508N00548887EA0184401778000
    +B1245114516547N00548912EA0184901786000
    +B1245184516588N00548931EA0186501794000
    +B1245254516618N00548948EA0187301805000
    +B1245324516648N00548967EA0187501815000
    +B1245394516679N00548990EA0188501823000
    +B1245464516718N00549005EA0188701830000
    +B1245534516760N00549024EA0188601836000
    +B1246004516802N00549045EA0187601839000
    +B1246074516848N00549070EA0186001838000
    +B1246144516899N00549093EA0184401834000
    +B1246214516946N00549119EA0182701826000
    +B1246284516992N00549139EA0181401817000
    +B1246354517037N00549169EA0180401807000
    +B1246424517080N00549195EA0180301798000
    +B1246494517127N00549218EA0179801790000
    +B1246564517172N00549248EA0178501782000
    +B1247034517215N00549280EA0176301772000
    +B1247104517265N00549314EA0175201761000
    +B1247174517308N00549341EA0174501751000
    +B1247244517353N00549374EA0173701741000
    +B1247314517399N00549404EA0173001733000
    +B1247384517445N00549438EA0172101724000
    +B1247454517494N00549459EA0171401716000
    +B1247524517541N00549458EA0170801709000
    +B1247594517584N00549475EA0171401704000
    +B1248064517626N00549492EA0172401700000
    +B1248134517659N00549512EA0174101700000
    +B1248204517687N00549541EA0174701703000
    +B1248274517713N00549577EA0176201707000
    +B1248344517732N00549613EA0177701714000
    +B1248414517753N00549655EA0177701723000
    +B1248484517771N00549707EA0176401728000
    +B1248554517794N00549765EA0175401730000
    +B1249024517817N00549826EA0174601731000
    +B1249094517850N00549874EA0175901731000
    +B1249164517888N00549919EA0175901732000
    +B1249234517924N00549975EA0175001731000
    +B1249304517952N00550032EA0174301727000
    +B1249374517973N00550088EA0174301723000
    +B1249444517991N00550142EA0173801721000
    +B1249514518004N00550192EA0173701719000
    +B1249594518026N00550256EA0172701716000
    +B1250064518041N00550309EA0171701711000
    +B1250134518064N00550357EA0172301705000
    +B1250204518081N00550405EA0173201702000
    +B1250274518087N00550444EA0172601701000
    +B1250344518102N00550494EA0171901698000
    +B1250414518114N00550548EA0171201694000
    +B1250484518123N00550602EA0170201689000
    +B1250554518139N00550652EA0168901684000
    +B1251024518164N00550709EA0167301678000
    +B1251094518181N00550779EA0166401670000
    +B1251164518198N00550852EA0166201661000
    +B1251234518214N00550903EA0167401655000
    +B1251304518229N00550955EA0168301651000
    +B1251374518236N00551013EA0167801649000
    +B1251444518231N00551070EA0168001646000
    +B1251514518228N00551119EA0169501646000
    +B1251584518232N00551164EA0170701649000
    +B1252054518242N00551210EA0172401654000
    +B1252124518247N00551258EA0172901662000
    +B1252194518272N00551308EA0175301670000
    +B1252264518255N00551320EA0176701677000
    +B1252334518284N00551284EA0178801685000
    +B1252404518297N00551311EA0180401697000
    +B1252474518290N00551274EA0181701713000
    +B1252544518322N00551292EA0183101729000
    +B1253014518306N00551306EA0184701742000
    +B1253084518331N00551280EA0186101758000
    +B1253154518350N00551318EA0187201772000
    +B1253224518339N00551289EA0189701783000
    +B1253294518365N00551302EA0191401785000
    +B1253364518350N00551293EA0193501788000
    +B1253434518380N00551303EA0196201804000
    +B1253504518360N00551304EA0198101817000
    +B1253574518394N00551291EA0200801843000
    +B1254044518385N00551328EA0203501871000
    +B1254114518389N00551287EA0206101900000
    +B1254184518422N00551297EA0209001933000
    +B1254254518396N00551321EA0211801956000
    +B1254324518421N00551300EA0215701981000
    +B1254394518430N00551336EA0217602013000
    +B1254464518420N00551300EA0221502041000
    +B1254534518444N00551309EA0224402073000
    +B1255004518421N00551325EA0227502104000
    +B1255074518442N00551293EA0231002137000
    +B1255144518459N00551325EA0233202169000
    +B1255214518428N00551331EA0236402201000
    +B1255284518453N00551315EA0239302205000
    +B1255354518494N00551347EA0241202218000
    +B1255434518534N00551351EA0242702255000
    +B1255504518578N00551359EA0244702288000
    +B1255574518623N00551382EA0245502318000
    +B1256044518664N00551409EA0247602346000
    +B1256114518697N00551435EA0249502372000
    +B1256184518737N00551463EA0250002396000
    +B1256254518773N00551496EA0249702416000
    +B1256324518814N00551527EA0250402430000
    +B1256394518848N00551560EA0250502443000
    +B1256464518883N00551596EA0249802451000
    +B1256534518926N00551625EA0250102457000
    +B1257004518972N00551644EA0252202463000
    +B1257074519011N00551662EA0252602471000
    +B1257144519050N00551678EA0253802480000
    +B1257214519092N00551699EA0254502488000
    +B1257284519137N00551724EA0253902495000
    +B1257354519187N00551761EA0253602500000
    +B1257424519233N00551793EA0252802503000
    +B1257494519283N00551827EA0252002504000
    +B1257564519330N00551866EA0250302503000
    +B1258034519383N00551903EA0249002499000
    +B1258104519433N00551941EA0247602494000
    +B1258174519478N00551990EA0246602486000
    +B1258244519521N00552038EA0245602477000
    +B1258314519570N00552085EA0244302467000
    +B1258384519618N00552133EA0241602454000
    +B1258454519663N00552183EA0240002440000
    +B1258524519703N00552215EA0239902427000
    +B1258594519744N00552255EA0239102415000
    +B1259064519781N00552299EA0238302403000
    +B1259134519822N00552345EA0238302391000
    +B1259204519863N00552393EA0238902382000
    +B1259274519905N00552433EA0240102378000
    +B1259344519950N00552464EA0241502376000
    +B1259414519990N00552499EA0242902379000
    +B1259484520023N00552532EA0243402385000
    +B1259554520058N00552578EA0245002390000
    +B1300024520096N00552610EA0246702397000
    +B1300094520137N00552644EA0247702408000
    +B1300164520173N00552681EA0247502420000
    +B1300234520209N00552712EA0247902428000
    +B1300304520246N00552736EA0249002436000
    +B1300374520288N00552755EA0250302443000
    +B1300444520331N00552770EA0251102451000
    +B1300514520373N00552784EA0252302460000
    +B1300584520412N00552802EA0253302469000
    +B1301054520455N00552831EA0254202478000
    +B1301124520491N00552866EA0253202486000
    +B1301194520535N00552917EA0251202490000
    +B1301274520592N00552976EA0249802491000
    +B1301344520645N00553027EA0249102489000
    +B1301414520694N00553070EA0249002488000
    +B1301484520741N00553123EA0247702486000
    +B1301554520795N00553181EA0246402481000
    +B1302024520850N00553231EA0244902473000
    +B1302094520904N00553277EA0243102462000
    +B1302164520958N00553327EA0241802451000
    +B1302234521013N00553375EA0240402438000
    +B1302304521060N00553424EA0239302425000
    +B1302374521104N00553471EA0237002411000
    +B1302444521153N00553510EA0236302397000
    +B1302514521198N00553548EA0234902383000
    +B1302584521245N00553573EA0233902368000
    +B1303054521294N00553588EA0233502355000
    +B1303124521338N00553599EA0233302345000
    +B1303194521380N00553612EA0235602338000
    +B1303264521430N00553624EA0238102335000
    +B1303334521473N00553626EA0240502339000
    +B1303404521508N00553635EA0241002346000
    +B1303474521544N00553628EA0243702356000
    +B1303544521587N00553633EA0246002368000
    +B1304014521629N00553647EA0247602382000
    +B1304084521670N00553663EA0249202397000
    +B1304154521711N00553682EA0250602413000
    +B1304224521752N00553701EA0252502430000
    +B1304294521793N00553714EA0253202445000
    +B1304364521834N00553735EA0254702461000
    +B1304434521881N00553761EA0255002474000
    +B1304504521927N00553770EA0254802486000
    +B1304574521976N00553788EA0254602495000
    +B1305044522033N00553806EA0254302501000
    +B1305114522081N00553847EA0253602504000
    +B1305184522134N00553884EA0252502506000
    +B1305254522192N00553922EA0250802504000
    +B1305324522251N00553956EA0248802498000
    +B1305394522308N00553990EA0246502488000
    +B1305464522368N00554021EA0244902476000
    +B1305534522434N00554057EA0243402462000
    +B1306004522498N00554087EA0241802447000
    +B1306074522562N00554113EA0240102432000
    +B1306144522627N00554136EA0238402417000
    +B1306214522692N00554162EA0236702402000
    +B1306284522756N00554188EA0235202386000
    +B1306354522821N00554211EA0233802370000
    +B1306424522883N00554238EA0232602355000
    +B1306494522939N00554263EA0232502341000
    +B1306564522981N00554287EA0230202329000
    +B1307034523035N00554314EA0229202316000
    +B1307114523092N00554337EA0228202302000
    +B1307184523140N00554360EA0226902290000
    +B1307254523181N00554373EA0225602279000
    +B1307324523221N00554374EA0225002269000
    +B1307394523264N00554385EA0224802258000
    +B1307464523305N00554395EA0225102248000
    +B1307534523349N00554397EA0225002239000
    +B1308004523393N00554406EA0224602233000
    +B1308074523436N00554416EA0225302229000
    +B1308144523475N00554424EA0225202228000
    +B1308214523515N00554442EA0224402227000
    +B1308284523557N00554461EA0223502224000
    +B1308354523602N00554487EA0222702219000
    +B1308424523647N00554512EA0221002213000
    +B1308494523694N00554547EA0219402205000
    +B1308564523739N00554575EA0217602196000
    +B1309034523780N00554608EA0216702185000
    +B1309104523818N00554646EA0215102173000
    +B1309174523858N00554675EA0214502161000
    +B1309244523897N00554703EA0214202150000
    +B1309314523933N00554732EA0213802140000
    +B1309384523969N00554765EA0213302131000
    +B1309454524012N00554803EA0211902122000
    +B1309524524048N00554839EA0212902116000
    +B1309594524087N00554871EA0213302112000
    +B1310064524123N00554905EA0214002111000
    +B1310134524159N00554934EA0213702111000
    +B1310204524195N00554975EA0213502110000
    +B1310274524235N00555024EA0214102110000
    +B1310344524272N00555065EA0215502110000
    +B1310414524303N00555093EA0216802114000
    +B1310484524337N00555108EA0218302120000
    +B1310554524356N00555074EA0219202128000
    +B1311024524343N00555103EA0220502134000
    +B1311094524383N00555114EA0221502143000
    +B1311164524431N00555113EA0223402154000
    +B1311234524473N00555113EA0224202165000
    +B1311304524511N00555110EA0225402177000
    +B1311374524544N00555113EA0225302189000
    +B1311444524582N00555125EA0225402198000
    +B1311514524619N00555132EA0225002205000
    +B1311584524659N00555135EA0225302210000
    +B1312054524699N00555129EA0224402214000
    +B1312124524737N00555129EA0223302215000
    +B1312194524775N00555146EA0222402214000
    +B1312264524816N00555171EA0220802211000
    +B1312334524864N00555206EA0219102205000
    +B1312404524908N00555238EA0219502199000
    +B1312474524951N00555271EA0218402192000
    +B1312544524985N00555315EA0218202183000
    +B1313014525012N00555362EA0217102176000
    +B1313094525038N00555420EA0217002167000
    +B1313164525068N00555461EA0216502160000
    +B1313234525110N00555504EA0215102155000
    +B1313304525153N00555551EA0214102149000
    +B1313374525193N00555598EA0214102141000
    +B1313444525231N00555628EA0214102135000
    +B1313514525267N00555651EA0213502130000
    +B1313584525301N00555677EA0212602125000
    +B1314054525341N00555707EA0211702118000
    +B1314124525381N00555739EA0210102110000
    +B1314194525424N00555764EA0208702102000
    +B1314264525467N00555786EA0207802092000
    +B1314334525504N00555823EA0206602084000
    +B1314404525552N00555873EA0205302074000
    +B1314474525603N00555920EA0204502063000
    +B1314544525654N00555963EA0203602052000
    +B1315014525705N00556003EA0202602040000
    +B1315084525751N00556043EA0201702030000
    +B1315154525786N00556067EA0201702021000
    +B1315224525827N00556089EA0201302014000
    +B1315294525867N00556114EA0200402007000
    +B1315364525906N00556144EA0199702000000
    +B1315434525946N00556173EA0198701992000
    +B1315504525982N00556177EA0197901983000
    +B1315574526018N00556157EA0197101975000
    +B1316044526058N00556149EA0197001968000
    +B1316114526087N00556129EA0195901962000
    +B1316184526115N00556072EA0194201954000
    +B1316254526150N00556010EA0192501945000
    +B1316324526182N00555945EA0190701933000
    +B1316394526212N00555876EA0189401921000
    +B1316464526241N00555810EA0187801908000
    +B1316534526268N00555747EA0185901894000
    +B1317004526297N00555682EA0184001879000
    +B1317074526324N00555620EA0182901864000
    +B1317144526355N00555561EA0181701849000
    +B1317214526384N00555500EA0181001835000
    +B1317284526413N00555443EA0179901824000
    +B1317354526450N00555387EA0179201813000
    +B1317424526489N00555331EA0178701801000
    +B1317494526515N00555285EA0179201791000
    +B1317564526535N00555254EA0180201783000
    +B1318034526555N00555228EA0180801780000
    +B1318104526583N00555210EA0182001781000
    +B1318174526617N00555207EA0183201785000
    +B1318244526647N00555223EA0184001791000
    +B1318314526619N00555245EA0184801795000
    +B1318384526640N00555213EA0185701802000
    +B1318454526677N00555213EA0186101809000
    +B1318534526716N00555195EA0186801815000
    +B1319004526742N00555171EA0187401821000
    +B1319074526769N00555152EA0188801828000
    +B1319144526797N00555135EA0190001836000
    +B1319214526822N00555120EA0191401846000
    +B1319284526848N00555107EA0193801857000
    +B1319354526839N00555086EA0195101867000
    +B1319424526847N00555126EA0197201875000
    +B1319494526853N00555100EA0198501885000
    +B1319564526848N00555139EA0199701892000
    +B1320034526871N00555132EA0201301906000
    +B1320104526849N00555140EA0202301922000
    +B1320174526877N00555154EA0204401937000
    +B1320244526855N00555142EA0205601955000
    +B1320314526871N00555176EA0207401972000
    +B1320384526877N00555159EA0208801990000
    +B1320454526869N00555199EA0210302006000
    +B1320524526878N00555177EA0212002022000
    +B1320594526844N00555153EA0213902039000
    +B1321064526847N00555192EA0216102057000
    +B1321134526834N00555172EA0217702074000
    +B1321204526847N00555209EA0220002091000
    +B1321274526837N00555185EA0221502110000
    +B1321344526818N00555237EA0223702128000
    +B1321414526824N00555227EA0225102141000
    +B1321484526802N00555268EA0227202156000
    +B1321554526820N00555255EA0229202176000
    +B1322024526792N00555273EA0230402196000
    +B1322094526820N00555307EA0232602215000
    +B1322164526803N00555276EA0234402230000
    +B1322234526776N00555311EA0236002243000
    +B1322304526800N00555312EA0237502262000
    +B1322374526775N00555298EA0238402280000
    +B1322444526781N00555354EA0240802297000
    +B1322514526788N00555330EA0241602314000
    +B1322584526762N00555366EA0243902332000
    +B1323054526782N00555408EA0245402350000
    +B1323124526814N00555387EA0246902367000
    +B1323194526848N00555388EA0247902384000
    +B1323264526886N00555384EA0250002402000
    +B1323334526921N00555381EA0250502418000
    +B1323404526958N00555377EA0251002432000
    +B1323474526997N00555371EA0251502444000
    +B1323544527036N00555375EA0251602455000
    +B1324014527077N00555372EA0250902463000
    +B1324084527120N00555368EA0249502466000
    +B1324154527173N00555374EA0249102467000
    +B1324234527226N00555379EA0248302464000
    +B1324304527271N00555376EA0248502462000
    +B1324374527315N00555381EA0248402460000
    +B1324444527359N00555387EA0248702458000
    +B1324514527399N00555387EA0249402458000
    +B1324584527440N00555390EA0250102460000
    +B1325054527483N00555385EA0249702462000
    +B1325124527530N00555382EA0249602463000
    +B1325194527579N00555375EA0249402464000
    +B1325264527627N00555377EA0249302465000
    +B1325334527674N00555397EA0249002465000
    +B1325404527722N00555409EA0248502465000
    +B1325474527773N00555413EA0247602463000
    +B1325544527824N00555423EA0246802460000
    +B1326014527875N00555435EA0245402455000
    +B1326084527927N00555455EA0244502448000
    +B1326154527976N00555480EA0243402440000
    +B1326224528019N00555517EA0242602433000
    +B1326294528062N00555554EA0241102427000
    +B1326364528112N00555595EA0240102419000
    +B1326434528159N00555643EA0238902410000
    +B1326504528206N00555692EA0237502407000
    +B1326574528254N00555744EA0236602395000
    +B1327044528303N00555798EA0235602381000
    +B1327114528354N00555854EA0234402372000
    +B1327184528410N00555910EA0233102363000
    +B1327254528463N00555960EA0231602350000
    +B1327324528508N00556013EA0230502335000
    +B1327394528552N00556052EA0229102322000
    +B1327464528602N00556095EA0227902307000
    +B1327534528652N00556140EA0227202293000
    +B1328004528700N00556185EA0226502279000
    +B1328074528748N00556229EA0225702267000
    +B1328144528794N00556273EA0225002257000
    +B1328214528837N00556327EA0224202246000
    +B1328284528880N00556382EA0223602237000
    +B1328354528925N00556432EA0222602229000
    +B1328424528973N00556485EA0221702225000
    +B1328494529015N00556531EA0221102218000
    +B1328564529058N00556576EA0220402210000
    +B1329034529099N00556624EA0219202201000
    +B1329104529137N00556675EA0218202192000
    +B1329174529171N00556732EA0216902182000
    +B1329244529205N00556791EA0215602171000
    +B1329314529239N00556844EA0214202160000
    +B1329384529273N00556898EA0212902148000
    +B1329454529307N00556951EA0211902136000
    +B1329524529344N00556991EA0210702124000
    +B1329594529384N00557030EA0209602114000
    +B1330064529423N00557076EA0208802104000
    +B1330134529463N00557126EA0207602093000
    +B1330204529504N00557175EA0207002081000
    +B1330284529550N00557226EA0206302070000
    +B1330354529588N00557276EA0205602060000
    +B1330424529626N00557327EA0204902052000
    +B1330494529664N00557378EA0204102045000
    +B1330564529701N00557425EA0203702036000
    +B1331034529735N00557473EA0203002028000
    +B1331104529771N00557521EA0202302020000
    +B1331174529807N00557570EA0201502012000
    +B1331244529839N00557622EA0200502003000
    +B1331314529869N00557676EA0199601995000
    +B1331384529899N00557726EA0198701986000
    +B1331454529932N00557774EA0197801977000
    +B1331524529966N00557822EA0196801968000
    +B1331594530000N00557872EA0195901958000
    +B1332064530032N00557924EA0195101948000
    +B1332134530063N00557979EA0194101939000
    +B1332204530095N00558033EA0193301930000
    +B1332274530124N00558084EA0192601922000
    +B1332344530156N00558129EA0191901915000
    +B1332414530191N00558168EA0191001907000
    +B1332484530226N00558213EA0190101900000
    +B1332554530262N00558257EA0189601895000
    +B1333024530296N00558302EA0189101887000
    +B1333094530329N00558348EA0188501882000
    +B1333164530361N00558393EA0187601875000
    +B1333234530393N00558439EA0186801867000
    +B1333304530425N00558486EA0185901859000
    +B1333374530459N00558529EA0184901851000
    +B1333444530493N00558571EA0183801843000
    +B1333514530529N00558614EA0183001837000
    +B1333584530565N00558657EA0182101828000
    +B1334054530602N00558702EA0181201819000
    +B1334124530637N00558744EA0180401809000
    +B1334194530672N00558787EA0179701800000
    +B1334264530705N00558832EA0179001790000
    +B1334334530739N00558875EA0178101783000
    +B1334404530773N00558918EA0177501776000
    +B1334474530806N00558959EA0176701769000
    +B1334544530839N00559000EA0175901763000
    +B1335014530871N00559039EA0175101758000
    +B1335084530902N00559082EA0174201750000
    +B1335154530933N00559123EA0173101742000
    +B1335224530963N00559169EA0172501733000
    +B1335294530992N00559215EA0171901727000
    +B1335364531017N00559261EA0171201724000
    +B1335434531042N00559316EA0170501715000
    +B1335504531066N00559371EA0170001706000
    +B1335574531092N00559426EA0169201696000
    +B1336044531118N00559478EA0168501688000
    +B1336124531145N00559533EA0167801684000
    +B1336194531174N00559583EA0167001677000
    +B1336264531205N00559628EA0166001671000
    +B1336334531239N00559671EA0164901666000
    +B1336404531271N00559716EA0163601659000
    +B1336474531304N00559762EA0162401655000
    +B1336544531337N00559805EA0161101646000
    +B1337014531369N00559844EA0160101633000
    +B1337084531403N00559885EA0159001631000
    +B1337154531435N00559920EA0157801616000
    +B1337224531465N00559964EA0156601611000
    +B1337294531492N00600011EA0155501600000
    +B1337364531519N00600058EA0154601586000
    +B1337434531547N00600106EA0153801570000
    +B1337504531573N00600156EA0153201558000
    +B1337574531600N00600207EA0152101549000
    +B1338044531629N00600266EA0151201539000
    +B1338114531656N00600324EA0150401526000
    +B1338184531682N00600379EA0149601513000
    +B1338254531702N00600432EA0148401500000
    +B1338324531722N00600489EA0147101487000
    +B1338394531744N00600547EA0145801473000
    +B1338464531760N00600595EA0144301460000
    +B1338534531779N00600645EA0143001447000
    +B1339004531808N00600697EA0141901433000
    +B1339074531833N00600746EA0140501419000
    +B1339144531855N00600786EA0138801406000
    +B1339214531871N00600837EA0138101393000
    +B1339284531887N00600886EA0136301381000
    +B1339354531912N00600927EA0134901368000
    +B1339424531939N00600971EA0133401354000
    +B1339494531961N00601019EA0132201340000
    +B1339564531983N00601065EA0130601327000
    +B1340034532006N00601116EA0129001313000
    +B1340104532026N00601173EA0127901299000
    +B1340174532040N00601239EA0128201287000
    +B1340244532057N00601301EA0128701277000
    +B1340314532067N00601374EA0129701271000
    +B1340384532043N00601456EA0130701269000
    +B1340454532007N00601499EA0130701268000
    +B1340524531974N00601529EA0131201267000
    +B1340594531965N00601506EA0130601268000
    +B1341064531997N00601497EA0132301268000
    +B1341134532014N00601488EA0132901270000
    +B1341204531990N00601507EA0132801273000
    +B1341274531987N00601528EA0134701273000
    +B1341344531987N00601522EA0135501276000
    +B1341414531997N00601577EA0135601283000
    +B1341484532022N00601576EA0137701287000
    +B1341554532041N00601569EA0139401291000
    +B1342024532051N00601568EA0141001299000
    +B1342104532064N00601571EA0142201310000
    +B1342174532075N00601559EA0143401321000
    +B1342244532093N00601555EA0143901337000
    +B1342314532112N00601555EA0144601352000
    +B1342384532133N00601555EA0143901364000
    +B1342454532161N00601559EA0143501375000
    +B1342524532189N00601556EA0143101379000
    +B1342594532218N00601551EA0142701381000
    +B1343064532248N00601547EA0142801383000
    +B1343134532281N00601551EA0142201383000
    +B1343204532315N00601556EA0142001383000
    +B1343274532344N00601558EA0141601382000
    +B1343344532377N00601562EA0141901380000
    +B1343414532411N00601566EA0142401379000
    +B1343484532443N00601584EA0143301380000
    +B1343554532431N00601639EA0144301384000
    +B1344024532422N00601618EA0145401390000
    +B1344094532447N00601646EA0146301396000
    +B1344164532425N00601698EA0146901404000
    +B1344234532414N00601679EA0148501410000
    +B1344304532445N00601710EA0148901422000
    +B1344374532421N00601768EA0150401432000
    +B1344444532404N00601755EA0151401439000
    +B1344514532412N00601735EA0152401448000
    +B1344584532443N00601765EA0152901459000
    +B1345054532418N00601801EA0153201467000
    +B1345124532441N00601805EA0154301474000
    +B1345194532478N00601843EA0153301481000
    +B1345264532519N00601887EA0153801486000
    +B1345334532557N00601921EA0152201488000
    +B1345404532590N00601969EA0152501489000
    +B1345474532613N00602015EA0150901488000
    +B1345544532639N00602061EA0149801484000
    +B1346014532667N00602115EA0148501478000
    +B1346084532697N00602166EA0146801469000
    +B1346154532722N00602213EA0144601457000
    +B1346224532749N00602274EA0142401443000
    +B1346294532774N00602344EA0140101429000
    +B1346364532799N00602417EA0138401413000
    +B1346434532825N00602489EA0136801396000
    +B1346504532842N00602556EA0137201382000
    +B1346574532860N00602608EA0137001370000
    +B1347044532872N00602659EA0136201359000
    +B1347114532881N00602716EA0135201348000
    +B1347184532885N00602780EA0134401338000
    +B1347254532885N00602845EA0134101330000
    +B1347324532890N00602906EA0133701323000
    +B1347394532903N00602965EA0133401317000
    +B1347474532930N00603017EA0133101310000
    +B1347544532955N00603053EA0133301304000
    +B1348014532978N00603085EA0132401300000
    +B1348084533008N00603116EA0131801294000
    +B1348154533034N00603133EA0133401290000
    +B1348224533055N00603133EA0133401289000
    +B1348294533084N00603124EA0132201287000
    +B1348364533112N00603119EA0133801287000
    +B1348434533133N00603110EA0134101288000
    +B1348504533157N00603102EA0135201291000
    +B1348574533176N00603101EA0136101295000
    +B1349044533192N00603092EA0136801300000
    +B1349114533212N00603087EA0137301307000
    +B1349184533227N00603074EA0137201311000
    +B1349254533201N00603089EA0137101314000
    +B1349324533170N00603137EA0137601316000
    +B1349394533143N00603131EA0137901320000
    +B1349464533167N00603130EA0138201327000
    +B1349534533191N00603125EA0138501333000
    +B1350004533214N00603110EA0138101336000
    +B1350074533242N00603098EA0138601338000
    +B1350144533273N00603109EA0139701340000
    +B1350214533290N00603092EA0140401342000
    +B1350284533269N00603101EA0141301342000
    +B1350354533280N00603123EA0141601346000
    +B1350424533250N00603111EA0142301350000
    +B1350494533263N00603144EA0142801354000
    +B1350564533266N00603116EA0143201360000
    +B1351034533239N00603134EA0143401365000
    +B1351104533259N00603174EA0143801371000
    +B1351174533269N00603152EA0144101376000
    +B1351244533243N00603143EA0143801380000
    +B1351314533243N00603197EA0144001382000
    +B1351384533264N00603239EA0144001383000
    +B1351454533287N00603284EA0145301385000
    +B1351524533272N00603325EA0145901389000
    +B1351594533265N00603313EA0146601392000
    +B1352064533269N00603352EA0147301398000
    +B1352134533244N00603332EA0148501405000
    +B1352204533263N00603346EA0149801413000
    +B1352274533247N00603391EA0150401423000
    +B1352344533231N00603349EA0152201433000
    +B1352414533237N00603326EA0152401443000
    +B1352484533254N00603376EA0152101451000
    +B1352554533283N00603428EA0153201458000
    +B1353024533302N00603480EA0153401466000
    +B1353094533320N00603534EA0153601472000
    +B1353164533339N00603588EA0154301477000
    +B1353234533359N00603645EA0155001483000
    +B1353314533384N00603696EV0154701486000
    +B1353384533405N00603735EV0155501486000
    +B1353454533437N00603789EA0155101486000
    +B1353524533481N00603813EA0155201489000
    +B1353594533511N00603831EV0155701489000
    +B1354064533554N00603846EA0155001490000
    +B1354134533597N00603857EA0154401493000
    +B1354204533644N00603862EA0154001495000
    +B1354274533689N00603862EA0153501496000
    +B1354344533735N00603860EA0153501496000
    +B1354414533777N00603855EA0153801496000
    +B1354484533819N00603848EA0155101497000
    +B1354554533857N00603846EA0156301500000
    +B1355024533892N00603837EA0157801505000
    +B1355094533916N00603824EA0158801512000
    +B1355164533897N00603822EA0161201515000
    +B1355234533878N00603849EA0163301516000
    +B1355304533869N00603843EA0165801521000
    +B1355374533892N00603894EA0168001530000
    +B1355444533866N00603912EA0170201551000
    +B1355514533873N00603888EA0171901554000
    +B1355584533887N00603939EA0173601557000
    +B1356054533861N00603951EA0175401570000
    +B1356124533895N00603963EA0175901579000
    +B1356194533893N00603998EA0177101591000
    +B1356264533877N00603974EA0177901613000
    +B1356334533893N00603947EA0176601620000
    +B1356404533926N00603941EA0175701641000
    +B1356474533965N00603958EA0177201659000
    +B1356544534006N00603966EA0178801666000
    +B1357014534026N00603979EV0179301666000
    +B1357084534048N00603978EA0180401679000
    +B1357154534072N00603994EA0181301695000
    +B1357224534108N00604019EA0181801712000
    +B1357294534137N00604045EA0182401728000
    +B1357364534162N00604063EA0183501738000
    +B1357434534176N00604063EV0182801738000
    +B1357504534228N00604082EA0181701747000
    +B1357574534265N00604104EA0181401755000
    +B1358044534301N00604125EA0180301760000
    +B1358114534344N00604150EA0179601760000
    +B1358184534385N00604170EA0178401759000
    +B1358254534428N00604186EA0177001754000
    +B1358324534472N00604207EA0175901752000
    +B1358394534515N00604220EA0175701750000
    +B1358464534559N00604238EA0175601744000
    +B1358534534600N00604260EA0174301741000
    +B1359004534637N00604291EA0173101737000
    +B1359074534680N00604318EA0171901730000
    +B1359144534724N00604349EA0170701719000
    +B1359214534770N00604376EA0170101707000
    +B1359284534815N00604397EA0168801695000
    +B1359364534866N00604418EA0167901681000
    +B1359434534909N00604437EA0167301669000
    +B1359504534954N00604459EA0167401660000
    +B1359574534998N00604477EA0167501653000
    +B1400044535042N00604488EA0167201648000
    +B1400114535082N00604501EA0166801643000
    +B1400184535123N00604509EA0166401639000
    +B1400254535160N00604506EA0166801635000
    +B1400324535205N00604524EA0168101634000
    +B1400394535246N00604555EA0170601637000
    +B1400464535264N00604551EA0171701645000
    +B1400534535241N00604548EA0173101651000
    +B1401004535259N00604536EA0173001659000
    +B1401074535292N00604564EA0174001666000
    +B1401144535330N00604584EA0174901673000
    +B1401214535364N00604596EA0175801682000
    +B1401284535404N00604605EA0176301690000
    +B1401354535445N00604620EA0176501697000
    +B1401424535487N00604656EA0177101703000
    +B1401494535481N00604711EA0177401710000
    +B1401564535475N00604684EA0177401714000
    +B1402034535506N00604658EA0178001720000
    +B1402104535537N00604636EA0180001727000
    +B1402174535559N00604665EA0181501736000
    +B1402244535534N00604683EA0183201746000
    +B1402314535540N00604654EA0185201757000
    +B1402384535557N00604692EA0187201770000
    +B1402454535547N00604684EA0189301784000
    +B1402524535585N00604705EA0191801799000
    +B1402594535565N00604742EA0194301818000
    +B1403064535583N00604714EA0197001836000
    +B1403134535607N00604749EA0198601855000
    +B1403204535578N00604766EA0201401877000
    +B1403274535594N00604733EA0203701893000
    +B1403344535608N00604779EA0206601917000
    +B1403414535591N00604770EA0209401943000
    +B1403484535624N00604784EA0212501968000
    +B1403554535595N00604827EA0214601995000
    +B1404024535578N00604794EA0217302021000
    +B1404094535610N00604800EA0220202047000
    +B1404164535591N00604845EA0223202073000
    +B1404234535595N00604806EA0225802101000
    +B1404304535627N00604833EA0226702126000
    +B1404374535614N00604894EA0229002152000
    +B1404444535588N00604891EA0231502177000
    +B1404514535618N00604884EA0233102200000
    +B1404584535593N00604911EA0235502222000
    +B1405054535583N00604872EA0237902245000
    +B1405124535612N00604877EA0238702262000
    +B1405194535596N00604932EA0240602282000
    +B1405264535564N00604911EA0242302302000
    +B1405344535552N00604852EA0243402325000
    +B1405414535586N00604818EA0244102344000
    +B1405484535624N00604804EA0246002362000
    +B1405554535662N00604796EA0246002379000
    +B1406024535704N00604815EA0244402389000
    +B1406094535744N00604829EA0243602395000
    +B1406164535787N00604850EA0241602394000
    +B1406234535831N00604865EA0241302391000
    +B1406304535869N00604886EA0241002388000
    +B1406374535907N00604910EA0239602384000
    +B1406444535943N00604938EA0238002378000
    +B1406514535984N00604969EA0236102369000
    +B1406584536027N00604995EA0233602357000
    +B1407054536076N00605025EA0230702343000
    +B1407124536127N00605060EA0228402326000
    +B1407194536180N00605099EA0227202308000
    +B1407264536217N00605127EA0227902294000
    +B1407334536259N00605155EA0227902283000
    +B1407404536311N00605183EA0227902274000
    +B1407474536359N00605207EA0228502268000
    +B1407544536400N00605231EA0229202264000
    +B1408014536439N00605258EA0230302262000
    +B1408084536471N00605280EA0231802264000
    +B1408154536508N00605311EA0232402269000
    +B1408224536552N00605338EA0232002274000
    +B1408294536607N00605365EA0230602277000
    +B1408364536661N00605395EA0228702277000
    +B1408434536711N00605422EA0227502273000
    +B1408504536766N00605452EA0225802265000
    +B1408574536816N00605495EA0224102255000
    +B1409044536861N00605540EA0223402244000
    +B1409114536905N00605583EA0221702233000
    +B1409184536959N00605634EA0220202219000
    +B1409254537018N00605675EA0219102205000
    +B1409324537067N00605708EA0217802193000
    +B1409394537125N00605752EA0217202182000
    +B1409464537177N00605801EA0216102170000
    +B1409534537224N00605844EA0216102160000
    +B1410004537262N00605874EA0215702152000
    +B1410074537300N00605899EA0215602146000
    +B1410144537339N00605920EA0215902141000
    +B1410214537374N00605935EA0217202138000
    +B1410284537402N00605946EA0217902138000
    +B1410354537436N00605967EA0218902140000
    +B1410424537475N00605979EA0218402142000
    +B1410494537514N00605983EA0218202145000
    +B1410564537551N00606010EA0217802146000
    +B1411034537581N00606040EA0218102145000
    +B1411104537610N00606064EA0218602146000
    +B1411174537636N00606090EA0219302148000
    +B1411254537666N00606118EA0219402152000
    +B1411324537699N00606150EA0219302154000
    +B1411394537735N00606176EA0219002154000
    +B1411464537768N00606194EA0218402154000
    +B1411534537807N00606215EA0217702152000
    +B1412004537844N00606244EA0216602148000
    +B1412074537879N00606278EA0215902142000
    +B1412144537915N00606284EA0216602138000
    +B1412214537947N00606269EA0216502136000
    +B1412284537988N00606261EA0215402132000
    +B1412354538029N00606263EA0213802127000
    +B1412424538069N00606272EA0212502120000
    +B1412494538107N00606282EA0211102112000
    +B1412564538149N00606297EA0209602102000
    +B1413034538192N00606306EA0208202092000
    +B1413104538237N00606321EA0206702080000
    +B1413174538279N00606338EA0205702072000
    +B1413244538322N00606356EA0204802062000
    +B1413314538363N00606384EA0204202052000
    +B1413384538406N00606406EA0203102042000
    +B1413454538446N00606431EA0202502032000
    +B1413524538485N00606450EA0201302022000
    +B1413594538526N00606465EA0200202011000
    +B1414064538563N00606489EA0198902000000
    +B1414134538601N00606517EA0197501988000
    +B1414204538638N00606543EA0196301975000
    +B1414274538673N00606571EA0194701962000
    +B1414344538712N00606596EA0193401949000
    +B1414414538748N00606621EA0192401936000
    +B1414484538778N00606657EA0191601924000
    +B1414554538808N00606687EA0192101913000
    +B1415024538834N00606720EA0192301906000
    +B1415094538857N00606755EA0191601901000
    +B1415164538894N00606754EA0191401897000
    +B1415234538929N00606758EA0191301893000
    +B1415304538963N00606764EA0190701889000
    +B1415374538999N00606779EA0190201886000
    +B1415444539040N00606787EA0189701882000
    +B1415514539080N00606791EA0188901877000
    +B1415584539114N00606795EV0188101872000
    +B1416054539150N00606796EV0187001866000
    +B1416124539186N00606785EA0185801855000
    +B1416194539219N00606784EA0184501845000
    +B1416264539249N00606777EA0183201836000
    +B1416334539280N00606762EA0181501825000
    +B1416404539314N00606750EA0180101814000
    +B1416474539344N00606744EA0178801802000
    +B1416544539375N00606758EA0177701790000
    +B1417014539408N00606781EA0177101778000
    +B1417084539438N00606809EA0177101768000
    +B1417154539467N00606829EA0176301759000
    +B1417234539503N00606826EA0176001750000
    +B1417304539529N00606845EA0174901743000
    +B1417374539547N00606893EA0174701736000
    +B1417444539561N00606939EA0174901730000
    +B1417514539574N00606991EA0173801724000
    +B1417584539587N00607049EA0173701718000
    +B1418054539601N00607096EA0173401712000
    +B1418124539624N00607138EA0173401708000
    +B1418194539642N00607188EA0173001705000
    +B1418264539664N00607237EA0172901704000
    +B1418334539685N00607285EA0173101702000
    +B1418404539706N00607332EA0173201701000
    +B1418474539734N00607374EA0173601700000
    +B1418544539763N00607419EA0173201700000
    +B1419014539791N00607462EA0171201699000
    +B1419084539828N00607530EA0169001692000
    +B1419154539868N00607599EA0167101681000
    +B1419224539904N00607668EA0165601669000
    +B1419294539942N00607732EA0164101656000
    +B1419364539980N00607792EA0162601643000
    +B1419434540018N00607849EA0160801629000
    +B1419504540053N00607903EA0159401613000
    +B1419574540084N00607964EA0157801598000
    +B1420044540118N00608026EA0156001582000
    +B1420114540152N00608096EA0155201567000
    +B1420184540185N00608154EA0153801553000
    +B1420254540219N00608214EA0152701539000
    +B1420324540255N00608279EA0152401526000
    +B1420394540290N00608338EA0151701515000
    +B1420464540321N00608396EA0152801507000
    +B1420534540338N00608442EA0153601503000
    +B1421004540306N00608489EA0153501501000
    +B1421074540316N00608468EA0154201500000
    +B1421144540341N00608499EA0154601500000
    +B1421214540361N00608522EA0155001502000
    +B1421284540387N00608551EA0155201503000
    +B1421354540414N00608584EA0155801506000
    +B1421424540442N00608613EA0155001508000
    +B1421494540469N00608653EA0153901508000
    +B1421564540494N00608694EA0152901505000
    +B1422034540521N00608729EA0152101500000
    +B1422104540550N00608770EA0150301494000
    +B1422174540583N00608817EA0148401484000
    +B1422244540613N00608861EA0147001475000
    +B1422314540644N00608910EA0146101464000
    +B1422384540673N00608959EA0145301453000
    +B1422454540702N00609006EA0144501441000
    +B1422524540726N00609046EA0144501431000
    +B1422594540751N00609072EA0143401422000
    +B1423064540783N00609106EA0142901413000
    +B1423134540812N00609137EA0141801405000
    +B1423214540845N00609170EA0141501397000
    +B1423284540877N00609196EA0141601391000
    +B1423354540905N00609214EA0141201386000
    +B1423424540934N00609232EA0142901383000
    +B1423494540957N00609241EA0143901382000
    +B1423564540975N00609265EA0145501385000
    +B1424034540997N00609276EA0144901389000
    +B1424104541027N00609285EA0143801391000
    +B1424174541057N00609291EA0143301391000
    +B1424244541087N00609303EA0142601390000
    +B1424314541110N00609309EA0143801390000
    +B1424384541132N00609320EA0143801391000
    +B1424454541154N00609322EA0143201392000
    +B1424524541179N00609327EA0143001392000
    +B1424594541210N00609334EA0142901389000
    +B1425064541232N00609337EA0144501389000
    +B1425134541249N00609353EA0145101391000
    +B1425204541274N00609363EA0145101394000
    +B1425274541302N00609380EA0145201398000
    +B1425344541332N00609399EA0146401402000
    +B1425414541364N00609412EA0146701406000
    +B1425484541395N00609413EA0147901411000
    +B1425554541410N00609455EA0148801419000
    +B1426024541380N00609511EA0149001423000
    +B1426094541392N00609482EA0150501430000
    +B1426164541402N00609465EA0151001435000
    +B1426234541385N00609469EA0152301437000
    +B1426304541404N00609473EA0151701442000
    +B1426374541438N00609467EA0152001449000
    +B1426444541472N00609466EA0152601456000
    +B1426514541512N00609473EA0153201461000
    +B1426584541548N00609478EA0154101468000
    +B1427054541583N00609487EA0156101473000
    +B1427124541588N00609533EA0157701483000
    +B1427194541578N00609523EA0160001486000
    +B1427264541599N00609564EA0162101491000
    +B1427334541590N00609549EA0164701502000
    +B1427404541623N00609549EA0167401523000
    +B1427474541602N00609585EA0169901547000
    +B1427544541591N00609560EA0173501567000
    +B1428014541611N00609589EA0176101597000
    +B1428084541583N00609597EA0179001622000
    +B1428154541572N00609580EA0182401644000
    +B1428224541572N00609625EA0185801666000
    +B1428294541558N00609602EA0189101693000
    +B1428364541573N00609628EA0191901725000
    +B1428434541540N00609673EA0194801749000
    +B1428504541518N00609640EA0198101779000
    +B1428584541529N00609654EV0201001808000
    +B1429054541496N00609675EA0204101835000
    +B1429124541499N00609648EA0207201862000
    +B1429194541505N00609684EA0210501896000
    +B1429264541466N00609675EA0214001919000
    +B1429334541468N00609653EA0216801936000
    +B1429404541454N00609706EA0220301975000
    +B1429474541414N00609688EA0223402018000
    +B1429544541425N00609658EA0225802052000
    +B1430014541431N00609696EA0229102095000
    +B1430084541393N00609725EA0232502136000
    +B1430154541368N00609689EA0234702169000
    +B1430224541386N00609690EA0236502191000
    +B1430294541395N00609723EA0239802219000
    +B1430364541363N00609760EA0242802256000
    +B1430434541340N00609725EA0244902289000
    +B1430504541353N00609740EA0247802317000
    +B1430574541321N00609768EA0250702352000
    +B1431044541315N00609720EA0253402373000
    +B1431114541333N00609694EA0256202404000
    +B1431184541351N00609670EA0259502434000
    +B1431254541372N00609662EA0262702462000
    +B1431324541390N00609655EA0265902481000
    +B1431394541409N00609638EA0268102516000
    +B1431464541431N00609617EA0269702553000
    +B1431534541459N00609606EA0271102582000
    +B1432004541484N00609591EA0272302607000
    +B1432074541513N00609570EA0272502628000
    +B1432144541540N00609538EA0272002644000
    +B1432214541564N00609500EA0270902655000
    +B1432284541591N00609482EA0269602661000
    +B1432354541628N00609476EA0268402662000
    +B1432424541673N00609467EA0266202660000
    +B1432494541725N00609457EA0264802653000
    +B1432564541781N00609445EA0262802644000
    +B1433034541833N00609430EA0261702633000
    +B1433104541885N00609423EA0260602622000
    +B1433174541930N00609420EA0260202612000
    +B1433244541979N00609414EA0259502602000
    +B1433314542030N00609409EA0259102593000
    +B1433384542065N00609409EA0258002586000
    +B1433454542115N00609409EA0256702577000
    +B1433524542164N00609396EA0257302569000
    +B1433594542205N00609388EA0257702563000
    +B1434064542247N00609386EA0257202559000
    +B1434134542287N00609390EA0257602556000
    +B1434204542318N00609389EA0256302554000
    +B1434274542361N00609389EA0255302550000
    +B1434344542411N00609396EA0253702546000
    +B1434414542465N00609406EA0252902543000
    +B1434484542514N00609405EA0251002533000
    +B1434564542576N00609400EA0250102520000
    +B1435034542631N00609391EA0249502508000
    +B1435104542678N00609384EA0248302497000
    +B1435174542730N00609388EA0247102485000
    +B1435244542774N00609387EA0246002473000
    +B1435314542819N00609383EA0244402460000
    +B1435384542863N00609390EA0242702446000
    +B1435454542913N00609392EA0240802431000
    +B1435524542966N00609402EA0239202416000
    +B1435594543018N00609412EA0237702402000
    +B1436064543066N00609421EA0236602389000
    +B1436134543115N00609422EA0235202377000
    +B1436204543157N00609424EA0234102365000
    +B1436274543201N00609423EA0232002352000
    +B1436344543248N00609424EA0230502338000
    +B1436414543296N00609427EA0229302324000
    +B1436484543340N00609435EA0227302309000
    +B1436554543390N00609445EA0227402294000
    +B1437024543440N00609451EA0227602282000
    +B1437094543491N00609458EA0227002272000
    +B1437164543534N00609464EA0226302264000
    +B1437234543579N00609475EA0227502257000
    +B1437304543616N00609488EA0226502254000
    +B1437374543656N00609494EA0226802251000
    +B1437444543696N00609500EA0225802247000
    +B1437514543745N00609511EA0224802242000
    +B1437584543796N00609523EA0223402235000
    +B1438054543848N00609534EA0222202226000
    +B1438124543903N00609547EA0220402216000
    +B1438194543953N00609557EA0220002207000
    +B1438264544001N00609565EA0219802198000
    +B1438334544048N00609581EA0219802191000
    +B1438404544086N00609589EA0221702188000
    +B1438474544114N00609583EA0221802188000
    +B1438544544146N00609579EA0222102189000
    +B1439014544176N00609579EA0222002191000
    +B1439084544210N00609580EA0221302192000
    +B1439154544252N00609574EA0221902190000
    +B1439224544282N00609572EA0221502189000
    +B1439294544316N00609568EA0221702190000
    +B1439364544353N00609565EA0221102190000
    +B1439434544390N00609564EA0220602190000
    +B1439504544428N00609570EA0219902187000
    +B1439574544473N00609584EA0218502183000
    +B1440044544528N00609597EA0217802176000
    +B1440114544579N00609614EA0217502170000
    +B1440184544633N00609628EA0217702164000
    +B1440254544660N00609639EA0218602161000
    +B1440324544692N00609650EA0218902161000
    +B1440394544726N00609660EA0220102163000
    +B1440464544756N00609662EA0221402167000
    +B1440544544785N00609657EA0220702172000
    +B1441014544820N00609643EA0219902174000
    +B1441084544863N00609638EA0219002173000
    +B1441154544893N00609638EA0221702174000
    +B1441224544907N00609632EA0221702176000
    +B1441294544929N00609628EA0223102181000
    +B1441364544951N00609603EA0224702188000
    +B1441434544925N00609560EA0224102196000
    +B1441504544903N00609607EA0225102200000
    +B1441574544903N00609656EA0227402207000
    +B1442044544923N00609640EA0228002216000
    +B1442114544880N00609654EA0230202223000
    +B1442184544868N00609689EA0231902232000
    +B1442254544865N00609655EA0233702245000
    +B1442324544835N00609677EA0235602258000
    +B1442394544847N00609676EA0237402263000
    +B1442464544847N00609676EV0239202263000
    +B1442534544865N00609614EA0240402279000
    +B1443004544822N00609612EA0242102301000
    +B1443074544808N00609648EV0243402316000
    +B1443144544812N00609658EV0244702324000
    +B1443214544773N00609640EA0246002339000
    +B1443284544773N00609679EA0247702358000
    +B1443354544778N00609668EV0248602364000
    +B1443424544778N00609649EA0250402371000
    +B1443494544789N00609660EA0251802388000
    +B1443564544755N00609664EA0253002405000
    +B1444034544767N00609691EA0253502426000
    +B1444104544797N00609695EA0252302444000
    +B1444174544836N00609688EA0250302454000
    +B1444244544889N00609682EA0248402456000
    +B1444314544932N00609680EA0247202455000
    +B1444384544982N00609690EA0246202450000
    +B1444454545028N00609702EA0247102446000
    +B1444524545061N00609708EA0247502444000
    +B1444594545101N00609719EA0247502444000
    +B1445064545137N00609729EA0248502445000
    +B1445134545176N00609733EA0248402449000
    +B1445204545212N00609737EA0248402451000
    +B1445274545243N00609734EA0247702452000
    +B1445344545288N00609726EA0246402450000
    +B1445414545332N00609719EA0245402446000
    +B1445484545380N00609718EA0243902439000
    +B1445554545421N00609711EA0242702432000
    +B1446024545467N00609706EA0241402422000
    +B1446094545507N00609706EA0240402412000
    +B1446164545546N00609704EA0238202401000
    +B1446234545596N00609702EA0237102388000
    +B1446304545642N00609699EA0235702375000
    +B1446374545692N00609707EA0235102363000
    +B1446444545739N00609717EA0234602353000
    +B1446524545790N00609724EA0235102342000
    +B1446594545814N00609718EA0236602339000
    +B1447064545824N00609743EA0237802340000
    +B1447134545787N00609738EA0239102344000
    +B1447204545799N00609729EA0240102348000
    +B1447274545767N00609755EA0241302355000
    +B1447344545775N00609721EA0242602362000
    +B1447414545794N00609751EA0244202372000
    +B1447484545761N00609772EA0245202382000
    +B1447554545755N00609734EA0247002391000
    +B1448024545777N00609766EA0248802403000
    +B1448094545752N00609798EA0249802418000
    +B1448164545751N00609756EA0251202431000
    +B1448234545790N00609771EA0252502445000
    +B1448304545831N00609792EA0253202460000
    +B1448374545868N00609820EA0253402472000
    +B1448444545904N00609855EA0252902481000
    +B1448514545944N00609900EA0251002486000
    +B1448584545997N00609953EA0250702484000
    +B1449054546037N00609980EA0250502484000
    +B1449124546078N00609999EA0249702482000
    +B1449194546117N00610027EA0248502479000
    +B1449264546168N00610060EA0247802473000
    +B1449334546204N00610096EA0248402468000
    +B1449404546240N00610139EA0249102466000
    +B1449474546275N00610182EA0248302466000
    +B1449544546321N00610229EA0246202461000
    +B1450014546366N00610272EA0243302453000
    +B1450084546415N00610325EA0242302440000
    +B1450154546451N00610376EA0241502429000
    +B1450224546486N00610440EA0240302418000
    +B1450294546516N00610517EA0239602408000
    +B1450364546545N00610594EA0238502397000
    +B1450434546581N00610663EA0237702387000
    +B1450504546617N00610725EA0236502379000
    +B1450574546651N00610789EA0236002370000
    +B1451044546679N00610843EA0234202361000
    +B1451114546706N00610899EA0231902351000
    +B1451184546728N00610954EA0231002339000
    +B1451254546750N00611009EA0231002327000
    +B1451324546770N00611054EA0231102317000
    +B1451394546793N00611097EA0231502311000
    +B1451464546821N00611137EA0231802306000
    +B1451534546852N00611180EA0231102303000
    +B1452004546889N00611230EA0230402299000
    +B1452074546928N00611279EA0229002294000
    +B1452144546973N00611323EA0227502288000
    +B1452214547021N00611372EA0227902280000
    +B1452284547057N00611405EA0226502272000
    +B1452354547097N00611444EA0225302265000
    +B1452424547140N00611487EA0223402257000
    +B1452504547195N00611543EA0221302244000
    +B1452574547243N00611600EA0219502231000
    +B1453044547287N00611652EA0218302218000
    +B1453114547330N00611694EA0216702203000
    +B1453184547377N00611733EA0215602186000
    +B1453254547416N00611773EA0215802172000
    +B1453324547449N00611806EA0215002160000
    +B1453394547476N00611848EA0214002150000
    +B1453464547501N00611880EV0213402143000
    +B1453534547531N00611923EA0212902143000
    +B1454004547550N00611975EA0211802136000
    +B1454074547575N00612022EA0211102128000
    +B1454144547597N00612050EV0209702125000
    +B1454214547631N00612094EA0208702114000
    +B1454284547662N00612133EA0207702106000
    +B1454354547694N00612174EA0206902094000
    +B1454424547725N00612219EA0206302083000
    +B1454494547754N00612266EA0206002072000
    +B1454564547783N00612308EA0205502063000
    +B1455034547809N00612350EA0204902055000
    +B1455104547836N00612388EA0204102049000
    +B1455174547865N00612415EA0203402046000
    +B1455244547893N00612457EA0202502041000
    +B1455314547916N00612513EA0200802033000
    +B1455384547946N00612569EA0199402024000
    +B1455454547976N00612617EA0198202021000
    +B1455524548005N00612673EA0196802010000
    +B1455594548028N00612723EA0195402010000
    +B1456064548055N00612778EA0193602010000
    +B1456134548087N00612834EA0192402001000
    +B1456204548096N00612849EV0191402001000
    +B1456274548141N00612931EA0190401995000
    +B1456344548163N00612981EA0189501982000
    +B1456414548180N00613042EA0188801967000
    +B1456484548199N00613102EA0187401945000
    +B1456554548226N00613174EA0186301922000
    +B1457024548256N00613241EA0185401900000
    +B1457094548284N00613308EA0184201880000
    +B1457164548313N00613378EA0182901862000
    +B1457234548343N00613451EA0181601845000
    +B1457304548375N00613517EA0179901828000
    +B1457374548406N00613584EA0178101811000
    +B1457444548437N00613650EA0177001795000
    +B1457514548464N00613716EA0174801778000
    +B1457584548491N00613783EA0173501762000
    +B1458054548514N00613845EA0172201747000
    +B1458124548534N00613915EA0171001731000
    +B1458194548560N00613977EA0169901717000
    +B1458264548589N00614037EA0168801702000
    +B1458334548616N00614097EA0167701689000
    +B1458414548651N00614168EA0166301674000
    +B1458484548681N00614232EA0165101661000
    +B1458554548713N00614296EA0163301648000
    +B1459024548746N00614362EA0162301634000
    +B1459094548775N00614430EA0161101621000
    +B1459164548801N00614499EA0160201609000
    +B1459234548831N00614561EA0159201598000
    +B1459304548863N00614623EA0158401587000
    +B1459374548897N00614688EA0157301577000
    +B1459444548928N00614757EA0155901565000
    +B1459514548948N00614817EA0157401556000
    +B1459584548963N00614877EA0159601552000
    +B1500054548978N00614932EA0162601555000
    +B1500124548974N00614923EA0165601564000
    +B1500194548953N00614985EA0169101577000
    +B1500264548941N00614973EA0172301595000
    +B1500334548919N00615034EA0175701616000
    +B1500404548931N00615039EA0178901635000
    +B1500474548905N00615077EA0182701663000
    +B1500544548915N00615069EA0186001686000
    +B1501014548875N00615091EA0189701719000
    +B1501084548886N00615089EA0192701745000
    +B1501154548858N00615129EA0195901778000
    +B1501224548849N00615098EA0199501808000
    +B1501294548819N00615136EA0202001840000
    +B1501364548814N00615097EA0204701869000
    +B1501434548771N00615104EA0205801902000
    +B1501504548772N00615142EA0208001926000
    +B1501574548787N00615153EA0209901946000
    +B1502044548804N00615154EA0212001969000
    +B1502114548823N00615160EA0213601997000
    +B1502184548838N00615168EA0213902018000
    +B1502254548862N00615163EA0214602046000
    +B1502324548893N00615153EA0214402065000
    +B1502394548919N00615159EA0213202077000
    +B1502464548951N00615169EA0213002084000
    +B1502534548982N00615180EA0212002088000
    +B1503004549019N00615197EA0210902088000
    +B1503074549060N00615219EA0210102085000
    +B1503144549095N00615239EA0209102082000
    +B1503214549143N00615263EA0207902076000
    +B1503284549191N00615287EA0207302070000
    +B1503354549243N00615307EA0206702064000
    +B1503424549292N00615319EA0205902057000
    +B1503494549338N00615337EA0205102049000
    +B1503564549380N00615353EA0204502042000
    +B1504034549429N00615362EA0203702034000
    +B1504104549472N00615372EA0202902026000
    +B1504174549514N00615384EA0201402018000
    +B1504244549558N00615398EA0200402010000
    +B1504314549602N00615408EA0198902000000
    +B1504384549648N00615418EA0197001989000
    +B1504464549702N00615438EA0197201976000
    +B1504534549746N00615453EA0197401967000
    +B1505004549783N00615466EA0198101962000
    +B1505074549819N00615475EA0199201960000
    +B1505144549851N00615485EA0198701959000
    +B1505214549890N00615492EA0197301956000
    +B1505284549932N00615497EA0196501951000
    +B1505354549974N00615498EA0196301947000
    +B1505424550020N00615500EA0196501944000
    +B1505494550069N00615497EA0196601941000
    +B1505564550113N00615495EA0197501940000
    +B1506034550151N00615492EA0197001941000
    +B1506104550189N00615491EA0196801941000
    +B1506174550230N00615483EA0196001940000
    +B1506244550268N00615480EA0194401937000
    +B1506314550304N00615476EA0194201933000
    +B1506384550337N00615475EA0195001929000
    +B1506454550359N00615478EA0195601928000
    +B1506524550380N00615480EA0196901927000
    +B1506594550404N00615475EA0197401929000
    +B1507064550428N00615468EA0198601933000
    +B1507134550450N00615465EA0199501939000
    +B1507204550472N00615457EA0200601945000
    +B1507274550491N00615447EA0201201950000
    +B1507344550512N00615447EA0202301956000
    +B1507414550532N00615443EA0203201963000
    +B1507484550548N00615437EV0204501969000
    +B1507554550564N00615427EA0204701973000
    +B1508024550586N00615424EA0205401981000
    +B1508094550605N00615412EA0206701988000
    +B1508164550627N00615408EA0207801997000
    +B1508234550649N00615401EA0208602006000
    +B1508304550672N00615389EA0209202015000
    +B1508374550697N00615384EA0209902025000
    +B1508444550724N00615376EA0210302035000
    +B1508514550744N00615361EA0211802044000
    +B1508584550768N00615366EA0212002053000
    +B1509054550802N00615378EA0212002062000
    +B1509124550833N00615372EA0213202069000
    +B1509194550863N00615364EA0214402078000
    +B1509264550877N00615420EA0215102086000
    +B1509334550847N00615443EA0215602094000
    +B1509404550843N00615412EA0217002102000
    +B1509474550834N00615380EA0218002112000
    +B1509544550867N00615410EA0219202121000
    +B1510014550844N00615465EA0220502131000
    +B1510084550836N00615445EA0221402140000
    +B1510154550868N00615475EA0222202151000
    +B1510224550849N00615536EA0223102161000
    +B1510294550812N00615538EA0224102172000
    +B1510374550765N00615551EA0224802182000
    +B1510444550725N00615557EA0225802191000
    +B1510514550691N00615557EA0227002201000
    +B1510584550673N00615536EA0229302213000
    +B1511054550702N00615536EA0231602225000
    +B1511124550687N00615591EA0233302240000
    +B1511194550664N00615577EA0235402253000
    +B1511264550696N00615586EA0237302269000
    +B1511334550686N00615647EA0239002287000
    +B1511404550660N00615637EA0240302302000
    +B1511474550692N00615637EA0241902319000
    +B1511544550697N00615705EA0242602335000
    +B1512014550672N00615706EA0243102349000
    +B1512084550677N00615680EA0243702357000
    +B1512154550694N00615651EA0244302370000
    +B1512224550718N00615619EA0246002383000
    +B1512294550755N00615652EA0248502394000
    +B1512364550729N00615666EA0250602407000
    +B1512434550759N00615642EA0253802425000
    +B1512504550792N00615680EA0257102447000
    +B1512574550769N00615693EA0259602470000
    +B1513044550793N00615660EA0262702495000
    +B1513114550829N00615679EA0265702522000
    +B1513184550819N00615728EA0268402549000
    +B1513254550821N00615700EA0270902576000
    +B1513324550859N00615704EA0272602603000
    +B1513394550893N00615719EA0273402627000
    +B1513464550932N00615736EA0273102646000
    +B1513534550973N00615754EA0272502660000
    +B1514004551006N00615798EA0270702669000
    +B1514074551026N00615846EA0269202672000
    +B1514144551047N00615904EA0268302671000
    +B1514214551063N00615964EA0267802668000
    +B1514284551080N00616027EA0267002665000
    +B1514354551101N00616099EA0265302659000
    +B1514424551130N00616173EA0264002651000
    +B1514494551158N00616246EA0262402641000
    +B1514564551184N00616332EA0260702628000
    +B1515034551214N00616419EA0259302614000
    +B1515104551245N00616500EA0258102601000
    +B1515174551276N00616575EA0257502588000
    +B1515244551300N00616635EA0256902577000
    +B1515314551330N00616695EA0257102568000
    +B1515384551359N00616763EA0256602561000
    +B1515454551389N00616830EA0256202556000
    +B1515524551420N00616888EA0254702549000
    +B1515594551457N00616959EA0254002541000
    +B1516064551496N00617023EA0253202533000
    +B1516134551545N00617076EA0252602526000
    +B1516214551604N00617139EA0252002517000
    +B1516284551656N00617195EA0251602510000
    +B1516354551705N00617247EA0250702503000
    +B1516424551751N00617299EA0250502497000
    +B1516494551790N00617349EA0249902492000
    +B1516564551819N00617413EA0249702487000
    +B1517034551841N00617478EA0249602484000
    +B1517104551859N00617545EA0249302481000
    +B1517174551880N00617626EA0248202476000
    +B1517244551897N00617708EA0247502471000
    +B1517314551915N00617787EA0247202466000
    +B1517384551927N00617872EA0246302460000
    +B1517454551941N00617946EA0245602453000
    +B1517524551959N00618026EA0244402445000
    +B1517594551981N00618112EA0243102436000
    +B1518064552000N00618201EA0241602426000
    +B1518134552022N00618285EA0240302415000
    +B1518204552041N00618370EA0239302404000
    +B1518274552062N00618448EA0238402393000
    +B1518344552084N00618520EA0237302383000
    +B1518414552113N00618576EA0235702372000
    +B1518484552136N00618636EA0235002361000
    +B1518554552147N00618705EA0234602350000
    +B1519024552160N00618772EA0233802341000
    +B1519094552176N00618840EA0232702332000
    +B1519164552193N00618913EA0230802322000
    +B1519234552214N00619002EA0230002311000
    +B1519304552234N00619086EA0228602299000
    +B1519374552251N00619168EA0226902288000
    +B1519444552271N00619254EA0225802274000
    +B1519514552289N00619334EA0224402261000
    +B1519584552309N00619417EA0223102247000
    +B1520054552330N00619500EA0222302234000
    +B1520124552348N00619584EA0221002221000
    +B1520194552361N00619669EA0220102212000
    +B1520264552375N00619756EA0219502200000
    +B1520334552392N00619839EA0218602190000
    +B1520404552411N00619919EA0217702180000
    +B1520474552426N00620000EA0216602170000
    +B1520544552441N00620085EA0215802159000
    +B1521014552459N00620171EA0214802149000
    +B1521084552481N00620253EA0213602139000
    +B1521154552503N00620336EA0212702129000
    +B1521224552525N00620418EA0211502118000
    +B1521294552545N00620503EA0210302108000
    +B1521364552570N00620583EA0208902096000
    +B1521434552597N00620667EA0208002085000
    +B1521504552620N00620749EA0207202078000
    +B1521574552639N00620835EA0206102068000
    +B1522054552663N00620929EA0205002059000
    +B1522124552678N00621014EA0203802050000
    +B1522194552685N00621106EA0202402040000
    +B1522264552699N00621197EA0201202028000
    +B1522334552715N00621285EA0199802017000
    +B1522404552731N00621369EA0199002005000
    +B1522474552748N00621451EA0197901993000
    +B1522544552765N00621524EA0196901980000
    +B1523014552782N00621594EA0195801967000
    +B1523084552798N00621659EA0194601955000
    +B1523154552814N00621722EA0193701943000
    +B1523224552833N00621788EA0192601932000
    +B1523294552853N00621859EA0191901921000
    +B1523364552871N00621933EA0191001911000
    +B1523434552891N00622004EA0190501902000
    +B1523504552910N00622068EA0189801893000
    +B1523574552930N00622126EA0189701886000
    +B1524044552949N00622188EA0188801879000
    +B1524114552966N00622252EA0187901872000
    +B1524184552983N00622314EA0187001864000
    +B1524254553005N00622377EA0186201856000
    +B1524324553025N00622445EA0186301848000
    +B1524394553035N00622508EA0186301842000
    +B1524464553041N00622580EA0188101839000
    +B1524534553028N00622634EA0189601841000
    +B1525004552988N00622602EA0191501842000
    +B1525074552985N00622589EV0193401843000
    +B1525144552988N00622610EA0194501843000
    +B1525214552977N00622603EA0196701846000
    +B1525284553002N00622595EA0198401848000
    +B1525354553014N00622642EA0199601856000
    +B1525424552975N00622615EA0202301861000
    +B1525494552999N00622621EA0201401875000
    +B1525564552995N00622686EA0203601896000
    +B1526034552947N00622702EA0206001910000
    +B1526104552948N00622663EA0207801924000
    +B1526174552953N00622689EA0209001943000
    +B1526244552919N00622680EA0210401968000
    +B1526314552932N00622645EA0211001993000
    +B1526384552947N00622683EA0211402013000
    +B1526454552945N00622746EA0212202031000
    +B1526524552955N00622789EA0214902046000
    +B1526594552932N00622820EA0217102062000
    +B1527064552912N00622810EV0218702063000
    +B1527134552934N00622816EV0220602066000
    +B1527204552939N00622839EV0222302066000
    +B1527274552942N00622837EA0223602067000
    +B1527344552950N00622855EA0224502078000
    +B1527414552919N00622860EA0225802083000
    +B1527494552939N00622819EA0226402089000
    +B1527564552946N00622834EA0228002090000
    +B1528034552907N00622845EA0227902096000
    +B1528104552914N00622805EA0227602098000
    +B1528174552907N00622879EA0228502116000
    +B1528244552894N00622947EA0231502144000
    +B1528314552862N00622970EV0233602144000
    +B1528384552848N00622974EA0235502155000
    +B1528454552842N00623042EA0239202186000
    +B1528524552807N00623049EA0241702218000
    +B1528594552824N00623030EA0245102255000
    +B1529064552824N00623088EV0248502277000
    +B1529134552784N00623096EA0250202315000
    +B1529204552771N00623052EA0252002350000
    +B1529274552793N00623050EA0254502384000
    +B1529344552802N00623099EA0257402416000
    +B1529414552814N00623135EA0259102448000
    +B1529484552829N00623186EA0259902475000
    +B1529554552840N00623242EA0261602500000
    +B1530024552860N00623301EA0263302518000
    +B1530094552870N00623358EA0265402539000
    +B1530164552888N00623412EA0267902561000
    +B1530234552907N00623472EA0269602582000
    +B1530304552917N00623536EA0270902599000
    +B1530374552920N00623596EA0270602617000
    +B1530444552927N00623657EA0268902629000
    +B1530514552942N00623731EA0267402635000
    +B1530584552957N00623793EA0266602638000
    +B1531054552977N00623866EA0265302637000
    +B1531124553002N00623945EA0264202633000
    +B1531194553029N00624019EA0262902627000
    +B1531264553051N00624093EA0261902621000
    +B1531334553078N00624171EA0260402612000
    +B1531404553104N00624257EA0259202602000
    +B1531474553129N00624340EA0258702594000
    +B1531544553150N00624421EA0258102587000
    +B1532014553174N00624494EA0256602578000
    +B1532084553199N00624567EA0255502568000
    +B1532154553221N00624640EA0254202558000
    +B1532224553242N00624715EA0252802547000
    +B1532294553263N00624791EA0251802535000
    +B1532364553282N00624867EA0250702524000
    +B1532434553300N00624946EA0249502512000
    +B1532504553319N00625026EA0248302500000
    +B1532574553338N00625104EA0247302488000
    +B1533044553358N00625182EA0246202476000
    +B1533114553375N00625262EA0245302465000
    +B1533184553392N00625337EA0243902454000
    +B1533254553412N00625412EA0242802442000
    +B1533324553435N00625484EA0241402430000
    +B1533404553457N00625568EA0240302418000
    +B1533474553475N00625644EA0239402407000
    +B1533544553484N00625720EA0238302396000
    +B1534014553492N00625797EA0237302385000
    +B1534084553502N00625873EA0236502375000
    +B1534154553508N00625953EA0235502364000
    +B1534224553511N00626033EA0234502354000
    +B1534294553521N00626112EA0233302343000
    +B1534364553528N00626191EA0232202333000
    +B1534434553536N00626271EA0231002322000
    +B1534504553547N00626350EA0230102312000
    +B1534574553559N00626427EA0229302301000
    +B1535044553567N00626503EA0228202291000
    +B1535114553575N00626576EA0227102280000
    +B1535184553584N00626649EA0225902269000
    +B1535254553590N00626724EA0224802258000
    +B1535324553597N00626799EA0223602248000
    +B1535394553602N00626874EA0222702238000
    +B1535464553602N00626951EA0221802228000
    +B1535534553606N00627023EA0221002219000
    +B1536004553612N00627099EA0220302209000
    +B1536074553618N00627175EA0219302199000
    +B1536144553622N00627248EA0218402189000
    +B1536214553627N00627319EA0217802181000
    +B1536284553633N00627391EA0217302174000
    +B1536354553643N00627461EA0216602167000
    +B1536424553656N00627532EA0216002162000
    +B1536494553670N00627599EA0215402158000
    +B1536564553680N00627666EA0214802154000
    +B1537034553694N00627734EA0214302151000
    +B1537104553709N00627802EA0213702146000
    +B1537174553724N00627869EA0212902139000
    +B1537244553741N00627938EA0212002133000
    +B1537314553752N00628009EA0211302128000
    +B1537384553766N00628081EA0210402121000
    +B1537454553782N00628153EA0209402115000
    +B1537524553795N00628231EA0208502106000
    +B1537594553809N00628310EA0207702096000
    +B1538064553826N00628391EA0207202087000
    +B1538134553844N00628466EA0207102079000
    +B1538204553855N00628530EA0207402074000
    +B1538274553870N00628592EA0206902068000
    +B1538344553889N00628653EA0206102062000
    +B1538414553908N00628711EA0205102054000
    +B1538484553927N00628766EA0204702047000
    +B1538554553951N00628813EA0204302041000
    +B1539024553980N00628862EA0204202037000
    +B1539094554008N00628912EA0204002031000
    +B1539164554038N00628960EA0203402025000
    +B1539234554068N00629012EA0202602019000
    +B1539304554101N00629062EA0202202013000
    +B1539374554138N00629109EA0203202008000
    +B1539454554174N00629145EA0202402006000
    +B1539524554209N00629163EA0202302005000
    +B1539594554241N00629177EV0202102005000
    +B1540064554283N00629187EA0202602004000
    +B1540134554317N00629193EA0203002004000
    +B1540204554346N00629194EA0203802004000
    +B1540274554374N00629198EA0204302006000
    +B1540344554380N00629186EV0204302008000
    +B1540414554363N00629170EA0204802009000
    +B1540484554344N00629165EA0204702010000
    +B1540554554368N00629178EA0205102011000
    +B1541024554400N00629210EA0206002013000
    +B1541094554431N00629236EA0207002016000
    +B1541164554440N00629214EA0207502018000
    +B1541234554422N00629255EA0207702021000
    +B1541304554451N00629285EA0209302025000
    +B1541374554463N00629269EA0209802032000
    +B1541444554431N00629293EA0210502039000
    +B1541514554404N00629284EA0211202046000
    +B1541584554410N00629278EA0212302048000
    +B1542054554434N00629297EA0212502049000
    +B1542124554420N00629280EA0213502055000
    +B1542194554391N00629316EA0213802059000
    +B1542264554382N00629322EV0213602059000
    +B1542334554373N00629360EA0214802062000
    +B1542404554396N00629330EA0215002067000
    +B1542474554396N00629368EV0215502076000
    +B1542544554415N00629364EV0216402080000
    +B1543014554428N00629323EA0214702082000
    +B1543084554457N00629304EA0216502091000
    +B1543154554460N00629311EA0217002095000
    +B1543224554488N00629318EA0216802105000
    +B1543294554455N00629310EA0218202112000
    +B1543364554435N00629351EA0218102117000
    +B1543434554455N00629346EA0219202124000
    +B1543504554439N00629333EA0219602130000
    +B1543574554443N00629382EA0220402137000
    +B1544044554427N00629363EA0221702143000
    +B1544114554441N00629332EA0221802148000
    +B1544184554469N00629357EA0223202150000
    +B1544254554447N00629391EA0224402150000
    +B1544324554450N00629383EA0224802157000
    +B1544394554459N00629424EA0225602165000
    +B1544464554427N00629412EA0227002178000
    +B1544534554450N00629407EA0227902192000
    +B1545004554455N00629459EA0228702194000
    +B1545074554419N00629450EA0229002205000
    +B1545144554427N00629421EA0229602218000
    +B1545214554422N00629477EA0230602228000
    +B1545284554385N00629481EA0231402240000
    +B1545354554396N00629453EA0231602250000
    +B1545434554394N00629512EA0232102260000
    +B1545504554361N00629513EA0233102269000
    +B1545574554371N00629478EA0232702275000
    +B1546044554393N00629489EV0232702278000
    +B1546114554405N00629522EA0232402279000
    +B1546184554390N00629486EA0232102281000
    +B1546254554410N00629468EA0232302284000
    +B1546324554437N00629463EA0234202288000
    +B1546394554416N00629436EA0234802289000
    +B1546464554426N00629443EA0236702293000
    +B1546534554416N00629414EA0237702297000
    +B1547004554384N00629439EA0238002304000
    +B1547074554406N00629425EA0238402311000
    +B1547144554434N00629451EV0239302311000
    +B1547214554408N00629480EA0240002312000
    +B1547284554390N00629453EA0240002314000
    +B1547354554397N00629446EV0241302315000
    +B1547424554441N00629428EA0241502318000
    +B1547494554416N00629415EA0242302330000
    +B1547564554433N00629444EA0243402343000
    +B1548034554400N00629499EA0244002347000
    +B1548104554395N00629478EA0244902354000
    +B1548174554419N00629512EA0245902366000
    +B1548244554383N00629549EA0246602381000
    +B1548314554376N00629520EA0247602390000
    +B1548384554404N00629538EA0248802397000
    +B1548454554373N00629555EA0249502407000
    +B1548524554351N00629511EA0249702420000
    +B1548594554380N00629533EA0250802432000
    +B1549064554357N00629563EA0251302442000
    +B1549134554368N00629558EA0251902449000
    +B1549204554357N00629605EA0252502457000
    +B1549274554322N00629590EA0254302467000
    +B1549344554346N00629602EA0255402477000
    +B1549414554316N00629641EA0256202487000
    +B1549484554291N00629611EA0258102499000
    +B1549554554313N00629634EA0260002512000
    +B1550024554281N00629652EA0261102527000
    +B1550094554281N00629616EA0261802541000
    +B1550164554304N00629646EA0264002555000
    +B1550234554274N00629652EA0265202567000
    +B1550304554304N00629647EA0267202581000
    +B1550374554297N00629694EA0268302595000
    +B1550444554272N00629667EA0268902610000
    +B1550514554297N00629671EA0270602624000
    +B1550584554289N00629724EA0271802639000
    +B1551054554255N00629741EA0271902652000
    +B1551124554228N00629710EA0272502659000
    +B1551194554255N00629700EA0272902669000
    +B1551264554251N00629754EA0273502678000
    +B1551334554232N00629814EA0274602685000
    +B1551414554214N00629876EA0275402694000
    +B1551484554205N00629903EV0276202695000
    +B1551554554179N00629995EA0277202697000
    +B1552024554177N00630061EA0277402706000
    +B1552094554175N00630120EA0276702715000
    +B1552164554169N00630183EA0275802719000
    +B1552234554165N00630210EV0274902721000
    +B1552304554150N00630301EV0273702722000
    +B1552374554141N00630368EA0272202722000
    +B1552444554130N00630430EA0271102721000
    +B1552514554117N00630492EA0270202718000
    +B1552584554097N00630558EA0269602712000
    +B1553054554074N00630616EA0269002704000
    +B1553124554047N00630671EA0268302696000
    +B1553194554018N00630727EA0267902689000
    +B1553264553991N00630782EA0267102681000
    +B1553334553964N00630837EA0266702673000
    +B1553404553940N00630892EA0266702665000
    +B1553474553923N00630951EA0266702660000
    +B1553544553909N00631015EA0266802655000
    +B1554014553889N00631066EA0267802653000
    +B1554084553853N00631072EA0268302653000
    +B1554154553864N00631023EA0269302655000
    +B1554224553899N00631032EA0269602658000
    +B1554294553897N00631087EA0270302662000
    +B1554364553863N00631107EA0270302667000
    +B1554434553852N00631055EA0270802670000
    +B1554504553886N00631049EA0271502675000
    +B1554574553887N00631107EA0272602679000
    +B1555044553884N00631157EA0272302685000
    +B1555114553877N00631223EA0272502688000
    +B1555184553873N00631286EA0272402691000
    +B1555254553872N00631349EA0272202694000
    +B1555324553874N00631413EA0272502695000
    +B1555394553872N00631474EA0273102698000
    +B1555464553875N00631527EA0272602701000
    +B1555534553874N00631584EA0272002702000
    +B1556004553872N00631641EA0271402703000
    +B1556074553866N00631699EA0270402703000
    +B1556144553857N00631758EA0269002700000
    +B1556214553850N00631819EA0267902697000
    +B1556284553849N00631877EA0267202696000
    +B1556354553840N00631940EA0266002689000
    +B1556424553824N00632003EA0265202684000
    +B1556494553812N00632063EA0264302682000
    +B1556564553802N00632119EA0263302678000
    +B1557034553790N00632183EA0263402670000
    +B1557104553778N00632248EA0264002660000
    +B1557174553774N00632304EA0264702654000
    +B1557254553748N00632315EA0265302647000
    +B1557324553761N00632274EA0265402643000
    +B1557394553784N00632323EA0265002640000
    +B1557464553773N00632387EA0265702638000
    +B1557534553755N00632443EA0266702637000
    +B1558004553737N00632483EA0267102639000
    +B1558074553719N00632441EA0267202642000
    +B1558144553749N00632402EA0268102645000
    +B1558214553774N00632431EA0268602649000
    +B1558284553747N00632435EA0268902654000
    +B1558354553766N00632406EA0269002657000
    +B1558424553776N00632458EA0269502660000
    +B1558494553770N00632508EA0269702663000
    +B1558564553765N00632563EA0268402665000
    +B1559034553762N00632625EA0267602664000
    +B1559104553759N00632686EA0266702660000
    +B1559174553758N00632745EA0266202656000
    +B1559244553755N00632806EA0264802650000
    +B1559314553752N00632867EA0263402641000
    +B1559384553750N00632931EA0262502632000
    +B1559454553749N00632990EA0261102624000
    +B1559524553759N00633052EA0260102615000
    +B1559594553761N00633115EA0259802606000
    +B1600064553759N00633180EA0259302598000
    +B1600134553757N00633249EA0259202592000
    +B1600204553750N00633315EA0258502585000
    +B1600274553746N00633387EA0257902578000
    +B1600344553749N00633452EA0258302572000
    +B1600414553754N00633514EA0257702569000
    +B1600484553744N00633581EA0257102565000
    +B1600554553736N00633648EA0257002560000
    +B1601024553735N00633707EA0256302557000
    +B1601094553732N00633779EA0256202552000
    +B1601164553734N00633850EA0255702549000
    +B1601234553736N00633918EA0255402545000
    +B1601304553728N00633982EA0254802540000
    +B1601374553715N00634044EA0254602536000
    +B1601444553701N00634105EA0254202533000
    +B1601514553691N00634169EA0254102531000
    +B1601584553685N00634235EA0253802529000
    +B1602054553681N00634300EA0253202526000
    +B1602124553676N00634364EA0252802523000
    +B1602194553676N00634432EA0252202519000
    +B1602264553680N00634501EA0251502514000
    +B1602334553687N00634570EA0250802509000
    +B1602404553693N00634641EA0250002502000
    +B1602474553694N00634711EA0249502496000
    +B1602544553682N00634779EA0248902490000
    +B1603024553672N00634856EA0248102483000
    +B1603094553668N00634923EA0247302478000
    +B1603164553661N00634992EA0246602471000
    +B1603234553652N00635061EA0246002465000
    +B1603304553645N00635133EA0245302458000
    +B1603374553644N00635207EA0244702452000
    +B1603444553643N00635277EA0243902445000
    +B1603514553642N00635350EA0242902438000
    +B1603584553643N00635423EA0242102430000
    +B1604054553647N00635495EA0241102421000
    +B1604124553648N00635567EA0240302413000
    +B1604194553646N00635640EA0239502404000
    +B1604264553641N00635712EA0238702396000
    +B1604334553637N00635784EA0237902387000
    +B1604404553630N00635853EA0237202378000
    +B1604474553620N00635923EA0236502370000
    +B1604544553609N00635992EA0235702361000
    +B1605014553602N00636063EA0235002353000
    +B1605084553597N00636131EA0234402345000
    +B1605154553593N00636197EA0233702338000
    +B1605224553590N00636263EA0232902331000
    +B1605294553585N00636329EA0232102323000
    +B1605364553577N00636395EA0231402316000
    +B1605434553568N00636461EA0230602308000
    +B1605504553560N00636526EA0229902300000
    +B1605574553551N00636591EA0229102293000
    +B1606044553543N00636657EA0228402285000
    +B1606114553537N00636724EA0227602279000
    +B1606184553534N00636790EA0226802272000
    +B1606254553526N00636856EA0226102265000
    +B1606324553518N00636922EA0225602259000
    +B1606394553511N00636990EA0225002253000
    +B1606464553505N00637056EA0224302250000
    +B1606534553499N00637122EA0223502242000
    +B1607004553496N00637190EA0222802233000
    +B1607074553495N00637257EA0222102225000
    +B1607144553494N00637325EA0221402216000
    +B1607214553492N00637392EA0220602207000
    +B1607284553487N00637459EA0219802200000
    +B1607354553484N00637528EA0218802192000
    +B1607424553479N00637596EA0218002184000
    +B1607494553472N00637664EA0217102174000
    +B1607564553468N00637733EA0216102165000
    +B1608034553463N00637801EA0215302156000
    +B1608104553459N00637871EA0214302147000
    +B1608174553459N00637941EA0213202137000
    +B1608244553459N00638012EA0212202127000
    +B1608314553455N00638082EA0211302117000
    +B1608384553446N00638147EA0210202108000
    +B1608454553444N00638216EA0209502100000
    +B1608524553444N00638287EA0208602091000
    +B1609004553447N00638367EA0207602080000
    +B1609074553449N00638436EA0206802072000
    +B1609144553451N00638507EA0206102064000
    +B1609214553449N00638579EA0205402056000
    +B1609284553446N00638649EA0204802050000
    +B1609354553443N00638720EA0204202042000
    +B1609424553440N00638790EA0203502034000
    +B1609494553436N00638861EA0202602028000
    +B1609564553434N00638931EA0201702022000
    +B1610034553431N00639003EA0200702014000
    +B1610104553427N00639070EA0199502005000
    +B1610174553423N00639142EA0198401995000
    +B1610244553417N00639212EA0197701984000
    +B1610314553410N00639281EA0196901974000
    +B1610384553406N00639355EA0196301965000
    +B1610454553407N00639423EA0195001956000
    +B1610524553408N00639488EA0193501946000
    +B1610594553409N00639554EA0192501935000
    +B1611064553403N00639621EA0191301925000
    +B1611134553396N00639692EA0190201914000
    +B1611204553385N00639766EA0190401904000
    +B1611274553376N00639836EA0189801895000
    +B1611344553362N00639907EA0189401888000
    +B1611414553353N00639978EA0188901882000
    +B1611484553348N00640049EA0188101876000
    +B1611554553348N00640119EA0187701870000
    +B1612024553349N00640191EA0187301864000
    +B1612094553351N00640264EA0186801858000
    +B1612164553351N00640339EA0186401852000
    +B1612234553349N00640415EA0185701846000
    +B1612304553344N00640492EA0185001840000
    +B1612374553340N00640569EA0184501833000
    +B1612444553338N00640647EA0184201827000
    +B1612514553340N00640725EA0183801822000
    +B1612584553339N00640800EA0184201818000
    +B1613054553339N00640864EA0182901814000
    +B1613124553337N00640939EA0182401810000
    +B1613194553335N00641016EA0181701806000
    +B1613264553335N00641093EA0181101801000
    +B1613334553336N00641171EA0180301795000
    +B1613404553339N00641248EA0179701789000
    +B1613474553341N00641327EA0179101782000
    +B1613544553341N00641408EA0178701776000
    +B1614014553343N00641484EA0177901769000
    +B1614084553352N00641558EA0177001763000
    +B1614154553360N00641634EA0176101756000
    +B1614224553373N00641706EA0175301749000
    +B1614294553384N00641780EA0174301742000
    +B1614364553390N00641857EA0173101732000
    +B1614444553397N00641948EA0172201721000
    +B1614514553400N00642027EA0171601712000
    +B1614584553404N00642103EA0170901703000
    +B1615054553411N00642181EA0170401695000
    +B1615124553416N00642258EA0169901688000
    +B1615194553422N00642335EA0169101681000
    +B1615264553429N00642412EA0168101672000
    +B1615334553435N00642489EA0167001664000
    +B1615404553441N00642568EA0166301655000
    +B1615474553445N00642642EA0165401646000
    +B1615544553448N00642709EA0164401637000
    +B1616014553452N00642775EA0163401628000
    +B1616084553463N00642846EA0162801620000
    +B1616154553467N00642920EA0162101613000
    +B1616224553470N00642995EA0160901605000
    +B1616294553472N00643076EA0161101598000
    +B1616364553477N00643152EA0160601591000
    +B1616434553481N00643230EA0160901586000
    +B1616504553483N00643308EA0161401583000
    +B1616574553479N00643385EA0161901582000
    +B1617044553475N00643461EA0162001582000
    +B1617114553442N00643506EA0161601582000
    +B1617184553432N00643476EA0161801581000
    +B1617254553454N00643460EA0162201582000
    +B1617324553483N00643472EA0162701582000
    +B1617394553476N00643539EA0162701583000
    +B1617464553438N00643542EA0163201584000
    +B1617534553444N00643517EA0163501585000
    +B1618004553465N00643573EA0164201586000
    +B1618074553435N00643620EA0164401586000
    +B1618144553436N00643596EA0164701587000
    +B1618214553463N00643645EA0165001589000
    +B1618284553455N00643724EA0165501594000
    +B1618354553418N00643756EA0165901599000
    +B1618424553418N00643734EA0166101601000
    +B1618494553443N00643777EA0166001606000
    +B1618564553419N00643840EA0166301608000
    +B1619034553376N00643854EA0166601611000
    +B1619104553348N00643912EA0166801614000
    +B1619174553365N00643970EA0167001618000
    +B1619244553391N00643977EA0167101621000
    +B1619314553379N00643960EA0167301624000
    +B1619384553338N00643964EA0167501626000
    +B1619454553292N00643983EA0167501628000
    +B1619524553248N00644003EA0167401629000
    +B1619594553204N00644021EA0167501630000
    +B1620064553163N00644040EA0167501631000
    +B1620134553117N00644050EA0167501632000
    +B1620204553073N00644065EA0168301634000
    +B1620274553063N00644044EA0169001636000
    +B1620344553084N00644073EA0168501639000
    +B1620424553054N00644114EA0169201642000
    +B1620494553013N00644141EA0169701646000
    +B1620564552973N00644169EA0170401650000
    +B1621034552955N00644149EA0171501654000
    +B1621104552983N00644168EA0173101660000
    +B1621174553010N00644198EA0174001668000
    +B1621244553037N00644214EA0174801677000
    +B1621314553038N00644199EV0175001682000
    +B1621384553004N00644154EA0175901682000
    +B1621454553004N00644140EA0176501684000
    +B1621524553009N00644184EV0176701687000
    +B1621594552974N00644200EA0178101691000
    +B1622064552989N00644207EA0178501692000
    +B1622134552962N00644251EA0179601700000
    +B1622204552940N00644268EV0180301707000
    +B1622274552892N00644291EA0181201711000
    +B1622344552892N00644291EV0181701711000
    +B1622414552892N00644291EV0182301711000
    +B1622484552779N00644345EA0182801711000
    +B1622554552734N00644366EA0183301724000
    +B1623024552695N00644378EA0184601741000
    +B1623094552698N00644353EA0186201753000
    +B1623164552721N00644394EA0187701770000
    +B1623234552679N00644424EA0189001785000
    +B1623304552657N00644398EA0191401801000
    +B1623374552667N00644449EA0192701811000
    +B1623444552647N00644453EA0194701821000
    +B1623514552646N00644510EA0196001837000
    +B1623584552627N00644500EA0197701853000
    +B1624054552638N00644490EA0198601866000
    +B1624124552651N00644508EA0197601884000
    +B1624194552619N00644576EA0198501903000
    +B1624264552587N00644581EA0198501917000
    +B1624334552598N00644573EA0198201923000
    +B1624404552608N00644559EA0197601928000
    +B1624474552615N00644539EA0197101931000
    +B1624544552623N00644522EA0197301933000
    +B1625014552641N00644530EA0196601935000
    +B1625084552664N00644544EA0195901934000
    +B1625154552665N00644519EA0194301932000
    +B1625224552653N00644490EA0195101928000
    +B1625294552629N00644468EA0195101925000
    +B1625364552601N00644448EA0195001923000
    +B1625434552600N00644427EA0194801921000
    +B1625504552616N00644412EA0194901920000
    +B1625574552634N00644402EA0195001920000
    +B1626044552659N00644411EA0194901919000
    +B1626114552684N00644428EA0194801918000
    +B1626184552677N00644418EA0194101917000
    +B1626254552632N00644424EA0193701917000
    +B1626334552600N00644406EA0193901915000
    +B1626404552609N00644385EA0193901913000
    +B1626474552630N00644435EA0194201912000
    +B1626544552595N00644484EA0194401911000
    +B1627014552553N00644490EA0195501911000
    +B1627084552539N00644483EA0195701912000
    +B1627154552567N00644513EA0196801914000
    +B1627224552557N00644575EA0196501914000
    +B1627294552537N00644554EA0197301919000
    +B1627364552552N00644541EA0196501923000
    +B1627434552569N00644529EA0196301925000
    +B1627504552594N00644529EA0195601925000
    +B1627574552620N00644528EA0195501923000
    +B1628044552646N00644529EA0195401923000
    +B1628114552670N00644525EA0194901921000
    +B1628184552695N00644523EA0194601919000
    +B1628254552719N00644519EA0194001916000
    +B1628324552745N00644513EA0194101912000
    +B1628394552767N00644512EA0194401910000
    +B1628464552786N00644503EA0195601910000
    +B1628534552798N00644563EA0195701911000
    +B1629004552761N00644607EA0196701911000
    +B1629074552751N00644587EA0197401912000
    +B1629144552771N00644604EA0197301916000
    +B1629214552744N00644652EA0197601921000
    +B1629284552715N00644639EA0197401925000
    +B1629354552722N00644613EA0197201927000
    +B1629424552747N00644601EA0197501930000
    +B1629494552775N00644601EA0197101932000
    +B1629564552804N00644601EA0197301933000
    +B1630034552832N00644598EA0196601934000
    +B1630104552857N00644598EA0196101934000
    +B1630174552881N00644596EA0196101934000
    +B1630244552904N00644594EA0195601934000
    +B1630314552929N00644585EA0196201931000
    +B1630384552957N00644583EA0197201930000
    +B1630454552987N00644582EA0197901932000
    +B1630524553014N00644583EA0198301936000
    +B1630594553048N00644595EA0198801939000
    +B1631064553079N00644602EA0197601943000
    +B1631134553112N00644607EA0196601942000
    +B1631204553148N00644625EA0195601940000
    +B1631274553183N00644638EA0195101935000
    +B1631344553217N00644651EA0194201931000
    +B1631414553254N00644667EA0194101926000
    +B1631484553294N00644684EA0193601921000
    +B1631554553335N00644702EA0193101918000
    +B1632024553375N00644715EA0192201913000
    +B1632094553416N00644726EA0191301907000
    +B1632164553460N00644736EA0190901900000
    +B1632234553495N00644733EA0192601895000
    +B1632314553525N00644740EA0193701895000
    +B1632384553535N00644785EA0195301896000
    +B1632454553517N00644786EA0196501901000
    +B1632524553549N00644804EA0197801909000
    +B1632594553539N00644874EA0199301916000
    +B1633064553528N00644857EA0200801926000
    +B1633134553555N00644912EA0202101937000
    +B1633204553527N00644938EA0203601949000
    +B1633274553547N00644931EA0205201959000
    +B1633344553547N00645001EA0206101973000
    +B1633414553532N00644987EA0208501983000
    +B1633484553556N00644997EA0209001996000
    +B1633554553561N00645079EA0210302012000
    +B1634024553543N00645080EA0212402024000
    +B1634094553554N00645144EA0213702035000
    +B1634164553531N00645148EA0215202052000
    +B1634234553553N00645180EA0216502069000
    +B1634304553540N00645229EA0217802078000
    +B1634374553546N00645221EA0219202087000
    +B1634444553548N00645291EA0220002101000
    +B1634514553517N00645294EA0220902114000
    +B1634584553537N00645280EA0221902128000
    +B1635054553545N00645338EV0222002138000
    +B1635124553510N00645377EA0223302144000
    +B1635194553501N00645351EV0224402151000
    +B1635264553521N00645391EA0224602158000
    +B1635334553495N00645442EA0225602161000
    +B1635404553476N00645422EA0226302170000
    +B1635474553499N00645432EA0226002179000
    +B1635544553482N00645499EA0226902186000
    +B1636014553452N00645489EA0227202195000
    +B1636084553478N00645496EA0226902196000
    +B1636154553481N00645577EA0227402203000
    +B1636224553453N00645636EA0227302211000
    +B1636294553419N00645694EA0226402217000
    +B1636364553391N00645766EA0224702219000
    +B1636434553364N00645838EA0223302219000
    +B1636504553337N00645908EA0222402214000
    +B1636574553309N00645969EA0221202208000
    +B1637044553282N00646032EA0220302200000
    +B1637114553256N00646093EA0219502192000
    +B1637184553233N00646154EA0218602184000
    +B1637254553209N00646215EA0217702176000
    +B1637324553176N00646266EA0218102169000
    +B1637394553155N00646315EA0218002165000
    +B1637464553134N00646373EA0217202161000
    +B1637534553105N00646429EA0216502157000
    +B1638004553077N00646481EA0216002152000
    +B1638074553057N00646538EA0214602147000
    +B1638144553036N00646600EA0213602140000
    +B1638224553005N00646667EA0213002131000
    +B1638294552978N00646721EA0212502123000
    +B1638364552953N00646781EA0211602115000
    +B1638434552929N00646847EA0210802107000
    +B1638504552902N00646909EA0210102098000
    +B1638574552876N00646969EA0209102090000
    +B1639044552851N00647025EA0208302081000
    +B1639114552823N00647080EA0207602073000
    +B1639184552793N00647137EA0206302065000
    +B1639254552762N00647195EA0205102056000
    +B1639324552731N00647248EA0205102049000
    +B1639394552699N00647306EA0204602042000
    +B1639464552665N00647358EA0203802035000
    +B1639534552635N00647420EA0203602028000
    +B1640004552607N00647487EA0203702022000
    +B1640074552578N00647553EA0203402018000
    +B1640144552549N00647619EA0202602013000
    +B1640214552527N00647691EA0202102008000
    +B1640284552505N00647764EA0202002004000
    +B1640354552483N00647838EA0201902000000
    +B1640424552464N00647914EA0202001997000
    +B1640494552441N00647991EA0201601994000
    +B1640564552420N00648058EA0202301992000
    +B1641034552394N00648116EA0202201991000
    +B1641104552366N00648171EA0202901991000
    +B1641174552338N00648219EA0203501993000
    +B1641244552318N00648272EA0203301995000
    +B1641314552310N00648332EA0202801996000
    +B1641384552308N00648398EA0202601995000
    +B1641454552306N00648463EA0202901995000
    +B1641524552304N00648525EA0202901996000
    +B1641594552304N00648585EA0203701997000
    +B1642064552311N00648640EA0203401998000
    +B1642134552318N00648697EA0203401999000
    +B1642204552323N00648749EA0203602000000
    +B1642274552324N00648799EA0203802002000
    +B1642344552332N00648850EA0203302003000
    +B1642414552334N00648903EA0203102003000
    +B1642484552346N00648955EA0203102001000
    +B1642554552362N00648999EA0202902001000
    +B1643024552390N00649033EA0203402000000
    +B1643094552414N00649062EA0204402001000
    +B1643164552440N00649086EA0205702004000
    +B1643234552455N00649077EA0205002009000
    +B1643304552412N00649061EA0205902012000
    +B1643374552368N00649050EA0206902013000
    +B1643444552340N00649027EA0206302014000
    +B1643514552365N00649018EA0207702015000
    +B1643584552380N00649060EA0208102021000
    +B1644054552402N00649096EA0209102027000
    +B1644124552409N00649072EA0209202035000
    +B1644194552365N00649056EA0210502041000
    +B1644274552325N00649041EA0210702047000
    +B1644344552345N00649017EA0210502051000
    +B1644414552366N00649056EA0211402057000
    +B1644484552384N00649093EA0212002064000
    +B1644554552407N00649121EA0212402070000
    +B1645024552400N00649093EA0211902074000
    +B1645094552359N00649071EA0212102077000
    +B1645164552327N00649040EA0212702081000
    +B1645234552339N00649005EA0212302083000
    +B1645304552351N00649058EA0213102087000
    +B1645374552373N00649095EA0213502091000
    +B1645444552370N00649068EA0212902094000
    +B1645514552326N00649058EA0213202095000
    +B1645584552289N00649034EA0213602095000
    +B1646054552269N00648998EA0213202096000
    +B1646124552292N00649023EA0214202098000
    +B1646194552313N00649060EA0214602100000
    +B1646264552338N00649093EA0214402103000
    +B1646334552371N00649108EA0214202105000
    +B1646404552400N00649106EA0214002106000
    +B1646474552367N00649089EA0213402107000
    +B1646544552331N00649065EA0213002106000
    +B1647014552350N00649042EA0212502104000
    +B1647084552375N00649082EA0211902102000
    +B1647154552406N00649122EA0211602098000
    +B1647224552435N00649160EA0211502094000
    +B1647294552461N00649202EA0211802091000
    +B1647364552475N00649256EA0212202090000
    +B1647434552453N00649258EA0210802088000
    +B1647504552485N00649284EA0211202087000
    +B1647574552504N00649339EA0211202086000
    +B1648044552522N00649389EA0210502085000
    +B1648114552547N00649442EA0209302082000
    +B1648184552576N00649497EA0209102077000
    +B1648254552603N00649557EA0208502072000
    +B1648324552629N00649612EA0208402067000
    +B1648394552663N00649655EA0208302064000
    +B1648464552697N00649701EA0207102060000
    +B1648534552730N00649756EA0205902054000
    +B1649004552764N00649811EA0205202047000
    +B1649074552800N00649862EA0205102041000
    +B1649144552828N00649911EA0205802037000
    +B1649214552827N00649938EV0206102036000
    +B1649284552786N00650011EA0206802035000
    +B1649354552743N00650025EA0207602035000
    +B1649424552701N00650022EA0208002036000
    +B1649494552695N00649989EA0207902036000
    +B1649564552729N00650006EA0208202038000
    +B1650034552767N00650013EA0209002041000
    +B1650104552803N00650015EA0209302045000
    +B1650174552819N00649986EA0209902048000
    +B1650244552786N00650002EA0210902050000
    +B1650314552759N00650048EA0211202056000
    +B1650394552719N00650064EA0211802062000
    +B1650464552711N00650033EA0211402066000
    +B1650534552747N00650027EA0211402069000
    +B1651004552785N00650029EA0212002072000
    +B1651074552824N00650034EA0212302076000
    +B1651144552862N00650084EA0212402079000
    +B1651214552895N00650139EA0212802082000
    +B1651284552919N00650193EA0212802085000
    +B1651354552942N00650251EA0212602087000
    +B1651424552968N00650305EA0212402088000
    +B1651494552990N00650362EA0212102088000
    +B1651564553012N00650426EA0211602088000
    +B1652034553040N00650490EA0211302086000
    +B1652104553071N00650556EA0210802084000
    +B1652174553102N00650626EA0209702081000
    +B1652244553129N00650699EA0208502076000
    +B1652314553155N00650769EA0207802071000
    +B1652384553180N00650837EA0207302065000
    +B1652454553208N00650903EA0207202060000
    +B1652524553233N00650967EA0207102056000
    +B1652594553259N00651041EA0207102052000
    +B1653064553285N00651116EA0207102049000
    +B1653134553309N00651194EA0208102048000
    +B1653204553317N00651248EA0208202049000
    +B1653274553301N00651268EV0208102049000
    +B1653344553327N00651265EA0208102050000
    +B1653414553360N00651314EA0208202050000
    +B1653484553381N00651364EA0207902050000
    +B1653554553403N00651415EA0207102050000
    +B1654024553433N00651473EA0206002047000
    +B1654094553468N00651533EA0205002041000
    +B1654164553502N00651599EA0203502034000
    +B1654234553540N00651652EA0202302025000
    +B1654304553576N00651714EA0201202016000
    +B1654374553611N00651777EA0200202005000
    +B1654444553655N00651831EA0200101997000
    +B1654514553696N00651886EA0199901989000
    +B1654584553734N00651942EA0199501983000
    +B1655054553769N00652002EA0199601978000
    +B1655124553804N00652060EA0199201975000
    +B1655194553844N00652114EA0198901972000
    +B1655264553882N00652166EA0199001968000
    +B1655334553924N00652214EA0198801965000
    +B1655404553964N00652253EA0199801963000
    +B1655474553969N00652307EV0199901963000
    +B1655544553952N00652321EA0200201963000
    +B1656014553981N00652296EA0201301963000
    +B1656084554018N00652319EA0202101966000
    +B1656154554008N00652378EA0202601970000
    +B1656224553986N00652359EA0203601974000
    +B1656294554018N00652347EA0204101982000
    +B1656364554035N00652406EA0205201991000
    +B1656434554010N00652420EA0206102000000
    +B1656504554028N00652401EA0207302009000
    +B1656584554031N00652465EA0208402019000
    +B1657054554009N00652453EA0209302027000
    +B1657124554037N00652442EA0210202035000
    +B1657194554038N00652497EA0211602044000
    +B1657264554017N00652488EA0212602054000
    +B1657334554047N00652479EA0213202063000
    +B1657404554054N00652544EA0214502073000
    +B1657474554027N00652563EA0215502084000
    +B1657544554051N00652545EA0216402095000
    +B1658014554065N00652600EA0217502107000
    +B1658084554035N00652616EA0218302118000
    +B1658154554041N00652586EA0218702128000
    +B1658224554082N00652601EA0219602139000
    +B1658294554074N00652652EA0220702149000
    +B1658364554087N00652634EA0221702158000
    +B1658434554093N00652689EA0222802167000
    +B1658504554085N00652674EA0223502176000
    +B1658574554121N00652689EA0224802186000
    +B1659044554118N00652748EA0226202196000
    +B1659114554100N00652754EV0226702202000
    +B1659184554120N00652737EA0227502208000
    +B1659254554161N00652759EA0228602217000
    +B1659324554156N00652803EA0229302226000
    +B1659394554141N00652769EA0230002234000
    +B1659464554175N00652751EA0230702242000
    +B1659534554180N00652806EA0232302249000
    +B1700004554173N00652778EA0232902257000
    +B1700074554214N00652804EA0233802265000
    +B1700144554230N00652861EA0234802275000
    +B1700214554207N00652872EA0235702286000
    +B1700284554220N00652840EA0236202298000
    +B1700354554255N00652882EA0237302309000
    +B1700424554241N00652937EA0238102319000
    +B1700494554222N00652927EA0238502327000
    +B1700564554260N00652920EA0238602335000
    +B1701034554299N00652968EA0239002343000
    +B1701104554344N00653003EA0239802348000
    +B1701174554387N00653047EA0239102353000
    +B1701244554433N00653089EA0238702355000
    +B1701314554476N00653139EA0238402356000
    +B1701384554523N00653185EA0236502355000
    +B1701454554571N00653234EA0235402351000
    +B1701524554619N00653286EA0234502345000
    +B1701594554665N00653341EA0234002338000
    +B1702064554708N00653399EA0233402331000
    +B1702134554754N00653461EA0232802325000
    +B1702204554796N00653527EA0233302319000
    +B1702274554837N00653589EA0233502315000
    +B1702344554878N00653636EA0232402312000
    +B1702424554922N00653698EA0230902307000
    +B1702494554963N00653752EA0229502300000
    +B1702564555006N00653812EA0228002291000
    +B1703034555046N00653862EA0227802283000
    +B1703104555085N00653911EA0227502275000
    +B1703174555125N00653955EA0226702268000
    +B1703244555162N00654002EA0225302260000
    +B1703314555207N00654047EA0224402251000
    +B1703384555252N00654092EA0224702244000
    +B1703454555298N00654138EA0224502238000
    +B1703524555345N00654170EA0223602232000
    +B1703594555396N00654190EA0222802226000
    +B1704064555446N00654222EA0223002220000
    +B1704134555491N00654263EA0223202216000
    +B1704204555528N00654300EA0223602214000
    +B1704274555571N00654344EA0224002213000
    +B1704344555616N00654388EA0224102212000
    +B1704414555660N00654417EA0225602214000
    +B1704484555662N00654405EA0227002217000
    +B1704554555675N00654456EA0227702223000
    +B1705024555712N00654451EA0228602229000
    +B1705094555707N00654434EA0228702234000
    +B1705164555726N00654495EA0229602240000
    +B1705234555765N00654529EA0230102248000
    +B1705304555813N00654561EA0229102254000
    +B1705374555859N00654612EA0228302256000
    +B1705444555905N00654661EA0227502255000
    +B1705514555941N00654719EA0226602252000
    +B1705584555974N00654774EA0225702248000
    +B1706054556008N00654828EA0224802243000
    +B1706124556039N00654886EA0224202236000
    +B1706194556067N00654948EA0223902230000
    +B1706264556096N00655006EA0223502224000
    +B1706334556125N00655065EA0223002219000
    +B1706404556158N00655115EA0222202213000
    +B1706474556198N00655152EA0221502207000
    +B1706544556239N00655189EA0220402201000
    +B1707014556280N00655222EA0219602195000
    +B1707084556321N00655258EA0218702188000
    +B1707154556361N00655298EA0218202180000
    +B1707224556396N00655345EA0217702173000
    +B1707294556428N00655400EA0217402166000
    +B1707364556460N00655455EA0217102160000
    +B1707434556492N00655511EA0217202155000
    +B1707504556525N00655561EA0216902151000
    +B1707574556560N00655596EA0218102149000
    +B1708044556592N00655621EA0218402149000
    +B1708114556625N00655645EA0218402149000
    +B1708184556663N00655646EA0218502150000
    +B1708254556700N00655635EA0219902151000
    +B1708324556740N00655636EA0221302155000
    +B1708394556776N00655629EA0223202162000
    +B1708474556788N00655603EA0224702171000
    +B1708544556766N00655635EA0225702179000
    +B1709014556797N00655658EA0227502188000
    +B1709084556832N00655649EA0228902200000
    +B1709154556824N00655644EA0230302213000
    +B1709224556801N00655701EA0232102223000
    +B1709294556775N00655759EA0234102231000
    +B1709364556765N00655753EA0235202240000
    +B1709434556802N00655743EA0236802256000
    +B1709504556839N00655738EA0238902275000
    +B1709574556860N00655713EA0240302293000
    +B1710044556834N00655740EA0241902310000
    +B1710114556805N00655783EA0243502329000
    +B1710184556775N00655829EA0245502347000
    +B1710254556749N00655845EA0248102363000
    +B1710324556762N00655871EA0250502375000
    +B1710394556757N00655860EA0252502389000
    +B1710464556786N00655851EA0254402414000
    +B1710534556802N00655819EA0255302437000
    +B1711004556775N00655842EA0256402458000
    +B1711074556760N00655908EA0257602476000
    +B1711144556740N00655980EA0259502493000
    +B1711214556727N00655976EA0260702507000
    +B1711284556754N00656023EA0261202525000
    +B1711354556748N00656065EA0262102535000
    +B1711424556761N00656031EA0263502545000
    +B1711494556771N00656002EA0264902556000
    +B1711564556774N00655969EA0265902568000
    +B1712034556796N00655941EA0267102588000
    +B1712104556800N00655907EA0268102603000
    +B1712174556781N00655947EA0269002615000
    +B1712244556787N00656018EA0270602628000
    +B1712314556764N00656070EA0270702640000
    +B1712384556758N00656042EA0271002649000
    +B1712454556767N00656006EA0271302657000
    +B1712524556794N00655983EA0271902665000
    +B1712594556821N00655954EA0273202674000
    +B1713064556845N00655923EA0274602682000
    +B1713134556872N00655902EA0275802692000
    +B1713204556881N00655958EA0277402703000
    +B1713274556850N00655961EA0278902715000
    +B1713344556865N00655925EA0280202727000
    +B1713414556899N00655948EA0281702740000
    +B1713484556876N00655976EA0283302753000
    +B1713554556881N00655940EA0284702768000
    +B1714024556912N00655972EA0286102782000
    +B1714094556894N00656026EA0288002797000
    +B1714174556872N00656000EA0289102814000
    +B1714244556903N00655989EA0290802829000
    +B1714314556912N00656050EA0292402844000
    +B1714384556880N00656073EA0293702858000
    +B1714454556878N00656042EA0295102873000
    +B1714524556912N00656052EA0296002889000
    +B1714594556918N00656109EA0297102902000
    +B1715064556887N00656129EA0298502915000
    +B1715134556867N00656107EA0299202928000
    +B1715204556882N00656075EA0300902941000
    +B1715274556903N00656056EA0302102952000
    +B1715344556933N00656072EA0304202966000
    +B1715414556947N00656116EA0306502983000
    +B1715484556916N00656129EA0308002999000
    +B1715554556934N00656094EA0310303015000
    +B1716024556962N00656128EA0312903033000
    +B1716094556990N00656157EA0314503053000
    +B1716164557021N00656185EA0315003071000
    +B1716234557057N00656213EA0315803086000
    +B1716304557093N00656246EA0316603100000
    +B1716374557132N00656277EA0316503111000
    +B1716444557165N00656308EA0315203119000
    +B1716514557199N00656350EA0314003121000
    +B1716584557236N00656388EA0313203121000
    +B1717054557275N00656417EA0312303118000
    +B1717124557313N00656448EA0311303114000
    +B1717194557352N00656479EA0310303108000
    +B1717264557392N00656502EA0309503101000
    +B1717334557433N00656529EA0308903094000
    +B1717404557471N00656561EA0308303089000
    +B1717474557509N00656594EA0308003084000
    +B1717544557544N00656630EA0307603079000
    +B1718014557580N00656666EA0306903073000
    +B1718084557615N00656705EA0306203067000
    +B1718154557652N00656742EA0305703062000
    +B1718224557690N00656777EA0305303057000
    +B1718294557728N00656812EA0304703052000
    +B1718364557766N00656847EA0304103046000
    +B1718434557803N00656881EA0303603040000
    +B1718504557839N00656917EA0303003034000
    +B1718574557872N00656957EA0302203029000
    +B1719044557909N00656992EA0301503023000
    +B1719114557950N00657019EA0300903023000
    +B1719184557989N00657048EA0300303020000
    +B1719254558027N00657077EA0299603013000
    +B1719324558064N00657104EA0298403006000
    +B1719394558100N00657134EA0297802999000
    +B1719464558136N00657166EA0297002989000
    +B1719544558174N00657210EA0296202979000
    +B1720014558207N00657250EA0295602970000
    +B1720084558241N00657288EA0294602961000
    +B1720154558276N00657322EA0293702952000
    +B1720224558312N00657361EA0293002943000
    +B1720294558345N00657392EA0292002934000
    +B1720364558381N00657424EA0290402924000
    +B1720434558417N00657455EA0289602913000
    +B1720504558454N00657484EA0288402902000
    +B1720574558492N00657510EA0287602892000
    +B1721044558530N00657531EA0286802881000
    +B1721114558568N00657554EA0285902871000
    +B1721184558606N00657576EA0284902861000
    +B1721254558645N00657587EA0284202852000
    +B1721324558684N00657612EA0283202843000
    +B1721394558724N00657635EA0281602834000
    +B1721464558768N00657652EA0280702825000
    +B1721534558810N00657666EA0280002815000
    +B1722004558852N00657685EA0279302805000
    +B1722074558893N00657707EA0278402796000
    +B1722144558936N00657728EA0277402786000
    +B1722214558980N00657748EA0276502776000
    +B1722284559023N00657770EA0276102768000
    +B1722354559066N00657789EA0276302761000
    +B1722424559104N00657795EA0276902756000
    +B1722494559136N00657785EA0277302755000
    +B1722564559167N00657767EA0277302754000
    +B1723034559205N00657785EA0277502753000
    +B1723104559242N00657807EA0276602752000
    +B1723174559285N00657828EA0276302749000
    +B1723244559323N00657852EA0276002746000
    +B1723314559359N00657882EA0274702741000
    +B1723384559398N00657915EA0274002736000
    +B1723454559436N00657948EA0273502731000
    +B1723524559474N00657985EA0273002725000
    +B1723594559510N00658023EA0272402720000
    +B1724064559547N00658057EA0271802715000
    +B1724134559584N00658091EA0270802709000
    +B1724204559624N00658121EA0270102703000
    +B1724274559664N00658155EA0269302696000
    +B1724344559702N00658193EA0268502688000
    +B1724414559743N00658219EA0267402681000
    +B1724484559786N00658241EA0266302672000
    +B1724554559830N00658253EA0265002663000
    +B1725024559872N00658278EA0263902653000
    +B1725094559915N00658302EA0262902643000
    +B1725164559959N00658326EA0261902633000
    +B1725234559982N00658287EA0260602623000
    +B1725314559963N00658219EA0259602611000
    +B1725384559940N00658163EA0258702600000
    +B1725454559912N00658105EA0258002590000
    +B1725524559897N00658055EA0257402582000
    +B1725594559932N00658050EA0256402573000
    +B1726064559962N00658094EA0255502567000
    +B1726134559932N00658108EA0254602559000
    +B1726204559899N00658065EA0253802550000
    +B1726274559869N00658019EA0252902542000
    +B1726344559837N00657972EA0251902532000
    +B1726414559803N00657925EA0251202523000
    +B1726484559767N00657878EA0250702514000
    +B1726554559729N00657834EA0250702508000
    +B1727024559697N00657793EA0249702500000
    +B1727094559663N00657753EA0249102492000
    +B1727164559631N00657707EA0248502485000
    +B1727234559599N00657660EA0248102479000
    +B1727304559566N00657614EA0247602473000
    +B1727374559533N00657572EA0247002467000
    +B1727444559496N00657531EA0246702461000
    +B1727514559463N00657487EA0246002456000
    +B1727584559426N00657447EA0245002450000
    +B1728054559391N00657402EA0244202443000
    +B1728124559356N00657359EA0242902435000
    +B1728194559316N00657324EA0242002426000
    +B1728264559275N00657292EA0241202418000
    +B1728334559233N00657257EA0240402409000
    +B1728404559191N00657219EA0239402401000
    +B1728474559148N00657187EA0239302393000
    +B1728544559103N00657161EA0239902387000
    +B1729014559067N00657126EA0239602384000
    +B1729084559033N00657080EA0238802379000
    +B1729154558997N00657038EA0238402375000
    +B1729224558957N00657008EA0237102370000
    +B1729294558909N00656969EA0235402363000
    +B1729364558862N00656918EA0234002354000
    +B1729434558812N00656872EA0232802345000
    +B1729504558763N00656824EA0231802336000
    +B1729574558719N00656771EA0229902326000
    +B1730044558677N00656711EA0228402315000
    +B1730114558637N00656647EA0227202303000
    +B1730184558596N00656589EA0226102290000
    +B1730254558554N00656535EA0224902278000
    +B1730324558512N00656477EA0223802265000
    +B1730394558473N00656421EA0222302253000
    +B1730464558433N00656366EA0220502240000
    +B1730534558393N00656310EA0219402226000
    +B1731014558344N00656243EA0218402211000
    +B1731084558305N00656177EA0217302197000
    +B1731154558260N00656113EA0216602184000
    +B1731224558211N00656057EA0215302172000
    +B1731294558161N00656002EA0215002161000
    +B1731364558106N00655951EA0213802150000
    +B1731434558051N00655901EA0213202140000
    +B1731504557998N00655844EA0212202130000
    +B1731574557948N00655786EA0210702121000
    +B1732044557896N00655730EA0209002110000
    +B1732114557847N00655670EA0207602098000
    +B1732184557800N00655615EA0206302086000
    +B1732254557753N00655569EA0205902073000
    +B1732324557715N00655536EA0205602063000
    +B1732394557677N00655509EA0205802056000
    +B1732464557639N00655481EA0205502049000
    +B1732534557604N00655464EA0205302044000
    +B1733004557569N00655448EA0205802040000
    +B1733074557535N00655432EA0205502037000
    +B1733144557499N00655412EA0205102034000
    +B1733214557466N00655392EA0204302030000
    +B1733284557435N00655375EA0203302025000
    +B1733354557409N00655351EA0201602018000
    +B1733424557394N00655297EA0200102009000
    +B1733494557384N00655240EA0197901998000
    +B1733564557369N00655184EA0195801985000
    +B1734034557359N00655134EA0195401971000
    +B1734104557349N00655106EA0194901960000
    +B1734174557335N00655083EA0193701951000
    +B1734244557314N00655041EA0192501939000
    +B1734314557296N00654989EA0191301927000
    +B1734384557274N00654940EA0190201915000
    +B1734454557258N00654880EA0189101903000
    +B1734524557245N00654816EA0187201890000
    +B1734594557226N00654757EA0186601877000
    +B1735064557215N00654696EA0186001865000
    +B1735134557203N00654633EA0184901854000
    +B1735204557195N00654565EA0183201844000
    +B1735274557192N00654491EA0182001833000
    +B1735344557186N00654423EA0180801822000
    +B1735414557175N00654357EA0179601810000
    +B1735484557169N00654287EA0178201800000
    +B1735554557169N00654218EA0177001787000
    +B1736024557170N00654159EA0176501775000
    +B1736094557164N00654107EA0176001764000
    +B1736164557155N00654057EA0175201753000
    +B1736234557142N00654007EA0174701743000
    +B1736304557129N00653958EA0173801734000
    +B1736374557116N00653903EA0173501725000
    +B1736454557100N00653843EA0173001717000
    +B1736524557085N00653794EA0172801711000
    +B1736594557061N00653750EA0172001704000
    +B1737064557031N00653704EA0170601697000
    +B1737134557004N00653655EA0170101688000
    +B1737204556985N00653606EA0170701682000
    +B1737274556972N00653547EA0171401679000
    +B1737344556959N00653493EA0172001679000
    +B1737414556940N00653427EA0172801681000
    +B1737484556926N00653364EA0173901684000
    +B1737554556946N00653314EA0174401688000
    +B1738024556970N00653347EA0174901693000
    +B1738094556952N00653377EA0175701699000
    +B1738164556927N00653333EA0175601706000
    +B1738234556905N00653268EA0176101711000
    +B1738304556886N00653206EA0175901714000
    +B1738374556857N00653157EA0175601717000
    +B1738444556830N00653105EA0175101717000
    +B1738514556803N00653053EA0174201717000
    +B1738584556773N00653004EA0173901714000
    +B1739054556749N00652949EA0173301711000
    +B1739124556732N00652894EA0172401707000
    +B1739194556699N00652827EA0171901702000
    +B1739264556673N00652764EA0171001696000
    +B1739334556643N00652698EA0169701689000
    +B1739404556611N00652622EA0168701681000
    +B1739474556583N00652551EA0168301673000
    +B1739544556561N00652472EA0167701665000
    +B1740014556541N00652404EA0168201659000
    +B1740084556518N00652353EA0168101654000
    +B1740154556501N00652305EA0168201651000
    +B1740224556482N00652263EA0167701648000
    +B1740294556462N00652222EA0167401646000
    +B1740364556439N00652179EA0166901643000
    +B1740434556416N00652133EA0166701639000
    +B1740504556391N00652095EA0166401636000
    +B1740574556367N00652057EA0166001633000
    +B1741044556340N00652026EA0165101629000
    +B1741114556315N00651993EA0163701625000
    +B1741184556280N00651966EA0162701618000
    +B1741254556243N00651941EA0162001611000
    +B1741324556209N00651912EA0161801604000
    +B1741394556177N00651887EA0160801597000
    +B1741464556140N00651873EA0159601589000
    +B1741534556100N00651864EA0158301580000
    +B1742004556061N00651838EA0157301572000
    +B1742074556023N00651833EA0156801564000
    +B1742154555977N00651838EA0156101555000
    +B1742224555943N00651825EA0155401546000
    +B1742294555909N00651798EA0154501538000
    +B1742364555878N00651760EA0153901529000
    +B1742434555852N00651718EA0152801521000
    +B1742504555828N00651670EA0152101513000
    +B1742574555805N00651618EA0151301504000
    +B1743044555780N00651565EA0150501495000
    +B1743114555748N00651521EA0149601486000
    +B1743184555720N00651469EA0148801479000
    +B1743254555689N00651423EA0147601470000
    +B1743324555653N00651390EA0146101462000
    +B1743394555606N00651393EA0144401451000
    +B1743464555562N00651423EA0142901439000
    +B1743534555532N00651473EA0141901427000
    +B1744004555523N00651540EA0141201414000
    +B1744074555534N00651602EA0140301402000
    +B1744144555541N00651668EA0139701390000
    +B1744214555539N00651738EA0139401380000
    +B1744284555543N00651808EA0138401371000
    +B1744354555591N00651824EA0137701363000
    +B1744424555637N00651826EA0137601354000
    +B1744494555681N00651821EA0137101347000
    +B1744564555721N00651821EA0136401341000
    +B1745034555759N00651838EA0135401335000
    +B1745104555797N00651865EA0134901328000
    +B1745174555831N00651894EA0133801322000
    +B1745244555864N00651928EA0133001314000
    +B1745314555893N00651969EA0132201305000
    +B1745384555929N00651998EA0131301297000
    +B1745454555939N00652035EA0129801288000
    +B1745524555903N00652030EA0128801279000
    +B1745594555927N00652070EA0127001269000
    +B1746064555915N00652118EA0126801261000
    +B1746134555896N00652167EA0126101251000
    +B1746204555906N00652207EA0124301242000
    +B1746274555893N00652203EA0123201235000
    +B1746344555892N00652262EA0122201224000
    +B1746414555885N00652322EA0121401213000
    +B1746484555878N00652380EA0120401202000
    +B1746554555867N00652446EA0119401191000
    +B1747024555850N00652505EA0118801181000
    +B1747094555826N00652535EA0117501171000
    +B1747164555851N00652543EA0116501160000
    +B1747234555846N00652529EA0115201152000
    +B1747304555862N00652573EA0114501142000
    +B1747374555854N00652623EA0114201133000
    +B1747444555837N00652650EA0113001124000
    +B1747514555845N00652604EA0112601115000
    +B1747594555826N00652578EA0111701105000
    +B1748064555800N00652585EA0110401096000
    +B1748134555771N00652577EA0109401086000
    +B1748204555754N00652589EA0109601080000
    +B1748274555754N00652590EA0109601075000
    +B1748344555755N00652590EA0109501073000
    +B1748414555754N00652590EA0109601070000
    +B1748484555755N00652589EA0109501068000
    +B1748554555755N00652589EA0109601064000
    +B1749024555755N00652591EA0109601063000
    +B1749094555755N00652592EA0109601061000
    +B1749164555753N00652594EA0109601056000
    diff --git a/mapbox-gl/examples/data/kml/2012-02-10.kml b/mapbox-gl/examples/data/kml/2012-02-10.kml
    new file mode 100644
    index 0000000000..ad7b112f4d
    --- /dev/null
    +++ b/mapbox-gl/examples/data/kml/2012-02-10.kml
    @@ -0,0 +1,9391 @@
    +<kml>
    +  <Document>
    +    <name>Skimap.org OpenSkiMap </name>
    +    <description>Created from OpenStreetMap data. Renderer v0.5</description>
    +    <!-- 
    +Copyright (c) OpenStreetMap Contributors
    +http://www.openstreetmap.org/
    +
    +This work is licensed under the
    +Creative Commons Attribution-ShareAlike 2.0 License.
    +http://creativecommons.org/licenses/by-sa/2.0/
    + -->
    +    <ScreenOverlay id="CopyrightNotice">
    +      <name>Copyright Notice</name>
    +      <description>
    +        <![CDATA[The data in this KML file is Copyright <a href="http://www.openstreetmap.org/">OpenStreetMap</a> Contributors. It is available under the <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons Attribution-ShareAlike (CC-BY-SA) Version 2.0 License</a>.]]>
    +      </description>
    +      <Snippet>Copyright (c) OpenStreetMap Contributors. CC-BY-SA 2.0 License.</Snippet>
    +      <Icon>
    +        <href>OSMCopyright.png</href>
    +      </Icon>
    +      <overlayXY x="0" xunits="fraction" y="1" yunits="fraction"/>
    +      <screenXY x="0" xunits="fraction" y="1" yunits="fraction"/>
    +      <size x="0" xunits="fraction" y="0" yunits="fraction"/>
    +    </ScreenOverlay>
    +    <Folder>
    +      <name>POI</name>
    +      <description>Points of Interest</description>
    +      <Folder>
    +        <name>Parking</name>
    +        <Style id="style-Parking">
    +          <IconStyle>
    +            <scale>0.2</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/shapes/parking_lot.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.5584046,46.4880564,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Coop</name>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.5609149,46.493261,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.5865419,46.4861414,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4447639,46.4572778,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4450385,46.4601225,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4393367,46.45965,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4413296,46.4571887,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.440856,46.4512387,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4379632,46.452765,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4439042,46.4554444,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4442161,46.4540646,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.4701427,46.4536698,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.5806365,46.5135164,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.6038938,46.5174355,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.6033249,46.5169628,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Parking</styleUrl>
    +          <Point>
    +            <coordinates>7.6062434,46.5290881,0</coordinates>
    +          </Point>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Fuel</name>
    +        <Style id="style-Fuel">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal2/icon21.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Garage Inniger</name>
    +          <styleUrl>#style-Fuel</styleUrl>
    +          <Point>
    +            <coordinates>7.5660783,46.4900069,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Fuel</styleUrl>
    +          <Point>
    +            <coordinates>7.5926292,46.5284662,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Fuel</styleUrl>
    +          <Point>
    +            <coordinates>7.442845,46.458965,0</coordinates>
    +          </Point>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Cinema</name>
    +        <Style id="style-Cinema">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal2/icon30.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Ciné Rex</name>
    +          <styleUrl>#style-Cinema</styleUrl>
    +          <Point>
    +            <coordinates>7.5630412,46.4929077,0</coordinates>
    +          </Point>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Hotel</name>
    +        <Style id="style-Hotel">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal2/icon28.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Hotel Crea</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5676532,46.4982767,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hotel Hari</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5650953,46.4963004,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bellevue</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5563578,46.4921191,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kreuz</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4409986,46.4570553,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krone</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4426496,46.4567252,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Wildstrubel</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4430785,46.4574807,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hotel des Alpes</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5387572,46.4794195,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Viktoria Eden</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5600327,46.4933357,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Berghaus Bärtschi</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5632096,46.4454517,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Tenne</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4461369,46.4589363,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Parkhotel Bellevue</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4398359,46.455012,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simmenhof</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4346619,46.4729836,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Iffigenalp</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4415194,46.4002088,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Reka Feriendorf</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4317292,46.4681013,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bären</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5583384,46.4923431,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Spillgerten</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.4824764,46.5681752,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hotel Steinmattli</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.555973,46.4892051,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Alpina</name>
    +          <styleUrl>#style-Hotel</styleUrl>
    +          <Point>
    +            <coordinates>7.5670248,46.4845444,0</coordinates>
    +          </Point>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Restaurant</name>
    +        <Style id="style-Restaurant">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal2/icon40.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Sillerenbühl</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5177184,46.4713607,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Berghaus Stoss</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4294222,46.4403995,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Geilsbrüggli</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5134309,46.4571755,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4954338,46.4528516,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Standhütte</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4947421,46.443782,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.56919,46.4384245,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Wallegstübli</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4251977,46.4550003,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Betelberghütte</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4209174,46.4353096,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Chüejer Stuba</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5083945,46.4586075,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hotel des Alpes</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5387572,46.4794195,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Berghaus Tschentenalp</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5457297,46.4987315,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Stierenberg</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4394912,46.5665216,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4761515,46.4266593,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Tenne</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4461369,46.4589363,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Andy&amp;apos;s Pub, Terrasse</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4436412,46.4558537,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Pizzeria Roberto</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4426305,46.457097,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krone</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4427032,46.4566172,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Anker</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.442741,46.4572569,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Pizzeria Antica Posta</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4418719,46.457229,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Rohrbach</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.6024672,46.5500964,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bunderalp Berghaus</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.6011565,46.4904257,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hohliebestübli</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5737679,46.48547,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Restaurant Siebenbrunnen</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4901423,46.4168265,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Berghölzli</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4229564,46.4967046,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gasthaus Elsigbach</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.606331,46.5289162,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Wildstrubel</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5599318,46.4813358,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kuspo</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.4527733,46.4494679,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bühlberg</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.469499,46.4536402,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5124053,46.4727902,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <name>Berghaus Höchst</name>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.6035985,46.516601,0</coordinates>
    +          </Point>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Restaurant</styleUrl>
    +          <Point>
    +            <coordinates>7.5059586,46.4504356,0</coordinates>
    +          </Point>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Hospital</name>
    +        <Style id="style-Hospital">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal3/icon38.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Toilet</name>
    +        <Style id="style-Toilet">
    +          <IconStyle>
    +            <scale>0.7</scale>
    +            <Icon>
    +              <href>http://maps.google.com/mapfiles/kml/pal3/icon38.png</href>
    +            </Icon>
    +          </IconStyle>
    +        </Style>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Ski_Runs</name>
    +      <Folder>
    +        <name>Ski_Run_Downhill</name>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Easy</name>
    +          <Style id="style-Ski_Run_Downhill_Easy">
    +            <LineStyle>
    +              <color>ff00ff00</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark id="w4380588">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.429755,46.4386039,0 7.4293368,46.4393223,0 7.429232,46.4404117,0 7.4293996,46.441124,0 7.4300282,46.4415011,0 7.4296801,46.4435027,0 7.4304432,46.4436726,0 7.4309129,46.4436944,0 7.4311473,46.4436377,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388751">
    +            <name>25</name>
    +            <description>25, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4878089,46.443423,0 7.4877097,46.4433227,0 7.4862994,46.4433438,0 7.4839067,46.4431223,0 7.482119,46.4430627,0 7.4773624,46.442722,0 7.4739186,46.4425618,0 7.4726715,46.4431339,0 7.4712165,46.4431469,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388754">
    +            <name>21</name>
    +            <description>21, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4979152,46.4417323,0 7.4975394,46.4421891,0 7.4973136,46.4422123,0 7.4966307,46.4419276,0 7.4956492,46.4412029,0 7.4950704,46.4411561,0 7.4935875,46.4409666,0 7.4916014,46.4405342,0 7.4899677,46.4413831,0 7.4890906,46.4415271,0 7.4877254,46.4414311,0 7.4850666,46.4418636,0 7.482119,46.4430627,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388755">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4862994,46.4433438,0 7.4862235,46.4429825,0 7.486705,46.4424627,0 7.4890906,46.4415271,0 7.4895612,46.4412262,0 7.489583,46.4406462,0 7.4890687,46.4396996,0 7.4889051,46.4389092,0 7.488508,46.4385783,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388832">
    +            <name>29</name>
    +            <description>29, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4951921,46.4444865,0 7.4948475,46.4446679,0 7.4910249,46.4455562,0 7.490986,46.4460492,0 7.4907888,46.4463504,0 7.4907043,46.4468387,0 7.4909002,46.4469646,0 7.4931591,46.4473935,0 7.4934449,46.4474943,0 7.4935055,46.4476487,0 7.4918349,46.4503992,0 7.4914431,46.450472,0 7.4913811,46.4506976,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388836">
    +            <name>27</name>
    +            <description>27, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4753104,46.4467318,0 7.4742803,46.4466354,0 7.4734687,46.4464045,0 7.4728137,46.4460338,0 7.4722901,46.4454816,0 7.4719258,46.4449058,0 7.4712165,46.4431469,0 7.4702165,46.4426691,0 7.469432,46.4425057,0 7.4683009,46.4424017,0 7.4681436,46.4424662,0 7.4682319,46.4426424,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388837">
    +            <name>34</name>
    +            <description>34, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4914805,46.4507107,0 7.4913811,46.4506976,0 7.4891761,46.4507056,0 7.4885692,46.4500736,0 7.4861844,46.4492757,0 7.4861011,46.4492478,0 7.484473,46.448703,0 7.4838871,46.4485069,0 7.4832597,46.4477197,0 7.4815483,46.4467728,0 7.4810792,46.4465722,0 7.4804303,46.4464648,0 7.4794506,46.4464069,0 7.4786449,46.4466018,0 7.4778745,46.4467856,0 7.4773741,46.4467058,0 7.4766312,46.4464834,0 7.4753104,46.4467318,0 7.4751279,46.4473859,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389270">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5183847,46.4722004,0 7.517934,46.471763,0 7.517821,46.4717479,0 7.5175412,46.4714731,0 7.5159991,46.4705791,0 7.5149527,46.4698298,0 7.5145904,46.4695704,0 7.514402,46.4689378,0 7.5134881,46.4678641,0 7.5128847,46.4677487,0 7.5125031,46.4673228,0 7.5119884,46.4670388,0 7.5118409,46.4666988,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389276">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4977486,46.4514628,0 7.497407,46.4524101,0 7.4968324,46.4531477,0 7.4968557,46.453831,0 7.4979505,46.4552131,0 7.5010796,46.4572863,0 7.5044316,46.4577113,0 7.5048258,46.4577613,0 7.5065939,46.4574074,0 7.5087808,46.4574575,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389278">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.49497,46.4533356,0 7.494742,46.4530283,0 7.4942068,46.4527205,0 7.4938455,46.451717,0 7.4928553,46.4509811,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389279">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4981254,46.4509924,0 7.4981368,46.4511522,0 7.4977486,46.4514628,0 7.4950854,46.451349,0 7.4928553,46.4509811,0 7.4914805,46.4507107,0 7.4915714,46.4504852,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389280">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4981254,46.4509924,0 7.4981445,46.4508305,0 7.4953246,46.4501477,0 7.4939146,46.450458,0 7.4928553,46.4509811,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569934">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5065878,46.4609089,0 7.5067588,46.4608409,0 7.5083597,46.4611852,0 7.5098203,46.4621242,0 7.5110321,46.4621081,0 7.5117865,46.462373,0 7.5122119,46.4631514,0 7.5129021,46.4633842,0 7.5135922,46.4634885,0 7.5141059,46.4636811,0 7.5149084,46.4637212,0 7.5157912,46.4643071,0 7.5168907,46.4644435,0 7.5179821,46.4643312,0 7.5187686,46.4646121,0 7.5204138,46.4662252,0 7.5208392,46.4662332,0 7.521409,46.4661208,0 7.5222597,46.4662894,0 7.5230542,46.4669796,0 7.523608,46.4678463,0 7.5241429,46.4682155,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569938">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5326801,46.4684788,0 7.532755,46.468548,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569946">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5316173,46.4680148,0 7.5324807,46.4685101,0 7.5326801,46.4684788,0 7.5328962,46.4684417,0 7.5329356,46.4685522,0 7.5327143,46.4686321,0 7.532755,46.468548,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569960">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4931915,46.4549659,0 7.4929375,46.454661,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569963">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4925723,46.455196,0 7.492699,46.454997,0 7.4929375,46.454661,0 7.4942068,46.4527205,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569968">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.492699,46.454997,0 7.4910376,46.4540056,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569973">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5132264,46.4562522,0 7.5130985,46.4572035,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569977">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4851581,46.4533824,0 7.4845569,46.4536013,0 7.4839198,46.4538334,0 7.482829,46.4540915,0 7.4815336,46.4540876,0 7.4800837,46.4537836,0 7.4779806,46.4533427,0 7.4772153,46.4531822,0 7.4764495,46.4530216,0 7.4753254,46.4534852,0 7.474042,46.4536903,0 7.4726118,46.4532512,0 7.472292,46.4528248,0 7.4722433,46.4525611,0 7.4719968,46.4512258,0 7.4713736,46.45107,0 7.4709826,46.4512884,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569980">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4910376,46.4540056,0 7.490236,46.4537956,0 7.4895041,46.4536038,0 7.4851581,46.4533824,0 7.4822321,46.4520159,0 7.4770883,46.4509952,0 7.4722247,46.4508951,0 7.4717194,46.4509989,0 7.4713736,46.45107,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569988">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4951921,46.4444865,0 7.4952107,46.4438631,0 7.4956323,46.4435254,0 7.495863,46.4431325,0 7.4958466,46.442669,0 7.4956544,46.4421238,0 7.4950704,46.4411561,0 7.4949338,46.4409187,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127858">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5749906,46.4438887,0 7.5747715,46.4435867,0 7.5745699,46.4428739,0 7.5733777,46.441992,0 7.572843,46.4418712,0 7.5718612,46.4418531,0 7.5706985,46.4416291,0 7.5686003,46.4406872,0 7.568574,46.4409349,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127859">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5747715,46.4435867,0 7.574491,46.4435383,0 7.5730797,46.442439,0 7.5710547,46.4418652,0 7.5706985,46.4416291,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127862">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5687446,46.4376384,0 7.5689165,46.4376761,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127864">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796203,46.43089,0 7.5799907,46.430675,0 7.5796593,46.4304735,0 7.578977,46.4304601,0 7.5783532,46.4302854,0 7.5755031,46.4292776,0 7.5748052,46.4286596,0 7.5743958,46.4286596,0 7.5737135,46.4284983,0 7.5734796,46.4281759,0 7.5725634,46.4274234,0 7.5717446,46.4273025,0 7.5712377,46.4269128,0 7.5701071,46.4264963,0 7.5693858,46.4272756,0 7.568684,46.4286999,0 7.5675338,46.4293717,0 7.5661994,46.4295295,0 7.5655793,46.4299569,0 7.5652203,46.4312391,0 7.5641106,46.4323413,0 7.5639801,46.433466,0 7.5648939,46.434973,0 7.5668522,46.4368849,0 7.5682133,46.437843,0 7.5685649,46.4378322,0 7.5687446,46.4376384,0 7.5688149,46.4375738,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127874">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5686003,46.4406872,0 7.5679939,46.4406328,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w138292329">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5065878,46.4609089,0 7.5056954,46.4603339,0 7.5058002,46.4601367,0 7.5057016,46.4599519,0 7.5066207,46.4591145,0 7.5076954,46.4577884,0 7.5087808,46.4574575,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w138292372">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5118409,46.4666988,0 7.5115891,46.4663378,0 7.5114205,46.4658587,0 7.510968,46.4655037,0 7.5107994,46.4648471,0 7.5087141,46.4627352,0 7.5081285,46.461697,0 7.5065878,46.4609089,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806187">
    +            <name>Kuonisbergliweg</name>
    +            <description>Kuonisbergliweg, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5508319,46.4815667,0 7.550886,46.481604,0 7.551017,46.481662,0 7.5517182,46.4815741,0 7.551993,46.481463,0 7.552258,46.481249,0 7.552478,46.481152,0 7.5528719,46.4810715,0 7.552972,46.481051,0 7.553187,46.480938,0 7.553375,46.480816,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806193">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5087808,46.4574575,0 7.5087624,46.4574732,0 7.5107571,46.4570949,0 7.512456,46.4571319,0 7.5130985,46.4572035,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806195">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4845569,46.4536013,0 7.4839705,46.4533006,0 7.4836023,46.4532825,0 7.4830277,46.4536666,0 7.4828207,46.453771,0 7.4826873,46.4537037,0 7.4825462,46.4531781,0 7.4822413,46.4531495,0 7.4808929,46.4536839,0 7.4800837,46.4537836,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806200">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.471843,46.4529933,0 7.4722433,46.4525611,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806202">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4781023,46.4524789,0 7.4772153,46.4531822,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806207">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.553375,46.480816,0 7.5536347,46.4807515,0 7.5541829,46.4807074,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806210">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4726118,46.4532512,0 7.4711386,46.4537235,0 7.4702555,46.4537971,0 7.4698184,46.4537065,0 7.4705402,46.4532965,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806212">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4966949,46.4456829,0 7.4987868,46.4474642,0 7.4990254,46.4485493,0 7.4986786,46.4497374,0 7.4981254,46.4509924,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806216">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4851581,46.4533824,0 7.4819071,46.4527893,0 7.4795821,46.4527576,0 7.4781023,46.4524789,0 7.4750189,46.4518982,0 7.4730834,46.4513691,0 7.4719968,46.4512258,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806220">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4779806,46.4533427,0 7.4774841,46.453759,0 7.4769609,46.4539583,0 7.4764209,46.4537428,0 7.4760416,46.4536833,0 7.475667,46.4538882,0 7.4750873,46.4539986,0 7.474042,46.4536903,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806222">
    +            <description>, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5541829,46.4807074,0 7.5541599,46.4812366,0 7.5544557,46.4816299,0 7.555156,46.4817335,0 7.5553097,46.4822055,0 7.5554386,46.4823711,0 7.5559054,46.4825407,0 7.5568671,46.4822132,0 7.5569895,46.4818235,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806225">
    +            <name>2</name>
    +            <description>2, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4314691,46.4551589,0 7.4322178,46.4554411,0 7.4328404,46.4555345,0 7.4337122,46.4559081,0 7.4347085,46.4560638,0 7.4353574,46.456162,0 7.4358604,46.4560015,0 7.4367179,46.4557467,0 7.4372508,46.4549744,0 7.4376135,46.4546647,0 7.4373964,46.4539233,0 7.4375265,46.4532365,0 7.4372187,46.4531533,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806229">
    +            <name>33</name>
    +            <description>33, Downhill Run Difficulty: Easy</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Easy</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4861011,46.4492478,0 7.4844268,46.4491036,0 7.4832187,46.4488441,0 7.4823814,46.4481279,0 7.4814712,46.4478087,0 7.4801554,46.4478027,0 7.4781901,46.4484089,0 7.4742765,46.4489608,0 7.4741321,46.4490468,0 7.473553,46.4493747,0 7.4717194,46.4509989,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Intermediate</name>
    +          <Style id="style-Ski_Run_Downhill_Intermediate">
    +            <LineStyle>
    +              <color>ffff0000</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark id="w4380587">
    +            <name>10</name>
    +            <description>10, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4144876,46.4472294,0 7.4149494,46.4475565,0 7.4152025,46.4477358,0 7.4154562,46.4478744,0 7.4167849,46.4483173,0 7.4184458,46.4500257,0 7.4214671,46.4526673,0 7.4235868,46.4533633,0 7.4242828,46.4539644,0 7.4253268,46.4544231,0 7.4260386,46.4555146,0 7.4271775,46.4562739,0 7.4277562,46.456385,0 7.4290744,46.4572198,0 7.4301992,46.4575186,0 7.4306302,46.4578101,0 7.4308495,46.4581425,0 7.4308935,46.458415,0 7.4307004,46.4584832,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4380593">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.409078,46.428856,0 7.4088684,46.4289664,0 7.4097542,46.4296455,0 7.410372,46.429866,0 7.411328,46.4299305,0 7.4122236,46.4299962,0 7.4128546,46.4302951,0 7.4136666,46.4313138,0 7.4162843,46.432802,0 7.4231145,46.4368711,0 7.4240783,46.4371015,0 7.424352,46.4373373,0 7.4244344,46.4376043,0 7.4240992,46.4384424,0 7.4241315,46.4385919,0 7.4242878,46.4386938,0 7.4245455,46.4386572,0 7.4257962,46.4380443,0 7.4269373,46.437921,0 7.4275298,46.4379127,0 7.4279308,46.4380355,0 7.4281223,46.438196,0 7.4281845,46.4387147,0 7.4285697,46.4391626,0 7.4287027,46.4394043,0 7.4286423,46.4398709,0 7.4286906,46.4401292,0 7.4289808,46.4403209,0 7.429232,46.4404117,0 7.429151,46.440554,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388833">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4948475,46.4446679,0 7.4937003,46.444504,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388834">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4877097,46.4433227,0 7.4872195,46.4435325,0 7.4865607,46.4435325,0 7.4841571,46.443395,0 7.4823979,46.4436194,0 7.4816834,46.4439676,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4388835">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4841571,46.443395,0 7.4839067,46.4431223,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389269">
    +            <name>42</name>
    +            <description>42, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.507632,46.44102,0 7.5076886,46.4408745,0 7.5079708,46.4410277,0 7.5086923,46.4410277,0 7.5091432,46.4420156,0 7.5088441,46.4425016,0 7.5066391,46.4435205,0 7.5059548,46.4445545,0 7.5060916,46.4454973,0 7.5055594,46.4485083,0 7.5066087,46.4500898,0 7.5079037,46.4503452,0 7.508768,46.4505156,0 7.5108818,46.4516561,0 7.5119614,46.4529182,0 7.5130411,46.4538915,0 7.5136795,46.4546008,0 7.5135329,46.455249,0 7.5134633,46.4557785,0 7.5132264,46.4562522,0 7.5133239,46.456517,0 7.5135374,46.456532,0 7.5134775,46.4563718,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389281">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.494809,46.462495,0 7.4945658,46.4625743,0 7.4944643,46.4625499,0 7.4944832,46.4624806,0 7.4939029,46.4614558,0 7.491698,46.459654,0 7.4913687,46.4586774,0 7.4918389,46.45772,0 7.4931915,46.4549659,0 7.49497,46.4533356,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w4389282">
    +            <name>55</name>
    +            <description>55, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4944832,46.4624806,0 7.4946897,46.4623555,0 7.4948693,46.462373,0 7.4953055,46.4625448,0 7.4966403,46.4646571,0 7.4987475,46.4646495,0 7.5003928,46.4650414,0 7.5015139,46.4653964,0 7.5032905,46.4674926,0 7.5062062,46.4683885,0 7.5078412,46.4692527,0 7.5081555,46.4737303,0 7.5098447,46.4750362,0 7.5119216,46.4781195,0 7.511841,46.4787868,0 7.5153242,46.4822775,0 7.5197463,46.483668,0 7.5198736,46.4840895,0 7.5194921,46.4842678,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w20016062">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6214035,46.5184624,0 7.6206061,46.5185013,0 7.6200227,46.5186374,0 7.619556,46.5189097,0 7.6191476,46.5193181,0 7.6182141,46.5193375,0 7.6178835,46.5192792,0 7.6178641,46.5181707,0 7.6177474,46.5177623,0 7.6176307,46.5175484,0 7.6168139,46.5171789,0 7.6164833,46.5166538,0 7.616036,46.5164399,0 7.6147525,46.5162065,0 7.6136877,46.5157618,0 7.6125232,46.5155388,0 7.6076942,46.5164486,0 7.6060633,46.5169747,0 7.605432,46.5170536,0 7.6044409,46.516792,0 7.6038575,46.516854,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w20016063">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6222677,46.5125575,0 7.6219536,46.5125696,0 7.6204665,46.5130361,0 7.6200208,46.5128293,0 7.6186894,46.5128372,0 7.6177891,46.5125865,0 7.6155716,46.5135034,0 7.6147737,46.5140404,0 7.6139445,46.5144307,0 7.6130136,46.5146209,0 7.6108572,46.5152546,0 7.6087275,46.5157391,0 7.608396,46.5153693,0 7.6079624,46.5152801,0 7.6056234,46.515944,0 7.6038575,46.516854,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w20016066">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6255371,46.5095259,0 7.6255618,46.5090433,0 7.6249926,46.5088453,0 7.6244357,46.5089072,0 7.6237799,46.5088329,0 7.6231982,46.5089814,0 7.622926,46.5089319,0 7.6216266,46.5090062,0 7.6211935,46.5088824,0 7.621144,46.5086349,0 7.6213173,46.5083751,0 7.6212554,46.5081771,0 7.6209552,46.5080301,0 7.6206413,46.5079572,0 7.6201032,46.5079796,0 7.619909,46.5078859,0 7.6192046,46.507952,0 7.6186945,46.5082071,0 7.618242,46.5086102,0 7.6170479,46.5090519,0 7.6134008,46.5099145,0 7.6119518,46.5104353,0 7.6115669,46.5107069,0 7.6102018,46.5110563,0 7.6098364,46.5112294,0 7.6103941,46.5118447,0 7.6116247,46.512287,0 7.6117594,46.5127485,0 7.6116055,46.5130946,0 7.6111248,46.5135561,0 7.6102983,46.513823,0 7.6087831,46.5148332,0 7.6079624,46.5152801,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w20016068">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6223085,46.5128439,0 7.6220228,46.5131772,0 7.621511,46.5141056,0 7.6214266,46.5149416,0 7.6207744,46.5165722,0 7.620101,46.517142,0 7.6197686,46.517605,0 7.6197092,46.5182816,0 7.621015,46.5182816,0 7.621478,46.5181985,0 7.6224277,46.5178305,0 7.6240841,46.5179513,0 7.6247575,46.5179513,0 7.6255728,46.5178449,0 7.6264944,46.5187665,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29376879">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4304432,46.4436726,0 7.4305366,46.443984,0 7.4310658,46.4445755,0 7.4306634,46.4459898,0 7.4299914,46.4465483,0 7.4303109,46.4472287,0 7.4299762,46.4484049,0 7.4302564,46.4493078,0 7.4315017,46.4511447,0 7.4317586,46.452333,0 7.4318155,46.4525963,0 7.4324157,46.4535271,0 7.432081,46.4536883,0 7.4310303,46.4533336,0 7.4311741,46.453932,0 7.4314691,46.4551589,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29376880">
    +            <name>Tschuggen</name>
    +            <description>Tschuggen, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4124787,46.433968,0 7.4140186,46.4327481,0 7.4162843,46.432802,0 7.4176098,46.434637,0 7.4180369,46.4362139,0 7.4170285,46.4381829,0 7.416902,46.4392336,0 7.4160927,46.4416618,0 7.4163856,46.4433081,0 7.4158679,46.4447115,0 7.4172976,46.4466046,0 7.4210461,46.449263,0 7.4230297,46.4499123,0 7.4246353,46.4516699,0 7.4266629,46.4522852,0 7.4293861,46.4546281,0 7.4314691,46.4551589,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569950">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5241429,46.4682155,0 7.5254618,46.4675494,0 7.5271632,46.4670117,0 7.5281584,46.4670197,0 7.5307345,46.4680148,0 7.5316173,46.4680148,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569956">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4913687,46.4586774,0 7.4912344,46.4582615,0 7.4921282,46.4561049,0 7.4910376,46.4540056,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569970">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4979152,46.4417323,0 7.4956492,46.4412029,0 7.4949338,46.4409187,0 7.4918344,46.4396017,0 7.488508,46.4385783,0 7.4862388,46.4377746,0 7.4860119,46.4374343,0 7.4861862,46.4374136,0 7.4864068,46.4375038,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569987">
    +            <name>26</name>
    +            <description>26, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4951921,46.4444865,0 7.494774,46.444374,0 7.4937003,46.444504,0 7.4906334,46.4443422,0 7.4888241,46.4444869,0 7.4871181,46.4440733,0 7.4863944,46.4442402,0 7.4830048,46.443886,0 7.4816834,46.4439676,0 7.4795401,46.4450123,0 7.4783043,46.4457271,0 7.4766312,46.4464834,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29920240">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.488878,46.370053,0 7.4894455,46.3702172,0 7.4899918,46.3703752,0 7.4913225,46.3705818,0 7.4927658,46.3712292,0 7.4942814,46.3711794,0 7.4954185,46.370487,0 7.4959412,46.3696855,0 7.4968794,46.3691378,0 7.4980271,46.3691169,0 7.4985995,46.3690143,0 7.4991844,46.3686383,0 7.4992309,46.3684758,0 7.4987031,46.368402,0 7.4977817,46.3683076,0 7.4988279,46.3674945,0 7.4990444,46.3655523,0 7.4983227,46.3649547,0 7.4983115,46.3639782,0 7.4985155,46.3636965,0 7.4981554,46.3629178,0 7.4983227,46.3625145,0 7.4992609,46.3612196,0 7.5004156,46.3600741,0 7.5016225,46.3579494,0 7.5019726,46.3576574,0 7.5020488,46.357115,0 7.5018589,46.3561892,0 7.502039,46.3546121,0 7.5014259,46.3535992,0 7.5030858,46.3539977,0 7.5049621,46.353898,0 7.5024363,46.350461,0 7.5032301,46.3474473,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w31677463">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4899918,46.3703752,0 7.4900977,46.3710331,0 7.4897799,46.3720807,0 7.4893209,46.3741029,0 7.4888265,46.3742978,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127861">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5798737,46.4320992,0 7.5797957,46.4311319,0 7.5796203,46.43089,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127863">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5798737,46.4320992,0 7.5798711,46.4325194,0 7.5799962,46.432821,0 7.5804649,46.4331279,0 7.5815978,46.4342533,0 7.5818635,46.434668,0 7.5818869,46.4349533,0 7.5808556,46.4367572,0 7.5800508,46.4371395,0 7.5785547,46.437191,0 7.576949,46.4381409,0 7.576699,46.4385663,0 7.5763943,46.4385663,0 7.5759959,46.4382224,0 7.57373,46.4376025,0 7.5731128,46.4379471,0 7.570222,46.4383563,0 7.5696588,46.4383437,0 7.5691665,46.4381822,0 7.5685649,46.4378322,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127870">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5804649,46.4331279,0 7.5785664,46.434178,0 7.5767537,46.4348241,0 7.5762068,46.4355026,0 7.5753864,46.4358579,0 7.5743629,46.4359495,0 7.5725659,46.4366656,0 7.5696588,46.4383437,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127872">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5731128,46.4379471,0 7.5727761,46.4382333,0 7.5727077,46.438534,0 7.5728577,46.4387621,0 7.5731354,46.4393476,0 7.5730157,46.4396247,0 7.5726324,46.4399562,0 7.5721003,46.4402378,0 7.5709796,46.4402849,0 7.5686003,46.4406872,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w39229510">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4942814,46.3711794,0 7.4942998,46.3716666,0 7.4926402,46.3719589,0 7.4897799,46.3720807,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w53129639">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4899918,46.3703752,0 7.4932364,46.3691539,0 7.4938051,46.3683088,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w121275306">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6164187,46.5309288,0 7.6157406,46.5308985,0 7.6152942,46.5307285,0 7.6147089,46.5306361,0 7.6141852,46.5303588,0 7.6140774,46.5301278,0 7.614447,46.5292652,0 7.61437,46.5288801,0 7.6138155,46.5281716,0 7.6140774,46.5273244,0 7.6145086,46.5267853,0 7.6148167,46.5264618,0 7.6143084,46.5259997,0 7.614062,46.526277,0 7.6136153,46.5263232,0 7.6133842,46.526508,0 7.613338,46.5267699,0 7.6129553,46.5269559,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w138292096">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6230501,46.5217836,0 7.6227543,46.5219743,0 7.6224002,46.522133,0 7.6221374,46.5222917,0 7.6220837,46.5224246,0 7.6222393,46.5226387,0 7.6222768,46.52279,0 7.6224217,46.5229598,0 7.6226577,46.5230521,0 7.6229367,46.5232477,0 7.6229933,46.5233276,0 7.6229423,46.5234075,0 7.6228178,46.5233511,0 7.6226145,46.5232171,0 7.6225402,46.5232813,0 7.6226676,46.5234991,0 7.6227387,46.5237748,0 7.6227055,46.5240428,0 7.622677,46.5241963,0 7.6224463,46.5244646,0 7.6223114,46.5245824,0 7.6221921,46.5247769,0 7.6214839,46.5257434,0 7.6210848,46.5263482,0 7.6197054,46.5273804,0 7.6187807,46.5279121,0 7.617833,46.5283281,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806196">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4296801,46.4435027,0 7.4283947,46.4438683,0 7.4276134,46.4448795,0 7.4282822,46.4462702,0 7.4276225,46.4479365,0 7.4270854,46.4485039,0 7.4283468,46.4503228,0 7.4289924,46.4517144,0 7.4295822,46.4524001,0 7.4290117,46.4528401,0 7.429563,46.4533341,0 7.4311741,46.453932,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806197">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5465752,46.4729147,0 7.5468033,46.4729927,0 7.5471194,46.473351,0 7.5477099,46.4747335,0 7.5477488,46.4754147,0 7.5475016,46.4763233,0 7.5473399,46.4765628,0 7.5474617,46.4766434,0 7.5494984,46.4768773,0 7.5499886,46.4772338,0 7.550697,46.4782474,0 7.551339,46.4789507,0 7.5516645,46.4797119,0 7.5514629,46.4801692,0 7.547108,46.4815103,0 7.5470161,46.4816959,0 7.5469546,46.4819903,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806198">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5126782,46.4560798,0 7.5132264,46.4562522,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806201">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4871181,46.4440733,0 7.4865607,46.4435325,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806203">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5183847,46.4722004,0 7.5189475,46.4725535,0 7.519035,46.4731315,0 7.5188279,46.4738282,0 7.5187137,46.4739436,0 7.5188805,46.4742245,0 7.5191219,46.4746667,0 7.5187133,46.4762815,0 7.5185001,46.4767863,0 7.5178777,46.4770115,0 7.5180839,46.4781219,0 7.5180828,46.47883,0 7.5182874,46.480318,0 7.5183371,46.4806788,0 7.5191752,46.4813467,0 7.5196958,46.4814845,0 7.5213221,46.4813858,0 7.5216438,46.4815441,0 7.5206847,46.4840895,0 7.5202806,46.4840895,0 7.5198736,46.4840895,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806204">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4951921,46.4444865,0 7.4964088,46.4441724,0 7.4962012,46.4450654,0 7.4965848,46.4456381,0 7.4966949,46.4456829,0 7.4968375,46.445741,0 7.4994665,46.4454308,0 7.502031,46.4464909,0 7.5033031,46.4476891,0 7.5034979,46.4480839,0 7.5033917,46.4492225,0 7.5043972,46.4500412,0 7.5050304,46.4507279,0 7.5054974,46.4518283,0 7.5064786,46.4530981,0 7.5069655,46.4535058,0 7.5078037,46.4537953,0 7.5097169,46.4542769,0 7.5126782,46.4560798,0 7.5128506,46.4564315,0 7.5128984,46.4568706,0 7.5131163,46.4569628,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806205">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5185001,46.4767863,0 7.5187069,46.4771038,0 7.5180839,46.4781219,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806208">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5465752,46.4729147,0 7.5465588,46.4727969,0 7.5463224,46.4727762,0 7.5457549,46.4722521,0 7.5454878,46.4722027,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806211">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5180828,46.47883,0 7.5187473,46.4791376,0 7.5191845,46.4795753,0 7.5186215,46.4800315,0 7.5182874,46.480318,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806213">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4918389,46.45772,0 7.4953567,46.4572878,0 7.4978347,46.4561065,0 7.4983647,46.4560378,0 7.4991114,46.4562022,0 7.4996241,46.4568782,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806217">
    +            <description>, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5189475,46.4725535,0 7.520193,46.4732475,0 7.5214428,46.4736263,0 7.5205581,46.4749161,0 7.5201512,46.4752867,0 7.519466,46.4755006,0 7.5187133,46.4762815,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806231">
    +            <name>38</name>
    +            <description>38, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.490236,46.4537956,0 7.4895639,46.4533929,0 7.4892057,46.4526215,0 7.4891761,46.4507056,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806239">
    +            <name>52</name>
    +            <description>52, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5003928,46.4650414,0 7.5023418,46.4647048,0 7.5008661,46.4641698,0 7.4994242,46.4632353,0 7.4992965,46.462876,0 7.4999102,46.4623177,0 7.5002146,46.4620329,0 7.5000366,46.4616093,0 7.5000901,46.4612612,0 7.4995804,46.4611479,0 7.4993116,46.4600075,0 7.4993116,46.4590555,0 7.4989271,46.4580207,0 7.4984584,46.4575571,0 7.4986987,46.4571762,0 7.4996241,46.4568782,0 7.5010796,46.4572863,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806245">
    +            <name>80</name>
    +            <description>80, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5464542,46.4719979,0 7.5467662,46.4722875,0 7.5465752,46.4729147,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806247">
    +            <name>84</name>
    +            <description>84, Downhill Run Difficulty: Intermediate</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Intermediate</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.546737,46.4678791,0 7.5477148,46.4684613,0 7.547106,46.4704934,0 7.5464542,46.4719979,0 7.5454878,46.4722027,0 7.5453695,46.4722278,0 7.5448716,46.4723836,0 7.5446572,46.4723806,0 7.5443664,46.4725512,0 7.5440913,46.4725106,0 7.5440027,46.472332,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Advanced</name>
    +          <Style id="style-Ski_Run_Downhill_Advanced">
    +            <LineStyle>
    +              <color>ff000000</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark id="w4389267">
    +            <name>40</name>
    +            <description>40, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5091432,46.4420156,0 7.5110414,46.4433129,0 7.5131315,46.4442903,0 7.5133879,46.4452478,0 7.5138387,46.4464089,0 7.5144526,46.4482402,0 7.5148288,46.449759,0 7.5147337,46.4509702,0 7.5151598,46.4521343,0 7.5147089,46.4532426,0 7.513803,46.4540077,0 7.5136795,46.4546008,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127866">
    +            <description>, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796203,46.43089,0 7.5790744,46.4310512,0 7.5786846,46.4309841,0 7.5786456,46.4308094,0 7.5786261,46.4306213,0 7.5783532,46.4302854,0 7.5779052,46.4304352,0 7.5774045,46.430602,0 7.5768107,46.4309467,0 7.5761544,46.4311244,0 7.5750606,46.4314798,0 7.5744512,46.4321798,0 7.5740527,46.4330468,0 7.5730292,46.4338276,0 7.5724197,46.4338222,0 7.5706227,46.4345814,0 7.5697555,46.4354376,0 7.5682133,46.437843,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127867">
    +            <description>, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5761544,46.4311244,0 7.574334,46.4310113,0 7.5719744,46.4327452,0 7.5707477,46.4341668,0 7.5706227,46.4345814,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w33127875">
    +            <description>, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5780116,46.4306132,0 7.5779052,46.4304352,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w103718991">
    +            <name>51</name>
    +            <description>51, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4948693,46.462373,0 7.4995804,46.4611479,0 7.5044316,46.4577113,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806227">
    +            <name>30</name>
    +            <description>30, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4907043,46.4468387,0 7.4893805,46.447632,0 7.4884723,46.4480082,0 7.4862639,46.4481923,0 7.484473,46.448703,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806234">
    +            <name>4</name>
    +            <description>4, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4317586,46.452333,0 7.4337182,46.4524766,0 7.434805,46.4526768,0 7.4356699,46.4530141,0 7.436796,46.453005,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806235">
    +            <name>41</name>
    +            <description>41, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5110414,46.4433129,0 7.5110529,46.4437445,0 7.5106546,46.4441126,0 7.5107095,46.4447933,0 7.5100632,46.445353,0 7.5079719,46.4459569,0 7.5066993,46.4469162,0 7.5061861,46.4474935,0 7.5071655,46.4486165,0 7.5105129,46.4501148,0 7.5108818,46.4516561,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806240">
    +            <name>54</name>
    +            <description>54, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5079037,46.4503452,0 7.5092498,46.4524805,0 7.5097169,46.4542769,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806242">
    +            <name>57</name>
    +            <description>57, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4966403,46.4646571,0 7.5032905,46.4674926,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806246">
    +            <name>80</name>
    +            <description>80, Downhill Run Difficulty: Advanced</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Advanced</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5465752,46.4729147,0 7.5475189,46.4734417,0 7.5477398,46.4739818,0 7.5496151,46.4749576,0 7.5498033,46.4759118,0 7.5509021,46.476891,0 7.5509823,46.4774471,0 7.5541829,46.4807074,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Expert</name>
    +          <Style id="style-Ski_Run_Downhill_Expert">
    +            <LineStyle>
    +              <color>ff0066ff</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Generic</name>
    +          <Style id="style-Ski_Run_Downhill_Generic">
    +            <LineStyle>
    +              <color>ffff3cc1</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark id="w4816707">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5535228,46.4865284,0 7.5535075,46.4866379,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w20016065">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6222148,46.5124924,0 7.6218631,46.5122645,0 7.6205305,46.5119903,0 7.620247,46.5118688,0 7.6199634,46.5115987,0 7.6199364,46.5109372,0 7.6197599,46.5108209,0 7.6194083,46.5108438,0 7.6191407,46.5109967,0 7.6180937,46.5111776,0 7.6178583,46.5110883,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569891">
    +            <name>Oeystrasse</name>
    +            <description>Oeystrasse, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5535075,46.4866379,0 7.5541077,46.486711,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569893">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5541077,46.486711,0 7.5548912,46.486666,0 7.5549402,46.4863477,0 7.5553483,46.4864375,0 7.5563116,46.4862579,0 7.556932,46.4864783,0 7.5570137,46.4867477,0 7.557332,46.4869844,0 7.5583606,46.4869191,0 7.55863,46.4870334,0 7.5590626,46.4870252,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w29569922">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5466507,46.4822665,0 7.5467405,46.4820455,0 7.5469546,46.4819903,0 7.546887,46.4821013,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w35035781">
    +            <name>55</name>
    +            <description>55, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5202806,46.4840895,0 7.5206995,46.4843934,0 7.521236,46.4846116,0 7.5215509,46.4848385,0 7.5235622,46.4848575,0 7.5243291,46.4849987,0 7.5254277,46.4849455,0 7.5258912,46.484845,0 7.5262259,46.4849337,0 7.5263644,46.4850796,0 7.5270499,46.4847209,0 7.5281228,46.4845732,0 7.5287322,46.4845909,0 7.5299857,46.4847785,0 7.5301055,46.4847918,0 7.5322129,46.484865,0 7.5335432,46.4842858,0 7.5344616,46.4843804,0 7.5353629,46.4842622,0 7.5359808,46.4840021,0 7.5364272,46.4839903,0 7.5368933,46.4838149,0 7.5374056,46.4843272,0 7.5383498,46.4843331,0 7.5392338,46.4841794,0 7.5411564,46.483553,0 7.5426756,46.483618,0 7.5434451,46.4835786,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w75076665">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5590626,46.4870252,0 7.5600259,46.4871885,0 7.5608912,46.4871232,0 7.5611443,46.4873681,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w103718990">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5548912,46.486666,0 7.5561531,46.4869638,0 7.5573714,46.4873324,0 7.5584605,46.4875866,0 7.5585589,46.487845,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w128629932">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5434451,46.4835786,0 7.5437149,46.4835647,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w147422272">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5442346,46.4836269,0 7.5443418,46.4836053,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w147422282">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5437149,46.4835647,0 7.5439543,46.4836822,0 7.5442346,46.4836269,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w147422285">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5443418,46.4836053,0 7.5452866,46.4835281,0 7.5460156,46.4835284,0 7.5483036,46.4841284,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w147422305">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5483036,46.4841284,0 7.5486892,46.4843265,0 7.5502237,46.4845773,0 7.5505897,46.4845683,0 7.5510877,46.4853643,0 7.5523785,46.486287,0 7.5528849,46.4864761,0 7.5535228,46.4865284,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark id="w148806194">
    +            <description>, Downhill Run Difficulty: Unknown</description>
    +            <styleUrl>#style-Ski_Run_Downhill_Generic</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5033917,46.4492225,0 7.503756,46.4493929,0 7.5041471,46.4492925,0 7.5047479,46.4492921,0 7.5054788,46.4495257,0 7.5060307,46.4500934,0 7.5059038,46.4503571,0 7.5047888,46.4499534,0 7.5044793,46.4499425,0 7.5043972,46.4500412,0</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Easy_Gladed</name>
    +          <Style id="style-Ski_Run_Downhill_Easy_Gladed">
    +            <LineStyle>
    +              <color>ff00ff00</color>
    +              <width></width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Intermediate_Gladed</name>
    +          <Style id="style-Ski_Run_Downhill_Intermediate_Gladed">
    +            <LineStyle>
    +              <color>ffff0000</color>
    +              <width></width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Advanced_Gladed</name>
    +          <Style id="style-Ski_Run_Downhill_Advanced_Gladed">
    +            <LineStyle>
    +              <color>ff000000</color>
    +              <width></width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Expert_Gladed</name>
    +          <Style id="style-Ski_Run_Downhill_Expert_Gladed">
    +            <LineStyle>
    +              <color>ff0066ff</color>
    +              <width></width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Generic_Gladed</name>
    +          <Style id="style-Ski_Run_Downhill_Generic_Gladed">
    +            <LineStyle>
    +              <color>ffff3cc1</color>
    +              <width></width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Easy_Lit</name>
    +          <Style id="style-Ski_Run_Downhill_Easy_Lit">
    +            <LineStyle>
    +              <color>#ff00ff00</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Intermediate_Lit</name>
    +          <Style id="style-Ski_Run_Downhill_Intermediate_Lit">
    +            <LineStyle>
    +              <color>#ffff0000</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Advanced_Lit</name>
    +          <Style id="style-Ski_Run_Downhill_Advanced_Lit">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Expert_Lit</name>
    +          <Style id="style-Ski_Run_Downhill_Expert_Lit">
    +            <LineStyle>
    +              <color>#ff0066ff</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Downhill_Generic_Lit</name>
    +          <Style id="style-Ski_Run_Downhill_Generic_Lit">
    +            <LineStyle>
    +              <color>#ffff3cc1</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Run_Nordic</name>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Easy</name>
    +          <Style id="style-Ski_Run_Nordic_Easy">
    +            <LineStyle>
    +              <color>#ff00ff00</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Intermediate</name>
    +          <Style id="style-Ski_Run_Nordic_Intermediate">
    +            <LineStyle>
    +              <color>#ffff0000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Advanced</name>
    +          <Style id="style-Ski_Run_Nordic_Advanced">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Expert</name>
    +          <Style id="style-Ski_Run_Nordic_Expert">
    +            <LineStyle>
    +              <color>#ff0066ff</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Generic</name>
    +          <Style id="style-Ski_Run_Nordic_Generic">
    +            <LineStyle>
    +              <color>#ffff3cc1</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Easy_Gladed</name>
    +          <Style id="style-Ski_Run_Nordic_Easy_Gladed">
    +            <LineStyle>
    +              <color>#ff00ff00</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Intermediate_Gladed</name>
    +          <Style id="style-Ski_Run_Nordic_Intermediate_Gladed">
    +            <LineStyle>
    +              <color>#ffff0000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Advanced_Gladed</name>
    +          <Style id="style-Ski_Run_Nordic_Advanced_Gladed">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Expert_Gladed</name>
    +          <Style id="style-Ski_Run_Nordic_Expert_Gladed">
    +            <LineStyle>
    +              <color>#ff0066ff</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Generic_Gladed</name>
    +          <Style id="style-Ski_Run_Nordic_Generic_Gladed">
    +            <LineStyle>
    +              <color>#ffff3cc1</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Easy_Lit</name>
    +          <Style id="style-Ski_Run_Nordic_Easy_Lit">
    +            <LineStyle>
    +              <color>#ff00ff00</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Intermediate_Lit</name>
    +          <Style id="style-Ski_Run_Nordic_Intermediate_Lit">
    +            <LineStyle>
    +              <color>#ffff0000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Advanced_Lit</name>
    +          <Style id="style-Ski_Run_Nordic_Advanced_Lit">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Expert_Lit</name>
    +          <Style id="style-Ski_Run_Nordic_Expert_Lit">
    +            <LineStyle>
    +              <color>#ff0066ff</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Ski_Run_Nordic_Generic_Lit</name>
    +          <Style id="style-Ski_Run_Nordic_Generic_Lit">
    +            <LineStyle>
    +              <color>#ffff3cc1</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Ski_Routes</name>
    +      <Style id="style-Ski_Routes">
    +        <LineStyle>
    +          <color>#ffff3cc1</color>
    +          <width>3</width>
    +        </LineStyle>
    +      </Style>
    +    </Folder>
    +    <Folder>
    +      <name>Ski_Lift</name>
    +      <Folder>
    +        <name>Ski_Lift_Chairlift</name>
    +        <Style id="style-Ski_Lift_Chairlift">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>6</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w4380585">
    +          <name>Walleg</name>
    +          <description>Walleg (Chairlift) Capacity: people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4307004,46.4584832,0 7.414217,46.447336,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w4388750">
    +          <name>Metschberg</name>
    +          <description>Metschberg (Chairlift) Capacity:4 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4682319,46.4426424,0 7.4878089,46.443423,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w4389265">
    +          <name>Luegli</name>
    +          <description>Luegli (Chairlift) Capacity:4 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5134775,46.4563718,0 7.507632,46.44102,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w4389266">
    +          <name>Lavey</name>
    +          <description>Lavey (Chairlift) Capacity:5 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5137218,46.4574839,0 7.494809,46.462495,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w4389284">
    +          <name>Aebi-Sillerenbühl</name>
    +          <description>Aebi-Sillerenbühl (Chairlift) Capacity:6 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5194921,46.4842678,0 7.5183847,46.4722004,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w8402416">
    +          <name>Metschstand</name>
    +          <description>Metschstand (Chairlift) Capacity:6 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4751279,46.4473859,0 7.4951921,46.4444865,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w23300776">
    +          <name>Bühlberg</name>
    +          <description>Bühlberg (Chairlift) Capacity: people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4709826,46.4512884,0 7.4925723,46.455196,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w147422268">
    +          <description> (Chairlift) Capacity:2 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5437149,46.4835647,0 7.5466507,46.4822665,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w148806183">
    +          <name>Boden-Chuenisbärgli</name>
    +          <description>Boden-Chuenisbärgli (Chairlift) Capacity:4 people</description>
    +          <Snippet>Chairlift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Chairlift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5570355,46.4818791,0 7.5465752,46.4729147,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Gondola</name>
    +        <Style id="style-Ski_Lift_Gondola">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>8</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w4380591">
    +          <name>Stoss-Leiterli</name>
    +          <description>Stoss-Leiterli (Gondola) Capacity: people</description>
    +          <Snippet>Gondola</Snippet>
    +          <styleUrl>#style-Ski_Lift_Gondola</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.429151,46.440554,0 7.4162843,46.432802,0 7.409078,46.428856,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w29376884">
    +          <name>Lenk-Stoss</name>
    +          <description>Lenk-Stoss (Gondola) Capacity: people</description>
    +          <Snippet>Gondola</Snippet>
    +          <styleUrl>#style-Ski_Lift_Gondola</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.436796,46.453005,0 7.429151,46.440554,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w29919593">
    +          <name>Funitel Violettes-Plaine morte</name>
    +          <description>Funitel Violettes-Plaine morte (Gondola) Capacity: people</description>
    +          <Snippet>Gondola</Snippet>
    +          <styleUrl>#style-Ski_Lift_Gondola</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4988727,46.3427065,0 7.4929566,46.358426,0 7.488878,46.370053,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Cable_Car</name>
    +        <Style id="style-Ski_Lift_Cable_Car">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>10</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w4389264">
    +          <name>Geils-Hahnenmoos</name>
    +          <description>Geils-Hahnenmoos (Cable Car) Capacity:4 people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.512872,46.457057,0 7.495256,46.453167,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w8402401">
    +          <name>Metschbahn</name>
    +          <description>Metschbahn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.456975,46.44131,0 7.467957,46.442374,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w20016054">
    +          <name>Elsigbach-Elsigenalp</name>
    +          <description>Elsigbach-Elsigenalp (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.606468,46.529129,0 7.622388,46.522452,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w25576984">
    +          <name>Gemmibahn</name>
    +          <description>Gemmibahn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.625227,46.383782,0 7.616339,46.398008,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w29569916">
    +          <name>Sillerenbahn</name>
    +          <description>Sillerenbahn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.560206,46.487728,0 7.546887,46.4821013,0 7.532755,46.468548,0 7.517934,46.471763,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w33127855">
    +          <name>Engstligenalp</name>
    +          <description>Engstligenalp (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.566433,46.459076,0 7.56516,46.449387,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w35037933">
    +          <name>Tschentenbahn</name>
    +          <description>Tschentenbahn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.55769,46.492944,0 7.5458795,46.4988409,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w40586987">
    +          <description> (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.616339,46.398008,0 7.6132768,46.4028233,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w41617754">
    +          <name>Iffigenalp-Rawil-Weisshorn</name>
    +          <description>Iffigenalp-Rawil-Weisshorn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4432715,46.4012898,0 7.4554936,46.3839921,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w41617756">
    +          <name>Iffigenalp-Rawil-Weisshorn</name>
    +          <description>Iffigenalp-Rawil-Weisshorn (Cable Car) Capacity: people</description>
    +          <Snippet>Cable Car</Snippet>
    +          <styleUrl>#style-Ski_Lift_Cable_Car</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4554936,46.3839921,0 7.4728668,46.3822403,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Mixed_Lift</name>
    +        <Style id="style-Ski_Lift_Mixed_Lift">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>8</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Drag_Lift</name>
    +        <Style id="style-Ski_Lift_Drag_Lift">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w4380584">
    +          <name>Balmen</name>
    +          <description>Balmen (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4311473,46.4436377,0 7.429755,46.4386039,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w33127856">
    +          <description> (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5655255,46.449212,0 7.5679939,46.4406328,0 7.568652,46.4383091,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w33127857">
    +          <description> (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.568574,46.4409349,0 7.5749906,46.4438887,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w33127868">
    +          <description> (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5689165,46.4376761,0 7.5758497,46.4318298,0 7.5777092,46.4308174,0 7.5779358,46.4307636,0 7.5786456,46.4308094,0 7.5796203,46.43089,0 7.5801782,46.4310221,0 7.5779827,46.4329768,0 7.5690523,46.4378121,0 7.5689165,46.4376761,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w33127869">
    +          <description> (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5688149,46.4375738,0 7.5758005,46.4317871,0 7.5780116,46.4306132,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w121554245">
    +          <name>Erli</name>
    +          <description>Erli (Drag Lift)</description>
    +          <Snippet>Drag Lift</Snippet>
    +          <styleUrl>#style-Ski_Lift_Drag_Lift</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.62299,46.3828465,0 7.6192261,46.3858283,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_TBar</name>
    +        <Style id="style-Ski_Lift_TBar">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w4388752">
    +          <name>Guetfläck</name>
    +          <description>Guetfläck (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4864068,46.4375038,0 7.4979322,46.4414643,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w4388830">
    +          <description> (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4915714,46.4504852,0 7.4981254,46.4509924,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w20016060">
    +          <description> (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6039602,46.5170216,0 7.6223253,46.5126763,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w20016061">
    +          <description> (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6178567,46.5109719,0 7.6255759,46.5097159,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w31677466">
    +          <name>Lac</name>
    +          <description>Lac (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4938051,46.3683088,0 7.4894455,46.3702172,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w148806184">
    +          <description> (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5074665,46.4712735,0 7.5028327,46.4648778,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w148806186">
    +          <description> (T-Bar)</description>
    +          <Snippet>T-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_TBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5440027,46.472332,0 7.546737,46.4678791,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_JBar</name>
    +        <Style id="style-Ski_Lift_JBar">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>3</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w31677464">
    +          <name>Glacier 2</name>
    +          <description>Glacier 2 (J-Bar)</description>
    +          <Snippet>J-Bar</Snippet>
    +          <styleUrl>#style-Ski_Lift_JBar</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4888265,46.3742978,0 7.4889324,46.3707894,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Platter</name>
    +        <Style id="style-Ski_Lift_Platter">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w8402539">
    +          <name>Aebi</name>
    +          <description>Aebi (Platter)</description>
    +          <Snippet>Platter</Snippet>
    +          <styleUrl>#style-Ski_Lift_Platter</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5212812,46.4844127,0 7.5220797,46.4823123,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Rope_Tow</name>
    +        <Style id="style-Ski_Lift_Rope_Tow">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w148806185">
    +          <description> (Rope Tow)</description>
    +          <Snippet>Rope Tow</Snippet>
    +          <styleUrl>#style-Ski_Lift_Rope_Tow</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5484827,46.4812398,0 7.5508319,46.4815667,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark id="w148806199">
    +          <description> (Rope Tow)</description>
    +          <Snippet>Rope Tow</Snippet>
    +          <styleUrl>#style-Ski_Lift_Rope_Tow</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5469546,46.4819903,0 7.5473199,46.4814378,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Ski_Lift_Magic_Carpet</name>
    +        <Style id="style-Ski_Lift_Magic_Carpet">
    +          <LineStyle>
    +            <color>#ff0e10b4</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark id="w148806182">
    +          <description> (Magic Carpet)</description>
    +          <Snippet>Magic Carpet</Snippet>
    +          <styleUrl>#style-Ski_Lift_Magic_Carpet</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4705402,46.4532965,0 7.471843,46.4529933,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Ski_Boundary</name>
    +      <Style id="style-Ski_Boundary">
    +        <LineStyle>
    +          <color>#ff0066ff</color>
    +          <width>1</width>
    +        </LineStyle>
    +      </Style>
    +    </Folder>
    +    <Folder>
    +      <name>Paths</name>
    +      <Folder>
    +        <name>Tracks</name>
    +        <Style id="style-Tracks">
    +          <LineStyle>
    +            <color>#ff003f78</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Hörnliweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.565494,46.5008679,0 7.5656791,46.500969,0 7.5658624,46.5011096,0 7.5661445,46.5013261,0 7.5666733,46.5015614,0 7.5667884,46.5017876,0 7.566748,46.5018344,0 7.5665807,46.5016761,0 7.5664381,46.5016324,0 7.566207,46.5016563,0 7.566185,46.501694,0 7.566361,46.501751,0 7.5664099,46.5018833,0 7.5665353,46.5022409,0 7.5667755,46.5024593,0 7.567001,46.502815,0 7.5669076,46.5033531,0 7.567179,46.5039149,0 7.5672453,46.5041525,0 7.5672579,46.5041977,0 7.5678887,46.5054809,0 7.5681607,46.5064122,0 7.5688551,46.5069114,0 7.5691378,46.50727,0 7.569223,46.5076089,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.55826,46.4885102,0 7.5584046,46.4880564,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4722325,46.5557203,0 7.4723727,46.5554306,0 7.4724179,46.5554155,0 7.4725118,46.5554491,0 7.4727424,46.5554155,0 7.4729023,46.5555256,0 7.4730437,46.555317,0 7.4733032,46.5553483,0 7.4740136,46.5550725,0 7.4739997,46.5552336,0 7.4742734,46.5551527,0 7.4745005,46.5549001,0 7.4749441,46.5543583,0 7.4751759,46.5539538,0 7.4755333,46.553592,0 7.4758117,46.5533618,0 7.4763245,46.5528235,0 7.4764189,46.5523924,0 7.4765056,46.5521307,0 7.4766873,46.5520046,0 7.4768397,46.551354,0 7.4770067,46.5513012,0 7.4770917,46.5510639,0 7.4770507,46.5509437,0 7.4770712,46.5508704,0 7.4771386,46.5508675,0 7.4775372,46.5504953,0 7.4774844,46.5504337,0 7.4774962,46.5501231,0 7.4774346,46.5499824,0 7.4776193,46.5494988,0 7.4775987,46.5492526,0 7.4777043,46.5491032,0 7.477672,46.5489039,0 7.4778918,46.5485492,0 7.4777218,46.548388,0 7.4778537,46.5476319,0 7.4779563,46.5475235,0 7.4782259,46.5460844,0 7.4783314,46.5459819,0 7.4785542,46.5459877,0 7.4787066,46.5462515,0 7.4787887,46.5462486,0 7.4788473,46.545554,0 7.4790231,46.5455334,0 7.4790817,46.54517,0 7.478568,46.5448331,0 7.4780354,46.5446806,0 7.4778625,46.5448301,0 7.4774258,46.5445428,0 7.477209,46.5447317,0 7.4774324,46.5448121,0 7.4774294,46.5450385,0 7.4772894,46.5450534,0 7.4773102,46.5454377,0 7.4768455,46.5459619,0 7.4764762,46.5460662,0 7.4762915,46.5451696,0 7.475651,46.54469,0 7.4754752,46.5447048,0 7.4752578,46.5449461,0 7.4748378,46.5449342,0 7.4744892,46.5447019,0 7.4737832,46.5448538,0 7.4734436,46.5442044,0 7.4738309,46.5439482,0 7.4740882,46.5440764,0 7.4743068,46.5439397,0 7.4742902,46.543847,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.57743,46.513533,0 7.57697,46.513383,0 7.576639,46.513398,0 7.575972,46.513236,0 7.5754347,46.51317,0 7.5735754,46.5130497,0 7.573283,46.513028,0 7.5727554,46.5131053,0 7.5725651,46.51319,0 7.572003,46.513318,0 7.57147,46.5134376,0 7.57086,46.513632,0 7.5704027,46.5136993,0 7.5698483,46.5135546,0 7.569485,46.513179,0 7.568995,46.512935,0 7.5682886,46.5130065,0 7.5676272,46.512977,0 7.567304,46.512969,0 7.566875,46.512785,0 7.566209,46.512784,0 7.565811,46.512851,0 7.5654903,46.5129931,0 7.565151,46.513025,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5243291,46.4849987,0 7.5247153,46.4845022,0 7.525197,46.4841121,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5439138,46.4880345,0 7.5436907,46.4883773,0 7.5436564,46.4888146,0 7.5445919,46.4885664,0 7.5449953,46.4886255,0 7.5454416,46.4885664,0 7.5460167,46.4881409,0 7.5460596,46.4878868,0 7.546257,46.4878336,0 7.5465059,46.4880227,0 7.546566,46.4882709,0 7.5471926,46.4885959,0 7.5477419,46.4888205,0 7.5486174,46.488986,0 7.5491066,46.4891869,0 7.5493813,46.4894646,0 7.5494156,46.4900083,0 7.5491066,46.4903747,0 7.5488834,46.4907942,0 7.5489435,46.4908828,0 7.5492268,46.4907528,0 7.5491152,46.4909124,0 7.5492783,46.4912079,0 7.549201,46.4915033,0 7.549304,46.4916747,0 7.5492783,46.4918933,0 7.5495186,46.4920647,0 7.550437,46.4920765,0 7.550909,46.4922242,0 7.5519562,46.492886,0 7.5520592,46.4934237,0 7.5523596,46.4936542,0 7.5524712,46.4936955,0 7.5525656,46.4936365,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5525656,46.4936365,0 7.5523274,46.4934746,0 7.5523274,46.4930905,0 7.5524115,46.4926899,0 7.5523257,46.4923796,0 7.5520468,46.4919365,0 7.5518322,46.4917444,0 7.5515532,46.4916706,0 7.5516391,46.4915228,0 7.551918,46.4915672,0 7.5524974,46.4918774,0 7.5528622,46.492099,0 7.553263,46.4922691,0 7.5533831,46.4923341,0 7.553469,46.4925764,0 7.5535806,46.4925409,0 7.5536063,46.4923164,0 7.5535291,46.4920859,0 7.5527051,46.4912587,0 7.5523274,46.4909928,0 7.5524562,46.4906559,0 7.552336,46.4902305,0 7.5524133,46.4901418,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Panoramaweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5487911,46.4869921,0 7.5491129,46.4871325,0 7.5495421,46.4872654,0 7.5500785,46.4874058,0 7.5505077,46.4875462,0 7.5511729,46.4879007,0 7.5515698,46.4881962,0 7.5519668,46.4885951,0 7.5521814,46.4889718,0 7.5523638,46.489415,0 7.5523852,46.4897843,0 7.5524133,46.4901418,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5662091,46.4580919,0 7.5662713,46.458293,0 7.5669387,46.4587127,0 7.5669473,46.4590261,0 7.5667928,46.4591384,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4445475,46.5790933,0 7.4458753,46.5773845,0 7.4458753,46.57704,0 7.4458753,46.5764872,0 7.4454415,46.5759023,0 7.4449725,46.5753015,0 7.4445622,46.5747407,0 7.4442338,46.5745324,0 7.4439055,46.5743321,0 7.4437531,46.5740276,0 7.4435655,46.5738594,0 7.4435772,46.5737072,0 7.4437179,46.5733947,0 7.4436593,46.5731144,0 7.4434131,46.572858,0 7.4434131,46.5725455,0 7.4432606,46.5724094,0 7.443073,46.5721289,0 7.4428151,46.5718886,0 7.4427096,46.5716242,0 7.4423461,46.5711435,0 7.4418908,46.5708556,0 7.4414336,46.5705832,0 7.4412694,46.5703749,0 7.4410584,46.5698701,0 7.4408942,46.5694536,0 7.4408042,46.5690943,0 7.4407082,46.5687701,0 7.4404839,46.5684761,0 7.4403197,46.5681076,0 7.4402211,46.5678374,0 7.4401859,46.5675851,0 7.4401068,46.5672666,0 7.4399319,46.5670379,0 7.4398305,46.566828,0 7.4394297,46.5665636,0 7.4392764,46.5663903,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.582288,46.50904,0 7.5818959,46.50922,0 7.5814771,46.5092119,0 7.58117,46.509261,0 7.580918,46.509277,0 7.5809353,46.5092018,0 7.581108,46.50897,0 7.581143,46.5088664,0 7.5811,46.508754,0 7.5810428,46.5086824,0 7.5809629,46.5086397,0 7.5806824,46.5085984,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5811311,46.5115993,0 7.5812245,46.5113495,0 7.581362,46.511302,0 7.581983,46.511495,0 7.5817095,46.5110604,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5659698,46.4470345,0 7.5650031,46.4460937,0 7.563934,46.445205,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5707798,46.4401998,0 7.569089,46.4414773,0 7.5687886,46.4416607,0 7.5684624,46.4417139,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5684624,46.4417139,0 7.5669518,46.4423053,0 7.5657759,46.4423822,0 7.5653982,46.4425064,0 7.5652008,46.4427608,0 7.5642996,46.4433581,0 7.564128,46.4436183,0 7.5631237,46.4440856,0 7.5624986,46.444518,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5633834,46.4449349,0 7.5632267,46.4453985,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5578623,46.4428613,0 7.5580572,46.4430058,0 7.558304,46.443065,0 7.5586366,46.4431167,0 7.5589907,46.4430946,0 7.5594091,46.4431389,0 7.5597284,46.4432301,0 7.5599777,46.4432794,0 7.5603342,46.4434764,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5014286,46.4245354,0 7.5006562,46.4241982,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5000468,46.4239793,0 7.4995318,46.4237071,0 7.4992657,46.4238313,0 7.4993,46.4240858,0 7.4987765,46.4239438,0 7.4983301,46.4237012,0 7.4979697,46.4233462,0 7.4974804,46.4232456,0 7.4969912,46.4232634,0 7.4965191,46.4230385,0 7.4949055,46.4225356,0 7.4928284,46.422293,0 7.4925108,46.4221865,0 7.4922705,46.4213819,0 7.4910088,46.421583,0 7.4907513,46.4216955,0 7.4911204,46.4219025,0 7.4904766,46.422222,0 7.4900475,46.4225415,0 7.4898844,46.4229202,0 7.4899359,46.4234704,0 7.489584,46.4236657,0 7.4893952,46.4239615,0 7.4878846,46.4243757,0 7.4875584,46.4246478,0 7.487052,46.4247839,0 7.4866143,46.4247898,0 7.4864512,46.4250915,0 7.4869662,46.4252809,0 7.4869576,46.4255589,0 7.4862624,46.4258547,0 7.4859791,46.4260973,0 7.4859276,46.4263813,0 7.4843457,46.4265489,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5006562,46.4241982,0 7.5003643,46.424139,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5003643,46.424139,0 7.5000468,46.4239793,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4843457,46.4265489,0 7.4840821,46.4265818,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4840821,46.4265818,0 7.4831295,46.4267008,0 7.482194,46.4265943,0 7.4811898,46.4266712,0 7.4788895,46.4265588,0 7.478323,46.4266475,0 7.4783109,46.4266478,0 7.4770527,46.4266771,0 7.4761172,46.426896,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5659698,46.4470345,0 7.5655203,46.4480124,0 7.5655891,46.448399,0 7.5651457,46.4489852,0 7.5649628,46.449227,0 7.56516,46.449387,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5623083,46.444399,0 7.5621882,46.4442866,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5621882,46.4442866,0 7.5619659,46.4441994,0 7.5603342,46.4434764,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5638662,46.4451512,0 7.5633834,46.4449349,0 7.5624986,46.444518,0 7.5623083,46.444399,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.563934,46.445205,0 7.5638662,46.4451512,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5650031,46.4460937,0 7.5644603,46.4461059,0 7.563467,46.446806,0 7.5631545,46.4467541,0 7.562727,46.446683,0 7.5621935,46.4470208,0 7.5616868,46.4474721,0 7.5615495,46.4474679,0 7.561039,46.447273,0 7.560624,46.447319,0 7.560726,46.447719,0 7.56098,46.448121,0 7.560985,46.448126,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5619659,46.4441994,0 7.5616754,46.4444958,0 7.5609186,46.4449775,0 7.56084,46.4452181,0 7.5609195,46.4455156,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6216319,46.5569936,0 7.621301,46.556518,0 7.621064,46.556056,0 7.620255,46.555568,0 7.61985,46.555023,0 7.619469,46.554934,0 7.61885,46.55517,0 7.618224,46.555101,0 7.616902,46.554591,0 7.616307,46.554441,0 7.6157588,46.5541889,0 7.615575,46.554088,0 7.615245,46.553345,0 7.615047,46.553178,0 7.614621,46.5530406,0 7.614502,46.552815,0 7.6145042,46.5525758,0 7.6144034,46.5523715,0 7.6143229,46.5523162,0 7.6137389,46.5522484,0 7.6133449,46.5520084,0 7.6131605,46.5518493,0 7.612778,46.551181,0 7.612155,46.550717,0 7.611452,46.550374,0 7.6106649,46.5501724,0 7.6104511,46.5495174,0 7.6104444,46.5492243,0 7.61033,46.549085,0 7.6099347,46.5488565,0 7.6099128,46.5485968,0 7.609773,46.5483856,0 7.6095989,46.5482497,0 7.6093448,46.5481847,0 7.609047,46.548181,0 7.6080786,46.5483557,0 7.6079368,46.5483324,0 7.607811,46.548242,0 7.6076426,46.5479874,0 7.6077006,46.5479132,0 7.607992,46.54754,0 7.6083158,46.5473431,0 7.6085167,46.5470549,0 7.6084328,46.5468316,0 7.6078638,46.54661,0 7.6078362,46.5465457,0 7.6079946,46.5463545,0 7.6082273,46.5462439,0 7.6085278,46.5461917,0 7.6087459,46.546067,0 7.6088015,46.5458812,0 7.608665,46.545726,0 7.6076322,46.5450348,0 7.6070738,46.5450152,0 7.6065814,46.5447507,0 7.606325,46.544763,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.614076,46.555321,0 7.6141065,46.555273,0 7.6139027,46.555118,0 7.613895,46.554985,0 7.614333,46.554527,0 7.614442,46.554234,0 7.61451,46.554227,0 7.614586,46.55458,0 7.614753,46.554636,0 7.615356,46.554676,0 7.615528,46.554846,0 7.615611,46.554802,0 7.615595,46.554721,0 7.615483,46.554504,0 7.615205,46.5542,0 7.615198,46.554095,0 7.615281,46.554102,0 7.615478,46.55423,0 7.6157588,46.5541889,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.562625,46.528737,0 7.56258,46.528984,0 7.5624755,46.5290639,0 7.5622926,46.5292237,0 7.5622248,46.5293516,0 7.5622072,46.5294627,0 7.5623111,46.5297611,0 7.5622992,46.5298085,0 7.5622281,46.5298254,0 7.5620681,46.5297868,0 7.5619444,46.5298217,0 7.5617042,46.5300155,0 7.5616171,46.5301839,0 7.5614941,46.5302822,0 7.5612441,46.5305146,0 7.5613218,46.5305287,0 7.5615238,46.5304796,0 7.5615986,46.5304887,0 7.5617029,46.5305871,0 7.5617754,46.5305914,0 7.5619127,46.5304947,0 7.5619779,46.5305096,0 7.5619084,46.5306169,0 7.5618508,46.5308156,0 7.5617721,46.5308511,0 7.5616366,46.5308149,0 7.5615781,46.5308304,0 7.5615803,46.5309603,0 7.5618485,46.5310641,0 7.562042,46.530987,0 7.562019,46.531133,0 7.5617081,46.5312275,0 7.5621951,46.5313441,0 7.5622535,46.5314364,0 7.561869,46.531579,0 7.5618187,46.5316877,0 7.5624312,46.5316881,0 7.5624457,46.5317378,0 7.5619692,46.5322001,0 7.561704,46.532197,0 7.5616572,46.5322558,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.448503,46.459865,0 7.4481932,46.4599236,0 7.447857,46.459965,0 7.4477077,46.4598442,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.422303,46.459911,0 7.422095,46.459751,0 7.421974,46.45922,0 7.42177,46.459237,0 7.421713,46.459428,0 7.421433,46.459584,0 7.421153,46.459927,0 7.421124,46.460335,0 7.421335,46.460904,0 7.421273,46.461004,0 7.421129,46.46101,0 7.420944,46.460704,0 7.420039,46.459998,0 7.419421,46.459828,0 7.419236,46.459693,0 7.419076,46.459556,0 7.418931,46.459118,0 7.418705,46.458838,0 7.417878,46.458255,0 7.417994,46.458118,0 7.4184,46.457984,0 7.418286,46.45737,0 7.418312,46.457205,0 7.418543,46.456938,0 7.418476,46.456862,0 7.417901,46.457018,0 7.417152,46.457407,0 7.41648,46.457492,0 7.416225,46.457234,0 7.416697,46.456572,0 7.416569,46.456405,0 7.416079,46.456122,0 7.415381,46.455862,0 7.415504,46.455627,0 7.415751,46.455464,0 7.416825,46.455187,0 7.416938,46.4549,0 7.416739,46.454633,0 7.415345,46.453554,0 7.414514,46.453405,0 7.413282,46.452981,0 7.411303,46.451664,0 7.410109,46.45107,0 7.40973,46.450997,0 7.409619,46.450662,0 7.409465,46.450521,0 7.408415,46.450157,0 7.408251,46.450016,0 7.408062,46.449662,0 7.407399,46.449145,0 7.4071281,46.4487423,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.464328,46.457064,0 7.463382,46.457176,0 7.462897,46.457377,0 7.462649,46.457567,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.469627,46.453826,0 7.469009,46.453963,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.448482,46.460149,0 7.448486,46.460146,0 7.448833,46.459856,0 7.448785,46.459794,0 7.448503,46.459865,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5903867,46.5152344,0 7.5903489,46.5153277,0 7.590271,46.515499,0 7.590262,46.515674,0 7.590325,46.515841,0 7.590276,46.515941,0 7.590159,46.516075,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.589031,46.518301,0 7.589029,46.518367,0 7.589031,46.51853,0 7.589068,46.51861,0 7.589181,46.518754,0 7.58928,46.518836,0 7.589371,46.518996,0 7.589441,46.519072,0 7.589449,46.519104,0 7.589424,46.519186,0 7.589486,46.519322,0 7.589476,46.519587,0 7.589486,46.519672,0 7.589452,46.51985,0 7.589515,46.519951,0 7.589558,46.520058,0 7.589665,46.520142,0 7.58973,46.520286,0 7.589863,46.520462,0 7.589887,46.520566,0 7.589898,46.520727,0 7.589946,46.520843,0 7.5900055,46.5210825,0 7.589943,46.521137,0 7.5898983,46.5211664,0 7.5898629,46.5212153,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6093448,46.5481847,0 7.6094427,46.5483293,0 7.6094101,46.548591,0 7.609356,46.548667,0 7.609166,46.548845,0 7.6089752,46.5489862,0 7.6088952,46.5490921,0 7.6086869,46.549561,0 7.608622,46.5497762,0 7.608591,46.54994,0 7.6086371,46.5501173,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Lischenstrasse</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5802181,46.4985699,0 7.5801466,46.4985211,0 7.580027,46.4983298,0 7.5798853,46.498233,0 7.5797367,46.4978481,0 7.5796769,46.4977522,0 7.579526,46.497591,0 7.5792835,46.4974686,0 7.5791957,46.4973171,0 7.5790319,46.4971786,0 7.5788853,46.4971104,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.578695,46.517886,0 7.578953,46.518042,0 7.579168,46.51811,0 7.579338,46.518213,0 7.579463,46.51835,0 7.579476,46.518507,0 7.579533,46.518679,0 7.579717,46.518822,0 7.579934,46.518941,0 7.580101,46.519058,0 7.580151,46.519196,0 7.580164,46.519362,0 7.580285,46.519495,0 7.5807866,46.5197277,0 7.581016,46.519826,0 7.581209,46.5199,0 7.581276,46.519904,0 7.5812961,46.5198713,0 7.581275,46.519829,0 7.581047,46.519686,0 7.58093,46.519564,0 7.580785,46.51945,0 7.580735,46.519297,0 7.580866,46.519152,0 7.580853,46.519022,0 7.580703,46.518801,0 7.580678,46.518635,0 7.580573,46.518538,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5807866,46.5197277,0 7.5808963,46.5198879,0 7.580874,46.519925,0 7.580826,46.519954,0 7.580638,46.520001,0 7.5803428,46.5199976,0 7.5801735,46.5200411,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6128563,46.5576717,0 7.612698,46.557657,0 7.612517,46.557642,0 7.612308,46.557685,0 7.612183,46.557782,0 7.61231,46.557889,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6045687,46.4880471,0 7.6045863,46.4881633,0 7.6045673,46.4882513,0 7.6044723,46.4884403,0 7.6043857,46.488565,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5897212,46.4818723,0 7.5895673,46.4819072,0 7.589368,46.482049,0 7.589186,46.482293,0 7.5889665,46.4825243,0 7.588688,46.482802,0 7.588667,46.482876,0 7.588272,46.483302,0 7.587817,46.483767,0 7.587592,46.484096,0 7.587544,46.484295,0 7.5873982,46.4845334,0 7.58722,46.484682,0 7.586952,46.484887,0 7.586723,46.484991,0 7.586202,46.485288,0 7.585801,46.485609,0 7.5853807,46.486011,0 7.585214,46.486292,0 7.5851904,46.4864815,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5773923,46.4947336,0 7.577378,46.494574,0 7.577124,46.494315,0 7.5770454,46.4942655,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4393406,46.4117801,0 7.4392719,46.411641,0 7.4393529,46.411519,0 7.4396951,46.4114482,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4864609,46.3705861,0 7.4871769,46.3704428,0 7.4885874,46.3703193,0 7.4891387,46.370344,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4891387,46.370344,0 7.4902628,46.3705021,0 7.4912509,46.3708331,0 7.4922462,46.3712679,0 7.4926614,46.3714013,0 7.4932557,46.3710307,0 7.4943369,46.3708875,0 7.4952462,46.370512,0 7.4956472,46.3701464,0 7.4958119,46.3696573,0 7.4961341,46.3693312,0 7.4969503,46.369099,0 7.4977594,46.3690793,0 7.4982964,46.3690496,0 7.4989479,46.368768,0 7.4992988,46.3682987,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4922705,46.4213819,0 7.4920912,46.4210829,0 7.492428,46.4207877,0 7.4926512,46.4205796,0 7.493215,46.420485,0 7.4936048,46.420432,0 7.4938281,46.4200158,0 7.4938848,46.4191076,0 7.4936426,46.4187141,0 7.4928631,46.4181881,0 7.4924317,46.4180405,0 7.4916144,46.4174502,0 7.4910922,46.4173707,0 7.4904754,46.4171777,0 7.4902029,46.4168183,0 7.4901878,46.4164474,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5640376,46.4659612,0 7.5641356,46.4658925,0 7.5644622,46.465664,0 7.5647672,46.4653051,0 7.5649746,46.4650798,0 7.5651461,46.4649462,0 7.5660472,46.4644537,0 7.5661701,46.464399,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5661701,46.464399,0 7.566232,46.46429,0 7.5662324,46.4640747,0 7.566153,46.463848,0 7.5658337,46.4636438,0 7.5656605,46.4634444,0 7.5655755,46.463216,0 7.5656183,46.4631362,0 7.5658357,46.4630618,0 7.5667984,46.4630235,0 7.5669353,46.4631081,0 7.5670907,46.4632844,0 7.5673083,46.4635677,0 7.5677701,46.4639593,0 7.5677916,46.4643203,0 7.5678558,46.4645145,0 7.5681316,46.4648778,0 7.5682555,46.4648917,0 7.5682601,46.4647927,0 7.5682358,46.4644346,0 7.5683003,46.4641251,0 7.5683716,46.464057,0 7.5684816,46.4640586,0 7.5687293,46.4644566,0 7.5687758,46.4647994,0 7.568836,46.4649425,0 7.5690456,46.4651543,0 7.5692344,46.4655112,0 7.5691868,46.465725,0 7.569286,46.4658155,0 7.569435,46.465632,0 7.569508,46.465472,0 7.5695098,46.4652372,0 7.5695761,46.4650048,0 7.5695761,46.4646523,0 7.5696509,46.4645017,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4248311,46.4957794,0 7.4245438,46.4955981,0 7.4236484,46.495249,0 7.4234662,46.4949758,0 7.423193,46.4947178,0 7.4228743,46.4944901,0 7.42196,46.4940534,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5735275,46.4691135,0 7.57336,46.469298,0 7.573238,46.469411,0 7.5731997,46.4695416,0 7.573284,46.4697528,0 7.5733001,46.4698644,0 7.573262,46.469952,0 7.5731973,46.4700178,0 7.573187,46.470135,0 7.5732019,46.4702022,0 7.573187,46.470352,0 7.5732409,46.4705046,0 7.573242,46.4705791,0 7.5732157,46.470724,0 7.5733061,46.4708862,0 7.5734015,46.4709989,0 7.5735742,46.4710778,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hohliebeweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5733697,46.4768436,0 7.573353,46.476752,0 7.5733911,46.4766038,0 7.573493,46.476286,0 7.5734351,46.4760864,0 7.5733474,46.4760144,0 7.5732267,46.4758689,0 7.573313,46.47557,0 7.573431,46.475316,0 7.573552,46.475106,0 7.573604,46.474837,0 7.57359,46.4746865,0 7.573548,46.474581,0 7.5732579,46.4742404,0 7.5731951,46.4737075,0 7.5731212,46.4734883,0 7.5730983,46.4732924,0 7.5731432,46.4730867,0 7.5732774,46.472861,0 7.5734388,46.472535,0 7.5736016,46.4723781,0 7.5738331,46.4722663,0 7.5739683,46.4721113,0 7.5740242,46.4719814,0 7.573929,46.471627,0 7.5735742,46.4710778,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5733911,46.4766038,0 7.5737293,46.4768559,0 7.5738944,46.4769527,0 7.5740528,46.4769137,0 7.5742429,46.476773,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5751232,46.4803012,0 7.574269,46.480065,0 7.574085,46.480036,0 7.573709,46.479919,0 7.573569,46.479818,0 7.573342,46.479577,0 7.573053,46.47915,0 7.5729777,46.4790803,0 7.5728374,46.4790077,0 7.5727664,46.4789417,0 7.5727002,46.4787992,0 7.5726607,46.4787749,0 7.5726211,46.4788179,0 7.572565,46.4789796,0 7.5725895,46.4791432,0 7.572533,46.479272,0 7.572535,46.479423,0 7.572453,46.480005,0 7.5724148,46.4801166,0 7.572435,46.480232,0 7.5724691,46.4803328,0 7.5725689,46.4804525,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5740293,46.4894136,0 7.574015,46.489153,0 7.5741006,46.4890715,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5741006,46.4890715,0 7.574056,46.488667,0 7.57397,46.488541,0 7.573774,46.488517,0 7.57331,46.488254,0 7.573174,46.488224,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.592367,46.5149672,0 7.5922017,46.5152686,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5463905,46.4726263,0 7.546366,46.472721,0 7.5463738,46.472813,0 7.5464139,46.4728843,0 7.546528,46.472992,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5477933,46.4743096,0 7.54757,46.474233,0 7.547457,46.474213,0 7.547301,46.474235,0 7.547152,46.474314,0 7.547062,46.474311,0 7.546977,46.474161,0 7.546801,46.473923,0 7.546706,46.47382,0 7.54664,46.473763,0 7.546483,46.473653,0 7.546253,46.47342,0 7.546126,46.47332,0 7.546031,46.473211,0 7.545932,46.473064,0 7.545876,46.472971,0 7.545769,46.472472,0 7.545809,46.472383,0 7.545972,46.472179,0 7.5460217,46.4721319,0 7.546117,46.472286,0 7.546154,46.472453,0 7.546266,46.472588,0 7.5463905,46.4726263,0 7.5464571,46.47259,0 7.5465039,46.4724999,0 7.5465251,46.472387,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.550773,46.4757458,0 7.5506962,46.4757786,0 7.5505663,46.4760656,0 7.550485,46.4762,0 7.5501625,46.4766475,0 7.549997,46.476819,0 7.549832,46.476925,0 7.549748,46.476959,0 7.5494582,46.4770455,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5494582,46.4770455,0 7.5493221,46.4771051,0 7.5491396,46.4772255,0 7.548826,46.4773225,0 7.5486479,46.4774293,0 7.548434,46.477516,0 7.548247,46.477632,0 7.5479858,46.4777277,0 7.5476525,46.4777526,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5788121,46.4963841,0 7.578966,46.4963599,0 7.5790202,46.4963178,0 7.579056,46.496135,0 7.5789867,46.4959231,0 7.5790707,46.495913,0 7.5793381,46.4961651,0 7.5795192,46.4962795,0 7.579574,46.496281,0 7.5795861,46.4962396,0 7.5795507,46.496092,0 7.5795179,46.4960341,0 7.5795848,46.495996,0 7.5797434,46.4960857,0 7.5799401,46.4961536,0 7.580077,46.496183,0 7.58027,46.496287,0 7.5803426,46.4962898,0 7.5803678,46.4962349,0 7.5802526,46.496084,0 7.5801532,46.4959733,0 7.580083,46.495718,0 7.579987,46.4956342,0 7.5798442,46.4954093,0 7.5797401,46.4953337,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.551122,46.463773,0 7.5510751,46.4639061,0 7.551126,46.464136,0 7.551157,46.46429,0 7.551131,46.464488,0 7.551011,46.464817,0 7.5511177,46.4651568,0 7.551141,46.465532,0 7.551148,46.465689,0 7.551205,46.465825,0 7.551178,46.465985,0 7.551117,46.466111,0 7.55102,46.466282,0 7.5509148,46.4667058,0 7.5508464,46.4667767,0 7.55076,46.466756,0 7.550766,46.466644,0 7.550804,46.466472,0 7.550801,46.46641,0 7.550818,46.466297,0 7.550888,46.46611,0 7.5508992,46.4659542,0 7.5507243,46.4657912,0 7.550437,46.465633,0 7.550125,46.465271,0 7.550097,46.4651635,0 7.549923,46.46466,0 7.549876,46.464615,0 7.549742,46.46452,0 7.549705,46.464381,0 7.5496948,46.4642671,0 7.549453,46.463839,0 7.549126,46.463452,0 7.548957,46.463311,0 7.548793,46.463125,0 7.548655,46.462876,0 7.548514,46.462743,0 7.548238,46.462601,0 7.548131,46.462514,0 7.548045,46.462474,0 7.547837,46.462405,0 7.547614,46.462349,0 7.5474366,46.4622477,0 7.547348,46.462173,0 7.547249,46.462054,0 7.547227,46.461882,0 7.547246,46.461712,0 7.547194,46.461545,0 7.546998,46.461234,0 7.546996,46.460975,0 7.546968,46.460886,0 7.5467453,46.4607696,0 7.5466425,46.4607158,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5324614,46.4521053,0 7.5326827,46.452079,0 7.532898,46.452116,0 7.5330862,46.4521987,0 7.5332827,46.4523798,0 7.533559,46.452494,0 7.534264,46.452695,0 7.5346068,46.4526309,0 7.5347041,46.4526012,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5505555,46.461797,0 7.5503452,46.461619,0 7.550283,46.461427,0 7.550137,46.4612643,0 7.549677,46.4609514,0 7.5496432,46.4608955,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5525656,46.4936365,0 7.5528161,46.4936972,0 7.5529717,46.4937895,0 7.5532023,46.4938745,0 7.5533579,46.4938597,0 7.5536959,46.4937378,0 7.5540177,46.4937341,0 7.5542269,46.4937489,0 7.5543128,46.4938375,0 7.5542538,46.4939262,0 7.5540499,46.494133,0 7.5538943,46.4943583,0 7.5538032,46.494602,0 7.5537978,46.4948273,0 7.553771,46.4950525,0 7.5538192,46.4951855,0 7.5540445,46.4955289,0 7.554184,46.4957985,0 7.5543771,46.4960496,0 7.554581,46.4961826,0 7.5548278,46.4962638,0 7.5550262,46.4962564,0 7.5551872,46.4962786,0 7.5552623,46.4963451,0 7.5553696,46.4965592,0 7.5554768,46.4968325,0 7.5555734,46.4971981,0 7.5556592,46.497416,0 7.5556592,46.4975489,0 7.5555466,46.4976892,0 7.5553588,46.4978997,0 7.5553213,46.4980807,0 7.5553427,46.4981656,0 7.5554339,46.4981804,0 7.5557343,46.4981656,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Dauberhorn - Gemmibahn</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6026829,46.3875479,0 7.602328,46.387297,0 7.6021778,46.3868011,0 7.6021457,46.3865273,0 7.6018452,46.3866235,0 7.6017809,46.3864903,0 7.6014697,46.3864385,0 7.6010835,46.3865347,0 7.60059,46.3866605,0 7.6001286,46.3867049,0 7.5997639,46.3868011,0 7.5994205,46.3868603,0 7.5989592,46.3867715,0 7.5984335,46.3867789,0 7.5978219,46.3869047,0 7.5970387,46.3870009,0 7.5962984,46.3870824,0 7.5958693,46.3873858,0 7.5956869,46.3876152,0 7.5957298,46.3879926,0 7.5956976,46.3883034,0 7.5950539,46.3883404,0 7.5943029,46.3885476,0 7.5939166,46.388666,0 7.5934124,46.3888807,0 7.5931978,46.3890657,0 7.5930369,46.3890805,0 7.5928438,46.3893247,0 7.5926292,46.3895171,0 7.5924468,46.3896281,0 7.5925433,46.3898279,0 7.5926506,46.3901091,0 7.5927043,46.3904791,0 7.5915885,46.3912709,0 7.591095,46.3912339,0 7.5907302,46.3909897,0 7.5902152,46.3909083,0 7.5895822,46.3908121,0 7.5890779,46.3908935,0 7.5887668,46.3911895,0 7.5887132,46.3913375,0 7.5882947,46.3914855,0 7.5875437,46.3916927,0 7.5872004,46.3918111,0 7.5868034,46.3919887,0 7.5864494,46.3920775,0 7.5863099,46.3922477,0 7.5848937,46.3921071,0 7.5844216,46.3922773,0 7.5837564,46.3924919,0 7.5830698,46.3926251,0 7.5825977,46.3927657,0 7.5824904,46.3930691,0 7.582587,46.3933725,0 7.5828123,46.3936093,0 7.5831878,46.3937499,0 7.5833809,46.3940088,0 7.5835955,46.394364,0 7.5840139,46.3947192,0 7.5842285,46.3949264,0 7.5844109,46.3950966,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4800756,46.5665507,0 7.4797926,46.5665166,0 7.4796178,46.5666915,0 7.4795866,46.567193,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4854834,46.5161258,0 7.4855385,46.5162773,0 7.4860047,46.516287,0 7.4862334,46.5162894,0 7.4863353,46.5163123,0 7.4863709,46.516376,0 7.4863073,46.5165008,0 7.4861876,46.516595,0 7.4859915,46.5169922,0 7.4858387,46.5171984,0 7.4857878,46.5173207,0 7.4856325,46.5174683,0 7.4855638,46.517476,0 7.485551,46.517504,0 7.4855638,46.5175778,0 7.4855638,46.517644,0 7.4854939,46.5177249,0 7.485479,46.5178293,0 7.4854094,46.5178741,0 7.48529,46.5179586,0 7.4852304,46.5180432,0 7.4851856,46.518073,0 7.4851508,46.5181625,0 7.4850513,46.518267,0 7.4849668,46.5183863,0 7.4849469,46.5185057,0 7.4849767,46.51863,0 7.4850463,46.5187444,0 7.4851309,46.5188389,0 7.4850762,46.5189035,0 7.4850165,46.5189334,0 7.4847927,46.5189682,0 7.4847032,46.5190129,0 7.4846684,46.5190676,0 7.4846187,46.5193412,0 7.4846734,46.5194357,0 7.4847032,46.5195848,0 7.4847281,46.5197092,0 7.4847032,46.5198982,0 7.4845988,46.5200026,0 7.4843849,46.5200722,0 7.4843799,46.5201369,0 7.4847529,46.5200871,0 7.4848822,46.5200374,0 7.4849668,46.5199429,0 7.4854691,46.5197042,0 7.485847,46.5197092,0 7.4863394,46.5197689,0 7.4866477,46.5197539,0 7.4868615,46.5196445,0 7.4869113,46.51956,0 7.4869361,46.5193412,0 7.4869859,46.5192467,0 7.4872097,46.5190577,0 7.4877169,46.5187991,0 7.4883535,46.5185405,0 7.4886569,46.5185355,0 7.4888409,46.5185057,0 7.4893133,46.5182371,0 7.4898007,46.518068,0 7.4903527,46.5178791,0 7.490661,46.5178293,0 7.4906511,46.5179039,0 7.4905417,46.5179735,0 7.4904273,46.5181774,0 7.4903079,46.5183664,0 7.4901438,46.5186549,0 7.4898255,46.5192914,0 7.4897708,46.5196296,0 7.4894476,46.5202562,0 7.4894128,46.5204949,0 7.4894526,46.5207834,0 7.4894277,46.5209176,0 7.4892984,46.5213304,0 7.4891591,46.5213951,0 7.4891392,46.5214547,0 7.4892089,46.5214846,0 7.4895669,46.5214199,0 7.4901388,46.5213056,0 7.490472,46.5212956,0 7.4908351,46.5212508,0 7.4912976,46.5212011,0 7.4914169,46.5212857,0 7.4918049,46.5213304,0 7.4919292,46.5213851,0 7.4920336,46.5215194,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4021735,46.5042278,0 7.4022338,46.5039567,0 7.4030306,46.5030461,0 7.4037515,46.5022493,0 7.4056866,46.5010162,0 7.4058004,46.5007506,0 7.4059332,46.4997451,0 7.4060281,46.4994985,0 7.406692,46.499195,0 7.4082666,46.4986638,0 7.4103534,46.4975065,0 7.4132181,46.495894,0 7.4147413,46.4949366,0 7.4158357,46.4942866,0 7.4175094,46.4931639,0 7.4190972,46.4920117,0 7.4198912,46.4915094,0 7.4200628,46.4913764,0 7.420213,46.4910662,0 7.4206636,46.4907264,0 7.4214576,46.4903571,0 7.4219511,46.4900912,0 7.4222944,46.4897809,0 7.4230025,46.4897514,0 7.4239037,46.4894116,0 7.4249123,46.4888355,0 7.4254272,46.4882593,0 7.4258779,46.4874467,0 7.426071,46.4870331,0 7.4261353,46.485925,0 7.4260066,46.4852306,0 7.4258564,46.4843146,0 7.4257277,46.483561,0 7.4256418,46.4830439,0 7.425556,46.4822756,0 7.4257277,46.4817141,0 7.4258993,46.4813891,0 7.4261139,46.4812709,0 7.4263714,46.4811675,0 7.4265645,46.4809458,0 7.4266932,46.480606,0 7.4266932,46.4802514,0 7.426543,46.4798229,0 7.4264787,46.4794387,0 7.426822,46.4788476,0 7.4272511,46.47836,0 7.4274872,46.4778576,0 7.4276803,46.4774587,0 7.4279378,46.4770301,0 7.4280236,46.4764982,0 7.4280486,46.4762655,0 7.4280665,46.4760992,0 7.4282269,46.4757891,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5590626,46.4870252,0 7.5596203,46.4873634,0 7.5598972,46.487543,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.42196,46.4940534,0 7.4214143,46.4939178,0 7.4213342,46.4938979,0 7.4209566,46.4938507,0 7.4206648,46.4939098,0 7.4203558,46.494022,0 7.4195919,46.4942879,0 7.4191112,46.4943588,0 7.4184675,46.4944416,0 7.4180469,46.4945775,0 7.4175132,46.4950208,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4262468,46.4595065,0 7.4262361,46.4592922,0 7.4262361,46.4590705,0 7.4262468,46.4587453,0 7.4261824,46.4585088,0 7.4257425,46.4580062,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4528183,46.5206213,0 7.4518921,46.5203291,0 7.4515129,46.5200991,0 7.4509055,46.5198462,0 7.4505617,46.5197602,0 7.4499281,46.5196986,0 7.4496551,46.5195907,0 7.4494311,46.5194705,0 7.4490583,46.5193646,0 7.4486957,46.5192179,0 7.4482353,46.5189694,0 7.4478544,46.5188064,0 7.4475591,46.5187391,0 7.4471488,46.5186023,0 7.4469498,46.5184034,0 7.4465396,46.5182355,0 7.4459925,46.5180117,0 7.4456879,46.5179496,0 7.4451533,46.5175952,0 7.444426,46.5172906,0 7.4441524,46.5170979,0 7.4437608,46.5165882,0 7.443077,46.5160473,0 7.4429775,46.5158546,0 7.4426046,46.5155873,0 7.4424367,46.51532,0 7.4417405,46.5148414,0 7.4415602,46.5144995,0 7.4413488,46.5142321,0 7.4411126,46.513424,0 7.4407645,46.513051,0 7.4405096,46.5126905,0 7.440437,46.5125172,0 7.4399804,46.5119635,0 7.4394754,46.5115556,0 7.439116,46.5109922,0 7.4387663,46.5104289,0 7.4387469,46.5101957,0 7.4386789,46.5098266,0 7.4385332,46.5094964,0 7.4384361,46.5092438,0 7.4386206,46.5089039,0 7.4385138,46.5086416,0 7.4384069,46.5082725,0 7.438572,46.507952,0 7.4387954,46.5077286,0 7.438504,46.5076023,0 7.4381641,46.5073109,0 7.4378824,46.506835,0 7.4375813,46.5061259,0 7.4372316,46.5051255,0 7.4373967,46.5046204,0 7.4378533,46.504057,0 7.4381641,46.5035228,0 7.4382224,46.5033285,0 7.4385915,46.5029011,0 7.4395434,46.5021144,0 7.4396599,46.5018327,0 7.4397085,46.5013179,0 7.4400193,46.5009779,0 7.4410198,46.5002689,0 7.4410459,46.5001952,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hörnliweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5634771,46.498246,0 7.563543,46.498316,0 7.5638087,46.4985406,0 7.5638266,46.4989595,0 7.5643825,46.4994173,0 7.5647636,46.4997905,0 7.5650245,46.5000946,0 7.5652805,46.5002911,0 7.565369,46.500541,0 7.5654359,46.5006704,0 7.5654456,46.5007267,0 7.565494,46.5008679,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5704442,46.5055077,0 7.5702808,46.5053663,0 7.5701857,46.5052743,0 7.5700595,46.505126,0 7.5699587,46.5049793,0 7.5697775,46.5047638,0 7.5696764,46.5046204,0 7.5696176,46.5045153,0 7.569522,46.504287,0 7.5693937,46.504028,0 7.5693182,46.5038638,0 7.5692352,46.5036487,0 7.5691654,46.5034532,0 7.5690593,46.5033257,0 7.5688693,46.5031471,0 7.568744,46.5029817,0 7.5687151,46.5029089,0 7.5686907,46.5028223,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4293897,46.4761015,0 7.4294959,46.4760031,0 7.4295844,46.47592,0 7.4297078,46.4758018,0 7.429925,46.4756596,0 7.4300725,46.4755229,0 7.4302362,46.475316,0 7.4303756,46.4750833,0 7.4304722,46.4749115,0 7.4305178,46.4747323,0 7.4306707,46.4745217,0 7.4308584,46.4742853,0 7.4311427,46.4740562,0 7.431352,46.4738216,0 7.4314887,46.4735446,0 7.431588,46.4733063,0 7.431706,46.4730477,0 7.4317516,46.4728703,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4268244,46.4763947,0 7.4269585,46.4764612,0 7.4271248,46.4764871,0 7.4273876,46.4764354,0 7.4276022,46.4763578,0 7.4277363,46.4762802,0 7.4278812,46.4762507,0 7.4280486,46.4762655,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4340985,46.4707201,0 7.434238,46.4705945,0 7.4344365,46.4703802,0 7.4346457,46.4702213,0 7.4350534,46.4703728,0 7.4353769,46.4704585,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4353769,46.4704585,0 7.435482,46.4704761,0 7.4355162,46.4704903,0 7.4356185,46.4705018,0 7.4364604,46.4704052,0 7.4370811,46.4707552,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5318629,46.4633692,0 7.5317263,46.4629743,0 7.5313508,46.4623387,0 7.5311041,46.4613854,0 7.5310075,46.4608607,0 7.5306964,46.4603581,0 7.5305891,46.4600477,0 7.5305462,46.4596042,0 7.5303852,46.4589982,0 7.5299883,46.4585769,0 7.5287008,46.4575126,0 7.5283682,46.4569361,0 7.5281322,46.4561453,0 7.5281429,46.4557461,0 7.5282287,46.4554727,0 7.5285399,46.4551622,0 7.5292158,46.4544453,0 7.5298273,46.4542235,0 7.530396,46.4541053,0 7.530632,46.4540535,0 7.5307286,46.4539279,0 7.5307071,46.4537948,0 7.5306427,46.4537061,0 7.5304389,46.4537357,0 7.5303101,46.4538096,0 7.5301814,46.4537357,0 7.5301063,46.4535435,0 7.5301385,46.4533218,0 7.5303423,46.4529891,0 7.5306642,46.4528857,0 7.5310719,46.4528931,0 7.5316191,46.4528487,0 7.5320053,46.4529004,0 7.532413,46.4530113,0 7.5328314,46.4530852,0 7.5330996,46.4530409,0 7.5331318,46.4528931,0 7.5329602,46.4526639,0 7.5328207,46.4524274,0 7.5326383,46.4522352,0 7.5324614,46.4521053,0 7.5322521,46.4520652,0 7.532016,46.4518508,0 7.5318658,46.4516512,0 7.531544,46.4512817,0 7.531029,46.4509564,0 7.5306213,46.4507494,0 7.5297415,46.450269,0 7.5292051,46.4499881,0 7.5288319,46.4496296,0 7.5282717,46.4492858,0 7.5278318,46.4490788,0 7.5274455,46.4489384,0 7.5270724,46.4487277,0 7.526555,46.4485983,0 7.5261903,46.4484875,0 7.5257504,46.4481844,0 7.5251519,46.4476484,0 7.5248921,46.447423,0 7.5246775,46.4471938,0 7.5244844,46.4471568,0 7.5242483,46.4472677,0 7.5241303,46.4473343,0 7.5239265,46.4473416,0 7.5236046,46.4472899,0 7.5233042,46.4472382,0 7.5229716,46.4472455,0 7.5226712,46.4472899,0 7.5224674,46.447386,0 7.5219416,46.4476152,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5388594,46.4323326,0 7.5392281,46.4322406,0 7.5397646,46.4321519,0 7.540065,46.4321667,0 7.54058,46.4322406,0 7.5410091,46.4322998,0 7.5412666,46.4325068,0 7.5416314,46.4326695,0 7.5418889,46.432773,0 7.5421035,46.43298,0 7.5425326,46.4332462,0 7.5428974,46.4333645,0 7.5433695,46.4334385,0 7.5435626,46.4335568,0 7.5439917,46.4337934,0 7.5445067,46.434104,0 7.5450861,46.4343406,0 7.5456225,46.4346364,0 7.5460088,46.4347842,0 7.5463306,46.4348434,0 7.5465452,46.4349173,0 7.5468671,46.4350356,0 7.5471889,46.4350208,0 7.5475323,46.4350356,0 7.5481116,46.4351539,0 7.5486051,46.4353314,0 7.5489914,46.4354793,0 7.5493562,46.4356567,0 7.5496351,46.435775,0 7.5499784,46.4358342,0 7.5503003,46.4357455,0 7.5506222,46.4355532,0 7.5507938,46.435361,0 7.5510942,46.435287,0 7.5513732,46.4353314,0 7.5516521,46.4355384,0 7.5519955,46.4357011,0 7.5523817,46.435775,0 7.5533044,46.4360264,0 7.5539696,46.4361891,0 7.5545275,46.4362335,0 7.5549352,46.436337,0 7.5550639,46.4365144,0 7.5549827,46.4367111,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gemmistrasse</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6237311,46.3837753,0 7.6234774,46.3838704,0 7.622544,46.3843698,0 7.6219235,46.3846108,0 7.6214367,46.3853915,0 7.6206176,46.3859502,0 7.6201284,46.3864565,0 7.6198804,46.3866291,0 7.6195346,46.3874896,0 7.6197172,46.3877987,0 7.6196075,46.3879736,0 7.6189509,46.3878995,0 7.6184904,46.3879967,0 7.6186038,46.3882094,0 7.6188989,46.3883671,0 7.6192004,46.3885456,0 7.619828,46.3888194,0 7.6202518,46.3889822,0 7.6205629,46.3889933,0 7.6210833,46.3890969,0 7.6215929,46.3890747,0 7.6224029,46.3887047,0 7.6229286,46.3884716,0 7.6235295,46.3881904,0 7.6238352,46.3880831,0 7.6242107,46.3883125,0 7.62461,46.3883714,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bütscheggenweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5645004,46.5024523,0 7.5643032,46.5024079,0 7.5642002,46.5024009,0 7.5639082,46.5024017,0 7.563623,46.502347,0 7.5633927,46.5022879,0 7.5632576,46.5022067,0 7.563109,46.5020934,0 7.563005,46.50203,0 7.562912,46.501915,0 7.5628685,46.5017841,0 7.562789,46.5017139,0 7.5625977,46.5016474,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bütscheggenweg</name>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5658571,46.5029369,0 7.565378,46.502792,0 7.5652788,46.5027476,0 7.5650875,46.5026587,0 7.5648487,46.5025808,0 7.5645004,46.5024523,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5883223,46.5173041,0 7.5883922,46.5173254,0 7.588884,46.51737,0 7.589137,46.517445,0 7.589223,46.517428,0 7.5892426,46.5172029,0 7.589195,46.517181,0 7.589143,46.517165,0 7.588956,46.517047,0 7.588803,46.516895,0 7.588737,46.516809,0 7.588709,46.516743,0 7.5886797,46.5165501,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5879633,46.5171897,0 7.5882182,46.5172418,0 7.5883223,46.5173041,0 7.5884541,46.5174144,0 7.588621,46.517648,0 7.588713,46.51781,0 7.588762,46.517866,0 7.58888,46.517958,0 7.588934,46.518028,0 7.58896,46.518155,0 7.589031,46.518301,0 7.589079,46.518323,0 7.5892056,46.5183555,0 7.589288,46.518439,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5826523,46.4989336,0 7.5825898,46.4990355,0 7.5826343,46.4991879,0 7.5826984,46.499309,0 7.582719,46.499403,0 7.5828014,46.4995759,0 7.582807,46.499739,0 7.5828446,46.4998144,0 7.5829721,46.4999336,0 7.58306,46.5000875,0 7.583046,46.500152,0 7.582917,46.500426,0 7.5826608,46.5005706,0 7.5823436,46.5007509,0 7.5822184,46.50073,0 7.5820124,46.5005653,0 7.5818994,46.5004572,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.578672,46.5205961,0 7.5787651,46.520547,0 7.5788446,46.5204993,0 7.578937,46.520553,0 7.5793743,46.5206724,0 7.579642,46.520806,0 7.579854,46.520889,0 7.580014,46.520939,0 7.580248,46.520982,0 7.580479,46.520835,0 7.580655,46.520778,0 7.581053,46.520755,0 7.581774,46.520671,0 7.581938,46.520619,0 7.582083,46.520557,0 7.582782,46.520178,0 7.5828672,46.5200806,0 7.582853,46.519966,0 7.582691,46.5197745,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5785017,46.5014825,0 7.5782187,46.5013039,0 7.5773506,46.5011965,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5543278,46.5106844,0 7.552418,46.510272,0 7.552234,46.510298,0 7.5518293,46.510394,0 7.551497,46.510395,0 7.5511102,46.5103357,0 7.5504234,46.5103985,0 7.5498621,46.5103121,0 7.5489084,46.5104103,0 7.547537,46.510344,0 7.547371,46.510243,0 7.5472993,46.5100845,0 7.547166,46.509991,0 7.5470386,46.5099552,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5665772,46.5111305,0 7.5661335,46.5110512,0 7.565676,46.511024,0 7.564624,46.510737,0 7.5637674,46.5108027,0 7.5627058,46.5109232,0 7.5620088,46.5104446,0 7.5618541,46.5104123,0 7.5611605,46.5106807,0 7.5608878,46.5107049,0 7.5601998,46.5106693,0 7.5595979,46.5109139,0 7.5590547,46.5110383,0 7.558377,46.511309,0 7.5576766,46.5114214,0 7.557316,46.511429,0 7.5558858,46.5107802,0 7.5548832,46.5106228,0 7.5543278,46.5106844,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5572602,46.5039626,0 7.5565998,46.5037653,0 7.556337,46.503737,0 7.5561181,46.5036637,0 7.555636,46.503467,0 7.5542456,46.5027933,0 7.553992,46.502666,0 7.55387,46.502572,0 7.553751,46.5024,0 7.553569,46.502294,0 7.552955,46.502105,0 7.552762,46.501995,0 7.552663,46.50191,0 7.552167,46.501397,0 7.551822,46.501019,0 7.551661,46.500913,0 7.551534,46.500853,0 7.551234,46.500736,0 7.550892,46.500652,0 7.550273,46.500621,0 7.549463,46.500519,0 7.548911,46.500354,0 7.548753,46.500326,0 7.548575,46.500277,0 7.548373,46.500246,0 7.547924,46.500091,0 7.547575,46.499861,0 7.5472465,46.4995287,0 7.546982,46.499337,0 7.5461074,46.4989712,0 7.5459336,46.498931,0 7.5457608,46.498928,0 7.545644,46.4989186,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5470386,46.5099552,0 7.546909,46.50992,0 7.546808,46.509814,0 7.546857,46.509759,0 7.547272,46.509792,0 7.547587,46.509838,0 7.547864,46.509808,0 7.5484138,46.5095411,0 7.548895,46.509268,0 7.5490654,46.5092289,0 7.549337,46.509187,0 7.549454,46.509095,0 7.549336,46.509014,0 7.548898,46.509065,0 7.5486376,46.5090759,0 7.5473181,46.5086821,0 7.5471077,46.5085813,0 7.546657,46.508168,0 7.546354,46.508088,0 7.5457686,46.5080083,0 7.545412,46.507947,0 7.5453714,46.5078755,0 7.545397,46.507821,0 7.545586,46.507789,0 7.545796,46.507852,0 7.5459958,46.5078633,0 7.5462241,46.5078289,0 7.5471737,46.5073725,0 7.547784,46.507391,0 7.5479861,46.5073411,0 7.5484178,46.5072077,0 7.5490615,46.507294,0 7.5496816,46.5071724,0 7.55049,46.507205,0 7.5510042,46.5071606,0 7.5518441,46.5068113,0 7.5523092,46.5067029,0 7.5526955,46.5066625,0 7.5531485,46.5066932,0 7.553226,46.506592,0 7.5530968,46.5065076,0 7.552063,46.506265,0 7.5516405,46.5060492,0 7.551345,46.505969,0 7.5511689,46.5059586,0 7.5508777,46.5059993,0 7.550618,46.5059837,0 7.5502813,46.5058175,0 7.5500701,46.5055161,0 7.550038,46.505387,0 7.550148,46.505321,0 7.550443,46.505316,0 7.5508943,46.5054926,0 7.551208,46.5055229,0 7.5522791,46.5054416,0 7.5525466,46.5054651,0 7.5526722,46.505418,0 7.5527,46.505346,0 7.552515,46.505299,0 7.552095,46.505311,0 7.5511044,46.5049005,0 7.5509361,46.5047297,0 7.5506706,46.5043701,0 7.5503646,46.5043215,0 7.549279,46.504364,0 7.5488652,46.504323,0 7.5486825,46.5042853,0 7.548669,46.5042092,0 7.5487161,46.5041464,0 7.5494069,46.5041857,0 7.550184,46.50395,0 7.5507692,46.5038245,0 7.5512873,46.5038139,0 7.5524549,46.5039821,0 7.5532009,46.5040121,0 7.5546574,46.5041122,0 7.5555299,46.5041995,0 7.5566327,46.5042191,0 7.557042,46.504217,0 7.5572457,46.5041657,0 7.557353,46.504123,0 7.5574117,46.5040674,0 7.557406,46.5040271,0 7.5572602,46.5039626,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6155446,46.4496036,0 7.6158266,46.4496374,0 7.6163109,46.4496955,0 7.6171772,46.4499824,0 7.61826,46.4503153,0 7.6194595,46.4502464,0 7.6204757,46.4507399,0 7.621342,46.4512335,0 7.6221583,46.4518188,0 7.6229818,46.4535272,0 7.6229158,46.4551282,0 7.6233595,46.4571271,0 7.6247231,46.458704,0 7.6254607,46.4588344,0 7.6255846,46.458493,0 7.6262756,46.458691,0 7.6262981,46.459034,0 7.627519,46.4599796,0 7.6276489,46.4609378,0 7.6285887,46.4615968,0 7.6286387,46.4621247,0 7.6291384,46.4623542,0 7.6290218,46.4626755,0 7.6293217,46.4633181,0 7.6295049,46.4646607,0 7.6298548,46.4655902,0 7.6306877,46.467013,0 7.6310876,46.4674031,0 7.6317373,46.4675179,0 7.6327368,46.4678736,0 7.6337197,46.4684702,0 7.6346693,46.4691128,0 7.6356855,46.4702257,0 7.6365351,46.4709715,0 7.6373014,46.4713042,0 7.6380844,46.4719926,0 7.6395504,46.4729908,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.617833,46.5283281,0 7.6175991,46.528688,0 7.6175883,46.5289759,0 7.6181999,46.529559,0 7.6185325,46.5300831,0 7.6184037,46.5301495,0 7.6178136,46.5297366,0 7.617009,46.5297288,0 7.6164189,46.5295295,0 7.6158824,46.5295295,0 7.6157644,46.5297288,0 7.6158932,46.5301274,0 7.6163545,46.5303713,0 7.6167086,46.5307105,0 7.6170658,46.5311194,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6109659,46.5266158,0 7.6117361,46.526354,0 7.6118901,46.5263694,0 7.6118131,46.5265542,0 7.6112585,46.5273552,0 7.6113048,46.5275554,0 7.6121982,46.5271857,0 7.6127681,46.5270625,0 7.6129553,46.5269559,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5189956,46.4843231,0 7.5190332,46.4840125,0 7.518926,46.4835988,0 7.518132,46.4833772,0 7.5167158,46.482934,0 7.5166944,46.4827123,0 7.5173166,46.482343,0 7.5171235,46.4822395,0 7.5169079,46.4822839,0 7.5161794,46.4823282,0 7.5153211,46.4821213,0 7.5151279,46.4820031,0 7.5148061,46.4812791,0 7.5132611,46.4802153,0 7.5121453,46.4796981,0 7.5121882,46.4795356,0 7.5129178,46.4798459,0 7.513304,46.479905,0 7.5146559,46.4803335,0 7.5159648,46.4805551,0 7.5169304,46.4809541,0 7.5174883,46.4808802,0 7.5181964,46.4809393,0 7.5186041,46.4813235,0 7.5194839,46.481486,0 7.5195482,46.4816337,0 7.5202778,46.4815303,0 7.5212402,46.481257,0 7.5217584,46.4814417,0 7.5225738,46.4816485,0 7.5231102,46.4815155,0 7.5235179,46.4811905,0 7.5247624,46.4810279,0 7.5264791,46.4803187,0 7.5276163,46.4792548,0 7.5279596,46.4786638,0 7.5284746,46.478383,0 7.5281742,46.4782501,0 7.5278953,46.478383,0 7.5264361,46.4787524,0 7.5253418,46.4787377,0 7.5242904,46.4786786,0 7.5232604,46.4786786,0 7.5222734,46.4785308,0 7.5212852,46.4786933,0 7.5198573,46.4781924,0 7.5196984,46.4778806,0 7.5187543,46.4780727,0 7.517896,46.4772305,0 7.5171021,46.476669,0 7.516351,46.4764621,0 7.5160292,46.4753686,0 7.5151923,46.4750886,0 7.5149348,46.4745854,0 7.5143555,46.4742307,0 7.5140551,46.4737578,0 7.5129393,46.4733293,0 7.5126818,46.4727382,0 7.512832,46.4725165,0 7.5127247,46.4723096,0 7.5120166,46.4720141,0 7.5119522,46.4717037,0 7.5114158,46.471482,0 7.5114587,46.470551,0 7.5113085,46.470152,0 7.5127676,46.4702998,0 7.5131538,46.4700633,0 7.5143984,46.4702259,0 7.5149134,46.4701232,0 7.5149527,46.4698298,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.580325,46.5027273,0 7.580134,46.5028204,0 7.5799106,46.5029702,0 7.5795711,46.5032229,0 7.579505,46.50336,0 7.5794962,46.5034404,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5887308,46.5121362,0 7.5878146,46.5121861,0 7.5875342,46.512005,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5486892,46.4843265,0 7.5482932,46.4846238,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5452866,46.4835281,0 7.5445192,46.4833872,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5439543,46.4836822,0 7.5442929,46.4838529,0 7.5444163,46.4839361,0 7.5444886,46.4839589,0 7.544905,46.4840167,0 7.5455029,46.4842106,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5482932,46.4846238,0 7.5469389,46.4843438,0 7.546616,46.484368,0 7.546398,46.4842227,0 7.5456648,46.4841873,0 7.5455029,46.4842106,0 7.5451848,46.4842565,0 7.5450642,46.484286,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Tracks</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.57139,46.4855698,0 7.5712639,46.4854836,0 7.571188,46.4854049,0 7.571083,46.4852752,0 7.5710397,46.4852471,0 7.5708579,46.4851424,0 7.5702202,46.4846207,0 7.5699875,46.4843914,0 7.5697506,46.4843808,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Winter_Hiking</name>
    +        <Style id="style-Winter_Hiking">
    +          <LineStyle>
    +            <color>#ff003f78</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Summer_Hiking</name>
    +        <Style id="style-Summer_Hiking">
    +          <LineStyle>
    +            <color>#ff003f78</color>
    +            <width>1</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Haselweg</name>
    +          <description>Haselweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5647157,46.4954991,0 7.5652338,46.4952461,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5654624,46.4590852,0 7.5657857,46.4590406,0 7.5660894,46.4588054,0 7.5661792,46.4586043,0 7.5661147,46.4582042,0 7.5662091,46.4580919,0 7.5662434,46.4579559,0 7.565883,46.4574237,0 7.5652732,46.457071,0 7.5649731,46.4568975,0 7.5644095,46.4567614,0 7.5638831,46.4564126,0 7.5632094,46.4558142,0 7.5631766,46.4555546,0 7.5628849,46.4551459,0 7.562303,46.4547165,0 7.5619564,46.454653,0 7.561394,46.4546978,0 7.5610892,46.4543912,0 7.5610362,46.4541518,0 7.5607503,46.4539705,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.569223,46.5076089,0 7.5689582,46.5076903,0 7.5679113,46.5074419,0 7.5678265,46.5072529,0 7.56764,46.5072284,0 7.5673403,46.507312,0 7.567078,46.50724,0 7.5666536,46.5072812,0 7.5655643,46.5070497,0 7.5654691,46.5069622,0 7.5645959,46.5067976,0 7.5646229,46.5066189,0 7.5643876,46.5063719,0 7.5638487,46.5060826,0 7.5636134,46.506044,0 7.5635323,46.5059295,0 7.5622206,46.5058099,0 7.5617203,46.5055797,0 7.5615608,46.5056736,0 7.5614142,46.5054717,0 7.561247,46.5055566,0 7.5606426,46.5053148,0 7.5602722,46.5052865,0 7.5589439,46.5043505,0 7.5585357,46.5043073,0 7.5582688,46.5040954,0 7.557406,46.5040271,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6155345,46.3998258,0 7.6153734,46.4003914,0 7.6157251,46.4016849,0 7.6155493,46.4025742,0 7.6149472,46.4029894,0 7.6144356,46.4033422,0 7.6127356,46.4038677,0 7.6115633,46.4035443,0 7.6120396,46.4045298,0 7.6123253,46.4051208,0 7.613146,46.4064546,0 7.613439,46.408031,0 7.61467,46.4095668,0 7.615139,46.4106177,0 7.6161251,46.4113811,0 7.6171756,46.4129423,0 7.6173782,46.4140618,0 7.6173696,46.4148133,0 7.6174554,46.4152985,0 7.6170005,46.415979,0 7.6168546,46.4165589,0 7.6165714,46.4166773,0 7.6161937,46.4166891,0 7.6160392,46.4169495,0 7.6164169,46.4174229,0 7.6171207,46.4176714,0 7.6173009,46.4179081,0 7.6173267,46.4184998,0 7.6176528,46.418908,0 7.6182752,46.418677,0 7.6185884,46.4183755,0 7.6188287,46.4183578,0 7.6193806,46.4173872,0 7.6199672,46.4174714,0 7.6210491,46.4180023,0 7.6225795,46.4190323,0 7.6229658,46.4189554,0 7.6232576,46.4190796,0 7.6234722,46.4190027,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5666254,46.4971783,0 7.5668656,46.497203,0 7.5672608,46.4970805,0 7.5678102,46.4970381,0 7.5683727,46.4970519,0 7.5693504,46.49736,0 7.5695856,46.4975015,0 7.5697801,46.497493,0 7.5698535,46.4975297,0 7.5697316,46.4975853,0 7.5698707,46.4976373,0 7.5701022,46.4975877,0 7.5701367,46.4975929,0 7.5707109,46.4976791,0 7.5708665,46.4976169,0 7.5711207,46.497742,0 7.5713865,46.4977649,0 7.5715411,46.4976389,0 7.5719446,46.4978024,0 7.5720307,46.4977428,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5654624,46.4590852,0 7.5649892,46.4595141,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Ueschinengrat</name>
    +          <description>Ueschinengrat, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.613129,46.4372863,0 7.6146783,46.4377111,0 7.6156779,46.4377915,0 7.6171439,46.4379063,0 7.6186432,46.438067,0 7.6196094,46.4384918,0 7.6204424,46.438951,0 7.6213586,46.4392265,0 7.6214919,46.4394561,0 7.6218251,46.4394906,0 7.6218584,46.4397317,0 7.6221416,46.4400531,0 7.6221583,46.4404205,0 7.6225248,46.4405812,0 7.6225914,46.4408797,0 7.6228746,46.4409371,0 7.6231078,46.4411207,0 7.6232245,46.4413044,0 7.622858,46.4414996,0 7.6230246,46.4420276,0 7.6232578,46.442372,0 7.6237242,46.442659,0 7.6240241,46.4432215,0 7.624274,46.443761,0 7.6243739,46.4443579,0 7.6243739,46.4447712,0 7.6240907,46.4450237,0 7.6241907,46.4454599,0 7.6245239,46.4457354,0 7.6250903,46.4460913,0 7.6252569,46.4464586,0 7.6249737,46.4467341,0 7.6252902,46.4469292,0 7.6251403,46.4471702,0 7.6259566,46.4474687,0 7.6266396,46.4476868,0 7.6276058,46.4479393,0 7.6279557,46.4485247,0 7.6283555,46.4490068,0 7.6292717,46.4496036,0 7.630038,46.4502694,0 7.6311875,46.4509006,0 7.6315374,46.451222,0 7.6317206,46.4517041,0 7.632237,46.4521517,0 7.6324869,46.4523353,0 7.6323703,46.4527256,0 7.63297,46.4532994,0 7.6333699,46.4535519,0 7.6342695,46.4543094,0 7.6345527,46.4547341,0 7.6349525,46.4553997,0 7.6355855,46.4559736,0 7.6366517,46.4571671,0 7.6377512,46.4582,0 7.638301,46.4591984,0 7.638884,46.4598181,0 7.6388674,46.4601624,0 7.638884,46.4603575,0 7.6391173,46.4604263,0 7.6394005,46.4605067,0 7.6398503,46.4611263,0 7.6401834,46.4615395,0 7.6410164,46.461746,0 7.6423158,46.462205,0 7.6431321,46.4625722,0 7.6439484,46.4633985,0 7.6445315,46.4637312,0 7.644948,46.4642017,0 7.6453644,46.4646722,0 7.6460475,46.4651541,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.613129,46.4372863,0 7.614645,46.4371601,0 7.6147949,46.436896,0 7.6150948,46.4367812,0 7.6165941,46.4367812,0 7.6169939,46.4366434,0 7.6178935,46.4359661,0 7.619193,46.4364368,0 7.6179269,46.4347261,0 7.6185766,46.4346802,0 7.6184933,46.4342439,0 7.6185932,46.4339224,0 7.6188098,46.4336469,0 7.619193,46.4339683,0 7.6194095,46.4337961,0 7.619876,46.434152,0 7.6199926,46.4339798,0 7.620559,46.4341865,0 7.6205257,46.4338191,0 7.620659,46.4335206,0 7.6207756,46.4333484,0 7.6211754,46.4332335,0 7.6210588,46.432958,0 7.6217751,46.4331417,0 7.6231578,46.4331761,0 7.6237242,46.4335435,0 7.6239741,46.4325446,0 7.6243047,46.4319653,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.613129,46.4372863,0 7.6114798,46.4367468,0 7.6103046,46.4371303,0 7.6100952,46.437372,0 7.6096793,46.4373191,0 7.609981,46.4374867,0 7.6092641,46.4375619,0 7.609614,46.4376767,0 7.6091475,46.4379407,0 7.6094474,46.4381244,0 7.6091142,46.4382048,0 7.6091308,46.4384114,0 7.6087977,46.4385492,0 7.6086644,46.4388706,0 7.6091808,46.4387214,0 7.6107634,46.4388132,0 7.6111799,46.4389166,0 7.6113632,46.4392265,0 7.6116963,46.4392839,0 7.6114131,46.4396628,0 7.6116797,46.4399268,0 7.6115797,46.4404319,0 7.6118796,46.441224,0 7.6118463,46.4416144,0 7.6116297,46.4417636,0 7.6115631,46.4420621,0 7.61093,46.4418669,0 7.61098,46.4420735,0 7.610147,46.4419587,0 7.6098305,46.4422802,0 7.6098139,46.4426016,0 7.6108634,46.444025,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6108634,46.444025,0 7.6112632,46.4445875,0 7.6115964,46.4452533,0 7.6121961,46.4457584,0 7.6125293,46.4463323,0 7.6131124,46.4478704,0 7.6135455,46.4484099,0 7.6141619,46.4488116,0 7.6144784,46.4491675,0 7.6150781,46.4493396,0 7.6155446,46.4496036,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4787161,46.5654517,0 7.4789409,46.56562,0 7.4788957,46.5659074,0 7.4785552,46.566147,0 7.4784732,46.566416,0 7.4785488,46.5665905,0 7.4795866,46.567193,0 7.4797515,46.5672887,0 7.4800482,46.5673099,0 7.4802419,46.5674885,0 7.4801802,46.5674932,0 7.4801654,46.5675985,0 7.4803315,46.5678527,0 7.4805001,46.5679469,0 7.4808497,46.568056,0 7.4811398,46.5680585,0 7.4815241,46.5681539,0 7.4816035,46.5684949,0 7.4818155,46.5686213,0 7.4821328,46.5687589,0 7.4821799,46.5687366,0 7.482268,46.5689251,0 7.4824725,46.569209,0 7.4825891,46.5692747,0 7.4827415,46.5698549,0 7.4827143,46.5698945,0 7.4827948,46.5699664,0 7.4828345,46.5699627,0 7.4831308,46.5704896,0 7.4831135,46.5707772,0 7.4833007,46.5711876,0 7.4832126,46.5711901,0 7.483256,46.5713463,0 7.4830366,46.571619,0 7.4831097,46.5718632,0 7.4832201,46.5720294,0 7.4832994,46.5726753,0 7.4834246,46.5730509,0 7.4833007,46.5733249,0 7.483473,46.5735716,0 7.4839689,46.5737985,0 7.4840594,46.5739559,0 7.4843152,46.5741399,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.569223,46.5076089,0 7.5694207,46.5076825,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4682319,46.4426424,0 7.468149,46.4425956,0 7.4681436,46.4424662,0 7.467957,46.442374,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4393848,46.4651486,0 7.4393956,46.4653186,0 7.4393366,46.4654848,0 7.4392534,46.465595,0 7.4387103,46.4661122,0 7.4372103,46.4679225,0 7.4370442,46.4681471,0 7.4360816,46.4694487,0 7.4353769,46.4704585,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simmendamm</name>
    +          <description>Simmendamm, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4440412,46.4544542,0 7.4442289,46.4544062,0 7.4523989,46.4496459,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4677582,46.4299748,0 7.4671079,46.4296049,0 7.4655576,46.4299747,0 7.4594593,46.4346501,0 7.4575621,46.4367506,0 7.4565457,46.4372249,0 7.4560037,46.438038,0 7.454784,46.438309,0 7.4537676,46.4392576,0 7.4535952,46.4392812,0 7.4532036,46.4391925,0 7.4529461,46.4390077,0 7.4525438,46.4389005,0 7.4516104,46.4388968,0 7.4507038,46.4388413,0 7.4504133,46.4388159,0 7.4502317,46.4387637,0 7.450119,46.4387267,0 7.4499634,46.4388549,0 7.449765,46.4390631,0 7.4495343,46.439259,0 7.4490998,46.4394476,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6092641,46.4375619,0 7.6088204,46.4375169,0 7.607629,46.437716,0 7.6078456,46.4379897,0 7.6053183,46.4381887,0 7.6040186,46.4382136,0 7.6040186,46.4378901,0 7.6036864,46.4378485,0 7.6032243,46.4377906,0 7.6029354,46.437293,0 7.6032965,46.4360489,0 7.6032604,46.4355015,0 7.6030437,46.4349292,0 7.6026466,46.4345809,0 7.602105,46.4335109,0 7.6010219,46.4318935,0 7.5998305,46.430948,0 7.5993611,46.4304005,0 7.5989279,46.428783,0 7.5988557,46.428136,0 7.5986391,46.427713,0 7.5978087,46.4267175,0 7.5975198,46.4261203,0 7.5974476,46.4256226,0 7.5971588,46.4252741,0 7.5969061,46.4242538,0 7.5961118,46.4229597,0 7.5956063,46.4231588,0 7.5952092,46.4234325,0 7.5947398,46.4247764,0 7.5943427,46.4250004,0 7.594451,46.426394,0 7.5941261,46.4271406,0 7.5937289,46.4276881,0 7.5925014,46.4288577,0 7.5915627,46.430077,0 7.5914183,46.4305249,0 7.5915266,46.4314208,0 7.5912459,46.4320011,0 7.5912016,46.4320926,0 7.5911503,46.4321334,0 7.5910484,46.4321962,0 7.5908982,46.4322258,0 7.5908285,46.4322665,0 7.5907266,46.4322776,0 7.5906407,46.4323293,0 7.5905495,46.4323404,0 7.5905388,46.4323959,0 7.5905989,46.4324636,0 7.5906139,46.4325216,0 7.5905817,46.4326288,0 7.5906139,46.4327028,0 7.590571,46.4327471,0 7.590512,46.4327323,0 7.5905012,46.4326732,0 7.5904154,46.4326547,0 7.5903886,46.4325992,0 7.5903296,46.4325992,0 7.5903189,46.4326695,0 7.5902759,46.4327249,0 7.5902652,46.4328063,0 7.5901526,46.4329763,0 7.5900505,46.4331525,0 7.5900506,46.4332241,0 7.5899969,46.4332504,0 7.5899648,46.4333091,0 7.5899112,46.4333498,0 7.5898736,46.4333017,0 7.5897985,46.4332795,0 7.5897395,46.4332388,0 7.5897073,46.4331649,0 7.5896322,46.433176,0 7.5895858,46.4332561,0 7.5896161,46.4333609,0 7.5895893,46.4334459,0 7.5894628,46.4335199,0 7.5894069,46.433383,0 7.5893318,46.4333128,0 7.5892084,46.4332795,0 7.5891387,46.4332721,0 7.5891212,46.4333221,0 7.5890529,46.4333793,0 7.5890046,46.4333941,0 7.5889724,46.4333645,0 7.5888812,46.4332093,0 7.5887685,46.4331242,0 7.5886237,46.4330836,0 7.5885384,46.4330396,0 7.5884896,46.4329985,0 7.5883662,46.4329431,0 7.5881892,46.4329394,0 7.5880336,46.432932,0 7.5879102,46.432895,0 7.5878244,46.4328913,0 7.5877171,46.4329061,0 7.587642,46.4328654,0 7.5875347,46.4328765,0 7.5874489,46.4329394,0 7.5874543,46.4330614,0 7.5875455,46.4331908,0 7.5875347,46.4332869,0 7.5875783,46.4333968,0 7.587642,46.4334496,0 7.5876796,46.4335309,0 7.5877815,46.4335383,0 7.5878878,46.4335835,0 7.5879531,46.433627,0 7.58798,46.4337232,0 7.5879811,46.4338264,0 7.5880229,46.4339006,0 7.5880014,46.43403,0 7.5879707,46.4341598,0 7.5879853,46.4342408,0 7.5880551,46.4343221,0 7.5881758,46.4343754,0 7.5882053,46.4344478,0 7.5881731,46.4345254,0 7.5880898,46.4345803,0 7.5880551,46.4346585,0 7.5879531,46.4347251,0 7.5878304,46.4347381,0 7.5877547,46.4347214,0 7.5876527,46.4347325,0 7.5875562,46.4347694,0 7.5874543,46.4347879,0 7.5873416,46.4348693,0 7.5873187,46.4349658,0 7.5872152,46.4350689,0 7.5871002,46.4351835,0 7.5870787,46.4352501,0 7.5869822,46.4353018,0 7.586832,46.4352981,0 7.5866818,46.4352538,0 7.586655,46.4351687,0 7.5865477,46.4350911,0 7.5863103,46.4350611,0 7.5861507,46.4350837,0 7.5859415,46.4350874,0 7.5856894,46.4351465,0 7.5855445,46.4352501,0 7.5854855,46.4353536,0 7.5854508,46.4354567,0 7.5854372,46.4356013,0 7.5853997,46.4357565,0 7.5852945,46.4359217,0 7.585169,46.4360116,0 7.5850134,46.4361189,0 7.5848418,46.4361336,0 7.5846755,46.4361299,0 7.5845038,46.4361558,0 7.5842285,46.4362702,0 7.5841122,46.4364516,0 7.5840908,46.4365921,0 7.5840532,46.4367067,0 7.5839567,46.436751,0 7.5838816,46.4367584,0 7.5838185,46.4367223,0 7.5837796,46.4366549,0 7.5836616,46.4366105,0 7.5835704,46.4365588,0 7.5834417,46.4365588,0 7.5833265,46.4365998,0 7.5832164,46.4366697,0 7.5830258,46.4367223,0 7.582916,46.4366993,0 7.5828623,46.4366586,0 7.582755,46.4366771,0 7.5827336,46.4367251,0 7.5827228,46.4367954,0 7.5826531,46.4368508,0 7.5825741,46.4368657,0 7.5824707,46.4368545,0 7.5823098,46.4368804,0 7.582122,46.436947,0 7.5819504,46.436995,0 7.5818699,46.4370024,0 7.5818002,46.436995,0 7.5817519,46.4369543,0 7.5817358,46.43691,0 7.5816768,46.4368915,0 7.58165,46.4368435,0 7.5816392,46.4367806,0 7.581607,46.4367362,0 7.5815909,46.4366734,0 7.5815427,46.4366475,0 7.5814461,46.4366475,0 7.5813388,46.4366771,0 7.5811725,46.436751,0 7.5810062,46.4367621,0 7.5808556,46.4367572,0 7.5806522,46.4367954,0 7.5804912,46.4367769,0 7.5803472,46.4367976,0 7.5802171,46.4368459,0 7.5802016,46.4369137,0 7.5801211,46.4369359,0 7.580046,46.4369211,0 7.5799333,46.4369322,0 7.5799119,46.4369765,0 7.5798421,46.4370061,0 7.5797779,46.4370077,0 7.5797188,46.4370357,0 7.5796383,46.4370579,0 7.5795526,46.4370554,0 7.5794988,46.4370357,0 7.5794237,46.4370135,0 7.5793379,46.4369876,0 7.5792908,46.4369913,0 7.5792252,46.4370135,0 7.5791609,46.4370431,0 7.5791072,46.4370579,0 7.5790268,46.4370616,0 7.5789356,46.4370357,0 7.5788122,46.4370579,0 7.5787049,46.4371429,0 7.5785547,46.437191,0 7.5784206,46.437239,0 7.5783294,46.437239,0 7.5782436,46.4372131,0 7.578147,46.4371836,0 7.5780451,46.437154,0 7.5779217,46.4371688,0 7.5777876,46.4371614,0 7.5776899,46.4371324,0 7.5775891,46.4371577,0 7.5774908,46.4372214,0 7.5773692,46.437239,0 7.5772726,46.437202,0 7.5771278,46.437276,0 7.5771009,46.4373351,0 7.5770366,46.437398,0 7.5770526,46.4374867,0 7.5771009,46.4375385,0 7.5771438,46.4376087,0 7.5771009,46.4376605,0 7.5770312,46.4376645,0 7.57694,46.4376198,0 7.5768756,46.4376531,0 7.5768273,46.4377122,0 7.576763,46.43769,0 7.5767201,46.4376568,0 7.5766181,46.4376457,0 7.576543,46.43769,0 7.5764518,46.4377159,0 7.5763445,46.4377048,0 7.5762212,46.4377233,0 7.5761943,46.4377714,0 7.5760871,46.4378083,0 7.576012,46.4378342,0 7.5760495,46.4378934,0 7.5761085,46.4379673,0 7.5761675,46.4379932,0 7.576189,46.438056,0 7.5762335,46.4381256,0 7.5762051,46.4381743,0 7.5759959,46.4382224,0 7.5757276,46.4382076,0 7.5754594,46.4382076,0 7.5753682,46.4381706,0 7.5752341,46.4381078,0 7.5751161,46.4381109,0 7.5750571,46.4381706,0 7.5750036,46.4381659,0 7.5748908,46.438141,0 7.5747835,46.4381189,0 7.5747406,46.4380819,0 7.5746816,46.438056,0 7.5746011,46.4380856,0 7.5745099,46.4380967,0 7.5744621,46.4380832,0 7.574349,46.4380819,0 7.5742941,46.4380642,0 7.5742095,46.4380043,0 7.5741344,46.4379303,0 7.5740003,46.4378601,0 7.5739037,46.4378194,0 7.5737428,46.4378083,0 7.5736095,46.437843,0 7.5735448,46.4379215,0 7.5734209,46.4379562,0 7.5733458,46.4380412,0 7.5733201,46.4381126,0 7.5732493,46.4381891,0 7.5732117,46.4382741,0 7.5731259,46.438422,0 7.5731152,46.4385847,0 7.5730776,46.4386438,0 7.572965,46.4387067,0 7.5728577,46.4387621,0 7.5727826,46.4388397,0 7.5727075,46.438995,0 7.5727348,46.4391616,0 7.5727665,46.439361,0 7.5726216,46.4395532,0 7.5725251,46.4396567,0 7.5725251,46.4397824,0 7.5725734,46.4399044,0 7.5726324,46.4399562,0 7.5726914,46.4400708,0 7.5726753,46.4401706,0 7.5725895,46.4402704,0 7.5723588,46.4404035,0 7.5716278,46.4407909,0 7.5706985,46.4416291,0 7.5701108,46.4423354,0 7.5693728,46.4429852,0 7.5688535,46.4437668,0 7.5675552,46.4450099,0 7.5667762,46.4458951,0 7.5659698,46.4470345,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4742902,46.543847,0 7.4740706,46.5437738,0 7.474096,46.5437005,0 7.4741839,46.5437162,0 7.4741585,46.5436234,0 7.4739037,46.5435112,0 7.473769,46.543193,0 7.4735787,46.5432623,0 7.4733698,46.5429187,0 7.4728954,46.5428416,0 7.4729051,46.5427908,0 7.4733112,46.542578,0 7.4735425,46.5427068,0 7.4735435,46.5426288,0 7.4732848,46.5422881,0 7.4731111,46.5423086,0 7.4730184,46.5422354,0 7.4730584,46.5420968,0 7.4730262,46.5419884,0 7.4727734,46.5419123,0 7.4729002,46.5417844,0 7.4724395,46.5414984,0 7.472902,46.5410832,0 7.4728875,46.5409579,0 7.4731061,46.5409579,0 7.4732465,46.5406833,0 7.4733172,46.5406909,0 7.4732599,46.5407253,0 7.4732991,46.5407434,0 7.4734232,46.5405983,0 7.4734576,46.5406871,0 7.4735388,46.5406947,0 7.4736859,46.5409144,0 7.4738822,46.5409576,0 7.4737586,46.5410479,0 7.473906,46.5410955,0 7.4739511,46.5410218,0 7.4740866,46.5410242,0 7.4741531,46.5411691,0 7.4743005,46.5411953,0 7.4743741,46.5412571,0 7.4742957,46.5411097,0 7.4743694,46.5409933,0 7.47431,46.5408602,0 7.4745595,46.5406986,0 7.4743504,46.5404181,0 7.4742957,46.5401187,0 7.4739061,46.5395675,0 7.4739061,46.5391722,0 7.4728853,46.5387885,0 7.4727226,46.5388327,0 7.4726063,46.538735,0 7.4720691,46.5387211,0 7.4719273,46.5385443,0 7.4718017,46.5385002,0 7.4718668,46.5384583,0 7.4718622,46.538356,0 7.471925,46.5382746,0 7.4721784,46.5382374,0 7.4720691,46.5374375,0 7.4722691,46.5372631,0 7.4722249,46.5371584,0 7.4723249,46.5371515,0 7.4720552,46.5368073,0 7.4719436,46.5365911,0 7.4717622,46.5365515,0 7.4717715,46.5366236,0 7.4716668,46.5365515,0 7.4715087,46.5365422,0 7.4714273,46.5365957,0 7.4711902,46.5365911,0 7.4712785,46.5362539,0 7.4714273,46.5360818,0 7.4713041,46.5360283,0 7.4712739,46.5359469,0 7.4711948,46.5358795,0 7.471125,46.5358865,0 7.4710599,46.5357516,0 7.4708669,46.5357167,0 7.4710948,46.5356353,0 7.4708367,46.535161,0 7.4710785,46.5351354,0 7.4711413,46.5348796,0 7.4709762,46.5347843,0 7.4711413,46.5346843,0 7.4710437,46.5345936,0 7.4714436,46.534368,0 7.4714041,46.5342797,0 7.4711832,46.5341936,0 7.4712739,46.5341448,0 7.4714785,46.534175,0 7.4714901,46.5340588,0 7.4715901,46.5339774,0 7.4714483,46.5339169,0 7.4715855,46.5338913,0 7.4715134,46.5337518,0 7.4716529,46.5335844,0 7.4715599,46.5334518,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4715599,46.5334518,0 7.4716933,46.5334754,0 7.4717591,46.5334871,0 7.4723134,46.5334289,0 7.4724659,46.5335346,0 7.4725436,46.5334476,0 7.472668,46.5334811,0 7.4727831,46.5334182,0 7.4727617,46.5334784,0 7.4729946,46.5335748,0 7.473016,46.5334463,0 7.473285,46.5334463,0 7.473562,46.5335266,0 7.4734933,46.5336569,0 7.4737722,46.5335744,0 7.4740695,46.533687,0 7.4742803,46.533611,0 7.4743445,46.5334447,0 7.4746862,46.5332667,0 7.4746312,46.5333989,0 7.4749193,46.5334225,0 7.4757731,46.5331973,0 7.4759525,46.5333426,0 7.4764802,46.5331855,0 7.4767827,46.5331868,0 7.4768233,46.5333151,0 7.4773418,46.533209,0 7.4774374,46.5330375,0 7.4775343,46.533027,0 7.4776076,46.5331331,0 7.4782663,46.5329118,0 7.4782532,46.532735,0 7.478481,46.5327075,0 7.478663,46.5327926,0 7.4789262,46.5326447,0 7.4791488,46.5326538,0 7.4791645,46.5325674,0 7.4797027,46.5324338,0 7.4804792,46.5328542,0 7.4806337,46.532828,0 7.480728,46.5329026,0 7.4809244,46.5328372,0 7.4809991,46.5327481,0 7.4812112,46.5327403,0 7.4812845,46.5326591,0 7.4813998,46.5326735,0 7.4815255,46.5327795,0 7.4816237,46.5327678,0 7.481642,46.5328673,0 7.4817546,46.532807,0 7.4818319,46.532976,0 7.4823072,46.5327795,0 7.4826136,46.5327953,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4826136,46.5327953,0 7.4832671,46.5330604,0 7.483638,46.5331012,0 7.4836392,46.5332404,0 7.4837808,46.5331216,0 7.4844133,46.5333052,0 7.4846353,46.5331804,0 7.4847205,46.5333172,0 7.4850038,46.533196,0 7.485209,46.5334301,0 7.4854334,46.5332644,0 7.4856758,46.5334481,0 7.4858403,46.5332236,0 7.4860635,46.5335045,0 7.4864127,46.5332812,0 7.4865448,46.5334001,0 7.4867464,46.5333544,0 7.4866924,46.5331996,0 7.4861523,46.5329248,0 7.4859435,46.5324759,0 7.4860743,46.5324471,0 7.486058,46.5321473,0 7.4864378,46.5318491,0 7.486282,46.5312689,0 7.4864986,46.5312408,0 7.4864363,46.5309826,0 7.4866945,46.5306265,0 7.4874629,46.530166,0 7.4876105,46.5306214,0 7.4874752,46.5308091,0 7.4880044,46.5309844,0 7.4879828,46.5311506,0 7.4884043,46.5312983,0 7.4889058,46.5313136,0 7.4890597,46.5315536,0 7.4894966,46.5316213,0 7.4894966,46.5317628,0 7.4901334,46.5321536,0 7.4907426,46.5323505,0 7.4913734,46.5323105,0 7.4918656,46.5323874,0 7.4920133,46.5319413,0 7.4923364,46.5318582,0 7.4925056,46.5319905,0 7.4940365,46.5316302,0 7.4942096,46.531841,0 7.494616,46.5317695,0 7.4954627,46.5322662,0 7.4954213,46.5326011,0 7.4961739,46.5326726,0 7.4965389,46.5330865,0 7.4963094,46.5331091,0 7.4962191,46.5338617,0 7.4959858,46.5339068,0 7.496046,46.5344826,0 7.4957449,46.5346632,0 7.495775,46.5352728,0 7.4950262,46.5360781,0 7.4937806,46.5359652,0 7.4934006,46.5361985,0 7.4927044,46.5360856,0 7.4926442,46.5362286,0 7.4925012,46.5361496,0 7.4922228,46.5363114,0 7.4921437,46.5362361,0 7.4919029,46.5364506,0 7.4918126,46.536413,0 7.4911804,46.5366802,0 7.4903827,46.5375419,0 7.4906687,46.5376886,0 7.490759,46.5379107,0 7.4911127,46.5378956,0 7.4915755,46.5381251,0 7.491775,46.5383472,0 7.4939236,46.5380913,0 7.4945671,46.5382079,0 7.4950563,46.5380348,0 7.4952181,46.5376886,0 7.4955605,46.5376096,0 7.4956885,46.5379784,0 7.4964938,46.5377225,0 7.4968926,46.5378994,0 7.4970545,46.5376736,0 7.4982774,46.5377902,0 7.4983038,46.5377075,0 7.4981231,46.5376661,0 7.4985446,46.5375456,0 7.4988005,46.5376284,0 7.498867,46.5378034,0 7.4990317,46.5377973,0 7.4990378,46.5379102,0 7.4997,46.5380597,0 7.4996664,46.5385602,0 7.4992514,46.5388867,0 7.499343,46.5390179,0 7.4997183,46.5389294,0 7.4996664,46.539024,0 7.4998464,46.5390728,0 7.5001241,46.5388287,0 7.5001882,46.5385998,0 7.5000265,46.5384686,0 7.5003987,46.5381391,0 7.5006276,46.5381208,0 7.5007252,46.5379804,0 7.5006642,46.5379224,0 7.5008015,46.5379194,0 7.5009022,46.5381024,0 7.5012501,46.5379224,0 7.5012806,46.5377058,0 7.5019458,46.5372664,0 7.5023699,46.5375013,0 7.5021197,46.5386273,0 7.5025042,46.5385144,0 7.5024676,46.5388592,0 7.5028063,46.5393444,0 7.5028378,46.5395826,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4450569,46.4629393,0 7.4444602,46.463138,0 7.443613,46.463674,0 7.4426102,46.4637086,0 7.4421779,46.4636567,0 7.4419013,46.463795,0 7.4417802,46.4640717,0 7.4414823,46.4643468,0 7.4411283,46.4644539,0 7.4406961,46.4643705,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6229468,46.3732175,0 7.6226819,46.3729271,0 7.6224073,46.3727198,0 7.6219266,46.3722934,0 7.6215061,46.3719381,0 7.6212314,46.3716301,0 7.6210426,46.3715235,0 7.6209545,46.3713816,0 7.6209319,46.3712759,0 7.6208667,46.3711625,0 7.6206931,46.3710179,0 7.62051,46.3708845,0 7.620338,46.3707338,0 7.6202476,46.3705534,0 7.6201733,46.3702665,0 7.6200628,46.3699434,0 7.6199611,46.3697469,0 7.6197608,46.3695189,0 7.6195411,46.3693534,0 7.6193051,46.3691457,0 7.6190999,46.3688752,0 7.6188746,46.3687665,0 7.6186973,46.3685452,0 7.618483,46.3684123,0 7.6182273,46.368213,0 7.6181587,46.3680412,0 7.6181758,46.3679524,0 7.6181158,46.3678932,0 7.6178325,46.3677569,0 7.6177124,46.3676859,0 7.6172403,46.3675615,0 7.6170085,46.3673897,0 7.6165021,46.3669159,0 7.6163219,46.3668093,0 7.6162103,46.3666316,0 7.6155752,46.366294,0 7.6149829,46.3661993,0 7.6144079,46.3660512,0 7.6141847,46.3660157,0 7.6141847,46.365838,0 7.6141332,46.3658025,0 7.6140474,46.365761,0 7.6137985,46.3657492,0 7.6133779,46.3656781,0 7.6130517,46.3653879,0 7.612923,46.365299,0 7.6128372,46.3650503,0 7.6127084,46.3649259,0 7.6125024,46.3648489,0 7.6121591,46.3648074,0 7.611996,46.3646949,0 7.6117214,46.3646297,0 7.611627,46.3646179,0 7.6114553,46.3645054,0 7.611318,46.3644698,0 7.6111978,46.3644935,0 7.6111206,46.3643632,0 7.6110691,46.364304,0 7.6109489,46.3642803,0 7.6107686,46.3642566,0 7.6105627,46.3642033,0 7.6104511,46.3641263,0 7.6102279,46.3640434,0 7.6099361,46.3639841,0 7.6099018,46.3639486,0 7.6098588,46.3639841,0 7.6099189,46.3640848,0 7.6099618,46.3641796,0 7.6103223,46.3643336,0 7.6103824,46.3644402,0 7.6105627,46.3645409,0 7.6107772,46.3647956,0 7.6110176,46.3649377,0 7.6112493,46.3651569,0 7.6114038,46.3653049,0 7.6114381,46.3653642,0 7.6113437,46.365376,0 7.6111806,46.3652813,0 7.6108459,46.3652102,0 7.6106742,46.3651154,0 7.6103738,46.3650443,0 7.609876,46.3648903,0 7.609567,46.3647245,0 7.6094039,46.3646594,0 7.6093009,46.3645764,0 7.6091121,46.3645172,0 7.6088546,46.3644994,0 7.6085456,46.3644876,0 7.6083053,46.3644165,0 7.6081165,46.3643514,0 7.6080221,46.3642092,0 7.6078933,46.3640138,0 7.6077989,46.3639545,0 7.6076702,46.3638835,0 7.6074728,46.3638657,0 7.6073783,46.3638005,0 7.6072238,46.3636643,0 7.6069921,46.3634985,0 7.6068805,46.3633563,0 7.6067775,46.3632734,0 7.6066574,46.3631845,0 7.6066488,46.3630779,0 7.6066059,46.3630187,0 7.6064342,46.3628884,0 7.606357,46.3628114,0 7.606254,46.3627403,0 7.6061338,46.3627225,0 7.6059707,46.3626811,0 7.6057819,46.3626396,0 7.6056274,46.3626041,0 7.6055158,46.3625745,0 7.6053699,46.3624975,0 7.605224,46.3624382,0 7.6051639,46.3624027,0 7.6050094,46.3623731,0 7.6048807,46.3623908,0 7.6047691,46.3623198,0 7.6046747,46.3623375,0 7.6045717,46.362379,0 7.6044773,46.3624027,0 7.6042026,46.3623968,0 7.6039794,46.3623849,0 7.6038249,46.3624442,0 7.6036361,46.362456,0 7.6034645,46.3624264,0 7.603267,46.3623968,0 7.6031812,46.3624264,0 7.6031211,46.3625626,0 7.603001,46.3625863,0 7.6028465,46.3626159,0 7.6025547,46.3625448,0 7.6022199,46.3624086,0 7.6019624,46.3623257,0 7.6018079,46.3623198,0 7.6016792,46.362225,0 7.6016792,46.3621184,0 7.6017822,46.3620118,0 7.6018251,46.3619111,0 7.6018251,46.3618281,0 7.6021255,46.3617393,0 7.60228,46.361686,0 7.6024517,46.3616741,0 7.6026062,46.3616564,0 7.6027349,46.3616327,0 7.6029666,46.3615498,0 7.6031211,46.3615675,0 7.6032499,46.3614905,0 7.6032499,46.3614313,0 7.6031383,46.3612951,0 7.6030954,46.3612121,0 7.6029323,46.3613187,0 7.6029151,46.3614076,0 7.602795,46.3614668,0 7.6027521,46.3615142,0 7.602589,46.3614491,0 7.602486,46.3613602,0 7.6026147,46.3612536,0 7.6027521,46.3611351,0 7.6028207,46.3609515,0 7.6029838,46.3607501,0 7.6030267,46.3606968,0 7.6030096,46.3604895,0 7.6030611,46.3602585,0 7.6030954,46.3601282,0 7.6030696,46.3599149,0 7.6029924,46.3595714,0 7.6030525,46.359293,0 7.6031898,46.3591034,0 7.60331,46.3587954,0 7.6034387,46.3584637,0 7.6033958,46.3581853,0 7.6033443,46.3579187,0 7.6033529,46.3577588,0 7.60331,46.3576699,0 7.6030954,46.3576521,0 7.6028722,46.3576166,0 7.6026062,46.35751,0 7.6023487,46.3573263,0 7.601971,46.3572434,0 7.6016534,46.3570183,0 7.6014904,46.3568287,0 7.6013015,46.3565029,0 7.6011899,46.3563489,0 7.6010698,46.3562719,0 7.6008895,46.356266,0 7.6007865,46.3562008,0 7.6005805,46.3559402,0 7.6005548,46.3557328,0 7.6005119,46.355567,0 7.6004089,46.3553359,0 7.600263,46.3551286,0 7.6001857,46.3549272,0 7.6002286,46.3548146,0 7.6000999,46.3547376,0 7.6002716,46.3546547,0 7.6002029,46.3545066,0 7.6000484,46.3542637,0 7.5999111,46.3540564,0 7.5999282,46.3539142,0 7.5998853,46.353849,0 7.5999025,46.3533218,0 7.5998338,46.3531737,0 7.5998081,46.3530433,0 7.5996707,46.3528656,0 7.5997051,46.3526287,0 7.5996707,46.3523443,0 7.5995763,46.3521073,0 7.5994476,46.35174,0 7.5993789,46.3514734,0 7.5993017,46.3513668,0 7.5993017,46.3512246,0 7.5995162,46.3509639,0 7.5994218,46.3506677,0 7.599439,46.3505492,0 7.5993961,46.3504426,0 7.599336,46.3502293,0 7.5992416,46.3501286,0 7.5991214,46.3499272,0 7.599027,46.3497494,0 7.5989755,46.3497079,0 7.5988039,46.3492162,0 7.5989068,46.348997,0 7.5989841,46.3488015,0 7.598924,46.34876,0 7.5988468,46.3487126,0 7.5987695,46.3486534,0 7.5988039,46.3483097,0 7.5988897,46.3481202,0 7.5989154,46.3480313,0 7.5990699,46.3476995,0 7.5991386,46.3475929,0 7.5992244,46.3475158,0 7.5994132,46.3474862,0 7.599542,46.3474566,0 7.5997137,46.3473203,0 7.5996536,46.3472374,0 7.5995506,46.3471722,0 7.5996278,46.3467397,0 7.5996278,46.3464197,0 7.5997652,46.346319,0 7.5999969,46.346005,0 7.6000226,46.345845,0 7.6001085,46.345768,0 7.6001171,46.3456673,0 7.6000656,46.345531,0 7.6000141,46.3453355,0 7.6000656,46.3452881,0 7.6002372,46.3451992,0 7.6003831,46.3450866,0 7.6003917,46.3448615,0 7.6004775,46.3447074,0 7.600572,46.3446067,0 7.6005119,46.3444704,0 7.6003831,46.3442749,0 7.600529,46.3442808,0 7.6006664,46.3442749,0 7.6007694,46.3442334,0 7.6007694,46.3440972,0 7.6008724,46.3439194,0 7.6011556,46.343789,0 7.6014303,46.3437416,0 7.6015762,46.3436705,0 7.6016019,46.3439609,0 7.6017135,46.3440438,0 7.6018852,46.3437831,0 7.601971,46.343635,0 7.6021427,46.3435698,0 7.6022542,46.3435994,0 7.6024002,46.343558,0 7.6025289,46.3435461,0 7.6027521,46.3435165,0 7.602898,46.3434276,0 7.6030096,46.3434217,0 7.6030868,46.3433861,0 7.6031898,46.3433269,0 7.60337,46.3432973,0 7.60337,46.343238,0 7.6034215,46.3431906,0 7.6035589,46.3431906,0 7.6036533,46.3431432,0 7.6036705,46.3430958,0 7.6039022,46.3429417,0 7.6042112,46.3428825,0 7.6042713,46.3428232,0 7.6044086,46.3427521,0 7.6045116,46.3426573,0 7.6045631,46.342527,0 7.6045631,46.3424203,0 7.6045373,46.3423611,0 7.6045116,46.3422959,0 7.6044343,46.3422011,0 7.6043485,46.3421418,0 7.6043485,46.3420826,0 7.6044086,46.3419581,0 7.6044086,46.3418752,0 7.6044601,46.3416737,0 7.6044601,46.3416085,0 7.6044944,46.341496,0 7.6044858,46.3414012,0 7.6044944,46.3413123,0 7.6044429,46.341176,0 7.6044429,46.3410693,0 7.6044601,46.3409212,0 7.6044172,46.3408205,0 7.6043142,46.340619,0 7.6043142,46.340536,0 7.6042198,46.3403642,0 7.6041769,46.3402279,0 7.6041597,46.3400798,0 7.6041597,46.3399316,0 7.6041425,46.339825,0 7.6040567,46.3396946,0 7.6039623,46.3395643,0 7.6038764,46.3394457,0 7.603782,46.339345,0 7.6036705,46.3392087,0 7.6034988,46.3391554,0 7.6033786,46.3391198,0 7.60331,46.3391672,0 7.6031898,46.3391435,0 7.6030353,46.3391791,0 7.6029323,46.3391791,0 7.6029151,46.3391554,0 7.6027778,46.3392028,0 7.6027177,46.3392324,0 7.6027177,46.3392561,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gemmiweg</name>
    +          <description>Gemmiweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6144451,46.3984215,0 7.6144629,46.3983312,0 7.6143611,46.398261,0 7.6140943,46.3980976,0 7.6139827,46.3980162,0 7.6139853,46.3978615,0 7.6139363,46.3976182,0 7.6133218,46.3975412,0 7.6124978,46.3971683,0 7.612206,46.3968545,0 7.6120139,46.3966832,0 7.6116052,46.3966355,0 7.611382,46.3964875,0 7.6117254,46.3962803,0 7.6120344,46.3962507,0 7.6122232,46.3963218,0 7.6123777,46.3963099,0 7.6124635,46.3962034,0 7.6121545,46.3961324,0 7.6121373,46.3960021,0 7.6123262,46.3959666,0 7.6123777,46.3957772,0 7.6128068,46.3951379,0 7.6131244,46.3949011,0 7.6132531,46.3945341,0 7.6134129,46.3943691,0 7.6134935,46.3947235,0 7.6136045,46.3948874,0 7.6136651,46.3945637,0 7.6138969,46.394463,0 7.6142193,46.3946836,0 7.6141604,46.3948245,0 7.6141127,46.3950487,0 7.6145638,46.394795,0 7.6151903,46.3940846,0 7.6153196,46.3934043,0 7.615535,46.3933165,0 7.6156972,46.3932504,0 7.6160405,46.3931675,0 7.6163495,46.3934161,0 7.6165875,46.3938373,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4393848,46.4651486,0 7.4391324,46.4649312,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4444111,46.4575848,0 7.4448112,46.457744,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Schulstrasse</name>
    +          <description>Schulstrasse, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4455274,46.4564945,0 7.4451624,46.4562853,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kronenplatz</name>
    +          <description>Kronenplatz, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4425957,46.4564794,0 7.4428776,46.4565207,0 7.4429918,46.455969,0 7.4427674,46.4559269,0 7.4425496,46.4558861,0 7.4424659,46.4563108,0 7.442629,46.4563359,0 7.4425957,46.4564794,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kronenplatz</name>
    +          <description>Kronenplatz, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4433217,46.4560368,0 7.4429918,46.455969,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.445441,46.4596274,0 7.4460723,46.4588277,0 7.4461285,46.4588623,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5597509,46.4884691,0 7.5600553,46.4879829,0 7.5605373,46.4880844,0 7.5607923,46.4876673,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4991265,46.3699083,0 7.4988246,46.3699358,0 7.498686,46.370369,0 7.4982529,46.370369,0 7.4976898,46.3704989,0 7.4968928,46.3705249,0 7.4968495,46.3707934,0 7.4958706,46.3712786,0 7.4948224,46.3714951,0 7.4936871,46.3716586,0 7.4926614,46.3714013,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5802311,46.4989017,0 7.5803456,46.4986969,0 7.5802886,46.4986171,0 7.5802181,46.4985699,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5659698,46.4470345,0 7.568545,46.4382212,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Hängebrücke Hohstalden</name>
    +          <description>Hängebrücke Hohstalden, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.61705,46.560728,0 7.6174601,46.5593795,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.579346,46.50235,0 7.579269,46.50252,0 7.579288,46.502698,0 7.57914,46.502895,0 7.579196,46.503146,0 7.5789389,46.5032379,0 7.5787664,46.5034171,0 7.5777661,46.503767,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bonderlenbach Walking Path</name>
    +          <description>Bonderlenbach Walking Path, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5774594,46.4953694,0 7.5773913,46.4953126,0 7.577239,46.495303,0 7.5771044,46.495242,0 7.576939,46.4951141,0 7.577052,46.494997,0 7.5772593,46.4948772,0 7.5773923,46.4947336,0 7.577531,46.494696,0 7.5776088,46.4943938,0 7.57769,46.49426,0 7.5778693,46.4941249,0 7.577957,46.493648,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bonderlenbach Walking Path</name>
    +          <description>Bonderlenbach Walking Path, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5788628,46.4927947,0 7.5789515,46.4926509,0 7.5791509,46.4925641,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.563427,46.498168,0 7.5642551,46.4979457,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5707894,46.5079608,0 7.5707636,46.5078013,0 7.5696221,46.5072814,0 7.5695448,46.5076713,0 7.5694207,46.5076825,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5655643,46.5070497,0 7.564925,46.5070055,0 7.564616,46.5070823,0 7.5642126,46.5070527,0 7.5633286,46.5066747,0 7.5635174,46.5065565,0 7.564041,46.506592,0 7.5636204,46.506397,0 7.5629681,46.5064088,0 7.5604532,46.5069996,0 7.5600241,46.5066688,0 7.5589855,46.5063202,0 7.5580242,46.5058299,0 7.5574663,46.5057413,0 7.5568998,46.5057531,0 7.5559986,46.5055995,0 7.5532009,46.5040121,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5676637,46.4896332,0 7.5676054,46.4894827,0 7.567476,46.489347,0 7.5669614,46.4890433,0 7.566749,46.488896,0 7.566351,46.488737,0 7.566058,46.48822,0 7.566112,46.488037,0 7.5659924,46.4879446,0 7.565645,46.487817,0 7.56517,46.487748,0 7.564918,46.4876,0 7.563815,46.486383,0 7.563781,46.48615,0 7.563258,46.485336,0 7.563173,46.485097,0 7.5635808,46.4847034,0 7.56399,46.484315,0 7.564052,46.484108,0 7.563941,46.483716,0 7.563332,46.48324,0 7.563376,46.482928,0 7.5637511,46.4825866,0 7.563831,46.482399,0 7.56378,46.482257,0 7.563003,46.481689,0 7.563023,46.481459,0 7.5633366,46.4810949,0 7.5634558,46.4809124,0 7.563457,46.48083,0 7.5633171,46.4806886,0 7.5630604,46.4805584,0 7.562948,46.480452,0 7.562808,46.479719,0 7.562709,46.479606,0 7.562005,46.479358,0 7.5618487,46.4792297,0 7.561782,46.47908,0 7.5617721,46.4788605,0 7.5618,46.47874,0 7.5624466,46.4780984,0 7.562288,46.477703,0 7.562083,46.477529,0 7.562101,46.477352,0 7.562461,46.476986,0 7.562711,46.47685,0 7.562794,46.476692,0 7.562781,46.476621,0 7.562696,46.476563,0 7.5622314,46.476513,0 7.5621583,46.4764818,0 7.561977,46.476351,0 7.5618962,46.4759014,0 7.561704,46.47567,0 7.561627,46.475207,0 7.561799,46.474588,0 7.561714,46.474406,0 7.5612418,46.474131,0 7.5611,46.473945,0 7.561488,46.47345,0 7.561538,46.47326,0 7.561166,46.472885,0 7.561107,46.472688,0 7.561294,46.472118,0 7.5612735,46.4718594,0 7.5611621,46.4717378,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5611621,46.4717378,0 7.5610469,46.4717054,0 7.5609132,46.4715494,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5621068,46.4705513,0 7.5622317,46.4705599,0 7.5627956,46.4700239,0 7.563029,46.469433,0 7.5632876,46.4691042,0 7.5635408,46.4688811,0 7.5635817,46.469008,0 7.5640286,46.4684621,0 7.564014,46.467933,0 7.5640877,46.4678364,0 7.5641633,46.4678644,0 7.564247,46.467826,0 7.564276,46.467683,0 7.564102,46.467479,0 7.5643394,46.4671283,0 7.564496,46.466775,0 7.5644585,46.4665703,0 7.5642804,46.4663023,0 7.5641749,46.4659848,0 7.5641356,46.4658925,0 7.5642694,46.4656671,0 7.5642626,46.4652282,0 7.563344,46.464121,0 7.56346,46.463889,0 7.5637092,46.4637241,0 7.563776,46.4636,0 7.563387,46.463496,0 7.563191,46.463134,0 7.5631304,46.4625612,0 7.563308,46.462064,0 7.563156,46.461487,0 7.563216,46.461386,0 7.563167,46.461144,0 7.5627558,46.4606653,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5627558,46.4606653,0 7.562395,46.460344,0 7.562311,46.459841,0 7.562104,46.459635,0 7.562128,46.459302,0 7.562277,46.459023,0 7.562066,46.458465,0 7.562088,46.458007,0 7.562559,46.457277,0 7.562258,46.456812,0 7.5625,46.456613,0 7.563527,46.456393,0 7.5638831,46.4564126,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5435227,46.4827013,0 7.5436247,46.4830653,0 7.5435592,46.4832093,0 7.5435643,46.4834307,0 7.5434451,46.4835786,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5436127,46.4825783,0 7.5435227,46.4827013,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5405449,46.4792038,0 7.5407348,46.4794281,0 7.5407722,46.4795516,0 7.5408429,46.4796713,0 7.5412903,46.480107,0 7.5414256,46.4803418,0 7.5418407,46.4804783,0 7.5427092,46.4811396,0 7.5431686,46.4820548,0 7.5434355,46.482271,0 7.5435897,46.4824673,0 7.5436127,46.4825783,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5392734,46.4802349,0 7.5395718,46.4800879,0 7.5395976,46.4799933,0 7.5400439,46.4798633,0 7.540207,46.4795973,0 7.5401555,46.4790949,0 7.54034,46.479224,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5287322,46.4845909,0 7.5294532,46.4849041,0 7.5297364,46.4848864,0 7.5299855,46.4847806,0 7.5301035,46.4847938,0 7.5299338,46.4850755,0 7.5309724,46.4849159,0 7.5322129,46.484865,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5564947,46.4921923,0 7.5561256,46.49234,0 7.5558595,46.4922159,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5289364,46.4906938,0 7.5297775,46.4904456,0 7.5303783,46.4905165,0 7.5325241,46.4899728,0 7.5333567,46.4895533,0 7.5343609,46.4893051,0 7.5350647,46.4892401,0 7.5353651,46.4892814,0 7.5353565,46.4891751,0 7.5358801,46.488986,0 7.5377512,46.4885191,0 7.5381632,46.4883359,0 7.539073,46.4883359,0 7.5392876,46.4882355,0 7.5399056,46.4881586,0 7.5406694,46.4881527,0 7.5425577,46.4885605,0 7.5426865,46.4886019,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5605804,46.4538416,0 7.5601817,46.4540178,0 7.5599834,46.4542594,0 7.5600283,46.4548461,0 7.560247,46.455188,0 7.560294,46.455557,0 7.5600484,46.4561044,0 7.5601535,46.4562692,0 7.560098,46.4567024,0 7.5602085,46.4572266,0 7.5611106,46.4583043,0 7.561229,46.4584659,0 7.5612212,46.4586588,0 7.5614828,46.4589198,0 7.5614623,46.4593025,0 7.5619777,46.4604155,0 7.5619267,46.4606423,0 7.5620549,46.4609595,0 7.5622266,46.4611132,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5607503,46.4539705,0 7.5605804,46.4538416,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5592176,46.4928554,0 7.5588805,46.4931539,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.58757,46.5206516,0 7.587624,46.520509,0 7.587569,46.520492,0 7.5873231,46.5205316,0 7.5872162,46.5205068,0 7.5870656,46.5205131,0 7.5870192,46.5204851,0 7.586853,46.520522,0 7.5867788,46.5204989,0 7.5867531,46.5204556,0 7.586698,46.520419,0 7.58654,46.520501,0 7.586356,46.520353,0 7.586116,46.520307,0 7.585928,46.520346,0 7.586103,46.520418,0 7.586086,46.520493,0 7.585796,46.520522,0 7.585176,46.5203633,0 7.5847859,46.5202177,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6265519,46.5699584,0 7.6262293,46.5697064,0 7.6258508,46.5695701,0 7.6252722,46.5695891,0 7.6245642,46.5694303,0 7.6239117,46.5688845,0 7.623715,46.5687,0 7.6236341,46.568349,0 7.62342,46.567928,0 7.6233636,46.567667,0 7.6230644,46.5674407,0 7.623046,46.567261,0 7.622904,46.567049,0 7.6228902,46.5669243,0 7.6225899,46.5663963,0 7.6221994,46.5658716,0 7.621795,46.565414,0 7.6214698,46.5652029,0 7.6213299,46.5649714,0 7.62114,46.564566,0 7.620754,46.564259,0 7.62047,46.563916,0 7.620153,46.563799,0 7.619982,46.563521,0 7.619814,46.563445,0 7.619689,46.563286,0 7.619159,46.562907,0 7.619115,46.562645,0 7.618927,46.562489,0 7.618964,46.562351,0 7.618862,46.56205,0 7.6183889,46.5610409,0 7.6180638,46.5608003,0 7.617347,46.560459,0 7.6172232,46.5604252,0 7.617017,46.560369,0 7.616846,46.5603423,0 7.6164317,46.5601639,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.618372,46.559119,0 7.618307,46.559387,0 7.618404,46.55956,0 7.618755,46.559665,0 7.618969,46.559649,0 7.619314,46.559828,0 7.619385,46.559913,0 7.619791,46.560116,0 7.619984,46.560295,0 7.6200836,46.5604261,0 7.620185,46.560498,0 7.6202482,46.5605223,0 7.6202935,46.5605318,0 7.6203308,46.5605679,0 7.6203162,46.5606165,0 7.620289,46.560703,0 7.620332,46.561026,0 7.620423,46.561139,0 7.620723,46.56129,0 7.620805,46.561706,0 7.6208667,46.5617554,0 7.6211,46.5618215,0 7.6212232,46.5618909,0 7.6212343,46.5621671,0 7.621288,46.5622445,0 7.6213723,46.5623164,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4499634,46.4388549,0 7.449004,46.4378119,0 7.4487208,46.4371258,0 7.4489182,46.4368774,0 7.4492358,46.4367177,0 7.4496821,46.4368892,0 7.4495619,46.4359014,0 7.4491671,46.4354518,0 7.4478625,46.4349549,0 7.4459484,46.4347183,0 7.4453047,46.434393,0 7.4449099,46.4337068,0 7.4447811,46.4330857,0 7.4444636,46.4330029,0 7.444043,46.4326302,0 7.4439314,46.4322634,0 7.4433821,46.4320268,0 7.4429959,46.4309502,0 7.4428843,46.4308082,0 7.4424895,46.4306544,0 7.4423521,46.4301752,0 7.4420946,46.430193,0 7.4416226,46.4297729,0 7.4408072,46.4295304,0 7.4405239,46.4290098,0 7.440481,46.4285247,0 7.4401892,46.4282999,0 7.4398373,46.4281756,0 7.439597,46.4278621,0 7.4393824,46.4273119,0 7.4392794,46.4266256,0 7.4386958,46.4261287,0 7.4388331,46.4257737,0 7.4388331,46.4254187,0 7.4386614,46.4251229,0 7.4386786,46.4236556,0 7.4386013,46.4233006,0 7.4382065,46.4226498,0 7.4386271,46.4224013,0 7.4388245,46.4221351,0 7.4388588,46.4218866,0 7.4398802,46.4208511,0 7.4397858,46.4204666,0 7.4394511,46.4201944,0 7.4392451,46.4198808,0 7.4397772,46.4188394,0 7.4395626,46.4185613,0 7.4395712,46.4181412,0 7.439185,46.4174962,0 7.4391678,46.4168808,0 7.4397257,46.4163187,0 7.4401034,46.4161767,0 7.4406613,46.4157625,0 7.4424637,46.4134132,0 7.4419144,46.413218,0 7.4419793,46.4126531,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4411642,46.3998745,0 7.4407992,46.3997408,0 7.4405978,46.3996171,0 7.440568,46.3996036,0 7.439607,46.3991398,0 7.4392012,46.3987612,0 7.4388588,46.3986409,0 7.4356488,46.3977589,0 7.4353398,46.3974985,0 7.4336403,46.3965928,0 7.4334086,46.3963205,0 7.432679,46.3958055,0 7.429941,46.3951426,0 7.4290913,46.3947104,0 7.4276236,46.3936508,0 7.4269198,46.3933371,0 7.4264391,46.3932246,0 7.4253662,46.3927096,0 7.4246882,46.392082,0 7.4240187,46.3917564,0 7.4232806,46.3916558,0 7.4219673,46.3907737,0 7.4212979,46.3905547,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4212979,46.3905547,0 7.4212979,46.3903416,0 7.4214094,46.3901462,0 7.4214094,46.3899982,0 7.4215296,46.3898028,0 7.4214867,46.3895838,0 7.421358,46.3895009,0 7.4211262,46.3895542,0 7.421049,46.3893411,0 7.4209288,46.3892049,0 7.4200018,46.3892641,0 7.4197358,46.3894654,0 7.4195813,46.3894772,0 7.4193152,46.3894654,0 7.4192208,46.3893055,0 7.4184483,46.3887786,0 7.4182337,46.3885951,0 7.4179419,46.3884412,0 7.417693,46.3884353,0 7.4176415,46.3887727,0 7.4177187,46.389051,0 7.4174355,46.3889622,0 7.4171952,46.3887727,0 7.4168175,46.3886129,0 7.4165171,46.3884708,0 7.4158905,46.388305,0 7.4157446,46.3881096,0 7.415633,46.3878787,0 7.4155644,46.387636,0 7.4154271,46.3874584,0 7.4151867,46.3876419,0 7.4148177,46.387488,0 7.4143628,46.3873696,0 7.4140967,46.3872038,0 7.4139851,46.3869492,0 7.4138306,46.3867479,0 7.4134358,46.3866532,0 7.413041,46.386671,0 7.4126719,46.386742,0 7.4119252,46.3866059,0 7.4115818,46.3866118,0 7.4112042,46.3866769,0 7.4108609,46.3868723,0 7.4105519,46.3869611,0 7.4103168,46.387076,0 7.4097279,46.3873637,0 7.4093932,46.3874406,0 7.4086121,46.3875117,0 7.4077366,46.3879202,0 7.4061402,46.3884116,0 7.4056166,46.3883524,0 7.4053334,46.3883524,0 7.4050673,46.3883879,0 7.4047583,46.3885537,0 7.404003,46.3885063,0 7.40287,46.388897,0 7.4025954,46.3889681,0 7.4022435,46.3890924,0 7.4017027,46.3891931,0 7.4012993,46.3891516,0 7.4006041,46.3888319,0 7.399411,46.3885833,0 7.3993252,46.3883761,0 7.3993338,46.3878965,0 7.3996256,46.3874466,0 7.399454,46.3872334,0 7.3994025,46.3869848,0 7.399557,46.3864638,0 7.3994883,46.3861796,0 7.3992308,46.3858007,0 7.3990334,46.3855461,0 7.3991879,46.3843086,0 7.3984326,46.383687,0 7.3977631,46.3834205,0 7.3966215,46.3832903,0 7.395892,46.3830297,0 7.3950852,46.3828758,0 7.394081,46.3824613,0 7.3934115,46.3821535,0 7.3931282,46.3819462,0 7.3927077,46.3816975,0 7.3925703,46.381591,0 7.3922528,46.381135,0 7.3916005,46.3808508,0 7.3911799,46.3807679,0 7.3904932,46.3805548,0 7.389592,46.3801521,0 7.3894886,46.3800737,0 7.3893345,46.3799567,0 7.3893345,46.3797791,0 7.3891371,46.379708,0 7.3889874,46.3795666,0 7.3889998,46.3793528,0 7.388974,46.3792521,0 7.3888281,46.3791929,0 7.3884676,46.3791929,0 7.3881758,46.3792284,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.3842169,46.3434602,0 7.3841439,46.3438004,0 7.3847101,46.3439064,0 7.3844032,46.3440306,0 7.3846307,46.3442059,0 7.384472,46.3446077,0 7.384091,46.3450132,0 7.3840434,46.3454953,0 7.3842868,46.3461382,0 7.3847683,46.34635,0 7.3851228,46.346803,0 7.3853715,46.3470586,0 7.3858901,46.3476723,0 7.3863028,46.3482859,0 7.387033,46.3486256,0 7.3874669,46.3491406,0 7.3872658,46.3495497,0 7.3875304,46.3498966,0 7.3881654,46.3499259,0 7.3878744,46.3501523,0 7.3882289,46.350145,0 7.3881178,46.3506308,0 7.3887263,46.3506271,0 7.3882359,46.3513098,0 7.3884405,46.3514744,0 7.3891496,46.3518433,0 7.3901285,46.3522524,0 7.3905994,46.3524715,0 7.3915043,46.3526687,0 7.3927689,46.3530376,0 7.3934092,46.3533919,0 7.3941976,46.3536694,0 7.3955522,46.3541442,0 7.3963459,46.3546299,0 7.3968221,46.3548052,0 7.3973618,46.3551668,0 7.3978287,46.3553046,0 7.3984467,46.3557607,0 7.3985926,46.3559384,0 7.398687,46.3560984,0 7.3988501,46.3563531,0 7.3991162,46.3565071,0 7.3995453,46.3570462,0 7.3996397,46.3574372,0 7.4000088,46.3576623,0 7.4003092,46.3577274,0 7.4005667,46.3579407,0 7.4003435,46.3582605,0 7.4003006,46.3586515,0 7.4005152,46.3586633,0 7.4005924,46.3588292,0 7.400807,46.3590069,0 7.4008585,46.3594452,0 7.4006525,46.3596526,0 7.4006354,46.3599665,0 7.4007384,46.3602034,0 7.4012877,46.3606773,0 7.4013735,46.3608965,0 7.4014851,46.361702,0 7.4016138,46.361939,0 7.4014851,46.3622647,0 7.400807,46.3625668,0 7.400807,46.3627327,0 7.4015881,46.3626557,0 7.4019057,46.362703,0 7.4023005,46.3630347,0 7.4025408,46.3632657,0 7.4027725,46.3633546,0 7.402867,46.3635797,0 7.4031588,46.3636152,0 7.4041544,46.3637159,0 7.4044462,46.3636803,0 7.4047638,46.3637218,0 7.4051844,46.3637218,0 7.405708,46.3635263,0 7.4062144,46.3634493,0 7.4067637,46.3632894,0 7.4070984,46.3636211,0 7.4073645,46.363787,0 7.408137,46.3639587,0 7.4091326,46.3640535,0 7.4095961,46.3640594,0 7.4099137,46.3642608,0 7.4103686,46.3645806,0 7.4104801,46.3648472,0 7.4109522,46.3652025,0 7.411141,46.365475,0 7.4114586,46.3656882,0 7.4116989,46.3658185,0 7.4121796,46.3660673,0 7.4128662,46.3660791,0 7.4131237,46.366239,0 7.4131409,46.3665055,0 7.4137589,46.3666773,0 7.413982,46.3668313,0 7.4143511,46.3669794,0 7.4148918,46.3672873,0 7.4155098,46.367542,0 7.4156128,46.3677315,0 7.4158617,46.36785,0 7.4164025,46.367927,0 7.4170204,46.3681106,0 7.4173981,46.3682231,0 7.4177672,46.3684304,0 7.4183165,46.368537,0 7.4186684,46.3686791,0 7.4189259,46.3685962,0 7.4191319,46.3687028,0 7.4194838,46.3687561,0 7.4198014,46.3686673,0 7.4200588,46.3687028,0 7.4202648,46.3689279,0 7.4202219,46.3692003,0 7.4205824,46.3693661,0 7.4211661,46.3695675,0 7.4221531,46.3697866,0 7.4224707,46.3701419,0 7.4230801,46.3700294,0 7.4233719,46.3700294,0 7.4235693,46.370444,0 7.4238783,46.370592,0 7.4241444,46.3708052,0 7.4243933,46.3709711,0 7.4246508,46.3710658,0 7.4257408,46.3708645,0 7.4260842,46.3708585,0 7.4263395,46.3709363,0 7.426376,46.3709474,0 7.4265734,46.3713382,0 7.4268652,46.3716284,0 7.4272343,46.3716521,0 7.4276377,46.3717409,0 7.4278179,46.3719482,0 7.4277664,46.3721969,0 7.4278351,46.3724338,0 7.4280926,46.3725582,0 7.4285733,46.3726648,0 7.4291397,46.3730083,0 7.4294316,46.3732984,0 7.4298865,46.3735649,0 7.4302813,46.3736597,0 7.4305216,46.3738433,0 7.4307791,46.3739735,0 7.4315087,46.3746072,0 7.431646,46.3748737,0 7.4324099,46.3751697,0 7.4329506,46.3755014,0 7.4332682,46.3756316,0 7.4339892,46.3755724,0 7.4346415,46.3757441,0 7.434899,46.3759277,0 7.4352938,46.3760165,0 7.43562,46.3761172,0 7.4360062,46.376135,0 7.4369589,46.3766798,0 7.4371821,46.3768929,0 7.4377743,46.3772186,0 7.4381777,46.37877,0 7.4383837,46.3789951,0 7.4382378,46.3794865,0 7.4385039,46.3796878,0 7.4392334,46.3799365,0 7.4395081,46.3801971,0 7.4395596,46.3809135,0 7.4407097,46.3815412,0 7.4406668,46.3818254,0 7.4408127,46.3820504,0 7.440787,46.3822991,0 7.44089,46.3825359,0 7.4416453,46.3827194,0 7.4417826,46.3829622,0 7.4422289,46.3833056,0 7.4428984,46.3833056,0 7.4436194,46.3831102,0 7.4444262,46.3831931,0 7.4447867,46.3831576,0 7.4451128,46.3830806,0 7.4454218,46.3831872,0 7.4457995,46.3831931,0 7.4468123,46.3828023,0 7.4474989,46.382151,0 7.448855,46.3817129,0 7.449207,46.3817425,0 7.4499108,46.3816004,0 7.4501425,46.3814227,0 7.4511811,46.3813695,0 7.4526402,46.381026,0 7.4528805,46.3811445,0 7.4531037,46.3812214,0 7.4537903,46.3812273,0 7.4545456,46.381026,0 7.4549834,46.3810142,0 7.4554039,46.3810912,0 7.4559189,46.3810675,0 7.4566742,46.3811563,0 7.4570347,46.3813043,0 7.4575669,46.3813635,0 7.4580389,46.381701,0 7.4583908,46.3817129,0 7.4590689,46.3818964,0 7.4594294,46.3818905,0 7.4598757,46.3819083,0 7.4603649,46.3819734,0 7.4605709,46.3820148,0 7.4609143,46.382151,0 7.4611073,46.3822106,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4681028,46.3828006,0 7.4685635,46.3827568,0 7.4690493,46.382662,0 7.469454,46.382552,0 7.4695566,46.3824441,0 7.4698788,46.3824318,0 7.4702351,46.3823567,0 7.4705841,46.3822218,0 7.4707965,46.3819506,0 7.4709561,46.3816876,0 7.4712107,46.3814354,0 7.4719174,46.3811665,0 7.4720289,46.3809119,0 7.4715655,46.3805211,0 7.4715578,46.3803844,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4773344,46.3802503,0 7.4784834,46.3804797,0 7.479522,46.3806514,0 7.481436,46.3807817,0 7.4856331,46.3808231,0 7.4888174,46.3808527,0 7.4920618,46.3819599,0 7.4971688,46.3817823,0 7.5060608,46.3832329,0 7.5186865,46.3873596,0 7.5244114,46.3897988,0 7.5249779,46.3906927,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5249779,46.3906927,0 7.5245058,46.3908348,0 7.5242569,46.3912137,0 7.5239479,46.3913499,0 7.5235617,46.3916636,0 7.5234329,46.3920425,0 7.5237677,46.3923503,0 7.5237248,46.3924806,0 7.5233729,46.3925398,0 7.5233128,46.3926522,0 7.5233214,46.3927825,0 7.522815,46.3936231,0 7.5228407,46.3938717,0 7.5227377,46.3940375,0 7.5228922,46.3945347,0 7.5232012,46.3946354,0 7.5235617,46.3946354,0 7.5238535,46.3947893,0 7.5238535,46.3952806,0 7.5239479,46.3954404,0 7.5242054,46.3954937,0 7.5242827,46.3960205,0 7.5242655,46.3965414,0 7.5241797,46.3968078,0 7.5242741,46.3970446,0 7.5246861,46.3970209,0 7.5250294,46.3969144,0 7.5254586,46.3969025,0 7.5259478,46.3968611,0 7.5260508,46.3976069,0 7.5264628,46.3978259,0 7.5265314,46.3979798,0 7.5264284,46.3982048,0 7.5266087,46.3985836,0 7.5269091,46.3987138,0 7.5269863,46.3989092,0 7.5269606,46.3991459,0 7.5271923,46.3992761,0 7.527158,46.3995366,0 7.5278961,46.3999036,0 7.5280936,46.4003061,0 7.5284329,46.4005085,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5284329,46.4005085,0 7.5284283,46.4009631,0 7.5287888,46.4013656,0 7.529072,46.401555,0 7.5293038,46.401774,0 7.5294411,46.4019811,0 7.5297501,46.4022001,0 7.5299475,46.4027328,0 7.5313036,46.4036384,0 7.5324194,46.4058933,0 7.5325138,46.4064674,0 7.5330717,46.4068048,0 7.5332262,46.4073492,0 7.5332691,46.4084382,0 7.5336468,46.4092075,0 7.533561,46.4094147,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.533561,46.4094147,0 7.5338185,46.4089767,0 7.5343077,46.408521,0 7.5394146,46.405408,0 7.5409853,46.4053429,0 7.5429079,46.4057513,0 7.5435517,46.4062484,0 7.5440838,46.4066509,0 7.5445301,46.4068166,0 7.5453541,46.4068225,0 7.5462296,46.4065858,0 7.5478604,46.4051417,0 7.5478432,46.4048043,0 7.548032,46.4044966,0 7.5486243,46.404177,0 7.5488045,46.4039166,0 7.5510447,46.4031175,0 7.5512764,46.402863,0 7.5519116,46.4027447,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5519116,46.4027447,0 7.5535424,46.4026973,0 7.5542977,46.4025789,0 7.5558941,46.4026204,0 7.5565464,46.4025197,0 7.5572932,46.4021232,0 7.5587351,46.401774,0 7.5594904,46.4015076,0 7.5599539,46.4011288,0 7.560941,46.4004718,0 7.5616619,46.399945,0 7.5617821,46.3993827,0 7.5619967,46.3991992,0 7.5620653,46.3988618,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5620653,46.3988618,0 7.5626404,46.3984415,0 7.5632241,46.3979147,0 7.563576,46.3978911,0 7.5648291,46.3976128,0 7.5652325,46.3976661,0 7.5661595,46.3980686,0 7.5670607,46.3982225,0 7.5689404,46.3990808,0 7.5700218,46.3994182,0 7.5704167,46.3994774,0 7.5709231,46.3998325,0 7.5716698,46.4000397,0 7.5719616,46.4003001,0 7.5723822,46.4003238,0 7.5727598,46.4002528,0 7.5731547,46.4002824,0 7.5734637,46.4000693,0 7.5739014,46.4000279,0 7.5741675,46.4001048,0 7.574279,46.4002114,0 7.5746567,46.4000811,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Lämmernweg nord</name>
    +          <description>Lämmernweg nord, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5746567,46.4000811,0 7.5745487,46.4005859,0 7.5747632,46.4010476,0 7.574892,46.4016217,0 7.575304,46.4014797,0 7.5758619,46.4012488,0 7.5763339,46.4004024,0 7.5762395,46.4000887,0 7.5764198,46.4000828,0 7.5762653,46.3998105,0 7.5760335,46.3995738,0 7.5760335,46.3991476,0 7.576703,46.3991535,0 7.5767803,46.3989819,0 7.577218,46.3991239,0 7.5771751,46.3988398,0 7.5767202,46.3983899,0 7.5771236,46.3984255,0 7.5772008,46.3983544,0 7.576909,46.3981887,0 7.5773607,46.3980323,0 7.5788917,46.397502,0 7.578866,46.3977743,0 7.5787458,46.3982064,0 7.5789775,46.3984136,0 7.5797243,46.3986326,0 7.5805225,46.39868,0 7.5812263,46.3988812,0 7.5831575,46.3990825,0 7.5835695,46.3991949,0 7.5845737,46.3996507,0 7.5849428,46.3997099,0 7.5857152,46.3996389,0 7.5857839,46.3994317,0 7.5853118,46.39897,0 7.5853204,46.3988635,0 7.5856723,46.3986445,0 7.5858268,46.3984669,0 7.5856122,46.3981946,0 7.5855693,46.3979282,0 7.5863418,46.3973777,0 7.5863075,46.3968687,0 7.5869941,46.396993,0 7.5880039,46.3968541,0 7.5881256,46.3965134,0 7.587925,46.3962126,0 7.5885906,46.3960459,0 7.5897493,46.3961228,0 7.5905303,46.3962767,0 7.5912256,46.3965431,0 7.5920152,46.3967325,0 7.5922813,46.396845,0 7.5926933,46.3969515,0 7.5933113,46.3969456,0 7.5947618,46.3974014,0 7.594985,46.3973718,0 7.595354,46.3974014,0 7.5972509,46.397875,0 7.597577,46.3980052,0 7.5983066,46.3981117,0 7.5987186,46.3980821,0 7.5994739,46.3979282,0 7.5999116,46.3979342,0 7.6005983,46.3979993,0 7.6023149,46.3979697,0 7.6037569,46.3976855,0 7.6041946,46.3976915,0 7.6045207,46.3978217,0 7.6046409,46.3979697,0 7.605422,46.3979697,0 7.6055936,46.3981768,0 7.6057052,46.3984195,0 7.6060056,46.3986267,0 7.6068811,46.3998224,0 7.6069841,46.4001006,0 7.6073961,46.4005563,0 7.6074905,46.4008108,0 7.6073875,46.4010831,0 7.6077308,46.4019354,0 7.6086578,46.40248,0 7.6087779,46.4026694,0 7.6097478,46.4032139,0 7.6101427,46.4035453,0 7.6102628,46.4037821,0 7.6120396,46.4045298,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6223005,46.4319807,0 7.6218319,46.4319245,0 7.6216002,46.4320073,0 7.6212483,46.4319067,0 7.6209393,46.4319067,0 7.6207762,46.4316878,0 7.6202698,46.4313152,0 7.6197205,46.430623,0 7.6196261,46.4300788,0 7.6191626,46.4296173,0 7.6190338,46.4291086,0 7.6188193,46.4290435,0 7.6188107,46.4287595,0 7.6186304,46.4285288,0 7.6188193,46.428097,0 7.6185618,46.4276,0 7.6169911,46.4264404,0 7.6170512,46.4261683,0 7.6174202,46.4257778,0 7.6176691,46.4257009,0 7.617446,46.4256003,0 7.6173258,46.4253223,0 7.6176262,46.4246182,0 7.6176262,46.4243342,0 7.6162014,46.4234113,0 7.6136437,46.4206423,0 7.6134892,46.4203819,0 7.6120301,46.4194826,0 7.6116696,46.4191039,0 7.6108971,46.4190388,0 7.6101933,46.418879,0 7.6100216,46.4187252,0 7.607687,46.4193583,0 7.6072665,46.4193997,0 7.6064167,46.4196778,0 7.6054211,46.4197784,0 7.6040049,46.4202695,0 7.6035757,46.4206245,0 7.6026831,46.4207547,0 7.6014643,46.4205239,0 7.6012669,46.420589,0 7.6007875,46.420399,0 7.6007789,46.4207135,0 7.6009446,46.420851,0 7.6012669,46.4208434,0 7.6013207,46.4211927,0 7.6010655,46.421439,0 7.6011759,46.4216698,0 7.6009562,46.4215633,0 7.6010091,46.4218257,0 7.6006785,46.4218724,0 7.6008396,46.4220273,0 7.6003657,46.4220445,0 7.6001339,46.4223759,0 7.5995094,46.4226935,0 7.5992772,46.4224137,0 7.5988342,46.4231467,0 7.5985281,46.4229856,0 7.5984798,46.4231628,0 7.5983912,46.423195,0 7.5983769,46.4233697,0 7.5971899,46.4232574,0 7.5961118,46.4229597,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5723588,46.4404035,0 7.572171,46.4403665,0 7.5717311,46.4401927,0 7.5716185,46.4400929,0 7.5714683,46.440019,0 7.5712591,46.4400116,0 7.5709855,46.4400375,0 7.5707798,46.4401998,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5603342,46.4434764,0 7.5606322,46.4434199,0 7.5607824,46.4433164,0 7.5610291,46.4433016,0 7.5612115,46.443309,0 7.5614261,46.4431907,0 7.5616943,46.4431759,0 7.5619779,46.4431581,0 7.5621449,46.443102,0 7.5622951,46.443028,0 7.5623917,46.4429171,0 7.5624561,46.4428062,0 7.562617,46.4427323,0 7.5628316,46.4427175,0 7.5631105,46.4426658,0 7.5633684,46.442614,0 7.5636792,46.4424809,0 7.5637328,46.4423479,0 7.5637757,46.4422074,0 7.563883,46.4421335,0 7.5640586,46.4420609,0 7.5642263,46.4419265,0 7.5643336,46.4417934,0 7.564516,46.4415864,0 7.5645482,46.4413572,0 7.5644447,46.4411958,0 7.5644624,46.4410245,0 7.5645027,46.440655,0 7.5644731,46.4404256,0 7.5644087,46.4401964,0 7.5644194,46.4401003,0 7.5645375,46.4399377,0 7.5645911,46.4398046,0 7.5645482,46.4396345,0 7.5644636,46.439478,0 7.5644553,46.4393461,0 7.5644516,46.4392871,0 7.5645298,46.4391761,0 7.5645267,46.4390209,0 7.5644927,46.4389173,0 7.5643867,46.438837,0 7.5642585,46.4387399,0 7.5641727,46.4385773,0 7.5640654,46.4384812,0 7.5640869,46.4383481,0 7.5640869,46.4381928,0 7.5640654,46.4380523,0 7.5638186,46.4379192,0 7.5635397,46.4377714,0 7.5633895,46.437727,0 7.5631964,46.4377344,0 7.5629818,46.437764,0 7.5627028,46.4377418,0 7.5625097,46.4377048,0 7.5622308,46.43769,0 7.5617587,46.4376679,0 7.5615015,46.4377341,0 7.5611472,46.437727,0 7.5608253,46.4376457,0 7.56039,46.4375687,0 7.5600421,46.4374534,0 7.5597417,46.4372982,0 7.5593769,46.4371133,0 7.5590597,46.4370482,0 7.5587593,46.4370482,0 7.5585293,46.4370246,0 7.5583577,46.4370616,0 7.558186,46.4371651,0 7.5580517,46.4372548,0 7.5579285,46.4372908,0 7.5577354,46.4372982,0 7.5575368,46.4372948,0 7.5573921,46.4372612,0 7.5573062,46.4371799,0 7.5571113,46.4371429,0 7.5569842,46.437187,0 7.5568771,46.4372242,0 7.5567376,46.4371799,0 7.5566089,46.4370985,0 7.5563643,46.4370678,0 7.5562333,46.4370172,0 7.5560724,46.4368398,0 7.5557827,46.436581,0 7.5555361,46.4364881,0 7.5553107,46.4364848,0 7.5551605,46.4366179,0 7.5549827,46.4367111,0 7.5553321,46.4370542,0 7.5555682,46.4373647,0 7.5557613,46.437483,0 7.5558256,46.4377566,0 7.5558839,46.4380538,0 7.5557827,46.4382076,0 7.5556969,46.438385,0 7.5556647,46.4385181,0 7.5556325,46.4386586,0 7.5555096,46.4386959,0 7.555418,46.4387917,0 7.5554659,46.4389223,0 7.5553965,46.4390283,0 7.5552034,46.43908,0 7.5550514,46.4391953,0 7.5549995,46.4393684,0 7.5550639,46.4395902,0 7.5551497,46.4397972,0 7.5553947,46.4399761,0 7.5555789,46.4400781,0 7.5556218,46.4403,0 7.5557981,46.4405675,0 7.5560402,46.4406696,0 7.556169,46.4407879,0 7.556253,46.4409697,0 7.5562763,46.4412685,0 7.5565981,46.4416233,0 7.5568881,46.4420225,0 7.5571238,46.442237,0 7.5574242,46.4423848,0 7.5576174,46.4424736,0 7.5577465,46.4425962,0 7.5578623,46.4428613,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5578623,46.4428613,0 7.5574675,46.4428376,0 7.5562401,46.4421989,0 7.5553904,46.4415956,0 7.5545922,46.4411993,0 7.5531674,46.4401583,0 7.5520945,46.4397857,0 7.5515452,46.4394486,0 7.5512276,46.4389458,0 7.5500603,46.4382479,0 7.5478888,46.4365917,0 7.5459061,46.435521,0 7.5430308,46.4344326,0 7.5427647,46.434196,0 7.5416146,46.4337878,0 7.5403786,46.4336045,0 7.5392456,46.4328177,0 7.5388594,46.4323326,0 7.5385161,46.4322143,0 7.538353,46.4320369,0 7.5376578,46.4320132,0 7.5371514,46.4324391,0 7.5367651,46.4324864,0 7.5366364,46.4326994,0 7.5354691,46.4325811,0 7.5352631,46.4330603,0 7.5350056,46.4329065,0 7.5343533,46.4329656,0 7.5341473,46.4326994,0 7.5336151,46.4325042,0 7.5332289,46.4325752,0 7.5329113,46.4324805,0 7.5324993,46.4327408,0 7.5314608,46.4328532,0 7.5309286,46.4325988,0 7.5305853,46.4326048,0 7.5302935,46.4323208,0 7.5299759,46.4321848,0 7.5298472,46.4319836,0 7.5296326,46.4310016,0 7.529727,46.4307473,0 7.5300188,46.4304278,0 7.5297613,46.4302385,0 7.5296583,46.4300374,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5099774,46.4268191,0 7.5097027,46.4267599,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.510046,46.4269729,0 7.5099774,46.4268191,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5097027,46.4267599,0 7.5091706,46.4267008,0 7.5088101,46.4268782,0 7.5086899,46.4270261,0 7.5082865,46.4270261,0 7.507514,46.4268901,0 7.507205,46.4267422,0 7.5070591,46.4265351,0 7.5070935,46.4263635,0 7.5055828,46.4258015,0 7.5046387,46.4252572,0 7.504201,46.4251566,0 7.5038748,46.4249614,0 7.5029049,46.4247543,0 7.50245,46.4245827,0 7.5018921,46.424636,0 7.5014286,46.4245354,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <description>Krummenbach, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4421122,46.4525537,0 7.4426894,46.4528647,0 7.4433701,46.4534102,0 7.4441109,46.4543138,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <description>Krummenbach, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4419716,46.4526353,0 7.4421122,46.4525537,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <description>Krummenbach, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4408543,46.4522683,0 7.4415573,46.4524314,0 7.4419716,46.4526353,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <description>Krummenbach, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4409061,46.4521561,0 7.4408543,46.4522683,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Chäligang - Klettersteig</name>
    +          <description>Chäligang - Klettersteig, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.560727,46.450533,0 7.5606144,46.4505986,0 7.5603311,46.4507338,0 7.5603426,46.4509315,0 7.5605864,46.4511387,0 7.5605865,46.4512611,0 7.560682,46.4513342,0 7.5607896,46.4513261,0 7.5611184,46.4513877,0 7.561297,46.4513409,0 7.5614312,46.4513311,0 7.5614866,46.4514444,0 7.5615925,46.4516057,0 7.5617415,46.4517005,0 7.5619471,46.4517731,0 7.561897,46.4519024,0 7.561663,46.4519071,0 7.5615552,46.4517685,0 7.561466,46.4516993,0 7.5612982,46.4516854,0 7.5611181,46.4516115,0 7.5608041,46.4515715,0 7.5606409,46.451533,0 7.5604993,46.4515084,0 7.5601961,46.451513,0 7.5603839,46.4516038,0 7.560233,46.4517454,0 7.5600529,46.4516916,0 7.5598755,46.4516727,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Lischmattenstrasse</name>
    +          <description>Lischmattenstrasse, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4394643,46.4538143,0 7.4390415,46.45383,0 7.4382168,46.4536333,0 7.437853,46.4534971,0 7.4376096,46.4533399,0 7.4375083,46.4531784,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kronenplatz</name>
    +          <description>Kronenplatz, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4433518,46.4557718,0 7.442802,46.4557537,0 7.4423281,46.4556781,0 7.4422089,46.4564137,0 7.4423398,46.456437,0 7.4423078,46.4566492,0 7.4425442,46.4567004,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Kronenplatz</name>
    +          <description>Kronenplatz, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4427674,46.4559269,0 7.442802,46.4557537,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4382168,46.4536333,0 7.4375687,46.4542067,0 7.437657,46.4544786,0 7.4381129,46.4544954,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.60993,46.555202,0 7.6100086,46.5551481,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6100086,46.5551481,0 7.610275,46.555066,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6095758,46.5550283,0 7.609867,46.5551309,0 7.60993,46.555202,0 7.6099053,46.5554036,0 7.6099572,46.5557181,0 7.6101794,46.5559656,0 7.6108008,46.5563903,0 7.611201,46.5569152,0 7.6114692,46.5571666,0 7.6117685,46.5573938,0 7.612146,46.5574221,0 7.612332,46.5574959,0 7.6126626,46.5575627,0 7.6128563,46.5576717,0 7.613043,46.557777,0 7.613162,46.557793,0 7.613359,46.557997,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6093995,46.5548684,0 7.6095758,46.5550283,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5655203,46.4480124,0 7.5666754,46.448165,0 7.5673192,46.4478693,0 7.5689671,46.4480468,0 7.5695336,46.4481946,0 7.5695508,46.4500988,0 7.5691388,46.4506665,0 7.5698941,46.4505482,0 7.571027,46.4506665,0 7.5716794,46.4504891,0 7.571954,46.4505305,0 7.5729153,46.4498386,0 7.5735419,46.4491881,0 7.5738251,46.4491408,0 7.5767777,46.4471419,0 7.5782368,46.4464796,0 7.5799277,46.4461365,0 7.5812409,46.4461957,0 7.5844767,46.445971,0 7.5848286,46.4460833,0 7.5858243,46.4459355,0 7.5871375,46.4459355,0 7.5876954,46.4458113,0 7.5882103,46.4458349,0 7.5883391,46.4457344,0 7.5885708,46.4457699,0 7.5887854,46.4456575,0 7.5897639,46.4458704,0 7.5897553,46.4456989,0 7.5898583,46.4457521,0 7.5901244,46.4456871,0 7.5909655,46.4452672,0 7.5923474,46.4448946,0 7.592356,46.4448,0 7.592768,46.4446048,0 7.5932658,46.4445811,0 7.5939181,46.4442322,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5939181,46.4442322,0 7.5959351,46.4431026,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5959351,46.4431026,0 7.5961202,46.4418992,0 7.5961202,46.4410298,0 7.595837,46.440184,0 7.5952104,46.4388413,0 7.5955194,46.4386284,0 7.5955366,46.4382735,0 7.5956911,46.4379541,0 7.5960086,46.4378476,0 7.5962232,46.4374336,0 7.5963949,46.4374336,0 7.5964635,46.437197,0 7.596867,46.4373153,0 7.598163,46.4381493,0 7.5981802,46.4380251,0 7.5983862,46.4380251,0 7.5989269,46.4382794,0 7.5993475,46.4388118,0 7.5999225,46.4390306,0 7.6001886,46.4393323,0 7.6002916,46.4396103,0 7.6005491,46.4395748,0 7.6009868,46.439764,0 7.6019567,46.439486,0 7.6027034,46.4389951,0 7.6029008,46.4388768,0 7.60333,46.4383681,0 7.6034845,46.438309,0 7.6036864,46.4378485,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5612557,46.4542097,0 7.56213,46.454218,0 7.562429,46.454144,0 7.563113,46.454125,0 7.563817,46.454263,0 7.563935,46.454456,0 7.564016,46.454315,0 7.564211,46.454463,0 7.564278,46.454308,0 7.564431,46.454381,0 7.564714,46.454334,0 7.564919,46.454464,0 7.564952,46.454256,0 7.565156,46.454369,0 7.565617,46.454428,0 7.5655305,46.4542175,0 7.566296,46.4542338,0 7.5666072,46.454436,0 7.566648,46.454824,0 7.56674,46.454724,0 7.5668857,46.4548685,0 7.56691,46.454801,0 7.567052,46.45484,0 7.567039,46.4546492,0 7.566979,46.454446,0 7.566903,46.454271,0 7.56665,46.454115,0 7.566702,46.453914,0 7.566393,46.453903,0 7.566252,46.453641,0 7.5659897,46.4535926,0 7.5657596,46.4534666,0 7.565605,46.453159,0 7.5654002,46.4529654,0 7.5652784,46.453026,0 7.5651459,46.4530153,0 7.5649962,46.453114,0 7.5648026,46.4530815,0 7.5646282,46.4530737,0 7.5646996,46.4529048,0 7.5645424,46.4527662,0 7.5645097,46.4524439,0 7.5645156,46.4522983,0 7.5643691,46.4522325,0 7.5643648,46.4520902,0 7.5642225,46.4520855,0 7.564087,46.452081,0 7.563577,46.451855,0 7.563358,46.451667,0 7.562807,46.451031,0 7.562811,46.450662,0 7.562459,46.450526,0 7.562297,46.450575,0 7.562138,46.450756,0 7.56196,46.450604,0 7.561888,46.450871,0 7.561585,46.450997,0 7.561227,46.450721,0 7.561186,46.450498,0 7.560727,46.450533,0 7.56057,46.450445,0 7.56074,46.450354,0 7.560304,46.450156,0 7.560038,46.450138,0 7.560136,46.449986,0 7.5603797,46.4499063,0 7.5604193,46.4498809,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5603797,46.4499063,0 7.560335,46.449238,0 7.560977,46.449165,0 7.561242,46.449273,0 7.560913,46.448711,0 7.560942,46.448252,0 7.560985,46.448126,0 7.561464,46.447771,0 7.5615495,46.4474679,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.610275,46.555066,0 7.6105103,46.555047,0 7.6107934,46.5551847,0 7.6109108,46.5551734,0 7.6110474,46.5552324,0 7.6114422,46.5551757,0 7.6119279,46.5551549,0 7.6122496,46.5548187,0 7.6123306,46.5545839,0 7.612552,46.554217,0 7.612904,46.554303,0 7.61296,46.554373,0 7.6130079,46.5544692,0 7.6130907,46.5545095,0 7.6132761,46.5545054,0 7.6134222,46.5545429,0 7.6135505,46.5546015,0 7.613591,46.554708,0 7.6135566,46.5548319,0 7.6135434,46.5549507,0 7.613595,46.5551225,0 7.6136905,46.555167,0 7.613754,46.55517,0 7.6138063,46.5551517,0 7.613901,46.555171,0 7.6139027,46.555118,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.617521,46.559201,0 7.6174601,46.5593795,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4246182,46.4549791,0 7.411038,46.4488524,0 7.4096398,46.4473342,0 7.4094562,46.4479668,0 7.4081709,46.4475873,0 7.4071281,46.4487423,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5359591,46.4821973,0 7.5356587,46.4821315,0 7.5352401,46.4817035,0 7.5341426,46.4802405,0 7.5343946,46.47985,0 7.5341784,46.4798098,0 7.5334093,46.4782263,0 7.5320814,46.4778883,0 7.5322482,46.4776474,0 7.5324047,46.4775918,0 7.5324047,46.4774868,0 7.5322359,46.477493,0 7.5319641,46.4772829,0 7.5311872,46.4771753,0 7.5311809,46.4769707,0 7.5303944,46.4766186,0 7.5299839,46.4762294,0 7.5298295,46.47583,0 7.5293827,46.4753729,0 7.5295227,46.4751876,0 7.5292591,46.475342,0 7.5294671,46.4757332,0 7.5293889,46.4760874,0 7.5295206,46.4765218,0 7.5292797,46.4765918,0 7.527116,46.4756238,0 7.5268425,46.4756158,0 7.5267259,46.4754911,0 7.5259256,46.4754067,0 7.5254631,46.4754831,0 7.5253666,46.4753584,0 7.525447,46.4752619,0 7.5251052,46.4753504,0 7.5249725,46.4752498,0 7.5247593,46.47529,0 7.5245301,46.4750689,0 7.5222742,46.4741891,0 7.5200561,46.4732089,0 7.5179775,46.47215,0 7.517934,46.471763,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.469009,46.453963,0 7.468853,46.454502,0 7.468722,46.454633,0 7.468855,46.454838,0 7.468892,46.455254,0 7.468526,46.45547,0 7.468214,46.455827,0 7.467811,46.456011,0 7.467729,46.456012,0 7.467757,46.45589,0 7.4676573,46.4558604,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4563512,46.4596817,0 7.455519,46.460138,0 7.454885,46.460326,0 7.454269,46.460142,0 7.453772,46.460157,0 7.453588,46.460227,0 7.453646,46.46034,0 7.453343,46.460297,0 7.45308,46.460158,0 7.452336,46.460143,0 7.452167,46.460202,0 7.4516199,46.4601237,0 7.451444,46.460176,0 7.451314,46.459955,0 7.451083,46.460086,0 7.450785,46.459827,0 7.450364,46.460159,0 7.450148,46.460235,0 7.449941,46.460206,0 7.449216,46.460572,0 7.449004,46.460411,0 7.44851,46.460273,0 7.448482,46.460149,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.462649,46.457567,0 7.462344,46.457799,0 7.461469,46.458186,0 7.460724,46.458266,0 7.459338,46.45872,0 7.458959,46.458929,0 7.4592,46.459053,0 7.458746,46.459142,0 7.458955,46.45922,0 7.458388,46.459277,0 7.458083,46.459431,0 7.457836,46.45934,0 7.457735,46.459414,0 7.457338,46.459278,0 7.45734,46.459367,0 7.457047,46.459355,0 7.4569312,46.4594356,0 7.4568436,46.4593926,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5805282,46.4980101,0 7.580457,46.497915,0 7.580459,46.4978244,0 7.580511,46.497795,0 7.580503,46.497773,0 7.580458,46.497732,0 7.5804891,46.4976849,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5919258,46.5139835,0 7.59165,46.514158,0 7.591592,46.514182,0 7.591323,46.514218,0 7.5911034,46.5143796,0 7.590773,46.514376,0 7.590312,46.514486,0 7.590308,46.514779,0 7.59027,46.515012,0 7.590204,46.515141,0 7.590222,46.51527,0 7.590202,46.515354,0 7.5903489,46.5153277,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5892426,46.5172029,0 7.589245,46.51707,0 7.5896037,46.5169541,0 7.590129,46.516719,0 7.590166,46.516508,0 7.590272,46.516442,0 7.590262,46.516414,0 7.590288,46.516298,0 7.590159,46.516075,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6058722,46.5395795,0 7.605907,46.539608,0 7.606061,46.539669,0 7.606172,46.539786,0 7.606187,46.539933,0 7.606162,46.540082,0 7.606252,46.540169,0 7.606368,46.540183,0 7.606546,46.540228,0 7.606795,46.540172,0 7.6069728,46.5401954,0 7.6069839,46.540212,0 7.606977,46.54025,0 7.606945,46.540322,0 7.606899,46.540361,0 7.606783,46.540417,0 7.606721,46.540496,0 7.606687,46.54052,0 7.606704,46.540646,0 7.606769,46.540707,0 7.606912,46.540757,0 7.60689,46.540889,0 7.606769,46.541122,0 7.606776,46.541186,0 7.606741,46.541226,0 7.606698,46.541421,0 7.606723,46.541467,0 7.606758,46.541487,0 7.606836,46.541643,0 7.606804,46.541701,0 7.606704,46.541808,0 7.606563,46.541924,0 7.606524,46.54199,0 7.606496,46.54206,0 7.606482,46.542063,0 7.606335,46.542319,0 7.606275,46.542406,0 7.606165,46.542443,0 7.606051,46.542553,0 7.605977,46.542663,0 7.605846,46.542743,0 7.60584,46.542865,0 7.605849,46.542887,0 7.605854,46.543004,0 7.605862,46.543182,0 7.605813,46.543282,0 7.605779,46.543294,0 7.605665,46.543297,0 7.605658,46.543321,0 7.605681,46.54336,0 7.605737,46.543391,0 7.605848,46.543431,0 7.605892,46.543428,0 7.605933,46.54344,0 7.605991,46.543483,0 7.60606,46.543547,0 7.606112,46.543597,0 7.606128,46.54365,0 7.606131,46.543807,0 7.606054,46.543956,0 7.6060019,46.5440194,0 7.605943,46.544046,0 7.605853,46.544099,0 7.605849,46.544168,0 7.605627,46.544263,0 7.605574,46.544402,0 7.605544,46.544518,0 7.605557,46.544576,0 7.605547,46.544633,0 7.605483,46.544684,0 7.605391,46.54472,0 7.605297,46.544742,0 7.605232,46.544778,0 7.605228,46.5448,0 7.605172,46.544869,0 7.6050221,46.5448767,0 7.604923,46.544864,0 7.60484,46.544879,0 7.604813,46.544902,0 7.604666,46.544998,0 7.604385,46.545125,0 7.6043368,46.5451739,0 7.604278,46.545281,0 7.60427,46.54534,0 7.604196,46.545481,0 7.604145,46.545502,0 7.604087,46.545512,0 7.603974,46.54558,0 7.603963,46.545603,0 7.603971,46.545623,0 7.603991,46.545636,0 7.604007,46.545671,0 7.604075,46.545699,0 7.604147,46.545758,0 7.604227,46.545806,0 7.604331,46.545844,0 7.604501,46.545838,0 7.60457,46.545911,0 7.604731,46.545867,0 7.604802,46.545883,0 7.604924,46.545865,0 7.604943,46.545883,0 7.605024,46.545883,0 7.605061,46.545915,0 7.605529,46.545902,0 7.605706,46.545864,0 7.605893,46.545934,0 7.606173,46.545897,0 7.6063485,46.5458893,0 7.6064439,46.5457843,0 7.606512,46.545831,0 7.606506,46.545981,0 7.606369,46.546097,0 7.606301,46.546263,0 7.606217,46.546344,0 7.606064,46.546443,0 7.606008,46.546533,0 7.606166,46.546699,0 7.606052,46.546836,0 7.6061285,46.5469487,0 7.606261,46.546939,0 7.606655,46.546994,0 7.607231,46.547225,0 7.607152,46.547353,0 7.607194,46.54744,0 7.60725,46.547455,0 7.607299,46.547533,0 7.607272,46.54762,0 7.607157,46.547774,0 7.607063,46.547823,0 7.607008,46.54784,0 7.606921,46.547907,0 7.606908,46.547935,0 7.607034,46.547886,0 7.607103,46.547916,0 7.6077006,46.5479132,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5816826,46.510603,0 7.5816199,46.5104057,0 7.5815976,46.510255,0 7.5814942,46.5099865,0 7.5814306,46.5099385,0 7.5813666,46.5097595,0 7.5812168,46.5096608,0 7.581074,46.509605,0 7.580956,46.509462,0 7.58117,46.509261,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5817563,46.5106037,0 7.5816826,46.510603,0 7.581587,46.510645,0 7.5815173,46.5107508,0 7.581501,46.510818,0 7.581546,46.5109334,0 7.5817095,46.5110604,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5820585,46.510608,0 7.5817563,46.5106037,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5751846,46.4916084,0 7.575377,46.491636,0 7.575561,46.491752,0 7.5756106,46.4917412,0 7.575728,46.491716,0 7.575891,46.491602,0 7.576027,46.491461,0 7.576068,46.491431,0 7.5763871,46.4912304,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5785473,46.524736,0 7.5785046,46.5247628,0 7.5783963,46.5246919,0 7.578219,46.52477,0 7.578012,46.524791,0 7.577872,46.524792,0 7.577779,46.524725,0 7.5777363,46.5246185,0 7.577476,46.524428,0 7.577197,46.524302,0 7.577086,46.524206,0 7.5770596,46.5241235,0 7.5771338,46.5239975,0 7.5771004,46.523902,0 7.5770609,46.5237564,0 7.576847,46.5235427,0 7.576721,46.523295,0 7.576719,46.523211,0 7.5764009,46.5230245,0 7.5762273,46.5227649,0 7.576096,46.522687,0 7.5759724,46.5226336,0 7.5757443,46.5226509,0 7.5756484,46.522612,0 7.5758244,46.5224861,0 7.575548,46.522413,0 7.5752814,46.5223836,0 7.5750582,46.522382,0 7.5749471,46.5223416,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6212158,46.4102508,0 7.6212492,46.4100544,0 7.6211982,46.4097513,0 7.6210007,46.4091406,0 7.6207267,46.4087452,0 7.6204904,46.4083771,0 7.620217,46.4075677,0 7.620217,46.4073832,0 7.6198856,46.4070185,0 7.6198283,46.4067857,0 7.619621,46.4066037,0 7.619344,46.4063771,0 7.6189554,46.4061266,0 7.618675,46.4058191,0 7.6178658,46.4053226,0 7.6174956,46.405028,0 7.6149472,46.4029894,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.605321,46.550618,0 7.6061811,46.5507498,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6047518,46.55048,0 7.605321,46.550618,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.605639,46.551512,0 7.605518,46.551205,0 7.605264,46.550932,0 7.605187,46.550759,0 7.6052305,46.5506929,0 7.605321,46.550618,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6172232,46.5604252,0 7.617237,46.560489,0 7.6173791,46.5605246,0 7.6174549,46.560685,0 7.617571,46.560749,0 7.617485,46.560801,0 7.617071,46.560756,0 7.6169366,46.5607994,0 7.6169276,46.5608025,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.613359,46.557997,0 7.613697,46.558202,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.613697,46.558202,0 7.6138249,46.5582483,0 7.6139093,46.5582304,0 7.6140763,46.5582933,0 7.614314,46.558345,0 7.614538,46.558564,0 7.614538,46.558685,0 7.614618,46.558742,0 7.614736,46.559067,0 7.615214,46.559535,0 7.616327,46.560016,0 7.6164317,46.5601639,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6070675,46.5503877,0 7.6072145,46.5505602,0 7.6073825,46.5506985,0 7.6073485,46.5507163,0 7.6068988,46.5507717,0 7.6068687,46.550799,0 7.606966,46.5508537,0 7.6070453,46.5509456,0 7.6069006,46.5509478,0 7.6065934,46.5508793,0 7.6064744,46.5508128,0 7.6061811,46.5507498,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5453714,46.5078755,0 7.545088,46.507775,0 7.5449722,46.5077871,0 7.5448429,46.507819,0 7.5445859,46.5078239,0 7.5442708,46.507799,0 7.544171,46.5077577,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5596756,46.5135221,0 7.5598557,46.5136074,0 7.5600289,46.5136176,0 7.5602821,46.5135897,0 7.5605638,46.5135813,0 7.5607673,46.5135679,0 7.5610239,46.5134916,0 7.5612473,46.5134206,0 7.5614853,46.5133285,0 7.5616609,46.5132468,0 7.5621499,46.5130522,0 7.562457,46.512863,0 7.562552,46.512868,0 7.562705,46.51281,0 7.562819,46.512666,0 7.562913,46.512718,0 7.563163,46.512741,0 7.56333,46.51267,0 7.563476,46.512661,0 7.563577,46.512724,0 7.563753,46.512733,0 7.564089,46.512938,0 7.564319,46.512998,0 7.56442,46.513007,0 7.564595,46.513001,0 7.56478,46.513029,0 7.565151,46.513025,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6260233,46.4823623,0 7.6258492,46.4823721,0 7.6255624,46.482439,0 7.6255071,46.4823834,0 7.6254122,46.4823258,0 7.6253676,46.4823044,0 7.625279,46.4822506,0 7.6251996,46.4822146,0 7.6250474,46.4821868,0 7.6250248,46.4823099,0 7.6247359,46.4822272,0 7.6247046,46.4824268,0 7.6245089,46.4823389,0 7.6243517,46.4822979,0 7.6242366,46.4822368,0 7.6242534,46.4824095,0 7.6242673,46.4825683,0 7.624107,46.4827243,0 7.623839,46.4830581,0 7.6236473,46.4832026,0 7.6233765,46.4833475,0 7.6232286,46.4834129,0 7.6228172,46.483644,0 7.622597,46.4838189,0 7.6224068,46.4839382,0 7.6221757,46.4841363,0 7.6218884,46.4843664,0 7.6217534,46.4844727,0 7.6215819,46.4845375,0 7.6213635,46.484593,0 7.6209341,46.4846251,0 7.6207812,46.4845936,0 7.6207351,46.4845971,0 7.6204754,46.4847285,0 7.6203775,46.48474,0 7.6202873,46.4847746,0 7.6202196,46.4847767,0 7.6200436,46.4847224,0 7.6198617,46.4847643,0 7.6195668,46.4847474,0 7.6193562,46.4847566,0 7.6190013,46.4847732,0 7.6186967,46.484811,0 7.6181212,46.4848624,0 7.6179412,46.4849032,0 7.6176836,46.4849937,0 7.6174843,46.4850212,0 7.6171033,46.4850063,0 7.6167943,46.4849573,0 7.6166793,46.4849163,0 7.6164843,46.4849707,0 7.6162531,46.4850023,0 7.6161673,46.4850224,0 7.6160972,46.4850741,0 7.6159545,46.4851447,0 7.6157593,46.4852007,0 7.6155646,46.4852336,0 7.6153623,46.4851233,0 7.6151993,46.4850703,0 7.6148583,46.4850343,0 7.6146363,46.4850853,0 7.6144973,46.4851353,0 7.6143253,46.4850743,0 7.6142553,46.4850173,0 7.6141723,46.4850003,0 7.6141023,46.4850283,0 7.6140603,46.4849993,0 7.6139823,46.4850153,0 7.61393,46.4850566,0 7.6138763,46.4850313,0 7.6138204,46.485091,0 7.6138023,46.4850633,0 7.6137593,46.4850763,0 7.6137203,46.4851253,0 7.6136753,46.4850863,0 7.6136393,46.4851503,0 7.6135663,46.4851123,0 7.6134953,46.4851663,0 7.6134643,46.4851073,0 7.6133643,46.4850353,0 7.6131953,46.4851953,0 7.6131063,46.4852473,0 7.6130853,46.4852793,0 7.6130183,46.4852923,0 7.6129953,46.4852803,0 7.6129803,46.4852453,0 7.6129063,46.4852833,0 7.6128523,46.4852753,0 7.6127303,46.4853543,0 7.6126483,46.4853683,0 7.6125433,46.4852773,0 7.6125053,46.4852903,0 7.6124663,46.4854113,0 7.6123263,46.4853613,0 7.6122583,46.4854193,0 7.612167,46.4853973,0 7.6120713,46.4855123,0 7.6119925,46.4855088,0 7.6119353,46.4854393,0 7.6118633,46.4854613,0 7.6118213,46.4854213,0 7.6114833,46.4856083,0 7.6113663,46.4856453,0 7.6112993,46.4857083,0 7.6113003,46.4857633,0 7.6113783,46.4858083,0 7.6114313,46.4858753,0 7.6114213,46.4859843,0 7.6113973,46.4860683,0 7.6112723,46.4862103,0 7.6112803,46.4862563,0 7.6112453,46.4863273,0 7.6112083,46.4862903,0 7.6111353,46.4862603,0 7.6109813,46.4862273,0 7.6108473,46.4862523,0 7.6105723,46.4864223,0 7.6105023,46.4864773,0 7.6104753,46.4865503,0 7.6104843,46.4865943,0 7.6103863,46.4866243,0 7.6103183,46.4866933,0 7.6101963,46.4867383,0 7.6098853,46.4869373,0 7.6098613,46.4870003,0 7.6096883,46.4871263,0 7.6096513,46.4871703,0 7.6094163,46.4872503,0 7.6093613,46.4872833,0 7.6093253,46.4873503,0 7.6091903,46.4874413,0 7.6088393,46.4876083,0 7.6083365,46.4877988,0 7.6080993,46.4878713,0 7.6079583,46.4879443,0 7.6076133,46.4880403,0 7.6072793,46.4881793,0 7.6070183,46.4882273,0 7.6063493,46.4884033,0 7.6062343,46.4884653,0 7.6059453,46.4885003,0 7.6057283,46.4885073,0 7.6053583,46.4886603,0 7.6049373,46.4887813,0 7.6048723,46.4887823,0 7.6046523,46.4888563,0 7.6046073,46.4888983,0 7.6044753,46.4888563,0 7.6043778,46.4887387,0 7.6043431,46.4886956,0 7.6043857,46.488565,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6155646,46.4852336,0 7.615535,46.485289,0 7.615492,46.485321,0 7.615502,46.485336,0 7.615555,46.485348,0 7.61559,46.485422,0 7.615556,46.485471,0 7.615536,46.48548,0 7.615488,46.485564,0 7.615401,46.485584,0 7.615524,46.485579,0 7.61562,46.485595,0 7.615819,46.485559,0 7.615941,46.485561,0 7.616144,46.485543,0 7.616285,46.485512,0 7.616448,46.485431,0 7.616671,46.485489,0 7.616797,46.485459,0 7.6168,46.485484,0 7.616873,46.485518,0 7.616883,46.485555,0 7.616845,46.485562,0 7.616772,46.485599,0 7.616718,46.485614,0 7.616657,46.485688,0 7.616609,46.48571,0 7.61657,46.48576,0 7.61656,46.485821,0 7.616501,46.485897,0 7.616542,46.485957,0 7.616525,46.485996,0 7.616622,46.486006,0 7.616723,46.485977,0 7.616802,46.485984,0 7.616924,46.486036,0 7.616961,46.486095,0 7.617153,46.486238,0 7.61727,46.486222,0 7.617359,46.486187,0 7.617604,46.486188,0 7.61774,46.486219,0 7.617793,46.486254,0 7.617831,46.486349,0 7.617833,46.48642,0 7.617861,46.486485,0 7.618009,46.486485,0 7.618205,46.486437,0 7.618295,46.486464,0 7.618329,46.486456,0 7.618436,46.486472,0 7.618602,46.486572,0 7.618792,46.486605,0 7.618953,46.486676,0 7.6192585,46.4867677,0 7.619325,46.48681,0 7.619549,46.48683,0 7.6197,46.486865,0 7.6199131,46.4869774,0 7.620189,46.487069,0 7.620236,46.487135,0 7.620337,46.487182,0 7.620472,46.487222,0 7.620583,46.487237,0 7.620652,46.487236,0 7.62087,46.487189,0 7.620953,46.48718,0 7.621037,46.487226,0 7.621099,46.48729,0 7.621169,46.487408,0 7.621125,46.487441,0 7.621083,46.487496,0 7.621073,46.487531,0 7.621001,46.487586,0 7.620807,46.487651,0 7.620689,46.48773,0 7.6205198,46.4878324,0 7.620482,46.487876,0 7.620496,46.487948,0 7.620563,46.487987,0 7.620704,46.488001,0 7.620883,46.488074,0 7.621437,46.48835,0 7.621532,46.488359,0 7.621586,46.488383,0 7.62157,46.488455,0 7.621615,46.48847,0 7.621711,46.488469,0 7.621795,46.488546,0 7.6219173,46.4885762,0 7.622028,46.4885985,0 7.6222705,46.4887699,0 7.622325,46.48893,0 7.622321,46.489149,0 7.622263,46.48936,0 7.622314,46.489402,0 7.62232,46.48944,0 7.622254,46.489487,0 7.622105,46.489527,0 7.621746,46.489663,0 7.621668,46.489673,0 7.621407,46.490046,0 7.621412,46.490111,0 7.6214,46.49014,0 7.621378,46.490162,0 7.62133,46.490311,0 7.621239,46.490413,0 7.621209,46.490417,0 7.621155,46.490499,0 7.6210982,46.4905384,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6210982,46.4905384,0 7.620964,46.4905577,0 7.6209202,46.490512,0 7.6207783,46.4905281,0 7.6206561,46.4906049,0 7.6205331,46.4906336,0 7.6203543,46.4906494,0 7.6202419,46.4906959,0 7.6200854,46.490772,0 7.6200422,46.4908145,0 7.6199348,46.490882,0 7.6198603,46.4909638,0 7.6198001,46.4910073,0 7.6198169,46.4910749,0 7.61978,46.491125,0 7.619825,46.491157,0 7.6199204,46.491208,0 7.6199692,46.4912974,0 7.6199436,46.4913231,0 7.6200376,46.4914214,0 7.6200257,46.4914703,0 7.620043,46.4915465,0 7.6200862,46.4916029,0 7.6201401,46.491697,0 7.6202303,46.4919044,0 7.620279,46.49209,0 7.6202839,46.4921193,0 7.620283,46.492293,0 7.6202503,46.492356,0 7.6202655,46.4924072,0 7.6202963,46.492469,0 7.6203822,46.4924904,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.590707,46.478723,0 7.590711,46.478539,0 7.590772,46.478439,0 7.590744,46.478278,0 7.590758,46.478056,0 7.59075,46.477896,0 7.5907974,46.4776477,0 7.59091,46.477543,0 7.590919,46.47749,0 7.590799,46.477172,0 7.590746,46.477112,0 7.590719,46.4770114,0 7.590691,46.4769634,0 7.590748,46.476776,0 7.590759,46.476723,0 7.5907497,46.476653,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5907497,46.476653,0 7.59074,46.47658,0 7.590712,46.476443,0 7.590647,46.476366,0 7.590672,46.475923,0 7.590589,46.475456,0 7.5904172,46.4757219,0 7.590124,46.47586,0 7.59005,46.475799,0 7.58971,46.475946,0 7.589323,46.475984,0 7.589385,46.476094,0 7.589159,46.476482,0 7.5888497,46.4765801,0 7.588444,46.476598,0 7.588278,46.476609,0 7.587762,46.476493,0 7.587559,46.476305,0 7.587223,46.476143,0 7.586993,46.476253,0 7.586734,46.476292,0 7.586469,46.47637,0 7.586058,46.476531,0 7.5858106,46.4766097,0 7.585545,46.476709,0 7.585407,46.476925,0 7.585245,46.47706,0 7.5850914,46.4772674,0 7.585041,46.477479,0 7.5848927,46.4776871,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5897212,46.4818723,0 7.589943,46.481844,0 7.5900556,46.4818181,0 7.590092,46.481759,0 7.589998,46.481475,0 7.58998,46.481335,0 7.590041,46.481122,0 7.5900774,46.481021,0 7.590016,46.480837,0 7.5900969,46.4806249,0 7.590166,46.480569,0 7.590156,46.4804,0 7.590309,46.480142,0 7.590366,46.480088,0 7.590412,46.479927,0 7.590453,46.479764,0 7.590448,46.479591,0 7.590517,46.479423,0 7.590573,46.479359,0 7.590686,46.479229,0 7.5907469,46.4790091,0 7.590726,46.478903,0 7.590707,46.478723,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6011402,46.490361,0 7.6011243,46.4902803,0 7.6011843,46.4901643,0 7.6014333,46.4899023,0 7.6016833,46.4894923,0 7.6017903,46.4891893,0 7.6017893,46.4889183,0 7.6015133,46.4888943,0 7.6012443,46.4886913,0 7.6012193,46.4886393,0 7.6012223,46.4882563,0 7.6012023,46.4882303,0 7.6009833,46.4882253,0 7.6009323,46.4882113,0 7.6006563,46.4882393,0 7.6003743,46.4883773,0 7.6001003,46.4884293,0 7.5999913,46.4883763,0 7.5997933,46.4883763,0 7.5996383,46.4884183,0 7.5995314,46.4883839,0 7.5993911,46.4885099,0 7.5992613,46.4884893,0 7.5990153,46.4885893,0 7.5989113,46.4885563,0 7.5987673,46.4886813,0 7.5987023,46.4886663,0 7.5985929,46.4886894,0 7.5984759,46.4887644,0 7.5984259,46.4889354,0 7.5983769,46.4888704,0 7.5982989,46.4888604,0 7.5982569,46.4887774,0 7.5982034,46.4887503,0 7.5981799,46.4887724,0 7.5981329,46.4887394,0 7.5980599,46.4887434,0 7.5980329,46.4888104,0 7.5979589,46.4885316,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5979589,46.4885316,0 7.5978355,46.488491,0 7.5977604,46.4884577,0 7.5976853,46.488454,0 7.5975887,46.488491,0 7.5974899,46.4885634,0 7.5972776,46.4886018,0 7.5970898,46.4887237,0 7.5969075,46.488768,0 7.5967733,46.4888603,0 7.5966285,46.4888714,0 7.5965319,46.4887827,0 7.5964461,46.4887163,0 7.5963764,46.4887089,0 7.5963281,46.4886055,0 7.596404,46.4883944,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5922214,46.488268,0 7.5923677,46.4881323,0 7.5925617,46.4879943,0 7.5926257,46.4879633,0 7.5927327,46.4878773,0 7.5928177,46.4877743,0 7.5928317,46.4877043,0 7.5927997,46.4874863,0 7.5927757,46.4874453,0 7.5924828,46.4871774,0 7.5922205,46.4870198,0 7.592117,46.4869065,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4419793,46.4126531,0 7.4419058,46.4125374,0 7.4412531,46.4122448,0 7.4409621,46.4121367,0 7.4400956,46.4120547,0 7.4397671,46.4120185,0 7.4393406,46.4117801,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4396951,46.4114482,0 7.4412106,46.4111941,0 7.4415882,46.4111763,0 7.4425324,46.4115077,0 7.4430285,46.4114178,0 7.4434583,46.4112146,0 7.4437284,46.4109221,0 7.4441582,46.410684,0 7.4448156,46.4103702,0 7.4448583,46.4102856,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4448583,46.4102856,0 7.4453851,46.4101867,0 7.4457995,46.4099307,0 7.4460429,46.4095548,0 7.4466952,46.4082055,0 7.4468089,46.4080037,0 7.44678,46.4078413,0 7.4465215,46.407632,0 7.4462232,46.4075539,0 7.4462137,46.4073898,0 7.4464603,46.4072978,0 7.4464994,46.4071501,0 7.4465756,46.4070943,0 7.4464327,46.4070516,0 7.4463232,46.4069827,0 7.4462423,46.4067824,0 7.4464797,46.406528,0 7.4467295,46.4064004,0 7.447149,46.4061475,0 7.4472186,46.4059651,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4411642,46.3998745,0 7.4411731,46.3997126,0 7.4414034,46.3994827,0 7.4414034,46.3994032,0 7.4415671,46.3992862,0 7.4417733,46.3992444,0 7.44184,46.3991273,0 7.4417611,46.398935,0 7.4418824,46.3986925,0 7.4421067,46.3983329,0 7.4421916,46.398036,0 7.4423553,46.3976848,0 7.4425069,46.3974548,0 7.4427736,46.3970869,0 7.4427069,46.3969447,0 7.4427615,46.3967106,0 7.4428949,46.3964346,0 7.4429858,46.3962548,0 7.4429797,46.3961378,0 7.4427979,46.396121,0 7.4422643,46.3962047,0 7.4420582,46.3962716,0 7.4412337,46.3962966,0 7.440876,46.3962381,0 7.4410942,46.3961127,0 7.4412943,46.3958702,0 7.4414641,46.3957782,0 7.4416884,46.3955022,0 7.4417187,46.3953475,0 7.4417066,46.3951802,0 7.4415429,46.3953182,0 7.4413913,46.3953559,0 7.4411973,46.3953349,0 7.4411912,46.395289,0 7.4413307,46.395243,0 7.4413913,46.3951092,0 7.4413338,46.3950921,0 7.4412586,46.3951415,0 7.4411655,46.3951292,0 7.4412443,46.3950823,0 7.4411691,46.3950304,0 7.4413696,46.3950131,0 7.4412228,46.394902,0 7.4412908,46.3948699,0 7.4414841,46.3948576,0 7.441656,46.3948897,0 7.4415378,46.3947218,0 7.4413194,46.3945465,0 7.4413489,46.394489,0 7.4414744,46.3945078,0 7.4415672,46.3945642,0 7.4418946,46.394632,0 7.4421838,46.394647,0 7.4419055,46.3945228,0 7.4424075,46.3944927,0 7.4422547,46.394425,0 7.4420801,46.3943911,0 7.441911,46.3942293,0 7.441649,46.3941992,0 7.4414854,46.3940675,0 7.4417309,46.3941465,0 7.4418728,46.3941503,0 7.4419273,46.3941089,0 7.4420583,46.3941277,0 7.442151,46.3942105,0 7.4422656,46.394218,0 7.4426803,46.3941013,0 7.4427076,46.3939847,0 7.4428604,46.3939245,0 7.4429859,46.3939056,0 7.4431441,46.3938078,0 7.4433296,46.3937815,0 7.4435752,46.3938454,0 7.4438589,46.3940185,0 7.4440117,46.3941013,0 7.4443773,46.3942067,0 7.4445519,46.3942519,0 7.4447047,46.3942105,0 7.4450102,46.3942669,0 7.4452121,46.3942632,0 7.4454413,46.3941653,0 7.4455668,46.3941728,0 7.4456977,46.3940938,0 7.445796,46.3940938,0 7.4459433,46.394154,0 7.4463853,46.3942406,0 7.4464671,46.3943271,0 7.4466144,46.3943271,0 7.4467181,46.3943083,0 7.4468982,46.3943685,0 7.4469636,46.3944551,0 7.4468927,46.3945906,0 7.4470182,46.3946395,0 7.4472201,46.3946583,0 7.4473892,46.3945906,0 7.4477494,46.3945341,0 7.4478421,46.3945454,0 7.447924,46.3945793,0 7.4479785,46.394504,0 7.448104,46.3945078,0 7.4482241,46.3945341,0 7.4483114,46.3944438,0 7.448426,46.3945002,0 7.4484914,46.394425,0 7.4486333,46.3944739,0 7.4485951,46.3946056,0 7.4487315,46.3946169,0 7.4487534,46.3946771,0 7.4486933,46.3947373,0 7.4487861,46.3947975,0 7.4489552,46.3947938,0 7.4488734,46.394632,0 7.4491844,46.3947147,0 7.4496482,46.3947599,0 7.4497955,46.3947298,0 7.449921,46.3946696,0 7.4497628,46.3946508,0 7.4495991,46.3945943,0 7.4494845,46.3944965,0 7.4492172,46.3944099,0 7.4491748,46.3943592,0 7.4491922,46.3943312,0 7.4492415,46.3943032,0 7.4490414,46.3942392,0 7.4491081,46.3941812,0 7.4490733,46.3940272,0 7.4490211,46.3939532,0 7.4489346,46.3938937,0 7.4487649,46.3939565,0 7.4485469,46.3939165,0 7.448529,46.3938473,0 7.4486543,46.3938029,0 7.4486793,46.3937634,0 7.4486264,46.3937261,0 7.4485247,46.3937017,0 7.4487236,46.3936285,0 7.448896,46.3936072,0 7.4489755,46.3935584,0 7.4488253,46.3935432,0 7.4488739,46.3934791,0 7.4487148,46.3934852,0 7.448812,46.3933907,0 7.4489313,46.3934121,0 7.4490286,46.3933938,0 7.448896,46.3932993,0 7.4490639,46.3932627,0 7.4492495,46.3931621,0 7.4494042,46.3931652,0 7.449749,46.3932871,0 7.4498904,46.3932993,0 7.4500451,46.3934151,0 7.4501512,46.3934365,0 7.4502175,46.393409,0 7.4501865,46.3933603,0 7.4499921,46.3932962,0 7.4501512,46.393278,0 7.4500805,46.3932017,0 7.450213,46.3932078,0 7.4502219,46.3931194,0 7.4503898,46.3931713,0 7.4506285,46.3931682,0 7.450823,46.393092,0 7.4509556,46.3929579,0 7.4510837,46.3929122,0 7.4511677,46.3928969,0 7.4511147,46.392839,0 7.4511191,46.3927628,0 7.4510749,46.392714,0 7.45096,46.3926713,0 7.4511368,46.3926195,0 7.4511633,46.3925707,0 7.4511191,46.3925403,0 7.4508186,46.3923695,0 7.4505269,46.392269,0 7.450655,46.3922293,0 7.4507655,46.392208,0 7.4508672,46.3921104,0 7.4510395,46.3920556,0 7.4507876,46.3920129,0 7.4505887,46.3920281,0 7.4504429,46.3919794,0 7.4505931,46.3919123,0 7.4505003,46.3917934,0 7.4503766,46.3917751,0 7.4502793,46.3916715,0 7.4501114,46.3916562,0 7.4500672,46.3915831,0 7.4498727,46.3915373,0 7.4497357,46.3914367,0 7.4496341,46.3913209,0 7.449665,46.3911319,0 7.4493556,46.3912904,0 7.4492495,46.3910435,0 7.4491081,46.3908667,0 7.4491258,46.3908027,0 7.449223,46.3906929,0 7.4491877,46.3905893,0 7.4485866,46.3901869,0 7.4485291,46.3900558,0 7.4485159,46.389882,0 7.4485733,46.3897692,0 7.4487324,46.3896778,0 7.4488695,46.3895955,0 7.4490197,46.3895315,0 7.4491612,46.3892601,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4491612,46.3892601,0 7.4494042,46.3890803,0 7.4496031,46.3889065,0 7.4499788,46.3887297,0 7.4500893,46.3886626,0 7.4503014,46.3885803,0 7.4503766,46.3885895,0 7.450434,46.3886596,0 7.4505622,46.3886017,0 7.4507965,46.3884553,0 7.4509114,46.3884248,0 7.4508804,46.3886321,0 7.4508274,46.388809,0 7.4508274,46.3889614,0 7.4508672,46.3890224,0 7.4510793,46.3890772,0 7.4512075,46.3891626,0 7.4513887,46.3891687,0 7.4518086,46.3892357,0 7.4520296,46.3891748,0 7.4522726,46.3892297,0 7.4525997,46.3892205,0 7.4529533,46.3891626,0 7.4531168,46.3890528,0 7.4533334,46.388998,0 7.4536207,46.3889248,0 7.4537621,46.3889522,0 7.4538903,46.3889583,0 7.4539742,46.3889035,0 7.4542306,46.3887907,0 7.4543941,46.3887388,0 7.4544074,46.3886138,0 7.4546416,46.388687,0 7.4547786,46.3887968,0 7.4548493,46.3886687,0 7.4549598,46.3887236,0 7.4550571,46.3886626,0 7.4550482,46.388626,0 7.4553532,46.3885529,0 7.4557289,46.3884553,0 7.4559764,46.388437,0 7.4561664,46.3883456,0 7.4564095,46.3881962,0 7.4565333,46.3881931,0 7.4567366,46.3881078,0 7.4567763,46.3880102,0 7.4569576,46.3879828,0 7.4570813,46.3878883,0 7.457152,46.3876871,0 7.4571388,46.387617,0 7.4571918,46.3875407,0 7.4573553,46.3875255,0 7.4574747,46.3875529,0 7.4575542,46.3875011,0 7.4578327,46.3872633,0 7.4579432,46.3872176,0 7.4580713,46.3871901,0 7.4581332,46.3871383,0 7.4581465,46.3870591,0 7.458226,46.3870042,0 7.458447,46.386928,0 7.4584868,46.3868853,0 7.4584603,46.3868335,0 7.4583586,46.3867786,0 7.4583277,46.3867267,0 7.4583674,46.3866536,0 7.4585177,46.3865042,0 7.4585708,46.3864493,0 7.458584,46.3863944,0 7.4584912,46.3863579,0 7.4584426,46.3862908,0 7.4583763,46.3860316,0 7.4583807,46.3859646,0 7.4584912,46.3858579,0 7.4586768,46.3856719,0 7.4587917,46.3856231,0 7.4588801,46.3855225,0 7.4590304,46.3854554,0 7.4592072,46.3852908,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4592072,46.3852908,0 7.4593044,46.3851567,0 7.4594017,46.3851536,0 7.4594459,46.3851018,0 7.459437,46.3850256,0 7.4595475,46.3849311,0 7.4596492,46.3849158,0 7.4597508,46.384928,0 7.4598657,46.3848975,0 7.4601398,46.3846567,0 7.4602591,46.3846079,0 7.4603696,46.3846292,0 7.4606524,46.3845164,0 7.4608734,46.3842359,0 7.4608867,46.384117,0 7.4609,46.3840225,0 7.4610016,46.3839128,0 7.4610237,46.3837725,0 7.4612049,46.3834554,0 7.4612447,46.3833182,0 7.4613331,46.3832054,0 7.4612403,46.3830316,0 7.4612933,46.3830072,0 7.4612005,46.3829066,0 7.4612226,46.3827298,0 7.4611386,46.3825865,0 7.4611386,46.3824828,0 7.4610414,46.3823304,0 7.4611073,46.3822106,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4611073,46.3822106,0 7.4612404,46.3822517,0 7.4614568,46.3823456,0 7.4616159,46.382348,0 7.4615585,46.3822694,0 7.4616911,46.3822938,0 7.4617211,46.3821629,0 7.4618236,46.3820919,0 7.4619784,46.3822176,0 7.4622961,46.3823287,0 7.4629055,46.38253,0 7.463308,46.3826662,0 7.4634069,46.3828895,0 7.4636321,46.382777,0 7.4639005,46.38287,0 7.4639274,46.3829525,0 7.4640506,46.382901,0 7.4640671,46.3829953,0 7.4643743,46.3829246,0 7.4643871,46.3830368,0 7.4645354,46.3829903,0 7.4645177,46.3831343,0 7.4646506,46.3831618,0 7.4646264,46.3833123,0 7.4648386,46.3832036,0 7.4650285,46.3832415,0 7.4649901,46.3833249,0 7.4652644,46.3832425,0 7.4652205,46.3834252,0 7.4657122,46.3832997,0 7.4661585,46.3832997,0 7.4664332,46.3832642,0 7.4664675,46.3834418,0 7.4663216,46.3836313,0 7.4666563,46.383578,0 7.4669653,46.3834536,0 7.4672915,46.3833826,0 7.4674803,46.3831754,0 7.4675693,46.3829127,0 7.4676724,46.3828786,0 7.4678802,46.3828314,0 7.4681028,46.3828006,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4715578,46.3803844,0 7.4716905,46.3801436,0 7.4721055,46.3799782,0 7.4722339,46.3800409,0 7.472586,46.3799856,0 7.472571,46.3798732,0 7.4733791,46.3800682,0 7.4740397,46.3799886,0 7.4740981,46.3801672,0 7.4743717,46.3802091,0 7.4747225,46.3801399,0 7.4753201,46.3804002,0 7.4756569,46.3803819,0 7.4760285,46.3805054,0 7.4773344,46.3802503,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4773344,46.3802503,0 7.4775896,46.3800954,0 7.478055,46.3799769,0 7.4786994,46.3798188,0 7.4797233,46.3793595,0 7.4798307,46.3792508,0 7.4798737,46.3786976,0 7.4799238,46.3784851,0 7.4801171,46.3782629,0 7.480246,46.3778331,0 7.4804321,46.3776553,0 7.4803963,46.3775269,0 7.4810479,46.3771514,0 7.4812842,46.3769291,0 7.4815634,46.3765389,0 7.4815634,46.3763364,0 7.4819429,46.3760548,0 7.4822293,46.3759511,0 7.4822794,46.3758473,0 7.4826875,46.3756053,0 7.4829596,46.3755015,0 7.4832961,46.3752496,0 7.4832961,46.3750174,0 7.4834393,46.3747062,0 7.4836613,46.3745481,0 7.4833677,46.374311,0 7.4832031,46.3742665,0 7.48311,46.374143,0 7.4833677,46.3740541,0 7.4835038,46.3740936,0 7.4838618,46.3740195,0 7.4839763,46.3738466,0 7.4841697,46.3737923,0 7.4841553,46.3736886,0 7.4842771,46.3735947,0 7.4842484,46.3735058,0 7.4840121,46.3732489,0 7.4840837,46.3731847,0 7.4843415,46.3729821,0 7.4845635,46.3729179,0 7.4846279,46.3727944,0 7.4846422,46.3727154,0 7.4847711,46.3726857,0 7.4849215,46.3726709,0 7.4849429,46.3725424,0 7.4849501,46.3723448,0 7.4853009,46.371752,0 7.4861673,46.3711938,0 7.4864609,46.3705861,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.574892,46.4016217,0 7.5752629,46.4028429,0 7.5758871,46.4029302,0 7.5773776,46.4030385,0 7.5779149,46.4031005,0 7.5783777,46.4034398,0 7.58076,46.4045863,0 7.582163,46.4048456,0 7.582205,46.4049976,0 7.5843654,46.405166,0 7.5848908,46.406216,0 7.5892869,46.4093376,0 7.5906357,46.409781,0 7.5916796,46.4096886,0 7.5924095,46.4102984,0 7.5930562,46.4102984,0 7.5939153,46.4108249,0 7.5942811,46.4113213,0 7.5946519,46.4119154,0 7.5946557,46.4121992,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5946557,46.4121992,0 7.5947645,46.4125775,0 7.5944483,46.4128069,0 7.5945428,46.4130565,0 7.5944653,46.413244,0 7.5944498,46.4133897,0 7.5946916,46.4136176,0 7.5947536,46.4140872,0 7.5948451,46.414329,0 7.5947955,46.4146018,0 7.5947645,46.4149149,0 7.5946374,46.4151908,0 7.5945723,46.4153086,0 7.5944064,46.4153582,0 7.5941894,46.4154868,0 7.5940592,46.4157705,0 7.5939368,46.4158464,0 7.5936888,46.4159673,0 7.5935586,46.4162881,0 7.5934367,46.4165774,0 7.5933156,46.4167476,0 7.5932134,46.4168574,0 7.5932172,46.4169406,0 7.5931264,46.4170012,0 7.5929485,46.4170466,0 7.5928161,46.417285,0 7.5927139,46.4175423,0 7.5921387,46.4175991,0 7.5919533,46.4177126,0 7.5918322,46.4176974,0 7.5917225,46.4174855,0 7.5915598,46.4174099,0 7.5913985,46.4174799,0 7.5913628,46.4174184,0 7.5911734,46.4175225,0 7.5908727,46.4180783,0 7.5901597,46.4188029,0 7.590028,46.4190354,0 7.5902314,46.4192795,0 7.5904387,46.4202889,0 7.5907177,46.4209379,0 7.5915276,46.4219047,0 7.5920628,46.4226534,0 7.5930543,46.4233989,0 7.5938905,46.4242919,0 7.5943427,46.4250004,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5296583,46.4300374,0 7.5307656,46.4296824,0 7.531478,46.4296114,0 7.5326109,46.4289607,0 7.5328856,46.4290672,0 7.5332632,46.4290672,0 7.5335465,46.4288779,0 7.5331946,46.4285643,0 7.5331087,46.4283632,0 7.5331946,46.4279491,0 7.5328942,46.4278071,0 7.5325423,46.4278366,0 7.5318213,46.4276592,0 7.531435,46.4276296,0 7.531272,46.4277657,0 7.5310316,46.427742,0 7.5309029,46.4274699,0 7.5305682,46.4272214,0 7.5304909,46.4270025,0 7.5302506,46.4269019,0 7.5299588,46.4269137,0 7.5293322,46.4264937,0 7.5287056,46.426334,0 7.5284739,46.4261683,0 7.5281134,46.4261269,0 7.5278559,46.4259671,0 7.5274697,46.4258843,0 7.5272551,46.4257305,0 7.5266629,46.4255471,0 7.5250836,46.4254406,0 7.5240622,46.4256358,0 7.5224915,46.4262275,0 7.5212212,46.4263813,0 7.5209294,46.4265588,0 7.5206461,46.4265055,0 7.5199766,46.4265588,0 7.5191784,46.4264286,0 7.5184059,46.426399,0 7.5181485,46.4264878,0 7.5179253,46.4263754,0 7.5172987,46.4268546,0 7.5166378,46.4266889,0 7.5163632,46.4265469,0 7.5160456,46.4265706,0 7.5149899,46.4262275,0 7.5148096,46.4260796,0 7.5149212,46.4258133,0 7.5144492,46.4257778,0 7.5146122,46.4253637,0 7.5137625,46.4253578,0 7.5135222,46.425269,0 7.512827,46.425482,0 7.5115052,46.4264937,0 7.511076,46.4266475,0 7.5105353,46.4270143,0 7.5101834,46.4270912,0 7.510046,46.4269729,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4901878,46.4164474,0 7.4912908,46.4158099,0 7.4918247,46.4154004,0 7.4920565,46.4151024,0 7.4923119,46.4150314,0 7.4928417,46.4151449,0 7.4930356,46.4152159,0 7.4930734,46.4151024,0 7.4929174,46.4148895,0 7.4929174,46.4147902,0 7.4934093,46.4148611,0 7.4939864,46.4152443,0 7.4942844,46.4152679,0 7.4943175,46.4150693,0 7.4943175,46.415022,0 7.4945634,46.4150503,0 7.4947574,46.4150929,0 7.4947621,46.4149557,0 7.4947526,46.4148611,0 7.495202,46.4148375,0 7.4958737,46.4152253,0 7.4960031,46.4154518,0 7.4961514,46.4154639,0 7.4962301,46.4154094,0 7.4963966,46.4155093,0 7.4965601,46.4156365,0 7.4966479,46.4156244,0 7.496554,46.4154488,0 7.496324,46.4152399,0 7.4962543,46.4148888,0 7.4963542,46.4147949,0 7.496327,46.4144831,0 7.4964178,46.4142561,0 7.49633,46.414023,0 7.4955157,46.41336,0 7.4950404,46.4133025,0 7.4946469,46.4130603,0 7.4943684,46.4127152,0 7.4942654,46.4124034,0 7.494217,46.4122944,0 7.4942715,46.4122581,0 7.4943472,46.412246,0 7.4948194,46.412358,0 7.4949375,46.4123368,0 7.4948557,46.41214,0 7.4944592,46.4116193,0 7.4944228,46.4115406,0 7.4948467,46.411362,0 7.4946559,46.4111229,0 7.4947316,46.4110744,0 7.494662,46.4109321,0 7.4945197,46.4108776,0 7.4945924,46.4107656,0 7.4945288,46.4107081,0 7.4943714,46.4108655,0 7.4940687,46.4106839,0 7.493663,46.4105416,0 7.4935631,46.4104902,0 7.4938628,46.4104054,0 7.4938356,46.4103509,0 7.4937357,46.4102571,0 7.4936085,46.4102026,0 7.4938931,46.4099695,0 7.4941837,46.410024,0 7.4945046,46.4099331,0 7.4952069,46.4099543,0 7.495437,46.4098938,0 7.4955611,46.4097969,0 7.4960122,46.4098393,0 7.4967357,46.4101027,0 7.496993,46.4101662,0 7.4971806,46.4100626,0 7.4972836,46.4100058,0 7.4973169,46.4099271,0 7.497217,46.4099241,0 7.4971613,46.409743,0 7.4968063,46.4094717,0 7.49627,46.4092268,0 7.4959926,46.4091075,0 7.4958764,46.4088269,0 7.4956563,46.4085867,0 7.4954188,46.4080324,0 7.4954703,46.4075632,0 7.495328,46.4072756,0 7.4953704,46.4070486,0 7.4957276,46.4069396,0 7.4958638,46.4065884,0 7.4968083,46.4062312,0 7.4977044,46.405992,0 7.4979405,46.4060405,0 7.4981282,46.4057529,0 7.4980919,46.4054744,0 7.4983825,46.4051989,0 7.4987034,46.4048326,0 7.4986943,46.4040485,0 7.4994231,46.4034022,0 7.4994988,46.4029718,0 7.499688,46.4024609,0 7.500109,46.4020447,0 7.5001941,46.4017278,0 7.5000191,46.4013778,0 7.5000806,46.400952,0 7.4998678,46.400673,0 7.4998163,46.4004746,0 7.4995795,46.4003655,0 7.4992645,46.4003866,0 7.4988702,46.4002985,0 7.4986284,46.400275,0 7.498196,46.4003894,0 7.4980919,46.4004863,0 7.4978473,46.4004233,0 7.4971159,46.4005347,0 7.496772,46.400588,0 7.4961786,46.4005565,0 7.4961084,46.4006292,0 7.4957064,46.4005468,0 7.4952463,46.4004524,0 7.4947861,46.4003652,0 7.4933998,46.3997392,0 7.4932165,46.3997687,0 7.4931219,46.3994967,0 7.491218,46.3992425,0 7.4899173,46.3989114,0 7.4893556,46.3990769,0 7.4888885,46.3988759,0 7.4864525,46.3987104,0 7.4846692,46.3988084,0 7.4843689,46.3989585,0 7.4823134,46.3989123,0 7.481863,46.3985312,0 7.4809507,46.3978383,0 7.478745,46.3969722,0 7.4771745,46.3951246,0 7.4767934,46.3946857,0 7.4768165,46.3940852,0 7.4755347,46.3924454,0 7.471643,46.3901935,0 7.4706267,46.3891542,0 7.4701879,46.3881957,0 7.4692872,46.3876183,0 7.4675434,46.3874105,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4675434,46.3874105,0 7.4669887,46.3873872,0 7.465311,46.386759,0 7.464535,46.3867072,0 7.4624656,46.3860347,0 7.4615639,46.3857169,0 7.4609857,46.3855182,0 7.4604773,46.3854862,0 7.4600662,46.3855409,0 7.4598666,46.3854598,0 7.4597279,46.3853215,0 7.4592072,46.3852908,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4715578,46.3803844,0 7.4713128,46.3802364,0 7.4710557,46.3801046,0 7.4707899,46.3799475,0 7.4698841,46.3796959,0 7.4694796,46.3795216,0 7.4681501,46.3786158,0 7.4675495,46.378284,0 7.4674162,46.3781604,0 7.4666402,46.3783304,0 7.4661228,46.3783304,0 7.4655094,46.3783895,0 7.4651103,46.3781752,0 7.4646468,46.3781259,0 7.4641455,46.3780629,0 7.4636854,46.377983,0 7.463201,46.3777408,0 7.4629927,46.3777214,0 7.4624696,46.3775035,0 7.4618424,46.3773751,0 7.4614379,46.3773025,0 7.4612345,46.377024,0 7.4610577,46.3768084,0 7.4610577,46.3766437,0 7.4611473,46.3765372,0 7.4614089,46.3764863,0 7.4615938,46.3762582,0 7.4617497,46.3761721,0 7.4619008,46.376207,0 7.4618642,46.3764452,0 7.4619998,46.3764452,0 7.4621249,46.3763998,0 7.4621408,46.3764214,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5888202,46.5408269,0 7.5888528,46.5408766,0 7.588875,46.54106,0 7.588924,46.54119,0 7.588901,46.54133,0 7.588834,46.541485,0 7.588767,46.541914,0 7.588787,46.541944,0 7.588761,46.542065,0 7.588373,46.542474,0 7.588166,46.542546,0 7.587836,46.5425,0 7.587632,46.542542,0 7.587456,46.542619,0 7.587403,46.542651,0 7.58737,46.542709,0 7.58737,46.542829,0 7.587412,46.542915,0 7.586862,46.543252,0 7.586847,46.543396,0 7.586674,46.543453,0 7.5862511,46.5434681,0 7.586041,46.543592,0 7.58596,46.5437,0 7.58591,46.543846,0 7.58599,46.543953,0 7.585999,46.544003,0 7.585923,46.544054,0 7.58558,46.544154,0 7.585515,46.544354,0 7.585589,46.544496,0 7.585709,46.54456,0 7.585731,46.544603,0 7.585658,46.544812,0 7.585706,46.544917,0 7.585653,46.545091,0 7.585334,46.545331,0 7.585114,46.545657,0 7.5849503,46.5458153,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6020138,46.5498841,0 7.601882,46.549922,0 7.601685,46.54992,0 7.601626,46.54996,0 7.601722,46.550054,0 7.601309,46.550264,0 7.6011991,46.550366,0 7.601219,46.55041,0 7.601399,46.550388,0 7.6015586,46.550506,0 7.601421,46.550513,0 7.6013647,46.5506373,0 7.601262,46.550705,0 7.601287,46.550811,0 7.601333,46.550808,0 7.6017485,46.5512085,0 7.601757,46.5512704,0 7.601563,46.551184,0 7.601708,46.551289,0 7.6015783,46.5512632,0 7.6018501,46.5513874,0 7.6016629,46.5513788,0 7.601709,46.551462,0 7.601649,46.55154,0 7.6018448,46.5515183,0 7.601677,46.551598,0 7.601566,46.551819,0 7.60172,46.551885,0 7.601752,46.551971,0 7.601523,46.552262,0 7.6014463,46.5522722,0 7.601403,46.552354,0 7.601349,46.55238,0 7.601272,46.552371,0 7.601262,46.552236,0 7.601138,46.552184,0 7.600934,46.552206,0 7.600846,46.552289,0 7.600269,46.552471,0 7.6002,46.5525494,0 7.600149,46.55276,0 7.600296,46.552897,0 7.6009266,46.5531762,0 7.6009329,46.5533856,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6009266,46.5531762,0 7.600218,46.55332,0 7.6001175,46.5534133,0 7.599965,46.553401,0 7.599942,46.553433,0 7.599895,46.553426,0 7.59987,46.553456,0 7.599729,46.553497,0 7.599659,46.553448,0 7.599272,46.553485,0 7.598813,46.55346,0 7.598656,46.5534106,0 7.598414,46.553382,0 7.598358,46.553294,0 7.598138,46.553232,0 7.5979213,46.5532318,0 7.5977704,46.5533893,0 7.597562,46.55353,0 7.5971441,46.5537263,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.585006,46.545887,0 7.585308,46.545812,0 7.585676,46.545648,0 7.586004,46.545501,0 7.586149,46.54546,0 7.586274,46.545474,0 7.586592,46.545575,0 7.586746,46.545413,0 7.587392,46.545051,0 7.587454,46.545042,0 7.58755,46.545093,0 7.587657,46.545196,0 7.587685,46.545255,0 7.587735,46.545312,0 7.587937,46.545379,0 7.588127,46.545313,0 7.588555,46.545203,0 7.588655,46.545235,0 7.588914,46.54519,0 7.58927,46.544999,0 7.589602,46.544965,0 7.589747,46.544873,0 7.590138,46.544825,0 7.590246,46.544838,0 7.590336,46.544905,0 7.590498,46.54495,0 7.59092,46.544973,0 7.591352,46.544928,0 7.5915543,46.5450082,0 7.5919183,46.5450711,0 7.5920883,46.5451263,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5849503,46.5458153,0 7.585006,46.545887,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5800023,46.5344493,0 7.5798945,46.5343797,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5798945,46.5343797,0 7.5799446,46.5343023,0 7.580358,46.534014,0 7.580229,46.53383,0 7.580478,46.53353,0 7.58063,46.533409,0 7.580662,46.533282,0 7.580985,46.533044,0 7.581097,46.532718,0 7.581299,46.53254,0 7.581403,46.532292,0 7.581436,46.532046,0 7.581655,46.531986,0 7.581711,46.531738,0 7.582004,46.531552,0 7.582176,46.531554,0 7.582523,46.531376,0 7.582614,46.531184,0 7.582583,46.53111,0 7.582906,46.530769,0 7.582863,46.530726,0 7.5829341,46.5305863,0 7.583097,46.530457,0 7.583587,46.53036,0 7.583919,46.530348,0 7.5840885,46.5302561,0 7.5842076,46.5301916,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.574907,46.522174,0 7.575241,46.522126,0 7.575519,46.522007,0 7.575683,46.5219091,0 7.576177,46.5216802,0 7.5766449,46.5215308,0 7.5769984,46.5213232,0 7.5771978,46.5209949,0 7.5773429,46.5207243,0 7.5778537,46.5203175,0 7.5781433,46.5201358,0 7.5782994,46.5200566,0 7.5784074,46.5199962,0 7.5786242,46.5200434,0 7.5788573,46.5200418,0 7.5791451,46.5200439,0 7.5795373,46.5200993,0 7.5797431,46.5200959,0 7.5799979,46.5200668,0 7.5801735,46.5200411,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5749471,46.5223416,0 7.574907,46.522174,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6160369,46.3979777,0 7.6157131,46.3979155,0 7.6154834,46.3978111,0 7.6151754,46.3978003,0 7.6150423,46.3977139,0 7.6148727,46.3976239,0 7.6147448,46.3976527,0 7.6145725,46.3976563,0 7.6144133,46.3977751,0 7.6141693,46.3976716,0 7.6139253,46.3976149,0 7.6138913,46.3974511,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5867784,46.5343331,0 7.586636,46.5343169,0 7.5862536,46.534387,0 7.5858189,46.5340589,0 7.5855344,46.5338818,0 7.5852999,46.5338814,0 7.5852102,46.5338558,0 7.5849817,46.5337454,0 7.5844205,46.5337057,0 7.5841258,46.5338472,0 7.5837589,46.5338793,0 7.583468,46.534194,0 7.5831458,46.5339879,0 7.5829768,46.5340463,0 7.5827503,46.5340428,0 7.5824606,46.5341234,0 7.5822823,46.533987,0 7.582175,46.534088,0 7.582192,46.534143,0 7.582098,46.534248,0 7.582032,46.534242,0 7.5817344,46.5340341,0 7.581564,46.534071,0 7.581458,46.534159,0 7.5813587,46.5343647,0 7.581224,46.5343566,0 7.5811,46.534221,0 7.5809361,46.5341512,0 7.5807098,46.5341913,0 7.5805463,46.5342494,0 7.580228,46.534435,0 7.5800023,46.5344493,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5760918,46.4642406,0 7.5761528,46.4641344,0 7.5760963,46.4639355,0 7.575963,46.4637864,0 7.5761054,46.4635605,0 7.576007,46.463488,0 7.5760865,46.4634391,0 7.5762155,46.4634251,0 7.5762909,46.4634787,0 7.5763343,46.4633983,0 7.5761492,46.4633829,0 7.5759358,46.4633025,0 7.5756982,46.4632692,0 7.5753259,46.4633112,0 7.5752247,46.4634065,0 7.5750741,46.4633328,0 7.574982,46.4633792,0 7.5748702,46.4634855,0 7.5746599,46.4634921,0 7.5743257,46.463372,0 7.5741265,46.4632372,0 7.5739792,46.4632088,0 7.5737606,46.4632926,0 7.5735824,46.4628614,0 7.5733497,46.4628337,0 7.5731491,46.4627127,0 7.5728305,46.4626798,0 7.5725916,46.4625619,0 7.5724166,46.4626136,0 7.5722627,46.4626243,0 7.5721752,46.4626614,0 7.571934,46.462583,0 7.5718543,46.4625871,0 7.5714721,46.4625473,0 7.5712765,46.4625598,0 7.571208,46.462656,0 7.571101,46.462616,0 7.571067,46.462726,0 7.5707706,46.4627343,0 7.57068,46.462818,0 7.570409,46.462675,0 7.570349,46.462904,0 7.569704,46.462655,0 7.5695604,46.462966,0 7.5697132,46.4634117,0 7.569812,46.4637868,0 7.569815,46.46394,0 7.5697137,46.4643644,0 7.5696509,46.4645017,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5697137,46.4643644,0 7.5696709,46.4640308,0 7.569641,46.463862,0 7.569569,46.463753,0 7.5695632,46.4636037,0 7.569509,46.46342,0 7.5695177,46.4633725,0 7.5695743,46.4633062,0 7.569566,46.4631985,0 7.5694414,46.4631404,0 7.569341,46.4630756,0 7.569295,46.462921,0 7.569227,46.462647,0 7.569219,46.462572,0 7.5696607,46.4623892,0 7.5698225,46.462294,0 7.5698465,46.4622325,0 7.5698383,46.4621907,0 7.5697712,46.4621462,0 7.569602,46.462112,0 7.5692687,46.4619905,0 7.5691654,46.461936,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5696509,46.4645017,0 7.5698207,46.4647295,0 7.5699592,46.4650421,0 7.5699802,46.46521,0 7.5702017,46.4657804,0 7.5705419,46.466011,0 7.5707753,46.4661825,0 7.570989,46.4663287,0 7.5710874,46.4664496,0 7.5712645,46.4664834,0 7.5713153,46.4665155,0 7.5713181,46.4665988,0 7.5712129,46.4666974,0 7.5713008,46.4667194,0 7.5713972,46.4667082,0 7.5715031,46.4667483,0 7.5715626,46.4668221,0 7.5717327,46.4668831,0 7.5717584,46.467238,0 7.571919,46.4672926,0 7.5719463,46.4674066,0 7.5720619,46.4674788,0 7.5721567,46.4676028,0 7.5722702,46.4678185,0 7.5723853,46.4679969,0 7.5724291,46.468146,0 7.572494,46.468344,0 7.5724388,46.4686796,0 7.5724432,46.4687755,0 7.5724457,46.4688768,0 7.572539,46.4690708,0 7.5725353,46.4691595,0 7.5725692,46.4692211,0 7.5725419,46.469325,0 7.572579,46.4694265,0 7.572718,46.469569,0 7.5729752,46.4697186,0 7.5730414,46.4698077,0 7.573089,46.4698415,0 7.5731178,46.4699205,0 7.5730953,46.4699843,0 7.5730765,46.4700483,0 7.573187,46.470135,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5733474,46.4760144,0 7.573387,46.476093,0 7.573364,46.476147,0 7.573205,46.47634,0 7.5731633,46.4764965,0 7.573011,46.476853,0 7.573171,46.477301,0 7.573158,46.477392,0 7.573204,46.477643,0 7.573264,46.477724,0 7.57326,46.47782,0 7.573312,46.477974,0 7.573405,46.478005,0 7.573513,46.478382,0 7.573327,46.478605,0 7.573124,46.478741,0 7.5730652,46.4788205,0 7.572965,46.478971,0 7.5729777,46.4790803,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.572435,46.480232,0 7.5723796,46.4802457,0 7.572367,46.480409,0 7.5724103,46.4805829,0 7.5724509,46.4806753,0 7.572588,46.4807929,0 7.5728106,46.4809482,0 7.572871,46.481109,0 7.5730504,46.4810728,0 7.5731338,46.4810853,0 7.573273,46.481198,0 7.5733182,46.4812789,0 7.573379,46.481352,0 7.5734294,46.4814738,0 7.573584,46.481614,0 7.5736388,46.4817822,0 7.5735255,46.4818236,0 7.5735632,46.481887,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4991732,46.4109774,0 7.4971806,46.4100626,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5630315,46.4863658,0 7.5630346,46.4863294,0 7.5629344,46.4861077,0 7.5628594,46.4858963,0 7.5628286,46.4857459,0 7.5627536,46.4854418,0 7.5629093,46.4851483,0 7.5631453,46.4849427,0 7.5634035,46.4848222,0 7.5635808,46.4847034,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5609581,46.485938,0 7.5605165,46.4858118,0 7.5600333,46.4857315,0 7.5598958,46.4857688,0 7.559625,46.485895,0 7.5595375,46.4858721,0 7.5593626,46.4856971,0 7.5591709,46.4856971,0 7.5590842,46.4856691,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5583125,46.4854727,0 7.5582182,46.4854102,0 7.5579355,46.4853406,0 7.5577332,46.4852617,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4491612,46.3892601,0 7.4489159,46.389038,0 7.448525,46.3888934,0 7.4484874,46.3885331,0 7.4481152,46.3884317,0 7.4478024,46.3883001,0 7.4481997,46.3879463,0 7.4479682,46.3878061,0 7.448763,46.3874695,0 7.4490379,46.387263,0 7.4491418,46.3869723,0 7.448928,46.3867195,0 7.4480727,46.3866647,0 7.4475718,46.3865635,0 7.4472541,46.3866225,0 7.4469059,46.3864371,0 7.4462221,46.3860969,0 7.4456494,46.3858171,0 7.445017,46.3854714,0 7.4447187,46.385348,0 7.4441818,46.3852245,0 7.4437523,46.3850681,0 7.4433466,46.3848706,0 7.4431576,46.3845101,0 7.4427768,46.3840883,0 7.4424288,46.383557,0 7.4422289,46.3833056,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.54034,46.479224,0 7.5405449,46.4792038,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5405449,46.4792038,0 7.5405797,46.4790433,0 7.5405327,46.4789723,0 7.5404097,46.4788593,0 7.5402977,46.4788013,0 7.5402217,46.4787403,0 7.5401727,46.4786613,0 7.5401477,46.4785803,0 7.5401417,46.4784913,0 7.5401847,46.4783583,0 7.5401077,46.4781393,0 7.5398797,46.4779323,0 7.5397957,46.4778963,0 7.5396827,46.4778273,0 7.5395287,46.4776643,0 7.5392937,46.4774943,0 7.5392307,46.4773863,0 7.5392767,46.4772613,0 7.5393877,46.4770953,0 7.5393527,46.4769173,0 7.5392957,46.4768123,0 7.5390797,46.4766623,0 7.5386757,46.4762033,0 7.5386637,46.4761703,0 7.5386847,46.4760593,0 7.5384857,46.4757803,0 7.5384877,46.4757453,0 7.5385827,46.4756555,0 7.5387457,46.475635,0 7.5388027,46.4755263,0 7.5387747,46.4754113,0 7.5387965,46.475197,0 7.5387556,46.4750208,0 7.5387107,46.4748276,0 7.5386463,46.4746207,0 7.5387322,46.4744286,0 7.5384961,46.4739853,0 7.5383245,46.4736011,0 7.5382601,46.4728917,0 7.5378953,46.4726701,0 7.5375949,46.4724927,0 7.5373374,46.4721972,0 7.5371872,46.4718425,0 7.5370799,46.4714878,0 7.5368224,46.4710149,0 7.5366508,46.4708671,0 7.5363933,46.4707489,0 7.5361572,46.4705125,0 7.5359427,46.4703942,0 7.5354491,46.4701873,0 7.5351487,46.4700691,0 7.5347839,46.4699509,0 7.5345265,46.4698474,0 7.534269,46.469271,0 7.5341188,46.4688868,0 7.5337111,46.4687094,0 7.5332913,46.4687698,0 7.5313936,46.467867,0 7.5306426,46.4676454,0 7.5300418,46.4673793,0 7.5295697,46.467069,0 7.528926,46.4667142,0 7.5285183,46.4665073,0 7.5280033,46.4664926,0 7.5275956,46.46633,0 7.5271021,46.4661822,0 7.5267373,46.4661822,0 7.5254284,46.46599,0 7.5249778,46.4657092,0 7.5243126,46.4652806,0 7.5243555,46.4650737,0 7.5236474,46.4643051,0 7.5229393,46.4638469,0 7.5222956,46.4632853,0 7.522081,46.4630045,0 7.5218664,46.4628419,0 7.5213944,46.4623393,0 7.5208365,46.4623393,0 7.5206863,46.4622802,0 7.5207935,46.4619698,0 7.5201927,46.4617777,0 7.5201284,46.4615116,0 7.5199352,46.4612012,0 7.519785,46.460876,0 7.5191198,46.4602552,0 7.5182186,46.4598413,0 7.5172959,46.4598266,0 7.5165878,46.459664,0 7.5163518,46.4594275,0 7.5160299,46.458851,0 7.515472,46.4580971,0 7.5148069,46.4578606,0 7.5148593,46.4576658,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5405449,46.4792038,0 7.5406524,46.4792776,0 7.5407781,46.479334,0 7.5408591,46.4794291,0 7.5409698,46.4795263,0 7.5411333,46.4796215,0 7.5412319,46.4796548,0 7.5413719,46.4796758,0 7.5416056,46.4797181,0 7.5418305,46.4797854,0 7.54201,46.4798595,0 7.5421246,46.4798622,0 7.5422038,46.4797679,0 7.5422344,46.4798088,0 7.5422886,46.4799859,0 7.5423131,46.4800371,0 7.5426664,46.4798968,0 7.5429766,46.4798709,0 7.5432583,46.4799198,0 7.5434381,46.4799008,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>weltcuptrail</name>
    +          <description>weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.546366,46.472721,0 7.546582,46.472858,0 7.547273,46.473451,0 7.547355,46.4734738,0 7.547384,46.473463,0 7.547498,46.473357,0 7.547742,46.47342,0 7.547824,46.473408,0 7.547981,46.473591,0 7.547883,46.473721,0 7.547835,46.473855,0 7.5478299,46.473905,0 7.547809,46.473981,0 7.5477828,46.4740075,0 7.547779,46.474138,0 7.547809,46.474276,0 7.5477933,46.4743096,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>weltcuptrail</name>
    +          <description>weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5480239,46.4745659,0 7.548071,46.474601,0 7.548147,46.474528,0 7.548245,46.474637,0 7.5483324,46.4746403,0 7.548341,46.47449,0 7.548407,46.474385,0 7.548466,46.474353,0 7.54865,46.474336,0 7.54875,46.47437,0 7.548891,46.474445,0 7.549013,46.474522,0 7.54923,46.474725,0 7.549299,46.4748896,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>weltcuptrail</name>
    +          <description>weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5493338,46.4748532,0 7.549414,46.47493,0 7.549582,46.475164,0 7.549651,46.475295,0 7.549684,46.475505,0 7.54967,46.475541,0 7.549721,46.475697,0 7.549688,46.475774,0 7.5496779,46.4758944,0 7.549687,46.476009,0 7.549675,46.476086,0 7.549617,46.476191,0 7.549632,46.476283,0 7.549615,46.476398,0 7.549678,46.47656,0 7.549695,46.476669,0 7.5497552,46.4767867,0 7.549748,46.476959,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Weltcuptrail</name>
    +          <description>Weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.549748,46.476959,0 7.549775,46.477008,0 7.549895,46.477112,0 7.550177,46.477211,0 7.55031,46.477289,0 7.55055,46.47749,0 7.55062,46.47756,0 7.550649,46.47761,0 7.5507845,46.4777077,0 7.55114,46.477912,0 7.551194,46.477987,0 7.551282,46.478149,0 7.551346,46.478194,0 7.551429,46.478302,0 7.551526,46.478356,0 7.551621,46.478457,0 7.551717,46.478663,0 7.551707,46.47887,0 7.551658,46.47893,0 7.551561,46.479008,0 7.551522,46.479094,0 7.551569,46.479147,0 7.551878,46.47925,0 7.5518842,46.4794254,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.551653,46.479461,0 7.5518381,46.4797245,0 7.5520744,46.4800436,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>weltcuptrail</name>
    +          <description>weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5520744,46.4800436,0 7.5523543,46.4803948,0 7.552633,46.480644,0 7.552654,46.480686,0 7.552661,46.480821,0 7.552597,46.480934,0 7.5528719,46.4810715,0 7.553538,46.481365,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>weltcuptrail</name>
    +          <description>weltcuptrail, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.553538,46.481365,0 7.5536631,46.4813963,0 7.5538861,46.4814296,0 7.5539842,46.4814906,0 7.554022,46.481474,0 7.5540645,46.48153,0 7.5541435,46.48172,0 7.5543894,46.4818397,0 7.554494,46.4819297,0 7.5545233,46.4819795,0 7.5545342,46.4819645,0 7.5545621,46.4819692,0 7.5545859,46.4820172,0 7.554821,46.481838,0 7.554956,46.481708,0 7.555151,46.48159,0 7.555222,46.481636,0 7.55523,46.48166,0 7.5553253,46.4817137,0 7.55537,46.481745,0 7.555477,46.481777,0 7.5556084,46.4817019,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5545859,46.4820172,0 7.55461,46.482137,0 7.554617,46.482234,0 7.554605,46.482389,0 7.554629,46.482655,0 7.554708,46.482908,0 7.5547019,46.4829911,0 7.5548169,46.4831683,0 7.55481,46.483357,0 7.5548512,46.4833925,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5589672,46.4720202,0 7.5588162,46.471941,0 7.5582203,46.4719026,0 7.5581135,46.4719351,0 7.558022,46.471878,0 7.5578016,46.4719941,0 7.5576517,46.4720351,0 7.5576179,46.4720443,0 7.5575959,46.4718987,0 7.557722,46.471563,0 7.5579085,46.471328,0 7.557727,46.471356,0 7.5578843,46.4712066,0 7.558052,46.470647,0 7.5583051,46.4703056,0 7.5580457,46.4703479,0 7.5579895,46.470307,0 7.55807,46.470201,0 7.557818,46.469769,0 7.557864,46.469491,0 7.5576416,46.4695163,0 7.5576139,46.4694301,0 7.557456,46.469458,0 7.5575474,46.4693574,0 7.5575239,46.4692166,0 7.5573659,46.4692865,0 7.557391,46.469176,0 7.5574551,46.4689968,0 7.5573844,46.4690143,0 7.557431,46.468801,0 7.5573958,46.4687989,0 7.5574087,46.4686203,0 7.557343,46.468344,0 7.5569532,46.4683026,0 7.5566536,46.4684285,0 7.5563305,46.4683727,0 7.5557709,46.4686531,0 7.555443,46.468453,0 7.555369,46.46828,0 7.555448,46.468245,0 7.555318,46.468132,0 7.555263,46.468036,0 7.5552562,46.4679723,0 7.555104,46.467981,0 7.555108,46.467899,0 7.5552802,46.4677086,0 7.555261,46.4676825,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5534638,46.4774581,0 7.5534455,46.4773887,0 7.5536469,46.4769512,0 7.5541106,46.4766077,0 7.554105,46.476402,0 7.5542249,46.476267,0 7.554407,46.476171,0 7.554414,46.476057,0 7.554147,46.475643,0 7.55415,46.475587,0 7.554261,46.475443,0 7.554396,46.475107,0 7.554392,46.474963,0 7.55435,46.474903,0 7.5544589,46.4747756,0 7.554432,46.47466,0 7.554649,46.474518,0 7.554669,46.474458,0 7.554782,46.474308,0 7.5551416,46.4740944,0 7.555307,46.473841,0 7.555855,46.47362,0 7.5563039,46.4733363,0 7.5564953,46.4732154,0 7.5571264,46.4724475,0 7.5575742,46.4721367,0 7.5576179,46.4720443,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5467453,46.4607696,0 7.54649,46.46086,0 7.546379,46.460852,0 7.546278,46.460823,0 7.545332,46.460219,0 7.5452056,46.4601933,0 7.5450453,46.4602259,0 7.544678,46.460625,0 7.5446722,46.4605383,0 7.5445511,46.460897,0 7.544186,46.460965,0 7.5441541,46.4609626,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.543932,46.457905,0 7.5439224,46.4576498,0 7.5439863,46.457507,0 7.5443697,46.4571846,0 7.544414,46.457028,0 7.5444229,46.4568876,0 7.5443132,46.4567359,0 7.5441254,46.4565893,0 7.5437854,46.4563725,0 7.543601,46.456324,0 7.5433883,46.456225,0 7.543038,46.45609,0 7.5429552,46.4560635,0 7.5426803,46.4560349,0 7.5424803,46.4559119,0 7.5424073,46.4558342,0 7.5423486,46.4557438,0 7.5422303,46.4556441,0 7.542099,46.4555716,0 7.541851,46.455355,0 7.541218,46.4550142,0 7.5410982,46.454977,0 7.5409976,46.454893,0 7.540705,46.454773,0 7.540596,46.4546219,0 7.5405802,46.4545527,0 7.540341,46.454266,0 7.540211,46.454177,0 7.5399529,46.4540621,0 7.5398294,46.4540301,0 7.5397101,46.4539652,0 7.5395719,46.4538225,0 7.5396175,46.4537215,0 7.5392434,46.4536621,0 7.5390846,46.4536203,0 7.5389972,46.4535609,0 7.5386669,46.4535118,0 7.5385755,46.4534562,0 7.5383003,46.4533948,0 7.537794,46.453134,0 7.5376568,46.4530828,0 7.5373042,46.4529123,0 7.53703,46.452881,0 7.5369754,46.4528616,0 7.5368853,46.4528814,0 7.536805,46.4528509,0 7.5367046,46.452847,0 7.5366355,46.4527915,0 7.5365538,46.4526502,0 7.5364786,46.4525963,0 7.5363413,46.4525663,0 7.53618,46.452558,0 7.535878,46.452489,0 7.5356819,46.4524571,0 7.535526,46.452412,0 7.535344,46.452435,0 7.5352519,46.4524668,0 7.5347041,46.4526012,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5441541,46.4609626,0 7.5440914,46.4606083,0 7.5438826,46.4599873,0 7.5438935,46.459889,0 7.5437841,46.4595562,0 7.543707,46.459351,0 7.543697,46.458911,0 7.543855,46.4586,0 7.5438914,46.4582169,0 7.543963,46.457984,0 7.543932,46.457905,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5441541,46.4609626,0 7.5443022,46.4612074,0 7.544328,46.461298,0 7.544419,46.461445,0 7.544713,46.461762,0 7.545105,46.462076,0 7.545267,46.462233,0 7.545313,46.462295,0 7.545411,46.462463,0 7.545425,46.462521,0 7.545408,46.462596,0 7.545312,46.462754,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.545312,46.462754,0 7.545134,46.462514,0 7.5450942,46.4625956,0 7.545048,46.462549,0 7.545021,46.462594,0 7.544907,46.462658,0 7.5448176,46.4625996,0 7.544809,46.462548,0 7.544682,46.462664,0 7.5445887,46.462662,0 7.544541,46.462678,0 7.5445217,46.4627506,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5445217,46.4627506,0 7.544556,46.462863,0 7.544642,46.462973,0 7.544646,46.463071,0 7.544535,46.463181,0 7.5444536,46.4632176,0 7.544159,46.463337,0 7.544005,46.463444,0 7.543837,46.463533,0 7.543703,46.463733,0 7.543456,46.463916,0 7.54331,46.464225,0 7.543007,46.464459,0 7.542768,46.464717,0 7.542685,46.464932,0 7.54251,46.465228,0 7.542382,46.46565,0 7.542912,46.466323,0 7.543704,46.466981,0 7.543754,46.467237,0 7.543974,46.46768,0 7.543981,46.467838,0 7.543918,46.467971,0 7.544025,46.468221,0 7.544473,46.468587,0 7.544433,46.468807,0 7.5444844,46.4688834,0 7.544452,46.468936,0 7.5444486,46.4690083,0 7.544404,46.469095,0 7.544283,46.4691718,0 7.544292,46.469265,0 7.5442081,46.4693977,0 7.544297,46.4696186,0 7.544416,46.469775,0 7.5445187,46.4698185,0 7.544622,46.46995,0 7.544745,46.47001,0 7.54538,46.470662,0 7.546082,46.471594,0 7.5460595,46.4716439,0 7.546271,46.471879,0 7.546345,46.472015,0 7.546457,46.472109,0 7.5465251,46.472387,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5882589,46.4861244,0 7.5885014,46.4860791,0 7.5886623,46.4860126,0 7.5888769,46.4859609,0 7.5890164,46.4859609,0 7.5890486,46.4860644,0 7.589349,46.4861973,0 7.5894348,46.4862269,0 7.5895528,46.4862121,0 7.5898425,46.4860644,0 7.5902395,46.4860791,0 7.5903575,46.486057,0 7.5905292,46.486057,0 7.5905935,46.4861382,0 7.5905721,46.486286,0 7.5904004,46.4863746,0 7.5902609,46.4864928,0 7.5901966,46.4866258,0 7.5903146,46.4867735,0 7.5904862,46.4868695,0 7.590615,46.4868474,0 7.5907652,46.4867735,0 7.5909583,46.4866923,0 7.5911836,46.486611,0 7.5912587,46.4869139,0 7.5912821,46.4869728,0 7.5913231,46.4870764,0 7.5914948,46.4870173,0 7.5916986,46.4868991,0 7.5918939,46.4869026,0 7.592117,46.4869065,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.592399,46.488507,0 7.5926427,46.4885981,0 7.5928466,46.4886055,0 7.5930183,46.4886793,0 7.5933508,46.4886202,0 7.5935225,46.4886276,0 7.5935547,46.4886646,0 7.5936727,46.4886572,0 7.5939302,46.4887901,0 7.5941448,46.4887384,0 7.5943701,46.4887089,0 7.5945847,46.4889231,0 7.5949924,46.4890561,0 7.5952391,46.4890191,0 7.5954108,46.4889896,0 7.5956146,46.48896,0 7.5957005,46.4888345,0 7.5958292,46.488731,0 7.5959365,46.4885981,0 7.5960104,46.4885348,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.624132,46.520973,0 7.6238068,46.5208419,0 7.6234849,46.5205687,0 7.623163,46.5203989,0 7.6226695,46.5204358,0 7.62253,46.5204727,0 7.6223584,46.5204248,0 7.6221062,46.5202107,0 7.6217951,46.5200667,0 7.6214893,46.5199338,0 7.6211728,46.5198046,0 7.6209046,46.5197825,0 7.6205506,46.5197714,0 7.6202448,46.5197419,0 7.6199712,46.519753,0 7.6196923,46.5198268,0 7.6194026,46.5199744,0 7.6190968,46.5200556,0 7.6188232,46.520063,0 7.6186247,46.520004,0 7.6183672,46.5199855,0 7.618099,46.5200224,0 7.6179649,46.5200003,0 7.6177235,46.5198711,0 7.6174768,46.5197566,0 7.6172836,46.519716,0 7.6169779,46.5196717,0 7.6167794,46.5196127,0 7.6166345,46.5194909,0 7.6165219,46.5192731,0 7.6164522,46.5189815,0 7.6163931,46.518568,0 7.616377,46.5183429,0 7.6163019,46.5181103,0 7.6161088,46.5178002,0 7.6159479,46.5175234,0 7.6156797,46.5173277,0 7.6154919,46.5172022,0 7.6154651,46.5170878,0 7.6154651,46.517014,0 7.6153959,46.5168917,0 7.6151502,46.5168899,0 7.6148965,46.5169087,0 7.6146497,46.5168515,0 7.6145317,46.516774,0 7.6143708,46.5164713,0 7.6141455,46.5162314,0 7.613829,46.5159508,0 7.6133569,46.5157256,0 7.6131048,46.5156149,0 7.612917,46.5155115,0 7.6127453,46.5154524,0 7.6126273,46.5154487,0 7.6122518,46.5154487,0 7.6118602,46.5153823,0 7.6114311,46.5153528,0 7.6110234,46.5153269,0 7.6109214,46.5153454,0 7.6108034,46.5154783,0 7.6106586,46.5154894,0 7.6104869,46.5154377,0 7.6103206,46.515338,0 7.610267,46.5152236,0 7.6102992,46.5150907,0 7.6104601,46.5147547,0 7.6106103,46.5144003,0 7.6106693,46.5141862,0 7.610723,46.5140681,0 7.610841,46.5139352,0 7.6109429,46.5137063,0 7.6110638,46.5135387,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.609237,46.5005217,0 7.6090546,46.5002927,0 7.608883,46.500049,0 7.6087757,46.4998792,0 7.6086147,46.4998127,0 7.6079495,46.4996945,0 7.607574,46.4996059,0 7.6074453,46.4994951,0 7.6073487,46.4993326,0 7.6071127,46.4991628,0 7.6068552,46.499052,0 7.6067694,46.4989338,0 7.6067265,46.4985793,0 7.6067694,46.4982987,0 7.6067694,46.4981805,0 7.6066835,46.4980476,0 7.6064368,46.4979811,0 7.6060827,46.4979368,0 7.6058038,46.4979442,0 7.6054926,46.4978999,0 7.6052566,46.4978334,0 7.6050098,46.4978186,0 7.6047202,46.497826,0 7.6045778,46.4977876,0 7.6044734,46.4977595,0 7.604173,46.4976931,0 7.603937,46.497634,0 7.6037868,46.4975454,0 7.6036473,46.4974937,0 7.6035293,46.4973533,0 7.6033576,46.4972499,0 7.6031323,46.4971761,0 7.6028963,46.4971244,0 7.6027353,46.4970284,0 7.6027031,46.4969397,0 7.6025228,46.4967002,0 7.6022815,46.4966125,0 7.6021656,46.4965905,0 7.6020622,46.4965404,0 7.6017895,46.4965373,0 7.6015231,46.4965373,0 7.6013884,46.4965122,0 7.6012693,46.4965561,0 7.6008681,46.4964997,0 7.6004513,46.4963524,0 7.6001975,46.4962991,0 7.5998966,46.4962396,0 7.5995268,46.4961017,0 7.5993169,46.4960296,0 7.5989376,46.496086,0 7.5986368,46.4959669,0 7.5982795,46.4960264,0 7.5979599,46.4960452,0 7.5976026,46.4961361,0 7.5973174,46.4962333,0 7.5971733,46.496227,0 7.5971513,46.4960891,0 7.5970416,46.4959387,0 7.596816,46.4958509,0 7.5968442,46.4957225,0 7.5968849,46.4957005,0 7.5969696,46.4958353,0 7.5970573,46.4957131,0 7.5972328,46.4955407,0 7.5972516,46.4954247,0 7.5974898,46.4953652,0 7.5978314,46.4951615,0 7.5981354,46.4951239,0 7.5982607,46.495127,0 7.5983767,46.4950299,0 7.5988938,46.4949108,0 7.5991915,46.4947384,0 7.5994359,46.4947321,0 7.599624,46.4947321,0 7.5998684,46.4945378,0 7.5997681,46.4942244,0 7.5995237,46.4939424,0 7.599367,46.4938891,0 7.5991508,46.4939111,0 7.5990442,46.4937136,0 7.5990881,46.4935162,0 7.5990317,46.4933814,0 7.598853,46.4932811,0 7.5985396,46.4930116,0 7.5979849,46.4925979,0 7.5971767,46.4928758,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5557343,46.4981656,0 7.5557397,46.4982247,0 7.55567,46.4982801,0 7.555568,46.4983096,0 7.5554661,46.4983761,0 7.5554232,46.4984906,0 7.5553964,46.4986457,0 7.5553857,46.4988008,0 7.5554286,46.4989522,0 7.5554232,46.4990888,0 7.5554822,46.499159,0 7.5556753,46.499303,0 7.5558845,46.4995024,0 7.5559489,46.4995356,0 7.5560187,46.4997609,0 7.5559489,46.4997978,0 7.5560938,46.4999566,0 7.5562279,46.5000046,0 7.5562118,46.500108,0 7.5560669,46.500167,0 7.5556968,46.5001449,0 7.5555037,46.5000932,0 7.5552462,46.500108,0 7.5550048,46.5000304,0 7.554876,46.4999123,0 7.5546346,46.4999972,0 7.5545649,46.500156,0 7.5546829,46.5005068,0 7.5547473,46.5005622,0 7.5545005,46.5007431,0 7.5545381,46.5009462,0 7.5548868,46.5011013,0 7.5549941,46.5013228,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Via Ferrata Dauberhorn</name>
    +          <description>Via Ferrata Dauberhorn, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.615535,46.3933165,0 7.6153571,46.3931316,0 7.6148421,46.392954,0 7.6139623,46.39288,0 7.6134473,46.3929836,0 7.6128036,46.3930724,0 7.6122672,46.3931316,0 7.6120311,46.3928356,0 7.6117736,46.3925988,0 7.6112801,46.3924212,0 7.6111084,46.3922288,0 7.611087,46.3919624,0 7.6113016,46.391696,0 7.6112801,46.3913112,0 7.6113445,46.390956,0 7.6112586,46.39066,0 7.6112801,46.3902012,0 7.6111728,46.389846,0 7.6108295,46.38955,0 7.610572,46.3892244,0 7.610572,46.3888248,0 7.6105398,46.3886545,0 7.6103145,46.3883363,0 7.6101858,46.3880403,0 7.6097459,46.3876851,0 7.6094884,46.3875667,0 7.6091558,46.3875667,0 7.6088339,46.3874705,0 7.6088125,46.3873743,0 7.609188,46.3874261,0 7.6093382,46.3874557,0 7.6095742,46.3874335,0 7.6099497,46.3874853,0 7.6099605,46.3876185,0 7.6099712,46.3881735,0 7.6098854,46.3883659,0 7.6095528,46.3885953,0 7.6094777,46.3884399,0 7.6094884,46.3880847,0 7.6094884,46.3879071,0 7.6096279,46.3873151,0 7.6099712,46.3873077,0 7.6101428,46.3876925,0 7.6101321,46.3885065,0 7.6099175,46.3892762,0 7.6095742,46.3900384,0 7.6088125,46.3906748,0 7.6087481,46.3897424,0 7.6089198,46.3888692,0 7.6091129,46.3883511,0 7.609542,46.3869968,0 7.609424,46.3867526,0 7.6091021,46.38676,0 7.6085121,46.3868858,0 7.6081687,46.3872855,0 7.6079327,46.3880329,0 7.6077289,46.3895722,0 7.6074821,46.3904454,0 7.6072139,46.3912964,0 7.606538,46.390068,0 7.6069393,46.3852733,0 7.6064139,46.3847204,0 7.605748,46.3839582,0 7.6053188,46.3836037,0 7.6041132,46.383141,0 7.6048643,46.3836591,0 7.6043278,46.3840735,0 7.6056797,46.3849913,0 7.6043064,46.3843844,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Via Ferrata Dauberhorn</name>
    +          <description>Via Ferrata Dauberhorn, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6043064,46.3843844,0 7.6048126,46.3847763,0 7.6042547,46.3845839,0 7.6050486,46.3850872,0 7.6058425,46.3852944,0 7.6066794,46.3852944,0 7.6065292,46.3857976,0 7.6058425,46.3860789,0 7.6052846,46.3862713,0 7.6052632,46.3864785,0 7.6045551,46.3867005,0 7.6042976,46.3868781,0 7.6040186,46.3870409,0 7.6034393,46.3870853,0 7.6030959,46.3870705,0 7.6028384,46.3874554,0 7.6026829,46.3875479,0 7.6022051,46.3877699,0 7.6020231,46.3879142,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Lämmernweg süd</name>
    +          <description>Lämmernweg süd, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5773607,46.3980323,0 7.5767505,46.3977455,0 7.5767827,46.3975531,0 7.5771046,46.3971092,0 7.5777805,46.3966134,0 7.578671,46.3962953,0 7.5799799,46.3960807,0 7.5813317,46.3957403,0 7.5844109,46.3950966,0 7.5849366,46.394993,0 7.5874471,46.3949782,0 7.5887775,46.3952742,0 7.5891478,46.3953803,0 7.5896036,46.3955109,0 7.5912022,46.3955923,0 7.5928438,46.3956663,0 7.5953221,46.3960955,0 7.5978327,46.3964729,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4394297,46.5665636,0 7.4395718,46.5667101,0 7.4396474,46.5668294,0 7.4396594,46.5670283,0 7.43954,46.5671159,0 7.4394047,46.5671875,0 7.4392894,46.5672074,0 7.4391501,46.5671954,0 7.4389114,46.5673228,0 7.4388159,46.5673267,0 7.4387125,46.567275,0 7.4385454,46.5672352,0 7.4383464,46.5672551,0 7.4380719,46.5673307,0 7.4378372,46.5674421,0 7.4374911,46.5675217,0 7.4371967,46.5675575,0 7.4370017,46.5675336,0 7.4367789,46.5676848,0 7.4365203,46.5677087,0 7.4361185,46.5676768,0 7.4359076,46.567832,0 7.435649,46.5677922,0 7.4354302,46.5678758,0 7.4352074,46.5680031,0 7.4350443,46.5679633,0 7.4344435,46.5681304,0 7.4341173,46.5683413,0 7.4341213,46.5684407,0 7.4340775,46.5686038,0 7.433791,46.5688664,0 7.4334568,46.5690216,0 7.4333256,46.5691887,0 7.4332579,46.5693836,0 7.4332221,46.5695706,0 7.4329834,46.5699486,0 7.4325935,46.5701714,0 7.4324224,46.57043,0 7.4323389,46.5706607,0 7.432323,46.5708119,0 7.4320246,46.5709154,0 7.4319888,46.5710069,0 7.4320485,46.5712058,0 7.4320087,46.5712456,0 7.431384,46.5713013,0 7.4307395,46.5714246,0 7.4300552,46.5712933,0 7.4297688,46.5712615,0 7.4294624,46.5712973,0 7.4290526,46.5715161,0 7.4283604,46.5715877,0 7.4282728,46.5715479,0 7.4280023,46.5715798,0 7.4278113,46.5716554,0 7.4274175,46.5716713,0 7.4271111,46.5717349,0 7.4268724,46.5719498,0 7.4266138,46.5720055,0 7.4264746,46.5720055,0 7.4259096,46.5722163,0 7.4258221,46.5722044,0 7.4256749,46.5721049,0 7.425472,46.5719975,0 7.4253486,46.5719975,0 7.4250622,46.5720612,0 7.4247757,46.5721049,0 7.4246325,46.5721686,0 7.4244216,46.5722124,0 7.4240874,46.5722402,0 7.4239562,46.5722879,0 7.4238408,46.5724312,0 7.4236856,46.5724789,0 7.4235185,46.5724471,0 7.4231963,46.5723635,0 7.4231008,46.5724988,0 7.4231286,46.5728648,0 7.4230928,46.5729404,0 7.4230212,46.5729802,0 7.4229257,46.5729524,0 7.4227706,46.5728211,0 7.4226114,46.5727296,0 7.4224284,46.5726341,0 7.4222096,46.572471,0 7.4215889,46.5722482,0 7.4215452,46.5723277,0 7.4215929,46.5723834,0 7.4215571,46.5727773,0 7.4214815,46.5728728,0 7.4213343,46.5729484,0 7.4211274,46.5729325,0 7.4209086,46.5729245,0 7.420646,46.5727892,0 7.420467,46.5727057,0 7.4202283,46.5726261,0 7.4197151,46.5725465,0 7.419548,46.5725028,0 7.419353,46.5725426,0 7.4192615,46.5724908,0 7.4188477,46.5723596,0 7.4185573,46.5721845,0 7.4179088,46.5719179,0 7.4174234,46.5718185,0 7.4172126,46.571727,0 7.416954,46.5717111,0 7.4165123,46.5716076,0 7.4162418,46.5714405,0 7.4161264,46.5713928,0 7.4156689,46.5715042,0 7.4153228,46.5715559,0 7.4149886,46.571528,0 7.4146743,46.571544,0 7.4142366,46.5714684,0 7.4134688,46.5716116,0 7.4130749,46.5717031,0 7.4125378,46.5717707,0 7.4118853,46.5718145,0 7.4110339,46.5716872,0 7.4109225,46.5716315,0 7.4106321,46.5716713,0 7.4103218,46.5716753,0 7.4097767,46.5716156,0 7.4092555,46.5715241,0 7.408607,46.5713689,0 7.408249,46.5712655,0 7.4079745,46.5712177,0 7.4076681,46.5710307,0 7.4073777,46.5708955,0 7.4071628,46.5707363,0 7.4070594,46.5707562,0 7.4071589,46.5708517,0 7.407314,46.5709711,0 7.4075209,46.5712575,0 7.407688,46.5714286,0 7.4078829,46.5716434,0 7.4081057,46.5718543,0 7.4081654,46.5720254,0 7.4081376,46.5721049,0 7.4079585,46.5719935,0 7.407684,46.5718782,0 7.4070793,46.5717906,0 7.4064666,46.5714843,0 7.406021,46.5713172,0 7.4052651,46.5711382,0 7.404915,46.5710228,0 7.4045649,46.5709432,0 7.4043023,46.5708437,0 7.4040039,46.5706846,0 7.4038567,46.5706528,0 7.4035344,46.5704578,0 7.4032838,46.5704459,0 7.4031525,46.5702549,0 7.4031008,46.5701873,0 7.4028024,46.5699048,0 7.4027268,46.5698969,0 7.4023329,46.5696462,0 7.4020425,46.5695348,0 7.4018197,46.5694871,0 7.401569,46.5693279,0 7.4014815,46.56932,0 7.4013423,46.5692404,0 7.4011195,46.5689937,0 7.4010399,46.5689977,0 7.4007733,46.5687789,0 7.4006858,46.5686357,0 7.4006062,46.5684288,0 7.4004391,46.5683094,0 7.4001487,46.5681344,0 7.3999975,46.5679593,0 7.3998662,46.5678758,0 7.3997509,46.5677524,0 7.3995639,46.567641,0 7.3993451,46.5674302,0 7.3990148,46.5673029,0 7.3984857,46.5671079,0 7.3981833,46.5669528,0 7.3980083,46.5668851,0 7.3978571,46.5668016,0 7.3973797,46.5667817,0 7.3971051,46.5666544,0 7.3968147,46.5664793,0 7.3964208,46.566344,0 7.3961543,46.5663241,0 7.3961145,46.5662764,0 7.3956251,46.5661809,0 7.3953745,46.5661093,0 7.3949528,46.5660536,0 7.3946385,46.5659939,0 7.3942446,46.5658825,0 7.3938348,46.565787,0 7.3933614,46.5656876,0 7.3930789,46.5655921,0 7.3927089,46.5654767,0 7.3922474,46.5653932,0 7.3918774,46.5653454,0 7.3916745,46.565238,0 7.3916387,46.5650073,0 7.3913045,46.5648402,0 7.3911772,46.5648521,0 7.3907833,46.564681,0 7.3905883,46.5646651,0 7.3906082,46.5648123,0 7.3909146,46.565047,0 7.391022,46.5653733,0 7.3909544,46.5655563,0 7.3911175,46.5658229,0 7.3915392,46.5658865,0 7.3919211,46.5661133,0 7.3919092,46.5663043,0 7.3917182,46.5663281,0 7.3913244,46.5662167,0 7.3909026,46.5661929,0 7.3904849,46.5662525,0 7.3901452,46.5663573,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4920336,46.5215194,0 7.4920087,46.5216089,0 7.4916557,46.5219819,0 7.4912678,46.5220963,0 7.491044,46.5220913,0 7.4906312,46.522335,0 7.4903278,46.5224941,0 7.4898653,46.5229069,0 7.4892835,46.5233495,0 7.4883087,46.5239214,0 7.4876324,46.5242944,0 7.4875777,46.5244237,0 7.4875578,46.5246624,0 7.4874981,46.5247967,0 7.4872644,46.524931,0 7.4871052,46.524921,0 7.4868019,46.5248116,0 7.4862598,46.524747,0 7.4861802,46.5247768,0 7.4861454,46.524926,0 7.4861554,46.5253239,0 7.4860261,46.5256571,0 7.4859564,46.5257714,0 7.4858968,46.5258112,0 7.485668,46.525851,0 7.4854392,46.5258361,0 7.4852154,46.5258013,0 7.4850463,46.5258063,0 7.4848424,46.5258958,0 7.4844297,46.5260201,0 7.484191,46.5260002,0 7.4837782,46.5262041,0 7.483445,46.5262787,0 7.4831466,46.5262091,0 7.4827189,46.5262588,0 7.4817392,46.5265423,0 7.4814607,46.5266964,0 7.4812469,46.5268854,0 7.4807297,46.5270744,0 7.480302,46.5271788,0 7.4800981,46.5273479,0 7.479556,46.5275717,0 7.4792775,46.5276612,0 7.4791482,46.5277607,0 7.4786608,46.5278353,0 7.4783127,46.5279546,0 7.4781536,46.5281536,0 7.4776612,46.5283575,0 7.4771142,46.5283973,0 7.4767561,46.5285116,0 7.476224,46.528626,0 7.4753935,46.5286807,0 7.4750951,46.5287056,0 7.4749906,46.52882,0 7.4750454,46.5289642,0 7.4750006,46.5291482,0 7.4748713,46.529362,0 7.4747171,46.5294814,0 7.4744436,46.5296008,0 7.4740706,46.5297301,0 7.4737921,46.5298444,0 7.4731953,46.5298942,0 7.4731207,46.5298842,0 7.472882,46.5300334,0 7.4728025,46.5302025,0 7.472703,46.5303169,0 7.4719023,46.5305705,0 7.4717432,46.5306998,0 7.4716537,46.5310579,0 7.4713603,46.5315602,0 7.4712409,46.5316696,0 7.4711116,46.5316994,0 7.4711912,46.531784,0 7.4711812,46.5318834,0 7.4712359,46.5319481,0 7.4713652,46.531968,0 7.471589,46.5321122,0 7.471599,46.5321768,0 7.4715642,46.5323111,0 7.4716338,46.5325001,0 7.4716935,46.5325299,0 7.4716785,46.5326095,0 7.4715741,46.5327189,0 7.4715393,46.5328482,0 7.4715244,46.5329676,0 7.4715642,46.5331217,0 7.4716835,46.5332908,0 7.4716933,46.5334754,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4751759,46.5539538,0 7.475336,46.5540147,0 7.4755769,46.5537815,0 7.4758178,46.5538048,0 7.4759654,46.5540224,0 7.4758722,46.5540768,0 7.4759499,46.5545431,0 7.4757789,46.5555377,0 7.4757945,46.5557786,0 7.4757246,46.5560117,0 7.4758023,46.5561516,0 7.4757028,46.5567762,0 7.4756876,46.5570949,0 7.4758849,46.5573377,0 7.475976,46.5578234,0 7.4759608,46.5582331,0 7.4760974,46.5586126,0 7.4763554,46.5587491,0 7.4763554,46.5589464,0 7.4766134,46.5592803,0 7.4772205,46.559599,0 7.4774633,46.5598571,0 7.4778882,46.5599785,0 7.47804,46.5601606,0 7.4781007,46.5604489,0 7.4784498,46.5608132,0 7.4787685,46.5611167,0 7.4793452,46.5616479,0 7.4800282,46.5620425,0 7.4803621,46.5621639,0 7.4807111,46.562513,0 7.4810754,46.5630442,0 7.48147,46.5633477,0 7.4816369,46.5636816,0 7.4821226,46.5641673,0 7.4826386,46.5644556,0 7.483276,46.5651841,0 7.4837465,46.5655939,0 7.4843839,46.5659885,0 7.4848392,46.5666107,0 7.4854159,46.567066,0 7.4855981,46.5674303,0 7.4862658,46.5682195,0 7.4866453,46.56869,0 7.4867515,46.5689176,0 7.4869488,46.569206,0 7.4868881,46.5694792,0 7.4866453,46.5697675,0 7.4867667,46.5700407,0 7.4867375,46.5701979,0 7.4866159,46.5701456,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4528183,46.5206213,0 7.4533716,46.5207021,0 7.4542419,46.5206399,0 7.4545279,46.520584,0 7.4548014,46.5206151,0 7.4551371,46.5207394,0 7.4557836,46.5207891,0 7.4566787,46.5207829,0 7.4572817,46.5208202,0 7.4578536,46.5207954,0 7.4583945,46.5206897,0 7.4586369,46.5206772,0 7.4588296,46.5207145,0 7.4591964,46.5206959,0 7.4596502,46.5206586,0 7.4601413,46.5207394,0 7.4604024,46.5207208,0 7.4610924,46.5204721,0 7.4613908,46.5203105,0 7.4618881,46.5202421,0 7.4627025,46.5198069,0 7.4632174,46.5198895,0 7.4636909,46.5198059,0 7.4639455,46.5197423,0 7.4643274,46.5195314,0 7.4645502,46.5194877,0 7.4647293,46.5193643,0 7.4653181,46.519245,0 7.4656483,46.5191933,0 7.4657677,46.519249,0 7.4660143,46.5192211,0 7.4662252,46.5191535,0 7.4665793,46.5191336,0 7.4671243,46.5190222,0 7.4672715,46.5190659,0 7.4677728,46.5191376,0 7.4680951,46.519233,0 7.4684969,46.5192927,0 7.4687317,46.5193484,0 7.469046,46.5193962,0 7.4693443,46.5193484,0 7.4697104,46.5193604,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5644603,46.4461059,0 7.5645107,46.4465933,0 7.5642589,46.446939,0 7.5639358,46.4470668,0 7.5637291,46.4472922,0 7.56347,46.4474613,0 7.5637404,46.4476568,0 7.5639733,46.447871,0 7.5642364,46.44801,0 7.5643529,46.4480589,0 7.5643905,46.4482881,0 7.5643341,46.4484234,0 7.5645483,46.448709,0 7.5648114,46.4488029,0 7.5651457,46.4489852,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5614623,46.4593025,0 7.5611273,46.4593835,0 7.560775,46.4592943,0 7.5603616,46.4591768,0 7.5601173,46.4593272,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6045778,46.4977876,0 7.6046204,46.4977082,0 7.6049205,46.4975268,0 7.6050087,46.4974899,0 7.6051066,46.4975268,0 7.6050665,46.4973744,0 7.6049991,46.4971754,0 7.6049301,46.4970679,0 7.6047729,46.4969989,0 7.6044584,46.4969508,0 7.6042706,46.4969203,0 7.6042706,46.4967968,0 7.6044664,46.4966941,0 7.6044532,46.4966183,0 7.604413,46.4964618,0 7.6044291,46.4962673,0 7.6043749,46.4958962,0 7.6043509,46.4956034,0 7.6043288,46.4952464,0 7.6041383,46.4949355,0 7.6040039,46.4946908,0 7.6038595,46.4943719,0 7.603683,46.4940329,0 7.6035305,46.4937601,0 7.6033761,46.4934713,0 7.6033059,46.4931384,0 7.6030772,46.493014,0 7.6028792,46.4927175,0 7.602652,46.4923963,0 7.6025894,46.4922122,0 7.60237,46.4919497,0 7.6021075,46.4915932,0 7.6019273,46.4914444,0 7.6019195,46.4913464,0 7.6017354,46.4910056,0 7.6014985,46.4907235,0 7.6012378,46.4904267,0 7.6011402,46.490361,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.561897,46.4519024,0 7.562231,46.4519425,0 7.5622941,46.4520086,0 7.5625157,46.4520579,0 7.5627528,46.4519979,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.560335,46.449238,0 7.5600857,46.4492598,0 7.5598715,46.449117,0 7.5596084,46.4490493,0 7.5593942,46.4489366,0 7.5593266,46.4488765,0 7.5591725,46.4486059,0 7.5593792,46.4484631,0 7.5597023,46.4483128,0 7.5599917,46.4481512,0 7.5604201,46.4480836,0 7.5608598,46.4481813,0 7.560985,46.448126,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6042768,46.486003,0 7.6044596,46.4859007,0 7.6047448,46.4859446,0 7.6050206,46.4858788,0 7.6052901,46.4858161,0 7.6055001,46.485719,0 7.605633,46.4855823,0 7.6054326,46.4855797,0 7.6054207,46.4855607,0 7.6056389,46.4855403,0 7.6056593,46.4855377,0 7.6058197,46.4854916,0 7.6059853,46.4853622,0 7.6061485,46.4851949,0 7.6063972,46.4851147,0 7.6065223,46.4850104,0 7.6065753,46.4848692,0 7.606816,46.4848274,0 7.6069893,46.4848066,0 7.6069796,46.4846285,0 7.606771,46.4844584,0 7.6065801,46.4842642,0 7.6063843,46.484115,0 7.606256,46.4839337,0 7.6060855,46.4838297,0 7.6060184,46.4837626,0 7.6060141,46.4837445,0 7.6059921,46.4837075,0 7.6058481,46.4835616,0 7.6058982,46.4834733,0 7.6056119,46.4834351,0 7.6054002,46.4833029,0 7.6053025,46.4831814,0 7.6052052,46.4830648,0 7.6050153,46.4830218,0 7.6047188,46.4829161,0 7.6046228,46.4829174,0 7.6045128,46.4828032,0 7.6044513,46.4827961,0 7.6043955,46.4827634,0 7.6044703,46.4828142,0 7.6045465,46.482924,0 7.6044778,46.4829415,0 7.6043781,46.4828328,0 7.6043175,46.4827661,0 7.6042463,46.4827656,0 7.6041887,46.4826955,0 7.6040464,46.4825943,0 7.6040113,46.4825787,0 7.6039903,46.4825597,0 7.6038715,46.4825311,0 7.6038475,46.482703,0 7.6039627,46.4827992,0 7.6040605,46.4830684,0 7.6041515,46.4832651,0 7.6041476,46.4833395,0 7.6040977,46.483453,0 7.6039332,46.4834921,0 7.6039313,46.4834285,0 7.6040497,46.4833297,0 7.6040067,46.4832338,0 7.6039313,46.4831124,0 7.6039078,46.4830821,0 7.603763,46.4829225,0 7.6036245,46.4827777,0 7.6037187,46.4825648,0 7.6038679,46.4823875,0 7.6037994,46.4823287,0 7.6038178,46.4822847,0 7.6039364,46.4821673,0 7.6037835,46.4818871,0 7.6037431,46.4817036,0 7.603841,46.4815727,0 7.6038643,46.4815482,0 7.6039425,46.4814369,0 7.6038936,46.4812669,0 7.6039181,46.4811323,0 7.6038031,46.4809953,0 7.6036538,46.4807665,0 7.6036844,46.4806895,0 7.6037015,46.4804925,0 7.6038973,46.4803298,0 7.6039291,46.4800937,0 7.6038899,46.4799799,0 7.603956,46.479898,0 7.6040637,46.4797976,0 7.6042545,46.4797316,0 7.6041982,46.4795677,0 7.6042007,46.4795187,0 7.6041383,46.4792924,0 7.6042166,46.4790942,0 7.6045198,46.4787531,0 7.6047549,46.4784167,0 7.6049491,46.4781901,0 7.6050274,46.4781117,0 7.6049512,46.4779239,0 7.6049965,46.4777746,0 7.6050332,46.4775887,0 7.6050863,46.477561,0 7.60505,46.4773811,0 7.6049021,46.4772892,0 7.6047002,46.4773577,0 7.6047076,46.4775228,0 7.60471,46.4776452,0 7.6046109,46.4778287,0 7.6045338,46.4776733,0 7.6044727,46.4775265,0 7.6045718,46.477392,0 7.6046623,46.4773014,0 7.6049033,46.4771399,0 7.605214,46.4770372,0 7.6053486,46.4770335,0 7.6053364,46.4767656,0 7.6050538,46.4765845,0 7.6047027,46.4765907,0 7.6043295,46.4765246,0 7.6042133,46.4764573,0 7.6040066,46.4762726,0 7.6039821,46.4761698,0 7.6040629,46.4758762,0 7.6039234,46.4757062,0 7.6037375,46.475568,0 7.6034536,46.4754444,0 7.6030976,46.4754884,0 7.6028175,46.4755043,0 7.6026658,46.4754676,0 7.602393,46.4754187,0 7.6020468,46.4753686,0 7.601785,46.4753172,0 7.6017104,46.4752792,0 7.6017006,46.4750994,0 7.6018303,46.4748584,0 7.6018621,46.4746162,0 7.601703,46.4745061,0 7.60144,46.4744156,0 7.601248,46.4743128,0 7.6008565,46.47427,0 7.6007966,46.4742431,0 7.6007293,46.4742162,0 7.6006033,46.4741648,0 7.6005372,46.47427,0 7.6003415,46.4743948,0 7.5999989,46.474544,0 7.5998839,46.4746052,0 7.5997023,46.4745672,0 7.5996295,46.4745073,0 7.5995414,46.4745061,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6073503,46.5155013,0 7.6071538,46.515465,0 7.606817,46.5154259,0 7.6065284,46.5153838,0 7.6061659,46.5153941,0 7.60594,46.5155194,0 7.6059144,46.5154506,0 7.6057305,46.5154896,0 7.6054882,46.5154968,0 7.6052941,46.5154126,0 7.6049911,46.5153746,0 7.6047252,46.5152986,0 7.6044397,46.5153243,0 7.6041881,46.5153089,0 7.603904,46.5152803,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5109074,46.4975939,0 7.5106958,46.4976333,0 7.5103212,46.4976753,0 7.510222,46.4976293,0 7.5098982,46.4976558,0 7.5096505,46.4976143,0 7.5095384,46.4976181,0 7.5091324,46.4975663,0 7.5088809,46.497604,0 7.5085719,46.4976313,0 7.5082007,46.4977481,0 7.5079323,46.4977971,0 7.5077203,46.4979365,0 7.5074575,46.4980448,0 7.507172,46.4981654,0 7.5070421,46.498269,0 7.5068461,46.4983538,0 7.5066002,46.4983557,0 7.5064655,46.4984311,0 7.5062762,46.498416,0 7.5062112,46.4984424,0 7.5060416,46.4984716,0 7.5059521,46.4985469,0 7.5057421,46.4986138,0 7.5055384,46.4987028,0 7.5053641,46.4987009,0 7.505203,46.4987386,0 7.5050561,46.4986877,0 7.5049346,46.4985756,0 7.5048093,46.4985426,0 7.5046887,46.4987404,0 7.5046925,46.4988422,0 7.5046143,46.4989044,0 7.5045493,46.4988328,0 7.504423,46.4987762,0 7.5041934,46.4987215,0 7.504058,46.4986995,0 7.5040786,46.4985685,0 7.5039829,46.4985596,0 7.5037725,46.4985479,0 7.5036621,46.4985258,0 7.5035502,46.4985199,0 7.5034943,46.4985184,0 7.5031999,46.4985287,0 7.5030115,46.4984581,0 7.5028319,46.498355,0 7.5027097,46.4982844,0 7.5025891,46.4982505,0 7.5024065,46.4984066,0 7.5022402,46.4985802,0 7.5021254,46.4987245,0 7.5021195,46.4988496,0 7.5020165,46.4989394,0 7.5020224,46.4991616,0 7.5021328,46.4993324,0 7.5021211,46.4993741,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5415101,46.4829137,0 7.5411092,46.4830045,0 7.5405377,46.4830449,0 7.5399432,46.4831921,0 7.5395854,46.4833624,0 7.5389331,46.4834865,0 7.5383761,46.4836019,0 7.537822,46.4836856,0 7.5371957,46.4838472,0 7.5368933,46.4838149,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5605804,46.4538416,0 7.5606075,46.4535339,0 7.5606906,46.4534892,0 7.5606227,46.4533264,0 7.5607983,46.4530402,0 7.5605217,46.4530282,0 7.5602932,46.4529945,0 7.5601489,46.4529392,0 7.5598771,46.452997,0 7.5600647,46.4528719,0 7.5600527,46.4527228,0 7.559995,46.4525761,0 7.5599204,46.4524606,0 7.5598411,46.4522947,0 7.5597232,46.4521792,0 7.5596078,46.4520566,0 7.5597112,46.4519026,0 7.5597881,46.4518401,0 7.5598699,46.4518281,0 7.55993,46.4518088,0 7.5599325,46.4517247,0 7.5598755,46.4516727,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5952104,46.4388413,0 7.5950256,46.4384877,0 7.5948831,46.4383082,0 7.5947934,46.4380997,0 7.5943235,46.4378331,0 7.5942576,46.4376351,0 7.5943499,46.4373606,0 7.5942655,46.4370122,0 7.5942971,46.4366611,0 7.5941916,46.4363998,0 7.5938564,46.4360514,0 7.5936689,46.4358772,0 7.5934235,46.4354338,0 7.5932202,46.4351381,0 7.5930117,46.4349323,0 7.5925736,46.4346314,0 7.5923492,46.4345284,0 7.5919374,46.4340348,0 7.5918899,46.4339108,0 7.5919454,46.433726,0 7.5918477,46.4333697,0 7.5916233,46.4332747,0 7.5914307,46.4327969,0 7.5913409,46.4325145,0 7.5913013,46.4322426,0 7.5912459,46.4320011,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5296583,46.4300374,0 7.5292858,46.4300524,0 7.5290588,46.4300577,0 7.5289348,46.4300867,0 7.5287368,46.4300762,0 7.5285785,46.4301184,0 7.5285204,46.4302715,0 7.5283462,46.4303111,0 7.5282274,46.4304193,0 7.5280031,46.4305117,0 7.5277813,46.4306278,0 7.5275095,46.4308363,0 7.5272006,46.4309762,0 7.5269367,46.4311716,0 7.5266701,46.4313062,0 7.5262715,46.431813,0 7.5259997,46.4321112,0 7.525873,46.4322221,0 7.5256011,46.4324781,0 7.5255826,46.4325599,0 7.5254058,46.4326101,0 7.5254111,46.4327157,0 7.5252289,46.4327447,0 7.5251392,46.4328793,0 7.5251366,46.4329374,0 7.5250389,46.4329691,0 7.5250811,46.4330694,0 7.5249967,46.4331063,0 7.5249967,46.4331565,0 7.524928,46.4332568,0 7.5249175,46.4333096,0 7.524775,46.4332884,0 7.5247281,46.4333904,0 7.5245886,46.433383,0 7.524138,46.4334496,0 7.5238591,46.4335457,0 7.5237089,46.4336123,0 7.5234836,46.4336862,0 7.5233065,46.4337749,0 7.5231939,46.4338415,0 7.523033,46.4339043,0 7.5227218,46.4340411,0 7.5225931,46.4341409,0 7.5225394,46.4342075,0 7.5223624,46.4343221,0 7.5223088,46.4344441,0 7.5221961,46.4345291,0 7.5220942,46.4346327,0 7.5219815,46.4347362,0 7.5215148,46.4349654,0 7.5211608,46.4351613,0 7.5210267,46.4352648,0 7.5209087,46.4353055,0 7.5208228,46.4353277,0 7.5207477,46.4353758,0 7.5206619,46.4353721,0 7.520619,46.4353351,0 7.5206136,46.4353129,0 7.5205707,46.4352685,0 7.520501,46.4352981,0 7.5204634,46.4353203,0 7.5204205,46.4353129,0 7.520399,46.4352685,0 7.5203829,46.435239,0 7.5203883,46.4352094,0 7.5204312,46.4351428,0 7.5204527,46.4351022,0 7.5204473,46.4350763,0 7.5203937,46.4350652,0 7.5203025,46.4350874,0 7.5202542,46.4350837,0 7.5201791,46.4350837,0 7.5200879,46.4351133,0 7.5200503,46.4351281,0 7.5199538,46.4351281,0 7.519884,46.435165,0 7.5198411,46.4351872,0 7.5198036,46.4352242,0 7.5197446,46.4352205,0 7.5197124,46.4351576,0 7.5197285,46.435117,0 7.519766,46.4350615,0 7.5197177,46.435043,0 7.519648,46.4350615,0 7.5196051,46.43508,0 7.5195783,46.435043,0 7.5195944,46.4350024,0 7.5195783,46.4349765,0 7.51953,46.4349654,0 7.519471,46.4350024,0 7.5194173,46.4350098,0 7.5193959,46.4349876,0 7.5193637,46.4349617,0 7.5193047,46.4349728,0 7.5192457,46.434958,0 7.5192028,46.4349173,0 7.5192135,46.4348804,0 7.5191813,46.4348397,0 7.5191813,46.4347916,0 7.5191438,46.434751,0 7.519074,46.4347399,0 7.5189936,46.4347399,0 7.5189292,46.4347251,0 7.5188594,46.4347288,0 7.5188112,46.4347657,0 7.5187682,46.4348212,0 7.5187146,46.4348508,0 7.5186341,46.4348508,0 7.5185161,46.4348582,0 7.5184625,46.4348841,0 7.5184625,46.4349432,0 7.5185054,46.4350282,0 7.5185537,46.4350874,0 7.5185751,46.4351355,0 7.5185751,46.435202,0 7.5185215,46.4352427,0 7.5184464,46.4352575,0 7.5182586,46.4352612,0 7.5180441,46.4352722,0 7.517808,46.4353499,0 7.5176471,46.4354275,0 7.5175827,46.4354793,0 7.5175022,46.4355052,0 7.5174915,46.4354719,0 7.5175076,46.4354386,0 7.5174325,46.4354608,0 7.5173628,46.4354571,0 7.5173091,46.4354904,0 7.5172662,46.4354682,0 7.5173252,46.4354349,0 7.5172769,46.4354201,0 7.5172072,46.4354386,0 7.5171482,46.4354608,0 7.5171267,46.4355015,0 7.5171106,46.4356272,0 7.5170731,46.4357122,0 7.5169819,46.4357676,0 7.5168961,46.4357676,0 7.516778,46.435812,0 7.5166761,46.4358305,0 7.5165635,46.4358083,0 7.5164991,46.4357972,0 7.5164079,46.4358231,0 7.5162684,46.4358342,0 7.5162041,46.4358896,0 7.5161611,46.4359451,0 7.5161772,46.4360005,0 7.5161504,46.4360634,0 7.5161129,46.436141,0 7.5160646,46.4362335,0 7.5160914,46.4362815,0 7.5160109,46.4363555,0 7.515968,46.4364183,0 7.5159251,46.4364922,0 7.5158607,46.4365181,0 7.5157749,46.4364738,0 7.5157105,46.4363702,0 7.515614,46.436263,0 7.515555,46.4362409,0 7.5155174,46.4362889,0 7.5155174,46.4363629,0 7.5155442,46.4364331,0 7.515555,46.4365033,0 7.5155496,46.4365736,0 7.5155925,46.4366253,0 7.5156408,46.4367067,0 7.5156676,46.4367843,0 7.5156462,46.436825,0 7.5155657,46.4368952,0 7.5155389,46.4369618,0 7.5155764,46.4370283,0 7.5155657,46.4370837,0 7.5155281,46.4370468,0 7.5154691,46.4369913,0 7.5154584,46.4370431,0 7.5154048,46.4370505,0 7.5153511,46.436995,0 7.5153457,46.4370579,0 7.5153779,46.437117,0 7.5153726,46.4371688,0 7.5153511,46.4371947,0 7.5152814,46.4371836,0 7.5152277,46.4372353,0 7.5151848,46.4372575,0 7.5151526,46.4373093,0 7.5151044,46.437313,0 7.5151151,46.4372538,0 7.5150507,46.4372575,0 7.5150078,46.4372908,0 7.5149434,46.437276,0 7.5149434,46.4372131,0 7.514922,46.4371799,0 7.514863,46.4371836,0 7.5148254,46.4372316,0 7.5148147,46.4372871,0 7.5147878,46.4372575,0 7.5147718,46.4372316,0 7.5147449,46.4373203,0 7.5147449,46.4374202,0 7.514702,46.4375422,0 7.5146001,46.4376679,0 7.5144928,46.4377159,0 7.5144231,46.4377085,0 7.5142085,46.4377566,0 7.5141441,46.4377972,0 7.5141173,46.4378342,0 7.5140315,46.4378675,0 7.5139564,46.4379155,0 7.5138544,46.4379377,0 7.5137096,46.4379747,0 7.5135862,46.4380671,0 7.5134736,46.4380597,0 7.5132107,46.4380819,0 7.5131088,46.438141,0 7.5130337,46.4382113,0 7.5129639,46.4382298,0 7.5128084,46.438215,0 7.5125884,46.438263,0 7.5123631,46.4383185,0 7.5121915,46.4383148,0 7.51217,46.4383444,0 7.5120466,46.4383739,0 7.5119501,46.4383739,0 7.511757,46.4383776,0 7.5116952,46.4384008,0 7.5115799,46.4384442,0 7.5114083,46.4385662,0 7.5112956,46.438666,0 7.5110864,46.4387067,0 7.5109255,46.4387436,0 7.5108128,46.4388508,0 7.5107109,46.4389987,0 7.5106197,46.4390431,0 7.5105285,46.4392242,0 7.510389,46.4394017,0 7.510094,46.4396382,0 7.5098633,46.439812,0 7.5097077,46.4399155,0 7.5096434,46.4400523,0 7.5094395,46.4402223,0 7.5090587,46.4404256,0 7.5088387,46.4405365,0 7.5085919,46.4405809,0 7.5084096,46.4406068,0 7.508254,46.4406511,0 7.5080823,46.4406622,0 7.5078892,46.4406585,0 7.5077015,46.4406844,0 7.5075781,46.4407362,0 7.5075244,46.4408138,0 7.5075191,46.4409025,0 7.507503,46.440969,0 7.5074279,46.4410393,0 7.5072723,46.4411206,0 7.5071704,46.4411724,0 7.507047,46.4412093,0 7.5069343,46.4412943,0 7.5068217,46.4413276,0 7.5067037,46.4413128,0 7.5064676,46.4412907,0 7.506119,46.4413054,0 7.5058132,46.4413239,0 7.5056415,46.4413387,0 7.5054323,46.4412943,0 7.5052714,46.4412463,0 7.5051265,46.4412352,0 7.5049656,46.4412685,0 7.5048529,46.4413054,0 7.5047296,46.441372,0 7.5045901,46.4413979,0 7.5045204,46.4414311,0 7.5045311,46.4414829,0 7.5046062,46.4414829,0 7.504633,46.4415198,0 7.5047671,46.4415235,0 7.5048905,46.4415642,0 7.50503,46.4415901,0 7.505148,46.4415605,0 7.5052285,46.4415494,0 7.505325,46.4415605,0 7.5053357,46.4415975,0 7.5052928,46.4416492,0 7.505148,46.4417527,0 7.5050407,46.4417971,0 7.5049173,46.4419191,0 7.5047671,46.4420558,0 7.5046491,46.4421409,0 7.5045043,46.4421593,0 7.5043809,46.4421002,0 7.5043004,46.4420374,0 7.5042468,46.4420004,0 7.5040644,46.4419893,0 7.5037908,46.4419154,0 7.5037532,46.4418636,0 7.5036674,46.441834,0 7.503603,46.4418045,0 7.5034367,46.4417638,0 7.5033402,46.4417084,0 7.5031685,46.4416492,0 7.5030451,46.441579,0 7.5028413,46.4415346,0 7.5026535,46.4415161,0 7.5024765,46.4415161,0 7.5023746,46.4415346,0 7.502219,46.4415568,0 7.5017952,46.4415568,0 7.5013553,46.4415272,0 7.5009423,46.4414903,0 7.5006848,46.4414755,0 7.5004905,46.4414532,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5531803,46.3637433,0 7.5528114,46.3639866,0 7.5525992,46.3642187,0 7.5525078,46.3645431,0 7.5526612,46.3646039,0 7.5525567,46.3646918,0 7.5527167,46.3648923,0 7.5526579,46.3649621,0 7.5531019,46.3651356,0 7.5533435,46.3653158,0 7.5534937,46.365487,0 7.553399,46.3657461,0 7.5533859,46.3658632,0 7.5540813,46.3660367,0 7.554398,46.3662507,0 7.554558,46.3662935,0 7.5544894,46.3665075,0 7.5575791,46.3665852,0 7.5581286,46.3669084,0 7.5585494,46.3672031,0 7.5593082,46.3680655,0 7.5601499,46.3683251,0 7.5608066,46.3683911,0 7.5614315,46.3685451,0 7.5621392,46.3686595,0 7.5623815,46.3688178,0 7.5628725,46.3687255,0 7.5632742,46.3687123,0 7.5640022,46.368933,0 7.5641483,46.3691113,0 7.5642859,46.3690848,0 7.5646324,46.3693502,0 7.5652508,46.369491,0 7.5654485,46.3693986,0 7.5658555,46.3695314,0 7.5661611,46.3693956,0 7.5664049,46.3694558,0 7.5665516,46.3694074,0 7.5667742,46.3694346,0 7.5668756,46.369562,0 7.5671841,46.3695809,0 7.5675053,46.3697029,0 7.5678332,46.3697682,0 7.5682137,46.3697683,0 7.5685091,46.3698694,0 7.5691078,46.3696964,0 7.5691873,46.3698198,0 7.569288,46.3698413,0 7.569423,46.3697524,0 7.5694081,46.3698386,0 7.5695654,46.3698578,0 7.5697199,46.3699574,0 7.569829,46.3700234,0 7.5699239,46.3700186,0 7.5700752,46.3700821,0 7.5702149,46.3700415,0 7.5702412,46.3701205,0 7.5703156,46.3701287,0 7.5704896,46.3701844,0 7.5705317,46.3701552,0 7.5706147,46.3701703,0 7.5706274,46.3701162,0 7.570783,46.3701671,0 7.5708306,46.3701076,0 7.5710723,46.3701124,0 7.5711163,46.370016,0 7.571372,46.3699618,0 7.5719076,46.3701201,0 7.5721641,46.3701943,0 7.5722033,46.3702231,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Geissweg</name>
    +          <description>Geissweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6222616,46.3768109,0 7.6221674,46.3766908,0 7.6219495,46.3765308,0 7.6217027,46.3762199,0 7.6214667,46.3759238,0 7.6214023,46.3757166,0 7.6213272,46.3755685,0 7.6213057,46.3754279,0 7.621295,46.3752429,0 7.6212839,46.3751056,0 7.6212735,46.3748801,0 7.6212735,46.3747543,0 7.6212092,46.3745767,0 7.6211233,46.3744064,0 7.6211126,46.374251,0 7.6211155,46.3740319,0 7.6210626,46.373873,0 7.6209285,46.3736805,0 7.6208534,46.3735175,0 7.6206785,46.3733627,0 7.6205037,46.3732489,0 7.6203591,46.3730989,0 7.6200909,46.372825,0 7.6198387,46.3725622,0 7.6195705,46.3725807,0 7.6194257,46.3724548,0 7.6194096,46.3723919,0 7.6192379,46.3722365,0 7.6191682,46.3719663,0 7.618857,46.3719589,0 7.6187337,46.3718848,0 7.6185405,46.371622,0 7.6183474,46.3714777,0 7.6181597,46.3712889,0 7.6179612,46.3712408,0 7.6177681,46.3711816,0 7.6176018,46.3711779,0 7.6175052,46.3711927,0 7.6171994,46.3710594,0 7.616781,46.3707966,0 7.6166523,46.3707707,0 7.6165986,46.3705709,0 7.6164377,46.3703266,0 7.6164216,46.3702599,0 7.6163143,46.3702266,0 7.6162338,46.3702562,0 7.6162553,46.3704006,0 7.6161373,46.3702562,0 7.6160944,46.3703118,0 7.615853,46.3702969,0 7.6158476,46.3705153,0 7.6156974,46.370445,0 7.6155794,46.3706449,0 7.6154882,46.3706671,0 7.6154828,46.3707448,0 7.615338,46.3706227,0 7.6153058,46.370693,0 7.6152575,46.3706819,0 7.6151878,46.370693,0 7.6152361,46.3708188,0 7.6152146,46.3709447,0 7.6152361,46.3710927,0 7.6153434,46.3711964,0 7.6154721,46.3712778,0 7.6154828,46.3714333,0 7.6153863,46.3716998,0 7.6154077,46.3718034,0 7.6152897,46.3719367,0 7.6151395,46.3720921,0 7.6152253,46.3723068,0 7.6151502,46.3724326,0 7.615,46.3725659,0 7.6149035,46.3727139,0 7.6150644,46.3727879,0 7.6152897,46.3727657,0 7.6154721,46.3728472,0 7.6155579,46.3729582,0 7.6155579,46.3730544,0 7.6153434,46.3731433,0 7.6151717,46.3732913,0 7.6150429,46.373432,0 7.6150751,46.3734986,0 7.6154292,46.3735948,0 7.6157832,46.3738835,0 7.6161266,46.3740982,0 7.6162875,46.3744165,0 7.6162017,46.3745127,0 7.6160836,46.3746607,0 7.6162231,46.3748902,0 7.6163626,46.3751123,0 7.6163733,46.3752529,0 7.6162768,46.3752455,0 7.6162124,46.3755194,0 7.6162875,46.3757489,0 7.6164913,46.3759117,0 7.6166845,46.3761264,0 7.6169527,46.3762004,0 7.6169419,46.3763928,0 7.6168025,46.3765335,0 7.6168561,46.3766297,0 7.6166094,46.3770294,0 7.6167596,46.3773107,0 7.6167059,46.3774291,0 7.6167596,46.377555,0 7.6167596,46.3776586,0 7.6165772,46.3776882,0 7.6164591,46.377703,0 7.6163626,46.3778806,0 7.6163733,46.3780435,0 7.6164377,46.3782285,0 7.6163411,46.3782803,0 7.6165128,46.378384,0 7.6167274,46.3788059,0 7.6165986,46.3790057,0 7.6166201,46.3792722,0 7.6163948,46.3794054,0 7.6163304,46.3795164,0 7.6165879,46.3795904,0 7.6163197,46.3797385,0 7.6160085,46.3798569,0 7.6159442,46.3799605,0 7.6159549,46.3801233,0 7.6162124,46.3802862,0 7.6161802,46.3803602,0 7.6163089,46.380523,0 7.6161802,46.3806636,0 7.6163152,46.3807166,0 7.6163197,46.3807155,0 7.6163152,46.3807166,0 7.616545,46.3806562,0 7.6168883,46.3808265,0 7.6172102,46.3809005,0 7.6174891,46.3814926,0 7.6175428,46.3816406,0 7.6173926,46.3817442,0 7.6174247,46.3820551,0 7.6173282,46.3824474,0 7.6175535,46.38244,0 7.6176608,46.3827952,0 7.6175857,46.3829728,0 7.6175857,46.3830765,0 7.6174569,46.3833355,0 7.6175428,46.3836389,0 7.6173604,46.3840608,0 7.6173496,46.3848305,0 7.6175213,46.3850007,0 7.6175535,46.3852079,0 7.6176608,46.3852227,0 7.6177788,46.3851487,0 7.6188946,46.3855558,0 7.6192594,46.3857556,0 7.6197422,46.3858962,0 7.6201737,46.3860141,0 7.6204151,46.3860289,0 7.6206176,46.3859502,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5284879,46.4891875,0 7.5286313,46.4893803,0 7.5287117,46.4900478,0 7.5284951,46.4908182,0 7.5289364,46.4906938,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4345295,46.4574371,0 7.4347762,46.4573336,0 7.4350874,46.4572893,0 7.4353341,46.4572301,0 7.4354414,46.4570823,0 7.4356774,46.4567867,0 7.4357204,46.4564319,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4357204,46.4564319,0 7.4363534,46.4565058,0 7.4372546,46.4566093,0 7.4375872,46.4566536,0 7.4377803,46.4567202,0 7.4379949,46.456698,0 7.4382202,46.4566389,0 7.4384777,46.4566019,0 7.4387566,46.4566536,0 7.4390892,46.4567793,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4319089,46.4675275,0 7.4326197,46.4677909,0 7.4330703,46.46802,0 7.4340467,46.4683451,0 7.4347869,46.4685668,0 7.4351195,46.4686629,0 7.4354092,46.4689289,0 7.4357955,46.4693649,0 7.4360816,46.4694487,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4389752,46.4648103,0 7.4388936,46.4647475,0 7.4374799,46.4641657,0 7.4368575,46.4640778,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4391324,46.4649312,0 7.4389752,46.4648103,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4291628,46.5638818,0 7.4290955,46.5638078,0 7.4290258,46.5636455,0 7.4289346,46.5634796,0 7.4288327,46.5633357,0 7.4286825,46.5631919,0 7.4284893,46.5630997,0 7.428264,46.5630443,0 7.4280441,46.5630812,0 7.4278563,46.5631624,0 7.4276686,46.5632103,0 7.4275774,46.5631439,0 7.4275345,46.5630443,0 7.4274755,46.5629005,0 7.427336,46.5627677,0 7.4273413,46.5626239,0 7.4272716,46.562528,0 7.4272126,46.5623583,0 7.4271482,46.5621923,0 7.42688,46.5619415,0 7.4265689,46.5618346,0 7.4262899,46.5616354,0 7.4258769,46.561285,0 7.4255443,46.5609973,0 7.4252385,46.5606579,0 7.4249756,46.5603887,0 7.4247021,46.5602153,0 7.4244016,46.559994,0 7.4240261,46.5598133,0 7.4237311,46.5597542,0 7.4235039,46.559884,0 7.4232751,46.5600198,0 7.4232161,46.5599608,0 7.4231464,46.5598797,0 7.4230605,46.559651,0 7.422964,46.559496,0 7.4227119,46.5593891,0 7.4224436,46.5593411,0 7.4223363,46.5593411,0 7.4221754,46.559319,0 7.4221057,46.5592083,0 7.4219769,46.5590903,0 7.4218911,46.5590424,0 7.4217731,46.5591087,0 7.4214727,46.5591899,0 7.4213439,46.5592157,0 7.4212313,46.5591641,0 7.4210864,46.5591604,0 7.420888,46.5592415,0 7.4203944,46.5593264,0 7.4196273,46.5593338,0 7.4191231,46.5593522,0 7.4186188,46.5594592,0 7.4183184,46.5595219,0 7.417798,46.5595256,0 7.4172401,46.5595219,0 7.4168915,46.559496,0 7.4167895,46.5595071,0 7.4166286,46.5594887,0 7.4162853,46.5594592,0 7.4160546,46.559426,0 7.4158615,46.5594997,0 7.4158293,46.5594481,0 7.4158293,46.5593891,0 7.4158669,46.5593448,0 7.4157864,46.559319,0 7.4156308,46.5592342,0 7.4155074,46.5590903,0 7.4152339,46.5589686,0 7.4148208,46.5588063,0 7.4146813,46.5586403,0 7.4144507,46.5585038,0 7.4143273,46.5584632,0 7.4141985,46.5584817,0 7.4139893,46.5584743,0 7.4137801,46.5583858,0 7.4134636,46.5583858,0 7.4129701,46.5583673,0 7.4126643,46.5583304,0 7.4123317,46.5582714,0 7.4122512,46.558205,0 7.4122834,46.5581128,0 7.4122995,46.5580501,0 7.4122995,46.5578915,0 7.412144,46.5577624,0 7.4119937,46.5575706,0 7.411468,46.5573861,0 7.411055,46.5573087,0 7.4106795,46.5572644,0 7.4104917,46.557246,0 7.4103469,46.5573087,0 7.4103254,46.5574415,0 7.4102986,46.5574968,0 7.4100626,46.557482,0 7.409671,46.5574083,0 7.4090487,46.5572644,0 7.4087161,46.5570615,0 7.4086088,46.5569988,0 7.4079382,46.556855,0 7.4077559,46.5566927,0 7.4077237,46.5564603,0 7.4077183,46.5562057,0 7.4076593,46.5560914,0 7.4075252,46.5559586,0 7.4075198,46.5558147,0 7.4075037,46.5557004,0 7.4073321,46.5555749,0 7.4071926,46.5554717,0 7.4069727,46.5553425,0 7.4066132,46.555195,0 7.4061734,46.5550954,0 7.4059105,46.55497,0 7.4057496,46.5549073,0 7.4056047,46.5545457,0 7.4054921,46.5543834,0 7.4053419,46.5543318,0 7.4052292,46.5542506,0 7.4051917,46.5541473,0 7.4052185,46.5539481,0 7.4050683,46.5538043,0 7.404961,46.5537194,0 7.4048591,46.5536124,0 7.4048591,46.5535239,0 7.4049556,46.5533874,0 7.4049771,46.5533026,0 7.4048698,46.5532251,0 7.4047035,46.5531439,0 7.4044031,46.5529779,0 7.4042207,46.5528599,0 7.4039793,46.5526312,0 7.4035609,46.5523213,0 7.4033678,46.5522364,0 7.4032605,46.5521737,0 7.4031586,46.5521295,0 7.4030513,46.5520667,0 7.4028474,46.5520151,0 7.4026811,46.5519044,0 7.4026918,46.5518196,0 7.4027079,46.5516609,0 7.4024504,46.5514986,0 7.4022037,46.5513437,0 7.4018604,46.551174,0 7.4016351,46.551126,0 7.4013186,46.5510854,0 7.4008733,46.5509821,0 7.40053,46.5509526,0 7.3999882,46.5510153,0 7.3997414,46.5513179,0 7.39972,46.5515835,0 7.3998326,46.5516831,0 7.3998916,46.5517126,0 7.4000418,46.55172,0 7.4001384,46.551779,0 7.4001545,46.551838,0 7.400074,46.5518343,0 7.399956,46.5518417,0 7.3999131,46.5519081,0 7.3998863,46.5519671,0 7.3999453,46.5520225,0 7.3998702,46.5520631,0 7.3999399,46.552111,0 7.3998594,46.5521073,0 7.4000204,46.5523028,0 7.4002189,46.5525574,0 7.4003208,46.5527529,0 7.400412,46.5528894,0 7.4005568,46.5530148,0 7.4006963,46.5531513,0 7.400707,46.5532288,0 7.4006265,46.5532435,0 7.4005514,46.5532067,0 7.4004549,46.5531882,0 7.4004173,46.5532509,0 7.4004173,46.5533063,0 7.4003476,46.5533616,0 7.4001813,46.5533505,0 7.3999506,46.5533579,0 7.3998916,46.5534132,0 7.3997146,46.5536235,0 7.3995268,46.5539186,0 7.3994088,46.5541473,0 7.3991138,46.5545605,0 7.3989475,46.5547855,0 7.3990923,46.5552946,0 7.3991728,46.5554864,0 7.3991835,46.5555639,0 7.3991782,46.5556155,0 7.399087,46.5555971,0 7.3989207,46.555479,0 7.3987275,46.5554089,0 7.3984593,46.5553057,0 7.3982501,46.5551581,0 7.3980945,46.5550917,0 7.3979122,46.5550991,0 7.3978585,46.5551839,0 7.3978639,46.5552725,0 7.3978049,46.5552725,0 7.3974776,46.5552429,0 7.397188,46.5550843,0 7.3967695,46.5550069,0 7.3966676,46.5550622,0 7.3966622,46.5551507,0 7.3966944,46.5552466,0 7.3965818,46.5553462,0 7.3963994,46.5554385,0 7.3963457,46.5555417,0 7.396276,46.5556081,0 7.3960614,46.5556192,0 7.3958683,46.5555122,0 7.3958415,46.5553167,0 7.3956698,46.5551692,0 7.3954874,46.5550106,0 7.395305,46.5549146,0 7.395305,46.5550069,0 7.3953211,46.5551028,0 7.3954177,46.5552393,0 7.3954606,46.5553241,0 7.3953855,46.555468,0 7.3952943,46.5556303,0 7.3952514,46.5558,0 7.3952568,46.555977,0 7.3952621,46.5561615,0 7.3952621,46.5563459,0 7.3951602,46.5564603,0 7.3950261,46.5565562,0 7.3948813,46.5566189,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5658624,46.5011096,0 7.5659292,46.5011241,0 7.5660166,46.5011678,0 7.5662026,46.5012103,0 7.5666147,46.5013842,0 7.5667588,46.5014242,0 7.5669066,46.5014373,0 7.5670507,46.5015243,0 7.5671619,46.5016086,0 7.5673501,46.5017095,0 7.5675991,46.5017956,0 7.5677317,46.5018426,0 7.5678619,46.5020131,0 7.5678935,46.5020157,0 7.5679011,46.5019704,0 7.56788,46.50188,0 7.567992,46.5019974,0 7.5681955,46.5021296,0 7.5685303,46.5022514,0 7.5686352,46.5023384,0 7.5686706,46.5024219,0 7.56873,46.5027741,0 7.5687868,46.5028576,0 7.5688462,46.5028419,0 7.5689132,46.5026845,0 7.5688905,46.5024593,0 7.568898,46.5023853,0 7.5689612,46.5023949,0 7.5690244,46.502501,0 7.5692304,46.5026471,0 7.5693011,46.5026828,0 7.5693555,46.5026523,0 7.5693529,46.502541,0 7.5693529,46.5024984,0 7.5694047,46.5024845,0 7.5696158,46.5027419,0 7.5696714,46.5027524,0 7.569713,46.5026723,0 7.5697067,46.5025419,0 7.5696416,46.5023484,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5678619,46.5020131,0 7.5679328,46.5022479,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5686907,46.5028223,0 7.568699,46.5027941,0 7.56873,46.5027741,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5613391,46.4939717,0 7.5611723,46.4943217,0 7.5611146,46.4943645,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4099747,46.4812081,0 7.4100874,46.4812561,0 7.4102644,46.4813559,0 7.4104897,46.48133,0 7.4106882,46.4812598,0 7.4109618,46.4812266,0 7.4111978,46.4811564,0 7.4113319,46.4810382,0 7.4114124,46.4808756,0 7.4114982,46.4807759,0 7.4116806,46.4807759,0 7.4118791,46.4809089,0 7.4120293,46.4809532,0 7.4122624,46.480898,0 7.4124263,46.4807981,0 7.4125819,46.4807242,0 7.4128125,46.4807611,0 7.4130003,46.4807205,0 7.4131827,46.4806282,0 7.413349,46.4805727,0 7.4136172,46.4805395,0 7.4140088,46.4805469,0 7.4141,46.4804878,0 7.414159,46.4803991,0 7.4143146,46.4803954,0 7.4144111,46.4804361,0 7.4143628,46.4805395,0 7.4143682,46.4806355,0 7.4147705,46.4806429,0 7.4148027,46.4805617,0 7.4150441,46.4805063,0 7.4154089,46.4805063,0 7.4157147,46.4805173,0 7.4158702,46.4805026,0 7.4160634,46.4803917,0 7.4163477,46.4803696,0 7.4166213,46.4804102,0 7.4167554,46.4805432,0 7.4167071,46.4807501,0 7.416868,46.4809421,0 7.4169324,46.4812746,0 7.4170075,46.481511,0 7.4172006,46.4816107,0 7.4174849,46.4816107,0 7.4178014,46.4815886,0 7.4179087,46.4816957,0 7.4179516,46.481825,0 7.4180857,46.4820946,0 7.4181877,46.4822941,0 7.4182574,46.4822756,0 7.4184505,46.48215,0 7.4187402,46.4819838,0 7.4189709,46.481825,0 7.4192844,46.4816599,0 7.4194698,46.4816033,0 7.4198882,46.4815073,0 7.4202798,46.4814076,0 7.4206982,46.4813152,0 7.4210737,46.4811675,0 7.4215136,46.4809865,0 7.4218462,46.4807611,0 7.422152,46.4805358,0 7.4224148,46.4803954,0 7.4227152,46.4802588,0 7.4228976,46.4801147,0 7.4232302,46.4799669,0 7.4236647,46.4797712,0 7.4239383,46.479531,0 7.4243085,46.4793759,0 7.4244694,46.4791432,0 7.4246411,46.4789289,0 7.4249039,46.4787184,0 7.4251239,46.4784819,0 7.4253492,46.4783305,0 7.4255691,46.4781605,0 7.4257247,46.4779167,0 7.4258481,46.4775806,0 7.4260144,46.4773035,0 7.4261592,46.477056,0 7.42629,46.4769389,0 7.4263738,46.4768639,0 7.4265723,46.4767309,0 7.42676,46.4765794,0 7.4269585,46.4764612,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4317516,46.4728703,0 7.4318669,46.4725932,0 7.4320708,46.4722681,0 7.4322907,46.47198,0 7.4326233,46.4716548,0 7.4330793,46.4713851,0 7.4334119,46.4711893,0 7.4336855,46.4709972,0 7.4340985,46.4707201,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6164187,46.5309288,0 7.6170658,46.5311194,0 7.6182054,46.5313292,0 7.6193915,46.5318375,0 7.6194839,46.5322688,0 7.6204851,46.5333009,0 7.6213939,46.5338554,0 7.6221795,46.5344869,0 7.6231345,46.5348566,0 7.6244438,46.5351031,0 7.6247981,46.5355343,0 7.6250907,46.5362891,0 7.6253526,46.5364431,0 7.6258455,46.5370439,0 7.6256606,46.5375368,0 7.625445,46.5377524,0 7.6255066,46.5380297,0 7.6262306,46.5384302,0 7.6269853,46.5383069,0 7.6273704,46.5384764,0 7.6280328,46.5390925,0 7.6286181,46.5392927,0 7.6284332,46.5401091,0 7.6285565,46.5408331,0 7.628957,46.5414184,0 7.6298349,46.5422964,0 7.6298811,46.5428663,0 7.6310353,46.5445644,0 7.6319403,46.5448838,0 7.632792,46.5459218,0 7.6333243,46.5463211,0 7.6338034,46.5470664,0 7.6350278,46.547998,0 7.6350012,46.5489296,0 7.635294,46.5494087,0 7.6347084,46.5505,0 7.6342027,46.5509791,0 7.6342293,46.551538,0 7.6355868,46.5524164,0 7.6357731,46.5529221,0 7.6366515,46.5538537,0 7.6373435,46.5548119,0 7.6383816,46.5557169,0 7.6389139,46.5569945,0 7.6396858,46.5579527,0 7.6397656,46.5586713,0 7.6403512,46.5592569,0 7.6412562,46.5595497,0 7.642241,46.5606144,0 7.642478,46.5605806,0 7.6425216,46.5611584,0 7.6429032,46.560984,0 7.6436118,46.5608096,0 7.644037,46.5608096,0 7.644822,46.5612784,0 7.6449855,46.5615073,0 7.6445712,46.5615182,0 7.6442769,46.5616817,0 7.6440043,46.5625866,0 7.6435791,46.5629464,0 7.6435682,46.5633825,0 7.6431209,46.5636803,0 7.6432613,46.5638823,0 7.6430399,46.5644631,0 7.6434328,46.5645423,0 7.6431219,46.5647846,0 7.6428512,46.5649312,0 7.6431547,46.5649537,0 7.6435178,46.564926,0 7.6432531,46.5651398,0 7.6428116,46.5653032,0 7.6419408,46.565309,0 7.6411101,46.5652106,0 7.64133,46.5653611,0 7.6419408,46.5655515,0 7.6426597,46.5656959,0 7.6429907,46.5657714,0 7.6435639,46.5660093,0 7.6440848,46.5657547,0 7.6449761,46.5657894,0 7.6465965,46.5652454,0 7.6475457,46.5652454,0 7.6476846,46.5653958,0 7.6472447,46.5654769,0 7.6466197,46.5656852,0 7.6460409,46.5660324,0 7.6457516,46.5660324,0 7.6456937,46.5661482,0 7.6458673,46.5661945,0 7.6466742,46.5659811,0 7.6471656,46.5658899,0 7.6475915,46.5659523,0 7.6480022,46.5661775,0 7.6485642,46.5662755,0 7.6480434,46.5663565,0 7.6470364,46.5664028,0 7.6464576,46.5668195,0 7.6468628,46.5676992,0 7.6460583,46.5680271,0 7.6456236,46.5682188,0 7.6453857,46.5682752,0 7.6452455,46.5684742,0 7.6453161,46.5686246,0 7.6447213,46.5689462,0 7.6442816,46.569285,0 7.6432514,46.569366,0 7.6427884,46.5695165,0 7.6419435,46.5695049,0 7.6410406,46.5698637,0 7.6401725,46.5698405,0 7.6399295,46.5701183,0 7.6401378,46.5703151,0 7.6400452,46.5705813,0 7.639322,46.5710956,0 7.6388716,46.5713042,0 7.6387679,46.5715394,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.490681,46.4144632,0 7.4904801,46.4146018,0 7.4903907,46.4147865,0 7.4903163,46.4150995,0 7.4902866,46.4155664,0 7.4902493,46.4159204,0 7.4902493,46.4162641,0 7.4902047,46.4164591,0 7.4899517,46.4165925,0 7.4896093,46.4167823,0 7.489267,46.4169927,0 7.4890958,46.4170543,0 7.4889395,46.4170594,0 7.4886865,46.416967,0 7.4884707,46.4168593,0 7.4882102,46.4166284,0 7.4878679,46.4164488,0 7.4874883,46.4164027,0 7.4871386,46.4164796,0 7.4869302,46.4165925,0 7.4866251,46.4166848,0 7.4862306,46.4166541,0 7.4858809,46.4165822,0 7.4854492,46.4164694,0 7.4849208,46.4164488,0 7.4842734,46.4164437,0 7.4838641,46.416454,0 7.4834101,46.4165258,0 7.4830454,46.4165053,0 7.4822268,46.4164129,0 7.4818026,46.4163052,0 7.4815719,46.4161461,0 7.4811254,46.4160076,0 7.4806119,46.4158742,0 7.4802175,46.4156844,0 7.4797709,46.4153355,0 7.4793319,46.4150276,0 7.4786174,46.4147301,0 7.4782453,46.4145813,0 7.4780444,46.4144786,0 7.4777467,46.4144838,0 7.4773672,46.4145556,0 7.4770769,46.414571,0 7.4766676,46.4145813,0 7.4764518,46.4145813,0 7.4763327,46.4146787,0 7.4763104,46.4148224,0 7.4759309,46.4149148,0 7.4756927,46.4149507,0 7.4753727,46.414925,0 7.4750973,46.4148994,0 7.4746955,46.4147814,0 7.4745728,46.4147323,0 7.4743746,46.4147097,0 7.4742482,46.4146698,0 7.4741233,46.4146451,0 7.4740734,46.4146451,0 7.4740078,46.4146978,0 7.4734741,46.4147753,0 7.4733523,46.4147753,0 7.4732743,46.4146967,0 7.4731838,46.4145558,0 7.4731243,46.4144834,0 7.4730237,46.4143847,0 7.4729444,46.4144309,0 7.472917,46.4143426,0 7.4728865,46.4143973,0 7.4728286,46.4143321,0 7.4727219,46.4144246,0 7.4726975,46.4143973,0 7.4724902,46.4146411,0 7.4723348,46.4147987,0 7.4722464,46.4148638,0 7.4721945,46.4149689,0 7.4720939,46.4149626,0 7.4720817,46.4150193,0 7.4719842,46.4150299,0 7.4719781,46.4148954,0 7.471847,46.4149668,0 7.4718531,46.4147419,0 7.4718196,46.4146453,0 7.4717099,46.4145864,0 7.471594,46.414557,0 7.4714904,46.4145654,0 7.4714416,46.4146516,0 7.4713837,46.4147587,0 7.4713502,46.4148197,0 7.4713288,46.4148869,0 7.4712679,46.4148975,0 7.4712099,46.4148806,0 7.4711398,46.4148722,0 7.4710362,46.4149689,0 7.4708685,46.4151118,0 7.4706734,46.4152274,0 7.4704448,46.415261,0 7.4702497,46.4152904,0 7.4699998,46.4152316,0 7.4697132,46.4150677,0 7.4695029,46.4148911,0 7.4694023,46.4147566,0 7.4693383,46.4145339,0 7.4693261,46.4142943,0 7.4693657,46.4140463,0 7.4693993,46.4138656,0 7.4694511,46.4137122,0 7.469506,46.413565,0 7.4695304,46.4133864,0 7.4694816,46.4133381,0 7.4692621,46.4133171,0 7.4690762,46.4133759,0 7.4688262,46.4133948,0 7.4686799,46.413481,0 7.4685214,46.4135167,0 7.4684299,46.4135251,0 7.4682166,46.4135377,0 7.4680519,46.4135692,0 7.4680093,46.4137185,0 7.4677532,46.4137353,0 7.4673356,46.4138929,0 7.4670216,46.4139391,0 7.466921,46.4139139,0 7.4668022,46.4138298,0 7.466726,46.4136953,0 7.4664973,46.4136176,0 7.4662382,46.4135356,0 7.4658877,46.4132981,0 7.4657048,46.4132519,0 7.4655615,46.4134284,0 7.4651256,46.4138593,0 7.4649732,46.4139538,0 7.4647171,46.4139728,0 7.4641807,46.4139496,0 7.4637996,46.4139791,0 7.4636167,46.4139959,0 7.4632205,46.4139496,0 7.4626199,46.4137857,0 7.4621718,46.4136155,0 7.461928,46.4135692,0 7.4614189,46.4136281,0 7.460791,46.4136743,0 7.4600139,46.4138459,0 7.4597162,46.4138705,0 7.4593709,46.4138254,0 7.4589184,46.4137105,0 7.4587696,46.4136243,0 7.458198,46.4133451,0 7.4579063,46.4129716,0 7.4576741,46.4125242,0 7.4575431,46.4122656,0 7.4573883,46.4119495,0 7.4571919,46.4116991,0 7.4567572,46.4114077,0 7.4563405,46.4111408,0 7.4561083,46.4110013,0 7.4559178,46.4107262,0 7.4557273,46.4104061,0 7.4555129,46.41009,0 7.4553939,46.4099832,0 7.454995,46.4098888,0 7.4548104,46.4099463,0 7.4544056,46.4101064,0 7.4538757,46.4102419,0 7.4537864,46.4104676,0 7.4534232,46.4106154,0 7.453453,46.4107016,0 7.4538459,46.4106893,0 7.4540841,46.410796,0 7.4541615,46.4109808,0 7.4541376,46.4112927,0 7.4541138,46.4114364,0 7.4540305,46.4116704,0 7.4539293,46.4118715,0 7.4539114,46.4120234,0 7.4539531,46.4121589,0 7.4538578,46.4121794,0 7.4536971,46.412048,0 7.4536137,46.4119741,0 7.4534827,46.4120521,0 7.4534292,46.4122409,0 7.4534292,46.4124421,0 7.4534173,46.4126104,0 7.4533458,46.412869,0 7.4532922,46.4130496,0 7.4530243,46.4132877,0 7.452685,46.4135709,0 7.4524944,46.4138131,0 7.4521789,46.4141702,0 7.4520658,46.4142687,0 7.4520003,46.4142564,0 7.451911,46.4141209,0 7.4516431,46.4138705,0 7.451399,46.4136407,0 7.4511608,46.4134683,0 7.4510953,46.4133657,0 7.4510259,46.4133771,0 7.4508918,46.4134506,0 7.4508309,46.4134065,0 7.4506541,46.4131816,0 7.4505413,46.413003,0 7.4503736,46.4128307,0 7.450209,46.412568,0 7.4500566,46.4124334,0 7.4499834,46.4124145,0 7.4499438,46.412444,0 7.4499255,46.4125028,0 7.4499499,46.4127319,0 7.4498981,46.4129168,0 7.4499042,46.4129946,0 7.4498707,46.4130282,0 7.4498188,46.413024,0 7.4497836,46.4129741,0 7.449693,46.4128052,0 7.4496072,46.4126728,0 7.4494308,46.4124942,0 7.4493434,46.4123597,0 7.4492748,46.4122575,0 7.4492217,46.4121994,0 7.4491843,46.412208,0 7.4491655,46.4122855,0 7.4491983,46.412406,0 7.4491796,46.4124802,0 7.4491296,46.4125125,0 7.4490017,46.4124964,0 7.4489189,46.4124383,0 7.4488877,46.4123522,0 7.4488612,46.4121563,0 7.4488393,46.4120229,0 7.4487941,46.4118948,0 7.448688,46.4117485,0 7.4485553,46.4115462,0 7.4484585,46.4113568,0 7.4483992,46.4112363,0 7.4483618,46.4110899,0 7.4482213,46.4108758,0 7.4481183,46.4107015,0 7.4480699,46.4105659,0 7.4480012,46.4103238,0 7.4479279,46.4100666,0 7.4479263,46.4098385,0 7.4479263,46.4096394,0 7.4479294,46.4095479,0 7.4478935,46.4094048,0 7.4478561,46.4093069,0 7.4478514,46.40921,0 7.4478467,46.4091099,0 7.4478249,46.4090271,0 7.447789,46.4089873,0 7.4477437,46.4089862,0 7.4477016,46.4090432,0 7.4476033,46.4092423,0 7.4475049,46.4093617,0 7.4472693,46.4096361,0 7.4471335,46.4098008,0 7.447032,46.4100128,0 7.4469837,46.4102323,0 7.4469212,46.410426,0 7.4468775,46.4104572,0 7.4466871,46.4104637,0 7.4464717,46.4104734,0 7.4463079,46.4105099,0 7.4461674,46.4105078,0 7.4458443,46.4104873,0 7.4456352,46.410497,0 7.445373,46.4105239,0 7.4452466,46.4105293,0 7.445128,46.4104798,0 7.4450858,46.4104034,0 7.4450484,46.410355,0 7.4449266,46.4103119,0 7.4449051,46.4103026,0 7.4448583,46.4102856,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4884845,46.4446966,0 7.4884442,46.4451792,0 7.4886803,46.4457411,0 7.4893562,46.4462955,0 7.4899463,46.4468869,0 7.4900643,46.4470791,0 7.490311,46.4472344,0 7.4906651,46.4473083,0 7.4910513,46.4473083,0 7.4916307,46.4472935,0 7.49221,46.4474118,0 7.4926499,46.4474414,0 7.4929074,46.447397,0 7.4932186,46.4472639,0 7.4937872,46.4471457,0 7.4945382,46.4471678,0 7.4951283,46.4473083,0 7.4956969,46.4477223,0 7.495933,46.4479588,0 7.4961904,46.4480771,0 7.4965552,46.4481732,0 7.4967698,46.4482249,0 7.496802,46.4483654,0 7.4966089,46.4487424,0 7.4964158,46.4490603,0 7.4963621,46.4495186,0 7.4962334,46.449829,0 7.496051,46.4501469,0 7.4960295,46.4504278,0 7.496169,46.4506052,0 7.4961046,46.4507752,0 7.4959759,46.4513,0 7.4959222,46.4515883,0 7.4959222,46.4517583,0 7.4960295,46.4518544,0 7.4962977,46.4520023,0 7.496523,46.4521723,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4909655,46.4491638,0 7.4912123,46.4492377,0 7.4914912,46.4491933,0 7.491738,46.4490677,0 7.4919204,46.4490233,0 7.4921779,46.4489716,0 7.4924568,46.448905,0 7.4929933,46.4487868,0 7.4932293,46.4487424,0 7.493358,46.4486759,0 7.4935404,46.4486241,0 7.4937014,46.4485946,0 7.4938623,46.4485206,0 7.4939374,46.4484245,0 7.4940661,46.4483358,0 7.4941949,46.4482915,0 7.4943236,46.4482915,0 7.4944309,46.448321,0 7.4944846,46.4483802,0 7.4945918,46.4484393,0 7.4947099,46.4484541,0 7.4946562,46.4486611,0 7.4946133,46.4488089,0 7.4946884,46.4489568,0 7.4948279,46.4491194,0 7.4950961,46.4493855,0 7.4951605,46.4495851,0 7.4952249,46.4497256,0 7.4952249,46.4500212,0 7.4952892,46.4502578,0 7.4952356,46.4504426,0 7.4950961,46.4506422,0 7.4950639,46.4507235,0 7.4951176,46.4508935,0 7.495257,46.4510635,0 7.495418,46.4512705,0 7.495654,46.4515957,0 7.4959222,46.4517583,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4988512,46.4461551,0 7.4989692,46.4461403,0 7.4991301,46.4460516,0 7.4992696,46.4460959,0 7.4993233,46.4462142,0 7.499452,46.4463399,0 7.4995915,46.4464064,0 7.4997524,46.4464803,0 7.4999563,46.4465395,0 7.500085,46.4465469,0 7.5000314,46.4463842,0 7.499967,46.4462955,0 7.499967,46.4462068,0 7.5000314,46.4461551,0 7.5001708,46.4462142,0 7.5002674,46.446266,0 7.500321,46.4462364,0 7.5002245,46.4461255,0 7.5002352,46.446022,0 7.5002459,46.4459481,0 7.500203,46.4458668,0 7.5001601,46.4457928,0 7.5002889,46.4457707,0 7.5003961,46.4457337,0 7.5004713,46.4456967,0 7.5005571,46.4457411,0 7.5006536,46.4458076,0 7.5008146,46.4458815,0 7.5009111,46.4459481,0 7.500997,46.4460146,0 7.5010828,46.4461403,0 7.5011472,46.446192,0 7.5012437,46.4462512,0 7.5013403,46.4462881,0 7.501469,46.4463251,0 7.5015656,46.4462807,0 7.5016729,46.4461699,0 7.5017909,46.4461551,0 7.501866,46.4462068,0 7.501984,46.4461994,0 7.5020377,46.4461699,0 7.5022201,46.4461255,0 7.5023917,46.4461625,0 7.5028852,46.446266,0 7.5030784,46.4462216,0 7.5031856,46.4462216,0 7.5030891,46.4462955,0 7.5031427,46.4463842,0 7.5032286,46.446399,0 7.5033573,46.4464582,0 7.5034968,46.4465247,0 7.503647,46.4465469,0 7.5037757,46.4465543,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5010721,46.4422369,0 7.5011686,46.4424217,0 7.5013188,46.4426139,0 7.5015549,46.4427322,0 7.5017694,46.4428135,0 7.5019733,46.4428875,0 7.5021771,46.4429466,0 7.502381,46.4430797,0 7.5026063,46.4431536,0 7.5027887,46.4432275,0 7.5030033,46.4433015,0 7.5031749,46.4433976,0 7.503368,46.4434641,0 7.5036577,46.4434789,0 7.5040654,46.4434789,0 7.5044624,46.4433828,0 7.5046555,46.4433384,0 7.505031,46.4432497,0 7.5052563,46.443161,0 7.5054494,46.4430131,0 7.5057177,46.4429244,0 7.505943,46.4428209,0 7.5061683,46.4427248,0 7.5062863,46.4426361,0 7.5066511,46.442577,0 7.5066511,46.4424882,0 7.5067476,46.4423847,0 7.5069086,46.4422664,0 7.5070051,46.4421925,0 7.507166,46.4421038,0 7.5074665,46.4420668,0 7.5078098,46.4420594,0 7.5079493,46.4420373,0 7.5082497,46.4421777,0 7.5085501,46.4422221,0 7.5092367,46.442296,0 7.5094298,46.4423626,0 7.5095157,46.4423626,0 7.5097946,46.4422073,0 7.5100736,46.4420964,0 7.5102345,46.4420373,0 7.5105885,46.4419485,0 7.5107387,46.4418894,0 7.5111464,46.4418155,0 7.5112752,46.4418007,0 7.5115863,46.4418229,0 7.5119296,46.4418229,0 7.5121335,46.4418376,0 7.5122558,46.4418969,0 7.5123846,46.441956,0 7.5126314,46.441956,0 7.5127708,46.4419191,0 7.5127065,46.441823,0 7.5126206,46.441749,0 7.5125992,46.4415494,0 7.5125777,46.4413794,0 7.5125563,46.4412389,0 7.512685,46.4412241,0 7.5129318,46.4412019,0 7.5130712,46.4411576,0 7.5131356,46.4410615,0 7.5131142,46.4408618,0 7.5131249,46.4407509,0 7.5132965,46.4407509,0 7.5134467,46.4407953,0 7.5136399,46.4408766,0 7.5139725,46.4409062,0 7.5141227,46.4408914,0 7.5142514,46.4410171,0 7.5144445,46.4410171,0 7.5145304,46.4409284,0 7.5143909,46.4408397,0 7.5142836,46.4407953,0 7.5143158,46.4407362,0 7.5144231,46.4407214,0 7.5145196,46.44064,0 7.5144982,46.4405587,0 7.5145733,46.4404626,0 7.5146591,46.4404035,0 7.5147878,46.4403739,0 7.5150453,46.4404404,0 7.5154423,46.4404774,0 7.5164401,46.4404996,0 7.5171911,46.44064,0 7.5178778,46.4407953,0 7.5183606,46.4408101,0 7.5187575,46.4408692,0 7.5189292,46.4410615,0 7.5189936,46.4412389,0 7.5191545,46.4414015,0 7.5193369,46.4414607,0 7.5194871,46.4415051,0 7.5194978,46.4416603,0 7.5195836,46.4425105,0 7.5197017,46.4426732,0 7.5197553,46.4427915,0 7.5197124,46.4428876,0 7.5196587,46.4429911,0 7.5196802,46.4430798,0 7.519766,46.4431611,0 7.5198626,46.4432129,0 7.520045,46.4432202,0 7.5200772,46.4431685,0 7.5200772,46.443065,0 7.5201308,46.4429837,0 7.5202059,46.4429615,0 7.520281,46.443028,0 7.5203454,46.4431389,0 7.5204419,46.4432055,0 7.5206673,46.4432572,0 7.5207531,46.4432868,0 7.5210106,46.4436712,0 7.5212895,46.4438265,0 7.5215899,46.4439817,0 7.522062,46.4441665,0 7.5224375,46.4443735,0 7.5228774,46.4447875,0 7.5237679,46.4458003,0 7.52424,46.4464509,0 7.5244653,46.4468353,0 7.5245833,46.447094,0 7.5246775,46.4471938,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6100216,46.4187252,0 7.6103389,46.4186035,0 7.6105213,46.4185296,0 7.6110684,46.4183669,0 7.6114761,46.4183669,0 7.6117336,46.4184704,0 7.6120769,46.4184926,0 7.6127421,46.4184482,0 7.6133,46.4182855,0 7.6139974,46.4180266,0 7.6144587,46.4178121,0 7.6147591,46.4174201,0 7.6151239,46.4171465,0 7.6154672,46.4168728,0 7.6156711,46.4165769,0 7.6159179,46.4162663,0 7.6159822,46.4164512,0 7.61595,46.4166213,0 7.6161937,46.4166891,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5100736,46.4420964,0 7.5100993,46.4420041,0 7.5101369,46.4419339,0 7.5102764,46.4418525,0 7.5104158,46.4417195,0 7.5105124,46.4415457,0 7.510609,46.4413054,0 7.5107484,46.4410836,0 7.5108396,46.4409321,0 7.5108504,46.4408027,0 7.5108396,46.4406437,0 7.5109362,46.4405624,0 7.5110381,46.4404293,0 7.511081,46.4403406,0 7.511081,46.4402223,0 7.5110435,46.4400375,0 7.5111132,46.4398157,0 7.5112098,46.4396456,0 7.5112849,46.439483,0 7.5112795,46.4392907,0 7.5113332,46.4391281,0 7.5113654,46.4389802,0 7.5114136,46.4388952,0 7.5115102,46.4387695,0 7.5116175,46.4386438,0 7.5116765,46.4385477,0 7.5116938,46.4384565,0 7.5116952,46.4384008,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4169002,46.3606281,0 7.4166642,46.3605984,0 7.4164925,46.360554,0 7.4164604,46.3606577,0 7.4166857,46.3606503,0 7.4167822,46.3607836,0 7.4165784,46.3607539,0 7.4163316,46.3607317,0 7.4161063,46.3607391,0 7.4159132,46.3608206,0 7.4157093,46.3608798,0 7.4156879,46.3609687,0 7.4153875,46.3611167,0 7.4151622,46.361213,0 7.4150012,46.361213,0 7.4147867,46.36125,0 7.4149047,46.3613537,0 7.4150763,46.3613907,0 7.4152587,46.3614499,0 7.4150978,46.3614869,0 7.4149369,46.361524,0 7.4147974,46.3614795,0 7.414733,46.3615314,0 7.4148403,46.3615906,0 7.4149691,46.3616794,0 7.4150871,46.3616868,0 7.4153446,46.3615462,0 7.4155377,46.3614573,0 7.4157093,46.3613759,0 7.4159239,46.3613315,0 7.4161385,46.3613018,0 7.4161707,46.3613463,0 7.4160527,46.3614055,0 7.4161278,46.3614425,0 7.4163209,46.3614351,0 7.4164818,46.3613685,0 7.4166213,46.3613463,0 7.4165998,46.3614351,0 7.4164818,46.3615536,0 7.4163423,46.361672,0 7.4162458,46.3617535,0 7.4164282,46.3618942,0 7.416632,46.3621459,0 7.4167393,46.3623014,0 7.417029,46.3625901,0 7.4171255,46.3627012,0 7.4172221,46.3628197,0 7.4173401,46.3628863,0 7.4172865,46.3629825,0 7.4172221,46.3629603,0 7.4171363,46.3628863,0 7.4170183,46.3627974,0 7.416911,46.3627752,0 7.4168573,46.3628271,0 7.4169324,46.3629011,0 7.4170183,46.3629825,0 7.4171363,46.3631232,0 7.4172972,46.3632417,0 7.4173616,46.3634638,0 7.4175762,46.3635674,0 7.4176298,46.3636563,0 7.4178444,46.3639598,0 7.4180268,46.3641449,0 7.4182306,46.3643522,0 7.4183164,46.3644707,0 7.4183272,46.3645447,0 7.4182306,46.3645669,0 7.4181126,46.3645077,0 7.4181448,46.3645595,0 7.4181448,46.3646188,0 7.4181233,46.3646928,0 7.4182199,46.3647964,0 7.4183594,46.3649223,0 7.418531,46.3650186,0 7.418531,46.3651148,0 7.4184559,46.3652629,0 7.418413,46.3653295,0 7.4184237,46.3653739,0 7.4185096,46.3653369,0 7.418531,46.3652777,0 7.418649,46.3652184,0 7.4187885,46.3652333,0 7.4189709,46.3651962,0 7.4191426,46.3650926,0 7.4192284,46.3649815,0 7.4193142,46.3648705,0 7.4194752,46.3648853,0 7.4196468,46.3649741,0 7.4197327,46.3650778,0 7.4198829,46.365063,0 7.4199043,46.3650111,0 7.4201618,46.3650334,0 7.4202906,46.3651296,0 7.4204193,46.3652407,0 7.4204729,46.3653739,0 7.42043,46.3654776,0 7.4204837,46.3655294,0 7.4204944,46.3657145,0 7.4204837,46.3659366,0 7.4203657,46.3660772,0 7.4202369,46.3662179,0 7.4202047,46.3664326,0 7.4202798,46.3665585,0 7.4203657,46.3667436,0 7.420548,46.3668916,0 7.4206231,46.3670619,0 7.4206768,46.367284,0 7.4207304,46.3673802,0 7.4210845,46.3674617,0 7.4212883,46.3675801,0 7.4213313,46.3676542,0 7.4215244,46.3676393,0 7.4217711,46.3676023,0 7.4220715,46.3675431,0 7.4225222,46.3676023,0 7.4228977,46.3677652,0 7.4231552,46.3678837,0 7.4233391,46.3679547,0 7.4234234,46.3679873,0 7.4236701,46.3680095,0 7.4238203,46.3680317,0 7.4239276,46.3681206,0 7.4240564,46.3683204,0 7.4241744,46.3684759,0 7.4241637,46.3685721,0 7.4240778,46.3686832,0 7.4240993,46.368809,0 7.4239598,46.3689275,0 7.4237882,46.3690681,0 7.4236701,46.3692014,0 7.4236701,46.3693347,0 7.4238203,46.3695271,0 7.4239169,46.3695567,0 7.4241851,46.3695864,0 7.4244212,46.3695715,0 7.4245499,46.3696308,0 7.4245714,46.3697566,0 7.4245606,46.3698381,0 7.4246357,46.3699639,0 7.4247645,46.3700379,0 7.4248289,46.3700749,0 7.4249342,46.3700574,0 7.4250399,46.37001,0 7.4251498,46.3700325,0 7.4253692,46.3700204,0 7.4254505,46.3701257,0 7.4255205,46.3701847,0 7.4254798,46.3702635,0 7.4255506,46.3704363,0 7.4256657,46.3705709,0 7.4258266,46.3705561,0 7.4259017,46.3705783,0 7.4259125,46.370682,0 7.4259339,46.3707634,0 7.4260734,46.3707856,0 7.4262451,46.370793,0 7.4263845,46.3708448,0 7.426376,46.3709474,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Lämmernweg süd</name>
    +          <description>Lämmernweg süd, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.611382,46.3964875,0 7.6114292,46.3962389,0 7.6108557,46.3963143,0 7.6102376,46.3963577,0 7.6097209,46.3964049,0 7.6097625,46.3965224,0 7.6089079,46.3964609,0 7.607996,46.3964387,0 7.6073737,46.3964387,0 7.6069124,46.3964239,0 7.6068694,46.3963499,0 7.6065261,46.3963277,0 7.6062794,46.3964387,0 7.606054,46.3964387,0 7.6053889,46.3962907,0 7.6048417,46.3963721,0 7.6044233,46.3964979,0 7.6042623,46.3965866,0 7.603919,46.3966754,0 7.6035435,46.3966236,0 7.6030929,46.3966902,0 7.6027496,46.3967938,0 7.602299,46.396927,0 7.6021058,46.396964,0 7.6015479,46.3968752,0 7.6010115,46.3967198,0 7.6008398,46.3966162,0 7.6006789,46.396631,0 7.6003678,46.3965053,0 7.6000566,46.3964017,0 7.5991554,46.3964313,0 7.5987155,46.3963499,0 7.5981791,46.3964017,0 7.5978327,46.3964729,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5890918,46.3955507,0 7.5891478,46.3953803,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5890918,46.3955507,0 7.5889469,46.3955137,0 7.5887431,46.3955433,0 7.5885929,46.3955359,0 7.5882281,46.3956617,0 7.5881798,46.3956099,0 7.5879277,46.3957505,0 7.5879009,46.3958134,0 7.5877989,46.3958171,0 7.5878043,46.3958615,0 7.5876916,46.3958282,0 7.5877077,46.3959318,0 7.587925,46.3962126,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bunkertunnel</name>
    +          <description>Bunkertunnel, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6097625,46.3965224,0 7.6108557,46.3963143,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.618857,46.3719589,0 7.6188592,46.3720632,0 7.6189053,46.3722772,0 7.6187819,46.3724918,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4673625,46.4564555,0 7.467345,46.456468,0 7.467191,46.456466,0 7.467011,46.456589,0 7.4668276,46.4566405,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6086644,46.4388706,0 7.6059896,46.4390346,0 7.6040186,46.4382136,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6188287,46.4183578,0 7.619399,46.41861,0 7.6196936,46.4187362,0 7.6200723,46.418852,0 7.6203984,46.4190413,0 7.6208718,46.4191045,0 7.6213242,46.4191045,0 7.6217029,46.4191045,0 7.6223026,46.4190203,0 7.6225795,46.4190323,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.545644,46.4989186,0 7.5453618,46.4983976,0 7.5448129,46.497796,0 7.5442378,46.4967916,0 7.5442063,46.4956444,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gemmiweg</name>
    +          <description>Gemmiweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6165875,46.3938373,0 7.6168364,46.3943921,0 7.6173565,46.3951755,0 7.6172441,46.395445,0 7.6169051,46.3958102,0 7.616989,46.3961054,0 7.6171827,46.3958529,0 7.6174721,46.3954421,0 7.617529,46.3951648,0 7.6173761,46.3949876,0 7.6176494,46.3952003,0 7.6175373,46.3949445,0 7.6173232,46.3945064,0 7.6170816,46.3940832,0 7.6167553,46.3939985,0 7.6167307,46.3937498,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gemmiweg</name>
    +          <description>Gemmiweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6167307,46.3937498,0 7.6172078,46.3936647,0 7.6176542,46.3933273,0 7.6179288,46.3933273,0 7.6176112,46.3937595,0 7.6179288,46.3941146,0 7.6181262,46.3942746,0 7.6181691,46.3937713,0 7.6181863,46.3935464,0 7.6189588,46.3927649,0 7.6184722,46.3925455,0 7.6182284,46.3927317,0 7.6178116,46.3929224,0 7.6166641,46.3926529,0 7.6163907,46.3927661,0 7.6159441,46.3927095,0 7.6158621,46.3925335,0 7.6153802,46.3923545,0 7.6151785,46.3920809,0 7.6151421,46.3918357,0 7.6153882,46.3920432,0 7.615616,46.3920683,0 7.6157618,46.3915529,0 7.6158803,46.3917729,0 7.6161628,46.3920935,0 7.6172656,46.3919866,0 7.6174935,46.3918483,0 7.6174265,46.391526,0 7.6177881,46.3912895,0 7.617637,46.3909179,0 7.6176299,46.3902353,0 7.6181192,46.3896255,0 7.6185741,46.3889151,0 7.618617,46.3885954,0 7.6186038,46.3882094,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4785053,46.4263539,0 7.4783109,46.4266478,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bonderlenbach Walking Path</name>
    +          <description>Bonderlenbach Walking Path, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5792169,46.4925621,0 7.5791509,46.4925641,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bonderlenbach Walking Path</name>
    +          <description>Bonderlenbach Walking Path, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5792169,46.4925621,0 7.5793125,46.4925286,0 7.5793674,46.4922433,0 7.579896,46.491972,0 7.580289,46.491577,0 7.580358,46.4913398,0 7.580717,46.4910715,0 7.5810337,46.4907202,0 7.5813065,46.4905421,0 7.5813831,46.4904028,0 7.5817008,46.4902111,0 7.582209,46.489783,0 7.582811,46.489678,0 7.583084,46.489476,0 7.583642,46.489351,0 7.584382,46.488724,0 7.584451,46.488496,0 7.584687,46.488298,0 7.584991,46.4884,0 7.585394,46.488367,0 7.586046,46.488033,0 7.586489,46.487635,0 7.586402,46.486841,0 7.586652,46.486558,0 7.587146,46.486256,0 7.5871827,46.4860238,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5835968,46.5163224,0 7.5835278,46.516243,0 7.5831875,46.5160665,0 7.582901,46.515494,0 7.5829329,46.5151783,0 7.583018,46.515011,0 7.582857,46.515017,0 7.582842,46.514962,0 7.582983,46.514806,0 7.583006,46.514618,0 7.5826838,46.5145655,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.583998,46.516732,0 7.5836354,46.5165224,0 7.583146,46.51642,0 7.583074,46.516172,0 7.582901,46.516045,0 7.5828838,46.5161271,0 7.582968,46.516266,0 7.58253,46.516101,0 7.582413,46.516123,0 7.582236,46.516301,0 7.581972,46.516325,0 7.5813369,46.5161948,0 7.5811935,46.5160699,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5758881,46.4885517,0 7.57579,46.488698,0 7.575762,46.488936,0 7.575673,46.48907,0 7.5754732,46.4891609,0 7.5754081,46.4892532,0 7.5751268,46.4894422,0 7.574963,46.489548,0 7.574921,46.489618,0 7.574974,46.489689,0 7.575061,46.489711,0 7.575154,46.489709,0 7.575232,46.489819,0 7.57509,46.489989,0 7.575101,46.490052,0 7.5751655,46.4901109,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.583616,46.5164476,0 7.5835968,46.5163224,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5836354,46.5165224,0 7.583616,46.5164476,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.577443,46.4885782,0 7.577979,46.488368,0 7.578077,46.488296,0 7.578219,46.488228,0 7.578307,46.48821,0 7.578387,46.488215,0 7.578434,46.488189,0 7.578734,46.488188,0 7.578873,46.488171,0 7.578928,46.488196,0 7.579113,46.488162,0 7.579226,46.488174,0 7.579338,46.488153,0 7.579673,46.488129,0 7.579867,46.488075,0 7.580044,46.488002,0 7.580178,46.487914,0 7.580318,46.487883,0 7.580371,46.487925,0 7.5806,46.488021,0 7.5807961,46.4880853,0 7.5815952,46.4884243,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5900055,46.5210825,0 7.590046,46.521112,0 7.590122,46.521211,0 7.590218,46.521275,0 7.590325,46.521253,0 7.590386,46.521219,0 7.590563,46.521275,0 7.590713,46.521298,0 7.590841,46.521428,0 7.590872,46.521404,0 7.590961,46.521528,0 7.590941,46.521642,0 7.590964,46.521732,0 7.591091,46.521789,0 7.591134,46.521776,0 7.5912266,46.5218003,0 7.591198,46.521758,0 7.591338,46.521783,0 7.591454,46.521725,0 7.591541,46.5217,0 7.591606,46.521663,0 7.5914358,46.5216049,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5788446,46.5204993,0 7.5787501,46.5204233,0 7.5786342,46.5202629,0 7.5782994,46.5200566,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5797057,46.509266,0 7.579856,46.508887,0 7.579984,46.509007,0 7.580344,46.509065,0 7.580417,46.509061,0 7.5806678,46.5089152,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5751655,46.4901109,0 7.575234,46.490227,0 7.575412,46.490443,0 7.575564,46.490731,0 7.575587,46.491121,0 7.575613,46.491252,0 7.5756106,46.4917412,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5718566,46.492604,0 7.5719484,46.4925554,0 7.5720948,46.4923908,0 7.5721505,46.4922987,0 7.5722725,46.4920071,0 7.5723428,46.4917874,0 7.5724562,46.4915977,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5299338,46.4850755,0 7.5298545,46.4853025,0 7.5296791,46.4856935,0 7.5292968,46.4861218,0 7.528969,46.4863518,0 7.5287592,46.4867026,0 7.5286528,46.4871826,0 7.5284947,46.4876225,0 7.5283193,46.4879071,0 7.5284171,46.4881687,0 7.5285206,46.4886632,0 7.5285629,46.4889495,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5611621,46.4717378,0 7.5611814,46.4715986,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5611814,46.4715986,0 7.5612549,46.47139,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5886158,46.521106,0 7.5882453,46.5211526,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5284782,46.4890732,0 7.5280137,46.4891138,0 7.527826,46.4891156,0 7.5277837,46.4891524,0 7.5275537,46.4892205,0 7.5273587,46.4891892,0 7.5272741,46.4893364,0 7.5271434,46.4897762,0 7.5270835,46.4902156,0 7.5269728,46.4906006,0 7.5266136,46.4908715,0 7.5262039,46.4908149,0 7.5263157,46.4908679,0 7.5264182,46.4909433,0 7.5267561,46.4910387,0 7.5270399,46.4912235,0 7.5267326,46.4911435,0 7.5260461,46.4911093,0 7.5256069,46.4911246,0 7.5252571,46.491094,0 7.5251971,46.4911317,0 7.525568,46.4912801,0 7.5258895,46.4914583,0 7.5259861,46.4915802,0 7.5258021,46.4915365,0 7.5253812,46.4915848,0 7.52505,46.4916009,0 7.5247787,46.4916952,0 7.5245414,46.4917116,0 7.524222,46.4917881,0 7.5237731,46.4918337,0 7.5236274,46.491791,0 7.5233992,46.491741,0 7.5236818,46.4918764,0 7.5238452,46.4919647,0 7.5240836,46.4920324,0 7.524353,46.4921531,0 7.5246857,46.4922017,0 7.5248211,46.4922959,0 7.5250536,46.4923989,0 7.5248755,46.4924784,0 7.5244443,46.4924814,0 7.524403,46.4925123,0 7.5242691,46.492577,0 7.5237893,46.4925329,0 7.5235891,46.4925049,0 7.5238614,46.4927345,0 7.5240395,46.4927875,0 7.5243118,46.4928302,0 7.5246018,46.4928626,0 7.5247121,46.492945,0 7.5246121,46.4931776,0 7.5243265,46.4931246,0 7.5238172,46.4931422,0 7.5234772,46.4932129,0 7.5231328,46.4933763,0 7.5232402,46.4933851,0 7.5236221,46.4934281,0 7.5239302,46.4936089,0 7.5240479,46.4936231,0 7.5240696,46.4936758,0 7.5238868,46.4936918,0 7.5236372,46.4936994,0 7.5236023,46.493836,0 7.5236598,46.4939707,0 7.5235581,46.4940658,0 7.523445,46.4942024,0 7.5233395,46.4941902,0 7.5232161,46.4941911,0 7.5231822,46.4942589,0 7.5232651,46.4943202,0 7.5232142,46.4943842,0 7.5230324,46.4944304,0 7.5229234,46.4944286,0 7.5227633,46.4945022,0 7.5226253,46.4946585,0 7.5224616,46.4946052,0 7.5223549,46.49455,0 7.5219556,46.4946733,0 7.5218802,46.4946641,0 7.5215729,46.4948205,0 7.5213668,46.4948996,0 7.5210725,46.4949952,0 7.5210246,46.4950946,0 7.5208793,46.4951572,0 7.5207707,46.495297,0 7.5205665,46.4954147,0 7.5203144,46.4955049,0 7.5201084,46.4955674,0 7.5197592,46.4957073,0 7.5195457,46.4957735,0 7.5193485,46.495959,0 7.5191233,46.4960591,0 7.5189143,46.4961753,0 7.5186096,46.4961488,0 7.5183255,46.4961327,0 7.5182475,46.4963063,0 7.5181504,46.4963063,0 7.5177485,46.4962975,0 7.517647,46.496402,0 7.5174233,46.4964462,0 7.5172996,46.4963299,0 7.517151,46.4963947,0 7.5170465,46.4963431,0 7.5167477,46.4963564,0 7.516443,46.496405,0 7.5161869,46.4963446,0 7.5158395,46.4965286,0 7.5156643,46.4966449,0 7.5156084,46.4966199,0 7.5156791,46.4965227,0 7.5154671,46.4964005,0 7.5152232,46.4962689,0 7.5150141,46.4961521,0 7.5148907,46.4961351,0 7.5147042,46.4961643,0 7.5146712,46.4961898,0 7.5145073,46.4963085,0 7.5143764,46.4964545,0 7.5141371,46.4964385,0 7.5139025,46.4963649,0 7.5138335,46.496289,0 7.5136509,46.4960829,0 7.5135387,46.4958989,0 7.5132461,46.4956174,0 7.5126905,46.4953966,0 7.5124256,46.495507,0 7.5121735,46.4952881,0 7.5119969,46.4951593,0 7.5116933,46.4950654,0 7.5115645,46.4947784,0 7.5112363,46.4945931,0 7.5108051,46.4944099,0 7.5108554,46.4945788,0 7.510841,46.4947764,0 7.510956,46.4949956,0 7.5109704,46.4950711,0 7.5110997,46.4952723,0 7.5109883,46.4954376,0 7.511053,46.4958078,0 7.5109272,46.4961563,0 7.5107547,46.4965696,0 7.5107691,46.4969756,0 7.5109488,46.4972955,0 7.5109074,46.4975939,0 7.5110763,46.4978024,0 7.511335,46.4978778,0 7.5117806,46.4980863,0 7.5121004,46.4982048,0 7.5128551,46.4984564,0 7.5132216,46.4986756,0 7.5136348,46.4988301,0 7.5138576,46.4990457,0 7.5140409,46.4990134,0 7.51435,46.4988589,0 7.5148602,46.498805,0 7.5153813,46.4989631,0 7.5159167,46.4989128,0 7.5160101,46.4990709,0 7.5162761,46.4990924,0 7.5165851,46.4990206,0 7.5168331,46.4990996,0 7.5168834,46.4992721,0 7.5167971,46.4995776,0 7.5168546,46.4998758,0 7.5168906,46.5000052,0 7.5167828,46.5001849,0 7.5167756,46.5003574,0 7.5168474,46.5005442,0 7.5171313,46.500882,0 7.5173218,46.5013815,0 7.5174044,46.5013959,0 7.5175446,46.5015612,0 7.5174763,46.501863,0 7.5175554,46.5020643,0 7.5176703,46.5023625,0 7.5179291,46.5025638,0 7.518177,46.5025242,0 7.5182489,46.5026213,0 7.5183819,46.5027219,0 7.5187161,46.5030812,0 7.5189101,46.5031818,0 7.5191617,46.5032896,0 7.5191329,46.5035232,0 7.5191509,46.5037173,0 7.5192156,46.5039437,0 7.5195857,46.5041341,0 7.5199414,46.5042383,0 7.520272,46.5041521,0 7.5204841,46.504224,0 7.5205775,46.504551,0 7.521095,46.5044432,0 7.521731,46.5046731,0 7.5218819,46.5047342,0 7.5222377,46.5048349,0 7.5229205,46.5051367,0 7.5223563,46.5051475,0 7.5217454,46.5051259,0 7.5216268,46.5053739,0 7.5219969,46.5054673,0 7.5225683,46.5055859,0 7.5230606,46.5055787,0 7.5233716,46.5055941,0 7.5236641,46.5057762,0 7.5238187,46.5057486,0 7.5240744,46.505561,0 7.5243154,46.5055941,0 7.5244645,46.505561,0 7.524794,46.505601,0 7.5248928,46.5054393,0 7.5251264,46.5055381,0 7.5252477,46.5053495,0 7.5253644,46.505345,0 7.5256025,46.5051384,0 7.5257732,46.505062,0 7.5259619,46.5051923,0 7.5262943,46.5051923,0 7.5263976,46.505071,0 7.5265009,46.5048464,0 7.526739,46.5049272,0 7.5269905,46.5048913,0 7.526986,46.5047745,0 7.5270219,46.5046577,0 7.5272555,46.5044601,0 7.5275116,46.5043253,0 7.5277047,46.5041142,0 7.5281359,46.5039884,0 7.5287378,46.5040693,0 7.5289535,46.5041412,0 7.5295239,46.5039345,0 7.5299327,46.5037818,0 7.5304987,46.5034225,0 7.5309434,46.5031035,0 7.5316388,46.5026561,0 7.5320087,46.5024271,0 7.532634,46.502154,0 7.5331537,46.501837,0 7.5337702,46.5015199,0 7.5343955,46.5011059,0 7.5349328,46.5007713,0 7.5353291,46.500463,0 7.535699,46.4998289,0 7.5358135,46.4996263,0 7.5366414,46.4992212,0 7.5413582,46.4976497,0 7.542458,46.4973265,0 7.5428044,46.497004,0 7.5429425,46.4974133,0 7.5430314,46.4972745,0 7.5432425,46.4975431,0 7.5432637,46.4974015,0 7.543431,46.4975358,0 7.5435531,46.4973951,0 7.5442586,46.4980313,0 7.5453618,46.4983976,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5284723,46.4889619,0 7.5284782,46.4890732,0 7.5284879,46.4891875,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.589224,46.5211243,0 7.5887088,46.5208145,0 7.5886158,46.521106,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5882453,46.5211526,0 7.587849,46.5206548,0 7.5876898,46.5205699,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5811876,46.5165553,0 7.581105,46.516431,0 7.581328,46.516318,0 7.5813369,46.5161948,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5572902,46.4904128,0 7.5569738,46.4903625,0 7.5567747,46.4902998,0 7.5565619,46.4901569,0 7.5564031,46.4900393,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5442063,46.4956444,0 7.5441027,46.4955627,0 7.543313,46.4953914,0 7.5417852,46.4952141,0 7.5384722,46.4958877,0 7.5378027,46.4959527,0 7.5386696,46.4954209,0 7.5404806,46.493666,0 7.5404463,46.4936187,0 7.5395536,46.4938492,0 7.539073,46.4940796,0 7.5377598,46.4944932,0 7.5374851,46.4946528,0 7.5371332,46.4947178,0 7.5368843,46.4946941,0 7.5364552,46.4944578,0 7.5350304,46.4942333,0 7.5356312,46.4942155,0 7.5346699,46.4938078,0 7.5345669,46.4938137,0 7.5337944,46.4945405,0 7.5335026,46.4946173,0 7.5330992,46.494895,0 7.5331593,46.4944755,0 7.5328331,46.4943219,0 7.5328674,46.4940678,0 7.532258,46.4942569,0 7.5322065,46.4943692,0 7.5320606,46.4942096,0 7.5315456,46.4943987,0 7.5310993,46.4946941,0 7.5309277,46.4945701,0 7.5308332,46.4942392,0 7.5310907,46.4939733,0 7.5310049,46.4938196,0 7.531065,46.4936187,0 7.5308332,46.4932701,0 7.5307217,46.4932228,0 7.53055,46.4932642,0 7.5301724,46.4934533,0 7.5301552,46.4931578,0 7.5298548,46.4931697,0 7.5296059,46.4933469,0 7.52952,46.4935656,0 7.5293484,46.4932997,0 7.5293655,46.4931342,0 7.5288248,46.4932347,0 7.528739,46.4930987,0 7.528945,46.492951,0 7.5286531,46.4929747,0 7.5289192,46.4927147,0 7.528945,46.4924074,0 7.5291853,46.4921119,0 7.5283613,46.4921651,0 7.528327,46.4920529,0 7.528945,46.4914915,0 7.5293655,46.4912729,0 7.5293055,46.4911842,0 7.5288506,46.491261,0 7.5283957,46.4911842,0 7.5286875,46.4910306,0 7.5289364,46.4906938,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5797311,46.5157548,0 7.5795577,46.5157983,0 7.5794887,46.5156381,0 7.5798922,46.5156733,0 7.5802352,46.5155193,0 7.5807117,46.5159997,0 7.5811935,46.5160699,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6240644,46.4319535,0 7.6229314,46.4322197,0 7.6225538,46.4321901,0 7.6223005,46.4319807,0 7.6220817,46.4317997,0 7.6217384,46.4311371,0 7.6211719,46.4303385,0 7.6206312,46.429321,0 7.6206397,46.428824,0 7.6206655,46.4282561,0 7.6202878,46.4277355,0 7.6201591,46.427138,0 7.6203136,46.4265109,0 7.6203994,46.4260021,0 7.6208887,46.4251619,0 7.621129,46.4249194,0 7.6213779,46.4249312,0 7.6214637,46.4246768,0 7.6217555,46.4245171,0 7.622056,46.424523,0 7.6221847,46.4243218,0 7.6222705,46.4235054,0 7.6223564,46.4232628,0 7.6228848,46.4228375,0 7.623146,46.4219611,0 7.6233162,46.4217127,0 7.6231975,46.4203281,0 7.6232747,46.4200559,0 7.6235705,46.4197968,0 7.6237812,46.4196122,0 7.6234722,46.4190027,0 7.6241075,46.4180133,0 7.6237558,46.4135276,0 7.622349,46.411426,0 7.622269,46.4103269,0 7.6222984,46.4100075,0 7.6223212,46.4098275,0 7.6221581,46.4095408,0 7.6220343,46.4092423,0 7.6220442,46.4087396,0 7.6219789,46.408495,0 7.622036,46.408225,0 7.6220138,46.4081401,0 7.6220272,46.4080307,0 7.6219912,46.4078595,0 7.6218334,46.4076621,0 7.621853,46.4075699,0 7.6218432,46.4075069,0 7.6218171,46.4073382,0 7.6216637,46.407201,0 7.6216116,46.4067555,0 7.6214745,46.4064181,0 7.6213817,46.4061354,0 7.6211026,46.4059412,0 7.6208351,46.4056105,0 7.6204991,46.4050999,0 7.6203001,46.4049064,0 7.6200956,46.4045367,0 7.6191214,46.4039463,0 7.6187152,46.4035454,0 7.6185974,46.4029784,0 7.6183611,46.4027152,0 7.618025,46.4024362,0 7.6177216,46.4020088,0 7.6173518,46.4015782,0 7.6171968,46.401491,0 7.6171248,46.4012666,0 7.6169562,46.4010242,0 7.616789,46.4008723,0 7.6166055,46.4005798,0 7.6163119,46.4004477,0 7.6162257,46.4002455,0 7.6159082,46.4001664,0 7.6158021,46.400068,0 7.615741,46.3999246,0 7.6155345,46.3998258,0 7.6155034,46.3995861,0 7.6153372,46.3993537,0 7.6151007,46.3993059,0 7.6147908,46.3988956,0 7.614583,46.39872,0 7.6144629,46.3985904,0 7.6144107,46.398504,0 7.6144451,46.3984215,0 7.6149668,46.3984892,0 7.6159224,46.3981975,0 7.6160369,46.3979777,0 7.6162827,46.3980041,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5939523,46.4441727,0 7.5941669,46.4450295,0 7.5934587,46.4457548,0 7.5933515,46.4466271,0 7.5945316,46.4482231,0 7.5945316,46.4490074,0 7.594943,46.450099,0 7.5953899,46.4508259,0 7.5959049,46.4512694,0 7.5969564,46.4515798,0 7.5982438,46.4518016,0 7.5986526,46.4521268,0 7.5989734,46.4519199,0 7.5995956,46.4519199,0 7.6003692,46.4512982,0 7.6023637,46.4513285,0 7.6027285,46.4516685,0 7.6031576,46.451639,0 7.6036941,46.4516537,0 7.6038443,46.4518311,0 7.6055823,46.4522155,0 7.606784,46.4522296,0 7.6084362,46.451313,0 7.6090166,46.4513573,0 7.609831,46.452407,0 7.6112472,46.4526147,0 7.6135442,46.4523486,0 7.6140581,46.4528652,0 7.6154314,46.4531321,0 7.6163122,46.4537966,0 7.6164828,46.4533383,0 7.6173626,46.4528956,0 7.6181136,46.4534869,0 7.6183078,46.4541078,0 7.6189075,46.4544034,0 7.6193582,46.4541373,0 7.6189075,46.4536938,0 7.6189505,46.4526295,0 7.6184999,46.451639,0 7.6174935,46.4511659,0 7.6163766,46.4503372,0 7.6160537,46.4500571,0 7.6155398,46.4499085,0 7.6158266,46.4496374,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.565617,46.454428,0 7.5666152,46.4552017,0 7.5666581,46.4555121,0 7.5668942,46.4560583,0 7.5663148,46.4560591,0 7.5659082,46.4558226,0 7.5657784,46.4563547,0 7.5656925,46.4567539,0 7.5652732,46.457071,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5631545,46.4467541,0 7.5632249,46.4472628,0 7.5633107,46.4479429,0 7.5632249,46.4485269,0 7.5631498,46.449,0 7.5635575,46.4492214,0 7.5643198,46.4494214,0 7.5645982,46.4497762,0 7.5646202,46.4501898,0 7.5648128,46.4507002,0 7.5644372,46.4508776,0 7.5647162,46.4511359,0 7.5650917,46.4512546,0 7.5650702,46.4513876,0 7.5658749,46.4517424,0 7.5653385,46.4518016,0 7.5646947,46.4515503,0 7.5639115,46.4515724,0 7.5640939,46.4518459,0 7.5642225,46.4520855,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5145904,46.4695704,0 7.5139015,46.469419,0 7.5126127,46.4689973,0 7.510378,46.4680514,0 7.5095656,46.4678181,0 7.5084436,46.4667202,0 7.5071567,46.4661894,0 7.505739,46.4654072,0 7.5052464,46.4648341,0 7.5041354,46.4642962,0 7.5039997,46.4641152,0 7.5025871,46.4638186,0 7.5017828,46.463507,0 7.5013203,46.4635723,0 7.5007337,46.4632858,0 7.5001289,46.4627517,0 7.5002388,46.4623825,0 7.5000975,46.462029,0 7.4998932,46.4618562,0 7.4996275,46.4618288,0 7.4998608,46.461322,0 7.4996878,46.4611049,0 7.4994787,46.4610968,0 7.4982335,46.4604124,0 7.4974418,46.4602679,0 7.4968574,46.4600731,0 7.4967003,46.4600542,0 7.4962353,46.4601925,0 7.4959148,46.4601799,0 7.4958959,46.4600102,0 7.4954686,46.4593819,0 7.4951482,46.4591054,0 7.4939668,46.4584896,0 7.4938194,46.4583292,0 7.4938408,46.4580558,0 7.4937617,46.457859,0 7.4935726,46.4576419,0 7.4935034,46.4572164,0 7.4935082,46.4569102,0 7.4938086,46.4566589,0 7.4937872,46.4564667,0 7.4935726,46.4563115,0 7.4935941,46.4561267,0 7.4937121,46.4559789,0 7.4936799,46.4558606,0 7.4935726,46.4557424,0 7.4935835,46.4556522,0 7.4936799,46.4554763,0 7.4939267,46.4554098,0 7.4940447,46.4552915,0 7.4940447,46.455055,0 7.494128,46.454872,0 7.4943129,46.4547002,0 7.4943987,46.4543306,0 7.4945216,46.4540381,0 7.4947635,46.4537097,0 7.49497,46.4533356,0 7.4952249,46.4530888,0 7.4955682,46.4528375,0 7.4957291,46.4526601,0 7.4961583,46.4523645,0 7.496523,46.4521723,0 7.4967269,46.4519653,0 7.4968664,46.4516844,0 7.4972955,46.4512261,0 7.497553,46.4510192,0 7.4976818,46.4508417,0 7.4979607,46.4505017,0 7.4983148,46.450036,0 7.498465,46.4495925,0 7.4987868,46.448979,0 7.4989048,46.4483802,0 7.4990336,46.4480401,0 7.4989048,46.4477666,0 7.4986366,46.4475153,0 7.4985508,46.4471974,0 7.4986581,46.44702,0 7.4987976,46.4468426,0 7.4988619,46.4466504,0 7.4989048,46.4463768,0 7.4988512,46.4461551,0 7.4986795,46.4456598,0 7.4985937,46.4454823,0 7.4984542,46.4452975,0 7.4983684,46.4451645,0 7.4984328,46.4450684,0 7.4986259,46.4449427,0 7.4988512,46.4447579,0 7.4990443,46.4444621,0 7.4992267,46.4443069,0 7.4994091,46.4442625,0 7.4995486,46.4441517,0 7.4995271,46.4439447,0 7.4995164,46.4437524,0 7.4996344,46.4434937,0 7.4999563,46.4431314,0 7.5002459,46.4427987,0 7.5003854,46.4426731,0 7.5006429,46.4424217,0 7.5009111,46.442296,0 7.5010721,46.4422369,0 7.5010506,46.442126,0 7.5009004,46.4420225,0 7.5007502,46.4419929,0 7.5006536,46.4419559,0 7.5006107,46.4419042,0 7.5006,46.4417711,0 7.5005678,46.441638,0 7.5004605,46.4415863,0 7.5004498,46.4415419,0 7.5004905,46.4414532,0 7.5005142,46.4414015,0 7.5005142,46.4412906,0 7.5004283,46.4413349,0 7.5002352,46.4413941,0 7.5001601,46.4413867,0 7.5000636,46.4412906,0 7.4999455,46.4411944,0 7.4997631,46.4411057,0 7.4996344,46.4411427,0 7.4993662,46.4411427,0 7.4991194,46.4410761,0 7.4987868,46.4409948,0 7.4984864,46.4408396,0 7.4982933,46.4406326,0 7.4977676,46.4403886,0 7.497435,46.4402481,0 7.4972097,46.4401963,0 7.4971668,46.4402851,0 7.4972526,46.4404255,0 7.4973492,46.4405438,0 7.4973062,46.4407139,0 7.4972848,46.4409209,0 7.4974135,46.4410909,0 7.4974886,46.441224,0 7.4975637,46.4414606,0 7.4976067,46.441638,0 7.4976603,46.4418303,0 7.4976603,46.4419485,0 7.4976388,46.4423404,0 7.4974564,46.4426879,0 7.4969522,46.442991,0 7.496566,46.4432497,0 7.4962226,46.443575,0 7.4961046,46.4437377,0 7.4959437,46.4437894,0 7.4956969,46.4437894,0 7.4954823,46.4438042,0 7.4951283,46.4438707,0 7.4948406,46.4440224,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4392764,46.5663903,0 7.4384143,46.5654398,0 7.4383285,46.5644956,0 7.4369552,46.5639645,0 7.4358394,46.5630793,0 7.4334362,46.5624892,0 7.4318054,46.561545,0 7.4313762,46.5601286,0 7.4301746,46.5598335,0 7.4283721,46.5600106,0 7.4268272,46.5598925,0 7.4253681,46.5597745,0 7.4243488,46.5597302,0 7.4237311,46.5597542,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6025506,46.5498156,0 7.6028653,46.5497501,0 7.6028389,46.54964,0 7.6031894,46.5495721,0 7.6035778,46.5494457,0 7.6037914,46.5494037,0 7.6040446,46.549479,0 7.6042355,46.5496465,0 7.6045554,46.5500853,0 7.604603,46.550328,0 7.6047518,46.55048,0 7.604832,46.550562,0 7.6048525,46.5506675,0 7.604877,46.550899,0 7.60495,46.550924,0 7.6051,46.551122,0 7.605191,46.551234,0 7.605464,46.551456,0 7.605639,46.551512,0 7.605784,46.551733,0 7.605943,46.551818,0 7.606095,46.552009,0 7.606435,46.552209,0 7.60649,46.552187,0 7.606543,46.552362,0 7.606685,46.552431,0 7.606803,46.552701,0 7.606729,46.552879,0 7.606867,46.553505,0 7.607232,46.553864,0 7.607482,46.553954,0 7.6077446,46.5542054,0 7.6081817,46.5543071,0 7.6082734,46.5543499,0 7.6085418,46.5544171,0 7.6087851,46.5544514,0 7.6090718,46.5545225,0 7.6093117,46.5546698,0 7.60936,46.5548103,0 7.6093995,46.5548684,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5849029,46.5115884,0 7.58481,46.5116222,0 7.5846421,46.5115122,0 7.5846131,46.5115867,0 7.5845005,46.5115558,0 7.5843126,46.5114523,0 7.584142,46.5112744,0 7.5839847,46.5111716,0 7.5834605,46.5109729,0 7.5833227,46.5109291,0 7.5831168,46.5109274,0 7.5827628,46.5107142,0 7.5825833,46.5106646,0 7.5823735,46.5106181,0 7.5820585,46.510608,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5620328,46.4912044,0 7.5624485,46.4911228,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5870469,46.5174702,0 7.5869142,46.5174469,0 7.5867683,46.5173533,0 7.5866814,46.5173433,0 7.5865673,46.5172963,0 7.5864711,46.5172304,0 7.5864401,46.5171526,0 7.5864303,46.5170445,0 7.5861962,46.5172065,0 7.5861315,46.5173462,0 7.5861156,46.5174414,0 7.5860537,46.5175235,0 7.5860095,46.5175323,0 7.5859337,46.5175202,0 7.5858968,46.5174461,0 7.5858323,46.5174205,0 7.5857805,46.5174263,0 7.5855589,46.517562,0 7.5853542,46.517598,0 7.5850387,46.5177229,0 7.5848903,46.5179375,0 7.5847829,46.5179983,0 7.5843919,46.5179006,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6168363,46.5609385,0 7.6169212,46.5608065,0 7.6169276,46.5608025,0 7.61705,46.560728,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.580134,46.5028204,0 7.5796817,46.5029734,0 7.5793242,46.5029233,0 7.579196,46.503146,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.580325,46.5027273,0 7.5804032,46.5026495,0 7.5801302,46.5020994,0 7.58,46.501965,0 7.5796142,46.5018839,0 7.578915,46.5018479,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.489344,46.4970621,0 7.4894459,46.4970965,0 7.4895445,46.4970424,0 7.4896947,46.4970225,0 7.4897056,46.4969934,0 7.4898076,46.4969784,0 7.4899233,46.4970458,0 7.4901552,46.4970463,0 7.4902717,46.4970463,0 7.4903687,46.4970477,0 7.4904743,46.4971045,0 7.4906798,46.4971168,0 7.4907075,46.497224,0 7.4909032,46.4972302,0 7.4910534,46.4972581,0 7.4912316,46.4972956,0 7.4913717,46.4972872,0 7.4914988,46.4973022,0 7.4915715,46.4972667,0 7.4916332,46.4972247,0 7.4918452,46.4972365,0 7.4919829,46.4973915,0 7.4921941,46.4973733,0 7.4923721,46.4972622,0 7.4924976,46.4972333,0 7.492589,46.4971636,0 7.4927543,46.4971714,0 7.4929234,46.4971128,0 7.4930718,46.4971128,0 7.493159,46.4970928,0 7.4933313,46.4971206,0 7.4935232,46.4972342,0 7.4936332,46.4972942,0 7.4937682,46.4973207,0 7.4938968,46.497335,0 7.4940006,46.497385,0 7.4940255,46.497415,0 7.4942216,46.4974164,0 7.4942725,46.4973392,0 7.4944624,46.49736,0 7.4946274,46.4974493,0 7.4946928,46.4975107,0 7.4947986,46.4974885,0 7.4950799,46.4975193,0 7.495331,46.4974493,0 7.4954389,46.4975107,0 7.4958032,46.4975586,0 7.4961539,46.4975907,0 7.4963303,46.4975907,0 7.4966717,46.4976114,0 7.497279,46.4976768,0 7.4973942,46.4977876,0 7.4978249,46.497889,0 7.498132,46.4979726,0 7.4985585,46.4980312,0 7.4988159,46.4980747,0 7.4994832,46.4980947,0 7.5000228,46.4982469,0 7.5002407,46.4983733,0 7.5005136,46.4985698,0 7.5008405,46.4987991,0 7.5011591,46.4989948,0 7.5012587,46.499107,0 7.5016936,46.4992741,0 7.5021211,46.4993741,0 7.5019653,46.4996232,0 7.5019439,46.49983,0 7.5017293,46.4999925,0 7.5016005,46.5001254,0 7.5016864,46.5006276,0 7.5020297,46.5008491,0 7.5019009,46.5010855,0 7.5018795,46.5012627,0 7.5023086,46.50144,0 7.5024588,46.5017797,0 7.5025447,46.5019569,0 7.5025876,46.5021785,0 7.5025661,46.5024886,0 7.5030167,46.502592,0 7.503639,46.5028136,0 7.5043257,46.5030351,0 7.5048621,46.5028874,0 7.5052698,46.502784,0 7.5056775,46.5027692,0 7.5062354,46.5029908,0 7.5066646,46.5033453,0 7.5069864,46.5035963,0 7.5073512,46.5038179,0 7.5076731,46.5040247,0 7.5076516,46.5041871,0 7.5073297,46.5042314,0 7.5067718,46.5043496,0 7.5064714,46.5043939,0 7.5064929,46.504512,0 7.5067075,46.5046007,0 7.5070508,46.5047779,0 7.5070293,46.5050142,0 7.5067504,46.5051471,0 7.5064714,46.5053391,0 7.5061281,46.5055016,0 7.5052698,46.5057379,0 7.5047763,46.5060776,0 7.5041325,46.5065649,0 7.5035103,46.5070228,0 7.5029309,46.507392,0 7.5026091,46.5076578,0 7.5020726,46.5079089,0 7.5017507,46.5081156,0 7.5014933,46.5083076,0 7.5010641,46.5087211,0 7.5009783,46.5090903,0 7.5009139,46.5095777,0 7.500871,46.5099764,0 7.5004204,46.5104047,0 7.50012,46.5108477,0 7.4996479,46.5110987,0 7.4990042,46.5111578,0 7.4984248,46.511335,0 7.4983604,46.5116451,0 7.4982317,46.5120438,0 7.4977811,46.5122063,0 7.4976952,46.5123244,0 7.4978025,46.512413,0 7.4976952,46.5125607,0 7.4972876,46.5127674,0 7.4966224,46.5129889,0 7.4961288,46.5132843,0 7.4958284,46.5133286,0 7.4954422,46.5134467,0 7.4951847,46.5136239,0 7.4946268,46.5138011,0 7.4940904,46.513875,0 7.4938758,46.513934,0 7.4935539,46.5140965,0 7.4933608,46.5141408,0 7.492996,46.5139488,0 7.4925883,46.5139931,0 7.492245,46.5140965,0 7.491966,46.5142589,0 7.4917944,46.5144213,0 7.4914511,46.5144804,0 7.4911077,46.5143475,0 7.490743,46.5142294,0 7.4903996,46.5143179,0 7.4901636,46.5145247,0 7.4897774,46.5147314,0 7.4892838,46.5148643,0 7.4884255,46.5152482,0 7.487696,46.5154845,0 7.4874599,46.5156174,0 7.486945,46.515957,0 7.4861725,46.5160456,0 7.485636,46.5160751,0 7.4854834,46.5161258,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5810925,46.497901,0 7.580999,46.498044,0 7.580567,46.497948,0 7.5805282,46.4980101,0 7.580489,46.498073,0 7.5806637,46.4981927,0 7.580764,46.498291,0 7.580732,46.498332,0 7.5806387,46.4983733,0 7.5803456,46.4986969,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5868088,46.5114574,0 7.5867929,46.5114531,0 7.5867416,46.5114409,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5875342,46.512005,0 7.5873571,46.5120171,0 7.5869236,46.5118061,0 7.5868421,46.5116025,0 7.5868468,46.5114841,0 7.5868088,46.5114574,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5633366,46.4810949,0 7.563085,46.4812124,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5461028,46.4845429,0 7.5460141,46.4844262,0 7.5458652,46.4843459,0 7.5456547,46.4842848,0 7.5455029,46.4842106,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4579366,46.4368633,0 7.4561507,46.438472,0 7.4554266,46.4407469,0 7.4562657,46.4407434,0 7.4565824,46.4408413,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Margeliweg</name>
    +          <description>Margeliweg, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5702308,46.4974098,0 7.5701367,46.4975929,0 7.570127,46.497612,0 7.569897,46.497685,0 7.5695,46.497694,0 7.569391,46.497615,0 7.569158,46.49761,0 7.569122,46.497717,0 7.569308,46.49812,0 7.570074,46.498377,0 7.57033,46.498687,0 7.5705898,46.498823,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5685948,46.4864379,0 7.5688213,46.4863868,0 7.5691195,46.4862141,0 7.569185,46.4860615,0 7.5693007,46.4859109,0 7.5694777,46.4858092,0 7.5698205,46.4856595,0 7.5701528,46.485471,0 7.5704268,46.485429,0 7.5706475,46.4853774,0 7.5710397,46.4852471,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5677844,46.4896259,0 7.567876,46.4895408,0 7.567961,46.489547,0 7.5679627,46.4894969,0 7.5680063,46.4894105,0 7.5680732,46.4893894,0 7.5681704,46.489411,0 7.5682202,46.4893757,0 7.5682202,46.489292,0 7.5682275,46.4892296,0 7.5682582,46.4891723,0 7.5683261,46.4891145,0 7.5683411,46.4890816,0 7.5683272,46.4889471,0 7.5684098,46.4889418,0 7.5685373,46.4889605,0 7.5687317,46.4889811,0 7.5688378,46.4889221,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.571083,46.4852752,0 7.5712609,46.4852006,0 7.5714092,46.4851008,0 7.5715368,46.4849655,0 7.571555,46.4849207,0 7.5718393,46.4846598,0 7.5719243,46.4846003,0 7.5721098,46.484547,0 7.5721753,46.4845139,0 7.5722317,46.4845225,0 7.572362,46.4844702,0 7.5724345,46.4844726,0 7.5724951,46.4845446,0 7.5725829,46.484605,0 7.5727,46.484345,0 7.5728469,46.4841998,0 7.5728858,46.4841269,0 7.5730058,46.4840919,0 7.5730633,46.4841159,0 7.573079,46.4840679,0 7.5731306,46.484102,0 7.5731696,46.4840926,0 7.5732194,46.4840055,0 7.5732887,46.4839626,0 7.5732988,46.4839684,0 7.5733114,46.4840442,0 7.5733703,46.484136,0 7.5734232,46.4842003,0 7.5734601,46.484221,0 7.5735123,46.4842222,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5684612,46.487926,0 7.5684632,46.487807,0 7.5684908,46.4874257,0 7.5683489,46.4873599,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5756624,46.4951092,0 7.5750979,46.4947883,0 7.5743523,46.4946252,0 7.5737649,46.4942215,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5728372,46.4890235,0 7.5727436,46.4888729,0 7.5722582,46.4884277,0 7.5718875,46.4882051,0 7.5717091,46.4881898,0 7.5714861,46.4882473,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <description>, Summer Walking Path</description>
    +          <styleUrl>#style-Summer_Hiking</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.563239,46.4912821,0 7.5633949,46.4913423,0 7.5634393,46.4913395,0 7.5642021,46.4916877,0 7.564343,46.4917357,0 7.5645316,46.4919494,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Waterways</name>
    +      <Folder>
    +        <name>Waterway_River</name>
    +        <Style id="style-Waterway_River">
    +          <LineStyle>
    +            <color>#a0ea8b17</color>
    +            <width>8</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark>
    +          <name>Rotten</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>8.3809611,46.5793095,0 8.3810126,46.578759,0 8.3804404,46.5779554,0 8.3798931,46.577374,0 8.3796941,46.57669,0 8.3797688,46.5757666,0 8.3793707,46.5750997,0 8.3802912,46.574313,0 8.3811619,46.5730647,0 8.3808633,46.5722438,0 8.3797328,46.5712548,0 8.3789478,46.5704823,0 8.3785,46.5695246,0 8.3773308,46.56908,0 8.375067,46.567746,0 8.3744202,46.5672671,0 8.3728778,46.5667711,0 8.3720818,46.5664119,0 8.3707135,46.5663948,0 8.3695194,46.5661554,0 8.3686985,46.5654712,0 8.3682507,46.5648555,0 8.3669571,46.5647016,0 8.3665591,46.564445,0 8.3660615,46.5645305,0 8.3656712,46.5644753,0 8.365166,46.5642227,0 8.3640982,46.5636204,0 8.3627363,46.5625072,0 8.3622116,46.5620904,0 8.3619162,46.5618251,0 8.3606878,46.5611105,0 8.3597744,46.5608453,0 8.3579486,46.5604548,0 8.3561402,46.5595722,0 8.3557931,46.5593294,0 8.3554263,46.5590004,0 8.3552344,46.5583665,0 8.3556378,46.5571362,0 8.3570111,46.5547753,0 8.3588994,46.5528866,0 8.3602827,46.5506929,0 8.3616855,46.5491811,0 8.3616995,46.5475524,0 8.3620441,46.5469392,0 8.3620639,46.546586,0 8.362161,46.5448586,0 8.3623602,46.5438652,0 8.3624521,46.5433528,0 8.3625424,46.5424862,0 8.3606462,46.5408034,0 8.3602507,46.5403122,0 8.3602754,46.5392381,0 8.3593975,46.538297,0 8.3585929,46.5374926,0 8.3579406,46.5372691,0 8.3569815,46.5372579,0 8.3551793,46.5374009,0 8.3529555,46.5368053,0 8.3509396,46.5364303,0 8.3503448,46.5353437,0 8.3494472,46.5345604,0 8.3482431,46.533861,0 8.3473434,46.5334395,0 8.3468182,46.533018,0 8.3460346,46.532176,0 8.3451745,46.5312024,0 8.3442859,46.5302302,0 8.341962,46.5278764,0 8.3412361,46.52715,0 8.3410492,46.5268648,0 8.3408741,46.5264552,0 8.3406487,46.5261124,0 8.3378035,46.5236785,0 8.3365463,46.5225635,0 8.3358665,46.5218821,0 8.3354103,46.5214204,0 8.335185,46.5211382,0 8.3344354,46.5198694,0 8.3338271,46.5187553,0 8.3335681,46.5184011,0 8.3333216,46.5180749,0 8.3329861,46.517854,0 8.3326138,46.517648,0 8.331599,46.5171055,0 8.3313651,46.5169186,0 8.3311949,46.5166985,0 8.330977,46.5162522,0 8.3306718,46.5157189,0 8.3303871,46.5154024,0 8.3301024,46.515126,0 8.3292847,46.5145684,0 8.3284889,46.5140258,0 8.3281602,46.5137946,0 8.3279672,46.5136939,0 8.3277519,46.5135948,0 8.327292,46.5134114,0 8.3267698,46.5132161,0 8.3259503,46.5129785,0 8.3255706,46.5128494,0 8.3252801,46.512685,0 8.3249628,46.5124709,0 8.3240832,46.5118632,0 8.3235147,46.5113328,0 8.3226547,46.5104817,0 8.3223496,46.5100019,0 8.32186,46.5095222,0 8.3216898,46.5094171,0 8.3214928,46.509322,0 8.3212136,46.5091708,0 8.3209988,46.5090744,0 8.3207783,46.5090015,0 8.3196984,46.5086575,0 8.3193492,46.5085431,0 8.3189509,46.5084362,0 8.3187236,46.5083803,0 8.3184974,46.5083419,0 8.3182567,46.5083375,0 8.3180263,46.5083508,0 8.3174734,46.5084226,0 8.3167471,46.5083953,0 8.3160315,46.5083187,0 8.3151054,46.5082564,0 8.3142082,46.5081715,0 8.313588,46.5081275,0 8.3129989,46.5080681,0 8.3125857,46.5079476,0 8.3121468,46.5077548,0 8.312012,46.5076174,0 8.311863,46.5073672,0 8.3116228,46.5069618,0 8.3114915,46.5067409,0 8.3113505,46.506547,0 8.3111267,46.5063412,0 8.3108778,46.5061337,0 8.3105821,46.5059194,0 8.3100814,46.5054643,0 8.3092124,46.5044489,0 8.3079707,46.5038562,0 8.3065427,46.5031652,0 8.2992117,46.4999818,0 8.2974913,46.4992347,0 8.2888207,46.4950271,0 8.2821475,46.4911101,0 8.2816738,46.4907287,0 8.2810227,46.4902096,0 8.2799624,46.4894364,0 8.2791991,46.4889918,0 8.2784596,46.4887505,0 8.2783195,46.4885775,0 8.2775333,46.4876068,0 8.2768434,46.486656,0 8.2762437,46.4856638,0 8.2761271,46.4852222,0 8.2758522,46.4847634,0 8.2754024,46.4844021,0 8.2750442,46.4842223,0 8.2746478,46.484089,0 8.2739879,46.4839606,0 8.2732285,46.4837942,0 8.2716792,46.4835591,0 8.270788,46.483387,0 8.2700457,46.4831351,0 8.2696145,46.4827465,0 8.2691198,46.4819259,0 8.268956,46.4817412,0 8.267543,46.4809836,0 8.265199,46.4785089,0 8.2613049,46.477341,0 8.258764,46.4769837,0 8.2557309,46.476287,0 8.2537907,46.4748393,0 8.2530884,46.4737978,0 8.2517779,46.4731821,0 8.2505962,46.4713575,0 8.2481102,46.4712526,0 8.2469721,46.4709783,0 8.2455664,46.4702859,0 8.2448658,46.4695507,0 8.2446235,46.4688246,0 8.2441669,46.4677964,0 8.2439909,46.4672901,0 8.2435946,46.4660171,0 8.2434751,46.4638729,0 8.2429109,46.4634003,0 8.2421367,46.4631794,0 8.2413471,46.4629851,0 8.2399768,46.4624948,0 8.2378375,46.4619808,0 8.2348559,46.4604787,0 8.2336767,46.4595306,0 8.2327392,46.4591274,0 8.2316244,46.4587223,0 8.230689,46.4582662,0 8.2292937,46.4567381,0 8.2271256,46.4551003,0 8.2264984,46.4548479,0 8.2259419,46.4548318,0 8.225287,46.4549385,0 8.2239994,46.4551344,0 8.2224883,46.4551344,0 8.2210779,46.4548211,0 8.2203727,46.4543684,0 8.219938,46.4540652,0 8.2194884,46.4536547,0 8.2192128,46.4535068,0 8.2189054,46.4533624,0 8.2186017,46.4531836,0 8.2183672,46.452995,0 8.2182045,46.4527239,0 8.2179551,46.4523145,0 8.217401,46.4518968,0 8.2170484,46.4514442,0 8.2168973,46.4509916,0 8.2162425,46.4503998,0 8.2156884,46.4500168,0 8.2150997,46.4498423,0 8.2144511,46.4497048,0 8.2134722,46.4495038,0 8.2127661,46.4491725,0 8.2124011,46.4488254,0 8.2123137,46.4484501,0 8.2120618,46.447719,0 8.2114071,46.4472664,0 8.2103493,46.4471968,0 8.2096442,46.4469183,0 8.2092915,46.4462567,0 8.2087879,46.4456996,0 8.2086662,46.4452839,0 8.2086368,46.444864,0 8.2084997,46.444571,0 8.2082775,46.4443192,0 8.2078873,46.4441367,0 8.2073272,46.4440632,0 8.2067585,46.4441111,0 8.2061841,46.444211,0 8.2056855,46.4442129,0 8.2050092,46.4441668,0 8.2045474,46.4438673,0 8.2042113,46.4435089,0 8.2041606,46.4431742,0 8.2043508,46.4426956,0 8.2044744,46.4421839,0 8.2044079,46.4418335,0 8.2042177,46.4415221,0 8.2037372,46.441096,0 8.2031997,46.4406043,0 8.2027641,46.4402438,0 8.2021294,46.4399356,0 8.2015235,46.4396386,0 8.2012417,46.4394368,0 8.2009982,46.4392035,0 8.2009129,46.4389971,0 8.2008667,46.4387763,0 8.2008223,46.4379607,0 8.2007907,46.4375375,0 8.200318,46.4368117,0 8.1993607,46.4365031,0 8.1982153,46.4363582,0 8.1958192,46.4366253,0 8.19479,46.4362763,0 8.1937483,46.43594,0 8.1933564,46.4354758,0 8.1932964,46.4351213,0 8.1930565,46.4342674,0 8.1922574,46.4338651,0 8.1891176,46.4321459,0 8.1870285,46.4325305,0 8.185658,46.4320112,0 8.1828991,46.4302863,0 8.1811784,46.4296467,0 8.1796629,46.4281609,0 8.1798238,46.4272433,0 8.1800746,46.4265742,0 8.1795634,46.4262843,0 8.1781283,46.4262043,0 8.1769875,46.4258846,0 8.1759086,46.4250951,0 8.1750912,46.424305,0 8.1707123,46.4202363,0 8.1702435,46.4198007,0 8.1585706,46.4131733,0 8.1506741,46.408439,0 8.1496459,46.4076273,0 8.1427777,46.4020471,0 8.1389729,46.4009274,0 8.1377901,46.3991976,0 8.1365368,46.398807,0 8.1345567,46.3965826,0 8.1318753,46.3954356,0 8.1275903,46.3932613,0 8.1248034,46.391294,0 8.1242142,46.3901885,0 8.1225604,46.3877846,0 8.1195583,46.3845555,0 8.117387,46.3840506,0 8.1151622,46.3827137,0 8.1120501,46.3820872,0 8.111043,46.3814855,0 8.1104735,46.380476,0 8.1080893,46.3795991,0 8.10604,46.3781469,0 8.1047198,46.377719,0 8.1037526,46.3774629,0 8.1007618,46.3753388,0 8.0965176,46.3755291,0 8.095282,46.3772748,0 8.0940139,46.3781826,0 8.0918295,46.3781278,0 8.0902618,46.3764919,0 8.0874549,46.3743397,0 8.0857466,46.3734776,0 8.0831482,46.3733223,0 8.081071,46.3719922,0 8.0792466,46.3716062,0 8.0779375,46.3719812,0 8.0763592,46.372377,0 8.0750815,46.3715033,0 8.072929,46.3696281,0 8.0717659,46.3690859,0 8.0704667,46.369113,0 8.0693186,46.3692328,0 8.0686961,46.3687732,0 8.068403,46.3677228,0 8.0676044,46.3671998,0 8.0658763,46.3668823,0 8.0633385,46.3656446,0 8.0606123,46.364566,0 8.0591321,46.3636382,0 8.0549773,46.3620484,0 8.0540035,46.3613046,0 8.0539798,46.3607469,0 8.0539543,46.3601453,0 8.0534732,46.3596983,0 8.0520047,46.3595265,0 8.0510579,46.3591344,0 8.0501139,46.3572737,0 8.0464833,46.3546331,0 8.0454189,46.3536618,0 8.0445865,46.3533216,0 8.0420523,46.3531278,0 8.0408092,46.3522215,0 8.0398632,46.350791,0 8.0388604,46.3505724,0 8.0373407,46.3494294,0 8.0356058,46.3480367,0 8.0343953,46.3472799,0 8.0333291,46.346744,0 8.0317421,46.3458979,0 8.0302326,46.3456978,0 8.0292399,46.3455515,0 8.0261735,46.3432866,0 8.0259836,46.3420655,0 8.0264551,46.3407013,0 8.0259262,46.3396684,0 8.0234949,46.3386346,0 8.020266,46.3377702,0 8.0160216,46.3346158,0 8.0141883,46.3326938,0 8.0135499,46.3301236,0 8.0124921,46.3283112,0 8.0115358,46.3273088,0 8.0108655,46.3266859,0 8.0102607,46.3262723,0 8.0095941,46.3259423,0 8.0087365,46.3256467,0 8.0080757,46.3255279,0 8.0073478,46.3254215,0 8.0055289,46.325252,0 8.0039364,46.3250342,0 8.0015581,46.324548,0 7.9993741,46.32384,0 7.9970168,46.3230628,0 7.9925065,46.3215534,0 7.9914432,46.3212472,0 7.991121,46.3211723,0 7.9903034,46.3209823,0 7.9886569,46.3207586,0 7.9869448,46.3206321,0 7.9836789,46.3201161,0 7.9788194,46.3181719,0 7.9758813,46.3174055,0 7.9600679,46.3117323,0 7.9565029,46.3106402,0 7.955247,46.310234,0 7.9542164,46.3097427,0 7.9527726,46.3088982,0 7.9519207,46.3084392,0 7.9504972,46.3077111,0 7.9489508,46.3071536,0 7.9473017,46.3066644,0 7.9425696,46.3053036,0 7.9413469,46.304854,0 7.9398251,46.3040189,0 7.9379488,46.3028253,0 7.9364718,46.3019089,0 7.9345709,46.3007621,0 7.933172,46.3000077,0 7.9318451,46.2994313,0 7.928514,46.2984772,0 7.9229629,46.2979464,0 7.9127991,46.2970075,0 7.900068,46.2959571,0 7.8969337,46.2962684,0 7.8883662,46.2998642,0 7.8831882,46.3016904,0 7.879355,46.3023285,0 7.870157,46.3024357,0 7.8594632,46.3028369,0 7.8473388,46.3065427,0 7.837881,46.3074945,0 7.8282882,46.3085712,0 7.8254374,46.3083172,0 7.8119843,46.3070984,0 7.79907,46.3068076,0 7.7961276,46.3060891,0 7.7825327,46.3059889,0 7.7791226,46.3062115,0 7.7759134,46.3067902,0 7.7584525,46.3091078,0 7.7557324,46.3089797,0 7.7502818,46.3078243,0 7.7421452,46.3062882,0 7.734567,46.3067893,0 7.7211303,46.3088396,0 7.7149656,46.3096625,0 7.7102771,46.3102013,0 7.7074025,46.3103195,0 7.7044396,46.3102865,0 7.6996507,46.3100588,0 7.6974949,46.3101029,0 7.6966104,46.3100717,0 7.6957296,46.3099474,0 7.694161,46.3094777,0 7.6927706,46.3088802,0 7.691423,46.3085228,0 7.6899252,46.308143,0 7.6817564,46.3065984,0 7.6791486,46.3062246,0 7.676791,46.3060118,0 7.6743843,46.3058588,0 7.6709507,46.3060023,0 7.6685512,46.3061912,0 7.667216,46.3063457,0 7.6661641,46.3065173,0 7.6609949,46.3077651,0 7.6568983,46.3086912,0 7.6499748,46.3103429,0 7.6470405,46.3121565,0 7.6456254,46.3125409,0 7.6411929,46.3128287,0 7.6364073,46.3118884,0 7.6334386,46.3118577,0 7.6328038,46.3119827,0 7.6321042,46.3121204,0 7.6275072,46.3135204,0 7.6208014,46.3155027,0 7.6179549,46.315624,0 7.6148903,46.3151243,0 7.6081835,46.3136662,0 7.6010581,46.3127966,0 7.5993032,46.3121006,0 7.5967373,46.3098956,0 7.5961853,46.3096521,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Engstlige</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6022785,46.5401947,0 7.6025091,46.540512,0 7.6031368,46.5413754,0 7.6026218,46.5429104,0 7.6027935,46.5440911,0 7.6017635,46.5453898,0 7.602103,46.5468677,0 7.6022785,46.5471607,0 7.6033085,46.5479872,0 7.6043384,46.5489316,0 7.6057117,46.54964,0 7.6057117,46.5507025,0 7.6077717,46.5521191,0 7.6090281,46.5540874,0 7.6097828,46.554791,0 7.6104014,46.5553546,0 7.6122349,46.5567229,0 7.6142948,46.556959,0 7.6150274,46.5575363,0 7.6158784,46.5582068,0 7.6165495,46.5597606,0 7.6176459,46.5602837,0 7.6196163,46.5615623,0 7.6206463,46.5632147,0 7.6208892,46.5635148,0 7.6216696,46.564479,0 7.6231862,46.5660439,0 7.6242334,46.5683426,0 7.6251095,46.5691157,0 7.6269613,46.569587,0 7.627585,46.5698201,0 7.6290577,46.5697058,0 7.6307743,46.5707679,0 7.6319759,46.5715939,0 7.6335209,46.573482,0 7.6348942,46.5744261,0 7.6358843,46.5774784,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4148104,46.4967759,0 7.4160013,46.4959967,0 7.4175624,46.4951252,0 7.4178913,46.4949773,0 7.4184173,46.4947409,0 7.4193487,46.4946931,0 7.4201105,46.4947596,0 7.4212638,46.4950993,0 7.4229066,46.4958041,0 7.4239943,46.496366,0 7.4253086,46.4972116,0 7.4259845,46.4977434,0 7.4264298,46.4983564,0 7.4267034,46.4990432,0 7.427154,46.4997744,0 7.4279211,46.5003061,0 7.4291656,46.5007825,0 7.4300186,46.5014102,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4148104,46.4967759,0 7.4140004,46.4972966,0 7.4128648,46.4984032,0 7.4120378,46.4988758,0 7.4116834,46.4993483,0 7.4114471,46.5002048,0 7.4109746,46.5010613,0 7.4102738,46.5016021,0 7.4094683,46.5017406,0 7.4089071,46.5017111,0 7.4082574,46.5019769,0 7.4079333,46.5023075,0 7.4067806,46.5030402,0 7.4056266,46.5035924,0 7.4046502,46.5039764,0 7.4037385,46.504192,0 7.4032394,46.5044749,0 7.402762,46.5046706,0 7.4023435,46.5047998,0 7.4018339,46.5048885,0 7.4011395,46.5049009,0 7.4002605,46.5049802,0 7.3995446,46.5052257,0 7.3988062,46.5054175,0 7.3977637,46.5056704,0 7.3967979,46.5057762,0 7.3956424,46.5059331,0 7.3945836,46.5062568,0 7.3941289,46.5065149,0 7.3937853,46.5067911,0 7.3931798,46.5074121,0 7.3928697,46.5082678,0 7.3927221,46.5088881,0 7.3928224,46.5093561,0 7.3926507,46.5097814,0 7.3921786,46.5102658,0 7.3915778,46.5107148,0 7.3910628,46.5110219,0 7.3904363,46.5111755,0 7.3892604,46.5112759,0 7.3888055,46.5114827,0 7.3888484,46.5121029,0 7.3889514,46.5125459,0 7.3891746,46.5130893,0 7.389679,46.5139383,0 7.3895618,46.5144406,0 7.3891188,46.5151494,0 7.388379,46.5162216,0 7.3879079,46.5172759,0 7.3877307,46.5179075,0 7.3877307,46.5188117,0 7.387583,46.5198455,0 7.387583,46.5213813,0 7.3877011,46.5231534,0 7.3879079,46.5251027,0 7.38841,46.5268157,0 7.3892074,46.5289422,0 7.3898764,46.5304745,0 7.3904774,46.5319547,0 7.3905365,46.5326931,0 7.3902707,46.533461,0 7.3898867,46.5341108,0 7.3891188,46.5346719,0 7.3882918,46.5349673,0 7.3872877,46.5350263,0 7.3863721,46.5353512,0 7.3858224,46.5362191,0 7.3856622,46.5375599,0 7.3854562,46.5381739,0 7.3851142,46.5385692,0 7.3840142,46.5395673,0 7.3835164,46.5400219,0 7.3830872,46.5405709,0 7.3826409,46.5408897,0 7.3817339,46.5417885,0 7.3808969,46.5424967,0 7.3809935,46.543044,0 7.3813476,46.5434625,0 7.3816696,46.5441064,0 7.3815086,46.5445893,0 7.3812189,46.5454263,0 7.3810901,46.5463599,0 7.3812832,46.5471969,0 7.3817017,46.5481305,0 7.3822168,46.5489031,0 7.382571,46.5494504,0 7.3826353,46.5499977,0 7.3822812,46.5506093,0 7.3817983,46.5515107,0 7.3817983,46.5521868,0 7.3821846,46.5528629,0 7.3823134,46.5532492,0 7.3819915,46.5538286,0 7.3816374,46.5546013,0 7.3812307,46.5561465,0 7.3812501,46.5566592,0 7.3812932,46.5571399,0 7.3812976,46.5574732,0 7.3812628,46.5576802,0 7.3812452,46.5577312,0 7.3811767,46.5578887,0 7.3810499,46.5581318,0 7.3808341,46.5585264,0 7.3803462,46.5593713,0 7.3798668,46.560396,0 7.3793147,46.5617983,0 7.3788502,46.5629464,0 7.3784991,46.563768,0 7.3774509,46.5660112,0 7.3772782,46.5665588,0 7.3772214,46.5669788,0 7.3772391,46.5675024,0 7.3774145,46.5680778,0 7.3776827,46.56869,0 7.3783264,46.5700693,0 7.378901,46.571245,0 7.379453,46.5718689,0 7.3800967,46.5723188,0 7.3807038,46.5727259,0 7.3817661,46.5733376,0 7.383086,46.5744643,0 7.3834248,46.5749918,0 7.3836909,46.5753722,0 7.3841028,46.575865,0 7.3844461,46.5762662,0 7.3846436,46.576573,0 7.3846779,46.5769447,0 7.3844204,46.5775346,0 7.3840518,46.5788425,0 7.3832792,46.5814502,0 7.3831651,46.5819281,0 7.3831866,46.5822231,0 7.3834226,46.5825623,0 7.3833583,46.5827835,0 7.3827789,46.5836905,0 7.382571,46.5845085,0 7.3826353,46.5855065,0 7.3826353,46.586404,0 7.3825429,46.5867432,0 7.3823475,46.5869475,0 7.3819313,46.5871488,0 7.3817704,46.5873257,0 7.381706,46.5877165,0 7.3810579,46.5885648,0 7.3810301,46.5887635,0 7.3811696,46.5893091,0 7.3810408,46.5894787,0 7.3807726,46.5895008,0 7.3803542,46.5895303,0 7.3799787,46.5896557,0 7.3793285,46.5898139,0 7.3785468,46.5897475,0 7.3778502,46.5896055,0 7.3772428,46.5896925,0 7.3770068,46.5898179,0 7.3768995,46.5900317,0 7.3769424,46.5904593,0 7.3767064,46.5907837,0 7.3759876,46.5914547,0 7.3754404,46.5922583,0 7.3753507,46.5926344,0 7.3754025,46.5932383,0 7.3754241,46.5934903,0 7.3756173,46.5940054,0 7.3758104,46.594778,0 7.3758506,46.5952003,0 7.3757515,46.595598,0 7.375655,46.5960772,0 7.3755906,46.5966375,0 7.3756764,46.5973821,0 7.3758373,46.5978907,0 7.3761163,46.5983552,0 7.3766098,46.5990923,0 7.3767171,46.5996305,0 7.3765991,46.6001023,0 7.3763255,46.600605,0 7.3758426,46.6010879,0 7.375231,46.6018605,0 7.3748768,46.6032448,0 7.3747644,46.6040594,0 7.374861,46.6045177,0 7.3750863,46.6048641,0 7.3759446,46.6058224,0 7.3766206,46.606729,0 7.3773179,46.6078198,0 7.3779724,46.609316,0 7.378579,46.6101663,0 7.378901,46.611132,0 7.378938,46.6123378,0 7.3790345,46.6126326,0 7.3792813,46.6131559,0 7.3794804,46.6138041,0 7.3795066,46.6141066,0 7.3793517,46.6146411,0 7.379174,46.6154331,0 7.3792229,46.6158,0 7.3793826,46.6161099,0 7.3796032,46.6163838,0 7.3802531,46.6170877,0 7.3813154,46.6182789,0 7.3825321,46.6192651,0 7.3836479,46.6203042,0 7.3841415,46.620621,0 7.3854504,46.6211663,0 7.3863409,46.6217116,0 7.3871955,46.622324,0 7.3884623,46.6235263,0 7.3891411,46.6241137,0 7.3909543,46.6254695,0 7.3924349,46.626221,0 7.3932288,46.6265821,0 7.3937867,46.626921,0 7.3943231,46.6273336,0 7.3949367,46.6279379,0 7.3955024,46.6286537,0 7.3958809,46.6289915,0 7.3968035,46.6296546,0 7.3984138,46.6307647,0 7.3990974,46.6311606,0 7.4000866,46.6314965,0 7.4013548,46.6318266,0 7.4028203,46.6322506,0 7.4036485,46.6326016,0 7.4044747,46.6331541,0 7.4053416,46.6338759,0 7.4065218,46.6346347,0 7.407584,46.6353641,0 7.4083585,46.6358357,0 7.4091954,46.6369775,0 7.4098176,46.6373827,0 7.4105901,46.6377584,0 7.4115879,46.6381562,0 7.4126608,46.6385318,0 7.4137337,46.6388044,0 7.4149031,46.6390622,0 7.4156401,46.6391335,0 7.4159545,46.6392243,0 7.4160189,46.6395852,0 7.4155585,46.6402065,0 7.4154717,46.6403955,0 7.4157075,46.6410153,0 7.4159223,46.6414931,0 7.4164159,46.6419645,0 7.4169931,46.6423048,0 7.417594,46.6426584,0 7.4178321,46.6429294,0 7.4177248,46.6435629,0 7.4177677,46.6437396,0 7.4180681,46.644211,0 7.4182612,46.6449402,0 7.4184865,46.6458167,0 7.4184543,46.6464281,0 7.4186163,46.6469861,0 7.4190122,46.6477833,0 7.4197203,46.6485345,0 7.4200422,46.6486671,0 7.4206645,46.648726,0 7.4216193,46.6487334,0 7.4220056,46.648807,0 7.4222845,46.6489175,0 7.4225528,46.6493005,0 7.4230731,46.6495547,0 7.4237007,46.6498602,0 7.4246771,46.6498896,0 7.4251484,46.6499459,0 7.4253315,46.6500443,0 7.4255354,46.6502947,0 7.4257357,46.6503853,0 7.4260182,46.6505967,0 7.4266793,46.6509836,0 7.4273938,46.6512898,0 7.4281961,46.6515467,0 7.4288057,46.6518341,0 7.4292046,46.651996,0 7.4295587,46.6520328,0 7.4304063,46.651996,0 7.4311573,46.6518634,0 7.4315614,46.6518511,0 7.4318761,46.6519076,0 7.43208,46.6520033,0 7.4321658,46.6522758,0 7.432075,46.6526151,0 7.4320207,46.6529228,0 7.4321229,46.653108,0 7.4324126,46.6532111,0 7.4327452,46.6532111,0 7.4334962,46.6530712,0 7.4344022,46.6528547,0 7.4347764,46.6526846,0 7.4352772,46.6526588,0 7.4357278,46.6528502,0 7.4359161,46.6531269,0 7.4360862,46.6535522,0 7.4362964,46.65422,0 7.4366183,46.6546692,0 7.4370898,46.65493,0 7.4379916,46.6552289,0 7.4383241,46.6552363,0 7.4390537,46.6550374,0 7.439912,46.6545956,0 7.4405665,46.6544483,0 7.4411887,46.6544925,0 7.4415144,46.6545737,0 7.4421865,46.6550301,0 7.4427015,46.6552142,0 7.443779,46.6555006,0 7.4441821,46.6557591,0 7.4444725,46.6561208,0 7.4447106,46.6568522,0 7.4448687,46.6572687,0 7.4451477,46.6575412,0 7.4455768,46.6576664,0 7.4461133,46.6577253,0 7.446639,46.6576885,0 7.4471003,46.6576148,0 7.4475617,46.6574455,0 7.4481947,46.6570846,0 7.4487204,46.6568269,0 7.4491174,46.6567754,0 7.4497182,46.6569153,0 7.450201,46.657092,0 7.4509535,46.6572605,0 7.451703,46.6574086,0 7.4520463,46.6575191,0 7.4523145,46.6577916,0 7.4524567,46.6580332,0 7.4525613,46.658712,0 7.4527222,46.6589035,0 7.4529583,46.6590066,0 7.4533016,46.6589698,0 7.4541814,46.6585353,0 7.4552232,46.6582471,0 7.4563289,46.658111,0 7.4575707,46.6578729,0 7.4586593,46.6577708,0 7.4589557,46.6578284,0 7.4594418,46.658094,0 7.4602924,46.6585193,0 7.460962,46.6587783,0 7.4616915,46.6588519,0 7.4625928,46.6588519,0 7.462979,46.6587415,0 7.4636435,46.6582471,0 7.4643409,46.658128,0 7.4651574,46.658128,0 7.466093,46.6581451,0 7.4670238,46.6580051,0 7.4677748,46.6578284,0 7.4683649,46.6574676,0 7.4688048,46.6571215,0 7.4691481,46.6570257,0 7.4698025,46.6570331,0 7.4706518,46.6573285,0 7.4711451,46.6577028,0 7.4717575,46.6579069,0 7.4720985,46.657902,0 7.472574,46.6577708,0 7.4738668,46.6574476,0 7.4742861,46.6574162,0 7.4747593,46.6574823,0 7.4753172,46.6577106,0 7.4755454,46.6578863,0 7.475755,46.6581621,0 7.4760038,46.6582187,0 7.4771518,46.6581671,0 7.4781603,46.6580419,0 7.4793298,46.658064,0 7.4802954,46.6583365,0 7.481379,46.6585574,0 7.4835247,46.6587709,0 7.4838788,46.6587488,0 7.4841577,46.6586163,0 7.4846935,46.6573897,0 7.4854345,46.6569374,0 7.4864537,46.6567754,0 7.4871403,46.6569815,0 7.4882347,46.6582187,0 7.4884493,46.6583733,0 7.4887926,46.6584248,0 7.4896509,46.6584101,0 7.4910671,46.6580861,0 7.4921614,46.6579609,0 7.4926013,46.6577474,0 7.4929333,46.6572719,0 7.4935179,46.6566448,0 7.494275,46.6558328,0 7.4947793,46.6555456,0 7.4952084,46.6556708,0 7.4961311,46.6568122,0 7.496292,46.6570625,0 7.496571,46.657092,0 7.4968815,46.6569184,0 7.4990861,46.6560537,0 7.4994249,46.656039,0 7.4998862,46.6561273,0 7.5016135,46.6567238,0 7.5019995,46.6567547,0 7.5024182,46.6566649,0 7.5027079,46.6565103,0 7.5034053,46.6551553,0 7.5038666,46.6549196,0 7.5043387,46.654927,0 7.5045854,46.6550522,0 7.5054759,46.6560463,0 7.5063557,46.656849,0 7.5068814,46.6570036,0 7.5079865,46.6569668,0 7.5084264,46.6567312,0 7.508877,46.655656,0 7.5091559,46.6554719,0 7.5100142,46.6554793,0 7.5118274,46.6558475,0 7.512396,46.655818,0 7.5129647,46.6555824,0 7.5148315,46.6543231,0 7.5151748,46.6541906,0 7.5160546,46.6543894,0 7.5170416,46.6549491,0 7.5182111,46.6553615,0 7.5187153,46.6553836,0 7.5191969,46.655285,0 7.5219876,46.6555677,0 7.5224275,46.6558622,0 7.5226313,46.6560242,0 7.5232,46.6566944,0 7.5233824,46.6567901,0 7.5238759,46.6568269,0 7.5243265,46.656768,0 7.5251412,46.656318,0 7.5255603,46.6561494,0 7.5259036,46.6561347,0 7.5261075,46.6562451,0 7.5263328,46.6565471,0 7.5264079,46.6568122,0 7.5264401,46.6573866,0 7.5265903,46.6577768,0 7.526998,46.6583144,0 7.5273306,46.6586237,0 7.5278563,46.658874,0 7.5285902,46.6589904,0 7.5291652,46.6590139,0 7.5300879,46.6589771,0 7.5313002,46.6588593,0 7.5321371,46.6586605,0 7.5332529,46.6583365,0 7.5339932,46.6582997,0 7.5349802,46.6583807,0 7.535675,46.6585294,0 7.5362033,46.6586016,0 7.5375236,46.6589473,0 7.5382789,46.6590238,0 7.5387461,46.6589845,0 7.5394863,46.6587341,0 7.5400127,46.6586291,0 7.5411714,46.6584995,0 7.5417379,46.6583405,0 7.5438579,46.6580047,0 7.5445396,46.6578873,0 7.545214,46.657716,0 7.5457977,46.6574274,0 7.5475283,46.6565588,0 7.5477583,46.6564513,0 7.547906,46.6564174,0 7.5481338,46.6564587,0 7.5483695,46.6565293,0 7.5486166,46.6567091,0 7.5495822,46.6572908,0 7.5505667,46.6577488,0 7.5511847,46.6579019,0 7.5522833,46.6581022,0 7.5537081,46.6582024,0 7.5552874,46.6582377,0 7.5565062,46.6583732,0 7.5580426,46.6587502,0 7.5598632,46.6591842,0 7.5612284,46.6593698,0 7.5628834,46.6595278,0 7.5646859,46.659734,0 7.565733,46.6600875,0 7.5669776,46.6607885,0 7.5672692,46.6609686,0 7.567561,46.6610981,0 7.5682734,46.6611099,0 7.5690632,46.6610594,0 7.570179,46.6608297,0 7.571621,46.6606471,0 7.573226,46.6605469,0 7.5741959,46.6603525,0 7.5747505,46.6602497,0 7.575552,46.6602524,0 7.5765393,46.6604319,0 7.5770308,46.6605957,0 7.5774575,46.6608356,0 7.5780154,46.6612951,0 7.579011,46.6621669,0 7.5799981,46.6625439,0 7.5809336,46.6626263,0 7.5824872,46.6625203,0 7.5834571,46.6623023,0 7.5850793,46.6618723,0 7.5859376,46.6618075,0 7.5869761,46.66189,0 7.5897999,46.6624202,0 7.5909189,46.6625818,0 7.5919832,46.6628881,0 7.5932191,46.6633947,0 7.595013,46.6643254,0 7.5968498,46.6652443,0 7.5988239,46.6661926,0 7.600283,46.6666284,0 7.6029438,46.6673411,0 7.6053384,46.6679242,0 7.6076301,46.6685309,0 7.6095527,46.6688784,0 7.6126083,46.6694556,0 7.6163934,46.6699916,0 7.6183847,46.6703273,0 7.6192687,46.6702802,0 7.6199627,46.6704663,0 7.6212429,46.6709928,0 7.6221355,46.6715464,0 7.6262226,46.6743988,0 7.6287174,46.6751778,0 7.6318073,46.6763556,0 7.6331806,46.6781223,0 7.6336956,46.6807133,0 7.6333277,46.6828388,0 7.6332934,46.6838751,0 7.6332076,46.6847583,0 7.6333792,46.6854944,0 7.6335595,46.6860596,0 7.6334222,46.6866484,0 7.6331818,46.6870075,0 7.6330359,46.6873549,0 7.6330874,46.6876846,0 7.6333449,46.6879202,0 7.6336196,46.6881027,0 7.6339972,46.6883735,0 7.634126,46.6885855,0 7.6341088,46.6888622,0 7.6339371,46.6890683,0 7.6334822,46.6894509,0 7.6331132,46.6898101,0 7.632993,46.6899867,0 7.6330188,46.6901869,0 7.6333277,46.6903694,0 7.633508,46.6905637,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4429173,46.4572666,0 7.4429683,46.4571225,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.44395,46.4543877,0 7.443789,46.4545688,0 7.4436469,46.4548072,0 7.4435745,46.4550326,0 7.443494,46.4555186,0 7.443441,46.455818,0 7.443435,46.4561136,0 7.4433377,46.4564021,0 7.4429683,46.4571225,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4612723,46.4333085,0 7.45888,46.4355827,0 7.4579349,46.4367641,0 7.4560742,46.4384476,0 7.4554244,46.4400129,0 7.455212,46.4413715,0 7.4552472,46.4423757,0 7.4547003,46.4438428,0 7.4532036,46.4478792,0 7.4527959,46.449025,0 7.4522863,46.4494907,0 7.4515084,46.4499268,0 7.4496061,46.4508817,0 7.4446738,46.4539829,0 7.4442665,46.4542916,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4442665,46.4542916,0 7.4441377,46.4543619,0 7.44395,46.4543877,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5823262,46.3982278,0 7.5836565,46.3984054,0 7.5848002,46.398503,0 7.5847788,46.3978223,0 7.5848431,46.3973118,0 7.5854654,46.3969048,0 7.5862915,46.3966828,0 7.5870533,46.3968456,0 7.5876434,46.3967346,0 7.5876648,46.3963795,0 7.5874717,46.3958763,0 7.5879121,46.395537,0 7.5886701,46.3953454,0 7.5898039,46.395609,0 7.5912654,46.3960014,0 7.5926709,46.3963765,0 7.5938339,46.3967716,0 7.596617,46.3973843,0 7.5990921,46.3975722,0 7.6018644,46.3974561,0 7.6032688,46.3970143,0 7.6046692,46.3966843,0 7.604964,46.3966148,0 7.6054361,46.3970143,0 7.6058438,46.3969107,0 7.6060424,46.3968161,0 7.6062248,46.3969197,0 7.6063213,46.3968383,0 7.6065359,46.3968087,0 7.6066217,46.3971787,0 7.6065896,46.3975856,0 7.6067934,46.3979778,0 7.6070187,46.398111,0 7.6072118,46.3982368,0 7.6074801,46.3985697,0 7.6071796,46.3988657,0 7.6065896,46.3991838,0 7.6071475,46.3995834,0 7.607126,46.3998793,0 7.6074371,46.4002345,0 7.6079843,46.400634,0 7.6076624,46.4008856,0 7.6079628,46.4014997,0 7.6084349,46.4018252,0 7.6089177,46.4021877,0 7.6094542,46.4026908,0 7.6095722,46.4028758,0 7.6098404,46.4028166,0 7.6102588,46.4025872,0 7.6104841,46.4023653,0 7.6107416,46.4024763,0 7.6114228,46.4030059,0 7.6134008,46.4052111,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Simme</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4145422,46.4951732,0 7.4142955,46.4947005,0 7.4144778,46.4943903,0 7.4168811,46.4925068,0 7.4176214,46.4915687,0 7.4179647,46.4914284,0 7.4194131,46.4914801,0 7.4201304,46.4912558,0 7.4213014,46.490638,0 7.4221978,46.4901039,0 7.4230395,46.4899437,0 7.4239108,46.4895723,0 7.4250036,46.4889521,0 7.4259192,46.4881251,0 7.4262736,46.4870323,0 7.4262441,46.4857623,0 7.4258601,46.4834291,0 7.4260078,46.4823658,0 7.4290744,46.4758704,0 7.4296701,46.4748344,0 7.4302313,46.4735644,0 7.4309696,46.4723831,0 7.4330075,46.4704042,0 7.4391299,46.4648035,0 7.4396745,46.4641251,0 7.4399642,46.4635893,0 7.4401198,46.4633971,0 7.4406526,46.4630362,0 7.4408064,46.4628318,0 7.4408874,46.4624093,0 7.4411605,46.4619376,0 7.4415682,46.4616087,0 7.4417291,46.461435,0 7.4419008,46.4611135,0 7.4420697,46.4603689,0 7.4420939,46.4601343,0 7.4419759,46.4596613,0 7.4420295,46.4592917,0 7.4422333,46.4591143,0 7.4428556,46.4588372,0 7.4429763,46.4587282,0 7.44303,46.4585914,0 7.4428717,46.4576509,0 7.4429173,46.4572666,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5848002,46.398503,0 7.5848152,46.3989973,0 7.5849719,46.3993465,0 7.5851104,46.3997361,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5926709,46.3963765,0 7.5940689,46.396307,0 7.5958359,46.3966059,0 7.5970139,46.3966103,0 7.5990503,46.3969699,0 7.6011746,46.3970535,0 7.6032688,46.3970143,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.617414,46.3854152,0 7.6178217,46.3849933,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6190072,46.3886233,0 7.6193077,46.3883828,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6198816,46.3890377,0 7.620123,46.3887972,0 7.6201366,46.3881642,0 7.6199113,46.3874908,0 7.6200401,46.3867211,0 7.6205486,46.38637,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6181114,46.3727879,0 7.6185084,46.3726473,0 7.6190448,46.3726399,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Engstlige</name>
    +          <styleUrl>#style-Waterway_River</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5679163,46.4904384,0 7.5682865,46.490783,0 7.56887,46.4909814,0 7.5690784,46.4912436,0 7.5690856,46.4915223,0 7.569252,46.4918013,0 7.5692638,46.4922844,0 7.569386,46.4926358,0 7.5694157,46.4934985,0 7.5699636,46.4939489,0 7.5700405,46.4942176,0 7.5699498,46.4946884,0 7.5705973,46.4953414,0 7.5713722,46.4959515,0 7.5716743,46.4961181,0 7.5720713,46.4967711,0 7.5722066,46.4968701,0 7.5723745,46.4969336,0 7.572558,46.4968807,0 7.5730315,46.4969189,0 7.5734753,46.4969547,0 7.5740499,46.4971762,0 7.575296,46.4973752,0 7.5758975,46.4976559,0 7.5763149,46.4979143,0 7.5764972,46.4981977,0 7.5766154,46.4986548,0 7.5766651,46.4991564,0 7.5767911,46.4994885,0 7.5767477,46.499635,0 7.5768027,46.4998829,0 7.577038,46.5002692,0 7.5771766,46.5006686,0 7.5773397,46.5008466,0 7.5775215,46.500978,0 7.5784139,46.501225,0 7.5791897,46.5017317,0 7.580061,46.5017524,0 7.5802143,46.50181,0 7.5804344,46.5018927,0 7.58069,46.5022649,0 7.5808113,46.5028054,0 7.5807013,46.5029565,0 7.580053,46.5034856,0 7.5801763,46.5037025,0 7.5808618,46.504245,0 7.5813645,46.5047991,0 7.5821249,46.505979,0 7.5824844,46.5066477,0 7.582539,46.5071021,0 7.5824704,46.5074207,0 7.5824319,46.5083621,0 7.5825869,46.5090491,0 7.5825806,46.5095171,0 7.5820257,46.5098178,0 7.5819193,46.5106443,0 7.5818162,46.5109854,0 7.5821224,46.5116504,0 7.5829228,46.5126941,0 7.583598,46.5135612,0 7.5842966,46.5165233,0 7.5844909,46.5173472,0 7.5846293,46.517583,0 7.5848776,46.5177875,0 7.5856085,46.5179936,0 7.5860292,46.518225,0 7.5864387,46.5183726,0 7.5865335,46.5185698,0 7.5872008,46.5186978,0 7.5875705,46.5195246,0 7.5882623,46.5204856,0 7.5886196,46.5208262,0 7.5889364,46.5211571,0 7.5890967,46.5217219,0 7.5888244,46.5220523,0 7.5891101,46.5228483,0 7.5911672,46.5254435,0 7.593835,46.5287145,0 7.5952076,46.5310701,0 7.5969656,46.5334405,0 7.5980018,46.5343057,0 7.5984988,46.5361517,0 7.598946,46.5370064,0 7.5995921,46.5370064,0 7.6006357,46.5372799,0 7.6007848,46.5385447,0 7.6022785,46.5401947,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Waterway_Stream</name>
    +        <Style id="style-Waterway_Stream">
    +          <LineStyle>
    +            <color>#a0ea8b17</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4482603,46.4605075,0 7.4479974,46.4601472,0 7.4477238,46.4599273,0 7.4475369,46.4598635,0 7.4465542,46.459732,0 7.4454239,46.4596948,0 7.4441592,46.4598904,0 7.4427902,46.4601787,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4427902,46.4601787,0 7.4422655,46.4603116,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4422655,46.4603116,0 7.4420697,46.4603689,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Tschentbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5498993,46.5131535,0 7.5516111,46.512984,0 7.5534331,46.5128769,0 7.5551428,46.5130268,0 7.556419,46.5133488,0 7.5568307,46.5137849,0 7.5571667,46.5138959,0 7.5583266,46.514089,0 7.5589673,46.5141025,0 7.5609632,46.5138736,0 7.5615361,46.5137486,0 7.5622618,46.5133878,0 7.5633041,46.5130869,0 7.5638865,46.5131159,0 7.5649706,46.5131294,0 7.565441,46.5131179,0 7.5657838,46.5130325,0 7.5664322,46.5130568,0 7.5671807,46.5131231,0 7.5683759,46.5133121,0 7.569028,46.5133944,0 7.5696122,46.5135692,0 7.5704753,46.5137527,0 7.5714119,46.5135382,0 7.5726887,46.5133277,0 7.573583,46.513235,0 7.574029,46.513337,0 7.574712,46.51333,0 7.574925,46.513248,0 7.577474,46.513801,0 7.577839,46.514218,0 7.578268,46.514264,0 7.57838,46.514375,0 7.578653,46.514452,0 7.578728,46.51488,0 7.5793375,46.5154736,0 7.5796094,46.5155709,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Tschentbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5796094,46.5155709,0 7.5798811,46.5155784,0 7.5804693,46.5157084,0 7.5808291,46.5159788,0 7.581439,46.5159571,0 7.5819875,46.5161131,0 7.5828673,46.5159575,0 7.5835048,46.5163951,0 7.5840989,46.5164935,0 7.5842966,46.5165233,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bonderlenbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5950493,46.4758228,0 7.5907678,46.4813085,0 7.5897696,46.4824297,0 7.5889451,46.4849235,0 7.588016,46.4856619,0 7.5871102,46.4859308,0 7.5866819,46.4864895,0 7.5847333,46.4880633,0 7.5834673,46.4887214,0 7.5817525,46.4898969,0 7.5814474,46.4901303,0 7.5810387,46.490633,0 7.5801554,46.4913069,0 7.5799536,46.4916604,0 7.5792927,46.4922356,0 7.5790885,46.4927514,0 7.5784129,46.4935776,0 7.5784782,46.494305,0 7.578291,46.4948469,0 7.5783849,46.4950906,0 7.5778436,46.4955583,0 7.5771013,46.4959518,0 7.576988,46.4961764,0 7.5767947,46.496307,0 7.5767531,46.496691,0 7.5765765,46.4969136,0 7.5759942,46.4969659,0 7.5759141,46.4970779,0 7.576244,46.4975421,0 7.5763149,46.4979143,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5864107,46.5038145,0 7.5849124,46.5043821,0 7.5842642,46.5051023,0 7.5834123,46.505664,0 7.582847,46.5058125,0 7.5826781,46.5057472,0 7.5823863,46.5057664,0 7.5821249,46.505979,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5901733,46.4963584,0 7.5878156,46.4992787,0 7.5872543,46.4997632,0 7.5868956,46.5001347,0 7.5862173,46.5005062,0 7.5854443,46.5007068,0 7.5845889,46.5007134,0 7.5841943,46.5006119,0 7.5836448,46.5004516,0 7.5831399,46.5005241,0 7.5821281,46.5010073,0 7.5802143,46.50181,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6031924,46.5097266,0 7.5935129,46.513263,0 7.5907758,46.5139709,0 7.5894533,46.5145143,0 7.5885612,46.5149031,0 7.5883184,46.5153558,0 7.5880954,46.5157955,0 7.586282,46.5170016,0 7.5857963,46.5175161,0 7.5856085,46.5179936,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.627585,46.5698201,0 7.6274104,46.5701313,0 7.6247923,46.57119,0 7.6243015,46.5713244,0 7.6229993,46.5714951,0 7.6219771,46.5717669,0 7.6195533,46.5723564,0 7.618023,46.5727097,0 7.6164988,46.5733015,0 7.6160679,46.5736132,0 7.615965,46.573772,0 7.6158862,46.574073,0 7.6158473,46.5742219,0 7.6155516,46.5747077,0 7.6118253,46.5770347,0 7.6101343,46.5797527,0 7.6049675,46.5845394,0 7.6009691,46.5902559,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4345353,46.4542156,0 7.4375443,46.4530688,0 7.4379198,46.4525218,0 7.4388861,46.4520338,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4388861,46.4520338,0 7.4394596,46.4521561,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Krummenbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4394596,46.4521561,0 7.440223,46.4521175,0 7.4411836,46.4522683,0 7.4419013,46.4524977,0 7.442767,46.4530941,0 7.4435144,46.4537976,0 7.4438251,46.4541901,0 7.44395,46.4543877,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6043384,46.5489316,0 7.6036134,46.5492848,0 7.6020999,46.5497933,0 7.6016803,46.5498747,0 7.6010186,46.5501518,0 7.5938758,46.5534441,0 7.5928254,46.5539036,0 7.5921917,46.5543492,0 7.5913683,46.5548097,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6097828,46.554791,0 7.6092077,46.5550334,0 7.6083698,46.555712,0 7.6069001,46.556455,0 7.6046978,46.5571409,0 7.6034027,46.5574888,0 7.6029575,46.5586298,0 7.6021076,46.559089,0 7.6011565,46.5596316,0 7.5990811,46.560389,0 7.5971888,46.5607735,0 7.5962684,46.5607439,0 7.5949954,46.5611342,0 7.5936106,46.5614773,0 7.592888,46.5622047,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Engstlige</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5694288,46.4386699,0 7.5689025,46.4392606,0 7.568774,46.43946,0 7.568331,46.439699,0 7.567927,46.44015,0 7.567747,46.440192,0 7.567521,46.44046,0 7.5667949,46.4408454,0 7.5660904,46.4414907,0 7.565442,46.441646,0 7.5644443,46.4421245,0 7.5642466,46.4424135,0 7.56422,46.442644,0 7.5632839,46.443386,0 7.5615746,46.4449583,0 7.5621347,46.4456216,0 7.5626654,46.4466302,0 7.5627503,46.4485911,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5684827,46.4381903,0 7.568705,46.4386855,0 7.5687017,46.4389496,0 7.5689025,46.4392606,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5646506,46.4447386,0 7.5645313,46.4447993,0 7.5644484,46.4450193,0 7.5640955,46.4450566,0 7.5638276,46.4451873,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Engstligfall</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5627503,46.4485911,0 7.5629257,46.4519973,0 7.5622285,46.4525302,0 7.5607905,46.4537849,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5620068,46.5282186,0 7.5616241,46.5285431,0 7.5612987,46.5290602,0 7.5610529,46.5299302,0 7.5611554,46.5308279,0 7.5614905,46.5326035,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Otterbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5888244,46.5220523,0 7.5871462,46.521574,0 7.5861579,46.5215056,0 7.5848115,46.5211241,0 7.5830923,46.5211693,0 7.580815,46.522144,0 7.5765962,46.5222701,0 7.5744876,46.5222718,0 7.5650623,46.5253673,0 7.5641575,46.5257686,0 7.5630746,46.526248,0 7.562304,46.5273981,0 7.5621005,46.5277942,0 7.5620068,46.5282186,0 7.5620048,46.5291866,0 7.5623207,46.5299472,0 7.5624241,46.5306152,0 7.5627651,46.5313534,0 7.5628991,46.531972,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6030484,46.5615227,0 7.6049878,46.5605166,0 7.6063117,46.5602212,0 7.6087389,46.5599177,0 7.6107247,46.5592709,0 7.6120968,46.5587128,0 7.6127925,46.5583692,0 7.6139269,46.5579905,0 7.6150274,46.5575363,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5897984,46.5505716,0 7.590784,46.549945,0 7.5924214,46.5492396,0 7.5962237,46.5481799,0 7.5984549,46.547619,0 7.5998111,46.5476291,0 7.602103,46.5468677,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6025091,46.540512,0 7.6004436,46.5410848,0 7.5997811,46.5413145,0 7.5993888,46.5415236,0 7.5987378,46.5417055,0 7.5981278,46.5415615,0 7.5960941,46.5413591,0 7.5895257,46.5433921,0 7.5862162,46.545057,0 7.5845458,46.5461244,0 7.5839739,46.5467957,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Bettbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5952076,46.5310701,0 7.5933564,46.5308251,0 7.5920313,46.5309442,0 7.5907951,46.5309688,0 7.5893698,46.5311437,0 7.5871115,46.5320641,0 7.5840636,46.5327994,0 7.5808357,46.5338491,0 7.5792828,46.5348533,0 7.5769635,46.5361725,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5632368,46.463919,0 7.5643968,46.463232,0 7.5657493,46.4626938,0 7.5683745,46.4623092,0 7.5707137,46.4620696,0 7.5751947,46.4618406,0 7.5771588,46.4618878,0 7.5796521,46.4627464,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.563575,46.4676896,0 7.564838,46.4677747,0 7.5668754,46.4675639,0 7.5702666,46.4666669,0 7.572771,46.4663288,0 7.5792436,46.4665687,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.563948,46.4668286,0 7.5654428,46.4666022,0 7.5697357,46.466088,0 7.5727472,46.4655845,0 7.5754053,46.4654321,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Waleggbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4408874,46.4624093,0 7.4405704,46.4620447,0 7.4382377,46.4605186,0 7.4374644,46.459883,0 7.4365609,46.459575,0 7.4343172,46.4589405,0 7.4305385,46.4591194,0 7.4286491,46.4587615,0 7.4281059,46.4584361,0 7.4259568,46.4575088,0 7.4244689,46.4571508,0 7.4239493,46.457281,0 7.423477,46.4564838,0 7.4226976,46.4560282,0 7.4220836,46.4550682,0 7.4213514,46.4545313,0 7.420265,46.4548567,0 7.4199816,46.4538642,0 7.419155,46.4534249,0 7.4176945,46.4527747,0 7.4167971,46.4523029,0 7.4148841,46.4518961,0 7.4140103,46.4511639,0 7.413042,46.4502852,0 7.4125224,46.4500086,0 7.4107747,46.4495204,0 7.4101843,46.4483651,0 7.4091451,46.4472911,0 7.4072085,46.4462659,0 7.406193,46.4454523,0 7.4056498,46.4437435,0 7.4055789,46.4422301,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.455212,46.4413715,0 7.4541445,46.440869,0 7.4523968,46.4403156,0 7.4508381,46.4397623,0 7.4500823,46.4393554,0 7.4491849,46.4388834,0 7.4487125,46.4384277,0 7.4484055,46.4379557,0 7.4480512,46.4371582,0 7.4469176,46.436377,0 7.4457368,46.4358887,0 7.4445087,46.4352702,0 7.4440363,46.4346517,0 7.4439891,46.4340983,0 7.443871,46.4334961,0 7.4439419,46.433138,0 7.4434931,46.4326659,0 7.4430916,46.4320474,0 7.4427846,46.4316567,0 7.441651,46.4311359,0 7.4410369,46.4310056,0 7.4406354,46.4303057,0 7.4403284,46.4296709,0 7.4401867,46.4286616,0 7.4390295,46.4272779,0 7.4383682,46.4258942,0 7.4383446,46.4252919,0 7.4383588,46.4234132,0 7.4379809,46.4229704,0 7.4379809,46.4224234,0 7.4383965,46.4222671,0 7.4385477,46.4220197,0 7.4394546,46.4209517,0 7.439572,46.4205138,0 7.439024,46.419662,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Gilsbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5444891,46.4838255,0 7.5441715,46.4835228,0 7.543632,46.4826983,0 7.5431692,46.482315,0 7.5427131,46.4818558,0 7.5424228,46.4811668,0 7.5418523,46.4805595,0 7.5414058,46.4804053,0 7.5410234,46.4801636,0 7.5404661,46.4792391,0 7.5401919,46.478841,0 7.5391844,46.4774431,0 7.5389666,46.4766853,0 7.5386024,46.4761968,0 7.5383865,46.4757296,0 7.5376236,46.4739031,0 7.537638,46.4735448,0 7.5382409,46.4729685,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5579549,46.4702079,0 7.5585842,46.4702669,0 7.5596437,46.4708957,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5610883,46.4713922,0 7.5596437,46.4708957,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6113718,46.5266965,0 7.610492,46.5267482,0 7.6098322,46.5271948,0 7.6091348,46.5277705,0 7.6084374,46.5283758,0 7.6074933,46.5287818,0 7.6072465,46.5290438,0 7.606989,46.5291951,0 7.6063078,46.5294461,0 7.6059752,46.5295568,0 7.6051061,46.5297709,0 7.6038991,46.5300772,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.6011565,46.5596316,0 7.600767,46.5601643,0 7.6003628,46.5608149,0 7.6000703,46.5616961,0 7.5995972,46.5624472,0 7.5990811,46.5628907,0 7.5982726,46.5633757,0 7.5976963,46.5635176,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5622868,46.4987922,0 7.563293,46.498268,0 7.5640554,46.4980879,0 7.5649734,46.4979027,0 7.5661467,46.4975824,0 7.5672189,46.4974246,0 7.5679539,46.4973689,0 7.5684799,46.4974478,0 7.5689384,46.4976242,0 7.5694981,46.4977727,0 7.570604,46.4978563,0 7.5716963,46.4978702,0 7.5722763,46.4977959,0 7.5730315,46.4969189,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Allebach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5396772,46.4842367,0 7.5411265,46.4838022,0 7.5436247,46.4837589,0 7.5442613,46.4839194,0 7.5444006,46.4838783,0 7.5444891,46.4838255,0 7.5460021,46.483802,0 7.5486107,46.4844951,0 7.5496668,46.4846853,0 7.5502713,46.4849285,0 7.550623,46.485323,0 7.5523906,46.4863698,0 7.5529233,46.4865507,0 7.5537024,46.4865959,0 7.5547532,46.4867557,0 7.5592633,46.4879144,0 7.5604607,46.4882109,0 7.562318,46.4882312,0 7.5628498,46.4883362,0 7.5651569,46.489581,0 7.5658291,46.4897725,0 7.5665939,46.4899418,0 7.5669049,46.4901147,0 7.5679163,46.4904384,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Engstlige</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5607905,46.4537849,0 7.5604924,46.4541209,0 7.560399,46.454507,0 7.5606654,46.4568691,0 7.5619095,46.4595042,0 7.5625295,46.4614662,0 7.5626063,46.4627988,0 7.5632368,46.463919,0 7.5637327,46.4648,0 7.5640365,46.4664917,0 7.563948,46.4668286,0 7.5639095,46.4669753,0 7.563575,46.4676896,0 7.5632036,46.4684826,0 7.562787,46.4697544,0 7.5623296,46.4703181,0 7.5619262,46.4705013,0 7.5613885,46.4709202,0 7.56112,46.4712946,0 7.5610883,46.4713922,0 7.5611153,46.4716139,0 7.5613631,46.4719076,0 7.5611917,46.4726986,0 7.5615884,46.4733249,0 7.5611864,46.473944,0 7.5617112,46.4743256,0 7.5618499,46.4745876,0 7.5617554,46.4755833,0 7.56195,46.4759029,0 7.5620659,46.4763197,0 7.5621603,46.4764219,0 7.5627234,46.4765325,0 7.5628681,46.4766962,0 7.5627775,46.4768782,0 7.5625288,46.4770123,0 7.5622246,46.4773669,0 7.5622322,46.4775806,0 7.5623728,46.4776887,0 7.5625066,46.4781258,0 7.5619567,46.4786796,0 7.5618736,46.4788606,0 7.5619391,46.4792018,0 7.5622445,46.4793954,0 7.5628111,46.4795891,0 7.5629037,46.4797214,0 7.563056,46.4804239,0 7.5635031,46.4807911,0 7.5635066,46.4809088,0 7.563095,46.4814951,0 7.563097,46.4816769,0 7.5636883,46.4820501,0 7.5639504,46.4823962,0 7.5638459,46.4826554,0 7.563501,46.4830543,0 7.5635042,46.4831882,0 7.5639195,46.4834569,0 7.5640923,46.4836871,0 7.5642456,46.4841074,0 7.5642177,46.4843665,0 7.5634591,46.4851604,0 7.5636818,46.4857398,0 7.5649601,46.4874304,0 7.5661911,46.4879853,0 7.5665395,46.4886838,0 7.5676235,46.489324,0 7.5679163,46.4904384,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5962122,46.5089224,0 7.5886622,46.5110814,0 7.5869149,46.5114364,0 7.5829228,46.5126941,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.5641575,46.5257686,0 7.5613671,46.5258087,0 7.5604396,46.5259298,0 7.5596879,46.5260952,0 7.5590727,46.5263174,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4458894,46.38078,0 7.445546,46.3804839,0 7.4447092,46.3800546,0 7.4438509,46.379951,0 7.4439796,46.3802767,0 7.4442371,46.3809132,0 7.4443659,46.3813425,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.4452456,46.3812536,0 7.4444517,46.3803803,0 7.4438509,46.379951,0 7.4431428,46.3797808,0 7.4419411,46.3796031,0 7.4409755,46.3796993,0 7.4399348,46.3796919,0 7.4392482,46.3795143,0 7.4387225,46.3794773,0 7.4384221,46.3795365,0 7.4379286,46.379766,0 7.4372848,46.3803581,0 7.4369522,46.3810612,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +        <Placemark>
    +          <name>Schmittenbach</name>
    +          <styleUrl>#style-Waterway_Stream</styleUrl>
    +          <LineString>
    +            <altitudeMode>relativeToGround</altitudeMode>
    +            <coordinates>7.561025,46.4950847,0 7.5620763,46.4944701,0 7.562971,46.4940767,0 7.5634598,46.4938949,0 7.564526,46.4932355,0 7.5657085,46.4926575,0 7.5662682,46.492473,0 7.5667333,46.4923318,0 7.5674467,46.4921962,0 7.5692638,46.4922844,0</coordinates>
    +          </LineString>
    +        </Placemark>
    +      </Folder>
    +      <Folder>
    +        <name>Waterway_Canal</name>
    +        <Style id="style-Waterway_Canal">
    +          <LineStyle>
    +            <color>#a0ea8b17</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Waterway_Drain</name>
    +        <Style id="style-Waterway_Drain">
    +          <LineStyle>
    +            <color>#a0ea8b17</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Railways</name>
    +      <Folder>
    +        <name>Railway_Rail_Casing</name>
    +        <Style id="style-Railway_Rail_Casing">
    +          <LineStyle>
    +            <color>#80ffffff</color>
    +            <width>6</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Railway_Rail_Core</name>
    +        <Style id="style-Railway_Rail_Core">
    +          <LineStyle>
    +            <color>#ff000000</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Railway_Lightrail</name>
    +        <Style id="style-Railway_Lightrail">
    +          <LineStyle>
    +            <color>#ff000000</color>
    +            <width>4</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +      <Folder>
    +        <name>Railway_Tram</name>
    +        <Style id="style-Railway_Tram">
    +          <LineStyle>
    +            <color>#ff000000</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Natural_Features</name>
    +      <Folder>
    +        <name>Cliff</name>
    +        <Style id="style-Cliff">
    +          <LineStyle>
    +            <color>#ff8b8b8b</color>
    +            <width>2</width>
    +          </LineStyle>
    +        </Style>
    +      </Folder>
    +    </Folder>
    +    <Folder>
    +      <name>Roads</name>
    +      <Folder>
    +        <name>Core</name>
    +        <Folder>
    +          <name>Motorway</name>
    +          <Style id="style-Motorway">
    +            <LineStyle>
    +              <color>#ffbf3333</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Motorway_Link</name>
    +          <Style id="style-Motorway_Link">
    +            <LineStyle>
    +              <color>#ffbf3333</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Trunk</name>
    +          <Style id="style-Trunk">
    +            <LineStyle>
    +              <color>#ff17cfea</color>
    +              <width>6</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Trunk_Link</name>
    +          <Style id="style-Trunk_Link">
    +            <LineStyle>
    +              <color>#ff17cfea</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Primary</name>
    +          <Style id="style-Primary">
    +            <LineStyle>
    +              <color>#ff17cfea</color>
    +              <width>4</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Primary_Link</name>
    +          <Style id="style-Primary_Link">
    +            <LineStyle>
    +              <color>#ff17cfea</color>
    +              <width>2</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Secondary</name>
    +          <Style id="style-Secondary">
    +            <LineStyle>
    +              <color>#ff17e8ea</color>
    +              <width>4</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4421289,46.4613278,1 7.4424372,46.4608567,1 7.4425659,46.4606405,1 7.4426223,46.4604964,1 7.4426893,46.4602414,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4323137,46.4764918,1 7.4336701,46.4747049,1 7.4348985,46.4732419,1 7.4359588,46.472141,1 7.4370811,46.4707552,1 7.4377578,46.4699427,1 7.4380689,46.469466,1 7.4384373,46.46873,1 7.4387197,46.4681415,1 7.4387985,46.4679364,1 7.4388238,46.4677498,1 7.4388285,46.4674118,1 7.4388628,46.4671901,1 7.4389654,46.4669807,1 7.4390935,46.4668317,1 7.4398408,46.466209,1 7.4400461,46.4659856,1 7.4402163,46.4657472,1 7.4403488,46.4655089,1 7.4404453,46.465217,1 7.4406961,46.4643705,1 7.4409781,46.4635043,1 7.441073,46.463266,1 7.4411695,46.4631071,1 7.441316,46.4629389,1 7.4414914,46.4626895,1 7.4416374,46.4622146,1 7.4419152,46.461629,1 7.4421289,46.4613278,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4245844,46.4920082,1 7.4262126,46.4909139,1 7.4265951,46.4906478,1 7.4269109,46.4904109,1 7.4271226,46.4901976,1 7.4272528,46.4900189,1 7.4272987,46.4897359,1 7.4273256,46.4892013,1 7.4274383,46.4887803,1 7.427669,46.4881746,1 7.427858,46.4877541,1 7.4279747,46.4875615,1 7.4281029,46.4874142,1 7.4283818,46.4871409,1 7.4290041,46.4866386,1 7.4293903,46.4862988,1 7.4296806,46.4859289,1 7.4299413,46.4854299,1 7.430212,46.4846591,1 7.4303388,46.4844164,1 7.4305926,46.4840341,1 7.4306993,46.4837871,1 7.4307522,46.4836178,1 7.4307733,46.4833785,1 7.4307207,46.4828415,1 7.4307529,46.4824647,1 7.4309246,46.4821397,1 7.4312142,46.4817777,1 7.4313475,46.481483,1 7.4313859,46.4806991,1 7.431493,46.4804403,1 7.431579,46.4802927,1 7.4316738,46.4801621,1 7.4318371,46.4798194,1 7.4318258,46.4795687,1 7.4317788,46.479296,1 7.4315224,46.4785956,1 7.4314932,46.4783644,1 7.4315213,46.4780594,1 7.4315726,46.477845,1 7.431653,46.4776233,1 7.4318747,46.4771523,1 7.4323137,46.4764918,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4191059,46.4991161,1 7.4194662,46.4987815,1 7.4197451,46.4984344,1 7.4198288,46.4982833,1 7.4198964,46.4981614,1 7.4200241,46.4977476,1 7.4201298,46.4970122,1 7.4203128,46.4962737,1 7.4204663,46.4959279,1 7.4206823,46.4955804,1 7.4210493,46.4951362,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.421178,46.4949811,1 7.42196,46.4940534,1 7.4224211,46.4935642,1 7.4227418,46.4933061,1 7.4230877,46.4930497,1 7.4245844,46.4920082,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4210493,46.4951362,1 7.421178,46.4949811,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.442705,46.4601691,1 7.4427198,46.4601214,1 7.4427644,46.4599513,1 7.4428057,46.4598036,1 7.442913,46.4596483,1 7.4430721,46.4594012,1 7.4431497,46.4591692,1 7.443156,46.4590737,1 7.4431495,46.4589587,1 7.442987,46.4576685,1 7.4429961,46.4574482,1 7.4430461,46.4572518,1 7.4430622,46.4571982,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lenkstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4426893,46.4602414,1 7.442705,46.4601691,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Landstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5690779,46.4942399,1 7.5692844,46.4945619,1 7.5698423,46.4953005,1 7.5709768,46.4961208,1 7.5713873,46.4963345,1 7.5716754,46.4967489,1 7.5721169,46.4972208,1 7.5741339,46.4978412,1 7.5749927,46.4993482,1 7.5753145,46.5003082,1 7.5763776,46.5014193,1 7.5765386,46.5015899,1 7.5769888,46.501843,1 7.5784191,46.501918,1 7.5789112,46.5019253,1 7.5796023,46.5019527,1 7.5798635,46.5020363,1 7.5800494,46.5021673,1 7.580325,46.5027273,1 7.5803532,46.5027703,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6122594,46.5585364,1 7.6122192,46.5585079,1 7.610827,46.5573928,1 7.6094322,46.5564928,1 7.6086383,46.5555632,1 7.6077585,46.5551205,1 7.6058702,46.5535859,1 7.6048832,46.5526563,1 7.6046901,46.5520365,1 7.6040249,46.5516086,1 7.6028571,46.5505041,1 7.6026829,46.5500915,1 7.6025506,46.5498156,1 7.6025022,46.5497569,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6025022,46.5497569,1 7.6023929,46.5496256,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.612416,46.5586345,1 7.6122594,46.5585364,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6001154,46.5474612,1 7.6002652,46.547606,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6246335,46.5705545,1 7.6246247,46.5705495,1 7.6242166,46.5702873,1 7.6233582,46.5694317,1 7.6227574,46.5683549,1 7.6224141,46.5680303,1 7.6221411,46.5675483,1 7.6218995,46.5671196,1 7.6216416,46.5666731,1 7.6211267,46.5661273,1 7.6204829,46.5648438,1 7.6191955,46.5639734,1 7.6182728,46.5626161,1 7.6179588,46.5615664,1 7.6178013,46.5612694,1 7.6174972,46.56112,1 7.6168363,46.5609385,1 7.6157193,46.560403,1 7.6145821,46.5600341,1 7.6137452,46.5592522,1 7.612416,46.5586345,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6023929,46.5496256,1 7.6020079,46.5491442,1 7.6007848,46.5481112,1 7.6002652,46.547606,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6001154,46.5474612,1 7.5998406,46.5471962,1 7.599669,46.5467387,1 7.5997977,46.5460599,1 7.6002483,46.5451891,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5917717,46.5267057,1 7.5923442,46.5260517,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592227,46.5309052,1 7.5922718,46.5309677,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5987934,46.5407011,1 7.5987112,46.5396482,1 7.5984459,46.5390659,1 7.5987605,46.5380854,1 7.5981669,46.5376949,1 7.5979738,46.5372669,1 7.5975232,46.5369126,1 7.5972013,46.5362041,1 7.5967401,46.5358949,1 7.5963645,46.5355251,1 7.596107,46.5352151,1 7.5959139,46.5346099,1 7.5954367,46.534118,1 7.5951966,46.5330623,1 7.5942831,46.5321152,1 7.5939398,46.5315985,1 7.5936058,46.5312116,1 7.5932888,46.5311187,1 7.5926021,46.5311329,1 7.5924089,46.5310813,1 7.5923789,46.5310671,1 7.5922718,46.5309677,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5923442,46.5260517,1 7.5925689,46.5257627,1 7.5928454,46.5253974,1 7.5927869,46.5248461,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5987934,46.5407011,1 7.5990252,46.5417356,1 7.5997548,46.5427687,1 7.600656,46.5435067,1 7.6005487,46.5442594,1 7.6002483,46.5451891,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Adelbodenstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592227,46.5309052,1 7.5922401,46.5306523,1 7.5928025,46.5295318,1 7.5927596,46.5290299,1 7.5926159,46.5287063,1 7.5920786,46.5277859,1 7.5917134,46.5271711,1 7.5916653,46.5270074,1 7.5917717,46.5267057,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Landstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5597987,46.4888045,1 7.5599098,46.488794,1 7.5600095,46.4888091,1 7.5600873,46.4888392,1 7.5604036,46.4890905,1 7.5605568,46.4891692,1 7.5607125,46.4892161,1 7.5615168,46.4893029,1 7.5626755,46.4897461,1 7.5641775,46.4897756,1 7.5658886,46.4903417,1 7.5659944,46.4903674,1 7.5670612,46.490656,1 7.5674115,46.490768,1 7.5677132,46.4909027,1 7.5688982,46.4924052,1 7.568897,46.4933261,1 7.5688982,46.4937051,1 7.5690779,46.4942399,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Landstrasse</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5807143,46.5031184,1 7.5803532,46.5027703,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Landstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5597987,46.4888045,1 7.5597424,46.4888794,1 7.5597289,46.4889654,1 7.5598431,46.4891256,1 7.5604219,46.4895759,1 7.5610442,46.4898123,1 7.5618596,46.4904771,1 7.5619481,46.4908394,1 7.5619716,46.4909369,1 7.5620328,46.4912044,1 7.5620419,46.4912371,1 7.5621386,46.4916589,1 7.5623751,46.4918667,1 7.5633831,46.4927964,1 7.5633791,46.492965,1 7.5633139,46.4930204,1 7.5632271,46.4930606,1 7.5631435,46.493056,1 7.5625349,46.4929938,1 7.5615826,46.4930937,1 7.5609767,46.4931868,1 7.5597217,46.4932641,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Landstrasse (223.1)</name>
    +            <styleUrl>#style-Secondary</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5927869,46.5248461,1 7.5922439,46.524217,1 7.5915151,46.5232566,1 7.5901418,46.5224298,1 7.5896053,46.5219869,1 7.589224,46.5211243,1 7.5890023,46.5205153,1 7.5882535,46.5188272,1 7.5879102,46.5182808,1 7.5872235,46.5177345,1 7.5870469,46.5174702,1 7.587009,46.517011,1 7.5876833,46.5164516,1 7.5880389,46.5161546,1 7.5880818,46.5159478,1 7.5878243,46.5158445,1 7.5868373,46.5160955,1 7.5864725,46.5160512,1 7.5863867,46.5158592,1 7.5868373,46.5151357,1 7.5868587,46.5144269,1 7.5861506,46.5135852,1 7.5858073,46.5129798,1 7.5856571,46.5122562,1 7.5853231,46.5118698,1 7.5849493,46.5116193,1 7.5849029,46.5115884,1 7.5845033,46.511303,1 7.5837259,46.5103807,1 7.5835972,46.5099525,1 7.5837819,46.5092432,1 7.5837688,46.5089696,1 7.5834684,46.5083132,1 7.5833721,46.5079092,1 7.5833611,46.5074862,1 7.5835114,46.5066296,1 7.5834554,46.5063745,1 7.5833344,46.5060923,1 7.5823956,46.5048989,1 7.5819422,46.5041172,1 7.5814943,46.503661,1 7.5808419,46.5032417,1 7.5807143,46.5031184,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Unclassified</name>
    +          <Style id="style-Unclassified">
    +            <LineStyle>
    +              <color>#ffd0d0d0</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark>
    +            <name>Schlegelistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5650662,46.4957333,1 7.5647157,46.4954991,1 7.5643603,46.4953787,1 7.5628576,46.4946658,1 7.5626535,46.4945986,1 7.5617886,46.4948002,1 7.561591,46.494845,1 7.5614673,46.4947425,1 7.5615188,46.494565,1 7.5617978,46.4943139,1 7.5617449,46.4941344,1 7.5613391,46.4939717,1 7.5598323,46.4933677,1 7.5597217,46.4932641,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ruheweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5628576,46.4946658,1 7.5631584,46.4945553,1 7.5636005,46.4947211,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Dorfstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5597217,46.4932641,1 7.5595144,46.4930964,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Sunneweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5589173,46.4942818,1 7.5592854,46.4947564,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Haltenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5580946,46.4928578,1 7.5580394,46.4929428,1 7.5580016,46.493001,1 7.5584826,46.4935359,1 7.5589806,46.4941802,1 7.5589173,46.4942818,1 7.5580289,46.4940058,1 7.557863,46.4940514,1 7.5585141,46.4945781,1 7.5584892,46.4949762,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bellevuegaessli</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5581971,46.4920378,1 7.5579392,46.492211,1 7.5576315,46.4922586,1 7.5573333,46.4922537,1 7.5568548,46.4922385,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bellevuestrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5568451,46.4909915,1 7.5562427,46.4910686,1 7.5560919,46.4912908,1 7.5563797,46.4920739,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Senggistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.556064,46.4904831,1 7.5558567,46.490661,1 7.555653,46.4908226,1 7.5555547,46.4908982,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Untere Kurhausstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5594245,46.493615,1 7.5603132,46.4940893,1 7.5611146,46.4943645,1 7.5612008,46.4943941,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Obere Dorfstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5576315,46.4922586,1 7.5580946,46.4928578,1 7.5584537,46.4927197,1 7.5588805,46.4931539,1 7.5590802,46.4933577,1 7.5594245,46.493615,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5559303,46.4904006,1 7.5560779,46.4899713,1 7.5561408,46.4893354,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.599669,46.5467387,1 7.5994759,46.5465921,1 7.5994329,46.5460756,1 7.5998836,46.5456919,1 7.5998192,46.5452196,1 7.6005487,46.5449983,1 7.6007848,46.5444965,1 7.6010637,46.5442308,1 7.6011066,46.5438176,1 7.60145,46.5432715,1 7.6011281,46.5426074,1 7.6011066,46.5421794,1 7.6007068,46.54153,1 7.6001666,46.5412566,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Adelbodenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5982021,46.522924,1 7.5984662,46.5231705,1 7.5989692,46.523522,1 7.5991969,46.5235962,1 7.5997134,46.5240328,1 7.5999724,46.5242085,1 7.6002352,46.5243045,1 7.6008843,46.5244115,1 7.6011526,46.5245222,1 7.6012759,46.5246809,1 7.6017856,46.5255889,1 7.6019197,46.525803,1 7.6021074,46.5259285,1 7.6024799,46.5260617,1 7.6030516,46.5262828,1 7.6033037,46.5263123,1 7.6034753,46.5262754,1 7.6038103,46.5260773,1 7.604218,46.5260773,1 7.6046472,46.5258854,1 7.6049047,46.525974,1 7.6049261,46.5262841,1 7.6048111,46.5267257,1 7.6048403,46.5269189,1 7.6049935,46.5271612,1 7.6054441,46.5276262,1 7.6058059,46.5280704,1 7.6059609,46.5289507,1 7.6061361,46.5293018,1 7.6062112,46.5294716,1 7.6061897,46.5295602,1 7.6060932,46.5296155,1 7.6059035,46.5296615,1 7.6056342,46.5297829,1 7.6054173,46.5299514,1 7.60509,46.5303462,1 7.6049665,46.5305456,1 7.6049613,46.5306895,1 7.6050847,46.530922,1 7.6052788,46.5311467,1 7.6054977,46.5313944,1 7.6058679,46.5317228,1 7.6059001,46.5317745,1 7.6059376,46.5318926,1 7.6059913,46.5320586,1 7.6061305,46.5322292,1 7.6063638,46.5324253,1 7.6064526,46.5325901,1 7.6064848,46.5327635,1 7.6066135,46.5329037,1 7.6070674,46.5331968,1 7.6075738,46.5335237,1 7.6078014,46.5337372,1 7.6080673,46.5340035,1 7.6084911,46.5345533,1 7.6088129,46.5349371,1 7.6091724,46.5354021,1 7.6094674,46.5357084,1 7.6099073,46.5360479,1 7.6105403,46.5366752,1 7.6109557,46.5370289,1 7.6117258,46.5375645,1 7.6119684,46.5378839,1 7.6121573,46.5382392,1 7.6123535,46.5384464,1 7.6124421,46.5386678,1 7.61245,46.5388523,1 7.612367,46.5392618,1 7.6123505,46.5394348,1 7.6123964,46.5396051,1 7.6123763,46.5398221,1 7.6122861,46.5400548,1 7.6121496,46.5402582,1 7.6120152,46.5403909,1 7.6117741,46.5406899,1 7.6114898,46.5410958,1 7.6112776,46.5414422,1 7.6111894,46.5417378,1 7.6111376,46.5422652,1 7.6111518,46.5425828,1 7.6112269,46.5427045,1 7.611361,46.5428226,1 7.6116239,46.5430292,1 7.6122193,46.5434203,1 7.6129865,46.5439111,1 7.6135926,46.5443612,1 7.6146119,46.5451434,1 7.614784,46.5453074,1 7.6148962,46.5454644,1 7.6149659,46.5456968,1 7.6150679,46.5458444,1 7.6157974,46.546693,1 7.6160263,46.546901,1 7.6166128,46.5473312,1 7.6167278,46.5474471,1 7.6170688,46.5479732,1 7.6173072,46.548244,1 7.6177393,46.5486742,1 7.6180505,46.5488845,1 7.6184582,46.5491206,1 7.6196544,46.5497404,1 7.6199768,46.5500353,1 7.6204185,46.5506204,1 7.6207273,46.550873,1 7.6210438,46.5510574,1 7.6220678,46.5515417,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hörnliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5617886,46.4948002,1 7.5618606,46.4948615,1 7.5623632,46.494749,1 7.5625028,46.4947991,1 7.5625853,46.4950604,1 7.5626507,46.4952674,1 7.5627191,46.4953707,1 7.5631362,46.496001,1 7.5633343,46.4964952,1 7.5633664,46.4966989,1 7.5633291,46.4970595,1 7.5632368,46.4976696,1 7.563267,46.49787,1 7.5633854,46.4980113,1 7.563427,46.498168,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.559232,46.4796016,1 7.5585617,46.4808415,1 7.5583492,46.4819039,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5609767,46.4931868,1 7.561166,46.4933554,1 7.5615163,46.4934914,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5793256,46.4906279,1 7.580064,46.4905866,1 7.5805941,46.4903711,1 7.581293,46.4898469,1 7.5815091,46.4898495,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5594245,46.493615,1 7.5598323,46.4933677,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5587375,46.4925106,1 7.5584537,46.4927197,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5535228,46.4865284,1 7.5535075,46.4866379,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4450513,46.4614534,1 7.4452221,46.4614886,1 7.4456382,46.4613184,1 7.4461757,46.4609278,1 7.4463157,46.4610248,1 7.4464015,46.4610858,1 7.4466914,46.4607591,1 7.4468012,46.4607348,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bühlbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4430622,46.4571982,1 7.4431212,46.4572001,1 7.4432434,46.4572082,1 7.4442759,46.457382,1 7.4444794,46.4574163,1 7.4448677,46.4575035,1 7.4464578,46.4579817,1 7.4465356,46.4580981,1 7.4476729,46.4597444,1 7.4477077,46.4598442,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gutenbrunnenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4448677,46.4575035,1 7.4448112,46.457744,1 7.4446903,46.4584104,1 7.4446715,46.4588889,1 7.4446685,46.4592476,1 7.4447089,46.4594931,1 7.4447493,46.4595984,1 7.444827,46.4597038,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Wallenbachstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4421289,46.4613278,1 7.4420439,46.4613149,1 7.4419537,46.4612981,1 7.4418739,46.4612798,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ägertenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.441257,46.4569858,1 7.4409713,46.4574792,1 7.4407225,46.4578869,1 7.4398636,46.4589239,1 7.4396997,46.4590756,1 7.4393993,46.4592865,1 7.4389969,46.4594599,1 7.4386873,46.4595765,1 7.4384892,46.4596916,1 7.438253,46.4598534,1 7.438103,46.459935,1 7.4378459,46.4600208,1 7.4377581,46.4600511,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rütistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4378459,46.4600208,1 7.4371477,46.4594844,1 7.4369002,46.4593711,1 7.4351732,46.4588118,1 7.4346689,46.4587157,1 7.4343168,46.4586884,1 7.433596,46.4585605,1 7.4332261,46.4585191,1 7.4327699,46.458627,1 7.432094,46.4586566,1 7.4318151,46.4585975,1 7.4316005,46.4585827,1 7.4312142,46.4585309,1 7.430828,46.4585679,1 7.4304096,46.4586862,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4427198,46.4601214,1 7.4441314,46.4598231,1 7.444827,46.4597038,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4403161,46.4578243,1 7.4407225,46.4578869,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4407225,46.4578869,1 7.4409101,46.4579181,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4404453,46.465217,1 7.4393848,46.4651486,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fuhrenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4296586,46.4726679,1 7.4298576,46.4724974,1 7.4299482,46.4723427,1 7.4302165,46.4718551,1 7.4304096,46.4715226,1 7.4309246,46.4709019,1 7.4314181,46.4703033,1 7.4340467,46.4683451,1 7.4344114,46.4681161,1 7.4347333,46.4678353,1 7.4351088,46.4673623,1 7.4359993,46.4657735,1 7.4367235,46.464426,1 7.4368575,46.4640778,1 7.4368843,46.4636223,1 7.4366858,46.462123,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schadaulistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4318747,46.4771523,1 7.4308709,46.4767833,1 7.4304263,46.4765871,1 7.4295513,46.4761553,1 7.4293897,46.4761015,1 7.4292187,46.4760445,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4523989,46.4496459,1 7.452694,46.4494094,1 7.4529944,46.4490804,1 7.4538849,46.446604,1 7.454593,46.4446597,1 7.4548183,46.4438761,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4430622,46.4571982,1 7.4430863,46.457126,1 7.4433572,46.4566273,1 7.443502,46.4562947,1 7.4435554,46.4560527,1 7.4435718,46.4557441,1 7.4436426,46.455437,1 7.4437272,46.454981,1 7.4437756,46.4548719,1 7.4440412,46.4544542,1 7.444062,46.4544122,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4490998,46.4394476,1 7.4493703,46.4398371,1 7.4496041,46.4400538,1 7.4498937,46.4404937,1 7.4501101,46.4406845,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4501101,46.4406845,1 7.4518839,46.4414253,1 7.4527026,46.4418144,1 7.4535265,46.4432753,1 7.4540243,46.4437721,1 7.4545085,46.4440271,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4518839,46.4414253,1 7.4463319,46.4499791,1 7.4459512,46.4506147,1 7.4453521,46.4516335,1 7.4453308,46.4518244,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Pöschenriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.439194,46.451784,1 7.4391746,46.4521405,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Wallenbachstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.441721,46.461265,1 7.4416647,46.4612502,1 7.4414716,46.4614165,1 7.4413107,46.4614646,1 7.4411444,46.461472,1 7.4407767,46.4613635,1 7.4398247,46.4608733,1 7.439138,46.4604323,1 7.4390154,46.4603058,1 7.438972,46.4601436,1 7.4389969,46.4594599,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Pöschenriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.439194,46.451784,1 7.4393621,46.4512978,1 7.4397084,46.4508377,1 7.4397951,46.4507502,1 7.4408595,46.449676,1 7.441074,46.4493211,1 7.4412135,46.4489959,1 7.4412457,46.4486706,1 7.4412457,46.4482641,1 7.4413208,46.4480793,1 7.441589,46.4477022,1 7.4417178,46.4472513,1 7.4424473,46.4464677,1 7.442676,46.4460304,1 7.4430129,46.4450125,1 7.4431554,46.4447748,1 7.4432091,46.4444791,1 7.4432735,46.4442573,1 7.4433807,46.444176,1 7.4435095,46.4441242,1 7.4436275,46.4440133,1 7.443649,46.443762,1 7.4436919,46.4430449,1 7.4437348,46.4427639,1 7.4436919,46.4423351,1 7.4437348,46.4421207,1 7.4438839,46.4418249,1 7.4442712,46.4403685,1 7.4444429,46.4399619,1 7.4444751,46.4397401,1 7.4444429,46.4394961,1 7.4444107,46.4389564,1 7.4444536,46.4387272,1 7.4445287,46.4385128,1 7.444518,46.4381653,1 7.4444291,46.4378855,1 7.4443034,46.4374185,1 7.4440861,46.4371049,1 7.4436812,46.4366126,1 7.4434558,46.4364056,1 7.4431783,46.4362938,1 7.4428842,46.4360814,1 7.4424312,46.4355924,1 7.4421117,46.4349575,1 7.4418358,46.4339655,1 7.4412534,46.4325618,1 7.4411599,46.4323018,1 7.4409084,46.4319278,1 7.4400947,46.4304322,1 7.4399153,46.4300391,1 7.4398617,46.4296989,1 7.439851,46.4295214,1 7.4400012,46.4291665,1 7.4400211,46.4289007,1 7.4398188,46.4285453,1 7.4396378,46.4283387,1 7.4388103,46.4276505,1 7.4386922,46.4275026,1 7.4384669,46.4269258,1 7.4383275,46.4266152,1 7.4380485,46.4261049,1 7.4378983,46.4254615,1 7.4379305,46.4249956,1 7.4381451,46.4242413,1 7.4382309,46.4238049,1 7.4381635,46.4236286,1 7.4376515,46.4230654,1 7.4375228,46.4227696,1 7.4374799,46.4225699,1 7.4376086,46.4223924,1 7.437791,46.4222962,1 7.4380378,46.4222223,1 7.4381987,46.4221409,1 7.438424,46.4218007,1 7.4386171,46.4214975,1 7.4393222,46.4207591,1 7.4394433,46.4206174,1 7.4394111,46.4204694,1 7.4393252,46.4203659,1 7.4391107,46.4202697,1 7.438939,46.4200996,1 7.4387352,46.4197816,1 7.4383704,46.4195301,1 7.4381635,46.4193094,1 7.4381236,46.4191085,1 7.4381558,46.4187165,1 7.438167,46.4183333,1 7.4382845,46.417866,1 7.4385206,46.4174814,1 7.4387566,46.4170154,1 7.4387723,46.4169033,1 7.4387888,46.4167861,1 7.4386272,46.4164164,1 7.4384991,46.4161204,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4384991,46.4161204,1 7.4388581,46.415908,1 7.4389068,46.4156322,1 7.4388317,46.4152994,1 7.4389927,46.4149221,1 7.4394325,46.4147224,1 7.4399797,46.4143378,1 7.4403767,46.4141307,1 7.4406985,46.413857,1 7.4409959,46.4135401,1 7.4413392,46.4130075,1 7.4419793,46.4126531,1 7.4419305,46.4125999,1 7.4415906,46.4124847,1 7.441101,46.4124355,1 7.4404666,46.4125933,1 7.4404194,46.4125372,1 7.4405193,46.4124716,1 7.4412531,46.4122448,1 7.4423692,46.4122678,1 7.4424048,46.4122319,1 7.4423429,46.412186,1 7.441468,46.4118535,1 7.4403289,46.4116246,1 7.4396951,46.4114482,1 7.4396417,46.4113803,1 7.4397084,46.4112026,1 7.4392793,46.4109067,1 7.438893,46.4108771,1 7.4396655,46.4107587,1 7.4407813,46.4110251,1 7.4421975,46.4110546,1 7.4425409,46.4113801,1 7.4430558,46.4113407,1 7.4434284,46.4111913,1 7.4436951,46.4108827,1 7.4441145,46.4106569,1 7.4448583,46.4102856,1 7.4461028,46.4086579,1 7.4461457,46.4082436,1 7.4459741,46.4078589,1 7.4456308,46.4079477,1 7.4454162,46.4082436,1 7.4452874,46.4079477,1 7.445502,46.4075926,1 7.4458453,46.4073263,1 7.4463174,46.4064089,1 7.4472186,46.4059651,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gutenbrunnenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4449449,46.4598479,1 7.4450899,46.4600345,1 7.4451328,46.4601213,1 7.4451487,46.4602746,1 7.4450926,46.4604484,1 7.4448382,46.4608729,1 7.4447171,46.4611856,1 7.4446607,46.4613556,1 7.444642,46.4615773,1 7.4446527,46.4617085,1 7.4446688,46.4618747,1 7.4447493,46.4621371,1 7.4450569,46.4629393,1 7.4451223,46.4632094,1 7.445055,46.4636188,1 7.4449424,46.4639884,1 7.4445079,46.4650156,1 7.4444084,46.4652649,1 7.4442226,46.465441,1 7.4440304,46.4655661,1 7.4435166,46.4657543,1 7.4432198,46.46584,1 7.4430595,46.4659171,1 7.4429683,46.4660723,1 7.4428743,46.4668792,1 7.4428824,46.4673286,1 7.4429361,46.4676205,1 7.4431238,46.468001,1 7.4433223,46.4681045,1 7.4434994,46.4680971,1 7.4436635,46.4680594,1 7.4438105,46.4679789,1 7.4440358,46.4679124,1 7.4442075,46.467905,1 7.4443308,46.4679567,1 7.4443149,46.4680648,1 7.4439285,46.4684851,1 7.4438212,46.4686883,1 7.4437958,46.4688583,1 7.4437622,46.4690319,1 7.4436496,46.4691908,1 7.4437193,46.4693016,1 7.4438761,46.4692998,1 7.4439929,46.4692129,1 7.4440902,46.4690344,1 7.4441645,46.46874,1 7.4442289,46.4686735,1 7.4444113,46.4685885,1 7.4448194,46.4685572,1 7.4464444,46.4684814,1 7.4468575,46.4684296,1 7.4472223,46.4683225,1 7.4479143,46.4683484,1 7.4481074,46.4683114,1 7.4482853,46.4680752,1 7.4484024,46.46782,1 7.4487458,46.4674912,1 7.4490163,46.4674327,1 7.4496041,46.4670958,1 7.4497596,46.4670589,1 7.4498401,46.4671106,1 7.4498508,46.4671882,1 7.4497566,46.4672727,1 7.4495987,46.4674284,1 7.4493307,46.4679198,1 7.4492876,46.4681784,1 7.4496416,46.4692868,1 7.4496563,46.4697213,1 7.4494699,46.470107,1 7.4492607,46.4702844,1 7.4489711,46.4704322,1 7.4478714,46.470713,1 7.4475441,46.4708275,1 7.4473456,46.4709642,1 7.447056,46.4711489,1 7.4463582,46.4714157,1 7.4459884,46.471607,1 7.4456988,46.4718509,1 7.4452286,46.4724169,1 7.445114,46.4726674,1 7.4450765,46.4731809,1 7.4449746,46.4733841,1 7.4447178,46.4736469,1 7.4446098,46.4738533,1 7.4445584,46.4740966,1 7.4449598,46.4755818,1 7.4449197,46.4763244,1 7.4445384,46.4777695,1 7.4449211,46.4786051,1 7.4448411,46.4792847,1 7.4442814,46.4802442,1 7.4437217,46.4809239,1 7.4432419,46.4819633,1 7.4429621,46.4833226,1 7.443082,46.4843221,1 7.4434418,46.4851617,1 7.4433219,46.4860013,1 7.4416827,46.4874405,1 7.4412829,46.4883201,1 7.441083,46.4898393,1 7.441163,46.4919582,1 7.4409631,46.4925579,1 7.4410031,46.4940371,1 7.4414828,46.4941171,1 7.443162,46.4933975,1 7.4438816,46.4935174,1 7.4453209,46.4933175,1 7.4477318,46.4928481,1 7.4476808,46.4927546,1 7.4472302,46.4928149,1 7.4455921,46.4926496,1 7.4451878,46.4926864,1 7.4452666,46.4925236,1 7.4456341,46.4924028,1 7.4470149,46.4921981,1 7.447288,46.4921193,1 7.4472985,46.4920563,1 7.4467734,46.4919776,1 7.4455659,46.4916048,1 7.4455134,46.4915365,1 7.4457444,46.4910168,1 7.4454188,46.490728,1 7.4453663,46.4905652,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gutenbrunnenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.444827,46.4597038,1 7.4448524,46.4597347,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Wallenbachstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4418739,46.4612798,1 7.441721,46.461265,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schanzenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4448677,46.4575035,1 7.4448703,46.4570212,1 7.4451135,46.4568262,1 7.4455274,46.4564945,1 7.4460994,46.4560016,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4432434,46.4572082,1 7.4433858,46.4575296,1 7.4438332,46.4578509,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oertlistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4446688,46.4618747,1 7.4449263,46.4618784,1 7.4450513,46.4614534,1 7.4455535,46.4605947,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4464015,46.4610858,1 7.4459878,46.4615401,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4477077,46.4598442,1 7.4465974,46.459692,1 7.445441,46.4596274,1 7.444827,46.4597038,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Untere Haltenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4464578,46.4579817,1 7.4474714,46.4583086,1 7.4475806,46.4582904,1 7.4478352,46.4579994,1 7.4480413,46.457563,1 7.4481747,46.4576539,1 7.448593,46.4576963,1 7.4486688,46.4577077,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Untere Haltenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4478352,46.4579994,1 7.4483384,46.4580722,1 7.4486688,46.4577077,1 7.4487506,46.4576175,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oeystrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5535075,46.4866379,1 7.5541077,46.486711,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5705898,46.498823,1 7.5706468,46.4988918,1 7.571328,46.499657,1 7.5715737,46.4997902,1 7.571972,46.499918,1 7.5724383,46.5003898,1 7.57264,46.500482,1 7.572698,46.50071,1 7.573314,46.500878,1 7.573326,46.500982,1 7.5736,46.501081,1 7.573867,46.501362,1 7.574116,46.501755,1 7.574486,46.502791,1 7.575551,46.504242,1 7.575842,46.505145,1 7.575844,46.5053877,1 7.5758952,46.5056591,1 7.5759918,46.5060425,1 7.57603,46.506199,1 7.57625,46.506853,1 7.5763751,46.5070038,1 7.5765527,46.5070657,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.570559,46.498933,1 7.5706468,46.4988918,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.580669,46.513662,1 7.5807275,46.5139652,1 7.5807645,46.5144849,1 7.5810451,46.5147994,1 7.5814548,46.5149276,1 7.5815259,46.5149997,1 7.5814644,46.5151169,1 7.581154,46.515336,1 7.5808687,46.5154174,1 7.5804932,46.5153665,1 7.5796209,46.5152968,1 7.5795576,46.5153762,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796209,46.5152968,1 7.5791481,46.514688,1 7.57917,46.514448,1 7.578968,46.514106,1 7.578725,46.513934,1 7.577749,46.513746,1 7.57743,46.513533,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Mattenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5617403,46.4958305,1 7.5617231,46.4956946,1 7.561869,46.4956828,1 7.5631362,46.496001,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fichtenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5642551,46.4979457,1 7.5637573,46.4971953,1 7.5633291,46.4970595,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hörnliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.563427,46.498168,1 7.5634771,46.498246,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Obere Kurhausstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5594245,46.493615,1 7.5594073,46.4937805,1 7.559845,46.4940345,1 7.5599738,46.4943241,1 7.5608149,46.494649,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.556064,46.4904831,1 7.5562058,46.4901582,1 7.5560779,46.4899713,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Aebiweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.525197,46.4841121,1 7.5254535,46.4838699,1 7.5283631,46.4827706,1 7.5290927,46.4828534,1 7.5296334,46.4826879,1 7.5306977,46.4826465,1 7.5316762,46.4823628,1 7.5330409,46.4820851,1 7.5332297,46.4820851,1 7.5339335,46.482351,1 7.5343284,46.4823096,1 7.5346202,46.4821914,1 7.5349549,46.4818014,1 7.5351352,46.4817541,1 7.5352897,46.4818014,1 7.5355986,46.4821501,1 7.5359591,46.4821973,1 7.5371522,46.4817482,1 7.5372294,46.4812576,1 7.537032,46.4807671,1 7.5371007,46.4804361,1 7.5378217,46.4802115,1 7.5390599,46.4794675,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5568548,46.4922385,1 7.5564947,46.4921923,1 7.5563797,46.4920739,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schwendliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5563573,46.493575,1 7.5559968,46.4931141,1 7.5558595,46.4922159,1 7.555705,46.4920327,1 7.5556793,46.492015,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5426865,46.4886019,1 7.5426779,46.4886728,1 7.5428925,46.4887023,1 7.5430813,46.4884246,1 7.5433731,46.4882059,1 7.5439138,46.4880345,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5621068,46.4705513,1 7.5621068,46.4703326,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.563823,46.4661917,1 7.5640376,46.4659612,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5649892,46.4595141,1 7.5667928,46.4591384,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5649892,46.4595141,1 7.5639522,46.4598625,1 7.5627558,46.4606653,1 7.5624583,46.460924,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5573333,46.4922537,1 7.5572692,46.4923768,1 7.5575911,46.4927979,1 7.5576125,46.4928259,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Holzachseggenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.574161,46.5050942,1 7.5745177,46.5050132,1 7.5746686,46.5050133,1 7.5748119,46.5051096,1 7.5749181,46.5052865,1 7.5749613,46.5054369,1 7.5749935,46.5057891,1 7.5751992,46.5059527,1 7.5762188,46.5070028,1 7.5765527,46.5070657,1 7.5768093,46.5071724,1 7.5769754,46.5073393,1 7.5770131,46.5075288,1 7.5769805,46.5077763,1 7.5771975,46.508068,1 7.5774508,46.5082856,1 7.5776375,46.5085748,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Holzachseggenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.580641,46.513675,1 7.580404,46.513551,1 7.5802043,46.5134503,1 7.5797717,46.5127021,1 7.579667,46.512463,1 7.5796377,46.5123126,1 7.5796306,46.5118712,1 7.5795023,46.511653,1 7.579245,46.511192,1 7.578725,46.510501,1 7.57876,46.510111,1 7.5785213,46.5099119,1 7.5783897,46.5097234,1 7.5784323,46.509327,1 7.5782946,46.5090724,1 7.5779976,46.5089351,1 7.5777993,46.5087052,1 7.5776375,46.5085748,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6170304,46.559005,1 7.617521,46.559201,1 7.61769,46.559157,1 7.618127,46.558916,1 7.618334,46.558972,1 7.618372,46.559119,1 7.6185053,46.5592427,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Badstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4360929,46.4545316,1 7.4354269,46.4543073,1 7.4353973,46.4541646,1 7.4355302,46.454095,1 7.4358702,46.4539509,1 7.4367144,46.4534407,1 7.4375083,46.4531784,1 7.4376245,46.45314,1 7.4379057,46.4527118,1 7.4380056,46.4525884,1 7.4382313,46.4524161,1 7.4385957,46.4522631,1 7.4391746,46.4521405,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4677582,46.4299748,1 7.4681778,46.429499,1 7.4698944,46.4283987,1 7.4703192,46.4282113,1 7.4717398,46.4279136,1 7.4726925,46.4278012,1 7.4760571,46.4270912,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4391746,46.4521405,1 7.4395197,46.4518404,1 7.4397686,46.4510539,1 7.4414767,46.4497766,1 7.4416701,46.4495646,1 7.4418257,46.4493133,1 7.4418875,46.4490852,1 7.4419222,46.4486812,1 7.4419812,46.448478,1 7.4421475,46.448271,1 7.442228,46.4480196,1 7.4422977,46.4478348,1 7.4425445,46.4476463,1 7.4426679,46.447273,1 7.4428288,46.4470808,1 7.4431238,46.4469588,1 7.4434028,46.4469071,1 7.4436335,46.4469108,1 7.4442576,46.447074,1 7.4454113,46.4464497,1 7.4456129,46.4462972,1 7.4457363,46.4460976,1 7.4463318,46.444693,1 7.4470935,46.4432588,1 7.4476943,46.4419132,1 7.4477681,46.4417216,1 7.4477912,46.4411924,1 7.4478338,46.4409595,1 7.4479572,46.440808,1 7.4493703,46.4398371,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schulstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4451624,46.4562853,1 7.445156,46.4561345,1 7.4435718,46.4557441,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schanzenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4442759,46.457382,1 7.4444358,46.4572544,1 7.4448703,46.4570212,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rawilstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4391746,46.4521405,1 7.4392237,46.4523389,1 7.4393814,46.4528307,1 7.4394509,46.45341,1 7.4394643,46.4538143,1 7.439476,46.4543009,1 7.4395189,46.4545023,1 7.4396638,46.4549532,1 7.4397362,46.4551084,1 7.4398844,46.4554098,1 7.4401167,46.4559428,1 7.4402861,46.4562004,1 7.4405596,46.4564869,1 7.4409145,46.4567899,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischmattenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4441109,46.4543138,1 7.4439634,46.4542879,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schulstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4435718,46.4557441,1 7.4435047,46.4557418,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schulstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4434082,46.4557385,1 7.4433518,46.4557718,1 7.4433217,46.4560368,1 7.4432619,46.4563432,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rawilstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4428556,46.4571631,1 7.4430005,46.457189,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hohliebestrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4409145,46.4567899,1 7.4403726,46.4567837,1 7.4398295,46.4567497,1 7.4395506,46.4568162,1 7.4390892,46.4567793,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Badgässli</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4396638,46.4549532,1 7.4383275,46.4545323,1 7.4381129,46.4544954,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5815091,46.4898495,1 7.5817066,46.490105,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.579127,46.5015699,1 7.5795483,46.5015782,1 7.579604,46.501575,1 7.5796209,46.5015556,1 7.5796354,46.5015389,1 7.5796387,46.5014767,1 7.579516,46.5011987,1 7.579582,46.5009187,1 7.579558,46.5007779,1 7.5793455,46.5005247,1 7.5789309,46.500105,1 7.5789603,46.4999343,1 7.5792023,46.4997735,1 7.5792682,46.499649,1 7.5793383,46.4996065,1 7.579615,46.499543,1 7.5798441,46.4995124,1 7.5798959,46.4994904,1 7.5800692,46.4992295,1 7.5802311,46.4989017,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789513,46.5016989,1 7.579127,46.5015699,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6045066,46.4846583,1 7.6045315,46.4850165,1 7.6044123,46.4855701,1 7.6042768,46.486003,1 7.6045886,46.4873469,1 7.6048256,46.487741,1 7.6047148,46.4879113,1 7.6045687,46.4880471,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5966573,46.4922393,1 7.5971767,46.4928758,1 7.5974568,46.4931936,1 7.598244,46.493757,1 7.598438,46.494168,1 7.598278,46.494257,1 7.596752,46.494475,1 7.5956114,46.49432,1 7.595106,46.494366,1 7.594802,46.494659,1 7.593749,46.496477,1 7.593536,46.497015,1 7.593565,46.497162,1 7.593723,46.497259,1 7.594193,46.497251,1 7.594485,46.497334,1 7.595287,46.497681,1 7.595584,46.497731,1 7.596066,46.497622,1 7.596171,46.49767,1 7.5961782,46.4978242,1 7.596517,46.49794,1 7.597417,46.497891,1 7.597849,46.498019,1 7.59888,46.498738,1 7.600041,46.499325,1 7.600693,46.500122,1 7.600832,46.500508,1 7.601464,46.501098,1 7.601946,46.50242,1 7.602582,46.503422,1 7.6027655,46.503524,1 7.60291,46.503966,1 7.603441,46.504399,1 7.603526,46.504844,1 7.603912,46.505249,1 7.603992,46.50585,1 7.604118,46.506031,1 7.605462,46.506316,1 7.606866,46.506045,1 7.608604,46.50587,1 7.60891,46.506088,1 7.609026,46.506509,1 7.609263,46.506585,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6027655,46.503524,1 7.6032706,46.5032719,1 7.6039262,46.5029587,1 7.6040764,46.5028406,1 7.6040979,46.5026559,1 7.6040979,46.5025304,1 7.6041623,46.5023901,1 7.6043768,46.5022645,1 7.6048275,46.5020873,1 7.6050098,46.5020873,1 7.6051601,46.502139,1 7.6053317,46.5021242,1 7.6058252,46.5019839,1 7.6061686,46.5018214,1 7.606351,46.5016442,1 7.6065548,46.501371,1 7.6067372,46.5012454,1 7.6071234,46.5011199,1 7.6080139,46.5007949,1 7.6083572,46.5007285,1 7.6087864,46.5006915,1 7.6091083,46.5006989,1 7.6092799,46.5006694,1 7.6093014,46.5006029,1 7.609237,46.5005217,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.609263,46.506585,1 7.609625,46.506622,1 7.609763,46.506742,1 7.608437,46.508551,1 7.608362,46.509014,1 7.608517,46.509398,1 7.608975,46.509513,1 7.609143,46.509667,1 7.608922,46.509968,1 7.608936,46.510136,1 7.6096937,46.5108052,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6132752,46.5086326,1 7.6130864,46.508084,1 7.6134655,46.5075013,1 7.6137376,46.5073456,1 7.6137642,46.5068918,1 7.6136461,46.5067741,1 7.6132371,46.5067476,1 7.6123986,46.5065152,1 7.6121506,46.5065575,1 7.6117664,46.5069924,1 7.6118209,46.5074455,1 7.6111969,46.5077415,1 7.610704,46.508093,1 7.609919,46.508256,1 7.609829,46.508341,1 7.609937,46.509377,1 7.609669,46.509781,1 7.6097521,46.5101913,1 7.6096937,46.5108052,1 7.609751,46.511164,1 7.610325,46.511757,1 7.611032,46.512156,1 7.611504,46.512256,1 7.61164,46.512395,1 7.611676,46.512859,1 7.6110638,46.5135387,1 7.610088,46.513843,1 7.609616,46.514223,1 7.609231,46.514344,1 7.608915,46.51455,1 7.608729,46.51482,1 7.608422,46.515019,1 7.607633,46.515308,1 7.6073503,46.5155013,1 7.607241,46.515576,1 7.606768,46.51574,1 7.606403,46.515938,1 7.60625,46.516128,1 7.606479,46.51664,1 7.606585,46.517717,1 7.606807,46.518003,1 7.607364,46.518265,1 7.60721,46.51834,1 7.606852,46.51835,1 7.605442,46.51814,1 7.604229,46.517357,1 7.6033694,46.5171449,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6109659,46.5266158,1 7.6107,46.526694,1 7.61007,46.527171,1 7.609988,46.527489,1 7.609498,46.527984,1 7.608709,46.529361,1 7.608698,46.530009,1 7.608567,46.530339,1 7.608337,46.530401,1 7.608207,46.530327,1 7.607761,46.529968,1 7.607315,46.529449,1 7.606862,46.52918,1 7.6065623,46.5291566,1 7.6061361,46.5293018,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rinderwaldstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5881401,46.5275704,1 7.5879485,46.527459,1 7.587679,46.527221,1 7.587317,46.526255,1 7.587425,46.5259306,1 7.5872959,46.5256373,1 7.586076,46.524567,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5826066,46.5259242,1 7.5829568,46.5262864,1 7.5833805,46.5268825,1 7.5836909,46.5270954,1 7.5838082,46.5273334,1 7.5840578,46.5276561,1 7.584423,46.5281035,1 7.584751,46.528836,1 7.5847782,46.5294052,1 7.5847345,46.5296666,1 7.5845956,46.5298968,1 7.5842076,46.5301916,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rinderwaldstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5774574,46.5275452,1 7.5772166,46.5274306,1 7.5766573,46.5267791,1 7.576019,46.525654,1 7.575555,46.524558,1 7.5753315,46.5243657,1 7.5749549,46.5242103,1 7.57448,46.5241341,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5745874,46.5254626,1 7.5721265,46.5258724,1 7.5710266,46.5262635,1 7.5703591,46.5265893,1 7.5704551,46.5266365,1 7.5708354,46.5265253,1 7.5721092,46.5264585,1 7.5735188,46.5265577,1 7.573599,46.526742,1 7.573578,46.527052,1 7.573646,46.5272475,1 7.5744837,46.5279216,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5703591,46.5265893,1 7.568595,46.5263443,1 7.5673853,46.5264725,1 7.5663447,46.5266784,1 7.5654095,46.5267497,1 7.564572,46.5270254,1 7.5632688,46.5279901,1 7.562625,46.528737,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5632688,46.5279901,1 7.563293,46.528047,1 7.5639,46.527924,1 7.565404,46.52747,1 7.565998,46.527405,1 7.5672493,46.5275054,1 7.568548,46.527754,1 7.5689,46.527947,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4348713,46.4607347,1 7.433706,46.46137,1 7.4332548,46.4615372,1 7.432767,46.461688,1 7.4322834,46.461897,1 7.430689,46.462586,1 7.430159,46.462852,1 7.429281,46.463456,1 7.427987,46.464066,1 7.427716,46.464332,1 7.427499,46.464333,1 7.427753,46.463371,1 7.427638,46.462469,1 7.427267,46.461642,1 7.426829,46.461129,1 7.426752,46.460286,1 7.426453,46.460009,1 7.426347,46.45962,1 7.4262468,46.4595065,1 7.425969,46.459287,1 7.42578,46.459278,1 7.42571,46.459377,1 7.425777,46.459664,1 7.425669,46.459887,1 7.425157,46.460384,1 7.425126,46.460757,1 7.424398,46.461387,1 7.424154,46.461895,1 7.423971,46.461934,1 7.423801,46.461447,1 7.423465,46.460931,1 7.423449,46.460419,1 7.423278,46.460279,1 7.422565,46.460132,1 7.422303,46.459911,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5426103,46.4830368,1 7.5424403,46.4825688,1 7.5421893,46.4825118,1 7.5419623,46.4825828,1 7.5416513,46.4828808,1 7.5415101,46.4829137,1 7.5411413,46.4827908,1 7.5402763,46.4830068,1 7.5400713,46.4829268,1 7.5403793,46.4826278,1 7.5407663,46.4824648,1 7.5407743,46.4822388,1 7.5405388,46.4820442,1 7.5403653,46.4819008,1 7.5396433,46.4815648,1 7.5392734,46.4802349,1 7.5390599,46.4794675,1 7.5381653,46.4784568,1 7.5376503,46.4775028,1 7.5369473,46.4771058,1 7.5367163,46.4767798,1 7.5365533,46.4764458,1 7.5365043,46.4756498,1 7.5361633,46.4746408,1 7.5353448,46.4737451,1 7.5352429,46.4735013,1 7.5351743,46.4725458,1 7.5350093,46.4718738,1 7.5351203,46.4712998,1 7.5350193,46.4709858,1 7.5346923,46.4703668,1 7.5339183,46.4698158,1 7.5332503,46.4690168,1 7.5332913,46.4687698,1 7.5345263,46.4679968,1 7.5346883,46.4676188,1 7.5346943,46.4673158,1 7.5334473,46.4653948,1 7.5333563,46.4649828,1 7.5340553,46.4636388,1 7.5345393,46.4632248,1 7.5344513,46.4619618,1 7.5347283,46.4613518,1 7.5346943,46.4610708,1 7.5348653,46.4608468,1 7.5348533,46.4606638,1 7.5345733,46.4604178,1 7.5338453,46.4601288,1 7.5336253,46.4601698,1 7.5335303,46.4606138,1 7.5330443,46.4611158,1 7.5328583,46.4620328,1 7.5324423,46.4625308,1 7.5322843,46.4630188,1 7.5318629,46.4633692,1 7.5310273,46.4640638,1 7.5303233,46.4642378,1 7.5296383,46.4642278,1 7.5291173,46.4643608,1 7.5287983,46.4643358,1 7.5274243,46.4636628,1 7.5262593,46.4635928,1 7.5256373,46.4634048,1 7.5241943,46.4624008,1 7.5232133,46.4614168,1 7.5214053,46.4606918,1 7.5210933,46.4601898,1 7.5200473,46.4595938,1 7.5191943,46.4593388,1 7.5174423,46.4592718,1 7.5170503,46.4591218,1 7.5160353,46.4580808,1 7.5156573,46.4578858,1 7.5148593,46.4576658,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.469627,46.453826,1 7.4703038,46.4532183,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alpenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.55887,46.49044,1 7.55864,46.490402,1 7.55846,46.49025,1 7.5579974,46.4904534,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.543024,46.4839871,1 7.5426103,46.4830368,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Dorfstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5595144,46.4930964,1 7.5592176,46.4928554,1 7.5587375,46.4925106,1 7.5581971,46.4920378,1 7.5571221,46.4911702,1 7.5568451,46.4909915,1 7.556403,46.4906599,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Dorfstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.556403,46.4906599,1 7.556064,46.4904831,1 7.5559303,46.4904006,1 7.5549954,46.4898085,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Risetensträssli</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5561408,46.4893354,1 7.5561531,46.4890823,1 7.5561565,46.4890135,1 7.556089,46.4888482,1 7.5559288,46.4884562,1 7.5555284,46.4878909,1 7.5553086,46.487412,1 7.5549867,46.4871765,1 7.5545078,46.4871844,1 7.5535075,46.4866379,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5981221,46.5280116,1 7.598181,46.528212,1 7.598159,46.528341,1 7.598092,46.528455,1 7.59791,46.528842,1 7.597834,46.52896,1 7.597782,46.529368,1 7.597859,46.5296,1 7.598016,46.529719,1 7.59821,46.529793,1 7.598476,46.529826,1 7.59895,46.529716,1 7.599487,46.529729,1 7.5996702,46.5297785,1 7.599726,46.529845,1 7.599787,46.529949,1 7.599809,46.530111,1 7.599803,46.530257,1 7.599647,46.530568,1 7.599725,46.53097,1 7.599832,46.531108,1 7.599978,46.531232,1 7.599953,46.531488,1 7.600036,46.531632,1 7.60003,46.53168,1 7.599971,46.531811,1 7.600043,46.531956,1 7.600178,46.532082,1 7.600263,46.532197,1 7.600269,46.532322,1 7.600217,46.532436,1 7.600152,46.532503,1 7.600094,46.532638,1 7.600076,46.532737,1 7.600014,46.532905,1 7.59999,46.533077,1 7.59989,46.533416,1 7.599934,46.533522,1 7.6001086,46.5335279,1 7.600186,46.5335,1 7.60064,46.533445,1 7.600763,46.53347,1 7.600892,46.53355,1 7.600963,46.533707,1 7.600955,46.533781,1 7.600893,46.533879,1 7.600796,46.53395,1 7.600721,46.53409,1 7.600808,46.534403,1 7.600856,46.534454,1 7.60095,46.534503,1 7.601187,46.534596,1 7.60136,46.534735,1 7.601596,46.535069,1 7.601825,46.535338,1 7.601902,46.535416,1 7.602041,46.535499,1 7.602206,46.535578,1 7.602625,46.5357,1 7.60283,46.535772,1 7.603179,46.535845,1 7.603494,46.535859,1 7.60365,46.535898,1 7.603681,46.535918,1 7.603769,46.536014,1 7.603788,46.536143,1 7.603764,46.536199,1 7.603762,46.5362107,1 7.603755,46.536253,1 7.603784,46.536359,1 7.60391,46.536497,1 7.6040614,46.536598,1 7.604125,46.536642,1 7.604243,46.53682,1 7.604291,46.536947,1 7.604399,46.537031,1 7.604713,46.537069,1 7.604998,46.537061,1 7.605114,46.537042,1 7.605361,46.53706,1 7.605801,46.537134,1 7.606041,46.537151,1 7.606205,46.537248,1 7.606276,46.537331,1 7.6062753,46.5374013,1 7.606258,46.537464,1 7.606091,46.537705,1 7.605981,46.538053,1 7.606033,46.538161,1 7.60606,46.538286,1 7.606016,46.538455,1 7.605935,46.538601,1 7.605874,46.538805,1 7.605819,46.539136,1 7.605733,46.539318,1 7.605705,46.539412,1 7.6058389,46.539552,1 7.6058722,46.5395795,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5744837,46.5279216,1 7.5746977,46.5280938,1 7.5771625,46.5297081,1 7.5773,46.529821,1 7.577183,46.530712,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4568436,46.4593926,1 7.4568637,46.4594526,1 7.4569135,46.4595047,1 7.4570376,46.4595784,1 7.4576408,46.459836,1 7.4576957,46.4598904,1 7.4576821,46.4599445,1 7.4576328,46.4599825,1 7.4575461,46.4599848,1 7.4572033,46.4598226,1 7.4568717,46.459731,1 7.4564869,46.4597211,1 7.4563512,46.4596817,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4563512,46.4596817,1 7.4562979,46.4595824,1 7.4562834,46.4593869,1 7.4562147,46.4592473,1 7.4559877,46.4590046,1 7.4559252,46.4587348,1 7.4558864,46.4581792,1 7.456013,46.4574831,1 7.4561972,46.456936,1 7.4560344,46.4563522,1 7.4560192,46.4561245,1 7.4563026,46.4555022,1 7.456367,46.4551475,1 7.4564528,46.4549996,1 7.4565816,46.4549331,1 7.4565816,46.454837,1 7.4565616,46.4547349,1 7.4564528,46.4547261,1 7.4563455,46.4548222,1 7.4562919,46.4549257,1 7.4562003,46.4550467,1 7.4560237,46.4551992,1 7.4556696,46.4553175,1 7.4554658,46.455384,1 7.45152,46.46002,1 7.4516199,46.4601237,1 7.45256,46.4611,1 7.4512878,46.4622786,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rawilstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4409145,46.4567899,1 7.4410666,46.4568934,1 7.441257,46.4569858,1 7.4424533,46.457099,1 7.4428556,46.4571631,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Egernschwandweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5786446,46.518287,1 7.578694,46.518135,1 7.578724,46.517978,1 7.578695,46.517886,1 7.578512,46.517455,1 7.578309,46.5171813,1 7.577878,46.516652,1 7.577605,46.516534,1 7.577438,46.51649,1 7.577041,46.516159,1 7.576838,46.516078,1 7.576264,46.515795,1 7.575735,46.515618,1 7.575121,46.515397,1 7.574844,46.515369,1 7.574591,46.515424,1 7.574228,46.515451,1 7.573965,46.515492,1 7.57367,46.515483,1 7.57341,46.515411,1 7.573285,46.51521,1 7.573152,46.515082,1 7.572439,46.515021,1 7.572135,46.514946,1 7.57191,46.514848,1 7.571398,46.514879,1 7.571265,46.51496,1 7.57104,46.514972,1 7.570159,46.514819,1 7.569955,46.514753,1 7.569293,46.514586,1 7.568896,46.514458,1 7.568662,46.514359,1 7.5681,46.514075,1 7.567915,46.513952,1 7.567848,46.513922,1 7.5671835,46.5137512,1 7.566948,46.513674,1 7.566778,46.513566,1 7.566565,46.513493,1 7.566333,46.513447,1 7.566124,46.51337,1 7.565802,46.513217,1 7.565665,46.51313,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5669006,46.5111824,1 7.566536,46.5113136,1 7.566172,46.511362,1 7.565683,46.511382,1 7.565051,46.511454,1 7.564704,46.511647,1 7.564224,46.51188,1 7.564123,46.512021,1 7.563941,46.51213,1 7.563809,46.512144,1 7.563635,46.512083,1 7.563415,46.512069,1 7.563285,46.512081,1 7.563183,46.512048,1 7.562725,46.511852,1 7.562645,46.511873,1 7.562606,46.511942,1 7.562637,46.512081,1 7.5625296,46.5121876,1 7.562224,46.512308,1 7.562061,46.512386,1 7.561732,46.512565,1 7.561621,46.51265,1 7.561383,46.512768,1 7.561246,46.512881,1 7.560984,46.513026,1 7.560705,46.513132,1 7.56041,46.513256,1 7.560199,46.513334,1 7.559963,46.513393,1 7.55977,46.513485,1 7.5596756,46.5135221,1 7.55961,46.513548,1 7.558865,46.513684,1 7.5581263,46.5137586,1 7.557572,46.513713,1 7.556972,46.513559,1 7.556817,46.5135283,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.556817,46.5135283,1 7.5564776,46.5135931,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5564776,46.5135931,1 7.555653,46.5136131,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Egerlenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.555653,46.5136131,1 7.5547124,46.5135725,1 7.554273,46.513568,1 7.553829,46.513568,1 7.553433,46.513609,1 7.5530228,46.5136488,1 7.552596,46.513654,1 7.552093,46.513613,1 7.551338,46.513505,1 7.5509052,46.5134765,1 7.5505971,46.5134655,1 7.5499677,46.5136782,1 7.5495893,46.5137309,1 7.549533,46.513828,1 7.5496296,46.5138873,1 7.54985,46.513882,1 7.55001,46.513903,1 7.550029,46.51409,1 7.550048,46.514291,1 7.5501728,46.5144035,1 7.550758,46.514543,1 7.5511026,46.5145888,1 7.5516662,46.5146211,1 7.5520151,46.5146864,1 7.552829,46.51488,1 7.5532371,46.514966,1 7.5535135,46.5150716,1 7.553654,46.515094,1 7.5538209,46.5150318,1 7.5540833,46.5149891,1 7.5548384,46.5150455,1 7.5551189,46.5150839,1 7.5553614,46.5152012,1 7.555649,46.5153894,1 7.5558741,46.515512,1 7.5561416,46.5156,1 7.5566571,46.5157065,1 7.556798,46.515774,1 7.5568977,46.5159041,1 7.556998,46.516064,1 7.5571154,46.5161086,1 7.557837,46.516132,1 7.558461,46.516234,1 7.5586448,46.5162468,1 7.559683,46.516477,1 7.560001,46.516488,1 7.5602308,46.5165359,1 7.5607407,46.5165139,1 7.5612266,46.5166202,1 7.5616355,46.5167467,1 7.5620778,46.5167717,1 7.5627706,46.5169076,1 7.5627921,46.5169559,1 7.5626331,46.5169857,1 7.562377,46.5169559,1 7.5620266,46.5169932,1 7.5614422,46.5171315,1 7.561326,46.517138,1 7.561267,46.517166,1 7.561159,46.517176,1 7.5610746,46.5171656,1 7.560836,46.51723,1 7.5603559,46.5172698,1 7.5585206,46.517503,1 7.5572554,46.5177377,1 7.5568904,46.5178344,1 7.5564558,46.5178795,1 7.5558549,46.5178842,1 7.5554229,46.5178329,1 7.5548414,46.5178271,1 7.5543567,46.5178481,1 7.553933,46.5178613,1 7.5538109,46.5179008,1 7.553859,46.517973,1 7.5540552,46.5179693,1 7.554263,46.51801,1 7.5552842,46.5184778,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Egernschwandweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.565665,46.51313,1 7.5655319,46.5130257,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Egernschwandweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5655319,46.5130257,1 7.5654903,46.5129931,1 7.56538,46.512798,1 7.565474,46.512629,1 7.565684,46.512455,1 7.565875,46.512336,1 7.566545,46.512105,1 7.566632,46.512061,1 7.566984,46.512021,1 7.567198,46.511978,1 7.567389,46.511901,1 7.567571,46.511851,1 7.568036,46.511904,1 7.568265,46.511945,1 7.568517,46.512006,1 7.568808,46.512056,1 7.569195,46.512073,1 7.569429,46.512012,1 7.569807,46.511803,1 7.570015,46.511579,1 7.570141,46.511395,1 7.570386,46.511321,1 7.570945,46.511298,1 7.571259,46.511326,1 7.571494,46.511316,1 7.5717288,46.5112797,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4472186,46.4059651,1 7.4473377,46.4046987,1 7.447185,46.4043595,1 7.4464891,46.403864,1 7.4461202,46.4034297,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Iffigenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4461202,46.4034297,1 7.44569,46.4028394,1 7.4452036,46.402289,1 7.4440926,46.4015624,1 7.4434951,46.4012667,1 7.4427384,46.4009962,1 7.4411642,46.3998745,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Labrunnenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5848963,46.5385742,1 7.584971,46.538598,1 7.5850687,46.5385678,1 7.585274,46.538445,1 7.585513,46.538362,1 7.585815,46.538336,1 7.5861975,46.5384377,1 7.586687,46.538565,1 7.586868,46.53855,1 7.586922,46.5385852,1 7.586858,46.538634,1 7.5865846,46.5387579,1 7.5864754,46.5388895,1 7.5863858,46.5390299,1 7.5863538,46.53914,1 7.586145,46.539473,1 7.585906,46.539748,1 7.5859743,46.5397566,1 7.5862337,46.5397277,1 7.5864971,46.5397002,1 7.5866758,46.539656,1 7.5869357,46.5396259,1 7.5872481,46.5395086,1 7.5874748,46.5393658,1 7.587549,46.5393914,1 7.5875686,46.5394438,1 7.587519,46.539663,1 7.5874201,46.539882,1 7.5870777,46.5403479,1 7.586974,46.540457,1 7.5870076,46.5404872,1 7.5870655,46.5404979,1 7.588135,46.540364,1 7.5885285,46.5402242,1 7.5886063,46.5402073,1 7.5886288,46.5402472,1 7.588604,46.5402947,1 7.5885094,46.540414,1 7.5883955,46.5406535,1 7.5883744,46.5407232,1 7.5883676,46.5408147,1 7.588401,46.540845,1 7.5885722,46.5409102,1 7.588615,46.5409026,1 7.5888202,46.5408269,1 7.589194,46.540661,1 7.589281,46.540632,1 7.58999,46.540264,1 7.590328,46.540011,1 7.590683,46.539741,1 7.590905,46.539618,1 7.591382,46.539386,1 7.591737,46.539145,1 7.5920484,46.538841,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6000148,46.5411065,1 7.5998328,46.5411104,1 7.599728,46.540985,1 7.599815,46.540729,1 7.599744,46.54068,1 7.599461,46.541161,1 7.5990539,46.5414724,1 7.5989053,46.5416069,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Linterstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5925397,46.5451903,1 7.592485,46.545252,1 7.592525,46.54534,1 7.592614,46.545474,1 7.592697,46.545941,1 7.592776,46.546324,1 7.592795,46.546568,1 7.592784,46.547071,1 7.592699,46.547674,1 7.592756,46.547908,1 7.592822,46.548097,1 7.592806,46.5481874,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Linterstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592806,46.5481874,1 7.592761,46.548238,1 7.592644,46.548376,1 7.592455,46.548557,1 7.592255,46.548698,1 7.592146,46.548812,1 7.591958,46.548924,1 7.591674,46.548987,1 7.591507,46.549084,1 7.591392,46.549196,1 7.591209,46.549305,1 7.591037,46.549435,1 7.590866,46.549519,1 7.590794,46.549642,1 7.590617,46.549749,1 7.590437,46.549933,1 7.5904257,46.5500262,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Linterstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5904257,46.5500262,1 7.5905254,46.5500717,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kratzernweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5905254,46.5500717,1 7.590707,46.549997,1 7.59085,46.549908,1 7.591069,46.549881,1 7.591421,46.549938,1 7.591668,46.550065,1 7.592004,46.550226,1 7.592637,46.550442,1 7.593226,46.55058,1 7.593383,46.550702,1 7.593531,46.550877,1 7.593684,46.55115,1 7.593958,46.551669,1 7.594276,46.55222,1 7.594324,46.552364,1 7.5943061,46.5527315,1 7.594134,46.552894,1 7.594091,46.55297,1 7.59395,46.553062,1 7.593757,46.553112,1 7.593606,46.553197,1 7.59307,46.5535336,1 7.5929968,46.5537118,1 7.5925956,46.5539528,1 7.5923612,46.5541928,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kratzernweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592429,46.554253,1 7.592863,46.5539574,1 7.593574,46.55385,1 7.59392,46.553942,1 7.594217,46.553964,1 7.594676,46.553977,1 7.594954,46.553786,1 7.595205,46.553817,1 7.595561,46.55408,1 7.595675,46.554108,1 7.59587,46.554013,1 7.596056,46.553807,1 7.596398,46.553526,1 7.59646,46.553509,1 7.5967647,46.5536546,1 7.596819,46.553667,1 7.5969261,46.5536743,1 7.5970949,46.5537103,1 7.5971441,46.5537263,1 7.5971816,46.5538848,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kratzernweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5923612,46.5541928,1 7.592429,46.554253,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.598828,46.5418043,1 7.5986652,46.5418556,1 7.5984839,46.5418271,1 7.5981032,46.541696,1 7.5978471,46.5416201,1 7.5977275,46.5416254,1 7.5978138,46.5416964,1 7.5979877,46.5417862,1 7.5981191,46.5418557,1 7.5980869,46.5419247,1 7.597906,46.5419125,1 7.5977139,46.5418563,1 7.5974461,46.5417375,1 7.5971628,46.5416562,1 7.596846,46.5416131,1 7.5964607,46.5415809,1 7.5962965,46.5415976,1 7.5963199,46.5416784,1 7.5964588,46.541693,1 7.596682,46.5416906,1 7.597639,46.542105,1 7.5979774,46.542212,1 7.5983329,46.5423104,1 7.5987195,46.5423762,1 7.598925,46.5423612,1 7.5991482,46.5423019,1 7.5994659,46.5421858,1 7.5996704,46.5420792,1 7.5998137,46.5420664,1 7.5998714,46.542131,1 7.5998295,46.5422306,1 7.5994128,46.5425684,1 7.599269,46.542718,1 7.5992063,46.5428643,1 7.5992312,46.5430102,1 7.5993576,46.5432551,1 7.5993203,46.543322,1 7.5991709,46.543321,1 7.598775,46.5432615,1 7.5985495,46.5431872,1 7.598132,46.5430061,1 7.5978407,46.542964,1 7.597512,46.542934,1 7.597277,46.542875,1 7.5969961,46.542788,1 7.5968514,46.5427994,1 7.5965859,46.5429628,1 7.5963745,46.5430484,1 7.5961846,46.5430552,1 7.5959447,46.542993,1 7.595764,46.542913,1 7.5955707,46.5427996,1 7.595428,46.5427595,1 7.5953594,46.5428276,1 7.595518,46.542962,1 7.5957387,46.5432782,1 7.595972,46.543449,1 7.5959446,46.5435388,1 7.5957942,46.5435186,1 7.5956638,46.5434226,1 7.5952803,46.5432314,1 7.5950789,46.5431496,1 7.5947568,46.5431029,1 7.5942665,46.5430803,1 7.593782,46.543173,1 7.593057,46.543361,1 7.5926799,46.5434775,1 7.592416,46.54366,1 7.5922365,46.5437425,1 7.592001,46.5437709,1 7.591858,46.543718,1 7.591699,46.543739,1 7.5916485,46.5437996,1 7.5917,46.5438532,1 7.5919348,46.5438772,1 7.5923353,46.5438815,1 7.5925324,46.5438545,1 7.592877,46.543856,1 7.5930756,46.5438769,1 7.5935319,46.5440157,1 7.593698,46.544084,1 7.593821,46.544181,1 7.5939285,46.544299,1 7.594035,46.544772,1 7.594137,46.5451511,1 7.593934,46.545935,1 7.5937548,46.5466349,1 7.5937849,46.5467479,1 7.593708,46.546875,1 7.5936038,46.546858,1 7.5935586,46.5467893,1 7.593523,46.546572,1 7.5933226,46.546083,1 7.5931368,46.5457079,1 7.5929462,46.5455571,1 7.592638,46.545243,1 7.5925397,46.5451903,1 7.592354,46.545117,1 7.5922372,46.5451099,1 7.5920883,46.5451263,1 7.5916217,46.545168,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5989053,46.5416069,1 7.598828,46.5418043,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6001666,46.5412566,1 7.6000721,46.5411632,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fehrmelthal</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4233856,46.4967518,1 7.4241495,46.4971123,1 7.4248619,46.4973545,1 7.4252291,46.4974725,1 7.4255929,46.4976732,1 7.4258661,46.4978981,1 7.426026,46.4981971,1 7.4261976,46.498398,1 7.4261976,46.4985752,1 7.4261376,46.498652,1 7.4256827,46.4984452,1 7.425305,46.4982798,1 7.4250743,46.4980211,1 7.4247643,46.4980198,1 7.4245669,46.4981203,1 7.4236742,46.4992133,1 7.4231077,46.4998042,1 7.4224039,46.5003654,1 7.4216744,46.5005013,1 7.4215885,46.5006195,1 7.4218718,46.5006904,1 7.4223009,46.500584,1 7.4228331,46.5004659,1 7.4235197,46.5004245,1 7.4238802,46.5002473,1 7.424481,46.5001468,1 7.4254509,46.5001468,1 7.4261603,46.5002229,1 7.4282833,46.5011512,1 7.4290987,46.5016239,1 7.4291416,46.502061,1 7.429442,46.5022974,1 7.4296137,46.5026105,1 7.429485,46.5028882,1 7.4299399,46.5036798,1 7.4301973,46.5044124,1 7.4305407,46.5051685,1 7.4310127,46.5059838,1 7.4316565,46.5063796,1 7.4327723,46.5072834,1 7.4335619,46.5078742,1 7.4340769,46.5087248,1 7.4358793,46.5105442,1 7.4364201,46.5108514,1 7.437081,46.5115957,1 7.437656,46.5123636,1 7.4381968,46.5130133,1 7.4389092,46.5135863,1 7.4395701,46.5139407,1 7.440128,46.5147263,1 7.4407288,46.5151811,1 7.4410721,46.515435,1 7.4415012,46.515565,1 7.4420162,46.5157304,1 7.4425312,46.516067,1 7.4427458,46.5165691,1 7.4430462,46.517266,1 7.4434754,46.5180338,1 7.4439045,46.5187544,1 7.4443508,46.5192387,1 7.4448057,46.5195517,1 7.4466793,46.5199766,1 7.4473145,46.5201656,1 7.4480526,46.5203724,1 7.4485333,46.5206204,1 7.4486363,46.5206972,1 7.4499581,46.5209039,1 7.451709,46.5212878,1 7.4525931,46.5215063,1 7.4536316,46.5215949,1 7.4544213,46.5217366,1 7.4552538,46.5219079,1 7.4557087,46.521961,1 7.456361,46.5219669,1 7.457082,46.5219374,1 7.458318,46.5218193,1 7.4592707,46.5217071,1 7.4599917,46.521648,1 7.4611504,46.5215831,1 7.4620774,46.521524,1 7.4627039,46.5213882,1 7.4638884,46.520898,1 7.4646523,46.5206145,1 7.4650814,46.5204373,1 7.4655106,46.5203783,1 7.4659998,46.5204491,1 7.4665406,46.5204964,1 7.4671414,46.5205259,1 7.4681713,46.5204668,1 7.4689009,46.5204373,1 7.4695875,46.5203664,1 7.4704888,46.5202011,1 7.4723341,46.5198349,1 7.4743769,46.5193034,1 7.4745657,46.5192856,1 7.474952,46.5192679,1 7.4759819,46.5190671,1 7.4768402,46.5188545,1 7.4780419,46.5183407,1 7.4798872,46.5176142,1 7.4822047,46.5170708,1 7.4829342,46.5167637,1 7.4837925,46.5161376,1 7.4845221,46.51586,1 7.4854834,46.5161258,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4198288,46.4982833,1 7.4199867,46.4982171,1 7.4204588,46.4980104,1 7.4212742,46.4977031,1 7.4221153,46.4973486,1 7.4228878,46.497,1 7.4233856,46.4967518,1 7.4239729,46.4964435,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4206823,46.4955804,1 7.4200296,46.495446,1 7.4196538,46.4953841,1 7.4194202,46.4953456,1 7.4187164,46.4951801,1 7.4180555,46.4951506,1 7.4177448,46.4951104,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6045687,46.4880471,1 7.604255,46.4883112,1 7.6039469,46.4884404,1 7.6036512,46.4886227,1 7.603341,46.4887189,1 7.6032878,46.4889924,1 7.6021413,46.489726,1 7.6017531,46.4900467,1 7.6011402,46.490361,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5922214,46.488268,1 7.5922897,46.4884803,1 7.592399,46.488507,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.596404,46.4883944,1 7.5967553,46.4880553,1 7.5966803,46.4878323,1 7.5963163,46.4876073,1 7.5963243,46.4872963,1 7.5964903,46.4875193,1 7.5966533,46.4875613,1 7.5969273,46.4874763,1 7.5971273,46.4876053,1 7.5975163,46.4876923,1 7.5975213,46.4878273,1 7.5977823,46.4880603,1 7.5979919,46.4884754,1 7.5979589,46.4885316,1 7.5978879,46.4887234,1 7.5972663,46.4893073,1 7.5965123,46.4896783,1 7.5951997,46.4905833,1 7.5944647,46.4907653,1 7.5942015,46.4909197,1 7.5938325,46.4911889,1 7.5935377,46.4913093,1 7.5929597,46.4912713,1 7.5924147,46.4913903,1 7.5922067,46.4915203,1 7.5921687,46.4916743,1 7.5922017,46.4919647,1 7.5922817,46.4920763,1 7.5923972,46.492067,1 7.5928397,46.4918383,1 7.5931657,46.4917383,1 7.5940527,46.4917123,1 7.5947097,46.4919123,1 7.5966573,46.4922393,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6011402,46.490361,1 7.6010393,46.4904099,1 7.6003528,46.4908825,1 7.5998973,46.4910808,1 7.599409,46.491188,1 7.5966772,46.4913521,1 7.5964583,46.4914338,1 7.5961988,46.4916268,1 7.5961708,46.4917495,1 7.596319,46.491996,1 7.5966573,46.4922393,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.419988,46.5053819,1 7.4200223,46.5055355,1 7.4199966,46.5057068,1 7.4199193,46.5058604,1 7.4197734,46.5060258,1 7.4195073,46.5061144,1 7.4191898,46.5062503,1 7.4189323,46.5064157,1 7.4188121,46.5066638,1 7.4188722,46.5068706,1 7.4189666,46.5070951,1 7.4189923,46.5073491,1 7.4190782,46.5075618,1 7.4191297,46.5077803,1 7.4191039,46.5079989,1 7.4190267,46.5082057,1 7.4189494,46.508377,1 7.4189139,46.5084718,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4230607,46.5136342,1 7.4228547,46.5134865,1 7.4226401,46.5132798,1 7.4222882,46.5131026,1 7.4218248,46.5129963,1 7.4211982,46.51289,1 7.4205545,46.5128014,1 7.4202283,46.5126596,1 7.4199365,46.5124765,1 7.4197476,46.5123761,1 7.4193185,46.5123465,1 7.4191211,46.512317,1 7.4190009,46.5121811,1 7.419061,46.5119862,1 7.4192327,46.5118444,1 7.4195932,46.5117086,1 7.4199966,46.5115314,1 7.4204085,46.5113483,1 7.4207175,46.511236,1 7.4210179,46.5111356,1 7.421387,46.5109879,1 7.4218162,46.5108107,1 7.4221595,46.5106512,1 7.4224599,46.5105331,1 7.4226058,46.5104445,1 7.4226144,46.5103795,1 7.4225286,46.5103145,1 7.4223998,46.5102791,1 7.4221423,46.5103086,1 7.4216788,46.510409,1 7.4212239,46.5104386,1 7.4208549,46.5104386,1 7.4205974,46.5105744,1 7.4202197,46.5107694,1 7.4198249,46.5109111,1 7.4193872,46.5109702,1 7.4189408,46.5109407,1 7.4185289,46.510917,1 7.4180997,46.5109407,1 7.4177478,46.5109466,1 7.4175418,46.5109879,1 7.4172929,46.5111238,1 7.4170869,46.511236,1 7.4167779,46.5113187,1 7.4164861,46.5114014,1 7.4162715,46.5114841,1 7.4162114,46.5115196,1 7.4161084,46.5115196,1 7.4161256,46.5114605,1 7.41622,46.5113896,1 7.4164775,46.5113187,1 7.4167436,46.5112301,1 7.4169496,46.5111179,1 7.417044,46.5109584,1 7.4170869,46.5107989,1 7.4171813,46.5106571,1 7.4175075,46.510474,1 7.4178079,46.5102791,1 7.4181855,46.5100133,1 7.4184859,46.5097829,1 7.4187778,46.5096411,1 7.4190438,46.509523,1 7.4193786,46.509393,1 7.4195331,46.5092985,1 7.4196017,46.5091567,1 7.4195588,46.5090268,1 7.4194472,46.5088791,1 7.4192584,46.5087078,1 7.4191125,46.5085542,1 7.4189139,46.5084718,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4259103,46.506333,1 7.4256528,46.5063212,1 7.4254125,46.506268,1 7.4251121,46.5061794,1 7.4249576,46.5060613,1 7.4248288,46.5059254,1 7.4247859,46.5057541,1 7.4246915,46.5056359,1 7.4243224,46.5055296,1 7.4236358,46.5053819,1 7.4232152,46.5052224,1 7.422829,46.5050452,1 7.4223827,46.5049211,1 7.4220737,46.5048798,1 7.4218248,46.5049034,1 7.421593,46.5049034,1 7.4213527,46.5049211,1 7.4210952,46.5050452,1 7.4208892,46.5051397,1 7.4206317,46.5052224,1 7.4203742,46.5052578,1 7.4202026,46.5052992,1 7.420091,46.5053583,1 7.4200137,46.505376,1 7.419988,46.5053819,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4305407,46.5051685,1 7.4303563,46.5050038,1 7.4298928,46.5045253,1 7.4297126,46.5043185,1 7.4294637,46.5041177,1 7.4290517,46.5038695,1 7.4288371,46.5036923,1 7.428468,46.5034324,1 7.4282106,46.5031192,1 7.4279016,46.5029479,1 7.4276012,46.5027766,1 7.4274982,46.5027411,1 7.4273866,46.5027589,1 7.4273351,46.5028179,1 7.4275153,46.5029302,1 7.427687,46.5030424,1 7.4278157,46.5031783,1 7.4279187,46.5032965,1 7.4279445,46.5033851,1 7.4279016,46.5034383,1 7.4277042,46.5034028,1 7.4274982,46.5032551,1 7.4272836,46.503137,1 7.4270433,46.5030424,1 7.4267085,46.5029834,1 7.4262193,46.5029538,1 7.4255841,46.5029774,1 7.4252494,46.5029834,1 7.4248117,46.5029006,1 7.4245113,46.5028947,1 7.4241336,46.5030306,1 7.4238933,46.503137,1 7.4237474,46.503196,1 7.4234212,46.5032256,1 7.4232925,46.5032551,1 7.4232324,46.5032906,1 7.4232238,46.5033378,1 7.423301,46.5033674,1 7.423404,46.5033851,1 7.4235757,46.5033733,1 7.4240649,46.503456,1 7.4244683,46.5035269,1 7.4247773,46.5036037,1 7.4250434,46.5037632,1 7.4254554,46.50397,1 7.4259103,46.5041354,1 7.4263223,46.5043126,1 7.4265369,46.5044485,1 7.4266828,46.5045253,1 7.4267858,46.504608,1 7.4269403,46.504673,1 7.4269832,46.5047084,1 7.426966,46.504738,1 7.4269231,46.5047439,1 7.4268544,46.5047202,1 7.42676,46.5046553,1 7.4266399,46.5045726,1 7.4264424,46.5044898,1 7.4261592,46.504413,1 7.4258502,46.5043422,1 7.4255326,46.5043008,1 7.4251207,46.5042949,1 7.4246228,46.5042949,1 7.4240649,46.5042949,1 7.4236014,46.5042831,1 7.4231551,46.5043008,1 7.4227775,46.5043126,1 7.4221166,46.5043599,1 7.4215243,46.504413,1 7.4211896,46.5045017,1 7.4209364,46.5046133,1 7.4207519,46.5048266,1 7.4206231,46.5049979,1 7.420357,46.5051338,1 7.4200738,46.5052401,1 7.419988,46.5053819,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4668276,46.4566405,1 7.466626,46.456393,1 7.46638,46.456395,1 7.465978,46.457075,1 7.46552,46.45722,1 7.464934,46.457247,1 7.464328,46.457064,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.464328,46.457064,1 7.46402,46.45673,1 7.46447,46.45557,1 7.4642956,46.4543861,1 7.464124,46.45378,1 7.4641132,46.4532257,1 7.4642966,46.4529589,1 7.4642849,46.4527822,1 7.46408,46.45269,1 7.4638128,46.4527083,1 7.4636626,46.4529744,1 7.4634575,46.4537658,1 7.4603495,46.4557922,1 7.4588078,46.4570244,1 7.4581153,46.4575795,1 7.4574404,46.4583035,1 7.4570682,46.4587555,1 7.4569587,46.4589788,1 7.4568436,46.4593926,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4703038,46.4532183,1 7.4710333,46.4527157,1 7.4713445,46.452553,1 7.4714196,46.4524422,1 7.4712908,46.4523978,1 7.4711192,46.4524274,1 7.4708774,46.4525547,1 7.4706364,46.4526048,1 7.4701321,46.4525826,1 7.4698401,46.452539,1 7.46948,46.4526,1 7.4683618,46.4533587,1 7.4678361,46.4538687,1 7.4676216,46.4543418,1 7.4674922,46.4547505,1 7.4675572,46.4549109,1 7.4675679,46.4551105,1 7.4675467,46.4553607,1 7.4675786,46.4555096,1 7.4676859,46.4556722,1 7.4676573,46.4558604,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4418257,46.4493133,1 7.4429563,46.4496699,1 7.4449209,46.4532605,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Eselmoosgassa</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5352429,46.4735013,1 7.5361986,46.4737653,1 7.5368105,46.4739849,1 7.5370959,46.4740236,1 7.5375596,46.4740753,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Eselmoosgassa</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5375596,46.4740753,1 7.5377988,46.4741219,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.54748,46.480984,1 7.5484605,46.4809685,1 7.5488016,46.4810009,1 7.5501316,46.4812134,1 7.5504155,46.4813022,1 7.5507216,46.4814522,1 7.5509021,46.4815181,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kuonisbergliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5518939,46.4844236,1 7.5519996,46.4841301,1 7.5523714,46.4835666,1 7.5525084,46.4833243,1 7.5528638,46.4823913,1 7.5529761,46.4821915,1 7.553538,46.481365,1 7.553631,46.481207,1 7.553657,46.480986,1 7.553549,46.480827,1 7.553375,46.480816,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4243085,46.4962111,1 7.4246201,46.4963695,1 7.4248078,46.4963991,1 7.4249741,46.4962993,1 7.4251565,46.4960408,1 7.4254194,46.4957565,1 7.4257949,46.4953466,1 7.4266078,46.4947785,1 7.4271663,46.49439,1 7.4277734,46.4936979,1 7.4282348,46.4930423,1 7.4286597,46.4925445,1 7.4289438,46.4921742,1 7.4290296,46.4920487,1 7.4291818,46.4920224,1 7.4292764,46.492141,1 7.4290968,46.4923259,1 7.4288097,46.4927134,1 7.4286112,46.4933701,1 7.4285262,46.4938679,1 7.4280041,46.4948028,1 7.4276399,46.4955677,1 7.4277006,46.495762,1 7.4281134,46.4963083,1 7.4286476,46.496709,1 7.4288783,46.4970125,1 7.4293882,46.4980203,1 7.4293761,46.4985181,1 7.4294611,46.4987609,1 7.4296918,46.4989795,1 7.4304297,46.4992462,1 7.4305531,46.4993902,1 7.4305052,46.4995865,1 7.4306388,46.4997079,1 7.4308643,46.4998038,1 7.4310681,46.4999256,1 7.4316421,46.5003946,1 7.431744,46.5004204,1 7.4318772,46.5004,1 7.4318942,46.5002986,1 7.4317977,46.5001841,1 7.4314523,46.4998658,1 7.4313417,46.4996339,1 7.4313794,46.4994165,1 7.431508,46.4991502,1 7.4317801,46.498943,1 7.4317073,46.4988095,1 7.4312944,46.4986395,1 7.4311609,46.4982752,1 7.4310896,46.4978651,1 7.4311808,46.4975955,1 7.4313256,46.4972706,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.603904,46.5152803,1 7.6038399,46.515509,1 7.6038519,46.5158038,1 7.6039341,46.5160746,1 7.6039201,46.5163173,1 7.6038358,46.5164998,1 7.6036794,46.5167405,1 7.6033694,46.5171449,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4398617,46.4296989,1 7.4397759,46.4296619,1 7.4397115,46.4295362,1 7.4396686,46.4293809,1 7.4395291,46.429233,1 7.4391643,46.4289742,1 7.4389927,46.4289077,1 7.4388961,46.4289594,1 7.4388317,46.4293292,1 7.4387459,46.4294623,1 7.4386064,46.4294845,1 7.4384562,46.4293957,1 7.4383489,46.4292404,1 7.4382094,46.4290334,1 7.4381343,46.4289151,1 7.4374584,46.4288263,1 7.436922,46.4288337,1 7.4367181,46.4288263,1 7.4361388,46.428708,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Glauistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4444107,46.4389564,1 7.4442498,46.4390377,1 7.444121,46.43923,1 7.4434022,46.4402429,1 7.4433056,46.4402724,1 7.4432413,46.4402724,1 7.4431769,46.4401911,1 7.4431876,46.4401172,1 7.4432305,46.4398806,1 7.4432305,46.4396218,1 7.4432091,46.43923,1 7.4432091,46.4388751,1 7.4432413,46.4386237,1 7.4432413,46.4383945,1 7.4431769,46.4382096,1 7.4424903,46.4373076,1 7.44234,46.4372115,1 7.4415676,46.4367531,1 7.4414388,46.4366348,1 7.4413852,46.4364795,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Glauistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4424903,46.4373076,1 7.4423615,46.4369601,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Glauistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4433056,46.4402724,1 7.4431233,46.4408047,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Seefluhstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4434129,46.4428009,1 7.4433056,46.443311,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Seefluhstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4437348,46.4427639,1 7.4434129,46.4428009,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4380056,46.4525884,1 7.4378983,46.4525958,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4378983,46.4525958,1 7.4377374,46.4526105,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4377374,46.4526105,1 7.4376301,46.452714,1 7.437437,46.453054,1 7.437319,46.4530762,1 7.4372117,46.4529875,1 7.4371366,46.4528249,1 7.4371151,46.4527066,1 7.4369756,46.4526253,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Untere Bühlstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.433596,46.4585605,1 7.433639,46.4585162,1 7.4338965,46.4585383,1 7.4342612,46.4585088,1 7.4344544,46.4584496,1 7.4346367,46.4583166,1 7.4346904,46.4581244,1 7.4346689,46.4579249,1 7.4344865,46.4577032,1 7.4344651,46.4575406,1 7.4345295,46.4574371,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rütistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4304096,46.4586862,1 7.4302379,46.4586344,1 7.4301962,46.4583535,1 7.4301362,46.4577031,1 7.430222,46.4569817,1 7.4301104,46.456284,1 7.4302821,46.4559884,1 7.4308571,46.4556691,1 7.4291826,46.4541298,1 7.427084,46.4535514,1 7.4274775,46.4522141,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4353588,46.4606376,1 7.4352054,46.4610438,1 7.4349693,46.4614355,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.430159,46.462852,1 7.4301306,46.4627805,1 7.4301306,46.4625736,1 7.430195,46.4620637,1 7.4302272,46.4618346,1 7.4301521,46.4614724,1 7.4300233,46.4613172,1 7.4297015,46.4611029,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Flöschstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.430159,46.462852,1 7.4302272,46.4629801,1 7.4302808,46.4631574,1 7.4303452,46.4642216,1 7.430313,46.4646429,1 7.4302379,46.4654336,1 7.4301199,46.4663278,1 7.4299697,46.4668007,1 7.4296908,46.4675249,1 7.4294547,46.4679831,1 7.4292187,46.468589,1 7.4291066,46.4688619,1 7.4290537,46.4691138,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4290537,46.4691138,1 7.4286822,46.4691654,1 7.4284659,46.4692383,1 7.4281887,46.4695127,1 7.4278132,46.4699265,1 7.4276201,46.4700817,1 7.4271158,46.4705176,1 7.4268262,46.4707615,1 7.4268047,46.4708649,1 7.4269334,46.4710275,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Obere Ägertenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4302379,46.4654336,1 7.430592,46.4651602,1 7.4308602,46.4648646,1 7.4309997,46.4646133,1 7.4313859,46.4639999,1 7.4317721,46.4632313,1 7.4320082,46.4627436,1 7.4321155,46.4624849,1 7.4322834,46.461897,1 7.4323193,46.461199,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Sagistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4351088,46.4673623,1 7.4358872,46.4676242,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4545769,46.4439833,1 7.4547271,46.4439241,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4547271,46.4439241,1 7.4548183,46.4438761,1 7.454876,46.443826,1 7.4550079,46.4436536,1 7.4551627,46.4430606,1 7.4553113,46.4426596,1 7.4554721,46.4423588,1 7.4560542,46.4417253,1 7.4562979,46.441431,1 7.45647,46.441134,1 7.4564938,46.441,1 7.4565824,46.4408413,1 7.4566176,46.4407782,1 7.4572602,46.4399218,1 7.4576464,46.4396497,1 7.4579549,46.4395665,1 7.4587721,46.4394172,1 7.4590882,46.439328,1 7.4594231,46.4391233,1 7.4597503,46.438854,1 7.4600197,46.4385501,1 7.4606355,46.4376757,1 7.4607449,46.4375203,1 7.4612856,46.4369761,1 7.4623242,46.4356452,1 7.463195,46.434967,1 7.4633488,46.4348497,1 7.4636343,46.4345773,1 7.4640337,46.4340246,1 7.4642365,46.4337625,1 7.4644614,46.4335157,1 7.4653563,46.432764,1 7.4661983,46.4321939,1 7.4667084,46.4318423,1 7.4669016,46.4316887,1 7.4670482,46.4314854,1 7.467344,46.4306214,1 7.4675058,46.4302754,1 7.4677582,46.4299748,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4440966,46.4543426,1 7.4441109,46.4543138,1 7.4442933,46.4540773,1 7.444583,46.4537447,1 7.4447935,46.4534764,1 7.4449209,46.4532605,1 7.4449978,46.4530345,1 7.4450658,46.4527542,1 7.4451127,46.4523018,1 7.4451741,46.4520707,1 7.4453308,46.4518244,1 7.4460796,46.4511056,1 7.4469774,46.4504619,1 7.4478827,46.4500392,1 7.448556,46.4497567,1 7.4490141,46.4495254,1 7.4498329,46.4490688,1 7.4503311,46.4488049,1 7.450763,46.448663,1 7.451105,46.4485549,1 7.4513722,46.4483848,1 7.451504,46.4482133,1 7.4520636,46.4473548,1 7.4524759,46.4465754,1 7.4528298,46.4461337,1 7.4531239,46.4458511,1 7.4534257,46.4454192,1 7.4538049,46.4449126,1 7.4542979,46.4443603,1 7.4545085,46.4440271,1 7.4545769,46.4439833,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oberriedstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.444062,46.4544122,1 7.4440966,46.4543426,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischmattenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4439634,46.4542879,1 7.4438105,46.4542602,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischmattenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4438105,46.4542602,1 7.4433035,46.4541697,1 7.4407581,46.4538384,1 7.4394643,46.4538143,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rawilstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4430005,46.457189,1 7.4430622,46.4571982,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schulstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4435047,46.4557418,1 7.4434082,46.4557385,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gutenbrunnenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4448524,46.4597347,1 7.4449449,46.4598479,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bühlbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4476773,46.4598777,1 7.4476229,46.4599377,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bühlbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4476229,46.4599377,1 7.4475736,46.459992,1 7.4472866,46.4601065,1 7.4471632,46.4601564,1 7.4470908,46.4602063,1 7.4469806,46.460365,1 7.4468012,46.4607348,1 7.4466644,46.4609528,1 7.4465596,46.4612913,1 7.446482,46.4616641,1 7.4465494,46.4620495,1 7.4467601,46.4623722,1 7.4469595,46.462428,1 7.4470958,46.4624741,1 7.4471548,46.4625571,1 7.4471354,46.4626788,1 7.4470643,46.4628183,1 7.4470759,46.4629039,1 7.447149,46.4629496,1 7.4472496,46.4629448,1 7.4473107,46.4629016,1 7.4474659,46.4626716,1 7.4474787,46.462537,1 7.44739,46.4622619,1 7.44739,46.4621592,1 7.447435,46.4620685,1 7.4475389,46.4619867,1 7.4476454,46.4619468,1 7.4477672,46.4619562,1 7.4478306,46.4620086,1 7.4478473,46.4620985,1 7.4478115,46.4623099,1 7.447841,46.4624429,1 7.4481888,46.4626571,1 7.4488776,46.4629016,1 7.4494952,46.4630339,1 7.4499363,46.4629898,1 7.4504521,46.4631043,1 7.4507207,46.4630546,1 7.4509197,46.4629352,1 7.4508202,46.4626367,1 7.4509495,46.4623582,1 7.4512878,46.4622786,1 7.4516857,46.4623283,1 7.4536257,46.4631043,1 7.4547299,46.4630645,1 7.4552771,46.463363,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ägertenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4376101,46.4601022,1 7.4375109,46.4601364,1 7.4365881,46.4604558,1 7.4361085,46.4605709,1 7.4353588,46.4606376,1 7.4350766,46.4606669,1 7.4348713,46.4607347,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ägertenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4377581,46.4600511,1 7.4376101,46.4601022,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Sagistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4361018,46.4677055,1 7.4363271,46.4677904,1 7.4370442,46.4681471,1 7.4371615,46.468206,1 7.4384373,46.46873,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Sagistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4358872,46.4676242,1 7.4361018,46.4677055,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Mittlere Ägertenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4290537,46.4691138,1 7.4305276,46.4683451,1 7.4316423,46.467711,1 7.4319089,46.4675275,1 7.4327807,46.4664017,1 7.4335209,46.4654705,1 7.435674,46.4631469,1 7.4366858,46.462123,1 7.4374483,46.4612465,1 7.4377862,46.4608179,1 7.4378453,46.4606479,1 7.4378131,46.4605149,1 7.4377148,46.4603861,1 7.4375109,46.4601364,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Flöschstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4296586,46.4726679,1 7.4295084,46.4724019,1 7.429171,46.4719433,1 7.4290276,46.4717095,1 7.4289541,46.4714632,1 7.4289403,46.4708977,1 7.4290537,46.4691138,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schadaulistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4288706,46.4759627,1 7.4284414,46.475863,1 7.4282269,46.4757891,1 7.4280485,46.4756506,1 7.4279098,46.4754978,1 7.4278668,46.4752687,1 7.427899,46.4750249,1 7.4280814,46.4747663,1 7.4289342,46.4740116,1 7.4295727,46.4730964,1 7.4296586,46.4728821,1 7.4296586,46.4726679,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schadaulistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4292187,46.4760445,1 7.4288706,46.4759627,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4239729,46.4964435,1 7.4241194,46.4963413,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4241194,46.4963413,1 7.4243085,46.4962111,1 7.4245872,46.4960192,1 7.4248311,46.4957794,1 7.4250679,46.4955465,1 7.425394,46.4950502,1 7.425394,46.494607,1 7.4253855,46.4943529,1 7.4252825,46.4941166,1 7.4252996,46.4939629,1 7.4255571,46.493443,1 7.4257889,46.4929289,1 7.4260378,46.4923971,1 7.4263553,46.4919657,1 7.4267244,46.4915698,1 7.427042,46.4912389,1 7.4274368,46.4908607,1 7.4277973,46.4903348,1 7.4277544,46.4901043,1 7.4276085,46.489927,1 7.4273767,46.4897616,1 7.4272987,46.4897359,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4451135,46.4568262,1 7.4457293,46.4570504,1 7.4457346,46.4571022,1 7.4455791,46.4573572,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4310896,46.4978651,1 7.4312559,46.4980313,1 7.4313202,46.4981679,1 7.4314812,46.4983193,1 7.4317494,46.4984966,1 7.43246,46.4986638,1 7.43263,46.4987973,1 7.4326179,46.498943,1 7.4327757,46.4992223,1 7.4332371,46.4995258,1 7.4335304,46.4996856,1 7.4339273,46.4998112,1 7.4342385,46.4998038,1 7.4345241,46.4997444,1 7.4352577,46.4996228,1 7.4360463,46.4995231,1 7.4366203,46.4994493,1 7.437087,46.4993717,1 7.4374357,46.4992757,1 7.4382029,46.4989309,1 7.4388787,46.4984079,1 7.439281,46.4981605,1 7.4399999,46.4976842,1 7.4411371,46.4971856,1 7.4421611,46.4969154,1 7.4432902,46.4966361,1 7.4441358,46.4965578,1 7.4445489,46.4965357,1 7.4447957,46.4966686,1 7.44486,46.4968385,1 7.4447259,46.4969936,1 7.4440503,46.4973064,1 7.4432926,46.4976366,1 7.4426516,46.4981223,1 7.4415831,46.4989576,1 7.4414107,46.4992129,1 7.4411693,46.4999145,1 7.4410996,46.5000438,1 7.4410272,46.5001361,1 7.4410459,46.5001952,1 7.4411103,46.5002007,1 7.4411774,46.5001767,1 7.4412176,46.5000863,1 7.4413919,46.4998112,1 7.4420705,46.4994308,1 7.4424139,46.4991428,1 7.4427464,46.4989434,1 7.4429503,46.4988843,1 7.4433902,46.498936,1 7.4439213,46.4988566,1 7.4442726,46.4988215,1 7.4446347,46.4987292,1 7.4450317,46.4984338,1 7.4455681,46.4981974,1 7.446244,46.4979943,1 7.4467966,46.4977876,1 7.4471775,46.4977137,1 7.4473438,46.4976251,1 7.4475047,46.4976066,1 7.4475905,46.4976472,1 7.4475852,46.4977174,1 7.4474403,46.4977949,1 7.447274,46.4978873,1 7.4471131,46.4979537,1 7.4467912,46.4982048,1 7.4464372,46.4985372,1 7.4460724,46.4990542,1 7.4455896,46.4994973,1 7.4449888,46.4998665,1 7.4444631,46.5002949,1 7.4442592,46.500738,1 7.4439052,46.5014617,1 7.4434867,46.5020008,1 7.4434224,46.5025916,1 7.4432078,46.5032267,1 7.4428215,46.5038618,1 7.4426928,46.504268,1 7.4424246,46.5045708,1 7.4419525,46.5049474,1 7.441577,46.5052686,1 7.4414268,46.5053978,1 7.4413785,46.5054643,1 7.4414322,46.5055455,1 7.4415502,46.5055566,1 7.4416467,46.5054938,1 7.441754,46.5053831,1 7.4418238,46.5053018,1 7.4423602,46.5049621,1 7.4430576,46.5046298,1 7.4436799,46.5041498,1 7.4444631,46.5038618,1 7.4456647,46.5034483,1 7.4466839,46.5029387,1 7.4477461,46.5024366,1 7.4483898,46.5019344,1 7.4490014,46.5017276,1 7.4500421,46.5016168,1 7.4508387,46.5015134,1 7.4512035,46.50153,1 7.4520323,46.5016076,1 7.4526867,46.5016759,1 7.453191,46.5016574,1 7.4535665,46.5016316,1 7.4538723,46.5015467,1 7.4540976,46.5013953,1 7.4543336,46.5013436,1 7.4544892,46.5013436,1 7.4546286,46.5013934,1 7.4548137,46.5014728,1 7.4549773,46.5014839,1 7.4551919,46.5014765,1 7.4554279,46.5015245,1 7.4557122,46.501639,1 7.4559831,46.5017073,1 7.4562701,46.5017756,1 7.4565491,46.5017959,1 7.4568227,46.5018845,1 7.4571794,46.5020212,1 7.4576032,46.5021172,1 7.4580458,46.502215,1 7.4582952,46.502311,1 7.458322,46.5023535,1 7.4583113,46.5023996,1 7.4582174,46.502407,1 7.4580833,46.5023922,1 7.4579278,46.5024144,1 7.4577132,46.5025326,1 7.4570051,46.5028427,1 7.4564043,46.5030643,1 7.4557391,46.5032563,1 7.4549451,46.5035074,1 7.4543443,46.5036698,1 7.4538723,46.5038471,1 7.4533787,46.5040391,1 7.4530354,46.5042458,1 7.4527565,46.5044083,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592399,46.488507,1 7.59321,46.488437,1 7.59392,46.488552,1 7.594131,46.488536,1 7.5943718,46.4884212,1 7.5948158,46.4885033,1 7.5952399,46.4885619,1 7.5955708,46.4884936,1 7.5960104,46.4885348,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5960104,46.4885348,1 7.5962371,46.488494,1 7.596404,46.4883944,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5148593,46.4576658,1 7.5139753,46.4570098,1 7.5137473,46.4566738,1 7.5134953,46.4566158,1 7.5132313,46.4567988,1 7.5131163,46.4569628,1 7.5132733,46.4572948,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Diemtigtalstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5600746,46.6314618,1 7.5598156,46.6312398,1 7.559655,46.6309751,1 7.5593873,46.6307986,1 7.5590242,46.6307277,1 7.5582769,46.6304248,1 7.5576765,46.6305044,1 7.5572299,46.6304384,1 7.5555999,46.6298843,1 7.5555016,46.629737,1 7.5554714,46.6294791,1 7.5553783,46.6293059,1 7.5551891,46.629144,1 7.5549186,46.6289567,1 7.5544468,46.6288002,1 7.5541557,46.6287772,1 7.5537162,46.6287061,1 7.553227,46.6285376,1 7.5526808,46.6281825,1 7.5524817,46.6280484,1 7.5518999,46.6277116,1 7.5514352,46.6270945,1 7.5510642,46.6263117,1 7.5508466,46.626119,1 7.5508232,46.6259826,1 7.5506946,46.6258393,1 7.5504849,46.625715,1 7.5503553,46.6254358,1 7.550316,46.6251753,1 7.550244,46.6250835,1 7.5500438,46.6248793,1 7.5488614,46.6241043,1 7.5485721,46.6238921,1 7.548328,46.6237442,1 7.5480229,46.6235274,1 7.54797,46.6233309,1 7.5477086,46.6229447,1 7.547457,46.6226864,1 7.5470609,46.6224583,1 7.5457883,46.6215605,1 7.5455609,46.6214509,1 7.5453464,46.621419,1 7.5446782,46.6212488,1 7.5433859,46.62073,1 7.5429287,46.6205111,1 7.5421192,46.6198929,1 7.5418311,46.619571,1 7.5417298,46.6192503,1 7.5415818,46.6188457,1 7.541494,46.6184536,1 7.5414137,46.6182422,1 7.5413211,46.6181037,1 7.5411663,46.6178794,1 7.5410485,46.6177926,1 7.5406361,46.6174361,1 7.5401975,46.6169536,1 7.5396964,46.6164697,1 7.5391172,46.616091,1 7.5386223,46.615864,1 7.5380109,46.6151669,1 7.5378718,46.6149389,1 7.5376117,46.6147741,1 7.537236,46.6147098,1 7.5370075,46.6146143,1 7.5367774,46.6144043,1 7.5355882,46.6137959,1 7.5352713,46.6136639,1 7.5350754,46.6135533,1 7.5337253,46.6127512,1 7.5333154,46.6125519,1 7.5329988,46.6124922,1 7.5323089,46.612488,1 7.5318122,46.6125197,1 7.5315634,46.6124686,1 7.5314699,46.6123848,1 7.5314302,46.6121513,1 7.5313915,46.6119232,1 7.5312951,46.6117809,1 7.5309538,46.6115558,1 7.5305549,46.6113981,1 7.5300789,46.6112805,1 7.5296412,46.611198,1 7.5292201,46.6111837,1 7.5283316,46.611403,1 7.5276551,46.6115571,1 7.5272051,46.6115766,1 7.5261496,46.6114159,1 7.52564,46.6113005,1 7.5253453,46.6111977,1 7.5248067,46.6109049,1 7.5245846,46.6108356,1 7.5236745,46.6108032,1 7.5233471,46.6107245,1 7.5230431,46.6105063,1 7.5228115,46.6101505,1 7.5225994,46.609902,1 7.522408,46.6097115,1 7.5221107,46.6095417,1 7.5215979,46.6093977,1 7.5212467,46.6093381,1 7.5210101,46.6092757,1 7.5207618,46.6091481,1 7.5202006,46.6082884,1 7.519828,46.6078676,1 7.5194859,46.6075841,1 7.5190794,46.6074094,1 7.5184389,46.6073161,1 7.5169906,46.6071004,1 7.5157977,46.6070594,1 7.5153595,46.6069773,1 7.515053,46.6067635,1 7.5140954,46.6063115,1 7.513268,46.6063192,1 7.5117359,46.6057293,1 7.5105255,46.6048942,1 7.5097041,46.6044949,1 7.5096904,46.6044882,1 7.5089857,46.604289,1 7.5084647,46.6038754,1 7.5070322,46.6034157,1 7.506404,46.6027646,1 7.5055996,46.6024505,1 7.5043126,46.6011558,1 7.5026183,46.5984083,1 7.502045,46.5974786,1 7.5000149,46.5965747,1 7.499065,46.5959771,1 7.4991109,46.595709,1 7.4998387,46.5957243,1 7.5004286,46.5958162,1 7.5005435,46.5957779,1 7.50026,46.5955022,1 7.4972494,46.5926064,1 7.4965446,46.5914113,1 7.4957172,46.5887913,1 7.495564,46.5859032,1 7.4954261,46.5842868,1 7.4941238,46.5812531,1 7.4929747,46.5807399,1 7.4924614,46.5802955,1 7.4917949,46.5801576,1 7.4911437,46.579744,1 7.4901019,46.5793379,1 7.4888838,46.5783574,1 7.487773,46.5775147,1 7.4871372,46.5767026,1 7.4871895,46.5761873,1 7.4873108,46.5760875,1 7.4861474,46.5754004,1 7.4858975,46.5750647,1 7.485351,46.5746508,1 7.4846404,46.573667,1 7.4837035,46.5731439,1 7.4837425,46.5728862,1 7.4843437,46.5723709,1 7.4846795,46.5722147,1 7.4849293,46.5717619,1 7.4853822,46.5713403,1 7.4866393,46.5706453,1 7.4866159,46.5701456,1 7.4856945,46.5700051,1 7.4837269,46.5688339,1 7.483438,46.5685138,1 7.4831257,46.5683576,1 7.4819015,46.5676487,1 7.481459,46.567211,1 7.4803362,46.5667495,1 7.4800756,46.5665507,1 7.4797748,46.5663213,1 7.4794227,46.5658312,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6072841,46.5284422,1 7.6065438,46.5287744,1 7.6059609,46.5289507,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rainweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5725176,46.503522,1 7.571786,46.5036569,1 7.5716948,46.503694,1 7.5715584,46.5037674,1 7.5714301,46.5038062,1 7.5713036,46.5036465,1 7.5711946,46.5034698,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rainweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5716948,46.503694,1 7.5714793,46.5034154,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Trogweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5717115,46.5024556,1 7.5720126,46.5023572,1 7.572155,46.5022396,1 7.5722022,46.5021698,1 7.5721933,46.5021194,1 7.572159,46.502032,1 7.5721677,46.5020008,1 7.572181,46.50198,1 7.572291,46.5019165,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Laserweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5717043,46.5024447,1 7.570935,46.502664,1 7.5706686,46.5027262,1 7.5703643,46.5027686,1 7.5702629,46.5027661,1 7.5701922,46.5027436,1 7.57008,46.502591,1 7.5699495,46.5024145,1 7.569842,46.502313,1 7.569743,46.5022761,1 7.5696887,46.5022836,1 7.5696452,46.5023073,1 7.5696416,46.5023484,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Tannenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5625853,46.4950604,1 7.5629277,46.4952317,1 7.5632285,46.4954048,1 7.56365,46.495627,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5627191,46.4953707,1 7.5625165,46.4953073,1 7.5621808,46.4951509,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Aebiweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5371522,46.4817482,1 7.5372839,46.4820083,1 7.537241,46.4823334,1 7.5370908,46.4827175,1 7.5371766,46.4828653,1 7.5376916,46.4829392,1 7.5382709,46.4830869,1 7.5387215,46.4830721,1 7.5390649,46.483013,1 7.5393653,46.482821,1 7.5395369,46.4827471,1 7.5397515,46.482688,1 7.5400614,46.4825351,1 7.5405388,46.4820442,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Senggistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5524133,46.4901418,1 7.552748,46.4907505,1 7.5531342,46.4908864,1 7.5536321,46.4912646,1 7.5545504,46.491625,1 7.5550053,46.4920091,1 7.5554688,46.4922455,1 7.5555976,46.4922278,1 7.5556793,46.492015,1 7.5554173,46.49169,1 7.5550311,46.4914773,1 7.5549109,46.4913118,1 7.5549882,46.4910991,1 7.5555547,46.4908982,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4673625,46.4564555,1 7.4673641,46.4565592,1 7.4673418,46.45667,1 7.4673426,46.4568327,1 7.467407,46.4569435,1 7.4675943,46.457032,1 7.4676,46.45709,1 7.4675143,46.4571431,1 7.4673962,46.4571579,1 7.4672782,46.4571061,1 7.4671066,46.4569879,1 7.4669969,46.4568804,1 7.4668383,46.4568253,1 7.4668276,46.4566405,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4676573,46.4558604,1 7.4673625,46.4564555,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5555691,46.489152,1 7.5555719,46.4891333,1 7.5555852,46.489115,1 7.5555979,46.4891035,1 7.5556169,46.4890931,1 7.5556628,46.4890846,1 7.5557363,46.4890835,1 7.5559446,46.4890818,1 7.5561531,46.4890823,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gruebiweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5552097,46.4887457,1 7.5553556,46.4888139,1 7.5555678,46.4888499,1 7.5558846,46.4888569,1 7.556089,46.4888482,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5850973,46.5007372,1 7.5850387,46.5007685,1 7.5846542,46.5009922,1 7.5846239,46.5011334,1 7.5847252,46.501739,1 7.5847275,46.5020678,1 7.5849919,46.5028044,1 7.5855069,46.5035872,1 7.5855713,46.5038678,1 7.5857301,46.5039004,1 7.5859361,46.5039121,1 7.5860004,46.5040303,1 7.5858717,46.5045176,1 7.5860434,46.5048573,1 7.5863867,46.5051675,1 7.5868778,46.506548,1 7.5869798,46.5067495,1 7.587087,46.5069209,1 7.58715,46.5070534,1 7.5872426,46.5074858,1 7.587245,46.5076043,1 7.58713,46.5078698,1 7.5871139,46.5079584,1 7.5871729,46.5080876,1 7.587307,46.5081578,1 7.5875108,46.5082168,1 7.5876199,46.5082948,1 7.5876557,46.5084384,1 7.5875806,46.5087337,1 7.5876556,46.5089368,1 7.5878863,46.5094832,1 7.5880044,46.5096567,1 7.5882243,46.5098561,1 7.5884442,46.5100702,1 7.5886588,46.5103323,1 7.5888412,46.5105723,1 7.5889177,46.5107599,1 7.5889616,46.5109714,1 7.5888627,46.5113365,1 7.5887152,46.5114922,1 7.5886612,46.5116064,1 7.5886588,46.5118054,1 7.5887308,46.5121362,1 7.5887696,46.5124147,1 7.5888949,46.5126656,1 7.5893693,46.5131274,1 7.5896405,46.5132526,1 7.5902491,46.5134523,1 7.5909226,46.5134852,1 7.5913249,46.5134999,1 7.5915127,46.5134888,1 7.5918024,46.5134076,1 7.5924407,46.5133559,1 7.5926392,46.5133707,1 7.5927197,46.5134261,1 7.5927143,46.5135258,1 7.5925989,46.5135841,1 7.5923227,46.5136993,1 7.5921118,46.5138459,1 7.5919258,46.5139835,1 7.5916629,46.5142346,1 7.5916009,46.5143235,1 7.5915288,46.5144782,1 7.5914,46.5145816,1 7.5912498,46.5146813,1 7.5911503,46.5147665,1 7.5911372,46.5148289,1 7.5911936,46.5149035,1 7.5912767,46.5149618,1 7.5914698,46.5150504,1 7.5919204,46.5151575,1 7.5922017,46.5152686,1 7.5922526,46.5153912,1 7.5922798,46.5155377,1 7.5922583,46.5156337,1 7.5921779,46.5157961,1 7.5921457,46.5158884,1 7.5921588,46.5159774,1 7.5923174,46.5162059,1 7.5923388,46.5163203,1 7.5923948,46.5163908,1 7.5934463,46.5170848,1 7.5941445,46.5176762,1 7.5943827,46.5179335,1 7.5946831,46.5185647,1 7.594785,46.5188416,1 7.5949566,46.519063,1 7.5951605,46.5192513,1 7.5955789,46.5194949,1 7.5959139,46.5196688,1 7.5962656,46.5197533,1 7.5964503,46.5198164,1 7.5965552,46.519901,1 7.5966035,46.5200117,1 7.5966947,46.5201815,1 7.5968557,46.5203624,1 7.5973599,46.5207942,1 7.5975454,46.5208933,1 7.5977461,46.5209603,1 7.5978856,46.5210748,1 7.5979715,46.5212519,1 7.5979983,46.521455,1 7.5979661,46.5216912,1 7.5979607,46.5218167,1 7.5980412,46.5219939,1 7.5980948,46.5221415,1 7.5980753,46.5223333,1 7.59811,46.5226362,1 7.5982021,46.522924,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5851089,46.5006757,1 7.5850973,46.5007372,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5851089,46.5006757,1 7.5850643,46.5006438,1 7.5845945,46.5005489,1 7.5842102,46.5002567,1 7.5838483,46.4999466,1 7.5830537,46.4991232,1 7.5826523,46.4989336,1 7.582594,46.4989054,1 7.5810925,46.497901,1 7.5804891,46.4976849,1 7.5801849,46.4975458,1 7.5795841,46.4969549,1 7.5791121,46.4966595,1 7.5788121,46.4963841,1 7.5784683,46.4960686,1 7.5779508,46.4959746,1 7.5778518,46.4959324,1 7.577856,46.4958566,1 7.5780392,46.495596,1 7.5779939,46.49555,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bütscheggenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5753139,46.5088011,1 7.5752226,46.5089091,1 7.5750338,46.5093631,1 7.5749573,46.5094711,1 7.574882,46.5094983,1 7.5747902,46.5094904,1 7.5746294,46.5094263,1 7.5745088,46.5093055,1 7.574491,46.5092875,1 7.57442,46.509189,1 7.574359,46.509088,1 7.574338,46.508939,1 7.574222,46.508688,1 7.574139,46.508567,1 7.574031,46.508442,1 7.573789,46.508273,1 7.573645,46.508149,1 7.573516,46.507976,1 7.57336,46.507811,1 7.573107,46.50769,1 7.572656,46.507421,1 7.572422,46.507265,1 7.572219,46.507108,1 7.57199,46.50692,1 7.571725,46.506695,1 7.571557,46.506537,1 7.571456,46.506313,1 7.571314,46.506114,1 7.570996,46.505779,1 7.570623,46.505583,1 7.5704442,46.5055077,1 7.570293,46.505449,1 7.570076,46.505348,1 7.569794,46.505203,1 7.569594,46.505027,1 7.5695203,46.50499,1 7.5694718,46.5049861,1 7.5694441,46.5050305,1 7.5694427,46.505069,1 7.569616,46.505224,1 7.569758,46.50538,1 7.569953,46.505639,1 7.570025,46.505838,1 7.570039,46.506066,1 7.569997,46.506254,1 7.569945,46.506424,1 7.569946,46.506613,1 7.5698959,46.5066559,1 7.5697967,46.5066484,1 7.569597,46.506478,1 7.569278,46.5061,1 7.5691127,46.5059387,1 7.568755,46.505672,1 7.5684874,46.5053996,1 7.568432,46.50536,1 7.568207,46.505142,1 7.567728,46.504666,1 7.56754,46.504485,1 7.567329,46.504179,1 7.5672453,46.5041525,1 7.5670457,46.5040965,1 7.566915,46.504026,1 7.5668264,46.5039619,1 7.5667654,46.5039084,1 7.566586,46.503722,1 7.5665589,46.5036645,1 7.5662748,46.5034588,1 7.566168,46.503343,1 7.5660937,46.5031901,1 7.5660213,46.5030833,1 7.565959,46.5030093,1 7.5658571,46.5029369,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hohliebeweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5748122,46.4926149,1 7.5748165,46.4925846,1 7.5748422,46.4925475,1 7.5752285,46.4925784,1 7.5753308,46.4925713,1 7.5753936,46.4925327,1 7.5753914,46.4924534,1 7.5752299,46.4923705,1 7.5750836,46.4923319,1 7.5747518,46.4923365,1 7.574514,46.4923496,1 7.5744161,46.4923149,1 7.5742703,46.4921827,1 7.5741195,46.4920273,1 7.5739786,46.4915334,1 7.5738497,46.4911149,1 7.5737082,46.4907245,1 7.5736295,46.490499,1 7.573569,46.490309,1 7.5735774,46.490168,1 7.5737097,46.4899012,1 7.573877,46.4897669,1 7.5740024,46.4896153,1 7.5739773,46.4894445,1 7.5739991,46.4894219,1 7.5740293,46.4894136,1 7.5740927,46.4894215,1 7.5741494,46.4894612,1 7.5742011,46.4895481,1 7.574245,46.4896146,1 7.5742987,46.4897163,1 7.5743469,46.4898096,1 7.5744159,46.4898903,1 7.57463,46.4900137,1 7.5750015,46.4901671,1 7.5751139,46.4901494,1 7.5751655,46.4901109,1 7.5752686,46.4900151,1 7.5753957,46.4898584,1 7.5756006,46.4897247,1 7.5760912,46.4895722,1 7.5766751,46.489079,1 7.5768604,46.4888862,1 7.5770611,46.4887403,1 7.5772841,46.4886847,1 7.5774307,46.4886108,1 7.577443,46.4885782,1 7.5774172,46.488546,1 7.5773527,46.4885144,1 7.5770322,46.488577,1 7.576403,46.4886185,1 7.5760685,46.4885897,1 7.5758881,46.4885517,1 7.5757861,46.4885253,1 7.5756417,46.488455,1 7.5754445,46.4882827,1 7.5752077,46.4878809,1 7.5749909,46.4874603,1 7.5747366,46.48733,1 7.5744824,46.4872079,1 7.5742341,46.4869365,1 7.5740212,46.4866488,1 7.5737818,46.4862913,1 7.573563,46.4859489,1 7.573498,46.4857182,1 7.5735256,46.4853043,1 7.5736283,46.4850564,1 7.5739892,46.4846275,1 7.5740561,46.4844231,1 7.5741244,46.4842225,1 7.5741746,46.4841227,1 7.5743418,46.4839567,1 7.5743683,46.483903,1 7.5743906,46.4837706,1 7.5742118,46.4830843,1 7.5741888,46.4829278,1 7.5739849,46.482695,1 7.5737651,46.482332,1 7.5736551,46.4820242,1 7.5735632,46.481887,1 7.5735731,46.4818453,1 7.5736473,46.4818219,1 7.5737583,46.4818693,1 7.573887,46.4820133,1 7.5744837,46.4821543,1 7.5746673,46.482228,1 7.5751903,46.4824967,1 7.5754308,46.4826309,1 7.5757703,46.4827575,1 7.5760306,46.4829216,1 7.5763564,46.4831348,1 7.5763735,46.4831899,1 7.5763394,46.4832512,1 7.5761156,46.4834307,1 7.5760319,46.4835878,1 7.5760578,46.4837445,1 7.5761335,46.4838993,1 7.5763194,46.484058,1 7.5765279,46.4841883,1 7.576996,46.4847005,1 7.5771557,46.484847,1 7.5774165,46.4849629,1 7.577649,46.485013,1 7.5778689,46.4850323,1 7.578133,46.48503,1 7.578373,46.4849835,1 7.5784787,46.4849298,1 7.5784966,46.4848874,1 7.5784279,46.4848328,1 7.5782986,46.4847952,1 7.5782212,46.4847547,1 7.5781483,46.4845932,1 7.578149,46.484371,1 7.5781647,46.4840848,1 7.5781932,46.4839283,1 7.5782963,46.4837267,1 7.5785167,46.4834123,1 7.5785346,46.4833762,1 7.5784025,46.4830335,1 7.5782586,46.4828248,1 7.5780223,46.4826498,1 7.5777744,46.482495,1 7.57752,46.4822396,1 7.5772621,46.4819819,1 7.5770082,46.4818722,1 7.5768561,46.4817701,1 7.5766102,46.4814291,1 7.5765466,46.4812489,1 7.5763526,46.4811718,1 7.576209,46.4811,1 7.5761374,46.4809328,1 7.575831,46.480722,1 7.575571,46.480581,1 7.5752753,46.480404,1 7.5751232,46.4803012,1 7.575108,46.480291,1 7.574856,46.480078,1 7.5746784,46.4799297,1 7.57458,46.479817,1 7.574411,46.4796044,1 7.5743116,46.4792862,1 7.5742532,46.4790813,1 7.574265,46.478525,1 7.574191,46.478269,1 7.5740445,46.4779657,1 7.573681,46.477433,1 7.573501,46.477152,1 7.5733697,46.4768436,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5855548,46.5376895,1 7.5856085,46.5377307,1 7.585568,46.5378347,1 7.5853884,46.5379208,1 7.5851067,46.5380153,1 7.584983,46.538079,1 7.5849247,46.5381363,1 7.5848752,46.5382295,1 7.5848662,46.5384329,1 7.5848963,46.5385742,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5817066,46.490105,1 7.5817008,46.4902111,1 7.5816733,46.4907526,1 7.5812232,46.4913729,1 7.5812639,46.4917049,1 7.5811183,46.492002,1 7.5811882,46.4921709,1 7.5809319,46.4930271,1 7.5812974,46.4941264,1 7.581237,46.494378,1 7.5814383,46.4945894,1 7.5819709,46.4947404,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5776565,46.5036379,1 7.5777661,46.503767,1 7.5789243,46.5048037,1 7.580017,46.5059799,1 7.5800649,46.5062539,1 7.5801586,46.5064359,1 7.5804202,46.5068391,1 7.580475,46.506968,1 7.5806871,46.5075547,1 7.580774,46.507992,1 7.5807475,46.5081673,1 7.5806755,46.5083959,1 7.5806824,46.5085984,1 7.5806998,46.5088309,1 7.5806678,46.5089152,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.561354,46.5249937,1 7.5611075,46.5254464,1 7.5603269,46.5258299,1 7.5602224,46.5259294,1 7.5602179,46.5259521,1 7.5602395,46.5259798,1 7.5603954,46.5260321,1 7.5606287,46.5260943,1 7.5612083,46.5262005,1 7.5612555,46.5262337,1 7.5613375,46.5263696,1 7.561382,46.526515,1 7.5613927,46.5267583,1 7.5615433,46.5277193,1 7.5616651,46.5279657,1 7.5617554,46.5283091,1 7.561906,46.5284,1 7.5620182,46.5284211,1 7.5621819,46.5281266,1 7.562649,46.527644,1 7.564524,46.5268768,1 7.5650682,46.5267509,1 7.5654095,46.5267497,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5914358,46.5216049,1 7.591663,46.52154,1 7.591774,46.521568,1 7.59211,46.521848,1 7.592208,46.521883,1 7.592531,46.521957,1 7.592895,46.522174,1 7.592974,46.522297,1 7.593075,46.522407,1 7.593585,46.522719,1 7.593747,46.52275,1 7.593935,46.522758,1 7.5944256,46.5230091,1 7.594503,46.523136,1 7.594599,46.52325,1 7.594762,46.523392,1 7.595077,46.523553,1 7.595237,46.523611,1 7.59534,46.523697,1 7.595377,46.523739,1 7.595485,46.524006,1 7.595641,46.524151,1 7.595763,46.524244,1 7.596368,46.524518,1 7.59669,46.524629,1 7.597151,46.524844,1 7.597305,46.524946,1 7.597364,46.52505,1 7.597337,46.5252,1 7.597339,46.525263,1 7.5974171,46.5254953,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5656379,46.4915646,1 7.5660518,46.4919536,1 7.5663034,46.4920439,1 7.5662885,46.4923288,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5855548,46.5376895,1 7.5855334,46.5376761,1 7.5854732,46.5376661,1 7.585217,46.537669,1 7.5845096,46.5376307,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bühlbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4477077,46.4598442,1 7.4476773,46.4598777,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rütistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4274775,46.4522141,1 7.4261397,46.4530454,1 7.4264807,46.4545093,1 7.4246182,46.4549791,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ausserschwandstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5739397,46.5051162,1 7.5733159,46.5041805,1 7.5725176,46.503522,1 7.5722936,46.5033372,1 7.5717115,46.5024556,1 7.5717043,46.5024447,1 7.5714965,46.50213,1 7.5709593,46.5015986,1 7.5696299,46.5006507,1 7.566752,46.4977228,1 7.5665412,46.4976961,1 7.5663817,46.4976202,1 7.5663132,46.4975194,1 7.5663009,46.4973709,1 7.566314,46.4972084,1 7.5662826,46.497065,1 7.5662005,46.4969304,1 7.5656932,46.4963425,1 7.5650662,46.4957333,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Birkenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5597583,46.4842011,1 7.560472,46.4841615,1 7.5610374,46.4841326,1 7.561747,46.4840627,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bütscheggenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5739397,46.5051162,1 7.5742401,46.5059307,1 7.5740276,46.5063991,1 7.5741474,46.5067344,1 7.5748979,46.5074196,1 7.5754552,46.508546,1 7.5753139,46.5088011,1 7.5752707,46.5092956,1 7.5756239,46.5103671,1 7.5758202,46.5105712,1 7.5757377,46.5108812,1 7.5750941,46.5111403,1 7.5726804,46.5114307,1 7.5719504,46.5112933,1 7.5717288,46.5112797,1 7.5712568,46.5112509,1 7.5709967,46.5111631,1 7.5695622,46.5107082,1 7.5693037,46.510755,1 7.5685318,46.5110658,1 7.5675927,46.5110709,1 7.5669006,46.5111824,1 7.5665772,46.5111305,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Dählenweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5619024,46.484733,1 7.5605313,46.4847046,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fliederweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5573139,46.4905062,1 7.5575742,46.4906144,1 7.5580001,46.49089,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Obere Bodenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5621068,46.4703326,1 7.5618747,46.4704067,1 7.5615007,46.4706892,1 7.5612123,46.4709431,1 7.5609132,46.4715494,1 7.5596006,46.476687,1 7.5595328,46.4773032,1 7.5598308,46.4782305,1 7.559232,46.4796016,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5167801,46.4849324,1 7.5181617,46.4850001,1 7.5189956,46.4843231,1 7.5198652,46.4843966,1 7.5207213,46.4847725,1 7.5217773,46.484755,1 7.5222435,46.4846581,1 7.5224367,46.4843815,1 7.524081,46.4841142,1 7.5247031,46.4843208,1 7.525197,46.4841121,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5622864,46.461042,1 7.5622266,46.4611132,1 7.5622539,46.462548,1 7.5636079,46.4648625,1 7.563823,46.4661917,1 7.5638651,46.4664717,1 7.5630107,46.4683629,1 7.5628846,46.468745,1 7.5628282,46.4694412,1 7.562485,46.4699422,1 7.5621068,46.4703326,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5622314,46.476374,1 7.5622314,46.4762793,1 7.561865,46.4755649,1 7.5619871,46.4745086,1 7.5617307,46.4740812,1 7.5617185,46.4732508,1 7.5613338,46.472689,1 7.5614864,46.4719136,1 7.5612588,46.4715361,1 7.5612549,46.47139,1 7.5612834,46.4712484,1 7.5615722,46.4708814,1 7.5621068,46.4705513,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5624583,46.460924,1 7.5622864,46.461042,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5622313,46.4764657,1 7.5622314,46.476374,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796758,46.5157114,1 7.5797311,46.5157548,1 7.5799023,46.5158239,1 7.5801322,46.5158917,1 7.5802236,46.5159468,1 7.5802588,46.5160395,1 7.5802508,46.5162606,1 7.5803341,46.5164925,1 7.580727,46.516711,1 7.581195,46.5171189,1 7.581244,46.51722,1 7.5812692,46.5174308,1 7.5813528,46.517721,1 7.5813228,46.5178771,1 7.5814321,46.518047,1 7.5817535,46.5185832,1 7.5820489,46.5190792,1 7.5821911,46.5191199,1 7.5823268,46.5191792,1 7.582469,46.5193924,1 7.5826412,46.519449,1 7.583197,46.519706,1 7.5836784,46.5197251,1 7.5840532,46.5197587,1 7.584179,46.51977,1 7.5844902,46.5200212,1 7.5847859,46.5202177,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796758,46.5157114,1 7.579568,46.5155332,1 7.5795576,46.5153762,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5167801,46.4849324,1 7.5166386,46.4850139,1 7.5163346,46.4854944,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5496477,46.485998,1 7.5490011,46.4856419,1 7.5478033,46.4852982,1 7.5471908,46.4852248,1 7.5459925,46.4848673,1 7.5448208,46.4848031,1 7.5437823,46.4847023,1 7.5435293,46.4846839,1 7.5433828,46.4847756,1 7.5432896,46.4849131,1 7.5430633,46.4850506,1 7.5429168,46.4850231,1 7.542757,46.4849039,1 7.5424774,46.4848764,1 7.5421313,46.4849681,1 7.5410395,46.4851881,1 7.5404803,46.4853807,1 7.5390955,46.4861049,1 7.537125,46.487095,1 7.5352743,46.4871775,1 7.5339162,46.4874433,1 7.5334102,46.4877459,1 7.533104,46.4882409,1 7.5325048,46.4887359,1 7.5320655,46.4888551,1 7.5307606,46.4887817,1 7.5290263,46.488887,1 7.5285629,46.4889495,1 7.5284723,46.4889619,1 7.5273381,46.4891146,1 7.5267159,46.4892819,1 7.525773,46.4897013,1 7.5254036,46.4897035,1 7.5237372,46.4889167,1 7.5230777,46.4888539,1 7.522833,46.4886169,1 7.5223705,46.4886012,1 7.521205,46.4883099,1 7.51934,46.4872071,1 7.5165633,46.4857592,1 7.5163346,46.4854944,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.556403,46.4906599,1 7.5564895,46.4903578,1 7.5564031,46.4900393,1 7.5564249,46.4897068,1 7.5563499,46.4894643,1 7.5561408,46.4893354,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Zelgstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5571221,46.4911702,1 7.557339,46.490971,1 7.5573284,46.4908942,1 7.5572427,46.4907622,1 7.5573095,46.4906602,1 7.5573139,46.4905062,1 7.5572902,46.4904128,1 7.557545,46.490364,1 7.5577631,46.4903969,1 7.5579974,46.4904534,1 7.5592506,46.4915649,1 7.5598352,46.4920944,1 7.5599801,46.492216,1 7.5605367,46.492528,1 7.5615826,46.4930937,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6221411,46.5675483,1 7.6217555,46.5670542,1 7.6214032,46.5667511,1 7.6212605,46.5665937,1 7.6211744,46.5664214,1 7.6211495,46.5663947,1 7.6211127,46.5663881,1 7.6210786,46.5663881,1 7.6210545,46.5663925,1 7.6210286,46.5664097,1 7.6210092,46.5666306,1 7.6210364,46.5667281,1 7.621389,46.567218,1 7.621477,46.567386,1 7.621554,46.567623,1 7.621569,46.567826,1 7.621649,46.568184,1 7.6217257,46.5683166,1 7.6218557,46.5683782,1 7.6219111,46.5684444,1 7.6219239,46.568476,1 7.6219211,46.5684997,1 7.6219016,46.5685194,1 7.6218707,46.5685301,1 7.6218457,46.5685301,1 7.6217421,46.5684822,1 7.621518,46.568279,1 7.621345,46.568087,1 7.621152,46.567792,1 7.620757,46.567417,1 7.6206225,46.5671884,1 7.6205955,46.5671633,1 7.6205627,46.5671482,1 7.6205261,46.5671487,1 7.6204933,46.567158,1 7.6204734,46.5671814,1 7.6204702,46.5672176,1 7.6205499,46.5673038,1 7.620818,46.567734,1 7.620884,46.567894,1 7.62097,46.568114,1 7.621003,46.568253,1 7.620997,46.568398,1 7.621007,46.568515,1 7.6210788,46.5686958,1 7.6211778,46.5687891,1 7.6213252,46.5688698,1 7.6213764,46.5689228,1 7.6213615,46.5689658,1 7.6212994,46.5689787,1 7.621143,46.568912,1 7.6209424,46.5688012,1 7.620798,46.56864,1 7.620768,46.568413,1 7.620644,46.568196,1 7.6205393,46.568144,1 7.6204996,46.5682166,1 7.620621,46.568325,1 7.620622,46.568522,1 7.6205702,46.5688341,1 7.6205875,46.5689279,1 7.6206182,46.5690024,1 7.6207232,46.5690962,1 7.6208673,46.5691844,1 7.6211219,46.569248,1 7.6212305,46.5693311,1 7.6213808,46.5694812,1 7.6214434,46.5696385,1 7.6214758,46.5699093,1 7.6215649,46.5701971,1 7.6215258,46.5702147,1 7.6214789,46.5701959,1 7.62133,46.570035,1 7.620979,46.569821,1 7.620628,46.569579,1 7.6203038,46.5692579,1 7.6201751,46.5691972,1 7.6189412,46.5688229,1 7.6186735,46.5687168,1 7.618385,46.568508,1 7.617659,46.567929,1 7.617399,46.567728,1 7.617231,46.567583,1 7.617111,46.567413,1 7.616913,46.567047,1 7.61675,46.566845,1 7.616575,46.566731,1 7.616185,46.566364,1 7.6154775,46.5657796,1 7.614885,46.565334,1 7.6146833,46.5651263,1 7.614526,46.564915,1 7.613981,46.564401,1 7.613375,46.563863,1 7.613015,46.563613,1 7.612686,46.563419,1 7.6124398,46.5632504,1 7.6121,46.562995,1 7.611927,46.562877,1 7.611741,46.562768,1 7.611586,46.562717,1 7.6108173,46.5625652,1 7.610664,46.562435,1 7.610636,46.562142,1 7.610607,46.562116,1 7.610379,46.562031,1 7.610198,46.561993,1 7.609934,46.561961,1 7.609264,46.561757,1 7.6086516,46.5616698,1 7.608165,46.561607,1 7.607721,46.561471,1 7.607153,46.561323,1 7.6067556,46.5612362,1 7.606619,46.561159,1 7.60639,46.56107,1 7.605845,46.56095,1 7.60538,46.56095,1 7.605156,46.560926,1 7.6046366,46.5609521,1 7.6043472,46.5608585,1 7.604221,46.56071,1 7.6044,46.560493,1 7.604458,46.560313,1 7.604451,46.56029,1 7.604328,46.560168,1 7.604223,46.560128,1 7.603285,46.559916,1 7.603037,46.559907,1 7.602788,46.55993,1 7.602538,46.559967,1 7.602492,46.55998,1 7.602347,46.560076,1 7.602262,46.560101,1 7.602154,46.560074,1 7.601967,46.559894,1 7.601779,46.559881,1 7.601623,46.559915,1 7.60157,46.559933,1 7.601479,46.560022,1 7.600828,46.560398,1 7.6007,46.560418,1 7.600504,46.560329,1 7.6004294,46.5600943,1 7.600394,46.560076,1 7.600191,46.560136,1 7.600046,46.560166,1 7.599891,46.560195,1 7.599868,46.560182,1 7.599826,46.56007,1 7.600174,46.559822,1 7.600349,46.55977,1 7.600482,46.559689,1 7.600531,46.559525,1 7.600488,46.559365,1 7.600479,46.559289,1 7.600507,46.559148,1 7.600627,46.559022,1 7.600735,46.558966,1 7.600695,46.55883,1 7.600642,46.558768,1 7.600627,46.558706,1 7.600642,46.558575,1 7.600765,46.558387,1 7.600618,46.557904,1 7.60047,46.557573,1 7.600087,46.557016,1 7.599944,46.556679,1 7.599778,46.556393,1 7.599694,46.556124,1 7.599694,46.556038,1 7.599393,46.555577,1 7.599123,46.555283,1 7.598703,46.554963,1 7.598536,46.554827,1 7.598348,46.554712,1 7.597928,46.554526,1 7.597716,46.554373,1 7.597273,46.554,1 7.5971816,46.5538848,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5716754,46.4967489,1 7.5718925,46.4966978,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5718925,46.4966978,1 7.5721073,46.4966416,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5721073,46.4966416,1 7.572546,46.4965413,1 7.5738335,46.4964527,1 7.5761938,46.4961277,1 7.5775242,46.4954778,1 7.5778322,46.4954612,1 7.5778754,46.4954761,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hubelstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5619481,46.4908394,1 7.5618004,46.4908374,1 7.5611634,46.4903397,1 7.5610885,46.4903465,1 7.5601168,46.4909892,1 7.5592506,46.4915649,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Ladholzstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5924089,46.5310813,1 7.5922889,46.5311342,1 7.5917636,46.5312026,1 7.5913287,46.5311455,1 7.590988,46.531039,1 7.590563,46.531075,1 7.590277,46.531153,1 7.589796,46.531169,1 7.589642,46.531198,1 7.5895858,46.5312368,1 7.5896155,46.5312723,1 7.589692,46.531289,1 7.589899,46.531281,1 7.59009,46.531248,1 7.590295,46.531242,1 7.590523,46.53127,1 7.59101,46.531411,1 7.591227,46.531491,1 7.591367,46.531568,1 7.591589,46.531763,1 7.591977,46.531849,1 7.592203,46.531863,1 7.592401,46.531958,1 7.592571,46.532083,1 7.592649,46.53219,1 7.592619,46.532245,1 7.592464,46.532225,1 7.5922679,46.5321729,1 7.592029,46.532173,1 7.591679,46.532128,1 7.591436,46.532064,1 7.59126,46.531985,1 7.591029,46.531952,1 7.590802,46.531942,1 7.590574,46.531889,1 7.59041,46.531811,1 7.590343,46.531799,1 7.590284,46.531807,1 7.590247,46.5318378,1 7.590272,46.531878,1 7.590439,46.531925,1 7.590867,46.532109,1 7.591051,46.532212,1 7.591078,46.532362,1 7.591136,46.532536,1 7.591266,46.532633,1 7.591414,46.532676,1 7.591683,46.532649,1 7.591889,46.532648,1 7.591964,46.532749,1 7.591866,46.532878,1 7.5918006,46.5330041,1 7.591615,46.533166,1 7.591448,46.533263,1 7.591294,46.533385,1 7.5913,46.533524,1 7.591405,46.533697,1 7.591559,46.533871,1 7.591716,46.53403,1 7.591931,46.534138,1 7.59214,46.534193,1 7.592314,46.534298,1 7.592383,46.534445,1 7.592398,46.534745,1 7.592615,46.535027,1 7.592633,46.535216,1 7.592563,46.535792,1 7.592536,46.536167,1 7.592524,46.536254,1 7.592457,46.536343,1 7.592252,46.536439,1 7.592169,46.536464,1 7.591936,46.536594,1 7.591871,46.536668,1 7.591796,46.53691,1 7.591882,46.537032,1 7.59199,46.537252,1 7.592385,46.53746,1 7.592711,46.537952,1 7.592876,46.538212,1 7.593007,46.53888,1 7.59305,46.539226,1 7.592988,46.539398,1 7.592884,46.539444,1 7.592731,46.539389,1 7.5924,46.539142,1 7.5920484,46.538841,1 7.591704,46.53838,1 7.591495,46.538035,1 7.590946,46.537616,1 7.590806,46.53741,1 7.590666,46.537254,1 7.590498,46.536913,1 7.590428,46.536669,1 7.590426,46.536518,1 7.590467,46.536344,1 7.5906402,46.5360987,1 7.590345,46.535752,1 7.589582,46.535183,1 7.589065,46.534861,1 7.588579,46.534642,1 7.588298,46.534535,1 7.587194,46.534279,1 7.587059,46.5342825,1 7.586884,46.534294,1 7.5867784,46.5343331,1 7.5866893,46.5343725,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789112,46.5019253,1 7.578915,46.5018479,1 7.5789126,46.5017241,1 7.5789513,46.5016989,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Neuweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5763776,46.5014193,1 7.576294,46.5014686,1 7.5765898,46.5017882,1 7.5770648,46.5028188,1 7.5776565,46.5036379,1 7.5775725,46.5038386,1 7.577659,46.504053,1 7.577975,46.504743,1 7.578067,46.505214,1 7.5785206,46.505896,1 7.57858,46.506791,1 7.578819,46.507217,1 7.579365,46.507691,1 7.579607,46.508434,1 7.579732,46.508536,1 7.5797057,46.509266,1 7.579671,46.510226,1 7.5797453,46.5109917,1 7.580007,46.511906,1 7.5802506,46.512384,1 7.5805152,46.5129738,1 7.5806365,46.5135164,1 7.580669,46.513662,1 7.580641,46.513675,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Oeystrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5597987,46.4888045,1 7.5596114,46.4887086,1 7.5590977,46.4887413,1 7.55826,46.4885102,1 7.557679,46.4883499,1 7.5562602,46.4872899,1 7.555135,46.486923,1 7.5541077,46.486711,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rinderwaldstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5920786,46.5277859,1 7.592013,46.5278814,1 7.5921113,46.5283229,1 7.5922599,46.5285511,1 7.5922599,46.5286679,1 7.592178,46.5287971,1 7.5920264,46.5289161,1 7.5918009,46.5290055,1 7.5914879,46.5290493,1 7.5913605,46.5289891,1 7.5913074,46.5288595,1 7.5915011,46.528447,1 7.5913605,46.528071,1 7.5910448,46.5275052,1 7.5906628,46.5273391,1 7.5905726,46.5270945,1 7.5902867,46.5267791,1 7.5898693,46.5266264,1 7.589723,46.5261891,1 7.5898509,46.5259126,1 7.5892239,46.525386,1 7.5887596,46.525155,1 7.58866,46.52505,1 7.5886593,46.5249033,1 7.588554,46.524815,1 7.5884977,46.5249146,1 7.5885064,46.5251373,1 7.5886429,46.5253687,1 7.5888901,46.5255442,1 7.5889148,46.5258339,1 7.5886581,46.5260421,1 7.5885021,46.5262391,1 7.5884711,46.5270452,1 7.5888648,46.527704,1 7.5888321,46.5278146,1 7.5886168,46.5279372,1 7.5884353,46.5278681,1 7.5881401,46.5275704,1 7.5876889,46.5275737,1 7.587346,46.527544,1 7.5863738,46.5272679,1 7.585991,46.5271518,1 7.5856872,46.5270452,1 7.5854035,46.5268207,1 7.5850142,46.5265989,1 7.5832724,46.5254439,1 7.5825523,46.5243282,1 7.582418,46.5243637,1 7.5824194,46.5250364,1 7.5824344,46.5254608,1 7.5826066,46.5259242,1 7.5824417,46.5258955,1 7.5812345,46.5251598,1 7.5807647,46.5249892,1 7.580322,46.5249821,1 7.5795587,46.5251441,1 7.5793111,46.5251274,1 7.5789818,46.524955,1 7.578854,46.5249327,1 7.5788147,46.5249916,1 7.578865,46.525061,1 7.5790663,46.5253114,1 7.5793669,46.5255973,1 7.5805877,46.5262635,1 7.581576,46.526976,1 7.5822447,46.5277997,1 7.5825672,46.5283477,1 7.5825694,46.5284335,1 7.582459,46.5284262,1 7.5823034,46.528276,1 7.581222,46.5276355,1 7.580843,46.52746,1 7.5805204,46.5273828,1 7.5785565,46.5276402,1 7.5774574,46.5275452,1 7.5763023,46.5271631,1 7.5755453,46.5267797,1 7.574859,46.526213,1 7.5747776,46.5259892,1 7.5748202,46.5255898,1 7.5745874,46.5254626,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schmittengraben</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5632271,46.4930606,1 7.5633842,46.4930788,1 7.5635249,46.4931345,1 7.5635872,46.49323,1 7.5633938,46.4935302,1 7.5633565,46.4937197,1 7.5633739,46.4938029,1 7.5633923,46.4938168,1 7.5634261,46.4938155,1 7.5635035,46.4937563,1 7.5636349,46.4936571,1 7.5641212,46.4931646,1 7.5643068,46.4930036,1 7.5644847,46.4928738,1 7.5648105,46.4927291,1 7.5649245,46.4926975,1 7.565606,46.4923128,1 7.5660014,46.4922682,1 7.5662885,46.4923288,1 7.5663814,46.4923822,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schmittengraben</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.568897,46.4933261,1 7.5687413,46.4932105,1 7.5686769,46.4927008,1 7.5684103,46.4924955,1 7.5675587,46.4923767,1 7.5671661,46.4924553,1 7.5665136,46.4924594,1 7.5664566,46.49243,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schulgässli</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5658886,46.4903417,1 7.5656473,46.4907108,1 7.5661236,46.4911985,1 7.5660442,46.491516,1 7.5656379,46.4915646,1 7.5653456,46.4914022,1 7.5648195,46.4911815,1 7.5641047,46.49108,1 7.5635494,46.4910794,1 7.563239,46.4912821,1 7.5627963,46.4915881,1 7.5623751,46.4918667,1 7.5620712,46.4920622,1 7.5613774,46.4922729,1 7.5610408,46.4922866,1 7.5605367,46.492528,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5876833,46.5164516,1 7.5875192,46.5167316,1 7.5874183,46.5169215,1 7.587425,46.5170785,1 7.5875775,46.5172966,1 7.5876717,46.5173423,1 7.5877738,46.517356,1 7.587872,46.517324,1 7.5879397,46.5171981,1 7.5879633,46.5171897,1 7.5879931,46.5171784,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5658886,46.4903417,1 7.565932,46.4902542,1 7.5659587,46.4900282,1 7.5658668,46.4898255,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5987112,46.5396482,1 7.5993066,46.5400455,1 7.5999067,46.5402763,1 7.6000501,46.5404757,1 7.6000148,46.5411065,1 7.6000721,46.5411632,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5922439,46.524217,1 7.5926503,46.5241251,1 7.5930389,46.524197,1 7.592967,46.5247871,1 7.5930735,46.5248648,1 7.593788,46.5252748,1 7.5941389,46.5256564,1 7.5940752,46.5260536,1 7.594492,46.52675,1 7.5951405,46.526862,1 7.5954175,46.5271921,1 7.5955472,46.5272157,1 7.5958773,46.5270624,1 7.5961485,46.5270919,1 7.5966466,46.5275795,1 7.5966706,46.527934,1 7.5973676,46.5279039,1 7.5980285,46.5279941,1 7.5981221,46.5280116,1 7.5983157,46.5281985,1 7.5984235,46.5283926,1 7.5984019,46.5286838,1 7.59865,46.5289211,1 7.5992216,46.5289427,1 7.5992108,46.5288456,1 7.5990167,46.5288133,1 7.5988226,46.528673,1 7.5988226,46.5282524,1 7.5990383,46.5276485,1 7.598499,46.5274112,1 7.5984235,46.5269798,1 7.5980892,46.5266562,1 7.5978843,46.5258797,1 7.5977987,46.5257423,1 7.5975919,46.5256673,1 7.5973988,46.525623,1 7.5973501,46.5255351,1 7.5974171,46.5254953,1 7.5978141,46.5255821,1 7.5987892,46.5260127,1 7.5999416,46.5266965,1 7.6009293,46.5271017,1 7.6013472,46.5274056,1 7.6016059,46.5274883,1 7.6019608,46.5274238,1 7.6029933,46.5275421,1 7.6031654,46.5274883,1 7.6032407,46.5272254,1 7.6036473,46.5268482,1 7.6042256,46.5264533,1 7.6045099,46.5265219,1 7.6043789,46.5268992,1 7.6046504,46.5272335,1 7.6051322,46.5276465,1 7.6054138,46.5281732,1 7.6052241,46.5286118,1 7.604846,46.5290789,1 7.6048905,46.5294792,1 7.6053798,46.5295682,1 7.6061361,46.5293018,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5784191,46.501918,1 7.5784483,46.5020248,1 7.5785677,46.5021453,1 7.5789152,46.5022869,1 7.579346,46.50235,1 7.57937,46.5023535,1 7.5795731,46.5024713,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bonderlenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5871283,46.4860133,1 7.5870001,46.4859898,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bonderlenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5870001,46.4859898,1 7.5865419,46.4861414,1 7.5861778,46.4863286,1 7.5851904,46.4864815,1 7.5844927,46.4870776,1 7.5840558,46.4872544,1 7.5834347,46.4869748,1 7.5831949,46.4870435,1 7.5833797,46.487296,1 7.5831716,46.4875457,1 7.5827035,46.4876497,1 7.5815952,46.4884243,1 7.5809932,46.489163,1 7.5795925,46.490413,1 7.5793256,46.4906279,1 7.5782826,46.4914569,1 7.5773048,46.4918834,1 7.5769663,46.4921038,1 7.5767396,46.4922837,1 7.5764568,46.4926086,1 7.5762001,46.4927026,1 7.5751353,46.4926314,1 7.5748122,46.4926149,1 7.5745794,46.4926012,1 7.5737472,46.49283,1 7.5733207,46.4921331,1 7.5729843,46.4919146,1 7.5724562,46.4915977,1 7.5716564,46.4911345,1 7.5705649,46.4909123,1 7.5696679,46.4904828,1 7.5678063,46.4896289,1 7.5677844,46.4896259,1 7.5676637,46.4896332,1 7.5675205,46.4896429,1 7.5670889,46.489664,1 7.5655667,46.489498,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bonderlenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5871283,46.4860133,1 7.5871827,46.4860238,1 7.5880126,46.4861803,1 7.588105,46.4862347,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hirzbodenportstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.587042,46.4886078,1 7.5865782,46.4891164,1 7.5860911,46.4895304,1 7.5853664,46.4900213,1 7.5849608,46.4901754,1 7.5842203,46.490802,1 7.5836122,46.4911889,1 7.5834458,46.4913556,1 7.5830459,46.4921806,1 7.5824807,46.4927062,1 7.5818375,46.4934555,1 7.5817816,46.4938726,1 7.5819709,46.4947404,1 7.5821341,46.4953662,1 7.5823615,46.4964461,1 7.5827043,46.4971018,1 7.5829548,46.4981568,1 7.5832431,46.4984279,1 7.5840128,46.4988362,1 7.5842205,46.4990653,1 7.583876,46.4996144,1 7.5838483,46.4999466,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5821341,46.4953662,1 7.5837231,46.4970956,1 7.5857965,46.4987966,1 7.5860984,46.499632,1 7.586803,46.4998836,1 7.5870948,46.4997729,1 7.5872156,46.499632,1 7.5874027,46.4997231,1 7.5871018,46.5006672,1 7.5871059,46.5011125,1 7.5874893,46.5015618,1 7.5883262,46.5019288,1 7.5886041,46.5022256,1 7.5892868,46.5025669,1 7.5894274,46.5030495,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.587042,46.4886078,1 7.587899,46.48825,1 7.5882043,46.4882427,1 7.5886503,46.488306,1 7.588931,46.488327,1 7.589214,46.488259,1 7.589931,46.487709,1 7.590149,46.487378,1 7.5904105,46.4872809,1 7.590543,46.487167,1 7.590873,46.487027,1 7.5911572,46.4869727,1 7.5912821,46.4869728,1 7.591414,46.486973,1 7.5915741,46.4868982,1 7.591735,46.486781,1 7.5919641,46.4868266,1 7.5918939,46.4869026,1 7.591729,46.487148,1 7.591454,46.488028,1 7.5911193,46.4884027,1 7.591501,46.48837,1 7.592061,46.487936,1 7.592171,46.488034,1 7.5922214,46.488268,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5850387,46.5007685,1 7.5850317,46.501067,1 7.5855276,46.5016065,1 7.5865636,46.5024871,1 7.5868966,46.5026795,1 7.5872814,46.5030569,1 7.5874664,46.5030643,1 7.5877032,46.5027091,1 7.5876292,46.5023613,1 7.587681,46.5021467,1 7.5879622,46.5022947,1 7.5889464,46.5026203,1 7.5894274,46.5030495,1 7.5895162,46.5032567,1 7.5898342,46.5032999,1 7.5898564,46.5037902,1 7.589797,46.5040501,1 7.5899159,46.5043324,1 7.5898936,46.5047335,1 7.5904433,46.5055655,1 7.5911267,46.5060261,1 7.5920352,46.5070536,1 7.5932521,46.5082243,1 7.5931905,46.5089944,1 7.5935293,46.5092409,1 7.5942071,46.5092563,1 7.5947924,46.5091177,1 7.5952083,46.5091793,1 7.5953623,46.5096568,1 7.5965638,46.5102575,1 7.5971029,46.5102575,1 7.597719,46.510427,1 7.5982736,46.5103807,1 7.5993672,46.5107966,1 7.6006765,46.5105656,1 7.6007535,46.5107196,1 7.6005378,46.5111201,1 7.6005378,46.5113357,1 7.6009075,46.5113049,1 7.6012926,46.5114436,1 7.6015391,46.5118441,1 7.6017547,46.5118903,1 7.60234,46.5124756,1 7.6035107,46.5129069,1 7.6037571,46.5130917,1 7.6032796,46.5132304,1 7.6031102,46.5134922,1 7.6032334,46.514247,1 7.6018317,46.5150942,1 7.601955,46.515433,1 7.6026943,46.5160184,1 7.6027251,46.516388,1 7.6033694,46.5171449,1 7.6040652,46.5178513,1 7.6041268,46.5186061,1 7.6044657,46.5195919,1 7.6045581,46.5203929,1 7.6050202,46.5205931,1 7.6061909,46.5203775,1 7.6073923,46.5208704,1 7.6077774,46.5208242,1 7.6081625,46.5215635,1 7.607916,46.5221951,1 7.6081009,46.522688,1 7.6085014,46.5229036,1 7.6085168,46.5234273,1 7.6089327,46.5240127,1 7.608717,46.5251679,1 7.6085168,46.5255376,1 7.6085014,46.5259227,1 7.6077774,46.5264156,1 7.6077004,46.5267391,1 7.6079776,46.5270471,1 7.6093331,46.526585,1 7.610704,46.5264464,1 7.6109659,46.5266158,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.587042,46.4886078,1 7.5874305,46.4882863,1 7.587553,46.4879879,1 7.5876084,46.4877632,1 7.588218,46.4872813,1 7.5886301,46.4867943,1 7.5885713,46.4864604,1 7.5883219,46.4862734,1 7.588105,46.4862347,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Bodenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5655667,46.489498,1 7.5651387,46.4893362,1 7.564879,46.4889907,1 7.5647194,46.4885724,1 7.5641218,46.4881265,1 7.5639912,46.4878728,1 7.5639752,46.4876249,1 7.5637244,46.487204,1 7.5634686,46.4869531,1 7.5632835,46.4867532,1 7.5630315,46.4863658,1 7.5626687,46.4859012,1 7.5625647,46.4856405,1 7.5623769,46.4854461,1 7.5621275,46.4851632,1 7.5619548,46.4849002,1 7.5619024,46.484733,1 7.5618104,46.4844506,1 7.561747,46.4840627,1 7.5614957,46.4825944,1 7.5614079,46.4812731,1 7.561295,46.4811046,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Eselmoosgassa</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5561553,46.4856415,1 7.5557635,46.4856558,1 7.5549529,46.485598,1 7.5547141,46.4854509,1 7.5544736,46.4853845,1 7.5543692,46.4854041,1 7.5542189,46.4854749,1 7.5541303,46.4854825,1 7.5540353,46.4855054,1 7.553853,46.485527,1 7.553726,46.485561,1 7.553685,46.485587,1 7.553433,46.485684,1 7.5532166,46.4856797,1 7.553134,46.485678,1 7.5529402,46.4854934,1 7.5526494,46.4852944,1 7.552326,46.484918,1 7.552172,46.4848,1 7.5520961,46.4847183,1 7.552012,46.484569,1 7.5518939,46.4844236,1 7.5517747,46.4842799,1 7.55168,46.48411,1 7.5513096,46.4837762,1 7.55115,46.483671,1 7.55098,46.483665,1 7.550794,46.483474,1 7.550622,46.483174,1 7.550476,46.483083,1 7.550345,46.483044,1 7.5502534,46.4830306,1 7.549997,46.483104,1 7.549532,46.483097,1 7.549363,46.483085,1 7.549299,46.48311,1 7.5491923,46.4831022,1 7.5491528,46.4830816,1 7.549018,46.482956,1 7.54888,46.482834,1 7.548732,46.482717,1 7.54851,46.482471,1 7.548538,46.48237,1 7.548732,46.48222,1 7.548814,46.482059,1 7.548795,46.481923,1 7.548658,46.481681,1 7.548255,46.481181,1 7.547886,46.481067,1 7.547641,46.481037,1 7.54748,46.480984,1 7.547357,46.480933,1 7.5457456,46.4805227,1 7.545419,46.480471,1 7.5450014,46.4805367,1 7.544647,46.48049,1 7.544299,46.4804,1 7.543866,46.48021,1 7.543796,46.48016,1 7.5434426,46.4799043,1 7.5434381,46.4799008,1 7.542671,46.479313,1 7.542223,46.478988,1 7.541957,46.478758,1 7.54153,46.478338,1 7.54131,46.477934,1 7.541097,46.477749,1 7.540842,46.477564,1 7.54067,46.477405,1 7.54044,46.477109,1 7.540176,46.476572,1 7.5399152,46.4762596,1 7.5393273,46.4757074,1 7.539045,46.475276,1 7.538843,46.475093,1 7.5387556,46.4750208,1 7.5384505,46.4747687,1 7.5382302,46.4743164,1 7.5381288,46.4742361,1 7.5377988,46.4741219,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Flecklistrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.561295,46.4811046,1 7.5614161,46.48107,1 7.5619614,46.4809337,1 7.56233,46.4809424,1 7.563085,46.4812124,1 7.5638695,46.48163,1 7.5642386,46.4822449,1 7.5651023,46.4831244,1 7.5654245,46.4839163,1 7.5657444,46.4842614,1 7.5661292,46.484468,1 7.5666203,46.4846783,1 7.5668295,46.4847669,1 7.5671931,46.484926,1 7.5674652,46.4851221,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fuhrenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5561553,46.4856415,1 7.5568207,46.4854527,1 7.5575266,46.4853598,1 7.5577332,46.4852617,1 7.5579355,46.484896,1 7.5580374,46.4845238,1 7.5577925,46.4836498,1 7.557817,46.4830379,1 7.5583492,46.4819039,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Gspennweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5638695,46.48163,1 7.564176,46.4816392,1 7.5645116,46.4816762,1 7.5650441,46.4817209,1 7.5652433,46.4817563,1 7.5655296,46.4818457,1 7.5658943,46.4819458,1 7.5661964,46.4820121,1 7.5663732,46.4820583,1 7.5665937,46.4821825,1 7.5668676,46.4822782,1 7.5670657,46.4823033,1 7.5673032,46.4822205,1 7.5673958,46.4820244,1 7.567317,46.4817693,1 7.5673377,46.4816797,1 7.5674461,46.4816661,1 7.5675411,46.481702,1 7.5676495,46.4818092,1 7.5677125,46.481968,1 7.5680791,46.4824497,1 7.5684379,46.4828004,1 7.5684393,46.4829933,1 7.5685006,46.4830787,1 7.5686817,46.4831555,1 7.569189,46.4833128,1 7.5693702,46.4834807,1 7.5695485,46.4837657,1 7.5696154,46.4840795,1 7.5697506,46.4843808,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5549954,46.4898085,1 7.553959,46.4889851,1 7.553271,46.4880191,1 7.552624,46.4874431,1 7.551504,46.4870491,1 7.550608,46.4864261,1 7.5496477,46.485998,1 7.549065,46.4855751,1 7.5461028,46.4845429,1 7.545601,46.4843911,1 7.5450642,46.484286,1 7.544646,46.4842091,1 7.544096,46.4842611,1 7.54335,46.4841501,1 7.543024,46.4839871,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kreuzgasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5583492,46.4819039,1 7.558488,46.4819183,1 7.5589982,46.4817042,1 7.5599043,46.4812412,1 7.560118,46.4811965,1 7.561295,46.4811046,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lismiweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5671931,46.484926,1 7.5670729,46.4855393,1 7.5667849,46.4857561,1 7.566834,46.4858727,1 7.5671599,46.4862692,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5658155,46.489718,1 7.5655667,46.489498,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5658668,46.4898255,1 7.5658155,46.489718,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.566834,46.4858727,1 7.5665666,46.4859805,1 7.566584,46.4861788,1 7.5667501,46.4863846,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.561295,46.4811046,1 7.560827,46.4806023,1 7.5607781,46.4791491,1 7.5609247,46.4781599,1 7.5622314,46.476548,1 7.5622314,46.476513,1 7.5622313,46.4764657,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Walezubestrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5674652,46.4851221,1 7.5677176,46.4855439,1 7.5679169,46.4859883,1 7.5686415,46.4865509,1 7.568748,46.486657,1 7.5688367,46.4867031,1 7.5696467,46.487175,1 7.570372,46.4879376,1 7.5704705,46.4881439,1 7.5707587,46.4883279,1 7.5708117,46.4883663,1 7.5709129,46.488434,1 7.5714324,46.4889418,1 7.5718473,46.4897383,1 7.5721035,46.4900381,1 7.5722968,46.4907052,1 7.5725212,46.4911091,1 7.5728347,46.4914564,1 7.5729843,46.4919146,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kuonisbergliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5508319,46.4815667,1 7.550886,46.481604,1 7.551017,46.481662,1 7.5517182,46.4815741,1 7.551993,46.481463,1 7.552258,46.481249,1 7.552478,46.481152,1 7.5528719,46.4810715,1 7.552972,46.481051,1 7.553187,46.480938,1 7.553375,46.480816,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kuonisbergliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5508319,46.4815667,1 7.5509021,46.4815181,1 7.551101,46.481458,1 7.551329,46.481362,1 7.5514522,46.4812387,1 7.551571,46.48097,1 7.551688,46.480578,1 7.551781,46.480405,1 7.551891,46.480258,1 7.5520744,46.4800436,1 7.5524006,46.4797169,1 7.5526215,46.4795293,1 7.5529788,46.4789449,1 7.553007,46.47881,1 7.552982,46.478716,1 7.5529262,46.4786992,1 7.5527854,46.4787285,1 7.5524896,46.4790539,1 7.5523318,46.4792022,1 7.5521392,46.479332,1 7.5518842,46.4794254,1 7.551653,46.479461,1 7.551379,46.479444,1 7.551097,46.479381,1 7.550863,46.479293,1 7.550652,46.479183,1 7.5503496,46.4791053,1 7.550073,46.479072,1 7.549843,46.479024,1 7.549589,46.478927,1 7.549422,46.478836,1 7.549175,46.478782,1 7.549041,46.478774,1 7.548782,46.478793,1 7.548526,46.478898,1 7.548479,46.478902,1 7.548408,46.478825,1 7.548488,46.478774,1 7.548709,46.478677,1 7.548824,46.478546,1 7.549044,46.478409,1 7.549316,46.478319,1 7.5496528,46.4782388,1 7.550112,46.478244,1 7.550378,46.478205,1 7.55059,46.47814,1 7.550712,46.478013,1 7.550768,46.477844,1 7.5507845,46.4777077,1 7.550831,46.477322,1 7.550837,46.476938,1 7.550793,46.476547,1 7.550741,46.47618,1 7.550773,46.4757458,1 7.550804,46.475324,1 7.550842,46.47512,1 7.550847,46.474665,1 7.550828,46.474547,1 7.550455,46.473916,1 7.550251,46.473663,1 7.550181,46.473515,1 7.550134,46.473398,1 7.550109,46.473304,1 7.550087,46.473272,1 7.550023,46.47325,1 7.549945,46.473281,1 7.549934,46.473323,1 7.5499512,46.4734529,1 7.549878,46.473637,1 7.549834,46.473902,1 7.549831,46.473957,1 7.549814,46.474073,1 7.549783,46.474197,1 7.5497672,46.4743214,1 7.549727,46.474393,1 7.549487,46.474707,1 7.5493338,46.4748532,1 7.5493182,46.4748681,1 7.549299,46.4748896,1 7.5492628,46.4749301,1 7.5491141,46.4751996,1 7.548961,46.47533,1 7.548566,46.475574,1 7.548254,46.475715,1 7.548062,46.475728,1 7.547878,46.475707,1 7.547709,46.475654,1 7.547616,46.475589,1 7.547543,46.475472,1 7.547525,46.475404,1 7.547521,46.475189,1 7.547554,46.475035,1 7.547616,46.474977,1 7.547708,46.474933,1 7.547938,46.474861,1 7.548019,46.474747,1 7.5480239,46.4745659,1 7.5479003,46.4743716,1 7.5477933,46.4743096,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Köpflisbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.457797,46.4368213,1 7.4575621,46.4367506,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Köpflisbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4579366,46.4368633,1 7.457797,46.4368213,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Köpflisbergstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4606355,46.4376757,1 7.4579366,46.4368633,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Alte Strasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5778754,46.4954761,1 7.5779939,46.49555,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lischenstrasse</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5778518,46.4959324,1 7.5777961,46.4960149,1 7.5778017,46.4962336,1 7.5779563,46.4963871,1 7.5783668,46.4966863,1 7.5788599,46.4970856,1 7.5788853,46.4971104,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Margeliweg</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5665136,46.4924594,1 7.5665266,46.4925761,1 7.5667785,46.4927724,1 7.5669022,46.4932502,1 7.566879,46.4935537,1 7.5670434,46.4938744,1 7.5672909,46.4940688,1 7.5678329,46.4948354,1 7.5681231,46.4949172,1 7.5682468,46.4954237,1 7.5687843,46.4960333,1 7.569229,46.4964059,1 7.5693856,46.4964512,1 7.5696317,46.496726,1 7.5697815,46.4969634,1 7.5699588,46.4971615,1 7.5702091,46.4972483,1 7.5702308,46.4974098,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Schmittengraben</name>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5663814,46.4923822,1 7.5664566,46.49243,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5648105,46.4927291,1 7.5650265,46.4920778,1 7.5647011,46.4919159,1 7.5645316,46.4919494,1 7.5646492,46.4920872,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Unclassified</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5724996,46.4938582,1 7.5721087,46.4928193,1 7.5718566,46.492604,1 7.5717708,46.4916012,1 7.5715854,46.4913815,1 7.5705649,46.4909123,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Pedestrian</name>
    +          <Style id="style-Pedestrian">
    +            <LineStyle>
    +              <color>#ff808080</color>
    +              <width>3</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark>
    +            <name>Schulgässli</name>
    +            <styleUrl>#style-Pedestrian</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5605367,46.492528,1 7.5595144,46.4930964,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Kronenplatz</name>
    +            <styleUrl>#style-Pedestrian</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4425957,46.4564794,1 7.4425442,46.4567004,1 7.4424533,46.457099,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Pedestrian</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5598352,46.4920944,1 7.5587375,46.4925106,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +        <Folder>
    +          <name>Service</name>
    +          <Style id="style-Service">
    +            <LineStyle>
    +              <color>#ffd0d0d0</color>
    +              <width>2</width>
    +            </LineStyle>
    +          </Style>
    +          <Placemark>
    +            <name>Gartenweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5643603,46.4953787,1 7.5650953,46.4963004,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fitzerweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5575266,46.4853598,1 7.5577103,46.485572,1 7.5583125,46.4854727,1 7.5585068,46.4854474,1 7.5590842,46.4856691,1 7.5593687,46.4856356,1 7.5591302,46.4852633,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Walehältiweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.559232,46.4796016,1 7.5586532,46.4793161,1 7.5582045,46.4789538,1 7.557758,46.4790122,1 7.5577696,46.4790842,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.566314,46.4972084,1 7.5666254,46.4971783,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5676532,46.4982767,1 7.5669653,46.497796,1 7.566752,46.4977228,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5555284,46.4878909,1 7.5553714,46.4877889,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5609767,46.4931868,1 7.5609149,46.493261,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4794227,46.5658312,1 7.4790691,46.5656171,1 7.4789409,46.56562,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4562979,46.441431,1 7.456496,46.4414858,1 7.456975,46.44131,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5028378,46.5395826,1 7.503522,46.5419711,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.574028,46.5050557,1 7.574161,46.5050942,1 7.5740709,46.5051857,1 7.5739397,46.5051162,1 7.574028,46.5050557,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4438332,46.4578509,1 7.4443253,46.4578916,1 7.444375,46.45771,1 7.4444111,46.4575848,1 7.4444794,46.4574163,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5798959,46.4994904,1 7.580177,46.499669,1 7.580039,46.499962,1 7.580111,46.50019,1 7.5802269,46.5002556,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5796209,46.5015556,1 7.5800453,46.5016055,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.577957,46.493648,1 7.578012,46.493478,1 7.578502,46.492951,1 7.5788628,46.4927947,1 7.5789815,46.4928026,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5823413,46.514709,1 7.5826838,46.5145655,1 7.582687,46.5143769,1 7.582609,46.514204,1 7.581843,46.513665,1 7.5816472,46.5136336,1 7.5810861,46.5130171,1 7.5802506,46.512384,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.581843,46.513665,1 7.5821177,46.5135156,1 7.582334,46.513319,1 7.582116,46.512569,1 7.581709,46.511981,1 7.5811311,46.5115993,1 7.5809768,46.5114974,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5840532,46.5197587,1 7.5841373,46.5198684,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4760571,46.4270912,1 7.4761172,46.426896,1 7.4759369,46.4265765,1 7.476203,46.426446,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Krummenbach</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4391746,46.4521405,1 7.4397148,46.4520287,1 7.440477,46.4520745,1 7.4409061,46.4521561,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Krummenbach</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.440477,46.4520745,1 7.440551,46.4518706,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Krummenbach</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4409061,46.4521561,1 7.4416534,46.4523294,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4355302,46.454095,1 7.436013,46.4543413,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4461285,46.4588623,1 7.4465356,46.4580981,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4358702,46.4539509,1 7.4359997,46.4540404,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789815,46.4928026,1 7.5790734,46.4928471,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5919641,46.4868266,1 7.592117,46.4869065,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6059609,46.5289507,1 7.6056635,46.5289968,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592703,46.526096,1 7.593466,46.525483,1 7.5930735,46.5248648,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.592178,46.5287971,1 7.5922406,46.5288691,1 7.5923247,46.529017,1 7.5923721,46.5291939,1 7.5923976,46.5293461,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.578854,46.5249327,1 7.5785473,46.524736,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5615433,46.5277193,1 7.561374,46.5277584,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5771625,46.5297081,1 7.5757467,46.5295395,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Hahnenmoosstrasse</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5132733,46.4572948,1 7.5133883,46.4573978,1 7.5132933,46.4574818,1 7.5130673,46.4573498,1 7.5126303,46.4572888,1 7.5118483,46.4574828,1 7.5101523,46.4576848,1 7.5095283,46.4576228,1 7.5085313,46.4572698,1 7.5080333,46.4572118,1 7.5069933,46.4572868,1 7.5061813,46.4572468,1 7.5057723,46.4574628,1 7.5058393,46.4578288,1 7.5087808,46.4574575,1 7.5031103,46.4577448,1 7.5030413,46.4578388,1 7.5034833,46.4581518,1 7.5034483,46.4582518,1 7.5024893,46.4581448,1 7.5003853,46.4569498,1 7.5003883,46.4567438,1 7.5005073,46.4565828,1 7.5007043,46.4565128,1 7.5007173,46.4564108,1 7.4998403,46.4561428,1 7.4992083,46.4558578,1 7.4989973,46.4558668,1 7.4982906,46.4562011,1 7.4978551,46.455962,1 7.4974551,46.455427,1 7.4968953,46.4543278,1 7.4966573,46.4542338,1 7.4961843,46.4543818,1 7.4960133,46.4543198,1 7.4959533,46.4541388,1 7.4965103,46.4536988,1 7.4964933,46.4536218,1 7.4953603,46.4533988,1 7.494721,46.453113,1 7.494109,46.453374,1 7.493658,46.453437,1 7.492815,46.453277,1 7.4922764,46.4533365,1 7.49174,46.4534252,1 7.4912679,46.453477,1 7.4910962,46.4533809,1 7.4906778,46.453307,1 7.489497,46.453256,1 7.488978,46.45327,1 7.487949,46.453519,1 7.486012,46.453411,1 7.483716,46.45287,1 7.482943,46.452812,1 7.482147,46.452909,1 7.4814,46.453269,1 7.481139,46.453269,1 7.480528,46.453029,1 7.479955,46.452984,1 7.4796057,46.4531296,1 7.479434,46.4531739,1 7.4792896,46.45308,1 7.4792194,46.4529744,1 7.4790478,46.4528931,1 7.478841,46.453033,1 7.47874,46.453293,1 7.477474,46.453309,1 7.476344,46.453107,1 7.475372,46.453488,1 7.474272,46.453536,1 7.473791,46.453454,1 7.472928,46.452962,1 7.472723,46.452998,1 7.472366,46.453213,1 7.4716234,46.4532996,1 7.4712372,46.453307,1 7.4709689,46.4532922,1 7.4707544,46.4533439,1 7.4705183,46.4533144,1 7.4703038,46.4532183,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5751846,46.4916084,1 7.5749365,46.491503,1 7.5747446,46.4914272,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5769663,46.4921038,1 7.576441,46.491776,1 7.576364,46.491621,1 7.576362,46.491427,1 7.5763871,46.4912304,1 7.5763182,46.4911237,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5795925,46.490413,1 7.5794074,46.4902555,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5734897,46.4935177,1 7.573538,46.4934839,1 7.5735483,46.493418,1 7.573556,46.4933099,1 7.5735755,46.4932551,1 7.573676,46.4931932,1 7.5738286,46.4931243,1 7.5739371,46.4931082,1 7.574012,46.493094,1 7.5741504,46.4930548,1 7.5742015,46.4930235,1 7.5742826,46.4928928,1 7.574376,46.492803,1 7.574421,46.492763,1 7.574598,46.492717,1 7.574774,46.492685,1 7.5749459,46.492669,1 7.5751353,46.4926314,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.574376,46.492803,1 7.5746354,46.4929073,1 7.5748091,46.4930498,1 7.5749942,46.4932398,1 7.5752138,46.4933824,1 7.5754645,46.4934693,1 7.5757087,46.4935283,1 7.5761725,46.4935343,1 7.5763535,46.4935589,1 7.5764212,46.4936211,1 7.576495,46.493829,1 7.576602,46.494096,1 7.576795,46.494181,1 7.5770454,46.4942655,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5849078,46.5347552,1 7.5849885,46.5347378,1 7.585183,46.534753,1 7.5855229,46.5347513,1 7.5856717,46.5347707,1 7.585943,46.5348464,1 7.586016,46.5348314,1 7.58602,46.5347903,1 7.5858345,46.5346537,1 7.5856432,46.5344437,1 7.5856581,46.5344076,1 7.5857148,46.5344078,1 7.5859478,46.5345439,1 7.5861633,46.5345952,1 7.586366,46.5346123,1 7.586484,46.534542,1 7.5866893,46.5343725,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.587059,46.5342825,1 7.587135,46.534398,1 7.5871929,46.5344569,1 7.5873657,46.5345937,1 7.587636,46.534746,1 7.5878421,46.5348345,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Falkiweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.561747,46.4840627,1 7.5623648,46.4840221,1 7.5624662,46.4841424,1 7.562591,46.4842295,1 7.5630513,46.4842235,1 7.5633106,46.4842166,1 7.5634827,46.4841367,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5623769,46.4854461,1 7.5622912,46.4855938,1 7.5622704,46.4860729,1 7.5621787,46.4861073,1 7.5619413,46.4860499,1 7.5615122,46.4859008,1 7.5611623,46.4859065,1 7.5609581,46.485938,1 7.5608373,46.4860184,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5622704,46.4860729,1 7.5623863,46.4862233,1 7.5624681,46.4863847,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5556026,46.4788917,1 7.5555477,46.479214,1 7.5556151,46.4795353,1 7.5556149,46.4801761,1 7.5556517,46.4804555,1 7.5557955,46.4808898,1 7.5558411,46.4812561,1 7.5558839,46.4816013,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5548512,46.4833925,1 7.5550889,46.4838028,1 7.5552822,46.4840196,1 7.5554987,46.4843666,1 7.5556369,46.4845961,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5601552,46.4830974,1 7.560116,46.4826468,1 7.5614957,46.4825944,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Falkiweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5630513,46.4842235,1 7.5630409,46.4844811,1 7.5628733,46.4846662,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Birkenweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.560472,46.4841615,1 7.5604406,46.4845196,1 7.5602381,46.4846229,1 7.5596029,46.4846037,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Birkenweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5610374,46.4841326,1 7.5611142,46.4843658,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Brunniweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5567689,46.4813658,1 7.5568027,46.4816531,1 7.5569895,46.4818235,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5566823,46.4811255,1 7.5567689,46.4813658,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Egghausweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.562353,46.4820061,1 7.5622413,46.4825617,1 7.5614957,46.4825944,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5783668,46.4966863,1 7.5779866,46.4968608,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5788599,46.4970856,1 7.578559,46.497615,1 7.5784905,46.4976816,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5804891,46.4976849,1 7.5804732,46.4976377,1 7.580323,46.4974715,1 7.580186,46.4973576,1 7.580208,46.4972438,1 7.5802253,46.4971513,1 7.5802103,46.4970622,1 7.5801714,46.4970016,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.580573,46.518538,1 7.580476,46.5182,1 7.580281,46.517923,1 7.580272,46.517884,1 7.5803133,46.5178587,1 7.580384,46.517862,1 7.580533,46.518081,1 7.580644,46.518193,1 7.580787,46.518268,1 7.580881,46.518395,1 7.581021,46.518517,1 7.581108,46.518651,1 7.581235,46.518762,1 7.581369,46.519014,1 7.5815,46.519189,1 7.581947,46.519404,1 7.582104,46.519512,1 7.582187,46.519603,1 7.582507,46.519782,1 7.58259,46.519851,1 7.582661,46.5198661,1 7.5827033,46.5198324,1 7.582691,46.5197745,1 7.582629,46.519707,1 7.582365,46.519429,1 7.5823268,46.5191792,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5532166,46.4856797,1 7.553475,46.485585,1 7.553817,46.485256,1 7.553812,46.485215,1 7.5536868,46.4849435,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5615007,46.4706892,1 7.5613997,46.470631,1 7.5607353,46.4711441,1 7.560183,46.471455,1 7.5601093,46.4714637,1 7.560081,46.4714134,1 7.5602488,46.4711103,1 7.5603802,46.4708687,1 7.5608448,46.4705818,1 7.5616215,46.4698555,1 7.5617283,46.4695452,1 7.5616912,46.4694069,1 7.5616128,46.4693072,1 7.5614945,46.469236,1 7.5613456,46.469107,1 7.5613033,46.4689269,1 7.5615058,46.4686482,1 7.5616043,46.468364,1 7.5615151,46.4679643,1 7.5616194,46.4674144,1 7.5614199,46.4671432,1 7.5613818,46.4669685,1 7.5615864,46.4665648,1 7.5615864,46.4661599,1 7.5616687,46.4659865,1 7.5616526,46.4658976,1 7.5615269,46.4658976,1 7.5615024,46.4659185,1 7.5614258,46.4660844,1 7.560844,46.466777,1 7.5606614,46.4668815,1 7.5604401,46.4670332,1 7.56013,46.4673879,1 7.559733,46.467773,1 7.55952,46.4679289,1 7.5594277,46.467948,1 7.5593446,46.4678938,1 7.559371,46.467807,1 7.5596978,46.4670772,1 7.5597256,46.4668715,1 7.559637,46.466644,1 7.5596008,46.4663719,1 7.5595604,46.4659766,1 7.5594765,46.4656802,1 7.5595737,46.465423,1 7.559713,46.465291,1 7.5598281,46.4651508,1 7.559757,46.464943,1 7.559711,46.464879,1 7.5595499,46.4645658,1 7.5594913,46.4644275,1 7.559382,46.464309,1 7.559302,46.4642619,1 7.559013,46.464152,1 7.5589202,46.4640915,1 7.5588007,46.4639848,1 7.558761,46.463881,1 7.5586757,46.4636012,1 7.558588,46.4634941,1 7.55817,46.463211,1 7.5578807,46.4630595,1 7.5575283,46.4628252,1 7.5573015,46.4626567,1 7.5571541,46.4625209,1 7.5570332,46.4624804,1 7.5569236,46.4625235,1 7.5568688,46.4626006,1 7.5569142,46.4628709,1 7.5567834,46.4630751,1 7.5567486,46.463256,1 7.5567563,46.4635207,1 7.5567409,46.4637356,1 7.556819,46.464071,1 7.556927,46.46428,1 7.5569515,46.464646,1 7.5569489,46.4648344,1 7.5570969,46.4651071,1 7.557132,46.465248,1 7.5571481,46.4654357,1 7.5571032,46.4656238,1 7.556845,46.4661,1 7.556808,46.466241,1 7.556705,46.466662,1 7.5566013,46.4668594,1 7.556422,46.4670092,1 7.556133,46.4671663,1 7.555967,46.467302,1 7.555582,46.46755,1 7.5553411,46.4676536,1 7.555261,46.4676825,1 7.555232,46.467693,1 7.55507,46.467698,1 7.555064,46.467579,1 7.555189,46.467454,1 7.555266,46.467329,1 7.555253,46.467204,1 7.555198,46.46708,1 7.555171,46.46692,1 7.5551885,46.4667743,1 7.555143,46.466618,1 7.555043,46.466467,1 7.554902,46.46618,1 7.5547665,46.4659837,1 7.5546718,46.4658163,1 7.554613,46.465756,1 7.554335,46.465537,1 7.553879,46.46527,1 7.55364,46.465173,1 7.553503,46.465092,1 7.553265,46.464872,1 7.553112,46.464693,1 7.553025,46.464559,1 7.552984,46.464428,1 7.552966,46.464301,1 7.553023,46.464129,1 7.553046,46.463964,1 7.55301,46.463811,1 7.552957,46.463687,1 7.552835,46.46356,1 7.552786,46.463439,1 7.552644,46.462916,1 7.552526,46.462782,1 7.552398,46.462681,1 7.552202,46.462578,1 7.552022,46.462506,1 7.551384,46.462183,1 7.551265,46.46208,1 7.550822,46.461898,1 7.550655,46.461816,1 7.5505555,46.461797,1 7.550536,46.461845,1 7.5506,46.461947,1 7.550775,46.462053,1 7.550877,46.462166,1 7.550961,46.462315,1 7.551074,46.462481,1 7.551251,46.462677,1 7.551333,46.462809,1 7.551424,46.463083,1 7.55148,46.463166,1 7.5514055,46.4633987,1 7.5513305,46.4635485,1 7.551122,46.463773,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5601093,46.4714637,1 7.5595884,46.4717158,1 7.559275,46.47182,1 7.5589672,46.4720202,1 7.5584806,46.4723453,1 7.5581622,46.4724206,1 7.55787,46.472668,1 7.5575608,46.4728455,1 7.55748,46.472929,1 7.557326,46.473397,1 7.557232,46.473534,1 7.557135,46.473651,1 7.556988,46.473764,1 7.5568899,46.4738759,1 7.556803,46.4740459,1 7.556738,46.474206,1 7.5567085,46.4744798,1 7.5567473,46.4745861,1 7.556734,46.474669,1 7.5566457,46.4748183,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.552982,46.478716,1 7.553269,46.477763,1 7.5534638,46.4774581,1 7.5535454,46.4773304,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4714196,46.4524422,1 7.4714839,46.4523756,1 7.4714947,46.4522943,1 7.4714517,46.4522278,1 7.471441,46.4519617,1 7.4716556,46.4516365,1 7.4717951,46.4512964,1 7.4718916,46.4508529,1 7.472471,46.4500102,1 7.472825,46.449308,1 7.473222,46.4487684,1 7.4733722,46.4486501,1 7.4739087,46.4484061,1 7.4745953,46.4483027,1 7.4755072,46.4481696,1 7.4758828,46.4480291,1 7.4760008,46.447933,1 7.4758077,46.4478222,1 7.4754751,46.4477556,1 7.4751532,46.4476817,1 7.4750459,46.447593,1 7.4750996,46.4475412,1 7.4751425,46.4474895,1 7.4754429,46.4474969,1 7.4758291,46.4475486,1 7.4762046,46.4475043,1 7.4766016,46.4474082,1 7.4769986,46.4473638,1 7.477299,46.4472751,1 7.477535,46.4471864,1 7.4776208,46.4469942,1 7.4776208,46.4468611,1 7.4776745,46.4466763,1 7.4779105,46.4465507,1 7.4781358,46.4465285,1 7.4782967,46.4465359,1 7.4785113,46.4464989,1 7.4786079,46.4463954,1 7.4785972,46.446255,1 7.4785113,46.446011,1 7.4784791,46.4457966,1 7.4783718,46.4455748,1 7.4783397,46.4452274,1 7.4785972,46.4445473,1 7.4788546,46.444015,1 7.4792409,46.4433053,1 7.4794233,46.443187,1 7.4797773,46.4429578,1 7.4803352,46.4426547,1 7.4806893,46.4423663,1 7.4811077,46.4422259,1 7.481569,46.4420337,1 7.4818587,46.4419893,1 7.4825346,46.4418858,1 7.4835753,46.4416936,1 7.4840367,46.441627,1 7.4842401,46.44165,1 7.4841654,46.4418562,1 7.4838543,46.4422554,1 7.4837899,46.4425512,1 7.483629,46.4430021,1 7.4835861,46.44332,1 7.4836826,46.4435566,1 7.4838436,46.4436158,1 7.4840152,46.4435418,1 7.4840796,46.4434679,1 7.4843264,46.4433718,1 7.4845302,46.4433718,1 7.4863434,46.4436084,1 7.4868691,46.4436823,1 7.4872124,46.4436971,1 7.487427,46.4436453,1 7.4875128,46.4435566,1 7.487545,46.4434975,1 7.4877703,46.4432757,1 7.4879634,46.4432535,1 7.48806,46.4432831,1 7.488178,46.4437045,1 7.4881029,46.4441333,1 7.4880493,46.4444216,1 7.4881887,46.4446286,1 7.4883282,46.4447173,1 7.4884845,46.4446966,1 7.4886072,46.4446803,1 7.4886823,46.4446064,1 7.4891972,46.4443329,1 7.4898732,46.444148,1 7.4904954,46.4440593,1 7.4909568,46.4440889,1 7.4914288,46.4441111,1 7.4917722,46.4440741,1 7.4921048,46.4440889,1 7.4923622,46.4441702,1 7.4928021,46.4442072,1 7.4933922,46.444222,1 7.4938009,46.4443235,1 7.4940467,46.4443846,1 7.4945187,46.4444733,1 7.494787,46.4444585,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.4938009,46.4443235,1 7.4948406,46.4440224,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5134953,46.4566158,1 7.5133908,46.4565887,1 7.513144,46.4564409,1 7.5130367,46.4562857,1 7.5130474,46.4560714,1 7.5129509,46.4559753,1 7.5124574,46.4556575,1 7.5121891,46.4554875,1 7.5120282,46.4554431,1 7.5115025,46.4553766,1 7.5107515,46.4552509,1 7.5101292,46.4549109,1 7.5096357,46.4545192,1 7.5092709,46.4540831,1 7.5091743,46.4537948,1 7.5093889,46.4534992,1 7.5097215,46.4531665,1 7.5098181,46.4527452,1 7.5101936,46.4521243,1 7.5107622,46.4518434,1 7.5111484,46.4515995,1 7.5112879,46.4512447,1 7.5114596,46.4508234,1 7.5114167,46.4506312,1 7.5113201,46.450439,1 7.5110626,46.4502394,1 7.5107837,46.4502837,1 7.5105369,46.4504981,1 7.510215,46.4506903,1 7.5098824,46.4507273,1 7.5095928,46.4506016,1 7.5090349,46.4503503,1 7.5086057,46.4502098,1 7.5082195,46.4501803,1 7.507962,46.450269,1 7.5075757,46.4503429,1 7.5073182,46.4503207,1 7.5068247,46.4503503,1 7.5066638,46.4502985,1 7.5064472,46.4502726,1 7.5063185,46.4501986,1 7.5061253,46.4500952,1 7.5059751,46.4499621,1 7.5058571,46.4498438,1 7.5055674,46.4495408,1 7.5053529,46.4493633,1 7.5051168,46.4493338,1 7.5049023,46.4492672,1 7.5047091,46.4492081,1 7.504398,46.4491785,1 7.5041727,46.4492155,1 7.5037543,46.4492599,1 7.5035075,46.4491416,1 7.5034002,46.4490233,1 7.5033466,46.448905,1 7.5032929,46.4487794,1 7.5032071,46.448395,1 7.50325,46.4480771,1 7.5034324,46.4477001,1 7.5035612,46.4474931,1 7.5036041,46.4473896,1 7.5035826,46.4472787,1 7.5035182,46.4472639,1 7.5034002,46.4472492,1 7.5033359,46.4471752,1 7.5033359,46.4470865,1 7.5033788,46.4470126,1 7.5034217,46.4469461,1 7.5035397,46.4469609,1 7.5037006,46.4470126,1 7.5037757,46.4469756,1 7.5038508,46.4468278,1 7.5038723,46.4466873,1 7.5037757,46.4465543,1 7.5037757,46.4464582,1 7.5037543,46.4463621,1 7.5037972,46.4462586,1 7.504001,46.4461994,1 7.5043122,46.4461477,1 7.5045804,46.4462068,1 7.5048915,46.4462881,1 7.5050847,46.4463177,1 7.50531,46.4462807,1 7.5054602,46.446229,1 7.5055996,46.4462881,1 7.5057069,46.4464434,1 7.5057713,46.4466504,1 7.5057713,46.4468869,1 7.5055996,46.4470717,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5346943,46.4673158,1 7.5349879,46.4672014,1 7.5352239,46.4672753,1 7.5352454,46.4675413,1 7.5350737,46.4678812,1 7.5349665,46.4680881,1 7.5350523,46.4683246,1 7.5352454,46.4685759,1 7.5356746,46.4688862,1 7.5363827,46.4692409,1 7.5369191,46.4694921,1 7.5374341,46.4696252,1 7.5378632,46.4696547,1 7.5380993,46.4696252,1 7.5383353,46.469433,1 7.5383782,46.4692261,1 7.5380564,46.468295,1 7.5380993,46.4679847,1 7.5384211,46.4675856,1 7.5388718,46.4671718,1 7.5389039,46.4670462,1 7.5388503,46.466928,1 7.5386786,46.4667506,1 7.5384641,46.4663146,1 7.5384426,46.4659156,1 7.5382066,46.4653983,1 7.5380134,46.465014,1 7.5379491,46.4646741,1 7.5378847,46.4643637,1 7.5380564,46.4639794,1 7.538228,46.4636099,1 7.5383782,46.4634769,1 7.5384962,46.4634695,1 7.5385713,46.4635064,1 7.5385713,46.4636986,1 7.5383568,46.4640089,1 7.5384211,46.4642306,1 7.5385606,46.4644154,1 7.5385713,46.4647627,1 7.5386357,46.4649697,1 7.5392365,46.4656052,1 7.54076,46.467157,1 7.540878,46.4673122,1 7.5409531,46.4675856,1 7.5410819,46.4682433,1 7.5411999,46.4683837,1 7.5414038,46.4685906,1 7.5417256,46.4687236,1 7.5422406,46.4689749,1 7.5425625,46.4693591,1 7.542777,46.4697729,1 7.5427341,46.4699355,1 7.54282,46.4700981,1 7.5428843,46.4701941,1 7.5430345,46.4702311,1 7.5431204,46.4701498,1 7.5431311,46.4700463,1 7.5430775,46.4699207,1 7.5430775,46.4697729,1 7.5430775,46.4695586,1 7.5430238,46.4692409,1 7.5428951,46.4688419,1 7.542541,46.4683394,1 7.5424337,46.4679551,1 7.5423694,46.4674083,1 7.5423694,46.4669354,1 7.5421548,46.4666102,1 7.5417685,46.4663442,1 7.5415325,46.4660042,1 7.5414467,46.4657382,1 7.5415754,46.4652948,1 7.5417471,46.4648958,1 7.5421762,46.4642306,1 7.5424123,46.4639942,1 7.5425625,46.4636986,1 7.5425196,46.4634473,1 7.5424981,46.463196,1 7.5426698,46.46293,1 7.5429702,46.4626787,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5090349,46.4503503,1 7.5091616,46.4501765,1 7.5092153,46.4499695,1 7.5092904,46.4498586,1 7.5095586,46.4497551,1 7.5096873,46.449659,1 7.5098268,46.4495186,1 7.5099234,46.4493855,1 7.5100414,46.4492746,1 7.5102881,46.4492599,1 7.510374,46.4491933,1 7.5104705,46.4490677,1 7.5107495,46.4489124,1 7.5110177,46.4488976,1 7.5112752,46.4488829,1 7.5115112,46.4488755,1 7.5117258,46.4488976,1 7.5119618,46.4489716,1 7.512273,46.448979,1 7.5126163,46.4491046,1 7.5127772,46.4492672,1 7.5129274,46.4493929,1 7.5131098,46.4494964,1 7.5130991,46.4496295,1 7.5130669,46.4497699,1 7.5131527,46.4499325,1 7.5134424,46.450036,1 7.5136033,46.4501543,1 7.5137321,46.4503243,1 7.5138608,46.4505091,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5675205,46.4896429,1 7.5675839,46.4901911,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5613927,46.5267583,1 7.5616121,46.5269084,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789126,46.5017241,1 7.5785017,46.5014825,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789309,46.500105,1 7.5788346,46.4997917,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5791699,46.4928879,1 7.5790734,46.4928471,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5789309,46.500105,1 7.5786124,46.5000666,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5844175,46.5180817,1 7.5843919,46.5179006,1 7.5842967,46.5176599,1 7.584072,46.5172756,1 7.583998,46.516732,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5747518,46.4923365,1 7.5747561,46.4922525,1 7.5748319,46.4921223,1 7.5748181,46.4918,1 7.574828,46.4916949,1 7.574893,46.4916664,1 7.5750862,46.4916562,1 7.5751846,46.4916084,1 7.5752826,46.491563,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5856684,46.5185937,1 7.585732,46.518904,1 7.585997,46.519247,1 7.5863,46.519441,1 7.5866689,46.5195382,1 7.586981,46.519691,1 7.587645,46.520291,1 7.587742,46.520478,1 7.5876898,46.5205699,1 7.58757,46.5206516,1 7.587464,46.5207519,1 7.587397,46.520905,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5871749,46.5189125,1 7.5864254,46.5189694,1 7.5859396,46.5188548,1 7.5856684,46.5185937,1 7.5852648,46.5183663,1 7.5851079,46.5182339,1 7.5848608,46.5181009,1 7.5844175,46.5180817,1 7.5841229,46.5179971,1 7.5839457,46.5179174,1 7.5837708,46.5177703,1 7.5837251,46.5176286,1 7.5836592,46.5174623,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5802588,46.5160395,1 7.5805381,46.5164442,1 7.5811876,46.5165553,1 7.5812782,46.5165868,1 7.5820222,46.5167732,1 7.5823119,46.5170409,1 7.5823387,46.5174783,1 7.5824085,46.5176703,1 7.5828028,46.5180929,1 7.5833177,46.5182535,1 7.5835431,46.5183753,1 7.5837979,46.5183661,1 7.5840124,46.5183144,1 7.5834867,46.5181206,1 7.5833553,46.5179767,1 7.583358,46.517626,1 7.5834867,46.5174323,1 7.5836592,46.5174623,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6025022,46.5497569,1 7.6020138,46.5498841,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.6122192,46.5585079,1 7.6125202,46.5582846,1 7.6125382,46.5581503,1 7.6124765,46.5580017,1 7.61231,46.557889,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5875485,46.5190239,1 7.5871749,46.5189125,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5620419,46.4912371,1 7.5627963,46.4915881,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5853231,46.5118698,1 7.5867416,46.5114409,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5808419,46.5032417,1 7.5806755,46.503651,1 7.5807632,46.5038849,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5833344,46.5060923,1 7.5829935,46.505959,1 7.582733,46.50693,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5619716,46.4909369,1 7.562116,46.4908836,1 7.5624485,46.4911228,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5845033,46.511303,1 7.5847716,46.5113253,1 7.5849493,46.5116193,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5925689,46.5257627,1 7.592703,46.526096,1 7.5926549,46.5263226,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5823956,46.5048989,1 7.5822898,46.5036369,1 7.5815068,46.5029364,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5890023,46.5205153,1 7.5884929,46.5201995,1 7.5883311,46.5199786,1 7.5882242,46.5196358,1 7.5875485,46.5190239,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5709768,46.4961208,1 7.5710612,46.4963304,1 7.5713268,46.4965265,1 7.5716017,46.497285,1 7.5720307,46.4977428,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.588105,46.4862347,1 7.5882589,46.4861244,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Brunniweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5583492,46.4819039,1 7.55795,46.481785,1 7.5573748,46.4816562,1 7.5568027,46.4816531,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Fuhrenstrasse</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5535228,46.4865284,1 7.5540973,46.4860504,1 7.5553724,46.4858594,1 7.5555516,46.4857964,1 7.555751,46.4857822,1 7.5561161,46.4856628,1 7.5561553,46.4856415,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Rörliweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5666203,46.4846783,1 7.5665448,46.4847291,1 7.5656341,46.4848814,1 7.5658042,46.4853983,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5589982,46.4817042,1 7.5590716,46.4818306,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.566311,46.4842938,1 7.5661292,46.484468,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5668295,46.4847669,1 7.5669459,46.4845543,1 7.566928,46.4842878,1 7.566311,46.4842938,1 7.5661779,46.4842949,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5616769,46.4873407,1 7.5618759,46.4873603,1 7.562336,46.4875068,1 7.5629211,46.4878619,1 7.5633173,46.488305,1 7.5638849,46.4885749,1 7.5651387,46.4893362,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Strubelweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5561553,46.4856415,1 7.5560704,46.4854118,1 7.5559655,46.4852385,1 7.5558801,46.4850392,1 7.5558516,46.4849379,1 7.5556369,46.4845961,1 7.5557556,46.4835758,1 7.5556465,46.4832228,1 7.5556302,46.482438,1 7.5556902,46.4823253,1 7.5559629,46.481946,1 7.5559405,46.4817142,1 7.5558839,46.4816013,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <name>Lärcheweg</name>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5611634,46.4903397,1 7.5610451,46.4902373,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5737082,46.4907245,1 7.5735328,46.490616,1 7.5733673,46.4903853,1 7.5733062,46.4902442,1 7.5732825,46.4900712,1 7.5733348,46.4898921,1 7.5733339,46.4894668,1 7.5732422,46.4892976,1 7.5730973,46.4891714,1 7.5728372,46.4890235,1 7.5726529,46.489033,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5696679,46.4904828,1 7.5696337,46.4903928,1 7.5694953,46.4902536,1 7.5693484,46.4900982,1 7.5692716,46.489935,1 7.5692449,46.4897418,1 7.569231,46.4895346,1 7.5691648,46.4893786,1 7.5689467,46.4891493,1 7.5688378,46.4889221,1 7.5689846,46.4887416,1 7.5687855,46.4885286,1 7.5687156,46.4884763,1 7.5686163,46.4882459,1 7.5685316,46.4880573,1 7.5684612,46.487926,1 7.5683453,46.4878612,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5739091,46.4841923,1 7.5735123,46.4842222,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5707587,46.4883279,1 7.5714861,46.4882473,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5737649,46.4942215,1 7.5729909,46.4941247,1 7.5724996,46.4938582,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5686415,46.4865509,1 7.5685948,46.4864379,1 7.5685481,46.4863398,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5687156,46.4884763,1 7.5686259,46.4885828,1 7.5686407,46.4886975,1 7.5688378,46.4889221,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5601168,46.4909892,1 7.5595847,46.4906371,1 7.5594989,46.4906215,1 7.5593649,46.490633,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5708117,46.4883663,1 7.5710165,46.488705,1 7.5710179,46.4888509,1 7.5708758,46.4889223,1 7.5708124,46.4889286,1 7.5707761,46.4888902,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5740561,46.4844231,1 7.5739091,46.4841923,1 7.5738582,46.4838387,1 7.5738109,46.4836506,1 7.5736241,46.4835863,1 7.5733468,46.4835758,1 7.5730751,46.4834722,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.5778322,46.4954612,1 7.5774594,46.4953694,1 7.57696,46.4953792,1 7.5765186,46.4953534,1 7.5761855,46.4952056,1 7.5756624,46.4951092,1 7.5748615,46.4951854,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +          <Placemark>
    +            <styleUrl>#style-Service</styleUrl>
    +            <LineString>
    +              <altitudeMode>relativeToGround</altitudeMode>
    +              <coordinates>7.568748,46.486657,1 7.5687618,46.4868008,1 7.5686633,46.4868728,1 7.5683637,46.4869243,1 7.5682553,46.4869895,1 7.5682593,46.4871482,1 7.5683489,46.4873599,1 7.5683539,46.4874461,1</coordinates>
    +            </LineString>
    +          </Placemark>
    +        </Folder>
    +      </Folder>
    +      <Folder>
    +        <name>Casings</name>
    +        <Folder>
    +          <name>Motorway_Casing</name>
    +          <Style id="style-Motorway_Casing">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>7</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Motorway_Link_Casing</name>
    +          <Style id="style-Motorway_Link_Casing">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>4</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Trunk_Casing</name>
    +          <Style id="style-Trunk_Casing">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>7</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +        <Folder>
    +          <name>Trunk_Link_Casing</name>
    +          <Style id="style-Trunk_Link_Casing">
    +            <LineStyle>
    +              <color>#ff000000</color>
    +              <width>4</width>
    +            </LineStyle>
    +          </Style>
    +        </Folder>
    +      </Folder>
    +    </Folder>
    +  </Document>
    +</kml>
    diff --git a/mapbox-gl/examples/data/kml/2012_Earthquakes_Mag5.kml b/mapbox-gl/examples/data/kml/2012_Earthquakes_Mag5.kml
    new file mode 100644
    index 0000000000..7a8466f5cb
    --- /dev/null
    +++ b/mapbox-gl/examples/data/kml/2012_Earthquakes_Mag5.kml
    @@ -0,0 +1,10758 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<kml xmlns="http://earth.google.com/kml/2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    +    <Document>
    +        <name>2012 Earthquakes, Magnitude 5</name>
    +        <atom:author>
    +            <atom:name>U.S. Geological Survey</atom:name>
    +        </atom:author>
    +        <atom:link href="http://earthquake.usgs.gov"/>
    +        <Folder>
    +            <name>Magnitude 5</name>
    +            <Placemark id="2012 Jan 15 13:40:16.40 UTC">
    +                <name>M 5.9 - 2012 Jan 15, SOUTH SHETLAND ISLANDS</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-56.072,-60.975,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 06:48:48.75 UTC">
    +                <name>M 5.9 - 2012 Jan 19, OFF W. COAST OF S. ISLAND, N.Z.</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>165.778,-46.686,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 17:42:51.72 UTC">
    +                <name>M 5.9 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-177.386,-29.43,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 18:52:48.39 UTC">
    +                <name>M 5.9 - 2012 Feb 9, MACQUARIE ISLAND REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>157.837,-58.312,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 10 01:47:34.19 UTC">
    +                <name>M 5.9 - 2012 Feb 10, FIJI REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-178.549,-17.968,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 10:55:09.44 UTC">
    +                <name>M 5.9 - 2012 Feb 13, COSTA RICA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-84.121,9.183,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 02:35:00.53 UTC">
    +                <name>M 5.9 - 2012 Feb 26, TAIWAN</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>120.891,22.661,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 8 22:50:08.41 UTC">
    +                <name>M 5.9 - 2012 Mar 8, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>81.307,39.383,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 23:46:35.40 UTC">
    +                <name>M 5.9 - 2012 Mar 19, SAMOA ISLANDS REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-175.702,-14.621,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:01:06.75 UTC">
    +                <name>M 5.9 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>89.441,2.199,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 22:28:59.18 UTC">
    +                <name>M 5.9 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>93.821,3.269,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 10 02:13:56.16 UTC">
    +                <name>M 5.9 - 2012 May 10, EASTER ISLAND REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-112.587,-28.73,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 19 19:05:18.97 UTC">
    +                <name>M 5.9 - 2012 May 19, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>143.311,39.665,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 23 15:02:25.31 UTC">
    +                <name>M 5.9 - 2012 May 23, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>142.082,41.335,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 23 23:19:18.05 UTC">
    +                <name>M 5.9 - 2012 May 23, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>139.536,-50.352,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 28 21:47:09.40 UTC">
    +                <name>M 5.9 - 2012 May 28, TONGA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-175.976,-19.962,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 2 07:52:53.99 UTC">
    +                <name>M 5.9 - 2012 Jun 2, SALTA, ARGENTINA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-63.555,-22.059,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 4 11:18:13.45 UTC">
    +                <name>M 5.9 - 2012 Jun 4, JAVA, INDONESIA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>106.371,-7.692,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 21:00:18.07 UTC">
    +                <name>M 5.9 - 2012 Jun 9, TAIWAN REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>122.248,24.572,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 16 22:18:47.47 UTC">
    +                <name>M 5.9 - 2012 Jun 16, LUZON, PHILIPPINES</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>119.563,15.593,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 18 04:25:25.60 UTC">
    +                <name>M 5.9 - 2012 Jul 18, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-128.893,-55.661,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 18 08:09:46.20 UTC">
    +                <name>M 5.9 - 2012 Jul 18, FIJI REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-178.508,-20.872,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 06:11:57.91 UTC">
    +                <name>M 5.9 - 2012 Jul 21, VANUATU REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>173.789,-19.19,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 29 12:22:11.81 UTC">
    +                <name>M 5.9 - 2012 Jul 29, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-92.294,14.191,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 8 10:57:44.30 UTC">
    +                <name>M 5.9 - 2012 Aug 8, FIJI</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>178.062,-16.02,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 25 14:16:17.08 UTC">
    +                <name>M 5.9 - 2012 Aug 25, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>142.913,42.419,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 05:39:37.05 UTC">
    +                <name>M 5.9 - 2012 Sep 9, KURIL ISLANDS</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>155.75,49.247,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 16:36:49.84 UTC">
    +                <name>M 5.9 - 2012 Sep 11, SOUTH OF MARIANA ISLANDS</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>143.218,11.838,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 8 06:26:23.02 UTC">
    +                <name>M 5.9 - 2012 Oct 8, GULF OF CALIFORNIA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-109.574,25.127,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 18 01:27:16.02 UTC">
    +                <name>M 5.9 - 2012 Oct 18, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>144.006,-54.217,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 23 08:53:38.24 UTC">
    +                <name>M 5.9 - 2012 Oct 23, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>139.251,29.057,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 15 08:21:48.45 UTC">
    +                <name>M 5.9 - 2012 Nov 15, BISMARCK SEA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>148.27,-3.109,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 17 05:12:56.77 UTC">
    +                <name>M 5.9 - 2012 Nov 17, TONGA REGION</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-172.32,-18.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 21 21:36:22.74 UTC">
    +                <name>M 5.9 - 2012 Nov 21, LIBERTADOR O'HIGGINS, CHILE</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-71.868,-33.939,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 22 13:07:10.42 UTC">
    +                <name>M 5.9 - 2012 Nov 22, SALTA, ARGENTINA</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-63.571,-22.742,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 11:10:21.67 UTC">
    +                <name>M 5.9 - 2012 Nov 29, NEAR N COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>145.562,-3.423,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 17 17:41:32.30 UTC">
    +                <name>M 5.9 - 2012 Dec 17, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-104.495,-4.178,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 27 00:37:08.83 UTC">
    +                <name>M 5.9 - 2012 Dec 27, OFFSHORE MAULE, CHILE</name>
    +                <magnitude>5.9</magnitude>
    +                <Point>
    +                    <coordinates>-73.255,-35.783,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 14 16:36:21.04 UTC">
    +                <name>M 5.8 - 2012 Jan 14, BABUYAN ISL REGION, PHILIPPINES</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>121.156,19.202,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 12:50:20.53 UTC">
    +                <name>M 5.8 - 2012 Jan 18, MOLUCCA SEA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>126.829,-0.877,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 07:40:12.80 UTC">
    +                <name>M 5.8 - 2012 Feb 4, TONGA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-174.035,-20.536,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 6 11:33:36.98 UTC">
    +                <name>M 5.8 - 2012 Feb 6, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>123.08,9.821,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 06:22:01.17 UTC">
    +                <name>M 5.8 - 2012 Feb 14, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>141.386,36.214,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 03:31:20.61 UTC">
    +                <name>M 5.8 - 2012 Feb 15, OFF COAST OF OREGON</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-127.518,43.625,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 16 02:20:28.73 UTC">
    +                <name>M 5.8 - 2012 Feb 16, MAURITIUS - REUNION REGION</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>65.405,-17.839,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 16 07:58:02.50 UTC">
    +                <name>M 5.8 - 2012 Mar 16, LEYTE, PHILIPPINES</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>125.633,10.037,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 17 19:50:20.56 UTC">
    +                <name>M 5.8 - 2012 Mar 17, SOUTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-107.644,-34.905,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 10 05:09:08.41 UTC">
    +                <name>M 5.8 - 2012 Apr 10, NORTH OF ASCENSION ISLAND</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-13.972,-1.261,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 08:55:46.96 UTC">
    +                <name>M 5.8 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>91.748,1.271,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 23:56:32.93 UTC">
    +                <name>M 5.8 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>89.685,1.841,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 19:26:43.25 UTC">
    +                <name>M 5.8 - 2012 Apr 14, SUNDA STRAIT, INDONESIA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>105.457,-6.81,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 02:17:50.34 UTC">
    +                <name>M 5.8 - 2012 Apr 16, SULAWESI, INDONESIA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>121.855,-2.64,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 11:23:43.38 UTC">
    +                <name>M 5.8 - 2012 Apr 16, SOUTHERN GREECE</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>21.475,36.632,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 23:14:31.37 UTC">
    +                <name>M 5.8 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>93.36,2.158,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 29 10:28:51.87 UTC">
    +                <name>M 5.8 - 2012 Apr 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>140.349,35.596,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 2 12:17:59.36 UTC">
    +                <name>M 5.8 - 2012 May 2, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>143.836,-54.565,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 19 08:35:10.70 UTC">
    +                <name>M 5.8 - 2012 May 19, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-70.562,-25.729,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 03:33:25.48 UTC">
    +                <name>M 5.8 - 2012 May 29, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-179.61,-22.13,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 07:00:03.00 UTC">
    +                <name>M 5.8 - 2012 May 29, NORTHERN ITALY</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>11.086,44.851,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 1 06:56:20.24 UTC">
    +                <name>M 5.8 - 2012 Jun 1, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>133.269,-0.72,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 7 09:01:51.13 UTC">
    +                <name>M 5.8 - 2012 Jun 7, FIJI REGION</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-176.34,-20.192,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 29 15:31:46.66 UTC">
    +                <name>M 5.8 - 2012 Jun 29, SOUTH ATLANTIC OCEAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-9.663,-24.747,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 12 12:51:58.86 UTC">
    +                <name>M 5.8 - 2012 Jul 12, KURIL ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>151.665,45.452,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 06:32:56.09 UTC">
    +                <name>M 5.8 - 2012 Jul 20, KURIL ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>156.132,49.354,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 5 13:55:10.36 UTC">
    +                <name>M 5.8 - 2012 Aug 5, FIJI REGION</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-178.358,-21.15,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 16 13:24:44.36 UTC">
    +                <name>M 5.8 - 2012 Aug 16, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-98.905,-36.429,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 18 15:31:40.06 UTC">
    +                <name>M 5.8 - 2012 Aug 18, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>128.697,2.645,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 3 06:49:49.73 UTC">
    +                <name>M 5.8 - 2012 Sep 3, MORO GULF, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>123.875,6.61,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 27 23:53:49.03 UTC">
    +                <name>M 5.8 - 2012 Sep 27, SOLOMON ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>157.456,-8.838,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 04:58:04.93 UTC">
    +                <name>M 5.8 - 2012 Oct 14, SOLOMON ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>156.048,-7.145,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 23 09:39:30.70 UTC">
    +                <name>M 5.8 - 2012 Oct 23, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>171.691,-22.312,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 10:54:41.35 UTC">
    +                <name>M 5.8 - 2012 Nov 11, MYANMAR</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>95.832,22.72,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 4 01:42:48.46 UTC">
    +                <name>M 5.8 - 2012 Dec 4, SOUTHERN ALASKA</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-150.76,61.237,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 5 17:08:13.56 UTC">
    +                <name>M 5.8 - 2012 Dec 5, EASTERN IRAN</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>59.571,33.506,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 14 16:52:38.24 UTC">
    +                <name>M 5.8 - 2012 Dec 14, FIJI REGION</name>
    +                <magnitude>5.8</magnitude>
    +                <Point>
    +                    <coordinates>-178.186,-15.307,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 18:38:08.09 UTC">
    +                <name>M 5.7 - 2012 Jan 7, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-176.935,-28.613,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 17 12:55:34.72 UTC">
    +                <name>M 5.7 - 2012 Jan 17, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>125.837,11.177,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 05:58:38.79 UTC">
    +                <name>M 5.7 - 2012 Jan 19, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>165.57,-10.966,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 20 20:32:53.44 UTC">
    +                <name>M 5.7 - 2012 Jan 20, FLORES REGION, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>119.701,-8.406,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 3 04:33:09.00 UTC">
    +                <name>M 5.7 - 2012 Feb 3, VANUATU</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>167.187,-17.463,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 10 05:34:05.03 UTC">
    +                <name>M 5.7 - 2012 Feb 10, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>132.691,-0.324,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 12:49:02.44 UTC">
    +                <name>M 5.7 - 2012 Mar 4, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>169.769,-21.529,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 25 01:05:34.53 UTC">
    +                <name>M 5.7 - 2012 Mar 25, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-135.923,-54.576,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 1 14:04:24.91 UTC">
    +                <name>M 5.7 - 2012 Apr 1, EASTERN HONSHU, JAPAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>140.957,37.116,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 1 21:44:04.13 UTC">
    +                <name>M 5.7 - 2012 Apr 1, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>153.427,-4.516,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 11:53:36.16 UTC">
    +                <name>M 5.7 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>89.544,2.913,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 10:10:01.20 UTC">
    +                <name>M 5.7 - 2012 Apr 13, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>141.152,36.988,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 22:19:46.81 UTC">
    +                <name>M 5.7 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>93.853,3.256,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 21 01:19:28.46 UTC">
    +                <name>M 5.7 - 2012 Apr 21, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-16.181,-35.229,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 21:21:44.86 UTC">
    +                <name>M 5.7 - 2012 Apr 23, MOLUCCA SEA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>125.293,0.374,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 25 07:42:23.38 UTC">
    +                <name>M 5.7 - 2012 Apr 25, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>93.945,9.011,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 29 08:09:04.34 UTC">
    +                <name>M 5.7 - 2012 Apr 29, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>94.509,2.704,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 08:00:10.00 UTC">
    +                <name>M 5.7 - 2012 Apr 30, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>89.598,1.757,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 1 16:37:58.80 UTC">
    +                <name>M 5.7 - 2012 May 1, GUERRERO, MEXICO</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-101.085,18.251,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 5 20:23:31.16 UTC">
    +                <name>M 5.7 - 2012 May 5, TONGA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-174.227,-21.477,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 12 23:28:43.57 UTC">
    +                <name>M 5.7 - 2012 May 12, TAJIKISTAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>70.354,38.612,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 16 00:59:32.38 UTC">
    +                <name>M 5.7 - 2012 May 16, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>149.711,-5.504,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 4 14:18:52.30 UTC">
    +                <name>M 5.7 - 2012 Jun 4, FIJI REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-177.919,-15.279,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 11 05:29:11.51 UTC">
    +                <name>M 5.7 - 2012 Jun 11, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>69.351,36.023,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 15 01:14:07.52 UTC">
    +                <name>M 5.7 - 2012 Jun 15, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>126.354,5.719,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 17 03:43:10.05 UTC">
    +                <name>M 5.7 - 2012 Jun 17, SOLOMON ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>160.356,-8.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 22 04:31:17.30 UTC">
    +                <name>M 5.7 - 2012 Jun 22, MACQUARIE ISLAND REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>158.79,-54.36,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 06:30:59.86 UTC">
    +                <name>M 5.7 - 2012 Jun 27, EL SALVADOR</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-89.967,13.834,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 2 23:31:37.91 UTC">
    +                <name>M 5.7 - 2012 Jul 2, NEAR COAST OF CENTRAL PERU</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-75.598,-14.423,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 03:35:28.58 UTC">
    +                <name>M 5.7 - 2012 Jul 7, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>153.296,-4.651,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 02:31:16.66 UTC">
    +                <name>M 5.7 - 2012 Jul 11, KURIL ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>151.424,45.401,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 11:12:40.25 UTC">
    +                <name>M 5.7 - 2012 Jul 11, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-177.362,-26.051,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 12 14:00:34.34 UTC">
    +                <name>M 5.7 - 2012 Jul 12, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>70.906,36.527,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 29 02:21:12.60 UTC">
    +                <name>M 5.7 - 2012 Jul 29, MYANMAR</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>94.299,22.935,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 18 10:23:32.21 UTC">
    +                <name>M 5.7 - 2012 Aug 18, VANUATU REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>172.379,-13.504,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 18 17:45:02.27 UTC">
    +                <name>M 5.7 - 2012 Aug 18, TONGA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-173.042,-15.598,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 24 04:43:48.35 UTC">
    +                <name>M 5.7 - 2012 Aug 24, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-176.807,-27.311,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 3 19:44:22.36 UTC">
    +                <name>M 5.7 - 2012 Sep 3, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>125.044,7.905,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 23:33:06.26 UTC">
    +                <name>M 5.7 - 2012 Sep 6, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-105.891,-4.6,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 8 06:54:18.57 UTC">
    +                <name>M 5.7 - 2012 Sep 8, MARIANA ISLANDS REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>145.923,21.527,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 8 20:29:31.21 UTC">
    +                <name>M 5.7 - 2012 Sep 8, COSTA RICA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-85.323,10.082,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 15 16:32:21.51 UTC">
    +                <name>M 5.7 - 2012 Sep 15, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>113.865,-10.732,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 21:03:42.12 UTC">
    +                <name>M 5.7 - 2012 Sep 20, FIJI REGION</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-178.513,-20.757,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 3 13:32:34.94 UTC">
    +                <name>M 5.7 - 2012 Oct 3, KEPULAUAN BATU, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>97.672,-0.429,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 00:19:56.96 UTC">
    +                <name>M 5.7 - 2012 Oct 5, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-46.457,17.479,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 8 01:50:26.21 UTC">
    +                <name>M 5.7 - 2012 Oct 8, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-68.165,-21.725,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 09:41:58.95 UTC">
    +                <name>M 5.7 - 2012 Oct 14, KURIL ISLANDS</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>154.428,48.308,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 15:29:50.39 UTC">
    +                <name>M 5.7 - 2012 Oct 29, MOLUCCA SEA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>125.185,-0.142,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 14:12:01.82 UTC">
    +                <name>M 5.7 - 2012 Nov 1, JAVA, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>107.529,-6.762,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 01:36:21.63 UTC">
    +                <name>M 5.7 - 2012 Nov 6, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>122.2,1.374,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 7 22:42:48.22 UTC">
    +                <name>M 5.7 - 2012 Nov 7, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-92.156,13.849,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 14 05:21:42.47 UTC">
    +                <name>M 5.7 - 2012 Nov 14, NEGROS, PHILIPPINES</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>122.472,9.982,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 27 02:59:06.52 UTC">
    +                <name>M 5.7 - 2012 Nov 27, SERAM, INDONESIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>129.219,-2.952,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 30 11:20:38.43 UTC">
    +                <name>M 5.7 - 2012 Nov 30, TONGA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-175.595,-18.824,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 19:50:23.43 UTC">
    +                <name>M 5.7 - 2012 Dec 7, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>146.954,-7.661,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 17 17:46:46.63 UTC">
    +                <name>M 5.7 - 2012 Dec 17, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>-104.177,-4.028,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 23 13:31:40.76 UTC">
    +                <name>M 5.7 - 2012 Dec 23, BLACK SEA, OFFSHORE GEORGIA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>41.075,42.42,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 07:59:41.10 UTC">
    +                <name>M 5.7 - 2012 Dec 29, BISMARCK SEA</name>
    +                <magnitude>5.7</magnitude>
    +                <Point>
    +                    <coordinates>148.899,-3.563,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 01:13:40.43 UTC">
    +                <name>M 5.6 - 2012 Jan 5, TONGA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-173.543,-17.691,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 16:02:23.03 UTC">
    +                <name>M 5.6 - 2012 Jan 13, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-27.073,-60.548,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 03:59:44.59 UTC">
    +                <name>M 5.6 - 2012 Jan 16, SOUTH SHETLAND ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-56.095,-60.752,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 17 23:21:35.04 UTC">
    +                <name>M 5.6 - 2012 Jan 17, COQUIMBO, CHILE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-71.499,-31.655,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 00:22:18.94 UTC">
    +                <name>M 5.6 - 2012 Jan 28, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>142.211,40.177,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 04:43:41.03 UTC">
    +                <name>M 5.6 - 2012 Jan 28, SOUTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-110.517,-36.793,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 06:46:30.29 UTC">
    +                <name>M 5.6 - 2012 Feb 2, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>149.774,-6.563,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 09:32:16.88 UTC">
    +                <name>M 5.6 - 2012 Feb 2, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>149.718,-6.586,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 13:09:23.17 UTC">
    +                <name>M 5.6 - 2012 Feb 4, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>125.754,11.872,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 20:05:31.71 UTC">
    +                <name>M 5.6 - 2012 Feb 4, VANCOUVER ISLAND, CANADA REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-127.906,48.887,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 6 04:19:59.91 UTC">
    +                <name>M 5.6 - 2012 Feb 6, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>123.227,10.092,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 11 02:58:17.00 UTC">
    +                <name>M 5.6 - 2012 Feb 11, OFFSHORE BIO-BIO, CHILE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-73.884,-37.456,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 21:07:02.77 UTC">
    +                <name>M 5.6 - 2012 Feb 13, NORTHERN CALIFORNIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-123.79,41.143,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 19:09:36.29 UTC">
    +                <name>M 5.6 - 2012 Feb 20, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-92.779,14.276,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 05:24:59.26 UTC">
    +                <name>M 5.6 - 2012 Feb 26, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-177.73,-32.495,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 15:07:35.04 UTC">
    +                <name>M 5.6 - 2012 Feb 28, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>139.391,28.2,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 14:32:47.83 UTC">
    +                <name>M 5.6 - 2012 Feb 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>141.001,35.2,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 23:06:30.88 UTC">
    +                <name>M 5.6 - 2012 Mar 5, MASBATE REGION, PHILIPPINES</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>123.7,12.354,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 7 12:00:46.01 UTC">
    +                <name>M 5.6 - 2012 Mar 7, PAPUA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>138.888,-2.814,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 12 06:06:40.64 UTC">
    +                <name>M 5.6 - 2012 Mar 12, NORTHWESTERN KASHMIR</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>73.152,36.741,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 14 10:57:40.10 UTC">
    +                <name>M 5.6 - 2012 Mar 14, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>144.806,40.755,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 16:58:10.63 UTC">
    +                <name>M 5.6 - 2012 Mar 26, SOUTHWEST INDIAN RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>60.653,-30.04,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 15:13:14.14 UTC">
    +                <name>M 5.6 - 2012 Apr 14, KURIL ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>155.651,49.38,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 17 08:51:24.92 UTC">
    +                <name>M 5.6 - 2012 Apr 17, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-177.236,-31.894,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 22 13:11:19.70 UTC">
    +                <name>M 5.6 - 2012 Apr 22, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>140.29,-52.867,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 22:40:22.44 UTC">
    +                <name>M 5.6 - 2012 Apr 23, KURIL ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>154.739,48.397,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 09:50:58.39 UTC">
    +                <name>M 5.6 - 2012 Apr 24, CARLSBERG RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>61.511,5.649,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 15:15:38.16 UTC">
    +                <name>M 5.6 - 2012 Apr 24, SOUTH OF TONGA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-175.95,-24.228,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 26 19:21:42.61 UTC">
    +                <name>M 5.6 - 2012 Apr 26, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>94.459,2.696,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 28 19:21:03.11 UTC">
    +                <name>M 5.6 - 2012 Apr 28, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>152.11,-5.328,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 29 15:02:18.42 UTC">
    +                <name>M 5.6 - 2012 Apr 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>142.037,39.745,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 07:39:45.56 UTC">
    +                <name>M 5.6 - 2012 Apr 30, OFFSHORE COQUIMBO, CHILE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-71.46,-29.868,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 6 12:40:59.05 UTC">
    +                <name>M 5.6 - 2012 May 6, CENTRAL PERU</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-75.801,-13.82,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 7 04:40:27.79 UTC">
    +                <name>M 5.6 - 2012 May 7, AZERBAIJAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>46.789,41.549,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 07:19:54.88 UTC">
    +                <name>M 5.6 - 2012 May 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>143.248,39.548,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 22 00:00:32.77 UTC">
    +                <name>M 5.6 - 2012 May 22, BULGARIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>22.968,42.645,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 02:40:53.32 UTC">
    +                <name>M 5.6 - 2012 May 29, KOMANDORSKIYE OSTROVA REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>168.925,54.368,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 31 23:01:03.36 UTC">
    +                <name>M 5.6 - 2012 May 31, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>133.181,-0.902,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 20:56:41.42 UTC">
    +                <name>M 5.6 - 2012 Jun 19, NEAR ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>171.701,53.351,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 1 04:13:52.09 UTC">
    +                <name>M 5.6 - 2012 Jul 1, MYANMAR-INDIA BORDER REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>94.696,25.592,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 9 13:55:00.58 UTC">
    +                <name>M 5.6 - 2012 Jul 9, EASTERN MEDITERRANEAN SEA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>28.919,35.604,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 9 19:25:07.11 UTC">
    +                <name>M 5.6 - 2012 Jul 9, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>130.099,29.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 16 16:33:10.08 UTC">
    +                <name>M 5.6 - 2012 Jul 16, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>137.053,-1.296,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 19 07:36:35.47 UTC">
    +                <name>M 5.6 - 2012 Jul 19, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>71.375,37.248,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 14:03:13.76 UTC">
    +                <name>M 5.6 - 2012 Jul 20, FIJI REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-179.287,-21.222,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 04:54:05.50 UTC">
    +                <name>M 5.6 - 2012 Jul 21, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-179.968,-37.713,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 07:02:13.62 UTC">
    +                <name>M 5.6 - 2012 Jul 22, BISMARCK SEA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>149.532,-4.872,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 26 10:48:22.10 UTC">
    +                <name>M 5.6 - 2012 Jul 26, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>169.509,-21.103,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 29 09:20:54.98 UTC">
    +                <name>M 5.6 - 2012 Jul 29, PRIMOR'YE, RUSSIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>139.073,47.379,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 9 02:34:41.12 UTC">
    +                <name>M 5.6 - 2012 Aug 9, REVILLA GIGEDO ISLANDS REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-109.332,19.946,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 16 12:08:20.94 UTC">
    +                <name>M 5.6 - 2012 Aug 16, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-36.768,7.335,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 19 08:07:35.78 UTC">
    +                <name>M 5.6 - 2012 Aug 19, OFF COAST OF WASHINGTON</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-128.619,47.789,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 21 17:39:38.00 UTC">
    +                <name>M 5.6 - 2012 Aug 21, SOUTHWEST OF SUMATRA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>92.056,-0.172,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 24 10:01:53.94 UTC">
    +                <name>M 5.6 - 2012 Aug 24, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-179.533,-33.435,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 26 11:22:22.09 UTC">
    +                <name>M 5.6 - 2012 Aug 26, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-179.778,-65.478,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 12:18:43.09 UTC">
    +                <name>M 5.6 - 2012 Aug 30, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>114.08,-50.143,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 23:37:58.35 UTC">
    +                <name>M 5.6 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>126.719,10.388,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 06:46:33.86 UTC">
    +                <name>M 5.6 - 2012 Sep 6, PAPUA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>140.252,-3.661,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 01:28:18.80 UTC">
    +                <name>M 5.6 - 2012 Sep 11, KURIL ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>151.111,45.335,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 03:06:53.85 UTC">
    +                <name>M 5.6 - 2012 Sep 25, TONGA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-173.901,-15.517,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 7 03:14:24.00 UTC">
    +                <name>M 5.6 - 2012 Oct 7, LUZON, PHILIPPINES</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>120.939,18.543,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 9 07:49:51.87 UTC">
    +                <name>M 5.6 - 2012 Oct 9, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>139.224,-2.72,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 01:19:03.19 UTC">
    +                <name>M 5.6 - 2012 Oct 15, OFF EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>159.379,51.9,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 16 12:41:25.76 UTC">
    +                <name>M 5.6 - 2012 Oct 16, KURIL ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>156.438,49.618,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 16 23:42:55.69 UTC">
    +                <name>M 5.6 - 2012 Oct 16, NORTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>176.167,-38.639,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 19 10:13:54.99 UTC">
    +                <name>M 5.6 - 2012 Oct 19, MID-INDIAN RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>67.057,-9.289,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 23 09:33:19.67 UTC">
    +                <name>M 5.6 - 2012 Oct 23, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>148.206,-57.512,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 10:32:28.19 UTC">
    +                <name>M 5.6 - 2012 Oct 25, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>141.699,38.306,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 3 12:58:12.22 UTC">
    +                <name>M 5.6 - 2012 Nov 3, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-34.073,7.048,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 5 04:30:26.72 UTC">
    +                <name>M 5.6 - 2012 Nov 5, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>143.61,37.791,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 01:42:26.33 UTC">
    +                <name>M 5.6 - 2012 Nov 6, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>122.167,1.357,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 06:17:27.78 UTC">
    +                <name>M 5.6 - 2012 Nov 6, CARLSBERG RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>57.203,9.963,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 7 23:42:19.46 UTC">
    +                <name>M 5.6 - 2012 Nov 7, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>148.034,-8.652,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 13 23:23:48.05 UTC">
    +                <name>M 5.6 - 2012 Nov 13, TONGA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-175.934,-21.043,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 14 23:41:56.43 UTC">
    +                <name>M 5.6 - 2012 Nov 14, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>151.341,-5.86,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 16 06:24:42.03 UTC">
    +                <name>M 5.6 - 2012 Nov 16, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-134.758,-53.955,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 17 02:51:23.39 UTC">
    +                <name>M 5.6 - 2012 Nov 17, VANUATU</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>167.46,-14.99,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 28 03:09:48.72 UTC">
    +                <name>M 5.6 - 2012 Nov 28, NORTHERN PERU</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-76.101,-4.522,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 28 08:56:16.22 UTC">
    +                <name>M 5.6 - 2012 Nov 28, BANDA SEA</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>131.134,-5.398,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 3 09:27:37.04 UTC">
    +                <name>M 5.6 - 2012 Dec 3, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>-136.244,-54.672,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 8 16:35:16.82 UTC">
    +                <name>M 5.6 - 2012 Dec 8, NEAR S COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>143.973,-7.211,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 9 21:45:35.21 UTC">
    +                <name>M 5.6 - 2012 Dec 9, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>126.166,6.703,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 17 00:28:58.80 UTC">
    +                <name>M 5.6 - 2012 Dec 17, KURIL ISLANDS</name>
    +                <magnitude>5.6</magnitude>
    +                <Point>
    +                    <coordinates>155.837,49.785,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 09:35:32.10 UTC">
    +                <name>M 5.5 - 2012 Jan 5, DOMINICAN REPUBLIC</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-70.361,18.325,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 6 18:54:39.31 UTC">
    +                <name>M 5.5 - 2012 Jan 6, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-107.312,-6.341,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 03:20:49.99 UTC">
    +                <name>M 5.5 - 2012 Jan 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>141.071,36.994,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 14:11:08.43 UTC">
    +                <name>M 5.5 - 2012 Jan 12, SOUTH OF AFRICA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>28.152,-52.113,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 22 06:00:05.41 UTC">
    +                <name>M 5.5 - 2012 Jan 22, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-24.899,-56.655,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 24 16:31:07.79 UTC">
    +                <name>M 5.5 - 2012 Jan 24, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-27.723,-56.336,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 17:27:06.98 UTC">
    +                <name>M 5.5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>167.179,-17.954,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 21:50:50.57 UTC">
    +                <name>M 5.5 - 2012 Feb 4, OWEN FRACTURE ZONE REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>57.561,13.046,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 08:49:25.27 UTC">
    +                <name>M 5.5 - 2012 Feb 9, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-25.816,-56.383,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 11 07:06:48.45 UTC">
    +                <name>M 5.5 - 2012 Feb 11, MOLUCCA SEA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>126.853,-0.978,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 03:27:50.50 UTC">
    +                <name>M 5.5 - 2012 Feb 14, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>141.402,36.193,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 23 05:08:12.79 UTC">
    +                <name>M 5.5 - 2012 Feb 23, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-13.193,-17.699,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 02:46:21.28 UTC">
    +                <name>M 5.5 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-177.5,-24.477,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 09:44:14.60 UTC">
    +                <name>M 5.5 - 2012 Mar 4, OFF COAST OF CENTRAL AMERICA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-84.34,2.687,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 10 14:10:39.85 UTC">
    +                <name>M 5.5 - 2012 Mar 10, ALASKA PENINSULA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-157.567,55.146,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 12 12:32:46.36 UTC">
    +                <name>M 5.5 - 2012 Mar 12, KURIL ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>147.609,45.239,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 13 23:06:43.56 UTC">
    +                <name>M 5.5 - 2012 Mar 13, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-93.026,14.959,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 15 19:20:18.26 UTC">
    +                <name>M 5.5 - 2012 Mar 15, NEAR S. COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>139.279,35.802,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 17 17:00:56.86 UTC">
    +                <name>M 5.5 - 2012 Mar 17, CARLSBERG RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>63.404,3.823,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 24 22:18:38.25 UTC">
    +                <name>M 5.5 - 2012 Mar 24, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-136.157,-54.367,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 17:48:42.42 UTC">
    +                <name>M 5.5 - 2012 Mar 26, NORTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-104.228,9.995,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 28 08:25:51.35 UTC">
    +                <name>M 5.5 - 2012 Mar 28, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-140.014,-57.331,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 6 19:24:13.94 UTC">
    +                <name>M 5.5 - 2012 Apr 6, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>100.426,-3.345,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:00:09.71 UTC">
    +                <name>M 5.5 - 2012 Apr 11, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-176.097,51.364,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 13:58:04.72 UTC">
    +                <name>M 5.5 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>90.854,1.495,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 19:04:20.01 UTC">
    +                <name>M 5.5 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>92.092,1.19,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 14:50:52.85 UTC">
    +                <name>M 5.5 - 2012 Apr 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>141.468,37.513,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 05:37:55.94 UTC">
    +                <name>M 5.5 - 2012 Apr 18, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>138.772,28.689,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 14:57:10.11 UTC">
    +                <name>M 5.5 - 2012 Apr 24, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>93.949,8.868,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 20:22:10.41 UTC">
    +                <name>M 5.5 - 2012 Apr 24, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>140.472,35.622,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 18:11:44.42 UTC">
    +                <name>M 5.5 - 2012 Apr 30, BANDA SEA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>128.511,-5.689,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 1 15:44:37.80 UTC">
    +                <name>M 5.5 - 2012 May 1, OFF COAST OF BAJA CALIFORNIA SUR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-108.44,21.951,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 2 17:06:05.53 UTC">
    +                <name>M 5.5 - 2012 May 2, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-102.728,-35.883,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 9 04:35:00.11 UTC">
    +                <name>M 5.5 - 2012 May 9, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>127.45,6.232,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 9 14:49:51.28 UTC">
    +                <name>M 5.5 - 2012 May 9, NORTH OF ASCENSION ISLAND</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-13.6,-0.966,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 10:55:57.00 UTC">
    +                <name>M 5.5 - 2012 May 29, NORTHERN ITALY</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>11.008,44.888,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 1 05:07:01.98 UTC">
    +                <name>M 5.5 - 2012 Jun 1, ANTARCTICA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-148.864,-77.08,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 3 11:14:47.75 UTC">
    +                <name>M 5.5 - 2012 Jun 3, MID-INDIAN RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>67.21,-16.235,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 6 01:08:35.52 UTC">
    +                <name>M 5.5 - 2012 Jun 6, TAIWAN REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>121.428,22.357,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 12 05:59:38.69 UTC">
    +                <name>M 5.5 - 2012 Jun 12, SUNDA STRAIT, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>105.494,-5.677,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 13 07:06:44.59 UTC">
    +                <name>M 5.5 - 2012 Jun 13, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-28.394,-55.796,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 20:17:24.52 UTC">
    +                <name>M 5.5 - 2012 Jun 14, MOLUCCA SEA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>126.828,1.293,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 21 22:34:37.28 UTC">
    +                <name>M 5.5 - 2012 Jun 21, FIJI REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-178.186,-17.963,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 22 02:20:06.63 UTC">
    +                <name>M 5.5 - 2012 Jun 22, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-178.65,-32.935,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 23 21:27:29.60 UTC">
    +                <name>M 5.5 - 2012 Jun 23, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>90.509,2.628,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 24 07:59:34.86 UTC">
    +                <name>M 5.5 - 2012 Jun 24, SICHUAN-YUNNAN BORDER REG, CHINA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>100.781,27.767,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 20:39:15.91 UTC">
    +                <name>M 5.5 - 2012 Jun 27, TONGA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-173.268,-15.222,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 30 16:24:36.99 UTC">
    +                <name>M 5.5 - 2012 Jun 30, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>170.992,-22.088,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 3 03:49:33.04 UTC">
    +                <name>M 5.5 - 2012 Jul 3, SOLOMON ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>163.331,-10.82,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 12 15:40:12.63 UTC">
    +                <name>M 5.5 - 2012 Jul 12, SERAM, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>129.321,-2.885,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 16 03:08:31.72 UTC">
    +                <name>M 5.5 - 2012 Jul 16, NEAR EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>161.234,55.777,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 26 10:20:05.49 UTC">
    +                <name>M 5.5 - 2012 Jul 26, TONGA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-174.491,-21.015,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 07:37:45.18 UTC">
    +                <name>M 5.5 - 2012 Aug 2, KEP. TANIMBAR REGION, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>131.121,-6.944,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 3 07:22:30.56 UTC">
    +                <name>M 5.5 - 2012 Aug 3, FIJI REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-176.916,-16.919,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 12 18:31:25.11 UTC">
    +                <name>M 5.5 - 2012 Aug 12, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-157.718,-63.004,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 15 23:45:52.33 UTC">
    +                <name>M 5.5 - 2012 Aug 15, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-89.215,13.148,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 17 21:27:53.73 UTC">
    +                <name>M 5.5 - 2012 Aug 17, NORTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-103.003,8.348,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 24 07:03:10.52 UTC">
    +                <name>M 5.5 - 2012 Aug 24, BALLENY ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>171.324,-63.581,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 26 20:57:58.20 UTC">
    +                <name>M 5.5 - 2012 Aug 26, SOUTHERN CALIFORNIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-115.549,33.024,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 05:38:04.42 UTC">
    +                <name>M 5.5 - 2012 Aug 27, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-88.612,12.297,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 28 06:08:16.10 UTC">
    +                <name>M 5.5 - 2012 Aug 28, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-88.654,12.458,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 29 19:05:11.12 UTC">
    +                <name>M 5.5 - 2012 Aug 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>141.814,38.425,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 18:01:48.20 UTC">
    +                <name>M 5.5 - 2012 Sep 1, CHIAPAS, MEXICO</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-92.773,16.229,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 03:19:42.53 UTC">
    +                <name>M 5.5 - 2012 Sep 7, SICHUAN-YUNNAN-GUIZHOU RG, CHINA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>103.983,27.575,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 19:23:51.01 UTC">
    +                <name>M 5.5 - 2012 Sep 9, RAT ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>175.01,52.78,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 03:27:46.31 UTC">
    +                <name>M 5.5 - 2012 Sep 12, CRETE, GREECE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>24.11,34.783,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 01:54:52.64 UTC">
    +                <name>M 5.5 - 2012 Sep 13, FIJI REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>175.845,-18.627,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 14 07:18:38.38 UTC">
    +                <name>M 5.5 - 2012 Sep 14, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-16.01,-39.676,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 17 20:07:54.38 UTC">
    +                <name>M 5.5 - 2012 Sep 17, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>150.119,-5.687,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 22 06:58:26.17 UTC">
    +                <name>M 5.5 - 2012 Sep 22, TONGA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-174.194,-20.896,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 24 10:31:23.33 UTC">
    +                <name>M 5.5 - 2012 Sep 24, TONGA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-174.292,-21.212,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 29 07:11:12.93 UTC">
    +                <name>M 5.5 - 2012 Sep 29, OAXACA, MEXICO</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-98.393,16.304,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 19:41:29.44 UTC">
    +                <name>M 5.5 - 2012 Oct 2, BALLENY ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>177.83,-64.969,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 18:22:56.83 UTC">
    +                <name>M 5.5 - 2012 Oct 5, OFF THE COAST OF GUATEMALA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-91.504,13.028,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 20:02:10.66 UTC">
    +                <name>M 5.5 - 2012 Oct 5, GULF OF CALIFORNIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-108.512,23.582,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 11 17:22:09.93 UTC">
    +                <name>M 5.5 - 2012 Oct 11, VALPARAISO, CHILE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-70.31,-32.865,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 10:13:38.60 UTC">
    +                <name>M 5.5 - 2012 Oct 14, AZERBAIJAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>46.405,41.825,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 10:41:23.26 UTC">
    +                <name>M 5.5 - 2012 Oct 14, CHIAPAS, MEXICO</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-92.289,14.684,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 17:52:20.96 UTC">
    +                <name>M 5.5 - 2012 Oct 15, GUATEMALA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-91.01,14.062,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 19 15:34:09.77 UTC">
    +                <name>M 5.5 - 2012 Oct 19, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-176.8,-30.809,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 21 01:25:22.59 UTC">
    +                <name>M 5.5 - 2012 Oct 21, ICELAND REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-18.666,66.309,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 19:09:53.90 UTC">
    +                <name>M 5.5 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-132.082,52.294,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 06:28:33.13 UTC">
    +                <name>M 5.5 - 2012 Oct 29, FLORES REGION, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>123.465,-8.185,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 2 01:52:03.35 UTC">
    +                <name>M 5.5 - 2012 Nov 2, NEAR EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>162.799,55.887,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 11:09:31.96 UTC">
    +                <name>M 5.5 - 2012 Nov 6, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-104.8,-35.465,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 18:19:45.08 UTC">
    +                <name>M 5.5 - 2012 Nov 11, MYANMAR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>95.866,23.132,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 15 11:22:22.26 UTC">
    +                <name>M 5.5 - 2012 Nov 15, NEAR ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>173.351,52.496,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 07:09:12.13 UTC">
    +                <name>M 5.5 - 2012 Nov 29, NEAR COAST OF NORTHERN PERU</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-81.07,-6.588,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 19:32:31.75 UTC">
    +                <name>M 5.5 - 2012 Nov 29, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-92.113,13.757,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 6 00:43:24.10 UTC">
    +                <name>M 5.5 - 2012 Dec 6, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>126.638,10.974,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 08:48:13.04 UTC">
    +                <name>M 5.5 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>143.607,37.828,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 13 23:03:38.53 UTC">
    +                <name>M 5.5 - 2012 Dec 13, EAST OF SEVERNAYA ZEMLYA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>121.383,80.804,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 22 16:41:45.74 UTC">
    +                <name>M 5.5 - 2012 Dec 22, MYANMAR</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>94.782,22.443,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 23:01:56.68 UTC">
    +                <name>M 5.5 - 2012 Dec 26, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>-144.483,-56.186,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 28 17:32:18.07 UTC">
    +                <name>M 5.5 - 2012 Dec 28, SULAWESI, INDONESIA</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>122.918,-0.145,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 17:50:52.84 UTC">
    +                <name>M 5.5 - 2012 Dec 29, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.5</magnitude>
    +                <Point>
    +                    <coordinates>70.599,35.711,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 07:31:42.70 UTC">
    +                <name>M 5.4 - 2012 Jan 7, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>127.475,1.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 17:24:51.12 UTC">
    +                <name>M 5.4 - 2012 Jan 7, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>145.214,-5.081,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 17 15:27:48.53 UTC">
    +                <name>M 5.4 - 2012 Jan 17, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-104.146,-4.045,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 20 22:50:53.74 UTC">
    +                <name>M 5.4 - 2012 Jan 20, KEPULAUAN SANGIHE, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>125.307,4.795,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 26 15:52:57.60 UTC">
    +                <name>M 5.4 - 2012 Jan 26, SCOTIA SEA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-51.411,-60.582,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 18:49:01.41 UTC">
    +                <name>M 5.4 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-177.172,-29.519,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 19:05:39.73 UTC">
    +                <name>M 5.4 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-177.259,-29.574,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 1 07:14:29.10 UTC">
    +                <name>M 5.4 - 2012 Feb 1, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>102.203,-3.676,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 3 06:01:38.88 UTC">
    +                <name>M 5.4 - 2012 Feb 3, VANUATU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>167.232,-17.386,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 04:09:33.99 UTC">
    +                <name>M 5.4 - 2012 Feb 4, CENTRAL MONGOLIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>105.61,42.291,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 19:29:15.00 UTC">
    +                <name>M 5.4 - 2012 Feb 4, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>125.719,11.937,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 12:01:37.48 UTC">
    +                <name>M 5.4 - 2012 Feb 8, NEAR WEST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>137.999,37.858,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 05:58:58.18 UTC">
    +                <name>M 5.4 - 2012 Feb 9, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>155.72,-6.987,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 18 19:15:25.52 UTC">
    +                <name>M 5.4 - 2012 Feb 18, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-173.521,-20.957,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 22 10:45:22.87 UTC">
    +                <name>M 5.4 - 2012 Feb 22, KEPULAUAN KAI, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>133.565,-5.173,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 08:08:52.95 UTC">
    +                <name>M 5.4 - 2012 Feb 26, TARAPACA, CHILE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-69.29,-18.811,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 05:07:48.35 UTC">
    +                <name>M 5.4 - 2012 Feb 27, KURIL ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>152.74,46.922,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 19:31:51.72 UTC">
    +                <name>M 5.4 - 2012 Feb 27, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>127.096,25.557,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 22:52:37.85 UTC">
    +                <name>M 5.4 - 2012 Feb 28, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>144.137,37.203,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 16:03:57.29 UTC">
    +                <name>M 5.4 - 2012 Feb 29, VANUATU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>168.038,-17.647,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 22:32:35.61 UTC">
    +                <name>M 5.4 - 2012 Feb 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>140.448,36.427,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 05:09:59.40 UTC">
    +                <name>M 5.4 - 2012 Mar 2, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>146.265,-8.048,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 08:12:33.65 UTC">
    +                <name>M 5.4 - 2012 Mar 5, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>128.626,1.937,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 6 02:52:41.96 UTC">
    +                <name>M 5.4 - 2012 Mar 6, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>93.691,8.571,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 7 12:02:52.29 UTC">
    +                <name>M 5.4 - 2012 Mar 7, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-25.306,-57.941,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 9 17:25:33.19 UTC">
    +                <name>M 5.4 - 2012 Mar 9, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>140.392,36.719,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 14 11:40:17.10 UTC">
    +                <name>M 5.4 - 2012 Mar 14, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>144.848,40.905,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 13:49:05.25 UTC">
    +                <name>M 5.4 - 2012 Mar 19, MOLUCCA SEA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>125.075,0.353,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 18:16:08.30 UTC">
    +                <name>M 5.4 - 2012 Mar 30, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-93.063,14.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 1 13:14:32.67 UTC">
    +                <name>M 5.4 - 2012 Apr 1, TRISTAN DA CUNHA REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-14.877,-35.191,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 2 08:17:06.95 UTC">
    +                <name>M 5.4 - 2012 Apr 2, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-173.514,-16.935,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 03:08:08.02 UTC">
    +                <name>M 5.4 - 2012 Apr 3, NEAR COAST OF NORTHERN PERU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-80.583,-5.18,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 23:42:19.37 UTC">
    +                <name>M 5.4 - 2012 Apr 3, KEPULAUAN KAI, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>133.876,-5.654,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 10:36:54.28 UTC">
    +                <name>M 5.4 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>91.87,1.167,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 11:29:02.49 UTC">
    +                <name>M 5.4 - 2012 Apr 11, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-27.979,-56.871,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 11:34:01.33 UTC">
    +                <name>M 5.4 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>92.855,0.734,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 18:54:45.16 UTC">
    +                <name>M 5.4 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>92.694,2.475,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 22:51:57.33 UTC">
    +                <name>M 5.4 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>89.556,2.865,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 15:21:56.33 UTC">
    +                <name>M 5.4 - 2012 Apr 14, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>92.141,0.244,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 21 11:36:05.37 UTC">
    +                <name>M 5.4 - 2012 Apr 21, CENTRAL PERU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-71.481,-14.82,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 22 12:20:46.12 UTC">
    +                <name>M 5.4 - 2012 Apr 22, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>140.329,-52.753,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 1 05:33:51.12 UTC">
    +                <name>M 5.4 - 2012 May 1, VOLCANO ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>141.207,25.404,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 04:59:54.50 UTC">
    +                <name>M 5.4 - 2012 May 3, OFF COAST OF BAJA CALIFORNIA SUR</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-108.779,21.21,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 11 12:41:35.30 UTC">
    +                <name>M 5.4 - 2012 May 11, ASSAM, INDIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>92.889,26.175,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 11 18:48:29.58 UTC">
    +                <name>M 5.4 - 2012 May 11, CYPRUS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>34.142,34.304,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 24 19:18:54.97 UTC">
    +                <name>M 5.4 - 2012 May 24, NEUQUEN, ARGENTINA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-70.467,-36.912,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 30 21:20:56.16 UTC">
    +                <name>M 5.4 - 2012 May 30, EASTERN KAZAKHSTAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>78.745,43.384,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 14:23:20.10 UTC">
    +                <name>M 5.4 - 2012 Jun 9, KURIL ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>154.852,48.851,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 11 05:02:14.25 UTC">
    +                <name>M 5.4 - 2012 Jun 11, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>69.401,36.039,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 15 08:43:32.72 UTC">
    +                <name>M 5.4 - 2012 Jun 15, BISMARCK SEA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>146.309,-3.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 20 21:55:53.81 UTC">
    +                <name>M 5.4 - 2012 Jun 20, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>123.426,0.102,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 20 23:20:56.03 UTC">
    +                <name>M 5.4 - 2012 Jun 20, SOLOMON ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>156.998,-8.7,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 24 10:25:54.75 UTC">
    +                <name>M 5.4 - 2012 Jun 24, JUJUY, ARGENTINA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-66.702,-22.445,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 26 17:19:04.15 UTC">
    +                <name>M 5.4 - 2012 Jun 26, GUATEMALA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-92.049,15.378,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 1 23:39:08.77 UTC">
    +                <name>M 5.4 - 2012 Jul 1, ATACAMA, CHILE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-70.878,-28.561,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 2 18:53:27.33 UTC">
    +                <name>M 5.4 - 2012 Jul 2, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-179.342,-33.385,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 08:11:37.02 UTC">
    +                <name>M 5.4 - 2012 Jul 7, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-27.414,-56.203,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 03:57:14.45 UTC">
    +                <name>M 5.4 - 2012 Jul 11, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-174.678,-16.103,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 20:52:37.03 UTC">
    +                <name>M 5.4 - 2012 Jul 11, NEAR COAST OF CENTRAL PERU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-75.288,-15.276,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 04:30:58.77 UTC">
    +                <name>M 5.4 - 2012 Jul 14, KURIL ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>151.316,45.516,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 14:22:22.56 UTC">
    +                <name>M 5.4 - 2012 Jul 14, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>124.64,-49.044,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 19 05:14:27.92 UTC">
    +                <name>M 5.4 - 2012 Jul 19, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-27.809,-56.038,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 18:14:53.26 UTC">
    +                <name>M 5.4 - 2012 Jul 20, FLORES REGION, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>123.682,-8.006,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 05:39:13.39 UTC">
    +                <name>M 5.4 - 2012 Jul 22, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>153.375,-4.169,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 27 03:37:56.72 UTC">
    +                <name>M 5.4 - 2012 Jul 27, MARIANA ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>142.985,21.671,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 28 16:01:11.47 UTC">
    +                <name>M 5.4 - 2012 Jul 28, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-32.667,4.548,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 1 20:43:40.68 UTC">
    +                <name>M 5.4 - 2012 Aug 1, NEAR N COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>144.639,-4.39,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 3 04:08:58.27 UTC">
    +                <name>M 5.4 - 2012 Aug 3, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>155.718,-6.939,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 3 11:58:58.20 UTC">
    +                <name>M 5.4 - 2012 Aug 3, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>165.038,-11.478,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 18:55:13.48 UTC">
    +                <name>M 5.4 - 2012 Aug 6, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>128.429,2.702,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 8 07:11:01.93 UTC">
    +                <name>M 5.4 - 2012 Aug 8, NORTHEAST OF TAIWAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>124.921,25.873,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 9 15:03:05.54 UTC">
    +                <name>M 5.4 - 2012 Aug 9, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-27.988,-59.762,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 13 20:32:59.28 UTC">
    +                <name>M 5.4 - 2012 Aug 13, PAKISTAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>73.663,34.849,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 26 19:31:22.90 UTC">
    +                <name>M 5.4 - 2012 Aug 26, SOUTHERN CALIFORNIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-115.546,33.019,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 10:53:58.42 UTC">
    +                <name>M 5.4 - 2012 Aug 30, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>114.106,-50.211,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 13:27:31.99 UTC">
    +                <name>M 5.4 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.887,10.485,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 01:16:08.78 UTC">
    +                <name>M 5.4 - 2012 Sep 1, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.683,10.449,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 01:20:38.27 UTC">
    +                <name>M 5.4 - 2012 Sep 1, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.812,10.528,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 2 14:42:15.52 UTC">
    +                <name>M 5.4 - 2012 Sep 2, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.652,11.308,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 09:58:10.97 UTC">
    +                <name>M 5.4 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.77,10.46,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 17:41:02.01 UTC">
    +                <name>M 5.4 - 2012 Sep 7, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>113.833,-10.73,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 04:28:12.90 UTC">
    +                <name>M 5.4 - 2012 Sep 12, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>152.098,-5.022,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 21:52:16.91 UTC">
    +                <name>M 5.4 - 2012 Sep 12, SOUTH INDIAN OCEAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>82.926,-23.996,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 05:54:48.96 UTC">
    +                <name>M 5.4 - 2012 Sep 13, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.938,10.238,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 23:40:02.95 UTC">
    +                <name>M 5.4 - 2012 Sep 13, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>113.755,-10.768,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 21 12:17:26.16 UTC">
    +                <name>M 5.4 - 2012 Sep 21, TARAPACA, CHILE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-68.961,-19.569,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 22 12:29:58.00 UTC">
    +                <name>M 5.4 - 2012 Sep 22, OAXACA, MEXICO</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-98.281,16.262,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 18:33:01.73 UTC">
    +                <name>M 5.4 - 2012 Sep 25, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-174.313,-21.192,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 30 15:35:53.73 UTC">
    +                <name>M 5.4 - 2012 Sep 30, EAST OF THE VOLCANO ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>146.043,23,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 3 18:28:33.86 UTC">
    +                <name>M 5.4 - 2012 Oct 3, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.95,5.858,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 4 23:14:56.40 UTC">
    +                <name>M 5.4 - 2012 Oct 4, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-46.498,17.443,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 00:15:41.71 UTC">
    +                <name>M 5.4 - 2012 Oct 5, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-46.453,17.501,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 7 11:42:47.16 UTC">
    +                <name>M 5.4 - 2012 Oct 7, AZERBAIJAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>48.437,40.747,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 10 22:49:33.05 UTC">
    +                <name>M 5.4 - 2012 Oct 10, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-26.573,-60.326,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 11:11:36.43 UTC">
    +                <name>M 5.4 - 2012 Oct 14, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>144.333,38.215,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 16 02:03:28.76 UTC">
    +                <name>M 5.4 - 2012 Oct 16, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>140.222,31.277,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 17 17:23:42.95 UTC">
    +                <name>M 5.4 - 2012 Oct 17, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-174.187,-19.042,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 21 06:55:09.50 UTC">
    +                <name>M 5.4 - 2012 Oct 21, CENTRAL CALIFORNIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-120.856,36.311,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 21 20:28:17.00 UTC">
    +                <name>M 5.4 - 2012 Oct 21, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-88.337,12.562,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 24 17:55:59.99 UTC">
    +                <name>M 5.4 - 2012 Oct 24, CAYMAN ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-81.692,17.785,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 20:49:35.76 UTC">
    +                <name>M 5.4 - 2012 Oct 25, NORTHERN PERU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-76.074,-5.919,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 27 20:13:03.30 UTC">
    +                <name>M 5.4 - 2012 Oct 27, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>151.532,-6.141,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 19:03:21.85 UTC">
    +                <name>M 5.4 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-132.667,52.834,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 02:22:44.50 UTC">
    +                <name>M 5.4 - 2012 Oct 29, NIAS REGION, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>98.381,0.88,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 23:37:18.24 UTC">
    +                <name>M 5.4 - 2012 Nov 1, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>122.105,1.229,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 4 06:37:38.26 UTC">
    +                <name>M 5.4 - 2012 Nov 4, SOUTHERN PERU</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-71.878,-15.874,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 7 06:26:32.84 UTC">
    +                <name>M 5.4 - 2012 Nov 7, NORTHWESTERN IRAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>46.62,38.418,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 17 18:12:19.87 UTC">
    +                <name>M 5.4 - 2012 Nov 17, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-95.075,-37.209,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 17 18:43:50.65 UTC">
    +                <name>M 5.4 - 2012 Nov 17, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-95.142,-37.182,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 20 16:23:24.78 UTC">
    +                <name>M 5.4 - 2012 Nov 20, OFFSHORE O'HIGGINS, CHILE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-72.254,-33.921,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 21 16:46:40.13 UTC">
    +                <name>M 5.4 - 2012 Nov 21, SOUTH OF SUMBAWA, INDONESIA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>117.931,-11.359,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 26 00:17:14.86 UTC">
    +                <name>M 5.4 - 2012 Nov 26, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-108.329,-9.098,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 27 07:34:25.40 UTC">
    +                <name>M 5.4 - 2012 Nov 27, ALAMAGAN REG, N. MARIANA ISLANDS</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>145.763,17.684,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 3 23:58:35.43 UTC">
    +                <name>M 5.4 - 2012 Dec 3, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-92.372,14.16,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 12 01:44:20.69 UTC">
    +                <name>M 5.4 - 2012 Dec 12, FIJI REGION</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-176.278,-19.556,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 20 21:47:29.76 UTC">
    +                <name>M 5.4 - 2012 Dec 20, MOLUCCA SEA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>126.217,0.562,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 25 07:34:41.22 UTC">
    +                <name>M 5.4 - 2012 Dec 25, MOLUCCA SEA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>125.464,1.109,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 27 09:41:59.69 UTC">
    +                <name>M 5.4 - 2012 Dec 27, TONGA</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>-173.562,-18.302,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 14:59:38.91 UTC">
    +                <name>M 5.4 - 2012 Dec 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.4</magnitude>
    +                <Point>
    +                    <coordinates>142.024,38.738,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 00:50:08.04 UTC">
    +                <name>M 5.3 - 2012 Jan 1, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>166.218,-11.366,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 00:54:31.11 UTC">
    +                <name>M 5.3 - 2012 Jan 5, OFF COAST OF AISEN, CHILE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-76.472,-45.992,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 8 21:51:49.54 UTC">
    +                <name>M 5.3 - 2012 Jan 8, VANUATU</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>167.887,-17.425,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 22:08:11.10 UTC">
    +                <name>M 5.3 - 2012 Jan 12, SOUTH OF AFRICA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>28.196,-52.042,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 22:30:59.28 UTC">
    +                <name>M 5.3 - 2012 Jan 12, TONGA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-173.59,-16.753,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 10:26:47.02 UTC">
    +                <name>M 5.3 - 2012 Jan 16, MOLUCCA SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>125.475,-0.228,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 17 23:23:23.00 UTC">
    +                <name>M 5.3 - 2012 Jan 17, OFFSHORE COQUIMBO, CHILE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-71.615,-31.728,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 17:01:22.37 UTC">
    +                <name>M 5.3 - 2012 Jan 18, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>165.536,-11.018,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 17:02:57.42 UTC">
    +                <name>M 5.3 - 2012 Jan 18, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>165.464,-10.999,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 26 22:04:06.91 UTC">
    +                <name>M 5.3 - 2012 Jan 26, MARIANA ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>142.852,21.645,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 01:33:24.00 UTC">
    +                <name>M 5.3 - 2012 Jan 27, DODECANESE ISLANDS, GREECE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>25.064,36.044,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 00:17:11.11 UTC">
    +                <name>M 5.3 - 2012 Jan 28, CATANDUANES, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>124.586,13.386,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 17:47:35.79 UTC">
    +                <name>M 5.3 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-177.139,-29.59,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 19:37:31.42 UTC">
    +                <name>M 5.3 - 2012 Jan 28, PAPUA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>140.788,-4.081,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 29 21:43:25.09 UTC">
    +                <name>M 5.3 - 2012 Jan 29, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-46.529,16.488,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 1 01:29:24.86 UTC">
    +                <name>M 5.3 - 2012 Feb 1, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>165.901,-10.802,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 1 04:30:47.11 UTC">
    +                <name>M 5.3 - 2012 Feb 1, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>125.557,11.6,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 1 21:43:46.48 UTC">
    +                <name>M 5.3 - 2012 Feb 1, EASTER ISLAND REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-111.525,-31.585,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 13:57:07.05 UTC">
    +                <name>M 5.3 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>167.176,-17.455,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 6 11:40:15.38 UTC">
    +                <name>M 5.3 - 2012 Feb 6, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>123.108,9.85,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 7 20:37:44.17 UTC">
    +                <name>M 5.3 - 2012 Feb 7, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>123.42,10.194,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 00:41:02.77 UTC">
    +                <name>M 5.3 - 2012 Feb 8, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>123.43,10.249,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 01:58:41.47 UTC">
    +                <name>M 5.3 - 2012 Feb 8, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-177.409,-29.455,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 23:39:16.42 UTC">
    +                <name>M 5.3 - 2012 Feb 14, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-29.228,0.859,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 17 14:35:42.66 UTC">
    +                <name>M 5.3 - 2012 Feb 17, SOUTHEAST INDIAN RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>84.932,-42.024,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 21 13:50:42.62 UTC">
    +                <name>M 5.3 - 2012 Feb 21, BERING STRAIT</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-167.057,67.744,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 22 12:02:45.51 UTC">
    +                <name>M 5.3 - 2012 Feb 22, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>94.4,4.99,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 24 13:43:16.66 UTC">
    +                <name>M 5.3 - 2012 Feb 24, NORTHERN COLOMBIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-72.952,6.702,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 24 19:12:20.54 UTC">
    +                <name>M 5.3 - 2012 Feb 24, MOLUCCA SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.34,1.516,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 10:11:43.22 UTC">
    +                <name>M 5.3 - 2012 Mar 2, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>141.043,35.16,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 3 11:01:47.25 UTC">
    +                <name>M 5.3 - 2012 Mar 3, COQUIMBO, CHILE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-71.129,-30.348,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 6 06:05:50.09 UTC">
    +                <name>M 5.3 - 2012 Mar 6, STATE OF YAP, MICRONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>140.306,10.527,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 8 20:03:17.23 UTC">
    +                <name>M 5.3 - 2012 Mar 8, MINDORO, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>120.519,13.57,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 10 02:26:57.00 UTC">
    +                <name>M 5.3 - 2012 Mar 10, TARAPACA, CHILE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-69.25,-19.738,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 11 18:11:09.36 UTC">
    +                <name>M 5.3 - 2012 Mar 11, CARLSBERG RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>65.125,3.077,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 11 22:30:26.58 UTC">
    +                <name>M 5.3 - 2012 Mar 11, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-16.696,-41.153,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 17 15:49:17.04 UTC">
    +                <name>M 5.3 - 2012 Mar 17, CARLSBERG RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>63.465,3.758,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 13:42:34.67 UTC">
    +                <name>M 5.3 - 2012 Mar 19, NEW GUINEA, PAPUA NEW GUINEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>143.888,-4.36,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 18:22:33.10 UTC">
    +                <name>M 5.3 - 2012 Mar 20, OAXACA, MEXICO</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-98.172,16.289,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 23 09:25:14.00 UTC">
    +                <name>M 5.3 - 2012 Mar 23, SOUTH AUSTRALIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>131.955,-26.163,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 09:30:18.74 UTC">
    +                <name>M 5.3 - 2012 Mar 26, CHUKOTKA, RUSSIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-174.648,66.363,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 17:48:09.27 UTC">
    +                <name>M 5.3 - 2012 Mar 26, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-179.075,-35.689,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 29 00:36:40.36 UTC">
    +                <name>M 5.3 - 2012 Mar 29, SOUTHWEST INDIAN RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>44.605,-40.957,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 1 22:23:48.00 UTC">
    +                <name>M 5.3 - 2012 Apr 1, GUERRERO, MEXICO</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-98.54,16.46,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 10:42:34.27 UTC">
    +                <name>M 5.3 - 2012 Apr 3, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>153.442,-4.397,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 4 20:27:26.39 UTC">
    +                <name>M 5.3 - 2012 Apr 4, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>152.173,-3.824,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 7 11:58:04.80 UTC">
    +                <name>M 5.3 - 2012 Apr 7, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>149.555,-6.746,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 8 21:43:30.90 UTC">
    +                <name>M 5.3 - 2012 Apr 8, TAIWAN REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>122.35,24,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:02:48.10 UTC">
    +                <name>M 5.3 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>91.785,1.636,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:51:40.14 UTC">
    +                <name>M 5.3 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>90.299,2.521,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 10:01:18.33 UTC">
    +                <name>M 5.3 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>90.351,2.483,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 10:21:15.38 UTC">
    +                <name>M 5.3 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.482,2.822,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 11:52:28.64 UTC">
    +                <name>M 5.3 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>91.958,1.084,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 13:42:40.26 UTC">
    +                <name>M 5.3 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>93.584,2.164,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 14:34:17.96 UTC">
    +                <name>M 5.3 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>90.833,1.477,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 14:54:28.46 UTC">
    +                <name>M 5.3 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>91.9,1.276,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 20:21:52.91 UTC">
    +                <name>M 5.3 - 2012 Apr 12, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.746,3.753,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 19:31:50.00 UTC">
    +                <name>M 5.3 - 2012 Apr 14, SUNDA STRAIT, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>105.444,-6.777,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 20:53:55.91 UTC">
    +                <name>M 5.3 - 2012 Apr 14, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-25.209,-56.845,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 03:27:40.75 UTC">
    +                <name>M 5.3 - 2012 Apr 16, GULF OF CALIFORNIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-108.793,23.952,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 09:46:27.66 UTC">
    +                <name>M 5.3 - 2012 Apr 16, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.417,0.798,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 18:01:14.94 UTC">
    +                <name>M 5.3 - 2012 Apr 16, SULAWESI, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>121.939,-2.58,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 19:52:38.96 UTC">
    +                <name>M 5.3 - 2012 Apr 18, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>131.909,-0.501,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 19 17:23:39.33 UTC">
    +                <name>M 5.3 - 2012 Apr 19, MOLUCCA SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.434,1.199,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 25 07:53:21.27 UTC">
    +                <name>M 5.3 - 2012 Apr 25, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>93.906,8.945,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 28 18:00:32.88 UTC">
    +                <name>M 5.3 - 2012 Apr 28, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>100.439,-3.338,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 16:14:16.63 UTC">
    +                <name>M 5.3 - 2012 May 3, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>152.085,-5.466,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 7 14:15:14.45 UTC">
    +                <name>M 5.3 - 2012 May 7, AZERBAIJAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>46.719,41.553,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 12 13:23:15.96 UTC">
    +                <name>M 5.3 - 2012 May 12, MOLUCCA SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.108,2.241,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 13 01:19:10.01 UTC">
    +                <name>M 5.3 - 2012 May 13, SOUTH INDIAN OCEAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>89.416,-3.347,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 13 04:46:05.93 UTC">
    +                <name>M 5.3 - 2012 May 13, SULAWESI, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>122.996,-0.115,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 08:20:57.95 UTC">
    +                <name>M 5.3 - 2012 May 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>143.175,39.619,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 23 20:25:58.20 UTC">
    +                <name>M 5.3 - 2012 May 23, KURIL ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>152.557,46.66,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 1 12:32:27.69 UTC">
    +                <name>M 5.3 - 2012 Jun 1, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>75.194,39.803,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 3 21:00:59.35 UTC">
    +                <name>M 5.3 - 2012 Jun 3, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-25.57,-60.562,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 5 11:01:20.00 UTC">
    +                <name>M 5.3 - 2012 Jun 5, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>94.672,5.482,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 05:52:53.61 UTC">
    +                <name>M 5.3 - 2012 Jun 14, TURKEY-SYRIA-IRAQ BORDER REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>42.325,37.294,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 17 17:26:26.94 UTC">
    +                <name>M 5.3 - 2012 Jun 17, TONGA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-174.311,-20.588,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 18 08:29:04.44 UTC">
    +                <name>M 5.3 - 2012 Jun 18, MENDOZA, ARGENTINA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-68.496,-33.009,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 20 00:41:44.43 UTC">
    +                <name>M 5.3 - 2012 Jun 20, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>133.281,-0.727,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 28 15:51:58.60 UTC">
    +                <name>M 5.3 - 2012 Jun 28, VANUATU</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>166.625,-13.504,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 3 02:31:03.07 UTC">
    +                <name>M 5.3 - 2012 Jul 3, NEAR S. COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>139.744,34.934,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 5 08:14:19.29 UTC">
    +                <name>M 5.3 - 2012 Jul 5, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.554,5.126,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 00:50:27.02 UTC">
    +                <name>M 5.3 - 2012 Jul 7, NORTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>175.807,-39.068,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 9 12:25:47.34 UTC">
    +                <name>M 5.3 - 2012 Jul 9, SULAWESI, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>122.791,-0.171,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 00:16:10.00 UTC">
    +                <name>M 5.3 - 2012 Jul 11, SOLOMON ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>156.275,-8.442,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 18 04:33:59.33 UTC">
    +                <name>M 5.3 - 2012 Jul 18, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>102.794,-4.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 18 06:36:48.23 UTC">
    +                <name>M 5.3 - 2012 Jul 18, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.073,11.281,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 03:40:12.15 UTC">
    +                <name>M 5.3 - 2012 Jul 20, KURIL ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>155.599,49.506,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 12:46:30.93 UTC">
    +                <name>M 5.3 - 2012 Jul 21, BANDA SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>129.266,-5.387,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 16:56:02.64 UTC">
    +                <name>M 5.3 - 2012 Jul 22, TONGA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-175.029,-21.804,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 29 22:05:09.17 UTC">
    +                <name>M 5.3 - 2012 Jul 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>142.323,39.075,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 4 11:24:15.01 UTC">
    +                <name>M 5.3 - 2012 Aug 4, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>96.296,4.857,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 03:28:23.11 UTC">
    +                <name>M 5.3 - 2012 Aug 6, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>130.603,29.487,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 9 11:04:24.90 UTC">
    +                <name>M 5.3 - 2012 Aug 9, LOMBOK REGION, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>116.363,-8.718,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 14 12:06:14.86 UTC">
    +                <name>M 5.3 - 2012 Aug 14, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>142.53,28.718,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 15 17:49:04.60 UTC">
    +                <name>M 5.3 - 2012 Aug 15, NORTHWESTERN IRAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>46.672,38.41,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 22 11:38:19.58 UTC">
    +                <name>M 5.3 - 2012 Aug 22, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>147.25,-6.047,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 17:54:26.13 UTC">
    +                <name>M 5.3 - 2012 Aug 27, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.698,3.622,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 28 08:53:36.88 UTC">
    +                <name>M 5.3 - 2012 Aug 28, OFF COAST OF CENTRAL AMERICA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-88.706,11.978,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 28 15:51:06.75 UTC">
    +                <name>M 5.3 - 2012 Aug 28, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.986,4.409,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 00:35:35.69 UTC">
    +                <name>M 5.3 - 2012 Aug 31, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-32.155,3.807,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 13:14:59.42 UTC">
    +                <name>M 5.3 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.926,10.562,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 18:38:55.08 UTC">
    +                <name>M 5.3 - 2012 Aug 31, GULF OF ADEN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>50.47,13.239,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 00:25:02.67 UTC">
    +                <name>M 5.3 - 2012 Sep 1, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.45,10.229,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 00:50:53.04 UTC">
    +                <name>M 5.3 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.888,10.423,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 06:43:01.00 UTC">
    +                <name>M 5.3 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.778,10.666,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 06:54:59.71 UTC">
    +                <name>M 5.3 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.827,10.634,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 14:43:17.98 UTC">
    +                <name>M 5.3 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.896,10.501,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 5 00:35:30.45 UTC">
    +                <name>M 5.3 - 2012 Sep 5, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.711,11.596,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 02:22:20.12 UTC">
    +                <name>M 5.3 - 2012 Sep 6, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.879,4.499,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 04:16:30.49 UTC">
    +                <name>M 5.3 - 2012 Sep 7, SICHUAN-YUNNAN-GUIZHOU RG, CHINA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>104.007,27.572,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 12:30:31.66 UTC">
    +                <name>M 5.3 - 2012 Sep 7, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>128.238,2.461,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 14:21:36.29 UTC">
    +                <name>M 5.3 - 2012 Sep 11, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>113.832,-10.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 15 10:26:25.96 UTC">
    +                <name>M 5.3 - 2012 Sep 15, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>125.468,12.556,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 15 17:35:40.98 UTC">
    +                <name>M 5.3 - 2012 Sep 15, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>113.883,-10.738,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 16 21:46:18.87 UTC">
    +                <name>M 5.3 - 2012 Sep 16, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.723,10.624,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 20:47:46.61 UTC">
    +                <name>M 5.3 - 2012 Sep 20, KEPULAUAN BATU, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>98.817,-0.065,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 21:21:05.44 UTC">
    +                <name>M 5.3 - 2012 Sep 20, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.381,5.998,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 23 06:40:57.74 UTC">
    +                <name>M 5.3 - 2012 Sep 23, FIJI REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>175.174,-16.93,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 23 23:42:27.14 UTC">
    +                <name>M 5.3 - 2012 Sep 23, SOUTH OF MARIANA ISLANDS</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>143.25,11.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 26 17:45:00.63 UTC">
    +                <name>M 5.3 - 2012 Sep 26, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-68.441,-22.262,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 29 11:24:32.65 UTC">
    +                <name>M 5.3 - 2012 Sep 29, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.78,6.107,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 06:25:32.57 UTC">
    +                <name>M 5.3 - 2012 Oct 2, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>123.287,8.297,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 17:37:22.01 UTC">
    +                <name>M 5.3 - 2012 Oct 2, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>101.919,-2.917,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 3 06:26:17.85 UTC">
    +                <name>M 5.3 - 2012 Oct 3, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>152.718,-6.299,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 7 08:36:32.18 UTC">
    +                <name>M 5.3 - 2012 Oct 7, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>151.794,-5.53,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 10 12:19:44.93 UTC">
    +                <name>M 5.3 - 2012 Oct 10, COSTA RICA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-85.476,10.1,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 11 17:07:54.73 UTC">
    +                <name>M 5.3 - 2012 Oct 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>92.585,1.471,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 13 23:07:54.65 UTC">
    +                <name>M 5.3 - 2012 Oct 13, NEAR N COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>142.326,-2.538,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 07:49:01.80 UTC">
    +                <name>M 5.3 - 2012 Oct 15, VANUATU</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>165.506,-13.114,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 16 13:39:25.58 UTC">
    +                <name>M 5.3 - 2012 Oct 16, KYUSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>130.199,31.216,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 21 00:10:35.87 UTC">
    +                <name>M 5.3 - 2012 Oct 21, ICELAND REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-18.569,66.37,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 18:40:38.89 UTC">
    +                <name>M 5.3 - 2012 Oct 25, FIJI REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-178.259,-19.857,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 23:05:24.00 UTC">
    +                <name>M 5.3 - 2012 Oct 25, SOUTHERN ITALY</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>16.01,39.88,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 02:43:08.62 UTC">
    +                <name>M 5.3 - 2012 Oct 28, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.635,10.431,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 18:57:31.30 UTC">
    +                <name>M 5.3 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-132.374,52.472,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 2 01:22:14.04 UTC">
    +                <name>M 5.3 - 2012 Nov 2, FLORES SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>121.64,-7.788,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 3 11:12:21.68 UTC">
    +                <name>M 5.3 - 2012 Nov 3, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>126.93,10.515,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 4 23:46:38.73 UTC">
    +                <name>M 5.3 - 2012 Nov 4, KEPULAUAN SANGIHE, INDONESIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>125.076,4.6,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 14:20:36.12 UTC">
    +                <name>M 5.3 - 2012 Nov 6, BISMARCK SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>148.846,-3.836,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 05:51:42.25 UTC">
    +                <name>M 5.3 - 2012 Nov 9, SOUTH OF AUSTRALIA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>124.723,-42.732,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 15:07:51.05 UTC">
    +                <name>M 5.3 - 2012 Nov 9, BALI SEA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>117.614,-7.947,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 21:33:07.72 UTC">
    +                <name>M 5.3 - 2012 Nov 11, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>151.688,-5.45,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 16 08:25:57.76 UTC">
    +                <name>M 5.3 - 2012 Nov 16, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>140.988,35.217,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 10:55:09.03 UTC">
    +                <name>M 5.3 - 2012 Nov 19, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>151.655,-5.785,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 17:54:07.46 UTC">
    +                <name>M 5.3 - 2012 Nov 19, PAKISTAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>67.584,30.538,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 22 05:21:47.86 UTC">
    +                <name>M 5.3 - 2012 Nov 22, KOMANDORSKIYE OSTROVA REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>168.316,54.099,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 23 20:21:30.45 UTC">
    +                <name>M 5.3 - 2012 Nov 23, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>141.694,38.212,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 10:37:41.85 UTC">
    +                <name>M 5.3 - 2012 Nov 29, FIJI REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-177.157,-20.721,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 1 08:00:57.94 UTC">
    +                <name>M 5.3 - 2012 Dec 1, ALASKA PENINSULA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-154.121,58.422,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 6 21:33:25.53 UTC">
    +                <name>M 5.3 - 2012 Dec 6, BOUVET ISLAND REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-1.917,-54.179,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 14:08:42.86 UTC">
    +                <name>M 5.3 - 2012 Dec 7, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>88.098,38.745,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 13 00:53:28.77 UTC">
    +                <name>M 5.3 - 2012 Dec 13, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-178.39,-30.911,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 13 21:26:39.18 UTC">
    +                <name>M 5.3 - 2012 Dec 13, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-89.967,13.071,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 24 07:54:10.00 UTC">
    +                <name>M 5.3 - 2012 Dec 24, NEAR EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>162.034,54.907,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 20:24:26.65 UTC">
    +                <name>M 5.3 - 2012 Dec 26, ASCENSION ISLAND REGION</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-13.132,-10.411,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 27 05:36:12.06 UTC">
    +                <name>M 5.3 - 2012 Dec 27, BARBADOS REGION, WINDWARD ISL.</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-58.002,13.006,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 30 12:21:21.87 UTC">
    +                <name>M 5.3 - 2012 Dec 30, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5.3</magnitude>
    +                <Point>
    +                    <coordinates>-92.991,14.446,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 16:45:05.02 UTC">
    +                <name>M 5.2 - 2012 Jan 1, SOUTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>172.881,-43.476,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 18:09:06.46 UTC">
    +                <name>M 5.2 - 2012 Jan 1, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>96.419,4.547,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 3 15:40:02.35 UTC">
    +                <name>M 5.2 - 2012 Jan 3, GUAM REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.737,12.689,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 15:35:51.03 UTC">
    +                <name>M 5.2 - 2012 Jan 4, KEPULAUAN BARAT DAYA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>128.737,-7.354,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 18:56:57.29 UTC">
    +                <name>M 5.2 - 2012 Jan 4, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>166.375,-10.67,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 21:55:21.32 UTC">
    +                <name>M 5.2 - 2012 Jan 5, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-143.95,-56.146,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 8 00:35:52.50 UTC">
    +                <name>M 5.2 - 2012 Jan 8, FIJI REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.541,-17.897,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 19:53:35.30 UTC">
    +                <name>M 5.2 - 2012 Jan 9, SOUTHERN IRAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>55.726,27.034,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 21:30:51.00 UTC">
    +                <name>M 5.2 - 2012 Jan 9, OFFSHORE VALPARAISO, CHILE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-71.594,-32.573,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 10 01:45:39.04 UTC">
    +                <name>M 5.2 - 2012 Jan 10, VANUATU REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>171.404,-16.993,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 10 13:08:38.95 UTC">
    +                <name>M 5.2 - 2012 Jan 10, TONGA REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-172.779,-17.414,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 11 22:21:33.99 UTC">
    +                <name>M 5.2 - 2012 Jan 11, BANDA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>123.462,-6.885,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 05:37:28.76 UTC">
    +                <name>M 5.2 - 2012 Jan 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>142.461,38.336,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 19:46:04.82 UTC">
    +                <name>M 5.2 - 2012 Jan 13, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-130.72,51.076,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 15 13:41:31.42 UTC">
    +                <name>M 5.2 - 2012 Jan 15, OFF E. COAST OF N. ISLAND, N.Z.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>177.217,-36.573,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 08:26:17.39 UTC">
    +                <name>M 5.2 - 2012 Jan 16, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>123.09,0.002,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 01:19:36.45 UTC">
    +                <name>M 5.2 - 2012 Jan 18, MOLUCCA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.898,-0.951,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 09:01:55.97 UTC">
    +                <name>M 5.2 - 2012 Jan 19, OFF W. COAST OF S. ISLAND, N.Z.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>165.604,-46.763,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 20 13:30:50.59 UTC">
    +                <name>M 5.2 - 2012 Jan 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.311,37.75,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 22 21:03:35.95 UTC">
    +                <name>M 5.2 - 2012 Jan 22, LAKE RUDOLF REGION, KENYA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>36.091,3.678,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 23 11:45:43.70 UTC">
    +                <name>M 5.2 - 2012 Jan 23, EASTERN HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.965,37.108,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 16:04:09.30 UTC">
    +                <name>M 5.2 - 2012 Jan 25, SOUTH PACIFIC OCEAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-122.202,-6.672,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 20:42:55.91 UTC">
    +                <name>M 5.2 - 2012 Jan 25, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.573,38.19,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 26 04:24:58.30 UTC">
    +                <name>M 5.2 - 2012 Jan 26, DODECANESE ISLANDS, GREECE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>25.07,36.06,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 26 04:42:05.57 UTC">
    +                <name>M 5.2 - 2012 Jan 26, ROTA REGION, N. MARIANA ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>145.189,14.338,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 22:43:16.14 UTC">
    +                <name>M 5.2 - 2012 Jan 27, EASTERN HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>138.888,35.474,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 17:45:46.80 UTC">
    +                <name>M 5.2 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-177.267,-29.452,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 18:17:50.68 UTC">
    +                <name>M 5.2 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-177.175,-29.69,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 29 00:06:52.05 UTC">
    +                <name>M 5.2 - 2012 Jan 29, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>124.727,5.936,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 30 13:20:35.83 UTC">
    +                <name>M 5.2 - 2012 Jan 30, SIMEULUE, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>96.647,2.072,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 17:28:46.42 UTC">
    +                <name>M 5.2 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>167.304,-17.597,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 17:45:28.54 UTC">
    +                <name>M 5.2 - 2012 Feb 4, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>167.81,-17.086,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 5 00:05:57.01 UTC">
    +                <name>M 5.2 - 2012 Feb 5, KEPULAUAN KAI, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>133.949,-5.489,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 5 07:10:16.58 UTC">
    +                <name>M 5.2 - 2012 Feb 5, KYRGYZSTAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>74.784,41.437,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 7 23:37:05.34 UTC">
    +                <name>M 5.2 - 2012 Feb 7, SVALBARD REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>7.323,76.848,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 10:54:42.29 UTC">
    +                <name>M 5.2 - 2012 Feb 8, NEAR COAST OF ECUADOR</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-79.258,0.658,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 10 05:36:22.56 UTC">
    +                <name>M 5.2 - 2012 Feb 10, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>132.794,-0.266,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 12 04:41:01.15 UTC">
    +                <name>M 5.2 - 2012 Feb 12, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>144.252,38.529,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 09:09:14.38 UTC">
    +                <name>M 5.2 - 2012 Feb 13, TONGA REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-172.415,-18.175,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 01:34:38.70 UTC">
    +                <name>M 5.2 - 2012 Feb 14, AEGEAN SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>24.09,40.13,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 01:49:40.01 UTC">
    +                <name>M 5.2 - 2012 Feb 15, FIJI REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-177.378,-19.321,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 14:07:51.22 UTC">
    +                <name>M 5.2 - 2012 Feb 15, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>146.958,-7.501,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 17 15:44:23.23 UTC">
    +                <name>M 5.2 - 2012 Feb 17, WESTERN XIZANG</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>82.787,32.388,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 19 05:54:49.68 UTC">
    +                <name>M 5.2 - 2012 Feb 19, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.313,36.761,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 02:28:17.77 UTC">
    +                <name>M 5.2 - 2012 Feb 20, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>99.562,1.846,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 25 06:15:13.60 UTC">
    +                <name>M 5.2 - 2012 Feb 25, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>155.941,49.186,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 25 06:18:04.29 UTC">
    +                <name>M 5.2 - 2012 Feb 25, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>155.982,49.23,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 11:59:06.80 UTC">
    +                <name>M 5.2 - 2012 Feb 26, SOUTHWESTERN SIBERIA, RUSSIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>95.981,51.687,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 16:28:31.86 UTC">
    +                <name>M 5.2 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-176.946,-25.159,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 01:11:48.39 UTC">
    +                <name>M 5.2 - 2012 Feb 27, SOUTHWESTERN RYUKYU ISL., JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>123.355,23.873,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 18:48:55.30 UTC">
    +                <name>M 5.2 - 2012 Feb 27, CENTRAL IRAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>56.778,31.428,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 09:00:05.37 UTC">
    +                <name>M 5.2 - 2012 Feb 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.513,37.305,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 00:51:39.29 UTC">
    +                <name>M 5.2 - 2012 Mar 4, FIJI REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.593,-17.835,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 03:31:08.40 UTC">
    +                <name>M 5.2 - 2012 Mar 4, AEGEAN SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>24.059,40.134,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 6 11:36:23.17 UTC">
    +                <name>M 5.2 - 2012 Mar 6, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>100.16,-0.836,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 7 06:18:11.03 UTC">
    +                <name>M 5.2 - 2012 Mar 7, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>127.13,25.609,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 8 03:51:35.04 UTC">
    +                <name>M 5.2 - 2012 Mar 8, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>167.152,-17.895,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 14 09:18:42.80 UTC">
    +                <name>M 5.2 - 2012 Mar 14, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-176.331,-24.412,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 14 21:38:55.35 UTC">
    +                <name>M 5.2 - 2012 Mar 14, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.612,35.502,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 16 00:38:03.91 UTC">
    +                <name>M 5.2 - 2012 Mar 16, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>156.074,49.274,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 17 15:16:23.75 UTC">
    +                <name>M 5.2 - 2012 Mar 17, CARLSBERG RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>63.41,3.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 19:02:38.90 UTC">
    +                <name>M 5.2 - 2012 Mar 20, OFFSHORE OAXACA, MEXICO</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-98.715,15.899,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 25 10:26:54.06 UTC">
    +                <name>M 5.2 - 2012 Mar 25, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-23.863,-60.835,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 25 13:22:41.65 UTC">
    +                <name>M 5.2 - 2012 Mar 25, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.603,37.692,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 17:56:18.91 UTC">
    +                <name>M 5.2 - 2012 Mar 26, NORTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-104.373,9.581,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 28 08:54:16.23 UTC">
    +                <name>M 5.2 - 2012 Mar 28, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>101.235,-4.403,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 19:51:05.98 UTC">
    +                <name>M 5.2 - 2012 Mar 30, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>154.659,-6.583,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 22:02:11.96 UTC">
    +                <name>M 5.2 - 2012 Mar 30, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>95.115,4.616,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 31 09:04:39.27 UTC">
    +                <name>M 5.2 - 2012 Mar 31, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>179.299,-23.63,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 07:25:08.00 UTC">
    +                <name>M 5.2 - 2012 Apr 3, TARAPACA, CHILE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-69.093,-19.638,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 7 20:09:40.55 UTC">
    +                <name>M 5.2 - 2012 Apr 7, BANDA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>130.684,-6.057,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 04:53:26.38 UTC">
    +                <name>M 5.2 - 2012 Apr 11, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-14.397,-16.856,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:08:01.61 UTC">
    +                <name>M 5.2 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>91.802,1.252,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 14:08:40.40 UTC">
    +                <name>M 5.2 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>92.11,1.107,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 15:46:51.22 UTC">
    +                <name>M 5.2 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>92.2,2.945,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 21:02:39.41 UTC">
    +                <name>M 5.2 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>91.662,0.306,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 11:20:00.98 UTC">
    +                <name>M 5.2 - 2012 Apr 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.032,36.865,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 13:09:45.56 UTC">
    +                <name>M 5.2 - 2012 Apr 12, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>93.401,2.396,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 10:10:04.62 UTC">
    +                <name>M 5.2 - 2012 Apr 13, OAXACA, MEXICO</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-98.138,16.236,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 16:05:53.62 UTC">
    +                <name>M 5.2 - 2012 Apr 16, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>92.235,0.176,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 19 03:33:27.36 UTC">
    +                <name>M 5.2 - 2012 Apr 19, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.181,36.976,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 19:34:06.77 UTC">
    +                <name>M 5.2 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>93.824,3.249,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 21 11:04:30.40 UTC">
    +                <name>M 5.2 - 2012 Apr 21, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>93.723,3.268,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 21 12:36:11.02 UTC">
    +                <name>M 5.2 - 2012 Apr 21, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>170.183,-21.825,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 22 11:44:32.60 UTC">
    +                <name>M 5.2 - 2012 Apr 22, SOLOMON ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>158.583,-9.192,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 00:20:34.44 UTC">
    +                <name>M 5.2 - 2012 Apr 23, GUATEMALA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-89.211,15.766,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 14:54:54.62 UTC">
    +                <name>M 5.2 - 2012 Apr 23, KEPULAUAN BABAR, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>129.226,-7.22,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 25 13:27:20.98 UTC">
    +                <name>M 5.2 - 2012 Apr 25, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-14.631,-12.771,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 29 01:57:51.90 UTC">
    +                <name>M 5.2 - 2012 Apr 29, PAPUA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>136.109,-3.059,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 13:06:58.74 UTC">
    +                <name>M 5.2 - 2012 Apr 30, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>93.327,14.405,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 10:09:35.64 UTC">
    +                <name>M 5.2 - 2012 May 3, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>47.726,32.747,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 10:19:36.85 UTC">
    +                <name>M 5.2 - 2012 May 3, GANSU-NEI MONGOL BDR REG, CHINA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>98.525,40.513,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 15:20:25.67 UTC">
    +                <name>M 5.2 - 2012 May 3, WESTERN TURKEY</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>29.092,39.175,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 10 00:28:21.78 UTC">
    +                <name>M 5.2 - 2012 May 10, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>142.171,39.772,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 10 19:11:22.65 UTC">
    +                <name>M 5.2 - 2012 May 10, MOLUCCA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.293,1.38,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 15 19:22:24.39 UTC">
    +                <name>M 5.2 - 2012 May 15, SOUTH OF TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-175.967,-24.568,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 16 01:28:17.96 UTC">
    +                <name>M 5.2 - 2012 May 16, SOUTH OF TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-175.965,-24.566,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 21 03:18:44.39 UTC">
    +                <name>M 5.2 - 2012 May 21, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>127.284,27.197,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 21 10:17:11.14 UTC">
    +                <name>M 5.2 - 2012 May 21, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.47,39.364,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 23 17:50:27.32 UTC">
    +                <name>M 5.2 - 2012 May 23, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-27.607,0.849,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 27 03:50:21.76 UTC">
    +                <name>M 5.2 - 2012 May 27, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>100.155,-2.82,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 01:56:04.44 UTC">
    +                <name>M 5.2 - 2012 May 29, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>144.645,39.099,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 30 21:22:32.83 UTC">
    +                <name>M 5.2 - 2012 May 30, NEAR EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>161.392,54.739,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 5 07:20:52.60 UTC">
    +                <name>M 5.2 - 2012 Jun 5, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>168.324,-18.114,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 16:15:13.63 UTC">
    +                <name>M 5.2 - 2012 Jun 14, TAIWAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>121.584,23.699,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 21:51:26.28 UTC">
    +                <name>M 5.2 - 2012 Jun 14, NORTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>84.279,42.166,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 10:53:29.00 UTC">
    +                <name>M 5.2 - 2012 Jun 19, NEAR S.E. COAST OF AUSTRALIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>146.2,-38.304,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 13:40:57.26 UTC">
    +                <name>M 5.2 - 2012 Jun 19, OFF COAST OF OREGON</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-127.273,43.443,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 20 09:59:04.59 UTC">
    +                <name>M 5.2 - 2012 Jun 20, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>103.194,-4.928,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 21 20:32:46.58 UTC">
    +                <name>M 5.2 - 2012 Jun 21, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.364,39.38,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 21 23:52:53.27 UTC">
    +                <name>M 5.2 - 2012 Jun 21, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.622,-32.829,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 26 14:17:40.92 UTC">
    +                <name>M 5.2 - 2012 Jun 26, KAMCHATKA PENINSULA, RUSSIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>160.519,56.157,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 12:57:01.34 UTC">
    +                <name>M 5.2 - 2012 Jun 27, FLORES REGION, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>119.938,-8.304,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 21:20:56.67 UTC">
    +                <name>M 5.2 - 2012 Jun 27, TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-173.341,-15.138,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 28 05:51:53.56 UTC">
    +                <name>M 5.2 - 2012 Jun 28, EASTERN HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.934,37.165,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 29 13:00:26.08 UTC">
    +                <name>M 5.2 - 2012 Jun 29, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-93.062,14.939,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 30 00:09:09.80 UTC">
    +                <name>M 5.2 - 2012 Jun 30, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.386,28.134,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 8 14:41:23.12 UTC">
    +                <name>M 5.2 - 2012 Jul 8, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-24.188,-56.456,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 10 10:26:59.57 UTC">
    +                <name>M 5.2 - 2012 Jul 10, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>134.248,-1.692,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 12 01:43:01.62 UTC">
    +                <name>M 5.2 - 2012 Jul 12, GUATEMALA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-90.938,14.75,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 12 04:11:16.04 UTC">
    +                <name>M 5.2 - 2012 Jul 12, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>165.977,-10.752,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 03:43:58.05 UTC">
    +                <name>M 5.2 - 2012 Jul 14, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>151.329,45.54,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 19:55:17.95 UTC">
    +                <name>M 5.2 - 2012 Jul 14, MYANMAR-INDIA BORDER REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>94.48,25.424,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 20:39:30.48 UTC">
    +                <name>M 5.2 - 2012 Jul 14, TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-173.13,-17.787,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 15 14:34:46.40 UTC">
    +                <name>M 5.2 - 2012 Jul 15, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>146.41,43.244,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 17 12:38:45.50 UTC">
    +                <name>M 5.2 - 2012 Jul 17, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.991,-31.431,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 06:04:21.25 UTC">
    +                <name>M 5.2 - 2012 Jul 21, OFFSHORE NORTHERN CALIFORNIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-125.331,40.412,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 23 00:22:04.72 UTC">
    +                <name>M 5.2 - 2012 Jul 23, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>135.414,-2.537,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 24 05:25:28.70 UTC">
    +                <name>M 5.2 - 2012 Jul 24, OAXACA, MEXICO</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-98.307,16.266,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 24 06:50:06.02 UTC">
    +                <name>M 5.2 - 2012 Jul 24, WESTERN IRAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>50.961,31.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 26 01:06:31.77 UTC">
    +                <name>M 5.2 - 2012 Jul 26, TAJIKISTAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>74.008,38.313,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 26 06:12:12.36 UTC">
    +                <name>M 5.2 - 2012 Jul 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.61,-26.442,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 29 14:21:18.43 UTC">
    +                <name>M 5.2 - 2012 Jul 29, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>104.418,-5.997,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 31 06:50:15.61 UTC">
    +                <name>M 5.2 - 2012 Jul 31, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>103.086,-4.769,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 03:15:27.01 UTC">
    +                <name>M 5.2 - 2012 Aug 2, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>154.833,-6.319,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 10:26:14.64 UTC">
    +                <name>M 5.2 - 2012 Aug 2, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-179.786,-35.56,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 7 00:39:02.00 UTC">
    +                <name>M 5.2 - 2012 Aug 7, ATACAMA, CHILE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-70.803,-27.801,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 8 14:05:17.90 UTC">
    +                <name>M 5.2 - 2012 Aug 8, ALASKA PENINSULA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-161.214,54.881,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 8 22:42:14.10 UTC">
    +                <name>M 5.2 - 2012 Aug 8, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-26.056,-55.769,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 9 07:09:46.67 UTC">
    +                <name>M 5.2 - 2012 Aug 9, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-15.876,-33.145,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 10 11:40:03.43 UTC">
    +                <name>M 5.2 - 2012 Aug 10, WEST CHILE RISE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-83.235,-42.814,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 11 17:47:30.92 UTC">
    +                <name>M 5.2 - 2012 Aug 11, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-31.541,52.149,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 14 02:13:57.45 UTC">
    +                <name>M 5.2 - 2012 Aug 14, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-26.108,-59.203,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 15 10:04:29.12 UTC">
    +                <name>M 5.2 - 2012 Aug 15, REYKJANES RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-32.646,57.886,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 17 00:12:10.39 UTC">
    +                <name>M 5.2 - 2012 Aug 17, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-18.707,-0.353,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 19 01:57:03.64 UTC">
    +                <name>M 5.2 - 2012 Aug 19, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>94.753,5.063,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 21 03:09:39.42 UTC">
    +                <name>M 5.2 - 2012 Aug 21, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>102.995,-4.749,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 22 18:19:33.75 UTC">
    +                <name>M 5.2 - 2012 Aug 22, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>179.107,-24.085,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 23 15:54:50.88 UTC">
    +                <name>M 5.2 - 2012 Aug 23, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-25.452,-58.895,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 25 18:36:59.82 UTC">
    +                <name>M 5.2 - 2012 Aug 25, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.969,36.962,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 05:23:20.34 UTC">
    +                <name>M 5.2 - 2012 Aug 27, GULF OF CALIFORNIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-113.89,30.551,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 09:01:23.13 UTC">
    +                <name>M 5.2 - 2012 Aug 27, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>99.031,2.376,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 12:05:23.41 UTC">
    +                <name>M 5.2 - 2012 Aug 27, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>92.802,10.21,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 23:16:00.51 UTC">
    +                <name>M 5.2 - 2012 Aug 27, GULF OF CALIFORNIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-113.909,30.545,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 06:56:44.28 UTC">
    +                <name>M 5.2 - 2012 Aug 30, CERAM SEA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>131.002,-3.432,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 13:51:05.88 UTC">
    +                <name>M 5.2 - 2012 Aug 30, JAN MAYEN ISLAND REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-7.936,70.94,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 13:16:14.03 UTC">
    +                <name>M 5.2 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.777,10.372,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 13:31:37.36 UTC">
    +                <name>M 5.2 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.916,10.456,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 18:21:34.47 UTC">
    +                <name>M 5.2 - 2012 Aug 31, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>151.891,-4.95,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 20:26:49.87 UTC">
    +                <name>M 5.2 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.73,10.727,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 19:46:07.88 UTC">
    +                <name>M 5.2 - 2012 Sep 1, TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-174.552,-21.623,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 2 12:44:03.46 UTC">
    +                <name>M 5.2 - 2012 Sep 2, SVALBARD REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>6.764,78.43,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 06:58:02.40 UTC">
    +                <name>M 5.2 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.701,10.535,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 12:07:26.63 UTC">
    +                <name>M 5.2 - 2012 Sep 4, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>113.822,-10.771,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 13:31:04.18 UTC">
    +                <name>M 5.2 - 2012 Sep 4, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>113.911,-10.652,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 15:11:13.35 UTC">
    +                <name>M 5.2 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.712,10.611,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 01:57:08.90 UTC">
    +                <name>M 5.2 - 2012 Sep 6, SOUTHERN IRAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>53.879,26.942,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 06:58:58.83 UTC">
    +                <name>M 5.2 - 2012 Sep 7, CHAGOS ARCHIPELAGO REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>72.179,-6.877,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 8 04:11:48.48 UTC">
    +                <name>M 5.2 - 2012 Sep 8, SOLOMON ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>160.404,-9.849,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 05:39:18.03 UTC">
    +                <name>M 5.2 - 2012 Sep 9, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>155.517,49.427,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 10 14:35:42.81 UTC">
    +                <name>M 5.2 - 2012 Sep 10, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>93.616,10.459,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 18:10:06.90 UTC">
    +                <name>M 5.2 - 2012 Sep 11, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.758,10.709,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 11:27:50.79 UTC">
    +                <name>M 5.2 - 2012 Sep 12, SOLOMON ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>161.016,-10.11,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 17:22:08.44 UTC">
    +                <name>M 5.2 - 2012 Sep 13, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.388,35.752,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 16 05:51:09.45 UTC">
    +                <name>M 5.2 - 2012 Sep 16, COSTA RICA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-85.459,10.168,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 18 01:46:44.39 UTC">
    +                <name>M 5.2 - 2012 Sep 18, MOLUCCA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>125.849,1.336,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 18 03:23:45.84 UTC">
    +                <name>M 5.2 - 2012 Sep 18, SOUTHWEST OF SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>103.739,-6.169,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 01:43:14.17 UTC">
    +                <name>M 5.2 - 2012 Sep 25, SOUTH OF AFRICA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>25.583,-53.21,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 05:37:58.39 UTC">
    +                <name>M 5.2 - 2012 Sep 25, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>178.327,-25.555,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 08:33:00.73 UTC">
    +                <name>M 5.2 - 2012 Sep 25, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>69.211,36.277,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 19:42:33.08 UTC">
    +                <name>M 5.2 - 2012 Sep 25, SOLOMON ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>159.746,-9.748,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 28 23:22:32.67 UTC">
    +                <name>M 5.2 - 2012 Sep 28, SOLOMON ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>157.425,-8.93,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 1 17:32:28.95 UTC">
    +                <name>M 5.2 - 2012 Oct 1, OFF COAST OF JALISCO, MEXICO</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-107.164,18.607,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 1 22:55:47.25 UTC">
    +                <name>M 5.2 - 2012 Oct 1, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.189,39.74,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 18:37:38.70 UTC">
    +                <name>M 5.2 - 2012 Oct 2, ASSAM, INDIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>92.818,26.879,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 10 16:56:33.00 UTC">
    +                <name>M 5.2 - 2012 Oct 10, SOUTHERN IRAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>52.49,29.33,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 12 04:57:19.83 UTC">
    +                <name>M 5.2 - 2012 Oct 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>140.346,35.764,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 20:19:00.50 UTC">
    +                <name>M 5.2 - 2012 Oct 15, FIJI REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-178.251,-20.446,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 22 09:55:55.15 UTC">
    +                <name>M 5.2 - 2012 Oct 22, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-25.59,-56.301,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 27 09:32:30.22 UTC">
    +                <name>M 5.2 - 2012 Oct 27, CARLSBERG RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>57.091,9.82,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 27 15:46:28.38 UTC">
    +                <name>M 5.2 - 2012 Oct 27, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.812,10.415,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 04:25:28.51 UTC">
    +                <name>M 5.2 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-132.49,52.624,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 06:57:23.50 UTC">
    +                <name>M 5.2 - 2012 Nov 1, KURIL ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>155.489,49.399,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 5 00:24:43.50 UTC">
    +                <name>M 5.2 - 2012 Nov 5, SOUTHWEST INDIAN RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>61.983,-28.775,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 5 00:29:59.16 UTC">
    +                <name>M 5.2 - 2012 Nov 5, MOLUCCA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>125.236,-0.144,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 8 17:38:05.32 UTC">
    +                <name>M 5.2 - 2012 Nov 8, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>117.354,-49.699,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 03:51:57.87 UTC">
    +                <name>M 5.2 - 2012 Nov 9, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.246,36.838,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 09:54:39.80 UTC">
    +                <name>M 5.2 - 2012 Nov 9, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-27.344,-56.391,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 19:59:45.99 UTC">
    +                <name>M 5.2 - 2012 Nov 9, NIAS REGION, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>97.464,0.886,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 14:56:10.59 UTC">
    +                <name>M 5.2 - 2012 Nov 11, SANTA CRUZ ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>164.828,-10.755,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 12 14:47:41.50 UTC">
    +                <name>M 5.2 - 2012 Nov 12, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>178.919,-24.277,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 14 07:41:53.21 UTC">
    +                <name>M 5.2 - 2012 Nov 14, TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-174.079,-20.739,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 14 18:22:32.06 UTC">
    +                <name>M 5.2 - 2012 Nov 14, FIJI REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>173.738,-14.484,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 20 17:08:59.79 UTC">
    +                <name>M 5.2 - 2012 Nov 20, TAIWAN REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>121.454,22.339,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 21 22:52:29.37 UTC">
    +                <name>M 5.2 - 2012 Nov 21, OFFSHORE O'HIGGINS, CHILE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-71.994,-33.916,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 22 18:01:10.39 UTC">
    +                <name>M 5.2 - 2012 Nov 22, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>102.826,-4.535,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 27 11:58:19.35 UTC">
    +                <name>M 5.2 - 2012 Nov 27, PAPUA, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>138.75,-2.712,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 08:36:20.37 UTC">
    +                <name>M 5.2 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.93,37.325,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 23:00:00.50 UTC">
    +                <name>M 5.2 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>143.762,37.739,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 9 20:52:39.24 UTC">
    +                <name>M 5.2 - 2012 Dec 9, TONGA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-174.22,-16.236,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 10 20:47:51.22 UTC">
    +                <name>M 5.2 - 2012 Dec 10, BANDA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>129.904,-6.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 11 12:18:21.49 UTC">
    +                <name>M 5.2 - 2012 Dec 11, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>167.669,-18.974,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 14 04:04:06.86 UTC">
    +                <name>M 5.2 - 2012 Dec 14, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-91.679,13.926,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 15 04:27:02.33 UTC">
    +                <name>M 5.2 - 2012 Dec 15, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.205,37.311,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 16 10:07:12.04 UTC">
    +                <name>M 5.2 - 2012 Dec 16, GUATEMALA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-91.457,14.675,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 16 15:52:00.92 UTC">
    +                <name>M 5.2 - 2012 Dec 16, ROTA REGION, N. MARIANA ISLANDS</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>146.733,14.185,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 23 00:29:56.57 UTC">
    +                <name>M 5.2 - 2012 Dec 23, VANUATU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>167.11,-13.086,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 23 06:38:57.45 UTC">
    +                <name>M 5.2 - 2012 Dec 23, TURKEY-IRAN BORDER REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>44.868,38.458,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 23 12:52:24.45 UTC">
    +                <name>M 5.2 - 2012 Dec 23, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.584,4.19,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 25 22:44:33.51 UTC">
    +                <name>M 5.2 - 2012 Dec 25, BLACK SEA, OFFSHORE GEORGIA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>40.974,42.436,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 13:17:57.26 UTC">
    +                <name>M 5.2 - 2012 Dec 26, BIO-BIO, CHILE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-73.267,-37.274,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 27 16:33:59.87 UTC">
    +                <name>M 5.2 - 2012 Dec 27, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-14.876,-12.396,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 23:05:24.20 UTC">
    +                <name>M 5.2 - 2012 Dec 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>141.168,37.018,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 30 06:44:41.48 UTC">
    +                <name>M 5.2 - 2012 Dec 30, SCOTIA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-36.887,-60.906,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 30 11:33:43.03 UTC">
    +                <name>M 5.2 - 2012 Dec 30, MOLUCCA SEA</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>126.587,1.569,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 30 20:49:27.83 UTC">
    +                <name>M 5.2 - 2012 Dec 30, CENTRAL PERU</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>-70.938,-12.861,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 31 20:02:04.16 UTC">
    +                <name>M 5.2 - 2012 Dec 31, BALLENY ISLANDS REGION</name>
    +                <magnitude>5.2</magnitude>
    +                <Point>
    +                    <coordinates>154.301,-61.623,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 00:30:08.77 UTC">
    +                <name>M 5.1 - 2012 Jan 1, GUAM REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.487,12.008,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 12:27:57.25 UTC">
    +                <name>M 5.1 - 2012 Jan 1, SOUTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>172.852,-43.47,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 15:44:12.07 UTC">
    +                <name>M 5.1 - 2012 Jan 1, GUAM REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.607,12.018,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 21:07:24.67 UTC">
    +                <name>M 5.1 - 2012 Jan 1, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>128.368,2.205,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 22:59:09.10 UTC">
    +                <name>M 5.1 - 2012 Jan 1, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>166.921,-14.381,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 04:47:34.05 UTC">
    +                <name>M 5.1 - 2012 Jan 4, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.44,-14.748,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 23:14:49.12 UTC">
    +                <name>M 5.1 - 2012 Jan 5, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>99.069,-0.935,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 6 00:38:48.40 UTC">
    +                <name>M 5.1 - 2012 Jan 6, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-14.263,-14.092,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 18:22:19.33 UTC">
    +                <name>M 5.1 - 2012 Jan 7, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-166.854,-63.697,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 20:00:44.74 UTC">
    +                <name>M 5.1 - 2012 Jan 7, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.318,-17.549,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 00:06:54.80 UTC">
    +                <name>M 5.1 - 2012 Jan 9, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.142,-15.063,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 10 18:52:09.11 UTC">
    +                <name>M 5.1 - 2012 Jan 10, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.937,2.386,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 11 17:08:00.60 UTC">
    +                <name>M 5.1 - 2012 Jan 11, NORTHERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>52.775,36.328,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 17:49:10.12 UTC">
    +                <name>M 5.1 - 2012 Jan 13, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.566,-32.809,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 20:03:46.82 UTC">
    +                <name>M 5.1 - 2012 Jan 13, SIMEULUE, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.273,2.405,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 20:25:24.54 UTC">
    +                <name>M 5.1 - 2012 Jan 13, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.476,-32.882,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 14 13:47:53.18 UTC">
    +                <name>M 5.1 - 2012 Jan 14, SOUTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>172.765,-43.538,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 15 18:41:09.13 UTC">
    +                <name>M 5.1 - 2012 Jan 15, SOUTH SHETLAND ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-53.571,-61.33,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 15 19:06:45.51 UTC">
    +                <name>M 5.1 - 2012 Jan 15, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-12.912,-15.675,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 15:06:03.09 UTC">
    +                <name>M 5.1 - 2012 Jan 16, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-25.045,-56.718,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 18:20:50.47 UTC">
    +                <name>M 5.1 - 2012 Jan 16, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.424,-33.33,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 16:09:08.65 UTC">
    +                <name>M 5.1 - 2012 Jan 18, MOLUCCA SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.806,-0.906,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 17:26:03.55 UTC">
    +                <name>M 5.1 - 2012 Jan 18, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>165.478,-11.023,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 04:06:42.00 UTC">
    +                <name>M 5.1 - 2012 Jan 19, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>165.477,-10.979,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 12:35:51.30 UTC">
    +                <name>M 5.1 - 2012 Jan 19, NORTHEASTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>58.835,36.288,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 23 20:50:14.50 UTC">
    +                <name>M 5.1 - 2012 Jan 23, DOMINICAN REPUBLIC</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-70.077,19.542,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 24 10:34:19.08 UTC">
    +                <name>M 5.1 - 2012 Jan 24, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.102,8.584,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 00:45:57.71 UTC">
    +                <name>M 5.1 - 2012 Jan 25, FOX ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-167.049,52.654,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 06:02:34.75 UTC">
    +                <name>M 5.1 - 2012 Jan 25, OFF W. COAST OF S. ISLAND, N.Z.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>165.714,-46.68,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 07:17:04.22 UTC">
    +                <name>M 5.1 - 2012 Jan 25, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>152.136,-5.209,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 00:06:49.36 UTC">
    +                <name>M 5.1 - 2012 Jan 27, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>138.259,-1.452,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 09:54:31.75 UTC">
    +                <name>M 5.1 - 2012 Jan 27, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>93.018,2.732,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 17:16:25.93 UTC">
    +                <name>M 5.1 - 2012 Jan 27, SOUTH OF LOMBOK, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>116.013,-10.369,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 04:46:23.41 UTC">
    +                <name>M 5.1 - 2012 Jan 28, BABUYAN ISL REGION, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>121.205,19.251,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 08:51:36.27 UTC">
    +                <name>M 5.1 - 2012 Jan 28, POTOSI, BOLIVIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-66.436,-21.464,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 14:47:19.36 UTC">
    +                <name>M 5.1 - 2012 Jan 28, SIMEULUE, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.678,2.07,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 17:45:30.74 UTC">
    +                <name>M 5.1 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.166,-29.428,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 29 18:18:24.19 UTC">
    +                <name>M 5.1 - 2012 Jan 29, KYUSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>131.806,32.588,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 31 19:58:02.47 UTC">
    +                <name>M 5.1 - 2012 Jan 31, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.18,-14.255,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 02:09:22.35 UTC">
    +                <name>M 5.1 - 2012 Feb 2, WESTERN INDIAN-ANTARCTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.049,-49.649,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 13:39:23.13 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.53,-17.754,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 15:04:52.86 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.32,-17.686,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 16:11:35.95 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.429,-17.717,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 19:30:59.21 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.287,-17.395,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 19:44:34.17 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.209,-17.43,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 20:26:31.91 UTC">
    +                <name>M 5.1 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.327,-17.534,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 02:54:22.49 UTC">
    +                <name>M 5.1 - 2012 Feb 4, TAIWAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>122.602,24.699,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 18:01:46.70 UTC">
    +                <name>M 5.1 - 2012 Feb 4, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-26.746,0.922,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 5 23:50:41.19 UTC">
    +                <name>M 5.1 - 2012 Feb 5, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.987,-20.423,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 08:52:27.16 UTC">
    +                <name>M 5.1 - 2012 Feb 8, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>119.951,-0.462,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 13:06:41.20 UTC">
    +                <name>M 5.1 - 2012 Feb 8, OFF COAST OF CHIAPAS, MEXICO</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-94.427,14.682,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 02:39:40.38 UTC">
    +                <name>M 5.1 - 2012 Feb 9, SERAM, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>130.789,-3.252,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 05:09:11.73 UTC">
    +                <name>M 5.1 - 2012 Feb 9, SERAM, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>130.694,-3.339,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 11:21:08.34 UTC">
    +                <name>M 5.1 - 2012 Feb 9, CROZET ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>44.526,-41.065,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 15:18:13.59 UTC">
    +                <name>M 5.1 - 2012 Feb 9, PAPUA REGION, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>140.72,-1.755,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 18:57:03.99 UTC">
    +                <name>M 5.1 - 2012 Feb 9, NORTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.985,44.742,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 19:17:30.55 UTC">
    +                <name>M 5.1 - 2012 Feb 9, UTTARANCHAL, INDIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>78.282,30.986,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 19:21:01.65 UTC">
    +                <name>M 5.1 - 2012 Feb 9, MACQUARIE ISLAND REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>158.057,-58.307,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 11 05:10:51.31 UTC">
    +                <name>M 5.1 - 2012 Feb 11, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-176.113,-20.991,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 11 15:23:07.90 UTC">
    +                <name>M 5.1 - 2012 Feb 11, SOLOMON ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>160.274,-9.954,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 10:12:07.24 UTC">
    +                <name>M 5.1 - 2012 Feb 13, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.851,-32.554,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 04:12:23.04 UTC">
    +                <name>M 5.1 - 2012 Feb 14, SOUTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-116.529,-49.723,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 22:34:38.20 UTC">
    +                <name>M 5.1 - 2012 Feb 14, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.642,36.16,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 23:03:48.83 UTC">
    +                <name>M 5.1 - 2012 Feb 15, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.97,51.763,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 17 01:57:29.47 UTC">
    +                <name>M 5.1 - 2012 Feb 17, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>168.847,-19.367,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 18 04:50:22.43 UTC">
    +                <name>M 5.1 - 2012 Feb 18, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.415,-5.285,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 23:54:20.40 UTC">
    +                <name>M 5.1 - 2012 Feb 20, TONGA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-172.96,-17.678,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 22 00:50:43.76 UTC">
    +                <name>M 5.1 - 2012 Feb 22, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.051,-17.868,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 23 13:25:30.19 UTC">
    +                <name>M 5.1 - 2012 Feb 23, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.926,-5.197,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 23 14:09:09.50 UTC">
    +                <name>M 5.1 - 2012 Feb 23, OFF COAST OF CHIAPAS, MEXICO</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-93.37,14.16,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 23 18:26:23.46 UTC">
    +                <name>M 5.1 - 2012 Feb 23, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-176.185,-29.231,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 25 01:12:00.09 UTC">
    +                <name>M 5.1 - 2012 Feb 25, MAUG ISLANDS REG, N. MARIANA IS.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>145.747,19.122,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 25 05:06:21.54 UTC">
    +                <name>M 5.1 - 2012 Feb 25, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>179.534,-25.686,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 25 07:01:45.35 UTC">
    +                <name>M 5.1 - 2012 Feb 25, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>125.782,11.774,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 11:07:41.20 UTC">
    +                <name>M 5.1 - 2012 Feb 26, SOUTHWESTERN SIBERIA, RUSSIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.084,51.693,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 11:17:02.95 UTC">
    +                <name>M 5.1 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.222,-24.869,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 13:49:59.05 UTC">
    +                <name>M 5.1 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.205,-24.672,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 19:37:09.24 UTC">
    +                <name>M 5.1 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.305,-24.849,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 14:34:21.87 UTC">
    +                <name>M 5.1 - 2012 Feb 27, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.123,-24.757,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 14:54:43.99 UTC">
    +                <name>M 5.1 - 2012 Feb 27, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-177.229,-24.688,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 05:33:32.54 UTC">
    +                <name>M 5.1 - 2012 Feb 28, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>156.1,49.262,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 07:05:12.11 UTC">
    +                <name>M 5.1 - 2012 Feb 29, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>166.487,-12.543,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 11:36:35.59 UTC">
    +                <name>M 5.1 - 2012 Feb 29, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>156.103,49.259,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 1 00:59:59.99 UTC">
    +                <name>M 5.1 - 2012 Mar 1, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.45,-17.893,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 16:48:30.66 UTC">
    +                <name>M 5.1 - 2012 Mar 2, RAT ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>178.588,52.228,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 3 18:22:32.53 UTC">
    +                <name>M 5.1 - 2012 Mar 3, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>170.307,-21.994,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 16:05:14.11 UTC">
    +                <name>M 5.1 - 2012 Mar 4, TONGA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.798,-22.309,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 23:17:07.17 UTC">
    +                <name>M 5.1 - 2012 Mar 4, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>102.706,-4.323,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 05:14:13.81 UTC">
    +                <name>M 5.1 - 2012 Mar 5, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>170.343,-21.591,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 06:55:30.78 UTC">
    +                <name>M 5.1 - 2012 Mar 5, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>97.062,4.197,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 07:41:04.53 UTC">
    +                <name>M 5.1 - 2012 Mar 5, HARYANA - DELHI REGION, INDIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>76.649,28.809,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 12:45:44.52 UTC">
    +                <name>M 5.1 - 2012 Mar 5, KEPULAUAN BABAR, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>129.212,-7.076,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 7 13:56:02.09 UTC">
    +                <name>M 5.1 - 2012 Mar 7, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>168.194,-19.571,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 8 15:30:27.19 UTC">
    +                <name>M 5.1 - 2012 Mar 8, MINDORO, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>120.556,13.592,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 8 18:21:38.50 UTC">
    +                <name>M 5.1 - 2012 Mar 8, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>46.957,32.874,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 13 23:16:16.51 UTC">
    +                <name>M 5.1 - 2012 Mar 13, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>166.983,-17.599,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 05:04:59.64 UTC">
    +                <name>M 5.1 - 2012 Mar 19, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>119.927,-0.882,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 17:50:50.54 UTC">
    +                <name>M 5.1 - 2012 Mar 19, KAMCHATKA PENINSULA, RUSSIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>164.724,58.864,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 18:35:36.95 UTC">
    +                <name>M 5.1 - 2012 Mar 20, OAXACA, MEXICO</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-98.22,16.228,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 20:14:39.99 UTC">
    +                <name>M 5.1 - 2012 Mar 20, OAXACA, MEXICO</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-97.987,16.478,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 21:07:11.82 UTC">
    +                <name>M 5.1 - 2012 Mar 20, TONGA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-175.753,-23.882,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 22 00:21:36.95 UTC">
    +                <name>M 5.1 - 2012 Mar 22, KEPULAUAN SANGIHE, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>125.859,3.513,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 22 16:46:47.79 UTC">
    +                <name>M 5.1 - 2012 Mar 22, OAXACA, MEXICO</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-98.343,16.334,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 25 13:59:22.57 UTC">
    +                <name>M 5.1 - 2012 Mar 25, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.774,-18.638,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 04:38:33.66 UTC">
    +                <name>M 5.1 - 2012 Mar 30, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.244,37.539,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 10:55:24.40 UTC">
    +                <name>M 5.1 - 2012 Mar 30, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>144.807,40.764,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 31 17:06:48.94 UTC">
    +                <name>M 5.1 - 2012 Mar 31, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-175.234,-20.326,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 6 19:26:15.43 UTC">
    +                <name>M 5.1 - 2012 Apr 6, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>100.452,-3.346,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 10:08:28.97 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>90.062,2.594,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 11:04:45.19 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.482,0.604,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 12:00:35.28 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.493,2.235,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 12:10:52.77 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.631,1.347,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 12:21:59.15 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.797,3.163,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 12:37:48.38 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.626,2.583,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 14:20:56.96 UTC">
    +                <name>M 5.1 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>89.933,2.833,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 15:09:25.71 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>90.164,2.674,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 16:04:23.88 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.863,3.368,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 22:15:25.88 UTC">
    +                <name>M 5.1 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.496,0.619,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 00:01:15.34 UTC">
    +                <name>M 5.1 - 2012 Apr 12, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.158,0.252,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 12:12:05.66 UTC">
    +                <name>M 5.1 - 2012 Apr 13, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.151,37.022,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 12:18:25.93 UTC">
    +                <name>M 5.1 - 2012 Apr 14, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>90.347,2.589,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 17 03:24:26.41 UTC">
    +                <name>M 5.1 - 2012 Apr 17, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>94.621,5.373,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 17:40:39.56 UTC">
    +                <name>M 5.1 - 2012 Apr 18, SOUTHEASTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>58.063,27.941,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 18:38:12.19 UTC">
    +                <name>M 5.1 - 2012 Apr 18, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>131.893,-0.545,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 18:42:57.96 UTC">
    +                <name>M 5.1 - 2012 Apr 18, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>47.004,32.506,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 01:21:07.54 UTC">
    +                <name>M 5.1 - 2012 Apr 20, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>47.023,32.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 22 03:17:28.98 UTC">
    +                <name>M 5.1 - 2012 Apr 22, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>91.745,1.379,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 02:14:18.82 UTC">
    +                <name>M 5.1 - 2012 Apr 23, SOUTH OF TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-175.993,-24.008,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 19:43:53.29 UTC">
    +                <name>M 5.1 - 2012 Apr 24, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>91.724,1.154,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 25 16:30:59.56 UTC">
    +                <name>M 5.1 - 2012 Apr 25, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>93.882,8.826,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 26 18:41:20.87 UTC">
    +                <name>M 5.1 - 2012 Apr 26, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.818,41.485,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 27 10:29:37.98 UTC">
    +                <name>M 5.1 - 2012 Apr 27, SULAWESI, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>121.913,-2.662,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 1 13:59:39.60 UTC">
    +                <name>M 5.1 - 2012 May 1, SULAWESI, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>121.906,-2.613,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 1 22:13:18.60 UTC">
    +                <name>M 5.1 - 2012 May 1, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-175.874,-30.267,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 2 05:14:05.93 UTC">
    +                <name>M 5.1 - 2012 May 2, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-176.175,-24.331,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 4 16:23:43.08 UTC">
    +                <name>M 5.1 - 2012 May 4, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>89.719,2.001,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 5 20:12:35.06 UTC">
    +                <name>M 5.1 - 2012 May 5, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.628,38.182,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 11 20:34:25.34 UTC">
    +                <name>M 5.1 - 2012 May 11, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>138.61,30.669,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 14 10:12:31.99 UTC">
    +                <name>M 5.1 - 2012 May 14, SOUTHERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>57.722,27.955,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 15 05:19:55.52 UTC">
    +                <name>M 5.1 - 2012 May 15, NORTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>89.561,2.71,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 17 21:40:42.20 UTC">
    +                <name>M 5.1 - 2012 May 17, SOUTHERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>53.851,26.942,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 18 14:47:22.40 UTC">
    +                <name>M 5.1 - 2012 May 18, AZERBAIJAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>46.789,41.439,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 01:55:50.06 UTC">
    +                <name>M 5.1 - 2012 May 20, MARIANA ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>147.257,17.017,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 06:54:43.78 UTC">
    +                <name>M 5.1 - 2012 May 20, EAST OF KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.761,44.688,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 08:40:42.83 UTC">
    +                <name>M 5.1 - 2012 May 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.197,39.577,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 22:15:45.08 UTC">
    +                <name>M 5.1 - 2012 May 20, NEAR N COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.917,-3.188,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 22:21:02.98 UTC">
    +                <name>M 5.1 - 2012 May 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.619,39.556,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 22 02:38:43.99 UTC">
    +                <name>M 5.1 - 2012 May 22, MOLUCCA SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.619,2.41,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 23 20:36:51.31 UTC">
    +                <name>M 5.1 - 2012 May 23, ROTA REGION, N. MARIANA ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>145.413,14.065,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 26 00:51:28.39 UTC">
    +                <name>M 5.1 - 2012 May 26, LOYALTY ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>168.626,-20.796,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 26 03:27:03.72 UTC">
    +                <name>M 5.1 - 2012 May 26, NORTH OF SVALBARD</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>32.186,85.939,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 28 16:36:46.24 UTC">
    +                <name>M 5.1 - 2012 May 28, NEAR S. COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>139.95,35.707,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 11:00:25.05 UTC">
    +                <name>M 5.1 - 2012 May 29, NORTHERN ITALY</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>10.955,44.873,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 1 08:48:10.44 UTC">
    +                <name>M 5.1 - 2012 Jun 1, NEAR S. COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>139.642,35.98,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 3 19:20:43.00 UTC">
    +                <name>M 5.1 - 2012 Jun 3, NORTHERN ITALY</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>10.94,44.9,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 5 00:35:26.03 UTC">
    +                <name>M 5.1 - 2012 Jun 5, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-44.854,13.501,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 6 14:04:16.81 UTC">
    +                <name>M 5.1 - 2012 Jun 6, SOUTHWESTERN SIBERIA, RUSSIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.037,51.706,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 7 01:10:25.93 UTC">
    +                <name>M 5.1 - 2012 Jun 7, MOLUCCA SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.477,1.602,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 08:49:30.21 UTC">
    +                <name>M 5.1 - 2012 Jun 9, CERAM SEA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>127.671,-2.987,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 09:53:51.51 UTC">
    +                <name>M 5.1 - 2012 Jun 9, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-26.424,-60.577,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 10 08:18:05.84 UTC">
    +                <name>M 5.1 - 2012 Jun 10, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-27.674,-55.981,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 12 13:48:34.71 UTC">
    +                <name>M 5.1 - 2012 Jun 12, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.657,-31.696,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 11:04:04.84 UTC">
    +                <name>M 5.1 - 2012 Jun 14, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>179.633,-22.25,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 16 13:05:26.88 UTC">
    +                <name>M 5.1 - 2012 Jun 16, MAUG ISLANDS REG, N. MARIANA IS.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>145.401,20.632,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 17 23:43:53.23 UTC">
    +                <name>M 5.1 - 2012 Jun 17, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-26.603,-57.124,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 18 12:50:42.63 UTC">
    +                <name>M 5.1 - 2012 Jun 18, HALMAHERA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>129.203,2.035,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 04:27:57.35 UTC">
    +                <name>M 5.1 - 2012 Jun 19, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>100.99,-2.921,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 22 05:02:06.78 UTC">
    +                <name>M 5.1 - 2012 Jun 22, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.33,-32.979,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 24 19:28:04.11 UTC">
    +                <name>M 5.1 - 2012 Jun 24, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-27.611,-60.228,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 25 03:30:07.73 UTC">
    +                <name>M 5.1 - 2012 Jun 25, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.051,-32.627,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 11:30:01.12 UTC">
    +                <name>M 5.1 - 2012 Jun 27, VOLCANO ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.482,23.126,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 1 22:01:25.90 UTC">
    +                <name>M 5.1 - 2012 Jul 1, NORTHEASTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>59.927,34.481,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 3 13:47:03.19 UTC">
    +                <name>M 5.1 - 2012 Jul 3, NORTHERN PERU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-76.15,-4.035,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 4 14:36:34.50 UTC">
    +                <name>M 5.1 - 2012 Jul 4, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.169,-30.749,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 00:56:29.73 UTC">
    +                <name>M 5.1 - 2012 Jul 7, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.232,45.465,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 20:32:38.23 UTC">
    +                <name>M 5.1 - 2012 Jul 7, DOMINICAN REPUBLIC</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-71.136,18.243,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 9 20:13:07.39 UTC">
    +                <name>M 5.1 - 2012 Jul 9, MYANMAR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.529,25.347,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 10 02:28:12.01 UTC">
    +                <name>M 5.1 - 2012 Jul 10, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-176.408,-25.142,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 02:49:57.54 UTC">
    +                <name>M 5.1 - 2012 Jul 11, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.435,45.377,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 15:15:47.05 UTC">
    +                <name>M 5.1 - 2012 Jul 11, GALAPAGOS ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-90.855,1.582,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 13 04:50:44.58 UTC">
    +                <name>M 5.1 - 2012 Jul 13, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.551,-18.273,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 13 21:26:13.21 UTC">
    +                <name>M 5.1 - 2012 Jul 13, SOUTHERN PERU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-73.172,-15.232,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 17 20:06:53.57 UTC">
    +                <name>M 5.1 - 2012 Jul 17, SOUTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-115.177,-23.496,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 18 18:24:08.68 UTC">
    +                <name>M 5.1 - 2012 Jul 18, OFFSHORE TARAPACA, CHILE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-70.365,-20.693,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 18:54:11.88 UTC">
    +                <name>M 5.1 - 2012 Jul 20, KODIAK ISLAND REGION, ALASKA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-153.542,56.595,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 01:52:01.69 UTC">
    +                <name>M 5.1 - 2012 Jul 21, OFFSHORE NORTHERN CALIFORNIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-125.528,40.385,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 02:11:12.21 UTC">
    +                <name>M 5.1 - 2012 Jul 22, MYANMAR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.427,24.998,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 04:41:57.29 UTC">
    +                <name>M 5.1 - 2012 Jul 22, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>142.848,42.552,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 24 19:41:42.51 UTC">
    +                <name>M 5.1 - 2012 Jul 24, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>166.416,-12.042,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 1 09:24:41.21 UTC">
    +                <name>M 5.1 - 2012 Aug 1, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-179.679,-35.663,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 05:17:08.59 UTC">
    +                <name>M 5.1 - 2012 Aug 2, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-179.6,-37.584,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 19:06:12.70 UTC">
    +                <name>M 5.1 - 2012 Aug 2, MYANMAR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.324,26.103,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 3 06:06:09.99 UTC">
    +                <name>M 5.1 - 2012 Aug 3, ROSS SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>164.067,-76.313,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 3 13:01:08.24 UTC">
    +                <name>M 5.1 - 2012 Aug 3, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-13.831,-25.807,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 4 02:00:30.07 UTC">
    +                <name>M 5.1 - 2012 Aug 4, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>100.471,-1.758,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 4 12:54:27.11 UTC">
    +                <name>M 5.1 - 2012 Aug 4, EAST OF NORTH ISLAND, N.Z.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-179.752,-35.779,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 12:20:44.41 UTC">
    +                <name>M 5.1 - 2012 Aug 6, SOUTHWEST OF AFRICA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>13.771,-52.261,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 10 04:41:40.25 UTC">
    +                <name>M 5.1 - 2012 Aug 10, NIAS REGION, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>96.965,1.901,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 11 09:34:22.79 UTC">
    +                <name>M 5.1 - 2012 Aug 11, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>78.154,39.963,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 11 22:24:02.00 UTC">
    +                <name>M 5.1 - 2012 Aug 11, NORTHWESTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>46.759,38.482,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 14 14:02:25.00 UTC">
    +                <name>M 5.1 - 2012 Aug 14, NORTHWESTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>46.79,38.45,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 16 14:14:30.90 UTC">
    +                <name>M 5.1 - 2012 Aug 16, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-18.759,-0.269,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 20 16:42:55.01 UTC">
    +                <name>M 5.1 - 2012 Aug 20, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.323,36.927,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 21 17:51:36.94 UTC">
    +                <name>M 5.1 - 2012 Aug 21, SOUTHWEST OF SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>92.008,-0.177,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 22 10:13:57.47 UTC">
    +                <name>M 5.1 - 2012 Aug 22, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.661,-20.038,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 04:39:27.94 UTC">
    +                <name>M 5.1 - 2012 Aug 27, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-88.399,12.76,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 09:05:03.87 UTC">
    +                <name>M 5.1 - 2012 Aug 27, NEAR COAST OF NICARAGUA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-88.282,12.172,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 29 13:34:30.18 UTC">
    +                <name>M 5.1 - 2012 Aug 29, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>168.334,-17.61,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 19:00:08.44 UTC">
    +                <name>M 5.1 - 2012 Aug 30, KEPULAUAN BARAT DAYA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>128.647,-7.806,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 16:55:11.39 UTC">
    +                <name>M 5.1 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.705,11.026,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 17:02:07.09 UTC">
    +                <name>M 5.1 - 2012 Aug 31, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-27.493,-55.804,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 22:01:20.25 UTC">
    +                <name>M 5.1 - 2012 Aug 31, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.867,10.977,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 05:09:45.65 UTC">
    +                <name>M 5.1 - 2012 Sep 1, NORTHERN COLOMBIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-72.919,6.719,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 2 00:50:00.50 UTC">
    +                <name>M 5.1 - 2012 Sep 2, EASTERN IRAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>59.897,33.474,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 3 02:14:43.26 UTC">
    +                <name>M 5.1 - 2012 Sep 3, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.393,10.338,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 5 03:47:18.60 UTC">
    +                <name>M 5.1 - 2012 Sep 5, TAIWAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>122.341,24.026,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 5 20:36:32.91 UTC">
    +                <name>M 5.1 - 2012 Sep 5, GULF OF ADEN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>46.238,12.084,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 8 16:27:38.99 UTC">
    +                <name>M 5.1 - 2012 Sep 8, MOLUCCA SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.691,2.387,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 09:36:41.14 UTC">
    +                <name>M 5.1 - 2012 Sep 9, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.322,45.291,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 10 11:23:29.54 UTC">
    +                <name>M 5.1 - 2012 Sep 10, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.132,3.999,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 06:35:38.00 UTC">
    +                <name>M 5.1 - 2012 Sep 11, SAN JUAN, ARGENTINA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-68.35,-31.875,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 19:29:56.18 UTC">
    +                <name>M 5.1 - 2012 Sep 12, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>71.442,36.697,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 01:40:30.04 UTC">
    +                <name>M 5.1 - 2012 Sep 13, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>113.98,-10.901,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 13 17:23:47.35 UTC">
    +                <name>M 5.1 - 2012 Sep 13, NORTH OF SEVERNAYA ZEMLYA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>116.863,82.899,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 14 06:56:16.84 UTC">
    +                <name>M 5.1 - 2012 Sep 14, SOUTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-16.021,-39.644,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 15 07:04:01.76 UTC">
    +                <name>M 5.1 - 2012 Sep 15, SAMAR, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>125.524,12.532,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 18 01:44:50.39 UTC">
    +                <name>M 5.1 - 2012 Sep 18, KODIAK ISLAND REGION, ALASKA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-154.142,56.937,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 17:44:49.12 UTC">
    +                <name>M 5.1 - 2012 Sep 20, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>149.845,-5.691,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 18:32:11.54 UTC">
    +                <name>M 5.1 - 2012 Sep 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.344,39.607,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 22 09:37:00.35 UTC">
    +                <name>M 5.1 - 2012 Sep 22, SAKHALIN, RUSSIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.915,46.078,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 24 00:28:01.88 UTC">
    +                <name>M 5.1 - 2012 Sep 24, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.54,5.629,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 26 01:41:39.06 UTC">
    +                <name>M 5.1 - 2012 Sep 26, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.328,-21.205,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 29 17:48:59.47 UTC">
    +                <name>M 5.1 - 2012 Sep 29, LA PAZ, BOLIVIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-69.405,-17.182,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 1 18:40:54.59 UTC">
    +                <name>M 5.1 - 2012 Oct 1, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.028,36.916,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 11:35:47.85 UTC">
    +                <name>M 5.1 - 2012 Oct 2, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.407,-21.155,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 4 01:55:21.71 UTC">
    +                <name>M 5.1 - 2012 Oct 4, MINDORO, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>120.664,13.249,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 9 07:14:58.86 UTC">
    +                <name>M 5.1 - 2012 Oct 9, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>139.107,-2.706,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 11 22:03:43.92 UTC">
    +                <name>M 5.1 - 2012 Oct 11, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-25.71,-56.316,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 13 10:03:59.73 UTC">
    +                <name>M 5.1 - 2012 Oct 13, SOLOMON ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>162.831,-11.106,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 13 17:52:49.92 UTC">
    +                <name>M 5.1 - 2012 Oct 13, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.991,-19.963,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 13 22:36:30.77 UTC">
    +                <name>M 5.1 - 2012 Oct 13, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.479,-16.886,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 11:09:34.49 UTC">
    +                <name>M 5.1 - 2012 Oct 14, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>154.429,48.345,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 13:17:28.57 UTC">
    +                <name>M 5.1 - 2012 Oct 14, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>69.216,36.167,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 13:18:53.03 UTC">
    +                <name>M 5.1 - 2012 Oct 14, SOLOMON ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>161.415,-10.271,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 22:45:02.69 UTC">
    +                <name>M 5.1 - 2012 Oct 15, OFF EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>159.519,51.81,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 18 04:14:32.39 UTC">
    +                <name>M 5.1 - 2012 Oct 18, FLORES REGION, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>123.671,-8.059,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 19 03:35:11.55 UTC">
    +                <name>M 5.1 - 2012 Oct 19, EASTERN MEDITERRANEAN SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>30.978,32.552,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 19 17:22:08.55 UTC">
    +                <name>M 5.1 - 2012 Oct 19, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>176.649,-21.039,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 21 20:44:15.00 UTC">
    +                <name>M 5.1 - 2012 Oct 21, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-88.332,12.59,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 22 14:46:41.90 UTC">
    +                <name>M 5.1 - 2012 Oct 22, FED. STATES OF MICRONESIA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>147.678,2.893,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 23 10:45:20.67 UTC">
    +                <name>M 5.1 - 2012 Oct 23, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-70.55,-25.767,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 23 18:00:44.84 UTC">
    +                <name>M 5.1 - 2012 Oct 23, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>147.873,-6.579,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 26 21:38:18.68 UTC">
    +                <name>M 5.1 - 2012 Oct 26, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>165.484,-11.58,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 04:26:45.87 UTC">
    +                <name>M 5.1 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-132.395,52.735,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 09:17:27.70 UTC">
    +                <name>M 5.1 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-132.01,52.243,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 19:16:50.76 UTC">
    +                <name>M 5.1 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-132.478,52.647,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 19:49:07.45 UTC">
    +                <name>M 5.1 - 2012 Oct 29, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>132.789,-0.457,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 30 07:12:31.01 UTC">
    +                <name>M 5.1 - 2012 Oct 30, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.873,-4.563,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 30 10:59:32.86 UTC">
    +                <name>M 5.1 - 2012 Oct 30, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.714,-18.275,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 09:50:59.09 UTC">
    +                <name>M 5.1 - 2012 Nov 1, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-179.699,51.011,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 3 19:25:13.22 UTC">
    +                <name>M 5.1 - 2012 Nov 3, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.617,-16.662,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 5 00:55:39.81 UTC">
    +                <name>M 5.1 - 2012 Nov 5, KEPULAUAN SANGIHE, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>125.144,4.684,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 7 10:46:40.80 UTC">
    +                <name>M 5.1 - 2012 Nov 7, CENTRAL EAST PACIFIC RISE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-108.73,-8.962,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 7 19:55:33.24 UTC">
    +                <name>M 5.1 - 2012 Nov 7, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-92.193,13.967,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 8 03:24:28.31 UTC">
    +                <name>M 5.1 - 2012 Nov 8, SOUTH INDIAN OCEAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>53.497,-36.132,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 8 07:39:42.85 UTC">
    +                <name>M 5.1 - 2012 Nov 8, VANUATU</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>167.194,-14.663,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 8 17:56:38.45 UTC">
    +                <name>M 5.1 - 2012 Nov 8, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.33,-18.807,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 10 06:05:27.19 UTC">
    +                <name>M 5.1 - 2012 Nov 10, JAVA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>106.666,-7.373,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 10 17:15:09.90 UTC">
    +                <name>M 5.1 - 2012 Nov 10, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-92.224,13.815,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 23:16:44.33 UTC">
    +                <name>M 5.1 - 2012 Nov 11, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>155.213,49.357,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 12 14:58:09.65 UTC">
    +                <name>M 5.1 - 2012 Nov 12, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-173.09,-17.631,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 13 01:56:46.76 UTC">
    +                <name>M 5.1 - 2012 Nov 13, OFF COAST OF ECUADOR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-81.577,-1.736,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 13 04:55:45.00 UTC">
    +                <name>M 5.1 - 2012 Nov 13, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-132.033,52.705,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 17 09:06:26.71 UTC">
    +                <name>M 5.1 - 2012 Nov 17, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>171.578,-22.278,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 18 20:01:26.48 UTC">
    +                <name>M 5.1 - 2012 Nov 18, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>125.809,5.979,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 04:09:26.72 UTC">
    +                <name>M 5.1 - 2012 Nov 19, EAST OF SOUTH SANDWICH ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-18.067,-59.391,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 10:15:53.90 UTC">
    +                <name>M 5.1 - 2012 Nov 19, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>151.625,-5.793,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 16:45:50.37 UTC">
    +                <name>M 5.1 - 2012 Nov 19, OFFSHORE O'HIGGINS, CHILE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-72.17,-33.928,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 21 18:16:38.50 UTC">
    +                <name>M 5.1 - 2012 Nov 21, OFFSHORE O'HIGGINS, CHILE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-72.1,-33.931,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 23 04:52:40.05 UTC">
    +                <name>M 5.1 - 2012 Nov 23, LUZON, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>120.708,14.069,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 24 01:30:00.63 UTC">
    +                <name>M 5.1 - 2012 Nov 24, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.702,38.23,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 26 07:23:39.61 UTC">
    +                <name>M 5.1 - 2012 Nov 26, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.702,10.438,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 26 23:13:52.47 UTC">
    +                <name>M 5.1 - 2012 Nov 26, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>93.637,13.85,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 27 14:42:15.10 UTC">
    +                <name>M 5.1 - 2012 Nov 27, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>154.943,48.17,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 27 15:40:55.94 UTC">
    +                <name>M 5.1 - 2012 Nov 27, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-27.281,-56.082,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 28 15:07:43.13 UTC">
    +                <name>M 5.1 - 2012 Nov 28, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.194,27.243,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 00:30:31.29 UTC">
    +                <name>M 5.1 - 2012 Nov 29, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.772,-21.106,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 2 04:44:37.27 UTC">
    +                <name>M 5.1 - 2012 Dec 2, OFF COAST OF CENTRAL AMERICA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-88.988,11.852,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 2 13:32:44.66 UTC">
    +                <name>M 5.1 - 2012 Dec 2, TONGA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-174.214,-20.69,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 5 23:08:36.86 UTC">
    +                <name>M 5.1 - 2012 Dec 5, FIJI REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-178.95,-20.781,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 6 02:24:28.33 UTC">
    +                <name>M 5.1 - 2012 Dec 6, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>90.434,2.421,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 14:32:17.20 UTC">
    +                <name>M 5.1 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.633,37.813,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 16:01:54.64 UTC">
    +                <name>M 5.1 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.771,37.654,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 8 06:16:57.03 UTC">
    +                <name>M 5.1 - 2012 Dec 8, NEAR S COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>144.006,-7.235,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 8 14:26:55.85 UTC">
    +                <name>M 5.1 - 2012 Dec 8, TAIWAN REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>122.35,25.748,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 9 02:34:17.09 UTC">
    +                <name>M 5.1 - 2012 Dec 9, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.617,37.744,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 11 18:21:38.45 UTC">
    +                <name>M 5.1 - 2012 Dec 11, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>143.833,37.633,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 14 04:58:48.46 UTC">
    +                <name>M 5.1 - 2012 Dec 14, OFFSHORE GUATEMALA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-91.725,13.824,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 14 17:46:45.30 UTC">
    +                <name>M 5.1 - 2012 Dec 14, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>153.404,-5.272,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 15 07:33:14.40 UTC">
    +                <name>M 5.1 - 2012 Dec 15, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>153.405,-4.893,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 18 08:12:22.03 UTC">
    +                <name>M 5.1 - 2012 Dec 18, OFFSHORE EL SALVADOR</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-89.978,13.054,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 18 18:36:24.30 UTC">
    +                <name>M 5.1 - 2012 Dec 18, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>141.917,36.664,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 22 03:50:08.20 UTC">
    +                <name>M 5.1 - 2012 Dec 22, NORTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>94.221,5.169,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 22 04:42:37.57 UTC">
    +                <name>M 5.1 - 2012 Dec 22, KURIL ISLANDS</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>155.103,49.818,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 22 18:07:17.45 UTC">
    +                <name>M 5.1 - 2012 Dec 22, BABUYAN ISL REGION, PHILIPPINES</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>121.194,19.061,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 24 12:12:35.34 UTC">
    +                <name>M 5.1 - 2012 Dec 24, OFFSHORE ATACAMA, CHILE</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-71.494,-29.037,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 24 12:58:20.18 UTC">
    +                <name>M 5.1 - 2012 Dec 24, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>126.679,4.149,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 25 08:46:31.83 UTC">
    +                <name>M 5.1 - 2012 Dec 25, SOUTHWEST OF SUMATRA, INDONESIA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>101.25,-7.893,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 02:24:32.79 UTC">
    +                <name>M 5.1 - 2012 Dec 26, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>147.287,-5.914,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 28 10:35:45.05 UTC">
    +                <name>M 5.1 - 2012 Dec 28, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-176.245,-29.596,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 28 13:42:34.81 UTC">
    +                <name>M 5.1 - 2012 Dec 28, KOMANDORSKIYE OSTROVA REGION</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>164.724,55.707,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 07:59:15.86 UTC">
    +                <name>M 5.1 - 2012 Dec 29, BISMARCK SEA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>148.757,-3.315,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 31 01:15:18.77 UTC">
    +                <name>M 5.1 - 2012 Dec 31, GUATEMALA</name>
    +                <magnitude>5.1</magnitude>
    +                <Point>
    +                    <coordinates>-92.037,15.031,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 04:56:40.98 UTC">
    +                <name>M 5 - 2012 Jan 1, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-149.214,-58.259,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 1 19:32:52.62 UTC">
    +                <name>M 5 - 2012 Jan 1, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>154.209,-5.413,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 02:07:53.42 UTC">
    +                <name>M 5 - 2012 Jan 4, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>130.221,27.917,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 18:04:48.18 UTC">
    +                <name>M 5 - 2012 Jan 4, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.671,-15.552,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 4 18:54:11.32 UTC">
    +                <name>M 5 - 2012 Jan 4, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.663,-15.14,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 5 16:47:08.10 UTC">
    +                <name>M 5 - 2012 Jan 5, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.197,38.714,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 6 08:08:40.46 UTC">
    +                <name>M 5 - 2012 Jan 6, OFF COAST OF ARAUCANIA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-75.091,-38.024,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 6 19:04:03.97 UTC">
    +                <name>M 5 - 2012 Jan 6, BONIN ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.559,26.944,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 6 23:37:45.47 UTC">
    +                <name>M 5 - 2012 Jan 6, SOUTHERN ALASKA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-153.232,59.852,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 03:21:27.44 UTC">
    +                <name>M 5 - 2012 Jan 7, PAPUA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>138.055,-3.752,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 04:30:31.08 UTC">
    +                <name>M 5 - 2012 Jan 7, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>152.594,-4.329,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 18:00:58.62 UTC">
    +                <name>M 5 - 2012 Jan 7, BABUYAN ISL REGION, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.191,19.254,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 7 21:30:11.00 UTC">
    +                <name>M 5 - 2012 Jan 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.073,36.063,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 8 06:20:06.38 UTC">
    +                <name>M 5 - 2012 Jan 8, NORTHERN XINJIANG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>87.474,42.203,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 8 23:40:58.46 UTC">
    +                <name>M 5 - 2012 Jan 8, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-179.895,-23.337,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 02:38:03.25 UTC">
    +                <name>M 5 - 2012 Jan 9, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.09,-29.512,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 17:45:32.38 UTC">
    +                <name>M 5 - 2012 Jan 9, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>165.105,-10.516,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 9 20:13:12.32 UTC">
    +                <name>M 5 - 2012 Jan 9, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>133.064,-0.814,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 10 18:07:12.99 UTC">
    +                <name>M 5 - 2012 Jan 10, NEAR COAST OF ECUADOR</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-80.28,-0.74,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 00:19:05.51 UTC">
    +                <name>M 5 - 2012 Jan 12, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.918,8.432,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 02:20:23.87 UTC">
    +                <name>M 5 - 2012 Jan 12, SOUTH OF AFRICA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>23.53,-53.252,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 02:52:38.46 UTC">
    +                <name>M 5 - 2012 Jan 12, SOUTH OF AFRICA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>23.746,-53.209,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 07:08:08.64 UTC">
    +                <name>M 5 - 2012 Jan 12, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-172.393,-17.989,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 15:26:28.76 UTC">
    +                <name>M 5 - 2012 Jan 12, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.895,36.672,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 12 17:12:08.93 UTC">
    +                <name>M 5 - 2012 Jan 12, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-172.531,-17.822,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 06:49:24.18 UTC">
    +                <name>M 5 - 2012 Jan 13, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>165.262,-10.52,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 15:54:41.08 UTC">
    +                <name>M 5 - 2012 Jan 13, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-27.208,-60.597,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 13 22:49:59.51 UTC">
    +                <name>M 5 - 2012 Jan 13, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>70.512,36.009,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 14 11:34:56.85 UTC">
    +                <name>M 5 - 2012 Jan 14, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-26.086,-60.695,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 12:50:13.59 UTC">
    +                <name>M 5 - 2012 Jan 16, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-24.981,-56.754,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 17:53:12.25 UTC">
    +                <name>M 5 - 2012 Jan 16, NORTH OF ASCENSION ISLAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-12.601,-2.114,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 16 18:39:51.13 UTC">
    +                <name>M 5 - 2012 Jan 16, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-172.909,-19.289,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 17 10:00:22.03 UTC">
    +                <name>M 5 - 2012 Jan 17, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.972,6.516,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 18 11:35:50.50 UTC">
    +                <name>M 5 - 2012 Jan 18, SAN JUAN, ARGENTINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-68.084,-31.767,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 06:28:19.09 UTC">
    +                <name>M 5 - 2012 Jan 19, DOMINICAN REPUBLIC REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-68.755,18.058,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 19 23:35:34.39 UTC">
    +                <name>M 5 - 2012 Jan 19, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-172.416,-17.898,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 21 03:19:50.71 UTC">
    +                <name>M 5 - 2012 Jan 21, BALLENY ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>177.584,-65.042,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 21 12:03:32.14 UTC">
    +                <name>M 5 - 2012 Jan 21, MARIANA ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>147.034,19.727,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 22 11:24:09.00 UTC">
    +                <name>M 5 - 2012 Jan 22, ATACAMA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-71.03,-28.682,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 23 17:22:07.00 UTC">
    +                <name>M 5 - 2012 Jan 23, OFFSHORE BIO-BIO, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-73.486,-36.424,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 23 21:55:16.77 UTC">
    +                <name>M 5 - 2012 Jan 23, OFFSHORE BIO-BIO, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-73.031,-36.338,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 14:37:02.66 UTC">
    +                <name>M 5 - 2012 Jan 25, NEAR WEST COAST OF COLOMBIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-77.505,5.519,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 25 17:47:11.35 UTC">
    +                <name>M 5 - 2012 Jan 25, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>153.487,-6.802,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 26 04:21:16.99 UTC">
    +                <name>M 5 - 2012 Jan 26, KEPULAUAN BABAR, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>129.935,-7.1,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 01:08:31.13 UTC">
    +                <name>M 5 - 2012 Jan 27, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.891,37.904,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 27 14:53:13.00 UTC">
    +                <name>M 5 - 2012 Jan 27, NORTHERN ITALY</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>10.03,44.48,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 17:56:41.71 UTC">
    +                <name>M 5 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.077,-29.52,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 18:09:26.24 UTC">
    +                <name>M 5 - 2012 Jan 28, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.893,-29.505,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 28 20:00:34.46 UTC">
    +                <name>M 5 - 2012 Jan 28, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.036,-29.554,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 29 21:54:20.07 UTC">
    +                <name>M 5 - 2012 Jan 29, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-46.467,16.624,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jan 31 19:53:44.14 UTC">
    +                <name>M 5 - 2012 Jan 31, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.843,-30.699,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 1 04:26:14.45 UTC">
    +                <name>M 5 - 2012 Feb 1, SAMAR, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.615,11.61,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 02:48:07.11 UTC">
    +                <name>M 5 - 2012 Feb 2, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>153.349,-60.843,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 09:16:17.24 UTC">
    +                <name>M 5 - 2012 Feb 2, NEW IRELAND REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>153.638,-5.341,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 13:48:16.50 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.248,-17.783,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 13:50:25.02 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.126,-17.933,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 15:07:06.51 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.413,-17.858,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 15:10:14.97 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.108,-17.433,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 15:14:59.93 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.198,-17.899,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 15:22:56.84 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.648,-18.2,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 18:35:29.37 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.327,-17.529,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 2 19:20:46.93 UTC">
    +                <name>M 5 - 2012 Feb 2, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.791,-18.011,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 3 11:26:51.24 UTC">
    +                <name>M 5 - 2012 Feb 3, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>149.939,-6.644,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 3 20:34:03.36 UTC">
    +                <name>M 5 - 2012 Feb 3, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.83,-19.49,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 05:56:02.53 UTC">
    +                <name>M 5 - 2012 Feb 4, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.869,37.343,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 4 20:10:12.94 UTC">
    +                <name>M 5 - 2012 Feb 4, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.423,-18.026,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 5 06:10:39.75 UTC">
    +                <name>M 5 - 2012 Feb 5, SOUTHERN IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>51.488,28.59,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 6 10:13:53.70 UTC">
    +                <name>M 5 - 2012 Feb 6, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.356,10.019,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 7 04:05:35.02 UTC">
    +                <name>M 5 - 2012 Feb 7, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.595,10.108,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 7 13:30:04.78 UTC">
    +                <name>M 5 - 2012 Feb 7, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.219,45.559,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 7 19:09:30.12 UTC">
    +                <name>M 5 - 2012 Feb 7, BABUYAN ISL REGION, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.5,19.684,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 8 01:53:01.30 UTC">
    +                <name>M 5 - 2012 Feb 8, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.183,9.839,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 9 05:18:48.51 UTC">
    +                <name>M 5 - 2012 Feb 9, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.545,-15.325,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 11 05:39:07.85 UTC">
    +                <name>M 5 - 2012 Feb 11, NEGROS- CEBU REG, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.264,10.071,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 12 09:28:16.35 UTC">
    +                <name>M 5 - 2012 Feb 12, NEAR COAST OF SOUTHERN PERU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-74.157,-15.64,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 12 18:09:21.67 UTC">
    +                <name>M 5 - 2012 Feb 12, ASCENSION ISLAND REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-13.051,-7.074,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 00:49:31.32 UTC">
    +                <name>M 5 - 2012 Feb 13, MOZAMBIQUE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>40.891,-13.928,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 03:32:05.67 UTC">
    +                <name>M 5 - 2012 Feb 13, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.233,-17.552,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 13 07:50:39.87 UTC">
    +                <name>M 5 - 2012 Feb 13, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.703,-33.982,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 04:03:58.81 UTC">
    +                <name>M 5 - 2012 Feb 14, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-25.357,0.599,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 14 18:07:56.36 UTC">
    +                <name>M 5 - 2012 Feb 14, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.153,-18.038,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 06:28:37.50 UTC">
    +                <name>M 5 - 2012 Feb 15, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.475,37.803,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 15 18:31:53.65 UTC">
    +                <name>M 5 - 2012 Feb 15, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.9,6.306,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 16 15:54:28.30 UTC">
    +                <name>M 5 - 2012 Feb 16, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>152.193,-5.697,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 17 11:26:49.97 UTC">
    +                <name>M 5 - 2012 Feb 17, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>166.829,-14.824,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 18 01:34:19.80 UTC">
    +                <name>M 5 - 2012 Feb 18, OFFSHORE OAXACA, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-95.67,15.264,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 18 22:58:49.56 UTC">
    +                <name>M 5 - 2012 Feb 18, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.909,2.446,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 19 08:07:24.79 UTC">
    +                <name>M 5 - 2012 Feb 19, NORTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>174.124,-39.382,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 19 15:17:28.99 UTC">
    +                <name>M 5 - 2012 Feb 19, CROZET ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>44.553,-41.015,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 19 23:33:08.23 UTC">
    +                <name>M 5 - 2012 Feb 19, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.639,-17.861,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 13:59:23.79 UTC">
    +                <name>M 5 - 2012 Feb 20, KASHMIR-XINJIANG BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>79.717,35.723,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 14:18:03.82 UTC">
    +                <name>M 5 - 2012 Feb 20, KASHMIR-XINJIANG BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>79.835,35.84,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 20 20:59:08.81 UTC">
    +                <name>M 5 - 2012 Feb 20, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.4,-4.792,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 22 20:44:42.83 UTC">
    +                <name>M 5 - 2012 Feb 22, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.294,-17.463,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 23 04:05:34.16 UTC">
    +                <name>M 5 - 2012 Feb 23, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.761,-20.57,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 24 02:02:44.06 UTC">
    +                <name>M 5 - 2012 Feb 24, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.264,-15.206,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 24 05:42:30.71 UTC">
    +                <name>M 5 - 2012 Feb 24, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.955,9.958,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 24 14:13:02.42 UTC">
    +                <name>M 5 - 2012 Feb 24, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>103.103,-4.695,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 09:23:09.69 UTC">
    +                <name>M 5 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.234,-24.739,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 12:12:23.46 UTC">
    +                <name>M 5 - 2012 Feb 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.459,-24.796,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 26 16:27:14.79 UTC">
    +                <name>M 5 - 2012 Feb 26, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.48,-18.036,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 00:33:49.07 UTC">
    +                <name>M 5 - 2012 Feb 27, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>154.871,-6.535,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 03:01:50.40 UTC">
    +                <name>M 5 - 2012 Feb 27, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.122,-24.734,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 03:43:05.03 UTC">
    +                <name>M 5 - 2012 Feb 27, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>155.843,49.272,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 27 16:29:25.42 UTC">
    +                <name>M 5 - 2012 Feb 27, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>127.182,25.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 05:20:48.52 UTC">
    +                <name>M 5 - 2012 Feb 28, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.023,36.373,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 15:24:21.10 UTC">
    +                <name>M 5 - 2012 Feb 28, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>156.127,49.115,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 28 18:46:23.74 UTC">
    +                <name>M 5 - 2012 Feb 28, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.419,-22.084,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 01:22:52.65 UTC">
    +                <name>M 5 - 2012 Feb 29, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>156.2,49.282,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Feb 29 21:59:37.94 UTC">
    +                <name>M 5 - 2012 Feb 29, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.399,-17.794,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 1 00:16:01.06 UTC">
    +                <name>M 5 - 2012 Mar 1, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>144.154,37.21,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 05:13:32.16 UTC">
    +                <name>M 5 - 2012 Mar 2, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>146.483,-7.956,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 12:25:30.77 UTC">
    +                <name>M 5 - 2012 Mar 2, BALLENY ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>176.623,-64.822,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 13:40:12.36 UTC">
    +                <name>M 5 - 2012 Mar 2, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>74.38,39.75,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 2 17:52:31.87 UTC">
    +                <name>M 5 - 2012 Mar 2, HALMAHERA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>127.077,1.809,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 3 17:29:52.95 UTC">
    +                <name>M 5 - 2012 Mar 3, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>170.341,-22.089,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 3 17:53:49.46 UTC">
    +                <name>M 5 - 2012 Mar 3, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.066,-23.934,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 3 22:12:55.00 UTC">
    +                <name>M 5 - 2012 Mar 3, OFFSHORE MAULE, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-72.8,-35.749,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 4 16:27:21.04 UTC">
    +                <name>M 5 - 2012 Mar 4, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-69.848,-21.554,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 5 06:24:26.84 UTC">
    +                <name>M 5 - 2012 Mar 5, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.389,40.167,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 6 02:59:25.97 UTC">
    +                <name>M 5 - 2012 Mar 6, NICOBAR ISLANDS, INDIA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>93.873,8.559,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 9 18:18:59.28 UTC">
    +                <name>M 5 - 2012 Mar 9, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.033,35.642,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 11 11:10:52.57 UTC">
    +                <name>M 5 - 2012 Mar 11, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.949,-24.921,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 11 22:02:40.90 UTC">
    +                <name>M 5 - 2012 Mar 11, VOLCANO ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.579,23.718,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 13 02:39:55.67 UTC">
    +                <name>M 5 - 2012 Mar 13, COMOROS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>43.286,-10.923,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 13 18:23:41.99 UTC">
    +                <name>M 5 - 2012 Mar 13, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>179.981,-23.734,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 14 17:41:10.14 UTC">
    +                <name>M 5 - 2012 Mar 14, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>93.854,10.535,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 16 17:18:33.03 UTC">
    +                <name>M 5 - 2012 Mar 16, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>171.119,-22.507,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 19 02:30:21.86 UTC">
    +                <name>M 5 - 2012 Mar 19, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>152.963,-4.585,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 15:05:58.60 UTC">
    +                <name>M 5 - 2012 Mar 20, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>169.33,-19.684,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 20 22:25:40.07 UTC">
    +                <name>M 5 - 2012 Mar 20, OFFSHORE GUERRERO, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-98.694,16.273,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 21 11:36:14.80 UTC">
    +                <name>M 5 - 2012 Mar 21, GUERRERO, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-98.504,16.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 22 12:59:28.88 UTC">
    +                <name>M 5 - 2012 Mar 22, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.526,-15.188,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 22 14:35:17.28 UTC">
    +                <name>M 5 - 2012 Mar 22, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.449,-21.355,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 23 14:35:31.87 UTC">
    +                <name>M 5 - 2012 Mar 23, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.263,-16.261,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 24 07:28:33.00 UTC">
    +                <name>M 5 - 2012 Mar 24, VALPARAISO, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-71.063,-33.052,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 25 20:45:45.70 UTC">
    +                <name>M 5 - 2012 Mar 25, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>168.75,-19.108,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 10:35:32.64 UTC">
    +                <name>M 5 - 2012 Mar 26, EASTERN TURKEY</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>42.33,39.171,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 26 17:58:25.76 UTC">
    +                <name>M 5 - 2012 Mar 26, NORTHERN EAST PACIFIC RISE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-104.337,10.001,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 27 23:40:12.60 UTC">
    +                <name>M 5 - 2012 Mar 27, NEPAL-INDIA BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>87.777,26.087,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 06:19:33.17 UTC">
    +                <name>M 5 - 2012 Mar 30, SOUTHEAST OF EASTER ISLAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-101.463,-35.843,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Mar 30 09:38:59.79 UTC">
    +                <name>M 5 - 2012 Mar 30, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.97,-21.177,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 2 19:54:33.61 UTC">
    +                <name>M 5 - 2012 Apr 2, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.813,37.937,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 2 23:55:00.12 UTC">
    +                <name>M 5 - 2012 Apr 2, OFFSHORE GUERRERO, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-98.572,16.281,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 02:11:03.00 UTC">
    +                <name>M 5 - 2012 Apr 3, OFFSHORE O'HIGGINS, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-72.757,-33.847,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 3 11:26:59.61 UTC">
    +                <name>M 5 - 2012 Apr 3, KEPULAUAN BARAT DAYA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.367,-7.035,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 6 08:50:57.01 UTC">
    +                <name>M 5 - 2012 Apr 6, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.414,-21.281,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 7 20:37:51.16 UTC">
    +                <name>M 5 - 2012 Apr 7, CENTRAL MID-ATLANTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-28.091,1.053,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 07:41:46.76 UTC">
    +                <name>M 5 - 2012 Apr 11, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>130.261,-6.236,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 09:06:22.58 UTC">
    +                <name>M 5 - 2012 Apr 11, NORTH INDIAN OCEAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>91.923,0.916,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 10:10:04.03 UTC">
    +                <name>M 5 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.726,2.668,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 13:15:30.07 UTC">
    +                <name>M 5 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>90.78,4.601,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 14:18:44.23 UTC">
    +                <name>M 5 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.287,2.539,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 11 15:06:39.81 UTC">
    +                <name>M 5 - 2012 Apr 11, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.311,1.269,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 02:46:49.00 UTC">
    +                <name>M 5 - 2012 Apr 12, NORTH INDIAN OCEAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>91.754,1.301,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 02:54:43.90 UTC">
    +                <name>M 5 - 2012 Apr 12, NORTH INDIAN OCEAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>91.717,1.172,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 07:01:44.00 UTC">
    +                <name>M 5 - 2012 Apr 12, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>93.9,1.983,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 12 14:46:25.66 UTC">
    +                <name>M 5 - 2012 Apr 12, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>93.746,3.279,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 04:15:10.45 UTC">
    +                <name>M 5 - 2012 Apr 13, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>168.086,-18.25,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 05:31:45.70 UTC">
    +                <name>M 5 - 2012 Apr 13, NORTH INDIAN OCEAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>89.705,2.653,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 13 13:06:28.40 UTC">
    +                <name>M 5 - 2012 Apr 13, OFFSHORE OAXACA, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-98.145,16.207,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 21:10:34.64 UTC">
    +                <name>M 5 - 2012 Apr 14, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>94.079,2.835,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 14 23:49:24.84 UTC">
    +                <name>M 5 - 2012 Apr 14, WEST CHILE RISE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-97.183,-36.3,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 18:36:15.36 UTC">
    +                <name>M 5 - 2012 Apr 16, NORTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>175.805,-38.556,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 16 23:53:29.81 UTC">
    +                <name>M 5 - 2012 Apr 16, MID-INDIAN RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>78.876,-36.534,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 17 19:45:36.76 UTC">
    +                <name>M 5 - 2012 Apr 17, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.61,2.584,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 01:18:37.95 UTC">
    +                <name>M 5 - 2012 Apr 18, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.052,-13.288,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 01:43:59.23 UTC">
    +                <name>M 5 - 2012 Apr 18, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.541,3.329,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 18 19:07:58.02 UTC">
    +                <name>M 5 - 2012 Apr 18, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.766,3.586,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 19 01:58:08.60 UTC">
    +                <name>M 5 - 2012 Apr 19, TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.67,24.13,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 19 15:46:50.72 UTC">
    +                <name>M 5 - 2012 Apr 19, NEAR N COAST OF PAPUA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>134.492,-1.677,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 03:05:41.78 UTC">
    +                <name>M 5 - 2012 Apr 20, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>46.99,32.453,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 07:51:23.75 UTC">
    +                <name>M 5 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>91.901,2.564,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 10:17:11.94 UTC">
    +                <name>M 5 - 2012 Apr 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.334,2.025,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 20:18:34.76 UTC">
    +                <name>M 5 - 2012 Apr 20, VOLCANO ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.688,25.447,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 20 20:44:57.06 UTC">
    +                <name>M 5 - 2012 Apr 20, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.594,-17.617,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 21 05:25:08.41 UTC">
    +                <name>M 5 - 2012 Apr 21, IRAN-IRAQ BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>47.049,32.407,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 02:57:50.79 UTC">
    +                <name>M 5 - 2012 Apr 23, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-27.674,-55.517,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 03:08:03.90 UTC">
    +                <name>M 5 - 2012 Apr 23, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.17,-17.966,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 23 10:57:19.65 UTC">
    +                <name>M 5 - 2012 Apr 23, SOUTH OF TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.934,-24.007,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 24 14:05:26.56 UTC">
    +                <name>M 5 - 2012 Apr 24, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.999,-23.785,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 25 20:17:55.08 UTC">
    +                <name>M 5 - 2012 Apr 25, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.694,-4.114,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 26 06:38:32.26 UTC">
    +                <name>M 5 - 2012 Apr 26, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.879,-18.802,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 28 19:06:48.09 UTC">
    +                <name>M 5 - 2012 Apr 28, SOUTHWEST INDIAN RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>43.501,-40.999,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 16:56:42.11 UTC">
    +                <name>M 5 - 2012 Apr 30, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>105.422,-8.354,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 19:12:50.00 UTC">
    +                <name>M 5 - 2012 Apr 30, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>127.507,3.712,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 20:17:43.43 UTC">
    +                <name>M 5 - 2012 Apr 30, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.231,-30.038,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Apr 30 21:40:38.51 UTC">
    +                <name>M 5 - 2012 Apr 30, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.962,3.314,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 2 13:48:12.60 UTC">
    +                <name>M 5 - 2012 May 2, NORTHERN MID-ATLANTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-44.95,23.352,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 2 20:04:20.28 UTC">
    +                <name>M 5 - 2012 May 2, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.668,-23.551,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 3 21:20:36.39 UTC">
    +                <name>M 5 - 2012 May 3, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>150.557,46.903,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 4 10:55:37.02 UTC">
    +                <name>M 5 - 2012 May 4, VANCOUVER ISLAND, CANADA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-130.241,50.648,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 7 06:46:56.29 UTC">
    +                <name>M 5 - 2012 May 7, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.659,-15.169,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 8 09:16:34.93 UTC">
    +                <name>M 5 - 2012 May 8, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>144.644,37.364,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 11 10:18:10.01 UTC">
    +                <name>M 5 - 2012 May 11, GANSU-QINGHAI BORDER REG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>102.048,37.735,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 14 02:45:21.58 UTC">
    +                <name>M 5 - 2012 May 14, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.832,51.445,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 14 19:28:47.04 UTC">
    +                <name>M 5 - 2012 May 14, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.338,1.29,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 17 19:49:32.79 UTC">
    +                <name>M 5 - 2012 May 17, SANTA CRUZ ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>166.185,-12.596,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 18 21:23:28.13 UTC">
    +                <name>M 5 - 2012 May 18, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.178,39.646,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 19 20:06:02.11 UTC">
    +                <name>M 5 - 2012 May 19, NORTH OF HALMAHERA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>128.118,3.304,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 19 22:23:23.12 UTC">
    +                <name>M 5 - 2012 May 19, FOX ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-171.728,52.068,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 08:41:44.90 UTC">
    +                <name>M 5 - 2012 May 20, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.116,39.543,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 20 13:18:02.00 UTC">
    +                <name>M 5 - 2012 May 20, NORTHERN ITALY</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>11.49,44.831,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 24 18:19:40.39 UTC">
    +                <name>M 5 - 2012 May 24, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>147.114,-7.33,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 25 00:25:55.93 UTC">
    +                <name>M 5 - 2012 May 25, NORWEGIAN SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>5.518,72.939,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 25 02:44:50.47 UTC">
    +                <name>M 5 - 2012 May 25, SOUTH ISLAND OF NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>172.81,-43.473,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 25 18:10:43.49 UTC">
    +                <name>M 5 - 2012 May 25, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.46,-33.075,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 25 18:20:07.60 UTC">
    +                <name>M 5 - 2012 May 25, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.763,-19.184,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 26 17:18:01.87 UTC">
    +                <name>M 5 - 2012 May 26, SOUTHERN SUMATRA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>103.03,-4.9,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 26 17:54:18.47 UTC">
    +                <name>M 5 - 2012 May 26, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.991,-30.825,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 29 20:08:15.07 UTC">
    +                <name>M 5 - 2012 May 29, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>173.796,-15.524,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 30 06:57:05.99 UTC">
    +                <name>M 5 - 2012 May 30, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>130.318,-6.199,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 30 13:56:14.70 UTC">
    +                <name>M 5 - 2012 May 30, STATE OF YAP, MICRONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>139.559,11.271,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 May 31 17:29:57.50 UTC">
    +                <name>M 5 - 2012 May 31, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.314,-17.683,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 3 11:39:23.91 UTC">
    +                <name>M 5 - 2012 Jun 3, POTOSI, BOLIVIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-68.026,-21.338,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 4 00:15:41.27 UTC">
    +                <name>M 5 - 2012 Jun 4, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>169.134,-21.941,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 4 16:40:03.22 UTC">
    +                <name>M 5 - 2012 Jun 4, HALMAHERA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>127.699,-0.677,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 6 04:17:31.75 UTC">
    +                <name>M 5 - 2012 Jun 6, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.905,-0.953,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 6 16:11:14.54 UTC">
    +                <name>M 5 - 2012 Jun 6, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.441,-27.034,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 7 00:02:27.98 UTC">
    +                <name>M 5 - 2012 Jun 7, TONGA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.717,-22.91,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 7 18:39:45.65 UTC">
    +                <name>M 5 - 2012 Jun 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.295,39.875,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 7 19:25:25.00 UTC">
    +                <name>M 5 - 2012 Jun 7, MAULE, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-71.075,-36.036,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 8 16:15:14.80 UTC">
    +                <name>M 5 - 2012 Jun 8, SOUTHERN IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>50.705,29.784,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 12:02:46.37 UTC">
    +                <name>M 5 - 2012 Jun 9, OFF EAST COAST OF KAMCHATKA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>159.206,51.698,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 9 20:25:26.71 UTC">
    +                <name>M 5 - 2012 Jun 9, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-23.239,-60.883,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 10 13:32:55.67 UTC">
    +                <name>M 5 - 2012 Jun 10, PAGAN REG., N. MARIANA ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>145.616,18.689,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 14 15:07:53.86 UTC">
    +                <name>M 5 - 2012 Jun 14, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.161,-17.111,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 17 09:01:52.45 UTC">
    +                <name>M 5 - 2012 Jun 17, TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.574,23.647,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 18 00:02:40.65 UTC">
    +                <name>M 5 - 2012 Jun 18, AZORES ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-29.151,41.84,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 18 20:14:55.38 UTC">
    +                <name>M 5 - 2012 Jun 18, NORTH OF FRANZ JOSEF LAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>41.025,86.386,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 04:37:34.90 UTC">
    +                <name>M 5 - 2012 Jun 19, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.624,-20.552,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 19 20:14:05.72 UTC">
    +                <name>M 5 - 2012 Jun 19, GUJARAT, INDIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>70.252,23.647,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 21 11:40:34.81 UTC">
    +                <name>M 5 - 2012 Jun 21, HALMAHERA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>128.278,2.7,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 22 07:58:43.42 UTC">
    +                <name>M 5 - 2012 Jun 22, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.492,39.369,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 23 07:43:45.27 UTC">
    +                <name>M 5 - 2012 Jun 23, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.666,51.242,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 25 03:09:04.31 UTC">
    +                <name>M 5 - 2012 Jun 25, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.878,-27.424,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 25 13:05:28.00 UTC">
    +                <name>M 5 - 2012 Jun 25, DODECANESE ISLANDS, GREECE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>28.941,36.439,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 26 12:32:35.16 UTC">
    +                <name>M 5 - 2012 Jun 26, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.697,-21.926,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 27 20:48:54.69 UTC">
    +                <name>M 5 - 2012 Jun 27, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.408,-15.132,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jun 29 17:47:25.48 UTC">
    +                <name>M 5 - 2012 Jun 29, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>152.342,-4.937,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 1 02:49:46.00 UTC">
    +                <name>M 5 - 2012 Jul 1, CENTRAL IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>51.02,31.841,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 4 21:29:26.15 UTC">
    +                <name>M 5 - 2012 Jul 4, ST. MARTIN REGION, LEEWARD ISL.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-62.983,18.12,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 5 09:46:21.39 UTC">
    +                <name>M 5 - 2012 Jul 5, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.64,32.284,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 6 06:16:47.00 UTC">
    +                <name>M 5 - 2012 Jul 6, ISLA CHILOE, LOS LAGOS, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-74.015,-42.646,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 14:26:03.85 UTC">
    +                <name>M 5 - 2012 Jul 7, KEP. MENTAWAI REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>100.591,-3.638,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 20:32:26.83 UTC">
    +                <name>M 5 - 2012 Jul 7, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-150.669,-59.52,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 7 22:49:20.08 UTC">
    +                <name>M 5 - 2012 Jul 7, MAURITIUS - REUNION REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>66.882,-17.083,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 9 10:39:00.85 UTC">
    +                <name>M 5 - 2012 Jul 9, ALAMAGAN REG, N. MARIANA ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>146.017,17.141,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 11 01:11:37.82 UTC">
    +                <name>M 5 - 2012 Jul 11, CERAM SEA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>127.378,-2.02,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 13 22:37:25.98 UTC">
    +                <name>M 5 - 2012 Jul 13, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.829,45.255,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 14 17:36:22.58 UTC">
    +                <name>M 5 - 2012 Jul 14, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.878,45.378,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 15 20:05:06.03 UTC">
    +                <name>M 5 - 2012 Jul 15, CENTRAL PERU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-73.796,-13.58,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 17 19:26:30.99 UTC">
    +                <name>M 5 - 2012 Jul 17, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-179.093,-31.283,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 20 20:24:20.47 UTC">
    +                <name>M 5 - 2012 Jul 20, KEPULAUAN SANGIHE, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.808,3.395,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 21 20:47:46.64 UTC">
    +                <name>M 5 - 2012 Jul 21, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.39,-15.197,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 09:26:02.99 UTC">
    +                <name>M 5 - 2012 Jul 22, CENTRAL TURKEY</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>36.384,37.546,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 22 18:02:12.08 UTC">
    +                <name>M 5 - 2012 Jul 22, NEAR COAST OF NICARAGUA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-87.028,11.639,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 23 06:54:31.15 UTC">
    +                <name>M 5 - 2012 Jul 23, NEAR ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>172.869,52.016,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 23 19:47:14.11 UTC">
    +                <name>M 5 - 2012 Jul 23, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.465,-30.83,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 24 10:28:06.13 UTC">
    +                <name>M 5 - 2012 Jul 24, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.631,45.464,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 24 19:06:26.09 UTC">
    +                <name>M 5 - 2012 Jul 24, OFF E. COAST OF N. ISLAND, N.Z.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>179.91,-37.776,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 25 17:20:11.14 UTC">
    +                <name>M 5 - 2012 Jul 25, KYUSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>130.348,30.981,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 25 19:01:36.42 UTC">
    +                <name>M 5 - 2012 Jul 25, VANUATU REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.728,-19.297,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 25 21:43:28.95 UTC">
    +                <name>M 5 - 2012 Jul 25, SOLOMON ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>159.699,-9.725,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Jul 28 11:23:43.82 UTC">
    +                <name>M 5 - 2012 Jul 28, NORTH OF SVALBARD</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-3.97,81.294,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 1 09:52:22.37 UTC">
    +                <name>M 5 - 2012 Aug 1, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>75.556,39.774,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 2 14:32:08.26 UTC">
    +                <name>M 5 - 2012 Aug 2, SOUTH SHETLAND ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-58.258,-61.401,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 4 19:05:39.16 UTC">
    +                <name>M 5 - 2012 Aug 4, SAN JUAN, ARGENTINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-69.358,-31.928,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 5 14:10:30.33 UTC">
    +                <name>M 5 - 2012 Aug 5, EASTERN XIZANG</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>94.849,30.352,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 5 20:37:23.80 UTC">
    +                <name>M 5 - 2012 Aug 5, TURKEY-SYRIA-IRAQ BORDER REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>42.965,37.421,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 04:43:25.81 UTC">
    +                <name>M 5 - 2012 Aug 6, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>170.39,-21.406,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 05:30:19.51 UTC">
    +                <name>M 5 - 2012 Aug 6, RYUKYU ISLANDS, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>130.521,29.495,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 6 11:49:13.92 UTC">
    +                <name>M 5 - 2012 Aug 6, SOUTHWEST OF AFRICA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>13.628,-52.29,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 10 09:01:20.91 UTC">
    +                <name>M 5 - 2012 Aug 10, NEAR COAST OF NICARAGUA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-88.17,12.075,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 11 12:30:12.00 UTC">
    +                <name>M 5 - 2012 Aug 11, NORTHWESTERN IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>46.78,38.44,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 11 15:43:19.20 UTC">
    +                <name>M 5 - 2012 Aug 11, NORTHWESTERN IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>46.735,38.436,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 16 06:35:38.82 UTC">
    +                <name>M 5 - 2012 Aug 16, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.856,-18.46,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 17 08:47:36.72 UTC">
    +                <name>M 5 - 2012 Aug 17, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>154.53,-6.747,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 17 16:09:28.27 UTC">
    +                <name>M 5 - 2012 Aug 17, BOUGAINVILLE REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>154.525,-6.792,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 20 11:18:28.43 UTC">
    +                <name>M 5 - 2012 Aug 20, OFF W COAST OF NORTHERN SUMATRA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.889,3.318,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 22 01:33:05.97 UTC">
    +                <name>M 5 - 2012 Aug 22, HOKKAIDO, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.86,42.404,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 23 16:30:21.79 UTC">
    +                <name>M 5 - 2012 Aug 23, NEPAL</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>82.69,28.47,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 26 23:06:14.30 UTC">
    +                <name>M 5 - 2012 Aug 26, HALMAHERA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>128.801,2.626,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 00:39:55.00 UTC">
    +                <name>M 5 - 2012 Aug 27, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-69.414,-23.739,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 27 03:47:19.02 UTC">
    +                <name>M 5 - 2012 Aug 27, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.835,2.154,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 29 19:35:54.24 UTC">
    +                <name>M 5 - 2012 Aug 29, VIRGIN ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-64.324,19.652,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 30 08:04:39.97 UTC">
    +                <name>M 5 - 2012 Aug 30, OFFSHORE BIO-BIO, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-73.397,-37.199,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 17:00:35.87 UTC">
    +                <name>M 5 - 2012 Aug 31, SOUTH SANDWICH ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-27.525,-55.789,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Aug 31 18:37:45.03 UTC">
    +                <name>M 5 - 2012 Aug 31, GALAPAGOS ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-95.26,2.526,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 08:08:11.15 UTC">
    +                <name>M 5 - 2012 Sep 1, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.845,10.351,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 08:13:16.36 UTC">
    +                <name>M 5 - 2012 Sep 1, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.881,10.356,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 10:38:39.16 UTC">
    +                <name>M 5 - 2012 Sep 1, OFFSHORE ATACAMA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-71.114,-27.24,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 1 20:41:37.32 UTC">
    +                <name>M 5 - 2012 Sep 1, NEW GUINEA, PAPUA NEW GUINEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.643,-6.531,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 3 08:49:22.83 UTC">
    +                <name>M 5 - 2012 Sep 3, MORO GULF, MINDANAO, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.861,6.561,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 3 10:00:36.54 UTC">
    +                <name>M 5 - 2012 Sep 3, ANDREANOF ISLANDS, ALEUTIAN IS.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.052,51.224,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 02:11:31.73 UTC">
    +                <name>M 5 - 2012 Sep 4, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.067,-21.956,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 02:12:52.87 UTC">
    +                <name>M 5 - 2012 Sep 4, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-179.958,-22.003,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 04:27:13.02 UTC">
    +                <name>M 5 - 2012 Sep 4, SOUTH OF JAVA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>113.962,-10.839,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 06:45:10.28 UTC">
    +                <name>M 5 - 2012 Sep 4, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.772,10.666,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 4 20:00:17.53 UTC">
    +                <name>M 5 - 2012 Sep 4, TAIWAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.135,22.174,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 03:43:40.09 UTC">
    +                <name>M 5 - 2012 Sep 6, SOUTHERN IRAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>53.97,26.96,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 6 19:04:25.79 UTC">
    +                <name>M 5 - 2012 Sep 6, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.613,10.946,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 02:36:59.87 UTC">
    +                <name>M 5 - 2012 Sep 7, ANTOFAGASTA, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-68.191,-22.351,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 17:44:38.99 UTC">
    +                <name>M 5 - 2012 Sep 7, PHILIPPINE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.73,10.55,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 7 18:43:59.64 UTC">
    +                <name>M 5 - 2012 Sep 7, MARIANA ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.322,21.606,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 8 07:39:58.47 UTC">
    +                <name>M 5 - 2012 Sep 8, SOUTH OF KERMADEC ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.875,-33.129,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 00:13:50.13 UTC">
    +                <name>M 5 - 2012 Sep 9, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-176.539,-28.091,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 9 14:36:36.64 UTC">
    +                <name>M 5 - 2012 Sep 9, KERMADEC ISLANDS, NEW ZEALAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.935,-30.404,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 03:21:21.22 UTC">
    +                <name>M 5 - 2012 Sep 11, YUNNAN, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>99.267,24.647,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 11 20:52:05.29 UTC">
    +                <name>M 5 - 2012 Sep 11, SOUTH OF MARIANA ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.353,11.828,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 12 02:57:26.55 UTC">
    +                <name>M 5 - 2012 Sep 12, SOUTHWEST INDIAN RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>46.07,-39.962,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 14 03:21:45.40 UTC">
    +                <name>M 5 - 2012 Sep 14, NORTHERN XINJIANG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>82.425,43.637,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 16 13:02:53.83 UTC">
    +                <name>M 5 - 2012 Sep 16, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.657,-18.447,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 17 19:09:33.68 UTC">
    +                <name>M 5 - 2012 Sep 17, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.052,39.832,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 17 21:15:46.09 UTC">
    +                <name>M 5 - 2012 Sep 17, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.397,-14.867,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 18 08:05:40.08 UTC">
    +                <name>M 5 - 2012 Sep 18, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.438,4.488,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 03:32:35.38 UTC">
    +                <name>M 5 - 2012 Sep 20, LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>168.242,-20.296,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 20 15:52:35.30 UTC">
    +                <name>M 5 - 2012 Sep 20, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.872,2.185,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 21 08:47:40.09 UTC">
    +                <name>M 5 - 2012 Sep 21, CENTRAL MEDITERRANEAN SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>22.63,35.3,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 21 11:59:37.81 UTC">
    +                <name>M 5 - 2012 Sep 21, SOLOMON ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>158.013,-9.069,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 22 03:52:26.10 UTC">
    +                <name>M 5 - 2012 Sep 22, GREECE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>22.77,38.1,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 25 14:21:39.28 UTC">
    +                <name>M 5 - 2012 Sep 25, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.319,36.114,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 26 06:45:17.97 UTC">
    +                <name>M 5 - 2012 Sep 26, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-175.399,-18.853,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 26 17:15:52.92 UTC">
    +                <name>M 5 - 2012 Sep 26, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.416,-26.04,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Sep 29 14:12:01.54 UTC">
    +                <name>M 5 - 2012 Sep 29, MINDORO, PHILIPPINES</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>120.723,13.847,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 1 17:24:40.58 UTC">
    +                <name>M 5 - 2012 Oct 1, SOUTH OF FIJI ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-179.427,-24.745,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 03:33:34.20 UTC">
    +                <name>M 5 - 2012 Oct 2, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.903,-18.839,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 2 18:39:08.88 UTC">
    +                <name>M 5 - 2012 Oct 2, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.157,-20.728,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 3 21:02:38.50 UTC">
    +                <name>M 5 - 2012 Oct 3, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.197,-5.727,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 08:13:20.04 UTC">
    +                <name>M 5 - 2012 Oct 5, NORTHEAST OF TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.078,26.271,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 5 18:08:19.93 UTC">
    +                <name>M 5 - 2012 Oct 5, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>129.547,-6.661,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 8 12:00:48.06 UTC">
    +                <name>M 5 - 2012 Oct 8, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>129.191,-4.478,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 8 22:26:05.96 UTC">
    +                <name>M 5 - 2012 Oct 8, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>129.292,-4.511,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 12 19:24:16.64 UTC">
    +                <name>M 5 - 2012 Oct 12, COSTA RICA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-84.268,9.598,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 14 14:16:53.32 UTC">
    +                <name>M 5 - 2012 Oct 14, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>154.453,48.38,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 21:04:20.60 UTC">
    +                <name>M 5 - 2012 Oct 15, OFFSHORE COQUIMBO, CHILE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-71.787,-31.814,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 15 23:43:46.58 UTC">
    +                <name>M 5 - 2012 Oct 15, HINDU KUSH REGION, AFGHANISTAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>69.683,35.949,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 17 14:49:57.67 UTC">
    +                <name>M 5 - 2012 Oct 17, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.573,-15.036,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 17 19:38:55.81 UTC">
    +                <name>M 5 - 2012 Oct 17, NIAS REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>97.229,1.265,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 18 02:33:27.97 UTC">
    +                <name>M 5 - 2012 Oct 18, MADHYA PRADESH, INDIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>81.314,23.769,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 10:09:45.00 UTC">
    +                <name>M 5 - 2012 Oct 25, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>124.55,0.69,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 10:31:19.18 UTC">
    +                <name>M 5 - 2012 Oct 25, TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>120.558,22.417,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 25 18:57:06.16 UTC">
    +                <name>M 5 - 2012 Oct 25, PAPUA, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>140.348,-3.907,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 27 20:14:57.47 UTC">
    +                <name>M 5 - 2012 Oct 27, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>169.829,-21.494,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 03:08:52.41 UTC">
    +                <name>M 5 - 2012 Oct 28, ANDAMAN ISLANDS, INDIA REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>92.575,10.345,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 06:16:52.14 UTC">
    +                <name>M 5 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-131.36,52.335,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 28 07:25:21.51 UTC">
    +                <name>M 5 - 2012 Oct 28, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-131.561,52.319,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 06:13:28.69 UTC">
    +                <name>M 5 - 2012 Oct 29, FLORES REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.571,-8.284,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 06:34:47.51 UTC">
    +                <name>M 5 - 2012 Oct 29, FLORES REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>123.561,-8.041,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 29 11:07:15.00 UTC">
    +                <name>M 5 - 2012 Oct 29, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-131.182,51.952,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Oct 30 02:55:08.00 UTC">
    +                <name>M 5 - 2012 Oct 30, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-132.117,52.222,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 03:16:01.50 UTC">
    +                <name>M 5 - 2012 Nov 1, QUEEN CHARLOTTE ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-132.024,52.55,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 03:31:24.98 UTC">
    +                <name>M 5 - 2012 Nov 1, BANDA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>129.137,-5.612,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 09:48:04.20 UTC">
    +                <name>M 5 - 2012 Nov 1, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.638,-20.284,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 1 13:26:12.27 UTC">
    +                <name>M 5 - 2012 Nov 1, EASTERN NEW GUINEA REG., P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>145.559,-5.28,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 4 12:13:03.58 UTC">
    +                <name>M 5 - 2012 Nov 4, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>171.388,-22.265,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 6 01:38:20.12 UTC">
    +                <name>M 5 - 2012 Nov 6, MINAHASA, SULAWESI, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>122.088,1.281,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 9 16:18:41.87 UTC">
    +                <name>M 5 - 2012 Nov 9, SOLOMON ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>161.13,-10.121,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 10 09:06:51.27 UTC">
    +                <name>M 5 - 2012 Nov 10, FLORES REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>120.676,-8.887,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 11 23:21:13.51 UTC">
    +                <name>M 5 - 2012 Nov 11, OFFSHORE GUATEMALA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-92.299,13.897,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 12 11:15:09.26 UTC">
    +                <name>M 5 - 2012 Nov 12, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>137.994,29.691,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 13 18:28:12.81 UTC">
    +                <name>M 5 - 2012 Nov 13, MYANMAR</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>95.943,22.895,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 15 06:42:34.80 UTC">
    +                <name>M 5 - 2012 Nov 15, OFFSHORE CHIAPAS, MEXICO</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-92.727,13.843,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 15 16:08:17.83 UTC">
    +                <name>M 5 - 2012 Nov 15, SOUTH OF MARIANA ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.554,11.668,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 19 17:10:12.31 UTC">
    +                <name>M 5 - 2012 Nov 19, PACIFIC-ANTARCTIC RIDGE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-147.523,-57.349,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 21 17:42:36.66 UTC">
    +                <name>M 5 - 2012 Nov 21, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.648,38.479,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 22 09:09:32.31 UTC">
    +                <name>M 5 - 2012 Nov 22, BALI REGION, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>115.124,-8.918,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 22 22:44:31.93 UTC">
    +                <name>M 5 - 2012 Nov 22, SOUTHEAST OF LOYALTY ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>173.923,-22.354,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 23 00:40:21.23 UTC">
    +                <name>M 5 - 2012 Nov 23, MOLUCCA SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.824,1.619,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 23 20:50:46.81 UTC">
    +                <name>M 5 - 2012 Nov 23, SOLOMON ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>162.74,-10.789,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 26 05:33:48.80 UTC">
    +                <name>M 5 - 2012 Nov 26, SOUTHERN XINJIANG, CHINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>90.355,40.411,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 26 17:35:41.80 UTC">
    +                <name>M 5 - 2012 Nov 26, DODECANESE ISLANDS, GREECE</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>27.993,36.655,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 28 23:15:29.84 UTC">
    +                <name>M 5 - 2012 Nov 28, NORTHERN ALGERIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>5.312,36.941,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 00:09:39.00 UTC">
    +                <name>M 5 - 2012 Nov 29, MENDOZA, ARGENTINA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-69.106,-32.91,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 12:41:35.78 UTC">
    +                <name>M 5 - 2012 Nov 29, NORTHEAST OF TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>125.191,26.112,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Nov 29 23:22:36.46 UTC">
    +                <name>M 5 - 2012 Nov 29, NEAR ISLANDS, ALEUTIAN ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>172.416,52.635,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 1 20:10:14.85 UTC">
    +                <name>M 5 - 2012 Dec 1, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.38,-20.688,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 2 17:45:37.18 UTC">
    +                <name>M 5 - 2012 Dec 2, TAIWAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>121.631,23.91,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 2 23:45:14.20 UTC">
    +                <name>M 5 - 2012 Dec 2, KURIL ISLANDS</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.47,45.701,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 5 13:48:42.22 UTC">
    +                <name>M 5 - 2012 Dec 5, SAMOA ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-173.266,-14.936,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 5 23:41:54.11 UTC">
    +                <name>M 5 - 2012 Dec 5, KERMADEC ISLANDS REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-177.523,-31.103,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 02:25:46.47 UTC">
    +                <name>M 5 - 2012 Dec 7, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>151.429,-5.293,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 12:58:07.15 UTC">
    +                <name>M 5 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.627,37.835,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 7 15:15:31.56 UTC">
    +                <name>M 5 - 2012 Dec 7, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.781,37.869,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 8 10:27:30.38 UTC">
    +                <name>M 5 - 2012 Dec 8, NEAR S COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>144.044,-7.167,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 8 19:00:07.41 UTC">
    +                <name>M 5 - 2012 Dec 8, NEAR S COAST OF NEW GUINEA, PNG.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>144.041,-7.258,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 9 18:30:04.63 UTC">
    +                <name>M 5 - 2012 Dec 9, NEW GUINEA, PAPUA NEW GUINEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.117,-3.563,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 9 23:50:33.02 UTC">
    +                <name>M 5 - 2012 Dec 9, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.811,37.519,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 10 17:34:36.13 UTC">
    +                <name>M 5 - 2012 Dec 10, KEPULAUAN TALAUD, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>126.869,3.655,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 11 23:16:23.17 UTC">
    +                <name>M 5 - 2012 Dec 11, VANUATU</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>167.736,-18.987,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 13 17:16:22.20 UTC">
    +                <name>M 5 - 2012 Dec 13, COSTA RICA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-84.015,9.062,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 14 02:34:52.55 UTC">
    +                <name>M 5 - 2012 Dec 14, NEW BRITAIN REGION, P.N.G.</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>152.959,-5.015,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 15 12:43:30.09 UTC">
    +                <name>M 5 - 2012 Dec 15, OFF THE COAST OF EL SALVADOR</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-88.605,12.247,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 17 22:52:19.63 UTC">
    +                <name>M 5 - 2012 Dec 17, OFF EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>143.568,37.728,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 19 16:02:19.99 UTC">
    +                <name>M 5 - 2012 Dec 19, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.283,-20.508,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 20 19:38:45.82 UTC">
    +                <name>M 5 - 2012 Dec 20, TONGA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-174.074,-20.765,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 21 08:07:20.27 UTC">
    +                <name>M 5 - 2012 Dec 21, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.71,38.633,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 21 14:43:39.42 UTC">
    +                <name>M 5 - 2012 Dec 21, IZU ISLANDS, JAPAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>142.212,29.779,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 24 23:24:50.06 UTC">
    +                <name>M 5 - 2012 Dec 24, WEST OF MACQUARIE ISLAND</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>146.898,-55.683,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 25 17:36:32.80 UTC">
    +                <name>M 5 - 2012 Dec 25, PAKISTAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>66.465,28.464,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 02:06:37.12 UTC">
    +                <name>M 5 - 2012 Dec 26, TAIWAN REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>122.55,24.043,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 26 02:58:23.23 UTC">
    +                <name>M 5 - 2012 Dec 26, FIJI REGION</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>-178.619,-20.69,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 07:19:35.32 UTC">
    +                <name>M 5 - 2012 Dec 29, NEAR EAST COAST OF HONSHU, JAPAN</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>141.025,37.064,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 29 07:56:00.31 UTC">
    +                <name>M 5 - 2012 Dec 29, BISMARCK SEA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>148.752,-3.371,0</coordinates>
    +                </Point>
    +            </Placemark>
    +            <Placemark id="2012 Dec 30 07:56:52.07 UTC">
    +                <name>M 5 - 2012 Dec 30, SUNDA STRAIT, INDONESIA</name>
    +                <magnitude>5</magnitude>
    +                <Point>
    +                    <coordinates>105.283,-6.96,0</coordinates>
    +                </Point>
    +            </Placemark>
    +        </Folder>
    +    </Document>
    +</kml>
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/data/kml/timezones.kml b/mapbox-gl/examples/data/kml/timezones.kml
    new file mode 100644
    index 0000000000..d16f018bbd
    --- /dev/null
    +++ b/mapbox-gl/examples/data/kml/timezones.kml
    @@ -0,0 +1,1608 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<kml xmlns="http://earth.google.com/kml/2.2">
    +<Document>
    +  <Style id="hidelist"><ListStyle><listItemType>checkHideChildren</listItemType><bgColor>00ffffff</bgColor><maxSnippetLines>0</maxSnippetLines></ListStyle></Style>
    +
    +  <Style id="nStylem1200"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStylem1100"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStylem1000"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStylem0950"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStylem0900"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>
    +  <Style id="nStylem0850"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStylem0800"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStylem0700"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStylem0600"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStylem0500"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>	
    +  <Style id="nStylem0400"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStylem0350"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStylem0300"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStylem0200"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStylem0100"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>		
    +  <Style id="nStyle0000"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStyle0100"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStyle0200"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStyle0300"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>	
    +  <Style id="nStyle0350"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStyle0400"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStyle0450"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2f8f6e2</color></PolyStyle></Style>
    +  <Style id="nStyle0500"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStyle0550"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStyle0575"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2f8f6e2</color></PolyStyle></Style>
    +  <Style id="nStyle0600"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStyle0650"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStyle0700"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>	
    +  <Style id="nStyle0800"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStyle0900"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +  <Style id="nStyle0950"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStyle1000"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d20c939c</color></PolyStyle></Style>			
    +  <Style id="nStyle1050"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStyle1100"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d299f7ff</color></PolyStyle></Style>	
    +  <Style id="nStyle1150"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2f8f6e2</color></PolyStyle></Style>
    +  <Style id="nStyle1200"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d261d6cd</color></PolyStyle></Style>
    +  <Style id="nStyle1275"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d2eafeff</color></PolyStyle></Style>
    +  <Style id="nStyle1300"><LineStyle><color>4cffffff</color><width>1.0</width></LineStyle><PolyStyle><color>d25bb85f</color></PolyStyle></Style>
    +
    +  <Style id="hStylem1200"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStylem1100"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStylem1000"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStylem0950"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStylem0900"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>
    +  <Style id="hStylem0850"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStylem0800"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStylem0700"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStylem0600"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStylem0500"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>	
    +  <Style id="hStylem0400"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStylem0350"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStylem0300"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStylem0200"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStylem0100"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>		
    +  <Style id="hStyle0000"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStyle0100"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStyle0200"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStyle0300"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>	
    +  <Style id="hStyle0350"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStyle0400"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStyle0450"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daf8f6e2</color></PolyStyle></Style>
    +  <Style id="hStyle0500"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStyle0550"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStyle0575"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daf8f6e2</color></PolyStyle></Style>
    +  <Style id="hStyle0600"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStyle0650"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStyle0700"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>	
    +  <Style id="hStyle0800"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStyle0900"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +  <Style id="hStyle0950"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStyle1000"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da0c939c</color></PolyStyle></Style>			
    +  <Style id="hStyle1050"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStyle1100"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da99f7ff</color></PolyStyle></Style>	
    +  <Style id="hStyle1150"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daf8f6e2</color></PolyStyle></Style>
    +  <Style id="hStyle1200"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da61d6cd</color></PolyStyle></Style>
    +  <Style id="hStyle1275"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>daeafeff</color></PolyStyle></Style>
    +  <Style id="hStyle1300"><LineStyle><color>c0ffffff</color><width>1.0</width></LineStyle><PolyStyle><color>da5bb85f</color></PolyStyle></Style>
    +
    +
    +  <StyleMap id="khStylem1200"><Pair><key>normal</key><styleUrl>#nStylem1200</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem1200</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem1100"><Pair><key>normal</key><styleUrl>#nStylem1100</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem1100</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem1000"><Pair><key>normal</key><styleUrl>#nStylem1000</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem1000</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0950"><Pair><key>normal</key><styleUrl>#nStylem0950</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0950</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0900"><Pair><key>normal</key><styleUrl>#nStylem0900</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0900</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0850"><Pair><key>normal</key><styleUrl>#nStylem0850</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0850</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0800"><Pair><key>normal</key><styleUrl>#nStylem0800</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0800</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0700"><Pair><key>normal</key><styleUrl>#nStylem0700</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0700</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0600"><Pair><key>normal</key><styleUrl>#nStylem0600</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0600</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0500"><Pair><key>normal</key><styleUrl>#nStylem0500</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0500</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0400"><Pair><key>normal</key><styleUrl>#nStylem0400</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0400</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0350"><Pair><key>normal</key><styleUrl>#nStylem0350</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0350</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0300"><Pair><key>normal</key><styleUrl>#nStylem0300</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0300</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0200"><Pair><key>normal</key><styleUrl>#nStylem0200</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0200</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStylem0100"><Pair><key>normal</key><styleUrl>#nStylem0100</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStylem0100</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0000"><Pair><key>normal</key><styleUrl>#nStyle0000</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0000</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0100"><Pair><key>normal</key><styleUrl>#nStyle0100</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0100</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0200"><Pair><key>normal</key><styleUrl>#nStyle0200</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0200</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0300"><Pair><key>normal</key><styleUrl>#nStyle0300</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0300</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0350"><Pair><key>normal</key><styleUrl>#nStyle0350</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0350</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0400"><Pair><key>normal</key><styleUrl>#nStyle0400</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0400</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0450"><Pair><key>normal</key><styleUrl>#nStyle0450</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0450</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0500"><Pair><key>normal</key><styleUrl>#nStyle0500</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0500</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0550"><Pair><key>normal</key><styleUrl>#nStyle0550</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0550</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0575"><Pair><key>normal</key><styleUrl>#nStyle0575</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0575</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0600"><Pair><key>normal</key><styleUrl>#nStyle0600</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0600</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0650"><Pair><key>normal</key><styleUrl>#nStyle0650</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0650</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0700"><Pair><key>normal</key><styleUrl>#nStyle0700</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0700</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0800"><Pair><key>normal</key><styleUrl>#nStyle0800</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0800</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0900"><Pair><key>normal</key><styleUrl>#nStyle0900</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0900</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle0950"><Pair><key>normal</key><styleUrl>#nStyle0950</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle0950</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1000"><Pair><key>normal</key><styleUrl>#nStyle1000</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1000</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1050"><Pair><key>normal</key><styleUrl>#nStyle1050</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1050</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1100"><Pair><key>normal</key><styleUrl>#nStyle1100</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1100</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1150"><Pair><key>normal</key><styleUrl>#nStyle1150</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1150</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1200"><Pair><key>normal</key><styleUrl>#nStyle1200</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1200</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1275"><Pair><key>normal</key><styleUrl>#nStyle1275</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1275</styleUrl></Pair></StyleMap>
    +  <StyleMap id="khStyle1300"><Pair><key>normal</key><styleUrl>#nStyle1300</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#hStyle1300</styleUrl></Pair></StyleMap>
    +
    +  <styleUrl>#hidelist</styleUrl>
    +
    +		<Placemark>
    +			<name>GMT -12:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-12);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem1200</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-179.0623,-89.90000000000001,0 -180,-89.90000000000001,0 -180,-86.5,0 -180,-81.49999,0 -180,-76.5,0 -180,-71.5,0 -180,-66.5,0 -180,-61.5,0 -180,-56.5,0 -180,-51.50000000000001,0 -176.2216,-48.22535,0 -172.5,-45,0 -172.4998,-50,0 -172.4996,-55.00000000000001,0 -172.4995,-60.00000000000001,0 -172.4993,-65,0 -172.4991,-70,0 -172.4989,-75,0 -172.4987,-80,0 -172.4986,-83.3,0 -172.4984,-88.3,0 -172.4984,-89.90000000000001,0 -173.4361,-89.90000000000001,0 -174.3738,-89.90000000000001,0 -175.3115,-89.90000000000001,0 -176.2492,-89.90000000000001,0 -177.1869,-89.90000000000001,0 -178.1246,-89.90000000000001,0 -179.0623,-89.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-172.509,74.89999,0 -172.509,70.23302,0 -175.7817,72.31562,0 -180,75,0 -180,79.90000000000001,0 -180,84.90001,0 -180,89.90000000000001,0 -179.0636,89.90000000000001,0 -178.1273,89.90000000000001,0 -177.1909,89.90000000000001,0 -176.2545,89.90000000000001,0 -175.3181,89.90000000000001,0 -174.3818,89.90000000000001,0 -173.4454,89.90000000000001,0 -172.509,89.90000000000001,0 -172.509,84.90001,0 -172.509,79.90000000000001,0 -172.509,74.89999,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -11:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-11);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem1100</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +180,49.99722,0 180,48,0 176.0055,49.99722,0 180,49.99722,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-167.5405,-5.592744,0 -167.5405,-7.092370000000001,0 -172.5404,-7.073442000000002,0 -172.9307,-7.071967,0 -172.9307,-10.5021,0 -167.9308,-10.46422,0 -167.5405,-10.46127,0 -167.5405,-15.46126,0 -167.5405,-20.46127,0 -167.5405,-22.51781,0 -162.5405,-22.51781,0 -157.5405,-22.51781,0 -157.5063,-22.51781,0 -157.5063,-27.51781,0 -157.5063,-32.51781,0 -157.5063,-37.51781,0 -157.5063,-40.2,0 -157.5063,-45.2,0 -157.5063,-50.2,0 -157.5063,-50.7,0 -157.5063,-55.7,0 -157.5063,-60.7,0 -157.5063,-65.7,0 -157.5063,-70.7,0 -157.5063,-75.7,0 -157.5063,-80.7,0 -157.5063,-85.7,0 -157.5063,-89.90000000000001,0 -158.5058,-89.90000000000001,0 -159.5053,-89.90000000000001,0 -160.5048,-89.90000000000001,0 -161.5042,-89.90000000000001,0 -162.5037,-89.90000000000001,0 -163.5032,-89.90000000000001,0 -164.5026,-89.90000000000001,0 -165.5021,-89.90000000000001,0 -166.5016,-89.90000000000001,0 -167.501,-89.90000000000001,0 -168.5005,-89.90000000000001,0 -169.5,-89.90000000000001,0 -170.4995,-89.90000000000001,0 -171.4989,-89.90000000000001,0 -172.4984,-89.90000000000001,0 -172.4984,-88.3,0 -172.4986,-83.3,0 -172.4987,-80,0 -172.4989,-75,0 -172.4991,-70,0 -172.4993,-65,0 -172.4995,-60.00000000000001,0 -172.4996,-55.00000000000001,0 -172.4998,-50,0 -172.5,-45,0 -172.5,-43.5,0 -172.5,-38.5,0 -172.5,-33.5,0 -172.5,-28.5,0 -172.5,-23.5,0 -172.5,-20,0 -172.5,-15,0 -174,-13,0 -177,-9.000000999999999,0 -180,-4.999996,0 -180,-4.989699,0 -180,0.01029546,0 -180,5.0103,0 -180,10.0103,0 -180,15.0103,0 -180,20.0103,0 -180,25.0103,0 -180,30.0103,0 -180,35.0103,0 -180,40.0103,0 -180,45.01030000000001,0 -180,50.0103,0 -175,50.01045,0 -170,50.01062000000001,0 -169.3893,50.01063000000001,0 -164.3893,50.01743,0 -159.3893,50.02422,0 -157.5064,50.02679,0 -157.5064,45.02678,0 -157.5064,40.02678,0 -157.5064,35.02678,0 -157.5064,30.02678,0 -157.5064,26.53553,0 -162.5064,26.53553,0 -167.5064,26.53553,0 -172.4834,26.53553,0 -172.4834,22.5508,0 -174.6819,22.5508,0 -174.6819,17.5508,0 -174.6819,14.44395,0 -169.6819,14.41826,0 -167.5405,14.40725,0 -167.5405,9.407258000000001,0 -167.5405,4.407255,0 -167.5405,-0.5927495,0 -167.5405,-5.592744,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-167.1914,68.69023,0 -169,68,0 -172.509,70.23302,0 -172.509,74.89999,0 -172.509,79.90000000000001,0 -172.509,84.90001,0 -172.509,89.90000000000001,0 -171.5101,89.90000000000001,0 -170.5112,89.90000000000001,0 -169.5124,89.90000000000001,0 -168.5135,89.90000000000001,0 -167.5146,89.90000000000001,0 -166.5157,89.90000000000001,0 -165.5168,89.90000000000001,0 -164.5179,89.90000000000001,0 -163.519,89.90000000000001,0 -162.5201,89.90000000000001,0 -161.5212,89.90000000000001,0 -160.5223,89.90000000000001,0 -159.5234,89.90000000000001,0 -158.5245,89.90000000000001,0 -157.5256,89.90000000000001,0 -157.5256,84.90001,0 -157.5256,79.90000000000001,0 -157.5256,74.89999,0 -157.5256,71.37826,0 -162.4455,70.48654999999999,0 -162.5201,70.47302999999999,0 -167.1914,68.69023,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -10:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-10);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem1000</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-142.5293,-4.65319,0 -142.5293,-9.653193,0 -142.5293,-13.48625,0 -137.5294,-13.45937,0 -134.3385,-13.44221,0 -134.3385,-18.44221,0 -134.3385,-21.37773,0 -136.8102,-23.676,0 -141.8102,-23.676,0 -142.5293,-23.676,0 -142.5293,-28.676,0 -142.5293,-33.676,0 -142.5293,-38.676,0 -142.5293,-43.676,0 -142.5293,-48.67599000000001,0 -142.5293,-53.676,0 -142.5293,-56.87600000000001,0 -142.5293,-61.876,0 -142.5293,-66.87600999999999,0 -142.5293,-71.87600000000001,0 -142.5293,-73.476,0 -142.5293,-78.476,0 -142.5293,-83.476,0 -142.5293,-88.476,0 -142.5293,-89.90000000000001,0 -143.5278,-89.90000000000001,0 -144.5262,-89.90000000000001,0 -145.5247,-89.90000000000001,0 -146.5232,-89.90000000000001,0 -147.5216,-89.90000000000001,0 -148.5201,-89.90000000000001,0 -149.5186,-89.90000000000001,0 -150.5171,-89.90000000000001,0 -151.5155,-89.90000000000001,0 -152.514,-89.90000000000001,0 -153.5125,-89.90000000000001,0 -154.5109,-89.90000000000001,0 -155.5094,-89.90000000000001,0 -156.5079,-89.90000000000001,0 -157.5063,-89.90000000000001,0 -157.5063,-85.7,0 -157.5063,-80.7,0 -157.5063,-75.7,0 -157.5063,-70.7,0 -157.5063,-65.7,0 -157.5063,-60.7,0 -157.5063,-55.7,0 -157.5063,-50.7,0 -157.5063,-50.2,0 -157.5063,-45.2,0 -157.5063,-40.2,0 -157.5063,-37.51781,0 -157.5063,-32.51781,0 -157.5063,-27.51781,0 -157.5063,-22.51781,0 -157.5405,-22.51781,0 -162.5405,-22.51781,0 -167.5405,-22.51781,0 -167.5405,-20.46127,0 -167.5405,-15.46126,0 -167.5405,-10.46127,0 -167.9308,-10.46422,0 -172.9307,-10.5021,0 -172.9307,-7.071967,0 -172.5404,-7.073442000000002,0 -167.5405,-7.092370000000001,0 -167.5405,-5.592744,0 -167.5405,-0.5927495,0 -167.5405,4.407255,0 -167.5405,9.407258000000001,0 -167.5405,14.40725,0 -169.6819,14.41826,0 -174.6819,14.44395,0 -174.6819,17.5508,0 -174.6819,22.5508,0 -172.4834,22.5508,0 -172.4834,26.53553,0 -167.5064,26.53553,0 -162.5064,26.53553,0 -157.5064,26.53553,0 -157.5064,30.02678,0 -157.5064,35.02678,0 -157.5064,40.02678,0 -157.5064,45.02678,0 -157.5064,50.02679,0 -152.5064,50.03357,0 -147.5064,50.04037,0 -142.5293,50.04713,0 -142.5293,45.04712000000001,0 -142.5293,40.04713,0 -142.5293,35.04712,0 -142.5293,30.04713,0 -142.5293,25.04712,0 -142.5293,20.04713,0 -142.5293,18.34681,0 -142.5293,13.34681,0 -142.5293,8.346807,0 -142.5293,3.346812,0 -142.5293,0.3468055,0 -142.5293,-4.65319,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-175,50.01045,0 -180,50.0103,0 -180,50.02665,0 -180,55.02666,0 -175,55.02376000000001,0 -170,55.02087000000001,0 -169.3902,55.02052,0 -169.3893,50.02052000000001,0 -169.3893,50.01063000000001,0 -170,50.01062000000001,0 -175,50.01045,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +180,49.99722,0 176.0055,49.99722,0 171.5334,52.23330000000001,0 170,53,0 173.415,55.03272,0 178.415,55.03272,0 180,55.03272,0 180,54.99723,0 180,49.99722,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-142.4651,74.90014000000001,0 -142.4453,70.32476,0 -147.4331,70.67321,0 -152.421,71.02166,0 -157.4088,71.37009999999999,0 -157.5256,71.37826,0 -157.5256,74.89999,0 -157.5256,79.90000000000001,0 -157.5256,84.90001,0 -157.5256,89.90000000000001,0 -156.5259,89.90000000000001,0 -155.5262,89.90000000000001,0 -154.5265,89.90000000000001,0 -153.5268,89.90000000000001,0 -152.5271,89.90000000000001,0 -151.5274,89.90000000000001,0 -150.5277,89.90000000000001,0 -149.528,89.90000000000001,0 -148.5283,89.90000000000001,0 -147.5285,89.90000000000001,0 -146.5288,89.90000000000001,0 -145.5291,89.90000000000001,0 -144.5294,89.90000000000001,0 -143.5297,89.90000000000001,0 -142.53,89.90000000000001,0 -142.5084,84.90004999999999,0 -142.4867,79.90009000000001,0 -142.4651,74.90014000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -09:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-9.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0950</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-141.5368,-7.197858,0 -136.5368,-7.197858,0 -135.6827,-7.197858,0 -135.6827,-11.10058,0 -140.6827,-11.10058,0 -141.5368,-11.10058,0 -141.5368,-7.197858,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -09:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-9);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0900</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +178.415,55.03272,0 173.415,55.03272,0 177.7114,57.59015000000001,0 180,58.95238000000001,0 180,55.03272,0 178.415,55.03272,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-127.5151,74.89999,0 -127.5151,70.49976,0 -128.2554,70.67574000000001,0 -128.8733,70.34173000000001,0 -129.7417,70.30833,0 -129.9588,70.14133,0 -130.4598,70.22484,0 -133.5828,69.57352,0 -134.3343,69.85742999999999,0 -135.6703,69.64033000000001,0 -136.2715,69.22282,0 -136.4206,68.90156,0 -137.0624,68.95941999999999,0 -137.2391,69.01522,0 -137.4809,69.01522,0 -138.2296,69.18262,0 -138.3319,69.24308000000001,0 -138.383,69.31749000000001,0 -138.5411,69.26168,0 -138.6248,69.28493,0 -138.8806,69.61044,0 -139.1271,69.66159,0 -139.3828,69.55929,0 -139.9315,69.62904000000002,0 -140.378,69.60579,0 -141.0033,69.6567,0 -140.9997,65.30721,0 -140.9955,60.30721,0 -140.5214,60.22222,0 -140.4508,60.30972000000001,0 -139.9794,60.18776,0 -139.6767,60.34055,0 -139.0681,60.35222,0 -139.1889,60.08889,0 -139.0478,59.99749,0 -138.6903,59.90693,0 -138.6158,59.77417000000001,0 -137.9578,59.43166,0 -137.5908,59.23860000000001,0 -137.4797,58.99805,0 -137.5075,58.92556,0 -137.445,58.90749,0 -137.2961,58.98999,0 -136.8089,59.16526,0 -136.5839,59.16332,0 -136.4628,59.28944,0 -136.4636,59.46970000000001,0 -136.3716,59.45249,0 -136.2339,59.52583,0 -136.2392,59.56138000000001,0 -136.3439,59.60277,0 -135.9492,59.66916,0 -135.4736,59.80194,0 -135.235,59.70305,0 -135.1775,59.63693,0 -135.0145,59.5675,0 -135.0211,59.4711,0 -135.0917,59.42694,0 -134.9892,59.38999,0 -135.0161,59.34777,0 -134.9519,59.27999,0 -134.688,59.24333,0 -134.6717,59.20027000000001,0 -134.5664,59.13055000000001,0 -134.4614,59.12665,0 -134.3794,59.04916,0 -134.3953,58.98305,0 -134.3114,58.96444000000001,0 -134.3322,58.92972,0 -134.2455,58.85695000000001,0 -133.8269,58.7261,0 -133.6833,58.59554,0 -133.43,58.45917,0 -133.3879,58.41206,0 -133.4303,58.35999,0 -133.3611,58.28054,0 -133.137,58.13582,0 -133.0708,58.01222,0 -132.8817,57.86361000000001,0 -132.2267,57.20471,0 -132.3369,57.08832,0 -132.0275,57.03638,0 -132.1031,56.86666,0 -131.8614,56.79972,0 -131.8845,56.74471000000001,0 -131.8269,56.67916,0 -131.8242,56.59693,0 -131.5578,56.60221,0 -131.055,56.39804,0 -130.7744,56.3661,0 -130.6272,56.25861,0 -130.4619,56.23527,0 -130.4142,56.12887,0 -130.2292,56.09027,0 -130.0886,56.11804000000001,0 -130.0147,56.02583000000001,0 -130.0151,55.90919000000001,0 -130.1747,55.75443000000001,0 -130.1225,55.65499,0 -130.1439,55.54139000000001,0 -129.9905,55.28166000000001,0 -130.1014,55.22776,0 -130.2311,55.035,0 -130.3597,54.90805,0 -130.5897,54.79371,0 -130.6491,54.70139000000001,0 -133.0791,54.26532,0 -133.1971,53.93898,0 -132.9926,53.51827999999999,0 -132.5012,53.02680000000001,0 -132.1788,52.75157,0 -131.0228,51.91016000000001,0 -127.5078,49.81058,0 -127.5078,48.21058000000001,0 -127.5078,43.21058,0 -127.5078,38.21058,0 -127.5078,33.21058,0 -127.5078,28.21058,0 -127.5078,23.21058,0 -127.5079,18.21058,0 -127.5079,15.01059,0 -127.5079,10.01058,0 -127.5079,5.010579,0 -127.5079,0.01058346,0 -127.5079,-2.500003,0 -127.5079,-7.499998000000002,0 -127.5079,-12.5,0 -127.5079,-17.5,0 -127.508,-22.5,0 -131.5,-22.5,0 -131.5,-26,0 -127.5075,-26,0 -127.5075,-29.9,0 -127.5075,-34.9,0 -127.5074,-39.90000000000001,0 -127.5074,-44.9,0 -127.5074,-49.90001,0 -127.5074,-54.9,0 -127.5074,-59.90001,0 -127.5074,-60.8,0 -127.5073,-65.8,0 -127.5073,-70.8,0 -127.5073,-75.8,0 -127.5073,-79.90000000000001,0 -127.5073,-84.89999,0 -127.5073,-89.90000000000001,0 -128.4461,-89.90000000000001,0 -129.385,-89.90000000000001,0 -130.3239,-89.90000000000001,0 -131.2628,-89.90000000000001,0 -132.2016,-89.90000000000001,0 -133.1405,-89.90000000000001,0 -134.0794,-89.90000000000001,0 -135.0183,-89.90000000000001,0 -135.9571,-89.90000000000001,0 -136.896,-89.90000000000001,0 -137.8349,-89.90000000000001,0 -138.7738,-89.90000000000001,0 -139.7127,-89.90000000000001,0 -140.6515,-89.90000000000001,0 -141.5904,-89.90000000000001,0 -142.5293,-89.90000000000001,0 -142.5293,-88.476,0 -142.5293,-83.476,0 -142.5293,-78.476,0 -142.5293,-73.476,0 -142.5293,-71.87600000000001,0 -142.5293,-66.87600999999999,0 -142.5293,-61.876,0 -142.5293,-56.87600000000001,0 -142.5293,-53.676,0 -142.5293,-48.67599000000001,0 -142.5293,-43.676,0 -142.5293,-38.676,0 -142.5293,-33.676,0 -142.5293,-28.676,0 -142.5293,-23.676,0 -141.8102,-23.676,0 -136.8102,-23.676,0 -134.3385,-21.37773,0 -134.3385,-18.44221,0 -134.3385,-13.44221,0 -137.5294,-13.45937,0 -142.5293,-13.48625,0 -142.5293,-9.653193,0 -142.5293,-4.65319,0 -142.5293,0.3468055,0 -142.5293,3.346812,0 -142.5293,8.346807,0 -142.5293,13.34681,0 -142.5293,18.34681,0 -142.5293,20.04713,0 -142.5293,25.04712,0 -142.5293,30.04713,0 -142.5293,35.04712,0 -142.5293,40.04713,0 -142.5293,45.04712000000001,0 -142.5293,50.04713,0 -147.5064,50.04037,0 -152.5064,50.03357,0 -157.5064,50.02679,0 -159.3893,50.02422,0 -164.3893,50.01743,0 -169.3893,50.01063000000001,0 -169.3893,50.02052000000001,0 -169.3902,55.02052,0 -170,55.02087000000001,0 -175,55.02376000000001,0 -180,55.02666,0 -180,58.95238000000001,0 -177.5929,60.38517000000001,0 -173.2965,62.94258000000001,0 -169,65.5,0 -169,68,0 -167.1914,68.69023,0 -162.5201,70.47302999999999,0 -162.4455,70.48654999999999,0 -157.5256,71.37826,0 -157.4088,71.37009999999999,0 -152.421,71.02166,0 -147.4331,70.67321,0 -142.4453,70.32476,0 -142.4651,74.90014000000001,0 -142.4867,79.90009000000001,0 -142.5084,84.90004999999999,0 -142.53,89.90000000000001,0 -141.5916,89.90000000000001,0 -140.6532,89.90000000000001,0 -139.7147,89.90000000000001,0 -138.7763,89.90000000000001,0 -137.8378,89.90000000000001,0 -136.8994,89.90000000000001,0 -135.961,89.90000000000001,0 -135.0226,89.90000000000001,0 -134.0841,89.90000000000001,0 -133.1457,89.90000000000001,0 -132.2073,89.90000000000001,0 -131.2688,89.90000000000001,0 -130.3304,89.90000000000001,0 -129.392,89.90000000000001,0 -128.4535,89.90000000000001,0 -127.5151,89.90000000000001,0 -127.5151,84.90001,0 -127.5151,79.90000000000001,0 -127.5151,74.89999,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<innerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-141.5368,-7.197858,0 -141.5368,-11.10058,0 -140.6827,-11.10058,0 -135.6827,-11.10058,0 -135.6827,-7.197858,0 -136.5368,-7.197858,0 -141.5368,-7.197858,0 </coordinates>
    +					</LinearRing>
    +				</innerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -08:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-8.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0850</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-127.5075,-26,0 -131.5,-26,0 -131.5,-22.5,0 -127.508,-22.5,0 -122.508,-22.5,0 -121,-22.5,0 -121,-26,0 -126,-26,0 -127.5075,-26,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -08:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-8);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0800</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-138.8806,69.61044,0 -138.6248,69.28493,0 -138.5411,69.26168,0 -138.383,69.31749000000001,0 -138.3319,69.24308000000001,0 -138.2296,69.18262,0 -137.4809,69.01522,0 -137.2391,69.01522,0 -137.0624,68.95941999999999,0 -136.4206,68.90156,0 -136.4461,68.87302,0 -136.4447,68.58778,0 -136.4442,68.43304000000001,0 -136.445,67.71554999999999,0 -136.4211,67.65526000000001,0 -136.2411,67.62053,0 -136.1578,67.50776999999999,0 -136.2053,67.40694000000001,0 -136.1164,67.38274,0 -136.1428,67.35942,0 -136.0914,67.30247,0 -136.1345,67.22638000000001,0 -136.2364,67.17386999999999,0 -136.1989,67.14333000000001,0 -136.2222,67.06415,0 -136.1645,67.00360000000001,0 -133.8311,67.00333000000001,0 -134.0461,66.98441,0 -134.0925,66.95527,0 -133.9081,66.90887000000001,0 -133.7639,66.80692999999999,0 -133.8403,66.72776,0 -133.7572,66.68526,0 -133.7719,66.64413999999999,0 -133.5867,66.5647,0 -133.6903,66.53193,0 -133.6294,66.44525,0 -133.7503,66.44775,0 -133.7681,66.37303,0 -133.8247,66.31666,0 -133.57,66.27831999999999,0 -133.5639,66.15583,0 -133.6731,66.11693,0 -133.7047,66.07193,0 -133.6033,66.05165,0 -133.6294,65.97443,0 -133.4075,65.94386,0 -133.1475,66.02469000000001,0 -132.9397,66.02887,0 -132.9084,65.99693000000001,0 -132.9939,65.92303,0 -132.9061,65.90833000000001,0 -132.817,65.91998,0 -132.7822,65.96720999999999,0 -132.5761,66.03055000000001,0 -132.5103,65.97887,0 -132.3536,65.99026000000001,0 -132.3267,65.95193,0 -132.54,65.88916,0 -132.5733,65.84859,0 -132.5294,65.82555000000001,0 -132.3239,65.73997,0 -132.1719,65.59804,0 -132.2736,65.54694000000001,0 -132.3278,65.43471,0 -132.5214,65.36304,0 -132.5553,65.28360000000001,0 -132.7836,65.22499000000001,0 -132.7169,65.20387,0 -132.7878,65.18777,0 -132.7147,65.15555000000001,0 -132.5764,65.19054,0 -132.5275,65.16277000000001,0 -132.5561,65.11803999999999,0 -132.54,65.09388,0 -132.3817,65.08499000000001,0 -132.3333,65.03471999999999,0 -132.5075,64.96442,0 -132.4825,64.87691,0 -132.6153,64.81219,0 -132.5706,64.77249999999999,0 -132.3689,64.77831999999999,0 -132.1636,64.70166,0 -132.0464,64.69553000000001,0 -131.9158,64.58249000000001,0 -131.6942,64.53498999999999,0 -131.6878,64.4986,0 -131.8039,64.43803,0 -131.8181,64.37831,0 -131.593,64.36859,0 -131.4275,64.41638,0 -131.3886,64.46720999999999,0 -131.1417,64.42443,0 -131.002,64.33028,0 -131.0605,64.27943000000001,0 -130.9797,64.26555000000001,0 -130.9372,64.20999999999999,0 -130.8672,64.18777,0 -130.9478,64.13721,0 -130.8536,64.07249,0 -130.8639,64.04304999999999,0 -130.7514,64.0461,0 -130.7786,64.00915999999999,0 -130.7556,63.98693,0 -130.5811,63.92416,0 -130.5222,63.93499,0 -130.3703,63.84388000000001,0 -130.1197,63.80805,0 -130.1039,63.76416,0 -130.2342,63.73915,0 -130.2825,63.73777,0 -130.3211,63.69582000000001,0 -130.252,63.66666000000001,0 -130.1097,63.70361,0 -130.0825,63.68833,0 -130.1108,63.66443,0 -130.093,63.63055,0 -129.9486,63.61361000000001,0 -129.958,63.58526,0 -129.8981,63.57222,0 -129.8195,63.48333000000001,0 -129.8972,63.45221,0 -129.9192,63.37276,0 -130.1319,63.31555,0 -130.1511,63.26582000000001,0 -130.0445,63.26999,0 -129.9786,63.20361,0 -129.9003,63.19721,0 -129.8353,63.11472000000001,0 -129.8472,63.08694,0 -129.6372,63.08027,0 -129.5989,63.05194000000001,0 -129.7058,62.95222,0 -129.7261,62.91416,0 -129.7025,62.89222000000002,0 -129.7739,62.87443,0 -129.6308,62.76083,0 -129.6253,62.70499,0 -129.517,62.67249000000001,0 -129.5239,62.64333,0 -129.4878,62.61889,0 -129.5678,62.57444000000001,0 -129.4178,62.58499,0 -129.1722,62.48166,0 -129.3083,62.42138000000001,0 -129.2158,62.37749000000001,0 -129.3105,62.32639,0 -129.2822,62.26721,0 -129.1992,62.21694,0 -129.2939,62.15611,0 -129.1228,62.11667,0 -128.9842,62.13611,0 -128.7653,62.05833,0 -128.6775,62.12526,0 -128.547,62.12222,0 -128.3753,62.02722000000001,0 -128.3881,61.99387999999999,0 -128.2686,61.94055,0 -128.2136,61.85055,0 -128.0806,61.84916,0 -127.9728,61.68860000000001,0 -127.5503,61.51138000000001,0 -127.3792,61.50721000000001,0 -127.2883,61.51916000000001,0 -127.1417,61.46666,0 -127.125,61.4011,0 -127.0644,61.37249,0 -127.0108,61.13416,0 -127.1019,61.07083,0 -127.0558,61.03999000000002,0 -126.9353,61.05916,0 -126.9394,61.00249,0 -126.8989,60.94165,0 -126.9253,60.86221,0 -126.8797,60.82999,0 -126.8869,60.78249000000001,0 -126.84,60.75722,0 -126.7764,60.78527,0 -126.6722,60.74347000000001,0 -126.5203,60.81442000000001,0 -126.3945,60.77668000000001,0 -126.2728,60.78532,0 -126.2228,60.80284,0 -126.2395,60.8567,0 -126.1709,60.86586000000001,0 -126.1075,60.85894,0 -126.1072,60.81701,0 -126.0092,60.80341,0 -125.957,60.87723,0 -125.8872,60.89694,0 -125.712,60.82726000000001,0 -125.3492,60.7834,0 -125.1747,60.85054,0 -124.8717,60.85804,0 -124.8778,60.91721,0 -124.813,60.97249000000002,0 -124.5919,60.96054000000001,0 -124.4769,60.79055000000001,0 -124.6192,60.70972,0 -124.6142,60.66859,0 -124.4594,60.56972,0 -124.4183,60.47915,0 -124.2064,60.45722,0 -124.1953,60.32888,0 -123.9743,60.09237000000001,0 -123.8157,60.00000000000001,0 -120,60.00000000000001,0 -120,57.26251,0 -120.2163,57.45917,0 -120.3444,57.478,0 -120.8415,57.23171000000001,0 -121.2803,57.24454,0 -121.7099,57.02437000000001,0 -121.8406,56.99683,0 -122.0286,57.01699,0 -122.1153,56.9519,0 -122.2026,56.93232,0 -123.4153,57.14465000000001,0 -123.4541,57.3213,0 -123.515,57.36665,0 -123.5888,57.33548,0 -123.7545,57.12799,0 -123.944,57.12243,0 -124.0969,57.03444,0 -124.1216,56.86174000000001,0 -123.5964,55.89894000000001,0 -122.8612,55.42631000000001,0 -121.6533,54.53355,0 -121.2157,54.48102000000001,0 -120.9006,54.20094000000001,0 -120.0003,53.83022,0 -119.9783,53.78889000000002,0 -119.9047,53.7786,0 -119.9211,53.71833000000002,0 -119.8103,53.70777,0 -119.7353,53.63499000000001,0 -119.7686,53.60332,0 -119.9322,53.60722,0 -119.8828,53.56027,0 -119.9025,53.52027,0 -119.8153,53.49972,0 -119.7381,53.395,0 -119.6369,53.35166,0 -119.5989,53.37887,0 -119.3858,53.36166,0 -119.3528,53.27916,0 -119.2655,53.19666,0 -119.0378,53.13583,0 -119.0136,53.14472000000001,0 -119.0122,53.22305,0 -118.9597,53.23333,0 -118.755,53.12083,0 -118.7811,53.05583,0 -118.7275,53.06138,0 -118.6633,53.01554000000002,0 -118.6797,52.97054,0 -118.625,52.88416,0 -118.4914,52.89777000000001,0 -118.4156,52.83638000000001,0 -118.4289,52.78611000000001,0 -118.3461,52.74333,0 -118.305,52.67471,0 -118.35,52.61805,0 -118.2817,52.56527,0 -118.2869,52.52777,0 -118.2083,52.48082,0 -118.2597,52.44221,0 -118.2276,52.37917000000001,0 -118.2918,52.33801,0 -118.3567,52.36639000000001,0 -118.4936,52.31367000000001,0 -118.5636,52.265,0 -118.5869,52.19099,0 -118.6751,52.15347,0 -118.7623,52.17984,0 -118.6163,52.06324,0 -118.1043,51.85538,0 -118.0445,51.7621,0 -118.0663,51.6597,0 -117.975,51.65362,0 -117.9355,51.61814,0 -117.8381,51.65870000000001,0 -117.8179,51.63131000000001,0 -117.8462,51.58163,0 -117.8006,51.56642,0 -117.8513,51.51675000000001,0 -117.7393,51.42244,0 -117.4224,51.40926000000001,0 -117.1684,51.02501,0 -117.137,51.01789000000001,0 -117.1659,50.97937000000001,0 -117.0382,50.95707,0 -117.0858,50.8648,0 -117.0432,50.85568000000001,0 -117.0392,50.81512,0 -116.9631,50.81107000000001,0 -116.915,50.77051,0 -116.8136,50.78572,0 -116.8288,50.75225,0 -116.7811,50.69954,0 -116.5839,50.64782,0 -116.6559,50.60120000000001,0 -116.6894,50.48155,0 -116.7228,50.46025,0 -116.6392,50.40956000000001,0 -116.6179,50.31628,0 -116.5408,50.31831,0 -116.5616,50.21387,0 -116.4521,50.15508000000001,0 -116.4993,50.06382000000001,0 -116.4435,49.98169000000002,0 -116.4709,49.89754,0 -116.7112,49.88435000000001,0 -116.6975,49.85698,0 -116.877,49.79817000000001,0 -116.8967,49.64001,0 -116.8359,49.59236,0 -116.7472,49.33989,0 -116.6671,49.28718,0 -116.5611,49.11279,0 -116.5332,49.00125000000001,0 -116.0483,48.99972000000001,0 -116.0492,47.99333,0 -115.7208,47.70805000000001,0 -115.6742,47.60999,0 -115.7375,47.55583,0 -115.6347,47.48444,0 -115.725,47.43721,0 -115.5803,47.38527000000001,0 -115.4958,47.3011,0 -115.3183,47.2561,0 -115.045,46.9886,0 -114.9156,46.92389000000001,0 -114.9064,46.84138000000001,0 -114.7655,46.77638,0 -114.765,46.71471,0 -114.6694,46.75111,0 -114.6178,46.71749000000001,0 -114.6367,46.67388,0 -114.5764,46.64444,0 -114.325,46.66916000000001,0 -114.3367,46.53582999999999,0 -114.4003,46.50555,0 -114.3708,46.45361,0 -114.4561,46.26999,0 -114.4319,46.19055000000001,0 -114.5045,46.15972,0 -114.45,46.10917,0 -114.4853,46.04971,0 -114.4742,46.00193,0 -114.4022,45.98804,0 -114.4164,45.92888000000001,0 -114.3819,45.89249,0 -114.4961,45.84470000000001,0 -114.5547,45.77943000000001,0 -114.4908,45.71027,0 -114.545,45.62778,0 -114.5395,45.56702000000001,0 -114.6612,45.46328000000001,0 -114.781,45.4964,0 -114.798,45.43404000000001,0 -114.7511,45.40534,0 -115.1775,45.58222000000001,0 -115.4781,45.3886,0 -115.7964,45.46944,0 -116.1983,45.40526000000001,0 -116.2811,45.42443000000001,0 -116.2992,45.4586,0 -116.2583,45.61999000000001,0 -116.2992,45.69137,0 -116.2728,45.86054000000002,0 -116.2922,45.89305,0 -116.6122,46.04194,0 -116.6217,46.00667000000001,0 -116.7175,45.97332000000001,0 -116.7283,45.89332,0 -116.7793,45.86157000000001,0 -116.5461,45.76222000000001,0 -116.4533,45.60694,0 -116.6503,45.35638,0 -116.7114,45.17027,0 -116.8372,45.02555000000001,0 -116.8378,44.92443,0 -116.8958,44.8311,0 -117.0278,44.74638000000001,0 -117.1336,44.54388,0 -117.2122,44.48249,0 -117.1985,44.30091,0 -117.482,44.29872,0 -117.4845,44.37986,0 -117.5899,44.44006,0 -117.9629,44.4388,0 -118.0106,44.42295,0 -118.1505,44.26134,0 -118.2288,44.24651,0 -118.2191,43.09735,0 -117.9707,43.09735,0 -117.9707,42.81335,0 -118.2076,42.81335,0 -118.2066,42.59258,0 -117.0169,42.58601000000001,0 -116.9849,42,0 -114.0426,41.99549,0 -114.0403,37.00311,0 -114.0419,36.19972,0 -114.1208,36.05833,0 -114.2372,36.02583,0 -114.3575,36.1461,0 -114.4514,36.12777,0 -114.5506,36.16194,0 -114.7264,36.08277,0 -114.7405,35.98611,0 -114.6603,35.88194,0 -114.6917,35.81499,0 -114.6903,35.72499,0 -114.6439,35.62665,0 -114.6683,35.51027000000001,0 -114.5839,35.3411,0 -114.5686,35.22332,0 -114.5795,35.14415,0 -114.6364,35.12526,0 -114.6075,35.08221,0 -114.6333,35.04749,0 -114.6219,34.99214,0 -114.6222,34.88499,0 -114.4561,34.6986,0 -114.3697,34.52917,0 -114.3733,34.47304,0 -114.1672,34.3575,0 -114.1147,34.28249,0 -114.3903,34.10972,0 -114.4228,34.02638,0 -114.5225,33.95027,0 -114.4886,33.89249,0 -114.5161,33.825,0 -114.4839,33.72193,0 -114.5225,33.68415,0 -114.5264,33.56166,0 -114.6367,33.41999,0 -114.7261,33.40804,0 -114.6992,33.36055,0 -114.7286,33.30749,0 -114.6678,33.21554,0 -114.6961,33.08693,0 -114.6608,33.04193,0 -114.5089,33.0225,0 -114.4644,32.97165,0 -114.4581,32.86027,0 -114.5269,32.80082,0 -114.5417,32.7272,0 -114.69,32.74276,0 -114.7191,32.71846,0 -114.7956,32.60999,0 -114.8098,32.50699,0 -114.9367,32.47305,0 -114.9642,32.36861,0 -115.0415,32.25468,0 -114.9989,32.13611,0 -115.0172,31.95443,0 -112.1676,28.00019,0 -112.7233,27.99974,0 -114.1616,28.00056,0 -114.2077,28.00027,0 -116.9047,28.00672,0 -116.9047,24.05259,0 -112.5009,24.04926,0 -112.501,19.04926,0 -112.5011,14.04925,0 -112.5012,9.049256,0 -112.5013,4.049253,0 -112.5014,-0.9507425000000002,0 -112.5015,-5.950747,0 -112.5016,-8.550748000000001,0 -112.5017,-13.55074,0 -112.5018,-18.55075,0 -112.5019,-23.55074,0 -112.502,-25.75075,0 -112.5021,-30.75074,0 -112.5022,-35.75074,0 -112.5023,-40.75074,0 -112.5024,-45.75074000000002,0 -112.5025,-50.75074000000001,0 -112.5026,-55.75074,0 -112.5027,-59.55075,0 -112.5028,-64.55074000000001,0 -112.5029,-69.55074000000001,0 -112.503,-74.55074000000001,0 -112.503,-75.55074999999999,0 -112.5031,-80.55074999999999,0 -112.5033,-85.55074999999999,0 -112.5033,-89.90000000000001,0 -113.4411,-89.90000000000001,0 -114.3788,-89.90000000000001,0 -115.3166,-89.90000000000001,0 -116.2543,-89.90000000000001,0 -117.1921,-89.90000000000001,0 -118.1298,-89.90000000000001,0 -119.0676,-89.90000000000001,0 -120.0053,-89.90000000000001,0 -120.943,-89.90000000000001,0 -121.8808,-89.90000000000001,0 -122.8185,-89.90000000000001,0 -123.7563,-89.90000000000001,0 -124.694,-89.90000000000001,0 -125.6318,-89.90000000000001,0 -126.5695,-89.90000000000001,0 -127.5073,-89.90000000000001,0 -127.5073,-84.89999,0 -127.5073,-79.90000000000001,0 -127.5073,-75.8,0 -127.5073,-70.8,0 -127.5073,-65.8,0 -127.5074,-60.8,0 -127.5074,-59.90001,0 -127.5074,-54.9,0 -127.5074,-49.90001,0 -127.5074,-44.9,0 -127.5074,-39.90000000000001,0 -127.5075,-34.9,0 -127.5075,-29.9,0 -127.5075,-26,0 -126,-26,0 -121,-26,0 -121,-22.5,0 -122.508,-22.5,0 -127.508,-22.5,0 -127.5079,-17.5,0 -127.5079,-12.5,0 -127.5079,-7.499998000000002,0 -127.5079,-2.500003,0 -127.5079,0.01058346,0 -127.5079,5.010579,0 -127.5079,10.01058,0 -127.5079,15.01058,0 -127.5079,18.21058,0 -127.5078,23.21058,0 -127.5078,28.21058,0 -127.5078,33.21058,0 -127.5078,38.21058,0 -127.5078,43.21058,0 -127.5078,48.21058000000001,0 -127.5078,49.81058,0 -131.0228,51.91016000000001,0 -132.1788,52.75157,0 -132.5012,53.02680000000001,0 -132.9926,53.51827999999999,0 -133.1971,53.93898,0 -133.0791,54.26532,0 -130.6491,54.70139000000001,0 -130.5897,54.79371,0 -130.3597,54.90805,0 -130.2311,55.035,0 -130.1014,55.22776,0 -129.9905,55.28166000000001,0 -130.1439,55.54139000000001,0 -130.1225,55.65499,0 -130.1747,55.75443000000001,0 -130.0151,55.90919000000001,0 -130.0147,56.02583000000001,0 -130.0886,56.11804000000001,0 -130.2292,56.09027,0 -130.4142,56.12887,0 -130.4619,56.23527,0 -130.6272,56.25861,0 -130.7744,56.3661,0 -131.055,56.39804,0 -131.5578,56.60221,0 -131.8242,56.59693,0 -131.8269,56.67916,0 -131.8845,56.74471000000001,0 -131.8614,56.79972,0 -132.1031,56.86666,0 -132.0275,57.03638,0 -132.3369,57.08832,0 -132.2267,57.20471,0 -132.8817,57.86361000000001,0 -133.0708,58.01222,0 -133.137,58.13582,0 -133.3611,58.28054,0 -133.4303,58.35999,0 -133.3879,58.41206,0 -133.43,58.45917,0 -133.6833,58.59554,0 -133.8269,58.7261,0 -134.2455,58.85695000000001,0 -134.3322,58.92972,0 -134.3114,58.96444000000001,0 -134.3953,58.98305,0 -134.3794,59.04916,0 -134.4614,59.12665,0 -134.5664,59.13055000000001,0 -134.6717,59.20027000000001,0 -134.688,59.24333,0 -134.9519,59.27999,0 -135.0161,59.34777,0 -134.9892,59.38999,0 -135.0917,59.42694,0 -135.0211,59.4711,0 -135.0145,59.5675,0 -135.1775,59.63693,0 -135.235,59.70305,0 -135.4736,59.80194,0 -135.9492,59.66916,0 -136.3439,59.60277,0 -136.2392,59.56138000000001,0 -136.2339,59.52583,0 -136.3716,59.45249,0 -136.4636,59.46970000000001,0 -136.4628,59.28944,0 -136.5839,59.16332,0 -136.8089,59.16526,0 -137.2961,58.98999,0 -137.445,58.90749,0 -137.5075,58.92556,0 -137.4797,58.99805,0 -137.5908,59.23860000000001,0 -137.9578,59.43166,0 -138.6158,59.77417000000001,0 -138.6903,59.90693,0 -139.0478,59.99749,0 -139.1889,60.08889,0 -139.0681,60.35222,0 -139.6767,60.34055,0 -139.9794,60.18776,0 -140.4508,60.30972000000001,0 -140.5214,60.22222,0 -140.9955,60.30721,0 -140.9997,65.30721,0 -141.0033,69.6567,0 -140.378,69.60579,0 -139.9315,69.62904000000002,0 -139.3828,69.55929,0 -139.1271,69.66159,0 -138.8806,69.61044,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-126.1624,71.89608,0 -127.5151,70.49976,0 -127.5151,74.89999,0 -127.5151,79.90000000000001,0 -127.5151,84.90001,0 -127.5151,89.90000000000001,0 -126.5769,89.90000000000001,0 -125.6388,89.90000000000001,0 -124.7006,89.90000000000001,0 -123.7625,89.90000000000001,0 -122.8243,89.90000000000001,0 -121.8862,89.90000000000001,0 -120.948,89.90000000000001,0 -120.0099,89.90000000000001,0 -119.0717,89.90000000000001,0 -118.1335,89.90000000000001,0 -117.1954,89.90000000000001,0 -116.2572,89.90000000000001,0 -115.3191,89.90000000000001,0 -114.3809,89.90000000000001,0 -113.4428,89.90000000000001,0 -112.5046,89.90000000000001,0 -112.5046,84.90001,0 -112.5046,79.90000000000001,0 -112.5046,78.6159,0 -114.0755,78.39772000000001,0 -116.999,77.65592,0 -118.9626,77.43774000000001,0 -120.5771,76.91412,0 -121.9734,76.60868000000002,0 -123.1516,76.30323,0 -123.1952,75.95415,0 -124.8534,74.47057,0 -125.0715,74.16511,0 -124.8097,73.59784999999999,0 -126.1624,71.89608,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>			
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -07:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-7);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0700</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-97.51546,89.90000000000001,0 -97.51546,84.90001,0 -97.51546,80.63566,0 -102.0002,80.63531,0 -102.0004,75.63531,0 -102.0005,73.3723,0 -101.191,73.22286,0 -100.9536,73.20439,0 -100.5149,73.23768,0 -98.38002,72.95116,0 -98.29798,72.94485,0 -97.00784,72.74532000000001,0 -96.89134,72.69624,0 -96.71005,72.71178000000001,0 -96.41336,72.64267,0 -95.11967,72.46542,0 -93.87927000000001,72.25537,0 -93.74417000000003,72.01089,0 -93.19248000000002,71.65391,0 -92.93285,71.44838,0 -92.78141000000001,71.28612,0 -92.71651,71.11304,0 -92.57588,70.92914,0 -92.41362,70.81014,0 -92.1648,70.74524,0 -91.90519000000001,70.62624,0 -90.85590000000001,69.76086000000001,0 -90.1852,69.51204,0 -89.60105,69.45796,0 -89.08181999999999,69.33895,0 -89.01796000000002,69.28883,0 -89.01671000000002,69.25249000000001,0 -88.98090000000001,66.89502000000002,0 -93.98089000000002,66.90748000000001,0 -96.05273000000001,66.91264,0 -96.2137,66.9131,0 -101.2137,66.91991,0 -102.0007,66.92099,0 -102.0008,61.92099,0 -102.0009,59.99936,0 -106.0682,59.99960999999999,0 -110.0009,59.99528,0 -110.0003,54.99528000000001,0 -109.9998,49.99528000000001,0 -109.9997,49.0006,0 -104.9997,49.00018,0 -104.0625,49.0001,0 -104.0479,48.00007000000001,0 -103.9318,47.96657,0 -103.915,47.91,0 -103.9551,47.82047,0 -103.817,47.81991000000001,0 -103.8168,47.71054,0 -103.6829,47.71054,0 -103.6829,47.58421,0 -103.5878,47.58411,0 -103.5878,47.52712,0 -103.4097,47.53999,0 -103.3961,47.605,0 -103.3642,47.61639000000002,0 -103.0439,47.59749,0 -103,47.67471,0 -102.8392,47.60444,0 -102.6803,47.58082000000001,0 -102.6592,47.53583,0 -102.4334,47.53006,0 -102.3872,47.59027,0 -102.1996,47.57961,0 -102.0614,47.57729,0 -102.0163,47.53939,0 -101.8144,47.5023,0 -101.4348,47.56065000000001,0 -101.3807,47.52191000000001,0 -101.4371,47.46869000000001,0 -101.3423,47.2963,0 -101.1977,47.25529000000001,0 -101.1344,47.29811999999999,0 -100.9885,47.25916,0 -100.9889,47.19390000000001,0 -100.8747,47.04248,0 -100.93,46.98355000000001,0 -100.9013,46.90117,0 -100.8188,46.8093,0 -100.8355,46.75002,0 -100.7838,46.73688,0 -100.7804,46.68494,0 -100.6479,46.6602,0 -100.6235,46.60821,0 -100.5627,46.58238,0 -100.5988,46.47136,0 -100.5543,46.36341000000001,0 -100.5843,46.27890000000001,0 -100.5613,46.24074000000001,0 -100.628,46.11914,0 -100.5153,45.92761,0 -100.3773,45.86584,0 -100.3715,45.79243000000001,0 -100.3069,45.72111,0 -100.3307,45.66776000000001,0 -100.422,45.6551,0 -100.4905,45.55487,0 -100.4568,45.51511000000001,0 -100.3704,45.50183000000001,0 -100.2794,45.38273,0 -100.3219,45.31851,0 -100.2706,45.25081,0 -100.3317,45.10395000000001,0 -100.2805,45.03674,0 -100.4246,45.01186,0 -100.4071,44.85565,0 -100.5412,44.76557,0 -100.5997,44.76569,0 -100.7014,44.83079,0 -100.7303,44.80638,0 -100.7185,44.77095000000001,0 -100.6327,44.73686,0 -100.6343,44.57531,0 -100.5215,44.54869,0 -100.5922,44.4953,0 -100.5861,44.4632,0 -100.3989,44.45416,0 -100.3857,44.40596,0 -100.7452,43.9761,0 -100.7452,43.72055,0 -100.5873,43.76701,0 -100.4769,43.72082,0 -100.232,43.71497,0 -100.1977,42.99109,0 -100.9094,42.98817,0 -100.9094,42.85428,0 -100.8633,42.85428,0 -100.8633,42.80814,0 -100.8056,42.80621,0 -100.8056,42.74853,0 -100.8979,42.74853,0 -100.8979,42.66779,0 -100.8267,42.66779,0 -100.8267,42.48515,0 -100.8729,42.48515,0 -100.8729,42.29674,0 -100.6806,42.29481,0 -100.6806,42.08107,0 -100.8447,42.082,0 -100.8393,41.73868,0 -101.4083,41.73717,0 -101.4027,41.39172,0 -101.2625,41.39017,0 -101.2429,41.04119,0 -101.243,40.69778,0 -101.3406,40.69749,0 -101.3217,40.00183,0 -102.0511,39.99892,0 -102.049,39.5687,0 -101.4063,39.56715,0 -101.3867,39.12926,0 -101.4727,39.12796,0 -101.4782,38.69465,0 -101.5631,38.69329,0 -101.5204,37.73152,0 -102.0435,37.73439,0 -102.0368,36.98897,0 -102.9972,36.99850000000001,0 -102.9969,36.5,0 -103.0656,32,0 -104.9226,31.99982,0 -104.9016,30.81495,0 -104.9149,30.68164000000001,0 -104.9921,30.63846,0 -105.0022,30.68027,0 -105.2075,30.80166,0 -105.2614,30.79722,0 -105.3897,30.86222,0 -105.5672,31.00945,0 -105.5978,31.08,0 -105.7703,31.17611,0 -105.9489,31.36195,0 -106.2144,31.47528000000001,0 -106.3908,31.74472,0 -106.5229,31.78075000000001,0 -108.2086,31.78334,0 -108.2083,31.33305,0 -108.8319,31.33264,0 -108.8914,31.19222,0 -108.8361,31.15695,0 -108.7981,31.205,0 -108.735,30.6325,0 -108.6778,30.57583,0 -108.5581,29.99277,0 -108.6136,29.40111,0 -108.7078,29.40084,0 -108.6239,28.77111,0 -108.6895,28.69639000000001,0 -108.5656,28.28916,0 -108.6525,28.21222,0 -108.8808,28.29834,0 -108.977,28.30556,0 -109.022,28.27584,0 -109.0558,28.29917,0 -109.1464,28.17583,0 -108.9139,27.785,0 -108.8097,27.71167,0 -108.775,27.59945,0 -108.6553,27.52,0 -108.6675,27.40416,0 -108.6364,27.31917,0 -108.6653,27.15195,0 -108.6042,27.03805,0 -108.4713,26.96133,0 -108.405,27.03082,0 -108.3053,27.06138,0 -108.2489,27.04083,0 -108.2206,26.97277,0 -108.0356,26.94750000000001,0 -108.0039,26.81972,0 -107.8467,26.64,0 -107.7844,26.20028,0 -107.3664,26.11527,0 -107.2997,25.94334,0 -107.1517,25.77556,0 -107.0841,25.60609,0 -107.1194,25.20945,0 -107.0422,24.99416,0 -106.9489,24.84194,0 -106.885,24.77028,0 -106.8,24.75277,0 -106.7828,24.7025,0 -106.6508,24.58861,0 -106.5192,24.30194,0 -106.3961,24.28472,0 -106.2492,24.39027,0 -106.0022,24.21194,0 -105.9597,24.09889,0 -105.9081,24.05527,0 -105.8964,23.78945,0 -105.7181,23.47472,0 -105.6842,23.28722,0 -105.5289,23.14444,0 -105.4167,23.14722,0 -105.4021,23.06746,0 -105.3125,23.03472,0 -105.172,23.03972,0 -104.9147,22.92472,0 -104.88,22.78306,0 -104.9981,22.67889,0 -104.9981,22.54834,0 -104.7575,22.67666,0 -104.6606,22.62444,0 -104.6125,22.4725,0 -104.4914,22.41027,0 -104.345,22.45138,0 -104.3114,22.31921,0 -104.3296,22.26453,0 -104.4028,22.07638,0 -104.0936,21.78583,0 -104.1528,21.59805,0 -104.2072,21.54721,0 -103.9449,21.37497,0 -103.9614,21.28777,0 -104.0425,21.21139,0 -104.2277,21.17774,0 -104.21,20.97805,0 -104.275,20.86084,0 -104.2847,20.70916,0 -104.4672,20.82972,0 -104.535,20.91611,0 -104.625,20.9236,0 -104.7219,21.01278,0 -104.77,21.02055,0 -104.9489,20.92556,0 -105.0833,20.92528,0 -105.2669,20.69905,0 -105.2336,20.62889,0 -105.2503,20.56556,0 -105.352,20.51305,0 -105.5903,20.47805,0 -105.677,20.42417,0 -105.68,20.38639,0 -105.562,20.21917,0 -105.5422,20.07695,0 -105.4569,19.92222,0 -105.2144,19.6225,0 -105.1025,19.565,0 -105.0225,19.37195,0 -104.8967,19.28389,0 -104.797,19.28861,0 -104.8097,19.22084,0 -104.735,19.23,0 -104.6628,19.16805,0 -104.4483,19.09083,0 -104.3278,19.09527,0 -104.315,19.00805,0 -103.972,18.87722,0 -103.7147,18.6625,0 -103.6839,18.61138,0 -103.7047,18.58528,0 -103.5792,18.50083,0 -103.45,18.31361,0 -103.0294,18.19,0 -102.745,18.06582,0 -102.4875,18.02333,0 -102.181,17.92083,0 -102.0292,17.99195,0 -101.9214,17.96194,0 -101.7878,17.87611,0 -101.6331,17.66695,0 -101.5561,17.61777,0 -101.4978,17.62195,0 -101.4178,17.51861,0 -101.1044,17.35889,0 -101.0486,17.26694,0 -100.1864,16.95556,0 -100.0783,16.94166,0 -99.9389,16.88222,0 -99.90001,16.82555,0 -99.87807000000002,16.87028,0 -99.83778000000001,16.81445,0 -99.84972000000002,16.7875,0 -99.69222000000001,16.70834,0 -99.03695999999999,16.59694,0 -98.86473000000001,16.52444,0 -98.78223,16.55305,0 -98.53667,16.30695,0 -98.09807000000001,16.21416,0 -98.06444999999999,16.18389,0 -98.16724000000002,16.19695,0 -97.87000999999999,16.06195,0 -97.87112000000001,16.02139,0 -97.785,15.96861,0 -97.51684,15.94563,0 -97.51781,10.94564,0 -97.51877000000002,5.945633,0 -97.51974,0.9456385000000001,0 -97.52070999999999,-4.054366,0 -97.52168,-9.054360000000001,0 -97.52263000000001,-14.05436,0 -97.52332,-17.55437,0 -97.52416,-21.93729,0 -102.5242,-21.93729,0 -107.5242,-21.93729,0 -111.1275,-21.93729,0 -111.1033,-26.93723,0 -111.079,-31.93718000000001,0 -111.071,-33.59291,0 -106.0709,-33.59291,0 -101.071,-33.59291,0 -97.52641,-33.59291,0 -97.52773000000001,-38.59291,0 -97.52906000000002,-43.59291000000001,0 -97.53039000000003,-48.59291,0 -97.53172000000002,-53.59291,0 -97.53304,-58.59291000000001,0 -97.53437,-63.59291,0 -97.53570000000001,-68.5929,0 -97.53608,-70.01852,0 -97.53912000000001,-75.01852,0 -97.54217,-80.01852000000001,0 -97.54416999999999,-83.31852000000001,0 -97.54723000000001,-88.31852000000001,0 -97.54818000000002,-89.90000000000001,0 -98.54521,-89.90000000000001,0 -99.54222,-89.90000000000001,0 -100.5392,-89.90000000000001,0 -101.5362,-89.90000000000001,0 -102.5332,-89.90000000000001,0 -103.5303,-89.90000000000001,0 -104.5273,-89.90000000000001,0 -105.5243,-89.90000000000001,0 -106.5213,-89.90000000000001,0 -107.5183,-89.90000000000001,0 -108.5153,-89.90000000000001,0 -109.5123,-89.90000000000001,0 -110.5093,-89.90000000000001,0 -111.5063,-89.90000000000001,0 -112.5033,-89.90000000000001,0 -112.5033,-85.55074999999999,0 -112.5031,-80.55074999999999,0 -112.503,-75.55074999999999,0 -112.503,-74.55074000000001,0 -112.5029,-69.55074000000001,0 -112.5028,-64.55074000000001,0 -112.5027,-59.55075,0 -112.5026,-55.75074,0 -112.5025,-50.75074000000001,0 -112.5024,-45.75074000000002,0 -112.5023,-40.75074,0 -112.5022,-35.75074,0 -112.5021,-30.75074,0 -112.502,-25.75075,0 -112.5019,-23.55074,0 -112.5018,-18.55075,0 -112.5017,-13.55074,0 -112.5016,-8.550748000000001,0 -112.5015,-5.950747,0 -112.5014,-0.9507425000000002,0 -112.5013,4.049253,0 -112.5012,9.049256,0 -112.5011,14.04925,0 -112.501,19.04926,0 -112.5009,24.04926,0 -116.9047,24.05259,0 -116.9047,28.00672,0 -114.2077,28.00027,0 -114.1616,28.00056,0 -112.7233,27.99974,0 -112.1676,28.00019,0 -115.0172,31.95443,0 -114.9989,32.13611,0 -115.0415,32.25468,0 -114.9642,32.36861,0 -114.9367,32.47305,0 -114.8098,32.50699,0 -114.7956,32.60999,0 -114.7191,32.71846,0 -114.69,32.74276,0 -114.5417,32.7272,0 -114.5269,32.80082,0 -114.4581,32.86027,0 -114.4644,32.97165,0 -114.5089,33.0225,0 -114.6608,33.04193,0 -114.6961,33.08693,0 -114.6678,33.21554,0 -114.7286,33.30749,0 -114.6992,33.36055,0 -114.7261,33.40804,0 -114.6367,33.41999,0 -114.5264,33.56166,0 -114.5225,33.68415,0 -114.4839,33.72193,0 -114.5161,33.825,0 -114.4886,33.89249,0 -114.5225,33.95027,0 -114.4228,34.02638,0 -114.3903,34.10972,0 -114.1147,34.28249,0 -114.1672,34.3575,0 -114.3733,34.47304,0 -114.3697,34.52917,0 -114.4561,34.6986,0 -114.6222,34.88499,0 -114.6219,34.99214,0 -114.6333,35.04749,0 -114.6075,35.08221,0 -114.6364,35.12526,0 -114.5795,35.14415,0 -114.5686,35.22332,0 -114.5839,35.3411,0 -114.6683,35.51027000000001,0 -114.6439,35.62665,0 -114.6903,35.72499,0 -114.6917,35.81499,0 -114.6603,35.88194,0 -114.7405,35.98611,0 -114.7264,36.08277,0 -114.5506,36.16194,0 -114.4514,36.12777,0 -114.3575,36.1461,0 -114.2372,36.02583,0 -114.1208,36.05833,0 -114.0419,36.19972,0 -114.0403,37.00311,0 -114.0426,41.99549,0 -116.9849,42,0 -117.0169,42.58601000000001,0 -118.2066,42.59258,0 -118.2076,42.81335,0 -117.9707,42.81335,0 -117.9707,43.09735,0 -118.2191,43.09735,0 -118.2288,44.24651,0 -118.1505,44.26134,0 -118.0106,44.42295,0 -117.9629,44.4388,0 -117.5899,44.44006,0 -117.4845,44.37986,0 -117.482,44.29872,0 -117.1985,44.30091,0 -117.2122,44.48249,0 -117.1336,44.54388,0 -117.0278,44.74638000000001,0 -116.8958,44.8311,0 -116.8378,44.92443,0 -116.8372,45.02555000000001,0 -116.7114,45.17027,0 -116.6503,45.35638,0 -116.4533,45.60694,0 -116.5461,45.76222000000001,0 -116.7793,45.86157000000001,0 -116.7283,45.89332,0 -116.7175,45.97332000000001,0 -116.6217,46.00667000000001,0 -116.6122,46.04194,0 -116.2922,45.89305,0 -116.2728,45.86054000000002,0 -116.2992,45.69137,0 -116.2583,45.61999000000001,0 -116.2992,45.4586,0 -116.2811,45.42443000000001,0 -116.1983,45.40526000000001,0 -115.7964,45.46944,0 -115.4781,45.3886,0 -115.1775,45.58222000000001,0 -114.7511,45.40534,0 -114.798,45.43404000000001,0 -114.781,45.4964,0 -114.6612,45.46328000000001,0 -114.5395,45.56702000000001,0 -114.545,45.62778,0 -114.4908,45.71027,0 -114.5547,45.77943000000001,0 -114.4961,45.84470000000001,0 -114.3819,45.89249,0 -114.4164,45.92888000000001,0 -114.4022,45.98804,0 -114.4742,46.00193,0 -114.4853,46.04971,0 -114.45,46.10917,0 -114.5045,46.15972,0 -114.4319,46.19055000000001,0 -114.4561,46.26999,0 -114.3708,46.45361,0 -114.4003,46.50555,0 -114.3367,46.53582999999999,0 -114.325,46.66916000000001,0 -114.5764,46.64444,0 -114.6367,46.67388,0 -114.6178,46.71749000000001,0 -114.6694,46.75111,0 -114.765,46.71471,0 -114.7655,46.77638,0 -114.9064,46.84138000000001,0 -114.9156,46.92389000000001,0 -115.045,46.9886,0 -115.3183,47.2561,0 -115.4958,47.3011,0 -115.5803,47.38527000000001,0 -115.725,47.43721,0 -115.6347,47.48444,0 -115.7375,47.55583,0 -115.6742,47.60999,0 -115.7208,47.70805000000001,0 -116.0492,47.99333,0 -116.0483,48.99972000000001,0 -116.5332,49.00125000000001,0 -116.5611,49.11279,0 -116.6671,49.28718,0 -116.7472,49.33989,0 -116.8359,49.59236,0 -116.8967,49.64001,0 -116.877,49.79817000000001,0 -116.6975,49.85698,0 -116.7112,49.88435000000001,0 -116.4709,49.89754,0 -116.4435,49.98169000000002,0 -116.4993,50.06382000000001,0 -116.4521,50.15508000000001,0 -116.5616,50.21387,0 -116.5408,50.31831,0 -116.6179,50.31628,0 -116.6392,50.40956000000001,0 -116.7228,50.46025,0 -116.6894,50.48155,0 -116.6559,50.60120000000001,0 -116.5839,50.64782,0 -116.7811,50.69954,0 -116.8288,50.75225,0 -116.8136,50.78572,0 -116.915,50.77051,0 -116.9631,50.81107000000001,0 -117.0392,50.81512,0 -117.0432,50.85568000000001,0 -117.0858,50.8648,0 -117.0382,50.95707,0 -117.1659,50.97937000000001,0 -117.137,51.01789000000001,0 -117.1684,51.02501,0 -117.4224,51.40926000000001,0 -117.7393,51.42244,0 -117.8513,51.51675000000001,0 -117.8006,51.56642,0 -117.8462,51.58163,0 -117.8179,51.63131000000001,0 -117.8381,51.65870000000001,0 -117.9355,51.61814,0 -117.975,51.65362,0 -118.0663,51.6597,0 -118.0445,51.7621,0 -118.1043,51.85538,0 -118.6163,52.06324,0 -118.7623,52.17984,0 -118.6751,52.15347,0 -118.5869,52.19099,0 -118.5636,52.265,0 -118.4936,52.31367000000001,0 -118.3567,52.36639000000001,0 -118.2918,52.33801,0 -118.2276,52.37917000000001,0 -118.2597,52.44221,0 -118.2083,52.48082,0 -118.2869,52.52777,0 -118.2817,52.56527,0 -118.35,52.61805,0 -118.305,52.67471,0 -118.3461,52.74333,0 -118.4289,52.78611000000001,0 -118.4156,52.83638000000001,0 -118.4914,52.89777000000001,0 -118.625,52.88416,0 -118.6797,52.97054,0 -118.6633,53.01554000000002,0 -118.7275,53.06138,0 -118.7811,53.05583,0 -118.755,53.12083,0 -118.9597,53.23333,0 -119.0122,53.22305,0 -119.0136,53.14472000000001,0 -119.0378,53.13583,0 -119.2655,53.19666,0 -119.3528,53.27916,0 -119.3858,53.36166,0 -119.5989,53.37887,0 -119.6369,53.35166,0 -119.7381,53.395,0 -119.8153,53.49972,0 -119.9025,53.52027,0 -119.8828,53.56027,0 -119.9322,53.60722,0 -119.7686,53.60332,0 -119.7353,53.63499000000001,0 -119.8103,53.70777,0 -119.9211,53.71833000000002,0 -119.9047,53.7786,0 -119.9783,53.78889000000002,0 -120.0003,53.83022,0 -120.9006,54.20094000000001,0 -121.2157,54.48102000000001,0 -121.6533,54.53355,0 -122.8612,55.42631000000001,0 -123.5964,55.89894000000001,0 -124.1216,56.86174000000001,0 -124.0969,57.03444,0 -123.944,57.12243,0 -123.7545,57.12799,0 -123.5888,57.33548,0 -123.515,57.36665,0 -123.4541,57.3213,0 -123.4153,57.14465000000001,0 -122.2026,56.93232,0 -122.1153,56.9519,0 -122.0286,57.01699,0 -121.8406,56.99683,0 -121.7099,57.02437000000001,0 -121.2803,57.24454,0 -120.8415,57.23171000000001,0 -120.3444,57.478,0 -120.2163,57.45917,0 -120,57.26251,0 -120,60.00000000000001,0 -123.8157,60.00000000000001,0 -123.9743,60.09237000000001,0 -124.1953,60.32888,0 -124.2064,60.45722,0 -124.4183,60.47915,0 -124.4594,60.56972,0 -124.6142,60.66859,0 -124.6192,60.70972,0 -124.4769,60.79055000000001,0 -124.5919,60.96054000000001,0 -124.813,60.97249000000002,0 -124.8778,60.91721,0 -124.8717,60.85804,0 -125.1747,60.85054,0 -125.3492,60.7834,0 -125.712,60.82726000000001,0 -125.8872,60.89694,0 -125.957,60.87723,0 -126.0092,60.80341,0 -126.1072,60.81701,0 -126.1075,60.85894,0 -126.1709,60.86586000000001,0 -126.2395,60.8567,0 -126.2228,60.80284,0 -126.2728,60.78532,0 -126.3945,60.77668000000001,0 -126.5203,60.81442000000001,0 -126.6722,60.74347000000001,0 -126.7764,60.78527,0 -126.84,60.75722,0 -126.8869,60.78249000000001,0 -126.8797,60.82999,0 -126.9253,60.86221,0 -126.8989,60.94165,0 -126.9394,61.00249,0 -126.9353,61.05916,0 -127.0558,61.03999000000002,0 -127.1019,61.07083,0 -127.0108,61.13416,0 -127.0644,61.37249,0 -127.125,61.4011,0 -127.1417,61.46666,0 -127.2883,61.51916000000001,0 -127.3792,61.50721000000001,0 -127.5503,61.51138000000001,0 -127.9728,61.68860000000001,0 -128.0806,61.84916,0 -128.2136,61.85055,0 -128.2686,61.94055,0 -128.3881,61.99387999999999,0 -128.3753,62.02722000000001,0 -128.547,62.12222,0 -128.6775,62.12526,0 -128.7653,62.05833,0 -128.9842,62.13611,0 -129.1228,62.11667,0 -129.2939,62.15611,0 -129.1992,62.21694,0 -129.2822,62.26721,0 -129.3105,62.32639,0 -129.2158,62.37749000000001,0 -129.3083,62.42138000000001,0 -129.1722,62.48166,0 -129.4178,62.58499,0 -129.5678,62.57444000000001,0 -129.4878,62.61889,0 -129.5239,62.64333,0 -129.517,62.67249000000001,0 -129.6253,62.70499,0 -129.6308,62.76083,0 -129.7739,62.87443,0 -129.7025,62.89222000000002,0 -129.7261,62.91416,0 -129.7058,62.95222,0 -129.5989,63.05194000000001,0 -129.6372,63.08027,0 -129.8472,63.08694,0 -129.8353,63.11472000000001,0 -129.9003,63.19721,0 -129.9786,63.20361,0 -130.0445,63.26999,0 -130.1511,63.26582000000001,0 -130.1319,63.31555,0 -129.9192,63.37276,0 -129.8972,63.45221,0 -129.8195,63.48333000000001,0 -129.8981,63.57222,0 -129.958,63.58526,0 -129.9486,63.61361000000001,0 -130.093,63.63055,0 -130.1108,63.66443,0 -130.0825,63.68833,0 -130.1097,63.70361,0 -130.252,63.66666000000001,0 -130.3211,63.69582000000001,0 -130.2825,63.73777,0 -130.2342,63.73915,0 -130.1039,63.76416,0 -130.1197,63.80805,0 -130.3703,63.84388000000001,0 -130.5222,63.93499,0 -130.5811,63.92416,0 -130.7556,63.98693,0 -130.7786,64.00915999999999,0 -130.7514,64.0461,0 -130.8639,64.04304999999999,0 -130.8536,64.07249,0 -130.9478,64.13721,0 -130.8672,64.18777,0 -130.9372,64.20999999999999,0 -130.9797,64.26555000000001,0 -131.0605,64.27943000000001,0 -131.002,64.33028,0 -131.1417,64.42443,0 -131.3886,64.46720999999999,0 -131.4275,64.41638,0 -131.593,64.36859,0 -131.8181,64.37831,0 -131.8039,64.43803,0 -131.6878,64.4986,0 -131.6942,64.53498999999999,0 -131.9158,64.58249000000001,0 -132.0464,64.69553000000001,0 -132.1636,64.70166,0 -132.3689,64.77831999999999,0 -132.5706,64.77249999999999,0 -132.6153,64.81219,0 -132.4825,64.87691,0 -132.5075,64.96442,0 -132.3333,65.03471999999999,0 -132.3817,65.08499000000001,0 -132.54,65.09388,0 -132.5561,65.11803999999999,0 -132.5275,65.16277000000001,0 -132.5764,65.19054,0 -132.7147,65.15555000000001,0 -132.7878,65.18777,0 -132.7169,65.20387,0 -132.7836,65.22499000000001,0 -132.5553,65.28360000000001,0 -132.5214,65.36304,0 -132.3278,65.43471,0 -132.2736,65.54694000000001,0 -132.1719,65.59804,0 -132.3239,65.73997,0 -132.5294,65.82555000000001,0 -132.5733,65.84859,0 -132.54,65.88916,0 -132.3267,65.95193,0 -132.3536,65.99026000000001,0 -132.5103,65.97887,0 -132.5761,66.03055000000001,0 -132.7822,65.96720999999999,0 -132.817,65.91998,0 -132.9061,65.90833000000001,0 -132.9939,65.92303,0 -132.9084,65.99693000000001,0 -132.9397,66.02887,0 -133.1475,66.02469000000001,0 -133.4075,65.94386,0 -133.6294,65.97443,0 -133.6033,66.05165,0 -133.7047,66.07193,0 -133.6731,66.11693,0 -133.5639,66.15583,0 -133.57,66.27831999999999,0 -133.8247,66.31666,0 -133.7681,66.37303,0 -133.7503,66.44775,0 -133.6294,66.44525,0 -133.6903,66.53193,0 -133.5867,66.5647,0 -133.7719,66.64413999999999,0 -133.7572,66.68526,0 -133.8403,66.72776,0 -133.7639,66.80692999999999,0 -133.9081,66.90887000000001,0 -134.0925,66.95527,0 -134.0461,66.98441,0 -133.8311,67.00333000000001,0 -136.1645,67.00360000000001,0 -136.2222,67.06415,0 -136.1989,67.14333000000001,0 -136.2364,67.17386999999999,0 -136.1345,67.22638000000001,0 -136.0914,67.30247,0 -136.1428,67.35942,0 -136.1164,67.38274,0 -136.2053,67.40694000000001,0 -136.1578,67.50776999999999,0 -136.2411,67.62053,0 -136.4211,67.65526000000001,0 -136.445,67.71554999999999,0 -136.4442,68.43304000000001,0 -136.4447,68.58778,0 -136.4461,68.87302,0 -136.4206,68.90156,0 -136.2715,69.22282,0 -135.6703,69.64033000000001,0 -134.3343,69.85742999999999,0 -133.5828,69.57352,0 -130.4598,70.22484,0 -129.9588,70.14133,0 -129.7417,70.30833,0 -128.8733,70.34173000000001,0 -128.2554,70.67574000000001,0 -127.5151,70.49976,0 -126.1624,71.89608,0 -124.8097,73.59784999999999,0 -125.0715,74.16511,0 -124.8534,74.47057,0 -123.1952,75.95415,0 -123.1516,76.30323,0 -121.9734,76.60868000000002,0 -120.5771,76.91412,0 -118.9626,77.43774000000001,0 -116.999,77.65592,0 -114.0755,78.39772000000001,0 -112.5046,78.6159,0 -112.5046,79.90000000000001,0 -112.5046,84.90001,0 -112.5046,89.90000000000001,0 -111.5053,89.90000000000001,0 -110.5061,89.90000000000001,0 -109.5068,89.90000000000001,0 -108.5075,89.90000000000001,0 -107.5082,89.90000000000001,0 -106.509,89.90000000000001,0 -105.5097,89.90000000000001,0 -104.5104,89.90000000000001,0 -103.5111,89.90000000000001,0 -102.5119,89.90000000000001,0 -101.5126,89.90000000000001,0 -100.5133,89.90000000000001,0 -99.51402000000002,89.90000000000001,0 -98.51475000000001,89.90000000000001,0 -97.51546,89.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -06:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-6);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0600</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-82.51007,89.90000000000001,0 -82.5051,84.90001,0 -82.50320000000001,82.97593999999999,0 -84.99902,82.98072000000001,0 -84.99903999999999,77.98072999999999,0 -84.99907,72.98072000000001,0 -84.9991,67.98071,0 -84.9991,66.25666,0 -85.64087000000001,66.05121,0 -86.11559,65.84233,0 -86.74223000000002,64.72197,0 -87.25494000000001,64.20925,0 -87.6917,63.6016,0 -87.52079000000001,63.03194,0 -86.66628,62.44327,0 -85.00069000000001,61.95694000000001,0 -85.00126,56.95695000000001,0 -85.00144,55.29702000000001,0 -85.39335,55.40889,0 -85.49944000000002,55.47222000000001,0 -85.56947,55.5586,0 -85.94445,55.69778,0 -86.27722000000001,55.72887,0 -86.47778,55.80888,0 -86.95889,55.90582,0 -86.97945,55.93166,0 -87.3625,55.94666,0 -87.35195,55.99276,0 -87.54861,56.04999,0 -87.61583,56.00277000000001,0 -87.58362,56.0761,0 -87.70805,56.1561,0 -87.72389,56.20388,0 -87.94305,56.37749000000001,0 -87.98277000000002,56.44165000000002,0 -88.3536,56.55332000000001,0 -88.48999000000001,56.64222000000001,0 -88.65472000000001,56.69638,0 -88.87944,56.85804000000001,0 -89.03778000000001,56.83611,0 -89.13284,56.86485,0 -89.30607999999999,56.62564,0 -89.62405000000001,56.43136,0 -89.60860000000001,51.43138,0 -89.59805,48.00918,0 -89.49317000000001,48.00332000000001,0 -90.41302,46.5882,0 -90.39612,46.54695,0 -90.33277,46.55915,0 -90.22278,46.50722,0 -90.12388,46.33972,0 -89.11749,46.14805,0 -88.83806,46.035,0 -88.64194999999999,46.00111,0 -88.52029000000002,46.02499,0 -88.11583000000002,45.93221,0 -88.08139,45.87777,0 -88.11834,45.80527000000001,0 -87.90083,45.75944,0 -87.80029,45.68415,0 -87.82556,45.65526000000001,0 -87.78751,45.59664999999999,0 -87.83,45.57166000000001,0 -87.8036,45.50139,0 -87.87027000000002,45.44221000000001,0 -87.88084000000001,45.36639,0 -87.67610000000002,45.38666000000001,0 -87.65055,45.35194,0 -87.71666999999999,45.26721,0 -87.735,45.18721000000001,0 -87.64861999999999,45.11694,0 -87.43716000000001,45.07106,0 -87.40428,45.19296000000001,0 -87.27666000000001,45.25579,0 -87.08848999999999,45.44498,0 -86.7586,45.44141000000001,0 -86.24245000000001,45.23384000000001,0 -86.57338000000001,44.99793,0 -86.71111000000001,44.83602,0 -86.83409,44.59161,0 -87.06783,43.96139,0 -87.15730000000001,43.41102,0 -87.02119000000002,42.48526,0 -87.21111000000002,41.76225,0 -86.52540999999999,41.75969,0 -86.52528000000001,41.66103,0 -86.48599,41.65967,0 -86.48571,41.58063,0 -86.5304,41.52581,0 -86.52912000000001,41.43995,0 -86.64751,41.43719,0 -86.76918000000001,41.30227,0 -86.88367,41.26608,0 -86.93285,41.17447,0 -86.93117,40.91845,0 -86.98653,40.9182,0 -86.98604,40.84695,0 -87.10338,40.84467,0 -87.10191,40.74436,0 -87.52641,40.74535,0 -87.53278,39.35695000000001,0 -87.62639,39.30471,0 -87.58945,39.20638,0 -87.65916,39.15582,0 -87.66861,39.10999,0 -87.59389,39.0561,0 -87.58611999999999,38.99554,0 -87.53416,38.95333,0 -87.55777000000001,38.86805,0 -87.51195,38.74527,0 -87.66583,38.55999,0 -87.64584000000001,38.49499,0 -87.75584000000001,38.45804,0 -87.75163000000001,38.41816,0 -87.61599,38.45092000000001,0 -87.54741,38.51014,0 -87.50431,38.49449,0 -87.47975,38.53218,0 -87.47684,38.46685,0 -87.41956,38.43461,0 -87.4182,38.37928,0 -87.319,38.37801,0 -87.30332,38.23492000000001,0 -86.79993,38.20176,0 -86.81465,38.13544,0 -86.78015000000001,38.10248,0 -86.77476,38.01123,0 -86.82632,37.99151,0 -86.75384,37.8983,0 -86.66867000000001,37.91314000000001,0 -86.64558,37.84594,0 -86.59833,37.92101,0 -86.52275,37.92781000000001,0 -86.50314,38.05159,0 -86.16003000000001,37.80325,0 -86.28140999999999,37.59924,0 -86.12286,37.5709,0 -86.05628,37.44679,0 -85.66943000000001,37.41729,0 -85.64707,37.4683,0 -85.59048,37.46286,0 -85.58540000000001,37.42731,0 -85.47422,37.34881,0 -85.39812000000002,37.2369,0 -85.41483,37.21706,0 -85.36129,37.19164,0 -85.32208,37.24111,0 -85.20011,37.26906,0 -85.17966,37.31235,0 -85.07001,37.25666,0 -85.0663,37.20251,0 -84.97011999999999,37.11,0 -84.91865,37.12032000000001,0 -84.92086000000001,37.05594,0 -84.84885000000001,36.99782,0 -84.77699,36.95735,0 -84.68555000000001,36.98052,0 -84.58721,36.87076,0 -84.59557000000001,36.80482,0 -84.67641,36.7645,0 -84.79111,36.60536,0 -84.65366,36.39601,0 -84.77486,36.3341,0 -84.78586,36.29548,0 -84.866,36.28408,0 -84.9053,36.15482,0 -84.79865,36.09025,0 -84.67876,35.91422,0 -84.76385999999999,35.82634,0 -84.89986,35.77536,0 -84.97051999999999,35.6754,0 -85.10691000000001,35.58106,0 -85.13974,35.50185,0 -85.11647000000001,35.48328,0 -85.14761,35.45309,0 -85.17104000000001,35.46849,0 -85.38441,35.15899,0 -85.36797,35.06706,0 -85.46741,34.99005,0 -85.62070000000001,34.9901,0 -85.3925,33.99749,0 -85.17416,32.82417000000001,0 -85.13276999999999,32.7786,0 -85.10445,32.64415,0 -84.99306,32.51193,0 -84.96278,32.42333,0 -84.99695,32.33583,0 -84.89417000000002,32.26944,0 -85.05083000000001,32.13832,0 -85.06027,32.00388,0 -85.14111,31.82611,0 -85.03751,31.54083,0 -85.10889,31.27111,0 -85.09972000000001,31.17888,0 -85.00557000000001,31.06222,0 -85.00448,31.00173,0 -84.93167,30.88832,0 -84.91332,30.75332,0 -84.86639000000001,30.71832,0 -84.98378,30.54212,0 -84.98756,30.44610000000001,0 -85.029,30.42799,0 -85.01819999999999,30.37397,0 -85.14525999999999,30.13059,0 -85.11667,30.03346,0 -85.02682,29.97955,0 -85.02878,29.81974,0 -85.04214,29.7827,0 -85.11682,29.77658,0 -85.21616,29.69783,0 -85.05280000000001,29.59025,0 -84.98586,29.60068,0 -84.76974,29.69064,0 -84.67976,29.76196,0 -84.34405,29.89086,0 -84.32649000000001,29.96329,0 -84.18716000000001,30.06915,0 -84.00285,30.07809,0 -83.73586,29.94671,0 -83.60023000000001,29.82804,0 -83.58752,29.756,0 -83.42648,29.65429,0 -83.40105,29.5102,0 -83.13406000000001,29.26864,0 -83.04929,29.13302,0 -82.97725000000001,29.1754,0 -82.81621,29.14997,0 -82.65092,28.70922,0 -82.66786999999999,28.52699,0 -82.81197,28.15403,0 -82.86282,27.87009,0 -82.65622999999999,27.34336,0 -82.67905,24.16667,0 -86.05847,24.16667,0 -86.05847,19.16667,0 -86.05847,18.18773,0 -82.60478999999999,18.18773,0 -82.60478999999999,13.18773,0 -82.60478999999999,9.495170999999999,0 -82.66473000000001,9.495278000000001,0 -82.74890000000001,9.584163,0 -82.83307000000001,9.612215000000001,0 -82.87575,9.558963,0 -82.83338999999999,9.498041000000001,0 -82.93473,9.471663,0 -82.93047,9.063126000000001,0 -82.88890000000001,9.074448,0 -82.71084999999999,8.931113,0 -82.87779000000001,8.827496999999999,0 -82.85778999999999,8.809163,0 -82.91446999999999,8.762786999999999,0 -82.83196,8.626661,0 -82.82993,8.474651,0 -83.04834000000001,8.326105999999999,0 -82.93085000000001,8.254719,0 -82.88612000000001,8.103338000000003,0 -82.89883,8.025668,0 -82.86584000000001,8.020835999999999,0 -82.85028,8.060003,0 -82.8753,8.190837,0 -82.84778,8.270838,0 -82.72168000000002,8.317214,0 -82.60478999999999,8.304010999999999,0 -82.60348999999999,3.304008,0 -82.60218,-1.695988,0 -82.60088,-6.695992,0 -82.59957,-11.69599,0 -82.59827000000001,-16.69599,0 -82.59696,-21.69599,0 -82.59626,-24.39248,0 -82.59408000000001,-29.39247,0 -82.59188,-34.39248,0 -82.58971,-39.39247,0 -82.58879000000002,-41.49248,0 -82.5866,-46.49247,0 -82.58441999999999,-51.49246999999999,0 -82.58223,-56.49247,0 -82.58005,-61.49247,0 -82.57787,-66.49247,0 -82.57568000000001,-71.49247,0 -82.57349000000001,-76.49247000000001,0 -82.57227,-79.29246999999999,0 -82.57008,-84.29246999999999,0 -82.56790000000001,-89.29246999999999,0 -82.56762999999999,-89.90000000000001,0 -83.56634,-89.90000000000001,0 -84.56502999999999,-89.90000000000001,0 -85.56374,-89.90000000000001,0 -86.56245,-89.90000000000001,0 -87.56115,-89.90000000000001,0 -88.55985,-89.90000000000001,0 -89.55856,-89.90000000000001,0 -90.55726,-89.90000000000001,0 -91.55596000000001,-89.90000000000001,0 -92.55466,-89.90000000000001,0 -93.55338,-89.90000000000001,0 -94.55207,-89.90000000000001,0 -95.55079000000001,-89.90000000000001,0 -96.54949000000003,-89.90000000000001,0 -97.54818000000002,-89.90000000000001,0 -97.54723000000001,-88.31852000000001,0 -97.54416999999999,-83.31852000000001,0 -97.54217,-80.01852000000001,0 -97.53912000000001,-75.01852,0 -97.53608,-70.01852,0 -97.53570000000001,-68.5929,0 -97.53437,-63.59291,0 -97.53304,-58.59291000000001,0 -97.53172000000002,-53.59291,0 -97.53039000000003,-48.59291,0 -97.52906000000002,-43.59291000000001,0 -97.52773000000001,-38.59291,0 -97.52641,-33.59291,0 -101.071,-33.59291,0 -106.0709,-33.59291,0 -111.071,-33.59291,0 -111.079,-31.93718000000001,0 -111.1033,-26.93723,0 -111.1275,-21.93729,0 -107.5242,-21.93729,0 -102.5242,-21.93729,0 -97.52416,-21.93729,0 -97.52332,-17.55437,0 -97.52263000000001,-14.05436,0 -97.52168,-9.054360000000001,0 -97.52070999999999,-4.054366,0 -97.51974,0.9456385000000001,0 -97.51877000000002,5.945633,0 -97.51781,10.94564,0 -97.51684,15.94563,0 -97.785,15.96861,0 -97.87112000000001,16.02139,0 -97.87000999999999,16.06195,0 -98.16724000000002,16.19695,0 -98.06444999999999,16.18389,0 -98.09807000000001,16.21416,0 -98.53667,16.30695,0 -98.78223,16.55305,0 -98.86473000000001,16.52444,0 -99.03695999999999,16.59694,0 -99.69222000000001,16.70834,0 -99.84972000000002,16.7875,0 -99.83778000000001,16.81445,0 -99.87807000000002,16.87028,0 -99.90001,16.82555,0 -99.9389,16.88222,0 -100.0783,16.94166,0 -100.1864,16.95556,0 -101.0486,17.26694,0 -101.1044,17.35889,0 -101.4178,17.51861,0 -101.4978,17.62195,0 -101.5561,17.61777,0 -101.6331,17.66695,0 -101.7878,17.87611,0 -101.9214,17.96194,0 -102.0292,17.99195,0 -102.181,17.92083,0 -102.4875,18.02333,0 -102.745,18.06582,0 -103.0294,18.19,0 -103.45,18.31361,0 -103.5792,18.50083,0 -103.7047,18.58528,0 -103.6839,18.61138,0 -103.7147,18.6625,0 -103.972,18.87722,0 -104.315,19.00805,0 -104.3278,19.09527,0 -104.4483,19.09083,0 -104.6628,19.16805,0 -104.735,19.23,0 -104.8097,19.22084,0 -104.797,19.28861,0 -104.8967,19.28389,0 -105.0225,19.37195,0 -105.1025,19.565,0 -105.2144,19.6225,0 -105.4569,19.92222,0 -105.5422,20.07695,0 -105.562,20.21917,0 -105.68,20.38639,0 -105.677,20.42417,0 -105.5903,20.47805,0 -105.352,20.51305,0 -105.2503,20.56556,0 -105.2336,20.62889,0 -105.2669,20.69905,0 -105.0833,20.92528,0 -104.9489,20.92556,0 -104.77,21.02055,0 -104.7219,21.01278,0 -104.625,20.9236,0 -104.535,20.91611,0 -104.4672,20.82972,0 -104.2847,20.70916,0 -104.275,20.86084,0 -104.21,20.97805,0 -104.2277,21.17774,0 -104.0425,21.21139,0 -103.9614,21.28777,0 -103.9449,21.37497,0 -104.2072,21.54721,0 -104.1528,21.59805,0 -104.0936,21.78583,0 -104.4028,22.07638,0 -104.3296,22.26453,0 -104.3114,22.31921,0 -104.345,22.45138,0 -104.4914,22.41027,0 -104.6125,22.4725,0 -104.6606,22.62444,0 -104.7575,22.67666,0 -104.9981,22.54834,0 -104.9981,22.67889,0 -104.88,22.78306,0 -104.9147,22.92472,0 -105.172,23.03972,0 -105.3125,23.03472,0 -105.4021,23.06746,0 -105.4167,23.14722,0 -105.5289,23.14444,0 -105.6842,23.28722,0 -105.7181,23.47472,0 -105.8964,23.78945,0 -105.9081,24.05527,0 -105.9597,24.09889,0 -106.0022,24.21194,0 -106.2492,24.39027,0 -106.3961,24.28472,0 -106.5192,24.30194,0 -106.6508,24.58861,0 -106.7828,24.7025,0 -106.8,24.75277,0 -106.885,24.77028,0 -106.9489,24.84194,0 -107.0422,24.99416,0 -107.1194,25.20945,0 -107.0841,25.60609,0 -107.1517,25.77556,0 -107.2997,25.94334,0 -107.3664,26.11527,0 -107.7844,26.20028,0 -107.8467,26.64,0 -108.0039,26.81972,0 -108.0356,26.94750000000001,0 -108.2206,26.97277,0 -108.2489,27.04083,0 -108.3053,27.06138,0 -108.405,27.03082,0 -108.4713,26.96133,0 -108.6042,27.03805,0 -108.6653,27.15195,0 -108.6364,27.31917,0 -108.6675,27.40416,0 -108.6553,27.52,0 -108.775,27.59945,0 -108.8097,27.71167,0 -108.9139,27.785,0 -109.1464,28.17583,0 -109.0558,28.29917,0 -109.022,28.27584,0 -108.977,28.30556,0 -108.8808,28.29834,0 -108.6525,28.21222,0 -108.5656,28.28916,0 -108.6895,28.69639000000001,0 -108.6239,28.77111,0 -108.7078,29.40084,0 -108.6136,29.40111,0 -108.5581,29.99277,0 -108.6778,30.57583,0 -108.735,30.6325,0 -108.7981,31.205,0 -108.8361,31.15695,0 -108.8914,31.19222,0 -108.8319,31.33264,0 -108.2083,31.33305,0 -108.2086,31.78334,0 -106.5229,31.78075000000001,0 -106.3908,31.74472,0 -106.2144,31.47528000000001,0 -105.9489,31.36195,0 -105.7703,31.17611,0 -105.5978,31.08,0 -105.5672,31.00945,0 -105.3897,30.86222,0 -105.2614,30.79722,0 -105.2075,30.80166,0 -105.0022,30.68027,0 -104.9921,30.63846,0 -104.9149,30.68164000000001,0 -104.9016,30.81495,0 -104.9226,31.99982,0 -103.0656,32,0 -102.9969,36.5,0 -102.9972,36.99850000000001,0 -102.0368,36.98897,0 -102.0435,37.73439,0 -101.5204,37.73152,0 -101.5631,38.69329,0 -101.4782,38.69465,0 -101.4727,39.12796,0 -101.3867,39.12926,0 -101.4063,39.56715,0 -102.049,39.5687,0 -102.0511,39.99892,0 -101.3217,40.00183,0 -101.3406,40.69749,0 -101.243,40.69778,0 -101.2429,41.04119,0 -101.2625,41.39017,0 -101.4027,41.39172,0 -101.4083,41.73717,0 -100.8393,41.73868,0 -100.8447,42.082,0 -100.6806,42.08107,0 -100.6806,42.29481,0 -100.8729,42.29674,0 -100.8729,42.48515,0 -100.8267,42.48515,0 -100.8267,42.66779,0 -100.8979,42.66779,0 -100.8979,42.74853,0 -100.8056,42.74853,0 -100.8056,42.80621,0 -100.8633,42.80814,0 -100.8633,42.85428,0 -100.9094,42.85428,0 -100.9094,42.98817,0 -100.1977,42.99109,0 -100.232,43.71497,0 -100.4769,43.72082,0 -100.5873,43.76701,0 -100.7452,43.72055,0 -100.7452,43.9761,0 -100.3857,44.40596,0 -100.3989,44.45416,0 -100.5861,44.4632,0 -100.5922,44.4953,0 -100.5215,44.54869,0 -100.6343,44.57531,0 -100.6327,44.73686,0 -100.7185,44.77095000000001,0 -100.7303,44.80638,0 -100.7014,44.83079,0 -100.5997,44.76569,0 -100.5412,44.76557,0 -100.4071,44.85565,0 -100.4246,45.01186,0 -100.2805,45.03674,0 -100.3317,45.10395000000001,0 -100.2706,45.25081,0 -100.3219,45.31851,0 -100.2794,45.38273,0 -100.3704,45.50183000000001,0 -100.4568,45.51511000000001,0 -100.4905,45.55487,0 -100.422,45.6551,0 -100.3307,45.66776000000001,0 -100.3069,45.72111,0 -100.3715,45.79243000000001,0 -100.3773,45.86584,0 -100.5153,45.92761,0 -100.628,46.11914,0 -100.5613,46.24074000000001,0 -100.5843,46.27890000000001,0 -100.5543,46.36341000000001,0 -100.5988,46.47136,0 -100.5627,46.58238,0 -100.6235,46.60821,0 -100.6479,46.6602,0 -100.7804,46.68494,0 -100.7838,46.73688,0 -100.8355,46.75002,0 -100.8188,46.8093,0 -100.9013,46.90117,0 -100.93,46.98355000000001,0 -100.8747,47.04248,0 -100.9889,47.19390000000001,0 -100.9885,47.25916,0 -101.1344,47.29811999999999,0 -101.1977,47.25529000000001,0 -101.3423,47.2963,0 -101.4371,47.46869000000001,0 -101.3807,47.52191000000001,0 -101.4348,47.56065000000001,0 -101.8144,47.5023,0 -102.0163,47.53939,0 -102.0614,47.57729,0 -102.1996,47.57961,0 -102.3872,47.59027,0 -102.4334,47.53006,0 -102.6592,47.53583,0 -102.6803,47.58082000000001,0 -102.8392,47.60444,0 -103,47.67471,0 -103.0439,47.59749,0 -103.3642,47.61639000000002,0 -103.3961,47.605,0 -103.4097,47.53999,0 -103.5878,47.52712,0 -103.5878,47.58411,0 -103.6829,47.58421,0 -103.6829,47.71054,0 -103.8168,47.71054,0 -103.817,47.81991000000001,0 -103.9551,47.82047,0 -103.915,47.91,0 -103.9318,47.96657,0 -104.0479,48.00007000000001,0 -104.0625,49.0001,0 -104.9997,49.00018,0 -109.9997,49.0006,0 -109.9998,49.99528000000001,0 -110.0003,54.99528000000001,0 -110.0009,59.99528,0 -106.0682,59.99960999999999,0 -102.0009,59.99936,0 -102.0008,61.92099,0 -102.0007,66.92099,0 -101.2137,66.91991,0 -96.2137,66.9131,0 -96.05273000000001,66.91264,0 -93.98089000000002,66.90748000000001,0 -88.98090000000001,66.89502000000002,0 -89.01671000000002,69.25249000000001,0 -89.01796000000002,69.28883,0 -89.08181999999999,69.33895,0 -89.60105,69.45796,0 -90.1852,69.51204,0 -90.85590000000001,69.76086000000001,0 -91.90519000000001,70.62624,0 -92.1648,70.74524,0 -92.41362,70.81014,0 -92.57588,70.92914,0 -92.71651,71.11304,0 -92.78141000000001,71.28612,0 -92.93285,71.44838,0 -93.19248000000002,71.65391,0 -93.74417000000003,72.01089,0 -93.87927000000001,72.25537,0 -95.11967,72.46542,0 -96.41336,72.64267,0 -96.71005,72.71178000000001,0 -96.89134,72.69624,0 -97.00784,72.74532000000001,0 -98.29798,72.94485,0 -98.38002,72.95116,0 -100.5149,73.23768,0 -100.9536,73.20439,0 -101.191,73.22286,0 -102.0005,73.3723,0 -102.0004,75.63531,0 -102.0002,80.63531,0 -97.51546,80.63566,0 -97.51546,84.90001,0 -97.51546,89.90000000000001,0 -96.57764,89.90000000000001,0 -95.63981000000001,89.90000000000001,0 -94.70197,89.90000000000001,0 -93.76412000000002,89.90000000000001,0 -92.82628,89.90000000000001,0 -91.88844000000002,89.90000000000001,0 -90.95061,89.90000000000001,0 -90.01277,89.90000000000001,0 -89.07494,89.90000000000001,0 -88.1371,89.90000000000001,0 -87.19926,89.90000000000001,0 -86.26142,89.90000000000001,0 -85.32359,89.90000000000001,0 -84.38574,89.90000000000001,0 -83.44790999999999,89.90000000000001,0 -82.51007,89.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -05:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0500</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-67.50302000000001,89.90000000000001,0 -67.50302000000001,84.90001,0 -67.50302000000001,82.99629,0 -64.77722,82.92871,0 -63.38053000000001,82.86113,0 -62.89619,82.57953999999999,0 -61.43191,82.50069000000002,0 -59.49456,82.01636000000002,0 -60.51955,81.93752000000001,0 -61.47697000000001,81.79107999999999,0 -61.49949,81.73477000000001,0 -61.02642,81.47570000000002,0 -61.31927,81.39686,0 -61.35306000000001,81.34054,0 -61.30801,81.16032,0 -61.55581000000002,81.11526000000001,0 -62.23162000000001,81.21664,0 -62.97503,81.25042999999999,0 -63.81981,81.14906000000001,0 -63.96624000000001,81.07021000000002,0 -64.79975,81.00261999999999,0 -64.91238,80.92377000000001,0 -66.44423999999999,80.6084,0 -66.78216000000001,80.72104,0 -66.92856999999999,80.65346,0 -66.92856999999999,80.47324,0 -67.49175,80.34933,0 -67.54808,80.29301,0 -67.52555,80.18038,0 -67.10879,80.04520000000001,0 -66.66951,80.05649,0 -66.0838,80.00015999999999,0 -65.64452,80.00015999999999,0 -65.29535,80.06775,0 -65.14892000000002,79.84247000000001,0 -64.97996999999999,79.47077,0 -65.98242999999999,79.14412,0 -67.31153999999999,79.16665000000002,0 -67.76724,79.10155,0 -72.55276000000001,78.52284,0 -72.5564,77.34211000000001,0 -68.07777,75.93836,0 -68.07777,75.31999999999999,0 -68.07777,70.31999999999999,0 -67.77307,70.28399,0 -67.50825,70.17017,0 -67.20399999999999,69.97538,0 -66.70717000000001,69.43039,0 -66.59773,69.21590000000001,0 -66.53207,68.44109,0 -66.06807000000001,68.37982,0 -64.37838000000001,68.03400000000001,0 -63.71739,67.86327,0 -61.94015000000002,67.30296000000001,0 -61.12597,66.90025,0 -60.93336000000002,66.62885,0 -61.43238,66.19110000000001,0 -61.97518,65.88468,0 -62.08899,65.51698,0 -62.6493,65.48196,0 -63.50727,64.82534,0 -63.97128,63.76601000000001,0 -63.98004,63.25823000000001,0 -64.29521,62.44402000000001,0 -64.6279,61.34092,0 -64.39993,60.38216,0 -64.48108000000001,60.36652000000001,0 -64.53325,60.3025,0 -64.56751,60.30471,0 -64.8586,60.25583000000001,0 -64.76723,60.175,0 -64.63721,60.17833,0 -64.59778,60.11749,0 -64.73416,60.10333,0 -64.7025,60.06499,0 -64.79695,60.03972,0 -64.90639,60.04805,0 -64.84778,59.96416,0 -64.6536,59.92999,0 -64.66191000000001,59.88361,0 -64.76778,59.87999,0 -64.74445,59.84499,0 -64.81416000000002,59.81889,0 -64.77306,59.69887,0 -64.83307000000001,59.53971,0 -64.70084,59.44583000000001,0 -64.43249,59.52944,0 -64.34334,59.50111,0 -64.50696000000001,59.42832000000001,0 -64.485,59.39638,0 -64.53806,59.37360000000001,0 -64.49916,59.33972000000001,0 -64.53834000000001,59.29055,0 -64.50751,59.25,0 -64.53471999999999,59.19749000000001,0 -64.49306,59.15888,0 -64.48917,59.09943,0 -64.34972000000001,59.06666000000001,0 -64.29138,59.00527,0 -64.46084000000001,58.98443,0 -64.72,59.07,0 -64.82946,59.03249,0 -64.86472000000001,58.99054,0 -64.83890000000001,58.9586,0 -64.88332,58.93194,0 -64.80943000000002,58.9111,0 -64.71888,58.94527000000001,0 -64.56807000000001,58.88721000000001,0 -64.30029,58.88249,0 -64.25861999999999,58.85000000000001,0 -64.25751,58.77944000000001,0 -64.15611,58.74528,0 -63.97472000000001,58.8261,0 -63.83333,58.8325,0 -63.77083,58.87554000000001,0 -63.58834,58.84638000000002,0 -63.4825,58.77055000000001,0 -63.50611,58.73194,0 -64.02916999999999,58.68694,0 -64.08611999999999,58.63777,0 -64.10056,58.57916,0 -64.04277,58.52388000000001,0 -63.89806,58.56499,0 -63.84584,58.50805,0 -63.81555,58.47805000000001,0 -63.85918,58.43776,0 -64.0025,58.42527,0 -64.03667,58.37499,0 -64.16306,58.34389,0 -64.21861,58.23082,0 -64.38721,58.19137000000001,0 -64.43195,58.08611,0 -64.20862,58.03334,0 -64.21445,57.97748,0 -64.05749,57.76943000000001,0 -64.02251,57.77138,0 -64.00666,57.81276,0 -63.92666,57.79722,0 -63.89194,57.73027,0 -63.82556000000002,57.69027,0 -63.76528,57.73277000000001,0 -63.70055,57.66277000000001,0 -63.65333,57.7311,0 -63.58944,57.72526,0 -63.60723,57.66361,0 -63.76501,57.57388,0 -63.75723000000001,57.43444000000001,0 -63.7075,57.3786,0 -63.83694000000001,57.32583000000002,0 -63.87583,57.23193000000001,0 -63.86584000000001,57.19583000000001,0 -63.83944,57.20054,0 -63.80500000000001,57.28027,0 -63.7411,57.23277,0 -63.79249999999999,57.16526000000001,0 -63.78751,57.11193999999999,0 -63.87361,57.06638000000002,0 -63.86333,56.99276,0 -63.91779,56.93027000000001,0 -63.87138,56.87805000000001,0 -64.00528,56.85054000000001,0 -63.99222000000002,56.78582,0 -64.08362,56.76554,0 -64.12888,56.69943,0 -63.945,56.54972,0 -63.94611,56.48388,0 -63.87138,56.43942,0 -63.97472000000001,56.41277,0 -64.16972,56.42778,0 -64.11417,56.39027000000001,0 -64.10584,56.33139000000001,0 -64.13667,56.3036,0 -64.08667,56.25916000000001,0 -63.97472000000001,56.25916000000001,0 -63.87694,56.21721,0 -64.01083,56.15999,0 -64.02028,56.07638000000001,0 -63.84778,56.12027,0 -63.88,56.07695,0 -63.81333,56.0425,0 -63.445,56.02276,0 -63.69416,55.94222,0 -63.71528,55.85917000000001,0 -63.48833000000001,55.69665000000001,0 -63.44806,55.59943,0 -63.29833000000001,55.52333,0 -63.14416,55.52638000000001,0 -63.17445,55.40971,0 -63.09333,55.34583000000001,0 -63.11423,55.31608,0 -63.51944,55.1836,0 -63.56611000000002,55.06027,0 -63.43528000000001,54.97360000000001,0 -63.54194,54.84055,0 -63.62361,54.66610000000001,0 -63.72084,54.59665,0 -63.97619000000001,54.62645000000001,0 -64.34723,54.75527,0 -64.55222000000001,54.72055,0 -64.81166,54.74917000000001,0 -65.09445,54.69555,0 -65.44387999999999,54.74583000000001,0 -65.68998000000001,54.71332,0 -65.83528,54.83138,0 -65.85834,54.90778,0 -65.95778,54.92944000000001,0 -66.035,54.90972,0 -66.23999000000001,54.99194000000001,0 -66.57640000000001,55.25055,0 -66.68195,55.25333,0 -66.68804,55.3011,0 -66.80029,55.33999000000001,0 -66.83389,55.31332,0 -66.6889,55.20416,0 -66.74083,55.17749,0 -66.76585,55.10556,0 -66.65944000000002,54.97942,0 -66.73166999999999,54.94332,0 -66.605,54.79084,0 -66.71222,54.78194,0 -66.71806000000001,54.73193,0 -66.94694,54.80165,0 -67.02806000000001,54.89778,0 -67.30943000000001,55.01554000000001,0 -67.27388999999999,55.06666,0 -67.42833,55.07111,0 -67.45305999999999,55.01888,0 -67.36221000000001,54.87582,0 -67.26334,54.82527,0 -67.30777000000001,54.78388,0 -67.185,54.71943000000001,0 -67.2139,54.66888000000001,0 -67.03639,54.62166,0 -67.21945,54.6075,0 -67.30556,54.50416,0 -67.49249,54.58194,0 -67.60139,54.56639000000001,0 -67.63306,54.47999000000001,0 -67.58611999999999,54.4175,0 -67.59583000000001,54.36527000000001,0 -67.49472,54.31444000000001,0 -67.53167000000001,54.24889,0 -67.485,54.23416,0 -67.69611,54.1386,0 -67.71556,54.12944000000001,0 -67.78195,54.06388,0 -67.76055,54.01583000000001,0 -67.60916,53.92804000000001,0 -67.58804000000001,53.85722,0 -67.43195,53.78305,0 -67.49194,53.60388,0 -67.14194999999999,53.54221,0 -67.12888,53.46416,0 -67.06862,53.4286,0 -67.06779,53.38417,0 -66.9875,53.40359,0 -66.9025,53.3261,0 -66.99027,53.20082000000001,0 -66.94861,53.10972000000002,0 -66.97945000000001,53.02777,0 -67.07805999999999,52.94804000000001,0 -67.05833,52.80499,0 -67.08945,52.7197,0 -66.89138,52.67333000000001,0 -66.87470999999999,52.74778,0 -66.80556,52.66554,0 -66.75501,52.71027000000001,0 -66.66806,52.72581,0 -66.64806,52.94583,0 -66.52528,52.94332000000001,0 -66.47694,53.03416,0 -66.35472,52.98166,0 -66.26973,52.87749,0 -66.37888,52.83611,0 -66.32834,52.73693,0 -66.43388,52.70638,0 -66.43222,52.61138,0 -66.37361,52.54249999999999,0 -66.44665999999999,52.53082,0 -66.35805000000002,52.40666,0 -66.45862,52.3661,0 -66.48556000000001,52.30721,0 -66.43889,52.20083,0 -66.33556,52.13639,0 -66.30388000000001,52.22276,0 -66.31973000000001,52.28916000000001,0 -66.26667000000001,52.31138,0 -66.17389000000001,52.22055,0 -66.06807000000001,52.21638000000001,0 -66.06751000000001,52.12276,0 -65.91695,52.05082,0 -65.80167,52.04278,0 -65.79944,52.09555,0 -65.75861,52.11139,0 -65.58056999999999,52.04278,0 -65.4614,52.20665000000001,0 -65.10861,52.17082,0 -64.85334,52.02193000000001,0 -64.84584,51.93083000000001,0 -64.58778,51.82888,0 -64.59389,51.79610000000001,0 -64.71805999999999,51.74778000000001,0 -64.70305999999999,51.69277000000002,0 -64.60417,51.64277,0 -64.58112,51.55778,0 -64.44249000000001,51.66776999999999,0 -64.26418,51.73972000000001,0 -64.35722,51.85055000000001,0 -64.31751,51.90359999999999,0 -64.33085,51.96527,0 -64.27444,52.00332,0 -64.29333,52.04639,0 -64.25779,52.12804,0 -64.11861,52.14472,0 -64.13137999999999,52.28139,0 -64.08308,52.37638,0 -64.20889000000001,52.54889000000001,0 -64.17529,52.60944000000001,0 -64.20223,52.67222000000001,0 -64.15666,52.73833,0 -64.20140000000001,52.79084,0 -64.1725,52.85083000000001,0 -64.08085,52.88138,0 -63.95417,52.87165,0 -63.98277,52.9336,0 -63.91945000000001,52.97693,0 -63.92805,53.07166,0 -63.86639000000001,53.10527,0 -63.655,53.105,0 -63.62611,53.04222000000002,0 -63.54805000000001,52.98499,0 -63.54084,52.91666,0 -63.59584,52.77077000000001,0 -63.42305000000001,52.70722,0 -63.37777,52.65083,0 -63.86305999999999,52.61277,0 -64.00194999999999,52.55471,0 -64.08085,52.47610000000001,0 -64.03749999999999,52.43444,0 -63.96222,52.45193,0 -63.91889000000001,52.38942999999999,0 -64.01306,52.38722,0 -63.9875,52.3625,0 -64.02528000000001,52.35444,0 -63.91805999999999,52.33611,0 -63.82917,52.25999,0 -63.84641000000001,52.00000000000001,0 -63.05001,52.00000000000001,0 -63.04419000000001,48.50467,0 -63.84024000000001,48.40712000000001,0 -64.52839,48.32669000000001,0 -64.76967999999999,48.17476000000001,0 -65.32375999999999,47.96921,0 -66.3727,48.08661000000001,0 -66.8437,47.99666,0 -66.9436,47.96305000000001,0 -66.94694,47.89916000000001,0 -67.04723,47.93083,0 -67.18859999999999,47.88388000000001,0 -67.32278000000001,47.89638,0 -67.35665,47.84166,0 -67.60417,47.9361,0 -67.61221000000001,47.99749000000001,0 -68.1161,48.00194000000001,0 -68.12027000000001,47.92665000000001,0 -68.37277,47.92471000000001,0 -68.38028,47.5586,0 -68.57611,47.42471,0 -69.05359,47.29379,0 -69.03612,47.25722,0 -68.88779,47.18804,0 -68.49805000000002,47.30555,0 -68.39751,47.29527,0 -68.37305000000001,47.34722000000001,0 -68.30665000000001,47.36444,0 -68.18582000000001,47.33277,0 -67.79333,47.07028,0 -67.77251,45.95750000000001,0 -67.74695,45.92917,0 -67.79028,45.88194000000001,0 -67.75723000000001,45.83333,0 -67.80638,45.78472,0 -67.79916,45.7011,0 -67.71751,45.69137,0 -67.65666,45.63055000000001,0 -67.41249999999999,45.58693,0 -67.41083,45.52999,0 -67.48528,45.49444,0 -67.4164,45.39888,0 -67.46278,45.2761,0 -67.34193999999999,45.14999,0 -67.27501,45.19887,0 -67.20654,45.18303,0 -67.15837999999999,45.17419,0 -67.09289,45.09019000000001,0 -66.97331,44.91223,0 -66.95337000000001,44.82681,0 -67.11709999999999,44.69726,0 -67.50364999999999,44.47953,0 -67.50364999999999,40.24134,0 -67.50364999999999,35.24134,0 -67.50364999999999,30.24134,0 -67.50364999999999,25.24134,0 -67.50364999999999,20.24133,0 -71.75587,20.2411,0 -71.75418000000001,19.70582,0 -71.69168000000001,19.48584,0 -71.69862000000001,19.40027,0 -71.76669,19.33195,0 -71.69611999999999,19.24167,0 -71.62918000000001,19.21972,0 -71.63502,19.15806,0 -71.75,19.01972,0 -71.85605,18.94783,0 -71.78889,18.96277,0 -71.71945,18.88389,0 -71.71584,18.74972,0 -71.83168000000001,18.6225,0 -72.00306999999999,18.60083,0 -71.88500999999999,18.47721,0 -71.9075,18.43777,0 -71.69473000000001,18.32222,0 -71.76918000000001,18.20222,0 -71.74473999999999,18.13389,0 -71.76786,18.0385,0 -71.76823,17.5584,0 -71.77196000000001,12.5584,0 -71.25400999999999,12.4433,0 -71.06217,12.23228,0 -71.08137000000001,12.00208,0 -71.32471,11.85305,0 -71.38473999999999,11.82889,0 -71.52943999999999,11.78005,0 -71.97723000000001,11.665,0 -72.26000999999999,11.15445,0 -72.34113000000001,11.165,0 -72.49306,11.12111,0 -72.55862000000001,10.97389,0 -72.66528,10.87277,0 -72.76028,10.65139,0 -72.90501999999999,10.4425,0 -72.98723,9.914165000000001,0 -72.96223000000001,9.836945999999999,0 -73.09279000000001,9.585557,0 -73.16833,9.536111999999999,0 -73.29834,9.284715,0 -73.38361999999999,9.189719,0 -73.37195,9.166949000000001,0 -73.21890000000001,9.171386999999999,0 -73.00639,9.304442,0 -72.94223,9.104165,0 -72.81696,9.145277,0 -72.76819,9.111428,0 -72.7903,9.083042000000001,0 -72.66446999999999,8.641107000000002,0 -72.38417,8.363051000000001,0 -72.38557,8.25389,0 -72.32529,8.095553000000001,0 -72.33362,8.049446,0 -72.40252,8.041949000000001,0 -72.42307,7.985835000000001,0 -72.48139999999999,7.947783,0 -72.44334000000001,7.828055000000001,0 -72.47168000000001,7.491941000000001,0 -72.39917,7.406108,0 -72.18333,7.382781000000001,0 -72.15085000000002,7.339715000000001,0 -72.14528,7.198614,0 -72.06611,7.062417000000001,0 -71.9268,7.009748000000001,0 -71.83278,6.987221,0 -71.76557,7.005555,0 -71.76418,7.032221,0 -71.60695,7.059716,0 -71.45778,7.015832,0 -71.29556,7.033338000000001,0 -71.26555999999999,6.98778,0 -71.18668,6.962769000000001,0 -71.12806999999999,6.998895,0 -71.02501,6.984449000000001,0 -70.89001,7.074998,0 -70.70251,7.108056,0 -70.61279,7.072496,0 -70.56362,7.089444000000001,0 -70.51279,7.015553000000001,0 -70.30083999999999,6.939161,0 -70.11917,6.975836,0 -69.42944,6.118605000000001,0 -69.32501000000002,6.153605,0 -69.24529,6.081389000000001,0 -69.0564,6.21611,0 -68.63721,6.133895,0 -68.4539,6.190550000000002,0 -68.31362,6.166944,0 -68.15224000000001,6.223338000000001,0 -67.99500999999999,6.207777,0 -67.82278,6.30945,0 -67.56834000000001,6.264719000000001,0 -67.45445000000001,6.193052,0 -67.49199,6.118659000000001,0 -67.41280999999999,5.992460000000001,0 -67.62223,5.787503,0 -67.65195,5.676390000000001,0 -67.61306,5.539166,0 -67.64944,5.478335,0 -67.80279,5.385563,0 -67.85062000000001,5.301639,0 -67.81168,5.217497,0 -67.82861,5.112216,0 -67.79056,5.054697,0 -67.87612,4.530833,0 -67.79194,4.427495000000001,0 -67.80556,4.227498,0 -67.70851,4.046957,0 -67.61333999999999,3.754719,0 -67.49195,3.726395000000001,0 -67.38779,3.493052,0 -67.30723999999999,3.455559,0 -67.29056,3.3975,0 -67.38417,3.259161000000001,0 -67.43556,3.253896,0 -67.84305999999999,2.868335000000001,0 -67.85506,2.789783,0 -67.75167999999999,2.840003,0 -67.60695,2.795552,0 -67.56778,2.683341,0 -67.50333999999999,2.67722,0 -67.32056,2.468339,0 -67.19251,2.392496,0 -67.17307,2.341665,0 -67.21084999999999,2.240558,0 -67.16446999999999,2.135556,0 -67.10945,2.094444,0 -67.13361999999999,1.987785,0 -66.99168,1.695833,0 -66.87045000000001,1.22094,0 -67.07668,1.173338,0 -67.07250999999999,1.625282,0 -67.33501,2.111111,0 -67.42417,2.143889,0 -67.64418000000001,1.999997,0 -67.80556,1.788335,0 -67.91474,1.745279,0 -68.00502,1.763054,0 -68.08945,1.933613,0 -68.17083,1.973889,0 -68.18668,2.020005,0 -68.27724000000001,1.837781,0 -68.22917,1.823336,0 -68.23278999999999,1.775555,0 -68.17583999999999,1.763054,0 -68.15306,1.724165,0 -69.37793000000001,1.726146,0 -69.55584000000002,1.784159,0 -69.63139,1.737782,0 -69.71693999999999,1.748051,0 -69.78695999999999,1.706111,0 -69.84610000000001,1.710459,0 -69.84222,1.062215,0 -69.76501,1.09584,0 -69.70473,1.059443,0 -69.60196999999999,1.080836,0 -69.4464,1.062215,0 -69.42084000000001,1.026107,0 -69.34361000000001,1.074716,0 -69.22417,0.9952825,0 -69.20278999999999,0.9066595,0 -69.14001,0.8669425,0 -69.18250999999999,0.7244455,0 -69.13863000000001,0.6866634,0 -69.13721,0.6299995000000001,0 -69.28695999999999,0.6208375,0 -69.29389999999999,0.6677815,0 -69.35001,0.6369475,0 -69.46417,0.7402855,0 -69.56723,0.7011085,0 -69.60779000000001,0.6463885,0 -69.67834000000001,0.6788875,0 -69.79779000000001,0.6002815000000001,0 -70.04416999999999,0.5908405,0 -70.05807000000002,-0.1575005,0 -69.92834000000002,-0.3094475,0 -69.85167,-0.3352775,0 -69.74612000000001,-0.4522145000000001,0 -69.60751000000001,-0.5175005,0 -69.57278,-0.6366605,0 -69.61472999999999,-0.7511135000000001,0 -69.5625,-0.8080565,0 -69.52916999999999,-0.9244446000000001,0 -69.43473,-0.9941676,0 -69.44198,-1.017811,0 -69.4389,-1.083889,0 -69.38084000000002,-1.174447,0 -69.37946,-1.35028,0 -69.46056,-1.496107,0 -69.95692,-4.236876,0 -69.96417,-4.290004,0 -70.01862,-4.339161000000001,0 -70.0741,-4.313079,0 -70.10695,-4.253058,0 -70.19410000000001,-4.333177,0 -70.30667,-4.244716,0 -70.32389000000001,-4.136662,0 -70.35973,-4.16584,0 -70.43333,-4.131667,0 -70.51195,-4.180285,0 -70.54528999999999,-4.138606,0 -70.58414999999999,-4.195558,0 -70.6289,-4.15666,0 -70.62000999999999,-4.114998,0 -70.68391,-4.173337,0 -70.76584,-4.146390000000001,0 -70.83474,-4.187781,0 -70.83056999999999,-4.234447000000001,0 -70.95029,-4.38139,0 -70.98862,-4.340557,0 -71.01140000000001,-4.382226,0 -71.08334000000001,-4.363605,0 -71.12918000000001,-4.398894,0 -71.16946,-4.357782,0 -71.20029000000001,-4.410559,0 -71.23296000000002,-4.374621000000001,0 -71.29056,-4.383055,0 -71.29528999999999,-4.433608,0 -71.3539,-4.457223,0 -71.38223000000001,-4.430836,0 -71.47083000000001,-4.438333,0 -71.51055,-4.481389,0 -71.60722,-4.498056,0 -71.62306,-4.475835,0 -71.64474,-4.509162,0 -71.69057000000001,-4.473892,0 -71.90249,-4.518189,0 -72.02806,-4.612501,0 -72.06657,-4.606092,0 -72.12917,-4.693609,0 -72.235,-4.753333,0 -72.37529000000001,-4.784446,0 -72.40611,-4.878891,0 -72.60916,-5.000833,0 -72.62529000000001,-5.054166,0 -72.74139,-5.050837,0 -72.89028999999999,-5.123610000000001,0 -72.91750999999999,-5.151105,0 -72.88417,-5.258052,0 -72.96278,-5.458051000000001,0 -72.94251,-5.543334,0 -72.96335000000001,-5.65416,0 -73.12612,-5.867496,0 -73.22974000000001,-6.093613,0 -73.16307000000001,-6.207229,0 -73.12390000000001,-6.447223,0 -73.19168000000001,-6.552783000000001,0 -73.35306,-6.576949,0 -73.67639,-6.804441000000002,0 -73.75446,-6.894163,0 -73.79513000000002,-7.122628,0 -72.63779,-7.603335,0 -70.35722,-8.160553,0 -68.65723,-9.066394000000001,0 -66.83056000000001,-9.838054,0 -66.64025,-9.91827,0 -66.88806,-10.08639,0 -67.08085,-10.27194,0 -67.17444999999999,-10.325,0 -67.32417,-10.32084,0 -67.33334000000001,-10.37,0 -67.41833,-10.38306,0 -67.46363,-10.45555,0 -67.61168000000001,-10.53222,0 -67.70334,-10.69573,0 -67.74557,-10.7125,0 -67.84557,-10.6575,0 -68.06111000000001,-10.67639,0 -68.28055999999999,-10.98,0 -68.3914,-11.00805,0 -68.41750999999999,-11.04444,0 -68.51694999999999,-11.05445,0 -68.58346,-11.10614,0 -68.77473000000001,-11.13639,0 -68.78084,-11.065,0 -68.74760000000001,-11.01038,0 -68.78583999999999,-10.99223,0 -68.90028,-11.01528,0 -69.39529,-10.92695,0 -69.56751,-10.95055,0 -68.925,-11.99667,0 -68.6739,-12.50115,0 -68.78084,-12.61778,0 -68.73139999999999,-12.66972,0 -68.75666,-12.71472,0 -68.84668000000002,-12.73278,0 -68.97362,-12.86334,0 -68.96335000000001,-13.02667,0 -68.96194,-13.51305,0 -69.01472,-13.63834,0 -69.08778,-13.66333,0 -69.00306000000001,-13.75445,0 -68.97751,-13.96166,0 -68.89558,-14.03389,0 -68.85306,-14.19917,0 -68.99918,-14.24334,0 -68.98139999999999,-14.37917,0 -69.15779000000001,-14.50528,0 -69.1575,-14.57555,0 -69.2289,-14.57639,0 -69.24195,-14.73028,0 -69.36639,-14.8025,0 -69.34945999999999,-14.895,0 -69.38139,-14.97,0 -69.13695,-15.24584,0 -69.26835,-15.32695,0 -69.27584,-15.40417,0 -69.32696,-15.43055,0 -69.42195,-15.61805,0 -69.21194,-16.16055,0 -69.13418,-16.22362,0 -68.96501000000001,-16.20944,0 -68.84084,-16.29555,0 -68.82252,-16.33973,0 -68.99084000000001,-16.41973,0 -69.04167000000001,-16.54112,0 -69.01112000000001,-16.65695,0 -69.17001000000001,-16.72389,0 -69.34334,-16.9825,0 -69.41112,-17.02056,0 -69.38834,-17.055,0 -69.61806,-17.18667,0 -69.6562,-17.28724,0 -69.59084,-17.29528,0 -69.50112,-17.37889,0 -69.49973,-17.50528,0 -69.68389000000001,-17.66361,0 -69.81473,-17.65223,0 -69.83223,-17.67694,0 -69.84972000000001,-17.75418,0 -69.77000000000001,-17.96889,0 -69.84141,-18.12861,0 -69.95113000000001,-18.24278,0 -70.14111,-18.32333,0 -70.35583,-18.31973,0 -70.40549,-18.34854,0 -70.31001000000001,-18.43445,0 -70.35778999999999,-18.78417,0 -70.27166,-19.14139,0 -70.28862,-19.29444,0 -70.19695,-19.50278,0 -70.19862000000001,-19.57694,0 -70.23002,-19.58667,0 -70.15167,-19.71445,0 -70.1664,-19.80695,0 -70.13612000000001,-19.86389,0 -70.12612,-20.07361,0 -70.16528,-20.20389,0 -70.13306,-20.27334000000001,0 -70.19056999999999,-20.36778,0 -70.16724000000001,-20.45166,0 -70.20473,-20.52916,0 -70.21223000000001,-20.79973,0 -70.13918,-20.87556,0 -70.17001000000001,-21.01083,0 -70.12306,-21.08139,0 -70.06473,-21.29639,0 -70.09529000000001,-21.33667,0 -70.05334000000002,-21.42565,0 -70.08446000000001,-21.48473,0 -70.06585,-21.52361,0 -70.15416999999999,-21.66694,0 -70.14805,-21.85556,0 -70.19334000000001,-21.88805,0 -70.16833,-21.94195,0 -70.30750999999999,-22.75806,0 -70.28917,-22.90139,0 -70.38862,-23.06083,0 -70.48112000000002,-23.0825,0 -70.50917,-23.01055,0 -70.57584,-23.07166,0 -70.5514,-23.16250000000001,0 -70.59361,-23.22222,0 -70.58474,-23.42389,0 -70.62306,-23.49973,0 -70.54667000000001,-23.52112,0 -70.48390000000001,-23.45028,0 -70.4164,-23.49305,0 -70.38943999999999,-23.59723,0 -70.51862,-23.86084,0 -70.50306999999999,-24.16556,0 -70.5825,-24.52667,0 -70.55695,-24.60889,0 -70.58064000000002,-24.70552,0 -72.11279999999999,-24.70552,0 -77.11279,-24.70552,0 -82.11279,-24.70552,0 -82.11279,-27.41935000000001,0 -82.11279,-32.41934,0 -82.11279,-37.41935,0 -78.60896,-37.41935,0 -73.60896,-37.41935,0 -73.60196999999999,-37.51333,0 -73.68722,-37.60111,0 -73.66945,-37.72611000000001,0 -73.53807000000001,-37.87333,0 -73.4539,-38.05556,0 -73.54224000000001,-38.41833,0 -73.44196,-38.76750000000001,0 -73.43307,-38.69168,0 -73.24779,-39.22223,0 -73.22501,-39.21945,0 -73.2353,-39.18584,0 -73.2075,-39.21556,0 -73.24597,-39.38584,0 -73.2225,-39.41473,0 -73.25751,-39.4539,0 -73.2439,-39.49139,0 -73.34557000000001,-39.66417,0 -73.40443999999999,-39.69167,0 -73.37973,-39.73639,0 -73.38251,-39.91445,0 -73.45168,-39.8325,0 -73.71445,-39.97862,0 -73.65807,-40.11028,0 -73.73112,-40.18,0 -73.71709,-40.20832,0 -73.75029000000001,-40.25723,0 -73.78445000000001,-40.42362,0 -73.74751000000001,-40.52472,0 -73.85778999999999,-40.6264,0 -73.84278999999999,-40.68696,0 -73.94778,-40.86389,0 -73.89806,-40.90639,0 -73.99500999999999,-40.97000000000001,0 -73.97806,-41.1189,0 -73.85890000000001,-41.33973,0 -73.87445000000001,-41.47333,0 -73.82390000000001,-41.51723,0 -73.82167,-41.56556,0 -73.59166999999999,-41.6025,0 -73.49195,-41.52084,0 -73.56417999999999,-41.61362,0 -73.68279,-41.62473,0 -73.75281000000001,-41.75128,0 -73.8556,-41.76842,0 -74.0545,-41.73290000000001,0 -74.18946,-42.17331,0 -74.43097,-43.22105,0 -74.87848,-43.52649,0 -75.18392,-44.80154,0 -75.32599,-46.32876,0 -75.71666999999999,-46.56318,0 -75.75219,-46.80824,0 -75.65273000000001,-46.92189000000001,0 -75.60303000000002,-48.05132,0 -75.68826,-48.46332,0 -75.73088,-49.03514,0 -75.36150000000001,-51.61010000000001,0 -74.76482000000001,-52.7786,0 -73.13816,-54.55088000000001,0 -71.60384000000001,-54.89185,0 -70.14055000000001,-55.36066000000002,0 -69.11057,-55.64479,0 -67.49276999999999,-55.95932,0 -67.49276999999999,-59.90001,0 -67.49276999999999,-64.90000000000001,0 -67.49276999999999,-69.90000000000001,0 -67.49276999999999,-74.90000000000001,0 -67.49276999999999,-79.90000000000001,0 -67.49276999999999,-84.89999,0 -67.49276999999999,-89.90000000000001,0 -68.43494,-89.90000000000001,0 -69.37713000000001,-89.90000000000001,0 -70.3193,-89.90000000000001,0 -71.26147,-89.90000000000001,0 -72.20367,-89.90000000000001,0 -73.14584000000001,-89.90000000000001,0 -74.08802,-89.90000000000001,0 -75.03020000000001,-89.90000000000001,0 -75.97238,-89.90000000000001,0 -76.91455000000001,-89.90000000000001,0 -77.85674,-89.90000000000001,0 -78.79893,-89.90000000000001,0 -79.74109,-89.90000000000001,0 -80.68326999999999,-89.90000000000001,0 -81.62546,-89.90000000000001,0 -82.56762999999999,-89.90000000000001,0 -82.56790000000001,-89.29246999999999,0 -82.57008,-84.29246999999999,0 -82.57227,-79.29246999999999,0 -82.57349000000001,-76.49247000000001,0 -82.57568000000001,-71.49247,0 -82.57787,-66.49247,0 -82.58005,-61.49247,0 -82.58223,-56.49247,0 -82.58441999999999,-51.49246999999999,0 -82.5866,-46.49247,0 -82.58879000000002,-41.49248,0 -82.58971,-39.39247,0 -82.59188,-34.39248,0 -82.59408000000001,-29.39247,0 -82.59626,-24.39248,0 -82.59696,-21.69599,0 -82.59827000000001,-16.69599,0 -82.59957,-11.69599,0 -82.60088,-6.695992,0 -82.60218,-1.695988,0 -82.60348999999999,3.304008,0 -82.60478999999999,8.304010999999999,0 -82.72168000000002,8.317214,0 -82.84778,8.270838,0 -82.8753,8.190837,0 -82.85028,8.060003,0 -82.86584000000001,8.020835999999999,0 -82.89883,8.025668,0 -82.88612000000001,8.103338000000003,0 -82.93085000000001,8.254719,0 -83.04834000000001,8.326105999999999,0 -82.82993,8.474651,0 -82.83196,8.626661,0 -82.91446999999999,8.762786999999999,0 -82.85778999999999,8.809163,0 -82.87779000000001,8.827496999999999,0 -82.71084999999999,8.931113,0 -82.88890000000001,9.074448,0 -82.93047,9.063126000000001,0 -82.93473,9.471663,0 -82.83338999999999,9.498041000000001,0 -82.87575,9.558963,0 -82.83307000000001,9.612215000000001,0 -82.74890000000001,9.584163,0 -82.66473000000001,9.495278000000001,0 -82.60478999999999,9.495170999999999,0 -82.60478999999999,13.18773,0 -82.60478999999999,18.18773,0 -86.05847,18.18773,0 -86.05847,19.16667,0 -86.05847,24.16667,0 -82.67905,24.16667,0 -82.65622999999999,27.34336,0 -82.86282,27.87009,0 -82.81197,28.15403,0 -82.66786999999999,28.52699,0 -82.65092,28.70922,0 -82.81621,29.14997,0 -82.97725000000001,29.1754,0 -83.04929,29.13302,0 -83.13406000000001,29.26864,0 -83.40105,29.5102,0 -83.42648,29.65429,0 -83.58752,29.756,0 -83.60023000000001,29.82804,0 -83.73586,29.94671,0 -84.00285,30.07809,0 -84.18716000000001,30.06915,0 -84.32649000000001,29.96329,0 -84.34405,29.89086,0 -84.67976,29.76196,0 -84.76974,29.69064,0 -84.98586,29.60068,0 -85.05280000000001,29.59025,0 -85.21616,29.69783,0 -85.11682,29.77658,0 -85.04214,29.7827,0 -85.02878,29.81974,0 -85.02682,29.97955,0 -85.11667,30.03346,0 -85.14525999999999,30.13059,0 -85.01819999999999,30.37397,0 -85.029,30.42799,0 -84.98756,30.44610000000001,0 -84.98378,30.54212,0 -84.86639000000001,30.71832,0 -84.91332,30.75332,0 -84.93167,30.88832,0 -85.00448,31.00173,0 -85.00557000000001,31.06222,0 -85.09972000000001,31.17888,0 -85.10889,31.27111,0 -85.03751,31.54083,0 -85.14111,31.82611,0 -85.06027,32.00388,0 -85.05083000000001,32.13832,0 -84.89417000000002,32.26944,0 -84.99695,32.33583,0 -84.96278,32.42333,0 -84.99306,32.51193,0 -85.10445,32.64415,0 -85.13276999999999,32.7786,0 -85.17416,32.82417000000001,0 -85.3925,33.99749,0 -85.62070000000001,34.9901,0 -85.46741,34.99005,0 -85.36797,35.06706,0 -85.38441,35.15899,0 -85.17104000000001,35.46849,0 -85.14761,35.45309,0 -85.11647000000001,35.48328,0 -85.13974,35.50185,0 -85.10691000000001,35.58106,0 -84.97051999999999,35.6754,0 -84.89986,35.77536,0 -84.76385999999999,35.82634,0 -84.67876,35.91422,0 -84.79865,36.09025,0 -84.9053,36.15482,0 -84.866,36.28408,0 -84.78586,36.29548,0 -84.77486,36.3341,0 -84.65366,36.39601,0 -84.79111,36.60536,0 -84.67641,36.7645,0 -84.59557000000001,36.80482,0 -84.58721,36.87076,0 -84.68555000000001,36.98052,0 -84.77699,36.95735,0 -84.84885000000001,36.99782,0 -84.92086000000001,37.05594,0 -84.91865,37.12032000000001,0 -84.97011999999999,37.11,0 -85.0663,37.20251,0 -85.07001,37.25666,0 -85.17966,37.31235,0 -85.20011,37.26906,0 -85.32208,37.24111,0 -85.36129,37.19164,0 -85.41483,37.21706,0 -85.39812000000002,37.2369,0 -85.47422,37.34881,0 -85.58540000000001,37.42731,0 -85.59048,37.46286,0 -85.64707,37.4683,0 -85.66943000000001,37.41729,0 -86.05628,37.44679,0 -86.12286,37.5709,0 -86.28140999999999,37.59924,0 -86.16003000000001,37.80325,0 -86.50314,38.05159,0 -86.52275,37.92781000000001,0 -86.59833,37.92101,0 -86.64558,37.84594,0 -86.66867000000001,37.91314000000001,0 -86.75384,37.8983,0 -86.82632,37.99151,0 -86.77476,38.01123,0 -86.78015000000001,38.10248,0 -86.81465,38.13544,0 -86.79993,38.20176,0 -87.30332,38.23492000000001,0 -87.319,38.37801,0 -87.4182,38.37928,0 -87.41956,38.43461,0 -87.47684,38.46685,0 -87.47975,38.53218,0 -87.50431,38.49449,0 -87.54741,38.51014,0 -87.61599,38.45092000000001,0 -87.75163000000001,38.41816,0 -87.75584000000001,38.45804,0 -87.64584000000001,38.49499,0 -87.66583,38.55999,0 -87.51195,38.74527,0 -87.55777000000001,38.86805,0 -87.53416,38.95333,0 -87.58611999999999,38.99554,0 -87.59389,39.0561,0 -87.66861,39.10999,0 -87.65916,39.15582,0 -87.58945,39.20638,0 -87.62639,39.30471,0 -87.53278,39.35695000000001,0 -87.52641,40.74535,0 -87.10191,40.74436,0 -87.10338,40.84467,0 -86.98604,40.84695,0 -86.98653,40.9182,0 -86.93117,40.91845,0 -86.93285,41.17447,0 -86.88367,41.26608,0 -86.76918000000001,41.30227,0 -86.64751,41.43719,0 -86.52912000000001,41.43995,0 -86.5304,41.52581,0 -86.48571,41.58063,0 -86.48599,41.65967,0 -86.52528000000001,41.66103,0 -86.52540999999999,41.75969,0 -87.21111000000002,41.76225,0 -87.02119000000002,42.48526,0 -87.15730000000001,43.41102,0 -87.06783,43.96139,0 -86.83409,44.59161,0 -86.71111000000001,44.83602,0 -86.57338000000001,44.99793,0 -86.24245000000001,45.23384000000001,0 -86.7586,45.44141000000001,0 -87.08848999999999,45.44498,0 -87.27666000000001,45.25579,0 -87.40428,45.19296000000001,0 -87.43716000000001,45.07106,0 -87.64861999999999,45.11694,0 -87.735,45.18721000000001,0 -87.71666999999999,45.26721,0 -87.65055,45.35194,0 -87.67610000000002,45.38666000000001,0 -87.88084000000001,45.36639,0 -87.87027000000002,45.44221000000001,0 -87.8036,45.50139,0 -87.83,45.57166000000001,0 -87.78751,45.59664999999999,0 -87.82556,45.65526000000001,0 -87.80029,45.68415,0 -87.90083,45.75944,0 -88.11834,45.80527000000001,0 -88.08139,45.87777,0 -88.11583000000002,45.93221,0 -88.52029000000002,46.02499,0 -88.64194999999999,46.00111,0 -88.83806,46.035,0 -89.11749,46.14805,0 -90.12388,46.33972,0 -90.22278,46.50722,0 -90.33277,46.55915,0 -90.39612,46.54695,0 -90.41302,46.5882,0 -89.49317000000001,48.00332000000001,0 -89.59805,48.00918,0 -89.60860000000001,51.43138,0 -89.62405000000001,56.43136,0 -89.30607999999999,56.62564,0 -89.13284,56.86485,0 -89.03778000000001,56.83611,0 -88.87944,56.85804000000001,0 -88.65472000000001,56.69638,0 -88.48999000000001,56.64222000000001,0 -88.3536,56.55332000000001,0 -87.98277000000002,56.44165000000002,0 -87.94305,56.37749000000001,0 -87.72389,56.20388,0 -87.70805,56.1561,0 -87.58362,56.0761,0 -87.61583,56.00277000000001,0 -87.54861,56.04999,0 -87.35195,55.99276,0 -87.3625,55.94666,0 -86.97945,55.93166,0 -86.95889,55.90582,0 -86.47778,55.80888,0 -86.27722000000001,55.72887,0 -85.94445,55.69778,0 -85.56947,55.5586,0 -85.49944000000002,55.47222000000001,0 -85.39335,55.40889,0 -85.00144,55.29702000000001,0 -85.00126,56.95695000000001,0 -85.00069000000001,61.95694000000001,0 -86.66628,62.44327,0 -87.52079000000001,63.03194,0 -87.6917,63.6016,0 -87.25494000000001,64.20925,0 -86.74223000000002,64.72197,0 -86.11559,65.84233,0 -85.64087000000001,66.05121,0 -84.9991,66.25666,0 -84.9991,67.98071,0 -84.99907,72.98072000000001,0 -84.99903999999999,77.98072999999999,0 -84.99902,82.98072000000001,0 -82.50320000000001,82.97593999999999,0 -82.5051,84.90001,0 -82.51007,89.90000000000001,0 -81.57213,89.90000000000001,0 -80.63419,89.90000000000001,0 -79.69624,89.90000000000001,0 -78.75831000000001,89.90000000000001,0 -77.82037,89.90000000000001,0 -76.88243,89.90000000000001,0 -75.94450000000001,89.90000000000001,0 -75.00655,89.90000000000001,0 -74.06861000000001,89.90000000000001,0 -73.13066000000001,89.90000000000001,0 -72.19273,89.90000000000001,0 -71.25478,89.90000000000001,0 -70.31685,89.90000000000001,0 -69.37891000000001,89.90000000000001,0 -68.44096999999999,89.90000000000001,0 -67.50302000000001,89.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -04:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-4);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0400</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-52.50595000000001,64.94479,0 -52.50631,59.94478000000001,0 -52.50666,54.94477,0 -52.50703,49.94478,0 -52.50738,44.94477,0 -52.50773000000001,39.94478,0 -52.50809000000001,34.94477,0 -52.50845000000001,29.94478,0 -52.50846,29.83971,0 -52.50841,24.83971,0 -52.50838,19.83971,0 -52.50835,15.23971,0 -52.5083,10.2397,0 -52.50826000000001,5.239709,0 -52.50826000000001,5.098526,0 -52.91902,5.485283,0 -54.47205,5.976981000000002,0 -55.15563000000001,6.048935,0 -56.97724,6.000021,0 -57.06833000000001,5.942223,0 -57.13139,5.773337,0 -57.16833,5.537222,0 -57.24850000000001,5.486112,0 -57.27223000000001,5.38028,0 -57.33555,5.313338,0 -57.28723000000002,5.310557,0 -57.27916,5.223339,0 -57.25639000000001,5.22278,0 -57.26307,5.26778,0 -57.23521000000001,5.259942,0 -57.18862,5.16861,0 -57.22889,5.140835,0 -57.25471,5.175279,0 -57.29695,5.160285000000001,0 -57.32305,5.069717,0 -57.28889,5.016663,0 -57.42028,4.989168,0 -57.68806000000001,5.010282,0 -57.77140000000001,4.922774,0 -57.84112,4.927778,0 -57.92333000000001,4.821948,0 -57.83862,4.654718,0 -57.95889,4.403339,0 -57.9539,4.28777,0 -58.0714,4.159161,0 -58.04723,4.008338,0 -57.87668,3.80916,0 -57.83583,3.664998000000001,0 -57.73473,3.610277,0 -57.65445,3.518612,0 -57.63721,3.455,0 -57.66693999999999,3.395555,0 -57.64223000000001,3.356387,0 -57.50334,3.351393,0 -57.48083000000001,3.323888999999999,0 -57.41057000000001,3.382496,0 -57.35028,3.355001,0 -57.30139,3.377501000000001,0 -57.28528,3.143888000000001,0 -57.24111000000001,3.138057,0 -57.21111,2.928051,0 -57.17917,2.914443,0 -57.21333,2.878892000000001,0 -57.20139000000001,2.821391000000001,0 -57.09806,2.749166,0 -56.91778,2.428056,0 -56.92806,2.391948,0 -56.87112,2.348891,0 -56.87833000000001,2.297781,0 -56.8064,2.250836,0 -56.81057,2.193336,0 -56.69187,2.028734,0 -56.56279,2.023892,0 -56.47063,1.944503,0 -56.03723,1.836944,0 -55.94667,1.853333,0 -55.90417,1.89305,0 -55.90139000000001,2.045285,0 -56.03639,2.177505,0 -56.0425,2.232782,0 -56.13807,2.266947,0 -56.08917,2.355281,0 -56.04528000000001,2.344445,0 -55.99195000000001,2.4075,0 -55.96584,2.53277,0 -55.71028,2.399165,0 -55.58473,2.438334,0 -55.38,2.428056,0 -55.32249,2.516948,0 -55.24139000000001,2.499164,0 -55.13807000000001,2.573891000000001,0 -55.11195,2.527217,0 -54.94834000000001,2.617496,0 -54.97223,2.554721,0 -54.86750000000001,2.440278,0 -54.77539,2.456846,0 -54.73722000000001,2.425833,0 -54.71111,2.274723,0 -54.78945,2.163609,0 -54.75029,2.076659,0 -54.80084,2.017781,0 -54.76307,1.986947,0 -54.735,1.768607,0 -54.36111,1.763054,0 -54.10139,1.613051,0 -54.08278000000001,1.505556,0 -53.98807000000001,1.520558,0 -53.85778,1.383605,0 -53.82723,1.384163,0 -53.82167,1.419722,0 -53.75307000000002,1.392497,0 -53.73695,1.437786,0 -53.69694000000001,1.410281,0 -53.66417,1.429442,0 -53.65639000000001,1.363058,0 -53.55306,1.367783,0 -53.54889,1.243889,0 -53.4325,1.25666,0 -53.40584000000001,1.187504,0 -53.46723,1.142496,0 -53.41861999999999,0.9422185000000001,0 -53.28250000000001,0.7902805000000003,0 -53.11723000000001,0.7422205,0 -53.12889,0.3866664,0 -53.04139000000001,0.2474995,0 -53.0061,0.1311115,0 -53.01583,0.03500046,0 -52.97112,-0.01638954,0 -52.92334,-0.1863905,0 -52.87194,-0.2033375,0 -52.81223000000001,-0.1780565,0 -52.63278,-0.3822215000000001,0 -52.62723,-0.5727785,0 -52.60056,-0.6144485,0 -52.53807,-0.6247175,0 -52.5066,-0.7438775,0 -52.50666,-0.7508345000000001,0 -52.51918000000001,-0.8797145,0 -52.38528000000002,-0.8791655,0 -52.37028000000001,-1.061388,0 -52.27112000000001,-1.133893,0 -52.11389000000001,-1.155286,0 -52.06945,-1.220284,0 -52.0475,-1.170838,0 -51.9775,-1.135279,0 -51.90139,-1.150552,0 -51.93501,-1.192501,0 -51.92334,-1.328608,0 -52.00557,-1.404163,0 -52.10778,-1.415835,0 -52.22139,-1.342504,0 -52.43805,-1.441108,0 -52.47639,-1.490553,0 -52.70722,-1.561105,0 -52.71278,-1.603333,0 -52.54084,-1.571949,0 -52.44971,-1.586944,0 -52.28528,-1.53361,0 -52.2275,-1.616392,0 -52.26806,-1.682218,0 -52.31139000000001,-1.627498,0 -52.34612,-1.674721,0 -52.26917,-1.813051,0 -52.31806000000001,-1.935559,0 -52.27139,-2.01916,0 -52.36806000000001,-2.098612,0 -52.26723,-2.061666,0 -52.25917,-2.161108,0 -52.15028,-2.358055,0 -52.20028,-2.407501,0 -52.12361000000001,-2.389717000000001,0 -52.03139,-2.59722,0 -52.06667000000001,-2.714715,0 -52.04999999999999,-2.809999,0 -51.94334000000001,-2.898334000000001,0 -51.82334,-3.100285,0 -51.61153,-3.147264,0 -51.63556000000001,-3.291661,0 -51.74472000000001,-3.35611,0 -51.66917,-3.471661,0 -51.74917,-3.562498000000001,0 -51.83223000000001,-3.609163,0 -51.92667,-3.553605999999999,0 -51.94445,-3.409444,0 -52.04611000000001,-3.294441,0 -52.15028,-3.235554,0 -52.15778,-3.34305,0 -52.23778,-3.510559,0 -52.35639000000001,-3.665836,0 -52.60722000000001,-3.860839,0 -52.63945,-4.193614000000001,0 -52.73806,-4.331107,0 -52.73,-4.376107,0 -52.66278000000001,-4.433338,0 -52.66223,-4.493052,0 -52.83056,-4.882780000000001,0 -52.92639,-5.008608,0 -52.86972,-5.210001,0 -52.86778000000001,-5.365558,0 -52.84084,-5.406949,0 -52.70612,-5.469165,0 -52.645,-5.688891,0 -52.53889,-5.840838000000001,0 -52.53807,-6.036390000000001,0 -52.43056000000001,-6.210279,0 -52.43055,-6.338053000000001,0 -52.36139000000001,-6.381109,0 -52.22473,-6.573888,0 -52.08528,-6.548607000000001,0 -52.01314,-6.657526,0 -51.9575,-6.613336,0 -51.78751,-6.690835,0 -51.71278000000002,-6.860278,0 -51.72723,-6.893613,0 -51.69805,-6.901659,0 -51.74084,-6.956110000000001,0 -51.65556000000002,-7.06222,0 -51.64500000000001,-7.158888000000001,0 -51.48695,-7.19416,0 -51.38807,-7.269723000000002,0 -51.27251,-7.274448000000001,0 -51.20167000000001,-7.342497,0 -51.21639,-7.419159,0 -51.18334,-7.543612,0 -51.23056,-7.623612,0 -51.10196000000001,-7.803054,0 -51.16889,-7.914447,0 -51.26918,-7.983613,0 -51.41695,-8.028333999999999,0 -51.43333000000001,-8.088058,0 -51.40862000000001,-8.134731,0 -51.44501,-8.193051000000001,0 -51.42722,-8.269164,0 -51.51973,-8.418888000000001,0 -51.52667000000001,-8.544447999999999,0 -51.44778000000001,-8.691948999999999,0 -51.33805,-8.772786,0 -51.1725,-8.998056,0 -51.12167,-9.258607,0 -51.07751,-9.31194,0 -51.1372,-9.797392,0 -50.23251000000001,-9.844165,0 -50.30944,-10.02889,0 -50.38167,-10.11029,0 -50.40862,-10.3075,0 -50.48278,-10.39361,0 -50.52278,-10.56334,0 -50.60667000000001,-10.65584,0 -50.5825,-10.73945,0 -50.62944,-10.82611,0 -50.61167,-10.88139,0 -50.63778,-10.9325,0 -50.60946,-11.06528,0 -50.66473000000001,-11.1425,0 -50.65917000000001,-11.24305,0 -50.74139,-11.46444,0 -50.73667,-11.52445,0 -50.64834,-11.60334,0 -50.71448,-11.74943,0 -50.67973000000001,-11.87,0 -50.64251,-11.88555,0 -50.68389,-12.04861,0 -50.67334000000001,-12.20223,0 -50.61972,-12.28444,0 -50.61361,-12.38416,0 -50.67362000000001,-12.62805,0 -50.62555,-12.65639,0 -50.59806,-12.81056,0 -50.4841,-12.84488,0 -50.48167000000001,-12.8475,0 -50.49806,-12.96056,0 -50.59028000000001,-13.07723,0 -50.56834,-13.22861,0 -50.59166,-13.29694,0 -50.75834,-13.53778,0 -50.78973000000001,-13.66806,0 -50.87056,-13.71861,0 -50.84555,-14.08639,0 -50.92694,-14.11972,0 -50.99918000000001,-14.40555,0 -50.96778000000001,-14.53083,0 -51.03833,-14.65973,0 -51.0975,-14.89111,0 -51.16028,-14.975,0 -51.25723,-15.02139,0 -51.29611,-15.01473,0 -51.31833000000001,-14.9625,0 -51.41251000000001,-14.99723,0 -51.63667000000001,-15.17556,0 -51.70361,-15.48194,0 -51.74973,-15.54084,0 -51.78861,-15.53417,0 -51.77973000000001,-15.63084,0 -51.87695,-15.80055,0 -51.90389,-15.8225,0 -51.96584,-15.80111,0 -52.03751,-15.87555,0 -52.24306000000001,-15.88028,0 -52.33055,-16.0425,0 -52.44056000000001,-16.09694,0 -52.43473,-16.27139,0 -52.57751,-16.34166,0 -52.63779000000001,-16.53889,0 -52.69139,-16.57639,0 -52.72084,-16.66471,0 -52.83222,-16.76417,0 -52.97334,-16.82111,0 -53.0275,-16.93778,0 -53.03973,-17.0525,0 -53.20166,-17.28473,0 -53.19084,-17.36723,0 -53.22778000000001,-17.49805,0 -53.12612000000001,-17.67167,0 -53.1139,-17.8889,0 -53.05389000000001,-18.01667,0 -53.05806,-18.28305,0 -53.02973,-18.37194,0 -52.98834000000001,-18.38889,0 -52.895,-18.34694,0 -52.75805,-18.4125,0 -52.8675,-18.57695,0 -52.89001,-18.67805,0 -52.63667,-18.72305,0 -52.47278,-18.70834,0 -52.34917,-18.81667,0 -52.18861,-18.83695,0 -52.06557,-18.94416,0 -51.89694,-18.995,0 -51.84166,-19.05361,0 -51.63223,-19.13334,0 -51.42306,-19.15806,0 -51.3064,-19.25889,0 -51.11418000000001,-19.29083,0 -50.92340000000001,-19.45669,0 -50.96278000000001,-19.475,0 -50.9325,-19.55667,0 -50.98112,-19.57166,0 -51.02361000000001,-19.7225,0 -50.98584000000001,-19.9075,0 -51.00643,-20.07929,0 -51.04611,-20.25028,0 -51.26055000000001,-20.31528,0 -51.46612,-20.53917,0 -51.58084000000001,-20.60028,0 -51.63001,-20.88028,0 -51.79223,-21.09139,0 -51.87111000000001,-21.14917,0 -51.84973,-21.26973,0 -51.87056000000001,-21.35029,0 -51.9625,-21.49778,0 -52.04973,-21.50278000000001,0 -52.09195,-21.55584,0 -52.03723,-21.64639,0 -52.05334,-21.71889,0 -52.15028,-21.78334,0 -52.17445,-21.85778,0 -52.29028,-21.96084,0 -52.36556,-22.10028000000001,0 -52.49723,-22.21611,0 -52.97528000000001,-22.48223,0 -53.15685,-22.70582,0 -53.57218000000002,-22.88526,0 -53.72139000000001,-23.30028,0 -53.96889000000001,-23.44695000000002,0 -54.08028000000001,-23.94806,0 -54.2439,-24.05361,0 -54.33778000000001,-24.14389,0 -54.24889,-24.35362,0 -54.33139000000001,-24.49278,0 -54.33055,-24.67945,0 -54.45250999999999,-25.01111,0 -54.45639,-25.08723,0 -54.42305,-25.14306,0 -54.61696,-25.43806,0 -54.59892,-25.57323,0 -54.58501,-25.64139000000001,0 -54.64556000000001,-25.66250000000001,0 -54.58584000000001,-25.81944,0 -54.61723,-25.89556,0 -54.60139,-25.95278,0 -54.665,-25.9825,0 -54.64083,-26.07139,0 -54.66166000000001,-26.15834,0 -54.63333999999999,-26.20028,0 -54.6675,-26.23888,0 -54.69278000000001,-26.42834,0 -54.79362,-26.52305,0 -54.77862,-26.61083,0 -54.80612,-26.66418,0 -54.92389,-26.68056,0 -54.95722,-26.77834,0 -55.05112,-26.80028,0 -55.12889,-26.86805,0 -55.12889,-26.94917,0 -55.27111,-26.9375,0 -55.41389,-26.98278000000001,0 -55.455,-27.09528,0 -55.53973,-27.105,0 -55.55667,-27.16166,0 -55.60196,-27.16834,0 -55.56751000000001,-27.24944,0 -55.59639000000001,-27.33806,0 -55.67557,-27.36862,0 -55.73612000000001,-27.43944,0 -55.84251,-27.40778,0 -55.89833000000001,-27.32918,0 -56.13778,-27.30889,0 -56.26805000000001,-27.3875,0 -56.27084,-27.46140000000001,0 -56.37806,-27.58834,0 -56.55,-27.4525,0 -56.67305,-27.46140000000001,0 -56.73695,-27.50694,0 -56.90862000000001,-27.41778,0 -57.07167000000001,-27.49195,0 -57.12944000000001,-27.48973000000001,0 -57.6264,-27.36916,0 -57.79139,-27.29223,0 -58.22,-27.26555,0 -58.60461000000001,-27.31692,0 -58.59888999999999,-27.245,0 -58.65573,-27.19161,0 -58.65278,-27.15834,0 -58.56417,-27.1175,0 -58.54639000000001,-27.04611,0 -58.51028,-27.05861,0 -58.47834,-26.93916000000001,0 -58.32167,-26.88223,0 -58.34584,-26.81722,0 -58.28389,-26.81139,0 -58.28584,-26.77195,0 -58.24639,-26.765,0 -58.23973,-26.64973,0 -58.18167,-26.65611,0 -58.16889000000001,-26.60279,0 -58.21917,-26.535,0 -58.18306000000001,-26.4614,0 -58.21139,-26.42640000000001,0 -58.16251,-26.31973,0 -58.17250000000001,-26.27305,0 -58.10389000000001,-26.23555,0 -58.1525,-26.18861,0 -58.11000000000001,-26.19806000000001,0 -58.07805,-26.12472,0 -57.85583000000001,-26.00195,0 -57.90361,-25.95833,0 -57.84973,-25.91833,0 -57.87445,-25.87861,0 -57.80361000000001,-25.83473,0 -57.81098,-25.77314,0 -57.74196,-25.72806,0 -57.77167,-25.7075,0 -57.75167,-25.67056,0 -57.57750999999999,-25.57889000000001,0 -57.55333000000001,-25.44834,0 -57.64029000000001,-25.37917,0 -57.7561,-25.17694,0 -57.87112000000001,-25.08778,0 -57.98083000000001,-25.07805000000001,0 -58.22139,-24.93916,0 -58.33833000000001,-24.99778,0 -58.45056,-24.85695000000001,0 -58.80334000000001,-24.78417,0 -59.06778,-24.60695000000001,0 -59.33889000000001,-24.48889,0 -59.47278000000001,-24.35306,0 -60.03806,-24.00972000000001,0 -60.28639,-24.0375,0 -60.57334000000001,-23.95111,0 -60.61389000000001,-23.89444,0 -60.85499999999999,-23.8725,0 -61.02473,-23.78612,0 -61.11639000000001,-23.66666,0 -61.11556000000001,-23.60362,0 -61.29472,-23.48917,0 -61.49694,-23.41361,0 -61.51722,-23.345,0 -61.73973000000001,-23.24362,0 -61.78167000000001,-23.1475,0 -61.98639,-23.01028000000001,0 -62.02944999999999,-22.89194,0 -62.18861,-22.71029,0 -62.19,-22.63,0 -62.25917000000001,-22.60445,0 -62.24694,-22.52889,0 -62.29056,-22.48055,0 -62.39918,-22.45278,0 -62.46028,-22.37971,0 -62.62417,-22.30694,0 -62.64377,-22.2389,0 -62.78946000000001,-22.12278,0 -62.81195,-21.99695,0 -63.68584000000001,-22.00306,0 -63.70057000000001,-22.03861,0 -63.73306,-22.00278,0 -63.94111,-22.00083,0 -64.12667,-22.41028,0 -64.25333999999999,-22.56778,0 -64.32472,-22.87361,0 -64.36,-22.74084,0 -64.45252000000001,-22.64834,0 -64.4314,-22.54778,0 -64.56612,-22.36139,0 -64.53889,-22.27639,0 -64.59473000000001,-22.21167,0 -64.98833999999999,-22.09083,0 -65.57861,-22.08472,0 -65.74806,-22.11166,0 -65.92612,-21.93333,0 -66.0414,-21.91667,0 -66.07751,-21.83195,0 -66.22301,-21.78051,0 -66.29333,-22.0825,0 -66.62806999999999,-22.21305,0 -66.6889,-22.19611,0 -66.73669,-22.2275,0 -66.77473000000001,-22.42695000000001,0 -66.92639,-22.47805,0 -66.96335000000001,-22.5225,0 -67.02251,-22.5239,0 -67.01221,-22.64279,0 -67.12306,-22.71805000000001,0 -67.18362000000001,-22.82166,0 -67.00084,-23.00278,0 -67.15085000000001,-23.39194000000001,0 -67.33584999999999,-24.02167,0 -68.25223,-24.39639,0 -68.30779,-24.4925,0 -68.38918,-24.50166,0 -68.44363,-24.62639,0 -68.48779,-24.6025,0 -68.56916,-24.78612,0 -68.54528999999999,-24.87361,0 -68.455,-24.90500000000001,0 -68.42419,-25.03361,0 -68.35197000000001,-25.11723,0 -68.42028999999999,-25.15279,0 -68.46502000000001,-25.14056,0 -68.50223,-25.17723,0 -68.5975,-25.44333,0 -68.39776999999999,-26.15611000000001,0 -68.56362,-26.30056,0 -68.58362,-26.50528,0 -68.29584,-26.89168,0 -68.31556999999999,-27.04194,0 -68.50306999999999,-27.07472,0 -68.58195000000002,-27.16973,0 -68.69667,-27.10750000000001,0 -68.81084000000001,-27.12056,0 -68.87723,-27.23083,0 -68.86585,-27.28806,0 -68.90472,-27.36667,0 -69.00696000000001,-27.45889,0 -69.01251000000001,-27.53168000000001,0 -69.08416000000001,-27.64583,0 -69.06556999999999,-27.69889,0 -69.10806,-27.72194,0 -69.18584,-27.96584,0 -69.30029999999999,-27.99944,0 -69.40640999999999,-28.20889,0 -69.47612,-28.19139,0 -69.53001999999999,-28.31223,0 -69.65537999999999,-28.40094,0 -69.6664,-28.57279,0 -69.74500999999999,-28.66055,0 -69.72307000000001,-28.80334,0 -69.79528999999999,-28.95112,0 -69.79389999999999,-29.09862,0 -69.90807,-29.14139,0 -70.03140000000001,-29.30639,0 -69.96056,-29.55029,0 -69.96834,-29.66112,0 -69.91501,-29.71778,0 -69.90555999999999,-29.79112,0 -69.91880000000001,-29.97406,0 -69.97083000000002,-30.08444,0 -69.85861,-30.10695000000001,0 -69.83168000000001,-30.19055,0 -69.87000000000001,-30.21084,0 -69.89751,-30.3225,0 -69.94416,-30.37639,0 -70.02972,-30.39778,0 -70.16334999999999,-30.36472,0 -70.1339,-30.43973,0 -70.20695000000001,-30.505,0 -70.32668,-30.88805,0 -70.32778999999999,-30.95834,0 -70.25502000000002,-31.03416,0 -70.32778999999999,-31.03972,0 -70.41335000000001,-31.15389,0 -70.44556,-31.09306,0 -70.47139,-31.09805,0 -70.52943999999999,-31.17444,0 -70.5611,-31.31416,0 -70.54224000000001,-31.37333,0 -70.58805,-31.56167,0 -70.47667,-31.72639,0 -70.45889,-31.8325,0 -70.41,-31.88084,0 -70.29056,-31.88668,0 -70.23445,-31.9425,0 -70.26251000000001,-32.04362,0 -70.36389,-32.02916,0 -70.38001,-32.05167,0 -70.32529,-32.13751,0 -70.31668999999999,-32.28333,0 -70.24432000000002,-32.30984,0 -70.23889,-32.4175,0 -70.16251,-32.46305,0 -70.12946,-32.56528,0 -70.17111,-32.60778,0 -70.14862000000001,-32.75249,0 -69.9875,-32.87889,0 -70.03528,-32.99834,0 -70.10139,-33.03001,0 -70.09889,-33.1725,0 -69.99583,-33.30194,0 -69.91916999999999,-33.23806,0 -69.80779,-33.27389,0 -69.77444,-33.38111,0 -69.82919,-33.53167,0 -69.8689,-33.55194,0 -69.88528,-33.66278,0 -69.84668000000001,-33.71751,0 -69.90611,-33.78778000000001,0 -69.88306,-33.87972,0 -69.90443999999999,-33.95166,0 -69.84889,-33.97195,0 -69.8689,-34.13361,0 -69.81249,-34.23555,0 -69.88918,-34.25945,0 -69.89335,-34.28612,0 -70.04944999999999,-34.28751,0 -70.0475,-34.40917,0 -70.22139,-34.59,0 -70.25306999999999,-34.71806000000001,0 -70.31362,-34.74889,0 -70.26363000000002,-34.79778,0 -70.36194999999999,-35.00445,0 -70.36806,-35.15889,0 -70.53639,-35.20639,0 -70.56751,-35.24778,0 -70.55029000000002,-35.29667,0 -70.42001000000001,-35.34723,0 -70.46223000000001,-35.38305,0 -70.39667000000002,-35.50362,0 -70.42028000000001,-35.64362,0 -70.33778,-35.80834,0 -70.41058,-35.88694,0 -70.37361,-35.91389,0 -70.40945000000001,-35.96362,0 -70.36917,-36.04667,0 -70.42431999999999,-36.13604,0 -70.47387999999999,-36.16529,0 -70.57999,-36.14861,0 -70.58974000000001,-36.19194,0 -70.70473,-36.27057,0 -70.70808,-36.41778,0 -70.80167,-36.43473,0 -70.88751000000001,-36.40055,0 -70.93611000000001,-36.48194,0 -71.02918,-36.47917,0 -71.06056000000001,-36.59306,0 -71.04584,-36.68584,0 -71.13834,-36.69084,0 -71.18696,-36.84889,0 -71.14028999999999,-36.8825,0 -71.15833000000001,-36.925,0 -71.12223,-36.94695,0 -71.20113000000001,-36.97055,0 -71.14612,-36.99667,0 -71.125,-37.07557,0 -71.07919,-37.10418,0 -71.13528,-37.14417,0 -71.13918,-37.22416,0 -71.19667,-37.29668,0 -71.10806,-37.45862,0 -71.11557000000001,-37.57944,0 -71.17167000000001,-37.61806000000001,0 -71.18028000000001,-37.69806,0 -70.99751999999999,-38.07112,0 -71.01306,-38.23056,0 -70.97083000000001,-38.41807,0 -70.82418,-38.56806,0 -70.87667,-38.63889,0 -70.86,-38.68806,0 -70.92583999999999,-38.7425,0 -71.22612,-38.80306,0 -71.4014,-38.92028,0 -71.42279000000001,-39.03694,0 -71.38751000000001,-39.23446,0 -71.40195,-39.32723,0 -71.47057,-39.38807,0 -71.44889000000001,-39.4325,0 -71.47335,-39.47862,0 -71.54085000000002,-39.53139,0 -71.48584,-39.5575,0 -71.48806,-39.59305,0 -71.50945,-39.61945,0 -71.61528000000001,-39.61973,0 -71.68223999999999,-39.56722,0 -71.70444999999999,-39.73278,0 -71.66945,-39.85723,0 -71.60639999999999,-39.91251,0 -71.67307,-40.01611000000001,0 -71.66166,-40.09389,0 -71.78889,-40.07861,0 -71.81863,-40.20196,0 -71.72861,-40.30112,0 -71.70251,-40.27639,0 -71.66972,-40.29473,0 -71.66389000000001,-40.33611,0 -71.71973,-40.42778,0 -71.7914,-40.41528,0 -71.85583,-40.555,0 -71.84390000000001,-40.61639,0 -71.95056,-40.73278,0 -71.83694,-40.95944,0 -71.89944,-41.35861,0 -71.86472999999999,-41.44083,0 -71.87779000000001,-41.52251,0 -71.84002,-41.57528,0 -71.91833,-41.64473,0 -71.78528,-41.86611,0 -71.75977,-41.99973,0 -71.72889000000001,-42.10139,0 -71.91389,-42.17806000000001,0 -71.9639,-42.12750000000001,0 -72.01221,-42.13111000000001,0 -72.13139,-42.28196,0 -72.04779000000001,-42.35667000000001,0 -72.07167,-42.42973,0 -72.02585000000001,-42.4775,0 -72.10945,-42.525,0 -72.13918,-42.58751000000001,0 -72.10196999999999,-42.85472,0 -72.13946,-42.99139,0 -72.05277,-43.10472,0 -71.83638999999999,-43.13195000000001,0 -71.73584,-43.17694,0 -71.74335000000001,-43.30223,0 -71.89418000000001,-43.32306,0 -71.94668,-43.44723,0 -71.85834,-43.46222,0 -71.86973999999999,-43.54028,0 -71.78001999999999,-43.54834,0 -71.70278999999999,-43.59584,0 -71.69862000000001,-43.68723,0 -71.81140000000001,-43.74917,0 -71.7914,-43.78223,0 -71.7375,-43.77194,0 -71.75557000000001,-43.82557,0 -71.69834,-43.86668,0 -71.64724,-43.91861,0 -71.65224000000001,-43.95166,0 -71.75806,-44.07168,0 -71.85474000000001,-44.11084000000001,0 -71.79389999999999,-44.21556,0 -71.82250999999999,-44.26529,0 -71.79389999999999,-44.32557,0 -71.85139,-44.36,0 -71.83835000000001,-44.39334,0 -71.20361,-44.42694,0 -71.10806,-44.53972,0 -71.13473000000001,-44.58389,0 -71.19723999999999,-44.59694,0 -71.23334,-44.66278,0 -71.22362,-44.745,0 -71.28223,-44.80028000000001,0 -71.50084,-44.74305,0 -71.65363000000001,-44.78279,0 -71.77,-44.75389,0 -71.84945999999999,-44.79306,0 -72.0425,-44.75417,0 -72.08085,-44.77473000000001,0 -72.06279000000001,-44.90556,0 -71.8839,-44.94862,0 -71.75335,-44.93194,0 -71.67473,-44.98028,0 -71.57362000000001,-44.97945,0 -71.45361,-45.15918,0 -71.33835000000001,-45.22055,0 -71.29779000000001,-45.29334000000001,0 -71.37334,-45.36944,0 -71.50639,-45.41139,0 -71.46751999999999,-45.4775,0 -71.50249,-45.51556,0 -71.75666,-45.56278,0 -71.73278999999999,-45.59890000000001,0 -71.78055999999999,-45.64889000000001,0 -71.79250999999999,-45.73251,0 -71.73833999999999,-45.79028,0 -71.75501,-45.84833,0 -71.63001000000001,-45.90084000000001,0 -71.60167,-45.97473000000002,0 -71.645,-45.99945,0 -71.76028,-46.11417,0 -71.90611,-46.14834000000001,0 -71.74751000000001,-46.24278,0 -71.74890000000001,-46.38612000000001,0 -71.66916999999999,-46.54750000000001,0 -71.68195,-46.69889,0 -71.80891,-46.78417,0 -71.94029000000001,-46.81556000000001,0 -71.92471999999999,-46.85056,0 -71.96056,-46.95,0 -71.90472,-47.00084,0 -71.99667,-47.06111,0 -71.86057,-47.14194,0 -71.86528,-47.21694000000001,0 -71.91752,-47.23446,0 -72.03195,-47.20333,0 -72.01501,-47.27444000000001,0 -72.08362,-47.35472000000001,0 -72.18056,-47.42028000000001,0 -72.25639,-47.41112,0 -72.34723,-47.445,0 -72.35945,-47.48111999999999,0 -72.30556,-47.50723,0 -72.33168000000001,-47.60168,0 -72.53528000000001,-47.90917000000001,0 -72.51501,-47.96111000000001,0 -72.32278,-48.07556,0 -72.28973000000001,-48.3475,0 -72.36668,-48.34612000000001,0 -72.4075,-48.39694,0 -72.58974000000001,-48.4664,0 -72.56417,-48.80445,0 -72.75528,-48.92806,0 -72.98805,-48.98167,0 -73.1664,-49.20001000000001,0 -73.16768999999999,-49.25264000000001,0 -73.08278000000001,-49.26917000000001,0 -73.13528,-49.30556,0 -73.46474000000001,-49.31139000000001,0 -73.45252000000001,-49.38528000000001,0 -73.53111,-49.43111,0 -73.52972,-49.49639000000001,0 -73.58168000000001,-49.53196,0 -73.51000999999999,-49.61334,0 -73.53333000000001,-49.68973,0 -73.44446000000001,-49.7825,0 -73.56445000000001,-49.92472999999999,0 -73.46722,-50.00666,0 -73.53223,-50.14056,0 -73.34639,-50.23806,0 -73.28001999999999,-50.32278,0 -73.24918,-50.57639,0 -73.17834000000001,-50.64777999999999,0 -73.18085000000001,-50.72918,0 -73.14445000000001,-50.76750000000001,0 -73.01806000000001,-50.74945,0 -72.90611,-50.6675,0 -72.75834999999999,-50.6189,0 -72.62973,-50.67305,0 -72.50112,-50.60334,0 -72.29416999999999,-50.64972000000001,0 -72.34166999999999,-50.7439,0 -72.24223000000001,-50.85111,0 -72.26694999999999,-51.01723,0 -72.34113000000001,-51.03666,0 -72.39778,-51.11418000000001,0 -72.37167,-51.16473000000001,0 -72.24529000000001,-51.24443999999999,0 -72.30917,-51.30222,0 -72.33945,-51.47583,0 -72.43944999999999,-51.55972000000001,0 -72.33085,-51.58723000000001,0 -72.28558,-51.69917000000001,0 -72.13555,-51.74001,0 -71.96639999999999,-51.84861,0 -71.93946,-51.89889,0 -71.96251,-51.96029000000001,0 -71.9314,-51.98889000000001,0 -69.99834,-51.9964,0 -69.46139000000001,-52.14362000000001,0 -69.23362,-52.14334000000001,0 -68.82861,-52.27973,0 -68.4375,-52.32833,0 -68.44176,-52.37778000000001,0 -68.61758000000002,-52.64151,0 -68.63583,-54.78834,0 -68.63612000000001,-54.80477,0 -68.6431,-54.88861,0 -68.55723999999999,-54.88001,0 -68.32334,-54.84361,0 -68.28834999999999,-54.79362,0 -67.84778,-54.87611,0 -67.01134,-54.90862,0 -66.79639,-54.94555,0 -66.64944,-55.02584,0 -66.45808000000001,-55.05168,0 -66.37195,-54.98500000000001,0 -66.01334,-54.95612000000001,0 -65.95529000000001,-54.89612000000001,0 -65.73779,-54.90222,0 -65.70585,-54.96611,0 -65.6575,-54.97223,0 -65.59889,-54.93056,0 -65.49001,-54.93472000000001,0 -65.47139,-54.88361,0 -65.37667999999999,-54.93056,0 -64.67834000000001,-54.90723,0 -64.60972,-54.9,0 -64.49612000000001,-54.82696,0 -64.41862,-54.83862000000001,0 -64.30945,-54.77778,0 -64.25306999999999,-54.77972,0 -64.25389,-54.84223,0 -63.97278,-54.75973000000001,0 -63.96861000000001,-54.81807,0 -63.81362000000001,-54.7314,0 -63.92001,-54.71417000000002,0 -64.06417999999999,-54.75000000000001,0 -64.16833,-54.71417000000002,0 -64.17834000000001,-54.75083999999999,0 -64.35806,-54.70639,0 -64.38112,-54.78861,0 -64.41278,-54.73667,0 -64.4539,-54.75833,0 -64.55267000000001,-54.715,0 -65.22362,-54.63361,0 -65.67361,-54.67306,0 -65.82751,-54.65584,0 -66.27166000000001,-54.52806000000001,0 -66.54667000000001,-54.41389,0 -66.70361,-54.28917,0 -66.92418000000001,-54.21918,0 -67.49554000000002,-53.95288,0 -67.50834999999999,-48.98417,0 -67.39917,-48.89056000000001,0 -67.23972999999999,-48.82278000000001,0 -67.08278,-48.63889000000001,0 -66.82668,-48.57111,0 -66.66750999999999,-48.44167,0 -66.46805999999999,-48.39917000000001,0 -66.44362,-48.34723000000001,0 -66.32751,-48.34555,0 -66.33556,-48.28362000000001,0 -66.1414,-48.18472,0 -66.07944999999999,-48.10444,0 -65.89778,-48.09973000000001,0 -65.97139,-47.96111000000001,0 -65.79584,-47.97028000000001,0 -65.78722999999999,-47.93557000000001,0 -65.87000999999999,-47.90222000000001,0 -65.91195999999999,-47.80418,0 -65.97333999999999,-47.78389000000001,0 -66.32418,-47.88278,0 -66.38863000000001,-47.8625,0 -66.32168,-47.87306,0 -66.12083,-47.81973,0 -66.02251,-47.76084,0 -65.87584,-47.76222,0 -65.75696000000001,-47.54194,0 -65.73222,-47.34418,0 -65.77224,-47.19834,0 -65.90001,-47.09862,0 -65.99335000000001,-47.06667000000001,0 -66.20916,-47.09361000000001,0 -66.71666999999999,-47.04000000000001,0 -67.05473000000001,-46.81001,0 -67.12445,-46.72639,0 -67.33805,-46.62528,0 -67.53667,-46.4175,0 -67.62029,-46.17139,0 -67.58667,-46.00389000000001,0 -67.58436000000002,-46.00031,0 -67.42222,-45.82195,0 -67.34166999999999,-45.77890000000001,0 -67.31611000000001,-45.61501,0 -67.18362000000001,-45.52861,0 -66.94946,-45.25557,0 -66.84668000000001,-45.21029,0 -66.53528,-45.20444000000001,0 -66.51918000000001,-45.17889,0 -66.58863,-45.13862,0 -66.45975,-45.14362,0 -66.53473,-45.11584,0 -66.52196000000001,-45.08667000000001,0 -66.28500000000001,-45.03167000000001,0 -66.19473000000001,-44.95917,0 -66.01528999999999,-44.97389,0 -65.94917,-45.02223,0 -65.87529000000001,-45.01944000000001,0 -65.85139,-44.98584,0 -65.68556,-45.04306,0 -65.60474000000001,-45.0164,0 -65.58474,-44.92445,0 -65.53055999999999,-44.895,0 -65.71084999999999,-44.83362,0 -65.71917999999999,-44.77612,0 -65.59945999999999,-44.61805,0 -65.44056999999999,-44.53807,0 -65.3614,-44.54418,0 -65.38751000000001,-44.52166,0 -65.35945,-44.48306,0 -65.27667,-44.48028,0 -65.34222,-44.43862,0 -65.22806,-44.33667000000001,0 -65.31249,-44.18696,0 -65.29695,-44.13223,0 -65.22445999999999,-44.10751000000001,0 -65.21028,-43.95528,0 -65.25834999999999,-43.96973,0 -65.32001,-43.81945,0 -65.33029000000001,-43.66779,0 -65.03722000000001,-43.39418,0 -65.02806,-43.30445,0 -64.80029999999999,-43.16112,0 -64.49168,-43.08445,0 -64.29501000000001,-42.99195,0 -64.31556999999999,-42.94944,0 -64.41028,-42.97805,0 -64.50389,-42.93806000000001,0 -64.59390000000001,-42.94112000000001,0 -64.80862000000001,-42.84028000000001,0 -64.99278,-42.79361,0 -65.01806999999999,-42.73944000000001,0 -64.96056000000002,-42.66639,0 -64.82084999999999,-42.62862,0 -64.62194,-42.52,0 -64.39806,-42.51584,0 -64.19611999999999,-42.63889,0 -64.24918,-42.77612,0 -64.08694,-42.89028,0 -63.69306000000001,-42.82168,0 -63.62583,-42.76194,0 -63.63334,-42.71278,0 -63.57805,-42.60612,0 -63.59083,-42.31695,0 -63.74694,-42.09056000000001,0 -63.87195000000001,-42.10250000000001,0 -64.19917,-42.22417,0 -64.33446000000001,-42.23667,0 -64.06305999999999,-42.27862,0 -64.04528999999999,-42.37251,0 -64.1189,-42.43444000000001,0 -64.30417,-42.41528,0 -64.45193999999999,-42.44584,0 -64.60695,-42.41889,0 -64.46251,-42.26362,0 -64.87139999999999,-42.19195,0 -64.99418,-42.11222,0 -65.06223,-42.01,0 -65.07445,-41.93167,0 -64.99084000000001,-41.77862,0 -65.02916999999999,-41.67556,0 -64.99111000000001,-41.51083000000001,0 -65.07111999999999,-41.43334,0 -65.17973000000001,-40.99667,0 -65.12584,-40.84056,0 -65.0125,-40.77195,0 -64.90611,-40.76862,0 -64.93807,-40.75639,0 -64.9264,-40.72862,0 -64.98139000000001,-40.72195,0 -64.77528,-40.73278,0 -64.7975,-40.77223,0 -64.75194999999999,-40.77695,0 -64.74918,-40.80278,0 -64.89612,-40.79862,0 -64.91166,-40.82028,0 -64.88528,-40.83806,0 -64.69112,-40.83889,0 -64.41251,-40.90945,0 -64.14223,-41.02195,0 -64.05334000000001,-40.99918,0 -64.05833,-41.04501,0 -63.96001,-41.06445,0 -63.8075,-41.15917,0 -63.14195000000001,-41.16056,0 -63.03334000000002,-41.14834,0 -62.80139,-41.04333,0 -62.72333,-41.04611,0 -62.32110999999999,-40.86723,0 -62.22972,-40.66361,0 -62.18389,-40.62972000000001,0 -62.32666,-40.67834000000001,0 -62.34807,-40.60723,0 -62.26139000000001,-40.63334,0 -62.26000000000001,-40.56001,0 -62.29111,-40.56583,0 -62.33139000000001,-40.49834,0 -62.42084,-40.45945,0 -62.48945,-40.29945,0 -62.35278000000002,-40.18167,0 -62.36418000000001,-40.05528,0 -62.32000000000001,-39.86834,0 -62.28612,-39.85473,0 -62.3075,-39.81861,0 -62.18917,-39.86,0 -62.11639,-39.83945,0 -62.05002,-39.48876,0 -61.85882,-39.24178,0 -61.56255,-39.01225,0 -61.38361,-38.97973000000001,0 -61.16223,-38.99889,0 -60.86000000000001,-38.97639,0 -59.78973,-38.83694,0 -59.61,-38.77973,0 -59.03807,-38.69251,0 -58.72279,-38.58055000000001,0 -58.31223000000001,-38.48972,0 -57.84584,-38.29917,0 -57.63473000000001,-38.18,0 -57.53306,-38.08611,0 -57.54112,-37.98195000000001,0 -57.48056,-37.82584,0 -57.09973000000002,-37.47445,0 -56.66306000000001,-36.90056,0 -56.67168000000001,-36.57584,0 -56.74167,-36.31834,0 -56.77361,-36.30445,0 -56.76778000000001,-36.3475,0 -56.84778,-36.34222,0 -56.94195,-36.38334,0 -56.93389,-36.34723,0 -57.05334,-36.31417,0 -57.23667,-36.18418,0 -57.37666999999999,-35.96278,0 -57.37612,-35.78139,0 -57.12972000000001,-35.47029,0 -57.12473,-35.41695,0 -57.18833000000001,-35.32055,0 -57.33555,-35.15556,0 -57.49889,-35.04556,0 -57.81361,-34.86584,0 -58.19723,-34.73861,0 -58.34472,-34.63612,0 -58.34633000000001,-34.63362,0 -58.36945000000001,-34.58389,0 -58.46972000000001,-34.53972,0 -58.52528,-34.43972,0 -58.48417000000001,-34.34166,0 -58.56667,-34.29056,0 -58.47195,-34.28111,0 -58.40885999999999,-34.22926,0 -58.18541,-34.16549,0 -58.08334,-34.18695,0 -57.90722,-34.37,0 -57.84028,-34.49473,0 -57.58195000000001,-34.43917,0 -57.51945000000001,-34.46445,0 -57.45778,-34.44222,0 -57.11778,-34.46222,0 -56.88779,-34.66612,0 -56.5639,-34.77251,0 -56.36723,-34.79444,0 -56.42556,-34.84306,0 -56.32028,-34.91057,0 -56.22084,-34.88945,0 -56.16861,-34.93194,0 -55.68557000000001,-34.77444,0 -55.36501,-34.81056,0 -55.25666,-34.90723,0 -55.05556000000001,-34.8914,0 -54.95612000000001,-34.94028,0 -54.95129000000001,-34.9778,0 -54.53111,-34.80609000000001,0 -54.27916,-34.69389,0 -54.30083000000001,-34.65,0 -54.34583,-34.64362,0 -54.28167,-34.59306,0 -54.32557,-34.56278,0 -54.2425,-34.58139,0 -54.25695,-34.68945000000001,0 -54.14500000000001,-34.67139,0 -54.13,-34.62389000000001,0 -53.98890000000001,-34.50389,0 -53.77139,-34.39083,0 -53.75029,-34.25751,0 -53.53723000000001,-34.06612,0 -53.48251000000001,-33.85445,0 -53.3743,-33.74068,0 -52.74139000000001,-33.25695,0 -52.50465000000001,-32.88476,0 -52.50452,-37.88475,0 -52.50438,-42.88476,0 -52.50425,-47.88475,0 -52.50412000000001,-52.88476000000001,0 -52.50398000000001,-57.88475,0 -52.50385,-62.88475,0 -52.50372,-67.88475,0 -52.50368,-69.03889,0 -52.50352,-74.03888000000001,0 -52.50335,-79.03888000000001,0 -52.50323000000001,-82.53889,0 -52.50305,-87.53888000000001,0 -52.50298000000001,-89.90000000000001,0 -53.50229000000001,-89.90000000000001,0 -54.50161000000001,-89.90000000000001,0 -55.50094,-89.90000000000001,0 -56.50026,-89.90000000000001,0 -57.49957,-89.90000000000001,0 -58.49889000000002,-89.90000000000001,0 -59.49821000000001,-89.90000000000001,0 -60.49753000000001,-89.90000000000001,0 -61.49686,-89.90000000000001,0 -62.49617,-89.90000000000001,0 -63.49549,-89.90000000000001,0 -64.49481,-89.90000000000001,0 -65.49413,-89.90000000000001,0 -66.49345,-89.90000000000001,0 -67.49276999999999,-89.90000000000001,0 -67.49276999999999,-84.89999,0 -67.49276999999999,-79.90000000000001,0 -67.49276999999999,-74.90000000000001,0 -67.49276999999999,-69.90000000000001,0 -67.49276999999999,-64.90000000000001,0 -67.49276999999999,-59.90001,0 -67.49276999999999,-55.95932,0 -69.11057,-55.64479,0 -70.14055000000001,-55.36066000000002,0 -71.60384000000001,-54.89185,0 -73.13816,-54.55088000000001,0 -74.76482000000001,-52.7786,0 -75.36150000000001,-51.61010000000001,0 -75.73088,-49.03514,0 -75.68826,-48.46332,0 -75.60303000000002,-48.05132,0 -75.65273000000001,-46.92189000000001,0 -75.75219,-46.80824,0 -75.71666999999999,-46.56318,0 -75.32599,-46.32876,0 -75.18392,-44.80154,0 -74.87848,-43.52649,0 -74.43097,-43.22105,0 -74.18946,-42.17331,0 -74.0545,-41.73290000000001,0 -73.8556,-41.76842,0 -73.75281000000001,-41.75128,0 -73.68279,-41.62473,0 -73.56417999999999,-41.61362,0 -73.49195,-41.52084,0 -73.59166999999999,-41.6025,0 -73.82167,-41.56556,0 -73.82390000000001,-41.51723,0 -73.87445000000001,-41.47333,0 -73.85890000000001,-41.33973,0 -73.97806,-41.1189,0 -73.99500999999999,-40.97000000000001,0 -73.89806,-40.90639,0 -73.94778,-40.86389,0 -73.84278999999999,-40.68696,0 -73.85778999999999,-40.6264,0 -73.74751000000001,-40.52472,0 -73.78445000000001,-40.42362,0 -73.75029000000001,-40.25723,0 -73.71709,-40.20832,0 -73.73112,-40.18,0 -73.65807,-40.11028,0 -73.71445,-39.97862,0 -73.45168,-39.8325,0 -73.38251,-39.91445,0 -73.37973,-39.73639,0 -73.40443999999999,-39.69167,0 -73.34557000000001,-39.66417,0 -73.2439,-39.49139,0 -73.25751,-39.4539,0 -73.2225,-39.41473,0 -73.24597,-39.38584,0 -73.2075,-39.21556,0 -73.2353,-39.18584,0 -73.22501,-39.21945,0 -73.24779,-39.22223,0 -73.43307,-38.69168,0 -73.44196,-38.76750000000001,0 -73.54224000000001,-38.41833,0 -73.4539,-38.05556,0 -73.53807000000001,-37.87333,0 -73.66945,-37.72611000000001,0 -73.68722,-37.60111,0 -73.60196999999999,-37.51333,0 -73.60896,-37.41935,0 -78.60896,-37.41935,0 -82.11279,-37.41935,0 -82.11279,-32.41934,0 -82.11279,-27.41935000000001,0 -82.11279,-24.70552,0 -77.11279,-24.70552,0 -72.11279999999999,-24.70552,0 -70.58064000000002,-24.70552,0 -70.55695,-24.60889,0 -70.5825,-24.52667,0 -70.50306999999999,-24.16556,0 -70.51862,-23.86084,0 -70.38943999999999,-23.59723,0 -70.4164,-23.49305,0 -70.48390000000001,-23.45028,0 -70.54667000000001,-23.52112,0 -70.62306,-23.49973,0 -70.58474,-23.42389,0 -70.59361,-23.22222,0 -70.5514,-23.16250000000001,0 -70.57584,-23.07166,0 -70.50917,-23.01055,0 -70.48112000000002,-23.0825,0 -70.38862,-23.06083,0 -70.28917,-22.90139,0 -70.30750999999999,-22.75806,0 -70.16833,-21.94195,0 -70.19334000000001,-21.88805,0 -70.14805,-21.85556,0 -70.15416999999999,-21.66694,0 -70.06585,-21.52361,0 -70.08446000000001,-21.48473,0 -70.05334000000002,-21.42565,0 -70.09529000000001,-21.33667,0 -70.06473,-21.29639,0 -70.12306,-21.08139,0 -70.17001000000001,-21.01083,0 -70.13918,-20.87556,0 -70.21223000000001,-20.79973,0 -70.20473,-20.52916,0 -70.16724000000001,-20.45166,0 -70.19056999999999,-20.36778,0 -70.13306,-20.27334000000001,0 -70.16528,-20.20389,0 -70.12612,-20.07361,0 -70.13612000000001,-19.86389,0 -70.1664,-19.80695,0 -70.15167,-19.71445,0 -70.23002,-19.58667,0 -70.19862000000001,-19.57694,0 -70.19695,-19.50278,0 -70.28862,-19.29444,0 -70.27166,-19.14139,0 -70.35778999999999,-18.78417,0 -70.31001000000001,-18.43445,0 -70.40549,-18.34854,0 -70.35583,-18.31973,0 -70.14111,-18.32333,0 -69.95113000000001,-18.24278,0 -69.84141,-18.12861,0 -69.77000000000001,-17.96889,0 -69.84972000000001,-17.75418,0 -69.83223,-17.67694,0 -69.81473,-17.65223,0 -69.68389000000001,-17.66361,0 -69.49973,-17.50528,0 -69.50112,-17.37889,0 -69.59084,-17.29528,0 -69.6562,-17.28724,0 -69.61806,-17.18667,0 -69.38834,-17.055,0 -69.41112,-17.02056,0 -69.34334,-16.9825,0 -69.17001000000001,-16.72389,0 -69.01112000000001,-16.65695,0 -69.04167000000001,-16.54112,0 -68.99084000000001,-16.41973,0 -68.82252,-16.33973,0 -68.84084,-16.29555,0 -68.96501000000001,-16.20944,0 -69.13415999999999,-16.22362,0 -69.21194,-16.16055,0 -69.42195,-15.61805,0 -69.32696,-15.43055,0 -69.27584,-15.40417,0 -69.26835,-15.32695,0 -69.13695,-15.24584,0 -69.38139,-14.97,0 -69.34945999999999,-14.895,0 -69.36639,-14.8025,0 -69.24195,-14.73028,0 -69.2289,-14.57639,0 -69.1575,-14.57555,0 -69.15779000000001,-14.50528,0 -68.98139999999999,-14.37917,0 -68.99918,-14.24334,0 -68.85306,-14.19917,0 -68.89558,-14.03389,0 -68.97751,-13.96166,0 -69.00306000000001,-13.75445,0 -69.08778,-13.66333,0 -69.01472,-13.63834,0 -68.96194,-13.51305,0 -68.96335000000001,-13.02667,0 -68.97362,-12.86334,0 -68.84668000000002,-12.73278,0 -68.75666,-12.71472,0 -68.73139999999999,-12.66972,0 -68.78084,-12.61778,0 -68.6739,-12.50115,0 -68.925,-11.99667,0 -69.56751,-10.95055,0 -69.39529,-10.92695,0 -68.90028,-11.01528,0 -68.78583999999999,-10.99223,0 -68.74760000000001,-11.01038,0 -68.78084,-11.065,0 -68.77473000000001,-11.13639,0 -68.58346,-11.10614,0 -68.51694999999999,-11.05445,0 -68.41750999999999,-11.04445,0 -68.3914,-11.00805,0 -68.28055999999999,-10.98,0 -68.06111000000001,-10.67639,0 -67.84557,-10.6575,0 -67.74557,-10.7125,0 -67.70334,-10.69573,0 -67.61168000000001,-10.53222,0 -67.46363,-10.45555,0 -67.41833,-10.38306,0 -67.33334000000001,-10.37,0 -67.32417,-10.32084,0 -67.17444999999999,-10.325,0 -67.08085,-10.27194,0 -66.88806,-10.08639,0 -66.64025,-9.91827,0 -66.83056000000001,-9.838054,0 -68.65723,-9.066394000000001,0 -70.35722,-8.160553,0 -72.63779,-7.603335,0 -73.79514,-7.122628,0 -73.75446,-6.894163,0 -73.67639,-6.804441000000002,0 -73.35306,-6.576949,0 -73.19168000000001,-6.552783000000001,0 -73.12390000000001,-6.447223,0 -73.16307000000001,-6.207229,0 -73.22974000000001,-6.093613,0 -73.12612,-5.867496,0 -72.96335000000001,-5.65416,0 -72.94251,-5.543334,0 -72.96278,-5.458051000000001,0 -72.88417,-5.258052,0 -72.91750999999999,-5.151105,0 -72.89028999999999,-5.123610000000001,0 -72.74139,-5.050837,0 -72.62529000000001,-5.054166,0 -72.60916,-5.000833,0 -72.40611,-4.878891,0 -72.37529000000001,-4.784446,0 -72.235,-4.753333,0 -72.12917,-4.693609,0 -72.06657,-4.606092,0 -72.02806,-4.612501,0 -71.90249,-4.518189,0 -71.69057000000001,-4.473892,0 -71.64474,-4.509162,0 -71.62306,-4.475835,0 -71.60722,-4.498056,0 -71.51055,-4.481389,0 -71.47083000000001,-4.438333,0 -71.38223000000001,-4.430836,0 -71.3539,-4.457223,0 -71.29528999999999,-4.433608,0 -71.29056,-4.383055,0 -71.23296000000002,-4.374621000000001,0 -71.20029000000001,-4.410559,0 -71.16946,-4.357782,0 -71.12918000000001,-4.398894,0 -71.08334000000001,-4.363605,0 -71.01140000000001,-4.382226,0 -70.98862,-4.340557,0 -70.95029,-4.38139,0 -70.83056999999999,-4.234447000000001,0 -70.83474,-4.187781,0 -70.76584,-4.146390000000001,0 -70.68391,-4.173337,0 -70.62000999999999,-4.114998,0 -70.6289,-4.15666,0 -70.58414999999999,-4.195558,0 -70.54528999999999,-4.138606,0 -70.51195,-4.180285,0 -70.43333,-4.131667,0 -70.35973,-4.16584,0 -70.32389000000001,-4.136662,0 -70.30667,-4.244716,0 -70.19410000000001,-4.333177,0 -70.10695,-4.253058,0 -70.0741,-4.313079,0 -70.01862,-4.339161000000001,0 -69.96417,-4.290004,0 -69.95692,-4.236876,0 -69.46056,-1.496107,0 -69.37946,-1.35028,0 -69.38084000000002,-1.174447,0 -69.4389,-1.083889,0 -69.44198,-1.017811,0 -69.43473,-0.9941676,0 -69.52916999999999,-0.9244446000000001,0 -69.5625,-0.8080565,0 -69.61472999999999,-0.7511045,0 -69.57278,-0.6366605,0 -69.60751000000001,-0.5175005,0 -69.74612000000001,-0.4522145000000001,0 -69.85167,-0.3352775,0 -69.92834000000002,-0.3094475,0 -70.05807000000002,-0.1575005,0 -70.04416999999999,0.5908405,0 -69.79779000000001,0.6002815000000001,0 -69.67834000000001,0.6788875,0 -69.60779000000001,0.6463885,0 -69.56723,0.7011085,0 -69.46417,0.7402855,0 -69.35001,0.6369475,0 -69.29389999999999,0.6677815,0 -69.28695999999999,0.6208375,0 -69.13721,0.6299995000000001,0 -69.13863000000001,0.6866634,0 -69.18250999999999,0.7244455,0 -69.14001,0.8669425,0 -69.20278999999999,0.9066595,0 -69.22417,0.9952825,0 -69.34361000000001,1.074716,0 -69.42084000000001,1.026107,0 -69.4464,1.062215,0 -69.60196999999999,1.080836,0 -69.70473,1.059443,0 -69.76501,1.09584,0 -69.84222,1.062215,0 -69.84610000000001,1.710459,0 -69.78695999999999,1.706111,0 -69.71693999999999,1.748051,0 -69.63139,1.737782,0 -69.55584000000002,1.784159,0 -69.37793000000001,1.726146,0 -68.15306,1.724165,0 -68.17583999999999,1.763054,0 -68.23278999999999,1.775555,0 -68.22917,1.823336,0 -68.27724000000001,1.837781,0 -68.18668,2.020005,0 -68.17083,1.973889,0 -68.08945,1.933613,0 -68.00502,1.763054,0 -67.91474,1.745279,0 -67.80556,1.788335,0 -67.64418000000001,1.999997,0 -67.42417,2.143889,0 -67.33501,2.111111,0 -67.07250999999999,1.625282,0 -67.07668,1.173338,0 -66.87045000000001,1.22094,0 -66.99168,1.695833,0 -67.13361999999999,1.987785,0 -67.10945,2.094444,0 -67.16446999999999,2.135556,0 -67.21084999999999,2.240558,0 -67.17307,2.341665,0 -67.19251,2.392496,0 -67.32056,2.468339,0 -67.50333999999999,2.67722,0 -67.56778,2.683341,0 -67.60695,2.795552,0 -67.75167999999999,2.840003,0 -67.85506,2.789783,0 -67.84305999999999,2.868335000000001,0 -67.43556,3.253896,0 -67.38417,3.259161000000001,0 -67.29056,3.3975,0 -67.30723999999999,3.455559,0 -67.38779,3.493052,0 -67.49195,3.726395000000001,0 -67.61333999999999,3.754719,0 -67.70851,4.046957,0 -67.80556,4.227498,0 -67.79194,4.427495000000001,0 -67.87612,4.530833,0 -67.79056,5.054697,0 -67.82861,5.112216,0 -67.81168,5.217497,0 -67.85062000000001,5.301639,0 -67.80279,5.385563,0 -67.64944,5.478335,0 -67.61306,5.539166,0 -67.65195,5.676390000000001,0 -67.62223,5.787503,0 -67.41280999999999,5.992460000000001,0 -67.49199,6.118659000000001,0 -67.45445000000001,6.193052,0 -67.56834000000001,6.264719000000001,0 -67.82278,6.30945,0 -67.99500999999999,6.207777,0 -68.15224000000001,6.223338000000001,0 -68.31362,6.166944,0 -68.4539,6.190550000000002,0 -68.63721,6.133895,0 -69.0564,6.21611,0 -69.24529,6.081389000000001,0 -69.32501000000002,6.153605,0 -69.42944,6.118605000000001,0 -70.11917,6.975836,0 -70.30083999999999,6.939161,0 -70.51279,7.015553000000001,0 -70.56362,7.089444000000001,0 -70.61279,7.072496,0 -70.70251,7.108056,0 -70.89001,7.074998,0 -71.02501,6.984449000000001,0 -71.12806999999999,6.998895,0 -71.18668,6.962769000000001,0 -71.26555999999999,6.98778,0 -71.29556,7.033338000000001,0 -71.45778,7.015832,0 -71.60695,7.059716,0 -71.76418,7.032221,0 -71.76557,7.005555,0 -71.83278,6.987221,0 -71.9268,7.009748000000001,0 -72.06611,7.062417000000001,0 -72.14528,7.198614,0 -72.15085000000002,7.339715000000001,0 -72.18333,7.382781000000001,0 -72.39917,7.406108,0 -72.47168000000001,7.491941000000001,0 -72.44334000000001,7.828055000000001,0 -72.48139999999999,7.947783,0 -72.42307,7.985835000000001,0 -72.40252,8.041949000000001,0 -72.33362,8.049446,0 -72.32529,8.095553000000001,0 -72.38557,8.25389,0 -72.38417,8.363051000000001,0 -72.66446999999999,8.641107000000002,0 -72.7903,9.083042000000001,0 -72.76819,9.111428,0 -72.81696,9.145277,0 -72.94223,9.104165,0 -73.00639,9.304442,0 -73.21890000000001,9.171386999999999,0 -73.37195,9.166949000000001,0 -73.38361999999999,9.189719,0 -73.29834,9.284715,0 -73.16833,9.536111999999999,0 -73.09279000000001,9.585557,0 -72.96223000000001,9.836945999999999,0 -72.98723,9.914165000000001,0 -72.90501999999999,10.4425,0 -72.76028,10.65139,0 -72.66528,10.87277,0 -72.55862000000001,10.97389,0 -72.49306,11.12111,0 -72.34113000000001,11.165,0 -72.26000999999999,11.15445,0 -71.97723000000001,11.665,0 -71.52943999999999,11.78005,0 -71.38473999999999,11.82889,0 -71.32471,11.85305,0 -71.08137000000001,12.00208,0 -71.06217,12.23228,0 -71.25400999999999,12.4433,0 -71.77196000000001,12.5584,0 -71.76823,17.5584,0 -71.76786,18.0385,0 -71.74473999999999,18.13389,0 -71.76918000000001,18.20222,0 -71.69473000000001,18.32222,0 -71.9075,18.43777,0 -71.88500999999999,18.47721,0 -72.00306999999999,18.60083,0 -71.83168000000001,18.6225,0 -71.71584,18.74972,0 -71.71945,18.88389,0 -71.78889,18.96277,0 -71.85605,18.94783,0 -71.75,19.01972,0 -71.63502,19.15806,0 -71.62918000000001,19.21972,0 -71.69611999999999,19.24167,0 -71.76669,19.33195,0 -71.69862000000001,19.40027,0 -71.69168000000001,19.48584,0 -71.75418000000001,19.70582,0 -71.75587,20.2411,0 -67.50364999999999,20.24133,0 -67.50364999999999,25.24134,0 -67.50364999999999,30.24134,0 -67.50364999999999,35.24134,0 -67.50364999999999,40.24134,0 -67.50364999999999,44.47953,0 -67.11709999999999,44.69726,0 -66.95337000000001,44.82681,0 -66.97331,44.91223,0 -67.09289,45.09019000000001,0 -67.15837999999999,45.17419,0 -67.20654,45.18303,0 -67.27501,45.19887,0 -67.34193999999999,45.14999,0 -67.46278,45.2761,0 -67.4164,45.39888,0 -67.48528,45.49444,0 -67.41083,45.52999,0 -67.41249999999999,45.58693,0 -67.65666,45.63055000000001,0 -67.71751,45.69137,0 -67.79916,45.7011,0 -67.80638,45.78472,0 -67.75723000000001,45.83333,0 -67.79028,45.88194000000001,0 -67.74695,45.92917,0 -67.77251,45.95750000000001,0 -67.79333,47.07028,0 -68.18582000000001,47.33277,0 -68.30665000000001,47.36444,0 -68.37305000000001,47.34722000000001,0 -68.39751,47.29527,0 -68.49805000000002,47.30555,0 -68.88779,47.18804,0 -69.03612,47.25722,0 -69.05359,47.29379,0 -68.57611,47.42471,0 -68.38028,47.5586,0 -68.37277,47.92471000000001,0 -68.12027000000001,47.92665000000001,0 -68.1161,48.00194000000001,0 -67.61221000000001,47.99749000000001,0 -67.60417,47.9361,0 -67.35665,47.84166,0 -67.32278000000001,47.89638,0 -67.18859999999999,47.88388000000001,0 -67.04723,47.93083,0 -66.94694,47.89916000000001,0 -66.9436,47.96305000000001,0 -66.8437,47.99666,0 -66.3727,48.08661000000001,0 -65.32375999999999,47.96921,0 -64.76967999999999,48.17476000000001,0 -64.52839,48.32669000000001,0 -63.84024000000001,48.40712000000001,0 -63.04419000000001,48.50467,0 -63.05001,52.00000000000001,0 -63.84641000000001,52.00000000000001,0 -63.82917,52.25999,0 -63.91805999999999,52.33611,0 -64.02528000000001,52.35443,0 -63.9875,52.3625,0 -64.01306,52.38722,0 -63.91889000000001,52.38942999999999,0 -63.96222,52.45193,0 -64.03749999999999,52.43444,0 -64.08085,52.47610000000001,0 -64.00194999999999,52.55471,0 -63.86305999999999,52.61277,0 -63.37777,52.65083,0 -63.42305000000001,52.70722,0 -63.59584,52.77077000000001,0 -63.54084,52.91666,0 -63.54805000000001,52.98499,0 -63.62611,53.04222000000002,0 -63.655,53.105,0 -63.86639000000001,53.10527,0 -63.92805,53.07166,0 -63.91945000000001,52.97693,0 -63.98277,52.9336,0 -63.95417,52.87165,0 -64.08085,52.88138,0 -64.1725,52.85083000000001,0 -64.20140000000001,52.79084,0 -64.15666,52.73833,0 -64.20223,52.67222000000001,0 -64.17529,52.60944000000001,0 -64.20889000000001,52.54889000000001,0 -64.08308,52.37638,0 -64.13137999999999,52.28139,0 -64.11861,52.14472,0 -64.25779,52.12804,0 -64.29333,52.04639,0 -64.27444,52.00332,0 -64.33085,51.96527,0 -64.31751,51.90359999999999,0 -64.35722,51.85055000000001,0 -64.26418,51.73972000000001,0 -64.44249000000001,51.66776999999999,0 -64.58112,51.55778,0 -64.60417,51.64277,0 -64.70305999999999,51.69277000000002,0 -64.71805999999999,51.74778000000001,0 -64.59389,51.79610000000001,0 -64.58778,51.82888,0 -64.84584,51.93083000000001,0 -64.85334,52.02193000000001,0 -65.10861,52.17083000000001,0 -65.4614,52.20665000000001,0 -65.58056999999999,52.04278,0 -65.75861,52.11139,0 -65.79944,52.09555,0 -65.80167,52.04278,0 -65.91695,52.05082,0 -66.06751000000001,52.12276,0 -66.06807000000001,52.21638000000001,0 -66.17389000000001,52.22055,0 -66.26667000000001,52.31138,0 -66.31973000000001,52.28916000000001,0 -66.30388000000001,52.22276,0 -66.33556,52.13639,0 -66.43889,52.20083,0 -66.48556000000001,52.30721,0 -66.45862,52.3661,0 -66.35805000000002,52.40666,0 -66.44665999999999,52.53082,0 -66.37361,52.54249999999999,0 -66.43222,52.61138,0 -66.43388,52.70638,0 -66.32834,52.73693,0 -66.37888,52.83611,0 -66.26973,52.87749,0 -66.35472,52.98166,0 -66.47694,53.03416,0 -66.52528,52.94332000000001,0 -66.64806,52.94583,0 -66.66806,52.72581,0 -66.75501,52.71027000000001,0 -66.80556,52.66554,0 -66.87470999999999,52.74778,0 -66.89138,52.67333000000001,0 -67.08945,52.7197,0 -67.05833,52.80499,0 -67.07805999999999,52.94804000000001,0 -66.97945000000001,53.02777,0 -66.94861,53.10972000000002,0 -66.99027,53.20082000000001,0 -66.9025,53.3261,0 -66.9875,53.40359,0 -67.06779,53.38417,0 -67.06862,53.4286,0 -67.12888,53.46416,0 -67.14194999999999,53.54221,0 -67.49194,53.60388,0 -67.43195,53.78305,0 -67.58804000000001,53.85722,0 -67.60916,53.92804000000001,0 -67.76055,54.01583000000001,0 -67.78195,54.06388,0 -67.71556,54.12944000000001,0 -67.69611,54.1386,0 -67.485,54.23416,0 -67.53167000000001,54.24889,0 -67.49472,54.31444000000001,0 -67.59583000000001,54.36527000000001,0 -67.58611999999999,54.4175,0 -67.63306,54.47999000000001,0 -67.60139,54.56639000000001,0 -67.49249,54.58194,0 -67.30556,54.50416,0 -67.21945,54.6075,0 -67.03639,54.62166,0 -67.2139,54.66888000000001,0 -67.185,54.71943000000001,0 -67.30777000000001,54.78388,0 -67.26334,54.82527,0 -67.36221000000001,54.87582,0 -67.45305999999999,55.01888,0 -67.42833,55.07111,0 -67.27388999999999,55.06666,0 -67.30943000000001,55.01554000000001,0 -67.02806000000001,54.89778,0 -66.94694,54.80165,0 -66.71806000000001,54.73193,0 -66.71222,54.78194,0 -66.605,54.79084,0 -66.73166999999999,54.94332,0 -66.65944000000002,54.97942,0 -66.76585,55.10556,0 -66.74083,55.17749,0 -66.6889,55.20416,0 -66.83389,55.31332,0 -66.80029,55.33999000000001,0 -66.68804,55.3011,0 -66.68195,55.25333,0 -66.57640000000001,55.25055,0 -66.23999000000001,54.99194000000001,0 -66.035,54.90972,0 -65.95778,54.92944000000001,0 -65.85834,54.90778,0 -65.83528,54.83138,0 -65.68998000000001,54.71332,0 -65.44387999999999,54.74583000000001,0 -65.09445,54.69555,0 -64.81166,54.74917000000001,0 -64.55222000000001,54.72055,0 -64.34723,54.75527,0 -63.97619000000001,54.62645000000001,0 -63.72084,54.59665,0 -63.62361,54.66610000000001,0 -63.54194,54.84055,0 -63.43528000000001,54.97360000000001,0 -63.56611000000002,55.06027,0 -63.51944,55.1836,0 -63.11423,55.31608,0 -63.09333,55.34583000000001,0 -63.17445,55.40971,0 -63.14416,55.52638000000001,0 -63.29833000000001,55.52333,0 -63.44806,55.59943,0 -63.48833000000001,55.69665000000001,0 -63.71528,55.85917000000001,0 -63.69416,55.94222,0 -63.445,56.02276,0 -63.81333,56.0425,0 -63.88,56.07695,0 -63.84778,56.12027,0 -64.02028,56.07638000000001,0 -64.01083,56.15999,0 -63.87694,56.21721,0 -63.97472000000001,56.25916000000001,0 -64.08667,56.25916000000001,0 -64.13667,56.3036,0 -64.10584,56.33139000000001,0 -64.11417,56.39027000000001,0 -64.16972,56.42778,0 -63.97472000000001,56.41277,0 -63.87138,56.43942,0 -63.94611,56.48388,0 -63.945,56.54972,0 -64.12888,56.69943,0 -64.08362,56.76554,0 -63.99222000000002,56.78582,0 -64.00528,56.85054000000001,0 -63.87138,56.87805000000001,0 -63.91779,56.93027000000001,0 -63.86333,56.99276,0 -63.87361,57.06638000000002,0 -63.78751,57.11193999999999,0 -63.79249999999999,57.16526000000001,0 -63.7411,57.23277,0 -63.80500000000001,57.28027,0 -63.83944,57.20054,0 -63.86584000000001,57.19583000000001,0 -63.87583,57.23193000000001,0 -63.83694000000001,57.32583000000002,0 -63.7075,57.3786,0 -63.75723000000001,57.43444000000001,0 -63.76501,57.57388,0 -63.60723,57.66361,0 -63.58944,57.72526,0 -63.65333,57.7311,0 -63.70055,57.66277000000001,0 -63.76528,57.73277000000001,0 -63.82556000000002,57.69027,0 -63.89194,57.73027,0 -63.92666,57.79722,0 -64.00666,57.81276,0 -64.02251,57.77138,0 -64.05749,57.76943000000001,0 -64.21445,57.97748,0 -64.20862,58.03334,0 -64.43195,58.08611,0 -64.38721,58.19137000000001,0 -64.21861,58.23082,0 -64.16306,58.34389,0 -64.03667,58.37499,0 -64.0025,58.42527,0 -63.85918,58.43776,0 -63.81555,58.47805000000001,0 -63.84584,58.50805,0 -63.89806,58.56499,0 -64.04277,58.52388000000001,0 -64.10056,58.57916,0 -64.08611999999999,58.63777,0 -64.02916999999999,58.68694,0 -63.50611,58.73194,0 -63.4825,58.77055000000001,0 -63.58834,58.84638000000002,0 -63.77083,58.87554000000001,0 -63.83333,58.8325,0 -63.97472000000001,58.8261,0 -64.15611,58.74528,0 -64.25751,58.77944000000001,0 -64.25861999999999,58.85000000000001,0 -64.30029,58.88249,0 -64.56807000000001,58.88721000000001,0 -64.71888,58.94527000000001,0 -64.80943000000002,58.9111,0 -64.88332,58.93194,0 -64.83890000000001,58.9586,0 -64.86472000000001,58.99054,0 -64.82944000000002,59.03249,0 -64.72,59.07,0 -64.46084000000001,58.98443,0 -64.29138,59.00527,0 -64.34972000000001,59.06666000000001,0 -64.48917,59.09943,0 -64.49306,59.15888,0 -64.53471999999999,59.19749000000001,0 -64.50751,59.25,0 -64.53834000000001,59.29055,0 -64.49916,59.33972000000001,0 -64.53806,59.37360000000001,0 -64.485,59.39638,0 -64.50696000000001,59.42832000000001,0 -64.34334,59.50111,0 -64.43249,59.52944,0 -64.70084,59.44583000000001,0 -64.83307000000001,59.53971,0 -64.77306,59.69887,0 -64.81416000000002,59.81889,0 -64.74445,59.84499,0 -64.76778,59.87999,0 -64.66191000000001,59.88361,0 -64.6536,59.92999,0 -64.84778,59.96416,0 -64.90639,60.04805,0 -64.79695,60.03972,0 -64.7025,60.06499,0 -64.73416,60.10333,0 -64.59778,60.11749,0 -64.63721,60.17833,0 -64.76723,60.175,0 -64.8586,60.25583000000001,0 -64.56751,60.30471,0 -64.53325,60.3025,0 -64.48108000000001,60.36652000000001,0 -64.39993,60.38216,0 -64.6279,61.34092,0 -64.29521,62.44402000000001,0 -63.98004,63.25823000000001,0 -63.97128,63.76601000000001,0 -63.50727,64.82534,0 -62.6493,65.48196,0 -62.08899,65.51698,0 -61.97518,65.88468,0 -61.43238,66.19110000000001,0 -60.93336000000002,66.62885,0 -61.12597,66.90025,0 -61.94015000000002,67.30296000000001,0 -63.71739,67.86327,0 -64.37838000000001,68.03400000000001,0 -66.06807000000001,68.37982,0 -66.53207,68.44109,0 -66.59773,69.21590000000001,0 -66.70717000000001,69.43039,0 -67.20399999999999,69.97538,0 -67.50825,70.17017,0 -67.77307,70.28399,0 -68.07777,70.31999999999999,0 -68.07777,75.31999999999999,0 -68.07777,75.93836,0 -72.5564,77.34211000000001,0 -72.55276000000001,78.52284,0 -67.76724,79.10155,0 -63.60658000000001,76.32865,0 -63.58193000000001,76.31222,0 -62.34444,76.01053,0 -59.57289,75.70371,0 -57.85474000000001,74.69123,0 -56.16726000000001,72.58955,0 -55.98318,71.67934000000001,0 -55.18546,71.07594,0 -55.07296,70.48277,0 -55.02182000000001,69.63903000000001,0 -53.82526,67.68565,0 -54.00934,67.10271,0 -53.71276,66.05441999999999,0 -53.01731,65.35898,0 -52.50595000000001,64.94479,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<innerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-56.12872,46.48062,0 -56.12872,47.16830000000001,0 -56.74985000000001,47.16830000000001,0 -56.74985000000001,46.48062,0 -56.12872,46.48062,0 </coordinates>
    +					</LinearRing>
    +					<LinearRing>
    +						<coordinates>
    +-57.07413,53.19926000000002,0 -57.10572,51.03270000000001,0 -57.32588,50.86326,0 -57.94108,49.80638,0 -58.39064,49.19907,0 -58.77711000000001,48.81260000000002,0 -59.22667000000001,48.58387000000001,0 -59.28977,48.48923,0 -59.43964,47.90558999999999,0 -59.3292,47.5901,0 -59.2109,47.527,0 -58.29600000000001,47.62165000000001,0 -57.71236,47.58221000000001,0 -56.97096000000001,47.3456,0 -56.81322,47.47968,0 -56.30055,47.5901,0 -56.01662,47.24306000000001,0 -55.88254000000001,47.25095000000001,0 -55.7169,47.14054000000001,0 -55.89832,47.07743000000001,0 -56.10338,46.90392,0 -56.10338,46.84081,0 -55.3541,46.82505,0 -55.14116,46.9749,0 -54.99129,47.17208000000001,0 -54.36033000000001,47.37714,0 -54.03695,47.25095000000001,0 -54.21047,46.91969000000001,0 -54.21047,46.78561,0 -54.06062,46.77773,0 -53.91864,46.84081,0 -53.80033,46.94335,0 -53.6426,46.91969000000001,0 -53.68204000000001,46.69886,0 -53.61893000000001,46.61998,0 -53.53217000000001,46.59632000000001,0 -53.42175000000002,46.66731,0 -53.32711000000001,46.66731,0 -53.12204000000001,46.59632000000001,0 -52.90121,46.76984000000002,0 -52.80656000000001,47.16419000000001,0 -52.59361000000001,47.51122,0 -52.68037,47.76362,0 -52.81445000000001,47.83459,0 -52.88544,47.69263,0 -53.09838,47.69263,0 -53.01952000000001,47.90558999999999,0 -52.77501,48.08699,0 -52.72769000000001,48.16587000000001,0 -52.93275,48.20530000000001,0 -53.18515000000001,48.10277,0 -53.36654000000001,48.25262,0 -53.05107,48.41823999999999,0 -52.94065000000001,48.59177,0 -53.08260000000001,48.72585,0 -53.26402000000001,48.59966,0 -53.41388,48.63908000000001,0 -53.50852,48.59177,0 -53.57161,48.71006000000001,0 -53.56372,48.82837,0 -53.59527,48.93091,0 -53.43753,49.27005,0 -53.91076,49.6092,0 -54.09216,49.77482000000001,0 -54.81778000000001,49.70385,0 -55.07016,49.58554000000001,0 -55.33043000000001,49.53822,0 -55.72480000000001,49.68018,0 -55.78001,49.76695,0 -55.43297,49.94835000000001,0 -55.50396,50.03511,0 -55.44086,50.79227,0 -55.53551,50.93423,0 -55.39375,51.64408,0 -55.61563,52.12514000000001,0 -55.60509,52.46212,0 -55.76833000000001,52.86228000000001,0 -55.742,53.19926000000002,0 -57.07413,53.19926000000002,0 </coordinates>
    +					</LinearRing>
    +				</innerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-52.52415,84.90001,0 -52.52951,82.29564000000001,0 -52.95565,82.35877000000001,0 -53.42914,82.29564000000001,0 -54.21828,82.37454,0 -56.03332,82.31142000000001,0 -58.87425,82.10625,0 -59.49456,82.01636000000002,0 -61.43191,82.50069000000002,0 -62.89619,82.57953999999999,0 -63.38053000000001,82.86113,0 -64.77722,82.92871,0 -67.50302000000001,82.99629,0 -67.50302000000001,84.90001,0 -67.50302000000001,89.90000000000001,0 -66.50375,89.90000000000001,0 -65.50446,89.90000000000001,0 -64.50519,89.90000000000001,0 -63.50591,89.90000000000001,0 -62.50663999999999,89.90000000000001,0 -61.50736,89.90000000000001,0 -60.50808,89.90000000000001,0 -59.50880000000001,89.90000000000001,0 -58.50953,89.90000000000001,0 -57.51024,89.90000000000001,0 -56.51097000000001,89.90000000000001,0 -55.5117,89.90000000000001,0 -54.51242000000001,89.90000000000001,0 -53.51314,89.90000000000001,0 -52.51387,89.90000000000001,0 -52.52415,84.90001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -03.30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-3.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0350</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-55.39375,51.64408,0 -55.53551,50.93423,0 -55.44086,50.79227,0 -55.50396,50.03511,0 -55.43297,49.94835000000001,0 -55.78001,49.76695,0 -55.72480000000001,49.68018,0 -55.33043000000001,49.53822,0 -55.07016,49.58554000000001,0 -54.81778000000001,49.70385,0 -54.09216,49.77482000000001,0 -53.91076,49.6092,0 -53.43753,49.27005,0 -53.59527,48.93091,0 -53.56372,48.82837,0 -53.57161,48.71006000000001,0 -53.50852,48.59177,0 -53.41388,48.63908000000001,0 -53.26402000000001,48.59966,0 -53.08260000000001,48.72585,0 -52.94065000000001,48.59177,0 -53.05107,48.41823999999999,0 -53.36654000000001,48.25262,0 -53.18515000000001,48.10277,0 -52.93275,48.20530000000001,0 -52.72769000000001,48.16587000000001,0 -52.77501,48.08699,0 -53.01952000000001,47.90558999999999,0 -53.09838,47.69263,0 -52.88544,47.69263,0 -52.81445000000001,47.83459,0 -52.68037,47.76362,0 -52.59361000000001,47.51122,0 -52.80656000000001,47.16419000000001,0 -52.90121,46.76984000000002,0 -53.12204000000001,46.59632000000001,0 -53.32711000000001,46.66731,0 -53.42175000000002,46.66731,0 -53.53217000000001,46.59632000000001,0 -53.61893000000001,46.61998,0 -53.68204000000001,46.69886,0 -53.6426,46.91969000000001,0 -53.80033,46.94335,0 -53.91864,46.84081,0 -54.06062,46.77773,0 -54.21047,46.78561,0 -54.21047,46.91969000000001,0 -54.03695,47.25095000000001,0 -54.36033000000001,47.37714,0 -54.99129,47.17208000000001,0 -55.14116,46.9749,0 -55.3541,46.82505,0 -56.10338,46.84081,0 -56.10338,46.90392,0 -55.89832,47.07743000000001,0 -55.71691000000001,47.14054000000001,0 -55.88254000000001,47.25095000000001,0 -56.01662,47.24306000000001,0 -56.30055,47.5901,0 -56.81322,47.47968,0 -56.97096000000001,47.3456,0 -57.71236,47.58221000000001,0 -58.29600000000001,47.62165000000001,0 -59.2109,47.527,0 -59.3292,47.5901,0 -59.43964,47.90558999999999,0 -59.28977,48.48923,0 -59.22667000000001,48.58387000000001,0 -58.77711000000001,48.81260000000002,0 -58.39064,49.19907,0 -57.94108,49.80638,0 -57.32588,50.86326,0 -57.10572,51.03270000000001,0 -57.07413,53.19926000000002,0 -55.742,53.19926000000002,0 -55.76833000000001,52.86228000000001,0 -55.60509,52.46212,0 -55.61563,52.12514000000001,0 -55.39375,51.64408,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -03:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-3);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0300</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-56.12872,46.48062,0 -56.74985000000001,46.48062,0 -56.74985000000001,47.16830000000001,0 -56.12872,47.16830000000001,0 -56.12872,46.48062,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-37.51992,84.90001,0 -37.51992,83.74590000000001,0 -34.51386,83.78599,0 -29.51426,83.7223,0 -28.22116000000001,83.70583000000001,0 -23.25304,83.14206,0 -22.56975,83.06453000000002,0 -21.33607,82.72414000000001,0 -19.25519,82.23316,0 -18.10955,81.78892999999999,0 -17.59517,81.92921,0 -16.16897,82.02274,0 -13.71401,81.90584,0 -12.03061,81.69541,0 -12.03061,81.55513000000001,0 -14.53233,80.80695,0 -15.51431,80.61991,0 -17.31461,79.94186999999999,0 -17.47827,79.26384,0 -17.52503,77.76749,0 -18.06279,76.85564000000001,0 -18.50702,75.85029,0 -17.24447,75.26577,0 -17.26786,74.98521,0 -18.13293,74.58775,0 -19.34872,74.17858,0 -20.28394,73.40702,0 -21.42958000000001,72.84589,0 -21.78029,72.14449000000001,0 -21.17239,70.78841,0 -21.35944,70.48445,0 -22.03747,69.97009,0 -22.51389,69.82120999999999,0 -23.90792,69.38557000000002,0 -25.89525,68.70753000000001,0 -27.43837,68.38021000000001,0 -29.7998,67.98274000000001,0 -31.90405,67.86584000000001,0 -33.65758,66.67344,0 -35.45789,65.8785,0 -37.51991,65.16843,0 -37.51991,64.96842,0 -37.51991,59.96843000000001,0 -37.51991,54.96843,0 -37.51991,49.96843,0 -37.51991,44.96843000000001,0 -37.51991,39.96843,0 -37.51991,34.96843,0 -37.51991,29.96842,0 -37.51991,25.36843,0 -37.51991,20.36843,0 -37.51991,15.36843,0 -37.51991,15.36609,0 -37.51991,10.36609,0 -37.51991,5.366087,0 -37.51991,0.3660925,0 -37.51991,-4.633912,0 -37.28973,-4.726108,0 -37.17445,-4.918609,0 -36.88278,-4.949163,0 -36.79723,-5.046391,0 -36.68722,-5.098339,0 -36.54417,-5.134447,0 -36.52583,-5.108608,0 -36.58167,-5.088051,0 -36.28028,-5.11111,0 -36.25472,-5.10166,0 -36.28751,-5.091660000000001,0 -36.12889,-5.097502,0 -35.93472,-5.052223,0 -35.60445000000001,-5.110839,0 -35.4175,-5.214717000000001,0 -35.26195,-5.484726,0 -35.14557,-5.938893000000001,0 -35.105,-5.981112000000001,0 -35.09278,-6.178887000000001,0 -35.11528,-6.165838,0 -35.15501,-6.208894,0 -35.13584,-6.228612,0 -35.08778,-6.199164,0 -35.04139,-6.233887000000001,0 -35.01501,-6.374998000000001,0 -34.98028,-6.40639,0 -34.94334,-6.779997000000001,0 -34.91195,-6.879997000000001,0 -34.85834,-6.919444,0 -34.915,-7.127497,0 -34.85695,-7.075837,0 -34.82556,-6.987502,0 -34.83084,-7.108605,0 -34.7925,-7.178607,0 -34.8075,-7.515000000000001,0 -34.81779,-7.546942,0 -34.8932,-7.543089,0 -34.92334,-7.563889000000001,0 -34.84056,-7.554448,0 -34.80083,-7.63111,0 -34.83084,-7.683337,0 -34.87555,-7.622227000000001,0 -34.85167,-7.695559000000002,0 -34.90696,-7.735834,0 -34.85834,-7.73028,0 -34.89333,-7.804998000000001,0 -34.84472,-7.835554000000001,0 -34.81528,-7.935283000000001,0 -34.85612,-8.073612000000001,0 -34.92668,-8.038333,0 -34.88001,-8.113049999999999,0 -34.94,-8.270550999999999,0 -34.93584,-8.350552,0 -35.02194,-8.423056000000001,0 -34.98694,-8.418888000000001,0 -34.99195,-8.448895,0 -34.97001,-8.430552000000001,0 -35.16612,-8.960283,0 -35.32751,-9.228889000000001,0 -35.45667,-9.346393000000001,0 -35.5825,-9.535833,0 -35.65751,-9.586387,0 -35.69028,-9.668889999999999,0 -35.77306,-9.695277000000001,0 -35.73695,-9.651394000000002,0 -35.79362,-9.589725,0 -35.79222,-9.683056000000001,0 -35.86,-9.739160999999999,0 -35.90528,-9.621667,0 -35.95196,-9.602784,0 -35.86917,-9.764163999999999,0 -35.79557,-9.728334,0 -36.04444000000001,-10.07583,0 -36.28139,-10.28695,0 -36.28445,-10.3525,0 -36.40483,-10.49879,0 -36.56751,-10.54445,0 -36.85861,-10.73916,0 -37.02166,-10.93584,0 -37.00862,-10.81056,0 -37.06889000000001,-10.79445,0 -37.07779,-10.73389,0 -37.15333,-10.74861,0 -37.09417000000001,-10.80111,0 -37.14168,-10.82528,0 -37.04028000000001,-10.8375,0 -37.05333,-10.94917,0 -37.15667,-11.10444,0 -37.20723,-11.03723,0 -37.27916,-11.02528,0 -37.26028,-11.07222,0 -37.20195,-11.0925,0 -37.22112,-11.10778,0 -37.16446,-11.11389,0 -37.15112,-11.19194,0 -37.26501,-11.28667,0 -37.33000000000001,-11.42723,0 -37.39834,-11.40223,0 -37.36362,-11.31556,0 -37.27584,-11.25973,0 -37.29862,-11.20195,0 -37.30555,-11.245,0 -37.34779,-11.18778,0 -37.31362,-11.26305,0 -37.34861,-11.25028,0 -37.33139,-11.28667,0 -37.39528,-11.33389,0 -37.41223,-11.40278,0 -37.46917,-11.36667,0 -37.39305,-11.45,0 -37.48791,-11.52023,0 -37.39806,-11.49972,0 -37.36223,-11.45472,0 -37.51972,-11.77389,0 -37.51972,-12.17389,0 -37.51974,-17.17389,0 -37.51975,-22.17389,0 -37.51977,-27.17389,0 -37.51978,-32.17389,0 -37.51979,-37.17389,0 -37.51979,-42.17389,0 -37.5198,-47.08813000000001,0 -37.51982,-52.08813,0 -40.01502,-52.08813,0 -45.01502000000001,-52.08813,0 -45.01502000000001,-54.93388,0 -42.51982,-54.93388,0 -37.51983,-54.93388,0 -37.51984,-59.90001,0 -37.51985000000001,-64.90000000000001,0 -37.51987,-69.90000000000001,0 -37.51987,-74.90000000000001,0 -37.51988,-79.90000000000001,0 -37.51989,-84.89999,0 -37.51991,-89.90000000000001,0 -38.51878,-89.90000000000001,0 -39.51765,-89.90000000000001,0 -40.51651,-89.90000000000001,0 -41.51539,-89.90000000000001,0 -42.51426,-89.90000000000001,0 -43.51314000000001,-89.90000000000001,0 -44.51201,-89.90000000000001,0 -45.51088,-89.90000000000001,0 -46.50975,-89.90000000000001,0 -47.50861,-89.90000000000001,0 -48.50749,-89.90000000000001,0 -49.50636,-89.90000000000001,0 -50.50524000000001,-89.90000000000001,0 -51.5041,-89.90000000000001,0 -52.50298000000001,-89.90000000000001,0 -52.50305,-87.53888000000001,0 -52.50323000000001,-82.53889,0 -52.50335,-79.03888000000001,0 -52.50352,-74.03888000000001,0 -52.50368,-69.03889,0 -52.50372,-67.88475,0 -52.50385,-62.88475,0 -52.50398000000001,-57.88475,0 -52.50412000000001,-52.88476000000001,0 -52.50425,-47.88475,0 -52.50438,-42.88476,0 -52.50452,-37.88475,0 -52.50465000000001,-32.88476,0 -52.74139000000001,-33.25695,0 -53.3743,-33.74068,0 -53.48251000000001,-33.85445,0 -53.53723000000001,-34.06612,0 -53.75029,-34.25751,0 -53.77139,-34.39083,0 -53.98890000000001,-34.50389,0 -54.13,-34.62389000000001,0 -54.14500000000001,-34.67139,0 -54.25695,-34.68945000000001,0 -54.2425,-34.58139,0 -54.32557,-34.56278,0 -54.28167,-34.59306,0 -54.34583,-34.64362,0 -54.30083000000001,-34.65,0 -54.27916,-34.69389,0 -54.53111,-34.80609000000001,0 -54.95129000000001,-34.9778,0 -54.95612000000001,-34.94028,0 -55.05556000000001,-34.8914,0 -55.25666,-34.90723,0 -55.36501,-34.81056,0 -55.68557000000001,-34.77444,0 -56.16861,-34.93194,0 -56.22084,-34.88945,0 -56.32028,-34.91057,0 -56.42556,-34.84306,0 -56.36723,-34.79444,0 -56.5639,-34.77251,0 -56.88779,-34.66612,0 -57.11778,-34.46222,0 -57.45778,-34.44222,0 -57.51945000000001,-34.46445,0 -57.58195000000001,-34.43917,0 -57.84028,-34.49473,0 -57.90722,-34.37,0 -58.08334,-34.18695,0 -58.18541,-34.16549,0 -58.40885999999999,-34.22926,0 -58.47195,-34.28111,0 -58.56667,-34.29056,0 -58.48417000000001,-34.34166,0 -58.52528,-34.43972,0 -58.46972000000001,-34.53972,0 -58.36945000000001,-34.58389,0 -58.34633000000001,-34.63362,0 -58.34472,-34.63612,0 -58.19723,-34.73861,0 -57.81361,-34.86584,0 -57.49889,-35.04556,0 -57.33555,-35.15556,0 -57.18833000000001,-35.32055,0 -57.12473,-35.41695,0 -57.12972000000001,-35.47029,0 -57.37612,-35.78139,0 -57.37666999999999,-35.96278,0 -57.23667,-36.18418,0 -57.05334,-36.31417,0 -56.93389,-36.34723,0 -56.94195,-36.38334,0 -56.84778,-36.34222,0 -56.76778000000001,-36.3475,0 -56.77361,-36.30445,0 -56.74167,-36.31834,0 -56.67168000000001,-36.57584,0 -56.66306000000001,-36.90056,0 -57.09973000000002,-37.47445,0 -57.48056,-37.82584,0 -57.54112,-37.98195000000001,0 -57.53306,-38.08611,0 -57.63473000000001,-38.18,0 -57.84584,-38.29917,0 -58.31223000000001,-38.48972,0 -58.72279,-38.58055000000001,0 -59.03807,-38.69251,0 -59.61,-38.77973,0 -59.78973,-38.83694,0 -60.86000000000001,-38.97639,0 -61.16223,-38.99889,0 -61.38361,-38.97973000000001,0 -61.56255,-39.01225,0 -61.85882,-39.24178,0 -62.05002,-39.48876,0 -62.11639,-39.83945,0 -62.18917,-39.86,0 -62.3075,-39.81861,0 -62.28612,-39.85473,0 -62.32000000000001,-39.86834,0 -62.36418000000001,-40.05528,0 -62.35278000000002,-40.18167,0 -62.48945,-40.29945,0 -62.42084,-40.45945,0 -62.33139000000001,-40.49834,0 -62.29111,-40.56583,0 -62.26000000000001,-40.56001,0 -62.26139000000001,-40.63334,0 -62.34807,-40.60723,0 -62.32666,-40.67834000000001,0 -62.18389,-40.62972000000001,0 -62.22972,-40.66361,0 -62.32110999999999,-40.86723,0 -62.72333,-41.04611,0 -62.80139,-41.04333,0 -63.03334000000002,-41.14834,0 -63.14195000000001,-41.16056,0 -63.8075,-41.15917,0 -63.96001,-41.06445,0 -64.05833,-41.04501,0 -64.05334000000001,-40.99918,0 -64.14223,-41.02195,0 -64.41251,-40.90945,0 -64.69112,-40.83889,0 -64.88528,-40.83806,0 -64.91166,-40.82028,0 -64.89612,-40.79862,0 -64.74918,-40.80278,0 -64.75194999999999,-40.77695,0 -64.7975,-40.77223,0 -64.77528,-40.73278,0 -64.98141,-40.72195,0 -64.9264,-40.72862,0 -64.93807,-40.75639,0 -64.90611,-40.76862,0 -65.0125,-40.77195,0 -65.12584,-40.84056,0 -65.17973000000001,-40.99667,0 -65.07111999999999,-41.43334,0 -64.99111000000001,-41.51083000000001,0 -65.02916999999999,-41.67556,0 -64.99084000000001,-41.77862,0 -65.07445,-41.93167,0 -65.06223,-42.01,0 -64.99418,-42.11222,0 -64.87139999999999,-42.19195,0 -64.46251,-42.26362,0 -64.60695,-42.41889,0 -64.45193999999999,-42.44584,0 -64.30417,-42.41528,0 -64.1189,-42.43444000000001,0 -64.04528999999999,-42.37251,0 -64.06305999999999,-42.27862,0 -64.33446000000001,-42.23667,0 -64.19917,-42.22417,0 -63.87195000000001,-42.10250000000001,0 -63.74694,-42.09056000000001,0 -63.59083,-42.31695,0 -63.57805,-42.60612,0 -63.63334,-42.71278,0 -63.62583,-42.76194,0 -63.69306000000001,-42.82168,0 -64.08694,-42.89028,0 -64.24918,-42.77612,0 -64.19611999999999,-42.63889,0 -64.39806,-42.51584,0 -64.62194,-42.52,0 -64.82084999999999,-42.62862,0 -64.96056000000002,-42.66639,0 -65.01806999999999,-42.73944000000001,0 -64.99278,-42.79361,0 -64.80862000000001,-42.84028000000001,0 -64.59390000000001,-42.94112000000001,0 -64.50389,-42.93806000000001,0 -64.41028,-42.97805,0 -64.31556999999999,-42.94944,0 -64.29501000000001,-42.99195,0 -64.49168,-43.08445,0 -64.80029999999999,-43.16112,0 -65.02806,-43.30445,0 -65.03722000000001,-43.39418,0 -65.33029000000001,-43.66779,0 -65.32001,-43.81945,0 -65.25834999999999,-43.96973,0 -65.21028,-43.95528,0 -65.22445999999999,-44.10751000000001,0 -65.29695,-44.13223,0 -65.31249,-44.18696,0 -65.22806,-44.33667000000001,0 -65.34222,-44.43862,0 -65.27667,-44.48028,0 -65.35945,-44.48306,0 -65.38751000000001,-44.52166,0 -65.3614,-44.54418,0 -65.44056999999999,-44.53807,0 -65.59945999999999,-44.61805,0 -65.71917999999999,-44.77612,0 -65.71084999999999,-44.83362,0 -65.53055999999999,-44.895,0 -65.58474,-44.92445,0 -65.60474000000001,-45.0164,0 -65.68556,-45.04306,0 -65.85139,-44.98584,0 -65.87529000000001,-45.01944000000001,0 -65.94917,-45.02223,0 -66.01528999999999,-44.97389,0 -66.19473000000001,-44.95917,0 -66.28500000000001,-45.03167000000001,0 -66.52196000000001,-45.08667000000001,0 -66.53473,-45.11584,0 -66.45975,-45.14362,0 -66.58863,-45.13862,0 -66.51918000000001,-45.17889,0 -66.53528,-45.20444000000001,0 -66.84668000000001,-45.21029,0 -66.94946,-45.25557,0 -67.18362000000001,-45.52861,0 -67.31611000000001,-45.61501,0 -67.34166999999999,-45.77890000000001,0 -67.42222,-45.82195,0 -67.58436000000002,-46.00031,0 -67.58667,-46.00389000000001,0 -67.62029,-46.17139,0 -67.53667,-46.4175,0 -67.33805,-46.62528,0 -67.12445,-46.72639,0 -67.05473000000001,-46.81001,0 -66.71666999999999,-47.04000000000001,0 -66.20916,-47.09361000000001,0 -65.99335000000001,-47.06667000000001,0 -65.90001,-47.09862,0 -65.77224,-47.19834,0 -65.73222,-47.34418,0 -65.75696000000001,-47.54194,0 -65.87584,-47.76222,0 -66.02251,-47.76084,0 -66.12083,-47.81973,0 -66.32168,-47.87306,0 -66.38863000000001,-47.8625,0 -66.32418,-47.88278,0 -65.97333999999999,-47.78389000000001,0 -65.91195999999999,-47.80418,0 -65.87000999999999,-47.90222000000001,0 -65.78722999999999,-47.93557000000001,0 -65.79584,-47.97028000000001,0 -65.97139,-47.96111000000001,0 -65.89778,-48.09973000000001,0 -66.07944999999999,-48.10444,0 -66.1414,-48.18472,0 -66.33556,-48.28362000000001,0 -66.32751,-48.34555,0 -66.44362,-48.34723000000001,0 -66.46805999999999,-48.39917000000001,0 -66.66750999999999,-48.44167,0 -66.82668,-48.57111,0 -67.08278,-48.63889000000001,0 -67.23972999999999,-48.82278000000001,0 -67.39917,-48.89056000000001,0 -67.50834999999999,-48.98417,0 -67.49554000000002,-53.95288,0 -66.92418000000001,-54.21918,0 -66.70361,-54.28917,0 -66.54667000000001,-54.41389,0 -66.27166000000001,-54.52806000000001,0 -65.82751,-54.65584,0 -65.67361,-54.67306,0 -65.22362,-54.63361,0 -64.55267000000001,-54.715,0 -64.4539,-54.75833,0 -64.41278,-54.73667,0 -64.38112,-54.78861,0 -64.35806,-54.70639,0 -64.17834000000001,-54.75083999999999,0 -64.16833,-54.71417000000002,0 -64.06417999999999,-54.75000000000001,0 -63.92001,-54.71417000000002,0 -63.81362000000001,-54.7314,0 -63.96861000000001,-54.81807,0 -63.97278,-54.75973000000001,0 -64.25389,-54.84223,0 -64.25306999999999,-54.77972,0 -64.30945,-54.77778,0 -64.41862,-54.83862000000001,0 -64.49612000000001,-54.82696,0 -64.60972,-54.9,0 -64.67834000000001,-54.90723,0 -65.37667999999999,-54.93056,0 -65.47139,-54.88361,0 -65.49001,-54.93472000000001,0 -65.59889,-54.93056,0 -65.6575,-54.97223,0 -65.70585,-54.96611,0 -65.73779,-54.90222,0 -65.95529000000001,-54.89612000000001,0 -66.01334,-54.95612000000001,0 -66.37195,-54.98500000000001,0 -66.45808000000001,-55.05168,0 -66.64944,-55.02584,0 -66.79639,-54.94555,0 -67.01134,-54.90862,0 -67.84778,-54.87611,0 -68.28834999999999,-54.79362,0 -68.32334,-54.84361,0 -68.55723999999999,-54.88001,0 -68.6431,-54.88861,0 -68.63612000000001,-54.80477,0 -68.63583,-54.78834,0 -68.61758000000002,-52.64151,0 -68.44176,-52.37778000000001,0 -68.4375,-52.32833,0 -68.82861,-52.27973,0 -69.23362,-52.14334000000001,0 -69.46139000000001,-52.14362000000001,0 -69.99834,-51.9964,0 -71.9314,-51.98889000000001,0 -71.96251,-51.96029000000001,0 -71.93946,-51.89889,0 -71.96639999999999,-51.84861,0 -72.13555,-51.74001,0 -72.28558,-51.69917000000001,0 -72.33085,-51.58723000000001,0 -72.43944999999999,-51.55972000000001,0 -72.33945,-51.47583,0 -72.30917,-51.30222,0 -72.24529000000001,-51.24443999999999,0 -72.37167,-51.16473000000001,0 -72.39778,-51.11418000000001,0 -72.34113000000001,-51.03666,0 -72.26694999999999,-51.01723,0 -72.24223000000001,-50.85111,0 -72.34166999999999,-50.7439,0 -72.29416999999999,-50.64972000000001,0 -72.50112,-50.60334,0 -72.62973,-50.67305,0 -72.75834999999999,-50.6189,0 -72.90611,-50.6675,0 -73.01806000000001,-50.74945,0 -73.14445000000001,-50.76750000000001,0 -73.18085000000001,-50.72918,0 -73.17834000000001,-50.64777999999999,0 -73.24918,-50.57639,0 -73.28001999999999,-50.32278,0 -73.34639,-50.23806,0 -73.53223,-50.14056,0 -73.46722,-50.00666,0 -73.56445000000001,-49.92472999999999,0 -73.44446000000001,-49.7825,0 -73.53333000000001,-49.68973,0 -73.51000999999999,-49.61334,0 -73.58168000000001,-49.53196,0 -73.52972,-49.49639000000001,0 -73.53111,-49.43111,0 -73.45252000000001,-49.38528000000001,0 -73.46474000000001,-49.31139000000001,0 -73.13528,-49.30556,0 -73.08278000000001,-49.26917000000001,0 -73.16768999999999,-49.25264000000001,0 -73.1664,-49.20001000000001,0 -72.98805,-48.98167,0 -72.75528,-48.92806,0 -72.56419,-48.80445,0 -72.58974000000001,-48.4664,0 -72.4075,-48.39694,0 -72.36668,-48.34612000000001,0 -72.28973000000001,-48.3475,0 -72.32278,-48.07556,0 -72.51501,-47.96111000000001,0 -72.53528000000001,-47.90917000000001,0 -72.33168000000001,-47.60168,0 -72.30556,-47.50723,0 -72.35945,-47.48111999999999,0 -72.34723,-47.445,0 -72.25639,-47.41112,0 -72.18056,-47.42028000000001,0 -72.08362,-47.35472000000001,0 -72.01501,-47.27444000000001,0 -72.03195,-47.20333,0 -71.91752,-47.23446,0 -71.86528,-47.21694000000001,0 -71.86057,-47.14194,0 -71.99667,-47.06111,0 -71.90472,-47.00084,0 -71.96056,-46.95,0 -71.92471999999999,-46.85056,0 -71.94029000000001,-46.81556000000001,0 -71.80891,-46.78417,0 -71.68195,-46.69889,0 -71.66916999999999,-46.54750000000001,0 -71.74890000000001,-46.38612000000001,0 -71.74751000000001,-46.24278,0 -71.90611,-46.14834000000001,0 -71.76028,-46.11417,0 -71.645,-45.99945,0 -71.60167,-45.97473000000002,0 -71.63001000000001,-45.90084000000001,0 -71.75501,-45.84833,0 -71.73833999999999,-45.79028,0 -71.79250999999999,-45.73251,0 -71.78055999999999,-45.64889000000001,0 -71.73278999999999,-45.59890000000001,0 -71.75666,-45.56278,0 -71.50249,-45.51556,0 -71.46751999999999,-45.4775,0 -71.50639,-45.41139,0 -71.37334,-45.36944,0 -71.29779000000001,-45.29334000000001,0 -71.33835000000001,-45.22055,0 -71.45361,-45.15918,0 -71.57362000000001,-44.97945,0 -71.67473,-44.98028,0 -71.75335,-44.93194,0 -71.8839,-44.94862,0 -72.06279000000001,-44.90556,0 -72.08085,-44.77473000000001,0 -72.0425,-44.75417,0 -71.84945999999999,-44.79306,0 -71.77,-44.75389,0 -71.65363000000001,-44.78279,0 -71.50084,-44.74305,0 -71.28223,-44.80028000000001,0 -71.22362,-44.745,0 -71.23334,-44.66278,0 -71.19723999999999,-44.59694,0 -71.13473000000001,-44.58389,0 -71.10806,-44.53972,0 -71.20361,-44.42694,0 -71.83835000000001,-44.39334,0 -71.85139,-44.36,0 -71.79389999999999,-44.32557,0 -71.82250999999999,-44.26529,0 -71.79389999999999,-44.21556,0 -71.85474000000001,-44.11084000000001,0 -71.75806,-44.07168,0 -71.65224000000001,-43.95166,0 -71.64724,-43.91861,0 -71.69834,-43.86668,0 -71.75557000000001,-43.82557,0 -71.7375,-43.77194,0 -71.7914,-43.78223,0 -71.81140000000001,-43.74917,0 -71.69862000000001,-43.68723,0 -71.70278999999999,-43.59584,0 -71.78001999999999,-43.54834,0 -71.86973999999999,-43.54028,0 -71.85834,-43.46222,0 -71.94665999999999,-43.44723,0 -71.89418000000001,-43.32306,0 -71.74335000000001,-43.30223,0 -71.73584,-43.17694,0 -71.83638999999999,-43.13195000000001,0 -72.05277,-43.10472,0 -72.13946,-42.99139,0 -72.10196999999999,-42.85472,0 -72.13918,-42.58751000000001,0 -72.10945,-42.525,0 -72.02585000000001,-42.4775,0 -72.07167,-42.42973,0 -72.04779000000001,-42.35667000000001,0 -72.13139,-42.28196,0 -72.01221,-42.13111000000001,0 -71.9639,-42.12750000000001,0 -71.91389,-42.17806000000001,0 -71.72889000000001,-42.10139,0 -71.75977,-41.99973,0 -71.78528,-41.86611,0 -71.91833,-41.64473,0 -71.84002,-41.57528,0 -71.87779000000001,-41.52251,0 -71.86472999999999,-41.44083,0 -71.89944,-41.35861,0 -71.83694,-40.95944,0 -71.95056,-40.73278,0 -71.84390000000001,-40.61639,0 -71.85583,-40.555,0 -71.7914,-40.41528,0 -71.71973,-40.42778,0 -71.66389000000001,-40.33611,0 -71.66972,-40.29473,0 -71.70251,-40.27639,0 -71.72861,-40.30112,0 -71.81863,-40.20196,0 -71.78889,-40.07861,0 -71.66166,-40.09389,0 -71.67307,-40.01611000000001,0 -71.60639999999999,-39.91251,0 -71.66945,-39.85723,0 -71.70444999999999,-39.73278,0 -71.68223999999999,-39.56722,0 -71.61528000000001,-39.61973,0 -71.50945,-39.61945,0 -71.48806,-39.59305,0 -71.48584,-39.5575,0 -71.54085000000002,-39.53139,0 -71.47335,-39.47862,0 -71.44889000000001,-39.4325,0 -71.47057,-39.38807,0 -71.40195,-39.32723,0 -71.38751000000001,-39.23446,0 -71.42279000000001,-39.03694,0 -71.4014,-38.92028,0 -71.22612,-38.80306,0 -70.92583999999999,-38.7425,0 -70.86,-38.68806,0 -70.87667,-38.63889,0 -70.82418,-38.56806,0 -70.97083000000001,-38.41807,0 -71.01306,-38.23056,0 -70.99751999999999,-38.07112,0 -71.18028000000001,-37.69806,0 -71.17167000000001,-37.61806000000001,0 -71.11557000000001,-37.57944,0 -71.10806,-37.45862,0 -71.19667,-37.29668,0 -71.13918,-37.22416,0 -71.13528,-37.14417,0 -71.07919,-37.10418,0 -71.125,-37.07557,0 -71.14612,-36.99667,0 -71.20113000000001,-36.97055,0 -71.12223,-36.94695,0 -71.15833000000001,-36.925,0 -71.14028999999999,-36.8825,0 -71.18696,-36.84889,0 -71.13834,-36.69084,0 -71.04584,-36.68584,0 -71.06056000000001,-36.59306,0 -71.02918,-36.47917,0 -70.93611000000001,-36.48194,0 -70.88751000000001,-36.40055,0 -70.80168999999999,-36.43473,0 -70.70808,-36.41778,0 -70.70473,-36.27057,0 -70.58974000000001,-36.19194,0 -70.57999,-36.14861,0 -70.47387999999999,-36.16529,0 -70.42431999999999,-36.13604,0 -70.36917,-36.04667,0 -70.40945000000001,-35.96362,0 -70.37361,-35.91389,0 -70.41058,-35.88694,0 -70.33778,-35.80834,0 -70.42028000000001,-35.64362,0 -70.39667000000002,-35.50362,0 -70.46223000000001,-35.38305,0 -70.42001000000001,-35.34723,0 -70.55029000000002,-35.29667,0 -70.56751,-35.24778,0 -70.53639,-35.20639,0 -70.36806,-35.15889,0 -70.36194999999999,-35.00445,0 -70.26363000000002,-34.79778,0 -70.31362,-34.74889,0 -70.25306999999999,-34.71806000000001,0 -70.22139,-34.59,0 -70.0475,-34.40917,0 -70.04944999999999,-34.28751,0 -69.89335,-34.28612,0 -69.88918,-34.25945,0 -69.81249,-34.23555,0 -69.8689,-34.13361,0 -69.84889,-33.97195,0 -69.90443999999999,-33.95166,0 -69.88306,-33.87972,0 -69.90611,-33.78778000000001,0 -69.84668000000001,-33.71751,0 -69.88528,-33.66278,0 -69.8689,-33.55194,0 -69.82919,-33.53167,0 -69.77444,-33.38111,0 -69.80779,-33.27389,0 -69.91916999999999,-33.23806,0 -69.99583,-33.30194,0 -70.09889,-33.1725,0 -70.10139,-33.03001,0 -70.03528,-32.99834,0 -69.9875,-32.87889,0 -70.14862000000001,-32.75249,0 -70.17111,-32.60778,0 -70.12946,-32.56528,0 -70.16251,-32.46305,0 -70.23889,-32.4175,0 -70.24432000000002,-32.30984,0 -70.31668999999999,-32.28333,0 -70.32529,-32.13751,0 -70.38001,-32.05167,0 -70.36389,-32.02916,0 -70.26251000000001,-32.04362,0 -70.23445,-31.9425,0 -70.29056,-31.88668,0 -70.41,-31.88084,0 -70.45889,-31.8325,0 -70.47667,-31.72639,0 -70.58805,-31.56167,0 -70.54224000000001,-31.37333,0 -70.5611,-31.31416,0 -70.52943999999999,-31.17444,0 -70.47139,-31.09805,0 -70.44556,-31.09306,0 -70.41335000000001,-31.15389,0 -70.32778999999999,-31.03972,0 -70.25502000000002,-31.03416,0 -70.32778999999999,-30.95834,0 -70.32668,-30.88805,0 -70.20695000000001,-30.505,0 -70.1339,-30.43973,0 -70.16334999999999,-30.36472,0 -70.02972,-30.39778,0 -69.94416,-30.37639,0 -69.89751,-30.3225,0 -69.87000000000001,-30.21084,0 -69.83168000000001,-30.19055,0 -69.85861,-30.10695000000001,0 -69.97083000000002,-30.08444,0 -69.91880000000001,-29.97406,0 -69.90555999999999,-29.79112,0 -69.91501,-29.71778,0 -69.96834,-29.66112,0 -69.96056,-29.55029,0 -70.03140000000001,-29.30639,0 -69.90807,-29.14139,0 -69.79389999999999,-29.09862,0 -69.79528999999999,-28.95112,0 -69.72307000000001,-28.80334,0 -69.74500999999999,-28.66055,0 -69.6664,-28.57279,0 -69.65537999999999,-28.40094,0 -69.53001999999999,-28.31223,0 -69.47612,-28.19139,0 -69.40640999999999,-28.20889,0 -69.30029999999999,-27.99944,0 -69.18584,-27.96584,0 -69.10806,-27.72194,0 -69.06556999999999,-27.69889,0 -69.08416000000001,-27.64583,0 -69.01251000000001,-27.53168000000001,0 -69.00696000000001,-27.45889,0 -68.90472,-27.36667,0 -68.86585,-27.28806,0 -68.87723,-27.23083,0 -68.81084000000001,-27.12056,0 -68.69667,-27.10750000000001,0 -68.58195000000002,-27.16973,0 -68.50306999999999,-27.07472,0 -68.31556999999999,-27.04194,0 -68.29584,-26.89168,0 -68.58362,-26.50528,0 -68.56362,-26.30056,0 -68.39776999999999,-26.15611000000001,0 -68.5975,-25.44333,0 -68.50223,-25.17723,0 -68.46502000000001,-25.14056,0 -68.42028999999999,-25.15279,0 -68.35197000000001,-25.11723,0 -68.42419,-25.03361,0 -68.455,-24.90500000000001,0 -68.54528999999999,-24.87361,0 -68.56916,-24.78612,0 -68.48779,-24.6025,0 -68.44363,-24.62639,0 -68.38918,-24.50166,0 -68.30779,-24.4925,0 -68.25223,-24.39639,0 -67.33584999999999,-24.02167,0 -67.15085000000001,-23.39194000000001,0 -67.00084,-23.00278,0 -67.18362000000001,-22.82166,0 -67.12306,-22.71805000000001,0 -67.01221,-22.64279,0 -67.02251,-22.5239,0 -66.96335000000001,-22.5225,0 -66.92639,-22.47805,0 -66.77473000000001,-22.42695000000001,0 -66.73669,-22.2275,0 -66.6889,-22.19611,0 -66.62806999999999,-22.21305,0 -66.29333,-22.0825,0 -66.22301,-21.78051,0 -66.07751,-21.83195,0 -66.0414,-21.91667,0 -65.92612,-21.93333,0 -65.74806,-22.11166,0 -65.57861,-22.08472,0 -64.98833999999999,-22.09083,0 -64.59473000000001,-22.21167,0 -64.53889,-22.27639,0 -64.56612,-22.36139,0 -64.4314,-22.54778,0 -64.45252000000001,-22.64834,0 -64.36,-22.74084,0 -64.32472,-22.87361,0 -64.25333999999999,-22.56778,0 -64.12667,-22.41028,0 -63.94111,-22.00083,0 -63.73306,-22.00278,0 -63.70057000000001,-22.03861,0 -63.68584000000001,-22.00306,0 -62.81195,-21.99695,0 -62.78946000000001,-22.12278,0 -62.64377,-22.2389,0 -62.62417,-22.30694,0 -62.46028,-22.37971,0 -62.39918,-22.45278,0 -62.29056,-22.48055,0 -62.24694,-22.52889,0 -62.25917000000001,-22.60445,0 -62.19,-22.63,0 -62.18861,-22.71029,0 -62.02944999999999,-22.89194,0 -61.98639,-23.01028000000001,0 -61.78167000000001,-23.1475,0 -61.73973000000001,-23.24362,0 -61.51722,-23.345,0 -61.49694,-23.41361,0 -61.29472,-23.48917,0 -61.11556000000001,-23.60362,0 -61.11639000000001,-23.66666,0 -61.02473,-23.78612,0 -60.85499999999999,-23.8725,0 -60.61389000000001,-23.89444,0 -60.57334000000001,-23.95111,0 -60.28639,-24.0375,0 -60.03806,-24.00972000000001,0 -59.47278000000001,-24.35306,0 -59.33889000000001,-24.48889,0 -59.06778,-24.60695000000001,0 -58.80334000000001,-24.78417,0 -58.45056,-24.85695000000001,0 -58.33833000000001,-24.99778,0 -58.22139,-24.93916,0 -57.98083000000001,-25.07805000000001,0 -57.87112000000001,-25.08778,0 -57.7561,-25.17694,0 -57.64029000000001,-25.37917,0 -57.55333000000001,-25.44834,0 -57.57750999999999,-25.57889000000001,0 -57.75167,-25.67056,0 -57.77167,-25.7075,0 -57.74196,-25.72806,0 -57.81098,-25.77314,0 -57.80361000000001,-25.83473,0 -57.87445,-25.87861,0 -57.84973,-25.91833,0 -57.90361,-25.95833,0 -57.85583000000001,-26.00195,0 -58.07805,-26.12472,0 -58.11000000000001,-26.19806000000001,0 -58.1525,-26.18861,0 -58.10389000000001,-26.23555,0 -58.17250000000001,-26.27305,0 -58.16251,-26.31973,0 -58.21139,-26.42640000000001,0 -58.18306000000001,-26.4614,0 -58.21917,-26.535,0 -58.16889000000001,-26.60279,0 -58.18167,-26.65611,0 -58.23973,-26.64973,0 -58.24639,-26.765,0 -58.28584,-26.77195,0 -58.28389,-26.81139,0 -58.34584,-26.81722,0 -58.32167,-26.88223,0 -58.47834,-26.93916000000001,0 -58.51028,-27.05861,0 -58.54639000000001,-27.04611,0 -58.56417,-27.1175,0 -58.65278,-27.15834,0 -58.65573,-27.19161,0 -58.59888999999999,-27.245,0 -58.60461000000001,-27.31692,0 -58.22,-27.26555,0 -57.79139,-27.29223,0 -57.6264,-27.36916,0 -57.12944000000001,-27.48973000000001,0 -57.07167000000001,-27.49195,0 -56.90862000000001,-27.41778,0 -56.73695,-27.50694,0 -56.67305,-27.46140000000001,0 -56.55,-27.4525,0 -56.37806,-27.58834,0 -56.27084,-27.46140000000001,0 -56.26805000000001,-27.3875,0 -56.13778,-27.30889,0 -55.89833000000001,-27.32918,0 -55.84251,-27.40778,0 -55.73612000000001,-27.43944,0 -55.67557,-27.36862,0 -55.59639000000001,-27.33806,0 -55.56751000000001,-27.24944,0 -55.60196,-27.16834,0 -55.55667,-27.16166,0 -55.53973,-27.105,0 -55.455,-27.09528,0 -55.41389,-26.98278000000001,0 -55.27111,-26.9375,0 -55.12889,-26.94917,0 -55.12889,-26.86805,0 -55.05112,-26.80028,0 -54.95722,-26.77834,0 -54.92389,-26.68056,0 -54.80612,-26.66418,0 -54.77862,-26.61083,0 -54.79362,-26.52305,0 -54.69278000000001,-26.42834,0 -54.6675,-26.23888,0 -54.63333999999999,-26.20028,0 -54.66166000000001,-26.15834,0 -54.64083,-26.07139,0 -54.665,-25.9825,0 -54.60139,-25.95278,0 -54.61723,-25.89556,0 -54.58584000000001,-25.81944,0 -54.64556000000001,-25.66250000000001,0 -54.58501,-25.64139000000001,0 -54.59892,-25.57323,0 -54.61696,-25.43806,0 -54.42305,-25.14306,0 -54.45639,-25.08723,0 -54.45250999999999,-25.01111,0 -54.33055,-24.67945,0 -54.33139000000001,-24.49278,0 -54.24889,-24.35362,0 -54.33778000000001,-24.14389,0 -54.2439,-24.05361,0 -54.08028000000001,-23.94806,0 -53.96889000000001,-23.44695000000002,0 -53.72139000000001,-23.30028,0 -53.57218000000002,-22.88526,0 -53.15685,-22.70582,0 -52.97528000000001,-22.48223,0 -52.49723,-22.21611,0 -52.36556,-22.10028000000001,0 -52.29028,-21.96084,0 -52.17445,-21.85778,0 -52.15028,-21.78334,0 -52.05334,-21.71889,0 -52.03723,-21.64639,0 -52.09195,-21.55584,0 -52.04973,-21.50278000000001,0 -51.9625,-21.49778,0 -51.87056000000001,-21.35029,0 -51.84973,-21.26973,0 -51.87111000000001,-21.14917,0 -51.79223,-21.09139,0 -51.63001,-20.88028,0 -51.58084000000001,-20.60028,0 -51.46612,-20.53917,0 -51.26055000000001,-20.31528,0 -51.04611,-20.25028,0 -51.00643,-20.07929,0 -50.98584000000001,-19.9075,0 -51.02361000000001,-19.7225,0 -50.98112,-19.57166,0 -50.9325,-19.55667,0 -50.96278000000001,-19.475,0 -50.92341,-19.45669,0 -51.11418000000001,-19.29083,0 -51.3064,-19.25889,0 -51.42306,-19.15806,0 -51.63223,-19.13334,0 -51.84166,-19.05361,0 -51.89694,-18.995,0 -52.06557,-18.94416,0 -52.18861,-18.83695,0 -52.34917,-18.81667,0 -52.47278,-18.70834,0 -52.63667,-18.72305,0 -52.89001,-18.67805,0 -52.8675,-18.57695,0 -52.75805,-18.4125,0 -52.895,-18.34694,0 -52.98834000000001,-18.38889,0 -53.02973,-18.37194,0 -53.05806,-18.28305,0 -53.05389000000001,-18.01667,0 -53.1139,-17.8889,0 -53.12612000000001,-17.67167,0 -53.22778000000001,-17.49805,0 -53.19084,-17.36723,0 -53.20166,-17.28473,0 -53.03973,-17.0525,0 -53.0275,-16.93778,0 -52.97334,-16.82111,0 -52.83222,-16.76417,0 -52.72084,-16.66471,0 -52.69139,-16.57639,0 -52.63779000000001,-16.53889,0 -52.57751,-16.34166,0 -52.43473,-16.27139,0 -52.44056000000001,-16.09694,0 -52.33055,-16.0425,0 -52.24306000000001,-15.88028,0 -52.03751,-15.87555,0 -51.96584,-15.80111,0 -51.90389,-15.8225,0 -51.87695,-15.80055,0 -51.77973000000001,-15.63084,0 -51.78861,-15.53417,0 -51.74973,-15.54084,0 -51.70361,-15.48194,0 -51.63667000000001,-15.17556,0 -51.41251000000001,-14.99723,0 -51.31833000000001,-14.9625,0 -51.29611,-15.01473,0 -51.25723,-15.02139,0 -51.16028,-14.975,0 -51.0975,-14.89111,0 -51.03833,-14.65973,0 -50.96778000000001,-14.53083,0 -50.99918000000001,-14.40555,0 -50.92694,-14.11972,0 -50.84555,-14.08639,0 -50.87056,-13.71861,0 -50.78973000000001,-13.66806,0 -50.75834,-13.53778,0 -50.59166,-13.29694,0 -50.56834,-13.22861,0 -50.59028000000001,-13.07723,0 -50.49806,-12.96056,0 -50.48167000000001,-12.8475,0 -50.4841,-12.84488,0 -50.59806,-12.81056,0 -50.62555,-12.65639,0 -50.67362000000001,-12.62805,0 -50.61361,-12.38416,0 -50.61972,-12.28444,0 -50.67334000000001,-12.20223,0 -50.68389,-12.04861,0 -50.64251,-11.88555,0 -50.67973000000001,-11.87,0 -50.71448,-11.74943,0 -50.64834,-11.60334,0 -50.73667,-11.52445,0 -50.74139,-11.46444,0 -50.65917000000001,-11.24305,0 -50.66473000000001,-11.1425,0 -50.60946,-11.06528,0 -50.63778,-10.9325,0 -50.61167,-10.88139,0 -50.62944,-10.82611,0 -50.5825,-10.73945,0 -50.60667000000001,-10.65584,0 -50.52278,-10.56334,0 -50.48278,-10.39361,0 -50.40862,-10.3075,0 -50.38167,-10.11029,0 -50.30944,-10.02889,0 -50.23251000000001,-9.844165,0 -51.1372,-9.797392,0 -51.07751,-9.31194,0 -51.12167,-9.258607,0 -51.1725,-8.998056,0 -51.33805,-8.772786,0 -51.44778000000001,-8.691948999999999,0 -51.52667000000001,-8.544447999999999,0 -51.51973,-8.418888000000001,0 -51.42722,-8.269164,0 -51.44501,-8.193051000000001,0 -51.40862000000001,-8.134731,0 -51.43333000000001,-8.088058,0 -51.41695,-8.028333999999999,0 -51.26918,-7.983613,0 -51.16889,-7.914447,0 -51.10196000000001,-7.803054,0 -51.23056,-7.623612,0 -51.18334,-7.543612,0 -51.21639,-7.419159,0 -51.20167000000001,-7.342497,0 -51.27251,-7.274448000000001,0 -51.38807,-7.269723000000002,0 -51.48695,-7.19416,0 -51.64500000000001,-7.158888000000001,0 -51.65556000000002,-7.06222,0 -51.74084,-6.956110000000001,0 -51.69805,-6.901659,0 -51.72723,-6.893613,0 -51.71278000000002,-6.860278,0 -51.78751,-6.690835,0 -51.9575,-6.613336,0 -52.01314,-6.657526,0 -52.08528,-6.548607000000001,0 -52.22473,-6.573888,0 -52.36139000000001,-6.381109,0 -52.43055,-6.338053000000001,0 -52.43056000000001,-6.210279,0 -52.53807,-6.036390000000001,0 -52.53889,-5.840838000000001,0 -52.645,-5.688891,0 -52.70612,-5.469165,0 -52.84084,-5.406949,0 -52.86778000000001,-5.365558,0 -52.86972,-5.210001,0 -52.92639,-5.008608,0 -52.83056,-4.882780000000001,0 -52.66223,-4.493052,0 -52.66278000000001,-4.433338,0 -52.73,-4.376107,0 -52.73806,-4.331107,0 -52.63945,-4.193614000000001,0 -52.60722000000001,-3.860839,0 -52.35639000000001,-3.665836,0 -52.23778,-3.510559,0 -52.15778,-3.34305,0 -52.15028,-3.235554,0 -52.04611000000001,-3.294441,0 -51.94445,-3.409444,0 -51.92667,-3.553605999999999,0 -51.83223000000001,-3.609163,0 -51.74917,-3.562498000000001,0 -51.66917,-3.471661,0 -51.74472000000001,-3.35611,0 -51.63556000000001,-3.291661,0 -51.61153,-3.147264,0 -51.82334,-3.100285,0 -51.94334000000001,-2.898334000000001,0 -52.04999999999999,-2.809999,0 -52.06667000000001,-2.714715,0 -52.03139,-2.59722,0 -52.12361000000001,-2.389717000000001,0 -52.20028,-2.407501,0 -52.15028,-2.358055,0 -52.25917,-2.161108,0 -52.26723,-2.061666,0 -52.36806000000001,-2.098612,0 -52.27139,-2.01916,0 -52.31806000000001,-1.935559,0 -52.26917,-1.813051,0 -52.34612,-1.674721,0 -52.31139000000001,-1.627498,0 -52.26806,-1.682218,0 -52.2275,-1.616392,0 -52.28528,-1.53361,0 -52.44972,-1.586944,0 -52.54084,-1.571949,0 -52.71278,-1.603333,0 -52.70722,-1.561105,0 -52.47639,-1.490553,0 -52.43805,-1.441108,0 -52.22139,-1.342504,0 -52.10778,-1.415835,0 -52.00557,-1.404163,0 -51.92334,-1.328608,0 -51.93501,-1.192501,0 -51.90139,-1.150552,0 -51.9775,-1.135279,0 -52.0475,-1.170838,0 -52.06945,-1.220284,0 -52.11389000000001,-1.155286,0 -52.27112000000001,-1.133893,0 -52.37028000000001,-1.061388,0 -52.38528000000002,-0.8791655,0 -52.51918000000001,-0.8797145,0 -52.50666,-0.7508345000000001,0 -52.5066,-0.7438775,0 -52.53807,-0.6247175,0 -52.60056,-0.6144485,0 -52.62723,-0.5727785,0 -52.63278,-0.3822215000000001,0 -52.81223000000001,-0.1780565,0 -52.87194,-0.2033375,0 -52.92334,-0.1863905,0 -52.97112,-0.01638954,0 -53.01583,0.03500046,0 -53.0061,0.1311115,0 -53.04139000000001,0.2474995,0 -53.12889,0.3866664,0 -53.11723000000001,0.7422205,0 -53.28250000000001,0.7902805000000003,0 -53.41861999999999,0.9422185000000001,0 -53.46723,1.142496,0 -53.40584000000001,1.187504,0 -53.4325,1.25666,0 -53.54889,1.243889,0 -53.55306,1.367783,0 -53.65639000000001,1.363058,0 -53.66417,1.429442,0 -53.69694000000001,1.410281,0 -53.73695,1.437786,0 -53.75307000000002,1.392497,0 -53.82167,1.419722,0 -53.82723,1.384163,0 -53.85778,1.383605,0 -53.98807000000001,1.520558,0 -54.08278000000001,1.505556,0 -54.10139,1.613051,0 -54.36111,1.763054,0 -54.735,1.768607,0 -54.76307,1.986947,0 -54.80084,2.017781,0 -54.75029,2.076659,0 -54.78945,2.163609,0 -54.71111,2.274723,0 -54.73722000000001,2.425833,0 -54.77539,2.456846,0 -54.86750000000001,2.440278,0 -54.97223,2.554721,0 -54.94834000000001,2.617496,0 -55.11195,2.527217,0 -55.13807000000001,2.573891000000001,0 -55.24139000000001,2.499164,0 -55.32249,2.516948,0 -55.38,2.428056,0 -55.58473,2.438334,0 -55.71028,2.399165,0 -55.96584,2.53277,0 -55.99195000000001,2.4075,0 -56.04528000000001,2.344445,0 -56.08917,2.355281,0 -56.13807,2.266947,0 -56.0425,2.232782,0 -56.03639,2.177505,0 -55.90139000000001,2.045285,0 -55.90417,1.89305,0 -55.94667,1.853333,0 -56.03723,1.836944,0 -56.47063,1.944503,0 -56.56279,2.023892,0 -56.69187,2.028734,0 -56.81057,2.193336,0 -56.8064,2.250836,0 -56.87833000000001,2.297781,0 -56.87112,2.348891,0 -56.92806,2.391948,0 -56.91778,2.428056,0 -57.09806,2.749166,0 -57.20139000000001,2.821391000000001,0 -57.21333,2.878892000000001,0 -57.17917,2.914443,0 -57.21111,2.928051,0 -57.24111000000001,3.138057,0 -57.28528,3.143888000000001,0 -57.30139,3.377501000000001,0 -57.35028,3.355001,0 -57.41057000000001,3.382496,0 -57.48083000000001,3.323888999999999,0 -57.50334,3.351393,0 -57.64223000000001,3.356387,0 -57.66693999999999,3.395555,0 -57.63721,3.455,0 -57.65445,3.518612,0 -57.73473,3.610277,0 -57.83583,3.664998000000001,0 -57.87668,3.80916,0 -58.04723,4.008338,0 -58.0714,4.159161,0 -57.9539,4.28777,0 -57.95889,4.403339,0 -57.83862,4.654718,0 -57.92333000000001,4.821948,0 -57.84112,4.927778,0 -57.77140000000001,4.922774,0 -57.68806000000001,5.010282,0 -57.42028,4.989168,0 -57.28889,5.016663,0 -57.32305,5.069717,0 -57.29695,5.160285000000001,0 -57.25471,5.175279,0 -57.22889,5.140835,0 -57.18862,5.16861,0 -57.23521000000001,5.259942,0 -57.26307,5.26778,0 -57.25639000000001,5.22278,0 -57.27916,5.223339,0 -57.28723000000002,5.310557,0 -57.33555,5.313338,0 -57.27223000000001,5.38028,0 -57.24850000000001,5.486112,0 -57.16833,5.537222,0 -57.13139,5.773337,0 -57.06833000000001,5.942223,0 -56.97724,6.000021,0 -55.15563000000001,6.048935,0 -54.47205,5.976981000000002,0 -52.91902,5.485283,0 -52.50826000000001,5.098526,0 -52.50826000000001,5.239709,0 -52.5083,10.2397,0 -52.50835,15.23971,0 -52.50838,19.83971,0 -52.50841,24.83971,0 -52.50846,29.83971,0 -52.50845000000001,29.94478,0 -52.50809000000001,34.94477,0 -52.50773000000001,39.94478,0 -52.50738,44.94477,0 -52.50703,49.94478,0 -52.50666,54.94477,0 -52.50631,59.94478000000001,0 -52.50595000000001,64.94479,0 -53.01731,65.35898,0 -53.71276,66.05441999999999,0 -54.00934,67.10271,0 -53.82526,67.68565,0 -55.02182000000001,69.63903000000001,0 -55.07296,70.48277,0 -55.18546,71.07594,0 -55.98318,71.67934000000001,0 -56.16726000000001,72.58955,0 -57.85474000000001,74.69123,0 -59.57289,75.70371,0 -62.34444,76.01053,0 -63.58193000000001,76.31222,0 -63.60658000000001,76.32865,0 -67.76724,79.10155,0 -67.31153999999999,79.16665000000002,0 -65.98242999999999,79.14412,0 -64.97996999999999,79.47077,0 -65.14892000000002,79.84247000000001,0 -65.29535,80.06775,0 -65.64452,80.00015999999999,0 -66.0838,80.00015999999999,0 -66.66951,80.05649,0 -67.10879,80.04520000000001,0 -67.52555,80.18038,0 -67.54808,80.29301,0 -67.49175,80.34933,0 -66.92856999999999,80.47324,0 -66.92856999999999,80.65346,0 -66.78216000000001,80.72104,0 -66.44423999999999,80.6084,0 -64.91238,80.92377000000001,0 -64.79975,81.00261999999999,0 -63.96624000000001,81.07021000000002,0 -63.81981,81.14906000000001,0 -62.97503,81.25042999999999,0 -62.23162000000001,81.21664,0 -61.55581000000002,81.11526000000001,0 -61.30801,81.16032,0 -61.35306000000001,81.34054,0 -61.31927,81.39686,0 -61.02642,81.47570000000002,0 -61.49949,81.73477000000001,0 -61.47697000000001,81.79107999999999,0 -60.51955,81.93752000000001,0 -59.49456,82.01636000000002,0 -58.87425,82.10625,0 -56.03332,82.31142000000001,0 -54.21828,82.37454,0 -53.42914,82.29564000000001,0 -52.95565,82.35877000000001,0 -52.52951,82.29564000000001,0 -52.52415,84.90001,0 -52.51387,89.90000000000001,0 -51.51426,89.90000000000001,0 -50.51467,89.90000000000001,0 -49.51508,89.90000000000001,0 -48.51547000000001,89.90000000000001,0 -47.51588000000001,89.90000000000001,0 -46.51628000000002,89.90000000000001,0 -45.51669,89.90000000000001,0 -44.51709,89.90000000000001,0 -43.51749,89.90000000000001,0 -42.5179,89.90000000000001,0 -41.51830000000001,89.90000000000001,0 -40.51871,89.90000000000001,0 -39.51911,89.90000000000001,0 -38.5195,89.90000000000001,0 -37.51992,89.90000000000001,0 -37.51992,84.90001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<innerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-19.77925,75.15038,0 -19.60578,75.14566000000001,0 -19.46688,75.2666,0 -19.43014,75.36224,0 -19.4368,75.46748000000001,0 -19.48732,75.56003,0 -19.57222,75.62255,0 -19.67559,75.64335,0 -19.77806,75.6185,0 -19.86044,75.55269,0 -19.90594,75.4571,0 -19.90611,75.27975000000001,0 -19.77925,75.15038,0 </coordinates>
    +					</LinearRing>
    +					<LinearRing>
    +						<coordinates>
    +-22.03397,75.85133,0 -21.86051,75.84662,0 -21.72161,75.96755,0 -21.68487,76.06319000000001,0 -21.69154,76.16843000000002,0 -21.74206,76.26098,0 -21.82695,76.32351,0 -21.93032,76.3443,0 -22.03279,76.31946000000001,0 -22.11516,76.25364,0 -22.16067,76.15807000000001,0 -22.16084,75.98069,0 -22.03397,75.85133,0 </coordinates>
    +					</LinearRing>
    +				</innerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>			
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -02:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-2);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0200</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-25.28129,67.4954,0 -25.28129,62.86423,0 -22.54213,62.86423,0 -22.5419,60.19076,0 -22.54149,55.19075000000002,0 -22.54108,50.19075,0 -22.54066,45.19075,0 -22.54025,40.19075,0 -26.74697,40.19075,0 -31.74697000000001,40.19075,0 -31.74697000000001,36.49937,0 -27.5395,36.49937,0 -22.5395,36.49937,0 -22.53887,33.39533,0 -22.53785,28.39532,0 -22.53685,23.39533000000001,0 -22.53583,18.39532,0 -27.51274,18.39532,0 -27.51274,13.4011,0 -22.53482,13.4011,0 -22.53417,10.20111,0 -22.53316,5.201108,0 -22.53215,0.2011045,0 -22.53113000000001,-4.798891,0 -22.53011,-9.798895,0 -22.52911,-14.79889,0 -22.52809,-19.79889,0 -22.52777,-21.39890000000001,0 -22.52676,-26.39889000000001,0 -22.52574,-31.3989,0 -22.52472,-36.39889,0 -22.52435,-38.2989,0 -22.52333,-43.29889000000001,0 -22.52232,-48.29890000000001,0 -22.5213,-53.2989,0 -22.52029,-58.2989,0 -22.51928,-63.2989,0 -22.51827,-68.2989,0 -22.51725,-73.2989,0 -22.51667,-76.19889000000001,0 -22.51565,-81.19889000000001,0 -22.51464,-86.19888,0 -22.51389,-89.90000000000001,0 -23.45178,-89.90000000000001,0 -24.38965,-89.90000000000001,0 -25.32752,-89.90000000000001,0 -26.2654,-89.90000000000001,0 -27.20327,-89.90000000000001,0 -28.14115,-89.90000000000001,0 -29.07903,-89.90000000000001,0 -30.0169,-89.90000000000001,0 -30.95478,-89.90000000000001,0 -31.89265,-89.90000000000001,0 -32.83052,-89.90000000000001,0 -33.76841,-89.90000000000001,0 -34.70629,-89.90000000000001,0 -35.64416,-89.90000000000001,0 -36.58204,-89.90000000000001,0 -37.51991,-89.90000000000001,0 -37.51989,-84.89999,0 -37.51988,-79.90000000000001,0 -37.51987,-74.90000000000001,0 -37.51987,-69.90000000000001,0 -37.51985000000001,-64.90000000000001,0 -37.51984,-59.90001,0 -37.51983,-54.93388,0 -42.51982,-54.93388,0 -45.01502000000001,-54.93388,0 -45.01502000000001,-52.08813,0 -40.01502,-52.08813,0 -37.51982,-52.08813,0 -37.5198,-47.08813000000001,0 -37.51979,-42.17389,0 -37.51979,-37.17389,0 -37.51978,-32.17389,0 -37.51977,-27.17389,0 -37.51975,-22.17389,0 -37.51974,-17.17389,0 -37.51972,-12.17389,0 -37.51972,-11.77389,0 -37.36223,-11.45472,0 -37.39806,-11.49972,0 -37.48791,-11.52023,0 -37.39305,-11.45,0 -37.46917,-11.36667,0 -37.41223,-11.40278,0 -37.39528,-11.33389,0 -37.33139,-11.28667,0 -37.34861,-11.25028,0 -37.31362,-11.26305,0 -37.34779,-11.18778,0 -37.30555,-11.245,0 -37.29862,-11.20195,0 -37.27584,-11.25973,0 -37.36362,-11.31556,0 -37.39834,-11.40223,0 -37.33000000000001,-11.42723,0 -37.26501,-11.28667,0 -37.15112,-11.19194,0 -37.16446,-11.11389,0 -37.22112,-11.10778,0 -37.20195,-11.0925,0 -37.26028,-11.07222,0 -37.27916,-11.02528,0 -37.20723,-11.03723,0 -37.15667,-11.10444,0 -37.05333,-10.94917,0 -37.04028000000001,-10.8375,0 -37.14168,-10.82528,0 -37.09417000000001,-10.80111,0 -37.15333,-10.74861,0 -37.07779,-10.73389,0 -37.06889000000001,-10.79445,0 -37.00862,-10.81056,0 -37.02166,-10.93584,0 -36.85861,-10.73916,0 -36.56751,-10.54445,0 -36.40483,-10.49879,0 -36.28445,-10.3525,0 -36.28139,-10.28695,0 -36.04444000000001,-10.07583,0 -35.79557,-9.728334,0 -35.86917,-9.764163999999999,0 -35.95196,-9.602784,0 -35.90528,-9.621667,0 -35.86,-9.739160999999999,0 -35.79222,-9.683056000000001,0 -35.79362,-9.589725,0 -35.73695,-9.651394000000002,0 -35.77306,-9.695277000000001,0 -35.69028,-9.668889999999999,0 -35.65751,-9.586387,0 -35.5825,-9.535833,0 -35.45667,-9.346393000000001,0 -35.32751,-9.228889000000001,0 -35.16612,-8.960283,0 -34.97001,-8.430552000000001,0 -34.99195,-8.448895,0 -34.98694,-8.418888000000001,0 -35.02194,-8.423056000000001,0 -34.93584,-8.350552,0 -34.94,-8.270550999999999,0 -34.88001,-8.113049999999999,0 -34.92668,-8.038333,0 -34.85612,-8.073612000000001,0 -34.81528,-7.935283000000001,0 -34.84472,-7.835554000000001,0 -34.89333,-7.804998000000001,0 -34.85834,-7.73028,0 -34.90696,-7.735834,0 -34.85167,-7.695559000000002,0 -34.87555,-7.622227000000001,0 -34.83084,-7.683337,0 -34.80083,-7.63111,0 -34.84056,-7.554448,0 -34.92334,-7.563889000000001,0 -34.8932,-7.543089,0 -34.81779,-7.546942,0 -34.8075,-7.515000000000001,0 -34.7925,-7.178607,0 -34.83084,-7.108605,0 -34.82556,-6.987502,0 -34.85695,-7.075837,0 -34.915,-7.127497,0 -34.85834,-6.919444,0 -34.91195,-6.879997000000001,0 -34.94334,-6.779997000000001,0 -34.98028,-6.40639,0 -35.01501,-6.374998000000001,0 -35.04139,-6.233887000000001,0 -35.08778,-6.199164,0 -35.13584,-6.228612,0 -35.15501,-6.208894,0 -35.11528,-6.165838,0 -35.09278,-6.178887000000001,0 -35.105,-5.981112000000001,0 -35.14557,-5.938893000000001,0 -35.26195,-5.484726,0 -35.4175,-5.214717000000001,0 -35.60445000000001,-5.110839,0 -35.93472,-5.052223,0 -36.12889,-5.097502,0 -36.28751,-5.091660000000001,0 -36.25472,-5.10166,0 -36.28028,-5.11111,0 -36.58167,-5.088051,0 -36.52583,-5.108608,0 -36.54417,-5.134447,0 -36.68722,-5.098339,0 -36.79723,-5.046391,0 -36.88278,-4.949163,0 -37.17445,-4.918609,0 -37.28973,-4.726108,0 -37.51991,-4.633912,0 -37.51991,0.3660925,0 -37.51991,5.366087,0 -37.51991,10.36609,0 -37.51991,15.36609,0 -37.51991,15.36843,0 -37.51991,20.36843,0 -37.51991,25.36843,0 -37.51991,29.96842,0 -37.51991,34.96843,0 -37.51991,39.96843,0 -37.51991,44.96843000000001,0 -37.51991,49.96843,0 -37.51991,54.96843,0 -37.51991,59.96843000000001,0 -37.51991,64.96842,0 -37.51991,65.16843,0 -35.45789,65.8785,0 -33.65758,66.67344,0 -31.90405,67.86584000000001,0 -29.7998,67.98274000000001,0 -27.43837,68.38021000000001,0 -25.89525,68.70753000000001,0 -23.90792,69.38557000000002,0 -22.51389,69.82120999999999,0 -22.51389,67.4954,0 -25.28129,67.4954,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-22.55908,84.90009000000001,0 -22.56975,83.06453000000002,0 -23.25304,83.14206,0 -28.22116000000001,83.70583000000001,0 -29.51426,83.7223,0 -34.51386,83.78599,0 -37.51992,83.74590000000001,0 -37.51992,84.90001,0 -37.51992,89.90000000000001,0 -36.5206,89.90000000000001,0 -35.52126,89.90000000000001,0 -34.52194,89.90000000000001,0 -33.52261,89.90000000000001,0 -32.52328,89.90000000000001,0 -31.52396,89.90000000000001,0 -30.52463,89.90000000000001,0 -29.5253,89.90000000000001,0 -28.52597,89.90000000000001,0 -27.52664,89.90000000000001,0 -26.52732,89.90000000000001,0 -25.52799,89.90000000000001,0 -24.52866,89.90000000000001,0 -23.52934,89.90000000000001,0 -22.53001,89.90000000000001,0 -22.55908,84.90009000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT -01:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(-1);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStylem0100</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-8.807823000000001,54.70094,0 -9.054081,54.29253,0 -9.834866999999999,54.36460000000001,0 -10.12917,54.26851,0 -10.25529,54.12435000000001,0 -10.29734,53.98021000000001,0 -10.32736,53.58982,0 -10.18322,53.39763,0 -9.8649,53.12735000000001,0 -9.498528000000002,52.94116,0 -9.68472,52.71294,0 -9.948978,52.56879000000001,0 -10.65169,52.04025,0 -10.42346,51.86608,0 -10.24928,51.55978,0 -9.828864000000001,51.43965,0 -9.50454,51.40962,0 -8.525547,51.58981000000001,0 -7.812513,51.89045000000001,0 -7.812513,46.89045,0 -7.812513,43.71458,0 -7.894449000000001,43.66638,0 -7.850556,43.71472,0 -7.856109000000001,43.765,0 -7.898058000000001,43.76416,0 -7.991946000000001,43.70555,0 -8.060841,43.70361,0 -8.049447000000001,43.63666,0 -8.089173000000001,43.66194,0 -8.253333,43.55806,0 -8.319447,43.56221,0 -8.295282,43.52639,0 -8.331660000000001,43.50889,0 -8.339724,43.45693,0 -8.166942000000001,43.50777,0 -8.194445999999999,43.46611,0 -8.317214999999999,43.44305,0 -8.13195,43.42334,0 -8.213058,43.4025,0 -8.215551,43.33055,0 -8.329725,43.40388,0 -8.360001000000001,43.34027,0 -8.38611,43.33944,0 -8.398611000000001,43.38582,0 -8.539443000000002,43.30972,0 -8.701389000000001,43.28722,0 -8.782785000000001,43.30306,0 -8.843994,43.36485,0 -8.848053,43.32222,0 -8.980281,43.28139,0 -8.91639,43.23222,0 -9.010557,43.23195,0 -9.056664,43.18332,0 -9.169731000000001,43.18583,0 -9.209448,43.15277,0 -9.203894999999999,43.12554,0 -9.164448,43.13777,0 -9.165555,43.10722,0 -9.218889000000001,43.10806,0 -9.25389,43.05029,0 -9.2925,43.04804000000001,0 -9.252504000000002,43.0061,0 -9.293336999999999,42.92249,0 -9.273339,42.88694,0 -9.226944,42.93805,0 -9.181107000000001,42.91555,0 -9.185003999999999,42.9525,0 -9.123606000000002,42.90555,0 -9.14139,42.85695,0 -9.101394000000001,42.82332,0 -9.140283,42.795,0 -9.086112,42.73528,0 -9.058887,42.78472,0 -8.960283,42.77333,0 -8.903610000000001,42.82833,0 -8.85834,42.82444,0 -9.020286000000001,42.70472,0 -9.086112,42.58138000000001,0 -9.027782999999999,42.57972,0 -9.041391000000001,42.52888,0 -8.967249000000001,42.46196,0 -8.898606000000001,42.10806,0 -8.882225999999999,41.89004,0 -10.07815,41.89004,0 -15.07814,41.89004,0 -16.53966,38.78279,0 -18.66778,34.25829,0 -20.79591,29.7338,0 -20.79591,25.84186,0 -19.57629,25.84186,0 -14.57629,25.84186,0 -14.82666,25.30916,0 -14.83473,24.91528,0 -14.90111,24.68805,0 -14.98694,24.63639,0 -15.03139,24.54167,0 -15.18028,24.48805000000001,0 -15.29639,24.3336,0 -15.61417,24.02778,0 -15.7625,23.95694,0 -15.91167,23.82195,0 -16.00167,23.64389,0 -15.94334,23.68222,0 -15.86972,23.82861000000001,0 -15.79028,23.85806,0 -15.78167,23.91,0 -15.76278,23.79138,0 -15.92583,23.57722,0 -16.13584,23.17917,0 -16.21612,23.08388,0 -16.15167,23.06888,0 -16.15416,23.01639,0 -16.18973,22.94221,0 -16.30361,22.87555,0 -16.35917,22.56806,0 -16.42889,22.51611000000001,0 -16.50028,22.32221,0 -16.62806,22.27361,0 -16.66889,22.29639,0 -16.71944,22.26082,0 -16.85334,22.07139,0 -16.95945,21.82222,0 -16.94416,21.73972,0 -17.10528,20.84806,0 -17.05233,20.76409,0 -16.53566,19.38283,0 -16.46306,19.2554,0 -16.35111,19.20556,0 -16.28139,19.12777000000001,0 -16.18806,18.93945,0 -16.13805,18.65389,0 -16.05778,18.43472,0 -16.02862,18.08056,0 -16.06945,17.58972,0 -16.17556,17.25416,0 -16.46583,16.61971,0 -16.47278,16.48222,0 -16.53723,16.30166,0 -16.52768,16.06025,0 -16.53723,15.77584,0 -16.87834,15.225,0 -17.16694,14.90305,0 -17.53723,14.75611,0 -17.45626,14.64857,0 -17.2475,14.67323,0 -16.82851,13.87214,0 -16.83957,12.41371,0 -16.36448,11.88337,0 -16.5081,11.28123,0 -16.17665,11.01605,0 -15.12702,10.87243,0 -13.84538,9.425052000000001,0 -13.34819,8.972054,0 -13.27084,8.088164000000001,0 -13.02778,7.524683000000001,0 -12.39801,7.325810000000001,0 -11.38153,6.684983,0 -9.348579000000001,5.116076,0 -8.442594,4.596795,0 -7.506171,4.24899,0 -7.506171,4.17239,0 -7.506171,-0.8276135000000001,0 -7.506171,-5.827609,0 -11.18769,-5.827609,0 -16.18769,-5.827609,0 -16.1435,-9.363187,0 -12.50618,-9.363187,0 -7.506171,-9.363187,0 -7.506612000000001,-10.65897,0 -7.508295000000001,-15.65897,0 -7.509978000000001,-20.65897,0 -7.51167,-25.65897,0 -7.513353,-30.65898,0 -7.515036,-35.65897,0 -9.640872,-35.65897,0 -14.64088,-35.65897,0 -14.64088,-36.71363,0 -14.64088,-41.71362,0 -12.51732,-41.71362,0 -7.517322,-41.71362,0 -7.518411000000001,-44.61363,0 -7.520301000000001,-49.61362,0 -7.522191000000001,-54.61362,0 -7.524081000000001,-59.61362,0 -7.525971,-64.61363,0 -7.527861000000001,-69.61362,0 -7.529751,-74.61362,0 -7.531632,-79.61362,0 -7.531740000000001,-79.90000000000001,0 -7.533630000000001,-84.89999,0 -7.53552,-89.90000000000001,0 -8.53407,-89.90000000000001,0 -9.532638,-89.90000000000001,0 -10.53119,-89.90000000000001,0 -11.52976,-89.90000000000001,0 -12.52831,-89.90000000000001,0 -13.52687,-89.90000000000001,0 -14.52542,-89.90000000000001,0 -15.52398,-89.90000000000001,0 -16.52254,-89.90000000000001,0 -17.5211,-89.90000000000001,0 -18.51966,-89.90000000000001,0 -19.51822,-89.90000000000001,0 -20.51678,-89.90000000000001,0 -21.51534,-89.90000000000001,0 -22.51389,-89.90000000000001,0 -22.51464,-86.19888,0 -22.51565,-81.19889000000001,0 -22.51667,-76.19889000000001,0 -22.51725,-73.2989,0 -22.51827,-68.2989,0 -22.51928,-63.2989,0 -22.52029,-58.2989,0 -22.5213,-53.2989,0 -22.52232,-48.29890000000001,0 -22.52333,-43.29889000000001,0 -22.52435,-38.2989,0 -22.52472,-36.39889,0 -22.52574,-31.3989,0 -22.52676,-26.39889000000001,0 -22.52777,-21.39890000000001,0 -22.52809,-19.79889,0 -22.52911,-14.79889,0 -22.53011,-9.798895,0 -22.53113000000001,-4.798891,0 -22.53215,0.2011045,0 -22.53316,5.201108,0 -22.53417,10.20111,0 -22.53482,13.4011,0 -27.51274,13.4011,0 -27.51274,18.39532,0 -22.53583,18.39532,0 -22.53685,23.39533000000001,0 -22.53785,28.39532,0 -22.53887,33.39533,0 -22.5395,36.49937,0 -27.5395,36.49937,0 -31.74697000000001,36.49937,0 -31.74697000000001,40.19075,0 -26.74697,40.19075,0 -22.54025,40.19075,0 -22.54066,45.19075,0 -22.54108,50.19075,0 -22.54149,55.19075000000002,0 -22.5419,60.19076,0 -22.54213,62.86423,0 -17.54214,62.85332,0 -12.54215,62.84239,0 -7.806969000000001,62.83205,0 -7.806969000000001,57.83205,0 -7.806969000000001,55.35279,0 -8.261279999999999,55.27752,0 -8.567595000000001,55.00724,0 -8.807823000000001,54.70094,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-19.46688,75.2666,0 -19.60578,75.14566000000001,0 -19.77925,75.15038,0 -19.90611,75.27975000000001,0 -19.90594,75.4571,0 -19.86044,75.55269,0 -19.77806,75.6185,0 -19.67559,75.64335,0 -19.57222,75.62255,0 -19.48732,75.56003,0 -19.4368,75.46748000000001,0 -19.43014,75.36224,0 -19.46688,75.2666,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-7.511742000000002,84.90001,0 -7.511742000000002,79.90000000000001,0 -7.511742000000002,74.89999,0 -7.511742000000002,69.90000000000001,0 -7.511742000000002,67.45737,0 -12.51173,67.47004,0 -17.51171,67.48272,0 -22.5117,67.49539,0 -22.51389,67.4954,0 -22.51389,69.82120999999999,0 -22.03747,69.97009,0 -21.35944,70.48445,0 -21.17239,70.78841,0 -21.78029,72.14449000000001,0 -21.42958000000001,72.84589,0 -20.28394,73.40702,0 -19.34872,74.17858,0 -18.13293,74.58775,0 -17.26786,74.98521,0 -17.24447,75.26577,0 -18.50702,75.85029,0 -18.06279,76.85564000000001,0 -17.52503,77.76749,0 -17.47827,79.26384,0 -17.31461,79.94186999999999,0 -15.51431,80.61991,0 -14.53233,80.80695,0 -12.03061,81.55513000000001,0 -12.03061,81.69541,0 -13.71401,81.90584,0 -16.16897,82.02274,0 -17.59517,81.92921,0 -18.10955,81.78892999999999,0 -19.25519,82.23316,0 -21.33607,82.72414000000001,0 -22.56975,83.06453000000002,0 -22.55908,84.90009000000001,0 -22.53001,89.90000000000001,0 -21.59137,89.90000000000001,0 -20.65272,89.90000000000001,0 -19.71409,89.90000000000001,0 -18.77545,89.90000000000001,0 -17.8368,89.90000000000001,0 -16.89817,89.90000000000001,0 -15.95952,89.90000000000001,0 -15.02087,89.90000000000001,0 -14.08224,89.90000000000001,0 -13.14359,89.90000000000001,0 -12.20495,89.90000000000001,0 -11.26631,89.90000000000001,0 -10.32766,89.90000000000001,0 -9.389025000000002,89.90000000000001,0 -8.450379,89.90000000000001,0 -7.511742000000002,89.90000000000001,0 -7.511742000000002,84.90001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +00:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(0);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0000</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-8.201223000000002,42.15273,0 -8.185554,42.06471,0 -8.121942000000001,42.07944,0 -8.079723,42.05416,0 -8.084997000000001,42.02027,0 -8.218610999999999,41.91361000000001,0 -8.140554,41.80917000000001,0 -8.014725,41.83388,0 -7.892217,41.92666,0 -7.880004,41.85277,0 -7.692498,41.905,0 -7.591662,41.87972,0 -7.607781,41.83111000000001,0 -7.493058,41.87306000000001,0 -7.420284,41.81332,0 -7.37028,41.85139,0 -7.200558,41.88361,0 -7.182783,41.97944,0 -7.133607,41.99499,0 -7.056108,41.95111,0 -6.982848,41.97386,0 -6.837498,41.94722,0 -6.788052000000001,41.99167,0 -6.716943,41.93666,0 -6.581664,41.96749,0 -6.544719,41.93722,0 -6.568335,41.88721,0 -6.508341,41.87389,0 -6.562503,41.74529,0 -6.551388,41.6911,0 -6.497496000000001,41.65749,0 -6.442218000000002,41.68582,0 -6.303609,41.66333,0 -6.187221,41.57972,0 -6.32916,41.41527,0 -6.318333000000001,41.38722,0 -6.388056,41.38167,0 -6.381945,41.355,0 -6.496947,41.26445,0 -6.64389,41.24471000000001,0 -6.764445,41.13083,0 -6.754446000000002,41.10249,0 -6.808338,41.04056000000001,0 -6.931665000000001,41.01806,0 -6.803892,40.87416000000001,0 -6.829164,40.75528,0 -6.797781,40.65777,0 -6.838893000000001,40.575,0 -6.790833,40.51805,0 -6.848334000000001,40.44333,0 -6.778611,40.36279,0 -6.873894,40.26332,0 -7.005834,40.23083,0 -7.027218,40.18777,0 -7.004718,40.12611,0 -6.934725000000001,40.1075,0 -6.863895,40.01555,0 -6.913611,39.86472,0 -6.98319,39.81694,0 -7.017219,39.675,0 -7.266942,39.67194,0 -7.337502,39.64388000000001,0 -7.515000000000001,39.66611,0 -7.532505000000001,39.66942000000001,0 -7.505838000000001,39.60583,0 -7.390836000000001,39.53277,0 -7.38,39.49611,0 -7.293609,39.46777,0 -7.322778,39.38833,0 -7.235037000000001,39.27697,0 -7.242786000000002,39.21305,0 -7.139997000000001,39.17334,0 -7.142499,39.11111,0 -7.004997,39.10722,0 -6.954795,39.02639,0 -7.050285000000001,38.91082,0 -7.041663,38.86861,0 -7.257231,38.72834,0 -7.248051,38.6236,0 -7.329717,38.44722,0 -7.143894,38.27027,0 -7.087779,38.17443,0 -6.947784,38.21833000000001,0 -6.939162,38.17805,0 -7.006662000000001,38.02805,0 -7.103889000000001,38.04694,0 -7.126947,38.00333,0 -7.254450000000001,37.98749000000001,0 -7.284996,37.86027,0 -7.422498000000001,37.75472000000001,0 -7.447779,37.65777,0 -7.524441000000002,37.55250000000001,0 -7.467498,37.49082,0 -7.431858,37.25319,0 -7.384167000000002,37.16972,0 -7.333605,37.21222000000001,0 -7.080282000000001,37.2175,0 -6.725835000000001,37.09194,0 -6.515838,36.97194,0 -6.364449000000001,36.79528,0 -6.347214000000002,36.89638,0 -6.246387,36.89581,0 -6.339717000000001,36.88944,0 -6.337224000000001,36.79610000000001,0 -6.443613,36.71889,0 -6.392502,36.62639,0 -6.230835,36.575,0 -6.240834,36.50111,0 -6.172785000000001,36.51221,0 -6.239718,36.46193,0 -6.291666000000001,36.5375,0 -6.318891000000001,36.53249,0 -6.037497,36.18027,0 -5.911389,36.17916,0 -5.797224,36.07388,0 -5.693895,36.06167,0 -5.611662,36.00027,0 -5.425281000000001,36.07083,0 -5.432778,36.17417,0 -5.350833,36.16,0 -5.34528,36.11278,0 -5.395554,35.91634,0 -5.345838,35.84166,0 -5.318892,35.695,0 -5.274999,35.68111,0 -5.253057,35.57695,0 -5.171391,35.5375,0 -5.063337,35.40972,0 -4.695840000000001,35.20888,0 -4.353894,35.14611,0 -3.925278,35.25999,0 -3.892779,35.21583,0 -3.813894,35.20027,0 -3.682503,35.28778,0 -3.577221,35.22667,0 -3.332502,35.19139,0 -3.06639,35.28944,0 -2.973888,35.44027,0 -2.946942,35.32917,0 -2.962782,35.27499,0 -2.914722,35.27361,0 -2.909448,35.17638,0 -2.841660000000001,35.12694,0 -2.747214,35.11555,0 -2.885553,35.24277,0 -2.754999,35.13499,0 -2.635002,35.09972,0 -2.516949,35.09832,0 -2.419164,35.15332,0 -2.209446,35.08582,0 -2.180286,35.01222,0 -2.065833,34.93695,0 -1.99305,34.92945,0 -1.961388,34.87029,0 -1.747215,34.74722,0 -1.857501,34.61028,0 -1.686708,34.48551,0 -1.79334,34.37833,0 -1.750554,34.34945,0 -1.653336,34.085,0 -1.710279,33.8825,0 -1.680552,33.76972,0 -1.730556,33.70499,0 -1.65222,33.66277,0 -1.606662,33.59527,0 -1.600551,33.50972,0 -1.666665,33.38333,0 -1.668051,33.26111,0 -1.480284,33.06305,0 -1.487214,32.97917000000001,0 -1.542501,32.93944,0 -1.382778,32.72445,0 -1.300284,32.70055,0 -1.010286,32.50832,0 -1.119996,32.41555,0 -1.196109,32.40472,0 -1.249722,32.32695,0 -1.25361,32.21472,0 -1.295838,32.16306,0 -1.200834,32.17027,0 -1.175553,32.12083,0 -1.213056,32.08971,0 -2.331108,32.15749,0 -2.853891,32.08834,0 -2.938059,32.02888000000001,0 -2.980557,31.84416,0 -3.386115,31.72611,0 -3.511665,31.67334,0 -3.714714,31.71639,0 -3.830832,31.6775,0 -3.84084,31.47583,0 -3.724722,31.39472,0 -3.803895,31.33916,0 -3.79611,31.22167,0 -3.826107,31.16389,0 -3.788892000000001,31.14861,0 -3.724164,31.18861,0 -3.602214,31.09556,0 -3.617784,30.97667,0 -3.718890000000001,30.93806,0 -4.016943,30.91111,0 -4.16916,30.76916,0 -4.396941,30.64833,0 -4.591944,30.62639,0 -4.920552,30.50805,0 -5.133609,30.19806000000001,0 -5.351949,29.99722,0 -5.560002,29.895,0 -5.89833,29.85667000000001,0 -6.002784000000001,29.82945,0 -6.145839000000001,29.8475,0 -6.217227000000001,29.79888,0 -6.474168000000001,29.83306,0 -6.508611,29.63888,0 -6.58584,29.56722,0 -7.123887,29.63695,0 -7.298334,29.53833,0 -7.433892,29.39722,0 -7.644726000000001,29.385,0 -7.995555,29.11639,0 -8.17695,29.04084,0 -8.495559,28.79056,0 -8.667225,28.70944,0 -8.666667,27.66667,0 -8.666793,27.29046,0 -6.662781,26.12916,0 -4.806108,25.00027,0 -2.448333,23.48722,0 -0.008055,21.83055,0 1.169667,21.10254,0 1.197144,21.00248,0 1.167498,20.74111,0 1.317222,20.73833,0 1.375002,20.6575,0 1.627497,20.57111,0 1.663056,20.53611,0 1.668051,20.40861,0 1.795833,20.30833,0 1.881108,20.30139,0 1.908891,20.23139000000001,0 1.983339,20.26027,0 2.097504,20.21889,0 2.213055,20.28139,0 2.344167,20.17777,0 2.411667,20.0575,0 2.587779,20.00166,0 2.967219,19.95,0 3.233052,19.82028,0 3.210003,19.56527,0 3.277215,19.38972,0 3.206664,19.32611,0 3.196107000000001,19.25861,0 3.114162,19.15639,0 3.331944,18.97639,0 4.245282,19.14666,0 4.2525,16.99445,0 4.21416,16.99361,0 4.198887,16.82083,0 4.200840000000001,16.39389,0 4.069719,16.29389,0 3.981942,16.07638,0 4.000833,15.98971,0 3.924440999999999,15.90416,0 3.885003,15.70917,0 3.823893,15.66527,0 3.726666,15.65277,0 3.539997,15.49722,0 3.523986,15.35816,0 3.032496,15.43306,0 3.020832,15.34195,0 2.628054,15.35055,0 1.312497,15.28666,0 0.974718,14.97861,0 0.694998,14.94221,0 0.512217,15.00083,0 0.3966660000000001,14.96056,0 0.229995,15.00251,0 0.235044,14.91506,0 0.193338,14.83583,0 0.241389,14.75221,0 0.166662,14.52306,0 0.18945,14.46472,0 0.321111,14.36278,0 0.410832,14.24584,0 0.352503,14.1214,0 0.424161,13.98361,0 0.4711050000000001,13.95555,0 0.465003,13.91361,0 0.616662,13.77805,0 0.602217,13.70389,0 0.7727850000000001,13.68832,0 0.77445,13.64416,0 0.902214,13.62278,0 0.9952830000000001,13.56806,0 1.031661,13.4625,0 1.244448,13.39305,0 1.285308,13.34995,0 1.204164,13.36805,0 1.182222,13.31277,0 0.991665,13.37167,0 0.9891630000000002,13.04722,0 1.117215,13.01111,0 1.578339,12.63,0 1.871667,12.60889,0 1.928052,12.70056,0 1.988892,12.73111,0 2.142504,12.69445,0 2.22084,12.59361,0 2.264166,12.4186,0 2.161665,12.41555,0 2.058336,12.35722,0 2.397924,11.89615,0 2.366946,11.79,0 2.308608,11.72555,0 2.310552,11.67055,0 2.193894,11.60361,0 2.014722,11.4225,0 1.868607,11.44584,0 1.609164,11.38834,0 1.56861,11.4525,0 1.439721,11.46834,0 1.328337,11.35139,0 1.351107,11.3025,0 1.303605,11.28945,0 1.274715,11.315,0 1.264716,11.24611,0 1.138059,11.26527,0 1.156392,11.16277,0 1.125279,11.17528,0 1.058058,11.13139,0 1.117782,11.0275,0 0.978894,11.08027,0 0.948609,11.02389,0 0.9677789999999999,10.99584,0 0.917973,10.9964,0 0.879444,10.79972,0 0.8021880000000001,10.71245,0 0.7766640000000001,10.37667,0 1.355004,9.995282,0 1.373895,9.646667000000003,0 1.337499,9.542501,0 1.364256,9.481346,0 1.38861,9.492217999999999,0 1.416663,9.321111,0 1.567215,9.17,0 1.617786,9.049715000000001,0 1.624716,8.548056000000003,0 1.660833,8.496943999999999,0 1.613052,8.374715999999999,0 1.634715,8.357768,0 1.655838,7.532495,0 1.6425,6.995555000000001,0 1.548531,6.995177,0 1.606113,6.895268,0 1.594719,6.80445,0 1.623051,6.757775,0 1.575279,6.679718,0 1.749168,6.456663000000001,0 1.799325,6.281054,0 1.652499,6.250283,0 1.635399,6.21872,0 1.868607,6.273612,0 2.719611,6.36551,0 3.370833000000001,6.385832000000001,0 3.795282,6.432786000000001,0 4.379445,6.368057,0 4.525839,6.305004000000001,0 4.713606,6.164721,0 5.000004,5.857496,0 5.194161,5.502222,0 5.345271,5.329998,0 5.383890000000001,5.111946,0 5.497497,4.805838,0 5.578056,4.718609,0 5.554719,4.71528,0 5.579442,4.659722,0 5.777784,4.454999,0 5.843340000000001,4.410557000000001,0 5.862780000000001,4.434722,0 5.862222000000001,4.388607,0 5.931108000000001,4.340556,0 6.111108,4.272498,0 6.410556,4.313339,0 6.431112,4.345002,0 6.460839000000001,4.322502,0 6.794163000000001,4.336668,0 6.962769000000001,4.389164,0 7.018200000000002,4.374809,0 7.195104000000001,4.365161,0 7.538670000000001,4.437656,0 7.538670000000001,-0.5623475,0 7.538670000000001,-0.6855035,0 2.656305,-0.6707075,0 2.656305,-3.94561,0 7.538670000000001,-3.94561,0 7.538670000000001,-8.945614000000001,0 7.538679000000002,-13.94561,0 7.538679000000002,-18.94561,0 7.538679000000002,-23.94561,0 7.538679000000002,-28.94561,0 7.538679000000002,-33.94561,0 7.538679000000002,-34.50439,0 7.538679000000002,-39.50439,0 7.538679000000002,-44.50439,0 7.538679000000002,-49.50439000000002,0 7.538679000000002,-53.7044,0 7.538679000000002,-58.70439,0 7.538679000000002,-63.70440000000001,0 7.538679000000002,-68.70439,0 7.538679000000002,-73.70439000000002,0 7.538679000000002,-78.70439,0 7.538679000000002,-83.70439,0 7.538679000000002,-88.70438,0 7.538679000000002,-89.90000000000001,0 6.59655,-89.90000000000001,0 5.654412,-89.90000000000001,0 4.712274,-89.90000000000001,0 3.770127,-89.90000000000001,0 2.827989,-89.90000000000001,0 1.885851,-89.90000000000001,0 0.943722,-89.90000000000001,0 0.001584,-89.90000000000001,0 -0.9405540000000001,-89.90000000000001,0 -1.882692,-89.90000000000001,0 -2.82483,-89.90000000000001,0 -3.766967999999999,-89.90000000000001,0 -4.709106,-89.90000000000001,0 -5.651244,-89.90000000000001,0 -6.593382,-89.90000000000001,0 -7.53552,-89.90000000000001,0 -7.533630000000001,-84.89999,0 -7.531740000000001,-79.90000000000001,0 -7.531632,-79.61362,0 -7.529751,-74.61362,0 -7.527861000000001,-69.61362,0 -7.525971,-64.61363,0 -7.524081000000001,-59.61362,0 -7.522191000000001,-54.61362,0 -7.520301000000001,-49.61362,0 -7.518411000000001,-44.61363,0 -7.517322,-41.71362,0 -12.51732,-41.71362,0 -14.64088,-41.71362,0 -14.64088,-36.71363,0 -14.64088,-35.65897,0 -9.640872,-35.65897,0 -7.515036,-35.65897,0 -7.513353,-30.65898,0 -7.51167,-25.65897,0 -7.509978000000001,-20.65897,0 -7.508295000000001,-15.65897,0 -7.506612000000001,-10.65897,0 -7.506171,-9.363187,0 -12.50618,-9.363187,0 -16.1435,-9.363187,0 -16.18769,-5.827609,0 -11.18769,-5.827609,0 -7.506171,-5.827609,0 -7.506171,-0.8276135000000001,0 -7.506171,4.17239,0 -7.506171,4.24899,0 -8.442594,4.596795,0 -9.348579000000001,5.116076,0 -11.38153,6.684983,0 -12.39801,7.325810000000001,0 -13.02778,7.524683000000001,0 -13.27084,8.088164000000001,0 -13.34819,8.972054,0 -13.84538,9.425052000000001,0 -15.12702,10.87243,0 -16.17665,11.01605,0 -16.5081,11.28123,0 -16.36448,11.88337,0 -16.83957,12.41371,0 -16.82851,13.87214,0 -17.2475,14.67323,0 -17.45626,14.64857,0 -17.53723,14.75611,0 -17.16694,14.90305,0 -16.87834,15.225,0 -16.53723,15.77584,0 -16.52768,16.06025,0 -16.53723,16.30166,0 -16.47278,16.48222,0 -16.46583,16.61971,0 -16.17556,17.25416,0 -16.06945,17.58972,0 -16.02862,18.08056,0 -16.05778,18.43472,0 -16.13805,18.65389,0 -16.18806,18.93945,0 -16.28139,19.12777000000001,0 -16.35111,19.20556,0 -16.46306,19.2554,0 -16.53566,19.38283,0 -17.05233,20.76409,0 -17.10528,20.84806,0 -16.94416,21.73972,0 -16.95945,21.82222,0 -16.85334,22.07139,0 -16.71944,22.26082,0 -16.66889,22.29639,0 -16.62806,22.27361,0 -16.50028,22.32221,0 -16.42889,22.51611000000001,0 -16.35917,22.56806,0 -16.30361,22.87555,0 -16.18973,22.94221,0 -16.15416,23.01639,0 -16.15167,23.06888,0 -16.21612,23.08388,0 -16.13584,23.17917,0 -15.92583,23.57722,0 -15.76278,23.79138,0 -15.78167,23.91,0 -15.79028,23.85806,0 -15.86972,23.82861000000001,0 -15.94334,23.68222,0 -16.00167,23.64389,0 -15.91167,23.82195,0 -15.7625,23.95694,0 -15.61417,24.02778,0 -15.29639,24.3336,0 -15.18028,24.48805000000001,0 -15.03139,24.54167,0 -14.98694,24.63639,0 -14.90111,24.68805,0 -14.83473,24.91528,0 -14.82666,25.30916,0 -14.57629,25.84186,0 -19.57629,25.84186,0 -20.79591,25.84186,0 -20.79591,29.7338,0 -18.66778,34.25829,0 -16.53966,38.78279,0 -15.07814,41.89004,0 -10.07815,41.89004,0 -8.882225999999999,41.89004,0 -8.865278999999999,41.87554,0 -8.745011999999999,41.95250000000001,0 -8.606942999999999,42.05916,0 -8.327232,42.08528,0 -8.201223000000002,42.15273,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-21.72161,75.96755,0 -21.86051,75.84662,0 -22.03397,75.85133,0 -22.16084,75.98069,0 -22.16067,76.15807000000001,0 -22.11516,76.25364,0 -22.03279,76.31946000000001,0 -21.93032,76.3443,0 -21.82695,76.32351,0 -21.74206,76.26098,0 -21.69154,76.16843000000002,0 -21.68487,76.06319000000001,0 -21.72161,75.96755,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +7.516629,69.90000000000001,0 7.516629,64.90000000000001,0 7.516629,63.25726,0 5.039118,62.19351,0 4.799088000000001,61.86831,0 4.590036,61.01659999999999,0 4.651983,60.7456,0 4.930722,60.25007,0 5.108805,59.16606000000001,0 5.45724,58.68601000000001,0 5.681772000000001,58.48469,0 6.595425,58.0511,0 7.013538000000001,57.97367,0 7.499043000000001,57.95962999999999,0 7.499043000000001,53.79065000000001,0 6.866424,53.69189,0 6.262488000000001,53.52154,0 5.495949,53.46734,0 5.023638,53.33572000000001,0 4.698432000000001,53.07247,0 4.551318,52.49174,0 4.319037,52.18204,0 3.978351,51.97298,0 3.428613,51.53938,0 3.072447,51.32258000000001,0 2.437524,51.08255,0 1.77939,50.97416,0 1.547109,50.88124000000001,0 1.52001,50.25794,0 1.396116,50.10309,0 1.016721,49.94823000000001,0 0.575379,49.8708,0 0.149526,49.71595,0 0.017901,49.46043000000001,0 -0.013077,49.33654000000001,0 -0.1989,49.29783000000001,0 -0.44667,49.35977,0 -1.18224,49.43721,0 -1.251927,49.72369000000001,0 -1.975878,49.74305,0 -1.882962,49.44108,0 -1.720368,49.23201,0 -1.604223,48.66679000000001,0 -1.828764,48.71325,0 -2.308815,48.71325,0 -2.479167,48.67454000000002,0 -2.66499,48.58163,0 -2.804364,48.67454000000002,0 -2.966967,48.88359000000001,0 -3.137301,48.91456,0 -4.058703,48.77519,0 -5.173668000000001,48.4887,0 -5.142699,48.41902,0 -4.732326000000001,48.0164,0 -4.337442,47.77636,0 -3.865130999999999,47.76862,0 -3.493476,47.61377,0 -3.253446,47.27309,0 -2.529495,47.25373,0 -2.297205,46.97499,0 -2.428839,46.72721,0 -1.778436,46.44073,0 -1.522926,46.18522000000001,0 -1.267407,45.71291,0 -1.204236,45.52971,0 -1.234737,45.14534,0 -1.338453,44.26832,0 -1.480338,43.68816000000001,0 -1.530639,43.54382,0 -1.619874,43.43629,0 -1.780839,43.38888,0 -2.130552,43.28916,0 -2.19834,43.31305,0 -2.295558,43.2961,0 -2.638053,43.4125,0 -2.687778,43.38139,0 -2.752497,43.45499,0 -2.941389,43.43556,0 -3.034998,43.37472,0 -3.020832,43.32861,0 -3.325284,43.41833,0 -3.422502,43.40972,0 -3.450834,43.43527,0 -3.462498000000001,43.39389,0 -3.482784000000001,43.42999,0 -3.430557,43.46444,0 -3.585555,43.51027,0 -3.719718,43.47166,0 -3.813057,43.40361,0 -3.83166,43.43943,0 -3.766392000000001,43.46999,0 -3.810285,43.49249,0 -3.944718,43.46778,0 -3.989448,43.43027000000001,0 -4.016943,43.44777,0 -4.025286,43.40554,0 -4.051395,43.4425,0 -4.21695,43.39389,0 -4.351941,43.40388,0 -4.383891000000001,43.39083,0 -4.371111,43.37028,0 -4.410279,43.40193,0 -4.514553,43.39634,0 -5.121108,43.48916000000001,0 -5.207499,43.47778,0 -5.288607000000001,43.53388,0 -5.365278,43.53917,0 -5.372667,43.51369,0 -5.418612,43.49694,0 -5.381109,43.5261,0 -5.398056,43.5525,0 -5.677785,43.54388,0 -5.844717000000001,43.65527,0 -5.954715,43.58055,0 -6.094998000000001,43.56194000000001,0 -6.235839,43.59499,0 -6.476949,43.55111,0 -6.943338,43.57777,0 -7.019442,43.56417,0 -7.002225000000001,43.53027,0 -7.044822000000001,43.4904,0 -7.045281,43.55943,0 -7.231113000000001,43.57028,0 -7.264998,43.54222,0 -7.248888000000001,43.57916,0 -7.365834,43.67611,0 -7.473609000000001,43.70555,0 -7.46361,43.72639,0 -7.551945,43.72861000000001,0 -7.595838,43.66611000000001,0 -7.629714000000001,43.74778,0 -7.693767,43.73127,0 -7.658892,43.76972,0 -7.683606,43.79277,0 -7.812513,43.71458,0 -7.812513,46.89045,0 -7.812513,51.89045000000001,0 -8.525547,51.58981000000001,0 -9.50454,51.40962,0 -9.828864000000001,51.43965,0 -10.24928,51.55978,0 -10.42346,51.86608,0 -10.65169,52.04025,0 -9.948978,52.56879000000001,0 -9.68472,52.71294,0 -9.498528000000002,52.94116,0 -9.8649,53.12735000000001,0 -10.18322,53.39763,0 -10.32736,53.58982,0 -10.29734,53.98021000000001,0 -10.25529,54.12435000000001,0 -10.12917,54.26851,0 -9.834866999999999,54.36460000000001,0 -9.054081,54.29253,0 -8.807823000000001,54.70094,0 -8.567595000000001,55.00724,0 -8.261279999999999,55.27752,0 -7.806969000000001,55.35279,0 -7.806969000000001,57.83205,0 -7.806969000000001,62.83205,0 -12.54215,62.84239,0 -17.54214,62.85332,0 -22.54213,62.86423,0 -25.28129,62.86423,0 -25.28129,67.4954,0 -22.51389,67.4954,0 -22.5117,67.49539,0 -17.51171,67.48272,0 -12.51173,67.47004,0 -7.511742000000002,67.45737,0 -7.511742000000002,69.90000000000001,0 -7.511742000000002,74.89999,0 -7.511742000000002,79.90000000000001,0 -7.511742000000002,84.90001,0 -7.511742000000002,89.90000000000001,0 -6.572475,89.90000000000001,0 -5.633190000000001,89.90000000000001,0 -4.693914000000001,89.90000000000001,0 -3.754647,89.90000000000001,0 -2.815371,89.90000000000001,0 -1.876104,89.90000000000001,0 -0.936828,89.90000000000001,0 0.002448,89.90000000000001,0 0.9417150000000001,89.90000000000001,0 1.880991,89.90000000000001,0 2.820267,89.90000000000001,0 3.759534,89.90000000000001,0 4.69881,89.90000000000001,0 5.638086,89.90000000000001,0 6.577353,89.90000000000001,0 7.516629,89.90000000000001,0 7.516629,84.90001,0 7.516629,79.90000000000001,0 7.516629,74.89999,0 7.516629,69.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +01:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(1);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0100</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +22.54661000000001,89.90000000000001,0 22.54661000000001,84.90001,0 22.54661000000001,81.51716999999999,0 27.54661,81.51716999999999,0 32.54661,81.51716999999999,0 33.89268,81.51716999999999,0 33.89268,76.51716999999999,0 33.89268,75.84414,0 28.89267,75.84414,0 23.89267,75.84414,0 22.54661000000001,75.84414,0 22.54661000000001,70.84514,0 23.95011,71.17124,0 24.72286,71.15327000000001,0 25.9988,71.18922000000001,0 28.20923,71.09936999999999,0 28.92806,70.93761000000001,0 30.18603,70.73994,0 31.17443,70.36256,0 30.85484,69.7923,0 30.89805,69.70555,0 30.95472,69.68138,0 30.94583,69.58222000000001,0 30.91,69.54886999999999,0 30.52916,69.5461,0 30.20361,69.66388000000001,0 30.10333,69.66306,0 30.07277,69.6311,0 30.14277,69.62833000000001,0 30.185,69.58275999999999,0 30.09777,69.50333000000001,0 29.945,69.43053999999999,0 29.31,69.31527,0 29.32167,69.24554000000001,0 29.23916,69.1075,0 29.06333,69.02916999999999,0 28.95734,69.05162,0 28.78694,69.11915999999999,0 28.81916,69.23193000000001,0 29.29972,69.48027,0 29.1275,69.68582000000001,0 28.37972,69.8275,0 28.32332,69.88749,0 28.09417,69.93859000000001,0 27.97916,70.00664999999999,0 27.94416,70.08027,0 27.87305,70.08804000000001,0 27.57333,70.06887999999999,0 27.53833,70.03525999999999,0 27.3,69.99055,0 27.29194,69.95,0 27.06889,69.90860000000001,0 26.80658,69.95206,0 26.47056,69.93527,0 26.37806000000001,69.85444,0 26.15583,69.74249,0 25.97834,69.70193,0 25.94194,69.66722,0 25.97416,69.63527000000001,0 25.9575,69.59027,0 25.85277,69.54666,0 25.87416,69.48636999999999,0 25.82666,69.43638,0 25.83361,69.38416000000001,0 25.755,69.34220999999999,0 25.71333,69.25526000000001,0 25.73222,69.10916,0 25.81277,69.0175,0 25.65277,68.90639,0 25.58555,68.87887000000001,0 25.40695,68.88638,0 25.21445,68.81749000000001,0 25.15472,68.76778,0 25.10334,68.63472,0 24.95523,68.60391,0 24.89566,68.5582,0 24.75445,68.66025999999999,0 24.35139,68.71053000000001,0 23.97639,68.83249000000001,0 23.83,68.82999,0 23.69277,68.71222,0 23.22500000000001,68.62971,0 23.06389,68.69555,0 22.80277,68.69138,0 22.55,68.73779,0 22.39833,68.71111000000001,0 22.34667,68.8447,0 21.68195,69.28471,0 21.32083,69.32611,0 21.055,69.22861,0 21.02806,69.20805,0 21.04222,69.16082,0 21.11305,69.10805000000002,0 21.05944,69.03444,0 20.73833,69.09636999999999,0 20.58093,69.0603,0 20.60417,69.05305,0 20.86666,69.00499000000001,0 20.93889,68.95834000000001,0 20.88888,68.92583000000002,0 20.89527,68.89361,0 21.08139,68.86665000000001,0 21.30195,68.75555,0 21.40666,68.74361,0 21.46556,68.67805,0 21.62306,68.64942000000001,0 21.7575,68.57693,0 21.89722,68.56999,0 22.05056,68.47915999999999,0 22.33471,68.47275999999999,0 22.8236,68.38805000000001,0 23.14222,68.23000999999999,0 23.15138,68.12277,0 23.30722,68.13916,0 23.39445,68.0425,0 23.65695,67.95081999999999,0 23.64472,67.91306,0 23.48333,67.86305,0 23.48083,67.72665000000001,0 23.55361,67.58749,0 23.47195,67.55638,0 23.42889,67.47584000000001,0 23.48695,67.43804,0 23.54917,67.45332999999999,0 23.76778,67.41611,0 23.74472,67.36834,0 23.77333,67.31444000000001,0 23.60555,67.25943,0 23.58445,67.22387000000001,0 23.61028,67.20166,0 23.57167,67.15666,0 24.00777,66.80055,0 23.89,66.74303999999999,0 23.87861,66.55722,0 23.72527,66.50082999999999,0 23.63917,66.43053999999999,0 23.72222,66.20027,0 23.9125,66.14833,0 23.9486,66.10138000000001,0 24.16601,65.8115,0 20.69736,63.31795000000001,0 19.33158,60.19100000000001,0 19.36751,59.65187,0 21.30838,59.61593000000001,0 20.94284,55.2872,0 20.98481,55.27655,0 21.26394,55.24899,0 21.38128,55.28709,0 21.50389,55.19332,0 21.64027,55.18472,0 21.86305,55.09194,0 22.00972,55.08778,0 22.06194,55.02749000000001,0 22.19722000000001,55.06166,0 22.5992,55.07113,0 22.64082000000001,54.96638,0 22.73222000000001,54.96249,0 22.77056,54.93888,0 22.75555,54.92055000000001,0 22.83499000000001,54.90166,0 22.86916,54.82333,0 22.84249,54.77749,0 22.74055,54.72777000000001,0 22.69832,54.58943000000001,0 22.69444,54.47332000000001,0 22.78588,54.36384,0 22.85611,54.4086,0 22.98749000000001,54.38917000000001,0 23.06166,54.35167,0 23.06611,54.30804,0 23.14249,54.31054,0 23.35028000000001,54.23888000000001,0 23.48444,54.13833000000001,0 23.51777,54.03028,0 23.47528000000001,53.99110000000001,0 23.50404,53.94704,0 23.67472,53.4911,0 23.81472,53.25444,0 23.91055,53.16721,0 23.87805000000001,53.07555,0 23.93305,53.01417,0 23.91527,52.91888,0 23.94083,52.73220000000001,0 23.75389,52.61583000000001,0 23.59722,52.59639,0 23.39722,52.51445,0 23.1654,52.28227,0 23.20638,52.22526,0 23.49304,52.17416000000001,0 23.51472,52.1236,0 23.63861,52.07944,0 23.67277,51.99138,0 23.61173,51.91397,0 23.59472,51.8336,0 23.62944,51.80471,0 23.55556,51.74943,0 23.53499,51.65359000000001,0 23.56138,51.5425,0 23.60463,51.52769,0 23.69306,51.40471,0 23.63611,51.30110000000001,0 23.86694,51.14861,0 23.86277,51.09833,0 23.96638,50.94887,0 24.14471,50.85804,0 24.11472,50.83138000000001,0 23.97277,50.82805000000001,0 23.9525,50.79472,0 24.07972,50.70499000000001,0 24.13277,50.54445000000001,0 24.02611,50.49388,0 23.99166,50.40721,0 23.70388,50.37833,0 23.67694,50.32444,0 23.14611,49.98305,0 22.68607,49.5771,0 22.65611,49.51972,0 22.69222,49.42333000000001,0 22.73833,49.39027,0 22.76027,49.29528,0 22.70028,49.16555,0 22.73472,49.17722000000001,0 22.87611,49.09,0 22.88607,49.00292,0 22.76916,49.05332,0 22.67417,49.04416000000001,0 22.59805,49.09109999999999,0 22.55805,49.07944,0 22.53721,48.99554,0 22.46749,48.97860000000001,0 22.37694,48.85194,0 22.38917,48.79722,0 22.34805,48.76417000000001,0 22.32888,48.68694000000001,0 22.15888,48.58027,0 22.15145,48.41192000000001,0 22.26889,48.40721,0 22.26028,48.36082,0 22.31944,48.36166,0 22.31888,48.31416000000001,0 22.37861,48.24554,0 22.50445,48.24749000000001,0 22.57528,48.19443,0 22.62116,48.10163,0 22.7975,48.11472,0 22.8825,48.04694,0 22.85056,47.98083,0 22.8948,47.95454,0 22.77084,47.87943,0 22.77444,47.8364,0 22.65361,47.77638000000001,0 22.45444,47.79639000000001,0 22.42139,47.74389,0 22.27694,47.72861000000001,0 22.18639,47.59861000000001,0 22.01527000000001,47.51445,0 22.00972,47.38083,0 21.9225,47.35443,0 21.79084,47.12028000000001,0 21.65111,47.02972000000001,0 21.69582,47.00082,0 21.67167,46.95138,0 21.59972000000001,46.91805000000001,0 21.60527,46.85722000000001,0 21.51972,46.81527,0 21.48806,46.75278000000001,0 21.52194,46.71277000000001,0 21.44194,46.68194,0 21.40500000000001,46.62111000000001,0 21.32666,46.61999,0 21.26056,46.49528000000001,0 21.29222,46.42027000000001,0 21.19721,46.39139,0 21.17667,46.29556,0 21.05944,46.23917,0 20.82028,46.27472,0 20.75332,46.23888000000001,0 20.76305,46.19917000000001,0 20.72056,46.19166,0 20.72056,46.16555,0 20.62472,46.13027000000002,0 20.48694,46.18110999999999,0 20.47805,46.14972,0 20.34027,46.15944,0 20.26102,46.11486,0 20.37805,45.97806,0 20.59167,45.89417000000001,0 20.71334,45.74389,0 20.80139,45.76416,0 20.79888,45.65667,0 20.76666,45.60999,0 20.82361,45.53249,0 20.78305,45.48472,0 21.04889,45.31639,0 21.16306,45.30972,0 21.2675,45.23278,0 21.41666,45.21472000000001,0 21.51361,45.15111000000001,0 21.51277,45.12333,0 21.46028,45.10277,0 21.44722000000001,45.035,0 21.36695,45.00832,0 21.54083,44.93861,0 21.55861,44.89611,0 21.36611,44.86444,0 21.40040000000001,44.78082,0 21.58834,44.76332000000001,0 21.64944,44.65778,0 21.99389,44.63556,0 22.08444,44.50306,0 22.14638,44.47916,0 22.17694,44.48194000000001,0 22.31361,44.66417,0 22.47916,44.71027,0 22.76489000000001,44.55901,0 22.69958,44.52252,0 22.59027,44.54916,0 22.54611,44.4875,0 22.45734,44.47435,0 22.54972,44.32306,0 22.6875,44.27777,0 22.68144,44.2247,0 22.61667,44.17278,0 22.6225,44.07083,0 22.41695,44.00695,0 22.35945,43.81694,0 22.4275,43.68861,0 22.49555000000001,43.64555,0 22.49555000000001,43.57028,0 22.54333000000001,43.4725,0 22.76666,43.38582,0 22.90444000000002,43.23082,0 23.00721,43.19611,0 22.96639,43.10167,0 22.79083,42.98527,0 22.74556000000001,42.89361,0 22.525,42.87861,0 22.44222,42.82167,0 22.50139,42.73916,0 22.46111,42.6925,0 22.43861,42.57471000000001,0 22.55805,42.47916,0 22.45639,42.34027,0 22.36527,42.32389,0 22.5211,42.14861,0 22.67528,42.0675,0 22.86444,42.02722,0 22.89584,41.88527,0 22.96193,41.77861,0 23.03277,41.72834000000001,0 22.9525,41.63444,0 22.96083,41.3586,0 22.77222,41.32444,0 22.72917,41.14471,0 22.6725,41.18777,0 22.58555,41.11944,0 22.33417,41.12722,0 22.25667,41.17139,0 22.13111,41.12500000000001,0 22.06667,41.15834,0 21.91611,41.09527,0 21.91195,41.04943,0 21.80416,40.97777,0 21.79222,40.93278,0 21.69445,40.93805,0 21.6,40.87277,0 21.41972,40.91917,0 21.34305,40.87193,0 20.98349,40.8559,0 20.96194,40.77166,0 21.05194,40.67666000000001,0 21.04138,40.56195,0 20.94556,40.46694,0 20.84889,40.47916,0 20.78889,40.42832,0 20.78972,40.36083,0 20.71306,40.27833,0 20.72111,40.22305,0 20.67861,40.18527,0 20.67195,40.09805,0 20.45,40.07249,0 20.39416,39.99917000000001,0 20.3161,39.99334,0 20.41445,39.82443,0 20.38138,39.78722,0 20.29138,39.80667,0 20.32083,39.7336,0 20.26444,39.66916000000001,0 20.19916,39.64472,0 20.01002,39.6912,0 19.98255,39.68724,0 19.95083,39.78693,0 19.57928,39.90293,0 19.37265,39.8712,0 19.36993,39.84311,0 19.80221,39.58348,0 19.87652,39.44391,0 20.07227,39.36417,0 20.13298,39.19515,0 20.2381,39.1222,0 20.49728,39.20377,0 20.73834,38.90834,0 20.72928,38.86393,0 20.63178,38.84898,0 20.54984,38.68631000000001,0 20.51631,38.3501,0 20.48369,38.32835,0 20.44472,38.36641,0 20.39216,38.36551000000001,0 20.31669,38.18789,0 20.37856,38.13986,0 20.45741,38.14892,0 20.51087,38.09272000000001,0 20.61781,37.81678,0 20.68738,37.72452,0 20.80903,37.64777,0 20.99752,37.69217,0 21.30836,37.62784,0 21.36454,37.6464,0 21.54217,37.54129,0 21.67267,37.33829,0 21.57297,37.21142,0 21.53381,37.02636,0 21.70063,36.72363,0 21.79948,36.68655,0 21.98484,36.77306,0 21.94816,36.8444,0 21.95903,36.9749,0 22.04059,37.01296,0 22.13302,37.00752,0 22.12033,36.90784,0 22.23996,36.84983,0 22.32878,36.7139,0 22.35234,36.64503,0 22.33602000000001,36.51816,0 22.3926,36.43942,0 22.50257,36.37666,0 22.50257,32.79314,0 23.12222,32.62666,0 23.09833,32.52166,0 23.14555,32.46694000000001,0 23.13333,32.39666,0 23.09306,32.42667,0 23.08139,32.33277,0 23.20889,32.27555,0 23.30916,32.15861,0 23.30778,32.21861,0 23.50166,32.17139,0 23.72639,32.17472,0 23.98639,32.09833,0 23.96778,32.0675,0 24.09472,32.00111,0 24.54305,31.98527,0 24.7275,32.02693,0 24.97389,31.97,0 25.03611,31.92445,0 25.02555,31.84139,0 25.15166,31.64694,0 25.06972,31.58167,0 25.04222,31.49972,0 24.86833,31.375,0 24.86945,31.15639,0 25.01916,30.7875,0 24.92611000000001,30.50611,0 24.78082,30.33388,0 24.70667,30.16861,0 24.83472,29.87666,0 24.8125,29.78445,0 24.88694,29.66389,0 24.8775,29.50638,0 24.99777,29.24888,0 24.9989,26.99969000000002,0 25.00142,21.99969,0 25.00083,19.99916,0 24.00275,19.49907,0 23.99981,18.7543,0 23.9996,15.69871,0 23.60889,15.75833,0 23.35694,15.68166,0 23.11816,15.71029,0 22.93722,15.56194,0 22.92305,15.48722,0 22.99834,15.35639,0 22.93583,15.11611,0 22.84916,15.07834,0 22.75332,14.97694,0 22.7389,14.90278,0 22.67056,14.85945,0 22.7025,14.69139,0 22.47,14.62945,0 22.38416,14.55416,0 22.38138,14.52306,0 22.44556,14.47972,0 22.46972,14.34472,0 22.43444,14.30972,0 22.44389,14.27222,0 22.55527,14.23194,0 22.555,14.12555,0 22.23833,13.96833,0 22.19833,13.9225,0 22.08444,13.77916,0 22.13861,13.72806,0 22.15361,13.63444,0 22.22472,13.57195,0 22.23056,13.47889,0 22.29416000000001,13.35861,0 22.15972,13.19027,0 22.02055,13.13779,0 21.94333,13.05361,0 21.82777,12.7975,0 21.95277,12.64389,0 22.14056,12.65527,0 22.22333,12.74722,0 22.46694,12.62166,0 22.38389,12.46527,0 22.43278,12.40333,0 22.50139,12.17695,0 22.47666,12.03416,0 22.56361,12.07471,0 22.64082000000001,12.07111,0 22.56167,11.62166,0 22.64195,11.51611,0 22.77555,11.46222,0 22.79111,11.40167,0 22.93084,11.41583,0 22.97083,11.28306,0 22.9425,11.0925,0 22.86651,10.92244,0 23.00945,10.69861,0 23.30389,10.45916,0 23.66916000000001,9.866942,0 23.69471,9.671659999999999,0 23.62333,9.535275,0 23.63138,9.449442,0 23.66445,9.440837999999999,0 23.65222,9.279999,0 23.55639,9.181664,0 23.49277,9.174168,0 23.44861,9.025002000000001,0 23.50361,8.959716000000002,0 23.56528000000001,9.005281999999999,0 23.58305,8.988605,0 23.58167,8.906112,0 23.49833,8.77833,0 23.51777,8.714159,0 23.63305,8.736947000000001,0 23.73916,8.705276,0 23.82389,8.727785000000001,0 24.25333,8.691110999999999,0 24.23027,8.624995999999999,0 24.26917,8.583055999999999,0 24.16584,8.477783000000001,0 24.14027,8.375832000000001,0 24.17721,8.316108,0 24.36111,8.247221,0 24.46,8.271386,0 24.54583,8.204715,0 24.66,8.218610999999999,0 24.86555,8.165285000000001,0 24.94277,8.083888999999999,0 24.97333,7.992774,0 25.08027,7.897500000000001,0 25.18611,7.901946,0 25.25833,7.840836,0 25.29611,7.65,0 25.18389000000001,7.583336000000002,0 25.20695,7.497503,0 25.33111,7.425279,0 25.36555,7.348338,0 25.465,7.280001000000001,0 25.53139,7.277786,0 25.59389,7.216388,0 25.81389,7.147214000000001,0 25.81971,7.108335000000001,0 26.04834,6.998336,0 26.10083,6.841106,0 26.40628,6.63525,0 26.28916,6.468894,0 26.31361,6.457769000000001,0 26.30778000000002,6.383051,0 26.39527,6.307785,0 26.45944,6.294446000000001,0 26.47611,6.224714,0 26.52361000000001,6.221106,0 26.52361000000001,6.173613,0 26.4375,6.077772,0 26.51416,6.109722,0 26.55445,6.033608,0 26.80861,5.980004,0 26.83945,5.898338,0 26.91027,5.899446000000001,0 26.94972,5.846949000000001,0 27.00111,5.862501,0 27.04917,5.789997,0 27.14278,5.771943,0 27.24084,5.647779000000001,0 27.23389,5.588334,0 27.28166,5.578613,0 27.23584,5.428610000000001,0 27.25472,5.331392,0 27.3,5.22611,0 27.45806,5.083613,0 27.45527,5.016392,0 27.39361,5.089167,0 27.08417,5.204159,0 26.93777,5.141106,0 26.86777,5.029163,0 26.73166,5.092784,0 26.48889,5.04639,0 26.38639,5.145552,0 26.29222,5.141394,0 26.13277,5.257215,0 26.05694,5.192504,0 25.97333,5.231663000000001,0 25.9,5.166945,0 25.87583,5.217497,0 25.82583,5.195556,0 25.79083,5.263893,0 25.74916,5.241941,0 25.54222,5.381387,0 25.53833,5.350554,0 25.36195,5.314715000000001,0 25.37555,5.294717,0 25.31056,5.19111,0 25.34945,5.144714,0 25.30832,5.032503,0 25.17583,4.995278,0 25.13361,5.017778000000001,0 25.07027,4.945832,0 24.95888,4.987215000000001,0 24.73445,4.910841,0 24.65056,4.92111,0 24.66111,4.964445,0 24.52556,5.077214000000001,0 24.44750000000001,5.105834,0 24.42555,5.065838,0 24.39417,5.115554,0 24.355,5.054715,0 24.40056,5.030001,0 24.28611,4.994999,0 24.25611,4.929443,0 24.2275,4.960278000000001,0 24.15194,4.900004,0 24.10555,4.920002,0 23.98195,4.855275,0 23.95417,4.873337,0 23.94222,4.809996,0 23.83027,4.82084,0 23.58445,4.734449,0 23.44166,4.653612,0 23.42639,4.591943,0 23.38556000000001,4.587777,0 23.26833,4.627214,0 23.1825,4.727222,0 23.10000000000001,4.70528,0 23.0225,4.743053,0 22.97500000000001,4.831388,0 22.89584,4.821110000000001,0 22.85582000000001,4.706945,0 22.77584,4.706666,0 22.69027000000001,4.483611,0 22.59445,4.470551,0 22.60972,4.358889,0 22.54111,4.28111,0 22.53861,4.218894,0 22.4536,4.144580000000001,0 22.45972,4.137497,0 22.56111,4.101389,0 22.68722,3.998050999999999,0 22.75861,3.991391000000001,0 22.76555,3.905,0 22.85806,3.858604999999999,0 22.99083,3.883337,0 23.03555,3.860559,0 23.14722,3.896108,0 23.35889,3.809448,0 23.39943,3.835278000000001,0 23.515,3.825836,0 23.48695,3.738338,0 23.36971,3.615839,0 23.17111,3.678056000000001,0 23.03750000000001,3.547503,0 22.92861,3.548051000000001,0 22.67834,3.453893,0 22.62361,3.388896,0 22.64333,3.325284,0 22.79639,3.259449,0 22.77972,3.063887,0 22.81472000000001,2.996109,0 22.86417000000001,3.011947999999999,0 23.00111,3.161664000000001,0 23.08195,3.147218,0 22.98472,2.857779,0 23.00389,2.757221,0 22.96,2.672216,0 23.03806,2.558051,0 23.12666,2.502503,0 23.35945,2.523887,0 23.4225,2.451941,0 23.6275,2.330837,0 23.65138,2.192498,0 23.34389,2.251395000000001,0 23.09027,2.093894,0 22.97583,2.184165,0 22.86084,2.045835,0 22.75722,2.010284,0 22.69972,2.034162,0 22.6475,2.109447,0 22.57222000000001,2.110283,0 22.58861,2.048606,0 22.51667,1.916387,0 22.47277,1.693889,0 22.31111,1.557503,0 22.28639,1.503332,0 22.4339,1.489715,0 22.62416,1.361663,0 22.81472000000001,0.9963894000000001,0 22.86139,0.6711115,0 22.93528,0.5708334000000001,0 22.9936,0.3750025000000001,0 23.41361,-0.2336045,0 23.29389,-0.3172145,0 23.13,-0.2808366,0 22.97027,-0.4063866,0 23.21778,-0.4249986,0 23.26917,-0.4952795999999999,0 23.46778000000001,-0.4386065,0 23.62945,-0.6358325,0 23.48555,-0.6988865,0 23.35611,-0.8072196,0 23.52667,-0.9758345000000001,0 23.72972,-1.08334,0 23.77278,-1.179163,0 23.94889,-1.306666,0 24.10583,-1.384723,0 24.30388,-1.393057,0 24.37583,-1.538893,0 24.4214,-1.786798,0 24.03527,-1.73278,0 23.98527,-1.769167,0 23.83777,-1.765,0 23.78306,-1.806661,0 23.77472,-1.917495,0 23.69805,-1.931392,0 23.69389,-2.018889,0 23.5,-2.009998,0 23.22111,-2.085552,0 23.10944,-1.945837,0 23.03277,-1.928053,0 22.93666,-1.962216,0 22.87028,-1.88416,0 22.67972,-1.806661,0 22.62556,-1.801108,0 22.50972000000001,-1.958887,0 22.185,-1.903339,0 22.16638,-1.942219,0 22.28722000000001,-2.445553,0 22.25139,-2.466946,0 22.19861,-2.447784,0 22.19055000000001,-2.313334,0 22.13972,-2.276389,0 22.0486,-2.370835,0 21.95194,-2.415555,0 21.89883,-2.490597,0 21.82688,-2.529217,0 21.46972,-2.335284,0 21.44083,-2.480554,0 21.24555,-2.512783000000001,0 21.00686,-2.48896,0 20.95916,-2.588608,0 20.95417,-2.692783,0 20.82945,-2.848887,0 20.65611000000001,-3.307221999999999,0 20.66917,-4.151665,0 20.50944,-4.254166000000001,0 20.37444,-4.295278,0 20.3375,-4.397779,0 20.26277,-4.395555,0 20.22445,-4.335003,0 20.17777,-4.33278,0 20.12861,-4.278889,0 20.03889,-4.278051,0 20.03333,-4.300551,0 20.11889,-4.500837,0 20.06222,-4.595832,0 20.05445,-4.769164,0 20.19166,-5.159161,0 20.19639,-5.273613,0 20.14305,-5.40361,0 20.18,-5.684445000000001,0 20.11222,-5.834448,0 20.05305,-5.865004,0 20.01974,-5.942214,0 19.84027,-6.031107000000001,0 19.68847,-6.187500000000002,0 19.73667,-6.575553000000002,0 19.9325,-6.842503,0 19.96277,-6.995556000000001,0 20.31138,-6.99472,0 20.3325,-6.914449,0 20.62975,-6.913881,0 20.54306,-7.124445,0 20.53861,-7.284483,0 21.78295,-7.280848,0 21.81555,-7.316110000000001,0 21.85388,-7.425559000000001,0 21.85111,-7.591662,0 21.77305,-7.776109,0 21.75416,-7.999444000000001,0 21.85861,-8.157781,0 21.87666,-8.279163000000001,0 21.91611,-8.308612,0 21.94,-8.496109000000001,0 21.84777,-8.949718000000001,0 21.85472,-9.229445999999999,0 21.79056,-9.405559,0 21.87472,-9.665001999999999,0 21.95639,-9.763335,0 21.985,-9.755838000000001,0 22.02138,-9.848053,0 22.1625,-9.930555,0 22.20722,-10.15416,0 22.31361,-10.37,0 22.26416,-10.50055,0 22.30334,-10.53779,0 22.30806,-10.61278,0 22.31944,-10.75973,0 22.16361,-10.86139,0 22.19361,-10.99417,0 22.24611,-11.07305,0 22.25111,-11.2525,0 22.48,-11.13333,0 22.52972,-11.03778,0 22.71667,-11.09334,0 22.86361,-11.04917,0 22.94277,-11.095,0 23.085,-11.09668,0 23.21861000000001,-11.07639,0 23.36806,-10.98278,0 23.47639,-10.95834,0 23.8639,-11.02722,0 23.98621,-10.87046,0 24.0225,-10.96889,0 24.02527,-11.28056,0 24.08445,-11.40278,0 24.03083,-11.45306,0 24.03028,-11.52639,0 23.96583,-11.655,0 24.01555,-11.81417,0 23.98416,-11.88222,0 23.975,-12.19917,0 24.035,-12.26417,0 24.05222,-12.38528,0 23.9475,-12.53723,0 23.88611,-12.77111,0 23.89389,-12.83223,0 23.99777,-12.93723,0 24.02055,-13.00639,0 21.99834,-13.00416,0 22.00015,-16.17166,0 22.12111,-16.34112,0 22.13888,-16.4925,0 22.48778,-16.77417,0 23.20167,-17.47973,0 23.47611,-17.62584,0 21.37936,-18.01489,0 21.24194,-17.93889,0 21.16389,-17.93139,0 20.95417,-17.9675,0 20.82,-18.03861,0 20.75195,-17.99611000000001,0 20.5475,-17.98168,0 20.33806,-17.85805,0 20.03583,-17.90028,0 19.92056,-17.85723,0 19.81194,-17.85833,0 19.76195,-17.8989,0 19.66166,-17.83778,0 19.55916,-17.8675,0 19.34333,-17.8475,0 19.24222,-17.80583,0 18.91584,-17.81555,0 18.75638,-17.74778,0 18.63722,-17.64473,0 18.45154,-17.38984,0 18.00055,-17.38617,0 14.20417,-17.3831,0 14.17694,-17.42028,0 13.98556,-17.42584,0 13.52471,-17.12695,0 13.52611,-17.07778,0 13.47445,-17.01194,0 13.37333,-16.96945,0 13.25166,-16.98528,0 13.16056,-16.95278,0 12.88666,-17.03361,0 12.81611,-17.10889,0 12.55722,-17.24333,0 12.45083,-17.25417,0 12.4214,-17.20361,0 12.24611,-17.22721,0 12.16778,-17.14389,0 12.13139,-17.16112,0 12.08471,-17.13528,0 11.92861,-17.18278,0 11.82584,-17.26639000000001,0 11.75278,-17.25484,0 11.7211,-17.655,0 11.80472,-18.08195,0 11.9489,-18.25639,0 12.02083,-18.47111,0 12.29611,-18.7061,0 12.46083,-18.92805,0 12.46416,-19.00418,0 12.54222,-19.06778,0 12.67,-19.295,0 12.70722,-19.41778,0 12.93167,-19.79583,0 13.05111,-20.06778,0 13.175,-20.18084,0 13.25083,-20.46584,0 13.35277,-20.64667,0 13.4,-20.85583,0 13.85361,-21.49139,0 13.87972,-21.59944,0 13.97028,-21.73167,0 13.94916,-21.78194,0 14.27777,-22.11028,0 14.39167,-22.29,0 14.51139,-22.55279,0 14.52485,-22.69206,0 14.52584,-22.91112,0 14.46278,-23.00055,0 14.43833,-22.87778,0 14.41056,-22.96723,0 14.45388,-23.1464,0 14.49833,-23.33056,0 14.49194,-23.37584,0 14.47,-23.34668,0 14.43444,-23.41778,0 14.49611,-23.58834,0 14.48445,-23.75639,0 14.51471,-23.83583,0 14.50945,-23.90473,0 14.45611,-23.9675,0 14.46333,-24.10917,0 14.61278,-24.45639,0 14.59916,-24.56555,0 14.77695,-24.79945,0 14.79277,-24.94361,0 14.83207,-24.99944,0 14.85778,-25.08723,0 14.80083,-25.285,0 14.88222,-25.5675,0 14.83777,-25.76194,0 14.91361,-25.84472,0 14.90333,-25.91055,0 14.98,-26.0575,0 14.97805,-26.12667,0 14.93861,-26.13945,0 14.96833,-26.33972,0 15.08792,-26.40098,0 15.17111,-26.5964,0 15.14194,-26.67778,0 15.13472,-26.62889,0 15.07972,-26.65,0 15.08389,-26.69861,0 15.15777,-26.90278,0 15.23471,-26.96667,0 15.29417,-27.32250000000001,0 15.46666,-27.54445000000001,0 15.53333,-27.66139,0 15.52861,-27.7275,0 15.67,-27.87139000000001,0 15.67639,-27.9339,0 15.75277,-28.03055,0 16.02778,-28.24473,0 16.35445,-28.56278,0 16.44195,-28.61834,0 16.48959,-28.57819,0 16.46972,-28.63028,0 16.56833,-28.72389,0 16.60611,-28.87195,0 16.67083,-28.90584,0 16.73277,-29.01889,0 16.81528,-29.085,0 16.84444,-29.22139,0 17.00332,-29.52445,0 17.08445,-29.83584000000002,0 17.27889,-30.34667,0 17.53944,-30.7575,0 17.61195,-30.92972,0 17.90944,-31.35973,0 18.18139,-31.67473,0 18.2786,-31.89278,0 18.35027,-32.28807,0 18.31611,-32.33501,0 18.335,-32.47834,0 18.29222,-32.62445,0 18.13175,-32.77878000000001,0 18.05361,-32.77612,0 17.96694,-32.69862,0 17.9025,-32.73333,0 17.87666,-32.80612,0 17.8675,-32.81196000000001,0 17.84527,-32.82584,0 17.87694,-32.86084,0 17.85714,-32.91527000000001,0 17.89305,-33.03111,0 17.91389,-33.04805,0 17.98333,-33.00028,0 18.03444,-33.0325,0 18.03721,-33.10196,0 18.12306,-33.20001,0 18.00167,-33.09333,0 17.95556,-33.10305,0 17.96722,-33.13472,0 18.09,-33.22501,0 18.14805,-33.35612,0 18.29471,-33.46611,0 18.31527,-33.56834,0 18.44111,-33.70667,0 18.4825,-33.89473,0 18.38,-33.91195,0 18.31027,-34.0325,0 18.35555,-34.05834,0 18.30972,-34.14918,0 18.36305,-34.18723,0 18.42027,-34.32306,0 18.4901,-34.34823,0 18.46666,-34.3289,0 18.47888,-34.23445000000001,0 18.43583,-34.16889,0 18.48805,-34.09778,0 18.60195,-34.07362,0 18.79332,-34.08667,0 18.85527,-34.15611,0 18.81417,-34.19028,0 18.84527,-34.245,0 18.80833,-34.29723,0 18.81778,-34.37778000000001,0 19.08334,-34.34639,0 19.13722,-34.29834,0 19.08334,-34.36084000000001,0 19.11222,-34.40334,0 19.21082,-34.42861,0 19.28194,-34.40973,0 19.3575,-34.50029,0 19.36333,-34.53778,0 19.29611,-34.61972,0 19.38945,-34.60196,0 19.51416,-34.67084,0 19.51639,-34.67556,0 19.65166,-34.77584,0 19.85777,-34.7525,0 19.99861,-34.82195,0 20.05722,-34.805,0 20.055,-34.7525,0 20.08972,-34.72639,0 20.21972,-34.67973,0 20.46029,-34.48223,0 20.53444,-34.45583,0 20.85222,-34.46305,0 20.85666000000001,-34.42083,0 20.80861000000001,-34.39723,0 20.9425,-34.36028,0 21.06277,-34.36389,0 21.29472000000001,-34.43389,0 21.52832,-34.3525,0 21.65556,-34.39056,0 21.8025,-34.38305,0 21.90166,-34.33611,0 21.94972,-34.22501,0 22.13416,-34.19112,0 22.11167,-34.14861000000001,0 22.15389,-34.09251,0 22.50382,-34.04004,0 22.50382,-35.54002000000001,0 22.50382,-40.54002,0 22.50382,-45.54003,0 22.50382,-50.54002,0 22.50382,-55.54003,0 22.50382,-60.54002000000001,0 22.50382,-65.54002,0 22.50382,-70.54002,0 22.50382,-73.84003,0 22.50382,-78.84003,0 22.50382,-83.84003000000001,0 22.50382,-84.89999,0 22.50382,-89.90000000000001,0 21.50615,-89.90000000000001,0 20.50847,-89.90000000000001,0 19.51079,-89.90000000000001,0 18.51312,-89.90000000000001,0 17.51545,-89.90000000000001,0 16.51776,-89.90000000000001,0 15.52009,-89.90000000000001,0 14.52242,-89.90000000000001,0 13.52473,-89.90000000000001,0 12.52706,-89.90000000000001,0 11.52938,-89.90000000000001,0 10.53171,-89.90000000000001,0 9.534033000000001,-89.90000000000001,0 8.536356,-89.90000000000001,0 7.538679000000002,-89.90000000000001,0 7.538679000000002,-88.70438,0 7.538679000000002,-83.70439,0 7.538679000000002,-78.70439,0 7.538679000000002,-73.70439000000002,0 7.538679000000002,-68.70439,0 7.538679000000002,-63.70440000000001,0 7.538679000000002,-58.70439,0 7.538679000000002,-53.7044,0 7.538679000000002,-49.50439000000002,0 7.538679000000002,-44.50439,0 7.538679000000002,-39.50439,0 7.538679000000002,-34.50439,0 7.538679000000002,-33.94561,0 7.538679000000002,-28.94561,0 7.538679000000002,-23.94561,0 7.538679000000002,-18.94561,0 7.538679000000002,-13.94561,0 7.538670000000001,-8.945614000000001,0 7.538670000000001,-3.94561,0 2.656305,-3.94561,0 2.656305,-0.6707075,0 7.538670000000001,-0.6855035,0 7.538670000000001,-0.5623475,0 7.538670000000001,4.437656,0 7.195104000000001,4.365161,0 7.018200000000002,4.374809,0 6.962769000000001,4.389164,0 6.794163000000001,4.336668,0 6.460839000000001,4.322502,0 6.431112,4.345002,0 6.410556,4.313339,0 6.111108,4.272498,0 5.931108000000001,4.340556,0 5.862222000000001,4.388607,0 5.862780000000001,4.434722,0 5.843340000000001,4.410557000000001,0 5.777784,4.454999,0 5.579442,4.659722,0 5.554719,4.71528,0 5.578056,4.718609,0 5.497497,4.805838,0 5.383890000000001,5.111946,0 5.345271,5.329998,0 5.194161,5.502222,0 5.000004,5.857496,0 4.713606,6.164721,0 4.525839,6.305004000000001,0 4.379445,6.368057,0 3.795282,6.432786000000001,0 3.370833000000001,6.385832000000001,0 2.719611,6.36551,0 1.868607,6.273612,0 1.635399,6.21872,0 1.652499,6.250283,0 1.799325,6.281054,0 1.749168,6.456663000000001,0 1.575279,6.679718,0 1.623051,6.757775,0 1.594719,6.80445,0 1.606113,6.895268,0 1.548531,6.995177,0 1.6425,6.995555000000001,0 1.655838,7.532495,0 1.634715,8.357768,0 1.613052,8.374715999999999,0 1.660833,8.496943999999999,0 1.624716,8.548056000000003,0 1.617786,9.049715000000001,0 1.567215,9.17,0 1.416663,9.321111,0 1.38861,9.492217999999999,0 1.364256,9.481346,0 1.337499,9.542501,0 1.373895,9.646667000000003,0 1.355004,9.995282,0 0.7766640000000001,10.37667,0 0.8021880000000001,10.71245,0 0.879444,10.79972,0 0.917973,10.9964,0 0.9677789999999999,10.99584,0 0.948609,11.02389,0 0.978894,11.08027,0 1.117782,11.0275,0 1.058058,11.13139,0 1.125279,11.17528,0 1.156392,11.16277,0 1.13805,11.26527,0 1.264716,11.24611,0 1.274715,11.315,0 1.303605,11.28945,0 1.351107,11.3025,0 1.328337,11.35139,0 1.439721,11.46834,0 1.56861,11.4525,0 1.609164,11.38834,0 1.868607,11.44584,0 2.014722,11.4225,0 2.193894,11.60361,0 2.310552,11.67055,0 2.308608,11.72555,0 2.366946,11.79,0 2.397924,11.89615,0 2.058336,12.35722,0 2.161665,12.41555,0 2.264166,12.4186,0 2.22084,12.59361,0 2.142504,12.69445,0 1.988892,12.73111,0 1.928052,12.70056,0 1.871667,12.60889,0 1.578339,12.63,0 1.117215,13.01111,0 0.9891630000000002,13.04722,0 0.991665,13.37167,0 1.182222,13.31277,0 1.204164,13.36805,0 1.285308,13.34995,0 1.244448,13.39305,0 1.031661,13.4625,0 0.9952830000000001,13.56806,0 0.902214,13.62278,0 0.7744410000000001,13.64416,0 0.7727850000000001,13.68832,0 0.602217,13.70389,0 0.616662,13.77805,0 0.465003,13.91361,0 0.4711050000000001,13.95555,0 0.424161,13.98361,0 0.352503,14.1214,0 0.410832,14.24584,0 0.321111,14.36278,0 0.189441,14.46472,0 0.166662,14.52306,0 0.241389,14.75221,0 0.193338,14.83583,0 0.235044,14.91506,0 0.229995,15.00251,0 0.3966660000000001,14.96056,0 0.512217,15.00083,0 0.694998,14.94221,0 0.974718,14.97861,0 1.312497,15.28666,0 2.628054,15.35055,0 3.020832,15.34195,0 3.032496,15.43306,0 3.523986,15.35816,0 3.539997,15.49722,0 3.726666,15.65277,0 3.823893,15.66527,0 3.885003,15.70917,0 3.924440999999999,15.90416,0 4.000833,15.98971,0 3.981942,16.07638,0 4.069719,16.29389,0 4.200840000000001,16.39389,0 4.198887,16.82083,0 4.21416,16.99361,0 4.2525,16.99445,0 4.245282,19.14666,0 3.331944,18.97639,0 3.114162,19.15639,0 3.196107000000001,19.25861,0 3.206664,19.32611,0 3.277215,19.38972,0 3.210003,19.56527,0 3.233052,19.82028,0 2.967219,19.95,0 2.587779,20.00166,0 2.411667,20.0575,0 2.344167,20.17777,0 2.213055,20.28139,0 2.097504,20.21889,0 1.983339,20.26028,0 1.908891,20.23139000000001,0 1.881108,20.30139,0 1.795833,20.30833,0 1.668051,20.40861,0 1.663056,20.53611,0 1.627497,20.57111,0 1.375002,20.6575,0 1.317222,20.73833,0 1.167498,20.74111,0 1.197144,21.00248,0 1.169667,21.10254,0 -0.008055,21.83055,0 -2.448333,23.48722,0 -4.806108,25.00027,0 -6.662781,26.12916,0 -8.666793,27.29046,0 -8.666667,27.66667,0 -8.667225,28.70944,0 -8.495559,28.79056,0 -8.17695,29.04084,0 -7.995555,29.11639,0 -7.644726000000001,29.385,0 -7.433892,29.39722,0 -7.298334,29.53833,0 -7.123887,29.63695,0 -6.58584,29.56722,0 -6.508611,29.63888,0 -6.474168000000001,29.83306,0 -6.217227000000001,29.79888,0 -6.145839000000001,29.8475,0 -6.002784000000001,29.82945,0 -5.89833,29.85667000000001,0 -5.560002,29.895,0 -5.351949,29.99722,0 -5.133609,30.19806000000001,0 -4.920552,30.50805,0 -4.591944,30.62639,0 -4.396941,30.64833,0 -4.16916,30.76916,0 -4.016943,30.91111,0 -3.718890000000001,30.93806,0 -3.617784,30.97667,0 -3.602214,31.09556,0 -3.724164,31.18861,0 -3.788892000000001,31.14861,0 -3.826107,31.16389,0 -3.79611,31.22167,0 -3.803895,31.33916,0 -3.724722,31.39472,0 -3.84084,31.47583,0 -3.830832,31.6775,0 -3.714714,31.71639,0 -3.511665,31.67334,0 -3.386115,31.72611,0 -2.980557,31.84416,0 -2.938059,32.02888000000001,0 -2.853891,32.08834,0 -2.331108,32.15749,0 -1.213056,32.08971,0 -1.175553,32.12083,0 -1.200834,32.17027,0 -1.295838,32.16306,0 -1.25361,32.21472,0 -1.249722,32.32695,0 -1.196109,32.40472,0 -1.119996,32.41555,0 -1.010286,32.50832,0 -1.300284,32.70055,0 -1.382778,32.72445,0 -1.542501,32.93944,0 -1.487214,32.97917000000001,0 -1.480284,33.06305,0 -1.668051,33.26111,0 -1.666665,33.38333,0 -1.600551,33.50972,0 -1.606662,33.59527,0 -1.65222,33.66277,0 -1.730556,33.70499,0 -1.680552,33.76972,0 -1.710279,33.8825,0 -1.653336,34.085,0 -1.750554,34.34945,0 -1.79334,34.37833,0 -1.686708,34.48551,0 -1.857501,34.61028,0 -1.747215,34.74722,0 -1.961388,34.87029,0 -1.993059,34.92945,0 -2.065833,34.93695,0 -2.180286,35.01222,0 -2.209446,35.08582,0 -2.419164,35.15332,0 -2.516949,35.09832,0 -2.635002,35.09972,0 -2.754999,35.13499,0 -2.885553,35.24277,0 -2.747214,35.11555,0 -2.841660000000001,35.12694,0 -2.909448,35.17638,0 -2.914722,35.27361,0 -2.962782,35.27499,0 -2.946942,35.32917,0 -2.973888,35.44027,0 -3.06639,35.28944,0 -3.332502,35.19139,0 -3.577221,35.22667,0 -3.682503,35.28778,0 -3.813894,35.20027,0 -3.892779,35.21583,0 -3.925278,35.25999,0 -4.353894,35.14611,0 -4.695840000000001,35.20888,0 -5.063337,35.40972,0 -5.171391,35.5375,0 -5.253057,35.57695,0 -5.274999,35.68111,0 -5.318892,35.695,0 -5.345838,35.84166,0 -5.395554,35.91634,0 -5.34528,36.11278,0 -5.350833,36.16,0 -5.432778,36.17417,0 -5.425281000000001,36.07083,0 -5.611662,36.00027,0 -5.693895,36.06167,0 -5.797224,36.07388,0 -5.911389,36.17916,0 -6.037497,36.18027,0 -6.318891000000001,36.53249,0 -6.291666000000001,36.5375,0 -6.239718,36.46193,0 -6.172785000000001,36.51221,0 -6.240834,36.50111,0 -6.230835,36.575,0 -6.392502,36.62639,0 -6.443613,36.71889,0 -6.337224000000001,36.79610000000001,0 -6.339717000000001,36.88944,0 -6.246387,36.89581,0 -6.347214000000002,36.89638,0 -6.364449000000001,36.79528,0 -6.515838,36.97194,0 -6.725835000000001,37.09194,0 -7.080282000000001,37.2175,0 -7.333605,37.21222000000001,0 -7.384167000000002,37.16972,0 -7.431858,37.25319,0 -7.467498,37.49082,0 -7.52445,37.55250000000001,0 -7.447779,37.65777,0 -7.422498000000001,37.75472000000001,0 -7.284996,37.86027,0 -7.254450000000001,37.98749000000001,0 -7.126947,38.00333,0 -7.103889000000001,38.04694,0 -7.006662000000001,38.02805,0 -6.939162,38.17805,0 -6.947784,38.21833000000001,0 -7.087779,38.17443,0 -7.143894,38.27027,0 -7.329717,38.44722,0 -7.248051,38.6236,0 -7.257231,38.72834,0 -7.041663,38.86861,0 -7.050285000000001,38.91082,0 -6.954795,39.02639,0 -7.004997,39.10722,0 -7.142499,39.11111,0 -7.139997000000001,39.17334,0 -7.242786000000002,39.21305,0 -7.235037000000001,39.27697,0 -7.322778,39.38833,0 -7.293609,39.46777,0 -7.38,39.49611,0 -7.390836000000001,39.53277,0 -7.505838000000001,39.60583,0 -7.532505000000001,39.66942000000001,0 -7.515000000000001,39.66611,0 -7.337502,39.64388000000001,0 -7.266942,39.67194,0 -7.017219,39.675,0 -6.98319,39.81694,0 -6.913611,39.86472,0 -6.863895,40.01555,0 -6.934725000000001,40.1075,0 -7.004718,40.12611,0 -7.027218,40.18777,0 -7.005834,40.23083,0 -6.873894,40.26332,0 -6.778611,40.36279,0 -6.848334000000001,40.44333,0 -6.790833,40.51805,0 -6.838893000000001,40.575,0 -6.797781,40.65777,0 -6.829164,40.75528,0 -6.803892,40.87416000000001,0 -6.931665000000001,41.01806,0 -6.808338,41.04056000000001,0 -6.754446000000002,41.10249,0 -6.764445,41.13083,0 -6.64389,41.24471000000001,0 -6.496947,41.26445,0 -6.381945,41.355,0 -6.388056,41.38167,0 -6.318333000000001,41.38722,0 -6.32916,41.41527,0 -6.187221,41.57972,0 -6.303609,41.66333,0 -6.442218000000002,41.68582,0 -6.497496000000001,41.65749,0 -6.551388,41.6911,0 -6.562503,41.74529,0 -6.508341,41.87389,0 -6.568335,41.88721,0 -6.544719,41.93722,0 -6.581664,41.96749,0 -6.716943,41.93666,0 -6.788052000000001,41.99167,0 -6.837498,41.94722,0 -6.982848,41.97386,0 -7.056108,41.95111,0 -7.133607,41.99499,0 -7.182783,41.97944,0 -7.200558,41.88361,0 -7.37028,41.85139,0 -7.420284,41.81332,0 -7.493058,41.87306000000001,0 -7.607781,41.83111000000001,0 -7.591662,41.87972,0 -7.692498,41.905,0 -7.880004,41.85277,0 -7.892217,41.92666,0 -8.014725,41.83388,0 -8.140554,41.80917000000001,0 -8.218610999999999,41.91361000000001,0 -8.084997000000001,42.02027,0 -8.079723,42.05416,0 -8.121942000000001,42.07944,0 -8.185554,42.06471,0 -8.201223000000002,42.15273,0 -8.327232,42.08528,0 -8.606942999999999,42.05916,0 -8.745011999999999,41.95250000000001,0 -8.865278999999999,41.87554,0 -8.882225999999999,41.89004,0 -8.898606000000001,42.10806,0 -8.967249000000001,42.46196,0 -9.041391000000001,42.52888,0 -9.027782999999999,42.57972,0 -9.086112,42.58138000000001,0 -9.020286000000001,42.70472,0 -8.85834,42.82444,0 -8.903610000000001,42.82833,0 -8.960283,42.77333,0 -9.058887,42.78472,0 -9.086112,42.73528,0 -9.140283,42.795,0 -9.101394000000001,42.82332,0 -9.14139,42.85695,0 -9.123606000000002,42.90555,0 -9.185003999999999,42.9525,0 -9.181107000000001,42.91555,0 -9.226944,42.93805,0 -9.273339,42.88694,0 -9.293336999999999,42.92249,0 -9.252504000000002,43.0061,0 -9.2925,43.04804000000001,0 -9.25389,43.05029,0 -9.218889000000001,43.10806,0 -9.165555,43.10722,0 -9.164448,43.13777,0 -9.203894999999999,43.12554,0 -9.209448,43.15277,0 -9.169731000000001,43.18583,0 -9.056664,43.18332,0 -9.010557,43.23195,0 -8.91639,43.23222,0 -8.980281,43.28139,0 -8.848053,43.32222,0 -8.843994,43.36485,0 -8.782785000000001,43.30306,0 -8.701389000000001,43.28722,0 -8.539443000000002,43.30972,0 -8.398611000000001,43.38582,0 -8.38611,43.33944,0 -8.360001000000001,43.34027,0 -8.329725,43.40388,0 -8.215551,43.33055,0 -8.213058,43.4025,0 -8.13195,43.42334,0 -8.317214999999999,43.44305,0 -8.194445999999999,43.46611,0 -8.166942000000001,43.50777,0 -8.339724,43.45693,0 -8.331660000000001,43.50889,0 -8.295282,43.52639,0 -8.319447,43.56221,0 -8.253333,43.55806,0 -8.089173000000001,43.66194,0 -8.049447000000001,43.63666,0 -8.060841,43.70361,0 -7.991946000000001,43.70555,0 -7.898058000000001,43.76416,0 -7.856109000000001,43.765,0 -7.850556,43.71472,0 -7.894449000000001,43.66638,0 -7.812513,43.71458,0 -7.683606,43.79277,0 -7.658892,43.76972,0 -7.693767,43.73127,0 -7.629714000000001,43.74778,0 -7.595838,43.66611000000001,0 -7.551945,43.72861000000001,0 -7.46361,43.72639,0 -7.473609000000001,43.70555,0 -7.365834,43.67611,0 -7.248888000000001,43.57916,0 -7.264998,43.54222,0 -7.231113000000001,43.57028,0 -7.045281,43.55943,0 -7.044822000000001,43.4904,0 -7.002225000000001,43.53027,0 -7.019442,43.56417,0 -6.943338,43.57777,0 -6.476949,43.55111,0 -6.235839,43.59499,0 -6.094998000000001,43.56194000000001,0 -5.954715,43.58055,0 -5.844717000000001,43.65527,0 -5.677785,43.54388,0 -5.398056,43.5525,0 -5.381109,43.5261,0 -5.418612,43.49694,0 -5.372667,43.51369,0 -5.365278,43.53917,0 -5.288607000000001,43.53388,0 -5.207499,43.47778,0 -5.121108,43.48916000000001,0 -4.514553,43.39634,0 -4.410279,43.40193,0 -4.371111,43.37028,0 -4.383891000000001,43.39083,0 -4.351941,43.40388,0 -4.21695,43.39389,0 -4.051395,43.4425,0 -4.025286,43.40554,0 -4.016943,43.44777,0 -3.989448,43.43027000000001,0 -3.944718,43.46778,0 -3.810285,43.49249,0 -3.766392000000001,43.46999,0 -3.83166,43.43943,0 -3.813057,43.40361,0 -3.719718,43.47166,0 -3.585555,43.51027,0 -3.430557,43.46444,0 -3.482784000000001,43.42999,0 -3.462498000000001,43.39389,0 -3.450834,43.43527,0 -3.422502,43.40972,0 -3.325284,43.41833,0 -3.020832,43.32861,0 -3.034998,43.37472,0 -2.941389,43.43556,0 -2.752497,43.45499,0 -2.687778,43.38139,0 -2.638053,43.4125,0 -2.295558,43.2961,0 -2.19834,43.31305,0 -2.130552,43.28916,0 -1.780839,43.38888,0 -1.619874,43.43629,0 -1.530639,43.54382,0 -1.480338,43.68816000000001,0 -1.338453,44.26832,0 -1.234737,45.14534,0 -1.204236,45.52971,0 -1.267407,45.71291,0 -1.522926,46.18522000000001,0 -1.778436,46.44073,0 -2.428839,46.72721,0 -2.297205,46.97499,0 -2.529495,47.25373,0 -3.253446,47.27309,0 -3.493476,47.61377,0 -3.865130999999999,47.76862,0 -4.337442,47.77636,0 -4.732326000000001,48.0164,0 -5.142699,48.41902,0 -5.173668000000001,48.4887,0 -4.058703,48.77519,0 -3.137301,48.91456,0 -2.966967,48.88359000000001,0 -2.804364,48.67454000000002,0 -2.66499,48.58163,0 -2.479167,48.67454000000002,0 -2.308815,48.71325,0 -1.828764,48.71325,0 -1.604223,48.66679000000001,0 -1.720368,49.23201,0 -1.882962,49.44108,0 -1.975878,49.74305,0 -1.251927,49.72369000000001,0 -1.18224,49.43721,0 -0.44667,49.35977,0 -0.1989,49.29783000000001,0 -0.013077,49.33654000000001,0 0.017901,49.46043000000001,0 0.149526,49.71595,0 0.575379,49.8708,0 1.016721,49.94823000000001,0 1.396116,50.10309,0 1.52001,50.25794,0 1.547109,50.88124000000001,0 1.77939,50.97416,0 2.437524,51.08255,0 3.072447,51.32258000000001,0 3.428613,51.53938,0 3.978351,51.97298,0 4.319037,52.18204,0 4.551318,52.49174,0 4.698432000000001,53.07247,0 5.023638,53.33572000000001,0 5.495949,53.46734,0 6.262488000000001,53.52154,0 6.866424,53.69189,0 7.499043000000001,53.79065000000001,0 7.499043000000001,57.95962999999999,0 7.013538000000001,57.97367,0 6.595425,58.0511,0 5.681772000000001,58.48469,0 5.45724,58.68601000000001,0 5.108805,59.16606000000001,0 4.930722,60.25007,0 4.651983,60.7456,0 4.590036,61.01659999999999,0 4.799088000000001,61.86831,0 5.039118,62.19351,0 7.516629,63.25726,0 7.516629,64.90000000000001,0 7.516629,69.90000000000001,0 7.516629,74.89999,0 7.516629,79.90000000000001,0 7.516629,84.90001,0 7.516629,89.90000000000001,0 8.456004,89.90000000000001,0 9.395379,89.90000000000001,0 10.33475,89.90000000000001,0 11.27413,89.90000000000001,0 12.2135,89.90000000000001,0 13.15287,89.90000000000001,0 14.09225,89.90000000000001,0 15.03162,89.90000000000001,0 15.97099,89.90000000000001,0 16.91036,89.90000000000001,0 17.84974,89.90000000000001,0 18.78911,89.90000000000001,0 19.72849,89.90000000000001,0 20.66786,89.90000000000001,0 21.60724,89.90000000000001,0 22.54661000000001,89.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +02:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(2);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0200</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +28.09417,69.93859000000001,0 28.32332,69.88749,0 28.37972,69.8275,0 29.1275,69.68582000000001,0 29.29972,69.48027,0 28.81916,69.23193000000001,0 28.78694,69.11915999999999,0 28.95734,69.05162,0 28.43195,68.89694,0 28.47917,68.8736,0 28.73778,68.87582,0 28.82056,68.84444000000001,0 28.73861,68.73416,0 28.4575,68.53194000000001,0 28.69334,68.19749,0 29.35695,68.08249000000001,0 29.69167,67.81527,0 30.02861,67.69472,0 29.98972,67.67194000000001,0 29.97778,67.57249,0 29.93279,67.51389,0 29.54611,67.31055000000001,0 29.08305,66.9586,0 29.09333,66.85138000000001,0 29.35027,66.68249,0 29.42056,66.58498,0 29.54833,66.49554000000001,0 29.56792,66.42856999999999,0 29.63861,66.32249,0 29.91028,66.12694000000001,0 29.97695,65.9811,0 30.09667,65.83333,0 30.13528000000001,65.70359999999999,0 30.12388,65.66499,0 30.02444,65.69110000000001,0 29.72194,65.63805000000001,0 29.85971,65.56610000000001,0 29.74611,65.51443,0 29.7475,65.34193000000001,0 29.59638,65.24887,0 29.85361,65.21612,0 29.87028,65.18747999999999,0 29.82333,65.14888999999999,0 29.87388,65.12082,0 29.62889,65.06305,0 29.60666,65.00166,0 29.63667,64.92805,0 29.80471,64.79389000000001,0 30.14083,64.77415000000001,0 30.12055000000001,64.73054999999999,0 30.19713000000001,64.68622000000001,0 30.19917,64.65028,0 29.97500000000001,64.58110000000001,0 30.00967,64.51286,0 30.08722,64.48305000000001,0 30.05222,64.40470999999999,0 30.15611000000001,64.35415999999999,0 30.38972,64.30943000000001,0 30.45416000000001,64.25694,0 30.56084,64.24442999999999,0 30.57722,64.21581999999999,0 30.54805,64.17471000000001,0 30.61444,64.08665000000001,0 30.59528,64.04694000000001,0 30.345,63.90972000000001,0 30.25584,63.81778,0 29.99671,63.74802,0 30.48166000000001,63.47861,0 30.79639,63.40194,0 30.9625,63.30972000000001,0 31.21972,63.22306,0 31.28222,63.10917,0 31.515,62.98693,0 31.58893,62.91440999999999,0 31.4675,62.80639,0 31.25972000000001,62.50777000000002,0 30.94777,62.31138000000001,0 30.64972,62.195,0 29.99138,61.72943,0 29.83222,61.65305000000002,0 29.80916,61.60833,0 29.52555,61.48277,0 29.48639,61.42611000000002,0 29.21361,61.25332,0 28.81277,61.10334,0 28.63888,60.96611,0 28.52916000000001,60.95499,0 28.38888,60.88667000000002,0 27.80783,60.5464,0 28.01583000000001,59.4786,0 28.19389,59.37582,0 28.17035,59.30978,0 27.91254,59.27182,0 27.72499,58.98777,0 27.4936,58.88193,0 27.42611,58.81361,0 27.5425,58.41389,0 27.53916,58.36166,0 27.46471,58.29777,0 27.47416,58.21388000000001,0 27.55417,58.13276000000001,0 27.67721,58.07417000000002,0 27.64222,58.02083,0 27.69722,58.00082,0 27.68388,57.92638,0 27.82306,57.87388,0 27.75306000000001,57.83916,0 27.54583,57.81777,0 27.57277,57.80277000000002,0 27.51639,57.77333,0 27.52666,57.72276,0 27.41306,57.69055,0 27.40583000000001,57.61361000000001,0 27.34806,57.59554000000001,0 27.37205,57.53564,0 27.54722,57.53638000000001,0 27.52722,57.4386,0 27.86111,57.30222000000001,0 27.84082,57.16361,0 27.70138,57.11916000000001,0 27.7775,57.06582,0 27.64,56.84805,0 27.81555,56.87833000000001,0 27.93527,56.82721,0 27.8861,56.74722,0 28.005,56.69137,0 28.03639,56.59054,0 28.1525,56.55443000000001,0 28.11306,56.50555000000001,0 28.18972,56.44055,0 28.18444,56.37471000000001,0 28.23777,56.2711,0 28.16801,56.15015,0 28.3125,56.04999,0 28.3875,56.0961,0 28.64611,56.09361000000001,0 28.75416,55.95388000000001,0 28.84694,55.94249000000001,0 28.86944,55.98055,0 29.05943,56.03278,0 29.41222,55.95777,0 29.46443,55.91109,0 29.40693,55.89582999999999,0 29.35916,55.78943,0 29.49388,55.68888,0 29.71916,55.78249000000002,0 29.82306,55.77528,0 29.92833,55.85167000000001,0 30.14055,55.82804000000002,0 30.22611,55.8661,0 30.48971,55.79999,0 30.49416,55.76388000000001,0 30.61249,55.72027,0 30.60889,55.68138,0 30.72694,55.65639,0 30.75666,55.59665,0 30.91888,55.60833000000001,0 30.93999,55.49583,0 30.90055,55.45915999999999,0 30.93943,55.39944000000001,0 30.81361,55.29333000000001,0 31.00472,55.14332,0 30.98722,55.11166000000002,0 31.02971,55.04332,0 30.92499,55.03333,0 30.94749,54.96915,0 30.83388,54.94415,0 30.845,54.8811,0 30.78,54.79694000000001,0 31.005,54.71167,0 31.02472,54.65222,0 31.18527,54.64222,0 31.18443,54.60609,0 31.0825,54.50556,0 31.1986,54.46093000000001,0 31.23194,54.45665,0 31.31167,54.35083,0 31.33166,54.2375,0 31.54416,54.14333,0 31.76167,54.10333,0 31.79333,54.05305000000001,0 31.84416,54.06444000000001,0 31.86083,53.96333,0 31.76423000000001,53.80262,0 31.90222,53.77777,0 32.11971,53.81999,0 32.32944,53.76222000000001,0 32.37694,53.71888,0 32.45166,53.72638,0 32.51056,53.68361,0 32.41471,53.64638,0 32.44389,53.57194000000001,0 32.5975,53.4911,0 32.67054,53.49722,0 32.66082,53.46666,0 32.7411,53.46305000000001,0 32.73693,53.34249,0 32.46249,53.30832,0 32.49833,53.27832,0 32.43999,53.20888000000001,0 32.29889,53.13193,0 32.22554,53.13333,0 32.22332,53.10555,0 31.93054,53.09554,0 31.80111,53.12583,0 31.77555,53.19415000000002,0 31.61611,53.21832,0 31.4225,53.20888000000001,0 31.37483,53.12193000000001,0 31.38638,53.09916,0 31.2664,53.02193,0 31.35638,52.97805000000001,0 31.41527,52.89305,0 31.53695,52.8561,0 31.58083,52.80304,0 31.595,52.73804,0 31.5,52.68999,0 31.65332,52.55305,0 31.58167,52.55083,0 31.59527,52.53221,0 31.56472000000001,52.51583,0 31.60889,52.49138,0 31.60166,52.40610000000001,0 31.62972,52.37054,0 31.58666,52.31888,0 31.71694,52.26167000000001,0 31.70389,52.1986,0 31.7825,52.16582,0 31.78388,52.10805000000001,0 31.89777,52.09610000000001,0 31.92638,52.03667,0 32.12444,52.04388000000001,0 32.22499,52.07944,0 32.33554,52.1572,0 32.34055000000001,52.22137,0 32.38638,52.25472,0 32.35833,52.30999,0 32.39415,52.33526,0 32.92361,52.24722,0 33.20471,52.37972,0 33.44887,52.34361,0 33.49778,52.36666000000002,0 33.53528,52.29333000000001,0 33.69638,52.36166999999999,0 33.83056,52.36472000000001,0 34.09778,52.15194,0 34.07306,52.06028000000001,0 34.12804,51.98972000000001,0 34.10943,51.95972,0 34.16945,51.95999,0 34.25777,51.87944000000001,0 34.38082,51.84499,0 34.44054,51.76721,0 34.40554,51.7186,0 34.10277,51.67443,0 34.10111,51.64749,0 34.15972,51.6286,0 34.23417,51.54666,0 34.23916,51.42777000000001,0 34.20361,51.40666,0 34.31499,51.35111,0 34.27388,51.28555,0 34.20892,51.25919,0 34.30526,51.23082000000001,0 34.38221,51.26361000000001,0 34.63361,51.24222,0 34.66999,51.17944,0 34.73499,51.17249,0 35.07381,51.22149,0 35.11852,51.20481,0 35.17075,51.0548,0 35.37189,51.04144,0 35.35855,51.01088000000001,0 35.29716000000001,50.99979,0 35.30577000000001,50.96033,0 35.36133,50.92614,0 35.3605,50.87226,0 35.39773,50.80308,0 35.45663,50.77279,0 35.45829,50.69417,0 35.48857,50.67585,0 35.39355,50.63808,0 35.44106,50.51198,0 35.5672,50.45306,0 35.58305,50.38305,0 35.64471,50.35416,0 35.72887,50.35332,0 35.73915,50.38333,0 35.85028,50.4286,0 36.13917,50.42527,0 36.29749,50.28444,0 36.41276,50.32166,0 36.56333000000001,50.27639,0 36.56249,50.22249,0 36.64028,50.21249000000001,0 36.7011,50.26693000000001,0 36.88444,50.33583000000001,0 37.13054,50.34833,0 37.31583,50.42804,0 37.4586,50.43971000000001,0 37.49444,50.35139000000001,0 37.62360000000001,50.30527,0 37.60527,50.21666,0 37.73304,50.08777000000001,0 37.92527,50.02471,0 38.01306,49.90138000000001,0 38.18526,49.9511,0 38.18415,50.06554000000001,0 38.30777,50.07388,0 38.3761,49.98749,0 38.49889,49.95193,0 38.68665,49.97887,0 38.69609,49.92749,0 38.88916,49.86805,0 38.94526,49.8075,0 39.06916,49.81777,0 39.18373,49.88041,0 39.27361,49.75194,0 39.58083,49.73082,0 39.63972,49.61555,0 39.79695000000001,49.55305,0 39.92167,49.56555,0 40.005,49.61444000000001,0 40.13977,49.60105000000001,0 40.14111,49.55916,0 40.07444,49.52694,0 40.05305,49.47833000000001,0 40.17972000000001,49.32695,0 40.16694,49.2486,0 39.96638,49.12388,0 39.93888,49.05499,0 39.69915,49.02277,0 39.69444,48.97305,0 39.76721,48.90388,0 39.97055,48.85999000000001,0 40.03027,48.8961,0 40.07667,48.87500000000001,0 39.98526,48.79139,0 39.80389,48.81777000000001,0 39.72721,48.76221,0 39.65693,48.61667,0 39.66966,48.58764000000002,0 39.85333,48.55471000000001,0 39.86694,48.50027000000001,0 39.83833,48.48388000000001,0 39.92152,48.36839,0 39.83805,48.3125,0 39.91277,48.27777,0 40.0125,48.28472,0 40.00249,48.24304000000001,0 39.93277,48.21332,0 39.84832,48.11306,0 39.86859,48.04084000000001,0 39.77609,48.02639000000001,0 39.81998,47.9697,0 39.79639,47.85722,0 39.72388,47.81833,0 39.34526,47.85304,0 39.13638,47.83388999999999,0 39.03817,47.8658,0 38.83833,47.85278000000001,0 38.75055,47.68387000000001,0 38.69137,47.69415000000002,0 38.60582,47.63803,0 38.33942,47.60139000000001,0 38.28083,47.5061,0 38.28555,47.3961,0 38.20804,47.31916,0 38.31916,47.27833,0 38.2161,47.19471,0 38.23582,47.10943,0 37.64138,46.73358000000001,0 36.67405,45.41802,0 36.54507,45.17941,0 36.53862,44.90856,0 36.5541,41.28566,0 36.61834,41.37498,0 36.85693,41.34721,0 37.02333,41.26749,0 37.04332,41.17555000000001,0 37.15228,41.14491,0 37.29499,41.14389,0 37.53721,41.02805,0 37.60777,41.04777,0 37.68748,41.13722,0 37.70638,41.11222,0 37.78944,41.11555,0 37.79361000000001,41.04554,0 37.89249,40.98277,0 38.2775,40.95056,0 38.36139,40.90943000000001,0 38.5236,40.92083,0 38.62554,40.97305,0 38.70999,40.95,0 38.81611,41.00999,0 39.16304,41.0825,0 39.2786,41.04972,0 39.4211,41.10944,0 39.64611,41.00166,0 39.73333,41.01388000000001,0 39.87721,40.95389,0 39.99776,40.97582,0 40.12859,40.91639,0 40.45804,41.0425,0 40.54332,41.02916,0 40.83305,41.19498,0 40.93111,41.18748,0 41.3875,41.37304000000001,0 41.53155,41.52388,0 41.65167,41.48277,0 41.72277,41.49554,0 41.72306,41.47055,0 41.83331,41.42832,0 41.96804,41.51915,0 42.05028,41.49056,0 42.18249,41.5075,0 42.47221,41.43333,0 42.51081,41.44333,0 42.59777,41.58305,0 42.82749,41.58500000000001,0 42.78832,41.50861,0 42.84722,41.47305,0 42.89278,41.50695,0 43.03972,41.37416,0 43.20777,41.30693,0 43.1236,41.25527,0 43.19943000000001,41.25527,0 43.23693,41.17638,0 43.3497,41.20193,0 43.42582,41.18527,0 43.47471,41.13999,0 43.46077,41.11296,0 43.47915,41.02528,0 43.59944,40.99249,0 43.67165,40.92806,0 43.6861,40.84195,0 43.75193,40.74,0 43.75361,40.67582,0 43.65304,40.53138,0 43.55415,40.4711,0 43.62027,40.41944,0 43.59417,40.33749,0 43.68,40.26443,0 43.67221,40.21972,0 43.72165,40.16332,0 43.65778,40.13971,0 43.65749,40.1086,0 43.93389000000001,40.01361,0 44.28,40.0461,0 44.34722,40.02389,0 44.48778,39.96277,0 44.59082,39.83305,0 44.69555,39.78998,0 44.77886,39.70638000000001,0 44.81304,39.63081,0 44.60582,39.78054000000001,0 44.47108,39.69886,0 44.4161,39.42527,0 44.31554,39.3875,0 44.20054,39.41694,0 44.03416,39.38499,0 44.09581,39.29138,0 44.09833,39.20638,0 44.21583,39.14083,0 44.18833,39.08306,0 44.20499,39.04165,0 44.15943,39.00221,0 44.20805,38.89581,0 44.30332000000001,38.83444,0 44.26082,38.72165,0 44.28276,38.69777,0 44.26333,38.66639,0 44.31388,38.63777,0 44.30693000000001,38.38833,0 44.38081,38.36971,0 44.43471000000001,38.39555,0 44.48416,38.34554,0 44.40471000000001,38.27166,0 44.38778,38.16583,0 44.26416,38.01416,0 44.22398000000001,37.89915,0 44.39333,37.86304,0 44.41916,37.81778,0 44.45193,37.81778,0 44.45609,37.77471,0 44.52693,37.78555,0 44.61805,37.72776,0 44.55804,37.64803,0 44.6136,37.60277,0 44.58859,37.44026,0 44.72499,37.38361,0 44.81527,37.30526,0 44.82193,37.2711,0 44.77332,37.22748,0 44.78733,37.1497,0 44.63806,37.18748,0 44.43638000000001,37.06304,0 44.35111,37.04833,0 44.31721,36.97055,0 44.25527,36.98666,0 44.19333,37.10027,0 44.26777,37.1675,0 44.26166,37.24194,0 44.11638,37.31638,0 44.01416,37.32054,0 43.91138,37.22415,0 43.83444,37.20499,0 43.79110000000001,37.23194,0 43.49471,37.24972,0 43.35666,37.33083,0 43.27916,37.3161,0 43.14971,37.37777,0 42.95554,37.32222,0 42.79083,37.38472,0 42.72666,37.35555000000001,0 42.58165,37.15277,0 42.35562,37.10693,0 42.37723,37.06467000000001,0 41.83528,36.59888,0 41.40304,36.52555,0 41.29027000000001,36.35555,0 41.25499000000001,36.05499,0 41.37806,35.83861,0 41.38388,35.62527,0 41.27777,35.49554,0 41.21527,35.22471,0 41.22915,34.78832,0 41.00388,34.41944,0 38.7947,33.37759,0 39.08601,32.50151,0 38.98602,32.47767,0 39.04366,32.30405,0 39.26,32.35555,0 39.30111,32.23638,0 39.19675,32.15495,0 39.005,32.00556,0 37.00527,31.50555,0 38.00139,30.50417,0 37.6675,30.33639,0 37.50277,30.00222000000001,0 36.74361,29.86472,0 36.50055,29.50277000000001,0 36.07,29.18889,0 34.96139,29.36083,0 34.85527,29.03861,0 34.84254,28.88452,0 34.78054,28.67083,0 34.80778,28.53639,0 34.64528,28.19361,0 34.65972,28.16445,0 34.62749000000001,28.17388,0 34.5714,28.09056,0 34.48494,27.99542,0 34.49483,27.93732,0 34.6346,27.87186,0 34.88189,27.89598,0 35.26789,27.63060000000001,0 35.71421,27.09985,0 36.49828,26.06245,0 36.47416,25.62217,0 36.54049,25.54376,0 36.83,25.51963,0 36.96872,25.39901,0 37.01093,24.97079,0 37.51406,24.19318,0 37.51406,19.32831,0 38.23912000000001,19.32831,0 38.24853,18.39601,0 38.51692,18.23592,0 38.64405,18.06641,0 38.60069,17.99488,0 38.45861,17.87111,0 38.37029,17.66611,0 38.27667,17.60694000000001,0 38.25777,17.5325,0 38.18583,17.56222,0 38.13361,17.5486,0 38.11138,17.48111,0 38.09555,17.54389,0 38.05584,17.55194,0 37.91722,17.4475,0 37.81888,17.48139,0 37.73584,17.37666,0 37.5125,17.32333,0 37.52058,17.1958,0 37.42329,17.03422,0 37.30693000000001,17.06111,0 37.15194,17.01555,0 36.99582,17.07389,0 37.01805,17.00361,0 36.98693,16.94306,0 37.02278,16.85056,0 36.98055,16.70611,0 36.91222,16.64111,0 36.89638,16.51777,0 36.95694,16.42528,0 36.97305,16.26944,0 36.91584,16.215,0 36.76361,15.80806,0 36.69777,15.75305,0 36.62611,15.48945,0 36.64777000000001,15.44971,0 36.61054,15.43721,0 36.54277,15.23472,0 36.44329,15.14995,0 36.54282,14.26206,0 36.44639,13.95695,0 36.48693,13.83944,0 36.3975,13.56778,0 36.24860000000001,13.36805,0 36.15944,13.04167,0 36.16972,12.98416,0 36.13278,12.92333,0 36.16528,12.87527,0 36.1427,12.70692,0 36.00638,12.72416,0 35.69944,12.66583,0 35.69528,12.61222,0 35.64916,12.59417,0 35.43249,12.20055,0 35.3811,12.16111,0 35.34555,12.03277,0 35.27111,11.97555,0 35.23555,11.90083,0 35.09693,11.82694,0 35.05806,11.72056,0 35.09361,11.55778,0 34.96388,11.25695,0 35.0075,11.19833,0 34.93333,10.95666,0 34.97861,10.91527,0 34.97527,10.86445,0 34.86861,10.78582,0 34.86443,10.735,0 34.80861,10.72945,0 34.77333,10.68694,0 34.77333,10.74584,0 34.59444,10.88779,0 34.43972,10.78445,0 34.28611,10.55416,0 34.34777,10.23195,0 34.32306,10.11721,0 34.23138000000001,10.03027,0 34.08583,9.553058999999999,0 34.13499,8.955270000000001,0 34.1228,8.584649000000001,0 33.97888,8.441387000000002,0 33.85249,8.425836,0 33.77599,8.364941999999999,0 33.69333,8.386389000000001,0 33.62555,8.470556,0 33.57804,8.449443000000001,0 33.50777,8.480834,0 33.38777,8.43777,0 33.2625,8.462502000000001,0 33.20444,8.433890000000002,0 33.16082,8.360001000000001,0 33.15834,8.291392999999999,0 33.20861,8.238049999999999,0 33.16527,8.204715,0 33.18643,8.136979999999999,0 33.03028,7.995248000000001,0 32.99683,7.887221000000001,0 33.05389,7.788888,0 33.16806,7.796393000000001,0 33.33944,7.713612000000001,0 33.47027,7.743339000000001,0 33.55138,7.686387,0 33.71139,7.660277,0 33.78889,7.56639,0 33.89278,7.518888,0 34.03083000000001,7.375004,0 34.03,7.240832,0 34.13361,7.158887,0 34.1825,7.163054000000001,0 34.2025,7.052219000000001,0 34.2947,6.966432000000001,0 34.46999,6.916112,0 34.53,6.743888,0 34.6375,6.727221,0 34.76277,6.605837000000002,0 34.79777,6.444719,0 34.85722,6.349445000000001,0 34.86055,6.26166,0 34.98782,6.013944,0 34.98639,5.878052000000001,0 35.10582,5.692779000000001,0 35.10388,5.632496000000001,0 35.28999,5.507774,0 35.26972,5.441112,0 35.31138,5.366942,0 35.35638,5.350275,0 35.44749,5.429448,0 35.50472,5.431391,0 35.605,5.373054,0 35.68443,5.391387,0 35.83583,5.318613,0 35.79556,5.251391,0 35.83083,5.153894000000001,0 35.77389,5.075279,0 35.77443,4.798611,0 35.94056,4.622499,0 35.50888,4.616738,0 34.38819,4.609683,0 33.99666,4.222782,0 33.51694,3.752217,0 33.18388,3.766113,0 33.01666,3.888612,0 32.89804,3.811949,0 32.71944,3.755834,0 32.40445,3.743613,0 32.19443,3.611042,0 32.19333,3.511394999999999,0 32.09361,3.532500000000001,0 32.0639,3.59028,0 31.95111,3.593889,0 31.93332,3.687785,0 31.79945,3.825557,0 31.70194,3.713058000000001,0 31.57389,3.691664,0 31.54083,3.653611999999999,0 31.28806,3.794714,0 31.17667000000001,3.795281,0 30.97471,3.691943,0 30.85882000000001,3.493394,0 30.91721,3.521942,0 30.935,3.411665,0 30.83,3.275558,0 30.7625,3.055004,0 30.85555,2.957499,0 30.87806000000001,2.843612,0 30.81667,2.769165,0 30.72972,2.448053,0 30.82667,2.442221,0 30.82527,2.399165,0 30.88027,2.337497,0 30.91416,2.339442,0 30.97084,2.411945,0 31.07027,2.335005,0 31.04972,2.303891,0 31.12528,2.278053,0 31.19694,2.305835,0 31.2025,2.229164,0 31.2992,2.162853,0 31.30277,2.121389,0 31.03666000000001,1.765556,0 30.68806,1.493891,0 30.45611,1.212785,0 30.34861,1.196108,0 30.33221,1.151387,0 30.27805,1.168893,0 30.23611,1.134161,0 30.21861000000001,0.9963894000000001,0 30.14583,0.8983344000000001,0 29.96055000000001,0.8255515,0 29.93833,0.6344455,0 29.9625,0.4872144,0 29.85833,0.3666595,0 29.81389,0.1586065,0 29.77332,0.1722145,0 29.71666000000002,0.07221546,0 29.735,-0.00083754,0 29.64861,-0.4397225,0 29.67195,-0.5722206,0 29.63805,-0.5933346000000001,0 29.61833,-0.7333385,0 29.62832,-0.8888946000000001,0 29.57833,-0.9000005,0 29.56639,-1.005841,0 29.59694,-1.385839,0 29.68332,-1.380556,0 29.73416,-1.341946,0 29.80306,-1.365553,0 29.84139,-1.320283,0 29.9075,-1.464166,0 29.97500000000001,-1.464445,0 30.17056,-1.339723,0 30.18472,-1.275004,0 30.22916,-1.266661,0 30.34566,-1.129168,0 30.36,-1.060003,0 30.48222,-1.063333,0 30.45333000000001,-1.095553,0 30.46945,-1.158337,0 30.51221,-1.164448,0 30.56166,-1.326943,0 30.73917,-1.436949,0 30.74888,-1.52055,0 30.84027,-1.670554,0 30.81111,-1.700551,0 30.835,-1.800559,0 30.80832,-1.929448,0 30.89417,-2.076112,0 30.82917,-2.357785,0 30.78222,-2.380554,0 30.69972,-2.347497,0 30.6525,-2.396665000000001,0 30.57332,-2.399167,0 30.43305,-2.647783,0 30.45721,-2.685835,0 30.52222,-2.657215,0 30.44028000000001,-2.747215,0 30.4175,-2.861946999999999,0 30.485,-2.947222,0 30.56333,-2.89305,0 30.66611,-2.976391,0 30.84477,-2.976012,0 30.79555,-3.048607,0 30.85222,-3.145285,0 30.835,-3.256948,0 30.65166,-3.332503,0 30.6222,-3.370122,0 30.66361,-3.386665,0 30.66222,-3.418893999999999,0 30.44916,-3.547215000000001,0 30.39139,-3.711106000000001,0 30.40084,-3.786112,0 30.32111,-3.78666,0 30.22806,-3.928051,0 30.17222,-4.086667,0 30.07277,-4.166667,0 30.02611,-4.269447,0 29.85777,-4.371111,0 29.81277,-4.360284,0 29.75222000000001,-4.461112,0 29.42334,-4.447504,0 29.42834,-4.552218,0 29.34139,-4.797783,0 29.34916000000001,-4.939732,0 29.63166,-5.721949,0 29.49777,-5.979726000000001,0 29.55029,-6.295285,0 29.66111,-6.455556,0 29.73250000000001,-6.627501,0 30.08611,-6.863607,0 30.31138,-7.136947000000001,0 30.40695,-7.356393,0 30.45583,-7.580277000000001,0 30.65445,-7.886943,0 30.77123,-8.192242,0 30.90138,-8.460001,0 31.04111,-8.590284000000001,0 31.13277,-8.615557000000001,0 31.22055,-8.576668,0 31.28527,-8.623333000000001,0 31.37556,-8.582229999999999,0 31.39806,-8.629443,0 31.45945,-8.629443,0 31.56332,-8.698608,0 31.57305,-8.818605,0 31.68361000000001,-8.908894,0 31.73861,-8.918056,0 31.78553000000001,-8.885061,0 31.95611,-8.930835999999999,0 31.93416,-9.018891,0 31.97972,-9.071109999999999,0 32.05611,-9.040832999999999,0 32.16,-9.063333999999999,0 32.24945000000001,-9.131949000000001,0 32.42556,-9.129718000000002,0 32.54527,-9.26667,0 32.74195,-9.281665,0 32.77166,-9.335557,0 32.9404,-9.405072000000001,0 32.99777,-9.373339,0 33.12583,-9.490285,0 33.1925,-9.509167,0 33.31444000000001,-9.485550999999999,0 33.39388,-9.538055999999999,0 33.43777,-9.615834,0 33.50889,-9.621945,0 33.585,-9.584164,0 33.68361,-9.610552,0 33.75583,-9.582777999999999,0 33.91961,-9.707652,0 33.95305000000001,-9.673335000000002,0 33.95721,-9.526671,0 34.03944,-9.482779000000001,0 34.32527,-9.73278,0 34.33805,-9.7875,0 34.5225,-10.01084,0 34.52389,-10.1075,0 34.58277,-10.27,0 34.57583,-10.51667,0 34.67611000000001,-10.74306,0 34.64305,-10.92194,0 34.59305,-11.01668,0 34.615,-11.10945,0 34.71027,-11.20111,0 34.76361000000001,-11.345,0 34.80555,-11.33222,0 34.89054,-11.38306,0 34.95972,-11.48666,0 34.96673,-11.57211,0 35.49416,-11.57918,0 35.51611,-11.61084,0 35.57638,-11.60667,0 35.6525,-11.58473,0 35.70389,-11.48778,0 35.7725,-11.47528,0 35.83833,-11.41472,0 35.94306,-11.43473,0 35.96556,-11.50084,0 36.16305,-11.57778,0 36.18972,-11.70666,0 36.49001,-11.68111,0 36.56195,-11.74028,0 36.67777,-11.71472,0 36.82972,-11.56861,0 36.90445,-11.59473,0 37.02584,-11.56721,0 37.12583,-11.67333,0 37.28611,-11.70418,0 37.34722,-11.68667,0 37.46204,-11.72732,0 37.52666,-11.6625,0 37.66306,-11.63833,0 37.81582,-11.53833,0 37.80972,-11.48,0 37.86861,-11.32666,0 38.09669,-11.25633,0 38.27972,-11.28834,0 38.37777,-11.37722,0 38.49171,-11.41531,0 38.65527,-11.2811,0 38.77389,-11.26166,0 38.89777,-11.17222,0 39.13444,-11.14195,0 39.26805,-11.16806,0 39.50833,-10.99528,0 39.76694000000001,-10.92056,0 39.89832000000001,-10.83166,0 40.00666,-10.80555,0 40.23611,-10.60278,0 40.3425,-10.56722,0 40.43681,-10.47817,0 40.47834,-10.4825,0 40.47277,-10.50778,0 40.52111,-10.48416,0 40.50388,-10.56444,0 40.55972,-10.58666,0 40.57332,-10.65,0 40.63945,-10.67166,0 40.64145,-10.69888,0 40.70627,-10.84757,0 40.71345000000001,-11.17438,0 40.47889,-11.40556,0 40.42471,-11.55416,0 40.45805,-11.60583,0 40.42417,-11.64973,0 40.47556,-11.82029,0 40.51611,-11.84333,0 40.49342,-11.91995,0 40.60549,-11.96501,0 40.63672,-12.37548,0 40.61845,-12.65416,0 40.57444,-12.64306,0 40.57139,-12.69806,0 40.61639,-12.71334,0 40.64194,-12.7814,0 40.55889,-12.785,0 40.56638,-12.81889,0 40.52361,-12.81778,0 40.55639,-12.85445,0 40.53416,-12.90416,0 40.47666,-12.88417,0 40.41,-12.96194,0 40.48972,-13.0275,0 40.50166000000001,-12.94445,0 40.59722,-12.97334,0 40.56,-13.07861,0 40.56806,-13.26222,0 40.52833,-13.3025,0 40.56638,-13.285,0 40.56528,-13.3664,0 40.59111,-13.37473,0 40.52584,-13.51528,0 40.60056,-13.57084,0 40.53749,-13.64528,0 40.64999000000001,-14.01694,0 40.63833000000001,-14.07918,0 40.59666,-14.08722,0 40.62277,-14.12555,0 40.53054,-14.17166,0 40.55584,-14.23,0 40.59389,-14.21084,0 40.59,-14.26834,0 40.64917,-14.1975,0 40.70361,-14.18222,0 40.74472,-14.27473,0 40.72861,-14.34306,0 40.66916,-14.36723,0 40.6375,-14.33973,0 40.65472000000001,-14.44195,0 40.6264,-14.445,0 40.62777,-14.56944,0 40.6661,-14.5375,0 40.68999,-14.43584,0 40.81166000000001,-14.40779,0 40.82804,-14.49944,0 40.76083,-14.53805,0 40.82999,-14.53028,0 40.8064,-14.61417,0 40.84472,-14.68611,0 40.84195,-14.795,0 40.72278,-14.89279,0 40.66888,-14.9075,0 40.64527,-14.85806,0 40.66305,-14.92111,0 40.74722,-14.92834,0 40.78027,-14.98667,0 40.66,-14.98721,0 40.70888000000001,-15.07834,0 40.64527,-15.08279,0 40.60167,-15.13389,0 40.52888,-15.13278,0 40.51445,-15.18723,0 40.57805,-15.20278,0 40.62722,-15.155,0 40.68472,-15.24973,0 40.57167,-15.45111,0 40.58777,-15.47973,0 40.54333,-15.53417,0 40.49666000000001,-15.52917,0 40.36028,-15.72333,0 40.17528,-15.92,0 40.06472,-15.99361,0 40.08749,-16.00944,0 40.13027,-15.9675,0 40.13582,-15.9925,0 39.98191,-16.23304,0 39.90667000000001,-16.4168,0 39.85722,-16.43055,0 39.7965,-16.46608,0 39.71716,-16.50974,0 39.28388,-16.76834,0 39.15639,-16.88083,0 39.12749,-16.87042,0 39.12917,-16.90084,0 39.08361,-16.91612,0 39.12749,-16.93305,0 39.08583,-16.99168,0 39.00154,-16.99003,0 38.81833,-17.05778,0 38.70972,-17.06361,0 38.66444,-17.02916,0 38.67889,-17.0775,0 38.60472,-17.09916,0 38.58806,-17.07723,0 38.54056,-17.12584,0 38.50638000000001,-17.10973,0 38.4975,-17.14417,0 38.15666,-17.2914,0 38.12889,-17.17834,0 38.11694,-17.20889,0 38.07889,-17.19278,0 38.13083,-17.25361,0 38.11138,-17.31139,0 37.87222,-17.37639,0 37.52949,-17.56545,0 37.52949,-19.06546,0 37.52949,-24.06546,0 37.52949,-29.06546000000001,0 37.52949,-34.06546,0 37.52949,-39.06546,0 37.52949,-44.06546000000001,0 37.52949,-49.06545000000001,0 37.52949,-52.36546,0 37.52949,-57.36545,0 37.52949,-62.36546000000001,0 37.52949,-67.36546000000001,0 37.52949,-69.90000000000001,0 37.52949,-74.90000000000001,0 37.52949,-79.90000000000001,0 37.52949,-84.89999,0 37.52949,-89.90000000000001,0 36.59039,-89.90000000000001,0 35.65129,-89.90000000000001,0 34.71218,-89.90000000000001,0 33.77307,-89.90000000000001,0 32.83397,-89.90000000000001,0 31.89487,-89.90000000000001,0 30.95576,-89.90000000000001,0 30.01666,-89.90000000000001,0 29.07755,-89.90000000000001,0 28.13845,-89.90000000000001,0 27.19934,-89.90000000000001,0 26.26024,-89.90000000000001,0 25.32114,-89.90000000000001,0 24.38203,-89.90000000000001,0 23.44293,-89.90000000000001,0 22.50382,-89.90000000000001,0 22.50382,-84.89999,0 22.50382,-83.84003000000001,0 22.50382,-78.84003,0 22.50382,-73.84003,0 22.50382,-70.54002,0 22.50382,-65.54002,0 22.50382,-60.54002000000001,0 22.50382,-55.54003,0 22.50382,-50.54002,0 22.50382,-45.54003,0 22.50382,-40.54002,0 22.50382,-35.54002000000001,0 22.50382,-34.04004,0 22.15389,-34.09251,0 22.11167,-34.14861000000001,0 22.13416,-34.19112,0 21.94972,-34.22501,0 21.90166,-34.33611,0 21.8025,-34.38305,0 21.65556,-34.39056,0 21.52832,-34.3525,0 21.29472000000001,-34.43389,0 21.06277,-34.36389,0 20.9425,-34.36028,0 20.80861000000001,-34.39723,0 20.85666000000001,-34.42083,0 20.85222,-34.46305,0 20.53444,-34.45583,0 20.46029,-34.48223,0 20.21972,-34.67973,0 20.08972,-34.72639,0 20.055,-34.7525,0 20.05722,-34.805,0 19.99861,-34.82195,0 19.85777,-34.7525,0 19.65166,-34.77584,0 19.51639,-34.67556,0 19.51416,-34.67084,0 19.38945,-34.60196,0 19.29611,-34.61972,0 19.36333,-34.53778,0 19.3575,-34.50029,0 19.28194,-34.40973,0 19.21082,-34.42861,0 19.11222,-34.40334,0 19.08334,-34.36084000000001,0 19.13722,-34.29834,0 19.08334,-34.34639,0 18.81778,-34.37778000000001,0 18.80833,-34.29723,0 18.84527,-34.245,0 18.81417,-34.19028,0 18.85527,-34.15611,0 18.79332,-34.08667,0 18.60195,-34.07362,0 18.48805,-34.09778,0 18.43583,-34.16889,0 18.47888,-34.23445000000001,0 18.46666,-34.3289,0 18.49011,-34.34823,0 18.42027,-34.32306,0 18.36305,-34.18723,0 18.30972,-34.14918,0 18.35555,-34.05834,0 18.31027,-34.0325,0 18.38,-33.91195,0 18.4825,-33.89473,0 18.44111,-33.70667,0 18.31527,-33.56834,0 18.29471,-33.46611,0 18.14805,-33.35612,0 18.09,-33.22501,0 17.96722,-33.13472,0 17.95556,-33.10305,0 18.00167,-33.09334,0 18.12306,-33.20001,0 18.03721,-33.10196,0 18.03444,-33.0325,0 17.98333,-33.00028,0 17.91389,-33.04805,0 17.89305,-33.03111,0 17.85714,-32.91527000000001,0 17.87694,-32.86084,0 17.84527,-32.82584,0 17.8675,-32.81196000000001,0 17.87666,-32.80612,0 17.9025,-32.73333,0 17.96694,-32.69862,0 18.05361,-32.77612,0 18.13175,-32.77878000000001,0 18.29222,-32.62445,0 18.335,-32.47834,0 18.31611,-32.33501,0 18.35027,-32.28806,0 18.2786,-31.89278,0 18.18139,-31.67473,0 17.90944,-31.35973,0 17.61195,-30.92972,0 17.53944,-30.7575,0 17.27889,-30.34667,0 17.08445,-29.83584000000002,0 17.00332,-29.52445,0 16.84444,-29.22139,0 16.81528,-29.085,0 16.73277,-29.01889,0 16.67083,-28.90584,0 16.60611,-28.87195,0 16.56833,-28.72389,0 16.46972,-28.63028,0 16.48959,-28.57819,0 16.44195,-28.61834,0 16.35445,-28.56278,0 16.02778,-28.24473,0 15.75277,-28.03055,0 15.67639,-27.9339,0 15.67,-27.87139000000001,0 15.52861,-27.7275,0 15.53333,-27.66139,0 15.46666,-27.54445000000001,0 15.29417,-27.32250000000001,0 15.23471,-26.96667,0 15.15777,-26.90278,0 15.08389,-26.69861,0 15.07972,-26.65,0 15.13472,-26.62889,0 15.14194,-26.67778,0 15.17111,-26.5964,0 15.08792,-26.40098,0 14.96833,-26.33972,0 14.93861,-26.13945,0 14.97805,-26.12667,0 14.98,-26.0575,0 14.90333,-25.91055,0 14.91361,-25.84472,0 14.83777,-25.76194,0 14.88222,-25.5675,0 14.80083,-25.285,0 14.85778,-25.08723,0 14.83207,-24.99944,0 14.79277,-24.94361,0 14.77695,-24.79945,0 14.59916,-24.56555,0 14.61278,-24.45639,0 14.46333,-24.10917,0 14.45611,-23.9675,0 14.50945,-23.90473,0 14.51471,-23.83583,0 14.48445,-23.75639,0 14.49611,-23.58834,0 14.43444,-23.41778,0 14.47,-23.34668,0 14.49194,-23.37584,0 14.49833,-23.33056,0 14.45388,-23.1464,0 14.41056,-22.96723,0 14.43833,-22.87778,0 14.46278,-23.00055,0 14.52584,-22.91112,0 14.52485,-22.69206,0 14.51139,-22.55279,0 14.39167,-22.29,0 14.27777,-22.11028,0 13.94916,-21.78194,0 13.97028,-21.73167,0 13.87972,-21.59944,0 13.85361,-21.49139,0 13.4,-20.85583,0 13.35277,-20.64667,0 13.25083,-20.46584,0 13.175,-20.18084,0 13.05111,-20.06778,0 12.93167,-19.79583,0 12.70722,-19.41778,0 12.67,-19.295,0 12.54222,-19.06778,0 12.46416,-19.00418,0 12.46083,-18.92805,0 12.29611,-18.7061,0 12.02083,-18.47111,0 11.9489,-18.25639,0 11.80472,-18.08195,0 11.7211,-17.655,0 11.75278,-17.25484,0 11.82584,-17.26639000000001,0 11.92861,-17.18278,0 12.08471,-17.13528,0 12.13139,-17.16112,0 12.16778,-17.14389,0 12.24611,-17.22721,0 12.42139,-17.20361,0 12.45083,-17.25417,0 12.55722,-17.24333,0 12.81611,-17.10889,0 12.88666,-17.03361,0 13.16056,-16.95278,0 13.25166,-16.98528,0 13.37333,-16.96945,0 13.47445,-17.01194,0 13.52611,-17.07778,0 13.52471,-17.12695,0 13.98556,-17.42584,0 14.17694,-17.42028,0 14.20417,-17.3831,0 18.00055,-17.38617,0 18.45154,-17.38984,0 18.63722,-17.64473,0 18.75638,-17.74778,0 18.91584,-17.81555,0 19.24222,-17.80583,0 19.34333,-17.8475,0 19.55916,-17.8675,0 19.66166,-17.83778,0 19.76195,-17.8989,0 19.81194,-17.85833,0 19.92056,-17.85723,0 20.03583,-17.90028,0 20.33806,-17.85805,0 20.5475,-17.98168,0 20.75195,-17.99611000000001,0 20.82,-18.03861,0 20.95417,-17.9675,0 21.16389,-17.93139,0 21.24194,-17.93889,0 21.37936,-18.01489,0 23.47611,-17.62584,0 23.20167,-17.47973,0 22.48778,-16.77417,0 22.13888,-16.4925,0 22.12111,-16.34112,0 22.00015,-16.17166,0 21.99834,-13.00416,0 24.02055,-13.00639,0 23.99777,-12.93723,0 23.89389,-12.83223,0 23.88611,-12.77111,0 23.9475,-12.53723,0 24.05222,-12.38528,0 24.035,-12.26417,0 23.975,-12.19917,0 23.98416,-11.88222,0 24.01555,-11.81417,0 23.96583,-11.655,0 24.03028,-11.52639,0 24.03083,-11.45306,0 24.08445,-11.40278,0 24.02527,-11.28056,0 24.0225,-10.96889,0 23.98621,-10.87046,0 23.8639,-11.02722,0 23.47639,-10.95834,0 23.36806,-10.98278,0 23.21861000000001,-11.07639,0 23.085,-11.09668,0 22.94277,-11.095,0 22.86361,-11.04917,0 22.71667,-11.09334,0 22.52972,-11.03778,0 22.48,-11.13333,0 22.25111,-11.2525,0 22.24611,-11.07305,0 22.19361,-10.99417,0 22.16361,-10.86139,0 22.31944,-10.75973,0 22.30806,-10.61278,0 22.30334,-10.53779,0 22.26416,-10.50055,0 22.31361,-10.37,0 22.20722,-10.15416,0 22.1625,-9.930555,0 22.02138,-9.848053,0 21.985,-9.755838000000001,0 21.95639,-9.763335,0 21.87472,-9.665001999999999,0 21.79056,-9.405559,0 21.85472,-9.229445999999999,0 21.84777,-8.949718000000001,0 21.94,-8.496109000000001,0 21.91611,-8.308612,0 21.87666,-8.279163000000001,0 21.85861,-8.157781,0 21.75416,-7.999444000000001,0 21.77305,-7.776109,0 21.85111,-7.591662,0 21.85388,-7.425559000000001,0 21.81555,-7.316110000000001,0 21.78295,-7.280848,0 20.53861,-7.284483,0 20.54306,-7.124445,0 20.62975,-6.913881,0 20.3325,-6.914449,0 20.31138,-6.99472,0 19.96277,-6.995556000000001,0 19.9325,-6.842503,0 19.73667,-6.575553000000002,0 19.68847,-6.187500000000002,0 19.84027,-6.031107000000001,0 20.01974,-5.942214,0 20.05305,-5.865004,0 20.11222,-5.834448,0 20.18,-5.684445000000001,0 20.14305,-5.40361,0 20.19639,-5.273613,0 20.19166,-5.159161,0 20.05445,-4.769164,0 20.06222,-4.595832,0 20.11889,-4.500837,0 20.03333,-4.300551,0 20.03889,-4.278051,0 20.12861,-4.278889,0 20.17777,-4.33278,0 20.22445,-4.335003,0 20.26277,-4.395555,0 20.3375,-4.397779,0 20.37444,-4.295278,0 20.50944,-4.254166000000001,0 20.66917,-4.151665,0 20.65611000000001,-3.307221999999999,0 20.82945,-2.848887,0 20.95417,-2.692783,0 20.95916,-2.588608,0 21.00686,-2.48896,0 21.24555,-2.512783000000001,0 21.44083,-2.480554,0 21.46972,-2.335284,0 21.82688,-2.529217,0 21.89883,-2.490597,0 21.95194,-2.415555,0 22.0486,-2.370835,0 22.13972,-2.276389,0 22.19055000000001,-2.313334,0 22.19861,-2.447784,0 22.25139,-2.466946,0 22.28722000000001,-2.445553,0 22.16638,-1.942219,0 22.185,-1.903339,0 22.50972000000001,-1.958887,0 22.62556,-1.801108,0 22.67972,-1.806661,0 22.87028,-1.88416,0 22.93666,-1.962216,0 23.03277,-1.928053,0 23.10944,-1.945837,0 23.22111,-2.085552,0 23.5,-2.009998,0 23.69389,-2.018889,0 23.69805,-1.931392,0 23.77472,-1.917505,0 23.78306,-1.806661,0 23.83777,-1.765,0 23.98527,-1.769167,0 24.03527,-1.73278,0 24.4214,-1.786798,0 24.37583,-1.538893,0 24.30388,-1.393057,0 24.10583,-1.384723,0 23.94889,-1.306666,0 23.77278,-1.179163,0 23.72972,-1.08334,0 23.52667,-0.9758345000000001,0 23.35611,-0.8072196,0 23.48555,-0.6988865,0 23.62945,-0.6358325,0 23.46778000000001,-0.4386065,0 23.26917,-0.4952795999999999,0 23.21778,-0.4249986,0 22.97027,-0.4063866,0 23.13,-0.2808366,0 23.29389,-0.3172145,0 23.41361,-0.2336045,0 22.9936,0.3750025000000001,0 22.93528,0.5708334000000001,0 22.86139,0.6711115,0 22.81472000000001,0.9963894000000001,0 22.62416,1.361663,0 22.4339,1.489715,0 22.28639,1.503332,0 22.31111,1.557503,0 22.47277,1.693889,0 22.51667,1.916387,0 22.58861,2.048606,0 22.57222000000001,2.110283,0 22.6475,2.109447,0 22.69972,2.034162,0 22.75722,2.010284,0 22.86084,2.045835,0 22.97583,2.184165,0 23.09027,2.093894,0 23.34389,2.251395000000001,0 23.65138,2.192498,0 23.6275,2.330837,0 23.4225,2.451941,0 23.35945,2.523887,0 23.12666,2.502503,0 23.03806,2.558051,0 22.96,2.672216,0 23.00389,2.757221,0 22.98472,2.857779,0 23.08195,3.147218,0 23.00111,3.161664000000001,0 22.86417000000001,3.011947999999999,0 22.81472000000001,2.996109,0 22.77972,3.063887,0 22.79639,3.259449,0 22.64333,3.325284,0 22.62361,3.388886,0 22.67834,3.453893,0 22.92861,3.548051000000001,0 23.03750000000001,3.547503,0 23.17111,3.678056000000001,0 23.36971,3.615839,0 23.48695,3.738338,0 23.515,3.825836,0 23.39943,3.835278000000001,0 23.35889,3.809448,0 23.14722,3.896108,0 23.03555,3.860559,0 22.99083,3.883337,0 22.85806,3.858604999999999,0 22.76555,3.905,0 22.75861,3.991391000000001,0 22.68722,3.998050999999999,0 22.56111,4.101389,0 22.45972,4.137497,0 22.4536,4.144580000000001,0 22.53861,4.218894,0 22.54111,4.28111,0 22.60972,4.358889,0 22.59445,4.470551,0 22.69027000000001,4.483611,0 22.77584,4.706666,0 22.85582000000001,4.706945,0 22.89584,4.821110000000001,0 22.97500000000001,4.831388,0 23.0225,4.743053,0 23.10000000000001,4.70528,0 23.1825,4.727222,0 23.26833,4.627214,0 23.38556000000001,4.587777,0 23.42639,4.591943,0 23.44166,4.653612,0 23.58445,4.734449,0 23.83027,4.82084,0 23.94222,4.810004,0 23.95417,4.873337,0 23.98195,4.855275,0 24.10555,4.920002,0 24.15194,4.900004,0 24.2275,4.960278000000001,0 24.25611,4.929443,0 24.28611,4.994999,0 24.40056,5.030001,0 24.355,5.054715,0 24.39417,5.115554,0 24.42555,5.065838,0 24.44750000000001,5.105834,0 24.52556,5.077214000000001,0 24.66111,4.964445,0 24.65056,4.92111,0 24.73445,4.910841,0 24.95888,4.987215000000001,0 25.07027,4.945832,0 25.13361,5.017778000000001,0 25.17583,4.995278,0 25.30832,5.032503,0 25.34945,5.144714,0 25.31056,5.19111,0 25.37555,5.294717,0 25.36195,5.314715000000001,0 25.53833,5.350554,0 25.54222,5.381387,0 25.74916,5.241941,0 25.79083,5.263893,0 25.82583,5.195556,0 25.87583,5.217497,0 25.9,5.166945,0 25.97333,5.231663000000001,0 26.05694,5.192504,0 26.13277,5.257215,0 26.29222,5.141394,0 26.38639,5.145552,0 26.48889,5.04639,0 26.73166,5.092784,0 26.86777,5.029163,0 26.93777,5.141106,0 27.08417,5.204159,0 27.39361,5.089167,0 27.45527,5.016392,0 27.45806,5.083613,0 27.3,5.22611,0 27.25472,5.331392,0 27.23584,5.428610000000001,0 27.28166,5.578613,0 27.23389,5.588334,0 27.24084,5.647779000000001,0 27.14278,5.771943,0 27.04917,5.789997,0 27.00111,5.862501,0 26.94972,5.846949000000001,0 26.91027,5.899446000000001,0 26.83945,5.898338,0 26.80861,5.980004,0 26.55445,6.033608,0 26.51416,6.109722,0 26.4375,6.077772,0 26.52361000000001,6.173613,0 26.52361000000001,6.221106,0 26.47611,6.224714,0 26.45944,6.294446000000001,0 26.39527,6.307785,0 26.30778000000002,6.383051,0 26.31361,6.457769000000001,0 26.28916,6.468894,0 26.40628,6.63525,0 26.10083,6.841106,0 26.04834,6.998336,0 25.81971,7.108335000000001,0 25.81389,7.147214000000001,0 25.59389,7.216388,0 25.53139,7.277786,0 25.465,7.280001000000001,0 25.36555,7.348338,0 25.33111,7.425279,0 25.20695,7.497503,0 25.18389000000001,7.583336000000002,0 25.29611,7.65,0 25.25833,7.840836,0 25.18611,7.901946,0 25.08027,7.897500000000001,0 24.97333,7.992774,0 24.94277,8.083888999999999,0 24.86555,8.165285000000001,0 24.66,8.218610999999999,0 24.54583,8.204715,0 24.46,8.271386,0 24.36111,8.247221,0 24.17722,8.316108,0 24.14027,8.375832000000001,0 24.16584,8.477783000000001,0 24.26917,8.583055999999999,0 24.23027,8.624995999999999,0 24.25333,8.691110999999999,0 23.82389,8.727785000000001,0 23.73916,8.705276,0 23.63305,8.736947000000001,0 23.51777,8.714159,0 23.49833,8.77833,0 23.58167,8.906112,0 23.58306,8.988605,0 23.56528000000001,9.005281999999999,0 23.50361,8.959716000000002,0 23.44861,9.025002000000001,0 23.49277,9.174168,0 23.55639,9.181664,0 23.65222,9.279999,0 23.66445,9.440837999999999,0 23.63138,9.449442,0 23.62333,9.535275,0 23.69471,9.671659999999999,0 23.66916000000001,9.866942,0 23.30389,10.45916,0 23.00945,10.69861,0 22.86651,10.92244,0 22.9425,11.0925,0 22.97083,11.28306,0 22.93084,11.41583,0 22.79111,11.40167,0 22.77555,11.46222,0 22.64195,11.51611,0 22.56167,11.62166,0 22.64082000000001,12.07111,0 22.56361,12.07471,0 22.47666,12.03416,0 22.50139,12.17695,0 22.43278,12.40333,0 22.38389,12.46527,0 22.46694,12.62166,0 22.22333,12.74722,0 22.14056,12.65527,0 21.95277,12.64389,0 21.82777,12.7975,0 21.94334,13.05361,0 22.02055,13.13779,0 22.15972,13.19027,0 22.29416000000001,13.35861,0 22.23056,13.47889,0 22.22472,13.57195,0 22.15361,13.63444,0 22.13861,13.72806,0 22.08444,13.77916,0 22.19833,13.9225,0 22.23833,13.96833,0 22.555,14.12555,0 22.55527,14.23194,0 22.44389,14.27222,0 22.43444,14.30972,0 22.46972,14.34472,0 22.44556,14.47972,0 22.38138,14.52306,0 22.38416,14.55416,0 22.47,14.62945,0 22.7025,14.69139,0 22.67056,14.85945,0 22.7389,14.90278,0 22.75332,14.97694,0 22.84916,15.07834,0 22.93583,15.11611,0 22.99834,15.35639,0 22.92305,15.48722,0 22.93722,15.56194,0 23.11816,15.71029,0 23.35694,15.68166,0 23.60889,15.75833,0 23.9996,15.69871,0 23.99981,18.7543,0 24.00275,19.49907,0 25.00083,19.99916,0 25.00142,21.99969,0 24.9989,26.99969000000002,0 24.99777,29.24888,0 24.8775,29.50638,0 24.88694,29.66389,0 24.8125,29.78445,0 24.83472,29.87666,0 24.70667,30.16861,0 24.78082,30.33388,0 24.92611000000001,30.50611,0 25.01916,30.7875,0 24.86945,31.15639,0 24.86833,31.375,0 25.04222,31.49972,0 25.06972,31.58167,0 25.15166,31.64694,0 25.02555,31.84139,0 25.03611,31.92445,0 24.97389,31.97,0 24.7275,32.02693,0 24.54305,31.98527,0 24.09472,32.00111,0 23.96778,32.0675,0 23.98639,32.09833,0 23.72639,32.17472,0 23.50166,32.17139,0 23.30778,32.21861,0 23.30916,32.15861,0 23.20889,32.27555,0 23.08139,32.33277,0 23.09306,32.42667,0 23.13333,32.39666,0 23.14555,32.46694000000001,0 23.09833,32.52166,0 23.12222,32.62666,0 22.50257,32.79314,0 22.50257,36.37666,0 22.3926,36.43942,0 22.33602000000001,36.51816,0 22.35234,36.64503,0 22.32878,36.7139,0 22.23996,36.84983,0 22.12033,36.90784,0 22.13302,37.00752,0 22.04059,37.01296,0 21.95903,36.9749,0 21.94816,36.8444,0 21.98484,36.77306,0 21.79948,36.68655,0 21.70063,36.72363,0 21.53381,37.02636,0 21.57297,37.21142,0 21.67267,37.33829,0 21.54217,37.54129,0 21.36454,37.6464,0 21.30836,37.62784,0 20.99752,37.69217,0 20.80903,37.64777,0 20.68738,37.72452,0 20.61781,37.81678,0 20.51087,38.09272000000001,0 20.45741,38.14892,0 20.37856,38.13986,0 20.31669,38.18789,0 20.39216,38.36551000000001,0 20.44472,38.36641,0 20.48369,38.32835,0 20.51631,38.3501,0 20.54984,38.68631000000001,0 20.63178,38.84898,0 20.72928,38.86393,0 20.73834,38.90834,0 20.49728,39.20377,0 20.2381,39.1222,0 20.13298,39.19515,0 20.07227,39.36417,0 19.87652,39.44391,0 19.80221,39.58348,0 19.36993,39.84311,0 19.37265,39.8712,0 19.57928,39.90293,0 19.95083,39.78693,0 19.98255,39.68724,0 20.01002,39.6912,0 20.19916,39.64472,0 20.26444,39.66916000000001,0 20.32083,39.7336,0 20.29138,39.80667,0 20.38138,39.78722,0 20.41445,39.82443,0 20.3161,39.99334,0 20.39416,39.99917000000001,0 20.45,40.07249,0 20.67195,40.09805,0 20.67861,40.18527,0 20.72111,40.22305,0 20.71306,40.27833,0 20.78972,40.36083,0 20.78889,40.42832,0 20.84889,40.47916,0 20.94556,40.46694,0 21.04138,40.56195,0 21.05194,40.67666000000001,0 20.96194,40.77166,0 20.98349,40.8559,0 21.34305,40.87193,0 21.41972,40.91917,0 21.6,40.87277,0 21.69445,40.93805,0 21.79222,40.93278,0 21.80416,40.97777,0 21.91195,41.04943,0 21.91611,41.09527,0 22.06667,41.15834,0 22.13111,41.12500000000001,0 22.25667,41.17139,0 22.33417,41.12722,0 22.58555,41.11944,0 22.6725,41.18777,0 22.72917,41.14471,0 22.77222,41.32444,0 22.96083,41.3586,0 22.9525,41.63444,0 23.03277,41.72834000000001,0 22.96193,41.77861,0 22.89584,41.88527,0 22.86444,42.02722,0 22.67528,42.0675,0 22.5211,42.14861,0 22.36527,42.32389,0 22.45639,42.34027,0 22.55805,42.47916,0 22.43861,42.57471000000001,0 22.46111,42.6925,0 22.50139,42.73916,0 22.44222,42.82167,0 22.525,42.87861,0 22.74556000000001,42.89361,0 22.79083,42.98527,0 22.96639,43.10167,0 23.00721,43.19611,0 22.90444000000002,43.23082,0 22.76666,43.38582,0 22.54333000000001,43.4725,0 22.49555000000001,43.57028,0 22.49555000000001,43.64555,0 22.4275,43.68861,0 22.35945,43.81694,0 22.41695,44.00695,0 22.6225,44.07083,0 22.61667,44.17278,0 22.68144,44.2247,0 22.6875,44.27777,0 22.54972,44.32306,0 22.45734,44.47435,0 22.54611,44.4875,0 22.59027,44.54916,0 22.69958,44.52252,0 22.76489000000001,44.55901,0 22.47916,44.71027,0 22.31361,44.66417,0 22.17694,44.48194000000001,0 22.14638,44.47916,0 22.08444,44.50306,0 21.99389,44.63556,0 21.64944,44.65778,0 21.58834,44.76332000000001,0 21.40040000000001,44.78082,0 21.36611,44.86444,0 21.55861,44.89611,0 21.54083,44.93861,0 21.36695,45.00832,0 21.44722000000001,45.035,0 21.46028,45.10277,0 21.51277,45.12333,0 21.51361,45.15111000000001,0 21.41666,45.21472000000001,0 21.2675,45.23278,0 21.16306,45.30972,0 21.04889,45.31639,0 20.78305,45.48472,0 20.82361,45.53249,0 20.76666,45.60999,0 20.79888,45.65667,0 20.80139,45.76416,0 20.71334,45.74389,0 20.59167,45.89417000000001,0 20.37805,45.97806,0 20.26102,46.11486,0 20.34027,46.15944,0 20.47805,46.14972,0 20.48694,46.18110999999999,0 20.62472,46.13027000000002,0 20.72056,46.16555,0 20.72056,46.19166,0 20.76305,46.19917000000001,0 20.75332,46.23888000000001,0 20.82028,46.27472,0 21.05944,46.23917,0 21.17667,46.29556,0 21.19721,46.39139,0 21.29222,46.42027000000001,0 21.26056,46.49528000000001,0 21.32666,46.61999,0 21.40500000000001,46.62111000000001,0 21.44194,46.68194,0 21.52194,46.71277000000001,0 21.48806,46.75278000000001,0 21.51972,46.81527,0 21.60527,46.85722000000001,0 21.59972000000001,46.91805000000001,0 21.67167,46.95138,0 21.69583,47.00082,0 21.65111,47.02972000000001,0 21.79084,47.12028000000001,0 21.9225,47.35443,0 22.00972,47.38083,0 22.01527000000001,47.51445,0 22.18639,47.59861000000001,0 22.27694,47.72861000000001,0 22.42139,47.74389,0 22.45444,47.79639000000001,0 22.65361,47.77638000000001,0 22.77444,47.8364,0 22.77084,47.87943,0 22.8948,47.95454,0 22.85056,47.98083,0 22.8825,48.04694,0 22.7975,48.11472,0 22.62116,48.10163,0 22.57528,48.19443,0 22.50445,48.24749000000001,0 22.37861,48.24554,0 22.31888,48.31416000000001,0 22.31944,48.36166,0 22.26028,48.36082,0 22.26889,48.40721,0 22.15145,48.41192000000001,0 22.15888,48.58027,0 22.32888,48.68694000000001,0 22.34805,48.76416,0 22.38917,48.79722,0 22.37694,48.85194,0 22.46749,48.97860000000001,0 22.53721,48.99554,0 22.55805,49.07944,0 22.59805,49.09109999999999,0 22.67417,49.04416000000001,0 22.76916,49.05332,0 22.88607,49.00292,0 22.87611,49.09,0 22.73472,49.17722000000001,0 22.70028,49.16555,0 22.76027,49.29528,0 22.73833,49.39027,0 22.69222,49.42333000000001,0 22.65611,49.51972,0 22.68607,49.5771,0 23.14611,49.98305,0 23.67694,50.32444,0 23.70388,50.37833,0 23.99166,50.40721,0 24.02611,50.49388,0 24.13277,50.54445000000001,0 24.07972,50.70499000000001,0 23.9525,50.79472,0 23.97277,50.82805000000001,0 24.11472,50.83138000000001,0 24.14471,50.85804,0 23.96638,50.94887,0 23.86277,51.09833,0 23.86694,51.14861,0 23.63611,51.30110000000001,0 23.69306,51.40471,0 23.60463,51.52769,0 23.56138,51.5425,0 23.53499,51.65359000000001,0 23.55556,51.74943,0 23.62944,51.80471,0 23.59472,51.8336,0 23.61173,51.91397,0 23.67277,51.99138,0 23.63861,52.07944,0 23.51472,52.1236,0 23.49304,52.17416000000001,0 23.20638,52.22526,0 23.1654,52.28227,0 23.39722,52.51445,0 23.59722,52.59639,0 23.75389,52.61583000000001,0 23.94083,52.73220000000001,0 23.91527,52.91888,0 23.93305,53.01417,0 23.87805000000001,53.07555,0 23.91055,53.16721,0 23.81472,53.25444,0 23.67472,53.4911,0 23.50404,53.94704,0 23.47528000000001,53.99110000000001,0 23.51777,54.03028,0 23.48444,54.13833000000001,0 23.35028000000001,54.23888000000001,0 23.14249,54.31054,0 23.06611,54.30804,0 23.06166,54.35167,0 22.98749000000001,54.38917000000001,0 22.85611,54.4086,0 22.78588,54.36384,0 22.69444,54.47332000000001,0 22.69832,54.58943000000001,0 22.74055,54.72777000000001,0 22.84249,54.77749,0 22.86916,54.82333,0 22.83499000000001,54.90166,0 22.75555,54.92055000000001,0 22.77056,54.93888,0 22.73222000000001,54.96249,0 22.64082000000001,54.96638,0 22.5992,55.07113,0 22.19722000000001,55.06166,0 22.06194,55.02749000000001,0 22.00972,55.08778,0 21.86305,55.09194,0 21.64027,55.18472,0 21.50389,55.19332,0 21.38128,55.28709,0 21.26394,55.24899,0 20.98481,55.27655,0 20.94284,55.2872,0 21.30838,59.61593000000001,0 19.36751,59.65187,0 19.33158,60.19100000000001,0 20.69736,63.31795000000001,0 24.16601,65.8115,0 23.9486,66.10138000000001,0 23.9125,66.14833,0 23.72222,66.20027,0 23.63917,66.43053999999999,0 23.72527,66.50082999999999,0 23.87861,66.55722,0 23.89,66.74303999999999,0 24.00777,66.80055,0 23.57167,67.15666,0 23.61028,67.20166,0 23.58445,67.22387000000001,0 23.60555,67.25943,0 23.77333,67.31444000000001,0 23.74472,67.36834,0 23.76778,67.41611,0 23.54917,67.45332999999999,0 23.48695,67.43804,0 23.42889,67.47584000000001,0 23.47195,67.55638,0 23.55361,67.58749,0 23.48083,67.72665000000001,0 23.48333,67.86305,0 23.64472,67.91306,0 23.65695,67.95081999999999,0 23.39445,68.0425,0 23.30722,68.13916,0 23.15138,68.12277,0 23.14222,68.23000999999999,0 22.8236,68.38805000000001,0 22.33471,68.47275999999999,0 22.05056,68.47915999999999,0 21.89722,68.56999,0 21.7575,68.57693,0 21.62306,68.64942000000001,0 21.46556,68.67805,0 21.40666,68.74361,0 21.30195,68.75555,0 21.08139,68.86665000000001,0 20.89527,68.89361,0 20.88888,68.92583000000002,0 20.93889,68.95834000000001,0 20.86666,69.00499000000001,0 20.60417,69.05305,0 20.58093,69.0603,0 20.73833,69.09636999999999,0 21.05944,69.03444,0 21.11305,69.10805000000002,0 21.04222,69.16082,0 21.02806,69.20805,0 21.055,69.22861,0 21.32083,69.32611,0 21.68195,69.28471,0 22.34667,68.8447,0 22.39833,68.71111000000001,0 22.55,68.73779,0 22.80277,68.69138,0 23.06389,68.69555,0 23.22500000000001,68.62971,0 23.69277,68.71222,0 23.83,68.82999,0 23.97639,68.83249000000001,0 24.35139,68.71053000000001,0 24.75445,68.66025999999999,0 24.89566,68.5582,0 24.95523,68.60391,0 25.10334,68.63472,0 25.15472,68.76778,0 25.21445,68.81749000000001,0 25.40695,68.88638,0 25.58555,68.87887000000001,0 25.65277,68.90639,0 25.81277,69.0175,0 25.73222,69.10916,0 25.71333,69.25526000000001,0 25.755,69.34220999999999,0 25.83361,69.38416000000001,0 25.82666,69.43638,0 25.87416,69.48636999999999,0 25.85277,69.54666,0 25.9575,69.59027,0 25.97416,69.63527000000001,0 25.94194,69.66722,0 25.97834,69.70193,0 26.15583,69.74249,0 26.37806000000001,69.85444,0 26.47056,69.93527,0 26.80658,69.95206,0 27.06889,69.90860000000001,0 27.29194,69.95,0 27.3,69.99055,0 27.53833,70.03525999999999,0 27.57333,70.06887999999999,0 27.87305,70.08804000000001,0 27.94416,70.08027,0 27.97916,70.00664999999999,0 28.09417,69.93859000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +37.5369,84.90001,0 37.5369,81.47494,0 35.86762,81.47494,0 35.86762,78.71200000000002,0 37.59446,78.71200000000002,0 37.56522,73.71208,0 37.5369,68.86902000000001,0 37.16386,68.89137,0 36.23384,69.15934,0 35.31956,69.30909,0 33.48315,69.49036,0 33.13635,69.77410999999999,0 32.28514,69.92385,0 31.92259,70.01843,0 31.17443,70.36256,0 30.18603,70.73994,0 28.92806,70.93761000000001,0 28.20923,71.09936999999999,0 25.9988,71.18922000000001,0 24.72286,71.15327000000001,0 23.95011,71.17124,0 22.54661000000001,70.84514,0 22.54661000000001,75.84414,0 23.89267,75.84414,0 28.89267,75.84414,0 33.89268,75.84414,0 33.89268,76.51716999999999,0 33.89268,81.51716999999999,0 32.54661,81.51716999999999,0 27.54661,81.51716999999999,0 22.54661000000001,81.51716999999999,0 22.54661000000001,84.90001,0 22.54661000000001,89.90000000000001,0 23.54596,89.90000000000001,0 24.54531,89.90000000000001,0 25.54466,89.90000000000001,0 26.54402,89.90000000000001,0 27.54338,89.90000000000001,0 28.54273,89.90000000000001,0 29.54207,89.90000000000001,0 30.54143,89.90000000000001,0 31.54078000000001,89.90000000000001,0 32.54013,89.90000000000001,0 33.53948,89.90000000000001,0 34.53884000000001,89.90000000000001,0 35.53819,89.90000000000001,0 36.53754,89.90000000000001,0 37.5369,89.90000000000001,0 37.5369,84.90001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +03:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(3);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0300</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +42.79083,37.38472,0 42.95554,37.32222,0 43.14971,37.37777,0 43.27916,37.3161,0 43.35666,37.33083,0 43.49471,37.24972,0 43.79110000000001,37.23194,0 43.83444,37.20499,0 43.91138,37.22415,0 44.01416,37.32054,0 44.11638,37.31638,0 44.26166,37.24194,0 44.26777,37.1675,0 44.19333,37.10027,0 44.25527,36.98666,0 44.31721,36.97055,0 44.35111,37.04833,0 44.43638000000001,37.06304,0 44.63806,37.18748,0 44.78733,37.1497,0 44.76554,37.11694,0 44.78499,37.08472,0 44.90972000000001,37.02388,0 44.88416,36.96166,0 44.90664,36.88832,0 44.84915,36.79832,0 45.01277000000001,36.7536,0 45.06666000000001,36.68054,0 45.01471000000002,36.53527,0 45.09415,36.42443,0 45.23916,36.43137,0 45.27916,36.3811,0 45.27303000000001,36.30859,0 45.30332000000001,36.28083,0 45.27582,36.25776,0 45.31971,36.23832,0 45.32027000000001,36.15609,0 45.36832,36.08637,0 45.33165,36.01416,0 45.34916,35.99055,0 45.50555000000001,36.02055,0 45.55777000000001,36.0022,0 45.66332000000001,35.94054,0 45.76583000000001,35.81194,0 46.08749,35.86639,0 46.18166000000001,35.80417,0 46.34444,35.82193,0 46.2686,35.72805,0 46.02944,35.68776,0 46.02388,35.57999,0 45.97999000000001,35.58471,0 46.0061,35.53778,0 45.99221,35.48109,0 46.14805,35.29746,0 46.11916000000001,35.24444,0 46.19305,35.21111,0 46.15331,35.1611,0 46.16638,35.10722,0 46.03944,35.05833,0 45.93378,35.09876,0 45.92193000000001,35.05443,0 45.8786,35.03527,0 45.89555,34.9636,0 45.86776,34.9036,0 45.77249,34.91444,0 45.76611,34.84583,0 45.68860000000002,34.81528,0 45.6511,34.72694,0 45.70833,34.65915,0 45.72220999999999,34.55665,0 45.61472,34.55999,0 45.52473000000001,34.6032,0 45.52555,34.50249,0 45.43555000000001,34.45528,0 45.48777000000001,34.33583,0 45.53555,34.35111,0 45.58472,34.30249,0 45.54695000000002,34.18833,0 45.56276,34.14054,0 45.46888,34.07917,0 45.40582,33.9711,0 45.50804,33.94249,0 45.68888,33.67499,0 45.75527000000001,33.63361,0 45.74694,33.59166,0 45.90659000000001,33.63655,0 45.89666,33.60139,0 45.9486,33.55665,0 45.87471,33.49166,0 46.00647000000001,33.48579,0 46.05027000000001,33.37778,0 46.17555000000001,33.2686,0 46.19944,33.19582,0 46.11138,33.11277,0 46.05138000000001,33.11832,0 46.05416000000001,33.08805,0 46.14972,33.06527,0 46.0975,33.00555,0 46.10722,32.96749,0 46.42388,32.93749,0 46.73944000000001,32.75999,0 47.1375,32.46305,0 47.27499000000001,32.49194,0 47.3561,32.47638,0 47.43416,32.39778,0 47.41332,32.34109,0 47.52887,32.22887,0 47.5075,32.15416,0 47.68971000000001,32.02388,0 47.79056000000001,31.86,0 47.86445,31.79861,0 47.69721000000001,31.40778,0 47.69388000000001,31.00111,0 48.03638,30.99749,0 48.03249,30.49138,0 48.16043,30.42708,0 48.1992,30.32573,0 48.27964000000001,30.33528,0 48.40781,30.21121,0 48.38777000000001,30.13694,0 48.45333000000001,29.99805,0 48.54556,29.96302,0 48.59428,29.94093000000001,0 49.22824000000001,30.14287,0 49.3535,30.13924,0 49.55681000000001,29.98495000000001,0 50.0717,29.82476,0 50.27212,29.1344,0 51.29651,27.95413,0 51.54147,27.7537,0 52.52133,27.48647,0 52.52133,24.12054,0 52.33083,23.99971,0 52.08055,23.95555,0 51.94305,23.99277,0 51.92555000000001,23.95471,0 51.79873,24.01633,0 51.77584000000001,24.27528,0 51.74389,24.28639,0 51.70888,24.21472,0 51.67222,24.2725,0 51.63556000000001,24.20972,0 51.62222000000001,24.34472,0 51.59138,24.37833,0 51.58423000000001,24.26046,0 51.58333,24.11666,0 52.58333,22.93889,0 55.14166,22.63333,0 55.19917000000002,22.69972,0 55.66611000000001,21.99972,0 55.00000000000001,20,0 51.99929,18.99934,0 53.11444,16.64278,0 52.94389,16.60806,0 52.52133,16.43764,0 52.52133,13.61265,0 55.86174,13.61265,0 55.86174,10.62857,0 52.52067,10.62583,0 52.52069,7.825823000000002,0 52.52073,2.825829000000001,0 52.52075,-2.174176,0 52.52079000000001,-7.17417,0 52.52081,-12.17417,0 52.52085,-17.17417,0 52.52089,-22.17417,0 52.52089,-24.17416,0 52.52093000000001,-29.17417,0 52.52097,-34.17416,0 52.521,-39.17416,0 52.52101,-41.47417,0 52.52105,-46.47416000000001,0 52.52108,-51.47417,0 52.52111,-56.47416,0 52.52115,-61.47417000000001,0 52.52118000000001,-66.47416,0 52.52121000000001,-71.47417,0 52.52125,-76.47417,0 52.52126,-78.97417,0 52.52129000000001,-83.97417000000002,0 52.52133,-88.97416,0 52.52133,-89.90000000000001,0 51.52188,-89.90000000000001,0 50.52242,-89.90000000000001,0 49.52297,-89.90000000000001,0 48.52351,-89.90000000000001,0 47.52406,-89.90000000000001,0 46.52459000000001,-89.90000000000001,0 45.52513,-89.90000000000001,0 44.52568,-89.90000000000001,0 43.52622,-89.90000000000001,0 42.52677,-89.90000000000001,0 41.52731,-89.90000000000001,0 40.52786,-89.90000000000001,0 39.5284,-89.90000000000001,0 38.52895,-89.90000000000001,0 37.52949,-89.90000000000001,0 37.52949,-84.89999,0 37.52949,-79.90000000000001,0 37.52949,-74.90000000000001,0 37.52949,-69.90000000000001,0 37.52949,-67.36546000000001,0 37.52949,-62.36546000000001,0 37.52949,-57.36545,0 37.52949,-52.36546,0 37.52949,-49.06545000000001,0 37.52949,-44.06546000000001,0 37.52949,-39.06546,0 37.52949,-34.06546,0 37.52949,-29.06546000000001,0 37.52949,-24.06546,0 37.52949,-19.06546,0 37.52949,-17.56545,0 37.87222,-17.37639,0 38.11138,-17.31139,0 38.13083,-17.25361,0 38.07889,-17.19278,0 38.11694,-17.20889,0 38.12889,-17.17834,0 38.15666,-17.2914,0 38.4975,-17.14417,0 38.50638000000001,-17.10973,0 38.54056,-17.12584,0 38.58806,-17.07723,0 38.60472,-17.09916,0 38.67889,-17.0775,0 38.66444,-17.02916,0 38.70972,-17.06361,0 38.81833,-17.05778,0 39.00154,-16.99003,0 39.08583,-16.99168,0 39.12749,-16.93305,0 39.08361,-16.91612,0 39.12917,-16.90084,0 39.12749,-16.87042,0 39.15639,-16.88083,0 39.28388,-16.76834,0 39.71716,-16.50974,0 39.7965,-16.46608,0 39.85722,-16.43055,0 39.90667000000001,-16.4168,0 39.98191,-16.23304,0 40.13582,-15.9925,0 40.13027,-15.9675,0 40.08749,-16.00944,0 40.06472,-15.99361,0 40.17528,-15.92,0 40.36028,-15.72333,0 40.49666000000001,-15.52917,0 40.54333,-15.53417,0 40.58777,-15.47973,0 40.57167,-15.45111,0 40.68472,-15.24973,0 40.62722,-15.155,0 40.57805,-15.20278,0 40.51445,-15.18723,0 40.52888,-15.13278,0 40.60167,-15.13389,0 40.64527,-15.08279,0 40.70888000000001,-15.07834,0 40.66,-14.98721,0 40.78027,-14.98667,0 40.74722,-14.92834,0 40.66305,-14.92111,0 40.64527,-14.85806,0 40.66888,-14.9075,0 40.72278,-14.89279,0 40.84195,-14.795,0 40.84472,-14.68611,0 40.8064,-14.61417,0 40.82999,-14.53028,0 40.76083,-14.53805,0 40.82804,-14.49944,0 40.81166000000001,-14.40779,0 40.68999,-14.43584,0 40.6661,-14.5375,0 40.62777,-14.56944,0 40.6264,-14.445,0 40.65472000000001,-14.44195,0 40.6375,-14.33973,0 40.66916,-14.36723,0 40.72861,-14.34306,0 40.74472,-14.27473,0 40.70361,-14.18222,0 40.64917,-14.1975,0 40.59,-14.26834,0 40.59389,-14.21084,0 40.55584,-14.23,0 40.53054,-14.17166,0 40.62277,-14.12555,0 40.59666,-14.08722,0 40.63833000000001,-14.07918,0 40.64999000000001,-14.01694,0 40.53749,-13.64528,0 40.60056,-13.57084,0 40.52584,-13.51528,0 40.59111,-13.37473,0 40.56528,-13.3664,0 40.56638,-13.285,0 40.52833,-13.3025,0 40.56806,-13.26222,0 40.56,-13.07861,0 40.59722,-12.97334,0 40.50166000000001,-12.94445,0 40.48972,-13.0275,0 40.41,-12.96194,0 40.47666,-12.88417,0 40.53416,-12.90416,0 40.55639,-12.85445,0 40.52361,-12.81778,0 40.56638,-12.81889,0 40.55889,-12.785,0 40.64194,-12.7814,0 40.61639,-12.71334,0 40.57139,-12.69806,0 40.57444,-12.64306,0 40.61845,-12.65416,0 40.63672,-12.37548,0 40.60549,-11.96501,0 40.49342,-11.91995,0 40.51611,-11.84333,0 40.47556,-11.82029,0 40.42417,-11.64973,0 40.45805,-11.60583,0 40.42471,-11.55416,0 40.47889,-11.40556,0 40.71345000000001,-11.17438,0 40.70627,-10.84757,0 40.64145,-10.69888,0 40.63945,-10.67166,0 40.57332,-10.65,0 40.55972,-10.58666,0 40.50388,-10.56444,0 40.52111,-10.48416,0 40.47277,-10.50778,0 40.47834,-10.4825,0 40.43681,-10.47817,0 40.3425,-10.56722,0 40.23611,-10.60278,0 40.00666,-10.80555,0 39.89832000000001,-10.83166,0 39.76694000000001,-10.92056,0 39.50833,-10.99528,0 39.26805,-11.16806,0 39.13444,-11.14195,0 38.89777,-11.17222,0 38.77389,-11.26166,0 38.65527,-11.2811,0 38.49171,-11.41531,0 38.37777,-11.37722,0 38.27972,-11.28834,0 38.09669,-11.25633,0 37.86861,-11.32666,0 37.80972,-11.48,0 37.81582,-11.53833,0 37.66306,-11.63833,0 37.52666,-11.6625,0 37.46204,-11.72732,0 37.34722,-11.68667,0 37.28611,-11.70418,0 37.12583,-11.67333,0 37.02584,-11.56721,0 36.90445,-11.59473,0 36.82972,-11.56861,0 36.67777,-11.71472,0 36.56195,-11.74028,0 36.49001,-11.68111,0 36.18972,-11.70666,0 36.16305,-11.57778,0 35.96556,-11.50084,0 35.94306,-11.43473,0 35.83833,-11.41472,0 35.7725,-11.47528,0 35.70389,-11.48778,0 35.6525,-11.58473,0 35.57638,-11.60667,0 35.51611,-11.61084,0 35.49416,-11.57918,0 34.96673,-11.57211,0 34.95972,-11.48666,0 34.89054,-11.38306,0 34.80555,-11.33222,0 34.76361000000001,-11.345,0 34.71027,-11.20111,0 34.615,-11.10945,0 34.59305,-11.01668,0 34.64305,-10.92194,0 34.67611000000001,-10.74306,0 34.57583,-10.51667,0 34.58277,-10.27,0 34.52389,-10.1075,0 34.5225,-10.01084,0 34.33805,-9.7875,0 34.32527,-9.73278,0 34.03944,-9.482779000000001,0 33.95721,-9.526671,0 33.95305000000001,-9.673335000000002,0 33.91961,-9.707652,0 33.75583,-9.582777999999999,0 33.68361,-9.610552,0 33.585,-9.584164,0 33.50889,-9.621945,0 33.43777,-9.615834,0 33.39388,-9.538055999999999,0 33.31444000000001,-9.485550999999999,0 33.1925,-9.509167,0 33.12583,-9.490285,0 32.99777,-9.373339,0 32.9404,-9.405072000000001,0 32.77166,-9.335557,0 32.74195,-9.281665,0 32.54527,-9.26667,0 32.42556,-9.129718000000002,0 32.24945000000001,-9.131949000000001,0 32.16,-9.063333999999999,0 32.05611,-9.040832999999999,0 31.97972,-9.071109999999999,0 31.93416,-9.018891,0 31.95611,-8.930835999999999,0 31.78553000000001,-8.885061,0 31.73861,-8.918056,0 31.68361000000001,-8.908894,0 31.57305,-8.818605,0 31.56332,-8.698608,0 31.45945,-8.629443,0 31.39806,-8.629443,0 31.37556,-8.582229999999999,0 31.28527,-8.623333000000001,0 31.22055,-8.576668,0 31.13277,-8.615557000000001,0 31.04111,-8.590284000000001,0 30.90138,-8.460001,0 30.77123,-8.192242,0 30.65445,-7.886943,0 30.45583,-7.580277000000001,0 30.40695,-7.356393,0 30.31138,-7.136947000000001,0 30.08611,-6.863607,0 29.73250000000001,-6.627501,0 29.66111,-6.455556,0 29.55029,-6.295285,0 29.49777,-5.979726000000001,0 29.63166,-5.721949,0 29.34916000000001,-4.939732,0 29.34139,-4.797783,0 29.42834,-4.552218,0 29.42334,-4.447504,0 29.75222000000001,-4.461112,0 29.81277,-4.360284,0 29.85777,-4.371111,0 30.02611,-4.269447,0 30.07277,-4.166667,0 30.17222,-4.086667,0 30.22806,-3.928051,0 30.32111,-3.78666,0 30.40084,-3.786112,0 30.39139,-3.711106000000001,0 30.44916,-3.547215000000001,0 30.66222,-3.418893999999999,0 30.66361,-3.386665,0 30.6222,-3.370122,0 30.65166,-3.332503,0 30.835,-3.256948,0 30.85222,-3.145285,0 30.79555,-3.048607,0 30.84477,-2.976012,0 30.66611,-2.976391,0 30.56333,-2.89305,0 30.485,-2.947222,0 30.4175,-2.861946999999999,0 30.44028000000001,-2.747215,0 30.52222,-2.657215,0 30.45721,-2.685835,0 30.43305,-2.647783,0 30.57332,-2.399167,0 30.6525,-2.396665000000001,0 30.69972,-2.347497,0 30.78222,-2.380554,0 30.82917,-2.357785,0 30.89417,-2.076112,0 30.80832,-1.929448,0 30.835,-1.800559,0 30.81111,-1.700551,0 30.84027,-1.670554,0 30.74888,-1.52055,0 30.73917,-1.436949,0 30.56166,-1.326943,0 30.51222,-1.164448,0 30.46945,-1.158337,0 30.45333000000001,-1.095553,0 30.48222,-1.063333,0 30.36,-1.060003,0 30.34566,-1.129168,0 30.22916,-1.266661,0 30.18472,-1.275004,0 30.17056,-1.339723,0 29.97500000000001,-1.464445,0 29.9075,-1.464166,0 29.84139,-1.320283,0 29.80306,-1.365553,0 29.73416,-1.341946,0 29.68332,-1.380556,0 29.59694,-1.385839,0 29.56639,-1.005841,0 29.57833,-0.9000005,0 29.62832,-0.8888946000000001,0 29.61833,-0.7333385,0 29.63805,-0.5933346000000001,0 29.67195,-0.5722206,0 29.64861,-0.4397225,0 29.735,-0.00083754,0 29.71666000000002,0.07221546,0 29.77332,0.1722145,0 29.81389,0.1586065,0 29.85833,0.3666595,0 29.9625,0.4872144,0 29.93833,0.6344455,0 29.96055000000001,0.8255515,0 30.14583,0.8983344000000001,0 30.21861000000001,0.9963894000000001,0 30.23611,1.134161,0 30.27805,1.168893,0 30.33221,1.151387,0 30.34861,1.196108,0 30.45611,1.212785,0 30.68806,1.493891,0 31.03666000000001,1.765556,0 31.30277,2.121389,0 31.2992,2.162853,0 31.2025,2.229164,0 31.19694,2.305835,0 31.12528,2.278053,0 31.04972,2.303891,0 31.07027,2.335005,0 30.97084,2.411945,0 30.91416,2.339442,0 30.88027,2.337497,0 30.82527,2.399165,0 30.82667,2.442221,0 30.72972,2.448053,0 30.81667,2.769165,0 30.87806000000001,2.843612,0 30.85555,2.957499,0 30.7625,3.055004,0 30.83,3.275558,0 30.935,3.411665,0 30.91721,3.521942,0 30.85882000000001,3.493394,0 30.97471,3.691943,0 31.17667000000001,3.795281,0 31.28806,3.794714,0 31.54083,3.653611999999999,0 31.57389,3.691664,0 31.70194,3.713058000000001,0 31.79945,3.825557,0 31.93332,3.687785,0 31.95111,3.593889,0 32.0639,3.59028,0 32.09361,3.532500000000001,0 32.19333,3.511394999999999,0 32.19443,3.611042,0 32.40445,3.743613,0 32.71944,3.755834,0 32.89804,3.811949,0 33.01666,3.888612,0 33.18388,3.766113,0 33.51694,3.752217,0 33.99666,4.222782,0 34.38819,4.609683,0 35.50888,4.616738,0 35.94056,4.622499,0 35.77443,4.798611,0 35.77389,5.075279,0 35.83083,5.153894000000001,0 35.79556,5.251391,0 35.83583,5.318613,0 35.68443,5.391387,0 35.605,5.373054,0 35.50472,5.431391,0 35.44749,5.429448,0 35.35638,5.350275,0 35.31138,5.366942,0 35.26972,5.441112,0 35.28999,5.507774,0 35.10388,5.632496000000001,0 35.10582,5.692779000000001,0 34.98639,5.878052000000001,0 34.98782,6.013944,0 34.86055,6.26166,0 34.85722,6.349445000000001,0 34.79777,6.444719,0 34.76277,6.605837000000002,0 34.6375,6.727221,0 34.53,6.743888,0 34.46999,6.916112,0 34.2947,6.966432000000001,0 34.2025,7.052219000000001,0 34.1825,7.163054000000001,0 34.13361,7.158887,0 34.03,7.240832,0 34.03083000000001,7.375004,0 33.89278,7.518888,0 33.78889,7.56639,0 33.71139,7.660277,0 33.55138,7.686387,0 33.47027,7.743339000000001,0 33.33944,7.713612000000001,0 33.16806,7.796393000000001,0 33.05389,7.788888,0 32.99683,7.887221000000001,0 33.03028,7.995248000000001,0 33.18643,8.136979999999999,0 33.16527,8.204715,0 33.20861,8.238049999999999,0 33.15834,8.291392999999999,0 33.16082,8.360001000000001,0 33.20444,8.433890000000002,0 33.2625,8.462502000000001,0 33.38777,8.43777,0 33.50777,8.480834,0 33.57804,8.449443000000001,0 33.62555,8.470556,0 33.69333,8.386389000000001,0 33.77599,8.364941999999999,0 33.85249,8.425836,0 33.97888,8.441387000000002,0 34.1228,8.584649000000001,0 34.13499,8.955270000000001,0 34.08583,9.553058999999999,0 34.23138000000001,10.03027,0 34.32306,10.11721,0 34.34777,10.23195,0 34.28611,10.55416,0 34.43972,10.78445,0 34.59444,10.88779,0 34.77333,10.74584,0 34.77333,10.68694,0 34.80861,10.72945,0 34.86443,10.735,0 34.86861,10.78582,0 34.97527,10.86445,0 34.97861,10.91527,0 34.93333,10.95666,0 35.0075,11.19833,0 34.96388,11.25695,0 35.09361,11.55778,0 35.05806,11.72056,0 35.09693,11.82694,0 35.23555,11.90083,0 35.27111,11.97555,0 35.34555,12.03277,0 35.3811,12.16111,0 35.43249,12.20055,0 35.64916,12.59417,0 35.69528,12.61222,0 35.69944,12.66583,0 36.00638,12.72416,0 36.1427,12.70692,0 36.16528,12.87527,0 36.13278,12.92333,0 36.16972,12.98416,0 36.15944,13.04167,0 36.24860000000001,13.36805,0 36.3975,13.56778,0 36.48693,13.83944,0 36.44639,13.95695,0 36.54282,14.26206,0 36.44329,15.14995,0 36.54277,15.23472,0 36.61054,15.43721,0 36.64777000000001,15.44971,0 36.62611,15.48945,0 36.69777,15.75305,0 36.76361,15.80806,0 36.91584,16.215,0 36.97305,16.26944,0 36.95694,16.42528,0 36.89638,16.51777,0 36.91222,16.64111,0 36.98055,16.70611,0 37.02278,16.85056,0 36.98693,16.94306,0 37.01805,17.00361,0 36.99582,17.07389,0 37.15194,17.01555,0 37.30693000000001,17.06111,0 37.42329,17.03422,0 37.52058,17.1958,0 37.5125,17.32333,0 37.73584,17.37666,0 37.81888,17.48139,0 37.91722,17.4475,0 38.05584,17.55194,0 38.09555,17.54389,0 38.11138,17.48111,0 38.13361,17.5486,0 38.18583,17.56222,0 38.25777,17.5325,0 38.27667,17.60694000000001,0 38.37029,17.66611,0 38.45861,17.87111,0 38.60069,17.99488,0 38.64405,18.06641,0 38.51692,18.23592,0 38.24853,18.39601,0 38.23912000000001,19.32831,0 37.51406,19.32831,0 37.51406,24.19318,0 37.01093,24.97079,0 36.96872,25.39901,0 36.83,25.51963,0 36.54049,25.54376,0 36.47416,25.62217,0 36.49828,26.06245,0 35.71421,27.09985,0 35.26789,27.63060000000001,0 34.88189,27.89598,0 34.6346,27.87186,0 34.49483,27.93732,0 34.48494,27.99542,0 34.5714,28.09056,0 34.62749000000001,28.17388,0 34.65972,28.16445,0 34.64528,28.19361,0 34.80778,28.53639,0 34.78054,28.67083,0 34.84254,28.88452,0 34.85527,29.03861,0 34.96139,29.36083,0 36.07,29.18889,0 36.50055,29.50277000000001,0 36.74361,29.86472,0 37.50277,30.00222000000001,0 37.6675,30.33639,0 38.00139,30.50417,0 37.00527,31.50555,0 39.005,32.00556,0 39.19675,32.15495,0 39.30111,32.23638,0 39.26,32.35555,0 39.04366,32.30405,0 38.98602,32.47767,0 39.08601,32.50151,0 38.7947,33.37759,0 41.00388,34.41944,0 41.22915,34.78832,0 41.21527,35.22471,0 41.27777,35.49554,0 41.38388,35.62527,0 41.37806,35.83861,0 41.25499000000001,36.05499,0 41.29027000000001,36.35555,0 41.40304,36.52555,0 41.83528,36.59888,0 42.37723,37.06467000000001,0 42.35562,37.10693,0 42.58165,37.15277,0 42.72666,37.35555000000001,0 42.79083,37.38472,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +52.49656000000001,84.90001,0 52.49656000000001,82.81507000000001,0 57.49650000000001,82.84008,0 62.49644000000001,82.86508000000001,0 67.49638,82.89008,0 67.53675,82.89028,0 67.53675,81.66452,0 67.53675,77.00954,0 68.50082,76.95638,0 68.47275999999999,76.92554,0 68.80942,76.86638000000002,0 68.75443,76.82222,0 68.93137,76.78276,0 68.89361,76.75749,0 68.90054000000001,76.72776,0 69.03027000000002,76.70802999999999,0 68.85637,76.65665,0 68.78471999999999,76.58778,0 68.92554,76.55888,0 68.57971000000001,76.43304999999999,0 68.40109,76.40609000000001,0 68.43941,76.38972,0 68.33333,76.33582,0 68.16943999999999,76.31805,0 68.31331,76.28333000000001,0 67.53675,76.19176,0 67.53675,73.95477,0 67.53675,71.40988,0 67.08581000000001,71.30387,0 66.91553999999999,71.29276,0 66.83665000000001,71.20972,0 66.78388,71.14221000000001,0 66.62154,71.04885,0 66.54228000000001,70.92879000000001,0 66.61275999999999,70.86773,0 66.68886999999999,70.76054000000001,0 67.10720000000001,70.83526999999999,0 67.22887,70.82555000000001,0 67.33249000000001,70.76888,0 67.33971,70.72499000000001,0 67.20889,70.52666000000001,0 67.29221,70.46998000000001,0 67.25194999999999,70.44832,0 67.26666,70.41110000000001,0 67.19971,70.39027,0 67.08862000000001,70.21623,0 67.07941,70.12733,0 66.99387,70.04886999999999,0 66.87331,70.00109999999999,0 66.76721000000001,69.75722,0 66.79192999999999,69.58028,0 66.93801000000001,69.52226,0 66.95407,69.40528000000001,0 67.16154,69.35935000000001,0 67.25494999999999,69.40298,0 67.37054999999999,69.52164999999999,0 67.38528000000001,69.61859,0 67.61304,69.59166999999999,0 67.78610000000001,69.51666,0 67.96748,69.48415,0 68.03804,69.48888,0 68.00721,69.51860000000001,0 68.10165000000001,69.54665,0 68.10942,69.38889,0 68.02139,69.3436,0 68.04749,69.28137,0 68.47331,68.97248999999999,0 68.78498,68.91081,0 69.02164000000001,68.96416000000001,0 69.03248000000001,68.94193,0 69.22026,68.95722000000001,0 68.94386,68.89803000000001,0 69.10831,68.87248,0 69.09526,68.83333,0 69.00888000000001,68.79998999999999,0 68.89776999999999,68.60748,0 68.56303,68.36942000000001,0 68.57165999999999,68.33778,0 68.64999,68.31219,0 68.56415,68.31052,0 68.43637,68.22499000000001,0 68.28333000000001,68.18498,0 68.27025999999999,68.23915,0 68.18526,68.23609999999999,0 68.20081999999999,68.29471,0 68.26054000000001,68.33472,0 68.15942,68.41137000000001,0 67.78306000000001,68.47749,0 67.70610000000001,68.51054000000001,0 67.80582,68.50082,0 67.77222,68.51776,0 67.32337,68.69279,0 67.27820000000001,68.74968,0 67.38152,68.76640999999999,0 67.39063,68.80437000000001,0 67.12005000000001,68.84113000000001,0 66.55627,68.91167,0 66.52336,68.9671,0 66.2572,69.07253,0 65.79581,69.13697000000001,0 65.38193,69.18471,0 65.04832,69.27332,0 64.91499,69.25471,0 64.94275,69.2047,0 64.83257,69.15213,0 64.51999000000001,69.01776,0 64.51749,68.98248,0 64.58389,68.96082,0 64.52139,68.90776,0 64.54998999999999,68.87053000000002,0 64.74582,68.85526,0 64.75499000000001,68.88527000000001,0 64.80415000000001,68.89194000000001,0 65.08055,68.80888,0 65.20220999999999,68.82331000000001,0 65.34860000000001,68.79192000000001,0 65.32639000000002,68.72499000000001,0 65.40498,68.73276,0 65.42941999999999,68.7,0 65.37831,68.68053999999999,0 65.52361000000001,68.58414999999999,0 65.66165,68.54694000000001,0 65.61443,68.52554000000001,0 65.60609,68.48499,0 65.48054999999999,68.43053999999999,0 65.26775000000001,68.21998000000001,0 65.33333,68.08638000000001,0 65.28498999999999,68.01194,0 65.43471,67.91804999999999,0 65.78055000000001,67.9233,0 65.91081,67.96332,0 66.08611000000001,67.93442,0 66.09248,67.89027,0 66.05443,67.88083,0 66.01805,67.79305000000001,0 66.18388,67.74832000000002,0 66.21053000000001,67.69609,0 66.10526,67.64554,0 65.99275,67.67915000000002,0 65.84054999999999,67.65192999999999,0 65.81805,67.62608,0 65.86832,67.60664,0 65.79165999999999,67.56971,0 66.0397,67.57693,0 66.10664,67.47859,0 65.96138000000001,67.39525999999999,0 65.69054,67.38665,0 65.67249,67.36609,0 65.71887,67.34054999999999,0 65.65886999999999,67.30609,0 65.18248,67.15082,0 65.22136999999999,67.07693,0 65.07055,67.03942000000001,0 65.10554000000001,67.00805000000001,0 65.10138000000001,66.89055000000001,0 64.5761,66.79915,0 64.53999,66.7272,0 64.41277,66.72387000000001,0 64.31247999999999,66.66443,0 63.98277,66.65358999999999,0 63.81554000000001,66.54721000000001,0 63.65888000000001,66.52582,0 63.53722000000001,66.46026999999999,0 63.40915,66.48109,0 63.40860000000001,66.45277,0 63.22332000000001,66.32638,0 63.30416000000001,66.2747,0 63.29056,66.23276,0 63.15388,66.26749,0 63.04694000000001,66.2247,0 62.95917000000002,66.1097,0 62.86166000000001,66.07720999999999,0 62.81750000000001,66.01194,0 62.88833000000002,65.97998,0 62.79777000000001,65.93109,0 62.86111,65.87804,0 62.73193000000001,65.85776,0 62.69388,65.86331,0 62.68193999999999,65.86304,0 62.11306,65.72083000000001,0 61.87027000000001,65.70222,0 61.68694,65.57388,0 61.58222000000001,65.55165,0 61.41555000000002,65.39639,0 61.24917,65.31887999999999,0 61.3336,65.28888000000001,0 61.23944,65.18552,0 61.11166,65.17221000000001,0 60.96277000000001,65.04165999999999,0 60.81194,65.04859999999999,0 60.63138,64.88361,0 60.59638,64.94193,0 60.42972000000001,65.00082,0 60.42749,65.05193,0 60.15804000000001,65.06721,0 59.65416,64.78333000000001,0 59.63805000000001,64.72387000000001,0 59.70249,64.65166000000001,0 59.61361,64.62276,0 59.47970999999999,64.48193000000001,0 59.60470999999999,64.46026999999999,0 59.6361,64.33749,0 59.57943,64.27943000000001,0 59.58805,64.23248,0 59.73388000000001,64.15331999999999,0 59.85972,64.1375,0 59.77055,63.99942999999999,0 59.56583000000001,63.91915,0 59.57249000000002,63.83666,0 59.51389,63.77999000000001,0 59.47776000000001,63.56805,0 59.31722000000001,63.39888000000001,0 59.35222,63.36306,0 59.28249,63.33278,0 59.3386,63.28999000000001,0 59.25889,63.2011,0 59.30277,63.12638,0 59.22221,63.07388,0 59.27665,62.96666,0 59.42471000000001,62.94415,0 59.48721000000001,62.89778,0 59.46083000000002,62.77917000000002,0 59.39138,62.72971,0 59.50972000000001,62.54722,0 59.65055,62.51721,0 59.60889,62.45333000000001,0 59.58472,62.41499,0 59.60028,62.36694,0 59.51527,62.31721000000002,0 59.40444,62.14222000000001,0 59.48555,61.98805,0 59.34137000000001,61.85695000000001,0 59.33915,61.80222,0 59.39694999999999,61.76110000000001,0 59.34887000000001,61.68221000000001,0 59.14333000000001,61.63305,0 59.01082000000001,61.54472,0 58.94054000000001,61.55972,0 58.80972,61.50556,0 57.18,61.51194,0 57.09666,61.48525999999999,0 56.67556,61.52777,0 56.60083,61.50249,0 56.54527,61.42888,0 56.3886,61.43943000000001,0 56.31638000000001,61.32527,0 56.34805,61.29388,0 56.29028000000001,61.20304000000001,0 55.82858,61.25057,0 55.72323,61.08145,0 55.27921000000001,61.12959000000002,0 55.19747,61.0041,0 55.06107,61.01941,0 54.97971,60.86388,0 53.86555,60.98749,0 53.80915000000001,60.84777,0 53.57389,60.86500000000001,0 53.33944000000001,60.89388,0 53.38499000000001,61.03389,0 52.87666,61.09388000000001,0 52.79499,60.94433,0 52.42917000000001,60.98137,0 52.34333,60.835,0 51.88777,60.87666,0 51.77333,60.59693999999999,0 52.15471000000001,60.54555000000001,0 52.12499,60.47027000000001,0 52.34304000000001,60.44638,0 52.34777,60.37330999999999,0 52.29815,60.24171,0 52.08278,60.31694000000002,0 52.0089,60.30360000000002,0 51.89999,60.24444,0 51.8,60.11083,0 51.53221000000001,59.94721,0 51.41332000000001,59.94165,0 51.34388,60.01833,0 51.07110999999999,60.06166000000001,0 50.82861000000001,59.86277000000001,0 50.40331000000001,59.84444,0 50.27887,59.80693000000002,0 50.2375,59.75277,0 49.99665,59.77887000000001,0 49.95193,59.74722000000001,0 50.05638,59.6875,0 49.85361,59.66361,0 49.83278,59.63693,0 49.78722000000001,59.50054999999999,0 49.82888000000001,59.48110000000001,0 49.79554,59.38388,0 49.82416,59.37054,0 49.79554,59.22221,0 49.59333,59.21776,0 49.50861,59.24610000000001,0 49.545,59.39305000000001,0 49.34194,59.41915000000001,0 49.3086,59.46694,0 49.14778,59.50889,0 49.03416,59.48444,0 49.08554,59.65554,0 48.50999,59.70388,0 48.45917000000001,60.19415,0 48.70499,60.30471,0 48.76305,60.365,0 48.67582000000001,60.40638000000001,0 48.54137999999999,60.41109,0 48.61777000000001,60.43333000000001,0 48.50416000000001,60.48276,0 48.51111000000001,60.51194,0 48.48110000000001,60.52499,0 48.53999,60.55915,0 48.53917,60.59999000000001,0 48.41924,60.85386000000001,0 47.9875,60.88665000000001,0 47.95999,61.00972,0 47.88139,60.99833,0 47.59415,61.05888,0 47.22833000000001,61.06444,0 47.19720000000002,61.05999,0 47.19138,60.99611000000002,0 47.10749,60.96194000000001,0 47.08027000000001,60.86221,0 47.04314000000001,60.85386000000001,0 47.08721000000001,60.82027,0 46.95277,60.65527,0 47.02388000000001,60.64695,0 47.01749,60.60249000000001,0 47.06166,60.57667000000001,0 46.85666,60.51527,0 46.87805,60.48166000000001,0 46.80444,60.38750000000001,0 46.83193000000001,60.37972,0 46.81305,60.32277,0 46.74499,60.25639000000001,0 46.43083000000001,60.27388,0 46.295,60.24249,0 46.35305,60.20972,0 46.36416000000001,60.1211,0 46.60609,60.06527,0 46.83138,60.07306000000001,0 46.89638,60.12583000000001,0 47.03694000000001,60.11832,0 46.92083000000001,59.84665,0 46.92305000000001,59.78444,0 47.12305,59.76139000000001,0 47.14305,59.71333,0 47.11351,59.61137999999999,0 47.05638,59.37860000000001,0 47.25249,59.35555,0 47.22332,59.2261,0 47.39443,59.14722,0 47.445,59.1675,0 47.45139,59.12694000000001,0 47.57639,59.06028,0 47.55082,58.9886,0 47.61166,58.91499,0 47.46777000000001,58.88416,0 47.45305000000001,58.92249,0 47.30721,58.91666,0 47.28111000000001,58.90222000000001,0 47.30388,58.85722,0 47.28028,58.81945,0 47.325,58.80082,0 47.265,58.75416,0 47.11333,58.77249,0 47.00417000000001,58.69583,0 47.0061,58.62249,0 46.58416,58.55804000000001,0 46.59026999999999,58.51832000000001,0 46.54972000000001,58.47665,0 46.59166000000001,58.43194,0 46.41137,58.36388,0 46.48109,58.33222,0 46.41026999999999,58.30805,0 46.41499000000001,58.21054000000001,0 46.35527,58.18721,0 46.30972000000001,58.11054,0 46.34132,58.07474,0 46.44972,58.01749,0 46.52304000000001,58.03889,0 47.38666,58.02332000000001,0 47.45610000000001,57.98555000000001,0 47.70083,57.96361,0 47.77027,57.90028,0 47.5611,57.80249,0 47.55082,57.765,0 47.58027000000001,57.74638,0 47.52859,57.70304,0 47.54888,57.66361,0 47.52638000000001,57.58416,0 47.48666,57.58472,0 47.4711,57.52859999999999,0 47.25027,57.57083,0 47.03082,57.49304,0 46.74443,57.52333,0 46.79722000000001,57.45666,0 46.70471000000001,57.42555000000001,0 46.68970000000001,57.39083,0 46.75388,57.37999,0 46.67611000000001,57.35,0 46.72055,57.29445,0 46.78972,57.28944,0 46.80888,57.23583,0 46.85194000000001,57.23444000000001,0 46.83443000000001,57.19971,0 46.72638000000001,57.20000000000001,0 46.76528,57.14388,0 46.76806,57.02971,0 46.70805,57.0164,0 46.75388,56.97417,0 46.71510000000001,56.96634000000001,0 46.74471,56.95471,0 46.71583,56.93721,0 46.94471,56.94833000000001,0 47.13277,56.91693000000001,0 47.18194,56.84805,0 47.12277,56.83749,0 47.17083000000001,56.80860000000001,0 47.42444,56.88082,0 47.50249,56.85139,0 47.51139,56.90666,0 47.61721,56.9761,0 47.68916,57.01250000000001,0 47.76971000000001,57.00972000000001,0 47.7336,57.05527000000001,0 47.785,57.05721,0 47.83083000000001,57.00416000000001,0 47.91666,56.97665000000001,0 47.93111,57.00944,0 47.97193,56.97387000000001,0 47.98694,57.01444999999999,0 48.13916,56.99833,0 48.2086,57.02722,0 48.16304,57.07972000000002,0 48.24888000000001,57.10666000000001,0 48.19222000000001,57.12471,0 48.30777000000001,57.16860000000001,0 48.4511,57.12054,0 48.56945,57.17249000000001,0 48.83555,57.16444,0 48.86639,57.12249,0 48.84583,57.0886,0 48.95471,57.08554,0 48.99804,57.11888,0 49.01194,57.19332,0 49.04610000000001,57.2011,0 49.00639000000001,57.23332000000001,0 49.01611,57.28305,0 49.18333000000001,57.33139,0 49.22805,57.28889,0 49.14361,57.15972,0 49.23138000000001,57.1311,0 49.26832,57.04361,0 49.41915,57.02194,0 49.41249000000001,57.05305000000001,0 49.71194,57.07361000000001,0 49.72499,56.99110000000001,0 49.76194,56.96500000000001,0 49.71582999999999,56.93361,0 49.75722,56.91415000000001,0 49.73443,56.89667000000001,0 50.01445,56.86721000000001,0 50.07082000000001,56.83082,0 50.0611,56.79361,0 50.10167000000001,56.79416,0 50.07221000000001,56.62266,0 50.16194000000001,56.615,0 50.22138000000001,56.66082,0 50.29834,56.63999,0 50.32306,56.65444,0 50.30304,56.67638,0 50.34777,56.67361,0 50.41361000000001,56.62804,0 50.42333,56.58526,0 50.49333,56.61639,0 50.45,56.54472,0 50.56971000000001,56.40250000000001,0 50.63110000000001,56.40526,0 50.58638,56.35443,0 50.73304,56.32999000000002,0 50.78527,56.35638,0 50.7625,56.39944,0 50.785,56.41555,0 50.85139000000001,56.39027000000001,0 50.87389,56.32666,0 50.90693,56.31583,0 50.90165,56.27944000000002,0 50.86111,56.27804,0 50.83942,56.2411,0 51.07639,56.12498999999999,0 51.1075,56.15139000000001,0 51.20499,56.13943,0 51.22304000000001,56.09916,0 51.3147,56.06972,0 51.36194,56.13054000000001,0 51.43667,56.14444000000001,0 51.6536,56.13444000000001,0 51.48999000000001,56.07888000000001,0 51.39721,55.9922,0 51.43027000000001,55.92722,0 51.64277,55.96054,0 51.68943,55.93278,0 51.72971,55.95971,0 51.78889,55.88388000000001,0 51.96083,55.97915,0 52.03749000000001,55.95916,0 52.05638,55.895,0 52.16916000000001,55.89306,0 52.25166,55.95805000000001,0 52.20417,56.00443000000001,0 52.2461,56.06694,0 52.18693,56.05527,0 52.20471000000001,56.08416,0 52.43665,56.02833,0 52.48471,56.03694000000002,0 52.44666,56.06361,0 52.46776,56.07749,0 52.66221,56.01554,0 52.72887,56.06471000000001,0 52.70054,56.14138000000001,0 52.83694000000001,56.09222000000001,0 52.85443,56.12666,0 52.81055,56.13388000000001,0 52.84554,56.15721,0 52.80582,56.18194,0 52.81694,56.20249,0 52.57583,56.22748000000001,0 52.55833,56.2625,0 52.63638,56.28638,0 52.74055,56.37471000000001,0 52.72277,56.39138,0 52.82665999999999,56.38888000000002,0 52.95278,56.44165000000002,0 52.95165999999999,56.46916,0 52.82555,56.52193,0 52.91778000000001,56.50111,0 52.94444,56.54472,0 53.09528,56.52694000000001,0 52.9386,56.36694,0 52.98054,56.3475,0 52.94138,56.30639,0 52.98221,56.28082,0 52.95610000000001,56.25166,0 53.00305,56.22165,0 52.99416,56.18833,0 53.04193,56.20805000000001,0 53.06888,56.18249,0 53.04749,56.13528,0 53.14666,56.16220000000001,0 53.26111,56.08554,0 53.34499,56.08471,0 53.34249,56.14749,0 53.2589,56.25471000000001,0 53.41666,56.27833,0 53.55610000000001,56.24388,0 53.54499,56.14083000000001,0 53.47470000000001,56.12110000000001,0 53.37888,55.98972,0 53.28694999999999,55.93555,0 53.1825,55.92389000000001,0 53.29725,55.85252,0 53.63842,55.91552,0 53.99722,55.85082,0 53.97027,55.78249000000002,0 54.03820000000001,55.80095000000001,0 54.0261,55.75666,0 54.09528000000001,55.77592,0 54.24194000000001,55.69137000000001,0 54.17833,55.62276000000001,0 53.99638000000001,55.54972000000002,0 53.99694,55.50027,0 53.93333000000001,55.4586,0 53.89833000000002,55.38166,0 53.70916,55.3336,0 53.59999,55.26027,0 53.58611000000001,55.20776999999999,0 53.39944000000001,55.22110000000001,0 53.33805000000001,55.19499,0 53.33443,55.16249000000001,0 53.13888000000001,55.14388,0 53.14138000000001,55.09389,0 53.26834,55.05331,0 53.26694000000001,55.01028000000001,0 53.40639000000001,54.99722,0 53.51412,54.88120000000001,0 53.64249000000002,54.90139,0 53.57695,54.81499,0 53.60777,54.81249,0 53.57888,54.74888,0 53.6086,54.71138,0 53.41055,54.55750000000001,0 53.42194,54.48666000000001,0 53.34444,54.38694000000001,0 53.36138,54.32888,0 53.43444,54.26915,0 53.41305,54.22222,0 53.47020000000001,54.0452,0 53.41082,54.02694,0 53.37194,53.96749,0 53.285,54.02750000000001,0 53.27221999999999,54.06999,0 53.19444,54.09832,0 53.06054,54.06999,0 53.09137,54.12054,0 53.03361000000001,54.12749000000001,0 52.93694,54.2136,0 53.11582,54.24944000000001,0 53.04749,54.34167000000002,0 53.0014,54.31249,0 53.02944000000001,54.28944,0 53.01361,54.26805,0 52.92916,54.26805,0 52.88166,54.27777,0 52.93249,54.28804000000001,0 52.92304,54.31388,0 52.68775999999999,54.36111,0 52.62194,54.3411,0 52.53745,54.37707,0 52.49416,54.39999,0 52.50306000000001,54.45193,0 52.43943,54.47887,0 52.37915,54.41444,0 52.35054,54.42333,0 52.36388,54.45916,0 52.20972,54.44304,0 52.2,54.40639,0 52.105,54.40054000000001,0 52.12028000000001,54.35111,0 52.08526000000001,54.33942,0 52.01999,54.43665,0 51.93277,54.43638,0 51.92722000000001,54.53611,0 51.67972,54.56166000000001,0 51.52333,54.64638,0 51.49388,54.59527,0 51.39388,54.59304000000001,0 51.39554,54.67722000000001,0 51.28139000000002,54.63694,0 51.18111,54.64638,0 51.09361,54.54861,0 51.00972000000001,54.55165,0 50.99277,54.49916000000001,0 51.01721,54.47916,0 50.93610000000001,54.33999,0 50.70305000000001,54.42833,0 50.5186,54.32639000000001,0 50.47165,54.42333,0 50.42944,54.42749000000001,0 50.38889,54.48888000000001,0 50.27665,54.44054,0 50.22527,54.50361000000001,0 50.07688,54.51667000000001,0 50.12555,54.48194,0 50.11721,54.43027000000001,0 50.21111000000001,54.42167,0 50.19944,54.38416,0 50.26222,54.28944,0 50.20304000000002,54.14027,0 50.22554,54.07916,0 50.19749,54.03722,0 49.97443,53.96138,0 49.95361,53.90527,0 50.05305,53.86860000000001,0 50.03778000000001,53.82527000000001,0 49.85722000000001,53.8336,0 49.83805,53.89083000000001,0 49.61805,53.81139,0 49.60916000000001,53.855,0 49.42249000000001,53.87082000000001,0 49.34304000000001,53.87,0 49.31694,53.83333,0 49.03694,53.85944,0 48.86333000000001,53.71277,0 48.47222,53.77083,0 48.51305,53.69638000000001,0 48.60333,53.66945,0 48.4461,53.6661,0 48.42695,53.62915000000002,0 48.35638,53.6711,0 48.31416000000001,53.65583000000002,0 48.355,53.60167000000001,0 48.44083000000001,53.58138000000001,0 48.37666000000001,53.54277,0 48.37833000000001,53.47137,0 48.23611,53.45888999999999,0 48.2225,53.42443,0 48.16638,53.48249,0 48.04853,53.47771,0 48.00360000000001,53.40666,0 47.94194,53.37583,0 48.09332,53.26194,0 48.13138000000001,53.16888,0 48.21221,53.14582999999999,0 48.14472000000001,53.05916,0 48.2236,52.99722,0 48.26944000000001,53.05777,0 48.36888,53.06638,0 48.31833,52.98555,0 48.51057,52.97799,0 48.57805,52.93554,0 48.58694,52.8686,0 48.43007000000001,52.78054,0 48.28889,52.81082,0 48.10944,52.80138,0 47.98499,52.74722,0 48.08749,52.73193,0 48.07861000000001,52.70555,0 47.92527,52.6661,0 47.90027,52.72221,0 47.85971,52.73166,0 47.77304,52.67972000000001,0 47.78111000000001,52.62915,0 47.72470000000001,52.57916,0 47.55638,52.61833,0 47.43831999999999,52.56722,0 47.20610000000001,52.56666,0 47.1536,52.60999,0 47.0686,52.56638,0 46.9461,52.63832000000001,0 46.83077,52.6164,0 46.61694000000001,52.66109,0 46.61388000000002,52.72748,0 46.58027,52.73249,0 46.39999,52.67249,0 46.29834,52.68055,0 46.2761,52.61805,0 46.15331,52.60194000000001,0 46.14582,52.54972000000001,0 46.08749,52.56388,0 46.06444,52.52693000000001,0 45.97137000000001,52.49583,0 46.01639000000001,52.45776999999999,0 45.97526000000001,52.4361,0 45.98082000000001,52.40721000000001,0 45.71805,52.45889,0 45.73554000000001,52.52583,0 45.64111000000001,52.52028,0 45.52693,52.48166,0 45.51639000000001,52.43444,0 45.44194,52.40055,0 45.11139,52.41109,0 45.06916,52.37778000000001,0 45.10139000000001,52.33332000000001,0 45.05054000000001,52.31721,0 44.97332,52.37332,0 44.90554,52.37665000000001,0 44.86749,52.44666,0 44.66489,52.47935,0 44.54805,52.55249,0 44.49361,52.52665,0 44.53943,52.48638,0 44.51445,52.45916,0 44.54361,52.42944,0 44.41082,52.41305000000001,0 44.38916,52.36833000000001,0 44.33332,52.34805000000001,0 44.34638,52.3161,0 44.05526,52.34610000000001,0 43.77749,52.43472,0 43.34055,52.39111,0 43.3086,52.44555,0 43.19027,52.46666000000001,0 43.12276,52.40192,0 43.0798,52.40288,0 43.00916000000001,52.36388,0 42.92527,52.37500000000001,0 42.92221,52.30526,0 42.75333,52.18581,0 42.74749,52.08027,0 42.8111,52.01027,0 42.70305,51.98194,0 42.67638,51.83972,0 42.48666000000001,51.69305,0 42.51471,51.62388,0 42.48951,51.60789,0 42.86166,51.38166,0 42.83942,51.31666000000001,0 42.91221,51.24083,0 42.87309,51.23142,0 42.68444,51.23693000000001,0 42.56722,51.16526000000001,0 42.41888,51.17222,0 42.2672,51.13166,0 42.09888,51.14750000000001,0 41.96333,51.2086,0 41.8686,51.19388000000001,0 41.7286,51.01721,0 41.41193,50.90359,0 41.37777000000001,50.87666000000001,0 41.38277,50.83027,0 41.15639,50.77638,0 41.32777,50.71916,0 41.40749,50.61944,0 41.52833,50.60166,0 41.52416,50.56860000000001,0 41.45471,50.55138,0 41.4175,50.50527,0 41.5125,50.3811,0 41.36221,50.22629,0 41.4822,50.09027,0 41.5911,50.05525999999999,0 41.58277,50.005,0 41.61361000000001,49.96361,0 41.8661,49.90777000000001,0 42.17749,49.69083,0 42.11277,49.52583,0 42.0736,49.51056,0 42.04972,49.40499000000001,0 42.09693,49.29222,0 42.03194,49.26639999999999,0 42.07694,49.19971,0 42.06443,49.15388,0 42.34443,49.15249,0 42.39277,49.04972,0 42.48388,49.04472,0 42.66361,48.96777,0 42.66249,48.8886,0 42.74610000000001,48.80638,0 42.65027,48.70527,0 42.70166,48.61805,0 42.63054,48.54694,0 42.27665,48.49860000000001,0 42.07249,48.40889,0 42.15193,48.28221,0 42.04499,48.25916000000001,0 42.10278,48.16389000000002,0 42.05111,48.12972,0 42.02193,48.03027,0 42.27555,48.01305000000001,0 42.32638,48.04777,0 42.4986,48.01056,0 42.7286,47.85556,0 42.93971,47.51361,0 42.98776,47.51388000000001,0 43.05915,47.57639,0 43.13554000000001,47.57556,0 43.15998,47.48249,0 43.22499,47.44972,0 43.55833000000001,47.48972,0 43.65331,47.44222,0 43.73177,47.47655000000001,0 43.62944,47.55916,0 43.70583,47.68444,0 43.84138,47.7575,0 43.97499,47.76639,0 44.04443,47.82415,0 44.01582,47.86388,0 44.06582000000001,47.9108,0 44.38915,47.86832,0 44.3861,47.91415,0 44.46776,47.9786,0 44.46637,48.04084000000001,0 44.32305,48.05999000000001,0 44.30944,48.25999000000001,0 44.53582,48.20638000000002,0 44.54971,48.05833,0 44.61221,48.05665,0 44.64415000000001,48.09166,0 44.80053,48.05249000000001,0 44.94221,48.14388000000002,0 44.92471,48.19916000000001,0 44.95666,48.21194,0 45.12749000000001,48.11306,0 45.30082000000001,48.09768,0 45.39499,48.09833,0 45.55193000000001,47.99193,0 45.7136,48.03750000000002,0 45.74166,48.00222,0 45.70664000000001,47.93193000000001,0 45.71970000000001,47.86528,0 45.78556,47.81305,0 45.82916,47.72304,0 45.90720000000001,47.7122,0 45.93804,47.62887,0 46.08887000000001,47.51692,0 46.29666000000001,47.43664,0 46.3886,47.44443,0 46.68081,47.63248,0 46.83055,47.5761,0 46.52387000000001,47.43499,0 46.51554,47.39193000000001,0 46.79639,46.99638,0 46.92777,46.97832,0 46.99082,46.85082,0 47.12721,46.85833000000001,0 47.22776,46.74081,0 47.14026,46.69638,0 47.1497,46.66832,0 46.99805,46.63361,0 46.91332,46.55526,0 46.94916,46.53943000000001,0 46.96194,46.4286,0 46.66027,46.42804,0 46.47833,46.27555,0 46.86665,46.24805000000001,0 46.8336,46.07277,0 47.09082000000002,46.1186,0 47.175,46.10556,0 47.10915,46.00777,0 47.0411,45.96970000000001,0 46.89304000000001,45.57472000000001,0 47.29361000000001,45.6436,0 47.35971,45.68942,0 47.46748,45.63943,0 47.48619,45.57145,0 47.54332,45.50527000000001,0 47.50166,45.47998,0 47.48611,45.41860000000001,0 47.37803,45.41137000000001,0 47.46277000000001,45.38081,0 47.38554000000001,45.29527,0 47.3761,45.18193000000001,0 47.29193,45.18082,0 47.19915000000001,44.96859,0 47.14861,44.92694,0 47.09722000000001,44.94971,0 47.11666,44.86221,0 47.07555,44.78277,0 46.99165,44.92192000000001,0 47.00944,44.82111,0 46.95943,44.76389,0 46.71249,44.64777,0 46.72444,44.54472,0 46.67971,44.52304,0 46.7,44.44276,0 46.76027,44.39192,0 47.01222,44.36083,0 47.09248,44.24027,0 47.1586,44.20054,0 47.20554,44.21859,0 47.47952,44.4864,0 47.5322,44.50008,0 47.80731,43.98692,0 47.69832000000001,43.86887000000001,0 47.58498,43.72221,0 47.48721,43.45638,0 47.48832,43.29361000000001,0 47.57249000000001,43.33054,0 47.58833000000001,43.29695,0 47.48333000000002,43.20777,0 47.46221,43.01915,0 47.69694000000001,42.86638,0 47.73832,42.63388,0 47.85942,42.59389,0 47.94498,42.46249,0 48.11776,42.32638,0 48.3036,42.09748,0 48.38665,41.92638,0 48.54472,41.88028,0 48.58395000000001,41.83577,0 48.47137000000001,41.72359000000001,0 48.3997,41.58914,0 48.23137000000001,41.50249,0 48.07444000000001,41.46776,0 48.02276,41.36193,0 47.96249,41.35943,0 47.90831000000001,41.29084,0 47.91999,41.22637,0 47.76693,41.19609000000001,0 47.65972000000001,41.23554,0 47.57166,41.2111,0 47.52193,41.25416,0 47.28555000000001,41.31219,0 47.25888000000001,41.42471,0 47.16054,41.5611,0 47.04305000000001,41.55833,0 46.92554,41.71776,0 46.77081,41.79666,0 46.76081,41.86388,0 46.59166000000001,41.79221,0 46.56667000000001,41.80777,0 46.56833,41.88248,0 46.45176000000001,41.89705,0 46.39415,41.94054,0 46.31998,41.93888,0 46.23998,42.00139,0 46.10389,41.99415,0 46.07805,42.03416,0 46.01082,42.04305,0 45.96053,42.02387,0 45.91028,42.105,0 45.64748,42.20471,0 45.76028,42.47791,0 45.55277,42.55026,0 45.35167,42.52943,0 45.17137000000001,42.70083,0 45.05304,42.69415,0 44.92749,42.76193,0 44.855,42.74331,0 44.80388000000001,42.6172,0 44.68443,42.73943000000001,0 44.54972,42.75944000000001,0 44.28665,42.71278,0 44.22082000000001,42.63693,0 44.0536,42.61804,0 43.9611,42.55778,0 43.73555,42.63916,0 43.8361,42.72776,0 43.82721,42.7514,0 43.67083,42.7911,0 43.56055,42.86082,0 43.20083,42.93637,0 43.01082,43.05666,0 43.0211,43.10444,0 42.85722000000001,43.17748,0 42.7011,43.1825,0 42.62583,43.14305,0 42.41526,43.23916,0 42.18111,43.23555,0 42.05777,43.18610000000001,0 41.93971,43.21749,0 41.58971,43.22275,0 41.39332,43.3461,0 41.29554,43.33610000000001,0 41.16805,43.38722,0 41.04416000000001,43.37498000000001,0 40.68471,43.54583,0 40.54306,43.50861,0 40.25339,43.58252,0 40.12665,43.57222,0 40.0775,43.54694,0 40.00297,43.37926,0 40.22471,43.31443,0 40.32638,43.14192,0 40.38472,43.16555,0 40.58665,43.0825,0 40.8436,43.06777,0 40.93277,42.97222,0 41.03276000000001,42.97304,0 41.15583,42.78917,0 41.22082,42.79861,0 41.46137,42.70804,0 41.55331,42.38581000000001,0 41.63277,42.27194,0 41.66166,42.12109,0 41.7625,41.99221,0 41.77472,41.88563,0 41.73026000000001,41.72555,0 41.53155,41.52388,0 41.3875,41.37304000000001,0 40.93111,41.18748,0 40.83305,41.19498,0 40.54332,41.02916,0 40.45804,41.0425,0 40.12859,40.91639,0 39.99776,40.97582,0 39.87721,40.95389,0 39.73333,41.01388000000001,0 39.64611,41.00166,0 39.4211,41.10944,0 39.2786,41.04972,0 39.16304,41.0825,0 38.81611,41.00999,0 38.70999,40.95,0 38.62554,40.97305,0 38.5236,40.92083,0 38.36139,40.90943000000001,0 38.2775,40.95056,0 37.89249,40.98277,0 37.79361000000001,41.04554,0 37.78944,41.11555,0 37.70638,41.11222,0 37.68748,41.13722,0 37.60777,41.04777,0 37.53721,41.02805,0 37.29499,41.14389,0 37.15228,41.14491,0 37.04332,41.17555000000001,0 37.02333,41.26749,0 36.85693,41.34721,0 36.61834,41.37498,0 36.5541,41.28566,0 36.53862,44.90856,0 36.54507,45.17941,0 36.67405,45.41802,0 37.64138,46.73358000000001,0 38.23582,47.10943,0 38.2161,47.19471,0 38.31916,47.27833,0 38.20804,47.31916,0 38.28555,47.3961,0 38.28083,47.5061,0 38.33942,47.60139000000001,0 38.60582,47.63803,0 38.69137,47.69415000000002,0 38.75055,47.68387000000001,0 38.83832,47.85278000000001,0 39.03817,47.8658,0 39.13638,47.83388999999999,0 39.34526,47.85304,0 39.72388,47.81833,0 39.79639,47.85722,0 39.81998,47.9697,0 39.77609,48.02639000000001,0 39.86859,48.04084000000001,0 39.84832,48.11306,0 39.93277,48.21332,0 40.00249,48.24304000000001,0 40.0125,48.28472,0 39.91277,48.27777,0 39.83805,48.3125,0 39.92152,48.36839,0 39.83833,48.48388000000001,0 39.86694,48.50027000000001,0 39.85333,48.55471000000001,0 39.66966,48.58764000000002,0 39.65693,48.61667,0 39.72721,48.76221,0 39.80389,48.81777000000001,0 39.98526,48.79139,0 40.07667,48.87500000000001,0 40.03027,48.8961,0 39.97055,48.85999000000001,0 39.76721,48.90388,0 39.69444,48.97305,0 39.69915,49.02277,0 39.93888,49.05499,0 39.96638,49.12388,0 40.16694,49.2486,0 40.17972000000001,49.32695,0 40.05305,49.47833000000001,0 40.07444,49.52694,0 40.14111,49.55916,0 40.13977,49.60105000000001,0 40.005,49.61444000000001,0 39.92167,49.56555,0 39.79695000000001,49.55305,0 39.63972,49.61555,0 39.58083,49.73082,0 39.27361,49.75194,0 39.18373,49.88041,0 39.06916,49.81777,0 38.94526,49.8075,0 38.88916,49.86805,0 38.69609,49.92749,0 38.68665,49.97887,0 38.49889,49.95193,0 38.3761,49.98749,0 38.30777,50.07388,0 38.18415,50.06554000000001,0 38.18526,49.9511,0 38.01306,49.90138000000001,0 37.92527,50.02471,0 37.73304,50.08777000000001,0 37.60527,50.21666,0 37.62360000000001,50.30527,0 37.49444,50.35139000000001,0 37.4586,50.43971000000001,0 37.31583,50.42804,0 37.13054,50.34833,0 36.88444,50.33583000000001,0 36.7011,50.26693000000001,0 36.64028,50.21249000000001,0 36.56249,50.22249,0 36.56333000000001,50.27639,0 36.41276,50.32166,0 36.29749,50.28444,0 36.13917,50.42527,0 35.85028,50.4286,0 35.73915,50.38333,0 35.72887,50.35332,0 35.64471,50.35416,0 35.58305,50.38305,0 35.5672,50.45306,0 35.44106,50.51198,0 35.39355,50.63808,0 35.48857,50.67585,0 35.45829,50.69417,0 35.45663,50.77279,0 35.39773,50.80308,0 35.3605,50.87226,0 35.36133,50.92614,0 35.30577000000001,50.96033,0 35.29716000000001,50.99979,0 35.35855,51.01088000000001,0 35.37189,51.04144,0 35.17076,51.0548,0 35.11852,51.20481,0 35.07381,51.22149,0 34.73499,51.17249,0 34.66999,51.17944,0 34.63361,51.24222,0 34.38221,51.26361000000001,0 34.30526,51.23082000000001,0 34.20892,51.25919,0 34.27388,51.28555,0 34.31499,51.35111,0 34.20361,51.40666,0 34.23916,51.42777000000001,0 34.23417,51.54666,0 34.15972,51.6286,0 34.10111,51.64749,0 34.10277,51.67443,0 34.40554,51.7186,0 34.44054,51.76721,0 34.38082,51.84499,0 34.25777,51.87944000000001,0 34.16945,51.95999,0 34.10943,51.95972,0 34.12804,51.98972000000001,0 34.07306,52.06028000000001,0 34.09778,52.15194,0 33.83056,52.36472000000001,0 33.69638,52.36166999999999,0 33.53528,52.29333000000001,0 33.49778,52.36666000000002,0 33.44887,52.34361,0 33.20471,52.37972,0 32.92361,52.24722,0 32.39415,52.33526,0 32.35833,52.30999,0 32.38638,52.25472,0 32.34055000000001,52.22137,0 32.33554,52.1572,0 32.22499,52.07944,0 32.12444,52.04388000000001,0 31.92638,52.03667,0 31.89777,52.09610000000001,0 31.78388,52.10805000000001,0 31.7825,52.16582,0 31.70389,52.1986,0 31.71694,52.26167000000001,0 31.58666,52.31888,0 31.62972,52.37054,0 31.60166,52.40610000000001,0 31.60889,52.49138,0 31.56472000000001,52.51583,0 31.59527,52.53221,0 31.58167,52.55083,0 31.65332,52.55305,0 31.5,52.68999,0 31.595,52.73804,0 31.58083,52.80304,0 31.53695,52.8561,0 31.41527,52.89305,0 31.35638,52.97805000000001,0 31.2664,53.02193,0 31.38638,53.09916,0 31.37483,53.12193000000001,0 31.4225,53.20888000000001,0 31.61611,53.21832,0 31.77555,53.19415000000002,0 31.80111,53.12583,0 31.93054,53.09554,0 32.22332,53.10555,0 32.22554,53.13333,0 32.29889,53.13193,0 32.43999,53.20888000000001,0 32.49833,53.27832,0 32.46249,53.30832,0 32.73693,53.34249,0 32.7411,53.46305000000001,0 32.66082,53.46666,0 32.67054,53.49722,0 32.5975,53.4911,0 32.44389,53.57194000000001,0 32.41471,53.64638,0 32.51056,53.68361,0 32.45166,53.72638,0 32.37694,53.71888,0 32.32944,53.76222000000001,0 32.11971,53.81999,0 31.90222,53.77777,0 31.76423000000001,53.80262,0 31.86083,53.96333,0 31.84416,54.06444000000001,0 31.79333,54.05305000000001,0 31.76167,54.10333,0 31.54416,54.14333,0 31.33166,54.2375,0 31.31167,54.35083,0 31.23194,54.45665,0 31.1986,54.46093000000001,0 31.0825,54.50556,0 31.18443,54.60609,0 31.18527,54.64222,0 31.02472,54.65222,0 31.005,54.71167,0 30.78,54.79694000000001,0 30.845,54.8811,0 30.83388,54.94415,0 30.94749,54.96915,0 30.92499,55.03333,0 31.02971,55.04332,0 30.98722,55.11166000000002,0 31.00472,55.14332,0 30.81361,55.29333000000001,0 30.93943,55.39944000000001,0 30.90055,55.45915999999999,0 30.93999,55.49583,0 30.91888,55.60833000000001,0 30.75666,55.59665,0 30.72694,55.65639,0 30.60889,55.68138,0 30.61249,55.72027,0 30.49416,55.76388000000001,0 30.48971,55.79999,0 30.22611,55.8661,0 30.14055,55.82804000000002,0 29.92833,55.85167000000001,0 29.82306,55.77528,0 29.71916,55.78249000000002,0 29.49388,55.68888,0 29.35916,55.78943,0 29.40693,55.89582999999999,0 29.46443,55.91109,0 29.41222,55.95777,0 29.05943,56.03278,0 28.86944,55.98055,0 28.84694,55.94249000000001,0 28.75416,55.95388000000001,0 28.64611,56.09361000000001,0 28.3875,56.0961,0 28.3125,56.04999,0 28.16801,56.15015,0 28.23777,56.2711,0 28.18444,56.37471000000001,0 28.18972,56.44055,0 28.11306,56.50555000000001,0 28.1525,56.55443000000001,0 28.03639,56.59054,0 28.005,56.69137,0 27.8861,56.74722,0 27.93527,56.82721,0 27.81555,56.87833000000001,0 27.64,56.84805,0 27.7775,57.06582,0 27.70138,57.11916000000001,0 27.84082,57.16361,0 27.86111,57.30222000000001,0 27.52722,57.4386,0 27.54722,57.53638000000001,0 27.37205,57.53564,0 27.34806,57.59554000000001,0 27.40583000000001,57.61361000000001,0 27.41306,57.69055,0 27.52666,57.72276,0 27.51639,57.77333,0 27.57277,57.80277000000002,0 27.54583,57.81777,0 27.75306000000001,57.83916,0 27.82306,57.87388,0 27.68388,57.92638,0 27.69722,58.00082,0 27.64222,58.02083,0 27.67721,58.07417000000002,0 27.55417,58.13276000000001,0 27.47416,58.21388000000001,0 27.46471,58.29777,0 27.53916,58.36166,0 27.5425,58.41389,0 27.42611,58.81361,0 27.4936,58.88193,0 27.72499,58.98777,0 27.91254,59.27182,0 28.17035,59.30978,0 28.19389,59.37582,0 28.01583000000001,59.4786,0 27.80783,60.5464,0 28.38888,60.88667000000002,0 28.52916000000001,60.95499,0 28.63888,60.96611,0 28.81277,61.10334,0 29.21361,61.25332,0 29.48639,61.42611000000002,0 29.52555,61.48277,0 29.80916,61.60833,0 29.83222,61.65305000000002,0 29.99138,61.72943,0 30.64972,62.195,0 30.94777,62.31138000000001,0 31.25972000000001,62.50777000000002,0 31.4675,62.80639,0 31.58893,62.91440999999999,0 31.515,62.98693,0 31.28222,63.10917,0 31.21972,63.22306,0 30.9625,63.30972000000001,0 30.79639,63.40194,0 30.48166000000001,63.47861,0 29.99671,63.74802,0 30.25584,63.81778,0 30.345,63.90972000000001,0 30.59528,64.04694000000001,0 30.61444,64.08665000000001,0 30.54805,64.17471000000001,0 30.57722,64.21581999999999,0 30.56084,64.24442999999999,0 30.45416000000001,64.25694,0 30.38972,64.30943000000001,0 30.15611000000001,64.35415999999999,0 30.05222,64.40470999999999,0 30.08722,64.48305000000001,0 30.00967,64.51286,0 29.97500000000001,64.58110000000001,0 30.19917,64.65028,0 30.19713000000001,64.68622000000001,0 30.12055000000001,64.73054999999999,0 30.14083,64.77415000000001,0 29.80471,64.79389000000001,0 29.63667,64.92805,0 29.60666,65.00166,0 29.62889,65.06305,0 29.87388,65.12082,0 29.82333,65.14888999999999,0 29.87028,65.18747999999999,0 29.85361,65.21612,0 29.59638,65.24887,0 29.7475,65.34193000000001,0 29.74611,65.51443,0 29.85971,65.56610000000001,0 29.72194,65.63805000000001,0 30.02444,65.69110000000001,0 30.12388,65.66499,0 30.13528000000001,65.70359999999999,0 30.09667,65.83333,0 29.97695,65.9811,0 29.91028,66.12694000000001,0 29.63861,66.32249,0 29.56792,66.42856999999999,0 29.54833,66.49554000000001,0 29.42056,66.58498,0 29.35027,66.68249,0 29.09333,66.85138000000001,0 29.08305,66.9586,0 29.54611,67.31055000000001,0 29.93279,67.51389,0 29.97778,67.57249,0 29.98972,67.67194000000001,0 30.02861,67.69472,0 29.69167,67.81527,0 29.35695,68.08249000000001,0 28.69334,68.19749,0 28.4575,68.53194000000001,0 28.73861,68.73416,0 28.82056,68.84444000000001,0 28.73778,68.87582,0 28.47917,68.8736,0 28.43195,68.89694,0 28.95734,69.05162,0 29.06333,69.02916999999999,0 29.23916,69.1075,0 29.32167,69.24554000000001,0 29.31,69.31527,0 29.945,69.43053999999999,0 30.09777,69.50333000000001,0 30.185,69.58275999999999,0 30.14277,69.62833000000001,0 30.07277,69.6311,0 30.10333,69.66306,0 30.20361,69.66388000000001,0 30.52916,69.5461,0 30.91,69.54886999999999,0 30.94583,69.58222000000001,0 30.95472,69.68138,0 30.89805,69.70555,0 30.85484,69.7923,0 31.17443,70.36256,0 31.92259,70.01843,0 32.28514,69.92385,0 33.13635,69.77410999999999,0 33.48315,69.49036,0 35.31956,69.30909,0 36.23384,69.15934,0 37.16386,68.89137,0 37.5369,68.86902000000001,0 37.56522,73.71208,0 37.59446,78.71200000000002,0 35.86762,78.71200000000002,0 35.86762,81.47494,0 37.5369,81.47494,0 37.5369,84.90001,0 37.5369,89.90000000000001,0 38.53421000000001,89.90000000000001,0 39.53152,89.90000000000001,0 40.52883,89.90000000000001,0 41.52613,89.90000000000001,0 42.52345,89.90000000000001,0 43.52076,89.90000000000001,0 44.51808,89.90000000000001,0 45.51538,89.90000000000001,0 46.51269,89.90000000000001,0 47.51001000000002,89.90000000000001,0 48.50732,89.90000000000001,0 49.50463,89.90000000000001,0 50.50193000000001,89.90000000000001,0 51.49924,89.90000000000001,0 52.49656000000001,89.90000000000001,0 52.49656000000001,84.90001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +03:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(3.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0350</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +44.81304,39.63081,0 44.8886,39.60582,0 44.95028000000001,39.43582,0 45.06304999999999,39.37305,0 45.13055000000001,39.28556,0 45.14193,39.21748,0 45.30999000000001,39.19832,0 45.43499000000001,39.0511,0 45.43941000000001,38.99804,0 45.87194000000001,38.87999,0 46.05637,38.87831,0 46.17825000000001,38.84114000000001,0 46.35471,38.91054,0 46.54037,38.87559,0 46.83832000000001,39.15527000000001,0 46.94886,39.15443,0 47.02556,39.18942,0 47.09971,39.30444,0 47.29221,39.37693,0 47.39166,39.47054000000001,0 47.53334,39.50888000000001,0 47.80666,39.67722,0 47.93055,39.68359,0 47.97667000000001,39.71922,0 48.35979,39.38522,0 48.18916000000001,39.33332,0 48.11998,39.26361,0 48.13499000000001,39.20888,0 48.29387000000001,39.11249,0 48.32776,39.05165,0 48.31248,39.00776,0 48.07138000000001,38.95138,0 48.01415,38.90583,0 48.02081,38.83554,0 48.24387,38.72776,0 48.24361,38.66721,0 48.32776,38.60333,0 48.44193,38.62165,0 48.45416,38.56471,0 48.62305000000001,38.39526,0 48.78943000000001,38.44777,0 48.88828000000001,38.44241,0 48.86942,38.30054,0 48.94166,37.90749,0 48.99776,37.76361,0 49.10248,37.64304,0 49.41999,37.49332,0 49.83443,37.4511,0 49.92499,37.47971,0 49.96888,37.45387,0 49.94166,37.44471,0 50.19137000000001,37.38917,0 50.35166,37.1211,0 51.10111,36.72887,0 51.97276000000002,36.57999,0 53.22305,36.84833,0 53.92276,36.90972,0 54.00860999999999,36.94833,0 53.98499,36.89861000000001,0 53.6075,36.86833000000001,0 53.66137000000001,36.85777,0 53.63388000000001,36.84804,0 53.68194,36.81722,0 53.94332,36.8,0 54.0286,36.82471,0 54.03166,36.94833,0 53.98833,36.9822,0 54.01583000000001,37.02193000000001,0 53.90564,37.35085,0 54.23165000000001,37.32722,0 54.55165,37.4461,0 54.68111,37.4436,0 54.78694,37.51915000000001,0 54.80972000000001,37.57944,0 54.78221000000001,37.64804,0 54.83306000000001,37.74639,0 54.95471000000001,37.80499,0 55.14083,37.9561,0 55.21027,37.96221,0 55.44276000000001,38.08611,0 55.7561,38.12526,0 55.98276000000001,38.07249,0 56.16444,38.09499,0 56.22305,38.07028,0 56.33305000000001,38.08554,0 56.35277,38.13889,0 56.32249000000001,38.18526,0 56.44276,38.25999,0 56.61388,38.24055,0 56.75574000000001,38.28626,0 56.84583,38.23166000000001,0 57.05861,38.1936,0 57.14979,38.26009,0 57.18888,38.28334,0 57.20471,38.28334,0 57.24388,38.27082,0 57.28833,38.17055,0 57.3711,38.09305,0 57.35306,37.98944,0 57.45028000000001,37.93916,0 57.70805,37.92665,0 58.04694000000001,37.80944,0 58.17443,37.79639,0 58.20304000000001,37.78334000000001,0 58.22748,37.68111,0 58.38915999999999,37.63611,0 58.49805,37.64777,0 58.5625,37.7086,0 58.71277000000001,37.64861,0 58.80444,37.66693,0 58.82278,37.70361,0 59.06027,37.62999,0 59.25,37.51333,0 59.33888,37.53916000000001,0 59.38082,37.48555,0 59.39305000000001,37.32306,0 59.49139,37.24833,0 59.47693,37.21889000000001,0 59.56693,37.20888,0 59.62027000000001,37.13194,0 59.67054000000001,37.15611,0 59.81305,37.12471000000001,0 59.91471000000001,37.0586,0 60.02944,37.03695,0 60.27840000000001,36.76272,0 60.33306000000001,36.6561,0 61.1572,36.64999,0 61.18999,36.56777,0 61.15028,36.31499,0 61.18444,36.28667,0 61.22916,36.12666,0 61.16471,36.03527,0 61.16999,35.99527,0 61.12428,35.97565,0 61.22582,35.90388,0 61.26222,35.81972,0 61.22887,35.67805,0 61.27655000000001,35.60725,0 61.28778000000001,35.54999,0 61.19444,35.40222,0 61.19276000000001,35.30583,0 61.10444,35.27916,0 61.09471,35.18416,0 61.14249000000001,35.09832,0 61.06972,34.92888000000001,0 61.06305,34.80888,0 60.98333000000001,34.73388,0 60.97137,34.64444,0 60.72165,34.52222,0 60.91527000000001,34.32,0 60.66554,34.31249,0 60.67555000000001,34.28833,0 60.54610000000001,34.19638,0 60.50444,34.12222,0 60.55417000000001,33.81332,0 60.50527,33.73915,0 60.52776999999999,33.64415,0 60.66610000000001,33.5675,0 60.88639,33.55721,0 60.94305,33.51944,0 60.85443,33.48999,0 60.85167,33.41805,0 60.59109,33.16305,0 60.58056,33.0886,0 60.85778000000001,32.23471,0 60.83665,32.09027,0 60.79694000000001,32.0375,0 60.84389,31.49833,0 61.10417,31.46305,0 61.71361000000001,31.38332000000001,0 61.7711,31.31833,0 61.76466000000001,31.24477,0 61.8511,31.02111,0 61.80403,30.94514,0 61.78652,30.80953,0 60.86630000000002,29.86366,0 61.31443000000001,29.39583,0 61.3586,29.38444000000001,0 61.36027,29.26583000000001,0 61.42471,29.15528000000001,0 61.51111,29.08861,0 61.48804000000001,29.04361,0 61.5336,29.0125,0 61.58138000000001,28.88083,0 61.90554,28.555,0 62.40499,28.41722,0 62.59249,28.2336,0 62.78138,28.26695,0 62.75777,28.00027,0 62.82193,27.76027,0 62.83972,27.47443000000001,0 62.77833,27.35333,0 62.80694,27.32332,0 62.79583,27.28555,0 62.76471,27.27194,0 62.82305000000002,27.23027,0 62.93333000000001,27.21333,0 63.20583,27.2675,0 63.28832,27.21861,0 63.34194,27.1225,0 63.27859,27.12194,0 63.25305,27.08361,0 63.25916,26.93443,0 63.29056,26.89222,0 63.20638,26.84222,0 63.17638000000001,26.63111,0 62.77276000000001,26.64972,0 62.74305,26.61167,0 62.44609,26.56888,0 62.32027000000001,26.51527,0 62.27138,26.42472,0 62.27803000000001,26.35402,0 62.13583000000001,26.38083,0 62.12887,26.34444,0 62.10388,26.31444,0 61.87610000000001,26.24638,0 61.84416,26.21166,0 61.77583000000001,25.81861,0 61.68638,25.795,0 61.70361000000001,25.67667000000001,0 61.66859000000001,25.63277,0 61.64695,25.30611,0 61.61103000000001,25.19764,0 61.54971,25.25,0 61.55388,25.19749,0 61.48611,25.19722,0 61.51667,25.15472,0 61.49527,25.11527,0 61.42305,25.0975,0 61.4386,25.07528,0 61.22499,25.11861,0 61.17305000000002,25.17444,0 60.62222,25.26806,0 60.60277000000001,25.40028,0 60.54722,25.44167,0 60.45499,25.43166,0 60.39528,25.37555,0 60.46111,25.26361,0 60.30721,25.32945,0 60.29361,25.37832,0 60.19694,25.36528,0 60.18249000000002,25.32111,0 60.07111,25.37916,0 59.89388,25.345,0 59.83889,25.41082,0 59.59916,25.39554,0 59.47805,25.47722,0 59.247,25.42619,0 59.02583000000001,25.3975,0 58.9325,25.505,0 58.81833,25.56,0 58.40895,25.59111,0 58.32286,25.57843,0 58.13499,25.54138,0 58.04554,25.57695,0 57.95777,25.70027,0 57.77694,25.62806,0 57.78695,25.69833,0 57.74805,25.74554,0 57.6261,25.73027,0 57.31806,25.77083,0 57.27138,25.91944,0 57.16721,26.07222,0 57.21388,26.1725,0 57.12579,26.26425,0 57.09309,26.29857,0 57.0935,26.38464,0 57.08777,26.63972,0 57.02138,26.85222,0 56.92694,26.95166,0 56.9711,26.96388,0 56.96970000000001,27,0 56.92389,26.98249,0 56.84196,27.03251,0 55.89573,26.62316,0 55.33095,26.23366,0 54.55681,25.86849,0 54.48864,25.8831,0 54.47404000000001,26.26287,0 53.14484,26.8033,0 52.56544,27.35835000000001,0 52.52133,27.48647,0 51.54147,27.7537,0 51.29651,27.95413,0 50.27212,29.1344,0 50.0717,29.82476,0 49.55681000000001,29.98495000000001,0 49.3535,30.13924,0 49.22824000000001,30.14287,0 48.59428,29.94093000000001,0 48.54556,29.96302,0 48.45333000000001,29.99805,0 48.38777000000001,30.13694,0 48.40781,30.21121,0 48.27964000000001,30.33528,0 48.1992,30.32573,0 48.16043,30.42708,0 48.03249,30.49138,0 48.03638,30.99749,0 47.69388000000001,31.00111,0 47.69721000000001,31.40778,0 47.86445,31.79861,0 47.79056000000001,31.86,0 47.68971000000001,32.02388,0 47.5075,32.15416,0 47.52887,32.22887,0 47.41332,32.34109,0 47.43416,32.39778,0 47.3561,32.47638,0 47.27499000000001,32.49194,0 47.1375,32.46305,0 46.73944000000001,32.75999,0 46.42388,32.93749,0 46.10722,32.96749,0 46.0975,33.00555,0 46.14972,33.06527,0 46.05416000000001,33.08805,0 46.05138000000001,33.11832,0 46.11138,33.11277,0 46.19944,33.19582,0 46.17555000000001,33.2686,0 46.05027000000001,33.37778,0 46.00647000000001,33.48579,0 45.87471,33.49166,0 45.9486,33.55665,0 45.89666,33.60139,0 45.90659000000001,33.63655,0 45.74694,33.59166,0 45.75527000000001,33.63361,0 45.68888,33.67499,0 45.50804,33.94249,0 45.40582,33.9711,0 45.46888,34.07917,0 45.56276,34.14054,0 45.54695000000002,34.18833,0 45.58472,34.30249,0 45.53555,34.35111,0 45.48777000000001,34.33583,0 45.43555000000001,34.45528,0 45.52555,34.50249,0 45.52473000000001,34.6032,0 45.61472,34.55999,0 45.72220999999999,34.55665,0 45.70833,34.65915,0 45.6511,34.72694,0 45.68860000000002,34.81528,0 45.76611,34.84583,0 45.77249,34.91444,0 45.86776,34.9036,0 45.89555,34.9636,0 45.8786,35.03527,0 45.92193000000001,35.05443,0 45.93378,35.09876,0 46.03944,35.05833,0 46.16638,35.10722,0 46.15331,35.1611,0 46.19305,35.21111,0 46.11916000000001,35.24444,0 46.14805,35.29746,0 45.99221,35.48109,0 46.0061,35.53778,0 45.97999000000001,35.58471,0 46.02388,35.57999,0 46.02944,35.68776,0 46.2686,35.72805,0 46.34444,35.82193,0 46.18166000000001,35.80417,0 46.08749,35.86639,0 45.76583000000001,35.81194,0 45.66332000000001,35.94054,0 45.55777000000001,36.0022,0 45.50555000000001,36.02055,0 45.34916,35.99055,0 45.33165,36.01416,0 45.36832,36.08637,0 45.32027000000001,36.15609,0 45.31971,36.23832,0 45.27582,36.25776,0 45.30332000000001,36.28083,0 45.27303000000001,36.30859,0 45.27916,36.3811,0 45.23916,36.43137,0 45.09415,36.42443,0 45.01471000000002,36.53527,0 45.06666000000001,36.68054,0 45.01277000000001,36.7536,0 44.84915,36.79832,0 44.90664,36.88832,0 44.88416,36.96166,0 44.90972000000001,37.02388,0 44.78499,37.08472,0 44.76554,37.11694,0 44.78733,37.1497,0 44.77332,37.22748,0 44.82193,37.2711,0 44.81527,37.30526,0 44.72499,37.38361,0 44.58859,37.44026,0 44.6136,37.60277,0 44.55804,37.64803,0 44.61805,37.72776,0 44.52693,37.78555,0 44.45609,37.77471,0 44.45193,37.81778,0 44.41916,37.81778,0 44.39333,37.86304,0 44.22398000000001,37.89915,0 44.26416,38.01416,0 44.38778,38.16583,0 44.40471000000001,38.27166,0 44.48416,38.34554,0 44.43471000000001,38.39555,0 44.38081,38.36971,0 44.30693000000001,38.38833,0 44.31388,38.63777,0 44.26333,38.66639,0 44.28276,38.69777,0 44.26082,38.72165,0 44.30332000000001,38.83444,0 44.20805,38.89581,0 44.15943,39.00221,0 44.20499,39.04165,0 44.18833,39.08306,0 44.21583,39.14083,0 44.09833,39.20638,0 44.09581,39.29138,0 44.03416,39.38499,0 44.20054,39.41694,0 44.31553000000001,39.3875,0 44.4161,39.42527,0 44.47108,39.69886,0 44.60582,39.78054000000001,0 44.81304,39.63081,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +04:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(4);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0400</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +54.48864,25.8831,0 54.55681,25.86849,0 55.33095,26.23366,0 55.89573,26.62316,0 56.84196,27.03251,0 56.92389,26.98249,0 56.96971,27,0 56.9711,26.96388,0 56.92694,26.95166,0 57.02138,26.85222,0 57.08777,26.63972,0 57.0935,26.38464,0 57.09309,26.29857,0 57.12579,26.26425,0 57.21388,26.1725,0 57.16721,26.07222,0 57.27138,25.91944,0 57.31806,25.77083,0 57.6261,25.73027,0 57.74805,25.74554,0 57.78695,25.69833,0 57.77694,25.62806,0 57.95777,25.70027,0 58.04554,25.57695,0 58.13499,25.54138,0 58.32286,25.57843,0 58.40895,25.59111,0 58.81833,25.56,0 58.9325,25.505,0 59.02583000000001,25.3975,0 59.247,25.42619,0 59.47805,25.47722,0 59.59916,25.39554,0 59.83889,25.41082,0 59.89388,25.345,0 60.07111,25.37916,0 60.18249000000002,25.32111,0 60.19694,25.36528,0 60.29361,25.37832,0 60.30721,25.32945,0 60.46111,25.26361,0 60.39528,25.37555,0 60.45499,25.43166,0 60.54722,25.44167,0 60.60277000000001,25.40028,0 60.62222,25.26806,0 61.17305000000002,25.17444,0 61.22499,25.11861,0 61.4386,25.07528,0 61.42305,25.0975,0 61.49527,25.11527,0 61.51667,25.15472,0 61.48611,25.19722,0 61.55388,25.19749,0 61.54971,25.25,0 61.61103000000001,25.19764,0 61.78221,25.19027000000001,0 61.72444,25.0525,0 61.83721,25.03444,0 61.86193000000001,25.09277,0 61.91137000000001,25.11277,0 62.10444,25.10167,0 62.07777000000002,25.18138,0 62.16777,25.21638,0 62.29527,25.19916,0 62.31917,25.14277,0 62.28167000000001,25.105,0 62.35305,25.09195,0 62.39111000000001,25.10221,0 62.33638,25.12778,0 62.36221000000001,25.17332,0 62.48554000000001,25.20944,0 62.46444,25.23638,0 62.52693,25.25556,0 62.74471,25.25945,0 63.00805,25.21471,0 63.15583,25.25638,0 63.50500000000002,25.18861,0 63.52361,25.21361,0 63.46998,25.28028,0 63.57249,25.36,0 63.7486,25.38527,0 63.92917000000001,25.33416,0 64.06299,25.33679,0 64.09166999999999,25.32861000000001,0 64.25555,25.31472,0 64.43497000000001,25.23111000000002,0 64.51666,25.26944,0 64.59276,25.25556,0 64.63109,25.22056,0 64.61998,25.16999,0 64.70999000000001,25.18027,0 64.66499,25.20944,0 64.66776000000002,25.25694,0 64.76888,25.32138,0 65.13970000000001,25.28778,0 65.25417,25.37749,0 65.38611,25.39056,0 65.38777,25.3625,0 65.46642,25.38267,0 65.66776,25.34278,0 65.84665,25.41667,0 66.26389,25.46778,0 66.49576999999999,25.39812,0 66.54637,25.3629,0 66.73276,25.19749,0 66.67415,24.93388,0 66.70499,24.89167,0 66.65028,24.82611,0 66.85943,24.85444,0 66.9511,24.80472,0 66.96082,24.83861,0 67.07283000000001,24.7676,0 67.14679,24.6441,0 67.15637000000001,24.6075,0 67.25166,24.58916,0 67.18165000000001,24.57972,0 67.22971,24.53305,0 67.21361000000002,24.50361,0 67.25722,24.41361,0 67.28554,24.43639,0 67.27887,24.35694,0 67.35942,24.34249,0 67.29803,24.33944,0 67.28166,24.28083000000001,0 67.3633,24.06714,0 67.45277,23.98666,0 67.50054,23.97499,0 67.50583,23.92138,0 67.53803000000002,23.93813000000001,0 67.48248,23.89583,0 67.51169000000002,23.87695,0 67.51169000000002,18.87694,0 67.51169000000002,13.87695,0 67.51169000000002,12.15223,0 67.51169000000002,7.824905,0 67.51169000000002,2.82491,0 67.51169000000002,-2.175094,0 67.51169000000002,-7.175088,0 67.51169000000002,-12.17509,0 67.51169000000002,-17.17509,0 67.51169000000002,-22.17509,0 67.51169000000002,-24.58749,0 67.51169000000002,-29.58749,0 67.51169000000002,-34.58749,0 67.51169000000002,-39.58749,0 67.51169000000002,-41.88749,0 67.51169000000002,-46.88749,0 67.51170000000001,-51.88749,0 67.51170000000001,-56.88749,0 67.51170000000001,-61.88749000000001,0 67.51170000000001,-66.88749,0 67.51170000000001,-71.88749,0 67.51170000000001,-76.88749,0 67.51170000000001,-79.64822,0 67.51170000000001,-84.64822,0 67.51170000000001,-89.64822,0 67.51170000000001,-89.90000000000001,0 66.51234,-89.90000000000001,0 65.51299,-89.90000000000001,0 64.51363000000001,-89.90000000000001,0 63.51426000000001,-89.90000000000001,0 62.51491,-89.90000000000001,0 61.51555000000001,-89.90000000000001,0 60.51619,-89.90000000000001,0 59.51684,-89.90000000000001,0 58.51747,-89.90000000000001,0 57.51812000000001,-89.90000000000001,0 56.51877000000001,-89.90000000000001,0 55.5194,-89.90000000000001,0 54.52005,-89.90000000000001,0 53.52069000000001,-89.90000000000001,0 52.52133,-89.90000000000001,0 52.52133,-88.97416,0 52.52129000000001,-83.97417000000002,0 52.52126,-78.97417,0 52.52125,-76.47417,0 52.52121000000001,-71.47417,0 52.52118000000001,-66.47416,0 52.52115,-61.47417000000001,0 52.52111,-56.47416,0 52.52108,-51.47417,0 52.52105,-46.47416000000001,0 52.52101,-41.47417,0 52.521,-39.17416,0 52.52097,-34.17416,0 52.52093000000001,-29.17417,0 52.52089,-24.17416,0 52.52089,-22.17417,0 52.52085,-17.17417,0 52.52081,-12.17417,0 52.52079000000001,-7.17417,0 52.52075,-2.174176,0 52.52073,2.825829000000001,0 52.52069,7.825823000000002,0 52.52067,10.62583,0 55.86174,10.62857,0 55.86174,13.61265,0 52.52133,13.61265,0 52.52133,16.43764,0 52.94389,16.60806,0 53.11444,16.64278,0 51.99929,18.99934,0 55.00000000000001,20,0 55.66611000000001,21.99972,0 55.19917000000002,22.69972,0 55.14166,22.63333,0 52.58333,22.93889,0 51.58333,24.11666,0 51.58423000000001,24.26046,0 51.59138,24.37833,0 51.62222000000001,24.34472,0 51.63556000000001,24.20972,0 51.67222,24.2725,0 51.70888,24.21472,0 51.74389,24.28639,0 51.77584000000001,24.27528,0 51.79873,24.01633,0 51.92555000000001,23.95471,0 51.94305,23.99277,0 52.08055,23.95555,0 52.33083,23.99971,0 52.52133,24.12054,0 52.52133,27.48647,0 52.56544,27.35835000000001,0 53.14484,26.8033,0 54.47404000000001,26.26287,0 54.48864,25.8831,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +40.25339,43.58252,0 40.54306,43.50861,0 40.68471,43.54583,0 41.04416000000001,43.37498000000001,0 41.16805,43.38722,0 41.29554,43.33610000000001,0 41.39332,43.3461,0 41.58971,43.22275,0 41.93971,43.21749,0 42.05777,43.18610000000001,0 42.18111,43.23555,0 42.41526,43.23916,0 42.62583,43.14305,0 42.7011,43.1825,0 42.85722000000001,43.17748,0 43.0211,43.10444,0 43.01082,43.05666,0 43.20083,42.93637,0 43.56055,42.86082,0 43.67083,42.7911,0 43.82721,42.7514,0 43.8361,42.72776,0 43.73555,42.63916,0 43.9611,42.55778,0 44.0536,42.61804,0 44.22082000000001,42.63693,0 44.28665,42.71278,0 44.54972,42.75944000000001,0 44.68443,42.73943000000001,0 44.80388000000001,42.6172,0 44.855,42.74331,0 44.92749,42.76193,0 45.05304,42.69415,0 45.17137000000001,42.70083,0 45.35167,42.52943,0 45.55277,42.55026,0 45.76028,42.47791,0 45.64748,42.20471,0 45.91028,42.105,0 45.96053,42.02387,0 46.01082,42.04305,0 46.07805,42.03416,0 46.10389,41.99415,0 46.23998,42.00139,0 46.31998,41.93888,0 46.39415,41.94054,0 46.45176000000001,41.89705,0 46.56833,41.88248,0 46.56667000000001,41.80777,0 46.59166000000001,41.79221,0 46.76082,41.86388,0 46.77081,41.79666,0 46.92554,41.71776,0 47.04305000000001,41.55833,0 47.16054,41.5611,0 47.25888000000001,41.42471,0 47.28555000000001,41.31219,0 47.52193,41.25416,0 47.57166,41.2111,0 47.65972000000001,41.23554,0 47.76693,41.19609000000001,0 47.91999,41.22637,0 47.90831000000001,41.29084,0 47.96249,41.35943,0 48.02276,41.36193,0 48.07444000000001,41.46776,0 48.23137000000001,41.50249,0 48.3997,41.58914,0 48.47137000000001,41.72359000000001,0 48.58395000000001,41.83577,0 48.67777000000001,41.78276,0 48.89944,41.52583,0 49.03998,41.41415,0 49.14055,41.26833000000001,0 49.15498000000001,41.15165,0 49.22221,41.01833,0 49.39804000000001,40.84971,0 49.53249000000001,40.78749,0 49.50805,40.69305,0 49.52805,40.66277,0 49.80499,40.56471,0 50.05525999999999,40.58444,0 50.125,40.51138,0 50.23536,40.4908,0 50.36295000000001,40.47673000000001,0 50.62228,40.32742,0 50.63309,40.29893,0 50.39325,40.22689,0 50.2625,40.32916,0 50.12417,40.35833000000001,0 49.98054,40.31998000000001,0 49.87804000000001,40.36276,0 49.82472,40.28499,0 49.57167,40.21277,0 49.48805000000001,40.15054,0 49.49582,40.09055,0 49.42499999999999,40.05388,0 49.48304,39.98943000000001,0 49.42110000000001,39.93081,0 49.44442000000001,39.8686,0 49.39332,39.77832,0 49.42416999999999,39.70638000000001,0 49.31889000000001,39.62498,0 49.28082000000001,39.50055,0 49.41361,39.38388,0 49.40331,39.31805,0 49.36388000000002,39.34499,0 49.40805,39.28776,0 49.31304,39.34138,0 49.25805,39.31944,0 49.20194,39.02471,0 49.12804,38.97331,0 49.06027000000001,38.96693,0 49.05165,39.00139,0 49.12694,39.02998,0 49.14471,39.09748,0 49.06722000000001,39.10777,0 49.03832000000001,39.18221,0 48.95554,39.16526,0 48.93304,38.95138,0 48.89083,38.88082,0 48.82999,38.85942,0 48.86665,38.74583,0 48.88828000000001,38.44241,0 48.78943000000001,38.44777,0 48.62305000000001,38.39526,0 48.45416,38.56471,0 48.44193,38.62165,0 48.32776,38.60333,0 48.24361,38.66721,0 48.24387,38.72776,0 48.02081,38.83554,0 48.01415,38.90583,0 48.07138000000001,38.95138,0 48.31248,39.00776,0 48.32776,39.05165,0 48.29387000000001,39.11249,0 48.13499000000001,39.20888,0 48.11998,39.26361,0 48.18916000000001,39.33332,0 48.35979,39.38522,0 47.97667000000001,39.71922,0 47.93055,39.68359,0 47.80666,39.67722,0 47.53334,39.50888000000001,0 47.39166,39.47054000000001,0 47.29221,39.37693,0 47.09971,39.30444,0 47.02556,39.18942,0 46.94886,39.15443,0 46.83832000000001,39.15527000000001,0 46.54037,38.87559,0 46.35471,38.91054,0 46.17825000000001,38.84114000000001,0 46.05637,38.87831,0 45.87194000000001,38.87999,0 45.43941000000001,38.99804,0 45.43499000000001,39.0511,0 45.30999000000001,39.19832,0 45.14193,39.21748,0 45.13055000000001,39.28556,0 45.06304999999999,39.37305,0 44.95028000000001,39.43582,0 44.8886,39.60582,0 44.81304,39.63081,0 44.77886,39.70638000000001,0 44.69555,39.78998,0 44.59082,39.83305,0 44.48778,39.96277,0 44.34722,40.02389,0 44.28,40.0461,0 43.93389000000001,40.01361,0 43.65749,40.1086,0 43.65778,40.13971,0 43.72165,40.16332,0 43.67221,40.21972,0 43.68,40.26443,0 43.59417,40.33749,0 43.62027,40.41944,0 43.55415,40.4711,0 43.65304,40.53138,0 43.75361,40.67582,0 43.75193,40.74,0 43.6861,40.84195,0 43.67165,40.92806,0 43.59944,40.99249,0 43.47915,41.02528,0 43.46077,41.11296,0 43.47471,41.13999,0 43.42582,41.18527,0 43.3497,41.20193,0 43.23693,41.17638,0 43.19943000000001,41.25527,0 43.1236,41.25527,0 43.20777,41.30693,0 43.03972,41.37416,0 42.89278,41.50695,0 42.84722,41.47305,0 42.78832,41.50861,0 42.82749,41.58500000000001,0 42.59777,41.58305,0 42.51081,41.44333,0 42.47221,41.43333,0 42.18249,41.5075,0 42.05029,41.49056,0 41.96804,41.51915,0 41.83331,41.42832,0 41.72306,41.47055,0 41.72277,41.49554,0 41.65167,41.48277,0 41.53155,41.52388,0 41.73026000000001,41.72555,0 41.77472,41.88563,0 41.7625,41.99221,0 41.66166,42.12109,0 41.63277,42.27194,0 41.55331,42.38581000000001,0 41.46137,42.70804,0 41.22082,42.79861,0 41.15583,42.78917,0 41.03276000000001,42.97304,0 40.93277,42.97222,0 40.8436,43.06777,0 40.58665,43.0825,0 40.38472,43.16555,0 40.32638,43.14192,0 40.22471,43.31443,0 40.00297,43.37926,0 40.0775,43.54694,0 40.12665,43.57222,0 40.25339,43.58252,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +51.39554,54.67722000000001,0 51.39388,54.59304000000001,0 51.49388,54.59527,0 51.52333,54.64638,0 51.67972,54.56166000000001,0 51.92722000000001,54.53611,0 51.93277,54.43638,0 52.01999,54.43665,0 52.08526000000001,54.33942,0 52.12028000000001,54.35111,0 52.105,54.40054000000001,0 52.2,54.40639,0 52.20972,54.44304,0 52.36388,54.45916,0 52.35054,54.42333,0 52.37915,54.41444,0 52.43943,54.47887,0 52.50306000000001,54.45193,0 52.49416,54.39999,0 52.53745,54.37707,0 52.51166,54.32194,0 52.27943,54.32833000000002,0 52.19444,54.29721000000001,0 52.34610000000001,54.22665000000001,0 52.34388000000001,54.17194,0 52.25833,54.14999,0 52.27194,54.11388000000001,0 52.39804000000001,54.1286,0 52.4875,54.05777000000001,0 52.37054,53.9761,0 52.41415,53.93027000000001,0 52.25804,53.65526,0 52.1836,53.55694,0 52.09167,53.55027,0 52.07527,53.50639,0 52.13499,53.44915,0 52.13803999999999,53.40305000000002,0 52.19665000000001,53.37944,0 52.13916,53.29166,0 52.16388000000001,53.26167,0 52.1236,53.22388,0 52.13417,53.17499,0 52.05471,53.14249000000001,0 52.15972,53.09665,0 52.06249,53.07833,0 52.05389,52.99055,0 51.86694000000001,52.92943,0 51.82417000000001,52.87388,0 51.72805,52.87194,0 51.71555,52.82277,0 51.77944,52.79583000000001,0 51.73360000000001,52.6675,0 51.53416,52.67416,0 51.50667000000001,52.63276000000001,0 51.55054,52.57417,0 51.47360000000001,52.57972,0 51.47083,52.52722,0 51.56361,52.43582,0 51.46388,52.41360000000001,0 51.47166,52.36583000000001,0 51.40694,52.34917,0 51.40805,52.25416,0 51.45750000000001,52.22776,0 51.41249000000001,52.08971,0 50.7733,51.76918,0 50.75861,51.71970000000001,0 50.81361,51.64805,0 50.81054000000001,51.59166,0 50.68277000000001,51.57306000000001,0 50.71749,51.62249,0 50.60277,51.63999,0 50.59471,51.58972,0 50.54361,51.56722000000001,0 50.54888,51.46861,0 50.38472,51.42527000000001,0 50.39721999999999,51.39972,0 50.35443,51.37805000000001,0 50.36332,51.31694000000001,0 49.96804,51.2261,0 49.80416,51.11027,0 49.46194,51.12416,0 49.36443,50.97971,0 49.44083,50.90583000000002,0 49.42583000000001,50.85139000000001,0 49.15833,50.79278000000002,0 49.03083,50.68804999999999,0 48.83027,50.59665,0 48.59361,50.64833000000001,0 48.59555,50.62249000000001,0 48.69749,50.59194000000001,0 48.72276000000001,50.35305000000002,0 48.81110000000001,50.19415000000001,0 48.79111,50.1236,0 48.87972000000001,50.10167000000001,0 48.91693000000001,50.03417,0 48.79695,49.94194,0 48.45082,49.81693000000001,0 48.33221,49.87887000000001,0 48.25166000000001,49.86971,0 48.24888000000001,49.90888000000001,0 48.16554000000001,49.9661,0 48.11054000000001,50.0986,0 47.76082000000001,50.37527,0 47.59972,50.46083000000001,0 47.4464,50.37695,0 47.4161,50.33193,0 47.31666,50.29445000000001,0 47.34276,50.24193,0 47.28054,50.18221,0 47.35666,50.09027,0 47.30249,50.03194000000001,0 47.18833,49.94415000000001,0 46.92944,49.86361000000001,0 46.79582,49.33972,0 47.02610000000001,49.23610999999999,0 47.06275999999999,49.14277,0 46.95444,49.01305,0 46.77888000000001,48.93665,0 46.49916000000002,48.4175,0 47.12277,48.27166,0 47.13332000000001,48.24249,0 47.09637,48.21000000000001,0 47.13832000000001,48.16999,0 47.12221,48.10222,0 47.21499000000001,48.07083,0 47.06248000000001,47.99609,0 47.18277,47.87417,0 47.13665000000001,47.82249000000001,0 47.38333,47.68888000000001,0 47.43581,47.84361000000001,0 47.69137,47.76583,0 48.06471000000001,47.77971,0 48.20442000000001,47.70499000000001,0 48.44388000000001,47.43693,0 48.54721000000001,47.41721,0 48.7211,47.09833,0 49.02720999999999,46.77610000000001,0 48.94137,46.70416,0 48.76527,46.69082,0 48.71054,46.75972,0 48.60333,46.77332000000001,0 48.52166,46.74305,0 48.4762,46.66768,0 48.55415,46.64389,0 48.58400000000001,46.59843000000002,0 48.56054,46.56499,0 48.74027000000001,46.55832,0 48.83388,46.48499000000001,0 48.91813000000001,46.48922,0 49.22253,46.34631,0 49.3143,46.2573,0 49.18897,46.05259,0 49.05737,45.98365,0 48.99471,46.00246,0 48.87982,45.94397000000001,0 48.79208,45.82907,0 48.72941,45.804,0 48.74612,45.69747000000001,0 48.36385000000001,45.64942000000001,0 48.20509,45.7121,0 48.0714,45.71836,0 48.015,45.69538,0 47.99829,45.51992000000001,0 47.81656,45.43218000000001,0 47.53247000000001,45.53033,0 47.48619,45.57145,0 47.46748,45.63943,0 47.35971,45.68942,0 47.29361000000001,45.6436,0 46.89304000000001,45.57472000000001,0 47.0411,45.96970000000001,0 47.10915,46.00777,0 47.175,46.10556,0 47.09082000000002,46.1186,0 46.8336,46.07277,0 46.86665,46.24805000000001,0 46.47833,46.27555,0 46.66027,46.42804,0 46.96194,46.4286,0 46.94916,46.53943000000001,0 46.91332,46.55526,0 46.99805,46.63361,0 47.1497,46.66832,0 47.14026,46.69638,0 47.22776,46.74081,0 47.12721,46.85833000000001,0 46.99082,46.85082,0 46.92777,46.97832,0 46.79639,46.99638,0 46.51554,47.39193000000001,0 46.52387000000001,47.43499,0 46.83055,47.5761,0 46.68081,47.63248,0 46.3886,47.44443,0 46.29666000000001,47.43664,0 46.08887000000001,47.51692,0 45.93804,47.62887,0 45.90720000000001,47.7122,0 45.82916,47.72304,0 45.78556,47.81305,0 45.71970000000001,47.86528,0 45.70664000000001,47.93193000000001,0 45.74166,48.00222,0 45.7136,48.03750000000002,0 45.55193000000001,47.99193,0 45.39499,48.09833,0 45.30082000000001,48.09768,0 45.12749000000001,48.11306,0 44.95666,48.21194,0 44.92471,48.19916000000001,0 44.94221,48.14388000000002,0 44.80053,48.05249000000001,0 44.64415000000001,48.09166,0 44.61221,48.05665,0 44.54971,48.05833,0 44.53582,48.20638000000002,0 44.30944,48.25999000000001,0 44.32305,48.05999000000001,0 44.46637,48.04084000000001,0 44.46776,47.9786,0 44.3861,47.91415,0 44.38915,47.86832,0 44.06582000000001,47.9108,0 44.01582,47.86388,0 44.04443,47.82415,0 43.97499,47.76639,0 43.84138,47.7575,0 43.70583,47.68444,0 43.62944,47.55916,0 43.73177,47.47655000000001,0 43.65331,47.44222,0 43.55833000000001,47.48972,0 43.22499,47.44972,0 43.15998,47.48249,0 43.13554000000001,47.57556,0 43.05915,47.57639,0 42.98776,47.51388000000001,0 42.93971,47.51361,0 42.7286,47.85556,0 42.4986,48.01056,0 42.32638,48.04777,0 42.27555,48.01305000000001,0 42.02193,48.03027,0 42.05111,48.12972,0 42.10278,48.16389000000002,0 42.04499,48.25916000000001,0 42.15193,48.28221,0 42.07249,48.40889,0 42.27665,48.49860000000001,0 42.63054,48.54694,0 42.70166,48.61805,0 42.65027,48.70527,0 42.74610000000001,48.80638,0 42.66249,48.8886,0 42.66361,48.96777,0 42.48388,49.04472,0 42.39277,49.04972,0 42.34443,49.15249,0 42.06443,49.15388,0 42.07694,49.19971,0 42.03194,49.26639999999999,0 42.09693,49.29222,0 42.04972,49.40499000000001,0 42.0736,49.51056,0 42.11277,49.52583,0 42.17749,49.69083,0 41.8661,49.90777000000001,0 41.61361000000001,49.96361,0 41.58277,50.005,0 41.5911,50.05525999999999,0 41.4822,50.09027,0 41.36221,50.22629,0 41.5125,50.3811,0 41.4175,50.50527,0 41.45471,50.55138,0 41.52416,50.56860000000001,0 41.52833,50.60166,0 41.40749,50.61944,0 41.32777,50.71916,0 41.15639,50.77638,0 41.38277,50.83027,0 41.37777000000001,50.87666000000001,0 41.41193,50.90359,0 41.7286,51.01721,0 41.8686,51.19388000000001,0 41.96333,51.2086,0 42.09888,51.14750000000001,0 42.2672,51.13166,0 42.41888,51.17222,0 42.56722,51.16526000000001,0 42.68444,51.23693000000001,0 42.87309,51.23142,0 42.91221,51.24083,0 42.83942,51.31666000000001,0 42.86166,51.38166,0 42.48951,51.60789,0 42.51471,51.62388,0 42.48666000000001,51.69305,0 42.67638,51.83972,0 42.70305,51.98194,0 42.8111,52.01027,0 42.74749,52.08027,0 42.75333,52.18581,0 42.92221,52.30526,0 42.92527,52.37500000000001,0 43.00916000000001,52.36388,0 43.0798,52.40288,0 43.12276,52.40192,0 43.19027,52.46666000000001,0 43.3086,52.44555,0 43.34055,52.39111,0 43.77749,52.43472,0 44.05526,52.34610000000001,0 44.34638,52.3161,0 44.33332,52.34805000000001,0 44.38916,52.36833000000001,0 44.41082,52.41305000000001,0 44.54361,52.42944,0 44.51445,52.45916,0 44.53943,52.48638,0 44.49361,52.52665,0 44.54805,52.55249,0 44.66489,52.47935,0 44.86749,52.44666,0 44.90554,52.37665000000001,0 44.97332,52.37332,0 45.05054000000001,52.31721,0 45.10139000000001,52.33332000000001,0 45.06916,52.37778000000001,0 45.11139,52.41109,0 45.44194,52.40055,0 45.51639000000001,52.43444,0 45.52693,52.48166,0 45.64111000000001,52.52028,0 45.73554000000001,52.52583,0 45.71805,52.45889,0 45.98082000000001,52.40721000000001,0 45.97526000000001,52.4361,0 46.01639000000001,52.45776999999999,0 45.97137000000001,52.49583,0 46.06444,52.52693000000001,0 46.08749,52.56388,0 46.14582,52.54972000000001,0 46.15331,52.60194000000001,0 46.2761,52.61805,0 46.29834,52.68055,0 46.39999,52.67249,0 46.58027,52.73249,0 46.61388000000002,52.72748,0 46.61694000000001,52.66109,0 46.83077,52.6164,0 46.9461,52.63832000000001,0 47.0686,52.56638,0 47.1536,52.60999,0 47.20610000000001,52.56666,0 47.43831999999999,52.56722,0 47.55638,52.61833,0 47.72470000000001,52.57916,0 47.78111000000001,52.62915,0 47.77304,52.67972000000001,0 47.85971,52.73166,0 47.90027,52.72221,0 47.92527,52.6661,0 48.07861000000001,52.70555,0 48.08749,52.73193,0 47.98499,52.74722,0 48.10944,52.80138,0 48.28889,52.81082,0 48.43007000000001,52.78054,0 48.58694,52.8686,0 48.57805,52.93554,0 48.51057,52.97799,0 48.31833,52.98555,0 48.36888,53.06638,0 48.26944000000001,53.05777,0 48.2236,52.99722,0 48.14472000000001,53.05916,0 48.21221,53.14582999999999,0 48.13138000000001,53.16888,0 48.09332,53.26194,0 47.94194,53.37583,0 48.00360000000001,53.40666,0 48.04853,53.47771,0 48.16638,53.48249,0 48.2225,53.42443,0 48.23611,53.45888999999999,0 48.37833000000001,53.47137,0 48.37666000000001,53.54277,0 48.44083000000001,53.58138000000001,0 48.355,53.60167000000001,0 48.31416000000001,53.65583000000002,0 48.35638,53.6711,0 48.42695,53.62915000000002,0 48.4461,53.6661,0 48.60333,53.66945,0 48.51305,53.69638000000001,0 48.47222,53.77083,0 48.86333000000001,53.71277,0 49.03694,53.85944,0 49.31694,53.83333,0 49.34304000000001,53.87,0 49.42249000000001,53.87082000000001,0 49.60916000000001,53.855,0 49.61805,53.81139,0 49.83805,53.89083000000001,0 49.85722000000001,53.8336,0 50.03778000000001,53.82527000000001,0 50.05305,53.86860000000001,0 49.95361,53.90527,0 49.97443,53.96138,0 50.19749,54.03722,0 50.22554,54.07916,0 50.20304000000002,54.14027,0 50.26222,54.28944,0 50.19944,54.38416,0 50.21111000000001,54.42167,0 50.11721,54.43027000000001,0 50.12555,54.48194,0 50.07688,54.51667000000001,0 50.22527,54.50361000000001,0 50.27665,54.44054,0 50.38889,54.48888000000001,0 50.42944,54.42749000000001,0 50.47165,54.42333,0 50.5186,54.32639000000001,0 50.70305000000001,54.42833,0 50.93610000000001,54.33999,0 51.01721,54.47916,0 50.99277,54.49916000000001,0 51.00972000000001,54.55165,0 51.09361,54.54861,0 51.18111,54.64638,0 51.28139000000002,54.63694,0 51.39554,54.67722000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +47.59415,61.05888,0 47.88139,60.99833,0 47.95999,61.00972,0 47.9875,60.88665000000001,0 48.41924,60.85386000000001,0 48.53917,60.59999000000001,0 48.53999,60.55915,0 48.48110000000001,60.52499,0 48.51111000000001,60.51194,0 48.50416000000001,60.48276,0 48.61777000000001,60.43333000000001,0 48.54137999999999,60.41109,0 48.67582000000001,60.40638000000001,0 48.76305,60.365,0 48.70499,60.30471,0 48.45917000000001,60.19415,0 48.50999,59.70388,0 49.08554,59.65554,0 49.03416,59.48444,0 49.14778,59.50889,0 49.3086,59.46694,0 49.34194,59.41915000000001,0 49.545,59.39305000000001,0 49.50861,59.24610000000001,0 49.59333,59.21776,0 49.79554,59.22221,0 49.82416,59.37054,0 49.79554,59.38388,0 49.82888000000001,59.48110000000001,0 49.78722000000001,59.50054999999999,0 49.83278,59.63693,0 49.85361,59.66361,0 50.05638,59.6875,0 49.95193,59.74722000000001,0 49.99665,59.77887000000001,0 50.2375,59.75277,0 50.27887,59.80693000000002,0 50.40331000000001,59.84444,0 50.82861000000001,59.86277000000001,0 51.07110999999999,60.06166000000001,0 51.34388,60.01833,0 51.41332000000001,59.94165,0 51.53221000000001,59.94721,0 51.8,60.11083,0 51.89999,60.24444,0 52.0089,60.30360000000002,0 52.08278,60.31694000000002,0 52.29815,60.24171,0 52.42722000000001,60.20304,0 53.01083,60.15915,0 53.38611,60.16555,0 53.41137000000001,60.22443000000001,0 53.59887000000001,60.15666,0 53.71194,60.00332,0 53.60278000000001,59.70055000000001,0 53.42054,59.68971,0 53.3661,59.51832,0 53.2047,59.34944,0 53.24415000000001,59.27583,0 53.38555,59.15554,0 53.7236,59.09194000000002,0 53.68916000000001,59.12332000000001,0 53.75417,59.14111000000001,0 53.88416000000001,59.09054,0 53.83500000000001,59.05832000000001,0 53.84470000000001,59.03054,0 53.76500000000001,59.02277000000002,0 53.71915,58.93748999999999,0 53.80721000000001,58.82777000000001,0 53.92833,58.78166000000001,0 53.77055,58.71638000000002,0 53.74027,58.67972,0 53.75721999999999,58.6525,0 53.69610000000001,58.60333000000001,0 53.78463,58.43996000000001,0 53.79027,58.39249000000001,0 53.88916,58.32222,0 53.80972,58.22887,0 53.89415,58.18804,0 53.94276,58.09527000000001,0 54.10722,57.97276,0 54.08749,57.91331,0 54.13639000000001,57.83222,0 54.15665000000001,57.70249,0 54.06916,57.66666000000001,0 54.12111,57.61193,0 53.98749000000001,57.57111,0 54.10139,57.51805,0 54.11888,57.55944000000001,0 54.16999000000001,57.55972000000001,0 54.20361,57.53083,0 54.16582,57.51222,0 54.17860000000001,57.465,0 54.26472,57.46443000000001,0 54.27166,57.42222,0 54.14443,57.38693,0 54.11639,57.31027000000001,0 54.32000000000001,57.28666000000001,0 54.33472,57.15554000000001,0 54.27555,57.10082000000001,0 54.37444000000001,57.07389000000001,0 54.40269,57.03656000000002,0 54.09805,56.96249,0 54.09177,56.79174,0 54.01056,56.74916,0 53.88999,56.76445,0 53.88943,56.80444,0 53.82832,56.81193,0 53.80637999999999,56.77332,0 53.88081,56.67222,0 53.83554,56.63556,0 53.87,56.6075,0 53.96388,56.63110000000001,0 54.0286,56.69276,0 54.10083,56.66276000000001,0 54.02664999999999,56.58416,0 54.09083,56.58250000000001,0 54.12194,56.53361000000001,0 54.18472000000001,56.53722,0 54.23611,56.43581,0 54.32472000000001,56.46249,0 54.35241,56.35857,0 54.31527,56.34138,0 54.37526,56.30498999999999,0 54.35889,56.26222000000001,0 54.10722,56.14749,0 54.02443,56.02944000000001,0 53.83795000000001,56.04377,0 53.78472,55.96915,0 53.67193,55.95999,0 53.63842,55.91552,0 53.29725,55.85252,0 53.1825,55.92389000000001,0 53.28694999999999,55.93555,0 53.37888,55.98972,0 53.47470000000001,56.12110000000001,0 53.54499,56.14083000000001,0 53.55610000000001,56.24388,0 53.41666,56.27833,0 53.2589,56.25471000000001,0 53.34249,56.14749,0 53.34499,56.08471,0 53.26111,56.08554,0 53.14666,56.16220000000001,0 53.04749,56.13528,0 53.06888,56.18249,0 53.04193,56.20805000000001,0 52.99416,56.18833,0 53.00305,56.22165,0 52.95610000000001,56.25166,0 52.98221,56.28082,0 52.94138,56.30639,0 52.98054,56.3475,0 52.9386,56.36694,0 53.09528,56.52694000000001,0 52.94444,56.54472,0 52.91778000000001,56.50111,0 52.82555,56.52193,0 52.95165999999999,56.46916,0 52.95278,56.44165000000002,0 52.82665999999999,56.38888000000002,0 52.72277,56.39138,0 52.74055,56.37471000000001,0 52.63638,56.28638,0 52.55833,56.2625,0 52.57583,56.22748000000001,0 52.81694,56.20249,0 52.80582,56.18194,0 52.84554,56.15721,0 52.81055,56.13388000000001,0 52.85443,56.12666,0 52.83694000000001,56.09222000000001,0 52.70054,56.14138000000001,0 52.72887,56.06471000000001,0 52.66221,56.01554,0 52.46776,56.07749,0 52.44666,56.06361,0 52.48471,56.03694000000002,0 52.43665,56.02833,0 52.20471000000001,56.08416,0 52.18693,56.05527,0 52.2461,56.06694,0 52.20417,56.00443000000001,0 52.25166,55.95805000000001,0 52.16916000000001,55.89306,0 52.05638,55.895,0 52.03749000000001,55.95916,0 51.96083,55.97915,0 51.78889,55.88388000000001,0 51.72971,55.95971,0 51.68943,55.93278,0 51.64277,55.96054,0 51.43027000000001,55.92722,0 51.39721,55.9922,0 51.48999000000001,56.07888000000001,0 51.6536,56.13444000000001,0 51.43667,56.14444000000001,0 51.36194,56.13054000000001,0 51.3147,56.06972,0 51.22304000000001,56.09916,0 51.20499,56.13943,0 51.1075,56.15139000000001,0 51.07639,56.12498999999999,0 50.83942,56.2411,0 50.86111,56.27804,0 50.90165,56.27944000000002,0 50.90693,56.31583,0 50.87389,56.32666,0 50.85139000000001,56.39027000000001,0 50.785,56.41555,0 50.7625,56.39944,0 50.78527,56.35638,0 50.73304,56.32999000000002,0 50.58638,56.35443,0 50.63110000000001,56.40526,0 50.56971000000001,56.40250000000001,0 50.45,56.54472,0 50.49333,56.61639,0 50.42333,56.58526,0 50.41361000000001,56.62804,0 50.34777,56.67361,0 50.30304,56.67638,0 50.32306,56.65444,0 50.29834,56.63999,0 50.22138000000001,56.66082,0 50.16194000000001,56.615,0 50.07221000000001,56.62266,0 50.10167000000001,56.79416,0 50.0611,56.79361,0 50.07082000000001,56.83082,0 50.01445,56.86721000000001,0 49.73443,56.89667000000001,0 49.75722,56.91415000000001,0 49.71582999999999,56.93361,0 49.76194,56.96500000000001,0 49.72499,56.99110000000001,0 49.71194,57.07361000000001,0 49.41249000000001,57.05305000000001,0 49.41915,57.02194,0 49.26832,57.04361,0 49.23138000000001,57.1311,0 49.14361,57.15972,0 49.22805,57.28889,0 49.18333000000001,57.33139,0 49.01611,57.28305,0 49.00639000000001,57.23332000000001,0 49.04610000000001,57.2011,0 49.01194,57.19332,0 48.99804,57.11888,0 48.95471,57.08554,0 48.84583,57.0886,0 48.86639,57.12249,0 48.83555,57.16444,0 48.56945,57.17249000000001,0 48.4511,57.12054,0 48.30777000000001,57.16860000000001,0 48.19222000000001,57.12471,0 48.24888000000001,57.10666000000001,0 48.16304,57.07972000000002,0 48.2086,57.02722,0 48.13916,56.99833,0 47.98694,57.01444999999999,0 47.97193,56.97387000000001,0 47.93111,57.00944,0 47.91666,56.97665000000001,0 47.83083000000001,57.00416000000001,0 47.785,57.05721,0 47.7336,57.05527000000001,0 47.76971000000001,57.00972000000001,0 47.68916,57.01250000000001,0 47.61721,56.9761,0 47.51139,56.90666,0 47.50249,56.85139,0 47.42444,56.88082,0 47.17083000000001,56.80860000000001,0 47.12277,56.83749,0 47.18194,56.84805,0 47.13277,56.91693000000001,0 46.94471,56.94833000000001,0 46.71583,56.93721,0 46.74471,56.95471,0 46.71510000000001,56.96634000000001,0 46.75388,56.97417,0 46.70805,57.0164,0 46.76806,57.02971,0 46.76528,57.14388,0 46.72638000000001,57.20000000000001,0 46.83443000000001,57.19971,0 46.85194000000001,57.23444000000001,0 46.80888,57.23583,0 46.78972,57.28944,0 46.72055,57.29445,0 46.67611000000001,57.35,0 46.75388,57.37999,0 46.68970000000001,57.39083,0 46.70471000000001,57.42555000000001,0 46.79722000000001,57.45666,0 46.74443,57.52333,0 47.03082,57.49304,0 47.25027,57.57083,0 47.4711,57.52859999999999,0 47.48666,57.58472,0 47.52638000000001,57.58416,0 47.54888,57.66361,0 47.52859,57.70304,0 47.58027000000001,57.74638,0 47.55082,57.765,0 47.5611,57.80249,0 47.77027,57.90028,0 47.70083,57.96361,0 47.45610000000001,57.98555000000001,0 47.38666,58.02332000000001,0 46.52304000000001,58.03889,0 46.44972,58.01749,0 46.34132,58.07474,0 46.30972000000001,58.11054,0 46.35527,58.18721,0 46.41499000000001,58.21054000000001,0 46.41026999999999,58.30805,0 46.48109,58.33222,0 46.41137,58.36388,0 46.59166000000001,58.43194,0 46.54972000000001,58.47665,0 46.59026999999999,58.51832000000001,0 46.58416,58.55804000000001,0 47.0061,58.62249,0 47.00417000000001,58.69583,0 47.11333,58.77249,0 47.265,58.75416,0 47.325,58.80082,0 47.28028,58.81945,0 47.30388,58.85722,0 47.28111000000001,58.90222000000001,0 47.30721,58.91666,0 47.45305000000001,58.92249,0 47.46777000000001,58.88416,0 47.61166,58.91499,0 47.55082,58.9886,0 47.57639,59.06028,0 47.45139,59.12694000000001,0 47.445,59.1675,0 47.39443,59.14722,0 47.22332,59.2261,0 47.25249,59.35555,0 47.05638,59.37860000000001,0 47.11351,59.61137999999999,0 47.14305,59.71333,0 47.12305,59.76139000000001,0 46.92305000000001,59.78444,0 46.92083000000001,59.84665,0 47.03694000000001,60.11832,0 46.89638,60.12583000000001,0 46.83138,60.07306000000001,0 46.60609,60.06527,0 46.36416000000001,60.1211,0 46.35305,60.20972,0 46.295,60.24249,0 46.43083000000001,60.27388,0 46.74499,60.25639000000001,0 46.81304,60.32277,0 46.83193000000001,60.37972,0 46.80444,60.38750000000001,0 46.87805,60.48166000000001,0 46.85666,60.51527,0 47.06166,60.57667000000001,0 47.01749,60.60249000000001,0 47.02388000000001,60.64695,0 46.95277,60.65527,0 47.08721000000001,60.82027,0 47.04314000000001,60.85386000000001,0 47.08027000000001,60.86221,0 47.10749,60.96194000000001,0 47.19138,60.99611000000002,0 47.19720000000002,61.05999,0 47.22833000000001,61.06444,0 47.59415,61.05888,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +57.49650000000001,82.84008,0 52.49656000000001,82.81507000000001,0 52.49656000000001,84.90001,0 52.49656000000001,89.90000000000001,0 53.43657000000001,89.90000000000001,0 54.37659,89.90000000000001,0 55.31659000000001,89.90000000000001,0 56.25661,89.90000000000001,0 57.19662,89.90000000000001,0 58.13662000000001,89.90000000000001,0 59.07665,89.90000000000001,0 60.01665,89.90000000000001,0 60.95667,89.90000000000001,0 61.89668,89.90000000000001,0 62.83669,89.90000000000001,0 63.7767,89.90000000000001,0 64.71672,89.90000000000001,0 65.65673,89.90000000000001,0 66.59672999999999,89.90000000000001,0 67.53675,89.90000000000001,0 67.53675,84.90001,0 67.53675,82.89028,0 67.49638,82.89008,0 62.49644000000001,82.86508000000001,0 57.49650000000001,82.84008,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +04:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(4.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0450</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +74.91575,37.23733,0 74.8111,37.22041,0 74.75110000000001,37.30375,0 74.65109,37.23457,0 74.52970999999999,37.24178,0 74.47942999999999,37.17925,0 74.39722,37.17841,0 74.40054000000001,37.13896,0 74.56543000000001,37.02782,0 74.55304,36.96173000000001,0 74.48888,37.01034000000001,0 74.41998,37.00144,0 74.25194000000001,36.89917,0 74.15166000000001,36.90387,0 74.12109000000001,36.83858000000001,0 74.03831,36.81468,0 73.8522,36.89273000000001,0 73.6683,36.90964,0 72.55663,36.82126,0 72.47416,36.7624,0 72.17554,36.7113,0 72.18163,36.64969,0 72.07916,36.63162,0 72.07471,36.58915,0 71.90443000000001,36.50722,0 71.79584,36.49164,0 71.80834,36.40364,0 71.63307,36.46555,0 71.56305999999999,36.37228,0 71.5789,36.33173,0 71.50725,36.31537,0 71.41252000000002,36.22043,0 71.3389,36.20492,0 71.18706,36.04219,0 71.38499,35.94194,0 71.38721,35.89695,0 71.43997,35.87082,0 71.49082,35.75667,0 71.54526,35.71111,0 71.50888000000001,35.62083,0 71.61665000000001,35.55526,0 71.60193,35.50055,0 71.64943,35.42499,0 71.54859999999999,35.30111,0 71.66388000000001,35.19555,0 71.53554,35.08665,0 71.53803000000001,35.01972,0 71.49890000000001,34.96635,0 71.30914000000001,34.88361,0 71.22553000000001,34.74444,0 71.09583000000001,34.67666,0 71.0886,34.55943,0 70.98748999999999,34.5511,0 71.00054,34.46305,0 71.15886999999999,34.34833,0 71.11832,34.26027,0 71.13554000000001,34.1661,0 71.07720999999999,34.11277,0 71.07720999999999,34.05222,0 70.98109,34.00889,0 70.90555000000001,34.01333,0 70.89109999999999,33.96638,0 70.49136,33.94305,0 70.00276,34.05277000000001,0 69.90637,34.03527,0 69.86913,33.92999,0 69.98554,33.75306,0 70.1375,33.73249000000001,0 70.15138,33.66554,0 70.19664,33.64083,0 70.16804000000001,33.52083,0 70.28610000000001,33.42276,0 70.32693,33.33193,0 70.14499000000001,33.20249,0 70.06776000000001,33.20499,0 70.02888,33.13583000000001,0 69.88109,33.08999,0 69.79137,33.12694,0 69.70081999999999,33.07777,0 69.58083000000001,33.08693,0 69.49081,33.00388,0 69.51277,32.86944,0 69.39499000000001,32.77388,0 69.45833,32.67054,0 69.37776,32.56833,0 69.28749000000001,32.52639,0 69.24802,32.44388,0 69.29137,32.3175,0 69.27193,32.14527,0 69.32825,31.94036,0 69.2244,31.87129,0 69.09972000000001,31.70667,0 69.01944,31.65055,0 68.83305,31.60388,0 68.72803999999999,31.70416000000001,0 68.70722000000001,31.78111000000001,0 68.51944,31.825,0 68.43719,31.78667,0 68.57165999999999,31.76527000000001,0 68.53137,31.72693,0 68.27222,31.77166,0 68.16609,31.83305,0 68.07165999999999,31.69527,0 67.98054999999999,31.63583,0 67.88193,31.63805,0 67.73193000000001,31.52916,0 67.57555000000001,31.53194,0 67.61526000000002,31.42777,0 67.66775,31.4,0 67.77303000000001,31.40861,0 67.79192000000001,31.3411,0 67.38472,31.21527,0 67.25648,31.22233,0 67.05582,31.24305,0 67.04276,31.31639,0 66.95694,31.31806,0 66.84166999999999,31.2725,0 66.79304000000001,31.21972,0 66.72304,31.21222,0 66.68414,31.08611,0 66.57277000000001,30.98138,0 66.39554,30.94083,0 66.28194000000001,30.57528,0 66.35165000000001,30.44527,0 66.32527,30.23778,0 66.23831000000001,30.07138,0 66.36221000000001,29.9561,0 66.25664999999999,29.85194,0 65.03416,29.54111,0 64.69553000000001,29.58638,0 64.47859,29.57306,0 64.21138000000001,29.4986,0 64.12221,29.38861,0 63.5872,29.50389,0 62.48444,29.40611,0 60.86630000000002,29.86366,0 61.78652,30.80953,0 61.80403,30.94514,0 61.8511,31.02111,0 61.76466000000001,31.24477,0 61.7711,31.31833,0 61.71361000000001,31.38332000000001,0 61.10417,31.46305,0 60.84389,31.49833,0 60.79694000000001,32.0375,0 60.83665,32.09027,0 60.85778000000001,32.23471,0 60.58056,33.0886,0 60.59109,33.16305,0 60.85167,33.41805,0 60.85443,33.48999,0 60.94305,33.51944,0 60.88639,33.55721,0 60.66610000000001,33.5675,0 60.52776999999999,33.64415,0 60.50527,33.73915,0 60.55417000000001,33.81332,0 60.50444,34.12222,0 60.54610000000001,34.19638,0 60.67555000000001,34.28833,0 60.66554,34.31249,0 60.91527000000001,34.32,0 60.72165,34.52222,0 60.97137,34.64444,0 60.98333000000001,34.73388,0 61.06305,34.80888,0 61.06972,34.92888000000001,0 61.14249000000001,35.09832,0 61.09471,35.18416,0 61.10444,35.27916,0 61.19276000000001,35.30583,0 61.19444,35.40222,0 61.28778000000001,35.54999,0 61.27655000000001,35.60725,0 61.37888,35.63527,0 61.40610000000002,35.56388,0 61.59583000000001,35.44415,0 61.80249,35.4111,0 61.99583,35.45832,0 62.13256,35.36806,0 62.26944000000001,35.28583,0 62.30915000000002,35.14166,0 62.46500000000001,35.28861,0 62.55444000000001,35.2336,0 62.72220999999999,35.25472,0 62.92249000000001,35.38417,0 63.10527,35.45083,0 63.12666,35.53361,0 63.09528,35.62609,0 63.24002000000002,35.69928,0 63.12972000000001,35.77527,0 63.11944,35.86193,0 63.34249000000001,35.85722000000001,0 63.52833,35.90499,0 63.60111000000001,35.96443,0 63.77832999999999,35.98054000000001,0 63.92055,36.0375,0 64.06386999999999,36.00027,0 64.05971,36.08805,0 64.09498000000001,36.12332,0 64.18137,36.17054,0 64.28248000000001,36.15193,0 64.33665000000001,36.22471,0 64.47387000000001,36.25555,0 64.61526000000001,36.42361,0 64.61693,36.63638,0 64.79526,36.92305,0 64.77916999999999,37.09583,0 64.82304000000001,37.1386,0 65.07277000000001,37.24444,0 65.55443,37.25638,0 65.65082,37.35555000000001,0 65.64444,37.44887,0 65.68109,37.51694,0 65.77193,37.53749,0 65.78167999999999,37.5715,0 65.84526,37.50444,0 66.09054999999999,37.43167,0 66.28999,37.32472,0 66.40833000000001,37.3461,0 66.44748,37.31860000000001,0 66.53773,37.36638,0 66.67221000000001,37.33833,0 67.00417,37.38221,0 67.09648,37.33997,0 67.11749,37.28028,0 67.18637,37.25805,0 67.23997,37.1861,0 67.39722,37.21749,0 67.49249,37.28083,0 67.55748,37.21555,0 67.64333000000001,37.24722,0 67.75526000000001,37.22054,0 67.77988000000001,37.18582,0 67.78221000000001,37.09610000000001,0 67.88554000000001,37.06138,0 68.02831999999999,36.92389,0 68.18719,37.02055,0 68.27499,37.01222,0 68.29471,37.10832,0 68.39749000000001,37.09916000000001,0 68.4122,37.14805,0 68.62388,37.20055000000001,0 68.66415000000001,37.27499,0 68.82165999999999,37.24499,0 68.80663,37.31806,0 68.83804000000001,37.32695,0 68.91943000000001,37.27555,0 68.88777,37.33860000000001,0 68.9997,37.30777,0 69.12527,37.16304,0 69.24664,37.09416,0 69.3886,37.1586,0 69.45055000000001,37.22527,0 69.41110000000001,37.24083000000001,0 69.38026000000001,37.4511,0 69.51582999999999,37.58082,0 69.80692999999999,37.58305,0 69.89915000000001,37.61501,0 69.9586,37.56499,0 70.06245,37.54134,0 70.16193,37.53956,0 70.21554999999999,37.61638,0 70.25360000000001,37.61574,0 70.28581,37.69707,0 70.24693000000001,37.81255,0 70.16553999999999,37.87172,0 70.16137000000001,37.93337,0 70.24745,37.93169,0 70.24721,37.96394,0 70.46416000000001,38.11119,0 70.58443,38.27816,0 70.59775999999999,38.34151,0 70.68552,38.37486,0 70.67221000000001,38.41403,0 70.87388,38.45295,0 70.92998,38.43045,0 70.96720000000002,38.47212,0 71.04971000000001,38.40294,0 71.14554,38.38574,0 71.36638000000002,38.23627,0 71.25277,37.92204,0 71.33472,37.88787,0 71.51537,37.94846,0 71.59193000000002,37.90262,0 71.58859,37.81565,0 71.52878,37.7639,0 71.49571,37.55815,0 71.5141,37.46773,0 71.47942000000001,37.41003,0 71.49026000000001,37.28362,0 71.44693000000001,37.20722000000001,0 71.42943,37.07583,0 71.45694,36.94474,0 71.55468999999999,36.76068,0 71.61006999999999,36.70038,0 71.69525,36.67184,0 71.84276,36.6921,0 72.30636,36.9781,0 72.65851000000001,37.0212,0 72.79359,37.22623,0 73.06914999999999,37.31407,0 73.15054000000001,37.40049,0 73.30721,37.46275,0 73.37248,37.44692,0 73.45637000000001,37.47611,0 73.77598,37.4343,0 73.76888,37.34113,0 73.64943,37.30415,0 73.61998,37.26248,0 73.73858,37.22164,0 74.19721,37.33897,0 74.23748999999999,37.36119,0 74.23997,37.40705,0 74.36388,37.42846,0 74.54221,37.38181,0 74.68427,37.39974,0 74.83277,37.32545,0 74.91575,37.23733,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +05:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0500</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +74.69081,37.06259,0 74.73943,37.02037,0 74.81749000000001,37.02176000000001,0 74.90166000000001,36.91093,0 74.95972,36.98012,0 75.14444,36.99987,0 75.39722,36.91406000000001,0 75.45444000000001,36.73044,0 75.52303000000001,36.72906,0 75.54637,36.76963,0 75.63916,36.76355,0 75.85526000000002,36.66717,0 76.00499000000001,36.4586,0 75.99082,36.31444,0 76.04166000000001,36.23751,0 75.99359,36.20833,0 75.99387,36.15971,0 75.92303,36.1236,0 75.92609,36.07721,0 75.97275999999999,36.01165,0 76.12387,35.97776,0 76.17693,35.81444,0 76.42165,35.8336,0 76.56416,35.91276,0 76.58971000000001,35.75695,0 76.7122,35.715,0 76.77805000000001,35.65526,0 76.86415,35.64888,0 76.91443,35.59777,0 77.07555000000001,35.58777,0 77.18747999999999,35.52805,0 77.30803000000002,35.53472,0 77.43137,35.47165,0 77.7011,35.46111,0 77.82393,35.50133000000001,0 77.04248,35.09916,0 77.06081,35.01472,0 76.96416000000001,34.94055,0 76.91692999999999,34.68138,0 76.86998,34.65888,0 76.80138,34.67222,0 76.7272,34.74388,0 76.56303000000001,34.73166,0 76.47026,34.76999,0 76.29359,34.70027,0 76.22916000000001,34.60944,0 76.16748,34.58638,0 75.98804,34.62694,0 75.80553999999999,34.50721,0 75.66137000000001,34.50082,0 75.39249,34.55471,0 75.24303999999999,34.62609,0 74.69275,34.6936,0 74.38916,34.78194,0 73.94914,34.65444,0 73.94637,34.56554,0 73.88193,34.52194,0 73.87054000000001,34.45833,0 73.79637,34.38555,0 73.84805,34.3186,0 73.97331,34.30277,0 74.01776,34.21804000000001,0 74.01416999999999,34.17556,0 73.9122,34.11527,0 73.91332,34.0686,0 73.97887,34.01139,0 74.24442999999999,34.01915,0 74.29582000000001,33.97748,0 74.23581,33.88416,0 74.05025999999999,33.83167,0 73.99471,33.75555,0 74,33.65417,0 74.15443000000001,33.56139,0 74.18248,33.51056,0 74.12581,33.34333,0 74.02415000000001,33.27638,0 74.01331999999999,33.19888,0 74.17331,33.04749,0 74.33414999999999,33.00054,0 74.36388,32.86944,0 74.32966999999999,32.81542,0 74.37164,32.7711,0 74.45444000000001,32.79306,0 74.53166,32.75027,0 74.63248,32.77055,0 74.70165,32.84694,0 74.65358999999999,32.59332,0 74.70387,32.48555,0 74.74443000000001,32.465,0 74.86165,32.48472,0 74.93858,32.44887,0 75.04886999999999,32.47832,0 75.31931,32.34466,0 75.32859000000001,32.29276,0 75.39130000000002,32.24158,0 75.32883,32.20907,0 75.30686,32.13391,0 75.25653,32.09287,0 75.22417,32.11676,0 75.19403,32.07404,0 75.04075000000002,32.04146,0 74.90347000000001,32.05329,0 74.83311000000001,31.96271000000001,0 74.60532000000001,31.87712,0 74.54922000000001,31.74385,0 74.48872,31.72159,0 74.61033999999999,31.55431,0 74.59842,31.48941,0 74.65510999999999,31.42797,0 74.54837000000001,31.35443,0 74.53898,31.11872,0 74.55728000000001,31.07605,0 74.62375,31.12393,0 74.68432,31.11202,0 74.69459000000002,31.05396,0 74.56112,31.04559,0 74.55499,30.99505,0 74.43221,30.94277,0 74.40063000000001,30.88415000000001,0 74.31693,30.88944,0 74.16109,30.66167,0 74.04886999999999,30.59916000000001,0 74.05025999999999,30.515,0 73.99609,30.505,0 73.93380000000002,30.41625,0 73.8725,30.3901,0 73.8586,30.36028,0 73.93442,30.31416,0 73.96442,30.23472,0 73.93341,30.136,0 73.39749000000001,29.94277,0 73.26527,29.55306,0 73.08638000000001,29.24582,0 72.98888,29.14194000000001,0 72.94832,29.03583,0 72.38970999999999,28.785,0 72.29221,28.68,0 72.20663999999999,28.4025,0 71.91998,28.12082,0 71.89694,27.96194,0 71.62691,27.87611,0 71.22581,27.84583,0 70.86665000000001,27.70693,0 70.73775999999999,27.73138,0 70.6622,27.86333000000001,0 70.6636,27.92833,0 70.51443,28.03917,0 70.36832,28.02083,0 70.20354,27.88558,0 70.11971,27.81611,0 70.0311,27.59055,0 69.86749,27.40861,0 69.58333,27.17833,0 69.50972,26.99221,0 69.48609999999999,26.80638,0 69.51277,26.74667000000001,0 69.72276000000001,26.66527,0 69.80081,26.59416,0 70.07138000000001,26.59611,0 70.17192,26.55277,0 70.17386999999999,26.24444,0 70.09276,26.08,0 70.1172,25.91888000000001,0 70.29276000000002,25.70138,0 70.38611,25.67472,0 70.60637,25.71416,0 70.66638,25.69833,0 70.67747,25.40166,0 70.75333000000001,25.27389000000001,0 70.8797,25.15611,0 70.9422,24.93555,0 71.07743000000001,24.68047,0 70.99276,24.6361,0 70.97971,24.56056,0 71.00555,24.54084,0 71.0061,24.45805,0 71.08194,24.44861,0 71.10581999999999,24.41638,0 71.02916999999999,24.36361,0 70.92804,24.36167,0 70.86443,24.31694,0 70.88582,24.27444,0 70.79082,24.23944,0 70.65054000000001,24.24138,0 70.58443,24.27834,0 70.56776000000001,24.37138,0 70.60081,24.415,0 70.56749000000002,24.43639,0 70.12009000000001,24.30904,0 70.07071000000002,24.19792,0 70.01470999999999,24.16972000000001,0 69.72360000000001,24.17111,0 69.59860000000001,24.28139,0 69.31081,24.29666,0 69.18941,24.25805,0 69.08028,24.29722,0 68.98803,24.25694,0 68.92693,24.32555,0 68.85804,24.24166,0 68.82471000000001,24.33278,0 68.78306000000001,24.33278,0 68.74525,24.31639,0 68.74721,23.96999,0 68.39055000000001,23.96749000000001,0 68.37637000000001,23.94417,0 68.36193,23.97388,0 68.33054,23.91472,0 68.29053999999999,23.94444,0 68.28194000000001,23.89777,0 68.21442,23.86833,0 68.1978,23.76668,0 68.21388,23.68055,0 68.28027,23.66666,0 68.23054999999999,23.64361,0 68.2011,23.66417,0 68.14028,23.60471,0 68.32138999999999,23.58083,0 68.44747,23.72693,0 68.58804000000001,23.76695,0 68.62831,23.82611,0 68.81833,23.87638000000001,0 68.66887,23.80972,0 68.63638,23.75722,0 68.46693000000001,23.65304,0 68.48553,23.61,0 68.42331,23.62722,0 68.40292,23.51842,0 68.39158999999999,23.4551,0 68.43692,23.42638,0 68.46278,23.43944,0 68.47638000000001,23.40027,0 68.53804,23.41805,0 68.51105,23.36989,0 68.53916,23.27833,0 68.56887999999999,23.25,0 68.62053,23.26777,0 68.5936,23.22888,0 68.66665999999999,23.14222,0 69.14346999999999,22.8714,0 69.20937000000001,22.73017,0 69.13518999999999,22.5504,0 69.06914,22.48121,0 68.96388,22.38445,0 68.94274,22.32166,0 68.96278,22.24777,0 69.22942000000001,21.94722,0 69.36053,21.84722,0 69.38970999999999,21.87444,0 69.40331999999999,21.80306,0 69.59582,21.63333,0 69.80888,21.46361,0 70.01582999999999,21.18611,0 70.26028,20.97666,0 70.52777,20.81111,0 70.83138,20.69444,0 70.96608999999999,20.70277,0 71.01195000000001,20.74166,0 71.46693000000001,20.88194,0 71.56191,20.97861,0 71.56026,20.94611,0 72.04886999999999,21.16083,0 72.11053,21.20194,0 72.08611000000001,21.24471,0 72.10387,21.30972,0 72.24249,21.46527,0 72.30326,21.63418,0 72.52789,21.67045,0 72.61156,21.47095,0 72.59081999999999,21.41195,0 72.6947,21.46972,0 72.75499000000001,21.46472,0 72.56667,21.3825,0 72.64776999999999,21.35889,0 72.58249000000001,21.34138,0 72.60988999999999,21.25536,0 72.61248999999999,21.12068,0 72.62608,21.08472,0 72.66921000000001,21.093,0 72.70011,21.07702,0 72.70739,21.07498,0 72.73706,20.99305,0 72.78471999999999,21.00082,0 72.89109999999999,20.97888,0 72.83098,20.92505,0 72.79096,20.87397,0 72.83443,20.83416,0 72.89305,20.87305,0 72.88916999999999,20.79555,0 72.92110000000001,20.81583,0 72.94609,20.75833,0 72.90166000000001,20.73222,0 72.88527000000001,20.51305,0 72.75359,20.295,0 72.72745999999999,20.13218,0 72.64748,19.84666,0 72.67525999999999,19.74611,0 72.73220000000001,19.78277,0 72.73165,19.70555,0 72.68831,19.72111,0 72.71693000000001,19.5475,0 72.74249,19.51056,0 72.73305000000001,19.57805,0 72.79443000000001,19.52361,0 72.87916000000001,19.52778,0 72.74359,19.47611,0 72.75443,19.36972,0 72.79776,19.32611,0 72.96388,19.30917,0 73.04722,19.21726,0 73.01609999999999,19.20583,0 72.96971000000001,19.29166,0 72.77748,19.30861,0 72.77638,19.20388,0 72.82666,19.25056,0 72.77998,19.16027,0 72.82082,19.17277,0 72.82249,19.03777,0 72.76999000000002,18.94306,0 72.80247,18.95304,0 72.80582,18.895,0 72.85741,18.79465,0 72.85748,18.64805,0 72.91443000000001,18.55277,0 72.9611,18.55139000000001,0 73.01694000000001,18.46722,0 72.99387,18.4561,0 72.95139,18.52944,0 72.90916,18.52638,0 72.90694000000001,18.36249,0 72.98581,18.28389,0 73.06499,18.27389,0 73.07887,18.21472,0 73.05165,18.18305,0 73.05663000000001,18.22138,0 72.97165000000001,18.23944,0 72.93803,18.27945,0 72.92221000000001,18.22722,0 72.96915,18.18694,0 72.97971,18.06305,0 73.04443000000001,18.05167000000001,0 73.00054,18.01444,0 73.03498999999999,17.98472,0 73.01166000000001,17.94805,0 73.10914,17.76972,0 73.11443,17.63666,0 73.14166,17.59361,0 73.19914,17.58638,0 73.12776,17.56611,0 73.12776,17.53943,0 73.18581,17.44027,0 73.15998999999999,17.40055,0 73.24442999999999,17.28417,0 73.18359,17.29888,0 73.2747,17.08194,0 73.27139,17.03056,0 73.24832000000002,17.03555,0 73.28805,16.98166,0 73.26972000000001,16.85333,0 73.30971,16.80583,0 73.29109,16.7575,0 73.31693,16.62861,0 73.37276,16.62305,0 73.37276,16.59249,0 73.30859,16.60306,0 73.36774,16.55861,0 73.36054,16.51222,0 73.31581,16.55695,0 73.30165,16.5225,0 73.38194,16.39861,0 73.35581999999999,16.36889,0 73.40998999999999,16.21749,0 73.43804,16.21832,0 73.44776,16.10889,0 73.45499,16.16916,0 73.48859,16.18,0 73.44776,16.05584,0 73.51331999999999,16.01972,0 73.48553,15.99139,0 73.59387,15.89278,0 73.64943,15.78416,0 73.64193,15.74027,0 73.69721,15.72195,0 73.71638,15.61972,0 73.83526000000001,15.65944,0 73.73081999999999,15.59777,0 73.75832,15.49471,0 73.86388,15.53472,0 73.86499000000002,15.50667,0 73.78859,15.455,0 73.91692999999999,15.41916,0 73.96361,15.36361,0 73.91249000000001,15.40111,0 73.78610000000001,15.39805,0 73.88054,15.35971,0 73.96082000000001,15.15472,0 73.91553999999999,15.07805,0 74.01999000000001,15.00055,0 74.03361,14.91722,0 74.10136,14.86709,0 74.12303000000001,14.84417,0 74.20914999999999,14.89527,0 74.23499,14.87471,0 74.08971,14.79777,0 74.19414000000002,14.71444,0 74.22331,14.73777,0 74.25082999999999,14.71889,0 74.26470999999999,14.62222,0 74.33859,14.60611,0 74.28831,14.58417,0 74.30081,14.52166,0 74.3336,14.51555,0 74.35803,14.56833,0 74.42443,14.47611,0 74.39444,14.45332,0 74.35138000000001,14.51028,0 74.40942,14.29445,0 74.51186,14.24342,0 74.42525999999999,14.24861,0 74.49442999999999,14.01639,0 74.61913,13.8325,0 74.65721000000002,13.65972,0 74.68193000000001,13.70611,0 74.73443,13.65222,0 74.66388000000002,13.63194,0 74.67609,13.51028,0 74.71249000000002,13.46806,0 74.70139,13.41056,0 74.68248000000001,13.46278,0 74.68665,13.37833,0 74.77916999999999,13.08389,0 74.81026000000001,12.86472,0 74.84276,12.84556,0 74.93998000000001,12.87111,0 74.85999,12.82389,0 74.82332,12.83361,0 74.85526,12.755,0 75.10721,12.22,0 75.17613,12.06975,0 75.19582,12.00389,0 75.2186,12.01806,0 75.27831999999999,11.96194,0 75.25912,12.01889,0 75.29636000000002,12.03777,0 75.33167,12.01472,0 75.27442000000001,12.00777,0 75.30971,11.95222,0 75.39388,11.97222,0 75.39664999999999,11.91611,0 75.30775,11.93806,0 75.31219,11.90056,0 75.52858999999999,11.70027,0 75.61304,11.47166,0 75.67859,11.44583,0 75.7411,11.36306,0 75.72553000000001,11.32972,0 75.90748000000001,10.80333,0 75.93858,10.79277,0 75.92416,10.75556,0 76.05553999999999,10.505,0 76.15776,10.19527,0 76.20663999999999,10.20306,0 76.19497,10.26445,0 76.24109,10.22666,0 76.17747,10.17,0 76.19637,10.06528,0 76.23437,9.986049,0 76.23848,9.953955000000002,0 76.31582,9.463608000000001,0 76.44247,9.143325000000001,0 76.41914,9.249165,0 76.46138000000001,9.149445,0 76.48387,9.161667,0 76.47581,9.115550000000001,0 76.45471000000001,9.122499,0 76.52694,8.938890000000001,0 76.55692999999999,8.990829,0 76.55859,8.95166,0 76.57720999999999,8.994166999999999,0 76.66748,9.003609000000001,0 76.62943,8.981945,0 76.65860000000001,8.964998,0 76.60053000000001,8.971947,0 76.57082,8.939159,0 76.61414000000001,8.928890000000001,0 76.60415999999999,8.90972,0 76.54498,8.930277,0 76.54387,8.907496999999999,0 76.9986,8.365273999999999,0 77.30304,8.123327,0 77.54082,8.069715,0 77.5872,8.134722,0 77.77554000000001,8.181108,0 77.79275,8.225,0 77.99693000000001,8.338328000000001,0 78.06053,8.364167,0 78.13164999999999,8.486667000000001,0 78.13997999999999,8.617221000000001,0 78.11192,8.654715,0 78.21053000000001,8.75555,0 78.20804,8.781658999999999,0 78.16361000000002,8.754165,0 78.19247,8.904166999999999,0 78.39664999999999,9.089721000000001,0 78.65637,9.147779,0 78.66221,9.176660999999999,0 78.97110000000001,9.273607999999999,0 79.27222,9.252773,0 79.44081,9.148337,0 79.69427,9.086679,0 79.88623,8.969967000000001,0 79.91638000000002,8.931663,0 79.92596,8.795825000000001,0 79.77399000000001,8.533196,0 79.69804000000001,8.217504,0 79.70638,8.035830000000001,0 79.79694000000001,7.661394,0 79.82639,7.198893000000001,0 79.86248999999999,7.155837,0 79.8436,7.125272000000001,0 79.80748,7.192214,0 79.85831,7.024167000000001,0 79.85137,6.83639,0 79.98648,6.432363,0 80.02666000000001,6.268337000000001,0 80.12581,6.093891,0 80.20081999999999,6.026112000000001,0 80.44026,5.96555,0 80.46471,5.933340000000001,0 80.59193000000001,5.918336,0 80.71638,5.956946,0 80.8186,6.037776,0 81.11887,6.112503000000001,0 81.19609,6.181946,0 81.21138000000001,6.160284,0 81.32417,6.198327,0 81.66110000000001,6.440003000000001,0 81.78221000000001,6.624720000000001,0 81.89166,7.013609,0 81.87886,7.312221000000001,0 81.82656,7.473186000000001,0 81.80025999999999,7.610552,0 81.69465,7.757684,0 81.59305000000001,7.838892,0 81.56944000000001,7.983891000000001,0 81.51555000000002,8.003052,0 81.46361,8.112769999999999,0 81.43359,8.085824000000002,0 81.39027,8.140003999999999,0 81.39916,8.187497000000001,0 81.4397,8.101395,0 81.44331,8.173889000000001,0 81.39499000000001,8.340831,0 81.35498,8.366382,0 81.35442000000001,8.403893,0 81.39137,8.367217999999999,0 81.36136999999999,8.487215000000001,0 81.32026999999999,8.520002,0 81.26555000000001,8.453609,0 81.16775,8.514998,0 81.13054,8.500005,0 81.16137000000001,8.534447999999999,0 81.21388,8.508609,0 81.20833,8.570826,0 81.25221000000001,8.542214,0 81.20555000000002,8.608059000000001,0 81.23109,8.651106,0 80.97971,8.973611999999999,0 80.92081000000002,8.935551,0 80.93027000000001,8.979444000000001,0 80.87608,9.036395000000001,0 80.95750000000001,9.010556000000001,0 80.89249,9.111392,0 80.83222000000001,9.139715000000001,0 80.88388000000001,9.138609000000001,0 80.82444000000001,9.261945000000001,0 80.79665,9.266660999999999,0 80.80582000000001,9.240273,0 80.73499,9.302498999999999,0 80.80193,9.292769,0 80.36803000000002,9.653893999999999,0 80.24818999999999,9.827252,0 79.93916,10.12232,0 79.85971000000001,10.29538,0 79.84081999999999,10.81778,0 79.83110000000001,11.345,0 79.76389,11.36,0 79.67192,11.29556,0 79.76667000000001,11.38389,0 79.82249,11.37333,0 79.75139,11.54916,0 79.78386999999999,11.775,0 79.86720000000001,12.03277,0 80.0061,12.23027,0 79.94193,12.22305,0 80.03360000000001,12.27861,0 80.16025999999999,12.47305,0 80.25888000000001,12.78221,0 80.26277,12.94916,0 80.34972000000001,13.33417,0 80.22581,13.68,0 80.24748,13.80889,0 80.14915000000002,14.01111,0 80.12886,14.23667,0 80.03999,14.19528,0 80.07916,14.24472,0 80.13721000000001,14.25639,0 80.17415,14.34389,0 80.19859,14.57027,0 80.14413999999999,14.56555,0 80.18221,14.59638,0 80.15331000000001,14.67445,0 80.10578000000001,14.78102,0 80.04886999999999,15.04889,0 80.09081999999999,15.30916,0 80.20637000000002,15.48861,0 80.27943000000001,15.69917,0 80.55415000000001,15.87694,0 80.68552,15.88306,0 80.65776,15.88861,0 80.67137000000001,15.90583,0 80.80443000000001,15.84472,0 80.81507000000001,15.71571,0 81.01727,15.77555,0 81.00171,15.84445,0 81.15837000000001,15.97527,0 81.15782,16.05306,0 81.25944000000001,16.27662,0 81.41947999999999,16.33777,0 81.54474999999999,16.34416,0 81.54504000000001,16.37305,0 81.5856,16.33721,0 81.72726,16.31083,0 82.07449,16.44305,0 82.30199,16.575,0 82.28032,16.61944,0 82.31226,16.60444,0 82.34644,16.70472,0 82.37195,16.86854,0 82.36449,16.93222,0 82.35971000000002,16.82056,0 82.27222,16.86222,0 82.2486,16.92027,0 82.31331,17.04556,0 82.50555,17.22305,0 82.50565,13.81117,0 82.50581,8.811171,0 82.50595000000001,3.811175,0 82.5061,-1.188829,0 82.50624999999999,-6.188824,0 82.5064,-11.18883,0 82.50655,-16.18883,0 82.50658,-17.48883,0 82.50671,-22.48882,0 82.50684,-27.48883,0 82.50697,-32.48883,0 82.50700000000002,-33.68883,0 82.50713,-38.68883,0 82.50727999999999,-43.68883,0 82.5074,-48.68883000000001,0 82.50753000000002,-53.68882000000001,0 82.50766,-58.68883,0 82.5078,-63.68882,0 82.50792,-68.68883,0 82.50802,-72.28883000000002,0 82.50816,-77.28883,0 82.50829,-82.28883,0 82.50834999999999,-84.89999,0 82.50848000000001,-89.90000000000001,0 81.50870000000001,-89.90000000000001,0 80.50892,-89.90000000000001,0 79.50912000000001,-89.90000000000001,0 78.50934,-89.90000000000001,0 77.50954000000002,-89.90000000000001,0 76.50977,-89.90000000000001,0 75.50998,-89.90000000000001,0 74.5102,-89.90000000000001,0 73.51040999999999,-89.90000000000001,0 72.51063000000001,-89.90000000000001,0 71.51083,-89.90000000000001,0 70.51105,-89.90000000000001,0 69.51125,-89.90000000000001,0 68.51148000000001,-89.90000000000001,0 67.51170000000001,-89.90000000000001,0 67.51170000000001,-89.64822,0 67.51170000000001,-84.64822,0 67.51170000000001,-79.64822,0 67.51170000000001,-76.88749,0 67.51170000000001,-71.88749,0 67.51170000000001,-66.88749,0 67.51170000000001,-61.88749000000001,0 67.51170000000001,-56.88749,0 67.51170000000001,-51.88749,0 67.51169000000002,-46.88749,0 67.51169000000002,-41.88749,0 67.51169000000002,-39.58749,0 67.51169000000002,-34.58749,0 67.51169000000002,-29.58749,0 67.51169000000002,-24.58749,0 67.51169000000002,-22.17509,0 67.51169000000002,-17.17509,0 67.51169000000002,-12.17509,0 67.51169000000002,-7.175088,0 67.51169000000002,-2.175094,0 67.51169000000002,2.82491,0 67.51169000000002,7.824905,0 69.14654,7.824959000000001,0 74.14655000000001,7.825121,0 74.14655000000001,12.15223,0 72.51169,12.15223,0 67.51169000000002,12.15223,0 67.51169000000002,13.87695,0 67.51169000000002,18.87694,0 67.51169000000002,23.87695,0 67.48248,23.89583,0 67.53803000000002,23.93813000000001,0 67.50583,23.92138,0 67.50054,23.97499,0 67.45277,23.98666,0 67.3633,24.06714,0 67.28166,24.28083000000001,0 67.29803,24.33944,0 67.35942,24.34249,0 67.27887,24.35694,0 67.28554,24.43639,0 67.25722,24.41361,0 67.21361000000002,24.50361,0 67.22971,24.53305,0 67.18165000000001,24.57972,0 67.25166,24.58916,0 67.15637000000001,24.6075,0 67.14679,24.6441,0 67.07283000000001,24.7676,0 66.96082,24.83861,0 66.9511,24.80472,0 66.85943,24.85444,0 66.65028,24.82611,0 66.70499,24.89167,0 66.67415,24.93388,0 66.73276,25.19749,0 66.54637,25.3629,0 66.49576999999999,25.39812,0 66.26389,25.46778,0 65.84665,25.41667,0 65.66776,25.34278,0 65.46642,25.38267,0 65.38777,25.3625,0 65.38611,25.39056,0 65.25417,25.37749,0 65.13970000000001,25.28778,0 64.76888,25.32138,0 64.66776000000002,25.25694,0 64.66499,25.20944,0 64.70999000000001,25.18027,0 64.61998,25.16999,0 64.63109,25.22056,0 64.59276,25.25556,0 64.51666,25.26944,0 64.43497000000001,25.23111000000002,0 64.25555,25.31472,0 64.09166999999999,25.32861000000001,0 64.06299,25.33679,0 63.92917000000001,25.33416,0 63.7486,25.38527,0 63.57249,25.36,0 63.46998,25.28028,0 63.52361,25.21361,0 63.50500000000002,25.18861,0 63.15583,25.25638,0 63.00805,25.21471,0 62.74471,25.25945,0 62.52693,25.25556,0 62.46444,25.23638,0 62.48554000000001,25.20944,0 62.36221000000001,25.17332,0 62.33638,25.12778,0 62.39111000000001,25.10221,0 62.35305,25.09195,0 62.28167000000001,25.105,0 62.31917,25.14277,0 62.29527,25.19916,0 62.16777,25.21638,0 62.07777000000002,25.18138,0 62.10444,25.10167,0 61.91137000000001,25.11277,0 61.86193000000001,25.09277,0 61.83721,25.03444,0 61.72444,25.0525,0 61.78221,25.19027000000001,0 61.61103000000001,25.19764,0 61.64695,25.30611,0 61.66859000000001,25.63277,0 61.70361000000001,25.67667000000001,0 61.68638,25.795,0 61.77583000000001,25.81861,0 61.84416,26.21166,0 61.87610000000001,26.24638,0 62.10388,26.31444,0 62.12887,26.34444,0 62.13583000000001,26.38083,0 62.27803000000001,26.35402,0 62.27138,26.42472,0 62.32027000000001,26.51527,0 62.44609,26.56888,0 62.74305,26.61167,0 62.77276000000001,26.64972,0 63.17638000000001,26.63111,0 63.20638,26.84222,0 63.29056,26.89222,0 63.25916,26.93443,0 63.25305,27.08361,0 63.27859,27.12194,0 63.34194,27.1225,0 63.28832,27.21861,0 63.20583,27.2675,0 62.93333000000001,27.21333,0 62.82305000000002,27.23027,0 62.76471,27.27194,0 62.79583,27.28555,0 62.80694,27.32332,0 62.77833,27.35333,0 62.83972,27.47443000000001,0 62.82193,27.76027,0 62.75777,28.00027,0 62.78138,28.26695,0 62.59249,28.2336,0 62.40499,28.41722,0 61.90554,28.555,0 61.58138000000001,28.88083,0 61.53361000000001,29.0125,0 61.48804000000001,29.04361,0 61.51111,29.08861,0 61.42471,29.15528000000001,0 61.36027,29.26583000000001,0 61.3586,29.38444000000001,0 61.31443000000001,29.39583,0 60.86630000000002,29.86366,0 62.48444,29.40611,0 63.5872,29.50389,0 64.12221,29.38861,0 64.21138000000001,29.4986,0 64.47859,29.57306,0 64.69553000000001,29.58638,0 65.03416,29.54111,0 66.25664999999999,29.85194,0 66.36221000000001,29.9561,0 66.23831000000001,30.07138,0 66.32527,30.23778,0 66.35165000000001,30.44527,0 66.28194000000001,30.57528,0 66.39554,30.94083,0 66.57277000000001,30.98138,0 66.68414,31.08611,0 66.72304,31.21222,0 66.79304000000001,31.21972,0 66.84166999999999,31.2725,0 66.95694,31.31806,0 67.04276,31.31639,0 67.05582,31.24305,0 67.25648,31.22233,0 67.38472,31.21527,0 67.79192000000001,31.3411,0 67.77303000000001,31.40861,0 67.66775,31.4,0 67.61526000000002,31.42777,0 67.57555000000001,31.53194,0 67.73193000000001,31.52916,0 67.88193,31.63805,0 67.98054999999999,31.63583,0 68.07165999999999,31.69527,0 68.16609,31.83305,0 68.27222,31.77166,0 68.53137,31.72693,0 68.57165999999999,31.76527000000001,0 68.43719,31.78667,0 68.51944,31.825,0 68.70722000000001,31.78111000000001,0 68.72803999999999,31.70416000000001,0 68.83305,31.60388,0 69.01944,31.65055,0 69.09972000000001,31.70667,0 69.2244,31.87129,0 69.32825,31.94036,0 69.27193,32.14527,0 69.29137,32.3175,0 69.24802,32.44388,0 69.28749000000001,32.52639,0 69.37776,32.56833,0 69.45833,32.67054,0 69.39499000000001,32.77388,0 69.51277,32.86944,0 69.49081,33.00388,0 69.58083000000001,33.08693,0 69.70081999999999,33.07777,0 69.79137,33.12694,0 69.88109,33.08999,0 70.02888,33.13583000000001,0 70.06776000000001,33.20499,0 70.14499000000001,33.20249,0 70.32693,33.33193,0 70.28610000000001,33.42276,0 70.16804000000001,33.52083,0 70.19664,33.64083,0 70.15138,33.66554,0 70.1375,33.73249000000001,0 69.98554,33.75306,0 69.86913,33.92999,0 69.90637,34.03527,0 70.00276,34.05277000000001,0 70.49136,33.94305,0 70.89109999999999,33.96638,0 70.90555000000001,34.01333,0 70.98109,34.00889,0 71.07720999999999,34.05222,0 71.07720999999999,34.11277,0 71.13554000000001,34.1661,0 71.11832,34.26027,0 71.15886999999999,34.34833,0 71.00054,34.46305,0 70.98748999999999,34.5511,0 71.0886,34.55943,0 71.09583000000001,34.67666,0 71.22553000000001,34.74444,0 71.30914000000001,34.88361,0 71.49890000000001,34.96635,0 71.53803000000001,35.01972,0 71.53554,35.08665,0 71.66388000000001,35.19555,0 71.54859999999999,35.30111,0 71.64943,35.42499,0 71.60193,35.50055,0 71.61665000000001,35.55526,0 71.50888000000001,35.62083,0 71.54526,35.71111,0 71.49082,35.75667,0 71.43997,35.87082,0 71.38721,35.89695,0 71.38499,35.94194,0 71.18706,36.04219,0 71.3389,36.20492,0 71.41252000000002,36.22043,0 71.50725,36.31537,0 71.5789,36.33173,0 71.56305999999999,36.37228,0 71.63307,36.46555,0 71.80834,36.40364,0 71.79584,36.49164,0 71.90443000000001,36.50722,0 72.07471,36.58915,0 72.07916,36.63162,0 72.18163,36.64969,0 72.17554,36.7113,0 72.47416,36.7624,0 72.55663,36.82126,0 73.6683,36.90964,0 73.8522,36.89273000000001,0 74.03831,36.81468,0 74.12109000000001,36.83858000000001,0 74.15166000000001,36.90387,0 74.25194000000001,36.89917,0 74.41998,37.00144,0 74.48888,37.01034000000001,0 74.55304,36.96173000000001,0 74.56543000000001,37.02782,0 74.69081,37.06259,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<innerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +70.25215,-6.970275,0 70.25215,-9.433405000000001,0 73.35092,-9.431101,0 73.34769,-6.970275,0 70.25215,-6.970275,0 </coordinates>
    +					</LinearRing>
    +				</innerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +78.59721999999999,72.20972,0 78.67693,72.17944,0 78.80803,72.18332000000001,0 78.7711,72.17027,0 78.91165,72.14444,0 78.91081,72.11471,0 79.01859,72.11998,0 79.09193000000001,72.10165000000001,0 79.09444000000001,72.07055,0 79.38275000000002,72.06219,0 79.42276,72.03554,0 79.69692000000001,72.05915,0 79.86720000000001,72.02249000000001,0 79.91002,71.98448000000001,0 80.23093,71.94193,0 80.24838,71.91006,0 80.34878999999999,71.88601,0 80.2719,71.87848,0 80.27464000000001,71.85148,0 80.17328999999999,71.86931,0 80.03504,71.83147,0 80.12739999999999,71.7749,0 80.06631,71.76943,0 80.13540999999999,71.67453,0 79.86972,71.60027,0 79.38026000000001,71.61637,0 79.33138,71.59775999999999,0 79.39082000000001,71.54387,0 79.3586,71.49413,0 79.14027,71.42165,0 79.1747,71.34609,0 79.22915999999999,71.3175,0 79.50832,71.29331999999999,0 79.96638,71.13109,0 80.55004,71.05757,0 80.56647,70.85477,0 80.69016000000001,70.80186000000001,0 80.59229000000001,70.71675000000001,0 80.75629000000001,70.66586,0 80.72072,70.5945,0 80.6601,70.57407000000001,0 80.66569,70.51388,0 80.60648999999999,70.50095,0 80.67014000000002,70.44623,0 80.79858,70.44119000000001,0 80.79106,70.41504999999999,0 80.72770000000001,70.41077,0 80.76217,70.39443,0 80.70740000000001,70.39100999999999,0 80.69155000000001,70.42319999999999,0 80.62567,70.42167000000001,0 80.57176,70.46187999999999,0 80.17683,70.43655,0 80.18601000000001,70.39182,0 80.08208999999999,70.39373000000001,0 80.11404,70.36096000000001,0 79.85748,70.27222000000002,0 79.7247,70.28415000000001,0 79.70360000000001,70.26999000000001,0 79.73831,70.24887,0 79.70889,70.23859,0 79.62943,70.22109,0 79.49887,70.11053,0 79.32805000000001,70.14888000000001,0 79.34943,70.04498,0 79.19443,70.05165,0 79.17859,70.03498,0 79.21860000000001,70.01166000000001,0 79.11971,70.00664999999999,0 79.18747999999999,69.96416000000002,0 78.96582000000001,69.88026000000002,0 78.87527,69.88164999999999,0 79.01888,69.83499999999999,0 78.97582000000001,69.81165,0 79.06331,69.78664000000001,0 79.06638,69.74721,0 79.13970999999999,69.74303999999999,0 79.00417,69.69693,0 79.50583,69.65358999999999,0 79.49165000000001,69.63638,0 79.5372,69.63081,0 79.50082,69.60471,0 79.59583000000001,69.58832,0 79.63916999999999,69.48582,0 79.74082,69.44832,0 79.71388,69.43248,0 79.82805000000001,69.42249,0 79.82278,69.39444,0 79.8772,69.37276,0 79.85914000000001,69.34610000000001,0 80.14597999999999,69.36324999999999,0 80.76669000000001,69.30504000000001,0 80.79311,69.26394999999999,0 80.95596000000001,69.20667,0 81.33582,69.28803000000001,0 81.44414,69.25443000000001,0 81.58249000000001,69.27582,0 81.64028,69.31135999999999,0 81.61387000000002,69.32833000000001,0 81.62302,69.41526,0 81.67249,69.44832,0 81.7811,69.43137,0 81.93611,69.34665,0 81.75972000000002,69.25249000000001,0 81.81331,69.20055000000001,0 82.46388,69.16054,0 82.46138999999999,69.12442,0 82.50472000000002,69.10831,0 82.31638,69.05387,0 82.49721,68.98775999999999,0 82.5522,68.81833,0 82.78288000000001,68.79881,0 82.80164000000001,68.74082,0 83.04276,68.66776,0 82.9011,68.62527,0 82.69302999999999,68.63275,0 82.58999,68.60276,0 82.55859000000001,68.61111,0 82.54915,68.61443,0 82.45610000000001,68.57832999999999,0 82.68053999999999,68.49137,0 82.71249,68.40443000000001,0 82.68443000000001,68.34636999999999,0 82.70663999999999,68.29331999999999,0 82.57361,68.25583,0 82.52332000000001,68.20694,0 82.54359,68.17276,0 82.39276,68.17747,0 82.35693000000001,68.09583000000001,0 82.39610999999999,68.05914,0 82.37442,67.94914,0 81.98888,67.96332,0 81.8772,67.93165999999999,0 81.76194,67.95247999999999,0 81.72443000000001,67.92415,0 81.90304999999999,67.77778000000001,0 82.10554999999999,67.72887,0 82.08055,67.68858,0 82.11913,67.68747999999999,0 82.04109,67.65665,0 82.11416,67.59943,0 82.39664999999999,67.53498999999999,0 82.35138000000001,67.50109999999999,0 82.38054,67.47971,0 82.32971000000001,67.48109,0 82.2336,67.42081,0 82.33778,67.3608,0 82.2811,67.34887000000001,0 82.3647,67.33055,0 82.21833,67.31137,0 82.25915999999999,67.27998,0 82.18165999999999,67.28803000000001,0 82.19721,67.25943,0 82.11942999999999,67.23888000000001,0 83.04109,66.89221000000002,0 83.06192,66.88081,0 83.02748,66.85971000000001,0 83.17249,66.80914,0 83.1836,66.74748,0 83.30582,66.66165,0 83.08666,66.61053,0 83.08693,66.46972,0 83.12469,66.42165,0 83.34415,66.34193000000001,0 83.53970000000001,66.19525,0 83.37109,66.13666000000001,0 83.28943,66.06693,0 83.37276000000001,65.9836,0 83.52804999999999,65.90443000000001,0 83.50637,65.82944000000001,0 83.59108999999999,65.7736,0 84.01111,65.79832,0 84.17665,65.7,0 84.32249,65.67609,0 84.31914999999999,65.66943000000001,0 84.29276000000002,65.6122,0 84.19054,65.59444000000001,0 84.45028000000001,65.54554,0 84.55464000000001,65.43516,0 84.50194000000001,65.39664999999999,0 84.58832,65.31303,0 84.52443,65.21278,0 84.40999000000001,65.19082,0 84.3111,65.09860000000001,0 84.31386999999999,65.05721,0 84.38108,65.03276,0 84.39665000000001,64.99387,0 84.27804999999999,64.94054,0 84.29137,64.9011,0 84.90665,64.93026999999999,0 84.97748,64.88666000000001,0 84.93222,64.82639,0 85.09444000000001,64.81219,0 85.14694,64.75166,0 85.54694000000001,64.83028,0 85.86081,64.75526000000001,0 85.80859,64.64749000000001,0 85.91804000000001,64.5836,0 85.84526,64.50749,0 85.84081999999999,64.45889,0 85.92359999999999,64.41721,0 85.94970000000002,64.32832999999999,0 86.0311,64.27528,0 85.93248000000001,64.12776,0 85.96776,64.09775999999999,0 85.95804,64.05443,0 85.51639,63.93000000000001,0 85.36693,63.81333,0 85.33998,63.70693000000001,0 85.26721000000001,63.64638000000001,0 85.17693,63.59137,0 85.08998,63.60860000000002,0 85.03203000000001,63.58649000000001,0 85.08194,63.53638000000001,0 85.21193,63.50306000000001,0 85.2961,63.51583,0 85.37415,63.46220000000002,0 85.38970000000002,63.42333000000001,0 85.35276,63.40276999999999,0 85.40776,63.35,0 85.55942,63.37499,0 85.67387000000001,63.35443999999999,0 85.53665,63.26083,0 85.47832,63.17054,0 85.46638,63.13805,0 85.58716000000001,63.06996,0 85.49332,62.99027000000001,0 85.52083,62.93277,0 85.4511,62.88277,0 85.33249000000001,62.87331,0 85.2372,62.81444000000001,0 85.07777,62.67138000000001,0 85.09860000000001,62.6386,0 84.94803000000002,62.59137000000001,0 84.89055000000001,62.54220999999999,0 84.91387,62.49944000000001,0 84.85915,62.44778,0 84.72638000000001,62.40694,0 84.43692,62.18915,0 84.55441999999999,61.99694,0 84.51666,61.93915,0 84.55749,61.89472000000001,0 84.63388999999999,61.87971000000001,0 84.68749,61.8061,0 85.00805,61.78416,0 85.24832000000001,61.68526000000001,0 85.38582000000001,61.70167,0 85.46777,61.62860000000001,0 85.72275999999999,61.57361,0 85.85193,61.59777,0 85.96915,61.54305,0 85.96639,61.46915,0 85.75249,61.43999,0 85.64360000000001,61.38805,0 85.71054,61.29749,0 84.61415,61,0 84.25964000000001,60.85542,0 83.99387,60.82472,0 83.50944,61.04916,0 83.14499000000001,61.03277000000001,0 82.38247,60.71278,0 82.39027,60.61028,0 82.16665,60.51832,0 81.86165,60.65055,0 81.49776,60.61555,0 81.11116,60.6375,0 81.0555,60.68084,0 81.03823000000001,60.75419,0 80.69771000000002,60.80017,0 80.63061,60.76768000000001,0 80.42100000000001,60.76556999999999,0 80.18398999999999,60.66193,0 79.71581999999999,60.69555,0 79.50249,60.68694,0 79.38666000000001,60.64861,0 79.28360000000001,60.71999000000001,0 79.31833,60.79667000000001,0 79.18553,60.83860000000001,0 78.96915000000001,60.82805,0 78.91110000000001,60.78722000000001,0 78.80721,60.77887,0 78.68941,60.83083,0 78.47527,60.77694,0 78.12025,60.80222000000001,0 77.92137,60.74665,0 77.68941,60.82749,0 77.40804,60.81027,0 77.10498,60.85333000000001,0 77.07805000000001,60.72721,0 76.96776,60.71,0 76.96527,60.64111,0 77.03167000000001,60.63277,0 77.04721000000001,60.60361,0 77.03749000000001,60.535,0 76.77443,60.47665,0 76.74471,60.34249000000001,0 76.8186,60.33028000000002,0 76.85165000000001,60.26806000000001,0 76.68719,60.10777000000001,0 76.75888999999999,60.06081,0 76.74136,59.72943000000001,0 76.64554000000001,59.69332,0 76.64833,59.58027,0 76.45583000000001,59.54445,0 76.16304,59.54361,0 75.99082,59.41972,0 75.89749000000001,59.43721000000001,0 75.83138,59.29639,0 75.61526000000001,59.2411,0 75.69054,59.01194,0 75.38164999999999,58.78804,0 75.15028000000001,58.67805,0 75.17081,58.6186,0 75.10526,58.58111,0 74.89166,58.47776,0 74.87608,58.42722,0 74.79998999999999,58.42694,0 74.62665000000001,58.33915,0 74.62053,58.29361000000001,0 74.45499,58.25861,0 74.20638,58.13361,0 73.08443,58.14916,0 72.93858,58.08749000000001,0 72.96915,58.06055,0 72.87221,58.0061,0 72.12164,58.02304000000001,0 72.07055,58.09415,0 71.98888,58.12471,0 71.23859,58.07111,0 71.24887,58.12194,0 71.21915,58.14695,0 71.24721,58.3561,0 71.30387,58.39749000000001,0 71.11998,58.44887,0 71.07888,58.5286,0 70.87302,58.5425,0 70.43691,57.9336,0 70.37864999999999,57.91712,0 70.44171,57.83843000000001,0 70.42554,57.76639,0 70.48387,57.76777,0 70.49887,57.7075,0 70.57443000000001,57.70916,0 70.57720999999999,57.57861,0 70.66721,57.59276,0 70.69108,57.56666,0 70.68747999999999,57.51445,0 70.42498999999999,57.38916,0 70.51721000000001,57.28695,0 70.65609000000001,57.26416,0 70.60971000000001,57.19916,0 70.69443,57.19305,0 70.78194000000001,57.19916,0 70.75417,57.27749,0 70.81165,57.32804,0 71.10248,57.34083,0 71.09248,57.28999,0 71.14776999999999,57.25139000000001,0 71.09027,57.22055,0 71.23387,57.20027,0 71.37082,57.06360999999999,0 71.61304,56.95693,0 71.65860000000001,56.91470000000001,0 71.67554,56.82638,0 71.58553000000001,56.79445,0 71.54082,56.71915000000001,0 71.39695,56.73305,0 71.39388,56.68472,0 71.34887000000001,56.66916,0 71.16665999999999,56.67138,0 71.05582,56.71028,0 71.03306000000001,56.69221000000001,0 71.11913,56.64555,0 71.09081999999999,56.55555,0 70.95416,56.4911,0 70.85776,56.50305,0 70.75972,56.46111000000001,0 70.78471999999999,56.43665,0 70.86248999999999,56.46389000000001,0 70.94136,56.41167000000001,0 70.93359,56.34694,0 70.76277,56.28667000000001,0 70.77222,56.25471000000001,0 70.84027,56.25695,0 70.81749000000001,56.17333,0 70.91638,56.11055,0 70.80415000000001,55.92028000000001,0 70.47415,55.89166000000001,0 70.47527,55.85222000000001,0 70.59138,55.83611000000001,0 70.75915999999999,55.86332,0 70.78554,55.83306,0 70.58360999999999,55.7961,0 70.54693,55.67499,0 70.7636,55.59778,0 70.69664,55.50638,0 70.54915,55.49138,0 70.48775999999999,55.43859000000001,0 70.46608999999999,55.40499000000001,0 70.50666,55.33777000000001,0 70.46998000000001,55.29667000000001,0 70.39833,55.20917,0 70.28804,55.18721,0 70.23888,55.13888,0 70.05582,55.20776999999999,0 69.94914,55.20416,0 69.84610000000001,55.29500000000001,0 69.67276,55.35443,0 69.35138000000001,55.39443000000001,0 69.23276,55.3325,0 69.18552,55.34388000000001,0 69.16831999999999,55.40054,0 68.95247999999999,55.44471,0 68.9233,55.42416,0 68.93941,55.37028000000001,0 69.00109999999999,55.35361,0 69.00249,55.28749,0 68.93332,55.28611,0 68.87997,55.34276,0 68.73137,55.37055,0 68.70499000000001,55.28667,0 68.63889,55.25555,0 68.62581,55.20192999999999,0 68.30443,55.20527,0 68.19971,55.17833000000001,0 68.24444,55.10471,0 68.33083000000001,55.08056,0 68.25943,55.04888,0 68.20526,54.96609,0 68.03442,54.95027,0 67.9297,54.98332,0 67.83223,54.96332000000001,0 67.76639000000002,54.89194,0 67.58138,54.85443,0 67.32277000000001,54.86749,0 67.24552,54.80665,0 66.80887,54.76666,0 66.72275999999999,54.72694,0 66.49525,54.72304,0 66.10831,54.64500000000002,0 66.11304,54.5425,0 66.15192,54.48555,0 66.1236,54.44500000000001,0 66.16277,54.41945,0 66.15358999999999,54.38055,0 66.21944000000001,54.28055,0 66.11803999999999,54.2375,0 66.20610000000001,54.18333000000001,0 66.27858000000001,54.19028,0 66.35693000000001,54.08444,0 66.21442,54.07694999999999,0 66.00139,53.99471,0 66.03610000000002,53.91805,0 66.07581999999999,53.91526000000001,0 66.06581,53.79277,0 66.16499,53.78111,0 66.18304000000001,53.68249000000001,0 66.11775,53.63082,0 66.11165,53.49276,0 66.25526000000001,53.48555,0 66.26888,53.28277,0 66.26639,53.19388000000001,0 66.41443,53.1686,0 66.40526,53.09972000000001,0 66.20359999999999,53.10443,0 66.08194,53.06277,0 66.07777,52.96305000000001,0 66.41277,52.95222,0 66.40886999999999,52.90331,0 66.14360000000001,52.88610000000001,0 66.13443,52.81998999999999,0 66.05247,52.8186,0 66.02887,52.45304000000001,0 65.58414999999999,52.38972,0 65.58472,52.32777,0 65.46082,52.29639,0 65.04498,52.28027,0 65.05859,52.23165,0 64.75166,52.18166999999999,0 64.74887,52.12832,0 64.80138,52.12444,0 64.80609,52.02555,0 64.925,51.90889,0 65.00888000000001,51.89249,0 65.01666,51.82332,0 64.95775999999999,51.74943,0 65.01138,51.63777,0 64.94247,51.61306,0 64.95444000000001,51.52694,0 64.88415999999999,51.47027,0 65.13164999999999,51.28139000000002,0 65.21608999999999,51.24888000000001,0 65.30359,51.28221,0 65.40694000000001,51.23166,0 65.29498,51.16471,0 65.20583000000001,51.17332,0 64.97776,51.10333,0 64.91415000000001,51.13972,0 64.84972000000001,51.09664999999999,0 64.81944,51.07222000000001,0 64.91553999999999,51.00805,0 64.81386999999999,50.78638,0 64.73027,50.73805,0 64.69275,50.6711,0 64.60415999999999,50.65138000000002,0 64.56219,50.67888,0 64.47248999999999,50.63304999999999,0 64.47837,50.58194000000001,0 64.38943,50.55166,0 64.36461,50.51109,0 64.25,50.54055,0 64.22553000000001,50.51416,0 64.05998,50.57749,0 64.18747999999999,50.7336,0 64.25888000000001,50.75054999999999,0 64.34915000000001,50.87749000000001,0 64.09942,51.03916,0 63.81416,50.93859,0 63.265,50.82638000000002,0 63.20193,50.8772,0 63.13193,50.82222,0 62.93999,50.86555,0 62.86833000000001,50.82110999999999,0 62.78388,50.86056,0 62.42528,50.85695,0 62.19276,50.80443000000001,0 62.23583,50.74138,0 62.18916000000001,50.71000000000001,0 62.25444000000001,50.68249,0 62.38249,50.7225,0 62.50833000000001,50.46805,0 62.69833,50.28305000000001,0 62.71027,50.2122,0 62.86415,50.09777,0 62.94083000000001,49.99138,0 62.90444,49.89415000000001,0 62.73861,49.94778,0 62.57667000000001,49.85277,0 62.59694,49.77999000000001,0 62.45444,49.67888,0 62.48417,49.54805000000001,0 62.37360000000001,49.56833,0 62.30305,49.48638,0 62.35027,49.46944000000001,0 62.54499000000001,49.25111,0 62.35665000000001,49.25944,0 62.33611,49.19971,0 62.46249,49.17638000000001,0 62.56527,49.01082,0 62.84722,48.84332000000001,0 62.97610000000001,48.91194,0 63.10055000000001,48.86055,0 63.05693,48.72249,0 63.09416,48.67943000000001,0 63.41499000000001,48.59249,0 63.47083,48.53583,0 63.76111,48.45082,0 63.91554,48.23388,0 64.01220000000001,48.15694,0 64.13443,47.98499,0 64.28915000000001,47.88832,0 64.01609999999999,47.72165,0 63.19526000000001,47.34083,0 63.14249,47.285,0 62.85190999999999,47.13889,0 62.69305000000001,47.04257,0 63.08611,47.08972,0 63.41805000000001,46.92028000000001,0 64.38889,46.69665000000001,0 64.97026,46.47276,0 65.11913,46.48082000000002,0 65.27638,46.36306,0 65.62943,46.18166000000001,0 66.53555,46.14277,0 67.12168,46.16551,0 67.15776,46.12944000000001,0 67.16988000000001,45.99897000000001,0 67.18192999999999,45.9411,0 67.31441,45.84720999999999,0 67.35831,45.7711,0 67.25333000000001,45.58389,0 67.24942,45.45778000000001,0 67.17833,45.29341,0 67.36275999999999,45.17916,0 67.50499000000001,45.16194,0 67.50360000000001,44.40028,0 67.47887,44.36361,0 67.79832,44.26417,0 67.765,44.23277,0 67.82887000000001,44.15971,0 68.03525999999999,44.06888000000001,0 68.08277,44.00528,0 67.96387000000001,43.94972,0 67.94193,43.90527,0 67.99026000000001,43.80555,0 67.97387000000001,43.69555,0 67.84666,43.74749,0 67.84221000000001,43.58804,0 67.66199,43.37128,0 67.6797,43.30249000000001,0 67.72026,43.37276,0 67.75694,43.36055,0 67.72638000000001,43.35305000000001,0 67.74274,43.33693,0 67.7961,43.33749,0 67.8111,43.30249000000001,0 67.77831000000001,43.27805,0 67.82971000000001,43.24944,0 67.01609999999999,42.8661,0 66.21886000000001,42.42361,0 66.07079,42.42387,0 66.03665,42.42332,0 66.02916999999999,42.00305000000001,0 66.52638000000002,42.00305000000001,0 66.56247999999999,41.71693,0 66.71998000000001,41.17499,0 66.75949000000001,41.16484000000001,0 66.75305,41.11693,0 66.60804,41.11333000000001,0 66.63247,40.93888,0 66.7664,40.88750000000001,0 66.80525,40.76611,0 66.62003,40.49509,0 66.88831999999999,40.47833,0 66.90860000000001,40.24804,0 66.97998,40.17389,0 66.93471,40.125,0 66.9422,40.06333,0 67.03471999999999,40.06888,0 67.02361000000001,40.03277,0 67.10554999999999,39.98611,0 67.47971,39.93194,0 67.50444,39.81554,0 67.38136,39.78055,0 67.37109,39.68777,0 67.29248,39.66555,0 67.29803,39.58611,0 67.44196,39.48359,0 67.41054,39.29916,0 67.36609,39.29361,0 67.37636999999999,39.21249,0 67.69026,39.13805,0 67.70943,38.99666000000001,0 68.12387,38.98554,0 68.19637,38.85444,0 68.15498,38.8086,0 68.08305,38.79445,0 68.10081,38.73916,0 68.05775,38.69694,0 68.07193,38.54138,0 68.1272,38.42444,0 68.21554999999999,38.33221,0 68.33138,38.27333,0 68.38415000000001,38.19555,0 68.27748,37.95889,0 68.15054000000001,37.92805,0 68.07305,37.76528,0 67.83832,37.50583,0 67.81749000000002,37.4461,0 67.83804000000001,37.26194,0 67.77988000000001,37.18582,0 67.75526000000001,37.22054,0 67.64333000000001,37.24722,0 67.55748,37.21555,0 67.49249,37.28083,0 67.39722,37.21749,0 67.23997,37.1861,0 67.18637,37.25805,0 67.11749,37.28028,0 67.09648,37.33997,0 67.00417,37.38221,0 66.67221000000001,37.33833,0 66.53773,37.36638,0 66.44748,37.31860000000001,0 66.40833000000001,37.3461,0 66.28999,37.32472,0 66.09054999999999,37.43167,0 65.84526,37.50444,0 65.78167999999999,37.5715,0 65.77193,37.53749,0 65.68109,37.51694,0 65.64444,37.44887,0 65.65082,37.35555000000001,0 65.55443,37.25638,0 65.07277000000001,37.24444,0 64.82304000000001,37.1386,0 64.77916999999999,37.09583,0 64.79526,36.92305,0 64.61693,36.63638,0 64.61526000000001,36.42361,0 64.47387000000001,36.25555,0 64.33665000000001,36.22471,0 64.28248000000001,36.15193,0 64.18137,36.17054,0 64.09498000000001,36.12332,0 64.05971,36.08805,0 64.06386999999999,36.00027,0 63.92055,36.0375,0 63.77832999999999,35.98054000000001,0 63.60111000000001,35.96443,0 63.52833,35.90499,0 63.34249000000001,35.85722000000001,0 63.11944,35.86193,0 63.12972000000001,35.77527,0 63.24002000000002,35.69928,0 63.09528,35.62609,0 63.12666,35.53361,0 63.10527,35.45083,0 62.92249000000001,35.38417,0 62.72220999999999,35.25472,0 62.55444000000001,35.2336,0 62.46500000000001,35.28861,0 62.30915000000002,35.14166,0 62.26944000000001,35.28583,0 62.13256,35.36806,0 61.99583,35.45832,0 61.80249,35.4111,0 61.59583000000001,35.44415,0 61.40610000000002,35.56388,0 61.37888,35.63527,0 61.27655000000001,35.60725,0 61.22887,35.67805,0 61.26222,35.81972,0 61.22582,35.90388,0 61.12428,35.97565,0 61.16999,35.99527,0 61.16471,36.03527,0 61.22916,36.12666,0 61.18444,36.28667,0 61.15028,36.31499,0 61.18999,36.56777,0 61.1572,36.64999,0 60.33306000000001,36.6561,0 60.27840000000001,36.76272,0 60.02944,37.03695,0 59.91471000000001,37.0586,0 59.81305,37.12471000000001,0 59.67054000000001,37.15611,0 59.62027000000001,37.13194,0 59.56693,37.20888,0 59.47693,37.21889000000001,0 59.49139,37.24833,0 59.39305000000001,37.32306,0 59.38082,37.48555,0 59.33888,37.53916000000001,0 59.25,37.51333,0 59.06027,37.62999,0 58.82278,37.70361,0 58.80444,37.66693,0 58.71277000000001,37.64861,0 58.5625,37.7086,0 58.49805,37.64777,0 58.38915999999999,37.63611,0 58.22748,37.68111,0 58.20304000000001,37.78334000000001,0 58.17443,37.79639,0 58.04694000000001,37.80944,0 57.70805,37.92665,0 57.45028000000001,37.93916,0 57.35306,37.98944,0 57.3711,38.09305,0 57.28833,38.17055,0 57.24388,38.27082,0 57.20471,38.28334,0 57.18888,38.28334,0 57.14979,38.26009,0 57.05861,38.1936,0 56.84583,38.23166000000001,0 56.75574000000001,38.28626,0 56.61388,38.24055,0 56.44276,38.25999,0 56.32249000000001,38.18526,0 56.35277,38.13889,0 56.33305000000001,38.08554,0 56.22305,38.07028,0 56.16444,38.09499,0 55.98276000000001,38.07249,0 55.7561,38.12526,0 55.44276000000001,38.08611,0 55.21027,37.96221,0 55.14083,37.9561,0 54.95471000000001,37.80499,0 54.83306000000001,37.74639,0 54.78221000000001,37.64804,0 54.80972000000001,37.57944,0 54.78694,37.51915000000001,0 54.68111,37.4436,0 54.55165,37.4461,0 54.23165000000001,37.32722,0 53.90564,37.35085,0 54.01583000000001,37.02193000000001,0 53.98833,36.9822,0 54.03166,36.94833,0 54.0286,36.82471,0 53.94332,36.8,0 53.68194,36.81722,0 53.63388000000001,36.84804,0 53.66137000000001,36.85777,0 53.6075,36.86833000000001,0 53.98499,36.89861000000001,0 54.00860999999999,36.94833,0 53.92276,36.90972,0 53.22305,36.84833,0 51.97276000000002,36.57999,0 51.10111,36.72887,0 50.35167000000001,37.1211,0 50.19137000000001,37.38917,0 49.94166,37.44471,0 49.96888,37.45387,0 49.92499,37.47971,0 49.83443,37.4511,0 49.41999,37.49332,0 49.10248,37.64304,0 48.99776,37.76361,0 48.94166,37.90749,0 48.86942,38.30054,0 48.88828000000001,38.44241,0 48.86665,38.74583,0 48.82999,38.85942,0 48.89083,38.88082,0 48.93304,38.95138,0 48.95554,39.16526,0 49.03832000000001,39.18221,0 49.06722000000001,39.10777,0 49.14471,39.09748,0 49.12694,39.02998,0 49.05165,39.00139,0 49.06027000000001,38.96693,0 49.12804,38.97331,0 49.20194,39.02471,0 49.25805,39.31944,0 49.31304,39.34138,0 49.40805,39.28776,0 49.36388000000002,39.34499,0 49.40331,39.31805,0 49.41361,39.38388,0 49.28082000000001,39.50055,0 49.31889000000001,39.62498,0 49.42416999999999,39.70638000000001,0 49.39332,39.77832,0 49.44442000000001,39.8686,0 49.42110000000001,39.93081,0 49.48304,39.98943000000001,0 49.42499999999999,40.05388,0 49.49582,40.09055,0 49.48805000000001,40.15054,0 49.57167,40.21277,0 49.82472,40.28499,0 49.87804000000001,40.36276,0 49.98054,40.31998000000001,0 50.12417,40.35833000000001,0 50.2625,40.32916,0 50.39325,40.22689,0 50.63309,40.29893,0 50.62228,40.32742,0 50.36295000000001,40.47673000000001,0 50.23536,40.4908,0 50.125,40.51138,0 50.05525999999999,40.58444,0 49.80499,40.56471,0 49.52805,40.66277,0 49.50805,40.69305,0 49.53249000000001,40.78749,0 49.39804000000001,40.84971,0 49.22221,41.01833,0 49.15498000000001,41.15165,0 49.14055,41.26833000000001,0 49.03998,41.41415,0 48.89944,41.52583,0 48.67777000000001,41.78276,0 48.58395000000001,41.83577,0 48.54472,41.88028,0 48.38665,41.92638,0 48.3036,42.09748,0 48.11776,42.32638,0 47.94498,42.46249,0 47.85942,42.59389,0 47.73832,42.63388,0 47.69694000000001,42.86638,0 47.46221,43.01915,0 47.48333000000002,43.20777,0 47.58833000000001,43.29695,0 47.57249000000001,43.33054,0 47.48832,43.29361000000001,0 47.48721,43.45638,0 47.58498,43.72221,0 47.69832000000001,43.86887000000001,0 47.80731,43.98692,0 47.5322,44.50008,0 47.47952,44.4864,0 47.20554,44.21859,0 47.1586,44.20054,0 47.09248,44.24027,0 47.01222,44.36083,0 46.76027,44.39192,0 46.7,44.44276,0 46.67971,44.52304,0 46.72444,44.54472,0 46.71249,44.64777,0 46.95943,44.76389,0 47.00944,44.82111,0 46.99165,44.92192000000001,0 47.07555,44.78277,0 47.11666,44.86221,0 47.09722000000001,44.94971,0 47.14861,44.92694,0 47.19915000000001,44.96859,0 47.29193,45.18082,0 47.3761,45.18193000000001,0 47.38554000000001,45.29527,0 47.46277000000001,45.38081,0 47.37803,45.41137000000001,0 47.48611,45.41860000000001,0 47.50166,45.47998,0 47.54332,45.50527000000001,0 47.48619,45.57145,0 47.53247000000001,45.53033,0 47.81656,45.43218000000001,0 47.99829,45.51992000000001,0 48.015,45.69538,0 48.0714,45.71836,0 48.20509,45.7121,0 48.36385000000001,45.64942000000001,0 48.74612,45.69747000000001,0 48.72941,45.804,0 48.79208,45.82907,0 48.87982,45.94397000000001,0 48.99471,46.00246,0 49.05737,45.98365,0 49.18897,46.05259,0 49.3143,46.2573,0 49.22253,46.34631,0 48.91813000000001,46.48922,0 48.83388,46.48499000000001,0 48.74027000000001,46.55832,0 48.56054,46.56499,0 48.58400000000001,46.59843000000002,0 48.55415,46.64389,0 48.4762,46.66768,0 48.52166,46.74305,0 48.60333,46.77332000000001,0 48.71054,46.75972,0 48.76527,46.69082,0 48.94137,46.70416,0 49.02720999999999,46.77610000000001,0 48.7211,47.09833,0 48.54721000000001,47.41721,0 48.44388000000001,47.43693,0 48.20442000000001,47.70499000000001,0 48.06471000000001,47.77971,0 47.69137,47.76583,0 47.43581,47.84361000000001,0 47.38333,47.68888000000001,0 47.13665000000001,47.82249000000001,0 47.18277,47.87417,0 47.06248000000001,47.99609,0 47.21499000000001,48.07083,0 47.12221,48.10222,0 47.13832000000001,48.16999,0 47.09638000000001,48.21000000000001,0 47.13332000000001,48.24249,0 47.12277,48.27166,0 46.49916000000002,48.4175,0 46.77888000000001,48.93665,0 46.95444,49.01305,0 47.06275999999999,49.14277,0 47.02610000000001,49.23610999999999,0 46.79582,49.33972,0 46.92944,49.86361000000001,0 47.18833,49.94415000000001,0 47.30249,50.03194000000001,0 47.35666,50.09027,0 47.28054,50.18221,0 47.34276,50.24193,0 47.31666,50.29445000000001,0 47.4161,50.33193,0 47.4464,50.37695,0 47.59972,50.46083000000001,0 47.76082000000001,50.37527,0 48.11054000000001,50.0986,0 48.16554000000001,49.9661,0 48.24888000000001,49.90888000000001,0 48.25166000000001,49.86971,0 48.33221,49.87887000000001,0 48.45082,49.81693000000001,0 48.79695,49.94194,0 48.91693000000001,50.03417,0 48.87972000000001,50.10167000000001,0 48.79111,50.1236,0 48.81110000000001,50.19415000000001,0 48.72276000000001,50.35305000000002,0 48.69749,50.59194000000001,0 48.59555,50.62249000000001,0 48.59361,50.64833000000001,0 48.83027,50.59665,0 49.03083,50.68804999999999,0 49.15833,50.79278000000002,0 49.42583000000001,50.85139000000001,0 49.44083,50.90583000000002,0 49.36443,50.97971,0 49.46194,51.12416,0 49.80416,51.11027,0 49.96804,51.2261,0 50.36332,51.31694000000001,0 50.35443,51.37805000000001,0 50.39721999999999,51.39972,0 50.38472,51.42527000000001,0 50.54888,51.46861,0 50.54361,51.56722000000001,0 50.59471,51.58972,0 50.60277,51.63999,0 50.71749,51.62249,0 50.68277000000001,51.57306000000001,0 50.81054000000001,51.59166,0 50.81361,51.64805,0 50.75861,51.71970000000001,0 50.7733,51.76918,0 51.41249000000001,52.08971,0 51.45750000000001,52.22776,0 51.40805,52.25416,0 51.40694,52.34917,0 51.47166,52.36583000000001,0 51.46388,52.41360000000001,0 51.56361,52.43582,0 51.47083,52.52722,0 51.47360000000001,52.57972,0 51.55054,52.57417,0 51.50667000000001,52.63276000000001,0 51.53416,52.67416,0 51.73360000000001,52.6675,0 51.77944,52.79583000000001,0 51.71555,52.82277,0 51.72805,52.87194,0 51.82417000000001,52.87388,0 51.86694000000001,52.92943,0 52.05389,52.99055,0 52.06249,53.07833,0 52.15972,53.09665,0 52.05471,53.14249000000001,0 52.13417,53.17499,0 52.1236,53.22388,0 52.16388000000001,53.26167,0 52.13916,53.29166,0 52.19665000000001,53.37944,0 52.13803999999999,53.40305000000002,0 52.13499,53.44915,0 52.07527,53.50639,0 52.09167,53.55027,0 52.1836,53.55694,0 52.25804,53.65526,0 52.41415,53.93027000000001,0 52.37054,53.9761,0 52.4875,54.05777000000001,0 52.39804000000001,54.1286,0 52.27194,54.11388000000001,0 52.25833,54.14999,0 52.34388000000001,54.17194,0 52.34610000000001,54.22665000000001,0 52.19444,54.29721000000001,0 52.27943,54.32833000000002,0 52.51166,54.32194,0 52.53745,54.37707,0 52.62194,54.3411,0 52.68775999999999,54.36111,0 52.92304,54.31388,0 52.93249,54.28804000000001,0 52.88166,54.27777,0 52.92916,54.26805,0 53.01361,54.26805,0 53.02944000000001,54.28944,0 53.0014,54.31249,0 53.04749,54.34167000000002,0 53.11582,54.24944000000001,0 52.93694,54.2136,0 53.03361000000001,54.12749000000001,0 53.09137,54.12054,0 53.06054,54.06999,0 53.19444,54.09832,0 53.27221999999999,54.06999,0 53.285,54.02750000000001,0 53.37194,53.96749,0 53.41082,54.02694,0 53.47020000000001,54.0452,0 53.41305,54.22222,0 53.43444,54.26915,0 53.36138,54.32888,0 53.34444,54.38694000000001,0 53.42194,54.48666000000001,0 53.41055,54.55750000000001,0 53.6086,54.71138,0 53.57888,54.74888,0 53.60777,54.81249,0 53.57695,54.81499,0 53.64249000000002,54.90139,0 53.51412,54.88120000000001,0 53.40639000000001,54.99722,0 53.26694000000001,55.01028000000001,0 53.26834,55.05331,0 53.14138000000001,55.09389,0 53.13888000000001,55.14388,0 53.33443,55.16249000000001,0 53.33805000000001,55.19499,0 53.39944000000001,55.22110000000001,0 53.58611000000001,55.20776999999999,0 53.59999,55.26027,0 53.70916,55.3336,0 53.89833000000002,55.38166,0 53.93333000000001,55.4586,0 53.99694,55.50027,0 53.99638000000001,55.54972000000002,0 54.17833,55.62276000000001,0 54.24194000000001,55.69137000000001,0 54.09528000000001,55.77592,0 54.0261,55.75666,0 54.03820000000001,55.80095000000001,0 53.97027,55.78249000000002,0 53.99722,55.85082,0 53.63842,55.91552,0 53.67193,55.95999,0 53.78472,55.96915,0 53.83795000000001,56.04377,0 54.02443,56.02944000000001,0 54.10722,56.14749,0 54.35889,56.26222000000001,0 54.37526,56.30498999999999,0 54.31527,56.34138,0 54.35241,56.35857,0 54.32472000000001,56.46249,0 54.23611,56.43581,0 54.18472000000001,56.53722,0 54.12194,56.53361000000001,0 54.09083,56.58250000000001,0 54.02664999999999,56.58416,0 54.10083,56.66276000000001,0 54.0286,56.69276,0 53.96388,56.63110000000001,0 53.87,56.6075,0 53.83554,56.63556,0 53.88081,56.67222,0 53.80637999999999,56.77332,0 53.82832,56.81193,0 53.88943,56.80444,0 53.88999,56.76445,0 54.01056,56.74916,0 54.09177,56.79174,0 54.09805,56.96249,0 54.40269,57.03656000000002,0 54.37444000000001,57.07389000000001,0 54.27555,57.10082000000001,0 54.33472,57.15554000000001,0 54.32000000000001,57.28666000000001,0 54.11639,57.31027000000001,0 54.14443,57.38693,0 54.27166,57.42222,0 54.26472,57.46443000000001,0 54.17860000000001,57.465,0 54.16582,57.51222,0 54.20361,57.53083,0 54.16999000000001,57.55972000000001,0 54.11888,57.55944000000001,0 54.10139,57.51805,0 53.98749000000001,57.57111,0 54.12111,57.61193,0 54.06916,57.66666000000001,0 54.15665000000001,57.70249,0 54.13639000000001,57.83222,0 54.08749,57.91331,0 54.10722,57.97276,0 53.94276,58.09527000000001,0 53.89415,58.18804,0 53.80972,58.22887,0 53.88916,58.32222,0 53.79027,58.39249000000001,0 53.78463,58.43996000000001,0 53.69610000000001,58.60333000000001,0 53.75721999999999,58.6525,0 53.74027,58.67972,0 53.77055,58.71638000000002,0 53.92833,58.78166000000001,0 53.80721000000001,58.82777000000001,0 53.71915,58.93748999999999,0 53.76500000000001,59.02277000000002,0 53.84470000000001,59.03054,0 53.83500000000001,59.05832000000001,0 53.88416000000001,59.09054,0 53.75417,59.14111000000001,0 53.68916000000001,59.12332000000001,0 53.7236,59.09194000000002,0 53.38555,59.15554,0 53.24415000000001,59.27583,0 53.2047,59.34944,0 53.3661,59.51832,0 53.42054,59.68971,0 53.60278000000001,59.70055000000001,0 53.71194,60.00332,0 53.59887000000001,60.15666,0 53.41137000000001,60.22443000000001,0 53.38611,60.16555,0 53.01083,60.15915,0 52.42722000000001,60.20304,0 52.29815,60.24171,0 52.34777,60.37330999999999,0 52.34304000000001,60.44638,0 52.12499,60.47027000000001,0 52.15471000000001,60.54555000000001,0 51.77333,60.59693999999999,0 51.88777,60.87666,0 52.34333,60.835,0 52.42917000000001,60.98137,0 52.79499,60.94433,0 52.87666,61.09388000000001,0 53.38499000000001,61.03389,0 53.33944000000001,60.89388,0 53.57389,60.86500000000001,0 53.80915000000001,60.84777,0 53.86555,60.98749,0 54.97971,60.86388,0 55.06107,61.01941,0 55.19747,61.0041,0 55.27921000000001,61.12959000000002,0 55.72323,61.08145,0 55.82858,61.25057,0 56.29028000000001,61.20304000000001,0 56.34805,61.29388,0 56.31638000000001,61.32527,0 56.3886,61.43943000000001,0 56.54527,61.42888,0 56.60083,61.50249,0 56.67556,61.52777,0 57.09666,61.48525999999999,0 57.18,61.51194,0 58.80972,61.50556,0 58.94054000000001,61.55972,0 59.01082000000001,61.54472,0 59.14333000000001,61.63305,0 59.34887000000001,61.68221000000001,0 59.39694999999999,61.76110000000001,0 59.33915,61.80222,0 59.34137000000001,61.85695000000001,0 59.48555,61.98805,0 59.40444,62.14222000000001,0 59.51527,62.31721000000002,0 59.60028,62.36694,0 59.58472,62.41499,0 59.60889,62.45333000000001,0 59.65055,62.51721,0 59.50972000000001,62.54722,0 59.39138,62.72971,0 59.46083000000002,62.77917000000002,0 59.48721000000001,62.89778,0 59.42471000000001,62.94415,0 59.27665,62.96666,0 59.22221,63.07388,0 59.30277,63.12638,0 59.25889,63.2011,0 59.3386,63.28999000000001,0 59.28249,63.33278,0 59.35222,63.36306,0 59.31722000000001,63.39888000000001,0 59.47776000000001,63.56805,0 59.51389,63.77999000000001,0 59.57249000000002,63.83666,0 59.56583000000001,63.91915,0 59.77055,63.99942999999999,0 59.85972,64.1375,0 59.73388000000001,64.15331999999999,0 59.58805,64.23248,0 59.57943,64.27943000000001,0 59.6361,64.33749,0 59.60470999999999,64.46026999999999,0 59.47970999999999,64.48193000000001,0 59.61361,64.62276,0 59.70249,64.65166000000001,0 59.63805000000001,64.72387000000001,0 59.65416,64.78333000000001,0 60.15804000000001,65.06721,0 60.42749,65.05193,0 60.42972000000001,65.00082,0 60.59638,64.94193,0 60.63138,64.88361,0 60.81194,65.04859999999999,0 60.96277000000001,65.04165999999999,0 61.11166,65.17221000000001,0 61.23944,65.18552,0 61.3336,65.28888000000001,0 61.24917,65.31887999999999,0 61.41555000000002,65.39639,0 61.58222000000001,65.55165,0 61.68694,65.57388,0 61.87027000000001,65.70222,0 62.11306,65.72083000000001,0 62.68193999999999,65.86304,0 62.69388,65.86331,0 62.73193000000001,65.85776,0 62.86111,65.87804,0 62.79777000000001,65.93109,0 62.88833000000002,65.97998,0 62.81750000000001,66.01194,0 62.86166000000001,66.07720999999999,0 62.95917000000002,66.1097,0 63.04694000000001,66.2247,0 63.15388,66.26749,0 63.29056,66.23276,0 63.30416000000001,66.2747,0 63.22332000000001,66.32638,0 63.40860000000001,66.45277,0 63.40915,66.48109,0 63.53722000000001,66.46026999999999,0 63.65888000000001,66.52582,0 63.81554000000001,66.54721000000001,0 63.98277,66.65358999999999,0 64.31247999999999,66.66443,0 64.41277,66.72387000000001,0 64.53999,66.7272,0 64.5761,66.79915,0 65.10138000000001,66.89055000000001,0 65.10554000000001,67.00805000000001,0 65.07055,67.03942000000001,0 65.22136999999999,67.07693,0 65.18248,67.15082,0 65.65888,67.30609,0 65.71887,67.34054999999999,0 65.67249,67.36609,0 65.69053,67.38665,0 65.96138000000001,67.39525999999999,0 66.10664,67.47859,0 66.0397,67.57693,0 65.79165999999999,67.56971,0 65.86832,67.60664,0 65.81805,67.62608,0 65.84054999999999,67.65192999999999,0 65.99275,67.67915000000002,0 66.10526,67.64554,0 66.21053000000001,67.69609,0 66.18388,67.74832000000002,0 66.01805,67.79305000000001,0 66.05443,67.88083,0 66.09248,67.89027,0 66.08611000000001,67.93442,0 65.91081,67.96332,0 65.78055000000001,67.9233,0 65.43471,67.91804999999999,0 65.28498999999999,68.01194,0 65.33333,68.08638000000001,0 65.26775000000001,68.21998000000001,0 65.48054999999999,68.43053999999999,0 65.60609,68.48499,0 65.61443,68.52554000000001,0 65.66165,68.54694000000001,0 65.52361000000001,68.58414999999999,0 65.37831,68.68053999999999,0 65.42941999999999,68.7,0 65.40498,68.73276,0 65.32639000000002,68.72499000000001,0 65.34860000000001,68.79192000000001,0 65.20220999999999,68.82331000000001,0 65.08055,68.80888,0 64.80415000000001,68.89194000000001,0 64.75499000000001,68.88527000000001,0 64.74582,68.85526,0 64.54998999999999,68.87053000000002,0 64.52139,68.90776,0 64.58389,68.96082,0 64.51749,68.98248,0 64.51999000000001,69.01776,0 64.83257,69.15213,0 64.94275,69.2047,0 64.91499,69.25471,0 65.04832,69.27332,0 65.38193,69.18471,0 65.79581,69.13697000000001,0 66.2572,69.07253,0 66.52336,68.9671,0 66.55627,68.91167,0 67.12005000000001,68.84113000000001,0 67.39063,68.80437000000001,0 67.38152,68.76640999999999,0 67.27820000000001,68.74968,0 67.32339,68.69279,0 67.77222,68.51776,0 67.80582,68.50082,0 67.70610000000001,68.51054000000001,0 67.78306000000001,68.47749,0 68.15942,68.41137000000001,0 68.26054000000001,68.33472,0 68.20081999999999,68.29471,0 68.18526,68.23609999999999,0 68.27025999999999,68.23915,0 68.28333000000001,68.18498,0 68.43637,68.22499000000001,0 68.56415,68.31052,0 68.64999,68.31219,0 68.57165999999999,68.33778,0 68.56303,68.36942000000001,0 68.89776999999999,68.60748,0 69.00888000000001,68.79998999999999,0 69.09526,68.83333,0 69.10831,68.87248,0 68.94386,68.89803000000001,0 69.22026,68.95722000000001,0 69.03248000000001,68.94193,0 69.02164000000001,68.96416000000001,0 68.78498,68.91081,0 68.47331,68.97248999999999,0 68.04749,69.28137,0 68.02139,69.3436,0 68.10942,69.38889,0 68.10165000000001,69.54665,0 68.00721,69.51860000000001,0 68.03804,69.48888,0 67.96748,69.48415,0 67.78610000000001,69.51666,0 67.61304,69.59166999999999,0 67.38528000000001,69.61859,0 67.37054999999999,69.52164999999999,0 67.25494999999999,69.40298,0 67.16154,69.35935000000001,0 66.95407,69.40528000000001,0 66.93801000000001,69.52226,0 66.79192999999999,69.58028,0 66.76721000000001,69.75722,0 66.87331,70.00109999999999,0 66.99387,70.04886999999999,0 67.07941,70.12733,0 67.08862000000001,70.21623,0 67.19971,70.39027,0 67.26666,70.41110000000001,0 67.25194999999999,70.44832,0 67.29221,70.46998000000001,0 67.20889,70.52666000000001,0 67.33971,70.72499000000001,0 67.33249000000001,70.76888,0 67.22887,70.82555000000001,0 67.10720000000001,70.83526999999999,0 66.68886999999999,70.76054000000001,0 66.61275999999999,70.86773,0 66.54228000000001,70.92879000000001,0 66.62154,71.04885,0 66.78388,71.14221000000001,0 66.83665000000001,71.20972,0 66.91553999999999,71.29276,0 67.08581000000001,71.30387,0 67.53675,71.40988,0 67.53675,73.95477,0 72.53674,73.94411,0 77.53673000000001,73.93346,0 82.27669,73.92335,0 81.08696000000001,73.75897999999999,0 79.49023,73.66505000000001,0 78.53532,73.30501,0 78.20658,73.02322,0 78.17527,72.89798,0 78.4727,72.64751,0 78.50015000000001,72.37733,0 78.45471000000001,72.36443,0 78.44637,72.35971000000001,0 78.65913999999999,72.26111,0 78.67804,72.22165,0 78.59721999999999,72.20972,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +68.67328999999999,82.13631,0 67.53675,81.66452,0 67.53675,82.89028,0 67.53675,84.90001,0 67.53675,89.90000000000001,0 68.53611000000001,89.90000000000001,0 69.53547,89.90000000000001,0 70.53484,89.90000000000001,0 71.53419,89.90000000000001,0 72.53355000000001,89.90000000000001,0 73.53292999999999,89.90000000000001,0 74.53227,89.90000000000001,0 75.53164,89.90000000000001,0 76.53100000000001,89.90000000000001,0 77.53036,89.90000000000001,0 78.52972,89.90000000000001,0 79.52907999999999,89.90000000000001,0 80.52843,89.90000000000001,0 81.5278,89.90000000000001,0 82.52717,89.90000000000001,0 82.52717,87.88696,0 77.9092,85.97008,0 73.29124,84.05318,0 68.67328999999999,82.13631,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +05:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(5.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0550</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +69.14654,7.824959000000001,0 67.51169000000002,7.824905,0 67.51169000000002,12.15223,0 72.51169,12.15223,0 74.14655000000001,12.15223,0 74.14655000000001,7.825121,0 69.14654,7.824959000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +94.5928,5.880672000000001,0 92.9025,5.880672000000001,0 90.34064000000001,5.884479,0 90.33504000000001,10.88448,0 90.33015,15.23029,0 94.58984000000001,15.23029,0 94.59142,10.23029,0 94.5928,5.880672000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +79.43803000000001,35.96054000000001,0 79.46193,35.85804,0 79.58109000000002,35.86971,0 79.65886999999999,35.81916,0 79.63443000000001,35.75916,0 79.69359,35.65083,0 79.81303,35.62943,0 79.85887,35.5661,0 79.91914,35.61749,0 79.96416000000001,35.59583,0 80.05749,35.40777,0 80.14776999999999,35.45722,0 80.20663999999999,35.56193,0 80.2741,35.52753,0 80.29776,35.50333,0 80.38388000000001,35.51111,0 80.38666000000001,35.42528,0 80.28943,35.33999,0 80.29803,35.2075,0 80.19026,35.08764,0 80.20166999999999,34.89388,0 80.10248,34.7747,0 80.07138000000001,34.66945,0 79.8875,34.66276000000001,0 79.84387,34.57444,0 79.7247,34.52999,0 79.7636,34.45499,0 79.72275999999999,34.42054,0 79.53915000000002,34.42999,0 79.57805000000001,34.2086,0 79.51166000000001,34.19027,0 79.5247,34.11805,0 79.42915000000001,34.02166,0 78.97527,34.01721,0 78.90886999999999,33.97028,0 78.94941,33.83221,0 79.01944,33.75444000000001,0 79.04082,33.63943,0 78.91582,33.62944,0 78.93651,33.408,0 78.99638000000002,33.31499,0 79.09137,33.27499,0 79.10359,33.21971,0 79.18387,33.22582,0 79.40637,33.16166,0 79.35609,32.965,0 79.45027,32.88221,0 79.47083000000001,32.79388,0 79.53027,32.75416,0 79.51443,32.67222,0 79.45722000000001,32.60249,0 79.31386999999999,32.49665,0 79.25317000000001,32.516,0 79.13164999999999,32.47499,0 79.09915,32.37638,0 78.97110000000001,32.35082,0 78.78027,32.47887,0 78.76055,32.63556,0 78.70443,32.6311,0 78.6386,32.57778,0 78.42054,32.5661,0 78.39722,32.53111,0 78.4772,32.42471,0 78.44971,32.38694,0 78.47832,32.34999,0 78.47593999999999,32.24305,0 78.54471000000001,32.20943,0 78.68053000000001,32.03527,0 78.7711,31.98111,0 78.74507,31.91036,0 78.69247,31.77417,0 78.74249,31.68499,0 78.79053999999999,31.67472,0 78.84387,31.60417,0 78.71278,31.49833,0 78.79276000000002,31.44333,0 78.77054,31.305,0 78.87766000000001,31.29163,0 78.94193,31.36,0 79.00555,31.32555,0 79.01859,31.41138,0 79.09248,31.4375,0 79.21748,31.34083,0 79.24026000000001,31.24417,0 79.28665,31.22333,0 79.31499,31.13305,0 79.39324000000001,31.10106,0 79.42249,31.0275,0 79.49413,31.02416,0 79.59943,30.93556,0 79.74887,30.98972,0 79.87916,30.95916,0 79.94981,30.8849,0 80.04192000000001,30.84694,0 80.09415,30.78666000000001,0 80.17831,30.79945,0 80.25027,30.73999,0 80.18749,30.67583000000001,0 80.22776,30.62193,0 80.20699999999999,30.57552,0 80.32193,30.56472,0 80.53888000000001,30.46443,0 80.60693000000001,30.47472,0 80.75194999999999,30.395,0 80.80386,30.32666,0 80.99888,30.26194,0 81.02536000000001,30.20436,0 80.93637,30.18138,0 80.89333000000001,30.21111,0 80.8586,30.17472,0 80.87276,30.13888,0 80.74609,30.02138,0 80.59887000000001,29.95527,0 80.53970000000001,29.84611,0 80.37691,29.7486,0 80.40694000000001,29.59638,0 80.23637000000001,29.42527,0 80.27582,29.3225,0 80.30693000000001,29.31055,0 80.28777,29.20556,0 80.23997,29.21472,0 80.25694,29.14472,0 80.14664999999999,29.10833,0 80.1297,28.99305,0 80.05998,28.93166,0 80.05582,28.83611,0 80.24609,28.75082,0 80.35776,28.62944,0 80.44026,28.62888,0 80.50861,28.56693,0 80.50555,28.65833,0 80.57720999999999,28.68556,0 80.58499000000001,28.6536,0 80.66249000000001,28.63277,0 80.79832,28.52389,0 80.89249,28.50638,0 80.93858,28.44111,0 81.00694,28.44305,0 81.01888,28.40416000000001,0 81.19359,28.36667,0 81.31218,28.19333,0 81.31999000000002,28.13,0 81.42192,28.16638,0 81.48027,28.08,0 81.88026000000001,27.85721,0 81.97776000000002,27.91722,0 82.0664,27.91416,0 82.43471,27.67556,0 82.7011,27.71111000000001,0 82.76860000000001,27.50166,0 83.17638,27.44888,0 83.29137,27.33777,0 83.37665,27.37527,0 83.39610000000002,27.47861,0 83.60776,27.46916,0 83.85832000000001,27.35221,0 83.87553,27.38083,0 83.83526999999999,27.43805,0 84.04026,27.44472,0 84.09887000000001,27.51611,0 84.14333000000001,27.52417,0 84.29776,27.38832,0 84.60997999999999,27.33556,0 84.68414,27.21555,0 84.64749000000001,27.04527,0 84.75027,27.0075,0 84.84360000000001,27.01527,0 84.95277,26.95804,0 84.97331,26.90304,0 85.04109,26.88666,0 85.04082,26.84777,0 85.18388,26.86666,0 85.21165000000001,26.76694,0 85.33443,26.73582,0 85.41249000000001,26.79472000000001,0 85.45305000000001,26.78694000000001,0 85.63026000000001,26.8675,0 85.72609,26.80167000000001,0 85.73054999999999,26.66722,0 85.85027,26.57222,0 86.02943000000001,26.66306,0 86.18387,26.60806,0 86.20664000000001,26.58556,0 86.33167,26.61277,0 86.33249000000001,26.58499,0 86.53498999999999,26.52806,0 86.73165,26.41999,0 86.88598,26.46866,0 87.06137,26.58833,0 87.09415,26.45638,0 87.1375,26.40972,0 87.24525,26.41222,0 87.32805999999999,26.34777,0 87.37082,26.40333,0 87.44748,26.43250000000001,0 87.59999000000001,26.37972,0 87.69748,26.42944,0 87.76639,26.40861,0 87.78554,26.45638,0 87.84305000000001,26.43888000000002,0 87.89055999999999,26.47333,0 87.94941,26.40055,0 88.02638,26.36916,0 88.09583000000001,26.43888000000002,0 88.10193,26.53028000000001,0 88.19497,26.75416,0 88.11859,26.98444,0 87.99303999999999,27.11444,0 88.05971,27.32777000000002,0 88.03888000000001,27.37611,0 88.06667000000002,27.43693,0 88.05692999999999,27.53334,0 88.11913,27.60833,0 88.18887000000001,27.78777,0 88.19386,27.8475,0 88.14279999999999,27.86605,0 88.12053,27.90471000000001,0 88.14554,27.95527,0 88.53415,28.02917,0 88.62276,28.11805,0 88.83167,28.01334,0 88.88081,27.83667,0 88.83443,27.66111,0 88.76331999999999,27.54333,0 88.78333000000001,27.43805,0 88.91692000000001,27.32083,0 88.90138000000002,27.28222,0 88.79803,27.24638,0 88.75193,27.14861,0 88.86360999999999,27.09194000000002,0 88.87636999999999,26.95055,0 88.93332000000001,26.98499,0 88.95193000000002,26.92667000000001,0 89.03055000000001,26.92749,0 89.12608,26.82361,0 89.22083000000001,26.81472,0 89.36081,26.86,0 89.46833,26.79944,0 89.53360000000001,26.81221,0 89.63611,26.77111,0 89.61832,26.72694,0 89.64305000000002,26.71527,0 89.68552000000001,26.7336,0 89.84887000000001,26.70167,0 89.85942,26.73666,0 90.1236,26.74805,0 90.17747,26.77306000000002,0 90.21693000000001,26.85082,0 90.38527000000001,26.90333,0 90.55914,26.80722,0 90.6947,26.77222,0 91.03194000000002,26.78278,0 91.09109000000001,26.81639,0 91.31441000000001,26.77806,0 91.3797,26.8075,0 91.4247,26.87556,0 91.49721000000001,26.8725,0 91.54221,26.80445,0 91.71249,26.8,0 91.84360000000001,26.86416,0 91.90498,26.80556,0 91.91526,26.89389,0 91.99721,26.85194,0 92.08150999999999,26.86905,0 92.11193000000002,26.94416,0 92.00999000000002,27.10472,0 92.01776,27.1725,0 92.11443000000001,27.28583,0 92.08916000000001,27.34944,0 91.99887,27.47805,0 91.76833000000002,27.41611,0 91.76555000000002,27.45694,0 91.67359,27.48861,0 91.62082000000001,27.54861,0 91.59498000000001,27.64554,0 91.65277000000002,27.71167,0 91.65776,27.76472,0 92.01443,27.73471,0 92.11832000000003,27.8125,0 92.25388,27.81582,0 92.26804,27.87472,0 92.33110000000001,27.79472,0 92.35831000000003,27.84443,0 92.38666000000002,27.80472,0 92.4147,27.86028,0 92.45248000000001,27.82639,0 92.69942000000002,27.95555,0 92.72838000000002,28.03598,0 92.67525000000001,28.08071,0 92.75290000000001,28.16892,0 92.83004000000001,28.21276,0 92.84125,28.1888,0 92.88734000000001,28.19872000000001,0 92.99153,28.26286,0 93.01044000000003,28.24225,0 93.04571,28.31026,0 93.22205000000001,28.3193,0 93.23817000000001,28.38745,0 93.19270000000002,28.43213,0 93.28152000000002,28.49296,0 93.35194000000001,28.61877000000001,0 93.49408,28.65192,0 93.50206000000001,28.67916,0 93.64729,28.64746,0 93.85851000000001,28.69038,0 93.98059000000001,28.64518,0 93.96173,28.6692,0 94.01243,28.74736,0 94.0211,28.83603,0 94.15246000000002,28.9307,0 94.23455,29.07348,0 94.42776000000002,29.21551,0 94.5812,29.23153,0 94.64751,29.33347,0 94.69736,29.33652,0 94.79227,29.2608,0 94.83335,29.16837,0 95.00915999999999,29.12388,0 94.98471000000001,29.17361,0 95.38777,29.03527,0 95.46053000000001,29.05167,0 95.49609000000001,29.14083000000001,0 95.54665,29.13861,0 95.53442,29.21167,0 95.59108999999999,29.255,0 96.08389,29.46777000000001,0 96.23526000000001,29.25445,0 96.28943,29.24139,0 96.36249000000001,29.29055,0 96.39525999999999,29.25527,0 96.34860000000001,29.18,0 96.25665000000001,29.16444,0 96.11913,29.07916000000001,0 96.18222000000002,28.99527,0 96.14861000000001,28.94333,0 96.16914,28.90361,0 96.34387,29.0325,0 96.51194000000001,29.07,0 96.47581000000001,28.97472,0 96.51027000000001,28.96444,0 96.61582000000001,28.79027,0 96.60609000000001,28.73944,0 96.47083000000001,28.68833,0 96.47832,28.63861,0 96.34526,28.55695,0 96.36638000000001,28.48082000000001,0 96.26471000000001,28.41527,0 96.36165000000001,28.39278,0 96.39694000000002,28.35333,0 96.40192,28.35111,0 96.46053000000001,28.42194,0 96.65387,28.46749,0 96.78415,28.37306,0 96.88970999999999,28.37861,0 96.98471000000001,28.3325,0 97.13499000000002,28.38138,0 97.34887000000002,28.22278,0 97.31608000000001,28.08916,0 97.39413999999999,28.01861,0 97.36775,27.89666,0 97.31721,27.87833,0 97.29916,27.91667,0 97.24942,27.9075,0 97.08389,27.7486,0 97.04665,27.74972,0 96.88889,27.6175,0 96.91804999999999,27.50527,0 96.89999000000002,27.46861000000001,0 97.12636999999999,27.19083,0 97.15888,27.13861,0 97.14444000000002,27.09249,0 97.05974000000001,27.093,0 96.88831999999999,27.17805,0 96.86137000000001,27.21028,0 96.88416000000001,27.25472,0 96.72915999999999,27.36555,0 96.66025999999999,27.33916,0 96.60999,27.36694,0 96.52415000000002,27.29416,0 96.19414999999999,27.27084,0 96.03581,27.19249,0 95.93082,27.04611,0 95.80386,27.00111,0 95.71775000000001,26.89138,0 95.65804000000001,26.89528,0 95.61081,26.82139,0 95.51944,26.82306,0 95.44108,26.70916,0 95.2711,26.64472,0 95.24638,26.67972,0 95.15248000000001,26.62333,0 95.14777000000001,26.56583,0 95.06638,26.47972,0 95.09526,26.41,0 95.06526,26.32556,0 95.08389000000003,26.22277,0 95.12082,26.20166,0 95.11331,26.10916,0 95.17804,26.05889,0 95.08665000000001,25.95556,0 95.02222,25.92861,0 95.03387000000002,25.74222,0 94.99193,25.72916,0 94.88527000000001,25.57222000000001,0 94.66165000000001,25.42804,0 94.62553000000001,25.39777,0 94.57639000000002,25.21444,0 94.59943,25.17833,0 94.72859,25.13638,0 94.73442,25.02472,0 94.59777,24.72528000000001,0 94.53859000000001,24.70888,0 94.51470999999999,24.61945,0 94.38915,24.48,0 94.14444,23.84778,0 93.92666,23.95583,0 93.81914999999999,23.93527,0 93.75332,24.0075,0 93.62136,24.01611000000001,0 93.59636999999999,23.97222,0 93.49359,23.96277,0 93.38293,24.08935,0 93.34207000000001,24.0818,0 93.3279,24.03597,0 93.32805000000001,23.98166,0 93.38443,23.92138,0 93.40138,23.73582,0 93.43346,23.67889,0 93.40331999999999,23.37806,0 93.36609,23.34945,0 93.38666000000001,23.14027000000002,0 93.29359000000001,23.00889000000002,0 93.22915999999999,23.01333,0 93.20138000000002,23.05889,0 93.13805000000004,23.04805,0 93.16137000000001,22.91945,0 93.09822000000001,22.79037,0 93.09722000000001,22.66528,0 93.13638,22.59444,0 93.11859,22.53333,0 93.18055,22.44055,0 93.19664000000002,22.25638,0 93.14249,22.23694,0 93.15387,22.18666,0 93.04414000000001,22.20194,0 93.05276000000001,22.11889,0 93.00972000000003,22.1075,0 92.99638000000003,21.99278,0 92.94304000000001,22.02833,0 92.89639,21.95472,0 92.86554,22.04888,0 92.71192000000002,22.15972,0 92.67970000000001,22.02694,0 92.60081,21.98222000000001,0 92.56218,22.13333,0 92.60609,22.16277,0 92.51639000000002,22.71583,0 92.47054000000001,22.75638,0 92.44386000000002,22.88944,0 92.37469,22.94193,0 92.34860000000001,23.22361,0 92.38697999999999,23.27993,0 92.37358,23.35925,0 92.27831999999999,23.605,0 92.28888000000001,23.69611,0 92.25221000000002,23.7225,0 92.21165000000001,23.65222,0 92.17486000000001,23.73682,0 92.0372,23.64528,0 91.95,23.73222000000001,0 91.96609000000001,23.50166,0 91.93304000000001,23.44444,0 91.84498000000001,23.41027,0 91.78360000000001,23.3125,0 91.76805000000002,23.25,0 91.82055,23.09777,0 91.72499000000001,22.99472,0 91.64722000000002,22.98638,0 91.61360000000001,22.94305,0 91.56359999999999,22.9836,0 91.49332000000001,23.18666,0 91.41665999999999,23.26611,0 91.37746,23.20693,0 91.40221,23.06945,0 91.33554,23.11555,0 91.28859,23.32832,0 91.31469000000001,23.36416,0 91.28221000000002,23.37332,0 91.15942,23.64055000000001,0 91.20028000000004,23.67805,0 91.15915,23.70082,0 91.15860000000002,23.73666,0 91.21638,23.75611,0 91.25054000000002,23.83027,0 91.23776000000001,23.90777,0 91.29832,23.99388,0 91.36110000000001,23.99528,0 91.37329,24.10194,0 91.54633,24.08693,0 91.62746,24.10972,0 91.6572,24.22416,0 91.70385000000002,24.15166,0 91.75525,24.15833,0 91.75775,24.23917000000001,0 91.83192000000001,24.22611,0 91.89249,24.14972,0 91.93248,24.27555,0 91.91776,24.32667,0 91.94664000000002,24.34945,0 91.98943000000001,24.31944,0 91.97470000000001,24.37889,0 92.08638000000001,24.37472,0 92.13527000000002,24.41416,0 92.13247,24.51388,0 92.19108000000001,24.56528,0 92.25664999999999,24.77390000000001,0 92.25526000000004,24.90361,0 92.40173000000002,24.84041,0 92.49384000000001,24.87879,0 92.48305000000001,24.93,0 92.41665999999999,24.9725,0 92.40637,25.03056000000001,0 92.04080999999999,25.18777,0 91.75749000000002,25.17388,0 91.64888000000002,25.12667,0 91.59027,25.17278,0 91.48221000000001,25.13861,0 91.25777000000001,25.20778,0 90.84804,25.14861000000001,0 90.65305000000001,25.18111,0 90.43831,25.14721000000001,0 90.11053000000001,25.22472,0 89.91721000000001,25.31,0 89.84526000000001,25.2886,0 89.81554,25.36693,0 89.86443,25.64056,0 89.80721,25.82305,0 89.86545,25.93686,0 89.81671,25.95416000000001,0 89.85637,25.98111,0 89.77804999999999,26.04195,0 89.73915,26.15638,0 89.6493,26.18946,0 89.6386,26.2278,0 89.60173,26.22748,0 89.60896000000001,26.17790000000002,0 89.55986,26.16146000000001,0 89.60242,26.06897,0 89.52551,25.98821,0 89.39355000000001,26.0479,0 89.31990999999999,26.02483000000001,0 89.12967,26.15927,0 89.06721,26.3877,0 88.93877999999999,26.44582,0 88.90916,26.39271,0 88.98159000000001,26.29568,0 89.04134000000001,26.28179,0 89.02048000000001,26.23517,0 88.88330000000001,26.2852,0 88.82383,26.23343,0 88.76734,26.29859,0 88.66918,26.2609,0 88.67950999999999,26.30742,0 88.73037000000001,26.32068,0 88.69164000000001,26.34277,0 88.68082,26.42027,0 88.43304000000001,26.55139000000001,0 88.40109,26.62527,0 88.33777000000001,26.47389,0 88.35359,26.45167,0 88.37109,26.48917,0 88.48331,26.46111000000001,0 88.52554000000002,26.35972,0 88.45749000000001,26.36916,0 88.35609,26.28444,0 88.35526,26.22333,0 88.18442,26.1525,0 88.15636000000001,26.09249,0 88.18333,26.03389,0 88.09554,25.91527,0 88.11053,25.83555000000001,0 88.15194,25.77861,0 88.27998,25.80167,0 88.44693,25.66611,0 88.45054,25.59389,0 88.54693,25.50416,0 88.69414,25.46778,0 88.79971000000001,25.51055,0 88.83443,25.47916,0 88.82166000000001,25.40166,0 88.86998,25.33583,0 89.00888000000001,25.30083,0 88.93304000000001,25.16444,0 88.83778,25.20361000000001,0 88.80582,25.16556,0 88.70499,25.19527,0 88.62025,25.19806,0 88.58138,25.16945,0 88.45556000000001,25.19055,0 88.45999000000001,25.06944,0 88.34249,24.87028,0 88.26749000000001,24.88472,0 88.22135000000002,24.95433,0 88.13777,24.92138,0 88.1711,24.85166,0 88.04387,24.68972,0 88.12553,24.50861,0 88.32388,24.43443,0 88.49776,24.32083,0 88.70638,24.30277,0 88.75291,24.21413,0 88.70833,24.17138,0 88.70734,24.12171,0 88.70138,24.07917,0 88.75918000000002,24.03604,0 88.72272,23.99796,0 88.73665,23.9198,0 88.66949,23.86855,0 88.60284,23.8624,0 88.56386999999999,23.65222,0 88.74942,23.46693000000001,0 88.79331999999999,23.49417,0 88.71138000000001,23.30861000000001,0 88.72110000000002,23.25166,0 88.98609999999999,23.20833,0 88.87025,23.08777,0 88.86276000000001,22.96667,0 88.97110000000001,22.84694,0 88.93026999999999,22.74833,0 88.95860000000002,22.59138,0 88.92918,22.54767,0 88.99776000000001,22.46555,0 89.00722,22.27639,0 89.09637000000001,22.14861,0 89.063,22.11547,0 89.04387,22.13638,0 89.03665,22.09805,0 89.05749,21.92833,0 89.00861,21.93027,0 88.99970999999999,21.90277,0 88.94275,21.93666,0 88.93137,21.98638,0 88.90555000000001,21.95916,0 89.01639,21.86582,0 89.02110000000002,21.78222,0 89.08971,21.65027,0 89.07416000000001,21.61417,0 89.02748,21.62695,0 89.00027,21.60209,0 88.88085000000001,21.51552,0 88.78551,21.55832,0 88.70639,21.57051,0 88.53887,21.52715,0 88.49802,21.52056,0 88.48305000000001,21.55694,0 88.52554000000002,21.65583,0 88.50082,21.7525,0 88.53749000000002,21.81388000000001,0 88.49802,21.87027,0 88.49609,21.95027,0 88.45387,21.615,0 88.39082000000001,21.58833,0 88.38694,21.69695,0 88.34610000000001,21.68388,0 88.33333,21.71389,0 88.34415,21.62222,0 88.30859000000001,21.60305,0 88.29721000000001,21.77277,0 88.26194,21.8,0 88.30831000000001,21.57055,0 88.25225000000002,21.55052,0 88.20502000000001,21.51847,0 88.17249,21.53918,0 88.12221,21.61312,0 88.05122000000002,21.63086,0 87.94918,21.80093000000001,0 87.91101999999999,21.80093000000001,0 87.82944000000001,21.71916,0 87.68498,21.64777,0 87.48090000000002,21.61178,0 87.11693,21.51139,0 86.87553,21.27443,0 86.83443,21.18944,0 86.83526999999999,21.10611,0 86.96748,20.79527,0 86.87886,20.76777,0 86.98553,20.75278,0 86.99498,20.70666,0 86.93718,20.7,0 86.98888,20.67167,0 87.04165999999999,20.69333,0 86.75584000000001,20.49138,0 86.72083000000002,20.3725,0 86.78525999999999,20.38527,0 86.80441999999999,20.43611,0 86.80054,20.38166,0 86.69275,20.29416,0 86.74802000000001,20.3061,0 86.57444,20.21249,0 86.48888,20.10721,0 86.39360000000001,20.01388,0 86.42386999999999,19.98611,0 86.28027,19.91416,0 85.62385999999999,19.71361,0 85.23415,19.51195,0 84.86638000000001,19.22027,0 84.78610000000001,19.11443,0 84.74193,19.14889,0 84.72659,19.124,0 84.73526,19.07472,0 84.78083,19.09417,0 84.67638,18.94194,0 84.43552,18.64638,0 84.27554000000001,18.51833,0 84.24498,18.525,0 84.24165000000001,18.49611,0 84.27666000000001,18.49944,0 84.34942,18.54888,0 84.14166,18.37111,0 84.08028,18.26861,0 83.77582,18.14277,0 83.58275999999999,18.0175,0 83.47054,17.9,0 83.44332000000001,17.91582,0 83.45664000000001,17.88138,0 83.34220000000001,17.71444,0 83.29498000000001,17.68693,0 83.23693,17.71167,0 83.29694000000002,17.66555,0 83.21304000000001,17.63139,0 83.23859,17.60083,0 83.21638,17.58055,0 82.50555,17.22305,0 82.31331,17.04556,0 82.2486,16.92027,0 82.27222,16.86222,0 82.35971000000002,16.82056,0 82.36449,16.93222,0 82.37195,16.86854,0 82.34644,16.70472,0 82.31226,16.60444,0 82.28032,16.61944,0 82.30199,16.575,0 82.07449,16.44305,0 81.72726,16.31083,0 81.5856,16.33721,0 81.54504000000001,16.37305,0 81.54474999999999,16.34416,0 81.41947999999999,16.33777,0 81.25944000000001,16.27662,0 81.15782,16.05306,0 81.15837000000001,15.97527,0 81.00171,15.84445,0 81.01727,15.77555,0 80.81507000000001,15.71571,0 80.80443000000001,15.84472,0 80.67137000000001,15.90583,0 80.65776,15.88861,0 80.68552,15.88306,0 80.55415000000001,15.87694,0 80.27943000000001,15.69917,0 80.20637000000002,15.48861,0 80.09081999999999,15.30916,0 80.04886999999999,15.04889,0 80.10578000000001,14.78102,0 80.15331000000001,14.67445,0 80.18221,14.59638,0 80.14413999999999,14.56555,0 80.19859,14.57027,0 80.17415,14.34389,0 80.13721000000001,14.25639,0 80.07916,14.24472,0 80.03999,14.19528,0 80.12886,14.23667,0 80.14915000000002,14.01111,0 80.24748,13.80889,0 80.22581,13.68,0 80.34972000000001,13.33417,0 80.26277,12.94916,0 80.25888000000001,12.78222,0 80.16025999999999,12.47305,0 80.03360000000001,12.27861,0 79.94193,12.22305,0 80.0061,12.23027,0 79.86720000000001,12.03277,0 79.78386999999999,11.775,0 79.75139,11.54916,0 79.82249,11.37333,0 79.76667000000001,11.38389,0 79.67192,11.29556,0 79.76389,11.36,0 79.83110000000001,11.345,0 79.84081999999999,10.81778,0 79.85971000000001,10.29538,0 79.93916,10.12232,0 80.24818999999999,9.827252,0 80.36803000000002,9.653893999999999,0 80.80193,9.292769,0 80.73499,9.302498999999999,0 80.80582000000001,9.240273,0 80.79665,9.266660999999999,0 80.82444000000001,9.261945000000001,0 80.88388000000001,9.138609000000001,0 80.83222000000001,9.139715000000001,0 80.89249,9.111392,0 80.95750000000001,9.010556000000001,0 80.87608,9.036395000000001,0 80.93027000000001,8.979444000000001,0 80.92081000000002,8.935551,0 80.97971,8.973611999999999,0 81.23109,8.651106,0 81.20555000000002,8.608059000000001,0 81.25221000000001,8.542214,0 81.20833,8.570826,0 81.21388,8.508609,0 81.16137000000001,8.534447999999999,0 81.13054,8.500005,0 81.16775,8.514998,0 81.26555000000001,8.453609,0 81.32026999999999,8.520002,0 81.36136999999999,8.487215000000001,0 81.39137,8.367217999999999,0 81.35442000000001,8.403893,0 81.35498,8.366382,0 81.39499000000001,8.340831,0 81.44331,8.173889000000001,0 81.4397,8.101395,0 81.39916,8.187497000000001,0 81.39027,8.140003999999999,0 81.43359,8.085824000000002,0 81.46361,8.112769999999999,0 81.51555000000002,8.003052,0 81.56944000000001,7.983891000000001,0 81.59305000000001,7.838892,0 81.69465,7.757684,0 81.80025999999999,7.610552,0 81.82656,7.473186000000001,0 81.87886,7.312221000000001,0 81.89166,7.013609,0 81.78221000000001,6.624720000000001,0 81.66110000000001,6.440003000000001,0 81.32417,6.198327,0 81.21138000000001,6.160284,0 81.19609,6.181946,0 81.11887,6.112503000000001,0 80.8186,6.037776,0 80.71638,5.956946,0 80.59193000000001,5.918336,0 80.46471,5.933340000000001,0 80.44026,5.96555,0 80.20081999999999,6.026112000000001,0 80.12581,6.093891,0 80.02666000000001,6.268337000000001,0 79.98648,6.432363,0 79.85137,6.83639,0 79.85831,7.024167000000001,0 79.80748,7.192214,0 79.8436,7.125272000000001,0 79.86248999999999,7.155837,0 79.82639,7.198893000000001,0 79.79694000000001,7.661394,0 79.70638,8.035830000000001,0 79.69804000000001,8.217504,0 79.77399000000001,8.533196,0 79.92596,8.795825000000001,0 79.91638000000002,8.931663,0 79.88623,8.969967000000001,0 79.69427,9.086679,0 79.44081,9.148337,0 79.27222,9.252773,0 78.97110000000001,9.273607999999999,0 78.66221,9.176660999999999,0 78.65637,9.147779,0 78.39664999999999,9.089721000000001,0 78.19247,8.904166999999999,0 78.16361000000002,8.754165,0 78.20804,8.781658999999999,0 78.21053000000001,8.75555,0 78.11192,8.654715,0 78.13997999999999,8.617221000000001,0 78.13164999999999,8.486667000000001,0 78.06053,8.364167,0 77.99693000000001,8.338328000000001,0 77.79275,8.225,0 77.77554000000001,8.181108,0 77.5872,8.134722,0 77.54082,8.069715,0 77.30304,8.123327,0 76.9986,8.365273999999999,0 76.54387,8.907496999999999,0 76.54498,8.930277,0 76.60415999999999,8.90972,0 76.61414000000001,8.928890000000001,0 76.57082,8.939159,0 76.60053000000001,8.971947,0 76.65860000000001,8.964998,0 76.62943,8.981945,0 76.66748,9.003609000000001,0 76.57720999999999,8.994166999999999,0 76.55859,8.95166,0 76.55692999999999,8.990829,0 76.52694,8.938890000000001,0 76.45471000000001,9.122499,0 76.47581,9.115550000000001,0 76.48387,9.161667,0 76.46138000000001,9.149445,0 76.41914,9.249165,0 76.44247,9.143325000000001,0 76.31582,9.463608000000001,0 76.23848,9.953955000000002,0 76.23437,9.986049,0 76.19637,10.06528,0 76.17747,10.17,0 76.24109,10.22666,0 76.19497,10.26445,0 76.20663999999999,10.20306,0 76.15776,10.19527,0 76.05553999999999,10.505,0 75.92416,10.75556,0 75.93858,10.79277,0 75.90748000000001,10.80333,0 75.72553000000001,11.32972,0 75.7411,11.36306,0 75.67859,11.44583,0 75.61304,11.47166,0 75.52858999999999,11.70027,0 75.31219,11.90056,0 75.30775,11.93806,0 75.39664999999999,11.91611,0 75.39388,11.97222,0 75.30971,11.95222,0 75.27442000000001,12.00777,0 75.33167,12.01472,0 75.29636000000002,12.03777,0 75.25912,12.01889,0 75.27831999999999,11.96194,0 75.2186,12.01806,0 75.19582,12.00389,0 75.17613,12.06975,0 75.10721,12.22,0 74.85526,12.755,0 74.82332,12.83361,0 74.85999,12.82389,0 74.93998000000001,12.87111,0 74.84276,12.84556,0 74.81026000000001,12.86472,0 74.77916999999999,13.08389,0 74.68665,13.37833,0 74.68248000000001,13.46278,0 74.70139,13.41056,0 74.71249000000002,13.46806,0 74.67609,13.51028,0 74.66388000000002,13.63194,0 74.73443,13.65222,0 74.68193000000001,13.70611,0 74.65721000000002,13.65972,0 74.61913,13.8325,0 74.49442999999999,14.01639,0 74.42525999999999,14.24861,0 74.51186,14.24342,0 74.40942,14.29445,0 74.35138000000001,14.51028,0 74.39444,14.45332,0 74.42443,14.47611,0 74.35803,14.56833,0 74.3336,14.51555,0 74.30081,14.52166,0 74.28831,14.58417,0 74.33859,14.60611,0 74.26470999999999,14.62222,0 74.25082999999999,14.71889,0 74.22331,14.73777,0 74.19414000000002,14.71444,0 74.08971,14.79777,0 74.23499,14.87471,0 74.20914999999999,14.89527,0 74.12303000000001,14.84417,0 74.10136,14.86709,0 74.03361,14.91722,0 74.01999000000001,15.00055,0 73.91553999999999,15.07805,0 73.96082000000001,15.15472,0 73.88054,15.35971,0 73.78610000000001,15.39805,0 73.91249000000001,15.40111,0 73.96361,15.36361,0 73.91692999999999,15.41916,0 73.78859,15.455,0 73.86499000000002,15.50667,0 73.86388,15.53472,0 73.75832,15.49471,0 73.73081999999999,15.59777,0 73.83526000000001,15.65944,0 73.71638,15.61972,0 73.69721,15.72195,0 73.64193,15.74027,0 73.64943,15.78416,0 73.59387,15.89278,0 73.48553,15.99139,0 73.51331999999999,16.01972,0 73.44776,16.05584,0 73.48859,16.18,0 73.45499,16.16916,0 73.44776,16.10889,0 73.43804,16.21832,0 73.40998999999999,16.21749,0 73.35581999999999,16.36889,0 73.38191999999999,16.39861,0 73.30165,16.5225,0 73.31581,16.55695,0 73.36054,16.51222,0 73.36774,16.55861,0 73.30859,16.60306,0 73.37276,16.59249,0 73.37276,16.62305,0 73.31693,16.62861,0 73.29109,16.7575,0 73.30971,16.80583,0 73.26972000000001,16.85333,0 73.28805,16.98166,0 73.24832000000002,17.03555,0 73.27139,17.03056,0 73.2747,17.08194,0 73.18359,17.29888,0 73.24442999999999,17.28417,0 73.15998999999999,17.40055,0 73.18581,17.44027,0 73.12776,17.53943,0 73.12776,17.56611,0 73.19914,17.58638,0 73.14166,17.59361,0 73.11443,17.63666,0 73.10914,17.76972,0 73.01166000000001,17.94805,0 73.03498999999999,17.98472,0 73.00054,18.01444,0 73.04443000000001,18.05167000000001,0 72.97971,18.06305,0 72.96915,18.18694,0 72.92221000000001,18.22722,0 72.93803,18.27945,0 72.97165000000001,18.23944,0 73.05663000000001,18.22138,0 73.05165,18.18305,0 73.07887,18.21472,0 73.06499,18.27389,0 72.98581,18.28389,0 72.90694000000001,18.36249,0 72.90916,18.52638,0 72.95138,18.52944,0 72.99387,18.4561,0 73.01694000000001,18.46722,0 72.9611,18.55139000000001,0 72.91443000000001,18.55277,0 72.85748,18.64805,0 72.85741,18.79465,0 72.80582,18.895,0 72.80247,18.95304,0 72.76999000000002,18.94306,0 72.82249,19.03777,0 72.82082,19.17277,0 72.77998,19.16027,0 72.82666,19.25056,0 72.77638,19.20388,0 72.77748,19.30861,0 72.96971000000001,19.29166,0 73.01609999999999,19.20583,0 73.04722,19.21726,0 72.96388,19.30917,0 72.79776,19.32611,0 72.75443,19.36972,0 72.74359,19.47611,0 72.87916000000001,19.52778,0 72.79443000000001,19.52361,0 72.73305000000001,19.57805,0 72.74249,19.51056,0 72.71693000000001,19.5475,0 72.68831,19.72111,0 72.73165,19.70555,0 72.73220000000001,19.78277,0 72.67525999999999,19.74611,0 72.64748,19.84666,0 72.72745999999999,20.13218,0 72.75359,20.295,0 72.88527000000001,20.51305,0 72.90166000000001,20.73222,0 72.94609,20.75833,0 72.92110000000001,20.81583,0 72.88916999999999,20.79555,0 72.89305,20.87305,0 72.83443,20.83416,0 72.79096,20.87397,0 72.83098,20.92505,0 72.89109999999999,20.97888,0 72.78471999999999,21.00082,0 72.73706,20.99305,0 72.70739,21.07498,0 72.70011,21.07702,0 72.66921000000001,21.093,0 72.62608,21.08472,0 72.61248999999999,21.12068,0 72.60988999999999,21.25536,0 72.58249000000001,21.34138,0 72.64776999999999,21.35889,0 72.56667,21.3825,0 72.75499000000001,21.46472,0 72.6947,21.46972,0 72.59081999999999,21.41195,0 72.61156,21.47095,0 72.52789,21.67045,0 72.30326,21.63418,0 72.24249,21.46527,0 72.10387,21.30972,0 72.08611000000001,21.24471,0 72.11053,21.20194,0 72.04886999999999,21.16083,0 71.56026,20.94611,0 71.56191,20.97861,0 71.46693000000001,20.88194,0 71.01195000000001,20.74166,0 70.96608999999999,20.70277,0 70.83138,20.69444,0 70.52777,20.81111,0 70.26028,20.97666,0 70.01582999999999,21.18611,0 69.80888,21.46361,0 69.59582,21.63333,0 69.40331999999999,21.80306,0 69.38970999999999,21.87444,0 69.36053,21.84722,0 69.22942000000001,21.94722,0 68.96278,22.24777,0 68.94274,22.32166,0 68.96388,22.38445,0 69.06914,22.48121,0 69.13518999999999,22.5504,0 69.20937000000001,22.73017,0 69.14346999999999,22.8714,0 68.66665,23.14222,0 68.5936,23.22888,0 68.62053,23.26777,0 68.56887999999999,23.25,0 68.53916,23.27833,0 68.51105,23.36989,0 68.53804,23.41805,0 68.47638000000001,23.40027,0 68.46278,23.43944,0 68.43692,23.42638,0 68.39158999999999,23.4551,0 68.40292,23.51842,0 68.42331,23.62722,0 68.48553,23.61,0 68.46693000000001,23.65304,0 68.63638,23.75722,0 68.66887,23.80972,0 68.81833,23.87638000000001,0 68.62831,23.82611,0 68.58804000000001,23.76695,0 68.44747,23.72693,0 68.32138999999999,23.58083,0 68.14028,23.60471,0 68.2011,23.66417,0 68.23054999999999,23.64361,0 68.28027,23.66666,0 68.21388,23.68055,0 68.1978,23.76668,0 68.21442,23.86833,0 68.28194000000001,23.89777,0 68.29053999999999,23.94444,0 68.33054,23.91472,0 68.36193,23.97388,0 68.37637000000001,23.94417,0 68.39055000000001,23.96749000000001,0 68.74721,23.96999,0 68.74525,24.31639,0 68.78306000000001,24.33278,0 68.82471000000001,24.33278,0 68.85804,24.24166,0 68.92693,24.32555,0 68.98803,24.25694,0 69.08028,24.29722,0 69.18941,24.25805,0 69.31081,24.29666,0 69.59860000000001,24.28139,0 69.72360000000001,24.17111,0 70.01470999999999,24.16972000000001,0 70.07071000000002,24.19792,0 70.12009000000001,24.30904,0 70.56749000000002,24.43639,0 70.60081,24.415,0 70.56776000000001,24.37138,0 70.58443,24.27834,0 70.65054000000001,24.24138,0 70.79082,24.23944,0 70.88582,24.27444,0 70.86443,24.31694,0 70.92804,24.36167,0 71.02916999999999,24.36361,0 71.10581999999999,24.41638,0 71.08194,24.44861,0 71.0061,24.45805,0 71.00555,24.54084,0 70.97971,24.56056,0 70.99276,24.6361,0 71.07743000000001,24.68047,0 70.9422,24.93555,0 70.8797,25.15611,0 70.75333000000001,25.27389000000001,0 70.67747,25.40166,0 70.66638,25.69833,0 70.60637,25.71416,0 70.38611,25.67472,0 70.29276000000002,25.70138,0 70.1172,25.91888000000001,0 70.09276,26.08,0 70.17386999999999,26.24444,0 70.17192,26.55277,0 70.07138000000001,26.59611,0 69.80081,26.59416,0 69.72276000000001,26.66527,0 69.51277,26.74667000000001,0 69.48609999999999,26.80638,0 69.50972,26.99221,0 69.58333,27.17833,0 69.86749,27.40861,0 70.0311,27.59055,0 70.11971,27.81611,0 70.20354,27.88558,0 70.36832,28.02083,0 70.51443,28.03917,0 70.6636,27.92833,0 70.6622,27.86333000000001,0 70.73775999999999,27.73138,0 70.86665000000001,27.70693,0 71.22581,27.84583,0 71.62691,27.87611,0 71.89694,27.96194,0 71.91998,28.12082,0 72.20663999999999,28.4025,0 72.29221,28.68,0 72.38970999999999,28.785,0 72.94832,29.03583,0 72.98888,29.14194000000001,0 73.08638000000001,29.24582,0 73.26527,29.55306,0 73.39749000000001,29.94277,0 73.93341,30.136,0 73.96442,30.23472,0 73.93442,30.31416,0 73.8586,30.36028,0 73.8725,30.3901,0 73.93380000000002,30.41625,0 73.99609,30.505,0 74.05025999999999,30.515,0 74.04886999999999,30.59916000000001,0 74.16109,30.66167,0 74.31693,30.88944,0 74.40063000000001,30.88415000000001,0 74.43221,30.94277,0 74.55499,30.99505,0 74.56112,31.04559,0 74.69459000000002,31.05396,0 74.68432,31.11202,0 74.62375,31.12393,0 74.55728000000001,31.07605,0 74.53898,31.11872,0 74.54837000000001,31.35443,0 74.65510999999999,31.42797,0 74.59842,31.48941,0 74.61033999999999,31.55431,0 74.48872,31.72159,0 74.54922000000001,31.74385,0 74.60532000000001,31.87712,0 74.83311000000001,31.96271000000001,0 74.90347000000001,32.05329,0 75.04075000000002,32.04146,0 75.19403,32.07404,0 75.22417,32.11676,0 75.25653,32.09287,0 75.30686,32.13391,0 75.32883,32.20907,0 75.39130000000002,32.24158,0 75.32859000000001,32.29276,0 75.31931,32.34466,0 75.04886999999999,32.47832,0 74.93858,32.44887,0 74.86165,32.48472,0 74.74443000000001,32.465,0 74.70387,32.48555,0 74.65358999999999,32.59332,0 74.70165,32.84694,0 74.63248,32.77055,0 74.53166,32.75027,0 74.45444000000001,32.79306,0 74.37164,32.7711,0 74.32966999999999,32.81542,0 74.36388,32.86944,0 74.33414999999999,33.00054,0 74.17331,33.04749,0 74.01331999999999,33.19888,0 74.02415000000001,33.27638,0 74.12581,33.34333,0 74.18248,33.51056,0 74.15443000000001,33.56139,0 74,33.65417,0 73.99471,33.75555,0 74.05025999999999,33.83167,0 74.23581,33.88416,0 74.29582000000001,33.97748,0 74.24442999999999,34.01915,0 73.97887,34.01139,0 73.91332,34.0686,0 73.9122,34.11527,0 74.01416999999999,34.17556,0 74.01776,34.21804000000001,0 73.97331,34.30277,0 73.84805,34.3186,0 73.79637,34.38555,0 73.87054000000001,34.45833,0 73.88193,34.52194,0 73.94637,34.56554,0 73.94914,34.65444,0 74.38916,34.78194,0 74.69275,34.6936,0 75.24303999999999,34.62609,0 75.39249,34.55471,0 75.66137000000001,34.50082,0 75.80553999999999,34.50721,0 75.98804,34.62694,0 76.16748,34.58638,0 76.22916000000001,34.60944,0 76.29359,34.70027,0 76.47026,34.76999,0 76.56303000000001,34.73166,0 76.7272,34.74388,0 76.80138,34.67222,0 76.86998,34.65888,0 76.91692999999999,34.68138,0 76.96416000000001,34.94055,0 77.06081,35.01472,0 77.04248,35.09916,0 77.82393,35.50133000000001,0 77.92470000000002,35.48972,0 77.97248999999999,35.57417000000001,0 78.13694,35.54333,0 78.17221000000001,35.61943,0 78.29915,35.70194,0 78.62553,35.81666,0 78.74193,35.92027,0 78.93442,35.89249,0 79.32832999999999,35.98638,0 79.43803000000001,35.96054000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +05:45</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(5.75);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0575</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +82.0664,27.91416,0 81.97776000000002,27.91722,0 81.88026000000001,27.85721,0 81.48027,28.08,0 81.42192,28.16638,0 81.31999000000002,28.13,0 81.31218,28.19333,0 81.19359,28.36667,0 81.01888,28.40416000000001,0 81.00694,28.44305,0 80.93858,28.44111,0 80.89249,28.50638,0 80.79832,28.52389,0 80.66249000000001,28.63277,0 80.58499000000001,28.6536,0 80.57720999999999,28.68556,0 80.50555,28.65833,0 80.50861,28.56693,0 80.44026,28.62888,0 80.35776,28.62944,0 80.24609,28.75082,0 80.05582,28.83611,0 80.05998,28.93166,0 80.1297,28.99305,0 80.14664999999999,29.10833,0 80.25694,29.14472,0 80.23997,29.21472,0 80.28777,29.20556,0 80.30693000000001,29.31055,0 80.27582,29.3225,0 80.23637000000001,29.42527,0 80.40694000000001,29.59638,0 80.37691,29.7486,0 80.53970000000001,29.84611,0 80.59887000000001,29.95527,0 80.74609,30.02138,0 80.87276,30.13888,0 80.8586,30.17472,0 80.89333000000001,30.21111,0 80.93637,30.18138,0 81.02536000000001,30.20436,0 81.10971000000001,30.02472,0 81.22360000000001,30.01027,0 81.30193,30.08361,0 81.29082,30.13055,0 81.37777,30.16667,0 81.42110000000001,30.38528,0 81.54192000000001,30.33056,0 81.55775,30.38194,0 81.63721,30.42444,0 81.80304,30.36361,0 82.10054,30.34222000000001,0 82.10721,30.23083,0 82.17859,30.19055,0 82.15027000000001,30.12556,0 82.17776000000001,30.06361,0 82.23109,30.07527,0 82.55775,29.94277,0 82.65581,29.83527000000001,0 82.71554999999999,29.83194,0 82.71053000000001,29.78972,0 82.85525000000001,29.66889000000002,0 82.90860000000001,29.69444,0 82.9922,29.6725,0 83.09276,29.58305,0 83.11749,29.62056000000001,0 83.19525,29.63083,0 83.28306000000001,29.55111000000001,0 83.28442,29.47443,0 83.34610000000001,29.49666,0 83.37916,29.46611,0 83.38054,29.41638,0 83.52748,29.26916,0 83.55277,29.18583,0 83.67192,29.16027,0 83.72054,29.22889,0 83.91249000000001,29.28972,0 84.11388000000001,29.26194,0 84.13667,29.21443,0 84.18552,29.20556,0 84.15833000000001,29.13472,0 84.20249,29.05695,0 84.2561,29.04222,0 84.23693000000002,28.91888000000001,0 84.39554,28.85917,0 84.48109,28.73667,0 84.63639000000001,28.70277000000002,0 84.70663999999999,28.66055,0 84.70610000000001,28.60139,0 84.7747,28.59888,0 84.84663999999999,28.54445,0 84.95470000000002,28.54416,0 85.05998,28.64111,0 85.18996,28.60332,0 85.18192999999999,28.53,0 85.10193,28.44666,0 85.10332,28.31639,0 85.37665,28.26361,0 85.45889,28.30166,0 85.61998,28.25111,0 85.71832000000002,28.33943,0 85.74722,28.23693000000001,0 85.88611,28.15167,0 85.91887,28.03333,0 85.97803999999999,27.98972,0 85.97304,27.91583,0 86.01443,27.88277,0 86.08942999999999,27.89083000000001,0 86.13721,27.93194,0 86.0936,28.08611,0 86.18359,28.16388,0 86.22638000000001,28.00667,0 86.44498,27.90805,0 86.53498999999999,27.95499,0 86.57193,28.10583000000001,0 86.63666000000001,28.08555,0 86.68637,28.11222,0 86.75805,28.08027,0 86.76416,28.02666,0 86.87415,28.01777,0 86.93304000000002,27.95055,0 87.03943000000001,27.96083,0 87.19275,27.82306,0 87.30609,27.81639,0 87.40416999999999,27.86055,0 87.39360000000001,27.80916,0 87.5461,27.84583,0 87.59775999999999,27.81027,0 87.66054,27.82888,0 87.72054,27.805,0 87.79721000000001,27.83722,0 87.86887,27.91888,0 88.14279999999999,27.86605,0 88.19386,27.8475,0 88.18887000000001,27.78777,0 88.11913,27.60833,0 88.05692999999999,27.53334,0 88.06667000000002,27.43693,0 88.03888000000001,27.37611,0 88.05971,27.32777000000002,0 87.99303999999999,27.11444,0 88.11859,26.98444,0 88.19497,26.75416,0 88.10193,26.53028000000001,0 88.09583000000001,26.43888000000002,0 88.02638,26.36916,0 87.94941,26.40055,0 87.89055999999999,26.47333,0 87.84305000000001,26.43888000000002,0 87.78554,26.45638,0 87.76639,26.40861,0 87.69748,26.42944,0 87.59999000000001,26.37972,0 87.44748,26.43250000000001,0 87.37082,26.40333,0 87.32805999999999,26.34777,0 87.24525,26.41222,0 87.1375,26.40972,0 87.09415,26.45638,0 87.06137,26.58833,0 86.88598,26.46866,0 86.73165,26.41999,0 86.53498999999999,26.52806,0 86.33249000000001,26.58499,0 86.33167,26.61277,0 86.20664000000001,26.58556,0 86.18387,26.60806,0 86.02943000000001,26.66306,0 85.85027,26.57222,0 85.73054999999999,26.66722,0 85.72609,26.80167000000001,0 85.63026000000001,26.8675,0 85.45305000000001,26.78694000000001,0 85.41249000000001,26.79472000000001,0 85.33443,26.73582,0 85.21165000000001,26.76694,0 85.18388,26.86666,0 85.04082,26.84777,0 85.04109,26.88666,0 84.97331,26.90304,0 84.95277,26.95804,0 84.84360000000001,27.01527,0 84.75027,27.0075,0 84.64749000000001,27.04527,0 84.68414,27.21555,0 84.60997999999999,27.33556,0 84.29776,27.38832,0 84.14333000000001,27.52417,0 84.09887000000001,27.51611,0 84.04026,27.44472,0 83.83526999999999,27.43805,0 83.87553,27.38083,0 83.85832000000001,27.35221,0 83.60776,27.46916,0 83.39610000000002,27.47861,0 83.37665,27.37527,0 83.29137,27.33777,0 83.17638,27.44888,0 82.76860000000001,27.50166,0 82.7011,27.71111000000001,0 82.43471,27.67556,0 82.0664,27.91416,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +06:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(6);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0600</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +73.35092,-9.431101,0 70.25215,-9.433405000000001,0 70.25215,-6.970275,0 73.34769,-6.970275,0 73.35092,-9.431101,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +88.43304000000001,26.55139000000001,0 88.68082,26.42027,0 88.69164000000001,26.34277,0 88.73037000000001,26.32068,0 88.67950999999999,26.30742,0 88.66918,26.2609,0 88.76734,26.29859,0 88.82383,26.23343,0 88.88330000000001,26.2852,0 89.02048000000001,26.23517,0 89.04134000000001,26.28179,0 88.98159000000001,26.29568,0 88.90916,26.39271,0 88.93877999999999,26.44582,0 89.06721,26.3877,0 89.12967,26.15927,0 89.31990999999999,26.02483000000001,0 89.39355000000001,26.0479,0 89.52551,25.98821,0 89.60242,26.06897,0 89.55986,26.16146000000001,0 89.60896000000001,26.17790000000002,0 89.60173,26.22748,0 89.6386,26.2278,0 89.6493,26.18946,0 89.73915,26.15638,0 89.77804999999999,26.04195,0 89.85637,25.98111,0 89.81671,25.95416000000001,0 89.86545,25.93686,0 89.80721,25.82305,0 89.86443,25.64056,0 89.81554,25.36693,0 89.84526000000001,25.2886,0 89.91721000000001,25.31,0 90.11053000000001,25.22472,0 90.43831,25.14721000000001,0 90.65305000000001,25.18111,0 90.84804,25.14861000000001,0 91.25777000000001,25.20778,0 91.48221000000001,25.13861,0 91.59027,25.17278,0 91.64888000000002,25.12667,0 91.75749000000002,25.17388,0 92.04080999999999,25.18777,0 92.40637,25.03056000000001,0 92.41665999999999,24.9725,0 92.48305000000001,24.93,0 92.49384000000001,24.87879,0 92.40173000000002,24.84041,0 92.25526000000004,24.90361,0 92.25664999999999,24.77390000000001,0 92.19108000000001,24.56528,0 92.13247,24.51388,0 92.13527000000002,24.41416,0 92.08638000000001,24.37472,0 91.97470000000001,24.37889,0 91.98943000000001,24.31944,0 91.94664000000002,24.34945,0 91.91776,24.32667,0 91.93248,24.27555,0 91.89249,24.14972,0 91.83192000000001,24.22611,0 91.75775,24.23917000000001,0 91.75525,24.15833,0 91.70385000000002,24.15166,0 91.6572,24.22416,0 91.62746,24.10972,0 91.54633,24.08693,0 91.37329,24.10194,0 91.36110000000001,23.99528,0 91.29832,23.99388,0 91.23776000000001,23.90777,0 91.25054000000002,23.83027,0 91.21638,23.75611,0 91.15860000000002,23.73666,0 91.15915,23.70082,0 91.20028000000004,23.67805,0 91.15942,23.64055000000001,0 91.28221000000002,23.37332,0 91.31469000000001,23.36416,0 91.28859,23.32832,0 91.33554,23.11555,0 91.40221,23.06945,0 91.37746,23.20693,0 91.41665999999999,23.26611,0 91.49332000000001,23.18666,0 91.56359999999999,22.9836,0 91.61360000000001,22.94305,0 91.64722000000002,22.98638,0 91.72499000000001,22.99472,0 91.82055,23.09777,0 91.76805000000002,23.25,0 91.78360000000001,23.3125,0 91.84498000000001,23.41027,0 91.93304000000001,23.44444,0 91.96609000000001,23.50166,0 91.95,23.73222000000001,0 92.0372,23.64528,0 92.17486000000001,23.73682,0 92.21165000000001,23.65222,0 92.25221000000002,23.7225,0 92.28888000000001,23.69611,0 92.27831999999999,23.605,0 92.37358,23.35925,0 92.38697999999999,23.27993,0 92.34860000000001,23.22361,0 92.37469,22.94193,0 92.44386000000002,22.88944,0 92.47054000000001,22.75638,0 92.51639000000002,22.71583,0 92.60609,22.16277,0 92.56218,22.13333,0 92.60081,21.98222000000001,0 92.62164,21.87583,0 92.60027000000001,21.63416,0 92.66935000000002,21.29698,0 92.59554000000001,21.25194,0 92.55359,21.37361,0 92.42747000000001,21.37472,0 92.37691,21.47416,0 92.31667,21.42138,0 92.26555000000002,21.42333,0 92.25305000000002,21.3725,0 92.19220000000001,21.32444,0 92.19859000000001,21.15417,0 92.26194,21.05431,0 92.26331999999999,20.91916000000001,0 92.33333,20.76416,0 92.31895,20.729,0 90.32641000000001,18.56566,0 90.33015,15.23029,0 90.33504000000001,10.88448,0 90.34064000000001,5.884479,0 92.9025,5.880672000000001,0 94.60397,3.503177,0 97.51384,-0.5628515,0 97.51384,-1.259497,0 97.51384,-6.259501,0 97.51384,-11.2595,0 93.69904000000001,-11.2595,0 93.69904000000001,-13.93192,0 97.51384,-13.93535,0 97.51384,-14.73534,0 97.51384,-19.73534,0 97.51384,-24.73534,0 97.51384,-29.73533,0 97.51384,-34.73534,0 97.51384,-39.73533000000001,0 97.51384,-44.73534,0 97.51384,-48.73533,0 97.51384,-53.73534000000002,0 97.51384,-58.73533,0 97.51384,-63.73534,0 97.51384,-64.90000000000001,0 97.51384,-69.90000000000001,0 97.51384,-74.90000000000001,0 97.51384,-79.90000000000001,0 97.51384,-84.89999,0 97.51384,-89.90000000000001,0 96.57599999999999,-89.90000000000001,0 95.63816,-89.90000000000001,0 94.70033000000002,-89.90000000000001,0 93.76249,-89.90000000000001,0 92.82466000000001,-89.90000000000001,0 91.88683000000002,-89.90000000000001,0 90.94899,-89.90000000000001,0 90.01115000000002,-89.90000000000001,0 89.07332,-89.90000000000001,0 88.13549,-89.90000000000001,0 87.19765,-89.90000000000001,0 86.25982000000001,-89.90000000000001,0 85.32198,-89.90000000000001,0 84.38415999999999,-89.90000000000001,0 83.44632000000001,-89.90000000000001,0 82.50848000000001,-89.90000000000001,0 82.50834999999999,-84.89999,0 82.50829,-82.28883,0 82.50816,-77.28883,0 82.50802,-72.28883000000002,0 82.50792,-68.68883,0 82.5078,-63.68882,0 82.50766,-58.68883,0 82.50753000000002,-53.68882000000001,0 82.5074,-48.68883000000001,0 82.50727999999999,-43.68883,0 82.50713,-38.68883,0 82.50700000000002,-33.68883,0 82.50697,-32.48883,0 82.50684,-27.48883,0 82.50671,-22.48882,0 82.50658,-17.48883,0 82.50655,-16.18883,0 82.5064,-11.18883,0 82.50624999999999,-6.188824,0 82.5061,-1.188829,0 82.50595000000001,3.811175,0 82.50581,8.811171,0 82.50565,13.81117,0 82.50555,17.22305,0 83.21638,17.58055,0 83.23859,17.60083,0 83.21304000000001,17.63138,0 83.29694000000002,17.66555,0 83.23693,17.71167,0 83.29498000000001,17.68693,0 83.34220000000001,17.71444,0 83.45664000000001,17.88138,0 83.44332000000001,17.91582,0 83.47054,17.9,0 83.58275999999999,18.0175,0 83.77582,18.14277,0 84.08028,18.26861,0 84.14166,18.37111,0 84.34942,18.54888,0 84.27666000000001,18.49944,0 84.24165000000001,18.49611,0 84.24498,18.525,0 84.27554000000001,18.51833,0 84.43552,18.64638,0 84.67638,18.94194,0 84.78083,19.09417,0 84.73526,19.07472,0 84.72659,19.124,0 84.74193,19.14889,0 84.78610000000001,19.11443,0 84.86638000000001,19.22027,0 85.23415,19.51195,0 85.62385999999999,19.71361,0 86.28027,19.91416,0 86.42386999999999,19.98611,0 86.39360000000001,20.01388,0 86.48888,20.10721,0 86.57444,20.21249,0 86.74802000000001,20.3061,0 86.69275,20.29416,0 86.80054,20.38166,0 86.80441999999999,20.43611,0 86.78525999999999,20.38527,0 86.72083000000002,20.3725,0 86.75584000000001,20.49138,0 87.04165999999999,20.69333,0 86.98888,20.67167,0 86.93718,20.7,0 86.99498,20.70666,0 86.98553,20.75278,0 86.87886,20.76777,0 86.96748,20.79527,0 86.83526999999999,21.10611,0 86.83443,21.18944,0 86.87553,21.27443,0 87.11693,21.51139,0 87.48090000000002,21.61178,0 87.68498,21.64777,0 87.82944000000001,21.71916,0 87.91101999999999,21.80093000000001,0 87.94918,21.80093000000001,0 88.05122000000002,21.63086,0 88.12221,21.61312,0 88.17249,21.53918,0 88.20502000000001,21.51847,0 88.25225000000002,21.55052,0 88.30831000000001,21.57055,0 88.26194,21.8,0 88.29721000000001,21.77277,0 88.30859000000001,21.60305,0 88.34415,21.62222,0 88.33333,21.71389,0 88.34610000000001,21.68388,0 88.38694,21.69695,0 88.39082000000001,21.58833,0 88.45387,21.615,0 88.49609,21.95027,0 88.49802,21.87027,0 88.53749000000002,21.81388000000001,0 88.50082,21.7525,0 88.52554000000002,21.65583,0 88.48305000000001,21.55694,0 88.49802,21.52056,0 88.53887,21.52715,0 88.70639,21.57051,0 88.78551,21.55832,0 88.88085000000001,21.51552,0 89.00027,21.60209,0 89.02748,21.62695,0 89.07416000000001,21.61417,0 89.08971,21.65027,0 89.02110000000002,21.78222,0 89.01639,21.86582,0 88.90555000000001,21.95916,0 88.93137,21.98638,0 88.94275,21.93666,0 88.99970999999999,21.90277,0 89.00861,21.93027,0 89.05749,21.92833,0 89.03665,22.09805,0 89.04387,22.13638,0 89.063,22.11547,0 89.09637000000001,22.14861,0 89.00722,22.27639,0 88.99776000000001,22.46555,0 88.92918,22.54767,0 88.95860000000002,22.59138,0 88.93026999999999,22.74833,0 88.97110000000001,22.84694,0 88.86276000000001,22.96667,0 88.87025,23.08777,0 88.98609999999999,23.20833,0 88.72110000000002,23.25166,0 88.71138000000001,23.30861000000001,0 88.79331999999999,23.49417,0 88.74942,23.46693000000001,0 88.56386999999999,23.65222,0 88.60284,23.8624,0 88.66949,23.86855,0 88.73665,23.9198,0 88.72272,23.99796,0 88.75918000000002,24.03604,0 88.70139,24.07917,0 88.70734,24.12171,0 88.70833,24.17138,0 88.75291,24.21413,0 88.70638,24.30277,0 88.49776,24.32083,0 88.32388,24.43443,0 88.12553,24.50861,0 88.04387,24.68972,0 88.1711,24.85166,0 88.13777,24.92138,0 88.22135000000002,24.95433,0 88.26749000000001,24.88472,0 88.34248,24.87028,0 88.45999000000001,25.06944,0 88.45556000000001,25.19055,0 88.58138,25.16945,0 88.62025,25.19806,0 88.70499,25.19527,0 88.80582,25.16556,0 88.83778,25.20361000000001,0 88.93304000000001,25.16444,0 89.00888000000001,25.30083,0 88.86998,25.33583,0 88.82166000000001,25.40166,0 88.83443,25.47916,0 88.79971000000001,25.51055,0 88.69414,25.46778,0 88.54693,25.50416,0 88.45054,25.59389,0 88.44693,25.66611,0 88.27998,25.80167,0 88.15194,25.77861,0 88.11053,25.83555000000001,0 88.09554,25.91527,0 88.18333,26.03389,0 88.15636000000001,26.09249,0 88.18442,26.1525,0 88.35526,26.22333,0 88.35609,26.28444,0 88.45749000000001,26.36916,0 88.52554000000002,26.35972,0 88.48331,26.46111000000001,0 88.37109,26.48917,0 88.35359,26.45167,0 88.33777000000001,26.47389,0 88.40109,26.62527,0 88.43304000000001,26.55139000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +90.00002000000002,28.32405,0 90.38165000000001,28.25139,0 90.39554000000001,28.19888,0 90.35027,28.11222,0 90.37053000000002,28.08027,0 90.60804,28.06445,0 90.69942,28.09583,0 90.90998999999999,28.04944,0 91.01082000000001,27.98,0 91.08693,27.96999,0 91.21444,28.07305,0 91.31499,28.09667,0 91.41943000000001,28.01056,0 91.66721,27.94194,0 91.65776,27.76472,0 91.65277000000002,27.71167,0 91.59498000000001,27.64554,0 91.62082000000001,27.54861,0 91.67359,27.48861,0 91.76555000000002,27.45694,0 91.76833000000002,27.41611,0 91.99887,27.47805,0 92.08916000000001,27.34944,0 92.11443000000001,27.28583,0 92.01776,27.1725,0 92.00999000000002,27.10472,0 92.11193000000002,26.94416,0 92.08150999999999,26.86905,0 91.99721,26.85194,0 91.91526,26.89389,0 91.90498,26.80556,0 91.84360000000001,26.86416,0 91.71249,26.8,0 91.54221,26.80445,0 91.49721000000001,26.8725,0 91.4247,26.87556,0 91.3797,26.8075,0 91.31441000000001,26.77806,0 91.09109000000001,26.81639,0 91.03194000000002,26.78278,0 90.6947,26.77222,0 90.55914,26.80722,0 90.38527000000001,26.90333,0 90.21693000000001,26.85082,0 90.17747,26.77306000000002,0 90.1236,26.74805,0 89.85942,26.73666,0 89.84887000000001,26.70167,0 89.68552000000001,26.7336,0 89.64305000000002,26.71527,0 89.61832,26.72694,0 89.63611,26.77111,0 89.53360000000001,26.81221,0 89.46833,26.79944,0 89.36081,26.86,0 89.22083000000001,26.81472,0 89.12608,26.82361,0 89.03055000000001,26.92749,0 88.95193000000002,26.92667000000001,0 88.93332000000001,26.98499,0 88.87636999999999,26.95055,0 88.86360999999999,27.09194000000002,0 88.75193,27.14861,0 88.79803,27.24638,0 88.90138000000002,27.28222,0 88.91692000000001,27.32083,0 88.97110000000001,27.47361,0 89.12581,27.5675,0 89.12804,27.62333,0 89.22553000000001,27.795,0 89.35999,27.87167000000001,0 89.48749000000001,28.05777,0 89.6236,28.15833,0 89.79276000000002,28.19416,0 89.89388,28.30611,0 89.95027,28.29222,0 90.00002000000002,28.32405,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +75.2397,58.21998,0 75.06638000000001,58.11833,0 75.56441,57.93387,0 75.58275999999999,57.64639,0 75.71249,57.63917000000001,0 75.8586,57.39917,0 75.96638,57.41416,0 76.10887,57.25027999999999,0 76.19192,56.97276000000001,0 76.25194999999999,56.91833,0 76.28943,56.72694,0 76.14915000000001,56.63082,0 76.0461,56.62165000000002,0 76.01528,56.55165000000001,0 75.86526000000001,56.49971000000001,0 75.84415,56.45,0 75.895,56.44276,0 75.93581,56.47971,0 76.0061,56.45388,0 76.05748,56.48277,0 76.14194000000001,56.4511,0 76.07805999999999,56.41471,0 76.11665000000002,56.31415,0 76.29971000000001,56.23638,0 76.31887999999999,56.18555,0 76.16304,56.14943,0 76.15082,56.18055,0 75.99442999999999,56.17832000000001,0 75.95638,56.12749000000001,0 75.74887,56.14111000000001,0 75.70055000000001,56.0675,0 75.54304999999999,56.04722000000002,0 75.50584000000001,55.99610000000001,0 75.31219,55.89166000000001,0 75.29749,55.84471,0 75.37331,55.81333,0 75.37025,55.76666,0 75.08194,55.75027,0 75.13694000000001,55.72332,0 75.12886,55.69943000000001,0 75.23775999999999,55.65999999999999,0 75.13136,55.59888000000002,0 75.1972,55.55972,0 75.30582000000001,55.56917,0 75.32223000000001,55.54027,0 75.325,55.50722,0 75.17137,55.45277,0 75.16859,55.4211,0 75.09138,55.38221,0 75.26944,55.25416,0 75.24803,55.1375,0 75.40027000000002,55.02804,0 75.34027,54.91416,0 75.54082,54.90194,0 75.49165000000001,54.86138,0 75.51331999999999,54.82333,0 75.65721000000001,54.80999,0 75.70972,54.68639,0 75.80887,54.69304,0 75.7897,54.66945,0 75.83499000000001,54.60471,0 75.80609,54.56554,0 75.67777000000001,54.53417,0 75.69136,54.11027,0 76.20721,54.25916000000002,0 76.18552,54.29554000000001,0 76.23888,54.35889,0 76.48775999999999,54.32556,0 76.65694000000001,54.36777000000001,0 76.70663999999999,54.44721,0 76.92359999999999,54.47554000000001,0 76.92498999999999,54.43749,0 76.77888,54.39415,0 76.85583000000001,54.35889,0 76.86498,54.31805000000001,0 76.72136999999999,54.15331000000001,0 76.39333000000001,54.16361,0 76.52164999999999,53.99388,0 77.52387,53.50639,0 77.92137,53.2589,0 78.92303,52.17638000000002,0 79.53637000000001,51.45000000000001,0 79.99387,50.81304999999999,0 80.1096,50.75947,0 80.03516999999999,50.83338,0 80.06545,50.86060000000001,0 80.17211,50.82919,0 80.19905,50.88837,0 80.46236,50.96408000000001,0 80.48678,51.05877,0 80.43068000000001,51.11909,0 80.43872,51.19797000000001,0 80.63344000000001,51.21476000000001,0 80.68783999999999,51.31472000000001,0 80.87224999999999,51.25904,0 80.93223999999999,51.28399,0 80.94029000000002,51.22122,0 81.04084,51.18143,0 81.18359,51.19609,0 81.07639,50.94781,0 81.4247,50.96638,0 81.42192,50.92777,0 81.48997,50.8411,0 81.43747999999999,50.76445,0 81.59540000000001,50.73573,0 81.72581,50.74749000000001,0 81.78276,50.79333,0 81.82887000000001,50.76833000000001,0 82.01111,50.77138999999999,0 82.15027000000001,50.71054,0 82.35637,50.75972,0 82.44274,50.75804999999999,0 82.49887,50.7211,0 82.72026,50.81081,0 82.72748,50.89888,0 82.76111,50.91082,0 83.00999,50.88944000000001,0 83.0872,50.92361,0 83.1386,51.00194000000001,0 83.45638,51.00249000000001,0 83.51361,50.95555000000001,0 83.84333,50.87749000000001,0 83.90166000000002,50.81388000000001,0 83.97693,50.79749,0 83.96998000000001,50.71555,0 84.10621999999999,50.64296,0 84.22304,50.51471000000002,0 84.19692000000001,50.45304,0 84.26277,50.35443,0 84.26694000000001,50.27054,0 84.37302,50.20971000000002,0 84.47386,50.25666,0 84.55998,50.19555,0 84.67443,50.19694,0 84.73636999999999,50.1461,0 84.81053,50.14416000000001,0 84.85193,50.09166000000001,0 85.01443,50.07583,0 85.05832,50.01361,0 84.97915999999999,49.99777,0 84.97776,49.91276,0 85.0836,49.88138,0 85.09193000000001,49.81971,0 85.21053000000001,49.71555000000001,0 85.21111000000001,49.64721000000001,0 85.2586,49.59138000000001,0 85.40387,49.62332,0 85.52443,49.59055,0 85.63081,49.62749,0 85.69054,49.56444,0 85.77083,49.57999,0 85.8372,49.54388000000001,0 85.96915,49.56638,0 85.96944000000001,49.48861000000001,0 86.13275,49.53277000000001,0 86.19107,49.4725,0 86.24582000000001,49.50139999999999,0 86.28749000000001,49.58582000000001,0 86.50583,49.67638,0 86.49706999999999,49.7155,0 86.61109,49.76111,0 86.61638000000001,49.79721,0 86.77499,49.78889,0 86.76277000000002,49.73444000000001,0 86.80025999999999,49.70222,0 86.68221000000001,49.66999,0 86.61665000000001,49.58721000000001,0 86.84694,49.52166,0 86.83971,49.45222,0 86.96442,49.39194,0 86.92665,49.35306,0 87.05247,49.25417000000001,0 87.21887,49.2461,0 87.29971000000001,49.21054000000001,0 87.34821,49.09261,0 87.16748,49.13304,0 86.99887,49.09944,0 86.87469,49.11083000000001,0 86.73054999999999,48.97222,0 86.75277000000001,48.90028000000002,0 86.82138000000001,48.84665,0 86.75943,48.79361,0 86.78333000000002,48.72776000000001,0 86.64499000000002,48.61639,0 86.59610000000001,48.53611,0 86.46362000000001,48.49361,0 86.34415,48.49527000000001,0 86.23081999999999,48.42527000000001,0 85.86248999999999,48.42527000000001,0 85.75915999999999,48.38777000000001,0 85.61388,48.15833,0 85.53777,47.95999,0 85.62221,47.62111,0 85.60359,47.51943,0 85.70055000000001,47.38388000000001,0 85.70555,47.2836,0 85.68665,47.21194000000001,0 85.57331000000002,47.17694,0 85.52931,47.06016,0 85.25776999999999,47.0511,0 84.96361,46.86415,0 84.75942000000001,46.82638,0 84.73526,46.83694,0 84.77887,46.93166,0 84.67804,46.99361,0 84.53194000000001,46.97527000000001,0 84.29471,47.0075,0 84.11109999999999,46.97166000000001,0 83.90000000000001,46.97721,0 83.56499,47.0611,0 83.18571,47.21664,0 83.03276,47.20416,0 83.01470999999999,46.96027,0 82.87387000000001,46.76359999999999,0 82.70889,46.35415,0 82.57332000000001,46.17305,0 82.51082,45.90192,0 82.31493,45.5448,0 82.3622,45.49552000000001,0 82.64943,45.43026,0 82.63554000000001,45.24053,0 82.54943,45.12331,0 82.48165,45.12748,0 82.29694000000001,45.23775,0 81.94804000000001,45.15748,0 81.81581,45.20053,0 81.79776,45.30581,0 81.68831,45.35082,0 81.05444,45.16608,0 80.46915,45.1111,0 80.39166,45.04915,0 80.12138,45.03942000000001,0 79.87109000000001,44.90497,0 79.87581000000002,44.86554,0 80.00166,44.79776,0 80.26221,44.82443,0 80.52083,44.73248,0 80.51666,44.7072,0 80.37497999999999,44.65607,0 80.40721000000002,44.6022,0 80.34887000000001,44.45024,0 80.39999000000002,44.26414,0 80.36276000000001,44.12524000000001,0 80.43136,44.05303,0 80.43886999999999,43.99219000000001,0 80.55371,43.81323,0 80.73859000000002,43.4719,0 80.66054,43.32884,0 80.76914000000001,43.28886,0 80.8147,43.13997,0 80.74359,43.10775,0 80.64637999999999,43.14135,0 80.37665000000001,43.02524,0 80.44443,42.95468,0 80.56135999999999,42.92635,0 80.57861,42.89108,0 80.26066,42.81941,0 80.17192,42.6605,0 80.18221,42.56802,0 80.24249,42.49051,0 80.21720999999999,42.45051,0 80.23831000000001,42.3633,0 80.29387,42.25218,0 80.2811,42.21218,0 80.23403,42.19622,0 80.28332,42.07968000000001,0 80.25139,42.04245,0 79.87943,42.01635,0 79.77858000000001,41.88717,0 79.65498,41.88218000000001,0 79.43192999999999,41.78216,0 79.30943000000001,41.79134,0 79.20749000000001,41.72828,0 78.69553000000001,41.54301,0 78.63997999999999,41.483,0 78.53109000000001,41.46384,0 78.37553,41.37882,0 78.37997,41.28384,0 78.24623,41.16513,0 78.08083000000001,41.04079,0 77.85471,41.0694,0 77.68471,41.00189,0 77.54359000000001,40.98661,0 77.37747,41.028,0 77.13805000000001,41.01606,0 77.05831000000001,41.04911,0 76.87387,41.01411,0 76.78194000000001,40.94077,0 76.79359,40.86245,0 76.64971,40.74494,0 76.63943,40.60856,0 76.55304,40.54411,0 76.48693,40.43105,0 76.33859,40.34855,0 76.27748,40.45438,0 76.19414,40.37799,0 76.06805,40.39577,0 75.97803999999999,40.36327,0 75.92886,40.30550000000001,0 75.6972,40.29911,0 75.66137000000001,40.39827,0 75.69664,40.44494,0 75.64554,40.4866,0 75.62276,40.59799,0 75.57805000000001,40.64799,0 75.22554,40.44688,0 75.16137000000001,40.47022,0 75.05888,40.44772,0 74.86026,40.51939000000001,0 74.81026000000001,40.4198,0 74.88443,40.32771,0 74.73054999999999,40.33966,0 74.36219,40.09965,0 74.19803,40.11383,0 73.99442999999999,40.04605000000001,0 73.84498000000002,39.8055,0 73.84444000000001,39.75576,0 73.91080000000001,39.7252,0 73.95609000000002,39.59771000000001,0 73.90248,39.56966,0 73.84805,39.47493000000001,0 73.65569000000001,39.45483,0 73.66998,39.34409,0 73.6172,39.23215,0 73.72165,39.08244,0 73.85332,38.96909,0 73.70499,38.88076,0 73.7625,38.80076,0 73.75776999999999,38.73021,0 73.80219,38.64983,0 73.81776000000001,38.6077,0 73.93665,38.54105000000001,0 74.06887999999999,38.53659,0 74.06052,38.60465,0 74.11081,38.6216,0 74.13415999999999,38.67326,0 74.35471,38.67439,0 74.71833,38.5291,0 74.78859,38.52742,0 74.85664,38.47048,0 74.8647,38.38882,0 74.79443000000001,38.30603,0 74.8147,38.07883,0 74.91361000000001,38.01772,0 74.90833000000001,37.91049,0 74.93665000000001,37.88884,0 74.90277,37.84717,0 74.99109000000001,37.78382,0 74.90277,37.64715,0 74.94942,37.56298,0 75.18747999999999,37.40658,0 75.11109999999999,37.38353,0 75.09665,37.32186,0 74.95804,37.28183,0 74.91575,37.23733,0 74.83277,37.32545,0 74.68427,37.39974,0 74.54221,37.38181,0 74.36388,37.42846,0 74.23997,37.40705,0 74.23748999999999,37.36119,0 74.19721,37.33897,0 73.73858,37.22164,0 73.61998,37.26248,0 73.64943,37.30415,0 73.76888,37.34113,0 73.77598,37.4343,0 73.45637000000001,37.47611,0 73.37248,37.44692,0 73.30721,37.46275,0 73.15054000000001,37.40049,0 73.06914999999999,37.31407,0 72.79359,37.22623,0 72.65851000000001,37.0212,0 72.30636,36.9781,0 71.84276,36.6921,0 71.69525,36.67184,0 71.61006999999999,36.70038,0 71.55468999999999,36.76068,0 71.45694,36.94474,0 71.42943,37.07583,0 71.44693000000001,37.20722000000001,0 71.49026000000001,37.28362,0 71.47942000000001,37.41003,0 71.5141,37.46773,0 71.49571,37.55815,0 71.52878,37.7639,0 71.58859,37.81565,0 71.59193000000002,37.90262,0 71.51537,37.94846,0 71.33472,37.88787,0 71.25277,37.92204,0 71.36638000000002,38.23627,0 71.14554,38.38574,0 71.04971000000001,38.40294,0 70.96720000000002,38.47212,0 70.92998,38.43045,0 70.87388,38.45295,0 70.67221000000001,38.41403,0 70.68552,38.37486,0 70.59775999999999,38.34151,0 70.58443,38.27816,0 70.46416000000001,38.11119,0 70.24721,37.96394,0 70.24745,37.93169,0 70.16137000000001,37.93337,0 70.16553999999999,37.87172,0 70.24693000000001,37.81255,0 70.28581,37.69707,0 70.25360000000001,37.61574,0 70.21554999999999,37.61638,0 70.16193,37.53956,0 70.06245,37.54134,0 69.9586,37.56499,0 69.89915000000001,37.61501,0 69.80692999999999,37.58305,0 69.51582999999999,37.58082,0 69.38026000000001,37.4511,0 69.41110000000001,37.24083000000001,0 69.45055000000001,37.22527,0 69.3886,37.1586,0 69.24664,37.09416,0 69.12527,37.16304,0 68.9997,37.30777,0 68.88777,37.33860000000001,0 68.91943000000001,37.27555,0 68.83804000000001,37.32695,0 68.80663,37.31806,0 68.82165999999999,37.24499,0 68.66415000000001,37.27499,0 68.62388,37.20055000000001,0 68.4122,37.14805,0 68.39749000000001,37.09916000000001,0 68.29471,37.10832,0 68.27499,37.01222,0 68.18719,37.02055,0 68.02831999999999,36.92389,0 67.88554000000001,37.06138,0 67.78221000000001,37.09610000000001,0 67.77988000000001,37.18582,0 67.83804000000001,37.26194,0 67.81749000000002,37.4461,0 67.83832,37.50583,0 68.07305,37.76528,0 68.15054000000001,37.92805,0 68.27748,37.95889,0 68.38415000000001,38.19555,0 68.33138,38.27333,0 68.21554999999999,38.33221,0 68.1272,38.42444,0 68.07193,38.54138,0 68.05775,38.69694,0 68.10081,38.73916,0 68.08305,38.79445,0 68.15498,38.8086,0 68.19637,38.85444,0 68.12387,38.98554,0 67.70943,38.99666000000001,0 67.69026,39.13805,0 67.37636999999999,39.21249,0 67.36609,39.29361,0 67.41054,39.29916,0 67.44196,39.48359,0 67.29803,39.58611,0 67.29248,39.66555,0 67.37109,39.68777,0 67.38136,39.78055,0 67.50444,39.81554,0 67.47971,39.93194,0 67.10554999999999,39.98611,0 67.02361000000001,40.03277,0 67.03471999999999,40.06888,0 66.9422,40.06333,0 66.93471,40.125,0 66.97998,40.17389,0 66.90860000000001,40.24804,0 66.88831999999999,40.47833,0 66.62003,40.49509,0 66.80525,40.76611,0 66.7664,40.88750000000001,0 66.63247,40.93888,0 66.60804,41.11333000000001,0 66.75305,41.11693,0 66.75949000000001,41.16484000000001,0 66.71998000000001,41.17499,0 66.56247999999999,41.71693,0 66.52638000000002,42.00305000000001,0 66.02916999999999,42.00305000000001,0 66.03665,42.42332,0 66.07079,42.42387,0 66.21886000000001,42.42361,0 67.01609999999999,42.8661,0 67.82971000000001,43.24944,0 67.77831000000001,43.27805,0 67.8111,43.30249000000001,0 67.7961,43.33749,0 67.74274,43.33693,0 67.72638000000001,43.35305000000001,0 67.75694,43.36055,0 67.72026,43.37276,0 67.6797,43.30249000000001,0 67.66199,43.37128,0 67.84221000000001,43.58804,0 67.84666,43.74749,0 67.97387000000001,43.69555,0 67.99026000000001,43.80555,0 67.94193,43.90527,0 67.96387000000001,43.94972,0 68.08277,44.00528,0 68.03525999999999,44.06888000000001,0 67.82887000000001,44.15971,0 67.765,44.23277,0 67.79832,44.26417,0 67.47887,44.36361,0 67.50360000000001,44.40028,0 67.50499000000001,45.16194,0 67.36275999999999,45.17916,0 67.17833,45.29341,0 67.24942,45.45778000000001,0 67.25333000000001,45.58389,0 67.35831,45.7711,0 67.31441,45.84720999999999,0 67.18192999999999,45.9411,0 67.16988000000001,45.99897000000001,0 67.15776,46.12944000000001,0 67.12168,46.16551,0 66.53555,46.14277,0 65.62943,46.18166000000001,0 65.27638,46.36306,0 65.11913,46.48082000000002,0 64.97026,46.47276,0 64.38889,46.69665000000001,0 63.41805000000001,46.92028000000001,0 63.08611,47.08972,0 62.69305000000001,47.04257,0 62.85190999999999,47.13889,0 63.14249,47.285,0 63.19526000000001,47.34083,0 64.01609999999999,47.72165,0 64.28915000000001,47.88832,0 64.13443,47.98499,0 64.01220000000001,48.15694,0 63.91554,48.23388,0 63.76111,48.45082,0 63.47083,48.53583,0 63.41499000000001,48.59249,0 63.09416,48.67943000000001,0 63.05693,48.72249,0 63.10055000000001,48.86055,0 62.97610000000001,48.91194,0 62.84722,48.84332000000001,0 62.56527,49.01082,0 62.46249,49.17638000000001,0 62.33611,49.19971,0 62.35665000000001,49.25944,0 62.54499000000001,49.25111,0 62.35027,49.46944000000001,0 62.30305,49.48638,0 62.37360000000001,49.56833,0 62.48417,49.54805000000001,0 62.45444,49.67888,0 62.59694,49.77999000000001,0 62.57667000000001,49.85277,0 62.73861,49.94778,0 62.90444,49.89415000000001,0 62.94083000000001,49.99138,0 62.86415,50.09777,0 62.71027,50.2122,0 62.69833,50.28305000000001,0 62.50833000000001,50.46805,0 62.38249,50.7225,0 62.25444000000001,50.68249,0 62.18916000000001,50.71000000000001,0 62.23583,50.74138,0 62.19276,50.80443000000001,0 62.42528,50.85695,0 62.78388,50.86056,0 62.86833000000001,50.82110999999999,0 62.93999,50.86555,0 63.13193,50.82222,0 63.20193,50.8772,0 63.265,50.82638000000002,0 63.81416,50.93859,0 64.09942,51.03916,0 64.34915000000001,50.87749000000001,0 64.25888000000001,50.75054999999999,0 64.18747999999999,50.7336,0 64.05998,50.57749,0 64.22553000000001,50.51416,0 64.25,50.54055,0 64.36461,50.51109,0 64.38943,50.55166,0 64.47837,50.58194000000001,0 64.47248999999999,50.63304999999999,0 64.56219,50.67888,0 64.60415999999999,50.65138000000002,0 64.69275,50.6711,0 64.73027,50.73805,0 64.81386999999999,50.78638,0 64.91553999999999,51.00805,0 64.81944,51.07222000000001,0 64.84972000000001,51.09664999999999,0 64.91415000000001,51.13972,0 64.97776,51.10333,0 65.20583000000001,51.17332,0 65.29498,51.16471,0 65.40694000000001,51.23166,0 65.30359,51.28221,0 65.21608999999999,51.24888000000001,0 65.13164999999999,51.28139000000002,0 64.88415999999999,51.47027,0 64.95444000000001,51.52694,0 64.94247,51.61306,0 65.01138,51.63777,0 64.95775999999999,51.74943,0 65.01666,51.82332,0 65.00888000000001,51.89249,0 64.925,51.90889,0 64.80609,52.02555,0 64.80138,52.12444,0 64.74887,52.12832,0 64.75166,52.18166999999999,0 65.05859,52.23165,0 65.04498,52.28027,0 65.46082,52.29639,0 65.58472,52.32777,0 65.58414999999999,52.38972,0 66.02887,52.45304000000001,0 66.05247,52.8186,0 66.13443,52.81998999999999,0 66.14360000000001,52.88610000000001,0 66.40886999999999,52.90331,0 66.41277,52.95222,0 66.07777,52.96305000000001,0 66.08194,53.06277,0 66.20359999999999,53.10443,0 66.40526,53.09972000000001,0 66.41443,53.1686,0 66.26639,53.19388000000001,0 66.26888,53.28277,0 66.25526000000001,53.48555,0 66.11165,53.49276,0 66.11775,53.63082,0 66.18304000000001,53.68249000000001,0 66.16499,53.78111,0 66.06581,53.79277,0 66.07581999999999,53.91526000000001,0 66.03610000000002,53.91805,0 66.00139,53.99471,0 66.21442,54.07694999999999,0 66.35693000000001,54.08444,0 66.27858000000001,54.19028,0 66.20610000000001,54.18333000000001,0 66.11803999999999,54.2375,0 66.21944000000001,54.28055,0 66.15358999999999,54.38055,0 66.16277,54.41945,0 66.1236,54.44500000000001,0 66.15192,54.48555,0 66.11304,54.5425,0 66.10831,54.64500000000002,0 66.49525,54.72304,0 66.72275999999999,54.72694,0 66.80887,54.76666,0 67.24552,54.80665,0 67.32277000000001,54.86749,0 67.58138,54.85443,0 67.76639000000002,54.89194,0 67.83223,54.96332000000001,0 67.9297,54.98332,0 68.03442,54.95027,0 68.20526,54.96609,0 68.25943,55.04888,0 68.33083000000001,55.08056,0 68.24444,55.10471,0 68.19971,55.17833000000001,0 68.30443,55.20527,0 68.62581,55.20192999999999,0 68.63889,55.25555,0 68.70499000000001,55.28667,0 68.73137,55.37055,0 68.87997,55.34276,0 68.93332,55.28611,0 69.00249,55.28749,0 69.00109999999999,55.35361,0 68.93941,55.37028000000001,0 68.9233,55.42416,0 68.95247999999999,55.44471,0 69.16831999999999,55.40054,0 69.18552,55.34388000000001,0 69.23276,55.3325,0 69.35138000000001,55.39443000000001,0 69.67276,55.35443,0 69.84610000000001,55.29500000000001,0 69.94914,55.20416,0 70.05582,55.20776999999999,0 70.23888,55.13888,0 70.28804,55.18721,0 70.39833,55.20917,0 70.46998000000001,55.29667000000001,0 70.50666,55.33777000000001,0 70.46608999999999,55.40499000000001,0 70.48775999999999,55.43859000000001,0 70.54915,55.49138,0 70.69664,55.50638,0 70.7636,55.59778,0 70.54693,55.67499,0 70.58360999999999,55.7961,0 70.78554,55.83306,0 70.75915999999999,55.86332,0 70.59138,55.83611000000001,0 70.47527,55.85222000000001,0 70.47415,55.89166000000001,0 70.80415000000001,55.92028000000001,0 70.91638,56.11055,0 70.81749000000001,56.17333,0 70.84027,56.25695,0 70.77222,56.25471000000001,0 70.76277,56.28667000000001,0 70.93359,56.34694,0 70.94136,56.41167000000001,0 70.86248999999999,56.46389000000001,0 70.78471999999999,56.43665,0 70.75972,56.46111000000001,0 70.85776,56.50305,0 70.95416,56.4911,0 71.09081999999999,56.55555,0 71.11913,56.64555,0 71.03306000000001,56.69221000000001,0 71.05582,56.71028,0 71.16665999999999,56.67138,0 71.34887000000001,56.66916,0 71.39388,56.68472,0 71.39695,56.73305,0 71.54082,56.71915000000001,0 71.58553000000001,56.79445,0 71.67554,56.82638,0 71.65860000000001,56.91470000000001,0 71.61304,56.95693,0 71.37082,57.06360999999999,0 71.23387,57.20027,0 71.09027,57.22055,0 71.14776999999999,57.25139000000001,0 71.09248,57.28999,0 71.10248,57.34083,0 70.81165,57.32804,0 70.75417,57.27749,0 70.78194000000001,57.19916,0 70.69443,57.19305,0 70.60971000000001,57.19916,0 70.65609000000001,57.26416,0 70.51721000000001,57.28695,0 70.42498999999999,57.38916,0 70.68747999999999,57.51445,0 70.69108,57.56666,0 70.66721,57.59276,0 70.57720999999999,57.57861,0 70.57443000000001,57.70916,0 70.49887,57.7075,0 70.48387,57.76777,0 70.42554,57.76639,0 70.44171,57.83843000000001,0 70.37864999999999,57.91712,0 70.43691,57.9336,0 70.87302,58.5425,0 71.07888,58.5286,0 71.11998,58.44887,0 71.30387,58.39749000000001,0 71.24721,58.3561,0 71.21915,58.14695,0 71.24887,58.12194,0 71.23859,58.07111,0 71.98888,58.12471,0 72.07055,58.09415,0 72.12164,58.02304000000001,0 72.87221,58.0061,0 72.96915,58.06055,0 72.93858,58.08749000000001,0 73.08443,58.14916,0 74.20638,58.13361,0 74.45499,58.25861,0 74.62053,58.29361000000001,0 74.62665000000001,58.33915,0 74.79998999999999,58.42694,0 74.87608,58.42722,0 74.89166,58.47776,0 75.10526,58.58111,0 75.35554999999999,58.47277,0 75.05415000000001,58.3447,0 75.2397,58.21998,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +82.27669,74.95653,0 82.27669,73.92335,0 77.53673000000001,73.93346,0 72.53674,73.94411,0 67.53675,73.95477,0 67.53675,76.19176,0 68.31331,76.28333000000001,0 68.16943999999999,76.31805,0 68.33333,76.33582,0 68.43941,76.38972,0 68.40109,76.40609000000001,0 68.57971000000001,76.43304999999999,0 68.92554,76.55888,0 68.78471999999999,76.58778,0 68.85637,76.65665,0 69.03027000000002,76.70802999999999,0 68.90054000000001,76.72776,0 68.89361,76.75749,0 68.93137,76.78276,0 68.75443,76.82222,0 68.80942,76.86638000000002,0 68.47275999999999,76.92554,0 68.50082,76.95638,0 67.53675,77.00954,0 67.53675,81.66452,0 68.67328999999999,82.13631,0 73.29124,84.05318,0 77.9092,85.97008,0 82.52717,87.88696,0 82.52717,89.90000000000001,0 83.52695,89.90000000000001,0 84.52673000000002,89.90000000000001,0 85.52652000000001,89.90000000000001,0 86.52631000000001,89.90000000000001,0 87.52609,89.90000000000001,0 88.52588000000002,89.90000000000001,0 89.52567000000001,89.90000000000001,0 90.52546,89.90000000000001,0 91.52525,89.90000000000001,0 92.52502000000001,89.90000000000001,0 93.52481,89.90000000000001,0 94.52460000000001,89.90000000000001,0 95.52439,89.90000000000001,0 96.52417000000001,89.90000000000001,0 97.52396,89.90000000000001,0 97.52396,84.90001,0 97.52396,83.39419,0 92.52396,83.38905,0 87.52397000000001,83.3839,0 82.52397000000001,83.37876,0 82.30801,83.37854000000002,0 82.30801,78.37854,0 82.30801,76.45934,0 82.84025,76.36541,0 83.27856,76.17757,0 83.49773000000002,75.83316000000002,0 83.37249,75.30092999999999,0 82.90286,74.98784000000001,0 82.27669,74.95653,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +06:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(6.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0650</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +97.51384,-11.2595,0 97.51384,-13.93535,0 93.69904000000001,-13.93192,0 93.69904000000001,-11.2595,0 97.51384,-11.2595,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +97.56944,28.54888,0 97.63136,28.48416,0 97.71138000000001,28.51694,0 97.79694000000001,28.34916,0 97.90886999999999,28.37638,0 98.01888,28.27832,0 98.01304,28.21472,0 98.06944,28.21138,0 98.13621999999999,28.15632,0 98.15776,28.11333,0 98.13082,27.96916,0 98.20387000000001,27.91638,0 98.16692000000001,27.87055,0 98.23248,27.82471,0 98.22693000000002,27.73471,0 98.31638,27.54194,0 98.36887,27.52944,0 98.40860000000001,27.55750000000002,0 98.43248000000001,27.68944,0 98.52164999999999,27.64667,0 98.55110000000001,27.66693,0 98.59471000000002,27.59971000000001,0 98.66804999999999,27.61944,0 98.70471000000001,27.5825,0 98.68552,27.46611,0 98.73665,27.32917000000001,0 98.69166,27.20193,0 98.70694,27.09778,0 98.76221,27.06445,0 98.7397,26.86416,0 98.77777,26.79167,0 98.75555,26.72417,0 98.77554000000001,26.60583000000002,0 98.73748999999999,26.38694,0 98.66582,26.26056000000001,0 98.70110000000001,26.25195,0 98.73055000000002,26.17722,0 98.66443,26.09833,0 98.64888000000001,26.15166,0 98.56805,26.12306,0 98.60525,25.98861,0 98.64166,25.98444,0 98.71638000000002,25.87749,0 98.63193,25.80111,0 98.53083,25.84555,0 98.39166,25.65666,0 98.38305,25.59195,0 98.32665000000002,25.55167000000001,0 98.16526,25.62277,0 98.16804999999999,25.55027,0 98.12497999999999,25.50584,0 98.1386,25.38694,0 98.10746,25.38722,0 98.06944,25.31444,0 97.94108000000001,25.21416,0 97.83333,25.25916000000001,0 97.78943,25.1375,0 97.72331000000003,25.06944,0 97.72581,25.06472,0 97.73665000000003,24.91888,0 97.80025999999999,24.85139,0 97.68941000000001,24.83445,0 97.55247,24.74304,0 97.56248000000001,24.57111,0 97.53082000000001,24.44749,0 97.66526,24.44416,0 97.71776000000001,24.35833,0 97.66888,24.34166000000001,0 97.6647,24.3061,0 97.74165000000002,24.29027,0 97.75999000000002,24.2575,0 97.72749,24.22499,0 97.74970999999999,24.16999,0 97.72470000000001,24.11806,0 97.53637000000001,23.95056,0 97.64998,23.8517,0 97.78831,23.94888,0 97.85249000000002,23.95832,0 97.91249000000001,24.02666,0 98.06448,24.0891,0 98.22078000000001,24.12531,0 98.35732000000002,24.10319000000001,0 98.44659000000002,24.13496,0 98.54462,24.1257,0 98.59703,24.07648,0 98.89072,24.16008,0 98.89391000000001,24.09795,0 98.67720000000001,23.96805,0 98.70387000000002,23.95138,0 98.68858000000002,23.80139000000001,0 98.81554000000001,23.76722,0 98.81915000000001,23.70556,0 98.88582,23.61333000000001,0 98.80609000000001,23.54167,0 98.82471000000001,23.48083,0 98.87997,23.4836,0 98.91470000000001,23.41722,0 98.91859000000001,23.36472,0 98.87886,23.32972,0 98.94025000000002,23.31361,0 98.88136,23.18805,0 99.05888,23.1675,0 99.06137000000001,23.12361,0 99.11109999999999,23.10194,0 99.35638,23.13499,0 99.50388,23.08972,0 99.56638000000001,22.93805,0 99.51416,22.91222000000001,0 99.44553000000003,22.94972,0 99.44053,22.865,0 99.32805000000001,22.74472,0 99.38499,22.60333,0 99.38442000000001,22.50389,0 99.26082,22.39111,0 99.27054,22.34777,0 99.18221000000001,22.195,0 99.20055000000001,22.17333,0 99.16277,22.15916,0 99.23248,22.11444,0 99.35942,22.09306,0 99.46555000000002,22.13583,0 99.52164999999999,22.1,0 99.64444,22.09917,0 99.70914999999999,22.03972,0 99.74915,22.07527,0 99.84388,22.02332,0 99.88055,22.06145,0 99.96805000000002,22.05027,0 99.99054,21.95833,0 99.94193000000001,21.825,0 99.97360000000001,21.73388,0 100.0022,21.69611,0 100.125,21.69056000000001,0 100.1719,21.65388,0 100.1061,21.58333,0 100.1239,21.50472,0 100.2039,21.43535,0 100.3789,21.53889,0 100.43,21.52861,0 100.485,21.45833,0 100.6667,21.47943,0 100.8867,21.68333,0 101.1053,21.77138,0 101.1686,21.63028,0 101.1482,21.57264,0 101.1617,21.53056,0 100.9975,21.39583,0 100.8347,21.3025,0 100.7226,21.30771,0 100.6719,21.12806,0 100.6214,21.05167,0 100.5455,21.02527,0 100.52,20.94943,0 100.5255,20.87111,0 100.6336,20.89154,0 100.6472,20.87056,0 100.5319,20.80527,0 100.3689,20.82694,0 100.2539,20.74555,0 100.1753,20.63527,0 100.0914,20.34861,0 99.95914000000001,20.45527,0 99.86997000000001,20.42722,0 99.80914,20.33638,0 99.67360000000001,20.31083,0 99.45610000000002,20.38861,0 99.55525000000003,20.20667,0 99.53887,20.14943,0 99.32555000000001,20.06611,0 99.18388,20.12805,0 99.07639,20.0975,0 99.04082,20.03722,0 99.02970999999999,19.82888000000001,0 98.91388000000001,19.74638,0 98.83693000000002,19.81639,0 98.77554000000001,19.75889,0 98.64915000000001,19.74193,0 98.56165,19.67527,0 98.50971,19.71249,0 98.25360000000002,19.67305,0 98.23859,19.72222,0 98.04998999999999,19.8075,0 98.03498,19.64389,0 97.86908,19.57018,0 97.87665,19.49528,0 97.79248,19.40194,0 97.83720000000001,19.29888,0 97.78831,19.27695,0 97.84180000000002,19.2071,0 97.83638000000001,19.1011,0 97.6772,18.93416,0 97.74776,18.86556,0 97.7747,18.57,0 97.62415000000003,18.55445,0 97.52692,18.48917,0 97.44247000000001,18.49722,0 97.36229,18.57965,0 97.34749000000001,18.53889,0 97.49444000000001,18.2825,0 97.53580000000001,18.26861,0 97.56359999999999,18.32777,0 97.64305000000002,18.29027,0 97.61803999999999,18.23138,0 97.68665,18.14666,0 97.69136000000002,18.05806,0 97.75193000000003,17.97222,0 97.68665,17.86444,0 97.77442000000001,17.75083,0 97.77831999999999,17.70332,0 98.01470999999999,17.50639,0 98.05304,17.41583,0 98.11554,17.37611,0 98.11887,17.31388,0 98.24249000000002,17.21861,0 98.33582000000001,17.05556,0 98.42359,17.05333,0 98.48831,16.98194,0 98.54998999999999,16.85471,0 98.51972000000002,16.86777,0 98.54665,16.80777,0 98.47776000000002,16.73499,0 98.53360000000001,16.64167,0 98.58028,16.62027,0 98.58998,16.55333,0 98.66249000000001,16.46027,0 98.68970000000002,16.285,0 98.71581000000002,16.27638,0 98.81441,16.37582,0 98.86081,16.47527,0 98.86749000000003,16.43416,0 98.92638,16.39111,0 98.91859000000001,16.26861,0 98.86331,16.21471,0 98.85276000000003,16.13416000000001,0 98.80803000000002,16.10305,0 98.69275000000002,16.12832,0 98.63805000000002,16.04721,0 98.57249,16.05166,0 98.61749000000002,15.97,0 98.59055000000001,15.92389,0 98.60999,15.86695,0 98.56526000000001,15.72277,0 98.57557,15.66188,0 98.59636999999999,15.45834,0 98.56999,15.33667,0 98.49748,15.38778,0 98.42249,15.36,0 98.41361000000001,15.26,0 98.31026,15.30305,0 98.2664,15.23555,0 98.20139,15.22083,0 98.21721000000001,15.1775,0 98.18442,15.09916,0 98.23442,15.04695,0 98.21388,15.00333,0 98.25499000000002,14.92083,0 98.25999,14.81666,0 98.44775000000001,14.60583,0 98.4772,14.52805,0 98.50221000000003,14.53139,0 98.61691999999999,14.31916,0 98.73109,14.27777,0 98.75972000000003,14.2175,0 98.97165,14.08361,0 99.03276,13.93777,0 99.11942999999999,13.87861,0 99.12776,13.77361,0 99.18054000000001,13.7125,0 99.17054,13.57111,0 99.21720999999999,13.40583,0 99.21278000000001,13.21611,0 99.12082,13.17583,0 99.11219,13.05584,0 99.19166000000001,12.97444,0 99.17859000000001,12.9175,0 99.24526,12.72361,0 99.43623,12.57444,0 99.41249000000001,12.46639,0 99.49970999999999,12.18833,0 99.47331,12.13389,0 99.57332000000002,12.13666,0 99.54526000000001,12.04555,0 99.59444000000002,11.99695,0 99.58942999999999,11.8725,0 99.66165000000001,11.82694,0 99.63581000000001,11.73361,0 99.56193000000002,11.63056,0 99.46776,11.60972,0 99.46887,11.52,0 99.40470999999999,11.45166,0 99.40054000000001,11.38416,0 99.33028,11.32222,0 99.23750000000003,11.10945,0 99.10664,11.01,0 99.08167,10.95139,0 99.02027,10.96333,0 99.00694,10.83195,0 98.91304,10.80972,0 98.7872,10.68222,0 98.77388999999999,10.61695,0 98.82388,10.51666,0 98.74275,10.34861,0 98.55254000000001,9.983105999999999,0 98.53130000000002,9.91836,0 98.45473,9.958472,0 98.19220000000001,9.911070000000001,0 98.11197,9.688651999999999,0 97.9479,9.590201,0 97.9953,9.819918000000001,0 98.10104000000001,9.932957999999999,0 98.16302000000002,10.06057,0 98.14115,10.11526,0 98.07916,10.10797,0 97.97341,10.02046,0 97.93331000000001,10.07151,0 97.96977000000001,10.31945,0 97.87497000000001,10.3778,0 97.88226,10.50542,0 97.93696,10.57105,0 98.20678,10.69868,0 98.14843999999999,10.84087,0 98.09374,10.819,0 97.95882,10.85181,0 97.86767999999999,10.81534,0 97.87132,10.89922,0 97.89686,10.94297,0 98.29064000000001,11.04142,0 98.19584,11.3003,0 98.11562000000001,11.36958,0 97.95882,11.39511,0 97.95882,11.46803,0 98.04635000000002,11.61752,0 97.98801,11.6941,0 97.97707,11.83266,0 97.48117000000001,11.7488,0 97.42283000000001,11.81078,0 97.71453000000003,12.19182,0 97.75829,12.29392,0 97.80204999999999,12.458,0 97.82026999999999,12.82627,0 98.24323000000001,12.98124,0 98.06093,13.72507,0 97.89319999999999,13.76518,0 97.88955000000003,13.9475,0 97.76558,14.16262,0 97.79111,14.22826,0 97.75100000000001,14.40693,0 97.90778000000002,14.53273,0 97.78746000000001,14.84631,0 97.75100000000001,15.20729,0 97.70359999999999,15.28751,0 97.71453000000003,15.40054,0 97.63795,15.48805,0 97.63068,15.5938,0 97.69629999999999,15.69954,0 97.68902000000001,15.88368,0 97.56868,15.98212,0 97.56868,16.15714,0 97.45565000000001,16.31758,0 97.41919,16.4379,0 97.34990999999999,16.47072,0 97.31709000000001,16.60199,0 97.21863999999999,16.76607,0 97.16030000000001,16.80983,0 96.92329000000001,16.81347,0 96.83213000000002,16.76972,0 96.77744000000001,16.67857,0 96.60242000000001,16.52907,0 96.06641999999999,16.28112,0 95.97891000000001,16.19361,0 95.86951000000001,16.18266,0 95.7346,16.06963,0 95.70179000000002,15.95296,0 95.43562,15.71047,0 95.32623000000001,15.68495,0 94.71001000000001,15.79069,0 94.53134,15.94019,0 94.49487000000001,15.93654,0 94.41101000000001,15.80893,0 94.37090000000002,15.93654,0 94.33808000000001,15.95842,0 94.24328,15.92924,0 94.17400000000001,16.0277,0 94.23962000000002,16.54183,0 94.32715000000003,16.65121,0 94.35996000000002,16.89915,0 94.43653000000001,16.99761,0 94.43288000000001,17.19451,0 94.54227,17.3677,0 94.5204,17.71045,0 94.47664,17.7615,0 94.4183,17.9584,0 94.45112,18.07508,0 94.39643,18.20634,0 94.30162,18.22458,0 94.33079000000001,18.3595,0 94.15577,18.68948,0 94.08649,18.72959,0 93.99169000000001,18.7077,0 93.76926,18.59103,0 93.63070999999999,18.66396,0 93.46298000000003,18.87544,0 93.61612000000001,18.93379,0 93.65987,19.02129,0 93.46662000000001,19.33487,0 93.47028,19.42239,0 93.52132,19.47342,0 93.3937,19.72502,0 93.42287,19.80888,0 93.38276999999999,19.90186,0 93.33535999999999,19.93833,0 93.27337,19.93468,0 93.25879000000002,19.81435,0 93.16398000000001,19.78154,0 93.08742,19.81435,0 93.00718999999999,19.80706,0 92.85404,20.1097,0 92.76654000000001,20.17898,0 92.62797999999999,20.40141,0 92.37385999999999,20.70083,0 92.26194,21.05431,0 92.19859000000001,21.15417,0 92.19220000000001,21.32444,0 92.25305000000002,21.3725,0 92.26555000000002,21.42333,0 92.31667,21.42138,0 92.37691,21.47416,0 92.42747000000001,21.37472,0 92.55359,21.37361,0 92.59554000000001,21.25194,0 92.66935000000002,21.29698,0 92.60027000000001,21.63416,0 92.62164,21.87583,0 92.60081,21.98222000000001,0 92.67970000000001,22.02694,0 92.71192000000002,22.15972,0 92.86554,22.04888,0 92.89639,21.95472,0 92.94304000000001,22.02833,0 92.99638000000003,21.99278,0 93.00972000000003,22.1075,0 93.05276000000001,22.11889,0 93.04414000000001,22.20194,0 93.15387,22.18666,0 93.14249,22.23694,0 93.19664000000002,22.25638,0 93.18055,22.44055,0 93.11859,22.53333,0 93.13638,22.59444,0 93.09722000000001,22.66528,0 93.09822000000001,22.79037,0 93.16137000000001,22.91945,0 93.13805000000004,23.04805,0 93.20139,23.05889,0 93.22915999999999,23.01333,0 93.29359000000001,23.00889000000002,0 93.38666000000001,23.14027000000002,0 93.36609,23.34945,0 93.40331999999999,23.37806,0 93.43346,23.67889,0 93.40138,23.73582,0 93.38443,23.92138,0 93.32805000000001,23.98166,0 93.3279,24.03597,0 93.34207000000001,24.0818,0 93.38293,24.08935,0 93.49359,23.96277,0 93.59636999999999,23.97222,0 93.62136,24.01611000000001,0 93.75332,24.0075,0 93.81914999999999,23.93527,0 93.92666,23.95583,0 94.14444,23.84778,0 94.38915,24.48,0 94.51470999999999,24.61945,0 94.53859000000001,24.70888,0 94.59777,24.72528000000001,0 94.73442,25.02472,0 94.72859,25.13638,0 94.59943,25.17833,0 94.57639000000002,25.21444,0 94.62553000000001,25.39777,0 94.66165000000001,25.42804,0 94.88527000000001,25.57222000000001,0 94.99193,25.72916,0 95.03387000000002,25.74222,0 95.02222,25.92861,0 95.08665000000001,25.95556,0 95.17804,26.05889,0 95.11331,26.10916,0 95.12082,26.20166,0 95.08389000000003,26.22277,0 95.06526,26.32556,0 95.09526,26.41,0 95.06638,26.47972,0 95.14777000000001,26.56583,0 95.15248000000001,26.62333,0 95.24638,26.67972,0 95.2711,26.64472,0 95.44108,26.70916,0 95.51944,26.82306,0 95.61081,26.82139,0 95.65804000000001,26.89528,0 95.71775000000001,26.89138,0 95.80386,27.00111,0 95.93082,27.04611,0 96.03581,27.19249,0 96.19414999999999,27.27084,0 96.52415000000002,27.29416,0 96.60999,27.36694,0 96.66025999999999,27.33916,0 96.72915999999999,27.36555,0 96.88416000000001,27.25472,0 96.86137000000001,27.21028,0 96.88831999999999,27.17805,0 97.05974000000001,27.093,0 97.14444000000002,27.09249,0 97.15888,27.13861,0 97.12636999999999,27.19083,0 96.89999000000002,27.46861000000001,0 96.91804999999999,27.50527,0 96.88889,27.6175,0 97.04665,27.74972,0 97.08389,27.7486,0 97.24942,27.9075,0 97.29916,27.91667,0 97.31721,27.87833,0 97.36775,27.89666,0 97.39413999999999,28.01861,0 97.31608000000001,28.08916,0 97.34887000000002,28.22278,0 97.41054,28.2925,0 97.46887000000001,28.29611,0 97.47083000000001,28.39111,0 97.56944,28.54888,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +07:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(7);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0700</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +105.3275,23.37803,0 105.398,23.28167,0 105.4458,23.28445,0 105.5544,23.16028,0 105.5657,23.09678,0 105.5775,23.05916,0 105.7244,23.04805,0 105.8725,22.91027,0 105.9872,22.93583,0 106.0153,22.98472,0 106.1347,22.98861,0 106.2294,22.95083,0 106.25,22.87416,0 106.285,22.8586,0 106.4194,22.87056,0 106.5203,22.93528,0 106.6005,22.90917,0 106.648,22.85333,0 106.7072,22.865,0 106.7614,22.79471,0 106.8217,22.80028,0 106.7669,22.74249,0 106.7128,22.58666,0 106.6761,22.57277,0 106.6386,22.61416,0 106.6072,22.60695,0 106.5517,22.45682,0 106.5655,22.34583,0 106.6583,22.32444,0 106.6958,22.2225,0 106.6653,22.19944,0 106.7033,22.08972,0 106.6794,21.97389,0 106.7553,22.01527000000001,0 106.8086,21.97361,0 106.9017,21.97056,0 106.9327,21.92749,0 107.0292,21.93666,0 107.0491,21.90722,0 107.0203,21.8225,0 107.2208,21.70832,0 107.3014,21.73111,0 107.3627,21.60526,0 107.4578,21.65833,0 107.4858,21.59778,0 107.5361,21.58638,0 107.7831,21.66693,0 107.8586,21.65083,0 107.948,21.54195,0 107.99,21.54241,0 108.0824,21.49174,0 108.0038,21.44677,0 107.9893,21.3862,0 107.9865,18.50449,0 110.1489,14.71173,0 112.5137,14.71173,0 112.5076,9.711738,0 112.5034,6.222851,0 107.5034,6.229232,0 102.5034,6.235613,0 102.0952,6.236135,0 102.0916,6.110559,0 101.9483,5.969448,0 101.9438,5.861942,0 101.8339,5.743340000000001,0 101.7625,5.788889,0 101.6916,5.763608,0 101.6589,5.860557,0 101.57,5.916663,0 101.3516,5.808339,0 101.2897,5.810553,0 101.2561,5.705837,0 101.1458,5.631111,0 100.9902,5.788053,0 101.0358,5.896106,0 101.1014,5.92695,0 101.1194,5.984441,0 101.12,6.098337,0 101.0755,6.150555,0 101.1216,6.187221,0 101.1155,6.248888000000001,0 101.0333,6.243894,0 100.9897,6.277500000000001,0 100.9514,6.239717000000001,0 100.8575,6.234444,0 100.8336,6.419447,0 100.7655,6.451946,0 100.7542,6.501942,0 100.6611,6.4475,0 100.4989,6.523056000000001,0 100.3875,6.536106,0 100.3344,6.586946000000001,0 100.2939,6.703892,0 100.1958,6.702497,0 100.1714,6.476660000000001,0 100.1271,6.424946000000001,0 99.84238000000002,6.479837,0 99.63088000000001,6.427682,0 99.64923,6.352362,0 99.72359,6.164054,0 100.1668,5.375079,0 100.1688,5.261129,0 100.3311,5.074793,0 100.4195,4.901795,0 100.5095,4.824638,0 100.5673,4.663917,0 100.5031,3.976011000000001,0 100.9499,3.657770000000001,0 101.1781,3.345965,0 101.2874,3.114522,0 101.2167,3.040577,0 101.2038,2.93129,0 101.2906,2.815568,0 101.416,2.786633,0 101.5188,2.654846,0 101.7567,2.561625,0 101.8371,2.442689,0 101.8499,2.362329,0 101.9303,2.391254,0 102.1489,2.208024,0 102.3418,2.134089,0 102.5378,2.01195,0 102.6921,1.831931,0 102.8175,1.831931,0 102.9461,1.719422,0 103.2097,1.584413,0 103.2418,1.45583,0 103.4122,1.301534,0 103.5311,1.234025,0 103.7561,1.185812,0 103.9265,1.256534,0 104.3122,1.346534,0 105.132,2.002301,0 105.1191,4.869648,0 109.3237,4.869648,0 109.3237,1.966571,0 109.2409,1.725713,0 109.0775,1.576584,0 109.0278,1.491362,0 109.0278,1.363535,0 108.9071,1.1789,0 108.9071,1.086578,0 108.7438,0.8593374000000001,0 108.6657,0.8522364,0 108.6586,0.7457215000000002,0 108.829,0.4758655000000001,0 108.8929,0.2912305000000001,0 109.0775,0.2344225,0 109.113,0.02848446,0 109.0207,-0.2484725,0 109.106,-0.5467235,0 109.2622,-0.9018005000000001,0 109.4255,-1.022518,0 109.39,-1.285273,0 109.461,-1.349182,0 109.7664,-1.207152,0 110.0433,-1.363393,0 109.9865,-1.682947,0 109.8942,-1.746865,0 109.8729,-1.839177,0 109.9297,-1.917289,0 110.0504,-1.952803,0 110.093,-2.030923,0 110.0717,-2.258163,0 110.1143,-2.499607,0 110.0646,-2.925694000000001,0 110.1712,-3.032208,0 110.3061,-3.060612,0 110.5049,-2.996703999999999,0 110.6754,-3.138724,0 110.7819,-3.103219000000001,0 110.9097,-3.138724,0 111.0162,-3.124531,0 111.3358,-3.032208,0 111.7761,-3.074815,0 111.7903,-3.387277000000001,0 111.7335,-3.536407,0 111.7903,-3.600316,0 111.9465,-3.62872,0 112.2021,-3.4867,0 112.5075,-3.479589,0 112.5004,-5.205232,0 116.3209,-5.205232,0 116.3209,-7.548669,0 115.767,-7.548669,0 115.767,-9.636480000000001,0 112.5004,-9.636480000000001,0 112.5004,-14.63648,0 112.5004,-19.63648,0 112.5004,-24.63648,0 112.5004,-29.63648,0 112.5004,-34.63648,0 112.5004,-39.63649,0 112.5004,-39.73648000000001,0 112.5004,-44.73647,0 112.5004,-49.73648,0 112.5004,-54.73646999999999,0 112.5004,-59.43648,0 112.5004,-64.43649000000001,0 112.5004,-69.43648000000002,0 112.5004,-74.43648,0 112.5004,-79.43648,0 112.5004,-84.43648,0 112.5004,-89.43648,0 112.5004,-89.90000000000001,0 111.5013,-89.90000000000001,0 110.5022,-89.90000000000001,0 109.5031,-89.90000000000001,0 108.504,-89.90000000000001,0 107.5049,-89.90000000000001,0 106.5058,-89.90000000000001,0 105.5067,-89.90000000000001,0 104.5076,-89.90000000000001,0 103.5085,-89.90000000000001,0 102.5094,-89.90000000000001,0 101.5103,-89.90000000000001,0 100.5111,-89.90000000000001,0 99.51205,-89.90000000000001,0 98.51294,-89.90000000000001,0 97.51384,-89.90000000000001,0 97.51384,-84.89999,0 97.51384,-79.90000000000001,0 97.51384,-74.90000000000001,0 97.51384,-69.90000000000001,0 97.51384,-64.90000000000001,0 97.51384,-63.73534,0 97.51384,-58.73533,0 97.51384,-53.73534000000002,0 97.51384,-48.73533,0 97.51384,-44.73534,0 97.51384,-39.73533000000001,0 97.51384,-34.73534,0 97.51384,-29.73533,0 97.51384,-24.73534,0 97.51384,-19.73534,0 97.51384,-14.73534,0 97.51384,-13.93535,0 97.51384,-11.2595,0 97.51384,-6.259501,0 97.51384,-1.259497,0 97.51384,-0.5628515,0 94.60397,3.503177,0 92.9025,5.880672000000001,0 94.5928,5.880672000000001,0 94.59142,10.23029,0 94.58984000000001,15.23029,0 90.33015,15.23029,0 90.32641000000001,18.56566,0 92.31895,20.729,0 92.33333,20.76416,0 92.26331999999999,20.91916000000001,0 92.26194,21.05431,0 92.37385999999999,20.70083,0 92.62797999999999,20.40141,0 92.76654000000001,20.17898,0 92.85404,20.1097,0 93.00718999999999,19.80706,0 93.08742,19.81435,0 93.16398000000001,19.78154,0 93.25879000000002,19.81435,0 93.27337,19.93468,0 93.33535999999999,19.93833,0 93.38276999999999,19.90186,0 93.42287,19.80888,0 93.3937,19.72502,0 93.52132,19.47342,0 93.47028,19.42239,0 93.46662000000001,19.33487,0 93.65987,19.02129,0 93.61612000000001,18.93379,0 93.46298000000003,18.87544,0 93.63070999999999,18.66396,0 93.76926,18.59103,0 93.99169000000001,18.7077,0 94.08649,18.72959,0 94.15577,18.68948,0 94.33079000000001,18.3595,0 94.30162,18.22458,0 94.39643,18.20634,0 94.45112,18.07508,0 94.4183,17.9584,0 94.47664,17.7615,0 94.5204,17.71045,0 94.54227,17.3677,0 94.43288000000001,17.19451,0 94.43653000000001,16.99761,0 94.35996000000002,16.89915,0 94.32715000000003,16.65121,0 94.23962000000002,16.54183,0 94.17400000000001,16.0277,0 94.24328,15.92924,0 94.33808000000001,15.95842,0 94.37090000000002,15.93654,0 94.41101000000001,15.80893,0 94.49487000000001,15.93654,0 94.53134,15.94019,0 94.71001000000001,15.79069,0 95.32623000000001,15.68495,0 95.43562,15.71047,0 95.70179000000002,15.95296,0 95.7346,16.06963,0 95.86951000000001,16.18266,0 95.97891000000001,16.19361,0 96.06641999999999,16.28112,0 96.60242000000001,16.52907,0 96.77744000000001,16.67857,0 96.83213000000002,16.76972,0 96.92329000000001,16.81347,0 97.16030000000001,16.80983,0 97.21863999999999,16.76607,0 97.31709000000001,16.60199,0 97.34990999999999,16.47072,0 97.41919,16.4379,0 97.45565000000001,16.31758,0 97.56868,16.15714,0 97.56868,15.98212,0 97.68902000000001,15.88368,0 97.69629999999999,15.69954,0 97.63068,15.5938,0 97.63795,15.48805,0 97.71453000000003,15.40054,0 97.70359999999999,15.28751,0 97.75100000000001,15.20729,0 97.78746000000001,14.84631,0 97.90778000000002,14.53273,0 97.75100000000001,14.40693,0 97.79111,14.22826,0 97.76558,14.16262,0 97.88955000000003,13.9475,0 97.89319999999999,13.76518,0 98.06093,13.72507,0 98.24323000000001,12.98124,0 97.82026999999999,12.82628,0 97.80204999999999,12.458,0 97.75829,12.29392,0 97.71453000000003,12.19182,0 97.42283000000001,11.81078,0 97.48117000000001,11.7488,0 97.97707,11.83266,0 97.98801,11.6941,0 98.04635000000002,11.61752,0 97.95882,11.46803,0 97.95882,11.39511,0 98.11562000000001,11.36958,0 98.19584,11.3003,0 98.29064000000001,11.04142,0 97.89686,10.94297,0 97.87132,10.89922,0 97.86767999999999,10.81534,0 97.95882,10.85181,0 98.09374,10.819,0 98.14843999999999,10.84087,0 98.20678,10.69868,0 97.93696,10.57105,0 97.88226,10.50542,0 97.87497000000001,10.3778,0 97.96977000000001,10.31945,0 97.93331000000001,10.07151,0 97.97341,10.02046,0 98.07916,10.10797,0 98.14115,10.11526,0 98.16302000000002,10.06057,0 98.10104000000001,9.932957999999999,0 97.9953,9.819918000000001,0 97.9479,9.590201,0 98.11197,9.688651999999999,0 98.19220000000001,9.911070000000001,0 98.45473,9.958472,0 98.53130000000002,9.91836,0 98.55254000000001,9.983105999999999,0 98.74275,10.34861,0 98.82388,10.51666,0 98.77388999999999,10.61695,0 98.7872,10.68222,0 98.91304,10.80972,0 99.00694,10.83195,0 99.02027,10.96333,0 99.08167,10.95139,0 99.10664,11.01,0 99.23750000000003,11.10945,0 99.33028,11.32222,0 99.40054000000001,11.38416,0 99.40470999999999,11.45166,0 99.46887,11.52,0 99.46776,11.60972,0 99.56193000000002,11.63056,0 99.63581000000001,11.73361,0 99.66165000000001,11.82694,0 99.58942999999999,11.8725,0 99.59444000000002,11.99695,0 99.54526000000001,12.04555,0 99.57332000000002,12.13666,0 99.47331,12.13389,0 99.49970999999999,12.18833,0 99.41249000000001,12.46639,0 99.43623,12.57444,0 99.24526,12.72361,0 99.17859000000001,12.9175,0 99.19166000000001,12.97444,0 99.11219,13.05584,0 99.12082,13.17583,0 99.21278000000001,13.21611,0 99.21720999999999,13.40583,0 99.17054,13.57111,0 99.18054000000001,13.7125,0 99.12776,13.77361,0 99.11942999999999,13.87861,0 99.03276,13.93777,0 98.97165,14.08361,0 98.75972000000003,14.2175,0 98.73109,14.27777,0 98.61691999999999,14.31916,0 98.50221000000003,14.53139,0 98.4772,14.52805,0 98.44775000000001,14.60583,0 98.25999,14.81666,0 98.25499000000002,14.92083,0 98.21388,15.00333,0 98.23442,15.04695,0 98.18442,15.09916,0 98.21721000000001,15.1775,0 98.20139,15.22083,0 98.2664,15.23555,0 98.31026,15.30305,0 98.41361000000001,15.26,0 98.42249,15.36,0 98.49748,15.38778,0 98.56999,15.33667,0 98.59636999999999,15.45834,0 98.57557,15.66188,0 98.56526000000001,15.72277,0 98.60999,15.86695,0 98.59055000000001,15.92389,0 98.61749000000002,15.97,0 98.57249,16.05166,0 98.63805000000002,16.04721,0 98.69275000000002,16.12832,0 98.80803000000002,16.10305,0 98.85276000000003,16.13416000000001,0 98.86331,16.21471,0 98.91859000000001,16.26861,0 98.92638,16.39111,0 98.86749000000003,16.43416,0 98.86081,16.47527,0 98.81441,16.37582,0 98.71581000000002,16.27638,0 98.68970000000002,16.285,0 98.66249000000001,16.46027,0 98.58998,16.55333,0 98.58028,16.62027,0 98.53360000000001,16.64167,0 98.47776000000002,16.73499,0 98.54665,16.80777,0 98.51972000000002,16.86777,0 98.54998999999999,16.85471,0 98.48831,16.98194,0 98.42359,17.05333,0 98.33582000000001,17.05556,0 98.24249000000002,17.21861,0 98.11887,17.31388,0 98.11554,17.37611,0 98.05304,17.41583,0 98.01470999999999,17.50639,0 97.77831999999999,17.70332,0 97.77442000000001,17.75083,0 97.68665,17.86444,0 97.75193000000003,17.97222,0 97.69136000000002,18.05806,0 97.68665,18.14666,0 97.61803999999999,18.23138,0 97.64305000000002,18.29027,0 97.56359999999999,18.32777,0 97.53580000000001,18.26861,0 97.49444000000001,18.2825,0 97.34749000000001,18.53889,0 97.36229,18.57965,0 97.44247000000001,18.49722,0 97.52692,18.48917,0 97.62415000000003,18.55445,0 97.7747,18.57,0 97.74776,18.86556,0 97.6772,18.93416,0 97.83638000000001,19.1011,0 97.84180000000002,19.2071,0 97.78831,19.27695,0 97.83720000000001,19.29888,0 97.79248,19.40194,0 97.87665,19.49528,0 97.86908,19.57018,0 98.03498,19.64389,0 98.04998999999999,19.8075,0 98.23859,19.72222,0 98.25360000000002,19.67305,0 98.50971,19.71249,0 98.56165,19.67527,0 98.64915000000001,19.74193,0 98.77554000000001,19.75889,0 98.83693000000002,19.81639,0 98.91388000000001,19.74638,0 99.02970999999999,19.82888000000001,0 99.04082,20.03722,0 99.07639,20.0975,0 99.18388,20.12805,0 99.32555000000001,20.06611,0 99.53887,20.14943,0 99.55525000000003,20.20667,0 99.45610000000002,20.38861,0 99.67360000000001,20.31083,0 99.80914,20.33638,0 99.86997000000001,20.42722,0 99.95914000000001,20.45527,0 100.0914,20.34861,0 100.1753,20.63527,0 100.2539,20.74555,0 100.3689,20.82694,0 100.5319,20.80527,0 100.6472,20.87056,0 100.6336,20.89154,0 100.5255,20.87111,0 100.52,20.94943,0 100.5455,21.02527,0 100.6214,21.05167,0 100.6719,21.12806,0 100.7226,21.30771,0 100.8347,21.3025,0 100.9975,21.39583,0 101.1617,21.53056,0 101.1482,21.57264,0 101.2164,21.54556,0 101.1936,21.41277,0 101.2653,21.37611,0 101.2283,21.31222,0 101.2819,21.18027,0 101.5686,21.24749,0 101.6153,21.22833,0 101.6075,21.18027,0 101.67,21.19971,0 101.73,21.13972,0 101.7872,21.14417,0 101.7822,21.20693,0 101.838,21.21111,0 101.8514,21.24916,0 101.7392,21.31639,0 101.7489,21.48194,0 101.7775,21.515,0 101.763,21.57833,0 101.8331,21.62138,0 101.7831,21.65083,0 101.7525,21.72443,0 101.7847,21.825,0 101.7086,21.92889,0 101.6255,21.97193,0 101.5744,22.10917,0 101.5969,22.16166,0 101.5389,22.24417,0 101.57,22.27917,0 101.6269,22.28166,0 101.6805,22.37917,0 101.6719,22.44639000000001,0 101.738,22.49694,0 101.8,22.48943,0 101.8755,22.38305,0 101.92,22.43611,0 102.0308,22.45527,0 102.1147,22.43389000000001,0 102.1407,22.39628,0 102.1867,22.43,0 102.2533,22.41471,0 102.2697,22.49166,0 102.4164,22.63583,0 102.383,22.67749,0 102.4261,22.68832,0 102.4797,22.77389,0 102.5428,22.74889,0 102.5603,22.69749,0 102.6103,22.72278,0 102.8628,22.60139,0 102.9286,22.48194000000001,0 103.0305,22.43555,0 103.0725,22.44749,0 103.0761,22.49833,0 103.1708,22.54694,0 103.1556,22.58666,0 103.1839,22.62888,0 103.28,22.67304,0 103.3289,22.79500000000001,0 103.4305,22.74388,0 103.4569,22.64249,0 103.5224,22.58437,0 103.5842,22.64416,0 103.5636,22.70138,0 103.6178,22.78,0 103.6553,22.7825,0 103.9644,22.49911,0 104.0097,22.52424,0 104.0331,22.68527,0 104.1116,22.79972,0 104.2672,22.82919,0 104.2619,22.73777,0 104.37,22.68583,0 104.5836,22.84472,0 104.6044,22.80972,0 104.7314,22.81666,0 104.8611,22.93666,0 104.825,23.10611,0 104.8763,23.11084,0 104.9075,23.18027,0 104.9619,23.16556,0 105.0811,23.24222,0 105.2405,23.26055,0 105.2647,23.33416,0 105.3275,23.37803,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +111.0883,72.37220000000001,0 111.3592,72.31693,0 111.5806,72.31944,0 111.6686,72.26582999999999,0 111.6242,72.21361,0 111.7436,72.20721,0 111.775,72.14499000000001,0 112.0122,72.13332,0 112.0111,71.40694000000001,0 112.333,71.34333,0 112.5128,71.26582999999999,0 112.6428,71.2897,0 112.7258,71.24359,0 112.6869,71.20277,0 112.5219,71.16388000000001,0 112.5975,71.10471,0 112.385,71.0647,0 112.2561,71.00000000000001,0 112.0775,71.06164,0 111.9772,70.98276,0 111.4647,70.93864000000001,0 111.3625,70.85526,0 111.0057,70.84365,0 110.8808,70.79638000000001,0 110.7223,70.82165999999999,0 110.5383,70.79721000000001,0 110.5031,70.77083,0 110.5136,70.71693000000001,0 110.3236,70.70471000000001,0 110.1236,70.63612000000001,0 110.1625,70.56554,0 110.1594,70.55247,0 110.0989,70.55776,0 110.1461,70.52388000000001,0 110.1405,70.47859,0 110.3036,70.44942,0 110.2725,70.41721000000001,0 109.6417,70.38499,0 109.5817,70.35526000000002,0 109.633,70.31972,0 109.5695,70.265,0 109.3205,70.21887,0 109.3055,70.17776000000001,0 109.3828,70.11942999999999,0 109.3811,70.07944000000001,0 109.5439,70.03498,0 109.3836,69.92944,0 109.4211,69.89276,0 109.3644,69.83832,0 109.2261,69.77164999999999,0 108.9539,69.78526000000001,0 108.8725,69.84109000000001,0 108.2366,69.85444,0 108.0536,69.77970999999999,0 108.0181,69.70749000000001,0 107.9022,69.67498999999999,0 107.6053,69.6636,0 106.8147,69.51361,0 106.5911,69.51666,0 106.4547,69.57249,0 106.2853,69.45833,0 106.1367,69.41971,0 106.8908,68.86026,0 106.8766,67.84166999999999,0 106.7989,67.33832,0 106.6678,67.24387,0 106.5036,67.28637000000001,0 106.2325,67.18498,0 106.0539,67.16804999999999,0 105.9955,67.07887,0 105.9053,67.04553,0 105.5283,67.02222,0 105.6436,66.94664,0 105.8189,66.94859,0 105.8686,66.91804999999999,0 106.0683,66.89525999999999,0 106.075,66.86081,0 105.9889,66.79776000000001,0 106.1875,66.80247,0 106.1997,66.78333000000001,0 106.1361,66.7561,0 106.1461,66.72693,0 106.26,66.69942,0 106.2272,66.66052999999999,0 106.2417,66.58167,0 106.3058,66.55359000000001,0 106.2939,66.50221000000001,0 106.3269,66.47553000000001,0 106.1008,66.37803,0 106.1489,66.25776999999999,0 106.1158,66.19859,0 106.1833,66.15555999999999,0 106.353,66.15082,0 106.4889,66.00471,0 106.4667,65.93192999999999,0 106.4961,65.895,0 106.4483,65.82138000000001,0 106.4764,65.80443,0 106.448,65.77415000000001,0 106.4711,65.74108,0 106.4153,65.70139,0 106.4214,65.66692999999999,0 106.6439,65.6322,0 106.6219,65.61053,0 106.715,65.59305999999999,0 106.6989,65.55109,0 106.9647,65.50443,0 106.8761,65.44108,0 106.8414,65.37608,0 106.6116,65.39276,0 106.4422,65.30888,0 106.4269,65.26138,0 106.5117,65.20387,0 106.3736,65.20499,0 106.2911,65.13889,0 106.1925,65.12858,0 106.1658,65.09027,0 105.9986,65.03915000000001,0 105.9748,65.00305,0 106.2233,64.85722,0 106.1058,64.85915,0 106.0408,64.90470999999999,0 105.9491,64.89499000000001,0 106.0097,64.81805,0 105.898,64.82138000000001,0 105.8472,64.79165999999999,0 105.8289,64.74193,0 105.8772,64.65804,0 105.7039,64.65886999999999,0 105.7494,64.61443,0 105.7261,64.57222,0 105.8042,64.53055000000001,0 105.7925,64.47803999999999,0 106.0389,64.51609999999999,0 106.1475,64.48526,0 106.0661,64.42081,0 106.1867,64.39639,0 106.3944,64.44331,0 106.5858,64.44443,0 106.5655,64.49582,0 106.6042,64.51331999999999,0 106.6733,64.49802,0 106.7533,64.41193,0 106.8864,64.41609,0 107.1022,64.33693,0 107.2342,64.33138,0 107.2366,64.28693,0 107.3344,64.24442999999999,0 107.5947,64.29915,0 107.8508,64.18053999999999,0 107.9933,64.16971,0 108.0265,64.22346,0 108.1253,64.24054,0 108.15,64.24082,0 108.2211,64.20833,0 108.3222,64.03943,0 108.2619,63.99083000000002,0 107.6792,63.98999,0 107.6194,63.9575,0 107.6219,63.91361000000001,0 107.5628,63.85695000000001,0 107.3592,63.86775999999999,0 107.2911,63.95083,0 107.1819,63.95361,0 107.0714,63.88528,0 106.8603,63.97721,0 106.7028,63.98555000000002,0 106.6803,63.92499,0 106.7972,63.84415,0 106.6283,63.69833,0 106.685,63.61361000000001,0 106.5069,63.49166000000002,0 106.7364,63.3686,0 106.6741,63.29667,0 106.4441,63.33111,0 106.3619,63.27139,0 106.3583,63.21999,0 106.433,63.20055,0 106.3836,63.1461,0 106.4108,63.11527000000001,0 106.3033,63.07277,0 106.1216,63.05833000000001,0 106.1875,62.98611,0 106.1561,62.9586,0 106.2194,62.94194000000001,0 106.2161,62.8961,0 106.4447,62.84361,0 106.4678,62.77694,0 106.5258,62.74722000000001,0 106.4691,62.68166000000001,0 106.8144,62.59972,0 106.7072,62.58277,0 106.6533,62.53833,0 106.7144,62.47721000000001,0 106.5308,62.37249000000001,0 106.4275,62.38388,0 106.4792,62.31721000000002,0 106.3939,62.31777000000001,0 106.3192,62.26194000000001,0 106.4477,62.24167,0 106.4475,62.18581,0 106.373,62.10277,0 106.4097,62.08472,0 106.3789,62.06528,0 106.4133,62.02750000000001,0 106.2,61.99749000000001,0 106.0478,61.9086,0 106.0431,61.90277,0 105.8894,61.82305000000001,0 105.9491,61.68333,0 105.8647,61.67389,0 105.8028,61.61776,0 105.6947,61.64943,0 105.5925,61.61721,0 105.5575,61.56554000000001,0 105.3664,61.53278,0 105.2736,61.47805000000002,0 105.2758,61.41999,0 105,61.40661000000001,0 104.785,61.34943,0 104.9272,61.31944,0 104.9314,61.26944,0 104.9628,61.26498999999999,0 105.0119,61.17749,0 104.835,61.17776000000001,0 104.7103,61.08999000000001,0 104.6147,61.07444,0 104.6003,61.05416,0 104.6419,61.03804,0 104.6,61.00054,0 104.6144,60.97639,0 104.5597,60.94554000000001,0 104.6344,60.89388,0 104.4569,60.83972,0 104.6022,60.74333000000001,0 104.5167,60.63332,0 104.5577,60.61721,0 104.568,60.54084,0 104.6414,60.49416000000002,0 104.6764,60.42499999999999,0 104.6539,60.40249,0 104.7642,60.38221,0 104.8772,60.30388000000001,0 105.2219,60.27528,0 105.2967,60.29860999999999,0 105.3758,60.24917000000001,0 105.4772,60.25443,0 105.4786,60.19249,0 105.4208,60.10999000000001,0 105.4889,60.01194000000002,0 105.4305,59.89388,0 105.4952,59.84554000000001,0 105.3783,59.82083000000001,0 105.1853,59.86027000000001,0 105.0191,59.83721,0 105.0191,59.77083,0 104.8922,59.75027000000001,0 104.7944,59.69332,0 104.8005,59.6361,0 104.8769,59.58582,0 104.8561,59.55110000000001,0 104.8828,59.52527,0 104.9853,59.46028,0 105.183,59.47305,0 105.2308,59.42527000000001,0 105.2136,59.40777,0 105.2094,59.40192,0 105.2455,59.35082,0 105.1841,59.31055000000001,0 105.2961,59.26111,0 105.3319,59.1986,0 105.3019,59.14861000000001,0 105.3628,59.09499,0 105.1439,59.04582,0 105.085,58.99277,0 104.8386,58.89443,0 104.7558,58.81526000000001,0 104.8733,58.72164000000001,0 104.795,58.66859,0 104.6314,58.64804,0 104.5828,58.61415,0 104.5233,58.63832000000001,0 104.4583,58.60054,0 104.3936,58.64526,0 104.3991,58.69915000000001,0 104.2836,58.71305000000001,0 104.2364,58.75866000000001,0 104.0444,58.77304,0 103.9922,58.73555,0 103.8558,58.73498000000001,0 103.9458,58.76582,0 103.9542,58.84859,0 103.8933,58.90555,0 103.5928,58.91304,0 103.5919,58.95193,0 103.5383,58.98611,0 103.3892,59.02138000000002,0 103.2444,59.17944,0 103.2366,59.2286,0 103.338,59.22721,0 103.3264,59.26082000000002,0 103.2064,59.28972,0 103.1644,59.25999000000001,0 103.0822,59.26416,0 103.0155,59.32193,0 102.8717,59.26527999999999,0 102.8286,59.21194,0 102.5353,59.19166000000001,0 102.4433,59.14471,0 102.4044,59.07249000000001,0 102.4508,59.00111,0 102.4369,58.94304000000001,0 102.54,58.89777000000001,0 102.5669,58.79694,0 102.5114,58.71667,0 102.3105,58.64527,0 102.1944,58.52527000000001,0 101.685,58.41888,0 101.5347,58.22471,0 101.2764,58.21861000000001,0 101.2161,58.14444,0 101.1097,58.11528000000001,0 101.105,58.10528,0 101.1608,58.06055,0 101.1583,58.02471000000001,0 101.1394,57.98249,0 101.0281,57.92249000000002,0 101.0353,57.89804000000001,0 100.953,57.86694000000001,0 100.8077,57.88611,0 100.8505,57.84222,0 100.84,57.80083000000001,0 100.8911,57.77361,0 100.7969,57.66833000000001,0 100.8461,57.60999000000001,0 100.9536,57.5611,0 100.9391,57.51583,0 100.9628,57.50083000000001,0 100.9112,57.44835,0 100.855,57.465,0 100.7419,57.40193000000001,0 100.6505,57.47444,0 100.4036,57.54804,0 100.3958,57.59887,0 100.3461,57.63833,0 100.3664,57.67556,0 100.328,57.68250000000001,0 100.373,57.7311,0 100.2664,57.76139,0 100.2744,57.80915,0 100.123,57.86554,0 100.0767,57.96111,0 100.0775,58.0561,0 100.0441,58.07944,0 99.80138000000001,58.05721,0 99.68193000000001,58.08111,0 99.61582,58.04805000000001,0 99.61971,58.00166,0 99.49442999999999,57.93415,0 99.42165000000001,57.79333000000001,0 99.08414999999999,57.77416,0 98.97415,57.72165,0 98.85415999999999,57.72804,0 98.76944,57.79166000000001,0 97.91721,57.80694,0 97.34009000000002,57.04317,0 97.47693000000001,56.96444,0 97.62302,56.95528000000001,0 97.48055000000001,56.89111000000001,0 97.48581000000003,56.81028,0 97.77609,56.78054,0 97.75027000000002,56.70417000000001,0 97.78581,56.66415000000001,0 97.75193000000003,56.64054,0 97.77528000000002,56.6075,0 97.74498,56.55860000000001,0 97.87943,56.55944,0 97.88193,56.39249000000002,0 97.55663000000001,56.3875,0 97.58693000000001,56.18943000000001,0 97.42859,56.14666,0 97.43248,56.09583000000001,0 97.35220000000001,56.04861,0 97.12361,56.11639,0 97.0611,56.02556,0 96.98054999999999,56.02221999999999,0 96.96971000000001,55.85583,0 96.82111,55.79166,0 96.75526000000002,55.74722,0 96.82471,55.69749,0 96.74638,55.65138000000001,0 96.85582000000001,55.59444000000001,0 96.81721,55.55915,0 96.85526000000002,55.47776,0 96.84137000000001,55.43749,0 96.92027,55.32472,0 96.70055000000001,55.25528,0 96.69721,55.17276,0 96.65192,55.0986,0 96.68692,55.08083,0 96.57777,55.02028,0 96.71527,54.92138000000001,0 96.67359999999999,54.80222000000001,0 96.56192,54.69694,0 96.58888,54.65972,0 96.55053,54.58082,0 96.57083,54.51250000000001,0 96.31582,54.57139000000001,0 96.21720999999999,54.54084,0 96.05582,54.5761,0 95.97638000000001,54.47721000000001,0 95.98665,54.44833,0 95.91577000000002,54.3956,0 95.79082,54.42082000000001,0 95.70526,54.37999,0 95.66303000000004,54.33999,0 95.65860000000002,54.25943,0 95.91805000000002,54.15999,0 95.98803000000001,54.08721,0 96.05332,54.09389000000002,0 96.08167000000003,54.05749000000001,0 96.04943,53.99554,0 96.20721,53.92638,0 96.5,53.68499000000001,0 96.57165999999999,53.64554,0 96.60860000000001,53.67583,0 96.68415,53.6386,0 96.72776,53.70665,0 96.88666000000001,53.72887000000001,0 97.12275,53.60944,0 97.33221,53.59999,0 97.44107,53.46027000000002,0 97.58749,53.43888000000001,0 97.59972000000001,53.37138,0 97.96304000000001,53.35999,0 97.96998000000002,53.29639000000001,0 98.03027,53.25054000000001,0 98.28581,53.22721,0 98.29998999999999,53.10055,0 98.60249000000002,53.10139000000001,0 98.68637000000001,53.15611000000001,0 98.8772,53.11944,0 98.92360000000001,53.15388,0 99.06526000000001,53.10249,0 98.98553,53.03444,0 99.24997000000001,52.95378,0 99.20610000000002,52.8511,0 98.93107999999999,52.925,0 98.84444000000001,52.83194,0 98.86526000000001,52.79167,0 98.9408,52.79722000000001,0 98.93248000000001,52.65444000000001,0 98.80415000000001,52.61166,0 98.78276,52.57333,0 98.82721000000001,52.53999000000001,0 98.81527,52.52054,0 98.73522000000001,52.5118,0 98.72083000000001,52.46054000000001,0 98.62553,52.42416,0 98.67970000000001,52.36999,0 98.64444,52.26167000000001,0 98.72248999999999,52.29527000000001,0 98.80721,52.27444,0 98.81360000000001,52.18693,0 98.91304,52.2011,0 98.94189,52.14068,0 98.88109,52.11416,0 98.86942999999999,52.03861,0 98.70804000000001,51.82805,0 98.62442,51.77999000000001,0 98.33527000000001,51.71804000000001,0 98.24802,51.5811,0 98.25999,51.51583000000002,0 98.23235,51.45989000000001,0 98.06469000000001,51.46694,0 97.93777,51.31249,0 97.9586,51.22332,0 97.83195000000001,50.98999000000001,0 97.87747,50.92972,0 98.01304,50.85583,0 97.96388,50.77832,0 98.06331000000002,50.63054000000001,0 98.16748,50.56889,0 98.27443,50.56055,0 98.32555000000001,50.51083000000001,0 98.28554,50.41361000000001,0 98.29221,50.29861,0 98.10721000000002,50.04888,0 97.86388000000001,49.93277,0 97.78609,49.96915,0 97.59415000000001,49.92749,0 97.58638000000001,49.84361000000001,0 97.31303,49.7311,0 97.16943000000001,49.76167,0 96.99693000000002,49.89415000000001,0 96.72248999999999,49.9211,0 96.59248000000001,49.86500000000001,0 96.54749,49.92583,0 96.37916,49.89360000000001,0 96.29443999999999,49.94665000000001,0 96.07444,50.00694,0 95.96026999999999,49.94415000000001,0 95.90721000000002,50.01415999999999,0 95.83472,50.02499000000001,0 95.73526,49.95471000000001,0 95.59193000000001,49.94083,0 95.53859,49.89472,0 95.49157,49.9077,0 95.42249000000001,49.95610000000001,0 95.06608,49.96193000000001,0 94.97609,50.0561,0 94.62276,50.02999,0 94.51582999999999,50.16109,0 94.38998000000001,50.21444000000001,0 94.3522,50.41499,0 94.27499,50.56944,0 93.55832000000001,50.58972000000001,0 93.44886,50.62249000000001,0 93.03333000000001,50.6086,0 92.97102,50.66649,0 92.99915,50.7514,0 92.97500000000001,50.7911,0 92.80276000000001,50.79361,0 92.76028,50.71249,0 92.66081,50.68166,0 92.5736,50.75778,0 92.47275999999999,50.78527,0 92.38361000000002,50.87471000000001,0 92.32693,50.855,0 92.29498,50.75361,0 92.22137000000002,50.70556,0 91.90556000000002,50.71472000000001,0 91.77222000000002,50.68999000000001,0 91.65886999999999,50.63722000000001,0 91.60693000000001,50.57305,0 91.46776000000001,50.52694000000001,0 91.44748,50.4711,0 91.02970999999999,50.43306,0 90.87025,50.32638000000001,0 90.74860000000001,50.29749,0 90.69859,50.21943,0 90.50973000000001,50.22444000000001,0 90.37858,50.18582000000001,0 90.24443000000001,50.12027,0 90.05137000000003,50.0825,0 89.98526,49.97222000000001,0 89.64270000000001,49.90482999999999,0 89.6375,49.855,0 89.67137,49.82083,0 89.65107999999999,49.79527000000002,0 89.73609999999999,49.74889000000001,0 89.71138000000001,49.71472,0 89.45972,49.64999000000001,0 89.37221,49.57972,0 89.22665000000001,49.63833,0 89.19748,49.61111,0 89.23054999999999,49.53667,0 89.01331999999999,49.46360000000001,0 88.92886,49.54639,0 88.8875,49.54361000000001,0 88.89999000000002,49.44332,0 88.73081999999999,49.45083,0 88.65082,49.50055,0 88.22083000000001,49.46166,0 88.13193,49.36833000000001,0 88.1622,49.27222,0 87.98859,49.17722000000001,0 87.8407,49.17296000000001,0 87.52528,49.12915,0 87.46693000000001,49.07972,0 87.34821,49.09261,0 87.29971000000001,49.21054000000001,0 87.21887,49.2461,0 87.05247,49.25417000000001,0 86.92665,49.35306,0 86.96442,49.39194,0 86.83971,49.45222,0 86.84694,49.52166,0 86.61665000000001,49.58721000000001,0 86.68221000000001,49.66999,0 86.80025999999999,49.70222,0 86.76277000000002,49.73444000000001,0 86.77499,49.78889,0 86.61638000000001,49.79721,0 86.61109,49.76111,0 86.49706999999999,49.7155,0 86.50583,49.67638,0 86.28749000000001,49.58582000000001,0 86.24582000000001,49.50139999999999,0 86.19107,49.4725,0 86.13275,49.53277000000001,0 85.96944000000001,49.48861000000001,0 85.96915,49.56638,0 85.8372,49.54388000000001,0 85.77083,49.57999,0 85.69054,49.56444,0 85.63081,49.62749,0 85.52443,49.59055,0 85.40387,49.62332,0 85.2586,49.59138000000001,0 85.21111000000001,49.64721000000001,0 85.21053000000001,49.71555000000001,0 85.09193000000001,49.81971,0 85.0836,49.88138,0 84.97776,49.91276,0 84.97915,49.99777,0 85.05832,50.01361,0 85.01443,50.07583,0 84.85193,50.09166000000001,0 84.81053,50.14416000000001,0 84.73636999999999,50.1461,0 84.67443,50.19694,0 84.55998,50.19555,0 84.47386,50.25666,0 84.37302,50.20971000000002,0 84.26694000000001,50.27054,0 84.26277,50.35443,0 84.19692000000001,50.45304,0 84.22304,50.51471000000002,0 84.10621999999999,50.64296,0 83.96998000000001,50.71555,0 83.97693,50.79749,0 83.90166000000002,50.81388000000001,0 83.84333,50.87749000000001,0 83.51361,50.95555000000001,0 83.45638,51.00249000000001,0 83.1386,51.00194000000001,0 83.0872,50.92361,0 83.00999,50.88944000000001,0 82.76111,50.91082,0 82.72748,50.89888,0 82.72026,50.81081,0 82.49887,50.7211,0 82.44274,50.75804999999999,0 82.35637,50.75972,0 82.15027000000001,50.71054,0 82.01111,50.77138999999999,0 81.82887000000001,50.76833000000001,0 81.78276,50.79333,0 81.72581,50.74749000000001,0 81.59540000000001,50.73573,0 81.43747999999999,50.76445,0 81.48997,50.8411,0 81.42192,50.92777,0 81.4247,50.96638,0 81.07639,50.94781,0 81.18359,51.19609,0 81.04084,51.18143,0 80.94029000000002,51.22122,0 80.93223999999999,51.28399,0 80.87224999999999,51.25904,0 80.68783999999999,51.31472000000001,0 80.63344000000001,51.21476000000001,0 80.43872,51.19797000000001,0 80.43068000000001,51.11909,0 80.48678,51.05877,0 80.46236,50.96408000000001,0 80.19905,50.88837,0 80.17211,50.82919,0 80.06545,50.86060000000001,0 80.03516999999999,50.83338,0 80.1096,50.75947,0 79.99387,50.81304999999999,0 79.53637000000001,51.45000000000001,0 78.92303,52.17638000000002,0 77.92137,53.2589,0 77.52387,53.50639,0 76.52164999999999,53.99388,0 76.39333000000001,54.16361,0 76.72136999999999,54.15331000000001,0 76.86498,54.31805000000001,0 76.85583000000001,54.35889,0 76.77888,54.39415,0 76.92498999999999,54.43749,0 76.92359999999999,54.47554000000001,0 76.70663999999999,54.44721,0 76.65694000000001,54.36777000000001,0 76.48775999999999,54.32556,0 76.23888,54.35889,0 76.18552,54.29554000000001,0 76.20721,54.25916000000002,0 75.69136,54.11027,0 75.67777000000001,54.53417,0 75.80609,54.56554,0 75.83499000000001,54.60471,0 75.7897,54.66945,0 75.80887,54.69304,0 75.70972,54.68639,0 75.65721000000001,54.80999,0 75.51331999999999,54.82333,0 75.49165000000001,54.86138,0 75.54082,54.90194,0 75.34027,54.91416,0 75.40027000000002,55.02804,0 75.24803,55.1375,0 75.26944,55.25416,0 75.09138,55.38221,0 75.16859,55.4211,0 75.17137,55.45277,0 75.325,55.50722,0 75.32223000000001,55.54027,0 75.30582000000001,55.56917,0 75.1972,55.55972,0 75.13136,55.59888000000002,0 75.23775999999999,55.65999999999999,0 75.12886,55.69943000000001,0 75.13694000000001,55.72332,0 75.08194,55.75027,0 75.37025,55.76666,0 75.37331,55.81333,0 75.29749,55.84471,0 75.31219,55.89166000000001,0 75.50584000000001,55.99610000000001,0 75.54304999999999,56.04722000000002,0 75.70055000000001,56.0675,0 75.74887,56.14111000000001,0 75.95638,56.12749000000001,0 75.99442999999999,56.17832000000001,0 76.15082,56.18055,0 76.16304,56.14943,0 76.31887999999999,56.18555,0 76.29971000000001,56.23638,0 76.11665000000002,56.31415,0 76.07805999999999,56.41471,0 76.14194000000001,56.4511,0 76.05748,56.48277,0 76.0061,56.45388,0 75.93581,56.47971,0 75.895,56.44276,0 75.84415,56.45,0 75.86526000000001,56.49971000000001,0 76.01528,56.55165000000001,0 76.0461,56.62165000000002,0 76.14915000000001,56.63082,0 76.28943,56.72694,0 76.25194999999999,56.91833,0 76.19192,56.97276000000001,0 76.10887,57.25027999999999,0 75.96638,57.41416,0 75.8586,57.39917,0 75.71249,57.63917000000001,0 75.58275999999999,57.64639,0 75.56441,57.93387,0 75.06638000000001,58.11833,0 75.2397,58.21998,0 75.05415000000001,58.3447,0 75.35554999999999,58.47277,0 75.10526,58.58111,0 75.17081,58.6186,0 75.15028000000001,58.67805,0 75.38164999999999,58.78804,0 75.69054,59.01194,0 75.61526000000001,59.2411,0 75.83138,59.29639,0 75.89749000000001,59.43721000000001,0 75.99082,59.41972,0 76.16304,59.54361,0 76.45583000000001,59.54445,0 76.64833,59.58027,0 76.64554000000001,59.69332,0 76.74136,59.72943000000001,0 76.75888999999999,60.06081,0 76.68719,60.10777000000001,0 76.85165000000001,60.26806000000001,0 76.8186,60.33028000000002,0 76.74471,60.34249000000001,0 76.77443,60.47665,0 77.03749000000001,60.535,0 77.04721000000001,60.60361,0 77.03167000000001,60.63277,0 76.96527,60.64111,0 76.96776,60.71,0 77.07805000000001,60.72721,0 77.10498,60.85333000000001,0 77.40804,60.81027,0 77.68941,60.82749,0 77.92137,60.74665,0 78.12025,60.80222000000001,0 78.47527,60.77694,0 78.68941,60.83083,0 78.80721,60.77887,0 78.91110000000001,60.78722000000001,0 78.96915000000001,60.82805,0 79.18553,60.83860000000001,0 79.31833,60.79667000000001,0 79.28360000000001,60.71999000000001,0 79.38666000000001,60.64861,0 79.50249,60.68694,0 79.71581999999999,60.69555,0 80.18398999999999,60.66193,0 80.42100000000001,60.76556999999999,0 80.63061,60.76768000000001,0 80.69771000000002,60.80017,0 81.03823000000001,60.75419,0 81.0555,60.68084,0 81.11116,60.6375,0 81.49776,60.61555,0 81.86165,60.65055,0 82.16665,60.51832,0 82.39027,60.61028,0 82.38247,60.71278,0 83.14499000000001,61.03277000000001,0 83.50944,61.04916,0 83.99387,60.82472,0 84.25964000000001,60.85542,0 84.61415,61,0 85.71054,61.29749,0 85.64360000000001,61.38805,0 85.75249,61.43999,0 85.96639,61.46915,0 85.96915,61.54305,0 85.85193,61.59777,0 85.72275999999999,61.57361,0 85.46777,61.62860000000001,0 85.38582000000001,61.70167,0 85.24832000000001,61.68526000000001,0 85.00805,61.78416,0 84.68749,61.8061,0 84.63388999999999,61.87971000000001,0 84.55749,61.89472000000001,0 84.51666,61.93915,0 84.55441999999999,61.99694,0 84.43692,62.18915,0 84.72638000000001,62.40694,0 84.85915,62.44778,0 84.91387,62.49944000000001,0 84.89055000000001,62.54220999999999,0 84.94803000000002,62.59137000000001,0 85.09860000000001,62.6386,0 85.07777,62.67138000000001,0 85.2372,62.81444000000001,0 85.33249000000001,62.87331,0 85.4511,62.88277,0 85.52083,62.93277,0 85.49332,62.99027000000001,0 85.58716000000001,63.06996,0 85.46638,63.13805,0 85.47832,63.17054,0 85.53665,63.26083,0 85.67387000000001,63.35443999999999,0 85.55942,63.37499,0 85.40776,63.35,0 85.35276,63.40276999999999,0 85.38970000000002,63.42333000000001,0 85.37415,63.46220000000002,0 85.2961,63.51583,0 85.21193,63.50306000000001,0 85.08194,63.53638000000001,0 85.03203000000001,63.58649000000001,0 85.08998,63.60860000000002,0 85.17693,63.59137,0 85.26721000000001,63.64638000000001,0 85.33998,63.70693000000001,0 85.36693,63.81333,0 85.51639,63.93000000000001,0 85.95804,64.05443,0 85.96776,64.09775999999999,0 85.93248000000001,64.12776,0 86.0311,64.27528,0 85.94970000000002,64.32832999999999,0 85.92359999999999,64.41721,0 85.84081999999999,64.45889,0 85.84526,64.50749,0 85.91804000000001,64.5836,0 85.80859,64.64749000000001,0 85.86081,64.75526000000001,0 85.54694000000001,64.83028,0 85.14694,64.75166,0 85.09444000000001,64.81219,0 84.93222,64.82639,0 84.97748,64.88666000000001,0 84.90665,64.93026999999999,0 84.29137,64.9011,0 84.27804999999999,64.94054,0 84.39665000000001,64.99387,0 84.38108,65.03276,0 84.31386999999999,65.05721,0 84.3111,65.09860000000001,0 84.40999000000001,65.19082,0 84.52443,65.21278,0 84.58832,65.31303,0 84.50194000000001,65.39664999999999,0 84.55464000000001,65.43516,0 84.45028000000001,65.54554,0 84.19054,65.59444000000001,0 84.29276000000002,65.6122,0 84.31914999999999,65.66943000000001,0 84.32249,65.67609,0 84.17665,65.7,0 84.01111,65.79832,0 83.59108999999999,65.7736,0 83.50637,65.82944000000001,0 83.52804999999999,65.90443000000001,0 83.37276000000001,65.9836,0 83.28943,66.06693,0 83.37109,66.13666000000001,0 83.53970000000001,66.19525,0 83.34415,66.34193000000001,0 83.12469,66.42165,0 83.08693,66.46972,0 83.08666,66.61053,0 83.30582,66.66165,0 83.1836,66.74748,0 83.17249,66.80914,0 83.02748,66.85971000000001,0 83.06192,66.88081,0 83.04109,66.89221000000002,0 82.11942999999999,67.23888000000001,0 82.19721,67.25943,0 82.18165999999999,67.28803000000001,0 82.25915999999999,67.27998,0 82.21833,67.31137,0 82.3647,67.33055,0 82.2811,67.34887000000001,0 82.33778,67.3608,0 82.2336,67.42081,0 82.32971000000001,67.48109,0 82.38054,67.47971,0 82.35138000000001,67.50109999999999,0 82.39664999999999,67.53498999999999,0 82.11416,67.59943,0 82.04109,67.65665,0 82.11913,67.68747999999999,0 82.08055,67.68858,0 82.10554999999999,67.72887,0 81.90304999999999,67.77778000000001,0 81.72443000000001,67.92415,0 81.76194,67.95247999999999,0 81.8772,67.93165999999999,0 81.98888,67.96332,0 82.37442,67.94914,0 82.39610999999999,68.05914,0 82.35693000000001,68.09583000000001,0 82.39276,68.17747,0 82.54359,68.17276,0 82.52332000000001,68.20694,0 82.57361,68.25583,0 82.70663999999999,68.29331999999999,0 82.68443000000001,68.34636999999999,0 82.71249,68.40443000000001,0 82.68053999999999,68.49137,0 82.45610000000001,68.57832999999999,0 82.54915,68.61443,0 82.55859000000001,68.61111,0 82.58999,68.60276,0 82.69302999999999,68.63275,0 82.9011,68.62527,0 83.04276,68.66776,0 82.80164000000001,68.74082,0 82.78288000000001,68.79881,0 82.5522,68.81833,0 82.49721,68.98775999999999,0 82.31638,69.05387,0 82.50472000000002,69.10831,0 82.46138999999999,69.12442,0 82.46388,69.16054,0 81.81331,69.20055000000001,0 81.75972000000002,69.25249000000001,0 81.93611,69.34665,0 81.7811,69.43137,0 81.67249,69.44832,0 81.62302,69.41526,0 81.61387000000002,69.32833000000001,0 81.64028,69.31135999999999,0 81.58249000000001,69.27582,0 81.44414,69.25443000000001,0 81.33582,69.28803000000001,0 80.95596000000001,69.20667,0 80.79311,69.26394999999999,0 80.76669000000001,69.30504000000001,0 80.14597999999999,69.36324999999999,0 79.85914000000001,69.34610000000001,0 79.8772,69.37276,0 79.82278,69.39444,0 79.82805000000001,69.42249,0 79.71388,69.43248,0 79.74082,69.44832,0 79.63916999999999,69.48582,0 79.59583000000001,69.58832,0 79.50082,69.60471,0 79.5372,69.63081,0 79.49165000000001,69.63638,0 79.50583,69.65358999999999,0 79.00417,69.69693,0 79.13970999999999,69.74303999999999,0 79.06638,69.74721,0 79.06331,69.78664000000001,0 78.97582000000001,69.81165,0 79.01888,69.83499999999999,0 78.87527,69.88164999999999,0 78.96582000000001,69.88026000000002,0 79.18747999999999,69.96416000000002,0 79.11971,70.00664999999999,0 79.21860000000001,70.01166000000001,0 79.17859,70.03498,0 79.19443,70.05165,0 79.34943,70.04498,0 79.32805000000001,70.14888000000001,0 79.49887,70.11053,0 79.62943,70.22109,0 79.70889,70.23859,0 79.73831,70.24887,0 79.70360000000001,70.26999000000001,0 79.7247,70.28415000000001,0 79.85748,70.27222000000002,0 80.11404,70.36096000000001,0 80.08208999999999,70.39373000000001,0 80.18601000000001,70.39182,0 80.17683,70.43655,0 80.57176,70.46187999999999,0 80.62567,70.42167000000001,0 80.69155000000001,70.42319999999999,0 80.70740000000001,70.39100999999999,0 80.76217,70.39443,0 80.72770000000001,70.41077,0 80.79106,70.41504999999999,0 80.79858,70.44119000000001,0 80.67014000000002,70.44623,0 80.60648999999999,70.50095,0 80.66569,70.51388,0 80.6601,70.57407000000001,0 80.72072,70.5945,0 80.75629000000001,70.66586,0 80.59229000000001,70.71675000000001,0 80.69016000000001,70.80186000000001,0 80.56647,70.85477,0 80.55004,71.05757,0 79.96638,71.13109,0 79.50832,71.29331999999999,0 79.22915999999999,71.3175,0 79.1747,71.34609,0 79.14027,71.42165,0 79.3586,71.49413,0 79.39082000000001,71.54387,0 79.33138,71.59775999999999,0 79.38026000000001,71.61637,0 79.86972,71.60027,0 80.13540999999999,71.67453,0 80.06631,71.76943,0 80.12739999999999,71.7749,0 80.03504,71.83147,0 80.17328999999999,71.86931,0 80.27464000000001,71.85148,0 80.2719,71.87848,0 80.34878999999999,71.88601,0 80.24838,71.91006,0 80.23093,71.94193,0 79.91002,71.98448000000001,0 79.86720000000001,72.02249000000001,0 79.69692000000001,72.05915,0 79.42276,72.03554,0 79.38275000000002,72.06219,0 79.09444000000001,72.07055,0 79.09193000000001,72.10165000000001,0 79.01859,72.11998,0 78.91081,72.11471,0 78.91165,72.14444,0 78.7711,72.17027,0 78.80803,72.18332000000001,0 78.67693,72.17944,0 78.59721999999999,72.20972,0 78.67804,72.22165,0 78.65913999999999,72.26111,0 78.44637,72.35971000000001,0 78.45471000000001,72.36443,0 78.50015000000001,72.37733,0 78.4727,72.64751,0 78.17527,72.89798,0 78.20658,73.02322,0 78.53532,73.30501,0 79.49023,73.66505000000001,0 81.08696000000001,73.75897999999999,0 82.27669,73.92335,0 82.27669,74.95653,0 82.90286,74.98784000000001,0 83.37249,75.30092999999999,0 83.49773000000002,75.83316000000002,0 83.27856,76.17757,0 82.84025,76.36541,0 82.30801,76.45934,0 82.30801,78.37854,0 82.30801,83.37854000000002,0 82.52397000000001,83.37876,0 87.52397000000001,83.3839,0 92.52396,83.38905,0 97.52396,83.39419,0 97.52396,84.90001,0 97.52396,89.90000000000001,0 98.4623,89.90000000000001,0 99.40063000000002,89.90000000000001,0 100.339,89.90000000000001,0 101.2773,89.90000000000001,0 102.2156,89.90000000000001,0 103.154,89.90000000000001,0 104.0923,89.90000000000001,0 105.0307,89.90000000000001,0 105.969,89.90000000000001,0 106.9073,89.90000000000001,0 107.8457,89.90000000000001,0 108.784,89.90000000000001,0 109.7223,89.90000000000001,0 110.6607,89.90000000000001,0 111.599,89.90000000000001,0 112.5374,89.90000000000001,0 112.5259,84.90002,0 112.5144,79.90003,0 112.5097,77.85098000000001,0 112.5083,77.24612000000001,0 113.351,76.87012,0 113.9618,76.43810000000001,0 114.1406,75.88692,0 113.8128,75.26125999999999,0 112.6509,74.76967999999999,0 111.3847,74.42704000000001,0 111.34,74.27808,0 111.6826,74.06952,0 111.6379,73.93545,0 111.2942,73.85934,0 110.8934,73.76541,0 110.9169,73.76028,0 110.8775,73.72859,0 110.9143,73.69668,0 111.0803,73.65860000000001,0 111.1433,73.63777,0 111.1178,73.59359000000001,0 111.0547,73.57165999999999,0 110.8483,73.56554,0 110.8036,73.50832,0 110.5641,73.40637,0 110.1644,73.40248,0 110.5667,73.31944,0 110.6272,73.26888,0 110.423,73.18997,0 110.7978,73.17303,0 110.8583,73.07805000000002,0 110.9417,73.03888000000001,0 110.7778,73.00054,0 110.8155,72.95359999999999,0 110.8755,72.94859,0 110.6466,72.89554,0 110.6597,72.85305,0 110.7317,72.82999,0 110.6322,72.78665000000001,0 110.7303,72.64472000000001,0 110.925,72.63193,0 110.89,72.58110000000002,0 111.3155,72.51277,0 111.3036,72.47832,0 111.0758,72.4061,0 111.0883,72.37220000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +08:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(8);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0800</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +108.4614,64.28581,0 108.5394,64.23248,0 108.4769,64.20081999999999,0 108.5441,64.1747,0 108.49,64.1147,0 108.7383,63.98082,0 108.6839,63.9086,0 108.7719,63.86472,0 108.6489,63.78582,0 108.6056,63.80749,0 108.4661,63.78944000000001,0 108.3716,63.82638,0 108.2886,63.80805,0 108.2703,63.67888,0 108.1164,63.62249,0 108.1314,63.56777,0 108.5586,63.59972000000001,0 108.8566,63.53582000000002,0 109.0433,63.55416000000002,0 109.2289,63.44027,0 109.2717,63.34722,0 109.363,63.36193,0 109.3917,63.32472,0 109.3753,63.25528000000001,0 109.4564,63.21860000000001,0 109.4061,63.18277,0 109.46,63.15055,0 109.4317,63.09361,0 109.4647,63.07749,0 109.4472,63.03611,0 109.4811,63.02777,0 109.4553,62.99972000000001,0 109.4811,62.9961,0 109.4683,62.93388000000002,0 109.6158,62.87609,0 109.6505,62.82444000000002,0 109.5039,62.77527,0 109.4561,62.66637999999999,0 109.375,62.64583,0 109.3869,62.58306000000001,0 109.2444,62.57277,0 109.2642,62.52332,0 109.3158,62.47554000000001,0 109.4205,62.45832,0 109.4711,62.40499,0 109.5442,62.4325,0 109.9036,62.41388,0 109.9247,62.39722,0 109.8911,62.35333,0 109.9628,62.32249000000002,0 109.96,62.31639,0 110,62.28220000000001,0 109.9333,62.23472,0 109.9997,62.16361,0 109.9036,62.11306,0 109.8183,62.00363000000001,0 109.6116,61.89277000000001,0 109.5864,61.82333000000001,0 109.5858,61.77443,0 109.6389,61.76083000000001,0 109.6126,61.71394000000001,0 109.7161,61.67666,0 109.7061,61.64804,0 109.8742,61.53333000000001,0 109.8653,61.49305,0 109.8247,61.47748,0 109.8492,61.43054000000001,0 109.7875,61.32721000000001,0 109.8553,61.29139000000001,0 110.0903,61.27027,0 110.1797,61.15639,0 110.3211,61.14638000000001,0 110.3972,61.18111,0 110.483,61.15971,0 110.5208,61.08138,0 110.4575,61.03667,0 110.448,60.97694000000001,0 110.23,60.81916,0 110.225,60.79610000000002,0 110.2947,60.76999000000001,0 110.2703,60.76222,0 110.2678,60.69194,0 110.04,60.66915,0 110.1222,60.62416,0 110.1178,60.57972,0 110.0389,60.55833,0 110.0219,60.51694,0 109.9164,60.45889,0 109.9519,60.40359999999999,0 109.8611,60.37417000000001,0 109.8789,60.33805,0 109.8055,60.31305,0 109.8069,60.25443,0 109.7264,60.22610000000001,0 109.6936,60.11193,0 109.7164,60.05222000000001,0 109.7694,60.02887000000001,0 109.62,59.90305,0 109.6514,59.86193000000001,0 109.4947,59.75027000000001,0 109.4842,59.68721000000002,0 109.5339,59.64555,0 109.2594,59.4511,0 109.3558,59.41667000000001,0 109.245,59.36306,0 109.2605,59.31694,0 109.5167,59.29193,0 109.5172,59.23082,0 109.5405,59.22083,0 109.5281,59.18027,0 109.6286,59.1511,0 109.6405,59.06166,0 109.7411,59.07194000000001,0 109.8203,58.97971,0 109.9355,59.00917,0 110.2783,58.98194,0 110.3522,59.03582,0 110.4819,59.03334,0 110.5386,59.06221000000001,0 110.5425,59.10556,0 110.5917,59.13110000000001,0 110.5789,59.19166000000001,0 110.6927,59.25777000000001,0 110.9969,59.20805000000001,0 111.0831,59.23944,0 111.1822,59.18332000000001,0 111.3042,59.25694000000001,0 111.4353,59.27388,0 111.6461,59.2061,0 111.7681,59.27805,0 111.9419,59.2711,0 111.9933,59.34470000000001,0 112.2214,59.50361,0 112.2739,59.50444000000001,0 112.2714,59.46638,0 112.3341,59.41694,0 112.2728,59.365,0 112.3075,59.33666000000002,0 112.2839,59.30305,0 112.438,59.33028,0 112.6322,59.31082000000001,0 112.6386,59.26944000000001,0 112.5378,59.23694,0 112.46,59.16166,0 112.4461,58.92805,0 112.5003,58.86999,0 112.6276,58.96118,0 112.5555,59.02665,0 112.5742,59.07168,0 112.6453,59.03249,0 112.7314,59.0872,0 112.8822,59.11833,0 112.9117,59.15499,0 112.9714,59.13444000000001,0 113.2817,59.16109000000001,0 113.4694,59.26500000000001,0 113.4558,59.29889,0 113.4869,59.33333,0 113.4211,59.37665,0 113.4728,59.43860000000001,0 113.6097,59.49527000000001,0 113.5864,59.55915000000002,0 113.6089,59.58111000000001,0 113.7836,59.60999,0 113.8492,59.68832,0 114.0419,59.68054999999999,0 114.0433,59.74110000000001,0 114.1655,59.74582000000002,0 114.3155,59.88361,0 114.5667,59.99777000000001,0 114.5211,60.04527,0 114.5461,60.11388000000001,0 114.6966,60.21998,0 114.8139,60.18054,0 115.013,60.27167,0 115.0906,60.38276000000001,0 115.1891,60.42972000000001,0 115.6755,60.52666,0 115.8428,60.465,0 116.0032,60.45931000000002,0 116.078,60.40417,0 116.6008,60.35722000000001,0 116.7322,60.25777,0 116.84,60.24222,0 116.9736,60.15777,0 117.0444,60.08250000000001,0 117.0486,60.03583,0 117.2975,60.01943,0 117.3089,59.99528,0 117.2333,59.95694,0 117.1139,59.96333,0 117.0572,59.90889,0 117.2275,59.82943,0 117.2008,59.65499,0 117.0844,59.58721,0 117.1966,59.56916,0 117.2361,59.52777,0 117.5989,59.47083,0 117.7583,59.54084000000001,0 117.8189,59.44944000000001,0 117.8836,59.44165,0 117.94,59.45471,0 117.9864,59.53638,0 118.0572,59.58333,0 118.2836,59.61055,0 118.3503,59.58888,0 118.3903,59.51777,0 118.7644,59.4011,0 118.7633,59.38722000000001,0 118.7553,59.33333,0 118.8514,59.28611,0 118.7014,59.21305000000001,0 118.7025,59.06554,0 118.7369,59.02361000000001,0 118.8478,59.01441000000001,0 118.8539,58.94360000000001,0 118.7922,58.9397,0 118.7867,58.8875,0 118.8969,58.81471000000001,0 118.8875,58.69554000000001,0 118.8269,58.61527,0 118.763,58.61249,0 119.1297,58.48249,0 119.1261,58.40833000000001,0 119.0655,58.36388,0 119.1317,58.29027,0 119.1084,58.22268,0 118.803,58.21666000000001,0 118.6475,58.17028000000001,0 118.5614,58.21444,0 118.4892,58.21111,0 118.3072,58.36028000000001,0 118.1354,58.38744,0 118.0639,58.33832000000001,0 118.013,58.39305,0 117.8308,58.4122,0 117.6722,58.40055,0 117.6644,58.3711,0 117.4792,58.31361,0 117.4714,58.28278,0 117.5906,58.18555,0 117.5739,58.13582,0 117.44,58.15721,0 117.4075,58.12721,0 117.4139,58.08749000000001,0 117.3725,58.0611,0 117.3958,58.02610000000001,0 117.3647,57.99554,0 117.3661,57.92749,0 117.4175,57.90666,0 117.31,57.84388000000001,0 117.123,57.84277,0 117.0844,57.80416000000001,0 117.1783,57.73888000000002,0 117.1378,57.63972,0 117.2878,57.61306,0 117.3105,57.56555,0 117.2522,57.51333000000002,0 117.2889,57.45833,0 117.3286,57.45443,0 117.365,57.32778,0 117.4444,57.32972,0 117.4264,57.25861,0 117.4742,57.23778,0 117.5464,57.25999,0 117.5964,57.32333,0 117.7758,57.30249000000001,0 117.7955,57.28722,0 117.7452,57.19583000000001,0 117.5969,57.15332,0 117.6783,57.06666,0 117.6889,57.01444999999999,0 117.6744,56.9747,0 117.57,56.93555000000001,0 117.5728,56.84722,0 117.498,56.87137,0 117.4605,56.81694000000001,0 117.4103,56.82583000000001,0 117.4153,56.85777,0 117.3447,56.86693000000001,0 117.3642,56.91610000000001,0 117.3311,56.9486,0 117.1933,56.87943,0 117.1355,56.80111,0 117.0222,56.81554,0 117.0017,56.79943000000001,0 117.0403,56.77638,0 116.9672,56.78111,0 116.8072,56.72388,0 116.7386,56.76083000000001,0 116.5594,56.7589,0 116.3639,56.84860000000001,0 116.1211,56.81221000000002,0 116.0417,56.86361,0 115.9628,56.86111,0 115.8617,56.93277000000001,0 115.7368,56.95655,0 115.5864,56.87249000000001,0 115.6136,56.78611,0 115.5903,56.71554,0 115.443,56.66388,0 115.4311,56.62823,0 115.4647,56.58971,0 115.6819,56.56527000000001,0 115.6755,56.49277,0 115.7988,56.42968000000001,0 115.7844,56.40193,0 115.6797,56.39721,0 115.6841,56.29694,0 115.7619,56.17416,0 115.8369,56.14943,0 115.7691,56.11694,0 115.7855,56.07193000000001,0 115.7586,56.04111000000001,0 115.898,55.91667000000001,0 115.8419,55.82638000000001,0 115.8683,55.74499,0 115.8411,55.67222000000001,0 115.913,55.6461,0 115.8894,55.49889000000001,0 115.9672,55.44526000000001,0 115.93,55.39694,0 115.9639,55.36555,0 116.0694,55.37944,0 116.1144,55.34749000000002,0 116.4022,55.36082,0 116.4616,55.30555,0 116.4694,55.22748,0 116.6991,55.11472000000001,0 116.7825,55.11028,0 116.8411,55.02138,0 116.8264,54.97860000000001,0 116.8892,54.94165,0 116.8778,54.90527,0 116.9239,54.79554,0 116.8694,54.7686,0 116.8739,54.70416,0 116.7697,54.66109,0 116.77,54.59222,0 116.7144,54.56333000000001,0 116.7119,54.52944,0 116.5372,54.50943,0 116.5228,54.46408,0 116.185,54.50722,0 115.6708,54.42388,0 115.5092,54.36472000000001,0 115.4993,54.30138000000001,0 115.1833,54.18444,0 115.1494,54.12027000000001,0 115.0241,54.07027000000002,0 115.0239,54.03082,0 114.9758,53.99249,0 114.5958,53.90277,0 114.5031,53.80915000000001,0 114.4453,53.80249000000001,0 114.3747,53.81249,0 114.2961,53.74666000000001,0 114.1431,53.75610000000001,0 113.9947,53.71694,0 113.9686,53.69944000000001,0 113.9744,53.64444000000001,0 113.8147,53.61644,0 113.8955,53.53472,0 113.8278,53.4575,0 113.8322,53.38971999999999,0 113.9433,53.35638,0 113.9639,53.31945,0 113.94,53.28444,0 113.9822,53.23305,0 114.048,53.25249,0 114.0464,53.22359,0 114.0925,53.2047,0 114.2017,53.19222,0 114.1269,53.07194,0 114.2314,53.10361000000001,0 114.3253,53.06777,0 114.2814,53.02139,0 114.3172,52.96721,0 114.2322,52.96527,0 114.2422,52.92749,0 114.2144,52.89054000000001,0 114.2422,52.82388,0 114.1236,52.71832,0 113.9542,52.62555,0 113.8978,52.63499,0 113.738,52.57722,0 113.5792,52.45027,0 113.5341,52.49416,0 113.2733,52.47805000000001,0 112.9303,52.33665000000001,0 112.5389,52.34972000000001,0 112.4925,52.25000000000001,0 112.4022,52.20694,0 112.2964,52.25278000000001,0 112.2089,52.24054,0 112.1683,52.30389000000001,0 112.0608,52.27833,0 111.6078,51.92778,0 111.198,51.82361000000001,0 111.1397,51.83527,0 111.015,51.78221000000001,0 111.0019,51.72222,0 110.8414,51.66915000000002,0 110.8639,51.59222,0 110.7817,51.57471,0 110.6961,51.50277,0 110.5447,51.49471,0 110.4205,51.52222,0 110.3947,51.57944,0 110.0491,51.62000000000001,0 109.6789,51.46305,0 109.4289,51.41610000000001,0 109.38,51.34666,0 109.3386,51.39138,0 109.2522,51.36221000000001,0 109.0636,51.50861,0 108.945,51.43999000000002,0 108.743,51.44583000000001,0 108.6208,51.51306,0 108.547,51.47527,0 108.4736,51.34499,0 108.3525,51.26528,0 108.4542,51.22776000000001,0 108.5747,51.11528,0 108.4997,51.05915000000002,0 108.3322,51.04887999999999,0 108.2515,50.83393,0 108.0655,50.75804999999999,0 108.1794,50.65915,0 108.4167,50.61361,0 108.5447,50.52777,0 108.6583,50.53777,0 108.6853,50.50417,0 108.6039,50.45221999999999,0 108.4369,50.46249000000002,0 108.3386,50.40444000000001,0 108.2042,50.41109,0 108.1325,50.31444,0 108.0761,50.35332,0 107.9855,50.33721,0 107.9375,50.22221,0 107.8936,50.19249000000001,0 107.8205,50.20665000000002,0 107.7736,50.15749000000002,0 107.7619,50.10471,0 107.8008,50.09166000000001,0 107.7989,50.02999,0 107.7536,49.97955000000001,0 107.8133,49.94860000000001,0 107.9806,49.94083,0 107.9553,49.85167,0 107.9892,49.74499,0 107.9475,49.68333,0 108.0319,49.67471,0 108.0419,49.61082,0 108.1375,49.55804,0 108.2719,49.53555000000001,0 108.3589,49.44526000000001,0 108.5275,49.34555,0 108.6511,49.33173000000001,0 109.2069,49.36028,0 109.3519,49.33694,0 109.5442,49.23249,0 109.7394,49.23499000000001,0 109.8503,49.18999,0 110.2369,49.16945,0 110.4011,49.25500000000001,0 110.6303,49.15249,0 110.7886,49.14944,0 110.9811,49.20083,0 111.3714,49.37416,0 111.498,49.34916000000001,0 111.6822,49.40665,0 111.9639,49.40694,0 112.4917,49.54138,0 112.7669,49.50083,0 113.0908,49.59859,0 113.1014,49.67888,0 113.2167,49.81917,0 113.5861,50.01639,0 113.7825,50.08693000000002,0 113.8747,50.08693000000002,0 114.1764,50.24833,0 114.313,50.28416,0 114.4439,50.24527,0 114.6758,50.26415999999999,0 114.8542,50.22832,0 115.0236,50.15193,0 115.2053,49.99833,0 115.3964,49.90110000000001,0 115.7547,49.88443000000001,0 116.0697,50.00278,0 116.2461,50.02749000000001,0 116.6155,49.93138,0 116.7114,49.83046000000001,0 117.17,49.6525,0 117.4944,49.62305,0 117.872,49.51022,0 117.8958,49.55,0 118.3658,49.77832000000002,0 118.3855,49.81667000000001,0 118.4647,49.82444,0 118.5833,49.92694,0 119.0996,49.99044,0 119.2141,50.01528,0 119.3222,50.09722000000001,0 119.3481,50.15948,0 119.3289,50.27388000000001,0 119.368,50.30999,0 119.348,50.34833,0 119.1983,50.34527,0 119.1353,50.38972,0 119.2422,50.44526,0 119.2869,50.59389000000001,0 119.5022,50.74221000000001,0 119.5225,50.90499,0 119.7467,51.0736,0 119.7805,51.1661,0 119.7553,51.20750000000001,0 119.818,51.21416,0 119.8097,51.26416,0 119.9208,51.33638,0 119.8914,51.37417,0 119.9566,51.38999,0 119.983,51.49305,0 120.0491,51.54777000000001,0 120.0969,51.66138,0 120.1972,51.68610000000002,0 120.4886,51.8772,0 120.6578,51.9175,0 120.7128,51.98249000000001,0 120.695,52.04860999999999,0 120.7531,52.07917,0 120.7828,52.15526,0 120.7592,52.23916,0 120.6228,52.34026999999999,0 120.6867,52.43361000000001,0 120.7136,52.54472000000001,0 120.4403,52.63722,0 120.1891,52.58028,0 120.073,52.59,0 120.0306,52.61721,0 120.0292,52.76805,0 120.2728,52.85777,0 120.8639,53.27972000000001,0 121.2083,53.27055000000001,0 121.325,53.31833,0 121.4738,53.32382,0 121.83,53.41305,0 122.0261,53.41397,0 122.3144,53.48777,0 122.41,53.43721000000001,0 122.8244,53.45,0 123.1533,53.50054999999999,0 123.2461,53.55305,0 123.4789,53.49917,0 123.4975,53.54694,0 123.5428,53.53388,0 123.5294,53.49944,0 123.6194,53.54277,0 123.6975,53.49583,0 123.9108,53.46471,0 124.1044,53.34083,0 124.2553,53.35665,0 124.4022,53.21221,0 124.6528,53.18581,0 124.7058,53.13443000000001,0 124.805,53.13305,0 124.8516,53.08915,0 124.89,53.09471,0 124.8755,53.14388000000001,0 124.9122,53.16972,0 125.1342,53.19749000000001,0 125.4661,53.08388000000001,0 125.5061,53.03832,0 125.623,53.05,0 125.7289,52.97943,0 125.7191,52.94222,0 125.6486,52.91276,0 125.6469,52.87887000000001,0 125.8447,52.89943000000002,0 125.8592,52.85166000000001,0 125.9705,52.76333,0 126.0508,52.78583,0 126.0955,52.76444,0 126.0355,52.7211,0 126.0642,52.67638,0 125.973,52.64167,0 125.9886,52.58222,0 126.0308,52.56333,0 126.0939,52.58804,0 126.2094,52.50139000000002,0 126.1977,52.46444,0 126.2675,52.4586,0 126.3403,52.38942999999999,0 126.3203,52.30832,0 126.4364,52.28082000000001,0 126.3064,52.20417,0 126.5572,52.13055,0 126.5122,52.03889,0 126.4616,52.02694,0 126.4422,51.98804,0 126.6414,51.74527,0 126.7214,51.7086,0 126.7292,51.63833000000001,0 126.6728,51.57471,0 126.8025,51.53694,0 126.8247,51.50139,0 126.7905,51.42443000000001,0 126.9153,51.37915,0 126.9019,51.33943,0 126.8214,51.33028000000001,0 126.8161,51.26639000000001,0 126.8933,51.23915000000001,0 126.8611,51.30693999999999,0 126.9755,51.31360999999999,0 126.8986,51.18665,0 126.9305,51.05999000000001,0 127.1272,50.91555,0 127.2175,50.78416,0 127.2972,50.73249,0 127.28,50.68083,0 127.3677,50.57083,0 127.2894,50.46027000000001,0 127.3542,50.41332,0 127.3366,50.31027,0 127.5861,50.20856,0 127.5697,50.12833,0 127.4883,50.04221,0 127.4878,49.98055000000002,0 127.5314,49.91972000000001,0 127.5083,49.82,0 127.5419,49.78249000000001,0 127.6717,49.74889000000001,0 127.6914,49.66721,0 127.8214,49.5911,0 127.9744,49.60249000000001,0 128.075,49.54222000000001,0 128.1911,49.53416000000001,0 128.5052,49.58694,0 128.6259,49.56967000000001,0 128.6689,49.61193,0 128.8025,49.55916,0 128.7525,49.46804000000001,0 128.8752,49.47776,0 128.9927,49.44165,0 129.0769,49.35279,0 129.1997,49.39059000000001,0 129.3535,49.3423,0 129.3691,49.41396000000001,0 129.4122,49.43176,0 129.5235,49.39124,0 129.545,49.29318,0 129.7219,49.26626,0 129.7327,49.19294,0 129.8474,49.15796000000001,0 129.833,49.11796,0 129.9127,49.08241,0 129.9164,49.02963,0 130.2289,48.86221,0 130.418,48.8961,0 130.5044,48.85194,0 130.6277,48.87465,0 130.675,48.84415000000001,0 130.523,48.60277,0 130.6175,48.56194000000001,0 130.6269,48.47720000000001,0 130.7578,48.48805,0 130.7375,48.41833,0 130.8322,48.29694,0 130.6594,48.0986,0 130.6814,48.04554,0 130.8897,47.90028,0 130.9461,47.80055,0 130.9464,47.71138,0 131.0405,47.67583,0 131.4167,47.7386,0 131.5372,47.71888,0 131.5755,47.6561,0 131.6783,47.69915000000002,0 131.9194,47.66999,0 132.3411,47.74248999999999,0 132.5425,47.71389000000001,0 132.6597,47.86583,0 132.6714,47.94972000000001,0 132.8089,47.94194,0 132.8844,48.00860999999999,0 133.018,48.03528000000001,0 133.0886,48.10167,0 133.3872,48.10528,0 133.5067,48.07138000000001,0 133.5936,48.17249,0 133.6964,48.18694,0 133.7514,48.24972,0 133.978,48.25972000000001,0 134.0458,48.32528000000001,0 134.4153,48.39112,0 134.4827,48.35028,0 134.5718,48.36446000000001,0 134.6105,48.30694,0 134.7408,48.26712,0 134.6814,48.22055,0 134.6678,48.10722,0 134.5839,48.06276,0 134.5572,47.99471000000001,0 134.5988,47.90471,0 134.6619,47.87417,0 134.6844,47.80090000000001,0 134.7703,47.73944,0 134.7672,47.7075,0 134.6836,47.62972,0 134.6741,47.57194,0 134.5521,47.46692,0 134.4791,47.42778,0 134.3147,47.4211,0 134.1825,47.32332000000001,0 134.1478,47.24942999999999,0 134.1947,47.20665000000001,0 134.2222,47.11083000000001,0 134.1312,47.07423,0 134.0627,46.97527000000001,0 134.0669,46.9161,0 134.0119,46.82389,0 134.0438,46.75289999999999,0 133.9977,46.64016,0 133.9016,46.57222,0 133.8458,46.47554000000001,0 133.85,46.43999,0 133.9286,46.39249,0 133.8619,46.33138,0 133.9102,46.31444,0 133.8839,46.29361,0 133.9002,46.25031000000001,0 133.6869,46.14332,0 133.7247,46.06528,0 133.6605,45.93458,0 133.6108,45.93194,0 133.583,45.87137,0 133.5222,45.88665,0 133.4747,45.82027999999999,0 133.508,45.77777,0 133.4419,45.69804,0 133.4886,45.68277000000001,0 133.4533,45.64722,0 133.4683,45.62222,0 133.4191,45.60722,0 133.4171,45.5691,0 133.1905,45.50416000000001,0 133.1491,45.45083000000001,0 133.0925,45.24415,0 133.1222,45.12860000000001,0 133.0333,45.06167,0 132.9233,45.02194000000001,0 131.9886,45.24194,0 131.8647,45.34555,0 131.7536,45.21305000000001,0 131.6464,45.19887,0 131.6444,45.10555,0 131.4666,44.96721,0 131.35,44.97471,0 131.2125,44.91055,0 131.118,44.92583,0 131.0425,44.86333,0 130.9539,44.8461,0 131.0955,44.69137,0 131.298,44.05194,0 131.2472,44.0275,0 131.2311,43.97387,0 131.2461,43.91806,0 131.1991,43.81889,0 131.2205,43.59527,0 131.1889,43.53666,0 131.1944,43.52777,0 131.223,43.51167,0 131.3044,43.45583,0 131.3114,43.39222,0 131.2741,43.36333,0 131.2611,43.26445,0 131.1911,43.19665,0 131.2144,43.13305,0 131.1047,43.03611,0 131.1322,42.91916,0 131.0458,42.90193,0 131.0153,42.855,0 130.8186,42.87610000000001,0 130.7705,42.83194,0 130.638,42.83167,0 130.4052,42.71804,0 130.4589,42.67999,0 130.5389,42.68194,0 130.6052,42.61971000000001,0 130.618,42.57944000000001,0 130.5591,42.50667000000001,0 130.6044,42.42186,0 130.5675,42.44305,0 130.5147,42.60222,0 130.4614,42.59389,0 130.4725,42.55694,0 130.4372,42.545,0 130.4255,42.60222,0 130.2433,42.71833,0 130.2522,42.89249,0 130.1008,42.91194,0 130.1358,42.96221,0 130.1075,42.97833,0 129.9996,42.96142,0 129.9075,43.00581,0 129.8756,42.9897,0 129.9056,42.96999,0 129.8512,42.95998,0 129.7578,42.72971,0 129.7533,42.69415,0 129.7875,42.675,0 129.7427,42.64083,0 129.7608,42.60332,0 129.725,42.56139,0 129.7119,42.44415,0 129.5908,42.44526,0 129.5397,42.36361,0 129.4422,42.43443000000001,0 129.3419,42.44583,0 129.3441,42.41471,0 129.24,42.37417,0 129.2377,42.32194,0 129.1905,42.31333,0 129.2244,42.29639,0 129.1758,42.23499,0 129.2102,42.21721,0 129.0141,42.0942,0 128.9577,42.08111,0 128.9122,42.0211,0 128.7291,42.04695,0 128.4894,41.99555,0 128.2825,42.03916,0 128.0589,42.00332,0 128.1019,41.7961,0 128.3019,41.57778,0 128.2044,41.41667000000001,0 128.1127,41.36971,0 128.1061,41.39415,0 128.0441,41.38943,0 128.0475,41.41888,0 128.0208,41.4086,0 128.0008,41.44555,0 127.9814,41.42276,0 127.8739,41.43943,0 127.8583,41.40444,0 127.6475,41.40277,0 127.5572,41.42972,0 127.5391,41.46361,0 127.2708,41.4725,0 127.2872,41.50111,0 127.1022,41.53528,0 127.1786,41.58749,0 127.057,41.64351,0 127.04,41.67194,0 127.078,41.68194,0 127.0453,41.72444,0 126.9364,41.76305,0 126.9361,41.79444,0 126.7975,41.7311,0 126.7869,41.69222,0 126.6947,41.74027,0 126.6789,41.72444,0 126.7222,41.69804000000001,0 126.6151,41.66418,0 126.5633,41.61092,0 126.5844,41.56860000000001,0 126.5008,41.43194,0 126.5272,41.3575,0 126.4561,41.35722,0 126.2792,41.15277,0 126.1239,41.08111,0 126.1322,41.04416000000001,0 126.0733,40.97305,0 125.9775,40.91331,0 126.0169,40.89999,0 125.8786,40.89472,0 125.8214,40.86139,0 125.7619,40.88361,0 125.6531,40.82166,0 125.6803,40.76554,0 125.5641,40.77832,0 125.5447,40.7186,0 125.4642,40.7136,0 125.4058,40.62082,0 125.2861,40.64749,0 125.248,40.60388,0 125.0178,40.53082,0 125.04,40.45722,0 124.8805,40.47054,0 124.8328,40.40915,0 124.7433,40.37,0 124.7084,40.30505,0 124.5547,40.24222000000001,0 124.3736,40.09361,0 124.3702,40.0028,0 124.1097,39.48034,0 124.1097,37.96414,0 124.1097,32.96414,0 127.5079,32.96414,0 127.5079,32.5252,0 127.5079,27.5252,0 122.8135,27.5252,0 122.8135,23.56643,0 127.5079,23.5489,0 127.5079,23.43974,0 127.5079,18.43974,0 127.5079,13.43974,0 127.5079,8.439740000000001,0 127.5079,3.439736,0 127.1436,2.912129,0 124.3024,-1.202193,0 124.3024,-3.067723,0 125.441,-3.067723,0 125.441,-7.306758,0 127.3328,-7.298002000000002,0 127.3328,-8.906616,0 127.3328,-13.90662,0 127.3967,-13.965,0 127.4278,-13.95666,0 127.4466,-14.05916,0 127.5044,-14.06334,0 127.6136,-14.13611,0 127.7605,-14.30055,0 127.8228,-14.38694,0 127.8247,-14.44444,0 127.865,-14.42056,0 127.8514,-14.48222,0 127.8855,-14.47277,0 127.9494,-14.52667,0 127.9453,-14.57639,0 128.0036,-14.56111,0 128.0697,-14.64584,0 128.2153,-14.69975,0 128.3259,-14.71017,0 128.3877,-14.79719,0 128.5417,-14.75778,0 129,-14.87108,0 129.0006,-16.0049,0 129.0032,-21.0049,0 129.0059,-26.0049,0 129.0052,-26.69261,0 129.0003,-31.69261,0 128.2483,-32.01084,0 128.0039,-32.09223,0 127.7539,-32.1286,0 127.508,-32.22667,0 127.508,-33.42665,0 127.508,-38.42665,0 127.508,-43.42665,0 127.508,-48.42665000000001,0 127.508,-53.42665000000001,0 127.508,-58.42665,0 127.508,-63.42665,0 127.508,-68.42665,0 127.508,-72.02666000000001,0 127.508,-77.02666000000001,0 127.508,-82.02666000000001,0 127.508,-84.89999,0 127.508,-89.90000000000001,0 126.57,-89.90000000000001,0 125.632,-89.90000000000001,0 124.694,-89.90000000000001,0 123.7561,-89.90000000000001,0 122.8181,-89.90000000000001,0 121.8801,-89.90000000000001,0 120.9422,-89.90000000000001,0 120.0042,-89.90000000000001,0 119.0662,-89.90000000000001,0 118.1282,-89.90000000000001,0 117.1903,-89.90000000000001,0 116.2523,-89.90000000000001,0 115.3143,-89.90000000000001,0 114.3764,-89.90000000000001,0 113.4384,-89.90000000000001,0 112.5004,-89.90000000000001,0 112.5004,-89.43648,0 112.5004,-84.43648,0 112.5004,-79.43648,0 112.5004,-74.43648,0 112.5004,-69.43648000000002,0 112.5004,-64.43649000000001,0 112.5004,-59.43648,0 112.5004,-54.73646999999999,0 112.5004,-49.73648,0 112.5004,-44.73647,0 112.5004,-39.73648000000001,0 112.5004,-39.63649,0 112.5004,-34.63648,0 112.5004,-29.63648,0 112.5004,-24.63648,0 112.5004,-19.63648,0 112.5004,-14.63648,0 112.5004,-9.636480000000001,0 115.767,-9.636480000000001,0 115.767,-7.548669,0 116.3209,-7.548669,0 116.3209,-5.205232,0 112.5004,-5.205232,0 112.5075,-3.479589,0 112.2021,-3.4867,0 111.9465,-3.62872,0 111.7903,-3.600316,0 111.7335,-3.536407,0 111.7903,-3.387277000000001,0 111.7761,-3.074815,0 111.3358,-3.032208,0 111.0162,-3.124531,0 110.9097,-3.138724,0 110.7819,-3.103219000000001,0 110.6754,-3.138724,0 110.5049,-2.996703999999999,0 110.3061,-3.060612,0 110.1712,-3.032208,0 110.0646,-2.925694000000001,0 110.1143,-2.499607,0 110.0717,-2.258163,0 110.093,-2.030923,0 110.0504,-1.952803,0 109.9297,-1.917289,0 109.8729,-1.839177,0 109.8942,-1.746865,0 109.9865,-1.682947,0 110.0433,-1.363393,0 109.7664,-1.207152,0 109.461,-1.349182,0 109.39,-1.285273,0 109.4255,-1.022518,0 109.2622,-0.9018005000000001,0 109.106,-0.5467235,0 109.0207,-0.2484725,0 109.113,0.02848446,0 109.0775,0.2344225,0 108.8929,0.2912305000000001,0 108.829,0.4758655000000001,0 108.6586,0.7457215000000002,0 108.6657,0.8522364,0 108.7438,0.8593374000000001,0 108.9071,1.086578,0 108.9071,1.1789,0 109.0278,1.363535,0 109.0278,1.491362,0 109.0775,1.576584,0 109.2409,1.725713,0 109.3237,1.966571,0 109.3237,4.869648,0 105.1191,4.869648,0 105.132,2.002301,0 104.3122,1.346534,0 103.9265,1.256534,0 103.7561,1.185812,0 103.5311,1.234025,0 103.4122,1.301534,0 103.2418,1.45583,0 103.2097,1.584413,0 102.9461,1.719422,0 102.8175,1.831931,0 102.6921,1.831931,0 102.5378,2.01195,0 102.3418,2.134089,0 102.1489,2.208024,0 101.9303,2.391254,0 101.8499,2.362329,0 101.8371,2.442689,0 101.7567,2.561625,0 101.5188,2.654846,0 101.416,2.786633,0 101.2906,2.815568,0 101.2038,2.93129,0 101.2167,3.040577,0 101.2874,3.114522,0 101.1781,3.345965,0 100.9499,3.657770000000001,0 100.5031,3.976011000000001,0 100.5673,4.663917,0 100.5095,4.824638,0 100.4195,4.901795,0 100.3311,5.074793,0 100.1688,5.261129,0 100.1668,5.375079,0 99.72359,6.164054,0 99.64923,6.352362,0 99.63088000000001,6.427682,0 99.84238000000002,6.479837,0 100.1271,6.424946000000001,0 100.1714,6.476660000000001,0 100.1958,6.702497,0 100.2939,6.703892,0 100.3344,6.586946000000001,0 100.3875,6.536106,0 100.4989,6.523056000000001,0 100.6611,6.4475,0 100.7542,6.501942,0 100.7655,6.451946,0 100.8336,6.419447,0 100.8575,6.234444,0 100.9514,6.239717000000001,0 100.9897,6.277500000000001,0 101.0333,6.243894,0 101.1155,6.248888000000001,0 101.1216,6.187221,0 101.0755,6.150555,0 101.12,6.098337,0 101.1194,5.984441,0 101.1014,5.92695,0 101.0358,5.896106,0 100.9902,5.788053,0 101.1458,5.631111,0 101.2561,5.705837,0 101.2897,5.810553,0 101.3516,5.808339,0 101.57,5.916663,0 101.6589,5.860557,0 101.6916,5.763608,0 101.7625,5.788889,0 101.8339,5.743340000000001,0 101.9438,5.861942,0 101.9483,5.969448,0 102.0916,6.110559,0 102.0952,6.236135,0 102.5034,6.235613,0 107.5034,6.229232,0 112.5034,6.222851,0 112.5076,9.711738,0 112.5137,14.71173,0 110.1489,14.71173,0 107.9865,18.50449,0 107.9893,21.3862,0 108.0038,21.44677,0 108.0824,21.49174,0 107.99,21.54241,0 107.948,21.54195,0 107.8586,21.65083,0 107.7831,21.66693,0 107.5361,21.58638,0 107.4858,21.59778,0 107.4578,21.65833,0 107.3627,21.60526,0 107.3014,21.73111,0 107.2208,21.70832,0 107.0203,21.8225,0 107.0491,21.90722,0 107.0292,21.93666,0 106.9327,21.92749,0 106.9017,21.97056,0 106.8086,21.97361,0 106.7553,22.01527000000001,0 106.6794,21.97389,0 106.7033,22.08972,0 106.6653,22.19944,0 106.6958,22.2225,0 106.6583,22.32444,0 106.5655,22.34583,0 106.5517,22.45682,0 106.6072,22.60695,0 106.6386,22.61416,0 106.6761,22.57277,0 106.7128,22.58666,0 106.7669,22.74249,0 106.8217,22.80028,0 106.7614,22.79471,0 106.7072,22.865,0 106.648,22.85333,0 106.6005,22.90917,0 106.5203,22.93528,0 106.4194,22.87056,0 106.285,22.8586,0 106.25,22.87416,0 106.2294,22.95083,0 106.1347,22.98861,0 106.0153,22.98472,0 105.9872,22.93583,0 105.8725,22.91027,0 105.7244,23.04805,0 105.5775,23.05916,0 105.5657,23.09678,0 105.5544,23.16028,0 105.4458,23.28445,0 105.398,23.28167,0 105.3275,23.37803,0 105.2647,23.33416,0 105.2405,23.26055,0 105.0811,23.24222,0 104.9619,23.16556,0 104.9075,23.18027,0 104.8763,23.11084,0 104.825,23.10611,0 104.8611,22.93666,0 104.7314,22.81666,0 104.6044,22.80972,0 104.5836,22.84472,0 104.37,22.68583,0 104.2619,22.73777,0 104.2672,22.82919,0 104.1116,22.79972,0 104.0331,22.68527,0 104.0097,22.52424,0 103.9644,22.49911,0 103.6553,22.7825,0 103.6178,22.78,0 103.5636,22.70138,0 103.5842,22.64416,0 103.5224,22.58437,0 103.4569,22.64249,0 103.4305,22.74388,0 103.3289,22.79500000000001,0 103.28,22.67304,0 103.1839,22.62888,0 103.1556,22.58666,0 103.1708,22.54694,0 103.0761,22.49833,0 103.0725,22.44749,0 103.0305,22.43555,0 102.9286,22.48194000000001,0 102.8628,22.60139,0 102.6103,22.72278,0 102.5603,22.69749,0 102.5428,22.74889,0 102.4797,22.77389,0 102.4261,22.68832,0 102.383,22.67749,0 102.4164,22.63583,0 102.2697,22.49166,0 102.2533,22.41471,0 102.1867,22.43,0 102.1407,22.39628,0 102.1147,22.43389000000001,0 102.0308,22.45527,0 101.92,22.43611,0 101.8755,22.38305,0 101.8,22.48943,0 101.738,22.49694,0 101.6719,22.44639000000001,0 101.6805,22.37917,0 101.6269,22.28166,0 101.57,22.27917,0 101.5389,22.24417,0 101.5969,22.16166,0 101.5744,22.10917,0 101.6255,21.97193,0 101.7086,21.92889,0 101.7847,21.825,0 101.7525,21.72443,0 101.7831,21.65083,0 101.8331,21.62138,0 101.763,21.57833,0 101.7775,21.515,0 101.7489,21.48194,0 101.7392,21.31639,0 101.8514,21.24916,0 101.838,21.21111,0 101.7822,21.20693,0 101.7872,21.14417,0 101.73,21.13972,0 101.67,21.19971,0 101.6075,21.18027,0 101.6153,21.22833,0 101.5686,21.24749,0 101.2819,21.18027,0 101.2283,21.31222,0 101.2653,21.37611,0 101.1936,21.41277,0 101.2164,21.54556,0 101.1482,21.57264,0 101.1686,21.63028,0 101.1053,21.77138,0 100.8867,21.68333,0 100.6667,21.47943,0 100.485,21.45833,0 100.43,21.52861,0 100.3789,21.53889,0 100.2039,21.43535,0 100.1239,21.50472,0 100.1061,21.58333,0 100.1719,21.65388,0 100.125,21.69056000000001,0 100.0022,21.69611,0 99.97360000000001,21.73388,0 99.94193000000001,21.825,0 99.99054,21.95833,0 99.96805000000002,22.05027,0 99.88055,22.06145,0 99.84388,22.02332,0 99.74915,22.07527,0 99.70914999999999,22.03972,0 99.64444,22.09917,0 99.52164999999999,22.1,0 99.46555000000002,22.13583,0 99.35942,22.09306,0 99.23248,22.11444,0 99.16277,22.15916,0 99.20055000000001,22.17333,0 99.18221000000001,22.195,0 99.27054,22.34777,0 99.26082,22.39111,0 99.38442000000001,22.50389,0 99.38499,22.60333,0 99.32805000000001,22.74472,0 99.44053,22.865,0 99.44553000000003,22.94972,0 99.51416,22.91222000000001,0 99.56638000000001,22.93805,0 99.50388,23.08972,0 99.35638,23.13499,0 99.11109999999999,23.10194,0 99.06137000000001,23.12361,0 99.05888,23.1675,0 98.88136,23.18805,0 98.94025000000002,23.31361,0 98.87886,23.32972,0 98.91859000000001,23.36472,0 98.91470000000001,23.41722,0 98.87997,23.4836,0 98.82471000000001,23.48083,0 98.80609000000001,23.54167,0 98.88582,23.61333000000001,0 98.81915000000001,23.70556,0 98.81554000000001,23.76722,0 98.68858000000002,23.80139000000001,0 98.70387000000002,23.95138,0 98.67720000000001,23.96805,0 98.89391000000001,24.09795,0 98.89072,24.16008,0 98.59703,24.07648,0 98.54462,24.1257,0 98.44659000000002,24.13496,0 98.35732000000002,24.10319000000001,0 98.22078000000001,24.12531,0 98.06448,24.0891,0 97.91249000000001,24.02666,0 97.85249000000002,23.95832,0 97.78831,23.94888,0 97.64998,23.8517,0 97.53637000000001,23.95056,0 97.72470000000001,24.11806,0 97.74970999999999,24.16999,0 97.72749,24.22499,0 97.75999000000002,24.2575,0 97.74165000000002,24.29027,0 97.6647,24.3061,0 97.66888,24.34166000000001,0 97.71776000000001,24.35833,0 97.66526,24.44416,0 97.53082000000001,24.44749,0 97.56248000000001,24.57111,0 97.55247,24.74304,0 97.68941000000001,24.83445,0 97.80025999999999,24.85139,0 97.73665000000003,24.91888,0 97.72581,25.06472,0 97.72331000000003,25.06944,0 97.78943,25.1375,0 97.83333,25.25916000000001,0 97.94108000000001,25.21416,0 98.06944,25.31444,0 98.10746,25.38722,0 98.1386,25.38694,0 98.12497999999999,25.50584,0 98.16804999999999,25.55027,0 98.16526,25.62277,0 98.32665000000002,25.55167000000001,0 98.38305,25.59195,0 98.39166,25.65666,0 98.53083,25.84555,0 98.63193,25.80111,0 98.71638000000002,25.87749,0 98.64166,25.98444,0 98.60525,25.98861,0 98.56805,26.12306,0 98.64888000000001,26.15166,0 98.66443,26.09833,0 98.73055000000002,26.17722,0 98.70110000000001,26.25195,0 98.66582,26.26056000000001,0 98.73748999999999,26.38694,0 98.77554000000001,26.60583000000002,0 98.75555,26.72417,0 98.77777,26.79167,0 98.7397,26.86416,0 98.76221,27.06445,0 98.70694,27.09778,0 98.69166,27.20193,0 98.73665,27.32917000000001,0 98.68552,27.46611,0 98.70471000000001,27.5825,0 98.66804999999999,27.61944,0 98.59471000000002,27.59971000000001,0 98.55110000000001,27.66693,0 98.52164999999999,27.64667,0 98.43248000000001,27.68944,0 98.40860000000001,27.55750000000002,0 98.36887,27.52944,0 98.31638,27.54194,0 98.22693000000002,27.73471,0 98.23248,27.82471,0 98.16692000000001,27.87055,0 98.20387000000001,27.91638,0 98.13082,27.96916,0 98.15776,28.11333,0 98.13621999999999,28.15632,0 98.06944,28.21138,0 98.01304,28.21472,0 98.01888,28.27832,0 97.90886999999999,28.37638,0 97.79694000000001,28.34916,0 97.71138000000001,28.51694,0 97.63136,28.48416,0 97.56944,28.54888,0 97.47083000000001,28.39111,0 97.46887000000001,28.29611,0 97.41054,28.2925,0 97.34887000000002,28.22278,0 97.13499000000002,28.38138,0 96.98471000000001,28.3325,0 96.88970999999999,28.37861,0 96.78415,28.37306,0 96.65387,28.46749,0 96.46053000000001,28.42194,0 96.40192,28.35111,0 96.39694000000002,28.35333,0 96.36165000000001,28.39278,0 96.26471000000001,28.41527,0 96.36638000000001,28.48082000000001,0 96.34526,28.55695,0 96.47832,28.63861,0 96.47083000000001,28.68833,0 96.60609000000001,28.73944,0 96.61582000000001,28.79027,0 96.51027000000001,28.96444,0 96.47581000000001,28.97472,0 96.51194000000001,29.07,0 96.34387,29.0325,0 96.16914,28.90361,0 96.14861000000001,28.94333,0 96.18222000000002,28.99527,0 96.11913,29.07916000000001,0 96.25665000000001,29.16444,0 96.34860000000001,29.18,0 96.39525999999999,29.25527,0 96.36249000000001,29.29055,0 96.28943,29.24139,0 96.23526000000001,29.25445,0 96.08389,29.46777000000001,0 95.59108999999999,29.255,0 95.53442,29.21167,0 95.54665,29.13861,0 95.49609000000001,29.14083000000001,0 95.46053000000001,29.05167,0 95.38777,29.03527,0 94.98471000000001,29.17361,0 95.00915999999999,29.12388,0 94.83335,29.16837,0 94.79227,29.2608,0 94.69736,29.33652,0 94.64751,29.33347,0 94.5812,29.23153,0 94.42776000000002,29.21551,0 94.23455,29.07348,0 94.15246000000002,28.9307,0 94.0211,28.83603,0 94.01243,28.74736,0 93.96173,28.6692,0 93.98059000000001,28.64518,0 93.85851000000001,28.69038,0 93.64729,28.64746,0 93.50206000000001,28.67916,0 93.49408,28.65192,0 93.35194000000001,28.61877000000001,0 93.28152000000002,28.49296,0 93.19270000000002,28.43213,0 93.23817000000001,28.38745,0 93.22205000000001,28.3193,0 93.04571,28.31026,0 93.01044000000003,28.24225,0 92.99153,28.26286,0 92.88734000000001,28.19872000000001,0 92.84125,28.1888,0 92.83004000000001,28.21276,0 92.75290000000001,28.16892,0 92.67525000000001,28.08071,0 92.72838000000002,28.03598,0 92.69942000000002,27.95555,0 92.45248000000001,27.82639,0 92.4147,27.86028,0 92.38666000000002,27.80472,0 92.35831000000003,27.84443,0 92.33110000000001,27.79472,0 92.26804,27.87472,0 92.25388,27.81582,0 92.11832000000003,27.8125,0 92.01443,27.73471,0 91.65776,27.76472,0 91.66721,27.94194,0 91.41943000000001,28.01056,0 91.31499,28.09667,0 91.21444,28.07305,0 91.08693,27.96999,0 91.01082000000001,27.98,0 90.90998999999999,28.04944,0 90.69942,28.09583,0 90.60804,28.06445,0 90.37053000000002,28.08027,0 90.35027,28.11222,0 90.39554000000001,28.19888,0 90.38165000000001,28.25139,0 90.00002000000002,28.32405,0 89.95027,28.29222,0 89.89388,28.30611,0 89.79276000000002,28.19416,0 89.6236,28.15833,0 89.48749000000001,28.05777,0 89.35999,27.87167000000001,0 89.22553000000001,27.795,0 89.12804,27.62333,0 89.12581,27.5675,0 88.97110000000001,27.47361,0 88.91692000000001,27.32083,0 88.78333000000001,27.43805,0 88.76331999999999,27.54333,0 88.83443,27.66111,0 88.88081,27.83667,0 88.83167,28.01334,0 88.62276,28.11805,0 88.53415,28.02917,0 88.14554,27.95527,0 88.12053,27.90471000000001,0 88.14279999999999,27.86605,0 87.86887,27.91888,0 87.79721000000001,27.83722,0 87.72054,27.805,0 87.66054,27.82888,0 87.59775999999999,27.81027,0 87.5461,27.84583,0 87.39360000000001,27.80916,0 87.40416999999999,27.86055,0 87.30609,27.81639,0 87.19275,27.82306,0 87.03943000000001,27.96083,0 86.93304000000002,27.95055,0 86.87415,28.01777,0 86.76416,28.02666,0 86.75805,28.08027,0 86.68637,28.11222,0 86.63666000000001,28.08555,0 86.57193,28.10583000000001,0 86.53498999999999,27.95499,0 86.44498,27.90805,0 86.22638000000001,28.00667,0 86.18359,28.16388,0 86.0936,28.08611,0 86.13721,27.93194,0 86.08942999999999,27.89083000000001,0 86.01443,27.88277,0 85.97304,27.91583,0 85.97803999999999,27.98972,0 85.91887,28.03333,0 85.88611,28.15167,0 85.74722,28.23693000000001,0 85.71832000000002,28.33943,0 85.61998,28.25111,0 85.45889,28.30166,0 85.37665,28.26361,0 85.10332,28.31639,0 85.10193,28.44666,0 85.18192999999999,28.53,0 85.18996,28.60332,0 85.05998,28.64111,0 84.95470000000002,28.54416,0 84.84663999999999,28.54445,0 84.7747,28.59888,0 84.70610000000001,28.60139,0 84.70663999999999,28.66055,0 84.63639000000001,28.70277000000002,0 84.48109,28.73667,0 84.39554,28.85917,0 84.23693000000002,28.91888000000001,0 84.2561,29.04222,0 84.20249,29.05695,0 84.15833000000001,29.13472,0 84.18552,29.20556,0 84.13667,29.21443,0 84.11388000000001,29.26194,0 83.91249000000001,29.28972,0 83.72054,29.22889,0 83.67192,29.16027,0 83.55277,29.18583,0 83.52748,29.26916,0 83.38054,29.41638,0 83.37916,29.46611,0 83.34610000000001,29.49666,0 83.28442,29.47443,0 83.28306000000001,29.55111000000001,0 83.19525,29.63083,0 83.11749,29.62056000000001,0 83.09276,29.58305,0 82.9922,29.6725,0 82.90860000000001,29.69444,0 82.85525000000001,29.66889000000002,0 82.71053000000001,29.78972,0 82.71554999999999,29.83194,0 82.65581,29.83527000000001,0 82.55775,29.94277,0 82.23109,30.07527,0 82.17776000000001,30.06361,0 82.15027000000001,30.12556,0 82.17859,30.19055,0 82.10721,30.23083,0 82.10054,30.34222000000001,0 81.80304,30.36361,0 81.63721,30.42444,0 81.55775,30.38194,0 81.54192000000001,30.33056,0 81.42110000000001,30.38528,0 81.37777,30.16667,0 81.29082,30.13055,0 81.30193,30.08361,0 81.22360000000001,30.01027,0 81.10971000000001,30.02472,0 81.02536000000001,30.20436,0 80.99888,30.26194,0 80.80386,30.32666,0 80.75194999999999,30.395,0 80.60693000000001,30.47472,0 80.53888000000001,30.46443,0 80.32193,30.56472,0 80.20699999999999,30.57552,0 80.22776,30.62193,0 80.18749,30.67583000000001,0 80.25027,30.73999,0 80.17831,30.79945,0 80.09415,30.78666000000001,0 80.04192000000001,30.84694,0 79.94981,30.8849,0 79.87916,30.95916,0 79.74887,30.98972,0 79.59943,30.93556,0 79.49413,31.02416,0 79.42249,31.0275,0 79.39324000000001,31.10106,0 79.31499,31.13305,0 79.28665,31.22333,0 79.24026000000001,31.24417,0 79.21748,31.34083,0 79.09248,31.4375,0 79.01859,31.41138,0 79.00555,31.32555,0 78.94193,31.36,0 78.87766000000001,31.29163,0 78.77054,31.305,0 78.79276000000002,31.44333,0 78.71278,31.49833,0 78.84387,31.60417,0 78.79053999999999,31.67472,0 78.74249,31.68499,0 78.69247,31.77417,0 78.74507,31.91036,0 78.7711,31.98111,0 78.68053000000001,32.03527,0 78.54471000000001,32.20943,0 78.47593999999999,32.24305,0 78.47832,32.34999,0 78.44971,32.38694,0 78.4772,32.42471,0 78.39722,32.53111,0 78.42054,32.5661,0 78.6386,32.57778,0 78.70443,32.6311,0 78.76055,32.63556,0 78.78027,32.47887,0 78.97110000000001,32.35082,0 79.09915,32.37638,0 79.13164999999999,32.47499,0 79.25317000000001,32.516,0 79.31386999999999,32.49665,0 79.45722000000001,32.60249,0 79.51443,32.67222,0 79.53027,32.75416,0 79.47083000000001,32.79388,0 79.45027,32.88221,0 79.35609,32.965,0 79.40637,33.16166,0 79.18387,33.22582,0 79.10359,33.21971,0 79.09137,33.27499,0 78.99638000000002,33.31499,0 78.93651,33.408,0 78.91582,33.62944,0 79.04082,33.63943,0 79.01944,33.75444000000001,0 78.94941,33.83221,0 78.90886999999999,33.97028,0 78.97527,34.01721,0 79.42915000000001,34.02166,0 79.5247,34.11805,0 79.51166000000001,34.19027,0 79.57805000000001,34.2086,0 79.53915000000002,34.42999,0 79.72275999999999,34.42054,0 79.7636,34.45499,0 79.7247,34.52999,0 79.84387,34.57444,0 79.8875,34.66276000000001,0 80.07138000000001,34.66945,0 80.10248,34.7747,0 80.20166999999999,34.89388,0 80.19026,35.08764,0 80.29803,35.2075,0 80.28943,35.33999,0 80.38666000000001,35.42528,0 80.38388000000001,35.51111,0 80.29776,35.50333,0 80.2741,35.52753,0 80.20663999999999,35.56193,0 80.14776999999999,35.45722,0 80.05749,35.40777,0 79.96416000000001,35.59583,0 79.91914,35.61749,0 79.85887,35.5661,0 79.81303,35.62943,0 79.69359,35.65083,0 79.63443000000001,35.75916,0 79.65886999999999,35.81916,0 79.58109000000002,35.86971,0 79.46193,35.85804,0 79.43803000000001,35.96054000000001,0 79.32832999999999,35.98638,0 78.93442,35.89249,0 78.74193,35.92027,0 78.62553,35.81666,0 78.29915,35.70194,0 78.17221000000001,35.61943,0 78.13694,35.54333,0 77.97248999999999,35.57417000000001,0 77.92470000000002,35.48972,0 77.82393,35.50133000000001,0 77.7011,35.46111,0 77.43137,35.47165,0 77.30803000000002,35.53472,0 77.18747999999999,35.52805,0 77.07555000000001,35.58777,0 76.91443,35.59777,0 76.86415,35.64888,0 76.77805000000001,35.65526,0 76.7122,35.715,0 76.58971000000001,35.75695,0 76.56416,35.91276,0 76.42165,35.8336,0 76.17693,35.81444,0 76.12387,35.97776,0 75.97275999999999,36.01165,0 75.92609,36.07721,0 75.92303,36.1236,0 75.99387,36.15971,0 75.99359,36.20833,0 76.04166000000001,36.23751,0 75.99082,36.31444,0 76.00499000000001,36.4586,0 75.85526000000002,36.66717,0 75.63916,36.76355,0 75.54637,36.76963,0 75.52303000000001,36.72906,0 75.45444000000001,36.73044,0 75.39722,36.91406000000001,0 75.14444,36.99987,0 74.95972,36.98012,0 74.90166000000001,36.91093,0 74.81749000000001,37.02176000000001,0 74.73943,37.02037,0 74.69081,37.06259,0 74.56543000000001,37.02782,0 74.40054000000001,37.13896,0 74.39722,37.17841,0 74.47942999999999,37.17925,0 74.52970999999999,37.24178,0 74.65109,37.23457,0 74.75110000000001,37.30375,0 74.8111,37.22041,0 74.91575,37.23733,0 74.95804,37.28183,0 75.09665,37.32186,0 75.11109999999999,37.38353,0 75.18747999999999,37.40658,0 74.94942,37.56298,0 74.90277,37.64715,0 74.99109000000001,37.78382,0 74.90277,37.84717,0 74.93665000000001,37.88884,0 74.90833000000001,37.91049,0 74.91361000000001,38.01772,0 74.8147,38.07883,0 74.79443000000001,38.30603,0 74.8647,38.38882,0 74.85664,38.47048,0 74.78859,38.52742,0 74.71833,38.5291,0 74.35471,38.67439,0 74.13415999999999,38.67326,0 74.11081,38.6216,0 74.06052,38.60465,0 74.06887999999999,38.53659,0 73.93665,38.54105000000001,0 73.81776000000001,38.6077,0 73.80219,38.64983,0 73.75776999999999,38.73021,0 73.7625,38.80076,0 73.70499,38.88076,0 73.85332,38.96909,0 73.72165,39.08244,0 73.6172,39.23215,0 73.66998,39.34409,0 73.65569000000001,39.45483,0 73.84805,39.47493000000001,0 73.90248,39.56966,0 73.95609000000002,39.59771000000001,0 73.91080000000001,39.7252,0 73.84444000000001,39.75576,0 73.84498000000002,39.8055,0 73.99442999999999,40.04605000000001,0 74.19803,40.11383,0 74.36219,40.09965,0 74.73054999999999,40.33966,0 74.88443,40.32771,0 74.81026000000001,40.4198,0 74.86026,40.51939000000001,0 75.05888,40.44772,0 75.16137000000001,40.47022,0 75.22554,40.44688,0 75.57805000000001,40.64799,0 75.62276,40.59799,0 75.64554,40.4866,0 75.69664,40.44494,0 75.66137000000001,40.39827,0 75.6972,40.29911,0 75.92886,40.30550000000001,0 75.97803999999999,40.36327,0 76.06805,40.39577,0 76.19414,40.37799,0 76.27748,40.45438,0 76.33859,40.34855,0 76.48693,40.43105,0 76.55304,40.54411,0 76.63943,40.60856,0 76.64971,40.74494,0 76.79359,40.86245,0 76.78194000000001,40.94077,0 76.87387,41.01411,0 77.05831000000001,41.04911,0 77.13805000000001,41.01606,0 77.37747,41.028,0 77.54359000000001,40.98661,0 77.68471,41.00189,0 77.85471,41.0694,0 78.08083000000001,41.04079,0 78.24623,41.16513,0 78.37997,41.28384,0 78.37553,41.37882,0 78.53109000000001,41.46384,0 78.63997999999999,41.483,0 78.69553000000001,41.54301,0 79.20749000000001,41.72828,0 79.30943000000001,41.79134,0 79.43192999999999,41.78216,0 79.65498,41.88218000000001,0 79.77858000000001,41.88717,0 79.87943,42.01635,0 80.25139,42.04245,0 80.28332,42.07968000000001,0 80.23403,42.19622,0 80.2811,42.21218,0 80.29387,42.25218,0 80.23831000000001,42.3633,0 80.21720999999999,42.45051,0 80.24249,42.49051,0 80.18221,42.56802,0 80.17192,42.6605,0 80.26066,42.81941,0 80.57861,42.89108,0 80.56135999999999,42.92635,0 80.44443,42.95468,0 80.37665000000001,43.02524,0 80.64637999999999,43.14135,0 80.74359,43.10775,0 80.8147,43.13997,0 80.76914000000001,43.28886,0 80.66054,43.32884,0 80.73859000000002,43.4719,0 80.55371,43.81323,0 80.43886999999999,43.99219000000001,0 80.43136,44.05303,0 80.36276000000001,44.12524000000001,0 80.39999000000002,44.26414,0 80.34887000000001,44.45024,0 80.40721000000002,44.6022,0 80.37497999999999,44.65607,0 80.51666,44.7072,0 80.52083,44.73248,0 80.26221,44.82443,0 80.00166,44.79776,0 79.87581000000002,44.86554,0 79.87109000000001,44.90497,0 80.12138,45.03942000000001,0 80.39166,45.04915,0 80.46915,45.1111,0 81.05444,45.16608,0 81.68831,45.35082,0 81.79776,45.30581,0 81.81581,45.20053,0 81.94804000000001,45.15748,0 82.29694000000001,45.23775,0 82.48165,45.12748,0 82.54943,45.12331,0 82.63554000000001,45.24053,0 82.64943,45.43026,0 82.3622,45.49552000000001,0 82.31493,45.5448,0 82.51082,45.90192,0 82.57332000000001,46.17305,0 82.70889,46.35415,0 82.87387000000001,46.76359999999999,0 83.01470999999999,46.96027,0 83.03276,47.20416,0 83.18571,47.21664,0 83.56499,47.0611,0 83.90000000000001,46.97721,0 84.11109999999999,46.97166000000001,0 84.29471,47.0075,0 84.53194000000001,46.97527000000001,0 84.67804,46.99361,0 84.77887,46.93166,0 84.73526,46.83694,0 84.75942000000001,46.82638,0 84.96361,46.86415,0 85.25776999999999,47.0511,0 85.52931,47.06016,0 85.57331000000002,47.17694,0 85.68665,47.21194000000001,0 85.70555,47.2836,0 85.70055000000001,47.38388000000001,0 85.60359,47.51943,0 85.62221,47.62111,0 85.53777,47.95999,0 85.61388,48.15833,0 85.75915999999999,48.38777000000001,0 85.86248999999999,48.42527000000001,0 86.23081999999999,48.42527000000001,0 86.34415,48.49527000000001,0 86.46362000000001,48.49361,0 86.59610000000001,48.53611,0 86.64499000000002,48.61639,0 86.78333000000002,48.72776000000001,0 86.75943,48.79361,0 86.82138000000001,48.84665,0 86.75277000000001,48.90028000000002,0 86.73054999999999,48.97222,0 86.87469,49.11083000000001,0 86.99887,49.09944,0 87.16748,49.13304,0 87.34821,49.09261,0 87.46693000000001,49.07972,0 87.52528,49.12915,0 87.8407,49.17296000000001,0 87.98859,49.17722000000001,0 88.1622,49.27222,0 88.13193,49.36833000000001,0 88.22083000000001,49.46166,0 88.65082,49.50055,0 88.73081999999999,49.45083,0 88.89999000000002,49.44332,0 88.8875,49.54361000000001,0 88.92886,49.54639,0 89.01331999999999,49.46360000000001,0 89.23054999999999,49.53667,0 89.19748,49.61111,0 89.22665000000001,49.63833,0 89.37221,49.57972,0 89.45972,49.64999000000001,0 89.71138000000001,49.71472,0 89.73609999999999,49.74889000000001,0 89.6511,49.79527000000002,0 89.67137,49.82083,0 89.6375,49.855,0 89.64270000000001,49.90482999999999,0 89.98526,49.97222000000001,0 90.05137000000003,50.0825,0 90.24443000000001,50.12027,0 90.37858,50.18582000000001,0 90.50973000000001,50.22444000000001,0 90.69859,50.21943,0 90.74860000000001,50.29749,0 90.87025,50.32638000000001,0 91.02970999999999,50.43306,0 91.44748,50.4711,0 91.46776000000001,50.52694000000001,0 91.60693000000001,50.57305,0 91.65886999999999,50.63722000000001,0 91.77222000000002,50.68999000000001,0 91.90556000000002,50.71472000000001,0 92.22137000000002,50.70556,0 92.29498,50.75361,0 92.32693,50.855,0 92.38361000000002,50.87471000000001,0 92.47275999999999,50.78527,0 92.5736,50.75778,0 92.66081,50.68166,0 92.76028,50.71249,0 92.80276000000001,50.79361,0 92.97500000000001,50.7911,0 92.99915,50.7514,0 92.97102,50.66649,0 93.03333000000001,50.6086,0 93.44886,50.62249000000001,0 93.55832000000001,50.58972000000001,0 94.27499,50.56944,0 94.3522,50.41499,0 94.38998000000001,50.21444000000001,0 94.51582999999999,50.16109,0 94.62276,50.02999,0 94.97609,50.0561,0 95.06608,49.96193000000001,0 95.42249000000001,49.95610000000001,0 95.49157,49.9077,0 95.53859,49.89472,0 95.59193000000001,49.94083,0 95.73526,49.95471000000001,0 95.83472,50.02499000000001,0 95.90721000000002,50.01415999999999,0 95.96026999999999,49.94415000000001,0 96.07444,50.00694,0 96.29443999999999,49.94665000000001,0 96.37916,49.89360000000001,0 96.54749,49.92583,0 96.59248000000001,49.86500000000001,0 96.72248999999999,49.9211,0 96.99693000000002,49.89415000000001,0 97.16943000000001,49.76167,0 97.31303,49.7311,0 97.58638000000001,49.84361000000001,0 97.59415000000001,49.92749,0 97.78609,49.96915,0 97.86388000000001,49.93277,0 98.10721000000002,50.04888,0 98.29221,50.29861,0 98.28554,50.41361000000001,0 98.32555000000001,50.51083000000001,0 98.27443,50.56055,0 98.16748,50.56889,0 98.06331000000002,50.63054000000001,0 97.96388,50.77832,0 98.01304,50.85583,0 97.87747,50.92972,0 97.83195000000001,50.98999000000001,0 97.9586,51.22332,0 97.93777,51.31249,0 98.06469000000001,51.46694,0 98.23235,51.45989000000001,0 98.25999,51.51583000000002,0 98.24802,51.5811,0 98.33527000000001,51.71804000000001,0 98.62442,51.77999000000001,0 98.70804000000001,51.82805,0 98.86942999999999,52.03861,0 98.88109,52.11416,0 98.94189,52.14068,0 98.91304,52.2011,0 98.81360000000001,52.18693,0 98.80721,52.27444,0 98.72248999999999,52.29527000000001,0 98.64444,52.26167000000001,0 98.67970000000001,52.36999,0 98.62553,52.42416,0 98.72083000000001,52.46054000000001,0 98.73522000000001,52.5118,0 98.81527,52.52054,0 98.82721000000001,52.53999000000001,0 98.78276,52.57333,0 98.80415000000001,52.61166,0 98.93248000000001,52.65444000000001,0 98.9408,52.79722000000001,0 98.86526000000001,52.79167,0 98.84444000000001,52.83194,0 98.93107999999999,52.925,0 99.20610000000002,52.8511,0 99.24997000000001,52.95378,0 98.98553,53.03444,0 99.06526000000001,53.10249,0 98.92360000000001,53.15388,0 98.8772,53.11944,0 98.68637000000001,53.15611000000001,0 98.60249000000002,53.10139000000001,0 98.29998999999999,53.10055,0 98.28581,53.22721,0 98.03027,53.25054000000001,0 97.96998000000002,53.29639000000001,0 97.96304000000001,53.35999,0 97.59972000000001,53.37138,0 97.58749,53.43888000000001,0 97.44107,53.46027000000002,0 97.33221,53.59999,0 97.12275,53.60944,0 96.88666000000001,53.72887000000001,0 96.72776,53.70665,0 96.68415,53.6386,0 96.60860000000001,53.67583,0 96.57165999999999,53.64554,0 96.5,53.68499000000001,0 96.20721,53.92638,0 96.04943,53.99554,0 96.08167000000003,54.05749000000001,0 96.05332,54.09389000000002,0 95.98803000000001,54.08721,0 95.91805000000002,54.15999,0 95.65860000000002,54.25943,0 95.66303000000004,54.33999,0 95.70526,54.37999,0 95.79082,54.42082000000001,0 95.91577000000002,54.3956,0 95.98665,54.44833,0 95.97638000000001,54.47721000000001,0 96.05582,54.5761,0 96.21720999999999,54.54084,0 96.31582,54.57139000000001,0 96.57083,54.51250000000001,0 96.55053,54.58082,0 96.58888,54.65972,0 96.56192,54.69694,0 96.67359999999999,54.80222000000001,0 96.71527,54.92138000000001,0 96.57777,55.02028,0 96.68692,55.08083,0 96.65192,55.0986,0 96.69721,55.17276,0 96.70055000000001,55.25528,0 96.92027,55.32472,0 96.84137000000001,55.43749,0 96.85526000000002,55.47776,0 96.81721,55.55915,0 96.85582000000001,55.59444000000001,0 96.74638,55.65138000000001,0 96.82471,55.69749,0 96.75526000000002,55.74722,0 96.82111,55.79166,0 96.96971000000001,55.85583,0 96.98054999999999,56.02221999999999,0 97.0611,56.02556,0 97.12361,56.11639,0 97.35220000000001,56.04861,0 97.43248,56.09583000000001,0 97.42859,56.14666,0 97.58693000000001,56.18943000000001,0 97.55663000000001,56.3875,0 97.88193,56.39249000000002,0 97.87943,56.55944,0 97.74498,56.55860000000001,0 97.77528000000002,56.6075,0 97.75193000000003,56.64054,0 97.78581,56.66415000000001,0 97.75027000000002,56.70417000000001,0 97.77609,56.78054,0 97.48581000000003,56.81028,0 97.48055000000001,56.89111000000001,0 97.62302,56.95528000000001,0 97.47693000000001,56.96444,0 97.34009000000002,57.04317,0 97.91721,57.80694,0 98.76944,57.79166000000001,0 98.85415999999999,57.72804,0 98.97415,57.72165,0 99.08414999999999,57.77416,0 99.42165000000001,57.79333000000001,0 99.49442999999999,57.93415,0 99.61971,58.00166,0 99.61582,58.04805000000001,0 99.68193000000001,58.08111,0 99.80138000000001,58.05721,0 100.0441,58.07944,0 100.0775,58.0561,0 100.0767,57.96111,0 100.123,57.86554,0 100.2744,57.80915,0 100.2664,57.76139,0 100.373,57.7311,0 100.328,57.68250000000001,0 100.3664,57.67556,0 100.3461,57.63833,0 100.3958,57.59887,0 100.4036,57.54804,0 100.6505,57.47444,0 100.7419,57.40193000000001,0 100.855,57.465,0 100.9112,57.44835,0 100.9628,57.50083000000001,0 100.9391,57.51583,0 100.9536,57.5611,0 100.8461,57.60999000000001,0 100.7969,57.66833000000001,0 100.8911,57.77361,0 100.84,57.80083000000001,0 100.8505,57.84222,0 100.8077,57.88611,0 100.953,57.86694000000001,0 101.0353,57.89804000000001,0 101.0281,57.92249000000002,0 101.1394,57.98249,0 101.1583,58.02471000000001,0 101.1608,58.06055,0 101.105,58.10528,0 101.1097,58.11528000000001,0 101.2161,58.14444,0 101.2764,58.21861000000001,0 101.5347,58.22471,0 101.685,58.41888,0 102.1944,58.52527000000001,0 102.3105,58.64527,0 102.5114,58.71667,0 102.5669,58.79694,0 102.54,58.89777000000001,0 102.4369,58.94304000000001,0 102.4508,59.00111,0 102.4044,59.07249000000001,0 102.4433,59.14471,0 102.5353,59.19166000000001,0 102.8286,59.21194,0 102.8717,59.26527999999999,0 103.0155,59.32193,0 103.0822,59.26416,0 103.1644,59.25999000000001,0 103.2064,59.28972,0 103.3264,59.26082000000002,0 103.338,59.22721,0 103.2366,59.2286,0 103.2444,59.17944,0 103.3892,59.02138000000002,0 103.5383,58.98611,0 103.5919,58.95193,0 103.5928,58.91304,0 103.8933,58.90555,0 103.9542,58.84859,0 103.9458,58.76582,0 103.8558,58.73498000000001,0 103.9922,58.73555,0 104.0444,58.77304,0 104.2364,58.75866000000001,0 104.2836,58.71305000000001,0 104.3991,58.69915000000001,0 104.3936,58.64526,0 104.4583,58.60054,0 104.5233,58.63832000000001,0 104.5828,58.61415,0 104.6314,58.64804,0 104.795,58.66859,0 104.8733,58.72164000000001,0 104.7558,58.81526000000001,0 104.8386,58.89443,0 105.085,58.99277,0 105.1439,59.04582,0 105.3628,59.09499,0 105.3019,59.14861000000001,0 105.3319,59.1986,0 105.2961,59.26111,0 105.1841,59.31055000000001,0 105.2455,59.35082,0 105.2094,59.40192,0 105.2136,59.40777,0 105.2308,59.42527000000001,0 105.183,59.47305,0 104.9853,59.46028,0 104.8828,59.52527,0 104.8561,59.55110000000001,0 104.8769,59.58582,0 104.8005,59.6361,0 104.7944,59.69332,0 104.8922,59.75027000000001,0 105.0191,59.77083,0 105.0191,59.83721,0 105.1853,59.86027000000001,0 105.3783,59.82083000000001,0 105.4952,59.84554000000001,0 105.4305,59.89388,0 105.4889,60.01194000000002,0 105.4208,60.10999000000001,0 105.4786,60.19249,0 105.4772,60.25443,0 105.3758,60.24917000000001,0 105.2967,60.29860999999999,0 105.2219,60.27528,0 104.8772,60.30388000000001,0 104.7642,60.38221,0 104.6539,60.40249,0 104.6764,60.42499999999999,0 104.6414,60.49416000000002,0 104.568,60.54084,0 104.5577,60.61721,0 104.5167,60.63332,0 104.6022,60.74333000000001,0 104.4569,60.83972,0 104.6344,60.89388,0 104.5597,60.94554000000001,0 104.6144,60.97639,0 104.6,61.00054,0 104.6419,61.03804,0 104.6003,61.05416,0 104.6147,61.07444,0 104.7103,61.08999000000001,0 104.835,61.17776000000001,0 105.0119,61.17749,0 104.9628,61.26498999999999,0 104.9314,61.26944,0 104.9272,61.31944,0 104.785,61.34943,0 105,61.40661000000001,0 105.2758,61.41999,0 105.2736,61.47805000000002,0 105.3664,61.53278,0 105.5575,61.56554000000001,0 105.5925,61.61721,0 105.6947,61.64943,0 105.8028,61.61776,0 105.8647,61.67389,0 105.9491,61.68333,0 105.8894,61.82305000000001,0 106.0431,61.90277,0 106.0478,61.9086,0 106.2,61.99749000000001,0 106.4133,62.02750000000001,0 106.3789,62.06528,0 106.4097,62.08472,0 106.373,62.10277,0 106.4475,62.18581,0 106.4477,62.24167,0 106.3192,62.26194000000001,0 106.3939,62.31777000000001,0 106.4792,62.31721000000002,0 106.4275,62.38388,0 106.5308,62.37249000000001,0 106.7144,62.47721000000001,0 106.6533,62.53833,0 106.7072,62.58277,0 106.8144,62.59972,0 106.4691,62.68166000000001,0 106.5258,62.74722000000001,0 106.4678,62.77694,0 106.4447,62.84361,0 106.2161,62.8961,0 106.2194,62.94194000000001,0 106.1561,62.9586,0 106.1875,62.98611,0 106.1216,63.05833000000001,0 106.3033,63.07277,0 106.4108,63.11527000000001,0 106.3836,63.1461,0 106.433,63.20055,0 106.3583,63.21999,0 106.3619,63.27139,0 106.4441,63.33111,0 106.6741,63.29667,0 106.7364,63.3686,0 106.5069,63.49166000000002,0 106.685,63.61361000000001,0 106.6283,63.69833,0 106.7972,63.84415,0 106.6803,63.92499,0 106.7028,63.98555000000002,0 106.8603,63.97721,0 107.0714,63.88528,0 107.1819,63.95361,0 107.2911,63.95083,0 107.3592,63.86775999999999,0 107.5628,63.85695000000001,0 107.6219,63.91361000000001,0 107.6194,63.9575,0 107.6792,63.98999,0 108.2619,63.99083000000002,0 108.3222,64.03943,0 108.2211,64.20833,0 108.15,64.24082,0 108.1253,64.24054,0 108.0265,64.22346,0 108.14,64.26221,0 108.3075,64.24915,0 108.3569,64.30081,0 108.4614,64.28581,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +112.8831,77.93326,0 112.5097,77.85098000000001,0 112.5144,79.90003,0 112.5259,84.90002,0 112.5374,89.90000000000001,0 113.5351,89.90000000000001,0 114.5329,89.90000000000001,0 115.5306,89.90000000000001,0 116.5284,89.90000000000001,0 117.5262,89.90000000000001,0 118.5239,89.90000000000001,0 119.5217,89.90000000000001,0 120.5195,89.90000000000001,0 121.5172,89.90000000000001,0 122.515,89.90000000000001,0 123.5128,89.90000000000001,0 124.5105,89.90000000000001,0 125.5083,89.90000000000001,0 126.5061,89.90000000000001,0 127.5038,89.90000000000001,0 127.5198,84.90002,0 127.5317,81.16092,0 122.6489,80.08503,0 117.766,79.00914,0 112.8831,77.93326,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +09:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(9);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0900</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +131.1522,-31.47416,0 131.7691,-31.72222,0 132.0908,-31.92111,0 132.2039,-32.03168,0 132.4142,-32.01167,0 132.4794,-32.03251,0 132.4508,-31.97277,0 132.5491,-31.93889,0 132.7644,-31.95084,0 132.8355,-31.97972,0 132.9774,-32.11306,0 133.0591,-32.11473,0 133.1447,-32.20583,0 133.1855,-32.18666,0 133.2672,-32.22084000000001,0 133.3041,-32.19694,0 133.4172,-32.21334,0 133.5382,-32.32338,0 133.7763,-32.43159,0 133.8505,-32.54055,0 133.9055,-32.54501,0 133.9314,-32.50084,0 133.9938,-32.50611,0 134.0355,-32.46389,0 134.143,-32.4575,0 134.2822,-32.60000000000001,0 134.2969,-32.68028,0 134.2241,-32.78389,0 134.2144,-32.81306,0 134.1941,-32.79029,0 134.2177,-32.72834,0 134.0683,-32.71667,0 134.1294,-32.84277,0 134.0611,-32.92139,0 134.1947,-32.95223,0 134.2039,-33.01112,0 134.1478,-33.0275,0 134.2355,-33.05140000000001,0 134.2691,-33.15972,0 134.3375,-33.20403,0 134.4536,-33.4363,0 134.4445,-33.78327,0 134.6956,-33.95219,0 135.0562,-34.44981,0 135.1056,-34.59302,0 135.2188,-34.56417,0 135.3219,-34.62166,0 135.3488,-34.65305,0 135.3225,-34.69111,0 135.4747,-34.75111,0 135.6061,-34.89305,0 135.6241,-34.95361,0 135.6778,-34.95555,0 135.7213,-34.86861,0 135.7867,-34.86029,0 135.928,-34.95945,0 135.9541,-35.01028,0 136.4239,-35.18024,0 136.4753,-35.25469,0 136.4895,-35.57197,0 136.5725,-35.77633,0 136.5344,-35.9125,0 136.6678,-35.98778,0 136.7164,-36.07168,0 136.8466,-36.03083,0 136.9061,-36.06028,0 137.0161,-36.02695,0 137.1566,-36.04361,0 137.2219,-35.98361000000001,0 137.3544,-36.00195,0 137.4577,-36.08666,0 137.6141,-36.01472,0 137.6055,-35.92945,0 137.7494,-35.86777,0 137.9289,-35.87666,0 138.038,-35.92418,0 138.1369,-35.855,0 138.2088,-35.71732000000001,0 138.3009,-35.65432,0 138.5355,-35.65445,0 138.6555,-35.56056,0 138.8005,-35.53694,0 139.2314,-35.76361,0 139.573,-36.0925,0 139.8269,-36.57778,0 139.8616,-36.66639,0 139.8547,-36.82611,0 139.8003,-36.89472,0 139.6697,-36.96362,0 139.7314,-37.00973000000001,0 139.7411,-37.08278,0 139.7803,-37.10778,0 139.7836,-37.15361,0 139.7375,-37.17805,0 139.8144,-37.29973,0 140.0022,-37.50501000000001,0 140.0408,-37.48721,0 140.1283,-37.53612,0 140.1141,-37.58055,0 140.228,-37.65944,0 140.405,-37.92805,0 140.675,-38.06861,0 140.968,-38.06421,0 141.2825,-38.22084,0 141.4022,-38.3289,0 141.3842,-38.39889,0 141.4694,-38.37527,0 141.5472,-38.44305,0 141.5908,-38.39917,0 141.6536,-38.40222,0 141.6213,-38.31751,0 141.7436,-38.26751,0 141.9619,-38.29083,0 142.163,-38.40111,0 142.3813,-38.36362,0 142.4572,-38.40722,0 142.5023,-38.37652,0 142.5023,-43.37653,0 142.5023,-48.37652000000001,0 142.5023,-53.37653,0 142.5023,-58.37653000000002,0 142.5023,-63.37653000000001,0 142.5023,-68.37653,0 142.5023,-73.37653,0 142.5023,-75.57653000000001,0 142.5023,-80.57653000000001,0 142.5023,-85.57653000000001,0 142.5023,-88.17653,0 142.5023,-89.90000000000001,0 141.5026,-89.90000000000001,0 140.503,-89.90000000000001,0 139.5034,-89.90000000000001,0 138.5038,-89.90000000000001,0 137.5042,-89.90000000000001,0 136.5045,-89.90000000000001,0 135.5049,-89.90000000000001,0 134.5053,-89.90000000000001,0 133.5057,-89.90000000000001,0 132.5061,-89.90000000000001,0 131.5064,-89.90000000000001,0 130.5068,-89.90000000000001,0 129.5072,-89.90000000000001,0 128.5076,-89.90000000000001,0 127.508,-89.90000000000001,0 127.508,-84.89999,0 127.508,-82.02666000000001,0 127.508,-77.02666000000001,0 127.508,-72.02666000000001,0 127.508,-68.42665,0 127.508,-63.42665,0 127.508,-58.42665,0 127.508,-53.42665000000001,0 127.508,-48.42665000000001,0 127.508,-43.42665,0 127.508,-38.42665,0 127.508,-33.42665,0 127.508,-32.22667,0 127.7539,-32.1286,0 128.0039,-32.09223,0 128.2483,-32.01084,0 129.0003,-31.69261,0 130.1489,-31.57916000000001,0 130.838,-31.60584,0 131.1522,-31.47416,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +140.948,46.03066,0 145.5292,44.44866,0 145.6765,43.53654,0 145.8775,43.38366,0 145.8579,43.31955,0 145.8469,43.2832,0 145.5455,43.11722,0 144.8695,42.9269,0 144.7778,42.92555,0 144.4636,42.93083,0 144.325,42.99805,0 144.0244,42.92139,0 143.8928,42.84916,0 143.5955,42.61971000000001,0 143.3738,42.36167,0 143.3297,42.28167,0 143.3169,42.04277,0 143.2463,41.92527,0 142.943,42.10665,0 142.501,42.25373,0 142.501,37.25373,0 142.501,32.25372,0 142.501,28.64826,0 137.5011,28.62327,0 135.5122,28.61333,0 135.5122,23.61333000000001,0 135.5122,19.23089,0 140.5122,19.23089,0 142.501,19.23089,0 142.501,14.23089,0 142.501,12,0 138.3636,12,0 137,11.16666,0 137,6.166665000000001,0 137,1.199996,0 141,-5.400000000000001e-007,0 141.0025,-2.607085,0 141.0035,-4.128462,0 141.007,-9.128467000000001,0 142.1419,-10.95666,0 142.1589,-11.14944,0 142.1241,-11.36722,0 141.9422,-11.95472,0 142.0289,-12.06221,0 141.9644,-12.04084,0 141.9175,-12.10528,0 141.8905,-11.98833,0 141.91,-11.95944,0 141.8524,-11.98445,0 141.7503,-12.205,0 141.8058,-12.2375,0 141.7161,-12.23305,0 141.6689,-12.38195,0 141.6658,-12.38666,0 141.593,-12.5575,0 141.6953,-12.54805,0 141.6544,-12.52833,0 141.6614,-12.44833,0 141.7444,-12.46111,0 141.6941,-12.50445,0 141.7589,-12.53222,0 141.7722,-12.57361,0 141.8191,-12.59695,0 141.9255,-12.56111,0 141.9753,-12.58944,0 141.8892,-12.60916,0 141.8297,-12.66278,0 141.9172,-12.71805,0 142.005,-12.72166,0 141.908,-12.75028,0 141.935,-12.91111,0 141.8905,-12.84639,0 141.8686,-12.70084,0 141.8158,-12.70361,0 141.8055,-12.67195,0 141.7597,-12.80805,0 141.6522,-12.9125,0 141.6299,-12.89528,0 141.5839,-12.98916,0 141.6883,-13.26083,0 141.6678,-13.34778,0 141.6297,-13.35416,0 141.5269,-13.57416,0 141.4661,-13.90667,0 141.6,-14.13445,0 141.5916,-14.26473,0 141.5203,-14.475,0 141.5677,-14.76889,0 141.6658,-15.03611,0 141.4605,-15.53611,0 141.3758,-15.92944,0 141.4269,-16.07917,0 141.3486,-16.21916,0 141.3269,-16.33195,0 141.2119,-16.6725,0 141.1299,-16.78083,0 141.0764,-16.80333,0 141.0511,-16.88389000000001,0 140.9491,-17.01362,0 140.8774,-17.37833,0 140.8352,-17.45027,0 140.758,-17.47778,0 140.7361,-17.52,0 140.3966,-17.67334,0 140.0514,-17.71806,0 139.9819,-17.70027,0 139.9308,-17.63139,0 139.8091,-17.57223,0 139.6219,-17.52556,0 139.4717,-17.40412,0 139.4543,-17.27115,0 139.4833,-17.18898,0 139.5816,-17.09876,0 139.5639,-17.02948,0 139.5075,-16.98919,0 139.4736,-16.83612,0 139.5574,-16.67661,0 139.7443,-16.50259,0 139.7379,-16.44136,0 139.5574,-16.3898,0 139.4543,-16.43814,0 139.3061,-16.45425,0 139.1562,-16.5896,0 139.1192,-16.78778,0 138.9894,-16.89061,0 138.8486,-16.87777,0 138.6466,-16.77973,0 138.4869,-16.78778,0 138.3039,-16.75112,0 138.1902,-16.705,0 138.0003,-16.55472,0 137.8605,-16.44,0 137.7355,-16.24916,0 137.4283,-16.15722,0 137.2488,-16.02194,0 137.0683,-15.93743,0 137.0997,-15.77525,0 137.0845,-15.66302,0 136.8828,-15.49533,0 136.7517,-15.55584,0 136.5764,-15.50668,0 136.4158,-15.62276,0 136.2791,-15.56722,0 136.2122,-15.39445,0 135.9369,-15.25361,0 135.7971,-15.14682,0 135.7612,-14.96609,0 135.7868,-14.90858,0 136.9212,-14.31743,0 136.9755,-14.26311,0 136.9308,-13.75824,0 136.7199,-13.64,0 136.6489,-13.01466,0 136.645,-12.9575,0 136.5392,-12.91639,0 136.5514,-12.88611,0 136.4843,-12.84594,0 136.4677,-12.78111,0 136.5397,-12.8175,0 136.5486,-12.74361,0 136.6186,-12.83584,0 136.6055,-12.72083,0 136.6808,-12.71029,0 136.6978,-12.62306,0 136.7683,-12.56972,0 136.7325,-12.48722,0 136.7986,-12.45667,0 136.7831,-12.545,0 136.9172,-12.3875,0 136.9794,-12.36139,0 136.9386,-12.35639,0 136.9336,-12.28694,0 136.8573,-12.21335,0 136.8317,-12.06468,0 136.6352,-11.74354,0 136.5952,-11.40484,0 136.7342,-11.20672,0 136.7838,-11.04376,0 136.7646,-11.00381,0 136.4738,-11.45757,0 136.2765,-11.5646,0 135.9633,-11.67485,0 135.0559,-11.9113,0 134.8282,-12.03977,0 134.8014,-12.04333,0 134.7661,-11.95055,0 134.6986,-11.9575,0 134.5941,-12.06611,0 134.415,-12.0575,0 134.2805,-11.97417,0 134.1855,-12.08277,0 134.1664,-11.97194,0 134.1997,-11.95861,0 134.1411,-11.88139,0 134.0841,-11.92805,0 134.045,-11.8425,0 133.9511,-11.91222,0 133.875,-11.89139,0 133.8352,-11.82695,0 133.9391,-11.75611,0 133.9136,-11.72722,0 133.8262,-11.72145,0 133.5198,-11.51235,0 133.479,-11.45118,0 132.5958,-10.95671,0 132.4684,-11.01023,0 132.346,-11.12238,0 131.9803,-11.11982,0 131.807,-11.1912,0 131.259,-11.1861,0 131.1188,-11.25238,0 130.8333,-11.31863,0 130.6982,-11.31863,0 130.5147,-11.27021,0 130.4051,-11.14532,0 130.3643,-11.16826,0 130.3516,-11.28041,0 130.2445,-11.33648,0 130.14,-11.47157,0 129.9998,-11.77998,0 130.0941,-11.84624,0 130.3133,-11.79272,0 130.4637,-11.85899,0 130.558,-12.07055,0 130.5766,-12.40278,0 130.6177,-12.43278,0 130.5875,-12.4575,0 130.6049,-12.49861,0 130.5733,-12.5825,0 130.6194,-12.60833,0 130.5536,-12.63029,0 130.598,-12.67666,0 130.6953,-12.7025,0 130.5855,-12.71472,0 130.505,-12.60221,0 130.5094,-12.65834,0 130.4433,-12.63083,0 130.3972,-12.68722,0 130.3558,-12.67,0 130.3408,-12.87611,0 130.235,-12.9475,0 130.1855,-12.9075,0 130.1408,-12.92416,0 130.1157,-13.14503,0 130.0907,-13.21581,0 130.2073,-13.22924,0 130.3264,-13.35389,0 130.2542,-13.32889,0 130.145,-13.45973,0 129.9861,-13.52778,0 129.9136,-13.51389,0 129.8861,-13.44528,0 129.8708,-13.50194,0 129.8275,-13.51945,0 129.7827,-13.69221,0 129.7844,-13.76305,0 129.8105,-13.75167,0 129.8152,-13.77779,0 129.7169,-13.86722,0 129.75,-13.93222,0 129.7327,-13.99472,0 129.6308,-14.04056,0 129.5977,-13.99945,0 129.6136,-14.05833,0 129.5889,-14.09722,0 129.4789,-14.04722,0 129.4925,-14.14223,0 129.4122,-14.23111,0 129.3632,-14.4277,0 129.4171,-14.63216,0 129.3895,-14.71341,0 129.0917,-14.90135,0 129,-14.87108,0 128.5417,-14.75778,0 128.3877,-14.79719,0 128.3259,-14.71017,0 128.2153,-14.69975,0 128.0697,-14.64584,0 128.0036,-14.56111,0 127.9453,-14.57639,0 127.9494,-14.52667,0 127.8855,-14.47277,0 127.8514,-14.48222,0 127.865,-14.42056,0 127.8247,-14.44444,0 127.8228,-14.38694,0 127.7605,-14.30055,0 127.6136,-14.13611,0 127.5044,-14.06334,0 127.4466,-14.05916,0 127.4278,-13.95666,0 127.3967,-13.965,0 127.3328,-13.90662,0 127.3328,-8.906616,0 127.3328,-7.298002000000002,0 125.441,-7.306758,0 125.441,-3.067723,0 124.3024,-3.067723,0 124.3024,-1.202193,0 127.1436,2.912129,0 127.5079,3.439736,0 127.5079,8.439740000000001,0 127.5079,13.43974,0 127.5079,18.43974,0 127.5079,23.43974,0 127.5079,23.5489,0 122.8135,23.56643,0 122.8135,27.5252,0 127.5079,27.5252,0 127.5079,32.5252,0 127.5079,32.96414,0 124.1097,32.96414,0 124.1097,37.96414,0 124.1097,39.48034,0 124.3702,40.0028,0 124.3736,40.09361,0 124.5547,40.24222000000001,0 124.7084,40.30505,0 124.7433,40.37,0 124.8328,40.40915,0 124.8805,40.47054,0 125.04,40.45722,0 125.0178,40.53082,0 125.248,40.60388,0 125.2861,40.64749,0 125.4058,40.62082,0 125.4642,40.7136,0 125.5447,40.7186,0 125.5641,40.77832,0 125.6803,40.76554,0 125.6531,40.82166,0 125.7619,40.88361,0 125.8214,40.86139,0 125.8786,40.89472,0 126.0169,40.89999,0 125.9775,40.91331,0 126.0733,40.97305,0 126.1322,41.04416000000001,0 126.1239,41.08111,0 126.2792,41.15277,0 126.4561,41.35722,0 126.5272,41.3575,0 126.5008,41.43194,0 126.5844,41.56860000000001,0 126.5633,41.61092,0 126.6151,41.66418,0 126.7222,41.69804000000001,0 126.6789,41.72444,0 126.6947,41.74027,0 126.7869,41.69222,0 126.7975,41.7311,0 126.9361,41.79444,0 126.9364,41.76305,0 127.0453,41.72444,0 127.078,41.68194,0 127.04,41.67194,0 127.057,41.64351,0 127.1786,41.58749,0 127.1022,41.53528,0 127.2872,41.50111,0 127.2708,41.4725,0 127.5391,41.46361,0 127.5572,41.42972,0 127.6475,41.40277,0 127.8583,41.40444,0 127.8739,41.43943,0 127.9814,41.42276,0 128.0008,41.44555,0 128.0208,41.4086,0 128.0475,41.41888,0 128.0441,41.38943,0 128.1061,41.39415,0 128.1127,41.36971,0 128.2044,41.41667000000001,0 128.3019,41.57778,0 128.1019,41.7961,0 128.0589,42.00332,0 128.2825,42.03916,0 128.4894,41.99555,0 128.7291,42.04695,0 128.9122,42.0211,0 128.9577,42.08111,0 129.0141,42.0942,0 129.2102,42.21721,0 129.1758,42.23499,0 129.2244,42.29639,0 129.1905,42.31333,0 129.2377,42.32194,0 129.24,42.37417,0 129.3441,42.41471,0 129.3419,42.44583,0 129.4422,42.43443000000001,0 129.5397,42.36361,0 129.5908,42.44526,0 129.7119,42.44415,0 129.725,42.56139,0 129.7608,42.60332,0 129.7427,42.64083,0 129.7875,42.675,0 129.7533,42.69415,0 129.7578,42.72971,0 129.8512,42.95998,0 129.9056,42.96999,0 129.8756,42.9897,0 129.9075,43.00581,0 129.9996,42.96142,0 130.1075,42.97833,0 130.1358,42.96221,0 130.1008,42.91194,0 130.2522,42.89249,0 130.2433,42.71833,0 130.4255,42.60222,0 130.4372,42.545,0 130.4725,42.55694,0 130.4614,42.59389,0 130.5147,42.60222,0 130.5675,42.44305,0 130.6044,42.42186,0 130.6591,42.37665,0 130.655,42.31388,0 130.6974,42.29221,0 130.7539,42.34749,0 130.7789,42.45194,0 130.8305,42.46388,0 130.8383,42.51832,0 130.8711,42.52333,0 130.7703,42.59638,0 130.7258,42.575,0 130.7366,42.61361,0 130.6733,42.64749,0 130.7108,42.68499,0 130.8411,42.6875,0 130.7861,42.64277,0 130.9528,42.645,0 130.93,42.61388,0 130.8191,42.62693,0 130.9253,42.59333,0 131.0228,42.645,0 131.1022,42.61804,0 131.1133,42.66444,0 131.1397,42.60917,0 131.1919,42.59527,0 131.1678,42.57138,0 131.2207,42.55566000000001,0 131.4794,42.62341,0 131.486,42.78592,0 131.8044,42.90532000000001,0 132.0598,43.06119,0 132.1858,43.00813,0 132.2953,42.74943,0 132.3549,42.70301000000001,0 132.4611,42.77265,0 132.6667,42.8224,0 132.853,42.73888,0 132.8878,42.80555,0 132.9905,42.82054,0 133.0775,42.7411,0 133.0008,42.72222,0 133.0236,42.68776,0 133.1583,42.68306,0 133.2269,42.74249,0 133.3747,42.7575,0 133.5944,42.84694,0 133.6602,42.85443,0 133.7105,42.81332,0 133.86,42.90387,0 133.9142,42.87555,0 134.115,42.99722,0 134.1883,43.07972000000001,0 134.4167,43.14833,0 134.6997,43.30111,0 134.7639,43.30694,0 134.9686,43.4586,0 135.1297,43.49888,0 135.2225,43.6386,0 135.2266,43.68749,0 135.1833,43.70943,0 135.2497,43.72915,0 135.273,43.68639,0 135.42,43.75417,0 135.5178,43.88499,0 135.4769,43.8761,0 135.4572,43.94332,0 135.5128,43.92139,0 135.5305,43.99166000000001,0 135.6319,44.04944,0 135.6483,44.15943,0 135.7216,44.23499,0 135.8005,44.26333,0 135.8847,44.39832,0 136.1844,44.51305,0 136.2491,44.67194,0 136.3336,44.70361,0 136.3075,44.73777000000001,0 136.3608,44.79027,0 136.4413,44.76832,0 136.4497,44.82695,0 136.5533,44.88889,0 136.5547,44.93833000000001,0 136.6522,45.02804,0 136.7622,45.08721,0 136.8167,45.19554,0 137.1414,45.37028,0 137.3383,45.57721,0 137.6569,45.78221,0 137.7037,45.85810000000001,0 140.948,46.03066,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +142.5194,79.90000000000001,0 142.5194,77.68817,0 138.667,77.71629,0 133.9557,76.04201000000001,0 131.8621,75.29801999999999,0 131.8621,71.38942,0 132.2701,66.40608,0 132.678,61.42275999999999,0 132.8709,59.06711,0 132.8588,59.02208000000001,0 132.9268,58.97153000000001,0 132.905,58.92134000000001,0 132.6281,58.88062,0 132.5582,58.80028999999999,0 132.5681,58.63096000000001,0 132.539,58.58086,0 132.404,58.50661,0 132.2431,58.51441000000001,0 132.1915,58.47281,0 132.2631,58.30735,0 132.0923,58.2122,0 132.1542,58.06819,0 131.732,58.18654,0 131.5646,58.1936,0 131.5448,58.17016000000001,0 131.5467,58.14072,0 131.6564,58.07726000000001,0 131.6874,58.0066,0 131.8208,57.9631,0 131.8702,57.83329000000001,0 132.1476,57.72349000000001,0 132.0838,57.63795,0 131.8647,57.60706,0 131.6878,57.45025,0 131.66,57.27824,0 131.5491,57.25599,0 131.2796,57.36280000000001,0 131.2694,57.28938,0 131.1433,57.21137000000001,0 131.1787,57.17673,0 131.3337,57.16616000000001,0 131.5165,57.0938,0 131.8783,56.76854,0 131.7752,56.62872,0 131.7346,56.49774000000001,0 131.5154,56.54887000000001,0 131.4501,56.48512000000001,0 131.3082,56.48672,0 131.182,56.41776000000001,0 131.1664,56.29863,0 130.974,55.9278,0 131.0924,55.7413,0 131.0922,55.68167,0 131.5875,55.7055,0 131.8277,55.67754,0 132.2958,55.75039,0 132.5187,55.73038,0 132.6783,55.64597,0 132.6778,55.5961,0 132.6198,55.5598,0 132.6032,55.50863,0 132.8499,55.45721000000001,0 132.9172,55.40487,0 132.7062,55.3788,0 132.5812,55.26958,0 132.4052,55.2324,0 132.3182,55.10742,0 132.102,55.05344000000001,0 131.8497,54.86316000000001,0 131.6391,54.78865,0 131.4381,54.76270000000001,0 131.2688,54.64863,0 131.2404,54.51491,0 131.1084,54.36098,0 131.0439,54.32481000000001,0 130.7881,54.30889,0 130.5956,54.16706,0 130.4401,53.95286000000001,0 130.4453,53.91442,0 130.5371,53.88297000000001,0 130.6684,53.88879,0 130.7704,53.82110999999999,0 130.8468,53.80831,0 131.0603,53.87516000000001,0 131.369,53.75986,0 131.4433,53.50145,0 131.4033,53.26408,0 131.4342,53.24406,0 131.7383,53.28881000000001,0 131.8331,53.2546,0 131.8363,53.20384,0 131.9606,53.16412,0 132.0767,53.2618,0 132.1492,53.28006000000001,0 132.7966,53.23391,0 132.9397,53.31208,0 133.1567,53.32175,0 133.2816,53.50229000000001,0 133.5405,53.55013,0 133.6865,53.48001000000001,0 133.7953,53.50423,0 134.045,53.46778000000001,0 134.4765,53.65137,0 134.8939,53.42523000000001,0 134.8608,53.39223,0 134.8063,53.39867,0 134.7772,53.35156000000001,0 134.8416,53.30129,0 134.8581,53.2211,0 134.6294,52.97923,0 134.572,52.87017000000001,0 134.5818,52.77556,0 134.7453,52.68349,0 134.605,52.58288,0 134.5947,52.53989,0 134.644,52.49686,0 134.5898,52.42293,0 134.1073,52.49435,0 133.9606,52.47187,0 133.4076,52.67120000000001,0 133.2485,52.66451,0 133.1741,52.56192000000001,0 133.2161,52.52879,0 133.1884,52.43139000000001,0 133.3646,52.27884,0 133.3551,52.22758,0 133.1079,52.16774000000001,0 132.7906,52.16892,0 132.5923,52.10018000000001,0 132.4668,51.94439000000001,0 132.3687,51.9752,0 132.2938,51.84838000000001,0 132.2201,51.81065,0 132.0319,51.81518,0 131.6162,51.66434000000002,0 131.3763,51.68487,0 131.4344,51.40771,0 131.4163,51.37443,0 131.1841,51.33065,0 131.1296,51.25358000000001,0 130.8823,51.1259,0 130.8353,51.0568,0 130.6651,50.99669,0 130.649,50.9649,0 130.7764,50.89289,0 130.5924,50.61773000000001,0 130.6539,50.59364000000001,0 130.7555,50.65919000000002,0 130.8691,50.65911,0 130.9657,50.59039000000001,0 130.9257,50.52943,0 130.84,50.48763,0 130.848,50.43361,0 131.0443,50.37701,0 131.2058,50.37281,0 131.4482,50.18406000000001,0 131.3611,50.08953,0 131.4545,49.94840000000001,0 131.4792,49.77464,0 131.4655,49.74212,0 131.3354,49.7038,0 131.3988,49.67189000000001,0 131.456,49.56964,0 131.4312,49.3426,0 131.3584,49.25607,0 131.0539,49.22533,0 130.9823,49.16279999999999,0 130.9003,48.97752000000001,0 130.5996,48.93345,0 130.5044,48.85194,0 130.418,48.8961,0 130.2289,48.86221,0 129.9164,49.02963,0 129.9127,49.08241,0 129.833,49.11796,0 129.8474,49.15796000000001,0 129.7327,49.19294,0 129.7219,49.26626,0 129.545,49.29318,0 129.5235,49.39124,0 129.4122,49.43176,0 129.3691,49.41396000000001,0 129.3535,49.3423,0 129.1997,49.39059000000001,0 129.0769,49.35279,0 128.9927,49.44165,0 128.8752,49.47776,0 128.7525,49.46804000000001,0 128.8025,49.55916,0 128.6689,49.61193,0 128.6259,49.56967000000001,0 128.5052,49.58694,0 128.1911,49.53416000000001,0 128.075,49.54222000000001,0 127.9744,49.60249000000001,0 127.8214,49.5911,0 127.6914,49.66721,0 127.6717,49.74889000000001,0 127.5419,49.78249000000001,0 127.5083,49.82,0 127.5314,49.91972000000001,0 127.4878,49.98055000000002,0 127.4883,50.04221,0 127.5697,50.12833,0 127.5861,50.20856,0 127.3366,50.31027,0 127.3542,50.41332,0 127.2894,50.46027000000001,0 127.3677,50.57083,0 127.28,50.68083,0 127.2972,50.73249,0 127.2175,50.78416,0 127.1272,50.91555,0 126.9305,51.05999000000001,0 126.8986,51.18665,0 126.9755,51.31360999999999,0 126.8611,51.30693999999999,0 126.8933,51.23915000000001,0 126.8161,51.26639000000001,0 126.8214,51.33028000000001,0 126.9019,51.33943,0 126.9153,51.37915,0 126.7905,51.42443000000001,0 126.8247,51.50139,0 126.8025,51.53694,0 126.6728,51.57471,0 126.7292,51.63833000000001,0 126.7214,51.7086,0 126.6414,51.74527,0 126.4422,51.98804,0 126.4616,52.02694,0 126.5122,52.03889,0 126.5572,52.13055,0 126.3064,52.20417,0 126.4364,52.28082000000001,0 126.3203,52.30832,0 126.3403,52.38942999999999,0 126.2675,52.4586,0 126.1977,52.46444,0 126.2094,52.50139000000002,0 126.0939,52.58804,0 126.0308,52.56333,0 125.9886,52.58222,0 125.973,52.64167,0 126.0642,52.67638,0 126.0355,52.7211,0 126.0955,52.76444,0 126.0508,52.78583,0 125.9705,52.76333,0 125.8592,52.85166000000001,0 125.8447,52.89943000000002,0 125.6469,52.87887000000001,0 125.6486,52.91276,0 125.7191,52.94222,0 125.7289,52.97943,0 125.623,53.05,0 125.5061,53.03832,0 125.4661,53.08388000000001,0 125.1342,53.19749000000001,0 124.9122,53.16972,0 124.8755,53.14388000000001,0 124.89,53.09471,0 124.8516,53.08915,0 124.805,53.13305,0 124.7058,53.13443000000001,0 124.6528,53.18581,0 124.4022,53.21221,0 124.2553,53.35665,0 124.1044,53.34083,0 123.9108,53.46471,0 123.6975,53.49583,0 123.6194,53.54277,0 123.5294,53.49944,0 123.5428,53.53388,0 123.4975,53.54694,0 123.4789,53.49917,0 123.2461,53.55305,0 123.1533,53.50054999999999,0 122.8244,53.45,0 122.41,53.43721000000001,0 122.3144,53.48777,0 122.0261,53.41397,0 121.83,53.41305,0 121.4738,53.32382,0 121.325,53.31833,0 121.2083,53.27055000000001,0 120.8639,53.27972000000001,0 120.2728,52.85777,0 120.0292,52.76805,0 120.0306,52.61721,0 120.073,52.59,0 120.1891,52.58028,0 120.4403,52.63722,0 120.7136,52.54472000000001,0 120.6867,52.43361000000001,0 120.6228,52.34026999999999,0 120.7592,52.23916,0 120.7828,52.15526,0 120.7531,52.07917,0 120.695,52.04860999999999,0 120.7128,51.98249000000001,0 120.6578,51.9175,0 120.4886,51.8772,0 120.1972,51.68610000000002,0 120.0969,51.66138,0 120.0491,51.54777000000001,0 119.983,51.49305,0 119.9566,51.38999,0 119.8914,51.37417,0 119.9208,51.33638,0 119.8097,51.26416,0 119.818,51.21416,0 119.7553,51.20750000000001,0 119.7805,51.1661,0 119.7467,51.0736,0 119.5225,50.90499,0 119.5022,50.74221000000001,0 119.2869,50.59389000000001,0 119.2422,50.44526,0 119.1353,50.38972,0 119.1983,50.34527,0 119.348,50.34833,0 119.368,50.30999,0 119.3289,50.27388000000001,0 119.3481,50.15948,0 119.3222,50.09722000000001,0 119.2141,50.01528,0 119.0996,49.99044,0 118.5833,49.92694,0 118.4647,49.82444,0 118.3855,49.81667000000001,0 118.3658,49.77832000000002,0 117.8958,49.55,0 117.872,49.51022,0 117.4944,49.62305,0 117.17,49.6525,0 116.7114,49.83046000000001,0 116.6155,49.93138,0 116.2461,50.02749000000001,0 116.0697,50.00278,0 115.7547,49.88443000000001,0 115.3964,49.90110000000001,0 115.2053,49.99833,0 115.0236,50.15193,0 114.8542,50.22832,0 114.6758,50.26415999999999,0 114.4439,50.24527,0 114.313,50.28416,0 114.1764,50.24833,0 113.8747,50.08693000000002,0 113.7825,50.08693000000002,0 113.5861,50.01639,0 113.2167,49.81917,0 113.1014,49.67888,0 113.0908,49.59859,0 112.7669,49.50083,0 112.4917,49.54138,0 111.9639,49.40694,0 111.6822,49.40665,0 111.498,49.34916000000001,0 111.3714,49.37416,0 110.9811,49.20083,0 110.7886,49.14944,0 110.6303,49.15249,0 110.4011,49.25500000000001,0 110.2369,49.16945,0 109.8503,49.18999,0 109.7394,49.23499000000001,0 109.5442,49.23249,0 109.3519,49.33694,0 109.2069,49.36028,0 108.6511,49.33173000000001,0 108.5275,49.34555,0 108.3589,49.44526000000001,0 108.2719,49.53555000000001,0 108.1375,49.55804,0 108.0419,49.61082,0 108.0319,49.67471,0 107.9475,49.68333,0 107.9892,49.74499,0 107.9553,49.85167,0 107.9806,49.94083,0 107.8133,49.94860000000001,0 107.7536,49.97955000000001,0 107.7989,50.02999,0 107.8008,50.09166000000001,0 107.7619,50.10471,0 107.7736,50.15749000000002,0 107.8205,50.20665000000002,0 107.8936,50.19249000000001,0 107.9375,50.22221,0 107.9855,50.33721,0 108.0761,50.35332,0 108.1325,50.31444,0 108.2042,50.41109,0 108.3386,50.40444000000001,0 108.4369,50.46249000000002,0 108.6039,50.45221999999999,0 108.6853,50.50417,0 108.6583,50.53777,0 108.5447,50.52777,0 108.4167,50.61361,0 108.1794,50.65915,0 108.0655,50.75804999999999,0 108.2515,50.83393,0 108.3322,51.04887999999999,0 108.4997,51.05915000000002,0 108.5747,51.11528,0 108.4542,51.22776000000001,0 108.3525,51.26528,0 108.4736,51.34499,0 108.547,51.47527,0 108.6208,51.51306,0 108.743,51.44583000000001,0 108.945,51.43999000000002,0 109.0636,51.50861,0 109.2522,51.36221000000001,0 109.3386,51.39138,0 109.38,51.34666,0 109.4289,51.41610000000001,0 109.6789,51.46305,0 110.0491,51.62000000000001,0 110.3947,51.57944,0 110.4205,51.52222,0 110.5447,51.49471,0 110.6961,51.50277,0 110.7817,51.57471,0 110.8639,51.59222,0 110.8414,51.66915000000002,0 111.0019,51.72222,0 111.015,51.78221000000001,0 111.1397,51.83527,0 111.198,51.82361000000001,0 111.6078,51.92778,0 112.0608,52.27833,0 112.1683,52.30389000000001,0 112.2089,52.24054,0 112.2964,52.25278000000001,0 112.4022,52.20694,0 112.4925,52.25000000000001,0 112.5389,52.34972000000001,0 112.9303,52.33665000000001,0 113.2733,52.47805000000001,0 113.5341,52.49416,0 113.5792,52.45027,0 113.738,52.57722,0 113.8978,52.63499,0 113.9542,52.62555,0 114.1236,52.71832,0 114.2422,52.82388,0 114.2144,52.89054000000001,0 114.2422,52.92749,0 114.2322,52.96527,0 114.3172,52.96721,0 114.2814,53.02139,0 114.3253,53.06777,0 114.2314,53.10361000000001,0 114.1269,53.07194,0 114.2017,53.19222,0 114.0925,53.2047,0 114.0464,53.22359,0 114.048,53.25249,0 113.9822,53.23305,0 113.94,53.28444,0 113.9639,53.31945,0 113.9433,53.35638,0 113.8322,53.38971999999999,0 113.8278,53.4575,0 113.8955,53.53472,0 113.8147,53.61644,0 113.9744,53.64444000000001,0 113.9686,53.69944000000001,0 113.9947,53.71694,0 114.1431,53.75610000000001,0 114.2961,53.74666000000001,0 114.3747,53.81249,0 114.4453,53.80249000000001,0 114.5031,53.80915000000001,0 114.5958,53.90277,0 114.9758,53.99249,0 115.0239,54.03082,0 115.0241,54.07027000000002,0 115.1494,54.12027000000001,0 115.1833,54.18444,0 115.4993,54.30138000000001,0 115.5092,54.36472000000001,0 115.6708,54.42388,0 116.185,54.50722,0 116.5228,54.46408,0 116.5372,54.50943,0 116.7119,54.52944,0 116.7144,54.56333000000001,0 116.77,54.59222,0 116.7697,54.66109,0 116.8739,54.70416,0 116.8694,54.7686,0 116.9239,54.79554,0 116.8778,54.90527,0 116.8892,54.94165,0 116.8264,54.97860000000001,0 116.8411,55.02138,0 116.7825,55.11028,0 116.6991,55.11472000000001,0 116.4694,55.22748,0 116.4616,55.30555,0 116.4022,55.36082,0 116.1144,55.34749000000002,0 116.0694,55.37944,0 115.9639,55.36555,0 115.93,55.39694,0 115.9672,55.44526000000001,0 115.8894,55.49889000000001,0 115.913,55.6461,0 115.8411,55.67222000000001,0 115.8683,55.74499,0 115.8419,55.82638000000001,0 115.898,55.91667000000001,0 115.7586,56.04111000000001,0 115.7855,56.07193000000001,0 115.7691,56.11694,0 115.8369,56.14943,0 115.7619,56.17416,0 115.6841,56.29694,0 115.6797,56.39721,0 115.7844,56.40193,0 115.7988,56.42968000000001,0 115.6755,56.49277,0 115.6819,56.56527000000001,0 115.4647,56.58971,0 115.4311,56.62823,0 115.443,56.66388,0 115.5903,56.71554,0 115.6136,56.78611,0 115.5864,56.87249000000001,0 115.7368,56.95655,0 115.8617,56.93277000000001,0 115.9628,56.86111,0 116.0417,56.86361,0 116.1211,56.81221000000002,0 116.3639,56.84860000000001,0 116.5594,56.7589,0 116.7386,56.76083000000001,0 116.8072,56.72388,0 116.9672,56.78111,0 117.0403,56.77638,0 117.0017,56.79943000000001,0 117.0222,56.81554,0 117.1355,56.80111,0 117.1933,56.87943,0 117.3311,56.9486,0 117.3642,56.91610000000001,0 117.3447,56.86693000000001,0 117.4153,56.85777,0 117.4103,56.82583000000001,0 117.4605,56.81694000000001,0 117.498,56.87137,0 117.5728,56.84722,0 117.57,56.93555000000001,0 117.6744,56.9747,0 117.6889,57.01444999999999,0 117.6783,57.06666,0 117.5969,57.15332,0 117.7452,57.19583000000001,0 117.7955,57.28722,0 117.7758,57.30249000000001,0 117.5964,57.32333,0 117.5464,57.25999,0 117.4742,57.23778,0 117.4264,57.25861,0 117.4444,57.32972,0 117.365,57.32778,0 117.3286,57.45443,0 117.2889,57.45833,0 117.2522,57.51333000000002,0 117.3105,57.56555,0 117.2878,57.61306,0 117.1378,57.63972,0 117.1783,57.73888000000002,0 117.0844,57.80416000000001,0 117.123,57.84277,0 117.31,57.84388000000001,0 117.4175,57.90666,0 117.3661,57.92749,0 117.3647,57.99554,0 117.3958,58.02610000000001,0 117.3725,58.0611,0 117.4139,58.08749000000001,0 117.4075,58.12721,0 117.44,58.15721,0 117.5739,58.13582,0 117.5906,58.18555,0 117.4714,58.28278,0 117.4792,58.31361,0 117.6644,58.3711,0 117.6722,58.40055,0 117.8308,58.4122,0 118.013,58.39305,0 118.0639,58.33832000000001,0 118.1354,58.38744,0 118.3072,58.36028000000001,0 118.4892,58.21111,0 118.5614,58.21444,0 118.6475,58.17028000000001,0 118.803,58.21666000000001,0 119.1084,58.22268,0 119.1317,58.29027,0 119.0655,58.36388,0 119.1261,58.40833000000001,0 119.1297,58.48249,0 118.763,58.61249,0 118.8269,58.61527,0 118.8875,58.69554000000001,0 118.8969,58.81471000000001,0 118.7867,58.8875,0 118.7922,58.9397,0 118.8539,58.94360000000001,0 118.8478,59.01441000000001,0 118.7369,59.02361000000001,0 118.7025,59.06554,0 118.7014,59.21305000000001,0 118.8514,59.28611,0 118.7553,59.33333,0 118.7633,59.38722000000001,0 118.7644,59.4011,0 118.3903,59.51777,0 118.3503,59.58888,0 118.2836,59.61055,0 118.0572,59.58333,0 117.9864,59.53638,0 117.94,59.45471,0 117.8836,59.44165,0 117.8189,59.44944000000001,0 117.7583,59.54084000000001,0 117.5989,59.47083,0 117.2361,59.52777,0 117.1966,59.56916,0 117.0844,59.58721,0 117.2008,59.65499,0 117.2275,59.82943,0 117.0572,59.90889,0 117.1139,59.96333,0 117.2333,59.95694,0 117.3089,59.99528,0 117.2975,60.01943,0 117.0486,60.03583,0 117.0444,60.08250000000001,0 116.9736,60.15777,0 116.84,60.24222,0 116.7322,60.25777,0 116.6008,60.35722000000001,0 116.078,60.40417,0 116.0032,60.45931000000002,0 115.8428,60.465,0 115.6755,60.52666,0 115.1891,60.42972000000001,0 115.0906,60.38276000000001,0 115.013,60.27167,0 114.8139,60.18054,0 114.6966,60.21998,0 114.5461,60.11388000000001,0 114.5211,60.04527,0 114.5667,59.99777000000001,0 114.3155,59.88361,0 114.1655,59.74582000000002,0 114.0433,59.74110000000001,0 114.0419,59.68054999999999,0 113.8492,59.68832,0 113.7836,59.60999,0 113.6089,59.58111000000001,0 113.5864,59.55915000000002,0 113.6097,59.49527000000001,0 113.4728,59.43860000000001,0 113.4211,59.37665,0 113.4869,59.33333,0 113.4558,59.29889,0 113.4694,59.26500000000001,0 113.2817,59.16109000000001,0 112.9714,59.13444000000001,0 112.9117,59.15499,0 112.8822,59.11833,0 112.7314,59.0872,0 112.6453,59.03249,0 112.5742,59.07168,0 112.5555,59.02665,0 112.6276,58.96118,0 112.5003,58.86999,0 112.4461,58.92805,0 112.46,59.16166,0 112.5378,59.23694,0 112.6386,59.26944000000001,0 112.6322,59.31082000000001,0 112.438,59.33028,0 112.2839,59.30305,0 112.3075,59.33666000000002,0 112.2728,59.365,0 112.3341,59.41694,0 112.2714,59.46638,0 112.2739,59.50444000000001,0 112.2214,59.50361,0 111.9933,59.34470000000001,0 111.9419,59.2711,0 111.7681,59.27805,0 111.6461,59.2061,0 111.4353,59.27388,0 111.3042,59.25694000000001,0 111.1822,59.18332000000001,0 111.0831,59.23944,0 110.9969,59.20805000000001,0 110.6927,59.25777000000001,0 110.5789,59.19166000000001,0 110.5917,59.13110000000001,0 110.5425,59.10556,0 110.5386,59.06221000000001,0 110.4819,59.03334,0 110.3522,59.03582,0 110.2783,58.98194,0 109.9355,59.00917,0 109.8203,58.97971,0 109.7411,59.07194000000001,0 109.6405,59.06166,0 109.6286,59.1511,0 109.5281,59.18027,0 109.5405,59.22083,0 109.5172,59.23082,0 109.5167,59.29193,0 109.2605,59.31694,0 109.245,59.36306,0 109.3558,59.41667000000001,0 109.2594,59.4511,0 109.5339,59.64555,0 109.4842,59.68721000000002,0 109.4947,59.75027000000001,0 109.6514,59.86193000000001,0 109.62,59.90305,0 109.7694,60.02887000000001,0 109.7164,60.05222000000001,0 109.6936,60.11193,0 109.7264,60.22610000000001,0 109.8069,60.25443,0 109.8055,60.31305,0 109.8789,60.33805,0 109.8611,60.37417000000001,0 109.9519,60.40359999999999,0 109.9164,60.45889,0 110.0219,60.51694,0 110.0389,60.55833,0 110.1178,60.57972,0 110.1222,60.62416,0 110.04,60.66915,0 110.2678,60.69194,0 110.2703,60.76222,0 110.2947,60.76999000000001,0 110.225,60.79610000000002,0 110.23,60.81916,0 110.448,60.97694000000001,0 110.4575,61.03667,0 110.5208,61.08138,0 110.483,61.15971,0 110.3972,61.18111,0 110.3211,61.14638000000001,0 110.1797,61.15639,0 110.0903,61.27027,0 109.8553,61.29139000000001,0 109.7875,61.32721000000001,0 109.8492,61.43054000000001,0 109.8247,61.47748,0 109.8653,61.49305,0 109.8742,61.53333000000001,0 109.7061,61.64804,0 109.7161,61.67666,0 109.6126,61.71394000000001,0 109.6389,61.76083000000001,0 109.5858,61.77443,0 109.5864,61.82333000000001,0 109.6116,61.89277000000001,0 109.8183,62.00363000000001,0 109.9036,62.11306,0 109.9997,62.16361,0 109.9333,62.23472,0 110,62.28220000000001,0 109.96,62.31639,0 109.9628,62.32249000000002,0 109.8911,62.35333,0 109.9247,62.39722,0 109.9036,62.41388,0 109.5442,62.4325,0 109.4711,62.40499,0 109.4205,62.45832,0 109.3158,62.47554000000001,0 109.2642,62.52332,0 109.2444,62.57277,0 109.3869,62.58306000000001,0 109.375,62.64583,0 109.4561,62.66637999999999,0 109.5039,62.77527,0 109.6505,62.82444000000002,0 109.6158,62.87609,0 109.4683,62.93388000000002,0 109.4811,62.9961,0 109.4553,62.99972000000001,0 109.4811,63.02777,0 109.4472,63.03611,0 109.4647,63.07749,0 109.4317,63.09361,0 109.46,63.15055,0 109.4061,63.18277,0 109.4564,63.21860000000001,0 109.3753,63.25528000000001,0 109.3917,63.32472,0 109.363,63.36193,0 109.2717,63.34722,0 109.2289,63.44027,0 109.0433,63.55416000000002,0 108.8566,63.53582000000002,0 108.5586,63.59972000000001,0 108.1314,63.56777,0 108.1164,63.62249,0 108.2703,63.67888,0 108.2886,63.80805,0 108.3716,63.82638,0 108.4661,63.78944000000001,0 108.6056,63.80749,0 108.6489,63.78582,0 108.7719,63.86472,0 108.6839,63.9086,0 108.7383,63.98082,0 108.49,64.1147,0 108.5441,64.1747,0 108.4769,64.20081999999999,0 108.5394,64.23248,0 108.4614,64.28581,0 108.3569,64.30081,0 108.3075,64.24915,0 108.14,64.26221,0 108.0265,64.22346,0 107.9933,64.16971,0 107.8508,64.18053999999999,0 107.5947,64.29915,0 107.3344,64.24442999999999,0 107.2366,64.28693,0 107.2342,64.33138,0 107.1022,64.33693,0 106.8864,64.41609,0 106.7533,64.41193,0 106.6733,64.49802,0 106.6042,64.51331999999999,0 106.5655,64.49582,0 106.5858,64.44443,0 106.3944,64.44331,0 106.1867,64.39639,0 106.0661,64.42081,0 106.1475,64.48526,0 106.0389,64.51609999999999,0 105.7925,64.47803999999999,0 105.8042,64.53055000000001,0 105.7261,64.57222,0 105.7494,64.61443,0 105.7039,64.65886999999999,0 105.8772,64.65804,0 105.8289,64.74193,0 105.8472,64.79165999999999,0 105.898,64.82138000000001,0 106.0097,64.81805,0 105.9491,64.89499000000001,0 106.0408,64.90470999999999,0 106.1058,64.85915,0 106.2233,64.85722,0 105.9748,65.00305,0 105.9986,65.03915000000001,0 106.1658,65.09027,0 106.1925,65.12858,0 106.2911,65.13889,0 106.3736,65.20499,0 106.5117,65.20387,0 106.4269,65.26138,0 106.4422,65.30888,0 106.6116,65.39276,0 106.8414,65.37608,0 106.8761,65.44108,0 106.9647,65.50443,0 106.6989,65.55109,0 106.715,65.59305999999999,0 106.6219,65.61053,0 106.6439,65.6322,0 106.4214,65.66692999999999,0 106.4153,65.70139,0 106.4711,65.74108,0 106.448,65.77415000000001,0 106.4764,65.80443,0 106.4483,65.82138000000001,0 106.4961,65.895,0 106.4667,65.93192999999999,0 106.4889,66.00471,0 106.353,66.15082,0 106.1833,66.15555999999999,0 106.1158,66.19859,0 106.1489,66.25776999999999,0 106.1008,66.37803,0 106.3269,66.47553000000001,0 106.2939,66.50221000000001,0 106.3058,66.55359000000001,0 106.2417,66.58167,0 106.2272,66.66052999999999,0 106.26,66.69942,0 106.1461,66.72693,0 106.1361,66.7561,0 106.1997,66.78333000000001,0 106.1875,66.80247,0 105.9889,66.79776000000001,0 106.075,66.86081,0 106.0683,66.89525999999999,0 105.8686,66.91804999999999,0 105.8189,66.94859,0 105.6436,66.94664,0 105.5283,67.02222,0 105.9053,67.04553,0 105.9955,67.07887,0 106.0539,67.16804999999999,0 106.2325,67.18498,0 106.5036,67.28637000000001,0 106.6678,67.24387,0 106.7989,67.33832,0 106.8766,67.84166999999999,0 106.8908,68.86026,0 106.1367,69.41971,0 106.2853,69.45833,0 106.4547,69.57249,0 106.5911,69.51666,0 106.8147,69.51361,0 107.6053,69.6636,0 107.9022,69.67498999999999,0 108.0181,69.70749000000001,0 108.0536,69.77970999999999,0 108.2366,69.85444,0 108.8725,69.84109000000001,0 108.9539,69.78526000000001,0 109.2261,69.77164999999999,0 109.3644,69.83832,0 109.4211,69.89276,0 109.3836,69.92944,0 109.5439,70.03498,0 109.3811,70.07944000000001,0 109.3828,70.11942999999999,0 109.3055,70.17776000000001,0 109.3205,70.21887,0 109.5695,70.265,0 109.633,70.31972,0 109.5817,70.35526000000002,0 109.6417,70.38499,0 110.2725,70.41721000000001,0 110.3036,70.44942,0 110.1405,70.47859,0 110.1461,70.52388000000001,0 110.0989,70.55776,0 110.1594,70.55247,0 110.1625,70.56554,0 110.1236,70.63612000000001,0 110.3236,70.70471000000001,0 110.5136,70.71693000000001,0 110.5031,70.77083,0 110.5383,70.79721000000001,0 110.7223,70.82165999999999,0 110.8808,70.79638000000001,0 111.0057,70.84365,0 111.3625,70.85526,0 111.4647,70.93864000000001,0 111.9772,70.98276,0 112.0775,71.06164,0 112.2561,71.00000000000001,0 112.385,71.0647,0 112.5975,71.10471,0 112.5219,71.16388000000001,0 112.6869,71.20277,0 112.7258,71.24359,0 112.6428,71.2897,0 112.5128,71.26582999999999,0 112.333,71.34333,0 112.0111,71.40694000000001,0 112.0122,72.13332,0 111.775,72.14499000000001,0 111.7436,72.20721,0 111.6242,72.21361,0 111.6686,72.26582999999999,0 111.5806,72.31944,0 111.3592,72.31693,0 111.0883,72.37220000000001,0 111.0758,72.4061,0 111.3036,72.47832,0 111.3155,72.51277,0 110.89,72.58110000000002,0 110.925,72.63193,0 110.7303,72.64472000000001,0 110.6322,72.78665000000001,0 110.7317,72.82999,0 110.6597,72.85305,0 110.6466,72.89554,0 110.8755,72.94859,0 110.8155,72.95359999999999,0 110.7778,73.00054,0 110.9417,73.03888000000001,0 110.8583,73.07805000000002,0 110.7978,73.17303,0 110.423,73.18997,0 110.6272,73.26888,0 110.5667,73.31944,0 110.1644,73.40248,0 110.5641,73.40637,0 110.8036,73.50832,0 110.8483,73.56554,0 111.0547,73.57165999999999,0 111.1178,73.59359000000001,0 111.1433,73.63777,0 111.0803,73.65860000000001,0 110.9143,73.69668,0 110.8775,73.72859,0 110.9169,73.76028,0 110.8934,73.76541,0 111.2942,73.85934,0 111.6379,73.93545,0 111.6826,74.06952,0 111.34,74.27808,0 111.3847,74.42704000000001,0 112.6509,74.76967999999999,0 113.8128,75.26125999999999,0 114.1406,75.88692,0 113.9618,76.43810000000001,0 113.351,76.87012,0 112.5083,77.24612000000001,0 112.5097,77.85098000000001,0 112.8831,77.93326,0 117.766,79.00914,0 122.6489,80.08503,0 127.5317,81.16092,0 127.5198,84.90002,0 127.5038,89.90000000000001,0 128.4423,89.90000000000001,0 129.3808,89.90000000000001,0 130.3193,89.90000000000001,0 131.2577,89.90000000000001,0 132.1962,89.90000000000001,0 133.1347,89.90000000000001,0 134.0732,89.90000000000001,0 135.0116,89.90000000000001,0 135.9501,89.90000000000001,0 136.8886,89.90000000000001,0 137.827,89.90000000000001,0 138.7655,89.90000000000001,0 139.704,89.90000000000001,0 140.6425,89.90000000000001,0 141.5809,89.90000000000001,0 142.5194,89.90000000000001,0 142.5194,84.90001,0 142.5194,79.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +09:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(9.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle0950</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +129,-14.87108,0 129.0917,-14.90135,0 129.3895,-14.71341,0 129.4171,-14.63216,0 129.3632,-14.4277,0 129.4122,-14.23111,0 129.4925,-14.14223,0 129.4789,-14.04722,0 129.5889,-14.09722,0 129.6136,-14.05833,0 129.5977,-13.99945,0 129.6308,-14.04056,0 129.7327,-13.99472,0 129.75,-13.93222,0 129.7169,-13.86722,0 129.8152,-13.77779,0 129.8105,-13.75167,0 129.7844,-13.76305,0 129.7827,-13.69221,0 129.8275,-13.51945,0 129.8708,-13.50194,0 129.8861,-13.44528,0 129.9136,-13.51389,0 129.9861,-13.52778,0 130.145,-13.45973,0 130.2542,-13.32889,0 130.3264,-13.35389,0 130.2073,-13.22924,0 130.0907,-13.21581,0 130.1157,-13.14503,0 130.1408,-12.92416,0 130.1855,-12.9075,0 130.235,-12.9475,0 130.3408,-12.87611,0 130.3558,-12.67,0 130.3972,-12.68722,0 130.4433,-12.63083,0 130.5094,-12.65834,0 130.505,-12.60221,0 130.5855,-12.71472,0 130.6953,-12.7025,0 130.598,-12.67666,0 130.5536,-12.63029,0 130.6194,-12.60833,0 130.5733,-12.5825,0 130.6049,-12.49861,0 130.5875,-12.4575,0 130.6177,-12.43278,0 130.5766,-12.40278,0 130.558,-12.07055,0 130.4637,-11.85899,0 130.3133,-11.79272,0 130.0941,-11.84624,0 129.9998,-11.77998,0 130.14,-11.47157,0 130.2445,-11.33648,0 130.3516,-11.28041,0 130.3643,-11.16826,0 130.4051,-11.14532,0 130.5147,-11.27021,0 130.6982,-11.31863,0 130.8333,-11.31863,0 131.1188,-11.25238,0 131.259,-11.1861,0 131.807,-11.1912,0 131.9803,-11.11982,0 132.346,-11.12238,0 132.4684,-11.01023,0 132.5958,-10.95671,0 133.479,-11.45118,0 133.5198,-11.51235,0 133.8262,-11.72145,0 133.9136,-11.72722,0 133.9391,-11.75611,0 133.8352,-11.82695,0 133.875,-11.89139,0 133.9511,-11.91222,0 134.045,-11.8425,0 134.0841,-11.92805,0 134.1411,-11.88139,0 134.1997,-11.95861,0 134.1664,-11.97194,0 134.1855,-12.08277,0 134.2805,-11.97417,0 134.415,-12.0575,0 134.5941,-12.06611,0 134.6986,-11.9575,0 134.7661,-11.95055,0 134.8014,-12.04333,0 134.8282,-12.03977,0 135.0559,-11.9113,0 135.9633,-11.67485,0 136.2765,-11.5646,0 136.4738,-11.45757,0 136.7646,-11.00381,0 136.7838,-11.04376,0 136.7342,-11.20672,0 136.5952,-11.40484,0 136.6352,-11.74354,0 136.8317,-12.06468,0 136.8573,-12.21335,0 136.9336,-12.28694,0 136.9386,-12.35639,0 136.9794,-12.36139,0 136.9172,-12.3875,0 136.7831,-12.545,0 136.7986,-12.45667,0 136.7325,-12.48722,0 136.7683,-12.56972,0 136.6978,-12.62306,0 136.6808,-12.71029,0 136.6055,-12.72083,0 136.6186,-12.83584,0 136.5486,-12.74361,0 136.5397,-12.8175,0 136.4677,-12.78111,0 136.4843,-12.84594,0 136.5514,-12.88611,0 136.5392,-12.91639,0 136.645,-12.9575,0 136.6489,-13.01466,0 136.7199,-13.64,0 136.9308,-13.75824,0 136.9755,-14.26311,0 136.9212,-14.31743,0 135.7868,-14.90858,0 135.7612,-14.96609,0 135.7971,-15.14682,0 135.9369,-15.25361,0 136.2122,-15.39445,0 136.2791,-15.56722,0 136.4158,-15.62276,0 136.5764,-15.50668,0 136.7517,-15.55584,0 136.8828,-15.49533,0 137.0845,-15.66302,0 137.0997,-15.77525,0 137.0683,-15.93743,0 137.2488,-16.02194,0 137.4283,-16.15722,0 137.7355,-16.24916,0 137.8605,-16.44,0 138.0003,-16.55472,0 138.0004,-21.55472,0 138.0005,-25.99945,0 141.0005,-25.99971,0 141.0027,-29.00021,0 140.9979,-34.00021,0 140.9979,-34.02403,0 140.968,-38.06421,0 140.675,-38.06861,0 140.405,-37.92805,0 140.228,-37.65944,0 140.1141,-37.58055,0 140.1283,-37.53612,0 140.0408,-37.48721,0 140.0022,-37.50501000000001,0 139.8144,-37.29973,0 139.7375,-37.17805,0 139.7836,-37.15361,0 139.7803,-37.10778,0 139.7411,-37.08278,0 139.7314,-37.00973000000001,0 139.6697,-36.96362,0 139.8003,-36.89472,0 139.8547,-36.82611,0 139.8616,-36.66639,0 139.8269,-36.57778,0 139.573,-36.0925,0 139.2314,-35.76361,0 138.8005,-35.53694,0 138.6555,-35.56056,0 138.5355,-35.65445,0 138.3009,-35.65432,0 138.2088,-35.71732000000001,0 138.1369,-35.855,0 138.038,-35.92418,0 137.9289,-35.87666,0 137.7494,-35.86777,0 137.6055,-35.92945,0 137.6141,-36.01472,0 137.4577,-36.08666,0 137.3544,-36.00195,0 137.2219,-35.98361000000001,0 137.1566,-36.04361,0 137.0161,-36.02695,0 136.9061,-36.06028,0 136.8466,-36.03083,0 136.7164,-36.07168,0 136.6678,-35.98778,0 136.5344,-35.9125,0 136.5725,-35.77633,0 136.4895,-35.57197,0 136.4753,-35.25469,0 136.4239,-35.18024,0 135.9541,-35.01028,0 135.928,-34.95945,0 135.7867,-34.86029,0 135.7213,-34.86861,0 135.6778,-34.95555,0 135.6241,-34.95361,0 135.6061,-34.89305,0 135.4747,-34.75111,0 135.3225,-34.69111,0 135.3488,-34.65305,0 135.3219,-34.62166,0 135.2188,-34.56417,0 135.1056,-34.59302,0 135.0562,-34.44981,0 134.6956,-33.95219,0 134.4445,-33.78327,0 134.4536,-33.4363,0 134.3375,-33.20403,0 134.2691,-33.15972,0 134.2355,-33.05140000000001,0 134.1478,-33.0275,0 134.2039,-33.01112,0 134.1947,-32.95223,0 134.0611,-32.92139,0 134.1294,-32.84277,0 134.0683,-32.71667,0 134.2177,-32.72834,0 134.1941,-32.79029,0 134.2144,-32.81306,0 134.2241,-32.78389,0 134.2969,-32.68028,0 134.2822,-32.60000000000001,0 134.143,-32.4575,0 134.0355,-32.46389,0 133.9938,-32.50611,0 133.9314,-32.50084,0 133.9055,-32.54501,0 133.8505,-32.54055,0 133.7763,-32.43159,0 133.5382,-32.32338,0 133.4172,-32.21334,0 133.3041,-32.19694,0 133.2672,-32.22084000000001,0 133.1855,-32.18666,0 133.1447,-32.20583,0 133.0591,-32.11473,0 132.9774,-32.11306,0 132.8355,-31.97972,0 132.7644,-31.95084,0 132.5491,-31.93889,0 132.4508,-31.97277,0 132.4794,-32.03251,0 132.4142,-32.01167,0 132.2039,-32.03168,0 132.0908,-31.92111,0 131.7691,-31.72222,0 131.1522,-31.47416,0 130.838,-31.60584,0 130.1489,-31.57916000000001,0 129.0003,-31.69261,0 129.0052,-26.69261,0 129.0059,-26.0049,0 129.0032,-21.0049,0 129.0006,-16.0049,0 129,-14.87108,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +10:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(10);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1000</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +157.5176,-9.204364,0 157.5176,-14.20437,0 157.5176,-19.20436,0 157.5176,-24.20437,0 157.5176,-29.20436000000001,0 157.5176,-34.20436,0 157.5176,-39.20436,0 157.5176,-39.30436,0 157.5176,-44.30436,0 157.5176,-49.30437,0 157.5176,-54.30436,0 157.5176,-59.00436,0 157.5176,-64.00436000000001,0 157.5176,-69.00436000000001,0 157.5176,-74.00436000000001,0 157.5176,-79.00436000000001,0 157.5176,-84.00436000000001,0 157.5176,-89.00436000000001,0 157.5176,-89.90000000000001,0 156.5791,-89.90000000000001,0 155.6407,-89.90000000000001,0 154.7022,-89.90000000000001,0 153.7638,-89.90000000000001,0 152.8253,-89.90000000000001,0 151.8868,-89.90000000000001,0 150.9484,-89.90000000000001,0 150.0099,-89.90000000000001,0 149.0715,-89.90000000000001,0 148.133,-89.90000000000001,0 147.1945,-89.90000000000001,0 146.2561,-89.90000000000001,0 145.3176,-89.90000000000001,0 144.3792,-89.90000000000001,0 143.4407,-89.90000000000001,0 142.5023,-89.90000000000001,0 142.5023,-88.17653,0 142.5023,-85.57653000000001,0 142.5023,-80.57653000000001,0 142.5023,-75.57653000000001,0 142.5023,-73.37653,0 142.5023,-68.37653,0 142.5023,-63.37653000000001,0 142.5023,-58.37653000000002,0 142.5023,-53.37653,0 142.5023,-48.37652000000001,0 142.5023,-43.37653,0 142.5023,-38.37652,0 142.4572,-38.40722,0 142.3813,-38.36362,0 142.163,-38.40111,0 141.9619,-38.29083,0 141.7436,-38.26751,0 141.6213,-38.31751,0 141.6536,-38.40222,0 141.5908,-38.39917,0 141.5472,-38.44305,0 141.4694,-38.37527,0 141.3842,-38.39889,0 141.4022,-38.3289,0 141.2825,-38.22084,0 140.968,-38.06421,0 140.9979,-34.02403,0 140.9979,-34.00021,0 141.0027,-29.00021,0 141.0005,-25.99971,0 138.0005,-25.99945,0 138.0004,-21.55472,0 138.0003,-16.55472,0 138.1902,-16.705,0 138.3039,-16.75112,0 138.4869,-16.78778,0 138.6466,-16.77973,0 138.8486,-16.87777,0 138.9894,-16.89061,0 139.1192,-16.78778,0 139.1562,-16.5896,0 139.3061,-16.45425,0 139.4543,-16.43814,0 139.5574,-16.3898,0 139.7379,-16.44136,0 139.7443,-16.50259,0 139.5574,-16.67661,0 139.4736,-16.83612,0 139.5075,-16.98919,0 139.5639,-17.02948,0 139.5816,-17.09876,0 139.4833,-17.18898,0 139.4543,-17.27115,0 139.4717,-17.40412,0 139.6219,-17.52556,0 139.8091,-17.57223,0 139.9308,-17.63139,0 139.9819,-17.70027,0 140.0514,-17.71806,0 140.3966,-17.67334,0 140.7361,-17.52,0 140.758,-17.47778,0 140.8352,-17.45027,0 140.8774,-17.37833,0 140.9491,-17.01362,0 141.0511,-16.88389000000001,0 141.0764,-16.80333,0 141.1299,-16.78083,0 141.2119,-16.6725,0 141.3269,-16.33195,0 141.3486,-16.21916,0 141.4269,-16.07917,0 141.3758,-15.92944,0 141.4605,-15.53611,0 141.6658,-15.03611,0 141.5677,-14.76889,0 141.5203,-14.475,0 141.5916,-14.26473,0 141.6,-14.13445,0 141.4661,-13.90667,0 141.5269,-13.57416,0 141.6297,-13.35416,0 141.6678,-13.34778,0 141.6883,-13.26083,0 141.5839,-12.98916,0 141.6299,-12.89528,0 141.6522,-12.9125,0 141.7597,-12.80805,0 141.8055,-12.67195,0 141.8158,-12.70361,0 141.8686,-12.70084,0 141.8905,-12.84639,0 141.935,-12.91111,0 141.908,-12.75028,0 142.005,-12.72166,0 141.9172,-12.71805,0 141.8297,-12.66278,0 141.8892,-12.60916,0 141.9753,-12.58944,0 141.9255,-12.56111,0 141.8191,-12.59695,0 141.7722,-12.57361,0 141.7589,-12.53222,0 141.6941,-12.50445,0 141.7444,-12.46111,0 141.6614,-12.44833,0 141.6544,-12.52833,0 141.6953,-12.54805,0 141.593,-12.5575,0 141.6658,-12.38666,0 141.6689,-12.38195,0 141.7161,-12.23305,0 141.8058,-12.2375,0 141.7503,-12.205,0 141.8524,-11.98445,0 141.91,-11.95944,0 141.8905,-11.98833,0 141.9175,-12.10528,0 141.9644,-12.04084,0 142.0289,-12.06221,0 141.9422,-11.95472,0 142.1241,-11.36722,0 142.1589,-11.14944,0 142.1419,-10.95666,0 141.007,-9.128467000000001,0 141.0035,-4.128462,0 141.0025,-2.607085,0 141,-5.400000000000001e-007,0 146,-0.00688554,0 151,-0.01377954,0 156,-0.02066454,0 157.5176,-0.02275254,0 157.5176,-5.022757,0 157.5176,-5.728428,0 155,-7.3,0 157.5176,-9.204364,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +157.5082,43.79277,0 157.5082,38.79276,0 157.5082,33.79276,0 157.5082,28.79276,0 157.5082,23.79276000000001,0 157.5082,18.79276,0 157.5082,17,0 157.5082,12,0 157.501,12,0 152.501,12,0 147.501,12,0 142.501,12,0 142.501,14.23089,0 142.501,19.23089,0 140.5122,19.23089,0 135.5122,19.23089,0 135.5122,23.61333000000001,0 135.5122,28.61333,0 137.5011,28.62327,0 142.501,28.64826,0 142.501,32.25372,0 142.501,37.25373,0 142.501,42.25373,0 142.943,42.10665,0 143.2463,41.92527,0 143.3169,42.04277,0 143.3297,42.28167,0 143.3738,42.36167,0 143.5955,42.61971000000001,0 143.8928,42.84916,0 144.0244,42.92139,0 144.325,42.99805,0 144.4636,42.93083,0 144.7778,42.92555,0 144.8695,42.9269,0 145.5455,43.11722,0 145.8469,43.2832,0 145.8579,43.31955,0 146.017,43.30567,0 147.0505,43.68716,0 147.4426,44.61853,0 150.3838,45.74596,0 152.32,46.87341,0 153.7416,48.22143,0 154.6484,48.93221,0 155.1141,49.64299000000001,0 156.4621,50.57436,0 156.6092,50.81945,0 156.6455,50.88888000000002,0 156.693,50.89138,0 156.8188,50.97971,0 156.8247,51.01221000000002,0 156.9511,51.06028,0 157.1417,51.19221000000002,0 157.2567,51.22970999999999,0 157.3572,51.33471,0 157.5086,51.39201,0 157.5082,50.69276,0 157.5082,48.79276000000002,0 157.5082,43.79277,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +157.5071,79.90000000000001,0 157.5071,77.646,0 158.1819,76.97113,0 158.435,76.60558,0 158.5194,76.15566,0 158.4631,75.95882,0 158.1257,75.79011,0 157.0853,75.4808,0 152.1637,74.59863,0 151.1239,74.41226,0 149.3243,74.29978,0 145.7813,74.66534,0 144.2066,74.66534,0 142.2944,74.52473000000001,0 142.2944,74.13106999999999,0 143.5036,73.76551000000001,0 143.9254,73.48432,0 144.0097,73.25936,0 143.9816,72.92194000000001,0 143.8973,72.69698,0 142.6355,67.8588,0 141.3738,63.02061,0 141.2248,62.44925000000001,0 141.3777,62.25851,0 141.469,62.21404,0 141.6523,62.03364000000001,0 141.809,62.01836000000001,0 142.0864,62.06575,0 142.2051,61.94452,0 142.2928,61.90464000000001,0 142.5545,61.97489000000001,0 142.8312,61.9179,0 142.8849,61.9355,0 143.0034,62.08195,0 143.0821,62.13218,0 143.2357,62.03323,0 143.4179,62.06457000000001,0 143.5195,62.03001,0 143.533,61.92546,0 143.6266,61.89821,0 143.7727,61.98169,0 143.8912,61.96601,0 143.9069,61.93699000000001,0 143.8109,61.79792,0 144.1019,61.75510000000001,0 144.1931,61.77938000000002,0 144.3145,61.86486,0 144.4896,61.91393,0 144.5109,61.9786,0 144.672,62.10590000000001,0 144.8737,62.09342000000001,0 145.0974,61.9896,0 145.3697,62.02246,0 145.4535,61.96645,0 145.5982,62.08148,0 145.6794,62.08096,0 146.3024,61.84119,0 146.3988,61.79554,0 146.4794,61.71044000000001,0 146.6458,61.46524000000002,0 146.5954,61.35341,0 146.6452,61.12606000000001,0 146.597,61.07125,0 146.623,61.02689,0 146.6056,61.00037,0 146.3812,60.95324,0 146.4342,60.87623000000001,0 146.3924,60.75424,0 146.2405,60.70178,0 146.0911,60.76063,0 145.9837,60.76147,0 145.8941,60.69299,0 145.6304,60.62862000000001,0 145.4411,60.52949,0 145.403,60.4785,0 145.4866,60.43041000000001,0 145.4511,60.35231,0 145.6592,60.22936,0 146.2814,60.24876,0 146.4616,60.14434000000001,0 146.5698,60.12513,0 146.6143,60.07499,0 146.6149,59.98568000000001,0 146.6968,59.94598,0 146.9257,59.95415,0 147.1046,59.86969000000001,0 147.1353,59.79827,0 147.079,59.73552,0 147.0555,59.47459000000001,0 147.0332,59.43111,0 146.8809,59.39854,0 146.8662,59.35934000000001,0 142.1674,59.0727,0 141.702,54.09441,0 141.2365,49.11613,0 140.948,46.03066,0 137.7037,45.85810000000001,0 137.6569,45.78221,0 137.3383,45.57721,0 137.1414,45.37028,0 136.8167,45.19554,0 136.7622,45.08721,0 136.6522,45.02804,0 136.5547,44.93833000000001,0 136.5533,44.88889,0 136.4497,44.82695,0 136.4413,44.76832,0 136.3608,44.79027,0 136.3075,44.73777000000001,0 136.3336,44.70361,0 136.2491,44.67194,0 136.1844,44.51305,0 135.8847,44.39832,0 135.8005,44.26333,0 135.7216,44.23499,0 135.6483,44.15943,0 135.6319,44.04944,0 135.5305,43.99166000000001,0 135.5128,43.92139,0 135.4572,43.94332,0 135.4769,43.8761,0 135.5178,43.88499,0 135.42,43.75417,0 135.273,43.68639,0 135.2497,43.72915,0 135.1833,43.70943,0 135.2266,43.68749,0 135.2225,43.6386,0 135.1297,43.49888,0 134.9686,43.4586,0 134.7639,43.30694,0 134.6997,43.30111,0 134.4167,43.14833,0 134.1883,43.07972000000001,0 134.115,42.99722,0 133.9142,42.87555,0 133.86,42.90387,0 133.7105,42.81332,0 133.6602,42.85443,0 133.5944,42.84694,0 133.3747,42.7575,0 133.2269,42.74249,0 133.1583,42.68306,0 133.0236,42.68776,0 133.0008,42.72222,0 133.0775,42.7411,0 132.9905,42.82054,0 132.8878,42.80555,0 132.853,42.73888,0 132.6667,42.8224,0 132.4611,42.77265,0 132.3549,42.70301000000001,0 132.2953,42.74943,0 132.1858,43.00813,0 132.0598,43.06119,0 131.8044,42.90532000000001,0 131.486,42.78592,0 131.4794,42.62341,0 131.2207,42.55566000000001,0 131.1678,42.57138,0 131.1919,42.59527,0 131.1397,42.60917,0 131.1133,42.66444,0 131.1022,42.61804,0 131.0228,42.645,0 130.9253,42.59333,0 130.8191,42.62693,0 130.93,42.61388,0 130.9528,42.645,0 130.7861,42.64277,0 130.8411,42.6875,0 130.7108,42.68499,0 130.6733,42.64749,0 130.7366,42.61361,0 130.7258,42.575,0 130.7703,42.59638,0 130.8711,42.52333,0 130.8383,42.51832,0 130.8305,42.46388,0 130.7789,42.45194,0 130.7539,42.34749,0 130.6974,42.29221,0 130.655,42.31388,0 130.6591,42.37665,0 130.6044,42.42186,0 130.5591,42.50667000000001,0 130.618,42.57944000000001,0 130.6052,42.61971000000001,0 130.5389,42.68194,0 130.4589,42.67999,0 130.4052,42.71804,0 130.638,42.83167,0 130.7705,42.83194,0 130.8186,42.87610000000001,0 131.0153,42.855,0 131.0458,42.90193,0 131.1322,42.91916,0 131.1047,43.03611,0 131.2144,43.13305,0 131.1911,43.19665,0 131.2611,43.26445,0 131.2741,43.36333,0 131.3114,43.39222,0 131.3044,43.45583,0 131.223,43.51167,0 131.1944,43.52777,0 131.1889,43.53666,0 131.2205,43.59527,0 131.1991,43.81889,0 131.2461,43.91806,0 131.2311,43.97387,0 131.2472,44.0275,0 131.298,44.05194,0 131.0955,44.69137,0 130.9539,44.8461,0 131.0425,44.86333,0 131.118,44.92583,0 131.2125,44.91055,0 131.35,44.97471,0 131.4666,44.96721,0 131.6444,45.10555,0 131.6464,45.19887,0 131.7536,45.21305000000001,0 131.8647,45.34555,0 131.9886,45.24194,0 132.9233,45.02194000000001,0 133.0333,45.06167,0 133.1222,45.12860000000001,0 133.0925,45.24415,0 133.1491,45.45083000000001,0 133.1905,45.50416000000001,0 133.4171,45.5691,0 133.4191,45.60722,0 133.4683,45.62222,0 133.4533,45.64722,0 133.4886,45.68277000000001,0 133.4419,45.69804,0 133.508,45.77777,0 133.4747,45.82027999999999,0 133.5222,45.88665,0 133.583,45.87137,0 133.6108,45.93194,0 133.6605,45.93458,0 133.7247,46.06528,0 133.6869,46.14332,0 133.9002,46.25031000000001,0 133.8839,46.29361,0 133.9102,46.31444,0 133.8619,46.33138,0 133.9286,46.39249,0 133.85,46.43999,0 133.8458,46.47554000000001,0 133.9016,46.57222,0 133.9977,46.64016,0 134.0438,46.75289999999999,0 134.0119,46.82389,0 134.0669,46.9161,0 134.0627,46.97527000000001,0 134.1312,47.07423,0 134.2222,47.11083000000001,0 134.1947,47.20665000000001,0 134.1478,47.24942999999999,0 134.1825,47.32332000000001,0 134.3147,47.4211,0 134.4791,47.42778,0 134.5521,47.46692,0 134.6741,47.57194,0 134.6836,47.62972,0 134.7672,47.7075,0 134.7703,47.73944,0 134.6844,47.80090000000001,0 134.6619,47.87417,0 134.5988,47.90471,0 134.5572,47.99471000000001,0 134.5839,48.06276,0 134.6678,48.10722,0 134.6814,48.22055,0 134.7408,48.26712,0 134.6105,48.30694,0 134.5718,48.36446000000001,0 134.4827,48.35028,0 134.4153,48.39112,0 134.0458,48.32528000000001,0 133.978,48.25972000000001,0 133.7514,48.24972,0 133.6964,48.18694,0 133.5936,48.17249,0 133.5067,48.07138000000001,0 133.3872,48.10528,0 133.0886,48.10167,0 133.018,48.03528000000001,0 132.8844,48.00860999999999,0 132.8089,47.94194,0 132.6714,47.94972000000001,0 132.6597,47.86583,0 132.5425,47.71389000000001,0 132.3411,47.74248999999999,0 131.9194,47.66999,0 131.6783,47.69915000000002,0 131.5755,47.6561,0 131.5372,47.71888,0 131.4167,47.7386,0 131.0405,47.67583,0 130.9464,47.71138,0 130.9461,47.80055,0 130.8897,47.90028,0 130.6814,48.04554,0 130.6594,48.0986,0 130.8322,48.29694,0 130.7375,48.41833,0 130.7578,48.48805,0 130.6269,48.47720000000001,0 130.6175,48.56194000000001,0 130.523,48.60277,0 130.675,48.84415000000001,0 130.6277,48.87465,0 130.5044,48.85194,0 130.5996,48.93345,0 130.9003,48.97752000000001,0 130.9823,49.16279999999999,0 131.0539,49.22533,0 131.3584,49.25607,0 131.4312,49.3426,0 131.456,49.56964,0 131.3988,49.67189000000001,0 131.3354,49.7038,0 131.4655,49.74212,0 131.4792,49.77464,0 131.4545,49.94840000000001,0 131.3611,50.08953,0 131.4482,50.18406000000001,0 131.2058,50.37281,0 131.0443,50.37701,0 130.848,50.43361,0 130.84,50.48763,0 130.9257,50.52943,0 130.9657,50.59039000000001,0 130.8691,50.65911,0 130.7555,50.65919000000002,0 130.6539,50.59364000000001,0 130.5924,50.61773000000001,0 130.7764,50.89289,0 130.649,50.9649,0 130.6651,50.99669,0 130.8353,51.0568,0 130.8823,51.1259,0 131.1296,51.25358000000001,0 131.1841,51.33065,0 131.4163,51.37443,0 131.4344,51.40771,0 131.3763,51.68487,0 131.6162,51.66434000000002,0 132.0319,51.81518,0 132.2201,51.81065,0 132.2938,51.84838000000001,0 132.3687,51.9752,0 132.4668,51.94439000000001,0 132.5923,52.10018000000001,0 132.7906,52.16892,0 133.1079,52.16774000000001,0 133.3551,52.22758,0 133.3646,52.27884,0 133.1884,52.43139000000001,0 133.2161,52.52879,0 133.1741,52.56192000000001,0 133.2485,52.66451,0 133.4076,52.67120000000001,0 133.9606,52.47187,0 134.1073,52.49435,0 134.5898,52.42293,0 134.644,52.49686,0 134.5947,52.53989,0 134.605,52.58288,0 134.7453,52.68349,0 134.5818,52.77556,0 134.572,52.87017000000001,0 134.6294,52.97923,0 134.8581,53.2211,0 134.8416,53.30129,0 134.7772,53.35156000000001,0 134.8063,53.39867,0 134.8608,53.39223,0 134.8939,53.42523000000001,0 134.4765,53.65137,0 134.045,53.46778000000001,0 133.7953,53.50423,0 133.6865,53.48001000000001,0 133.5405,53.55013,0 133.2816,53.50229000000001,0 133.1567,53.32175,0 132.9397,53.31208,0 132.7966,53.23391,0 132.1492,53.28006000000001,0 132.0767,53.2618,0 131.9606,53.16412,0 131.8363,53.20384,0 131.8331,53.2546,0 131.7383,53.28881000000001,0 131.4342,53.24406,0 131.4033,53.26408,0 131.4433,53.50145,0 131.369,53.75986,0 131.0603,53.87516000000001,0 130.8468,53.80831,0 130.7704,53.82110999999999,0 130.6684,53.88879,0 130.5371,53.88297000000001,0 130.4453,53.91442,0 130.4401,53.95286000000001,0 130.5956,54.16706,0 130.7881,54.30889,0 131.0439,54.32481000000001,0 131.1084,54.36098,0 131.2404,54.51491,0 131.2688,54.64863,0 131.4381,54.76270000000001,0 131.6391,54.78865,0 131.8497,54.86316000000001,0 132.102,55.05344000000001,0 132.3182,55.10742,0 132.4052,55.2324,0 132.5812,55.26958,0 132.7062,55.3788,0 132.9172,55.40487,0 132.8499,55.45721000000001,0 132.6032,55.50863,0 132.6198,55.5598,0 132.6778,55.5961,0 132.6783,55.64597,0 132.5187,55.73038,0 132.2958,55.75039,0 131.8277,55.67754,0 131.5875,55.7055,0 131.0922,55.68167,0 131.0924,55.7413,0 130.974,55.9278,0 131.1664,56.29863,0 131.182,56.41776000000001,0 131.3082,56.48672,0 131.4501,56.48512000000001,0 131.5154,56.54887000000001,0 131.7346,56.49774000000001,0 131.7752,56.62872,0 131.8783,56.76854,0 131.5165,57.0938,0 131.3337,57.16616000000001,0 131.1787,57.17673,0 131.1433,57.21137000000001,0 131.2694,57.28938,0 131.2796,57.36280000000001,0 131.5491,57.25599,0 131.66,57.27824,0 131.6878,57.45025,0 131.8647,57.60706,0 132.0838,57.63795,0 132.1476,57.72349000000001,0 131.8702,57.83329000000001,0 131.8208,57.9631,0 131.6874,58.0066,0 131.6564,58.07726000000001,0 131.5467,58.14072,0 131.5448,58.17016000000001,0 131.5646,58.1936,0 131.732,58.18654,0 132.1542,58.06819,0 132.0923,58.2122,0 132.2631,58.30735,0 132.1915,58.47281,0 132.2431,58.51441000000001,0 132.404,58.50661,0 132.539,58.58086,0 132.5681,58.63096000000001,0 132.5582,58.80028999999999,0 132.6281,58.88062,0 132.905,58.92134000000001,0 132.9268,58.97153000000001,0 132.8588,59.02208000000001,0 132.8709,59.06711,0 132.678,61.42275999999999,0 132.2701,66.40608,0 131.8621,71.38942,0 131.8621,75.29801999999999,0 133.9557,76.04201000000001,0 138.667,77.71629,0 142.5194,77.68817,0 142.5194,79.90000000000001,0 142.5194,84.90001,0 142.5194,89.90000000000001,0 143.5186,89.90000000000001,0 144.5178,89.90000000000001,0 145.5169,89.90000000000001,0 146.5161,89.90000000000001,0 147.5153,89.90000000000001,0 148.5145,89.90000000000001,0 149.5136,89.90000000000001,0 150.5128,89.90000000000001,0 151.512,89.90000000000001,0 152.5112,89.90000000000001,0 153.5104,89.90000000000001,0 154.5095,89.90000000000001,0 155.5087,89.90000000000001,0 156.5079,89.90000000000001,0 157.5071,89.90000000000001,0 157.5071,84.90001,0 157.5071,79.90000000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +10:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(10.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1050</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +157.8347,-33.62319,0 157.833,-30.36589,0 162.833,-30.36515,0 164.8248,-30.36486,0 164.8248,-33.62319,0 159.8248,-33.62319,0 157.8347,-33.62319,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +11:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(11);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1100</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +166.3565,-27.96984000000001,0 166.3565,-30.78259,0 167.5012,-30.78259,0 172.5012,-30.78259,0 172.5012,-32.24902,0 169.8135,-36.46523,0 167.1259,-40.68143,0 165.0656,-43.91332,0 165.0573,-48.91331,0 165.049,-53.9133,0 165.048,-54.52042000000001,0 170.048,-54.52042000000001,0 172.5188,-54.52042000000001,0 172.5163,-59.52043,0 172.5138,-64.52042,0 172.5113,-69.52042,0 172.5088,-74.52042,0 172.5063,-79.52042,0 172.5038,-84.52042000000002,0 172.5013,-89.52042,0 172.5011,-89.90000000000001,0 171.5022,-89.90000000000001,0 170.5033,-89.90000000000001,0 169.5044,-89.90000000000001,0 168.5055,-89.90000000000001,0 167.5066,-89.90000000000001,0 166.5077,-89.90000000000001,0 165.5088,-89.90000000000001,0 164.5099,-89.90000000000001,0 163.511,-89.90000000000001,0 162.5121,-89.90000000000001,0 161.5132,-89.90000000000001,0 160.5143,-89.90000000000001,0 159.5154,-89.90000000000001,0 158.5165,-89.90000000000001,0 157.5176,-89.90000000000001,0 157.5176,-89.00436000000001,0 157.5176,-84.00436000000001,0 157.5176,-79.00436000000001,0 157.5176,-74.00436000000001,0 157.5176,-69.00436000000001,0 157.5176,-64.00436000000001,0 157.5176,-59.00436,0 157.5176,-54.30436,0 157.5176,-49.30437,0 157.5176,-44.30436,0 157.5176,-39.30436,0 157.5176,-39.20436,0 157.5176,-34.20436,0 157.5176,-29.20436000000001,0 157.5176,-24.20437,0 157.5176,-19.20436,0 157.5176,-14.20437,0 157.5176,-9.204364,0 155,-7.3,0 157.5176,-5.728428,0 157.5176,-5.022757,0 157.5176,-0.02275254,0 156,-0.02066454,0 151,-0.01377954,0 146,-0.00688554,0 141,-5.400000000000001e-007,0 137,1.199996,0 137,6.166665000000001,0 137,11.16666,0 138.3636,12,0 142.501,12,0 147.501,12,0 152.501,12,0 157.501,12,0 157.5082,12,0 157.5082,17,0 157.5082,18.79276,0 157.5082,23.79276000000001,0 157.5082,28.79276,0 157.5082,33.79276,0 157.5082,38.79276,0 157.5082,43.79277,0 157.5082,48.79276000000002,0 157.5082,50.69276,0 162.5082,50.68582000000001,0 167.5082,50.67888,0 172.5082,50.67194,0 172.5127,50.67192,0 172.5106,45.67193000000001,0 172.5085,40.67192,0 172.5064,35.67193,0 172.5043,30.67193,0 172.5022,25.67193,0 172.5002,20.86006,0 167.5002,20.85992,0 162.5002,20.85979,0 160.5733,20.85973,0 160.5671,15.85973,0 160.5609,10.85973,0 160.5546,5.859737,0 160.5484,0.8597425000000001,0 160.5444,-2.353266,0 165.5444,-2.354284,0 170.5444,-2.355309,0 172.5015,-2.355705,0 172.5014,-7.35571,0 172.5014,-12.35571,0 172.5013,-17.35571,0 172.5013,-22.35571,0 172.5013,-27.35571,0 172.5013,-27.96984000000001,0 171.3565,-27.96984000000001,0 166.3565,-27.96984000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<innerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +157.833,-30.36589,0 157.8347,-33.62319,0 159.8248,-33.62319,0 164.8248,-33.62319,0 164.8248,-30.36486,0 162.833,-30.36515,0 157.833,-30.36589,0 </coordinates>
    +					</LinearRing>
    +				</innerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +172.5346,74.89999,0 172.5346,71.32091,0 167.5346,71.32091,0 162.5346,71.32091,0 159.7593,71.32091,0 160.3981,70.18025,0 160.6752,70.07872999999999,0 160.9,70.04337,0 162.2945,69.98492,0 162.3981,69.86644,0 162.4037,69.67095000000001,0 162.271,69.58094000000001,0 162.2592,69.52155000000001,0 162.1759,69.45047,0 162.1934,69.42339,0 162.4407,69.38332,0 162.8192,69.22402,0 162.5843,69.15031000000001,0 162.5256,69.11716,0 162.4894,69.04481,0 162.4924,69.00284000000001,0 162.5952,68.95761,0 162.6687,68.86439000000001,0 162.5342,68.81356,0 162.4721,68.75452,0 162.6575,68.64811,0 162.5248,68.5097,0 162.516,68.39265,0 162.4043,68.32226,0 161.9774,68.33798,0 161.3543,68.46493,0 160.8015,68.27876000000001,0 160.4726,68.27036,0 160.0085,68.31707,0 159.7156,68.27837,0 159.6493,68.22415,0 159.4943,68.19717,0 159.219,68.22060000000001,0 158.8942,68.17033000000001,0 158.4859,68.15252,0 158.1873,68.05862000000001,0 157.957,67.91137000000001,0 157.9457,67.8794,0 158.1858,67.82414000000001,0 158.2565,67.76581,0 158.1298,67.73808,0 157.772,67.74168,0 157.6639,67.70263,0 157.6332,67.61433,0 157.822,67.39413999999999,0 157.8414,67.30539,0 158.5028,67.09791,0 158.7005,67.06403,0 158.7453,66.92084,0 158.8598,66.84213,0 158.8488,66.78941,0 158.6252,66.67805,0 158.4739,66.56100000000001,0 158.2896,66.48923000000001,0 158.2673,66.45123,0 158.4219,66.36732000000001,0 158.5064,66.35485,0 158.7805,66.2747,0 159.2178,66.20954999999999,0 159.1812,66.14093,0 158.9673,65.98079,0 158.9386,65.85458000000001,0 158.8529,65.72281,0 159.01,65.70184,0 159.2512,65.61183,0 160.0269,65.49356,0 160.2953,65.31647,0 160.4545,65.15871,0 161.2346,65.12815000000001,0 161.595,64.98701,0 161.8677,64.81350000000001,0 162.2872,64.74589,0 162.4811,64.75023,0 162.7342,64.67844,0 163.1004,64.66866,0 163.1754,64.69203,0 163.1694,64.79909000000001,0 163.2627,64.74456000000001,0 163.1777,64.67222,0 163.1747,64.62662,0 163.3588,64.38509000000001,0 163.2731,64.30114,0 163.2748,64.25255,0 163.2118,64.2088,0 163.0435,64.23567,0 162.7443,64.17494000000001,0 162.7434,64.14100000000001,0 162.8791,64.04618000000001,0 162.9514,63.91394,0 162.7401,63.85479,0 162.6724,63.80212,0 162.7899,63.71773,0 162.9543,63.65721000000001,0 162.9894,63.51587000000001,0 162.6794,63.42236000000001,0 162.7222,63.37083,0 162.8369,63.33097,0 162.835,63.26305,0 162.6071,63.20139000000001,0 162.5314,63.12977,0 162.2438,63.13342000000001,0 162.169,63.02766,0 162.1873,62.96365000000001,0 162.244,62.93481,0 162.5737,62.85993,0 162.633,62.83200000000002,0 162.6456,62.77090000000001,0 162.8105,62.71667000000002,0 162.72,62.65807,0 162.7746,62.47603,0 162.7042,62.35186,0 162.529,62.28267000000001,0 162.1591,62.23799,0 162.1606,62.20911000000001,0 162.5421,62.11821000000001,0 162.5609,62.07495,0 162.5376,62.0244,0 162.4909,61.99523,0 162.3335,61.98738,0 162.3959,61.93176,0 162.4478,61.67067000000001,0 162.2883,61.62471000000001,0 162.2875,61.58943,0 162.0422,61.45555,0 162.023,61.41777,0 161.9286,61.37778,0 161.883,61.42944,0 161.8658,61.35054,0 161.8005,61.38082000000001,0 161.7422,61.30804000000001,0 161.5633,61.26694000000001,0 161.6155,61.24305,0 161.5725,61.20193,0 161.473,61.20444000000001,0 161.4155,61.16693,0 161.43,61.13221000000001,0 161.2161,61.06555,0 161.1658,61.00054,0 160.9008,60.90942,0 160.7978,60.73527,0 160.6705,60.77638,0 160.6186,60.7311,0 160.5602,60.75861,0 160.443,60.71249,0 160.4003,60.73332,0 160.4228,60.75722,0 160.3777,60.75139999999999,0 160.3389,60.71776,0 160.3533,60.69332,0 160.2611,60.62555,0 160.1378,60.58359999999999,0 160.1244,60.6786,0 160.1852,60.68888,0 160.2247,60.73721,0 160.2247,60.78443999999999,0 160.1619,60.83778,0 160.3519,60.92221,0 160.3322,60.9547,0 160.4319,61.04721,0 160.1642,61.05082,0 159.8705,60.9286,0 159.7805,60.94083,0 159.7964,60.97776,0 159.9769,61.12111,0 159.94,61.14360000000001,0 159.9516,61.1825,0 159.9011,61.21083,0 159.918,61.24888,0 159.7455,61.25083,0 159.84,61.31192999999999,0 159.9325,61.28334,0 159.9922,61.30888,0 159.8905,61.32861,0 159.9708,61.39861000000001,0 160.0369,61.38694,0 160.0255,61.41526,0 160.0791,61.44833,0 160.0083,61.46194000000001,0 160.2297,61.56361,0 160.2311,61.63611,0 160.2839,61.66249,0 160.2372,61.70249,0 160.3738,61.77804,0 160.3461,61.81889,0 160.3736,61.84276,0 160.3452,61.87082000000001,0 160.3652,61.9086,0 160.3344,61.92333,0 160.3549,61.94749000000001,0 159.9602,61.82333000000001,0 159.8375,61.71221,0 159.6711,61.7236,0 159.5311,61.66276,0 159.485,61.7197,0 159.5378,61.82027000000001,0 159.4458,61.77443,0 159.3341,61.82666,0 159.3816,61.85917,0 159.3697,61.88777000000001,0 159.2475,61.92222,0 158.9055,61.89582,0 158.8386,61.85749,0 158.8472,61.81777,0 158.6333,61.83500000000001,0 158.5189,61.79167000000001,0 158.4377,61.82444,0 158.2978,61.82167,0 158.0283,61.72999,0 157.9,61.78556,0 157.5038,61.80479,0 157.5038,57.82619,0 157.4064,57.785,0 157.1744,57.77193000000001,0 157.0489,57.80028,0 156.985,57.85361,0 156.7869,57.76361,0 156.7488,57.72833000000001,0 156.8455,57.72554000000002,0 156.9355,57.64054,0 156.9805,57.52777,0 156.9778,57.41444,0 156.6953,57.10860000000001,0 156.7194,57.08416,0 156.6794,57.0425,0 156.5472,57.1075,0 156.4544,56.95722,0 156.1997,56.85666,0 156.1978,56.81694000000001,0 156.0633,56.71471,0 156.0747,56.75999000000002,0 156.1689,56.83526000000001,0 156.1044,56.81527000000001,0 155.9408,56.63360999999999,0 155.7361,56.13693,0 155.76,56.1061,0 155.6986,56.06554000000001,0 155.6461,55.91194000000002,0 155.6183,55.62138,0 155.5441,55.3036,0 155.6994,54.51250000000001,0 155.9408,53.84694,0 155.9194,53.83749,0 156.0858,53.09778000000001,0 156.1033,52.84916,0 156.2608,52.56722,0 156.365,52.51666,0 156.4411,52.52083,0 156.37,52.44249,0 156.3655,52.49527,0 156.2775,52.52361,0 156.4247,52.20916,0 156.4919,51.93166000000001,0 156.4727,51.51361000000001,0 156.5164,51.41667,0 156.518,51.32277,0 156.5514,51.27388,0 156.6736,51.22943000000001,0 156.708,51.18444,0 156.7016,51.09833,0 156.7461,51.07443,0 156.7455,50.98777000000001,0 156.6455,50.88888000000002,0 156.6092,50.81945,0 156.4621,50.57436,0 155.1141,49.64299000000001,0 154.6484,48.93221,0 153.7416,48.22143,0 152.32,46.87341,0 150.3838,45.74596,0 147.4426,44.61853,0 147.0505,43.68716,0 146.017,43.30567,0 145.8579,43.31955,0 145.8775,43.38366,0 145.6765,43.53654,0 145.5292,44.44866,0 140.948,46.03066,0 141.2365,49.11613,0 141.702,54.09441,0 142.1674,59.0727,0 146.8662,59.35934000000001,0 146.8809,59.39854,0 147.0332,59.43111,0 147.0555,59.47459000000001,0 147.079,59.73552,0 147.1353,59.79827,0 147.1046,59.86969000000001,0 146.9257,59.95415,0 146.6968,59.94598,0 146.6149,59.98568000000001,0 146.6143,60.07499,0 146.5698,60.12513,0 146.4616,60.14434000000001,0 146.2814,60.24876,0 145.6592,60.22936,0 145.4511,60.35231,0 145.4866,60.43041000000001,0 145.403,60.4785,0 145.4411,60.52949,0 145.6304,60.62862000000001,0 145.8941,60.69299,0 145.9837,60.76147,0 146.0911,60.76063,0 146.2405,60.70178,0 146.3924,60.75424,0 146.4342,60.87623000000001,0 146.3812,60.95324,0 146.6056,61.00037,0 146.623,61.02689,0 146.597,61.07125,0 146.6452,61.12606000000001,0 146.5954,61.35341,0 146.6458,61.46524000000002,0 146.4794,61.71044000000001,0 146.3988,61.79554,0 146.3024,61.84119,0 145.6794,62.08096,0 145.5982,62.08148,0 145.4535,61.96645,0 145.3697,62.02246,0 145.0974,61.9896,0 144.8737,62.09342000000001,0 144.672,62.10590000000001,0 144.5109,61.9786,0 144.4896,61.91393,0 144.3145,61.86486,0 144.1931,61.77938000000002,0 144.1019,61.75510000000001,0 143.8109,61.79792,0 143.9069,61.93699000000001,0 143.8912,61.96601,0 143.7727,61.98169,0 143.6266,61.89821,0 143.533,61.92546,0 143.5195,62.03001,0 143.4179,62.06457000000001,0 143.2357,62.03323,0 143.0821,62.13218,0 143.0034,62.08195,0 142.8849,61.9355,0 142.8312,61.9179,0 142.5545,61.97489000000001,0 142.2928,61.90464000000001,0 142.2051,61.94452,0 142.0864,62.06575,0 141.809,62.01836000000001,0 141.6523,62.03364000000001,0 141.469,62.21404,0 141.3777,62.25851,0 141.2248,62.44925000000001,0 141.3738,63.02061,0 142.6355,67.8588,0 143.8973,72.69698,0 143.9816,72.92194000000001,0 144.0097,73.25936,0 143.9254,73.48432,0 143.5036,73.76551000000001,0 142.2944,74.13106999999999,0 142.2944,74.52473000000001,0 144.2066,74.66534,0 145.7813,74.66534,0 149.3243,74.29978,0 151.1239,74.41226,0 152.1637,74.59863,0 157.0853,75.4808,0 158.1257,75.79011,0 158.4631,75.95882,0 158.5194,76.15566,0 158.435,76.60558,0 158.1819,76.97113,0 157.5071,77.646,0 157.5071,79.90000000000001,0 157.5071,84.90001,0 157.5071,89.90000000000001,0 158.4463,89.90000000000001,0 159.3855,89.90000000000001,0 160.3247,89.90000000000001,0 161.264,89.90000000000001,0 162.2032,89.90000000000001,0 163.1424,89.90000000000001,0 164.0816,89.90000000000001,0 165.0208,89.90000000000001,0 165.9601,89.90000000000001,0 166.8993,89.90000000000001,0 167.8385,89.90000000000001,0 168.7777,89.90000000000001,0 169.7169,89.90000000000001,0 170.6562,89.90000000000001,0 171.5954,89.90000000000001,0 172.5346,89.90000000000001,0 172.5346,84.90001,0 172.5346,79.90000000000001,0 172.5346,74.89999,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +11:30</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(11.5);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1150</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +172.5013,-27.96984000000001,0 172.5012,-30.78259,0 167.5012,-30.78259,0 166.3565,-30.78259,0 166.3565,-27.96984000000001,0 171.3565,-27.96984000000001,0 172.5013,-27.96984000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +12:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(12);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1200</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-172.5,-38.5,0 -172.5,-43.5,0 -172.5,-45,0 -176.2216,-48.22535,0 -180,-51.50000000000001,0 -180,-46.50682,0 -175.0824,-46.50682,0 -175.0824,-43.00079,0 -180,-43.00079,0 -180,-38.5,0 -180,-33.5,0 -180,-28.5,0 -180,-23.5,0 -175,-23.5,0 -172.5,-23.5,0 -172.5,-28.5,0 -172.5,-33.5,0 -172.5,-38.5,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-177,-20.99139,0 -180,-21.87818,0 -180,-20,0 -180,-15,0 -180,-10,0 -180,-4.999996,0 -177,-9.000000999999999,0 -174,-13,0 -172.5,-15,0 -177,-15,0 -177,-15.99139,0 -177,-20.99139,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-177.5929,60.38517000000001,0 -180,58.95238000000001,0 -180,60.00000000000001,0 -180,65,0 -180,70,0 -180,75,0 -175.7817,72.31562,0 -172.509,70.23302,0 -169,68,0 -169,65.5,0 -173.2965,62.94258000000001,0 -177.5929,60.38517000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +180,68.95238000000001,0 180,63.95238000000001,0 180,58.95238000000001,0 177.7114,57.59015000000001,0 173.415,55.03272,0 170,53,0 171.5334,52.23330000000001,0 176.0055,49.99722,0 180,48,0 180,46.99903,0 180,41.99904,0 180,36.99903,0 180,31.99903000000001,0 180,26.99904,0 180,21.99903,0 180,16.99904,0 180,11.99903,0 180,6.999039000000001,0 180,1.999034,0 180,-3.000961,0 180,-8.000964,0 180,-13.00097,0 180,-18.00096,0 180,-23.00097,0 180,-28.00096,0 180,-33.00097,0 180,-38.00096,0 180,-43.00097,0 177.8549,-43.00097,0 176.0435,-45.19365,0 176.0435,-46.50451,0 180,-46.50451,0 180,-49.90001,0 180,-54.9,0 180,-59.90001,0 180,-64.90000000000001,0 180,-69.90000000000001,0 180,-74.90000000000001,0 180,-79.90000000000001,0 180,-84.89999,0 180,-89.90000000000001,0 179.0627,-89.90000000000001,0 178.1253,-89.90000000000001,0 177.1879,-89.90000000000001,0 176.2506,-89.90000000000001,0 175.3132,-89.90000000000001,0 174.3758,-89.90000000000001,0 173.4385,-89.90000000000001,0 172.5011,-89.90000000000001,0 172.5013,-89.52042,0 172.5038,-84.52042000000002,0 172.5063,-79.52042,0 172.5088,-74.52042,0 172.5113,-69.52042,0 172.5138,-64.52042,0 172.5163,-59.52043,0 172.5188,-54.52042000000001,0 170.048,-54.52042000000001,0 165.048,-54.52042000000001,0 165.049,-53.9133,0 165.0573,-48.91331,0 165.0656,-43.91332,0 167.1259,-40.68143,0 169.8135,-36.46523,0 172.5012,-32.24902,0 172.5012,-30.78259,0 172.5013,-27.96984000000001,0 172.5013,-27.35571,0 172.5013,-22.35571,0 172.5013,-17.35571,0 172.5014,-12.3557,0 172.5014,-7.35571,0 172.5015,-2.355705,0 170.5444,-2.355309,0 165.5444,-2.354284,0 160.5444,-2.353266,0 160.5484,0.8597425000000001,0 160.5546,5.859737,0 160.5609,10.85973,0 160.5671,15.85973,0 160.5733,20.85973,0 162.5002,20.85979,0 167.5002,20.85992,0 172.5002,20.86006,0 172.5022,25.67193,0 172.5043,30.67193,0 172.5064,35.67193,0 172.5085,40.67192,0 172.5106,45.67193000000001,0 172.5127,50.67192,0 172.5082,50.67194,0 167.5082,50.67888,0 162.5082,50.68582000000001,0 157.5082,50.69276,0 157.5086,51.39201,0 157.3572,51.33471,0 157.2567,51.22970999999999,0 157.1417,51.19221000000002,0 156.9511,51.06028,0 156.8247,51.01221000000002,0 156.8188,50.97971,0 156.693,50.89138,0 156.6455,50.88888000000002,0 156.7455,50.98777000000001,0 156.7461,51.07443,0 156.7016,51.09833,0 156.708,51.18444,0 156.6736,51.22943000000001,0 156.5514,51.27388,0 156.518,51.32277,0 156.5164,51.41667,0 156.4727,51.51361000000001,0 156.4919,51.93166000000001,0 156.4247,52.20916,0 156.2775,52.52361,0 156.3655,52.49527,0 156.37,52.44249,0 156.4411,52.52083,0 156.365,52.51666,0 156.2608,52.56722,0 156.1033,52.84916,0 156.0858,53.09778000000001,0 155.9194,53.83749,0 155.9408,53.84694,0 155.6994,54.51250000000001,0 155.5441,55.3036,0 155.6183,55.62138,0 155.6461,55.91194000000002,0 155.6986,56.06554000000001,0 155.76,56.1061,0 155.7361,56.13693,0 155.9408,56.63360999999999,0 156.1044,56.81527000000001,0 156.1689,56.83526000000001,0 156.0747,56.75999000000002,0 156.0633,56.71471,0 156.1978,56.81694000000001,0 156.1997,56.85666,0 156.4544,56.95722,0 156.5472,57.1075,0 156.6794,57.0425,0 156.7194,57.08416,0 156.6953,57.10860000000001,0 156.9778,57.41444,0 156.9805,57.52777,0 156.9355,57.64054,0 156.8455,57.72554000000002,0 156.7488,57.72833000000001,0 156.7869,57.76361,0 156.985,57.85361,0 157.0489,57.80028,0 157.1744,57.77193000000001,0 157.4064,57.785,0 157.5038,57.82619,0 157.5038,61.80479,0 157.9,61.78556,0 158.0283,61.72999,0 158.2978,61.82167,0 158.4377,61.82444,0 158.5189,61.79167000000001,0 158.6333,61.83500000000001,0 158.8472,61.81777,0 158.8386,61.85749,0 158.9055,61.89582,0 159.2475,61.92222,0 159.3697,61.88777000000001,0 159.3816,61.85917,0 159.3341,61.82666,0 159.4458,61.77443,0 159.5378,61.82027000000001,0 159.485,61.7197,0 159.5311,61.66276,0 159.6711,61.7236,0 159.8375,61.71221,0 159.9602,61.82333000000001,0 160.3549,61.94749000000001,0 160.3344,61.92333,0 160.3652,61.9086,0 160.3452,61.87082000000001,0 160.3736,61.84276,0 160.3461,61.81889,0 160.3738,61.77804,0 160.2372,61.70249,0 160.2839,61.66249,0 160.2311,61.63611,0 160.2297,61.56361,0 160.0083,61.46194000000001,0 160.0791,61.44833,0 160.0255,61.41526,0 160.0369,61.38694,0 159.9708,61.39861000000001,0 159.8905,61.32861,0 159.9922,61.30888,0 159.9325,61.28334,0 159.84,61.31192999999999,0 159.7455,61.25083,0 159.918,61.24888,0 159.9011,61.21083,0 159.9516,61.1825,0 159.94,61.14360000000001,0 159.9769,61.12111,0 159.7964,60.97776,0 159.7805,60.94083,0 159.8705,60.9286,0 160.1642,61.05082,0 160.4319,61.04721,0 160.3322,60.9547,0 160.3519,60.92221,0 160.1619,60.83778,0 160.2247,60.78443999999999,0 160.2247,60.73721,0 160.1852,60.68888,0 160.1244,60.6786,0 160.1378,60.58359999999999,0 160.2611,60.62555,0 160.3533,60.69332,0 160.3389,60.71776,0 160.3777,60.75139999999999,0 160.4228,60.75722,0 160.4003,60.73332,0 160.443,60.71249,0 160.5602,60.75861,0 160.6186,60.7311,0 160.6705,60.77638,0 160.7978,60.73527,0 160.9008,60.90942,0 161.1658,61.00054,0 161.2161,61.06555,0 161.43,61.13221000000001,0 161.4155,61.16693,0 161.473,61.20444000000001,0 161.5725,61.20193,0 161.6155,61.24305,0 161.5633,61.26694000000001,0 161.7422,61.30804000000001,0 161.8005,61.38082000000001,0 161.8658,61.35054,0 161.883,61.42944,0 161.9286,61.37778,0 162.023,61.41777,0 162.0422,61.45555,0 162.2875,61.58943,0 162.2883,61.62471000000001,0 162.4478,61.67067000000001,0 162.3959,61.93176,0 162.3335,61.98738,0 162.4909,61.99523,0 162.5376,62.0244,0 162.5609,62.07495,0 162.5421,62.11821000000001,0 162.1606,62.20911000000001,0 162.1591,62.23799,0 162.529,62.28267000000001,0 162.7042,62.35186,0 162.7746,62.47603,0 162.72,62.65807,0 162.8105,62.71667000000002,0 162.6456,62.77090000000001,0 162.633,62.83200000000002,0 162.5737,62.85993,0 162.244,62.93481,0 162.1873,62.96365000000001,0 162.169,63.02766,0 162.2438,63.13342000000001,0 162.5314,63.12977,0 162.6071,63.20139000000001,0 162.835,63.26305,0 162.8369,63.33097,0 162.7222,63.37083,0 162.6794,63.42236000000001,0 162.9894,63.51587000000001,0 162.9543,63.65721000000001,0 162.7899,63.71773,0 162.6724,63.80212,0 162.7401,63.85479,0 162.9514,63.91394,0 162.8791,64.04618000000001,0 162.7434,64.14100000000001,0 162.7443,64.17494000000001,0 163.0435,64.23567,0 163.2118,64.2088,0 163.2748,64.25255,0 163.2731,64.30114,0 163.3588,64.38509000000001,0 163.1747,64.62662,0 163.1777,64.67222,0 163.2627,64.74456000000001,0 163.1694,64.79909000000001,0 163.1754,64.69203,0 163.1004,64.66866,0 162.7342,64.67844,0 162.4811,64.75023,0 162.2872,64.74589,0 161.8677,64.81350000000001,0 161.595,64.98701,0 161.2346,65.12815000000001,0 160.4545,65.15871,0 160.2953,65.31647,0 160.0269,65.49356,0 159.2512,65.61183,0 159.01,65.70184,0 158.8529,65.72281,0 158.9386,65.85458000000001,0 158.9673,65.98079,0 159.1812,66.14093,0 159.2178,66.20954999999999,0 158.7805,66.2747,0 158.5064,66.35485,0 158.4219,66.36732000000001,0 158.2673,66.45123,0 158.2896,66.48923000000001,0 158.4739,66.56100000000001,0 158.6252,66.67805,0 158.8488,66.78941,0 158.8598,66.84213,0 158.7453,66.92084,0 158.7005,67.06403,0 158.5028,67.09791,0 157.8414,67.30539,0 157.822,67.39413999999999,0 157.6332,67.61433,0 157.6639,67.70263,0 157.772,67.74168,0 158.1298,67.73808,0 158.2565,67.76581,0 158.1858,67.82414000000001,0 157.9457,67.8794,0 157.957,67.91137000000001,0 158.1873,68.05862000000001,0 158.4859,68.15252,0 158.8942,68.17033000000001,0 159.219,68.22060000000001,0 159.4943,68.19717,0 159.6493,68.22415,0 159.7156,68.27837,0 160.0085,68.31707,0 160.4726,68.27036,0 160.8015,68.27876000000001,0 161.3543,68.46493,0 161.9774,68.33798,0 162.4043,68.32226,0 162.516,68.39265,0 162.5248,68.5097,0 162.6575,68.64811,0 162.4721,68.75452,0 162.5342,68.81356,0 162.6687,68.86439000000001,0 162.5952,68.95761,0 162.4924,69.00284000000001,0 162.4894,69.04481,0 162.5256,69.11716,0 162.5843,69.15031000000001,0 162.8192,69.22402,0 162.4407,69.38332,0 162.1934,69.42339,0 162.1759,69.45047,0 162.2592,69.52155000000001,0 162.271,69.58094000000001,0 162.4037,69.67095000000001,0 162.3981,69.86644,0 162.2945,69.98492,0 160.9,70.04337,0 160.6752,70.07872999999999,0 160.3981,70.18025,0 159.7593,71.32091,0 162.5346,71.32091,0 167.5346,71.32091,0 172.5346,71.32091,0 172.5346,74.89999,0 172.5346,79.90000000000001,0 172.5346,84.90001,0 172.5346,89.90000000000001,0 173.4678,89.90000000000001,0 174.401,89.90000000000001,0 175.3341,89.90000000000001,0 176.2673,89.90000000000001,0 177.2005,89.90000000000001,0 178.1337,89.90000000000001,0 179.0668,89.90000000000001,0 180,89.90000000000001,0 180,88.95238000000001,0 180,83.95238000000002,0 180,78.95237,0 180,73.95238000000001,0 180,68.95238000000001,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>	
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +12:45</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(12.75);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1275</styleUrl>
    +			<MultiGeometry>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +180,-46.50451,0 176.0435,-46.50451,0 176.0435,-45.19365,0 177.8549,-43.00097,0 180,-43.00097,0 180,-46.50451,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-175.0824,-46.50682,0 -180,-46.50682,0 -180,-43.00079,0 -175.0824,-43.00079,0 -175.0824,-46.50682,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +			</MultiGeometry>
    +		</Placemark>
    +		<Placemark>
    +			<name>GMT +13:00</name>
    +		<description><![CDATA[<div style="width:300px;height:50px;"  id="clock" ></div>
    +<script language="JavaScript">
    +      var int=self.setInterval("tick()",50);
    +      function tick(){
    +        document.getElementById("clock").innerHTML=calc(13);
    +      }
    +    function calc(offset) {
    +       d = new Date();
    +       utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    +       nd = new Date(utc + (3600000*offset));
    +       return nd.toLocaleString();
    +   }
    +</script>
    +]]></description>
    +		  <styleUrl>#khStyle1300</styleUrl>
    +			<Polygon>
    +				<outerBoundaryIs>
    +					<LinearRing>
    +						<coordinates>
    +-180,-21.87818,0 -177,-20.99139,0 -177,-15.99139,0 -177,-15,0 -172.5,-15,0 -172.5,-20,0 -172.5,-23.5,0 -175,-23.5,0 -180,-23.5,0 -180,-21.87818,0 </coordinates>
    +					</LinearRing>
    +				</outerBoundaryIs>
    +				<tessellate>1</tessellate>
    +			</Polygon>
    +		</Placemark>
    +</Document>
    +</kml>
    diff --git a/mapbox-gl/examples/data/ogcsample.xml b/mapbox-gl/examples/data/ogcsample.xml
    new file mode 100644
    index 0000000000..0d4c52da60
    --- /dev/null
    +++ b/mapbox-gl/examples/data/ogcsample.xml
    @@ -0,0 +1,284 @@
    +<?xml version='1.0' encoding="UTF-8"?>
    +<WMS_Capabilities version="1.3.0" xmlns="http://www.opengis.net/wms"
    +  xmlns:xlink="http://www.w3.org/1999/xlink"
    +  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +  xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd">
    +<Service>
    +  <Name>WMS</Name>
    +  <Title>Acme Corp. Map Server</Title>
    +  <Abstract>Map Server maintained by Acme Corporation.  Contact: webmaster@wmt.acme.com.  High-quality maps showing roadrunner nests and possible ambush locations.</Abstract>
    +
    +  <KeywordList>
    +    <Keyword>bird</Keyword>
    +    <Keyword>roadrunner</Keyword>
    +    <Keyword>ambush</Keyword>
    +  </KeywordList>
    +  <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
    +   xlink:href="http://hostname/" />
    +
    +
    +  <ContactInformation>
    +    <ContactPersonPrimary>
    +      <ContactPerson>Jeff Smith</ContactPerson>
    +      <ContactOrganization>NASA</ContactOrganization>
    +    </ContactPersonPrimary>
    +    <ContactPosition>Computer Scientist</ContactPosition>
    +
    +    <ContactAddress>
    +      <AddressType>postal</AddressType>
    +      <Address>NASA Goddard Space Flight Center</Address>
    +      <City>Greenbelt</City>
    +      <StateOrProvince>MD</StateOrProvince>
    +      <PostCode>20771</PostCode>
    +
    +      <Country>USA</Country>
    +    </ContactAddress>
    +    <ContactVoiceTelephone>+1 301 555-1212</ContactVoiceTelephone>
    +    <ContactElectronicMailAddress>user@host.com</ContactElectronicMailAddress>
    +  </ContactInformation>
    +
    +  <Fees>none</Fees>
    +
    +  <AccessConstraints>none</AccessConstraints>
    +  <LayerLimit>16</LayerLimit>
    +  <MaxWidth>2048</MaxWidth>
    +  <MaxHeight>2048</MaxHeight>
    +</Service>
    +<Capability>
    +  <Request>
    +    <GetCapabilities>
    +
    +      <Format>text/xml</Format>
    +      <DCPType>
    +        <HTTP>
    +          <Get>
    +            <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +             xlink:type="simple"
    +             xlink:href="http://hostname/path?" />
    +          </Get>
    +          <Post>
    +            <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +             xlink:type="simple"
    +             xlink:href="http://hostname/path?" />
    +
    +          </Post>
    +        </HTTP>
    +      </DCPType>
    +    </GetCapabilities>
    +    <GetMap>
    +      <Format>image/gif</Format>
    +      <Format>image/png</Format>
    +      <Format>image/jpeg</Format>
    +
    +      <DCPType>
    +        <HTTP>
    +          <Get>
    +            <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +             xlink:type="simple"
    +             xlink:href="http://hostname/path?" />
    +          </Get>
    +        </HTTP>
    +      </DCPType>
    +    </GetMap>
    +
    +    <GetFeatureInfo>
    +      <Format>text/xml</Format>
    +      <Format>text/plain</Format>
    +      <Format>text/html</Format>
    +      <DCPType>
    +        <HTTP>
    +          <Get>
    +
    +            <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +             xlink:type="simple"
    +             xlink:href="http://hostname/path?" />
    +          </Get>
    +        </HTTP>
    +      </DCPType>
    +    </GetFeatureInfo>
    +  </Request>
    +  <Exception>
    +    <Format>XML</Format>
    +
    +    <Format>INIMAGE</Format>
    +    <Format>BLANK</Format>
    +  </Exception>
    +  <Layer>
    +    <Title>Acme Corp. Map Server</Title>
    +    <CRS>CRS:84</CRS>
    +
    +    <AuthorityURL name="DIF_ID">
    +      <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
    +       xlink:href="http://gcmd.gsfc.nasa.gov/difguide/whatisadif.html" />
    +    </AuthorityURL>
    +    <BoundingBox CRS="CRS:84"
    +     minx="-1" miny="-1" maxx="1" maxy="1" resx="0.0" resy="0.0"/>
    +    <Layer>
    +
    +      <Name>ROADS_RIVERS</Name>
    +      <Title>Roads and Rivers</Title>
    +
    +      <CRS>EPSG:26986</CRS>
    +      <EX_GeographicBoundingBox>
    +        <westBoundLongitude>-71.63</westBoundLongitude>
    +        <eastBoundLongitude>-70.78</eastBoundLongitude>
    +        <southBoundLatitude>41.75</southBoundLatitude>
    +        <northBoundLatitude>42.90</northBoundLatitude>
    +
    +      </EX_GeographicBoundingBox>
    +      <BoundingBox CRS="CRS:84"
    +       minx="-71.63" miny="41.75" maxx="-70.78" maxy="42.90" resx="0.01" resy="0.01"/>
    +      <BoundingBox CRS="EPSG:26986"
    +       minx="189000" miny="834000" maxx="285000" maxy="962000" resx="1" resy="1" />
    +      <Attribution>
    +        <Title>State College University</Title>
    +        <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
    +         xlink:href="http://www.university.edu/" />
    +
    +        <LogoURL width="100" height="100">
    +          <Format>image/gif</Format>
    +          <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +           xlink:type="simple"
    +           xlink:href="http://www.university.edu/icons/logo.gif" />
    +        </LogoURL>
    +      </Attribution>
    +      <Identifier authority="DIF_ID">123456</Identifier>
    +      <FeatureListURL>
    +
    +        <Format>XML</Format>
    +        <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
    +         xlink:href="http://www.university.edu/data/roads_rivers.gml" />
    +      </FeatureListURL>
    +      <Style>
    +        <Name>USGS</Name>
    +        <Title>USGS Topo Map Style</Title>
    +        <Abstract>Features are shown in a style like that used in USGS topographic maps.</Abstract>
    +
    +        <LegendURL width="72" height="72">
    +          <Format>image/gif</Format>
    +          <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +           xlink:type="simple"
    +           xlink:href="http://www.university.edu/legends/usgs.gif" />
    +        </LegendURL>
    +        <StyleSheetURL>
    +          <Format>text/xsl</Format>
    +
    +          <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +           xlink:type="simple"
    +           xlink:href="http://www.university.edu/stylesheets/usgs.xsl" />
    +        </StyleSheetURL>
    +      </Style>
    +      <MinScaleDenominator>1000</MinScaleDenominator>
    +      <MaxScaleDenominator>250000</MaxScaleDenominator>
    +      <Layer queryable="1">
    +        <Name>ROADS_1M</Name>
    +        <Title>Roads at 1:1M scale</Title>
    +        <Abstract>Roads at a scale of 1 to 1 million.</Abstract>
    +
    +        <KeywordList>
    +          <Keyword>road</Keyword>
    +          <Keyword>transportation</Keyword>
    +          <Keyword>atlas</Keyword>
    +        </KeywordList>
    +        <Identifier authority="DIF_ID">123456</Identifier>
    +        <MetadataURL type="FGDC:1998">
    +
    +                <Format>text/plain</Format>
    +                <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +                 xlink:type="simple"
    +                 xlink:href="http://www.university.edu/metadata/roads.txt" />
    +             </MetadataURL>
    +        <MetadataURL type="ISO19115:2003">
    +               <Format>text/xml</Format>
    +               <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +                xlink:type="simple"
    +                xlink:href="http://www.university.edu/metadata/roads.xml" />
    +             </MetadataURL>
    +
    +        <Style>
    +          <Name>ATLAS</Name>
    +          <Title>Road atlas style</Title>
    +          <Abstract>Roads are shown in a style like that used in a commercial road atlas.</Abstract>
    +        <LegendURL width="72" height="72">
    +          <Format>image/gif</Format>
    +          <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink"
    +           xlink:type="simple"
    +           xlink:href="http://www.university.edu/legends/atlas.gif" />
    +
    +        </LegendURL>
    +        </Style>
    +      </Layer>
    +      <Layer queryable="1">
    +        <Name>RIVERS_1M</Name>
    +        <Title>Rivers at 1:1M scale</Title>
    +        <Abstract>Rivers at a scale of 1 to 1 million.</Abstract>
    +
    +        <KeywordList>
    +          <Keyword>river</Keyword>
    +          <Keyword>canal</Keyword>
    +          <Keyword>waterway</Keyword>
    +        </KeywordList>
    +      </Layer>
    +    </Layer>
    +
    +    <Layer queryable="1">
    +      <Title>Weather Forecast Data</Title>
    +      <CRS>CRS:84</CRS>
    +
    +      <EX_GeographicBoundingBox>
    +        <westBoundLongitude>-180</westBoundLongitude>
    +        <eastBoundLongitude>180</eastBoundLongitude>
    +
    +        <southBoundLatitude>-90</southBoundLatitude>
    +        <northBoundLatitude>90</northBoundLatitude>
    +      </EX_GeographicBoundingBox>
    +      <Dimension name="time" units="ISO8601" default="2000-08-22">1999-01-01/2000-08-22/P1D</Dimension>
    +      <Layer>
    +
    +        <Name>Clouds</Name>
    +        <Title>Forecast cloud cover</Title>
    +      </Layer>
    +      <Layer>
    +        <Name>Temperature</Name>
    +        <Title>Forecast temperature</Title>
    +      </Layer>
    +
    +      <Layer>
    +        <Name>Pressure</Name>
    +        <Title>Forecast barometric pressure</Title>
    +         <Dimension name="elevation" units="EPSG:5030" />
    +         <Dimension name="time" units="ISO8601" default="2000-08-22">
    +           1999-01-01/2000-08-22/P1D</Dimension>
    +
    +         <Dimension name="elevation" units="CRS:88" default="0" nearestValue="1">0,1000,3000,5000,10000</Dimension>
    +      </Layer>
    +    </Layer>
    +    <Layer opaque="1" noSubsets="1" fixedWidth="512" fixedHeight="256">
    +      <Name>ozone_image</Name>
    +      <Title>Global ozone distribution (1992)</Title>
    +
    +      <EX_GeographicBoundingBox>
    +        <westBoundLongitude>-180</westBoundLongitude>
    +        <eastBoundLongitude>180</eastBoundLongitude>
    +        <southBoundLatitude>-90</southBoundLatitude>
    +        <northBoundLatitude>90</northBoundLatitude>
    +      </EX_GeographicBoundingBox>
    +      <Dimension name="time" units="ISO8601" default="1992">1992</Dimension>
    +
    +    </Layer>
    +    <Layer cascaded="1">
    +      <Name>population</Name>
    +      <Title>World population, annual</Title>
    +      <EX_GeographicBoundingBox>
    +        <westBoundLongitude>-180</westBoundLongitude>
    +
    +        <eastBoundLongitude>180</eastBoundLongitude>
    +        <southBoundLatitude>-90</southBoundLatitude>
    +        <northBoundLatitude>90</northBoundLatitude>
    +      </EX_GeographicBoundingBox>
    +      <Dimension name="time" units="ISO8601" default="2000">1990/2000/P1Y</Dimension>
    +    </Layer>
    +  </Layer>
    +
    +</Capability>
    +</WMS_Capabilities>
    diff --git a/mapbox-gl/examples/data/sld/countries.sld b/mapbox-gl/examples/data/sld/countries.sld
    new file mode 100644
    index 0000000000..9ae5eebea3
    --- /dev/null
    +++ b/mapbox-gl/examples/data/sld/countries.sld
    @@ -0,0 +1,43 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml" version="1.0.0">
    +  <sld:NamedLayer>
    +    <sld:Name>countries</sld:Name>
    +    <sld:UserStyle>
    +      <sld:Name>countries</sld:Name>
    +      <sld:Title>A sample style for countries</sld:Title>
    +      <sld:IsDefault>1</sld:IsDefault>
    +      <sld:Abstract>A sample style for countries</sld:Abstract>
    +      <sld:FeatureTypeStyle>
    +        <sld:Name>name</sld:Name>
    +        <sld:Rule>
    +          <sld:Name>Sample</sld:Name>
    +          <sld:Title>Sample</sld:Title>
    +          <sld:PolygonSymbolizer>
    +            <sld:Fill>
    +              <sld:CssParameter name="fill">#ff0000</sld:CssParameter>
    +              <sld:CssParameter name="fill-opacity">0.6</sld:CssParameter>
    +            </sld:Fill>
    +            <sld:Stroke>
    +              <sld:CssParameter name="stroke">#00FF00</sld:CssParameter>
    +              <sld:CssParameter name="stroke-opacity">0.5</sld:CssParameter>
    +              <sld:CssParameter name="stroke-width">4</sld:CssParameter>
    +            </sld:Stroke>
    +          </sld:PolygonSymbolizer>
    +        </sld:Rule>
    +        <sld:Rule>  
    +          <sld:MaxScaleDenominator>20000000</sld:MaxScaleDenominator>
    +          <sld:TextSymbolizer>
    +            <sld:Label>
    +              <ogc:PropertyName>name</ogc:PropertyName>
    +            </sld:Label>
    +            <sld:Font>
    +              <sld:CssParameter name="font-family">Arial</sld:CssParameter>
    +              <sld:CssParameter name="font-size">10</sld:CssParameter>
    +              <sld:CssParameter name="font-style">Normal</sld:CssParameter>
    +            </sld:Font>
    +          </sld:TextSymbolizer>          
    +        </sld:Rule>
    +      </sld:FeatureTypeStyle>
    +    </sld:UserStyle>
    +  </sld:NamedLayer>
    +</sld:StyledLayerDescriptor>
    diff --git a/mapbox-gl/examples/data/topojson/us.json b/mapbox-gl/examples/data/topojson/us.json
    new file mode 100644
    index 0000000000..721f640680
    --- /dev/null
    +++ b/mapbox-gl/examples/data/topojson/us.json
    @@ -0,0 +1 @@
    +{"type":"Topology","objects":{"counties":{"type":"GeometryCollection","geometries":[{"type":"Polygon","id":53073,"arcs":[[0,1,2,3]]},{"type":"Polygon","id":30105,"arcs":[[4,5,6,7,8,9]]},{"type":"Polygon","id":30029,"arcs":[[10,11,12,13,14,15,16,17,18,19]]},{"type":"Polygon","id":16021,"arcs":[[20,21,22,23]]},{"type":"Polygon","id":30071,"arcs":[[-9,24,25,26,27,28,29]]},{"type":"Polygon","id":38079,"arcs":[[30,31,32,33]]},{"type":"Polygon","id":30053,"arcs":[[-19,34,35,-21,36]]},{"type":"Polygon","id":38009,"arcs":[[-32,37,38,39,40]]},{"type":"Polygon","id":30035,"arcs":[[41,42,-11,43]]},{"type":"Polygon","id":30041,"arcs":[[44,45,46,47]]},{"type":"Polygon","id":30005,"arcs":[[-29,-28,48,49,-48,50]]},{"type":"Polygon","id":30019,"arcs":[[51,52,-5,53]]},{"type":"Polygon","id":38067,"arcs":[[54,55,56,57]]},{"type":"Polygon","id":27069,"arcs":[[58,59,-55,60]]},{"type":"Polygon","id":38095,"arcs":[[61,62,63,-34,64,65]]},{"type":"Polygon","id":38019,"arcs":[[-57,66,67,-66,68]]},{"type":"Polygon","id":53047,"arcs":[[69,70,71,72,73,74,-1,75]]},{"type":"Polygon","id":53065,"arcs":[[76,77,78,79,80]]},{"type":"Polygon","id":53051,"arcs":[[-23,81,82,-77,83]]},{"type":"Polygon","id":53019,"arcs":[[-80,84,-70,85]]},{"type":"Polygon","id":30051,"arcs":[[86,87,88,-46,89]]},{"type":"Polygon","id":38023,"arcs":[[90,91,92,93]]},{"type":"Polygon","id":38013,"arcs":[[94,95,96,97,-91,98]]},{"type":"Polygon","id":30101,"arcs":[[99,-88,100,-42]]},{"type":"Polygon","id":38075,"arcs":[[101,102,-95,103,-40]]},{"type":"Polygon","id":27135,"arcs":[[104,105,-59,106,107]]},{"type":"Polygon","id":30091,"arcs":[[-93,108,109,-52,110]]},{"type":"Polygon","id":16017,"arcs":[[-36,111,112,113,114,-82,-22]]},{"type":"Polygon","id":38101,"arcs":[[-103,115,116,117,-96]]},{"type":"MultiPolygon","id":53055,"arcs":[[[118]],[[119]],[[120]]]},{"type":"Polygon","id":27071,"arcs":[[121,122,123,124,125]]},{"type":"Polygon","id":53057,"arcs":[[126,-2,-75,127,128,129]]},{"type":"Polygon","id":38105,"arcs":[[-98,130,131,132,-109,-92]]},{"type":"Polygon","id":38049,"arcs":[[133,134,135,-116,-102,-39]]},{"type":"Polygon","id":27137,"arcs":[[136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,-122,151]]},{"type":"Polygon","id":30085,"arcs":[[-110,-133,152,153,-6,-53]]},{"type":"Polygon","id":53007,"arcs":[[-74,154,155,156,157,-128]]},{"type":"Polygon","id":38061,"arcs":[[158,159,160,-131,-97,-118]]},{"type":"Polygon","id":27089,"arcs":[[161,162,163,164,165,-60,-106]]},{"type":"Polygon","id":38069,"arcs":[[-64,166,167,168,-134,-38,-31]]},{"type":"Polygon","id":38071,"arcs":[[169,170,171,-62,-68]]},{"type":"Polygon","id":38099,"arcs":[[-56,-166,172,173,-170,-67]]},{"type":"Polygon","id":27007,"arcs":[[-124,174,175,176,177,178,-162,-105,179]]},{"type":"Polygon","id":30073,"arcs":[[-101,-87,180,181,-12,-43]]},{"type":"MultiPolygon","id":53029,"arcs":[[[182,183]],[[184]]]},{"type":"Polygon","id":53009,"arcs":[[185,186,187]]},{"type":"Polygon","id":38005,"arcs":[[-63,-172,188,189,190,-167]]},{"type":"Polygon","id":30015,"arcs":[[-50,191,192,193,194,-181,-90,-45]]},{"type":"Polygon","id":53061,"arcs":[[-158,195,196,-183,197,-129]]},{"type":"Polygon","id":30089,"arcs":[[-18,198,199,200,201,-112,-35]]},{"type":"Polygon","id":27075,"arcs":[[202,203,-137,204]]},{"type":"Polygon","id":38063,"arcs":[[205,206,207,208,-189,-171,-174]]},{"type":"Polygon","id":38035,"arcs":[[-165,209,210,211,-206,-173]]},{"type":"Polygon","id":27119,"arcs":[[212,213,214,215,216,217,218,-210,-164]]},{"type":"Polygon","id":27113,"arcs":[[-179,219,-215,220,-213,-163]]},{"type":"Polygon","id":30083,"arcs":[[221,222,223,224,-153]]},{"type":"Polygon","id":53017,"arcs":[[225,226,-155,-73]]},{"type":"Polygon","id":38053,"arcs":[[-161,227,228,229,230,-222,-132]]},{"type":"Polygon","id":53031,"arcs":[[231,232,233,-186,234]]},{"type":"Polygon","id":30099,"arcs":[[-195,235,236,-13,-182]]},{"type":"Polygon","id":30055,"arcs":[[-225,237,238,239,-7,-154]]},{"type":"Polygon","id":16079,"arcs":[[-202,240,241,242,243,244,-113]]},{"type":"Polygon","id":30047,"arcs":[[245,-199,-17]]},{"type":"Polygon","id":53063,"arcs":[[-83,-115,246,247,248,249,-78]]},{"type":"Polygon","id":27029,"arcs":[[250,251,252,-216,-220,-178]]},{"type":"Polygon","id":16055,"arcs":[[-245,253,-247,-114]]},{"type":"Polygon","id":30033,"arcs":[[-240,254,255,256,257,-25,-8]]},{"type":"Polygon","id":27125,"arcs":[[-214,-221]]},{"type":"Polygon","id":53025,"arcs":[[-72,258,259,260,261,262,263,-226]]},{"type":"Polygon","id":53043,"arcs":[[-85,-79,-250,264,265,-259,-71]]},{"type":"Polygon","id":30049,"arcs":[[266,267,268,269,270,-14,-237]]},{"type":"MultiPolygon","id":53035,"arcs":[[[271]],[[272,273,274,275,276]]]},{"type":"Polygon","id":27061,"arcs":[[-151,277,278,-175,-123]]},{"type":"Polygon","id":38055,"arcs":[[279,280,281,282,283,-159,-117,-136]]},{"type":"Polygon","id":38027,"arcs":[[-209,284,285,286,-190]]},{"type":"Polygon","id":38103,"arcs":[[-191,-287,287,288,289,290,-168]]},{"type":"Polygon","id":38083,"arcs":[[-169,-291,291,292,-280,-135]]},{"type":"Polygon","id":38025,"arcs":[[-284,293,294,295,-228,-160]]},{"type":"Polygon","id":30027,"arcs":[[-27,296,297,298,299,300,-192,-49]]},{"type":"Polygon","id":30021,"arcs":[[-224,301,302,-238]]},{"type":"MultiPolygon","id":53033,"arcs":[[[303]],[[-157,304,305,306,307,-196]]]},{"type":"Polygon","id":30013,"arcs":[[308,309,-267,-236,-194]]},{"type":"Polygon","id":38091,"arcs":[[-212,310,311,312,313,-207]]},{"type":"Polygon","id":38039,"arcs":[[-314,314,315,316,-285,-208]]},{"type":"Polygon","id":38097,"arcs":[[317,318,-311,-211,-219]]},{"type":"MultiPolygon","id":53045,"arcs":[[[319,-276,320,321,322,323,-232]]]},{"type":"Polygon","id":30063,"arcs":[[-16,324,325,326,327,328,329,-200,-246]]},{"type":"Polygon","id":30077,"arcs":[[-271,330,331,332,-325,-15]]},{"type":"Polygon","id":30069,"arcs":[[-258,333,334,-297,-26]]},{"type":"Polygon","id":53037,"arcs":[[-227,-264,335,-305,-156]]},{"type":"Polygon","id":38031,"arcs":[[-317,336,-288,-286]]},{"type":"Polygon","id":38057,"arcs":[[337,338,339,-294,-283]]},{"type":"Polygon","id":53027,"arcs":[[-324,340,341,342,343,-233]]},{"type":"Polygon","id":27087,"arcs":[[344,345,-217,-253]]},{"type":"Polygon","id":27107,"arcs":[[-218,-346,346,347,348,-318]]},{"type":"Polygon","id":30061,"arcs":[[-330,349,-241,-201]]},{"type":"Polygon","id":27021,"arcs":[[350,351,352,353,354,355,-176,-279]]},{"type":"Polygon","id":23003,"arcs":[[356,357,358,359,360]]},{"type":"Polygon","id":30045,"arcs":[[-301,361,362,-309,-193]]},{"type":"Polygon","id":16009,"arcs":[[-244,363,364,-248,-254]]},{"type":"Polygon","id":27057,"arcs":[[-356,365,366,-251,-177]]},{"type":"MultiPolygon","id":53053,"arcs":[[[-307,-306,367,368,369,370]],[[-273,371]],[[-321,-275,372]]]},{"type":"Polygon","id":30109,"arcs":[[-231,373,374,375,-302,-223]]},{"type":"Polygon","id":38007,"arcs":[[-296,376,377,378,-229]]},{"type":"Polygon","id":38033,"arcs":[[-379,379,380,-374,-230]]},{"type":"Polygon","id":38043,"arcs":[[381,382,383,384,-292,-290]]},{"type":"Polygon","id":38093,"arcs":[[-316,385,386,387,-382,-289,-337]]},{"type":"Polygon","id":38015,"arcs":[[-385,388,389,390,-281,-293]]},{"type":"Polygon","id":38065,"arcs":[[-391,391,-338,-282]]},{"type":"Polygon","id":53001,"arcs":[[392,393,-260,-266]]},{"type":"Polygon","id":53075,"arcs":[[-249,-365,394,395,396,397,398,399,-393,-265]]},{"type":"Polygon","id":38003,"arcs":[[-313,400,401,402,-386,-315]]},{"type":"Polygon","id":38017,"arcs":[[-319,-349,403,404,405,-401,-312]]},{"type":"Polygon","id":53067,"arcs":[[-370,406,-341,-323,407]]},{"type":"Polygon","id":30079,"arcs":[[-376,408,409,-255,-239,-303]]},{"type":"Polygon","id":27005,"arcs":[[-367,410,411,412,-347,-345,-252]]},{"type":"Polygon","id":27027,"arcs":[[-413,413,414,415,-404,-348]]},{"type":"Polygon","id":16057,"arcs":[[-243,416,417,-395,-364]]},{"type":"Polygon","id":53077,"arcs":[[-263,418,419,420,421,-368,-336]]},{"type":"Polygon","id":30059,"arcs":[[-363,422,423,424,425,426,-268,-310]]},{"type":"Polygon","id":27001,"arcs":[[-150,427,428,429,430,431,-351,-278]]},{"type":"Polygon","id":26131,"arcs":[[432,433,434,435]]},{"type":"Polygon","id":38089,"arcs":[[-340,436,437,438,439,-377,-295]]},{"type":"Polygon","id":38059,"arcs":[[-390,440,441,442,-437,-339,-392]]},{"type":"Polygon","id":26013,"arcs":[[443,444,445,446]]},{"type":"Polygon","id":16035,"arcs":[[-350,-329,447,448,449,-417,-242]]},{"type":"Polygon","id":30017,"arcs":[[450,451,452,453,-256,-410]]},{"type":"Polygon","id":30087,"arcs":[[-454,454,455,456,457,458,-334,-257]]},{"type":"Polygon","id":30039,"arcs":[[459,460,-326,-333]]},{"type":"Polygon","id":27159,"arcs":[[-355,461,462,-411,-366]]},{"type":"Polygon","id":27035,"arcs":[[-432,463,464,-352]]},{"type":"Polygon","id":53049,"arcs":[[465,466,467,-343]]},{"type":"Polygon","id":53041,"arcs":[[-407,-369,-422,468,469,470,-466,-342]]},{"type":"Polygon","id":30007,"arcs":[[-427,471,472,-269]]},{"type":"Polygon","id":27017,"arcs":[[473,474,-428,-149]]},{"type":"Polygon","id":26053,"arcs":[[475,476,477,478,479,480,481,482,483,484,-434]]},{"type":"Polygon","id":30065,"arcs":[[-459,485,486,-298,-335]]},{"type":"Polygon","id":26095,"arcs":[[487,488,489,490,491]]},{"type":"Polygon","id":30037,"arcs":[[-487,492,493,494,495,-299]]},{"type":"Polygon","id":30107,"arcs":[[-300,-496,496,-423,-362]]},{"type":"Polygon","id":53021,"arcs":[[497,498,499,-261,-394,-400]]},{"type":"Polygon","id":53005,"arcs":[[500,501,502,503,-419,-262,-500]]},{"type":"Polygon","id":27111,"arcs":[[-463,504,505,506,507,-414,-412]]},{"type":"Polygon","id":38037,"arcs":[[508,509,510,-438,-443]]},{"type":"Polygon","id":53023,"arcs":[[511,512,513,-398]]},{"type":"Polygon","id":30025,"arcs":[[-381,514,515,516,517,-451,-409,-375]]},{"type":"Polygon","id":16049,"arcs":[[518,519,520,521,522,523,524,-448,-328]]},{"type":"Polygon","id":30081,"arcs":[[-461,525,526,527,-519,-327]]},{"type":"Polygon","id":38029,"arcs":[[-384,528,529,530,531,-441,-389]]},{"type":"Polygon","id":38047,"arcs":[[-388,532,533,-529,-383]]},{"type":"Polygon","id":16069,"arcs":[[-450,534,-524,535,536,-396,-418]]},{"type":"Polygon","id":38087,"arcs":[[537,538,539,-515,-380,-378,-440]]},{"type":"Polygon","id":38045,"arcs":[[540,541,542,-533,-387,-403]]},{"type":"Polygon","id":38041,"arcs":[[-511,543,-538,-439]]},{"type":"Polygon","id":27167,"arcs":[[-508,544,545,546,-415]]},{"type":"Polygon","id":38073,"arcs":[[-406,547,548,549,-541,-402]]},{"type":"Polygon","id":38077,"arcs":[[-547,550,551,552,-548,-405,-416]]},{"type":"Polygon","id":53013,"arcs":[[553,554,555,-498,-399,-514]]},{"type":"Polygon","id":53071,"arcs":[[-556,556,-501,-499]]},{"type":"Polygon","id":55051,"arcs":[[557,-484,-483,-482,-481,-480,-479,558,559,560,561]]},{"type":"Polygon","id":23025,"arcs":[[562,563,564,565,566,567,-360]]},{"type":"Polygon","id":23021,"arcs":[[568,-563,-359]]},{"type":"Polygon","id":30043,"arcs":[[-473,569,570,571,572,-331,-270]]},{"type":"Polygon","id":26153,"arcs":[[-490,573,574,575,576]]},{"type":"Polygon","id":30111,"arcs":[[577,578,579,580,-493,-486,-458]]},{"type":"Polygon","id":30103,"arcs":[[581,-578,-457]]},{"type":"Polygon","id":16061,"arcs":[[-449,-525,-535]]},{"type":"Polygon","id":53003,"arcs":[[-537,582,-512,-397]]},{"type":"Polygon","id":38085,"arcs":[[-532,583,584,-509,-442]]},{"type":"Polygon","id":26071,"arcs":[[-445,585,586,587,588,589,590,-476,-433,591]]},{"type":"Polygon","id":27115,"arcs":[[592,593,594,595,596,-429,-475]]},{"type":"Polygon","id":23019,"arcs":[[597,598,599,600,-564,-569,-358]]},{"type":"Polygon","id":53059,"arcs":[[-421,601,602,603,604,605,-469]]},{"type":"Polygon","id":53015,"arcs":[[-606,606,607,608,609,-470]]},{"type":"Polygon","id":53069,"arcs":[[-471,-610,610,-467]]},{"type":"Polygon","id":27153,"arcs":[[-354,611,612,613,-505,-462]]},{"type":"Polygon","id":27097,"arcs":[[-465,614,615,616,-612,-353]]},{"type":"Polygon","id":55125,"arcs":[[617,618,-559,-478,-477,-591,619]]},{"type":"Polygon","id":41007,"arcs":[[620,621,622]]},{"type":"Polygon","id":38001,"arcs":[[-510,-585,623,624,625,626,-539,-544]]},{"type":"Polygon","id":38081,"arcs":[[-553,627,628,629,-549]]},{"type":"Polygon","id":38051,"arcs":[[-543,630,631,632,-530,-534]]},{"type":"Polygon","id":38021,"arcs":[[-550,-630,633,634,-631,-542]]},{"type":"Polygon","id":38011,"arcs":[[-627,635,-516,-540]]},{"type":"Polygon","id":30023,"arcs":[[-573,636,637,-526,-460,-332]]},{"type":"Polygon","id":26043,"arcs":[[638,639,640,-587,641]]},{"type":"Polygon","id":27095,"arcs":[[642,643,644,645,-615,-464,-431]]},{"type":"Polygon","id":30097,"arcs":[[-495,646,647,-424,-497]]},{"type":"Polygon","id":30031,"arcs":[[-426,648,649,650,651,652,-570,-472]]},{"type":"Polygon","id":30067,"arcs":[[-648,653,654,655,-649,-425]]},{"type":"Polygon","id":30093,"arcs":[[656,657,-637,-572]]},{"type":"Polygon","id":41009,"arcs":[[658,659,660,-621,661,-608]]},{"type":"Polygon","id":27065,"arcs":[[-597,662,-643,-430]]},{"type":"Polygon","id":55013,"arcs":[[663,664,665,666,-595,-594,667]]},{"type":"Polygon","id":55113,"arcs":[[668,669,670,671,672]]},{"type":"Polygon","id":55129,"arcs":[[-672,673,-664,674]]},{"type":"Polygon","id":30011,"arcs":[[675,676,677,678,-452,-518]]},{"type":"Polygon","id":30095,"arcs":[[-581,679,-654,-647,-494]]},{"type":"Polygon","id":27051,"arcs":[[-507,680,681,682,-545]]},{"type":"Polygon","id":27041,"arcs":[[-614,683,684,-681,-506]]},{"type":"Polygon","id":55041,"arcs":[[685,686,687,688,689,-620,-590]]},{"type":"Polygon","id":53011,"arcs":[[-605,690,-659,-607]]},{"type":"Polygon","id":53039,"arcs":[[-504,691,692,693,694,695,-602,-420]]},{"type":"Polygon","id":30003,"arcs":[[-456,696,697,698,699,-579,-582]]},{"type":"Polygon","id":27155,"arcs":[[-683,700,701,702,-551,-546]]},{"type":"Polygon","id":55037,"arcs":[[-588,-641,703,-686,-589]]},{"type":"Polygon","id":41059,"arcs":[[-555,704,705,706,707,-502,-557]]},{"type":"Polygon","id":41063,"arcs":[[-513,-583,-536,-523,708,709,710,-705,-554]]},{"type":"Polygon","id":26109,"arcs":[[711,712,713,714,-639,715]]},{"type":"Polygon","id":55099,"arcs":[[-619,716,717,718,719,-670,720,-560]]},{"type":"Polygon","id":46105,"arcs":[[721,722,723,724,725,-625]]},{"type":"Polygon","id":46031,"arcs":[[726,727,728,729,-722,-624,-584]]},{"type":"Polygon","id":46063,"arcs":[[-626,-726,730,-676,-517,-636]]},{"type":"Polygon","id":46021,"arcs":[[-633,731,732,-727,-531]]},{"type":"Polygon","id":30001,"arcs":[[-658,733,734,735,736,-527,-638]]},{"type":"Polygon","id":46089,"arcs":[[-635,737,738,739,-732,-632]]},{"type":"Polygon","id":46013,"arcs":[[-629,740,741,742,743,744,-738,-634]]},{"type":"Polygon","id":46109,"arcs":[[-703,745,746,747,748,-552]]},{"type":"Polygon","id":46091,"arcs":[[-749,749,-741,-628]]},{"type":"Polygon","id":41049,"arcs":[[-708,750,751,752,-692,-503]]},{"type":"Polygon","id":55085,"arcs":[[-690,753,754,-717,-618]]},{"type":"Polygon","id":41061,"arcs":[[755,756,-706,-711]]},{"type":"Polygon","id":30057,"arcs":[[-653,757,-734,-657,-571]]},{"type":"Polygon","id":27009,"arcs":[[-646,758,759,-616]]},{"type":"Polygon","id":41021,"arcs":[[760,761,762,-693,-753]]},{"type":"Polygon","id":30075,"arcs":[[-679,763,764,765,-697,-455,-453]]},{"type":"Polygon","id":26031,"arcs":[[766,767,768,769,770,771]]},{"type":"Polygon","id":41057,"arcs":[[772,773,774,775,776,-622]]},{"type":"Polygon","id":41067,"arcs":[[777,778,779,-773,-661]]},{"type":"Polygon","id":27145,"arcs":[[-760,780,781,782,783,784,-684,-613,-617]]},{"type":"Polygon","id":27149,"arcs":[[785,786,787,-701,-682]]},{"type":"Polygon","id":27121,"arcs":[[-785,788,789,-786,-685]]},{"type":"Polygon","id":41055,"arcs":[[790,-694,-763]]},{"type":"Polygon","id":27059,"arcs":[[791,792,793,-644,-663]]},{"type":"Polygon","id":27025,"arcs":[[-667,794,795,796,-792,-596]]},{"type":"Polygon","id":55095,"arcs":[[797,798,799,-795,-666]]},{"type":"Polygon","id":41051,"arcs":[[-691,-604,800,801,-778,-660]]},{"type":"Polygon","id":41027,"arcs":[[802,803,-801,-603,-696]]},{"type":"Polygon","id":41065,"arcs":[[-791,-762,804,805,806,807,-803,-695]]},{"type":"Polygon","id":16059,"arcs":[[-737,808,809,810,811,-520,-528]]},{"type":"Polygon","id":23029,"arcs":[[812,-598,-357,813]]},{"type":"Polygon","id":23007,"arcs":[[814,815,816,817,-567]]},{"type":"Polygon","id":26141,"arcs":[[818,819,-767,820]]},{"type":"Polygon","id":55005,"arcs":[[-674,821,822,823,-798,-665]]},{"type":"Polygon","id":55107,"arcs":[[-720,824,825,-822,-671]]},{"type":"Polygon","id":30009,"arcs":[[-700,826,827,-655,-680,-580]]},{"type":"Polygon","id":46129,"arcs":[[-740,828,829,830,-728,-733]]},{"type":"Polygon","id":46045,"arcs":[[-745,831,832,-829,-739]]},{"type":"Polygon","id":46037,"arcs":[[-750,-748,833,834,835,836,-742]]},{"type":"Polygon","id":27011,"arcs":[[-702,-788,837,838,839,840,-746]]},{"type":"Polygon","id":27141,"arcs":[[-645,-794,841,842,843,-781,-759]]},{"type":"Polygon","id":55069,"arcs":[[844,845,846,-718,-755]]},{"type":"Polygon","id":46041,"arcs":[[-831,847,848,849,850,-729]]},{"type":"Polygon","id":46137,"arcs":[[-851,851,852,-723,-730]]},{"type":"Polygon","id":55067,"arcs":[[-689,853,854,855,856,-845,-754]]},{"type":"Polygon","id":41005,"arcs":[[-804,-808,857,858,-779,-802]]},{"type":"Polygon","id":41071,"arcs":[[-859,859,860,861,-774,-780]]},{"type":"Polygon","id":27171,"arcs":[[-844,862,863,864,865,-782]]},{"type":"Polygon","id":27003,"arcs":[[-797,866,867,868,-842,-793]]},{"type":"Polygon","id":27067,"arcs":[[869,870,871,872,-789,-784]]},{"type":"Polygon","id":27151,"arcs":[[-873,873,874,-838,-787,-790]]},{"type":"Polygon","id":55119,"arcs":[[-847,875,876,877,-825,-719]]},{"type":"Polygon","id":55083,"arcs":[[878,879,880,881,882,883,884,-854,-688]]},{"type":"Polygon","id":23017,"arcs":[[-817,885,886,887,888,-889,889,890,891]]},{"type":"Polygon","id":46051,"arcs":[[-841,892,893,894,-834,-747]]},{"type":"Polygon","id":27093,"arcs":[[-866,895,896,-870,-783]]},{"type":"Polygon","id":33007,"arcs":[[897,898,899,900,-891]]},{"type":"Polygon","id":27163,"arcs":[[-800,901,902,903,904,-867,-796]]},{"type":"Polygon","id":55017,"arcs":[[-878,905,906,907,-823,-826]]},{"type":"Polygon","id":41047,"arcs":[[-860,-858,-807,908,909,910,-861]]},{"type":"Polygon","id":16003,"arcs":[[911,912,913,914,-709,-522]]},{"type":"Polygon","id":27073,"arcs":[[-839,-875,915,916,917,-893,-840]]},{"type":"MultiPolygon","id":23009,"arcs":[[[918]],[[919]],[[920,-599,-813]]]},{"type":"Polygon","id":46107,"arcs":[[-833,921,922,923,-848,-830]]},{"type":"Polygon","id":46049,"arcs":[[-744,924,925,926,-922,-832]]},{"type":"Polygon","id":27053,"arcs":[[-843,-869,927,928,929,930,-863]]},{"type":"Polygon","id":46115,"arcs":[[-837,931,932,933,-925,-743]]},{"type":"Polygon","id":16085,"arcs":[[-812,934,935,936,-912,-521]]},{"type":"Polygon","id":46019,"arcs":[[-725,937,938,939,-677,-731]]},{"type":"Polygon","id":55109,"arcs":[[940,941,-902,-799]]},{"type":"Polygon","id":55033,"arcs":[[-908,942,943,944,-941,-824]]},{"type":"Polygon","id":26009,"arcs":[[945,946,947,948,949]]},{"type":"Polygon","id":26137,"arcs":[[950,951,-946,952,-769]]},{"type":"Polygon","id":26119,"arcs":[[953,954,-951,-768,-820]]},{"type":"Polygon","id":46025,"arcs":[[955,956,957,958,-932,-836]]},{"type":"Polygon","id":46029,"arcs":[[-895,959,960,-956,-835]]},{"type":"Polygon","id":27023,"arcs":[[-872,961,962,-916,-874]]},{"type":"Polygon","id":27123,"arcs":[[-905,963,-928,-868]]},{"type":"Polygon","id":55073,"arcs":[[-846,-857,964,965,966,967,-876]]},{"type":"Polygon","id":55078,"arcs":[[-885,968,-855]]},{"type":"Polygon","id":41001,"arcs":[[-915,969,970,971,-756,-710]]},{"type":"Polygon","id":41053,"arcs":[[-911,972,973,974,-775,-862]]},{"type":"Polygon","id":41069,"arcs":[[-752,975,976,977,-805,-761]]},{"type":"Polygon","id":41041,"arcs":[[-975,978,979,980,-776]]},{"type":"Polygon","id":46093,"arcs":[[-853,981,982,983,-938,-724]]},{"type":"Polygon","id":55019,"arcs":[[-968,984,985,986,-906,-877]]},{"type":"Polygon","id":55115,"arcs":[[-969,-884,987,988,989,-965,-856]]},{"type":"Polygon","id":50011,"arcs":[[990,991,992,993,994]]},{"type":"Polygon","id":50009,"arcs":[[995,996,997,998,-900]]},{"type":"Polygon","id":50013,"arcs":[[999,1000,1001,-994]]},{"type":"Polygon","id":36019,"arcs":[[1002,1003,1004,1005,-1001]]},{"type":"Polygon","id":50019,"arcs":[[-998,1006,1007,-991,1008]]},{"type":"Polygon","id":56029,"arcs":[[1009,1010,1011,1012,1013,-650,-656,-828]]},{"type":"Polygon","id":36089,"arcs":[[1014,1015,1016,1017,1018,1019]]},{"type":"Polygon","id":56003,"arcs":[[1020,1021,1022,-1010,-827,-699]]},{"type":"Polygon","id":56005,"arcs":[[1023,1024,1025,1026,1027,-765]]},{"type":"Polygon","id":56033,"arcs":[[-766,-1028,1028,-1021,-698]]},{"type":"Polygon","id":36033,"arcs":[[-1005,1029,1030,-1015,1031]]},{"type":"Polygon","id":56011,"arcs":[[-678,-940,1032,1033,-1024,-764]]},{"type":"Polygon","id":41023,"arcs":[[-757,-972,1034,1035,1036,-976,-751,-707]]},{"type":"Polygon","id":27085,"arcs":[[-865,1037,1038,1039,-896]]},{"type":"Polygon","id":27019,"arcs":[[-931,1040,1041,-1038,-864]]},{"type":"Polygon","id":46039,"arcs":[[-918,1042,1043,1044,1045,-960,-894]]},{"type":"Polygon","id":27173,"arcs":[[1046,1047,1048,1049,-1043,-917,-963]]},{"type":"Polygon","id":27037,"arcs":[[-904,1050,1051,1052,1053,-929,-964]]},{"type":"Polygon","id":46119,"arcs":[[1054,1055,1056,-849,-924]]},{"type":"Polygon","id":46069,"arcs":[[-927,1057,1058,1059,1060,-1055,-923]]},{"type":"Polygon","id":46059,"arcs":[[-934,1061,1062,1063,-1058,-926]]},{"type":"Polygon","id":27129,"arcs":[[-897,-1040,1064,1065,1066,1067,-1047,-962,-871]]},{"type":"Polygon","id":16037,"arcs":[[1068,1069,1070,1071,1072,-935,-811]]},{"type":"Polygon","id":55093,"arcs":[[-945,1073,1074,-1051,-903,-942]]},{"type":"Polygon","id":26001,"arcs":[[1075,1076,1077,1078]]},{"type":"Polygon","id":26079,"arcs":[[1079,1080,1081,-947]]},{"type":"Polygon","id":26039,"arcs":[[1082,1083,-1080,-952]]},{"type":"Polygon","id":55035,"arcs":[[-987,1084,1085,1086,1087,-943,-907]]},{"type":"Polygon","id":26135,"arcs":[[-1078,1088,-1083,-955]]},{"type":"Polygon","id":16087,"arcs":[[1089,1090,1091,-970,-914]]},{"type":"Polygon","id":41031,"arcs":[[-978,1092,1093,1094,-909,-806]]},{"type":"Polygon","id":27139,"arcs":[[-1054,1095,1096,1097,-1041,-930]]},{"type":"Polygon","id":46057,"arcs":[[-1046,1098,1099,-957,-961]]},{"type":"Polygon","id":50015,"arcs":[[1100,1101,1102,-992,-1008]]},{"type":"Polygon","id":41043,"arcs":[[-1095,1103,1104,1105,-973,-910]]},{"type":"Polygon","id":46117,"arcs":[[-1057,1106,1107,1108,1109,-850]]},{"type":"Polygon","id":26019,"arcs":[[1110,1111,1112,1113]]},{"type":"Polygon","id":50005,"arcs":[[1114,1115,1116,-1101,-1007,-997]]},{"type":"MultiPolygon","id":23027,"arcs":[[[-601,1117,1118,1119,1120,-565]]]},{"type":"Polygon","id":16043,"arcs":[[1121,1122,1123,1124,1125,-735,-758,-652]]},{"type":"Polygon","id":46055,"arcs":[[1126,1127,1128,-982,-852,-1110]]},{"type":"Polygon","id":50007,"arcs":[[-1103,1129,1130,1131,-1003,-1000,-993]]},{"type":"Polygon","id":41003,"arcs":[[-1106,1132,-979,-974]]},{"type":"Polygon","id":23011,"arcs":[[1133,1134,1135,1136,-815,-566,-1121]]},{"type":"Polygon","id":27143,"arcs":[[-1042,-1098,1137,1138,-1065,-1039]]},{"type":"Polygon","id":27049,"arcs":[[1139,1140,1141,1142,1143,-1052,-1075]]},{"type":"Polygon","id":27127,"arcs":[[1144,1145,1146,1147,-1048,-1068]]},{"type":"Polygon","id":55097,"arcs":[[1148,1149,1150,1151,-966]]},{"type":"Polygon","id":55141,"arcs":[[-1152,1152,1153,1154,-985,-967]]},{"type":"Polygon","id":55009,"arcs":[[-883,1155,1156,1157,1158,1159,-988]]},{"type":"Polygon","id":55091,"arcs":[[-1088,1160,1161,-1140,-1074,-944]]},{"type":"Polygon","id":55135,"arcs":[[1162,1163,1164,-1149,-990]]},{"type":"Polygon","id":55061,"arcs":[[1165,1166,-1157,1167,1168]]},{"type":"Polygon","id":56039,"arcs":[[1169,1170,1171,1172,1173,-1122,-651,-1014]]},{"type":"Polygon","id":46005,"arcs":[[-959,1174,1175,1176,-1062,-933]]},{"type":"Polygon","id":27081,"arcs":[[1177,1178,1179,-1044,-1050]]},{"type":"Polygon","id":27083,"arcs":[[-1148,1180,1181,-1178,-1049]]},{"type":"Polygon","id":46081,"arcs":[[-984,1182,1183,-1033,-939]]},{"type":"Polygon","id":55011,"arcs":[[-1087,1184,1185,1186,-1161]]},{"type":"Polygon","id":55121,"arcs":[[1187,1188,1189,1190,-1185,-1086]]},{"type":"Polygon","id":55053,"arcs":[[-986,-1155,1191,1192,1193,-1188,-1085]]},{"type":"Polygon","id":55087,"arcs":[[-989,-1160,1194,1195,-1163]]},{"type":"Polygon","id":16033,"arcs":[[-1126,1196,1197,-809,-736]]},{"type":"Polygon","id":56019,"arcs":[[-1027,1198,1199,1200,-1022,-1029]]},{"type":"Polygon","id":41013,"arcs":[[-977,-1037,1201,1202,-1093]]},{"type":"Polygon","id":46065,"arcs":[[-1061,1203,-1107,-1056]]},{"type":"Polygon","id":36031,"arcs":[[1204,1205,1206,1207,-1030,-1004,-1132]]},{"type":"Polygon","id":27079,"arcs":[[1208,1209,1210,1211,-1138,-1097]]},{"type":"Polygon","id":27131,"arcs":[[-1053,-1144,1212,1213,1214,-1209,-1096]]},{"type":"Polygon","id":46077,"arcs":[[-1100,1215,1216,1217,1218,-1175,-958]]},{"type":"Polygon","id":46011,"arcs":[[-1180,1219,1220,1221,-1216,-1099,-1045]]},{"type":"Polygon","id":26101,"arcs":[[1222,1223,1224,1225,-1112]]},{"type":"Polygon","id":26165,"arcs":[[1226,1227,1228,-1223,1229]]},{"type":"Polygon","id":26143,"arcs":[[1230,1231,1232,1233,-1084]]},{"type":"Polygon","id":26113,"arcs":[[-1234,1234,1235,-1227,-1081]]},{"type":"Polygon","id":26069,"arcs":[[1236,1237,1238,-1077]]},{"type":"Polygon","id":26129,"arcs":[[-1239,1239,1240,-1231,-1089]]},{"type":"Polygon","id":16045,"arcs":[[-937,1241,1242,1243,1244,-1090,-913]]},{"type":"Polygon","id":46103,"arcs":[[-1129,1245,1246,1247,1248,-1183,-983]]},{"type":"Polygon","id":50023,"arcs":[[1249,1250,-1130,-1102,-1117]]},{"type":"Polygon","id":27015,"arcs":[[1251,1252,1253,1254,-1145,-1067]]},{"type":"Polygon","id":23001,"arcs":[[1255,1256,-886,-816,-1137]]},{"type":"Polygon","id":27103,"arcs":[[1257,-1252,-1066,-1139,-1212]]},{"type":"Polygon","id":27157,"arcs":[[-1162,-1187,1258,1259,-1141]]},{"type":"Polygon","id":41045,"arcs":[[-1092,1260,1261,1262,1263,1264,-1035,-971]]},{"type":"Polygon","id":33009,"arcs":[[1265,1266,1267,1268,1269,1270,-1115,-996,-899]]},{"type":"Polygon","id":41017,"arcs":[[-1203,1271,1272,1273,1274,-1104,-1094]]},{"type":"Polygon","id":36045,"arcs":[[1275,1276,-1019,1277,1278,1279,1280,1281]]},{"type":"MultiPolygon","id":23013,"arcs":[[[1282,1283,-1119]]]},{"type":"Polygon","id":16015,"arcs":[[-1073,1284,1285,-1242,-936]]},{"type":"Polygon","id":23015,"arcs":[[-1120,-1284,1286,1287,1288,-1134]]},{"type":"Polygon","id":55071,"arcs":[[-1167,1289,1290,1291,-1158]]},{"type":"Polygon","id":50001,"arcs":[[-1251,1292,1293,1294,1295,-1205,-1131]]},{"type":"Polygon","id":41039,"arcs":[[-1275,1296,1297,1298,-980,-1133,-1105]]},{"type":"Polygon","id":33003,"arcs":[[-890,888,1299,1300,1301,-1266,-898]]},{"type":"Polygon","id":27013,"arcs":[[-1211,1302,1303,1304,1305,-1253,-1258]]},{"type":"Polygon","id":55057,"arcs":[[1306,1307,1308,1309,-1192,-1154]]},{"type":"Polygon","id":55001,"arcs":[[-1151,1310,1311,1312,-1307,-1153]]},{"type":"Polygon","id":55137,"arcs":[[-1165,1313,1314,1315,-1311,-1150]]},{"type":"Polygon","id":55139,"arcs":[[1316,1317,1318,-1314,-1164,-1196]]},{"type":"Polygon","id":55015,"arcs":[[-1159,-1292,1319,1320,-1317,-1195]]},{"type":"Polygon","id":16023,"arcs":[[-1198,1321,1322,1323,-1070,-1069,-810]]},{"type":"Polygon","id":50017,"arcs":[[-1271,1324,-1293,-1250,-1116]]},{"type":"Polygon","id":36049,"arcs":[[1325,1326,-1278,-1018,1327]]},{"type":"Polygon","id":46085,"arcs":[[1328,1329,1330,1331,1332,-1108,-1204,-1060,1333]]},{"type":"Polygon","id":27117,"arcs":[[-1182,1334,1335,1336,-1220,-1179]]},{"type":"Polygon","id":27101,"arcs":[[-1147,1337,1338,1339,-1335,-1181]]},{"type":"Polygon","id":46073,"arcs":[[-1177,1340,1341,1342,1343,-1063]]},{"type":"Polygon","id":27147,"arcs":[[1344,1345,1346,1347,-1214]]},{"type":"Polygon","id":27039,"arcs":[[1348,1349,-1345,-1213,-1143]]},{"type":"Polygon","id":46101,"arcs":[[-1337,1350,1351,-1221]]},{"type":"Polygon","id":27161,"arcs":[[-1348,1352,1353,-1303,-1210,-1215]]},{"type":"Polygon","id":46017,"arcs":[[-1344,1354,-1334,-1059,-1064]]},{"type":"Polygon","id":46111,"arcs":[[-1219,1355,1356,1357,1358,-1341,-1176]]},{"type":"Polygon","id":27109,"arcs":[[-1260,1359,1360,1361,-1349,-1142]]},{"type":"Polygon","id":27033,"arcs":[[-1255,1362,1363,1364,-1338,-1146]]},{"type":"Polygon","id":46079,"arcs":[[-1222,-1352,1365,1366,1367,-1217]]},{"type":"Polygon","id":46097,"arcs":[[-1368,1368,1369,-1356,-1218]]},{"type":"Polygon","id":27169,"arcs":[[-1186,-1191,1370,1371,1372,-1360,-1259]]},{"type":"Polygon","id":56045,"arcs":[[-1184,-1249,1373,1374,1375,-1025,-1034]]},{"type":"Polygon","id":26105,"arcs":[[1376,1377,1378,-1225]]},{"type":"Polygon","id":23005,"arcs":[[-1257,1379,1380,1381,1382,1383,-887]]},{"type":"Polygon","id":46075,"arcs":[[-1333,1384,1385,-1386,1386,-1127,-1109]]},{"type":"Polygon","id":56043,"arcs":[[-1201,1387,1388,1389,-1011,-1023]]},{"type":"MultiPolygon","id":23023,"arcs":[[[-1382,1390]],[[-1288,1391]],[[1392,-1380,-1256,-1136]]]},{"type":"Polygon","id":26085,"arcs":[[-1224,-1229,1393,1394,-1377]]},{"type":"Polygon","id":26133,"arcs":[[-1236,1395,1396,-1394,-1228]]},{"type":"Polygon","id":26035,"arcs":[[-1233,1397,1398,-1396,-1235]]},{"type":"Polygon","id":26051,"arcs":[[1399,1400,1401,-1398,-1232,-1241]]},{"type":"Polygon","id":55081,"arcs":[[-1310,1402,1403,-1193]]},{"type":"Polygon","id":16075,"arcs":[[-1245,1404,-1261,-1091]]},{"type":"Polygon","id":36041,"arcs":[[1405,1406,1407,1408,-1016,-1031,-1208]]},{"type":"Polygon","id":27165,"arcs":[[-1306,1409,1410,-1363,-1254]]},{"type":"Polygon","id":16039,"arcs":[[-1072,1411,1412,1413,1414,1415,1416,-1285]]},{"type":"Polygon","id":36043,"arcs":[[-1409,1417,1418,1419,1420,-1328,-1017]]},{"type":"Polygon","id":55063,"arcs":[[-1194,-1404,1421,1422,-1371,-1190,-1189]]},{"type":"Polygon","id":56017,"arcs":[[-1390,1423,-1012]]},{"type":"Polygon","id":16051,"arcs":[[-1125,1424,1425,1426,-1322,-1197]]},{"type":"Polygon","id":41025,"arcs":[[-1265,1427,1428,1429,-1272,-1202,-1036]]},{"type":"Polygon","id":56013,"arcs":[[-1424,-1389,1430,1431,1432,1433,-1170,-1013]]},{"type":"Polygon","id":26017,"arcs":[[1434,1435,1436,1437,-1401,1438]]},{"type":"Polygon","id":46071,"arcs":[[-1387,1385,1439,1440,1441,-1246,-1128]]},{"type":"Polygon","id":16013,"arcs":[[-1324,1442,1443,1444,1445,1446,1447,-1412,-1071]]},{"type":"Polygon","id":16081,"arcs":[[-1174,1448,1449,-1123]]},{"type":"Polygon","id":55047,"arcs":[[-1319,1450,1451,1452,1453,-1315]]},{"type":"Polygon","id":55077,"arcs":[[-1454,1454,-1312,-1316]]},{"type":"Polygon","id":50027,"arcs":[[-1270,1455,1456,1457,1458,-1294,-1325]]},{"type":"Polygon","id":41019,"arcs":[[1459,1460,1461,1462,1463,1464,-1298]]},{"type":"Polygon","id":55039,"arcs":[[1465,1466,1467,-1451,-1318,-1321]]},{"type":"Polygon","id":46003,"arcs":[[-1359,1468,1469,1470,1471,-1342]]},{"type":"Polygon","id":46015,"arcs":[[-1472,1472,-1329,-1355,-1343]]},{"type":"Polygon","id":16065,"arcs":[[-1450,1473,-1425,-1124]]},{"type":"Polygon","id":55117,"arcs":[[1474,1475,1476,-1466,-1320,-1291]]},{"type":"Polygon","id":16027,"arcs":[[-1244,1477,1478,-1262,-1405]]},{"type":"Polygon","id":46095,"arcs":[[-1332,1479,1480,-1440,-1386,-1385]]},{"type":"Polygon","id":16025,"arcs":[[1481,1482,-1413,-1448]]},{"type":"Polygon","id":50021,"arcs":[[1483,1484,-1295,-1459]]},{"type":"Polygon","id":46033,"arcs":[[1485,1486,1487,-1374,-1248]]},{"type":"Polygon","id":27133,"arcs":[[-1340,1488,1489,1490,-1336]]},{"type":"Polygon","id":27105,"arcs":[[-1365,1491,1492,1493,-1489,-1339]]},{"type":"Polygon","id":27047,"arcs":[[1494,1495,1496,1497,-1353,-1347]]},{"type":"Polygon","id":27099,"arcs":[[-1362,1498,1499,1500,1501,-1495,-1346,-1350]]},{"type":"Polygon","id":27055,"arcs":[[-1423,1502,1503,1504,1505,-1372]]},{"type":"Polygon","id":46035,"arcs":[[1506,1507,1508,-1469,-1358]]},{"type":"Polygon","id":46061,"arcs":[[-1370,1509,1510,-1507,-1357]]},{"type":"Polygon","id":27045,"arcs":[[-1373,-1506,1511,1512,-1499,-1361]]},{"type":"Polygon","id":27063,"arcs":[[-1411,1513,1514,1515,1516,-1492,-1364]]},{"type":"Polygon","id":27043,"arcs":[[-1354,-1498,1517,1518,1519,-1304]]},{"type":"Polygon","id":46099,"arcs":[[1520,1521,1522,1523,-1366,-1351,-1491]]},{"type":"Polygon","id":27091,"arcs":[[-1305,-1520,1524,1525,-1514,-1410]]},{"type":"Polygon","id":46087,"arcs":[[-1367,-1524,1526,1527,-1510,-1369]]},{"type":"Polygon","id":26111,"arcs":[[-1438,1528,1529,1530,-1402]]},{"type":"Polygon","id":26127,"arcs":[[1531,1532,1533,-1378]]},{"type":"Polygon","id":26073,"arcs":[[-1531,1534,1535,1536,-1399]]},{"type":"Polygon","id":23031,"arcs":[[1537,1538,-1300,-889,-888,-1384]]},{"type":"Polygon","id":26107,"arcs":[[-1537,1539,1540,-1397]]},{"type":"Polygon","id":26123,"arcs":[[-1541,1541,1542,1543,-1532,-1395]]},{"type":"Polygon","id":36115,"arcs":[[-1485,1544,1545,1546,1547,-1206,-1296]]},{"type":"Polygon","id":16001,"arcs":[[-1286,-1417,1548,-1478,-1243]]},{"type":"Polygon","id":36113,"arcs":[[1549,-1406,-1207,-1548]]},{"type":"Polygon","id":46123,"arcs":[[1550,1551,1552,-1480,-1331]]},{"type":"Polygon","id":33001,"arcs":[[1553,1554,-1267,-1302]]},{"type":"Polygon","id":26157,"arcs":[[1555,1556,1557,1558,1559,-1436,1560,1561]]},{"type":"Polygon","id":55123,"arcs":[[1562,1563,1564,1565,-1503,-1422,-1403,-1309]]},{"type":"Polygon","id":36075,"arcs":[[-1327,1566,1567,1568,1569,1570,-1279]]},{"type":"Polygon","id":46113,"arcs":[[1571,1572,1573,1574,-1486,-1247,-1442]]},{"type":"Polygon","id":26151,"arcs":[[1575,1576,-1557,1577,1578]]},{"type":"Polygon","id":16073,"arcs":[[-1549,-1416,1579,1580,1581,-1263,-1479]]},{"type":"Polygon","id":55021,"arcs":[[1582,1583,1584,-1313,-1455,-1453]]},{"type":"Polygon","id":55111,"arcs":[[-1585,1585,1586,1587,-1563,-1308]]},{"type":"Polygon","id":55027,"arcs":[[1588,1589,1590,1591,-1583,-1452,-1468]]},{"type":"Polygon","id":16019,"arcs":[[-1474,-1449,-1173,1592,1593,1594,-1426]]},{"type":"Polygon","id":16011,"arcs":[[1595,1596,1597,-1443,-1323,-1427,-1595]]},{"type":"Polygon","id":41035,"arcs":[[1598,1599,1600,1601,-1460,-1297,-1274]]},{"type":"Polygon","id":41037,"arcs":[[1602,1603,-1599,-1273,-1430]]},{"type":"Polygon","id":36065,"arcs":[[1604,1605,-1567,-1326,-1421]]},{"type":"Polygon","id":33013,"arcs":[[-1555,1606,1607,1608,1609,-1268]]},{"type":"Polygon","id":41011,"arcs":[[1610,1611,-1464]]},{"type":"Polygon","id":33019,"arcs":[[-1610,1612,1613,1614,-1456,-1269]]},{"type":"Polygon","id":33017,"arcs":[[-1539,1615,1616,-1607,-1554,-1301]]},{"type":"Polygon","id":26145,"arcs":[[-1560,1617,1618,1619,-1529,-1437]]},{"type":"Polygon","id":55103,"arcs":[[1620,1621,1622,-1564,-1588]]},{"type":"Polygon","id":55089,"arcs":[[1623,1624,1625,-1476]]},{"type":"Polygon","id":55131,"arcs":[[-1477,-1626,1626,-1589,-1467]]},{"type":"Polygon","id":19189,"arcs":[[1627,1628,1629,-1518,-1497]]},{"type":"Polygon","id":19109,"arcs":[[1630,1631,1632,-1525,-1519,-1630,1633]]},{"type":"Polygon","id":19059,"arcs":[[1634,1635,-1516,1636]]},{"type":"Polygon","id":19063,"arcs":[[1637,-1637,-1515,-1526,-1633]]},{"type":"Polygon","id":19195,"arcs":[[1638,-1628,-1496,-1502,1639]]},{"type":"Polygon","id":19143,"arcs":[[1640,1641,-1493,-1517,-1636]]},{"type":"Polygon","id":56027,"arcs":[[-1488,1642,1643,1644,1645,1646,-1375]]},{"type":"Polygon","id":19131,"arcs":[[1647,1648,-1640,-1501,1649]]},{"type":"Polygon","id":19119,"arcs":[[1650,1651,-1521,-1490,-1494,-1642]]},{"type":"Polygon","id":19089,"arcs":[[1652,1653,-1650,-1500,-1513]]},{"type":"Polygon","id":19005,"arcs":[[-1566,1654,1655,1656,-1504]]},{"type":"Polygon","id":19191,"arcs":[[1657,1658,-1653,-1512,-1505,-1657]]},{"type":"Polygon","id":46083,"arcs":[[-1652,1659,1660,1661,1662,-1522]]},{"type":"Polygon","id":56009,"arcs":[[-1647,1663,1664,1665,1666,-1199,-1026,-1376]]},{"type":"Polygon","id":46023,"arcs":[[-1471,1667,1668,1669,1670,1671,1672,-1473]]},{"type":"Polygon","id":46125,"arcs":[[-1523,-1663,1673,1674,1675,-1527]]},{"type":"Polygon","id":46067,"arcs":[[-1528,-1676,1676,1677,-1669,1678,-1508,-1511]]},{"type":"Polygon","id":56025,"arcs":[[-1200,-1667,1679,-1431,-1388]]},{"type":"Polygon","id":46043,"arcs":[[-1509,-1679,-1668,-1470]]},{"type":"Polygon","id":46053,"arcs":[[-1673,1680,1681,-1551,-1330]]},{"type":"Polygon","id":46047,"arcs":[[-1575,1682,1683,-1643,-1487]]},{"type":"Polygon","id":26121,"arcs":[[-1544,1684,1685,1686,-1533]]},{"type":"Polygon","id":26117,"arcs":[[-1536,1687,1688,1689,-1542,-1540]]},{"type":"Polygon","id":56035,"arcs":[[1690,1691,-1171,-1434]]},{"type":"Polygon","id":26057,"arcs":[[-1530,-1620,1692,1693,-1688,-1535]]},{"type":"Polygon","id":55023,"arcs":[[-1623,1694,1695,-1655,-1565]]},{"type":"Polygon","id":36011,"arcs":[[1696,1697,1698,1699,1700,1701,-1570]]},{"type":"Polygon","id":36091,"arcs":[[-1547,1702,1703,1704,1705,1706,-1407,-1550]]},{"type":"Polygon","id":46007,"arcs":[[1707,1708,-1572,-1441]]},{"type":"Polygon","id":46121,"arcs":[[-1553,1709,-1708,-1481]]},{"type":"Polygon","id":36073,"arcs":[[1710,1711,1712,1713]]},{"type":"Polygon","id":36063,"arcs":[[-1713,1714,1715,1716]]},{"type":"Polygon","id":36055,"arcs":[[1717,1718,1719,1720,-1711,1721]]},{"type":"Polygon","id":36117,"arcs":[[1722,1723,-1718,1724,-1701]]},{"type":"Polygon","id":26087,"arcs":[[1725,1726,1727,1728,-1558,-1577]]},{"type":"Polygon","id":56023,"arcs":[[-1692,1729,1730,1731,1732,1733,-1593,-1172]]},{"type":"Polygon","id":50003,"arcs":[[-1458,1734,1735,1736,1737,-1545,-1484]]},{"type":"Polygon","id":55025,"arcs":[[-1592,1738,1739,1740,1741,-1586,-1584]]},{"type":"Polygon","id":26081,"arcs":[[1742,1743,1744,1745,-1685,-1543,-1690]]},{"type":"Polygon","id":36035,"arcs":[[-1707,1746,-1418,-1408]]},{"type":"Polygon","id":33015,"arcs":[[1747,1748,1749,-1608,-1617]]},{"type":"Polygon","id":36067,"arcs":[[1750,1751,-1697,-1569]]},{"type":"Polygon","id":50025,"arcs":[[-1615,1752,1753,-1735,-1457]]},{"type":"Polygon","id":19167,"arcs":[[1754,1755,1756,-1660,-1651]]},{"type":"Polygon","id":19141,"arcs":[[1757,1758,-1755,-1641]]},{"type":"Polygon","id":19033,"arcs":[[-1649,1759,1760,1761,-1639]]},{"type":"Polygon","id":19081,"arcs":[[-1762,1762,-1634,-1629]]},{"type":"Polygon","id":19147,"arcs":[[-1632,1763,1764,-1638]]},{"type":"Polygon","id":19041,"arcs":[[-1765,1765,-1758,-1635]]},{"type":"Polygon","id":26049,"arcs":[[-1729,1766,1767,1768,-1618,-1559]]},{"type":"Polygon","id":19037,"arcs":[[-1659,1769,1770,1771,-1654]]},{"type":"Polygon","id":19067,"arcs":[[-1772,1772,-1760,-1648]]},{"type":"Polygon","id":55049,"arcs":[[-1587,-1742,1773,1774,1775,-1621]]},{"type":"Polygon","id":55043,"arcs":[[-1776,1776,1777,1778,1779,-1695,-1622]]},{"type":"Polygon","id":33011,"arcs":[[-1750,1780,1781,1782,1783,-1613,-1609]]},{"type":"Polygon","id":26139,"arcs":[[-1746,1784,1785,-1686]]},{"type":"Polygon","id":16063,"arcs":[[1786,1787,1788,-1482,-1447]]},{"type":"Polygon","id":16047,"arcs":[[-1789,1789,1790,1791,-1414,-1483]]},{"type":"Polygon","id":55055,"arcs":[[1792,1793,1794,-1739,-1591]]},{"type":"Polygon","id":16067,"arcs":[[1795,1796,-1787,-1446]]},{"type":"Polygon","id":55133,"arcs":[[-1627,1797,1798,1799,-1793,-1590]]},{"type":"Polygon","id":55079,"arcs":[[1800,1801,-1798,-1625]]},{"type":"Polygon","id":36053,"arcs":[[-1606,1802,1803,1804,-1751,-1568]]},{"type":"Polygon","id":33005,"arcs":[[-1784,1805,1806,-1753,-1614]]},{"type":"Polygon","id":46135,"arcs":[[1807,1808,1809,1810,-1677,-1675]]},{"type":"Polygon","id":46009,"arcs":[[-1811,1811,-1670,-1678]]},{"type":"Polygon","id":26155,"arcs":[[-1769,1812,1813,1814,-1693,-1619]]},{"type":"Polygon","id":36037,"arcs":[[1815,1816,1817,-1715,-1712,-1721]]},{"type":"Polygon","id":26067,"arcs":[[1818,1819,1820,-1743,-1689]]},{"type":"Polygon","id":26037,"arcs":[[-1815,1821,1822,-1819,-1694]]},{"type":"Polygon","id":16077,"arcs":[[-1598,1823,1824,1825,-1444]]},{"type":"MultiPolygon","id":36029,"arcs":[[[1826]],[[-1818,1827,1828,1829,1830,1831,-1716]]]},{"type":"Polygon","id":46127,"arcs":[[-1757,1832,1833,1834,1835,1836,-1661]]},{"type":"Polygon","id":46027,"arcs":[[-1837,1837,1838,-1808,-1674,-1662]]},{"type":"Polygon","id":19065,"arcs":[[1839,1840,1841,-1770,-1658]]},{"type":"Polygon","id":19043,"arcs":[[-1696,-1780,1842,1843,-1840,-1656]]},{"type":"Polygon","id":36057,"arcs":[[-1706,1844,1845,1846,-1419,-1747]]},{"type":"Polygon","id":36069,"arcs":[[1847,1848,1849,1850,-1719,-1724]]},{"type":"Polygon","id":16005,"arcs":[[1851,1852,1853,-1824,-1597]]},{"type":"Polygon","id":16029,"arcs":[[-1594,-1734,1854,1855,-1852,-1596]]},{"type":"Polygon","id":36099,"arcs":[[1856,1857,1858,-1848,-1723,-1700]]},{"type":"Polygon","id":31165,"arcs":[[1859,1860,1861,-1644,-1684,1862]]},{"type":"Polygon","id":31161,"arcs":[[1863,1864,1865,1866,1867,-1573,1868]]},{"type":"Polygon","id":31045,"arcs":[[1869,-1863,-1683,-1574,-1868]]},{"type":"Polygon","id":31015,"arcs":[[1870,1871,1872,-1681,-1672]]},{"type":"Polygon","id":31103,"arcs":[[-1873,1873,1874,1875,-1552,-1682]]},{"type":"Polygon","id":31031,"arcs":[[-1710,-1876,1876,1877,1878,1879,1880,-1869,-1709]]},{"type":"Polygon","id":41029,"arcs":[[-1602,1881,1882,-1461]]},{"type":"Polygon","id":36051,"arcs":[[-1851,1883,1884,1885,-1816,-1720]]},{"type":"Polygon","id":36083,"arcs":[[-1738,1886,1887,1888,-1703,-1546]]},{"type":"Polygon","id":36093,"arcs":[[1889,1890,-1845,-1705]]},{"type":"Polygon","id":41015,"arcs":[[-1463,1891,1892,1893,-1611]]},{"type":"Polygon","id":16083,"arcs":[[-1791,1894,1895,1896,-1580,-1415,-1792]]},{"type":"Polygon","id":19149,"arcs":[[1897,1898,-1833,-1756]]},{"type":"Polygon","id":19021,"arcs":[[1899,1900,1901,-1766]]},{"type":"Polygon","id":19035,"arcs":[[-1902,1902,1903,-1898,-1759]]},{"type":"Polygon","id":19151,"arcs":[[1904,1905,1906,-1900,-1764]]},{"type":"Polygon","id":19197,"arcs":[[1907,1908,1909,1910,-1763]]},{"type":"Polygon","id":19091,"arcs":[[-1911,1911,-1905,-1631]]},{"type":"Polygon","id":19069,"arcs":[[1912,1913,-1908,-1761]]},{"type":"Polygon","id":19023,"arcs":[[1914,1915,1916,-1913,-1773]]},{"type":"Polygon","id":19017,"arcs":[[-1842,1917,-1915,-1771]]},{"type":"Polygon","id":36077,"arcs":[[-1847,1918,1919,1920,-1803,-1605,-1420]]},{"type":"Polygon","id":31089,"arcs":[[1921,1922,1923,1924,1925,1926,-1872]]},{"type":"Polygon","id":26099,"arcs":[[1927,1928,1929,-1727,1930]]},{"type":"Polygon","id":26125,"arcs":[[1931,1932,1933,-1767,-1728,-1930]]},{"type":"Polygon","id":25009,"arcs":[[1934,1935,1936,-1781,-1749]]},{"type":"Polygon","id":31107,"arcs":[[-1812,-1810,1937,1938,1939,-1922,-1871,-1671]]},{"type":"Polygon","id":31027,"arcs":[[-1839,1940,1941,1942,-1938,-1809]]},{"type":"Polygon","id":36121,"arcs":[[-1886,1943,1944,-1828,-1817]]},{"type":"Polygon","id":55045,"arcs":[[1945,1946,1947,1948,-1774,-1741]]},{"type":"Polygon","id":16053,"arcs":[[-1797,1949,-1895,-1790,-1788]]},{"type":"Polygon","id":55105,"arcs":[[-1795,1950,1951,1952,-1946,-1740]]},{"type":"Polygon","id":31017,"arcs":[[1953,1954,1955,-1877,-1875]]},{"type":"Polygon","id":55127,"arcs":[[-1800,1956,1957,1958,1959,-1951,-1794]]},{"type":"Polygon","id":55101,"arcs":[[1960,1961,-1957,-1799,-1802]]},{"type":"Polygon","id":36095,"arcs":[[-1891,1962,1963,1964,-1919,-1846]]},{"type":"Polygon","id":36001,"arcs":[[-1889,1965,-1963,-1890,-1704]]},{"type":"Polygon","id":55065,"arcs":[[-1949,1966,1967,-1777,-1775]]},{"type":"Polygon","id":31149,"arcs":[[1968,-1954,-1874,-1927]]},{"type":"Polygon","id":36023,"arcs":[[1969,1970,-1698,-1752,-1805,1971,1972]]},{"type":"Polygon","id":41033,"arcs":[[-1462,-1883,1973,1974,-1892]]},{"type":"Polygon","id":26093,"arcs":[[-1934,1975,1976,1977,-1813,-1768]]},{"type":"Polygon","id":26065,"arcs":[[-1978,1978,1979,-1822,-1814]]},{"type":"Polygon","id":26045,"arcs":[[-1823,-1980,1980,1981,1982,-1820]]},{"type":"Polygon","id":26015,"arcs":[[-1983,1983,1984,1985,-1744,-1821]]},{"type":"Polygon","id":26005,"arcs":[[-1745,-1986,1986,1987,1988,-1785]]},{"type":"Polygon","id":36123,"arcs":[[1989,1990,-1849,-1859]]},{"type":"Polygon","id":31051,"arcs":[[-1836,1991,1992,1993,-1941,-1838]]},{"type":"Polygon","id":25003,"arcs":[[1994,1995,1996,1997,1998,1999,-1887,-1737]]},{"type":"Polygon","id":36017,"arcs":[[-1921,2000,2001,2002,-1972,-1804]]},{"type":"Polygon","id":25011,"arcs":[[2003,-1995,-1736,-1754,-1807,2004]]},{"type":"Polygon","id":25017,"arcs":[[2005,2006,2007,2008,2009,2010,2011,2012,2013,-1782,-1937]]},{"type":"Polygon","id":25027,"arcs":[[-1783,-2014,2014,2015,2016,2017,2018,2019,-2005,-1806]]},{"type":"Polygon","id":16031,"arcs":[[-1826,2020,2021,2022,-1896,-1950,-1796,-1445]]},{"type":"Polygon","id":19061,"arcs":[[2023,2024,2025,2026,-1843,-1779]]},{"type":"Polygon","id":55059,"arcs":[[2027,2028,2029,-1958,-1962]]},{"type":"Polygon","id":19055,"arcs":[[-2027,2030,2031,2032,-1844]]},{"type":"Polygon","id":19187,"arcs":[[-1910,2033,2034,2035,2036,-1906,-1912]]},{"type":"Polygon","id":19019,"arcs":[[-2033,2037,2038,2039,-1841]]},{"type":"Polygon","id":19013,"arcs":[[-2040,2040,2041,2042,-1916,-1918]]},{"type":"Polygon","id":36109,"arcs":[[-1971,2043,2044,2045,-1857,-1699]]},{"type":"Polygon","id":56015,"arcs":[[-1862,2046,2047,2048,2049,-1645]]},{"type":"Polygon","id":56031,"arcs":[[-2050,2050,2051,-1664,-1646]]},{"type":"Polygon","id":16007,"arcs":[[-1733,2052,2053,-1855]]},{"type":"Polygon","id":36101,"arcs":[[-1850,-1991,2054,2055,2056,2057,2058,-1884]]},{"type":"Polygon","id":36013,"arcs":[[2059,2060,2061,2062,-1831]]},{"type":"Polygon","id":19193,"arcs":[[-1904,2063,2064,2065,2066,-1834,-1899]]},{"type":"Polygon","id":19161,"arcs":[[2067,2068,2069,2070,-1901]]},{"type":"Polygon","id":19093,"arcs":[[-2071,2071,-2064,-1903]]},{"type":"Polygon","id":19025,"arcs":[[-2037,2072,2073,-2068,-1907]]},{"type":"Polygon","id":19079,"arcs":[[2074,2075,2076,-2034,-1909]]},{"type":"Polygon","id":19083,"arcs":[[-1914,2077,2078,2079,-2075]]},{"type":"Polygon","id":19075,"arcs":[[-2043,2080,2081,-2078,-1917]]},{"type":"Polygon","id":25015,"arcs":[[-2020,2082,-1996,-2004]]},{"type":"Polygon","id":36097,"arcs":[[-2046,2083,-2055,-1990,-1858]]},{"type":"Polygon","id":36009,"arcs":[[-1829,-1945,2084,2085,2086,-2060,-1830]]},{"type":"Polygon","id":31043,"arcs":[[-1835,-2067,2087,-1992]]},{"type":"Polygon","id":36003,"arcs":[[-1885,-2059,2088,2089,-2085,-1944]]},{"type":"Polygon","id":36025,"arcs":[[-1965,2090,2091,2092,2093,2094,-2001,-1920]]},{"type":"Polygon","id":17085,"arcs":[[-1968,2095,2096,2097,-2024,-1778]]},{"type":"Polygon","id":36021,"arcs":[[2098,2099,2100,-1888,-2000]]},{"type":"Polygon","id":17177,"arcs":[[2101,-2096,-1967,-1948,2102,2103]]},{"type":"Polygon","id":17201,"arcs":[[2104,2105,-2103,-1947,-1953]]},{"type":"Polygon","id":16071,"arcs":[[-1854,2106,2107,2108,-2021,-1825]]},{"type":"Polygon","id":17111,"arcs":[[2109,2110,2111,2112,2113,-1959,-2030]]},{"type":"Polygon","id":17007,"arcs":[[-1960,-2114,2114,-2105,-1952]]},{"type":"Polygon","id":17097,"arcs":[[2115,-2110,-2029,2116]]},{"type":"Polygon","id":36039,"arcs":[[2117,-2091,-1964,-1966,-2101]]},{"type":"MultiPolygon","id":25025,"arcs":[[[2118,2119,-2012,2120,-2010]]]},{"type":"Polygon","id":31139,"arcs":[[2121,2122,2123,-1939,-1943]]},{"type":"Polygon","id":31013,"arcs":[[2124,2125,-1860,-1870,-1867]]},{"type":"Polygon","id":31003,"arcs":[[-2124,2126,2127,2128,-1923,-1940]]},{"type":"Polygon","id":26161,"arcs":[[2129,2130,2131,-1976,-1933,2132]]},{"type":"Polygon","id":56007,"arcs":[[-1666,2133,2134,2135,2136,2137,-1432,-1680]]},{"type":"Polygon","id":56001,"arcs":[[-2052,2138,2139,2140,-2134,-1665]]},{"type":"Polygon","id":16041,"arcs":[[-2054,2141,-2107,-1853,-1856]]},{"type":"Polygon","id":26075,"arcs":[[-1977,-2132,2142,2143,2144,-1981,-1979]]},{"type":"Polygon","id":26025,"arcs":[[-2145,2145,2146,2147,-1984,-1982]]},{"type":"Polygon","id":26159,"arcs":[[2148,2149,2150,2151,-1988]]},{"type":"Polygon","id":26077,"arcs":[[-2148,2152,-2149,-1987,-1985]]},{"type":"Polygon","id":36007,"arcs":[[-2002,-2095,2153,2154,2155,-1973,-2003]]},{"type":"Polygon","id":36107,"arcs":[[-2156,2156,2157,2158,-2044,-1970]]},{"type":"Polygon","id":19097,"arcs":[[-2098,2159,2160,2161,-2025]]},{"type":"Polygon","id":31179,"arcs":[[-1994,2162,2163,2164,-2122,-1942]]},{"type":"Polygon","id":25021,"arcs":[[-2120,2165,2166,2167,2168,-2015,-2013]]},{"type":"Polygon","id":25013,"arcs":[[-2019,2169,2170,2171,-1997,-2083]]},{"type":"Polygon","id":25023,"arcs":[[2172,2173,2174,2175,2176,-2167,2177]]},{"type":"Polygon","id":19011,"arcs":[[2178,2179,2180,-2041,-2039]]},{"type":"Polygon","id":19113,"arcs":[[-2032,2181,2182,2183,-2179,-2038]]},{"type":"Polygon","id":19171,"arcs":[[-2181,2184,2185,-2081,-2042]]},{"type":"Polygon","id":19105,"arcs":[[-2026,-2162,2186,2187,-2182,-2031]]},{"type":"Polygon","id":36015,"arcs":[[-2045,-2159,2188,2189,-2056,-2084]]},{"type":"Polygon","id":31173,"arcs":[[-2066,2190,2191,2192,-2163,-1993,-2088]]},{"type":"Polygon","id":56037,"arcs":[[-1433,-2138,2193,2194,2195,2196,-1730,-1691]]},{"type":"Polygon","id":42049,"arcs":[[2197,2198,2199,2200,-2062]]},{"type":"Polygon","id":26021,"arcs":[[2201,2202,2203,2204,-2151]]},{"type":"Polygon","id":19133,"arcs":[[2205,2206,2207,-2191,-2065]]},{"type":"Polygon","id":19127,"arcs":[[-2186,2208,2209,-2079,-2082]]},{"type":"Polygon","id":19027,"arcs":[[-2074,2210,2211,2212,2213,-2069]]},{"type":"Polygon","id":19047,"arcs":[[-2072,-2070,-2214,2214,2215,-2206]]},{"type":"Polygon","id":19015,"arcs":[[-2077,2216,2217,2218,2219,-2035]]},{"type":"Polygon","id":19073,"arcs":[[-2220,2220,2221,-2211,-2073,-2036]]},{"type":"Polygon","id":19169,"arcs":[[-2080,-2210,2222,2223,-2217,-2076]]},{"type":"Polygon","id":17141,"arcs":[[2224,2225,2226,2227,-2104,-2106]]},{"type":"Polygon","id":17015,"arcs":[[-2228,2228,2229,-2160,-2097,-2102]]},{"type":"Polygon","id":36111,"arcs":[[-2100,2230,2231,2232,-2092,-2118]]},{"type":"Polygon","id":17031,"arcs":[[-2116,2233,2234,2235,2236,2237,2238,-2111]]},{"type":"Polygon","id":17037,"arcs":[[2239,2240,2241,2242,-2225,-2115,-2113]]},{"type":"Polygon","id":17089,"arcs":[[-2239,2243,2244,-2240,-2112]]},{"type":"Polygon","id":31075,"arcs":[[2245,2246,2247,-1864,-1881]]},{"type":"Polygon","id":25005,"arcs":[[2248,2249,2250,2251,2252,-2168,-2177]]},{"type":"Polygon","id":31091,"arcs":[[2253,2254,2255,-2246,-1880]]},{"type":"Polygon","id":31039,"arcs":[[-2193,2256,2257,2258,2259,-2164]]},{"type":"Polygon","id":31119,"arcs":[[2260,2261,2262,-2127,-2123]]},{"type":"Polygon","id":31167,"arcs":[[-2260,2263,2264,-2261,-2165]]},{"type":"Polygon","id":31171,"arcs":[[2265,2266,2267,-2254,-1879]]},{"type":"Polygon","id":31183,"arcs":[[2268,2269,2270,2271,-1924,-2129]]},{"type":"Polygon","id":31009,"arcs":[[-1878,-1956,2272,2273,2274,-2266]]},{"type":"Polygon","id":31115,"arcs":[[2275,2276,-2273,-1955,-1969,-1926]]},{"type":"Polygon","id":31071,"arcs":[[-2272,2277,2278,-2276,-1925]]},{"type":"Polygon","id":26091,"arcs":[[2279,2280,2281,2282,-2143,-2131]]},{"type":"Polygon","id":36027,"arcs":[[-1999,2283,2284,2285,2286,-2231,-2099]]},{"type":"Polygon","id":25001,"arcs":[[-2175,2287]]},{"type":"Polygon","id":26023,"arcs":[[2288,2289,2290,2291,-2147]]},{"type":"Polygon","id":26059,"arcs":[[-2283,2292,2293,2294,-2289,-2146,-2144]]},{"type":"Polygon","id":26149,"arcs":[[-2292,2295,2296,2297,-2153]]},{"type":"Polygon","id":26027,"arcs":[[-2298,2298,2299,-2202,-2150]]},{"type":"Polygon","id":9005,"arcs":[[-2172,2300,2301,2302,-2284,-1998]]},{"type":"Polygon","id":31021,"arcs":[[2303,2304,2305,-2257,-2192,-2208]]},{"type":"Polygon","id":9003,"arcs":[[2306,2307,2308,2309,-2301,-2171]]},{"type":"Polygon","id":9013,"arcs":[[-2018,2310,2311,-2307,-2170]]},{"type":"Polygon","id":19045,"arcs":[[-2230,2312,2313,2314,2315,2316,-2187,-2161]]},{"type":"Polygon","id":9015,"arcs":[[2317,2318,2319,-2311,-2017]]},{"type":"Polygon","id":44007,"arcs":[[-2253,2320,2321,2322,-2318,-2016,-2169]]},{"type":"Polygon","id":36105,"arcs":[[2323,2324,2325,-2093,-2233]]},{"type":"Polygon","id":6093,"arcs":[[-1601,2326,2327,2328,2329,2330,-1974,-1882]]},{"type":"Polygon","id":31069,"arcs":[[-2248,2331,2332,2333,2334,2335,-1865]]},{"type":"Polygon","id":31123,"arcs":[[-2336,2336,2337,2338,-2125,-1866]]},{"type":"Polygon","id":49005,"arcs":[[2339,2340,2341,-2108,-2142]]},{"type":"Polygon","id":31157,"arcs":[[-2339,2342,-2047,-1861,-2126]]},{"type":"Polygon","id":49033,"arcs":[[-1732,2343,2344,2345,2346,-2340,-2053]]},{"type":"Polygon","id":42015,"arcs":[[-2189,-2158,2347,2348,2349,2350,2351]]},{"type":"Polygon","id":42117,"arcs":[[-2190,-2352,2352,2353,-2057]]},{"type":"Polygon","id":49003,"arcs":[[2354,2355,2356,-2022,-2109,-2342]]},{"type":"Polygon","id":32013,"arcs":[[2357,2358,2359,-1428,-1264,-1582,2360]]},{"type":"Polygon","id":32007,"arcs":[[-2023,-2357,2361,2362,2363,2364,-2361,-1581,-1897]]},{"type":"Polygon","id":42083,"arcs":[[-2090,2365,2366,2367,2368,-2086]]},{"type":"Polygon","id":42105,"arcs":[[-2058,-2354,2369,2370,2371,-2366,-2089]]},{"type":"Polygon","id":6015,"arcs":[[-2331,2372,2373,-1893,-1975]]},{"type":"Polygon","id":42127,"arcs":[[-2094,-2326,2374,2375,2376,2377,-2154]]},{"type":"Polygon","id":42115,"arcs":[[2378,2379,-2348,-2157,-2155,-2378]]},{"type":"Polygon","id":42123,"arcs":[[-2369,2380,2381,2382,-2198,-2061,-2087]]},{"type":"Polygon","id":6049,"arcs":[[-1604,2383,2384,2385,-2327,-1600]]},{"type":"Polygon","id":32031,"arcs":[[2386,2387,2388,2389,2390,2391,2392,2393,2394,-2384,-1603,-1429,-2360]]},{"type":"Polygon","id":17043,"arcs":[[2395,2396,-2244,-2238]]},{"type":"Polygon","id":39007,"arcs":[[2397,2398,2399,2400,2401,-2200]]},{"type":"Polygon","id":19031,"arcs":[[-2188,-2317,2402,2403,2404,-2183]]},{"type":"Polygon","id":17195,"arcs":[[-2227,2405,2406,2407,2408,-2313,-2229]]},{"type":"Polygon","id":31011,"arcs":[[-2263,2409,2410,2411,-2269,-2128]]},{"type":"Polygon","id":17103,"arcs":[[-2243,2412,2413,-2406,-2226]]},{"type":"Polygon","id":19085,"arcs":[[-2216,2414,2415,2416,-2304,-2207]]},{"type":"Polygon","id":19095,"arcs":[[2417,2418,2419,2420,-2180]]},{"type":"Polygon","id":19049,"arcs":[[2421,2422,2423,-2221,-2219]]},{"type":"Polygon","id":19165,"arcs":[[2424,2425,2426,-2415,-2215]]},{"type":"Polygon","id":19009,"arcs":[[2427,2428,-2425,-2213]]},{"type":"Polygon","id":19157,"arcs":[[-2421,2429,2430,2431,-2185]]},{"type":"Polygon","id":19153,"arcs":[[-2224,2432,2433,2434,-2422,-2218]]},{"type":"Polygon","id":19099,"arcs":[[-2209,-2432,2435,2436,-2433,-2223]]},{"type":"Polygon","id":19077,"arcs":[[-2424,2437,-2428,-2212,-2222]]},{"type":"Polygon","id":19103,"arcs":[[-2405,2438,2439,2440,-2418,-2184]]},{"type":"Polygon","id":39085,"arcs":[[2441,2442,2443,-2401]]},{"type":"Polygon","id":42039,"arcs":[[-2383,2444,2445,2446,-2398,-2199]]},{"type":"Polygon","id":17161,"arcs":[[2447,2448,2449,2450,2451,-2314,-2409]]},{"type":"Polygon","id":44001,"arcs":[[2452,-2321,-2252]]},{"type":"Polygon","id":19163,"arcs":[[-2452,2453,-2403,-2316,-2315]]},{"type":"Polygon","id":44003,"arcs":[[2454,2455,2456,-2319,-2323]]},{"type":"Polygon","id":18039,"arcs":[[2457,2458,2459,2460,2461,-2299,-2297]]},{"type":"Polygon","id":18141,"arcs":[[2462,2463,2464,-2203,-2300,-2462]]},{"type":"Polygon","id":18091,"arcs":[[2465,2466,2467,-2204,-2465]]},{"type":"Polygon","id":18151,"arcs":[[2468,2469,-2290,-2295,2470]]},{"type":"Polygon","id":18087,"arcs":[[2471,-2458,-2296,-2291,-2470]]},{"type":"Polygon","id":31037,"arcs":[[-2259,2472,2473,2474,-2264]]},{"type":"Polygon","id":31141,"arcs":[[-2475,2475,2476,2477,2478,-2410,-2262,-2265]]},{"type":"Polygon","id":31053,"arcs":[[-2306,2479,2480,2481,2482,-2473,-2258]]},{"type":"Polygon","id":31117,"arcs":[[-2268,2483,2484,2485,2486,-2255]]},{"type":"Polygon","id":31005,"arcs":[[-2487,2487,-2332,-2247,-2256]]},{"type":"Polygon","id":31077,"arcs":[[2488,2489,2490,2491,-2270,-2412]]},{"type":"Polygon","id":31041,"arcs":[[-2279,2492,2493,2494,2495,2496,2497,-2274,-2277]]},{"type":"Polygon","id":31113,"arcs":[[-2275,-2498,2498,-2484,-2267]]},{"type":"Polygon","id":31175,"arcs":[[-2271,-2492,2499,-2493,-2278]]},{"type":"Polygon","id":39095,"arcs":[[2500,2501,2502,2503,-2281,2504,2505,2506,2507,2508,2509]]},{"type":"Polygon","id":17197,"arcs":[[-2237,2510,2511,2512,2513,-2396]]},{"type":"Polygon","id":39123,"arcs":[[2514,2515,2516,2517,2518,-2501]]},{"type":"Polygon","id":17093,"arcs":[[-2514,2519,2520,-2241,-2245,-2397]]},{"type":"Polygon","id":39051,"arcs":[[2521,2522,-2293,-2282,-2504]]},{"type":"Polygon","id":39055,"arcs":[[2523,2524,2525,-2442,-2400]]},{"type":"Polygon","id":9011,"arcs":[[-2457,2526,2527,2528,-2308,-2312,-2320]]},{"type":"Polygon","id":18089,"arcs":[[2529,2530,2531,2532,-2511,-2236,2533]]},{"type":"Polygon","id":18127,"arcs":[[2534,-2530,2535,-2467]]},{"type":"Polygon","id":39171,"arcs":[[2536,2537,2538,-2471,-2294,-2523]]},{"type":"Polygon","id":31007,"arcs":[[-2338,2539,2540,2541,-2048,-2343]]},{"type":"Polygon","id":31177,"arcs":[[-2417,2542,2543,-2480,-2305]]},{"type":"MultiPolygon","id":44005,"arcs":[[[2544]],[[2545,-2250]]]},{"type":"Polygon","id":9001,"arcs":[[2546,2547,2548,2549,-2285,-2303]]},{"type":"Polygon","id":56021,"arcs":[[-2049,-2542,2550,2551,2552,-2139,-2051]]},{"type":"Polygon","id":44009,"arcs":[[-2527,-2456,2553]]},{"type":"Polygon","id":42131,"arcs":[[-2380,2554,2555,2556,-2349]]},{"type":"Polygon","id":9007,"arcs":[[-2529,2557,2558,-2309]]},{"type":"Polygon","id":9009,"arcs":[[-2559,2559,2560,2561,-2547,-2302,-2310]]},{"type":"Polygon","id":42069,"arcs":[[-2377,2562,2563,-2555,-2379]]},{"type":"Polygon","id":36071,"arcs":[[-2287,2564,2565,2566,2567,2568,-2324,-2232]]},{"type":"Polygon","id":17099,"arcs":[[-2521,2569,2570,2571,2572,2573,2574,-2413,-2242]]},{"type":"Polygon","id":39035,"arcs":[[-2526,2575,2576,2577,2578,2579,-2443]]},{"type":"Polygon","id":42047,"arcs":[[2580,2581,2582,2583,-2368]]},{"type":"Polygon","id":42053,"arcs":[[-2584,2584,2585,2586,-2381]]},{"type":"Polygon","id":42121,"arcs":[[2587,2588,2589,-2445,-2382,-2587]]},{"type":"Polygon","id":39043,"arcs":[[2590,2591,2592,2593,2594,2595]]},{"type":"Polygon","id":39173,"arcs":[[-2519,2596,2597,2598,2599,-2502]]},{"type":"Polygon","id":42023,"arcs":[[-2372,2600,2601,-2581,-2367]]},{"type":"Polygon","id":42103,"arcs":[[-2569,2602,2603,-2375,-2325]]},{"type":"Polygon","id":19139,"arcs":[[-2404,-2454,-2451,2604,-2439]]},{"type":"Polygon","id":42081,"arcs":[[2605,2606,2607,2608,2609,2610,-2370,-2353,-2351]]},{"type":"Polygon","id":42113,"arcs":[[-2606,-2350,-2557,2611,2612]]},{"type":"Polygon","id":17011,"arcs":[[-2575,2613,2614,2615,2616,-2407,-2414]]},{"type":"Polygon","id":17073,"arcs":[[-2617,2617,2618,2619,-2448,-2408]]},{"type":"Polygon","id":56041,"arcs":[[-2197,2620,-2344,-1731]]},{"type":"Polygon","id":18033,"arcs":[[2621,2622,2623,-2469,-2539]]},{"type":"Polygon","id":18113,"arcs":[[-2624,2624,2625,2626,-2459,-2472]]},{"type":"Polygon","id":31125,"arcs":[[2627,2628,-2489,-2411,-2479]]},{"type":"Polygon","id":36079,"arcs":[[2629,2630,-2565,-2286,-2550,2631]]},{"type":"Polygon","id":25007,"arcs":[[2632]]},{"type":"Polygon","id":39093,"arcs":[[-2579,2633,2634,2635,-2591,2636]]},{"type":"Polygon","id":19183,"arcs":[[2637,2638,2639,2640,-2419,-2441]]},{"type":"Polygon","id":19181,"arcs":[[2641,2642,2643,2644,-2435]]},{"type":"Polygon","id":19107,"arcs":[[-2641,2645,2646,2647,-2430,-2420]]},{"type":"Polygon","id":19121,"arcs":[[2648,2649,2650,-2423,-2645]]},{"type":"Polygon","id":19123,"arcs":[[-2648,2651,2652,2653,-2436,-2431]]},{"type":"Polygon","id":19125,"arcs":[[-2654,2654,2655,-2642,-2434,-2437]]},{"type":"Polygon","id":19155,"arcs":[[-2427,2656,2657,2658,2659,2660,-2543,-2416]]},{"type":"Polygon","id":19029,"arcs":[[-2429,2661,2662,2663,-2657,-2426]]},{"type":"Polygon","id":19001,"arcs":[[-2438,-2651,2664,2665,-2662]]},{"type":"Polygon","id":39155,"arcs":[[-2447,2666,2667,2668,-2524,-2399]]},{"type":"Polygon","id":39143,"arcs":[[2669,-2593,2670,2671,-2597,-2518]]},{"type":"Polygon","id":42085,"arcs":[[-2590,2672,2673,2674,-2667,-2446]]},{"type":"Polygon","id":39069,"arcs":[[-2503,-2600,2675,2676,-2537,-2522]]},{"type":"Polygon","id":18099,"arcs":[[-2461,2677,2678,2679,-2463]]},{"type":"Polygon","id":42035,"arcs":[[-2611,2680,2681,2682,-2601,-2371]]},{"type":"Polygon","id":6023,"arcs":[[-2330,2683,2684,2685,-2373]]},{"type":"Polygon","id":17063,"arcs":[[2686,2687,-2570,-2520,-2513]]},{"type":"Polygon","id":31023,"arcs":[[-2483,2688,2689,2690,2691,-2476,-2474]]},{"type":"Polygon","id":31155,"arcs":[[2692,2693,2694,2695,-2689,-2482]]},{"type":"Polygon","id":18085,"arcs":[[-2627,2696,2697,2698,-2678,-2460]]},{"type":"Polygon","id":31033,"arcs":[[-2335,2699,2700,2701,2702,-2540,-2337]]},{"type":"Polygon","id":42031,"arcs":[[2703,2704,2705,-2588,-2586]]},{"type":"Polygon","id":49057,"arcs":[[-2341,-2347,2706,2707,-2355]]},{"type":"Polygon","id":18149,"arcs":[[-2680,2708,2709,-2466,-2464]]},{"type":"Polygon","id":42079,"arcs":[[2710,2711,2712,2713,-2612,-2556,-2564]]},{"type":"Polygon","id":39039,"arcs":[[2714,2715,2716,-2622,-2538,-2677]]},{"type":"Polygon","id":19115,"arcs":[[-2605,-2450,2717,2718,2719,-2638,-2440]]},{"type":"Polygon","id":31101,"arcs":[[2720,2721,2722,-2333,-2488,-2486]]},{"type":"Polygon","id":31111,"arcs":[[-2499,-2497,2723,2724,2725,2726,-2721,-2485]]},{"type":"Polygon","id":25019,"arcs":[[2727]]},{"type":"Polygon","id":31143,"arcs":[[2728,2729,2730,-2477,-2692]]},{"type":"Polygon","id":31105,"arcs":[[-2703,2731,2732,-2551,-2541]]},{"type":"Polygon","id":31121,"arcs":[[-2731,2733,2734,2735,-2628,-2478]]},{"type":"Polygon","id":31093,"arcs":[[-2629,-2736,2736,2737,2738,-2490]]},{"type":"Polygon","id":31163,"arcs":[[-2491,-2739,2739,-2494,-2500]]},{"type":"Polygon","id":31055,"arcs":[[-2544,-2661,2740,-2693,-2481]]},{"type":"Polygon","id":49029,"arcs":[[2741,2742,2743,-2707,-2346]]},{"type":"Polygon","id":42065,"arcs":[[2744,2745,2746,-2704,-2585,-2583]]},{"type":"Polygon","id":6105,"arcs":[[2747,2748,2749,-2684,-2329]]},{"type":"Polygon","id":36119,"arcs":[[2750,2751,-2632,-2549,2752]]},{"type":"Polygon","id":34037,"arcs":[[2753,2754,2755,2756,-2603,-2568]]},{"type":"Polygon","id":39153,"arcs":[[2757,2758,2759,2760,-2577]]},{"type":"Polygon","id":39133,"arcs":[[-2669,2761,2762,-2758,-2576,-2525]]},{"type":"Polygon","id":17131,"arcs":[[-2620,2763,2764,2765,2766,-2718,-2449]]},{"type":"Polygon","id":17155,"arcs":[[-2574,2767,-2614]]},{"type":"Polygon","id":36087,"arcs":[[2768,2769,-2566,-2631,2770]]},{"type":"Polygon","id":42037,"arcs":[[2771,2772,2773,-2607,-2613,-2714]]},{"type":"Polygon","id":17091,"arcs":[[-2533,2774,2775,2776,2777,-2687,-2512]]},{"type":"Polygon","id":18183,"arcs":[[2778,2779,2780,-2697,-2626]]},{"type":"MultiPolygon","id":36103,"arcs":[[[2781,2782]]]},{"type":"Polygon","id":39077,"arcs":[[-2636,2783,2784,2785,2786,-2671,-2592]]},{"type":"Polygon","id":18073,"arcs":[[-2710,2787,2788,2789,2790,-2531,-2535]]},{"type":"Polygon","id":39103,"arcs":[[-2761,2791,2792,-2634,-2578]]},{"type":"Polygon","id":18003,"arcs":[[2793,2794,2795,2796,2797,-2779,-2625,-2623,-2717]]},{"type":"Polygon","id":39147,"arcs":[[-2787,2798,2799,2800,-2598,-2672]]},{"type":"Polygon","id":42033,"arcs":[[2801,2802,2803,2804,-2745,-2582,-2602,-2683]]},{"type":"Polygon","id":49043,"arcs":[[-2621,-2196,2805,2806,2807,2808,-2742,-2345]]},{"type":"Polygon","id":42027,"arcs":[[2809,2810,2811,2812,-2802,-2682]]},{"type":"Polygon","id":42089,"arcs":[[-2757,2813,2814,2815,-2711,-2563,-2376,-2604]]},{"type":"Polygon","id":39125,"arcs":[[2816,2817,-2794,-2716]]},{"type":"Polygon","id":17175,"arcs":[[-2616,2818,2819,2820,-2618]]},{"type":"Polygon","id":31049,"arcs":[[-2723,2821,2822,-2700,-2334]]},{"type":"Polygon","id":18111,"arcs":[[2823,2824,-2775,-2532,-2791]]},{"type":"Polygon","id":34031,"arcs":[[2825,2826,-2754,-2567,-2770,2827]]},{"type":"Polygon","id":31153,"arcs":[[-2660,2828,2829,-2694,-2741]]},{"type":"Polygon","id":6035,"arcs":[[-2395,2830,2831,2832,-2385]]},{"type":"Polygon","id":6089,"arcs":[[-2386,-2833,2833,2834,-2748,-2328]]},{"type":"Polygon","id":42097,"arcs":[[2835,-2773,2836,2837,2838,2839,2840,-2609]]},{"type":"Polygon","id":18049,"arcs":[[2841,2842,2843,2844,-2679,-2699]]},{"type":"Polygon","id":31081,"arcs":[[2845,2846,2847,2848,-2734,-2730]]},{"type":"Polygon","id":18131,"arcs":[[-2845,2849,2850,-2788,-2709]]},{"type":"Polygon","id":42093,"arcs":[[-2774,-2836,-2608]]},{"type":"Polygon","id":42019,"arcs":[[-2706,2851,2852,2853,2854,-2673,-2589]]},{"type":"Polygon","id":42005,"arcs":[[-2747,2855,2856,2857,-2852,-2705]]},{"type":"Polygon","id":39063,"arcs":[[-2801,2858,2859,2860,2861,-2599]]},{"type":"Polygon","id":39137,"arcs":[[-2862,2862,2863,-2817,-2715,-2676]]},{"type":"Polygon","id":19101,"arcs":[[-2640,2864,2865,2866,-2646]]},{"type":"Polygon","id":19087,"arcs":[[-2720,2867,2868,2869,-2865,-2639]]},{"type":"Polygon","id":19179,"arcs":[[-2867,2870,2871,-2652,-2647]]},{"type":"Polygon","id":19039,"arcs":[[2872,2873,2874,-2649,-2644]]},{"type":"Polygon","id":19117,"arcs":[[-2656,2875,2876,-2873,-2643]]},{"type":"Polygon","id":19135,"arcs":[[-2872,2877,-2876,-2655,-2653]]},{"type":"Polygon","id":19129,"arcs":[[2878,2879,2880,-2829,-2659]]},{"type":"Polygon","id":19137,"arcs":[[-2664,2881,2882,-2879,-2658]]},{"type":"Polygon","id":19003,"arcs":[[-2666,2883,2884,-2882,-2663]]},{"type":"Polygon","id":19175,"arcs":[[-2650,-2875,2885,-2884,-2665]]},{"type":"Polygon","id":49011,"arcs":[[-2744,2886,2887,-2708]]},{"type":"Polygon","id":17095,"arcs":[[-2821,2888,2889,2890,-2764,-2619]]},{"type":"Polygon","id":42119,"arcs":[[2891,2892,-2810,-2681,-2610,-2841]]},{"type":"Polygon","id":17123,"arcs":[[-2768,-2573,2893,2894,-2819,-2615]]},{"type":"Polygon","id":34003,"arcs":[[2895,2896,-2828,-2769,2897]]},{"type":"Polygon","id":39099,"arcs":[[-2675,2898,2899,2900,-2762,-2668]]},{"type":"Polygon","id":42025,"arcs":[[-2816,2901,2902,2903,-2712]]},{"type":"Polygon","id":42073,"arcs":[[-2855,2904,2905,-2899,-2674]]},{"type":"Polygon","id":17105,"arcs":[[-2778,2906,2907,2908,-2571,-2688]]},{"type":"Polygon","id":34041,"arcs":[[2909,2910,2911,2912,-2814,-2756]]},{"type":"Polygon","id":34027,"arcs":[[2913,2914,2915,2916,-2910,-2755,-2827]]},{"type":"Polygon","id":49045,"arcs":[[2917,2918,2919,2920,-2362,-2356,-2888]]},{"type":"Polygon","id":19057,"arcs":[[-2767,2921,2922,2923,-2868,-2719]]},{"type":"Polygon","id":17071,"arcs":[[2924,2925,2926,2927,-2922,-2766]]},{"type":"Polygon","id":17187,"arcs":[[-2891,2928,2929,-2925,-2765]]},{"type":"Polygon","id":39005,"arcs":[[-2793,2930,2931,2932,2933,-2784,-2635]]},{"type":"Polygon","id":31025,"arcs":[[-2881,2934,2935,2936,-2695,-2830]]},{"type":"Polygon","id":31185,"arcs":[[2937,2938,2939,-2846,-2729]]},{"type":"Polygon","id":31079,"arcs":[[-2735,-2849,2940,2941,-2737]]},{"type":"Polygon","id":31047,"arcs":[[2942,2943,2944,2945,-2724,-2496]]},{"type":"Polygon","id":31019,"arcs":[[-2738,-2942,2946,2947,2948,-2943,-2495,-2740]]},{"type":"Polygon","id":31159,"arcs":[[2949,2950,-2938,-2691]]},{"type":"Polygon","id":31109,"arcs":[[-2937,2951,2952,2953,-2950,-2690,-2696]]},{"type":"Polygon","id":18169,"arcs":[[-2781,2954,2955,2956,-2842,-2698]]},{"type":"Polygon","id":17075,"arcs":[[-2825,2957,2958,2959,-2776]]},{"type":"Polygon","id":18069,"arcs":[[-2798,2960,2961,-2955,-2780]]},{"type":"Polygon","id":8123,"arcs":[[2962,2963,2964,2965,2966,-2552,-2733,2967]]},{"type":"Polygon","id":31135,"arcs":[[-2727,2968,2969,2970,2971,-2822,-2722]]},{"type":"Polygon","id":8107,"arcs":[[2972,2973,2974,2975,2976,-2136,2977]]},{"type":"Polygon","id":8057,"arcs":[[2978,-2978,-2135,-2141,2979]]},{"type":"Polygon","id":8081,"arcs":[[-2977,2980,2981,2982,-2194,-2137]]},{"type":"Polygon","id":8075,"arcs":[[2983,2984,2985,-2968,-2732,-2702,2986,2987]]},{"type":"Polygon","id":8115,"arcs":[[2988,-2987,-2701,-2823,-2972]]},{"type":"Polygon","id":49009,"arcs":[[2989,2990,-2806,-2195,-2983]]},{"type":"Polygon","id":32011,"arcs":[[2991,2992,2993,-2364]]},{"type":"Polygon","id":32015,"arcs":[[-2994,2994,2995,2996,-2358,-2365]]},{"type":"Polygon","id":8069,"arcs":[[2997,2998,-2980,-2140,-2553,-2967]]},{"type":"Polygon","id":18103,"arcs":[[-2957,2999,3000,3001,-2843]]},{"type":"Polygon","id":39033,"arcs":[[3002,3003,3004,3005,-2799,-2786]]},{"type":"Polygon","id":17053,"arcs":[[3006,3007,3008,-2907,-2777,-2960]]},{"type":"Polygon","id":39139,"arcs":[[-2934,3009,3010,-3003,-2785]]},{"type":"Polygon","id":39175,"arcs":[[-3006,3011,3012,-2859,-2800]]},{"type":"Polygon","id":39169,"arcs":[[-2760,3013,3014,-2931,-2792]]},{"type":"Polygon","id":39161,"arcs":[[-2864,3015,3016,3017,3018,-2795,-2818]]},{"type":"Polygon","id":39151,"arcs":[[-2901,3019,3020,3021,3022,-3014,-2759,-2763]]},{"type":"Polygon","id":17143,"arcs":[[-2895,3023,3024,3025,-2889,-2820]]},{"type":"Polygon","id":42095,"arcs":[[3026,3027,-2902,-2815,-2913]]},{"type":"Polygon","id":32027,"arcs":[[-2997,3028,-2387,-2359]]},{"type":"Polygon","id":42107,"arcs":[[-2904,3029,3030,3031,3032,-2837,-2772,-2713]]},{"type":"Polygon","id":39029,"arcs":[[-2906,3033,3034,3035,3036,-3020,-2900]]},{"type":"Polygon","id":17203,"arcs":[[3037,3038,-3024,-2894,-2572,-2909]]},{"type":"Polygon","id":18001,"arcs":[[3039,3040,3041,-2796,-3019]]},{"type":"Polygon","id":39003,"arcs":[[-2861,3042,3043,-3016,-2863]]},{"type":"Polygon","id":49035,"arcs":[[-2743,-2809,3044,3045,-2918,-2887]]},{"type":"Polygon","id":36005,"arcs":[[3046,3047,3048,-2751]]},{"type":"Polygon","id":18179,"arcs":[[-3042,3049,3050,3051,-2961,-2797]]},{"type":"Polygon","id":36059,"arcs":[[-2782,3052,3053,3054,3055,3056]]},{"type":"Polygon","id":18181,"arcs":[[3057,3058,3059,3060,-2789,-2851]]},{"type":"Polygon","id":18017,"arcs":[[-2844,-3002,3061,3062,-3058,-2850]]},{"type":"Polygon","id":42063,"arcs":[[-2805,3063,3064,-2856,-2746]]},{"type":"Polygon","id":34013,"arcs":[[-2826,-2897,3065,3066,3067,-2914]]},{"type":"Polygon","id":19071,"arcs":[[3068,3069,3070,-2935,-2880]]},{"type":"Polygon","id":19145,"arcs":[[3071,3072,3073,-3069,-2883]]},{"type":"Polygon","id":19173,"arcs":[[3074,3075,3076,-3072,-2885]]},{"type":"Polygon","id":19177,"arcs":[[-2870,3077,3078,3079,3080,-2866]]},{"type":"Polygon","id":19051,"arcs":[[-3081,3081,3082,3083,-2871]]},{"type":"Polygon","id":19159,"arcs":[[3084,3085,3086,-3075,-2886]]},{"type":"Polygon","id":19053,"arcs":[[3087,3088,3089,-3085,-2874]]},{"type":"Polygon","id":19185,"arcs":[[3090,3091,3092,-3088,-2877]]},{"type":"Polygon","id":19007,"arcs":[[-3084,3093,3094,-3091,-2878]]},{"type":"Polygon","id":42109,"arcs":[[-2840,3095,3096,-2892]]},{"type":"Polygon","id":36061,"arcs":[[3097,-3048]]},{"type":"Polygon","id":49047,"arcs":[[-2982,3098,3099,3100,3101,3102,3103,-2990]]},{"type":"Polygon","id":42007,"arcs":[[-2854,3104,3105,3106,-3034,-2905]]},{"type":"Polygon","id":42087,"arcs":[[3107,-2811,-2893,-3097,3108]]},{"type":"Polygon","id":49013,"arcs":[[-2991,-3104,3109,3110,3111,-2807]]},{"type":"Polygon","id":39065,"arcs":[[-3013,3112,3113,3114,3115,-3043,-2860]]},{"type":"Polygon","id":34017,"arcs":[[3116,-3066,-2896]]},{"type":"Polygon","id":19111,"arcs":[[-2924,-2923,-2928,3117,3118,-3078,-2869]]},{"type":"Polygon","id":36081,"arcs":[[-3056,3119,3120,3121]]},{"type":"Polygon","id":34019,"arcs":[[3122,3123,3124,-2911,-2917]]},{"type":"Polygon","id":42077,"arcs":[[3125,3126,3127,-3030,-2903,-3028]]},{"type":"Polygon","id":31131,"arcs":[[-3071,3128,3129,3130,-2952,-2936]]},{"type":"Polygon","id":34035,"arcs":[[3131,3132,3133,-3123,-2916]]},{"type":"Polygon","id":17113,"arcs":[[-3009,3134,3135,3136,3137,3138,-3038,-2908]]},{"type":"Polygon","id":8095,"arcs":[[-2971,3139,3140,-2988,-2989]]},{"type":"Polygon","id":17179,"arcs":[[-3139,3141,3142,3143,-3025,-3039]]},{"type":"Polygon","id":42061,"arcs":[[-3108,3144,3145,3146,3147,3148,3149,-2812]]},{"type":"Polygon","id":42013,"arcs":[[-3149,3150,3151,-2803,-2813,-3150]]},{"type":"Polygon","id":36047,"arcs":[[3152,-3121]]},{"type":"Polygon","id":18007,"arcs":[[-2790,-3061,3153,3154,3155,-2958,-2824]]},{"type":"Polygon","id":34039,"arcs":[[3156,3157,-3132,-2915,-3068]]},{"type":"Polygon","id":18015,"arcs":[[-3063,3158,3159,3160,-3059]]},{"type":"Polygon","id":39019,"arcs":[[-3037,3161,3162,3163,-3021]]},{"type":"Polygon","id":39107,"arcs":[[3164,3165,3166,3167,-3040,-3018]]},{"type":"Polygon","id":42021,"arcs":[[-3152,3168,3169,3170,-3064,-2804]]},{"type":"Polygon","id":17057,"arcs":[[-3026,-3144,3171,3172,3173,-2929,-2890]]},{"type":"Polygon","id":39117,"arcs":[[-3011,3174,3175,3176,-3004]]},{"type":"Polygon","id":39101,"arcs":[[-3177,3177,3178,-3113,-3012,-3005]]},{"type":"Polygon","id":31063,"arcs":[[-2946,3179,3180,3181,3182,3183,-2725]]},{"type":"Polygon","id":31001,"arcs":[[-2848,3184,3185,3186,-2947,-2941]]},{"type":"Polygon","id":31073,"arcs":[[3187,3188,-3180,-2945]]},{"type":"Polygon","id":31085,"arcs":[[-3184,3189,3190,3191,-2969,-2726]]},{"type":"Polygon","id":31029,"arcs":[[-3192,3192,3193,-3140,-2970]]},{"type":"Polygon","id":31059,"arcs":[[3194,3195,3196,3197,-2939]]},{"type":"Polygon","id":31151,"arcs":[[-2951,-2954,3198,3199,-3195]]},{"type":"Polygon","id":31035,"arcs":[[-3198,3200,3201,-3185,-2847,-2940]]},{"type":"Polygon","id":42067,"arcs":[[-2839,3202,3203,-3145,-3109,-3096]]},{"type":"Polygon","id":49051,"arcs":[[-3112,3204,-3045,-2808]]},{"type":"Polygon","id":31099,"arcs":[[3205,3206,3207,-2948,-3187]]},{"type":"Polygon","id":39011,"arcs":[[-3116,3208,3209,-3165,-3017,-3044]]},{"type":"Polygon","id":31137,"arcs":[[-2949,-3208,3210,3211,-3188,-2944]]},{"type":"Polygon","id":42129,"arcs":[[-3065,-3171,3212,3213,3214,3215,-2857]]},{"type":"Polygon","id":42011,"arcs":[[3216,3217,3218,3219,-3031,-3128]]},{"type":"Polygon","id":42003,"arcs":[[-2858,-3216,3220,-3105,-2853]]},{"type":"Polygon","id":39075,"arcs":[[-3023,3221,3222,3223,-2932,-3015]]},{"type":"Polygon","id":39157,"arcs":[[-3164,3224,3225,3226,-3222,-3022]]},{"type":"Polygon","id":42043,"arcs":[[-3033,3227,3228,3229,3230,3231,-2838]]},{"type":"Polygon","id":18053,"arcs":[[-3052,3232,3233,3234,3235,3236,-3000,-2956,-2962]]},{"type":"Polygon","id":36085,"arcs":[[3237]]},{"type":"Polygon","id":17067,"arcs":[[3238,3239,3240,3241,3242,-3118,-2927]]},{"type":"Polygon","id":54029,"arcs":[[3243,3244,3245,-3035,-3107]]},{"type":"Polygon","id":17109,"arcs":[[-3174,3246,-3239,-2926,-2930]]},{"type":"Polygon","id":42099,"arcs":[[3247,3248,-3203,-3232]]},{"type":"Polygon","id":29045,"arcs":[[-3243,3249,3250,3251,-3079,-3119]]},{"type":"Polygon","id":42017,"arcs":[[3252,3253,3254,3255,-3126,-3027,-2912,-3125]]},{"type":"Polygon","id":34023,"arcs":[[3256,3257,3258,-3133,-3158]]},{"type":"Polygon","id":29199,"arcs":[[-3252,3259,3260,3261,-3082,-3080]]},{"type":"Polygon","id":39081,"arcs":[[-3246,3262,3263,3264,3265,-3162,-3036]]},{"type":"Polygon","id":29197,"arcs":[[3266,3267,-3094,-3083,-3262]]},{"type":"Polygon","id":29171,"arcs":[[-3268,3268,3269,3270,-3092,-3095]]},{"type":"Polygon","id":29005,"arcs":[[-3074,3271,3272,3273,-3129,-3070]]},{"type":"Polygon","id":29129,"arcs":[[-3271,3274,3275,3276,-3089,-3093]]},{"type":"Polygon","id":29147,"arcs":[[-3077,3277,3278,3279,3280,-3272,-3073]]},{"type":"Polygon","id":49049,"arcs":[[-3111,3281,3282,3283,-2919,-3046,-3205]]},{"type":"Polygon","id":29081,"arcs":[[-3277,3284,3285,3286,3287,-3086,-3090]]},{"type":"Polygon","id":39083,"arcs":[[3288,3289,3290,-3175,-3010,-2933,-3224]]},{"type":"Polygon","id":18075,"arcs":[[-3168,3291,3292,3293,3294,-3050,-3041]]},{"type":"Polygon","id":29227,"arcs":[[-3288,3295,-3278,-3076,-3087]]},{"type":"Polygon","id":18009,"arcs":[[-3295,3296,-3233,-3051]]},{"type":"Polygon","id":18067,"arcs":[[3297,3298,-3159,-3062,-3001,-3237]]},{"type":"Polygon","id":18157,"arcs":[[-3161,3299,3300,3301,3302,-3154,-3060]]},{"type":"Polygon","id":31127,"arcs":[[3303,3304,3305,3306,-3130,-3274]]},{"type":"Polygon","id":42075,"arcs":[[-3228,-3032,-3220,3307]]},{"type":"Polygon","id":39091,"arcs":[[-3115,3308,3309,3310,-3209]]},{"type":"Polygon","id":8087,"arcs":[[-2986,3311,3312,-2963]]},{"type":"Polygon","id":31067,"arcs":[[3313,3314,3315,3316,3317,-3199,-2953]]},{"type":"Polygon","id":31097,"arcs":[[-3307,3318,-3314,-3131]]},{"type":"Polygon","id":39159,"arcs":[[3319,3320,3321,-3309,-3114,-3179,3322]]},{"type":"Polygon","id":17183,"arcs":[[-3156,3323,3324,3325,3326,-3007,-2959]]},{"type":"Polygon","id":8049,"arcs":[[3327,3328,3329,3330,3331,-2973,-2979,-2999]]},{"type":"Polygon","id":39149,"arcs":[[-3311,3332,3333,3334,-3166,-3210]]},{"type":"Polygon","id":42125,"arcs":[[-3221,-3215,3335,3336,3337,3338,3339,-3244,-3106]]},{"type":"Polygon","id":18171,"arcs":[[-3303,3340,3341,-3324,-3155]]},{"type":"Polygon","id":34025,"arcs":[[3342,3343,3344,3345,-3258,3346]]},{"type":"Polygon","id":39031,"arcs":[[-3227,3347,3348,3349,-3289,-3223]]},{"type":"Polygon","id":42091,"arcs":[[3350,3351,3352,3353,-3217,-3127,-3256]]},{"type":"Polygon","id":6103,"arcs":[[3354,3355,3356,3357,-2749,-2835]]},{"type":"Polygon","id":6063,"arcs":[[-2832,3358,3359,3360,-3355,-2834]]},{"type":"Polygon","id":39041,"arcs":[[-3291,3361,3362,-3323,-3178,-3176]]},{"type":"Polygon","id":8125,"arcs":[[-3194,3363,3364,3365,3366,-2984,-3141]]},{"type":"Polygon","id":8121,"arcs":[[-3367,3367,3368,3369,3370,-3312,-2985]]},{"type":"Polygon","id":17125,"arcs":[[3371,3372,3373,3374,-3172,-3143]]},{"type":"Polygon","id":39067,"arcs":[[-3266,3375,3376,-3225,-3163]]},{"type":"Polygon","id":18023,"arcs":[[-3299,3377,3378,3379,3380,-3300,-3160]]},{"type":"Polygon","id":34021,"arcs":[[-3259,-3346,3381,-3253,-3124,-3134]]},{"type":"Polygon","id":18159,"arcs":[[-3236,3382,3383,-3378,-3298]]},{"type":"Polygon","id":54009,"arcs":[[-3340,3384,-3263,-3245]]},{"type":"Polygon","id":17019,"arcs":[[-3327,3385,3386,-3135,-3008]]},{"type":"Polygon","id":29211,"arcs":[[3387,3388,3389,-3275,-3270]]},{"type":"Polygon","id":29075,"arcs":[[-3287,3390,3391,3392,-3279,-3296]]},{"type":"Polygon","id":18095,"arcs":[[3393,3394,3395,3396,-3383,-3235]]},{"type":"Polygon","id":18035,"arcs":[[-3297,-3294,3397,3398,-3394,-3234]]},{"type":"Polygon","id":18045,"arcs":[[-3302,3399,3400,3401,-3341]]},{"type":"Polygon","id":39037,"arcs":[[-3335,3402,3403,3404,3405,3406,-3292,-3167]]},{"type":"Polygon","id":31181,"arcs":[[-3202,3407,3408,3409,3410,-3206,-3186]]},{"type":"Polygon","id":31061,"arcs":[[-3411,3411,3412,3413,-3207]]},{"type":"Polygon","id":31129,"arcs":[[-3197,3414,3415,3416,-3408,-3201]]},{"type":"Polygon","id":31057,"arcs":[[3417,3418,3419,-3364,-3193,-3191]]},{"type":"Polygon","id":31065,"arcs":[[-3212,3420,3421,3422,3423,-3181,-3189]]},{"type":"Polygon","id":31169,"arcs":[[3424,3425,-3415,-3196]]},{"type":"Polygon","id":31095,"arcs":[[-3200,-3318,3426,-3425]]},{"type":"Polygon","id":31145,"arcs":[[-3424,3427,3428,3429,-3182]]},{"type":"Polygon","id":31087,"arcs":[[-3183,-3430,3430,-3418,-3190]]},{"type":"Polygon","id":31083,"arcs":[[-3414,3431,3432,-3421,-3211]]},{"type":"Polygon","id":29001,"arcs":[[-3261,3433,3434,3435,-3388,-3269,-3267]]},{"type":"Polygon","id":42041,"arcs":[[3436,3437,3438,-3248,-3231]]},{"type":"Polygon","id":17107,"arcs":[[3439,3440,3441,3442,-3372,-3142,-3138]]},{"type":"Polygon","id":42009,"arcs":[[-3148,3443,3444,3445,-3169,-3151]]},{"type":"Polygon","id":42071,"arcs":[[3446,3447,3448,3449,-3229,-3308,-3219]]},{"type":"Polygon","id":18135,"arcs":[[-3407,3450,3451,-3398,-3293]]},{"type":"Polygon","id":29103,"arcs":[[-3251,3452,3453,3454,-3434,-3260]]},{"type":"Polygon","id":42055,"arcs":[[-3439,3455,3456,3457,3458,-3146,-3204,-3249]]},{"type":"Polygon","id":42111,"arcs":[[-3446,3459,3460,3461,-3213,-3170]]},{"type":"Polygon","id":17039,"arcs":[[3462,3463,-3440,-3137]]},{"type":"Polygon","id":17169,"arcs":[[-3173,-3375,3464,3465,3466,-3240,-3247]]},{"type":"Polygon","id":17147,"arcs":[[-3387,3467,3468,3469,-3463,-3136]]},{"type":"Polygon","id":39089,"arcs":[[-3350,3470,3471,3472,3473,-3362,-3290]]},{"type":"Polygon","id":39021,"arcs":[[-3322,3474,3475,3476,-3333,-3310]]},{"type":"Polygon","id":29079,"arcs":[[-3390,3477,3478,3479,-3285,-3276]]},{"type":"Polygon","id":8013,"arcs":[[-2966,3480,3481,3482,-3328,-2998]]},{"type":"Polygon","id":31133,"arcs":[[-3306,3483,3484,3485,-3315,-3319]]},{"type":"Polygon","id":31147,"arcs":[[3486,3487,3488,3489,-3484,-3305]]},{"type":"Polygon","id":29087,"arcs":[[-3281,3490,3491,-3487,-3304,-3273]]},{"type":"Polygon","id":29111,"arcs":[[-3242,3492,3493,3494,-3453,-3250]]},{"type":"Polygon","id":42029,"arcs":[[3495,3496,3497,-3447,-3218,-3354]]},{"type":"Polygon","id":42133,"arcs":[[-3450,3498,3499,3500,3501,-3437,-3230]]},{"type":"Polygon","id":8103,"arcs":[[3502,-3099,-2981,-2976]]},{"type":"Polygon","id":39059,"arcs":[[3503,3504,-3348,-3226,-3377,3505]]},{"type":"Polygon","id":18057,"arcs":[[3506,3507,3508,-3379,-3384,-3397]]},{"type":"Polygon","id":18107,"arcs":[[-3381,3509,3510,3511,3512,-3400,-3301]]},{"type":"Polygon","id":17001,"arcs":[[-3467,3513,3514,3515,-3493,-3241]]},{"type":"Polygon","id":39109,"arcs":[[-3477,3516,3517,-3403,-3334]]},{"type":"Polygon","id":54069,"arcs":[[-3339,3518,3519,-3264,-3385]]},{"type":"Polygon","id":34005,"arcs":[[-3345,3520,3521,3522,3523,3524,3525,-3254,-3382]]},{"type":"Polygon","id":18011,"arcs":[[-3509,3526,3527,-3510,-3380]]},{"type":"Polygon","id":39013,"arcs":[[-3265,-3520,3528,3529,3530,-3506,-3376]]},{"type":"Polygon","id":34029,"arcs":[[-3343,3531,-3521,-3344]]},{"type":"Polygon","id":39119,"arcs":[[-3505,3532,3533,3534,-3471,-3349]]},{"type":"Polygon","id":42057,"arcs":[[-3459,3535,3536,-3444,-3147]]},{"type":"Polygon","id":17129,"arcs":[[-3443,3537,3538,-3373]]},{"type":"Polygon","id":6007,"arcs":[[3539,3540,3541,3542,-3356,-3361]]},{"type":"Polygon","id":18165,"arcs":[[-3402,3543,3544,3545,-3325,-3342]]},{"type":"Polygon","id":42051,"arcs":[[-3462,3546,3547,3548,3549,-3336,-3214]]},{"type":"Polygon","id":39049,"arcs":[[-3474,3550,3551,3552,-3320,-3363]]},{"type":"Polygon","id":42101,"arcs":[[-3526,3553,3554,-3352,-3351,-3255]]},{"type":"Polygon","id":29061,"arcs":[[-3286,-3480,3555,3556,3557,-3391]]},{"type":"Polygon","id":29003,"arcs":[[-3393,3558,3559,3560,-3491,-3280]]},{"type":"Polygon","id":32033,"arcs":[[-2921,3561,3562,3563,3564,-2992,-2363]]},{"type":"Polygon","id":17017,"arcs":[[-3374,-3539,3565,3566,3567,-3465]]},{"type":"Polygon","id":39097,"arcs":[[3568,3569,3570,3571,-3475,-3321,-3553]]},{"type":"Polygon","id":17009,"arcs":[[-3568,3572,3573,-3514,-3466]]},{"type":"Polygon","id":8045,"arcs":[[-2975,3574,3575,3576,3577,-3100,-3503]]},{"type":"Polygon","id":18065,"arcs":[[-3452,3578,3579,3580,3581,-3395,-3399]]},{"type":"Polygon","id":42001,"arcs":[[3582,3583,-3456,-3438,-3502]]},{"type":"Polygon","id":42045,"arcs":[[-3555,3584,3585,3586,3587,-3496,-3353]]},{"type":"Polygon","id":17115,"arcs":[[-3470,3588,3589,3590,3591,-3441,-3464]]},{"type":"Polygon","id":8014,"arcs":[[3592,3593,3594,-3481,-2965]]},{"type":"Polygon","id":29121,"arcs":[[-3455,3595,3596,3597,3598,3599,-3435]]},{"type":"Polygon","id":39023,"arcs":[[-3572,3600,3601,-3517,-3476]]},{"type":"Polygon","id":29115,"arcs":[[-3600,3602,3603,-3478,-3389,-3436]]},{"type":"Polygon","id":29063,"arcs":[[-3558,3604,3605,3606,-3559,-3392]]},{"type":"Polygon","id":54051,"arcs":[[3607,3608,-3529,-3519,-3338,3609]]},{"type":"Polygon","id":42059,"arcs":[[-3550,3610,3611,-3610,-3337]]},{"type":"Polygon","id":49023,"arcs":[[-3284,3612,3613,-3562,-2920]]},{"type":"Polygon","id":18177,"arcs":[[-3406,3614,3615,3616,-3579,-3451]]},{"type":"Polygon","id":20023,"arcs":[[3617,3618,3619,-3365,-3420]]},{"type":"Polygon","id":20153,"arcs":[[-3429,3620,3621,3622,-3618,-3419,-3431]]},{"type":"Polygon","id":6045,"arcs":[[-2750,-3358,3623,3624,3625,3626,-2685]]},{"type":"Polygon","id":20089,"arcs":[[3627,3628,3629,3630,3631,-3409,-3417]]},{"type":"Polygon","id":20183,"arcs":[[-3632,3632,3633,3634,-3412,-3410]]},{"type":"Polygon","id":20157,"arcs":[[3635,3636,-3628,-3416,-3426]]},{"type":"Polygon","id":20201,"arcs":[[-3317,3637,3638,3639,3640,-3636,-3427]]},{"type":"Polygon","id":20039,"arcs":[[-3423,3641,3642,3643,-3621,-3428]]},{"type":"Polygon","id":32001,"arcs":[[-2996,3644,3645,3646,-2388,-3029]]},{"type":"Polygon","id":20137,"arcs":[[3647,3648,3649,-3642,-3422,-3433]]},{"type":"Polygon","id":20147,"arcs":[[-3635,3650,3651,-3648,-3432,-3413]]},{"type":"Polygon","id":20117,"arcs":[[-3486,3652,3653,3654,-3638,-3316]]},{"type":"Polygon","id":8001,"arcs":[[-3371,3655,3656,3657,-3594,-3593,-2964,-3313]]},{"type":"Polygon","id":20013,"arcs":[[3658,3659,3660,3661,-3489]]},{"type":"Polygon","id":20131,"arcs":[[-3490,-3662,3662,3663,-3653,-3485]]},{"type":"Polygon","id":20043,"arcs":[[-3492,-3561,3664,3665,-3659,-3488]]},{"type":"Polygon","id":34007,"arcs":[[3666,3667,3668,-3524]]},{"type":"Polygon","id":17167,"arcs":[[-3592,3669,3670,3671,3672,-3566,-3538,-3442]]},{"type":"Polygon","id":29117,"arcs":[[3673,3674,3675,-3556,-3479,-3604]]},{"type":"Polygon","id":29205,"arcs":[[-3495,3676,3677,-3596,-3454]]},{"type":"Polygon","id":18121,"arcs":[[-3513,3678,3679,3680,-3544,-3401]]},{"type":"Polygon","id":39121,"arcs":[[-3531,3681,3682,3683,-3533,-3504]]},{"type":"Polygon","id":29127,"arcs":[[-3516,3684,3685,3686,-3677,-3494]]},{"type":"Polygon","id":18059,"arcs":[[-3582,3687,3688,3689,-3507,-3396]]},{"type":"Polygon","id":39045,"arcs":[[-3473,3690,3691,3692,-3551]]},{"type":"Polygon","id":8047,"arcs":[[3693,3694,-3329,-3483]]},{"type":"Polygon","id":39127,"arcs":[[3695,3696,3697,-3691,-3472,-3535]]},{"type":"Polygon","id":18097,"arcs":[[-3690,3698,3699,3700,3701,-3527,-3508]]},{"type":"Polygon","id":8037,"arcs":[[3702,3703,3704,-3575,-2974,-3332]]},{"type":"Polygon","id":8117,"arcs":[[3705,3706,3707,-3703,-3331]]},{"type":"Polygon","id":18063,"arcs":[[-3702,3708,3709,-3511,-3528]]},{"type":"Polygon","id":39113,"arcs":[[-3602,3710,3711,3712,3713,-3404,-3518]]},{"type":"Polygon","id":39135,"arcs":[[-3714,3714,3715,-3615,-3405]]},{"type":"Polygon","id":8059,"arcs":[[-3658,3716,3717,3718,3719,3720,3721,-3694,-3482,-3595]]},{"type":"Polygon","id":8031,"arcs":[[3722,-3717,-3657]]},{"type":"Polygon","id":34015,"arcs":[[3723,3724,3725,3726,-3668]]},{"type":"Polygon","id":17045,"arcs":[[3727,3728,3729,3730,-3326,-3546]]},{"type":"Polygon","id":17041,"arcs":[[-3731,3731,3732,-3468,-3386]]},{"type":"Polygon","id":17137,"arcs":[[-3673,3733,3734,3735,3736,-3573,-3567]]},{"type":"Polygon","id":39111,"arcs":[[-3609,3737,3738,3739,-3682,-3530]]},{"type":"Polygon","id":18133,"arcs":[[-3710,3740,3741,3742,-3679,-3512]]},{"type":"Polygon","id":8019,"arcs":[[-3722,3743,-3706,-3330,-3695]]},{"type":"Polygon","id":39057,"arcs":[[-3571,3744,3745,3746,-3711,-3601]]},{"type":"Polygon","id":17149,"arcs":[[-3737,3747,3748,3749,3750,3751,-3685,-3515,-3574]]},{"type":"Polygon","id":10003,"arcs":[[3752,3753,3754,3755,-3497,-3588,3756,-3586]]},{"type":"Polygon","id":17021,"arcs":[[-3591,3757,3758,-3670]]},{"type":"Polygon","id":29021,"arcs":[[-3607,3759,3760,3761,-3665,-3560]]},{"type":"Polygon","id":49007,"arcs":[[-3110,-3103,3762,3763,-3282]]},{"type":"Polygon","id":49039,"arcs":[[-3764,3764,3765,3766,-3613,-3283]]},{"type":"Polygon","id":39129,"arcs":[[-3693,3767,3768,3769,-3569,-3552]]},{"type":"Polygon","id":6021,"arcs":[[-3543,3770,3771,-3624,-3357]]},{"type":"Polygon","id":17139,"arcs":[[-3733,3772,3773,-3589,-3469]]},{"type":"Polygon","id":18041,"arcs":[[3774,3775,3776,-3580,-3617]]},{"type":"Polygon","id":18139,"arcs":[[-3777,3777,3778,3779,-3688,-3581]]},{"type":"Polygon","id":17171,"arcs":[[3780,-3748,-3736]]},{"type":"Polygon","id":29025,"arcs":[[-3676,3781,3782,3783,-3605,-3557]]},{"type":"Polygon","id":34033,"arcs":[[3784,3785,3786,3787,3788,3789,-3726]]},{"type":"Polygon","id":6091,"arcs":[[-2831,-2394,3790,3791,-3359]]},{"type":"Polygon","id":39115,"arcs":[[-3684,3792,3793,-3696,-3534]]},{"type":"Polygon","id":29049,"arcs":[[-3784,3794,3795,3796,-3760,-3606]]},{"type":"Polygon","id":8005,"arcs":[[-3370,3797,3798,3799,-3718,-3723,-3656]]},{"type":"Polygon","id":32019,"arcs":[[3800,3801,3802,3803,3804,-2389,-3647]]},{"type":"Polygon","id":34001,"arcs":[[3805,3806,3807,-3724,-3667,-3523]]},{"type":"Polygon","id":18161,"arcs":[[-3716,3808,3809,-3775,-3616]]},{"type":"Polygon","id":24043,"arcs":[[-3458,3810,3811,3812,3813,3814,3815,-3536]]},{"type":"Polygon","id":24001,"arcs":[[-3537,-3816,3816,3817,3818,3819,-3460,-3445]]},{"type":"Polygon","id":24023,"arcs":[[-3820,3820,3821,3822,-3547,-3461]]},{"type":"Polygon","id":24015,"arcs":[[-3756,3823,3824,3825,-3448,-3498]]},{"type":"Polygon","id":54061,"arcs":[[-3549,3826,3827,3828,3829,-3611]]},{"type":"Polygon","id":54077,"arcs":[[-3823,3830,3831,3832,3833,-3827,-3548]]},{"type":"Polygon","id":24025,"arcs":[[-3449,-3826,3834,3835,-3499]]},{"type":"Polygon","id":54103,"arcs":[[-3612,-3830,3836,3837,3838,3839,-3738,-3608]]},{"type":"Polygon","id":24005,"arcs":[[-3836,3840,3841,3842,3843,3844,-3500]]},{"type":"Polygon","id":24013,"arcs":[[-3845,3845,3846,-3583,-3501]]},{"type":"Polygon","id":24021,"arcs":[[-3847,3847,3848,3849,-3811,-3457,-3584]]},{"type":"Polygon","id":39047,"arcs":[[-3770,3850,3851,3852,-3745,-3570]]},{"type":"Polygon","id":49015,"arcs":[[-3102,3853,3854,3855,-3765,-3763]]},{"type":"Polygon","id":29041,"arcs":[[-3599,3856,3857,3858,3859,-3674,-3603]]},{"type":"Polygon","id":18145,"arcs":[[-3780,3860,3861,3862,-3699,-3689]]},{"type":"Polygon","id":54065,"arcs":[[3863,3864,3865,-3817,-3815]]},{"type":"Polygon","id":29173,"arcs":[[-3752,3866,3867,3868,-3686]]},{"type":"Polygon","id":17029,"arcs":[[-3730,3869,3870,3871,-3773,-3732]]},{"type":"Polygon","id":29137,"arcs":[[-3687,-3869,3872,3873,-3597,-3678]]},{"type":"Polygon","id":39073,"arcs":[[3874,3875,3876,-3768,-3692,-3698]]},{"type":"Polygon","id":20029,"arcs":[[-3641,3877,3878,3879,-3629,-3637]]},{"type":"Polygon","id":20085,"arcs":[[3880,3881,3882,3883,-3663,-3661]]},{"type":"Polygon","id":20005,"arcs":[[-3762,3884,3885,3886,-3881,-3660,-3666]]},{"type":"Polygon","id":17173,"arcs":[[-3774,-3872,3887,3888,3889,3890,-3758,-3590]]},{"type":"Polygon","id":54057,"arcs":[[3891,3892,-3821,-3819]]},{"type":"Polygon","id":39167,"arcs":[[-3740,3893,3894,3895,3896,-3793,-3683]]},{"type":"Polygon","id":54049,"arcs":[[-3829,3897,3898,-3837]]},{"type":"Polygon","id":18081,"arcs":[[-3863,3899,3900,3901,-3700]]},{"type":"Polygon","id":6115,"arcs":[[-3792,3902,3903,3904,-3540,-3360]]},{"type":"Polygon","id":18109,"arcs":[[-3902,3905,3906,3907,-3741,-3709,-3701]]},{"type":"Polygon","id":32029,"arcs":[[-2390,-3805]]},{"type":"Polygon","id":54003,"arcs":[[-3814,3908,3909,-3864]]},{"type":"Polygon","id":29033,"arcs":[[-3860,3910,3911,3912,-3782,-3675]]},{"type":"Polygon","id":29175,"arcs":[[-3874,3913,3914,3915,-3857,-3598]]},{"type":"Polygon","id":18167,"arcs":[[3916,3917,3918,-3728,-3545,-3681]]},{"type":"Polygon","id":18021,"arcs":[[-3743,3919,3920,3921,-3917,-3680]]},{"type":"Polygon","id":54095,"arcs":[[3922,3923,3924,-3894,-3739,-3840]]},{"type":"Polygon","id":29163,"arcs":[[3925,3926,3927,-3867,-3751,3928]]},{"type":"Polygon","id":39017,"arcs":[[3929,3930,3931,-3809,-3715,-3713]]},{"type":"Polygon","id":39165,"arcs":[[-3747,3932,3933,3934,-3930,-3712]]},{"type":"Polygon","id":6033,"arcs":[[3935,3936,3937,3938,-3625,-3772]]},{"type":"Polygon","id":8063,"arcs":[[-3620,3939,3940,3941,3942,-3368,-3366]]},{"type":"Polygon","id":34011,"arcs":[[3943,3944,-3785,-3725,-3808,3945,3946]]},{"type":"Polygon","id":20163,"arcs":[[3947,3948,3949,3950,-3651,-3634]]},{"type":"Polygon","id":39027,"arcs":[[3951,3952,-3933,-3746,-3853,3953]]},{"type":"Polygon","id":20181,"arcs":[[-3623,3954,3955,3956,-3940,-3619]]},{"type":"Polygon","id":20193,"arcs":[[-3644,3957,3958,3959,-3955,-3622]]},{"type":"Polygon","id":20141,"arcs":[[-3631,3960,3961,3962,3963,-3948,-3633]]},{"type":"Polygon","id":20179,"arcs":[[-3650,3964,3965,-3958,-3643]]},{"type":"Polygon","id":20027,"arcs":[[3966,3967,3968,3969,-3878,-3640]]},{"type":"Polygon","id":20123,"arcs":[[-3880,3970,3971,-3961,-3630]]},{"type":"Polygon","id":20065,"arcs":[[-3951,3972,3973,-3965,-3649,-3652]]},{"type":"Polygon","id":20161,"arcs":[[3974,3975,3976,-3967,-3639,-3655]]},{"type":"Polygon","id":8035,"arcs":[[3977,3978,3979,-3719,-3800]]},{"type":"Polygon","id":8039,"arcs":[[3980,3981,-3978,-3799]]},{"type":"Polygon","id":20149,"arcs":[[-3664,-3884,3982,3983,-3975,-3654]]},{"type":"Polygon","id":8073,"arcs":[[-3943,3984,3985,3986,3987,-3981,-3798,-3369]]},{"type":"Polygon","id":8093,"arcs":[[-3721,3988,3989,3990,3991,-3707,-3744]]},{"type":"Polygon","id":39009,"arcs":[[-3897,3992,3993,3994,-3875,-3697,-3794]]},{"type":"Polygon","id":49027,"arcs":[[-3767,3995,3996,3997,-3563,-3614]]},{"type":"Polygon","id":54027,"arcs":[[-3866,3998,3999,-3892,-3818]]},{"type":"Polygon","id":29165,"arcs":[[-3761,-3797,4000,4001,4002,-3885]]},{"type":"Polygon","id":18047,"arcs":[[-3810,-3932,4003,4004,4005,-3778,-3776]]},{"type":"Polygon","id":29177,"arcs":[[-3913,4006,4007,4008,-3795,-3783]]},{"type":"Polygon","id":17135,"arcs":[[-3759,-3891,4009,4010,4011,4012,-3671]]},{"type":"Polygon","id":17117,"arcs":[[-4013,4013,4014,4015,-3734,-3672]]},{"type":"Polygon","id":6057,"arcs":[[-2393,4016,-3903,-3791]]},{"type":"Polygon","id":17061,"arcs":[[-3735,-4016,4017,4018,-3749,-3781]]},{"type":"Polygon","id":39141,"arcs":[[-3877,4019,4020,4021,4022,-3851,-3769]]},{"type":"Polygon","id":54037,"arcs":[[4023,4024,-3909,-3813]]},{"type":"Polygon","id":49019,"arcs":[[-3578,4025,4026,-3854,-3101]]},{"type":"Polygon","id":17023,"arcs":[[-3919,4027,4028,4029,4030,-3870,-3729]]},{"type":"Polygon","id":54073,"arcs":[[4031,4032,-3895,-3925]]},{"type":"Polygon","id":18119,"arcs":[[-3908,4033,4034,-3920,-3742]]},{"type":"Polygon","id":54033,"arcs":[[4035,4036,4037,4038,4039,-3838,-3899]]},{"type":"Polygon","id":51069,"arcs":[[4040,4041,4042,4043,-3999,-3865,-3910]]},{"type":"Polygon","id":29047,"arcs":[[-3796,-4009,4044,4045,-4001]]},{"type":"Polygon","id":18031,"arcs":[[-4006,4046,4047,4048,-3861,-3779]]},{"type":"Polygon","id":54091,"arcs":[[4049,-4036,-3898,-3828,-3834]]},{"type":"Polygon","id":54017,"arcs":[[-4040,4050,4051,4052,-3923,-3839]]},{"type":"Polygon","id":20087,"arcs":[[4053,4054,4055,-3882,-3887]]},{"type":"Polygon","id":20103,"arcs":[[-4003,4056,4057,4058,-4054,-3886]]},{"type":"Polygon","id":6011,"arcs":[[-3542,4059,4060,-3936,-3771]]},{"type":"Polygon","id":29195,"arcs":[[-3859,4061,4062,4063,4064,-3911]]},{"type":"Polygon","id":54107,"arcs":[[-4033,4065,4066,4067,4068,-3993,-3896]]},{"type":"Polygon","id":17013,"arcs":[[-4019,4069,4070,4071,-3929,-3750]]},{"type":"Polygon","id":39163,"arcs":[[-3995,4072,4073,4074,-4020,-3876]]},{"type":"Polygon","id":54085,"arcs":[[-4053,4075,4076,4077,-4066,-4032,-3924]]},{"type":"Polygon","id":24029,"arcs":[[-3755,4078,4079,4080,-3824]]},{"type":"Polygon","id":8065,"arcs":[[-3708,-3992,4081,4082,-3704]]},{"type":"Polygon","id":17035,"arcs":[[-4031,4083,4084,-3888,-3871]]},{"type":"Polygon","id":39071,"arcs":[[-4023,4085,4086,4087,-3954,-3852]]},{"type":"Polygon","id":24510,"arcs":[[4088,4089,-3842]]},{"type":"Polygon","id":24027,"arcs":[[-3844,4090,4091,4092,-3848,-3846]]},{"type":"Polygon","id":8077,"arcs":[[4093,4094,4095,4096,-4026,-3577]]},{"type":"Polygon","id":8097,"arcs":[[-4083,4097,4098,-4094,-3576,-3705]]},{"type":"Polygon","id":10001,"arcs":[[4099,4100,4101,-4079,-3754,4102]]},{"type":"Polygon","id":18105,"arcs":[[4103,4104,4105,4106,-4034,-3907]]},{"type":"Polygon","id":18005,"arcs":[[-4049,4107,4108,4109,-3900,-3862]]},{"type":"Polygon","id":29007,"arcs":[[-3868,-3928,4110,4111,4112,-3914,-3873]]},{"type":"Polygon","id":24031,"arcs":[[4113,4114,4115,4116,-3849,-4093]]},{"type":"Polygon","id":18013,"arcs":[[-4110,4117,-4104,-3906,-3901]]},{"type":"Polygon","id":29089,"arcs":[[4118,-4062,-3858,-3916,4119]]},{"type":"Polygon","id":54023,"arcs":[[4120,4121,4122,4123,-3831,-3822,-3893]]},{"type":"Polygon","id":34009,"arcs":[[4124,-3946,-3807]]},{"type":"Polygon","id":51107,"arcs":[[-4117,4125,4126,4127,4128,-4024,-3812,-3850]]},{"type":"Polygon","id":6061,"arcs":[[-2392,4129,4130,4131,4132,4133,-3904,-4017]]},{"type":"Polygon","id":39061,"arcs":[[-3935,4134,4135,4136,4137,4138,-3931]]},{"type":"Polygon","id":18137,"arcs":[[4139,4140,4141,4142,4143,-4047,-4005]]},{"type":"Polygon","id":18029,"arcs":[[-4004,-4139,4144,4145,-4140]]},{"type":"Polygon","id":20143,"arcs":[[-3970,4146,4147,4148,-3971,-3879]]},{"type":"Polygon","id":6101,"arcs":[[-3905,-4134,4149,4150,-4060,-3541]]},{"type":"Polygon","id":54001,"arcs":[[4151,4152,4153,-4037,-4050,-3833]]},{"type":"Polygon","id":29107,"arcs":[[4154,4155,4156,-4007,-3912,-4065]]},{"type":"Polygon","id":54093,"arcs":[[4157,-4152,-3832,-4124]]},{"type":"Polygon","id":39025,"arcs":[[4158,4159,4160,-4135,-3934,-3953,4161]]},{"type":"Polygon","id":51043,"arcs":[[-4129,4162,4163,-4041,-4025]]},{"type":"Polygon","id":17083,"arcs":[[-4015,4164,4165,-4070,-4018]]},{"type":"MultiPolygon","id":24035,"arcs":[[[4166]],[[-4102,4167,4168,4169,-4080]]]},{"type":"Polygon","id":18153,"arcs":[[-3922,4170,4171,4172,-4028,-3918]]},{"type":"Polygon","id":8051,"arcs":[[-4099,4173,4174,4175,4176,4177,4178,-4095]]},{"type":"Polygon","id":39015,"arcs":[[4179,4180,4181,-4162,-3952,-4088]]},{"type":"Polygon","id":29019,"arcs":[[-4113,4182,4183,4184,4185,-4120,-3915]]},{"type":"Polygon","id":32510,"arcs":[[4186,-4130,-2391,-3804]]},{"type":"Polygon","id":54031,"arcs":[[-4044,4187,4188,4189,-4121,-4000]]},{"type":"Polygon","id":24003,"arcs":[[4190,4191,-4091,-3843,-4090,4192,4193,4194]]},{"type":"Polygon","id":29095,"arcs":[[-4008,-4157,4195,4196,4197,4198,-4045]]},{"type":"Polygon","id":29113,"arcs":[[-4072,4199,4200,4201,-3926]]},{"type":"Polygon","id":20061,"arcs":[[4202,4203,4204,-3968,-3977]]},{"type":"Polygon","id":8029,"arcs":[[-4179,4205,-4096]]},{"type":"Polygon","id":20105,"arcs":[[-4149,4206,4207,4208,-3962,-3972]]},{"type":"Polygon","id":20177,"arcs":[[-4056,4209,4210,4211,-3983,-3883]]},{"type":"Polygon","id":17051,"arcs":[[-3890,4212,4213,4214,4215,4216,-4010]]},{"type":"Polygon","id":17049,"arcs":[[-4085,4217,4218,-4213,-3889]]},{"type":"Polygon","id":20197,"arcs":[[-4212,4219,4220,4221,-4203,-3976,-3984]]},{"type":"Polygon","id":39079,"arcs":[[4222,4223,4224,4225,-4021,-4075]]},{"type":"Polygon","id":39105,"arcs":[[4226,4227,-4073,-3994,-4069,4228]]},{"type":"Polygon","id":20209,"arcs":[[-4002,-4046,-4199,4229,-4057]]},{"type":"Polygon","id":39131,"arcs":[[4230,4231,-4086,-4022,-4226]]},{"type":"Polygon","id":18079,"arcs":[[4232,4233,4234,-4108,-4048,-4144]]},{"type":"Polygon","id":54105,"arcs":[[4235,4236,4237,-4067,-4078]]},{"type":"Polygon","id":17033,"arcs":[[-4173,4238,4239,4240,4241,-4029]]},{"type":"Polygon","id":17079,"arcs":[[-4242,4242,4243,-4218,-4084,-4030]]},{"type":"Polygon","id":18055,"arcs":[[-4035,-4107,4244,4245,4246,4247,-4171,-3921]]},{"type":"Polygon","id":54041,"arcs":[[4248,4249,4250,4251,-4051,-4039]]},{"type":"Polygon","id":32023,"arcs":[[-2993,-3565,4252,4253,4254,4255,4256,-3645,-2995]]},{"type":"Polygon","id":29139,"arcs":[[-3927,-4202,4257,4258,4259,4260,-4111]]},{"type":"Polygon","id":21015,"arcs":[[4261,4262,4263,4264,4265,-4145,-4138]]},{"type":"Polygon","id":24011,"arcs":[[4266,4267,4268,4269,-4168,-4101]]},{"type":"Polygon","id":20109,"arcs":[[4270,4271,4272,4273,-3956,-3960]]},{"type":"Polygon","id":20199,"arcs":[[-4274,4274,4275,4276,-3941,-3957]]},{"type":"Polygon","id":20051,"arcs":[[-3964,4277,4278,4279,4280,-3949]]},{"type":"Polygon","id":20063,"arcs":[[-3966,-3974,4281,4282,4283,4284,-4271,-3959]]},{"type":"Polygon","id":20041,"arcs":[[-4205,4285,4286,4287,-4147,-3969]]},{"type":"Polygon","id":20167,"arcs":[[-4209,4288,4289,4290,-4278,-3963]]},{"type":"Polygon","id":20195,"arcs":[[-3950,-4281,4291,-4282,-3973]]},{"type":"Polygon","id":24033,"arcs":[[-4192,4292,4293,4294,4295,4296,-4114,-4092]]},{"type":"Polygon","id":8119,"arcs":[[4297,4298,-3989,-3720,-3980]]},{"type":"Polygon","id":8041,"arcs":[[-3982,-3988,4299,4300,-4298,-3979]]},{"type":"Polygon","id":21037,"arcs":[[-4161,4301,4302,-4136]]},{"type":"Polygon","id":54083,"arcs":[[-4123,4303,4304,4305,4306,-4153,-4158]]},{"type":"Polygon","id":32005,"arcs":[[-3803,4307,4308,4309,-4131,-4187]]},{"type":"Polygon","id":54097,"arcs":[[-4154,-4307,4310,-4249,-4038]]},{"type":"Polygon","id":54021,"arcs":[[-4252,4311,4312,-4076,-4052]]},{"type":"Polygon","id":51171,"arcs":[[4313,4314,4315,-4188,-4043]]},{"type":"Polygon","id":21117,"arcs":[[-4303,4316,4317,-4262,-4137]]},{"type":"Polygon","id":54035,"arcs":[[-4238,4318,4319,4320,4321,-4229,-4068]]},{"type":"Polygon","id":32021,"arcs":[[-4257,4322,4323,-3801,-3646]]},{"type":"Polygon","id":18071,"arcs":[[-4235,4324,4325,4326,-4105,-4118,-4109]]},{"type":"Polygon","id":20045,"arcs":[[-4059,4327,4328,4329,-4210,-4055]]},{"type":"Polygon","id":6017,"arcs":[[-4310,4330,4331,4332,-4132]]},{"type":"Polygon","id":29027,"arcs":[[-4112,-4261,4333,4334,-4183]]},{"type":"Polygon","id":29053,"arcs":[[-4186,4335,4336,4337,-4063,-4119]]},{"type":"Polygon","id":51059,"arcs":[[4338,4339,4340,4341,4342,4343,-4126,-4116]]},{"type":"Polygon","id":8015,"arcs":[[-3991,4344,4345,-4174,-4098,-4082]]},{"type":"Polygon","id":20091,"arcs":[[-4198,4346,4347,-4328,-4058,-4230]]},{"type":"Polygon","id":39001,"arcs":[[4348,4349,4350,-4180,-4087,-4232]]},{"type":"Polygon","id":49041,"arcs":[[-3856,4351,4352,4353,-3996,-3766]]},{"type":"Polygon","id":8017,"arcs":[[-4277,4354,4355,-3985,-3942]]},{"type":"Polygon","id":54013,"arcs":[[-4313,4356,4357,4358,-4236,-4077]]},{"type":"Polygon","id":51187,"arcs":[[4359,4360,4361,-4314,-4042,-4164]]},{"type":"Polygon","id":39053,"arcs":[[4362,4363,4364,-4223,-4074,-4228]]},{"type":"Polygon","id":18115,"arcs":[[4365,-4141,-4146,-4266]]},{"type":"Polygon","id":54053,"arcs":[[-4322,4366,4367,-4363,-4227]]},{"type":"Polygon","id":17005,"arcs":[[-4217,4368,4369,-4011]]},{"type":"Polygon","id":39145,"arcs":[[-4225,4370,4371,4372,-4349,-4231]]},{"type":"Polygon","id":51061,"arcs":[[4373,4374,4375,4376,-4360,-4163,-4128]]},{"type":"Polygon","id":17119,"arcs":[[-4014,-4012,-4370,4377,4378,4379,4380,4381,-4165]]},{"type":"Polygon","id":29219,"arcs":[[4382,4383,4384,-4258,-4201]]},{"type":"Polygon","id":11001,"arcs":[[4385,4386,-4115,-4297]]},{"type":"Polygon","id":18093,"arcs":[[-4327,4387,4388,4389,-4245,-4106]]},{"type":"Polygon","id":29183,"arcs":[[-4382,4390,4391,-4383,-4200,-4071,-4166]]},{"type":"Polygon","id":10005,"arcs":[[4392,4393,4394,4395,4396,4397,-4267,-4100]]},{"type":"Polygon","id":20169,"arcs":[[-4288,4398,4399,-4207,-4148]]},{"type":"Polygon","id":54071,"arcs":[[-4190,4400,4401,4402,4403,-4304,-4122]]},{"type":"Polygon","id":29159,"arcs":[[-4338,4404,4405,4406,4407,-4155,-4064]]},{"type":"Polygon","id":24041,"arcs":[[-4270,4408,-4169]]},{"type":"Polygon","id":51153,"arcs":[[-4344,4409,4410,-4374,-4127]]},{"type":"Polygon","id":54087,"arcs":[[-4359,4411,4412,-4319,-4237]]},{"type":"Polygon","id":29101,"arcs":[[-4408,4413,4414,-4196,-4156]]},{"type":"Polygon","id":51013,"arcs":[[4415,-4341,4416,-4339,-4387,4417]]},{"type":"Polygon","id":6003,"arcs":[[4418,4419,4420,4421,-4331,-4309]]},{"type":"Polygon","id":18155,"arcs":[[-4265,4422,4423,4424,-4142,-4366]]},{"type":"Polygon","id":29135,"arcs":[[4425,4426,4427,-4336,-4185]]},{"type":"Polygon","id":6113,"arcs":[[-4151,4428,4429,4430,-4431,4431,-3937,-4061]]},{"type":"Polygon","id":18077,"arcs":[[-4425,4432,4433,4434,4435,-4233,-4143]]},{"type":"Polygon","id":17025,"arcs":[[-4244,4436,4437,4438,-4214,-4219]]},{"type":"Polygon","id":18083,"arcs":[[4439,4440,4441,4442,4443,4444,-4239,-4172,-4248]]},{"type":"Polygon","id":18101,"arcs":[[-4390,4445,4446,4447,-4246]]},{"type":"Polygon","id":18027,"arcs":[[4448,4449,-4440,-4247,-4448]]},{"type":"Polygon","id":54007,"arcs":[[-4251,4450,4451,4452,-4357,-4312]]},{"type":"Polygon","id":29189,"arcs":[[-4381,4453,4454,4455,4456,4457,-4391]]},{"type":"Polygon","id":21191,"arcs":[[4458,4459,4460,-4317,-4302,-4160,4461]]},{"type":"Polygon","id":20127,"arcs":[[-4222,4462,4463,4464,-4286,-4204]]},{"type":"Polygon","id":20053,"arcs":[[-4400,4465,4466,4467,-4289,-4208]]},{"type":"Polygon","id":20139,"arcs":[[-4330,4468,4469,4470,-4220,-4211]]},{"type":"Polygon","id":6055,"arcs":[[4430,4471,4472,4473,4474,4475,-3938,-4432]]},{"type":"Polygon","id":51157,"arcs":[[4476,4477,4478,-4361,-4377]]},{"type":"Polygon","id":21077,"arcs":[[4479,4480,4481,-4423,-4264]]},{"type":"Polygon","id":6097,"arcs":[[-3939,-4476,4482,4483,4484,-3626]]},{"type":"Polygon","id":17101,"arcs":[[4485,4486,-4240,-4445]]},{"type":"Polygon","id":51165,"arcs":[[-4316,4487,4488,4489,4490,-4401,-4189]]},{"type":"Polygon","id":17159,"arcs":[[-4241,-4487,4491,4492,4493,-4437,-4243]]},{"type":"Polygon","id":39087,"arcs":[[4494,4495,4496,4497,-4371,-4224,-4365]]},{"type":"Polygon","id":29037,"arcs":[[-4415,4498,4499,4500,-4347,-4197]]},{"type":"Polygon","id":51139,"arcs":[[-4479,4501,4502,-4488,-4315,-4362]]},{"type":"Polygon","id":18143,"arcs":[[-4436,4503,4504,-4325,-4234]]},{"type":"Polygon","id":21023,"arcs":[[-4182,4505,4506,4507,-4462,-4159]]},{"type":"Polygon","id":17121,"arcs":[[-4439,4508,4509,4510,4511,-4215]]},{"type":"Polygon","id":21081,"arcs":[[-4318,-4461,4512,4513,4514,-4480,-4263]]},{"type":"Polygon","id":18175,"arcs":[[-4505,4515,4516,4517,4518,4519,-4388,-4326]]},{"type":"Polygon","id":29510,"arcs":[[-4454,-4380,4520]]},{"type":"Polygon","id":24009,"arcs":[[4521,-4293,-4191]]},{"type":"Polygon","id":21161,"arcs":[[-4351,4522,4523,4524,-4506,-4181]]},{"type":"Polygon","id":21041,"arcs":[[4525,4526,4527,-4433,-4424,-4482]]},{"type":"Polygon","id":21089,"arcs":[[-4498,4528,4529,4530,-4372]]},{"type":"Polygon","id":17027,"arcs":[[-4216,-4512,4531,4532,4533,-4378,-4369]]},{"type":"Polygon","id":54075,"arcs":[[-4404,4534,4535,4536,4537,-4305]]},{"type":"Polygon","id":29051,"arcs":[[-4335,4538,4539,-4426,-4184]]},{"type":"Polygon","id":20111,"arcs":[[-4471,4540,4541,4542,-4463,-4221]]},{"type":"Polygon","id":20059,"arcs":[[4543,4544,4545,-4469,-4329]]},{"type":"Polygon","id":20121,"arcs":[[-4501,4546,4547,-4544,-4348]]},{"type":"Polygon","id":54101,"arcs":[[-4311,-4306,-4538,4548,4549,-4451,-4250]]},{"type":"Polygon","id":6067,"arcs":[[4550,4551,4552,4553,-4429,-4150,-4133,-4333]]},{"type":"Polygon","id":21223,"arcs":[[-4528,4554,4555,4556,-4434]]},{"type":"Polygon","id":21187,"arcs":[[-4515,4557,4558,4559,-4526,-4481]]},{"type":"Polygon","id":21135,"arcs":[[4560,4561,4562,-4523,-4350,-4373,-4531]]},{"type":"Polygon","id":29073,"arcs":[[-4385,4563,4564,4565,4566,4567,-4259]]},{"type":"Polygon","id":6051,"arcs":[[-3802,-4324,4568,4569,4570,4571,4572,-4419,-4308]]},{"type":"Polygon","id":29071,"arcs":[[-4392,-4458,4573,4574,4575,-4564,-4384]]},{"type":"Polygon","id":29151,"arcs":[[-4260,-4568,4576,4577,-4539,-4334]]},{"type":"Polygon","id":6005,"arcs":[[-4422,4578,4579,-4551,-4332]]},{"type":"Polygon","id":24019,"arcs":[[-4268,-4398,4580,4581]]},{"type":"Polygon","id":20171,"arcs":[[-4285,4582,4583,4584,4585,-4272]]},{"type":"Polygon","id":20101,"arcs":[[4586,4587,-4583,-4284]]},{"type":"Polygon","id":20071,"arcs":[[4588,4589,4590,4591,-4355,-4276]]},{"type":"Polygon","id":20203,"arcs":[[-4586,4592,4593,-4589,-4275,-4273]]},{"type":"Polygon","id":51047,"arcs":[[4594,4595,4596,4597,-4477,-4376]]},{"type":"Polygon","id":20135,"arcs":[[-4292,-4280,4598,4599,4600,4601,-4587,-4283]]},{"type":"Polygon","id":29141,"arcs":[[-4428,4602,4603,4604,-4405,-4337]]},{"type":"Polygon","id":20009,"arcs":[[-4468,4605,4606,4607,4608,-4290]]},{"type":"Polygon","id":8043,"arcs":[[-4301,4609,4610,4611,-4345,-3990,-4299]]},{"type":"Polygon","id":20165,"arcs":[[-4291,-4609,4612,-4599,-4279]]},{"type":"Polygon","id":24017,"arcs":[[-4295,4613,4614,4615]]},{"type":"Polygon","id":18117,"arcs":[[-4520,4616,4617,-4446,-4389]]},{"type":"Polygon","id":54079,"arcs":[[4618,4619,-4367,-4321,4620]]},{"type":"Polygon","id":32017,"arcs":[[-3998,4621,4622,4623,4624,4625,-4253,-3564]]},{"type":"Polygon","id":8085,"arcs":[[-4178,4626,4627,4628,-4097,-4206]]},{"type":"Polygon","id":54015,"arcs":[[4629,4630,-4412,-4358,-4453]]},{"type":"Polygon","id":17163,"arcs":[[-4534,4631,4632,4633,-4455,-4521,-4379]]},{"type":"Polygon","id":51113,"arcs":[[-4598,4634,4635,-4502,-4478]]},{"type":"Polygon","id":54039,"arcs":[[-4413,-4631,4636,4637,4638,4639,4640,-4621,-4320]]},{"type":"Polygon","id":8061,"arcs":[[-4592,4641,4642,4643,4644,-3986,-4356]]},{"type":"Polygon","id":20113,"arcs":[[4645,4646,4647,4648,-4466,-4399]]},{"type":"Polygon","id":20115,"arcs":[[-4465,4649,4650,4651,-4646,-4287]]},{"type":"Polygon","id":21201,"arcs":[[-4525,4652,4653,4654,-4507]]},{"type":"Polygon","id":17191,"arcs":[[-4494,4655,4656,4657,4658,-4509,-4438]]},{"type":"Polygon","id":18019,"arcs":[[-4557,4659,4660,4661,-4516,-4504,-4435]]},{"type":"Polygon","id":54011,"arcs":[[-4368,-4620,4662,4663,-4495,-4364]]},{"type":"Polygon","id":21103,"arcs":[[4664,4665,4666,-4555,-4527,-4560]]},{"type":"Polygon","id":51091,"arcs":[[4667,4668,-4535,-4403]]},{"type":"Polygon","id":51179,"arcs":[[4669,4670,4671,4672,4673,4674,-4595,-4375,-4411]]},{"type":"Polygon","id":21097,"arcs":[[-4655,4675,4676,4677,-4513,-4460,-4459,-4508]]},{"type":"Polygon","id":49001,"arcs":[[-4354,4678,4679,4680,-4622,-3997]]},{"type":"Polygon","id":17047,"arcs":[[4681,4682,-4656,-4493]]},{"type":"Polygon","id":17185,"arcs":[[-4492,-4486,-4444,4683,4684,-4682]]},{"type":"Polygon","id":29083,"arcs":[[-4407,4685,4686,4687,-4499,-4414]]},{"type":"Polygon","id":24045,"arcs":[[4688,4689,4690,-4581,-4397]]},{"type":"Polygon","id":54067,"arcs":[[-4550,4691,4692,-4637,-4630,-4452]]},{"type":"Polygon","id":18125,"arcs":[[4693,4694,4695,-4441,-4450]]},{"type":"MultiPolygon","id":6095,"arcs":[[[4696,-4474]],[[-4554,4697,-4472,-4431,-4430]]]},{"type":"Polygon","id":29015,"arcs":[[-4605,4698,4699,4700,-4686,-4406]]},{"type":"Polygon","id":18051,"arcs":[[4701,4702,4703,4704,-4684,-4443,-4442,-4696]]},{"type":"Polygon","id":18037,"arcs":[[-4618,4705,4706,4707,4708,-4694,-4449,-4447]]},{"type":"Polygon","id":21069,"arcs":[[4709,4710,4711,-4653,-4524,-4563]]},{"type":"Polygon","id":21185,"arcs":[[-4667,4712,4713,-4660,-4556]]},{"type":"Polygon","id":20017,"arcs":[[-4543,4714,4715,-4650,-4464]]},{"type":"Polygon","id":8101,"arcs":[[4716,4717,4718,4719,4720,-4610,-4300]]},{"type":"Polygon","id":8025,"arcs":[[-4645,4721,4722,-4717,-3987]]},{"type":"Polygon","id":20159,"arcs":[[-4649,4723,4724,-4606,-4467]]},{"type":"Polygon","id":17133,"arcs":[[4725,4726,4727,-4456,-4634]]},{"type":"Polygon","id":17189,"arcs":[[-4532,-4511,4728,4729,4730,-4632,-4533]]},{"type":"Polygon","id":24037,"arcs":[[-4615,4731]]},{"type":"Polygon","id":6009,"arcs":[[-4421,4732,4733,4734,-4579]]},{"type":"Polygon","id":49031,"arcs":[[4735,4736,-4679,-4353]]},{"type":"Polygon","id":49055,"arcs":[[-3855,4737,4738,-4736,-4352]]},{"type":"Polygon","id":29099,"arcs":[[-4728,4739,4740,4741,-4574,-4457]]},{"type":"Polygon","id":21019,"arcs":[[4742,4743,-4529,-4497,4744]]},{"type":"Polygon","id":21043,"arcs":[[-4744,4745,4746,4747,-4561,-4530]]},{"type":"Polygon","id":49037,"arcs":[[-4629,4748,4749,4750,4751,4752,4753,4754,4755,-4738,-4027]]},{"type":"Polygon","id":21209,"arcs":[[4756,4757,4758,4759,-4558,-4514,-4678]]},{"type":"Polygon","id":51079,"arcs":[[4760,4761,-4489,-4503,-4636]]},{"type":"Polygon","id":51015,"arcs":[[4762,4763,4764,4765,-4668,-4402,-4491]]},{"type":"Polygon","id":17081,"arcs":[[-4659,4766,4767,4768,-4729,-4510]]},{"type":"Polygon","id":29013,"arcs":[[-4688,4769,4770,4771,-4547,-4500]]},{"type":"Polygon","id":32009,"arcs":[[4772,-4569,-4323,-4256]]},{"type":"Polygon","id":21181,"arcs":[[-4712,4773,4774,-4676,-4654]]},{"type":"Polygon","id":8109,"arcs":[[-4612,4775,4776,4777,4778,4779,4780,-4175,-4346]]},{"type":"MultiPolygon","id":24047,"arcs":[[[4781,4782,4783,-4689,-4396,4784]]]},{"type":"Polygon","id":6109,"arcs":[[-4573,4785,4786,4787,-4733,-4420]]},{"type":"Polygon","id":20031,"arcs":[[-4546,4788,4789,4790,-4541,-4470]]},{"type":"Polygon","id":29131,"arcs":[[-4540,-4578,4791,4792,4793,-4603,-4427]]},{"type":"Polygon","id":18025,"arcs":[[4794,4795,4796,-4706,-4617,-4519]]},{"type":"Polygon","id":18061,"arcs":[[4797,4798,4799,4800,-4795,-4518]]},{"type":"Polygon","id":54099,"arcs":[[-4664,4801,4802,4803,4804,-4745,-4496]]},{"type":"Polygon","id":18043,"arcs":[[-4662,4805,-4798,-4517]]},{"type":"Polygon","id":51099,"arcs":[[4806,4807,4808,4809,-4671,4810]]},{"type":"Polygon","id":21205,"arcs":[[-4748,4811,4812,4813,4814,-4710,-4562]]},{"type":"Polygon","id":51137,"arcs":[[4815,4816,4817,-4761,-4635,-4597]]},{"type":"Polygon","id":20003,"arcs":[[4818,4819,-4789,-4545]]},{"type":"Polygon","id":20107,"arcs":[[-4772,4820,4821,-4819,-4548]]},{"type":"Polygon","id":21111,"arcs":[[4822,4823,4824,4825,-4799,-4806,-4661,-4714]]},{"type":"Polygon","id":51177,"arcs":[[-4675,4826,-4673,4827,4828,4829,-4816,-4596]]},{"type":"Polygon","id":54043,"arcs":[[-4619,-4641,4830,4831,4832,-4802,-4663]]},{"type":"Polygon","id":21073,"arcs":[[-4559,-4760,4833,4834,4835,-4665]]},{"type":"Polygon","id":21017,"arcs":[[4836,4837,4838,-4757,-4677,-4775]]},{"type":"Polygon","id":21211,"arcs":[[4839,4840,-4823,-4713,-4666,-4836]]},{"type":"Polygon","id":20145,"arcs":[[-4608,4841,4842,4843,-4600,-4613]]},{"type":"Polygon","id":8091,"arcs":[[-4177,4844,4845,4846,-4627]]},{"type":"Polygon","id":6041,"arcs":[[4847,-4484]]},{"type":"Polygon","id":21011,"arcs":[[-4815,4848,4849,-4774,-4711]]},{"type":"Polygon","id":6077,"arcs":[[-4580,-4735,4850,4851,4852,4853,-4552]]},{"type":"Polygon","id":29125,"arcs":[[-4567,4854,4855,-4792,-4577]]},{"type":"Polygon","id":24039,"arcs":[[-4690,-4784,4856,4857,4858]]},{"type":"Polygon","id":51003,"arcs":[[-4818,4859,4860,4861,4862,-4763,-4490,-4762]]},{"type":"Polygon","id":51193,"arcs":[[4863,4864,4865,-4807,4866]]},{"type":"Polygon","id":29029,"arcs":[[-4794,4867,4868,4869,4870,-4699,-4604]]},{"type":"Polygon","id":21127,"arcs":[[-4805,4871,4872,4873,4874,-4746,-4743]]},{"type":"Polygon","id":51017,"arcs":[[-4669,-4766,4875,4876,4877,-4536]]},{"type":"Polygon","id":8099,"arcs":[[-4591,4878,4879,4880,4881,-4642]]},{"type":"Polygon","id":54025,"arcs":[[-4878,4882,4883,4884,4885,-4692,-4549,-4537]]},{"type":"Polygon","id":21063,"arcs":[[-4875,4886,-4812,-4747]]},{"type":"Polygon","id":8027,"arcs":[[-4721,4887,-4776,-4611]]},{"type":"Polygon","id":8011,"arcs":[[-4882,4888,4889,4890,-4643]]},{"type":"Polygon","id":8089,"arcs":[[-4891,4891,-4718,-4723,-4722,-4644]]},{"type":"Polygon","id":18123,"arcs":[[4892,4893,4894,4895,-4707,-4797]]},{"type":"Polygon","id":54019,"arcs":[[-4693,-4886,4896,4897,-4638]]},{"type":"Polygon","id":20093,"arcs":[[4898,4899,4900,-4593,-4585]]},{"type":"Polygon","id":20055,"arcs":[[-4588,-4602,4901,4902,4903,-4899,-4584]]},{"type":"Polygon","id":20075,"arcs":[[-4901,4904,-4879,-4590,-4594]]},{"type":"Polygon","id":17193,"arcs":[[-4685,-4705,4905,4906,4907,-4657,-4683]]},{"type":"Polygon","id":20185,"arcs":[[-4725,4908,4909,4910,-4842,-4607]]},{"type":"Polygon","id":20083,"arcs":[[-4844,4911,4912,4913,-4902,-4601]]},{"type":"Polygon","id":17065,"arcs":[[-4908,4914,4915,4916,-4767,-4658]]},{"type":"Polygon","id":51033,"arcs":[[-4810,4917,4918,4919,4920,-4828,-4672]]},{"type":"Polygon","id":18173,"arcs":[[-4709,4921,4922,4923,4924,4925,-4702,-4695]]},{"type":"Polygon","id":18129,"arcs":[[4926,4927,4928,4929,-4906,-4704]]},{"type":"Polygon","id":54005,"arcs":[[4930,4931,4932,-4831,-4640]]},{"type":"Polygon","id":17157,"arcs":[[-4633,-4731,4933,4934,4935,4936,-4726]]},{"type":"Polygon","id":29185,"arcs":[[-4701,4937,4938,4939,4940,-4770,-4687]]},{"type":"Polygon","id":17145,"arcs":[[-4730,-4769,4941,4942,-4934]]},{"type":"Polygon","id":29055,"arcs":[[-4576,4943,4944,4945,4946,-4565]]},{"type":"Polygon","id":21067,"arcs":[[4947,4948,4949,4950,-4758,-4839]]},{"type":"Polygon","id":29221,"arcs":[[-4742,4951,4952,-4944,-4575]]},{"type":"Polygon","id":18147,"arcs":[[-4896,4953,4954,-4923,-4922,-4708]]},{"type":"Polygon","id":21163,"arcs":[[4955,4956,-4893,-4796,-4801]]},{"type":"Polygon","id":21239,"arcs":[[-4951,4957,4958,4959,-4834,-4759]]},{"type":"Polygon","id":21173,"arcs":[[4960,4961,4962,-4837,-4850]]},{"type":"Polygon","id":20079,"arcs":[[4963,4964,4965,-4647,-4652]]},{"type":"Polygon","id":20073,"arcs":[[-4542,-4791,4966,4967,4968,4969,-4715]]},{"type":"Polygon","id":20155,"arcs":[[-4648,-4966,4970,4971,4972,-4909,-4724]]},{"type":"Polygon","id":18163,"arcs":[[-4926,4973,4974,-4927,-4703]]},{"type":"Polygon","id":29161,"arcs":[[-4947,4975,4976,4977,-4855,-4566]]},{"type":"Polygon","id":49017,"arcs":[[-4756,4978,4979,-4680,-4737,-4739]]},{"type":"Polygon","id":51057,"arcs":[[4980,4981,-4918,-4809,4982]]},{"type":"Polygon","id":8113,"arcs":[[-4847,4983,4984,-4749,-4628]]},{"type":"Polygon","id":51109,"arcs":[[-4830,4985,4986,4987,-4860,-4817]]},{"type":"Polygon","id":49021,"arcs":[[-4980,4988,4989,-4623,-4681]]},{"type":"Polygon","id":21215,"arcs":[[4990,4991,4992,-4824,-4841]]},{"type":"Polygon","id":8053,"arcs":[[-4781,4993,4994,4995,4996,-4845,-4176]]},{"type":"Polygon","id":21005,"arcs":[[-4960,4997,4998,4999,-4991,-4840,-4835]]},{"type":"Polygon","id":17055,"arcs":[[-4917,5000,5001,5002,-4942,-4768]]},{"type":"Polygon","id":51159,"arcs":[[5003,5004,5005,-4865]]},{"type":"Polygon","id":29186,"arcs":[[-4937,5006,5007,-4740,-4727]]},{"type":"Polygon","id":21029,"arcs":[[5008,5009,-4825,-4993]]},{"type":"Polygon","id":21175,"arcs":[[-4874,5010,5011,5012,5013,-4813,-4887]]},{"type":"Polygon","id":21049,"arcs":[[5014,5015,5016,-4948,-4838,-4963]]},{"type":"Polygon","id":6013,"arcs":[[-4853,5017,5018]]},{"type":"Polygon","id":20015,"arcs":[[-4716,-4970,5019,5020,5021,-4964,-4651]]},{"type":"Polygon","id":20047,"arcs":[[-4911,5022,5023,5024,-4912,-4843]]},{"type":"Polygon","id":51163,"arcs":[[5025,5026,5027,5028,-4876,-4765,5029]]},{"type":"Polygon","id":29187,"arcs":[[-5008,5030,5031,5032,-4952,-4741]]},{"type":"Polygon","id":6099,"arcs":[[5033,5034,5035,-4851,-4734,-4788]]},{"type":"Polygon","id":29085,"arcs":[[-4871,5036,5037,-4938,-4700]]},{"type":"Polygon","id":29217,"arcs":[[-4941,5038,5039,5040,5041,-4821,-4771]]},{"type":"Polygon","id":21165,"arcs":[[-4814,-5014,5042,5043,-4961,-4849]]},{"type":"Polygon","id":51125,"arcs":[[5044,5045,5046,-5030,-4764,-4863]]},{"type":"Polygon","id":21027,"arcs":[[5047,5048,5049,5050,-4894,-4957]]},{"type":"Polygon","id":20207,"arcs":[[5051,5052,-4967,-4790]]},{"type":"Polygon","id":20001,"arcs":[[5053,5054,-5052,-4820]]},{"type":"Polygon","id":20011,"arcs":[[-5042,5055,5056,-5054,-4822]]},{"type":"MultiPolygon","id":51001,"arcs":[[[5057,5058,-4782,5059]]]},{"type":"Polygon","id":54045,"arcs":[[-4933,5060,5061,-4832]]},{"type":"Polygon","id":51133,"arcs":[[5062,-5004,-4864,5063]]},{"type":"Polygon","id":29169,"arcs":[[-4856,-4978,5064,5065,-4868,-4793]]},{"type":"Polygon","id":8055,"arcs":[[-4720,5066,5067,5068,-4777,-4888]]},{"type":"Polygon","id":51085,"arcs":[[-4921,5069,5070,5071,5072,-4986,-4829]]},{"type":"Polygon","id":51065,"arcs":[[5073,5074,-4861,-4988,5075]]},{"type":"Polygon","id":21093,"arcs":[[5076,5077,5078,5079,5080,-5048,-4956,-4800,-4826,-5010]]},{"type":"Polygon","id":21113,"arcs":[[5081,5082,5083,-4958,-4950]]},{"type":"Polygon","id":21115,"arcs":[[5084,5085,5086,-5011,-4873]]},{"type":"Polygon","id":20069,"arcs":[[-4914,5087,5088,5089,-4903]]},{"type":"Polygon","id":21091,"arcs":[[-5051,5090,5091,-4954,-4895]]},{"type":"Polygon","id":54081,"arcs":[[-4639,-4898,5092,5093,5094,-4931]]},{"type":"Polygon","id":21179,"arcs":[[-5000,5095,5096,5097,-5077,-5009,-4992]]},{"type":"Polygon","id":54059,"arcs":[[5098,5099,5100,5101,5102,-4803,-4833,-5062]]},{"type":"Polygon","id":21101,"arcs":[[-4974,-4925,5103,5104,5105,5106,-4928,-4975]]},{"type":"Polygon","id":51097,"arcs":[[-4982,5107,5108,5109,5110,-4919]]},{"type":"Polygon","id":8111,"arcs":[[-4997,5111,5112,-4984,-4846]]},{"type":"Polygon","id":21167,"arcs":[[-5084,5113,5114,5115,-4998,-4959]]},{"type":"Polygon","id":8079,"arcs":[[5116,5117,-4994,-4780]]},{"type":"Polygon","id":21159,"arcs":[[-4804,-5103,5118,5119,-5085,-4872]]},{"type":"Polygon","id":51005,"arcs":[[-5029,5120,5121,5122,-4883,-4877]]},{"type":"Polygon","id":17077,"arcs":[[-5003,5123,5124,5125,-4935,-4943]]},{"type":"Polygon","id":21197,"arcs":[[-5044,5126,5127,5128,-5015,-4962]]},{"type":"Polygon","id":21059,"arcs":[[5129,5130,-5104,-4924,-4955,-5092]]},{"type":"Polygon","id":21151,"arcs":[[5131,5132,5133,5134,-5082,-4949,-5017]]},{"type":"Polygon","id":17059,"arcs":[[-4930,5135,5136,5137,-4915,-4907]]},{"type":"Polygon","id":21229,"arcs":[[-5116,5138,5139,-5096,-4999]]},{"type":"Polygon","id":20057,"arcs":[[-5025,5140,5141,5142,-5088,-4913]]},{"type":"Polygon","id":20173,"arcs":[[-5022,5143,5144,-4971,-4965]]},{"type":"Polygon","id":17165,"arcs":[[-5138,5145,5146,5147,-5001,-4916]]},{"type":"Polygon","id":51101,"arcs":[[-5111,5148,5149,-5070,-4920]]},{"type":"Polygon","id":29059,"arcs":[[-4870,5150,5151,5152,5153,-5037]]},{"type":"Polygon","id":6001,"arcs":[[-4852,-5036,5154,5155,-5018]]},{"type":"Polygon","id":51075,"arcs":[[5156,5157,5158,-5076,-4987,-5073]]},{"type":"Polygon","id":29157,"arcs":[[-5126,5159,5160,5161,5162,-5031,-5007,-4936]]},{"type":"Polygon","id":29039,"arcs":[[5163,5164,5165,-5039,-4940]]},{"type":"Polygon","id":6043,"arcs":[[5166,5167,-4787]]},{"type":"Polygon","id":21225,"arcs":[[5168,5169,5170,-5136,-4929,-5107]]},{"type":"Polygon","id":29105,"arcs":[[-5066,5171,5172,5173,-5151,-4869]]},{"type":"Polygon","id":8033,"arcs":[[-5113,5174,-4750,-4985]]},{"type":"Polygon","id":21153,"arcs":[[-5087,5175,5176,5177,5178,-5012]]},{"type":"Polygon","id":54089,"arcs":[[5179,5180,-5093,-4897,-4885,5181]]},{"type":"Polygon","id":17199,"arcs":[[-5148,5182,5183,-5124,-5002]]},{"type":"Polygon","id":21237,"arcs":[[-5179,5184,5185,-5127,-5043,-5013]]},{"type":"Polygon","id":21065,"arcs":[[-5129,5186,5187,-5132,-5016]]},{"type":"Polygon","id":51103,"arcs":[[-5005,-5063,5188]]},{"type":"Polygon","id":8105,"arcs":[[-4779,5189,5190,5191,-5117]]},{"type":"Polygon","id":6075,"arcs":[[5192,5193]]},{"type":"Polygon","id":29167,"arcs":[[-5038,-5154,5194,5195,-5164,-4939]]},{"type":"Polygon","id":21079,"arcs":[[-5135,5196,5197,5198,-5114,-5083]]},{"type":"Polygon","id":20151,"arcs":[[-4973,5199,5200,5201,-5023,-4910]]},{"type":"Polygon","id":51009,"arcs":[[5202,5203,5204,5205,-5026,-5047]]},{"type":"Polygon","id":8071,"arcs":[[-4890,5206,5207,5208,5209,-5067,-4719,-4892]]},{"type":"Polygon","id":51023,"arcs":[[5210,5211,5212,-5121,-5028]]},{"type":"Polygon","id":29065,"arcs":[[-4946,5213,5214,5215,5216,-4976]]},{"type":"Polygon","id":54109,"arcs":[[5217,5218,-5099,-5061,-4932,-5095]]},{"type":"Polygon","id":51029,"arcs":[[5219,5220,5221,-5045,-4862,-5075]]},{"type":"Polygon","id":6039,"arcs":[[-4572,5222,5223,-5167,-4786]]},{"type":"Polygon","id":51119,"arcs":[[5224,-5108,-4981,5225]]},{"type":"Polygon","id":21071,"arcs":[[-5120,5226,5227,-5176,-5086]]},{"type":"Polygon","id":8003,"arcs":[[-5069,5228,5229,-5190,-4778]]},{"type":"Polygon","id":51049,"arcs":[[-5159,5230,5231,5232,-5220,-5074]]},{"type":"Polygon","id":21195,"arcs":[[5233,5234,5235,5236,5237,-5227,-5119,-5102]]},{"type":"Polygon","id":29093,"arcs":[[-5033,5238,5239,5240,-5214,-4945,-4953]]},{"type":"Polygon","id":20187,"arcs":[[5241,5242,5243,5244,-4880,-4905]]},{"type":"Polygon","id":21183,"arcs":[[-5050,5245,5246,5247,5248,-5130,-5091]]},{"type":"Polygon","id":20067,"arcs":[[5249,5250,-5242,-4900]]},{"type":"Polygon","id":20081,"arcs":[[-5090,5251,5252,5253,-5250,-4904]]},{"type":"Polygon","id":20097,"arcs":[[-5202,5254,5255,5256,-5141,-5024]]},{"type":"Polygon","id":20095,"arcs":[[-5145,5257,5258,5259,-5200,-4972]]},{"type":"Polygon","id":20205,"arcs":[[5260,5261,5262,-4968,-5053]]},{"type":"Polygon","id":20133,"arcs":[[-5057,5263,5264,5265,-5261,-5055]]},{"type":"Polygon","id":21123,"arcs":[[5266,5267,5268,5269,-5078,-5098]]},{"type":"Polygon","id":21155,"arcs":[[-5140,5270,5271,5272,-5267,-5097]]},{"type":"Polygon","id":54063,"arcs":[[-5123,5273,5274,-5182,-4884]]},{"type":"Polygon","id":21129,"arcs":[[-5186,5275,5276,5277,-5187,-5128]]},{"type":"Polygon","id":21021,"arcs":[[-5199,5278,5279,-5271,-5139,-5115]]},{"type":"Polygon","id":51087,"arcs":[[5280,5281,5282,5283,5284,5285,5286,5287,-5157,-5072]]},{"type":"Polygon","id":6081,"arcs":[[5288,5289,5290,5291,-5193]]},{"type":"Polygon","id":21025,"arcs":[[-5178,5292,5293,5294,-5276,-5185]]},{"type":"Polygon","id":51145,"arcs":[[5295,5296,-5231,-5158]]},{"type":"Polygon","id":21149,"arcs":[[-5249,5297,5298,5299,-5105,-5131]]},{"type":"Polygon","id":20037,"arcs":[[-5041,5300,5301,5302,5303,-5264,-5056]]},{"type":"Polygon","id":51045,"arcs":[[5304,5305,5306,-5274,-5122,-5213]]},{"type":"Polygon","id":8023,"arcs":[[-5210,5307,5308,5309,-5229,-5068]]},{"type":"Polygon","id":29011,"arcs":[[-5166,5310,5311,-5301,-5040]]},{"type":"Polygon","id":21233,"arcs":[[5312,5313,5314,-5169,-5106,-5300]]},{"type":"Polygon","id":29123,"arcs":[[-5163,5315,5316,-5239,-5032]]},{"type":"Polygon","id":8009,"arcs":[[-5245,5317,5318,5319,-5207,-4889,-4881]]},{"type":"Polygon","id":8083,"arcs":[[5320,5321,-4751,-5175]]},{"type":"Polygon","id":8067,"arcs":[[-4996,5322,5323,-5321,-5112]]},{"type":"Polygon","id":6047,"arcs":[[-5224,5324,5325,5326,-5034,-5168]]},{"type":"Polygon","id":21137,"arcs":[[5327,5328,5329,-5279,-5198]]},{"type":"Polygon","id":51127,"arcs":[[5330,5331,5332,-5281,-5071,-5150]]},{"type":"Polygon","id":21085,"arcs":[[-5081,-5080,5333,5334,5335,-5246,-5049]]},{"type":"Polygon","id":51019,"arcs":[[5336,5337,5338,5339,5340,-5211,-5027,-5206]]},{"type":"Polygon","id":49053,"arcs":[[5341,5342,-4624,-4990]]},{"type":"Polygon","id":20049,"arcs":[[-5263,5343,5344,5345,-5020,-4969]]},{"type":"Polygon","id":29179,"arcs":[[5346,5347,5348,-5215,-5241]]},{"type":"Polygon","id":29031,"arcs":[[5349,5350,5351,5352,5353,-5161]]},{"type":"Polygon","id":29215,"arcs":[[-4977,-5217,5354,5355,5356,5357,-5172,-5065]]},{"type":"Polygon","id":17069,"arcs":[[-5137,-5171,5358,5359,5360,-5146]]},{"type":"Polygon","id":17151,"arcs":[[-5361,5361,5362,5363,-5147]]},{"type":"Polygon","id":51760,"arcs":[[-5285,5364,-5287,-5286]]},{"type":"Polygon","id":17087,"arcs":[[-5364,5365,5366,5367,-5183]]},{"type":"Polygon","id":17181,"arcs":[[-5184,-5368,5368,5369,-5350,-5160,-5125]]},{"type":"Polygon","id":51073,"arcs":[[5370,5371,5372,-5109,-5225]]},{"type":"Polygon","id":29017,"arcs":[[-5354,5373,5374,-5316,-5162]]},{"type":"Polygon","id":54055,"arcs":[[-5181,5375,5376,5377,5378,-5218,-5094]]},{"type":"Polygon","id":6019,"arcs":[[5379,5380,5381,5382,5383,-5325,-5223,-4571]]},{"type":"Polygon","id":29057,"arcs":[[-5196,5384,5385,5386,-5311,-5165]]},{"type":"Polygon","id":21109,"arcs":[[-5278,5387,5388,5389,5390,-5133,-5188]]},{"type":"Polygon","id":21107,"arcs":[[5391,5392,5393,-5313,-5299]]},{"type":"Polygon","id":51041,"arcs":[[-5365,-5284,5394,5395,5396,5397,5398,5399,5400,5401,-5296,-5288]]},{"type":"Polygon","id":51011,"arcs":[[5402,5403,5404,-5203,-5046,-5222]]},{"type":"MultiPolygon","id":51131,"arcs":[[[5405,-5058]]]},{"type":"Polygon","id":21045,"arcs":[[-5330,5406,5407,5408,5409,-5272,-5280]]},{"type":"Polygon","id":54047,"arcs":[[-5379,5410,5411,-5100,-5219]]},{"type":"Polygon","id":21055,"arcs":[[-5315,5412,5413,5414,-5359,-5170]]},{"type":"Polygon","id":49025,"arcs":[[-4979,-4755,5415,5416,-5342,-4989]]},{"type":"Polygon","id":21189,"arcs":[[-5295,5417,5418,-5388,-5277]]},{"type":"Polygon","id":21203,"arcs":[[-5391,5419,5420,-5328,-5197,-5134]]},{"type":"Polygon","id":51027,"arcs":[[-5412,5421,5422,5423,-5234,-5101]]},{"type":"Polygon","id":51115,"arcs":[[-5372,5424]]},{"type":"Polygon","id":21119,"arcs":[[-5177,-5228,-5238,5425,5426,-5293]]},{"type":"Polygon","id":51007,"arcs":[[-5402,5427,5428,5429,-5232,-5297]]},{"type":"Polygon","id":51036,"arcs":[[5430,5431,-5282,-5333]]},{"type":"Polygon","id":29225,"arcs":[[-5174,5432,5433,5434,5435,-5152]]},{"type":"Polygon","id":6085,"arcs":[[-5035,-5327,5436,5437,-5290,5438,-5155]]},{"type":"Polygon","id":21217,"arcs":[[-5273,-5410,5439,5440,-5268]]},{"type":"Polygon","id":29229,"arcs":[[-5358,5441,-5433,-5173]]},{"type":"Polygon","id":51071,"arcs":[[-5307,5442,5443,5444,-5376,-5180,-5275]]},{"type":"Polygon","id":20035,"arcs":[[-5346,5445,5446,5447,5448,-5021]]},{"type":"Polygon","id":20191,"arcs":[[-5449,5449,5450,5451,-5258,-5144]]},{"type":"Polygon","id":20119,"arcs":[[-5143,5452,5453,5454,-5252,-5089]]},{"type":"Polygon","id":20025,"arcs":[[-5257,5455,5456,5457,-5453,-5142]]},{"type":"Polygon","id":20007,"arcs":[[-5260,5458,5459,5460,5461,-5255,-5201]]},{"type":"Polygon","id":21087,"arcs":[[-5441,5462,5463,5464,-5269]]},{"type":"Polygon","id":51680,"arcs":[[-5205,5465,-5337]]},{"type":"Polygon","id":6027,"arcs":[[-4773,-4255,5466,5467,5468,5469,-5380,-4570]]},{"type":"Polygon","id":51095,"arcs":[[5470,5471,5472,-5431,-5332,5473,5474,5475]]},{"type":"Polygon","id":21099,"arcs":[[-5270,-5465,5476,5477,5478,-5334,-5079]]},{"type":"Polygon","id":21193,"arcs":[[-5427,5479,5480,5481,5482,-5418,-5294]]},{"type":"Polygon","id":51031,"arcs":[[-5405,5483,5484,5485,-5338,-5466,-5204]]},{"type":"Polygon","id":29077,"arcs":[[-5153,-5436,5486,5487,-5385,-5195]]},{"type":"Polygon","id":21139,"arcs":[[-5415,5488,5489,5490,5491,-5362,-5360]]},{"type":"Polygon","id":29203,"arcs":[[-5349,5492,5493,5494,-5355,-5216]]},{"type":"Polygon","id":8007,"arcs":[[-5118,-5192,5495,5496,5497,-5323,-4995]]},{"type":"Polygon","id":51161,"arcs":[[-5341,5498,5499,5500,-5305,-5212],[5501,5502]]},{"type":"Polygon","id":21177,"arcs":[[5503,5504,5505,5506,-5392,-5298,-5248]]},{"type":"Polygon","id":51147,"arcs":[[-5233,-5430,5507,5508,5509,-5403,-5221]]},{"type":"Polygon","id":8021,"arcs":[[-5191,-5230,-5310,5510,5511,-5496]]},{"type":"Polygon","id":21031,"arcs":[[5512,5513,-5504,-5247,-5336,5514]]},{"type":"Polygon","id":20129,"arcs":[[5515,5516,5517,-5318,-5244]]},{"type":"Polygon","id":20189,"arcs":[[-5254,5518,5519,-5516,-5243,-5251]]},{"type":"Polygon","id":20175,"arcs":[[-5455,5520,5521,-5519,-5253]]},{"type":"Polygon","id":20125,"arcs":[[-5262,-5266,5522,5523,5524,5525,-5344]]},{"type":"Polygon","id":20077,"arcs":[[5526,5527,-5459,-5259,-5452]]},{"type":"Polygon","id":20033,"arcs":[[5528,5529,-5456,-5256,-5462]]},{"type":"Polygon","id":20099,"arcs":[[-5304,5530,5531,5532,-5523,-5265]]},{"type":"Polygon","id":21033,"arcs":[[-5394,5533,5534,5535,-5413,-5314]]},{"type":"Polygon","id":51199,"arcs":[[5536,5537,5538,5539,-5471,5540,-5475]]},{"type":"Polygon","id":29097,"arcs":[[-5387,5541,5542,5543,-5302,-5312]]},{"type":"Polygon","id":51121,"arcs":[[5544,5545,5546,5547,-5443,-5306,-5501]]},{"type":"Polygon","id":21051,"arcs":[[-5483,5548,5549,5550,5551,-5389,-5419]]},{"type":"Polygon","id":21199,"arcs":[[5552,5553,5554,-5407,-5329,-5421,5555]]},{"type":"Polygon","id":20021,"arcs":[[-5544,5556,5557,5558,-5531,-5303]]},{"type":"Polygon","id":21061,"arcs":[[-5479,5559,5560,-5515,-5335]]},{"type":"Polygon","id":51770,"arcs":[[5561,-5503]]},{"type":"Polygon","id":17127,"arcs":[[-5492,5562,5563,-5366,-5363]]},{"type":"Polygon","id":51185,"arcs":[[-5378,5564,5565,5566,-5422,-5411]]},{"type":"Polygon","id":17003,"arcs":[[5567,5568,5569,5570,-5351,-5370]]},{"type":"Polygon","id":21125,"arcs":[[-5552,5571,5572,5573,-5556,-5420,-5390]]},{"type":"Polygon","id":17153,"arcs":[[-5367,-5564,5574,5575,-5568,-5369]]},{"type":"Polygon","id":21131,"arcs":[[5576,5577,-5549,-5482]]},{"type":"Polygon","id":29223,"arcs":[[-5375,5578,5579,5580,-5347,-5240,-5317]]},{"type":"Polygon","id":51149,"arcs":[[5581,5582,5583,5584,5585,-5397,5586]]},{"type":"Polygon","id":21001,"arcs":[[-5409,5587,5588,5589,-5463,-5440]]},{"type":"Polygon","id":51051,"arcs":[[5590,5591,-5235,-5424]]},{"type":"Polygon","id":20019,"arcs":[[-5526,5592,5593,-5446,-5345]]},{"type":"Polygon","id":51021,"arcs":[[5594,5595,5596,-5565,-5377,-5445]]},{"type":"Polygon","id":51135,"arcs":[[5597,5598,-5508,-5429,5599]]},{"type":"Polygon","id":29109,"arcs":[[-5488,5600,5601,5602,5603,-5542,-5386]]},{"type":"Polygon","id":6087,"arcs":[[5604,5605,5606,-5291,-5438]]},{"type":"Polygon","id":51053,"arcs":[[5607,-5585,5608,5609,5610,-5600,-5428,-5401]]},{"type":"Polygon","id":21133,"arcs":[[-5237,5611,5612,-5480,-5426]]},{"type":"Polygon","id":29201,"arcs":[[5613,5614,5615,-5352,-5571]]},{"type":"Polygon","id":51037,"arcs":[[5616,5617,5618,-5484,-5404,-5510]]},{"type":"Polygon","id":51155,"arcs":[[5619,-5546,5620,5621,5622,-5595,-5444,-5548]]},{"type":"Polygon","id":51181,"arcs":[[5623,5624,5625,-5583,5626]]},{"type":"Polygon","id":51067,"arcs":[[5627,5628,5629,5630,-5499,-5340]]},{"type":"Polygon","id":21007,"arcs":[[5631,5632,-5569,-5576,5633]]},{"type":"Polygon","id":21145,"arcs":[[-5491,5634,5635,5636,-5634,-5575,-5563]]},{"type":"Polygon","id":51700,"arcs":[[5637,5638,-5472,-5540]]},{"type":"Polygon","id":51195,"arcs":[[5639,5640,5641,5642,-5612,-5236,-5592]]},{"type":"Polygon","id":21227,"arcs":[[-5561,5643,5644,5645,5646,-5513]]},{"type":"Polygon","id":21207,"arcs":[[-5555,5647,5648,5649,-5588,-5408]]},{"type":"Polygon","id":21169,"arcs":[[-5590,5650,5651,5652,-5477,-5464]]},{"type":"Polygon","id":21143,"arcs":[[-5536,5653,5654,-5489,-5414]]},{"type":"Polygon","id":21009,"arcs":[[5655,5656,-5644,-5560,-5478,-5653]]},{"type":"Polygon","id":21047,"arcs":[[-5507,5657,5658,5659,5660,-5534,-5393]]},{"type":"Polygon","id":51093,"arcs":[[5661,5662,5663,5664,-5624,5665]]},{"type":"Polygon","id":51167,"arcs":[[5666,5667,5668,-5640,-5591,-5423,-5567]]},{"type":"Polygon","id":51143,"arcs":[[5669,5670,5671,5672,5673,5674,-5628,-5339,-5486]]},{"type":"Polygon","id":51063,"arcs":[[-5631,5675,5676,-5621,-5545,-5500]]},{"type":"Polygon","id":29207,"arcs":[[-5353,-5616,5677,5678,5679,-5579,-5374]]},{"type":"Polygon","id":51111,"arcs":[[-5599,5680,5681,-5617,-5509]]},{"type":"Polygon","id":51650,"arcs":[[-5638,-5539,-5683,5683]]},{"type":"Polygon","id":51183,"arcs":[[5684,5685,-5609,-5584,-5626]]},{"type":"Polygon","id":29035,"arcs":[[-5581,5686,5687,5688,-5493,-5348]]},{"type":"Polygon","id":29043,"arcs":[[-5435,5689,5690,5691,-5601,-5487]]},{"type":"Polygon","id":51197,"arcs":[[5692,5693,5694,-5596,-5623]]},{"type":"Polygon","id":21141,"arcs":[[5695,5696,5697,-5505,-5514,-5647]]},{"type":"Polygon","id":21219,"arcs":[[-5698,5698,5699,-5658,-5506]]},{"type":"Polygon","id":29067,"arcs":[[-5442,-5357,5700,5701,5702,-5690,-5434]]},{"type":"Polygon","id":21157,"arcs":[[-5655,5703,5704,5705,-5635,-5490]]},{"type":"Polygon","id":29133,"arcs":[[-5633,5706,5707,5708,5709,-5614,-5570]]},{"type":"Polygon","id":51083,"arcs":[[-5619,5710,5711,5712,5713,-5670,-5485]]},{"type":"Polygon","id":29091,"arcs":[[-5495,5714,5715,5716,-5701,-5356]]},{"type":"Polygon","id":29145,"arcs":[[-5604,5717,5718,5719,-5557,-5543]]},{"type":"Polygon","id":21121,"arcs":[[5720,5721,-5572,-5551]]},{"type":"Polygon","id":51025,"arcs":[[-5611,5722,5723,5724,5725,-5681,-5598]]},{"type":"Polygon","id":21095,"arcs":[[-5613,-5643,5726,5727,-5577,-5481]]},{"type":"Polygon","id":51173,"arcs":[[-5597,-5695,5728,5729,-5667,-5566]]},{"type":"Polygon","id":4017,"arcs":[[5730,5731,5732,5733,-4753]]},{"type":"Polygon","id":4005,"arcs":[[-5734,5734,5735,5736,-5416,-4754]]},{"type":"Polygon","id":21221,"arcs":[[5737,5738,-5704,-5654,-5535,-5661]]},{"type":"Polygon","id":40105,"arcs":[[-5533,5739,5740,5741,-5524]]},{"type":"Polygon","id":40113,"arcs":[[5742,5743,5744,5745,5746,-5447,-5594]]},{"type":"Polygon","id":40151,"arcs":[[-5461,5747,5748,5749,5750,5751,-5529]]},{"type":"Polygon","id":40035,"arcs":[[-5559,5752,5753,5754,5755,-5740,-5532]]},{"type":"Polygon","id":40147,"arcs":[[-5742,5756,5757,-5743,-5593,-5525]]},{"type":"Polygon","id":40053,"arcs":[[5758,5759,5760,-5527,-5451]]},{"type":"Polygon","id":40003,"arcs":[[-5528,-5761,5761,5762,-5748,-5460]]},{"type":"Polygon","id":40071,"arcs":[[-5747,5763,5764,-5759,-5450,-5448]]},{"type":"Polygon","id":40115,"arcs":[[-5720,5765,5766,-5753,-5558]]},{"type":"Polygon","id":40059,"arcs":[[-5530,-5752,-5751,5767,5768,5769,-5457]]},{"type":"Polygon","id":35039,"arcs":[[-5512,5770,5771,5772,5773,5774,5775,-5497]]},{"type":"Polygon","id":35045,"arcs":[[-5776,5776,5777,5778,-5322,-5324,-5498]]},{"type":"Polygon","id":4001,"arcs":[[-5779,5779,5780,5781,5782,5783,-5731,-4752]]},{"type":"Polygon","id":35059,"arcs":[[5784,5785,5786,5787,5788,5789,-5208,-5320]]},{"type":"Polygon","id":40025,"arcs":[[-5518,5790,5791,5792,-5785,-5319]]},{"type":"Polygon","id":40139,"arcs":[[5793,5794,5795,5796,-5791,-5517,-5520,-5522]]},{"type":"Polygon","id":40007,"arcs":[[-5458,-5770,5797,5798,5799,-5794,-5521,-5454]]},{"type":"Polygon","id":51175,"arcs":[[5800,-5663,5801,5802,5803,5804,5805,-5685,-5625,-5665]]},{"type":"Polygon","id":21231,"arcs":[[5806,5807,5808,5809,-5648,-5554]]},{"type":"Polygon","id":35055,"arcs":[[5810,5811,-5771,-5511,-5309]]},{"type":"Polygon","id":35007,"arcs":[[-5790,5812,5813,-5811,-5308,-5209]]},{"type":"Polygon","id":29209,"arcs":[[5814,5815,5816,-5602,-5692]]},{"type":"Polygon","id":6069,"arcs":[[-5326,-5384,5817,-5605,-5437]]},{"type":"Polygon","id":21235,"arcs":[[-5722,5818,5819,5820,5821,-5573]]},{"type":"Polygon","id":51710,"arcs":[[5822,5823]]},{"type":"Polygon","id":21147,"arcs":[[-5822,5824,5825,-5807,-5553,-5574]]},{"type":"Polygon","id":21013,"arcs":[[-5578,-5728,5826,5827,-5819,-5721,-5550]]},{"type":"Polygon","id":21039,"arcs":[[-5637,5828,5829,-5707,-5632]]},{"type":"Polygon","id":21083,"arcs":[[-5706,5830,5831,5832,5833,-5829,-5636]]},{"type":"Polygon","id":21057,"arcs":[[5834,5835,5836,-5651,-5589,-5650]]},{"type":"Polygon","id":21003,"arcs":[[-5657,5837,5838,5839,5840,-5645]]},{"type":"Polygon","id":29009,"arcs":[[-5817,5841,5842,5843,-5718,-5603]]},{"type":"Polygon","id":51810,"arcs":[[5844,5845,5846,5847,5848,5849,5850,-5823,5851]]},{"type":"Polygon","id":51035,"arcs":[[5852,5853,5854,5855,5856,-5693,-5622,-5677]]},{"type":"Polygon","id":51191,"arcs":[[5857,5858,5859,-5668,-5730,5860,5861,5862,5863]]},{"type":"Polygon","id":29023,"arcs":[[-5680,5864,5865,5866,-5687,-5580]]},{"type":"Polygon","id":51800,"arcs":[[-5868,5868,5869,5870,-5802,-5662,5871]]},{"type":"Polygon","id":6053,"arcs":[[-5818,-5383,5872,5873,5874,-5606]]},{"type":"Polygon","id":51081,"arcs":[[-5686,-5806,5875,-5723,-5610]]},{"type":"Polygon","id":51105,"arcs":[[5876,5877,5878,-5827,-5727,-5642]]},{"type":"Polygon","id":51117,"arcs":[[-5726,5879,5880,5881,-5711,-5618,-5682]]},{"type":"Polygon","id":21053,"arcs":[[-5810,5882,5883,-5835,-5649]]},{"type":"Polygon","id":29149,"arcs":[[-5689,5884,5885,5886,5887,-5715,-5494]]},{"type":"Polygon","id":51169,"arcs":[[-5669,-5860,5888,5889,5890,-5877,-5641]]},{"type":"Polygon","id":21213,"arcs":[[5891,5892,-5696,-5646,-5841]]},{"type":"Polygon","id":51141,"arcs":[[5893,5894,5895,-5853,-5676,-5630]]},{"type":"Polygon","id":29143,"arcs":[[-5710,5896,5897,5898,5899,5900,5901,-5678,-5615]]},{"type":"Polygon","id":51550,"arcs":[[5902,-5904,5904,5905,-5850,5906,5907,-5869,-5909]]},{"type":"Polygon","id":51089,"arcs":[[5909,5910,-5894,-5629,-5675]]},{"type":"Polygon","id":32003,"arcs":[[5911,5912,-5467,-4254,-4626]]},{"type":"Polygon","id":21171,"arcs":[[-5837,5913,5914,-5838,-5656,-5652]]},{"type":"Polygon","id":29181,"arcs":[[-5867,5915,5916,-5885,-5688]]},{"type":"Polygon","id":29213,"arcs":[[-5703,5917,5918,5919,5920,-5815,-5691]]},{"type":"Polygon","id":40153,"arcs":[[5921,5922,5923,-5768,5750,-5751,-5750]]},{"type":"Polygon","id":51077,"arcs":[[-5857,5924,-5855,5925,5926,5927,5928,-5861,-5729,-5694]]},{"type":"Polygon","id":29153,"arcs":[[-5717,5929,5930,5931,-5918,-5702]]},{"type":"Polygon","id":21105,"arcs":[[-5834,5932,5933,5934,-5708,-5830]]},{"type":"Polygon","id":29119,"arcs":[[-5844,5935,5936,-5766,-5719]]},{"type":"Polygon","id":21035,"arcs":[[-5739,5937,5938,-5831,-5705]]},{"type":"Polygon","id":6107,"arcs":[[5939,5940,-5381,-5470]]},{"type":"Polygon","id":47161,"arcs":[[-5660,5941,5942,5943,5944,-5938,-5738]]},{"type":"Polygon","id":40041,"arcs":[[-5937,5945,5946,5947,5948,-5754,-5767]]},{"type":"Polygon","id":47147,"arcs":[[5949,5950,5951,-5699,-5697,-5893,5952]]},{"type":"Polygon","id":47165,"arcs":[[5953,5954,5955,5956,-5953,-5892,-5840]]},{"type":"MultiPolygon","id":21075,"arcs":[[[5957,5958,-5897,-5709,-5935]]]},{"type":"Polygon","id":47125,"arcs":[[-5952,5959,5960,5961,-5942,-5659,-5700]]},{"type":"Polygon","id":51590,"arcs":[[5962,-5672]]},{"type":"Polygon","id":47111,"arcs":[[5963,5964,5965,-5954,-5839,-5915,5966]]},{"type":"Polygon","id":29069,"arcs":[[-5902,5967,5968,5969,5970,5971,5972,-5865,-5679]]},{"type":"Polygon","id":47137,"arcs":[[5973,5974,5975,5976,-5883,-5809]]},{"type":"Polygon","id":47027,"arcs":[[5977,5978,-5967,-5914,-5836,-5884,-5977]]},{"type":"Polygon","id":47163,"arcs":[[5979,5980,5981,-5889,-5859,-5983,-5863,5983]]},{"type":"Polygon","id":47091,"arcs":[[-5929,5984,5985,5986,5987,-5984,-5862]]},{"type":"Polygon","id":47151,"arcs":[[5988,5989,5990,5991,-5974,-5808,-5826]]},{"type":"Polygon","id":47025,"arcs":[[5992,5993,5994,5995,-5820,-5828,-5879]]},{"type":"Polygon","id":40103,"arcs":[[5996,5997,5998,5999,-5764,-5746]]},{"type":"Polygon","id":47067,"arcs":[[-5891,6000,6001,-5993,-5878]]},{"type":"Polygon","id":40131,"arcs":[[6002,6003,6004,-5757,-5741,-5756]]},{"type":"Polygon","id":47013,"arcs":[[6005,6006,-5989,-5825,-5821,-5996]]},{"type":"Polygon","id":47073,"arcs":[[-5982,6007,6008,6009,6010,-6001,-5890]]},{"type":"Polygon","id":40047,"arcs":[[-5765,-6000,6011,6012,6013,-5762,-5760]]},{"type":"Polygon","id":40045,"arcs":[[-5924,6014,6015,6016,6017,-5798,-5769]]},{"type":"Polygon","id":37009,"arcs":[[6018,6019,6020,-5985,-5928]]},{"type":"Polygon","id":47049,"arcs":[[-5992,6021,6022,6023,6024,-5975]]},{"type":"Polygon","id":37005,"arcs":[[6025,6026,-6019,-5927]]},{"type":"Polygon","id":40117,"arcs":[[6027,6028,6029,-5997,-5745]]},{"type":"Polygon","id":37171,"arcs":[[-5896,6030,6031,6032,6033,-6026,-5926,-5854]]},{"type":"Polygon","id":37073,"arcs":[[6034,6035,6036,6037,6038,6039,-5803,-5871]]},{"type":"MultiPolygon","id":37053,"arcs":[[[6040,-5907,-5849,6041]],[[-5845,6042,6043,6044]]]},{"type":"Polygon","id":37169,"arcs":[[-5911,6045,6046,-6031,-5895]]},{"type":"Polygon","id":37029,"arcs":[[-5870,-5908,-6041,6047,6048,-6035]]},{"type":"Polygon","id":37185,"arcs":[[6049,6050,6051,6052,-5880,-5725]]},{"type":"Polygon","id":37131,"arcs":[[6053,6054,-6050,-5724,-5876,-5805,6055]]},{"type":"Polygon","id":37091,"arcs":[[-6040,6056,6057,-6056,-5804]]},{"type":"Polygon","id":37145,"arcs":[[6058,6059,6060,6061,-5713]]},{"type":"Polygon","id":37181,"arcs":[[-6053,6062,6063,-5881]]},{"type":"Polygon","id":37077,"arcs":[[6064,6065,6066,-6059,-5712,-5882,-6064]]},{"type":"Polygon","id":37157,"arcs":[[-5674,6067,6068,6069,-6046,-5910]]},{"type":"Polygon","id":37033,"arcs":[[-6062,6070,6071,-6068,-5673,-5963,-5671,-5714]]},{"type":"Polygon","id":47133,"arcs":[[-6025,6072,6073,-5978,-5976]]},{"type":"Polygon","id":47087,"arcs":[[-6074,6074,6075,-5964,-5979]]},{"type":"Polygon","id":37083,"arcs":[[6076,6077,6078,6079,-6051,-6055]]},{"type":"Polygon","id":40097,"arcs":[[-5949,6080,6081,-6003,-5755]]},{"type":"Polygon","id":47019,"arcs":[[6082,6083,6084,6085,-5980,-5988]]},{"type":"Polygon","id":37139,"arcs":[[6086,6087,-6036,-6049]]},{"type":"Polygon","id":47131,"arcs":[[-5934,6088,6089,6090,6091,-5958]]},{"type":"Polygon","id":40093,"arcs":[[-5763,-6014,6092,6093,6094,-5922,-5749]]},{"type":"Polygon","id":47183,"arcs":[[6095,6096,6097,-6089,-5933,-5833]]},{"type":"Polygon","id":47095,"arcs":[[6098,6099,-5900,-6101,-5898,-5959,-6092]]},{"type":"Polygon","id":48421,"arcs":[[6101,6102,-5792,-5797,6103]]},{"type":"Polygon","id":47079,"arcs":[[-5945,6104,6105,-6096,-5832,-5939]]},{"type":"Polygon","id":48195,"arcs":[[6106,6107,-6104,-5796,6108]]},{"type":"Polygon","id":48111,"arcs":[[-5793,-6103,6109,-5786]]},{"type":"Polygon","id":48357,"arcs":[[6110,6111,-6109,-5795,-5800]]},{"type":"Polygon","id":48295,"arcs":[[6112,6113,-6111,-5799,-6018]]},{"type":"Polygon","id":5007,"arcs":[[-5843,6114,6115,6116,6117,-5946,-5936]]},{"type":"Polygon","id":5049,"arcs":[[6118,6119,6120,-5930,-5716,-5888]]},{"type":"Polygon","id":5015,"arcs":[[-5816,-5921,6121,6122,6123,-6115,-5842]]},{"type":"Polygon","id":5135,"arcs":[[-5887,6124,6125,6126,6127,-6119]]},{"type":"Polygon","id":5121,"arcs":[[6128,6129,-6125,-5886,-5917,6130]]},{"type":"Polygon","id":5009,"arcs":[[6131,6132,6133,-6122,-5920]]},{"type":"Polygon","id":5089,"arcs":[[6134,6135,-6132,-5919,-5932]]},{"type":"Polygon","id":5005,"arcs":[[-6121,6136,6137,6138,-6135,-5931]]},{"type":"Polygon","id":5021,"arcs":[[-5866,-5973,6139,-6131,-5916]]},{"type":"Polygon","id":6031,"arcs":[[6140,6141,-5873,-5382,-5941]]},{"type":"Polygon","id":47169,"arcs":[[-5966,6142,6143,-5955]]},{"type":"Polygon","id":47021,"arcs":[[6144,6145,6146,6147,-5960,-5951]]},{"type":"Polygon","id":47179,"arcs":[[-6086,6148,6149,-6008,-5981]]},{"type":"Polygon","id":37193,"arcs":[[-6034,6150,6151,6152,6153,6154,-6020,-6027]]},{"type":"Polygon","id":47173,"arcs":[[6155,6156,6157,-6006,-5995]]},{"type":"Polygon","id":47159,"arcs":[[-6076,6158,6159,6160,-6143,-5965]]},{"type":"Polygon","id":29155,"arcs":[[-6100,6161,6162,-5968,-5901]]},{"type":"Polygon","id":40143,"arcs":[[-6005,6163,6164,6165,-6028,-5744,-5758]]},{"type":"Polygon","id":47057,"arcs":[[-6002,-6011,6166,6167,6168,-6156,-5994]]},{"type":"Polygon","id":47037,"arcs":[[-5957,6169,6170,6171,-6145,-5950]]},{"type":"Polygon","id":47059,"arcs":[[6172,6173,6174,6175,6176,-6009,-6150]]},{"type":"Polygon","id":37189,"arcs":[[-6155,6177,6178,-5986,-6021]]},{"type":"Polygon","id":37143,"arcs":[[6179,6180,-6037,-6088]]},{"type":"Polygon","id":47129,"arcs":[[-5991,6181,6182,6183,-6022]]},{"type":"Polygon","id":47083,"arcs":[[-5962,6184,6185,6186,-5943]]},{"type":"Polygon","id":47005,"arcs":[[-6187,6187,6188,6189,6190,-6105,-5944]]},{"type":"Polygon","id":47189,"arcs":[[-6144,-6161,6191,6192,6193,-6170,-5956]]},{"type":"Polygon","id":37041,"arcs":[[6194,-6038,-6181]]},{"type":"Polygon","id":47063,"arcs":[[-6177,6195,6196,-6167,-6010]]},{"type":"Polygon","id":47043,"arcs":[[-6148,-6147,6197,6198,6199,-6185,-5961]]},{"type":"Polygon","id":5087,"arcs":[[6200,6201,6202,6203,6204,-6116,-6124]]},{"type":"Polygon","id":47141,"arcs":[[-6024,6205,6206,6207,-6159,-6075,-6073]]},{"type":"Polygon","id":47001,"arcs":[[-6158,6208,6209,-6182,-5990,-6007]]},{"type":"Polygon","id":37197,"arcs":[[6210,6211,6212,-6151,-6033]]},{"type":"Polygon","id":37011,"arcs":[[6213,6214,6215,6216,6217,-6083,-5987,-6179]]},{"type":"Polygon","id":37069,"arcs":[[6218,6219,-6065,-6063,-6052]]},{"type":"Polygon","id":5055,"arcs":[[-5972,-5971,6220,6221,-6129,-6140]]},{"type":"Polygon","id":35033,"arcs":[[-5814,6222,6223,6224,-5772,-5812]]},{"type":"Polygon","id":37067,"arcs":[[6225,6226,6227,-6211,-6032,-6047]]},{"type":"Polygon","id":5065,"arcs":[[-6128,6228,6229,-6137,-6120]]},{"type":"Polygon","id":47171,"arcs":[[6230,6231,6232,-6173,-6149,-6085]]},{"type":"Polygon","id":5075,"arcs":[[-6222,6233,6234,6235,-6126,-6130]]},{"type":"Polygon","id":37081,"arcs":[[6236,6237,6238,-6226,-6070]]},{"type":"Polygon","id":37001,"arcs":[[6239,6240,6241,-6237,-6069,-6072]]},{"type":"Polygon","id":40119,"arcs":[[6242,6243,6244,-5998,-6030]]},{"type":"Polygon","id":47085,"arcs":[[-6200,6245,6246,-6188,-6186]]},{"type":"Polygon","id":37015,"arcs":[[6247,6248,6249,-6250,6250,-6077,-6054,-6058]]},{"type":"Polygon","id":37135,"arcs":[[-6061,6251,6252,-6240,-6071]]},{"type":"Polygon","id":37063,"arcs":[[-6067,6253,6254,-6252,-6060]]},{"type":"Polygon","id":5143,"arcs":[[6255,6256,-6117,-6205]]},{"type":"MultiPolygon","id":37055,"arcs":[[[6257,6258]],[[-6044,6259]]]},{"type":"Polygon","id":47053,"arcs":[[6260,6261,6262,6263,-6090,-6098]]},{"type":"Polygon","id":35043,"arcs":[[6264,6265,6266,6267,6268,-5777,-5775]]},{"type":"Polygon","id":35021,"arcs":[[-5789,6269,6270,-6223,-5813]]},{"type":"Polygon","id":47045,"arcs":[[6271,6272,6273,-6162,-6099,-6091,-6264]]},{"type":"Polygon","id":37127,"arcs":[[-6080,6274,6275,6276,-6219]]},{"type":"Polygon","id":47089,"arcs":[[-6197,6277,6278,6279,-6168]]},{"type":"Polygon","id":47093,"arcs":[[-6280,6280,6281,6282,6283,-6209,-6157,-6169]]},{"type":"Polygon","id":47029,"arcs":[[-6175,6284,6285,6286,6287,-6278,-6196,-6176]]},{"type":"Polygon","id":47035,"arcs":[[-6184,6288,6289,6290,6291,6292,6293,6294,-6206,-6023]]},{"type":"Polygon","id":40073,"arcs":[[6295,6296,6297,-6093,-6013]]},{"type":"Polygon","id":40083,"arcs":[[-5999,-6245,6298,6299,-6296,-6012]]},{"type":"Polygon","id":40011,"arcs":[[-6298,6300,6301,6302,6303,-6094]]},{"type":"Polygon","id":40043,"arcs":[[6304,6305,-6015,-5923,-6095,-6304]]},{"type":"Polygon","id":40037,"arcs":[[-6166,6306,6307,6308,-6243,-6029]]},{"type":"Polygon","id":40145,"arcs":[[-6082,6309,6310,6311,-6164,-6004]]},{"type":"Polygon","id":40021,"arcs":[[-5948,6312,6313,6314,-6310,-6081]]},{"type":"Polygon","id":40001,"arcs":[[-6118,-6257,6315,6316,-6313,-5947]]},{"type":"Polygon","id":37121,"arcs":[[-6218,6317,6318,-6231,-6084]]},{"type":"Polygon","id":37065,"arcs":[[6319,6320,6321,-6275,-6079]]},{"type":"Polygon","id":47017,"arcs":[[-6191,6322,6323,6324,-6261,-6097,-6106]]},{"type":"Polygon","id":47041,"arcs":[[6325,6326,6327,-6192,-6160,-6208]]},{"type":"Polygon","id":5137,"arcs":[[6328,6329,6330,6331,-6138,-6230]]},{"type":"Polygon","id":5101,"arcs":[[-6134,6332,6333,6334,-6201,-6123]]},{"type":"Polygon","id":37027,"arcs":[[-6154,6335,6336,6337,-6214,-6178]]},{"type":"Polygon","id":5129,"arcs":[[-6139,-6332,6338,6339,-6333,-6133,-6136]]},{"type":"Polygon","id":47149,"arcs":[[6340,6341,6342,6343,6344,-6171,-6194]]},{"type":"Polygon","id":37199,"arcs":[[6345,6346,6347,-6232,-6319]]},{"type":"Polygon","id":47185,"arcs":[[-6295,6348,6349,-6326,-6207]]},{"type":"Polygon","id":37183,"arcs":[[-6220,6350,6351,6352,-6254,-6066]]},{"type":"Polygon","id":37117,"arcs":[[6249,6353,6354,6355,-6320,-6078,-6251]]},{"type":"Polygon","id":37115,"arcs":[[-6233,-6348,6356,6357,-6285,6174,-6175,-6174]]},{"type":"Polygon","id":37059,"arcs":[[6358,6359,6360,-6212,-6228]]},{"type":"Polygon","id":48393,"arcs":[[-6114,6361,6362,6363,6364,-6107,-6112]]},{"type":"Polygon","id":48211,"arcs":[[-6113,-6017,6365,6366,-6362]]},{"type":"Polygon","id":48233,"arcs":[[-6108,-6365,6367,6368]]},{"type":"Polygon","id":48205,"arcs":[[6369,6370,6371,-5787,-6110]]},{"type":"Polygon","id":37097,"arcs":[[-6213,-6361,6372,6373,6374,6375,6376,6377,-6152]]},{"type":"Polygon","id":48341,"arcs":[[-6369,6378,6379,6380,-6370,-6102]]},{"type":"Polygon","id":47187,"arcs":[[-6345,6381,6382,6383,-6198,-6146,-6172]]},{"type":"Polygon","id":37003,"arcs":[[6384,-6336,-6153,-6378]]},{"type":"Polygon","id":47145,"arcs":[[6385,6386,6387,6388,-6289,-6183,-6210,-6284,6389]]},{"type":"Polygon","id":47155,"arcs":[[-6288,-6287,6286,6390,6391,6392,-6281,-6279]]},{"type":"Polygon","id":37057,"arcs":[[-6239,6393,6394,6395,-6359,-6227]]},{"type":"Polygon","id":40129,"arcs":[[-6306,6396,6397,6398,-6366,-6016]]},{"type":"Polygon","id":35031,"arcs":[[-6269,6399,-5780,-5778]]},{"type":"Polygon","id":35049,"arcs":[[-6225,6400,6401,6402,-6266,6403,6404,6405,-5773]]},{"type":"Polygon","id":5093,"arcs":[[6406,6407,6408,6409,6410,6411,6412,-5969,-6163,-6274]]},{"type":"Polygon","id":37023,"arcs":[[-6338,6413,6414,6415,6416,6417,-6216,-6215]]},{"type":"Polygon","id":47033,"arcs":[[6418,6419,6420,-6272,-6263]]},{"type":"Polygon","id":5031,"arcs":[[-6413,6421,6422,-6234,-6221,-5970]]},{"type":"Polygon","id":37177,"arcs":[[6423,6424,6425]]},{"type":"Polygon","id":47081,"arcs":[[-6384,6426,6427,6428,-6246,-6199]]},{"type":"Polygon","id":37187,"arcs":[[-6425,6429,6430,-6354,-6250,-6249,6431]]},{"type":"Polygon","id":35028,"arcs":[[-5774,-6406,6432,-6404,-6265]]},{"type":"Polygon","id":47015,"arcs":[[6433,6434,-6341,-6193,-6328]]},{"type":"Polygon","id":37111,"arcs":[[6435,6436,-6346,-6318,-6217,-6418]]},{"type":"Polygon","id":47097,"arcs":[[-6421,6437,6438,-6407,-6273]]},{"type":"Polygon","id":40081,"arcs":[[-6309,6439,6440,6441,-6299,-6244]]},{"type":"Polygon","id":5063,"arcs":[[-6236,6442,6443,6444,-6329,-6229,-6127]]},{"type":"Polygon","id":37151,"arcs":[[-6242,6445,6446,6447,-6394,-6238]]},{"type":"Polygon","id":47105,"arcs":[[-6390,-6283,6448,6449,6450,-6386]]},{"type":"Polygon","id":5067,"arcs":[[-6235,-6423,6451,6452,6453,6454,-6443]]},{"type":"Polygon","id":47009,"arcs":[[6455,6456,6457,-6449,-6282,-6393]]},{"type":"Polygon","id":37037,"arcs":[[-6255,-6353,6458,6459,6460,6461,-6446,-6241,-6253]]},{"type":"Polygon","id":35047,"arcs":[[-6271,6462,6463,6464,-6401,-6224]]},{"type":"Polygon","id":37195,"arcs":[[6465,6466,6467,6468,-6276,-6322]]},{"type":"Polygon","id":37159,"arcs":[[-6396,6469,6470,-6373,-6360]]},{"type":"Polygon","id":40111,"arcs":[[-6165,-6312,6471,6472,6473,-6307]]},{"type":"Polygon","id":40101,"arcs":[[-6315,6474,6475,6476,-6472,-6311]]},{"type":"Polygon","id":47177,"arcs":[[-6350,6477,6478,6479,6480,-6434,-6327]]},{"type":"Polygon","id":47119,"arcs":[[6481,6482,6483,6484,-6427,-6383]]},{"type":"Polygon","id":47039,"arcs":[[6485,6486,6487,6488,-6323,-6190]]},{"type":"Polygon","id":47135,"arcs":[[-6429,6489,6490,-6486,-6189,-6247]]},{"type":"Polygon","id":37147,"arcs":[[-6356,6491,6492,6493,6494,6495,6496,-6466,-6321]]},{"type":"Polygon","id":37035,"arcs":[[-6377,6497,-6414,-6337,-6385]]},{"type":"Polygon","id":37021,"arcs":[[-6437,6498,6499,6500,-6357,-6347]]},{"type":"Polygon","id":47143,"arcs":[[6501,6502,6503,-6290,-6389]]},{"type":"Polygon","id":47175,"arcs":[[-6294,6504,-6292,6505,6506,-6478,-6349]]},{"type":"Polygon","id":47077,"arcs":[[-6489,6507,6508,6509,-6324]]},{"type":"Polygon","id":47075,"arcs":[[6510,6511,6512,6513,-6438,-6420]]},{"type":"Polygon","id":37101,"arcs":[[-6469,6514,6515,6516,-6351,-6277]]},{"type":"Polygon","id":40039,"arcs":[[-6303,6517,6518,6519,-6397,-6305]]},{"type":"Polygon","id":6071,"arcs":[[6520,6521,6522,6523,6524,6525,-5468,-5913]]},{"type":"Polygon","id":6079,"arcs":[[-6142,6526,6527,6528,-5874]]},{"type":"Polygon","id":6029,"arcs":[[-6526,6529,6530,6531,-6527,-6141,-5940,-5469]]},{"type":"Polygon","id":47113,"arcs":[[-6325,-6510,6532,6533,-6511,-6419,-6262]]},{"type":"Polygon","id":5141,"arcs":[[-6331,6534,6535,6536,6537,-6339]]},{"type":"Polygon","id":37087,"arcs":[[-6501,6538,6539,6540,-6391,-6287,-6286,-6358]]},{"type":"Polygon","id":5047,"arcs":[[6541,6542,6543,-6203,6544]]},{"type":"Polygon","id":47007,"arcs":[[-6504,6545,6546,-6506,-6291]]},{"type":"Polygon","id":5071,"arcs":[[-6202,-6335,6547,6548,-6545]]},{"type":"Polygon","id":5033,"arcs":[[6549,6550,-6316,-6256,-6204,-6544]]},{"type":"Polygon","id":47121,"arcs":[[6551,6552,6553,-6502,-6388]]},{"type":"Polygon","id":35037,"arcs":[[-6372,6554,6555,6556,6557,6558,6559,-6463,-6270,-5788]]},{"type":"MultiPolygon","id":37013,"arcs":[[[6560,6561,6562,-6494]],[[-6431,6563,6564,-6492,-6355]]]},{"type":"Polygon","id":5115,"arcs":[[-6340,-6538,6565,6566,6567,-6548,-6334]]},{"type":"Polygon","id":40017,"arcs":[[6568,6569,6570,6571,6572,-6301,-6297]]},{"type":"Polygon","id":40109,"arcs":[[-6442,6573,6574,-6569,-6300]]},{"type":"Polygon","id":5023,"arcs":[[-6445,6575,6576,-6535,-6330]]},{"type":"Polygon","id":47117,"arcs":[[6577,6578,6579,-6482,-6382,-6344]]},{"type":"Polygon","id":5111,"arcs":[[-6412,6580,6581,-6452,-6422]]},{"type":"Polygon","id":37095,"arcs":[[-6424,6582,-6258,6583,-6564,-6430]]},{"type":"Polygon","id":47031,"arcs":[[-6435,-6481,6584,6585,6586,6587,-6342]]},{"type":"Polygon","id":47003,"arcs":[[-6588,6588,6589,-6578,-6343]]},{"type":"Polygon","id":37173,"arcs":[[6590,6591,6592,-6456,-6392,-6541]]},{"type":"Polygon","id":47123,"arcs":[[-6458,6593,6594,6595,6596,6597,6598,-6450]]},{"type":"Polygon","id":47101,"arcs":[[-6485,6599,6600,-6490,-6428]]},{"type":"Polygon","id":37079,"arcs":[[6601,6602,-6467,-6497]]},{"type":"MultiPolygon","id":47167,"arcs":[[[6603,6604,-6410]],[[-6514,6605,6606,-6408,-6439]]]},{"type":"Polygon","id":47107,"arcs":[[-6451,-6599,6607,-6597,6608,6609,-6552,-6387]]},{"type":"Polygon","id":40135,"arcs":[[-6317,-6551,6610,6611,6612,-6475,-6314]]},{"type":"Polygon","id":40107,"arcs":[[-6474,6613,6614,6615,6616,-6440,-6308]]},{"type":"Polygon","id":37105,"arcs":[[6617,6618,-6461,-6460]]},{"type":"Polygon","id":48359,"arcs":[[-6381,6619,6620,-6555,-6371]]},{"type":"Polygon","id":48065,"arcs":[[-6364,6621,6622,6623,-6379,-6368]]},{"type":"Polygon","id":48179,"arcs":[[6624,6625,-6622,-6363]]},{"type":"Polygon","id":48483,"arcs":[[-6399,6626,6627,-6625,-6367]]},{"type":"Polygon","id":48375,"arcs":[[-6624,6628,-6620,-6380]]},{"type":"Polygon","id":37161,"arcs":[[6629,6630,6631,6632,6633,-6499,-6436,-6417]]},{"type":"Polygon","id":37191,"arcs":[[-6603,6634,6635,6636,-6515,-6468]]},{"type":"Polygon","id":47023,"arcs":[[6637,6638,6639,-6533,-6509]]},{"type":"Polygon","id":37085,"arcs":[[-6517,6640,6641,6642,-6618,-6459,-6352]]},{"type":"Polygon","id":37045,"arcs":[[6643,6644,6645,6646,-6630,-6416]]},{"type":"Polygon","id":37109,"arcs":[[-6376,6647,6648,-6644,-6415,-6498]]},{"type":"Polygon","id":47153,"arcs":[[6649,6650,6651,-6479,-6507,-6547]]},{"type":"Polygon","id":40091,"arcs":[[6652,6653,6654,-6614,-6473,-6477]]},{"type":"Polygon","id":40015,"arcs":[[-6573,6655,6656,6657,6658,-6518,-6302]]},{"type":"Polygon","id":5145,"arcs":[[-6455,6659,6660,6661,6662,-6576,-6444]]},{"type":"Polygon","id":47061,"arcs":[[6663,6664,-6585,-6480,-6652]]},{"type":"Polygon","id":4025,"arcs":[[6665,6666,6667,6668,6669,-5736]]},{"type":"Polygon","id":37099,"arcs":[[-6540,6670,6671,6672,6673,-6591]]},{"type":"Polygon","id":37119,"arcs":[[6674,6675,6676,6677,6678,-6648,-6375]]},{"type":"Polygon","id":37125,"arcs":[[-6619,-6643,6679,6680,6681,6682,-6447,-6462]]},{"type":"Polygon","id":37123,"arcs":[[-6683,6683,6684,-6395,-6448]]},{"type":"Polygon","id":37025,"arcs":[[6685,6686,-6675,-6374,-6471]]},{"type":"Polygon","id":40009,"arcs":[[-6520,6687,6688,6689,6690,6691,-6627,-6398]]},{"type":"Polygon","id":37167,"arcs":[[-6685,6692,6693,-6686,-6470]]},{"type":"Polygon","id":37089,"arcs":[[-6634,6694,6695,6696,-6500]]},{"type":"Polygon","id":47181,"arcs":[[6697,6698,6699,-6487,-6491,-6601]]},{"type":"Polygon","id":40133,"arcs":[[6700,6701,6702,-6616]]},{"type":"Polygon","id":37075,"arcs":[[6703,6704,-6594,-6457,-6593]]},{"type":"Polygon","id":40149,"arcs":[[-6659,6705,-6688,-6519]]},{"type":"Polygon","id":40125,"arcs":[[-6617,-6703,6706,6707,6708,-6574,-6441]]},{"type":"Polygon","id":5029,"arcs":[[6709,6710,6711,-6566,-6537]]},{"type":"Polygon","id":47099,"arcs":[[-6484,6712,6713,-6698,-6600]]},{"type":"Polygon","id":40061,"arcs":[[6714,6715,6716,-6653,-6476,-6613]]},{"type":"Polygon","id":47065,"arcs":[[-6554,6717,6718,6719,6720,6721,-6650,-6546,-6503]]},{"type":"Polygon","id":47055,"arcs":[[-6580,6722,6723,6724,-6713,-6483]]},{"type":"Polygon","id":5131,"arcs":[[-6543,6725,6726,6727,-6611,-6550]]},{"type":"Polygon","id":5037,"arcs":[[6728,6729,6730,-6453,-6582]]},{"type":"Polygon","id":5147,"arcs":[[-6731,6731,6732,6733,-6660,-6454]]},{"type":"Polygon","id":5035,"arcs":[[-6411,-6605,6734,6735,6736,6737,6738,-6729,-6581]]},{"type":"Polygon","id":47069,"arcs":[[-6534,-6640,6739,6740,6741,6742,6743,-6512]]},{"type":"Polygon","id":37107,"arcs":[[6744,6745,6746,-6635,-6602,-6496]]},{"type":"Polygon","id":5083,"arcs":[[-6549,-6568,6747,6748,-6726,-6542]]},{"type":"Polygon","id":37175,"arcs":[[6749,6750,6751,-6671,-6539,-6697]]},{"type":"Polygon","id":47071,"arcs":[[-6700,6752,6753,6754,6755,-6638,-6508,-6488]]},{"type":"Polygon","id":37071,"arcs":[[-6679,6756,-6645,-6649]]},{"type":"MultiPolygon","id":37049,"arcs":[[[6757,6758,6759]],[[-6563,6760,6761,6762,-6745,-6495]]]},{"type":"Polygon","id":47127,"arcs":[[6763,6764,-6589,-6587]]},{"type":"Polygon","id":47157,"arcs":[[6765,6766,6767,-6735,-6604,-6409,-6607]]},{"type":"Polygon","id":37149,"arcs":[[6768,6769,-6695,-6633]]},{"type":"Polygon","id":47047,"arcs":[[-6744,6770,6771,-6766,-6606,-6513]]},{"type":"Polygon","id":47109,"arcs":[[-6756,6772,-6740,-6639]]},{"type":"Polygon","id":40079,"arcs":[[6773,6774,6775,6776,6777,-6715,-6612,-6728]]},{"type":"Polygon","id":40051,"arcs":[[6778,6779,6780,6781,-6656,-6572]]},{"type":"Polygon","id":40027,"arcs":[[-6709,6782,-6570,-6575]]},{"type":"Polygon","id":47103,"arcs":[[-6765,6783,6784,6785,-6723,-6579,-6590]]},{"type":"Polygon","id":5045,"arcs":[[-6577,-6663,6786,6787,6788,-6710,-6536]]},{"type":"Polygon","id":47051,"arcs":[[-6665,6789,6790,6791,-6784,-6764,-6586]]},{"type":"Polygon","id":47011,"arcs":[[6792,6793,6794,-6718,-6553,-6610]]},{"type":"Polygon","id":35006,"arcs":[[-6268,6795,6796,6797,6798,-5781,-6400]]},{"type":"Polygon","id":40087,"arcs":[[-6708,6799,6800,-6779,-6571,-6783]]},{"type":"Polygon","id":37113,"arcs":[[6801,6802,6803,-6704,-6592,-6674]]},{"type":"Polygon","id":37137,"arcs":[[-6761,-6562,6804]]},{"type":"Polygon","id":47115,"arcs":[[-6722,6805,6806,-6790,-6664,-6651]]},{"type":"Polygon","id":37163,"arcs":[[-6637,6807,6808,6809,6810,-6641,-6516]]},{"type":"Polygon","id":5149,"arcs":[[-6712,6811,6812,6813,6814,-6748,-6567]]},{"type":"Polygon","id":40121,"arcs":[[-6717,6815,6816,6817,6818,6819,-6654]]},{"type":"Polygon","id":37039,"arcs":[[-6804,6820,6821,6822,6823,-6595,-6705]]},{"type":"Polygon","id":40063,"arcs":[[-6655,-6820,6824,6825,-6701,-6615]]},{"type":"Polygon","id":47139,"arcs":[[-6824,6826,6827,-6793,-6609,-6596]]},{"type":"Polygon","id":37051,"arcs":[[-6811,6828,6829,6830,-6680,-6642]]},{"type":"Polygon","id":37103,"arcs":[[6831,-6759,6832,6833,6834,-6746,-6763]]},{"type":"Polygon","id":35001,"arcs":[[-6403,6835,6836,-6796,-6267]]},{"type":"Polygon","id":35019,"arcs":[[-6560,6837,6838,6839,-6464]]},{"type":"Polygon","id":45045,"arcs":[[-6770,6840,6841,6842,6843,6844,-6750,-6696]]},{"type":"Polygon","id":37007,"arcs":[[6845,6846,6847,6848,-6693]]},{"type":"Polygon","id":37093,"arcs":[[-6831,6849,6850,6851,-6681]]},{"type":"Polygon","id":37179,"arcs":[[-6849,6852,6853,-6676,-6687,-6694]]},{"type":"Polygon","id":45083,"arcs":[[6854,6855,-6841,-6769,-6632,6856]]},{"type":"Polygon","id":37061,"arcs":[[-6747,-6835,6857,6858,-6808,-6636]]},{"type":"Polygon","id":48117,"arcs":[[6859,6860,6861,6862,-6556,-6621]]},{"type":"Polygon","id":45021,"arcs":[[6863,6864,-6857,-6631,-6647,6865]]},{"type":"Polygon","id":48129,"arcs":[[6866,6867,6868,6869,-6626]]},{"type":"Polygon","id":48087,"arcs":[[-6692,6870,6871,6872,-6867,-6628]]},{"type":"Polygon","id":48381,"arcs":[[6873,6874,6875,-6860,-6629]]},{"type":"Polygon","id":37153,"arcs":[[-6682,6876,6877,-6846,-6684]]},{"type":"Polygon","id":48011,"arcs":[[-6870,6878,6879,-6874,-6623]]},{"type":"Polygon","id":45091,"arcs":[[6880,6881,6882,-6866,-6646,-6757,-6678]]},{"type":"Polygon","id":37043,"arcs":[[-6803,6883,6884,6885,-6821]]},{"type":"Polygon","id":5123,"arcs":[[-6739,6886,6887,-6732,-6730]]},{"type":"Polygon","id":40075,"arcs":[[-6658,6888,6889,6890,6891,-6689,-6706]]},{"type":"MultiPolygon","id":37031,"arcs":[[[6892,-6833,-6758,6893]]]},{"type":"Polygon","id":40055,"arcs":[[-6892,6894,6895,-6690]]},{"type":"MultiPolygon","id":6083,"arcs":[[[6896]],[[6897]],[[-6532,6898,6899,-6528]]]},{"type":"Polygon","id":5105,"arcs":[[6900,6901,6902,-6812,-6711,-6789]]},{"type":"Polygon","id":5127,"arcs":[[-6815,6903,6904,-6774,-6727,-6749]]},{"type":"Polygon","id":5117,"arcs":[[-6734,6905,6906,6907,-6661]]},{"type":"Polygon","id":45077,"arcs":[[-6845,6908,6909,-6751]]},{"type":"Polygon","id":5085,"arcs":[[-6908,6910,6911,6912,-6787,-6662]]},{"type":"Polygon","id":45057,"arcs":[[-6854,6913,6914,6915,6916,-6881,-6677]]},{"type":"Polygon","id":40077,"arcs":[[-6778,6917,-6816,-6716]]},{"type":"Polygon","id":45073,"arcs":[[6918,6919,6920,6921,6922,6923,-6672,-6752,-6910]]},{"type":"Polygon","id":35057,"arcs":[[-6840,6924,6925,6926,-6836,-6402,-6465]]},{"type":"Polygon","id":37165,"arcs":[[-6851,6927,6928,-6877,-6852]]},{"type":"Polygon","id":40057,"arcs":[[-6896,6929,6930,6931,-6871,-6691]]},{"type":"Polygon","id":5119,"arcs":[[-6913,6932,6933,6934,-6901,-6788]]},{"type":"Polygon","id":1077,"arcs":[[6935,6936,6937,-6753,-6699,-6714,-6725,6938]]},{"type":"Polygon","id":5095,"arcs":[[-6888,6939,6940,6941,-6906,-6733]]},{"type":"Polygon","id":13241,"arcs":[[6942,6943,-6884,-6802,-6673,-6924]]},{"type":"Polygon","id":1083,"arcs":[[-6786,6944,6945,6946,-6939,-6724]]},{"type":"Polygon","id":28003,"arcs":[[6947,6948,-6741,-6773,-6755,6949]]},{"type":"Polygon","id":28141,"arcs":[[6950,6951,6952,-6950,-6754,-6938,6953]]},{"type":"Polygon","id":28139,"arcs":[[6954,6955,6956,-6742,-6949]]},{"type":"Polygon","id":28033,"arcs":[[6957,6958,6959,-6736,-6768,6960]]},{"type":"Polygon","id":28009,"arcs":[[6961,6962,-6771,-6743,-6957]]},{"type":"Polygon","id":28093,"arcs":[[6963,6964,6965,-6961,-6767,-6772,-6963]]},{"type":"Polygon","id":13281,"arcs":[[-6944,6966,6967,6968,-6885]]},{"type":"Polygon","id":1089,"arcs":[[-6792,6969,6970,6971,-6945,-6785]]},{"type":"Polygon","id":1071,"arcs":[[6972,6973,-6970,-6791,-6807,6974]]},{"type":"Polygon","id":13213,"arcs":[[6975,6976,6977,6978,-6794,-6828,6979]]},{"type":"Polygon","id":13111,"arcs":[[-6823,6980,6981,6982,6983,-6980,-6827]]},{"type":"Polygon","id":13313,"arcs":[[-6978,6984,6985,6986,-6795,-6979]]},{"type":"Polygon","id":13047,"arcs":[[6987,-6719,-6987]]},{"type":"Polygon","id":13291,"arcs":[[-6886,-6969,6988,6989,-6981,-6822]]},{"type":"Polygon","id":37133,"arcs":[[-6893,6990,6991,-6858,-6834]]},{"type":"Polygon","id":13083,"arcs":[[-6806,-6721,6992,6993,-6975]]},{"type":"Polygon","id":13295,"arcs":[[-6986,6994,6995,6996,6997,-6993,-6720,-6988]]},{"type":"Polygon","id":40123,"arcs":[[-6702,-6826,6998,6999,7000,7001,-6800,-6707]]},{"type":"Polygon","id":35061,"arcs":[[-6837,-6927,7002,-6797]]},{"type":"Polygon","id":37155,"arcs":[[7003,7004,7005,7006,7007,-6928,-6850,-6830]]},{"type":"Polygon","id":35009,"arcs":[[-6863,7008,7009,7010,-6557]]},{"type":"Polygon","id":45087,"arcs":[[-6864,-6883,7011,7012,7013,7014,-6855,-6865]]},{"type":"Polygon","id":5077,"arcs":[[-6738,7015,7016,-6940,-6887]]},{"type":"Polygon","id":1033,"arcs":[[7017,7018,-6954,-6937]]},{"type":"MultiPolygon","id":6111,"arcs":[[[7019]],[[7020,-6899,-6531,7021]]]},{"type":"Polygon","id":28143,"arcs":[[-6737,-6960,7022,7023,7024,7025,7026,-7016]]},{"type":"Polygon","id":1049,"arcs":[[-6998,7027,7028,7029,7030,-6973,-6994]]},{"type":"Polygon","id":37017,"arcs":[[7031,7032,-7004,-6829,-6810]]},{"type":"Polygon","id":40065,"arcs":[[7033,7034,7035,7036,-6930,-6895,-6891]]},{"type":"Polygon","id":5125,"arcs":[[-6935,7037,7038,7039,-6902]]},{"type":"Polygon","id":40031,"arcs":[[-6657,-6782,7040,7041,7042,-6889]]},{"type":"Polygon","id":40049,"arcs":[[-7002,7043,7044,7045,-6780,-6801]]},{"type":"Polygon","id":13123,"arcs":[[7046,7047,7048,-6976,-6984]]},{"type":"Polygon","id":13137,"arcs":[[-6923,7049,7050,7051,7052,-6967,-6943]]},{"type":"MultiPolygon","id":6037,"arcs":[[[7053]],[[7054]],[[-6525,7055,7056,-7022,-6530]]]},{"type":"Polygon","id":45023,"arcs":[[-6917,7057,-7012,-6882]]},{"type":"Polygon","id":45007,"arcs":[[-6843,7058,7059,7060,-6919,-6909,-6844]]},{"type":"Polygon","id":45025,"arcs":[[7061,7062,7063,-6914,-6853,-6848,7064]]},{"type":"Polygon","id":45069,"arcs":[[-7008,7065,7066,7067,-7065,-6847,-6878,-6929]]},{"type":"Polygon","id":1079,"arcs":[[-6947,7068,7069,7070,7071,-7018,-6936]]},{"type":"Polygon","id":13311,"arcs":[[7072,7073,-6989,-6968,-7053]]},{"type":"Polygon","id":45059,"arcs":[[-7015,7074,7075,7076,-6842,-6856]]},{"type":"Polygon","id":35011,"arcs":[[7077,7078,7079,-6838,-6559]]},{"type":"Polygon","id":28137,"arcs":[[-6966,7080,7081,-7023,-6959,-6958]]},{"type":"Polygon","id":5051,"arcs":[[-7040,7082,7083,-6813,-6903]]},{"type":"Polygon","id":40029,"arcs":[[-6819,7084,7085,-6999,-6825]]},{"type":"Polygon","id":28117,"arcs":[[-6953,7086,7087,7088,-6955,-6948]]},{"type":"Polygon","id":48437,"arcs":[[-6880,7089,7090,7091,7092,-6875]]},{"type":"Polygon","id":48045,"arcs":[[-6869,7093,7094,7095,-7090,-6879]]},{"type":"Polygon","id":48069,"arcs":[[-7093,7096,7097,7098,-6861,-6876]]},{"type":"Polygon","id":48191,"arcs":[[-6873,7099,7100,7101,-7094,-6868]]},{"type":"Polygon","id":48075,"arcs":[[-6932,7102,7103,-7100,-6872]]},{"type":"Polygon","id":5097,"arcs":[[-7084,7104,7105,7106,7107,-6904,-6814]]},{"type":"Polygon","id":48369,"arcs":[[-7099,7108,7109,-7009,-6862]]},{"type":"Polygon","id":13187,"arcs":[[-7074,7110,7111,-6982,-6990]]},{"type":"Polygon","id":37141,"arcs":[[-6992,7112,7113,7114,7115,-7032,-6809,-6859]]},{"type":"Polygon","id":5113,"arcs":[[7116,7117,7118,-6775,-6905,-7108]]},{"type":"Polygon","id":1103,"arcs":[[-6972,7119,7120,-7069,-6946]]},{"type":"Polygon","id":13257,"arcs":[[7121,7122,-7050,-6922]]},{"type":"Polygon","id":40137,"arcs":[[-7046,7123,7124,7125,-7041,-6781]]},{"type":"Polygon","id":40005,"arcs":[[7126,7127,7128,7129,-7085,-6818]]},{"type":"Polygon","id":40127,"arcs":[[-6777,7130,7131,-7127,-6817,-6918]]},{"type":"Polygon","id":5107,"arcs":[[-7027,7132,7133,7134,7135,-6941,-7017]]},{"type":"Polygon","id":40099,"arcs":[[-7001,7136,7137,-7044]]},{"type":"Polygon","id":40141,"arcs":[[-7043,7138,7139,7140,-7035,-7034,-6890]]},{"type":"Polygon","id":13129,"arcs":[[-7049,7141,7142,7143,-6995,-6985,-6977]]},{"type":"Polygon","id":45033,"arcs":[[7144,7145,7146,-7066,-7007]]},{"type":"Polygon","id":13085,"arcs":[[-7112,7147,7148,7149,7150,-7047,-6983]]},{"type":"Polygon","id":45055,"arcs":[[-7063,7151,7152,7153,7154,7155,-6915,-7064]]},{"type":"Polygon","id":35041,"arcs":[[-7011,7156,7157,7158,7159,-7078,-6558]]},{"type":"Polygon","id":28145,"arcs":[[-7089,7160,7161,7162,-6964,-6962,-6956]]},{"type":"Polygon","id":1095,"arcs":[[-7031,7163,7164,7165,-7120,-6971,-6974]]},{"type":"Polygon","id":13055,"arcs":[[7166,7167,-7028,-6997]]},{"type":"Polygon","id":13115,"arcs":[[-7144,7168,7169,7170,-7167,-6996]]},{"type":"Polygon","id":35053,"arcs":[[-6926,7171,7172,7173,-6798,-7003]]},{"type":"Polygon","id":35003,"arcs":[[-7174,7174,7175,7176,-5782,-6799]]},{"type":"Polygon","id":1059,"arcs":[[-7072,7177,7178,7179,-6951,-7019]]},{"type":"Polygon","id":48197,"arcs":[[-7037,7180,7181,7182,-7103,-6931]]},{"type":"Polygon","id":45039,"arcs":[[-6916,-7156,7183,7184,-7013,-7058]]},{"type":"Polygon","id":5001,"arcs":[[-6942,-7136,7185,7186,7187,-6911,-6907]]},{"type":"Polygon","id":13227,"arcs":[[-7151,7188,-7142,-7048]]},{"type":"Polygon","id":28071,"arcs":[[-7163,7189,7190,7191,7192,-7081,-6965]]},{"type":"Polygon","id":28107,"arcs":[[-7193,7193,7194,7195,-7024,-7082]]},{"type":"Polygon","id":13119,"arcs":[[7196,7197,7198,-7122,-6921]]},{"type":"Polygon","id":45031,"arcs":[[-7068,7199,7200,-7152,-7062]]},{"type":"Polygon","id":45071,"arcs":[[-7185,7201,7202,7203,7204,-7075,-7014]]},{"type":"Polygon","id":1019,"arcs":[[-7171,7205,7206,7207,7208,-7029,-7168]]},{"type":"Polygon","id":28027,"arcs":[[7209,7210,7211,-7133,-7026,7212]]},{"type":"Polygon","id":28119,"arcs":[[-7196,7213,-7213,-7025]]},{"type":"Polygon","id":13139,"arcs":[[-7052,7214,7215,7216,7217,-7148,-7111,-7073]]},{"type":"Polygon","id":28081,"arcs":[[7218,7219,7220,7221,-7161,-7088]]},{"type":"Polygon","id":40019,"arcs":[[-7138,7222,7223,7224,7225,-7124,-7045]]},{"type":"Polygon","id":40069,"arcs":[[-7000,-7086,-7130,7226,7227,-7223,-7137]]},{"type":"Polygon","id":40033,"arcs":[[-7126,7228,7229,7230,-7139,-7042]]},{"type":"Polygon","id":40089,"arcs":[[-7119,7231,7232,7233,7234,7235,-7131,-6776]]},{"type":"Polygon","id":5059,"arcs":[[-7039,7236,7237,7238,-7105,-7083]]},{"type":"Polygon","id":4007,"arcs":[[-5733,7239,7240,7241,-6667,-6666,-5735]]},{"type":"Polygon","id":13147,"arcs":[[-7061,7242,7243,-7197,-6920]]},{"type":"Polygon","id":5053,"arcs":[[-6934,7244,7245,7246,-7237,-7038]]},{"type":"Polygon","id":5069,"arcs":[[-6912,-7188,7247,7248,-7245,-6933]]},{"type":"Polygon","id":13011,"arcs":[[7249,7250,-7215,-7051,-7123,-7199]]},{"type":"Polygon","id":37047,"arcs":[[-7116,7251,7252,-7005,-7033]]},{"type":"Polygon","id":45001,"arcs":[[7253,7254,7255,-7059,-7077]]},{"type":"Polygon","id":28057,"arcs":[[-6952,-7180,7256,7257,-7219,-7087]]},{"type":"Polygon","id":48487,"arcs":[[-7141,7258,7259,7260,-7181,-7036]]},{"type":"Polygon","id":13015,"arcs":[[7261,7262,7263,-7169,-7143,7264]]},{"type":"Polygon","id":13057,"arcs":[[-7150,7265,7266,7267,-7265,-7189]]},{"type":"Polygon","id":45047,"arcs":[[-7205,7268,7269,7270,-7254,-7076]]},{"type":"Polygon","id":37129,"arcs":[[7271,7272,7273,7274,-7114]]},{"type":"Polygon","id":28115,"arcs":[[-7222,7275,7276,-7190,-7162]]},{"type":"Polygon","id":45061,"arcs":[[7277,7278,-7153,-7201]]},{"type":"Polygon","id":37019,"arcs":[[-7275,7279,7280,7281,7282,-7252,-7115]]},{"type":"Polygon","id":5061,"arcs":[[7283,7284,7285,7286,-7117]]},{"type":"Polygon","id":5109,"arcs":[[7287,7288,7289,-7284,-7107]]},{"type":"Polygon","id":35027,"arcs":[[-7080,7290,7291,7292,-7172,-6925,-6839]]},{"type":"Polygon","id":5019,"arcs":[[-7239,7293,7294,7295,-7288,-7106]]},{"type":"Polygon","id":13117,"arcs":[[-7218,7296,7297,-7266,-7149]]},{"type":"Polygon","id":1093,"arcs":[[7298,7299,7300,7301,7302,-7257,-7179]]},{"type":"Polygon","id":4012,"arcs":[[-6669,7303,7304,7305,7306,-6522,7307]]},{"type":"Polygon","id":48345,"arcs":[[7308,7309,7310,-7095,-7102]]},{"type":"Polygon","id":48101,"arcs":[[-7104,-7183,7311,7312,-7309,-7101]]},{"type":"Polygon","id":1043,"arcs":[[-7166,7313,7314,7315,-7070,-7121]]},{"type":"Polygon","id":48153,"arcs":[[-7311,7316,7317,-7091,-7096]]},{"type":"Polygon","id":48189,"arcs":[[-7097,-7092,-7318,7318,7319]]},{"type":"Polygon","id":48279,"arcs":[[-7320,7320,7321,-7109,-7098]]},{"type":"Polygon","id":45041,"arcs":[[7322,7323,7324,7325,-7278,-7200,-7067,-7147]]},{"type":"Polygon","id":48017,"arcs":[[-7322,7326,-7157,-7010,-7110]]},{"type":"Polygon","id":1133,"arcs":[[-7071,-7316,7327,-7299,-7178]]},{"type":"Polygon","id":45051,"arcs":[[-7253,-7283,7328,7329,7330,-7145,-7006]]},{"type":"Polygon","id":45067,"arcs":[[-7331,7331,7332,-7323,-7146]]},{"type":"Polygon","id":13157,"arcs":[[7333,7334,7335,-7216,-7251]]},{"type":"Polygon","id":40067,"arcs":[[-7226,7336,7337,7338,-7229,-7125]]},{"type":"Polygon","id":13105,"arcs":[[-7256,7339,7340,7341,7342,7343,-7243,-7060]]},{"type":"Polygon","id":13195,"arcs":[[-7344,7344,7345,7346,-7334,-7250,-7198,-7244]]},{"type":"Polygon","id":45079,"arcs":[[7347,7348,7349,-7202,-7184,-7155]]},{"type":"Polygon","id":1009,"arcs":[[-7165,7350,7351,7352,7353,-7314]]},{"type":"Polygon","id":48155,"arcs":[[-7182,-7261,7354,7355,7356,-7312]]},{"type":"Polygon","id":48485,"arcs":[[-7231,7357,7358,-7259,-7140]]},{"type":"Polygon","id":1055,"arcs":[[-7209,7359,7360,-7351,-7164,-7030]]},{"type":"Polygon","id":45063,"arcs":[[7361,7362,7363,7364,-7203,-7350]]},{"type":"Polygon","id":28161,"arcs":[[7365,7366,7367,-7194,-7192]]},{"type":"Polygon","id":5133,"arcs":[[7368,-7232,-7118,-7287]]},{"type":"Polygon","id":45081,"arcs":[[-7365,7369,7370,-7269,-7204]]},{"type":"Polygon","id":13121,"arcs":[[-7298,7371,7372,7373,7374,7375,7376,7377,7378,-7267]]},{"type":"Polygon","id":40095,"arcs":[[7379,7380,7381,-7224,-7228]]},{"type":"Polygon","id":5079,"arcs":[[-7187,7382,7383,7384,-7248]]},{"type":"Polygon","id":45085,"arcs":[[-7326,7385,7386,-7348,-7154,-7279]]},{"type":"Polygon","id":13135,"arcs":[[7387,7388,7389,7390,-7372,-7297,-7217]]},{"type":"Polygon","id":28135,"arcs":[[-7368,7391,7392,7393,-7210,-7214,-7195]]},{"type":"Polygon","id":28013,"arcs":[[-7277,7394,7395,7396,-7366,-7191]]},{"type":"Polygon","id":40013,"arcs":[[7397,7398,7399,7400,-7380,-7227,-7129]]},{"type":"Polygon","id":40023,"arcs":[[-7236,7401,7402,-7398,-7128,-7132]]},{"type":"Polygon","id":5039,"arcs":[[-7247,7403,7404,7405,-7294,-7238]]},{"type":"Polygon","id":48077,"arcs":[[-7339,7406,7407,7408,-7358,-7230]]},{"type":"Polygon","id":13013,"arcs":[[7409,7410,-7388,-7336]]},{"type":"Polygon","id":28011,"arcs":[[7411,7412,7413,7414,-7134,-7212]]},{"type":"Polygon","id":5041,"arcs":[[-7415,7415,7416,-7383,-7186,-7135]]},{"type":"Polygon","id":13233,"arcs":[[-7264,7417,7418,7419,-7206,-7170]]},{"type":"Polygon","id":28095,"arcs":[[-7258,-7303,7420,7421,7422,7423,-7220]]},{"type":"Polygon","id":35005,"arcs":[[-7160,7424,7425,7426,-7291,-7079]]},{"type":"Polygon","id":13067,"arcs":[[-7379,7427,7428,-7262,-7268]]},{"type":"Polygon","id":6065,"arcs":[[7429,7430,7431,-6523,-7307]]},{"type":"Polygon","id":13223,"arcs":[[-7429,7432,7433,7434,-7418,-7263]]},{"type":"Polygon","id":45065,"arcs":[[-7271,7435,7436,7437,-7340,-7255]]},{"type":"Polygon","id":28017,"arcs":[[-7221,-7424,7438,7439,-7395,-7276]]},{"type":"Polygon","id":40085,"arcs":[[-7382,7440,7441,7442,-7337,-7225]]},{"type":"Polygon","id":5025,"arcs":[[-7385,7443,7444,7445,-7404,-7246,-7249]]},{"type":"Polygon","id":1075,"arcs":[[7446,7447,7448,-7421,-7302]]},{"type":"Polygon","id":13221,"arcs":[[-7343,7449,7450,7451,7452,7453,-7346,-7345]]},{"type":"Polygon","id":4013,"arcs":[[-7242,7454,7455,7456,-7304,-6668]]},{"type":"Polygon","id":13059,"arcs":[[-7454,7457,-7335,-7347]]},{"type":"Polygon","id":5057,"arcs":[[7458,7459,7460,7461,-7285,-7290]]},{"type":"Polygon","id":1127,"arcs":[[-7315,-7354,7462,7463,7464,-7300,-7328]]},{"type":"Polygon","id":13317,"arcs":[[7465,7466,7467,7468,-7450,-7342]]},{"type":"Polygon","id":48337,"arcs":[[-7443,7469,7470,7471,-7407,-7338]]},{"type":"Polygon","id":1115,"arcs":[[7472,7473,7474,7475,-7352,-7361]]},{"type":"Polygon","id":28133,"arcs":[[-7394,7476,7477,7478,-7412,-7211]]},{"type":"Polygon","id":13181,"arcs":[[-7438,7479,7480,-7466,-7341]]},{"type":"Polygon","id":45037,"arcs":[[7481,7482,7483,-7436,-7270,-7371]]},{"type":"Polygon","id":13089,"arcs":[[-7391,7484,7485,7486,-7373]]},{"type":"Polygon","id":1015,"arcs":[[7487,-7473,-7360,-7208,7488]]},{"type":"Polygon","id":13219,"arcs":[[-7453,7489,7490,7491,-7410,-7458]]},{"type":"Polygon","id":1029,"arcs":[[-7420,7492,7493,7494,7495,7496,-7489,-7207]]},{"type":"Polygon","id":48387,"arcs":[[-7235,7497,7498,7499,7500,7501,7502,-7402]]},{"type":"Polygon","id":5099,"arcs":[[-7296,7503,7504,7505,-7459,-7289]]},{"type":"Polygon","id":48181,"arcs":[[-7401,7506,7507,7508,7509,-7441,-7381]]},{"type":"Polygon","id":48097,"arcs":[[-7510,7510,7511,-7470,-7442]]},{"type":"Polygon","id":5081,"arcs":[[-7286,-7462,7512,7513,-7233,-7369]]},{"type":"Polygon","id":45027,"arcs":[[7514,7515,7516,7517,-7386,-7325]]},{"type":"Polygon","id":6059,"arcs":[[-6524,-7432,7518,7519,-7056]]},{"type":"Polygon","id":48277,"arcs":[[-7503,7520,7521,-7399,-7403]]},{"type":"Polygon","id":13297,"arcs":[[-7492,7522,7523,7524,-7389,-7411]]},{"type":"Polygon","id":1057,"arcs":[[-7465,7525,7526,-7447,-7301]]},{"type":"Polygon","id":13143,"arcs":[[7527,-7493,-7419,-7435]]},{"type":"Polygon","id":28043,"arcs":[[-7397,7528,7529,7530,7531,-7392,-7367]]},{"type":"Polygon","id":45089,"arcs":[[-7333,7532,7533,-7515,-7324]]},{"type":"Polygon","id":48147,"arcs":[[-7522,7534,7535,7536,-7507,-7400]]},{"type":"Polygon","id":45017,"arcs":[[-7387,-7518,7537,-7362,-7349]]},{"type":"Polygon","id":45003,"arcs":[[7538,7539,7540,7541,-7482,-7370,-7364]]},{"type":"Polygon","id":1073,"arcs":[[-7476,7542,7543,7544,-7463,-7353]]},{"type":"Polygon","id":48269,"arcs":[[-7357,7545,7546,7547,-7313]]},{"type":"Polygon","id":48275,"arcs":[[7548,7549,-7546,-7356]]},{"type":"Polygon","id":48009,"arcs":[[-7409,7550,7551,7552,-7359]]},{"type":"Polygon","id":48125,"arcs":[[-7548,7553,7554,-7310]]},{"type":"Polygon","id":48107,"arcs":[[-7555,7555,7556,-7317]]},{"type":"Polygon","id":48023,"arcs":[[-7553,7557,-7549,-7355,-7260]]},{"type":"Polygon","id":48303,"arcs":[[-7557,7558,7559,-7319]]},{"type":"Polygon","id":48079,"arcs":[[7560,7561,7562,-7158,-7327]]},{"type":"Polygon","id":48219,"arcs":[[-7560,7563,-7561,-7321]]},{"type":"Polygon","id":5103,"arcs":[[-7406,7564,7565,7566,-7504,-7295]]},{"type":"Polygon","id":13211,"arcs":[[7567,7568,7569,7570,-7523,-7491]]},{"type":"Polygon","id":13045,"arcs":[[-7377,7571,7572,7573,-7494,-7528,-7434,7574]]},{"type":"Polygon","id":28083,"arcs":[[-7532,7575,7576,7577,-7477,-7393]]},{"type":"Polygon","id":28025,"arcs":[[-7423,7578,7579,7580,-7439]]},{"type":"Polygon","id":13097,"arcs":[[-7378,-7575,-7433,-7428]]},{"type":"Polygon","id":5013,"arcs":[[7581,7582,-7565,-7405,-7446]]},{"type":"Polygon","id":5043,"arcs":[[-7417,7583,7584,7585,-7444,-7384]]},{"type":"Polygon","id":13247,"arcs":[[7586,-7485,-7390,-7525,7587]]},{"type":"Polygon","id":45043,"arcs":[[-7330,7588,7589,7590,-7533,-7332]]},{"type":"Polygon","id":4011,"arcs":[[-7177,7591,7592,7593,7594,-5783]]},{"type":"Polygon","id":13133,"arcs":[[7595,7596,7597,-7568,-7490,-7452]]},{"type":"Polygon","id":28087,"arcs":[[-7449,7598,7599,7600,-7579,-7422]]},{"type":"Polygon","id":13217,"arcs":[[-7571,7601,7602,7603,-7588,-7524]]},{"type":"Polygon","id":28155,"arcs":[[-7581,7604,7605,7606,-7529,-7396,-7440]]},{"type":"Polygon","id":13265,"arcs":[[7607,7608,7609,-7596,-7451,-7469]]},{"type":"Polygon","id":48037,"arcs":[[-7514,7610,7611,7612,-7498,-7234]]},{"type":"Polygon","id":45075,"arcs":[[-7517,7613,7614,7615,7616,7617,-7539,-7363,-7538]]},{"type":"Polygon","id":5011,"arcs":[[-7586,7618,7619,-7582,-7445]]},{"type":"Polygon","id":13073,"arcs":[[-7437,-7484,7620,7621,-7480]]},{"type":"Polygon","id":1121,"arcs":[[-7488,-7497,7622,7623,7624,-7474]]},{"type":"Polygon","id":28097,"arcs":[[-7607,7625,7626,7627,-7530]]},{"type":"Polygon","id":28015,"arcs":[[-7628,7628,7629,-7576,-7531]]},{"type":"Polygon","id":13189,"arcs":[[-7481,-7622,7630,7631,7632,7633,-7467]]},{"type":"Polygon","id":4009,"arcs":[[-7595,7634,7635,7636,-7240,-5732,-5784]]},{"type":"Polygon","id":13063,"arcs":[[-7487,7637,7638,7639,-7374]]},{"type":"Polygon","id":13151,"arcs":[[-7587,-7604,7640,7641,-7638,-7486]]},{"type":"Polygon","id":5091,"arcs":[[7642,7643,7644,7645,-7611,-7513,-7461]]},{"type":"Polygon","id":13301,"arcs":[[7646,7647,7648,-7609,-7608,-7468,-7634]]},{"type":"Polygon","id":1125,"arcs":[[-7464,-7545,7649,7650,7651,7652,-7526]]},{"type":"Polygon","id":35025,"arcs":[[-7563,7653,7654,7655,7656,7657,7658,-7425,-7159]]},{"type":"Polygon","id":28105,"arcs":[[-7601,7659,7660,7661,-7605,-7580]]},{"type":"Polygon","id":5017,"arcs":[[-7414,7662,7663,7664,7665,7666,7667,-7584,-7416]]},{"type":"Polygon","id":13113,"arcs":[[7668,7669,-7375,-7640]]},{"type":"Polygon","id":1117,"arcs":[[-7625,7670,7671,7672,-7543,-7475]]},{"type":"Polygon","id":13245,"arcs":[[7673,7674,7675,-7631,-7621,-7483,-7542]]},{"type":"Polygon","id":1107,"arcs":[[-7527,-7653,7676,7677,7678,-7599,-7448]]},{"type":"Polygon","id":28019,"arcs":[[-7662,7679,7680,-7626,-7606]]},{"type":"Polygon","id":28151,"arcs":[[-7479,7681,7682,7683,-7663,-7413]]},{"type":"Polygon","id":13159,"arcs":[[7684,7685,7686,7687,-7602,-7570]]},{"type":"Polygon","id":13077,"arcs":[[-7670,7688,7689,7690,7691,-7572,-7376]]},{"type":"Polygon","id":45015,"arcs":[[-7591,7692,7693,7694,7695,-7614,-7516,-7534]]},{"type":"Polygon","id":6073,"arcs":[[7696,7697,-7519,-7431]]},{"type":"Polygon","id":1111,"arcs":[[-7574,7698,7699,7700,7701,7702,-7495]]},{"type":"Polygon","id":1027,"arcs":[[-7703,7703,7704,-7623,-7496]]},{"type":"Polygon","id":48119,"arcs":[[-7502,7705,7706,7707,-7535,-7521]]},{"type":"Polygon","id":45011,"arcs":[[7708,7709,7710,-7540,-7618]]},{"type":"Polygon","id":13237,"arcs":[[7711,7712,7713,-7685,-7569,-7598]]},{"type":"Polygon","id":35051,"arcs":[[-7293,7714,7715,7716,7717,-7175,-7173]]},{"type":"Polygon","id":5073,"arcs":[[-7506,7718,7719,7720,-7643,-7460]]},{"type":"Polygon","id":13141,"arcs":[[-7649,7721,7722,7723,-7712,-7597,-7610]]},{"type":"Polygon","id":4027,"arcs":[[-7457,7724,7725,7726,-7305]]},{"type":"Polygon","id":4021,"arcs":[[-7241,-7637,7727,-7455]]},{"type":"Polygon","id":48237,"arcs":[[-7472,7728,7729,7730,7731,-7551,-7408]]},{"type":"Polygon","id":5027,"arcs":[[-7567,7732,7733,7734,-7719,-7505]]},{"type":"Polygon","id":13035,"arcs":[[-7688,7735,7736,7737,-7641,-7603]]},{"type":"Polygon","id":45009,"arcs":[[7738,7739,-7709,-7617]]},{"type":"Polygon","id":6025,"arcs":[[-7727,7740,-7697,-7430,-7306]]},{"type":"Polygon","id":48497,"arcs":[[-7512,7741,7742,7743,-7729,-7471]]},{"type":"Polygon","id":48121,"arcs":[[7744,7745,7746,-7742,-7511,-7509]]},{"type":"Polygon","id":13149,"arcs":[[-7692,7747,-7699,-7573]]},{"type":"Polygon","id":48231,"arcs":[[7748,7749,7750,7751,7752,-7536,-7708,7753]]},{"type":"Polygon","id":48085,"arcs":[[-7537,-7753,7754,7755,-7745,-7508]]},{"type":"Polygon","id":48263,"arcs":[[7756,7757,7758,7759,-7554]]},{"type":"Polygon","id":48433,"arcs":[[7760,7761,7762,-7757,-7547]]},{"type":"Polygon","id":48449,"arcs":[[7763,7764,7765,-7500]]},{"type":"Polygon","id":48169,"arcs":[[-7760,7766,7767,7768,-7556]]},{"type":"Polygon","id":48447,"arcs":[[7769,7770,7771,7772,-7558]]},{"type":"Polygon","id":48503,"arcs":[[-7732,7773,7774,-7770,-7552]]},{"type":"Polygon","id":48207,"arcs":[[-7773,7775,7776,-7761,-7550]]},{"type":"Polygon","id":5003,"arcs":[[-7668,7777,7778,-7619,-7585]]},{"type":"Polygon","id":48305,"arcs":[[-7769,7779,7780,7781,-7559]]},{"type":"Polygon","id":35035,"arcs":[[-7427,7782,7783,7784,7785,7786,-7715,-7292]]},{"type":"Polygon","id":48501,"arcs":[[7787,7788,-7654,-7562]]},{"type":"Polygon","id":48445,"arcs":[[-7782,7789,7790,-7788,-7564]]},{"type":"Polygon","id":48159,"arcs":[[-7766,7791,7792,7793,-7706,-7501]]},{"type":"Polygon","id":5139,"arcs":[[-7583,-7620,-7779,7794,7795,-7733,-7566]]},{"type":"Polygon","id":48223,"arcs":[[-7794,7796,7797,-7754,-7707]]},{"type":"Polygon","id":48343,"arcs":[[-7613,7798,7799,7800,7801,-7764,-7499]]},{"type":"Polygon","id":28051,"arcs":[[-7630,7802,7803,7804,-7577]]},{"type":"Polygon","id":13255,"arcs":[[-7738,7805,7806,7807,-7689,-7669,-7639,-7642]]},{"type":"Polygon","id":45035,"arcs":[[-7696,7808,7809,-7615]]},{"type":"Polygon","id":28053,"arcs":[[-7578,-7805,7810,7811,-7682,-7478]]},{"type":"Polygon","id":13125,"arcs":[[7812,7813,7814,-7722,-7648]]},{"type":"Polygon","id":13163,"arcs":[[-7675,7815,7816,7817,7818,-7813,-7647,-7633,-7632,-7676]]},{"type":"Polygon","id":48067,"arcs":[[-7646,7819,7820,-7799,-7612]]},{"type":"Polygon","id":13033,"arcs":[[-7541,-7711,7821,7822,7823,7824,-7816,-7674]]},{"type":"Polygon","id":28103,"arcs":[[-7679,7825,7826,7827,-7660,-7600]]},{"type":"Polygon","id":28159,"arcs":[[-7828,7828,7829,7830,-7680,-7661]]},{"type":"Polygon","id":28007,"arcs":[[-7831,7831,7832,-7803,-7629,-7627,-7681]]},{"type":"Polygon","id":1007,"arcs":[[7833,7834,7835,-7650,-7544,-7673]]},{"type":"Polygon","id":13303,"arcs":[[-7815,-7814,-7819,7836,7837,7838,-7723]]},{"type":"Polygon","id":13199,"arcs":[[-7808,7839,7840,7841,7842,7843,-7690]]},{"type":"Polygon","id":13285,"arcs":[[-7844,7844,7845,-7700,-7748,-7691]]},{"type":"Polygon","id":35017,"arcs":[[-7718,7846,7847,-7592,-7176]]},{"type":"Polygon","id":13231,"arcs":[[7848,7849,-7840,-7807]]},{"type":"Polygon","id":13171,"arcs":[[7850,7851,-7849,-7806,-7737]]},{"type":"Polygon","id":13207,"arcs":[[-7687,7852,7853,7854,7855,-7851,-7736]]},{"type":"Polygon","id":13009,"arcs":[[-7724,-7839,7856,7857,-7713]]},{"type":"Polygon","id":13169,"arcs":[[-7858,7858,7859,7860,-7853,-7686,-7714]]},{"type":"Polygon","id":45029,"arcs":[[-7810,7861,7862,7863,7864,-7739,-7616]]},{"type":"Polygon","id":45005,"arcs":[[-7740,7865,7866,7867,-7822,-7710]]},{"type":"Polygon","id":1063,"arcs":[[7868,7869,7870,-7677,-7652]]},{"type":"Polygon","id":1017,"arcs":[[-7846,7871,7872,7873,-7701]]},{"type":"Polygon","id":1123,"arcs":[[-7874,7874,7875,7876,7877,-7704,-7702]]},{"type":"Polygon","id":1037,"arcs":[[-7878,7878,7879,-7671,-7624,-7705]]},{"type":"Polygon","id":28125,"arcs":[[-7812,7880,7881,-7683]]},{"type":"Polygon","id":48063,"arcs":[[-7802,7882,7883,-7792,-7765]]},{"type":"Polygon","id":1021,"arcs":[[-7880,7884,7885,7886,7887,-7834,-7672]]},{"type":"Polygon","id":35013,"arcs":[[7888,7889,7890,-7716,-7787]]},{"type":"Polygon","id":13251,"arcs":[[-7867,7891,7892,7893,7894,7895,-7823,-7868]]},{"type":"Polygon","id":45049,"arcs":[[7896,7897,7898,-7892,-7866,-7865]]},{"type":"Polygon","id":28163,"arcs":[[7899,7900,7901,7902,7903,-7881,-7811,-7804]]},{"type":"Polygon","id":22017,"arcs":[[7904,7905,7906,7907,7908,-7820,-7645,7909]]},{"type":"Polygon","id":22015,"arcs":[[7910,7911,7912,-7910,-7644,-7721]]},{"type":"Polygon","id":22119,"arcs":[[7913,-7911,-7720,-7735,7914]]},{"type":"Polygon","id":22027,"arcs":[[7915,7916,-7915,-7734,-7796,7917]]},{"type":"Polygon","id":22111,"arcs":[[7918,7919,-7918,-7795,7920]]},{"type":"Polygon","id":48499,"arcs":[[7921,7922,7923,7924,7925,-7797,-7793,-7884]]},{"type":"Polygon","id":28055,"arcs":[[-7882,-7904,7926,7927,-7664,-7684]]},{"type":"Polygon","id":13319,"arcs":[[-7838,7928,7929,7930,-7859,-7857]]},{"type":"Polygon","id":22067,"arcs":[[-7667,7931,7932,7933,-7921,-7778]]},{"type":"Polygon","id":1065,"arcs":[[-7836,7934,7935,-7869,-7651]]},{"type":"Polygon","id":22123,"arcs":[[7936,7937,-7932,-7666]]},{"type":"Polygon","id":48363,"arcs":[[7938,7939,7940,7941,7942,-7774,-7731]]},{"type":"Polygon","id":22035,"arcs":[[7943,7944,7945,-7937,-7665,-7928]]},{"type":"Polygon","id":48367,"arcs":[[7946,7947,7948,-7939,-7730,-7744]]},{"type":"Polygon","id":13293,"arcs":[[-7852,-7856,7949,7950,7951,-7841,-7850]]},{"type":"Polygon","id":1119,"arcs":[[7952,7953,7954,7955,-7826,-7678,-7871]]},{"type":"Polygon","id":48439,"arcs":[[-7743,-7747,7956,7957,7958,-7947]]},{"type":"Polygon","id":48113,"arcs":[[-7756,7959,7960,7961,-7957,-7746]]},{"type":"Polygon","id":48397,"arcs":[[-7752,7962,-7960,-7755]]},{"type":"Polygon","id":48379,"arcs":[[-7926,7963,-7749,-7798]]},{"type":"Polygon","id":48415,"arcs":[[7964,7965,7966,-7767,-7759]]},{"type":"Polygon","id":35015,"arcs":[[-7659,7967,7968,7969,-7783,-7426]]},{"type":"Polygon","id":48151,"arcs":[[-7763,7970,7971,-7965,-7758]]},{"type":"Polygon","id":48033,"arcs":[[-7967,7972,7973,7974,-7780,-7768]]},{"type":"Polygon","id":48115,"arcs":[[-7975,7975,7976,-7790,-7781]]},{"type":"Polygon","id":48165,"arcs":[[-7791,-7977,7977,7978,-7655,-7789]]},{"type":"Polygon","id":48253,"arcs":[[-7777,7979,7980,7981,-7971,-7762]]},{"type":"Polygon","id":48417,"arcs":[[7982,7983,7984,-7980,-7776,-7772]]},{"type":"Polygon","id":48429,"arcs":[[-7943,7985,-7983,-7771,-7775]]},{"type":"Polygon","id":13165,"arcs":[[-7896,7986,7987,-7824]]},{"type":"Polygon","id":13021,"arcs":[[7988,7989,7990,7991,-7854,-7861]]},{"type":"Polygon","id":28099,"arcs":[[7992,7993,7994,-7830]]},{"type":"Polygon","id":28079,"arcs":[[-7995,7995,7996,7997,-7832]]},{"type":"Polygon","id":28069,"arcs":[[-7956,7998,-7993,-7829,-7827]]},{"type":"Polygon","id":48459,"arcs":[[-7801,7999,8000,8001,8002,-7922,-7883]]},{"type":"Polygon","id":13289,"arcs":[[8003,8004,-7990,-7989,-7860,-7931]]},{"type":"Polygon","id":13263,"arcs":[[8005,8006,8007,8008,8009,-7842,-7952]]},{"type":"Polygon","id":48315,"arcs":[[-7909,8010,-8000,-7800,-7821]]},{"type":"Polygon","id":28089,"arcs":[[-7998,8011,8012,8013,-7901,-7900,-7833]]},{"type":"Polygon","id":1105,"arcs":[[-7888,8014,8015,-7935,-7835]]},{"type":"Polygon","id":13145,"arcs":[[-8010,8016,8017,-7872,-7845,-7843]]},{"type":"Polygon","id":13079,"arcs":[[-7992,8018,8019,8020,-7950,-7855]]},{"type":"Polygon","id":48257,"arcs":[[8021,8022,8023,-7961,-7963,-7751]]},{"type":"Polygon","id":13107,"arcs":[[-7825,-7988,8024,8025,8026,8027,8028,8029,-7817]]},{"type":"Polygon","id":48467,"arcs":[[-7964,-7925,8030,8031,-8022,-7750]]},{"type":"Polygon","id":13167,"arcs":[[-8030,8032,-7929,-7837,-7818]]},{"type":"Polygon","id":48203,"arcs":[[-7908,8033,8034,8035,-8001,-8011]]},{"type":"Polygon","id":35023,"arcs":[[8036,8037,8038,-7593,-7848]]},{"type":"Polygon","id":1051,"arcs":[[8039,8040,8041,-7885,-7879,-7877]]},{"type":"Polygon","id":22061,"arcs":[[8042,8043,8044,-7916,-7920]]},{"type":"Polygon","id":45053,"arcs":[[8045,8046,8047,8048,8049,8050,8051,8052,8053,8054,8055,8056,-7898]]},{"type":"Polygon","id":13269,"arcs":[[-8021,8057,8058,8059,8060,-8006,-7951]]},{"type":"Polygon","id":1081,"arcs":[[-8018,8061,8062,8063,-7875,-7873]]},{"type":"Polygon","id":1047,"arcs":[[8064,8065,8066,8067,-8015,-7887]]},{"type":"Polygon","id":22073,"arcs":[[8068,8069,8070,-8043,-7919,-7934]]},{"type":"Polygon","id":13175,"arcs":[[8071,8072,8073,-7930,-8033,8074]]},{"type":"Polygon","id":1001,"arcs":[[-8042,8075,8076,-8065,-7886]]},{"type":"MultiPolygon","id":45013,"arcs":[[[8077]],[[-8053,-8052,8078]],[[8079]],[[8080]],[[8081,-8046,-7897,-7864]]]},{"type":"Polygon","id":13225,"arcs":[[8082,-8058,-8020,8083]]},{"type":"Polygon","id":13153,"arcs":[[-8005,8084,8085,8086,8087,-8084,-8019,-7991]]},{"type":"Polygon","id":48423,"arcs":[[-7923,-8003,8088,8089,8090,8091,-8031,-7924]]},{"type":"Polygon","id":22083,"arcs":[[-7946,8092,8093,8094,-8069,-7933,-7938]]},{"type":"Polygon","id":48183,"arcs":[[-8036,8095,-8089,-8002]]},{"type":"Polygon","id":13031,"arcs":[[-7894,8096,8097,8098,8099,-8025,-7987,-7895]]},{"type":"Polygon","id":28123,"arcs":[[-7997,8100,8101,8102,-8012]]},{"type":"Polygon","id":28149,"arcs":[[8103,8104,8105,8106,8107,8108,-7944,-7927,-7903]]},{"type":"Polygon","id":13215,"arcs":[[-8009,8109,8110,-8062,-8017]]},{"type":"Polygon","id":35029,"arcs":[[-7891,8111,-8037,-7847,-7717]]},{"type":"Polygon","id":13103,"arcs":[[-8057,-8056,8112,8113,-8097,-7893,-7899]]},{"type":"Polygon","id":1087,"arcs":[[8114,8115,8116,8117,-8040,-7876,-8064]]},{"type":"Polygon","id":28121,"arcs":[[-8103,8118,8119,8120,-8013]]},{"type":"Polygon","id":22013,"arcs":[[-8045,8121,8122,8123,8124,-7912,-7914,-7917]]},{"type":"Polygon","id":13023,"arcs":[[8125,8126,-8085,-8004,-8074]]},{"type":"Polygon","id":28075,"arcs":[[-7955,8127,8128,8129,-7999]]},{"type":"Polygon","id":28101,"arcs":[[-8130,8130,-8101,-7996,-7994]]},{"type":"Polygon","id":28049,"arcs":[[-8121,8131,8132,-8104,-7902,-8014]]},{"type":"Polygon","id":22065,"arcs":[[8133,8134,-8093,-7945,-8109]]},{"type":"Polygon","id":13197,"arcs":[[8135,8136,8137,8138,8139,-8007,-8061]]},{"type":"Polygon","id":13043,"arcs":[[8140,8141,-8026,-8100]]},{"type":"Polygon","id":48221,"arcs":[[8142,8143,8144,-7940,-7949]]},{"type":"Polygon","id":48251,"arcs":[[8145,8146,8147,8148,-8143,-7948,-7959]]},{"type":"Polygon","id":48139,"arcs":[[-7962,-8024,8149,8150,8151,-8146,-7958]]},{"type":"Polygon","id":1091,"arcs":[[-7936,-8016,-8068,8152,8153,8154,-7953,-7870]]},{"type":"Polygon","id":13053,"arcs":[[-8140,8155,8156,-8110,-8008]]},{"type":"Polygon","id":48227,"arcs":[[8157,8158,8159,8160,-7974]]},{"type":"Polygon","id":48335,"arcs":[[8161,8162,8163,-8158,-7973,-7966]]},{"type":"Polygon","id":48353,"arcs":[[8164,8165,8166,-8162,-7972]]},{"type":"Polygon","id":48317,"arcs":[[-8161,8167,8168,8169,-7978,-7976]]},{"type":"Polygon","id":48003,"arcs":[[-8170,8170,8171,8172,-7656,-7979]]},{"type":"Polygon","id":13193,"arcs":[[-8083,-8088,8173,8174,8175,-8059]]},{"type":"Polygon","id":48441,"arcs":[[8176,8177,8178,-8165,-7982]]},{"type":"Polygon","id":48133,"arcs":[[-7942,8179,8180,8181,8182,-7984,-7986]]},{"type":"Polygon","id":48143,"arcs":[[-7941,-8145,8183,8184,8185,8186,-8180]]},{"type":"Polygon","id":48059,"arcs":[[-8183,8187,8188,-8177,-7981,-7985]]},{"type":"Polygon","id":4019,"arcs":[[-7636,8189,8190,8191,-7725,-7456,-7728]]},{"type":"Polygon","id":13283,"arcs":[[8192,8193,-8075,-8029]]},{"type":"Polygon","id":1113,"arcs":[[-8111,-8157,8194,8195,8196,-8115,-8063]]},{"type":"Polygon","id":1101,"arcs":[[-8118,-8117,8197,8198,8199,8200,-8076,-8041]]},{"type":"Polygon","id":22049,"arcs":[[-8071,8201,8202,-8122,-8044]]},{"type":"Polygon","id":13091,"arcs":[[8203,8204,8205,8206,-8126,-8073]]},{"type":"Polygon","id":13249,"arcs":[[-8176,8207,-8136,-8060]]},{"type":"Polygon","id":4003,"arcs":[[-7594,-8039,8208,8209,-8190,-7635]]},{"type":"Polygon","id":1085,"arcs":[[-8201,8210,8211,8212,-8066,-8077]]},{"type":"Polygon","id":48401,"arcs":[[8213,8214,8215,8216,-8090,-8096,-8035]]},{"type":"Polygon","id":22041,"arcs":[[-8135,8217,8218,8219,-8094]]},{"type":"Polygon","id":13235,"arcs":[[-8127,-8207,8220,8221,-8086]]},{"type":"Polygon","id":48365,"arcs":[[-7907,8222,8223,-8214,-8034]]},{"type":"Polygon","id":48213,"arcs":[[-8032,-8092,8224,8225,8226,8227,-8150,-8023]]},{"type":"Polygon","id":13209,"arcs":[[8228,8229,-8193,8230]]},{"type":"Polygon","id":13279,"arcs":[[-8028,8231,8232,8233,-8231]]},{"type":"Polygon","id":22031,"arcs":[[8234,8235,8236,8237,-8223,-7906]]},{"type":"Polygon","id":48349,"arcs":[[8238,8239,8240,-8151,-8228]]},{"type":"Polygon","id":48425,"arcs":[[-8149,8241,-8184,-8144]]},{"type":"Polygon","id":13267,"arcs":[[8242,8243,8244,8245,8246,-8232,-8027,-8142]]},{"type":"Polygon","id":1023,"arcs":[[8247,8248,8249,8250,-8128,-7954,-8155]]},{"type":"Polygon","id":13309,"arcs":[[-8230,8251,8252,-8204,-8072,-8194]]},{"type":"Polygon","id":1011,"arcs":[[-8197,8253,8254,-8198,-8116]]},{"type":"Polygon","id":13093,"arcs":[[-8222,8255,8256,8257,8258,-8174,-8087]]},{"type":"Polygon","id":13109,"arcs":[[-8099,8259,8260,-8243,-8141]]},{"type":"Polygon","id":22021,"arcs":[[-8095,-8220,8261,8262,8263,-8202,-8070]]},{"type":"Polygon","id":1131,"arcs":[[-8067,-8213,8264,8265,8266,-8153]]},{"type":"Polygon","id":48217,"arcs":[[-8241,8267,8268,8269,-8147,-8152]]},{"type":"Polygon","id":48093,"arcs":[[8270,8271,8272,-8181,-8187]]},{"type":"Polygon","id":22107,"arcs":[[-8108,8273,-8106,8274,8275,8276,8277,8278,-8218,-8134]]},{"type":"Polygon","id":13029,"arcs":[[8279,8280,8281,-8260,-8098,-8114]]},{"type":"Polygon","id":22081,"arcs":[[-7913,-8125,8282,-8235,-7905]]},{"type":"MultiPolygon","id":13051,"arcs":[[[8283]],[[8284]],[[8285,-8280,-8113,-8055]]]},{"type":"Polygon","id":13307,"arcs":[[8286,8287,8288,8289,-8138]]},{"type":"Polygon","id":13259,"arcs":[[-8290,8290,8291,8292,-8195,-8156,-8139]]},{"type":"Polygon","id":13261,"arcs":[[-8259,-8258,8293,8294,8295,-8287,-8137,-8208,-8175]]},{"type":"Polygon","id":28023,"arcs":[[-8251,8296,8297,-8129]]},{"type":"Polygon","id":28021,"arcs":[[-8133,8298,8299,-8275,-8105]]},{"type":"Polygon","id":28061,"arcs":[[-8298,8300,8301,8302,-8131]]},{"type":"Polygon","id":28129,"arcs":[[-8303,8303,8304,8305,-8119,-8102]]},{"type":"Polygon","id":48035,"arcs":[[-8148,-8270,8306,8307,8308,-8185,-8242]]},{"type":"Polygon","id":13271,"arcs":[[8309,8310,8311,8312,-8205,-8253]]},{"type":"Polygon","id":22127,"arcs":[[-8203,-8264,8313,8314,8315,-8123]]},{"type":"Polygon","id":22069,"arcs":[[-8316,8316,8317,8318,8319,-8236,-8283,-8124]]},{"type":"Polygon","id":1005,"arcs":[[-8293,8320,8321,8322,8323,8324,8325,-8254,-8196]]},{"type":"Polygon","id":48073,"arcs":[[-8217,8326,8327,8328,8329,-8225,-8091]]},{"type":"Polygon","id":13315,"arcs":[[-8206,-8313,8330,8331,8332,-8256,-8221]]},{"type":"MultiPolygon","id":13179,"arcs":[[[8333]],[[8334,8335,-8244,-8261,-8282,8336]]]},{"type":"Polygon","id":48431,"arcs":[[-8164,8337,8338,8339,8340,-8159]]},{"type":"Polygon","id":48173,"arcs":[[-8341,8341,8342,-8168,-8160]]},{"type":"Polygon","id":48329,"arcs":[[-8343,8343,8344,-8171,-8169]]},{"type":"Polygon","id":48135,"arcs":[[-8345,8345,8346,8347,8348,-8172]]},{"type":"Polygon","id":48495,"arcs":[[-8349,8349,8350,-7657,-8173]]},{"type":"Polygon","id":48081,"arcs":[[-8167,8351,8352,-8338,-8163]]},{"type":"Polygon","id":48001,"arcs":[[8353,8354,8355,-8226,-8330]]},{"type":"Polygon","id":48083,"arcs":[[-8189,8356,8357,8358,8359,-8178]]},{"type":"Polygon","id":48399,"arcs":[[-8360,8360,8361,-8352,-8166,-8179]]},{"type":"Polygon","id":48049,"arcs":[[-8273,8362,8363,8364,-8357,-8188,-8182]]},{"type":"Polygon","id":1109,"arcs":[[-8326,-8325,8365,8366,8367,-8199,-8255]]},{"type":"Polygon","id":1041,"arcs":[[-8368,8368,8369,8370,-8211,-8200]]},{"type":"Polygon","id":28029,"arcs":[[8371,8372,8373,8374,-8299,-8132]]},{"type":"Polygon","id":28127,"arcs":[[-8306,8375,8376,8377,-8372,-8120]]},{"type":"Polygon","id":13081,"arcs":[[-8333,8378,8379,8380,-8294,-8257]]},{"type":"Polygon","id":48193,"arcs":[[8381,-8271,-8186,-8309,8382,8383]]},{"type":"Polygon","id":13183,"arcs":[[8384,8385,-8245,-8336]]},{"type":"Polygon","id":48161,"arcs":[[8386,8387,-8239,-8227,-8356]]},{"type":"Polygon","id":48109,"arcs":[[8388,8389,8390,-7784,-7970]]},{"type":"Polygon","id":48229,"arcs":[[8391,8392,8393,-7785,-8391]]},{"type":"Polygon","id":48141,"arcs":[[8394,-7889,-7786,-8394]]},{"type":"Polygon","id":48301,"arcs":[[8395,8396,-7968,-7658,-8351]]},{"type":"Polygon","id":48389,"arcs":[[8397,8398,8399,-8389,-7969,-8397]]},{"type":"Polygon","id":13239,"arcs":[[8400,8401,-8321,-8292]]},{"type":"Polygon","id":1025,"arcs":[[-8267,8402,8403,8404,-8248,-8154]]},{"type":"Polygon","id":48419,"arcs":[[8405,8406,8407,8408,-8215,-8224,-8238]]},{"type":"Polygon","id":22025,"arcs":[[-8279,8409,8410,8411,-8262,-8219]]},{"type":"Polygon","id":13161,"arcs":[[8412,8413,8414,-8310,-8252,-8229,-8234]]},{"type":"Polygon","id":13273,"arcs":[[8415,8416,8417,8418,-8288,-8296]]},{"type":"Polygon","id":13001,"arcs":[[-8247,8419,8420,8421,-8413,-8233]]},{"type":"Polygon","id":1013,"arcs":[[-8371,8422,8423,8424,-8265,-8212]]},{"type":"Polygon","id":13243,"arcs":[[-8289,-8419,8425,8426,-8401,-8291]]},{"type":"Polygon","id":22059,"arcs":[[-8412,8427,8428,8429,-8314,-8263]]},{"type":"Polygon","id":13177,"arcs":[[-8381,8430,8431,-8416,-8295]]},{"type":"Polygon","id":28153,"arcs":[[8432,8433,8434,-8301,-8297,-8250,8435]]},{"type":"Polygon","id":28063,"arcs":[[-8375,8436,8437,8438,-8276,-8300]]},{"type":"Polygon","id":48309,"arcs":[[8439,-8307,-8269,8440,8441,8442]]},{"type":"Polygon","id":13287,"arcs":[[8443,8444,8445,8446,-8379,-8332]]},{"type":"Polygon","id":13017,"arcs":[[-8312,8447,8448,-8444,-8331]]},{"type":"Polygon","id":13321,"arcs":[[-8380,-8447,8449,8450,8451,8452,-8431]]},{"type":"Polygon","id":48347,"arcs":[[-8409,8453,8454,-8327,-8216]]},{"type":"Polygon","id":22085,"arcs":[[-8320,8455,8456,8457,-8406,-8237]]},{"type":"Polygon","id":13305,"arcs":[[-8246,-8386,8458,8459,8460,8461,-8420]]},{"type":"Polygon","id":1099,"arcs":[[-8425,8462,8463,8464,-8403,-8266]]},{"type":"Polygon","id":28067,"arcs":[[8465,8466,8467,-8304,-8302,-8435]]},{"type":"Polygon","id":48293,"arcs":[[8468,-8441,-8268,-8240,-8388,8469,8470]]},{"type":"Polygon","id":13069,"arcs":[[8471,8472,8473,8474,8475,-8448,-8311,-8415]]},{"type":"Polygon","id":22043,"arcs":[[-8430,8476,-8317,-8315]]},{"type":"Polygon","id":28031,"arcs":[[-8468,8477,8478,8479,-8376,-8305]]},{"type":"Polygon","id":13061,"arcs":[[-8427,8480,8481,8482,-8322,-8402]]},{"type":"Polygon","id":1067,"arcs":[[-8483,8483,8484,8485,-8323]]},{"type":"Polygon","id":28065,"arcs":[[8486,8487,8488,-8377,-8480]]},{"type":"Polygon","id":13155,"arcs":[[-8476,8489,8490,-8445,-8449]]},{"type":"Polygon","id":22029,"arcs":[[-8278,8491,8492,8493,8494,8495,8496,-8410]]},{"type":"Polygon","id":28077,"arcs":[[8497,8498,8499,-8373,-8378,-8489]]},{"type":"Polygon","id":1035,"arcs":[[8500,8501,-8463,-8424]]},{"type":"Polygon","id":28001,"arcs":[[8502,8503,-8492,-8277,-8439]]},{"type":"Polygon","id":4023,"arcs":[[-8210,8504,-8191]]},{"type":"Polygon","id":48333,"arcs":[[8505,8506,-8363,-8272,-8382]]},{"type":"Polygon","id":28085,"arcs":[[8507,8508,8509,8510,-8437,-8374,-8500]]},{"type":"Polygon","id":13005,"arcs":[[8511,8512,-8472,-8414,-8422]]},{"type":"Polygon","id":48099,"arcs":[[8513,-8383,-8308,-8440,8514]]},{"type":"Polygon","id":48451,"arcs":[[-8362,8515,8516,8517,8518,-8339,-8353]]},{"type":"Polygon","id":1129,"arcs":[[-8405,8519,8520,8521,-8436,-8249]]},{"type":"MultiPolygon","id":13191,"arcs":[[[8522]],[[8523,8524,-8459,-8385,-8335]]]},{"type":"Polygon","id":48289,"arcs":[[8525,8526,8527,-8470,-8387,-8355]]},{"type":"Polygon","id":48461,"arcs":[[8528,8529,8530,-8346,-8344]]},{"type":"Polygon","id":48103,"arcs":[[-8531,8531,8532,8533,-8347]]},{"type":"Polygon","id":48405,"arcs":[[8534,8535,8536,-8454,-8408]]},{"type":"Polygon","id":48383,"arcs":[[-8340,-8519,8537,8538,-8529,-8342]]},{"type":"Polygon","id":48475,"arcs":[[-8396,-8350,-8348,-8534,8539,-8398]]},{"type":"Polygon","id":13095,"arcs":[[-8453,8540,8541,8542,-8417,-8432]]},{"type":"Polygon","id":13037,"arcs":[[-8418,-8543,8543,8544,-8481,-8426]]},{"type":"Polygon","id":1045,"arcs":[[-8486,8545,8546,8547,-8366,-8324]]},{"type":"Polygon","id":1031,"arcs":[[8548,8549,-8369,-8367,-8548]]},{"type":"Polygon","id":28037,"arcs":[[-8511,8550,8551,-8503,-8438]]},{"type":"Polygon","id":48403,"arcs":[[-8458,8552,8553,-8535,-8407]]},{"type":"Polygon","id":13277,"arcs":[[-8491,8554,8555,8556,-8450,-8446]]},{"type":"Polygon","id":48225,"arcs":[[8557,8558,8559,8560,-8526,-8354,-8329]]},{"type":"Polygon","id":48095,"arcs":[[-8359,8561,8562,-8516,-8361]]},{"type":"Polygon","id":13229,"arcs":[[-8462,8563,8564,-8512,-8421]]},{"type":"Polygon","id":1039,"arcs":[[-8550,8565,8566,8567,8568,-8501,-8423,-8370]]},{"type":"Polygon","id":48235,"arcs":[[8569,8570,-8538,-8518]]},{"type":"Polygon","id":48005,"arcs":[[-8537,8571,8572,8573,8574,-8558,-8328,-8455]]},{"type":"Polygon","id":13099,"arcs":[[-8545,8575,8576,8577,8578,-8484,-8482]]},{"type":"Polygon","id":48145,"arcs":[[8579,8580,-8442,-8469,8581]]},{"type":"Polygon","id":22079,"arcs":[[-8429,8582,8583,8584,8585,-8318,-8477]]},{"type":"Polygon","id":48307,"arcs":[[-8365,8586,8587,8588,-8562,-8358]]},{"type":"Polygon","id":48411,"arcs":[[-8507,8589,8590,8591,8592,-8587,-8364]]},{"type":"Polygon","id":13019,"arcs":[[-8475,8593,8594,8595,8596,-8555,-8490]]},{"type":"Polygon","id":13299,"arcs":[[-8565,8597,8598,8599,8600,8601,8602,-8473,-8513]]},{"type":"Polygon","id":48281,"arcs":[[8603,8604,-8590,-8506,-8384,-8514]]},{"type":"MultiPolygon","id":13127,"arcs":[[[8605,8606]],[[8607,8608,8609,8610,8611,-8460,-8525]]]},{"type":"Polygon","id":13007,"arcs":[[-8542,8612,8613,8614,-8576,-8544]]},{"type":"Polygon","id":13205,"arcs":[[-8452,8615,8616,8617,8618,-8613,-8541]]},{"type":"Polygon","id":28041,"arcs":[[-8522,8619,8620,8621,-8433]]},{"type":"Polygon","id":28035,"arcs":[[8622,8623,8624,8625,-8478,-8467]]},{"type":"Polygon","id":28073,"arcs":[[-8479,-8626,8626,8627,-8487]]},{"type":"Polygon","id":28111,"arcs":[[-8434,-8622,8628,8629,-8623,-8466]]},{"type":"Polygon","id":28091,"arcs":[[-8628,8630,8631,8632,-8498,-8488]]},{"type":"Polygon","id":13003,"arcs":[[-8603,-8602,8633,8634,-8594,-8474]]},{"type":"Polygon","id":48455,"arcs":[[8635,8636,8637,-8559,-8575]]},{"type":"Polygon","id":48371,"arcs":[[-8533,8638,8639,8640,8641,-8399,-8540]]},{"type":"Polygon","id":13025,"arcs":[[-8612,8642,8643,-8598,-8564,-8461]]},{"type":"Polygon","id":28157,"arcs":[[-8552,8644,8645,8646,-8493,-8504]]},{"type":"Polygon","id":22115,"arcs":[[-8586,8647,8648,8649,-8456,-8319]]},{"type":"Polygon","id":48395,"arcs":[[8650,8651,8652,-8582,-8471,-8528]]},{"type":"Polygon","id":13075,"arcs":[[8653,8654,8655,8656,-8556,-8597]]},{"type":"Polygon","id":28005,"arcs":[[-8510,8657,8658,8659,8660,-8645,-8551]]},{"type":"Polygon","id":28113,"arcs":[[8661,8662,8663,-8658,-8509]]},{"type":"Polygon","id":28147,"arcs":[[-8633,8664,-8662,-8508,-8499]]},{"type":"Polygon","id":22009,"arcs":[[-8411,-8497,8665,8666,8667,8668,-8583,-8428]]},{"type":"Polygon","id":13071,"arcs":[[-8557,-8657,8669,8670,-8616,-8451]]},{"type":"Polygon","id":48027,"arcs":[[-8581,8671,8672,8673,-8604,-8515,-8443]]},{"type":"Polygon","id":1003,"arcs":[[-8465,8674,8675,8676,8677,8678,8679,-8520,-8404]]},{"type":"Polygon","id":1069,"arcs":[[-8579,8680,8681,8682,-8546,-8485]]},{"type":"Polygon","id":1053,"arcs":[[-8569,8683,8684,8685,-8675,-8464,-8502]]},{"type":"Polygon","id":13201,"arcs":[[-8615,8686,8687,-8577]]},{"type":"Polygon","id":1061,"arcs":[[8688,8689,8690,-8566,-8549,-8547,-8683]]},{"type":"Polygon","id":48351,"arcs":[[8691,8692,8693,8694,-8553,-8457,-8650]]},{"type":"Polygon","id":13065,"arcs":[[-8601,8695,8696,8697,8698,-8634]]},{"type":"Polygon","id":13173,"arcs":[[-8699,8699,8700,-8595,-8635]]},{"type":"Polygon","id":1097,"arcs":[[8701,8702,8703,-8620,-8521,-8680]]},{"type":"MultiPolygon","id":13039,"arcs":[[[8704]],[[8705,8706,8707,-8643,-8611]]]},{"type":"Polygon","id":48241,"arcs":[[8708,8709,8710,-8572,-8536,-8554,-8695]]},{"type":"Polygon","id":48373,"arcs":[[8711,8712,8713,8714,-8636,-8574]]},{"type":"Polygon","id":48331,"arcs":[[8715,8716,8717,-8672,-8580,-8653]]},{"type":"Polygon","id":48243,"arcs":[[8718,-8392,-8390,-8400,-8642,8719]]},{"type":"Polygon","id":48313,"arcs":[[8720,8721,8722,-8527,-8561]]},{"type":"Polygon","id":48327,"arcs":[[-8589,8723,8724,8725,-8563]]},{"type":"Polygon","id":48413,"arcs":[[-8726,8726,8727,-8570,-8517]]},{"type":"Polygon","id":48105,"arcs":[[-8530,-8539,-8571,-8728,8728,8729,8730,-8639,-8532]]},{"type":"Polygon","id":13087,"arcs":[[-8614,-8619,8731,8732,8733,-8687]]},{"type":"Polygon","id":13131,"arcs":[[8734,8735,8736,-8732,-8618]]},{"type":"Polygon","id":13027,"arcs":[[-8655,8737,8738,8739,8740,-8670,-8656]]},{"type":"Polygon","id":13275,"arcs":[[-8671,-8741,8741,8742,-8735,-8617]]},{"type":"Polygon","id":13253,"arcs":[[-8734,8743,8744,-8681,-8578,-8688]]},{"type":"Polygon","id":13049,"arcs":[[8745,8746,-8599,-8644,-8708]]},{"type":"Polygon","id":48457,"arcs":[[-8711,8747,-8712,-8573]]},{"type":"Polygon","id":48471,"arcs":[[8748,8749,8750,-8721,-8560,-8638]]},{"type":"MultiPolygon","id":22125,"arcs":[[[8751,8752,-8494,-8647,8753]]]},{"type":"Polygon","id":48053,"arcs":[[-8674,8754,8755,8756,8757,-8591,-8605]]},{"type":"Polygon","id":13185,"arcs":[[-8596,-8701,8758,8759,8760,8761,-8738,-8654]]},{"type":"Polygon","id":22077,"arcs":[[-8495,-8753,8762,8763,8764,8765,-8667,-8767]]},{"type":"Polygon","id":28109,"arcs":[[-8625,8767,8768,8769,8770,-8631,-8627]]},{"type":"Polygon","id":22117,"arcs":[[-8771,8771,8772,-8663,-8665,-8632]]},{"type":"Polygon","id":22039,"arcs":[[-8669,8773,8774,8775,8776,-8584]]},{"type":"Polygon","id":12063,"arcs":[[-8745,8777,8778,8779,8780,8781,-8689,-8682]]},{"type":"Polygon","id":22105,"arcs":[[-8773,8782,8783,8784,8785,8786,-8659,-8664]]},{"type":"Polygon","id":12033,"arcs":[[8787,-8676,-8686,8788]]},{"type":"Polygon","id":12113,"arcs":[[8789,8790,-8789,-8685]]},{"type":"Polygon","id":28039,"arcs":[[-8704,8791,8792,-8629,-8621]]},{"type":"Polygon","id":22091,"arcs":[[-8787,8793,8794,8795,-8660]]},{"type":"MultiPolygon","id":12091,"arcs":[[[-8568,8796,8797,-8790,-8684]]]},{"type":"Polygon","id":22037,"arcs":[[-8796,8798,8799,-8754,-8646,-8661]]},{"type":"Polygon","id":12059,"arcs":[[-8782,8800,8801,-8690]]},{"type":"Polygon","id":12131,"arcs":[[-8691,-8802,8802,8803,8804,-8806,8806,-8797,-8567]]},{"type":"Polygon","id":48041,"arcs":[[8807,8808,8809,-8651,-8723]]},{"type":"Polygon","id":48319,"arcs":[[-8593,8810,8811,8812,-8724,-8588]]},{"type":"Polygon","id":48299,"arcs":[[-8758,8813,8814,-8811,-8592]]},{"type":"Polygon","id":28131,"arcs":[[-8793,8815,8816,-8768,-8624,-8630]]},{"type":"Polygon","id":48491,"arcs":[[8817,8818,8819,-8755,-8673,-8718]]},{"type":"Polygon","id":48407,"arcs":[[-8715,8820,8821,-8749,-8637]]},{"type":"Polygon","id":22003,"arcs":[[-8777,8822,8823,-8648,-8585]]},{"type":"Polygon","id":22011,"arcs":[[-8824,8824,8825,-8692,-8649]]},{"type":"Polygon","id":13101,"arcs":[[8826,8827,-8759,-8700,-8698]]},{"type":"Polygon","id":48185,"arcs":[[-8751,8828,8829,8830,-8808,-8722]]},{"type":"Polygon","id":22097,"arcs":[[-8766,8831,8832,8833,-8774,-8668]]},{"type":"Polygon","id":12133,"arcs":[[8834,-8803,-8801,-8781]]},{"type":"Polygon","id":12089,"arcs":[[-8707,8835,8836,8837,-8746]]},{"type":"Polygon","id":28059,"arcs":[[-8703,8838,8839,-8816,-8792]]},{"type":"Polygon","id":48051,"arcs":[[-8810,8840,8841,-8716,-8652]]},{"type":"Polygon","id":22033,"arcs":[[8842,8843,8844,8845,-8799,-8795]]},{"type":"Polygon","id":22103,"arcs":[[-8770,8846,8847,8848,8849,-8783,-8772]]},{"type":"Polygon","id":12039,"arcs":[[-8737,8850,8851,-8778,-8744,-8733]]},{"type":"Polygon","id":48267,"arcs":[[8852,8853,8854,8855,-8725,-8813]]},{"type":"Polygon","id":48435,"arcs":[[-8856,8856,8857,-8729,-8727]]},{"type":"Polygon","id":12073,"arcs":[[-8743,8858,8859,8860,-8851,-8736]]},{"type":"Polygon","id":28047,"arcs":[[-8840,8861,8862,-8817]]},{"type":"Polygon","id":12065,"arcs":[[-8740,8863,8864,8865,8866,-8859,-8742]]},{"type":"Polygon","id":48043,"arcs":[[8867,-8720,-8641,8868,8869]]},{"type":"Polygon","id":22121,"arcs":[[8870,-8763,-8752,-8800,-8846]]},{"type":"Polygon","id":48443,"arcs":[[8871,8872,-8869,-8640,-8731]]},{"type":"Polygon","id":22063,"arcs":[[-8786,8873,8874,-8843,-8794]]},{"type":"Polygon","id":12079,"arcs":[[-8762,8875,8876,8877,8878,8879,-8864,-8739]]},{"type":"Polygon","id":28045,"arcs":[[-8863,8880,-8847,-8769]]},{"type":"Polygon","id":12047,"arcs":[[-8828,8881,8882,-8877,-8760]]},{"type":"Polygon","id":48377,"arcs":[[-8868,8883,-8719]]},{"type":"Polygon","id":48339,"arcs":[[-8822,8884,8885,8886,-8829,-8750]]},{"type":"Polygon","id":48453,"arcs":[[8887,8888,8889,8890,-8756,-8820]]},{"type":"Polygon","id":12013,"arcs":[[8891,8892,8893,-8779]]},{"type":"Polygon","id":12077,"arcs":[[-8861,8894,8895,8896,-8892,-8852]]},{"type":"Polygon","id":12023,"arcs":[[-8697,8897,8898,8899,8900,8901,-8882,-8827]]},{"type":"MultiPolygon","id":12031,"arcs":[[[8902,8903]],[[8904,8905]],[[8906,8907,8908,-8837]]]},{"type":"Polygon","id":12003,"arcs":[[-8838,-8909,8909,8910,8911,-8898,-8696,-8600,-8747]]},{"type":"MultiPolygon","id":12005,"arcs":[[[8912,8913]],[[-8894,8914,8915,-8804,-8835,-8780]]]},{"type":"Polygon","id":48287,"arcs":[[8916,8917,8918,-8818,-8717,-8842]]},{"type":"Polygon","id":48199,"arcs":[[-8710,8919,8920,8921,-8713,-8748]]},{"type":"Polygon","id":48171,"arcs":[[-8815,8922,8923,8924,-8853,-8812]]},{"type":"Polygon","id":48031,"arcs":[[-8757,-8891,8925,8926,8927,-8923,-8814]]},{"type":"MultiPolygon","id":22099,"arcs":[[[8928,8929,8930]],[[8931,8932,-8832,-8765,8933]]]},{"type":"Polygon","id":22047,"arcs":[[-8871,-8845,8934,8935,8936,-8934,-8764]]},{"type":"Polygon","id":48291,"arcs":[[-8922,8937,8938,8939,-8885,-8821,-8714]]},{"type":"Polygon","id":22019,"arcs":[[8940,8941,8942,-8693,-8826]]},{"type":"Polygon","id":22053,"arcs":[[-8776,8943,8944,8945,8946,-8941,-8825,-8823]]},{"type":"Polygon","id":22001,"arcs":[[-8834,8947,8948,8949,-8945,-8944,-8775]]},{"type":"Polygon","id":12121,"arcs":[[-8902,8950,8951,-8878,-8883]]},{"type":"Polygon","id":48021,"arcs":[[8952,8953,-8888,-8819,-8919]]},{"type":"Polygon","id":48477,"arcs":[[-8831,8954,8955,8956,-8917,-8841,-8809]]},{"type":"Polygon","id":22055,"arcs":[[-8933,8957,8958,-8948,-8833]]},{"type":"Polygon","id":48209,"arcs":[[8959,8960,-8926,-8890,8961]]},{"type":"Polygon","id":22005,"arcs":[[-8875,8962,8963,8964,8965,-8935,-8844]]},{"type":"Polygon","id":12123,"arcs":[[8966,8967,8968,-8865,-8880]]},{"type":"Polygon","id":12129,"arcs":[[-8867,8969,8970,-8895,-8860]]},{"type":"Polygon","id":22095,"arcs":[[8971,8972,8973,8974,-8963,-8874,-8785]]},{"type":"Polygon","id":48465,"arcs":[[-8858,8975,8976,8977,-8872,-8730]]},{"type":"Polygon","id":48137,"arcs":[[8978,8979,8980,8981,-8976,-8857,-8855]]},{"type":"Polygon","id":48265,"arcs":[[8982,8983,8984,-8979,-8854,-8925]]},{"type":"Polygon","id":12067,"arcs":[[-8952,8985,8986,-8967,-8879]]},{"type":"MultiPolygon","id":12109,"arcs":[[[8987,8988,8989,8990,8991,-8903,8992]],[[-8905,8993]]]},{"type":"Polygon","id":48361,"arcs":[[-8943,8994,8995,-8920,-8709,-8694]]},{"type":"Polygon","id":48473,"arcs":[[8996,8997,8998,-8955,-8830,-8887]]},{"type":"Polygon","id":22089,"arcs":[[8999,-8972,9000]]},{"type":"Polygon","id":22051,"arcs":[[9001,9002,9003,9004,-9001,-8784,-8850]]},{"type":"Polygon","id":12045,"arcs":[[-8897,9005,9006,9007,9008,-8913,9009,-8915,-8893]]},{"type":"Polygon","id":22071,"arcs":[[9010,9011,9012,-9002,-8849]]},{"type":"Polygon","id":12019,"arcs":[[9013,9014,9015,-8910,-8908]]},{"type":"Polygon","id":48245,"arcs":[[-8996,9016,9017,9018,-8938,-8921]]},{"type":"Polygon","id":48201,"arcs":[[-8940,9019,9020,9021,9022,9023,-8997,-8886]]},{"type":"MultiPolygon","id":22087,"arcs":[[[9024,9025,9026,-9012,9027]],[[9028]]]},{"type":"Polygon","id":22093,"arcs":[[9029,9030,-8965,-8964,-8975]]},{"type":"Polygon","id":48149,"arcs":[[9031,9032,9033,9034,9035,-8953,-8918,-8957]]},{"type":"Polygon","id":22113,"arcs":[[-8959,9036,9037,9038,-8946,-8950,-8949]]},{"type":"Polygon","id":12007,"arcs":[[-9016,9039,9040,9041,-8911]]},{"type":"Polygon","id":12125,"arcs":[[-9042,9042,-8899,-8912]]},{"type":"Polygon","id":48259,"arcs":[[-8928,9043,9044,9045,-8983,-8924]]},{"type":"MultiPolygon","id":22045,"arcs":[[[9046]],[[-8937,9047,-8930,9048,9049,-9037,-8958,-8932]]]},{"type":"Polygon","id":48015,"arcs":[[-8999,9050,9051,9052,-9032,-8956]]},{"type":"Polygon","id":22007,"arcs":[[-8965,8964,-9031,9053,9054,9055,-8931,-9048,-8936,-8966]]},{"type":"Polygon","id":48385,"arcs":[[-8985,9056,9057,-8980]]},{"type":"Polygon","id":48055,"arcs":[[-8954,-9036,9058,9059,-8962,-8889]]},{"type":"Polygon","id":22023,"arcs":[[-8947,-9039,9060,-9017,-8995,-8942]]},{"type":"Polygon","id":48091,"arcs":[[9061,9062,-9044,-8927,-8961]]},{"type":"MultiPolygon","id":12037,"arcs":[[[9063]],[[-9008,9064]],[[-8971,9065,-9006,-8896]]]},{"type":"Polygon","id":48089,"arcs":[[9066,9067,9068,-9033,-9053]]},{"type":"Polygon","id":22101,"arcs":[[-9056,9069,9070,-9049,-8929]]},{"type":"Polygon","id":12001,"arcs":[[-9041,9071,9072,9073,9074,-8900,-9043]]},{"type":"Polygon","id":12041,"arcs":[[-9075,9075,9076,-8986,-8951,-8901]]},{"type":"Polygon","id":22057,"arcs":[[9077,9078,9079,9080,9081,9082,9083,-9054,-9030,-8974,-8973,-9000,-9005,9084]]},{"type":"Polygon","id":48019,"arcs":[[9085,-9057,-8984,-9046,9086,9087]]},{"type":"MultiPolygon","id":22075,"arcs":[[[9088]],[[-9003,-9013,-9027,9089]]]},{"type":"Polygon","id":48071,"arcs":[[-9019,9090,9091,9092,-9020,-8939]]},{"type":"Polygon","id":48187,"arcs":[[9093,9094,9095,-9062,-8960,-9060]]},{"type":"MultiPolygon","id":12107,"arcs":[[[-8991,9096,9097,9098]],[[9099,9100,-9072,-9040,-9015]]]},{"type":"Polygon","id":48157,"arcs":[[9101,9102,9103,-9051,-8998,-9024]]},{"type":"Polygon","id":48177,"arcs":[[9104,9105,9106,9107,-9094,-9059,-9035]]},{"type":"MultiPolygon","id":22109,"arcs":[[[9108]],[[-9070,-9055,-9084,9109]]]},{"type":"Polygon","id":48029,"arcs":[[-9096,9110,9111,9112,-9087,-9045,-9063]]},{"type":"Polygon","id":48325,"arcs":[[9113,9114,9115,-9088,-9113]]},{"type":"Polygon","id":12035,"arcs":[[9116,9117,-9097,-8990]]},{"type":"Polygon","id":48481,"arcs":[[-9104,9118,9119,9120,-9067,-9052]]},{"type":"Polygon","id":48285,"arcs":[[9121,9122,9123,-9105,-9034,-9069]]},{"type":"Polygon","id":48463,"arcs":[[-9116,9124,9125,-8981,-9058,-9086]]},{"type":"Polygon","id":48271,"arcs":[[-9126,9126,9127,-8977,-8982]]},{"type":"MultiPolygon","id":48167,"arcs":[[[9128]],[[9129,-9022,9130]],[[9131,-9092]]]},{"type":"Polygon","id":48039,"arcs":[[-9130,9132,9133,-9119,-9103,-9102,-9023]]},{"type":"Polygon","id":12075,"arcs":[[-9074,9134,9135,9136,9137,9138,-9076]]},{"type":"Polygon","id":12083,"arcs":[[9139,9140,9141,9142,-9135,-9073,-9101]]},{"type":"Polygon","id":48493,"arcs":[[-9108,9143,9144,-9111,-9095]]},{"type":"MultiPolygon","id":12127,"arcs":[[[9145,9146,9147,9148,9149,9150,9151,9152,-9098,-9118,9153]]]},{"type":"Polygon","id":48123,"arcs":[[9154,9155,-9106,-9124,9156]]},{"type":"Polygon","id":12069,"arcs":[[-9152,-9151,9157,9158,9159,9160,-9141,9161]]},{"type":"Polygon","id":48239,"arcs":[[9162,9163,9164,9165,9166,9167,9168,9169,-9122,-9068,-9121]]},{"type":"Polygon","id":48013,"arcs":[[9170,9171,-9114,-9112,-9145,9172,9173]]},{"type":"Polygon","id":48321,"arcs":[[9174,-9163,-9120,-9134,9175]]},{"type":"Polygon","id":48255,"arcs":[[9176,9177,-9173,-9144,-9107,-9156,9178]]},{"type":"Polygon","id":48469,"arcs":[[-9170,9179,9180,9181,9182,-9157,-9123]]},{"type":"Polygon","id":48163,"arcs":[[-9172,9183,9184,9185,-9115]]},{"type":"Polygon","id":48507,"arcs":[[-9186,9186,9187,-9125]]},{"type":"Polygon","id":48323,"arcs":[[-9188,9188,9189,9190,-9127]]},{"type":"Polygon","id":12017,"arcs":[[-9136,-9143,9191,9192,9193]]},{"type":"Polygon","id":12119,"arcs":[[-9161,9194,9195,9196,-9192,-9142]]},{"type":"Polygon","id":48175,"arcs":[[-9183,9197,9198,-9179,-9155]]},{"type":"Polygon","id":12117,"arcs":[[-9149,9199,-9158,-9150]]},{"type":"MultiPolygon","id":12009,"arcs":[[[9200,9201,9202,9203,-9148]],[[9204,9205,9206,-9146]]]},{"type":"Polygon","id":48297,"arcs":[[9207,9208,9209,-9174,-9178,9210,9211]]},{"type":"Polygon","id":12095,"arcs":[[-9200,-9204,9212,-9159]]},{"type":"MultiPolygon","id":48057,"arcs":[[[9213,9214]],[[9215,-9181,9216]],[[-9166,9217]]]},{"type":"Polygon","id":48025,"arcs":[[-9199,9218,9219,-9211,-9177]]},{"type":"Polygon","id":12053,"arcs":[[-9197,9220,9221,-9193]]},{"type":"Polygon","id":48283,"arcs":[[9222,9223,9224,-9184]]},{"type":"Polygon","id":48311,"arcs":[[-9210,9225,-9223,-9171]]},{"type":"Polygon","id":48127,"arcs":[[-9185,-9225,9226,-9189,-9187]]},{"type":"Polygon","id":48391,"arcs":[[-9216,9227,9228,9229,9230,9231,-9219,-9198,-9182]]},{"type":"Polygon","id":12101,"arcs":[[-9196,9232,9233,9234,9235,-9221]]},{"type":"Polygon","id":12105,"arcs":[[9236,9237,9238,9239,-9233,-9195,-9160]]},{"type":"Polygon","id":12097,"arcs":[[-9203,9240,9241,-9237,-9213]]},{"type":"MultiPolygon","id":48007,"arcs":[[[9242]],[[9243,9244,-9231]],[[9245,9246]],[[-9229,9247,9248,9249]]]},{"type":"Polygon","id":48479,"arcs":[[9250,9251,9252,9253,-9190,-9227,-9224]]},{"type":"Polygon","id":48409,"arcs":[[-9245,9254,-9246,9255,9256,9257,9258,9259,9260,-9212,-9220,-9232]]},{"type":"Polygon","id":12103,"arcs":[[9261,9262,-9235]]},{"type":"Polygon","id":12057,"arcs":[[-9240,9263,9264,-9262,-9234]]},{"type":"Polygon","id":48131,"arcs":[[9265,9266,9267,-9251,-9226,-9209]]},{"type":"Polygon","id":48249,"arcs":[[-9261,-9260,9268,9269,9270,-9266,-9208]]},{"type":"MultiPolygon","id":48355,"arcs":[[[9271]],[[9272,-9269,-9259,9273]]]},{"type":"MultiPolygon","id":12061,"arcs":[[[9274,9275,-9241,-9202,9276]]]},{"type":"Polygon","id":12055,"arcs":[[9277,9278,9279,9280,-9238]]},{"type":"Polygon","id":12049,"arcs":[[-9281,9281,9282,-9239]]},{"type":"Polygon","id":12081,"arcs":[[-9283,9283,9284,9285,-9264]]},{"type":"Polygon","id":12093,"arcs":[[-9276,9286,9287,9288,-9278,-9242]]},{"type":"MultiPolygon","id":48273,"arcs":[[[9289,9290,9291,9292]],[[9293,9294,9295,-9270,-9273]]]},{"type":"Polygon","id":12111,"arcs":[[9296,9297,9298,9299,-9287,-9275]]},{"type":"Polygon","id":12115,"arcs":[[-9285,9300,9301,9302,9303,9304]]},{"type":"Polygon","id":48247,"arcs":[[9305,9306,9307,-9252,-9268]]},{"type":"Polygon","id":12027,"arcs":[[-9280,9308,-9301,-9284,-9282]]},{"type":"Polygon","id":48505,"arcs":[[-9308,9309,9310,-9253]]},{"type":"MultiPolygon","id":48261,"arcs":[[[9311,9312]],[[9313,-9290,9314]],[[9315,9316,9317,-9295,9318]]]},{"type":"Polygon","id":48047,"arcs":[[-9271,-9296,-9318,9319,9320,-9306,-9267]]},{"type":"MultiPolygon","id":12085,"arcs":[[[9321,9322,9323,9324,9325,-9288,-9300,9326]]]},{"type":"Polygon","id":12043,"arcs":[[9327,9328,-9279,-9289]]},{"type":"MultiPolygon","id":12015,"arcs":[[[-9304,9329]],[[-9329,9330,9331,-9302,-9309]]]},{"type":"Polygon","id":12099,"arcs":[[9332,9333,9334,-9326]]},{"type":"Polygon","id":12051,"arcs":[[9335,9336,9337,-9328,-9335]]},{"type":"MultiPolygon","id":12071,"arcs":[[[9338]],[[-9338,9339,9340,-9331]]]},{"type":"Polygon","id":48427,"arcs":[[-9321,9341,9342,-9310,-9307]]},{"type":"Polygon","id":48215,"arcs":[[-9317,9343,9344,9345,-9342,-9320]]},{"type":"Polygon","id":48489,"arcs":[[9346,-9344,-9316,9347]]},{"type":"MultiPolygon","id":12021,"arcs":[[[9348,9349,9350,9351,-9340,-9337]]]},{"type":"Polygon","id":48061,"arcs":[[-9345,-9347,9352]]},{"type":"Polygon","id":12011,"arcs":[[9353,9354,-9349,-9336,-9334]]},{"type":"Polygon","id":12086,"arcs":[[9355,9356,9357,9358,9359,-9350,-9355,9360]]},{"type":"MultiPolygon","id":12087,"arcs":[[[9361]],[[-9360,9362,-9351]]]},{"type":"Polygon","id":4015,"arcs":[[-5737,-6670,-7308,-6521,-5912,-4625,-5343,-5417]]},{"type":"Polygon","id":12029,"arcs":[[-9077,-9139,9363,-8968,-8987]]},{"type":"Polygon","id":27077,"arcs":[[-125,-180,-108,9364]]},{"type":"Polygon","id":27031,"arcs":[[9365,-203,9366]]},{"type":"Polygon","id":55031,"arcs":[[9367,-675,-668,-593,-474,-148,9368]]},{"type":"Polygon","id":55007,"arcs":[[9369,-673,-9368,9370]]},{"type":"Polygon","id":55003,"arcs":[[-561,-721,-669,-9370,9371]]},{"type":"Polygon","id":55003,"arcs":[[9372]]},{"type":"Polygon","id":26083,"arcs":[[9373,9374,9375]]},{"type":"Polygon","id":26083,"arcs":[[9376]]},{"type":"Polygon","id":26061,"arcs":[[9377,-446,-592,-436,9378]]},{"type":"Polygon","id":26061,"arcs":[[9379,-9375]]},{"type":"Polygon","id":26103,"arcs":[[9380,9381,-716,-642,-586,-444,9382]]},{"type":"Polygon","id":26003,"arcs":[[-577,9383,-9381,9384,-491]]},{"type":"Polygon","id":26041,"arcs":[[-9384,-576,9385,-712,-9382]]},{"type":"Polygon","id":55075,"arcs":[[-715,9386,-879,-687,-704,-640]]},{"type":"Polygon","id":55029,"arcs":[[-1169,9387]]},{"type":"Polygon","id":26033,"arcs":[[9388,9389]]},{"type":"Polygon","id":26033,"arcs":[[9390]]},{"type":"Polygon","id":26033,"arcs":[[9391,9392,9393,-488,9394]]},{"type":"Polygon","id":26097,"arcs":[[9395]]},{"type":"Polygon","id":26097,"arcs":[[9396,9397,-574,-489,-9394]]},{"type":"Polygon","id":26047,"arcs":[[-771,9398,9399]]},{"type":"Polygon","id":26029,"arcs":[[-9399,-770,-953,-950,9400]]},{"type":"Polygon","id":26029,"arcs":[[9401]]},{"type":"Polygon","id":26089,"arcs":[[9402]]},{"type":"Polygon","id":26089,"arcs":[[9403,-1114,9404]]},{"type":"Polygon","id":26055,"arcs":[[-948,-1082,-1230,-1111,-9404,9405]]},{"type":"Polygon","id":26007,"arcs":[[9406,-1079,-954,-819]]},{"type":"Polygon","id":26011,"arcs":[[9407,-1439,-1400,-1240,-1238]]},{"type":"Polygon","id":26063,"arcs":[[-1578,-1556,9408]]},{"type":"Polygon","id":26147,"arcs":[[-1931,-1726,-1576,9409]]},{"type":"Polygon","id":26163,"arcs":[[9410,9411,-2133,-1932,-1929]]},{"type":"Polygon","id":26115,"arcs":[[9412,-2505,-2280,-2130,-9412]]},{"type":"MultiPolygon","id":45019,"arcs":[[[9413,9414,9415,-7862,-7809,-7695]],[[-7693,-7590,9416]]]},{"type":"Polygon","id":15001,"arcs":[[9417]]},{"type":"Polygon","id":15007,"arcs":[[9418]]},{"type":"Polygon","id":15009,"arcs":[[-9420,9420]]},{"type":"Polygon","id":15009,"arcs":[[9421]]},{"type":"Polygon","id":15009,"arcs":[[9422]]},{"type":"Polygon","id":15009,"arcs":[[9423]]},{"type":"Polygon","id":15003,"arcs":[[9424]]},{"type":"Polygon","id":15007,"arcs":[[9425]]},{"type":"MultiPolygon","id":2016,"arcs":[[[9426]],[[9427]],[[9428]],[[9429]],[[9430]],[[9431]],[[9432]],[[9433]],[[9434]],[[9435]],[[9436]],[[9437]],[[9438]],[[9439]],[[9440]],[[9441]],[[9442]],[[9443]],[[9444]],[[9445]],[[9446]],[[9447]],[[9448]],[[9449]]]},{"type":"MultiPolygon","id":2013,"arcs":[[[9450]],[[9451]],[[9452]],[[9453]],[[9454]],[[9455]],[[9456]],[[9457]],[[9458]],[[9459]],[[9460]],[[9461]],[[9462,9463,9464,9465]]]},{"type":"MultiPolygon","id":2130,"arcs":[[[9466]],[[9467]]]},{"type":"Polygon","id":2060,"arcs":[[9468,9469]]},{"type":"MultiPolygon","id":2070,"arcs":[[[9470]],[[9471,9472]],[[9473,9474,9475]]]},{"type":"MultiPolygon","id":2164,"arcs":[[[9476]],[[9477,9478,9479,-9465,9480,-9470,9481,-9475,9482]]]},{"type":"MultiPolygon","id":2150,"arcs":[[[9483]],[[9484]],[[9485]],[[9486]],[[9487]],[[9488]],[[9489]],[[9490]],[[9491]],[[9492]],[[9493,-9479,9494,9495]]]},{"type":"MultiPolygon","id":2110,"arcs":[[[9496,9497,9498,9499]],[[9500]],[[9501,9502,9503,9504,9505,9506]],[[9507,9508]]]},{"type":"MultiPolygon","id":2280,"arcs":[[[9509]],[[9510]],[[9511]],[[9512]],[[9513]],[[9514]],[[9515]],[[9516,9517,9518,9519]]]},{"type":"MultiPolygon","id":2232,"arcs":[[[9520,9521,9522,9523]],[[9524]],[[-9520,9525,9526,9527,-9503,9528]],[[-9497,9529,9530,9531]],[[9532,9533]],[[-9499,9534]],[[9535,9536,9537,9538]],[[9539,9540,9541,9542]]]},{"type":"MultiPolygon","id":2100,"arcs":[[[-9507,9543,-9508,9544,-9540,9545]],[[9546,-9538,9547,-9542]]]},{"type":"MultiPolygon","id":2220,"arcs":[[[9548]],[[9549,9550]],[[-9521,9551,-9533,9552]]]},{"type":"MultiPolygon","id":2270,"arcs":[[[9553]],[[9554]],[[9555]],[[9556,9557,9558,9559]]]},{"type":"MultiPolygon","id":2050,"arcs":[[[9560]],[[9561]],[[9562]],[[9563]],[[-9559,9564,9565,9566,-9483,-9474,9567,-9473,9568]]]},{"type":"Polygon","id":2170,"arcs":[[9569,9570,9571,9572,-9566,9573,9574,9575]]},{"type":"Polygon","id":2068,"arcs":[[9576,9577,-9575,9578]]},{"type":"Polygon","id":2020,"arcs":[[-9571,9579,9580,9581]]},{"type":"MultiPolygon","id":2261,"arcs":[[[9582]],[[9583]],[[9584]],[[9585]],[[9586]],[[9587]],[[9588]],[[9589]],[[9590,9591]],[[9592]],[[9593]],[[9594]],[[9595,9596]],[[9597]],[[9598,-9580,-9570,9599,9600,9601,9602]]]},{"type":"MultiPolygon","id":2122,"arcs":[[[9603]],[[9604]],[[9605]],[[-9581,-9599,9606,-9596,9607,-9591,9608]],[[-9495,-9478,-9567,-9573,9609]]]},{"type":"Polygon","id":2282,"arcs":[[-9602,9610,-9536,9611]]},{"type":"Polygon","id":2290,"arcs":[[9612,9613,9614,-9579,-9574,-9565,-9558,9615,9616,9617]]},{"type":"Polygon","id":2090,"arcs":[[9618,-9577,-9615]]},{"type":"Polygon","id":2240,"arcs":[[-9600,-9576,-9578,-9619,-9614,9619]]},{"type":"MultiPolygon","id":2185,"arcs":[[[9620]],[[-9618,9621,9622]]]},{"type":"Polygon","id":2188,"arcs":[[-9617,9623,9624,-9622]]},{"type":"MultiPolygon","id":2180,"arcs":[[[9625]],[[9626]],[[9627]],[[-9624,-9616,-9557,9628]]]},{"type":"Polygon","id":2201,"arcs":[[9629]]},{"type":"Polygon","id":2201,"arcs":[[9630]]},{"type":"Polygon","id":2201,"arcs":[[9631]]},{"type":"Polygon","id":2201,"arcs":[[9632]]},{"type":"Polygon","id":2201,"arcs":[[9633]]},{"type":"Polygon","id":2201,"arcs":[[9634]]},{"type":"Polygon","id":2201,"arcs":[[9635]]},{"type":"Polygon","id":2201,"arcs":[[9636]]},{"type":"Polygon","id":2201,"arcs":[[9637]]},{"type":"Polygon","id":2201,"arcs":[[9638]]},{"type":"Polygon","id":2201,"arcs":[[9639]]},{"type":"Polygon","id":2201,"arcs":[[9640]]},{"type":"Polygon","id":2201,"arcs":[[9641]]},{"type":"Polygon","id":2201,"arcs":[[9642]]},{"type":"Polygon","id":2201,"arcs":[[9643]]},{"type":"Polygon","id":2201,"arcs":[[-9518,9644]]},{"type":"Polygon","id":72125,"arcs":[[9645,9646,9647,9648,9649,9650]]},{"type":"Polygon","id":72003,"arcs":[[9651,9652,9653,9654,9655]]},{"type":"Polygon","id":72097,"arcs":[[9656,9657,-9651,9658,9659,9660,9661]]},{"type":"Polygon","id":72065,"arcs":[[9662,9663,9664,9665,9666]]},{"type":"Polygon","id":72055,"arcs":[[9667,9668,9669,9670]]},{"type":"Polygon","id":72083,"arcs":[[9671,9672,-9657,9673,9674]]},{"type":"Polygon","id":72025,"arcs":[[9675,9676,9677,9678,9679,9680,9681]]},{"type":"Polygon","id":72045,"arcs":[[9682,9683,9684,9685,9686]]},{"type":"Polygon","id":72133,"arcs":[[9687,9688,9689,9690]]},{"type":"Polygon","id":72121,"arcs":[[-9671,9691,-9647,9692,9693]]},{"type":"Polygon","id":72027,"arcs":[[-9666,9694,9695,9696,9697]]},{"type":"Polygon","id":72001,"arcs":[[9698,9699,9700,9701,9702,9703]]},{"type":"Polygon","id":72111,"arcs":[[9704,9705,9706,-9700]]},{"type":"Polygon","id":72047,"arcs":[[9707,9708,9709,9710,9711,9712]]},{"type":"Polygon","id":72091,"arcs":[[9713,9714,9715,9716,9717,9718]]},{"type":"Polygon","id":72013,"arcs":[[9719,9720,9721,9722,-9663,9723]]},{"type":"Polygon","id":72145,"arcs":[[9724,9725,-9714,9726]]},{"type":"Polygon","id":72031,"arcs":[[9727,9728,9729,9730,9731,9732]]},{"type":"Polygon","id":72061,"arcs":[[9733,9734,9735,9736,9737]]},{"type":"Polygon","id":72129,"arcs":[[9738,9739,9740,9741,9742,-9678,9743]]},{"type":"Polygon","id":72075,"arcs":[[9744,9745,-9691,9746,9747,9748,9749]]},{"type":"Polygon","id":72063,"arcs":[[-9729,9750,-9744,-9677,9751]]},{"type":"Polygon","id":72073,"arcs":[[9752,-9749,9753,9754,9755]]},{"type":"Polygon","id":72143,"arcs":[[9756,9757,-9712,9758,-9725,9759]]},{"type":"Polygon","id":72011,"arcs":[[9760,-9674,-9662,9761,9762,-9653,9763]]},{"type":"Polygon","id":72081,"arcs":[[-9665,9764,-9703,9765,9766,-9672,9767,-9695]]},{"type":"Polygon","id":72079,"arcs":[[-9692,-9670,9768,9769,-9648]]},{"type":"Polygon","id":72009,"arcs":[[9770,9771,9772,9773,9774]]},{"type":"Polygon","id":72099,"arcs":[[9775,9776,-9764,-9652,9777]]},{"type":"Polygon","id":72023,"arcs":[[9778,-9649,-9770,9779,-9660]]},{"type":"Polygon","id":72109,"arcs":[[9780,9781,9782,-9784,9784,9785,-9742]]},{"type":"Polygon","id":72101,"arcs":[[-9759,-9711,9786,9787,-9715,-9726]]},{"type":"Polygon","id":72005,"arcs":[[-9778,-9656,9788,9789]]},{"type":"Polygon","id":72059,"arcs":[[-9707,9790,9791,-9701]]},{"type":"Polygon","id":72021,"arcs":[[-9736,9792,-9683,9793,9794,9795,9796]]},{"type":"Polygon","id":72141,"arcs":[[9797,-9755,9798,-9704,-9765,-9664,-9723]]},{"type":"Polygon","id":72041,"arcs":[[-9680,9799,-9775,9800,-9685,9801]]},{"type":"Polygon","id":72123,"arcs":[[9802,9803,-9689,9804,-9772,9805]]},{"type":"Polygon","id":72131,"arcs":[[9806,-9696,-9768,-9675,-9761,-9777,9807]]},{"type":"Polygon","id":72035,"arcs":[[-9743,-9786,9808,-9806,-9771,-9800,-9679]]},{"type":"Polygon","id":72135,"arcs":[[-9795,9809,-9713,-9758,9810,9811]]},{"type":"Polygon","id":72115,"arcs":[[-9697,-9807,9812,9813]]},{"type":"Polygon","id":72054,"arcs":[[-9717,9814,-9721,9815]]},{"type":"Polygon","id":72105,"arcs":[[-9794,-9687,9816,-9708,-9810]]},{"type":"Polygon","id":72017,"arcs":[[-9718,-9816,-9720,9817]]},{"type":"Polygon","id":72127,"arcs":[[-9731,9818,-9682,9819,-9734,9820]]},{"type":"Polygon","id":72139,"arcs":[[-9730,-9752,-9676,-9819]]},{"type":"Polygon","id":72057,"arcs":[[-9785,-9822,9822,-9803,-9809]]},{"type":"Polygon","id":72153,"arcs":[[-9702,-9792,9823,-9668,-9694,9824,-9766]]},{"type":"Polygon","id":72043,"arcs":[[9825,9826,-9773,-9805,-9688,-9746,9827]]},{"type":"Polygon","id":72149,"arcs":[[-9828,-9745,9828]]},{"type":"Polygon","id":72039,"arcs":[[-9788,9829,-9756,-9798,-9722,-9815,-9716]]},{"type":"Polygon","id":72113,"arcs":[[-9748,9830,-9705,-9699,-9799,-9754]]},{"type":"Polygon","id":72107,"arcs":[[9831,-9826,-9829,-9750,-9753,-9830,-9787,-9710]]},{"type":"Polygon","id":72071,"arcs":[[-9813,-9808,-9776,-9790,9832]]},{"type":"Polygon","id":72007,"arcs":[[-9681,-9802,-9684,-9793,-9735,-9820]]},{"type":"Polygon","id":72019,"arcs":[[-9686,-9801,-9774,-9827,-9832,-9709,-9817]]},{"type":"Polygon","id":72093,"arcs":[[-9767,-9825,-9693,-9646,-9658,-9673]]},{"type":"Polygon","id":72151,"arcs":[[9833,9834,9835,-9781,-9741,9836]]},{"type":"Polygon","id":72137,"arcs":[[9837,-9796,-9812,9838,9839]]},{"type":"Polygon","id":78030,"arcs":[[9840]]},{"type":"Polygon","id":72089,"arcs":[[9841,9842,9843,9844]]},{"type":"Polygon","id":72087,"arcs":[[9845,9846,-9733,9847]]},{"type":"Polygon","id":72095,"arcs":[[9848,-9782,-9836]]},{"type":"Polygon","id":72119,"arcs":[[-9844,9849,9850,9851,9852,-9846,9853]]},{"type":"Polygon","id":72103,"arcs":[[9854,9855,9856,9857,-9851]]},{"type":"Polygon","id":72085,"arcs":[[-9858,9858,-9837,-9740,9859,9860,-9852]]},{"type":"Polygon","id":72029,"arcs":[[-9853,-9861,9861,-9728,-9847]]},{"type":"Polygon","id":72053,"arcs":[[9862,-9842,9863]]},{"type":"Polygon","id":72077,"arcs":[[-9860,-9739,-9751,-9862]]},{"type":"Polygon","id":72037,"arcs":[[9864,-9855,-9850,-9843,-9863]]},{"type":"Polygon","id":72069,"arcs":[[9865,-9834,-9859,-9857]]},{"type":"Polygon","id":72147,"arcs":[[9866]]},{"type":"Polygon","id":78010,"arcs":[[9867]]},{"type":"Polygon","id":72051,"arcs":[[-9839,-9811,-9757,9868]]}]},"states":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","id":2,"arcs":[[[9426]],[[9427]],[[9428]],[[9429]],[[9430]],[[9431]],[[9432]],[[9433]],[[9434]],[[9435]],[[9436]],[[9437]],[[9438]],[[9439]],[[9440]],[[9441]],[[9442]],[[9443]],[[9444]],[[9445]],[[9446]],[[9447]],[[9450]],[[9451]],[[9452]],[[9453]],[[9629]],[[9454]],[[9630]],[[9455]],[[9456]],[[9631]],[[9457]],[[9632]],[[9633]],[[9458]],[[9634]],[[9459]],[[9460]],[[9466]],[[9635]],[[9461]],[[9636]],[[9637]],[[9638]],[[9639]],[[9483]],[[9640]],[[9509]],[[9467]],[[9641]],[[9642]],[[9510]],[[9643]],[[9511]],[[9512]],[[9484]],[[9476]],[[9485]],[[9486]],[[9448]],[[9513]],[[9514]],[[9515]],[[9487]],[[9449]],[[9548]],[[9869,9550]],[[9488]],[[9489]],[[9524]],[[9490]],[[9552,9521,9870,9523,9551,9533]],[[9500]],[[9499,9529,9871,9531,9497,9534]],[[9491]],[[9492]],[[9470]],[[9603]],[[9604]],[[9582]],[[9583]],[[9584]],[[9585]],[[9560]],[[9586]],[[9587]],[[9561]],[[9588]],[[9605]],[[9589]],[[9562]],[[9592]],[[9593]],[[9594]],[[9563]],[[9597]],[[9553]],[[9554]],[[9555]],[[9625]],[[9626]],[[9627]],[[9620]],[[9611,9602,9606,9596,9607,9591,9608,9581,9571,9609,9872,9493,9479,9465,9873,9463,9480,9468,9481,9475,9567,9471,9568,9559,9628,9624,9622,9612,9619,9600,9610,9536,9547,9542,9545,9501,9528,9516,9644,9518,9525,9874,9527,9503,9875,9505,9543,9508,9544,9540,9546,9538]]]},{"type":"MultiPolygon","id":15,"arcs":[[[9417]],[[9422]],[[9421]],[[9423]],[[9876,9420]],[[9424]],[[9425]],[[9418]]]},{"type":"MultiPolygon","id":72,"arcs":[[[9866]],[[9813,9697,9666,9723,9817,9718,9726,9759,9868,9839,9877,9737,9820,9731,9847,9853,9844,9863,9864,9855,9865,9834,9848,9782,9878,9822,9803,9689,9746,9830,9705,9790,9823,9668,9768,9779,9660,9761,9879,9654,9788,9832]]]},{"type":"Polygon","id":1,"arcs":[[-6786,-6785,-6792,-6791,-6807,6974,-6994,-6998,7027,-7168,-7171,7205,-7420,7492,7493,-7574,7698,7699,-7846,7871,-8018,8061,-8111,-8157,8194,-8293,8320,8321,-8483,8483,-8579,8680,8681,8688,8689,8690,8566,8567,8683,8684,8685,8675,8676,9880,8678,8701,8702,8703,-8620,8521,-8436,8249,8250,-8128,7954,7955,-7826,7678,-7599,7448,-7421,7302,-7257,7179,-6951,-6954,6937,-6753,-6699,-6714,-6725,-6724]]},{"type":"Polygon","id":5,"arcs":[[-5843,-5842,-5816,-5921,-5920,-5919,-5932,-5931,-5930,-5716,-5888,-5887,-5886,-5917,-5916,-5866,-5973,-5972,-5971,-5970,-5969,-6163,-6274,6406,6407,6408,6409,-6605,6734,6735,6736,7015,-7027,7132,7133,-7415,-7414,7662,7663,7664,7665,7666,7777,7794,7795,7733,7734,7719,7720,7643,7644,7645,-7611,7513,-7233,-7232,7118,-6775,-6774,6727,-6611,6550,-6316,6256,6117,-5946,-5936]]},{"type":"Polygon","id":4,"arcs":[[-4752,-5779,5779,5780,5781,-7177,7591,7592,-8039,8208,8504,8191,7725,7726,7305,7306,-6522,-6521,-5912,-4625,-5343,-5417,-5416,-4754,-4753]]},{"type":"MultiPolygon","id":6,"arcs":[[[7053]],[[7019]],[[7054]],[[6896]],[[6897]],[[-1601,-1600,-1604,2383,-2395,-2394,-2393,-2392,4129,4130,-4310,-4309,-4308,-3802,-4324,4568,-4773,-4255,5466,-5913,6520,6521,-7307,-7306,-7727,7740,7697,7519,7056,7020,6899,6528,5874,5606,5291,5193,5288,5438,5155,5018,4853,4552,4697,4472,4696,4474,4482,4847,4484,3626,2685,2373,-1893,-1975,-1974,-1882]]]},{"type":"Polygon","id":8,"arcs":[[-2141,-2140,-2553,-2552,-2733,-2732,-2702,-2701,-2823,-2972,-2971,3139,-3194,3363,3364,-3620,3939,3940,-4277,4354,-4592,-4591,4878,4879,-5245,5317,5318,5319,5207,5208,5307,5308,5510,5511,5496,5497,5323,5321,-4751,-4750,-4749,4628,-4026,3577,-3100,-3099,2981,2982,-2194,-2137,-2136,-2135]]},{"type":"Polygon","id":9,"arcs":[[-2172,-2171,-2170,-2018,-2017,2317,2318,-2457,2526,2527,2557,2559,9881,2561,2547,2548,2549,-2285,-2284,-1998]]},{"type":"Polygon","id":11,"arcs":[[4385,4386,-4115,-4297]]},{"type":"Polygon","id":10,"arcs":[[3752,4102,4392,4393,4394,4395,4396,4397,-4267,4100,4101,-4079,3754,3755,-3497,-3588,3756,-3586]]},{"type":"MultiPolygon","id":12,"arcs":[[[9361]],[[9338]],[[9063]],[[9882,9883]],[[8993,8905]],[[9884,9356,9885,9358,9362,9351,9340,9331,9302,9329,9304,9285,9264,9262,9235,9221,9193,9886,9137,9363,8968,8865,8969,9065,9006,9064,9008,8913,9009,8915,8804,9887,8806,8797,8790,8787,-8676,-8686,-8685,-8684,-8568,-8567,-8691,-8690,-8689,-8682,-8745,-8744,-8733,-8737,-8736,-8743,-8742,-8740,-8739,-8762,8875,-8760,-8828,-8827,-8697,-8696,-8600,-8747,-8746,-8707,8835,8906,9013,9099,9139,9161,9152,9098,8991,8903,8992,9888,8988,9116,9153,9204,9889,9206,9146,9200,9276,9296,9890,9298,9326,9891,9322,9892,9324,9332,9353,9360]]]},{"type":"MultiPolygon","id":13,"arcs":[[[8704]],[[9893,8606]],[[8522]],[[8333]],[[8283]],[[8284]],[[8523,8607,9894,8609,8705,8706,8745,8746,8599,8695,8696,8826,8827,8759,-8876,8761,8738,8739,8741,8742,8735,8736,8732,8743,8744,-8681,8578,-8484,8482,-8322,-8321,8292,-8195,8156,8110,-8062,8017,-7872,7845,-7700,-7699,7573,-7494,-7493,7419,-7206,7170,7167,-7028,6997,6993,-6975,-6806,-6721,-6720,-6719,-6795,-6794,-6828,-6827,-6823,-6822,-6886,-6885,-6884,-6802,-6673,-6924,-6923,-6922,-6921,-6920,-7061,-7060,-7256,7339,-7438,-7437,-7484,-7483,-7542,-7541,-7711,7821,-7868,-7867,7891,-7899,-8057,-8056,-8055,8285,8280,8336]]]},{"type":"Polygon","id":19,"arcs":[[-1566,1654,-1696,-1780,-1779,2023,-2098,2159,-2230,2312,2313,-2452,-2451,-2450,2717,-2767,2921,-2928,3117,3118,3078,3079,3081,3082,3093,3094,3091,3092,3088,3089,3085,3086,3075,3076,3072,3073,3069,3070,-2935,2880,-2829,2659,2660,-2543,2416,-2304,2207,-2191,2065,2066,-1834,-1833,1756,-1660,1651,-1521,-1490,-1494,-1493,-1517,-1516,-1515,-1526,-1525,-1519,-1518,-1497,-1496,-1502,-1501,-1500,-1513,-1512,-1505,-1504]]},{"type":"Polygon","id":16,"arcs":[[20,-36,111,-202,240,-350,-329,-328,518,-528,-737,-736,-735,-758,-652,1121,-1174,-1173,1592,-1734,-1733,2052,2141,2107,2108,2021,2022,1896,1580,1581,-1263,-1262,-1261,1091,-970,914,-709,522,535,536,-396,-395,364,-248,-247,114,-82,22,23]]},{"type":"Polygon","id":17,"arcs":[[-1968,-1967,-1948,-1947,-1953,-1952,-1960,-1959,-2030,-2029,2116,2233,2234,2235,2510,-2533,2774,-2825,2957,-3156,3323,3324,-3546,3727,-3919,4027,-4173,4238,-4445,-4444,4683,-4705,4905,-4930,5135,-5171,5358,5359,5361,-5492,5562,5574,5575,5568,5569,5570,-5351,-5350,-5160,5125,4935,4936,4726,4727,-4456,-4455,-4521,4379,4380,4381,4165,4070,4071,-3929,3750,3751,-3685,3515,-3493,3241,3242,-3118,2927,-2922,2766,-2718,2449,2450,2451,-2314,-2313,2229,-2160,2097,-2024,-1778]]},{"type":"Polygon","id":18,"arcs":[[-2297,-2296,-2291,-2290,-2295,2470,-2539,2621,-2717,2793,2794,-3019,3039,-3168,3291,-3407,-3406,3614,-3716,3808,-3932,-4139,4144,-4266,-4265,4422,4423,4432,4433,-4557,4659,4660,4805,4798,4799,4800,4795,4892,4893,4894,4953,4954,4923,4924,4973,4974,4927,4928,4929,-4906,4704,-4684,4443,4444,-4239,4172,-4028,3918,-3728,3545,-3325,-3324,3155,-2958,2824,-2775,2532,-2511,-2236,2533,2535,2467,-2204,-2203,-2300,-2299]]},{"type":"Polygon","id":20,"arcs":[[-3419,-3431,-3429,-3428,-3423,-3422,-3433,-3432,-3413,-3412,-3410,-3409,-3417,-3416,-3426,-3427,-3317,-3316,-3486,-3485,-3490,-3489,-3488,-3492,-3561,3664,-3762,3884,-4003,-4002,-4046,-4199,-4198,4346,-4501,4546,-4772,4820,-5042,-5041,5300,5301,-5544,5556,5557,5558,5531,5532,5523,5524,5592,5593,5446,5447,5449,5450,5526,5527,5459,5460,5528,5529,5456,5457,5453,5520,5521,5519,5516,5517,-5318,5244,-4880,-4879,4590,4591,-4355,4276,-3941,-3940,3619,-3365,-3420]]},{"type":"MultiPolygon","id":21,"arcs":[[[-4137,-4136,-4161,-4160,-4159,-4182,-4181,-4351,-4350,-4373,-4372,-4498,-4497,4744,-4805,-4804,-5103,-5102,5233,5234,5235,5611,-5643,5726,5826,5827,5819,5820,5824,5825,5807,5808,5882,5883,5835,5913,5914,5838,5839,5891,5892,5696,5698,5699,5658,5659,5737,5937,5938,5831,5832,5932,5933,5957,5958,-5897,-5709,-5708,-5707,5632,-5569,-5576,-5575,-5563,5491,-5362,-5360,-5359,5170,-5136,-4929,-4928,-4975,-4974,-4925,-4924,-4955,-4954,-4895,-4894,-4893,-4796,-4801,-4800,-4799,-4806,-4661,-4660,4556,-4434,-4433,-4424,-4423,4264,4265,-4145,-4138]]]},{"type":"MultiPolygon","id":22,"arcs":[[[9108]],[[9088]],[[9046]],[[9028]],[[9899,9025,9089,9003,9084,9900,9078,9901,9080,9902,9082,9109,9070,9049,9037,9060,-9017,-8995,8942,-8693,-8692,8649,8456,8457,-8406,8237,-8223,7906,7907,7908,-7820,-7645,-7644,-7721,-7720,-7735,-7734,-7796,-7795,-7778,-7667,-7666,-7665,-7928,7943,-8109,-8108,-8107,-8106,8274,8275,8276,8491,8492,-8647,-8646,-8661,-8660,-8659,-8664,-8663,-8665,-8632,-8771,-8770,8846,8847,9010,9027]]]},{"type":"MultiPolygon","id":25,"arcs":[[[2727]],[[2632]],[[2175,2248,2249,2250,2251,2252,2168,2015,2016,2017,2169,2170,2171,1997,1998,1999,-1887,-1737,-1736,-1754,-1807,-1806,-1783,-1782,-1781,-1749,1934,9903,2006,9904,2008,2118,2165,2177,9905,2173,2287]]]},{"type":"MultiPolygon","id":24,"arcs":[[[4166]],[[4781,4782,4856,4857,4858,4690,4581,4268,4408,4169,4080,3824,3834,3840,4088,4192,9906,4194,4521,4293,4613,4731,4615,4295,4296,4114,4115,4116,3849,3811,3812,3813,3814,3816,3817,3818,3820,3821,3822,-3547,-3461,-3460,-3445,-3537,-3536,-3458,-3457,-3584,-3583,-3501,-3500,-3499,-3449,-3448,-3498,-3756,-3755,4078,-4102,-4101,4266,-4398,-4397,-4396,4784]]]},{"type":"MultiPolygon","id":23,"arcs":[[[918]],[[919]],[[1288,1134,1392,1380,1390,1382,1537,1538,-1300,-889,889,890,891,817,567,360,813,920,599,1117,1282,1286,1391]]]},{"type":"MultiPolygon","id":26,"arcs":[[[9402]],[[9401]],[[771,820,9406,1075,1236,9407,1434,1560,1561,9408,1578,9409,1927,9410,9412,-2505,2280,2281,2292,2293,2294,2289,2290,2295,2296,2298,2299,2202,2203,2204,2151,1988,1785,1686,1533,1378,1225,1112,9404,9405,948,9400,9399]],[[9395]],[[9388,9389]],[[9390]],[[9377,446,9382,9384,491,9394,9907,9392,9396,9397,574,9385,712,713,714,639,640,587,588,589,590,476,477,478,479,480,481,482,483,484,434,9378]],[[9373,9379,9375]],[[9376]]]},{"type":"Polygon","id":27,"arcs":[[125,151,204,9366,9365,203,137,138,139,140,141,142,143,144,145,146,147,473,592,593,594,-667,794,-800,901,902,1050,-1075,1139,-1162,-1187,-1186,-1191,1370,-1423,1502,1503,1504,1511,1512,1499,1500,1501,1495,1496,1517,1518,1524,1525,1514,1515,1516,1492,1493,1489,1490,1336,-1220,1179,-1044,-1043,917,-893,840,-746,702,-551,546,415,-404,348,-318,218,-210,164,165,-55,60,106,9364]]},{"type":"Polygon","id":29,"arcs":[[-3119,-3243,-3242,3492,-3516,3684,-3752,-3751,3928,-4072,-4071,-4166,-4382,-4381,-4380,4520,4454,4455,-4728,-4727,-4937,-4936,-5126,5159,5349,5350,-5571,-5570,-5633,5706,5707,5708,5896,5897,5898,5899,-6100,6161,6162,5968,5969,5970,5971,5972,5865,5915,5916,5885,5886,5887,5715,5929,5930,5931,5918,5919,5920,5815,5841,5842,5935,5936,-5766,5719,-5557,5543,-5302,-5301,5040,5041,-4821,4771,-4547,4500,-4347,4197,4198,4045,4001,4002,-3885,3761,-3665,3560,3491,-3487,-3304,3273,-3129,-3070,-3074,-3073,-3077,-3076,-3087,-3086,-3090,-3089,-3093,-3092,-3095,-3094,-3083,-3082,-3080,-3079]]},{"type":"Polygon","id":28,"arcs":[[-6755,-6754,-6938,6953,6950,-7180,7256,-7303,7420,-7449,7598,-7679,7825,-7956,-7955,8127,-8251,-8250,8435,-8522,8619,-8704,-8703,8838,8861,8880,-8847,8769,8770,8631,8664,8662,8663,8658,8659,8660,8645,8646,-8493,-8492,-8277,-8276,-8275,8105,8106,8107,8108,-7944,7927,-7664,-7663,7413,7414,-7134,-7133,7026,-7016,-6737,-6736,-6768,-6767,-6772,-6771,-6743,-6742,-6741,-6773]]},{"type":"Polygon","id":30,"arcs":[[43,99,88,46,50,29,9,53,110,-93,108,-133,221,-231,373,-381,514,515,516,675,676,677,763,764,765,697,698,826,827,655,649,650,651,757,734,735,736,527,-519,327,328,349,-241,201,-112,35,-21,36,19]]},{"type":"MultiPolygon","id":37,"arcs":[[[-5845,6042,6259,6044]],[[6047,6086,6179,6194,6038,6056,6247,6431,6425,6582,6258,6583,6564,6492,6560,6804,6761,6831,6759,6893,6990,7112,7271,9920,7273,7279,7280,7281,7282,7252,7005,7006,7007,6928,6877,6846,6847,6852,6853,6676,6677,6756,6645,6646,6630,6631,6768,6769,6695,6749,6750,6751,6671,6672,6801,6883,6884,6885,6821,6822,6823,-6595,-6594,-6457,-6456,-6392,-6391,-6287,-6286,-6285,6174,-6175,-6174,-6233,-6232,-6231,-6084,-6083,-5987,-5986,-5985,-5928,-5927,-5926,-5854,-5896,-5895,-5911,-5910,-5674,-5673,-5963,-5671,-5714,-5713,-5712,-5882,-5881,-5880,-5725,-5724,-5876,-5805,-5804,-5803,-5871,-5870,-5908,-5907,-5849,6041]]]},{"type":"Polygon","id":38,"arcs":[[54,-166,-165,209,-219,317,-349,403,-416,-547,550,551,627,628,633,634,631,632,530,583,623,624,625,635,-516,-515,380,-374,230,-222,132,-109,92,93,98,103,40,32,64,68,57]]},{"type":"Polygon","id":31,"arcs":[[-1710,-1552,-1682,-1681,-1672,-1671,-1812,-1810,-1809,-1839,-1838,-1836,-1835,-2067,-2066,2190,-2208,2303,-2417,2542,-2661,-2660,2828,-2881,2934,-3071,3128,-3274,3303,3486,3487,3488,3489,3484,3485,3315,3316,3426,3425,3415,3416,3408,3409,3411,3412,3431,3432,3421,3422,3427,3428,3430,3418,3419,-3364,3193,-3140,2970,2971,2822,2700,2701,2731,2732,-2551,2541,-2048,-2047,1861,-1644,-1684,-1683,-1574,-1573,-1709]]},{"type":"Polygon","id":33,"arcs":[[-890,888,1299,-1539,1615,1747,1748,1780,1781,1782,1805,1806,-1753,1614,-1456,1269,1270,-1115,-996,899,900,-891]]},{"type":"Polygon","id":34,"arcs":[[9921,3944,3785,3786,3787,3788,3789,3726,3668,3524,3525,-3254,-3253,3124,2911,2912,-2814,2756,-2603,-2568,-2567,-2770,-2769,2897,3116,3066,3156,3256,3346,3531,3521,3805,4124,3946]]},{"type":"Polygon","id":35,"arcs":[[5784,5785,5786,-6372,6554,6555,-6863,7008,7009,7156,7157,-7563,7653,7654,7655,7656,7657,7967,7968,7969,7783,7784,7785,7888,7889,8111,8037,8038,-7593,-7592,7176,-5782,-5781,-5780,5778,-5322,-5324,-5498,-5497,-5512,-5511,-5309,-5308,-5209,-5208,-5320]]},{"type":"Polygon","id":32,"arcs":[[-2023,-2357,2361,-2921,3561,3562,-3998,4621,4622,4623,4624,5911,5912,-5467,4254,4772,-4569,4323,3801,4307,4308,4309,-4131,-4130,2391,2392,2393,2394,-2384,-1603,-1429,-1428,-1264,-1582,-1581,-1897]]},{"type":"MultiPolygon","id":36,"arcs":[[[3237]],[[3052,9922,3054,3119,3152,3121,3056,2782]],[[1826]],[[3046,3097,3048,2751,2629,2770,2768,2769,2566,2567,2568,2324,2325,2093,2153,2154,2156,2157,2188,2189,2056,2057,2088,2089,2085,2086,2060,2061,2062,1831,1716,1713,1721,1724,1701,1570,1279,9923,1281,1275,1276,1019,1031,1005,-1001,1002,-1132,1204,-1296,-1485,1544,-1738,1886,-2000,-1999,2283,2284,-2550,-2549,2752]]]},{"type":"Polygon","id":39,"arcs":[[2397,-2447,2666,-2675,2898,-2906,3033,3034,-3246,3262,3263,-3520,3528,-3609,3737,3738,3893,3894,3895,3992,-4069,4228,4226,4362,4363,4494,4495,4496,4497,4371,4372,4349,4350,4180,4181,4158,4159,4160,4135,4136,4137,4138,3931,-3809,3715,-3615,3405,3406,-3292,3167,-3040,3018,-2795,-2794,2716,-2622,2538,-2471,-2294,-2293,-2282,-2281,2504,2505,2506,2507,2508,2509,2514,9926,2516,2669,2593,2594,2595,2636,2579,2443,2401,-2200]]},{"type":"Polygon","id":40,"arcs":[[-5458,-5457,-5530,-5529,-5461,-5460,-5528,-5527,-5451,-5450,-5448,-5447,-5594,-5593,-5525,-5524,-5533,-5532,-5559,-5558,-5720,5765,-5937,5945,-6118,-6257,6315,-6551,6610,-6728,6773,6774,-7119,7231,7232,7233,7234,7401,7402,7398,7399,7400,7380,7440,7441,7442,7337,7338,7229,7230,7139,7140,7035,7036,6930,6931,-6871,6691,-6627,6398,-6366,6016,6017,5798,5799,5794,5795,5796,5791,5792,-5785,-5319,-5518,-5517,-5520,-5522,-5521,-5454]]},{"type":"Polygon","id":41,"arcs":[[661,-608,658,-691,-604,-603,-696,-695,-694,-693,-692,-503,-502,-557,-555,-554,-513,-583,-536,-523,708,-915,969,-1092,1260,1261,1262,1263,1427,1428,1602,1603,1599,1600,1881,1973,1974,1892,1893,1611,1464,1298,980,776,622]]},{"type":"Polygon","id":42,"arcs":[[-2061,-2087,-2086,-2090,-2089,-2058,-2057,-2190,-2189,-2158,-2157,-2155,-2154,-2094,-2326,-2325,-2569,2602,-2757,2813,-2913,-2912,-3125,3252,3253,-3526,3553,3584,3585,-3757,3587,3496,3497,3447,3448,3498,3499,3500,3582,3583,3456,3457,3535,3536,3444,3459,3460,3546,3547,3548,3610,3611,-3610,3337,3338,3339,-3244,3106,-3034,2905,-2899,2674,-2667,2446,-2398,2199,2200,-2062]]},{"type":"MultiPolygon","id":44,"arcs":[[[2544]],[[2545,-2250]],[[-2253,-2252,2452,2321,2454,2553,-2527,2456,-2319,-2318,-2016,-2169]]]},{"type":"MultiPolygon","id":45,"arcs":[[[8077]],[[8079]],[[8080]],[[7693,9413,9930,9415,7862,8081,8046,9931,8048,9932,8050,8078,8053,8054,8055,8056,7898,-7892,7866,7867,-7822,7710,7540,7541,7482,7483,7436,7437,-7340,7255,7059,7060,6919,6920,6921,6922,6923,-6672,-6752,-6751,-6750,-6696,-6770,-6769,-6632,-6631,-6647,-6646,-6757,-6678,-6677,-6854,-6853,-6848,-6847,-6878,-6929,-7008,-7007,-7006,-7253,-7283,7328,7588,9416]]]},{"type":"Polygon","id":46,"arcs":[[-624,-584,-531,-633,-632,-635,-634,-629,-628,-552,-703,745,-841,892,-918,1042,1043,-1180,1219,-1337,-1491,1520,-1652,1659,-1757,1832,1833,1834,1835,1837,1838,1808,1809,1811,1670,1671,1680,1681,1551,1709,1708,1572,1573,1682,1683,-1643,1487,-1374,1248,1183,-1033,939,-677,-676,-517,-636,-626,-625]]},{"type":"Polygon","id":47,"arcs":[[-5660,-5659,-5700,-5699,-5697,-5893,-5892,-5840,-5839,-5915,-5914,-5836,-5884,-5883,-5809,-5808,-5826,-5825,-5821,-5820,-5828,-5879,-5878,-5891,-5890,-5889,-5859,-5983,-5863,-5862,-5929,5984,5985,5986,6082,6083,6230,6231,6232,6173,6174,-6175,6284,6285,6286,6390,6391,6455,6456,6593,6594,-6824,6826,6827,6793,6794,6718,6719,6720,6805,6806,6790,6791,6784,6785,6723,6724,6713,6698,6752,6753,6754,6772,6740,6741,6742,6770,6771,6766,6767,-6735,6604,-6410,-6409,-6408,-6407,6273,-6162,6099,-5900,-6101,-5898,-5959,-5958,-5934,-5933,-5833,-5832,-5939,-5938,-5738]]},{"type":"MultiPolygon","id":48,"arcs":[[[9933]],[[9311,9312]],[[9313,9290,9934,9292,9314]],[[9271]],[[9242]],[[9935,9214]],[[9128]],[[9132,9175,9936,9164,9217,9166,9937,9168,9179,9216,9227,9247,9938,9249,9229,9243,9254,9246,9255,9939,9257,9273,9293,9318,9347,9352,9345,9342,9310,9253,9190,9127,8977,8872,8869,8883,8392,8394,-7889,-7786,-7785,-7784,-7970,-7969,-7968,-7658,-7657,-7656,-7655,-7654,7562,-7158,-7157,-7010,-7009,6862,-6556,-6555,6371,-5787,-5786,-5793,-5792,-5797,-5796,-5795,-5800,-5799,-6018,-6017,6365,-6399,6626,-6692,6870,-6932,-6931,-7037,-7036,-7141,-7140,-7231,-7230,-7339,-7338,-7443,-7442,-7441,-7381,-7401,-7400,-7399,-7403,-7402,-7235,-7234,-7514,7610,-7646,7819,-7909,-7908,-7907,8222,-8238,8405,-8458,-8457,-8650,8691,8692,-8943,8994,9016,9017,9090,9131,9092,9020,9130]]]},{"type":"Polygon","id":49,"arcs":[[-1732,2343,-2621,-2196,-2195,-2983,-2982,3098,3099,-3578,4025,-4629,4748,4749,4750,4751,4752,4753,5415,5416,5342,-4624,-4623,-4622,3997,-3563,-3562,2920,-2362,2356,-2022,-2109,-2108,-2142,-2053]]},{"type":"MultiPolygon","id":51,"arcs":[[[5405,5058,-4782,5059]],[[5005,4865,4807,4982,5225,5370,5424,5372,5109,5148,5330,5473,5536,9940,5683,5638,5472,5431,5282,5394,9941,5581,5626,5665,5871,9942,5902,9943,9944,5905,5850,5823,5851,5844,5845,5846,5847,5848,5906,5907,5869,5870,5802,5803,5804,5875,5723,5724,5879,5880,5881,5711,5712,5713,5670,5962,5672,5673,5909,5910,5894,5895,5853,5925,5926,5927,5928,5861,5862,5982,5858,5888,5889,5890,5877,5878,-5827,-5727,5642,-5612,-5236,-5235,-5234,-5101,-5412,-5411,-5378,-5377,-5376,-5180,-5275,-5274,5122,-4883,4877,-4536,-4535,-4403,-4402,-4401,-4189,-4188,4043,-3999,-3865,-3910,-4025,-4024,-3812,-3850,-4117,-4116,-4387,4417,9945,4342,4409,4669,4810,4866,5063,5188]]]},{"type":"Polygon","id":50,"arcs":[[1008,998,-900,995,1114,-1271,-1270,1455,-1615,1752,1753,1735,1736,1737,-1545,1484,1295,-1205,1131,-1003,1000,1001,994]]},{"type":"MultiPolygon","id":53,"arcs":[[[303]],[[271]],[[184]],[[118]],[[119]],[[120]],[[126,2,3,75,85,80,83,-23,81,-115,246,247,-365,394,395,-537,582,512,553,554,556,501,502,691,692,693,694,695,602,603,690,-659,607,608,610,467,343,233,186,187,234,319,276,371,273,372,321,407,370,307,196,183,197,129]]]},{"type":"MultiPolygon","id":55,"arcs":[[[9372]],[[9371,561,557,-484,-483,-482,-481,-480,-479,-478,-477,-591,-590,-589,-588,-641,-640,-715,9386,879,9949,881,1155,1167,9387,1165,1289,1474,1623,1800,1960,2027,2028,2029,1958,1959,1951,1952,1946,1947,1966,1967,1777,1778,1779,1695,-1655,1565,-1503,1422,-1371,1190,1185,1186,1161,-1140,1074,-1051,-903,-902,799,-795,666,-595,-594,-593,-474,-148,9368,9370]]]},{"type":"Polygon","id":54,"arcs":[[3243,-3340,-3339,-3338,3609,-3612,-3611,-3549,-3548,-3823,-3822,-3821,-3819,-3818,-3817,-3815,-3814,-3813,4023,4024,3909,3864,3998,-4044,4187,4188,4400,4401,4402,4534,4535,-4878,4882,-5123,5273,5274,5179,5375,5376,5377,5410,5411,5100,5101,5102,4803,4804,-4745,-4496,-4495,-4364,-4363,-4227,-4229,4068,-3993,-3896,-3895,-3894,-3739,-3738,3608,-3529,3519,-3264,-3263,3245,-3035,-3107]]},{"type":"Polygon","id":56,"arcs":[[-827,-699,-698,-766,-765,-764,-678,-940,1032,-1184,-1249,1373,-1488,1642,1643,-1862,2046,2047,-2542,2550,2551,2552,2139,2140,2134,2135,2136,2193,2194,2195,2620,-2344,1731,1732,1733,-1593,1172,1173,-1122,-651,-650,-656,-828]]},{"type":"MultiPolygon","id":78,"arcs":[[[9867]],[[9840]]]}]},"land":{"type":"MultiPolygon","arcs":[[[9361]],[[9933]],[[9338]],[[9311,9312]],[[9313,9290,9934,9292,9314]],[[9271]],[[9242]],[[9935,9214]],[[9128]],[[9108]],[[9088]],[[9046]],[[9063]],[[9882,9883]],[[9028]],[[8993,8905]],[[8704]],[[9893,8606]],[[8522]],[[8333]],[[8283]],[[8284]],[[8077]],[[8079]],[[8080]],[[7053]],[[7019]],[[7054]],[[6896]],[[6897]],[[4166]],[[3237]],[[3052,9922,3054,3119,3152,3121,3056,2782]],[[2727]],[[2632]],[[2544]],[[1826]],[[918]],[[919]],[[9402]],[[9401]],[[9395]],[[9388,9389]],[[9390]],[[9372]],[[9373,9379,9375]],[[-9918]],[[1275,1276,1019,1031,1005,1001,994,1008,998,900,891,817,567,360,813,920,599,1117,1282,1286,1391,1288,1134,1392,1380,1390,1382,1537,1615,1747,1934,9903,2006,9904,2008,2118,2165,2177,9905,2173,2287,2175,2248,2545,2250,2452,2321,2454,2553,2527,2557,2559,9881,2561,2547,2752,3046,3097,3048,2751,2629,2770,2897,3116,3066,3156,3256,3346,3531,3521,3805,4124,3946,9921,3944,3785,9954,3787,9955,3789,3726,3668,3524,3553,3584,3752,4102,4392,9956,4394,4784,5059,5405,5058,4782,4856,9957,4858,4690,4581,4268,4408,4169,4080,3824,3834,3840,4088,4192,9906,4194,4521,4293,4613,4731,4615,4295,4385,4417,9945,4342,4409,4669,4810,4866,5063,5188,5005,4865,4807,4982,5225,5370,5424,5372,5109,5148,5330,5473,5536,9940,5683,5638,5472,5431,5282,5394,9941,5581,5626,5665,5871,9942,5902,9943,9944,5905,5850,5823,5851,6042,6259,6044,5845,9958,9959,9960,5847,6041,6047,6086,6179,6194,6038,6056,6247,6431,6425,6582,6258,6583,6564,6492,6560,6804,6761,6831,6759,6893,6990,7112,7271,9920,7273,7279,9961,7281,7328,7588,9416,7693,9413,9930,9415,7862,8081,8046,9931,8048,9932,8050,8078,8053,8285,8280,8336,8523,8607,9894,8609,8705,8835,8906,9013,9099,9139,9161,9152,9098,8991,8903,8992,9888,8988,9116,9153,9204,9889,9206,9146,9200,9276,9296,9890,9298,9326,9891,9322,9892,9324,9332,9353,9360,9884,9356,9885,9358,9362,9351,9340,9331,9302,9329,9304,9285,9264,9262,9235,9221,9193,9886,9137,9363,8968,8865,8969,9065,9006,9064,9008,8913,9009,8915,8804,9887,8806,8797,8790,8787,8676,9880,8678,8701,8838,8861,8880,8847,9010,9027,9899,9025,9089,9003,9084,9900,9078,9901,9080,9902,9082,9109,9070,9049,9037,9060,9017,9090,9131,9092,9020,9130,9132,9175,9936,9164,9217,9166,9937,9168,9179,9216,9227,9247,9938,9249,9229,9243,9254,9246,9255,9939,9257,9273,9293,9318,9347,9352,9345,9342,9310,9253,9190,9127,8977,8872,8869,8883,8392,8394,7889,8111,8037,8208,8504,8191,7725,7740,7697,7519,7056,7020,6899,6528,5874,5606,5291,5193,5288,5438,5155,5018,4853,4552,4697,4472,4696,4474,4482,4847,4484,3626,2685,2373,1893,1611,1464,1298,980,776,622,661,608,610,467,343,233,186,9946,9962,9948,3,75,85,80,83,23,36,19,43,99,88,46,50,29,9,53,110,93,98,103,40,32,64,68,57,60,106,9364,125,151,204,9366,9365,203,137,138,139,140,141,142,143,144,145,146,9368,9370,9371,561,557,484,434,9378,9377,446,9382,9384,491,9394,9907,9392,9396,9397,574,9385,712,713,9386,879,9949,881,1155,1167,9387,1165,1289,1474,1623,1800,1960,2027,2116,2233,2234,2533,2535,2467,2204,2151,1988,1785,1686,1533,1378,1225,1112,9404,9405,948,9400,9399,771,820,9406,1075,1236,9407,1434,1560,1561,9408,1578,9409,1927,9410,9412,2505,-9912,2507,-9910,2509,2514,9926,2516,2669,2593,2594,2595,2636,2579,2443,2401,2200,2062,1831,1716,1713,1721,1724,1701,1570,1279,9923,1281]],[[9612,9619,9600,9610,9536,9547,9542,9545,9501,9528,9516,9644,9518,9525,9874,9527,9503,9875,9505,9543,9508,9544,9540,9546,9538,9611,9602,9606,9596,9607,9591,9608,9581,9571,9609,9872,9493,9479,9465,9873,9463,9480,9468,9481,9475,9567,9471,9568,9559,9628,9624,9622]],[[9489]],[[9626]],[[9552,9521,9870,9523,9551,9533]],[[9561]],[[9499,9529,9871,9531,9497,9534]],[[9869,9550]],[[9455]],[[9515]],[[9447]],[[9563]],[[9514]],[[9491]],[[9445]],[[9555]],[[9554]],[[9440]],[[9586]],[[9510]],[[9444]],[[9434]],[[9513]],[[9512]],[[9588]],[[9511]],[[9431]],[[9548]],[[9642]],[[9460]],[[9438]],[[9562]],[[9432]],[[9470]],[[9451]],[[9589]],[[9487]],[[9458]],[[9426]],[[9553]],[[9437]],[[9524]],[[9492]],[[9500]],[[9441]],[[9435]],[[9490]],[[9592]],[[9439]],[[9625]],[[9486]],[[9485]],[[9488]],[[9442]],[[9452]],[[9443]],[[9597]],[[9454]],[[9436]],[[9453]],[[9585]],[[9483]],[[9449]],[[9428]],[[9456]],[[9446]],[[9620]],[[9603]],[[9459]],[[9448]],[[9457]],[[9450]],[[9594]],[[9583]],[[9584]],[[9582]],[[9476]],[[9587]],[[9605]],[[9604]],[[9484]],[[9627]],[[9461]],[[9560]],[[9429]],[[9427]],[[9433]],[[9593]],[[9430]],[[9629]],[[9630]],[[9631]],[[9632]],[[9633]],[[9634]],[[9466]],[[9635]],[[9636]],[[9637]],[[9638]],[[9639]],[[9640]],[[9509]],[[9467]],[[9641]],[[9643]],[[9867]],[[9866]],[[9840]],[[9813,9697,9666,9723,9817,9718,9726,9759,9868,9839,9877,9737,9820,9731,9847,9853,9844,9863,9864,9855,9865,9834,9848,9782,9878,9822,9803,9689,9746,9830,9705,9790,9823,9668,9768,9779,9660,9761,9879,9654,9788,9832]],[[9417]],[[9422]],[[9421]],[[9423]],[[9876,9420]],[[9424]],[[9425]],[[9418]]]}},"arcs":[[[162416,583189],[236,-863],[94,-3199],[220,-1079],[-271,-1241]],[[162695,576807],[-443,-309],[-4396,83]],[[157856,576581],[-6,1800],[-436,606],[-476,3062],[167,1173]],[[157105,583222],[2782,-89],[2529,56]],[[203483,583173],[-111,-3268],[363,0],[1,-4838],[604,-14]],[[204340,575053],[0,-6406],[-109,-3],[-1,-3886]],[[204230,564758],[-1074,300],[-478,661],[-49,-1946]],[[202629,563773],[-411,-2654],[-860,-2419],[-1043,-435],[-462,561]],[[199853,558826],[29,9804],[98,1617],[303,-7],[-114,3279],[302,529],[-36,7508],[72,1628]],[[200507,583184],[2976,-11]],[[181317,583162],[311,-3373],[318,707],[389,-2530],[-133,-1970],[788,-1235],[-38,-1636],[347,-662],[20,-2123]],[[183319,570340],[343,-1416],[-25,-1101],[608,-791]],[[184245,567032],[386,-2907],[-296,-416]],[[184335,563709],[-284,-667],[56,-2637],[-277,-1038],[59,-2316]],[[183889,557051],[-896,68]],[[182993,557119],[-468,-2]],[[182525,557117],[101,1562],[-233,2072],[31,1860],[-477,1550],[-122,1357],[-537,-515],[111,-1134],[-1263,-12],[41,-3203],[-354,-5]],[[179823,560649],[-1,1601],[-1073,-52],[-59,2655]],[[178690,564853],[-26,3920],[480,4],[-5,6466],[-109,1581],[659,10],[112,1730],[-222,1681],[-105,2947]],[[179474,583192],[1843,-30]],[[175797,583199],[0,-9287]],[[175797,573912],[-2052,-28],[-1,6456],[-689,-16]],[[173055,580324],[0,2844]],[[173055,583168],[2742,31]],[[199853,558826],[-98,-1296],[-681,144],[-597,-3335]],[[198477,554339],[-62,2205],[-374,544],[-690,-278]],[[197351,556810],[-1278,1156],[-333,1655]],[[195740,559621],[8,3507],[732,-36],[49,1307],[484,-269],[0,4521],[87,4167]],[[197100,572818],[159,-390],[75,5957],[166,-2],[64,4793]],[[197564,583176],[2943,8]],[[221924,574709],[-1820,8]],[[220104,574717],[-102,3252],[0,5201]],[[220002,583170],[1830,2]],[[221832,583172],[3,-5232],[89,-3231]],[[178690,564853],[-400,43],[-118,-1893],[-228,-480],[-676,326],[-48,1381],[-615,5245],[-698,-1923],[-112,1018]],[[175795,568570],[2,5342]],[[175797,583199],[3677,-7]],[[220104,574717],[-363,-10]],[[219741,574707],[-362,-1],[-1,1624],[-1819,10],[0,-1619]],[[217559,574721],[-1091,11],[0,3239],[-126,10],[-1,5192]],[[216341,583173],[3661,-3]],[[186539,583158],[25,-9701]],[[186564,573457],[-1094,108],[-9,-3230],[-2142,5]],[[181317,583162],[5222,-4]],[[193948,567081],[-538,-46],[0,1653],[-330,-43],[0,1617],[-2172,10],[0,-1617],[-363,1]],[[190545,568656],[35,14501]],[[190580,583157],[3492,32]],[[194072,583189],[-44,-7981],[115,-2218],[-242,-2960],[47,-2949]],[[195740,559621],[-133,1040],[-934,55],[-434,-1362],[-302,351]],[[193937,559705],[11,7376]],[[194072,583189],[3492,-13]],[[206421,583169],[53,-6498],[181,-1619]],[[206655,575052],[-2315,1]],[[203483,583173],[2938,-4]],[[228232,583191],[206,-4516],[166,-1312],[-187,-2686]],[[228417,574677],[-2130,0]],[[226287,574677],[-64,8514]],[[226223,583191],[2009,0]],[[230526,583184],[49,-8486]],[[230575,574698],[-2158,-21]],[[228232,583191],[2294,-7]],[[223380,574698],[-1,-3220],[-639,-11]],[[222740,571467],[-816,5]],[[221924,571472],[0,3237]],[[221832,583172],[1466,10]],[[223298,583182],[0,-5247],[82,-3237]],[[226287,574677],[-1090,12]],[[225197,574689],[-1817,9]],[[223298,583182],[2925,9]],[[168030,583185],[-42,-19407]],[[167988,563778],[-338,-262]],[[167650,563516],[-25,343]],[[167625,563859],[88,1056],[-308,2218],[-251,148],[-798,-1281],[-586,-1450],[-104,1725],[-570,-648],[55,-1799]],[[165151,563828],[-497,4],[-263,1945],[-611,1931],[-82,1381],[-532,1417],[-331,3957]],[[162835,574463],[-140,2344]],[[162416,583189],[2209,-15],[3405,11]],[[171951,583192],[-373,-3049],[-183,-3367],[361,7],[-7,-9706],[182,-1632]],[[171931,565445],[-274,12],[-1,-4693],[-436,1680],[-359,-1132]],[[170861,561312],[-166,418],[-548,-890],[-371,2707],[-364,-947]],[[169412,562600],[-132,2358],[405,757],[-4,1210],[289,2381],[-187,3288],[316,4420],[-331,3951],[45,2219]],[[169813,583184],[2138,8]],[[173055,580324],[-26,-14910]],[[173029,565414],[-1098,31]],[[171951,583192],[1104,-24]],[[169412,562600],[-45,-983],[-485,620],[-109,1069],[-446,-598],[-339,1070]],[[168030,583185],[1783,-1]],[[188725,567028],[0,1624]],[[188725,568652],[374,0],[11,14480]],[[189110,583132],[1470,25]],[[190545,568656],[-200,-1611],[-1620,-17]],[[212324,583170],[-3,-5175],[150,-1639]],[[212471,576356],[-3238,15]],[[209233,576371],[-1,6810]],[[209232,583181],[3092,-11]],[[214880,583162],[-1,-3570]],[[214879,579592],[-364,-1],[-1,-1615],[-226,0],[0,-3239]],[[214288,574737],[-1816,1]],[[212472,574738],[-1,1618]],[[212324,583170],[2556,-8]],[[186539,583158],[2571,-26]],[[188725,568652],[-718,-1],[2,2422],[-362,804],[-543,30],[-81,1075],[-459,475]],[[217559,574721],[0,-1621]],[[217559,573100],[-2179,21],[-1,4852],[-499,6],[-1,1613]],[[214880,583162],[1461,11]],[[233488,574623],[-724,-26]],[[232764,574597],[-2189,101]],[[230526,583184],[3019,-21]],[[233545,583163],[240,-2188],[402,728],[5,-3862],[-704,-3],[0,-3215]],[[209233,576371],[4,-4555]],[[209237,571816],[-1620,-4],[1,1622],[-363,4],[0,1612],[-600,2]],[[206421,583169],[2811,12]],[[175795,568570],[0,-4401]],[[175795,564169],[-503,1656],[-278,-929],[18,-2373]],[[175032,562523],[-508,-3],[3,1885],[-1501,-262]],[[173026,564143],[3,1271]],[[217559,573100],[127,-1619],[-2,-6492],[124,-2],[-3,-3238]],[[217805,561749],[-2509,-20]],[[215296,561729],[1,3269],[-141,0],[0,6485],[-143,10],[0,3259],[-725,-15]],[[156776,574866],[145,-2337],[-336,700],[191,1637]],[[156018,576188],[367,-1161],[-54,-1921],[-274,763],[-39,2319]],[[156711,577874],[437,-1003],[-572,-1182],[-174,1060],[309,1125]],[[239768,576252],[-26,-7172],[46,-6528]],[[239788,562552],[-1935,133],[-2,-978],[-1789,-11]],[[236062,561696],[-29,9709]],[[236033,571405],[-4,6210]],[[236029,577615],[339,173],[235,-1132],[1047,-347],[154,-2131],[907,561],[6,841],[719,948],[332,-276]],[[157403,572219],[-142,1501],[248,575],[362,-1247],[-15,3533]],[[162835,574463],[-341,252],[-585,-931],[-106,-3299],[197,-417]],[[162000,570068],[-3852,30]],[[158148,570098],[-745,2121]],[[212472,574738],[0,-3235],[160,6],[-1,-4641]],[[212631,566868],[-1041,215],[-260,-1783],[-494,-878],[-372,2570],[-383,-709],[-219,-2110],[-618,322]],[[209244,564495],[-7,7321]],[[219741,574707],[109,-3230],[1,-6504],[110,0],[-1,-3243]],[[219960,561730],[-1079,-3]],[[218881,561727],[-1076,22]],[[243362,568344],[29,-12216],[-17,-11280]],[[243374,544848],[-834,-2840],[212,-1505]],[[242752,540503],[-2,-7]],[[242750,540496],[-224,823]],[[242526,541319],[-10,0]],[[242516,541319],[-187,-592]],[[242329,540727],[-9,7]],[[242320,540734],[-75,-212]],[[242245,540522],[-5,-38]],[[242240,540484],[-9,-872]],[[242231,539612],[3,11]],[[242234,539623],[-248,57]],[[241986,539680],[-26,1879],[-2117,44]],[[239843,541603],[15,4838]],[[239858,546441],[-15,12915],[-55,3196]],[[239768,576252],[1003,-1644],[260,59],[-216,-1483],[572,-280],[382,-4206],[280,489],[20,1987],[577,82],[135,-1750],[581,-1162]],[[209244,564495],[-258,1115],[-593,-179],[-307,1362],[-1057,19],[-145,560],[-288,-1583],[-558,-1]],[[206038,565788],[-93,498],[-752,103],[-963,-1631]],[[165151,563828],[-359,-3363],[-516,-331],[-139,-2995],[-195,-1515],[49,-2367],[561,-1042],[-25,-1385]],[[164527,550830],[-822,-18],[-768,1529],[-383,1474],[-339,202],[-528,3049]],[[161687,557066],[129,2030],[-144,1373]],[[161672,560469],[135,875],[-307,1331],[76,2648],[386,644],[303,1650],[-265,2451]],[[215296,561729],[-1431,3],[-2,-1673]],[[213863,560059],[-714,1226]],[[213149,561285],[-15,1450],[265,1806],[-308,1452],[-460,875]],[[232764,574597],[24,-6811]],[[232788,567786],[-2527,21]],[[230261,567807],[-1799,-6]],[[228462,567801],[14,367]],[[228476,568168],[71,2441],[-130,4068]],[[221924,571472],[-986,7],[-3,-6507],[100,-3249]],[[221035,561723],[-716,0]],[[220319,561723],[-359,7]],[[225197,574689],[73,-6488]],[[225270,568201],[-362,-8],[-2,-3234],[-287,1],[0,-1975]],[[224619,562985],[-263,25],[-571,2718],[-293,-1216],[-752,3114],[0,3841]],[[228476,568168],[-2123,27]],[[226353,568195],[-1083,6]],[[236062,561696],[6,-7465]],[[236068,554231],[0,-615],[-707,-32]],[[235361,553584],[-1430,50]],[[233931,553634],[-29,8560],[-224,1564],[125,1180],[-986,11]],[[232817,564949],[-29,2837]],[[233488,574623],[363,-5],[3,-3239],[2179,26]],[[188725,567028],[1,-2703]],[[188726,564325],[-1602,-42],[-179,1901],[-363,820],[-2337,28]],[[158090,569229],[27,-389]],[[158117,568840],[-233,-1009],[277,-1641],[-369,861],[-55,2156],[353,22]],[[157568,571941],[243,-1776],[-417,-307],[-219,-1059],[350,-339],[185,-3598],[45,1502],[415,-1154],[-2,-2382],[-642,2318],[-20,2344],[-434,1258],[298,3296],[198,-103]],[[156634,565779],[-60,-3687],[-1564,-7],[0,264],[-3066,-4]],[[151944,562345],[-337,5365],[202,2998],[-202,971]],[[151607,571679],[463,-265],[484,-1494],[396,-424],[748,-1863],[763,58],[868,-995],[776,701],[529,-1618]],[[224619,562985],[0,-1281]],[[224619,561704],[-2151,10]],[[222468,561714],[-1433,9]],[[193937,559705],[-803,-422],[-51,-3212],[-1027,-2365]],[[192056,553706],[-1187,-7]],[[190869,553699],[-361,552],[1,1469],[-266,4],[-517,1364],[204,1867],[-1205,-7]],[[188725,558948],[1,5377]],[[161672,560469],[-3555,-37]],[[158117,560432],[249,3191],[301,1598],[-407,1180],[-143,2439]],[[158090,569229],[58,869]],[[179823,560649],[58,-3498],[649,4],[-91,-2233],[238,251],[-154,-1949],[417,-649],[44,-4053]],[[180984,548522],[-292,-238],[-171,1188],[-551,1163],[-685,298]],[[179285,550933],[-478,836],[-38,1568],[-837,1677],[-963,-135]],[[176969,554879],[-354,1336],[183,799],[-95,1862],[-283,1075],[-625,4218]],[[245498,568091],[24,-13480]],[[245522,554611],[-1265,-6316],[-883,-3447]],[[243362,568344],[242,-1593],[425,-163],[-24,-1207],[886,751],[607,1959]],[[226353,568195],[59,-3242],[-1,-6490]],[[226411,558463],[-285,2]],[[226126,558465],[-1436,0]],[[224690,558465],[-71,3239]],[[228462,567801],[350,-5611],[344,-3679]],[[229156,558511],[-1603,-50]],[[227553,558461],[-1142,2]],[[230261,567807],[50,-3920]],[[230311,563887],[2,-2161],[360,-5],[2,-1615],[1435,5],[0,1629],[356,7],[-1,1624]],[[232465,563371],[355,-34]],[[232820,563337],[77,-8073]],[[232897,555264],[-1430,-21]],[[231467,555243],[-2182,14]],[[229285,555257],[-129,3254]],[[232817,564949],[3,-1612]],[[232465,563371],[-1,531],[-2153,-15]],[[209244,564495],[-2,-11147]],[[209242,553348],[-241,-806],[-805,9]],[[208196,552551],[-259,2],[1,3249],[-225,-5],[0,1619],[-1072,7],[1,1622],[-715,4],[0,1622]],[[205927,560671],[111,1440],[0,3677]],[[167625,563859],[-339,-5],[-301,-1380],[-1,-1619],[-240,-1347],[-120,-2159],[-533,-1577],[-2,-1607],[-955,-85],[-358,-2172],[-6,-1862]],[[164770,550046],[-243,784]],[[213149,561285],[0,-2790],[-1274,10],[2,-6436]],[[211877,552069],[-1581,12]],[[210296,552081],[-1055,15]],[[209241,552096],[1,1252]],[[156465,557234],[-1439,15],[0,-1663]],[[155026,555586],[-1512,9],[-857,298]],[[152657,555893],[-215,4015],[-498,2437]],[[156634,565779],[273,1293],[567,-4576],[-189,-396],[-251,-3033],[-105,2479],[-246,-3231],[-218,-1081]],[[188725,558948],[-718,-7],[0,-1612],[-713,3],[0,-1972],[-347,194]],[[186947,555554],[-644,-185],[-426,1863],[-409,506],[-656,-490],[-280,1681],[6,2406],[-203,2374]],[[205927,560671],[-357,-7],[-3,-4859],[-120,-3],[2,-6473]],[[205449,549329],[-1188,-10],[0,-1636],[-355,-3],[0,1641],[-354,-3]],[[203552,549318],[-232,0],[-1,3235],[-355,-6],[105,3251],[-2,6316],[-270,1],[-168,1658]],[[176969,554879],[-257,-1008],[473,-1408],[60,-942],[526,-680],[549,-2997],[263,-2458],[250,-686]],[[178833,544700],[-3820,28]],[[175013,544728],[0,1637]],[[175013,546365],[0,7286]],[[175013,553651],[19,8872]],[[182525,557117],[-825,-26],[-58,-2280],[224,-1574],[136,-3906],[-311,-808],[-707,-1]],[[173026,564143],[8,-11380]],[[173034,552763],[0,-1987]],[[173034,550776],[-2181,20]],[[170853,550796],[8,10516]],[[233931,553634],[40,-4845]],[[233971,548789],[-1065,-20]],[[232906,548769],[-9,6495]],[[175013,553651],[-715,16],[-120,-800],[-1144,-104]],[[203552,549318],[2,-5970]],[[203554,543348],[0,-266]],[[203554,543082],[-1766,-14],[-2,268],[-3263,-152]],[[198523,543184],[-142,489],[173,2154],[-209,2560],[-11,4245],[143,1707]],[[167650,563516],[-17,-12700]],[[167633,550816],[-9,-6518],[-1075,22],[-3,-3262]],[[166546,541058],[-233,-1081]],[[166313,539977],[-187,857],[-295,-1494],[-690,-306]],[[165141,539034],[-276,2022]],[[164865,541056],[127,1141],[-311,5065],[89,2784]],[[170853,550796],[-381,1]],[[170472,550797],[-2839,19]],[[186947,555554],[7,-6012],[711,-1191],[-2,-4005],[368,-11]],[[188031,544335],[35,-1328],[411,-1480]],[[188477,541527],[-392,-449],[14,-3150],[-430,-3]],[[187669,537925],[-672,2],[-784,-2757]],[[186213,535170],[46,3441],[-325,1748],[-347,-22],[-1,2529],[-723,-49],[-4,6432],[-731,40],[-1,5728],[-238,2034]],[[157723,558953],[152,-2103],[-263,244],[111,1859]],[[157679,553460],[-209,2]],[[157470,553462],[-20,-1]],[[157450,553461],[-465,10]],[[156985,553471],[3,2161],[-621,2]],[[156367,555634],[297,1874],[455,873],[193,2438],[440,2118],[103,-3144],[-199,149],[-132,-3407],[313,-1094],[-158,-1981]],[[239858,546441],[-2004,75]],[[237854,546516],[-6,4597],[-694,1379],[-255,2407],[-570,-1212],[-261,544]],[[218881,561727],[0,-3232],[-241,5],[-5,-6449]],[[218635,552051],[-211,-1],[0,-3173],[-602,-11]],[[217822,548866],[-251,2540],[-563,-513]],[[217008,550893],[-256,567],[-256,3108],[36,1844],[-852,-555],[-222,-665],[-1091,1453]],[[214367,556645],[-155,784],[-3,3078],[-346,-448]],[[224690,558465],[0,-1585]],[[224690,556880],[-2133,9]],[[222557,556889],[-89,4825]],[[222557,556889],[-2,-4848]],[[222555,552041],[-597,-4]],[[221958,552037],[-1539,4]],[[220419,552041],[1,6449],[-101,3233]],[[220419,552041],[-222,0]],[[220197,552041],[-1562,10]],[[214367,556645],[0,-4588],[170,0],[-1,-5915]],[[214536,546142],[-1061,-2],[0,-539],[-1412,-13]],[[212063,545588],[-1,6481],[-185,0]],[[197351,556810],[-6,-4998],[-770,-161],[-356,-670],[-49,-3204],[356,-6],[-61,-6487]],[[196465,541284],[-1055,22]],[[195410,541306],[-1057,8],[-1,-1068]],[[194352,540246],[-993,3]],[[193359,540249],[-236,2014],[209,1754],[-4,5386],[-1034,1610],[0,1620],[-238,1073]],[[208196,552551],[218,-2029],[102,-3785],[-297,-2572],[-534,-806]],[[207685,543359],[-1181,12],[-117,1073],[-355,1],[0,1079],[-356,1],[0,3802],[-227,2]],[[157999,553531],[-253,-1157],[151,3089],[102,-1932]],[[161687,557066],[-282,-523],[-596,-2846],[-98,-1164],[460,-3838],[-225,-1131]],[[160946,547564],[-1160,1608],[-293,-321]],[[159493,548851],[-540,718],[-133,1180],[-534,2],[-231,1165]],[[158055,551916],[258,528],[-269,4233],[225,530],[-269,1061],[117,2164]],[[190869,553699],[1,-1066],[-358,1],[-13,-4900],[354,-2],[-21,-5066]],[[190832,542666],[-725,2921],[-441,463],[-28,1539],[-216,-1426],[-1061,-216],[1,-1610],[-331,-2]],[[227553,558461],[-3,-6470],[62,-1591]],[[227612,550400],[-709,17]],[[226903,550417],[-711,10]],[[226192,550427],[-72,1593],[6,6445]],[[226192,550427],[-1411,-1]],[[224781,550426],[-88,1602]],[[224693,552028],[-3,4852]],[[229285,555257],[53,-4884]],[[229338,550373],[-1726,27]],[[156465,557234],[-98,-1600]],[[156985,553471],[-2,-837]],[[156983,552634],[-349,-1455],[-54,-1446],[-357,-1953]],[[156223,547780],[-354,-250]],[[155869,547530],[-801,-41],[-42,8097]],[[182993,557119],[1,-7838],[454,9],[0,-6466]],[[183448,542824],[-493,-548],[1,-1075],[-524,-539],[1,-1072],[-447,49]],[[181986,539639],[-1407,-5]],[[180579,539634],[-732,-499]],[[179847,539135],[-222,1916]],[[179625,541051],[352,195],[-48,4028],[320,550],[-90,1379],[-636,2137],[-238,1593]],[[186213,535170],[-690,-2887]],[[185523,532283],[-1333,17]],[[184190,532300],[-6,8737],[-214,-420],[-522,2207]],[[198523,543184],[180,-1780]],[[198703,541404],[-2238,-120]],[[164865,541056],[-1495,18],[1,1609],[-349,24],[-1,1604],[-1091,-12],[-177,1484],[-533,1821],[-274,-40]],[[224693,552028],[-2138,13]],[[217008,550893],[-1,-451],[-1411,-2],[0,-4839]],[[215596,545601],[-928,-4]],[[214668,545597],[-132,545]],[[155869,547530],[120,-1651],[-5,-3776]],[[155984,542103],[-587,-24]],[[155397,542079],[-2025,38]],[[153372,542117],[-110,2053],[253,-202],[623,1195],[-914,1191],[-230,4847],[-241,1362],[-96,3330]],[[232906,548769],[-1437,4]],[[231469,548773],[-2,6470]],[[231469,548773],[-356,-11]],[[231113,548762],[-1799,-4]],[[229314,548758],[24,1615]],[[179625,541051],[-761,2278],[-31,1371]],[[237854,546516],[-2,-4237]],[[237852,542279],[-1578,50],[42,-9604],[-40,-223]],[[236276,532502],[-231,1006],[-307,-538],[-332,854]],[[235406,533824],[-216,372]],[[235190,534196],[-133,486],[-22,7620]],[[235035,542302],[353,-11],[-27,11293]],[[310206,521389],[-674,-758]],[[309532,520631],[-1071,-1126],[-11,14936],[-1066,258]],[[307384,534699],[-8,3291],[-2505,26]],[[304871,538016],[-841,2]],[[304030,538018],[72,2263],[2153,14228],[505,-616],[6,-3382],[393,-1252],[815,1288],[76,747],[564,5],[41,1253],[358,12],[783,-2953],[455,-2410],[25,-20921],[-70,-4891]],[[193359,540249],[-620,1062],[-850,-749]],[[191889,540562],[-514,-598],[-543,2702]],[[175013,546365],[-1409,188],[-569,1764]],[[173035,548317],[-1,2459]],[[235035,542302],[-1048,14]],[[233987,542316],[-16,6473]],[[160946,547564],[-215,-3053],[-185,-928],[189,-1670]],[[160735,541913],[-845,11],[-206,-1040],[-1032,654]],[[158652,541538],[-352,1507],[-447,420],[-411,2054],[-166,2243]],[[157276,547762],[314,1630],[142,2452],[323,72]],[[157679,553460],[-93,-2779],[-243,609],[127,2172]],[[157450,553461],[-258,-1218],[-95,-3180],[-196,1415],[82,2156]],[[209241,552096],[0,-12823]],[[209241,539273],[-860,0],[-178,810],[-527,0]],[[207676,540083],[9,3276]],[[212063,545588],[-553,-13],[-2,-6518]],[[211508,539057],[-1052,-1]],[[210456,539056],[-1,6519],[-160,-2],[1,6508]],[[210456,539056],[-534,-4],[0,-1652],[-681,3]],[[209241,537403],[0,1870]],[[221958,552037],[-1,-6447],[97,4],[-9,-6501]],[[222045,539093],[-1299,23]],[[220746,539116],[-462,8]],[[220284,539124],[18,6480],[-110,4],[5,6433]],[[224781,550426],[0,-4857],[78,2],[0,-6489]],[[224859,539082],[-1664,-10]],[[223195,539072],[-1150,21]],[[220284,539124],[-1617,18]],[[218667,539142],[-333,973],[-96,2185],[-334,3330]],[[217904,545630],[153,619],[-235,2617]],[[217904,545630],[-2308,-29]],[[170472,550797],[5,-6432],[-150,-2317],[-550,-959]],[[169777,541089],[-3231,-31]],[[173035,548317],[0,-10901]],[[173035,537416],[-1,-2155]],[[173034,535261],[-435,-110],[-89,788]],[[172510,535939],[-70,1545],[-616,2886],[-769,-192],[-280,-1214]],[[170775,538964],[-894,-1263],[-120,595]],[[169761,538296],[16,2793]],[[226903,550417],[68,-4868],[-1,-6487]],[[226970,539062],[-981,13]],[[225989,539075],[-1130,7]],[[229314,548758],[6,-2652],[212,-1371],[-110,-2548],[10,-3117]],[[229432,539070],[-1342,-24]],[[228090,539046],[-1120,16]],[[158652,541538],[-2669,23],[1,542]],[[156223,547780],[371,1366],[-165,-1818],[270,11],[248,1911],[329,-1488]],[[207676,540083],[-352,-1344]],[[207324,538739],[-349,-4],[-351,-1338],[-710,7],[-585,557],[-127,1621],[-225,-2],[-117,3243],[-599,530],[-707,-5]],[[233987,542316],[-1,-1623]],[[233986,540693],[-2817,4]],[[231169,540697],[-56,8065]],[[231169,540697],[7,-1623],[-303,3]],[[230873,539077],[-1399,-8]],[[229474,539069],[-42,1]],[[175013,544728],[1,-5708],[-353,21]],[[174661,539041],[-461,-3],[-274,-1604],[-891,-18]],[[165141,539034],[20,-10944]],[[165161,528090],[-2024,0],[-2593,63]],[[160544,528153],[0,6416]],[[160544,534569],[359,11],[-157,2684],[251,3313],[-262,1336]],[[191889,540562],[-24,-9125]],[[191865,531437],[0,-675]],[[191865,530762],[-1399,153]],[[190466,530915],[-777,9]],[[189689,530924],[-178,1515],[195,2306],[-474,569],[-329,2182],[-426,4031]],[[239843,541603],[18,-6465]],[[239861,535138],[2,-4863]],[[239863,530275],[-1052,-76]],[[238811,530199],[4,1716],[-1019,-55]],[[237796,531860],[-43,6341],[94,109],[5,3969]],[[251183,535146],[2,-1610]],[[251185,533536],[-1045,-5],[1,3214],[-1046,-7],[1,1617],[-346,13],[-67,3221]],[[248683,541589],[281,995],[1035,453],[493,1189],[335,1615],[517,690]],[[251344,546531],[2,-4943],[190,-1609],[-348,15],[-5,-4848]],[[214668,545597],[3,-4901]],[[214671,540696],[-1,-1616]],[[214670,539080],[-2316,-18]],[[212354,539062],[-846,-5]],[[218667,539142],[262,-751],[-62,-3093]],[[218867,535298],[-369,-884],[-548,257],[-360,-2039]],[[217590,532632],[0,1615],[-698,1],[0,4816],[-1166,16],[-1,1620],[-1054,-4]],[[253820,544308],[0,-2728],[148,-1606],[-346,-13],[-2,-4817]],[[253620,535144],[-1568,10]],[[252052,535154],[1,8035],[354,0],[-1,1615],[281,8]],[[252687,544812],[-46,-3487],[634,3347],[545,-364]],[[179847,539135],[-2885,-2989],[-517,-3856],[-988,1959]],[[175457,534249],[-555,1774]],[[174902,536023],[-222,561],[-19,2457]],[[207324,538739],[0,-2429],[-481,-1],[56,-6431]],[[206899,529878],[-149,-3],[0,-6510],[-119,1]],[[206631,523366],[-3371,30]],[[203260,523396],[2,6487],[106,4],[-6,6446],[97,2],[-1,6474],[96,273]],[[203260,523396],[-118,-5],[-3,-8127],[-123,-27],[0,-3173]],[[203016,512064],[-1361,-4],[-1,3224],[108,-22],[0,5983],[-512,206],[100,2748],[-171,715]],[[201179,524914],[0,4963],[-191,-49],[-58,4866],[-348,2],[-60,1616],[-1571,-11]],[[198951,536301],[-119,265]],[[198832,536566],[-129,4838]],[[184190,532300],[-1,-814],[-687,-6],[15,-3299],[-666,-1967]],[[182851,526214],[-255,180],[-220,1599],[-323,48],[141,1392],[-197,1673],[207,1275],[-352,4420],[134,2838]],[[235190,534196],[-1183,4]],[[234007,534200],[-21,6493]],[[237796,531860],[-40,-1612]],[[237756,530248],[-1572,8],[92,2246]],[[155397,542079],[36,-7597]],[[155433,534482],[-1024,26],[-6,-1764]],[[154403,532744],[-413,-941],[-401,1402],[-173,-855],[164,5828],[40,-3886],[154,66],[163,3197],[-178,1201],[335,1945],[-445,-237],[-277,1653]],[[160544,534569],[-1998,-62]],[[158546,534507],[-2722,-5]],[[155824,534502],[-391,-20]],[[189689,530924],[-790,-90],[-193,-3456],[-681,-3145]],[[188025,524233],[-2,4020],[-344,10],[-10,9662]],[[241986,539680],[-2,-4580]],[[241984,535100],[-2123,38]],[[251185,533536],[-1,-4392]],[[251184,529144],[-281,766],[-2333,3002]],[[248570,532912],[1,-217]],[[248571,532695],[-1,217]],[[248570,532912],[-533,692],[-150,1914]],[[247887,535518],[-158,1305]],[[247729,536823],[-232,120]],[[247497,536943],[-117,450]],[[247380,537393],[-170,465]],[[247210,537858],[1092,2058],[381,1673]],[[198832,536566],[-2,-1875],[-409,1],[-280,-2448],[-812,5],[-234,-521],[0,-1926],[-1045,-3]],[[196050,529799],[2,2685],[-229,1641],[-121,3955],[-233,23],[-59,3203]],[[261640,541399],[0,-9499]],[[261640,531900],[-1747,-14]],[[259893,531886],[0,4842]],[[259893,536728],[1,3448]],[[259894,540176],[1066,-173],[680,1396]],[[196050,529799],[-402,-5]],[[195648,529794],[-1369,1],[-6,-1634]],[[194273,528161],[-522,16],[-137,3239]],[[193614,531416],[699,2],[39,8828]],[[193614,531416],[-1749,21]],[[169761,538296],[-37,88]],[[169724,538384],[-623,62],[-454,-1721],[-56,-1735],[-390,-2246],[-741,-1821]],[[167460,530923],[-633,1584],[-9,4567],[-505,2903]],[[167460,530923],[206,-1170],[-54,-2423]],[[167612,527330],[-388,-1247],[-851,-272]],[[166373,525811],[-390,134],[-275,-1277],[-551,-390]],[[165157,524278],[4,3812]],[[234007,534200],[29,-4880]],[[234036,529320],[-1739,14]],[[232297,529334],[-1380,20]],[[230917,529354],[-44,9723]],[[217590,532632],[-397,-2552],[-436,-1343],[-920,-1191],[-478,1151],[-416,-370]],[[214943,528327],[1,2829]],[[214944,531156],[-135,1457],[-12,6465],[-127,2]],[[172510,535939],[0,-949],[-531,-522],[0,-4866],[-168,2],[-1,-2312]],[[171810,527292],[-342,20]],[[171468,527312],[-26,6322],[-175,-16],[-173,2490],[-351,-8],[32,2864]],[[209241,537403],[-2,-4854]],[[209239,532549],[2,-6235]],[[209241,526314],[3,-1180]],[[209244,525134],[-252,2],[0,1618],[-714,26],[2,1575],[-351,-6],[0,1522],[-1030,7]],[[180579,539634],[-29,-2628],[-262,-4539],[-67,-3326],[-131,-1223],[266,-995],[5,-2349],[-249,-34],[-187,-1420],[189,-1949],[-181,-2072]],[[179933,519099],[-282,-1616],[-344,766],[-14,-1410],[527,-2617],[-250,-1838]],[[179570,512384],[-3571,-27],[-470,-1640]],[[175529,510717],[-390,422],[-163,2564],[-956,-4]],[[174020,513699],[362,4439],[258,2019],[-236,2563],[-686,1930]],[[173718,524650],[258,2608]],[[173976,527258],[446,46],[76,2435],[240,1363],[665,-892],[157,1127],[335,203],[-438,2709]],[[182851,526214],[3,-24]],[[182854,526190],[-34,-1070],[-471,-700],[-670,-2752]],[[181679,521668],[-944,-3979],[-194,-400],[-349,1946],[-259,-136]],[[220746,539116],[-2,-6512],[105,-7]],[[220849,532597],[-3,-6355]],[[220846,526242],[-1761,43]],[[219085,526285],[-327,3734],[198,1955],[-89,3324]],[[223195,539072],[-2,-6466]],[[223193,532606],[-2344,-9]],[[174902,536023],[1,-2306],[-958,-326],[31,-6133]],[[173718,524650],[-339,2599]],[[173379,527249],[-122,1925],[104,1310],[-372,3279],[45,1498]],[[212354,539062],[9,-6489]],[[212363,532573],[-197,-14]],[[212166,532559],[-2927,-10]],[[225989,539075],[-2,-6478]],[[225987,532597],[-2698,1]],[[223289,532598],[-96,8]],[[214944,531156],[-1391,13],[0,1436],[-1190,-32]],[[230917,529354],[-1,-1621]],[[230916,527733],[-866,4]],[[230050,527737],[45,1766],[-114,3993],[-380,2928],[-127,2645]],[[228090,539046],[-2,-6457]],[[228088,532589],[-2023,4]],[[226065,532593],[-78,4]],[[230050,527737],[35,-1611]],[[230085,526126],[-1852,-1]],[[228233,526125],[-87,1154],[-58,5310]],[[171468,527312],[-1045,34]],[[170423,527346],[-53,1]],[[170370,527347],[16,3854],[-350,1],[2,1631],[-349,-8],[35,5559]],[[170370,527347],[-2758,-17]],[[247207,537862],[3,-4]],[[248571,532695],[0,-5698],[-321,-1]],[[248250,526996],[-719,-11]],[[247531,526985],[-3,3230],[-343,-15],[1,1609],[-352,7],[6,6374]],[[246840,538190],[367,-328]],[[304871,538016],[-24,-11134],[233,-2084],[-240,-2015],[-258,-234],[344,-2135],[-221,-1593],[445,-9902],[739,1165]],[[305889,510084],[248,-6545]],[[306137,503539],[-330,592],[-241,-1132]],[[305566,502999],[-450,139],[79,-2294],[-907,625]],[[304288,501469],[-183,1244],[-102,3478],[-276,-236],[62,1778],[-139,3374],[-376,-344],[-347,651],[-372,9721]],[[302555,521135],[466,1247],[-87,1101],[431,1781],[-152,2424],[219,2371],[-153,838],[286,2948],[372,1242],[93,2931]],[[307384,534699],[-16,-13241],[-374,-415],[-14,-2790],[297,274],[227,-5338],[-292,-284],[68,-1531],[-1391,-1290]],[[188025,524233],[-403,-667]],[[187622,523566],[-350,1101],[-490,-286],[-233,-1732]],[[186549,522649],[-588,1549],[-293,6520],[-198,-55]],[[185470,530663],[53,1620]],[[259893,531886],[-1,-5144]],[[259892,526742],[-133,-900],[-413,823],[-698,-828],[-156,-2487],[-258,-538]],[[258234,522812],[-83,2590],[0,4873],[-349,11]],[[257802,530286],[0,3251],[697,-26],[0,3238],[1394,-21]],[[198951,536301],[351,-3235],[408,-2428],[-126,-2500]],[[199584,528138],[-454,7],[0,-1079],[-345,0],[-233,-1614],[-461,6],[-62,-7075],[-689,-1094],[-914,66]],[[196426,517355],[-551,2736]],[[195875,520091],[-174,1330],[10,5159],[-63,3214]],[[201179,524914],[-661,1614],[-693,2],[0,1610],[-241,-2]],[[173379,527249],[-1569,43]],[[219085,526285],[-4142,17]],[[214943,526302],[0,2025]],[[253620,535144],[-3,-3220]],[[253617,531924],[2,-6051]],[[253619,525873],[-776,1095]],[[252843,526968],[-385,757],[-418,-132]],[[252040,527593],[-694,1110]],[[251346,528703],[-162,441]],[[251183,535146],[869,8]],[[241984,535100],[-2,-4840]],[[241982,530260],[-158,-2623],[-854,-1719]],[[240970,525918],[-513,-3612]],[[240457,522306],[-838,12]],[[239619,522318],[-5,4660],[247,5],[2,3292]],[[309532,520631],[304,-6938],[-325,-226]],[[309511,513467],[-690,-626],[93,-2259],[-353,-313],[61,-1593],[-355,-304],[157,-4050],[-1023,-1483]],[[307401,502839],[-7,-12]],[[307394,502827],[-61,341],[-964,-1079],[-232,1450]],[[160544,528153],[-247,-4],[10,-4864],[241,-1079]],[[160548,522206],[-796,-345],[-318,-1062]],[[159434,520799],[-910,-1886]],[[158524,518913],[9,9424]],[[158533,528337],[13,6170]],[[158533,528337],[-318,-1795],[-454,494],[-602,-1308],[-130,-1178]],[[157029,524550],[-330,4339],[-600,1896],[-259,-235]],[[155840,530550],[-2,54]],[[155838,530604],[-14,3898]],[[155838,530604],[-183,-462],[-495,2189],[-757,413]],[[235406,533824],[28,-10701]],[[235434,523123],[-1381,-10]],[[234053,523113],[-17,6207]],[[237756,530248],[0,-3231],[116,-9],[14,-2987]],[[237886,524021],[-1641,-2],[226,-872]],[[236471,523147],[-1037,-24]],[[251026,525384],[-2773,33]],[[248253,525417],[-3,1579]],[[251346,528703],[-2,-1704],[-318,2],[0,-1617]],[[155418,529962],[8,-6731]],[[155426,523231],[-1689,63]],[[153737,523294],[-68,3037],[184,1180],[-262,4093],[309,-882],[393,425],[493,-351],[259,901],[373,-1735]],[[214943,526302],[-5,-2]],[[214938,526300],[-2623,9]],[[212315,526309],[-150,2]],[[212165,526311],[1,6248]],[[228233,526125],[-2090,12]],[[226143,526137],[-83,1]],[[226060,526138],[5,6455]],[[223289,532598],[-7,-6387]],[[223282,526211],[-1986,21]],[[221296,526232],[-450,10]],[[226060,526138],[-1995,52]],[[224065,526190],[-783,21]],[[212165,526311],[-2924,3]],[[185470,530663],[-558,-802],[1,-1628],[-449,-1027],[40,-1093],[-451,-1375]],[[184053,524738],[-389,163],[-476,-1895],[-334,3184]],[[255011,527072],[0,-1623],[-222,0],[-2,-3274],[-416,-8]],[[254371,522167],[-587,1081]],[[253784,523248],[-213,794],[48,1831]],[[253617,531924],[1399,-8],[-5,-4844]],[[238811,530199],[-2,-3213],[-238,7],[11,-4605]],[[238582,522388],[9,-3269]],[[238591,519119],[-694,16]],[[237897,519135],[-11,4886]],[[194273,528161],[-16,-1570],[-232,-20],[-119,-1641],[-35,-4874],[-1029,-1611],[-3,-3241],[-365,3],[-1,-3287]],[[192473,511920],[-459,14],[21,11394],[-200,2],[30,7432]],[[190466,530915],[-30,-11211],[-346,-1209],[4,-3242],[-336,0],[-17,-6512]],[[189741,508741],[-32,-6238]],[[189709,502503],[19,-3579]],[[189728,498924],[-493,1905],[18,850],[-441,2415]],[[188812,504094],[13,11149],[64,-1],[-3,5424],[-518,4],[-341,1882],[-405,1014]],[[192473,511920],[739,-89]],[[193212,511831],[-1,-3075]],[[193211,508756],[-2525,-184],[-945,169]],[[186549,522649],[-756,-104],[-628,-2190]],[[185165,520355],[-202,2288],[-910,2095]],[[157029,524550],[63,-2277]],[[157092,522273],[-463,-110]],[[156629,522163],[-284,1059],[-919,9]],[[155418,529962],[422,588]],[[239619,522318],[-1037,70]],[[242661,530268],[51,-9640]],[[242712,520628],[-343,-3]],[[242369,520625],[1,1598],[-1042,59],[-2,-1612],[-997,38]],[[240329,520708],[128,1598]],[[241982,530260],[679,8]],[[245796,530213],[-1,-3233],[691,9]],[[246486,526989],[-5,-6392]],[[246481,520597],[-2401,-12]],[[244080,520585],[-30,9671]],[[244050,530256],[1746,-43]],[[244080,520585],[-1368,43]],[[242661,530268],[1389,-12]],[[209244,525134],[12,-12455]],[[209256,512679],[-50,-4012]],[[209206,508667],[-2732,55]],[[206474,508722],[1,6547],[142,-1],[14,8098]],[[195875,520091],[-795,-463],[-177,-2270],[-459,3],[-573,-1888],[-130,-1894],[-228,1],[-3,-1752],[-298,3]],[[232297,529334],[32,-6470]],[[232329,522864],[-1381,-3]],[[230948,522861],[-32,4872]],[[234053,523113],[0,-269]],[[234053,522844],[-1724,20]],[[252040,527593],[20,-5420],[700,-8]],[[252760,522165],[-8,-6432]],[[252752,515733],[-696,32]],[[252056,515765],[-692,-1],[9,1610],[-345,-7]],[[251028,517367],[-2,8017]],[[158524,518913],[-1068,1088],[-329,768],[-35,1504]],[[165157,524278],[-366,-437]],[[164791,523841],[-600,-1643],[-745,-544],[-478,786]],[[162968,522440],[-730,-1784]],[[162238,520656],[-782,-457],[-46,928],[-635,569]],[[160775,521696],[-227,510]],[[203016,512064],[45,-3465]],[[203061,508599],[-4592,141]],[[198469,508740],[-941,-34]],[[197528,508706],[337,4066],[-1591,0],[48,4006],[104,577]],[[230948,522861],[3,-3246]],[[230951,519615],[-1623,13]],[[229328,519628],[-7,1157],[513,1813],[251,3528]],[[253784,523248],[-5,-1258],[-1019,175]],[[170423,527346],[14,-2610]],[[170437,524736],[-401,-3204],[0,-4052],[-226,-813],[-642,33],[1,-1372],[-752,-187],[118,-2760],[304,1],[77,-3742]],[[168916,508640],[-1798,-1]],[[167118,508639],[49,1621],[4,8063],[-288,1],[0,1596],[-518,16],[8,5875]],[[174020,513699],[-270,-3512]],[[173750,510187],[-1345,32]],[[172405,510219],[-2,1583],[-584,-9],[-295,3221],[-171,-21],[-111,3312],[-289,3248],[113,3191],[-629,-8]],[[255706,527058],[117,-1614],[-5,-6481],[175,-7]],[[255993,518956],[-481,-4769],[-453,-2674],[26,-1006]],[[255085,510507],[-74,93]],[[255011,510600],[-348,1798],[263,2651],[-666,243],[263,2751],[-117,1110],[148,2102],[-183,912]],[[255011,527072],[695,-14]],[[248253,525417],[-2,-6367]],[[248251,519050],[4,-3225]],[[248255,515825],[-1774,-77]],[[246481,515748],[0,4849]],[[246486,526989],[1045,-4]],[[214938,526300],[2,-8789]],[[214940,517511],[-1,-8089]],[[214939,509422],[-2667,29]],[[212272,509451],[0,3227]],[[212272,512678],[43,13631]],[[219085,526285],[420,-1613],[146,-2779],[-339,-2104]],[[219312,519789],[-135,-1298],[382,-971]],[[219559,517520],[-3145,-9]],[[216414,517511],[-1474,0]],[[212272,512678],[-3016,1]],[[221296,526232],[3,-6469]],[[221299,519763],[-1987,26]],[[185165,520355],[-3,-2744],[506,-650],[135,-1730],[3,-4866],[343,-1],[-3,-1637],[400,-13],[67,-3224],[282,-801],[790,-48],[2,-1347],[860,8]],[[188547,503302],[-99,-3225],[-305,274]],[[188143,500351],[-567,-772],[-129,1024],[-637,-765],[-533,835],[-280,-2222],[-238,584],[-857,90],[-96,-2021]],[[184806,497104],[-526,1386],[-10,1397],[-348,4611],[-316,914],[-262,-706],[-323,1502],[13,3600],[-287,1010],[-383,2793],[-233,2701],[-86,3613],[-272,361],[-94,1382]],[[224065,526190],[-1,-6467]],[[224064,519723],[-2739,40]],[[221325,519763],[-26,0]],[[226143,526137],[-2,-6467]],[[226141,519670],[-6,-6480]],[[226135,513190],[-2051,63]],[[224084,513253],[-13,1]],[[224071,513254],[-7,6469]],[[229328,519628],[389,-3087],[628,-1746]],[[230345,514795],[-1456,6],[-40,-559],[-610,15]],[[228239,514257],[-2,4850]],[[228237,519107],[-4,7018]],[[228237,519107],[-408,548],[-1688,15]],[[167118,508639],[-1414,-28]],[[165704,508611],[-332,4],[2,1361]],[[165374,509976],[84,1859],[-342,39],[1,1607],[-347,28],[21,10332]],[[251028,517367],[-1054,48]],[[249974,517415],[-9,1640],[-1714,-5]],[[172405,510219],[-826,-23],[-2,-1616],[-596,-10],[-301,1240],[-304,-1200],[-378,943],[-318,-1615]],[[169680,507938],[-764,702]],[[188812,504094],[-265,-792]],[[237897,519135],[-1085,-11]],[[236812,519124],[-341,4023]],[[165374,509976],[-1964,15]],[[163410,509991],[-24,269]],[[163386,510260],[-111,2406],[145,4765],[366,767],[-140,1600],[-678,2642]],[[206474,508722],[-142,2]],[[206332,508724],[-2606,-129]],[[203726,508595],[-665,4]],[[264518,520373],[-131,-4],[15,-7954]],[[264402,512415],[-335,1]],[[264067,512416],[-1024,59]],[[263043,512475],[5,1628]],[[263048,514103],[0,9277]],[[263048,523380],[752,-2508],[718,-499]],[[155426,523231],[0,-1322],[-345,8],[171,-1358],[345,-537],[-450,-2154],[-12,-1076]],[[155135,516792],[6,-4051],[-896,-4],[0,-2601],[169,-5]],[[154414,510131],[-4,-597]],[[154410,509534],[-766,17]],[[153644,509551],[191,6727],[-98,7016]],[[156629,522163],[0,-1647],[515,-2132],[1,-1597]],[[157145,516787],[-1,-1893],[-346,-271]],[[156798,514623],[-744,2159],[-919,10]],[[236812,519124],[284,-2536]],[[237096,516588],[-222,-1934],[-374,-651]],[[236500,514003],[-341,795],[-1058,-11]],[[235101,514787],[1,1614],[-1027,-8]],[[234075,516393],[-22,6451]],[[232329,522864],[30,-6471]],[[232359,516393],[-1030,-8]],[[231329,516385],[-347,13],[-31,3217]],[[234075,516393],[-344,3]],[[233731,516396],[-1372,-3]],[[163386,510260],[-629,17],[17,874],[-376,1203],[-462,444],[26,1607],[414,1539],[112,2268],[-250,2444]],[[239619,522318],[0,-3212],[333,-17],[8,-2705]],[[239960,516384],[-1367,53]],[[238593,516437],[-2,2682]],[[240329,520708],[8,-1270],[308,-144],[354,-2360],[-276,-2707]],[[240723,514227],[-762,15]],[[239961,514242],[-1,2142]],[[242369,520625],[-6,-8009]],[[242363,512616],[-1675,1]],[[240688,512617],[35,1610]],[[159434,520799],[0,-1905],[284,-1586]],[[159718,517308],[-2288,-7],[-285,-514]],[[160775,521696],[0,-3313],[-116,6],[3,-4865],[-600,-5]],[[160062,513519],[3,2149],[-347,1640]],[[163410,509991],[119,-3799],[225,-798]],[[163754,505394],[-3866,76]],[[159888,505470],[69,1165]],[[159957,506635],[-196,2311],[405,1008],[-239,2616],[135,949]],[[184806,497104],[3,-2717],[-506,66]],[[184303,494453],[-906,-18]],[[183397,494435],[-56,1597],[-348,2544],[-106,-614],[-845,1225],[-633,3398],[-141,1578],[-365,-189],[52,2183],[-181,-234],[-402,-2430],[55,-905],[-491,-1771],[-716,3192],[20,1138]],[[179240,505147],[217,1310],[9,5102],[104,825]],[[309697,499440],[-326,8144],[343,307],[-203,5576]],[[310206,521389],[551,-1389],[510,-516],[-213,-1647],[200,-1810],[-163,-2048],[408,-2916],[145,1141],[349,-534],[352,-3817],[161,-2744],[-592,-2997],[-880,-140],[-164,-1985],[-252,636],[-149,-1354],[-202,2089],[-181,-786],[-141,-3116],[-248,1984]],[[304288,501469],[-305,-404],[-9,-1934],[-241,39]],[[303733,499170],[-304,-527]],[[303429,498643],[-117,2061],[-640,1070],[-291,3135],[-430,-1180],[-152,5251],[122,80],[-151,4819]],[[301770,513879],[26,2283],[518,-340],[-235,2392],[476,2921]],[[266792,512571],[-1372,-94]],[[265420,512477],[-1018,-62]],[[264518,520373],[261,-2225],[563,-388],[377,-1459],[555,-1119],[218,171],[319,-1624],[-19,-1158]],[[244080,520585],[-2,-6434]],[[244078,514151],[-2,-1590],[-344,26]],[[243732,512587],[-1369,29]],[[246481,515748],[-686,31],[0,-1629]],[[245795,514150],[-1717,1]],[[197528,508706],[-1036,15]],[[196492,508721],[-3281,35]],[[221325,519763],[-3,-6470]],[[221322,513293],[-1536,18]],[[219786,513311],[-158,1348],[120,1153],[-189,1708]],[[224071,513254],[-2371,30]],[[221700,513284],[-378,9]],[[228239,514257],[0,-2714]],[[228239,511543],[-746,-4]],[[227493,511539],[-1359,31]],[[226134,511570],[1,1620]],[[231329,516385],[38,-4380]],[[231367,512005],[-336,821]],[[231031,512826],[-637,896]],[[230394,513722],[-49,1073]],[[238593,516437],[-6,-3147]],[[238587,513290],[-26,17]],[[238561,513307],[-163,843],[-522,101],[-780,2337]],[[249974,517415],[-2,-6482]],[[249972,510933],[-1721,20]],[[248251,510953],[4,4872]],[[219786,513311],[-178,-2703],[123,-1550],[-312,142],[-35,-2389]],[[219384,506811],[-387,-2543],[-370,1156],[-115,-974]],[[218512,504450],[-583,57],[-208,-834],[-377,334]],[[217344,504007],[2,4597],[-1015,-5],[83,8912]],[[217344,504007],[-269,-1030],[-621,-1015],[-210,-1237],[-503,175],[-264,-1035],[-539,-243]],[[214938,499622],[1,9800]],[[252056,515765],[-7,-3234],[109,-1632]],[[252158,510899],[-950,13],[2,-1661]],[[251210,509251],[-675,9]],[[250535,509260],[-1,1662],[-562,11]],[[159957,506635],[-1846,-55],[-387,968],[-169,1538],[-534,2059],[129,2390],[-300,15]],[[156850,513550],[-52,1073]],[[156850,513550],[-43,-124]],[[156807,513426],[-486,-577],[117,-1993],[-201,-742]],[[156237,510114],[-1823,17]],[[238561,513307],[-382,-977],[-277,-2080],[-23,-1945]],[[237879,508305],[-685,14]],[[237194,508319],[-679,14]],[[236515,508333],[-15,5670]],[[239961,514242],[-4,-3220]],[[239957,511022],[-577,12],[2,-1654]],[[239382,509380],[-216,1965],[-579,1945]],[[235101,514787],[15,-8081]],[[235116,506706],[-1367,-14]],[[233749,506692],[6,4861]],[[233755,511553],[-24,4843]],[[233755,511553],[-2201,-4]],[[231554,511549],[-187,456]],[[248251,510953],[-430,7],[0,-1618],[-327,3]],[[247494,509345],[-1694,-51]],[[245800,509294],[-5,4856]],[[252752,515733],[340,-44],[-9,-3220],[342,-69],[182,-3245],[999,-1069]],[[254606,508086],[-385,-2583],[-242,-2789]],[[253979,502714],[-8,-1]],[[253971,502713],[-10,-1]],[[253961,502712],[-693,38]],[[253268,502750],[-16,3221],[-670,52]],[[252582,506023],[14,4857],[-438,19]],[[303429,498643],[-266,-4622],[-406,-3326]],[[302757,490695],[-370,576],[-32,2012],[-647,-2107],[393,-1789],[-189,-2754]],[[301912,486633],[-573,-426]],[[301339,486207],[0,-16]],[[301339,486207],[-59,9193]],[[301280,495400],[-86,9641],[-121,9365]],[[301073,514406],[217,752],[480,-1279]],[[230394,513722],[3,-5426]],[[230397,508296],[-1199,-12]],[[229198,508284],[-5,3228],[-954,31]],[[236515,508333],[-686,5],[-1,-1627]],[[235828,506711],[-712,-5]],[[301280,495400],[-90,-875],[-585,121],[-254,-2830],[-201,2230]],[[300150,494046],[-446,795],[-524,2812]],[[299180,497653],[520,1849],[112,1480],[-264,3054],[383,3039],[-19,1891]],[[299912,508966],[388,4730],[214,623],[386,-1123],[173,1210]],[[240688,512617],[-96,-2433],[64,-4037]],[[240656,506147],[-95,-2137]],[[240561,504010],[-562,444],[-38,2207]],[[239961,506661],[97,22],[-1,4337],[-100,2]],[[245800,509294],[3,-3236]],[[245803,506058],[-2030,-24]],[[243773,506034],[-41,6553]],[[159888,505470],[-167,-669],[70,-1970]],[[159791,502831],[-1153,206],[-234,1076],[-631,10],[-781,720],[-578,-1960],[-382,1172]],[[156032,504055],[-98,1557],[388,2208],[-85,2294]],[[175529,510717],[154,-826],[-340,-5246],[151,-1455],[0,-3805]],[[175494,499385],[-398,-1021]],[[175096,498364],[-634,868],[-4,2312],[-267,893],[0,3275],[-765,34]],[[173426,505746],[189,1739],[-44,1635],[179,1067]],[[231554,511549],[662,-3749],[173,-272]],[[232389,507528],[-309,-828],[-3,-1608],[-1680,19]],[[230397,505111],[0,3185]],[[307819,495119],[63,-2040],[-255,1149],[192,891]],[[308823,498239],[322,-1210],[-422,-2791],[-256,1682],[356,2319]],[[309697,499440],[-194,-3153],[-121,2654],[-613,1160],[154,-1192],[-465,-1333],[31,1761],[-402,-1706],[118,-3275],[-828,3345],[180,1911],[-237,2132],[81,1095]],[[221700,513284],[1,-6478]],[[221701,506806],[-279,1]],[[221422,506807],[-2038,4]],[[224084,513253],[3,-6454]],[[224087,506799],[-1659,3]],[[222428,506802],[-727,4]],[[239382,509380],[134,-2743]],[[239516,506637],[-427,-1841]],[[239089,504796],[-534,227]],[[238555,505023],[9,1658],[-687,3],[2,1621]],[[226134,511570],[12,-9713]],[[226146,501857],[-2029,49]],[[224117,501906],[-30,4893]],[[179240,505147],[-395,-1629],[-367,938],[-230,-2310],[-308,-655],[142,-3509],[-185,-1566]],[[177897,496416],[-297,-1464],[-332,-476],[-1622,25],[-30,-1653],[-277,66]],[[175339,492914],[2,3219],[164,2],[-11,3250]],[[212272,509451],[-19,-8102],[-1681,-4]],[[210572,501345],[-701,13],[-661,-630]],[[209210,500728],[-4,7939]],[[242363,512616],[57,-6548]],[[242420,506068],[-1764,79]],[[243773,506034],[0,-3210]],[[243773,502824],[-1350,15]],[[242423,502839],[-3,3229]],[[262700,510887],[28,-4815]],[[262728,506072],[-1240,27],[-113,-876]],[[261375,505223],[-305,883]],[[261070,506106],[220,4694],[-69,1784]],[[261221,512584],[458,-42],[1,-1633],[1020,-22]],[[264067,512416],[-14,-6399]],[[264053,506017],[-1325,55]],[[262700,510887],[342,-32],[1,1620]],[[265420,512477],[-20,-6431]],[[265400,506046],[-1347,-29]],[[227493,511539],[8,-6473]],[[227501,505066],[-4,-4842]],[[227497,500224],[-1004,10]],[[226493,500234],[-9,1613],[-338,10]],[[229198,508284],[-7,-3209]],[[229191,505075],[-1690,-9]],[[233749,506692],[-652,-11],[0,-2565]],[[233097,504116],[-708,3412]],[[239961,506661],[-445,-24]],[[250535,509260],[0,-6476]],[[250535,502784],[-1731,65]],[[248804,502849],[-1313,5]],[[247491,502854],[3,6491]],[[252582,506023],[-689,18],[2,1616],[-685,-6],[0,1600]],[[173426,505746],[-385,-1718],[-296,-3771],[-219,-1145],[9,-3403]],[[172535,495709],[-741,0],[0,1607],[-294,1057],[-1059,-21],[-726,-3486]],[[169715,494866],[-738,-18],[245,2192],[-29,1556],[314,2473],[-552,1434],[612,1573],[-29,2092],[142,1770]],[[156032,504055],[-16,-548]],[[156016,503507],[-1264,17]],[[154752,503524],[-340,334],[-2,5676]],[[165704,508611],[55,-3187],[-11,-9610]],[[165748,495814],[-676,-6],[-1,1543],[-342,912],[-1014,-19],[1,2354]],[[163716,500598],[38,4796]],[[154752,503524],[16,-5366],[-345,3],[16,-1350],[-284,-293],[116,-1140]],[[154271,495378],[-947,-137]],[[153324,495241],[89,2631],[44,7616],[170,1569],[17,2494]],[[214938,499622],[-3,-12]],[[214935,499610],[-311,-1407],[-474,240],[-222,-2776],[-71,-2959],[-2964,12]],[[210893,492720],[-2,2156],[-338,-10],[19,6479]],[[247491,502854],[1,-4850]],[[247492,498004],[-1354,-31],[0,1610],[-337,6],[2,1612]],[[245803,501201],[0,4857]],[[253268,502750],[-7,-1765]],[[253261,500985],[-1005,107]],[[252256,501092],[2,1635],[-1723,57]],[[296979,508865],[60,-3257],[-127,-958]],[[296912,504650],[-471,86],[-134,-1750],[-359,-1102]],[[295948,501884],[-849,1672]],[[295099,503556],[103,5410]],[[295202,508966],[1777,-101]],[[299180,497653],[-205,-1076]],[[298975,496577],[-266,1736],[213,1023],[-144,948],[183,1193],[-188,668],[204,1295],[-286,977]],[[298691,504417],[181,2000],[-64,2446]],[[298808,508863],[1104,103]],[[295099,503556],[-145,-2596],[-226,-366]],[[294728,500594],[-75,974],[149,3512],[-119,604],[97,3231]],[[294780,508915],[422,51]],[[294728,500594],[65,-315]],[[294793,500279],[-930,-2003],[-662,-178]],[[293201,498098],[-213,5176],[-115,5362]],[[292873,508636],[1907,279]],[[298691,504417],[-203,-1307],[-284,945],[-413,-3859],[-312,777]],[[297479,500973],[-299,925],[174,1659],[-442,1093]],[[296979,508865],[1829,-2]],[[196492,508721],[81,-2400],[-1,-6524],[53,-2],[-1,-6560],[65,0]],[[196689,493235],[-1,-1658]],[[196688,491577],[-673,4],[0,-1650],[-344,-3],[9,-1603],[-502,-5],[2,-1644],[-615,-55]],[[194565,486621],[-361,2183],[-278,575],[-278,-718],[-65,-1424],[-340,-839],[-198,2909],[-378,-147],[-165,1088]],[[192502,490248],[0,2320],[-270,2958],[-406,2562],[50,1661],[-272,1204],[-814,-4],[0,1554],[-1081,0]],[[290927,508615],[234,-781],[324,-14558],[-26,-1329]],[[291459,491947],[-889,-543]],[[290570,491404],[-581,-367],[-299,867]],[[289690,491904],[-769,2247]],[[288921,494151],[-1154,3457]],[[287767,497608],[260,2084],[1343,6200],[870,2516],[687,207]],[[198469,508740],[221,-3931],[453,-2060],[152,402],[684,-2631]],[[199979,500520],[185,-1795],[287,-397],[154,-2198],[-5,-2947]],[[200600,493183],[-3911,52]],[[206332,508724],[28,-15346]],[[206360,493378],[-1,-12616]],[[206359,480762],[-2612,-66]],[[203747,480696],[21,19902]],[[203768,500598],[-42,7997]],[[203768,500598],[-1168,-134],[-2621,56]],[[293201,498098],[-645,-417],[133,-5014],[-525,-324]],[[292164,492343],[-705,-396]],[[290927,508615],[1946,21]],[[209210,500728],[5,-7271]],[[209215,493457],[-2855,-79]],[[169715,494866],[13,-4024]],[[169728,490842],[-1637,145],[-4,-1633],[-2343,-17]],[[165744,489337],[4,6477]],[[237194,508319],[5,-4865]],[[237199,503454],[-679,9],[-2,-1632],[-677,-6],[-1,1627]],[[235840,503452],[-12,3259]],[[238555,505023],[-264,-1740],[-417,-1264]],[[237874,502019],[-336,1431],[-339,4]],[[230397,505111],[0,-3258]],[[230397,501853],[0,-1635]],[[230397,500218],[-1208,4]],[[229189,500222],[2,4853]],[[233097,504116],[341,-1014]],[[233438,503102],[0,-2921],[-654,12]],[[232784,500193],[-25,1632],[-1362,11]],[[231397,501836],[-1000,17]],[[240561,504010],[200,-612]],[[240761,503398],[-169,-3191],[-689,-567],[0,-757]],[[239903,498883],[-674,2],[0,1341]],[[239229,500226],[-140,4570]],[[221422,506807],[-9,-6505]],[[221413,500302],[-2368,-13]],[[219045,500289],[-317,676],[77,2217],[-293,1268]],[[222428,506802],[33,-13076]],[[222461,493726],[-769,-45]],[[221692,493681],[-243,467]],[[221449,494148],[-36,6154]],[[224117,501906],[15,-8145]],[[224132,493761],[-628,-3]],[[223504,493758],[-1043,-32]],[[235840,503452],[-367,-8],[16,-4852]],[[235489,498592],[-437,8]],[[235052,498600],[-236,771]],[[234816,499371],[-1378,3731]],[[183397,494435],[-268,4],[1,-3240],[-116,1],[10,-3446],[227,-982],[-443,-1878]],[[182808,484894],[-596,-1711],[-145,-1114]],[[182067,482069],[-654,3911],[-113,-748],[-563,1718],[-9,879],[-657,322],[-39,-921],[-426,1521],[-370,1],[1,1214],[-439,-1021]],[[178798,488945],[-56,216]],[[178742,489161],[-154,1983],[62,1848],[-398,879],[-33,1744],[-322,801]],[[242423,502839],[0,-2695],[-505,27]],[[241918,500171],[-647,507],[-510,2720]],[[266980,506080],[138,-2703],[-131,-3752]],[[266987,499625],[-1582,-51]],[[265405,499574],[-5,6472]],[[265400,506046],[1580,34]],[[262728,506072],[-10,-6460]],[[262718,499612],[-1346,23]],[[261372,499635],[3,5588]],[[264053,506017],[3,-6475]],[[264056,499542],[-1338,70]],[[245803,501201],[-678,11]],[[245125,501212],[-1014,-15]],[[244111,501197],[-336,9]],[[243775,501206],[-2,1618]],[[265405,499574],[-1349,-32]],[[175096,498364],[-132,-2275],[-32,-3181],[-260,7]],[[174672,492915],[-1240,21]],[[173432,492936],[-217,1684],[-680,1089]],[[163716,500598],[-1231,-21],[0,-1605],[-451,-3],[2,-1609],[-331,10]],[[161705,497370],[-2049,35]],[[159656,497405],[135,5426]],[[239229,500226],[-676,-7]],[[238553,500219],[-1077,-9]],[[237476,500210],[398,1809]],[[229189,500222],[-676,-12]],[[228513,500210],[-1016,14]],[[297479,500973],[-166,-1451]],[[297313,499522],[-157,-1497],[-296,998],[-160,-1510],[-420,996]],[[296280,498509],[-67,1872],[-265,1503]],[[159656,497405],[121,-2501]],[[159777,494904],[-1469,-89],[-166,-673],[-532,204],[-411,1117],[-498,-541],[-6,-1096],[-722,-4],[-55,1558]],[[155918,495380],[-123,1127],[43,2998],[-132,924],[521,1787],[-211,1291]],[[219045,500289],[-133,-1711],[523,-288],[58,-1572],[780,-877],[539,-1239],[-128,-869]],[[220684,493733],[-1196,-11],[0,-483]],[[219488,493239],[-1893,-6]],[[217595,493233],[-334,-5],[34,9715],[49,1064]],[[260032,504518],[-8,-4869]],[[260024,499649],[-1160,99]],[[258864,499748],[-13,3454],[458,1388]],[[259309,504590],[723,-72]],[[298975,496577],[-544,-513],[-20,-3063]],[[298411,493001],[-914,898]],[[297497,493899],[402,4106],[-586,1517]],[[307394,502827],[-140,-1573],[180,-625],[-64,-1886],[-523,-588],[177,-1427],[-225,-1986]],[[306799,494742],[-257,115],[-412,2029],[-390,-679]],[[305740,496207],[-272,275]],[[305468,496482],[309,2126],[8,3412],[-219,979]],[[189728,498924],[5,-9133]],[[189733,489791],[-389,-951],[-593,-175]],[[188751,488665],[-448,-740],[-1162,820]],[[187141,488745],[-1,811],[-499,8],[1,1607]],[[186642,491171],[338,-6],[2,1607],[490,820],[15,2406],[170,1557],[484,19],[2,2777]],[[217595,493233],[-48,-3229]],[[217547,490004],[-2628,-7]],[[214919,489997],[16,9613]],[[296280,498509],[-410,-5407]],[[295870,493102],[-68,2572],[-938,-658]],[[294864,495016],[48,3058],[-119,2205]],[[155918,495380],[-1647,-2]],[[305468,496482],[-133,-1505],[-266,329],[-18,-2929],[-277,307]],[[304774,492684],[-7,9]],[[304767,492693],[-269,531],[-119,-1120],[-300,314]],[[304079,492418],[-196,1521],[-150,5231]],[[237476,500210],[-51,-1566]],[[237425,498644],[-1936,-52]],[[241918,500171],[207,-1615]],[[242125,498556],[-525,16],[0,-1617],[-334,5],[-1,-3232]],[[241265,493728],[-356,11]],[[240909,493739],[-1010,22]],[[239899,493761],[4,5122]],[[234816,499371],[-1,-4018],[-672,-9],[-5,-1614]],[[234138,493730],[-984,19]],[[233154,493749],[-366,-7]],[[232788,493742],[-4,6451]],[[250535,502784],[-3,-8151]],[[250532,494633],[-1040,40]],[[249492,494673],[-353,36]],[[249139,494709],[-6,4906],[-327,5],[-2,3229]],[[249139,494709],[-495,33]],[[248644,494742],[-1141,-12]],[[247503,494730],[-11,3274]],[[253961,502712],[-152,-2104],[333,-532],[464,2018]],[[254606,502094],[-10,-5904]],[[254596,496190],[-339,8],[-1,-1624],[-432,11]],[[253824,494585],[-416,19]],[[253408,494604],[6,6419],[-153,-38]],[[243775,501206],[-1056,-193],[-152,-3331]],[[242567,497682],[-442,874]],[[252256,501092],[-365,1],[-8,-6465]],[[251883,494628],[-410,-12]],[[251473,494616],[-941,17]],[[255685,502676],[-302,-3069],[-169,-3411]],[[255214,496196],[-618,-6]],[[254606,502094],[72,609]],[[254678,502703],[1007,-27]],[[192502,490248],[5,-10113]],[[192507,480135],[-16,-1620],[-799,3],[0,-1615],[-653,8],[-1,-1051]],[[191038,475860],[-655,-1],[0,1504],[-643,-3]],[[189740,477360],[-2,3452]],[[189738,480812],[-5,8979]],[[226493,500234],[-2,-6499]],[[226491,493735],[-1332,24]],[[225159,493759],[-1027,2]],[[231397,501836],[40,-8076]],[[231437,493760],[-1042,2]],[[230395,493762],[2,6456]],[[232788,493742],[-1310,16]],[[231478,493758],[-41,2]],[[210893,492720],[-1678,5]],[[209215,492725],[0,732]],[[244111,501197],[-1,-6504],[-219,-857],[207,-1948],[-69,-1318]],[[244029,490570],[-834,3122]],[[243195,493692],[-279,3080],[-349,910]],[[245125,501212],[38,-9630]],[[245163,491582],[-120,124]],[[245043,491706],[-313,-400],[-41,-1496],[-287,-3]],[[244402,489807],[-373,763]],[[247503,494730],[-1,-1740]],[[247502,492990],[-1654,56],[-187,-1630]],[[245661,491416],[-498,166]],[[253408,494604],[-589,39]],[[252819,494643],[-936,-15]],[[186642,491171],[-1501,14],[-1,-1610]],[[185140,489575],[-834,8],[-3,4870]],[[203747,480696],[-170,-9]],[[203577,480687],[-2877,-39],[0,148]],[[200700,480796],[-49,12388],[-51,-1]],[[165744,489337],[-329,0],[-5,-4859],[-338,5]],[[165072,484483],[-1001,8],[0,1610],[-336,-4],[-2,1618],[-1027,-13],[-1,1606],[-664,64],[-2,3219],[-321,77],[-13,4702]],[[221449,494148],[180,-1781],[-749,47],[-196,1319]],[[294864,495016],[-214,-1344],[-132,-2763],[162,-4374]],[[294680,486535],[-165,-92]],[[294515,486443],[-1724,-1101]],[[292791,485342],[28,960],[-465,275],[-343,2130],[225,833],[-72,2803]],[[238553,500219],[-2,-6472]],[[238551,493747],[-677,-2]],[[237874,493745],[1,807],[-681,-7]],[[237194,494545],[221,1970],[10,2129]],[[239899,493761],[-13,0]],[[239886,493761],[-1006,-6]],[[238880,493755],[-329,-8]],[[228513,500210],[-4,-6463]],[[228509,493747],[-670,-20]],[[227839,493727],[-1336,7]],[[226503,493734],[-12,1]],[[230395,493762],[0,-2]],[[230395,493760],[-1217,-20]],[[229178,493740],[-669,7]],[[260024,499649],[-9,-6494]],[[260015,493155],[-615,55]],[[259400,493210],[-964,215]],[[258436,493425],[364,3555],[64,2768]],[[261372,499635],[-2,-6463]],[[261370,493172],[-640,-4]],[[260730,493168],[-715,-13]],[[260024,499649],[1348,-14]],[[264056,499542],[8,-6454]],[[264064,493088],[-669,-1]],[[263395,493087],[-679,16]],[[262716,493103],[2,6509]],[[262716,493103],[-656,52]],[[262060,493155],[-690,17]],[[266987,499625],[-62,-3376],[-470,-1067],[-157,-2040]],[[266298,493142],[-886,-31]],[[265412,493111],[-7,6463]],[[265412,493111],[-786,0]],[[264626,493111],[-562,-23]],[[175339,492914],[-169,-3],[-23,-6408]],[[175147,486503],[-644,5]],[[174503,486508],[-557,-1]],[[173946,486507],[0,3241],[391,-17],[114,1594],[220,-13],[1,1603]],[[214919,489997],[-33,-5323],[-334,-145]],[[214552,484529],[-104,-245],[-1766,9]],[[212682,484293],[343,3115],[-3812,-38]],[[209213,487370],[2,5355]],[[297497,493899],[-208,-1404],[-408,589],[-262,-2738],[-163,304]],[[296456,490650],[-487,788],[-99,1664]],[[235052,498600],[861,-2186],[275,-1379]],[[236188,495035],[12,-2914]],[[236200,492121],[-1368,-13]],[[234832,492108],[-669,3],[-25,1619]],[[304079,492418],[-79,-2770]],[[304000,489648],[-229,-1286],[-556,2438],[-134,-953],[-324,848]],[[237194,494545],[-162,-1485],[-844,1975]],[[243195,493692],[-616,1],[0,-1602]],[[242579,492091],[-668,7],[-1,1620],[-645,10]],[[173432,492936],[-215,-1206],[111,-1864],[-135,-2004]],[[173193,487862],[-118,-728],[-1,-2989]],[[173074,484145],[-2,-31284]],[[173072,452861],[-3259,-52]],[[169813,452809],[-49,5195],[-6,11887],[-34,1],[4,20950]],[[300150,494046],[150,-5520],[-565,-417],[90,-2385]],[[299825,485724],[28,-1263],[-360,-306],[-213,-2182]],[[299280,481973],[-251,-349],[-88,1241],[-239,-1545]],[[298702,481320],[-1098,1335]],[[297604,482655],[73,1782],[273,1403]],[[297950,485840],[461,7161]],[[165072,484483],[0,-1675]],[[165072,482808],[-3996,131]],[[161076,482939],[-1867,-3]],[[159209,482936],[78,4514],[296,1024],[283,3753],[-89,2677]],[[286590,490299],[-326,1891]],[[286264,492190],[242,1614],[1261,3804]],[[288921,494151],[-278,-2136],[171,-535],[-163,-1981],[-600,-1658],[-229,98],[184,-3632]],[[288006,484307],[-698,337],[-439,-517]],[[286869,484127],[-30,11]],[[286839,484138],[-17,6]],[[286822,484144],[-93,2654],[-183,296],[473,1068],[-221,1526],[192,1728],[-400,-1117]],[[306799,494742],[-208,-3178],[83,-580],[-394,-2170],[-422,630],[40,936]],[[305898,490380],[198,677],[-157,2940],[-190,25],[-9,2185]],[[178742,489161],[-408,649],[-97,1865],[-374,-2892],[-523,-708],[-420,-3554],[-518,-1775],[-403,-257]],[[175999,482489],[-852,4014]],[[305898,490380],[-88,1150],[-333,-4456],[-153,1112],[-294,-803],[27,3034],[-191,-1422]],[[304866,488995],[63,1229],[-293,335]],[[304636,490559],[138,2125]],[[255214,496196],[10,-3137],[-283,-948],[-252,-4020]],[[254689,488091],[-861,-9]],[[253828,488082],[-4,6503]],[[296456,490650],[-138,-1260]],[[296318,489390],[-526,-1476],[60,-1043]],[[295852,486871],[-725,197],[35,-1311],[-435,-257]],[[294727,485500],[-47,1035]],[[159209,482936],[-356,-1083],[-4,-2172]],[[158849,479681],[-1695,-61],[-4,1999],[-1023,-130],[-80,4507],[-584,9],[-506,918],[-251,1689],[-581,464],[-272,-1480],[-649,-38]],[[153204,487558],[120,7683]],[[301339,486191],[74,-4637]],[[301413,481554],[-561,-31]],[[300852,481523],[-271,609],[-756,3592]],[[237874,493745],[1,-6477]],[[237875,487268],[-1335,-3]],[[236540,487265],[-340,-1]],[[236200,487264],[0,4857]],[[248644,494742],[4,-1203],[-356,-1775],[167,-1777],[17,-2211],[270,-1702],[223,-2658]],[[248969,483416],[-1467,-1]],[[247502,483415],[1,1681]],[[247503,485096],[-1,7894]],[[249492,494673],[0,-4905]],[[249492,489768],[-4,-6324]],[[249488,483444],[-519,-28]],[[251473,494616],[2,-4829]],[[251475,489787],[-785,-8]],[[250690,489779],[-1198,-11]],[[252819,494643],[-1,-5695]],[[252818,488948],[3,-842],[-1344,42]],[[251477,488148],[-2,1639]],[[253828,488082],[-336,-5]],[[253492,488077],[2,862],[-676,9]],[[185140,489575],[-7,-6496]],[[185133,483079],[-5,-1541],[-325,-81],[-2,-1617],[-333,6],[-31,-3059],[-167,-6]],[[184270,476781],[-988,-2],[0,1538],[-762,6],[71,2286],[-253,1758],[-271,-298]],[[297950,485840],[-1609,2937],[-23,613]],[[289857,482936],[-1180,-3640],[-620,942]],[[288057,480238],[-51,4069]],[[289690,491904],[167,-8968]],[[222306,488875],[139,-1400],[-277,-1473],[-155,-2285],[239,-7],[214,-2924]],[[222466,480786],[-655,1]],[[221811,480787],[-251,2],[-1,4520],[-205,288],[-400,-1289],[-686,-69],[-402,533]],[[219866,484772],[-299,59]],[[219567,484831],[-77,2419],[-2,5989]],[[221692,493681],[38,-1693],[582,-1977],[-6,-1136]],[[231478,493758],[-2,-6467]],[[231476,487291],[-1083,9]],[[230393,487300],[2,6460]],[[233154,493749],[0,-6482]],[[233154,487267],[-1644,21]],[[231510,487288],[-34,3]],[[225159,493759],[1,-4821]],[[225160,488938],[-1327,-45]],[[223833,488893],[-333,-2]],[[223500,488891],[4,4867]],[[239886,493761],[1,-6484]],[[239887,487277],[-11,1]],[[239876,487278],[-996,-7]],[[238880,487271],[0,6484]],[[240909,493739],[-31,-6461]],[[240878,487278],[-991,-1]],[[230393,487300],[-1213,-24]],[[229180,487276],[-2,6464]],[[238880,487271],[-672,1]],[[238208,487272],[-333,-4]],[[223500,488891],[-1194,-16]],[[226503,493734],[-5,-6430]],[[226498,487304],[-323,15]],[[226175,487319],[-998,7]],[[225177,487326],[-17,1612]],[[242579,492091],[0,-4839]],[[242579,487252],[-1030,-242]],[[241549,487010],[-671,268]],[[234832,492108],[2,-4840]],[[234834,487268],[-1653,0]],[[233181,487268],[-27,-1]],[[229180,487276],[-670,-7]],[[228510,487269],[-670,12]],[[227840,487281],[-1,6446]],[[227840,487281],[-666,8]],[[227174,487289],[-676,15]],[[244402,489807],[392,-2555]],[[244794,487252],[-1243,-3]],[[243551,487249],[-972,3]],[[209213,487370],[2,-6520]],[[209215,480850],[-2355,-66]],[[206860,480784],[-501,-22]],[[259400,493210],[11,-6545]],[[259411,486665],[-1095,72]],[[258316,486737],[-227,4105],[347,2583]],[[304000,489648],[314,-1030]],[[304314,488618],[155,528]],[[304469,489146],[-41,-1011]],[[304428,488135],[-42,-1969],[-144,1438],[-555,-1235],[-307,-2321],[164,-2049],[-435,-545]],[[303109,481454],[-386,1324],[-161,2030],[-306,-178],[3,1605],[-347,398]],[[219567,484831],[-690,962],[-261,-1066],[-279,233],[-415,1973],[-376,207]],[[217546,487140],[0,-1]],[[217546,487140],[1,2864]],[[200700,480796],[-1181,20]],[[199519,480816],[-168,0]],[[199351,480816],[0,2700],[-338,-12],[1,1631],[-671,1],[0,1587],[-983,520],[0,1054],[-332,2],[5,1652],[-345,1626]],[[304469,489146],[96,425],[146,-4224],[-211,-753],[-72,3541]],[[304866,488995],[-144,-863],[186,-1284],[-136,-1342],[-199,4245],[63,808]],[[304767,492693],[-187,-2521],[-266,-1554]],[[260730,493168],[6,-6502]],[[260736,486666],[-1325,-1]],[[262060,493155],[-5,-6525]],[[262055,486630],[-1319,36]],[[263395,493087],[5,-6429]],[[263400,486658],[-1345,-28]],[[264626,493111],[-1,-3070]],[[264625,490041],[-2,-3183]],[[264623,486858],[-1223,-200]],[[247503,485096],[-1667,-111]],[[245836,484985],[-8,6443],[-167,-12]],[[173946,486507],[-392,-272],[-361,1627]],[[292791,485342],[-439,-295],[152,-6649]],[[292504,478398],[55,-2188]],[[292559,476210],[-1099,-481],[-496,1080]],[[290964,476809],[-432,1000],[257,2733],[-219,10862]],[[236200,487264],[-1352,5]],[[234848,487269],[-14,-1]],[[178798,488945],[-51,-1480]],[[178747,487465],[-147,-2107],[27,-2295],[-141,-364],[-9,-7526]],[[178477,475173],[-2,-5280],[149,-49]],[[178624,469844],[-11,-2674]],[[178613,467170],[-1161,-3],[35,3027],[-978,384],[-398,937],[-84,-1083],[-654,1955],[-183,1188]],[[175190,473575],[801,17],[8,8897]],[[290964,476809],[48,-2087],[-179,-2358]],[[290833,472364],[-10,-3429]],[[290823,468935],[-322,652],[-79,-1360],[-540,1549],[-309,-528]],[[289573,469248],[-20,3211],[418,3255],[-260,632],[239,1293],[-93,5297]],[[245836,484985],[-965,57]],[[244871,485042],[-77,2210]],[[199351,480816],[0,-542],[-2071,-233],[-330,808],[-664,541],[-332,1359],[-996,270],[0,1480],[-393,2122]],[[187141,488745],[-18,-3230],[155,229],[839,-2595]],[[188117,483149],[-2489,-4]],[[185628,483145],[-495,-66]],[[169813,452809],[-3140,-59]],[[166673,452750],[-102,8]],[[166571,452758],[-13,14052],[-1606,-147],[6,8084],[116,22],[-2,8039]],[[199519,480816],[49,-535],[44,-12867],[-117,0],[4,-6462],[53,0]],[[199552,460952],[1,-3214]],[[199553,457738],[-4238,36]],[[195315,457774],[-81,8002],[-208,644],[-621,4049],[-508,1245],[-388,4713],[-175,3678],[-827,30]],[[265345,488432],[-13,-4364],[594,-1608]],[[265926,482460],[2,-2061]],[[265928,480399],[-330,807],[-651,39],[-327,827]],[[264620,482072],[3,4786]],[[264625,490041],[337,2],[2,-1583],[381,-28]],[[217546,487139],[-458,-846],[2,-7566]],[[217090,478727],[-2459,14]],[[214631,478741],[-79,1615],[0,4173]],[[184270,476781],[-1,-3235]],[[184269,473546],[-657,2],[18,-6493],[161,-1616],[-161,-935]],[[183630,464504],[-371,1135],[-285,-316]],[[182974,465323],[1,3349],[164,1],[-2,6527],[-838,4]],[[182299,475204],[-1840,-8]],[[180459,475196],[-55,2307],[-390,208],[75,2719],[-486,2042],[-29,3598],[-455,236],[-372,1159]],[[189738,480812],[-430,714],[-126,1528],[-434,6]],[[188748,483060],[3,5605]],[[251477,488148],[-2,-4871]],[[251475,483277],[-336,-10]],[[251139,483267],[-664,186]],[[250475,483453],[215,6326]],[[250475,483453],[-987,-9]],[[297604,482655],[-139,-531],[-150,-6299]],[[297315,475825],[-695,-198],[-380,613]],[[296240,476240],[-135,842]],[[296105,477082],[40,1309],[261,-315],[91,2132],[-178,490],[251,3497],[-332,632],[104,1629],[-255,1003],[-235,-588]],[[158849,479681],[187,-616],[254,-2713],[-330,-3406],[-529,-206],[-3,-1355]],[[158428,471385],[-331,3],[-437,-1062],[-659,-2951],[-995,-473],[-214,-851]],[[155792,466051],[-606,-123],[-747,708],[-3,821]],[[154436,467457],[-267,104]],[[154169,467561],[-22,3824],[329,1721],[-5,3184],[-170,-23],[1,3263],[-310,1557],[0,1719],[-955,38]],[[153037,482844],[167,4714]],[[253492,488077],[4,-6488]],[[253496,481589],[-667,11]],[[252829,481600],[-2,1626],[-1352,51]],[[225177,487326],[14,-6561]],[[225191,480765],[-1074,13]],[[224117,480778],[-247,-3]],[[223870,480775],[-37,8118]],[[223870,480775],[-1404,11]],[[188748,483060],[-631,89]],[[254689,488091],[84,-3796],[-251,-2704]],[[254522,481591],[-691,-12]],[[253831,481579],[-335,10]],[[174503,486508],[-1,-3222],[107,-1],[3,-3245],[-111,-9],[5,-3147]],[[174506,476884],[-751,3480],[-169,2160],[-512,1621]],[[219866,484772],[49,-6023]],[[219915,478749],[-2825,-22]],[[180459,475196],[-612,-25]],[[179847,475171],[-1370,2]],[[296105,477082],[-1078,260]],[[295027,477342],[17,4427],[-151,1362],[-345,330],[179,2039]],[[212682,484293],[-532,-1541],[1,-2393]],[[212151,480359],[-2937,18]],[[209214,480377],[1,473]],[[231510,487288],[-3,-6495]],[[231507,480793],[-1113,5]],[[230394,480798],[-1,6502]],[[233181,487268],[-6,-6467]],[[233175,480801],[-1132,-10]],[[232043,480791],[-536,2]],[[239876,487278],[3,-6493]],[[239879,480785],[-1250,-4]],[[238629,480781],[-423,3]],[[238206,480784],[2,6488]],[[241549,487010],[0,-6211]],[[241549,480799],[-290,-4]],[[241259,480795],[-1313,-10]],[[239946,480785],[-67,0]],[[244871,485042],[-35,-2081],[144,-2160]],[[244980,480801],[-1096,2]],[[243884,480803],[-334,1]],[[243550,480804],[1,6445]],[[226175,487319],[6,-6546]],[[226181,480773],[-417,-14]],[[225764,480759],[-573,6]],[[227174,487289],[5,-6502]],[[227179,480787],[-998,-14]],[[243550,480804],[-972,-1]],[[242578,480803],[-1029,-4]],[[234848,487269],[-4,-6462]],[[234844,480807],[-164,-8]],[[234680,480799],[-1319,-4]],[[233361,480795],[-186,6]],[[238206,480784],[-897,-1]],[[237309,480783],[-772,10]],[[236537,480793],[3,6472]],[[230394,480798],[-407,0]],[[229987,480798],[-908,-8]],[[229079,480790],[-570,-5]],[[228509,480785],[1,6484]],[[236537,480793],[-544,5]],[[235993,480798],[-1149,9]],[[228509,480785],[-758,1]],[[227751,480786],[-572,1]],[[264620,482072],[-6,-1618],[-556,-296]],[[264058,480158],[-662,0]],[[263396,480158],[4,6500]],[[259411,486665],[-4,-6481]],[[259407,480184],[-1175,91]],[[258232,480275],[-219,3393],[303,3069]],[[263396,480158],[-664,2]],[[262732,480160],[-666,1]],[[262066,480161],[-11,6469]],[[303109,481454],[-237,-3363],[-309,-544],[-343,-4752],[-437,1185]],[[301783,473980],[56,1699],[-493,2897],[67,2978]],[[262066,480161],[-1330,37]],[[260736,480198],[0,6468]],[[260736,480198],[0,-3238]],[[260736,476960],[-635,-23]],[[260101,476937],[-693,9],[-1,3238]],[[295027,477342],[-54,-6909]],[[294973,470433],[-1008,-44]],[[293965,470389],[167,2590],[-54,4202]],[[294078,477181],[-94,3357],[378,3142],[153,2763]],[[175190,473575],[-334,1506],[-129,1913],[-221,-110]],[[294078,477181],[-671,-975],[-134,2686],[-769,-494]],[[221811,480787],[-2,-9343]],[[221809,471444],[-1850,12]],[[219959,471456],[-44,7293]],[[300852,481523],[10,-3416],[-211,-1325]],[[300651,476782],[-895,3175],[-123,-380],[-353,2396]],[[266573,485031],[2,-1116],[972,143]],[[267547,484058],[-7,-6487]],[[267540,477571],[-652,-91],[7,-1620],[-304,-225]],[[266591,475635],[-654,-32]],[[265937,475603],[-9,4796]],[[265926,482460],[77,10]],[[266003,482470],[432,2693],[138,-132]],[[247502,483415],[3,-1619]],[[247505,481796],[-1004,-19],[9,-2420]],[[246510,479357],[-1496,-1]],[[245014,479356],[-34,1445]],[[288057,480238],[-163,246],[-201,-2951],[7,-3154]],[[287700,474379],[-304,516]],[[287396,474895],[-580,1634],[-156,-692],[-618,-119]],[[286042,475718],[-350,485],[-33,3060]],[[285659,479263],[575,1959],[489,118],[146,2787]],[[214631,478741],[5,-4840],[73,-2432]],[[214709,471469],[-1978,12]],[[212731,471481],[-580,15]],[[212151,471496],[0,8863]],[[269259,474627],[-1374,-276]],[[267885,474351],[-11,2434],[-334,786]],[[267547,484058],[1423,278]],[[268970,484336],[189,-4720],[100,-4989]],[[178613,467170],[-2,-14382]],[[178611,452788],[-3086,41],[-2428,19]],[[173097,452848],[-25,13]],[[251139,483267],[-6,-6483]],[[251133,476784],[-1982,151]],[[249151,476935],[334,1631],[4,3332],[-358,174],[-162,1344]],[[249151,476935],[-328,-1617]],[[248823,475318],[-991,-774]],[[247832,474544],[6,7269],[-333,-17]],[[252829,481600],[-49,-6493]],[[252780,475107],[-328,24]],[[252452,475131],[-1318,30]],[[251134,475161],[-1,1623]],[[189740,477360],[2,-5505]],[[189742,471855],[-1523,-1]],[[188219,471854],[6,4870],[-654,10],[0,1562],[-614,11],[-169,1087],[-1161,0],[1,3751]],[[188219,471854],[-1319,42]],[[186900,471896],[-1651,-7],[-264,-1313]],[[184985,470576],[-183,-1642],[-532,-4],[-1,4616]],[[161076,482939],[-47,-16196],[1299,-29],[11,-13965]],[[162339,452749],[-1582,70]],[[160757,452819],[-2346,190]],[[158411,453009],[17,18376]],[[166571,452758],[-1779,10]],[[164792,452768],[-2453,-19]],[[289573,469248],[-100,-158]],[[289473,469090],[-527,-154],[-20,1300],[-279,-24],[-23,1968],[-519,2370],[-405,-171]],[[300651,476782],[-32,-195]],[[300619,476587],[-404,-4938]],[[300215,471649],[-352,1116],[-342,-274],[-74,1832],[-524,-693],[-159,1711],[-267,-361]],[[298497,474980],[-226,2506],[101,2208],[330,1626]],[[154169,467561],[-316,-270],[-199,-1661],[-396,-276],[-61,3940],[-268,1346],[-619,4]],[[152310,470644],[269,5888],[458,6312]],[[298497,474980],[-81,-1100]],[[298416,473880],[-391,59],[61,931],[-822,-385]],[[297264,474485],[51,1340]],[[301783,473980],[-163,-962]],[[301620,473018],[-513,-14],[127,1049],[-615,2534]],[[265937,475603],[-658,-19],[7,-1630]],[[265286,473954],[-1222,-81]],[[264064,473873],[-6,6285]],[[247832,474544],[-657,679]],[[247175,475223],[-660,-544]],[[246515,474679],[-5,4678]],[[254522,481591],[-329,-5330],[49,-1204]],[[254242,475057],[-476,2]],[[253766,475059],[65,6520]],[[253766,475059],[-986,48]],[[238629,480781],[-1,-4543]],[[238628,476238],[-1318,-2]],[[237310,476236],[-1,4547]],[[237308,469765],[-1315,6]],[[235993,469771],[0,6459]],[[235993,476230],[0,4568]],[[237310,476236],[-2,-6471]],[[234682,476230],[-1321,3]],[[233361,476233],[0,4562]],[[234680,480799],[2,-4569]],[[235993,476230],[-1311,0]],[[239947,476239],[-1319,-1]],[[239946,480785],[1,-4546]],[[233361,476233],[-1320,43]],[[232041,476276],[2,4515]],[[209214,480377],[5,-8812]],[[209219,471565],[0,-7313]],[[209219,464252],[-1679,-40]],[[207540,464212],[-659,-10]],[[206881,464202],[-21,16582]],[[241256,475444],[-1309,0]],[[239947,475444],[0,795]],[[241259,480795],[-3,-5351]],[[232041,476276],[-1933,42]],[[230108,476318],[88,2490],[-209,1990]],[[242578,480803],[-3,-5357]],[[242575,475446],[-1319,-2]],[[245014,479356],[4,-1361],[405,-1717],[-331,-3302]],[[245092,472976],[-1192,26]],[[243900,473002],[-16,7801]],[[243900,473002],[-1326,21]],[[242574,473023],[1,2423]],[[230108,476318],[224,-745],[59,-2539]],[[230391,473034],[-980,6]],[[229411,473040],[-330,1]],[[229081,473041],[-2,7749]],[[206881,464202],[-1098,-58],[7,-3247]],[[205790,460897],[-134,-6],[-121,-2624],[-664,265],[160,2348],[-1441,51]],[[203590,460931],[-7,-1]],[[203583,460930],[-6,19757]],[[224117,480778],[91,-2393],[1571,-3239]],[[225779,475146],[90,-530]],[[225869,474616],[-47,-3529],[-165,-2603]],[[225657,468484],[-436,813]],[[225221,469297],[-526,2160]],[[224695,471457],[-1032,3041],[-78,1580],[-470,1325],[-243,2027],[-406,1356]],[[229081,473041],[-659,-11]],[[228422,473030],[0,1617],[-665,-11]],[[227757,474636],[-6,6150]],[[227757,474636],[-663,-12]],[[227094,474624],[-1225,-8]],[[225779,475146],[-15,5613]],[[203583,460930],[-1530,-51],[-2501,73]],[[224695,471457],[-2109,-11]],[[222586,471446],[-777,-2]],[[212151,471496],[-1401,0]],[[210750,471496],[-1531,69]],[[260101,476937],[-1,-1635]],[[260100,475302],[-329,14],[4,-1620],[-1016,-10]],[[258759,473686],[-527,6589]],[[262732,480160],[25,-6465]],[[262757,473695],[-1325,-1]],[[261432,473694],[1,3265],[-697,1]],[[195315,457774],[-2814,132]],[[192501,457906],[-1359,125],[-5,3185],[-131,-2],[32,14646]],[[264064,473873],[0,-195]],[[264064,473678],[-1307,17]],[[246515,474679],[-1111,-3345],[-255,-73]],[[245149,471261],[-57,1715]],[[286042,475718],[-35,-4144],[115,-2963],[261,67],[229,-1455]],[[286612,467223],[26,-2749]],[[286638,464474],[-1117,-2]],[[285521,464472],[-187,1943],[55,5513]],[[285389,471928],[-24,5905]],[[285365,477833],[294,1430]],[[293965,470389],[-115,-2942]],[[293850,467447],[-369,-82]],[[293481,467365],[-266,2462],[-523,832]],[[292692,470659],[-14,505]],[[292678,471164],[-119,5046]],[[217090,478727],[0,-7285]],[[217090,471442],[-2381,27]],[[219959,471456],[-2869,-14]],[[281818,478275],[-5,-4317]],[[281813,473958],[-1305,-81]],[[280508,473877],[0,4510]],[[280508,478387],[1310,-112]],[[280508,473877],[4,-838]],[[280512,473039],[-752,7],[-414,-1125]],[[279346,471921],[-518,1158],[12,3307],[719,1122],[949,879]],[[283548,476628],[9,-4501]],[[283557,472127],[-318,-2],[8,-1701],[-272,15]],[[282975,470439],[-429,33],[9,794],[-498,-13]],[[282057,471253],[10,2716],[-254,-11]],[[281818,478275],[657,-443],[527,-1887],[546,683]],[[285389,471928],[-697,-201]],[[284692,471727],[-474,498],[-661,-98]],[[283548,476628],[635,251],[429,-526],[753,1480]],[[267885,474351],[36,-4847]],[[267921,469504],[-333,-96]],[[267588,469408],[-976,-152]],[[266612,469256],[-21,6379]],[[192501,457906],[15,-12908]],[[192516,444998],[-2779,45]],[[189737,445043],[-3,7851]],[[189734,452894],[0,9524]],[[189734,462418],[8,9437]],[[296240,476240],[-137,-2650],[-357,111],[-31,-3519],[241,-61],[-25,-3494]],[[295931,466627],[-259,37]],[[295672,466664],[-674,89]],[[294998,466753],[-25,3680]],[[251134,475161],[-13,-6521]],[[251121,468640],[-991,-43]],[[250130,468597],[-1307,230]],[[248823,468827],[0,6491]],[[261432,473694],[8,-6496]],[[261440,467198],[-657,-32]],[[260783,467166],[-660,1]],[[260123,467167],[-23,8135]],[[292678,471164],[-1239,47],[-606,1153]],[[301620,473018],[189,655],[301,-1389],[-295,-3187]],[[301815,469097],[-594,-238],[-94,-983],[-503,-1186]],[[300624,466690],[-506,1531],[-67,1971],[164,1457]],[[287396,474895],[215,-2005],[56,-5300]],[[287667,467590],[-1055,-367]],[[297264,474485],[21,-2797],[-310,-2918],[270,-2372]],[[297245,466398],[-1314,229]],[[232041,476276],[7,-6486]],[[232048,469790],[-1892,-6]],[[230156,469784],[78,2628],[157,622]],[[233361,476233],[0,-6428]],[[233361,469805],[-1313,-15]],[[239947,475444],[-4,-5679]],[[239943,469765],[-1318,14]],[[238625,469779],[3,6459]],[[238625,469779],[-1317,-14]],[[235993,469771],[-1311,30]],[[234682,469801],[0,6429]],[[234682,469801],[-1321,4]],[[266612,469256],[-658,-171],[8,-1638]],[[265962,467447],[-657,-45]],[[265305,467402],[-19,6552]],[[242574,473023],[-2,-3272]],[[242572,469751],[-1316,2]],[[241256,469753],[0,5691]],[[241256,469753],[-1313,12]],[[248823,468827],[0,-812]],[[248823,468015],[-1640,-18]],[[247183,467997],[-8,7226]],[[247183,467997],[1,-5692]],[[247184,462305],[-597,23]],[[246587,462328],[-172,2280],[-542,826]],[[245873,465434],[-469,1433],[-255,4394]],[[300624,466690],[-29,-121]],[[300595,466569],[-107,-729],[-1684,270]],[[298804,466110],[-84,15]],[[298720,466125],[-215,4313],[-157,16],[68,3426]],[[260123,467167],[-1187,-13]],[[258936,467154],[-50,4104],[-127,2428]],[[182299,475204],[2,-6522],[-141,-1591],[-469,14]],[[181691,467105],[-327,810],[-823,11],[-55,776],[-638,0]],[[179848,468702],[-1,6469]],[[179848,468702],[-63,-3749]],[[179785,464953],[-565,398],[-162,1616]],[[179058,466967],[-63,1875],[-371,1002]],[[252452,475131],[-16,-6573]],[[252436,468558],[-656,-5]],[[251780,468553],[-659,87]],[[182974,465323],[-238,-239],[-275,-2258],[-767,3]],[[181694,462829],[-3,4276]],[[253766,475059],[-16,-6494]],[[253750,468565],[-660,-23]],[[253090,468542],[-654,16]],[[254242,475057],[185,-6512]],[[254427,468545],[-677,20]],[[289473,469090],[-133,-2371]],[[289340,466719],[-1655,-378]],[[287685,466341],[-18,1249]],[[298720,466125],[-986,173]],[[297734,466298],[-489,100]],[[228422,473030],[0,-5278]],[[228422,467752],[-410,1264],[-493,-321]],[[227519,468695],[-420,21]],[[227099,468716],[-5,5908]],[[227099,468716],[-647,153],[-239,-1688],[-225,-93],[-331,1396]],[[265305,467402],[-657,-79]],[[264648,467323],[-572,-16]],[[264076,467307],[-12,6371]],[[282057,471253],[-126,-2331]],[[281931,468922],[-1419,93]],[[280512,469015],[0,4024]],[[262757,473695],[0,-6486]],[[262757,467209],[-661,6]],[[262096,467215],[-656,-17]],[[264076,467307],[-666,-109]],[[263410,467198],[-653,11]],[[184985,470576],[7,-703],[463,1104],[183,-1172],[54,-3295],[325,-1616],[-111,-2670]],[[185906,462224],[-218,4],[1,-1621],[-435,0],[0,-1622],[-966,-25]],[[184288,458960],[-6,4851],[-652,693]],[[279029,472686],[285,-724],[-124,-1251],[-161,1975]],[[280512,469015],[1,-6165]],[[280513,462850],[-1219,-1634]],[[279294,461216],[-444,1662]],[[278850,462878],[-210,586]],[[278640,463464],[245,2248],[543,1799],[-163,3149],[81,1261]],[[230156,469784],[-257,-2656],[18,-1098],[319,-1424],[30,-1300]],[[230266,463306],[144,-1322]],[[230410,461984],[-513,643]],[[229897,462627],[-187,2346],[-300,1001]],[[229410,465974],[1,7066]],[[229410,465974],[-586,1070]],[[228824,467044],[-402,708]],[[243900,473002],[-5,-8148]],[[243895,464854],[-1323,-36]],[[242572,464818],[0,4933]],[[245873,465434],[-1,-548],[-656,3]],[[245216,464889],[-1321,-35]],[[292692,470659],[24,-1088],[-500,-1877]],[[292216,467694],[-534,-438],[-538,1053]],[[291144,468309],[-321,626]],[[284692,471727],[-24,-4636]],[[284668,467091],[-949,-53],[-149,-3444]],[[283570,463594],[-344,17]],[[283226,463611],[9,1733],[-312,31],[52,5064]],[[186900,471896],[-241,-2319],[147,-3747],[355,398],[146,-1874],[116,-3740]],[[187423,460614],[-60,-2973],[-639,535]],[[186724,458176],[-180,1226],[-380,-494],[84,2409],[-342,907]],[[189734,462418],[-524,-641],[35,1743],[-1064,341],[14,-3257]],[[188195,460604],[-772,10]],[[285521,464472],[224,-1369],[-308,-60]],[[285437,463043],[-555,-97]],[[284882,462946],[2,2100],[-216,2045]],[[210915,461007],[1,-6453],[118,-1623]],[[211034,452931],[-1815,-34]],[[209219,452897],[0,11355]],[[210750,471496],[65,-4017],[1,-6473],[99,1]],[[214922,454659],[-168,-1624]],[[214754,453035],[-1702,-71]],[[213052,452964],[-57,-9]],[[212995,452955],[-124,1630],[-20,6469],[-66,0]],[[212785,461054],[0,6430],[-54,3997]],[[214709,471469],[45,-10348],[76,-3],[-4,-6452],[96,-7]],[[212785,461054],[-1870,-47]],[[225221,469297],[13,-2261]],[[225234,467036],[-777,345],[-862,1235],[-166,869],[-848,-1646]],[[222581,467839],[5,3607]],[[222581,467839],[-781,-1614],[-391,247]],[[221409,466472],[-1271,1432],[-179,713]],[[219959,468617],[0,2839]],[[219959,468617],[86,-14154]],[[220045,454463],[-279,6]],[[219766,454469],[-1612,38]],[[218154,454507],[-1617,75]],[[216537,454582],[-1615,77]],[[158411,453009],[-2622,-72]],[[155789,452937],[3,13114]],[[283226,463611],[-473,58],[-176,-2031]],[[282577,461638],[-328,866],[-551,60]],[[281698,462564],[229,2735],[4,3623]],[[294998,466753],[-244,-4393]],[[294754,462360],[-1202,-852]],[[293552,461508],[61,2653],[237,3286]],[[293481,467365],[-1033,-910]],[[292448,466455],[-257,-293],[25,1532]],[[154436,467457],[-322,-2815],[-164,-2462],[-328,-79],[-48,-2548],[372,-119],[281,-2180],[-183,-1761],[99,-2713]],[[154143,452780],[-1087,57]],[[153056,452837],[-394,1953],[-208,4302],[105,4473],[-71,1599],[-264,1356],[-114,2029],[200,2095]],[[179785,464953],[674,-952],[683,-1920],[360,583]],[[181502,462664],[-185,-223],[-6,-1813],[-601,5],[9,-7878]],[[180719,452755],[-2108,33]],[[232048,469790],[-2,-6489]],[[232046,463301],[-1780,5]],[[234682,469801],[-2,-6505]],[[234680,463296],[-1319,27]],[[233361,463323],[0,6482]],[[233361,463323],[-985,-8]],[[232376,463315],[-330,-14]],[[235993,469771],[0,-4895]],[[235993,464876],[0,-1607]],[[235993,463269],[-1313,27]],[[238625,469779],[-3,-6531]],[[238622,463248],[-1315,8]],[[237307,463256],[0,1612]],[[237307,464868],[1,4897]],[[237307,464868],[-1314,8]],[[239943,469765],[-4,-6533]],[[239939,463232],[-1317,16]],[[241256,469753],[-1,-4930]],[[241255,464823],[1,-1610]],[[241256,463213],[-1317,19]],[[242572,464818],[-1317,5]],[[291144,468309],[49,-3778],[-226,-2026]],[[290967,462505],[-369,-97],[-984,-2871],[-607,-823]],[[289007,458714],[112,1789],[-67,1881],[190,1044],[98,3291]],[[225234,467036],[12,-6037]],[[225246,460999],[0,-6480]],[[225246,454519],[-1283,-6]],[[223963,454513],[-1286,-11]],[[222677,454502],[-33,1]],[[222644,454503],[-31,12930],[-32,406]],[[268694,465642],[-316,-1305],[-142,-3076]],[[268236,461261],[-593,-71]],[[267643,461190],[-55,8218]],[[267921,469504],[681,71],[92,-3933]],[[267643,461190],[-1306,-223]],[[266337,460967],[-314,-75]],[[266023,460892],[-61,6555]],[[301815,469097],[129,-3011],[504,-1291],[-172,-917],[-656,-1009],[-20,-1435],[-247,-532]],[[301353,460902],[-115,246]],[[301238,461148],[-90,2974],[-550,962],[-3,1485]],[[227519,468695],[-2,-7665]],[[227517,461030],[-972,-16]],[[226545,461014],[-1299,-15]],[[228824,467044],[-4,-7638]],[[228820,459406],[-975,10]],[[227845,459416],[1,1618],[-329,-4]],[[281698,462564],[-753,1]],[[280945,462565],[-432,285]],[[250130,468597],[9,-6418]],[[250139,462179],[-99,3]],[[250040,462182],[-1216,98]],[[248824,462280],[-1,5735]],[[181694,462829],[-192,-165]],[[251780,468553],[1,-6529]],[[251781,462024],[-457,58]],[[251324,462082],[-1185,97]],[[221409,466472],[43,-12006]],[[221452,454466],[-68,2]],[[221384,454468],[-1339,-5]],[[253090,468542],[2,-4305]],[[253092,464237],[4,-2164]],[[253096,462073],[-1122,-19]],[[251974,462054],[-193,-30]],[[254427,468545],[185,-1078],[-127,-2165]],[[254485,465302],[-1064,31],[0,-1088],[-329,-8]],[[292448,466455],[-171,-3336],[-36,-2654]],[[292241,460465],[-527,-991]],[[291714,459474],[-489,1291],[-258,1740]],[[293552,461508],[-1311,-1043]],[[248824,462280],[-247,1]],[[248577,462281],[-1393,24]],[[222644,454503],[-1192,-37]],[[287015,460504],[-343,-51]],[[286672,460453],[-34,4021]],[[287685,466341],[71,-5738]],[[287756,460603],[-741,-99]],[[155789,452937],[-801,-56]],[[154988,452881],[-845,-101]],[[266023,460892],[-1299,-123]],[[264724,460769],[-27,1]],[[264697,460770],[-49,6553]],[[264697,460770],[-1281,-49]],[[263416,460721],[-6,6477]],[[263416,460721],[-329,-8]],[[263087,460713],[-985,-2]],[[262102,460711],[-6,6504]],[[262102,460711],[-632,-30]],[[261470,460681],[-681,30]],[[260789,460711],[-6,6455]],[[260789,460711],[-616,-16]],[[260173,460695],[-1419,-24]],[[258754,460671],[182,6483]],[[284882,462946],[17,-1460],[-606,385]],[[284293,461871],[-102,1733],[-621,-10]],[[229897,462627],[-253,6],[-13,-4592]],[[229631,458041],[-268,-255]],[[229363,457786],[-543,3],[0,1617]],[[295672,466664],[201,-1871],[-69,-1578]],[[295804,463215],[-69,-4535]],[[295735,458680],[-148,-5073]],[[295587,453607],[-1209,182]],[[294378,453789],[-26,1]],[[294352,453790],[-31,692],[433,7878]],[[289007,458714],[-9,-2219]],[[288998,456495],[-609,12],[-4,988],[-551,-86]],[[287834,457409],[-78,3194]],[[297645,459260],[-169,1432],[-916,-278],[9,882],[-474,583],[-291,1336]],[[297734,466298],[148,-1093],[-43,-2779],[-194,-3166]],[[301238,461148],[-70,-970]],[[301168,460178],[-67,-41]],[[301101,460137],[28,-397]],[[301129,459740],[-2,-4]],[[301127,459736],[-256,-726]],[[300871,459010],[-22,-492]],[[300849,458518],[-74,-390]],[[300775,458128],[-385,553],[-40,-2081],[-373,-819]],[[299977,455781],[-294,713],[-11,1200],[281,1310],[-388,368],[185,1162],[57,2438],[-390,-234],[39,1513],[-540,411],[-112,1448]],[[299977,455781],[-56,-2596]],[[299921,453185],[-840,-171]],[[299081,453014],[-844,387]],[[298237,453401],[-93,27]],[[298144,453428],[2,2448],[-359,413],[119,1141]],[[297906,457430],[27,1226],[-288,604]],[[184288,458960],[0,-6128]],[[184288,452832],[-2898,-86]],[[181390,452746],[-671,9]],[[246587,462328],[-37,-556],[502,-1805]],[[247052,459967],[-532,14],[-2,-1629],[-648,10]],[[245870,458362],[-647,9]],[[245223,458371],[-7,6518]],[[254485,465302],[3,-3266]],[[254488,462036],[-1092,47]],[[253396,462083],[-300,-10]],[[245223,458371],[-649,13]],[[244574,458384],[-652,-1]],[[243922,458383],[-27,6471]],[[237307,463256],[111,-1603],[1,-4876]],[[237419,456777],[-650,-3]],[[236769,456774],[-649,-15]],[[236120,456759],[0,4918],[-127,1592]],[[243922,458383],[-651,50]],[[243271,458433],[-651,-34]],[[242620,458399],[-48,6419]],[[242620,458399],[-653,5]],[[241967,458404],[-654,-10]],[[241313,458394],[-57,4819]],[[286672,460453],[9,-2065],[-460,403],[-3,-1030],[-340,349]],[[285878,458110],[-226,21]],[[285652,458131],[-201,28],[-14,4884]],[[209219,452897],[0,-5656]],[[209219,447241],[2,-2488]],[[209221,444753],[-1673,14],[-1,1638]],[[207547,446405],[-7,17807]],[[207547,446405],[-1742,68]],[[205805,446473],[-15,14424]],[[189734,452894],[-1286,-39]],[[188448,452855],[-243,1756],[-87,2141],[178,1265],[-101,2587]],[[284293,461871],[21,-3935],[375,114]],[[284689,458050],[-2,-5187]],[[284687,452863],[-1795,-1]],[[282892,452862],[-390,-48]],[[282502,452814],[75,8824]],[[278850,462878],[-1,-10015]],[[278849,452863],[-1529,-47]],[[277320,452816],[-423,27],[0,5034]],[[276897,457877],[602,1758],[633,2570],[508,1259]],[[232376,463315],[-1,-1600],[202,-13],[-4,-4901]],[[232573,456801],[-1911,67]],[[230662,456868],[1,1145]],[[230663,458013],[-154,1201],[39,2679],[-138,91]],[[234680,463296],[161,-1608],[-5,-4919]],[[234836,456769],[-647,13]],[[234189,456782],[-648,10]],[[233541,456792],[3,4901],[-183,1630]],[[233541,456792],[-968,9]],[[236120,456759],[-644,5]],[[235476,456764],[-640,5]],[[238622,463248],[103,-1618],[-1,-4868]],[[238724,456762],[-656,0]],[[238068,456762],[-649,15]],[[239939,463232],[70,-6471]],[[240009,456761],[-640,-7]],[[239369,456754],[-645,8]],[[241313,458394],[-653,-6],[2,-1611]],[[240662,456777],[-653,-16]],[[297906,457430],[-486,-1106],[-24,856],[-523,-374],[-118,1160],[-154,-1681],[-621,1035],[76,1737],[-321,-377]],[[285652,458131],[-963,-81]],[[280945,462565],[4,-9702]],[[280949,452863],[-1704,-50]],[[279245,452813],[-396,50]],[[230663,458013],[-1032,28]],[[282502,452814],[-1272,49]],[[281230,452863],[-281,0]],[[291714,459474],[-261,-2859],[238,-600]],[[291691,456015],[-916,-2845]],[[290775,453170],[-1003,-3057]],[[289772,450113],[-329,184],[-290,2566]],[[289153,452863],[-165,772],[10,2860]],[[248577,462281],[18,-5761]],[[248595,456520],[-1108,-48]],[[247487,456472],[-211,623],[-224,2872]],[[294352,453790],[-85,-1335],[-514,519],[-607,1358]],[[293146,454332],[53,903]],[[293199,455235],[352,2851],[1,3422]],[[249240,456572],[-645,-52]],[[250040,462182],[14,-5553]],[[250054,456629],[-814,-57]],[[251324,462082],[2,-6381]],[[251326,455701],[-650,-38],[2,1005],[-624,-39]],[[186724,458176],[47,-5357]],[[186771,452819],[-155,1]],[[186616,452820],[-2328,12]],[[253396,462083],[-8,-6346]],[[253388,455737],[-109,0]],[[253279,455737],[-975,-13]],[[252304,455724],[-325,0]],[[251979,455724],[-5,6330]],[[251979,455724],[-653,-23]],[[254614,455702],[-1226,35]],[[254488,462036],[-82,-3532],[208,-2802]],[[293199,455235],[-367,801],[-92,-1372],[-647,330],[-402,1021]],[[301127,459736],[69,-1616]],[[301196,458120],[-253,-1012],[-168,1020]],[[300849,458518],[22,492]],[[227845,459416],[-2,-4858]],[[227843,454558],[-1298,-23]],[[226545,454535],[0,6479]],[[212995,452955],[-1853,-35]],[[211142,452920],[-108,11]],[[226545,454535],[3,-3229]],[[226548,451306],[-1288,-24]],[[225260,451282],[-14,3237]],[[266372,454459],[-653,-60]],[[265719,454399],[-998,-201]],[[264721,454198],[3,6571]],[[266337,460967],[35,-6508]],[[203590,460931],[13,-19328],[-708,2],[8,-7373]],[[202903,434232],[-1501,65]],[[201402,434297],[-1279,5]],[[200123,434302],[-1673,-16]],[[198450,434286],[-31,12241],[1170,-39],[16,8015],[-52,3235]],[[205805,446473],[4,-12257]],[[205809,434216],[-2545,-6]],[[203264,434210],[-361,22]],[[188448,452855],[-1677,-36]],[[264721,454198],[-644,35]],[[264077,454233],[-965,-59]],[[263112,454174],[-25,6539]],[[263112,454174],[-326,40]],[[262786,454214],[-1301,-17]],[[261485,454197],[-15,6484]],[[260173,460695],[3,-6540]],[[260176,454155],[-1280,41]],[[258896,454196],[-2,3199],[-393,-4]],[[258501,457391],[253,3280]],[[261485,454197],[-1309,-42]],[[289153,452863],[-334,-23]],[[288819,452840],[-1736,23]],[[287083,452863],[-29,3458],[98,834],[-137,3349]],[[287083,452863],[-115,0]],[[286968,452863],[-1145,0]],[[285823,452863],[55,5247]],[[247487,456472],[432,-1434],[26,-1577]],[[247945,453461],[-2077,28]],[[245868,453489],[2,4873]],[[229363,457786],[-2,-3238]],[[229361,454548],[-545,3]],[[228816,454551],[-973,7]],[[301196,458120],[304,-647]],[[301500,457473],[18,-1677],[-433,-1150]],[[301085,454646],[-839,-2060]],[[300246,452586],[-325,599]],[[298144,453428],[-1041,73]],[[297103,453501],[-1391,88]],[[295712,453589],[-125,18]],[[301793,457791],[116,-445]],[[301909,457346],[403,-2846],[-196,-1501],[475,-1514],[5,-2152]],[[302596,449333],[-234,-1189]],[[302362,448144],[-280,-204],[-97,-1526],[-241,-453]],[[301744,445961],[-218,3011],[-291,-217],[146,1519],[-214,1940],[-82,2432]],[[301500,457473],[293,318]],[[243271,458433],[-1,-8140]],[[243270,450293],[-1301,18]],[[241969,450311],[-2,8093]],[[244574,458384],[-7,-6498]],[[244567,451886],[-1,-1627]],[[244566,450259],[-1296,34]],[[241969,450311],[-1303,-7]],[[240666,450304],[-4,6473]],[[245868,453489],[0,-1629]],[[245868,451860],[-1301,26]],[[285823,452863],[-1027,0]],[[284796,452863],[-109,0]],[[230662,456868],[249,-1941],[-15,-1182]],[[230896,453745],[-107,-597],[-679,13]],[[230110,453161],[-2,1379],[-747,8]],[[198450,434286],[-3154,-25]],[[195296,434261],[-2647,-56]],[[192649,434205],[-133,0]],[[192516,434205],[0,10793]],[[277320,452816],[-6,-2742]],[[277314,450074],[-2528,-14]],[[274786,450060],[0,2383]],[[274786,452443],[526,1090],[1585,4344]],[[258896,454196],[-10,-5792]],[[258886,448404],[-830,-14]],[[258056,448390],[-837,12]],[[257219,448402],[579,2539],[358,3966],[345,2484]],[[232573,456801],[-5,-6484]],[[232568,450317],[-1299,59]],[[231269,450376],[19,2011],[-392,1358]],[[240666,450304],[-1299,5]],[[239367,450309],[2,6445]],[[235476,456764],[0,-6454]],[[235476,450310],[-323,-7]],[[235153,450303],[-970,19]],[[234183,450322],[6,6460]],[[234183,450322],[-1294,1]],[[232889,450323],[-321,-6]],[[238068,456762],[0,-6440]],[[238068,450322],[-326,1]],[[237742,450323],[-971,-4]],[[236771,450319],[-2,6455]],[[236771,450319],[-324,-4]],[[236447,450315],[-971,-5]],[[239367,450309],[-322,8]],[[239045,450317],[-977,5]],[[251326,455701],[-4,-4851]],[[251322,450850],[-1168,-69],[-750,255]],[[249404,451036],[-159,538]],[[249245,451574],[-5,4998]],[[249245,451574],[-1296,-32]],[[247949,451542],[-4,1919]],[[293146,454332],[-66,-9100]],[[293080,445232],[-482,-138],[-384,934],[-288,-777]],[[291926,445251],[-77,1003],[-501,1869],[338,2426],[-911,2621]],[[254614,455702],[218,-1446],[316,-5438]],[[255148,448818],[123,-1399]],[[255271,447419],[-6,-4485]],[[255265,442934],[-737,66],[-9,1648],[-322,-35],[-7,1623],[-327,774]],[[253863,447010],[319,582],[-16,5161],[-954,-144]],[[253212,452609],[67,3128]],[[252304,455724],[-37,-8080]],[[252267,447644],[0,-1641]],[[252267,446003],[-938,-57]],[[251329,445946],[-7,4904]],[[253212,452609],[3,-4870]],[[253215,447739],[-948,-95]],[[216537,454582],[2,-6500]],[[216539,448082],[-1559,-14]],[[214980,448068],[-230,-4],[4,4971]],[[301744,445961],[-236,-1674],[-533,-780]],[[300975,443507],[-36,3034],[-176,277]],[[300763,446818],[-87,707]],[[300676,447525],[-253,1172]],[[300423,448697],[-177,3889]],[[218154,454507],[10,-6475]],[[218164,448032],[-1572,52]],[[216592,448084],[-53,-2]],[[230110,453161],[-2,-5099]],[[230108,448062],[-976,13]],[[229132,448075],[-318,5]],[[228814,448080],[2,6471]],[[227843,454558],[1,-6473]],[[227844,448085],[-1288,-18]],[[226556,448067],[-8,3239]],[[228814,448080],[-648,2]],[[228166,448082],[-322,3]],[[219766,454469],[6,-6447]],[[219772,448022],[-1243,-2]],[[218529,448020],[-365,12]],[[225260,451282],[0,-3235]],[[225260,448047],[-1272,-15]],[[223988,448032],[-19,0]],[[223969,448032],[-6,6481]],[[221384,454468],[0,-6437]],[[221384,448031],[-1572,-10]],[[219812,448021],[-40,1]],[[222677,454502],[3,-6462]],[[222680,448040],[-1296,-9]],[[223969,448032],[-1263,4]],[[222706,448036],[-26,4]],[[265719,454399],[30,-6681]],[[265749,447718],[-327,-64]],[[265422,447654],[-1337,-251]],[[264085,447403],[-8,6830]],[[294378,453789],[-86,-7126]],[[294292,446663],[-33,-2600]],[[294259,444063],[-1125,-725],[-133,-918]],[[293001,442420],[79,2812]],[[302596,449333],[321,-1189],[408,-369],[764,1511],[-341,4569],[401,-2137],[107,-3239],[-210,-1768],[-1085,-604],[-241,-1586],[-502,-404],[144,4027]],[[262786,454214],[4,-5818]],[[262790,448396],[-1035,-6]],[[261755,448390],[-267,4]],[[261488,448394],[-3,5803]],[[264085,447403],[-109,-15]],[[263976,447388],[-1133,-180]],[[262843,447208],[-53,1188]],[[261488,448394],[-1023,-15]],[[260465,448379],[-349,0]],[[260116,448379],[69,741],[-9,5035]],[[260116,448379],[-774,27]],[[259342,448406],[-456,-2]],[[295712,453589],[-58,-1345],[397,121],[-173,-3103],[-189,-155],[94,-2951]],[[295783,446156],[-481,-2335],[-431,-845]],[[294871,442976],[-365,419],[-214,3268]],[[231269,450376],[205,-1283],[-160,-2129]],[[231314,446964],[-897,11]],[[230417,446975],[1,1083],[-310,4]],[[297103,453501],[5,-4180],[88,63],[182,-3932]],[[297378,445452],[-156,-333]],[[297222,445119],[-108,1173],[-581,-358],[-107,-908]],[[296426,445026],[-541,-412],[-102,1542]],[[298237,453401],[8,-1301],[-328,-15],[-2,-2289],[170,-1515],[-230,-737]],[[297855,447544],[-477,-2092]],[[247949,451542],[-252,-2717]],[[247697,448825],[-205,-1009]],[[247492,447816],[-632,692]],[[246860,448508],[-476,-580],[-516,680]],[[245868,448608],[0,3252]],[[299081,453014],[27,-5277]],[[299108,447737],[6,-1575]],[[299114,446162],[-459,-104],[-560,583],[-240,903]],[[300423,448697],[-95,-429]],[[300328,448268],[-110,206]],[[300218,448474],[-189,-592],[-921,-145]],[[291926,445251],[-301,-1608],[-771,-203],[-6,-1282]],[[290848,442158],[-634,1017],[-245,2280]],[[289969,445455],[47,2798],[-244,1860]],[[160757,452819],[3,-15155]],[[160760,437664],[-2931,-15]],[[157829,437649],[50,2566],[-253,887],[-643,-3074],[-236,36],[-242,-1729],[156,-2198],[-334,192],[-289,1393],[-274,-63],[-470,1939]],[[155294,437598],[-187,3492],[-519,272]],[[154588,441362],[111,941],[-260,2964],[146,1892],[-113,2512],[387,1397],[129,1813]],[[214980,448068],[2,-6471]],[[214982,441597],[-197,8],[0,-3232]],[[214785,438373],[-1547,1]],[[213238,438374],[-73,4047]],[[213165,442421],[1,5662],[-117,1089],[3,3792]],[[213165,442421],[-2040,-26]],[[211125,442395],[-4,4869]],[[211121,447264],[21,5656]],[[188448,452855],[2,-2742],[226,-3238],[-46,-2736],[-189,-2015]],[[188441,442124],[-433,106],[-167,-983],[-443,939]],[[187398,442186],[-88,2093],[-182,-89],[-193,3078],[-4,3038],[-315,2514]],[[211121,447264],[-1902,-23]],[[189737,445043],[-2,-6109]],[[189735,438934],[-608,-2000]],[[189127,436934],[120,1271],[-305,2684],[-249,88]],[[188693,440977],[-252,1147]],[[286968,452863],[88,-6473]],[[287056,446390],[-234,-102],[-57,-1948]],[[286765,444340],[-1655,896]],[[285110,445236],[-170,121]],[[284940,445357],[-144,7506]],[[284940,445357],[-238,-844],[-1781,-169]],[[282921,444344],[-29,8518]],[[187398,442186],[-205,139],[-194,-1813],[-584,5],[-711,-4841]],[[185704,435676],[-852,-1423],[-3463,-5]],[[181389,434248],[1,18498]],[[173097,434253],[-1,-6651],[-636,-10],[-149,-2165]],[[172311,425427],[-25,2930],[-933,15],[0,3230],[-3183,-37],[0,1955],[-1457,-9]],[[166713,433511],[-57,0],[17,19239]],[[173097,452848],[0,-18595]],[[181389,434248],[-13,-16439]],[[181376,417809],[-3391,104],[-1592,99]],[[176393,418012],[-461,-9],[-438,10035],[-1,6209],[-1195,9]],[[174298,434256],[-1201,-3]],[[281230,452863],[9,-7106]],[[281239,445757],[-601,-298]],[[280638,445459],[-179,516],[-1317,-113]],[[279142,445862],[103,6951]],[[282921,444344],[4,-1186]],[[282925,443158],[-1089,-71]],[[281836,443087],[-171,12],[-426,2658]],[[154588,441362],[-303,-18],[-4,1572],[-819,-16]],[[153462,442900],[-267,5082],[-262,812],[123,4043]],[[289969,445455],[-804,-6748]],[[289165,438707],[-411,-130]],[[288754,438577],[180,478],[-59,7138]],[[288875,446193],[-56,6647]],[[288875,446193],[-721,12]],[[288154,446205],[-1098,185]],[[279142,445862],[-1549,13]],[[277593,445875],[-281,-13]],[[277312,445862],[2,4212]],[[164792,452768],[-2,-15095]],[[164790,437673],[-3709,-1]],[[161081,437672],[-321,-8]],[[166713,433511],[-9,-8230],[-71,-9],[-2,-9666],[318,13]],[[166949,415619],[45,-3525],[106,-405],[-51,-2898]],[[167049,408791],[-264,-205]],[[166785,408586],[-505,-709],[-438,-1338],[-205,182],[104,-3535],[-151,-1501]],[[165590,401685],[-469,-1590],[-343,3]],[[164778,400098],[-9,2800]],[[164769,402898],[12,2458]],[[164781,405356],[6,5108]],[[164787,410464],[3,27209]],[[253863,447010],[-4,809],[-643,-82]],[[253216,447737],[-1,2]],[[274786,450060],[0,-6514]],[[274786,443546],[-1348,42]],[[273438,443588],[-1,3974]],[[273437,447562],[3,2573]],[[273440,450135],[1346,2308]],[[245868,448608],[-3,-3243]],[[245865,445365],[-1300,22]],[[244565,445387],[1,4872]],[[249404,451036],[-5,-5898]],[[249399,445138],[-644,-16]],[[248755,445122],[-900,11]],[[247855,445133],[-158,3692]],[[226556,448067],[2,-4023]],[[226558,444044],[-753,-845],[-533,-1]],[[225272,443198],[-12,4849]],[[251329,445946],[-636,7],[1,-814]],[[250694,445139],[-1295,-1]],[[232889,450323],[0,-4862],[165,-1791]],[[233054,443670],[-1389,16]],[[231665,443686],[-256,439],[-95,2839]],[[243270,450293],[6,-6524]],[[243276,443769],[-326,-13]],[[242950,443756],[-979,-17]],[[241971,443739],[-2,6572]],[[237742,450323],[68,-6545]],[[237810,443778],[-1255,-153]],[[236555,443625],[-107,1818],[-1,4872]],[[234183,450322],[1,-4860],[144,-1818]],[[234328,443644],[-317,10]],[[234011,443654],[-957,16]],[[235153,450303],[2,-4797],[121,-1872]],[[235276,443634],[-948,10]],[[241971,443739],[-319,-4]],[[241652,443735],[-960,-14]],[[240692,443721],[-26,6583]],[[239045,450317],[53,-6615]],[[239098,443702],[0,-314]],[[239098,443388],[-1288,390]],[[240692,443721],[-320,-6]],[[240372,443715],[-1274,-13]],[[236555,443625],[-1279,9]],[[244565,445387],[-5,-3261]],[[244560,442126],[-322,12]],[[244238,442138],[-104,1623],[-858,8]],[[273437,447562],[-275,-15],[3,-1357],[-539,-11],[-3,-1320],[-265,-4]],[[272358,444855],[-272,1146]],[[272086,446001],[570,2452],[784,1682]],[[277312,445862],[-602,-34],[-476,-2456]],[[276234,443372],[-1448,-20]],[[274786,443352],[0,194]],[[247855,445133],[-29,-825],[-657,-1555],[-5,-2418]],[[247164,440335],[-1778,120]],[[245386,440455],[0,4]],[[245386,440459],[89,1581],[707,640]],[[246182,442680],[363,171],[871,2340],[76,2625]],[[300676,447525],[-94,-1308],[-254,2051]],[[246182,442680],[7,2674],[-324,11]],[[300218,448474],[-108,-2110]],[[300110,446364],[-154,-894],[-847,-110]],[[299109,445360],[5,802]],[[260465,448379],[14,-4389]],[[260479,443990],[3,-1610]],[[260482,442380],[-1130,-18]],[[259352,442362],[-1,806]],[[259351,443168],[-9,5238]],[[259351,443168],[-1136,-50],[2,-810]],[[258217,442308],[-163,-1]],[[258054,442307],[104,4029],[-102,2054]],[[258054,442307],[-490,-569],[-231,-2173],[-408,-905]],[[256925,438660],[-7,8808]],[[256918,447468],[301,934]],[[262847,444118],[-1085,-69]],[[261762,444049],[-7,4341]],[[262843,447208],[4,-3090]],[[261762,444049],[-1283,-59]],[[229132,448075],[0,-5329]],[[229132,442746],[-759,-1483],[-211,138]],[[228162,441401],[4,6681]],[[228162,441401],[-318,227]],[[227844,441628],[-643,-1180]],[[227201,440448],[0,1151],[-292,1]],[[226909,441600],[0,2456],[-351,-12]],[[230417,446975],[2,-2974],[320,-2434]],[[230739,441567],[-395,2]],[[230344,441569],[-276,747],[-936,373]],[[229132,442689],[0,57]],[[218529,448020],[-5,-6446]],[[218524,441574],[-1550,27]],[[216974,441601],[-383,9]],[[216591,441610],[1,6474]],[[216591,441610],[-1609,-13]],[[225272,443198],[0,-1617]],[[225272,441581],[-1262,6]],[[224010,441587],[-20,-2]],[[223990,441585],[-2,6447]],[[222706,448036],[25,-6450]],[[222731,441586],[-12,-6463]],[[222719,435123],[-609,-9]],[[222110,435114],[-2224,2]],[[219886,435116],[-74,6454]],[[219812,441570],[0,6451]],[[219812,441570],[-1288,4]],[[223990,441585],[-1259,1]],[[267414,445850],[-697,-80]],[[266717,445770],[-430,-32],[-394,-2264],[-479,-1510]],[[265414,441964],[1,1360]],[[265415,443324],[7,4330]],[[265749,447718],[781,161]],[[266530,447879],[6,1]],[[266536,447880],[30,6]],[[266566,447886],[16,1]],[[266582,447887],[26,6]],[[266608,447893],[806,-2043]],[[255265,442934],[-2,-3134]],[[255263,439800],[-1357,-110],[6,-1614],[-648,-80]],[[253264,437996],[-23,4868]],[[253241,442864],[-25,4873]],[[267414,445850],[546,-1925],[381,1255],[294,-1578],[-868,-836]],[[267767,442766],[-50,-4]],[[267717,442762],[-15,-1]],[[267702,442761],[-769,5],[-213,794]],[[266720,443560],[-3,2210]],[[253241,442864],[-956,-107]],[[252285,442757],[-18,3246]],[[265415,443324],[-1279,-37]],[[264136,443287],[-108,529],[-52,3572]],[[273438,443588],[0,-2864]],[[273438,440724],[-1081,8]],[[272357,440732],[1,4123]],[[299109,445360],[-23,-3357],[-140,-1618]],[[298946,440385],[-436,-421],[-482,288],[-416,-840],[-291,2723]],[[297321,442135],[350,235],[-50,1824],[-299,-181],[-100,1106]],[[256110,445860],[11,-7106]],[[256121,438754],[-158,-435]],[[255963,438319],[-386,-1051],[-314,71]],[[255263,437339],[0,2461]],[[255271,447419],[248,-1015],[591,-544]],[[256925,438660],[-559,918],[-245,-824]],[[256110,445860],[808,1608]],[[264136,443287],[1,-1079]],[[264137,442208],[-1288,-29]],[[262849,442179],[-2,1939]],[[211125,442395],[-24,-800]],[[211101,441595],[-1879,-26]],[[209222,441569],[-1,3184]],[[231665,443686],[164,-2163]],[[231829,441523],[-1090,44]],[[300704,446133],[-85,-2793],[-210,505],[295,2288]],[[300975,443507],[-208,-748],[-4,4059]],[[294871,442976],[348,-1153],[336,-2034],[-121,-1229]],[[295434,438560],[-200,-1187],[-705,-2090],[-626,-1185]],[[293903,434098],[-195,2025],[683,2086],[-173,2860]],[[294218,441069],[41,2994]],[[209222,441569],[-4,-7295]],[[209218,434274],[-2479,-59]],[[206739,434215],[-930,1]],[[300110,446364],[27,-3366],[-179,-1833],[-1042,-1242],[30,462]],[[288154,446205],[-316,-4003]],[[287838,442202],[-487,-794],[-761,-136]],[[286590,441272],[175,3068]],[[297321,442135],[207,-2884],[-531,-114]],[[296997,439137],[-298,3270],[-256,-278],[-17,2897]],[[296997,439137],[9,-62]],[[297006,439075],[21,-79]],[[297027,438996],[-1112,58],[-473,-1600],[-8,1106]],[[288754,438577],[-264,-1321]],[[288490,437256],[-133,486],[-104,2827],[-415,1633]],[[293001,442420],[-13,-2190]],[[292988,440230],[-683,-3302]],[[292305,436928],[-373,1054]],[[291932,437982],[-934,2867]],[[290998,440849],[-150,1309]],[[252285,442757],[26,-6500]],[[252311,436257],[-960,-39],[-1,-3314]],[[251350,432904],[-324,-34]],[[251026,432870],[0,3329],[-319,-14]],[[250707,436185],[-5,3838]],[[250702,440023],[-8,5116]],[[272357,440732],[-1,0]],[[272356,440732],[-569,47],[73,-1359],[-321,-13]],[[271539,439407],[-538,-38]],[[271001,439369],[3,1405],[-263,14],[8,2861]],[[270749,443649],[623,-332],[714,2684]],[[280638,445459],[-3,-3672],[463,-4],[46,-3243]],[[281144,438540],[-1140,433],[-177,-968]],[[279827,438005],[-70,1137],[-624,1848],[-379,-396]],[[278754,440594],[88,2092],[292,-302],[8,3478]],[[278754,440594],[-317,-166]],[[278437,440428],[5,1839],[-538,103],[-216,-927]],[[277688,441443],[-95,4432]],[[277688,441443],[-1,-945],[-351,-264],[-253,-2768]],[[277083,437466],[-849,-18]],[[276234,437448],[0,5924]],[[269692,442195],[16,-2668]],[[269708,439527],[-1391,120]],[[268317,439647],[-18,2605],[-150,10]],[[268149,442262],[253,825],[384,-407]],[[268786,442680],[-5,-153]],[[268781,442527],[480,-1172],[431,840]],[[266720,443560],[-15,-4583]],[[266705,438977],[-3,-1624]],[[266702,437353],[-1282,19]],[[265420,437372],[-6,4592]],[[281836,443087],[1,-1967],[-292,-2833]],[[281545,438287],[-401,253]],[[290998,440849],[-384,-1439],[-427,-3445]],[[290187,435965],[-461,1079],[82,1909],[-643,-246]],[[245386,440459],[-830,46],[4,1621]],[[285110,445236],[175,-3416],[441,-1905],[405,-536]],[[286131,439379],[-539,-2232]],[[285592,437147],[-256,304]],[[285336,437451],[-455,-615]],[[284881,436836],[-228,-951],[-463,-355]],[[284190,435530],[-1068,5402],[-197,2226]],[[286590,441272],[-78,-1277]],[[286512,439995],[-381,-616]],[[250702,440023],[-480,-176],[-57,-1256],[-306,11],[0,-1588]],[[249859,437014],[-479,1]],[[249380,437015],[-1,1587],[-610,12]],[[248769,438614],[-14,6508]],[[248769,438614],[-31,-1592],[-323,7]],[[248415,437029],[-1262,34]],[[247153,437063],[11,3272]],[[192516,434205],[-2782,5],[1,4724]],[[262849,442179],[1,-2880]],[[262850,439299],[-1082,-133]],[[261768,439166],[-6,4883]],[[261768,439166],[-323,-1]],[[261445,439165],[-639,38],[-321,533]],[[260485,439736],[-3,2644]],[[226909,441600],[-6,-1597],[-967,-298],[-621,-536],[-31,2413]],[[225284,441582],[-12,-1]],[[293006,440243],[-18,-18]],[[292988,440225],[0,5]],[[294218,441069],[-1212,-826]],[[302428,442673],[372,-1939],[-773,-264],[113,1840],[288,363]],[[271001,439369],[-263,5],[-6,-1403],[-273,-2],[-271,-1169],[-5,-1369]],[[270183,435431],[-459,41]],[[269724,435472],[-16,4055]],[[269692,442195],[936,1653],[121,-199]],[[244238,442138],[-5,-4866]],[[244233,437272],[-641,8]],[[243592,437280],[-640,13]],[[242952,437293],[-2,6463]],[[239098,443388],[2,-6149]],[[239100,437239],[-639,12]],[[238461,437251],[-646,14]],[[237815,437265],[-5,6513]],[[242952,437293],[-653,-16]],[[242299,437277],[-642,-14]],[[241657,437263],[-5,6472]],[[237815,437265],[-627,-98]],[[237188,437167],[-633,7]],[[236555,437174],[0,6451]],[[241657,437263],[-642,-12]],[[241015,437251],[-638,-8]],[[240377,437243],[-5,6472]],[[240377,437243],[-639,-2]],[[239738,437241],[-638,-2]],[[234011,443654],[-4,-6441]],[[234007,437213],[-636,14]],[[233371,437227],[-1382,-2]],[[231989,437225],[-120,575]],[[231869,437800],[88,2413],[-128,1310]],[[235276,443634],[0,-6443]],[[235276,437191],[-633,8]],[[234643,437199],[-636,14]],[[236555,437174],[-638,8]],[[235917,437182],[-641,9]],[[274786,443352],[1,-6623]],[[274787,436729],[-1339,18]],[[273448,436747],[-10,3977]],[[267702,442761],[447,-499]],[[268317,439647],[3,-645]],[[268320,439002],[-1615,-25]],[[276234,437448],[-271,-1900]],[[275963,435548],[-140,819],[-1037,201]],[[274786,436568],[1,161]],[[265420,437372],[-967,-37]],[[264453,437335],[-1,4878],[-315,-5]],[[259352,442362],[14,-4890],[-65,-6]],[[259301,437466],[-1086,-26]],[[258215,437440],[2,4868]],[[284190,435530],[0,-456]],[[284190,435074],[-1015,-1516],[-180,1961],[-470,731],[-132,1393],[-289,-77],[-14,1365],[-389,-1859]],[[281701,437072],[-156,1215]],[[155294,437598],[-138,-2080],[141,-717],[-208,-2138],[-386,312],[218,-3716],[-6,-13593]],[[154915,415666],[-1334,-12]],[[153581,415654],[-243,1901],[-703,2914],[-129,3412],[387,4770],[132,-280],[308,6363],[-131,874],[260,7292]],[[253264,437996],[-21,-1627]],[[253243,436369],[-932,-112]],[[229132,442689],[-7,-7583]],[[229125,435106],[-7,1]],[[229118,435107],[-1274,15]],[[227844,435122],[0,6506]],[[230344,441569],[400,-3784]],[[230744,437785],[23,-2699]],[[230767,435086],[-404,-540]],[[230363,434546],[0,542],[-1238,18]],[[260485,439736],[-89,-4618]],[[260396,435118],[-728,-71]],[[259668,435047],[-364,782],[-3,1637]],[[213238,438374],[-29,-4081]],[[213209,434293],[-91,-1]],[[213118,434292],[-2030,-1]],[[211088,434291],[13,7304]],[[278437,440428],[-17,-5242]],[[278420,435186],[-185,-771],[-669,-629],[-401,1395],[-70,2246]],[[277095,437427],[-12,39]],[[188693,440977],[-212,-2506],[-449,-761],[-413,552],[-140,-1426]],[[187479,436836],[-928,257],[-847,-1417]],[[258215,437440],[-1290,17]],[[256925,437457],[0,1203]],[[288490,437256],[-135,-732]],[[288355,436524],[-329,-581],[98,-1543],[-739,-1772]],[[287385,432628],[-586,684]],[[286799,433312],[-59,3514],[-251,1238],[23,1931]],[[264453,437335],[-316,-6]],[[264137,437329],[-325,1637],[-962,-17]],[[262850,438949],[0,350]],[[245386,440455],[-115,-1740],[344,-1568],[113,-1545]],[[245728,435602],[-1175,26]],[[244553,435628],[2,1613],[-322,31]],[[216974,441601],[-2,-6470],[56,-810]],[[217028,434321],[-2233,-1]],[[214795,434320],[-10,4053]],[[219886,435116],[3,-6448]],[[219889,428668],[-1545,-2]],[[218344,428666],[-1309,-44]],[[217035,428622],[-7,5699]],[[304069,440802],[121,-1847],[-424,-197],[303,2044]],[[227844,435122],[-1276,-6]],[[226568,435116],[-6,2365]],[[226562,437481],[639,2967]],[[211088,434291],[-535,-10]],[[210553,434281],[-1335,-7]],[[226562,437481],[-534,-2005],[-733,-3605]],[[225295,431871],[-11,3241]],[[225284,435112],[0,6470]],[[225284,435112],[-1212,5]],[[224072,435117],[-74,0]],[[223998,435117],[12,6470]],[[223998,435117],[-1279,6]],[[231869,437800],[-1125,-15]],[[189127,436934],[-680,-1517],[-235,-1307],[316,-2474],[-193,-1471],[-256,343]],[[188079,430508],[-272,1154]],[[187807,431662],[-214,1796],[-114,3378]],[[279827,438005],[-264,-1317],[-1,-4191]],[[279562,432497],[-1143,101]],[[278419,432598],[1,2588]],[[157829,437649],[146,-436],[-429,-4820],[-421,-4012],[145,-2152],[-836,-2813],[-185,-2444]],[[156249,420972],[222,-710],[144,-5037]],[[156615,415225],[-1701,-22],[1,463]],[[293524,432082],[-325,588]],[[293199,432670],[123,2992],[-28,2071],[-288,2510]],[[293903,434098],[-379,-2016]],[[291932,437982],[-383,-2133]],[[291549,435849],[-417,-3054],[-322,-187]],[[290810,432608],[-554,3400]],[[290256,436008],[-69,-43]],[[272356,440732],[-5,-6697]],[[272351,434035],[-74,-1524],[-634,144]],[[271643,432655],[-114,1381]],[[271529,434036],[10,5371]],[[273448,436747],[0,-2714],[-243,-5]],[[273205,434028],[-854,7]],[[247153,437063],[-6,-1623]],[[247147,435440],[-963,88]],[[246184,435528],[-456,35]],[[245728,435563],[0,39]],[[250707,436185],[-548,-8],[-300,837]],[[293219,434221],[-863,2534]],[[292356,436755],[-51,173]],[[292988,440225],[220,-3323],[11,-2681]],[[286799,433312],[-277,-2753],[-204,-490]],[[286318,430069],[-412,1992]],[[285906,432061],[44,1187],[-292,2181],[-66,1718]],[[255263,437339],[2,-2898]],[[255265,434441],[-1688,-234]],[[253577,434207],[-321,-56]],[[253256,434151],[-13,2218]],[[261445,439165],[-77,-4820]],[[261368,434345],[-859,-54]],[[260509,434291],[-113,827]],[[294554,427872],[-111,3761]],[[294443,431633],[-93,1192],[769,-305],[174,1128],[1072,-46],[376,298],[903,2985],[-441,-2838],[-371,-1388],[292,-372],[379,1926],[532,898],[205,-1023],[552,1761],[82,-626],[-1554,-3632],[-192,472],[-405,-1606],[-231,346],[-428,-1336],[-389,99],[-351,-945],[-238,353],[-532,-1102]],[[269724,435472],[-278,-7],[9,-1349]],[[269455,434116],[-813,51]],[[268642,434167],[-292,19]],[[268350,434186],[-30,4816]],[[256925,437457],[-1,-4839]],[[256924,432618],[-156,-1387],[-315,-5],[2,-1882]],[[256455,429344],[-469,-1]],[[255986,429343],[-23,8976]],[[271529,434036],[-1228,59]],[[270301,434095],[-118,1336]],[[262850,438949],[1,-4898]],[[262851,434051],[1,-1244]],[[262852,432807],[-755,-89]],[[262097,432718],[-730,-14]],[[261367,432704],[1,1641]],[[268350,434186],[-789,-60]],[[267561,434126],[-857,-30]],[[266704,434096],[-2,3257]],[[281701,437072],[-194,-834],[74,-2825],[-363,-706],[-462,-3035],[47,-403]],[[280803,429269],[26,-143]],[[280829,429126],[-1257,-6]],[[279572,429120],[-10,3377]],[[192649,434205],[0,-3424]],[[192649,430781],[-728,405],[-315,-879],[-787,-888],[-261,131],[-420,-1293]],[[190138,428257],[-340,-1866],[-343,-529],[-618,1479],[-69,1143],[-267,-1698],[-177,190]],[[188324,426976],[-245,3532]],[[284190,435074],[-613,-3675]],[[283577,431399],[-884,-2182]],[[282693,429217],[-399,259],[-333,-970],[-479,890]],[[281482,429396],[-679,-127]],[[290256,436008],[-428,-2351]],[[289828,433657],[-466,-1940],[-519,-917]],[[288843,430800],[-280,2542],[218,671],[-426,2511]],[[264137,437329],[-1,-3255],[-160,-7]],[[263976,434067],[-1125,-16]],[[249380,437015],[-1,-3256]],[[249379,433759],[-966,19]],[[248413,433778],[2,3251]],[[214795,434320],[-2,-28]],[[214793,434292],[-1584,1]],[[255986,429343],[-721,8]],[[255265,429351],[0,5090]],[[292587,430897],[-395,1442]],[[292192,432339],[-104,1880],[-539,1630]],[[292356,436755],[-165,-2166],[367,-971],[29,-2721]],[[231989,437225],[3,-1985]],[[231992,435240],[-431,236],[-484,-1309],[-310,919]],[[164787,410464],[-407,-275]],[[164380,410189],[102,1081],[5,3235],[-258,1376],[-24,1351],[-366,545],[-472,2488],[-707,1250],[-457,-2310],[-371,1200],[1,3539],[-743,-22]],[[161090,423922],[22,8573],[-31,5177]],[[161090,423922],[-473,4]],[[160617,423926],[-1427,-349],[-831,-1032],[-549,336],[-407,-1138],[-271,689],[-883,-1460]],[[285336,437451],[-167,-4196],[366,397],[371,-1591]],[[286318,430069],[-895,-2188]],[[285423,427881],[-525,-887],[-165,332]],[[284733,427326],[26,188]],[[284759,427514],[265,1448],[124,3090]],[[285148,432052],[-223,1660],[-44,3124]],[[259668,435047],[-2,-813]],[[259666,434234],[-620,-58],[0,-1610]],[[259046,432566],[-835,5]],[[258211,432571],[4,4869]],[[226568,435116],[1,-6480]],[[226569,428636],[-1260,-4]],[[225309,428632],[-14,-1]],[[225295,428631],[0,3240]],[[258211,432571],[-314,19]],[[257897,432590],[-973,28]],[[277095,437427],[-6,-9328]],[[277089,428099],[-1270,90]],[[275819,428189],[-27,3362]],[[275792,431551],[-19,2706],[190,1291]],[[278419,432598],[1,-2521],[-655,-4576]],[[277765,425501],[-675,2580]],[[277090,428081],[-1,18]],[[266704,434096],[-266,-3234]],[[266438,430862],[-1015,34]],[[265423,430896],[-1,1871]],[[265422,432767],[-2,4605]],[[265422,432767],[-638,-293],[0,-813],[-644,-36]],[[264140,431625],[-164,2442]],[[243592,437280],[-8,-4878]],[[243584,432402],[-1283,-20]],[[242301,432382],[-2,4895]],[[244553,435628],[-3,-4868],[-104,1]],[[244446,430761],[-862,15]],[[243584,430776],[0,1626]],[[242301,432382],[-1281,-16]],[[241020,432366],[-5,4885]],[[238461,437251],[3,-4896]],[[238464,432355],[-1277,-24]],[[237187,432331],[1,4836]],[[239738,437241],[4,-4883]],[[239742,432358],[-1278,-3]],[[241020,432366],[-1278,-8]],[[233371,437227],[-2,-4811]],[[233369,432416],[-1193,2]],[[232176,432418],[-184,2822]],[[234643,437199],[-2,-4800]],[[234641,432399],[-1272,17]],[[235917,437182],[-1,-4805]],[[235916,432377],[-1275,22]],[[237187,432331],[-1271,46]],[[187807,431662],[-565,-723],[-182,1854],[-716,-2763]],[[186344,430030],[-640,5646]],[[248413,433778],[-2,-4886]],[[248411,428892],[-1277,44]],[[247134,428936],[13,6504]],[[285148,432052],[-398,107],[-1152,-1498]],[[283598,430661],[-21,738]],[[251026,432870],[-1183,-88]],[[249843,432782],[68,968],[-532,9]],[[292970,430541],[-431,-269]],[[292539,430272],[48,625]],[[293219,434221],[-249,-3680]],[[274786,436568],[0,-4189]],[[274786,432379],[-1580,38]],[[273206,432417],[-1,1611]],[[288843,430800],[-376,-510]],[[288467,430290],[-415,-967]],[[288052,429323],[-667,3305]],[[275792,431551],[-1004,-68]],[[274788,431483],[-2,896]],[[253256,434151],[34,-7012],[-625,-15]],[[252665,427124],[-321,-16],[-28,2626],[-961,-79]],[[251355,429655],[-5,3249]],[[290810,432608],[-339,-2310]],[[290471,430298],[-220,-1439],[-616,-2210]],[[289635,426649],[-20,311]],[[289615,426960],[68,3155],[341,1723],[-196,1819]],[[292192,432339],[-162,208],[-116,-3152]],[[291914,429395],[-248,-1212]],[[291666,428183],[-265,1560],[-471,-723]],[[290930,429020],[-459,1278]],[[186344,430030],[155,-1074],[97,-4631]],[[186596,424325],[-109,-154],[37,-2824],[149,-2529],[-95,-2971]],[[186578,415847],[-452,-1998],[-1271,-19],[-3480,52]],[[181375,413882],[1,3927]],[[245728,435563],[-37,-2695],[-356,-1884],[-63,-2389]],[[245272,428595],[-424,1079]],[[244848,429674],[-402,1087]],[[246184,435528],[-12,-8061]],[[246172,427467],[-319,64]],[[245853,427531],[-783,-26]],[[245070,427505],[202,1090]],[[247134,428936],[-3,-1616]],[[247131,427320],[-959,147]],[[270301,434095],[8,-6024]],[[270309,428071],[-263,-11],[2,-1850]],[[270048,426210],[-326,-246]],[[269722,425964],[-6,3195],[-215,1865],[-46,3092]],[[232176,432418],[-58,-2195]],[[232118,430223],[-1754,3]],[[230364,430226],[-1,4320]],[[227844,435122],[-1,-6484]],[[227843,428638],[-1271,-2]],[[226572,428636],[-3,0]],[[225295,428631],[-1221,12]],[[224074,428643],[-2,6474]],[[222110,435114],[25,-6995]],[[222135,428119],[-630,263]],[[221505,428382],[-943,281]],[[220562,428663],[-673,5]],[[224074,428643],[-9,-170]],[[224065,428473],[-507,-690],[-760,186]],[[222798,427969],[-663,150]],[[229118,435107],[-7,-6481]],[[229111,428626],[-1268,12]],[[230364,430226],[0,-4858]],[[230364,425368],[-1251,12]],[[229113,425380],[-2,3246]],[[260509,434291],[14,-6500]],[[260523,427791],[-630,-27]],[[259893,427764],[-208,-13],[-19,6483]],[[255265,429351],[-1,-4575]],[[255264,424776],[-1140,-98]],[[254124,424678],[-508,40],[-39,9489]],[[261367,432704],[3,-4889],[-318,-15]],[[261052,427800],[-529,-9]],[[210532,425375],[-1577,18],[-7,-9746]],[[208948,415647],[-2260,-10]],[[206688,415637],[-254,-1]],[[206434,415636],[-6,4869]],[[206428,420505],[-5,1629],[310,-3],[6,12084]],[[210553,434281],[-21,-8906]],[[217035,428622],[-272,6]],[[216763,428628],[-1968,-10]],[[214795,428618],[0,969]],[[214795,429587],[-2,4705]],[[201978,423908],[55,-1918],[16,-7760]],[[202049,414230],[-1129,-108]],[[200920,414122],[-15,3214]],[[200905,417336],[-2,2479],[-1118,-26]],[[199785,419789],[29,5937],[320,1081],[-11,7495]],[[201402,434297],[26,-1459],[436,-670],[159,-1784],[-185,-3985],[140,-2491]],[[204199,424684],[-292,-2572],[-164,468],[-589,-929],[-98,691],[-519,-162],[-107,1114],[-340,-603],[-112,1217]],[[203264,434210],[15,-1191],[530,-3758],[390,-4577]],[[199785,419789],[-1878,-31],[-2612,18]],[[195295,419776],[7,8024]],[[195302,427800],[-6,6461]],[[213087,423794],[-321,0]],[[212766,423794],[-1910,-39]],[[210856,423755],[-5,1618],[-319,2]],[[213118,434292],[7,-4708]],[[213125,429584],[-38,-5790]],[[214795,429587],[-1670,-3]],[[195302,427800],[-315,562],[-118,3255],[-531,-11],[-250,-2208],[-269,1599],[-807,-1146],[-299,857]],[[192713,430708],[-64,73]],[[176393,418012],[113,-5116],[-101,-2197],[49,-2635],[-263,-2403],[2,-5633]],[[176193,400028],[-1933,-7]],[[174260,400021],[29,16934],[-18,16297],[27,1004]],[[174260,400021],[-2037,36],[-1236,-1303]],[[170987,398754],[153,2224],[-118,2607],[482,3073],[266,298],[86,1838],[-89,2169],[75,3206],[-204,1481]],[[171638,415650],[673,9777]],[[206428,420505],[-1667,-25]],[[204761,420480],[-562,4204]],[[259893,427764],[3,-1604]],[[259896,426160],[-839,-53]],[[259057,426107],[-11,6459]],[[268642,434167],[-7,-5295]],[[268635,428872],[-365,-116]],[[268270,428756],[-705,-39]],[[267565,428717],[-4,5409]],[[254124,424678],[8,-1608]],[[254132,423070],[-1470,-13]],[[252662,423057],[3,4067]],[[269722,425964],[-798,-95]],[[268924,425869],[-10,2970],[-279,33]],[[267565,428717],[-860,-299]],[[266705,428418],[-211,275],[-56,2169]],[[271643,432655],[-7,-4586]],[[271636,428069],[-1327,2]],[[264140,431625],[-158,-803],[1,-2439]],[[263983,428383],[-165,-1]],[[263818,428382],[0,807],[-964,-2]],[[262854,429187],[-2,3620]],[[273206,432417],[-2,-3235]],[[273204,429182],[-418,-80],[-224,-1340]],[[272562,427762],[-923,-316]],[[271639,427446],[-3,623]],[[249843,432782],[-231,-3232]],[[249612,429550],[-356,-3635],[-528,-731]],[[248728,425184],[-4,2060],[-320,38],[7,1610]],[[289615,426960],[-382,-1335]],[[289233,425625],[-268,2542],[-150,-335],[-348,2458]],[[171638,415650],[-2672,-44],[-2017,13]],[[288052,429323],[-372,-1084]],[[287680,428239],[-781,-2648],[-748,-727]],[[286151,424864],[-265,1103]],[[285886,425967],[-463,1914]],[[274788,431483],[-1,-3962]],[[274787,427521],[-415,-1047]],[[274372,426474],[-540,317]],[[273832,426791],[-154,2374],[-474,17]],[[251355,429655],[-154,-1644],[-415,-1270],[-378,-47]],[[250408,426694],[-162,394],[-9,2476],[-625,-14]],[[262854,429187],[-1,-2903]],[[262853,426284],[-742,-74]],[[262111,426210],[-14,6508]],[[265423,430896],[0,-3263]],[[265423,427633],[-634,-31],[-321,800],[-485,-19]],[[188324,426976],[-114,-601]],[[188210,426375],[-465,-891],[-618,-2134],[-142,895],[-389,80]],[[293524,432082],[-12,-1259],[-325,-351]],[[293187,430472],[-27,1524]],[[293160,431996],[39,674]],[[262111,426210],[-369,-18]],[[261742,426192],[-685,-6]],[[261057,426186],[-5,1614]],[[294554,427872],[-875,-1167]],[[293679,426705],[-79,470]],[[293600,427175],[63,323]],[[293663,427498],[119,2121],[-126,535]],[[293656,430154],[90,1670],[410,854],[287,-1045]],[[257897,432590],[-1,-3261]],[[257896,429329],[-463,33],[-65,-3266]],[[257368,426096],[-903,15]],[[256465,426111],[-10,3233]],[[259057,426107],[-583,-24]],[[258474,426083],[2,2440],[-422,-1],[-158,807]],[[279572,429120],[-482,-6118]],[[279090,423002],[-351,-508],[-158,819],[-661,807],[-155,1381]],[[292539,430272],[83,-963]],[[292622,429309],[-118,-889]],[[292504,428420],[-590,975]],[[233369,432416],[31,-5977]],[[233400,426439],[-1091,87]],[[232309,426526],[-329,3028],[138,669]],[[234641,432399],[38,-6061]],[[234679,426338],[-801,63]],[[233878,426401],[-478,38]],[[235916,432377],[-1,-6119]],[[235915,426258],[-449,10]],[[235466,426268],[-787,70]],[[243584,430776],[5,-4002]],[[243589,426774],[-630,136]],[[242959,426910],[-660,-98]],[[242299,426812],[2,5570]],[[242299,426812],[-476,-61]],[[241823,426751],[-800,-120]],[[241023,426631],[-3,5735]],[[237187,432331],[-2,-6015]],[[237185,426316],[-604,-38]],[[236581,426278],[-666,-20]],[[238464,432355],[-1,-5921]],[[238463,426434],[-606,-50]],[[237857,426384],[-672,-68]],[[239742,432358],[1,-5855]],[[239743,426503],[-772,-67]],[[238971,426436],[-508,-2]],[[241023,426631],[-213,-26]],[[240810,426605],[-1067,-102]],[[284759,427514],[-273,768],[-697,261]],[[283789,428543],[-185,151],[-6,1967]],[[293187,430472],[-271,-1807],[244,3331]],[[195295,419776],[-1,-10465]],[[195294,409311],[0,-3031]],[[195294,406280],[-156,-665],[-2555,-5]],[[192583,405610],[1,135]],[[192584,405745],[-39,1805],[122,2542],[304,1923],[-230,5]],[[192741,412020],[-28,18688]],[[275819,428189],[-81,-1216],[-510,-2451]],[[275228,424522],[-441,-3]],[[274787,424519],[0,3002]],[[282497,422685],[-451,359],[271,1888],[-71,939],[431,2360],[16,986]],[[283789,428543],[-584,-2082],[-708,-3776]],[[192741,412020],[-2481,120]],[[190260,412140],[-95,1614]],[[190165,413754],[-27,14503]],[[266705,428418],[4,-3381]],[[266709,425037],[-283,-21]],[[266426,425016],[-1003,645]],[[265423,425661],[0,1972]],[[292970,430541],[-211,-2552],[-210,-378],[73,1698]],[[245070,427505],[-470,-582],[-146,-1016],[115,-2813],[-151,-420]],[[244418,422674],[-829,4100]],[[293663,427498],[-315,245]],[[293348,427743],[-288,1615]],[[293060,429358],[139,999],[457,-203]],[[290930,429020],[33,-2516],[-174,-1499],[76,-1477]],[[290865,423528],[-301,-1443],[-241,-92]],[[290323,421993],[-332,1496],[-16,2220],[-340,940]],[[289233,425625],[-418,-2203]],[[288815,423422],[-127,532]],[[288688,423954],[-1008,4285]],[[232309,426526],[155,-1143]],[[232464,425383],[-995,-12]],[[231469,425371],[-1105,-3]],[[291666,428183],[-6,-1402]],[[291660,426781],[-177,-793],[187,-1217],[-446,-2170]],[[291224,422601],[-359,927]],[[252662,423057],[2,-2183]],[[252664,420874],[-322,-1]],[[252342,420873],[-1598,9]],[[250744,420882],[-321,803]],[[250423,421685],[-15,5009]],[[214795,428618],[0,-4795]],[[214795,423823],[-1708,-29]],[[250423,421685],[-945,-94]],[[249478,421591],[-311,-17],[-6,2159],[-579,14]],[[248582,423747],[146,1437]],[[282497,422685],[136,-2142]],[[282633,420543],[-450,-3766]],[[282183,416777],[-753,1929]],[[281430,418706],[-282,1104],[-60,1366]],[[281088,421176],[343,3266],[-54,1256]],[[281377,425698],[-185,2188],[290,1510]],[[281088,421176],[-85,-1011],[-408,1094],[-38,-1088],[-480,1549]],[[280077,421720],[157,810],[68,2530],[527,4066]],[[293348,427743],[-53,-1237],[-451,497],[216,2355]],[[256465,426111],[5,-1623]],[[256470,424488],[-1206,27]],[[255264,424515],[0,261]],[[292504,428420],[-134,-1705]],[[292370,426715],[-710,66]],[[258474,426083],[-4,-2412]],[[258470,423671],[-889,5]],[[257581,423676],[-3,2416],[-210,4]],[[273832,426791],[-165,-804],[-57,-2449]],[[273610,423538],[-915,163]],[[272695,423701],[15,2450],[-148,1611]],[[263818,428382],[2,-6071],[59,-79]],[[263879,422232],[-1,-5]],[[263878,422227],[-1029,-28]],[[262849,422199],[4,4085]],[[280077,421720],[-106,-1566]],[[279971,420154],[-653,120],[-455,659]],[[278863,420933],[227,2069]],[[248582,423747],[-330,-1227],[-73,-1405],[-363,-2059]],[[247816,419056],[-702,92],[4,1628]],[[247118,420776],[13,6544]],[[268924,425869],[-64,-3813],[-274,84]],[[268586,422140],[-515,158],[-254,1409]],[[267817,423707],[174,1059],[2,2875],[277,1115]],[[267817,423707],[-635,190]],[[267182,423897],[2,1173],[-475,-33]],[[220562,428663],[2,-4872],[-317,7],[0,-1627]],[[220247,422171],[-287,-23]],[[219960,422148],[-1562,-10]],[[218398,422138],[-52,0]],[[218346,422138],[-2,6528]],[[225309,428632],[0,-6478]],[[225309,422154],[-1242,1]],[[224067,422155],[-2,6318]],[[221505,428382],[3,-6212]],[[221508,422170],[-1261,1]],[[218346,422138],[-1521,20]],[[216825,422158],[-55,-2]],[[216770,422156],[-7,6472]],[[216770,422156],[-1975,-23]],[[214795,422133],[0,1690]],[[227843,428638],[0,-6483]],[[227843,422155],[-1260,3]],[[226583,422158],[-9,0]],[[226574,422158],[-2,6478]],[[229113,425380],[-8,-3237]],[[229105,422143],[-1262,12]],[[226574,422158],[-1253,-4]],[[225321,422154],[-12,0]],[[284733,427326],[-651,-856],[-995,-3406],[-367,-2037]],[[282720,421027],[-87,-484]],[[190165,413754],[-530,18],[-2,802],[-456,2164],[59,2009],[-250,2239],[-310,-14],[-357,1412],[-183,1615],[74,2376]],[[224067,422155],[-8,0]],[[224059,422155],[-1261,5]],[[222798,422160],[0,5809]],[[265423,425661],[-341,-1036]],[[265082,424625],[-938,-40],[1,-1903],[-266,-450]],[[222798,422160],[-1257,8]],[[221541,422168],[-33,2]],[[278863,420933],[-360,-3256],[-302,-1293]],[[278201,416384],[-562,1959],[-401,-1171],[-297,845],[-366,-25]],[[276575,417992],[19,1314]],[[276594,419306],[179,812],[290,5226],[-200,1084],[227,1653]],[[288688,423954],[-466,-3820]],[[288222,420134],[-493,-1946]],[[287729,418188],[-773,3321]],[[286956,421509],[-805,3355]],[[276594,419306],[-123,1029],[-749,1490],[-494,2697]],[[271639,427446],[-58,-3551],[-110,16]],[[271471,423911],[-1324,215]],[[270147,424126],[-99,2084]],[[272695,423701],[-15,-2420],[-175,-1661]],[[272505,419620],[-793,132]],[[271712,419752],[19,2733],[-268,49],[8,1377]],[[285886,425967],[-397,-1498],[304,-5190]],[[285793,419279],[-424,-1382]],[[285369,417897],[-35,1119],[-345,857]],[[284989,419873],[-161,1874]],[[284828,421747],[-318,1084],[208,1376],[-107,1919],[122,1200]],[[261057,426186],[8,-3496]],[[261065,422690],[-374,7]],[[260691,422697],[-791,-21]],[[259900,422676],[0,530]],[[259900,423206],[-4,2954]],[[292731,427719],[-75,-1794],[-401,-1052],[179,2727],[297,119]],[[245853,427531],[-16,-6606]],[[245837,420925],[-6,-1698]],[[245831,419227],[-1653,137]],[[244178,419364],[22,898]],[[244200,420262],[218,2412]],[[274787,424519],[3,-1461]],[[274790,423058],[-309,-66]],[[274481,422992],[90,1247],[-199,2235]],[[247118,420776],[-1281,149]],[[284828,421747],[-989,-927],[-267,651],[-693,-2144]],[[282879,419327],[-159,1700]],[[244200,420262],[-1263,158]],[[242937,420420],[2,812]],[[242939,421232],[20,5678]],[[290323,421993],[609,-3619]],[[290932,418374],[-696,-1839]],[[290236,416535],[-114,1663]],[[290122,418198],[-1307,5224]],[[292370,426715],[-184,-1985],[143,-700]],[[292329,424030],[-474,-3179],[-254,-504]],[[291601,420347],[-383,1322],[6,932]],[[242939,421232],[-1108,36]],[[241831,421268],[-6,813]],[[241825,422081],[-2,4670]],[[274481,422992],[82,-1388],[-230,-2519]],[[274333,419085],[-58,-527]],[[274275,418558],[-502,43]],[[273773,418601],[49,4903],[-212,34]],[[241825,422081],[-931,-55]],[[240894,422026],[21,2915],[-105,1664]],[[240894,422026],[-478,-14]],[[240416,422012],[1,803],[-1426,-54]],[[238991,422761],[-20,3675]],[[233878,426401],[60,-5898]],[[233938,420503],[-1042,58]],[[232896,420561],[-163,921],[-269,3901]],[[238991,422761],[0,-2171]],[[238991,420590],[-1104,-44]],[[237887,420546],[-30,5838]],[[235466,426268],[95,-3437]],[[235561,422831],[-21,-4851]],[[235540,417980],[-1218,74]],[[234322,418054],[-79,2428],[-305,21]],[[190260,412140],[-1085,14]],[[189175,412154],[-1095,-4]],[[188080,412150],[-519,2499],[-397,-2608],[-336,-472],[101,2059],[-351,2219]],[[237887,420546],[-2,-2440]],[[237885,418106],[-1261,68]],[[236624,418174],[7,4610]],[[236631,422784],[-50,3494]],[[270147,424126],[-32,-4044]],[[270115,420082],[-784,126],[-762,580]],[[268569,420788],[17,1352]],[[262849,422199],[1,-796]],[[262850,421403],[-1157,-62]],[[261693,421341],[-3,1350]],[[261690,422691],[52,3501]],[[236631,422784],[-1070,47]],[[261690,422691],[-625,-1]],[[259900,423206],[-1060,-73],[0,-541]],[[258840,422592],[-370,1079]],[[257581,423676],[-3,-4054]],[[257578,419622],[-1105,9]],[[256473,419631],[1,2822]],[[256474,422453],[-4,2035]],[[232896,420561],[7,-55]],[[232903,420506],[-1276,-7]],[[231627,420499],[-157,10]],[[231470,420509],[-1,4862]],[[286956,421509],[-435,-1129],[-728,-1101]],[[266426,425016],[-87,-5114]],[[266339,419902],[-1291,820]],[[265048,420722],[34,3903]],[[210856,423755],[-18,-8096],[-651,-3]],[[210187,415656],[-1239,-9]],[[230364,425368],[0,-4857]],[[230364,420511],[0,-4861]],[[230364,415650],[-953,8]],[[229411,415658],[-308,1]],[[229103,415659],[2,6484]],[[231470,420509],[-1106,2]],[[267401,418295],[-101,-658]],[[267300,417637],[-828,69]],[[266472,417706],[25,2123],[-158,73]],[[267182,423897],[1,-3714],[212,-4],[6,-1884]],[[255264,424515],[-14,-6129]],[[255250,418386],[-5,-4933]],[[255245,413453],[-1127,-59]],[[254118,413394],[14,9676]],[[204761,420480],[44,-3995],[-107,-2111]],[[204698,414374],[-40,-1499]],[[204658,412875],[-515,-982],[-138,-1866]],[[204005,410027],[-269,-196],[-186,2235],[-440,1922],[-526,245]],[[202584,414233],[-535,-3]],[[265048,420722],[-23,-1666]],[[265025,419056],[-1141,243]],[[263884,419299],[-6,2928]],[[276575,417992],[2,-1690],[-338,-982]],[[276239,415320],[-807,669],[-645,-1057]],[[274787,414932],[1,1006]],[[274788,415938],[-1,2667]],[[274787,418605],[3,4453]],[[256474,422453],[-391,-1026],[-571,-2640],[86,-782]],[[255598,418005],[-348,381]],[[292681,417933],[-2,100]],[[292679,418033],[-411,-199],[-60,940],[-392,65],[-407,-1734]],[[291409,417105],[-97,1099]],[[291312,418204],[289,2143]],[[292329,424030],[617,-741],[71,-1974],[-164,-3756],[-172,374]],[[271712,419752],[-260,-1288]],[[271452,418464],[-1312,274]],[[270140,418738],[-25,1344]],[[290122,418198],[-263,-1712]],[[289859,416486],[-319,873],[49,-1506],[-196,-651]],[[289393,415202],[-235,1650]],[[289158,416852],[-265,413],[-399,2752],[-272,117]],[[160617,423926],[82,-1783],[339,-681],[-59,-1870],[-213,-1153]],[[160766,418439],[-393,-947],[-168,-2179],[-364,-1740],[-738,-94],[-15,-1615]],[[159088,411864],[-2483,10]],[[156605,411874],[10,3351]],[[164380,410189],[-1412,-44],[-74,-533],[-535,1856],[-381,-2584]],[[161978,408884],[-186,-741]],[[161792,408143],[-928,5114],[-31,2656],[120,1726],[-187,800]],[[268569,420788],[-30,-2813]],[[268539,417975],[-1138,320]],[[214795,422133],[0,-6444]],[[214795,415689],[6,-7987]],[[214801,407702],[-2099,-116]],[[212702,407586],[-2,8093],[66,8115]],[[212702,407586],[-979,-41]],[[211723,407545],[-1541,11]],[[210182,407556],[3,3232]],[[210185,410788],[2,4868]],[[249478,421591],[4,-3680]],[[249482,417911],[-1008,385],[-87,-647]],[[248387,417649],[-390,-864],[-421,-125],[-193,1284]],[[247383,417944],[433,1112]],[[273773,418601],[-955,197]],[[272818,418798],[-313,822]],[[258840,422592],[0,-2942]],[[258840,419650],[1,-653]],[[258841,418997],[-1263,-39]],[[257578,418958],[0,664]],[[291312,418204],[-380,170]],[[259900,422676],[0,-2968]],[[259900,419708],[-1060,-58]],[[274787,418605],[-454,480]],[[254118,413394],[-1462,-13]],[[252656,413381],[8,7493]],[[240416,422012],[-1,-5688]],[[240415,416324],[-1409,-76]],[[239006,416248],[-15,4342]],[[236624,418174],[-6,-1893]],[[236618,416281],[-1077,81]],[[235541,416362],[-1,1618]],[[260691,422697],[7,-5629]],[[260698,417068],[-1,-2446]],[[260697,414622],[-797,-40]],[[259900,414582],[0,5126]],[[261693,421341],[13,-4278]],[[261706,417063],[-1008,5]],[[256473,419631],[4,-4874]],[[256477,414757],[-917,-11]],[[255560,414746],[38,3259]],[[263884,419299],[17,-5164]],[[263901,414135],[-164,-21]],[[263737,414114],[-912,-29]],[[262825,414085],[5,1641]],[[262830,415726],[20,5677]],[[225321,422154],[-1,-6473]],[[225320,415681],[-642,-7]],[[224678,415674],[-618,2]],[[224060,415676],[-1,6479]],[[224060,415676],[-949,-1]],[[223111,415675],[-312,-4]],[[222799,415671],[-1,6489]],[[226583,422158],[-2,-6488]],[[226581,415670],[-307,0]],[[226274,415670],[-954,11]],[[216825,422158],[-7,-6476]],[[216818,415682],[-238,-3]],[[216580,415679],[-1785,10]],[[221541,422168],[6,-6503]],[[221547,415665],[-1532,-5]],[[220015,415660],[-43,2]],[[219972,415662],[-12,6486]],[[227843,422155],[-2,-6487]],[[227841,415668],[-1260,2]],[[229103,415659],[-1262,9]],[[219972,415662],[-1519,9]],[[218453,415671],[-56,3]],[[218397,415674],[1,6464]],[[218397,415674],[-1579,8]],[[222799,415671],[-1245,-6]],[[221554,415665],[-7,0]],[[241831,421268],[7,-4945]],[[241838,416323],[-1399,2]],[[240439,416325],[-24,-1]],[[284989,419873],[-135,-1158],[-365,-437],[-280,-1345]],[[284209,416933],[-135,-748],[-607,-656],[-188,-939]],[[283279,414590],[-400,4737]],[[250744,420882],[11,-4342]],[[250755,416540],[4,-2437],[-209,-17]],[[250550,414086],[-738,301],[-265,799]],[[249547,415186],[-65,2725]],[[281430,418706],[-459,-6339],[-226,-1900]],[[280745,410467],[-1192,6]],[[279553,410473],[143,1890],[-17,4331],[292,3460]],[[287729,418188],[-193,-522],[-134,-4476],[-402,-2750]],[[287000,410440],[-272,4]],[[286728,410444],[-19,0]],[[286709,410444],[-635,4132],[-197,2069],[-508,1252]],[[262830,415726],[-1089,-10]],[[261741,415716],[-35,1347]],[[242937,420420],[-5,-5731]],[[242932,414689],[-936,53]],[[241996,414742],[-158,1581]],[[283279,414590],[33,-4171]],[[283312,410419],[-28,0]],[[283284,410419],[-1756,39]],[[281528,410458],[270,1944],[385,4375]],[[279553,410473],[-342,-2]],[[279211,410471],[-1285,-23]],[[277926,410448],[-70,2461],[345,3475]],[[252342,420873],[-474,-4214]],[[251868,416659],[-1113,-119]],[[247383,417944],[-442,-2539]],[[246941,415405],[-262,-106],[-246,2264],[-607,13]],[[245826,417576],[5,1651]],[[252656,413381],[-31,-1626]],[[252625,411755],[-759,5]],[[251866,411760],[2,4899]],[[270140,418738],[-33,-4034],[-98,-688]],[[270009,414016],[-637,319]],[[269372,414335],[-891,172]],[[268481,414507],[58,3468]],[[266472,417706],[-35,-1885]],[[266437,415821],[-1449,559]],[[264988,416380],[37,2676]],[[239006,416248],[0,-1219]],[[239006,415029],[-1115,-150]],[[237891,414879],[-6,3227]],[[206434,415636],[-263,-1609]],[[206171,414027],[-698,-18]],[[205473,414009],[-2,412],[-773,-47]],[[231627,420499],[-1,-4856]],[[231626,415643],[-637,4]],[[230989,415647],[-625,3]],[[232903,420506],[433,-2548],[-67,-1276],[313,-1051]],[[233582,415631],[-87,0]],[[233495,415631],[-1249,8]],[[232246,415639],[-620,4]],[[234322,418054],[138,-4323]],[[234460,413731],[-255,-671],[-623,2571]],[[244178,419364],[30,-3037],[165,-1715]],[[244373,414612],[-1128,56]],[[243245,414668],[-313,21]],[[289158,416852],[-454,-2262],[-196,-1986]],[[288508,412604],[-541,-2144]],[[287967,410460],[-967,-20]],[[286709,410444],[-918,-3]],[[285791,410441],[-606,-7]],[[285185,410434],[-592,-13]],[[284593,410421],[124,2546],[-179,2926],[-329,1040]],[[200905,417336],[-779,-15],[-5,-3273],[-315,52],[0,-1664],[-1412,-62],[0,-2430],[-1717,-16],[1,-809],[-1384,192]],[[272795,414723],[-423,-10],[-218,-1045],[-319,59],[-5,-1092],[-317,67]],[[271513,412702],[-96,1662],[35,4100]],[[272818,418798],[-23,-4075]],[[259900,414582],[-209,-308]],[[259691,414274],[-843,-20]],[[258848,414254],[-7,4743]],[[257578,418958],[2,-4763]],[[257580,414195],[0,-1081]],[[257580,413114],[-876,36]],[[256704,413150],[-229,10],[2,1597]],[[245826,417576],[-7,-4829]],[[245819,412747],[1,-1636],[-1253,29]],[[244567,411140],[-193,1581],[-1,1891]],[[264988,416380],[-41,-2986]],[[264947,413394],[-298,808],[-748,-67]],[[274788,415938],[-600,318]],[[274188,416256],[87,2302]],[[291409,417105],[453,-5673],[-67,-3974]],[[291795,407458],[-8,-68]],[[291787,407390],[-674,1374],[-215,1839]],[[290898,410603],[-464,1137],[-81,1797],[-341,1921]],[[290012,415458],[-13,49]],[[289999,415507],[237,1028]],[[258848,414254],[-240,-36]],[[258608,414218],[-1028,-23]],[[274188,416256],[-53,-2245],[-194,-1195]],[[273941,412816],[-1155,355]],[[272786,413171],[9,1552]],[[292681,417933],[179,-418],[-159,-5398],[-46,2228],[-255,-3465],[103,-763],[-505,-2576],[-203,-83]],[[271513,412702],[-9,-1621]],[[271504,411081],[-1056,287]],[[270448,411368],[-261,927],[22,1648],[-200,73]],[[281528,410458],[-678,3]],[[280850,410461],[-105,6]],[[249547,415186],[-334,-14],[-11,-1088],[-814,-276]],[[248388,413808],[-1,3841]],[[161792,408143],[-203,-1301],[-432,-149],[-282,-3313],[-609,-861]],[[160266,402519],[-794,177]],[[159472,402696],[52,1463]],[[159524,404159],[106,2863],[-398,-48],[-7,2486],[153,591],[-290,1813]],[[255560,414746],[183,-1655],[-78,-4758]],[[255665,408333],[-417,-12]],[[255248,408321],[-3,5132]],[[277926,410448],[-234,-9]],[[277692,410439],[-800,-6]],[[276892,410433],[-426,0]],[[276466,410433],[-22,3775],[-205,1112]],[[268481,414507],[-116,-2692]],[[268365,411815],[-1169,326]],[[267196,412141],[-43,3579],[147,1917]],[[289999,415507],[-235,-1943],[-206,-350]],[[289558,413214],[-165,1988]],[[237891,414879],[8,-3256]],[[237899,411623],[-1244,79]],[[236655,411702],[-37,4579]],[[235541,416362],[8,-4089]],[[235549,412273],[-763,19]],[[234786,412292],[-326,1439]],[[181375,413882],[-2,-6764]],[[181373,407118],[-6,-16110]],[[181367,391008],[-2649,0]],[[178718,391008],[-2525,9020]],[[248388,413808],[0,-544]],[[248388,413264],[-1641,72]],[[246747,413336],[194,2069]],[[267196,412141],[-24,-2179]],[[267172,409962],[-1117,398]],[[266055,410360],[17,1046]],[[266072,411406],[162,-81],[203,4496]],[[246747,413336],[32,-697]],[[246779,412639],[-960,108]],[[200920,414122],[-228,4],[1,-10296]],[[200693,403830],[-885,2]],[[199808,403832],[-4513,9]],[[195295,403841],[-1,2439]],[[261741,415716],[-55,-4023]],[[261686,411693],[-223,-17]],[[261463,411676],[-823,-18]],[[260640,411658],[57,2964]],[[284593,410421],[-604,-5]],[[283989,410416],[-677,3]],[[289558,413214],[-576,-1173]],[[288982,412041],[-434,587]],[[288548,412628],[-38,-23]],[[288510,412605],[-2,-1]],[[251866,411760],[-181,-2587]],[[251685,409173],[-599,19]],[[251086,409192],[-316,21],[-7,2708],[-211,269]],[[250552,412190],[-2,1896]],[[206688,415637],[-41,-6]],[[206647,415631],[-213,-1605]],[[206434,414026],[-263,1]],[[241996,414742],[-30,-6459]],[[241966,408283],[-4,0]],[[241962,408283],[-1090,94]],[[240872,408377],[8,1624],[-470,45]],[[240410,410046],[29,6279]],[[266072,411406],[-502,924],[-631,517]],[[264939,412847],[8,547]],[[240410,410046],[-1140,66]],[[239270,410112],[-272,4],[8,4913]],[[236655,411702],[-1,-802]],[[236654,410900],[-1104,25]],[[235550,410925],[-1,1348]],[[274786,410447],[-874,-18]],[[273912,410429],[29,2387]],[[274787,414932],[-1,-4485]],[[276466,410433],[-1406,8]],[[275060,410441],[-274,6]],[[188080,412150],[154,-1357],[-372,-847],[15,-4365],[-94,-1439],[-489,-27],[-259,-1246]],[[187035,402869],[-502,281],[-66,4178],[-5094,-210]],[[262825,414085],[-5,-3544]],[[262820,410541],[-613,-224]],[[262207,410317],[-419,17],[-102,1359]],[[216580,415679],[-8,-8083]],[[216572,407596],[-1771,-4]],[[214801,407592],[0,110]],[[218453,415671],[-6,-8082]],[[218447,407589],[-1806,7]],[[216641,407596],[-69,0]],[[156605,411874],[135,-4012]],[[156740,407862],[1,-964],[-483,-512],[-38,-1771],[225,-3191],[-238,-1152],[-36,-1898],[296,-1405],[109,-1803],[362,-938]],[[156938,394228],[-728,41],[-164,-808],[-645,-63],[-455,-694]],[[154946,392704],[-541,2801],[109,2093],[-386,5926],[150,3428],[-35,2228],[-194,3368],[-468,3106]],[[226274,415670],[1,-6485]],[[226275,409185],[-1,-1617]],[[226274,407568],[-1548,10]],[[224726,407578],[-50,3]],[[224676,407581],[2,8093]],[[224676,407581],[-1502,8]],[[223174,407589],[-61,1]],[[223113,407590],[-2,8085]],[[227841,415668],[2,-6478]],[[227843,409190],[-1568,-5]],[[229411,415658],[-2,-8099]],[[229409,407559],[-425,-1]],[[228984,407558],[-1141,12]],[[227843,407570],[0,1620]],[[220015,415660],[-6,-8093]],[[220009,407567],[-1504,22]],[[218505,407589],[-58,0]],[[170987,398754],[-249,-368]],[[170738,398386],[-2476,18]],[[168262,398404],[-1,696],[-1014,5147],[112,6217],[-310,-1673]],[[221554,415665],[-6,-8087]],[[221548,407578],[-1487,-3]],[[220061,407575],[-52,-8]],[[223113,407590],[-1493,-14]],[[221620,407576],[-72,2]],[[230989,415647],[1,-8092]],[[230990,407555],[-955,2]],[[230035,407557],[-626,2]],[[210185,410788],[-3283,6]],[[206902,410794],[415,545],[-67,968],[390,99],[54,1354],[-365,-16],[-165,-1868],[-731,-135]],[[206433,411741],[1,2285]],[[233495,415631],[0,-6461]],[[233495,409170],[-625,0]],[[232870,409170],[-626,1]],[[232244,409171],[2,6468]],[[232244,409171],[-1,-1611],[-687,-7]],[[231556,407553],[-566,2]],[[234786,412292],[-21,-1748],[-200,350],[-276,-2271]],[[234289,408623],[-168,539],[-626,8]],[[290898,410603],[-392,-2264]],[[290506,408339],[-409,2275],[-302,2865]],[[289795,413479],[217,1979]],[[250552,412190],[-498,-1355],[-387,-1885],[5,-2169]],[[249672,406781],[-468,-23]],[[249204,406758],[-627,-24]],[[248577,406734],[-163,3649],[-26,2881]],[[239270,410112],[-32,-1637]],[[239238,408475],[-1337,-78]],[[237901,408397],[-2,3226]],[[243245,414668],[-19,-5394]],[[243226,409274],[-941,82],[-5,-1096],[-314,23]],[[256704,413150],[-11,-4877]],[[256693,408273],[-517,45]],[[256176,408318],[-511,15]],[[272786,413171],[-213,31],[-15,-3018],[100,-1891]],[[272658,408293],[-850,-351]],[[271808,407942],[-304,3139]],[[244567,411140],[169,-1392]],[[244736,409748],[-1143,47],[1,-537]],[[243594,409258],[-368,16]],[[260640,411658],[-101,-1640]],[[260539,410018],[-888,-25]],[[259651,409993],[40,4281]],[[269372,414335],[-25,-1734],[207,-77],[64,-3317]],[[269618,409207],[-318,124],[-21,-1094],[-338,108],[-318,-1009]],[[268623,407336],[-310,141],[52,4338]],[[205473,414009],[-1,-3086]],[[205472,410923],[-407,495],[-407,1457]],[[270448,411368],[146,-862],[-65,-3237]],[[270529,407269],[-311,107]],[[270218,407376],[-615,748],[15,1083]],[[259651,409993],[-1,-1094]],[[259650,408899],[-829,-88]],[[258821,408811],[-213,-27]],[[258608,408784],[0,5434]],[[202584,414233],[137,-2936],[562,-2151],[-209,-2932],[145,-2133]],[[203219,404081],[-612,-329]],[[202607,403752],[-1914,78]],[[204005,410027],[367,-1170],[-102,-1326]],[[204270,407531],[-525,-3718],[-328,267]],[[203417,404080],[-198,1]],[[258608,408784],[-389,-585],[-532,8]],[[257687,408207],[-86,542],[-21,4365]],[[264939,412847],[-109,-226],[-59,-4847]],[[264771,407774],[-700,215]],[[264071,407989],[-317,28]],[[263754,408017],[-17,6097]],[[263754,408017],[1,-413],[-936,-24]],[[262819,407580],[1,2961]],[[206433,411741],[-1,-3287]],[[206432,408454],[13,-903]],[[206445,407551],[-516,-5958],[-264,-2164]],[[205665,399429],[-5,0]],[[205660,399429],[-187,-2],[-3,8127]],[[205470,407554],[2,3369]],[[206902,410794],[-80,-2161],[-167,812],[-223,-991]],[[290506,408339],[-301,-1740]],[[290205,406599],[-214,1002]],[[289991,407601],[-385,1831],[-352,446],[-281,1743]],[[288973,411621],[822,1858]],[[255248,408321],[1,-2424]],[[255249,405897],[-1194,77]],[[254055,405974],[-17,3810]],[[254038,409784],[80,3610]],[[254038,409784],[-270,-622],[-1139,-17]],[[252629,409145],[-4,2610]],[[248577,406734],[-633,-33]],[[247944,406701],[-412,-2]],[[247532,406699],[2,2166],[-201,554],[5,1620],[-635,683]],[[246703,411722],[76,917]],[[273912,410429],[-309,-2118]],[[273603,408311],[-264,-1244]],[[273339,407067],[6,599],[-687,627]],[[257687,408207],[-84,-2441]],[[257603,405766],[-705,61]],[[256898,405827],[-209,8],[4,2438]],[[205470,407554],[-1200,-23]],[[266055,410360],[-47,-3101]],[[266008,407259],[-855,352]],[[265153,407611],[-382,163]],[[246703,411722],[-132,-1725],[183,-3269]],[[246754,406728],[-92,-2345]],[[246662,404383],[-900,73]],[[245762,404456],[-253,771],[-419,2924]],[[245090,408151],[-354,1597]],[[288982,412041],[-534,-3560],[147,-1015],[-73,-1880],[204,-1804]],[[288726,403782],[-376,-1333],[-304,88]],[[288046,402537],[-18,1506]],[[288028,404043],[-61,6417]],[[288510,412605],[38,23]],[[251086,409192],[2,-5734],[-320,52]],[[250768,403510],[-1088,3],[-8,3268]],[[235550,410925],[3,-4037]],[[235553,406888],[-1398,56]],[[234155,406944],[134,1679]],[[192584,405745],[-2935,12],[-153,2687],[-321,1686]],[[189175,410130],[0,2024]],[[189175,410130],[1,-4419],[-152,2],[6,-8099]],[[189030,397614],[-1545,14],[-447,229]],[[187038,397857],[-3,5012]],[[268623,407336],[-26,-1603]],[[268597,405733],[-717,218],[40,496],[-788,178]],[[267132,406625],[40,3337]],[[159524,404159],[-693,578],[2,-532],[-1674,-52]],[[157159,404153],[9,3692],[-428,17]],[[252629,409145],[5,-3808]],[[252634,405337],[-318,9],[-371,2452],[-255,17],[-5,1358]],[[262207,410317],[-5,-3506]],[[262202,406811],[-729,-13]],[[261473,406798],[-10,4878]],[[261473,406798],[1,-1345]],[[261474,405453],[-925,-9]],[[260549,405444],[-10,4574]],[[247532,406699],[-778,29]],[[237901,408397],[-2,-1616]],[[237899,406781],[-1252,49]],[[236647,406830],[7,4070]],[[289991,407601],[-61,-2065],[-434,1889],[-494,-3193]],[[289002,404232],[-350,2027]],[[288652,406259],[12,7]],[[288664,406266],[-51,2043]],[[288613,408309],[-8,430]],[[288605,408739],[368,2882]],[[164781,405356],[-1401,-15],[-152,1283],[-273,170],[-234,-1182],[-772,-1245]],[[161949,404367],[29,4517]],[[271808,407942],[-352,-103],[16,-1874],[-379,-569]],[[271093,405396],[-587,184],[23,1689]],[[236647,406830],[-6,-1351]],[[236641,405479],[-1087,27]],[[235554,405506],[-1,1382]],[[210182,407556],[-20,1]],[[210162,407557],[-2635,-8]],[[207527,407549],[-1082,2]],[[168262,398404],[-491,-22],[-237,-2422],[1,-1714],[313,16],[-13,-8145],[-697,1]],[[167138,386118],[-482,2257]],[[166656,388375],[-55,3726],[-157,-50],[-42,4120],[307,671],[-2,1728],[-666,42]],[[166041,398612],[-32,2039],[-419,1034]],[[165590,401685],[652,1617],[543,5284]],[[291787,407390],[22,-3591],[-686,-1401]],[[291123,402398],[-572,644]],[[290551,403042],[14,1877],[-360,1680]],[[262819,407580],[-2,-848]],[[262817,406732],[-615,79]],[[283284,410419],[-281,-1862],[-298,-5500]],[[282705,403057],[-115,-74]],[[282590,402983],[-298,3217]],[[282292,406200],[-167,1078],[132,1025],[-513,247]],[[281744,408550],[-440,1436],[-237,-1383],[-192,274]],[[280875,408877],[-25,1584]],[[280875,408877],[-278,-307],[-96,-1937]],[[280501,406633],[-526,345]],[[279975,406978],[-386,1376],[-475,-3170],[-283,767]],[[278831,405951],[380,4520]],[[278831,405951],[-568,-2831]],[[278263,403120],[-600,-2270]],[[277663,400850],[29,9589]],[[288028,404043],[-186,-68]],[[287842,403975],[-578,293],[198,3147],[-296,-282]],[[287166,407133],[-438,3311]],[[276892,410433],[-365,-5307]],[[276527,405126],[-123,314]],[[276404,405440],[-598,2940],[-675,502]],[[275131,408882],[-71,1559]],[[277663,400850],[0,-206]],[[277663,400644],[-557,1424],[-342,-761]],[[276764,401307],[-239,1285]],[[276525,402592],[2,2534]],[[287166,407133],[-74,-2015],[-419,-1267],[83,1880],[-379,-1384]],[[286377,404347],[-194,1934],[-292,888],[-100,3272]],[[275131,408882],[-285,-1378],[11,-1745]],[[274857,405759],[-140,-755]],[[274717,405004],[-207,396]],[[274510,405400],[-283,562],[-368,2261],[-256,88]],[[286377,404347],[69,-1031],[-251,-2457],[-293,640]],[[285902,401499],[0,2434],[-506,6],[2,-1756],[256,-748]],[[285654,401435],[-221,-442]],[[285433,400993],[-513,2542]],[[284920,403535],[-27,1753],[292,5146]],[[284920,403535],[-797,69]],[[284123,403604],[171,2561],[-558,2378],[253,1873]],[[284123,403604],[-2,-8]],[[284121,403596],[-805,-2420]],[[283316,401176],[-301,1526],[-310,355]],[[267132,406625],[-296,-2585]],[[266836,404040],[-607,26]],[[266229,404066],[40,3087],[-261,106]],[[192583,405610],[37,-825],[-294,-4891],[-129,-5401],[227,-2018],[156,-4770]],[[192580,387705],[-3568,0],[0,191]],[[189012,387896],[18,9718]],[[240872,408377],[-43,-5375]],[[240829,403002],[-232,408],[-163,-2184]],[[240434,401226],[-711,2931]],[[239723,404157],[-347,980],[-138,3338]],[[260549,405444],[-153,-1912]],[[260396,403532],[-746,-50]],[[259650,403482],[0,5417]],[[281744,408550],[-325,-479],[-251,-3773]],[[281168,404298],[-330,1391]],[[280838,405689],[-337,944]],[[245090,408151],[-786,-2744],[61,-2460]],[[244365,402947],[-779,381]],[[243586,403328],[8,5930]],[[254055,405974],[-144,-1904]],[[253911,404070],[-1278,-83]],[[252633,403987],[1,1350]],[[243586,403328],[-1650,162]],[[241936,403490],[26,4793]],[[270218,407376],[-23,-1681],[-315,124],[-26,-1652]],[[269854,404167],[-626,216],[-9,-491],[-644,-24]],[[268575,403868],[22,1865]],[[227843,407570],[-4,-4859]],[[227839,402711],[-1559,8]],[[226280,402719],[-6,4849]],[[232870,409170],[-17,-4355]],[[232853,404815],[-53,-3774]],[[232800,401041],[-1244,5]],[[231556,401046],[0,6507]],[[234155,406944],[373,-2132]],[[234528,404812],[-590,6]],[[233938,404818],[-1085,-3]],[[252633,403987],[0,-2970]],[[252633,401017],[-933,24]],[[251700,401041],[-932,31]],[[250768,401072],[0,2438]],[[279975,406978],[-539,-3798],[-360,-1739]],[[279076,401441],[-431,1393],[-382,286]],[[273339,407067],[-230,-1522]],[[273109,405545],[-695,-2161]],[[272414,403384],[-195,1249],[-314,-1223],[-55,-1445],[-293,114],[-131,-1044]],[[271426,401035],[10,997],[-367,905],[24,2459]],[[276404,405440],[-240,-950],[-479,-156]],[[275685,404334],[-828,1425]],[[259650,403482],[-372,-61]],[[259278,403421],[-462,-45]],[[258816,403376],[5,5435]],[[161949,404367],[-299,-280],[-426,-2782],[0,-3647]],[[161224,397658],[-376,-706]],[[160848,396952],[-363,-449],[-90,-981],[-129,6997]],[[258816,403376],[-362,-36]],[[258454,403340],[-695,149]],[[257759,403489],[-4,2280],[-152,-3]],[[282292,406200],[-394,-2354],[-185,-1907]],[[281713,401939],[-545,2359]],[[239723,404157],[-203,523],[-147,-1561],[-300,-406],[-194,-1441],[-195,1193]],[[238684,402465],[-29,-1189],[-453,360],[-301,-768]],[[237901,400868],[-2,5913]],[[241936,403490],[-12,-1887]],[[241924,401603],[-322,44]],[[241602,401647],[-773,1355]],[[256176,408318],[-109,-1613],[-6,-4868]],[[256061,401837],[-1014,8]],[[255047,401845],[202,1649],[0,2403]],[[256898,405827],[-7,-2443],[-310,8],[-2,-3248]],[[256579,400144],[-520,69]],[[256059,400213],[2,1624]],[[274510,405400],[-752,-2906]],[[273758,402494],[-136,1719],[-196,-664]],[[273426,403549],[-49,2100],[-268,-104]],[[246361,401187],[-1284,58],[-7,-1638],[-209,11]],[[244861,399618],[-414,50]],[[244447,399668],[-82,3279]],[[245762,404456],[468,-1904],[131,-1365]],[[264071,407989],[93,-711],[-59,-4822]],[[264105,402456],[-1299,240]],[[262806,402696],[11,4036]],[[265153,407611],[-83,-5848]],[[265070,401763],[-704,292]],[[264366,402055],[-261,401]],[[157159,404153],[-103,-1293],[316,-1811],[516,-796],[-42,-2231],[424,-2417]],[[158270,395605],[-153,-1117]],[[158117,394488],[-193,-2961],[-454,-702]],[[157470,390825],[-532,3403]],[[214801,407592],[7,-8099]],[[214808,399493],[2,-1604]],[[214810,397889],[-3111,-174]],[[211699,397715],[24,9830]],[[289029,404122],[-16,45]],[[289013,404167],[-11,65]],[[290551,403042],[-150,-2724]],[[290401,400318],[-533,654],[-508,1437],[-331,1713]],[[223174,407589],[-9,-8099]],[[223165,399490],[-1516,-10]],[[221649,399480],[-38,0]],[[221611,399480],[9,8096]],[[265463,401619],[-346,126]],[[265117,401745],[-47,18]],[[266229,404066],[-541,-2162],[-225,-285]],[[216641,407596],[-7,-8066]],[[216634,399530],[-241,-6]],[[216393,399524],[-1585,-31]],[[218505,407589],[-3,-8092]],[[218502,399497],[-253,0]],[[218249,399497],[-1615,33]],[[224726,407578],[-8,-6473]],[[224718,401105],[0,-1621]],[[224718,399484],[-1525,8]],[[223193,399492],[-28,-2]],[[220061,407575],[-7,-8104]],[[220054,399471],[-1552,26]],[[228984,407558],[-8,-6447]],[[228976,401111],[-4,-1634]],[[228972,399477],[-1137,-7]],[[227835,399470],[4,3241]],[[226280,402719],[-2,-1625]],[[226278,401094],[-1560,11]],[[221611,399480],[-1512,-5]],[[220099,399475],[-45,-4]],[[230035,407557],[-404,-3028],[468,-4114],[473,-185]],[[230572,400230],[-5,-2410],[-307,8]],[[230260,397828],[0,542],[-975,290],[5,2433],[-314,18]],[[207527,407549],[-7,-8122]],[[207520,399427],[-1032,5]],[[206488,399432],[-823,-3]],[[210162,407557],[-14,-13021],[-936,36]],[[209212,394572],[12,4830],[-1704,25]],[[231556,401046],[-18,-1625]],[[231538,399421],[-118,1200],[-442,280],[-406,-671]],[[211699,397715],[-29,-7916]],[[211670,389799],[2,-1622],[-925,-165]],[[210747,388012],[-1530,111]],[[209217,388123],[-5,6449]],[[205660,399429],[4,-8051]],[[205664,391378],[-1785,-68]],[[203879,391310],[176,2110],[-338,2531],[-236,-28],[-218,2136]],[[203263,398059],[20,4732],[134,1289]],[[271426,401035],[-87,-662]],[[271339,400373],[-1426,435]],[[269913,400808],[-59,3359]],[[187038,397857],[-586,-3353],[17,-2559],[-254,-958],[-384,92],[-188,-2026]],[[185643,389053],[-4276,11]],[[181367,389064],[0,1944]],[[280838,405689],[20,-2091],[-220,-1799],[43,-1667],[-291,-1467]],[[280390,398665],[-1091,2059],[-223,717]],[[235554,405506],[-2,-5520]],[[235552,399986],[-482,732]],[[235070,400718],[-348,1876],[57,1415],[-251,803]],[[262806,402696],[-686,37]],[[262120,402733],[-425,22],[-218,-746]],[[261477,402009],[-3,3444]],[[237901,400868],[-183,147],[-782,-1329]],[[236936,399686],[-300,1183]],[[236636,400869],[5,4610]],[[250768,401072],[-307,-8],[0,-3525]],[[250461,397539],[-935,2],[-151,-542]],[[249375,396999],[-164,-3]],[[249211,396996],[-7,9762]],[[249211,396996],[-1245,20]],[[247966,397016],[-6,4876]],[[247960,401892],[-16,4809]],[[164769,402898],[-1779,-16],[-231,-508],[-692,-3277],[-169,-1785],[-674,346]],[[247960,401892],[-463,-687],[1,-945],[-743,197],[-64,-1253]],[[246691,399204],[-29,5179]],[[268575,403868],[-39,-2983]],[[268536,400885],[-64,-729]],[[268472,400156],[-1582,537]],[[266890,400693],[-54,3347]],[[282590,402983],[-306,-3506]],[[282284,399477],[-571,2462]],[[195295,403841],[-25,-16136]],[[195270,387705],[-2690,0]],[[255047,401845],[-69,-1900]],[[254978,399945],[-895,326]],[[254083,400271],[-160,-18]],[[253923,400253],[-12,3817]],[[273426,403549],[-646,-1538]],[[272780,402011],[-366,1373]],[[257759,403489],[-152,-219],[7,-3170]],[[257614,400100],[-1035,44]],[[275685,404334],[85,-2807]],[[275770,401527],[-169,-2411]],[[275601,399116],[-203,-165]],[[275398,398951],[-825,1176]],[[274573,400127],[195,794],[-216,1514],[165,2569]],[[281713,401939],[-331,-4249]],[[281382,397690],[-453,-530]],[[280929,397160],[-54,1697],[-336,-1326],[-249,538]],[[280290,398069],[100,596]],[[236636,400869],[-257,453],[-437,-2067],[-407,-128]],[[235535,399127],[17,859]],[[261477,402009],[-400,-1353]],[[261077,400656],[-346,-1150],[-342,-59]],[[260389,399447],[7,4085]],[[276525,402592],[-354,-957],[-401,-108]],[[274573,400127],[-368,-1330]],[[274205,398797],[-236,255]],[[273969,399052],[-281,3046],[70,396]],[[233938,404818],[-18,-6982]],[[233920,397836],[-872,234]],[[233048,398070],[-272,169],[24,2802]],[[235070,400718],[-349,74],[-25,-3937]],[[234696,396855],[-411,-173]],[[234285,396682],[-361,-327],[-4,1481]],[[159472,402696],[-103,-2315],[298,-2192],[9,-2579]],[[159676,395610],[-1406,-5]],[[240434,401226],[-265,-1911],[27,-1099]],[[240196,398216],[-322,-2537]],[[239874,395679],[-1243,272]],[[238631,395951],[53,6514]],[[272780,402011],[-162,-1543]],[[272618,400468],[-357,-942],[-433,-2022]],[[271828,397504],[-465,1315]],[[271363,398819],[-24,1554]],[[246691,399204],[104,-2426],[318,-370]],[[247113,396408],[-268,-1767],[-327,1147]],[[246518,395788],[-157,5399]],[[269913,400808],[-106,32],[-45,-3310]],[[269762,397530],[-314,137]],[[269448,397667],[30,1913],[-311,800],[-631,505]],[[273969,399052],[-615,-1858]],[[273354,397194],[-359,392]],[[272995,397586],[-242,74],[-135,2808]],[[288046,402537],[11,-942]],[[288057,401595],[-639,-5]],[[287418,401590],[-372,-2533],[-163,577],[-139,-1617],[-138,1747],[461,4160],[775,51]],[[203263,398059],[-1079,33]],[[202184,398092],[232,2578],[24,2143],[167,939]],[[253923,400253],[-983,-53]],[[252940,400200],[-307,817]],[[266890,400693],[-89,-2651]],[[266801,398042],[-801,-648]],[[266000,397394],[-555,16],[18,4209]],[[285902,401499],[-150,-622]],[[285752,400877],[-98,558]],[[285433,400993],[-397,-2058]],[[285036,398935],[-134,518]],[[284902,399453],[-167,-26],[-614,4169]],[[199808,403832],[101,-2046]],[[199909,401786],[-296,-714]],[[199613,401072],[-737,-3251],[-270,662],[-334,-354],[-440,-2814],[-665,-1480],[1,-2999]],[[197168,390836],[-2,-3131],[-1896,0]],[[202184,398092],[-58,-1116]],[[202126,396976],[-415,809],[-150,-1174],[-541,1039],[-198,1577],[-600,-2],[-313,2561]],[[289289,396094],[-480,-882],[-190,-1251],[-469,-135]],[[288150,393826],[-70,5854]],[[288080,399680],[-23,1915]],[[288726,403782],[311,-1995],[21,-3695],[231,-1998]],[[258454,403340],[32,-5425],[144,8]],[[258630,397923],[2,-1078]],[[258632,396845],[-1016,28]],[[257616,396873],[-2,3227]],[[260389,399447],[-315,-33],[3,-1128]],[[260077,398286],[-173,-511],[-611,176]],[[259293,397951],[-15,5470]],[[244447,399668],[-615,99],[-12,-1654]],[[243820,398113],[-1328,97]],[[242492,398210],[16,3269],[-584,124]],[[284902,399453],[-317,-3074]],[[284585,396379],[-327,-588]],[[284258,395791],[-575,2284]],[[283683,398075],[-376,337],[-185,1327],[194,1437]],[[259293,397951],[-663,-28]],[[241246,396473],[-791,166],[-259,1577]],[[241602,401647],[-356,-5174]],[[279076,401441],[-306,-3713],[122,-2044],[-246,-2142]],[[278646,393542],[-601,2684]],[[278045,396226],[-22,130]],[[278023,396356],[164,2029],[-157,242],[174,1961],[-541,56]],[[291123,402398],[69,-689],[-433,-4685],[-217,-1125],[-299,-2],[197,2821],[-39,1600]],[[283683,398075],[-584,-3904]],[[283099,394171],[-333,1776]],[[282766,395947],[-855,1322]],[[281911,397269],[373,2208]],[[164778,400098],[2,-985]],[[164780,399113],[3,-842]],[[164783,398271],[-389,10],[-272,-829],[-495,64],[-430,-2100],[-547,1772],[-795,-2034],[-254,-3502]],[[161601,391652],[-950,430]],[[160651,392082],[48,3551],[149,1319]],[[264366,402055],[-167,-4639]],[[264199,397416],[-520,1367]],[[263679,398783],[-327,-384]],[[263352,398399],[-336,1361],[-212,-782]],[[262804,398978],[2,3718]],[[262120,402733],[-187,-6686]],[[261933,396047],[-9,-343]],[[261924,395704],[-187,-294]],[[261737,395410],[-674,-15]],[[261063,395395],[14,5261]],[[262804,398978],[-161,-1387]],[[262643,397591],[-710,-1544]],[[227835,399470],[-1,-3241]],[[227834,396229],[-1551,10]],[[226283,396239],[-5,4855]],[[160651,392082],[-328,7]],[[160323,392089],[-249,595],[-92,1588],[-306,1338]],[[276764,401307],[-43,-2135]],[[276721,399172],[-197,-2650],[-527,-489]],[[275997,396033],[78,1671],[-474,1412]],[[238631,395951],[-2,-269]],[[238629,395682],[-940,177],[-5,-542],[-774,158]],[[236910,395475],[26,4211]],[[278023,396356],[-421,199],[-771,1158],[-110,1459]],[[264942,392757],[-495,1045]],[[264447,393802],[-4,881]],[[264443,394683],[-244,2733]],[[265117,401745],[-175,-8988]],[[281911,397269],[-117,-636]],[[281794,396633],[-412,1057]],[[247966,397016],[-356,-13],[-7,-1413]],[[247603,395590],[-490,818]],[[286601,396690],[-270,-2438],[180,3428],[90,-990]],[[288080,399680],[-243,-645],[-318,-3541]],[[287519,395494],[-452,119]],[[287067,395613],[-237,-813],[-18,1649],[166,2200],[440,2941]],[[256059,400213],[0,-4914]],[[256059,395299],[-785,-99]],[[255274,395200],[-296,4745]],[[202126,396976],[391,-1636],[384,-265],[-241,-1239],[-125,-2430],[202,-1122],[70,-1956],[300,-2059]],[[203107,386269],[-2100,52],[-1,-5185]],[[201006,381136],[-1582,2]],[[199424,381138],[-184,2879]],[[199240,384017],[376,1],[-3,6826]],[[199613,390844],[0,10228]],[[266000,397394],[-90,-7128]],[[265910,390266],[-554,2430]],[[265356,392696],[-414,61]],[[242492,398210],[-119,-2235],[32,-2529],[-220,-3074]],[[242185,390372],[-394,741],[-91,1045]],[[241700,392158],[-289,3405]],[[241411,395563],[-165,910]],[[166041,398612],[-586,525],[-675,-24]],[[280290,398069],[-483,-2836],[-192,-318],[-231,-2313]],[[279384,392602],[-347,1620],[-177,-1641]],[[278860,392581],[-214,961]],[[285904,391677],[-438,651]],[[285466,392328],[40,2580],[-96,1946],[-374,2081]],[[285752,400877],[17,-34]],[[285769,400843],[123,37]],[[285892,400880],[302,-2195],[-274,-289],[359,-1162],[-397,-2847],[22,-2710]],[[236910,395475],[-14,-1566]],[[236896,393909],[-1362,260]],[[235534,394169],[1,3667]],[[235535,397836],[0,1291]],[[246518,395788],[-459,-1047],[-357,-130]],[[245702,394611],[-426,36],[-426,2233]],[[244850,396880],[11,2738]],[[230260,397828],[-3,-3239]],[[230257,394589],[-1081,7]],[[229176,394596],[-204,1767],[0,3114]],[[199613,390844],[-2445,-8]],[[226283,396239],[-1,-1625]],[[226282,394614],[-1549,-5]],[[224733,394609],[-15,4875]],[[233048,398070],[-2,-3479]],[[233046,394591],[-1241,3]],[[231805,394594],[-1,4258],[-266,569]],[[251700,401041],[-3,-5671],[315,56]],[[252012,395426],[-4,-1645]],[[252008,393781],[-1234,-55],[-1,-1621]],[[250773,392105],[-323,107]],[[250450,392212],[11,5327]],[[252940,400200],[-3,-4846]],[[252937,395354],[-925,72]],[[231805,394594],[2,-2436]],[[231807,392158],[-1134,-2]],[[230673,392156],[-105,1611],[-311,822]],[[269448,397667],[-55,-3546],[-336,-2]],[[269057,394119],[-207,85]],[[268850,394204],[-308,109],[-130,1734]],[[268412,396047],[60,4109]],[[270916,394754],[20,1086],[-281,1702],[-269,-1234]],[[270386,396308],[9,758],[-633,464]],[[271363,398819],[-98,-3218],[-349,-847]],[[235535,397836],[-738,278],[-101,-1259]],[[268412,396047],[-1128,225],[-164,1037]],[[267120,397309],[-319,733]],[[261063,395395],[-665,-1817]],[[260398,393578],[-311,-147]],[[260087,393431],[-10,4855]],[[272995,397586],[-322,-2153]],[[272673,395433],[-626,51]],[[272047,395484],[-219,2020]],[[255274,395200],[-23,-931]],[[255251,394269],[-1050,-45]],[[254201,394224],[-106,0]],[[254095,394224],[-12,6047]],[[254095,394224],[-871,-49]],[[253224,394175],[-287,84],[0,1095]],[[257616,396873],[-2,-1632]],[[257614,395241],[-617,-10]],[[256997,395231],[-542,-4]],[[256455,395227],[-396,72]],[[275398,398951],[-49,-2705],[-193,-1560],[-17,-2747]],[[275139,391939],[-179,223]],[[274960,392162],[-177,2249],[-237,821]],[[274546,395232],[-309,1940],[-32,1625]],[[178718,391008],[0,-11659],[-2488,-10],[-9,-22498]],[[176221,356841],[9,-15660]],[[176230,341181],[-3544,18062]],[[172686,359243],[1,19187],[-1464,8789]],[[171223,387219],[-1407,8282],[-2,1512],[924,1373]],[[244850,396880],[-2,-2773],[-421,28],[-7,-2523]],[[244420,391612],[-356,-627],[-262,515]],[[243802,391500],[-20,-7]],[[243782,391493],[38,6620]],[[263352,398399],[22,-5065]],[[263374,393334],[-124,-465]],[[263250,392869],[-377,1491]],[[262873,394360],[-210,810]],[[262663,395170],[-20,2421]],[[288150,393826],[43,-3599]],[[288193,390227],[-151,927],[-491,-152]],[[287551,391002],[-166,1427],[180,785]],[[287565,393214],[-46,2280]],[[218249,399497],[-18,-8071]],[[218231,391426],[-863,14]],[[217368,391440],[-992,-7]],[[216376,391433],[17,8091]],[[216376,391433],[-231,-10]],[[216145,391423],[-1332,-39]],[[214813,391384],[-3,6505]],[[223193,399492],[-14,-8122]],[[223179,391370],[-1512,-4]],[[221667,391366],[-36,-1]],[[221631,391365],[18,8115]],[[220099,399475],[-16,-8095]],[[220083,391380],[-260,15]],[[219823,391395],[-1228,34]],[[218595,391429],[-364,-3]],[[229176,394596],[-104,-1079],[-8,-3775]],[[229064,389742],[-1230,0]],[[227834,389742],[0,6487]],[[224733,394609],[-4,-3237]],[[224729,391372],[-1523,-3]],[[223206,391369],[-27,1]],[[221631,391365],[-1548,15]],[[285466,392328],[-31,-1556]],[[285435,390772],[-7,-2276]],[[285428,388496],[-134,1401],[-606,809],[-230,-837],[-81,1376]],[[284377,391245],[169,2219]],[[284546,393464],[298,1555],[-259,1360]],[[206488,399432],[13,-4858],[-121,-1295],[371,-47],[-10,-2736]],[[206741,390496],[-830,-42],[-247,924]],[[209217,388123],[-2473,-52]],[[206744,388071],[-3,2425]],[[264443,394683],[-524,-1280]],[[263919,393403],[-110,901],[24,2588],[-154,1891]],[[278045,396226],[-294,-515],[-232,-1658],[80,-1120],[-325,-2167]],[[277274,390766],[-416,1407],[-243,-3513],[-463,-1711],[-244,275],[-358,-1593]],[[275550,385631],[171,2537],[-267,3168]],[[275454,391336],[428,949],[115,3748]],[[166656,388375],[-699,3239]],[[165957,391614],[-901,4159]],[[165056,395773],[-269,1240],[-4,1258]],[[275454,391336],[-315,603]],[[274546,395232],[-268,-471],[-788,-2996]],[[273490,391765],[-200,1393],[150,994],[-86,3042]],[[280929,397160],[39,-1197],[-240,-2110]],[[280728,393853],[-452,-1704],[-259,-2493]],[[280017,389656],[-633,2946]],[[263919,393403],[-308,-257]],[[263611,393146],[-237,188]],[[272047,395484],[-122,-4567],[68,-1111]],[[271993,389806],[-308,-1084],[-178,1340]],[[271507,390062],[-213,1013]],[[271294,391075],[-378,3679]],[[171223,387219],[-1840,-10801],[-214,48]],[[169169,376466],[-2031,9652]],[[260087,393431],[-260,-1361]],[[259827,392070],[-266,627],[-811,-80]],[[258750,392617],[-16,4231],[-102,-3]],[[234285,396682],[0,-4534]],[[234285,392148],[-1239,4]],[[233046,392152],[0,2439]],[[165056,395773],[67,-1276],[-536,-3016]],[[164587,391481],[-681,-2964],[-867,-752],[-370,962],[-735,-860]],[[161934,387867],[-256,3852],[-77,-67]],[[243782,391493],[-600,-672],[-237,-1354]],[[242945,389467],[-301,-676],[-459,1581]],[[241411,395563],[-330,-4505],[-622,73]],[[240459,391131],[-612,170]],[[239847,391301],[27,4378]],[[284258,395791],[-146,-762]],[[284112,395029],[62,-330]],[[284174,394699],[109,-599]],[[284283,394100],[166,-999]],[[284449,393101],[22,-1315],[-319,-1468],[-235,541]],[[283917,390859],[-818,3312]],[[203879,391310],[232,-1351],[-63,-2148],[-283,-1101]],[[203765,386710],[-658,-441]],[[235534,394169],[-3,-2030]],[[235531,392139],[-1246,9]],[[267120,397309],[10,-7398]],[[267130,389911],[-689,1520],[-363,-1183]],[[266078,390248],[-168,18]],[[189012,387896],[-1284,-154]],[[187728,387742],[-2094,148]],[[185634,387890],[9,1163]],[[214813,391384],[1,-1534]],[[214814,389850],[-3144,-51]],[[273490,391765],[-131,-932]],[[273359,390833],[-145,-1041]],[[273214,389792],[-209,606],[-29,1581],[-303,3454]],[[281794,396633],[-352,-2134]],[[281442,394499],[-181,-1569],[-249,-398]],[[281012,392532],[-284,1321]],[[270386,396308],[-126,-2312],[-210,-922],[124,-3421],[-112,-240]],[[270062,389413],[-201,-166]],[[269861,389247],[-208,45],[20,1691],[-328,121],[-31,1670],[-276,128],[19,1217]],[[262663,395170],[-751,43],[12,491]],[[271294,391075],[-509,-438],[-303,-919],[26,-2485]],[[270508,387233],[-446,2180]],[[250450,392212],[-956,23]],[[249494,392235],[-119,4764]],[[268850,394204],[-252,-4689],[-210,-491]],[[268388,389024],[-201,3414],[-398,-533]],[[267789,391905],[-350,-1991],[-309,-3]],[[282766,395947],[-168,-2056],[513,-5136]],[[283111,388755],[-288,-2729]],[[282823,386026],[-280,58],[-558,5269]],[[281985,391353],[-543,3146]],[[249494,392235],[6,-1626],[-311,-14]],[[249189,390595],[-1322,101]],[[247867,390696],[42,2085]],[[247909,392781],[135,617]],[[248044,393398],[6,862],[-447,1330]],[[245702,394611],[-17,-6019]],[[245685,388592],[-299,1157],[-435,348],[-393,1319]],[[244558,391416],[-138,196]],[[284546,393464],[-223,1829]],[[284323,395293],[-65,498]],[[258750,392617],[-93,-1410]],[[258657,391207],[-1043,-28]],[[257614,391179],[0,4062]],[[248044,393398],[-494,1528],[-406,-1104],[-259,-2536],[-553,-983]],[[246332,390303],[-515,-1949],[-132,238]],[[289289,396094],[351,-2735],[270,-53],[-16,-3322],[132,-3186]],[[290026,386798],[-57,0]],[[289969,386798],[-44,0]],[[289925,386798],[-712,13]],[[289213,386811],[-982,153],[-22,1873]],[[288209,388837],[-16,1390]],[[227834,389742],[-1541,10]],[[226293,389752],[-11,4862]],[[278860,392581],[-466,-5287]],[[278394,387294],[-254,-1223]],[[278140,386071],[-449,839],[-166,1723],[-310,768]],[[277215,389401],[59,1365]],[[239847,391301],[-20,-3034]],[[239827,388267],[-622,98],[-4,-541],[-615,114]],[[238586,387938],[4,815]],[[238590,388753],[39,6929]],[[287565,393214],[-230,-1318],[167,-933],[-195,-1781],[-402,1979],[-480,1174],[215,1900],[256,-1835],[-68,1744],[239,1469]],[[283917,390859],[-162,-3064]],[[283755,387795],[-414,1424],[-230,-464]],[[273214,389792],[-307,-1569]],[[272907,388223],[-755,413],[-159,1170]],[[238590,388753],[-1541,208]],[[237049,388961],[-153,25],[0,4923]],[[284458,394060],[-175,40]],[[284174,394699],[-62,330]],[[284323,395293],[135,-1233]],[[165957,391614],[-111,-1812],[217,-2118],[-268,-3202]],[[165795,384482],[-318,1675],[-351,-1063],[-392,1451]],[[164734,386545],[-147,1347]],[[164587,387892],[0,3589]],[[262873,394360],[-58,-1367],[-578,-418]],[[262237,392575],[-496,-1308]],[[261741,391267],[-4,4143]],[[241700,392158],[-280,-5796]],[[241420,386362],[-362,56]],[[241058,386418],[-603,3090],[4,1623]],[[160323,392089],[-77,-1062],[333,-1452],[-117,-1633],[135,-802],[-46,-1999],[-216,-908]],[[160335,384233],[-267,44],[0,3908],[-681,143],[-458,-370]],[[158929,387958],[0,-4]],[[158929,387958],[-514,6077],[-298,453]],[[261741,391267],[-366,837]],[[261375,392104],[-331,-489],[66,-2303]],[[261110,389312],[-395,371]],[[260715,389683],[-3,1350],[-313,1080],[-1,1465]],[[253224,394175],[-108,-3960],[133,-658]],[[253249,389557],[-1250,128]],[[251999,389685],[9,4096]],[[256455,395227],[-410,-1812],[-93,-2129],[104,-2737]],[[256056,388549],[-615,-232]],[[255441,388317],[-297,-785]],[[255144,387532],[-486,-1420]],[[254658,386112],[-34,905],[308,954],[-17,1005]],[[254915,388976],[435,3214],[-99,2079]],[[257614,391179],[2,-2983]],[[257616,388196],[-674,-391]],[[256942,387805],[55,7426]],[[256942,387805],[-413,171]],[[256529,387976],[-473,573]],[[274960,392162],[-143,-1744],[-396,-2216]],[[274421,388202],[-376,577],[-266,-940]],[[273779,387839],[-420,2994]],[[247909,392781],[-238,-998],[-192,-2324],[175,-1160]],[[247654,388299],[-24,-168]],[[247630,388131],[-211,-2528]],[[247419,385603],[-176,2124],[-512,9],[-410,-650]],[[246321,387086],[11,3217]],[[264517,389239],[-698,-957]],[[263819,388282],[-57,221]],[[263762,388503],[-151,4643]],[[264447,393802],[70,-4563]],[[230673,392156],[-3,-4046]],[[230670,388110],[-1298,15]],[[229372,388125],[-307,-1],[-1,1618]],[[226293,389752],[2,-1622]],[[226295,388130],[-1550,-17]],[[224745,388113],[-16,3259]],[[233046,392152],[-20,-5676]],[[233026,386476],[-1232,4]],[[231794,386480],[13,5678]],[[158929,387954],[-62,-1644],[169,-2032],[-399,3],[-19,-2538],[-216,-434]],[[158402,381309],[-24,5]],[[158378,381314],[-291,-21]],[[158087,381293],[-3,45]],[[158084,381338],[140,477],[-107,2272],[-522,4538],[-125,2200]],[[281985,391353],[-669,-3258],[-157,217]],[[281159,388312],[-296,1764]],[[280863,390076],[149,2456]],[[263250,392869],[-350,-1063]],[[262900,391806],[-413,-1090]],[[262487,390716],[-250,1859]],[[158084,381338],[-237,-853],[-152,859]],[[157695,381344],[-539,908],[-435,1971],[-296,-310]],[[156425,383913],[-353,2861],[-569,2152],[-557,3778]],[[254915,388976],[-726,35]],[[254189,389011],[12,5213]],[[280017,389656],[-138,-1763],[572,-1648]],[[280451,386245],[-493,-2674]],[[279958,383571],[-240,-1325]],[[279718,382246],[-1324,5048]],[[254189,389011],[-119,3]],[[254070,389014],[-537,-21]],[[253533,388993],[-284,564]],[[269861,389247],[-95,-2476],[-512,-708]],[[269254,386063],[-253,186]],[[269001,386249],[-194,1565]],[[268807,387814],[-419,1210]],[[237049,388961],[-4,-2241]],[[237045,386720],[-1525,568]],[[235520,387288],[11,4851]],[[280863,390076],[-319,-2827]],[[280544,387249],[-93,-1004]],[[260715,389683],[-623,-25],[-151,-811]],[[259941,388847],[-106,262],[-8,2961]],[[265356,392696],[-244,-3246]],[[265112,389450],[-475,-738]],[[264637,388712],[-120,527]],[[251999,389685],[-2,-2447]],[[251997,387238],[-1241,-17]],[[250756,387221],[2,543]],[[250758,387764],[15,4341]],[[263762,388503],[-226,-927]],[[263536,387576],[-64,-376]],[[263472,387200],[-541,2720],[-31,1886]],[[259941,388847],[-406,-1355],[-4,-1306]],[[259531,386186],[-106,-8]],[[259425,386178],[-618,89]],[[258807,386267],[-150,4]],[[258657,386271],[0,4936]],[[247867,390696],[-213,-2397]],[[285904,391677],[33,-3347],[377,-2748],[-110,-1238],[-607,2727],[-162,3701]],[[266078,390248],[8,-2072]],[[266086,388176],[-605,-1279],[-198,665]],[[265283,387562],[-171,1888]],[[262487,390716],[-392,-1207]],[[262095,389509],[-262,-210]],[[261833,389299],[-458,2805]],[[268807,387814],[-424,-2456]],[[268383,385358],[-426,367],[-546,2055]],[[267411,387780],[397,3341],[-19,784]],[[250758,387764],[-457,107]],[[250301,387871],[-488,-745],[-369,60],[-248,-998]],[[249196,386188],[-7,4407]],[[277215,389401],[-117,-2971],[-334,-2300],[41,-777]],[[276805,383353],[-350,-1565],[-111,-2182]],[[276344,379606],[-848,-338],[-274,1261],[9,2076]],[[275231,382605],[22,2224],[297,802]],[[242945,389467],[-266,-2385],[-345,60],[127,-1459],[-203,-1038]],[[242258,384645],[-596,107],[14,1563],[-256,47]],[[231794,386480],[-23,-4900]],[[231771,381580],[-1110,51]],[[230661,381631],[9,6479]],[[234285,392148],[-26,-6491]],[[234259,385657],[-1233,6]],[[233026,385663],[0,813]],[[235520,387288],[0,-1654]],[[235520,385634],[-1261,23]],[[275231,382605],[-213,772]],[[275018,383377],[-482,1699],[-115,3126]],[[161934,387867],[6,-3922]],[[161940,383945],[-1040,-1303],[-210,591],[-282,-2812]],[[160408,380421],[-311,-431],[76,1800]],[[160173,381790],[162,2443]],[[261833,389299],[-405,-1730]],[[261428,387569],[-332,583]],[[261096,388152],[14,1160]],[[263472,387200],[-446,-2246]],[[263026,384954],[-363,80]],[[262663,385034],[-258,1506],[-110,2136],[-200,833]],[[267411,387780],[-197,-1449],[23,-1521],[-313,-464]],[[266924,384346],[-311,1158]],[[266613,385504],[-354,897],[-173,1775]],[[244558,391416],[4,-9113]],[[244562,382303],[-467,23],[2,-1088]],[[244097,381238],[-267,17]],[[243830,381255],[-40,2514]],[[243790,383769],[12,7731]],[[169169,376466],[1659,-8029]],[[170828,368437],[-2625,-35]],[[168203,368402],[-689,2284]],[[167514,370686],[-267,2691],[-422,166]],[[166825,373543],[187,2774],[-290,1077],[-121,2597],[-312,216],[-475,1906],[-19,2369]],[[246321,387086],[-122,-4889]],[[246199,382197],[-880,-1]],[[245319,382196],[-757,107]],[[243790,383769],[-1536,69]],[[242254,383838],[4,807]],[[164587,387892],[-720,-834],[-259,133],[-515,-1390],[-61,-1062],[-1009,-2141]],[[162023,382598],[-83,1347]],[[288209,388837],[-340,-1484],[-14,-1069]],[[287855,386284],[-365,-3226],[-285,1002],[9,-1396],[-759,4562],[218,1227],[-54,1256],[301,244],[335,-1096],[296,2145]],[[218595,391429],[0,-8114]],[[218595,383315],[-1157,7]],[[217438,383322],[-63,-1]],[[217375,383321],[-7,8119]],[[219823,391395],[8,-8114]],[[219831,383281],[-1236,34]],[[216145,391423],[-1,-8126]],[[216144,383297],[-1330,-14]],[[214814,383283],[1,118]],[[214815,383401],[-1,6449]],[[217375,383321],[-1161,-23]],[[216214,383298],[-70,-1]],[[282823,386026],[45,-780]],[[282868,385246],[-236,-135]],[[282632,385111],[-191,603],[-899,-1519]],[[281542,384195],[7,1473],[-390,2644]],[[221667,391366],[1,-6464]],[[221668,384902],[2,-1623]],[[221670,383279],[-1791,1]],[[219879,383280],[-48,1]],[[241058,386418],[-180,-1533],[-17,-2354]],[[240861,382531],[-193,-605],[-544,142],[-327,1182]],[[239797,383250],[30,5017]],[[224745,388113],[2,-4860]],[[224747,383253],[-1206,6]],[[223541,383259],[-334,2],[0,1622]],[[223207,384883],[-1,6486]],[[206744,388071],[3,-4865],[-303,-5]],[[206444,383201],[-2083,130]],[[204361,383331],[-596,3379]],[[223207,384883],[-1539,19]],[[285428,388496],[2,-844]],[[285430,387652],[-211,220],[-263,-2218]],[[284956,385654],[81,-2289],[-234,434],[-329,2869],[-459,-1569],[-187,2281],[549,3865]],[[258657,386271],[-1,-537],[-1038,13]],[[257618,385747],[-2,2449]],[[270898,384461],[-214,-1052],[-154,1960]],[[270530,385369],[-22,1864]],[[271507,390062],[-150,-656],[68,-2319],[-484,-1260],[-43,-1366]],[[181367,389064],[0,-7900]],[[181367,381164],[-7,-10123]],[[181360,371041],[4,-11253]],[[181364,359788],[2,-2927]],[[181366,356861],[-1984,13],[0,178],[-2727,-7],[-434,-204]],[[199240,384017],[-2,565],[-1384,-11],[128,-1422],[415,-674],[-79,-1241]],[[198318,381234],[-2999,12]],[[195319,381246],[-49,6459]],[[273779,387839],[177,-533],[-261,-1185],[-892,-2812]],[[272803,383309],[-146,3878],[250,1036]],[[249196,386188],[3,-3707]],[[249199,382481],[-545,27]],[[248654,382508],[-40,1621],[-342,19],[-642,3983]],[[281542,384195],[-437,-1435],[-99,693]],[[281006,383453],[-396,1782],[-66,2014]],[[272803,383309],[-1,-6]],[[272802,383303],[-272,-1508],[-143,-3971]],[[272387,377824],[-212,340]],[[272175,378164],[-165,2328],[-356,2011],[-528,-202]],[[271126,382301],[46,1976],[-274,184]],[[214815,383401],[-1944,-34]],[[212871,383367],[-1832,-28]],[[211039,383339],[-284,-8]],[[210755,383331],[-8,4681]],[[227834,389742],[0,-8111]],[[227834,381631],[-920,3]],[[226914,381634],[-613,-2]],[[226301,381632],[-6,6498]],[[229372,388125],[-59,-8133]],[[229313,379992],[-869,39]],[[228444,380031],[-2,1618],[-608,-18]],[[265283,387562],[-139,-989]],[[265144,386573],[-341,379]],[[264803,386952],[-166,1760]],[[253533,388993],[-9,-5827]],[[253524,383166],[-612,-14]],[[252912,383152],[-924,24]],[[251988,383176],[9,4062]],[[261096,388152],[-573,-2670]],[[260523,385482],[-285,-2089],[-141,377]],[[260097,383770],[-46,2157],[-520,259]],[[270530,385369],[-363,-645],[-243,-2058]],[[269924,382666],[-670,3397]],[[262663,385034],[-353,-388]],[[262310,384646],[-796,417]],[[261514,385063],[-86,2506]],[[278140,386071],[-341,-3644],[-207,-671]],[[277592,381756],[-787,1597]],[[283755,387795],[-68,-3119]],[[283687,384676],[-6,-1714]],[[283681,382962],[-121,26]],[[283560,382988],[-214,707]],[[283346,383695],[-232,459]],[[283114,384154],[-246,1092]],[[264803,386952],[-255,-1635]],[[264548,385317],[-418,-1608],[-274,-40]],[[263856,383669],[-44,1680],[-276,2227]],[[185634,387890],[509,-3310],[-59,-2002],[-242,-1389]],[[185842,381189],[-97,-48]],[[185745,381141],[-4378,23]],[[254070,389014],[36,-2007],[-137,-3775]],[[253969,383232],[-445,-66]],[[254658,386112],[-463,-2723],[-218,-213]],[[253977,383176],[-8,56]],[[238586,387938],[-30,-5629]],[[238556,382309],[-1467,73]],[[237089,382382],[-71,8],[27,4330]],[[289213,386811],[95,-1816],[-160,-1192],[-692,-216]],[[288456,383587],[-506,-230]],[[287950,383357],[-283,-697],[-32,1164],[220,2460]],[[275018,383377],[-768,-3505],[-473,452]],[[273777,380324],[-279,2237],[-256,-553],[-440,1295]],[[256529,387976],[-3,-5248]],[[256526,382728],[-678,251]],[[255848,382979],[2,2497],[-255,-92],[-154,2933]],[[158378,381314],[146,-1464],[-437,1443]],[[160173,381790],[-408,-2224],[-354,-188],[-351,1518],[-215,-1698],[-283,502],[-160,1609]],[[239797,383250],[35,-3689]],[[239832,379561],[-1222,173]],[[238610,379734],[-54,2575]],[[255848,382979],[-1,-825],[-418,-677]],[[255429,381477],[-616,58]],[[254813,381535],[-309,1118],[-517,34]],[[253987,382687],[-10,489]],[[257618,385747],[6,-2451]],[[257624,383296],[-312,-1080]],[[257312,382216],[-630,-29]],[[256682,382187],[-156,541]],[[266613,385504],[-146,-2035],[-362,-1580]],[[266105,381889],[-246,1825],[-347,209]],[[265512,383923],[-368,2650]],[[261514,385063],[-513,-1353]],[[261001,383710],[-478,1772]],[[230661,381631],[-3,-1635],[-459,10]],[[230199,380006],[-886,-14]],[[209217,388123],[-12,-6998]],[[209205,381125],[-8,-7666]],[[209197,373459],[-808,1541]],[[208389,375000],[-823,1546],[-1023,-359],[-102,637]],[[206441,376824],[3,6377]],[[210755,383331],[-21,-1721],[-306,-20],[0,-1082],[-596,-8],[-593,529]],[[209239,381029],[-34,96]],[[226301,381632],[-1535,-18]],[[224766,381614],[-19,1639]],[[248654,382508],[-383,35],[1,-1620],[-477,-883]],[[247795,380040],[-127,736]],[[247668,380776],[-303,1892],[54,2935]],[[250756,387221],[-8,-4860]],[[250748,382361],[-1241,119]],[[249507,382480],[-308,1]],[[285430,387652],[553,-2587],[494,-4061],[-10,-1655],[-344,2010],[-710,2346],[-112,-1069],[-345,3018]],[[164734,386545],[-434,-1184],[-950,-7856],[-382,-2239]],[[162968,375266],[-758,4581]],[[162210,379847],[-187,2751]],[[187728,387742],[-228,-1434],[16,-5094]],[[187516,381214],[-1674,-25]],[[192580,387705],[27,-2493],[351,-3273],[-106,-713]],[[192852,381226],[-4142,-34],[-1194,22]],[[247668,380776],[-455,-1591]],[[247213,379185],[-466,-722],[-158,1359]],[[246589,379822],[-390,2375]],[[268978,383008],[-530,-70]],[[268448,382938],[-65,2420]],[[269001,386249],[-23,-3241]],[[268448,382938],[-363,-1281]],[[268085,381657],[-378,305],[-289,1389],[-223,-1372]],[[267195,381979],[-271,2367]],[[195319,381246],[2,-5061]],[[195321,376185],[-5,-7382]],[[195316,368803],[-5,-9039]],[[195311,359764],[-2662,-21]],[[192649,359743],[-2090,97]],[[190559,359840],[-1844,-32]],[[188715,359808],[653,1884],[604,385],[346,3275],[287,901],[-33,1956],[278,1645]],[[190850,369854],[446,2473],[227,3835],[527,220],[571,4056],[231,788]],[[263856,383669],[113,-1403]],[[263969,382266],[-621,-1698]],[[263348,380568],[-281,1465]],[[263067,382033],[-41,2921]],[[281006,383453],[-231,-1615]],[[280775,381838],[-817,1733]],[[279718,382246],[-89,-2346],[-162,-617]],[[279467,379283],[-168,-1840],[-296,-1251],[-422,177]],[[278581,376369],[-906,3632]],[[277675,380001],[127,1436],[-210,319]],[[251988,383176],[-8,-2447]],[[251980,380729],[-1183,-9]],[[250797,380720],[-49,1641]],[[237089,382382],[-24,-3302]],[[237065,379080],[-1548,436]],[[235517,379516],[3,6118]],[[172686,359243],[-1858,9194]],[[265512,383923],[-362,-1946]],[[265150,381977],[-326,1245],[-276,2095]],[[204361,383331],[336,-2212],[254,-3271],[312,-1387]],[[205263,376461],[44,-2691]],[[205307,373770],[-1620,-57]],[[203687,373713],[-1526,-10],[-2,1613],[-297,15]],[[201862,375331],[0,678],[-568,1790],[-287,-220]],[[201007,377579],[-1,3557]],[[289111,378680],[-677,-384]],[[288434,378296],[-6,32]],[[288428,378328],[-108,834],[337,902],[-201,3523]],[[289925,386798],[-186,-3801],[-305,-810],[-323,-3507]],[[166825,373543],[-110,722]],[[166715,374265],[-627,2326],[-335,-1722],[-420,-936],[-390,89],[-379,1183],[-853,-3610]],[[163711,371595],[-743,3671]],[[233026,385663],[-31,-6553]],[[232995,379110],[-1225,36]],[[231770,379146],[1,2434]],[[242254,383838],[30,-5137]],[[242284,378701],[-616,91]],[[241668,378792],[-306,22],[-345,3550],[-156,167]],[[258807,386267],[-62,-3640],[-147,-849]],[[258598,381778],[-366,-1124]],[[258232,380654],[2,1594],[-309,13],[3,1094],[-304,-59]],[[259425,386178],[1,-1624],[368,-2837]],[[259794,381717],[-134,-3193]],[[259660,378524],[-140,-131]],[[259520,378393],[-503,215],[-244,853],[-175,2317]],[[269924,382666],[-59,-1571],[261,-1815],[-318,-1930]],[[269808,377350],[-304,-1659]],[[269504,375691],[-230,1692]],[[269274,377383],[-144,2308],[-237,1273],[85,2044]],[[260097,383770],[-303,-2053]],[[284512,383385],[-92,-1945]],[[284420,381440],[-8,-27]],[[284412,381413],[-144,-224]],[[284268,381189],[-437,1843],[-150,-70]],[[283687,384676],[250,-96],[535,1272],[40,-2467]],[[267195,381979],[-57,-1464]],[[267138,380515],[-472,-1482]],[[266666,379033],[-181,318]],[[266485,379351],[-380,2538]],[[282632,385111],[-701,-4528]],[[281931,380583],[-408,650],[-297,-356]],[[281226,380877],[-451,961]],[[234259,385657],[-34,-6557]],[[234225,379100],[-1230,10]],[[235517,379516],[0,-428]],[[235517,379088],[-1292,12]],[[261001,383710],[173,-405],[-53,-2162]],[[261121,381143],[-14,-546]],[[261107,380597],[-355,-820],[-434,244],[-629,-1650]],[[259689,378371],[-29,153]],[[283114,384154],[232,-459]],[[283560,382988],[-758,-4755]],[[282802,378233],[-126,314]],[[282676,378547],[-383,1648],[-362,388]],[[271126,382301],[-263,-1063],[-13,-2367]],[[270850,378871],[-720,-941]],[[270130,377930],[-322,-580]],[[263067,382033],[-389,-1467]],[[262678,380566],[-442,233]],[[262236,380799],[74,3847]],[[265150,381977],[-286,-1440]],[[264864,380537],[-573,-892]],[[264291,379645],[-263,850],[-59,1771]],[[262236,380799],[-219,-1712]],[[262017,379087],[-896,2056]],[[223541,383259],[-1,-4853],[-304,-4]],[[223236,378402],[-914,9],[-1,1610],[-610,3]],[[221711,380024],[-41,3255]],[[199424,381138],[158,-1618],[-158,-1773]],[[199424,377747],[-472,-1110]],[[198952,376637],[-30,888],[-421,1384],[24,1647],[-207,678]],[[157695,381344],[162,-972],[-41,-3299],[179,-856],[-251,-1183],[-370,1731],[-114,-336],[-479,2414],[-374,-283],[165,2872],[317,-1457],[-464,3938]],[[266485,379351],[-223,-717],[-503,-259]],[[265759,378375],[-341,2566],[-268,1036]],[[162210,379847],[2,-6325],[-188,423],[-596,-1237],[-739,-3958]],[[160689,368750],[-239,1114],[0,5132]],[[160450,374996],[-71,4167]],[[160379,379163],[29,1258]],[[243830,381255],[-6,-1908],[-732,4],[-351,-755]],[[242741,378596],[-457,105]],[[288428,378328],[-760,-1368],[99,2154],[225,837]],[[287992,379951],[5,43]],[[287997,379994],[-216,-302],[-1,1870],[-264,-794],[434,2589]],[[281226,380877],[-275,-2361]],[[280951,378516],[-515,-3903]],[[280436,374613],[-425,-1184]],[[280011,373429],[-544,5854]],[[285766,378696],[-155,-920]],[[285611,377776],[-363,657],[-314,2280],[-163,-800]],[[284771,379913],[-351,1527]],[[284512,383385],[528,-1942],[635,-277],[253,-1943],[-162,-527]],[[241668,378792],[-8,-3016]],[[241660,375776],[-363,-1063],[-225,1683],[-655,61]],[[240417,376457],[-605,128]],[[239812,376585],[20,2976]],[[269274,377383],[-231,160],[-89,-1393]],[[268954,376150],[-910,2229],[-138,-652]],[[267906,377727],[-93,803]],[[267813,378530],[371,1947],[-99,1180]],[[277675,380001],[-469,-3994]],[[277206,376007],[-657,395],[-135,1144],[-337,-42]],[[276077,377504],[267,2102]],[[214814,383283],[8,-9753]],[[214822,373530],[1,-1754]],[[214823,371776],[-1967,-12]],[[212856,371764],[15,11603]],[[276077,377504],[-296,-1431],[-373,-3413]],[[275408,372660],[-430,-146],[-592,890]],[[274386,373404],[-399,2555]],[[273987,375959],[102,2169],[-312,2196]],[[267813,378530],[-479,65],[-196,1920]],[[206441,376824],[-330,1926],[-322,-2223],[-84,711],[-442,-777]],[[212856,371764],[-914,-4]],[[211942,371760],[-915,4]],[[211027,371764],[12,11575]],[[211027,371764],[-1822,7],[-8,1688]],[[258232,380654],[-83,-1403]],[[258149,379251],[-55,-2225],[-404,-1554]],[[257690,375472],[32,1005],[-295,274],[-178,1615]],[[257249,378366],[114,-4],[-51,3854]],[[273987,375959],[-386,-927]],[[273601,375032],[-357,1057],[-619,250],[-238,1485]],[[217438,383322],[-3,-8107],[40,-1726]],[[217475,373489],[-1218,0]],[[216257,373489],[-44,1699],[1,8110]],[[219879,383280],[1,-4863]],[[219880,378417],[-1219,28],[33,-4957]],[[218694,373488],[-1219,1]],[[216257,373489],[-1435,41]],[[253987,382687],[206,-1256],[-505,-5063]],[[253688,376368],[-787,325]],[[252901,376693],[11,6459]],[[224766,381614],[1,-6484]],[[224767,375130],[-1507,16]],[[223260,375146],[-24,3256]],[[221711,380024],[-1,-3245]],[[221710,376779],[-1831,17]],[[219879,376796],[1,1621]],[[252901,376693],[0,-15]],[[252901,376678],[-925,-16]],[[251976,376662],[4,4067]],[[284268,381189],[-150,-1311],[278,-2136]],[[284396,377742],[-309,-256],[-1,-1105]],[[284086,376381],[-456,-1904]],[[283630,374477],[-165,-177],[-222,1929],[-441,2004]],[[256682,382187],[-392,-3475]],[[256290,378712],[-307,-2568]],[[255983,376144],[-94,369]],[[255889,376513],[-415,787]],[[255474,377300],[-45,4177]],[[254813,381535],[-35,-5040]],[[254778,376495],[-630,72]],[[254148,376567],[54,-1761],[-336,-146]],[[253866,374660],[-178,1708]],[[272175,378164],[-318,-1099],[158,-2552]],[[272015,374513],[-260,-47]],[[271755,374466],[-295,397],[-169,2098],[-573,1278],[132,632]],[[249507,382480],[-7,-4915]],[[249500,377565],[-220,-2832]],[[249280,374733],[-473,1903],[-262,-565]],[[248545,376071],[-165,1642],[-585,2327]],[[238610,379734],[-22,-2902],[-171,-1635]],[[238417,375197],[-154,26]],[[238263,375223],[-509,95],[7,1074],[-716,163]],[[237045,376555],[20,2525]],[[250797,380720],[-133,-3247]],[[250664,377473],[-1164,92]],[[245319,382196],[-11,-8638]],[[245308,373558],[-149,-795]],[[245159,372763],[-438,51],[4,1622],[-612,34]],[[244113,374470],[-16,6768]],[[264291,379645],[-143,-3260]],[[264148,376385],[-273,-833]],[[263875,375552],[-122,1730],[-504,1185]],[[263249,378467],[99,2101]],[[246589,379822],[-14,-6361]],[[246575,373461],[-1267,97]],[[257249,378366],[-460,-1268]],[[256789,377098],[-280,-2533],[-526,1579]],[[259520,378393],[-23,-3367],[-403,-371]],[[259094,374655],[-945,4596]],[[263249,378467],[-138,-2638]],[[263111,375829],[-141,-279],[-100,2291]],[[262870,377841],[-192,2725]],[[265759,378375],[-26,-1497]],[[265733,376878],[-550,230]],[[265183,377108],[-319,3429]],[[228444,380031],[1,-3258]],[[228445,376773],[-1531,-4]],[[226914,376769],[0,4865]],[[231770,379146],[-5,-5695]],[[231765,373451],[0,-2430]],[[231765,371021],[-1573,61]],[[230192,371082],[7,8924]],[[226914,376769],[9,-3303],[-304,-24]],[[226619,373442],[-1831,-22]],[[224788,373420],[-21,1710]],[[255474,377300],[-169,-643],[-206,1280]],[[255099,377937],[-83,-2666],[-238,1224]],[[244113,374470],[-771,42],[-10,-3582]],[[243332,370930],[-613,69]],[[242719,370999],[22,7597]],[[190850,369854],[-4778,-85],[-898,125]],[[185174,369894],[-16,4888],[309,-23],[-28,1599],[335,1],[-29,4782]],[[285474,374121],[-189,-776]],[[285285,373345],[-143,1232],[-392,-213],[-211,1145],[-143,2233]],[[284412,381413],[24,-1012],[357,-714],[24,-1757],[347,-1481],[310,-2328]],[[198952,376637],[-342,-2418]],[[198610,374219],[-358,1511],[-614,-665],[-130,1368],[-1561,20],[-626,-268]],[[282676,378547],[-305,-5181]],[[282371,373366],[-288,723],[-132,1443],[-320,1091]],[[281631,376623],[-680,1893]],[[185174,369894],[-607,4],[0,-813]],[[184567,369085],[-377,-469],[-604,143],[0,868],[-616,2],[-1,1663],[-315,-252],[-1294,1]],[[262017,379087],[-182,-1209]],[[261835,377878],[-647,-598],[-256,976]],[[260932,378256],[175,2341]],[[201007,377579],[-383,-319],[-19,-4903],[45,-4703]],[[200650,367654],[-984,-5]],[[199666,367649],[-1,4038]],[[199665,371687],[0,2392],[-241,3668]],[[262870,377841],[-381,-297],[-271,-1165]],[[262218,376379],[-341,113]],[[261877,376492],[-42,1386]],[[251976,376662],[-1,-808]],[[251975,375854],[-1238,-25]],[[250737,375829],[-73,1644]],[[285611,377776],[352,-2379]],[[285963,375397],[-300,-592]],[[285663,374805],[-240,293],[-546,3121],[-106,1694]],[[248545,376071],[-481,-3793]],[[248064,372278],[-249,-32],[-722,3898],[380,1913],[-260,1128]],[[260932,378256],[-418,-2942],[-267,-430]],[[260247,374884],[-558,3487]],[[267906,377727],[-44,-1950]],[[267862,375777],[-303,91],[-437,-1812],[23,-1008]],[[267145,373048],[-650,2775]],[[266495,375823],[171,3210]],[[265183,377108],[-100,-1732]],[[265083,375376],[-218,283]],[[264865,375659],[-717,726]],[[160450,374996],[-1125,-1844],[-234,1488],[-260,115],[-482,1735]],[[158349,376490],[-209,1238],[349,1656],[315,-572],[373,668],[783,-708],[357,1506],[62,-1115]],[[230192,371082],[-1,-2432]],[[230191,368650],[-1749,-16]],[[228442,368634],[3,8139]],[[223260,375146],[0,-1720]],[[223260,373426],[-1521,36]],[[221739,373462],[-29,3317]],[[278540,374733],[-257,-158],[-226,-2494],[-255,-813]],[[277802,371268],[-172,-1567]],[[277630,369701],[-515,2398],[26,1888]],[[277141,373987],[65,2020]],[[278581,376369],[-41,-1636]],[[248064,372278],[-101,-552]],[[247963,371726],[-1081,38]],[[246882,371764],[-315,-16],[8,1713]],[[163711,371595],[-1656,-4355],[43,-1065],[-727,-3846]],[[161371,362329],[-154,872],[-328,-612],[-163,2474],[146,498],[-186,3197]],[[160686,368758],[3,-8]],[[239812,376585],[-307,30],[-14,-1889]],[[239491,374726],[-1074,471]],[[237045,376555],[-22,-4864]],[[237023,371691],[-1517,258]],[[235506,371949],[0,363]],[[235506,372312],[11,6776]],[[266495,375823],[-378,-675]],[[266117,375148],[-384,1730]],[[280011,373429],[-196,-1763],[-242,63],[-67,-1661]],[[279506,370068],[-122,-195]],[[279384,369873],[-552,4224],[-292,636]],[[259094,374655],[-346,-3823]],[[258748,370832],[-372,-503],[-265,1038],[-246,-1075]],[[257865,370292],[-129,1809]],[[257736,372101],[-46,3371]],[[232995,379110],[-18,-5688]],[[232977,373422],[-1212,29]],[[234225,379100],[-29,-5681]],[[234196,373419],[-1219,3]],[[235506,372312],[-1310,27]],[[234196,372339],[0,1080]],[[287953,369117],[-126,1004]],[[287827,370121],[-283,134],[320,3324],[379,1762],[191,2955]],[[289111,378680],[-116,-2440],[-517,-3407],[-55,-2436],[-470,-1280]],[[271755,374466],[-438,-677],[176,-948],[-282,-761]],[[271211,372080],[-347,405],[-184,-776],[-249,1135],[-83,2839],[-218,2247]],[[286401,372837],[-192,2274],[-246,286]],[[285766,378696],[289,-8],[647,-2450],[-301,-3401]],[[242719,370999],[-614,37]],[[242105,371036],[2,817],[-459,1220],[12,2703]],[[208389,375000],[-289,-1427],[-262,-3093],[-561,-3167],[-509,-171],[-616,-1904]],[[206152,365238],[46,2010],[-135,4024],[-310,764],[-525,-1496]],[[205228,370540],[79,3230]],[[283630,374477],[147,-1396],[514,-1038],[-44,-591]],[[284247,371452],[-292,-1688]],[[283955,369764],[-489,1286],[-105,1395],[-525,503]],[[282836,372948],[-465,418]],[[281362,373717],[-225,-1081]],[[281137,372636],[-621,1277],[-80,700]],[[281631,376623],[-269,-2906]],[[260247,374884],[160,-1473]],[[260407,373411],[-544,-3500],[-48,-1933]],[[259815,367978],[-431,176]],[[259384,368154],[-184,2177],[-263,88]],[[258937,370419],[-189,413]],[[263875,375552],[-250,-1451]],[[263625,374101],[-126,-753],[-407,1614]],[[263092,374962],[19,867]],[[268954,376150],[-162,-516],[84,-2449]],[[268876,373185],[-340,667],[-495,-733]],[[268041,373119],[-179,2658]],[[219879,376796],[30,-8168]],[[219909,368628],[-1216,-7]],[[218693,368621],[1,4867]],[[257736,372101],[-514,1415]],[[257222,373516],[-85,1915],[-348,1667]],[[273601,375032],[187,-2459],[-193,530],[-410,-2364]],[[273185,370739],[-292,-249],[-64,-1205]],[[272829,369285],[-281,1663],[-394,3474],[-139,91]],[[261877,376492],[-288,-708],[-405,-2399]],[[261184,373385],[-333,-3287]],[[260851,370098],[-286,-39],[142,1604],[-300,1748]],[[271211,372080],[-148,-2078]],[[271063,370002],[-199,-676]],[[270864,369326],[-114,472]],[[270750,369798],[-437,240],[-582,3546]],[[269731,373584],[-227,2107]],[[255889,376513],[81,-2236],[-378,-1771]],[[255592,372506],[-241,-671]],[[255351,371835],[-666,-172]],[[254685,371663],[-57,1803],[-480,3101]],[[285285,373345],[270,-2335]],[[285555,371010],[-138,-2862]],[[285417,368148],[-256,2601]],[[285161,370749],[-819,3092],[-256,2540]],[[199665,371687],[-1364,1]],[[198301,371688],[8,1040],[301,1491]],[[263092,374962],[-79,-1906]],[[263013,373056],[-780,-638]],[[262233,372418],[68,3271],[-83,690]],[[201862,375331],[-47,-8024]],[[201815,367307],[-1165,-220],[0,567]],[[269731,373584],[-340,-1357],[-300,301]],[[269091,372528],[-215,657]],[[277141,373987],[-397,706],[-568,-2859]],[[276176,371834],[-345,-947],[-213,581]],[[275618,371468],[-210,1192]],[[250737,375829],[-7,-4869]],[[250730,370960],[-827,-8],[-198,-520]],[[249705,370432],[27,2111],[-434,1360],[-18,830]],[[266117,375148],[-215,-2029]],[[265902,373119],[-39,-3]],[[265863,373116],[-510,788],[-270,1472]],[[257222,373516],[17,-1185],[-615,-2106]],[[256624,370225],[-1032,2281]],[[264865,375659],[-28,-5332]],[[264837,370327],[-303,-813]],[[264534,369514],[-414,301]],[[264120,369815],[-495,4286]],[[253866,374660],[-364,-2527],[71,-1661]],[[253573,370472],[-674,471]],[[252899,370943],[2,5735]],[[262233,372418],[-14,-886]],[[262219,371532],[-865,379],[-170,1474]],[[221739,373462],[9,-4974]],[[221748,368488],[-1535,133]],[[220213,368621],[-304,7]],[[228442,368634],[-1822,-25]],[[226620,368609],[-1,4833]],[[252899,370943],[-103,7]],[[252796,370950],[-827,-12]],[[251969,370938],[6,4916]],[[285161,370749],[-468,-117]],[[284693,370632],[-446,820]],[[240417,376457],[5,-7665]],[[240422,368792],[-608,47],[-4,-1332]],[[239810,367507],[-305,42]],[[239505,367549],[-14,7177]],[[160686,368758],[-1094,44],[-338,-404]],[[159254,368398],[-341,754],[-149,3106],[-463,2406],[48,1826]],[[282836,372948],[-67,-2692]],[[282769,370256],[-815,2165],[-30,-1006],[-320,603]],[[281604,372018],[-242,1699]],[[249705,370432],[0,-110]],[[249705,370322],[-946,616]],[[248759,370938],[-796,-36]],[[247963,370902],[0,824]],[[238263,375223],[35,-4781]],[[238298,370442],[-1280,166]],[[237018,370608],[5,1083]],[[166715,374265],[-765,-4053],[-188,-2675],[-313,4],[-790,-4320]],[[164659,363221],[-270,1013],[-380,3367],[-298,3994]],[[254685,371663],[-553,-2946]],[[254132,368717],[-234,1246],[-124,-776]],[[253774,369187],[-201,1285]],[[242105,371036],[-9,-2451]],[[242096,368585],[-1210,161]],[[240886,368746],[-464,46]],[[198301,371688],[-2633,-143],[-352,-2742]],[[268041,373119],[135,-3677],[-93,-602]],[[268083,368840],[-64,308]],[[268019,369148],[-398,2375],[-439,724]],[[267182,372247],[-37,801]],[[273842,367755],[-4,20]],[[273838,367775],[-653,2964]],[[274386,373404],[-544,-5649]],[[251969,370938],[-926,-51]],[[251043,370887],[-313,73]],[[267182,372247],[-389,564],[-373,-1145]],[[266420,371666],[-518,1453]],[[265863,373116],[-284,-991],[-227,-2237]],[[265352,369888],[-515,439]],[[286401,372837],[-31,-1676],[-479,915],[-228,2729]],[[203687,373713],[-2,-6471]],[[203685,367242],[-1781,53]],[[201904,367295],[-89,12]],[[158120,372965],[-303,-2]],[[157817,372963],[-33,1344],[287,552],[49,-1894]],[[239505,367549],[-1221,189]],[[238284,367738],[14,2704]],[[264120,369815],[-276,-992]],[[263844,368823],[-589,2769]],[[263255,371592],[-242,1464]],[[224788,373420],[0,-4871]],[[224788,368549],[-1526,-10]],[[223262,368539],[-2,4887]],[[279384,369873],[-416,-2088]],[[278968,367785],[-186,-655]],[[278782,367130],[-282,1313]],[[278500,368443],[-442,1074],[-256,1751]],[[211942,371760],[-29,-11975]],[[211913,359785],[-2568,-74]],[[209345,359711],[-3196,-18]],[[206149,359693],[3,5545]],[[277630,369701],[-234,-1501],[-213,613],[-430,-1507],[-95,-1769]],[[276658,365537],[-630,2108]],[[276028,367645],[291,2275],[-143,1914]],[[245159,372763],[-5,-2033]],[[245154,370730],[-438,88],[-5,-1634],[288,-70],[-3,-1602]],[[244996,367512],[-1212,138]],[[243784,367650],[-301,26],[0,3252],[-151,2]],[[272829,369285],[-248,-1599]],[[272581,367686],[-815,1322],[-628,339],[-75,655]],[[281137,372636],[-25,-1023],[-602,-5508]],[[280510,366105],[-361,1084]],[[280149,367189],[-643,2879]],[[167514,370686],[-806,-4323],[-52,-2727],[-160,-1067],[-478,-108],[49,-890],[-436,-1627],[-319,-2893],[-1552,-1282],[-242,1461],[-239,3370]],[[163279,360600],[183,976],[522,1046],[675,599]],[[285850,370354],[-295,656]],[[285474,374121],[422,-2948],[651,-1011],[-346,-835],[-351,1027]],[[269091,372528],[-167,-3365],[109,-686],[-352,-3385]],[[268681,365092],[-160,2649],[-438,1099]],[[205228,370540],[-718,-4116]],[[204510,366424],[-823,2],[-2,816]],[[281604,372018],[-166,-3772]],[[281438,368246],[-287,-1612]],[[281151,366634],[-415,-1159],[-226,630]],[[270750,369798],[-954,-4452]],[[269796,365346],[-678,-1792]],[[269118,363554],[-32,-122]],[[269086,363432],[-466,1404]],[[268620,364836],[61,256]],[[246882,371764],[-42,-6070]],[[246840,365694],[-2,-838],[-515,-29]],[[246323,364827],[-104,1909],[11,4271],[-1076,-277]],[[216257,373489],[3,-6477]],[[216260,367012],[-86,2]],[[216174,367014],[-1351,13]],[[214823,367027],[0,4749]],[[257865,370292],[-200,-146],[146,-3006]],[[257811,367140],[-547,-1924],[-253,-1484]],[[257011,363732],[-559,3794]],[[256452,367526],[172,2699]],[[217475,373489],[0,-6490]],[[217475,366999],[-1215,13]],[[218693,368621],[0,-1623]],[[218693,366998],[-1158,2]],[[217535,367000],[-60,-1]],[[223262,368539],[4,-1605]],[[223266,366934],[-1483,-59]],[[221783,366875],[-35,1613]],[[226620,368609],[1,-1637]],[[226621,366972],[-1512,-41]],[[225109,366931],[-321,1],[0,1617]],[[232977,373422],[0,-6492]],[[232977,366930],[-1214,49]],[[231763,366979],[2,4042]],[[234196,372339],[0,-5412]],[[234196,366927],[-1206,3]],[[232990,366930],[-13,0]],[[260851,370098],[153,-1651]],[[261004,368447],[-327,81]],[[260677,368528],[-206,-892]],[[260471,367636],[-656,342]],[[262219,371532],[-27,-1601]],[[262192,369931],[-95,-2448]],[[262097,367483],[-273,950],[-278,-893],[-215,1025],[-327,-118]],[[275618,371468],[-300,-1181],[80,-1044],[-490,-1589]],[[274908,367654],[-100,1094],[-720,-2036],[-246,1043]],[[266420,371666],[-159,-2460]],[[266261,369206],[-144,586],[-553,-948],[-155,568]],[[265409,369412],[-57,476]],[[263255,371592],[-527,-1618]],[[262728,369974],[-536,-43]],[[283955,369764],[143,-836]],[[284098,368928],[-199,-2026]],[[283899,366902],[-182,62]],[[283717,366964],[-298,1139]],[[283419,368103],[99,1639],[-146,1185]],[[283372,370927],[-246,-568]],[[283126,370359],[-198,-214]],[[282928,370145],[-159,111]],[[158120,372965],[96,-2153],[271,-374],[409,-1981]],[[158896,368457],[-209,-644],[106,-2702]],[[158793,365111],[-4,-1304],[-465,-535],[78,-1485]],[[158402,361787],[-314,1660],[11,3040],[-319,2976],[37,3500]],[[268019,369148],[-408,283],[-84,-2105]],[[267527,367326],[-252,702],[-463,-1950],[-186,747],[-277,-816]],[[266349,366009],[-88,3197]],[[282769,370256],[-560,-2687]],[[282209,367569],[-433,1404],[-338,-727]],[[256452,367526],[-546,-453]],[[255906,367073],[-158,651],[-62,2671]],[[255686,370395],[-335,1440]],[[235506,371949],[0,-5388]],[[235506,366561],[0,-480]],[[235506,366081],[-1273,28]],[[234233,366109],[-37,818]],[[276028,367645],[-525,-1521]],[[275503,366124],[-183,485],[-294,-965]],[[275026,365644],[-118,2010]],[[206149,359693],[-184,-2]],[[205965,359691],[-1385,13]],[[204580,359704],[-148,938],[143,4466],[-65,1316]],[[237018,370608],[-19,-4325]],[[236999,366283],[-1493,278]],[[255686,370395],[-1221,-4085]],[[254465,366310],[33,525]],[[254498,366835],[-359,563],[-7,1319]],[[247963,370902],[3,-5312],[-200,55]],[[247766,365645],[-926,49]],[[214823,367027],[-1,-7366]],[[214822,359661],[-2676,121]],[[212146,359782],[-233,3]],[[198301,371688],[-141,-896],[-30,-2362],[-478,-2240],[-241,-2267],[2,-1425],[-247,-2726]],[[197166,359772],[-1855,-8]],[[199666,367649],[1,-7868]],[[199667,359781],[-2501,-9]],[[163279,360600],[-1049,-5634]],[[162230,354966],[-621,1774],[-205,2320]],[[161404,359060],[-33,3269]],[[263844,368823],[-156,-2927]],[[263688,365896],[-150,509],[-457,-2191]],[[263081,364214],[-5,2272],[-355,1236],[7,2252]],[[284693,370632],[402,-618],[175,-1717]],[[285270,368297],[-384,-511],[-28,-974]],[[284858,366812],[-760,2116]],[[259384,368154],[-65,-2032],[-241,-92]],[[259078,366030],[-868,-273]],[[258210,365757],[-399,1383]],[[278500,368443],[-229,-637],[32,-1405]],[[278303,366401],[-518,-5580]],[[277785,360821],[-108,-926],[-313,672]],[[277364,360567],[-363,2794],[-342,617]],[[276659,363978],[-1,1559]],[[184567,369085],[6,-9298]],[[184573,359787],[-3209,1]],[[231763,366979],[-8,-1628]],[[231755,365351],[-1561,66]],[[230194,365417],[-3,3233]],[[246323,364827],[-123,-4110]],[[246200,360717],[-515,62],[-148,783]],[[245537,361562],[-47,1372],[-348,1595],[-44,2975],[-102,8]],[[249705,370322],[275,-3108],[-171,-1185]],[[249809,366029],[-13,-1567]],[[249796,364462],[-367,-344],[-402,-1991]],[[249027,362127],[-283,3]],[[248744,362130],[15,8808]],[[243784,367650],[-24,-6958]],[[243760,360692],[-1208,136]],[[242552,360828],[-452,64]],[[242100,360892],[-4,7693]],[[253774,369187],[-63,-598],[-543,-370],[-221,-906]],[[252947,367313],[-159,350]],[[252788,367663],[8,3287]],[[252788,367663],[-169,-665],[-98,-2358],[240,-2056],[-183,-1537]],[[252578,361047],[0,1701],[-613,3308]],[[251965,366056],[4,4882]],[[283419,368103],[-491,2042]],[[251965,366056],[-546,-28],[-64,-613]],[[251355,365415],[-321,507]],[[251034,365922],[9,4965]],[[251034,365922],[-568,97]],[[250466,366019],[-657,10]],[[285850,370354],[308,-1052]],[[286158,369302],[-18,-1124]],[[286140,368178],[152,-3489],[-321,-310],[-554,3769]],[[248744,362130],[-259,20],[1,-1356],[-425,-246]],[[248061,360548],[-311,844],[16,4253]],[[273838,367775],[29,-1536],[-368,-994]],[[273499,365245],[-679,-1091]],[[272820,364154],[-382,1913]],[[272438,366067],[143,1619]],[[168203,368402],[-33,-2228],[201,-311],[166,-3441],[607,-1527],[212,-3239],[-22,-2583]],[[169334,355073],[-1716,-82],[-1,-1560],[-890,26],[-3,-1596],[-460,19],[-298,-1620]],[[165966,350260],[-246,-1296],[-814,-375],[-3,-4045],[-992,-5108]],[[163911,339436],[-928,3750],[56,1793],[-140,1162]],[[162899,346141],[229,1144],[-1,2966],[-897,4715]],[[238284,367738],[-14,-2707]],[[238270,365031],[-1188,150]],[[237082,365181],[-83,1102]],[[265409,369412],[279,-3141]],[[265688,366271],[-442,-1825]],[[265246,364446],[-535,1287]],[[264711,365733],[-177,3781]],[[255906,367073],[-251,-1812],[146,-2556]],[[255801,362705],[-529,-954],[-438,809]],[[254834,362560],[-346,1711],[-23,2039]],[[283717,366964],[-7,-1436]],[[283710,365528],[-51,69]],[[283659,365597],[-117,-1255]],[[283542,364342],[-75,-143]],[[283467,364199],[-36,-77]],[[283431,364122],[-87,-195]],[[283344,363927],[-565,790]],[[282779,364717],[-265,34],[-366,1828],[61,990]],[[280149,367189],[-243,-2778]],[[279906,364411],[-397,-807]],[[279509,363604],[-259,695],[-282,3486]],[[287953,369117],[-346,-3597],[-150,-3563],[-151,2650],[266,5441],[255,73]],[[263081,364214],[-502,-2273]],[[262579,361941],[-398,1304]],[[262181,363245],[-37,1322],[-302,999]],[[261842,365566],[255,1917]],[[272438,366067],[-555,-2439],[-498,578]],[[271385,364206],[-316,930],[-383,3657],[178,533]],[[254498,366835],[-802,-2923],[-19,-787]],[[253677,363125],[-271,-607]],[[253406,362518],[-71,2344],[-388,2451]],[[188715,359808],[-3142,-15]],[[185573,359793],[-1000,-6]],[[266349,366009],[57,-1412]],[[266406,364597],[-349,1839],[-369,-165]],[[264711,365733],[-224,-683],[-203,-2441]],[[264284,362609],[-596,3287]],[[271385,364206],[-448,-1771]],[[270937,362435],[-326,-1378],[-364,-500]],[[270247,360557],[-283,4288],[-168,501]],[[286158,369302],[207,215],[310,-1575],[-73,-2372],[-462,2608]],[[268620,364836],[-243,-50],[-502,-1356]],[[267875,363430],[-354,1381],[6,2515]],[[282779,364717],[-405,-1350]],[[282374,363367],[-1213,1929]],[[281161,365296],[-10,1338]],[[284858,366812],[77,-164]],[[284935,366648],[-7,-2001],[-380,948],[-673,137],[24,1170]],[[240886,368746],[3,-7717]],[[240889,361029],[-605,67]],[[240284,361096],[-452,335]],[[239832,361431],[-22,6076]],[[161404,359060],[-763,407],[-257,-1537]],[[160384,357930],[-486,2766],[-658,1780],[-447,2635]],[[158896,368457],[358,-59]],[[261842,365566],[-523,-2202]],[[261319,363364],[-642,5164]],[[242100,360892],[-1211,137]],[[275026,365644],[-484,-1303]],[[274542,364341],[-690,-1814]],[[273852,362527],[-445,2380],[92,338]],[[230194,365417],[-2,-5660]],[[230192,359757],[-625,5]],[[229567,359762],[-1109,1]],[[228458,359763],[-16,8871]],[[228458,359763],[-876,-6]],[[227582,359757],[-948,0]],[[226634,359757],[-13,7215]],[[220213,368621],[49,-8799]],[[220262,359822],[-1515,-40]],[[218747,359782],[-2,7215],[-52,1]],[[221783,366875],[7,-7101]],[[221790,359774],[-1286,38]],[[220504,359812],[-242,10]],[[225109,366931],[7,-7184]],[[225116,359747],[-550,11]],[[224566,359758],[-1269,13]],[[223297,359771],[-31,7163]],[[261319,363364],[-484,-1545]],[[260835,361819],[-445,1353]],[[260390,363172],[-31,2226],[112,2238]],[[278782,367130],[-479,-729]],[[176230,341181],[676,-3535]],[[176906,337646],[-237,-293],[-2131,-6],[-3163,16]],[[171375,337363],[-1036,-168]],[[170339,337195],[74,1516],[-404,8051],[81,566],[-423,2802],[-72,2164],[-261,2779]],[[285483,364729],[247,-950]],[[285730,363779],[-49,-868]],[[285681,362911],[-94,912],[-633,479],[-19,2346]],[[285270,368297],[233,-1659]],[[285503,366638],[-153,-1180]],[[285350,365458],[133,-729]],[[260390,363172],[-160,-225]],[[260230,362947],[-453,-403],[-418,352]],[[259359,362896],[21,897],[-302,2237]],[[267875,363430],[-170,-1501],[19,-1797],[-189,-331]],[[267535,359801],[-167,368]],[[267368,360169],[-60,2882],[-338,936],[-80,1342],[-349,-222],[-82,-931]],[[266459,364176],[-53,421]],[[279509,363604],[-224,-3408]],[[279285,360196],[-519,775]],[[278766,360971],[-292,-175],[-159,1424],[-530,-1399]],[[239832,361431],[-1514,177]],[[238318,361608],[-48,3423]],[[253406,362518],[-128,-3094]],[[253278,359424],[-344,1547],[-334,-766]],[[252600,360205],[-233,1071]],[[252367,361276],[211,-229]],[[245537,361562],[-556,-176],[-16,-3767]],[[244965,357619],[-1212,97]],[[243753,357716],[7,2976]],[[201904,367295],[0,-3259],[564,-4371]],[[202468,359665],[-2632,116]],[[199836,359781],[-169,0]],[[276659,363978],[-321,-1646],[-128,663],[-334,-926]],[[275876,362069],[-139,-177]],[[275737,361892],[-37,2241],[-197,1991]],[[276171,365518],[-31,-845]],[[276140,364673],[211,-935],[217,1690],[-397,90]],[[257011,363732],[-118,-2665]],[[256893,361067],[-311,-151]],[[256582,360916],[-548,-371],[-28,565]],[[256006,361110],[-205,1595]],[[281161,365296],[-22,-3279]],[[281139,362017],[-569,-758]],[[280570,361259],[-694,2327],[30,825]],[[204580,359704],[-804,-10]],[[203776,359694],[-1308,-29]],[[258405,362945],[-625,205],[-142,-3375]],[[257638,359775],[-745,1292]],[[258210,365757],[195,-2812]],[[216174,367014],[4,-7320]],[[216178,359694],[-1317,-41]],[[214861,359653],[-39,8]],[[217535,367000],[4,-7258]],[[217539,359742],[-1361,-48]],[[218747,359782],[-870,-30]],[[217877,359752],[-338,-10]],[[232990,366930],[-4,-7162]],[[232986,359768],[-736,0]],[[232250,359768],[-495,-4]],[[231755,359764],[0,5587]],[[226634,359757],[-863,-11]],[[225771,359746],[-655,1]],[[223297,359771],[-1270,0]],[[222027,359771],[-237,3]],[[234233,366109],[3,-6336]],[[234236,359773],[-930,-5]],[[233306,359768],[-320,0]],[[254834,362560],[-147,-2742]],[[254687,359818],[-400,-772]],[[254287,359046],[-461,3988],[-149,91]],[[285503,366638],[154,-1165],[635,-2093],[-66,-1012]],[[286226,362368],[53,-609]],[[286279,361759],[-115,-211]],[[286164,361548],[-434,2231]],[[285483,364729],[-133,729]],[[237082,365181],[-19,-4504]],[[237063,360677],[-1557,162]],[[235506,360839],[0,5242]],[[275737,361892],[-473,-1709],[-546,-689]],[[274718,359494],[-101,1954]],[[274617,361448],[153,1099]],[[274770,362547],[-228,1794]],[[266459,364176],[-121,-2911],[119,-2630]],[[266457,358635],[-224,322]],[[266233,358957],[-784,1845]],[[265449,360802],[-274,2103],[71,1541]],[[264091,359038],[-613,-1731]],[[263478,357307],[-281,2109],[-435,319]],[[262762,359735],[-183,2206]],[[264284,362609],[-193,-3571]],[[235506,360839],[0,-1078]],[[235506,359761],[-1085,11]],[[234421,359772],[-185,1]],[[259359,362896],[-159,-1977]],[[259200,360919],[-468,373],[-327,1653]],[[276140,364673],[31,845]],[[252367,361276],[-1008,2722]],[[251359,363998],[-4,1417]],[[272820,364154],[-287,-1786],[161,-862],[-535,-1063],[70,-474]],[[272229,359969],[-685,-1435],[-271,477]],[[271273,359011],[-336,3424]],[[250466,366019],[-48,-4180],[274,-850]],[[250692,360989],[92,-1564]],[[250784,359425],[-347,1556],[-155,-1005]],[[250282,359976],[-177,539],[-309,3947]],[[265449,360802],[-249,-1448],[-375,-410]],[[264825,358944],[-564,-169]],[[264261,358775],[-170,263]],[[251359,363998],[-15,-29]],[[251344,363969],[-427,-1199],[-225,-1781]],[[267368,360169],[-217,-969],[-641,-1367]],[[266510,357833],[-53,802]],[[248061,360548],[-409,-2218]],[[247652,358330],[-1170,82]],[[246482,358412],[-282,2305]],[[283884,365227],[441,265],[239,-1371]],[[284564,364121],[-405,-2255]],[[284159,361866],[-678,-2215]],[[283481,359651],[-2,3311]],[[283479,362962],[63,1380]],[[283659,365597],[225,-370]],[[262181,363245],[-452,-3529],[-71,-1327]],[[261658,358389],[-614,240]],[[261044,358629],[-209,3190]],[[270247,360557],[-500,-1318]],[[269747,359239],[-387,951],[-242,3364]],[[231755,359764],[-101,-4]],[[231654,359760],[-1462,-3]],[[273852,362527],[-162,-1380]],[[273690,361147],[-561,-945],[-310,462],[-429,-1770]],[[272390,358894],[-161,1075]],[[282112,359572],[-317,633]],[[281795,360205],[-220,-114],[-436,1926]],[[282374,363367],[-290,-906],[28,-2889]],[[238318,361608],[-4,-1905]],[[238314,359703],[-2,-1358]],[[238312,358345],[-1259,166]],[[237053,358511],[10,2166]],[[160384,357930],[-174,-102]],[[160210,357828],[-281,264],[-164,-998]],[[159765,357094],[-379,2271],[-464,-404],[-520,2826]],[[283344,363927],[135,-965]],[[283481,359651],[-614,-2145]],[[282867,357506],[-109,311]],[[282758,357817],[-299,1492],[-347,263]],[[269086,363432],[-450,-1508],[10,-1311],[-407,-1312]],[[268239,359301],[-193,764],[-584,-1127],[73,863]],[[250282,359976],[-572,-2622]],[[249710,357354],[-479,-188]],[[249231,357166],[11,1267],[-274,2900],[59,794]],[[280570,361259],[-138,-3504]],[[280432,357755],[-433,-3598]],[[279999,354157],[-48,2703],[-666,3336]],[[274770,362547],[-153,-1099]],[[274718,359494],[-251,-983]],[[274467,358511],[-297,-983]],[[274170,357528],[-480,3619]],[[285497,362381],[-63,-1487],[-424,-1183]],[[285010,359711],[-289,-964]],[[284721,358747],[-34,2055],[-528,1064]],[[284564,364121],[581,-507],[352,-1233]],[[277364,360567],[-130,-3464]],[[277234,357103],[-337,-1260],[-663,787],[-116,-677]],[[276118,355953],[-70,868],[-471,580]],[[275577,357401],[360,2850],[-61,1818]],[[251671,358926],[-486,-648],[-308,443]],[[250877,358721],[-93,704]],[[251344,363969],[327,-5043]],[[252600,360205],[-4,-1497]],[[252596,358708],[-916,78]],[[251680,358786],[-9,140]],[[286164,361548],[133,-1960]],[[286297,359588],[-664,2557],[48,766]],[[269747,359239],[82,-1209],[-301,-552]],[[269528,357478],[-635,40],[-381,-1462]],[[268512,356056],[-298,1723]],[[268214,357779],[25,1522]],[[259200,360919],[-148,-2366]],[[259052,358553],[-666,-2939]],[[258386,355614],[14,578],[-508,429],[-79,979]],[[257813,357600],[-175,2175]],[[262762,359735],[-638,-2584]],[[262124,357151],[-421,-78]],[[261703,357073],[-45,1316]],[[261044,358629],[-402,-2234]],[[260642,356395],[-399,432]],[[260243,356827],[-13,6120]],[[254287,359046],[-781,-1739]],[[253506,357307],[-228,2117]],[[260243,356827],[-662,-2212]],[[259581,354615],[-529,3938]],[[256006,361110],[-211,-8003]],[[255795,353107],[-851,-64]],[[254944,353043],[-147,-18]],[[254797,353025],[95,6137],[-205,656]],[[285916,358061],[-1039,-4907]],[[284877,353154],[-34,77]],[[284843,353231],[-42,1109]],[[284801,354340],[305,3818],[-96,1553]],[[285497,362381],[59,-1873],[467,-1557],[-107,-890]],[[271273,359011],[-157,-551]],[[271116,358460],[-308,-1199],[-364,-179],[-73,-1054],[-635,-1658]],[[269736,354370],[-208,3108]],[[278766,360971],[-356,-9724]],[[278410,351247],[-345,-4]],[[278065,351243],[106,760],[-263,1161],[-285,-493],[86,-1438]],[[277709,351233],[-112,-1]],[[277597,351232],[-569,21]],[[277028,351253],[206,5850]],[[275577,357401],[-419,-1374],[-218,-1669]],[[274940,354358],[-473,4153]],[[249231,357166],[-25,-4320],[-720,33]],[[248486,352879],[-527,25]],[[247959,352904],[-9,1582],[-298,3844]],[[281795,360205],[-66,-4560]],[[281729,355645],[-596,561],[-701,1549]],[[286315,361864],[-36,-105]],[[286315,361864],[307,-509],[-325,-1767]],[[284721,358747],[-1328,-4408]],[[283393,354339],[-167,252],[88,2639],[-447,276]],[[246482,358412],[44,-2141]],[[246526,356271],[-1263,227]],[[245263,356498],[-303,38],[5,1083]],[[240284,361096],[-17,-4867]],[[240267,356229],[-1101,142]],[[239166,356371],[-106,8],[13,3262],[-759,62]],[[274170,357528],[-841,-1351]],[[273329,356177],[-156,-696],[-455,-110]],[[272718,355371],[-328,3523]],[[257813,357600],[-221,-728],[-203,-3628]],[[257389,353244],[-828,-107]],[[256561,353137],[21,7779]],[[256561,353137],[-151,-14]],[[256410,353123],[-615,-16]],[[242552,360828],[-65,-4878]],[[242487,355950],[-1819,221]],[[240668,356171],[-401,58]],[[253506,357307],[130,-2237]],[[253636,355070],[-1045,64]],[[252591,355134],[5,3574]],[[250877,358721],[-61,-2945]],[[250816,355776],[-215,-1258],[64,-1202]],[[250665,353316],[-204,-1647],[-215,1259]],[[250246,352928],[-536,4426]],[[279999,354157],[-151,-412],[-88,-2492]],[[279760,351253],[-173,-5]],[[279587,351248],[-953,1]],[[278634,351249],[-224,-2]],[[243753,357716],[-40,-7253]],[[243713,350463],[-1248,-11]],[[242465,350452],[22,5498]],[[237053,358511],[-17,-3425]],[[237036,355086],[-1531,348]],[[235505,355434],[1,4327]],[[266233,358957],[17,-976],[-599,-2205],[-221,-1818]],[[265430,353958],[-521,3091],[-84,1895]],[[282758,357817],[6,-3369],[-310,-3129]],[[282454,351319],[-369,-16]],[[282085,351303],[-408,-9]],[[281677,351294],[52,4351]],[[268214,357779],[-540,-708],[-177,-2081],[-906,-1448]],[[266591,353542],[-141,2662],[60,1629]],[[272718,355371],[-733,-1028],[-226,-1288]],[[271759,353055],[-261,2813],[-382,2592]],[[192649,359743],[0,-24860],[483,-3],[-46,-2691],[-3,-12919],[-57,-4902],[46,-1624],[-29,-12941],[-91,-9],[0,-3908]],[[192952,295886],[-303,169]],[[192649,296055],[0,7877],[-2090,0],[0,4900]],[[190559,308832],[0,51008]],[[190559,308832],[-267,69],[-549,2097],[-586,1352],[-625,-723],[-214,1094]],[[188318,312721],[8,6150],[-629,15],[-1,3313],[-1550,-154],[-3,3263],[-297,21],[4,2068],[-380,-319],[-149,1054],[-899,1045],[-616,2799],[-445,401]],[[183361,332377],[1,5120],[66,1656],[-122,2775],[363,944],[70,1118],[518,1618],[615,865],[469,2332],[-89,1927],[91,1455],[24,3834],[237,2874],[-31,898]],[[254797,353025],[-1049,763]],[[253748,353788],[-112,1282]],[[233306,359768],[-67,-1052],[1,-6427]],[[233240,352289],[-1054,-2]],[[232186,352287],[-1,6412],[65,1069]],[[231654,359760],[-1,-10708]],[[231653,349052],[-2,-4884],[-743,12]],[[230908,344180],[41,1148],[-278,232],[-357,1844],[-191,-673],[-230,2345],[-236,376],[-145,2238],[-299,-1766],[-371,677]],[[228842,350601],[273,1701],[-395,-68]],[[228720,352234],[-26,1706],[357,-36],[129,1256],[379,564],[8,4038]],[[224566,359758],[31,-9970]],[[224597,349788],[-210,-1342],[-482,1078],[-172,1064],[-325,12]],[[223408,350600],[-48,1718],[-452,3424],[-433,679]],[[222475,356421],[0,1]],[[222475,356422],[-448,3349]],[[234421,359772],[22,-6122]],[[234443,353650],[-18,-2995]],[[234425,350655],[-897,9]],[[233528,350664],[-1,1625],[-287,0]],[[232186,352287],[-8,-3237]],[[232178,349050],[-525,2]],[[227582,359757],[-1,-7542]],[[227581,352215],[-1788,-1]],[[225793,352214],[-22,7532]],[[225793,352214],[0,-2428]],[[225793,349786],[-1196,2]],[[228720,352234],[-1136,-19]],[[227584,352215],[-3,0]],[[235505,355434],[1,-1837]],[[235506,353597],[-1063,53]],[[222475,356421],[0,-4177],[-865,-45]],[[221610,352199],[-1108,8]],[[220502,352207],[2,7605]],[[203776,359694],[98,-2074],[-26,-3169],[117,-4650],[-91,-2222],[-239,-893],[559,-1233],[337,-2424],[573,-1622]],[[205104,341407],[-115,-753],[-407,115]],[[204582,340769],[2,439],[-988,-6],[48,-1326],[-533,2]],[[203111,339878],[-7,660]],[[203104,340538],[1,655],[-1776,-49],[0,4052],[-2059,59]],[[199270,345255],[16,10890],[481,2044],[69,1592]],[[199270,345255],[-7,-4085]],[[199263,341170],[-3954,38]],[[195309,341208],[2,18556]],[[195309,341208],[0,-19410]],[[195309,321798],[-1,-7083]],[[195308,314715],[-3,-14913]],[[195305,299802],[-837,-13],[-15,-900],[-398,-1421]],[[194055,297468],[-881,-3135],[-222,1553]],[[212146,359782],[0,-9302]],[[212146,350480],[-110,1],[3,-8289]],[[212039,342192],[-2,-5881]],[[212037,336311],[-929,13]],[[211108,336324],[28,6448],[-1196,-12],[1,1644],[-599,9],[0,810]],[[209342,345223],[3,14488]],[[214861,359653],[-12,-9170]],[[214849,350483],[-362,-4]],[[214487,350479],[-2341,1]],[[217877,359752],[-24,-9286]],[[217853,350466],[-365,-7]],[[217488,350459],[-1501,5]],[[215987,350464],[-1138,19]],[[220502,352207],[-2,-1740]],[[220500,350467],[-1510,-6]],[[218990,350461],[-1137,5]],[[284801,354340],[42,-1109]],[[284877,353154],[-51,-1711]],[[284826,351443],[-1,-153]],[[284825,351290],[-691,44]],[[284134,351334],[-370,-26]],[[283764,351308],[-248,1789],[-323,550],[200,692]],[[263478,357307],[2,-789],[-559,-4123]],[[262921,352395],[-20,2]],[[262901,352397],[-528,227]],[[262373,352624],[-249,4527]],[[205965,359691],[58,-1482],[-85,-3570],[-387,-996],[-22,-2199],[163,-1118],[-61,-4279]],[[205631,346047],[-184,-448],[-36,-1983],[-307,-2209]],[[209342,345223],[-1191,14]],[[208151,345237],[-1196,-14],[2,803],[-1326,21]],[[239166,356371],[-30,-5933]],[[239136,350438],[-750,14]],[[238386,350452],[29,5979],[-103,1914]],[[162899,346141],[-231,-1190],[-456,1973],[-261,-928],[-61,1185],[-572,3411],[-183,-60],[-19,1981],[-417,1414],[44,778],[-533,3123]],[[265430,353958],[-148,-1853]],[[265282,352105],[-222,46]],[[265060,352151],[-603,35]],[[264457,352186],[-224,1544],[-135,2569],[163,2476]],[[286870,358327],[-79,-1657]],[[286791,356670],[-328,768],[3,1656],[404,-767]],[[264457,352186],[-96,-2]],[[264361,352184],[-1440,211]],[[266591,353542],[-596,-1193]],[[265995,352349],[-713,-244]],[[251680,358786],[-1,-3231]],[[251679,355555],[-863,221]],[[252591,355134],[-9,-4639]],[[252582,350495],[-78,9]],[[252504,350504],[-835,20]],[[251669,350524],[10,5031]],[[261703,357073],[-226,-4256]],[[261477,352817],[-391,-141]],[[261086,352676],[-444,3719]],[[259581,354615],[12,-1749]],[[259593,352866],[-647,210]],[[258946,353076],[-576,206]],[[258370,353282],[16,2332]],[[238386,350452],[-787,-3]],[[237599,350449],[-586,0]],[[237013,350449],[23,4637]],[[287748,351418],[-69,0]],[[287679,351418],[-139,3114],[-110,-3118]],[[287430,351414],[-90,0]],[[287340,351414],[-61,2]],[[287279,351416],[-242,-2]],[[287037,351414],[1,2143],[150,496],[-378,1441],[-61,1239]],[[286749,356733],[42,-63]],[[286870,358327],[530,-179],[348,-6730]],[[274940,354358],[-364,-882],[-48,-1926]],[[274528,351550],[-629,24]],[[273899,351574],[-213,1696]],[[273686,353270],[-50,397]],[[273636,353667],[-307,2510]],[[270321,353193],[-338,-940]],[[269983,352253],[-141,1]],[[269842,352254],[-106,2116]],[[271759,353055],[-114,-499]],[[271645,352556],[-501,43]],[[271144,352599],[-890,-363]],[[270254,352236],[67,957]],[[247959,352904],[-202,-2471]],[[247757,350433],[-990,13]],[[246767,350446],[-44,3365],[-197,2460]],[[286206,357292],[36,565]],[[286206,357292],[-103,-959],[-95,-4917]],[[286008,351416],[-140,0]],[[285868,351416],[-1042,27]],[[285916,358061],[-22,-1250],[348,1046]],[[163911,339436],[276,-2210]],[[164187,337226],[-3149,125]],[[161038,337351],[-326,1746],[-77,1735],[-231,787],[-396,3161],[-503,2053],[-222,5105],[442,1739],[40,3417]],[[283764,351308],[-1310,11]],[[268512,356056],[-491,-2310],[-105,-1529]],[[267916,352217],[-1356,67]],[[266560,352284],[-565,65]],[[281677,351294],[-774,-7]],[[280903,351287],[-371,-27]],[[280532,351260],[-772,-7]],[[262373,352624],[-838,204]],[[261535,352828],[-58,-11]],[[245263,356498],[-29,-6068]],[[245234,350430],[-782,-12]],[[244452,350418],[-119,11]],[[244333,350429],[-620,34]],[[269842,352254],[-878,-31]],[[268964,352223],[-617,-6]],[[268347,352217],[-431,0]],[[258370,353282],[-422,-315]],[[257948,352967],[-559,277]],[[276118,355953],[-148,-277],[114,-4413]],[[276084,351263],[-1077,152]],[[275007,351415],[-479,135]],[[250246,352928],[-253,-2473]],[[249993,350455],[-186,-21]],[[249807,350434],[-151,5]],[[249656,350439],[-18,-3004]],[[249638,347435],[-129,1228],[-367,441],[-80,-746],[-582,45]],[[248480,348403],[6,4476]],[[286305,356800],[-51,-466]],[[286570,356468],[-316,-134]],[[286570,356468],[124,213]],[[286694,356681],[55,52]],[[287037,351414],[-532,-2]],[[286505,351412],[-497,4]],[[286305,356800],[-99,492]],[[277028,351253],[-871,10]],[[276157,351263],[-73,0]],[[181366,356861],[18,-12097],[-305,-3169],[-223,-192],[-381,2417],[-725,-8],[-350,-1053],[144,-4362],[76,-6300],[227,-3470],[73,-3058],[-216,-1125],[36,-1876]],[[179740,322568],[-2834,15078]],[[261086,352676],[-980,65]],[[260106,352741],[-513,125]],[[246767,350446],[-580,-1]],[[246187,350445],[-953,-15]],[[240668,356171],[-20,-5742]],[[240648,350429],[-227,4]],[[240421,350433],[-1234,7]],[[239187,350440],[-51,-2]],[[223408,350600],[12,-6428]],[[223420,344172],[-1187,51]],[[222233,344223],[-596,-4],[-27,7980]],[[273636,353667],[50,-397]],[[273899,351574],[-178,50]],[[273721,351624],[-1258,239]],[[272463,351863],[-903,250]],[[271560,352113],[85,443]],[[242465,350452],[-84,-5]],[[242381,350447],[-1055,0]],[[241326,350447],[-678,-18]],[[251669,350524],[-28,4]],[[251641,350528],[-22,-4]],[[251619,350524],[-191,1382],[-763,1410]],[[237013,350449],[-1507,13]],[[235506,350462],[0,3135]],[[253748,353788],[47,-3358]],[[253795,350430],[-1213,65]],[[170339,337195],[-4266,60]],[[166073,337255],[29,8917],[143,-16],[5,2474],[-284,1630]],[[254944,353043],[136,-5034]],[[255080,348009],[-640,-725],[-443,466]],[[253997,347750],[-26,131]],[[253971,347881],[-176,2549]],[[235506,350462],[154,-6283]],[[235660,344179],[-653,-10]],[[235007,344169],[-597,8]],[[234410,344177],[15,6478]],[[257414,348712],[-201,-1047],[-242,621]],[[256971,348286],[-207,-250],[-369,1607]],[[256395,349643],[15,3480]],[[257948,352967],[-373,-4227],[-161,-28]],[[258946,353076],[-66,-2837]],[[258880,350239],[-152,-2605]],[[258728,347634],[-470,-221],[-397,-1687]],[[257861,345726],[-447,2986]],[[251619,350524],[-1425,9]],[[250194,350533],[-201,-78]],[[256395,349643],[-463,-2485]],[[255932,347158],[-632,238]],[[255300,347396],[-220,613]],[[278065,351243],[-356,-10]],[[260035,350463],[-39,-1632]],[[259996,348831],[-420,265]],[[259576,349096],[-528,339],[-168,804]],[[260106,352741],[-71,-2278]],[[248480,348403],[6,-7256]],[[248486,341147],[-919,-50]],[[247567,341097],[-247,-13]],[[247320,341084],[102,953]],[[247422,342037],[419,2876]],[[247841,344913],[353,1892],[-44,1811],[-209,1819],[-184,-2]],[[262901,352397],[149,-1465]],[[263050,350932],[-592,1050],[-298,-536],[-193,-2703]],[[261967,348743],[-458,2312]],[[261509,351055],[26,1773]],[[261509,351055],[-433,-573],[-153,-1811]],[[260923,348671],[-582,2219],[-306,-427]],[[270706,350628],[-572,-2009],[-308,-73]],[[269826,348546],[-389,912],[-672,-256]],[[268765,349202],[199,3021]],[[270254,352236],[-271,17]],[[271144,352599],[-438,-1971]],[[271560,352113],[-142,-3665]],[[271418,348448],[-528,-1943]],[[270890,346505],[-41,-397]],[[270849,346108],[-351,1893],[300,1976],[-92,651]],[[264361,352184],[5,-1438],[-252,-900],[56,-3382],[-120,-1264]],[[264050,345200],[-190,-992]],[[263860,344208],[-474,1989],[-250,1871]],[[263136,348068],[117,454],[-203,2410]],[[266560,352284],[206,-1169],[31,-2258]],[[266797,348857],[-174,-720],[-607,-562]],[[266016,347575],[-183,13],[-202,1691],[-279,-297]],[[265352,348982],[-292,3169]],[[228842,350601],[-61,-3232],[298,0],[0,-1617]],[[229079,345752],[-602,-1],[-1,-1624],[-593,-2]],[[227883,344125],[-297,101]],[[227586,344226],[-2,7989]],[[268347,352217],[-502,-559],[-752,-3147]],[[267093,348511],[-296,346]],[[233528,350664],[-299,-2],[-14,-8097]],[[233215,342565],[-365,1],[-131,1627],[-399,1]],[[232320,344194],[-150,-1],[8,4857]],[[265352,348982],[-275,-2785]],[[265077,346197],[-193,-790],[-429,315],[-38,-1252],[-367,730]],[[268765,349202],[-61,-397]],[[268704,348805],[-665,-1448],[-393,-1682]],[[267646,345675],[-224,1917],[-258,-1046]],[[267164,346546],[-71,1965]],[[227586,344226],[-599,4]],[[226987,344230],[-1193,4]],[[225794,344234],[-1,5552]],[[222233,344223],[3,-2818]],[[222236,341405],[-226,-158],[-275,-2614],[-162,-422],[-441,841],[-101,2156],[-521,-2268]],[[220510,338940],[0,3261]],[[220510,342201],[-10,8266]],[[272463,351863],[277,-3899]],[[272740,347964],[-622,-2325]],[[272118,345639],[-700,2809]],[[263136,348068],[-494,-1684],[-81,-2308]],[[262561,344076],[-537,-332]],[[262024,343744],[-54,115]],[[261970,343859],[103,956],[-106,3928]],[[273721,351624],[-182,-2986]],[[273539,348638],[-167,-516],[-189,1242],[-443,-1400]],[[230908,344180],[-82,5]],[[230826,344185],[-902,-44]],[[229924,344141],[-550,-15],[0,1626],[-295,0]],[[275007,351415],[-33,-5379]],[[274974,346036],[-1,-378]],[[274973,345658],[-247,762],[-926,-859]],[[273800,345561],[12,1685],[-273,1392]],[[285868,351416],[140,-749]],[[286008,350667],[106,-2464]],[[286114,348203],[-296,-486]],[[285818,347717],[-335,-961]],[[285483,346756],[-266,1180],[-452,774]],[[284765,348710],[60,2580]],[[287331,347551],[-272,271],[-554,3590]],[[287279,351416],[112,-2597],[320,-2367],[229,-3945],[-412,3972],[-197,1072]],[[287748,351418],[261,-5943]],[[288009,345475],[-7,-9]],[[288002,345466],[-124,1107],[-199,4845]],[[276157,351263],[-22,-5309]],[[276135,345954],[-1161,82]],[[287331,347551],[294,-3319],[-296,418],[-595,3435]],[[286734,348085],[-726,2582]],[[282085,351303],[5,-704]],[[282090,350599],[-40,-2323],[-263,-3337]],[[281787,344939],[-837,1178]],[[280950,346117],[82,750],[-129,4420]],[[284010,345759],[-232,-1457]],[[283778,344302],[-264,840],[-153,2057],[-292,-389],[-26,2140],[-250,1169],[-703,480]],[[284134,351334],[123,-1350],[-247,-4225]],[[284765,348710],[428,-835],[170,-2171]],[[285363,345704],[-1353,55]],[[279587,351248],[-16,-5691]],[[279571,345557],[-415,60]],[[279156,345617],[-565,56]],[[278591,345673],[43,5576]],[[280950,346117],[-322,-1897],[-207,205]],[[280421,344425],[-40,5196],[151,1639]],[[280421,344425],[-138,-2849]],[[280283,341576],[-564,914]],[[279719,342490],[-155,442],[7,2625]],[[277597,351232],[-60,-5440]],[[277537,345792],[-1,-133]],[[277536,345659],[-1401,295]],[[278591,345673],[-288,31]],[[278303,345704],[-766,88]],[[261970,343859],[-836,1715],[-211,1218]],[[260923,346792],[0,1879]],[[260923,346792],[-350,-1703],[-444,506]],[[260129,345595],[-184,949],[51,2287]],[[283778,344302],[137,-1341],[-235,-398]],[[283680,342563],[-211,-1306]],[[283469,341257],[-343,603],[-21,992],[-459,1162]],[[282646,344014],[-526,-168],[-333,1093]],[[234410,344177],[-298,-3],[1,-1624],[-250,5]],[[233863,342555],[-648,10]],[[270849,346108],[-412,-3021]],[[270437,343087],[-395,999]],[[270042,344086],[-95,1419],[-234,366]],[[269713,345871],[113,2675]],[[286734,348085],[37,-1381],[441,-2720],[-295,-510],[-337,1726]],[[286580,345200],[-247,781],[-219,2222]],[[251641,350528],[29,-1722],[-373,-22],[-27,-3471]],[[251270,345313],[-542,-338]],[[250728,344975],[-915,150]],[[249813,345125],[341,3659],[40,1749]],[[225794,344234],[-297,0]],[[225497,344234],[-1188,0]],[[224309,344234],[-889,-62]],[[252504,350504],[-37,-6508]],[[252467,343996],[-452,-1664]],[[252015,342332],[-416,1162],[-329,1819]],[[249813,345125],[-408,-510]],[[249405,344615],[-185,1255],[449,35],[-218,1039],[187,491]],[[249807,350434],[-151,5]],[[215987,342196],[-1502,-4]],[[214485,342192],[2,8287]],[[215987,350464],[0,-8268]],[[253971,347881],[-287,-10],[29,-1851],[-361,-2614]],[[253352,343406],[3,475],[-888,115]],[[217486,342236],[0,-44]],[[217486,342192],[-1499,4]],[[217488,350459],[-2,-8223]],[[214485,342192],[-2446,0]],[[218990,350461],[-2,-8244]],[[218988,342217],[-1502,19]],[[220510,342201],[-1504,15]],[[219006,342216],[-18,1]],[[237599,350449],[-67,-2392],[210,-1186]],[[237742,346871],[-201,-1331]],[[237541,345540],[-238,-554],[-1004,172],[-161,-2143],[-448,53]],[[235690,343068],[-30,1111]],[[244333,350429],[-13,-3019],[-310,36],[-1,-1628],[-349,62]],[[243660,345880],[-1297,148]],[[242363,346028],[18,4419]],[[239187,350440],[-9,-7019]],[[239178,343421],[-500,72]],[[238678,343493],[-199,24],[-297,3289],[-440,65]],[[244452,350418],[273,-1429],[140,-3024]],[[244865,345965],[-253,-501],[-21,-6336]],[[244591,339128],[-295,860],[-689,92]],[[243607,340080],[77,1094],[-24,4706]],[[246124,346133],[-9,-2184]],[[246115,343949],[-921,327],[-329,1689]],[[246187,350445],[91,-4020],[-154,-292]],[[240421,350433],[-86,-130],[-16,-7014]],[[240319,343289],[-151,19]],[[240168,343308],[-990,113]],[[241326,350447],[-51,-4301],[214,-302],[163,-1657],[2,-1854]],[[241654,342333],[-1188,137],[-147,819]],[[242363,346028],[-109,-2389]],[[242254,343639],[-4,-1372],[-299,33],[-7,-1620],[-298,35]],[[241646,340715],[8,1618]],[[247841,344913],[-363,-14],[3,1092],[-1357,142]],[[166073,337255],[-1825,-24]],[[164248,337231],[-61,-5]],[[259576,349096],[-441,-2425]],[[259135,346671],[-407,963]],[[256971,348286],[-234,-4732],[-157,-1542]],[[256580,342012],[-358,78]],[[256222,342090],[31,3291]],[[256253,345381],[-11,1627],[-310,150]],[[269713,345871],[-270,-1826],[-440,-1091]],[[269003,342954],[-147,935],[39,5066],[-191,-150]],[[273800,345561],[-24,-3359]],[[273776,342202],[-410,-186]],[[273366,342016],[-834,-921]],[[272532,341095],[-604,2256]],[[271928,343351],[190,2288]],[[266016,347575],[-182,-3347]],[[265834,344228],[-583,406]],[[265251,344634],[-174,1563]],[[260129,345595],[-76,-1981]],[[260053,343614],[-159,-706],[-544,-143]],[[259350,342765],[-215,3906]],[[249405,344615],[100,-1065],[-238,-811],[-78,-1540]],[[249189,341199],[-703,-52]],[[232320,344194],[0,-4873],[-163,-839]],[[232157,338482],[-593,17]],[[231564,338499],[9,4068],[-746,8],[-1,1610]],[[267164,346546],[-203,141],[-387,-2271]],[[266574,344416],[-254,92],[-305,-1843]],[[266015,342665],[-181,1563]],[[257861,345726],[219,-2689]],[[258080,343037],[-288,-2464]],[[257792,340573],[-469,1261],[-395,312],[-275,-1179],[-73,1045]],[[269003,342954],[-22,-1048]],[[268981,341906],[-95,483],[-414,-1436],[-99,-1242],[-215,430]],[[268158,340141],[0,1]],[[268158,340142],[-746,4390]],[[267412,344532],[234,1143]],[[271928,343351],[-738,-110]],[[271190,343241],[-274,1942],[-26,1322]],[[286580,345200],[260,-2059],[-166,-58],[-563,1793],[417,-1963],[-516,-159]],[[286012,342754],[-231,471],[37,4492]],[[263860,344208],[277,-2133]],[[264137,342075],[-291,-1173],[-653,-1442]],[[263193,339460],[-112,1610],[-520,3006]],[[255300,347396],[-148,-2919]],[[255152,344477],[-646,1243],[-422,-29]],[[254084,345691],[-87,2059]],[[254084,345691],[77,-2250],[-252,-3270],[243,-246],[-104,-1722]],[[254048,338203],[-22,-470]],[[254026,337733],[-578,561]],[[253448,338294],[-109,14],[13,5098]],[[259350,342765],[125,-2327]],[[259475,340438],[-385,-123]],[[259090,340315],[-438,450],[-572,2272]],[[286012,342754],[225,-326],[-290,-1136],[-496,1123],[-83,1530],[115,2811]],[[267412,344532],[-191,-1784]],[[267221,342748],[-165,990],[-482,678]],[[256222,342090],[-60,-1686]],[[256162,340404],[-921,638]],[[255241,341042],[-89,3435]],[[238678,343493],[80,-2934],[-203,-1599],[13,-2199]],[[238568,336761],[-493,96]],[[238075,336857],[-599,-165]],[[237476,336692],[-145,21]],[[237331,336713],[52,6147],[158,2680]],[[262024,343744],[-409,-705],[-49,-2263]],[[261566,340776],[-126,977],[-554,938],[-379,-1246]],[[260507,341445],[-454,2169]],[[265251,344634],[-424,-2187],[97,-598],[-378,-805],[-214,-1553]],[[264332,339491],[-195,2584]],[[274973,345658],[38,-1939],[-162,-1685]],[[274849,342034],[-546,79]],[[274303,342113],[-527,89]],[[271190,343241],[205,-849],[-197,-1935]],[[271198,340457],[-257,707]],[[270941,341164],[-122,-754]],[[270819,340410],[-107,-893]],[[270712,339517],[-146,572],[-129,2998]],[[281787,344939],[-692,-7162]],[[281095,337777],[-139,1398],[-673,2401]],[[247320,341084],[-33,-576],[-1290,60]],[[245997,340568],[118,3381]],[[208151,345237],[293,-4778],[-163,-1891],[63,-1472]],[[208344,337096],[-1781,-187],[-951,1848],[-1038,6]],[[204574,338763],[8,2006]],[[276135,345954],[-22,-4592]],[[276113,341362],[-980,-698]],[[275133,340664],[-284,1370]],[[243607,340080],[-387,-1384]],[[243220,338696],[-296,1224],[-438,78],[155,1793],[-387,1848]],[[270042,344086],[-542,-1570]],[[269500,342516],[-247,-1768]],[[269253,340748],[-294,-190],[22,1348]],[[245997,340568],[-63,-1620],[-438,76]],[[245496,339024],[-464,93]],[[245032,339117],[-441,11]],[[277536,345659],[-27,-6364]],[[277509,339295],[-1406,392]],[[276103,339687],[10,1675]],[[278303,345704],[22,-6834]],[[278325,338870],[33,-603],[-850,-17]],[[277508,338250],[1,1045]],[[229924,344141],[1,-4065]],[[229925,340076],[-1447,8]],[[228478,340084],[-594,800],[-1,3241]],[[255241,341042],[-480,-1635],[-27,-1245]],[[254734,338162],[-379,-650],[-307,691]],[[285363,345704],[-90,-1720],[200,-3016],[-54,-1230]],[[285419,339738],[-161,-1089]],[[285258,338649],[0,-1]],[[285258,338649],[-207,364],[-191,-1126],[-97,1433],[-186,-785],[-144,1522],[-394,-15],[-22,1228],[-337,1293]],[[279156,345617],[-183,-6999]],[[278973,338618],[-648,252]],[[279719,342490],[139,-1156],[-370,-2639],[-207,11]],[[279281,338706],[-308,-88]],[[237331,336713],[-1478,-32]],[[235853,336681],[-163,6387]],[[287690,334442],[-349,563]],[[287341,335005],[74,4092],[488,660],[174,-762],[41,-4814],[-337,-751],[-91,1012]],[[288009,345475],[568,-6870],[-469,3339],[-106,3522]],[[252015,342332],[-40,-5057]],[[251975,337275],[-583,117]],[[251392,337392],[-539,1204],[-221,2559]],[[250632,341155],[96,3820]],[[203104,340538],[-422,-118],[-2,-2837],[423,-966]],[[203103,336617],[9,-10066]],[[203112,326551],[-2650,80]],[[200462,326631],[-119,1601],[-198,-3]],[[200145,328229],[4,12910],[-886,31]],[[211108,336324],[-14,-6367],[-716,-155]],[[210378,329802],[-244,606],[-702,7065],[-124,-377],[-964,0]],[[250632,341155],[-254,-2106],[-175,-101]],[[250203,338948],[-180,1222],[-661,-786]],[[249362,339384],[-173,1815]],[[282646,344014],[-362,-5323]],[[282284,338691],[-1014,-2547]],[[281270,336144],[-175,1633]],[[267221,342748],[-212,-3523]],[[267009,339225],[-457,517],[-553,2139]],[[265999,341881],[16,784]],[[265999,341881],[52,-1240],[-389,-1579]],[[265662,339062],[-165,-855],[-257,652],[-539,-385],[-75,-938]],[[264626,337536],[-271,1710]],[[264355,339246],[-23,245]],[[268158,340141],[-176,-2867]],[[267982,337274],[-326,-27],[-492,-1365]],[[267164,335882],[-1,-24]],[[267163,335858],[-154,3367]],[[263193,339460],[-282,-1555]],[[262911,337905],[-375,-1179]],[[262536,336726],[-941,75]],[[261595,336801],[0,25]],[[261595,336826],[-52,385]],[[261543,337211],[10,106]],[[261553,337317],[156,2178],[-143,1281]],[[226987,344230],[5,-8168]],[[226992,336062],[-1485,-16]],[[225507,336046],[-10,8188]],[[228478,340084],[-1,-4054]],[[228477,336030],[-1485,32]],[[225507,336046],[-296,-3],[0,-3229]],[[225211,332814],[-863,2]],[[224348,332816],[-25,4855]],[[224323,337671],[-14,6563]],[[224323,337671],[-2072,-4]],[[222251,337667],[-15,3738]],[[231564,338499],[-444,3],[-1,-4058]],[[231119,334444],[-1192,-1]],[[229927,334443],[-2,5633]],[[233863,342555],[-208,-1974],[145,-1820],[-98,-1074]],[[233702,337687],[-1043,-408],[-29,1219],[-323,-7]],[[232307,338491],[-150,-9]],[[235007,344169],[-28,-9734]],[[234979,334435],[-892,6]],[[234087,334441],[0,3236],[-385,10]],[[235853,336681],[58,-2247]],[[235911,334434],[-932,1]],[[270712,339517],[-422,-1622]],[[270290,337895],[-183,3520],[-607,1101]],[[283469,341257],[168,-1847],[-22,-1611]],[[283615,337799],[-112,244],[-755,-2883]],[[282748,335160],[-250,2798],[-214,733]],[[253448,338294],[-3,-541]],[[253445,337753],[-1089,148],[-102,-658]],[[252254,337243],[-279,32]],[[260507,341445],[21,-2479],[-126,-943]],[[260402,338023],[-565,156]],[[259837,338179],[-362,2259]],[[243220,338696],[34,-3025]],[[243254,335671],[-1124,134]],[[242130,335805],[-496,72],[5,1351]],[[241639,337228],[7,3487]],[[240168,343308],[-18,-7271]],[[240150,336037],[-594,79]],[[239556,336116],[-989,104],[1,541]],[[272532,341095],[-16,-3720]],[[272516,337375],[-83,-532]],[[272433,336843],[-535,176],[-700,3438]],[[241639,337228],[-595,42],[-2,-1349],[-498,75]],[[240544,335996],[-394,41]],[[259090,340315],[-150,-4665]],[[258940,335650],[-106,-1338]],[[258834,334312],[-300,532],[-141,-775],[-313,1293],[-345,-45]],[[257735,335317],[-130,449]],[[257605,335766],[203,1497],[-16,3310]],[[270290,337895],[-396,-2233]],[[269894,335662],[-202,1972],[-170,143]],[[269522,337777],[-269,2971]],[[261553,337317],[-455,475],[-475,-445]],[[260623,337347],[-221,676]],[[281095,337777],[-585,-2047],[-679,-3519]],[[279831,332211],[-571,1186]],[[279260,333397],[-227,517],[248,4792]],[[285258,338648],[-235,-2967]],[[285023,335681],[-392,-875],[-524,1390]],[[284107,336196],[-492,1603]],[[269522,337777],[-293,-274],[-813,-2185],[-219,-149]],[[268197,335169],[-215,2105]],[[275133,340664],[83,-1448],[-296,-1329],[32,-1467]],[[274952,336420],[-480,1873],[-210,137]],[[274262,338430],[41,3683]],[[219006,342216],[1,-8139]],[[219007,334077],[-1521,-4]],[[217486,334073],[0,114]],[[217486,334187],[0,8005]],[[220510,338940],[0,-4867]],[[220510,334073],[-1503,4]],[[217486,334187],[-1496,-22]],[[215990,334165],[-3,8031]],[[214485,342192],[1,-7963]],[[214486,334229],[-2449,-95]],[[212037,334134],[0,2177]],[[274262,338430],[-178,-3320],[96,-3115]],[[274180,331995],[-135,-24]],[[274045,331971],[-450,-339]],[[273595,331632],[-36,1117]],[[273559,332749],[96,1381],[-522,2938]],[[273133,337068],[300,3339],[-67,1609]],[[215990,334165],[0,-71]],[[215990,334094],[-1504,-3]],[[214486,334091],[0,138]],[[257605,335766],[-267,-59]],[[257338,335707],[-1207,2676]],[[256131,338383],[31,2021]],[[273133,337068],[-79,883],[-538,-576]],[[263585,335029],[-124,-484]],[[263461,334545],[-99,4]],[[263362,334549],[-9,1249],[-281,766]],[[263072,336564],[-161,1341]],[[264355,339246],[-770,-4217]],[[267163,335858],[4,-355]],[[267167,335503],[-680,-2475],[-459,116]],[[266028,333144],[-109,2607],[-257,3311]],[[276103,339687],[-56,-7726]],[[276047,331961],[-323,-24]],[[275724,331937],[-70,1389],[-359,2653],[-343,441]],[[222251,337667],[32,-5647]],[[222283,332020],[-589,1],[-2,-1624],[-1182,13]],[[220510,330410],[0,3663]],[[200145,328229],[-3228,29],[-2,-6474],[-1606,14]],[[204574,338763],[16,-15442]],[[204590,323321],[-1474,-28]],[[203116,323293],[-4,3258]],[[203103,336617],[208,1296],[-208,257]],[[203103,338170],[0,312]],[[203103,338482],[8,1396]],[[249362,339384],[-275,31],[115,-1382],[-335,-1404],[-366,-378],[270,-1925],[-281,-846],[135,-919]],[[248625,332561],[-364,176],[-4,-2806]],[[248257,329931],[-36,-137]],[[248221,329794],[-36,1335],[-212,-483]],[[247973,330646],[-400,66]],[[247573,330712],[0,4865]],[[247573,335577],[-6,5520]],[[272433,336843],[-475,-3705]],[[271958,333138],[-10,-83]],[[271948,333055],[-430,282]],[[271518,333337],[-367,-86]],[[271151,333251],[-126,2692],[-326,1639],[222,1422],[-102,1406]],[[251392,337392],[-291,-2345],[-136,397]],[[250965,335444],[-553,1154],[-247,1169]],[[250165,337767],[38,1181]],[[247573,335577],[-2090,162]],[[245483,335739],[13,3285]],[[286926,335501],[-145,-1821],[-233,206],[79,1506],[-351,140]],[[286276,335532],[78,4358]],[[286354,339890],[552,1171],[303,-80],[50,-5636],[-333,156]],[[256131,338383],[-53,-2345],[-292,-1220]],[[255786,334818],[-336,-820],[-354,712],[-199,-818]],[[254897,333892],[-239,1015],[76,3255]],[[286276,335532],[-676,145]],[[285600,335677],[-577,4]],[[285419,339738],[847,1102],[88,-950]],[[203103,338482],[0,-312]],[[259837,338179],[-278,-1956],[-1,-1386]],[[259558,334837],[-289,-248],[-329,1061]],[[271151,333251],[-416,-936],[-546,60]],[[270189,332375],[-338,1184],[43,2103]],[[250165,337767],[-126,96],[-281,-4503]],[[249758,333360],[-309,1168],[-404,124],[-420,-2091]],[[229927,334443],[-12,-3281]],[[229915,331162],[-1439,22]],[[228476,331184],[1,4846]],[[245032,339117],[-92,-1440],[-340,-1929],[-44,-3402],[-591,77]],[[243965,332423],[-591,68]],[[243374,332491],[12,3306],[-132,-126]],[[277508,338250],[-37,-6115]],[[277471,332135],[-595,-66]],[[276876,332069],[-829,-108]],[[264626,337536],[88,-2617],[-150,-1005]],[[264564,333914],[-285,822],[-464,206],[-191,-774]],[[263624,334168],[-163,377]],[[245483,335739],[-6,-4945]],[[245477,330794],[-2,-1651]],[[245475,329143],[-590,63],[-3,1642],[-263,-127]],[[244619,330721],[-662,64],[8,1638]],[[266028,333144],[-304,-143],[-504,-1878]],[[265220,331123],[-22,58]],[[265198,331181],[-124,1694],[-214,-289],[-296,1328]],[[279260,333397],[-157,-1140]],[[279103,332257],[-411,1984],[-405,-1720],[-166,127]],[[278121,332648],[-77,-460]],[[278044,332188],[-573,-53]],[[210378,329802],[0,-2771],[-614,433],[-745,-2270]],[[209019,325194],[-2,1368],[-3247,12],[1,-3243]],[[205771,323331],[-1181,-10]],[[282748,335160],[-108,-471]],[[282640,334689],[-343,-1479]],[[282297,333210],[-672,237]],[[281625,333447],[-169,331],[-186,2366]],[[275724,331937],[-313,-27]],[[275411,331910],[-1231,85]],[[232307,338491],[0,-2433],[148,-23],[1,-3216]],[[232456,332819],[-305,-2],[-149,-3256],[-295,1]],[[231707,329562],[-295,1],[-1,3262],[-292,-4],[0,1623]],[[234087,334441],[-14,-2089],[230,-1261]],[[234303,331091],[-538,-3686],[-283,598]],[[233482,328003],[-1,4815],[-1025,1]],[[260623,337347],[23,-3296],[103,-1581]],[[260749,332470],[-138,-25]],[[260611,332445],[-753,-140]],[[259858,332305],[-300,2532]],[[257338,335707],[-156,-2905],[-342,-2492]],[[256840,330310],[-446,716],[-239,-414]],[[256155,330612],[-245,215]],[[255910,330827],[24,1814],[-148,2177]],[[254026,337733],[-164,-1607],[-17,-2092],[167,-1717],[-89,-1888]],[[253923,330429],[-48,-579]],[[253875,329850],[-477,-215],[-128,791]],[[253270,330426],[-3,1967],[147,1493],[31,3867]],[[254897,333892],[-166,-2346]],[[254731,331546],[-712,-436],[-96,-681]],[[284107,336196],[0,-1815],[235,-1426]],[[284342,332955],[-25,-150]],[[284317,332805],[-216,-594],[-40,-1864]],[[284061,330347],[-198,-1249],[-361,-217]],[[283502,328881],[-237,1612]],[[283265,330493],[-76,1699],[-549,2497]],[[273559,332749],[-1601,389]],[[270189,332375],[-269,-1121]],[[269920,331254],[-1335,-829]],[[268585,330425],[-153,883],[95,1791],[-330,2070]],[[263072,336564],[-195,-3681],[-172,-21],[-175,-2137],[-273,-558]],[[262257,330167],[-331,919]],[[261926,331086],[76,2096],[534,3544]],[[261543,337211],[52,-385]],[[261595,336801],[-473,-3690]],[[261122,333111],[-373,-641]],[[253270,330426],[-331,-79]],[[252939,330347],[-8,1375],[-395,358],[-301,1417]],[[252235,333497],[19,3746]],[[250965,335444],[-26,-4865]],[[250939,330579],[-293,-638]],[[250646,329941],[-810,109]],[[249836,330050],[-78,3310]],[[281625,333447],[-251,-3124],[4,-1252],[-421,-1196]],[[280957,327875],[-522,-413],[-138,954]],[[280297,328416],[-332,3370],[-134,425]],[[224348,332816],[-1,-1627]],[[224347,331189],[-2064,20]],[[222283,331209],[0,811]],[[179740,322568],[3,-2452],[444,-2894],[103,-2182],[267,-2704],[564,-2760]],[[181121,309576],[-326,-2417],[-494,-1592]],[[180301,305567],[-2460,-124],[1,-805],[-4499,-63],[2,-553],[-829,-9],[-928,528],[-315,-3026]],[[171273,301515],[-312,1400]],[[170961,302915],[145,1407],[238,4958],[-64,9969]],[[171280,319249],[98,4],[-3,18110]],[[164248,337231],[-3,-3233],[301,-23],[0,-1605],[244,-1640],[325,-33],[5,-1631],[192,12],[4,-1614],[397,-45],[3,-1614],[314,77],[-22,-1690],[246,-211],[0,-3299]],[[166254,320682],[-755,1375],[-936,2625],[-284,-1411],[-421,-724],[101,-1704],[-444,1648],[-536,-409]],[[162979,322082],[-5,3222],[-296,222],[-393,1771],[197,1535],[-165,1791],[-326,509],[-446,3198],[-339,588],[-168,2433]],[[171280,319249],[-3410,-90],[36,-551]],[[167906,318608],[-267,446],[-750,39],[-88,1202],[-462,376]],[[166339,320671],[-85,11]],[[252235,333497],[-177,-1852],[-458,-1134]],[[251600,330511],[-661,68]],[[242130,335805],[-34,-6486]],[[242096,329319],[-637,87]],[[241459,329406],[3,1623],[-1032,137]],[[240430,331166],[104,1602],[10,3228]],[[268585,330425],[-242,-1949],[-248,-496]],[[268095,327980],[-738,4146]],[[267357,332126],[82,740],[-272,2637]],[[238036,329533],[-270,1020],[-9,-2177],[-298,111],[-9,-1669],[-302,-290]],[[237148,326528],[-149,21],[20,4291]],[[237019,330840],[144,2397],[-140,1638],[249,-26],[204,1843]],[[238075,336857],[-39,-7324]],[[261926,331086],[-253,-1944]],[[261673,329142],[-494,2572],[-57,1397]],[[239556,336116],[-9,-2441],[-149,23],[-7,-2427],[-202,25],[-8,-2654]],[[239181,328642],[-294,242],[-94,1576],[-757,-927]],[[237019,330840],[-164,-1085],[-526,-532],[-175,1675],[-128,-957]],[[236026,329941],[-115,4493]],[[263362,334549],[-670,-5484]],[[262692,329065],[-240,-1164]],[[262452,327901],[-195,2266]],[[212037,334134],[-2,-8177]],[[212035,325957],[-1,-4264]],[[212034,321693],[-669,4],[-4,-1665],[-293,7],[-3,-1619],[-292,8],[-1,-1623],[-585,17],[-91,-1634]],[[210096,315188],[-586,7]],[[209510,315195],[-502,3],[-1,3241]],[[209007,318439],[12,6755]],[[284317,332805],[371,-2175],[955,-1730],[26,-1206]],[[285669,327694],[-54,-767],[-593,-358],[-140,698]],[[284882,327267],[-821,3080]],[[285600,335677],[126,-1604],[273,-883]],[[285999,333190],[-398,-1100],[175,-2297],[-1124,1768],[-310,1394]],[[240430,331166],[-12,-5402],[-114,-39]],[[240304,325725],[-550,-592],[-185,1872],[-331,1451]],[[239238,328456],[-57,186]],[[226992,336062],[8,-6492]],[[227000,329570],[-1,-749]],[[226999,328821],[0,-19]],[[226999,328802],[-475,-44],[-290,766],[-418,55]],[[225816,329579],[-586,-9],[-19,3244]],[[228476,331184],[-1,-1622]],[[228475,329562],[-1475,8]],[[243374,332491],[-161,-3273],[-723,82]],[[242490,329300],[-394,19]],[[257735,335317],[-73,-4276],[185,-1700]],[[257847,329341],[-411,-2087],[-227,202]],[[257209,327456],[-369,2854]],[[247573,330712],[-600,58]],[[246973,330770],[-1496,24]],[[286926,335501],[415,-496]],[[287690,334442],[20,-1175],[-419,-3065],[-333,-1394],[-288,214],[-380,1512],[-221,-1047],[-325,2540],[265,-143],[-10,1306]],[[259858,332305],[-101,-4380]],[[259757,327925],[-680,1377],[-289,-544]],[[258788,328758],[11,1469]],[[258799,330227],[35,4085]],[[258799,330227],[-519,-1708],[-228,632]],[[258052,329151],[-205,190]],[[267357,332126],[-461,-863],[33,-2552]],[[266929,328711],[-948,-960]],[[265981,327751],[263,2892],[-1024,480]],[[265198,331181],[-169,-1111],[-21,-2086]],[[265008,327984],[-272,-947],[-243,467],[-219,-1109]],[[264274,326395],[-566,1467]],[[263708,327862],[264,835],[-23,1267],[-316,3898]],[[263633,333862],[1,202]],[[263634,334064],[-10,104]],[[255910,330827],[-429,244],[-353,-1106]],[[255128,329965],[-397,1581]],[[283265,330493],[-572,-1458],[-346,398]],[[282347,329433],[-50,3777]],[[248221,329794],[-58,-95]],[[248163,329699],[-190,947]],[[249836,330050],[-441,-507]],[[249395,329543],[-1138,388]],[[263634,334064],[-1,-202]],[[263708,327862],[-580,-828]],[[263128,327034],[-436,2031]],[[236026,329941],[-4,-304]],[[236022,329637],[-175,-1477],[-443,-195],[-252,1214],[-191,-612]],[[234961,328567],[-77,886],[-581,1638]],[[231707,329562],[0,-1617]],[[231707,327945],[-1281,2]],[[230426,327947],[0,3299],[-261,-1414],[-247,176]],[[229918,330008],[-3,1154]],[[279103,332257],[-595,-3991]],[[278508,328266],[-251,855],[-213,3067]],[[214486,334091],[-13,-8132]],[[214473,325959],[-2438,-2]],[[217486,334073],[-2,-8135]],[[217484,325938],[-1494,19]],[[215990,325957],[0,8137]],[[219007,334077],[3,-8124]],[[219010,325953],[-1526,-15]],[[220510,330410],[0,-4461]],[[220510,325949],[-1500,4]],[[215990,325957],[-1517,2]],[[271518,333337],[-29,-4139],[-182,-3291]],[[271307,325907],[-296,68]],[[271011,325975],[-264,59]],[[270747,326034],[1,1307],[-511,2796],[-304,-278]],[[269933,329859],[-13,1395]],[[282347,329433],[-80,-3574]],[[282267,325859],[-165,-563],[-420,837],[-332,-60]],[[281350,326073],[-393,1802]],[[252939,330347],[-5,-701]],[[252934,329646],[-672,-91],[-323,-2399],[-173,1]],[[251766,327157],[-166,3354]],[[280297,328416],[-211,-1293]],[[280086,327123],[-581,255],[-754,-1252]],[[278751,326126],[-353,1413],[110,727]],[[271948,333055],[227,-2695]],[[272175,330360],[382,-2967],[-24,-1802]],[[272533,325591],[-107,26]],[[272426,325617],[-1119,290]],[[273595,331632],[-24,-1620]],[[273571,330012],[-1396,348]],[[261673,329142],[-449,-3851]],[[261224,325291],[-188,2236],[-289,965]],[[260747,328492],[203,1622],[-339,2331]],[[233482,328003],[-312,226]],[[233170,328229],[-399,-1548],[-1070,-1312]],[[231701,325369],[6,2576]],[[225816,329579],[10,-9732]],[[225826,319847],[-1469,0]],[[224357,319847],[-5,4513]],[[224352,324360],[-5,6829]],[[244619,330721],[-212,-1147],[105,-2458],[-223,-2903]],[[244289,324213],[-330,36],[-3,-1236],[-368,689],[-238,-589]],[[243350,323113],[-586,854],[-297,-183]],[[242467,323784],[23,5516]],[[260747,328492],[-422,-561],[-84,-1375],[-372,150]],[[259869,326706],[-112,1219]],[[188318,312721],[-108,-986]],[[188210,311735],[-183,-611],[-147,-4378],[653,-3],[-47,-2816]],[[188486,303927],[-643,1],[-1223,898],[-311,-3086],[-1306,2189],[-1641,-13]],[[183362,303916],[1,5933]],[[183363,309849],[-2,22528]],[[268095,327980],[-175,-2988],[-203,-1444],[134,-484]],[[267851,323064],[-277,-501]],[[267574,322563],[-1,-3]],[[267573,322560],[-644,6151]],[[274045,331971],[332,-4443],[324,-1102]],[[274701,326426],[-806,-3867]],[[273895,322559],[-185,1400]],[[273710,323959],[-377,-576],[22,1922]],[[273355,325305],[216,4707]],[[278751,326126],[-5,-338]],[[278746,325788],[-367,682],[-294,-913],[-345,-2209]],[[277740,323348],[-312,427],[-120,1819]],[[277308,325594],[-233,1635],[-199,4840]],[[277308,325594],[-662,369],[-623,-753]],[[276023,325210],[-58,1792],[126,2469],[-367,2466]],[[275411,331910],[-586,-5936]],[[274825,325974],[-124,452]],[[222283,331209],[11,-6489]],[[222294,324720],[-137,-41]],[[222157,324679],[-1041,42],[0,-1614],[-294,3]],[[220822,323110],[-312,20]],[[220510,323130],[0,2819]],[[276023,325210],[-562,989]],[[275461,326199],[-228,-565],[-408,340]],[[269933,329859],[-238,-2104],[-19,-1655]],[[269676,326100],[-619,-853]],[[269057,325247],[-122,2958],[-350,2220]],[[255128,329965],[-90,-7353]],[[255038,322612],[-1051,45]],[[253987,322657],[8,5395],[-120,1798]],[[230426,327947],[0,-3244],[-137,1],[-1,-3821]],[[230288,320883],[-667,-984],[-127,786]],[[229494,320685],[-2,9491],[426,-168]],[[265981,327751],[-64,-576]],[[265917,327175],[-396,127],[-315,-711],[-198,1393]],[[224352,324360],[-359,353],[-1699,7]],[[229494,320685],[-431,1199]],[[229063,321884],[-238,-1062],[-352,454]],[[228473,321276],[2,8286]],[[241459,329406],[-204,-4750]],[[241255,324656],[-449,-779],[-905,103]],[[239901,323980],[403,1745]],[[256155,330612],[-49,-8076]],[[256106,322536],[-1068,76]],[[234961,328567],[-3,-2253],[-315,-6],[0,-2676]],[[234643,323632],[-1174,5]],[[233469,323637],[0,1895],[-295,3],[-4,2694]],[[262452,327901],[-222,-2978],[129,-2605]],[[262359,322318],[-796,-48]],[[261563,322270],[-276,-33]],[[261287,322237],[-306,-7]],[[260981,322230],[247,1084],[-4,1977]],[[257209,327456],[-20,-5059]],[[257189,322397],[-1046,132]],[[256143,322529],[-37,7]],[[237148,326528],[-7,-1358],[-305,-767]],[[236836,324403],[-229,-383],[-21,-2059],[-606,-644]],[[235980,321317],[42,8320]],[[246973,330770],[-2,-5539]],[[246971,325231],[-1504,75]],[[245467,325306],[8,3837]],[[245467,325306],[-8,-2742],[-290,30]],[[245169,322594],[-399,-324],[-210,-1347]],[[244560,320923],[13,2577],[-284,713]],[[248163,329699],[-214,-2387],[219,-627],[20,-1533],[-293,-395],[-116,-1725],[-268,-566]],[[247511,322466],[186,-1296],[-172,-1230]],[[247525,319940],[-289,-503]],[[247236,319437],[-2,1326]],[[247234,320763],[22,4543],[-285,-75]],[[251766,327157],[-13,-4699]],[[251753,322458],[-101,0]],[[251652,322458],[-541,-5]],[[251111,322453],[-505,-9]],[[250606,322444],[40,7497]],[[283502,328881],[-231,-2074]],[[283271,326807],[-356,-2933],[-358,-1181]],[[282557,322693],[-108,2562],[-182,604]],[[239238,328456],[-4,-1941],[-493,49],[-203,-1309],[-493,-195],[5,-2146]],[[238050,322914],[-1029,136],[-185,1353]],[[269057,325247],[-524,-1430]],[[268533,323817],[-371,-223]],[[268162,323594],[-311,-530]],[[253987,322657],[-600,-192]],[[253387,322465],[-453,-3]],[[252934,322462],[-51,3]],[[252883,322465],[51,7181]],[[273355,325305],[-822,286]],[[285469,320638],[-203,-948],[-926,-816]],[[284340,318874],[-64,5013]],[[284276,323887],[204,503],[398,-2748],[591,-1004]],[[284882,327267],[-235,-2198],[118,-897]],[[284765,324172],[-259,864],[-282,-1162]],[[284224,323874],[-953,2933]],[[258788,328758],[-158,-3843]],[[258630,324915],[-206,2015],[-376,1415],[4,806]],[[249395,329543],[-32,-7084]],[[249363,322459],[-223,-9]],[[249140,322450],[-1629,16]],[[270747,326034],[-688,163]],[[270059,326197],[-383,-97]],[[250606,322444],[-430,-12]],[[250176,322432],[-813,27]],[[252883,322465],[-1130,-7]],[[235980,321317],[-18,-3815]],[[235962,317502],[-20,-4124]],[[235942,313378],[-1326,-11]],[[234616,313367],[-2,3239],[-338,-2]],[[234276,316604],[0,3244],[171,1607],[196,17],[0,2160]],[[226999,328802],[9,-8951]],[[227008,319851],[-1,-3236]],[[227007,316615],[-1172,1]],[[225835,316616],[-9,3231]],[[228473,321276],[-555,611],[-74,2247],[-845,4687]],[[258630,324915],[-2,-2532]],[[258628,322383],[-1295,15]],[[257333,322398],[-144,-1]],[[242467,323784],[-5,-1009]],[[242462,322775],[-297,41],[-5,-1617],[-876,479]],[[241284,321678],[-151,871],[122,2107]],[[259869,326706],[27,-4377]],[[259896,322329],[-1243,53]],[[258653,322382],[-25,1]],[[263128,327034],[11,-1303],[-212,-3409]],[[262927,322322],[-101,-5]],[[262826,322317],[-467,1]],[[200462,326631],[361,-4881]],[[200823,321750],[-379,16],[-5,-7060]],[[200439,314706],[-1451,-14]],[[198988,314692],[-3680,23]],[[229063,321884],[-7,-2046]],[[229056,319838],[-2048,13]],[[267573,322560],[-1043,-137]],[[266530,322423],[-489,3118],[-223,-91]],[[265818,325450],[99,1725]],[[285669,327694],[310,746],[30,-1788],[-452,-995],[295,-346],[-138,-1387],[-575,-2025],[-396,1477],[22,796]],[[260981,322230],[-364,32]],[[260617,322262],[-721,67]],[[281350,326073],[46,-5342],[-149,-3077],[243,-284]],[[281490,317370],[-394,-3132]],[[281096,314238],[-204,2184],[-465,3449]],[[280427,319871],[-419,2517],[-71,1882],[149,2853]],[[239901,323980],[-690,-4023],[1,-1632]],[[239212,318325],[-296,-555]],[[238916,317770],[-881,35]],[[238035,317805],[15,5109]],[[233469,323637],[-441,-557],[-20,-6469]],[[233008,316611],[1,-1619],[-439,-6]],[[232570,314986],[-587,-6],[0,1619],[-585,2]],[[231398,316601],[2,1620]],[[231400,318221],[7,5253],[294,1895]],[[265818,325450],[-331,-347],[-413,-2793]],[[265074,322310],[-349,6]],[[264725,322316],[-533,16]],[[264192,322332],[82,4063]],[[231400,318221],[-877,0]],[[230523,318221],[1,2620],[-236,42]],[[264192,322332],[-835,-2]],[[263357,322330],[-430,-8]],[[280427,319871],[-1132,-401]],[[279295,319470],[-372,2213]],[[278923,321683],[-155,1648],[-22,2457]],[[284224,323874],[52,13]],[[284340,318874],[-211,-331]],[[284129,318543],[-198,2128],[-360,239],[-380,1072],[-481,71]],[[282710,322053],[-153,640]],[[203116,323293],[0,-1615],[261,2],[1,-1550],[-729,1]],[[202649,320131],[-58,616],[-705,53],[-101,-673],[-844,4],[-118,1619]],[[209007,318439],[-587,-7],[1,-1615],[-294,-10],[-3,-1617],[-1242,-7],[0,-4792]],[[206882,310391],[-1173,-5]],[[205709,310386],[1,4846],[62,-6],[-1,8105]],[[270059,326197],[-28,-6431],[221,-1212]],[[270252,318554],[-254,-3697],[-212,-1915]],[[269786,312942],[-11,23]],[[269775,312965],[-379,2612],[-91,3608]],[[269305,319185],[-142,3645],[-154,854],[-476,133]],[[276023,325210],[227,-1072],[186,597],[147,-1356],[71,-2679],[-218,-1752]],[[276436,318948],[-6,0]],[[276430,318948],[-1089,137]],[[275341,319085],[120,7114]],[[278923,321683],[-437,-2239],[-436,98]],[[278050,319542],[11,671]],[[278061,320213],[-25,1291],[-296,1844]],[[275341,319085],[-679,65]],[[274662,319150],[-651,42],[43,2132],[-159,1235]],[[271464,320921],[-199,-1405],[-198,-4516]],[[271067,315000],[-604,2048],[-211,1506]],[[271011,325975],[333,-4682],[120,-372]],[[282710,322053],[66,-1015],[-81,-3692]],[[282695,317346],[-1205,24]],[[214473,325959],[-5,-8115]],[[214468,317844],[-992,-9]],[[213476,317835],[-1442,8]],[[212034,317843],[0,3850]],[[272173,319555],[-237,565]],[[271936,320120],[-472,801]],[[272426,325617],[-120,-2206],[-216,-215],[83,-3641]],[[219010,325953],[-5,-8103]],[[219005,317850],[-1126,11]],[[217879,317861],[-406,0]],[[217473,317861],[11,8077]],[[220510,323130],[0,-5304]],[[220510,317826],[-1158,20]],[[219352,317846],[-347,4]],[[215990,325957],[-18,-8108]],[[215972,317849],[-1028,11]],[[214944,317860],[-476,-16]],[[277740,323348],[-316,-945],[31,-1526],[-351,-1962]],[[277104,318915],[-668,33]],[[217473,317861],[-1061,-16]],[[216412,317845],[-440,4]],[[273710,323959],[112,-2797],[-91,-1953]],[[273731,319209],[-1617,15]],[[272114,319224],[59,331]],[[266530,322423],[-186,-14]],[[266344,322409],[-1079,-93]],[[265265,322316],[-191,-6]],[[247234,320763],[-1924,142],[-2,-829]],[[245308,320076],[-139,2518]],[[224357,319847],[-575,10],[0,-4860]],[[223782,314997],[-488,-1],[-284,811]],[[223010,315807],[139,1188],[-267,11],[14,2696],[-285,-467]],[[222611,319235],[-167,661],[-83,3113],[-204,1670]],[[284158,318179],[-29,364]],[[285469,320638],[29,1355],[695,561],[-107,-1081],[521,322],[-484,-2668],[-165,-1717],[-182,-44],[-131,1684],[-204,-1777],[-551,162],[-575,-954],[-157,1698]],[[222611,319235],[-441,-168],[-51,-1639],[-679,-8]],[[221440,317420],[-149,817],[-33,3268],[-436,-2],[0,1607]],[[164640,304627],[231,-1775],[-418,-877],[-322,2121],[509,531]],[[165054,305328],[976,-1440],[-461,-704],[-359,131],[-156,2013]],[[166339,320671],[-2,-7969],[-90,-1807]],[[166247,310895],[-250,760],[-867,-159],[-357,994],[-804,202],[-490,-416],[-116,1396],[-373,1056],[124,2395],[-24,2471],[-171,1150],[60,1338]],[[241284,321678],[-584,-794],[39,-1065]],[[240739,319819],[-931,86],[-3,-1605]],[[239805,318300],[-593,25]],[[238035,317805],[-614,-1483]],[[237421,316322],[-244,500],[-834,91],[-381,589]],[[244560,320923],[-20,-4593],[-199,25],[193,-1934]],[[244534,314421],[-604,81],[-3,-1622],[-230,23]],[[243697,312903],[96,2891],[-160,660],[20,4598],[-307,36],[4,2025]],[[269305,319185],[-840,-2741],[-145,-912]],[[268320,315532],[-168,2206],[10,5856]],[[243697,312903],[-75,13]],[[243622,312916],[-905,125]],[[242717,313041],[1,2457],[-235,29],[98,2072],[20,5168],[-139,8]],[[274662,319150],[434,-3775]],[[275096,315375],[-397,-1019],[74,-878],[-605,-586],[-6,1090],[-378,-1531]],[[273784,312451],[4,1591]],[[273788,314042],[49,2841],[-106,2326]],[[234276,316604],[-1268,7]],[[268320,315532],[-415,-2628]],[[267905,312904],[-174,218]],[[267731,313122],[-145,795]],[[267586,313917],[-653,2806]],[[266933,316723],[-40,540]],[[266893,317263],[305,3008],[323,1133],[53,1159]],[[205709,310386],[-2,-1614],[-1700,0]],[[204007,308772],[-1371,-6],[-3,3352]],[[202633,312118],[-147,1515],[16,6496],[147,2]],[[278050,319542],[43,-1242],[-360,-2633]],[[277733,315667],[-629,3248]],[[221440,317420],[1,-4034],[-493,-32]],[[220948,313354],[-223,1273],[-207,-261]],[[220518,314366],[-8,3460]],[[242717,313041],[-496,63]],[[242221,313104],[-106,14]],[[242115,313118],[5,1627],[-291,31],[-264,1665],[-303,325],[6,1345],[-291,53],[9,1622],[-247,33]],[[256004,318053],[-462,772]],[[255542,318825],[-243,556],[-714,-1852],[-404,1290],[-162,1541],[-347,181]],[[253672,320541],[-285,1924]],[[256143,322529],[-11,-3407],[-128,-1069]],[[245308,320076],[-7,-2464],[140,-1661]],[[245441,315951],[-7,-3254],[151,-26],[-6,-2481],[-150,6]],[[245429,310196],[-103,136],[-579,3569],[-213,520]],[[266893,317263],[-555,643],[-280,1346]],[[266058,319252],[-12,1037],[298,2120]],[[257333,322398],[-17,-8213]],[[257316,314185],[-332,545],[-546,1972]],[[256438,316702],[-434,1351]],[[252925,317998],[-982,22]],[[251943,318020],[-96,1897],[-197,-2],[2,2543]],[[252934,322462],[-9,-4464]],[[253555,314761],[-47,-2207]],[[253508,312554],[-481,7]],[[253027,312561],[-4,5160],[-98,277]],[[253672,320541],[-117,-5780]],[[251943,318020],[-47,-2986]],[[251896,315034],[-984,33]],[[250912,315067],[1,4042],[197,807],[1,2537]],[[249142,318304],[-519,9],[-251,-975]],[[248372,317338],[-343,700],[-215,-623]],[[247814,317415],[-5,2533],[-284,-8]],[[249140,322450],[2,-4146]],[[250912,315067],[-438,-278]],[[250474,314789],[-147,-6],[-2,4867],[-149,0],[0,2782]],[[250474,314789],[-2,-1631]],[[250472,313158],[-642,1090],[-534,3]],[[249296,314251],[-2,2446],[-152,1607]],[[266058,319252],[-82,-420]],[[265976,318832],[-275,-140]],[[265701,318692],[-106,2241],[-330,1383]],[[258653,322382],[-137,-6674],[89,-612]],[[258605,315096],[-29,-1679],[-447,-518],[-146,1195]],[[257983,314094],[-427,804],[-240,-713]],[[260678,319948],[-562,-4378],[-610,-2702],[-150,-76]],[[259356,312792],[-223,839],[-30,1451],[-498,14]],[[260617,322262],[61,-2314]],[[263365,319856],[101,-595],[-208,-1794],[7,-2679]],[[263265,314788],[-172,733],[-550,214]],[[262543,315735],[233,4869]],[[262776,320604],[50,1713]],[[263357,322330],[8,-2474]],[[264725,322316],[-135,-684],[239,-2782],[-180,-2853]],[[264649,315997],[-86,-841]],[[264563,315156],[-22,277]],[[264541,315433],[-329,3527],[-293,882],[-554,14]],[[262543,315735],[-381,-217]],[[262162,315518],[-1,1816],[-262,888]],[[261899,318222],[232,964],[228,3132]],[[261899,318222],[-334,1613],[-2,2435]],[[265701,318692],[-212,-1319]],[[265489,317373],[-231,351],[-271,-1838],[-338,111]],[[284158,318179],[119,-1091],[-640,-3358],[-489,-1594]],[[283148,312136],[-159,657],[-294,4553]],[[261287,322237],[-239,-2821],[0,-1352],[-233,-2520]],[[260815,315544],[-137,4404]],[[262162,315518],[-52,-656]],[[262110,314862],[-108,4]],[[262002,314866],[-1168,24]],[[260834,314890],[-19,654]],[[230523,318221],[0,-1628],[-295,-3],[-4,-3251]],[[230224,313339],[-873,12]],[[229351,313351],[0,1622],[-293,812]],[[229058,315785],[-2,4053]],[[202633,312118],[-979,1703],[-317,896],[-898,-11]],[[279295,319470],[266,-2708],[-183,-3842]],[[279378,312920],[-224,-636],[-333,-2783]],[[278821,309501],[-4,24]],[[278817,309525],[-1052,5966]],[[277765,315491],[-32,176]],[[212034,317843],[-3,-8091]],[[212031,309752],[0,-188]],[[212031,309564],[-1940,9],[5,5615]],[[272114,319224],[155,-4645]],[[272269,314579],[-1,-1445]],[[272268,313134],[-327,-920],[-286,1648]],[[271655,313862],[-588,1138]],[[247236,319437],[-213,565],[88,-2172],[-320,-435],[282,-923],[-325,-595]],[[246748,315877],[-1307,74]],[[255542,318825],[30,-628],[-318,-3711]],[[255254,314486],[-1699,275]],[[166086,290611],[276,-965],[-300,-16],[24,981]],[[167726,304768],[-743,1873],[-183,2255],[-553,1999]],[[167906,318608],[691,-9433],[-104,-2110],[-329,6],[-438,-2303]],[[247814,317415],[-122,-2322],[127,-841]],[[247819,314252],[0,-807]],[[247819,313445],[-294,-28],[6,-1584],[-272,-13]],[[247259,311820],[-473,1881]],[[246786,313701],[-38,2176]],[[260834,314890],[37,-1200]],[[260871,313690],[-340,-2929],[-579,-3107]],[[259952,307654],[-731,14]],[[259221,307668],[-12,4227],[147,897]],[[281096,314238],[217,-1643],[-222,-1225]],[[281091,311370],[-528,-425],[-618,1727],[-567,248]],[[223010,315807],[-346,-3461]],[[222664,312346],[46,-2146]],[[222710,310200],[-463,2268],[-59,-1528],[-214,361]],[[221974,311301],[-292,391],[-321,-718],[-413,2380]],[[242115,313118],[-438,43],[-4,-817],[-514,69],[-222,-771]],[[240937,311642],[-45,1094],[-302,570]],[[240590,313306],[-42,1616],[-478,872],[30,2513],[-295,-7]],[[225835,316616],[-146,-4],[-1,-3243]],[[225688,313369],[-284,3],[-145,-1083],[-582,7],[-438,-808]],[[224239,311488],[-1,1890],[-456,-2],[0,1621]],[[229058,315785],[-745,-262],[155,-2162],[-579,4]],[[227889,313365],[-586,3]],[[227303,313368],[-1,3244],[-295,3]],[[264541,315433],[-415,-1026]],[[264126,314407],[-860,-258]],[[263266,314149],[-1,639]],[[266933,316723],[-339,-3833]],[[266594,312890],[-433,-921]],[[266161,311969],[-142,1338]],[[266019,313307],[139,813],[-135,1990],[115,1469],[-162,1253]],[[168718,285870],[613,-3553],[-362,241],[-251,3312]],[[168675,294217],[663,-1336],[119,-1995],[-366,418],[-149,2047],[-267,866]],[[170961,302915],[-533,9],[-383,-3729]],[[170045,299195],[-299,468],[-188,-1226],[-342,667],[55,1824],[-185,2348],[-237,1371],[-531,-106],[-203,-603],[-389,830]],[[273788,314042],[-1519,537]],[[269786,312942],[-1189,-5125]],[[268597,307817],[-93,1502]],[[268504,309319],[-251,3128],[-348,457]],[[276707,313833],[-152,-754],[-450,18],[-676,-2348]],[[275429,310749],[-151,2724]],[[275278,313473],[-182,1902]],[[276430,318948],[164,-2140],[207,-809],[-94,-2166]],[[277765,315491],[-510,-6013]],[[277255,309478],[-68,132]],[[277187,309610],[-190,3520],[-290,703]],[[256438,316702],[-14,-6930]],[[256424,309772],[0,-270]],[[256424,309502],[-1170,99]],[[255254,309601],[0,4885]],[[266019,313307],[-494,36]],[[265525,313343],[-95,2291],[59,1739]],[[271655,313862],[-387,-3128],[-316,-1221],[-138,-1800]],[[270814,307713],[-619,2360],[-220,1485]],[[269975,311558],[-189,1384]],[[209510,315195],[7,-9723]],[[209517,305472],[-585,10],[0,-1637],[-1755,87],[0,1633],[-298,9]],[[206879,305574],[3,4817]],[[249296,314251],[-147,-1]],[[249149,314250],[-1330,2]],[[240590,313306],[-340,47],[-91,-2176],[-1279,142]],[[238880,311319],[36,6451]],[[231398,316601],[2,-3247],[-148,-1629],[-731,-2]],[[230521,311723],[-297,19],[0,1597]],[[253027,312561],[-593,32]],[[252434,312593],[1,817],[-542,-5]],[[251893,313405],[3,1629]],[[216412,317845],[0,-8101]],[[216412,309744],[-260,3]],[[216152,309747],[-1208,11]],[[214944,309758],[0,8102]],[[217879,317861],[-4,-8109]],[[217875,309752],[-265,-6]],[[217610,309746],[-1198,-2]],[[214944,309758],[-258,1]],[[214686,309759],[-1211,-2]],[[213475,309757],[1,8078]],[[219352,317846],[-4,-8079]],[[219348,309767],[-278,10]],[[219070,309777],[-1195,-25]],[[220518,314366],[0,-4631]],[[220518,309735],[-1170,32]],[[238880,311319],[-3,-1067]],[[238877,310252],[-196,24]],[[238681,310276],[-1273,171]],[[237408,310447],[13,5875]],[[213475,309757],[-250,-2]],[[213225,309755],[-1194,-3]],[[265525,313343],[-379,-1617]],[[265146,311726],[-589,2243],[6,1187]],[[283148,312136],[-617,-2746]],[[282531,309390],[-232,1787],[-388,-117],[-188,-953]],[[281723,310107],[-370,469]],[[281353,310576],[-262,794]],[[237408,310447],[-890,102],[31,-3035]],[[236549,307514],[-631,-50]],[[235918,307464],[24,5914]],[[257983,314094],[-89,-4491]],[[257894,309603],[-1470,169]],[[267586,313917],[-206,-1091],[-615,-312]],[[266765,312514],[-171,376]],[[227303,313368],[-1,-4055]],[[227302,309313],[-1606,12]],[[225696,309325],[-8,4044]],[[232570,314986],[0,-1620],[-300,-11],[2,-6507]],[[232272,306848],[-593,2]],[[231679,306850],[-1159,9]],[[230520,306859],[1,4864]],[[234616,313367],[-304,7],[0,-4907],[-291,4],[-15,-1618]],[[234006,306853],[-1734,-5]],[[246786,313701],[-19,-2054],[-497,-863],[38,-1147],[-293,-1724],[-253,399],[310,-1738],[-364,-407]],[[245708,306167],[3,-24]],[[245711,306143],[-453,5]],[[245258,306148],[92,410],[79,3638]],[[229351,313351],[-149,-2],[-1,-3219],[-146,-6]],[[229055,310124],[-301,797],[-865,13],[0,2431]],[[224239,311488],[147,-1345],[-3,-3288]],[[224383,306855],[-432,-385],[-522,1415]],[[223429,307885],[-656,27],[-63,2288]],[[263266,314149],[3,-2538]],[[263269,311611],[-982,-374]],[[262287,311237],[-274,215],[97,3410]],[[278817,309525],[-155,-872]],[[278662,308653],[-702,872],[-466,-1308]],[[277494,308217],[-239,1261]],[[265146,311726],[62,-1577]],[[265208,310149],[-838,21]],[[264370,310170],[1,855]],[[264371,311025],[-245,3382]],[[275429,310749],[0,-38]],[[275429,310711],[-371,-1301],[-162,-2339]],[[274896,307071],[-379,-1286]],[[274517,305785],[-288,-570],[-418,2121],[121,1598]],[[273932,308934],[155,2001],[-303,1516]],[[212031,309564],[-10,-8889]],[[212021,300675],[-15,-4739]],[[212006,295936],[-1274,-7]],[[210732,295929],[1,1618],[-582,-19],[5,3039],[-348,-1],[0,4885],[-291,21]],[[251893,313405],[-148,-262],[-98,-2422]],[[251647,310721],[-1175,275]],[[250472,310996],[0,2162]],[[259221,307668],[-550,-1892]],[[258671,305776],[-416,2982]],[[258255,308758],[-361,845]],[[262002,314866],[-202,-2820],[-428,-1223],[-148,-1566],[-209,4]],[[261015,309261],[-144,4429]],[[262287,311237],[-115,-5511]],[[262172,305726],[-1045,-291]],[[261127,305435],[-112,3826]],[[204007,308772],[-2,-8089],[-357,6],[0,-3269],[-895,189],[3,-3357]],[[202756,294252],[-282,-80],[-3451,20]],[[199023,294192],[-10,14576],[-25,5924]],[[199023,294192],[-802,21],[0,-5147]],[[198221,289066],[-2916,138]],[[195305,289204],[0,10598]],[[255254,309601],[-292,45]],[[254962,309646],[-1501,262]],[[253461,309908],[47,2646]],[[221974,311301],[-1,-5776]],[[221973,305525],[-842,203],[-613,2379]],[[220518,308107],[0,1628]],[[273932,308934],[-751,-1023],[-232,-720],[-385,1197]],[[272564,308388],[-296,4746]],[[245258,306148],[-220,-1244],[179,-1807],[-328,48],[-478,1054]],[[244411,304199],[-66,1224]],[[244345,305423],[-235,2560],[-507,303],[19,4630]],[[264371,311025],[-903,8],[-199,578]],[[250472,310996],[3,-4065]],[[250475,306931],[-737,17]],[[249738,306948],[0,538],[-588,29]],[[249150,307515],[-1,6735]],[[249150,307515],[-2,-541],[-585,15]],[[248563,306989],[-568,-78]],[[247995,306911],[2,1971],[-165,659],[-13,3904]],[[267731,313122],[-172,-4100]],[[267559,309022],[-165,-650],[-348,481],[-159,-755]],[[266887,308098],[-122,4416]],[[277187,309610],[-200,-184],[-957,-3894]],[[276030,305532],[-239,1408],[128,659],[-179,3128],[-311,-16]],[[272564,308388],[-61,-777]],[[272503,307611],[-371,-2256]],[[272132,305355],[-182,82],[-533,1971],[-394,-959]],[[271023,306449],[-209,1264]],[[261127,305435],[65,-2172]],[[261192,303263],[-366,-424]],[[260826,302839],[-582,505]],[[260244,303344],[-292,4310]],[[247116,305288],[0,-1624]],[[247116,303664],[-574,36]],[[246542,303700],[-7,2441],[-827,26]],[[247259,311820],[2,-3021],[-145,16],[0,-3527]],[[247995,306911],[-1,-1623],[-878,0]],[[266161,311969],[-151,-1209],[137,-1333]],[[266147,309427],[-550,-3124]],[[265597,306303],[-143,-536],[-540,1289]],[[264914,307056],[377,2075],[-83,1018]],[[252434,312593],[-3,-7006]],[[252431,305587],[-491,-269]],[[251940,305318],[-293,8]],[[251647,305326],[0,5395]],[[229055,310124],[0,-2981]],[[229055,307143],[-102,-1894]],[[228953,305249],[-1646,5]],[[227307,305254],[-5,4059]],[[230520,306859],[-129,-799],[-366,1]],[[230025,306061],[-236,1077],[-734,5]],[[225696,309325],[4,-2728]],[[225700,306597],[-87,-540],[-545,799],[-164,-1383]],[[224904,305473],[-175,-372],[-346,1754]],[[235918,307464],[-20,-4634]],[[235898,302830],[-25,-5641]],[[235873,297189],[-725,1210]],[[235148,298399],[-85,1118],[-236,-357],[-302,2195],[-518,1395]],[[234007,302750],[-1,4103]],[[240937,311642],[-27,-4926]],[[240910,306716],[-580,117]],[[240330,306833],[-387,76],[-25,1423],[-898,817],[-143,1103]],[[192649,296055],[0,-2067],[-471,-22],[-761,-1596],[-17,-3435]],[[191400,288935],[-519,-982],[-411,-2924],[-457,4942],[-260,4020]],[[189753,293991],[-316,3938],[-198,-1382],[-453,2730],[43,1284],[-343,3366]],[[268504,309319],[-576,-1452],[-270,224]],[[267658,308091],[-99,931]],[[242221,313104],[-71,-8013]],[[242150,305091],[-288,-50]],[[241862,305041],[-455,1583],[-497,92]],[[244345,305423],[-785,1742],[-49,-1484],[-582,-12],[0,-545]],[[242929,305124],[-779,-33]],[[266887,308098],[-133,-492]],[[266754,307606],[-217,1202],[-390,619]],[[281353,310576],[-256,-2617],[-334,-211],[-135,-1226],[-339,132],[-50,-1711],[-247,-2039]],[[279992,302904],[-1171,6597]],[[269975,311558],[-244,-1242],[240,-2297],[-54,-2071],[-167,-825]],[[269750,305123],[-567,148],[-179,-1083]],[[269004,304188],[-407,3629]],[[253461,309908],[-84,-4366]],[[253377,305542],[-946,45]],[[223429,307885],[-1,-7045]],[[223428,300840],[-1456,0]],[[221972,300840],[1,4685]],[[263252,305382],[-219,27]],[[263033,305409],[-515,58]],[[262518,305467],[-346,259]],[[263269,311611],[-17,-6229]],[[264370,310170],[-2,-2778]],[[264368,307392],[-267,-204],[-179,-1893]],[[263922,305295],[-670,87]],[[271023,306449],[-385,-3232]],[[270638,303217],[-108,-158]],[[270530,303059],[5,561],[-727,-210],[-58,1713]],[[282531,309390],[61,24]],[[282592,309414],[10,-65]],[[282602,309349],[-405,-2626],[-165,-1820],[-109,2615]],[[281923,307518],[-200,2589]],[[251647,305326],[-877,-15]],[[250770,305311],[-293,-4],[-2,1624]],[[276030,305532],[200,-730]],[[276230,304802],[-500,-1759],[-394,2383],[-444,601],[4,1044]],[[281923,307518],[8,-4063],[-186,-1610],[-387,486],[-737,-285],[-373,-589]],[[280248,301457],[-33,176]],[[280215,301633],[-11,67]],[[280204,301700],[-212,1204]],[[237408,310447],[27,-3044],[293,-50],[-7,-3257]],[[237721,304096],[-23,-4862],[-351,60]],[[237347,299294],[-4,1]],[[237343,299295],[-252,2449],[-133,5739],[-409,31]],[[238681,310276],[-66,-2358],[143,-158],[46,-2922],[169,-1709]],[[238973,303129],[-231,-2]],[[238742,303127],[-202,-247],[-819,1216]],[[206879,305574],[2,-12856],[-37,-4822],[-1146,-121]],[[205698,287775],[-6,3219],[-1134,-5],[-3,1594],[-1724,6]],[[202831,292589],[-75,1663]],[[240330,306833],[-20,-6441]],[[240310,300392],[-587,-612]],[[239723,299780],[-511,1233],[-239,2116]],[[264914,307056],[-97,-2181]],[[264817,304875],[-449,1093],[0,1424]],[[254962,309646],[-4,-5675]],[[254958,303971],[0,-1619]],[[254958,302352],[-879,89]],[[254079,302441],[-98,2459],[-614,117]],[[253367,305017],[10,525]],[[183362,303916],[0,-11575]],[[183362,292341],[-1740,3],[0,1620],[-862,-20],[-3,-8075],[-686,-61]],[[180071,285808],[-439,260],[-94,887],[82,3389],[-151,600],[21,1938],[272,564]],[[179762,293446],[283,2148],[77,2692],[-106,4262],[274,1899],[11,1120]],[[181121,309576],[281,-790],[1201,-586],[253,1502],[507,147]],[[219070,309777],[-4,-8906]],[[219066,300871],[-1455,-38]],[[217611,300833],[-1,8913]],[[220518,308107],[-138,101],[-4,-7331]],[[220376,300877],[-1310,-6]],[[258255,308758],[-186,-144],[-224,-2438],[-291,-747],[-162,-1946],[-430,-1123],[-129,-1066]],[[256833,301294],[-358,590],[-165,1921]],[[256310,303805],[109,-16],[5,5713]],[[217611,300833],[-1456,-60]],[[216155,300773],[-3,8974]],[[216155,300773],[-1454,-106]],[[214701,300667],[-15,9092]],[[214701,300667],[-1476,8]],[[213225,300675],[0,9080]],[[277494,308217],[-65,-786],[217,-4445],[468,-2796]],[[278114,300190],[-479,-436],[-840,1115],[-215,922]],[[276580,301791],[-276,1144]],[[276304,302935],[225,772],[-299,1095]],[[213225,300675],[-1204,0]],[[256310,303805],[-1352,166]],[[280204,301700],[21,-471],[-685,-2076],[-529,-3188]],[[279011,295965],[-269,-6],[-253,2493]],[[278489,298452],[-185,3128],[-229,1784],[245,1596],[342,3693]],[[278489,298452],[-265,205],[-88,1174]],[[278136,299831],[-22,359]],[[266754,307606],[118,-2921]],[[266872,304685],[-494,-1390]],[[266378,303295],[-72,1227],[-571,652],[-138,1129]],[[227307,305254],[-1,-3184]],[[227306,302070],[-352,1622],[-507,-2568],[-302,757]],[[226145,301881],[90,1860],[-381,232],[-154,2624]],[[269004,304188],[84,-1081]],[[269088,303107],[-225,529]],[[268863,303636],[-374,-245]],[[268489,303391],[-548,1345]],[[267941,304736],[-283,3355]],[[267941,304736],[-591,-490]],[[267350,304246],[-195,-334]],[[267155,303912],[-283,773]],[[274517,305785],[-85,-1864],[134,-4155],[-61,-616]],[[274505,299150],[-581,663],[-511,1884]],[[273413,301697],[-135,2327],[-372,1680],[-227,39],[-176,1868]],[[258671,305776],[-63,-2961]],[[258608,302815],[-229,-1970],[-471,-601],[9,-685]],[[257917,299559],[-515,1403],[-542,-470]],[[256860,300492],[-27,802]],[[221972,300840],[4,-1864]],[[221976,298976],[-229,1201],[-392,509],[-834,191]],[[220521,300877],[-145,0]],[[224904,305473],[0,-4595]],[[224904,300878],[-1476,-38]],[[260244,303344],[-448,-474],[-461,-1878]],[[259335,300992],[-374,2733],[-353,-910]],[[273413,301697],[-85,-3220]],[[273328,298477],[-403,-1010]],[[272925,297467],[-503,1412],[-567,2766]],[[271855,301645],[277,3710]],[[249738,306948],[7,-5486]],[[249745,301462],[-730,-2],[-438,571]],[[248577,302031],[-14,4958]],[[237343,299295],[-645,694],[-800,2841]],[[271855,301645],[-224,-1169]],[[271631,300476],[-514,964],[-162,2031],[-317,-254]],[[264817,304875],[-499,-1732]],[[264318,303143],[-198,104],[-7,-5871]],[[264113,297376],[-299,18],[-3,-1823]],[[263811,295571],[-420,-902]],[[263391,294669],[-672,168]],[[262719,294837],[116,1165]],[[262835,296002],[415,1551],[228,1602]],[[263478,299155],[298,1539],[245,2560],[-99,2041]],[[230025,306061],[101,-881],[-211,-1084],[101,-2120]],[[230016,301976],[-186,350],[-327,-1482],[-269,387],[-183,1849]],[[229051,303080],[-98,2169]],[[244411,304199],[-353,-701],[-25,-3600]],[[244033,299898],[-1166,155]],[[242867,300053],[62,5071]],[[276304,302935],[-783,-2654],[-394,-674],[-382,-2307]],[[274745,297300],[-240,1850]],[[265597,306303],[-143,-2279],[195,-1400]],[[265649,302624],[-510,-2675]],[[265139,299949],[-116,-620]],[[265023,299329],[-659,3103],[-46,711]],[[248577,302031],[0,-1380],[-585,-270],[-2,-1632]],[[247990,298749],[-442,543],[316,1089],[-750,38]],[[247114,300419],[2,3245]],[[250770,305311],[4,-4878],[-150,-1369]],[[250624,299064],[-878,-312]],[[249746,298752],[-1,2710]],[[231679,306850],[149,-590],[1,-3199],[271,-1474],[224,-5]],[[232324,301582],[-241,-630]],[[232083,300952],[-247,888],[-429,-565],[-250,-1798],[-558,-652]],[[230599,298825],[-125,949],[-568,1263],[110,939]],[[234007,302750],[-176,481],[-257,-1570]],[[233574,301661],[-669,201],[-133,967],[-448,-1247]],[[241862,305041],[-376,-48],[-11,-1571],[394,-3307]],[[241869,300115],[-693,133]],[[241176,300248],[-866,144]],[[226145,301881],[-3,-7871]],[[226142,294010],[-1232,-6]],[[224910,294004],[-6,6874]],[[266378,303295],[-305,-1110]],[[266073,302185],[-424,439]],[[246542,303700],[-5,-6913],[-293,-2],[0,-1616]],[[246244,295169],[-1256,-5]],[[244988,295164],[-45,601]],[[244943,295765],[269,599],[-236,1265],[282,815],[-40,1455],[303,-382],[-185,3716],[560,1252],[-185,1658]],[[244943,295765],[-41,-823],[-579,866]],[[244323,295808],[8,4034],[-298,56]],[[262518,305467],[-156,-2441],[-200,-874]],[[262162,302152],[-936,-50]],[[261226,302102],[-34,1161]],[[253367,305017],[-117,-5834]],[[253250,299183],[-737,-1766]],[[252513,297417],[38,554],[-611,6],[-2,2436]],[[251938,300413],[2,4905]],[[210732,295929],[-587,-11],[-3,-3235],[-124,-7],[0,-8008],[-134,-3]],[[209884,284665],[-2861,-35],[-28,-8243]],[[206995,276387],[-1400,-32],[4,8250],[101,1],[-2,3169]],[[263478,299155],[-127,1132],[-280,33]],[[263071,300320],[-38,5089]],[[179762,293446],[-4058,-181]],[[175704,293265],[-2643,-5],[-585,89],[-362,1122],[-387,245]],[[171727,294716],[267,2870],[-339,971],[-382,2958]],[[263071,300320],[-494,-473]],[[262577,299847],[-380,582]],[[262197,300429],[-35,1723]],[[270530,303059],[-303,-446],[112,-1813],[-159,-1241],[160,-3138]],[[270340,296421],[-326,1801]],[[270014,298222],[-319,2646],[-607,2239]],[[251938,300413],[-588,24],[-1,-1081],[-287,-276]],[[251062,299080],[-438,-16]],[[229051,303080],[-27,-97]],[[229024,302983],[-420,-2885],[-33,-1359],[-271,1490],[50,1869],[-342,-239],[-177,-1289],[-249,387],[-61,1401]],[[227521,302358],[-215,-288]],[[242867,300053],[0,-1626]],[[242867,298427],[-989,64]],[[241878,298491],[-9,1624]],[[254079,302441],[14,-7371]],[[254093,295070],[-914,185]],[[253179,295255],[71,3928]],[[268489,303391],[-191,-580],[-387,-2963],[106,-884]],[[268017,298964],[-130,-739]],[[267887,298225],[-343,99],[-450,1171]],[[267094,299495],[12,1605]],[[267106,301100],[49,2812]],[[189753,293991],[-1506,-3],[-6,-4849],[-1376,-19],[-349,1601],[-5,-14590]],[[186511,276131],[-3149,-35]],[[183362,276096],[0,16245]],[[267106,301100],[-728,2195]],[[238742,303127],[-74,-8950]],[[238668,294177],[-668,109]],[[238000,294286],[-287,2381]],[[237713,296667],[-388,1098],[22,1529]],[[256860,300492],[-290,-2960],[-338,-792],[-245,-1877]],[[255987,294863],[-144,1386],[-293,274]],[[255550,296523],[0,1620],[-291,54],[-9,3268],[-292,887]],[[268863,303636],[139,-2861],[323,-3555]],[[269325,297220],[-471,-581]],[[268854,296639],[-87,-160]],[[268767,296479],[-371,1042],[-216,-421],[-163,1864]],[[227521,302358],[-8,-8969]],[[227513,293389],[-1201,1]],[[226312,293390],[-170,620]],[[259335,300992],[56,-1316],[-280,-1720]],[[259111,297956],[-212,-1709],[70,-887],[-508,-2769]],[[258461,292591],[2,2075],[-386,811]],[[258077,295477],[-160,4082]],[[247114,300419],[-4,-8971]],[[247110,291448],[-285,-7],[1,-1091],[-449,-9]],[[246377,290341],[-133,6],[0,4822]],[[270014,298222],[-538,-798]],[[269476,297424],[-151,-204]],[[271631,300476],[-1005,-5256]],[[270626,295220],[-44,238]],[[270582,295458],[-242,963]],[[265023,299329],[-257,-2572],[-190,585]],[[264576,297342],[-271,24]],[[264305,297366],[-192,10]],[[260085,295668],[2,547],[-556,17],[-158,1641],[-262,83]],[[260826,302839],[-296,-1218],[-7,-4236],[-438,-1717]],[[267094,299495],[-351,-1180]],[[266743,298315],[-277,2222]],[[266466,300537],[-393,1648]],[[261226,302102],[133,-4629]],[[261359,297473],[96,-3170]],[[261455,294303],[-945,242]],[[260510,294545],[-581,52]],[[259929,294597],[156,1071]],[[235148,298399],[-1,-6962]],[[235147,291437],[-172,647]],[[234975,292084],[-223,645],[-660,-165]],[[234092,292564],[-504,-212]],[[233588,292352],[-6,49]],[[233582,292401],[-8,9260]],[[239723,299780],[-33,-6035]],[[239690,293745],[-694,-157]],[[238996,293588],[-328,589]],[[230599,298825],[-15,-6105]],[[230584,292720],[-1252,143]],[[229332,292863],[-306,203]],[[229026,293066],[-2,9917]],[[229026,293066],[-1224,262]],[[227802,293328],[-289,61]],[[237713,296667],[-606,-1073]],[[237107,295594],[-320,702],[-637,-837],[-277,1730]],[[276580,301791],[-335,-2182],[7,-1181],[-302,-3863]],[[275950,294565],[-336,-991]],[[275614,293574],[-478,39],[-285,2097]],[[274851,295710],[-106,1590]],[[171727,294716],[-239,-2194]],[[171488,292522],[-527,2873],[-607,2124],[-309,1676]],[[233582,292401],[-516,1393],[-745,742],[-269,-626]],[[232052,293910],[31,7042]],[[266466,300537],[-488,-4110]],[[265978,296427],[-652,2740]],[[265326,299167],[-187,782]],[[255550,296523],[-579,144],[-98,-1641],[-485,59]],[[254388,295085],[-295,-15]],[[262197,300429],[-36,-1811],[-802,-1145]],[[249746,298752],[0,-823]],[[249746,297929],[-780,-17]],[[248966,297912],[-952,5]],[[248014,297917],[-24,832]],[[278136,299831],[-261,-1729],[-65,-2062],[-295,-1492],[-382,-3555]],[[277133,290993],[-221,469],[-599,3177],[-363,-74]],[[232052,293910],[-3,-968]],[[232049,292942],[-1216,-1076]],[[230833,291866],[-249,854]],[[274851,295710],[-156,131],[-298,-1760],[-415,2481],[-367,85],[-287,1830]],[[272925,297467],[-517,-3023]],[[272408,294444],[-1070,-5442]],[[271338,289002],[-265,919]],[[271073,289921],[-257,2318],[43,1727],[-233,1254]],[[258077,295477],[-439,-1478],[-435,-2504],[-548,-1594]],[[256655,289901],[-109,9]],[[256546,289910],[-267,1112],[-470,3450],[178,391]],[[220521,300877],[16,-8166]],[[220537,292711],[-1467,10]],[[219070,292721],[-4,8150]],[[221976,298976],[5,-6234]],[[221981,292742],[-1444,-31]],[[224910,294004],[2,-1315]],[[224912,292689],[-1486,26]],[[223426,292715],[2,8125]],[[219070,292721],[-1453,-13]],[[217617,292708],[-6,8125]],[[217617,292708],[-1443,-45]],[[216174,292663],[-19,8110]],[[223426,292715],[-1445,27]],[[216174,292663],[-1446,-96]],[[214728,292567],[-27,8100]],[[213225,300675],[57,-8129]],[[213282,292546],[-1287,-1]],[[211995,292545],[11,3391]],[[214728,292567],[-1446,-21]],[[241176,300248],[-12,-1321],[-518,-4118],[192,-1475],[382,-1184]],[[241220,292150],[-1146,188]],[[240074,292338],[-391,56],[7,1351]],[[266743,298315],[382,-3067],[-30,-933]],[[267095,294315],[-707,-913]],[[266388,293402],[-414,1708]],[[265974,295110],[4,1317]],[[262719,294837],[-459,-1602]],[[262260,293235],[-776,48]],[[261484,293283],[-29,1020]],[[262577,299847],[-12,-3859],[270,14]],[[248014,297917],[76,-4060],[-205,-780],[1,-1558]],[[247886,291519],[-437,-567]],[[247449,290952],[-227,-1245],[-112,1741]],[[252513,297417],[69,-2403],[-508,-277]],[[252074,294737],[0,1098],[-968,-56]],[[251106,295779],[-44,3301]],[[241878,298491],[54,-3825],[-183,-2389],[75,-1398]],[[241824,290879],[-230,-523],[-374,1794]],[[244323,295808],[-11,-3253]],[[244312,292555],[-1484,172]],[[242828,292727],[39,5700]],[[264965,295106],[-389,2236]],[[265326,299167],[-46,-1734],[-315,-2327]],[[279011,295965],[-403,-3616],[-92,-2363],[-262,1132],[231,-2620],[-212,-589],[-375,805]],[[277898,288714],[-123,574]],[[277775,289288],[-642,1705]],[[195305,289204],[0,-8032]],[[195305,281172],[-1,-6537]],[[195304,274635],[-186,-4]],[[195118,274631],[-312,2178],[43,3573],[-198,582],[-596,5810],[0,10694]],[[267887,298225],[124,-661],[-175,-1445],[3,-2070]],[[267839,294049],[-421,-2128]],[[267418,291921],[-323,2394]],[[253179,295255],[-83,-4574]],[[253096,290681],[-1016,-26]],[[252080,290655],[-6,4082]],[[265974,295110],[21,-726],[-419,-926],[-105,-1288]],[[265471,292170],[-170,1415]],[[265301,293585],[-336,1521]],[[251106,295779],[-194,-540]],[[250912,295239],[-465,-210],[-350,-1146]],[[250097,293883],[-350,16],[-1,4030]],[[268767,296479],[-104,-668]],[[268663,295811],[-273,-796],[-103,-1445]],[[268287,293570],[-448,479]],[[237107,295594],[1,-5232]],[[237108,290362],[-341,757],[-196,-1322],[-639,965],[-521,-443]],[[235411,290319],[-264,1118]],[[275614,293574],[-88,-2688],[-301,-779]],[[275225,290107],[-353,382],[-40,1051],[-800,-2860]],[[274032,288680],[-22,-72]],[[274010,288608],[-398,2352],[-785,2545]],[[272827,293505],[-419,939]],[[242828,292727],[22,-1854],[-368,-1418],[-65,-1107]],[[242417,288348],[-241,1667],[-352,864]],[[270582,295458],[-741,-3541]],[[269841,291917],[-395,3623],[30,1884]],[[259929,294597],[-147,0],[-211,-3806],[-383,38],[-157,-3571]],[[259031,287258],[-882,-28]],[[258149,287230],[-35,1334],[210,2007],[198,282],[-61,1738]],[[250097,293883],[-203,-505],[0,-2741]],[[249894,290637],[-534,-9]],[[249360,290628],[-4,2180],[-385,256],[-5,4848]],[[249360,290628],[-290,-1321]],[[249070,289307],[-1184,2212]],[[269841,291917],[-165,-789]],[[269676,291128],[-4,-5]],[[269672,291123],[-80,1]],[[269592,291124],[-424,845],[-314,4670]],[[195118,274631],[-3725,22]],[[191393,274653],[0,1610]],[[191393,276263],[7,12672]],[[264305,297366],[79,-1215],[-282,-4274]],[[264102,291877],[-95,1]],[[264007,291878],[19,2058],[-215,1635]],[[265301,293585],[-113,-1287],[-385,-1434]],[[264803,290864],[-134,698],[-567,315]],[[238000,294286],[55,-4237],[-203,-2132],[-208,87],[128,-2331]],[[237772,285673],[-29,1]],[[237743,285674],[-635,-3]],[[237108,285671],[0,4691]],[[269592,291124],[-134,-694]],[[269458,290430],[-356,976],[-547,-1372]],[[268555,290034],[-268,3536]],[[256546,289910],[-601,-2119],[-102,-2363],[-288,-51]],[[255555,285377],[-818,63]],[[254737,285440],[-326,195],[-14,2539]],[[254397,288174],[-9,6911]],[[211995,292545],[-22,-7993]],[[211973,284552],[0,-8133]],[[211973,276419],[1,-8101]],[[211974,268318],[0,-1611],[-731,-3]],[[211243,266704],[-1104,-3]],[[210139,266701],[0,9724],[-255,-1],[0,8241]],[[252080,290655],[-401,-9]],[[251679,290646],[-766,-8]],[[250913,290638],[-1,4601]],[[244988,295164],[-59,-1673],[289,734],[-26,-3356],[151,-2943],[-317,-598],[225,-984],[-128,-820]],[[245123,285524],[0,-134]],[[245123,285390],[-271,18]],[[244852,285408],[-481,19]],[[244371,285427],[-67,-3]],[[244304,285424],[8,7131]],[[264007,291878],[-125,-1789],[-179,18]],[[263703,290107],[-245,1867],[-67,2695]],[[258149,287230],[-73,-1530]],[[258076,285700],[-260,915],[-756,37],[2,-410]],[[257062,286242],[-403,2158],[-4,1501]],[[271073,289921],[-669,-319],[-229,1236],[-260,-546]],[[269915,290292],[-148,616]],[[269767,290908],[-91,220]],[[254397,288174],[-931,-2937]],[[253466,285237],[-470,-87]],[[252996,285150],[100,5531]],[[250913,290638],[-58,-1638],[-291,-9],[-3,-1640],[-289,-10]],[[250272,287341],[5,3289],[-383,7]],[[246377,290341],[40,-3234]],[[246417,287107],[-592,20],[0,-1670]],[[245825,285457],[-702,67]],[[266388,293402],[-34,-4888]],[[266354,288514],[-753,-747]],[[265601,287767],[-17,901]],[[265584,288668],[-113,3502]],[[263703,290107],[-14,-676]],[[263689,289431],[-975,44],[-28,-604]],[[262686,288871],[-213,626]],[[262473,289497],[-213,3738]],[[277775,289288],[-200,-1210],[-345,-446],[-431,-3536]],[[276799,284096],[-357,-1958],[-73,1501]],[[276369,283639],[-170,-185],[-119,1854],[-263,409]],[[275817,285717],[-479,2444],[-113,1946]],[[175704,293265],[8,-6545],[-64,-14],[-11,-8498]],[[175637,278208],[-2838,-1564],[17,2580],[-320,834],[-131,1949],[82,971],[-206,4490],[-532,4208],[-221,846]],[[261484,293283],[160,-5557]],[[261644,287726],[11,-400]],[[261655,287326],[-1005,-14]],[[260650,287312],[-168,-13]],[[260482,287299],[28,7246]],[[260482,287299],[-991,-305]],[[259491,286994],[-460,264]],[[233588,292352],[-6,-55]],[[233582,292297],[-854,-556],[-398,-900],[-289,-1444]],[[232041,289397],[8,3545]],[[272827,293505],[-15,-5200],[98,-784]],[[272910,287521],[-478,-195],[-495,933],[-201,-1171]],[[271736,287088],[-363,872],[-35,1042]],[[267418,291921],[45,-836],[-297,-951],[-55,-1334]],[[267111,288800],[-431,-35]],[[266680,288765],[-326,-251]],[[202831,292589],[15,-6292]],[[202846,286297],[-1525,-4281],[1,-811],[-1146,18],[-2,-3253]],[[200174,277970],[-860,-3]],[[199314,277967],[-358,449],[43,2766],[-165,3253],[-161,392],[-104,4258],[-348,-19]],[[238996,293588],[-214,-3466],[-111,31],[-24,-4496]],[[238647,285657],[-85,2]],[[238562,285659],[-790,14]],[[268555,290034],[21,-284]],[[268576,289750],[-391,-1856],[-455,-1079]],[[267730,286815],[-127,1309],[-492,676]],[[183362,276096],[1,-8676]],[[183363,267420],[-4125,8475],[13,2284],[251,1898]],[[179502,280077],[536,715],[179,2807],[-146,2209]],[[191393,276263],[-1966,-35],[0,-192],[-2916,95]],[[226312,293390],[-10,-8052]],[[226302,285338],[-375,38]],[[225927,285376],[-1032,85]],[[224895,285461],[17,7228]],[[240074,292338],[-28,-6700]],[[240046,285638],[-697,10]],[[239349,285648],[-702,9]],[[265584,288668],[-611,418]],[[264973,289086],[-229,4]],[[264744,289090],[59,1774]],[[274010,288608],[-793,-2802]],[[273217,285806],[-307,1715]],[[179502,280077],[-3865,-1869]],[[227802,293328],[-42,-8197]],[[227760,285131],[-406,73]],[[227354,285204],[-1052,134]],[[229332,292863],[-29,-7782]],[[229303,285081],[-523,-53]],[[228780,285028],[-1020,103]],[[262473,289497],[-829,-1771]],[[232039,284934],[-231,5],[-115,-1982],[146,-674]],[[231839,282283],[-397,23]],[[231442,282306],[-615,61]],[[230827,282367],[1,2606]],[[230828,284973],[5,6893]],[[232041,289397],[-2,-4463]],[[230828,284973],[-612,11]],[[230216,284984],[-913,97]],[[219070,292721],[-5,-8098]],[[219065,284623],[-381,10]],[[218684,284633],[-1066,127]],[[217618,284760],[-1,7948]],[[220537,292711],[5,-8140]],[[220542,284571],[-432,-2]],[[220110,284569],[-1045,54]],[[234975,292084],[-31,-7088]],[[234944,284996],[-292,1575],[-561,-613]],[[234091,285958],[1,6606]],[[217618,284760],[-376,-125]],[[217242,284635],[-1070,-47]],[[216172,284588],[2,8075]],[[223426,292715],[8,-8204]],[[223434,284511],[-404,2]],[[223030,284513],[-1045,0]],[[221985,284513],[-4,8229]],[[224895,285461],[-2,-1035],[-413,38]],[[224480,284464],[-1046,47]],[[221985,284513],[-393,-1]],[[221592,284512],[-1050,59]],[[244304,285424],[-1696,42]],[[242608,285466],[-191,2882]],[[216172,284588],[-374,14]],[[215798,284602],[-1072,-38]],[[214726,284564],[2,8003]],[[206995,276387],[10,-9680]],[[207005,266707],[-197,-1]],[[206808,266706],[-3007,34]],[[203801,266740],[-1057,-19]],[[202744,266721],[2,17810],[100,1766]],[[213282,292546],[0,-7999]],[[213282,284547],[-1309,5]],[[214726,284564],[-368,-14]],[[214358,284550],[-1076,-3]],[[234091,285958],[-73,-393]],[[234018,285565],[-60,-961],[-377,12]],[[233581,284616],[1,7681]],[[242608,285466],[-1828,115]],[[240780,285581],[-734,57]],[[233581,284616],[-993,-39]],[[232588,284577],[-549,357]],[[235411,290319],[-5,-7248]],[[235406,283071],[-145,-6]],[[235261,283065],[-39,471]],[[235222,283536],[-278,1460]],[[249070,289307],[-206,-1883],[-209,-3399],[-189,-953]],[[248466,283072],[-1108,2439]],[[247358,285511],[-151,1938],[245,380],[-3,3123]],[[264744,289090],[-346,-257]],[[264398,288833],[-567,369],[-126,-463]],[[263705,288739],[-16,692]],[[275817,285717],[197,-1750],[-222,-2037],[-677,747]],[[275115,282677],[4,3611],[-520,96],[-567,2296]],[[247358,285511],[-237,-29],[-3,-1592],[-571,-19]],[[246547,283871],[-8,3240],[-122,-4]],[[269458,290430],[-641,-2764]],[[268817,287666],[-197,886]],[[268620,288552],[-44,1198]],[[269915,290292],[98,-679],[-163,-3128],[52,-2328],[-122,-1899]],[[269780,282258],[-329,-1370]],[[269451,280888],[-242,1119]],[[269209,282007],[-81,3665],[-311,1994]],[[237108,285671],[0,-2571]],[[237108,283100],[-1702,-29]],[[271736,287088],[202,-924]],[[271938,286164],[-630,-2537]],[[271308,283627],[-251,828],[-618,-687],[-182,-1942]],[[270257,281826],[-477,432]],[[252996,285150],[-21,-1156]],[[252975,283994],[-1295,-59]],[[251680,283935],[-1,6711]],[[251680,283935],[-283,11]],[[251397,283946],[-1128,96]],[[250269,284042],[3,3299]],[[250269,284042],[-1132,-33],[-12,-840]],[[249125,283169],[-659,-97]],[[257062,286242],[14,-3976],[-399,14]],[[256677,282280],[-836,-103],[-284,800]],[[255557,282977],[-2,2400]],[[269209,282007],[-689,-991],[-95,755],[-402,-936]],[[268023,280835],[-353,3484]],[[267670,284319],[60,2496]],[[263705,288739],[-84,-3973]],[[263621,284766],[55,-1652]],[[263676,283114],[-539,-692]],[[263137,282422],[-449,518]],[[262688,282940],[-2,5931]],[[262688,282940],[-898,-38]],[[261790,282902],[-135,4424]],[[199314,277967],[-878,-19],[0,-1622],[-852,-5],[-2,-8135],[36,-4019]],[[197618,264167],[-857,-7],[-37,4023],[2,8133],[-306,3],[2,1614],[-576,1],[-5,3232],[-536,6]],[[264398,288833],[-62,-3686]],[[264336,285147],[-715,-381]],[[264973,289086],[-7,-4742],[-221,-294]],[[264745,284050],[2,1070],[-411,27]],[[265601,287767],[265,-1986],[29,-1346]],[[265895,284435],[-506,-1945]],[[265389,282490],[-646,23]],[[264743,282513],[2,1537]],[[267670,284319],[-205,1161],[-587,-1542]],[[266878,283938],[-198,4827]],[[266878,283938],[-137,-519]],[[266741,283419],[-297,-993]],[[266444,282426],[-403,799],[-146,1210]],[[275115,282677],[-128,-2424],[77,-2306]],[[275064,277947],[21,-1799],[-453,1073]],[[274632,277221],[-397,2101],[-304,487]],[[273931,279809],[-206,2389],[-508,3608]],[[273217,285806],[-382,-1361],[-117,-2128],[-399,-1751]],[[272319,280566],[-269,3521],[-24,1416]],[[272026,285503],[-88,661]],[[254737,285440],[-323,-2639],[122,-3923],[141,-1192],[-210,-1223]],[[254467,276463],[-114,140]],[[254353,276603],[-184,1820],[-374,-689],[-62,3380],[-367,2778],[100,1345]],[[261790,282902],[136,-2308]],[[261926,280594],[-1276,-334]],[[260650,280260],[0,7052]],[[260650,280260],[-288,-579],[2,-1890]],[[260364,277791],[-286,-278],[4,-1615],[-248,-23]],[[259834,275875],[-17,4870],[-320,8]],[[259497,280753],[-6,6241]],[[259497,280753],[-1025,-27]],[[258472,280726],[-231,1150],[-165,3824]],[[246547,283871],[-292,-1615],[176,-342],[-73,-2890]],[[246358,279024],[-674,12],[3,3263],[138,3158]],[[235222,283536],[-1204,-38]],[[234018,283498],[0,2067]],[[258472,280726],[-106,-860]],[[258366,279866],[-839,-31],[-2,-819],[-563,45]],[[256962,279061],[-281,-16],[0,1226]],[[256681,280271],[-4,2009]],[[202744,266721],[-672,-16],[-49,-2500],[301,-1531]],[[202324,262674],[-2142,-5]],[[200182,262669],[-8,15301]],[[272319,280566],[44,-2784]],[[272363,277782],[-442,-1977]],[[271921,275805],[-595,2074]],[[271326,277879],[-222,903]],[[271104,278782],[-65,751],[269,4094]],[[273931,279809],[-121,-807]],[[273810,279002],[-90,-725],[-314,2439],[-742,-3661]],[[272664,277055],[-301,727]],[[248466,283072],[-231,-2682],[-557,-1363]],[[247678,279027],[-560,-1640]],[[247118,277387],[-289,-1241]],[[246829,276146],[-463,2072]],[[246366,278218],[-8,806]],[[238701,271113],[-400,6]],[[238301,271119],[-149,1513],[-322,444],[-467,-2738],[-254,8]],[[237109,270346],[-1,3653]],[[237108,273999],[0,5600]],[[237108,279599],[0,3501]],[[237743,285674],[-73,-1364],[157,-1652],[-2,-3964],[475,-5512],[401,-2069]],[[238562,285659],[194,-3036],[29,-8285],[191,-5]],[[238976,274333],[-156,-3244]],[[238820,271089],[-119,24]],[[239516,277585],[89,-2415],[-250,-839],[-379,2]],[[239349,285648],[4,-5606],[141,-1],[22,-2456]],[[240779,280837],[-287,-14],[-146,-3236]],[[240346,277587],[-830,-2]],[[240780,285581],[-1,-4744]],[[242619,280157],[-575,-2639],[-400,30]],[[241644,277548],[0,1623],[-290,937],[-575,729]],[[242608,285466],[11,-5309]],[[234018,283498],[-3,-6187]],[[234015,277311],[-813,788]],[[233202,278099],[-417,1389]],[[232785,279488],[-113,619]],[[232672,280107],[-84,4470]],[[246366,278218],[-28,-989],[-363,176],[-2,-2478],[-251,42],[-36,2444],[-221,12]],[[245465,277425],[-297,1279],[258,1420],[-227,256],[-7,1864],[186,934],[-62,1882],[-224,-1603],[31,1933]],[[268023,280835],[-29,-951]],[[267994,279884],[-750,-2311]],[[267244,277573],[-503,5846]],[[244371,285427],[-163,-2146],[-308,-2022],[-90,-2138]],[[243810,279121],[-368,-2107],[-400,-899]],[[243042,276115],[-26,3010],[-397,1032]],[[255557,282977],[-143,-816],[-4,-3254],[-143,-3],[2,-3233]],[[255269,275671],[-569,-21],[-233,813]],[[244852,285408],[-216,-2699],[-7,-2066],[-283,-3112]],[[244346,277531],[-420,5],[-116,1585]],[[225927,285376],[-30,-8275]],[[225897,277101],[-4,-879]],[[225893,276222],[-1134,26]],[[224759,276248],[-281,39]],[[224478,276287],[2,8177]],[[245465,277425],[-76,-261]],[[245389,277164],[-175,-453],[-903,-2]],[[244311,276709],[35,822]],[[227354,285204],[-18,-8167]],[[227336,277037],[-186,1]],[[227150,277038],[-1253,63]],[[264743,282513],[-219,-2968]],[[264524,279545],[-235,1092]],[[264289,280637],[-264,580],[-349,1897]],[[254353,276603],[-498,-1834],[-40,-1059],[323,-1231]],[[254138,272479],[-1368,-35]],[[252770,272444],[91,5015]],[[252861,277459],[114,6535]],[[228780,285028],[-17,-8118]],[[228763,276910],[-135,16]],[[228628,276926],[-1292,111]],[[230216,284984],[-6,-3141]],[[230210,281843],[-31,-4995]],[[230179,276848],[-1416,62]],[[230827,282367],[0,-513],[-617,-11]],[[232672,280107],[-600,939],[-233,1237]],[[218684,284633],[-13,-8156]],[[218671,276477],[-1432,43]],[[217239,276520],[3,8115]],[[210139,266701],[-717,-2]],[[209422,266699],[-123,-1]],[[209299,266698],[-2294,9]],[[220110,284569],[-7,-8139]],[[220103,276430],[-1432,47]],[[217239,276520],[0,-66]],[[217239,276454],[-1432,21]],[[215807,276475],[-9,8127]],[[215807,276475],[-1432,-37]],[[214375,276438],[-17,8112]],[[214375,276438],[-25,-1]],[[214350,276437],[-2377,-18]],[[221592,284512],[0,-8234]],[[221592,276278],[-49,0]],[[221543,276278],[-1440,152]],[[223030,284513],[0,-8233]],[[223030,276280],[-51,1]],[[222979,276281],[-1387,-3]],[[224478,276287],[-1448,-7]],[[271104,278782],[-447,-786]],[[270657,277996],[-226,1046],[-174,2784]],[[266444,282426],[-237,-2443]],[[266207,279983],[2,-920]],[[266209,279063],[-288,519]],[[265921,279582],[-345,931],[-187,1977]],[[251397,283946],[0,-6508]],[[251397,277438],[-1123,-9]],[[250274,277429],[-5,6613]],[[250274,277429],[-14,-1]],[[250260,277428],[-1136,15],[0,1061]],[[249124,278504],[1,4665]],[[252861,277459],[-1464,-21]],[[235261,283065],[11,-1603]],[[235272,281462],[1,-2618]],[[235273,278844],[-795,-2143]],[[234478,276701],[-463,610]],[[267244,277573],[-756,-2456]],[[266488,275117],[-124,3123],[-155,823]],[[264289,280637],[-142,-524],[-197,-2955],[-99,3]],[[263851,277161],[-538,-506]],[[263313,276655],[-159,-301]],[[263154,276354],[0,1215]],[[263154,277569],[-17,4853]],[[237108,279599],[-281,902],[-86,-872],[-299,1007],[-286,-1037],[-341,13],[-543,1850]],[[249124,278504],[-151,-881]],[[248973,277623],[-205,-34],[-580,-3455]],[[248188,274134],[-494,8],[0,1631],[-576,8],[0,1606]],[[256681,280271],[-69,-2498],[-191,-1960],[-869,-125],[4,-3255],[-143,-9]],[[255413,272424],[-143,-6],[-1,3253]],[[263154,277569],[-1076,448]],[[262078,278017],[-152,2577]],[[265921,279582],[1,-28]],[[265922,279554],[-184,35],[-659,-3027]],[[265079,276562],[-284,2688],[-271,295]],[[231442,282306],[2,-8953]],[[231444,273353],[-1049,21]],[[230395,273374],[-216,3474]],[[270657,277996],[-80,-1269]],[[270577,276727],[-329,-341],[-291,-3111],[55,-643]],[[270012,272632],[0,-5]],[[270012,272627],[-338,-491],[-153,1148]],[[269521,273284],[74,1606],[-301,1073],[-437,275]],[[268857,276238],[507,2500],[87,2150]],[[232785,279488],[1,-3858],[404,-2322]],[[233190,273308],[-1746,45]],[[268857,276238],[-59,1856],[-542,1927],[-262,-137]],[[237108,273999],[-848,-1143],[-402,1183]],[[235858,274039],[-244,1]],[[235614,274040],[-342,532],[1,4272]],[[197618,264167],[24,-1497]],[[197642,262670],[0,-8384],[-2346,-23]],[[195296,254263],[8,20372]],[[259834,275875],[18,-769],[-399,-590]],[[259453,274516],[-479,386],[-118,1013],[-486,-1586]],[[258370,274329],[-4,5537]],[[241644,277548],[-1,-1627]],[[241643,275921],[-577,18],[2,-799],[-433,-2]],[[240635,275138],[-285,12],[-4,2437]],[[273810,279002],[98,-1654]],[[273908,277348],[8,-2098]],[[273916,275250],[-11,-827]],[[273905,274423],[15,-90]],[[273920,274333],[-20,-184]],[[273900,274149],[-60,-392]],[[273840,273757],[-438,-2518],[175,-1903]],[[273577,269336],[17,-224]],[[273594,269112],[-42,-1026],[-431,800]],[[273121,268886],[-76,2024]],[[273045,270910],[44,2085],[-299,3226]],[[272790,276221],[-126,834]],[[265079,276562],[-41,-437]],[[265038,276125],[-259,143],[-399,-2644]],[[264380,273624],[-384,781]],[[263996,274405],[-145,2756]],[[262078,278017],[222,-1817]],[[262300,276200],[-162,-699],[-765,-79],[10,-1076],[-289,-21]],[[261094,274325],[-155,1623],[-575,1843]],[[256962,279061],[29,-2256],[255,-3763]],[[257246,273042],[-260,-2156],[4,-3296]],[[256990,267590],[-755,-8],[-207,1771],[-614,2276]],[[255414,271629],[-1,795]],[[243042,276115],[-252,-2320],[-86,-1940]],[[242704,271855],[-773,7]],[[241931,271862],[-288,2425],[0,1634]],[[268650,272453],[-454,-2106]],[[268196,270347],[-296,-908],[-412,5134]],[[267488,274573],[-244,3000]],[[268857,276238],[-273,-3365],[66,-420]],[[258370,274329],[-238,-1220]],[[258132,273109],[-630,1133],[-256,-1200]],[[274213,271739],[154,-1026],[-395,-1964],[241,2990]],[[273840,273757],[261,-1594],[-185,-2612],[-322,-439]],[[274414,275981],[264,-153],[280,-1524],[-294,-990],[-167,-1798],[-139,1959],[56,2506]],[[274160,276707],[170,-315],[46,-4156],[-277,1435],[-122,1906],[183,1130]],[[274632,277221],[33,-1171],[-536,816],[-157,-984],[-64,1466]],[[265516,275419],[-478,706]],[[265922,279554],[-53,-2957],[-353,-1178]],[[266488,275117],[0,-942]],[[266488,274175],[-328,-2106]],[[266160,272069],[-649,44]],[[265511,272113],[5,3306]],[[234478,276701],[5,-3100]],[[234483,273601],[0,-4335]],[[234483,269266],[-1322,-45]],[[233161,269221],[-81,1800],[110,2287]],[[244311,276709],[-53,-2458]],[[244258,274251],[-441,-8],[-351,-1303],[-29,-1625],[-344,-1798]],[[243093,269517],[-234,272],[-155,2066]],[[235614,274040],[-175,-434],[-956,-5]],[[271921,275805],[37,-1771],[275,-2845]],[[272233,271189],[-961,-1642]],[[271272,269547],[-98,1564],[-427,591]],[[270747,271702],[140,2717],[-310,2308]],[[250260,277428],[-1,-6571]],[[250259,270857],[-1135,-31]],[[249124,270826],[0,2438],[-151,4359]],[[246829,276146],[-140,-527],[-67,-2600],[-183,267],[-97,-2398]],[[246342,270888],[-114,-1301],[-411,-1386],[-315,624]],[[245502,268825],[-3,115]],[[245499,268940],[-61,70]],[[245438,269010],[-315,191],[118,1426]],[[245241,270627],[337,223],[252,2167],[-256,411],[68,1606],[-306,122],[53,2008]],[[263154,276354],[-211,-1787],[-562,-838]],[[262381,273729],[-81,2471]],[[200182,262669],[-2540,1]],[[273045,270910],[-142,209],[-547,-2637]],[[272356,268482],[-123,2707]],[[261094,274325],[1,-3262]],[[261095,271063],[-1173,-46],[-180,788]],[[259742,271805],[-224,1640]],[[259518,273445],[-65,1071]],[[249124,270826],[0,-3253]],[[249124,267573],[-1393,50]],[[247731,267623],[-42,2293],[499,4218]],[[240635,275138],[6,-4081],[-110,-1625]],[[240531,269432],[-347,19]],[[240184,269451],[-691,9]],[[239493,269460],[-75,1632],[-598,-3]],[[267488,274573],[-96,548],[-482,-3351]],[[266910,271770],[-422,2405]],[[252770,272444],[-27,-1509]],[[252743,270935],[-1345,-64]],[[251398,270871],[-1,6567]],[[251398,270871],[-1139,-14]],[[247731,267623],[-1360,-26]],[[246371,267597],[-29,3291]],[[245241,270627],[-1027,-144]],[[244214,270483],[-103,1786],[147,1982]],[[263996,274405],[-106,-4613]],[[263890,269792],[-3,-599]],[[263887,269193],[-267,9],[-340,1832]],[[263280,271034],[-27,-2]],[[263253,271032],[60,5623]],[[270747,271702],[-157,187]],[[270590,271889],[-578,743]],[[227150,277038],[6,-4410]],[[227156,272628],[-469,-47],[-452,-1537]],[[226235,271044],[-342,5178]],[[228628,276926],[1,-5287]],[[228629,271639],[-1086,-1713]],[[227543,269926],[-107,-742],[-280,1303]],[[227156,270487],[0,2141]],[[230395,273374],[194,-555]],[[230589,272819],[-1430,-4745]],[[229159,268074],[-124,-408],[-406,3973]],[[255414,271629],[-142,-188],[4,-2273],[-284,-8],[4,-2361],[-126,-263]],[[254870,266536],[-1130,-23]],[[253740,266513],[-126,1159],[208,569],[68,3761],[248,477]],[[263253,271032],[-736,-38]],[[262517,270994],[98,589],[-330,1226],[96,920]],[[217239,276454],[-26,-8133]],[[217213,268321],[-225,-2]],[[216988,268319],[-1199,8]],[[215789,268327],[18,8148]],[[218671,276477],[-13,-8190]],[[218658,268287],[-436,22]],[[218222,268309],[-1009,12]],[[220103,276430],[-15,-8195]],[[220088,268235],[-232,-5]],[[219856,268230],[-1198,57]],[[215789,268327],[-226,-12]],[[215563,268315],[-1213,-2]],[[214350,268313],[0,8124]],[[214350,268313],[-210,4]],[[214140,268317],[-1427,-23]],[[212713,268294],[-739,24]],[[265511,272113],[-316,-757],[-183,-1474]],[[265012,269882],[-430,1091]],[[264582,270973],[-206,1238],[4,1413]],[[221543,276278],[-5,-8068]],[[221538,268210],[-231,14]],[[221307,268224],[-1219,11]],[[224759,276248],[2,-3983],[-213,-701]],[[224548,271564],[-1040,-3414]],[[223508,268150],[-541,29]],[[222967,268179],[12,8102]],[[226235,271044],[225,-2721]],[[226460,268323],[-392,-1293]],[[226068,267030],[-565,-1869]],[[225503,265161],[-119,1166],[-213,-724],[-623,5961]],[[222967,268179],[-215,-7]],[[222752,268172],[-1214,38]],[[191393,274653],[-1,-12960]],[[191392,261693],[-1985,-97],[1,-3801],[-565,-10],[0,-1773]],[[188843,256012],[-5480,11408]],[[269521,273284],[-686,-1046]],[[268835,272238],[-185,215]],[[262517,270994],[-386,-1847],[16,-1276]],[[262147,267871],[-360,-24],[-203,1604],[-425,-24]],[[261159,269427],[-64,1636]],[[259742,271805],[-223,-445],[6,-3713]],[[259525,267647],[5,-1554],[-546,-20]],[[258984,266073],[-309,-26],[-4,1611],[-286,-16]],[[258385,267642],[-7,3607],[-246,1860]],[[241931,271862],[-1,-2445]],[[241930,269417],[-1399,15]],[[268196,270347],[-120,-1132]],[[268076,269215],[-774,-4371]],[[267302,264844],[-222,1408],[-144,2418]],[[266936,268670],[128,1356],[-154,1744]],[[264582,270973],[-5,-1325],[-687,144]],[[195296,254263],[-3927,9]],[[191369,254272],[23,7421]],[[258385,267642],[-117,-1603]],[[258268,266039],[-1141,-46]],[[257127,265993],[-137,1597]],[[235858,274039],[-247,-3595],[-56,-4246],[248,15]],[[235803,266213],[165,-2415]],[[235968,263798],[-1351,24]],[[234617,263822],[-133,12],[-1,5432]],[[244214,270483],[8,-2105],[-240,-3867]],[[243982,264511],[-60,1623],[-510,-1824],[-304,1856]],[[243108,266166],[-15,3351]],[[266936,268670],[-756,231]],[[266180,268901],[-20,3168]],[[237109,270346],[74,-4024]],[[237183,266322],[-1380,-109]],[[233161,269221],[87,-951]],[[233248,268270],[-1739,-1463]],[[231509,266807],[-6,123]],[[231503,266930],[-162,2701],[-752,3188]],[[269315,266119],[-168,-187]],[[269147,265932],[-152,1040],[-160,5266]],[[269521,273284],[-206,-7165]],[[270012,272627],[143,-2843],[-121,-4705]],[[270034,265079],[-575,985]],[[269459,266064],[-144,55]],[[238301,271119],[215,-818],[-12,-1664],[522,-3202]],[[239026,265435],[-240,-345],[-1,-1274]],[[238785,263816],[-1220,-19]],[[237565,263797],[-382,2525]],[[231503,266930],[-1231,-4027]],[[230272,262903],[-620,348]],[[229652,263251],[-493,4823]],[[227156,270487],[-696,-2164]],[[270590,271889],[127,-3676],[608,-623]],[[271325,267590],[-175,-617]],[[271150,266973],[-403,-4199]],[[270747,262774],[-221,704]],[[270526,263478],[-177,1370],[-315,231]],[[253740,266513],[-294,-3347],[251,-2068]],[[253697,261098],[-1046,-26]],[[252651,261072],[-26,3649]],[[252625,264721],[118,6214]],[[269147,265932],[-285,-745]],[[268862,265187],[-233,1642],[-442,1367],[-111,1019]],[[261159,269427],[-50,-2453],[-440,-327],[-199,-2179]],[[260470,264468],[-372,2],[4,1617],[-263,0],[-26,1492],[-288,68]],[[266180,268901],[-4,-1686]],[[266176,267215],[-979,52]],[[265197,267267],[-144,1878]],[[265053,269145],[-41,737]],[[271272,269547],[173,-1188]],[[271445,268359],[-120,-769]],[[243108,266166],[-323,-822]],[[242785,265344],[-856,1]],[[241929,265345],[1,4072]],[[257127,265993],[-143,-9],[6,-2440]],[[256990,263544],[-1656,-27]],[[255334,263517],[-331,-38],[-133,3057]],[[229652,263251],[-593,-1974]],[[229059,261277],[-293,2871],[-669,-2188]],[[228097,261960],[2,1607],[-453,1892],[173,815],[-272,611],[-4,3041]],[[225503,265161],[144,-1412],[-855,-2935]],[[224792,260814],[-82,738],[-489,-431]],[[224221,261121],[-713,7029]],[[245438,269010],[61,-70]],[[245502,268825],[-127,-1815],[-313,-1032],[-168,-1704]],[[244894,264274],[-270,-484],[77,-1752]],[[244701,262038],[-172,-287]],[[244529,261751],[-462,332]],[[244067,262083],[-85,2428]],[[272356,268482],[444,-3810]],[[272800,264672],[248,-708],[-50,-2336],[-442,1524]],[[272556,263152],[-244,2510],[-518,506],[-349,2191]],[[239493,269460],[179,-3272],[-322,2],[-3,-1288],[-321,533]],[[273103,263918],[241,-620],[-261,-1764],[-124,1738],[144,646]],[[273429,267956],[229,-1123],[-211,-968],[-18,2091]],[[273121,268886],[209,-711],[80,-2196],[219,-966],[-158,-953],[-438,1415],[-233,-803]],[[263887,269193],[-34,-3123]],[[263853,266070],[-27,-877],[-408,18]],[[263418,265211],[-156,2]],[[263262,265213],[18,5821]],[[263262,265213],[-700,78]],[[262562,265291],[-449,1252]],[[262113,266543],[34,1328]],[[265197,267267],[107,-2252]],[[265304,265015],[-1157,121],[0,-794]],[[264147,264342],[-294,1728]],[[252625,264721],[-414,-683],[-803,-568]],[[251408,263470],[-10,7401]],[[246371,267597],[-50,-4882]],[[246321,262715],[-773,1410],[-654,149]],[[251408,263470],[-93,-39]],[[251315,263431],[-1039,-418]],[[250276,263013],[-17,7844]],[[250276,263013],[-235,-98]],[[250041,262915],[-702,-302]],[[249339,262613],[-25,4964],[-190,-4]],[[228097,261960],[-913,-2938]],[[227184,259022],[-236,2295],[-214,-718]],[[226734,260599],[-666,6431]],[[268862,265187],[-531,-2011]],[[268331,263176],[-444,-556]],[[267887,262620],[-507,1245]],[[267380,263865],[-78,979]],[[241929,265345],[-140,-2434]],[[241789,262911],[-714,10],[-2,-1631],[-985,-12]],[[240088,261278],[64,2739],[215,1718],[-183,3716]],[[240088,261278],[187,-1588],[292,-495],[230,-1479]],[[240797,257716],[-541,-2780],[-190,-403]],[[240066,254533],[-710,332]],[[239356,254865],[-1,3236],[-281,-4],[-5,3280],[-287,5],[3,2434]],[[262113,266543],[-202,-2490],[-1,-1443]],[[261910,262610],[38,-331]],[[261948,262279],[-247,-1122],[-557,78],[-1,-1623]],[[261143,259612],[-925,-26]],[[260218,259586],[89,792]],[[260307,260378],[163,4090]],[[234617,263822],[-104,-1184],[69,-2704],[234,-2044]],[[234816,257890],[-383,-1877]],[[234433,256013],[-270,786],[-111,1767],[-372,551]],[[233680,259117],[-7,1203],[-351,1944],[-118,1559],[44,4447]],[[267380,263865],[-842,-11]],[[266538,263854],[-369,126]],[[266169,263980],[7,3235]],[[273082,261033],[-133,-2581],[-38,2028],[171,553]],[[272467,260258],[-390,845]],[[272077,261103],[-418,1230],[-343,2022],[41,1842],[-207,776]],[[272556,263152],[364,-1898],[-453,-996]],[[218222,268309],[-10,-7269]],[[218212,261040],[-112,-2492],[-1121,-109]],[[216979,258439],[2,1757]],[[216981,260196],[7,8123]],[[216981,260196],[-1417,10]],[[215564,260206],[-1,8109]],[[215564,260206],[-1425,-1]],[[214139,260205],[1,8112]],[[214139,260205],[-87,1]],[[214052,260206],[-1250,7]],[[212802,260213],[-89,1]],[[212713,260214],[0,8080]],[[212713,260214],[-1472,-6]],[[211241,260208],[2,6496]],[[219856,268230],[-1,-7250]],[[219855,260980],[-1643,60]],[[233680,259117],[-1054,-950],[-246,-706]],[[232380,257461],[15,2779],[-147,-648]],[[232248,259592],[-249,2566],[-298,601],[38,1653],[-230,2395]],[[222752,268172],[-19,-11396]],[[222733,256776],[-311,-1069],[-354,1218],[-198,-944],[-249,1262]],[[221621,257243],[-334,1575]],[[221287,258818],[20,9406]],[[221287,258818],[-1085,65]],[[220202,258883],[-345,20],[-2,2077]],[[224221,261121],[-256,-235],[-55,-1438],[-590,-2354]],[[223320,257094],[-275,-434]],[[223045,256660],[-312,116]],[[260218,259586],[-114,-1]],[[260104,259585],[-994,-4]],[[259110,259581],[-7,3224],[-142,-9],[23,3277]],[[259110,259581],[-135,-3310]],[[258975,256271],[-230,291],[-9,1358],[-330,-1425],[-5,1399],[-275,-34]],[[258126,257860],[-1,2419],[142,6],[1,5754]],[[247731,267623],[274,-3564],[25,-1966]],[[248030,262093],[-341,-656]],[[247689,261437],[-106,-329],[-1263,-24]],[[246320,261084],[1,1631]],[[249339,262613],[-284,-111]],[[249055,262502],[-612,-243]],[[248443,262259],[-413,-166]],[[266169,263980],[-1,-929],[-528,-12]],[[265640,263039],[-382,824]],[[265258,263863],[46,1152]],[[225328,255832],[-536,4982]],[[226734,260599],[-1152,-3887]],[[225582,256712],[-254,-880]],[[272077,261103],[-207,-2290],[-271,-705]],[[271599,258108],[-852,4666]],[[232248,259592],[-1251,-3816]],[[230997,255776],[-725,7127]],[[209299,266698],[-218,-16661]],[[209081,250037],[-2269,-8219]],[[206812,241818],[27,3208],[-31,21680]],[[206812,241818],[-175,-639]],[[206637,241179],[-655,3287],[-500,980],[-452,2433],[-137,1784],[-467,1522],[-266,2270],[-357,1834]],[[203803,255289],[-2,11451]],[[203803,255289],[-618,1714],[-454,4718],[-407,953]],[[211241,260208],[-789,7]],[[210452,260215],[-226,1786],[-294,419],[-120,2210],[-128,-359],[-262,2428]],[[210452,260215],[261,-433],[176,-3774],[534,31],[187,-1030],[512,-17]],[[212122,254992],[-1599,-11259]],[[210523,243733],[-1442,6304]],[[262562,265291],[-9,-2748],[-135,18]],[[262418,262561],[-508,49]],[[255334,263517],[-41,-2445],[-142,-14],[4,-2674],[-156,-742],[132,-1478],[-365,-604],[-167,-1944]],[[254599,253616],[-506,-1944]],[[254093,251672],[-63,1221],[229,2446],[-147,-275],[95,2173],[-484,2051],[-26,1810]],[[237565,263797],[234,-2648],[-111,-2155]],[[237688,258994],[-417,-314]],[[237271,258680],[-297,954],[-857,614]],[[236117,260248],[-149,3550]],[[244067,262083],[-416,-314],[-36,-1776],[-276,-171],[-2,-3574],[-156,-1569],[81,-1641]],[[243262,253038],[-258,531],[-178,-1296],[-48,1873]],[[242778,254146],[7,11198]],[[269459,266064],[0,-2383],[-247,7],[-4,-2375]],[[269208,261313],[-295,-711]],[[268913,260602],[-583,-22],[1,2596]],[[264147,264342],[212,-842],[-100,-3841]],[[264259,259659],[-425,-12]],[[263834,259647],[-269,0]],[[263565,259647],[-156,2805],[9,2759]],[[270526,263478],[-236,-1000],[1,-5622]],[[270291,256856],[-262,1105]],[[270029,257961],[-821,3352]],[[258126,257860],[-564,-27]],[[257562,257833],[-384,26],[-188,2000],[3,2240]],[[256993,262099],[-3,1445]],[[263565,259647],[-754,-30]],[[262811,259617],[-348,-20],[-45,2964]],[[242778,254146],[-219,178]],[[242559,254324],[-305,2653]],[[242254,256977],[-195,277],[-241,2436],[-29,3221]],[[265258,263863],[-181,-811],[-39,-2867]],[[265038,260185],[-69,-496],[-710,-30]],[[252692,256194],[-1093,-41]],[[251599,256153],[-284,-4]],[[251315,256149],[0,7282]],[[252651,261072],[41,-4878]],[[246320,261084],[-2,-1627]],[[246318,259457],[-1160,-19]],[[245158,259438],[-408,1159],[-49,1441]],[[227703,254041],[-519,4981]],[[229059,261277],[364,-3474]],[[229423,257803],[-1328,-4514]],[[228095,253289],[-182,-662],[-210,1414]],[[266538,263854],[73,-1664]],[[266611,262190],[-131,-3053]],[[266480,259137],[-414,-482]],[[266066,258655],[-411,1022],[-15,3362]],[[267887,262620],[-7,-2008]],[[267880,260612],[-912,138],[0,1415],[-357,25]],[[266066,258655],[-13,-4419]],[[266053,254236],[-962,79]],[[265091,254315],[4,2025]],[[265095,256340],[-57,3845]],[[236117,260248],[245,-1198],[-44,-6786]],[[236318,252264],[-314,1644],[-175,1815],[-632,880],[-381,1287]],[[239356,254865],[-430,-6],[-146,-1627],[-240,7],[-65,-1705]],[[238475,251534],[-133,-174]],[[238342,251360],[-234,2339],[-88,3954],[-332,1341]],[[271599,258108],[109,-1595]],[[271708,256513],[-299,-2289]],[[271409,254224],[-861,806]],[[270548,255030],[-29,1447],[-228,379]],[[256993,262099],[-405,-645],[-234,-1413],[-90,-2314],[-724,-4800]],[[255540,252927],[-525,-296]],[[255015,252631],[-416,985]],[[251315,256149],[-565,11]],[[250750,256160],[-705,0]],[[250045,256160],[-4,6755]],[[229993,252193],[-570,5610]],[[230997,255776],[-230,-1047]],[[230767,254729],[-774,-2536]],[[268913,260602],[84,-3783]],[[268997,256819],[-88,-1964]],[[268909,254855],[-525,10],[-61,691],[-839,350]],[[267484,255906],[-15,966]],[[267469,256872],[411,3740]],[[242254,256977],[-1212,-1625],[-245,2364]],[[250045,256160],[-145,0]],[[249900,256160],[-376,-7]],[[249524,256153],[-46,1959],[-425,1320],[2,3070]],[[262811,259617],[-6,-2207]],[[262805,257410],[-584,-281],[-45,579]],[[262176,257708],[-228,4571]],[[262176,257708],[-117,-3882]],[[262059,253826],[-920,118]],[[261139,253944],[4,5668]],[[249524,256153],[-188,2]],[[249336,256155],[-474,-1],[2,-814],[-378,9]],[[248486,255349],[-43,6910]],[[267469,256872],[-538,69]],[[266931,256941],[-451,2196]],[[244529,261751],[-62,-2123],[-258,460],[229,-1495],[-276,-599],[18,-3120],[-205,750],[174,-2365],[-387,-521],[182,-1100]],[[243944,251638],[-102,-1383],[173,-931],[-202,-1256]],[[243813,248068],[-73,-540]],[[243740,247528],[12,447]],[[243752,247975],[-182,929]],[[243570,248904],[129,2677],[-437,1457]],[[248486,255349],[-225,-1013]],[[248261,254336],[-569,265]],[[247692,254601],[-3,6836]],[[257562,257833],[96,-2876],[-100,-437],[8,-2858]],[[257566,251662],[-179,1283],[-1847,-18]],[[245158,259438],[-12,-4904]],[[245146,254534],[-217,450],[-649,-954],[-49,-1688],[-287,-704]],[[191369,254272],[-1713,-9],[-813,1749]],[[225328,255832],[-812,-3459]],[[224516,252373],[-177,743],[-194,2530],[-443,141],[-382,1307]],[[247692,254601],[-45,0]],[[247647,254601],[-802,-13]],[[246845,254588],[-237,-7]],[[246608,254581],[0,4882],[-290,-6]],[[270029,257961],[-328,-1873],[-203,-243]],[[269498,255845],[-501,974]],[[226342,249372],[-760,7340]],[[227703,254041],[-1361,-4669]],[[220202,258883],[-12,-9165]],[[220190,249718],[-1597,-27]],[[218593,249691],[-12,8144],[-1602,88]],[[216979,257923],[0,516]],[[254093,251672],[-73,-563]],[[254020,251109],[-140,-343],[-854,-8],[-285,-550]],[[252741,250208],[-49,5986]],[[272895,258076],[-221,-2947],[-140,1836],[361,1111]],[[272467,260258],[359,-497],[69,-955],[-372,-536],[-14,-3438],[142,-738],[-530,300]],[[272121,254394],[-413,2119]],[[232380,257461],[233,-3410],[-192,-919],[-109,-3298]],[[232312,249834],[-587,-40],[-741,-2203]],[[230984,247591],[-246,5282],[29,1856]],[[215564,260206],[-1,-10641]],[[215563,249565],[-1463,119]],[[214100,249684],[-48,10522]],[[214100,249684],[-246,18]],[[213854,249702],[-112,1974],[-687,2489],[-255,-616]],[[212800,253549],[2,6664]],[[237271,258680],[3,-1922],[-169,-1045],[14,-5133]],[[237119,250580],[-252,-652]],[[236867,249928],[-549,2336]],[[216979,257923],[-20,-8366]],[[216959,249557],[-1396,8]],[[212800,253549],[-158,-489],[-520,1932]],[[265095,256340],[-399,-67]],[[264696,256273],[-805,-61]],[[263891,256212],[-57,3435]],[[263891,256212],[-578,-54]],[[263313,256158],[-423,46],[-85,1206]],[[261139,253944],[-190,-1282],[-485,462],[-142,-1410]],[[260322,251714],[-224,22]],[[260098,251736],[6,7849]],[[260098,251736],[-1121,-78]],[[258977,251658],[-2,4613]],[[246608,254581],[-1058,-7],[-230,-519]],[[245320,254055],[-174,479]],[[238342,251360],[-866,-337]],[[237476,251023],[-357,-443]],[[266931,256941],[-267,-2338]],[[266664,254603],[-217,-428]],[[266447,254175],[-394,61]],[[234433,256013],[126,-728]],[[234559,255285],[-1328,-6123]],[[233231,249162],[-512,-2377]],[[232719,246785],[-160,1025],[33,1830],[-280,194]],[[221621,257243],[-4,-7538]],[[221617,249705],[-1427,13]],[[270548,255030],[135,-1217],[-559,-1966],[-260,400]],[[269864,252247],[-282,1360],[-84,2238]],[[258977,251658],[18,-3690]],[[258995,247968],[-561,3]],[[258434,247971],[-835,7]],[[257599,247978],[-33,3684]],[[218593,249691],[-762,-75]],[[217831,249616],[-872,-59]],[[236867,249928],[-914,-1225]],[[235953,248703],[-290,477]],[[235663,249180],[-783,1632]],[[234880,250812],[-186,3541],[-135,932]],[[263313,256158],[-12,-3257]],[[263301,252901],[-768,-60],[-14,-3409]],[[262519,249432],[-296,57]],[[262223,249489],[-201,1664],[37,2673]],[[229345,250051],[-671,-2227]],[[228674,247824],[-579,5465]],[[229993,252193],[-648,-2142]],[[242559,254324],[-409,-313],[69,-6634],[-201,53]],[[242018,247430],[-298,679],[-278,-1715],[-307,-248]],[[241135,246146],[-630,-114]],[[240505,246032],[-17,6745],[-422,1756]],[[223045,256660],[-5,-9678]],[[223040,246982],[-1093,-7]],[[221947,246975],[-331,2],[1,2728]],[[224516,252373],[110,-2638],[233,-1105]],[[224859,248630],[-17,-2012]],[[224842,246618],[-1446,-2]],[[223396,246616],[-356,366]],[[267484,255906],[289,-2603],[-29,-1804]],[[267744,251499],[-329,-678],[-91,-2266]],[[267324,248555],[-270,35]],[[267054,248590],[-114,4038],[-276,1975]],[[269864,252247],[216,-986],[1,-1604],[213,-1377]],[[270294,248280],[-791,53],[-15,-4066],[756,-202],[-181,-4018]],[[270063,240047],[-569,247]],[[269494,240294],[-47,4435],[-166,-9],[13,2675],[-283,1029],[-221,3072]],[[268790,251496],[-78,1758],[187,-78]],[[268899,253176],[10,1679]],[[226342,249372],[-12,-642]],[[226330,248730],[-1471,-100]],[[272518,253791],[20,18]],[[272538,253809],[99,-1699],[-281,-1544],[-109,1279],[124,2060],[147,-114]],[[272121,254394],[4,-51]],[[272125,254343],[81,-132]],[[272206,254211],[155,-845],[-204,-1996],[71,-2600],[-325,802]],[[271903,249572],[-591,1666]],[[271312,251238],[97,2986]],[[264696,256273],[-167,-1561],[-391,-949],[-69,-1201],[-328,-1573],[-68,-1447]],[[263673,249542],[-96,10]],[[263577,249552],[15,3293],[-291,56]],[[265091,254315],[-12,-4796]],[[265079,249519],[-315,12]],[[264764,249531],[-724,18]],[[264040,249549],[-367,-7]],[[252741,250208],[18,-2159]],[[252759,248049],[-1139,-6]],[[251620,248043],[-21,8110]],[[250750,256160],[24,-9758]],[[250774,246402],[-565,1]],[[250209,246403],[-23,1870]],[[250186,248273],[3,6261],[-287,-6],[-2,1632]],[[250186,248273],[-850,-146]],[[249336,248127],[0,8028]],[[251620,248043],[-141,-1624]],[[251479,246419],[-705,-17]],[[249336,248127],[-206,-4]],[[249130,248123],[-300,-5]],[[248830,248118],[0,1358],[-237,-6],[-309,1882],[-23,2984]],[[268790,251496],[-833,9]],[[267957,251505],[-213,-6]],[[234880,250812],[-269,-1952],[-727,-4046]],[[233884,244814],[-145,1529],[-209,-878]],[[233530,245465],[-193,-35],[-106,3732]],[[213854,249702],[556,-1560],[549,-266],[329,-1468],[180,-3869],[116,-913]],[[215584,241626],[-1029,50],[-1,-1090],[-571,24],[0,-5857],[-623,-16],[0,-4293]],[[213360,230444],[-2433,11417]],[[210927,241861],[-404,1872]],[[271312,251238],[-472,-2258]],[[270840,248980],[-349,503],[-197,-1203]],[[245320,254055],[101,-733],[-2,-5263]],[[245419,248059],[-325,3]],[[245094,248062],[-1281,6]],[[240505,246032],[-431,-220]],[[240074,245812],[-1234,93],[-382,-268]],[[238458,245637],[138,2982],[-121,2915]],[[230984,247591],[-297,-1133],[-306,-3132],[-307,-919]],[[230074,242407],[-150,660]],[[229924,243067],[-83,1790],[-230,1424],[-92,2592],[-174,1178]],[[267054,248590],[-504,85]],[[266550,248675],[-89,305]],[[266461,248980],[-188,553]],[[266273,249533],[181,2384],[-7,2258]],[[246845,254588],[1,-6514]],[[246846,248074],[-55,0]],[[246791,248074],[-720,-7]],[[246071,248067],[-652,-8]],[[247647,254601],[2,-6508]],[[247649,248093],[-244,-6]],[[247405,248087],[-559,-13]],[[248830,248118],[-1181,-25]],[[243570,248904],[-75,-474]],[[243495,248430],[-154,-909],[-26,-2268]],[[243315,245253],[-1107,8]],[[242208,245261],[-190,2169]],[[266273,249533],[3,-822],[-452,71]],[[265824,248782],[-746,72],[1,665]],[[228674,247824],[-526,-1799],[-157,-2556]],[[227991,243469],[-863,2199],[-567,665]],[[226561,246333],[-231,2397]],[[255015,252631],[47,-4598]],[[255062,248033],[-100,-2449],[292,-2293],[343,-1258],[-72,-3553]],[[255525,238480],[118,-709],[-472,-3244],[-776,-834],[-363,138],[617,1003],[-445,2269],[13,2457],[-99,3216],[-227,556]],[[253891,243332],[-3,424]],[[253888,243756],[51,358]],[[253939,244114],[171,4892],[-90,2103]],[[262223,249489],[73,-1396]],[[262296,248093],[-1355,-67]],[[260941,248026],[8,3775],[-627,-87]],[[257599,247978],[-271,46]],[[257328,248024],[-1052,37]],[[256276,248061],[-1214,-28]],[[263577,249552],[-524,-184]],[[263053,249368],[-534,64]],[[260941,248026],[-26,-3]],[[260915,248023],[-1496,-68]],[[259419,247955],[-424,13]],[[238458,245637],[-203,-3553],[-301,-2557],[116,-1801],[-122,-774]],[[237948,236952],[-67,-1097],[156,-1846]],[[238037,234009],[-534,-28]],[[237503,233981],[98,13117],[-125,3925]],[[269494,240294],[-114,45]],[[269380,240339],[-346,137]],[[269034,240476],[-18,2136],[-493,492],[-222,1908],[-346,636]],[[267955,245648],[2,5857]],[[267955,245648],[-134,-370]],[[267821,245278],[-66,1821],[-380,52],[-51,1404]],[[253939,244114],[-283,-5327],[-10,-2293],[-805,174]],[[252841,236668],[-47,6490]],[[252794,243158],[-35,4891]],[[272307,246842],[-203,-3711],[-81,2742],[284,969]],[[271903,249572],[306,-2478],[-160,-4072],[-292,-203]],[[271757,242819],[-831,2065]],[[270926,244884],[0,3895],[-86,201]],[[237503,233981],[-603,-19]],[[236900,233962],[96,1937],[-93,1644],[125,1714]],[[237028,239257],[57,2122],[-200,796],[-362,6258],[-570,270]],[[235663,249180],[-268,-877],[313,-9029]],[[235708,239274],[21,-670],[-542,-17]],[[235187,238587],[-329,77]],[[234858,238664],[56,882],[-384,658],[-240,1922],[-168,-357],[-46,2247],[-192,798]],[[229924,243067],[-953,-3232]],[[228971,239835],[-534,-1857]],[[228437,237978],[-325,5186],[-121,305]],[[209922,237143],[-279,-6],[-3006,4042]],[[210927,241861],[-1005,-4718]],[[232719,246785],[-682,-1234]],[[232037,245551],[-850,-766]],[[231187,244785],[-203,2806]],[[221947,246975],[2,-4279]],[[221949,242696],[-1761,-8]],[[220188,242688],[2,7030]],[[220188,242688],[-2353,-81]],[[217835,242607],[-4,7009]],[[217835,242607],[0,-7787]],[[217835,234820],[-2223,6]],[[215612,234826],[279,1284],[-229,1216],[263,2565],[-18,1122],[-323,613]],[[264040,249549],[-12,-7241]],[[264028,242308],[-1345,403]],[[262683,242711],[300,3247],[70,3410]],[[264764,249531],[114,-3092],[-22,-4394]],[[264856,242045],[-554,176]],[[264302,242221],[-274,87]],[[266550,248675],[98,-2368],[249,-1172],[-274,-444],[-99,-1222],[356,-2141]],[[266880,241328],[-709,260]],[[266171,241588],[-368,134]],[[265803,241722],[21,7060]],[[265803,241722],[-735,254]],[[265068,241976],[-212,69]],[[262683,242711],[-3,0]],[[262680,242711],[-384,5382]],[[270926,244884],[-366,-1369],[-37,-1852],[123,-1709],[-124,-3745]],[[270522,236209],[-335,-64],[-107,1022],[-17,2880]],[[237028,239257],[-1320,17]],[[233530,245465],[-89,-6610]],[[233441,238855],[-666,84],[-646,2258]],[[232129,241197],[-92,4354]],[[244740,241601],[-76,124]],[[244664,241725],[-138,1864],[-596,-160],[213,1883],[-298,-206],[-105,2422]],[[245094,248062],[-354,-6461]],[[226561,246333],[-375,-2252],[-241,-2998]],[[225945,241083],[-212,-3685]],[[225733,237398],[-624,1108]],[[225109,238506],[-245,3503],[173,1353],[-5,1659],[-190,1597]],[[267821,245278],[-326,-1352],[-1,-2846]],[[267494,241080],[-481,193]],[[267013,241273],[-88,37]],[[266925,241310],[-45,18]],[[244664,241725],[-233,-2739],[-196,-268]],[[244235,238718],[-602,5]],[[243633,238723],[-154,6]],[[243479,238729],[63,3797],[-227,2727]],[[243752,247975],[-257,455]],[[250209,246403],[-2,-4881]],[[250207,241522],[-565,12],[0,-2449],[-406,-1071]],[[249236,238014],[-349,2359],[-75,1498]],[[248812,241871],[318,6252]],[[248812,241871],[-426,-22],[-726,870]],[[247660,242719],[-255,5368]],[[242208,245261],[112,-1501],[-109,-3720],[-662,-628],[-123,-1003]],[[241426,238409],[-387,12]],[[241039,238421],[5,125]],[[241044,238546],[92,1719],[-1,5881]],[[262680,242711],[-189,-1959]],[[262491,240752],[-656,31],[-11,-813],[-591,47]],[[261233,240017],[-143,13]],[[261090,240030],[8,4056],[-466,837]],[[260632,244923],[283,3100]],[[247660,242719],[34,-9078]],[[247694,233641],[-96,120]],[[247598,233761],[-55,1170],[-290,-153]],[[247253,234778],[-284,1435],[-176,2313],[-2,3039]],[[246791,241565],[0,6509]],[[256232,239600],[16,-2077],[-262,-1642],[-543,-650],[107,1725],[233,527],[-258,997]],[[256276,248061],[-334,-1385],[-81,-3536],[371,-3540]],[[257328,248024],[-39,-10998]],[[257289,237026],[-1052,-1037],[665,1910],[-225,961],[-136,-1053],[-250,2451],[-59,-658]],[[252794,243158],[-1314,0]],[[251480,243158],[-1,3261]],[[246791,241565],[-956,-12]],[[245835,241553],[167,1301]],[[246002,242854],[-21,4096],[90,1117]],[[258434,247971],[-17,-10056]],[[258417,237915],[-166,1067],[-410,-2043],[-552,87]],[[246002,242854],[-1125,-260],[-121,-1038]],[[244756,241556],[-16,45]],[[260632,244923],[-55,-1607],[-562,869],[-66,-1633]],[[259949,242552],[-537,37],[7,5366]],[[259949,242552],[-196,-1239],[99,-2459],[-315,-2140]],[[259537,236714],[-9,-2240]],[[259528,234474],[-1118,2046]],[[258410,236875],[0,-355]],[[258410,236875],[816,-133],[-426,1780],[-383,-607]],[[231187,244785],[-45,-4378],[221,-3089],[-139,-1697]],[[231224,235621],[12,737],[-414,180]],[[230822,236538],[-165,3013],[-358,1438],[-320,391],[95,1027]],[[223396,246616],[1,-7873]],[[223397,238743],[-946,26]],[[222451,238769],[-503,-4],[1,3931]],[[225109,238506],[-674,264]],[[224435,238770],[-1038,-27]],[[251480,243158],[0,-1087]],[[251480,242071],[-990,-2],[-283,-547]],[[228437,237978],[-499,-1008]],[[227938,236970],[-97,304]],[[227841,237274],[-634,1522],[-437,-1264],[-263,726],[-303,2900],[-259,-75]],[[234858,238664],[-878,-2778]],[[233980,235886],[-396,1124],[-143,1845]],[[241044,238546],[-396,8],[-9,-943],[-990,-262]],[[239649,237349],[2,3245],[421,9],[2,5209]],[[239649,237349],[0,-382]],[[239649,236967],[-709,-8],[2,1629],[-283,7],[1,-1639],[-712,-4]],[[269034,240476],[-293,113]],[[268741,240589],[-1247,491]],[[232129,241197],[72,-7162]],[[232201,234035],[-806,-384]],[[231395,233651],[-171,1970]],[[243479,238729],[216,-1917],[-535,411],[-328,-897]],[[242832,236326],[-172,207],[-257,-1508]],[[242403,235025],[-94,2569],[-194,809],[-689,6]],[[261090,240030],[-142,0],[-5,-2437],[-1018,73],[-388,-952]],[[271757,242819],[498,-308],[-34,-3432],[-155,926],[-295,-107]],[[271771,239898],[-322,203],[-925,-5548]],[[270524,234553],[-2,1656]],[[252841,236668],[-233,-1277],[-178,1270],[-575,-702],[-371,1453]],[[251484,237412],[-4,4659]],[[230822,236538],[-952,-1549]],[[229870,234989],[-278,409],[-621,4437]],[[245835,241553],[-210,-1460],[-15,-2062],[278,-2140]],[[245888,235891],[-360,-439]],[[245528,235452],[-338,30]],[[245190,235482],[-277,639],[124,2784],[-183,-36],[-98,2687]],[[249236,238014],[203,-4319],[261,-907]],[[249700,232788],[-283,-440]],[[249417,232348],[-471,-68],[-254,869],[-304,-894],[-309,779]],[[248079,233034],[-385,607]],[[264302,242221],[-267,-2074],[-54,-1701],[-693,-1753]],[[263288,236693],[-375,1330],[-2,1097],[-281,268],[-139,1364]],[[222451,238769],[6,-3970]],[[222457,234799],[-1261,76]],[[221196,234875],[-1009,-7]],[[220187,234868],[1,7820]],[[220187,234868],[-1627,-38]],[[218560,234830],[-725,-10]],[[265068,241976],[85,-2796],[-178,-1],[-97,-4622]],[[264878,234557],[-479,557],[-1297,-63]],[[263102,235051],[186,1642]],[[251484,237412],[-1134,-2305],[-141,1298]],[[250209,236405],[-2,5117]],[[266171,241588],[21,-1291],[-370,-1383],[42,-1369],[-274,-2432]],[[265590,235113],[-266,-1104],[-213,-2775]],[[265111,231234],[-234,66]],[[264877,231300],[1,3257]],[[209944,215738],[-22,21405]],[[213360,230444],[686,-3232]],[[214046,227212],[-190,-2194],[-356,428],[-422,-724],[-393,-4187],[-190,-3082],[-26,-2697],[-288,-495],[-452,-3923],[-784,1354],[-338,2103],[-458,443],[-205,1500]],[[245190,235482],[-472,-15],[-483,3251]],[[215612,234826],[-8,-9436]],[[215604,225390],[-988,210],[-570,1612]],[[247253,234778],[-437,-1709],[-207,516]],[[246609,233585],[-184,-841],[-537,3147]],[[266925,241310],[88,-37]],[[267013,241273],[105,-135],[78,-2865],[203,-1637]],[[267399,236636],[-213,-2319]],[[267186,234317],[-334,-5]],[[266852,234312],[-283,815],[-979,-14]],[[250209,236405],[34,-1301],[-543,-2316]],[[268741,240589],[-104,-715],[226,-2817],[-101,-1193],[-315,-130]],[[268447,235734],[-586,1764],[-462,-862]],[[209944,215738],[-499,619],[-308,933],[-500,3117],[-209,111],[-481,2314],[-467,5014],[-27,4955],[-481,3934],[-35,2230],[-300,2214]],[[233980,235886],[192,-3310]],[[234172,232576],[-466,-2513],[-754,2533],[-380,-1253],[-369,-201]],[[232203,231142],[-2,2893]],[[227841,237274],[-344,-3901],[-437,-2646]],[[227060,230727],[-169,-807]],[[226891,229920],[-1289,6177]],[[225602,236097],[131,1301]],[[262491,240752],[-511,-7567]],[[261980,233185],[-763,18]],[[261217,233203],[16,6814]],[[263102,235051],[119,-3211],[349,-2174]],[[263570,229666],[-1288,55],[-50,-791]],[[262232,228930],[-308,1351],[56,2904]],[[269380,240339],[4,-8335]],[[269384,232004],[-309,-3002],[110,-640]],[[269185,228362],[-359,-2062]],[[268826,226300],[-394,1903]],[[268432,228203],[15,7531]],[[272248,234159],[-9,-2722],[-434,368]],[[271805,231805],[-186,100],[91,1895],[-113,885],[151,2029],[471,-634],[29,-1921]],[[272388,234172],[-135,-12]],[[272253,234160],[-2,2672],[137,-2660]],[[271771,239898],[387,-696],[23,-1979],[-488,-401],[1,-1430],[-265,-958],[92,-1411]],[[271521,233023],[-998,-79]],[[270523,232944],[1,1609]],[[270523,232944],[0,-816]],[[270523,232128],[-259,0]],[[270264,232128],[-880,-124]],[[261213,230026],[6,-1939]],[[261219,228087],[-831,3621],[181,87],[319,-1692],[325,-77]],[[261217,233203],[-4,-3055]],[[261213,230148],[-256,-46],[-113,1656],[-199,-122],[-386,1289],[-6,-1143],[-725,2692]],[[229870,234989],[-428,-2537]],[[229442,232452],[-640,-2032]],[[228802,230420],[-160,3872],[-704,2678]],[[236900,233962],[5,-1513]],[[236905,232449],[-445,185],[-184,-1246],[-288,178]],[[235988,231566],[-422,-16],[-379,7037]],[[224435,238770],[11,-6721]],[[224446,232049],[-926,-12]],[[223520,232037],[-1061,-73],[-2,2835]],[[225602,236097],[-347,-5926]],[[225255,230171],[-325,-1870]],[[224930,228301],[-484,3748]],[[245308,223867],[-799,1569],[36,944],[-274,2264]],[[244271,228644],[603,287]],[[244874,228931],[203,-2331],[251,-832],[-20,-1901]],[[244558,230559],[-645,-479],[-351,1662],[-258,-1459],[-367,543]],[[242937,230826],[126,1509],[-231,3991]],[[243633,238723],[169,-1018],[54,-2471],[406,-1498],[38,-2365],[258,-812]],[[245528,235452],[-238,-4825]],[[245290,230627],[-329,-692]],[[244961,229935],[-403,624]],[[235988,231566],[8,-4159]],[[235996,227407],[-1499,-100]],[[234497,227307],[-325,5269]],[[239649,236967],[279,-442],[95,-4141],[287,-11],[14,-1373],[-301,-819]],[[240023,230181],[-367,261],[-1652,-20]],[[238004,230422],[33,3587]],[[241039,238421],[19,-2543]],[[241058,235878],[90,-2556],[-90,-2140]],[[241058,231182],[-312,-1023]],[[240746,230159],[-723,22]],[[242403,235025],[-396,-2817]],[[242007,232208],[-408,-1799],[-460,906]],[[241139,231315],[-81,-133]],[[268432,228203],[-222,-845]],[[268210,227358],[-526,3596],[-439,645],[-59,2718]],[[228802,230420],[-812,-4930]],[[227990,225490],[-930,5237]],[[231395,233651],[-275,-1643],[131,-1238]],[[231251,230770],[-412,483],[-916,-965]],[[229923,230288],[-71,1866],[-410,298]],[[242937,230826],[-38,-676]],[[242899,230150],[-457,1473],[-435,585]],[[226432,226825],[-346,-1970]],[[226086,224855],[-87,1789],[-744,3527]],[[226891,229920],[-513,-2622],[54,-473]],[[246609,233585],[-25,-1023]],[[246584,232562],[-762,-996],[-135,-865]],[[245687,230701],[0,-1]],[[245687,230700],[-397,-73]],[[266852,234312],[-10,-6935],[145,-1217]],[[266987,226160],[-200,-2815]],[[266787,223345],[-399,937],[-283,2980],[-405,1944],[-589,2028]],[[264877,231300],[-794,-653],[8,-1736],[-255,56]],[[263836,228967],[-266,699]],[[247598,233761],[-477,-3687],[-248,-294],[-10,-2337]],[[246863,227443],[-276,26]],[[246587,227469],[-43,-63]],[[246544,227406],[-91,2727],[131,2429]],[[218560,234830],[1,-12369]],[[218561,222461],[3,-3801],[-131,-3064],[-135,-246]],[[218298,215350],[-586,2295],[-155,1738],[-559,1471],[-109,1938],[-428,2706],[-230,-539],[-627,431]],[[221196,234875],[-9,-4032]],[[221187,230843],[-585,153],[-126,-5325],[-5,-3218]],[[220471,222453],[-272,-3]],[[220199,222450],[-1638,11]],[[223520,232037],[7,-6643]],[[223527,225394],[-721,2115],[-1183,235]],[[221623,227744],[-251,17],[4,3079],[-189,3]],[[268210,227358],[-114,-1168]],[[268096,226190],[-1109,-30]],[[272837,223331],[-14,0]],[[272823,223331],[-64,-213]],[[272759,223118],[-215,-625],[-556,-55]],[[271988,222438],[-4,2546]],[[271984,224984],[-177,2427],[99,1442],[-322,1679],[221,1273]],[[272248,234159],[350,-6434],[-19,-1080],[258,-3314]],[[272388,234172],[216,-5656],[-351,5644]],[[238004,230422],[-370,-3472]],[[237634,226950],[-15,2086],[-480,949],[-234,2464]],[[232203,231142],[-438,1363],[375,-6983]],[[232140,225522],[-575,-1123]],[[231565,224399],[-196,1520],[-118,4851]],[[247737,223729],[-329,25],[-92,1327],[-453,2362]],[[247598,233761],[-7,-4805],[112,-2059],[203,-841],[-169,-2327]],[[248079,233034],[-93,-5082],[222,493],[142,-926]],[[248350,227519],[-169,-1243],[4,-1675],[243,-2463],[7,-2735]],[[248435,219403],[-412,1679],[32,1884],[-166,601]],[[247889,223567],[-152,162]],[[262232,228930],[-64,-3572]],[[262168,225358],[-1,-31]],[[262167,225327],[-472,-1559]],[[261695,223768],[-260,-97],[30,2044],[-246,2372]],[[261213,230026],[0,122]],[[249417,232348],[-288,-1736],[-365,-1099]],[[248764,229513],[-363,-244],[-62,-812],[282,-1440]],[[248621,227017],[-271,502]],[[271521,233023],[-209,-1367],[211,19],[-61,-1459],[280,-1434],[17,-2305]],[[271759,226477],[-575,-58],[-356,-1658],[-304,-535]],[[270524,224226],[-1,7902]],[[237634,226950],[-209,-1155],[251,-2061]],[[237676,223734],[-488,-232],[-948,-2204]],[[236240,221298],[-9,6070],[-235,39]],[[234497,227307],[188,-998],[-40,-2779]],[[234645,223530],[-249,562],[-163,2138],[-77,-1073],[335,-1692],[-100,-2415]],[[234391,221050],[-400,-946],[-152,1094]],[[233839,221198],[-128,697],[-451,-247]],[[233260,221648],[-1120,3874]],[[249637,222999],[-31,137]],[[249606,223136],[-17,-11]],[[249589,223125],[-854,2530],[-114,1362]],[[248764,229513],[178,-1314],[189,484],[62,-1577],[278,-41],[74,2259],[374,1265],[70,-3363],[267,-194],[-426,-2469],[-490,256],[297,-1820]],[[250525,230647],[-312,-71],[261,1146],[51,-1075]],[[246544,227406],[-360,597],[-279,-302]],[[245905,227701],[-208,1350],[-10,1649]],[[229923,230288],[145,-1541]],[[230068,228747],[-848,-6121]],[[229220,222626],[-749,-87]],[[228471,222539],[-487,2914]],[[227984,225453],[6,37]],[[242899,230150],[-21,-3785]],[[242878,226365],[-34,-727],[-510,-1743],[182,-15],[-22,-1563],[-530,-1558],[-885,886]],[[241079,221645],[5,8529],[-338,-15]],[[270524,224226],[-18,-8]],[[270506,224218],[-216,2191],[-389,132],[-406,1491]],[[269495,228032],[311,619],[458,3477]],[[269495,228032],[-310,330]],[[224930,228301],[-647,-3579]],[[224283,224722],[-370,-472]],[[223913,224250],[-386,1144]],[[243094,222650],[507,-1497],[-373,-1379],[-504,1635],[370,1241]],[[244961,229935],[-87,-1004]],[[244271,228644],[-426,-208],[-645,-3854]],[[243200,224582],[80,1697],[-402,86]],[[231565,224399],[21,-2330],[-179,-21]],[[231407,222048],[-240,599]],[[231167,222647],[-226,617],[-208,2752],[-251,273],[-414,2458]],[[245905,227701],[-338,-3548]],[[245567,224153],[-211,-1645]],[[245356,222508],[-48,1359]],[[221623,227744],[-6,-5221]],[[221617,222523],[-1146,-70]],[[227984,225453],[-780,-2865],[-98,358]],[[227106,222946],[-308,892],[-366,2987]],[[241079,221645],[-915,2302],[-643,1244],[-860,-33],[-722,-625],[-263,-799]],[[226086,224855],[-251,-1276],[-617,-1664]],[[225218,221915],[-190,1265],[73,1338],[-818,204]],[[261984,223666],[48,-1027],[-349,843],[301,184]],[[262167,225327],[153,-1172],[-625,-387]],[[263836,228967],[267,-1312],[-501,151],[-1029,-3276],[-405,828]],[[231167,222647],[-368,-2222],[-115,-1978],[-812,-2987]],[[229872,215460],[-51,299]],[[229821,215759],[273,1332],[-648,4505],[-226,1030]],[[245356,222508],[-369,-726]],[[244987,221782],[-280,-1858],[-256,1228],[-396,-628],[-7,2090],[-206,-84],[-20,2140],[-622,-88]],[[270506,224218],[-1,-4598]],[[270505,219620],[-384,-936],[-50,1175],[-543,18]],[[269528,219877],[-418,-93],[-2,1070],[-275,505]],[[268833,221359],[-7,4941]],[[268833,221359],[-783,495]],[[268050,221854],[-36,3076],[82,1260]],[[248195,214699],[-70,-748]],[[248125,213951],[-368,-1498],[-381,4155],[-144,-1654]],[[247232,214954],[0,260]],[[247232,215214],[0,103]],[[247232,215317],[15,318]],[[247247,215635],[13,86]],[[247260,215721],[55,1625],[-668,4635],[-414,1833],[-105,1452],[-208,-1076],[-353,-37]],[[247889,223567],[32,-2068],[-180,-982],[125,-870],[386,-448],[38,-1287],[-225,-2000],[130,-1213]],[[222150,222529],[-533,-6]],[[223913,224250],[-73,-521]],[[223840,223729],[-340,-2416],[-157,1139],[-1193,77]],[[249173,217874],[111,-1305],[-400,134],[289,1171]],[[249589,223125],[-488,-958],[203,-1847],[362,-1192],[-111,-777],[530,-230],[375,-1687],[141,831],[307,-3039],[-50,-1667],[-254,-1182],[-172,579],[-447,-2429],[422,3793],[-138,1557],[-368,-603],[-134,2620],[-304,1182],[-628,94],[58,1184],[-307,734],[-151,-685]],[[236240,221298],[-45,-112]],[[236195,221186],[-276,35]],[[235919,221221],[-211,269],[-540,-841],[124,4279],[-302,699],[-198,-2304],[-147,207]],[[227106,222946],[-578,-5086]],[[226528,217860],[-680,53],[-139,1116]],[[225709,219029],[22,814],[-513,2072]],[[271988,222438],[9,-2348],[241,-1825]],[[272238,218265],[-390,-928]],[[271848,217337],[-291,1592],[-5,2834],[246,456],[-91,1669],[277,1096]],[[271759,226477],[138,-1242],[-266,-1802],[-119,-4400],[73,-1266]],[[271585,217767],[-204,-13],[-96,2166],[-186,606],[-594,-906]],[[233260,221648],[-107,-2624],[-243,-16],[-116,-2071]],[[232794,216937],[-175,-671],[-275,621],[-264,-1151]],[[232080,215736],[-404,3185],[-269,3127]],[[228471,222539],[-170,-901],[-100,-3633]],[[228201,218005],[-1040,-5116]],[[227161,212889],[-321,2085]],[[226840,214974],[-364,2442],[52,444]],[[244928,217715],[211,-933],[-107,-1820],[-305,1569],[201,1184]],[[247260,215721],[-525,940],[-7,-1505],[-198,-130],[-95,-1883],[-522,-151],[-171,914],[376,1325],[-332,809],[-84,1095],[-261,-2776],[-233,825],[15,1953],[-358,1998],[122,2647]],[[225709,219029],[-761,-6052]],[[224948,212977],[-1106,2536]],[[223842,215513],[-2,8216]],[[223842,215513],[-1,-2983]],[[223841,212530],[-1697,16]],[[222144,212546],[6,9983]],[[272759,223118],[386,-4323]],[[273145,218795],[-132,-298],[16,-2699],[-744,-89],[-47,2556]],[[232080,215736],[-73,-613]],[[232007,215123],[-254,-1543],[-960,-3417]],[[230793,210163],[-921,5297]],[[229821,215759],[-780,-3735]],[[229041,212024],[-105,759]],[[228936,212783],[-735,5222]],[[222144,212546],[-1943,-92]],[[220201,212454],[-2,9996]],[[220201,212454],[-1549,-37]],[[218652,212417],[-301,1679],[-53,1254]],[[235183,217123],[-168,-988],[-897,-3581],[738,3848],[327,721]],[[234284,214573],[-491,4941],[46,1684]],[[234391,221050],[301,-995],[33,-3555],[-122,190],[-319,-2117]],[[236195,221186],[-1085,-2978],[545,2519],[264,494]],[[234284,214573],[-259,-341],[-245,-3555],[-456,-2355],[-295,-718]],[[233029,207604],[-168,76],[-313,2527],[-238,31],[-6,1604],[-193,734],[-104,2547]],[[269528,219877],[10,-5017],[-370,-17],[1,-3161]],[[269169,211682],[-610,-875]],[[268559,210807],[-4,-26]],[[268555,210781],[11,1180],[-229,1769],[-631,423],[-130,2753]],[[267576,216906],[349,3015],[125,1933]],[[271585,217767],[74,-1765]],[[271659,216002],[-47,-5892],[-823,-7]],[[270789,210103],[-996,7]],[[269793,210110],[-470,1716],[-154,-144]],[[226840,214974],[-1288,-6318]],[[225552,208656],[-604,4321]],[[274038,206958],[-133,1]],[[273905,206959],[-39,-1]],[[273866,206958],[-329,-17],[-54,-3299]],[[273483,203642],[-296,3940]],[[273187,207582],[-245,-773],[-517,1780]],[[272425,208589],[33,1990],[-268,1417]],[[272190,211996],[-290,2402]],[[271900,214398],[73,2026],[-125,913]],[[273145,218795],[71,-1438],[822,-10399]],[[228017,208315],[-311,1140],[-437,-2085]],[[227269,207370],[-501,3617],[393,1902]],[[228936,212783],[-919,-4468]],[[272425,208589],[-133,-1746]],[[272292,206843],[-680,-113],[4,-8039]],[[271616,198691],[-837,-35]],[[270779,198656],[10,11447]],[[271659,216002],[241,-1604]],[[230793,210163],[-40,-5357]],[[230753,204806],[-186,-16]],[[230567,204790],[-105,611]],[[230462,205401],[-405,-37]],[[230057,205364],[-229,272]],[[229828,205636],[-9,-8]],[[229819,205628],[-2,32]],[[229817,205660],[-203,3106],[-295,2552],[-278,706]],[[225150,203636],[-1,663],[-1297,-17]],[[223852,204282],[-11,8248]],[[225552,208656],[258,-1776]],[[225810,206880],[-660,-3244]],[[230757,204203],[-4,603]],[[233029,207604],[-514,-1808],[-270,336],[-409,-941],[-73,-1321],[716,1768],[-1755,-5263],[983,3206],[18,865],[-525,-948],[-443,705]],[[226700,204666],[-382,955],[-249,-541]],[[226069,205080],[-259,1800]],[[227269,207370],[-569,-2704]],[[229817,205660],[1,-376]],[[229818,205284],[-753,-2126],[109,-1486]],[[229174,201672],[-69,-398],[-683,1260]],[[228422,202534],[5,4144],[-122,1448],[-288,189]],[[223852,204282],[-1657,-123]],[[222195,204159],[-37,-4]],[[222158,204155],[-14,8391]],[[222158,204155],[-1965,141]],[[220193,204296],[8,8158]],[[220193,204296],[2,-8383]],[[220195,195913],[-275,-20]],[[219920,195893],[-228,1634],[-299,5632],[-270,1346],[-137,3111],[-290,2181],[-44,2620]],[[269793,210110],[391,-3170],[-256,-2281]],[[269928,204659],[-433,506],[-598,-8]],[[268897,205157],[-149,697],[-6,3295],[-183,1658]],[[270779,198656],[-274,-620]],[[270505,198036],[2,3099]],[[270507,201135],[2,766],[-428,978],[-153,1780]],[[228422,202534],[-599,-3072]],[[227823,199462],[-496,1215],[-66,1282],[-333,364],[-228,2343]],[[273483,203642],[-949,-51],[-367,555],[125,2697]],[[273866,206958],[152,-4435],[384,-6703],[460,-6126],[-21,-545]],[[274841,189149],[-1029,-223]],[[273812,188926],[17,9772]],[[273829,198698],[-61,3007],[-285,1937]],[[274038,206958],[411,-2425],[-276,2427]],[[274173,206960],[19,0]],[[274192,206960],[437,-3794],[144,-2446],[-167,-836],[-12,3069],[-288,-3646],[-72,100],[-11,3804],[-174,606],[153,1506],[-297,1636]],[[226409,193288],[-981,21]],[[225428,193309],[-276,-3]],[[225152,193306],[-2,10330]],[[226069,205080],[-235,-508],[782,-8928],[-25,-123]],[[226591,195521],[-240,-1164],[58,-1069]],[[273829,198698],[-2213,-7]],[[229313,193901],[29,366]],[[229342,194267],[112,1478],[226,125],[508,2370],[340,470],[-1215,-4809]],[[229467,200562],[-293,1110]],[[229818,205284],[138,-1955],[593,-2862],[-702,-2230],[-380,2325]],[[230462,205401],[-19,-1857],[-353,715],[-33,1105]],[[227823,199462],[-461,-4167]],[[227362,195295],[-76,-648],[-695,874]],[[270507,201135],[-552,2],[0,-813],[-1166,-23]],[[268789,200301],[108,4856]],[[223852,204282],[-7,-10983]],[[223845,193299],[-1631,-503],[-15,3242]],[[222199,196038],[-4,8121]],[[225152,193306],[-1307,-7]],[[222199,196038],[-2004,-125]],[[229467,200562],[-206,-608],[191,-1783]],[[229452,198171],[-926,-864],[295,-1302]],[[228821,196005],[-626,-2606]],[[228195,193399],[-53,240]],[[228142,193639],[-158,1151],[-622,505]],[[270505,198036],[-139,-2611]],[[270366,195425],[-1519,30]],[[268847,195455],[-374,-12]],[[268473,195443],[316,4858]],[[271616,198691],[0,-1635],[277,2],[94,-2171],[192,-1069],[303,-11],[-262,-959],[585,-3549],[246,-3711]],[[273051,185588],[-1173,63]],[[271878,185651],[-1368,-4]],[[270510,185647],[-6,9777],[-138,1]],[[273812,188926],[-11,-3356]],[[273801,185570],[-750,18]],[[229222,193536],[-326,-2723],[39,1456],[287,1267]],[[228195,193399],[229,-262],[-76,-1380]],[[228348,191757],[-206,1882]],[[228490,190446],[-134,1235]],[[228356,191681],[444,2665],[-7,-1493],[-303,-2407]],[[229452,198171],[-21,-806]],[[229431,197365],[-30,-1032]],[[229401,196333],[-338,-1783],[-242,1455]],[[223845,193299],[14,-13095]],[[223859,180204],[1,-1601],[-436,24]],[[223424,178627],[-1056,73],[-103,854],[-232,-1005]],[[222033,178549],[-141,1379],[72,2611],[-322,2960],[-327,370],[-463,2627],[-155,3392],[-155,198],[-278,2896],[-344,911]],[[228348,191757],[8,-76]],[[228490,190446],[-3,-28]],[[228487,190418],[-16,-156]],[[228471,190262],[-133,-1330],[-231,1091],[-604,-774]],[[227503,189249],[-413,475],[-457,1288],[12,1137]],[[226645,192149],[-45,799]],[[226600,192948],[-191,340]],[[268847,195455],[7,-2885]],[[268854,192570],[-198,-1538],[342,-1335],[-115,-2957],[-287,863],[-300,2065],[148,2365],[29,3410]],[[270510,185647],[-1388,-19]],[[269122,185628],[467,3254],[-294,1838],[49,-1786],[-175,307],[19,1799],[-334,1530]],[[225428,193309],[7,-14809]],[[225435,178500],[-810,49]],[[224625,178549],[3,1455],[-769,200]],[[226645,192149],[-378,-2056],[-22,-4642]],[[226245,185451],[-328,-1],[5,-6978]],[[225922,178472],[-487,28]],[[228402,187096],[249,1941],[-343,-3973],[94,2032]],[[227963,184054],[-1438,-46],[-280,1443]],[[227503,189249],[288,8],[201,-2841],[170,164],[-199,-2526]],[[275241,183987],[-902,23]],[[274339,184010],[-271,4],[-3,1573],[-264,-17]],[[274841,189149],[341,-3281],[59,-1881]],[[273051,185588],[-176,-2846],[396,-2050],[334,-3158]],[[273605,177534],[-628,-17],[1,-1639],[-277,-12],[2,-1649],[-824,22]],[[271879,174239],[-2,5717]],[[271877,179956],[1,5695]],[[271877,179956],[-1371,-47]],[[270506,179909],[4,5738]],[[270506,179909],[-2,-2429]],[[270504,177480],[-549,22],[3,3302],[-874,53]],[[269084,180857],[-345,1646],[240,734],[143,2391]],[[274339,184010],[6,-6562]],[[274345,177448],[1,-1562],[-580,-3038]],[[273766,172848],[42,3614],[-203,1072]],[[227901,178788],[-37,1]],[[227864,178789],[9,1767],[307,3881]],[[228180,184437],[70,-94]],[[228250,184343],[-349,-5555]],[[227963,184054],[-251,-4475],[-258,-852],[114,2438],[-407,-1626],[-336,1829],[200,-2389],[-320,-88]],[[226705,178891],[-582,-1386]],[[226123,177505],[-201,967]],[[275241,183987],[343,-5478]],[[275584,178509],[-145,-584]],[[275439,177925],[-1,-410]],[[275438,177515],[-1093,-67]],[[270504,177480],[-3,-3270]],[[270501,174210],[-551,17],[0,-506]],[[269950,173721],[0,-130]],[[269950,173591],[-1,-996],[-273,12]],[[269676,172607],[-254,2093],[-338,6157]],[[224625,178549],[159,-3917],[135,10],[-13,-5059]],[[224906,169583],[-1481,40]],[[223425,169623],[-1,9004]],[[271879,174239],[-1378,-29]],[[223425,169623],[-158,-2061],[-441,-1922]],[[222826,165640],[-276,5054],[-494,3303],[54,2886],[-77,1666]],[[228015,167092],[-166,1925],[-8,2670]],[[227841,171687],[174,-4595]],[[227841,171687],[-52,110],[22,6050],[53,942]],[[227901,178788],[-82,-3294],[22,-3807]],[[227632,166164],[-1163,-35],[-267,256]],[[226202,166385],[-78,3148]],[[226124,169533],[-1,7972]],[[226705,178891],[360,-137],[283,-871],[341,625],[-132,-4582],[-184,-313],[-31,-3271],[212,-1257],[78,-2921]],[[226124,169533],[-934,42]],[[225190,169575],[-284,8]],[[275994,173067],[-63,2]],[[275931,173069],[-9,1]],[[275922,173070],[-37,2]],[[275885,173072],[-22,0]],[[275863,173072],[-26,-266],[-2071,42]],[[275438,177515],[375,-1551],[181,-2897]],[[273766,172848],[-167,-3535],[-1729,10]],[[271870,169323],[9,4916]],[[269950,173591],[197,-1158],[100,-2595],[-427,921],[-144,1848]],[[271870,169323],[-1374,16]],[[270496,169339],[-105,2644],[304,1032],[-368,4],[-94,-803],[-283,1505]],[[275863,173072],[173,-517],[58,-7156],[-69,-4426]],[[276025,160973],[-424,252],[-1823,-64]],[[273778,161161],[-12,11687]],[[273778,161161],[4,-1336]],[[273782,159825],[-1083,-119],[-9,4916],[-814,-70]],[[271876,164552],[-6,4771]],[[270224,168129],[277,-3877],[-101,-154],[-176,4031]],[[271876,164552],[5,-1688],[-271,-31],[4,-1924],[-522,237]],[[271092,161146],[-65,2473],[-474,1226],[-124,2786],[67,1708]],[[225190,169575],[-740,-9780]],[[224450,159795],[-249,-323],[-357,2361],[-856,1101],[54,931],[-216,1775]],[[226202,166385],[-130,-3036],[395,-282],[0,-1591]],[[226467,161476],[2,-5174]],[[226469,156302],[-921,-317],[-542,1946],[-162,1244],[-394,620]],[[227781,162637],[-161,-1600],[-221,-469],[-932,908]],[[227632,166164],[36,-3208],[113,-319]],[[273782,159825],[19,-5212]],[[273801,154613],[0,-3238]],[[273801,151375],[-1312,-39]],[[272489,151336],[-461,1867],[-228,-193],[-395,1936],[-191,1815],[-122,4385]],[[227781,162637],[266,-4006],[-45,-1342],[291,-825],[-120,-2091],[-349,-1094],[6,-1266],[-411,861],[-516,2812],[-434,616]],[[276025,160973],[-123,-6440]],[[275902,154533],[-491,-343],[-1072,2],[-538,421]],[[275036,140768],[-16,1]],[[275020,140769],[-149,-434]],[[274871,140335],[3,-76]],[[274874,140259],[-199,601],[-388,-1467],[-445,274]],[[273842,139667],[-42,3489],[1,8219]],[[275902,154533],[-196,-4031],[-305,-2455],[-117,-2922],[81,-1855],[-329,-2502]],[[275491,142866],[-401,-4417],[-362,-2015],[523,3742],[240,2690]],[[273842,139667],[-118,-686],[-512,-438],[-244,1966],[102,2254],[184,-1641],[296,-971],[166,920],[-246,1771],[-349,146],[-259,2834],[-188,3420],[234,599],[-210,868],[-82,-1356],[-127,1983]],[[267576,216906],[-181,85],[-177,1931],[-468,2015],[37,2408]],[[233545,583163],[470,-1],[0,7179],[546,-266],[367,-1412],[396,-7638],[-21,-1975],[288,-1125],[438,-310]],[[249572,564554],[-1138,-3158],[-1287,-1848],[-816,-1938],[-809,-2999]],[[245498,568091],[407,1054],[402,-2818],[1697,311],[722,-2325],[296,673],[550,-432]],[[244050,541402],[0,-11146]],[[242234,539623],[263,1550],[296,-1143],[509,150],[748,1222]],[[245788,538230],[8,-8017]],[[244050,541402],[832,1557],[406,335],[672,1956],[311,-1206],[-365,-2639],[91,-1053],[-209,-2122]],[[245788,538230],[630,1480],[422,-1520]],[[246668,543604],[119,-491],[-601,-1772],[482,2263]],[[254361,554779],[386,-1343],[-683,-275],[52,-770],[-806,-2719]],[[253310,549672],[-198,1603],[-596,-3]],[[252516,551272],[245,1549],[576,1487],[1024,471]],[[252129,567028],[638,1075],[-743,-3451],[-872,-1783],[142,-551],[-582,-1037],[-166,1627],[1583,4120]],[[252473,548019],[214,-3207]],[[251344,546531],[128,1308],[717,2354],[12,-1815],[272,-359]],[[253310,549672],[-511,-4124],[-63,2158],[-507,612],[-28,1852],[315,1102]],[[256406,536544],[0,-6253]],[[256406,530291],[-348,-1],[-4,-3236],[-348,4]],[[253820,544308],[634,-392],[509,-1456],[171,-1720],[674,-4188],[598,-8]],[[257802,530286],[-1396,5]],[[256406,536544],[265,725],[573,-1784],[182,767],[315,-1250],[489,2628],[901,2228],[763,318]],[[258234,522812],[-689,-2000],[491,3712],[-698,206],[-249,-2795],[-468,641],[-361,-1504],[-267,-2116]],[[255011,510600],[-35,-2168],[-370,-346]],[[254678,502703],[346,2967],[570,1286],[473,4864],[330,489],[143,1898],[235,8],[-572,-6086],[-44,-1740],[-320,-1641],[-154,-2072]],[[266291,525881],[-781,769]],[[265510,526650],[490,1336],[-121,1234],[374,-217],[304,-1926],[-266,-1196]],[[264534,537282],[192,-119],[-169,-2149],[-263,1387],[240,881]],[[264771,527003],[-281,49]],[[264490,527052],[-48,-2]],[[264442,527050],[-19,1612],[-346,2],[-1,1612],[-2086,1],[-3,1628],[-347,-5]],[[261640,541399],[788,243],[-207,-1485],[-13,-3740],[568,-816],[557,744],[103,-1308],[702,1641],[226,-1325],[215,-3355],[-110,-1628],[279,286],[140,-1587],[477,-2056],[-594,-10]],[[263518,523913],[579,-847],[-387,-679],[-192,1526]],[[264442,527050],[-1182,1276],[-230,-2015],[-35,-1966],[-708,3125],[-1172,1762],[-254,-167],[-506,-2455]],[[260355,526610],[-463,132]],[[263048,514103],[-754,-309],[-259,1732]],[[262035,515526],[484,1050],[-316,238],[-233,2467],[498,2959],[580,1140]],[[261221,512584],[62,1278],[458,1591],[294,73]],[[260809,522684],[123,-2641],[-358,-487],[235,3128]],[[259619,509895],[-266,697],[189,946],[77,-1643]],[[260516,504530],[-484,-12]],[[259309,504590],[9,2168],[372,1310],[375,-215],[515,4334],[143,-2358],[-221,-3319],[14,-1980]],[[260516,504530],[127,-152],[342,4121],[-150,-4481],[235,2088]],[[266792,512571],[208,-2839],[-312,126],[-6,-2477],[298,-1301]],[[266298,493142],[-35,-2125],[-276,-1072],[-460,40],[-182,-1553]],[[266573,485031],[572,4601],[580,621],[347,1131],[524,-1430],[297,-2969],[77,-2649]],[[269259,474627],[236,-3607],[-157,-1606],[-122,-4884],[-353,83],[-169,1029]],[[268236,461261],[-80,-1234],[-498,-1330],[-248,-2669],[-87,-2521]],[[267323,453507],[-275,1112],[-676,-160]],[[267323,453507],[-736,-4608],[-57,-1020]],[[276369,283639],[51,-2897],[155,-81],[-317,-2464],[-721,-1619]],[[275537,276578],[-263,-628]],[[275274,275950],[-210,1997]],[[277898,288714],[355,-1067],[-412,-2026],[-248,443],[-747,-4416],[-356,-321],[309,2769]],[[64909,48285],[735,-2763],[362,-391],[488,-1483],[555,-3196],[-25,-2084],[244,11],[54,-1736],[492,-2286],[-474,-3219],[-428,-1373],[-453,-186],[-605,-2496],[-405,-3858],[-627,2125],[-103,1501],[89,4218],[-292,5441],[-196,1715],[345,2265],[317,3334],[-187,1774],[-23,2014],[137,673]],[[55028,84760],[233,-813],[-51,-4138],[-317,-1724],[-531,856],[-341,1190],[-70,1626],[168,1566],[392,1359],[517,78]],[[61944,65038],[-272,274]],[[61944,65038],[565,-356],[-456,-1900],[-525,1012],[-688,11],[160,2281],[672,-774]],[[61752,60573],[246,-292],[244,-1961],[-82,-859],[-351,-533],[-258,3324],[201,321]],[[62904,54516],[45,-1246],[-351,-599],[9,965],[297,880]],[[62814,62495],[340,-2480],[439,903],[263,-353],[320,-1916],[312,-600],[35,-1559],[-160,-1021],[-712,-1316],[-391,412],[-54,3221],[-459,616],[-172,1326],[59,2293],[180,474]],[[58972,75138],[409,-3430],[-19,-1219],[215,21],[315,-3031],[-404,-786],[-271,1419],[-580,-706],[-493,5221],[435,169],[393,2342]],[[53118,80469],[25,-1794],[-233,-683],[-126,-1603],[-75,1953],[409,2127]],[[996993,632383],[817,-1163],[91,-906],[715,-2639],[-621,1211],[-350,1710],[-879,1732],[227,55]],[[949,635992],[99,-1643],[-281,619],[182,1024]],[[7983,636500],[-23,-2275],[-307,-73],[-67,2101],[397,247]],[[8254,636861],[430,-729],[-177,-971],[-343,386],[90,1314]],[[8791,637399],[79,-1229],[-422,751],[343,478]],[[2943,637533],[355,-28],[110,-1138],[762,-730],[-472,-573],[-84,-1947],[-423,-823],[-299,1293],[442,1083],[-738,1716],[347,1147]],[[5406,633632],[-183,-597],[-329,1038],[-855,-380],[1115,1264],[256,737],[15,1940],[429,-501],[-230,-1194],[22,-1773],[-240,-534]],[[996376,638802],[311,-785],[-243,-852],[-68,1637]],[[7153,639094],[-123,-3160],[551,52],[-111,-1993],[-622,-692],[-248,-1116],[-148,1715],[-276,-2445],[-150,1181],[346,1636],[-142,1180],[574,-355],[-294,2578],[643,1419]],[[999634,639522],[333,-975],[-327,-1865],[-356,430],[-110,1601],[460,809]],[[8394,641129],[361,-843],[-150,-1151],[-356,-113],[145,2107]],[[993962,641501],[134,-1164],[-301,-1591],[5,-1345],[-561,-90],[-112,-1517],[-310,1266],[482,1562],[297,123],[366,2756]],[[15681,641867],[-88,-644],[551,-599],[499,441],[600,-278],[-1374,-850],[-663,468],[-396,-613],[-512,1117],[346,752],[243,-725],[794,931]],[[18716,646240],[355,-1060],[-312,-984],[-542,-452],[86,1790],[413,706]],[[13937,646817],[445,-1871],[-209,-1713],[-378,-563],[294,-1046],[-846,-839],[-955,-1616],[-414,666],[-938,-680],[1039,1800],[664,138],[756,1388],[293,1606],[-346,796],[246,1637],[349,297]],[[983194,648582],[-58,-2897],[-305,734],[-723,157],[686,1802],[400,204]],[[23639,652034],[277,-453],[-113,-1345],[-515,-1146],[-83,1789],[434,1155]],[[26147,655623],[246,-1330],[-168,-813],[-713,1494],[635,649]],[[980646,657671],[765,-147],[436,-2390],[463,-235],[-708,-1136],[-317,775],[-432,-1614],[-470,872],[165,1668],[-516,-336],[77,1140],[-541,-71],[552,1545],[526,-71]],[[28035,654543],[906,4624],[-94,1472],[527,2186],[747,66],[-272,830],[81,1805],[502,2014],[613,648],[607,-970],[-156,-2372],[-1193,-2675],[-517,-3466],[-1751,-4162]],[[36357,673363],[-391,-2467],[-196,1415],[587,1052]],[[34797,676523],[213,-3286],[496,2734],[387,-123],[69,-1817],[-313,-391],[-520,-2697],[580,1301],[182,-1595],[-673,-869],[-156,-1540],[-280,592],[46,-1877],[-401,265],[-1841,-3579],[-465,-1377],[-652,1241],[1054,2460],[1094,1206],[-224,1758],[468,338],[-151,1493],[938,464],[-875,524],[-340,2033],[352,1704],[1012,1038]],[[26197,724966],[756,-263],[-288,-1191],[-468,1454]],[[25148,736553],[-442,-1993],[-390,1138],[832,855]],[[39421,678834],[125,-1114],[-550,-30],[-134,746],[559,398]],[[36825,680387],[730,-1661],[-578,-1781],[-479,150],[-104,2381],[431,911]],[[38083,681762],[-169,-1483],[356,-62],[-384,-1861],[-336,2175],[180,1214],[353,17]],[[45571,685391],[688,-1487],[-645,-37],[-43,1524]],[[46952,694607],[166,-1890],[-229,-1028],[-301,1739],[364,1179]],[[42853,695877],[635,14],[266,-1568],[335,-4089],[373,437],[214,-1478],[-487,52],[-124,932],[-254,-1722],[-453,-779],[-609,441],[-827,-359],[-641,-1585],[-62,-1249],[-802,-1375],[-569,491],[-279,2148],[72,1348],[583,1047],[405,3984],[357,1064],[527,-514],[982,2495],[358,265]],[[48297,698203],[503,-1314],[-281,-972],[-458,2014],[236,272]],[[54719,699114],[35,-1698],[-430,-1611],[395,3309]],[[53768,699716],[-57,-3065],[-690,-2054],[16,3270],[392,-758],[85,2465],[254,142]],[[52387,701641],[19,-2213],[-523,1390],[504,823]],[[51366,702388],[94,-1872],[270,219],[343,-2313],[-187,-1094],[-921,1412],[40,2470],[361,1178]],[[52632,703466],[224,-1137],[-482,351],[258,786]],[[56428,729876],[0,-164]],[[56428,729712],[1,-1271]],[[56429,728441],[-376,-902],[0,-1576],[-688,44],[0,-1691],[-885,0],[-1,-1609],[-854,-47],[-10,-3153],[262,-17],[11,-6313],[-174,-1752],[845,0],[0,-4665]],[[54559,706760],[-221,3584],[-465,487],[-226,-1331],[-227,543],[-128,-1791],[-942,-1954],[-501,-2558],[-203,1961],[-156,-2246],[-321,-160],[-388,1335],[-213,-1550],[-896,-1527],[-526,87],[101,2154],[312,2133],[-635,501],[-315,-1612],[38,-2336],[-533,-3433],[-423,296],[235,-2134],[-319,-953],[-328,1503],[-197,-2494],[-614,575],[-124,3742],[-387,952],[-195,-964],[305,-1471],[135,-4091],[-322,1731],[-87,-1650],[-581,-104],[-229,2389],[-557,961],[-44,-1927],[533,-1473],[-911,-2585],[204,4150],[-79,1484],[293,977],[934,318],[218,2402],[379,1261],[396,-106],[-126,1804],[846,4218],[1241,3631],[1202,1199],[764,131],[636,709],[-429,-1919],[670,-3198],[315,-565],[-272,3531],[664,-282],[156,-1434],[616,-516],[78,1328],[-802,1925],[-146,1183],[588,5125],[1475,4921],[1402,2354],[1201,3895]],[[131839,702693],[478,-1815],[-261,-3590],[-338,3444],[121,1961]],[[133475,712615],[472,-2306],[365,-3908],[-109,-3956],[-237,-2723],[-412,-1256],[-725,1926],[513,3110],[-666,-3077],[-842,2748],[552,2915],[-268,716],[-19,1677],[519,1253],[-147,1905],[1004,976]],[[60956,762087],[579,2152],[207,3039]],[[61742,767278],[1861,118],[1,-5313],[-2648,4]],[[51410,765657],[-525,-4199],[-534,-542],[50,2808],[1009,1933]],[[50361,766039],[-741,-1613]],[[49620,764426],[-84,1634],[825,-21]],[[50463,766367],[0,3048],[447,4],[153,1512],[-4,3289],[475,13],[-1,1618],[474,68],[155,1546],[1,3271],[484,-4],[8,3225],[637,43],[12,3203],[445,89],[14,3067],[182,1579],[477,25],[0,3225],[493,-12],[187,1563],[6,3225],[507,4],[7,1610],[466,16],[197,3194],[2958,4],[8,-1469],[509,-23],[-9,1634],[498,0],[0,1661],[983,-7],[4,-1637],[3363,-86]],[[64599,804865],[6,-22904],[-455,-40],[-4,-1619],[-951,22],[-1,-1609],[-472,18],[-264,-3260],[-959,50],[-1,-1622],[-475,33],[8,-3299],[224,-22],[8,-3864]],[[61263,766749],[-1094,-1969],[-1642,-2596],[-488,694],[-100,1404],[-595,1396],[200,3649],[-346,-1592],[-442,-553],[-70,-2804],[-205,-87],[353,-4364],[-131,-1596],[-719,558],[-940,6038],[-710,1488],[71,1756],[-303,-417],[-191,-2035],[-311,-514],[-282,1932],[-662,238],[-84,2398],[-285,617],[-1091,-3021],[-733,-1002]],[[61033,724292],[684,-545],[-930,-219],[246,764]],[[71633,804882],[114,-1587],[1,-6528],[-370,-12],[-9,-6508],[-350,-18],[4,-6265],[-356,2],[-2,-1646],[-479,-11],[4,-3276],[-475,-9],[9,-1643],[-816,-49],[14,-3240],[-942,-29],[5,-3278],[149,-20],[-2,-6355],[154,-1634],[924,0]],[[69210,762776],[7,-3471],[-453,-1843],[-736,-1056],[-774,-256],[-138,-1018],[-764,-799],[4,-5901],[-324,-916],[-689,-542],[-206,-2415],[-365,-3],[219,-1278],[-365,-813],[-334,533],[-169,-1391],[-664,236],[-689,-3490],[-377,-3219],[1107,-39]],[[63500,735095],[-534,-3229],[-498,991],[-283,-2643],[-165,1250],[-876,-3730],[-513,1840],[-93,-1738],[-418,-1467],[297,-1220],[-476,-272],[-290,1208],[-838,-1435],[-237,-1294],[835,518],[-97,-1512],[-773,886],[47,-1061],[-586,328],[-1008,-4054],[729,1416],[658,-1055],[-890,-5668],[-359,2476],[4,-2388],[-569,564],[-201,-1660],[-1170,-915],[-208,-1647],[-122,1992],[-209,-168],[97,-2311],[-195,-2337]],[[56429,728441],[768,-812],[-200,4018],[209,1634],[848,4197],[641,1528],[416,1983],[586,1663],[449,-1864],[-119,2914],[-249,-206],[-35,2059],[292,6723],[196,1491],[338,171],[-417,2931],[211,2814],[593,2402]],[[61742,767278],[-123,1251],[-356,-1780]],[[64599,804865],[4288,-12],[2746,29]],[[65699,709069],[-504,744],[538,1338],[-34,-2082]],[[70400,724037],[-204,-1087],[-508,-19],[712,1106]],[[68718,724702],[-249,-2013],[-521,-1694],[196,2303],[574,1404]],[[69851,724354],[-436,-1711],[-363,957],[373,1310],[426,-556]],[[72263,735888],[654,-276],[179,-1133],[-749,-602],[-369,-1913],[-213,2136],[498,1788]],[[71587,750055],[468,-1112],[218,-1969],[-712,1537],[26,1544]],[[72121,750653],[752,-1609],[143,801],[402,-896],[-319,-1580],[498,-23],[268,1873],[864,-1804],[-645,-1900],[256,-24],[-10,-2224],[878,340],[-525,-3652],[-488,209],[-628,1356],[-204,-635],[516,-1217],[-216,-2411],[-309,-183],[-530,1368],[-251,-544],[428,-942],[-369,-902],[-533,226],[-638,-2935],[-448,348],[322,-1679],[-993,-4198],[-693,-436],[211,1779],[606,2267],[-320,-122],[143,1970],[-433,-1663],[-186,436],[448,2279],[-693,794],[-670,-679],[120,-1420],[344,1135],[575,213],[-523,-3914],[-670,1507],[-38,3723],[-597,1836],[85,2427],[257,1668],[757,2434],[973,102],[63,-1862],[628,-4462],[-147,2063],[79,2795],[-204,1554],[706,-247],[-928,1791],[8,1470],[588,1559],[419,-1165],[70,-2442],[174,2478],[786,-908],[-91,1984],[527,-1432],[-621,2635],[26,690]],[[72294,752633],[298,-243],[384,-2001],[-771,1279],[-524,168],[436,1600],[177,-803]],[[74768,758552],[203,-1494],[403,602],[-149,-1955],[513,1173],[-64,-2164],[-263,-1096],[-683,1715],[168,-2076],[-475,81],[-333,-1022],[-37,2484],[-168,-2623],[-420,-227],[-2,-1319],[-1063,2147],[-114,1830],[590,-260],[-304,1148],[155,946],[718,-477],[-72,2281],[417,1422],[495,-330],[341,-2521],[144,1735]],[[73815,761335],[865,1186],[-376,-3031],[-417,471],[-72,1374]],[[72144,766667],[-256,-165],[-279,-2562],[-649,-1819],[-613,-91],[-174,-2213],[-343,-43],[106,-2403],[-387,-650],[215,-848],[-602,-2221],[-49,-1387],[-356,1779],[54,-1574],[-325,275],[-479,-1769],[-793,237],[-111,-2260],[-819,-2014],[-130,-1038],[-600,882],[68,-2347],[-396,-579],[9,-1505],[-775,384],[-35,-2432],[-520,654],[-902,-2724],[-22,-1053],[573,813],[-196,-1902],[142,-997]],[[69210,762776],[1368,8],[-5,1620],[1015,10],[-7,2250],[405,-14]],[[71986,766650],[158,17]],[[124853,753145],[-549,-606],[-394,-1297],[-255,1284]],[[123655,752526],[58,1721]],[[123713,754247],[93,682]],[[123806,754929],[27,-1188],[798,113],[222,-709]],[[124293,757146],[749,-2547],[-1014,763],[-164,962],[429,822]],[[124853,768717],[211,-1953],[1165,-2450],[370,-2181],[920,-3383],[-235,-797],[808,-4371]],[[128092,753582],[-945,-4191]],[[127147,749391],[-329,-1604]],[[126818,747787],[-267,-754]],[[126551,747033],[-348,2791],[-600,2743],[-36,3375],[303,711],[-120,1531],[-569,-3645],[-841,2824],[-590,340],[-226,2827],[-376,2538],[-6,5749]],[[123142,768817],[1711,-100]],[[122507,768865],[608,-35]],[[123115,768830],[94,-1237],[-284,-3190],[-329,1968],[-89,2494]],[[125028,711856],[427,56],[-445,-1309],[18,1253]],[[130215,719801],[107,-2063],[756,-2315],[-338,-626],[254,-2100],[-618,-805],[-298,1242],[209,718],[-487,1319],[-280,-690],[-164,3076],[325,413],[163,1718],[371,113]],[[129378,721918],[249,-1134],[-115,-2167],[-590,-788],[-540,1882],[182,1807],[814,400]],[[130830,716910],[-489,1840],[74,2204],[-143,1693],[379,-778],[302,-2036],[326,196],[295,-2757],[-436,-1557],[-308,1195]],[[128815,728558],[1034,-4174],[-772,-1663],[-360,265],[62,2359],[-148,3099],[184,114]],[[126153,727422],[-66,1104],[446,-558],[-459,-4949],[-272,-4761],[208,396],[-146,-3602],[-196,615],[-111,3651],[-137,-5807],[-277,1260],[-136,3641],[146,2921],[525,49],[-191,2101],[-591,341],[101,963],[-332,2219],[-28,2428],[338,-759],[186,2187],[562,-1046],[430,-2394]],[[126096,733937],[1281,-1597],[902,-116],[343,-1458],[134,-4906],[-199,-1131],[-500,1925],[-370,3179],[207,-4437],[380,-383],[50,-1490],[-321,-1688],[-659,1031],[14,-897],[-617,-308],[-202,4527],[180,2218],[-303,119],[-132,1682],[-636,2448],[448,1282]],[[130329,738612],[336,-2561],[-336,-2230],[901,-864],[-218,-3192],[698,-1259],[105,-3850],[709,243],[1430,-3886]],[[133954,721013],[215,-2257],[-268,-1573],[-450,60],[-523,-1243],[217,-2328],[-367,57],[-534,1418],[-526,-1313],[-53,-1739],[-415,-1188],[272,-1077],[-70,-1918],[-374,-1019],[-347,1785]],[[130731,708678],[461,1375],[54,2694],[202,40],[-10,3932],[574,625],[-464,526],[-186,2188],[-506,719],[-71,1280],[-436,1390],[254,2614],[-500,-62],[-115,1369],[-779,1724],[-462,2944],[356,-607],[-84,2467],[-160,-1289],[-1099,1537],[-631,1283]],[[127129,735427],[607,1116],[481,-957],[392,2691],[952,1778],[768,-1443]],[[120135,747679],[-911,-75]],[[119224,747604],[-113,-9]],[[119111,747595],[-67,-5]],[[119044,747590],[175,3136],[916,-3047]],[[118824,752542],[436,-1596],[-323,-3068],[-279,1217],[166,3447]],[[127129,735427],[-11,1829],[1069,643],[-784,626],[-296,2733],[133,1559],[-543,1078],[247,1825],[1153,-2770],[-708,2710],[-430,722],[26,2218]],[[126985,748600],[107,525]],[[127092,749125],[55,266]],[[128092,753582],[282,-2841],[560,-2991],[880,-6389],[515,-2749]],[[124853,753145],[18,23]],[[124871,753168],[418,537]],[[125289,753705],[-148,-676],[909,-5880],[23,-2256],[-213,92],[-809,7066],[78,-1904],[-258,510],[590,-4994],[507,-2211],[51,-2144],[-521,218],[645,-2298],[-310,-1473],[-533,1666],[53,-3416],[-405,-716],[-196,-1446],[-544,-1275],[-190,2104],[50,2518],[367,839],[-3,1567],[-686,5962],[69,2065],[-286,4181],[126,722]],[[120491,750760],[-344,-2996]],[[120147,747764],[-1106,4388],[166,2241],[347,-765],[278,1062],[445,-194],[427,1507],[913,-2103],[-367,-2249],[68,-1158],[543,2952],[316,-1048],[502,33],[561,-2130],[-236,-1608],[-622,1179],[757,-2767],[-774,-493],[-454,1349],[-1420,2800]],[[123713,754247],[-95,-1670],[-341,1775],[-156,4018],[685,-3441]],[[114794,765672],[1165,1936]],[[115959,767608],[1943,4721],[682,110],[264,1748]],[[118848,774187],[1665,-1619],[529,-1279],[326,-2198],[-480,-4537],[582,-819],[409,-1530],[231,-2153],[-489,-11]],[[121621,760041],[49,-2296],[-405,961],[-824,-873],[206,2553],[-684,5535],[900,1274],[-791,545],[-394,2927],[235,-3862],[-191,-2032],[-886,1580],[-101,2460],[-280,-1505],[-688,1343],[-390,-1137],[761,-460],[670,-1418],[-346,-493],[774,-1580],[-470,-1770],[538,847],[342,-513],[455,-4297],[-639,-1196],[-13,909],[-621,-1129],[-298,1069],[41,-2621],[-717,2666],[-654,322],[-1470,4078],[-936,3744]],[[122912,775799],[-900,-103]],[[122012,775696],[-90,222]],[[121922,775918],[-936,7001]],[[120986,782919],[676,1289],[685,-1898],[571,-2465],[-202,-2534],[196,-1512]],[[123142,768817],[-27,13]],[[122507,768865],[-495,6831]],[[122912,775799],[198,-1219],[722,-593],[59,-1056],[546,-1130],[416,-3084]],[[121922,775918],[-145,-2315],[-358,301],[513,-2663],[-50,-2481],[722,-6540],[-70,-1018],[302,-3962],[-98,-2100],[-512,-56],[-321,1677],[-284,3280]],[[118848,774187],[34,3836],[479,-24],[183,1767],[-318,752],[1118,1161],[642,1240]],[[120877,738496],[389,-2216],[137,-2451],[-166,-1459],[-605,-380],[11,6413],[234,93]],[[123928,718235],[-2,-1610]],[[123926,716625],[-393,1362],[-697,5371],[278,1647],[-447,-368],[-256,2377],[77,1437],[-515,245],[-59,2351],[446,1679],[-481,1224],[98,2800],[-464,-184],[-452,2135],[588,60],[-326,842],[146,1989],[629,565],[-77,-1197],[395,257],[557,-1676],[490,7],[-81,-2391],[463,-6886],[222,-4906],[-139,-7130]],[[120135,747679],[12,85]],[[120491,750760],[1455,-3708],[395,-1834],[349,1018],[504,16],[258,-5147],[-689,-337],[-265,1057],[-1794,4644],[718,-3300],[33,-2583],[-443,-1626],[-365,435],[-451,2590],[538,-1592],[-959,3723],[84,790],[-635,2698]],[[40063,839903],[770,-536],[-859,-2772],[-125,3412],[214,-104]],[[40964,844347],[883,-2223],[79,-1915],[441,-2171],[-40,-1740],[-704,2418],[-1774,1537],[90,1825],[445,2301],[580,-32]],[[42065,848667],[903,-976],[563,-1663],[-706,-1821],[-339,-2285],[-564,-2],[-711,2401],[-620,812],[14,1297],[651,1761],[809,476]],[[46153,848898],[568,1],[10,-1610],[529,1],[18,-1676],[524,-12],[-4,-1547],[3176,0]],[[50974,844055],[-395,-2136],[-20,-6445],[-126,-25],[-6,-6437],[383,-5],[-10,-4748],[1058,-39]],[[51858,824220],[180,-1451],[604,-215],[-1456,-2220],[-980,-2588],[-900,-571],[-1213,-1602],[-1234,662],[-494,709],[-1854,-1999],[-659,648],[-690,-2756],[-941,669],[31,-2392],[-389,-2165],[-92,-1943],[-1046,-1523],[-746,776],[-935,-1159]],[[39044,805100],[-172,1112],[664,830],[-1128,3201],[-13,-2176],[-520,182],[-234,2567],[116,849],[-614,571],[-207,2123],[418,1187],[-481,1394],[-546,-1187],[-95,2644],[1073,795],[-526,599],[-413,1924],[1380,582],[-440,3023],[251,2549],[1109,5289],[619,2101],[570,-119],[933,3977],[823,-896],[965,-4031],[-209,4581],[-457,1793],[12,1197],[848,640],[78,1725],[725,1785],[524,-1399],[778,464],[610,2670],[668,1252]],[[46896,791982],[261,-1285],[-270,-665],[9,1950]],[[36192,795959],[180,-1952],[581,363],[562,-947],[-123,-4413],[448,-2402],[-1265,-1162],[-486,-2126],[-685,2000],[-493,-131],[-1055,2670],[-324,-89],[-644,1570],[-206,2197],[272,858],[1286,-625],[56,1283],[1008,2111],[905,-187],[-17,982]],[[17304,799216],[126,-1745],[804,-2158],[616,-183],[448,-1365],[-1028,65],[-1000,3083],[-322,373],[356,1930]],[[39654,805231],[136,-1106],[641,589],[-101,-1482],[649,41],[525,-808],[139,-1889],[-482,-1833],[-426,-534],[213,-1308],[-375,-571],[-319,-2845],[-422,135],[-775,2413],[439,1980],[-612,-774],[-640,997],[1252,2977],[-199,1359],[401,589],[-44,2070]],[[51858,824220],[3536,-32],[10,1609],[2034,-5],[-4,1617],[4083,203],[1,-1838],[5893,-29],[3388,4],[-14,1716],[962,59],[3,1571],[903,-11],[192,1616]],[[72845,830700],[-3,-16190]],[[72842,814510],[-1303,45],[-14,-5422],[124,-21],[-16,-4230]],[[50463,766367],[-102,-328]],[[49620,764426],[-139,-1468],[-1061,-1905],[-97,1293],[-1035,446],[862,1205],[617,1650],[-307,160],[-238,3846],[703,1138],[-113,1059],[-716,-1213],[-383,2903],[195,2895],[687,2210],[-441,2484],[-273,3082],[-346,1471],[-416,3726],[137,1138],[-767,3115],[393,1259],[285,3791],[-129,577],[-402,-4189],[-434,-1001],[311,-2534],[-69,-3036],[-406,-1085],[-1628,-2447],[-1520,-851],[-1012,789],[-258,2086],[267,429],[-760,1956],[-694,3087],[-87,1177],[383,1597],[-110,1291],[397,379],[-183,1458],[364,50],[277,1573],[411,342],[228,1752],[702,-118],[-16,-3249],[425,240],[607,2498],[-384,1562],[-1050,843],[629,147],[400,1001],[-600,267],[-481,-1345],[-125,3490],[-458,-1834],[195,-1307],[-1242,-569],[-216,1520],[-538,-627],[-174,927],[-743,-457]],[[91002,847056],[9,-3988],[145,-1795],[8,-11469],[-1548,26],[56,-1646],[46,-12769],[-726,14],[-2,-927],[-3493,32]],[[85497,814534],[-2026,-32],[-141,995]],[[83330,815497],[-404,601],[-735,-459],[-672,-2022],[-283,-2604],[-1335,126],[-328,2166],[-15,-1517],[-1058,-1636]],[[78500,810152],[-4,1152],[-1002,21],[-4,3164],[-4648,21]],[[72845,830700],[0,8044]],[[72845,838744],[3088,-82],[10,1347],[6637,9993],[4096,-25],[12,2587],[2946,186]],[[89634,852750],[1353,59],[15,-5753]],[[86653,868811],[757,-1570],[2145,14]],[[89555,867255],[87,-6323],[-8,-8182]],[[72845,838744],[1580,8236],[-1,2534],[-536,-83],[-516,926],[-152,2433],[36,3161],[1704,93],[24,3109],[496,-4],[22,3291],[619,95],[97,1432],[588,58],[151,-985],[625,-419],[1536,6612],[4538,-401],[2997,-21]],[[85497,814534],[-37,-10730],[-760,-83],[1,-2090]],[[84701,801631],[-828,17],[9,1987]],[[83882,803635],[-435,1931],[-460,-308],[-1062,1669],[-902,2471],[707,3049],[1129,2748],[471,302]],[[97244,787641],[-970,-3439],[63,995],[907,2444]],[[87222,789210],[-181,-1772],[-422,-410],[603,2182]],[[86635,788749],[223,2200],[232,-961],[-455,-1239]],[[86658,791573],[-369,-3115],[-330,1166],[699,1949]],[[89188,794370],[360,-14],[-243,-1523],[503,637],[-806,-2688],[-814,-3927],[156,-1241],[-672,-1392],[-694,-249],[130,1479],[1249,4306],[686,2953],[145,1659]],[[86769,795107],[-71,-1859],[-324,817],[395,1042]],[[90857,797012],[511,-366],[-57,-1276],[666,500],[124,-712],[-950,-1371],[-533,-1467],[-218,828],[596,1566],[-651,-377],[-25,892],[537,1783]],[[87021,792777],[237,3392],[601,-294],[-413,-4995],[-425,1897]],[[84910,786500],[234,323],[68,9012],[-286,660]],[[84926,796495],[584,1623],[519,-1019],[523,1881],[285,-1347],[30,-1922],[-996,-2869],[605,-1030],[-717,-2215],[-215,-2677],[-634,-420]],[[92921,798974],[-9,-545],[-1147,-2071],[-222,1208],[1378,1408]],[[88339,800844],[383,-576],[-511,-771],[128,1347]],[[86236,802132],[283,-1843],[-421,600],[138,1243]],[[84951,797243],[-1,3142]],[[84950,800385],[697,2105],[-36,-2998],[388,2501],[244,-2671],[-390,-1481],[-418,814],[-484,-1412]],[[86676,805210],[325,-2117],[-624,-286],[299,2403]],[[84951,801507],[-250,124]],[[91002,847056],[976,234],[63,648],[2682,18],[0,-1651],[1595,-292],[4037,-37],[350,-2273],[-454,-2972],[436,-1269],[-270,-2840],[732,-302],[295,1951],[1174,-336],[-15,-1588],[537,-65],[-10,-1550],[466,-24],[-40,-6434],[413,-697],[4,-4218],[2303,15]],[[106276,823374],[-1,-28114]],[[106275,795260],[-589,822],[-1600,-31],[4,1610],[-3847,-34],[-2014,-8871],[13,-855]],[[98242,787901],[-1021,2713],[47,775],[-1005,-33],[-276,1650],[-683,426],[369,3141],[18,2628],[-612,-1565],[-178,-1401],[-659,-2022],[-457,1505],[-714,1071],[-573,-359],[943,3800],[-1116,-764],[259,1691],[-925,-1558],[532,2101],[-1009,-888],[-677,62],[-94,801],[1014,956],[446,1115],[-1050,-704],[-516,1833],[317,3213],[1053,57],[-177,903],[-744,-283],[-1116,-3389],[-359,1426],[-193,-1467],[-586,-1116],[-376,408],[6,3472],[-402,-3497],[-547,-1350],[-586,2075],[324,2590],[504,2086],[123,1742],[-770,-3524],[-194,-1549],[-275,1236],[-413,-4415],[-943,-1986]],[[71670,777076],[233,-1112],[-578,-408],[345,1520]],[[79473,776784],[-279,-2074],[90,2387],[189,-313]],[[75949,796147],[-337,-1236],[224,2635],[113,-1399]],[[84951,801507],[-1,-1122]],[[84951,797243],[-25,-748]],[[84910,786500],[-1245,807],[240,1474],[-371,-167],[-386,-2038],[-206,3702],[-29,-2396],[-481,-1666],[-173,-1732],[-183,2620],[-197,-5326],[-848,3357],[-39,-1545],[420,-800],[-371,-1104],[-68,-1569],[-635,-858],[208,3841],[-673,-5212],[-363,1475],[21,-2088],[-388,-20],[-756,-4011],[-74,2111],[-347,-2231],[-660,934],[-173,-859],[-799,-887],[-18,1049],[-623,715],[205,2741],[546,1203],[718,23],[17,1317],[737,963],[-66,842],[764,2927],[-372,25],[-1227,-2967],[-387,246],[-629,2275],[460,4880],[786,3381],[112,2715],[231,475],[102,3025],[-410,3227],[967,1254],[994,2799],[846,1832],[515,-1977],[448,-352],[796,1052],[346,-814],[1490,-887],[200,-646]],[[78500,810152],[-529,-2655],[-889,-709],[-891,-2927],[268,-2258],[-402,43],[-550,-1341],[-153,-1302],[-582,-1608],[-230,-3803],[-474,-1479],[-1035,411],[707,-1449],[308,-1771],[-381,-2913],[-287,-680],[-1149,-274],[-159,-989],[796,2],[-164,-2224],[-453,-1068],[-562,296],[227,1314],[-337,1411],[33,-1984],[-398,-2576],[-671,-197],[279,-1966],[-1055,-1700],[-61,-2764],[-263,-488],[166,-2860],[277,1050],[1025,35],[413,-1682],[604,-1164],[58,-1233]],[[106275,795260],[0,-1588],[1343,-1612],[173,1644],[1330,-2348],[801,2853],[1723,322],[-345,-4919],[404,-1740],[964,-1648],[226,-2564],[2839,-9771],[300,-4810],[-74,-1471]],[[114794,765672],[-73,1821],[-694,2541],[-828,1292],[163,1473],[-568,-631],[-1881,4464],[-679,903],[-783,1959],[551,424],[542,2604],[-421,3603],[333,1618],[530,-2532],[164,177],[-480,2635],[-360,838],[-174,-1871],[-463,-2242],[-1472,-2407],[-1544,735],[-1623,2726],[527,2089],[-371,3153],[-375,-856],[437,-1616],[-624,-1272],[-2761,2302],[-2696,-711],[-929,-990]],[[106279,946179],[-1,-49412]],[[106278,896767],[-929,-2457],[-130,-1600],[-1321,-3359],[-907,419],[-783,-1693],[-450,408],[-349,-3372],[-325,-1449],[-654,-436],[-1188,-2953],[58,-3794],[-686,-1988],[-821,654]],[[97793,875147],[-205,2151],[358,3325],[-316,719],[513,846],[-159,1080],[-963,-156],[-338,-900],[-1244,1394],[-886,-1195],[-579,221],[-639,-953],[-286,1202],[374,853],[-1289,1884],[-166,1202],[398,1802],[-514,869],[-625,-618],[-460,-1403],[-1061,-1321],[-1073,-10],[-601,-1159],[-3102,6],[49,-11363],[273,317],[853,-2473],[548,-2656]],[[50974,844055],[10,4776],[492,29],[-58,4949],[548,10],[4,1425],[1103,13],[-17,1635],[486,23],[-3,1682],[645,-27],[-89,4840],[-627,-26],[-61,12986],[569,33],[-9,3217],[557,13],[-4,6309],[539,-18],[-5,4867],[-575,-8]],[[54479,890783],[-5,8105],[1752,-14],[2,3093],[2984,34],[5,6543],[2343,38],[1,-3290],[1183,54],[6,3236],[1194,12],[-3,-1576],[600,61],[1,-1630],[1168,-18],[135,4836],[1805,-29],[9,2778],[1841,-6],[167,1643],[-10,6646],[-437,-20],[-10,1684],[-1234,59],[4,4809],[169,1664],[-624,-6],[17,1606],[-634,-27],[7,1645],[-621,10],[151,4212]],[[66445,936935],[6419,-2],[8315,-30],[7299,18],[3888,6],[-10,9096],[4663,309],[4790,-300],[4470,147]],[[97793,875147],[-1158,-1860],[-1437,-204],[-262,-1096],[-804,-486],[-476,-1291],[-576,796],[-732,-1427],[-433,43],[-568,-2104],[-1792,-263]],[[106278,896767],[-2,-73393]],[[79659,983425],[-186,-1458],[-395,1373],[581,85]],[[66445,936935],[-1,525],[-2610,-23],[-8,1900],[-2541,-186],[-15,1669],[-3117,28],[-4016,91],[-18,1253],[-1129,58],[26,-1250],[-2705,21],[-18,1268],[-1182,3],[11,-1279],[-1329,46],[-7,1283],[-2020,165],[0,-1272],[-4957,-98],[15,-3782],[-2411,70]],[[38413,937425],[-1695,2109],[-331,1545],[-1320,2645],[530,804],[403,3101],[38,5653],[2426,-416],[2905,1304],[905,1071],[1222,2916],[1184,4349],[538,5906],[-329,649],[1231,3476],[365,2385],[846,2065],[658,2577],[521,-1587],[1146,663],[1622,2412],[1470,3493],[882,527],[-302,-3710],[454,-2622],[-62,3461],[380,972],[-494,2433],[-541,46],[1260,3273],[1370,1209],[-523,-1007],[415,-852],[3297,1404],[1421,2118],[704,1933],[1322,4593],[409,808],[627,-1392],[915,-472],[117,-1342],[1298,-97],[153,-1576],[-589,-1821],[-1263,-1269],[606,-390],[-400,-1319],[1303,-54],[456,606],[-100,1673],[703,1368],[285,1695],[248,-1305],[706,928],[682,-1755],[-272,-1975],[1375,-2189],[693,2096],[1211,87],[987,691],[970,-830],[279,-2575],[299,2682],[989,-1071],[-561,-2519],[5,-1532],[1009,-525],[-1482,-681],[2474,169],[-499,-2280],[1552,-378],[326,-1080],[240,1607],[1280,271],[-195,-2495],[904,1573],[845,524],[665,1466],[1963,-425],[886,-1703],[688,50],[281,-1515],[1081,464],[877,-2131],[2068,-1579],[1439,785],[1493,-1038],[433,638],[1637,-3240],[1773,-387],[359,912],[3399,1866],[1396,-1344],[1104,-2165],[387,-1505],[1266,-1051],[1131,-2960],[450,854],[588,-630],[-1,-21362]],[[54479,890783],[-578,0],[-10,-1617],[-6227,42],[-4789,-36],[-1,3200],[-592,6],[0,3250],[-769,-10],[8,6456],[-205,26],[-5,6430],[-226,1957]],[[41085,910487],[1578,205],[202,-2556],[-311,-1225],[120,-2178],[-263,-1028],[215,-1763],[590,-1196],[375,540],[1138,-445],[965,699],[321,-1177],[667,-159],[982,797],[546,-2004],[189,2045],[810,3513],[796,-959],[609,527],[-495,1902],[-1494,1020],[-695,-1271],[194,3234],[-839,3465],[-836,788],[-422,2410],[386,1581],[446,30],[876,-3094],[-154,-2498],[448,-1913],[931,-1944],[1114,1832],[1074,-3146],[1511,331],[131,1770],[-331,2163],[-932,-162],[-460,1241],[-885,-263],[-253,-1860],[-717,-285],[-1099,3517],[232,3056],[871,1516],[-974,1717],[-1121,-1076],[-1026,-153],[-389,1249],[-528,-564],[-2241,1824],[-397,5492],[-677,3578],[-1097,2080],[-2353,5735]],[[46592,855663],[131,-1524],[-910,378],[779,1146]],[[20846,858392],[155,-1918],[1767,-2169],[1395,2514],[527,-232],[506,-1478],[153,-2243],[1093,-1044],[302,-1308],[1483,-327],[899,-741],[-443,-2851],[-1263,664],[-756,-2937],[125,-925],[-599,-398],[122,1508],[-581,2032],[-993,769],[95,1587],[-980,2254],[-777,1164],[-1179,-1499],[-406,-1269],[-853,1089],[-307,2233],[515,5525]],[[40155,909679],[-378,-812],[-1698,-1707],[2076,2519]],[[46153,848898],[640,3384],[128,1617],[836,-1042],[-394,-958],[1743,358],[1080,995],[1027,4968],[-512,5797],[-74,2808],[-783,2760],[-729,205],[138,2075],[1089,-245],[770,2178],[52,1988],[-320,1965],[-760,1982],[-574,-2906],[-898,82],[-597,-1411],[-710,-399],[-196,-1163],[-840,-1617],[-252,-2634],[-446,-1194],[-161,3173],[-951,2836],[-459,-1140],[680,-1515],[9,-1792],[-1367,2870],[-2018,-58],[-2019,-2251],[-807,901],[-2508,1784],[-689,2790],[279,1486],[-107,1445],[-749,1760],[-607,2842],[664,-527],[574,1254],[301,1828],[876,-777],[601,-2903],[499,-323],[640,1345],[-382,828],[-828,195],[-166,-711],[-669,2577],[-1941,1613],[-1534,482],[-1420,2818],[-465,461],[786,2442],[578,22],[0,1593],[845,1741],[325,-909],[992,2481],[418,1888],[1044,1672],[643,-1028],[1215,99],[319,783],[-1041,1553],[504,1846],[720,1193],[1099,506],[107,-679],[853,2834],[831,668]],[[129335,693546],[271,-1010],[-142,-1955],[-385,2864],[256,101]],[[133465,694933],[144,-1619],[-433,-1116],[-420,1401],[709,1334]],[[129050,698432],[385,-3029],[-187,-665],[-381,868],[233,1060],[-50,1766]],[[128271,699419],[264,-2151],[-73,-1620],[899,-5179],[112,-1526],[-465,199],[-651,4105],[-450,3522],[53,2603],[311,47]],[[132791,699517],[329,-1553],[-33,-2741],[-745,304],[319,2136],[-199,1261],[329,593]],[[127590,701351],[340,-1447],[-98,-960],[-490,-120],[14,2204],[234,323]],[[126995,702605],[287,-601],[-587,-1676],[300,2277]],[[127349,703974],[-507,-707],[254,1440],[253,-733]],[[126549,705223],[400,-333],[-240,-1814],[-293,1014],[133,1133]],[[127577,705503],[192,-1281],[-213,-946],[-252,1520],[273,707]],[[126975,710441],[673,-1209],[-593,136],[46,-1542],[-470,1638],[344,977]],[[127738,711890],[119,-2595],[-286,1329],[167,1266]],[[132951,712558],[-517,-394],[513,1288],[4,-894]],[[127377,716539],[387,-260],[-15,-2495],[-236,369],[-706,-1255],[-240,-1211],[-257,709],[519,3076],[548,1067]],[[126878,720140],[451,-579],[750,57],[384,-2788],[-191,-1469],[383,-1822],[435,266],[497,-1761],[456,-2620],[47,-3196],[267,734],[169,-1960],[371,-1718],[-850,2044],[-552,-1434],[684,586],[160,-2342],[299,487],[445,-2791],[-464,-922],[454,-352],[205,1480],[89,-3147],[-431,-827],[396,-512],[101,-2502],[-398,-59],[421,-1193],[-183,-2479],[-613,702],[-449,4336],[-561,-35],[258,2640],[-329,-710],[132,1822],[-300,-502],[-725,2251],[-725,264],[28,1947],[514,-21],[-361,1667],[236,2818],[-439,-873],[-448,796],[643,3530],[-238,1458],[-150,5160],[-814,306],[-187,1877],[133,1386]],[[133954,721013],[796,-683],[442,-1861],[432,-443],[118,-1890],[501,-841],[396,492],[280,-2136],[-1,-1873],[-414,-2628],[68,-3449],[429,-5569],[-359,-1662],[-236,-2416],[-441,-2702],[-867,-2699],[-213,1932],[-286,-2060],[-269,663],[-207,4192],[490,1686],[-537,-477],[-156,1948],[582,2169],[-76,7458],[-887,5088],[-486,-470],[-72,869],[-950,-2430],[-396,-118],[344,-1651],[-545,-5283],[-588,1664],[-115,2845]],[[312327,9345],[187,-607]],[[312514,8738],[-15,-1699]],[[312499,7039],[-350,67]],[[312149,7106],[17,912]],[[312166,8018],[43,812]],[[312209,8830],[118,515]],[[312084,13310],[6,-1330]],[[312090,11980],[-147,-91]],[[311943,11889],[-157,1158]],[[311786,13047],[223,739]],[[312009,13786],[75,-476]],[[312224,10808],[178,-1249]],[[312402,9559],[-75,-214]],[[312209,8830],[-202,83]],[[312007,8913],[-56,276]],[[311951,9189],[-19,1837]],[[311932,11026],[292,-218]],[[313107,15065],[-14,-2951]],[[313093,12114],[-156,-33]],[[312937,12081],[5,365]],[[312942,12446],[-35,2687]],[[312907,15133],[200,-68]],[[312774,6524],[70,-1308]],[[312844,5216],[-339,-42]],[[312505,5174],[62,1491]],[[312567,6665],[207,-141]],[[312709,10759],[26,-1200]],[[312735,9559],[-333,0]],[[312224,10808],[123,645]],[[312347,11453],[362,-694]],[[315123,11875],[54,-128]],[[315177,11747],[78,-1803]],[[315255,9944],[-163,-1770]],[[315092,8174],[-119,1033]],[[314973,9207],[-63,762]],[[314910,9969],[144,1736]],[[315054,11705],[69,170]],[[314670,11208],[37,-330]],[[314707,10878],[60,-642]],[[314767,10236],[-199,-737]],[[314568,9499],[-72,1129]],[[314496,10628],[174,580]],[[314052,6833],[264,-476]],[[314316,6357],[-19,-702]],[[314297,5655],[-318,75]],[[313979,5730],[73,1103]],[[312567,6665],[-68,374]],[[312514,8738],[144,137]],[[312658,8875],[116,-2351]],[[312942,12446],[-192,449]],[[312750,12895],[-16,-17]],[[312734,12878],[-7,2191]],[[312727,15069],[180,64]],[[313370,8907],[-65,-415]],[[313305,8492],[-209,82]],[[313096,8574],[-80,-28]],[[313016,8546],[-82,705]],[[312934,9251],[27,1093]],[[312961,10344],[345,-244],[64,-1193]],[[313305,8492],[-14,-2847]],[[313291,5645],[-154,398]],[[313137,6043],[-41,2531]],[[314464,12204],[-98,-1501]],[[314366,10703],[-105,-151]],[[314261,10552],[-75,983]],[[314186,11535],[35,729]],[[314221,12264],[129,637]],[[314350,12901],[114,-697]],[[314016,15104],[-14,-2136]],[[314002,12968],[-54,9]],[[313948,12977],[-196,-375]],[[313752,12602],[-27,1006]],[[313725,13608],[25,1394]],[[313750,15002],[266,102]],[[313604,15085],[-4,-1775]],[[313600,13310],[-8,-955]],[[313592,12355],[-45,-184]],[[313547,12171],[-454,-57]],[[313107,15065],[497,20]],[[314264,15070],[-76,-2600]],[[314188,12470],[-186,498]],[[314016,15104],[248,-34]],[[315464,13512],[1,-2420]],[[315465,11092],[-66,434]],[[315399,11526],[-159,1610]],[[315240,13136],[-103,1307]],[[315137,14443],[122,138]],[[315259,14581],[205,-1069]],[[314938,14053],[76,-2457]],[[315014,11596],[-176,-329]],[[314838,11267],[34,2700]],[[314872,13967],[51,292]],[[314923,14259],[15,-206]],[[315375,10330],[63,-1609]],[[315438,8721],[6,-427]],[[315444,8294],[-234,-827]],[[315210,7467],[-116,640]],[[315094,8107],[-2,67]],[[315255,9944],[120,386]],[[313784,8891],[255,-1303]],[[314039,7588],[13,-755]],[[313979,5730],[-242,-150]],[[313737,5580],[-39,3313]],[[313698,8893],[16,19]],[[313714,8912],[70,-21]],[[315465,11092],[-90,-762]],[[315177,11747],[222,-221]],[[313725,9097],[-11,-185]],[[313698,8893],[-269,126]],[[313429,9019],[231,2602]],[[313660,11621],[65,-2524]],[[314361,14911],[31,-1699]],[[314392,13212],[-42,-311]],[[314221,12264],[-33,206]],[[314264,15070],[97,-159]],[[312306,11771],[41,-318]],[[311932,11026],[-107,569]],[[311825,11595],[118,294]],[[312090,11980],[216,-209]],[[312937,12081],[24,-1737]],[[312934,9251],[-27,-13]],[[312907,9238],[-172,321]],[[312709,10759],[41,2136]],[[312505,5174],[-359,-115]],[[312146,5059],[3,2047]],[[314634,8728],[-21,-893]],[[314613,7835],[-92,-362]],[[314521,7473],[-160,1420]],[[314361,8893],[201,525]],[[314562,9418],[72,-690]],[[312295,14639],[81,-1251]],[[312376,13388],[-70,-1617]],[[312084,13310],[211,1329]],[[312007,8913],[159,-895]],[[312146,5059],[-281,903],[86,3227]],[[315210,7467],[60,-308]],[[315270,7159],[221,-1461]],[[315491,5698],[-308,-32]],[[315009,6674],[174,-1008]],[[315009,6674],[81,1384]],[[315090,8058],[4,49]],[[314186,11535],[-217,-675]],[[313969,10860],[-21,2117]],[[312009,13786],[157,1824]],[[312166,15610],[129,-971]],[[313137,6043],[-273,-821]],[[312864,5222],[152,3324]],[[314838,11267],[-131,-389]],[[314670,11208],[-8,753]],[[314662,11961],[28,1346]],[[314690,13307],[76,779]],[[314766,14086],[106,-119]],[[313547,12171],[113,-550]],[[313429,9019],[-59,-112]],[[314973,9207],[-339,-479]],[[314562,9418],[6,81]],[[314767,10236],[143,-267]],[[314776,6994],[-112,-1632]],[[314664,5362],[-367,293]],[[314316,6357],[205,1116]],[[314613,7835],[163,-841]],[[312679,13385],[55,-507]],[[312376,13388],[303,-3]],[[315090,8058],[-314,-1064]],[[314662,11961],[-198,243]],[[314392,13212],[143,200]],[[314535,13412],[155,-105]],[[312679,13385],[-109,1772]],[[312570,15157],[157,-88]],[[313752,12602],[-160,-247]],[[313600,13310],[125,298]],[[314496,10628],[-130,75]],[[313604,15085],[146,-83]],[[315240,13136],[-117,-1261]],[[315054,11705],[-40,-109]],[[314938,14053],[199,390]],[[315043,5450],[-34,1224]],[[315043,5450],[-379,-88]],[[312864,5222],[-20,-6]],[[312658,8875],[249,363]],[[314001,9350],[196,-57]],[[314197,9293],[164,-400]],[[314039,7588],[-38,1762]],[[313784,8891],[217,459]],[[313969,10860],[-244,-1763]],[[313737,5580],[-446,65]],[[314261,10552],[-64,-1259]],[[312166,15610],[404,-453]],[[315579,8262],[226,-901]],[[315805,7361],[-150,-1087]],[[315655,6274],[-385,885]],[[315444,8294],[135,-32]],[[314849,14669],[-83,-583]],[[314535,13412],[108,1312]],[[314643,14724],[206,-55]],[[318309,12804],[169,-865],[-544,829],[375,36]],[[316158,12800],[-233,-1215]],[[315925,11585],[-15,-114]],[[315910,11471],[23,1757]],[[315933,13228],[225,-428]],[[315714,13935],[-106,-832]],[[315608,13103],[-144,409]],[[315259,14581],[455,-646]],[[315655,6274],[-164,-576]],[[315910,11471],[-22,-197]],[[315888,11274],[-160,-129]],[[315728,11145],[-34,40]],[[315694,11185],[-86,1918]],[[315714,13935],[219,-707]],[[315888,11274],[293,-1348]],[[316181,9926],[-216,-625]],[[315965,9301],[-242,491]],[[315723,9792],[5,1353]],[[315723,9792],[-144,-1530]],[[315438,8721],[211,2085]],[[315649,10806],[45,379]],[[315649,10806],[-184,286]],[[316256,11346],[-331,239]],[[316158,12800],[154,393],[-56,-1847]],[[316256,11346],[125,-1004],[-200,-416]],[[315965,9301],[-160,-1940]],[[316936,9055],[328,-560],[-757,-909],[-91,685],[520,784]],[[318661,1986],[376,-1085],[-740,-781],[24,1181],[340,685]],[[314361,14911],[282,-187]],[[123928,718235],[-2,-1610]],[[119111,747595],[-67,-5]],[[124871,753168],[418,537]],[[71986,766650],[158,17]],[[56428,729876],[0,-164]],[[126985,748600],[107,525]],[[126818,747787],[-267,-754]],[[61672,65312],[272,-274]],[[314849,14669],[74,-410]],[[315183,5666],[-140,-216]],[[311825,11595],[-39,1452]],[[253891,243332],[-3,424]],[[297006,439075],[21,-79]],[[273150,218795],[-218,2571],[-109,2556],[341,-5125]],[[273164,218797],[-14,-2]],[[275036,140768],[-16,1]],[[274871,140335],[3,-76]],[[268559,210807],[-4,-26]],[[258410,236520],[0,355]],[[272837,223331],[-14,0]],[[274173,206960],[19,0]],[[275584,178509],[-145,-584]],[[275994,173067],[-63,2]],[[275922,173070],[-37,2]],[[272518,253791],[20,18]],[[272125,254343],[81,-132]],[[256680,462075],[-142,-2897],[-384,-10769]],[[256154,448409],[-883,-7],[0,-983]],[[254488,462036],[2192,39]],[[256154,448409],[1065,-7]],[[249637,222999],[-31,137]],[[248195,214699],[-70,-748]],[[247232,214954],[0,260]],[[247232,215317],[15,318]],[[301353,460902],[-185,-724]],[[301101,460137],[28,-397]],[[301793,457791],[116,-445]],[[285769,400843],[123,37]],[[264771,527003],[-281,49]],[[267568,452013],[-852,-4103],[-108,-17]],[[266608,447893],[-26,-6]],[[266582,447887],[-16,-1]],[[266566,447886],[-30,-6]],[[266536,447880],[-6,-1]],[[256680,462075],[-375,16538],[12,3500],[187,5880],[609,12037],[445,5943],[542,3980],[721,3116],[-1423,3912],[-936,-37],[-588,-3704],[-267,-718],[-95,-2358],[-427,343]],[[247207,537862],[1281,13508]],[[248488,551370],[1322,13439]],[[249810,564809],[408,-727],[1834,5024],[865,1148],[2109,-5688],[7666,-20686],[271,-4739],[574,-3230],[509,900],[683,396],[-47,-2079],[105,-3301],[426,-3438],[360,1156],[708,-243],[385,-2004],[-455,-3293],[3005,-8985],[1103,-32532],[-926,-12370],[-34,-3060],[-156,-2886],[-328,-976],[-521,-3379],[-656,-1036],[-180,-1472],[-60,-3681],[110,-1615]],[[252129,567028],[-1583,-4120],[166,-1627],[582,1037],[-142,551],[872,1783],[743,3451],[-638,-1075]],[[248488,551370],[-1963,212],[-2260,-6869],[-1513,-4210]],[[249572,564554],[238,255]],[[282592,309414],[10,-65]],[[289029,404122],[-16,45]],[[293679,426705],[-79,470]],[[286839,484138],[-17,6]],[[276897,457877],[-3,4558]],[[276894,462435],[2305,5850],[8,2378],[-220,544],[-164,5160],[-363,3502],[1422,3362],[1923,0],[3353,0],[997,8619],[109,340]],[[267767,442766],[-50,-4]],[[267568,452013],[114,-1687],[1085,-3482],[787,0],[3209,9884],[2039,2175]],[[274802,458903],[-16,-6460]],[[274802,458903],[1209,1289],[883,2243]],[[275537,276578],[-263,-628]],[[273916,275250],[-11,-827]],[[273920,274333],[-20,-184]],[[228015,167092],[147,-2633],[-252,2651],[105,-18]],[[228180,184437],[70,-94]],[[229313,193901],[29,366]],[[230757,204203],[-190,587]],[[229828,205636],[-9,-8]],[[229431,197365],[-30,-1032]],[[228487,190418],[-16,-156]],[[286226,362368],[89,-504]],[[283710,365528],[174,-301]],[[286242,357857],[63,-1057]],[[286254,356334],[304,311]],[[286558,356645],[136,36]],[[284458,394060],[-9,-959]],[[151607,571679],[12,2074],[1993,-3664],[1312,-1342],[815,1107],[372,2584],[-289,2342],[-137,2706],[722,1361],[0,1194],[-873,3181],[648,-1]],[[156182,583221],[153,1]],[[156335,583222],[770,0]],[[253979,502714],[-8,-1]],[[242231,539612],[9,872]],[[242245,540522],[75,212]],[[242329,540727],[187,592]],[[242526,541319],[224,-823]],[[288652,406259],[12,7]],[[288613,408309],[-8,430]],[[290026,386798],[-57,0]],[[287992,379951],[5,43]],[[287430,351414],[50,-2]],[[287480,351412],[118,4]],[[287598,351416],[-258,-2]],[[280248,301457],[-33,176]],[[156182,583221],[153,1]]],"transform":{"scale":[0.00035892617892657177,0.00005371486851395936],"translate":[-179.1473400003406,17.67439566600018]}}
    diff --git a/mapbox-gl/examples/data/topojson/world-110m.json b/mapbox-gl/examples/data/topojson/world-110m.json
    new file mode 100644
    index 0000000000..2f4dd1fa57
    --- /dev/null
    +++ b/mapbox-gl/examples/data/topojson/world-110m.json
    @@ -0,0 +1 @@
    +{"type":"Topology","transform":{"scale":[0.0036000360003600037,0.0016925586033320111],"translate":[-180,-85.60903777459777]},"objects":{"land":{"type":"MultiPolygon","arcs":[[[0]],[[1]],[[2]],[[3]],[[4]],[[5]],[[6]],[[7,8,9]],[[10,11]],[[12]],[[13]],[[14]],[[15]],[[16]],[[17]],[[18]],[[19]],[[20]],[[21]],[[22]],[[23]],[[24]],[[25]],[[26]],[[27]],[[28]],[[29,30]],[[31]],[[32]],[[33]],[[34]],[[35]],[[36]],[[37]],[[38]],[[39]],[[40]],[[41]],[[42,43]],[[44]],[[45]],[[46]],[[47,48,49,50]],[[51]],[[52]],[[53]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]],[[62,63]],[[64]],[[65]],[[66]],[[67]],[[68]],[[69]],[[70]],[[71]],[[72]],[[73]],[[74]],[[75]],[[76,77]],[[78]],[[79]],[[80]],[[81]],[[82]],[[83]],[[84]],[[85]],[[86]],[[87]],[[88]],[[89]],[[90,91]],[[92]],[[93]],[[94]],[[95]],[[96]],[[97]],[[98]],[[99]],[[100]],[[101]],[[102]],[[103]],[[104]],[[105]],[[106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221]],[[222,223]],[[224]],[[225]],[[226]],[[227]],[[228]],[[229]],[[230,231,232,233]],[[234]],[[235]],[[236]],[[237]],[[238]],[[239]],[[240]],[[241]],[[242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477],[478,479,480,481,482,483,484]],[[485]],[[486]],[[487]],[[488]],[[489]],[[490]],[[491]],[[492]],[[493]],[[494]],[[495]],[[496]],[[497]],[[498]]]},"countries":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[499,500,501,502,503,504]],"id":4},{"type":"MultiPolygon","arcs":[[[505,506,352,507]],[[354,508,509]]],"id":24},{"type":"Polygon","arcs":[[510,511,414,512,513,514]],"id":8},{"type":"Polygon","arcs":[[312,515,314,516,517]],"id":784},{"type":"MultiPolygon","arcs":[[[518,11]],[[519,520,521,166,522,168,523,524]]],"id":32},{"type":"Polygon","arcs":[[525,526,527,528,529]],"id":51},{"type":"MultiPolygon","arcs":[[[0]],[[1]],[[2]],[[3]],[[4]],[[5]],[[6]],[[530,531]]],"id":10},{"type":"Polygon","arcs":[[13]],"id":260},{"type":"MultiPolygon","arcs":[[[14]],[[24]]],"id":36},{"type":"Polygon","arcs":[[532,533,534,535,536,537,538]],"id":40},{"type":"MultiPolygon","arcs":[[[539,-528]],[[484,540,479,541,-526,542,543]]],"id":31},{"type":"Polygon","arcs":[[544,545,546]],"id":108},{"type":"Polygon","arcs":[[547,548,549,550,437]],"id":56},{"type":"Polygon","arcs":[[551,552,553,554,366]],"id":204},{"type":"Polygon","arcs":[[555,556,557,-553,558,559]],"id":854},{"type":"Polygon","arcs":[[560,561,289,562]],"id":50},{"type":"Polygon","arcs":[[563,404,564,565,566,567]],"id":100},{"type":"MultiPolygon","arcs":[[[71]],[[73]],[[74]]],"id":44},{"type":"Polygon","arcs":[[568,569,570]],"id":70},{"type":"Polygon","arcs":[[571,572,573,574,575]],"id":112},{"type":"Polygon","arcs":[[576,145,577]],"id":84},{"type":"Polygon","arcs":[[578,579,580,581,-525]],"id":68},{"type":"Polygon","arcs":[[-521,582,-581,583,584,585,586,587,588,164,589]],"id":76},{"type":"Polygon","arcs":[[48,590]],"id":96},{"type":"Polygon","arcs":[[591,592]],"id":64},{"type":"Polygon","arcs":[[593,594,595,596]],"id":72},{"type":"Polygon","arcs":[[597,598,599,600,601,602,603]],"id":140},{"type":"MultiPolygon","arcs":[[[84]],[[85]],[[86]],[[87]],[[88]],[[96]],[[97]],[[99]],[[101]],[[103]],[[604,107,605,109,606,111,607,113,608,115,609,117,610,199,611,201,612,215,613,217,614,219,615,221]],[[616,223]],[[224]],[[225]],[[226]],[[227]],[[229]],[[230,617,232,618]],[[235]],[[237]],[[238]],[[240]],[[241]],[[485]],[[486]],[[488]],[[489]],[[490]],[[496]],[[497]]],"id":124},{"type":"Polygon","arcs":[[-536,619,620,621]],"id":756},{"type":"MultiPolygon","arcs":[[[-519,622,623,624]],[[-524,169,625,171,626,-579]]],"id":152},{"type":"MultiPolygon","arcs":[[[64]],[[627,274,628,276,629,278,630,280,631,632,633,634,635,-593,636,637,638,639,-503,640,641,642,643,644,645]]],"id":156},{"type":"Polygon","arcs":[[369,646,647,648,-556,649]],"id":384},{"type":"Polygon","arcs":[[650,651,652,359,653,654,655,656,-604,657]],"id":120},{"type":"Polygon","arcs":[[658,659,-545,660,661,662,663,-508,353,-510,664,-602,665]],"id":180},{"type":"Polygon","arcs":[[-509,355,666,-658,-603,-665]],"id":178},{"type":"Polygon","arcs":[[667,174,668,155,669,-585,670]],"id":170},{"type":"Polygon","arcs":[[178,671,151,672]],"id":188},{"type":"Polygon","arcs":[[70]],"id":192},{"type":"Polygon","arcs":[[77,673]],"id":-99},{"type":"Polygon","arcs":[[76,-674]],"id":196},{"type":"Polygon","arcs":[[-538,674,675,676]],"id":203},{"type":"Polygon","arcs":[[445,677,-675,-537,-622,678,679,-549,680,441,681]],"id":276},{"type":"Polygon","arcs":[[337,682,683,684]],"id":262},{"type":"MultiPolygon","arcs":[[[92]],[[-682,442,685,444]]],"id":208},{"type":"Polygon","arcs":[[62,686]],"id":214},{"type":"Polygon","arcs":[[687,688,689,690,691,384,692,693]],"id":12},{"type":"Polygon","arcs":[[173,-668,694]],"id":218},{"type":"Polygon","arcs":[[333,695,696,390,697]],"id":818},{"type":"Polygon","arcs":[[698,699,700,336,-685]],"id":232},{"type":"Polygon","arcs":[[431,701,433,702,427,703,429,704]],"id":724},{"type":"Polygon","arcs":[[450,705,706]],"id":233},{"type":"Polygon","arcs":[[-684,707,708,709,710,711,712,-699]],"id":231},{"type":"Polygon","arcs":[[713,452,714,715,455,716,717]],"id":246},{"type":"MultiPolygon","arcs":[[[18]],[[19]],[[20]]],"id":242},{"type":"Polygon","arcs":[[12]],"id":238},{"type":"MultiPolygon","arcs":[[[718,719,163,-589]],[[82]],[[720,-679,-621,721,426,-703,434,722,436,-551]]],"id":250},{"type":"Polygon","arcs":[[356,723,-651,-667]],"id":266},{"type":"MultiPolygon","arcs":[[[724,90]],[[725,726,727,728,729,730,731,732]]],"id":826},{"type":"Polygon","arcs":[[400,733,-543,-530,734]],"id":268},{"type":"Polygon","arcs":[[368,-650,-560,735]],"id":288},{"type":"Polygon","arcs":[[736,737,374,738,739,740,-648]],"id":324},{"type":"Polygon","arcs":[[741,377]],"id":270},{"type":"Polygon","arcs":[[375,742,-739]],"id":624},{"type":"Polygon","arcs":[[357,-652,-724]],"id":226},{"type":"MultiPolygon","arcs":[[[78]],[[407,743,409,744,411,745,413,-512,746,-566,747]]],"id":300},{"type":"Polygon","arcs":[[498]],"id":304},{"type":"Polygon","arcs":[[185,748,-578,146,749,750]],"id":320},{"type":"Polygon","arcs":[[161,751,-587,752]],"id":328},{"type":"Polygon","arcs":[[182,753,754,-750,147,755,149,756]],"id":340},{"type":"Polygon","arcs":[[757,-571,758,417,759,419,760,761]],"id":191},{"type":"Polygon","arcs":[[-687,63]],"id":332},{"type":"Polygon","arcs":[[-533,762,763,764,765,-762,766]],"id":348},{"type":"MultiPolygon","arcs":[[[26]],[[767,30]],[[31]],[[32]],[[35]],[[36]],[[39]],[[40]],[[768,43]],[[44]],[[45]],[[769,50]],[[46]]],"id":360},{"type":"Polygon","arcs":[[-639,770,-637,-592,-636,771,-563,290,772,292,773,294,774,296,775]],"id":356},{"type":"Polygon","arcs":[[91,-725]],"id":372},{"type":"Polygon","arcs":[[776,-505,777,300,778,302,779,780,781,-540,-527,-542,480]],"id":364},{"type":"Polygon","arcs":[[782,783,784,785,786,-781,787]],"id":368},{"type":"Polygon","arcs":[[100]],"id":352},{"type":"Polygon","arcs":[[788,789,-698,391,790,791,792]],"id":376},{"type":"MultiPolygon","arcs":[[[79]],[[80]],[[793,421,794,423,795,425,-722,-620,-535]]],"id":380},{"type":"Polygon","arcs":[[61]],"id":388},{"type":"Polygon","arcs":[[796,-785,797,332,-790,798,-793]],"id":400},{"type":"MultiPolygon","arcs":[[[75]],[[81]],[[83]]],"id":392},{"type":"Polygon","arcs":[[799,800,482,801,-643,802]],"id":398},{"type":"Polygon","arcs":[[342,803,804,805,-710,806]],"id":404},{"type":"Polygon","arcs":[[-803,-642,807,808]],"id":417},{"type":"Polygon","arcs":[[809,810,811,283]],"id":116},{"type":"Polygon","arcs":[[265,812,267,813]],"id":410},{"type":"Polygon","arcs":[[-515,814,815,816]],"id":-99},{"type":"Polygon","arcs":[[304,817,-783]],"id":414},{"type":"Polygon","arcs":[[818,819,-634,820,-811]],"id":418},{"type":"Polygon","arcs":[[-791,392,821]],"id":422},{"type":"Polygon","arcs":[[370,822,372,823,-737,-647]],"id":430},{"type":"Polygon","arcs":[[824,-694,825,388,826,-697,827,828]],"id":434},{"type":"Polygon","arcs":[[52]],"id":144},{"type":"Polygon","arcs":[[829]],"id":426},{"type":"Polygon","arcs":[[830,448,831,-572,832]],"id":440},{"type":"Polygon","arcs":[[-680,-721,-550]],"id":442},{"type":"Polygon","arcs":[[449,-707,833,-573,-832]],"id":428},{"type":"Polygon","arcs":[[-692,834,835,836,837,383]],"id":504},{"type":"Polygon","arcs":[[838,839]],"id":498},{"type":"Polygon","arcs":[[23]],"id":450},{"type":"Polygon","arcs":[[840,-577,-749,186,841,188,842,190,843,192,844,194,845]],"id":484},{"type":"Polygon","arcs":[[-817,846,-567,-747,-511]],"id":807},{"type":"Polygon","arcs":[[847,-689,848,-557,-649,-741,849]],"id":466},{"type":"Polygon","arcs":[[287,-561,-772,-635,-820,850]],"id":104},{"type":"Polygon","arcs":[[416,-759,-570,851,-815,-514,852]],"id":499},{"type":"Polygon","arcs":[[853,-645]],"id":496},{"type":"Polygon","arcs":[[854,344,855,856,347,857,858,859,860,861,862]],"id":508},{"type":"Polygon","arcs":[[863,379,864,-690,-848]],"id":478},{"type":"Polygon","arcs":[[-863,865,866]],"id":454},{"type":"MultiPolygon","arcs":[[[285,867]],[[-770,47,-591,49]]],"id":458},{"type":"Polygon","arcs":[[351,-507,868,-595,869]],"id":516},{"type":"Polygon","arcs":[[17]],"id":540},{"type":"Polygon","arcs":[[-558,-849,-688,-825,870,-656,871,-554]],"id":562},{"type":"Polygon","arcs":[[361,872,363,873,365,-555,-872,-655]],"id":566},{"type":"Polygon","arcs":[[179,874,181,-757,150,-672]],"id":558},{"type":"Polygon","arcs":[[-681,-548,438,875,440]],"id":528},{"type":"MultiPolygon","arcs":[[[876,-718,877,457,878,459,879,461]],[[487]],[[492]],[[493]]],"id":578},{"type":"Polygon","arcs":[[-771,-638]],"id":524},{"type":"MultiPolygon","arcs":[[[15]],[[16]]],"id":554},{"type":"MultiPolygon","arcs":[[[880,319,881,882,883,-517,315,884,317]],[[-516,313]]],"id":512},{"type":"Polygon","arcs":[[-640,-776,297,885,299,-778,-504]],"id":586},{"type":"Polygon","arcs":[[175,886,177,-673,152,887,154,-669]],"id":591},{"type":"Polygon","arcs":[[-627,172,-695,-671,-584,-580]],"id":604},{"type":"MultiPolygon","arcs":[[[51]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]]],"id":608},{"type":"MultiPolygon","arcs":[[[37]],[[38]],[[-769,42]],[[41]]],"id":598},{"type":"Polygon","arcs":[[-678,446,888,-833,-576,889,890,-676]],"id":616},{"type":"Polygon","arcs":[[60]],"id":630},{"type":"Polygon","arcs":[[262,891,264,-814,892,269,893,271,894,273,-628,895]],"id":408},{"type":"Polygon","arcs":[[-705,430]],"id":620},{"type":"Polygon","arcs":[[-582,-583,-520]],"id":600},{"type":"Polygon","arcs":[[-799,-789]],"id":275},{"type":"Polygon","arcs":[[308,896,310,897]],"id":634},{"type":"Polygon","arcs":[[898,-840,899,403,-564,900,-765]],"id":642},{"type":"MultiPolygon","arcs":[[[89]],[[-889,447,-831]],[[102]],[[104]],[[105]],[[228]],[[234]],[[236]],[[239]],[[901,243,902,245,903,247,904,249,905,251,906,253,907,255,908,257,909,259,910,261,-896,-646,-854,-644,-802,483,-544,-734,401,911,-574,-834,-706,451,-714,-877,912,913,914,915,464,916,466,917,468,918,470,919,920,473,921,475,922,477]],[[491]],[[494]],[[495]]],"id":643},{"type":"Polygon","arcs":[[923,-546,-660,924]],"id":646},{"type":"Polygon","arcs":[[-691,-865,380,-835]],"id":732},{"type":"Polygon","arcs":[[925,329,926,331,-798,-784,-818,305,927,307,-898,311,-518,-884,928]],"id":682},{"type":"Polygon","arcs":[[-599,929,-828,-696,334,-700,-713,930,931,932]],"id":729},{"type":"Polygon","arcs":[[-711,-806,933,-666,-601,934,-932,935]],"id":728},{"type":"Polygon","arcs":[[378,-864,-850,-740,-743,376,-742]],"id":686},{"type":"MultiPolygon","arcs":[[[25]],[[27]],[[28]],[[33]],[[34]]],"id":90},{"type":"Polygon","arcs":[[373,-738,-824]],"id":694},{"type":"Polygon","arcs":[[184,-751,-755,936]],"id":222},{"type":"Polygon","arcs":[[-708,-683,338,937,340,938]],"id":-99},{"type":"Polygon","arcs":[[-807,-709,-939,341]],"id":706},{"type":"Polygon","arcs":[[-568,-847,-816,-852,-569,-758,-766,-901]],"id":688},{"type":"Polygon","arcs":[[162,-720,939,-588,-752]],"id":740},{"type":"Polygon","arcs":[[-891,940,-763,-539,-677]],"id":703},{"type":"Polygon","arcs":[[-534,-767,-761,420,-794]],"id":705},{"type":"Polygon","arcs":[[-878,-717,456]],"id":752},{"type":"Polygon","arcs":[[941,-859]],"id":748},{"type":"Polygon","arcs":[[-797,-792,-822,393,942,-786]],"id":760},{"type":"Polygon","arcs":[[-871,-829,-930,-598,-657]],"id":148},{"type":"Polygon","arcs":[[-736,-559,-552,367]],"id":768},{"type":"Polygon","arcs":[[284,-868,286,-851,-819,-810]],"id":764},{"type":"Polygon","arcs":[[-808,-641,-502,943]],"id":762},{"type":"Polygon","arcs":[[-777,481,-801,944,-500]],"id":795},{"type":"Polygon","arcs":[[29,-768]],"id":626},{"type":"Polygon","arcs":[[53]],"id":780},{"type":"Polygon","arcs":[[-693,385,945,387,-826]],"id":788},{"type":"MultiPolygon","arcs":[[[399,-735,-529,-782,-787,-943,394,946,947,397,948]],[[949,-748,-565,405]]],"id":792},{"type":"Polygon","arcs":[[72]],"id":158},{"type":"Polygon","arcs":[[-804,343,-855,-867,950,-663,951,-661,-547,-924,952]],"id":834},{"type":"Polygon","arcs":[[-925,-659,-934,-805,-953]],"id":800},{"type":"Polygon","arcs":[[-912,402,-900,-839,-899,-764,-941,-890,-575]],"id":804},{"type":"Polygon","arcs":[[-590,165,-522]],"id":858},{"type":"MultiPolygon","arcs":[[[65]],[[66]],[[67]],[[68]],[[69]],[[118,953,120,954,122,955,124,956,126,957,128,958,130,959,132,960,134,961,136,962,138,963,140,964,142,-846,195,965,966,967,968,969,-611]],[[93]],[[95]],[[98]],[[-613,202,970,204,971,206,972,208,973,210,974,212,975,214]]],"id":840},{"type":"Polygon","arcs":[[-945,-800,-809,-944,-501]],"id":860},{"type":"Polygon","arcs":[[156,976,158,977,160,-753,-586,-670]],"id":862},{"type":"Polygon","arcs":[[282,-812,-821,-633]],"id":704},{"type":"MultiPolygon","arcs":[[[21]],[[22]]],"id":548},{"type":"Polygon","arcs":[[321,978,323,979,325,980,327,-929,-883,981]],"id":887},{"type":"Polygon","arcs":[[982,350,-870,-594,983,-860,-942,-858,348],[-830]],"id":710},{"type":"Polygon","arcs":[[-866,-862,984,-596,-869,-506,-664,-951]],"id":894},{"type":"Polygon","arcs":[[-984,-597,-985,-861]],"id":716}]}},"arcs":[[[31586,3163],[625,-23],[599,-58],[207,243],[147,208],[288,-243],[-82,-301],[-81,-266],[-582,81],[-621,-35],[-348,197],[0,23],[-152,174]],[[4524,4144],[169,220],[517,-93],[277,-185],[212,-209],[76,-266],[-533,-81],[-364,208],[-163,209],[-11,35],[-180,162]],[[34954,2940],[49,243],[593,162],[239,197],[174,254],[126,220],[168,209],[180,243],[141,0],[414,127],[419,-127],[342,-255],[120,-359],[33,-254],[11,-301],[-430,-186],[-452,-150],[-522,-139],[-582,-116],[-658,35],[-365,197]],[[15938,7061],[60,197],[332,-104],[359,-93],[332,104],[-158,-208],[-261,-151],[-386,47],[-278,208]],[[14643,7177],[202,127],[277,-139],[425,-231],[-164,23],[-359,58],[-381,162]],[[21575,8103],[174,104],[353,-81],[403,-46],[305,-81],[304,69],[163,-335],[-217,46],[-337,-23],[-343,23],[-376,-35],[-283,116],[-146,243]],[[29163,8241],[305,231],[190,70],[321,-23],[82,301],[16,219],[-6,475],[158,278],[256,93],[147,-220],[65,-220],[120,-267],[92,-254],[76,-267],[33,-266],[-49,-231],[-76,-220],[-326,-81],[-311,-116],[-364,11],[136,232],[-327,-81],[-310,-81],[-212,174],[-16,243]],[[794,704],[78,49],[94,61],[81,52],[41,26]],[[1088,892],[41,-1],[29,-10]],[[1158,881],[402,-246],[352,246],[63,34],[816,104],[265,-138],[130,-71],[419,-196],[789,-151],[625,-185],[1072,-139],[800,162],[1181,-116],[669,-185],[734,174],[773,162],[60,278],[-1094,23],[-898,139],[-234,231],[-745,128],[49,266],[103,243],[104,220],[-55,243],[-462,162],[-212,209],[-430,185],[675,-35],[642,93],[402,-197],[495,173],[457,220],[223,197],[-98,243],[-359,162],[-408,174],[-571,35],[-500,81],[-539,58],[-180,220],[-359,185],[-217,208],[-87,672],[136,-58],[250,-185],[457,58],[441,81],[228,-255],[441,58],[370,127],[348,162],[315,197],[419,58],[-11,220],[-97,220],[81,208],[359,104],[163,-196],[425,115],[321,151],[397,12],[375,57],[376,139],[299,128],[337,127],[218,-35],[190,-46],[414,81],[370,-104],[381,11],[364,81],[375,-57],[414,-58],[386,23],[403,-12],[413,-11],[381,23],[283,174],[337,92],[349,-127],[331,104],[300,208],[179,-185],[98,-208],[180,-197],[288,174],[332,-220],[375,-70],[321,-162],[392,35],[354,104],[418,-23],[376,-81],[381,-104],[147,254],[-180,197],[-136,209],[-359,46],[-158,220],[-60,220],[-98,440],[213,-81],[364,-35],[359,35],[327,-93],[283,-174],[119,-208],[376,-35],[359,81],[381,116],[342,70],[283,-139],[370,46],[239,451],[224,-266],[321,-104],[348,58],[228,-232],[365,-23],[337,-69],[332,-128],[218,220],[108,209],[278,-232],[381,58],[283,-127],[190,-197],[370,58],[288,127],[283,151],[337,81],[392,69],[354,81],[272,127],[163,186],[65,254],[-32,244],[-87,231],[-98,232],[-87,231],[-71,209],[-16,231],[27,232],[130,220],[109,243],[44,231],[-55,255],[-32,232],[136,266],[152,173],[180,220],[190,186],[223,173],[109,255],[152,162],[174,151],[267,34],[174,186],[196,115],[228,70],[202,150],[157,186],[218,69],[163,-151],[-103,-196],[-283,-174],[-120,-127],[-206,92],[-229,-58],[-190,-139],[-202,-150],[-136,-174],[-38,-231],[17,-220],[130,-197],[-190,-139],[-261,-46],[-153,-197],[-163,-185],[-174,-255],[-44,-220],[98,-243],[147,-185],[229,-139],[212,-185],[114,-232],[60,-220],[82,-232],[130,-196],[82,-220],[38,-544],[81,-220],[22,-232],[87,-231],[-38,-313],[-152,-243],[-163,-197],[-370,-81],[-125,-208],[-169,-197],[-419,-220],[-370,-93],[-348,-127],[-376,-128],[-223,-243],[-446,-23],[-489,23],[-441,-46],[-468,0],[87,-232],[424,-104],[311,-162],[174,-208],[-310,-185],[-479,58],[-397,-151],[-17,-243],[-11,-232],[327,-196],[60,-220],[353,-220],[588,-93],[500,-162],[398,-185],[506,-186],[690,-92],[681,-162],[473,-174],[517,-197],[272,-278],[136,-220],[337,209],[457,173],[484,186],[577,150],[495,162],[691,12],[680,-81],[560,-139],[180,255],[386,173],[702,12],[550,127],[522,128],[577,81],[614,104],[430,150],[-196,209],[-119,208],[0,220],[-539,-23],[-571,-93],[-544,0],[-77,220],[39,440],[125,128],[397,138],[468,139],[337,174],[337,174],[251,231],[380,104],[376,81],[190,47],[430,23],[408,81],[343,116],[337,139],[305,139],[386,185],[245,197],[261,173],[82,232],[-294,139],[98,243],[185,185],[288,116],[305,139],[283,185],[217,232],[136,277],[202,163],[331,-35],[136,-197],[332,-23],[11,220],[142,231],[299,-58],[71,-220],[331,-34],[360,104],[348,69],[315,-34],[120,-243],[305,196],[283,105],[315,81],[310,81],[283,139],[310,92],[240,128],[168,208],[207,-151],[288,81],[202,-277],[157,-209],[316,116],[125,232],[283,162],[365,-35],[108,-220],[229,220],[299,69],[326,23],[294,-11],[310,-70],[300,-34],[130,-197],[180,-174],[304,104],[327,24],[315,0],[310,11],[278,81],[294,70],[245,162],[261,104],[283,58],[212,162],[152,324],[158,197],[288,-93],[109,-208],[239,-139],[289,46],[196,-208],[206,-151],[283,139],[98,255],[250,104],[289,197],[272,81],[326,116],[218,127],[228,139],[218,127],[261,-69],[250,208],[180,162],[261,-11],[229,139],[54,208],[234,162],[228,116],[278,93],[256,46],[244,-35],[262,-58],[223,-162],[27,-254],[245,-197],[168,-162],[332,-70],[185,-162],[229,-162],[266,-35],[223,116],[240,243],[261,-127],[272,-70],[261,-69],[272,-46],[277,0],[229,-614],[-11,-150],[-33,-267],[-266,-150],[-218,-220],[38,-232],[310,12],[-38,-232],[-141,-220],[-131,-243],[212,-185],[321,-58],[321,104],[153,232],[92,220],[153,185],[174,174],[70,208],[147,289],[174,58],[316,24],[277,69],[283,93],[136,231],[82,220],[190,220],[272,151],[234,115],[153,197],[157,104],[202,93],[277,-58],[250,58],[272,69],[305,-34],[201,162],[142,393],[103,-162],[131,-278],[234,-115],[266,-47],[267,70],[283,-46],[261,-12],[174,58],[234,-35],[212,-127],[250,81],[300,0],[255,81],[289,-81],[185,197],[141,196],[191,163],[348,439],[179,-81],[212,-162],[185,-208],[354,-359],[272,-12],[256,0],[299,70],[299,81],[229,162],[190,174],[310,23],[207,127],[218,-116],[141,-185],[196,-185],[305,23],[190,-150],[332,-151],[348,-58],[288,47],[218,185],[185,185],[250,46],[251,-81],[288,-58],[261,93],[250,0],[245,-58],[256,-58],[250,104],[299,93],[283,23],[316,0],[255,58],[251,46],[76,290],[11,243],[174,-162],[49,-266],[92,-244],[115,-196],[234,-105],[315,35],[365,12],[250,35],[364,0],[262,11],[364,-23],[310,-46],[196,-186],[-54,-220],[179,-173],[299,-139],[310,-151],[360,-104],[375,-92],[283,-93],[315,-12],[180,197],[245,-162],[212,-185],[245,-139],[337,-58],[321,-69],[136,-232],[316,-139],[212,-208],[310,-93],[321,12],[299,-35],[332,12],[332,-47],[310,-81],[288,-139],[289,-116],[195,-173],[-32,-232],[-147,-208],[-125,-266],[-98,-209],[-131,-243],[-364,-93],[-163,-208],[-360,-127],[-125,-232],[-190,-220],[-201,-185],[-115,-243],[-70,-220],[-28,-266],[6,-220],[158,-232],[60,-220],[130,-208],[517,-81],[109,-255],[-501,-93],[-424,-127],[-528,-23],[-234,-336],[-49,-278],[-119,-220],[-147,-220],[370,-196],[141,-244],[239,-219],[338,-197],[386,-186],[419,-185],[636,-185],[142,-289],[800,-128],[53,-45],[208,-175],[767,151],[636,-186],[-99504,-147],[245,344],[501,-185],[32,21]],[[31400,18145],[-92,-239],[-238,-183],[-301,67],[-202,177],[-291,86],[-350,330],[-283,317],[-383,662],[229,-124],[390,-395],[369,-212],[143,271],[90,405],[256,244],[198,-70]],[[30935,19481],[106,-274],[139,-443],[361,-355],[389,-147],[-125,-296],[-264,-29],[-141,208]],[[33000,19946],[333,354],[236,-148],[167,237],[222,-266],[-83,-207],[-375,-177],[-125,207],[-236,-266],[-139,266]],[[69088,21486],[41,244],[19,121],[179,-186],[263,-74],[9,-112],[-77,-269],[-427,-38],[-7,314]],[[90199,26260],[7,271],[181,-52],[269,-204],[151,81],[217,113],[166,-39],[20,-702],[-95,-203],[-29,-476],[-97,162],[-193,-412],[-57,32],[-171,19],[-171,505],[-38,390],[-160,515]],[[96252,23489],[149,438],[349,583],[179,111],[200,225],[238,310],[167,306],[123,441],[106,149],[41,330],[195,273],[61,-251],[63,-244],[198,239],[80,-249],[0,-249],[-103,-274],[-182,-435],[-142,-238],[103,-284],[-214,-7],[-238,-223],[-75,-387],[-157,-597],[-219,-264],[-138,-169],[-256,13],[-180,194],[-302,42],[-46,217]],[[97953,30179],[104,46],[151,-328],[216,-153],[78,-526],[202,-622],[5,403],[126,-161],[41,-447],[224,-192],[188,-48],[158,226],[141,-69],[-67,-524],[-85,-345],[-212,12],[-74,-179],[26,-254],[-41,-110],[-105,-319],[-138,-404],[-214,-236],[-48,155],[-116,85],[160,486],[-91,326],[-299,236],[8,214],[201,206],[47,455],[-13,382],[-113,396],[8,104],[-133,244],[-218,523],[-117,418]],[[95563,38701],[119,-9],[156,-201],[122,-200],[89,-166],[228,-366],[144,-272],[-105,-142],[-153,160],[-199,266],[-179,313],[-184,416],[-38,201]],[[99245,40108],[107,203],[126,-74],[69,98],[96,-171],[-46,-308],[-172,-81],[-153,73],[-27,260]],[[99609,40749],[139,121],[88,33],[163,184],[0,-289],[-177,-145],[-177,-124],[-36,220]],[[0,40798],[0,289],[57,27],[-34,-284],[-23,-32]],[[96438,41032],[10,158],[175,-339],[-92,-78],[-93,259]],[[96285,41938],[133,-182],[45,-476],[-75,74],[-58,-32],[-39,163],[-6,453]],[[62014,37548],[50,426],[128,102],[1,197],[133,447],[25,377],[-65,280],[-52,372],[-23,544],[97,331],[38,375],[138,22],[155,121],[103,107],[122,7],[158,337],[229,364],[83,297],[-38,253],[118,-71],[153,410],[6,356],[92,264],[96,-254],[74,-251],[69,-390],[45,-711],[72,-276],[-28,-284],[-49,-174],[-94,347],[-53,-175],[53,-438],[-24,-250],[-77,-137],[-18,-500],[-109,-689],[-137,-814],[-172,-1120],[-106,-821],[-125,-685],[-226,-140],[-243,-250],[-160,151],[-220,211],[-77,312],[-18,524],[-98,471],[-26,425]],[[81482,35149],[122,-255],[-93,548],[137,-171],[83,-229],[-5,303],[-138,465],[-26,186],[-65,177],[31,341],[56,146],[38,295],[-29,346],[114,425],[21,-450],[118,406],[225,198],[136,252],[212,217],[126,46],[77,-73],[219,220],[168,66],[42,129],[74,54],[153,-14],[292,173],[151,262],[71,316],[163,300],[13,236],[7,321],[194,502],[117,-510],[119,118],[-99,279],[87,287],[122,-128],[34,449],[152,291],[67,233],[140,101],[4,165],[122,-69],[5,148],[122,85],[134,80],[205,-271],[155,-350],[173,-4],[177,-56],[-59,325],[133,473],[126,155],[-44,147],[121,338],[168,208],[142,-70],[234,111],[-5,302],[-204,195],[148,86],[184,-147],[148,-242],[234,-151],[79,60],[172,-182],[162,169],[105,-51],[65,113],[127,-292],[-74,-316],[-105,-239],[-96,-20],[32,-236],[-81,-295],[-99,-291],[20,-166],[221,-327],[214,-189],[143,-204],[201,-350],[78,1],[145,-151],[43,-183],[265,-200],[183,202],[55,317],[56,262],[34,324],[85,470],[-39,286],[20,171],[-32,339],[37,445],[53,120],[-43,197],[67,313],[52,325],[7,168],[104,222],[78,-289],[19,-371],[70,-71],[11,-249],[101,-300],[21,-335],[-10,-214],[100,-464],[179,223],[92,-250],[133,-231],[-29,-262],[60,-506],[42,-295],[70,-72],[75,-505],[-27,-307],[90,-400],[301,-309],[197,-281],[186,-257],[-37,-143],[159,-371],[108,-639],[111,130],[113,-256],[68,91],[48,-626],[197,-363],[129,-226],[217,-478],[78,-475],[7,-337],[-19,-365],[132,-502],[-16,-523],[-48,-274],[-75,-527],[6,-339],[-55,-423],[-123,-538],[-205,-290],[-102,-458],[-93,-292],[-82,-510],[-107,-294],[-70,-442],[-36,-407],[14,-187],[-159,-205],[-311,-22],[-257,-242],[-127,-229],[-168,-254],[-230,262],[-170,104],[43,308],[-152,-112],[-243,-428],[-240,160],[-158,94],[-159,42],[-269,171],[-179,364],[-52,449],[-64,298],[-137,240],[-267,71],[91,287],[-67,438],[-136,-408],[-247,-109],[146,327],[42,341],[107,289],[-22,438],[-226,-504],[-174,-202],[-106,-470],[-217,243],[9,313],[-174,429],[-147,221],[52,137],[-356,358],[-195,17],[-267,287],[-498,-56],[-359,-211],[-317,-197],[-265,39],[-294,-303],[-241,-137],[-53,-309],[-103,-240],[-236,-15],[-174,-52],[-246,107],[-199,-64],[-191,-27],[-165,-315],[-81,26],[-140,-167],[-133,-187],[-203,23],[-186,0],[-295,377],[-149,113],[6,338],[138,81],[47,134],[-10,212],[34,411],[-31,350],[-147,598],[-45,337],[12,336],[-111,385],[-7,174],[-123,235],[-35,463],[-158,467],[-39,252]],[[94810,44550],[166,-142],[56,-22],[78,-203],[-194,4],[-106,363]],[[83046,44933],[259,116],[146,-180],[97,-180],[-17,-159],[-117,-11],[-368,414]],[[94344,44884],[17,235],[183,-93],[91,-124],[45,-155],[-108,-14],[-170,60],[-58,91]],[[94605,45664],[94,0],[100,-473],[111,-283],[-42,-109],[-206,512],[-57,353]],[[84713,45326],[32,139],[239,133],[194,20],[87,74],[105,-74],[-102,-160],[-289,-258],[-233,-170]],[[84746,45030],[-181,-441],[-238,-130],[-33,71],[25,201],[119,360],[275,235]],[[82427,45243],[95,340],[153,5],[74,209],[100,-158],[172,48],[69,-251],[-321,-119],[-193,-79],[-149,5]],[[83311,45374],[0,216],[220,123],[174,-177],[185,45],[249,216],[-41,-328],[-417,-168],[-370,73]],[[93947,46195],[41,60],[128,-142],[228,-272],[65,-187],[12,-119],[-218,251],[-152,212],[-104,197]],[[93469,46582],[14,99],[166,-250],[111,-193],[-56,-33],[-121,134],[-114,243]],[[79267,46532],[191,564],[337,-35],[224,-231],[115,-45],[38,-210],[533,-59],[61,244],[515,-284],[101,-383],[417,-108],[341,-351],[-317,-225],[-306,238],[-251,-16],[-288,44],[-260,106],[-322,225],[-204,59],[-116,-74],[-506,243],[-48,254],[-255,44]],[[87253,46951],[49,212],[58,200],[63,-173],[0,-282],[-143,-402],[-27,445]],[[92920,47543],[38,57],[30,-175],[84,-134],[135,-375],[131,-200],[-39,-166],[-78,-59],[-120,227],[-122,375],[-59,450]],[[91199,47184],[23,183],[249,-86],[152,46],[42,283],[40,15],[27,-314],[158,45],[78,202],[155,211],[-30,348],[166,11],[56,-97],[-5,-327],[-93,-361],[-146,-48],[-44,-166],[-152,-144],[-142,-138],[-148,1],[-228,171],[-158,165]],[[84996,48702],[281,29],[69,-195],[-104,-196],[-192,108],[-54,254]],[[85527,48575],[65,325],[344,24],[305,-172],[101,-452],[-234,244],[-232,49],[-157,-39],[-192,21]],[[91850,48960],[77,143],[150,-166],[94,-130],[117,-142],[111,-248],[106,-189],[33,-307],[-87,-157],[-52,348],[-65,229],[-126,193],[-158,252],[-200,174]],[[89166,49043],[482,-407],[513,-338],[192,-302],[154,-297],[43,-349],[462,-365],[68,-313],[-256,-64],[62,-393],[248,-388],[180,-627],[159,20],[-11,-262],[215,-100],[-84,-111],[295,-249],[-30,-171],[-184,-41],[-69,153],[-238,66],[-281,89],[-216,377],[-158,325],[-144,517],[-362,259],[-235,-169],[-170,-195],[35,-436],[-218,-203],[-155,99],[-288,25]],[[89175,45193],[-247,485],[-282,118],[-69,-168],[-352,-18],[118,481],[175,164],[-72,642],[-134,496],[-538,500],[-229,50],[-417,546],[-82,-287],[-107,-52],[-63,216],[-1,257],[-212,290],[299,213],[198,-11],[-23,156],[-407,1],[-110,352],[-248,109],[-117,293],[374,143],[142,192],[446,-242],[44,-220],[78,-955],[287,-354],[232,627],[319,356],[247,1],[238,-206],[206,-212],[298,-113]],[[82990,48924],[115,387],[40,469],[139,891],[58,243],[237,439],[217,-174],[350,-82],[319,25],[275,429],[48,-132],[-223,-587],[-209,-113],[-267,115],[-463,-29],[-243,-85],[-39,-447],[248,-526],[150,268],[518,201],[-22,-272],[-121,86],[-121,-347],[-245,-229],[263,-757],[-50,-203],[249,-682],[-2,-388],[-148,-173],[-109,207],[134,484],[-273,-229],[-69,164],[36,228],[-200,346],[21,576],[-186,-179],[24,-689],[11,-846],[-176,-85],[-119,173],[79,544],[-43,570],[-117,4],[-86,405]],[[85388,51177],[56,472],[92,215],[20,-322],[164,-52],[26,-241],[-15,-517],[-143,58],[-42,-359],[114,-312],[-78,-71],[-112,374],[-82,755]],[[76470,53817],[178,-24],[430,-114],[246,-577],[215,-401],[153,-246],[263,-635],[283,-9],[233,-405],[161,-495],[211,-270],[-111,-482],[159,-205],[100,-15],[47,-412],[97,-330],[204,-52],[135,-374],[-70,-735],[-11,-914],[-308,-12],[-234,494],[-356,482],[-119,358],[-210,481],[-138,443],[-212,827],[-244,493],[-81,508],[-103,461],[-250,372],[-145,506],[-209,330],[-290,652],[-24,300]],[[80461,51765],[204,-202],[214,110],[56,500],[119,112],[333,128],[199,467],[137,374]],[[81723,53254],[110,221],[236,323]],[[82069,53798],[214,411],[140,462],[112,2],[143,-299],[13,-257],[183,-165],[231,-177],[-20,-232],[-186,-29],[50,-289],[-205,-201]],[[82744,53024],[-158,-533],[204,-560],[-48,-272],[312,-546],[-329,-70],[-93,-403],[12,-535],[-267,-404],[-7,-589],[-107,-903],[-41,210],[-316,-266],[-110,361],[-198,34],[-139,189],[-330,-212],[-101,285],[-182,-32],[-229,68],[-43,793],[-138,164],[-134,505],[-38,517],[32,548],[165,392]],[[83866,54829],[109,498],[175,166],[151,223],[98,-268],[212,162],[45,264],[196,15],[-16,457],[225,-280],[23,-297],[20,-218],[28,-392],[16,-332],[-94,-540],[-102,602],[-130,-300],[89,-435],[-79,-277],[-327,343],[-78,428],[84,280],[-176,280],[-87,-245],[-131,23],[-205,-330],[-46,173]],[[72137,55425],[126,959],[192,-328],[129,-416],[134,-616],[-42,-615],[-116,-168],[-242,-135],[-132,470],[-49,849]],[[32791,56541],[81,163],[-6,233],[160,77],[58,-21],[-11,-440],[-232,-65],[-50,53]],[[83994,56319],[57,158],[70,165],[30,367],[153,35],[-44,-398],[205,570],[-26,-563],[-100,-195],[-87,-373],[-87,-175],[-171,409]],[[82548,55523],[136,414],[200,364],[167,409],[146,587],[49,-482],[-183,-325],[-146,-406],[-369,-561]],[[83856,57606],[166,-183],[177,1],[-5,-247],[-129,-251],[-176,-178],[-10,275],[20,301],[-43,282]],[[84518,57999],[266,-13],[77,-220],[78,-660],[-214,157],[5,-199],[68,-364],[-132,-133],[-11,416],[-84,31],[-43,357],[163,-47],[-4,224],[-169,451]],[[83422,58536],[238,-22],[97,-213],[-74,-510],[-119,295],[-142,450]],[[83300,60248],[112,-195],[29,925],[90,535],[169,-1],[171,-168],[85,153],[26,-150],[-46,-245],[95,-423],[-73,-491],[-164,-196],[-43,-476],[62,-471],[147,-65],[123,70],[347,-328],[-27,-321],[91,-142],[-29,-272],[-216,290],[-103,310],[-71,-217],[-177,354],[-253,-87],[-138,130],[14,244],[87,151],[-83,136],[-36,-213],[-137,340],[-41,257],[-11,566]],[[31321,61436],[40,86],[227,-3],[142,-52],[50,-118],[-71,-149],[-209,4],[-163,-21],[-16,253]],[[28239,61348],[34,135],[116,41],[64,-20],[187,-53],[147,-142],[46,-161],[-195,-11],[-84,-99],[-156,95],[-159,215]],[[30080,62227],[34,101],[217,-3],[165,-152],[73,15],[50,-209],[152,11],[-9,-176],[124,-21],[136,-217],[-103,-240],[-132,128],[-127,-25],[-92,28],[-50,-107],[-106,-37],[-43,144],[-92,-85],[-111,-405],[-71,94],[-14,170]],[[30081,61241],[-185,100],[-131,-41],[-169,43],[-130,-110],[-149,184],[24,190],[256,-82],[210,-47],[100,131],[-127,256],[2,226],[-175,92],[62,163],[170,-26],[241,-93]],[[80173,62023],[137,267],[304,166],[159,-14],[62,-226],[-122,-260],[-64,-341],[-240,-284],[-228,183],[-8,509]],[[6646,62221],[14,65],[48,97],[-19,116],[16,55],[21,-11],[107,-100],[49,-51],[45,-79],[71,-207],[-7,-33],[-108,-126],[-89,-92],[-41,-99],[-69,84],[8,165],[-46,216]],[[6469,62944],[27,50],[99,-56],[73,-90],[-23,-71],[-94,-43],[-47,125],[-32,48],[-3,37]],[[6298,63045],[21,72],[137,-26],[-9,-63],[-149,17]],[[6030,63329],[74,82],[23,-38],[80,-196],[-15,-34],[-19,8],[-97,21],[-35,133],[-11,24]],[[5611,63616],[14,43],[43,58],[64,-12],[5,-138],[-33,-58],[-93,107]],[[26396,63516],[146,183],[60,213],[126,131],[142,116],[210,56],[67,65],[240,-42],[219,-7],[261,-201],[110,-216],[260,66],[98,-138],[235,-366],[173,-267],[92,8],[165,-120],[-20,-167],[205,-24],[210,-242],[-33,-138],[-185,-75],[-187,-29],[-191,46],[-398,-57],[186,329],[-113,154],[-179,39],[-96,171],[-66,336],[-157,-23],[-259,159],[-83,124],[-362,91],[-97,115],[104,148],[-273,30],[-199,-307],[-115,-8],[-40,-144],[-138,-65],[-118,56]],[[28220,65099],[60,375],[84,-23],[97,-491],[1,-343],[-68,-29],[-70,340],[-104,171]],[[83362,64497],[163,581],[223,447],[127,-176],[-49,-357],[-167,-947],[-119,-485],[-146,499],[-32,438]],[[28061,66408],[130,47],[184,-18],[8,-153],[-303,-95],[-19,219]],[[28391,66555],[220,-265],[-48,-420],[-51,75],[4,309],[-124,234],[-1,67]],[[86767,70070],[2,281],[154,352],[158,-68],[114,248],[204,-127],[35,-203],[-156,-357],[-114,189],[-143,-137],[-73,-346],[-181,168]],[[59437,71293],[8,-48],[-285,-240],[-136,77],[-64,237],[132,22]],[[59092,71341],[19,3],[40,143],[200,-8],[253,176],[-188,-251],[21,-111]],[[56531,71424],[52,251],[152,-199],[216,34],[207,-42],[-7,-103],[151,71],[-35,-175],[-400,-50],[3,98],[-339,115]],[[53453,72802],[38,304],[325,-54],[284,64],[211,51],[-100,-465],[41,-183],[-58,-303],[-213,222],[-141,64],[-387,300]],[[52266,74774],[153,-30],[139,183],[166,-419],[-39,-782],[-126,38],[-113,-197],[-105,156],[-11,713],[-64,338]],[[85946,70252],[263,182],[145,371],[280,306],[203,403],[553,177],[297,-121],[291,1050],[185,-282],[408,591],[158,229],[174,723],[-47,664],[117,374],[295,108],[152,-819],[-9,-479],[-256,-595],[4,-610],[-104,-472],[48,-296],[-145,-416],[-355,-278],[-488,-36],[-396,-675],[-186,227],[-12,442],[-483,-130],[-329,-279],[-325,-11],[282,-435],[-186,-1004],[-179,-248],[-135,229],[69,533],[-176,172],[-113,405]],[[52373,75546],[56,219],[179,226],[47,-507],[-92,-456],[-126,120],[-64,398]],[[88837,75727],[138,455],[296,33],[81,817],[83,460],[326,-615],[213,-198],[195,-126],[197,250],[62,-663],[-412,-162],[-244,-587],[-436,404],[-152,-646],[-308,-9],[-39,587]],[[32113,78187],[105,183],[97,-288],[202,-79],[257,16],[-137,-242],[-102,-38],[-353,250],[-69,198]],[[32078,80046],[96,49],[365,-148],[284,-247],[8,-108],[-135,-11],[-360,186],[-258,279]],[[14321,80439],[24,137],[291,-129],[171,-89],[261,-63],[94,-204],[138,-280],[277,-244],[115,-327],[-140,-82],[-456,269],[-84,209],[-248,207],[-50,168],[-286,107],[-107,321]],[[33494,78880],[173,208],[-121,160],[234,356],[287,941],[172,336],[241,204],[129,-26],[-54,-160],[-148,-372],[-184,-517],[181,199],[187,-126],[-98,-206],[247,-162],[128,144],[277,-182],[-86,-433],[194,101],[36,-313],[86,-367],[-117,-520],[-125,-22],[-183,111],[60,484],[-77,75],[-322,-513],[-166,21],[196,277],[-267,144],[-298,-35],[-539,18],[-43,175]],[[12989,82396],[16,188],[131,-76],[267,47],[-84,-671],[242,-475],[-111,1],[-167,270],[-103,272],[-140,184],[-51,260]],[[89331,81264],[24,808],[257,271],[-110,274],[123,83],[73,-391],[96,-569],[-7,-581],[114,-597],[280,-1046],[-411,195],[-171,-854],[271,-605],[-8,-413],[-211,356],[-182,-457],[-51,496],[31,575],[-32,638],[64,446],[13,790],[-163,581]],[[47896,83153],[233,24],[298,-365],[-149,-406]],[[48278,82406],[46,-422],[-210,-528],[-493,-349],[-393,89],[225,617],[-145,601],[378,463],[210,276]],[[53028,83536],[408,195],[88,-296],[-166,-478],[-291,333],[-39,246]],[[7036,84529],[252,210],[148,90],[185,-40],[117,-183],[-240,-281],[-277,-225],[-142,152],[-43,277]],[[48291,84129],[101,611],[216,480],[222,-47],[335,49],[-297,-639],[283,81],[304,-3],[-72,-481],[-250,-530],[287,-38],[270,-759],[190,-95],[171,-673],[79,-233],[337,-113],[-34,-378],[-142,-173],[111,-305],[-250,-310],[-371,6],[-473,-163],[-130,116],[-183,-276],[-257,67],[-195,-226],[-148,118],[407,621],[249,127],[-436,99],[-79,235],[291,183],[-152,319],[52,387],[414,-54],[40,343],[-190,372],[-337,104],[-66,160],[101,264],[-92,163],[-149,-279],[-17,569],[-140,301]],[[3485,86155],[274,101],[220,-54],[27,-226],[-171,-92],[-182,110],[-168,161]],[[27677,87220],[13,41],[107,177],[114,-13],[70,-121],[-108,-310],[-123,50],[-73,176]],[[26668,87478],[207,273],[381,-6],[-6,-114],[-325,-326],[-196,13],[-61,160]],[[2280,88041],[17,223],[171,-113],[173,61],[225,-156],[276,-79],[-23,-64],[-211,-125],[-211,128],[-106,107],[-245,-34],[-66,52]],[[25771,88121],[242,292],[35,465],[95,542],[201,-49],[51,-259],[143,91],[161,-155],[304,-203],[318,-184],[25,-281],[204,46],[199,-196],[-247,-186],[-432,142],[-156,266],[-275,-314],[-396,-306],[-95,346],[-377,-57]],[[43242,89344],[188,385],[421,87],[433,-400],[422,321],[349,-167],[453,315],[461,-42],[-64,-382],[314,-403],[-361,-451],[-801,-405],[-240,-107],[-365,87],[-775,187],[273,261],[-605,289],[492,114],[-12,174],[-583,137]],[[28545,90512],[118,331],[255,82],[217,-163],[3,-253],[-32,-82],[-180,-174],[-312,-30],[-69,289]],[[0,88971],[0,2354],[681,-451],[728,-588],[-24,-367],[187,-147],[-64,429],[754,-88],[544,-553],[-276,-257],[-455,-61],[-7,-578],[-111,-122],[-260,17],[-212,206],[-369,172],[-62,257],[-283,96],[-315,-76],[-151,207],[60,219],[-333,-140],[126,-278],[-158,-251]],[[22278,91583],[245,183],[194,256],[295,-168],[166,-106],[84,-112],[169,-226],[-173,-207],[-374,179],[-226,-65],[-380,266]],[[99645,92586],[354,247],[0,-404],[-305,-30],[-49,187]],[[0,92429],[0,404],[36,24],[235,-1],[402,-169],[-24,-81],[-286,-141],[-363,-36]],[[26228,91219],[16,648],[394,-45]],[[26638,91822],[411,-87],[373,-293],[17,-293],[-207,-315],[196,-316],[-36,-288],[-544,-413],[-386,-91],[-287,178],[-83,-297],[-268,-498]],[[25824,89109],[-81,-258],[-322,-400]],[[25421,88451],[-397,-39],[-220,-250],[-18,-384],[-323,-74],[-340,-479],[-301,-665],[-108,-466]],[[23714,86094],[-15,-686],[408,-99]],[[24107,85309],[125,-553],[130,-448],[388,117],[517,-256],[277,-225],[199,-279]],[[25743,83665],[348,-162],[294,-249]],[[26385,83254],[459,-34],[302,-58],[-45,-511],[86,-594],[201,-661],[414,-561],[214,192],[150,607],[-145,934],[-196,311],[445,276],[314,415],[154,411]],[[28738,83981],[-22,395],[-189,502]],[[28527,84878],[-338,445],[328,619],[-121,535],[-93,922],[194,137],[476,-161],[286,-57],[230,155],[258,-200],[342,-343],[85,-229],[495,-45],[-8,-496],[92,-747],[254,-92],[201,-348],[402,328],[266,652],[184,274],[216,-527],[362,-754],[307,-709],[-112,-371],[370,-333],[250,-338],[442,-152],[179,-189],[110,-500],[216,-78],[112,-223],[20,-664],[-202,-222],[-199,-207],[-458,-210],[-349,-486],[-470,-96],[-594,125],[-417,4],[-287,-41],[-233,-424],[-354,-262],[-401,-782],[-320,-545],[236,97],[446,776],[583,493]],[[31513,79609],[416,59],[245,-290]],[[32174,79378],[-262,-397],[88,-637],[91,-446],[361,-295],[459,86],[278,664],[19,-429],[180,-214],[-344,-387],[-615,-351],[-276,-239],[-310,-426],[-211,44],[-11,500],[483,488],[-445,-19],[-309,-72]],[[31350,77248],[48,-194],[-296,-286],[-286,-204],[-293,-175]],[[30523,76389],[-159,-386],[-35,-98]],[[30329,75905],[-3,-313],[92,-313],[115,-15],[-29,216],[83,-131],[-22,-169],[-188,-96]],[[30377,75084],[-133,12],[-205,-104]],[[30039,74992],[-121,-29],[-162,-29],[-231,-171],[408,111],[82,-112],[-389,-177],[-177,-1],[8,72],[-84,-164],[82,-27],[-60,-424],[-203,-455],[-20,152]],[[29172,73738],[-61,31],[-91,147]],[[29020,73916],[57,-318]],[[29077,73598],[66,-106],[8,-222]],[[29151,73270],[-89,-230],[-157,-472],[-25,24],[86,402]],[[28966,72994],[-142,226],[-33,490]],[[28791,73710],[-53,-255],[59,-375]],[[28797,73080],[-175,88],[183,-186]],[[28805,72982],[12,-562],[79,-41],[29,-204],[39,-591],[-176,-439],[-288,-175],[-182,-346],[-139,-38],[-141,-217],[-39,-199],[-305,-383],[-157,-281],[-131,-351],[-43,-419],[50,-411],[92,-505],[124,-418],[1,-256],[132,-685],[-9,-398],[-12,-230],[-69,-361]],[[27672,65472],[-83,-74],[-137,71]],[[27452,65469],[-44,259]],[[27408,65728],[-106,136],[-147,508]],[[27155,66372],[-129,452],[-42,231],[57,393],[-77,325],[-217,494]],[[26747,68267],[-108,91],[-281,-269]],[[26358,68089],[-49,30]],[[26309,68119],[-135,276],[-174,146]],[[26000,68541],[-314,-75],[-247,66],[-212,-41]],[[25227,68491],[-118,-83],[54,-166]],[[25163,68242],[-5,-240],[59,-117],[-53,-77],[-103,87],[-104,-112],[-202,18]],[[24755,67801],[-207,313],[-242,-74]],[[24306,68040],[-202,137],[-173,-42],[-234,-138],[-253,-438],[-276,-255],[-152,-282],[-63,-266],[-3,-407],[14,-284],[52,-201]],[[23016,65864],[1,-1],[-1,-1],[-107,-516]],[[22909,65346],[-49,-426],[-20,-791],[-27,-289],[48,-322],[86,-288],[56,-458],[184,-440],[65,-337],[109,-291],[295,-157],[114,-247],[244,165],[212,60],[208,106],[175,101],[176,241],[67,345],[22,496],[48,173],[188,155],[294,137],[246,-21],[169,50],[66,-125],[-9,-285],[-149,-351],[-66,-360],[51,-103],[-42,-255],[-69,-461],[-71,152],[-58,-10]],[[25472,61510],[1,-87],[53,-3],[-5,-160],[-45,-256],[24,-91],[-29,-212],[18,-56],[-32,-299],[-55,-156],[-50,-19],[-55,-205]],[[25297,59966],[90,-107],[24,88],[82,-75]],[[25493,59872],[29,-23],[61,104],[79,8],[26,-48],[43,29],[129,-53]],[[25860,59889],[128,16],[90,65]],[[26078,59970],[32,66],[89,-31],[66,-40],[73,14],[55,51],[127,-82],[44,-13],[85,-110],[80,-132],[101,-91],[73,-162]],[[26903,59440],[-24,-57],[-14,-132],[29,-216],[-64,-202],[-30,-237],[-9,-261],[15,-152],[7,-266],[-43,-58],[-26,-253],[19,-156],[-56,-151],[12,-159],[43,-97]],[[26762,57043],[70,-321],[108,-238],[130,-252]],[[27070,56232],[100,-212]],[[27170,56020],[-6,-125],[111,-27]],[[27275,55868],[26,48],[77,-145],[136,42],[119,150],[168,119],[95,176],[153,-34],[-10,-58],[155,-21],[124,-102],[90,-177],[105,-164]],[[28513,55702],[143,-18],[209,412],[114,63],[3,195],[51,500],[159,274],[175,11],[22,123],[218,-49],[218,298],[109,132],[134,285],[98,-36],[73,-156],[-54,-199]],[[30185,57537],[-8,-139],[-163,-69],[91,-268],[-3,-309]],[[30102,56752],[-123,-343],[105,-469]],[[30084,55940],[120,38],[62,427],[-86,208],[-14,447],[346,241],[-38,278],[97,186],[100,-415],[195,-9],[180,-330],[11,-195],[249,-6],[297,61],[159,-264]],[[31762,56607],[213,-73],[155,184]],[[32130,56718],[4,149],[344,35],[333,9],[-236,-175],[95,-279],[222,-44],[210,-291],[45,-473],[144,13],[109,-139]],[[33400,55523],[183,-217],[171,-385],[8,-304],[105,-14],[149,-289],[109,-205]],[[34125,54109],[333,-119],[30,107],[225,43],[298,-159]],[[35011,53981],[95,-65],[204,-140],[294,-499],[46,-242]],[[35650,53035],[95,28],[69,-327],[155,-1033],[149,-97],[7,-408],[-208,-487],[86,-178],[491,-92],[10,-593],[211,388],[349,-212],[462,-361],[135,-346],[-45,-327],[323,182],[540,-313],[415,23],[411,-489],[355,-662],[214,-170],[237,-24],[101,-186],[94,-752],[46,-358],[-110,-977],[-142,-385],[-391,-822],[-177,-668],[-206,-513],[-69,-11],[-78,-435],[20,-1107],[-77,-910],[-30,-390],[-88,-233],[-49,-790],[-282,-771],[-47,-610],[-225,-256],[-65,-355],[-302,2],[-437,-227],[-195,-263],[-311,-173],[-327,-470],[-235,-586],[-41,-441],[46,-326],[-51,-597],[-63,-289],[-195,-325],[-308,-1040],[-244,-468],[-189,-277],[-127,-562],[-183,-337]],[[35174,30629],[-121,-372],[-313,-328],[-205,118],[-151,-63],[-256,253],[-189,-19],[-169,327]],[[33770,30545],[-19,-308],[353,-506],[-38,-408],[173,-257],[-14,-289],[-267,-757],[-412,-317],[-557,-123],[-305,59],[59,-352],[-57,-442],[51,-298],[-167,-208],[-284,-82],[-267,216],[-108,-155],[39,-587],[188,-178],[152,186],[82,-307],[-255,-183],[-223,-367],[-41,-595],[-66,-316],[-262,-2],[-218,-302],[-80,-443]],[[31227,23224],[274,-433],[265,-119]],[[31766,22672],[-96,-531],[-328,-333],[-180,-692],[-254,-234],[-113,-276],[89,-614],[185,-342],[-117,30]],[[30952,19680],[-247,4],[-134,-145],[-250,-213],[-45,-552],[-118,-14],[-313,192],[-318,412],[-346,338],[-87,374],[79,346],[-140,393],[-36,1007],[119,568],[293,457],[-422,172],[265,522],[94,982],[309,-208],[145,1224],[-186,157],[-87,-738],[-175,83],[87,845],[95,1095],[127,404]],[[29661,27385],[-79,576],[-23,666]],[[29559,28627],[117,19],[170,954],[192,945],[118,881],[-64,885],[83,487],[-34,730],[163,721],[50,1143],[89,1227],[87,1321],[-20,967],[-58,832]],[[30452,39739],[-279,340],[-24,242],[-551,593],[-498,646],[-214,365],[-115,488],[46,170],[-236,775],[-274,1090],[-262,1177],[-114,269],[-87,435],[-216,386],[-198,239],[90,264],[-134,563],[86,414],[221,373]],[[27693,48568],[148,442],[-60,258],[-106,-275],[-166,259],[56,167],[-47,536],[97,89],[52,368],[105,381],[-20,241],[153,126],[190,236]],[[28095,51396],[-37,183],[103,44],[-12,296],[65,214],[138,40],[117,371],[106,310],[-102,141],[52,343],[-62,540],[59,155],[-44,500],[-112,315]],[[28366,54848],[-93,170],[-59,319],[68,158],[-70,40]],[[28212,55535],[-52,195],[-138,165]],[[28022,55895],[-122,-38],[-56,-205],[-112,-149],[-61,-20],[-27,-123],[132,-321],[-75,-76],[-40,-87],[-130,-30],[-48,353],[-36,-101],[-92,35],[-56,238],[-114,39],[-72,69],[-119,-1],[-8,-128],[-32,89]],[[26954,55439],[-151,131],[-56,124],[32,103],[-11,130],[-77,142],[-109,116],[-95,76],[-19,173],[-73,105],[18,-172],[-55,-141],[-64,164],[-89,58],[-38,120],[2,179],[36,187],[-78,83],[64,114]],[[26191,57131],[-96,186],[-130,238],[-61,200],[-117,185],[-140,267]],[[25647,58207],[31,92],[46,-89]],[[25724,58210],[21,41]],[[25745,58251],[-48,185]],[[25697,58436],[-84,52],[-31,-140]],[[25582,58348],[-161,9],[-100,57],[-115,117],[-154,37],[-79,127]],[[24973,58695],[-142,103],[-174,11],[-127,117],[-149,244]],[[24381,59170],[-314,636]],[[24067,59806],[-144,192],[-226,154]],[[23697,60152],[-156,-43],[-223,-223],[-140,-58],[-196,156],[-208,112],[-260,271],[-208,83],[-314,275],[-233,282],[-70,158],[-155,35],[-284,187],[-116,270],[-299,335],[-139,373],[-66,288],[93,57],[-29,169],[64,153],[1,204],[-93,266],[-25,235],[-94,298],[-244,587],[-280,462],[-135,368],[-238,241],[-51,145],[42,365]],[[19641,66203],[-142,137],[-164,288]],[[19335,66628],[-69,412],[-149,48],[-162,311],[-130,288],[-12,184],[-149,446],[-99,452],[5,227]],[[18570,68996],[-201,235],[-93,-26]],[[18276,69205],[-159,163],[-44,-240],[46,-284],[27,-444],[95,-243],[206,-407],[46,-139],[42,-42],[37,-203],[49,8],[56,-381],[85,-150],[59,-210],[174,-300],[92,-550],[83,-259],[77,-277],[15,-311],[134,-20],[112,-268],[100,-264],[-6,-106],[-117,-217],[-49,3],[-74,359]],[[19362,64423],[-182,337],[-200,286]],[[18980,65046],[-142,150],[9,432],[-42,320],[-132,183],[-191,264],[-37,-76],[-70,154],[-171,143],[-164,343],[20,44],[115,-33],[103,221],[10,266],[-214,422],[-163,163],[-102,369],[-103,388],[-129,472],[-113,531]],[[17464,69802],[-46,302],[-180,340],[-130,71],[-30,169],[-156,30],[-100,159],[-258,59]],[[16564,70932],[-70,95],[-34,324]],[[16460,71351],[-270,594],[-231,821],[10,137],[-123,195],[-215,495],[-38,482],[-148,323],[61,489],[-10,507],[-89,453],[109,557],[67,1072],[-50,792],[-88,506],[-80,274],[33,115],[402,-200],[148,-558]],[[15948,78405],[68,156],[-44,485],[-94,484]],[[15878,79530],[-38,1],[-537,581],[-199,255]],[[15104,80367],[-503,245],[-155,523],[40,362]],[[14486,81497],[-356,252],[-48,476],[-336,429],[-6,304]],[[13740,82958],[-153,223],[-245,188],[-78,515],[-358,478],[-150,558],[-267,38],[-441,15],[-326,170],[-574,613],[-266,112],[-486,211]],[[10396,86079],[-385,-50],[-546,271]],[[9465,86300],[-330,252],[-309,-125],[58,-411],[-154,-38],[-321,-123],[-245,-199]],[[8164,85656],[-307,-126],[-40,348]],[[7817,85878],[125,580],[295,182],[-76,148],[-354,-329],[-190,-394],[-400,-420],[203,-287],[-262,-424]],[[7158,84934],[-299,-247],[-278,-181]],[[6581,84506],[-69,-261],[-434,-305],[-87,-278],[-325,-252],[-191,45],[-259,-165],[-282,-201],[-231,-197],[-477,-169],[-43,99],[304,276],[271,182],[296,324],[345,66],[137,243],[385,353],[62,119],[205,208],[48,448],[141,349],[-320,-179],[-90,102],[-150,-215],[-181,300],[-75,-212],[-104,294],[-278,-236],[-170,0],[-24,352]],[[4985,85596],[50,217],[-179,210]],[[4856,86023],[-361,-113],[-235,277],[-190,142],[-1,334],[-214,252],[108,340],[226,330],[99,303],[225,43],[191,-94],[224,285],[201,-51],[212,183],[-52,270],[-155,106],[205,228],[-170,-7],[-295,-128],[-85,-131],[-219,131],[-392,-67],[-407,142],[-117,238],[-351,343],[390,247],[620,289],[228,0]],[[4541,89915],[-38,-295],[586,22]],[[5089,89642],[-225,366]],[[4864,90008],[-342,226],[-197,295]],[[4325,90529],[-267,252],[-381,187],[155,309],[493,19],[350,270],[66,287],[284,281],[271,68],[526,262],[256,-40],[427,315],[421,-124],[201,-266],[123,114],[469,-35],[-16,-136],[425,-101],[283,59],[585,-186],[534,-56],[214,-77],[370,96],[421,-177],[302,-83]],[[10837,91767],[518,-142]],[[11355,91625],[438,-284],[289,-55]],[[12082,91286],[244,247],[336,184],[413,-72],[416,259],[455,148],[191,-245],[207,138],[62,278],[192,-63],[470,-530],[369,401]],[[15437,92031],[38,-448],[341,96]],[[15816,91679],[105,173],[337,-34],[424,-248],[650,-217],[383,-100],[272,38]],[[17987,91291],[375,-300],[-391,-293]],[[17971,90698],[502,-127],[750,70],[236,103],[296,-354],[302,299],[-283,251],[179,202],[338,27],[223,59],[224,-141],[279,-321],[310,47],[491,-266],[431,94],[405,-14],[-32,367],[247,103],[431,-200],[-2,-559],[177,471],[223,-16],[126,594],[-298,364],[-324,239],[22,653],[329,429],[366,-95],[281,-261],[378,-666],[-247,-290],[517,-120],[-1,-604],[371,463],[332,-380],[-83,-438],[269,-399],[290,427],[202,510]],[[19722,91216],[-824,-103],[-374,-41]],[[18524,91072],[-151,279],[-379,161],[-246,-66],[-343,468],[185,62],[429,101],[392,-26],[362,103],[-537,138],[-594,-47],[-394,12],[-146,217],[644,237],[-428,-9],[-485,156],[233,443],[193,235],[744,359],[284,-114],[-139,-277],[618,179],[386,-298],[314,302],[254,-194],[227,-580],[140,244],[-197,606],[244,86],[276,-94],[311,-239],[175,-575],[86,-417],[466,-293],[502,-279],[-31,-260],[-456,-48],[178,-227],[-94,-217],[-503,93],[-478,160],[-322,-36],[-522,-201]],[[20294,93981],[95,83],[372,24],[211,-130],[-244,-390],[-434,413]],[[27534,93906],[12,213],[133,39],[636,-63],[479,-325],[25,-163],[-296,17],[-299,13],[-304,-80],[-80,36],[-306,313]],[[24943,93258],[213,528],[286,241],[717,158],[-204,-382],[219,-369],[256,477],[704,242],[477,-611],[-42,-387],[550,172],[263,235],[616,-299],[383,-282],[36,-258],[515,134],[290,-376],[670,-234],[242,-238],[263,-553],[-510,-275],[654,-386],[441,-130],[400,-543],[437,-39],[-87,-414],[-487,-687],[-342,253],[-437,568],[-359,-74],[-35,-338],[292,-344],[377,-272],[114,-157],[181,-584],[-96,-425],[-350,160],[-697,473],[393,-509],[289,-357],[45,-206],[-753,236],[-596,343],[-337,287],[97,167],[-414,304],[-405,286],[5,-171],[-803,-94],[-235,203],[183,435],[522,10],[571,76],[-92,211],[96,294],[360,576],[-77,261],[-107,203],[-425,286],[-563,201],[178,150],[-294,367],[-245,34],[-219,201],[-149,-175],[-503,-76],[-1011,132],[-588,174],[-450,89],[-231,207],[290,270],[-394,2],[-88,599]],[[21528,93420],[5,189],[567,-73],[-306,386],[329,286],[331,-124],[496,75],[72,-172],[-259,-283],[420,-254],[-50,-532],[-455,-229],[-268,50],[-192,225],[-690,456]],[[88850,93928],[263,234],[348,54],[394,-226],[34,-155],[-421,-4],[-569,66],[-49,31]],[[23324,93674],[4,294],[145,251],[276,161],[579,-20],[530,-144],[-415,-526],[-331,-115],[-298,-442],[-317,22],[-173,519]],[[15808,92470],[-147,259],[-641,312]],[[15020,93041],[93,193],[218,489]],[[15331,93723],[241,388],[-272,362],[939,93],[397,-123],[709,-33],[270,-171],[298,-249],[-349,-149],[-681,-415],[-344,-414]],[[16539,93012],[0,-248],[-731,-294]],[[90588,94993],[66,192],[518,-89],[697,-155],[-321,-234],[-444,53],[-516,233]],[[23105,94849],[148,266],[399,159],[243,-208],[101,-187],[-151,-229],[-403,44],[-337,155]],[[88048,95046],[149,406],[366,111],[734,-26],[1004,-313],[-219,-439],[-1023,16],[-461,-139],[-550,384]],[[21509,95681],[299,-18],[419,201],[390,-34],[22,77],[212,-273],[9,-303],[-127,-440],[-458,-60],[-298,94],[5,345],[-455,-46],[-18,457]],[[17302,95023],[379,577],[262,165],[782,-199],[493,-350],[485,-45],[-397,565],[255,215],[286,-68],[94,-282],[109,-210],[247,99],[291,-26],[49,-289],[-169,-281],[-940,-91],[-701,-256],[-423,-14],[-35,193],[577,261],[-1255,-70],[-389,106]],[[64293,93128],[284,126],[-10,323],[551,503],[-255,73],[665,518],[-75,268],[621,312],[917,380],[925,110],[475,220],[541,76],[193,-233],[-187,-184],[-984,-293],[-848,-282],[-863,-562],[-414,-577],[-435,-568],[56,-491],[531,-484],[-164,-52],[-907,77],[-74,262],[-503,158],[-40,320]],[[23022,95926],[104,242],[573,-37],[308,-190],[547,1],[240,-194],[-64,-222],[319,-134],[177,-140],[374,-26],[406,-50],[441,128],[566,51],[451,-42],[298,-223],[62,-244],[-174,-157],[-414,-127],[-355,72],[-797,-91],[-570,-11],[-449,73],[-738,190],[-96,325],[-34,293],[-279,258],[-574,72],[-322,183]],[[15873,95551],[472,442],[570,383],[426,-9],[381,87],[-38,-454],[-214,-205],[-259,-29],[-517,-252],[-444,-91],[-377,128]],[[81143,94175],[250,112],[142,-379]],[[81535,93908],[122,153],[444,93],[892,-97],[67,-276],[1162,-88],[15,451]],[[84237,94144],[590,-103],[443,3]],[[85270,94044],[449,-312],[128,-378],[-165,-247],[349,-465],[437,-240],[268,620],[446,-266],[473,159],[538,-182],[204,166],[455,-83],[-201,549],[367,256],[2509,-384],[236,-351],[727,-451],[1122,112],[553,-98],[231,-244],[-33,-432],[342,-168],[372,121],[492,15],[525,-116],[526,66],[484,-526],[344,189],[-224,378]],[[97224,91732],[123,263],[886,-166]],[[98233,91829],[578,36],[799,-282],[389,-258],[0,-2354],[-2,-3],[-357,-260],[-360,44],[250,-315],[166,-487],[128,-159],[32,-244],[-71,-157],[-518,129],[-777,-445],[-247,-69],[-425,-415],[-403,-362],[-102,-269],[-397,409],[-724,-464]],[[96192,85904],[-126,220],[-268,-254]],[[95798,85870],[-371,81],[-90,-388],[-333,-572],[10,-239],[316,-132],[-37,-860],[-258,-22],[-119,-494],[116,-255]],[[95032,82989],[-486,-301],[-96,-675]],[[94450,82013],[-415,-144],[-83,-600],[-400,-551],[-103,407],[-119,862],[-155,1313],[134,819],[234,353]],[[93543,84472],[15,276],[431,132]],[[93989,84880],[496,744],[479,608],[499,471],[223,833],[-337,-50],[-167,-487]],[[95182,86999],[-705,-648],[-227,726]],[[94250,87077],[-717,-201],[-696,-990],[230,-362],[-620,-154],[-430,-61],[20,427],[-431,90],[-344,-291],[-850,102]],[[90412,85637],[-913,-175],[-900,-1153]],[[88599,84309],[-1065,-1394],[438,-74],[136,-370],[270,-132]],[[88378,82339],[178,296],[305,-39]],[[88861,82596],[401,-650]],[[89262,81946],[9,-502],[-217,-591]],[[89054,80853],[-23,-705],[-126,-945],[-418,-855],[-94,-409],[-377,-688],[-374,-682],[-179,-349],[-370,-346],[-175,-8],[-175,287],[-373,-432],[-43,-197]],[[86327,75524],[-106,36]],[[86221,75560],[-120,-201],[-83,-201]],[[86018,75158],[10,-424],[-143,-130],[-50,-105],[-104,-174],[-185,-97],[-121,-159],[-9,-256],[-32,-65],[111,-96],[157,-259]],[[85652,73393],[240,-697],[68,-383],[3,-681],[-105,-325],[-252,-113],[-222,-245],[-250,-51],[-31,322]],[[85103,71220],[52,443],[-123,615]],[[85032,72278],[206,99],[-190,506]],[[85048,72883],[-135,113],[-34,-112]],[[84879,72884],[-81,-49],[-10,112],[-72,54],[-75,94]],[[84641,73095],[77,260],[65,69]],[[84783,73424],[-25,108],[71,319]],[[84829,73851],[-18,97],[-163,64]],[[84648,74012],[-131,158]],[[84517,74170],[-388,-171],[-204,-277],[-300,-161],[148,274],[-58,230],[220,397],[-147,310],[-242,-209],[-314,-411],[-171,-381],[-272,-29],[-142,-275],[147,-400],[227,-97],[9,-265]],[[83030,72705],[220,-172],[311,421]],[[83561,72954],[247,-230],[179,-15]],[[83987,72709],[46,-310],[-394,-165]],[[83639,72234],[-130,-319],[-270,-296],[-142,-414]],[[83097,71205],[299,-324],[109,-582]],[[83505,70299],[169,-541],[189,-454],[-5,-439],[-174,-161],[66,-315],[164,-184],[-43,-481],[-71,-468],[-155,-53],[-203,-640],[-225,-775],[-258,-705],[-382,-545],[-386,-498],[-313,-68],[-170,-262],[-96,192],[-157,-294],[-388,-296],[-294,-90],[-95,-624],[-154,-35],[-73,429],[66,228]],[[80517,63220],[-373,190],[-131,-97]],[[80013,63313],[-371,-505],[-231,-558],[-61,-410],[212,-623],[260,-772],[252,-365],[169,-475],[127,-1093],[-37,-1039],[-232,-389],[-318,-381],[-227,-492],[-346,-550],[-101,378],[78,401],[-206,335]],[[78981,56775],[-233,87],[-112,307],[-141,611]],[[78495,57780],[-249,271],[-238,-11],[41,464],[-245,-3],[-22,-650],[-150,-863],[-90,-522],[19,-428],[181,-18],[113,-539],[50,-512],[155,-338],[168,-69],[144,-306]],[[78372,54256],[64,-56],[164,-356],[116,-396],[16,-398],[-29,-269],[27,-203],[20,-349],[98,-163],[109,-523],[-5,-199],[-197,-40],[-263,438],[-329,469],[-32,301],[-161,395],[-38,489],[-100,322],[30,431],[-61,250]],[[77801,54399],[-110,227],[-47,292],[-148,334],[-135,280],[-45,-347],[-53,328],[30,369],[82,566]],[[77375,56448],[-27,439],[86,452],[-94,350],[23,644],[-113,306],[-90,707],[-50,746],[-121,490],[-183,-297],[-315,-421],[-156,53],[-172,138],[96,732],[-58,554],[-218,681],[34,213],[-163,76],[-197,481]],[[75657,62792],[-79,309],[-16,301],[-53,284]],[[75509,63686],[-116,344],[-256,23],[25,-243],[-87,-329],[-118,120],[-41,-108],[-78,65],[-108,53]],[[74730,63611],[-39,-216],[-189,7],[-343,-122],[16,-445],[-148,-349],[-400,-398],[-311,-695],[-209,-373]],[[73107,61020],[-276,-386],[-1,-272]],[[72830,60362],[-138,-146]],[[72692,60216],[-250,-212],[-130,-31]],[[72312,59973],[-84,-450],[58,-769],[15,-490],[-118,-561],[-1,-1004],[-144,-29],[-126,-450],[84,-195]],[[71996,56025],[-253,-167],[-93,-402]],[[71650,55456],[-112,-170],[-263,552],[-128,827],[-107,596],[-97,279],[-148,568],[-69,739],[-48,369],[-253,811],[-115,1145],[-83,756],[1,716],[-54,553],[-404,-353],[-196,70],[-362,716],[133,214],[-82,232],[-326,501]],[[68937,64577],[-203,150]],[[68734,64727],[-83,425],[-215,449]],[[68436,65601],[-512,-111],[-451,-11],[-391,-83]],[[67082,65396],[-523,179]],[[66559,65575],[-302,136],[-314,76]],[[65943,65787],[-118,725],[-133,105],[-214,-106],[-280,-286],[-339,196],[-281,454],[-267,168],[-186,561],[-205,788],[-149,-96],[-177,196]],[[63594,68492],[-103,-231],[-165,29]],[[63326,68290],[58,-261],[-25,-135],[89,-445]],[[63448,67449],[109,-510],[137,-135],[47,-207]],[[63741,66597],[190,-248],[16,-244]],[[63947,66105],[-27,-197],[35,-199],[80,-165],[37,-194],[41,-145]],[[64113,65205],[-18,430],[75,310],[76,64]],[[64246,66009],[84,-186],[5,-345]],[[64335,65478],[-61,-348]],[[64274,65130],[53,-226]],[[64327,64904],[49,29],[11,-162],[217,93],[230,-15],[168,-18],[190,400],[207,379],[176,364]],[[65575,65974],[80,201],[35,-51],[-26,-244],[-37,-108]],[[65627,65772],[38,-466]],[[65665,65306],[125,-404],[155,-214]],[[65945,64688],[204,-78],[164,-107]],[[66313,64503],[125,-339],[75,-196],[100,-75],[-1,-132],[-101,-352],[-44,-166],[-117,-189],[-104,-404],[-126,31],[-58,-141],[-44,-300],[34,-395],[-26,-72],[-128,2],[-174,-221],[-27,-288],[-63,-125],[-173,5],[-109,-149]],[[65352,60997],[1,-239],[-134,-164]],[[65219,60594],[-153,56],[-186,-199]],[[64880,60451],[-128,-33],[-201,-159]],[[64551,60259],[-54,-263],[-6,-201],[-277,-249],[-444,-276],[-249,-417]],[[63521,58853],[-122,-32],[-83,34]],[[63316,58855],[-163,-245]],[[63153,58610],[-177,-113],[-233,-31]],[[62743,58466],[-70,-34],[-61,-156],[-73,-43]],[[62539,58233],[-42,-150],[-138,13]],[[62359,58096],[-89,-80],[-192,30],[-72,345],[8,323],[-46,174],[-54,437],[-80,243],[56,29],[-29,270],[34,114],[-12,257]],[[61883,60238],[-36,253],[-84,177]],[[61763,60668],[-22,236],[-143,212],[-148,495],[-79,482],[-192,406],[-124,97],[-184,563],[-32,411],[12,350],[-159,655],[-130,231],[-150,122],[-92,339],[15,133]],[[60335,65400],[-77,307],[-81,131]],[[60177,65838],[-108,440],[-170,476],[-141,406],[-139,-3],[44,325],[12,206],[34,236]],[[59709,67924],[-9,86]],[[59700,68010],[-78,-238],[-60,-446],[-75,-308],[-65,-103],[-93,191],[-125,263],[-198,847],[-29,-53],[115,-624],[171,-594],[210,-920],[102,-321],[90,-334],[249,-654],[-55,-103],[9,-384],[323,-530],[49,-121]],[[60240,63578],[90,-580],[-61,-107],[40,-608],[102,-706],[106,-145],[152,-219]],[[60669,61213],[161,-683],[77,-543]],[[60907,59987],[152,-288],[379,-558],[154,-336],[151,-341],[87,-203],[136,-178]],[[61966,58083],[66,-183],[-9,-245],[-158,-142],[119,-161]],[[61984,57352],[91,-109]],[[62075,57243],[54,-244],[125,-248]],[[62254,56751],[138,-2],[262,151],[302,70],[245,184],[138,39],[99,108],[158,20]],[[63596,57321],[89,12],[128,88],[147,59],[132,202],[105,2],[6,-163],[-25,-344],[1,-310],[-59,-214],[-78,-639],[-134,-659],[-172,-755],[-238,-866],[-237,-661],[-327,-806],[-278,-479],[-415,-586],[-259,-450],[-304,-715],[-64,-312],[-63,-140]],[[61551,49585],[-195,-236],[-68,-246],[-104,-44],[-40,-416],[-89,-238],[-54,-393],[-112,-195]],[[60889,47817],[-128,-728],[16,-335],[178,-216],[8,-153],[-76,-357],[16,-180],[-18,-282],[97,-370],[115,-583],[101,-129]],[[61198,44484],[45,-265],[-11,-588],[34,-519],[11,-923],[49,-290],[-83,-422],[-108,-410],[-177,-366],[-254,-225],[-313,-287],[-313,-634],[-107,-108],[-194,-420],[-115,-136],[-23,-421],[132,-448],[54,-346],[4,-177],[49,29],[-8,-579]],[[59870,36949],[-45,-274],[65,-102]],[[59890,36573],[-41,-246],[-116,-210]],[[59733,36117],[-229,-199],[-334,-320],[-122,-219],[24,-248],[71,-40],[-24,-311]],[[59119,34780],[-70,-430],[-32,-491],[-72,-267],[-190,-298],[-54,-86],[-118,-300],[-77,-303],[-158,-424],[-314,-609],[-196,-355]],[[57838,31217],[-209,-269],[-291,-229]],[[57338,30719],[-141,-31],[-36,-164],[-169,88],[-138,-113],[-301,114],[-168,-72],[-115,31],[-286,-233],[-238,-94],[-171,-223],[-127,-14],[-117,210],[-94,11],[-120,264],[-13,-82],[-37,159],[2,346],[-90,396],[89,108],[-7,453],[-182,553],[-139,501],[-1,1],[-199,768]],[[54540,33696],[-207,446],[-108,432],[-62,575],[-68,428],[-93,910],[-7,707],[-35,322],[-108,243],[-144,489],[-146,708],[-60,371],[-226,577],[-17,453]],[[53259,40357],[-26,372],[38,519],[96,541],[15,254],[90,532],[66,243],[159,386],[90,263],[29,438],[-15,335],[-83,211],[-74,358],[-68,355],[15,122],[85,235],[-84,570],[-57,396],[-139,374],[26,115]],[[53422,46976],[-39,183]],[[53383,47159],[-74,444]],[[53309,47603],[-228,626]],[[53081,48229],[-285,596],[-184,488],[-169,610],[9,196],[61,189],[67,430],[56,438]],[[52636,51176],[-52,90],[96,663]],[[52680,51929],[40,467],[-108,390]],[[52612,52786],[-127,100],[-56,265]],[[52429,53151],[-71,85],[3,163]],[[52361,53399],[-289,-213]],[[52072,53186],[-105,32],[-107,-133]],[[51860,53085],[-222,13],[-149,370],[-91,427]],[[51398,53895],[-197,390],[-209,-8]],[[50992,54277],[-245,1]],[[50747,54278],[-229,-69]],[[50518,54209],[-224,-126]],[[50294,54083],[-436,-346],[-154,-203],[-250,-171],[-248,168]],[[49206,53531],[-126,-7],[-194,116],[-178,-7],[-329,-103],[-193,-170],[-275,-217],[-54,15]],[[47857,53158],[-73,-5],[-286,282]],[[47498,53435],[-252,450],[-237,323]],[[47009,54208],[-187,381]],[[46822,54589],[-75,44],[-200,238],[-144,316],[-49,216],[-34,437]],[[46320,55840],[-122,349],[-108,232],[-71,76],[-69,118],[-32,261],[-41,130],[-80,97]],[[45797,57103],[-149,247],[-117,39],[-63,166],[1,90],[-84,125],[-18,127]],[[45367,57897],[-46,453]],[[45321,58350],[36,262]],[[45357,58612],[-115,460],[-138,210],[122,112],[134,415],[66,304]],[[45426,60113],[-24,318],[78,291],[34,557],[-30,583],[-34,294],[28,295],[-72,281],[-146,255]],[[45260,62987],[12,249]],[[45272,63236],[13,274],[106,161],[91,308],[-18,200],[96,417],[155,376],[93,95],[74,344],[6,315],[100,365],[185,216],[177,603],[144,235]],[[46494,67145],[259,66],[219,403],[139,158]],[[47111,67772],[232,493],[-70,735],[106,508],[37,312],[179,399],[278,270],[206,244],[186,612],[87,362],[205,-2],[167,-251],[264,41],[288,-131],[121,-6]],[[49397,71358],[267,323],[300,102],[175,244],[268,180],[471,105],[459,48],[140,-87],[262,232],[297,5],[113,-137],[190,35]],[[52339,72408],[302,239],[195,-71],[-9,-299],[236,217],[20,-113]],[[53083,72381],[-139,-289],[-2,-274]],[[52942,71818],[96,-147],[-36,-511],[-183,-297],[53,-322],[143,-10],[70,-281],[106,-92]],[[53191,70158],[326,-204],[117,51],[232,-98],[368,-264],[130,-526],[250,-114],[391,-248],[296,-293],[136,153],[133,272],[-65,452],[87,288],[200,277],[192,80],[375,-121],[95,-264],[104,-2],[88,-101]],[[56646,69496],[276,-69],[68,-196]],[[56990,69231],[369,10],[268,-156],[275,-175],[129,-92],[214,188],[114,169],[245,49],[198,-75],[75,-293],[65,193],[222,-140],[217,-33],[137,149]],[[59518,69025],[80,194],[-19,34],[74,276],[56,446],[40,149],[8,6]],[[59757,70130],[99,482],[138,416],[5,21]],[[59999,71049],[-26,452],[68,243]],[[60041,71744],[-102,268],[105,222],[-169,-51],[-233,136],[-191,-340],[-421,-66],[-225,317],[-300,20],[-64,-245]],[[58441,72005],[-192,-71],[-268,315]],[[57981,72249],[-303,-10],[-165,587]],[[57513,72826],[-203,328],[135,459],[-176,283],[308,565],[428,23],[117,449],[529,-78],[334,383],[324,167],[459,13]],[[59768,75418],[485,-416],[399,-229]],[[60652,74773],[323,91],[239,-53],[328,309]],[[61542,75120],[42,252],[-70,403],[-160,218],[-154,68],[-102,181]],[[61098,76242],[-354,499],[-317,223],[-240,347],[202,95],[231,494],[-156,234],[410,241],[-8,129],[-249,-95]],[[60617,78409],[-222,-48],[-185,-191],[-260,-31],[-239,-220],[16,-368],[136,-142],[284,35],[-55,-210],[-304,-103],[-377,-342],[-154,121],[61,277],[-304,173],[50,113],[265,197],[-80,135],[-432,149],[-19,221],[-257,-73],[-103,-325],[-215,-437]],[[58223,77340],[6,-152],[-135,-128],[-84,56],[-78,-713]],[[57932,76403],[-144,-245],[-101,-422],[89,-337]],[[57776,75399],[33,-228],[243,-190],[-51,-145],[-330,-33],[-118,-182],[-232,-319]],[[57321,74302],[-87,275],[3,122]],[[57237,74699],[-169,17],[-145,56],[-336,-154],[192,-332],[-141,-96]],[[56638,74190],[-154,0],[-147,304]],[[56337,74494],[-52,-130],[62,-353],[139,-277]],[[56486,73734],[-105,-130],[155,-272]],[[56536,73332],[137,-171],[4,-334],[-257,157],[82,-302],[-176,-62],[105,-521]],[[56431,72099],[-184,-7],[-228,257],[-104,472]],[[55915,72821],[-49,393],[-108,272],[-143,337],[-18,168]],[[55597,73991],[-48,41],[-5,130],[-154,199],[-24,281],[23,403],[38,184]],[[55427,75229],[-46,93],[-59,46]],[[55322,75368],[-78,192],[-120,118]],[[55124,75678],[-261,218],[-161,213],[-254,176]],[[54448,76285],[-233,435],[56,44]],[[54271,76764],[-127,248],[-5,200],[-179,93],[-85,-255],[-82,198],[6,205],[10,9]],[[53809,77462],[62,54]],[[53871,77516],[-221,86],[-226,-210],[15,-293],[-34,-168],[91,-301],[261,-298],[140,-488],[309,-476],[217,3],[68,-130],[-78,-118]],[[54413,75123],[249,-213],[204,-179]],[[54866,74731],[238,-308],[29,-111],[-52,-211],[-154,276],[-242,97],[-116,-382],[200,-219],[-33,-309],[-116,-35],[-148,-506],[-116,-46],[1,181],[57,317],[60,126],[-108,342],[-85,298],[-115,74],[-82,255],[-179,107],[-120,238],[-206,38],[-217,267],[-254,384]],[[53108,75604],[-189,341],[-86,584]],[[52833,76529],[-138,68],[-226,195],[-128,-80],[-161,-274],[-115,-43]],[[52065,76395],[-252,-334],[-548,160],[-404,-192],[-32,-355]],[[50829,75674],[15,-344],[-263,-393],[-356,-125],[-25,-199],[-171,-327],[-107,-481],[108,-338],[-160,-263],[-60,-384],[-210,-118]],[[49600,72702],[-197,-455],[-352,-8]],[[49051,72239],[-265,11],[-174,-209],[-106,-223],[-136,49],[-103,199],[-79,340],[-259,92]],[[47929,72498],[-112,-153],[-146,83],[-143,-65],[42,462],[-26,363],[-124,55],[-67,224],[22,386],[111,215],[20,239],[58,355],[-6,250],[-56,212],[-12,200]],[[47490,75324],[14,420],[-114,257],[393,426]],[[47783,76427],[340,-107],[373,4]],[[48496,76324],[296,-101],[230,31],[449,-19]],[[49471,76235],[144,354],[53,1177],[-287,620],[-205,299]],[[49176,78685],[-424,228],[-28,430]],[[48724,79343],[360,129],[466,-152],[-88,669],[263,-254],[646,461],[84,484],[243,119]],[[50698,80799],[222,117]],[[50920,80916],[143,162]],[[51063,81078],[244,870],[380,247]],[[51687,82195],[231,-17]],[[51918,82178],[54,125],[232,32],[52,-130],[188,291],[-63,222],[-13,335]],[[52368,83053],[-113,328],[-8,604],[46,159]],[[52293,84144],[80,178],[244,36]],[[52617,84358],[98,163],[223,167],[-9,-304],[-82,-192],[33,-166],[151,-89],[-68,-223],[-83,64],[-200,-425],[76,-288]],[[52756,83065],[4,-228],[281,-138],[-3,-210],[283,111],[156,162],[313,-233],[132,-189]],[[53922,82340],[189,174],[434,273],[350,200],[277,-100],[21,-144],[268,-7]],[[55461,82736],[63,260],[383,191]],[[55907,83187],[-59,497]],[[55848,83684],[10,445],[136,371],[262,202],[221,-442],[223,12],[53,453]],[[56753,84725],[32,349],[-102,-75],[-176,210],[-24,340],[351,164],[350,86],[301,-97],[287,17]],[[57772,85719],[316,327],[-291,280]],[[57797,86326],[-504,-47],[-489,-216],[-452,-125]],[[56352,85938],[-161,322],[-269,195],[62,581]],[[55984,87036],[-135,534],[133,344]],[[55982,87914],[252,371],[635,640],[185,124],[-28,250],[-387,279]],[[56639,89578],[-478,-167],[-269,-413],[43,-361],[-441,-475],[-537,-509],[-202,-832],[198,-416],[265,-328],[-255,-666],[-289,-138],[-106,-992],[-157,-554],[-337,57],[-158,-468],[-321,-27],[-89,558],[-232,671],[-211,835]],[[53063,85353],[-187,363],[-548,-684]],[[52328,85032],[-370,-138],[-385,301]],[[51573,85195],[-99,635]],[[51474,85830],[-88,1364],[256,380]],[[51642,87574],[733,496],[549,609],[508,824],[668,1141],[465,444],[763,741],[610,259],[457,-31],[423,489],[506,-26],[499,118],[869,-433],[-358,-158],[305,-371]],[[58639,91676],[286,206],[456,-358],[761,-140],[1050,-668],[213,-281],[18,-393],[-308,-311],[-454,-157],[-1240,449],[-204,-75],[453,-433],[36,-878],[358,-180],[217,-153],[36,286]],[[60317,88590],[-174,263],[183,215]],[[60326,89068],[672,-368]],[[60998,88700],[234,144],[-187,433]],[[61045,89277],[647,578],[256,-34],[260,-206],[161,406],[-231,352],[136,353],[-204,367],[777,-190],[158,-331],[-351,-73]],[[62654,90499],[2,-328],[218,-203]],[[62874,89968],[429,128],[68,377]],[[63371,90473],[581,282],[969,507]],[[64921,91262],[209,-29],[-273,-359],[344,-61],[199,202],[521,16],[412,245],[317,-356],[315,391],[-291,343],[145,195],[820,-179],[385,-185],[1006,-675],[186,309],[-282,313],[-8,125],[-335,58],[92,280],[-149,461],[-8,189],[512,535]],[[69038,93080],[182,537],[207,116]],[[69427,93733],[735,-156],[58,-328]],[[70220,93249],[-263,-479],[173,-189],[89,-413],[-63,-809],[307,-362],[-120,-395],[-544,-839],[318,-87],[110,213],[306,151],[74,293],[240,281],[-162,336],[130,390],[-304,49],[-67,328]],[[70444,91717],[222,594],[-361,481]],[[70305,92792],[497,398],[-64,421],[139,13],[145,-328],[-109,-570],[297,-108],[-127,426],[465,233],[577,31],[513,-337],[-247,492],[-28,630]],[[72363,94093],[484,119],[668,-26]],[[73515,94186],[602,77],[-226,309],[321,388],[319,16],[540,293],[734,79],[93,162],[729,55],[227,-133],[624,314],[510,-10],[77,255],[265,252],[656,242],[476,-191],[-378,-146],[629,-90],[75,-292],[254,143],[812,-7],[626,-289],[223,-221],[-69,-307],[-307,-175],[-730,-328],[-209,-175],[345,-83],[410,-149]],[[63720,73858],[-47,-207],[-102,-138]],[[63571,73513],[7,-293]],[[63578,73220],[88,-436],[263,-123],[193,-296],[395,-102],[434,156],[27,139]],[[64978,72558],[-52,417],[40,618],[-216,200],[71,405],[-184,34],[61,498],[262,-145],[244,189],[-202,355],[-80,338],[-224,-151],[-28,-433],[-87,383]],[[64583,75266],[-15,144],[68,246],[-53,206],[-322,202],[-125,530],[-154,150],[-9,192],[270,-56],[11,432],[236,96],[243,-88],[50,576],[-50,365],[-278,-28],[-236,144],[-321,-260],[-259,-124]],[[63639,77993],[-127,-350],[-269,-97],[-276,-610],[252,-561],[-27,-398],[303,-696]],[[63495,75281],[146,-311],[141,-419],[130,-28],[85,-159],[-228,-47],[-49,-459]],[[23212,96566],[559,-9],[195,-109],[-33,-68],[-126,-17],[-521,38],[-74,165]],[[18463,96506],[224,188],[406,60],[392,-92],[-93,-177],[-518,-170],[-411,191]],[[55757,96473],[191,152],[-167,189],[575,119],[110,-222],[401,-134],[-620,-241],[-490,137]],[[18738,96905],[5,84],[285,177],[149,-27],[361,-120],[-339,-115],[-461,1]],[[22602,97179],[360,-24],[162,-39],[332,-205],[-76,-214],[-411,-122],[-226,138],[-119,221],[-22,245]],[[20696,97433],[546,-81],[751,-215],[212,-281],[108,-247],[-453,66],[-457,192],[-619,21],[268,176],[-335,142],[-21,227]],[[77621,96617],[507,776],[229,66],[208,-38],[704,-336],[-82,-240],[-1566,-228]],[[52901,97640],[757,212],[152,-207],[396,8],[105,202],[408,20],[350,-206],[915,-440],[-699,-233],[-155,-435],[-243,-111],[-132,-490],[-335,-23],[-598,361],[252,210],[-416,170],[-541,499],[-216,463]],[[54824,98034],[858,165],[403,-142],[281,177],[702,-148],[545,-207],[-412,-318],[-806,-70],[-819,98],[-50,163],[-398,11],[-304,271]],[[62457,98194],[542,107],[422,8],[57,-160],[159,142],[262,97],[412,-129],[-107,-90],[-373,-78],[-250,-45],[-39,-97],[-324,-98],[-301,140],[158,185],[-618,18]],[[75327,98047],[722,404],[600,133],[540,-297],[640,-572],[-69,-531],[-606,-73],[-773,170],[-462,226],[-213,423],[-379,117]],[[23136,97939],[193,262],[192,180],[285,42],[-122,135],[646,30],[355,-315],[468,-127],[455,-112],[220,-390],[334,-190],[-381,-176],[-513,-445],[-492,-42],[-575,76],[-299,240],[4,215],[220,157],[-508,-4],[-306,196],[-176,268]],[[24559,98965],[413,112],[324,19],[545,96],[409,220],[344,-30],[300,-166],[211,319],[367,95],[498,65],[849,24],[148,-63],[802,100],[601,-38],[602,-37],[742,-47],[597,-75],[508,-161],[-12,-157],[-678,-257],[-672,-119],[-251,-133],[605,3],[-656,-358],[-452,-167],[-476,-483],[-573,-98],[-177,-120],[-841,-64],[383,-74],[-192,-105],[230,-292],[-264,-202],[-429,-167],[-132,-232],[-388,-176],[39,-134],[475,23],[6,-144],[-742,-355],[-726,163],[-816,-91],[-414,71],[-525,31],[-35,284],[514,133],[-137,427],[170,41],[742,-255],[-379,379],[-450,113],[225,229],[492,141],[79,206],[-392,231],[-118,304],[759,-26],[220,-64],[433,216],[-625,68],[-972,-38],[-491,201],[-232,239],[-324,173],[-61,202]],[[29639,96690],[39,229],[1051,285],[1018,284],[107,214],[-750,213],[243,235],[961,413],[404,63],[-115,265],[658,156],[854,93],[853,5],[303,-184],[737,325],[663,-221],[390,-46],[577,-192],[-660,318],[38,253],[932,353],[975,-27],[354,218],[982,57],[2219,-74],[1737,-469],[-513,-227],[-1062,-26],[-1496,-58],[140,-105],[984,65],[836,-204],[540,181],[231,-212],[-305,-344],[707,220],[1348,229],[833,-114],[156,-253],[-1132,-420],[-157,-136],[-888,-102],[643,-28],[-324,-431],[-224,-383],[9,-658],[333,-386],[-434,-24],[-457,-187],[513,-313],[65,-502],[-297,-55],[360,-508],[-617,-42],[322,-241],[-91,-208],[-391,-91],[-388,-2],[348,-400],[4,-263],[-549,244],[-143,-158],[375,-148],[364,-361],[105,-476],[-495,-114],[-214,228],[-344,340],[95,-401],[-322,-311],[732,-25],[383,-32],[-745,-515],[-755,-466],[-813,-204],[-306,-2],[-288,-228],[-386,-624],[-597,-414],[-192,-24],[-370,-145],[-399,-138],[-238,-365],[-4,-415],[-141,-388],[-453,-472],[112,-462],[-125,-488],[-142,-577],[-391,-36],[-410,482],[-556,3],[-269,324],[-186,577],[-481,735],[-141,385],[-38,530],[-384,546],[100,435],[-186,208],[275,691],[418,220],[110,247],[58,461],[-318,-209],[-151,-88],[-249,-84],[-341,193],[-19,401],[109,314],[258,9],[567,-157],[-478,375],[-249,202],[-276,-83],[-232,147],[310,550],[-169,220],[-220,409],[-335,626],[-353,230],[3,247],[-745,346],[-590,43],[-743,-24],[-677,-44],[-323,188],[-482,372],[729,186],[559,31],[-1188,154],[-627,241]],[[67002,71642],[284,-224],[209,79],[58,268],[219,89],[157,180],[55,472],[234,114],[44,211],[131,-158],[84,-19]],[[68477,72654],[154,-4],[210,-124]],[[68841,72526],[85,-72],[201,189],[93,-114],[90,271],[166,-12],[43,86],[29,239],[120,205],[150,-134],[-30,-181],[84,-28],[-26,-496],[110,-194],[97,125],[123,58],[173,265],[192,-44],[286,-1]],[[70827,72688],[50,-169]],[[70877,72519],[-162,-67],[-141,-109],[-319,-68],[-298,-124],[-163,-258],[66,-250],[32,-294],[-139,-248],[12,-227],[-76,-213],[-265,18],[110,-390],[-177,-150],[-118,-356],[15,-355],[-108,-166],[-103,55],[-212,-77],[-31,-166],[-207,1],[-154,-334],[-10,-503],[-361,-246],[-194,52],[-56,-129],[-166,75],[-278,-88],[-465,301]],[[66909,68203],[252,536],[-23,380],[-210,100],[-22,375],[-91,472],[119,323],[-121,87],[76,430],[113,736]],[[56642,44124],[29,-184],[-32,-286],[49,-277],[-41,-222],[24,-203],[-579,7],[-13,-1880],[188,-483],[181,-369]],[[56448,40227],[-510,-241],[-673,83],[-192,284],[-1126,-26],[-42,-41],[-166,267],[-180,17],[-166,-100],[-134,-113]],[[53422,46976],[115,79],[80,-11],[98,71],[820,-8],[68,-440],[80,-354],[64,-191],[106,-309],[184,47],[91,83],[154,-83],[42,148],[69,344],[172,23],[15,103],[142,2],[-24,-213],[337,5],[5,-372],[56,-228],[-41,-356],[21,-363],[93,-219],[-15,-703],[68,54],[121,-15],[172,89],[127,-35]],[[53309,47603],[112,255],[84,100],[104,-203]],[[53609,47755],[-101,-124],[-45,-152],[-9,-258],[-71,-62]],[[55719,75309],[-35,-201],[39,-254],[115,-144]],[[55838,74710],[-5,-155],[-91,-85],[-16,-192],[-129,-287]],[[55427,75229],[-47,93]],[[55380,75322],[-18,188],[120,291],[18,-111],[75,52]],[[55575,75742],[59,-159],[66,-60],[19,-214]],[[65575,65974],[52,-202]],[[65665,65306],[-142,-3],[-23,-384],[50,-82],[-126,-117],[-1,-241],[-81,-245],[-7,-238]],[[65335,63996],[-56,-125],[-835,298],[-106,599],[-11,136]],[[31400,18145],[-168,16],[-297,1],[0,1319]],[[32587,37434],[511,-964],[227,-89],[339,-437],[286,-231],[40,-261],[-273,-898],[280,-160],[312,-91],[220,95],[252,453],[45,521]],[[34826,35372],[138,114],[139,-341],[-6,-472],[-234,-326],[-186,-241],[-314,-573],[-370,-806]],[[33993,32727],[-70,-473],[-74,-607],[3,-588],[-61,-132],[-21,-382]],[[31227,23224],[273,-433],[266,-119]],[[30952,19680],[-257,93],[-672,79],[-115,344],[6,443],[-185,-38],[-98,214],[-24,626],[213,260],[88,375],[-33,299],[148,504],[101,782],[-30,347],[122,112],[-30,223],[-129,118],[92,248],[-126,224],[-65,682],[112,120],[-47,720],[65,605],[75,527],[166,215],[-84,576],[-1,543],[210,386],[-7,494],[159,576],[1,544],[-72,108],[-128,1020],[171,607],[-27,572],[100,537],[182,555],[196,367],[-83,232],[58,190],[-9,985],[302,291],[96,614],[-34,148]],[[31359,37147],[231,534],[364,-144],[163,-427],[109,475],[316,-24],[45,-127]],[[62492,74950],[57,-155],[106,-103],[-56,-148],[148,-202],[-78,-189],[118,-160],[124,-97],[7,-410]],[[62918,73486],[-101,-17]],[[62817,73469],[-113,342],[1,91],[-123,-2],[-82,159],[-58,-16]],[[62442,74043],[-109,172],[-207,147],[27,288],[-47,208]],[[62106,74858],[386,92]],[[1088,892],[38,-7],[32,-4]],[[1158,881],[402,-246],[352,246],[63,34],[816,104],[265,-138],[130,-71],[419,-196],[789,-151],[625,-185],[1072,-139],[800,162],[1181,-116],[669,-185],[734,174],[773,162],[60,278],[-1094,23],[-898,139],[-234,231],[-745,128],[49,266],[103,243],[104,220],[-55,243],[-462,162],[-212,209],[-430,185],[675,-35],[642,93],[402,-197],[495,173],[457,220],[223,197],[-98,243],[-359,162],[-408,174],[-571,35],[-500,81],[-539,58],[-180,220],[-359,185],[-217,208],[-87,672],[136,-58],[250,-185],[457,58],[441,81],[228,-255],[441,58],[370,127],[348,162],[315,197],[419,58],[-11,220],[-97,220],[81,208],[359,104],[163,-196],[425,115],[321,151],[397,12],[375,57],[376,139],[299,128],[337,127],[218,-35],[190,-46],[414,81],[370,-104],[381,11],[364,81],[375,-57],[414,-58],[386,23],[403,-12],[413,-11],[381,23],[283,174],[337,92],[349,-127],[331,104],[300,208],[179,-185],[98,-208],[180,-197],[288,174],[332,-220],[375,-70],[321,-162],[392,35],[354,104],[418,-23],[376,-81],[381,-104],[147,254],[-180,197],[-136,209],[-359,46],[-158,220],[-60,220],[-98,440],[213,-81],[364,-35],[359,35],[327,-93],[283,-174],[119,-208],[376,-35],[359,81],[381,116],[342,70],[283,-139],[370,46],[239,451],[224,-266],[321,-104],[348,58],[228,-232],[365,-23],[337,-69],[332,-128],[218,220],[108,209],[278,-232],[381,58],[283,-127],[190,-197],[370,58],[288,127],[283,151],[337,81],[392,69],[354,81],[272,127],[163,186],[65,254],[-32,244],[-87,231],[-98,232],[-87,231],[-71,209],[-16,231],[27,232],[130,220],[109,243],[44,231],[-55,255],[-32,232],[136,266],[152,173],[180,220],[190,186],[223,173],[109,255],[152,162],[174,151],[267,34],[174,186],[196,115],[228,70],[202,150],[157,186],[218,69],[163,-151],[-103,-196],[-283,-174],[-120,-127],[-206,92],[-229,-58],[-190,-139],[-202,-150],[-136,-174],[-38,-231],[17,-220],[130,-197],[-190,-139],[-261,-46],[-153,-197],[-163,-185],[-174,-255],[-44,-220],[98,-243],[147,-185],[229,-139],[212,-185],[114,-232],[60,-220],[82,-232],[130,-196],[82,-220],[38,-544],[81,-220],[22,-232],[87,-231],[-38,-313],[-152,-243],[-163,-197],[-370,-81],[-125,-208],[-169,-197],[-419,-220],[-370,-93],[-348,-127],[-376,-128],[-223,-243],[-446,-23],[-489,23],[-441,-46],[-468,0],[87,-232],[424,-104],[311,-162],[174,-208],[-310,-185],[-479,58],[-397,-151],[-17,-243],[-11,-232],[327,-196],[60,-220],[353,-220],[588,-93],[500,-162],[398,-185],[506,-186],[690,-92],[681,-162],[473,-174],[517,-197],[272,-278],[136,-220],[337,209],[457,173],[484,186],[577,150],[495,162],[691,12],[680,-81],[560,-139],[180,255],[386,173],[702,12],[550,127],[522,128],[577,81],[614,104],[430,150],[-196,209],[-119,208],[0,220],[-539,-23],[-571,-93],[-544,0],[-77,220],[39,440],[125,128],[397,138],[468,139],[337,174],[337,174],[251,231],[380,104],[376,81],[190,47],[430,23],[408,81],[343,116],[337,139],[305,139],[386,185],[245,197],[261,173],[82,232],[-294,139],[98,243],[185,185],[288,116],[305,139],[283,185],[217,232],[136,277],[202,163],[331,-35],[136,-197],[332,-23],[11,220],[142,231],[299,-58],[71,-220],[331,-34],[360,104],[348,69],[315,-34],[120,-243],[305,196],[283,105],[315,81],[310,81],[283,139],[310,92],[240,128],[168,208],[207,-151],[288,81],[202,-277],[157,-209],[316,116],[125,232],[283,162],[365,-35],[108,-220],[229,220],[299,69],[326,23],[294,-11],[310,-70],[300,-34],[130,-197],[180,-174],[304,104],[327,24],[315,0],[310,11],[278,81],[294,70],[245,162],[261,104],[283,58],[212,162],[152,324],[158,197],[288,-93],[109,-208],[239,-139],[289,46],[196,-208],[206,-151],[283,139],[98,255],[250,104],[289,197],[272,81],[326,116],[218,127],[228,139],[218,127],[261,-69],[250,208],[180,162],[261,-11],[229,139],[54,208],[234,162],[228,116],[278,93],[256,46],[244,-35],[262,-58],[223,-162],[27,-254],[245,-197],[168,-162],[332,-70],[185,-162],[229,-162],[266,-35],[223,116],[240,243],[261,-127],[272,-70],[261,-69],[272,-46],[277,0],[229,-614],[-11,-150],[-33,-267],[-266,-150],[-218,-220],[38,-232],[310,12],[-38,-232],[-141,-220],[-131,-243],[212,-185],[321,-58],[321,104],[153,232],[92,220],[153,185],[174,174],[70,208],[147,289],[174,58],[316,24],[277,69],[283,93],[136,231],[82,220],[190,220],[272,151],[234,115],[153,197],[157,104],[202,93],[277,-58],[250,58],[272,69],[305,-34],[201,162],[142,393],[103,-162],[131,-278],[234,-115],[266,-47],[267,70],[283,-46],[261,-12],[174,58],[234,-35],[212,-127],[250,81],[300,0],[255,81],[289,-81],[185,197],[141,196],[191,163],[348,439],[179,-81],[212,-162],[185,-208],[354,-359],[272,-12],[256,0],[299,70],[299,81],[229,162],[190,174],[310,23],[207,127],[218,-116],[141,-185],[196,-185],[305,23],[190,-150],[332,-151],[348,-58],[288,47],[218,185],[185,185],[250,46],[251,-81],[288,-58],[261,93],[250,0],[245,-58],[256,-58],[250,104],[299,93],[283,23],[316,0],[255,58],[251,46],[76,290],[11,243],[174,-162],[49,-266],[92,-244],[115,-196],[234,-105],[315,35],[365,12],[250,35],[364,0],[262,11],[364,-23],[310,-46],[196,-186],[-54,-220],[179,-173],[299,-139],[310,-151],[360,-104],[375,-92],[283,-93],[315,-12],[180,197],[245,-162],[212,-185],[245,-139],[337,-58],[321,-69],[136,-232],[316,-139],[212,-208],[310,-93],[321,12],[299,-35],[332,12],[332,-47],[310,-81],[288,-139],[289,-116],[195,-173],[-32,-232],[-147,-208],[-125,-266],[-98,-209],[-131,-243],[-364,-93],[-163,-208],[-360,-127],[-125,-232],[-190,-220],[-201,-185],[-115,-243],[-70,-220],[-28,-266],[6,-220],[158,-232],[60,-220],[130,-208],[517,-81],[109,-255],[-501,-93],[-424,-127],[-528,-23],[-234,-336],[-49,-278],[-119,-220],[-147,-220],[370,-196],[141,-244],[239,-219],[338,-197],[386,-186],[419,-185],[636,-185],[142,-289],[800,-128],[53,-45],[208,-175],[767,151],[636,-186],[-99504,-147],[245,344],[501,-185],[32,21],[294,188]],[[54716,79012],[-21,-241],[-156,-2],[53,-128],[-92,-380]],[[54500,78261],[-53,-100],[-243,-14],[-140,-134],[-229,45]],[[53835,78058],[-398,153],[-62,205],[-274,-102],[-32,-113],[-169,84]],[[52900,78285],[-142,16],[-125,108],[42,145],[-10,104]],[[52665,78658],[83,33],[141,-164],[39,156],[245,-25],[199,106],[133,-18],[87,-121],[26,100],[-40,385],[100,75],[98,272]],[[53776,79457],[206,-190],[157,242],[98,44],[215,-180],[131,30],[128,-111]],[[54711,79292],[-23,-75],[28,-205]],[[62817,73469],[-190,78],[-141,273],[-44,223]],[[63720,73858],[-48,-207],[-101,-138]],[[63578,73220],[-69,-29],[-173,309],[95,292],[-82,174],[-104,-44],[-327,-436]],[[62492,74950],[68,96],[207,-169],[149,-36],[38,70],[-136,319],[72,82]],[[62890,75312],[78,-20],[191,-359],[122,-40],[48,150],[166,238]],[[58149,47921],[-17,713],[-70,268]],[[58062,48902],[169,-46],[85,336],[147,-38]],[[58463,49154],[16,-233],[60,-134],[3,-192],[-69,-124],[-108,-308],[-101,-214],[-115,-28]],[[50920,80916],[204,-47],[257,123],[176,-258],[153,-138]],[[51710,80596],[-32,-400]],[[51678,80196],[-72,-22],[-30,-331]],[[51576,79843],[-243,269],[-143,-46],[-194,279],[-129,237],[-129,10],[-40,207]],[[50518,54209],[-69,407],[13,1357],[-56,122],[-11,290],[-96,207],[-85,174],[35,311]],[[50249,57077],[96,67],[56,258],[136,56],[61,176]],[[50598,57634],[93,173],[100,2],[212,-340]],[[51003,57469],[-11,-197],[62,-350],[-54,-238],[29,-159],[-135,-366],[-86,-181],[-52,-372],[7,-376],[-16,-952]],[[49214,56277],[-190,152],[-130,-22],[-97,-149],[-125,125],[-49,195],[-125,129]],[[48498,56707],[-18,343],[76,250],[-7,200],[221,490],[41,405],[76,144],[134,-79],[116,120],[38,152],[216,265],[53,184],[259,246],[153,84],[70,-114],[178,3]],[[50104,59400],[-22,-286],[37,-269],[156,-386],[9,-286],[320,-134],[-6,-405]],[[50249,57077],[-243,13]],[[50006,57090],[-128,47],[-90,-96],[-123,43],[-482,-27],[-7,-336],[38,-444]],[[75742,63602],[-6,-424],[-97,90],[18,-476]],[[75657,62792],[-79,308],[-16,301],[-53,285]],[[74730,63611],[-43,486],[-96,444],[47,356],[-171,159],[62,215],[173,220],[-200,313],[98,401],[220,-255],[133,-30],[24,-410],[265,-81],[257,8],[160,-101],[-128,-500],[-124,-34],[-86,-336],[152,-306],[46,377],[76,2],[147,-937]],[[56293,76715],[80,-243],[108,43],[213,-92],[408,-31],[138,150],[327,138],[202,-215],[163,-62]],[[57776,75399],[-239,79],[-283,-186]],[[57254,75292],[-3,-294],[-252,-56],[-196,206],[-222,-162],[-206,17]],[[56375,75003],[-20,391],[-139,189]],[[56216,75583],[46,84],[-30,70],[47,188],[105,185],[-135,255],[-24,216],[68,134]],[[55279,77084],[100,2],[-69,-260],[134,-227],[-41,-278],[-65,-27]],[[55338,76294],[-52,-53],[-90,-138],[-41,-325]],[[55155,75778],[-246,224],[-105,247],[-106,130],[-127,221],[-61,183],[-136,277],[59,245],[99,-136],[60,123],[130,13],[239,-98],[192,8],[126,-131]],[[56523,82432],[268,-4],[302,223],[64,333],[228,190],[-26,264]],[[57359,83438],[169,100],[298,228]],[[57826,83766],[293,-149],[39,-146],[146,70],[272,-141],[27,-277],[-60,-159],[174,-387],[113,-108],[-16,-107],[187,-104],[80,-157],[-108,-129],[-224,20],[-54,-55],[66,-196],[68,-379]],[[58829,81362],[-239,-35],[-85,-129],[-18,-298],[-111,57],[-250,-28],[-73,138],[-104,-103],[-105,86],[-218,12],[-310,141],[-281,47],[-215,-14],[-152,-160],[-133,-23]],[[56535,81053],[-6,263],[-85,274],[166,121],[2,235],[-77,225],[-12,261]],[[25238,61101],[-2,87],[33,27],[51,-70],[99,357],[53,8]],[[25297,59966],[-83,0],[22,667],[2,468]],[[31359,37147],[-200,-81],[-109,814],[-150,663],[88,572],[-146,250],[-37,426],[-136,402]],[[30669,40193],[175,638],[-119,496],[63,199],[-49,219],[108,295],[6,503],[13,415],[60,200],[-240,951]],[[30686,44109],[206,-50],[143,13],[62,179],[243,239],[147,222],[363,100],[-29,-443],[34,-227],[-23,-396],[302,-529],[311,-98],[109,-220],[188,-117],[115,-172],[175,6],[161,-175],[12,-342],[55,-172],[3,-255],[-81,-10],[107,-688],[533,-24],[-41,-342],[30,-233],[151,-166],[66,-367],[-49,-465],[-77,-259],[27,-337],[-87,-122]],[[33842,38659],[-4,182],[-259,302],[-258,9],[-484,-172],[-133,-520],[-7,-318],[-110,-708]],[[34826,35372],[54,341],[38,350],[0,325],[-100,107],[-104,-96],[-103,26],[-33,228],[-26,541],[-52,177],[-187,160],[-114,-116],[-293,113],[18,802],[-82,329]],[[30686,44109],[-157,-102],[-126,68],[18,898],[-228,-348],[-245,15],[-105,315],[-184,34],[59,254],[-155,359],[-115,532],[73,108],[0,250],[168,171],[-28,319],[71,206],[20,275],[318,402],[227,114],[37,89],[251,-28]],[[30585,48040],[125,1620],[6,256],[-43,339],[-123,215],[1,430],[156,97],[56,-61],[9,226],[-162,61],[-4,370],[541,-13],[92,203],[77,-187],[55,-349],[52,73]],[[31423,51320],[153,-312],[216,38],[54,181],[206,138],[115,97],[32,250],[198,168],[-15,124],[-235,51],[-39,372],[12,396],[-125,153],[52,55],[206,-76],[221,-148],[80,140],[200,92],[310,221],[102,225],[-37,167]],[[33129,53652],[145,26],[64,-136],[-36,-259],[96,-90],[63,-274],[-77,-209],[-44,-502],[71,-299],[20,-274],[171,-277],[137,-29],[30,116],[88,25],[126,104],[90,157],[154,-50],[67,21]],[[34294,51702],[151,-48],[25,120],[-46,118],[28,171],[112,-53],[131,61],[159,-125]],[[34854,51946],[121,-122],[86,160],[62,-25],[38,-166],[133,42],[107,224],[85,436],[164,540]],[[35174,30629],[-77,334],[122,280],[-160,402],[-218,327],[-286,379],[-103,-18],[-279,457],[-180,-63]],[[82069,53798],[-13,-291],[-16,-377],[-133,19],[-58,-202],[-126,307]],[[75471,66988],[113,-189],[-20,-363],[-227,-17],[-234,39],[-175,-92],[-252,224],[-6,119]],[[74670,66709],[184,439],[150,150],[198,-137],[147,-14],[122,-159]],[[58175,37528],[-393,-435],[-249,-442],[-93,-393],[-83,-222],[-152,-47],[-48,-283],[-28,-184],[-178,-138],[-226,29],[-133,166],[-117,71],[-135,-137],[-68,-283],[-132,-177],[-139,-264],[-199,-60],[-62,207],[26,360],[-165,562],[-75,88]],[[55526,35946],[0,1725],[274,20],[8,2105],[207,19],[428,207],[106,-243],[177,231],[85,2],[156,133]],[[56967,40145],[50,-44]],[[57017,40101],[107,-473],[56,-105],[87,-342],[315,-649],[119,-64],[0,-208],[82,-375],[215,-90],[177,-267]],[[54244,54965],[229,44],[52,152],[46,-11],[69,-134],[350,226],[118,230],[145,207],[-28,208],[78,54],[269,-36],[261,273],[201,645],[141,239],[176,101]],[[56351,57163],[31,-253],[160,-369],[1,-241],[-45,-246],[18,-184],[96,-170]],[[56612,55700],[212,-258]],[[56824,55442],[152,-239],[2,-192],[187,-308],[116,-255],[70,-355],[208,-234],[44,-187]],[[57603,53672],[-91,-63],[-178,14],[-209,62],[-104,-51],[-41,-143],[-90,-18],[-110,125],[-309,-295],[-127,60],[-38,-46],[-83,-357],[-207,115],[-203,59],[-177,218],[-229,200],[-149,-190],[-108,-300],[-25,-412]],[[55125,52650],[-178,33],[-188,99],[-166,-313],[-146,-550]],[[54447,51919],[-29,172],[-12,269],[-127,190],[-103,305],[-23,212],[-132,309],[23,176],[-28,249],[21,458],[67,107],[140,599]],[[26228,91219],[16,649],[394,-46]],[[25824,89109],[-81,-259],[-322,-399]],[[23714,86094],[-16,-686],[409,-99]],[[25743,83665],[348,-163],[294,-248]],[[28738,83981],[-23,395],[-188,502]],[[31513,79609],[415,58],[246,-289]],[[31350,77248],[-181,334],[0,805],[-123,171],[-187,-100],[-92,155],[-212,-446],[-84,-460],[-99,-269],[-118,-91],[-89,-30],[-28,-146],[-512,0],[-422,-4],[-125,-109],[-294,-425],[-34,-46],[-89,-231],[-255,1],[-273,-3],[-125,-93],[44,-116],[25,-181],[-5,-60],[-363,-293],[-286,-93],[-323,-316],[-70,0],[-94,93],[-31,85],[6,61],[61,207],[131,325],[81,349],[-56,514],[-59,536],[-290,277],[35,105],[-41,73],[-76,0],[-56,93],[-14,140],[-54,-61],[-75,18],[17,59],[-65,58],[-27,155],[-216,189],[-224,197],[-272,229],[-261,214],[-248,-167],[-91,-6],[-342,154],[-225,-77],[-269,183],[-284,94],[-194,36],[-86,100],[-49,325],[-94,-3],[-1,-227],[-575,0],[-951,0],[-944,0],[-833,0],[-834,0],[-819,0],[-847,0],[-273,0],[-825,0],[-788,0]],[[15104,80367],[-503,244],[-155,523],[40,363]],[[13740,82958],[154,285],[-7,373],[-473,376],[-284,674],[-173,424],[-255,266],[-187,242],[-147,306],[-279,-192],[-270,-330],[-247,388],[-194,259],[-271,164],[-273,17],[1,3364],[2,2193]],[[11355,91625],[438,-285],[289,-54]],[[15437,92031],[38,-449],[341,97]],[[17987,91291],[374,-300],[-390,-293]],[[19722,91216],[-704,-88],[-494,-56]],[[15020,93041],[119,250],[192,432]],[[16539,93012],[0,-257],[-731,-285]],[[52900,78285],[-22,-242],[-122,-100],[-206,75],[-60,-239],[-132,-19],[-48,94],[-156,-200],[-134,-28],[-120,126]],[[51900,77752],[-95,259],[-133,-92],[5,267],[203,332],[-9,150],[126,-54],[77,101]],[[52074,78715],[236,-4],[57,128],[298,-181]],[[31400,18145],[-92,-239],[-238,-183]],[[31070,17723],[-137,19],[-164,48]],[[30769,17790],[-202,177],[-291,86],[-350,330],[-283,317],[-383,662],[229,-124],[390,-395],[369,-212],[143,271],[90,405],[256,244],[198,-70]],[[29661,27385],[-80,576],[-22,666]],[[30452,39739],[143,151],[74,303]],[[86288,75628],[-179,348],[-111,-331],[-429,-254],[44,-312],[-241,22],[-131,185],[-191,-419],[-306,-318],[-227,-379]],[[83030,72705],[220,-173],[311,422]],[[83987,72709],[45,-310],[-393,-165]],[[83097,71205],[299,-325],[109,-581]],[[80517,63220],[-373,189],[-131,-96]],[[80013,63313],[-280,154],[-132,240],[44,340],[-254,108],[-134,222],[-236,-315],[-271,-68],[-221,3],[-149,-145]],[[78380,63852],[-144,-86],[42,-676],[-148,16],[-25,139]],[[78105,63245],[-9,244],[-203,-172],[-121,109],[-206,222],[81,490],[-176,115],[-66,544],[-293,-98],[33,701],[263,493],[11,487],[-8,452],[-121,141],[-93,348],[-162,-44]],[[77035,67277],[-300,89],[94,248],[-130,367],[-198,-249],[-233,145],[-321,-376],[-252,-439],[-224,-74]],[[74670,66709],[-23,465],[-170,-124]],[[74477,67050],[-324,57],[-314,136],[-225,259],[-216,117],[-93,284],[-157,84],[-280,385],[-223,182],[-115,-141]],[[72530,68413],[-386,413],[-273,374],[-78,651],[200,-79],[9,301],[-111,303],[28,482],[-298,692]],[[71621,71550],[-457,239],[-82,454],[-205,276]],[[70827,72688],[-42,337],[10,230],[-169,134],[-91,-59],[-70,546]],[[70465,73876],[79,136],[-39,138],[266,279],[192,116],[294,-80],[105,378],[356,70],[99,234],[438,320],[39,134]],[[72294,75601],[-22,337],[190,154],[-250,1026],[550,236],[143,131],[200,1058],[551,-194],[155,267],[13,592],[230,56],[212,393]],[[74266,79657],[109,49]],[[74375,79706],[73,-413],[233,-313],[396,-222],[192,-476],[-107,-690],[100,-256],[330,-101],[374,-83],[336,-368],[171,-66],[127,-544],[163,-351],[306,14],[574,-133],[369,82],[274,-88],[411,-359],[336,1],[123,-184],[324,318],[448,205],[417,22],[324,208],[200,316],[194,199],[-45,195],[-89,227],[146,381],[156,-53],[286,-120],[277,313],[423,229],[204,391],[195,168],[404,78],[219,-66],[30,210],[-251,413],[-223,189],[-214,-219],[-274,92],[-157,-74],[-72,241],[197,590],[135,446]],[[82410,80055],[333,-223],[392,373],[-3,260],[251,627],[155,189],[-4,326],[-152,141],[229,294],[345,106],[369,16],[415,-176],[244,-217],[172,-596],[104,-254],[97,-363],[103,-579],[483,-189],[329,-420],[112,-555],[423,-1],[240,233],[459,175],[-146,-532],[-107,-216],[-96,-647],[-186,-575],[-338,104],[-238,-208],[73,-506],[-40,-698],[-142,-16],[2,-300]],[[47857,53158],[22,487],[26,74],[-8,233],[-118,247],[-88,40],[-81,162],[60,262],[-28,286],[13,172]],[[47655,55121],[44,0],[17,258],[-22,114],[27,82],[103,71],[-69,473],[-64,245],[23,200],[55,46]],[[47769,56610],[36,54],[77,-89],[215,-5],[51,172],[48,-11],[80,67],[43,-253],[65,74],[114,88]],[[49214,56277],[74,-841],[-117,-496],[-73,-667],[121,-509],[-13,-233]],[[53632,51919],[-35,32],[-164,-76],[-169,79],[-132,-38]],[[53132,51916],[-452,13]],[[52680,51929],[40,466],[-108,391]],[[52429,53151],[-72,85],[4,163]],[[52361,53399],[71,418],[132,570],[81,6],[165,345],[105,10],[156,-243],[191,199],[26,246],[63,238],[43,299],[148,243],[56,414],[59,132],[39,307],[74,377],[234,457],[14,196],[31,107],[-110,235]],[[53939,57955],[9,188],[78,34]],[[54026,58177],[111,-378],[18,-392],[-10,-393],[151,-537],[-155,6],[-78,-42],[-127,60],[-60,-279],[164,-345],[121,-100],[39,-245],[87,-407],[-43,-160]],[[54447,51919],[-20,-319],[-220,140],[-225,156],[-350,23]],[[58564,52653],[-16,-691],[111,-80],[-89,-210],[-107,-157],[-106,-308],[-59,-274],[-15,-475],[-65,-225],[-2,-446]],[[58216,49787],[-80,-165],[-10,-351],[-38,-46],[-26,-323]],[[58149,47921],[50,-544],[-27,-307]],[[58172,47070],[55,-343],[161,-330]],[[58388,46397],[150,-745]],[[58538,45652],[-109,60],[-373,-99],[-75,-71],[-79,-377],[62,-261],[-49,-699],[-34,-593],[75,-105],[194,-230],[76,107],[23,-637],[-212,5],[-114,325],[-103,252],[-213,82],[-62,310],[-170,-187],[-222,83],[-93,268],[-176,55],[-131,-15],[-15,184],[-96,15]],[[53609,47755],[73,-60],[95,226],[152,-6],[17,-167],[104,-105],[164,370],[161,289],[71,189],[-10,486],[121,574],[127,304],[183,285],[32,189],[7,216],[45,205],[-14,335],[34,524],[55,368],[83,316],[16,357]],[[57603,53672],[169,-488],[124,-71],[75,99],[128,-39],[155,125],[66,-252],[244,-393]],[[53081,48229],[212,326],[-105,391],[95,148],[187,73],[23,261],[148,-283],[245,-25],[85,279],[36,393],[-31,461],[-131,350],[120,684],[-69,117],[-207,-48],[-78,305],[21,258]],[[29063,50490],[-119,140],[-137,195],[-79,-94],[-235,82],[-68,255],[-52,-10],[-278,338]],[[28366,54848],[36,287],[89,-43],[52,176],[-64,348],[34,86]],[[30185,57537],[-178,-99],[-71,-295],[-107,-169],[-81,-220],[-34,-422],[-77,-345],[144,-40],[35,-271],[62,-130],[21,-238],[-33,-219],[10,-123],[69,-49],[66,-207],[357,57],[161,-75],[196,-508],[112,63],[200,-32],[158,68],[99,-102],[-50,-318],[-62,-199],[-22,-423],[56,-393],[79,-175],[9,-133],[-140,-294],[100,-130],[74,-207],[85,-589]],[[30585,48040],[-139,314],[-83,14],[179,602],[-213,276],[-166,-51],[-101,103],[-153,-157],[-207,74],[-163,620],[-129,152],[-89,279],[-184,280],[-74,-56]],[[26191,57131],[42,76],[183,-156],[63,77],[89,-50],[46,-121],[82,-40],[66,126]],[[27070,56232],[-107,-53],[1,-238],[58,-88],[-41,-70],[10,-107],[-23,-120],[-14,-117]],[[59437,71293],[-30,21],[-53,-45],[-42,12],[-14,-22],[-5,59],[-20,37],[-54,6],[-75,-51],[-52,31]],[[53776,79457],[-157,254],[-141,142],[-30,249],[-49,176],[202,129],[103,147],[200,114],[70,113],[73,-68],[124,62]],[[54171,80775],[132,-191],[207,-51],[-17,-163],[151,-122],[41,153],[191,-66],[26,-185],[207,-36],[127,-291]],[[55236,79823],[-82,-1],[-43,-106],[-64,-26],[-18,-134],[-54,-28],[-7,-55],[-95,-61],[-123,10],[-39,-130]],[[53922,82340],[64,-300],[-77,-158],[101,-210],[69,-316],[-22,-204],[114,-377]],[[52074,78715],[35,421],[140,404],[-400,109],[-131,155]],[[51718,79804],[16,259],[-56,133]],[[51710,80596],[-47,619],[167,0],[70,222],[69,541],[-51,200]],[[52368,83053],[210,-78],[178,90]],[[61984,57352],[-102,-317]],[[61882,57035],[-62,106],[-67,-42],[-155,10],[-4,180],[-22,163],[94,277],[98,261]],[[61764,57990],[119,-51],[83,144]],[[52293,84144],[80,177],[244,37]],[[30081,61241],[5,161],[-71,177],[68,99],[21,228],[-24,321]],[[53333,64447],[-952,-1126],[-804,-1161],[-392,-263]],[[51185,61897],[-308,-58],[-3,376],[-129,96],[-173,169],[-66,277],[-937,1289],[-937,1289]],[[48632,65335],[-1045,1431]],[[47587,66766],[6,114],[-1,40]],[[47592,66920],[-2,700],[449,436],[277,90],[227,159],[107,295],[324,234],[12,438],[161,51],[126,219],[363,99],[51,230],[-73,125],[-96,624],[-17,359],[-104,379]],[[52339,72408],[-57,-303],[44,-563],[-65,-487],[-171,-330],[24,-445],[227,-352],[3,-143],[171,-238],[118,-1061]],[[52633,68486],[90,-522],[15,-274],[-49,-482],[21,-270],[-36,-323],[24,-371],[-110,-247],[164,-431],[11,-253],[99,-330],[130,109],[219,-275],[122,-370]],[[29063,50490],[38,-449],[-86,-384],[-303,-619],[-334,-233],[-170,-514],[-53,-398],[-157,-243],[-116,298],[-113,64],[-114,-47],[-8,216],[79,141],[-33,246]],[[60240,63578],[-1102,0],[-1077,0],[-1117,0]],[[56944,63578],[0,2175],[0,2101],[-83,476],[71,365],[-43,253],[101,283]],[[59518,69025],[182,-1015]],[[61764,57990],[-95,191],[-114,346],[-124,190],[-71,204],[-242,237],[-191,7],[-67,124],[-163,-139],[-168,268],[-87,-441],[-323,124]],[[60119,59101],[-30,236],[120,868],[27,393],[88,181],[204,97],[141,337]],[[60669,61213],[161,-684],[77,-542]],[[47783,76427],[340,-106],[373,3]],[[49471,76235],[111,-230],[511,-268],[101,127],[313,-267],[322,77]],[[49600,72702],[-197,-454],[-352,-9]],[[47929,72498],[-23,195],[103,222],[38,161],[-96,175],[77,388],[-111,355],[120,48],[11,280],[45,86],[3,461],[129,160],[-78,296],[-162,21],[-47,-75],[-164,0],[-70,289],[-113,-86],[-101,-150]],[[57772,85719],[42,-103],[-198,-341],[83,-551],[-120,-187]],[[57579,84537],[-229,1],[-239,219],[-121,73],[-237,-105]],[[61882,57035],[-61,-209],[103,-325],[102,-285],[106,-210],[909,-702],[233,4]],[[63274,55308],[-785,-1773],[-362,-26],[-247,-417],[-178,-11],[-76,-186]],[[61626,52895],[-190,0],[-112,200],[-254,-247],[-82,-247],[-185,47],[-62,68],[-65,-16],[-87,6],[-352,502],[-193,0],[-95,194],[0,332],[-145,99]],[[59804,53833],[-164,643],[-127,137],[-48,236],[-141,288],[-171,42],[95,337],[147,14],[42,181]],[[59437,55711],[-4,531]],[[59433,56242],[82,618],[132,166],[28,241],[119,451],[168,293],[112,582],[45,508]],[[57942,91385],[-41,-414],[425,-394],[-256,-445],[323,-673],[-187,-506],[250,-440],[-113,-385],[411,-405],[-105,-301],[-258,-341],[-594,-755]],[[56352,85938],[-161,323],[-269,193],[62,582]],[[55984,87036],[-135,533],[133,345]],[[56639,89578],[-93,230],[-8,910],[-433,402],[-371,289]],[[55734,91409],[167,156],[309,-312],[362,29],[298,-143],[265,262],[137,433],[431,200],[356,-235],[-117,-414]],[[34854,51946],[70,252],[24,269],[48,253],[-107,349]],[[34889,53069],[-22,404],[144,508]],[[51576,79843],[62,-52],[80,13]],[[51900,77752],[-11,-167],[82,-222],[-97,-180],[72,-457],[151,-75],[-32,-256]],[[49176,78685],[-424,227],[-28,431]],[[52636,51176],[94,35],[404,-6],[-2,711]],[[48278,82406],[-210,122],[-172,-9],[57,317],[-57,317]],[[49420,83612],[22,-62],[248,-697]],[[49690,82853],[190,-95],[171,-673],[79,-233],[337,-113],[-34,-378],[-142,-173],[111,-305],[-250,-310],[-371,6],[-473,-163],[-130,116],[-183,-276],[-257,67],[-195,-226],[-148,118],[407,621],[249,127]],[[49051,80963],[-2,1],[-434,98]],[[48615,81062],[-79,235],[291,183],[-152,319],[52,387]],[[48727,82186],[413,-54],[1,0]],[[49141,82132],[40,343]],[[49181,82475],[-186,364],[-4,8]],[[48991,82847],[-337,104],[-66,160],[101,264],[-92,163],[-149,-279],[-17,569],[-140,301],[101,611],[216,480],[222,-47],[335,49],[-297,-639],[283,81],[304,-3],[-72,-481],[-250,-530],[287,-38]],[[61098,76242],[34,70],[235,-101],[409,-96],[378,-283],[48,-110],[169,93],[259,-124],[85,-242],[175,-137]],[[62106,74858],[-268,290],[-296,-28]],[[50006,57090],[-20,-184],[116,-305],[-1,-429],[27,-466],[69,-215],[-61,-532],[22,-294],[74,-375],[62,-207]],[[47655,55121],[-78,15],[-57,-238],[-78,3],[-55,126],[19,237],[-116,362],[-73,-67],[-59,-13]],[[47158,55546],[-77,-34],[3,217],[-44,155],[9,171],[-60,249],[-78,211],[-222,1],[-65,-112],[-76,-13],[-48,-128],[-32,-163],[-148,-260]],[[45797,57103],[123,288],[84,-11],[73,99],[61,1],[44,78],[-24,196],[31,62],[5,200]],[[46194,58016],[134,-6],[200,-144],[61,13],[21,66],[151,-47],[40,33]],[[46801,57931],[16,-216],[44,1],[73,78],[46,-19],[77,-150],[119,-48],[76,128],[90,79],[67,83],[55,-15],[62,-130],[33,-163],[114,-248],[-57,-152],[-11,-192],[59,58],[35,-69],[-15,-176],[85,-170]],[[45357,58612],[302,17],[63,140],[88,9],[110,-145],[86,-3],[92,99],[56,-170],[-120,-133],[-121,11],[-119,124],[-103,-136],[-50,-5],[-67,-83],[-253,13]],[[45367,57897],[147,96],[92,-19],[75,67],[513,-25]],[[56638,74190],[-154,-1],[-147,305]],[[56486,73734],[-105,-129],[155,-273]],[[56431,72099],[-184,-8],[-228,257],[-104,473]],[[55838,74710],[182,53],[106,129],[150,-12],[46,103],[53,20]],[[57254,75292],[135,-157],[-86,-369],[-66,-67]],[[24381,59170],[7,172],[32,138],[-39,111],[133,481],[357,2],[7,201],[-45,36],[-31,128],[-103,136],[-103,198],[125,1],[1,333],[259,1],[257,-7]],[[25493,59872],[-127,-225],[-131,-166],[-20,-113],[22,-116],[-58,-150]],[[25179,59102],[-65,-37],[15,-69],[-52,-66],[-95,-149],[-9,-86]],[[34125,54109],[-44,-532],[-169,-154],[15,-139],[-51,-305],[123,-429],[89,-1],[37,-333],[169,-514]],[[33129,53652],[-188,448],[75,163],[-5,273],[171,95],[69,110],[-95,220],[24,215],[220,347]],[[25697,58436],[-84,51]],[[25613,58487],[19,237],[-38,64],[-57,42],[-122,-70],[-10,79],[-84,95],[-60,118],[-82,50]],[[25860,59889],[128,15],[90,66]],[[26903,59440],[-95,12],[-38,-81],[-97,-77],[-70,0],[-61,-76],[-56,27],[-47,90],[-29,-17],[-36,-141],[-27,5],[-4,-121],[-97,-163],[-51,-70],[-29,-74],[-82,120],[-60,-158],[-58,4],[-65,-14],[6,-290],[-41,-5],[-35,-135],[-86,-25]],[[55230,77704],[67,-229],[89,-169],[-107,-222]],[[55155,75778],[-31,-100]],[[54448,76285],[-233,434],[56,45]],[[53809,77462],[194,-20],[51,100],[94,-97],[109,-11],[-1,165],[97,60],[27,239],[221,157]],[[54601,78055],[88,-73],[208,-253],[229,-114],[104,89]],[[54716,79012],[141,-151],[103,-65],[233,73],[22,118],[111,18],[135,92],[30,-38],[130,74],[66,139],[91,36],[297,-180],[59,61]],[[56134,79189],[155,-161],[19,-159]],[[56308,78869],[-170,-123],[-131,-401],[-168,-401],[-223,-111]],[[55616,77833],[-173,26],[-213,-155]],[[54601,78055],[-54,200],[-47,6]],[[84713,45326],[28,-117],[5,-179]],[[89166,49043],[5,-1925],[4,-1925]],[[80461,51765],[47,-395],[190,-334],[179,121],[177,-43],[162,299],[133,52],[263,-166],[226,126],[143,822],[107,205],[96,672],[319,0],[241,-100]],[[72530,68413],[-176,-268],[-108,-553],[269,-224],[262,-289],[362,-332],[381,-76],[160,-301],[215,-56],[334,-138],[231,10],[32,234],[-36,375],[21,255]],[[77035,67277],[20,-224],[-97,-108],[23,-364],[-199,107],[-359,-408],[8,-338],[-153,-496],[-14,-288],[-124,-487],[-217,135],[-11,-612],[-63,-201],[30,-251],[-137,-140]],[[73107,61020],[-276,-387],[-1,-271]],[[72692,60216],[-251,-212],[-129,-31]],[[71996,56025],[-253,-168],[-93,-401]],[[68937,64577],[185,395],[612,-2],[-56,507],[-156,300],[-31,455],[-182,265],[306,619],[323,-45],[290,620],[174,599],[270,593],[-4,421],[236,342],[-224,292],[-96,400],[-99,517],[137,255],[421,-144],[310,88],[268,496]],[[64978,72558],[244,114],[197,338],[186,-17],[122,110],[197,-55],[308,-299],[221,-65],[318,-523],[207,-21],[24,-498]],[[66909,68203],[137,-310],[112,-357],[266,-260],[7,-520],[133,-96],[23,-272],[-400,-305],[-105,-687]],[[66559,65575],[-303,136],[-313,76]],[[63594,68492],[-104,-231]],[[63490,68261],[-153,311],[-3,314],[-89,0],[46,428],[-143,449],[-340,324],[-193,562],[65,461],[139,204],[-21,345],[-182,177],[-180,705]],[[62436,72541],[-152,473],[55,183],[-87,678],[190,168]],[[63326,68290],[-187,49],[-204,-567]],[[62935,67772],[-516,47],[-784,1188],[-413,414],[-335,160]],[[60887,69581],[-112,720]],[[60775,70301],[615,614],[105,715],[-26,431],[152,146],[142,369]],[[61763,72576],[119,92],[324,-77],[97,-150],[133,100]],[[63490,68261],[-164,29]],[[59873,69719],[-100,82],[-58,-394],[69,-66],[-71,-81],[-12,-156],[131,80]],[[59832,69184],[7,-230],[-139,-944]],[[59757,70130],[93,-1],[25,104],[75,8]],[[59950,70241],[4,-242],[-38,-90],[6,-4]],[[59922,69905],[-49,-186]],[[53835,78058],[-31,-291],[67,-251]],[[54413,75123],[249,-214],[204,-178]],[[53108,75604],[-189,340],[-86,585]],[[59922,69905],[309,-234],[544,630]],[[60887,69581],[-53,-89],[-556,-296],[277,-591],[-92,-101],[-46,-197],[-212,-82],[-66,-213],[-120,-182],[-310,94]],[[59832,69184],[41,173],[0,362]],[[69711,75551],[-159,-109],[-367,-412],[-121,-422],[-104,-4],[-76,280],[-353,19],[-57,484],[-135,4],[21,593],[-333,431],[-476,-46],[-326,-86],[-265,533],[-227,223],[-431,423],[-52,51],[-715,-349],[11,-2178]],[[65546,74986],[-142,-29],[-195,463],[-188,166],[-315,-123],[-123,-197]],[[63639,77993],[-142,96],[29,304],[-177,395],[-207,-17],[-235,401],[160,448],[-81,120],[222,649],[285,-342],[35,431],[573,643],[434,15],[612,-409],[329,-239],[295,249],[440,12],[356,-306],[80,175],[391,-25],[69,280],[-450,406],[267,288],[-52,161],[266,153],[-200,405],[127,202],[1039,205],[136,146],[695,218],[250,245],[499,-127],[88,-612],[290,144],[356,-202],[-23,-322],[267,33],[696,558],[-102,-185],[355,-457],[620,-1500],[148,309],[383,-340],[399,151],[154,-106],[133,-341],[194,-115],[119,-251],[358,79],[147,-361]],[[72294,75601],[-171,87],[-140,212],[-412,62],[-461,16],[-100,-65],[-396,248],[-158,-122],[-43,-349],[-457,204],[-183,-84],[-62,-259]],[[60889,47817],[-399,590],[-19,343],[-1007,1203],[-47,65]],[[59417,50018],[-3,627],[80,239],[137,391],[101,431],[-123,678],[-32,296],[-132,411]],[[59445,53091],[171,352],[188,390]],[[61626,52895],[-243,-670],[3,-2152],[165,-488]],[[70465,73876],[-526,-89],[-343,192],[-301,-46],[26,340],[303,-98],[101,182]],[[69725,74357],[212,-58],[355,425],[-329,311],[-198,-147],[-205,223],[234,382],[-83,58]],[[78495,57780],[-66,713],[178,492],[359,112],[261,-84]],[[79227,59013],[229,-232],[126,407],[246,-217]],[[79828,58971],[64,-394],[-34,-708],[-467,-455],[122,-358],[-292,-43],[-240,-238]],[[85103,71220],[51,443],[-122,615]],[[85048,72883],[17,54],[124,-21],[108,266],[197,29],[118,39],[40,143]],[[55575,75742],[52,132]],[[55627,75874],[66,43],[38,196],[50,33],[40,-84],[52,-36],[36,-94],[46,-28],[54,-110],[39,4],[-31,-144],[-33,-71],[9,-44]],[[55993,75539],[-62,-23],[-164,-91],[-13,-121],[-35,5]],[[63448,67449],[-196,-16],[-69,282],[-248,57]],[[79227,59013],[90,266],[12,500],[-224,515],[-18,583],[-211,480],[-210,40],[-56,-205],[-163,-17],[-83,104],[-293,-353],[-6,530],[68,623],[-188,27],[-16,355],[-120,182]],[[77809,62643],[59,218],[237,384]],[[78380,63852],[162,-466],[125,-537],[342,-5],[108,-515],[-178,-155],[-80,-212],[333,-353],[231,-699],[175,-520],[210,-411],[70,-418],[-50,-590]],[[59999,71049],[125,-31],[45,-231],[-151,-223],[-68,-323]],[[47498,53435],[-252,449],[-237,324]],[[46822,54589],[66,189],[15,172],[126,320],[129,276]],[[54125,64088],[-197,-220],[-156,324],[-439,255]],[[52633,68486],[136,137],[24,250],[-30,244],[191,228],[86,189],[135,170],[16,454]],[[56646,69496],[276,-70],[68,-195]],[[56944,63578],[0,-1180],[-320,-2],[-3,-248]],[[56621,62148],[-1108,1131],[-1108,1132],[-280,-323]],[[57499,32928],[148,374],[151,232],[130,120],[121,-182],[96,-178],[-85,-288],[-47,-192],[-155,-93],[-51,-188],[-99,-59],[-209,454]],[[56314,82678],[-23,150],[30,162],[-123,94],[-291,103]],[[55848,83684],[318,181],[466,-38],[273,59],[39,-123],[148,-38],[267,-287]],[[56523,82432],[-67,182],[-142,64]],[[57579,84537],[134,-136],[24,-287],[89,-348]],[[47592,66920],[-42,0],[7,-317],[-172,-19],[-90,-134],[-126,0],[-100,76],[-234,-63],[-91,-460],[-86,-44],[-131,-745],[-386,-637],[-92,-816],[-114,-265],[-33,-213],[-625,-48],[-5,1]],[[45272,63236],[13,274],[106,161],[91,308],[-18,200],[96,417],[155,376],[93,95],[74,344],[6,315],[100,365],[185,216],[177,603]],[[46350,66910],[5,8],[139,227]],[[46494,67145],[259,65],[218,404],[140,158]],[[57394,79070],[66,87],[185,58],[204,-184],[115,-22],[125,-159],[-20,-200],[101,-97],[40,-247],[97,-150],[-19,-88],[52,-60],[-74,-44],[-164,18],[-27,81],[-58,-47],[20,-106],[-76,-188],[-49,-203],[-70,-64]],[[57842,77455],[-50,270],[30,252],[-9,259],[-160,352],[-89,249],[-86,175],[-84,58]],[[23016,65864],[-107,-518],[-49,-426],[-20,-791],[-27,-289],[48,-322],[86,-288],[56,-458],[184,-440],[65,-337],[109,-291],[295,-157],[114,-247],[244,165],[212,60],[208,106],[175,101],[176,241],[67,345],[22,496],[48,173],[188,155],[294,137],[246,-21],[169,50],[66,-125],[-9,-285],[-149,-351],[-66,-360],[51,-103],[-42,-255],[-69,-461],[-71,152],[-58,-10]],[[24067,59806],[-144,191],[-226,155]],[[19641,66203],[-142,138],[-164,287]],[[18570,68996],[-201,234],[-93,-25]],[[19362,64423],[-181,337],[-201,286]],[[17464,69802],[316,46],[353,64],[-26,-116],[419,-287],[634,-416],[552,4],[221,0],[0,244],[481,0],[102,-210],[142,-186],[165,-260],[92,-309],[69,-325],[144,-178],[230,-177],[175,467],[227,11],[196,-236],[139,-404],[96,-346],[164,-337],[61,-414],[78,-277],[217,-184],[197,-130],[108,18]],[[55993,75539],[95,35],[128,9]],[[46619,59216],[93,107],[47,348],[88,14],[194,-165],[157,117],[107,-39],[42,131],[1114,9],[62,414],[-48,73],[-134,2550],[-134,2550],[425,10]],[[51185,61897],[1,-1361],[-152,-394],[-24,-364],[-247,-94],[-379,-51],[-102,-210],[-178,-23]],[[46801,57931],[13,184],[-24,229],[-104,166],[-54,338],[-13,368]],[[77809,62643],[-159,-137],[-162,-256],[-196,-26],[-127,-639],[-117,-107],[134,-519],[177,-431],[113,-390],[-101,-514],[-96,-109],[66,-296],[185,-470],[32,-330],[-4,-274],[108,-539],[-152,-551],[-135,-607]],[[55338,76294],[74,-101],[40,-82],[91,-63],[106,-123],[-22,-51]],[[55380,75322],[-58,46]],[[74375,79706],[292,102],[530,509],[423,278],[242,-182],[289,-8],[186,-276],[277,-22],[402,-148],[270,411],[-113,348],[288,612],[311,-244],[252,-69],[327,-152],[53,-443],[394,-248],[263,109],[351,78],[279,-78],[272,-284],[168,-302],[258,6],[350,-96],[255,146],[366,98],[407,416],[166,-63],[146,-198],[331,49]],[[59599,43773],[209,48],[334,-166],[73,74],[193,16],[99,177],[167,-10],[303,230],[221,342]],[[59870,36949],[-45,-275],[65,-101]],[[59890,36573],[-41,-245],[-116,-211]],[[59119,34780],[-211,5]],[[58908,34785],[-24,261],[-41,265]],[[58843,35311],[-23,212],[49,659],[-72,419],[-133,832]],[[58664,37433],[292,671],[74,426],[42,53],[31,348],[-45,175],[12,442],[54,409],[0,748],[-145,190],[-132,43],[-60,146],[-128,125],[-232,-12],[-18,220]],[[58409,41417],[-26,421],[843,487]],[[59226,42325],[159,-284],[77,54],[110,-149],[16,-237],[-59,-274],[21,-417],[181,-365],[85,410],[120,124],[-24,760],[-116,427],[-100,191],[-97,-9],[-77,768],[77,449]],[[46619,59216],[-184,405],[-168,435],[-184,157],[-133,173],[-155,-6],[-135,-129],[-138,51],[-96,-189]],[[45260,62987],[60,197],[1088,-4],[-53,853],[68,304],[261,53],[-9,1512],[911,-31],[1,895]],[[59226,42325],[-147,153],[85,549],[87,205],[-53,490],[56,479],[47,160],[-71,501],[-131,264]],[[59099,45126],[273,-110],[55,-164],[95,-275],[77,-804]],[[77801,54399],[48,105],[227,-258],[22,-304],[183,71],[91,243]],[[56448,40227],[228,134],[180,-34],[109,-133],[2,-49]],[[55526,35946],[0,-2182],[-248,-302],[-149,-43],[-175,112],[-125,43],[-47,252],[-109,162],[-133,-292]],[[54125,64088],[68,-919],[104,-153],[4,-188],[116,-203],[-60,-254],[-107,-1199],[-15,-769],[-354,-557],[-120,-778],[115,-219],[0,-380],[178,-13],[-28,-279]],[[53939,57955],[-52,-13],[-188,647],[-65,24],[-217,-331],[-215,173],[-150,34],[-80,-83],[-163,18],[-164,-252],[-141,-14],[-337,305],[-131,-145],[-142,10],[-104,223],[-279,221],[-298,-70],[-72,-128],[-39,-340],[-80,-238],[-19,-527]],[[52072,53186],[-105,31],[-107,-132]],[[51398,53895],[-197,389],[-209,-7]],[[25647,58207],[31,91],[46,-88]],[[51063,81078],[244,869],[380,248]],[[58639,91676],[-473,-237],[-224,-54]],[[55734,91409],[-172,-24],[-41,-389],[-523,95],[-74,-329],[-267,2],[-183,-421],[-278,-655],[-431,-831],[101,-202],[-97,-234],[-275,10],[-180,-554],[17,-784],[177,-300],[-92,-694],[-231,-405],[-122,-341]],[[52328,85032],[-371,-138],[-384,301]],[[51474,85830],[-88,1363],[256,381]],[[65352,60997],[1,-238],[-134,-165]],[[64880,60451],[-128,-34]],[[64752,60417],[-91,413],[-217,975]],[[64444,61805],[833,591],[185,1182],[-127,418]],[[65945,64688],[203,-78],[165,-107]],[[68734,64727],[-83,424],[-215,450]],[[28212,55535],[-52,196],[-138,164]],[[27170,56020],[-6,-126],[111,-26]],[[55461,82736],[342,-67],[511,9]],[[56535,81053],[139,-515],[-29,-166],[-138,-69],[-252,-491],[71,-266],[-60,35]],[[56266,79581],[-264,227],[-200,-84],[-131,61],[-165,-127],[-140,210],[-114,-81],[-16,36]],[[86221,75560],[-120,-200],[-83,-202]],[[85048,72883],[-135,112],[-34,-111]],[[84641,73095],[76,260],[66,69]],[[84829,73851],[-18,96],[-163,65]],[[86288,75628],[39,-104]],[[64246,66009],[84,-185],[5,-346]],[[64274,65130],[-77,-42],[-84,117]],[[56308,78869],[120,127],[172,-65],[178,-3],[129,-144],[95,91],[205,56],[69,139],[118,0]],[[57842,77455],[124,-109],[131,95],[126,-101]],[[56293,76715],[-51,103],[65,99],[-69,74],[-87,-133],[-162,172],[-22,244],[-169,139],[-31,188],[-151,232]],[[81143,94175],[251,112],[141,-379]],[[84237,94144],[590,-104],[443,4]],[[97224,91732],[123,262],[886,-165]],[[96192,85904],[-126,219],[-268,-253]],[[95032,82989],[-486,-302],[-96,-674]],[[93543,84472],[14,276],[432,132]],[[95182,86999],[-705,-649],[-227,727]],[[90412,85637],[-914,-175],[-899,-1153]],[[88378,82339],[178,295],[305,-38]],[[89262,81946],[9,-503],[-217,-590]],[[60617,78409],[9,262],[143,165],[269,43],[44,197],[-62,326],[113,310],[-3,173],[-410,192],[-162,-6],[-172,277],[-213,-94],[-352,208],[6,116],[-99,256],[-222,29],[-23,183],[70,120],[-178,334],[-288,-57],[-84,30],[-70,-134],[-104,23]],[[58639,91676],[286,206],[456,-358],[761,-140],[1050,-668],[213,-281],[18,-393],[-308,-311],[-454,-157],[-1240,449],[-204,-75],[453,-433]],[[59670,89515],[18,-274],[18,-604]],[[59706,88637],[358,-180],[217,-153],[36,286]],[[60317,88590],[-168,254],[177,224]],[[60998,88700],[233,144],[-186,433]],[[62654,90499],[1,-328],[219,-203]],[[63371,90473],[580,282],[970,507]],[[69038,93080],[183,537],[206,116]],[[69427,93733],[736,-156],[57,-328]],[[70444,91717],[222,593],[-361,482]],[[72363,94093],[483,119],[669,-26]],[[58449,49909],[110,-333],[-16,-348],[-80,-74]],[[58216,49787],[67,-60],[166,182]],[[61883,60238],[-37,252],[-83,178]],[[60335,65400],[-77,306],[-81,132]],[[63741,66597],[190,-249],[16,-243]],[[64444,61805],[-801,-226],[-259,-266],[-199,-620],[-130,-99],[-70,197],[-106,-30],[-269,60],[-50,59],[-321,-14],[-75,-53],[-114,153],[-74,-290],[28,-249],[-121,-189]],[[56351,57163],[3,143],[-102,174],[-3,343],[-58,228],[-98,-34],[28,217],[72,246],[-32,245],[92,181],[-58,138],[73,365],[127,435],[240,-41],[-14,2345]],[[59433,56242],[1,-71]],[[59434,56171],[-39,12],[5,294],[-33,203],[-143,233],[-34,426],[34,436],[-129,41],[-19,-132],[-167,-30],[67,-173],[23,-355],[-152,-324],[-138,-426],[-144,-61],[-233,345],[-105,-122],[-29,-172],[-143,-112],[-9,-122],[-277,0],[-38,122],[-200,20],[-100,-101],[-77,51],[-143,344],[-48,163],[-200,-81],[-76,-274],[-72,-528],[-95,-111],[-85,-65]],[[56635,55672],[-23,28]],[[59445,53091],[-171,-272],[-195,1],[-224,-138],[-176,132],[-115,-161]],[[56824,55442],[-189,230]],[[59434,56171],[3,-460]],[[25613,58487],[-31,-139]],[[62075,57243],[54,-245],[125,-247]],[[63596,57321],[-2,-9],[-1,-244],[0,-596],[0,-308],[-125,-363],[-194,-493]],[[34889,53069],[109,-351],[-49,-254],[-24,-270],[-71,-248]],[[56266,79581],[-77,-154],[-55,-238]],[[58908,34785],[-56,-263],[-163,-63],[-166,320],[-2,204],[76,222],[26,172],[80,42],[140,-108]],[[60041,71744],[74,129],[75,130],[15,329],[91,-115],[306,165],[147,-112],[229,2],[320,222],[149,-10],[316,92]],[[68841,72526],[156,598],[-60,440],[-204,140],[72,261],[232,-28],[132,326],[89,380],[371,137],[-58,-274],[40,-164],[114,15]],[[65546,74986],[313,8],[-45,297],[237,204],[234,343],[374,-312],[30,-471],[106,-121],[301,27],[93,-108],[137,-609],[317,-408],[181,-278],[291,-289],[369,-253],[-7,-362]],[[53083,72381],[-139,-290],[-2,-273]],[[58441,72005],[-192,-70],[-268,314]],[[57981,72249],[-303,-11],[-165,588]],[[59768,75418],[485,-417],[399,-228]],[[57321,74302],[-87,276],[3,121]],[[59099,45126],[-157,177],[-177,100],[-111,99],[-116,150]],[[58388,46397],[-161,331],[-55,342]],[[58449,49909],[98,71],[304,-7],[566,45]],[[30523,76389],[-147,-351],[-47,-133]],[[30377,75084],[-133,11],[-205,-103]],[[29172,73738],[-61,30],[-91,148]],[[29077,73598],[69,-105],[5,-223]],[[28966,72994],[-142,225],[-33,491]],[[28797,73080],[-183,93],[191,-191]],[[27672,65472],[-83,-75],[-137,72]],[[27408,65728],[-105,136],[-148,508]],[[26747,68267],[-108,90],[-281,-268]],[[26309,68119],[-135,275],[-174,147]],[[25227,68491],[-114,-92],[50,-157]],[[24755,67801],[-207,312],[-242,-73]],[[16564,70932],[-71,95],[-33,324]],[[16460,71351],[-270,594],[-231,821],[10,137],[-123,195],[-215,495],[-38,482],[-148,323],[61,489],[-10,507],[-89,453],[109,557]],[[15516,76404],[34,536],[33,536]],[[15583,77476],[-50,792],[-88,506],[-80,274],[33,115],[402,-200],[148,-558]],[[15948,78405],[69,156],[-45,484],[-94,485]],[[10396,86079],[-385,-51],[-546,272]],[[8164,85656],[-308,-126],[-39,348]],[[7158,84934],[-299,-248],[-278,-180]],[[4985,85596],[50,216],[-179,211]],[[4541,89915],[-38,-296],[586,23]],[[4864,90008],[-342,225],[-197,296]],[[30102,56752],[-123,-344],[105,-468]],[[31762,56607],[213,-74],[155,185]],[[63521,58853],[-122,-33],[-83,35]],[[63153,58610],[-177,-114],[-233,-30]],[[62539,58233],[-43,-150],[-137,13]],[[64752,60417],[-201,-158]],[[57838,31217],[-210,-269],[-290,-229]],[[58175,37528],[113,-7],[134,-100],[94,71],[148,-59]],[[58409,41417],[-210,-81],[-159,-235],[-33,-205],[-100,-46],[-241,-486],[-154,-383],[-94,-13],[-90,68],[-311,65]]]}
    diff --git a/mapbox-gl/examples/device-orientation-require.js b/mapbox-gl/examples/device-orientation-require.js
    new file mode 100644
    index 0000000000..7c39e59a1e
    --- /dev/null
    +++ b/mapbox-gl/examples/device-orientation-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.DeviceOrientation');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.dom.Input');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/device-orientation.html b/mapbox-gl/examples/device-orientation.html
    new file mode 100644
    index 0000000000..589ddb2623
    --- /dev/null
    +++ b/mapbox-gl/examples/device-orientation.html
    @@ -0,0 +1,58 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Device-Orientation example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Device orientation example</h4>
    +          <label class="checkbox" for="track">
    +            <input id="track" type="checkbox"/>track changes
    +          </label>
    +          <p>&alpha; : <code id="alpha"></code></p>
    +          <p>&beta; : <code id="beta"></code></p>
    +          <p>&gamma; : <code id="gamma"></code></p>
    +          <p>heading : <code id="heading"></code></p>
    +          <p id="shortdesc">Listen to DeviceOrientation events</p>
    +          <div id="docs">
    +            <p>See the <a href="device-orientation.js" target="_blank">device-orientation.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">orientation, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=device-orientation" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/device-orientation.js b/mapbox-gl/examples/device-orientation.js
    new file mode 100644
    index 0000000000..ed6a16ef3f
    --- /dev/null
    +++ b/mapbox-gl/examples/device-orientation.js
    @@ -0,0 +1,46 @@
    +var projection = ol.proj.get('EPSG:3857');
    +var view = new ol.View({
    +  center: [0, 0],
    +  projection: projection,
    +  extent: projection.getExtent(),
    +  zoom: 2
    +});
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +var deviceOrientation = new ol.DeviceOrientation();
    +var track = new ol.dom.Input(document.getElementById('track'));
    +track.bindTo('checked', deviceOrientation, 'tracking');
    +
    +deviceOrientation.on('change', function(event) {
    +  $('#alpha').text(deviceOrientation.getAlpha() + ' [rad]');
    +  $('#beta').text(deviceOrientation.getBeta() + ' [rad]');
    +  $('#gamma').text(deviceOrientation.getGamma() + ' [rad]');
    +  $('#heading').text(deviceOrientation.getHeading() + ' [rad]');
    +});
    +
    +// tilt the map
    +deviceOrientation.on(['change:beta', 'change:gamma'], function(event) {
    +  var center = view.getCenter();
    +  var resolution = view.getResolution();
    +  var beta = event.target.getBeta() || 0;
    +  var gamma = event.target.getGamma() || 0;
    +
    +  center[0] -= resolution * gamma * 25;
    +  center[1] += resolution * beta * 25;
    +
    +  view.setCenter(view.constrainCenter(center));
    +});
    diff --git a/mapbox-gl/examples/drag-and-drop-image-vector-require.js b/mapbox-gl/examples/drag-and-drop-image-vector-require.js
    new file mode 100644
    index 0000000000..bf68bc455c
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop-image-vector-require.js
    @@ -0,0 +1,18 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.GPX');
    +goog.require('ol.format.GeoJSON');
    +goog.require('ol.format.IGC');
    +goog.require('ol.format.KML');
    +goog.require('ol.format.TopoJSON');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.DragAndDrop');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.ImageVector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/drag-and-drop-image-vector.html b/mapbox-gl/examples/drag-and-drop-image-vector.html
    new file mode 100644
    index 0000000000..422d31e648
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop-image-vector.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Drag-and-Drop image vector example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">Drag-and-Drop image vector example</h4>
    +          <p id="shortdesc">Example of using the drag-and-drop interaction with a ol.source.ImageVector. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. Each file is rendered to an image on the client.</p>
    +          <div id="docs">
    +            <p>See the <a href="drag-and-drop-image-vector.js" target="_blank">drag-and-drop-image-vector.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">drag-and-drop-image-vector, gpx, geojson, igc, kml, topojson, vector, image</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=drag-and-drop-image-vector" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/drag-and-drop-image-vector.js b/mapbox-gl/examples/drag-and-drop-image-vector.js
    new file mode 100644
    index 0000000000..a5c3dab8e9
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop-image-vector.js
    @@ -0,0 +1,138 @@
    +var defaultStyle = {
    +  'Point': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,255,0,0.5)'
    +      }),
    +      radius: 5,
    +      stroke: new ol.style.Stroke({
    +        color: '#ff0',
    +        width: 1
    +      })
    +    })
    +  })],
    +  'LineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#f00',
    +      width: 3
    +    })
    +  })],
    +  'Polygon': [new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0,255,255,0.5)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#0ff',
    +      width: 1
    +    })
    +  })],
    +  'MultiPoint': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,0,255,0.5)'
    +      }),
    +      radius: 5,
    +      stroke: new ol.style.Stroke({
    +        color: '#f0f',
    +        width: 1
    +      })
    +    })
    +  })],
    +  'MultiLineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#0f0',
    +      width: 3
    +    })
    +  })],
    +  'MultiPolygon': [new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0,0,255,0.5)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#00f',
    +      width: 1
    +    })
    +  })]
    +};
    +
    +var styleFunction = function(feature, resolution) {
    +  var featureStyleFunction = feature.getStyleFunction();
    +  if (featureStyleFunction) {
    +    return featureStyleFunction.call(feature, resolution);
    +  } else {
    +    return defaultStyle[feature.getGeometry().getType()];
    +  }
    +};
    +
    +var dragAndDropInteraction = new ol.interaction.DragAndDrop({
    +  formatConstructors: [
    +    ol.format.GPX,
    +    ol.format.GeoJSON,
    +    ol.format.IGC,
    +    ol.format.KML,
    +    ol.format.TopoJSON
    +  ]
    +});
    +
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([dragAndDropInteraction]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        imagerySet: 'Aerial',
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +dragAndDropInteraction.on('addfeatures', function(event) {
    +  var vectorSource = new ol.source.Vector({
    +    features: event.features,
    +    projection: event.projection
    +  });
    +  map.getLayers().push(new ol.layer.Image({
    +    source: new ol.source.ImageVector({
    +      source: vectorSource,
    +      style: styleFunction
    +    })
    +  }));
    +  var view = map.getView();
    +  view.fitExtent(
    +      vectorSource.getExtent(), /** @type {ol.Size} */ (map.getSize()));
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  var features = [];
    +  map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    features.push(feature);
    +  });
    +  if (features.length > 0) {
    +    var info = [];
    +    var i, ii;
    +    for (i = 0, ii = features.length; i < ii; ++i) {
    +      info.push(features[i].get('name'));
    +    }
    +    document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
    +  } else {
    +    document.getElementById('info').innerHTML = '&nbsp;';
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/drag-and-drop-require.js b/mapbox-gl/examples/drag-and-drop-require.js
    new file mode 100644
    index 0000000000..3b7dd1fbd4
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop-require.js
    @@ -0,0 +1,17 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.GPX');
    +goog.require('ol.format.GeoJSON');
    +goog.require('ol.format.IGC');
    +goog.require('ol.format.KML');
    +goog.require('ol.format.TopoJSON');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.DragAndDrop');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/drag-and-drop.html b/mapbox-gl/examples/drag-and-drop.html
    new file mode 100644
    index 0000000000..eff604f539
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Drag-and-Drop example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">Drag-and-Drop example</h4>
    +          <p id="shortdesc">Example of using the drag-and-drop interaction. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.</p>
    +          <div id="docs">
    +            <p>See the <a href="drag-and-drop.js" target="_blank">drag-and-drop.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">drag-and-drop, gpx, geojson, igc, kml, topojson</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=drag-and-drop" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/drag-and-drop.js b/mapbox-gl/examples/drag-and-drop.js
    new file mode 100644
    index 0000000000..6da24ef663
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-and-drop.js
    @@ -0,0 +1,135 @@
    +var defaultStyle = {
    +  'Point': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,255,0,0.5)'
    +      }),
    +      radius: 5,
    +      stroke: new ol.style.Stroke({
    +        color: '#ff0',
    +        width: 1
    +      })
    +    })
    +  })],
    +  'LineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#f00',
    +      width: 3
    +    })
    +  })],
    +  'Polygon': [new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0,255,255,0.5)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#0ff',
    +      width: 1
    +    })
    +  })],
    +  'MultiPoint': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,0,255,0.5)'
    +      }),
    +      radius: 5,
    +      stroke: new ol.style.Stroke({
    +        color: '#f0f',
    +        width: 1
    +      })
    +    })
    +  })],
    +  'MultiLineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#0f0',
    +      width: 3
    +    })
    +  })],
    +  'MultiPolygon': [new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0,0,255,0.5)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#00f',
    +      width: 1
    +    })
    +  })]
    +};
    +
    +var styleFunction = function(feature, resolution) {
    +  var featureStyleFunction = feature.getStyleFunction();
    +  if (featureStyleFunction) {
    +    return featureStyleFunction.call(feature, resolution);
    +  } else {
    +    return defaultStyle[feature.getGeometry().getType()];
    +  }
    +};
    +
    +var dragAndDropInteraction = new ol.interaction.DragAndDrop({
    +  formatConstructors: [
    +    ol.format.GPX,
    +    ol.format.GeoJSON,
    +    ol.format.IGC,
    +    ol.format.KML,
    +    ol.format.TopoJSON
    +  ]
    +});
    +
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([dragAndDropInteraction]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        imagerySet: 'Aerial',
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +dragAndDropInteraction.on('addfeatures', function(event) {
    +  var vectorSource = new ol.source.Vector({
    +    features: event.features,
    +    projection: event.projection
    +  });
    +  map.getLayers().push(new ol.layer.Vector({
    +    source: vectorSource,
    +    style: styleFunction
    +  }));
    +  var view = map.getView();
    +  view.fitExtent(
    +      vectorSource.getExtent(), /** @type {ol.Size} */ (map.getSize()));
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  var features = [];
    +  map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    features.push(feature);
    +  });
    +  if (features.length > 0) {
    +    var info = [];
    +    var i, ii;
    +    for (i = 0, ii = features.length; i < ii; ++i) {
    +      info.push(features[i].get('name'));
    +    }
    +    document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
    +  } else {
    +    document.getElementById('info').innerHTML = '&nbsp;';
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/drag-features-require.js b/mapbox-gl/examples/drag-features-require.js
    new file mode 100644
    index 0000000000..ee593ea030
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-features-require.js
    @@ -0,0 +1,16 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.Pointer');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.TileJSON');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/drag-features.html b/mapbox-gl/examples/drag-features.html
    new file mode 100644
    index 0000000000..109afa0074
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-features.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Drag features example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Drag features example</h4>
    +          <p id="shortdesc">Example of a drag features interaction.</p>
    +          <div id="docs">
    +            <p>See the <a href="drag-features.js" target="_blank">drag-features.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">drag, feature, vector, editing</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=drag-features" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/drag-features.js b/mapbox-gl/examples/drag-features.js
    new file mode 100644
    index 0000000000..691abff003
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-features.js
    @@ -0,0 +1,174 @@
    +/**
    + * Define a namespace for the application.
    + */
    +window.app = {};
    +var app = window.app;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + */
    +app.Drag = function() {
    +
    +  ol.interaction.Pointer.call(this, {
    +    handleDownEvent: app.Drag.prototype.handleDownEvent,
    +    handleDragEvent: app.Drag.prototype.handleDragEvent,
    +    handleMoveEvent: app.Drag.prototype.handleMoveEvent,
    +    handleUpEvent: app.Drag.prototype.handleUpEvent
    +  });
    +
    +  /**
    +   * @type {ol.Pixel}
    +   * @private
    +   */
    +  this.coordinate_ = null;
    +
    +  /**
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.cursor_ = 'pointer';
    +
    +  /**
    +   * @type {ol.Feature}
    +   * @private
    +   */
    +  this.feature_ = null;
    +
    +  /**
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.previousCursor_ = undefined;
    +
    +};
    +ol.inherits(app.Drag, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} evt Map browser event.
    + * @return {boolean} `true` to start the drag sequence.
    + */
    +app.Drag.prototype.handleDownEvent = function(evt) {
    +  var map = evt.map;
    +
    +  var feature = map.forEachFeatureAtPixel(evt.pixel,
    +      function(feature, layer) {
    +        return feature;
    +      });
    +
    +  if (feature) {
    +    this.coordinate_ = evt.coordinate;
    +    this.feature_ = feature;
    +  }
    +
    +  return !!feature;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} evt Map browser event.
    + */
    +app.Drag.prototype.handleDragEvent = function(evt) {
    +  var map = evt.map;
    +
    +  var feature = map.forEachFeatureAtPixel(evt.pixel,
    +      function(feature, layer) {
    +        return feature;
    +      });
    +
    +  var deltaX = evt.coordinate[0] - this.coordinate_[0];
    +  var deltaY = evt.coordinate[1] - this.coordinate_[1];
    +
    +  var geometry = /** @type {ol.geom.SimpleGeometry} */
    +      (this.feature_.getGeometry());
    +  geometry.translate(deltaX, deltaY);
    +
    +  this.coordinate_[0] = evt.coordinate[0];
    +  this.coordinate_[1] = evt.coordinate[1];
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} evt Event.
    + */
    +app.Drag.prototype.handleMoveEvent = function(evt) {
    +  if (this.cursor_) {
    +    var map = evt.map;
    +    var feature = map.forEachFeatureAtPixel(evt.pixel,
    +        function(feature, layer) {
    +          return feature;
    +        });
    +    var element = evt.map.getTargetElement();
    +    if (feature) {
    +      if (element.style.cursor != this.cursor_) {
    +        this.previousCursor_ = element.style.cursor;
    +        element.style.cursor = this.cursor_;
    +      }
    +    } else if (this.previousCursor_ !== undefined) {
    +      element.style.cursor = this.previousCursor_;
    +      this.previousCursor_ = undefined;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} evt Map browser event.
    + * @return {boolean} `false` to stop the drag sequence.
    + */
    +app.Drag.prototype.handleUpEvent = function(evt) {
    +  this.coordinate_ = null;
    +  this.feature_ = null;
    +  return false;
    +};
    +
    +
    +var pointFeature = new ol.Feature(new ol.geom.Point([0, 0]));
    +
    +var lineFeature = new ol.Feature(
    +    new ol.geom.LineString([[-1e7, 1e6], [-1e6, 3e6]]));
    +
    +var polygonFeature = new ol.Feature(
    +    new ol.geom.Polygon([[[-3e6, -1e6], [-3e6, 1e6],
    +        [-1e6, 1e6], [-1e6, -1e6], [-3e6, -1e6]]]));
    +
    +
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([new app.Drag()]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.TileJSON({
    +        url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.jsonp'
    +      })
    +    }),
    +    new ol.layer.Vector({
    +      source: new ol.source.Vector({
    +        features: [pointFeature, lineFeature, polygonFeature]
    +      }),
    +      style: new ol.style.Style({
    +        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
    +          anchor: [0.5, 46],
    +          anchorXUnits: 'fraction',
    +          anchorYUnits: 'pixels',
    +          opacity: 0.95,
    +          src: 'data/icon.png'
    +        })),
    +        stroke: new ol.style.Stroke({
    +          width: 3,
    +          color: [255, 0, 0, 1]
    +        }),
    +        fill: new ol.style.Fill({
    +          color: [0, 0, 255, 0.6]
    +        })
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/drag-rotate-and-zoom-require.js b/mapbox-gl/examples/drag-rotate-and-zoom-require.js
    new file mode 100644
    index 0000000000..f7bf738575
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-rotate-and-zoom-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.DragRotateAndZoom');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/drag-rotate-and-zoom.html b/mapbox-gl/examples/drag-rotate-and-zoom.html
    new file mode 100644
    index 0000000000..5dcb3fe950
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-rotate-and-zoom.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Drag rotate and zoom example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Drag rotate and zoom example</h4>
    +          <p id="shortdesc">A single interaction to drag, rotate, and zoom.</p>
    +          <div id="docs">
    +            <p><code>Shift</code> + Drag to rotate and zoom the map around its center.</p>
    +            <p>See the <a href="drag-rotate-and-zoom.js" target="_blank">drag-rotate-and-zoom.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">drag, rotate, zoom, interaction</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=drag-rotate-and-zoom" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/drag-rotate-and-zoom.js b/mapbox-gl/examples/drag-rotate-and-zoom.js
    new file mode 100644
    index 0000000000..212609befe
    --- /dev/null
    +++ b/mapbox-gl/examples/drag-rotate-and-zoom.js
    @@ -0,0 +1,16 @@
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([
    +    new ol.interaction.DragRotateAndZoom()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/draw-and-modify-features-require.js b/mapbox-gl/examples/draw-and-modify-features-require.js
    new file mode 100644
    index 0000000000..e218846000
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-and-modify-features-require.js
    @@ -0,0 +1,12 @@
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Draw');
    +goog.require('ol.interaction.Modify');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/draw-and-modify-features.html b/mapbox-gl/examples/draw-and-modify-features.html
    new file mode 100644
    index 0000000000..277812df9a
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-and-modify-features.html
    @@ -0,0 +1,61 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Draw and modify features example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Draw and modify features example</h4>
    +          <p id="shortdesc">Example of using the ol.interaction.Draw interaction together with
    +          the ol.interaction.Modify interaction.</p>
    +          <form class="form-inline">
    +            <label>Geometry type &nbsp;</label>
    +              <select id="type">
    +                <option value="Point">Point</option>
    +                <option value="LineString">LineString</option>
    +                <option value="Polygon">Polygon</option>
    +              </select>
    +          </form>
    +
    +          <div id="docs">
    +            <p>See the <a href="draw-and-modify-features.js" target="_blank">draw-and-modify-features.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">draw, edit, modify, vector, featureoverlay</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=draw-and-modify-features" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/draw-and-modify-features.js b/mapbox-gl/examples/draw-and-modify-features.js
    new file mode 100644
    index 0000000000..16e474eb18
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-and-modify-features.js
    @@ -0,0 +1,70 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-11000000, 4600000],
    +    zoom: 4
    +  })
    +});
    +
    +// The features are not added to a regular vector layer/source,
    +// but to a feature overlay which holds a collection of features.
    +// This collection is passed to the modify and also the draw
    +// interaction, so that both can add or modify features.
    +var featureOverlay = new ol.FeatureOverlay({
    +  style: new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 255, 0.2)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#ffcc33',
    +      width: 2
    +    }),
    +    image: new ol.style.Circle({
    +      radius: 7,
    +      fill: new ol.style.Fill({
    +        color: '#ffcc33'
    +      })
    +    })
    +  })
    +});
    +featureOverlay.setMap(map);
    +
    +var modify = new ol.interaction.Modify({
    +  features: featureOverlay.getFeatures(),
    +  // the SHIFT key must be pressed to delete vertices, so
    +  // that new vertices can be drawn at the same position
    +  // of existing vertices
    +  deleteCondition: function(event) {
    +    return ol.events.condition.shiftKeyOnly(event) &&
    +        ol.events.condition.singleClick(event);
    +  }
    +});
    +map.addInteraction(modify);
    +
    +var draw; // global so we can remove it later
    +function addInteraction() {
    +  draw = new ol.interaction.Draw({
    +    features: featureOverlay.getFeatures(),
    +    type: /** @type {ol.geom.GeometryType} */ (typeSelect.value)
    +  });
    +  map.addInteraction(draw);
    +}
    +
    +var typeSelect = document.getElementById('type');
    +
    +
    +/**
    + * Let user change the geometry type.
    + * @param {Event} e Change event.
    + */
    +typeSelect.onchange = function(e) {
    +  map.removeInteraction(draw);
    +  addInteraction();
    +};
    +
    +addInteraction();
    diff --git a/mapbox-gl/examples/draw-features-require.js b/mapbox-gl/examples/draw-features-require.js
    new file mode 100644
    index 0000000000..b34a1c2efa
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-features-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.interaction.Draw');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/draw-features.html b/mapbox-gl/examples/draw-features.html
    new file mode 100644
    index 0000000000..0cf553fe94
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-features.html
    @@ -0,0 +1,62 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Draw features example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Draw features example</h4>
    +          <p id="shortdesc">Example of using the ol.interaction.Draw interaction.</p>
    +          <form class="form-inline">
    +            <label>Geometry type &nbsp;</label>
    +              <select id="type">
    +                <option value="None">None</option>
    +                <option value="Point">Point</option>
    +                <option value="LineString">LineString</option>
    +                <option value="Polygon">Polygon</option>
    +                <option value="Circle">Circle</option>
    +              </select>
    +          </form>
    +
    +          <div id="docs">
    +            <p>See the <a href="draw-features.js" target="_blank">draw-features.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">draw, edit, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=draw-features" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/draw-features.js b/mapbox-gl/examples/draw-features.js
    new file mode 100644
    index 0000000000..009f640fb6
    --- /dev/null
    +++ b/mapbox-gl/examples/draw-features.js
    @@ -0,0 +1,60 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var source = new ol.source.Vector();
    +
    +var vector = new ol.layer.Vector({
    +  source: source,
    +  style: new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 255, 0.2)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#ffcc33',
    +      width: 2
    +    }),
    +    image: new ol.style.Circle({
    +      radius: 7,
    +      fill: new ol.style.Fill({
    +        color: '#ffcc33'
    +      })
    +    })
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-11000000, 4600000],
    +    zoom: 4
    +  })
    +});
    +
    +var typeSelect = document.getElementById('type');
    +
    +var draw; // global so we can remove it later
    +function addInteraction() {
    +  var value = typeSelect.value;
    +  if (value !== 'None') {
    +    draw = new ol.interaction.Draw({
    +      source: source,
    +      type: /** @type {ol.geom.GeometryType} */ (value)
    +    });
    +    map.addInteraction(draw);
    +  }
    +}
    +
    +
    +/**
    + * Let user change the geometry type.
    + * @param {Event} e Change event.
    + */
    +typeSelect.onchange = function(e) {
    +  map.removeInteraction(draw);
    +  addInteraction();
    +};
    +
    +addInteraction();
    diff --git a/mapbox-gl/examples/dynamic-data-require.js b/mapbox-gl/examples/dynamic-data-require.js
    new file mode 100644
    index 0000000000..ae6e6a2f80
    --- /dev/null
    +++ b/mapbox-gl/examples/dynamic-data-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/dynamic-data.html b/mapbox-gl/examples/dynamic-data.html
    new file mode 100644
    index 0000000000..b7ea32de74
    --- /dev/null
    +++ b/mapbox-gl/examples/dynamic-data.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Dynamic data example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Dynamic data example</h4>
    +          <p id="shortdesc">Example of dynamic data.</p>
    +          <div id="docs">
    +            <p>See the <a href="dynamic-data.js" target="_blank">dynamic-data.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">dynamic-data</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=dynamic-data" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/dynamic-data.js b/mapbox-gl/examples/dynamic-data.js
    new file mode 100644
    index 0000000000..b9e46490bd
    --- /dev/null
    +++ b/mapbox-gl/examples/dynamic-data.js
    @@ -0,0 +1,66 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var imageStyle = new ol.style.Circle({
    +  radius: 5,
    +  snapToPixel: false,
    +  fill: new ol.style.Fill({color: 'yellow'}),
    +  stroke: new ol.style.Stroke({color: 'red', width: 1})
    +});
    +
    +var headInnerImageStyle = new ol.style.Style({
    +  image: new ol.style.Circle({
    +    radius: 2,
    +    snapToPixel: false,
    +    fill: new ol.style.Fill({color: 'blue'})
    +  })
    +});
    +
    +var headOuterImageStyle = new ol.style.Circle({
    +  radius: 5,
    +  snapToPixel: false,
    +  fill: new ol.style.Fill({color: 'black'})
    +});
    +
    +var n = 200;
    +var omegaTheta = 30000; // Rotation period in ms
    +var R = 7e6;
    +var r = 2e6;
    +var p = 2e6;
    +map.on('postcompose', function(event) {
    +  var vectorContext = event.vectorContext;
    +  var frameState = event.frameState;
    +  var theta = 2 * Math.PI * frameState.time / omegaTheta;
    +  var coordinates = [];
    +  var i;
    +  for (i = 0; i < n; ++i) {
    +    var t = theta + 2 * Math.PI * i / n;
    +    var x = (R + r) * Math.cos(t) + p * Math.cos((R + r) * t / r);
    +    var y = (R + r) * Math.sin(t) + p * Math.sin((R + r) * t / r);
    +    coordinates.push([x, y]);
    +  }
    +  vectorContext.setImageStyle(imageStyle);
    +  vectorContext.drawMultiPointGeometry(
    +      new ol.geom.MultiPoint(coordinates), null);
    +
    +  var headPoint = new ol.geom.Point(coordinates[coordinates.length - 1]);
    +  var headFeature = new ol.Feature(headPoint);
    +  vectorContext.drawFeature(headFeature, headInnerImageStyle);
    +
    +  vectorContext.setImageStyle(headOuterImageStyle);
    +  vectorContext.drawMultiPointGeometry(headPoint, null);
    +
    +  map.render();
    +});
    +map.render();
    diff --git a/mapbox-gl/examples/earthquake-clusters-require.js b/mapbox-gl/examples/earthquake-clusters-require.js
    new file mode 100644
    index 0000000000..cc8623945f
    --- /dev/null
    +++ b/mapbox-gl/examples/earthquake-clusters-require.js
    @@ -0,0 +1,16 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.extent');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.Select');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Cluster');
    +goog.require('ol.source.KML');
    +goog.require('ol.source.Stamen');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.RegularShape');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.style.Text');
    diff --git a/mapbox-gl/examples/earthquake-clusters.html b/mapbox-gl/examples/earthquake-clusters.html
    new file mode 100644
    index 0000000000..d3f630e066
    --- /dev/null
    +++ b/mapbox-gl/examples/earthquake-clusters.html
    @@ -0,0 +1,75 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Earthquake Clusters</title>
    +    <style>
    +      #map {
    +        position: relative;
    +      }
    +      #info {
    +        position: absolute;
    +        height: 1px;
    +        width: 1px;
    +        z-index: 100;
    +      }
    +      .tooltip.in {
    +        opacity: 1;
    +        filter: alpha(opacity=100);
    +      }
    +      .tooltip.top .tooltip-arrow {
    +        border-top-color: white;
    +      }
    +      .tooltip-inner {
    +        border: 2px solid white;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Earthquake Clusters</h4>
    +          <p id="shortdesc">Demonstrates the use of style geometries to render source features of a cluster.</p>
    +          <div id="docs">
    +            <p>
    +              This example parses a KML file and renders the features as clusters on a vector layer.  The styling in this example is quite involved. Single earthquake locations (rendered as stars) have a size relative to their magnitude. Clusters have an opacity relative to the number of features in the cluster, and a size that represents the extent of the features that make up the cluster. When clicking or hovering on a cluster, the individual features that make up the cluster will be shown.
    +            </p>
    +            <p>To achieve this, we make heavy use of style functions and <code>ol.style.Style#geometry</code>. See the <a href="earthquake-clusters.js" target="_blank">earthquake-clusters.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">KML, vector, style, geometry, cluster</div>
    +        </div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=earthquake-clusters" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/earthquake-clusters.js b/mapbox-gl/examples/earthquake-clusters.js
    new file mode 100644
    index 0000000000..75b8b29df5
    --- /dev/null
    +++ b/mapbox-gl/examples/earthquake-clusters.js
    @@ -0,0 +1,136 @@
    +var earthquakeFill = new ol.style.Fill({
    +  color: 'rgba(255, 153, 0, 0.8)'
    +});
    +var earthquakeStroke = new ol.style.Stroke({
    +  color: 'rgba(255, 204, 0, 0.2)',
    +  width: 1
    +});
    +var textFill = new ol.style.Fill({
    +  color: '#fff'
    +});
    +var textStroke = new ol.style.Stroke({
    +  color: 'rgba(0, 0, 0, 0.6)',
    +  width: 3
    +});
    +var invisibleFill = new ol.style.Fill({
    +  color: 'rgba(255, 255, 255, 0.01)'
    +});
    +
    +function createEarthquakeStyle(feature) {
    +  // 2012_Earthquakes_Mag5.kml stores the magnitude of each earthquake in a
    +  // standards-violating <magnitude> tag in each Placemark.  We extract it
    +  // from the Placemark's name instead.
    +  var name = feature.get('name');
    +  var magnitude = parseFloat(name.substr(2));
    +  var radius = 5 + 20 * (magnitude - 5);
    +
    +  return new ol.style.Style({
    +    geometry: feature.getGeometry(),
    +    image: new ol.style.RegularShape({
    +      radius1: radius,
    +      radius2: 3,
    +      points: 5,
    +      angle: Math.PI,
    +      fill: earthquakeFill,
    +      stroke: earthquakeStroke
    +    })
    +  });
    +}
    +
    +var maxFeatureCount;
    +function calculateClusterInfo(resolution) {
    +  maxFeatureCount = 0;
    +  var features = vector.getSource().getFeatures();
    +  var feature, radius;
    +  for (var i = features.length - 1; i >= 0; --i) {
    +    feature = features[i];
    +    var originalFeatures = feature.get('features');
    +    var extent = ol.extent.createEmpty();
    +    for (var j = 0, jj = originalFeatures.length; j < jj; ++j) {
    +      ol.extent.extend(extent, originalFeatures[j].getGeometry().getExtent());
    +    }
    +    maxFeatureCount = Math.max(maxFeatureCount, jj);
    +    radius = 0.25 * (ol.extent.getWidth(extent) + ol.extent.getHeight(extent)) /
    +        resolution;
    +    feature.set('radius', radius);
    +  }
    +}
    +
    +var currentResolution;
    +function styleFunction(feature, resolution) {
    +  if (resolution != currentResolution) {
    +    calculateClusterInfo(resolution);
    +    currentResolution = resolution;
    +  }
    +  var style;
    +  var size = feature.get('features').length;
    +  if (size > 1) {
    +    style = [new ol.style.Style({
    +      image: new ol.style.Circle({
    +        radius: feature.get('radius'),
    +        fill: new ol.style.Fill({
    +          color: [255, 153, 0, Math.min(0.8, 0.4 + (size / maxFeatureCount))]
    +        })
    +      }),
    +      text: new ol.style.Text({
    +        text: size.toString(),
    +        fill: textFill,
    +        stroke: textStroke
    +      })
    +    })];
    +  } else {
    +    var originalFeature = feature.get('features')[0];
    +    style = [createEarthquakeStyle(originalFeature)];
    +  }
    +  return style;
    +}
    +
    +function selectStyleFunction(feature, resolution) {
    +  var styles = [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      radius: feature.get('radius'),
    +      fill: invisibleFill
    +    })
    +  })];
    +  var originalFeatures = feature.get('features');
    +  var originalFeature;
    +  for (var i = originalFeatures.length - 1; i >= 0; --i) {
    +    originalFeature = originalFeatures[i];
    +    styles.push(createEarthquakeStyle(originalFeature));
    +  }
    +  return styles;
    +}
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.Cluster({
    +    distance: 40,
    +    source: new ol.source.KML({
    +      extractStyles: false,
    +      projection: 'EPSG:3857',
    +      url: 'data/kml/2012_Earthquakes_Mag5.kml'
    +    })
    +  }),
    +  style: styleFunction
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.Stamen({
    +    layer: 'toner'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  interactions: ol.interaction.defaults().extend([new ol.interaction.Select({
    +    condition: function(evt) {
    +      return evt.originalEvent.type == 'mousemove' ||
    +          evt.type == 'singleclick';
    +    },
    +    style: selectStyleFunction
    +  })]),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/epsg-4326-require.js b/mapbox-gl/examples/epsg-4326-require.js
    new file mode 100644
    index 0000000000..4ba5710109
    --- /dev/null
    +++ b/mapbox-gl/examples/epsg-4326-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.ScaleLine');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.TileWMS');
    diff --git a/mapbox-gl/examples/epsg-4326.html b/mapbox-gl/examples/epsg-4326.html
    new file mode 100644
    index 0000000000..8fd47ec4e3
    --- /dev/null
    +++ b/mapbox-gl/examples/epsg-4326.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>EPSG:4326 example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">EPSG:4326 example</h4>
    +          <p id="shortdesc">Example of a map in EPSG:4326.</p>
    +          <div id="docs">
    +            <p>See the <a href="epsg-4326.js" target="_blank">epsg-4326.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">epsg4326</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=epsg-4326" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/epsg-4326.js b/mapbox-gl/examples/epsg-4326.js
    new file mode 100644
    index 0000000000..2f92ac6e3d
    --- /dev/null
    +++ b/mapbox-gl/examples/epsg-4326.js
    @@ -0,0 +1,25 @@
    +var layers = [
    +  new ol.layer.Tile({
    +    source: new ol.source.TileWMS({
    +      url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +      params: {
    +        'LAYERS': 'ne:NE1_HR_LC_SR_W_DR'
    +      }
    +    })
    +  })
    +];
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.ScaleLine({
    +      units: 'degrees'
    +    })
    +  ]),
    +  layers: layers,
    +  target: 'map',
    +  view: new ol.View({
    +    projection: 'EPSG:4326',
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/example-list.js b/mapbox-gl/examples/example-list.js
    new file mode 100644
    index 0000000000..3e022c2c3f
    --- /dev/null
    +++ b/mapbox-gl/examples/example-list.js
    @@ -0,0 +1 @@
    +var info = {"examples":[{"link":"accessible.html","example":"accessible.html","title":"Accessibility example","shortdesc":"Example of an accessible map.","tags":"accessibility, tabindex"},{"link":"animation.html","example":"animation.html","title":"Animation example","shortdesc":"Demonstrates animated pan, zoom, and rotation.","tags":"animation"},{"link":"arcgis-tiled.html","example":"arcgis-tiled.html","title":"Tiled ArcGIS MapServer example","shortdesc":"Example of a tiled ArcGIS layer.","tags":"arcgis, tile, tilelayer"},{"link":"attributions.html","example":"attributions.html","title":"Attributions example","shortdesc":"Example of a attributions visibily change on map resize, to collapse them on small maps.","tags":"attributions, openstreetmap"},{"link":"bind-input.html","example":"bind-input.html","title":"Bind HTML input example","shortdesc":"Demonstrates two-way binding of HTML input elements to OpenLayers objects.","tags":"input, bind, openstreetmap"},{"link":"bing-maps.html","example":"bing-maps.html","title":"Bing Maps example","shortdesc":"Example of a Bing Maps layer.","tags":"bing, bing-maps"},{"link":"box-selection.html","example":"box-selection.html","title":"Box selection example","shortdesc":"Using a DragBox interaction to select features.","tags":"DragBox, feature, selection, box"},{"link":"brightness-contrast.html","example":"brightness-contrast.html","title":"Brightness/contrast example","shortdesc":"Example of brightness/contrast control on the client (WebGL only).","tags":"brightness, contrast, webgl"},{"link":"button-title.html","example":"button-title.html","title":"Custom tooltips","shortdesc":"This example shows how to customize the buttons tooltips with Bootstrap .","tags":"custom, tooltip"},{"link":"canvas-tiles.html","example":"canvas-tiles.html","title":"Canvas tiles example","shortdesc":"Renders tiles with coordinates for debugging.","tags":"layers, openstreetmap, canvas"},{"link":"center.html","example":"center.html","title":"Advanced View Positioning example","shortdesc":"This example demonstrates how a map's view can be            adjusted so a geometry or coordinate is positioned at a specific            pixel location. The map above has top, right, bottom, and left            padding applied inside the viewport. The view's fitGeometry method            is used to fit a geometry in the view with the same padding. The            view's centerOn method is used to position a coordinate (Lausanne)            at a specific pixel location (the center of the black box).","tags":"center, rotation, openstreetmap"},{"link":"cluster.html","example":"cluster.html","title":"Clustering example","shortdesc":"Example of using ol.Cluster .","tags":"cluster vector"},{"link":"custom-controls.html","example":"custom-controls.html","title":"Custom controls","shortdesc":"This example shows how to create custom controls.","tags":"custom, control"},{"link":"d3.html","example":"d3.html","title":"d3 integration example","shortdesc":"Example of using ol3 and d3 together.","tags":"d3"},{"link":"device-orientation.html","example":"device-orientation.html","title":"Device orientation example","shortdesc":"Listen to DeviceOrientation events","tags":"orientation, openstreetmap"},{"link":"drag-and-drop-image-vector.html","example":"drag-and-drop-image-vector.html","title":"Drag-and-Drop image vector example","shortdesc":"Example of using the drag-and-drop interaction with a ol.source.ImageVector. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. Each file is rendered to an image on the client.","tags":"drag-and-drop-image-vector, gpx, geojson, igc, kml, topojson, vector, image"},{"link":"drag-and-drop.html","example":"drag-and-drop.html","title":"Drag-and-Drop example","shortdesc":"Example of using the drag-and-drop interaction. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.","tags":"drag-and-drop, gpx, geojson, igc, kml, topojson"},{"link":"drag-features.html","example":"drag-features.html","title":"Drag features example","shortdesc":"Example of a drag features interaction.","tags":"drag, feature, vector, editing"},{"link":"drag-rotate-and-zoom.html","example":"drag-rotate-and-zoom.html","title":"Drag rotate and zoom example","shortdesc":"A single interaction to drag, rotate, and zoom.","tags":"drag, rotate, zoom, interaction"},{"link":"draw-and-modify-features.html","example":"draw-and-modify-features.html","title":"Draw and modify features example","shortdesc":"Example of using the ol.interaction.Draw interaction together with          the ol.interaction.Modify interaction.","tags":"draw, edit, modify, vector, featureoverlay"},{"link":"draw-features.html","example":"draw-features.html","title":"Draw features example","shortdesc":"Example of using the ol.interaction.Draw interaction.","tags":"draw, edit, vector"},{"link":"dynamic-data.html","example":"dynamic-data.html","title":"Dynamic data example","shortdesc":"Example of dynamic data.","tags":"dynamic-data"},{"link":"earthquake-clusters.html","example":"earthquake-clusters.html","title":"Earthquake Clusters","shortdesc":"Demonstrates the use of style geometries to render source features of a cluster.","tags":"KML, vector, style, geometry, cluster"},{"link":"epsg-4326.html","example":"epsg-4326.html","title":"EPSG:4326 example","shortdesc":"Example of a map in EPSG:4326.","tags":"epsg4326"},{"link":"export-map.html","example":"export-map.html","title":"Export map example","shortdesc":"Example of exporting a map as a PNG image.","tags":"export, png, openstreetmap"},{"link":"fractal.html","example":"fractal.html","title":"Fractal Example","shortdesc":"Example of a fractal.","tags":"fractal, vector"},{"link":"full-screen-drag-rotate-and-zoom.html","example":"full-screen-drag-rotate-and-zoom.html","title":"Full screen drag rotate and zoom example","shortdesc":"Example of drag rotate and zoom control with full screen effect.","tags":"full-screen, drag, rotate, zoom, bing, bing-maps"},{"link":"full-screen.html","example":"full-screen.html","title":"Full screen control example","shortdesc":"Example of a full screen control.","tags":"full-screen, bing, bing-maps"},{"link":"geojson.html","example":"geojson.html","title":"GeoJSON example","shortdesc":"Example of GeoJSON features.","tags":"geojson, vector, openstreetmap"},{"link":"geolocation-orientation.html","example":"geolocation-orientation.html","title":"Geolocation tracking with orientation example","shortdesc":"Example of a geolocated and oriented map.","tags":"fullscreen, geolocation, orientation, mobile"},{"link":"geolocation.html","example":"geolocation.html","title":"Geolocation example","shortdesc":"Example of a geolocation map.","tags":"geolocation, openstreetmap"},{"link":"getfeatureinfo-image.html","example":"getfeatureinfo-image.html","title":"GetFeatureInfo example (image layer)","shortdesc":"This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS image layer.","tags":"getfeatureinfo, forEachLayerAtPixel"},{"link":"getfeatureinfo-tile.html","example":"getfeatureinfo-tile.html","title":"WMS GetFeatureInfo example (tile layer)","shortdesc":"This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS tile layer.","tags":"getfeatureinfo, forEachLayerAtPixel"},{"link":"gpx.html","example":"gpx.html","title":"GPX example","shortdesc":"Example of using the GPX source.","tags":"GPX"},{"link":"graticule.html","example":"graticule.html","title":"Graticule example","shortdesc":"This example shows how to add a graticule overlay to a map.","tags":"graticule"},{"link":"heatmap-earthquakes.html","example":"heatmap-earthquakes.html","title":"Earthquakes heatmap","shortdesc":"Demonstrates the use of a heatmap layer.","tags":"heatmap, kml, vector, style"},{"link":"hue-saturation.html","example":"hue-saturation.html","title":"Hue/saturation example","shortdesc":"Example of hue/saturation control on the client (WebGL only).","tags":"hue, saturation, webgl"},{"link":"icon-sprite-webgl.html","example":"icon-sprite-webgl.html","title":"Icon sprite with WebGL example","shortdesc":"Icon sprite with WebGL.","tags":"webgl, icon, sprite, vector, point"},{"link":"icon.html","example":"icon.html","title":"Icon example","shortdesc":"Example using an icon to symbolize a point.","tags":"vector, style, icon, marker, popup"},{"link":"igc.html","example":"igc.html","title":"IGC example","shortdesc":"Example of tracks recorded from multiple paraglider flights on the same day, read from an IGC file.","tags":"complex-geometry, closest-feature, igc, opencyclemap"},{"link":"image-filter.html","example":"image-filter.html","title":"Image filter example","shortdesc":"Apply a filter to imagery","tags":"filter, image manipulation"},{"link":"image-vector-layer.html","example":"image-vector-layer.html","title":"Image vector example","shortdesc":"Example of an image vector layer.","tags":"vector, image"},{"link":"kml-earthquakes.html","example":"kml-earthquakes.html","title":"Earthquakes in KML","shortdesc":"Demonstrates the use of a Shape symbolizer to render earthquake locations.","tags":"KML, vector, style, tooltip"},{"link":"kml-timezones.html","example":"kml-timezones.html","title":"Timezones in KML","shortdesc":"Demonstrates rendering timezones from KML.","tags":"KML, vector, style"},{"link":"kml.html","example":"kml.html","title":"KML example","shortdesc":"Example of using the KML source.","tags":"KML"},{"link":"layer-clipping-webgl.html","example":"layer-clipping-webgl.html","title":"Layer WebGL clipping example","shortdesc":"Layer WebGL clipping example.","tags":"clipping, webgl, openstreetmap"},{"link":"layer-clipping.html","example":"layer-clipping.html","title":"Layer clipping example","shortdesc":"Layer clipping example.","tags":"clipping, openstreetmap"},{"link":"layer-extent.html","example":"layer-extent.html","title":"Limited layer extent","shortdesc":"Restricting layer rendering to a limited extent.","tags":"extent, tilejson"},{"link":"layer-group.html","example":"layer-group.html","title":"Layer group example","shortdesc":"Example of a map with layer group.","tags":"tilejson, input, bind, group, layergroup"},{"link":"layer-spy.html","example":"layer-spy.html","title":"Layer spy example","shortdesc":"View a portion of one layer over another","tags":"spy, image manipulation"},{"link":"layer-swipe.html","example":"layer-swipe.html","title":"Layer Swipe example","shortdesc":"Example of a Layer swipe map.","tags":"swipe, openstreetmap"},{"link":"lazy-source.html","example":"lazy-source.html","title":"Lazy source example","shortdesc":"Example of setting a layer source after construction.","tags":"source"},{"link":"line-arrows.html","example":"line-arrows.html","title":"LineString arrows example","shortdesc":"Example of drawing arrows for each line string segment.","tags":"draw, vector, arrow"},{"link":"localized-openstreetmap.html","example":"localized-openstreetmap.html","title":"Localized OpenStreetMap example","shortdesc":"Example of a localized OpenStreetMap map with a custom tile server and a custom attribution.","tags":"cors, localized-openstreetmap, openseamap, openstreetmap"},{"link":"mapbox-gl.html","example":"mapbox-gl.html","title":"Mapbox-GL vector tiles example","shortdesc":"Example of a map that uses mapbox-gl-js to render Mapbox vector tiles.","tags":"mapbox, webgl, vector tiles"},{"link":"mapguide-untiled.html","example":"mapguide-untiled.html","title":"MapGuide untiled example","shortdesc":"Example of a untiled MapGuide map.","tags":"mapguide"},{"link":"mapquest.html","example":"mapquest.html","title":"MapQuest example","shortdesc":"Example of a MapQuest map.","tags":"mapquest"},{"link":"measure.html","example":"measure.html","title":"Measure example","shortdesc":"Example of using the          ol.interaction.Draw interaction for creating simple          measuring application.","tags":"draw, edit, measure, vector"},{"link":"min-max-resolution.html","example":"min-max-resolution.html","title":"Min/max resolution example","shortdesc":"Show/hide layers depending on current view resolution.","tags":"minResolution, maxResolution, resolution"},{"link":"mobile-full-screen.html","example":"mobile-full-screen.html","title":"Mobile full screen example","shortdesc":"Example of a full screen map.","tags":"fullscreen, bing, geolocation, mobile"},{"link":"modify-features.html","example":"modify-features.html","title":"Modify features example","shortdesc":"Editing features with the modify interaction.","tags":"modify, edit, vector"},{"link":"modify-test.html","example":"modify-test.html","title":"Modify features test","shortdesc":"Example for testing feature modification.","tags":"modify, edit, vector"},{"link":"mouse-position.html","example":"mouse-position.html","title":"Mouse position example","shortdesc":"Example of a mouse position control, outside the map.","tags":"mouse-position, openstreetmap"},{"link":"moveend.html","example":"moveend.html","title":"Move end example","shortdesc":"Use of the moveend event.","tags":"moveend, map, event"},{"link":"navigation-controls.html","example":"navigation-controls.html","title":"Navigation controls example","shortdesc":"Shows how to add navigation controls.","tags":"control, navigation, extent"},{"link":"overlay.html","example":"overlay.html","title":"Overlay example","shortdesc":"Demonstrates overlays.","tags":"overlay, popup, bootstrap, popover, mapquest, openaerial"},{"link":"overviewmap-custom.html","example":"overviewmap-custom.html","title":"OverviewMap control, advanced","shortdesc":"Example of OverviewMap control with advanced customization.","tags":"overview, overviewmap"},{"link":"overviewmap.html","example":"overviewmap.html","title":"OverviewMap control","shortdesc":"Example of OverviewMap control.","tags":"overview, overviewmap"},{"link":"polygon-styles.html","example":"polygon-styles.html","title":"Custom styles for polygons","shortdesc":"Showing the vertices of a polygon with a custom style geometry.","tags":"polygon, vector, style, GeometryFunction"},{"link":"popup.html","example":"popup.html","title":"Popup example","shortdesc":"Uses an overlay to create a popup.","tags":"overlay, popup, mapquest, openaerial"},{"link":"preload.html","example":"preload.html","title":"Preload example","shortdesc":"Example of tile preloading.","tags":"preload, bing"},{"link":"regularshape.html","example":"regularshape.html","title":"Regular Shape example","shortdesc":"Example of some Regular Shape styles.","tags":"vector, symbol, regularshape, style, square, cross, star, triangle, x"},{"link":"rotation.html","example":"rotation.html","title":"Rotation example","shortdesc":"Example of a rotated map.","tags":"rotation, openstreetmap"},{"link":"scale-line.html","example":"scale-line.html","title":"Scale line example","shortdesc":"Example of a scale line.","tags":"scale-line, openstreetmap"},{"link":"select-features.html","example":"select-features.html","title":"Select features example","shortdesc":"Example of using the Select interaction. Choose between Single-click , Click and Hover as the event type for selection in the combobox below. When using Single-click or Click you can hold do Shift key to toggle the feature in the selection.","tags":"select, vector"},{"link":"semi-transparent-layer.html","example":"semi-transparent-layer.html","title":"Semi-transparent layer example","shortdesc":"Example of a map with a semi-transparent layer.","tags":"transparent, mapquest, tilejson"},{"link":"side-by-side.html","example":"side-by-side.html","title":"Side-by-side example","shortdesc":"The three maps, one WebGL, one Canvas, one DOM, share the same center, resolution, rotation and layers.","tags":"side-by-side, canvas, webgl, dom, canvas, sync, object"},{"link":"simple.html","example":"simple.html","title":"Simple example","shortdesc":"Example of a simple map.","tags":"simple, openstreetmap"},{"link":"sphere-mollweide.html","example":"sphere-mollweide.html","title":"Sphere Mollweide example","shortdesc":"Example of a Sphere Mollweide map with a Graticule component.","tags":"graticule, Mollweide, projection, proj4js"},{"link":"stamen.html","example":"stamen.html","title":"Stamen example","shortdesc":"Example of a Stamen tile source.  Two layers are composed: the watercolor base layer with the terrain labels.","tags":"stamen, watercolor, terrain-labels, two-layers"},{"link":"static-image.html","example":"static-image.html","title":"Static image example","shortdesc":"Example of a static image layer.","tags":"static image, xkcd"},{"link":"symbol-atlas-webgl.html","example":"symbol-atlas-webgl.html","title":"Symbols with WebGL example","shortdesc":"Using symbols in an atlas with WebGL.","tags":"webgl, symbol, atlas, vector, point"},{"link":"synthetic-lines.html","example":"synthetic-lines.html","title":"Synthetic lines example","shortdesc":"Synthetic lines example.","tags":"vector"},{"link":"synthetic-points.html","example":"synthetic-points.html","title":"Synthetic points example","shortdesc":"Synthetic points  example.","tags":"vector"},{"link":"teleport.html","example":"teleport.html","title":"Teleport example","shortdesc":"Example of moving a map from one target to another.","tags":"teleport, openstreetmap"},{"link":"tile-load-events.html","example":"tile-load-events.html","title":"Tile load events example","shortdesc":"Example using tile load events.","tags":"tile, events, loading"},{"link":"tile-vector.html","example":"tile-vector.html","title":"Tile vector example","shortdesc":"Example of vector tiles from openstreetmap.us.","tags":"tile-vector, openstreetmap"},{"link":"tilejson.html","example":"tilejson.html","title":"TileJSON example","shortdesc":"Example of a TileJSON layer.","tags":"tilejson"},{"link":"tileutfgrid.html","example":"tileutfgrid.html","title":"TileUTFGrid example","shortdesc":"This example shows how to read data from a TileUTFGrid layer.","tags":"utfgrid, tileutfgrid, tilejson"},{"link":"tissot.html","example":"tissot.html","title":"Tissot indicatrix example","shortdesc":"Example of Tissot indicatrix maps. The map on the left is an EPSG:4326 map. The one on the right is EPSG:3857.","tags":"tissot, circle"},{"link":"topojson.html","example":"topojson.html","title":"TopoJSON example","shortdesc":"Demonstrates rendering of features from a TopoJSON topology.","tags":"vector, topojson, style"},{"link":"vector-labels.html","example":"vector-labels.html","title":"Vector labels example","shortdesc":"Example of GeoJSON features with labels.","tags":"geojson, vector, openstreetmap, label"},{"link":"vector-layer.html","example":"vector-layer.html","title":"Vector layer example","shortdesc":"Example of a countries vector layer with country information on hover and country labels at higher zoom levels.","tags":"vector, geojson, style, feature overlay"},{"link":"vector-osm.html","example":"vector-osm.html","title":"OSM XML example","shortdesc":"Example of using the OSM XML source.  Vector data is loaded dynamically from a server using a tiling strategy.","tags":"vector, osm, xml, loading, server"},{"link":"vector-wfs.html","example":"vector-wfs.html","title":"WFS example","shortdesc":"Example of using WFS with a BBOX strategy.","tags":"vector, WFS, bbox, loading, server"},{"link":"wkt.html","example":"wkt.html","title":"WKT example","shortdesc":"Example of using the WKT parser.","tags":"WKT Well Known Text"},{"link":"wms-capabilities.html","example":"wms-capabilities.html","title":"WMS GetCapabilities parsing example","shortdesc":"Example of parsing a WMS GetCapabilities response.","tags":"wms, capabilities, getcapabilities"},{"link":"wms-custom-proj.html","example":"wms-custom-proj.html","title":"Tiled WMS with custom projection example","shortdesc":"Example of two tiled WMS layers (Pixelmap 1:1'000'000 and national parks) using the projection EPSG:21781.","tags":"wms, tile, tilelayer, projection"},{"link":"wms-image-custom-proj.html","example":"wms-image-custom-proj.html","title":"Single image WMS with Proj4js projection example","shortdesc":"Example of two single image WMS layers.","tags":"wms, single image, proj4js, projection"},{"link":"wms-image.html","example":"wms-image.html","title":"Single image WMS example","shortdesc":"Example of a single image WMS layer.","tags":"wms, image"},{"link":"wms-no-proj.html","example":"wms-no-proj.html","title":"WMS without client projection example","shortdesc":"Example of two WMS layers using the projection EPSG:21781, which is unknown to the client.","tags":"wms, projection"},{"link":"wms-tiled.html","example":"wms-tiled.html","title":"Tiled WMS example","shortdesc":"Example of a tiled WMS layer.","tags":"wms, tile, tilelayer"},{"link":"wmts-capabilities.html","example":"wmts-capabilities.html","title":"WMTS GetCapabilities parsing example","shortdesc":"Example of parsing a WMTS GetCapabilities response.","tags":"wmts, capabilities, getcapabilities"},{"link":"wmts-hidpi.html","example":"wmts-hidpi.html","title":"WMTS HiDPI example","shortdesc":"Example of a WMTS based HiDPI layer.","tags":"hidpi, retina, wmts"},{"link":"wmts-layer-from-capabilities.html","example":"wmts-layer-from-capabilities.html","title":"WMTS Capabilities example","shortdesc":"Example of a WMTS source created from a WMTS capabilities document.","tags":"wmts, capabilities, getcapabilities"},{"link":"wmts.html","example":"wmts.html","title":"WMTS example","shortdesc":"Example of a WMTS source.","tags":"wmts"},{"link":"xyz-esri-4326-512.html","example":"xyz-esri-4326-512.html","title":"XYZ Esri EPSG:4326 tileSize 512 example","shortdesc":"Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.","tags":"xyz, esri, tilesize, custom projection"},{"link":"xyz-esri.html","example":"xyz-esri.html","title":"XYZ Esri example","shortdesc":"Example of a XYZ source using Esri tiles.","tags":"xyz, esri"},{"link":"xyz-retina.html","example":"xyz-retina.html","title":"XYZ with Retina tiles example","shortdesc":"Example of Retina / HiDPI mercator tiles (512x512px) available as XYZ.","tags":"retina, hidpi, xyz, maptiler, @2x, devicePixelRatio"},{"link":"xyz.html","example":"xyz.html","title":"XYZ example","shortdesc":"Example of a XYZ source.","tags":"xyz"},{"link":"zoom-constrained.html","example":"zoom-constrained.html","title":"Zoom constrained example","shortdesc":"Example of a zoom constrained view.","tags":"bing, zoom, minZoom, maxZoom"},{"link":"zoomify.html","example":"zoomify.html","title":"Zoomify example","shortdesc":"Example of a Zoomify source.","tags":"zoomify"},{"link":"zoomslider.html","example":"zoomslider.html","title":"ZoomSlider control","shortdesc":"Example of various ZoomSlider controls.","tags":"zoom, zoomslider, slider, style, styling, css, control"}],"index":{"1":{"97":1},"512":{"106":1},"3857":{"16":1,"89":1},"4326":{"16":1,"23":1,"89":1,"106":1},"21781":{"97":1,"100":1},"example":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"44":1,"45":1,"46":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":1,"64":1,"65":1,"66":1,"67":1,"69":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"81":1,"82":1,"83":1,"84":1,"85":1,"86":1,"87":1,"88":1,"89":1,"90":1,"91":1,"92":1,"93":1,"94":1,"95":1,"96":1,"97":1,"98":1,"99":1,"100":1,"101":1,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"108":1,"109":1,"110":1,"111":1,"112":1},"of":{"0":1,"2":1,"3":1,"4":1,"5":1,"7":1,"10":1,"11":1,"13":1,"15":1,"16":1,"17":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"33":1,"35":1,"36":1,"39":1,"41":1,"42":1,"44":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"59":1,"62":1,"63":1,"66":1,"67":1,"68":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"77":1,"78":1,"79":1,"80":1,"84":1,"86":1,"87":1,"89":1,"90":1,"91":1,"92":1,"93":1,"94":1,"95":1,"96":1,"97":1,"98":1,"99":1,"100":1,"101":1,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"108":1,"109":1,"110":1,"111":1,"112":1},"an":{"0":1,"15":1,"38":1,"39":1,"41":1,"69":1,"81":1,"89":1},"accessible":{"0":1},"map":{"0":1,"3":1,"10":1,"15":1,"16":1,"23":1,"24":1,"29":1,"30":1,"34":1,"48":1,"50":1,"53":1,"54":1,"55":1,"56":1,"59":1,"62":1,"63":1,"72":1,"75":1,"77":1,"78":1,"84":1,"89":1},"accessibility":{"0":1},"tabindex":{"0":1},"demonstrates":{"1":1,"4":1,"10":1,"22":1,"35":1,"42":1,"43":1,"65":1,"90":1},"animated":{"1":1},"pan":{"1":1},"zoom":{"1":1,"18":1,"26":1,"92":1,"110":1,"112":1},"and":{"1":1,"10":1,"13":1,"15":1,"16":1,"18":1,"19":1,"26":1,"29":1,"53":1,"74":1,"76":1,"92":1,"97":1},"rotation":{"1":1,"10":1,"72":1,"76":1},"animation":{"1":1},"a":{"2":1,"3":1,"5":1,"6":1,"10":1,"15":1,"17":1,"18":1,"22":1,"23":1,"24":1,"25":1,"27":1,"29":1,"30":1,"31":1,"32":1,"34":1,"35":1,"38":1,"40":1,"42":1,"47":1,"48":1,"49":1,"50":1,"51":1,"53":1,"54":1,"55":1,"56":1,"59":1,"62":1,"68":1,"69":1,"72":1,"73":1,"75":1,"77":1,"78":1,"79":1,"80":1,"84":1,"87":1,"88":1,"90":1,"92":1,"93":1,"94":1,"96":1,"99":1,"101":1,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"109":1,"110":1,"111":1},"tiled":{"2":1,"97":1,"101":1},"arcgis":{"2":1},"layer":{"2":1,"5":1,"31":1,"32":1,"35":1,"41":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"75":1,"79":1,"80":1,"87":1,"88":1,"92":1,"99":1,"101":1,"103":1},"mapserver":{"2":1},"tile":{"2":1,"32":1,"53":1,"70":1,"79":1,"85":1,"86":1,"97":1,"101":1},"tilelayer":{"2":1,"97":1,"101":1},"attributions":{"3":1},"visibily":{"3":1},"change":{"3":1},"on":{"3":1,"7":1,"15":1,"16":1,"31":1,"32":1,"36":1,"39":1,"58":1,"89":1,"92":1},"resize":{"3":1},"to":{"3":1,"4":1,"6":1,"8":1,"10":1,"12":1,"14":1,"15":1,"16":1,"18":1,"22":1,"31":1,"32":1,"34":1,"38":1,"40":1,"42":1,"47":1,"54":1,"64":1,"69":1,"74":1,"84":1,"88":1,"100":1},"collapse":{"3":1},"them":{"3":1},"small":{"3":1},"maps":{"3":1,"5":1,"26":1,"27":1,"76":1,"89":1},"openstreetmap":{"3":1,"4":1,"9":1,"10":1,"14":1,"24":1,"28":1,"30":1,"45":1,"46":1,"50":1,"53":1,"62":1,"72":1,"73":1,"77":1,"84":1,"86":1,"91":1},"two":{"4":1,"79":1,"97":1,"98":1,"100":1},"way":{"4":1},"binding":{"4":1},"html":{"4":1},"input":{"4":1,"48":1},"elements":{"4":1},"openlayers":{"4":1},"objects":{"4":1},"bind":{"4":1,"48":1},"bing":{"5":1,"26":1,"27":1,"59":1,"70":1,"110":1},"using":{"6":1,"11":1,"13":1,"15":1,"16":1,"19":1,"20":1,"33":1,"38":1,"44":1,"57":1,"74":1,"81":1,"85":1,"93":1,"94":1,"95":1,"97":1,"100":1,"106":1,"107":1},"dragbox":{"6":1},"interaction":{"6":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"57":1,"60":1,"74":1},"select":{"6":1,"74":1},"features":{"6":1,"17":1,"19":1,"20":1,"22":1,"28":1,"60":1,"61":1,"74":1,"90":1,"91":1},"box":{"6":1,"10":1},"selection":{"6":1,"74":1},"feature":{"6":1,"17":1,"39":1,"61":1,"74":1,"92":1},"brightness":{"7":1},"contrast":{"7":1},"control":{"7":1,"12":1,"26":1,"27":1,"36":1,"62":1,"64":1,"66":1,"67":1,"112":1},"the":{"7":1,"8":1,"10":1,"15":1,"16":1,"19":1,"20":1,"22":1,"33":1,"35":1,"36":1,"39":1,"42":1,"44":1,"57":1,"60":1,"62":1,"63":1,"68":1,"74":1,"76":1,"79":1,"89":1,"93":1,"95":1,"97":1,"100":1},"client":{"7":1,"15":1,"36":1,"100":1},"webgl":{"7":1,"36":1,"37":1,"45":1,"54":1,"76":1,"81":1},"only":{"7":1,"16":1,"36":1},"this":{"8":1,"10":1,"12":1,"16":1,"31":1,"32":1,"34":1,"88":1},"shows":{"8":1,"12":1,"31":1,"32":1,"34":1,"64":1,"88":1},"how":{"8":1,"10":1,"12":1,"31":1,"32":1,"34":1,"64":1,"88":1},"customize":{"8":1},"buttons":{"8":1},"tooltips":{"8":1},"with":{"8":1,"9":1,"10":1,"15":1,"16":1,"19":1,"26":1,"29":1,"37":1,"48":1,"53":1,"60":1,"66":1,"68":1,"75":1,"78":1,"79":1,"81":1,"91":1,"92":1,"94":1,"97":1,"98":1,"108":1},"bootstrap":{"8":1,"65":1},"custom":{"8":1,"12":1,"53":1,"68":1,"97":1,"106":1},"tooltip":{"8":1,"42":1},"renders":{"9":1},"tiles":{"9":1,"54":1,"86":1,"106":1,"107":1,"108":1},"coordinates":{"9":1},"for":{"9":1,"31":1,"32":1,"52":1,"57":1,"61":1,"68":1,"74":1},"debugging":{"9":1},"canvas":{"9":1,"76":1},"layers":{"9":1,"58":1,"76":1,"79":1,"97":1,"98":1,"100":1},"s":{"10":1},"view":{"10":1,"49":1,"58":1,"110":1},"can":{"10":1,"74":1},"be":{"10":1},"adjusted":{"10":1},"so":{"10":1,"16":1},"geometry":{"10":1,"22":1,"39":1,"68":1},"or":{"10":1,"15":1,"16":1,"74":1},"coordinate":{"10":1},"is":{"10":1,"15":1,"16":1,"89":1,"93":1,"100":1},"positioned":{"10":1},"at":{"10":1,"92":1},"specific":{"10":1},"pixel":{"10":1},"location":{"10":1},"above":{"10":1},"has":{"10":1},"top":{"10":1},"right":{"10":1,"89":1},"bottom":{"10":1},"left":{"10":1,"89":1},"padding":{"10":1},"applied":{"10":1},"inside":{"10":1},"viewport":{"10":1},"fitgeometry":{"10":1},"method":{"10":1},"used":{"10":1},"fit":{"10":1},"in":{"10":1,"16":1,"23":1,"42":1,"43":1,"74":1,"81":1,"106":1},"same":{"10":1,"39":1,"76":1},"centeron":{"10":1},"position":{"10":1,"62":1},"lausanne":{"10":1},"center":{"10":1,"76":1},"black":{"10":1},"advanced":{"10":1,"66":1},"positioning":{"10":1},"ol":{"11":1,"15":1,"19":1,"20":1,"57":1},"cluster":{"11":1,"22":1},"clustering":{"11":1},"vector":{"11":1,"15":1,"17":1,"19":1,"20":1,"22":1,"25":1,"28":1,"35":1,"37":1,"38":1,"41":1,"42":1,"43":1,"52":1,"54":1,"57":1,"60":1,"61":1,"68":1,"71":1,"74":1,"81":1,"82":1,"83":1,"86":1,"90":1,"91":1,"92":1,"93":1,"94":1},"create":{"12":1,"69":1},"controls":{"12":1,"64":1,"112":1},"ol3":{"13":1},"d3":{"13":1},"together":{"13":1,"19":1},"integration":{"13":1},"listen":{"14":1},"deviceorientation":{"14":1},"events":{"14":1,"85":1},"device":{"14":1},"orientation":{"14":1,"29":1},"drag":{"15":1,"16":1,"17":1,"18":1,"26":1},"drop":{"15":1,"16":1},"source":{"15":1,"22":1,"33":1,"44":1,"51":1,"79":1,"93":1,"104":1,"105":1,"106":1,"107":1,"109":1,"111":1},"imagevector":{"15":1},"gpx":{"15":1,"16":1,"33":1},"geojson":{"15":1,"16":1,"28":1,"91":1,"92":1},"igc":{"15":1,"16":1,"39":1},"kml":{"15":1,"16":1,"22":1,"35":1,"42":1,"43":1,"44":1},"topojson":{"15":1,"16":1,"90":1},"files":{"15":1,"16":1},"each":{"15":1,"52":1},"file":{"15":1,"39":1},"rendered":{"15":1},"image":{"15":1,"24":1,"31":1,"40":1,"41":1,"49":1,"80":1,"98":1,"99":1},"there":{"16":1},"no":{"16":1},"projection":{"16":1,"78":1,"97":1,"98":1,"100":1,"106":1},"transform":{"16":1},"support":{"16":1},"will":{"16":1},"work":{"16":1},"data":{"16":1,"21":1,"88":1,"93":1},"epsg":{"16":1,"23":1,"89":1,"97":1,"100":1,"106":1},"editing":{"17":1,"60":1},"single":{"18":1,"74":1,"98":1,"99":1},"rotate":{"18":1,"26":1},"draw":{"19":1,"20":1,"52":1,"57":1},"modify":{"19":1,"60":1,"61":1},"edit":{"19":1,"20":1,"57":1,"60":1,"61":1},"featureoverlay":{"19":1},"dynamic":{"21":1},"use":{"22":1,"35":1,"42":1,"63":1},"style":{"22":1,"35":1,"38":1,"42":1,"43":1,"68":1,"71":1,"90":1,"92":1,"112":1},"geometries":{"22":1},"render":{"22":1,"42":1,"54":1},"earthquake":{"22":1,"42":1},"clusters":{"22":1},"epsg4326":{"23":1},"exporting":{"24":1},"as":{"24":1,"74":1,"108":1},"png":{"24":1},"export":{"24":1},"fractal":{"25":1},"full":{"26":1,"27":1,"59":1},"screen":{"26":1,"27":1,"59":1},"effect":{"26":1},"geolocated":{"29":1},"oriented":{"29":1},"geolocation":{"29":1,"30":1,"59":1},"tracking":{"29":1},"fullscreen":{"29":1,"59":1},"mobile":{"29":1,"59":1},"trigger":{"31":1,"32":1},"wms":{"31":1,"32":1,"96":1,"97":1,"98":1,"99":1,"100":1,"101":1},"getfeatureinfo":{"31":1,"32":1},"requests":{"31":1,"32":1},"click":{"31":1,"32":1,"74":1},"foreachlayeratpixel":{"31":1,"32":1},"add":{"34":1,"64":1},"graticule":{"34":1,"78":1},"overlay":{"34":1,"65":1,"69":1,"92":1},"heatmap":{"35":1},"earthquakes":{"35":1,"42":1},"hue":{"36":1},"saturation":{"36":1},"icon":{"37":1,"38":1},"sprite":{"37":1},"point":{"37":1,"38":1,"81":1},"symbolize":{"38":1},"marker":{"38":1},"popup":{"38":1,"65":1,"69":1},"tracks":{"39":1},"recorded":{"39":1},"from":{"39":1,"43":1,"84":1,"86":1,"88":1,"90":1,"93":1,"104":1},"multiple":{"39":1},"paraglider":{"39":1},"flights":{"39":1},"day":{"39":1},"read":{"39":1,"88":1},"complex":{"39":1},"closest":{"39":1},"opencyclemap":{"39":1},"apply":{"40":1},"filter":{"40":1},"imagery":{"40":1},"manipulation":{"40":1,"49":1},"shape":{"42":1,"71":1},"symbolizer":{"42":1},"locations":{"42":1},"rendering":{"43":1,"47":1,"90":1},"timezones":{"43":1},"clipping":{"45":1,"46":1},"restricting":{"47":1},"limited":{"47":1},"extent":{"47":1,"64":1},"tilejson":{"47":1,"48":1,"75":1,"87":1,"88":1},"group":{"48":1},"layergroup":{"48":1},"portion":{"49":1},"one":{"49":1,"76":1,"84":1,"89":1},"over":{"49":1},"another":{"49":1,"84":1},"spy":{"49":1},"swipe":{"50":1},"setting":{"51":1},"after":{"51":1},"construction":{"51":1},"lazy":{"51":1},"drawing":{"52":1},"arrows":{"52":1},"line":{"52":1,"73":1},"string":{"52":1},"segment":{"52":1},"linestring":{"52":1},"arrow":{"52":1},"localized":{"53":1},"server":{"53":1,"93":1,"94":1},"attribution":{"53":1},"cors":{"53":1},"openseamap":{"53":1},"that":{"54":1},"uses":{"54":1,"69":1},"mapbox":{"54":1},"gl":{"54":1},"js":{"54":1},"untiled":{"55":1},"mapguide":{"55":1},"mapquest":{"56":1,"65":1,"69":1,"75":1},"creating":{"57":1},"simple":{"57":1,"77":1},"measuring":{"57":1},"application":{"57":1},"measure":{"57":1},"show":{"58":1},"hide":{"58":1},"depending":{"58":1},"current":{"58":1},"resolution":{"58":1,"76":1},"min":{"58":1},"max":{"58":1},"minresolution":{"58":1},"maxresolution":{"58":1},"testing":{"61":1},"modification":{"61":1},"test":{"61":1},"mouse":{"62":1},"outside":{"62":1},"moveend":{"63":1},"event":{"63":1,"74":1},"move":{"63":1},"end":{"63":1},"navigation":{"64":1},"overlays":{"65":1},"popover":{"65":1},"openaerial":{"65":1,"69":1},"overviewmap":{"66":1,"67":1},"customization":{"66":1},"overview":{"66":1,"67":1},"showing":{"68":1},"vertices":{"68":1},"polygon":{"68":1},"styles":{"68":1,"71":1},"polygons":{"68":1},"geometryfunction":{"68":1},"preloading":{"70":1},"preload":{"70":1},"some":{"71":1},"regular":{"71":1},"symbol":{"71":1,"81":1},"regularshape":{"71":1},"square":{"71":1},"cross":{"71":1},"star":{"71":1},"triangle":{"71":1},"x":{"71":1},"rotated":{"72":1},"scale":{"73":1},"choose":{"74":1},"between":{"74":1},"hover":{"74":1,"92":1},"type":{"74":1},"combobox":{"74":1},"below":{"74":1},"when":{"74":1},"you":{"74":1},"hold":{"74":1},"do":{"74":1},"shift":{"74":1},"key":{"74":1},"toggle":{"74":1},"semi":{"75":1},"transparent":{"75":1},"three":{"76":1},"dom":{"76":1},"share":{"76":1},"side":{"76":1},"by":{"76":1},"sync":{"76":1},"object":{"76":1},"sphere":{"78":1},"mollweide":{"78":1},"component":{"78":1},"proj4js":{"78":1,"98":1},"stamen":{"79":1},"are":{"79":1},"composed":{"79":1},"watercolor":{"79":1},"base":{"79":1},"terrain":{"79":1},"labels":{"79":1,"91":1,"92":1},"static":{"80":1},"xkcd":{"80":1},"symbols":{"81":1},"atlas":{"81":1},"synthetic":{"82":1,"83":1},"lines":{"82":1},"points":{"83":1},"moving":{"84":1},"target":{"84":1},"teleport":{"84":1},"load":{"85":1},"loading":{"85":1,"93":1,"94":1},"us":{"86":1},"tileutfgrid":{"88":1},"utfgrid":{"88":1},"tissot":{"89":1},"indicatrix":{"89":1},"circle":{"89":1},"topology":{"90":1},"label":{"91":1},"countries":{"92":1},"country":{"92":1},"information":{"92":1},"higher":{"92":1},"levels":{"92":1},"osm":{"93":1},"xml":{"93":1},"loaded":{"93":1},"dynamically":{"93":1},"tiling":{"93":1},"strategy":{"93":1,"94":1},"wfs":{"94":1},"bbox":{"94":1},"wkt":{"95":1},"parser":{"95":1},"well":{"95":1},"known":{"95":1},"text":{"95":1},"parsing":{"96":1,"102":1},"getcapabilities":{"96":1,"102":1,"104":1},"response":{"96":1,"102":1},"capabilities":{"96":1,"102":1,"104":1},"pixelmap":{"97":1},"000":{"97":1},"national":{"97":1},"parks":{"97":1},"which":{"100":1},"unknown":{"100":1},"without":{"100":1},"wmts":{"102":1,"103":1,"104":1,"105":1},"based":{"103":1},"hidpi":{"103":1,"108":1},"retina":{"103":1,"108":1},"created":{"104":1},"document":{"104":1},"xyz":{"106":1,"107":1,"108":1,"109":1},"esri":{"106":1,"107":1},"512x512":{"106":1},"tilesize":{"106":1},"mercator":{"108":1},"512x512px":{"108":1},"available":{"108":1},"maptiler":{"108":1},"2x":{"108":1},"devicepixelratio":{"108":1},"constrained":{"110":1},"minzoom":{"110":1},"maxzoom":{"110":1},"zoomify":{"111":1},"various":{"112":1},"zoomslider":{"112":1},"slider":{"112":1},"styling":{"112":1},"css":{"112":1}}}
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/example-list.xml b/mapbox-gl/examples/example-list.xml
    new file mode 100644
    index 0000000000..c3059431a3
    --- /dev/null
    +++ b/mapbox-gl/examples/example-list.xml
    @@ -0,0 +1 @@
    +<?xml version="1.0" ?><feed xmlns="http://www.w3.org/2005/Atom"><title>OpenLayers Examples</title><id>http://openlayers.github.io/ol3/master/examples/example-list.xml#2015-03-03T04:22:40Z</id><updated>2015-03-03T04:22:40Z</updated><entry><title>Mapbox-GL vector tiles example</title><tags>mapbox, webgl, vector tiles</tags><link href="http://openlayers.github.io/ol3/master/examples/mapbox-gl.html"/><summary>Example of a map that uses &lt;a href=&quot;https://github.com/mapbox/mapbox-gl-js&quot;&gt;mapbox-gl-js&lt;/a&gt; to render Mapbox vector tiles.</summary><updated>2015-03-03T04:22:27Z</updated><author><name>anonymous</name></author><id>http://openlayers.github.io/ol3/master/examples/mapbox-gl.html#2015-03-03T04:22:27Z</id></entry><entry><title>Select features example</title><tags>select, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/select-features.html"/><summary>Example of using the Select interaction. Choose between &lt;code&gt;Single-click&lt;/code&gt;, &lt;code&gt;Click&lt;/code&gt; and &lt;code&gt;Hover&lt;/code&gt; as the event type for selection in the combobox below. When using &lt;code&gt;Single-click&lt;/code&gt; or &lt;code&gt;Click&lt;/code&gt; you can hold do &lt;code&gt;Shift&lt;/code&gt; key to toggle the feature in the selection.</summary><updated>2015-02-27T12:14:13-0500</updated><author><name>Alexandre Dubé</name></author><id>http://openlayers.github.io/ol3/master/examples/select-features.html#2015-02-27T12:14:13-0500</id></entry><entry><title>Earthquakes heatmap</title><tags>heatmap, kml, vector, style</tags><link href="http://openlayers.github.io/ol3/master/examples/heatmap-earthquakes.html"/><summary>Demonstrates the use of a heatmap layer.</summary><updated>2015-02-24T17:10:15+0100</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/heatmap-earthquakes.html#2015-02-24T17:10:15+0100</id></entry><entry><title>Tile load events example</title><tags>tile, events, loading</tags><link href="http://openlayers.github.io/ol3/master/examples/tile-load-events.html"/><summary>Example using tile load events.</summary><updated>2015-02-17T13:12:00-0700</updated><author><name>Tim Schaub</name></author><id>http://openlayers.github.io/ol3/master/examples/tile-load-events.html#2015-02-17T13:12:00-0700</id></entry><entry><title>Vector labels example</title><tags>geojson, vector, openstreetmap, label</tags><link href="http://openlayers.github.io/ol3/master/examples/vector-labels.html"/><summary>Example of GeoJSON features with labels.</summary><updated>2015-02-12T17:45:34+0100</updated><author><name>Stefan Weil</name></author><id>http://openlayers.github.io/ol3/master/examples/vector-labels.html#2015-02-12T17:45:34+0100</id></entry><entry><title>Measure example</title><tags>draw, edit, measure, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/measure.html"/><summary>Example of using the          ol.interaction.Draw interaction for creating simple          measuring application.</summary><updated>2015-02-10T13:01:22+0100</updated><author><name>Bart van den Eijnden</name></author><id>http://openlayers.github.io/ol3/master/examples/measure.html#2015-02-10T13:01:22+0100</id></entry><entry><title>WMTS GetCapabilities parsing example</title><tags>wmts, capabilities, getcapabilities</tags><link href="http://openlayers.github.io/ol3/master/examples/wmts-capabilities.html"/><summary>Example of parsing a WMTS GetCapabilities response.</summary><updated>2015-02-03T14:43:49+0100</updated><author><name>Hadrien Tulipe</name></author><id>http://openlayers.github.io/ol3/master/examples/wmts-capabilities.html#2015-02-03T14:43:49+0100</id></entry><entry><title>Custom styles for polygons</title><tags>polygon, vector, style, GeometryFunction</tags><link href="http://openlayers.github.io/ol3/master/examples/polygon-styles.html"/><summary>Showing the vertices of a polygon with a custom style geometry.</summary><updated>2015-01-27T10:02:28+0100</updated><author><name>tsauerwein</name></author><id>http://openlayers.github.io/ol3/master/examples/polygon-styles.html#2015-01-27T10:02:28+0100</id></entry><entry><title>GetFeatureInfo example (image layer)</title><tags>getfeatureinfo, forEachLayerAtPixel</tags><link href="http://openlayers.github.io/ol3/master/examples/getfeatureinfo-image.html"/><summary>This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS image layer.</summary><updated>2015-01-26T11:20:59+0100</updated><author><name>tsauerwein</name></author><id>http://openlayers.github.io/ol3/master/examples/getfeatureinfo-image.html#2015-01-26T11:20:59+0100</id></entry><entry><title>WMS GetFeatureInfo example (tile layer)</title><tags>getfeatureinfo, forEachLayerAtPixel</tags><link href="http://openlayers.github.io/ol3/master/examples/getfeatureinfo-tile.html"/><summary>This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS tile layer.</summary><updated>2015-01-26T11:20:59+0100</updated><author><name>tsauerwein</name></author><id>http://openlayers.github.io/ol3/master/examples/getfeatureinfo-tile.html#2015-01-26T11:20:59+0100</id></entry><entry><title>WMTS Capabilities example</title><tags>wmts, capabilities, getcapabilities</tags><link href="http://openlayers.github.io/ol3/master/examples/wmts-layer-from-capabilities.html"/><summary>Example of a WMTS source created from a WMTS capabilities document.</summary><updated>2015-01-16T09:25:27+1300</updated><author><name>Sara Metz</name></author><id>http://openlayers.github.io/ol3/master/examples/wmts-layer-from-capabilities.html#2015-01-16T09:25:27+1300</id></entry><entry><title>Popup example</title><tags>overlay, popup, mapquest, openaerial</tags><link href="http://openlayers.github.io/ol3/master/examples/popup.html"/><summary>Uses an overlay to create a popup.</summary><updated>2015-01-05T14:45:31+0100</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/popup.html#2015-01-05T14:45:31+0100</id></entry><entry><title>Icon sprite with WebGL example</title><tags>webgl, icon, sprite, vector, point</tags><link href="http://openlayers.github.io/ol3/master/examples/icon-sprite-webgl.html"/><summary>Icon sprite with WebGL.</summary><updated>2014-12-19T14:51:17+0100</updated><author><name>tsauerwein</name></author><id>http://openlayers.github.io/ol3/master/examples/icon-sprite-webgl.html#2014-12-19T14:51:17+0100</id></entry><entry><title>Drag features example</title><tags>drag, feature, vector, editing</tags><link href="http://openlayers.github.io/ol3/master/examples/drag-features.html"/><summary>Example of a drag features interaction.</summary><updated>2014-12-15T17:31:41+0100</updated><author><name>Éric Lemoine</name></author><id>http://openlayers.github.io/ol3/master/examples/drag-features.html#2014-12-15T17:31:41+0100</id></entry><entry><title>Earthquake Clusters</title><tags>KML, vector, style, geometry, cluster</tags><link href="http://openlayers.github.io/ol3/master/examples/earthquake-clusters.html"/><summary>Demonstrates the use of style geometries to render source features of a cluster.</summary><updated>2014-12-09T20:07:53+0100</updated><author><name>Andreas Hocevar</name></author><id>http://openlayers.github.io/ol3/master/examples/earthquake-clusters.html#2014-12-09T20:07:53+0100</id></entry><entry><title>TileUTFGrid example</title><tags>utfgrid, tileutfgrid, tilejson</tags><link href="http://openlayers.github.io/ol3/master/examples/tileutfgrid.html"/><summary>This example shows how to read data from a TileUTFGrid layer.</summary><updated>2014-12-06T15:07:52+0100</updated><author><name>Petr Sloup</name></author><id>http://openlayers.github.io/ol3/master/examples/tileutfgrid.html#2014-12-06T15:07:52+0100</id></entry><entry><title>Symbols with WebGL example</title><tags>webgl, symbol, atlas, vector, point</tags><link href="http://openlayers.github.io/ol3/master/examples/symbol-atlas-webgl.html"/><summary>Using symbols in an atlas with WebGL.</summary><updated>2014-12-04T10:21:32+0100</updated><author><name>tsauerwein</name></author><id>http://openlayers.github.io/ol3/master/examples/symbol-atlas-webgl.html#2014-12-04T10:21:32+0100</id></entry><entry><title>Regular Shape example</title><tags>vector, symbol, regularshape, style, square, cross, star, triangle, x</tags><link href="http://openlayers.github.io/ol3/master/examples/regularshape.html"/><summary>Example of some Regular Shape styles.</summary><updated>2014-11-27T13:33:33+0100</updated><author><name>Bart van den Eijnden</name></author><id>http://openlayers.github.io/ol3/master/examples/regularshape.html#2014-11-27T13:33:33+0100</id></entry><entry><title>Limited layer extent</title><tags>extent, tilejson</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-extent.html"/><summary>Restricting layer rendering to a limited extent.</summary><updated>2014-11-11T09:00:20-0800</updated><author><name>Tim Schaub</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-extent.html#2014-11-11T09:00:20-0800</id></entry><entry><title>Mobile full screen example</title><tags>fullscreen, bing, geolocation, mobile</tags><link href="http://openlayers.github.io/ol3/master/examples/mobile-full-screen.html"/><summary>Example of a full screen map.</summary><updated>2014-11-04T09:44:55+0100</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/mobile-full-screen.html#2014-11-04T09:44:55+0100</id></entry><entry><title>Custom controls</title><tags>custom, control</tags><link href="http://openlayers.github.io/ol3/master/examples/custom-controls.html"/><summary>This example shows how to create custom controls.</summary><updated>2014-10-30T12:37:01+0100</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/custom-controls.html#2014-10-30T12:37:01+0100</id></entry><entry><title>Custom tooltips</title><tags>custom, tooltip</tags><link href="http://openlayers.github.io/ol3/master/examples/button-title.html"/><summary>This example shows how to customize the buttons tooltips with            &lt;a href=&quot;http://getbootstrap.com/javascript/#tooltips&quot;&gt;Bootstrap&lt;/a&gt;.</summary><updated>2014-10-29T16:12:02+0100</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/button-title.html#2014-10-29T16:12:02+0100</id></entry><entry><title>Lazy source example</title><tags>source</tags><link href="http://openlayers.github.io/ol3/master/examples/lazy-source.html"/><summary>Example of setting a layer source after construction.</summary><updated>2014-10-23T09:24:03-0600</updated><author><name>Tim Schaub</name></author><id>http://openlayers.github.io/ol3/master/examples/lazy-source.html#2014-10-23T09:24:03-0600</id></entry><entry><title>ZoomSlider control</title><tags>zoom, zoomslider, slider, style, styling, css, control</tags><link href="http://openlayers.github.io/ol3/master/examples/zoomslider.html"/><summary>Example of various ZoomSlider controls.</summary><updated>2014-10-13T10:13:50-0600</updated><author><name>Tim Schaub</name></author><id>http://openlayers.github.io/ol3/master/examples/zoomslider.html#2014-10-13T10:13:50-0600</id></entry><entry><title>OverviewMap control, advanced</title><tags>overview, overviewmap</tags><link href="http://openlayers.github.io/ol3/master/examples/overviewmap-custom.html"/><summary>Example of OverviewMap control with advanced customization.</summary><updated>2014-10-09T10:10:47-0400</updated><author><name>Alexandre Dubé</name></author><id>http://openlayers.github.io/ol3/master/examples/overviewmap-custom.html#2014-10-09T10:10:47-0400</id></entry><entry><title>OverviewMap control</title><tags>overview, overviewmap</tags><link href="http://openlayers.github.io/ol3/master/examples/overviewmap.html"/><summary>Example of OverviewMap control.</summary><updated>2014-10-09T10:10:27-0400</updated><author><name>Alexandre Dubé</name></author><id>http://openlayers.github.io/ol3/master/examples/overviewmap.html#2014-10-09T10:10:27-0400</id></entry><entry><title>XYZ Esri EPSG:4326 tileSize 512 example</title><tags>xyz, esri, tilesize, custom projection</tags><link href="http://openlayers.github.io/ol3/master/examples/xyz-esri-4326-512.html"/><summary>Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.</summary><updated>2014-10-08T14:27:21+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/xyz-esri-4326-512.html#2014-10-08T14:27:21+0200</id></entry><entry><title>Earthquakes in KML</title><tags>KML, vector, style, tooltip</tags><link href="http://openlayers.github.io/ol3/master/examples/kml-earthquakes.html"/><summary>Demonstrates the use of a Shape symbolizer to render earthquake locations.</summary><updated>2014-10-07T15:58:28+0200</updated><author><name>Frédéric Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/kml-earthquakes.html#2014-10-07T15:58:28+0200</id></entry><entry><title>Static image example</title><tags>static image, xkcd</tags><link href="http://openlayers.github.io/ol3/master/examples/static-image.html"/><summary>Example of a static image layer.</summary><updated>2014-10-05T08:45:19-0600</updated><author><name>Tim Schaub</name></author><id>http://openlayers.github.io/ol3/master/examples/static-image.html#2014-10-05T08:45:19-0600</id></entry><entry><title>Accessibility example</title><tags>accessibility, tabindex</tags><link href="http://openlayers.github.io/ol3/master/examples/accessible.html"/><summary>Example of an accessible map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/accessible.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Animation example</title><tags>animation</tags><link href="http://openlayers.github.io/ol3/master/examples/animation.html"/><summary>Demonstrates animated pan, zoom, and rotation.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/animation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Attributions example</title><tags>attributions, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/attributions.html"/><summary>Example of a attributions visibily change on map resize, to collapse them on small maps.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/attributions.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Bind HTML input example</title><tags>input, bind, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/bind-input.html"/><summary>Demonstrates two-way binding of HTML input elements to OpenLayers objects.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/bind-input.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Bing Maps example</title><tags>bing, bing-maps</tags><link href="http://openlayers.github.io/ol3/master/examples/bing-maps.html"/><summary>Example of a Bing Maps layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/bing-maps.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Box selection example</title><tags>DragBox, feature, selection, box</tags><link href="http://openlayers.github.io/ol3/master/examples/box-selection.html"/><summary>Using a DragBox interaction to select features.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/box-selection.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Brightness/contrast example</title><tags>brightness, contrast, webgl</tags><link href="http://openlayers.github.io/ol3/master/examples/brightness-contrast.html"/><summary>Example of brightness/contrast control on the client (WebGL only).</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/brightness-contrast.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Canvas tiles example</title><tags>layers, openstreetmap, canvas</tags><link href="http://openlayers.github.io/ol3/master/examples/canvas-tiles.html"/><summary>Renders tiles with coordinates for debugging.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/canvas-tiles.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Advanced View Positioning example</title><tags>center, rotation, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/center.html"/><summary>This example demonstrates how a map's view can be            adjusted so a geometry or coordinate is positioned at a specific            pixel location. The map above has top, right, bottom, and left            padding applied inside the viewport. The view's &lt;code&gt;fitGeometry&lt;/code&gt; method            is used to fit a geometry in the view with the same padding. The            view's &lt;code&gt;centerOn&lt;/code&gt; method is used to position a coordinate (Lausanne)            at a specific pixel location (the center of the black box).</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/center.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Clustering example</title><tags>cluster vector</tags><link href="http://openlayers.github.io/ol3/master/examples/cluster.html"/><summary>Example of using &lt;code&gt;ol.Cluster&lt;/code&gt;.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/cluster.html#2014-10-01T09:59:20+0200</id></entry><entry><title>d3 integration example</title><tags>d3</tags><link href="http://openlayers.github.io/ol3/master/examples/d3.html"/><summary>Example of using ol3 and d3 together.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/d3.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Device orientation example</title><tags>orientation, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/device-orientation.html"/><summary>Listen to DeviceOrientation events</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/device-orientation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Drag-and-Drop image vector example</title><tags>drag-and-drop-image-vector, gpx, geojson, igc, kml, topojson, vector, image</tags><link href="http://openlayers.github.io/ol3/master/examples/drag-and-drop-image-vector.html"/><summary>Example of using the drag-and-drop interaction with a ol.source.ImageVector. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. Each file is rendered to an image on the client.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/drag-and-drop-image-vector.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Drag-and-Drop example</title><tags>drag-and-drop, gpx, geojson, igc, kml, topojson</tags><link href="http://openlayers.github.io/ol3/master/examples/drag-and-drop.html"/><summary>Example of using the drag-and-drop interaction. Drag and drop GPX, GeoJSON, IGC, KML, or TopoJSON files on to the map. There is no projection transform support, so this will only work with data in EPSG:4326 and EPSG:3857.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/drag-and-drop.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Drag rotate and zoom example</title><tags>drag, rotate, zoom, interaction</tags><link href="http://openlayers.github.io/ol3/master/examples/drag-rotate-and-zoom.html"/><summary>A single interaction to drag, rotate, and zoom.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/drag-rotate-and-zoom.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Draw and modify features example</title><tags>draw, edit, modify, vector, featureoverlay</tags><link href="http://openlayers.github.io/ol3/master/examples/draw-and-modify-features.html"/><summary>Example of using the ol.interaction.Draw interaction together with          the ol.interaction.Modify interaction.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/draw-and-modify-features.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Draw features example</title><tags>draw, edit, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/draw-features.html"/><summary>Example of using the ol.interaction.Draw interaction.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/draw-features.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Dynamic data example</title><tags>dynamic-data</tags><link href="http://openlayers.github.io/ol3/master/examples/dynamic-data.html"/><summary>Example of dynamic data.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/dynamic-data.html#2014-10-01T09:59:20+0200</id></entry><entry><title>EPSG:4326 example</title><tags>epsg4326</tags><link href="http://openlayers.github.io/ol3/master/examples/epsg-4326.html"/><summary>Example of a map in EPSG:4326.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/epsg-4326.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Export map example</title><tags>export, png, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/export-map.html"/><summary>Example of exporting a map as a PNG image.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/export-map.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Fractal Example</title><tags>fractal, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/fractal.html"/><summary>Example of a fractal.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/fractal.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Full screen drag rotate and zoom example</title><tags>full-screen, drag, rotate, zoom, bing, bing-maps</tags><link href="http://openlayers.github.io/ol3/master/examples/full-screen-drag-rotate-and-zoom.html"/><summary>Example of drag rotate and zoom control with full screen effect.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/full-screen-drag-rotate-and-zoom.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Full screen control example</title><tags>full-screen, bing, bing-maps</tags><link href="http://openlayers.github.io/ol3/master/examples/full-screen.html"/><summary>Example of a full screen control.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/full-screen.html#2014-10-01T09:59:20+0200</id></entry><entry><title>GeoJSON example</title><tags>geojson, vector, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/geojson.html"/><summary>Example of GeoJSON features.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/geojson.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Geolocation tracking with orientation example</title><tags>fullscreen, geolocation, orientation, mobile</tags><link href="http://openlayers.github.io/ol3/master/examples/geolocation-orientation.html"/><summary>Example of a geolocated and oriented map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/geolocation-orientation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Geolocation example</title><tags>geolocation, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/geolocation.html"/><summary>Example of a geolocation map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/geolocation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>GPX example</title><tags>GPX</tags><link href="http://openlayers.github.io/ol3/master/examples/gpx.html"/><summary>Example of using the GPX source.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/gpx.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Graticule example</title><tags>graticule</tags><link href="http://openlayers.github.io/ol3/master/examples/graticule.html"/><summary>This example shows how to add a graticule overlay to a map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/graticule.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Hue/saturation example</title><tags>hue, saturation, webgl</tags><link href="http://openlayers.github.io/ol3/master/examples/hue-saturation.html"/><summary>Example of hue/saturation control on the client (WebGL only).</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/hue-saturation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Icon example</title><tags>vector, style, icon, marker, popup</tags><link href="http://openlayers.github.io/ol3/master/examples/icon.html"/><summary>Example using an icon to symbolize a point.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/icon.html#2014-10-01T09:59:20+0200</id></entry><entry><title>IGC example</title><tags>complex-geometry, closest-feature, igc, opencyclemap</tags><link href="http://openlayers.github.io/ol3/master/examples/igc.html"/><summary>Example of tracks recorded from multiple paraglider flights on the same day, read from an IGC file.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/igc.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Image filter example</title><tags>filter, image manipulation</tags><link href="http://openlayers.github.io/ol3/master/examples/image-filter.html"/><summary>Apply a filter to imagery</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/image-filter.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Image vector example</title><tags>vector, image</tags><link href="http://openlayers.github.io/ol3/master/examples/image-vector-layer.html"/><summary>Example of an image vector layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/image-vector-layer.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Timezones in KML</title><tags>KML, vector, style</tags><link href="http://openlayers.github.io/ol3/master/examples/kml-timezones.html"/><summary>Demonstrates rendering timezones from KML.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/kml-timezones.html#2014-10-01T09:59:20+0200</id></entry><entry><title>KML example</title><tags>KML</tags><link href="http://openlayers.github.io/ol3/master/examples/kml.html"/><summary>Example of using the KML source.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/kml.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Layer WebGL clipping example</title><tags>clipping, webgl, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-clipping-webgl.html"/><summary>Layer WebGL clipping example.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-clipping-webgl.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Layer clipping example</title><tags>clipping, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-clipping.html"/><summary>Layer clipping example.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-clipping.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Layer group example</title><tags>tilejson, input, bind, group, layergroup</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-group.html"/><summary>Example of a map with layer group.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-group.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Layer spy example</title><tags>spy, image manipulation</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-spy.html"/><summary>View a portion of one layer over another</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-spy.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Layer Swipe example</title><tags>swipe, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/layer-swipe.html"/><summary>Example of a Layer swipe map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/layer-swipe.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Localized OpenStreetMap example</title><tags>cors, localized-openstreetmap, openseamap, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/localized-openstreetmap.html"/><summary>Example of a localized OpenStreetMap map with a custom tile server and a custom attribution.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/localized-openstreetmap.html#2014-10-01T09:59:20+0200</id></entry><entry><title>MapGuide untiled example</title><tags>mapguide</tags><link href="http://openlayers.github.io/ol3/master/examples/mapguide-untiled.html"/><summary>Example of a untiled MapGuide map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/mapguide-untiled.html#2014-10-01T09:59:20+0200</id></entry><entry><title>MapQuest example</title><tags>mapquest</tags><link href="http://openlayers.github.io/ol3/master/examples/mapquest.html"/><summary>Example of a MapQuest map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/mapquest.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Min/max resolution example</title><tags>minResolution, maxResolution, resolution</tags><link href="http://openlayers.github.io/ol3/master/examples/min-max-resolution.html"/><summary>Show/hide layers depending on current view resolution.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/min-max-resolution.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Modify features example</title><tags>modify, edit, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/modify-features.html"/><summary>Editing features with the modify interaction.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/modify-features.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Modify features test</title><tags>modify, edit, vector</tags><link href="http://openlayers.github.io/ol3/master/examples/modify-test.html"/><summary>Example for testing feature modification.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/modify-test.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Mouse position example</title><tags>mouse-position, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/mouse-position.html"/><summary>Example of a mouse position control, outside the map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/mouse-position.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Move end example</title><tags>moveend, map, event</tags><link href="http://openlayers.github.io/ol3/master/examples/moveend.html"/><summary>Use of the moveend event.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/moveend.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Navigation controls example</title><tags>control, navigation, extent</tags><link href="http://openlayers.github.io/ol3/master/examples/navigation-controls.html"/><summary>Shows how to add navigation controls.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/navigation-controls.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Overlay example</title><tags>overlay, popup, bootstrap, popover, mapquest, openaerial</tags><link href="http://openlayers.github.io/ol3/master/examples/overlay.html"/><summary>Demonstrates overlays.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/overlay.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Preload example</title><tags>preload, bing</tags><link href="http://openlayers.github.io/ol3/master/examples/preload.html"/><summary>Example of tile preloading.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/preload.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Rotation example</title><tags>rotation, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/rotation.html"/><summary>Example of a rotated map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/rotation.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Scale line example</title><tags>scale-line, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/scale-line.html"/><summary>Example of a scale line.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/scale-line.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Semi-transparent layer example</title><tags>transparent, mapquest, tilejson</tags><link href="http://openlayers.github.io/ol3/master/examples/semi-transparent-layer.html"/><summary>Example of a map with a semi-transparent layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/semi-transparent-layer.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Side-by-side example</title><tags>side-by-side, canvas, webgl, dom, canvas, sync, object</tags><link href="http://openlayers.github.io/ol3/master/examples/side-by-side.html"/><summary>The three maps, one WebGL, one Canvas, one DOM, share the same center, resolution, rotation and layers.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/side-by-side.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Simple example</title><tags>simple, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/simple.html"/><summary>Example of a simple map.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/simple.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Sphere Mollweide example</title><tags>graticule, Mollweide, projection, proj4js</tags><link href="http://openlayers.github.io/ol3/master/examples/sphere-mollweide.html"/><summary>Example of a Sphere Mollweide map with a Graticule component.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/sphere-mollweide.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Stamen example</title><tags>stamen, watercolor, terrain-labels, two-layers</tags><link href="http://openlayers.github.io/ol3/master/examples/stamen.html"/><summary>Example of a Stamen tile source.  Two layers are composed: the watercolor base layer with the terrain labels.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/stamen.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Synthetic lines example</title><tags>vector</tags><link href="http://openlayers.github.io/ol3/master/examples/synthetic-lines.html"/><summary>Synthetic lines example.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/synthetic-lines.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Synthetic points example</title><tags>vector</tags><link href="http://openlayers.github.io/ol3/master/examples/synthetic-points.html"/><summary>Synthetic points  example.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/synthetic-points.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Teleport example</title><tags>teleport, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/teleport.html"/><summary>Example of moving a map from one target to another.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/teleport.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Tile vector example</title><tags>tile-vector, openstreetmap</tags><link href="http://openlayers.github.io/ol3/master/examples/tile-vector.html"/><summary>Example of vector tiles from openstreetmap.us.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/tile-vector.html#2014-10-01T09:59:20+0200</id></entry><entry><title>TileJSON example</title><tags>tilejson</tags><link href="http://openlayers.github.io/ol3/master/examples/tilejson.html"/><summary>Example of a TileJSON layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/tilejson.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Tissot indicatrix example</title><tags>tissot, circle</tags><link href="http://openlayers.github.io/ol3/master/examples/tissot.html"/><summary>Example of &lt;a href=&quot;http://en.wikipedia.org/wiki/Tissot's_indicatrix&quot;&gt;Tissot indicatrix&lt;/a&gt; maps. The map on the left is an EPSG:4326 map. The one on the left is EPSG:3857.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/tissot.html#2014-10-01T09:59:20+0200</id></entry><entry><title>TopoJSON example</title><tags>vector, topojson, style</tags><link href="http://openlayers.github.io/ol3/master/examples/topojson.html"/><summary>Demonstrates rendering of features from a TopoJSON topology.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/topojson.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Vector layer example</title><tags>vector, geojson, style, feature overlay</tags><link href="http://openlayers.github.io/ol3/master/examples/vector-layer.html"/><summary>Example of a countries vector layer with country information on hover and country labels at higher zoom levels.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/vector-layer.html#2014-10-01T09:59:20+0200</id></entry><entry><title>OSM XML example</title><tags>vector, osm, xml, loading, server</tags><link href="http://openlayers.github.io/ol3/master/examples/vector-osm.html"/><summary>Example of using the OSM XML source.  Vector data is loaded dynamically from a server using a tiling strategy.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/vector-osm.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WFS example</title><tags>vector, WFS, bbox, loading, server</tags><link href="http://openlayers.github.io/ol3/master/examples/vector-wfs.html"/><summary>Example of using WFS with a BBOX strategy.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/vector-wfs.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WKT example</title><tags>WKT Well Known Text</tags><link href="http://openlayers.github.io/ol3/master/examples/wkt.html"/><summary>Example of using the WKT parser.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wkt.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WMS GetCapabilities parsing example</title><tags>wms, capabilities, getcapabilities</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-capabilities.html"/><summary>Example of parsing a WMS GetCapabilities response.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-capabilities.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Tiled WMS with custom projection example</title><tags>wms, tile, tilelayer, projection</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-custom-proj.html"/><summary>Example of two tiled WMS layers (Pixelmap 1:1'000'000 and national parks) using the projection EPSG:21781.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-custom-proj.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Single image WMS with Proj4js projection example</title><tags>wms, single image, proj4js, projection</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-image-custom-proj.html"/><summary>Example of two single image WMS layers.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-image-custom-proj.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Single image WMS example</title><tags>wms, image</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-image.html"/><summary>Example of a single image WMS layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-image.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WMS without client projection example</title><tags>wms, projection</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-no-proj.html"/><summary>Example of two WMS layers using the projection EPSG:21781, which is unknown to the client.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-no-proj.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Tiled WMS example</title><tags>wms, tile, tilelayer</tags><link href="http://openlayers.github.io/ol3/master/examples/wms-tiled.html"/><summary>Example of a tiled WMS layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wms-tiled.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WMTS HiDPI example</title><tags>hidpi, retina, wmts</tags><link href="http://openlayers.github.io/ol3/master/examples/wmts-hidpi.html"/><summary>Example of a WMTS based HiDPI layer.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wmts-hidpi.html#2014-10-01T09:59:20+0200</id></entry><entry><title>WMTS example</title><tags>wmts</tags><link href="http://openlayers.github.io/ol3/master/examples/wmts.html"/><summary>Example of a WMTS source.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/wmts.html#2014-10-01T09:59:20+0200</id></entry><entry><title>XYZ Esri example</title><tags>xyz, esri</tags><link href="http://openlayers.github.io/ol3/master/examples/xyz-esri.html"/><summary>Example of a XYZ source using Esri tiles.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/xyz-esri.html#2014-10-01T09:59:20+0200</id></entry><entry><title>XYZ with Retina tiles example</title><tags>retina, hidpi, xyz, maptiler, @2x, devicePixelRatio</tags><link href="http://openlayers.github.io/ol3/master/examples/xyz-retina.html"/><summary>Example of Retina / HiDPI mercator tiles (512x512px) available as XYZ.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/xyz-retina.html#2014-10-01T09:59:20+0200</id></entry><entry><title>XYZ example</title><tags>xyz</tags><link href="http://openlayers.github.io/ol3/master/examples/xyz.html"/><summary>Example of a XYZ source.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/xyz.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Zoom constrained example</title><tags>bing, zoom, minZoom, maxZoom</tags><link href="http://openlayers.github.io/ol3/master/examples/zoom-constrained.html"/><summary>Example of a zoom constrained view.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/zoom-constrained.html#2014-10-01T09:59:20+0200</id></entry><entry><title>Zoomify example</title><tags>zoomify</tags><link href="http://openlayers.github.io/ol3/master/examples/zoomify.html"/><summary>Example of a Zoomify source.</summary><updated>2014-10-01T09:59:20+0200</updated><author><name>Frederic Junod</name></author><id>http://openlayers.github.io/ol3/master/examples/zoomify.html#2014-10-01T09:59:20+0200</id></entry></feed>
    \ No newline at end of file
    diff --git a/mapbox-gl/examples/export-map-require.js b/mapbox-gl/examples/export-map-require.js
    new file mode 100644
    index 0000000000..cc153d196e
    --- /dev/null
    +++ b/mapbox-gl/examples/export-map-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/export-map.html b/mapbox-gl/examples/export-map.html
    new file mode 100644
    index 0000000000..a0766335d2
    --- /dev/null
    +++ b/mapbox-gl/examples/export-map.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Export map example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <div id="no-download" class="alert alert-error" style="display: none">
    +            This example requires a browser that supports the
    +            <a href="http://caniuse.com/#feat=download">link download</a> attribute.
    +          </div>
    +          <a id="export-png" class="btn" download="map.png"><i class="icon-download"></i> Export PNG</a>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Export map example</h4>
    +          <p id="shortdesc">Example of exporting a map as a PNG image.</p>
    +          <div id="docs">
    +            <p>See the <a href="export-map.js" target="_blank">export-map.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">export, png, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=export-map" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/export-map.js b/mapbox-gl/examples/export-map.js
    new file mode 100644
    index 0000000000..71e507f6c6
    --- /dev/null
    +++ b/mapbox-gl/examples/export-map.js
    @@ -0,0 +1,41 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    new ol.layer.Vector({
    +      source: new ol.source.GeoJSON({
    +        projection: 'EPSG:3857',
    +        url: 'data/geojson/countries.geojson'
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var exportPNGElement = document.getElementById('export-png');
    +
    +if ('download' in exportPNGElement) {
    +  exportPNGElement.addEventListener('click', function(e) {
    +    map.once('postcompose', function(event) {
    +      var canvas = event.context.canvas;
    +      exportPNGElement.href = canvas.toDataURL('image/png');
    +    });
    +    map.renderSync();
    +  }, false);
    +} else {
    +  var info = document.getElementById('no-download');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +}
    diff --git a/mapbox-gl/examples/fractal-require.js b/mapbox-gl/examples/fractal-require.js
    new file mode 100644
    index 0000000000..a25e353ed3
    --- /dev/null
    +++ b/mapbox-gl/examples/fractal-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    diff --git a/mapbox-gl/examples/fractal.html b/mapbox-gl/examples/fractal.html
    new file mode 100644
    index 0000000000..4904d81d94
    --- /dev/null
    +++ b/mapbox-gl/examples/fractal.html
    @@ -0,0 +1,64 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Fractal Example</title>
    +    <style>
    +      .map {
    +        background: whitesmoke;
    +      }
    +      #depth {
    +        width: 100px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>        
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Fractal Example</h4>
    +          <p id="shortdesc">Example of a fractal.</p>
    +          <label for="depth">
    +            depth:&nbsp;
    +            <input id="depth" type="range" min="0" max="9" step="1" value="5">
    +            &nbsp;(<span id="count">#</span> points)
    +          </label>
    +          <div id="docs">
    +            <p>See the <a href="fractal.js" target="_blank">fractal.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">fractal, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=fractal" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/fractal.js b/mapbox-gl/examples/fractal.js
    new file mode 100644
    index 0000000000..3e0440713b
    --- /dev/null
    +++ b/mapbox-gl/examples/fractal.js
    @@ -0,0 +1,117 @@
    +var radius = 10e6;
    +var cos30 = Math.cos(Math.PI / 6);
    +var sin30 = Math.sin(Math.PI / 6);
    +var rise = radius * sin30;
    +var run = radius * cos30;
    +
    +var triangle = new ol.geom.LineString([
    +  [0, radius], [run, -rise], [-run, -rise], [0, radius]
    +]);
    +
    +var feature = new ol.Feature(triangle);
    +
    +var layer = new ol.layer.Vector({
    +  source: new ol.source.Vector({
    +    features: [feature]
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [layer],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    +
    +function makeFractal(depth) {
    +  var geometry = /** @type {ol.geom.LineString} */ (triangle.clone());
    +  var graph = coordsToGraph(geometry.getCoordinates());
    +  for (var i = 0; i < depth; ++i) {
    +    var node = graph;
    +    while (node.next) {
    +      var next = node.next;
    +      injectNodes(node);
    +      node = next;
    +    }
    +  }
    +  var coordinates = graphToCoords(graph);
    +  document.getElementById('count').innerHTML = coordinates.length;
    +  geometry.setCoordinates(coordinates);
    +  feature.setGeometry(geometry);
    +}
    +
    +function injectNodes(startNode) {
    +  var endNode = startNode.next;
    +
    +  var start = startNode.point;
    +  var end = startNode.next.point;
    +  var dx = end[0] - start[0];
    +  var dy = end[1] - start[1];
    +
    +  // first point at 1/3 along the segment
    +  var firstNode = {
    +    point: [start[0] + dx / 3, start[1] + dy / 3]
    +  };
    +
    +  // second point at peak of _/\_
    +  var r = Math.sqrt(dx * dx + dy * dy) / (2 * cos30);
    +  var a = Math.atan2(dy, dx) + Math.PI / 6;
    +  var secondNode = {
    +    point: [start[0] + r * Math.cos(a), start[1] + r * Math.sin(a)]
    +  };
    +
    +  // third point at 2/3 along the segment
    +  var thirdNode = {
    +    point: [end[0] - dx / 3, end[1] - dy / 3]
    +  };
    +
    +  startNode.next = firstNode;
    +  firstNode.next = secondNode;
    +  secondNode.next = thirdNode;
    +  thirdNode.next = endNode;
    +}
    +
    +
    +function coordsToGraph(coordinates) {
    +  var graph = {
    +    point: coordinates[0]
    +  };
    +  var length = coordinates.length;
    +  for (var level = 0, node = graph; level < length - 1; ++level) {
    +    node.next = {
    +      point: coordinates[level + 1]
    +    };
    +    node = node.next;
    +  }
    +  return graph;
    +}
    +
    +function graphToCoords(graph) {
    +  var coordinates = [graph.point];
    +  for (var node = graph, i = 1; node.next; node = node.next, ++i) {
    +    coordinates[i] = node.next.point;
    +  }
    +  return coordinates;
    +}
    +
    +var depthInput = document.getElementById('depth');
    +
    +function update() {
    +  makeFractal(Number(depthInput.value));
    +}
    +
    +var updateTimer;
    +
    +
    +/**
    + * Regenerate fractal on depth change.  Change events are debounced so updates
    + * only occur every 200ms.
    + */
    +depthInput.onchange = function() {
    +  window.clearTimeout(updateTimer);
    +  updateTimer = window.setTimeout(update, 200);
    +};
    +
    +update();
    diff --git a/mapbox-gl/examples/full-screen-drag-rotate-and-zoom-require.js b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom-require.js
    new file mode 100644
    index 0000000000..aed8f5dad7
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.FullScreen');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.DragRotateAndZoom');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.html b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.html
    new file mode 100644
    index 0000000000..698181dbf0
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.html
    @@ -0,0 +1,64 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style>
    +      .map:-moz-full-screen {
    +        height: 100%;
    +      }
    +      .map:-webkit-full-screen {
    +        height: 100%;
    +      }
    +      .map:full-screen {
    +        height: 100%;
    +      }
    +    </style>
    +    <title>Full screen drag rotate and zoom example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Full screen drag rotate and zoom example</h4>
    +          <p id="shortdesc">Example of drag rotate and zoom control with full screen effect.</p>
    +          <div id="docs">
    +            <p>Hold down <code>Shift</code> + drag to rotate and zoom.  Click the button in the top right corner to go full screen.  Then do the <code>Shift</code> + drag thing again.</p>
    +            <p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p>
    +            <p>See the <a href="full-screen-drag-rotate-and-zoom.js" target="_blank">full-screen-drag-rotate-and-zoom.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">full-screen, drag, rotate, zoom, bing, bing-maps</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=full-screen-drag-rotate-and-zoom" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.js b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.js
    new file mode 100644
    index 0000000000..585ce715ff
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen-drag-rotate-and-zoom.js
    @@ -0,0 +1,23 @@
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.FullScreen()
    +  ]),
    +  interactions: ol.interaction.defaults().extend([
    +    new ol.interaction.DragRotateAndZoom()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'Aerial'
    +      })
    +    })
    +  ],
    +  // Use the canvas renderer because it's currently the fastest
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-33519607, 5616436],
    +    rotation: -Math.PI / 8,
    +    zoom: 8
    +  })
    +});
    diff --git a/mapbox-gl/examples/full-screen-require.js b/mapbox-gl/examples/full-screen-require.js
    new file mode 100644
    index 0000000000..778f8b5f8e
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.FullScreen');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/full-screen.html b/mapbox-gl/examples/full-screen.html
    new file mode 100644
    index 0000000000..da9b8e6258
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen.html
    @@ -0,0 +1,70 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style>
    +      .map:-moz-full-screen {
    +        height: 100%;
    +      }
    +      .map:-webkit-full-screen {
    +        height: 100%;
    +      }
    +      .map:-ms-fullscreen {
    +        height: 100%;
    +      }
    +      .map:full-screen {
    +        height: 100%;
    +      }
    +      .ol-rotate {
    +        top: 3em;
    +      }
    +    </style>
    +    <title>Full screen control example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Full screen control example</h4>
    +          <p id="shortdesc">Example of a full screen control.</p>
    +          <div id="docs">
    +            <p>Click the control in the top right corner to go full screen.  Click it again to exit full screen.</p>
    +            <p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p>
    +            <p>See the <a href="full-screen.js" target="_blank">full-screen.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">full-screen, bing, bing-maps</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=full-screen" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/full-screen.js b/mapbox-gl/examples/full-screen.js
    new file mode 100644
    index 0000000000..1b25a5f15a
    --- /dev/null
    +++ b/mapbox-gl/examples/full-screen.js
    @@ -0,0 +1,21 @@
    +var view = new ol.View({
    +  center: [-9101767, 2822912],
    +  zoom: 14
    +});
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.FullScreen()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'Aerial'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: view
    +});
    diff --git a/mapbox-gl/examples/geojson-require.js b/mapbox-gl/examples/geojson-require.js
    new file mode 100644
    index 0000000000..107f029d2c
    --- /dev/null
    +++ b/mapbox-gl/examples/geojson-require.js
    @@ -0,0 +1,13 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.geom.Circle');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/geojson.html b/mapbox-gl/examples/geojson.html
    new file mode 100644
    index 0000000000..54cc237742
    --- /dev/null
    +++ b/mapbox-gl/examples/geojson.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>GeoJSON example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">GeoJSON example</h4>
    +          <p id="shortdesc">Example of GeoJSON features.</p>
    +          <div id="docs">
    +            <p>See the <a href="geojson.js" target="_blank">geojson.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">geojson, vector, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=geojson" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/geojson.js b/mapbox-gl/examples/geojson.js
    new file mode 100644
    index 0000000000..2fda726e9e
    --- /dev/null
    +++ b/mapbox-gl/examples/geojson.js
    @@ -0,0 +1,186 @@
    +var image = new ol.style.Circle({
    +  radius: 5,
    +  fill: null,
    +  stroke: new ol.style.Stroke({color: 'red', width: 1})
    +});
    +
    +var styles = {
    +  'Point': [new ol.style.Style({
    +    image: image
    +  })],
    +  'LineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'green',
    +      width: 1
    +    })
    +  })],
    +  'MultiLineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'green',
    +      width: 1
    +    })
    +  })],
    +  'MultiPoint': [new ol.style.Style({
    +    image: image
    +  })],
    +  'MultiPolygon': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'yellow',
    +      width: 1
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 0, 0.1)'
    +    })
    +  })],
    +  'Polygon': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'blue',
    +      lineDash: [4],
    +      width: 3
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0, 0, 255, 0.1)'
    +    })
    +  })],
    +  'GeometryCollection': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'magenta',
    +      width: 2
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'magenta'
    +    }),
    +    image: new ol.style.Circle({
    +      radius: 10,
    +      fill: null,
    +      stroke: new ol.style.Stroke({
    +        color: 'magenta'
    +      })
    +    })
    +  })],
    +  'Circle': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'red',
    +      width: 2
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255,0,0,0.2)'
    +    })
    +  })]
    +};
    +
    +var styleFunction = function(feature, resolution) {
    +  return styles[feature.getGeometry().getType()];
    +};
    +
    +var vectorSource = new ol.source.GeoJSON(
    +    /** @type {olx.source.GeoJSONOptions} */ ({
    +      object: {
    +        'type': 'FeatureCollection',
    +        'crs': {
    +          'type': 'name',
    +          'properties': {
    +            'name': 'EPSG:3857'
    +          }
    +        },
    +        'features': [
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'Point',
    +              'coordinates': [0, 0]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'LineString',
    +              'coordinates': [[4e6, -2e6], [8e6, 2e6]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'LineString',
    +              'coordinates': [[4e6, 2e6], [8e6, -2e6]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'Polygon',
    +              'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6]]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'MultiLineString',
    +              'coordinates': [
    +                [[-1e6, -7.5e5], [-1e6, 7.5e5]],
    +                [[1e6, -7.5e5], [1e6, 7.5e5]],
    +                [[-7.5e5, -1e6], [7.5e5, -1e6]],
    +                [[-7.5e5, 1e6], [7.5e5, 1e6]]
    +              ]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'MultiPolygon',
    +              'coordinates': [
    +                [[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6], [-3e6, 6e6]]],
    +                [[[-2e6, 6e6], [-2e6, 8e6], [0, 8e6], [0, 6e6]]],
    +                [[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6], [3e6, 6e6]]]
    +              ]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'GeometryCollection',
    +              'geometries': [
    +                {
    +                  'type': 'LineString',
    +                  'coordinates': [[-5e6, -5e6], [0, -5e6]]
    +                },
    +                {
    +                  'type': 'Point',
    +                  'coordinates': [4e6, -5e6]
    +                },
    +                {
    +                  'type': 'Polygon',
    +                  'coordinates': [[[1e6, -6e6], [2e6, -4e6], [3e6, -6e6]]]
    +                }
    +              ]
    +            }
    +          }
    +        ]
    +      }
    +    }));
    +
    +vectorSource.addFeature(new ol.Feature(new ol.geom.Circle([5e6, 7e6], 1e6)));
    +
    +var vectorLayer = new ol.layer.Vector({
    +  source: vectorSource,
    +  style: styleFunction
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    vectorLayer
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/geolocation-orientation-require.js b/mapbox-gl/examples/geolocation-orientation-require.js
    new file mode 100644
    index 0000000000..dda7199a4a
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation-orientation-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Geolocation');
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/geolocation-orientation.html b/mapbox-gl/examples/geolocation-orientation.html
    new file mode 100644
    index 0000000000..5dd4fe3f61
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation-orientation.html
    @@ -0,0 +1,53 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Mobile Geolocation Tracking with Orientation</title>
    +    <style type="text/css">
    +      html, body, .map {
    +        margin: 0;
    +        padding: 0;
    +        width: 100%;
    +        height: 100%;
    +      }
    +      #info {
    +          position: absolute;
    +          font-size: 0.7em;
    +          top: 10px;
    +          right: 10px;
    +          background-color: lightgrey;
    +          padding: 4px;
    +      }
    +      .button {
    +          position: absolute;
    +          bottom: 40px;
    +          left: 10px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +    <div id="map" class="map"></div>
    +    <div id="info"></div>
    +    <img id="geolocation_marker" src="data/geolocation_marker.png" />
    +    <div class="button">
    +        <button id="geolocate">Geolocate Me!</button>
    +        <button id="simulate">Simulate</button>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=geolocation-orientation" type="text/javascript"></script>
    +
    +    <div style="display: none;">
    +      <div id="title">Geolocation tracking with orientation example</div>
    +      <div id="shortdesc">Example of a geolocated and oriented map.</div>
    +      <div id="tags">fullscreen, geolocation, orientation, mobile</div>
    +    </div>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/geolocation-orientation.js b/mapbox-gl/examples/geolocation-orientation.js
    new file mode 100644
    index 0000000000..c6caf42106
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation-orientation.js
    @@ -0,0 +1,219 @@
    +// creating the view
    +var view = new ol.View({
    +  center: ol.proj.transform([5.8713, 45.6452], 'EPSG:4326', 'EPSG:3857'),
    +  zoom: 19
    +});
    +
    +// creating the map
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +// Geolocation marker
    +var markerEl = document.getElementById('geolocation_marker');
    +var marker = new ol.Overlay({
    +  positioning: 'center-center',
    +  element: markerEl,
    +  stopEvent: false
    +});
    +map.addOverlay(marker);
    +
    +// LineString to store the different geolocation positions. This LineString
    +// is time aware.
    +// The Z dimension is actually used to store the rotation (heading).
    +var positions = new ol.geom.LineString([],
    +    /** @type {ol.geom.GeometryLayout} */ ('XYZM'));
    +
    +// Geolocation Control
    +var geolocation = new ol.Geolocation(/** @type {olx.GeolocationOptions} */ ({
    +  projection: view.getProjection(),
    +  trackingOptions: {
    +    maximumAge: 10000,
    +    enableHighAccuracy: true,
    +    timeout: 600000
    +  }
    +}));
    +
    +var deltaMean = 500; // the geolocation sampling period mean in ms
    +
    +// Listen to position changes
    +geolocation.on('change', function(evt) {
    +  var position = geolocation.getPosition();
    +  var accuracy = geolocation.getAccuracy();
    +  var heading = geolocation.getHeading() || 0;
    +  var speed = geolocation.getSpeed() || 0;
    +  var m = Date.now();
    +
    +  addPosition(position, heading, m, speed);
    +
    +  var coords = positions.getCoordinates();
    +  var len = coords.length;
    +  if (len >= 2) {
    +    deltaMean = (coords[len - 1][3] - coords[0][3]) / (len - 1);
    +  }
    +
    +  var html = [
    +    'Position: ' + position[0].toFixed(2) + ', ' + position[1].toFixed(2),
    +    'Accuracy: ' + accuracy,
    +    'Heading: ' + Math.round(radToDeg(heading)) + '&deg;',
    +    'Speed: ' + (speed * 3.6).toFixed(1) + ' km/h',
    +    'Delta: ' + Math.round(deltaMean) + 'ms'
    +  ].join('<br />');
    +  document.getElementById('info').innerHTML = html;
    +});
    +
    +geolocation.on('error', function() {
    +  alert('geolocation error');
    +  // FIXME we should remove the coordinates in positions
    +});
    +
    +// convert radians to degrees
    +function radToDeg(rad) {
    +  return rad * 360 / (Math.PI * 2);
    +}
    +// convert degrees to radians
    +function degToRad(deg) {
    +  return deg * Math.PI * 2 / 360;
    +}
    +// modulo for negative values
    +function mod(n) {
    +  return ((n % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
    +}
    +
    +function addPosition(position, heading, m, speed) {
    +  var x = position[0];
    +  var y = position[1];
    +  var fCoords = positions.getCoordinates();
    +  var previous = fCoords[fCoords.length - 1];
    +  var prevHeading = previous && previous[2];
    +  if (prevHeading) {
    +    var headingDiff = heading - mod(prevHeading);
    +
    +    // force the rotation change to be less than 180°
    +    if (Math.abs(headingDiff) > Math.PI) {
    +      var sign = (headingDiff >= 0) ? 1 : -1;
    +      headingDiff = - sign * (2 * Math.PI - Math.abs(headingDiff));
    +    }
    +    heading = prevHeading + headingDiff;
    +  }
    +  positions.appendCoordinate([x, y, heading, m]);
    +
    +  // only keep the 20 last coordinates
    +  positions.setCoordinates(positions.getCoordinates().slice(-20));
    +
    +  // FIXME use speed instead
    +  if (heading && speed) {
    +    markerEl.src = 'data/geolocation_marker_heading.png';
    +  } else {
    +    markerEl.src = 'data/geolocation_marker.png';
    +  }
    +}
    +
    +var previousM = 0;
    +// change center and rotation before render
    +map.beforeRender(function(map, frameState) {
    +  if (frameState !== null) {
    +    // use sampling period to get a smooth transition
    +    var m = frameState.time - deltaMean * 1.5;
    +    m = Math.max(m, previousM);
    +    previousM = m;
    +    // interpolate position along positions LineString
    +    var c = positions.getCoordinateAtM(m, true);
    +    var view = frameState.viewState;
    +    if (c) {
    +      view.center = getCenterWithHeading(c, -c[2], view.resolution);
    +      view.rotation = -c[2];
    +      marker.setPosition(c);
    +    }
    +  }
    +  return true; // Force animation to continue
    +});
    +
    +// recenters the view by putting the given coordinates at 3/4 from the top or
    +// the screen
    +function getCenterWithHeading(position, rotation, resolution) {
    +  var size = map.getSize();
    +  var height = size[1];
    +
    +  return [
    +    position[0] - Math.sin(rotation) * height * resolution * 1 / 4,
    +    position[1] + Math.cos(rotation) * height * resolution * 1 / 4
    +  ];
    +}
    +
    +// postcompose callback
    +function render() {
    +  map.render();
    +}
    +
    +// geolocate device
    +var geolocateBtn = document.getElementById('geolocate');
    +geolocateBtn.addEventListener('click', function() {
    +  geolocation.setTracking(true); // Start position tracking
    +
    +  map.on('postcompose', render);
    +  map.render();
    +
    +  disableButtons();
    +}, false);
    +
    +// simulate device move
    +var simulationData;
    +$.getJSON('data/geolocation-orientation.json', function(data) {
    +  simulationData = data.data;
    +});
    +var simulateBtn = document.getElementById('simulate');
    +simulateBtn.addEventListener('click', function() {
    +  var coordinates = simulationData;
    +
    +  var first = coordinates.shift();
    +  simulatePositionChange(first);
    +
    +  var prevDate = first.timestamp;
    +  function geolocate() {
    +    var position = coordinates.shift();
    +    if (!position) {
    +      return;
    +    }
    +    var newDate = position.timestamp;
    +    simulatePositionChange(position);
    +    window.setTimeout(function() {
    +      prevDate = newDate;
    +      geolocate();
    +    }, (newDate - prevDate) / 0.5);
    +  }
    +  geolocate();
    +
    +  map.on('postcompose', render);
    +  map.render();
    +
    +  disableButtons();
    +}, false);
    +
    +function simulatePositionChange(position) {
    +  var coords = position.coords;
    +  geolocation.set('accuracy', coords.accuracy);
    +  geolocation.set('heading', degToRad(coords.heading));
    +  var position_ = [coords.longitude, coords.latitude];
    +  var projectedPosition = ol.proj.transform(position_, 'EPSG:4326',
    +      'EPSG:3857');
    +  geolocation.set('position', projectedPosition);
    +  geolocation.set('speed', coords.speed);
    +  geolocation.changed();
    +}
    +
    +function disableButtons() {
    +  geolocateBtn.disabled = 'disabled';
    +  simulateBtn.disabled = 'disabled';
    +}
    diff --git a/mapbox-gl/examples/geolocation-require.js b/mapbox-gl/examples/geolocation-require.js
    new file mode 100644
    index 0000000000..a70d18fbeb
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation-require.js
    @@ -0,0 +1,14 @@
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Geolocation');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.dom.Input');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/geolocation.html b/mapbox-gl/examples/geolocation.html
    new file mode 100644
    index 0000000000..b6cff8851a
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation.html
    @@ -0,0 +1,61 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Geolocation example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span8">
    +          <h4 id="title">Geolocation example</h4>
    +          <label class="checkbox" for="track">
    +            <input id="track" type="checkbox"/>track position
    +          </label>
    +          <p>position accuracy : <code id="accuracy"></code></p>
    +          <p>altitude : <code id="altitude"></code></p>
    +          <p>altitude accuracy : <code id="altitudeAccuracy"></code></p>
    +          <p>heading : <code id="heading"></code></p>
    +          <p>speed : <code id="speed"></code></p>
    +
    +          <p id="shortdesc">Example of a geolocation map.</p>
    +          <div id="docs">
    +            <p>See the <a href="geolocation.js" target="_blank">geolocation.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">geolocation, openstreetmap</div>
    +        </div>
    +        <div class="span4 pull-right">
    +          <div id="info" class="alert alert-error" style="display: none;"></div>
    +        </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=geolocation" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/geolocation.js b/mapbox-gl/examples/geolocation.js
    new file mode 100644
    index 0000000000..b9c8b0aed3
    --- /dev/null
    +++ b/mapbox-gl/examples/geolocation.js
    @@ -0,0 +1,69 @@
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 2
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: view
    +});
    +
    +var geolocation = new ol.Geolocation({
    +  projection: view.getProjection()
    +});
    +
    +var track = new ol.dom.Input(document.getElementById('track'));
    +track.bindTo('checked', geolocation, 'tracking');
    +
    +// update the HTML page when the position changes.
    +geolocation.on('change', function() {
    +  $('#accuracy').text(geolocation.getAccuracy() + ' [m]');
    +  $('#altitude').text(geolocation.getAltitude() + ' [m]');
    +  $('#altitudeAccuracy').text(geolocation.getAltitudeAccuracy() + ' [m]');
    +  $('#heading').text(geolocation.getHeading() + ' [rad]');
    +  $('#speed').text(geolocation.getSpeed() + ' [m/s]');
    +});
    +
    +// handle geolocation error.
    +geolocation.on('error', function(error) {
    +  var info = document.getElementById('info');
    +  info.innerHTML = error.message;
    +  info.style.display = '';
    +});
    +
    +var accuracyFeature = new ol.Feature();
    +accuracyFeature.bindTo('geometry', geolocation, 'accuracyGeometry');
    +
    +var positionFeature = new ol.Feature();
    +positionFeature.setStyle(new ol.style.Style({
    +  image: new ol.style.Circle({
    +    radius: 6,
    +    fill: new ol.style.Fill({
    +      color: '#3399CC'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#fff',
    +      width: 2
    +    })
    +  })
    +}));
    +
    +positionFeature.bindTo('geometry', geolocation, 'position')
    +    .transform(function() {}, function(coordinates) {
    +      return coordinates ? new ol.geom.Point(coordinates) : null;
    +    });
    +
    +var featuresOverlay = new ol.FeatureOverlay({
    +  map: map,
    +  features: [accuracyFeature, positionFeature]
    +});
    diff --git a/mapbox-gl/examples/getfeatureinfo-image-require.js b/mapbox-gl/examples/getfeatureinfo-image-require.js
    new file mode 100644
    index 0000000000..e06fe29188
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-image-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Image');
    +goog.require('ol.source.ImageWMS');
    diff --git a/mapbox-gl/examples/getfeatureinfo-image.html b/mapbox-gl/examples/getfeatureinfo-image.html
    new file mode 100644
    index 0000000000..bde7a8501d
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-image.html
    @@ -0,0 +1,59 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>GetFeatureInfo example (image layer)</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">GetFeatureInfo example (image layer)</h4>
    +          <p id="shortdesc">This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS image layer.</p>
    +          <div id="docs">
    +            <p>Additionally <code>map.forEachLayerAtPixel</code> is used to change the mouse
    +            pointer when hovering a non-transparent pixel on the map.</p>
    +            <p>See the <a href="getfeatureinfo-image.js" target="_blank">getfeatureinfo-image.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">getfeatureinfo, forEachLayerAtPixel</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=getfeatureinfo-image" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/getfeatureinfo-image.js b/mapbox-gl/examples/getfeatureinfo-image.js
    new file mode 100644
    index 0000000000..96274363ce
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-image.js
    @@ -0,0 +1,45 @@
    +var wmsSource = new ol.source.ImageWMS({
    +  url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +  params: {'LAYERS': 'ne:ne'},
    +  serverType: 'geoserver',
    +  crossOrigin: ''
    +});
    +
    +var wmsLayer = new ol.layer.Image({
    +  source: wmsSource
    +});
    +
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 1
    +});
    +
    +var map = new ol.Map({
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  layers: [wmsLayer],
    +  target: 'map',
    +  view: view
    +});
    +
    +map.on('singleclick', function(evt) {
    +  document.getElementById('info').innerHTML = '';
    +  var viewResolution = /** @type {number} */ (view.getResolution());
    +  var url = wmsSource.getGetFeatureInfoUrl(
    +      evt.coordinate, viewResolution, 'EPSG:3857',
    +      {'INFO_FORMAT': 'text/html'});
    +  if (url) {
    +    document.getElementById('info').innerHTML =
    +        '<iframe seamless src="' + url + '"></iframe>';
    +  }
    +});
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  var hit = map.forEachLayerAtPixel(pixel, function(layer) {
    +    return true;
    +  });
    +  map.getTargetElement().style.cursor = hit ? 'pointer' : '';
    +});
    diff --git a/mapbox-gl/examples/getfeatureinfo-tile-require.js b/mapbox-gl/examples/getfeatureinfo-tile-require.js
    new file mode 100644
    index 0000000000..efb325068f
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-tile-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.TileWMS');
    diff --git a/mapbox-gl/examples/getfeatureinfo-tile.html b/mapbox-gl/examples/getfeatureinfo-tile.html
    new file mode 100644
    index 0000000000..b59b9205ec
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-tile.html
    @@ -0,0 +1,59 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMS GetFeatureInfo example (tile layer)</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">WMS GetFeatureInfo example (tile layer)</h4>
    +          <p id="shortdesc">This example shows how to trigger WMS GetFeatureInfo requests on click for a WMS tile layer.</p>
    +          <div id="docs">
    +            <p>Additionally <code>map.forEachLayerAtPixel</code> is used to change the mouse
    +            pointer when hovering a non-transparent pixel on the map.</p>
    +            <p>See the <a href="getfeatureinfo-tile.js" target="_blank">getfeatureinfo-tile.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">getfeatureinfo, forEachLayerAtPixel</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=getfeatureinfo-tile" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/getfeatureinfo-tile.js b/mapbox-gl/examples/getfeatureinfo-tile.js
    new file mode 100644
    index 0000000000..5c5ef57222
    --- /dev/null
    +++ b/mapbox-gl/examples/getfeatureinfo-tile.js
    @@ -0,0 +1,45 @@
    +var wmsSource = new ol.source.TileWMS({
    +  url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +  params: {'LAYERS': 'ne:ne'},
    +  serverType: 'geoserver',
    +  crossOrigin: ''
    +});
    +
    +var wmsLayer = new ol.layer.Tile({
    +  source: wmsSource
    +});
    +
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 1
    +});
    +
    +var map = new ol.Map({
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  layers: [wmsLayer],
    +  target: 'map',
    +  view: view
    +});
    +
    +map.on('singleclick', function(evt) {
    +  document.getElementById('info').innerHTML = '';
    +  var viewResolution = /** @type {number} */ (view.getResolution());
    +  var url = wmsSource.getGetFeatureInfoUrl(
    +      evt.coordinate, viewResolution, 'EPSG:3857',
    +      {'INFO_FORMAT': 'text/html'});
    +  if (url) {
    +    document.getElementById('info').innerHTML =
    +        '<iframe seamless src="' + url + '"></iframe>';
    +  }
    +});
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  var hit = map.forEachLayerAtPixel(pixel, function(layer) {
    +    return true;
    +  });
    +  map.getTargetElement().style.cursor = hit ? 'pointer' : '';
    +});
    diff --git a/mapbox-gl/examples/gpx-require.js b/mapbox-gl/examples/gpx-require.js
    new file mode 100644
    index 0000000000..db1f46e7af
    --- /dev/null
    +++ b/mapbox-gl/examples/gpx-require.js
    @@ -0,0 +1,12 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.GPX');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.GPX');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/gpx.html b/mapbox-gl/examples/gpx.html
    new file mode 100644
    index 0000000000..5c35655732
    --- /dev/null
    +++ b/mapbox-gl/examples/gpx.html
    @@ -0,0 +1,62 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>GPX example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <div id="no-download" class="alert alert-error" style="display: none">
    +            The "Export GPX" functionality requires a browser that supports the
    +            <a href="http://caniuse.com/#feat=download">link download</a> attribute.
    +          </div>
    +          <a id="export-gpx" class="btn" download="map.gpx"><i class="icon-download"></i>Export GPX</a>
    +        </div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">GPX example</h4>
    +          <p id="shortdesc">Example of using the GPX source.</p>
    +          <div id="docs">
    +            <p>See the <a href="gpx.js" target="_blank">gpx.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">GPX</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=gpx" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/gpx.js b/mapbox-gl/examples/gpx.js
    new file mode 100644
    index 0000000000..d3da3a37cc
    --- /dev/null
    +++ b/mapbox-gl/examples/gpx.js
    @@ -0,0 +1,110 @@
    +var projection = ol.proj.get('EPSG:3857');
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({
    +    imagerySet: 'Aerial',
    +    key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +  })
    +});
    +
    +var style = {
    +  'Point': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,255,0,0.4)'
    +      }),
    +      radius: 5,
    +      stroke: new ol.style.Stroke({
    +        color: '#ff0',
    +        width: 1
    +      })
    +    })
    +  })],
    +  'LineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#f00',
    +      width: 3
    +    })
    +  })],
    +  'MultiLineString': [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#0f0',
    +      width: 3
    +    })
    +  })]
    +};
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.GPX({
    +    projection: projection,
    +    url: 'data/gpx/fells_loop.gpx'
    +  }),
    +  style: function(feature, resolution) {
    +    return style[feature.getGeometry().getType()];
    +  }
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [-7916041.528716288, 5228379.045749711],
    +    zoom: 12
    +  })
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  var features = [];
    +  map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    features.push(feature);
    +  });
    +  if (features.length > 0) {
    +    var info = [];
    +    var i, ii;
    +    for (i = 0, ii = features.length; i < ii; ++i) {
    +      info.push(features[i].get('desc'));
    +    }
    +    document.getElementById('info').innerHTML = info.join(', ') || '(unknown)';
    +    map.getTarget().style.cursor = 'pointer';
    +  } else {
    +    document.getElementById('info').innerHTML = '&nbsp;';
    +    map.getTarget().style.cursor = '';
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    +
    +var exportGPXElement = document.getElementById('export-gpx');
    +if ('download' in exportGPXElement) {
    +  var vectorSource = vector.getSource();
    +  exportGPXElement.addEventListener('click', function(e) {
    +    if (!exportGPXElement.href) {
    +      var features = [];
    +      vectorSource.forEachFeature(function(feature) {
    +        var clone = feature.clone();
    +        clone.getGeometry().transform(projection, 'EPSG:4326');
    +        features.push(clone);
    +      });
    +      var string = new ol.format.GPX().writeFeatures(features);
    +      var base64 = exampleNS.strToBase64(string);
    +      exportGPXElement.href =
    +          'data:text/gpx+xml;base64,' + base64;
    +    }
    +  }, false);
    +} else {
    +  var info = document.getElementById('no-download');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +}
    diff --git a/mapbox-gl/examples/graticule-require.js b/mapbox-gl/examples/graticule-require.js
    new file mode 100644
    index 0000000000..1933502b6d
    --- /dev/null
    +++ b/mapbox-gl/examples/graticule-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Graticule');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Stroke');
    diff --git a/mapbox-gl/examples/graticule.html b/mapbox-gl/examples/graticule.html
    new file mode 100644
    index 0000000000..89c57be0e5
    --- /dev/null
    +++ b/mapbox-gl/examples/graticule.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Graticule example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Graticule example</h4>
    +          <p id="shortdesc">This example shows how to add a graticule overlay to a map.</p>
    +          <div id="docs">
    +            <p>See the <a href="graticule.js" target="_blank">graticule.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">graticule</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=graticule" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/graticule.js b/mapbox-gl/examples/graticule.js
    new file mode 100644
    index 0000000000..42404c3448
    --- /dev/null
    +++ b/mapbox-gl/examples/graticule.js
    @@ -0,0 +1,24 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: 'canvas',
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([4.8, 47.75], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 5
    +  })
    +});
    +
    +// Create the graticule component
    +var graticule = new ol.Graticule({
    +  // the style to use for the lines, optional.
    +  strokeStyle: new ol.style.Stroke({
    +    color: 'rgba(255,120,0,0.9)',
    +    width: 2,
    +    lineDash: [0.5, 4]
    +  })
    +});
    +graticule.setMap(map);
    diff --git a/mapbox-gl/examples/heatmap-earthquakes-require.js b/mapbox-gl/examples/heatmap-earthquakes-require.js
    new file mode 100644
    index 0000000000..0df4c31be0
    --- /dev/null
    +++ b/mapbox-gl/examples/heatmap-earthquakes-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Heatmap');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.KML');
    +goog.require('ol.source.Stamen');
    diff --git a/mapbox-gl/examples/heatmap-earthquakes.html b/mapbox-gl/examples/heatmap-earthquakes.html
    new file mode 100644
    index 0000000000..79a5e2e59a
    --- /dev/null
    +++ b/mapbox-gl/examples/heatmap-earthquakes.html
    @@ -0,0 +1,61 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Earthquakes heatmap</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"><div id="info"></div></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span8">
    +          <h4 id="title">Earthquakes heatmap</h4>
    +          <p id="shortdesc">Demonstrates the use of a heatmap layer.</p>
    +          <div id="docs">
    +            <p>
    +              This example parses a KML file and renders the features as a <code>ol.layer.Heatmap</code> layer.
    +            </p>
    +            <p>See the <a href="heatmap-earthquakes.js" target="_blank">heatmap-earthquakes.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">heatmap, kml, vector, style</div>
    +        </div>
    +
    +        <div class="span4">
    +          <form>
    +            <label>radius size</label>
    +            <input id="radius" type="range" min="1" max="50" step="1" value="5"/>
    +            <label>blur size</label>
    +            <input id="blur" type="range" min="1" max="50" step="1" value="15"/>
    +          </form>
    +        </div>
    +
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="loader.js?id=heatmap-earthquakes" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/heatmap-earthquakes.js b/mapbox-gl/examples/heatmap-earthquakes.js
    new file mode 100644
    index 0000000000..315c89e176
    --- /dev/null
    +++ b/mapbox-gl/examples/heatmap-earthquakes.js
    @@ -0,0 +1,45 @@
    +var blur = $('#blur');
    +var radius = $('#radius');
    +
    +var vector = new ol.layer.Heatmap({
    +  source: new ol.source.KML({
    +    extractStyles: false,
    +    projection: 'EPSG:3857',
    +    url: 'data/kml/2012_Earthquakes_Mag5.kml'
    +  }),
    +  blur: parseInt(blur.val(), 10),
    +  radius: parseInt(radius.val(), 10)
    +});
    +
    +vector.getSource().on('addfeature', function(event) {
    +  // 2012_Earthquakes_Mag5.kml stores the magnitude of each earthquake in a
    +  // standards-violating <magnitude> tag in each Placemark.  We extract it from
    +  // the Placemark's name instead.
    +  var name = event.feature.get('name');
    +  var magnitude = parseFloat(name.substr(2));
    +  event.feature.set('weight', magnitude - 5);
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.Stamen({
    +    layer: 'toner'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +
    +blur.on('input', function() {
    +  vector.setBlur(parseInt(blur.val(), 10));
    +});
    +
    +radius.on('input', function() {
    +  vector.setRadius(parseInt(radius.val(), 10));
    +});
    diff --git a/mapbox-gl/examples/hue-saturation-require.js b/mapbox-gl/examples/hue-saturation-require.js
    new file mode 100644
    index 0000000000..8982c69b51
    --- /dev/null
    +++ b/mapbox-gl/examples/hue-saturation-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/hue-saturation.html b/mapbox-gl/examples/hue-saturation.html
    new file mode 100644
    index 0000000000..b5f4abc94b
    --- /dev/null
    +++ b/mapbox-gl/examples/hue-saturation.html
    @@ -0,0 +1,74 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Hue/saturation example</title>
    +    <style>
    +      #reset-hue {
    +        min-width: 90px;
    +      }
    +      #reset-saturation {
    +        min-width: 124px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +
    +          <div id="no-webgl" class="alert alert-error" style="display: none">
    +            This example requires a browser that supports <a href="http://get.webgl.org/">WebGL</a>.
    +          </div>
    +
    +          <div class="btn-group">
    +            <button id="increase-hue"><i class="icon-plus"></i></button>
    +            <button id="reset-hue">Hue</button>
    +            <button id="decrease-hue"><i class="icon-minus"></i></button>
    +          </div>
    +          <div class="btn-group">
    +            <button id="increase-saturation"><i class="icon-plus"></i></button>
    +            <button id="reset-saturation">Saturation</button>
    +            <button id="decrease-saturation"><i class="icon-minus"></i></button>
    +          </div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Hue/saturation example</h4>
    +          <p id="shortdesc">Example of hue/saturation control on the client (WebGL only).</p>
    +          <div id="docs">
    +            <p>See the <a href="hue-saturation.js" target="_blank">hue-saturation.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">hue, saturation, webgl</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=hue-saturation" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/hue-saturation.js b/mapbox-gl/examples/hue-saturation.js
    new file mode 100644
    index 0000000000..09164d1bdd
    --- /dev/null
    +++ b/mapbox-gl/examples/hue-saturation.js
    @@ -0,0 +1,71 @@
    +function setResetHueButtonHTML() {
    +  resetHue.innerHTML = 'Hue (' + layer.getHue().toFixed(2) + ')';
    +}
    +
    +function setResetSaturationButtonHTML() {
    +  resetSaturation.innerHTML = 'Saturation (' +
    +      layer.getSaturation().toFixed(2) + ')';
    +}
    +
    +if (!ol.has.WEBGL) {
    +  var info = document.getElementById('no-webgl');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +} else {
    +  var layer = new ol.layer.Tile({
    +    source: new ol.source.BingMaps({
    +      key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +      imagerySet: 'Aerial'
    +    })
    +  });
    +
    +  var map = new ol.Map({
    +    layers: [layer],
    +    renderer: 'webgl',
    +    target: 'map',
    +    view: new ol.View({
    +      center: ol.proj.transform([-9.375, 51.483333], 'EPSG:4326', 'EPSG:3857'),
    +      zoom: 15
    +    })
    +  });
    +
    +  var increaseHue = document.getElementById('increase-hue');
    +  var resetHue = document.getElementById('reset-hue');
    +  var decreaseHue = document.getElementById('decrease-hue');
    +
    +  setResetHueButtonHTML();
    +
    +  increaseHue.addEventListener('click', function() {
    +    layer.setHue(layer.getHue() + 0.25);
    +    setResetHueButtonHTML();
    +  }, false);
    +  resetHue.addEventListener('click', function() {
    +    layer.setHue(0);
    +    setResetHueButtonHTML();
    +  }, false);
    +  decreaseHue.addEventListener('click', function() {
    +    layer.setHue(layer.getHue() - 0.25);
    +    setResetHueButtonHTML();
    +  }, false);
    +
    +  var increaseSaturation = document.getElementById('increase-saturation');
    +  var resetSaturation = document.getElementById('reset-saturation');
    +  var decreaseSaturation = document.getElementById('decrease-saturation');
    +
    +  setResetSaturationButtonHTML();
    +
    +  increaseSaturation.addEventListener('click', function() {
    +    layer.setSaturation(layer.getSaturation() + 0.25);
    +    setResetSaturationButtonHTML();
    +  }, false);
    +  resetSaturation.addEventListener('click', function() {
    +    layer.setSaturation(1);
    +    setResetSaturationButtonHTML();
    +  }, false);
    +  decreaseSaturation.addEventListener('click', function() {
    +    layer.setSaturation(Math.max(layer.getSaturation() - 0.25, 0));
    +    setResetSaturationButtonHTML();
    +  }, false);
    +}
    diff --git a/mapbox-gl/examples/icon-require.js b/mapbox-gl/examples/icon-require.js
    new file mode 100644
    index 0000000000..83a9be7c85
    --- /dev/null
    +++ b/mapbox-gl/examples/icon-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.TileJSON');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/icon-sprite-webgl-require.js b/mapbox-gl/examples/icon-sprite-webgl-require.js
    new file mode 100644
    index 0000000000..b9a404ab41
    --- /dev/null
    +++ b/mapbox-gl/examples/icon-sprite-webgl-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/icon-sprite-webgl.html b/mapbox-gl/examples/icon-sprite-webgl.html
    new file mode 100644
    index 0000000000..1d50a55616
    --- /dev/null
    +++ b/mapbox-gl/examples/icon-sprite-webgl.html
    @@ -0,0 +1,58 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Icon sprites with WebGL example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span8">
    +          <h4 id="title">Icon sprite with WebGL example</h4>
    +          <p id="shortdesc">Icon sprite with WebGL.</p>
    +          <div id="docs">
    +            <p>See the <a href="icon-sprite-webgl.js" target="_blank">icon-sprite-webgl.js source</a> to see how this is done.</p>
    +            <p>In this example a sprite image is used for the icon styles. Using a sprite is required to get good performance with WebGL.</p>
    +          </div>
    +          <div id="tags">webgl, icon, sprite, vector, point</div>
    +        </div>
    +        <div class="span2 offset2">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=icon-sprite-webgl" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/icon-sprite-webgl.js b/mapbox-gl/examples/icon-sprite-webgl.js
    new file mode 100644
    index 0000000000..7b60fb2502
    --- /dev/null
    +++ b/mapbox-gl/examples/icon-sprite-webgl.js
    @@ -0,0 +1,131 @@
    +var iconInfo = [{
    +  offset: [0, 0],
    +  opacity: 1.0,
    +  rotateWithView: true,
    +  rotation: 0.0,
    +  scale: 1.0,
    +  size: [55, 55]
    +}, {
    +  offset: [110, 86],
    +  opacity: 0.75,
    +  rotateWithView: false,
    +  rotation: Math.PI / 2.0,
    +  scale: 1.25,
    +  size: [55, 55]
    +}, {
    +  offset: [55, 0],
    +  opacity: 0.5,
    +  rotateWithView: true,
    +  rotation: Math.PI / 3.0,
    +  scale: 1.5,
    +  size: [55, 86]
    +}, {
    +  offset: [212, 0],
    +  opacity: 1.0,
    +  rotateWithView: true,
    +  rotation: 0.0,
    +  scale: 1.0,
    +  size: [44, 44]
    +}];
    +
    +var i;
    +
    +var iconCount = iconInfo.length;
    +var icons = new Array(iconCount);
    +for (i = 0; i < iconCount; ++i) {
    +  var info = iconInfo[i];
    +  icons[i] = new ol.style.Icon({
    +    offset: info.offset,
    +    opacity: info.opacity,
    +    rotateWithView: info.rotateWithView,
    +    rotation: info.rotation,
    +    scale: info.scale,
    +    size: info.size,
    +    src: 'data/Butterfly.png'
    +  });
    +}
    +
    +var featureCount = 50000;
    +var features = new Array(featureCount);
    +var feature, geometry;
    +var e = 25000000;
    +for (i = 0; i < featureCount; ++i) {
    +  geometry = new ol.geom.Point(
    +      [2 * e * Math.random() - e, 2 * e * Math.random() - e]);
    +  feature = new ol.Feature(geometry);
    +  feature.setStyle(
    +      new ol.style.Style({
    +        image: icons[i % (iconCount - 1)]
    +      })
    +  );
    +  features[i] = feature;
    +}
    +
    +var vectorSource = new ol.source.Vector({
    +  features: features
    +});
    +var vector = new ol.layer.Vector({
    +  source: vectorSource
    +});
    +
    +// Use the "webgl" renderer by default.
    +var renderer = exampleNS.getRendererFromQueryString();
    +if (!renderer) {
    +  renderer = 'webgl';
    +}
    +
    +var map = new ol.Map({
    +  renderer: renderer,
    +  layers: [vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 5
    +  })
    +});
    +
    +var overlayFeatures = [];
    +for (i = 0; i < featureCount; i += 30) {
    +  var clone = features[i].clone();
    +  clone.setStyle(null);
    +  overlayFeatures.push(clone);
    +}
    +
    +var featureOverlay = new ol.FeatureOverlay({
    +  map: map,
    +  style: new ol.style.Style({
    +    image: icons[iconCount - 1]
    +  }),
    +  features: overlayFeatures
    +});
    +
    +map.on('click', function(evt) {
    +  var info = document.getElementById('info');
    +  info.innerHTML =
    +      'Hold on a second, while I catch those butterflies for you ...';
    +
    +  window.setTimeout(function() {
    +    var features = [];
    +    map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
    +      features.push(features);
    +      return false;
    +    });
    +
    +    if (features.length === 1) {
    +      info.innerHTML = 'Got one butterfly';
    +    } else if (features.length > 1) {
    +      info.innerHTML = 'Got ' + features.length + ' butterflies';
    +    } else {
    +      info.innerHTML = 'Couldn\'t catch a single butterfly';
    +    }
    +  }, 1);
    +});
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  var hit = map.hasFeatureAtPixel(pixel);
    +  map.getTarget().style.cursor = hit ? 'pointer' : '';
    +});
    diff --git a/mapbox-gl/examples/icon.html b/mapbox-gl/examples/icon.html
    new file mode 100644
    index 0000000000..6b1ddaeae0
    --- /dev/null
    +++ b/mapbox-gl/examples/icon.html
    @@ -0,0 +1,62 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Vector Icon Example</title>
    +    <style>
    +      #map {
    +        position: relative;
    +      }
    +      #popup {
    +        padding-bottom: 45px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map">
    +            <div id="popup"></div>
    +          </div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Icon example</h4>
    +          <p id="shortdesc">Example using an icon to symbolize a point.</p>
    +          <div id="docs">
    +            <p>See the <a href="icon.js" target="_blank">icon.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, style, icon, marker, popup</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=icon" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/icon.js b/mapbox-gl/examples/icon.js
    new file mode 100644
    index 0000000000..4c8f323838
    --- /dev/null
    +++ b/mapbox-gl/examples/icon.js
    @@ -0,0 +1,84 @@
    +var iconFeature = new ol.Feature({
    +  geometry: new ol.geom.Point([0, 0]),
    +  name: 'Null Island',
    +  population: 4000,
    +  rainfall: 500
    +});
    +
    +var iconStyle = new ol.style.Style({
    +  image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
    +    anchor: [0.5, 46],
    +    anchorXUnits: 'fraction',
    +    anchorYUnits: 'pixels',
    +    opacity: 0.75,
    +    src: 'data/icon.png'
    +  }))
    +});
    +
    +iconFeature.setStyle(iconStyle);
    +
    +var vectorSource = new ol.source.Vector({
    +  features: [iconFeature]
    +});
    +
    +var vectorLayer = new ol.layer.Vector({
    +  source: vectorSource
    +});
    +
    +var rasterLayer = new ol.layer.Tile({
    +  source: new ol.source.TileJSON({
    +    url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.jsonp',
    +    crossOrigin: ''
    +  })
    +});
    +
    +var map = new ol.Map({
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  layers: [rasterLayer, vectorLayer],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 3
    +  })
    +});
    +
    +var element = document.getElementById('popup');
    +
    +var popup = new ol.Overlay({
    +  element: element,
    +  positioning: 'bottom-center',
    +  stopEvent: false
    +});
    +map.addOverlay(popup);
    +
    +// display popup on click
    +map.on('click', function(evt) {
    +  var feature = map.forEachFeatureAtPixel(evt.pixel,
    +      function(feature, layer) {
    +        return feature;
    +      });
    +  if (feature) {
    +    var geometry = feature.getGeometry();
    +    var coord = geometry.getCoordinates();
    +    popup.setPosition(coord);
    +    $(element).popover({
    +      'placement': 'top',
    +      'html': true,
    +      'content': feature.get('name')
    +    });
    +    $(element).popover('show');
    +  } else {
    +    $(element).popover('destroy');
    +  }
    +});
    +
    +// change mouse cursor when over marker
    +map.on('pointermove', function(e) {
    +  if (e.dragging) {
    +    $(element).popover('destroy');
    +    return;
    +  }
    +  var pixel = map.getEventPixel(e.originalEvent);
    +  var hit = map.hasFeatureAtPixel(pixel);
    +  map.getTarget().style.cursor = hit ? 'pointer' : '';
    +});
    diff --git a/mapbox-gl/examples/igc-require.js b/mapbox-gl/examples/igc-require.js
    new file mode 100644
    index 0000000000..5a4ad48a79
    --- /dev/null
    +++ b/mapbox-gl/examples/igc-require.js
    @@ -0,0 +1,16 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.IGC');
    +goog.require('ol.source.OSM');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/igc.html b/mapbox-gl/examples/igc.html
    new file mode 100644
    index 0000000000..78e6b5c88e
    --- /dev/null
    +++ b/mapbox-gl/examples/igc.html
    @@ -0,0 +1,58 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>IGC example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">IGC example</h4>
    +          <p id="shortdesc">Example of tracks recorded from multiple paraglider flights on the same day, read from an IGC file.</p>
    +          <div id="docs">
    +            <p>The five tracks contain a total of 49,707 unique coordinates. Zoom in to see more detail. The background layer is from <a href="http://www.opencyclemap.org/">OpenCycleMap</a>.</p>
    +            <p>See the <a href="igc.js" target="_blank">igc.js source</a> to see how this is done.</p>
    +          </div>
    +          <input id="time" type="range" value="0" steps="1" />
    +          <div id="tags">complex-geometry, closest-feature, igc, opencyclemap</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=igc" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/igc.js b/mapbox-gl/examples/igc.js
    new file mode 100644
    index 0000000000..533c5ff020
    --- /dev/null
    +++ b/mapbox-gl/examples/igc.js
    @@ -0,0 +1,176 @@
    +var colors = {
    +  'Clement Latour': 'rgba(0, 0, 255, 0.7)',
    +  'Damien de Baesnt': 'rgba(0, 215, 255, 0.7)',
    +  'Sylvain Dhonneur': 'rgba(0, 165, 255, 0.7)',
    +  'Tom Payne': 'rgba(0, 255, 255, 0.7)',
    +  'Ulrich Prinz': 'rgba(0, 215, 255, 0.7)'
    +};
    +
    +var styleCache = {};
    +var styleFunction = function(feature, resolution) {
    +  var color = colors[feature.get('PLT')];
    +  var styleArray = styleCache[color];
    +  if (!styleArray) {
    +    styleArray = [new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: color,
    +        width: 3
    +      })
    +    })];
    +    styleCache[color] = styleArray;
    +  }
    +  return styleArray;
    +};
    +
    +var vectorSource = new ol.source.IGC({
    +  projection: 'EPSG:3857',
    +  urls: [
    +    'data/igc/Clement-Latour.igc',
    +    'data/igc/Damien-de-Baenst.igc',
    +    'data/igc/Sylvain-Dhonneur.igc',
    +    'data/igc/Tom-Payne.igc',
    +    'data/igc/Ulrich-Prinz.igc'
    +  ]
    +});
    +
    +var time = {
    +  start: Infinity,
    +  stop: -Infinity,
    +  duration: 0
    +};
    +vectorSource.on('addfeature', function(event) {
    +  var geometry = event.feature.getGeometry();
    +  time.start = Math.min(time.start, geometry.getFirstCoordinate()[2]);
    +  time.stop = Math.max(time.stop, geometry.getLastCoordinate()[2]);
    +  time.duration = time.stop - time.start;
    +});
    +
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM({
    +        attributions: [
    +          new ol.Attribution({
    +            html: 'All maps &copy; ' +
    +                '<a href="http://www.opencyclemap.org/">OpenCycleMap</a>'
    +          }),
    +          ol.source.OSM.ATTRIBUTION
    +        ],
    +        url: 'http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
    +      })
    +    }),
    +    new ol.layer.Vector({
    +      source: vectorSource,
    +      style: styleFunction
    +    })
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [703365.7089403362, 5714629.865071137],
    +    zoom: 9
    +  })
    +});
    +
    +
    +var point = null;
    +var line = null;
    +var displaySnap = function(coordinate) {
    +  var closestFeature = vectorSource.getClosestFeatureToCoordinate(coordinate);
    +  var info = document.getElementById('info');
    +  if (closestFeature === null) {
    +    point = null;
    +    line = null;
    +    info.innerHTML = '&nbsp;';
    +  } else {
    +    var geometry = closestFeature.getGeometry();
    +    var closestPoint = geometry.getClosestPoint(coordinate);
    +    if (point === null) {
    +      point = new ol.geom.Point(closestPoint);
    +    } else {
    +      point.setCoordinates(closestPoint);
    +    }
    +    var date = new Date(closestPoint[2] * 1000);
    +    info.innerHTML =
    +        closestFeature.get('PLT') + ' (' + date.toUTCString() + ')';
    +    var coordinates = [coordinate, [closestPoint[0], closestPoint[1]]];
    +    if (line === null) {
    +      line = new ol.geom.LineString(coordinates);
    +    } else {
    +      line.setCoordinates(coordinates);
    +    }
    +  }
    +  map.render();
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var coordinate = map.getEventCoordinate(evt.originalEvent);
    +  displaySnap(coordinate);
    +});
    +
    +map.on('click', function(evt) {
    +  displaySnap(evt.coordinate);
    +});
    +
    +var imageStyle = new ol.style.Circle({
    +  radius: 5,
    +  fill: null,
    +  stroke: new ol.style.Stroke({
    +    color: 'rgba(255,0,0,0.9)',
    +    width: 1
    +  })
    +});
    +var strokeStyle = new ol.style.Stroke({
    +  color: 'rgba(255,0,0,0.9)',
    +  width: 1
    +});
    +map.on('postcompose', function(evt) {
    +  var vectorContext = evt.vectorContext;
    +  if (point !== null) {
    +    vectorContext.setImageStyle(imageStyle);
    +    vectorContext.drawPointGeometry(point);
    +  }
    +  if (line !== null) {
    +    vectorContext.setFillStrokeStyle(null, strokeStyle);
    +    vectorContext.drawLineStringGeometry(line);
    +  }
    +});
    +
    +var featureOverlay = new ol.FeatureOverlay({
    +  map: map,
    +  style: new ol.style.Style({
    +    image: new ol.style.Circle({
    +      radius: 5,
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255,0,0,0.9)'
    +      }),
    +      stroke: null
    +    })
    +  })
    +});
    +
    +$('#time').on('input', function(event) {
    +  var value = parseInt($(this).val(), 10) / 100;
    +  var m = time.start + (time.duration * value);
    +  vectorSource.forEachFeature(function(feature) {
    +    var geometry = /** @type {ol.geom.LineString} */ (feature.getGeometry());
    +    var coordinate = geometry.getCoordinateAtM(m, true);
    +    var highlight = feature.get('highlight');
    +    if (highlight === undefined) {
    +      highlight = new ol.Feature(new ol.geom.Point(coordinate));
    +      feature.set('highlight', highlight);
    +      featureOverlay.addFeature(highlight);
    +    } else {
    +      highlight.getGeometry().setCoordinates(coordinate);
    +    }
    +  });
    +  map.render();
    +});
    diff --git a/mapbox-gl/examples/image-filter-require.js b/mapbox-gl/examples/image-filter-require.js
    new file mode 100644
    index 0000000000..ff168f7791
    --- /dev/null
    +++ b/mapbox-gl/examples/image-filter-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/image-filter.html b/mapbox-gl/examples/image-filter.html
    new file mode 100644
    index 0000000000..1b0b2fede3
    --- /dev/null
    +++ b/mapbox-gl/examples/image-filter.html
    @@ -0,0 +1,65 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Image Filter Example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Image filter example</h4>
    +          <p id="shortdesc">Apply a filter to imagery</p>
    +          <select id="kernel" name="kernel">
    +            <option>none</option>
    +            <option selected>sharpen</option>
    +            <option value="sharpenless">sharpen less</option>
    +            <option>blur</option>
    +            <option>shadow</option>
    +            <option>emboss</option>
    +            <option value="edge">edge detect</option>
    +          </select>
    +          <div id="docs">
    +            <p>
    +              Layer rendering can be manipulated in <code>precompose</code> and <code>postcompose</code> event listeners.
    +              These listeners get an event with a reference to the Canvas rendering context.
    +              In this example, the <code>postcompose</code> listener applies a filter to the image data.
    +            </p>
    +            <p>See the <a href="image-filter.js" target="_blank">image-filter.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">filter, image manipulation</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=image-filter" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/image-filter.js b/mapbox-gl/examples/image-filter.js
    new file mode 100644
    index 0000000000..18002082d2
    --- /dev/null
    +++ b/mapbox-gl/examples/image-filter.js
    @@ -0,0 +1,140 @@
    +var key = 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3';
    +
    +var imagery = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
    +});
    +
    +var map = new ol.Map({
    +  layers: [imagery],
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([-120, 50], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 6
    +  })
    +});
    +
    +var kernels = {
    +  none: [
    +    0, 0, 0,
    +    0, 1, 0,
    +    0, 0, 0
    +  ],
    +  sharpen: [
    +    0, -1, 0,
    +    -1, 5, -1,
    +    0, -1, 0
    +  ],
    +  sharpenless: [
    +    0, -1, 0,
    +    -1, 10, -1,
    +    0, -1, 0
    +  ],
    +  blur: [
    +    1, 1, 1,
    +    1, 1, 1,
    +    1, 1, 1
    +  ],
    +  shadow: [
    +    1, 2, 1,
    +    0, 1, 0,
    +    -1, -2, -1
    +  ],
    +  emboss: [
    +    -2, 1, 0,
    +    -1, 1, 1,
    +    0, 1, 2
    +  ],
    +  edge: [
    +    0, 1, 0,
    +    1, -4, 1,
    +    0, 1, 0
    +  ]
    +};
    +
    +function normalize(kernel) {
    +  var len = kernel.length;
    +  var normal = new Array(len);
    +  var i, sum = 0;
    +  for (i = 0; i < len; ++i) {
    +    sum += kernel[i];
    +  }
    +  if (sum <= 0) {
    +    normal.normalized = false;
    +    sum = 1;
    +  } else {
    +    normal.normalized = true;
    +  }
    +  for (i = 0; i < len; ++i) {
    +    normal[i] = kernel[i] / sum;
    +  }
    +  return normal;
    +}
    +
    +var select = document.getElementById('kernel');
    +var selectedKernel = normalize(kernels[select.value]);
    +
    +
    +/**
    + * Update the kernel and re-render on change.
    + */
    +select.onchange = function() {
    +  selectedKernel = normalize(kernels[select.value]);
    +  map.render();
    +};
    +
    +
    +/**
    + * Apply a filter on "postcompose" events.
    + * @param {ol.render.Event} event Render event.
    + */
    +imagery.on('postcompose', function(event) {
    +  convolve(event.context, selectedKernel);
    +});
    +
    +
    +/**
    + * Apply a convolution kernel to canvas.  This works for any size kernel, but
    + * performance starts degrading above 3 x 3.
    + * @param {CanvasRenderingContext2D} context Canvas 2d context.
    + * @param {Array.<number>} kernel Kernel.
    + */
    +function convolve(context, kernel) {
    +  var canvas = context.canvas;
    +  var width = canvas.width;
    +  var height = canvas.height;
    +
    +  var size = Math.sqrt(kernel.length);
    +  var half = Math.floor(size / 2);
    +
    +  var inputData = context.getImageData(0, 0, width, height).data;
    +
    +  var output = context.createImageData(width, height);
    +  var outputData = output.data;
    +
    +  for (var pixelY = 0; pixelY < height; ++pixelY) {
    +    var pixelsAbove = pixelY * width;
    +    for (var pixelX = 0; pixelX < width; ++pixelX) {
    +      var r = 0, g = 0, b = 0, a = 0;
    +      for (var kernelY = 0; kernelY < size; ++kernelY) {
    +        for (var kernelX = 0; kernelX < size; ++kernelX) {
    +          var weight = kernel[kernelY * size + kernelX];
    +          var neighborY = Math.min(
    +              height - 1, Math.max(0, pixelY + kernelY - half));
    +          var neighborX = Math.min(
    +              width - 1, Math.max(0, pixelX + kernelX - half));
    +          var inputIndex = (neighborY * width + neighborX) * 4;
    +          r += inputData[inputIndex] * weight;
    +          g += inputData[inputIndex + 1] * weight;
    +          b += inputData[inputIndex + 2] * weight;
    +          a += inputData[inputIndex + 3] * weight;
    +        }
    +      }
    +      var outputIndex = (pixelsAbove + pixelX) * 4;
    +      outputData[outputIndex] = r;
    +      outputData[outputIndex + 1] = g;
    +      outputData[outputIndex + 2] = b;
    +      outputData[outputIndex + 3] = kernel.normalized ? a : 255;
    +    }
    +  }
    +  context.putImageData(output, 0, 0);
    +}
    diff --git a/mapbox-gl/examples/image-vector-layer-require.js b/mapbox-gl/examples/image-vector-layer-require.js
    new file mode 100644
    index 0000000000..b5f97ec817
    --- /dev/null
    +++ b/mapbox-gl/examples/image-vector-layer-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.ImageVector');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/image-vector-layer.html b/mapbox-gl/examples/image-vector-layer.html
    new file mode 100644
    index 0000000000..2de416994b
    --- /dev/null
    +++ b/mapbox-gl/examples/image-vector-layer.html
    @@ -0,0 +1,59 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Image vector layer example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span8">
    +          <h4 id="title">Image vector example</h4>
    +          <p id="shortdesc">Example of an image vector layer.</p>
    +          <div id="docs">
    +            <p>This example uses a <code>ol.source.ImageVector</code> source. That source gets vector features from the
    +            <code>ol.source.Vector</code> it's configured with, and draw these features to an HTML5 canvas element that
    +            is then used as the image of an image layer.</p>
    +            <p>See the <a href="image-vector-layer.js" target="_blank">image-vector-layer.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, image</div>
    +        </div>
    +        <div class="span4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=image-vector-layer" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/image-vector-layer.js b/mapbox-gl/examples/image-vector-layer.js
    new file mode 100644
    index 0000000000..9275ba9eeb
    --- /dev/null
    +++ b/mapbox-gl/examples/image-vector-layer.js
    @@ -0,0 +1,80 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    }),
    +    new ol.layer.Image({
    +      source: new ol.source.ImageVector({
    +        source: new ol.source.GeoJSON({
    +          projection: 'EPSG:3857',
    +          url: 'data/geojson/countries.geojson'
    +        }),
    +        style: new ol.style.Style({
    +          fill: new ol.style.Fill({
    +            color: 'rgba(255, 255, 255, 0.6)'
    +          }),
    +          stroke: new ol.style.Stroke({
    +            color: '#319FD3',
    +            width: 1
    +          })
    +        })
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    +
    +var featureOverlay = new ol.FeatureOverlay({
    +  map: map,
    +  style: new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#f00',
    +      width: 1
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255,0,0,0.1)'
    +    })
    +  })
    +});
    +
    +var highlight;
    +var displayFeatureInfo = function(pixel) {
    +
    +  var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    return feature;
    +  });
    +
    +  var info = document.getElementById('info');
    +  if (feature) {
    +    info.innerHTML = feature.getId() + ': ' + feature.get('name');
    +  } else {
    +    info.innerHTML = '&nbsp;';
    +  }
    +
    +  if (feature !== highlight) {
    +    if (highlight) {
    +      featureOverlay.removeFeature(highlight);
    +    }
    +    if (feature) {
    +      featureOverlay.addFeature(feature);
    +    }
    +    highlight = feature;
    +  }
    +
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/index.html b/mapbox-gl/examples/index.html
    new file mode 100644
    index 0000000000..696db3930d
    --- /dev/null
    +++ b/mapbox-gl/examples/index.html
    @@ -0,0 +1,209 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style>
    +      .example {
    +        height: 140px;
    +        padding: 3px;
    +        background-color: #eee;
    +        border-radius: 3px;
    +        margin-bottom: 10px;
    +        overflow: auto;
    +      }
    +      .example:hover {
    +        background-color: #ddd;
    +      }
    +      .navbar-search.pull-left {
    +        padding: 5px;
    +      }
    +
    +      ::-webkit-scrollbar {
    +        width: 8px;
    +      }
    +
    +      ::-webkit-scrollbar-button {
    +        width: 0;
    +        height: 0;
    +        display: none;
    +      }
    +
    +      ::-webkit-scrollbar-thumb {
    +        background-color: rgba(0, 0, 0, 0.2);
    +        -webkit-box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.10),inset 0 -1px 0 rgba(0, 0, 0, 0.07);
    +      }
    +
    +      ::-webkit-scrollbar-thumb:hover {
    +        background-color: rgba(0, 0, 0, 0.4);
    +      }
    +
    +      ::-webkit-scrollbar-corner {
    +        background-color: transparent;
    +      }
    +    </style>
    +    <script type="text/javascript" src="Jugl.js"></script>
    +    <script type="text/javascript" src="example-list.js"></script>
    +    <script type="text/javascript">
    +      var template, target;
    +
    +      function listExamples(examples) {
    +        target.innerHTML = "";
    +        var node = template.process({
    +          context: {examples: examples},
    +          clone: true,
    +          parent: target
    +        });
    +        document.getElementById("count").innerHTML = "(" + examples.length + ")";
    +      }
    +
    +      var timerId;
    +      function inputChange() {
    +        if (timerId) {
    +          window.clearTimeout(timerId);
    +        }
    +        var text = this.value;
    +        timerId = window.setTimeout(function() {
    +          filterList(text);
    +        }, 500);
    +      }
    +
    +      function filterList(text) {
    +        var examples;
    +        if (text.length < 2) {
    +          examples = info.examples;
    +        } else {
    +          var words = text.split(/\W+/);
    +          var scores = {};
    +          for(var i=0; i<words.length; ++i) {
    +            var word = words[i].toLowerCase();
    +            var dict = info.index[word];
    +            var updateScores = function() {
    +              for(exIndex in dict) {
    +                var count = dict[exIndex];
    +                if(scores[exIndex]) {
    +                  if(scores[exIndex][word]) {
    +                    scores[exIndex][word] += count;
    +                  } else {
    +                    scores[exIndex][word] = count;
    +                  }
    +                } else {
    +                  scores[exIndex] = {};
    +                  scores[exIndex][word] = count;
    +                }
    +              }
    +            };
    +            if (dict) {
    +              updateScores();
    +            } else {
    +              var r;
    +              for (idx in info.index) {
    +                r = new RegExp(word);
    +                if (r.test(idx)) {
    +                  dict = info.index[idx];
    +                  updateScores();
    +                }
    +              }
    +            }
    +          }
    +          examples = [];
    +          for (var j in scores) {
    +            var ex = info.examples[j];
    +            ex.score = scores[j];
    +            examples.push(ex);
    +          }
    +          // sort examples by first by number of words matched, then
    +          // by word frequency
    +          examples.sort(function(a, b) {
    +            var cmp;
    +            var aWords = 0, bWords = 0;
    +            var aScore = 0, bScore = 0;
    +            for (var i in a.score) {
    +              aScore += a.score[i];
    +              aWords += 1;
    +            }
    +            for (var j in b.score) {
    +              bScore += b.score[j];
    +              bWords += 1;
    +            }
    +            if (aWords == bWords) {
    +              cmp = bScore - aScore;
    +            } else {
    +              cmp = bWords - aWords;
    +            }
    +            return cmp;
    +          });
    +        }
    +        listExamples(examples);
    +      }
    +
    +      function parseQuery() {
    +        var params = {};
    +        var list = window.location.search.substring(1).split("&");
    +        for (var i = 0; i < list.length; ++i) {
    +          var pair = list[i].split("=");
    +          if (pair.length == 2) {
    +            params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
    +          }
    +        }
    +        if (params["q"]) {
    +          var input = document.getElementById("keywords");
    +          input.value = params["q"];
    +          inputChange.call(input);
    +        }
    +      }
    +
    +      window.onload = function() {
    +        for (var i = 0; i < info.examples.length; ++i) {
    +          info.examples[i].link += window.location.search;
    +        }
    +        // document.getElementById('keywords').focus();
    +        template = new jugl.Template("template");
    +        target = document.getElementById("examples");
    +        listExamples(info.examples);
    +        document.getElementById("keywords").onkeyup = inputChange;
    +        parseQuery();
    +      };
    +    </script>
    +
    +    <title>OpenLayers 3 Examples</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +          <form class="navbar-search pull-left">
    +            <input name="q" type="text" id="keywords" class="search-query" placeholder="Search">
    +            <span id="count"></span>
    +          </form>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div id="examples"></div>
    +
    +      <div style="display: none;">
    +        <div id="template">
    +          <div class="span4 example" jugl:repeat="example examples">
    +            <a jugl:attributes="href example.link" class="mainlink">
    +              <strong><span jugl:replace="example.title">title</span></strong><br>
    +              <small jugl:content="'(' + example.example + ')'"></small>
    +            </a>
    +            <p><div jugl:content="example.shortdesc"></div></p>
    +            <p><small jugl:content="'tags: ' + example.tags"></small></p>
    +          </div>
    +        </div>
    +      </div>
    +
    +    </div>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/kml-earthquakes-require.js b/mapbox-gl/examples/kml-earthquakes-require.js
    new file mode 100644
    index 0000000000..92e71fbada
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-earthquakes-require.js
    @@ -0,0 +1,10 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.KML');
    +goog.require('ol.source.Stamen');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/kml-earthquakes.html b/mapbox-gl/examples/kml-earthquakes.html
    new file mode 100644
    index 0000000000..7352359da7
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-earthquakes.html
    @@ -0,0 +1,75 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Earthquakes in KML</title>
    +    <style>
    +      #map {
    +        position: relative;
    +      }
    +      #info {
    +        position: absolute;
    +        height: 1px;
    +        width: 1px;
    +        z-index: 100;
    +      }
    +      .tooltip.in {
    +        opacity: 1;
    +        filter: alpha(opacity=100);
    +      }
    +      .tooltip.top .tooltip-arrow {
    +        border-top-color: white;
    +      }
    +      .tooltip-inner {
    +        border: 2px solid white;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"><div id="info"></div></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Earthquakes in KML</h4>
    +          <p id="shortdesc">Demonstrates the use of a Shape symbolizer to render earthquake locations.</p>
    +          <div id="docs">
    +            <p>
    +              This example parses a KML file and renders the features as a vector layer.  The layer is given a <code>style</code> that renders earthquake locations with a size relative to their magnitude.
    +            </p>
    +            <p>See the <a href="kml-earthquakes.js" target="_blank">kml-earthquakes.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">KML, vector, style, tooltip</div>
    +        </div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=kml-earthquakes" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/kml-earthquakes.js b/mapbox-gl/examples/kml-earthquakes.js
    new file mode 100644
    index 0000000000..3c8761fd19
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-earthquakes.js
    @@ -0,0 +1,86 @@
    +var styleCache = {};
    +var styleFunction = function(feature, resolution) {
    +  // 2012_Earthquakes_Mag5.kml stores the magnitude of each earthquake in a
    +  // standards-violating <magnitude> tag in each Placemark.  We extract it from
    +  // the Placemark's name instead.
    +  var name = feature.get('name');
    +  var magnitude = parseFloat(name.substr(2));
    +  var radius = 5 + 20 * (magnitude - 5);
    +  var style = styleCache[radius];
    +  if (!style) {
    +    style = [new ol.style.Style({
    +      image: new ol.style.Circle({
    +        radius: radius,
    +        fill: new ol.style.Fill({
    +          color: 'rgba(255, 153, 0, 0.4)'
    +        }),
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(255, 204, 0, 0.2)',
    +          width: 1
    +        })
    +      })
    +    })];
    +    styleCache[radius] = style;
    +  }
    +  return style;
    +};
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.KML({
    +    extractStyles: false,
    +    projection: 'EPSG:3857',
    +    url: 'data/kml/2012_Earthquakes_Mag5.kml'
    +  }),
    +  style: styleFunction
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.Stamen({
    +    layer: 'toner'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var info = $('#info');
    +info.tooltip({
    +  animation: false,
    +  trigger: 'manual'
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  info.css({
    +    left: pixel[0] + 'px',
    +    top: (pixel[1] - 15) + 'px'
    +  });
    +  var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    return feature;
    +  });
    +  if (feature) {
    +    info.tooltip('hide')
    +        .attr('data-original-title', feature.get('name'))
    +        .tooltip('fixTitle')
    +        .tooltip('show');
    +  } else {
    +    info.tooltip('hide');
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    info.tooltip('hide');
    +    return;
    +  }
    +  displayFeatureInfo(map.getEventPixel(evt.originalEvent));
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/kml-require.js b/mapbox-gl/examples/kml-require.js
    new file mode 100644
    index 0000000000..7f43472157
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.KML');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.KML');
    diff --git a/mapbox-gl/examples/kml-timezones-require.js b/mapbox-gl/examples/kml-timezones-require.js
    new file mode 100644
    index 0000000000..90b7cd6cdb
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-timezones-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.KML');
    +goog.require('ol.source.Stamen');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/kml-timezones.html b/mapbox-gl/examples/kml-timezones.html
    new file mode 100644
    index 0000000000..564c648e7a
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-timezones.html
    @@ -0,0 +1,63 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Timezones in KML</title>
    +    <style>
    +      #map {
    +        position: relative;
    +      }
    +      #info {
    +        position: absolute;
    +        height: 1px;
    +        width: 1px;
    +        z-index: 100;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"><div id="info"></div></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Timezones in KML</h4>
    +          <p id="shortdesc">Demonstrates rendering timezones from KML.</p>
    +          <div id="docs">
    +            <p>This example parses a KML file and renders the features as a vector layer.  The layer is given a <code>ol.style.Style</code> that fills timezones yellow with an opacity calculated based on the current offset to local noon.</p>
    +            <p>See the <a href="kml-timezones.js" target="_blank">kml-timezones.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">KML, vector, style</div>
    +        </div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=kml-timezones" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/kml-timezones.js b/mapbox-gl/examples/kml-timezones.js
    new file mode 100644
    index 0000000000..c83389a2cf
    --- /dev/null
    +++ b/mapbox-gl/examples/kml-timezones.js
    @@ -0,0 +1,94 @@
    +/*
    + * Compute the style of the feature.  Here we want the opacity of polygons to
    + * be based on the offset from local noon.  For example, a timezone where it is
    + * currently noon would have an opacity of 0.75.  And a timezone where it is
    + * currently midnight would have an opacity of 0.  This doesn't account for
    + * daylight savings, so don't use it to plan your vacation.
    + */
    +var styleFunction = function(feature, resolution) {
    +  var offset = 0;
    +  var name = feature.get('name'); // e.g. GMT -08:30
    +  var match = name.match(/([\-+]\d{2}):(\d{2})$/);
    +  if (match) {
    +    var hours = parseInt(match[1], 10);
    +    var minutes = parseInt(match[2], 10);
    +    offset = 60 * hours + minutes;
    +  }
    +  var date = new Date();
    +  var local = new Date(date.getTime() +
    +      (date.getTimezoneOffset() + offset) * 60000);
    +  // offset from local noon (in hours)
    +  var delta = Math.abs(12 - local.getHours() + (local.getMinutes() / 60));
    +  if (delta > 12) {
    +    delta = 24 - delta;
    +  }
    +  var opacity = 0.75 * (1 - delta / 12);
    +  return [new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: [0xff, 0xff, 0x33, opacity]
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#ffffff'
    +    })
    +  })];
    +};
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.KML({
    +    extractStyles: false,
    +    projection: 'EPSG:3857',
    +    url: 'data/kml/timezones.kml'
    +  }),
    +  style: styleFunction
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.Stamen({
    +    layer: 'toner'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var info = $('#info');
    +info.tooltip({
    +  animation: false,
    +  trigger: 'manual'
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  info.css({
    +    left: pixel[0] + 'px',
    +    top: (pixel[1] - 15) + 'px'
    +  });
    +  var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    return feature;
    +  });
    +  if (feature) {
    +    info.tooltip('hide')
    +        .attr('data-original-title', feature.get('name'))
    +        .tooltip('fixTitle')
    +        .tooltip('show');
    +  } else {
    +    info.tooltip('hide');
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    info.tooltip('hide');
    +    return;
    +  }
    +  displayFeatureInfo(map.getEventPixel(evt.originalEvent));
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/kml.html b/mapbox-gl/examples/kml.html
    new file mode 100644
    index 0000000000..7a10f71e23
    --- /dev/null
    +++ b/mapbox-gl/examples/kml.html
    @@ -0,0 +1,61 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>KML example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <div id="no-download" class="alert alert-error" style="display: none">
    +            The "Export KML" functionality requires a browser that supports the
    +            <a href="http://caniuse.com/#feat=download">link download</a> attribute.
    +          </div>
    +          <a id="export-kml" class="btn" download="map.kml"><i class="icon-download"></i>Export KML</a>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">KML example</h4>
    +          <p id="shortdesc">Example of using the KML source.</p>
    +          <div id="docs">
    +            <p>See the <a href="kml.js" target="_blank">kml.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">KML</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=kml" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/kml.js b/mapbox-gl/examples/kml.js
    new file mode 100644
    index 0000000000..6c41421269
    --- /dev/null
    +++ b/mapbox-gl/examples/kml.js
    @@ -0,0 +1,82 @@
    +var projection = ol.proj.get('EPSG:3857');
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({
    +    imagerySet: 'Aerial',
    +    key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +  })
    +});
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.KML({
    +    projection: projection,
    +    url: 'data/kml/2012-02-10.kml'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [876970.8463461736, 5859807.853963373],
    +    projection: projection,
    +    zoom: 10
    +  })
    +});
    +
    +var displayFeatureInfo = function(pixel) {
    +  var features = [];
    +  map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    features.push(feature);
    +  });
    +  if (features.length > 0) {
    +    var info = [];
    +    var i, ii;
    +    for (i = 0, ii = features.length; i < ii; ++i) {
    +      info.push(features[i].get('name'));
    +    }
    +    document.getElementById('info').innerHTML = info.join(', ') || '(unknown)';
    +    map.getTarget().style.cursor = 'pointer';
    +  } else {
    +    document.getElementById('info').innerHTML = '&nbsp;';
    +    map.getTarget().style.cursor = '';
    +  }
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    +
    +var exportKMLElement = document.getElementById('export-kml');
    +if ('download' in exportKMLElement) {
    +  var vectorSource = vector.getSource();
    +  exportKMLElement.addEventListener('click', function(e) {
    +    if (!exportKMLElement.href) {
    +      var features = [];
    +      vectorSource.forEachFeature(function(feature) {
    +        var clone = feature.clone();
    +        clone.setId(feature.getId());  // clone does not set the id
    +        clone.getGeometry().transform(projection, 'EPSG:4326');
    +        features.push(clone);
    +      });
    +      var string = new ol.format.KML().writeFeatures(features);
    +      var base64 = exampleNS.strToBase64(string);
    +      exportKMLElement.href =
    +          'data:application/vnd.google-earth.kml+xml;base64,' + base64;
    +    }
    +  }, false);
    +} else {
    +  var info = document.getElementById('no-download');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +}
    diff --git a/mapbox-gl/examples/layer-clipping-require.js b/mapbox-gl/examples/layer-clipping-require.js
    new file mode 100644
    index 0000000000..ff458ec379
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/layer-clipping-webgl-require.js b/mapbox-gl/examples/layer-clipping-webgl-require.js
    new file mode 100644
    index 0000000000..5cfd1d8322
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping-webgl-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/layer-clipping-webgl.html b/mapbox-gl/examples/layer-clipping-webgl.html
    new file mode 100644
    index 0000000000..f28de14ec2
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping-webgl.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Layer WebGL clipping example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div id="no-webgl" class="alert alert-error" style="display: none">
    +        This example requires a browser that supports <a href="http://get.webgl.org/">WebGL</a>.
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Layer WebGL clipping example</h4>
    +          <p id="shortdesc">Layer WebGL clipping example.</p>
    +          <div id="docs">
    +            <p>This example shows how to use the <code>precompose</code> and <code>postcompose</code> rendering hooks to clip layers using WebGL.</p>
    +            <p>See the <a href="layer-clipping-webgl.js" target="_blank">layer-clipping-webgl.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">clipping, webgl, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-clipping-webgl" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-clipping-webgl.js b/mapbox-gl/examples/layer-clipping-webgl.js
    new file mode 100644
    index 0000000000..1d37c586ed
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping-webgl.js
    @@ -0,0 +1,101 @@
    +if (!ol.has.WEBGL) {
    +  var info = document.getElementById('no-webgl');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +} else {
    +
    +  var osm = new ol.layer.Tile({
    +    source: new ol.source.OSM()
    +  });
    +
    +  var map = new ol.Map({
    +    layers: [osm],
    +    renderer: 'webgl',
    +    target: 'map',
    +    controls: ol.control.defaults({
    +      attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +        collapsible: false
    +      })
    +    }),
    +    view: new ol.View({
    +      center: [0, 0],
    +      zoom: 2
    +    })
    +  });
    +
    +  var fragmentShaderSource = [
    +    'precision mediump float;',
    +    'void main() {',
    +    '}'
    +  ].join('');
    +
    +  var vertexShaderSource = [
    +    'attribute vec2 a_position;',
    +    'void main() {',
    +    '  gl_Position = vec4(a_position, 0, 1);',
    +    '}'
    +  ].join('');
    +
    +  osm.on('precompose', function(event) {
    +    var context = event.glContext;
    +
    +    var gl = context.getGL();
    +    var program = gl.createProgram();
    +
    +    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    +    gl.shaderSource(vertexShader, vertexShaderSource);
    +    gl.compileShader(vertexShader);
    +    gl.attachShader(program, vertexShader);
    +
    +    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    +    gl.shaderSource(fragmentShader, fragmentShaderSource);
    +    gl.compileShader(fragmentShader);
    +    gl.attachShader(program, fragmentShader);
    +
    +    gl.linkProgram(program);
    +    context.useProgram(program);
    +
    +    var positionLocation = gl.getAttribLocation(program, 'a_position');
    +
    +    gl.enable(gl.STENCIL_TEST);
    +    gl.colorMask(false, false, false, false);
    +    gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
    +    gl.stencilFunc(gl.ALWAYS, 1, 0xff);
    +
    +    var buffer = gl.createBuffer();
    +    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    +    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    +      // first band
    +      -1.0, -1.0, -0.75, -1.0, -1.0, 1.0,
    +      -1.0, 1.0, -0.75, -1.0, -0.75, 1.0,
    +      // second band
    +      -0.5, -1.0, -0.25, -1.0, -0.5, 1.0,
    +      -0.5, 1.0, -0.25, -1.0, -0.25, 1.0,
    +      // third band
    +      0.0, -1.0, 0.25, -1.0, 0.0, 1.0,
    +      0.0, 1.0, 0.25, -1.0, 0.25, 1.0,
    +      // forth band
    +      0.5, -1.0, 0.75, -1.0, 0.5, 1.0,
    +      0.5, 1.0, 0.75, -1.0, 0.75, 1.0
    +    ]), gl.STATIC_DRAW);
    +
    +    gl.enableVertexAttribArray(positionLocation);
    +    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    +    gl.drawArrays(gl.TRIANGLES, 0, 24);
    +
    +    gl.bindBuffer(gl.ARRAY_BUFFER, null);
    +    gl.deleteBuffer(buffer);
    +
    +    gl.colorMask(true, true, true, true);
    +    gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
    +    gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
    +  });
    +
    +  osm.on('postcompose', function(event) {
    +    var context = event.glContext;
    +    var gl = context.getGL();
    +    gl.disable(gl.STENCIL_TEST);
    +  });
    +}
    diff --git a/mapbox-gl/examples/layer-clipping.html b/mapbox-gl/examples/layer-clipping.html
    new file mode 100644
    index 0000000000..8b70917bd1
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Layer clipping example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Layer clipping example</h4>
    +          <p id="shortdesc">Layer clipping example.</p>
    +          <div id="docs">
    +            <p>See the <a href="layer-clipping.js" target="_blank">layer-clipping.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">clipping, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-clipping" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-clipping.js b/mapbox-gl/examples/layer-clipping.js
    new file mode 100644
    index 0000000000..932bc1f862
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-clipping.js
    @@ -0,0 +1,40 @@
    +var osm = new ol.layer.Tile({
    +  source: new ol.source.OSM()
    +});
    +
    +var map = new ol.Map({
    +  layers: [osm],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +osm.on('precompose', function(event) {
    +  var ctx = event.context;
    +  ctx.save();
    +  ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    +  ctx.scale(3, 3);
    +  ctx.translate(-75, -80);
    +  ctx.beginPath();
    +  ctx.moveTo(75, 40);
    +  ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
    +  ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
    +  ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
    +  ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
    +  ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
    +  ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
    +  ctx.clip();
    +  ctx.setTransform(1, 0, 0, 1, 0, 0);
    +});
    +
    +osm.on('postcompose', function(event) {
    +  var ctx = event.context;
    +  ctx.restore();
    +});
    diff --git a/mapbox-gl/examples/layer-extent-require.js b/mapbox-gl/examples/layer-extent-require.js
    new file mode 100644
    index 0000000000..6f88b73206
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-extent-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/layer-extent.html b/mapbox-gl/examples/layer-extent.html
    new file mode 100644
    index 0000000000..95bc7d041c
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-extent.html
    @@ -0,0 +1,68 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Limited Layer Extent</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Limited layer extent</h4>
    +          <p id="shortdesc">Restricting layer rendering to a limited extent.</p>
    +          <div id="docs">
    +            <p>
    +              This example uses the <code>layer.setExtent()</code> method to
    +              modify the extent of the overlay layer.  Use the controls below
    +              to limit rendering based on an extent.
    +            </p>
    +            <p>
    +              <div class="btn-group">
    +                <button type="button" class="btn btn-default" id="northwest">northwest</button>
    +                <button type="button" class="btn btn-default" id="northeast">northeast</button>
    +                <button type="button" class="btn btn-default" id="southeast">southeast</button>
    +                <button type="button" class="btn btn-default" id="southwest">southwest</button>
    +                <button type="button" class="btn btn-default" id="world">world</button>
    +              </div>
    +            </p>
    +            <p>
    +              See the <a href="layer-extent.js" target="_blank">layer-extent.js
    +              source</a> for details on how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">extent, tilejson</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-extent" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-extent.js b/mapbox-gl/examples/layer-extent.js
    new file mode 100644
    index 0000000000..1d2a1e6cfe
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-extent.js
    @@ -0,0 +1,44 @@
    +function transform(extent) {
    +  return ol.proj.transformExtent(extent, 'EPSG:4326', 'EPSG:3857');
    +}
    +
    +var extents = {
    +  northwest: transform([-180, 0, 0, 85]),
    +  northeast: transform([0, 0, 180, 85]),
    +  southeast: transform([0, -85, 180, 0]),
    +  southwest: transform([-180, -85, 0, 0]),
    +  world: transform([-180, -85, 180, 85])
    +};
    +
    +var base = new ol.layer.Tile({
    +  source: new ol.source.TileJSON({
    +    url: 'http://api.tiles.mapbox.com/v3/' +
    +        'mapbox.world-black.jsonp',
    +    crossOrigin: 'anonymous'
    +  })
    +});
    +
    +var overlay = new ol.layer.Tile({
    +  extent: extents.northwest,
    +  source: new ol.source.TileJSON({
    +    url: 'http://api.tiles.mapbox.com/v3/' +
    +        'mapbox.world-glass.jsonp',
    +    crossOrigin: 'anonymous'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [base, overlay],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    +
    +for (var key in extents) {
    +  document.getElementById(key).onclick = function(event) {
    +    overlay.setExtent(extents[event.target.id]);
    +  };
    +}
    diff --git a/mapbox-gl/examples/layer-group-require.js b/mapbox-gl/examples/layer-group-require.js
    new file mode 100644
    index 0000000000..e4808da86f
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-group-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.dom.Input');
    +goog.require('ol.layer.Group');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/layer-group.html b/mapbox-gl/examples/layer-group.html
    new file mode 100644
    index 0000000000..a86f9dc072
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-group.html
    @@ -0,0 +1,126 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style>
    +      #layertree li > span {
    +        cursor: pointer;
    +      }
    +    </style>
    +    <title>Layer group example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span6">
    +          <div id="map" class="map"></div>
    +
    +          <h4 id="title">Layer group example</h4>
    +          <p id="shortdesc">Example of a map with layer group.</p>
    +          <div id="docs">
    +            <p>See the <a href="layer-group.js" target="_blank">layer-group.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">tilejson, input, bind, group, layergroup</div>
    +        </div>
    +
    +        <div id="layertree" class="span6">
    +          <h5>Click on layer nodes below to change their properties.</h5>
    +              <ul>
    +                <li><span>OpenAerial layer</span>
    +                  <fieldset id="layer0">
    +                    <label class="checkbox" for="visible0">
    +                      <input id="visible0" class="visible" type="checkbox"/>visibility
    +                    </label>
    +                    <label>opacity</label>
    +                    <input class="opacity" type="range" min="0" max="1" step="0.01"/>
    +                    <label>hue</label>
    +                    <input class="hue" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +                    <label>saturation</label>
    +                    <input class="saturation" type="range" min="0" max="5" step="0.01"/>
    +                    <label>contrast</label>
    +                    <input class="contrast" type="range" min="0" max="2" step="0.01"/>
    +                    <label>brightness</label>
    +                    <input class="brightness" type="range" min="-1" max="1" step="0.01"/>
    +                  </fieldset>
    +                </li>
    +                <li><span>Layer group</span>
    +                  <fieldset id="layer1">
    +                    <label class="checkbox" for="visible1">
    +                      <input id="visible1" class="visible" type="checkbox"/>visibility
    +                    </label>
    +                    <label>opacity</label>
    +                    <input class="opacity" type="range" min="0" max="1" step="0.01"/>
    +                    <label>hue</label>
    +                    <input class="hue" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +                    <label>saturation</label>
    +                    <input class="saturation" type="range" min="0" max="5" step="0.01"/>
    +                    <label>contrast</label>
    +                    <input class="contrast" type="range" min="0" max="2" step="0.01"/>
    +                    <label>brightness</label>
    +                    <input class="brightness" type="range" min="-1" max="1" step="0.01"/>
    +                  </fieldset>
    +                  <ul>
    +                    <li><span>Food insecurity layer</span>
    +                      <fieldset id="layer10">
    +                        <label class="checkbox" for="visible10">
    +                          <input id="visible10" class="visible" type="checkbox"/>visibility
    +                        </label>
    +                        <label>opacity</label>
    +                        <input class="opacity" type="range" min="0" max="1" step="0.01"/>
    +                        <label>hue</label>
    +                        <input class="hue" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +                        <label>saturation</label>
    +                        <input class="saturation" type="range" min="0" max="5" step="0.01"/>
    +                        <label>contrast</label>
    +                        <input class="contrast" type="range" min="0" max="2" step="0.01"/>
    +                        <label>brightness</label>
    +                        <input class="brightness" type="range" min="-1" max="1" step="0.01"/>
    +                      </fieldset>
    +                    </li>
    +                    <li><span>World borders layer</span>
    +                      <fieldset id="layer11">
    +                        <label class="checkbox" for="visible11">
    +                          <input id="visible11" class="visible" type="checkbox"/>visibility
    +                        </label>
    +                        <label>opacity</label>
    +                        <input class="opacity" type="range" min="0" max="1" step="0.01"/>
    +                        <label>hue</label>
    +                        <input class="hue" type="range" min="-3.141592653589793" max="3.141592653589793" step="0.01"/>
    +                        <label>saturation</label>
    +                        <input class="saturation" type="range" min="0" max="5" step="0.01"/>
    +                        <label>contrast</label>
    +                        <input class="contrast" type="range" min="0" max="2" step="0.01"/>
    +                        <label>brightness</label>
    +                        <input class="brightness" type="range" min="-1" max="1" step="0.01"/>
    +                      </fieldset>
    +                    </li>
    +                  </ul>
    +                </li>
    +              </ul>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-group" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-group.js b/mapbox-gl/examples/layer-group.js
    new file mode 100644
    index 0000000000..30e085b318
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-group.js
    @@ -0,0 +1,54 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    }), new ol.layer.Group({
    +      layers: [
    +        new ol.layer.Tile({
    +          source: new ol.source.TileJSON({
    +            url: 'http://api.tiles.mapbox.com/v3/' +
    +                'mapbox.20110804-hoa-foodinsecurity-3month.jsonp',
    +            crossOrigin: 'anonymous'
    +          })
    +        }),
    +        new ol.layer.Tile({
    +          source: new ol.source.TileJSON({
    +            url: 'http://api.tiles.mapbox.com/v3/' +
    +                'mapbox.world-borders-light.jsonp',
    +            crossOrigin: 'anonymous'
    +          })
    +        })
    +      ]
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([37.40570, 8.81566], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 4
    +  })
    +});
    +
    +function bindInputs(layerid, layer) {
    +  new ol.dom.Input($(layerid + ' .visible')[0])
    +      .bindTo('checked', layer, 'visible');
    +  $.each(['opacity', 'hue', 'saturation', 'contrast', 'brightness'],
    +      function(i, v) {
    +        new ol.dom.Input($(layerid + ' .' + v)[0])
    +            .bindTo('value', layer, v)
    +            .transform(parseFloat, String);
    +      }
    +  );
    +}
    +map.getLayers().forEach(function(layer, i) {
    +  bindInputs('#layer' + i, layer);
    +  if (layer instanceof ol.layer.Group) {
    +    layer.getLayers().forEach(function(sublayer, j) {
    +      bindInputs('#layer' + i + j, sublayer);
    +    });
    +  }
    +});
    +
    +$('#layertree li > span').click(function() {
    +  $(this).siblings('fieldset').toggle();
    +}).siblings('fieldset').hide();
    diff --git a/mapbox-gl/examples/layer-spy-require.js b/mapbox-gl/examples/layer-spy-require.js
    new file mode 100644
    index 0000000000..ff168f7791
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-spy-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/layer-spy.html b/mapbox-gl/examples/layer-spy.html
    new file mode 100644
    index 0000000000..1eb895c269
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-spy.html
    @@ -0,0 +1,60 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Layer Spy Example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Layer spy example</h4>
    +          <p id="shortdesc">View a portion of one layer over another</p>
    +          <div id="docs">
    +            <p>
    +              Layer rendering can be manipulated in <code>precompose</code> and <code>postcompose</code> event listeners.
    +              These listeners get an event with a reference to the Canvas rendering context.
    +              In this example, the <code>precompose</code> listener sets a clipping mask around the most
    +              recent mouse position, giving you a spyglass effect for viewing one layer over another.
    +            </p>
    +            <p>
    +              Move around the map to see the effect.  Use the ↑ up and ↓ down arrow keys to adjust the spyglass size.
    +            </p>
    +            <p>See the <a href="layer-spy.js" target="_blank">layer-spy.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">spy, image manipulation</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-spy" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-spy.js b/mapbox-gl/examples/layer-spy.js
    new file mode 100644
    index 0000000000..9ca1dee1ad
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-spy.js
    @@ -0,0 +1,62 @@
    +var key = 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3';
    +
    +var roads = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({key: key, imagerySet: 'Road'})
    +});
    +
    +var imagery = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
    +});
    +
    +var map = new ol.Map({
    +  layers: [roads, imagery],
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([-109, 46.5], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 6
    +  })
    +});
    +
    +var radius = 75;
    +$(document).keydown(function(evt) {
    +  if (evt.which === 38) {
    +    radius = Math.min(radius + 5, 150);
    +    map.render();
    +  } else if (evt.which === 40) {
    +    radius = Math.max(radius - 5, 25);
    +    map.render();
    +  }
    +});
    +
    +// get the pixel position with every move
    +var mousePosition = null;
    +$(map.getViewport()).on('mousemove', function(evt) {
    +  mousePosition = map.getEventPixel(evt.originalEvent);
    +  map.render();
    +}).on('mouseout', function() {
    +  mousePosition = null;
    +  map.render();
    +});
    +
    +// before rendering the layer, do some clipping
    +imagery.on('precompose', function(event) {
    +  var ctx = event.context;
    +  var pixelRatio = event.frameState.pixelRatio;
    +  ctx.save();
    +  ctx.beginPath();
    +  if (mousePosition) {
    +    // only show a circle around the mouse
    +    ctx.arc(mousePosition[0] * pixelRatio, mousePosition[1] * pixelRatio,
    +        radius * pixelRatio, 0, 2 * Math.PI);
    +    ctx.lineWidth = 5 * pixelRatio;
    +    ctx.strokeStyle = 'rgba(0,0,0,0.5)';
    +    ctx.stroke();
    +  }
    +  ctx.clip();
    +});
    +
    +// after rendering the layer, restore the canvas context
    +imagery.on('postcompose', function(event) {
    +  var ctx = event.context;
    +  ctx.restore();
    +});
    diff --git a/mapbox-gl/examples/layer-swipe-require.js b/mapbox-gl/examples/layer-swipe-require.js
    new file mode 100644
    index 0000000000..8307dc999c
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-swipe-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/layer-swipe.html b/mapbox-gl/examples/layer-swipe.html
    new file mode 100644
    index 0000000000..15e34dc16e
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-swipe.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Layer Swipe example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <input id="swipe" type="range" style="width: 100%">
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Layer Swipe example</h4>
    +          <p id="shortdesc">Example of a Layer swipe map.</p>
    +          <div id="docs">
    +            <p>See the <a href="layer-swipe.js" target="_blank">layer-swipe.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">swipe, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=layer-swipe" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/layer-swipe.js b/mapbox-gl/examples/layer-swipe.js
    new file mode 100644
    index 0000000000..c10d1ed215
    --- /dev/null
    +++ b/mapbox-gl/examples/layer-swipe.js
    @@ -0,0 +1,44 @@
    +var osm = new ol.layer.Tile({
    +  source: new ol.source.OSM()
    +});
    +var bing = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({
    +    key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +    imagerySet: 'Aerial'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [osm, bing],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var swipe = document.getElementById('swipe');
    +
    +bing.on('precompose', function(event) {
    +  var ctx = event.context;
    +  var width = ctx.canvas.width * (swipe.value / 100);
    +
    +  ctx.save();
    +  ctx.beginPath();
    +  ctx.rect(width, 0, ctx.canvas.width - width, ctx.canvas.height);
    +  ctx.clip();
    +});
    +
    +bing.on('postcompose', function(event) {
    +  var ctx = event.context;
    +  ctx.restore();
    +});
    +
    +swipe.addEventListener('input', function() {
    +  map.render();
    +}, false);
    diff --git a/mapbox-gl/examples/lazy-source-require.js b/mapbox-gl/examples/lazy-source-require.js
    new file mode 100644
    index 0000000000..084dd51781
    --- /dev/null
    +++ b/mapbox-gl/examples/lazy-source-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/lazy-source.html b/mapbox-gl/examples/lazy-source.html
    new file mode 100644
    index 0000000000..ce57a051d5
    --- /dev/null
    +++ b/mapbox-gl/examples/lazy-source.html
    @@ -0,0 +1,73 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Lazy Source</title>
    +    <style>
    +      button.code {
    +        font-family: Monaco,Menlo,Consolas,"Courier New",monospace;
    +        font-size: 12px;
    +        padding: 5px;
    +        margin: 0 5px;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Lazy source example</h4>
    +          <p id="shortdesc">Example of setting a layer source after construction.</p>
    +          <div id="docs">
    +            <p>
    +              Typically, the source for a layer is provided to the layer constructor.
    +              If you need to set a layer source after construction, this can be
    +              done with the <code>layer.setSource()</code> method.
    +            </p>
    +            <p>
    +              The layer in the map above is constructed with no source.  Use the
    +              links below to set/unset the layer source.  A layer is not rendered
    +              until its source is set.
    +            </p>
    +            <p>
    +              <button id="set-source" class="code">layer.setSource(source)</button>
    +              <button id="unset-source" class="code">layer.setSource(null)</button>
    +            </p>
    +            <p>See the <a href="lazy-source.js" target="_blank">lazy-source.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">source</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=lazy-source" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/lazy-source.js b/mapbox-gl/examples/lazy-source.js
    new file mode 100644
    index 0000000000..0a77ace089
    --- /dev/null
    +++ b/mapbox-gl/examples/lazy-source.js
    @@ -0,0 +1,21 @@
    +var source = new ol.source.MapQuest({layer: 'sat'});
    +
    +var layer = new ol.layer.Tile();
    +
    +var map = new ol.Map({
    +  layers: [layer],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +document.getElementById('set-source').onclick = function() {
    +  layer.setSource(source);
    +};
    +
    +document.getElementById('unset-source').onclick = function() {
    +  layer.setSource(null);
    +};
    diff --git a/mapbox-gl/examples/line-arrows-require.js b/mapbox-gl/examples/line-arrows-require.js
    new file mode 100644
    index 0000000000..c8b2348e0a
    --- /dev/null
    +++ b/mapbox-gl/examples/line-arrows-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.interaction.Draw');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/line-arrows.html b/mapbox-gl/examples/line-arrows.html
    new file mode 100644
    index 0000000000..6338653387
    --- /dev/null
    +++ b/mapbox-gl/examples/line-arrows.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>LineString arrows example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">LineString arrows example</h4>
    +          <p id="shortdesc">Example of drawing arrows for each line string segment.</p>
    +          <div id="docs">
    +            <p>See the <a href="line-arrows.js" target="_blank">line-arrows.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">draw, vector, arrow</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=line-arrows" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/line-arrows.js b/mapbox-gl/examples/line-arrows.js
    new file mode 100644
    index 0000000000..7df0e9bae3
    --- /dev/null
    +++ b/mapbox-gl/examples/line-arrows.js
    @@ -0,0 +1,55 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var source = new ol.source.Vector();
    +
    +var styleFunction = function(feature, resolution) {
    +  var geometry = feature.getGeometry();
    +  var styles = [
    +    // linestring
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: '#ffcc33',
    +        width: 2
    +      })
    +    })
    +  ];
    +
    +  geometry.forEachSegment(function(start, end) {
    +    var dx = end[0] - start[0];
    +    var dy = end[1] - start[1];
    +    var rotation = Math.atan2(dy, dx);
    +    // arrows
    +    styles.push(new ol.style.Style({
    +      geometry: new ol.geom.Point(end),
    +      image: new ol.style.Icon({
    +        src: 'data/arrow.png',
    +        anchor: [0.75, 0.5],
    +        rotateWithView: false,
    +        rotation: -rotation
    +      })
    +    }));
    +  });
    +
    +  return styles;
    +};
    +var vector = new ol.layer.Vector({
    +  source: source,
    +  style: styleFunction
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-11000000, 4600000],
    +    zoom: 4
    +  })
    +});
    +
    +map.addInteraction(new ol.interaction.Draw({
    +  source: source,
    +  type: /** @type {ol.geom.GeometryType} */ ('LineString')
    +}));
    diff --git a/mapbox-gl/examples/loader.js b/mapbox-gl/examples/loader.js
    new file mode 100644
    index 0000000000..3ca942911b
    --- /dev/null
    +++ b/mapbox-gl/examples/loader.js
    @@ -0,0 +1,69 @@
    +/**
    + * This loader is used for the hosted examples. It is used in place of the
    + * development loader (examples/loader.js).
    + *
    + * The loader loads ol.js and the example's script in "production" mode; it
    + * loads Closure Library's base.js, ol-deps.js, the example's "goog.require"
    + * script, and the example's script in "development" mode.
    + *
    + * The ol.js and ol-deps.js scripts are built by OL3's build.py script.
    + * They are located in the ../build/ directory, relative to this script.
    + *
    + * The script must be named loader.js.
    + *
    + * Usage:
    + *
    + *     <script src="../loader.js?id=my-demo"></script>
    + */
    +
    +(function() {
    +
    +  var i, pair;
    +
    +  var href = window.location.href, start, end, paramsString, pairs,
    +      pageParams = {};
    +  if (href.indexOf('?') > 0) {
    +    start = href.indexOf('?') + 1;
    +    end = href.indexOf('#') > 0 ? href.indexOf('#') : href.length;
    +    paramsString = href.substring(start, end);
    +    pairs = paramsString.split(/[&;]/);
    +    for (i = 0; i < pairs.length; ++i) {
    +      pair = pairs[i].split('=');
    +      if (pair[0]) {
    +          pageParams[decodeURIComponent(pair[0])] =
    +              decodeURIComponent(pair[1]);
    +      }
    +    }
    +  }
    +
    +  var scripts = document.getElementsByTagName('script');
    +  var src, index, search, chunks, scriptParams = {};
    +  for (i = scripts.length - 1; i >= 0; --i) {
    +    src = scripts[i].getAttribute('src');
    +    if (~(index = src.indexOf('loader.js?'))) {
    +      search = src.substr(index + 10);
    +      chunks = search ? search.split('&') : [];
    +      for (i = chunks.length - 1; i >= 0; --i) {
    +        pair = chunks[i].split('=');
    +        if (pair[0]) {
    +          scriptParams[decodeURIComponent(pair[0])] =
    +              decodeURIComponent(pair[1]);
    +        }
    +      }
    +      break;
    +    }
    +  }
    +
    +  var raw = pageParams.mode && pageParams.mode.toLowerCase() === 'raw';
    +
    +  var scriptId = encodeURIComponent(scriptParams.id);
    +  if (!raw) {
    +    document.write('<scr' + 'ipt type="text/javascript" src="../build/ol.js"></scr' + 'ipt>');
    +  } else {
    +    window.CLOSURE_NO_DEPS = true; // we've got our own deps file
    +    document.write('<scr' + 'ipt type="text/javascript" src="../closure-library/closure/goog/base.js"></scr' + 'ipt>');
    +    document.write('<scr' + 'ipt type="text/javascript" src="../build/ol-deps.js"></scr' + 'ipt>');
    +    document.write('<scr' + 'ipt type="text/javascript" src="' + scriptId + '-require.js"></scr' + 'ipt>');
    +  }
    +  document.write('<scr' + 'ipt type="text/javascript" src="' + scriptId + '.js"></scr' + 'ipt>');
    +}());
    diff --git a/mapbox-gl/examples/localized-openstreetmap-require.js b/mapbox-gl/examples/localized-openstreetmap-require.js
    new file mode 100644
    index 0000000000..0a2048e378
    --- /dev/null
    +++ b/mapbox-gl/examples/localized-openstreetmap-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/localized-openstreetmap.html b/mapbox-gl/examples/localized-openstreetmap.html
    new file mode 100644
    index 0000000000..aeecd23e99
    --- /dev/null
    +++ b/mapbox-gl/examples/localized-openstreetmap.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Localized OpenStreetMap example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Localized OpenStreetMap example</h4>
    +          <p id="shortdesc">Example of a localized OpenStreetMap map with a custom tile server and a custom attribution.</p>
    +          <div id="docs">
    +            <p>The base layer is <a href="http://www.opencyclemap.org/">OpenCycleMap</a> with an overlay from <a href="http://www.openseamap.org/">OpenSeaMap</a>. The OpenSeaMap tile server does not support <a href="http://enable-cors.org/">CORS</a> headers.</p>
    +            <p>See the <a href="localized-openstreetmap.js" target="_blank">localized-openstreetmap.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">cors, localized-openstreetmap, openseamap, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=localized-openstreetmap" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/localized-openstreetmap.js b/mapbox-gl/examples/localized-openstreetmap.js
    new file mode 100644
    index 0000000000..8e2b3f1ebe
    --- /dev/null
    +++ b/mapbox-gl/examples/localized-openstreetmap.js
    @@ -0,0 +1,48 @@
    +// tiles.openseamap.org does not set CORS headers, so we have to disable
    +// crossOrigin and we cannot use WebGL.
    +
    +var openCycleMapLayer = new ol.layer.Tile({
    +  source: new ol.source.OSM({
    +    attributions: [
    +      new ol.Attribution({
    +        html: 'All maps &copy; ' +
    +            '<a href="http://www.opencyclemap.org/">OpenCycleMap</a>'
    +      }),
    +      ol.source.OSM.ATTRIBUTION
    +    ],
    +    url: 'http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
    +  })
    +});
    +
    +var openSeaMapLayer = new ol.layer.Tile({
    +  source: new ol.source.OSM({
    +    attributions: [
    +      new ol.Attribution({
    +        html: 'All maps &copy; ' +
    +            '<a href="http://www.openseamap.org/">OpenSeaMap</a>'
    +      }),
    +      ol.source.OSM.ATTRIBUTION
    +    ],
    +    crossOrigin: null,
    +    url: 'http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png'
    +  })
    +});
    +
    +
    +var map = new ol.Map({
    +  layers: [
    +    openCycleMapLayer,
    +    openSeaMapLayer
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    maxZoom: 18,
    +    center: [-244780.24508882355, 5986452.183179816],
    +    zoom: 15
    +  })
    +});
    diff --git a/mapbox-gl/examples/mapbox-gl-require.js b/mapbox-gl/examples/mapbox-gl-require.js
    new file mode 100644
    index 0000000000..b9d53b05d2
    --- /dev/null
    +++ b/mapbox-gl/examples/mapbox-gl-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj');
    +goog.require('ol.source.ImageCanvas');
    diff --git a/mapbox-gl/examples/mapbox-gl.html b/mapbox-gl/examples/mapbox-gl.html
    new file mode 100644
    index 0000000000..c379484c65
    --- /dev/null
    +++ b/mapbox-gl/examples/mapbox-gl.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +
    +    <!-- Mapbox GL -->
    +    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.6.0/mapbox-gl.css' rel='stylesheet' />
    +    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.6.0/mapbox-gl.js'></script>
    +
    +    <title>Mapbox-GL vector tiles example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Mapbox-GL vector tiles example</h4>
    +          <p id="shortdesc">Example of a map that uses <a href="https://github.com/mapbox/mapbox-gl-js">mapbox-gl-js</a> to render Mapbox vector tiles.</p>
    +          <div id="docs">
    +            <p>See the <a href="mapbox-gl.js" target="_blank">mapbox-gl.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">mapbox, webgl, vector tiles</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=mapbox-gl" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/mapbox-gl.js b/mapbox-gl/examples/mapbox-gl.js
    new file mode 100644
    index 0000000000..55295c8aa9
    --- /dev/null
    +++ b/mapbox-gl/examples/mapbox-gl.js
    @@ -0,0 +1,58 @@
    +(function() {
    +  var match = location.search.match(/access_token=([^&\/]*)/);
    +  var accessToken = match && match[1];
    +  mapboxgl.accessToken = accessToken;
    +})();
    +
    +var glMap = new mapboxgl.Map({
    +  style: 'https://www.mapbox.com/mapbox-gl-styles/styles/bright-v7.json',
    +  container: document.createElement('div'),
    +  interactive: false,
    +  attributionControl: false
    +});
    +
    +var mapboxLayer = new ol.layer.Image();
    +
    +glMap.on('render', function() {
    +  mapboxLayer.changed();
    +});
    +
    +var map = new ol.Map({
    +  target: 'map',
    +  layers: [mapboxLayer],
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var mapboxSource = new ol.source.ImageCanvas({
    +  ratio: 1,
    +  projection: 'EPSG:3857',
    +  canvasFunction: function(extent, resolution, pixelRatio,
    +      size, projection) {
    +    var glTransform = glMap.transform;
    +    var width = size[0];
    +    var height = size[1];
    +    if (glTransform.width !== width || glTransform.height !== height) {
    +      glMap.canvas.resize(width, height);
    +      glMap.painter.resize(width, height);
    +      glTransform.width = width;
    +      glTransform.height = height;
    +    }
    +    var glCenter = ol.proj.transform(ol.extent.getCenter(extent),
    +        projection, 'EPSG:4326').reverse();
    +    var glZoom = map.getView().getZoom() - (pixelRatio >= 2 ? 0 : 1);
    +    glMap.setView(glCenter, glZoom, 0);
    +    glMap.render();
    +    // constrain center of ol map, because mapbox map constrains it
    +    var newGlCenter = glMap.getCenter();
    +    if (newGlCenter.lat != glCenter[0]) {
    +      map.getView().setCenter(ol.proj.transform(
    +          [newGlCenter.lng, newGlCenter.lat], 'EPSG:4326', 'EPSG:3857'));
    +    }
    +    return glMap.canvas.canvas;
    +  }
    +});
    +
    +mapboxLayer.setSource(mapboxSource);
    diff --git a/mapbox-gl/examples/mapguide-untiled-require.js b/mapbox-gl/examples/mapguide-untiled-require.js
    new file mode 100644
    index 0000000000..03b06d91aa
    --- /dev/null
    +++ b/mapbox-gl/examples/mapguide-untiled-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Image');
    +goog.require('ol.source.ImageMapGuide');
    diff --git a/mapbox-gl/examples/mapguide-untiled.html b/mapbox-gl/examples/mapguide-untiled.html
    new file mode 100644
    index 0000000000..3a3af560aa
    --- /dev/null
    +++ b/mapbox-gl/examples/mapguide-untiled.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>MapGuide untiled example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">MapGuide untiled example</h4>
    +          <p id="shortdesc">Example of a untiled MapGuide map.</p>
    +          <div id="docs">
    +            <p>See the <a href="mapguide-untiled.js" target="_blank">mapguide-untiled.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">mapguide</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=mapguide-untiled" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/mapguide-untiled.js b/mapbox-gl/examples/mapguide-untiled.js
    new file mode 100644
    index 0000000000..55df9bb1c6
    --- /dev/null
    +++ b/mapbox-gl/examples/mapguide-untiled.js
    @@ -0,0 +1,33 @@
    +var mdf = 'Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition';
    +var agentUrl =
    +    'http://data.mapguide.com/mapguide/mapagent/mapagent.fcgi?USERNAME=Anonymous';
    +var bounds = [
    +  -87.865114442365922,
    +  43.665065564837931,
    +  -87.595394059497067,
    +  43.823852564430069
    +];
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Image({
    +      extent: bounds,
    +      source: new ol.source.ImageMapGuide({
    +        projection: 'EPSG:4326',
    +        url: agentUrl,
    +        useOverlay: false,
    +        metersPerUnit: 111319.4908, //value returned from mapguide
    +        params: {
    +          MAPDEFINITION: mdf,
    +          FORMAT: 'PNG'
    +        },
    +        ratio: 2
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-87.7302542509315, 43.744459064634],
    +    projection: 'EPSG:4326',
    +    zoom: 12
    +  })
    +});
    diff --git a/mapbox-gl/examples/mapquest-require.js b/mapbox-gl/examples/mapquest-require.js
    new file mode 100644
    index 0000000000..6caac26a03
    --- /dev/null
    +++ b/mapbox-gl/examples/mapquest-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Group');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/mapquest.html b/mapbox-gl/examples/mapquest.html
    new file mode 100644
    index 0000000000..c9ef06a26f
    --- /dev/null
    +++ b/mapbox-gl/examples/mapquest.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>MapQuest example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <select id="layer-select">
    +            <option value="Aerial">Aerial</option>
    +            <option value="AerialWithLabels">Aerial with labels</option>
    +            <option value="Road" selected>Road</option>
    +          </select>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">MapQuest example</h4>
    +          <p id="shortdesc">Example of a MapQuest map.</p>
    +          <div id="docs">
    +            <p>See the <a href="mapquest.js" target="_blank">mapquest.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">mapquest</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=mapquest" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/mapquest.js b/mapbox-gl/examples/mapquest.js
    new file mode 100644
    index 0000000000..3538f2a8d2
    --- /dev/null
    +++ b/mapbox-gl/examples/mapquest.js
    @@ -0,0 +1,43 @@
    +var layers = [
    +  new ol.layer.Tile({
    +    style: 'Road',
    +    source: new ol.source.MapQuest({layer: 'osm'})
    +  }),
    +  new ol.layer.Tile({
    +    style: 'Aerial',
    +    visible: false,
    +    source: new ol.source.MapQuest({layer: 'sat'})
    +  }),
    +  new ol.layer.Group({
    +    style: 'AerialWithLabels',
    +    visible: false,
    +    layers: [
    +      new ol.layer.Tile({
    +        source: new ol.source.MapQuest({layer: 'sat'})
    +      }),
    +      new ol.layer.Tile({
    +        source: new ol.source.MapQuest({layer: 'hyb'})
    +      })
    +    ]
    +  })
    +];
    +
    +var map = new ol.Map({
    +  layers: layers,
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform(
    +        [-73.979378, 40.702222], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 9
    +  })
    +});
    +
    +$('#layer-select').change(function() {
    +  var style = $(this).find(':selected').val();
    +  var i, ii;
    +  for (i = 0, ii = layers.length; i < ii; ++i) {
    +    layers[i].set('visible', (layers[i].get('style') == style));
    +  }
    +});
    +$('#layer-select').trigger('change');
    diff --git a/mapbox-gl/examples/measure-require.js b/mapbox-gl/examples/measure-require.js
    new file mode 100644
    index 0000000000..7ed328465e
    --- /dev/null
    +++ b/mapbox-gl/examples/measure-require.js
    @@ -0,0 +1,16 @@
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.Sphere');
    +goog.require('ol.View');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.interaction.Draw');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.proj');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/measure.html b/mapbox-gl/examples/measure.html
    new file mode 100644
    index 0000000000..0341173c75
    --- /dev/null
    +++ b/mapbox-gl/examples/measure.html
    @@ -0,0 +1,98 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Measure example</title>
    +    <style>
    +      .tooltip {
    +        position: relative;
    +        background: rgba(0, 0, 0, 0.5);
    +        border-radius: 4px;
    +        color: white;
    +        padding: 4px 8px;
    +        opacity: 0.7;
    +        white-space: nowrap;
    +      }
    +      .tooltip-measure {
    +        opacity: 1;
    +        font-weight: bold;
    +      }
    +      .tooltip-static {
    +        background-color: #ffcc33;
    +        color: black;
    +        border: 1px solid white;
    +      }
    +      .tooltip-measure:before,
    +      .tooltip-static:before {
    +        border-top: 6px solid rgba(0, 0, 0, 0.5);
    +        border-right: 6px solid transparent;
    +        border-left: 6px solid transparent;
    +        content: "";
    +        position: absolute;
    +        bottom: -6px;
    +        margin-left: -7px;
    +        left: 50%;
    +      }
    +      .tooltip-static:before {
    +        border-top-color: #ffcc33;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Measure example</h4>
    +          <p id="shortdesc">Example of using the
    +          ol.interaction.Draw interaction for creating simple
    +          measuring application. </p>
    +          <form class="form-inline">
    +            <label>Geometry type &nbsp;</label>
    +              <select id="type">
    +                <option value="length">Length</option>
    +                <option value="area">Area</option>
    +              </select>
    +              <label class="checkbox"><input type="checkbox" id="geodesic"/>use geodesic measures</label>
    +          </form>
    +
    +          <div id="docs">
    +              <p><i>NOTE: If use geodesic measures is not checked, measure is done in simple way on projected plane. Earth
    +              curvature is not taken into account</i></p>
    +            <p>See the <a href="measure.js" target="_blank">measure.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">draw, edit, measure, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=measure" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/measure.js b/mapbox-gl/examples/measure.js
    new file mode 100644
    index 0000000000..fac9d1abaa
    --- /dev/null
    +++ b/mapbox-gl/examples/measure.js
    @@ -0,0 +1,280 @@
    +var wgs84Sphere = new ol.Sphere(6378137);
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var source = new ol.source.Vector();
    +
    +var vector = new ol.layer.Vector({
    +  source: source,
    +  style: new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 255, 0.2)'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#ffcc33',
    +      width: 2
    +    }),
    +    image: new ol.style.Circle({
    +      radius: 7,
    +      fill: new ol.style.Fill({
    +        color: '#ffcc33'
    +      })
    +    })
    +  })
    +});
    +
    +
    +/**
    + * Currently drawn feature.
    + * @type {ol.Feature}
    + */
    +var sketch;
    +
    +
    +/**
    + * The help tooltip element.
    + * @type {Element}
    + */
    +var helpTooltipElement;
    +
    +
    +/**
    + * Overlay to show the help messages.
    + * @type {ol.Overlay}
    + */
    +var helpTooltip;
    +
    +
    +/**
    + * The measure tooltip element.
    + * @type {Element}
    + */
    +var measureTooltipElement;
    +
    +
    +/**
    + * Overlay to show the measurement.
    + * @type {ol.Overlay}
    + */
    +var measureTooltip;
    +
    +
    +/**
    + * Message to show when the user is drawing a polygon.
    + * @type {string}
    + */
    +var continuePolygonMsg = 'Click to continue drawing the polygon';
    +
    +
    +/**
    + * Message to show when the user is drawing a line.
    + * @type {string}
    + */
    +var continueLineMsg = 'Click to continue drawing the line';
    +
    +
    +/**
    + * Handle pointer move.
    + * @param {ol.MapBrowserEvent} evt
    + */
    +var pointerMoveHandler = function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  /** @type {string} */
    +  var helpMsg = 'Click to start drawing';
    +  /** @type {ol.Coordinate|undefined} */
    +  var tooltipCoord = evt.coordinate;
    +
    +  if (sketch) {
    +    var output;
    +    var geom = (sketch.getGeometry());
    +    if (geom instanceof ol.geom.Polygon) {
    +      output = formatArea(/** @type {ol.geom.Polygon} */ (geom));
    +      helpMsg = continuePolygonMsg;
    +      tooltipCoord = geom.getInteriorPoint().getCoordinates();
    +    } else if (geom instanceof ol.geom.LineString) {
    +      output = formatLength( /** @type {ol.geom.LineString} */ (geom));
    +      helpMsg = continueLineMsg;
    +      tooltipCoord = geom.getLastCoordinate();
    +    }
    +    measureTooltipElement.innerHTML = output;
    +    measureTooltip.setPosition(tooltipCoord);
    +  }
    +
    +  helpTooltipElement.innerHTML = helpMsg;
    +  helpTooltip.setPosition(evt.coordinate);
    +};
    +
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-11000000, 4600000],
    +    zoom: 15
    +  })
    +});
    +
    +map.on('pointermove', pointerMoveHandler);
    +
    +var typeSelect = document.getElementById('type');
    +var geodesicCheckbox = document.getElementById('geodesic');
    +
    +var draw; // global so we can remove it later
    +function addInteraction() {
    +  var type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString');
    +  draw = new ol.interaction.Draw({
    +    source: source,
    +    type: /** @type {ol.geom.GeometryType} */ (type),
    +    style: new ol.style.Style({
    +      fill: new ol.style.Fill({
    +        color: 'rgba(255, 255, 255, 0.2)'
    +      }),
    +      stroke: new ol.style.Stroke({
    +        color: 'rgba(0, 0, 0, 0.5)',
    +        lineDash: [10, 10],
    +        width: 2
    +      }),
    +      image: new ol.style.Circle({
    +        radius: 5,
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(0, 0, 0, 0.7)'
    +        }),
    +        fill: new ol.style.Fill({
    +          color: 'rgba(255, 255, 255, 0.2)'
    +        })
    +      })
    +    })
    +  });
    +  map.addInteraction(draw);
    +
    +  createMeasureTooltip();
    +  createHelpTooltip();
    +
    +  draw.on('drawstart',
    +      function(evt) {
    +        // set sketch
    +        sketch = evt.feature;
    +      }, this);
    +
    +  draw.on('drawend',
    +      function(evt) {
    +        measureTooltipElement.className = 'tooltip tooltip-static';
    +        measureTooltip.setOffset([0, -7]);
    +        // unset sketch
    +        sketch = null;
    +        // unset tooltip so that a new one can be created
    +        measureTooltipElement = null;
    +        createMeasureTooltip();
    +      }, this);
    +}
    +
    +
    +/**
    + * Creates a new help tooltip
    + */
    +function createHelpTooltip() {
    +  if (helpTooltipElement) {
    +    helpTooltipElement.parentNode.removeChild(helpTooltipElement);
    +  }
    +  helpTooltipElement = document.createElement('div');
    +  helpTooltipElement.className = 'tooltip';
    +  helpTooltip = new ol.Overlay({
    +    element: helpTooltipElement,
    +    offset: [15, 0],
    +    positioning: 'center-left'
    +  });
    +  map.addOverlay(helpTooltip);
    +}
    +
    +
    +/**
    + * Creates a new measure tooltip
    + */
    +function createMeasureTooltip() {
    +  if (measureTooltipElement) {
    +    measureTooltipElement.parentNode.removeChild(measureTooltipElement);
    +  }
    +  measureTooltipElement = document.createElement('div');
    +  measureTooltipElement.className = 'tooltip tooltip-measure';
    +  measureTooltip = new ol.Overlay({
    +    element: measureTooltipElement,
    +    offset: [0, -15],
    +    positioning: 'bottom-center'
    +  });
    +  map.addOverlay(measureTooltip);
    +}
    +
    +
    +/**
    + * Let user change the geometry type.
    + * @param {Event} e Change event.
    + */
    +typeSelect.onchange = function(e) {
    +  map.removeInteraction(draw);
    +  addInteraction();
    +};
    +
    +
    +/**
    + * format length output
    + * @param {ol.geom.LineString} line
    + * @return {string}
    + */
    +var formatLength = function(line) {
    +  var length;
    +  if (geodesicCheckbox.checked) {
    +    var coordinates = line.getCoordinates();
    +    length = 0;
    +    var sourceProj = map.getView().getProjection();
    +    for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
    +      var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
    +      var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
    +      length += wgs84Sphere.haversineDistance(c1, c2);
    +    }
    +  } else {
    +    length = Math.round(line.getLength() * 100) / 100;
    +  }
    +  var output;
    +  if (length > 100) {
    +    output = (Math.round(length / 1000 * 100) / 100) +
    +        ' ' + 'km';
    +  } else {
    +    output = (Math.round(length * 100) / 100) +
    +        ' ' + 'm';
    +  }
    +  return output;
    +};
    +
    +
    +/**
    + * format length output
    + * @param {ol.geom.Polygon} polygon
    + * @return {string}
    + */
    +var formatArea = function(polygon) {
    +  var area;
    +  if (geodesicCheckbox.checked) {
    +    var sourceProj = map.getView().getProjection();
    +    var geom = /** @type {ol.geom.Polygon} */(polygon.clone().transform(
    +        sourceProj, 'EPSG:4326'));
    +    var coordinates = geom.getLinearRing(0).getCoordinates();
    +    area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
    +  } else {
    +    area = polygon.getArea();
    +  }
    +  var output;
    +  if (area > 10000) {
    +    output = (Math.round(area / 1000000 * 100) / 100) +
    +        ' ' + 'km<sup>2</sup>';
    +  } else {
    +    output = (Math.round(area * 100) / 100) +
    +        ' ' + 'm<sup>2</sup>';
    +  }
    +  return output;
    +};
    +
    +addInteraction();
    diff --git a/mapbox-gl/examples/min-max-resolution-require.js b/mapbox-gl/examples/min-max-resolution-require.js
    new file mode 100644
    index 0000000000..9c98bd6537
    --- /dev/null
    +++ b/mapbox-gl/examples/min-max-resolution-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/min-max-resolution.html b/mapbox-gl/examples/min-max-resolution.html
    new file mode 100644
    index 0000000000..0f79406311
    --- /dev/null
    +++ b/mapbox-gl/examples/min-max-resolution.html
    @@ -0,0 +1,60 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Min/max resolution example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Min/max resolution example</h4>
    +          <p id="shortdesc">Show/hide layers depending on current view resolution.</p>
    +          <div id="docs">
    +            <p>
    +            Zoom in twice: the MapBox layer should hide whereas the OSM layer should be shown.
    +            </p>
    +            <p>
    +            If you continue to zoom in, you'll see the OSM layer also disappear.
    +            </p>
    +            <p>
    +            The rendering of the layers are here controlled using minResolution and maxResolution options.
    +            </p>
    +            <p>See the <a href="min-max-resolution.js" target="_blank">min-max-resolution.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">minResolution, maxResolution, resolution</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=min-max-resolution" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/min-max-resolution.js b/mapbox-gl/examples/min-max-resolution.js
    new file mode 100644
    index 0000000000..79ca09c5b7
    --- /dev/null
    +++ b/mapbox-gl/examples/min-max-resolution.js
    @@ -0,0 +1,32 @@
    +/**
    + * Create the map.
    + */
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM(),
    +      minResolution: 200,
    +      maxResolution: 2000
    +    }),
    +    new ol.layer.Tile({
    +      source: new ol.source.TileJSON({
    +        url: 'http://api.tiles.mapbox.com/v3/' +
    +            'mapbox.natural-earth-hypso-bathy.jsonp',
    +        crossOrigin: 'anonymous'
    +      }),
    +      minResolution: 2000,
    +      maxResolution: 20000
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [653600, 5723680],
    +    zoom: 5
    +  })
    +});
    diff --git a/mapbox-gl/examples/mobile-full-screen-require.js b/mapbox-gl/examples/mobile-full-screen-require.js
    new file mode 100644
    index 0000000000..8920461f7b
    --- /dev/null
    +++ b/mapbox-gl/examples/mobile-full-screen-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Geolocation');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/mobile-full-screen.html b/mapbox-gl/examples/mobile-full-screen.html
    new file mode 100644
    index 0000000000..1afd9087f0
    --- /dev/null
    +++ b/mapbox-gl/examples/mobile-full-screen.html
    @@ -0,0 +1,30 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <title>Mobile full screen example</title>
    +    <style type="text/css">
    +      html, body, .map {
    +        margin: 0;
    +        padding: 0;
    +        width: 100%;
    +        height: 100%;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +    <div id="map" class="map"></div>
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="http://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.3/fastclick.js" type="text/javascript"></script>
    +    <script src="loader.js?id=mobile-full-screen" type="text/javascript"></script>
    +
    +    <div style="display: none;">
    +      <div id="title">Mobile full screen example</div>
    +      <div id="shortdesc">Example of a full screen map.</div>
    +      <div id="tags">fullscreen, bing, geolocation, mobile</div>
    +    </div>
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/mobile-full-screen.js b/mapbox-gl/examples/mobile-full-screen.js
    new file mode 100644
    index 0000000000..ae81ebd06f
    --- /dev/null
    +++ b/mapbox-gl/examples/mobile-full-screen.js
    @@ -0,0 +1,35 @@
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 2
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'Road'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: view
    +});
    +
    +var geolocation = new ol.Geolocation({
    +  projection: view.getProjection(),
    +  tracking: true
    +});
    +geolocation.once('change:position', function() {
    +  view.setCenter(geolocation.getPosition());
    +  view.setResolution(2.388657133911758);
    +});
    +
    +// Use FastClick to eliminate the 300ms delay between a physical tap
    +// and the firing of a click event on mobile browsers.
    +// See http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away
    +// for more information.
    +$(function() {
    +  FastClick.attach(document.body);
    +});
    diff --git a/mapbox-gl/examples/modify-features-require.js b/mapbox-gl/examples/modify-features-require.js
    new file mode 100644
    index 0000000000..67e747343f
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-features-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.Modify');
    +goog.require('ol.interaction.Select');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/modify-features.html b/mapbox-gl/examples/modify-features.html
    new file mode 100644
    index 0000000000..1a569054d8
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-features.html
    @@ -0,0 +1,53 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Modify features example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Modify features example</h4>
    +          <p id="shortdesc">Editing features with the modify interaction.</p>
    +          <div id="docs">
    +            <p>This example demonstrates how the modify and select interactions can be used together.  Zoom in to an area of interest and select a feature for editing.  Then drag points around to modify the feature.  You can preserve topology by selecting multiple features before editing (<code>Shift</code>+click to select multiple features).</p>
    +            <p>See the <a href="modify-features.js" target="_blank">modify-features.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">modify, edit, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="loader.js?id=modify-features" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/modify-features.js b/mapbox-gl/examples/modify-features.js
    new file mode 100644
    index 0000000000..b4cf1db76c
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-features.js
    @@ -0,0 +1,28 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({
    +    layer: 'sat'
    +  })
    +});
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/countries.geojson'
    +  })
    +});
    +
    +var select = new ol.interaction.Select();
    +
    +var modify = new ol.interaction.Modify({
    +  features: select.getFeatures()
    +});
    +
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([select, modify]),
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/modify-test-require.js b/mapbox-gl/examples/modify-test-require.js
    new file mode 100644
    index 0000000000..7bb7805a1b
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-test-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.Modify');
    +goog.require('ol.interaction.Select');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/modify-test.html b/mapbox-gl/examples/modify-test.html
    new file mode 100644
    index 0000000000..c4b3b61012
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-test.html
    @@ -0,0 +1,57 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Modify features test</title>
    +    <style>
    +      .map {
    +        background: grey;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Modify features test</h4>
    +          <p id="shortdesc">Example for testing feature modification.</p>
    +          <div id="docs">
    +            <p>See the <a href="modify-test.js" target="_blank">modify-test.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">modify, edit, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="loader.js?id=modify-test" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/modify-test.js b/mapbox-gl/examples/modify-test.js
    new file mode 100644
    index 0000000000..5ad3e386d2
    --- /dev/null
    +++ b/mapbox-gl/examples/modify-test.js
    @@ -0,0 +1,237 @@
    +var styleFunction = (function() {
    +  /* jshint -W069 */
    +  var styles = {};
    +  var image = new ol.style.Circle({
    +    radius: 5,
    +    fill: null,
    +    stroke: new ol.style.Stroke({color: 'orange', width: 2})
    +  });
    +  styles['Point'] = [new ol.style.Style({image: image})];
    +  styles['Polygon'] = [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'blue',
    +      width: 3
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0, 0, 255, 0.1)'
    +    })
    +  })];
    +  styles['MultiLinestring'] = [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'green',
    +      width: 3
    +    })
    +  })];
    +  styles['MultiPolygon'] = [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'yellow',
    +      width: 1
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 255, 0, 0.1)'
    +    })
    +  })];
    +  styles['default'] = [new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'red',
    +      width: 3
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(255, 0, 0, 0.1)'
    +    }),
    +    image: image
    +  })];
    +  return function(feature, resolution) {
    +    return styles[feature.getGeometry().getType()] || styles['default'];
    +  };
    +  /* jshint +W069 */
    +})();
    +
    +var source = new ol.source.GeoJSON(
    +    /** @type {olx.source.GeoJSONOptions} */ ({
    +      object: {
    +        'type': 'FeatureCollection',
    +        'crs': {
    +          'type': 'name',
    +          'properties': {
    +            'name': 'EPSG:3857'
    +          }
    +        },
    +        'features': [
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'Point',
    +              'coordinates': [0, 0]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'MultiPoint',
    +              'coordinates': [[-2e6, 0], [0, -2e6]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'LineString',
    +              'coordinates': [[4e6, -2e6], [8e6, 2e6], [9e6, 2e6]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'LineString',
    +              'coordinates': [[4e6, -2e6], [8e6, 2e6], [8e6, 3e6]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'Polygon',
    +              'coordinates': [[[-5e6, -1e6], [-4e6, 1e6],
    +                  [-3e6, -1e6], [-5e6, -1e6]], [[-4.5e6, -0.5e6],
    +                  [-3.5e6, -0.5e6], [-4e6, 0.5e6], [-4.5e6, -0.5e6]]]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'MultiLineString',
    +              'coordinates': [
    +                [[-1e6, -7.5e5], [-1e6, 7.5e5]],
    +                [[1e6, -7.5e5], [1e6, 7.5e5]],
    +                [[-7.5e5, -1e6], [7.5e5, -1e6]],
    +                [[-7.5e5, 1e6], [7.5e5, 1e6]]
    +              ]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'MultiPolygon',
    +              'coordinates': [
    +                [[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6],
    +                    [-3e6, 6e6], [-5e6, 6e6]]],
    +                [[[-2e6, 6e6], [-2e6, 8e6], [0, 8e6],
    +                    [0, 6e6], [-2e6, 6e6]]],
    +                [[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6],
    +                    [3e6, 6e6], [1e6, 6e6]]]
    +              ]
    +            }
    +          },
    +          {
    +            'type': 'Feature',
    +            'geometry': {
    +              'type': 'GeometryCollection',
    +              'geometries': [
    +                {
    +                  'type': 'LineString',
    +                  'coordinates': [[-5e6, -5e6], [0, -5e6]]
    +                },
    +                {
    +                  'type': 'Point',
    +                  'coordinates': [4e6, -5e6]
    +                },
    +                {
    +                  'type': 'Polygon',
    +                  'coordinates': [
    +                    [[1e6, -6e6], [2e6, -4e6], [3e6, -6e6], [1e6, -6e6]]
    +                  ]
    +                }
    +              ]
    +            }
    +          }
    +        ]
    +      }
    +    }));
    +
    +var layer = new ol.layer.Vector({
    +  source: source,
    +  style: styleFunction
    +});
    +
    +var overlayStyle = (function() {
    +  /* jshint -W069 */
    +  var styles = {};
    +  styles['Polygon'] = [
    +    new ol.style.Style({
    +      fill: new ol.style.Fill({
    +        color: [255, 255, 255, 0.5]
    +      })
    +    }),
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: [255, 255, 255, 1],
    +        width: 5
    +      })
    +    }),
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: [0, 153, 255, 1],
    +        width: 3
    +      })
    +    })
    +  ];
    +  styles['MultiPolygon'] = styles['Polygon'];
    +
    +  styles['LineString'] = [
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: [255, 255, 255, 1],
    +        width: 5
    +      })
    +    }),
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: [0, 153, 255, 1],
    +        width: 3
    +      })
    +    })
    +  ];
    +  styles['MultiLineString'] = styles['LineString'];
    +
    +  styles['Point'] = [
    +    new ol.style.Style({
    +      image: new ol.style.Circle({
    +        radius: 7,
    +        fill: new ol.style.Fill({
    +          color: [0, 153, 255, 1]
    +        }),
    +        stroke: new ol.style.Stroke({
    +          color: [255, 255, 255, 0.75],
    +          width: 1.5
    +        })
    +      }),
    +      zIndex: 100000
    +    })
    +  ];
    +  styles['MultiPoint'] = styles['Point'];
    +
    +  styles['GeometryCollection'] = styles['Polygon'].concat(styles['Point']);
    +
    +  return function(feature, resolution) {
    +    return styles[feature.getGeometry().getType()];
    +  };
    +  /* jshint +W069 */
    +})();
    +
    +var select = new ol.interaction.Select({
    +  style: overlayStyle
    +});
    +
    +var modify = new ol.interaction.Modify({
    +  features: select.getFeatures(),
    +  style: overlayStyle
    +});
    +
    +var map = new ol.Map({
    +  interactions: ol.interaction.defaults().extend([select, modify]),
    +  layers: [layer],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 1000000],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/mouse-position-require.js b/mapbox-gl/examples/mouse-position-require.js
    new file mode 100644
    index 0000000000..3f565bbc82
    --- /dev/null
    +++ b/mapbox-gl/examples/mouse-position-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.MousePosition');
    +goog.require('ol.coordinate');
    +goog.require('ol.dom.Input');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/mouse-position.html b/mapbox-gl/examples/mouse-position.html
    new file mode 100644
    index 0000000000..de9b13e996
    --- /dev/null
    +++ b/mapbox-gl/examples/mouse-position.html
    @@ -0,0 +1,59 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Mouse position example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span6">
    +          <h4 id="title">Mouse position example</h4>
    +          <p id="shortdesc">Example of a mouse position control, outside the map.</p>
    +          <div id="docs">
    +            <p>See the <a href="mouse-position.js" target="_blank">mouse-position.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">mouse-position, openstreetmap</div>
    +          <form>
    +            <label>Projection </label>
    +            <select id="projection">
    +              <option value="EPSG:4326">EPSG:4326</option>
    +              <option value="EPSG:3857">EPSG:3857</option>
    +            </select>
    +            <label>Precision </label>
    +            <input id="precision" type="number" min="0" max="12" value="4"/>
    +          </form>
    +        </div>
    +        <div class="span6" id="mouse-position">&nbsp;</div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=mouse-position" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/mouse-position.js b/mapbox-gl/examples/mouse-position.js
    new file mode 100644
    index 0000000000..254d9f5717
    --- /dev/null
    +++ b/mapbox-gl/examples/mouse-position.js
    @@ -0,0 +1,46 @@
    +var mousePositionControl = new ol.control.MousePosition({
    +  coordinateFormat: ol.coordinate.createStringXY(4),
    +  projection: 'EPSG:4326',
    +  // comment the following two lines to have the mouse position
    +  // be placed within the map.
    +  className: 'custom-mouse-position',
    +  target: document.getElementById('mouse-position'),
    +  undefinedHTML: '&nbsp;'
    +});
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }).extend([mousePositionControl]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var projectionSelect = new ol.dom.Input(document.getElementById('projection'));
    +projectionSelect.bindTo('value', mousePositionControl, 'projection')
    +  .transform(
    +    function(code) {
    +      // projectionSelect.value -> mousePositionControl.projection
    +      return ol.proj.get(/** @type {string} */ (code));
    +    },
    +    function(projection) {
    +      // mousePositionControl.projection -> projectionSelect.value
    +      return projection.getCode();
    +    });
    +
    +var precisionInput = document.getElementById('precision');
    +precisionInput.addEventListener('change', function() {
    +  var format = ol.coordinate.createStringXY(precisionInput.valueAsNumber);
    +  mousePositionControl.setCoordinateFormat(format);
    +}, false);
    diff --git a/mapbox-gl/examples/moveend-require.js b/mapbox-gl/examples/moveend-require.js
    new file mode 100644
    index 0000000000..a09ff5c753
    --- /dev/null
    +++ b/mapbox-gl/examples/moveend-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/moveend.html b/mapbox-gl/examples/moveend.html
    new file mode 100644
    index 0000000000..f88b248974
    --- /dev/null
    +++ b/mapbox-gl/examples/moveend.html
    @@ -0,0 +1,54 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Moveend Example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <h4 id="title">Move end example</h4>
    +          <p id="shortdesc">Use of the moveend event.</p>
    +          <div id="docs">
    +            <p>In this example, a listener is registered for the map's <code>moveend</code> event.  Whenever this listener is called, it updates the inputs below with the map extent in decimal degrees.</p>
    +            <p>See the <a href="moveend.js" target="_blank">moveend.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">moveend, map, event</div>
    +          <label>top</label><input type="text" id="top">
    +          <label>right</label><input type="text" id="right">
    +          <label>bottom</label><input type="text" id="bottom">
    +          <label>left</label><input type="text" id="left">
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=moveend" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/moveend.js b/mapbox-gl/examples/moveend.js
    new file mode 100644
    index 0000000000..127fd00801
    --- /dev/null
    +++ b/mapbox-gl/examples/moveend.js
    @@ -0,0 +1,42 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +function display(id, value) {
    +  document.getElementById(id).value = value.toFixed(2);
    +}
    +
    +function wrapLon(value) {
    +  var worlds = Math.floor((value + 180) / 360);
    +  return value - (worlds * 360);
    +}
    +
    +function onMoveEnd(evt) {
    +  var map = evt.map;
    +  var extent = map.getView().calculateExtent(map.getSize());
    +  var bottomLeft = ol.proj.transform(ol.extent.getBottomLeft(extent),
    +      'EPSG:3857', 'EPSG:4326');
    +  var topRight = ol.proj.transform(ol.extent.getTopRight(extent),
    +      'EPSG:3857', 'EPSG:4326');
    +  display('left', wrapLon(bottomLeft[0]));
    +  display('bottom', bottomLeft[1]);
    +  display('right', wrapLon(topRight[0]));
    +  display('top', topRight[1]);
    +}
    +
    +map.on('moveend', onMoveEnd);
    diff --git a/mapbox-gl/examples/navigation-controls-require.js b/mapbox-gl/examples/navigation-controls-require.js
    new file mode 100644
    index 0000000000..66600c6ee2
    --- /dev/null
    +++ b/mapbox-gl/examples/navigation-controls-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.ZoomToExtent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/navigation-controls.html b/mapbox-gl/examples/navigation-controls.html
    new file mode 100644
    index 0000000000..f4d3ba0a85
    --- /dev/null
    +++ b/mapbox-gl/examples/navigation-controls.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Navigation controls example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Navigation controls example</h4>
    +          <p id="shortdesc">Shows how to add navigation controls.</p>
    +            The following navigation controls are added to the map:
    +            <ul>
    +              <li><code>ol.control.Zoom</code> (added by default)</li>
    +              <li><code>ol.control.ZoomToExtent</code></li>
    +            </ul>
    +          <div id="docs">
    +            <p>See the <a href="navigation-controls.js" target="_blank">navigation-controls.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">control, navigation, extent</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=navigation-controls" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/navigation-controls.js b/mapbox-gl/examples/navigation-controls.js
    new file mode 100644
    index 0000000000..a12aff8eec
    --- /dev/null
    +++ b/mapbox-gl/examples/navigation-controls.js
    @@ -0,0 +1,25 @@
    +var map = new ol.Map({
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }).extend([
    +    new ol.control.ZoomToExtent({
    +      extent: [
    +        813079.7791264898, 5929220.284081122,
    +        848966.9639063801, 5936863.986909639
    +      ]
    +    })
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/overlay-require.js b/mapbox-gl/examples/overlay-require.js
    new file mode 100644
    index 0000000000..554365cb07
    --- /dev/null
    +++ b/mapbox-gl/examples/overlay-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.View');
    +goog.require('ol.coordinate');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/overlay.html b/mapbox-gl/examples/overlay.html
    new file mode 100644
    index 0000000000..c3351e0d22
    --- /dev/null
    +++ b/mapbox-gl/examples/overlay.html
    @@ -0,0 +1,82 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      #marker {
    +        width: 20px;
    +        height: 20px;
    +        border: 1px solid #088;
    +        border-radius: 10px;
    +        background-color: #0FF;
    +        opacity: 0.5;
    +      }
    +      #vienna {
    +        text-decoration: none;
    +        color: white;
    +        font-size: 11pt;
    +        font-weight: bold;
    +        text-shadow: black 0.1em 0.1em 0.2em;
    +      }
    +      .popover-content {
    +        min-width: 180px;
    +      }
    +    </style>
    +    <title>Overlay example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Overlay example</h4>
    +          <p id="shortdesc">Demonstrates overlays.</p>
    +          <div id="docs">
    +            <p>The popups are created using
    +              <a href="http://twitter.github.com/bootstrap/javascript.html#popovers">Popovers</a> from Bootstrap.</p>
    +            <p>See the <a href="overlay.js" target="_blank">overlay.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">overlay, popup, bootstrap, popover, mapquest, openaerial</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <div style="display: none;">
    +      <!-- Clickable label for Vienna -->
    +      <a class="overlay" id="vienna" target="_blank" href="http://en.wikipedia.org/wiki/Vienna">Vienna</a>
    +      <div id="marker" title="Marker"></div>
    +      <!-- Popup -->
    +      <div id="popup" title="Welcome to ol3"></div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=overlay" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/overlay.js b/mapbox-gl/examples/overlay.js
    new file mode 100644
    index 0000000000..4890692ff3
    --- /dev/null
    +++ b/mapbox-gl/examples/overlay.js
    @@ -0,0 +1,55 @@
    +var layer = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var map = new ol.Map({
    +  layers: [layer],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var pos = ol.proj.transform([16.3725, 48.208889], 'EPSG:4326', 'EPSG:3857');
    +
    +// Vienna marker
    +var marker = new ol.Overlay({
    +  position: pos,
    +  positioning: 'center-center',
    +  element: document.getElementById('marker'),
    +  stopEvent: false
    +});
    +map.addOverlay(marker);
    +
    +// Vienna label
    +var vienna = new ol.Overlay({
    +  position: pos,
    +  element: document.getElementById('vienna')
    +});
    +map.addOverlay(vienna);
    +
    +// Popup showing the position the user clicked
    +var popup = new ol.Overlay({
    +  element: document.getElementById('popup')
    +});
    +map.addOverlay(popup);
    +
    +map.on('click', function(evt) {
    +  var element = popup.getElement();
    +  var coordinate = evt.coordinate;
    +  var hdms = ol.coordinate.toStringHDMS(ol.proj.transform(
    +      coordinate, 'EPSG:3857', 'EPSG:4326'));
    +
    +  $(element).popover('destroy');
    +  popup.setPosition(coordinate);
    +  // the keys are quoted to prevent renaming in ADVANCED mode.
    +  $(element).popover({
    +    'placement': 'top',
    +    'animation': false,
    +    'html': true,
    +    'content': '<p>The location you clicked was:</p><code>' + hdms + '</code>'
    +  });
    +  $(element).popover('show');
    +});
    diff --git a/mapbox-gl/examples/overviewmap-custom-require.js b/mapbox-gl/examples/overviewmap-custom-require.js
    new file mode 100644
    index 0000000000..33a0c96c20
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap-custom-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.OverviewMap');
    +goog.require('ol.interaction');
    +goog.require('ol.interaction.DragRotateAndZoom');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/overviewmap-custom.html b/mapbox-gl/examples/overviewmap-custom.html
    new file mode 100644
    index 0000000000..d00be1357d
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap-custom.html
    @@ -0,0 +1,77 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      .ol-custom-overviewmap,
    +      .ol-custom-overviewmap.ol-uncollapsible {
    +        bottom: auto;
    +        left: auto;
    +        right: 0;
    +        top: 0;
    +      }
    +
    +      .ol-custom-overviewmap:not(.ol-collapsed)  {
    +        border: 1px solid black;
    +      }
    +
    +      .ol-custom-overviewmap .ol-overviewmap-map {
    +        border: none;
    +        width: 300px;
    +      }
    +
    +      .ol-custom-overviewmap .ol-overviewmap-box {
    +        border: 2px solid red;
    +      }
    +
    +      .ol-custom-overviewmap:not(.ol-collapsed) button{
    +        bottom: auto;
    +        left: auto;
    +        right: 1px;
    +        top: 1px;
    +      }
    +
    +      .ol-rotate {
    +        top: 170px;
    +        right: 0;
    +      }
    +
    +    </style>
    +    <title>ol3 OverviewMap control with advanced customization example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +      <h4 id="title">OverviewMap control, advanced</h4>
    +      <div id="map" class="map"></div>
    +
    +      <div class="row-fluid">
    +        <p id="shortdesc">Example of OverviewMap control with advanced customization.</p>
    +        <div id="docs">
    +          <p>See the <a href="overviewmap-custom.js" target="_blank">overviewmap-custom.js source</a> to see how this is done.</p>
    +          <p>This example demonstrates how you can customize the overviewmap control using its supported options as well as defining custom CSS.  You can also rotate the map using the shift key to see how the overview map reacts.</p>
    +        </div>
    +        <div id="tags">overview, overviewmap</div>
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=overviewmap-custom" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/overviewmap-custom.js b/mapbox-gl/examples/overviewmap-custom.js
    new file mode 100644
    index 0000000000..3f0e7e4027
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap-custom.js
    @@ -0,0 +1,33 @@
    +var overviewMapControl = new ol.control.OverviewMap({
    +  // see in overviewmap-custom.html to see the custom CSS used
    +  className: 'ol-overviewmap ol-custom-overviewmap',
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM({
    +        'url': '//{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
    +      })
    +    })
    +  ],
    +  collapseLabel: '\u00BB',
    +  label: '\u00AB',
    +  collapsed: false
    +});
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    overviewMapControl
    +  ]),
    +  interactions: ol.interaction.defaults().extend([
    +    new ol.interaction.DragRotateAndZoom()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [500000, 6000000],
    +    zoom: 7
    +  })
    +});
    diff --git a/mapbox-gl/examples/overviewmap-require.js b/mapbox-gl/examples/overviewmap-require.js
    new file mode 100644
    index 0000000000..792d8dc746
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.OverviewMap');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/overviewmap.html b/mapbox-gl/examples/overviewmap.html
    new file mode 100644
    index 0000000000..ca0a0da1a4
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap.html
    @@ -0,0 +1,41 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>ol3 OverviewMap control example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +      <h4 id="title">OverviewMap control</h4>
    +      <div id="map" class="map"></div>
    +
    +      <div class="row-fluid">
    +        <p id="shortdesc">Example of OverviewMap control.</p>
    +        <div id="docs">
    +          <p>See the <a href="overviewmap.js" target="_blank">overviewmap.js source</a> to see how this is done.</p>
    +        </div>
    +        <div id="tags">overview, overviewmap</div>
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=overviewmap" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/overviewmap.js b/mapbox-gl/examples/overviewmap.js
    new file mode 100644
    index 0000000000..691bde6b34
    --- /dev/null
    +++ b/mapbox-gl/examples/overviewmap.js
    @@ -0,0 +1,15 @@
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.OverviewMap()
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [500000, 6000000],
    +    zoom: 7
    +  })
    +});
    diff --git a/mapbox-gl/examples/polygon-styles-require.js b/mapbox-gl/examples/polygon-styles-require.js
    new file mode 100644
    index 0000000000..aae34ee2ef
    --- /dev/null
    +++ b/mapbox-gl/examples/polygon-styles-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/polygon-styles.html b/mapbox-gl/examples/polygon-styles.html
    new file mode 100644
    index 0000000000..5464d2f097
    --- /dev/null
    +++ b/mapbox-gl/examples/polygon-styles.html
    @@ -0,0 +1,57 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Custom styles for polygons</title>
    +    <style>
    +      .map {
    +        background: grey;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Custom styles for polygons</h4>
    +          <p id="shortdesc">Showing the vertices of a polygon with a custom style geometry.</p>
    +          <div id="docs">
    +            <p>See the <a href="polygon-styles.js" target="_blank">polygon-styles.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">polygon, vector, style, GeometryFunction</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="loader.js?id=polygon-styles" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/polygon-styles.js b/mapbox-gl/examples/polygon-styles.js
    new file mode 100644
    index 0000000000..0ea23aab2d
    --- /dev/null
    +++ b/mapbox-gl/examples/polygon-styles.js
    @@ -0,0 +1,91 @@
    +var styles = [
    +  /* We are using two different styles for the polygons:
    +   *  - The first style is for the polygons themselves.
    +   *  - The second style is to draw the vertices of the polygons.
    +   *    In a custom `geometry` function the vertices of a polygon are
    +   *    returned as `MultiPoint` geometry, which will be used to render
    +   *    the style.
    +   */
    +  new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'blue',
    +      width: 3
    +    }),
    +    fill: new ol.style.Fill({
    +      color: 'rgba(0, 0, 255, 0.1)'
    +    })
    +  }),
    +  new ol.style.Style({
    +    image: new ol.style.Circle({
    +      radius: 5,
    +      fill: new ol.style.Fill({
    +        color: 'orange'
    +      })
    +    }),
    +    geometry: function(feature) {
    +      // return the coordinates of the first ring of the polygon
    +      var coordinates = feature.getGeometry().getCoordinates()[0];
    +      return new ol.geom.MultiPoint(coordinates);
    +    }
    +  })
    +];
    +
    +var source = new ol.source.GeoJSON(/** @type {olx.source.GeoJSONOptions} */ ({
    +  object: {
    +    'type': 'FeatureCollection',
    +    'crs': {
    +      'type': 'name',
    +      'properties': {
    +        'name': 'EPSG:3857'
    +      }
    +    },
    +    'features': [
    +      {
    +        'type': 'Feature',
    +        'geometry': {
    +          'type': 'Polygon',
    +          'coordinates': [[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6],
    +              [-3e6, 6e6], [-5e6, 6e6]]]
    +        }
    +      },
    +      {
    +        'type': 'Feature',
    +        'geometry': {
    +          'type': 'Polygon',
    +          'coordinates': [[[-2e6, 6e6], [-2e6, 8e6], [0, 8e6],
    +              [0, 6e6], [-2e6, 6e6]]]
    +        }
    +      },
    +      {
    +        'type': 'Feature',
    +        'geometry': {
    +          'type': 'Polygon',
    +          'coordinates': [[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6],
    +              [3e6, 6e6], [1e6, 6e6]]]
    +        }
    +      },
    +      {
    +        'type': 'Feature',
    +        'geometry': {
    +          'type': 'Polygon',
    +          'coordinates': [[[-2e6, -1e6], [-1e6, 1e6],
    +              [0, -1e6], [-2e6, -1e6]]]
    +        }
    +      }
    +    ]
    +  }
    +}));
    +
    +var layer = new ol.layer.Vector({
    +  source: source,
    +  style: styles
    +});
    +
    +var map = new ol.Map({
    +  layers: [layer],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 1000000],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/popup-require.js b/mapbox-gl/examples/popup-require.js
    new file mode 100644
    index 0000000000..7c076f9467
    --- /dev/null
    +++ b/mapbox-gl/examples/popup-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.View');
    +goog.require('ol.coordinate');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/popup.html b/mapbox-gl/examples/popup.html
    new file mode 100644
    index 0000000000..272fa75009
    --- /dev/null
    +++ b/mapbox-gl/examples/popup.html
    @@ -0,0 +1,104 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      .ol-popup {
    +        position: absolute;
    +        background-color: white;
    +        -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
    +        filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
    +        padding: 15px;
    +        border-radius: 10px;
    +        border: 1px solid #cccccc;
    +        bottom: 12px;
    +        left: -50px;
    +      }
    +      .ol-popup:after, .ol-popup:before {
    +        top: 100%;
    +        border: solid transparent;
    +        content: " ";
    +        height: 0;
    +        width: 0;
    +        position: absolute;
    +        pointer-events: none;
    +      }
    +      .ol-popup:after {
    +        border-top-color: white;
    +        border-width: 10px;
    +        left: 48px;
    +        margin-left: -10px;
    +      }
    +      .ol-popup:before {
    +        border-top-color: #cccccc;
    +        border-width: 11px;
    +        left: 48px;
    +        margin-left: -11px;
    +      }
    +      .ol-popup-closer {
    +        text-decoration: none;
    +        position: absolute;
    +        top: 2px;
    +        right: 8px;
    +      }
    +      .ol-popup-closer:after {
    +        content: "✖";
    +      }
    +    </style>
    +    <title>Popup example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map">
    +            <div id="popup" class="ol-popup">
    +                <a href="#" id="popup-closer" class="ol-popup-closer"></a>
    +                <div id="popup-content"></div>
    +            </div>
    +          </div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Popup example</h4>
    +          <p id="shortdesc">Uses an overlay to create a popup.</p>
    +          <div id="docs">
    +            <p>
    +              Click on the map to get a popup.  The popup is composed of a few basic elements: a container, a close button, and a place for the content.  To anchor the popup to the map, an <code>ol.Overlay</code> is created with the popup container.  A listener is registered for the map's <code>click</code> event to display the popup, and another listener is set as the <code>click</code> handler for the close button to hide the popup.
    +            </p>
    +            <p>
    +              See the <a href="popup.js" target="_blank">popup.js source</a> to see how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">overlay, popup, mapquest, openaerial</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=popup" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/popup.js b/mapbox-gl/examples/popup.js
    new file mode 100644
    index 0000000000..e310550671
    --- /dev/null
    +++ b/mapbox-gl/examples/popup.js
    @@ -0,0 +1,66 @@
    +/**
    + * Elements that make up the popup.
    + */
    +var container = document.getElementById('popup');
    +var content = document.getElementById('popup-content');
    +var closer = document.getElementById('popup-closer');
    +
    +
    +/**
    + * Add a click handler to hide the popup.
    + * @return {boolean} Don't follow the href.
    + */
    +closer.onclick = function() {
    +  overlay.setPosition(undefined);
    +  closer.blur();
    +  return false;
    +};
    +
    +
    +/**
    + * Create an overlay to anchor the popup to the map.
    + */
    +var overlay = new ol.Overlay(/** @type {olx.OverlayOptions} */ ({
    +  element: container,
    +  autoPan: true,
    +  autoPanAnimation: {
    +    duration: 250
    +  }
    +}));
    +
    +
    +/**
    + * Create the map.
    + */
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.TileJSON({
    +        url: 'http://api.tiles.mapbox.com/v3/' +
    +            'mapbox.natural-earth-hypso-bathy.jsonp',
    +        crossOrigin: 'anonymous'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  overlays: [overlay],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +
    +/**
    + * Add a click handler to the map to render the popup.
    + */
    +map.on('singleclick', function(evt) {
    +  var coordinate = evt.coordinate;
    +  var hdms = ol.coordinate.toStringHDMS(ol.proj.transform(
    +      coordinate, 'EPSG:3857', 'EPSG:4326'));
    +
    +  content.innerHTML = '<p>You clicked here:</p><code>' + hdms +
    +      '</code>';
    +  overlay.setPosition(coordinate);
    +});
    diff --git a/mapbox-gl/examples/preload-require.js b/mapbox-gl/examples/preload-require.js
    new file mode 100644
    index 0000000000..6c7285a11d
    --- /dev/null
    +++ b/mapbox-gl/examples/preload-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/preload.html b/mapbox-gl/examples/preload.html
    new file mode 100644
    index 0000000000..b45933d6da
    --- /dev/null
    +++ b/mapbox-gl/examples/preload.html
    @@ -0,0 +1,55 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Preload example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span6">
    +          <div id="map1" class="map"></div>
    +        </div>
    +        <div class="span6">
    +          <div id="map2" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Preload example</h4>
    +          <p id="shortdesc">Example of tile preloading.</p>
    +          <div id="docs">
    +            <p>The map on the left preloads low resolution tiles.  The map on the right does not use any preloading.  Try zooming out and panning to see the difference.</p>
    +            <p>See the <a href="preload.js" target="_blank">preload.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">preload, bing</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=preload" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/preload.js b/mapbox-gl/examples/preload.js
    new file mode 100644
    index 0000000000..88d57c4616
    --- /dev/null
    +++ b/mapbox-gl/examples/preload.js
    @@ -0,0 +1,32 @@
    +var map1 = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      preload: Infinity,
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'Aerial'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map1',
    +  view: new ol.View({
    +    center: [-4808600, -2620936],
    +    zoom: 8
    +  })
    +});
    +
    +var map2 = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      preload: 0, // default value
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'AerialWithLabels'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map2'
    +});
    +map2.bindTo('view', map1);
    diff --git a/mapbox-gl/examples/regularshape-require.js b/mapbox-gl/examples/regularshape-require.js
    new file mode 100644
    index 0000000000..53c1a254a1
    --- /dev/null
    +++ b/mapbox-gl/examples/regularshape-require.js
    @@ -0,0 +1,10 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.RegularShape');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/regularshape.html b/mapbox-gl/examples/regularshape.html
    new file mode 100644
    index 0000000000..2b1ecfcc34
    --- /dev/null
    +++ b/mapbox-gl/examples/regularshape.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Regular Shape example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Regular Shape example</h4>
    +          <p id="shortdesc">Example of some Regular Shape styles.</p>
    +          <div id="docs">
    +            <p>See the <a href="regularshape.js" target="_blank">regularshape.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, symbol, regularshape, style, square, cross, star, triangle, x</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=regularshape" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/regularshape.js b/mapbox-gl/examples/regularshape.js
    new file mode 100644
    index 0000000000..db44e04c04
    --- /dev/null
    +++ b/mapbox-gl/examples/regularshape.js
    @@ -0,0 +1,89 @@
    +var stroke = new ol.style.Stroke({color: 'black', width: 2});
    +var fill = new ol.style.Fill({color: 'red'});
    +
    +var styles = {
    +  'square': [new ol.style.Style({
    +    image: new ol.style.RegularShape(
    +        /** @type {olx.style.RegularShapeOptions} */({
    +          fill: fill,
    +          stroke: stroke,
    +          points: 4,
    +          radius: 10,
    +          angle: Math.PI / 4
    +        }))
    +  })],
    +  'triangle': [new ol.style.Style({
    +    image: new ol.style.RegularShape(
    +        /** @type {olx.style.RegularShapeOptions} */({
    +          fill: fill,
    +          stroke: stroke,
    +          points: 3,
    +          radius: 10,
    +          rotation: Math.PI / 4,
    +          angle: 0
    +        }))
    +  })],
    +  'star': [new ol.style.Style({
    +    image: new ol.style.RegularShape(
    +        /** @type {olx.style.RegularShapeOptions} */({
    +          fill: fill,
    +          stroke: stroke,
    +          points: 5,
    +          radius: 10,
    +          radius2: 4,
    +          angle: 0
    +        }))
    +  })],
    +  'cross': [new ol.style.Style({
    +    image: new ol.style.RegularShape(
    +        /** @type {olx.style.RegularShapeOptions} */({
    +          fill: fill,
    +          stroke: stroke,
    +          points: 4,
    +          radius: 10,
    +          radius2: 0,
    +          angle: 0
    +        }))
    +  })],
    +  'x': [new ol.style.Style({
    +    image: new ol.style.RegularShape(
    +        /** @type {olx.style.RegularShapeOptions} */({
    +          fill: fill,
    +          stroke: stroke,
    +          points: 4,
    +          radius: 10,
    +          radius2: 0,
    +          angle: Math.PI / 4
    +        }))
    +  })]
    +};
    +
    +
    +var styleKeys = ['x', 'cross', 'star', 'triangle', 'square'];
    +var count = 250;
    +var features = new Array(count);
    +var e = 4500000;
    +for (var i = 0; i < count; ++i) {
    +  var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
    +  features[i] = new ol.Feature(new ol.geom.Point(coordinates));
    +  features[i].setStyle(styles[styleKeys[Math.floor(Math.random() * 5)]]);
    +}
    +
    +var source = new ol.source.Vector({
    +  features: features
    +});
    +
    +var vectorLayer = new ol.layer.Vector({
    +  source: source
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    vectorLayer
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/rotation-require.js b/mapbox-gl/examples/rotation-require.js
    new file mode 100644
    index 0000000000..ff458ec379
    --- /dev/null
    +++ b/mapbox-gl/examples/rotation-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/rotation.html b/mapbox-gl/examples/rotation.html
    new file mode 100644
    index 0000000000..b6e94d986e
    --- /dev/null
    +++ b/mapbox-gl/examples/rotation.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Rotation example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Rotation example</h4>
    +          <p id="shortdesc">Example of a rotated map.</p>
    +          <div id="docs">
    +            <p>Use <code>Alt</code>+<code>Shift</code>+drag to rotate the map.</p>
    +            <p>See the <a href="rotation.js" target="_blank">rotation.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">rotation, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=rotation" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/rotation.js b/mapbox-gl/examples/rotation.js
    new file mode 100644
    index 0000000000..194ce6b3eb
    --- /dev/null
    +++ b/mapbox-gl/examples/rotation.js
    @@ -0,0 +1,19 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [-25860000, 4130000],
    +    rotation: Math.PI / 6,
    +    zoom: 10
    +  })
    +});
    diff --git a/mapbox-gl/examples/scale-line-require.js b/mapbox-gl/examples/scale-line-require.js
    new file mode 100644
    index 0000000000..26f4d34959
    --- /dev/null
    +++ b/mapbox-gl/examples/scale-line-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.ScaleLine');
    +goog.require('ol.dom.Input');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/scale-line.html b/mapbox-gl/examples/scale-line.html
    new file mode 100644
    index 0000000000..2151535a52
    --- /dev/null
    +++ b/mapbox-gl/examples/scale-line.html
    @@ -0,0 +1,58 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Scale line example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +          <select id="units">
    +            <option value="degrees">degrees</option>
    +            <option value="imperial">imperial inch</option>
    +            <option value="us">us inch</option>
    +            <option value="nautical">nautical mile</option>
    +            <option value="metric">metric</option>
    +          </select>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Scale line example</h4>
    +          <p id="shortdesc">Example of a scale line.</p>
    +          <div id="docs">
    +            <p>See the <a href="scale-line.js" target="_blank">scale-line.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">scale-line, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=scale-line" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/scale-line.js b/mapbox-gl/examples/scale-line.js
    new file mode 100644
    index 0000000000..3cc3a3a1d5
    --- /dev/null
    +++ b/mapbox-gl/examples/scale-line.js
    @@ -0,0 +1,26 @@
    +var scaleLineControl = new ol.control.ScaleLine();
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }).extend([
    +    scaleLineControl
    +  ]),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +
    +var unitsSelect = new ol.dom.Input(document.getElementById('units'));
    +unitsSelect.bindTo('value', scaleLineControl, 'units');
    diff --git a/mapbox-gl/examples/select-features-require.js b/mapbox-gl/examples/select-features-require.js
    new file mode 100644
    index 0000000000..cad5da68c1
    --- /dev/null
    +++ b/mapbox-gl/examples/select-features-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Select');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/select-features.html b/mapbox-gl/examples/select-features.html
    new file mode 100644
    index 0000000000..353183ecef
    --- /dev/null
    +++ b/mapbox-gl/examples/select-features.html
    @@ -0,0 +1,64 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Select features example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Select features example</h4>
    +          <p id="shortdesc">Example of using the Select interaction. Choose between <code>Single-click</code>, <code>Click</code> and <code>Hover</code> as the event type for selection in the combobox below. When using <code>Single-click</code> or <code>Click</code> you can hold do <code>Shift</code> key to toggle the feature in the selection.</p>
    +          <p>Note: when <code>Single-click</code> is used double-clicks won't select features. This in contrast to <code>Click</code>, where a double-click will both select the feature and zoom the map (because of the <code>DoubleClickZoom</code> interaction). Note that <code>Single-click</code> is less responsive than <code>Click</code> because of the delay it uses to detect double-clicks.</p>
    +          <p>In this example, a listener is registered for the Select interaction's <code>select</code> event in order to update the selection status below.
    +          <form class="form-inline">
    +            <label>Action type &nbsp;</label>
    +              <select id="type">
    +                <option value="none" selected>None</option>
    +                <option value="singleclick">Single-click</option>
    +                <option value="click">Click</option>
    +                <option value="pointermove">Hover</option>
    +              </select>
    +              <span id="status">&nbsp;0 selected features</span>
    +          </form>
    +          <div id="docs">
    +            <p>See the <a href="select-features.js" target="_blank">select-features.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">select, vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=select-features" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/select-features.js b/mapbox-gl/examples/select-features.js
    new file mode 100644
    index 0000000000..b2a2670a9d
    --- /dev/null
    +++ b/mapbox-gl/examples/select-features.js
    @@ -0,0 +1,67 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.MapQuest({layer: 'sat'})
    +});
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/countries.geojson'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var select = null;  // ref to currently selected interaction
    +
    +// select interaction working on "singleclick"
    +var selectSingleClick = new ol.interaction.Select();
    +
    +// select interaction working on "click"
    +var selectClick = new ol.interaction.Select({
    +  condition: ol.events.condition.click
    +});
    +
    +// select interaction working on "pointermove"
    +var selectPointerMove = new ol.interaction.Select({
    +  condition: ol.events.condition.pointerMove
    +});
    +
    +var selectElement = document.getElementById('type');
    +
    +var changeInteraction = function() {
    +  if (select !== null) {
    +    map.removeInteraction(select);
    +  }
    +  var value = selectElement.value;
    +  if (value == 'singleclick') {
    +    select = selectSingleClick;
    +  } else if (value == 'click') {
    +    select = selectClick;
    +  } else if (value == 'pointermove') {
    +    select = selectPointerMove;
    +  } else {
    +    select = null;
    +  }
    +  if (select !== null) {
    +    map.addInteraction(select);
    +    select.on('select', function(e) {
    +      $('#status').html('&nbsp;' + e.target.getFeatures().getLength() +
    +          ' selected features (last operation selected ' + e.selected.length +
    +          ' and deselected ' + e.deselected.length + ' features)');
    +    });
    +  }
    +};
    +
    +
    +/**
    + * onchange callback on the select element.
    + */
    +selectElement.onchange = changeInteraction;
    +changeInteraction();
    diff --git a/mapbox-gl/examples/semi-transparent-layer-require.js b/mapbox-gl/examples/semi-transparent-layer-require.js
    new file mode 100644
    index 0000000000..21b2c05c29
    --- /dev/null
    +++ b/mapbox-gl/examples/semi-transparent-layer-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/semi-transparent-layer.html b/mapbox-gl/examples/semi-transparent-layer.html
    new file mode 100644
    index 0000000000..24f7302eca
    --- /dev/null
    +++ b/mapbox-gl/examples/semi-transparent-layer.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Semi-transparent layer example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Semi-transparent layer example</h4>
    +          <p id="shortdesc">Example of a map with a semi-transparent layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="semi-transparent-layer.js" target="_blank">semi-transparent-layer.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">transparent, mapquest, tilejson</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=semi-transparent-layer" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/semi-transparent-layer.js b/mapbox-gl/examples/semi-transparent-layer.js
    new file mode 100644
    index 0000000000..5480173b33
    --- /dev/null
    +++ b/mapbox-gl/examples/semi-transparent-layer.js
    @@ -0,0 +1,19 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    }),
    +    new ol.layer.Tile({
    +      source: new ol.source.TileJSON({
    +        url: 'http://api.tiles.mapbox.com/v3/mapbox.va-quake-aug.jsonp',
    +        crossOrigin: 'anonymous'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform([-77.93255, 37.9555], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 5
    +  })
    +});
    diff --git a/mapbox-gl/examples/side-by-side-require.js b/mapbox-gl/examples/side-by-side-require.js
    new file mode 100644
    index 0000000000..b95503f517
    --- /dev/null
    +++ b/mapbox-gl/examples/side-by-side-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/side-by-side.html b/mapbox-gl/examples/side-by-side.html
    new file mode 100644
    index 0000000000..67bb0f8cfd
    --- /dev/null
    +++ b/mapbox-gl/examples/side-by-side.html
    @@ -0,0 +1,63 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Side-by-side example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span4">
    +          <h4>Canvas</h4>
    +          <div id="canvasMap" class="map"></div>
    +        </div>
    +        <div class="span4">
    +          <h4>WebGL</h4>
    +          <div id="webglMap" class="map"></div>
    +          <div id="no-webgl" class="alert alert-error" style="display: none">
    +            This map requires a browser that supports <a href="http://get.webgl.org/">WebGL</a>.
    +          </div>
    +        </div>
    +        <div class="span4">
    +          <h4>DOM</h4>
    +          <div id="domMap" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Side-by-side example</h4>
    +          <p id="shortdesc">The three maps, one WebGL, one Canvas, one DOM, share the same center, resolution, rotation and layers.</p>
    +          <div id="docs">
    +            <p>See the <a href="side-by-side.js" target="_blank">side-by-side.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">side-by-side, canvas, webgl, dom, canvas, sync, object</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=side-by-side" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/side-by-side.js b/mapbox-gl/examples/side-by-side.js
    new file mode 100644
    index 0000000000..5696e0a5c1
    --- /dev/null
    +++ b/mapbox-gl/examples/side-by-side.js
    @@ -0,0 +1,34 @@
    +var domMap = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    })
    +  ],
    +  renderer: 'dom',
    +  target: 'domMap',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    +
    +if (ol.has.WEBGL) {
    +  var webglMap = new ol.Map({
    +    renderer: 'webgl',
    +    target: 'webglMap'
    +  });
    +  webglMap.bindTo('layergroup', domMap);
    +  webglMap.bindTo('view', domMap);
    +} else {
    +  var info = document.getElementById('no-webgl');
    +  /**
    +   * display error message
    +   */
    +  info.style.display = '';
    +}
    +
    +var canvasMap = new ol.Map({
    +  target: 'canvasMap'
    +});
    +canvasMap.bindTo('layergroup', domMap);
    +canvasMap.bindTo('view', domMap);
    diff --git a/mapbox-gl/examples/simple-require.js b/mapbox-gl/examples/simple-require.js
    new file mode 100644
    index 0000000000..ff458ec379
    --- /dev/null
    +++ b/mapbox-gl/examples/simple-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/simple.html b/mapbox-gl/examples/simple.html
    new file mode 100644
    index 0000000000..4f760282af
    --- /dev/null
    +++ b/mapbox-gl/examples/simple.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Simple example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Simple example</h4>
    +          <p id="shortdesc">Example of a simple map.</p>
    +          <div id="docs">
    +            <p>See the <a href="simple.js" target="_blank">simple.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">simple, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=simple" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/simple.js b/mapbox-gl/examples/simple.js
    new file mode 100644
    index 0000000000..d1ff2b7532
    --- /dev/null
    +++ b/mapbox-gl/examples/simple.js
    @@ -0,0 +1,18 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/sphere-mollweide-require.js b/mapbox-gl/examples/sphere-mollweide-require.js
    new file mode 100644
    index 0000000000..fddce4a360
    --- /dev/null
    +++ b/mapbox-gl/examples/sphere-mollweide-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Graticule');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.proj');
    +goog.require('ol.source.GeoJSON');
    diff --git a/mapbox-gl/examples/sphere-mollweide.html b/mapbox-gl/examples/sphere-mollweide.html
    new file mode 100644
    index 0000000000..9493769cea
    --- /dev/null
    +++ b/mapbox-gl/examples/sphere-mollweide.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Sphere Mollweide example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Sphere Mollweide example</h4>
    +          <p id="shortdesc">Example of a Sphere Mollweide map with a Graticule component.</p>
    +          <div id="docs">
    +            <p>See the <a href="sphere-mollweide.js" target="_blank">sphere-mollweide.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">graticule, Mollweide, projection, proj4js</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.2.1/proj4.js" type="text/javascript"></script>
    +    <script src="loader.js?id=sphere-mollweide" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/sphere-mollweide.js b/mapbox-gl/examples/sphere-mollweide.js
    new file mode 100644
    index 0000000000..00131eff1f
    --- /dev/null
    +++ b/mapbox-gl/examples/sphere-mollweide.js
    @@ -0,0 +1,34 @@
    +proj4.defs('ESRI:53009', '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +a=6371000 ' +
    +    '+b=6371000 +units=m +no_defs');
    +
    +// Configure the Sphere Mollweide projection object with an extent,
    +// and a world extent. These are required for the Graticule.
    +var sphereMollweideProjection = ol.proj.get('ESRI:53009');
    +sphereMollweideProjection.setExtent([
    +  -9009954.605703328, -9009954.605703328,
    +  9009954.605703328, 9009954.605703328]);
    +sphereMollweideProjection.setWorldExtent([-179, -90, 179, 90]);
    +
    +var map = new ol.Map({
    +  keyboardEventTarget: document,
    +  layers: [
    +    new ol.layer.Vector({
    +      source: new ol.source.GeoJSON({
    +        projection: sphereMollweideProjection,
    +        url: 'data/geojson/countries-110m.geojson'
    +      })
    +    })
    +  ],
    +  renderer: 'canvas',
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    projection: sphereMollweideProjection,
    +    resolutions: [65536, 32768, 16384, 8192, 4096, 2048],
    +    zoom: 0
    +  })
    +});
    +
    +var graticule = new ol.Graticule({
    +  map: map
    +});
    diff --git a/mapbox-gl/examples/stamen-require.js b/mapbox-gl/examples/stamen-require.js
    new file mode 100644
    index 0000000000..d57fe8f304
    --- /dev/null
    +++ b/mapbox-gl/examples/stamen-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.Stamen');
    diff --git a/mapbox-gl/examples/stamen.html b/mapbox-gl/examples/stamen.html
    new file mode 100644
    index 0000000000..4663db3467
    --- /dev/null
    +++ b/mapbox-gl/examples/stamen.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Stamen example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Stamen example</h4>
    +          <p id="shortdesc">Example of a Stamen tile source.  Two layers are composed: the watercolor base layer with the terrain labels.</p>
    +          <div id="docs">
    +            <p>See the <a href="stamen.js" target="_blank">stamen.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">stamen, watercolor, terrain-labels, two-layers</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=stamen" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/stamen.js b/mapbox-gl/examples/stamen.js
    new file mode 100644
    index 0000000000..4f553f0fb7
    --- /dev/null
    +++ b/mapbox-gl/examples/stamen.js
    @@ -0,0 +1,21 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.Stamen({
    +        layer: 'watercolor'
    +      })
    +    }),
    +    new ol.layer.Tile({
    +      source: new ol.source.Stamen({
    +        layer: 'terrain-labels'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: ol.proj.transform(
    +        [-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 12
    +  })
    +});
    diff --git a/mapbox-gl/examples/static-image-require.js b/mapbox-gl/examples/static-image-require.js
    new file mode 100644
    index 0000000000..69129d0ab0
    --- /dev/null
    +++ b/mapbox-gl/examples/static-image-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.source.ImageStatic');
    diff --git a/mapbox-gl/examples/static-image.html b/mapbox-gl/examples/static-image.html
    new file mode 100644
    index 0000000000..4e761dc286
    --- /dev/null
    +++ b/mapbox-gl/examples/static-image.html
    @@ -0,0 +1,60 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Static image example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Static image example</h4>
    +          <p id="shortdesc">Example of a static image layer.</p>
    +          <div id="docs">
    +            <p>
    +              This example uses a <a href="http://xkcd.com/256/">static image</a>
    +              as a layer source.  The map view is configured with a custom
    +              projection that translates image coordinates directly into map
    +              coordinates.
    +            </p>
    +            <p>
    +              See the <a href="static-image.js" target="_blank">static-image.js source</a>
    +              for details on how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">static image, xkcd</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=static-image" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/static-image.js b/mapbox-gl/examples/static-image.js
    new file mode 100644
    index 0000000000..c1ae0485cb
    --- /dev/null
    +++ b/mapbox-gl/examples/static-image.js
    @@ -0,0 +1,32 @@
    +// Map views always need a projection.  Here we just want to map image
    +// coordinates directly to map coordinates, so we create a projection that uses
    +// the image extent in pixels.
    +var extent = [0, 0, 1024, 968];
    +var projection = new ol.proj.Projection({
    +  code: 'xkcd-image',
    +  units: 'pixels',
    +  extent: extent
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Image({
    +      source: new ol.source.ImageStatic({
    +        attributions: [
    +          new ol.Attribution({
    +            html: '&copy; <a href="http://xkcd.com/license.html">xkcd</a>'
    +          })
    +        ],
    +        url: 'http://imgs.xkcd.com/comics/online_communities.png',
    +        projection: projection,
    +        imageExtent: extent
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    projection: projection,
    +    center: ol.extent.getCenter(extent),
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/symbol-atlas-webgl-require.js b/mapbox-gl/examples/symbol-atlas-webgl-require.js
    new file mode 100644
    index 0000000000..b8c83fa055
    --- /dev/null
    +++ b/mapbox-gl/examples/symbol-atlas-webgl-require.js
    @@ -0,0 +1,12 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.AtlasManager');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.RegularShape');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/symbol-atlas-webgl.html b/mapbox-gl/examples/symbol-atlas-webgl.html
    new file mode 100644
    index 0000000000..666bc43ab5
    --- /dev/null
    +++ b/mapbox-gl/examples/symbol-atlas-webgl.html
    @@ -0,0 +1,58 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Symbols with WebGL example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Symbols with WebGL example</h4>
    +          <p id="shortdesc">Using symbols in an atlas with WebGL.</p>
    +          <div id="docs">
    +            <p>When using symbol styles with WebGL, OpenLayers would render the symbol
    +            on a temporary image and would create a WebGL texture for each image. For a
    +            better performance, it is recommended to use atlas images (similar to
    +            image sprites with CSS), so that the number of textures is reduced. OpenLayers
    +            provides an <code>AtlasManager</code>, which when passed to the constructor
    +            of a symbol style, will create atlases for the symbols.</p>
    +            <p>See the <a href="symbol-atlas-webgl.js" target="_blank">symbol-atlas-webgl.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">webgl, symbol, atlas, vector, point</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=symbol-atlas-webgl" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/symbol-atlas-webgl.js b/mapbox-gl/examples/symbol-atlas-webgl.js
    new file mode 100644
    index 0000000000..72f5293de8
    --- /dev/null
    +++ b/mapbox-gl/examples/symbol-atlas-webgl.js
    @@ -0,0 +1,110 @@
    +var atlasManager = new ol.style.AtlasManager({
    +  // we increase the initial size so that all symbols fit into
    +  // a single atlas image
    +  initialSize: 512
    +});
    +
    +var symbolInfo = [{
    +  opacity: 1.0,
    +  scale: 1.0,
    +  fillColor: 'rgba(255, 153, 0, 0.4)',
    +  strokeColor: 'rgba(255, 204, 0, 0.2)'
    +}, {
    +  opacity: 0.75,
    +  scale: 1.25,
    +  fillColor: 'rgba(70, 80, 224, 0.4)',
    +  strokeColor: 'rgba(12, 21, 138, 0.2)'
    +}, {
    +  opacity: 0.5,
    +  scale: 1.5,
    +  fillColor: 'rgba(66, 150, 79, 0.4)',
    +  strokeColor: 'rgba(20, 99, 32, 0.2)'
    +}, {
    +  opacity: 1.0,
    +  scale: 1.0,
    +  fillColor: 'rgba(176, 61, 35, 0.4)',
    +  strokeColor: 'rgba(145, 43, 20, 0.2)'
    +}];
    +
    +var radiuses = [3, 6, 9, 15, 19, 25];
    +var symbolCount = symbolInfo.length * radiuses.length * 2;
    +var symbols = [];
    +var i, j;
    +for (i = 0; i < symbolInfo.length; ++i) {
    +  var info = symbolInfo[i];
    +  for (j = 0; j < radiuses.length; ++j) {
    +    // circle symbol
    +    symbols.push(new ol.style.Circle({
    +      opacity: info.opacity,
    +      scale: info.scale,
    +      radius: radiuses[j],
    +      fill: new ol.style.Fill({
    +        color: info.fillColor
    +      }),
    +      stroke: new ol.style.Stroke({
    +        color: info.strokeColor,
    +        width: 1
    +      }),
    +      // by passing the atlas manager to the symbol,
    +      // the symbol will be added to an atlas
    +      atlasManager: atlasManager
    +    }));
    +
    +    // star symbol
    +    symbols.push(new ol.style.RegularShape({
    +      points: 8,
    +      opacity: info.opacity,
    +      scale: info.scale,
    +      radius: radiuses[j],
    +      radius2: radiuses[j] * 0.7,
    +      angle: 1.4,
    +      fill: new ol.style.Fill({
    +        color: info.fillColor
    +      }),
    +      stroke: new ol.style.Stroke({
    +        color: info.strokeColor,
    +        width: 1
    +      }),
    +      atlasManager: atlasManager
    +    }));
    +  }
    +}
    +
    +var featureCount = 50000;
    +var features = new Array(featureCount);
    +var feature, geometry;
    +var e = 25000000;
    +for (i = 0; i < featureCount; ++i) {
    +  geometry = new ol.geom.Point(
    +      [2 * e * Math.random() - e, 2 * e * Math.random() - e]);
    +  feature = new ol.Feature(geometry);
    +  feature.setStyle(
    +      new ol.style.Style({
    +        image: symbols[i % symbolCount]
    +      })
    +  );
    +  features[i] = feature;
    +}
    +
    +var vectorSource = new ol.source.Vector({
    +  features: features
    +});
    +var vector = new ol.layer.Vector({
    +  source: vectorSource
    +});
    +
    +// Use the "webgl" renderer by default.
    +var renderer = exampleNS.getRendererFromQueryString();
    +if (!renderer) {
    +  renderer = 'webgl';
    +}
    +
    +var map = new ol.Map({
    +  renderer: renderer,
    +  layers: [vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/synthetic-lines-require.js b/mapbox-gl/examples/synthetic-lines-require.js
    new file mode 100644
    index 0000000000..99f27ce774
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-lines-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/synthetic-lines.html b/mapbox-gl/examples/synthetic-lines.html
    new file mode 100644
    index 0000000000..64f497f935
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-lines.html
    @@ -0,0 +1,110 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Synthetic lines example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Synthetic lines example</h4>
    +          <p id="shortdesc">Synthetic lines example.</p>
    +          <div id="docs">
    +            <p>See the <a href="synthetic-lines.js" target="_blank">synthetic-lines.js source</a> to see how this is done.</p>
    +            <p>Performance results:</p>
    +            <table border="1">
    +              <thead>
    +               <tr>
    +                 <th>Device/Browser</th>
    +                 <th>200 lines</th>
    +                 <th>500 lines</th>
    +                 <th>1000 lines</th>
    +                 <th>2000 lines</th>
    +                 <th>5000 lines</th>
    +                 <th>10000 lines</th>
    +                 <th>20000 lines</th>
    +               </tr>
    +              </thead>
    +              <tbody>
    +               <tr>
    +                 <td>Mac Book Air / Chrome 33 canary</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +               </tr>
    +               <tr>
    +                 <td>Mac Book Air / FireFox 25</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>22 fps</td>
    +                 <td>6 fps</td>
    +               </tr>
    +               <tr>
    +                 <td>Mac Book Air / Safari 7</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>60 fps</td>
    +                 <td>40 fps</td>
    +                 <td>10 fps</td>
    +                 <td>N/A</td>
    +                 <td>N/A</td>
    +               </tr>
    +               <tr>
    +                 <td>iPhone 4S / iOS 7 / Safari</td>
    +                 <td>60 fps</td>
    +                 <td>33 fps</td>
    +                 <td>15 fps</td>
    +                 <td>5 fps</td>
    +                 <td>N/A</td>
    +                 <td>N/A</td>
    +                 <td>N/A</td>
    +               </tr>
    +             </tbody>
    +            </table>
    +
    +          </div>
    +          <div id="tags">vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=synthetic-lines" type="text/javascript"></script>
    +    <script src="../resources/display-frame-rate.js" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/synthetic-lines.js b/mapbox-gl/examples/synthetic-lines.js
    new file mode 100644
    index 0000000000..bf3a3e7d72
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-lines.js
    @@ -0,0 +1,50 @@
    +var count = 10000;
    +var features = new Array(count);
    +
    +var startPoint = [0, 0];
    +var endPoint;
    +
    +var delta, deltaX, deltaY;
    +var signX = 1;
    +var signY = -1;
    +
    +// Create a square spiral.
    +var i;
    +for (i = 0; i < count; ++i) {
    +  delta = (i + 1) * 2500;
    +  if (i % 2 === 0) {
    +    signY *= -1;
    +  } else {
    +    signX *= -1;
    +  }
    +  deltaX = delta * signX;
    +  deltaY = delta * signY;
    +  endPoint = [startPoint[0] + deltaX, startPoint[1] + deltaY];
    +  features[i] = new ol.Feature({
    +    'geometry': new ol.geom.LineString([startPoint, endPoint])
    +  });
    +  startPoint = endPoint;
    +}
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.Vector({
    +    features: features
    +  }),
    +  style: new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: '#666666',
    +      width: 1
    +    })
    +  })
    +});
    +
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 0
    +});
    +
    +var map = new ol.Map({
    +  layers: [vector],
    +  target: 'map',
    +  view: view
    +});
    diff --git a/mapbox-gl/examples/synthetic-points-require.js b/mapbox-gl/examples/synthetic-points-require.js
    new file mode 100644
    index 0000000000..e40573c053
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-points-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.Point');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/synthetic-points.html b/mapbox-gl/examples/synthetic-points.html
    new file mode 100644
    index 0000000000..45cd56ddb6
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-points.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Synthetic points example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Synthetic points example</h4>
    +          <p id="shortdesc">Synthetic points  example.</p>
    +          <div id="docs">
    +            <p>See the <a href="synthetic-points.js" target="_blank">synthetic-points.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=synthetic-points" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/synthetic-points.js b/mapbox-gl/examples/synthetic-points.js
    new file mode 100644
    index 0000000000..d5cfec7249
    --- /dev/null
    +++ b/mapbox-gl/examples/synthetic-points.js
    @@ -0,0 +1,120 @@
    +var count = 20000;
    +var features = new Array(count);
    +var e = 18000000;
    +for (var i = 0; i < count; ++i) {
    +  features[i] = new ol.Feature({
    +    'geometry': new ol.geom.Point(
    +        [2 * e * Math.random() - e, 2 * e * Math.random() - e]),
    +    'i': i,
    +    'size': i % 2 ? 10 : 20
    +  });
    +}
    +
    +var styles = {
    +  '10': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      radius: 5,
    +      fill: new ol.style.Fill({color: '#666666'}),
    +      stroke: new ol.style.Stroke({color: '#bada55', width: 1})
    +    })
    +  })],
    +  '20': [new ol.style.Style({
    +    image: new ol.style.Circle({
    +      radius: 10,
    +      fill: new ol.style.Fill({color: '#666666'}),
    +      stroke: new ol.style.Stroke({color: '#bada55', width: 1})
    +    })
    +  })]
    +};
    +
    +var vectorSource = new ol.source.Vector({
    +  features: features
    +});
    +var vector = new ol.layer.Vector({
    +  source: vectorSource,
    +  style: function(feature, resolution) {
    +    return styles[feature.get('size')];
    +  }
    +});
    +
    +var map = new ol.Map({
    +  layers: [vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var point = null;
    +var line = null;
    +var displaySnap = function(coordinate) {
    +  var closestFeature = vectorSource.getClosestFeatureToCoordinate(coordinate);
    +  if (closestFeature === null) {
    +    point = null;
    +    line = null;
    +  } else {
    +    var geometry = closestFeature.getGeometry();
    +    var closestPoint = geometry.getClosestPoint(coordinate);
    +    if (point === null) {
    +      point = new ol.geom.Point(closestPoint);
    +    } else {
    +      point.setCoordinates(closestPoint);
    +    }
    +    if (line === null) {
    +      line = new ol.geom.LineString([coordinate, closestPoint]);
    +    } else {
    +      line.setCoordinates([coordinate, closestPoint]);
    +    }
    +  }
    +  map.render();
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var coordinate = map.getEventCoordinate(evt.originalEvent);
    +  displaySnap(coordinate);
    +});
    +
    +map.on('click', function(evt) {
    +  displaySnap(evt.coordinate);
    +});
    +
    +var imageStyle = new ol.style.Circle({
    +  radius: 10,
    +  fill: null,
    +  stroke: new ol.style.Stroke({
    +    color: 'rgba(255,255,0,0.9)',
    +    width: 3
    +  })
    +});
    +var strokeStyle = new ol.style.Stroke({
    +  color: 'rgba(255,255,0,0.9)',
    +  width: 3
    +});
    +map.on('postcompose', function(evt) {
    +  var vectorContext = evt.vectorContext;
    +  if (point !== null) {
    +    vectorContext.setImageStyle(imageStyle);
    +    vectorContext.drawPointGeometry(point);
    +  }
    +  if (line !== null) {
    +    vectorContext.setFillStrokeStyle(null, strokeStyle);
    +    vectorContext.drawLineStringGeometry(line);
    +  }
    +});
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  var hit = map.hasFeatureAtPixel(pixel);
    +  if (hit) {
    +    map.getTarget().style.cursor = 'pointer';
    +  } else {
    +    map.getTarget().style.cursor = '';
    +  }
    +});
    diff --git a/mapbox-gl/examples/teleport-require.js b/mapbox-gl/examples/teleport-require.js
    new file mode 100644
    index 0000000000..ff458ec379
    --- /dev/null
    +++ b/mapbox-gl/examples/teleport-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    diff --git a/mapbox-gl/examples/teleport.html b/mapbox-gl/examples/teleport.html
    new file mode 100644
    index 0000000000..c501ff48a7
    --- /dev/null
    +++ b/mapbox-gl/examples/teleport.html
    @@ -0,0 +1,61 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Teleport example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span8">
    +          <div id="map1" class="map"></div>
    +        </div>
    +        <div class="span4">
    +          <div id="map2" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <a id="teleport" href="#" class="btn">Teleport</a>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Teleport example</h4>
    +          <p id="shortdesc">Example of moving a map from one target to another.</p>
    +          <div id="docs">
    +            <p>Click on the Teleport button below the map to move the map from one target to another.</p>
    +            <p>See the <a href="teleport.js" target="_blank">teleport.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">teleport, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=teleport" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/teleport.js b/mapbox-gl/examples/teleport.js
    new file mode 100644
    index 0000000000..efcee05b35
    --- /dev/null
    +++ b/mapbox-gl/examples/teleport.js
    @@ -0,0 +1,26 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +map.setTarget('map1');
    +
    +var teleportButton = document.getElementById('teleport');
    +
    +teleportButton.addEventListener('click', function() {
    +  var target = map.getTarget() === 'map1' ? 'map2' : 'map1';
    +  map.setTarget(target);
    +}, false);
    diff --git a/mapbox-gl/examples/tile-load-events-require.js b/mapbox-gl/examples/tile-load-events-require.js
    new file mode 100644
    index 0000000000..f337d143e5
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-load-events-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/tile-load-events.html b/mapbox-gl/examples/tile-load-events.html
    new file mode 100644
    index 0000000000..883e42ec6d
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-load-events.html
    @@ -0,0 +1,79 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tile load events example</title>
    +    <style>
    +      .map {
    +        background: #E0ECED;
    +      }
    +      .wrapper {
    +        position: relative;
    +      }
    +      #progress {
    +        position: absolute;
    +        bottom: 0;
    +        left: 0;
    +        height: 2px;
    +        background: rgba(0, 60, 136, 0.4);
    +        width: 0;
    +        transition: width 250ms;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12 wrapper">
    +          <div id="map" class="map"></div>
    +          <div id="progress"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tile load events example</h4>
    +          <p id="shortdesc">Example using tile load events.</p>
    +          <div id="docs">
    +            <p>
    +              Image tile sources fire events related to tile loading.  You can
    +              listen for <code>tileloadstart</code>, <code>tileloadend</code>,
    +              and <code>tileloaderror</code> type events to monitor tile loading
    +              progress.  This example registers listeners for these events and
    +              renders a tile loading progress bar at the bottom of the map.
    +            </p>
    +            <p>
    +              See the <a href="tile-load-events.js" target="_blank">tile-load-events.js source</a>
    +              for more detail on how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">tile, events, loading</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=tile-load-events" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/tile-load-events.js b/mapbox-gl/examples/tile-load-events.js
    new file mode 100644
    index 0000000000..ab7de54751
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-load-events.js
    @@ -0,0 +1,97 @@
    +/**
    + * Renders a progress bar.
    + * @param {Element} el The target element.
    + * @constructor
    + */
    +function Progress(el) {
    +  this.el = el;
    +  this.loading = 0;
    +  this.loaded = 0;
    +}
    +
    +
    +/**
    + * Increment the count of loading tiles.
    + */
    +Progress.prototype.addLoading = function() {
    +  if (this.loading === 0) {
    +    this.show();
    +  }
    +  ++this.loading;
    +  this.update();
    +};
    +
    +
    +/**
    + * Increment the count of loaded tiles.
    + */
    +Progress.prototype.addLoaded = function() {
    +  setTimeout(function() {
    +    ++this.loaded;
    +    this.update();
    +  }.bind(this), 100);
    +};
    +
    +
    +/**
    + * Update the progress bar.
    + */
    +Progress.prototype.update = function() {
    +  var width = (this.loaded / this.loading * 100).toFixed(1) + '%';
    +  this.el.style.width = width;
    +  if (this.loading === this.loaded) {
    +    this.loading = 0;
    +    this.loaded = 0;
    +    setTimeout(this.hide.bind(this), 500);
    +  }
    +};
    +
    +
    +/**
    + * Show the progress bar.
    + */
    +Progress.prototype.show = function() {
    +  this.el.style.visibility = 'visible';
    +};
    +
    +
    +/**
    + * Hide the progress bar.
    + */
    +Progress.prototype.hide = function() {
    +  if (this.loading === this.loaded) {
    +    this.el.style.visibility = 'hidden';
    +    this.el.style.width = 0;
    +  }
    +};
    +
    +var progress = new Progress(document.getElementById('progress'));
    +
    +var source = new ol.source.TileJSON({
    +  url: 'http://api.tiles.mapbox.com/v3/mapbox.world-bright.jsonp',
    +  crossOrigin: 'anonymous'
    +});
    +
    +source.on('tileloadstart', function(event) {
    +  progress.addLoading();
    +});
    +
    +source.on('tileloadend', function(event) {
    +  progress.addLoaded();
    +});
    +source.on('tileloaderror', function(event) {
    +  progress.addLoaded();
    +});
    +
    +var map = new ol.Map({
    +  logo: false,
    +  layers: [
    +    new ol.layer.Tile({source: source})
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/tile-vector-require.js b/mapbox-gl/examples/tile-vector-require.js
    new file mode 100644
    index 0000000000..e0574a4711
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-vector-require.js
    @@ -0,0 +1,10 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.TopoJSON');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileVector');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.tilegrid.XYZ');
    diff --git a/mapbox-gl/examples/tile-vector.html b/mapbox-gl/examples/tile-vector.html
    new file mode 100644
    index 0000000000..192e786cfd
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-vector.html
    @@ -0,0 +1,67 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tile vector example</title>
    +    <style>
    +      #map {
    +        max-width: 600px;
    +        margin: 0 auto;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map" style="background: white;"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tile vector example</h4>
    +          <p id="shortdesc">Example of vector tiles from openstreetmap.us.</p>
    +          <div id="docs">
    +            <p>See the <a href="tile-vector.js" target="_blank">tile-vector.js source</a> to see how this is done.</p>
    +          </div>
    +          <div class="alert">
    +            <strong>Warning</strong> Map is becoming unresponsive with too many layers.
    +          </div>
    +          <fieldset>
    +            <legend>Layers</legend>
    +            <label class="checkbox"><input type="checkbox" id="landuse"/> Landuse</label>
    +            <label class="checkbox"><input type="checkbox" id="buildings"/> Buildings</label>
    +            <label class="checkbox"><input type="checkbox" id="water" checked/> Water</label>
    +            <label class="checkbox"><input type="checkbox" id="roads" checked/> Roads</label>
    +          </fieldset>
    +          <div id="tags">tile-vector, openstreetmap</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=tile-vector" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/tile-vector.js b/mapbox-gl/examples/tile-vector.js
    new file mode 100644
    index 0000000000..e820393ac3
    --- /dev/null
    +++ b/mapbox-gl/examples/tile-vector.js
    @@ -0,0 +1,158 @@
    +var waterLayer = new ol.layer.Vector({
    +  source: new ol.source.TileVector({
    +    format: new ol.format.TopoJSON(),
    +    projection: 'EPSG:3857',
    +    tileGrid: new ol.tilegrid.XYZ({
    +      maxZoom: 19
    +    }),
    +    url: 'http://{a-c}.tile.openstreetmap.us/' +
    +        'vectiles-water-areas/{z}/{x}/{y}.topojson'
    +  }),
    +  style: new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: '#9db9e8'
    +    })
    +  })
    +});
    +
    +var roadStyleCache = {};
    +var roadLayer = new ol.layer.Vector({
    +  source: new ol.source.TileVector({
    +    format: new ol.format.TopoJSON(),
    +    projection: 'EPSG:3857',
    +    tileGrid: new ol.tilegrid.XYZ({
    +      maxZoom: 19
    +    }),
    +    url: 'http://{a-c}.tile.openstreetmap.us/' +
    +        'vectiles-highroad/{z}/{x}/{y}.topojson'
    +  }),
    +  style: function(feature, resolution) {
    +    var kind = feature.get('kind');
    +    var railway = feature.get('railway');
    +    var sort_key = feature.get('sort_key');
    +    var styleKey = kind + '/' + railway + '/' + sort_key;
    +    var styleArray = roadStyleCache[styleKey];
    +    if (!styleArray) {
    +      var color, width;
    +      if (railway) {
    +        color = '#7de';
    +        width = 1;
    +      } else {
    +        color = {
    +          'major_road': '#776',
    +          'minor_road': '#ccb',
    +          'highway': '#f39'
    +        }[kind];
    +        width = kind == 'highway' ? 1.5 : 1;
    +      }
    +      styleArray = [new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: color,
    +          width: width
    +        }),
    +        zIndex: sort_key
    +      })];
    +      roadStyleCache[styleKey] = styleArray;
    +    }
    +    return styleArray;
    +  }
    +});
    +
    +var buildingStyle = [
    +  new ol.style.Style({
    +    fill: new ol.style.Fill({
    +      color: '#666',
    +      opacity: 0.4
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#444',
    +      width: 1
    +    })
    +  })
    +];
    +var buildingLayer = new ol.layer.Vector({
    +  source: new ol.source.TileVector({
    +    format: new ol.format.TopoJSON({
    +      defaultProjection: 'EPSG:4326'
    +    }),
    +    projection: 'EPSG:3857',
    +    tileGrid: new ol.tilegrid.XYZ({
    +      maxZoom: 19
    +    }),
    +    url: 'http://{a-c}.tile.openstreetmap.us/' +
    +        'vectiles-buildings/{z}/{x}/{y}.topojson'
    +  }),
    +  visible: false,
    +  style: function(f, resolution) {
    +    return (resolution < 10) ? buildingStyle : [];
    +  }
    +});
    +
    +var landuseStyleCache = {};
    +var landuseLayer = new ol.layer.Vector({
    +  source: new ol.source.TileVector({
    +    format: new ol.format.TopoJSON({
    +      defaultProjection: 'EPSG:4326'
    +    }),
    +    projection: 'EPSG:3857',
    +    tileGrid: new ol.tilegrid.XYZ({
    +      maxZoom: 19
    +    }),
    +    url: 'http://{a-c}.tile.openstreetmap.us/' +
    +        'vectiles-land-usages/{z}/{x}/{y}.topojson'
    +  }),
    +  visible: false,
    +  style: function(feature, resolution) {
    +    var kind = feature.get('kind');
    +    var styleKey = kind;
    +    var styleArray = landuseStyleCache[styleKey];
    +    if (!styleArray) {
    +      var color, width;
    +      color = {
    +        'parking': '#ddd',
    +        'industrial': '#aaa',
    +        'urban area': '#aaa',
    +        'park': '#76C759',
    +        'school': '#DA10E7',
    +        'garden': '#76C759',
    +        'pitch': '#D58F8D',
    +        'scrub': '#3E7D28',
    +        'residential': '#4C9ED9'
    +      }[kind];
    +      width = kind == 'highway' ? 1.5 : 1;
    +      styleArray = [new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: color,
    +          width: width
    +        }),
    +        fill: new ol.style.Fill({
    +          color: color,
    +          opacity: 0.5
    +        })
    +      })];
    +      landuseStyleCache[styleKey] = styleArray;
    +    }
    +    return styleArray;
    +  }
    +});
    +
    +var map = new ol.Map({
    +  layers: [landuseLayer, buildingLayer, waterLayer, roadLayer],
    +  renderer: 'canvas',
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: ol.proj.transform([-74.0064, 40.7142], 'EPSG:4326', 'EPSG:3857'),
    +    maxZoom: 19,
    +    zoom: 15
    +  })
    +});
    +
    +$('input[type=checkbox]').on('change', function() {
    +  var layer = {
    +    landuse: landuseLayer,
    +    buildings: buildingLayer,
    +    water: waterLayer,
    +    roads: roadLayer
    +  }[$(this).attr('id')];
    +  layer.setVisible(!layer.getVisible());
    +});
    diff --git a/mapbox-gl/examples/tilejson-require.js b/mapbox-gl/examples/tilejson-require.js
    new file mode 100644
    index 0000000000..f337d143e5
    --- /dev/null
    +++ b/mapbox-gl/examples/tilejson-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.TileJSON');
    diff --git a/mapbox-gl/examples/tilejson.html b/mapbox-gl/examples/tilejson.html
    new file mode 100644
    index 0000000000..8a5e94a092
    --- /dev/null
    +++ b/mapbox-gl/examples/tilejson.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>TileJSON example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">TileJSON example</h4>
    +          <p id="shortdesc">Example of a TileJSON layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="tilejson.js" target="_blank">tilejson.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">tilejson</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=tilejson" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/tilejson.js b/mapbox-gl/examples/tilejson.js
    new file mode 100644
    index 0000000000..8865ffa4a3
    --- /dev/null
    +++ b/mapbox-gl/examples/tilejson.js
    @@ -0,0 +1,16 @@
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.TileJSON({
    +        url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.jsonp',
    +        crossOrigin: 'anonymous'
    +      })
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/tileutfgrid-require.js b/mapbox-gl/examples/tileutfgrid-require.js
    new file mode 100644
    index 0000000000..728f24c80a
    --- /dev/null
    +++ b/mapbox-gl/examples/tileutfgrid-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.Overlay');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.TileJSON');
    +goog.require('ol.source.TileUTFGrid');
    diff --git a/mapbox-gl/examples/tileutfgrid.html b/mapbox-gl/examples/tileutfgrid.html
    new file mode 100644
    index 0000000000..23c39c577c
    --- /dev/null
    +++ b/mapbox-gl/examples/tileutfgrid.html
    @@ -0,0 +1,69 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      #country-name {
    +        color: black;
    +        font-size: 12pt;
    +        font-weight: bold;
    +        text-shadow: white 0.1em 0.1em 0.2em;
    +      }
    +    </style>
    +    <title>TileUTFGrid example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">TileUTFGrid example</h4>
    +          <p id="shortdesc">This example shows how to read data from a TileUTFGrid layer.</p>
    +          <p>Point to a country to see its name and flag.</p>
    +          <div id="docs">
    +            <p>Tiles made with <a href="http://tilemill.com/">TileMill</a>. Hosting on MapBox.com or with open-source <a href="https://github.com/klokantech/tileserver-php/">TileServer</a>.</p>
    +            <p>See the <a href="tileutfgrid.js" target="_blank">tileutfgrid.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">utfgrid, tileutfgrid, tilejson</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <div style="display: none;">
    +      <!-- Overlay with the country info -->
    +      <div id="country-info">
    +        <div id="country-name">&nbsp;</div>
    +        <img id="country-flag" src="" />
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=tileutfgrid" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/tileutfgrid.js b/mapbox-gl/examples/tileutfgrid.js
    new file mode 100644
    index 0000000000..a3cf33f362
    --- /dev/null
    +++ b/mapbox-gl/examples/tileutfgrid.js
    @@ -0,0 +1,64 @@
    +var mapLayer = new ol.layer.Tile({
    +  source: new ol.source.TileJSON({
    +    url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json'
    +  })
    +});
    +
    +var gridSource = new ol.source.TileUTFGrid({
    +  url: 'http://api.tiles.mapbox.com/v3/mapbox.geography-class.json'
    +});
    +
    +var gridLayer = new ol.layer.Tile({source: gridSource});
    +
    +var view = new ol.View({
    +  center: [0, 0],
    +  zoom: 1
    +});
    +
    +var mapElement = document.getElementById('map');
    +var map = new ol.Map({
    +  layers: [mapLayer, gridLayer],
    +  target: mapElement,
    +  view: view
    +});
    +
    +var infoElement = document.getElementById('country-info');
    +var flagElement = document.getElementById('country-flag');
    +var nameElement = document.getElementById('country-name');
    +
    +var infoOverlay = new ol.Overlay({
    +  element: infoElement,
    +  offset: [15, 15],
    +  stopEvent: false
    +});
    +map.addOverlay(infoOverlay);
    +
    +var displayCountryInfo = function(coordinate) {
    +  var viewResolution = /** @type {number} */ (view.getResolution());
    +  gridSource.forDataAtCoordinateAndResolution(coordinate, viewResolution,
    +      function(data) {
    +        // If you want to use the template from the TileJSON,
    +        //  load the mustache.js library separately and call
    +        //  info.innerHTML = Mustache.render(gridSource.getTemplate(), data);
    +        mapElement.style.cursor = data ? 'pointer' : '';
    +        if (data) {
    +          /* jshint -W069 */
    +          flagElement.src = 'data:image/png;base64,' + data['flag_png'];
    +          nameElement.innerHTML = data['admin'];
    +          /* jshint +W069 */
    +        }
    +        infoOverlay.setPosition(data ? coordinate : undefined);
    +      });
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var coordinate = map.getEventCoordinate(evt.originalEvent);
    +  displayCountryInfo(coordinate);
    +});
    +
    +map.on('click', function(evt) {
    +  displayCountryInfo(evt.coordinate);
    +});
    diff --git a/mapbox-gl/examples/tissot-require.js b/mapbox-gl/examples/tissot-require.js
    new file mode 100644
    index 0000000000..192a4287a5
    --- /dev/null
    +++ b/mapbox-gl/examples/tissot-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.Sphere');
    +goog.require('ol.View');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.TileWMS');
    +goog.require('ol.source.Vector');
    diff --git a/mapbox-gl/examples/tissot.html b/mapbox-gl/examples/tissot.html
    new file mode 100644
    index 0000000000..8d77e8253a
    --- /dev/null
    +++ b/mapbox-gl/examples/tissot.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tissot indicatrix example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span6">
    +          <h4>EPSG:4326</h4>
    +          <div id="map4326" class="map"></div>
    +        </div>
    +        <div class="span6">
    +          <h4>EPSG:3857</h4>
    +          <div id="map3857" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tissot indicatrix example</h4>
    +          <p id="shortdesc">Example of <a href="http://en.wikipedia.org/wiki/Tissot's_indicatrix">Tissot indicatrix</a> maps. The map on the left is an EPSG:4326 map. The one on the right is EPSG:3857.</p>
    +          <div id="docs">
    +            <p>See the <a href="tissot.js" target="_blank">tissot.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">tissot, circle</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=tissot" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/tissot.js b/mapbox-gl/examples/tissot.js
    new file mode 100644
    index 0000000000..b9b40674c5
    --- /dev/null
    +++ b/mapbox-gl/examples/tissot.js
    @@ -0,0 +1,61 @@
    +var vectorLayer4326 = new ol.layer.Vector({
    +  source: new ol.source.Vector()
    +});
    +
    +var vectorLayer3857 = new ol.layer.Vector({
    +  source: new ol.source.Vector()
    +});
    +
    +var map4326 = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.TileWMS({
    +        url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +        params: {
    +          'LAYERS': 'ne:NE1_HR_LC_SR_W_DR'
    +        }
    +      })
    +    }),
    +    vectorLayer4326
    +  ],
    +  renderer: 'canvas',
    +  target: 'map4326',
    +  view: new ol.View({
    +    projection: 'EPSG:4326',
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var map3857 = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.TileWMS({
    +        url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +        params: {
    +          'LAYERS': 'ne:NE1_HR_LC_SR_W_DR'
    +        }
    +      })
    +    }),
    +    vectorLayer3857
    +  ],
    +  renderer: 'canvas',
    +  target: 'map3857',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 2
    +  })
    +});
    +
    +var wgs84Sphere = new ol.Sphere(6378137);
    +
    +var radius = 800000;
    +var x, y;
    +for (x = -180; x < 180; x += 30) {
    +  for (y = -90; y < 90; y += 30) {
    +    var circle4326 = ol.geom.Polygon.circular(wgs84Sphere, [x, y], radius, 64);
    +    var circle3857 = circle4326.clone().transform('EPSG:4326', 'EPSG:3857');
    +    vectorLayer4326.getSource().addFeature(new ol.Feature(circle4326));
    +    vectorLayer3857.getSource().addFeature(new ol.Feature(circle3857));
    +  }
    +}
    diff --git a/mapbox-gl/examples/topojson-require.js b/mapbox-gl/examples/topojson-require.js
    new file mode 100644
    index 0000000000..7ead36ef0e
    --- /dev/null
    +++ b/mapbox-gl/examples/topojson-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.TileJSON');
    +goog.require('ol.source.TopoJSON');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    diff --git a/mapbox-gl/examples/topojson.html b/mapbox-gl/examples/topojson.html
    new file mode 100644
    index 0000000000..031bc17d32
    --- /dev/null
    +++ b/mapbox-gl/examples/topojson.html
    @@ -0,0 +1,53 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>TopoJSON Example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span8">
    +          <h4 id="title">TopoJSON example</h4>
    +          <p id="shortdesc">Demonstrates rendering of features from a TopoJSON topology.</p>
    +          <div id="docs">
    +            <p>See the <a href="topojson.js" target="_blank">topojson.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, topojson, style</div>
    +        </div>
    +        <div id="info" class="span4">&nbsp;</div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=topojson" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/topojson.js b/mapbox-gl/examples/topojson.js
    new file mode 100644
    index 0000000000..f8ea11de9a
    --- /dev/null
    +++ b/mapbox-gl/examples/topojson.js
    @@ -0,0 +1,35 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.TileJSON({
    +    url: 'http://api.tiles.mapbox.com/v3/mapbox.world-dark.jsonp'
    +  })
    +});
    +
    +var styleArray = [new ol.style.Style({
    +  fill: new ol.style.Fill({
    +    color: 'rgba(255, 255, 255, 0.6)'
    +  }),
    +  stroke: new ol.style.Stroke({
    +    color: '#319FD3',
    +    width: 1
    +  })
    +})];
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.TopoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/topojson/world-110m.json'
    +  }),
    +  style: function(feature, resolution) {
    +    // don't want to render the full world polygon, which repeats all countries
    +    return feature.getId() !== undefined ? styleArray : null;
    +  }
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    diff --git a/mapbox-gl/examples/vector-labels-require.js b/mapbox-gl/examples/vector-labels-require.js
    new file mode 100644
    index 0000000000..838cd8f5d9
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-labels-require.js
    @@ -0,0 +1,12 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.style.Text');
    diff --git a/mapbox-gl/examples/vector-labels.html b/mapbox-gl/examples/vector-labels.html
    new file mode 100644
    index 0000000000..9d316e64c1
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-labels.html
    @@ -0,0 +1,361 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +      h2 {
    +        font-size: 1.5em;
    +        line-height: 15px;
    +      }
    +
    +      .scale-cnt {
    +        margin: 5px;
    +      }
    +
    +      .edit-form-ctn {
    +      }
    +
    +      .edit-form {
    +        float: left;
    +        margin: 5px;
    +        width: 230px;
    +        padding: 4px;
    +        border: 1px solid black;
    +      }
    +
    +      .edit-form input[type="button"] {
    +        float: right;
    +      }
    +
    +      .edit-form-elem label {
    +        display: block;
    +        float: left;
    +        width: 85px;
    +      }
    +
    +      .edit-form-elem input[type="text"] {
    +        width: 60px;
    +      }
    +
    +      .edit-form-elem select {
    +        width: 130px;
    +      }
    +    </style>
    +    <title>Vector labels example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="edit-form">
    +        <input id="refresh-points" type="button" value="Refresh" />
    +        <h2>Points</h2>
    +        <div class="edit-form-elem">
    +          <label>Text: </label>
    +          <select id="points-text">
    +            <option value="hide">Hide</option>
    +            <option value="normal">Normal</option>
    +            <option value="shorten" selected="selected">Shorten</option>
    +            <option value="wrap">Wrap</option>
    +          </select>
    +          <br />
    +          <label title="Max Resolution Denominator">MaxReso.:</label>
    +          <select id="points-maxreso">
    +            <option value="38400">38,400</option>
    +            <option value="19200">19,200</option>
    +            <option value="9600">9,600</option>
    +            <option value="4800">4,800</option>
    +            <option value="2400">2,400</option>
    +            <option value="1200" selected="selected">1,200</option>
    +            <option value="600">600</option>
    +            <option value="300">300</option>
    +            <option value="150">150</option>
    +            <option value="75">75</option>
    +            <option value="32">32</option>
    +            <option value="16">16</option>
    +            <option value="8">8</option>
    +          </select>
    +          <br />
    +          <label>Align: </label>
    +          <select id="points-align">
    +            <option value="center" selected="selected">Center</option>
    +            <option value="end">End</option>
    +            <option value="left">Left</option>
    +            <option value="right">Right</option>
    +            <option value="start">Start</option>
    +          </select>
    +          <br />
    +          <label>Baseline: </label>
    +          <select id="points-baseline">
    +            <option value="alphabetic">Alphabetic</option>
    +            <option value="bottom">Bottom</option>
    +            <option value="hanging">Hanging</option>
    +            <option value="ideographic">Ideographic</option>
    +            <option value="middle" selected="selected">Middle</option>
    +            <option value="top">Top</option>
    +          </select>
    +          <br />
    +          <label>Rotation: </label>
    +          <select id="points-rotation">
    +            <option value="0">0°</option>
    +            <option value="0.785398164">45°</option>
    +            <option value="1.570796327">90°</option>
    +          </select>
    +          <br />
    +          <label>Font: </label>
    +          <select id="points-font">
    +            <option value="Arial" selected="selected">Arial</option>
    +            <option value="Courier New">Courier New</option>
    +            <option value="Quattrocento Sans">Quattrocento</option>
    +            <option value="Verdana">Verdana</option>
    +          </select>
    +          <br />
    +          <label>Weight: </label>
    +          <select id="points-weight">
    +            <option value="bold">Bold</option>
    +            <option value="normal" selected="selected">Normal</option>
    +          </select>
    +          <br />
    +          <label>Size: </label>
    +          <input type="text" value="12px" id="points-size" />
    +          <br />
    +          <label>Offset X:</label>
    +          <input type="text" value="0" id="points-offset-x" />
    +          <br />
    +          <label>Offset Y:</label>
    +          <input type="text" value="0" id="points-offset-y" />
    +          <br />
    +          <label>Color :</label>
    +          <input type="text" value="#aa3300" id="points-color" />
    +          <br />
    +          <label title="Outline Color">O. Color:</label>
    +          <input type="text" value="#ffffff" id="points-outline" />
    +          <br />
    +          <label title="Outline Width">O. Width :</label>
    +          <input type="text" value="3" id="points-outline-width" />
    +        </div>
    +      </div>
    +
    +      <div class="edit-form">
    +        <input id="refresh-lines" type="button" value="Refresh" />
    +        <h2>Lines</h2>
    +        <div class="edit-form-elem">
    +          <label>Text: </label>
    +          <select id="lines-text">
    +            <option value="hide">Hide</option>
    +            <option value="normal">Normal</option>
    +            <option value="shorten">Shorten</option>
    +            <option value="wrap" selected="selected">Wrap</option>
    +          </select>
    +          <br />
    +          <label title="Max Resolution Denominator">MaxReso.:</label>
    +          <select id="lines-maxreso">
    +            <option value="38400">38,400</option>
    +            <option value="19200">19,200</option>
    +            <option value="9600">9,600</option>
    +            <option value="4800">4,800</option>
    +            <option value="2400">2,400</option>
    +            <option value="1200" selected="selected">1,200</option>
    +            <option value="600">600</option>
    +            <option value="300">300</option>
    +            <option value="150">150</option>
    +            <option value="75">75</option>
    +            <option value="32">32</option>
    +            <option value="16">16</option>
    +            <option value="8">8</option>
    +          </select>
    +          <br />
    +          <label>Align: </label>
    +          <select id="lines-align">
    +            <option value="center" selected="selected">Center</option>
    +            <option value="end">End</option>
    +            <option value="left">Left</option>
    +            <option value="right">Right</option>
    +            <option value="start">Start</option>
    +          </select>
    +          <br />
    +          <label>Baseline: </label>
    +          <select id="lines-baseline">
    +            <option value="alphabetic">Alphabetic</option>
    +            <option value="bottom">Bottom</option>
    +            <option value="hanging">Hanging</option>
    +            <option value="ideographic">Ideographic</option>
    +            <option value="middle" selected="selected">Middle</option>
    +            <option value="top">Top</option>
    +          </select>
    +          <br />
    +          <label>Rotation: </label>
    +          <select id="lines-rotation">
    +            <option value="0">0°</option>
    +            <option value="0.785398164">45°</option>
    +            <option value="1.570796327">90°</option>
    +          </select>
    +          <br />
    +          <label>Font: </label>
    +          <select id="lines-font">
    +            <option value="Arial">Arial</option>
    +            <option value="Courier New" selected="selected">Courier New</option>
    +            <option value="Quattrocento Sans">Quattrocento</option>
    +            <option value="Verdana">Verdana</option>
    +          </select>
    +          <br />
    +          <label>Weight: </label>
    +          <select id="lines-weight">
    +            <option value="bold" selected="selected">Bold</option>
    +            <option value="normal">Normal</option>
    +          </select>
    +          <br />
    +          <label>Size: </label>
    +          <input type="text" value="12px" id="lines-size" />
    +          <br />
    +          <label>Offset X:</label>
    +          <input type="text" value="0" id="lines-offset-x" />
    +          <br />
    +          <label>Offset Y:</label>
    +          <input type="text" value="0" id="lines-offset-y" />
    +          <br />
    +          <label>Color :</label>
    +          <input type="text" value="green" id="lines-color" />
    +          <br />
    +          <label title="Outline Color">O. Color:</label>
    +          <input type="text" value="#ffffff" id="lines-outline" />
    +          <br />
    +          <label title="Outline Width">O. Width :</label>
    +          <input type="text" value="3" id="lines-outline-width" />
    +        </div>
    +      </div>
    +
    +      <div class="edit-form">
    +        <input id="refresh-polygons" type="button" value="Refresh" />
    +        <h2>Polygons</h2>
    +        <div class="edit-form-elem">
    +          <label>Text: </label>
    +          <select id="polygons-text">
    +            <option value="hide">Hide</option>
    +            <option value="normal" selected="selected">Normal</option>
    +            <option value="shorten">Shorten</option>
    +            <option value="wrap">Wrap</option>
    +          </select>
    +          <br />
    +          <label title="Max Resolution Denominator">MaxReso.:</label>
    +          <select id="polygons-maxreso">
    +            <option value="38400">38,400</option>
    +            <option value="19200">19,200</option>
    +            <option value="9600">9,600</option>
    +            <option value="4800">4,800</option>
    +            <option value="2400">2,400</option>
    +            <option value="1200" selected="selected">1,200</option>
    +            <option value="600">600</option>
    +            <option value="300">300</option>
    +            <option value="150">150</option>
    +            <option value="75">75</option>
    +            <option value="32">32</option>
    +            <option value="16">16</option>
    +            <option value="8">8</option>
    +          </select>
    +          <br />
    +          <label>Align: </label>
    +          <select id="polygons-align">
    +            <option value="center" selected="selected">Center</option>
    +            <option value="end">End</option>
    +            <option value="left">Left</option>
    +            <option value="right">Right</option>
    +            <option value="start">Start</option>
    +          </select>
    +          <br />
    +          <label>Baseline: </label>
    +          <select id="polygons-baseline">
    +            <option value="alphabetic">Alphabetic</option>
    +            <option value="bottom">Bottom</option>
    +            <option value="hanging">Hanging</option>
    +            <option value="ideographic">Ideographic</option>
    +            <option value="middle" selected="selected">Middle</option>
    +            <option value="top">Top</option>
    +          </select>
    +          <br />
    +          <label>Rotation: </label>
    +          <select id="polygons-rotation">
    +            <option value="0">0°</option>
    +            <option value="0.785398164">45°</option>
    +            <option value="1.570796327">90°</option>
    +          </select>
    +          <br />
    +          <label>Font: </label>
    +          <select id="polygons-font">
    +            <option value="Arial">Arial</option>
    +            <option value="Courier New">Courier New</option>
    +            <option value="Quattrocento Sans">Quattrocento</option>
    +            <option value="Verdana" selected="selected">Verdana</option>
    +          </select>
    +          <br />
    +          <label>Weight: </label>
    +          <select id="polygons-weight">
    +            <option value="bold" selected="selected">Bold</option>
    +            <option value="normal">Normal</option>
    +          </select>
    +          <br />
    +          <label>Size: </label>
    +          <input type="text" value="10px" id="polygons-size" />
    +          <br />
    +          <label>Offset X:</label>
    +          <input type="text" value="0" id="polygons-offset-x" />
    +          <br />
    +          <label>Offset Y:</label>
    +          <input type="text" value="0" id="polygons-offset-y" />
    +          <br />
    +          <label>Color :</label>
    +          <input type="text" value="blue" id="polygons-color" />
    +          <br />
    +          <label title="Outline Color">O. Color:</label>
    +          <input type="text" value="#ffffff" id="polygons-outline" />
    +          <br />
    +          <label title="Outline Width">O. Width :</label>
    +          <input type="text" value="3" id="polygons-outline-width" />
    +        </div>
    +      </div>
    +
    +    <div style="clear:left;"></div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Vector labels example</h4>
    +          <p id="shortdesc">Example of GeoJSON features with labels.</p>
    +          <div id="docs">
    +            <p>See the <a href="vector-labels.js" target="_blank">vector-labels.js source</a> to see how this is done.</p>
    +            <p><strong>Note:</strong> The 'Text/Wrap' option is currently not working properly.  This is because ol3 uses Canvas's strokeText and fillText functions that do not support text wrapping.</p>
    +          </div>
    +          <div id="tags">geojson, vector, openstreetmap, label</div>
    +        </div>
    +
    +      </div>
    +
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=vector-labels" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/vector-labels.js b/mapbox-gl/examples/vector-labels.js
    new file mode 100644
    index 0000000000..a51446df34
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-labels.js
    @@ -0,0 +1,224 @@
    +var myDom = {
    +  points: {
    +    text: document.getElementById('points-text'),
    +    align: document.getElementById('points-align'),
    +    baseline: document.getElementById('points-baseline'),
    +    rotation: document.getElementById('points-rotation'),
    +    font: document.getElementById('points-font'),
    +    weight: document.getElementById('points-weight'),
    +    size: document.getElementById('points-size'),
    +    offsetX: document.getElementById('points-offset-x'),
    +    offsetY: document.getElementById('points-offset-y'),
    +    color: document.getElementById('points-color'),
    +    outline: document.getElementById('points-outline'),
    +    outlineWidth: document.getElementById('points-outline-width'),
    +    maxreso: document.getElementById('points-maxreso')
    +  },
    +  lines: {
    +    text: document.getElementById('lines-text'),
    +    align: document.getElementById('lines-align'),
    +    baseline: document.getElementById('lines-baseline'),
    +    rotation: document.getElementById('lines-rotation'),
    +    font: document.getElementById('lines-font'),
    +    weight: document.getElementById('lines-weight'),
    +    size: document.getElementById('lines-size'),
    +    offsetX: document.getElementById('lines-offset-x'),
    +    offsetY: document.getElementById('lines-offset-y'),
    +    color: document.getElementById('lines-color'),
    +    outline: document.getElementById('lines-outline'),
    +    outlineWidth: document.getElementById('lines-outline-width'),
    +    maxreso: document.getElementById('lines-maxreso')
    +  },
    +  polygons: {
    +    text: document.getElementById('polygons-text'),
    +    align: document.getElementById('polygons-align'),
    +    baseline: document.getElementById('polygons-baseline'),
    +    rotation: document.getElementById('polygons-rotation'),
    +    font: document.getElementById('polygons-font'),
    +    weight: document.getElementById('polygons-weight'),
    +    size: document.getElementById('polygons-size'),
    +    offsetX: document.getElementById('polygons-offset-x'),
    +    offsetY: document.getElementById('polygons-offset-y'),
    +    color: document.getElementById('polygons-color'),
    +    outline: document.getElementById('polygons-outline'),
    +    outlineWidth: document.getElementById('polygons-outline-width'),
    +    maxreso: document.getElementById('polygons-maxreso')
    +  }
    +};
    +
    +var getText = function(feature, resolution, dom) {
    +  var type = dom.text.value;
    +  var maxResolution = dom.maxreso.value;
    +  var text = feature.get('name');
    +
    +  if (resolution > maxResolution) {
    +    text = '';
    +  } else if (type == 'hide') {
    +    text = '';
    +  } else if (type == 'shorten') {
    +    text = text.trunc(12);
    +  } else if (type == 'wrap') {
    +    text = stringDivider(text, 16, '\n');
    +  }
    +
    +  return text;
    +};
    +
    +
    +var createTextStyle = function(feature, resolution, dom) {
    +  var align = dom.align.value;
    +  var baseline = dom.baseline.value;
    +  var size = dom.size.value;
    +  var offsetX = parseInt(dom.offsetX.value, 10);
    +  var offsetY = parseInt(dom.offsetY.value, 10);
    +  var weight = dom.weight.value;
    +  var rotation = parseFloat(dom.rotation.value);
    +  var font = weight + ' ' + size + ' ' + dom.font.value;
    +  var fillColor = dom.color.value;
    +  var outlineColor = dom.outline.value;
    +  var outlineWidth = parseInt(dom.outlineWidth.value, 10);
    +
    +  return new ol.style.Text({
    +    textAlign: align,
    +    textBaseline: baseline,
    +    font: font,
    +    text: getText(feature, resolution, dom),
    +    fill: new ol.style.Fill({color: fillColor}),
    +    stroke: new ol.style.Stroke({color: outlineColor, width: outlineWidth}),
    +    offsetX: offsetX,
    +    offsetY: offsetY,
    +    rotation: rotation
    +  });
    +};
    +
    +
    +// Polygons
    +var createPolygonStyleFunction = function() {
    +  return function(feature, resolution) {
    +    var style = new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: 'blue',
    +        width: 1
    +      }),
    +      fill: new ol.style.Fill({
    +        color: 'rgba(0, 0, 255, 0.1)'
    +      }),
    +      text: createTextStyle(feature, resolution, myDom.polygons)
    +    });
    +    return [style];
    +  };
    +};
    +
    +var vectorPolygons = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/polygon-samples.geojson'
    +  }),
    +  style: createPolygonStyleFunction()
    +});
    +
    +
    +// Lines
    +var createLineStyleFunction = function() {
    +  return function(feature, resolution) {
    +    var style = new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: 'green',
    +        width: 2
    +      }),
    +      text: createTextStyle(feature, resolution, myDom.lines)
    +    });
    +    return [style];
    +  };
    +};
    +
    +var vectorLines = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/line-samples.geojson'
    +  }),
    +  style: createLineStyleFunction()
    +});
    +
    +
    +// Points
    +var createPointStyleFunction = function() {
    +  return function(feature, resolution) {
    +    var style = new ol.style.Style({
    +      image: new ol.style.Circle({
    +        radius: 10,
    +        fill: new ol.style.Fill({color: 'rgba(255, 0, 0, 0.1)'}),
    +        stroke: new ol.style.Stroke({color: 'red', width: 1})
    +      }),
    +      text: createTextStyle(feature, resolution, myDom.points)
    +    });
    +    return [style];
    +  };
    +};
    +
    +var vectorPoints = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/point-samples.geojson'
    +  }),
    +  style: createPointStyleFunction()
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'osm'})
    +    }),
    +    vectorPolygons,
    +    vectorLines,
    +    vectorPoints
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-8161939, 6095025],
    +    zoom: 8
    +  })
    +});
    +
    +$('#refresh-points').click(function() {
    +  vectorPoints.setStyle(createPointStyleFunction());
    +});
    +
    +$('#refresh-lines').click(function() {
    +  vectorLines.setStyle(createLineStyleFunction());
    +});
    +
    +$('#refresh-polygons').click(function() {
    +  vectorPolygons.setStyle(createPolygonStyleFunction());
    +});
    +
    +
    +/**
    + * @param {number} n The max number of characters to keep.
    + * @return {string} Truncated string.
    + */
    +String.prototype.trunc = String.prototype.trunc ||
    +    function(n) {
    +      return this.length > n ? this.substr(0, n - 1) + '...' : this.substr(0);
    +    };
    +
    +
    +// http://stackoverflow.com/questions/14484787/wrap-text-in-javascript
    +function stringDivider(str, width, spaceReplacer) {
    +  if (str.length > width) {
    +    var p = width;
    +    for (; p > 0 && (str[p] != ' ' && str[p] != '-'); p--) {
    +    }
    +    if (p > 0) {
    +      var left;
    +      if (str.substring(p, p + 1) == '-') {
    +        left = str.substring(0, p + 1);
    +      } else {
    +        left = str.substring(0, p);
    +      }
    +      var right = str.substring(p + 1);
    +      return left + spaceReplacer + stringDivider(right, width, spaceReplacer);
    +    }
    +  }
    +  return str;
    +}
    diff --git a/mapbox-gl/examples/vector-layer-require.js b/mapbox-gl/examples/vector-layer-require.js
    new file mode 100644
    index 0000000000..3fc8e9dd5e
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-layer-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.GeoJSON');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.style.Text');
    diff --git a/mapbox-gl/examples/vector-layer.html b/mapbox-gl/examples/vector-layer.html
    new file mode 100644
    index 0000000000..3589342a69
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-layer.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Vector layer example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">Vector layer example</h4>
    +          <p id="shortdesc">Example of a countries vector layer with country information on hover and country labels at higher zoom levels.</p>
    +          <div id="docs">
    +            <p>See the <a href="vector-layer.js" target="_blank">vector-layer.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, geojson, style, feature overlay</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=vector-layer" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/vector-layer.js b/mapbox-gl/examples/vector-layer.js
    new file mode 100644
    index 0000000000..c9e607babe
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-layer.js
    @@ -0,0 +1,114 @@
    +var style = new ol.style.Style({
    +  fill: new ol.style.Fill({
    +    color: 'rgba(255, 255, 255, 0.6)'
    +  }),
    +  stroke: new ol.style.Stroke({
    +    color: '#319FD3',
    +    width: 1
    +  }),
    +  text: new ol.style.Text({
    +    font: '12px Calibri,sans-serif',
    +    fill: new ol.style.Fill({
    +      color: '#000'
    +    }),
    +    stroke: new ol.style.Stroke({
    +      color: '#fff',
    +      width: 3
    +    })
    +  })
    +});
    +var styles = [style];
    +var vectorLayer = new ol.layer.Vector({
    +  source: new ol.source.GeoJSON({
    +    projection: 'EPSG:3857',
    +    url: 'data/geojson/countries.geojson'
    +  }),
    +  style: function(feature, resolution) {
    +    style.getText().setText(resolution < 5000 ? feature.get('name') : '');
    +    return styles;
    +  }
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.MapQuest({layer: 'sat'})
    +    }),
    +    vectorLayer
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [0, 0],
    +    zoom: 1
    +  })
    +});
    +
    +var highlightStyleCache = {};
    +
    +var featureOverlay = new ol.FeatureOverlay({
    +  map: map,
    +  style: function(feature, resolution) {
    +    var text = resolution < 5000 ? feature.get('name') : '';
    +    if (!highlightStyleCache[text]) {
    +      highlightStyleCache[text] = [new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: '#f00',
    +          width: 1
    +        }),
    +        fill: new ol.style.Fill({
    +          color: 'rgba(255,0,0,0.1)'
    +        }),
    +        text: new ol.style.Text({
    +          font: '12px Calibri,sans-serif',
    +          text: text,
    +          fill: new ol.style.Fill({
    +            color: '#000'
    +          }),
    +          stroke: new ol.style.Stroke({
    +            color: '#f00',
    +            width: 3
    +          })
    +        })
    +      })];
    +    }
    +    return highlightStyleCache[text];
    +  }
    +});
    +
    +var highlight;
    +var displayFeatureInfo = function(pixel) {
    +
    +  var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
    +    return feature;
    +  });
    +
    +  var info = document.getElementById('info');
    +  if (feature) {
    +    info.innerHTML = feature.getId() + ': ' + feature.get('name');
    +  } else {
    +    info.innerHTML = '&nbsp;';
    +  }
    +
    +  if (feature !== highlight) {
    +    if (highlight) {
    +      featureOverlay.removeFeature(highlight);
    +    }
    +    if (feature) {
    +      featureOverlay.addFeature(feature);
    +    }
    +    highlight = feature;
    +  }
    +
    +};
    +
    +map.on('pointermove', function(evt) {
    +  if (evt.dragging) {
    +    return;
    +  }
    +  var pixel = map.getEventPixel(evt.originalEvent);
    +  displayFeatureInfo(pixel);
    +});
    +
    +map.on('click', function(evt) {
    +  displayFeatureInfo(evt.pixel);
    +});
    diff --git a/mapbox-gl/examples/vector-osm-require.js b/mapbox-gl/examples/vector-osm-require.js
    new file mode 100644
    index 0000000000..4a4436d90d
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-osm-require.js
    @@ -0,0 +1,15 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.format.OSMXML');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.loadingstrategy');
    +goog.require('ol.proj');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.ServerVector');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.tilegrid.XYZ');
    diff --git a/mapbox-gl/examples/vector-osm.html b/mapbox-gl/examples/vector-osm.html
    new file mode 100644
    index 0000000000..b505c591a4
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-osm.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>OSM XML example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>          
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">OSM XML example</h4>
    +          <p id="shortdesc">Example of using the OSM XML source.  Vector data is loaded dynamically from a server using a tiling strategy.</p>
    +          <div id="docs">
    +            <p>See the <a href="vector-osm.js" target="_blank">vector-osm.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, osm, xml, loading, server</div>
    +        </div>
    +        <div class="span4 offset4">
    +          <div id="info" class="alert alert-success">
    +            &nbsp;
    +          </div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=vector-osm" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/vector-osm.js b/mapbox-gl/examples/vector-osm.js
    new file mode 100644
    index 0000000000..4d7610c7dc
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-osm.js
    @@ -0,0 +1,129 @@
    +var styles = {
    +  'amenity': {
    +    'parking': [
    +      new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(170, 170, 170, 1.0)',
    +          width: 1
    +        }),
    +        fill: new ol.style.Fill({
    +          color: 'rgba(170, 170, 170, 0.3)'
    +        })
    +      })
    +    ]
    +  },
    +  'building': {
    +    '.*': [
    +      new ol.style.Style({
    +        zIndex: 100,
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(246, 99, 79, 1.0)',
    +          width: 1
    +        }),
    +        fill: new ol.style.Fill({
    +          color: 'rgba(246, 99, 79, 0.3)'
    +        })
    +      })
    +    ]
    +  },
    +  'highway': {
    +    'service': [
    +      new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(255, 255, 255, 1.0)',
    +          width: 2
    +        })
    +      })
    +    ],
    +    '.*': [
    +      new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(255, 255, 255, 1.0)',
    +          width: 3
    +        })
    +      })
    +    ]
    +  },
    +  'landuse': {
    +    'forest|grass|allotments': [
    +      new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: 'rgba(140, 208, 95, 1.0)',
    +          width: 1
    +        }),
    +        fill: new ol.style.Fill({
    +          color: 'rgba(140, 208, 95, 0.3)'
    +        })
    +      })
    +    ]
    +  },
    +  'natural': {
    +    'tree': [
    +      new ol.style.Style({
    +        image: new ol.style.Circle({
    +          radius: 2,
    +          fill: new ol.style.Fill({
    +            color: 'rgba(140, 208, 95, 1.0)'
    +          }),
    +          stroke: null
    +        })
    +      })
    +    ]
    +  }
    +};
    +
    +var vectorSource = new ol.source.ServerVector({
    +  format: new ol.format.OSMXML(),
    +  loader: function(extent, resolution, projection) {
    +    var epsg4326Extent =
    +        ol.proj.transformExtent(extent, projection, 'EPSG:4326');
    +    var url = 'http://overpass-api.de/api/xapi?map?bbox=' +
    +        epsg4326Extent.join(',');
    +    $.ajax(url).then(function(response) {
    +      vectorSource.addFeatures(vectorSource.readFeatures(response));
    +    });
    +  },
    +  strategy: ol.loadingstrategy.createTile(new ol.tilegrid.XYZ({
    +    maxZoom: 19
    +  })),
    +  projection: 'EPSG:3857'
    +});
    +
    +var vector = new ol.layer.Vector({
    +  source: vectorSource,
    +  style: function(feature, resolution) {
    +    for (var key in styles) {
    +      var value = feature.get(key);
    +      if (value !== undefined) {
    +        for (var regexp in styles[key]) {
    +          if (new RegExp(regexp).test(value)) {
    +            return styles[key][regexp];
    +          }
    +        }
    +      }
    +    }
    +    return null;
    +  }
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({
    +    imagerySet: 'Aerial',
    +    key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: document.getElementById('map'),
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [739218, 5906096],
    +    maxZoom: 19,
    +    zoom: 17
    +  })
    +});
    diff --git a/mapbox-gl/examples/vector-wfs-require.js b/mapbox-gl/examples/vector-wfs-require.js
    new file mode 100644
    index 0000000000..59adb2b38d
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-wfs-require.js
    @@ -0,0 +1,11 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.GeoJSON');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.loadingstrategy');
    +goog.require('ol.source.BingMaps');
    +goog.require('ol.source.ServerVector');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.tilegrid.XYZ');
    diff --git a/mapbox-gl/examples/vector-wfs.html b/mapbox-gl/examples/vector-wfs.html
    new file mode 100644
    index 0000000000..e038956163
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-wfs.html
    @@ -0,0 +1,50 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WFS example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>          
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">WFS example</h4>
    +          <p id="shortdesc">Example of using WFS with a BBOX strategy.</p>
    +          <div id="docs">
    +            <p>See the <a href="vector-wfs.js" target="_blank">vector-wfs.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">vector, WFS, bbox, loading, server</div>
    +        </div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=vector-wfs" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/vector-wfs.js b/mapbox-gl/examples/vector-wfs.js
    new file mode 100644
    index 0000000000..4d4b4f78cf
    --- /dev/null
    +++ b/mapbox-gl/examples/vector-wfs.js
    @@ -0,0 +1,48 @@
    +var vectorSource = new ol.source.ServerVector({
    +  format: new ol.format.GeoJSON(),
    +  loader: function(extent, resolution, projection) {
    +    var url = 'http://demo.boundlessgeo.com/geoserver/wfs?service=WFS&' +
    +        'version=1.1.0&request=GetFeature&typename=osm:water_areas&' +
    +        'outputFormat=text/javascript&format_options=callback:loadFeatures' +
    +        '&srsname=EPSG:3857&bbox=' + extent.join(',') + ',EPSG:3857';
    +    $.ajax({
    +      url: url,
    +      dataType: 'jsonp'
    +    });
    +  },
    +  strategy: ol.loadingstrategy.createTile(new ol.tilegrid.XYZ({
    +    maxZoom: 19
    +  })),
    +  projection: 'EPSG:3857'
    +});
    +
    +var loadFeatures = function(response) {
    +  vectorSource.addFeatures(vectorSource.readFeatures(response));
    +};
    +
    +var vector = new ol.layer.Vector({
    +  source: vectorSource,
    +  style: new ol.style.Style({
    +    stroke: new ol.style.Stroke({
    +      color: 'rgba(0, 0, 255, 1.0)',
    +      width: 2
    +    })
    +  })
    +});
    +
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.BingMaps({
    +    imagerySet: 'Aerial',
    +    key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: document.getElementById('map'),
    +  view: new ol.View({
    +    center: [-8908887.277395891, 5381918.072437216],
    +    maxZoom: 19,
    +    zoom: 12
    +  })
    +});
    diff --git a/mapbox-gl/examples/wkt-require.js b/mapbox-gl/examples/wkt-require.js
    new file mode 100644
    index 0000000000..32b62fed86
    --- /dev/null
    +++ b/mapbox-gl/examples/wkt-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Feature');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.WKT');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.Vector');
    diff --git a/mapbox-gl/examples/wkt.html b/mapbox-gl/examples/wkt.html
    new file mode 100644
    index 0000000000..a00a58f843
    --- /dev/null
    +++ b/mapbox-gl/examples/wkt.html
    @@ -0,0 +1,50 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WKT example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>          
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">WKT example</h4>
    +          <p id="shortdesc">Example of using the WKT parser.</p>
    +          <div id="docs">
    +            <p>See the <a href="wkt.js" target="_blank">wkt.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">WKT Well Known Text</div>
    +        </div>
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wkt" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wkt.js b/mapbox-gl/examples/wkt.js
    new file mode 100644
    index 0000000000..af19d1e5ea
    --- /dev/null
    +++ b/mapbox-gl/examples/wkt.js
    @@ -0,0 +1,25 @@
    +var raster = new ol.layer.Tile({
    +  source: new ol.source.OSM()
    +});
    +
    +var format = new ol.format.WKT();
    +var feature = format.readFeature(
    +    'POLYGON((10.689697265625 -25.0927734375, 34.595947265625 ' +
    +        '-20.1708984375, 38.814697265625 -35.6396484375, 13.502197265625 ' +
    +        '-39.1552734375, 10.689697265625 -25.0927734375))');
    +feature.getGeometry().transform('EPSG:4326', 'EPSG:3857');
    +
    +var vector = new ol.layer.Vector({
    +  source: new ol.source.Vector({
    +    features: [feature]
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [raster, vector],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [2952104.019976033, -3277504.823700756],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/wms-capabilities-require.js b/mapbox-gl/examples/wms-capabilities-require.js
    new file mode 100644
    index 0000000000..5f3c4a24b6
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-capabilities-require.js
    @@ -0,0 +1 @@
    +goog.require('ol.format.WMSCapabilities');
    diff --git a/mapbox-gl/examples/wms-capabilities.html b/mapbox-gl/examples/wms-capabilities.html
    new file mode 100644
    index 0000000000..c0c70a11f7
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-capabilities.html
    @@ -0,0 +1,49 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMS GetCapabilities parsing example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">WMS GetCapabilities parsing example</h4>
    +          <p id="shortdesc">Example of parsing a WMS GetCapabilities response.</p>
    +          <div id="docs">
    +            <p>See the <a href="wms-capabilities.js" target="_blank">wms-capabilities.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, capabilities, getcapabilities</div>
    +        </div>
    +
    +        <div class="span8">
    +          <pre id="log"></pre>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-capabilities" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-capabilities.js b/mapbox-gl/examples/wms-capabilities.js
    new file mode 100644
    index 0000000000..f36bc67b2d
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-capabilities.js
    @@ -0,0 +1,6 @@
    +var parser = new ol.format.WMSCapabilities();
    +
    +$.ajax('data/ogcsample.xml').then(function(response) {
    +  var result = parser.read(response);
    +  $('#log').html(window.JSON.stringify(result, null, 2));
    +});
    diff --git a/mapbox-gl/examples/wms-custom-proj-require.js b/mapbox-gl/examples/wms-custom-proj-require.js
    new file mode 100644
    index 0000000000..3564e94db2
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-custom-proj-require.js
    @@ -0,0 +1,9 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.ScaleLine');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.source.TileWMS');
    diff --git a/mapbox-gl/examples/wms-custom-proj.html b/mapbox-gl/examples/wms-custom-proj.html
    new file mode 100644
    index 0000000000..f87405a33e
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-custom-proj.html
    @@ -0,0 +1,50 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tiled WMS with custom projection example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tiled WMS with custom projection example</h4>
    +          <p id="shortdesc">Example of two tiled WMS layers (Pixelmap 1:1'000'000 and national parks) using the projection EPSG:21781.</p>
    +          <div id="docs">
    +            <p>See the <a href="wms-custom-proj.js" target="_blank">wms-custom-proj.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, tile, tilelayer, projection</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-custom-proj" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-custom-proj.js b/mapbox-gl/examples/wms-custom-proj.js
    new file mode 100644
    index 0000000000..b04d81b5b8
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-custom-proj.js
    @@ -0,0 +1,236 @@
    +// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection.
    +// So we create a projection instance for EPSG:21781 and pass it to
    +// ol.proj.addProjection to make it available to the library.
    +
    +var projection = new ol.proj.Projection({
    +  code: 'EPSG:21781',
    +  // The extent is used to determine zoom level 0. Recommended values for a
    +  // projection's validity extent can be found at http://epsg.io/.
    +  extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864],
    +  units: 'm'
    +});
    +ol.proj.addProjection(projection);
    +
    +// We also declare EPSG:21781/EPSG:4326 transform functions. These functions
    +// are necessary for the ScaleLine control and when calling ol.proj.transform
    +// for setting the view's initial center (see below).
    +
    +ol.proj.addCoordinateTransforms('EPSG:4326', projection,
    +    function(coordinate) {
    +      return [
    +        WGStoCHy(coordinate[1], coordinate[0]),
    +        WGStoCHx(coordinate[1], coordinate[0])
    +      ];
    +    },
    +    function(coordinate) {
    +      return [
    +        CHtoWGSlng(coordinate[0], coordinate[1]),
    +        CHtoWGSlat(coordinate[0], coordinate[1])
    +      ];
    +    });
    +
    +var extent = [420000, 30000, 900000, 350000];
    +var layers = [
    +  new ol.layer.Tile({
    +    extent: extent,
    +    source: new ol.source.TileWMS({
    +      url: 'http://wms.geo.admin.ch/',
    +      crossOrigin: 'anonymous',
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'Pixelmap 1:1000000 / geo.admin.ch</a>'
    +      })],
    +      params: {
    +        'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale',
    +        'FORMAT': 'image/jpeg'
    +      },
    +      serverType: 'mapserver'
    +    })
    +  }),
    +  new ol.layer.Tile({
    +    extent: extent,
    +    source: new ol.source.TileWMS({
    +      url: 'http://wms.geo.admin.ch/',
    +      crossOrigin: 'anonymous',
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'National parks / geo.admin.ch</a>'
    +      })],
    +      params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'},
    +      serverType: 'mapserver'
    +    })
    +  })
    +];
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.ScaleLine({
    +      units: 'metric'
    +    })
    +  ]),
    +  layers: layers,
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    projection: projection,
    +    center: ol.proj.transform([8.23, 46.86], 'EPSG:4326', 'EPSG:21781'),
    +    extent: extent,
    +    zoom: 2
    +  })
    +});
    +
    +
    +
    +/*
    + * Swiss projection transform functions downloaded from
    + * http://www.swisstopo.admin.ch/internet/swisstopo/en/home/products/software/products/skripts.html
    + */
    +
    +// Convert WGS lat/long (° dec) to CH y
    +function WGStoCHy(lat, lng) {
    +
    +  // Converts degrees dec to sex
    +  lat = DECtoSEX(lat);
    +  lng = DECtoSEX(lng);
    +
    +  // Converts degrees to seconds (sex)
    +  lat = DEGtoSEC(lat);
    +  lng = DEGtoSEC(lng);
    +
    +  // Axiliary values (% Bern)
    +  var lat_aux = (lat - 169028.66) / 10000;
    +  var lng_aux = (lng - 26782.5) / 10000;
    +
    +  // Process Y
    +  var y = 600072.37 +
    +      211455.93 * lng_aux -
    +      10938.51 * lng_aux * lat_aux -
    +      0.36 * lng_aux * Math.pow(lat_aux, 2) -
    +      44.54 * Math.pow(lng_aux, 3);
    +
    +  return y;
    +}
    +
    +// Convert WGS lat/long (° dec) to CH x
    +function WGStoCHx(lat, lng) {
    +
    +  // Converts degrees dec to sex
    +  lat = DECtoSEX(lat);
    +  lng = DECtoSEX(lng);
    +
    +  // Converts degrees to seconds (sex)
    +  lat = DEGtoSEC(lat);
    +  lng = DEGtoSEC(lng);
    +
    +  // Axiliary values (% Bern)
    +  var lat_aux = (lat - 169028.66) / 10000;
    +  var lng_aux = (lng - 26782.5) / 10000;
    +
    +  // Process X
    +  var x = 200147.07 +
    +      308807.95 * lat_aux +
    +      3745.25 * Math.pow(lng_aux, 2) +
    +      76.63 * Math.pow(lat_aux, 2) -
    +      194.56 * Math.pow(lng_aux, 2) * lat_aux +
    +      119.79 * Math.pow(lat_aux, 3);
    +
    +  return x;
    +
    +}
    +
    +
    +// Convert CH y/x to WGS lat
    +function CHtoWGSlat(y, x) {
    +
    +  // Converts militar to civil and  to unit = 1000km
    +  // Axiliary values (% Bern)
    +  var y_aux = (y - 600000) / 1000000;
    +  var x_aux = (x - 200000) / 1000000;
    +
    +  // Process lat
    +  var lat = 16.9023892 +
    +      3.238272 * x_aux -
    +      0.270978 * Math.pow(y_aux, 2) -
    +      0.002528 * Math.pow(x_aux, 2) -
    +      0.0447 * Math.pow(y_aux, 2) * x_aux -
    +      0.0140 * Math.pow(x_aux, 3);
    +
    +  // Unit 10000" to 1 " and converts seconds to degrees (dec)
    +  lat = lat * 100 / 36;
    +
    +  return lat;
    +
    +}
    +
    +// Convert CH y/x to WGS long
    +function CHtoWGSlng(y, x) {
    +
    +  // Converts militar to civil and  to unit = 1000km
    +  // Axiliary values (% Bern)
    +  var y_aux = (y - 600000) / 1000000;
    +  var x_aux = (x - 200000) / 1000000;
    +
    +  // Process long
    +  var lng = 2.6779094 +
    +      4.728982 * y_aux +
    +      0.791484 * y_aux * x_aux +
    +      0.1306 * y_aux * Math.pow(x_aux, 2) -
    +      0.0436 * Math.pow(y_aux, 3);
    +
    +  // Unit 10000" to 1 " and converts seconds to degrees (dec)
    +  lng = lng * 100 / 36;
    +
    +  return lng;
    +
    +}
    +
    +
    +// Convert SEX DMS angle to DEC
    +function SEXtoDEC(angle) {
    +
    +  // Extract DMS
    +  var deg = parseInt(angle, 10);
    +  var min = parseInt((angle - deg) * 100, 10);
    +  var sec = (((angle - deg) * 100) - min) * 100;
    +
    +  // Result in degrees sex (dd.mmss)
    +  return deg + (sec / 60 + min) / 60;
    +
    +}
    +
    +// Convert DEC angle to SEX DMS
    +function DECtoSEX(angle) {
    +
    +  // Extract DMS
    +  var deg = parseInt(angle, 10);
    +  var min = parseInt((angle - deg) * 60, 10);
    +  var sec = (((angle - deg) * 60) - min) * 60;
    +
    +  // Result in degrees sex (dd.mmss)
    +  return deg + min / 100 + sec / 10000;
    +
    +}
    +
    +// Convert Degrees angle to seconds
    +function DEGtoSEC(angle) {
    +
    +  // Extract DMS
    +  var deg = parseInt(angle, 10);
    +  var min = parseInt((angle - deg) * 100, 10);
    +  var sec = (((angle - deg) * 100) - min) * 100;
    +
    +  // Avoid rounding problems with seconds=0
    +  var parts = String(angle).split('.');
    +  if (parts.length == 2 && parts[1].length == 2) {
    +    min = Number(parts[1]);
    +    sec = 0;
    +  }
    +
    +  // Result in degrees sex (dd.mmss)
    +  return sec + min * 60 + deg * 3600;
    +
    +}
    diff --git a/mapbox-gl/examples/wms-image-custom-proj-require.js b/mapbox-gl/examples/wms-image-custom-proj-require.js
    new file mode 100644
    index 0000000000..12a0648339
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image-custom-proj-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.control.ScaleLine');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj');
    +goog.require('ol.source.ImageWMS');
    diff --git a/mapbox-gl/examples/wms-image-custom-proj.html b/mapbox-gl/examples/wms-image-custom-proj.html
    new file mode 100644
    index 0000000000..59af93e1a1
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image-custom-proj.html
    @@ -0,0 +1,54 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Single image WMS with Proj4js projection example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Single image WMS with Proj4js projection example</h4>
    +          <p id="shortdesc">Example of two single image WMS layers.</p>
    +          <div id="docs">
    +            <p>Pixelmap 1:1'000'000 with National Parks overlay using the projection EPSG:21781.</p>
    +            <p>See the <a href="wms-image-custom-proj.js" target="_blank">wms-image-custom-proj.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, single image, proj4js, projection</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.2.1/proj4.js" type="text/javascript"></script>
    +    <script src="http://epsg.io/21781-1753.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-image-custom-proj" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-image-custom-proj.js b/mapbox-gl/examples/wms-image-custom-proj.js
    new file mode 100644
    index 0000000000..b9df5700d0
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image-custom-proj.js
    @@ -0,0 +1,64 @@
    +// Transparent Proj4js support: ol.proj.get() creates and returns a projection
    +// known to Proj4js if it is unknown to OpenLayers, and registers functions to
    +// transform between all registered projections.
    +// EPSG:21781 is known to Proj4js because its definition was loaded in the html.
    +// Note that we are getting the projection object here to set the extent. If
    +// you do not need this, you do not have to use ol.proj.get(); simply use the
    +// string code in the view projection below and the transforms will be
    +// registered transparently.
    +var projection = ol.proj.get('EPSG:21781');
    +// The extent is used to determine zoom level 0. Recommended values for a
    +// projection's validity extent can be found at http://epsg.io/.
    +projection.setExtent([485869.5728, 76443.1884, 837076.5648, 299941.7864]);
    +
    +var extent = [420000, 30000, 900000, 350000];
    +var layers = [
    +  new ol.layer.Image({
    +    extent: extent,
    +    source: new ol.source.ImageWMS({
    +      url: 'http://wms.geo.admin.ch/',
    +      crossOrigin: 'anonymous',
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'Pixelmap 1:1000000 / geo.admin.ch</a>'
    +      })],
    +      params: {
    +        'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale',
    +        'FORMAT': 'image/jpeg'
    +      },
    +      serverType: /** @type {ol.source.wms.ServerType} */ ('mapserver')
    +    })
    +  }),
    +  new ol.layer.Image({
    +    extent: extent,
    +    source: new ol.source.ImageWMS({
    +      url: 'http://wms.geo.admin.ch/',
    +      crossOrigin: 'anonymous',
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'National parks / geo.admin.ch</a>'
    +      })],
    +      params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'},
    +      serverType: /** @type {ol.source.wms.ServerType} */ ('mapserver')
    +    })
    +  })
    +];
    +
    +var map = new ol.Map({
    +  controls: ol.control.defaults().extend([
    +    new ol.control.ScaleLine()
    +  ]),
    +  layers: layers,
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    projection: projection,
    +    center: ol.proj.transform([8.23, 46.86], 'EPSG:4326', 'EPSG:21781'),
    +    extent: extent,
    +    zoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/wms-image-require.js b/mapbox-gl/examples/wms-image-require.js
    new file mode 100644
    index 0000000000..40998cef34
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.ImageWMS');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/wms-image.html b/mapbox-gl/examples/wms-image.html
    new file mode 100644
    index 0000000000..d8a4a0adf3
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Single image WMS example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Single image WMS example</h4>
    +          <p id="shortdesc">Example of a single image WMS layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="wms-image.js" target="_blank">wms-image.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, image</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-image" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-image.js b/mapbox-gl/examples/wms-image.js
    new file mode 100644
    index 0000000000..2013548d7f
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-image.js
    @@ -0,0 +1,21 @@
    +var layers = [
    +  new ol.layer.Tile({
    +    source: new ol.source.MapQuest({layer: 'sat'})
    +  }),
    +  new ol.layer.Image({
    +    extent: [-13884991, 2870341, -7455066, 6338219],
    +    source: new ol.source.ImageWMS({
    +      url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +      params: {'LAYERS': 'topp:states'},
    +      serverType: 'geoserver'
    +    })
    +  })
    +];
    +var map = new ol.Map({
    +  layers: layers,
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-10997148, 4569099],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/wms-no-proj-require.js b/mapbox-gl/examples/wms-no-proj-require.js
    new file mode 100644
    index 0000000000..0cf8aa8255
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-no-proj-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.source.ImageWMS');
    +goog.require('ol.source.TileWMS');
    diff --git a/mapbox-gl/examples/wms-no-proj.html b/mapbox-gl/examples/wms-no-proj.html
    new file mode 100644
    index 0000000000..acd45d3e9b
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-no-proj.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMS without client projection example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">WMS without client projection example</h4>
    +          <p id="shortdesc">Example of two WMS layers using the projection EPSG:21781, which is unknown to the client.</p>
    +          <div id="docs">
    +            <p>See the <a href="wms-no-proj.js" target="_blank">wms-no-proj.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, projection</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-no-proj" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-no-proj.js b/mapbox-gl/examples/wms-no-proj.js
    new file mode 100644
    index 0000000000..33445e767f
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-no-proj.js
    @@ -0,0 +1,53 @@
    +var layers = [
    +  new ol.layer.Tile({
    +    source: new ol.source.TileWMS({
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'Pixelmap 1:1000000 / geo.admin.ch</a>'
    +      })],
    +      crossOrigin: 'anonymous',
    +      params: {
    +        'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale',
    +        'FORMAT': 'image/jpeg'
    +      },
    +      url: 'http://wms.geo.admin.ch/'
    +    })
    +  }),
    +  new ol.layer.Image({
    +    source: new ol.source.ImageWMS({
    +      attributions: [new ol.Attribution({
    +        html: '&copy; ' +
    +            '<a href="http://www.geo.admin.ch/internet/geoportal/' +
    +            'en/home.html">' +
    +            'National parks / geo.admin.ch</a>'
    +      })],
    +      crossOrigin: 'anonymous',
    +      params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'},
    +      serverType: 'mapserver',
    +      url: 'http://wms.geo.admin.ch/'
    +    })
    +  })
    +];
    +
    +// A minimal projection object is configured with only the SRS code and the map
    +// units. No client-side coordinate transforms are possible with such a
    +// projection object. Requesting tiles only needs the code together with a
    +// tile grid of Cartesian coordinates; it does not matter how those
    +// coordinates relate to latitude or longitude.
    +var projection = new ol.proj.Projection({
    +  code: 'EPSG:21781',
    +  units: 'm'
    +});
    +
    +var map = new ol.Map({
    +  layers: layers,
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    center: [660000, 190000],
    +    projection: projection,
    +    zoom: 9
    +  })
    +});
    diff --git a/mapbox-gl/examples/wms-tiled-require.js b/mapbox-gl/examples/wms-tiled-require.js
    new file mode 100644
    index 0000000000..340945c4f8
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-tiled-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    +goog.require('ol.source.TileWMS');
    diff --git a/mapbox-gl/examples/wms-tiled.html b/mapbox-gl/examples/wms-tiled.html
    new file mode 100644
    index 0000000000..d16ccb70e5
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-tiled.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Tiled WMS example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Tiled WMS example</h4>
    +          <p id="shortdesc">Example of a tiled WMS layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="wms-tiled.js" target="_blank">wms-tiled.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wms, tile, tilelayer</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wms-tiled" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wms-tiled.js b/mapbox-gl/examples/wms-tiled.js
    new file mode 100644
    index 0000000000..b9201a2879
    --- /dev/null
    +++ b/mapbox-gl/examples/wms-tiled.js
    @@ -0,0 +1,21 @@
    +var layers = [
    +  new ol.layer.Tile({
    +    source: new ol.source.MapQuest({layer: 'sat'})
    +  }),
    +  new ol.layer.Tile({
    +    extent: [-13884991, 2870341, -7455066, 6338219],
    +    source: new ol.source.TileWMS(/** @type {olx.source.TileWMSOptions} */ ({
    +      url: 'http://demo.boundlessgeo.com/geoserver/wms',
    +      params: {'LAYERS': 'topp:states', 'TILED': true},
    +      serverType: 'geoserver'
    +    }))
    +  })
    +];
    +var map = new ol.Map({
    +  layers: layers,
    +  target: 'map',
    +  view: new ol.View({
    +    center: [-10997148, 4569099],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/wmts-capabilities-require.js b/mapbox-gl/examples/wmts-capabilities-require.js
    new file mode 100644
    index 0000000000..7b92ad84b6
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-capabilities-require.js
    @@ -0,0 +1 @@
    +goog.require('ol.format.WMTSCapabilities');
    diff --git a/mapbox-gl/examples/wmts-capabilities.html b/mapbox-gl/examples/wmts-capabilities.html
    new file mode 100644
    index 0000000000..1a37d7784c
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-capabilities.html
    @@ -0,0 +1,49 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMTS GetCapabilities parsing example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +
    +        <div class="span4">
    +          <h4 id="title">WMTS GetCapabilities parsing example</h4>
    +          <p id="shortdesc">Example of parsing a WMTS GetCapabilities response.</p>
    +          <div id="docs">
    +            <p>See the <a href="wmts-capabilities.js" target="_blank">wmts-capabilities.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wmts, capabilities, getcapabilities</div>
    +        </div>
    +
    +        <div class="span8">
    +          <pre id="log"></pre>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wmts-capabilities" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wmts-capabilities.js b/mapbox-gl/examples/wmts-capabilities.js
    new file mode 100644
    index 0000000000..04b0579249
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-capabilities.js
    @@ -0,0 +1,6 @@
    +var parser = new ol.format.WMTSCapabilities();
    +
    +$.ajax('data/WMTSCapabilities.xml').then(function(response) {
    +  var result = parser.read(response);
    +  $('#log').html(window.JSON.stringify(result, null, 2));
    +});
    diff --git a/mapbox-gl/examples/wmts-hidpi-require.js b/mapbox-gl/examples/wmts-hidpi-require.js
    new file mode 100644
    index 0000000000..e2634e59fe
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-hidpi-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.has');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.WMTS');
    +goog.require('ol.tilegrid.WMTS');
    diff --git a/mapbox-gl/examples/wmts-hidpi.html b/mapbox-gl/examples/wmts-hidpi.html
    new file mode 100644
    index 0000000000..8deed62a4b
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-hidpi.html
    @@ -0,0 +1,56 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMTS HiDPI example</title>
    +    <style>
    +      .map {
    +        background: white;
    +      }
    +    </style>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">WMTS HiDPI example</h4>
    +          <p id="shortdesc">Example of a WMTS based HiDPI layer.</p>
    +          <div id="docs">
    +            <p>See the <a href="wmts-hidpi.js" target="_blank">wmts-hidpi.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">hidpi, retina, wmts</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wmts-hidpi" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wmts-hidpi.js b/mapbox-gl/examples/wmts-hidpi.js
    new file mode 100644
    index 0000000000..122a1eff31
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-hidpi.js
    @@ -0,0 +1,67 @@
    +var template =
    +    '{Layer}/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.jpeg';
    +var urls = [
    +  'http://maps1.wien.gv.at/basemap/' + template,
    +  'http://maps2.wien.gv.at/basemap/' + template,
    +  'http://maps3.wien.gv.at/basemap/' + template,
    +  'http://maps4.wien.gv.at/basemap/' + template,
    +  'http://maps.wien.gv.at/basemap/' + template
    +];
    +
    +
    +// HiDPI support:
    +// * Use 'bmaphidpi' layer (pixel ratio 2) for device pixel ratio > 1
    +// * Use 'geolandbasemap' layer (pixel ratio 1) for device pixel ratio == 1
    +var hiDPI = ol.has.DEVICE_PIXEL_RATIO > 1;
    +
    +var source = new ol.source.WMTS({
    +  projection: 'EPSG:3857',
    +  layer: hiDPI ? 'bmaphidpi' : 'geolandbasemap',
    +  tilePixelRatio: hiDPI ? 2 : 1,
    +  style: 'normal',
    +  matrixSet: 'google3857',
    +  urls: urls,
    +  requestEncoding: 'REST',
    +  tileGrid: new ol.tilegrid.WMTS({
    +    origin: [-20037508.3428, 20037508.3428],
    +    resolutions: [
    +      559082264.029 * 0.28E-3,
    +      279541132.015 * 0.28E-3,
    +      139770566.007 * 0.28E-3,
    +      69885283.0036 * 0.28E-3,
    +      34942641.5018 * 0.28E-3,
    +      17471320.7509 * 0.28E-3,
    +      8735660.37545 * 0.28E-3,
    +      4367830.18773 * 0.28E-3,
    +      2183915.09386 * 0.28E-3,
    +      1091957.54693 * 0.28E-3,
    +      545978.773466 * 0.28E-3,
    +      272989.386733 * 0.28E-3,
    +      136494.693366 * 0.28E-3,
    +      68247.3466832 * 0.28E-3,
    +      34123.6733416 * 0.28E-3,
    +      17061.8366708 * 0.28E-3,
    +      8530.91833540 * 0.28E-3,
    +      4265.45916770 * 0.28E-3,
    +      2132.72958385 * 0.28E-3,
    +      1066.36479193 * 0.28E-3
    +    ],
    +    matrixIds: [
    +      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
    +    ]
    +  })
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      extent: [977844.377599999, 5837774.6617, 1915609.8654, 6295560.8122],
    +      source: source
    +    })
    +  ],
    +  target: 'map',
    +  view: new ol.View({
    +    center: [1823849, 6143760],
    +    zoom: 11
    +  })
    +});
    diff --git a/mapbox-gl/examples/wmts-layer-from-capabilities-require.js b/mapbox-gl/examples/wmts-layer-from-capabilities-require.js
    new file mode 100644
    index 0000000000..4025609c49
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-layer-from-capabilities-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.format.WMTSCapabilities');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.WMTS');
    diff --git a/mapbox-gl/examples/wmts-layer-from-capabilities.html b/mapbox-gl/examples/wmts-layer-from-capabilities.html
    new file mode 100644
    index 0000000000..4918488e4c
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-layer-from-capabilities.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMTS Layer example from capabilities </title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">WMTS Capabilities example</h4>
    +          <p id="shortdesc">Example of a WMTS source created from a WMTS capabilities document.</p>
    +          <div id="docs">
    +            <p>See the <a href="wmts-layer-from-capabilities.js" target="_blank">wmts-layer-from-capabilities.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wmts, capabilities, getcapabilities</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wmts-layer-from-capabilities" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wmts-layer-from-capabilities.js b/mapbox-gl/examples/wmts-layer-from-capabilities.js
    new file mode 100644
    index 0000000000..d2b8794ca1
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-layer-from-capabilities.js
    @@ -0,0 +1,29 @@
    +var parser = new ol.format.WMTSCapabilities();
    +var map;
    +
    +$.ajax('data/WMTSCapabilities.xml').then(function(response) {
    +  var result = parser.read(response);
    +  var options = ol.source.WMTS.optionsFromCapabilities(result,
    +      {layer: 'layer-7328', matrixSet: 'EPSG:3857'});
    +
    +  var projection = ol.proj.get('EPSG:3857');
    +  var projectionExtent = projection.getExtent();
    +  map = new ol.Map({
    +    layers: [
    +      new ol.layer.Tile({
    +        source: new ol.source.OSM(),
    +        opacity: 0.7
    +      }),
    +      new ol.layer.Tile({
    +        opacity: 1,
    +        extent: projectionExtent,
    +        source: new ol.source.WMTS(options)
    +      })
    +    ],
    +    target: 'map',
    +    view: new ol.View({
    +      center: [19412406.33, -5050500.21],
    +      zoom: 5
    +    })
    +  });
    +});
    diff --git a/mapbox-gl/examples/wmts-require.js b/mapbox-gl/examples/wmts-require.js
    new file mode 100644
    index 0000000000..069a4a946d
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts-require.js
    @@ -0,0 +1,10 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.WMTS');
    +goog.require('ol.tilegrid.WMTS');
    diff --git a/mapbox-gl/examples/wmts.html b/mapbox-gl/examples/wmts.html
    new file mode 100644
    index 0000000000..4b48f24f16
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>WMTS example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">WMTS example</h4>
    +          <p id="shortdesc">Example of a WMTS source.</p>
    +          <div id="docs">
    +            <p>See the <a href="wmts.js" target="_blank">wmts.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">wmts</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=wmts" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/wmts.js b/mapbox-gl/examples/wmts.js
    new file mode 100644
    index 0000000000..c71db2c53d
    --- /dev/null
    +++ b/mapbox-gl/examples/wmts.js
    @@ -0,0 +1,54 @@
    +var projection = ol.proj.get('EPSG:3857');
    +var projectionExtent = projection.getExtent();
    +var size = ol.extent.getWidth(projectionExtent) / 256;
    +var resolutions = new Array(14);
    +var matrixIds = new Array(14);
    +for (var z = 0; z < 14; ++z) {
    +  // generate resolutions and matrixIds arrays for this WMTS
    +  resolutions[z] = size / Math.pow(2, z);
    +  matrixIds[z] = z;
    +}
    +
    +var attribution = new ol.Attribution({
    +  html: 'Tiles &copy; <a href="http://services.arcgisonline.com/arcgis/rest/' +
    +      'services/Demographics/USA_Population_Density/MapServer/">ArcGIS</a>'
    +});
    +
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM(),
    +      opacity: 0.7
    +    }),
    +    new ol.layer.Tile({
    +      opacity: 0.7,
    +      extent: projectionExtent,
    +      source: new ol.source.WMTS({
    +        attributions: [attribution],
    +        url: 'http://services.arcgisonline.com/arcgis/rest/' +
    +            'services/Demographics/USA_Population_Density/MapServer/WMTS/',
    +        layer: '0',
    +        matrixSet: 'EPSG:3857',
    +        format: 'image/png',
    +        projection: projection,
    +        tileGrid: new ol.tilegrid.WMTS({
    +          origin: ol.extent.getTopLeft(projectionExtent),
    +          resolutions: resolutions,
    +          matrixIds: matrixIds
    +        }),
    +        style: 'default'
    +      })
    +    })
    +  ],
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  view: new ol.View({
    +    center: [-11158582, 4813697],
    +    zoom: 4
    +  })
    +});
    diff --git a/mapbox-gl/examples/xyz-esri-4326-512-require.js b/mapbox-gl/examples/xyz-esri-4326-512-require.js
    new file mode 100644
    index 0000000000..f1163690a8
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri-4326-512-require.js
    @@ -0,0 +1,8 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilegrid.TileGrid');
    diff --git a/mapbox-gl/examples/xyz-esri-4326-512.html b/mapbox-gl/examples/xyz-esri-4326-512.html
    new file mode 100644
    index 0000000000..584d04608e
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri-4326-512.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>XYZ Esri EPSG:4326 tileSize 512 example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">XYZ Esri EPSG:4326 tileSize 512 example</h4>
    +          <p id="shortdesc">Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.</p>
    +          <div id="docs">
    +            <p>See the <a href="xyz-esri-4326-512.js" target="_blank">xyz-esri-4326-512.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">xyz, esri, tilesize, custom projection</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=xyz-esri-4326-512" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/xyz-esri-4326-512.js b/mapbox-gl/examples/xyz-esri-4326-512.js
    new file mode 100644
    index 0000000000..7303217f2f
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri-4326-512.js
    @@ -0,0 +1,63 @@
    +var attribution = new ol.Attribution({
    +  html: 'Copyright:&copy; 2013 ESRI, i-cubed, GeoEye'
    +});
    +
    +var projection = ol.proj.get('EPSG:4326');
    +var projectionExtent = projection.getExtent();
    +
    +// The tile size supported by the ArcGIS tile service.
    +var tileSize = 512;
    +
    +// Calculate the resolutions supported by the ArcGIS tile service.
    +// There are 16 resolutions, with a factor of 2 between successive
    +// resolutions. The max resolution is such that the world (360°)
    +// fits into two (512x512 px) tiles.
    +var maxResolution = ol.extent.getWidth(projectionExtent) / (tileSize * 2);
    +var resolutions = new Array(16);
    +var z;
    +for (z = 0; z < 16; ++z) {
    +  resolutions[z] = maxResolution / Math.pow(2, z);
    +}
    +
    +var urlTemplate = 'http://services.arcgisonline.com/arcgis/rest/services/' +
    +    'ESRI_Imagery_World_2D/MapServer/tile/{z}/{y}/{x}';
    +
    +var map = new ol.Map({
    +  target: 'map',
    +  layers: [
    +    new ol.layer.Tile({
    +      /* ol.source.XYZ and ol.tilegrid.XYZ have no resolutions config */
    +      source: new ol.source.TileImage({
    +        attributions: [attribution],
    +        tileUrlFunction: function(tileCoord, pixelRatio, projection) {
    +          var z = tileCoord[0];
    +          var x = tileCoord[1];
    +          var y = -tileCoord[2] - 1;
    +          // wrap the world on the X axis
    +          var n = Math.pow(2, z + 1); // 2 tiles at z=0
    +          x = x % n;
    +          if (x * n < 0) {
    +            // x and n differ in sign so add n to wrap the result
    +            // to the correct sign
    +            x = x + n;
    +          }
    +          return urlTemplate.replace('{z}', z.toString())
    +              .replace('{y}', y.toString())
    +              .replace('{x}', x.toString());
    +        },
    +        projection: projection,
    +        tileGrid: new ol.tilegrid.TileGrid({
    +          origin: ol.extent.getTopLeft(projectionExtent),
    +          resolutions: resolutions,
    +          tileSize: 512
    +        })
    +      })
    +    })
    +  ],
    +  view: new ol.View({
    +    center: [0, 0],
    +    projection: projection,
    +    zoom: 2,
    +    minZoom: 2
    +  })
    +});
    diff --git a/mapbox-gl/examples/xyz-esri-require.js b/mapbox-gl/examples/xyz-esri-require.js
    new file mode 100644
    index 0000000000..300af9955c
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.XYZ');
    diff --git a/mapbox-gl/examples/xyz-esri.html b/mapbox-gl/examples/xyz-esri.html
    new file mode 100644
    index 0000000000..31f21a082a
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>XYZ Esri example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">XYZ Esri example</h4>
    +          <p id="shortdesc">Example of a XYZ source using Esri tiles.</p>
    +          <div id="docs">
    +            <p>See the <a href="xyz-esri.js" target="_blank">xyz-esri.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">xyz, esri</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=xyz-esri" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/xyz-esri.js b/mapbox-gl/examples/xyz-esri.js
    new file mode 100644
    index 0000000000..91e0f3d170
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-esri.js
    @@ -0,0 +1,21 @@
    +var attribution = new ol.Attribution({
    +  html: 'Tiles &copy; <a href="http://services.arcgisonline.com/ArcGIS/' +
    +      'rest/services/World_Topo_Map/MapServer">ArcGIS</a>'
    +});
    +
    +var map = new ol.Map({
    +  target: 'map',
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.XYZ({
    +        attributions: [attribution],
    +        url: 'http://server.arcgisonline.com/ArcGIS/rest/services/' +
    +            'World_Topo_Map/MapServer/tile/{z}/{y}/{x}'
    +      })
    +    })
    +  ],
    +  view: new ol.View({
    +    center: ol.proj.transform([-121.1, 47.5], 'EPSG:4326', 'EPSG:3857'),
    +    zoom: 7
    +  })
    +});
    diff --git a/mapbox-gl/examples/xyz-require.js b/mapbox-gl/examples/xyz-require.js
    new file mode 100644
    index 0000000000..7943461009
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.XYZ');
    diff --git a/mapbox-gl/examples/xyz-retina-require.js b/mapbox-gl/examples/xyz-retina-require.js
    new file mode 100644
    index 0000000000..71e67cb26b
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-retina-require.js
    @@ -0,0 +1,7 @@
    +goog.require('ol.Attribution');
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.XYZ');
    diff --git a/mapbox-gl/examples/xyz-retina.html b/mapbox-gl/examples/xyz-retina.html
    new file mode 100644
    index 0000000000..acc25a45b8
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-retina.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>XYZ Retina tiles example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">XYZ with Retina tiles example</h4>
    +          <p id="shortdesc">Example of Retina / HiDPI mercator tiles (512x512px) available as XYZ.</p>
    +          <div id="docs">
    +              <p>The ol.source.XYZ must contain <code>tilePixelRatio</code> parameter. The tiles were prepared from a GeoTIFF file with <a href="http://www.maptiler.com/">MapTiler</a>.</p>
    +            <p>See the <a href="xyz-retina.js" target="_blank">xyz-retina.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">retina, hidpi, xyz, maptiler, @2x, devicePixelRatio</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=xyz-retina" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/xyz-retina.js b/mapbox-gl/examples/xyz-retina.js
    new file mode 100644
    index 0000000000..20ac3c6f59
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz-retina.js
    @@ -0,0 +1,33 @@
    +var attribution = new ol.Attribution({
    +  html: 'Tiles &copy; USGS, rendered with ' +
    +      '<a href="http://www.maptiler.com/">MapTiler</a>'
    +});
    +
    +var mapMinZoom = 1;
    +var mapMaxZoom = 15;
    +var mapExtent = [-112.261791, 35.983744, -112.113981, 36.132062];
    +
    +var map = new ol.Map({
    +  target: 'map',
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM()
    +    }),
    +    new ol.layer.Tile({
    +      extent: ol.proj.transformExtent(mapExtent, 'EPSG:4326', 'EPSG:3857'),
    +      source: new ol.source.XYZ({
    +        attributions: [attribution],
    +        url: 'http://tileserver.maptiler.com/grandcanyon@2x/{z}/{x}/{y}.png',
    +        tilePixelRatio: 2, // THIS IS IMPORTANT
    +        minZoom: mapMinZoom,
    +        maxZoom: mapMaxZoom
    +      })
    +    })
    +  ],
    +  view: new ol.View({
    +    projection: 'EPSG:3857',
    +    center: ol.proj.transform([-112.18688965, 36.057944835],
    +        'EPSG:4326', 'EPSG:3857'),
    +    zoom: 12
    +  })
    +});
    diff --git a/mapbox-gl/examples/xyz.html b/mapbox-gl/examples/xyz.html
    new file mode 100644
    index 0000000000..ed08aeec4c
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>XYZ example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">XYZ example</h4>
    +          <p id="shortdesc">Example of a XYZ source.</p>
    +          <div id="docs">
    +            <p>See the <a href="xyz.js" target="_blank">xyz.js source</a> for details on how this is done.</p>
    +          </div>
    +          <div id="tags">xyz</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=xyz" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/xyz.js b/mapbox-gl/examples/xyz.js
    new file mode 100644
    index 0000000000..d6642c17f5
    --- /dev/null
    +++ b/mapbox-gl/examples/xyz.js
    @@ -0,0 +1,37 @@
    +var attribution = new ol.Attribution({
    +  html: 'Tiles &copy; <a href="http://maps.nls.uk/townplans/glasgow_1.html">' +
    +      'National Library of Scotland</a>'
    +});
    +
    +var map = new ol.Map({
    +  target: 'map',
    +  controls: ol.control.defaults({
    +    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
    +      collapsible: false
    +    })
    +  }),
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.OSM({
    +        attributions: [
    +          new ol.Attribution({
    +            html: 'Tiles &copy; <a href="http://www.opencyclemap.org/">' +
    +                'OpenCycleMap</a>'
    +          }),
    +          ol.source.OSM.ATTRIBUTION
    +        ],
    +        url: 'http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png'
    +      })
    +    }),
    +    new ol.layer.Tile({
    +      source: new ol.source.XYZ({
    +        attributions: [attribution],
    +        url: 'http://geo.nls.uk/maps/towns/glasgow1857/{z}/{x}/{-y}.png'
    +      })
    +    })
    +  ],
    +  view: new ol.View({
    +    center: [-472202, 7530279],
    +    zoom: 12
    +  })
    +});
    diff --git a/mapbox-gl/examples/zoom-constrained-require.js b/mapbox-gl/examples/zoom-constrained-require.js
    new file mode 100644
    index 0000000000..6c7285a11d
    --- /dev/null
    +++ b/mapbox-gl/examples/zoom-constrained-require.js
    @@ -0,0 +1,4 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.BingMaps');
    diff --git a/mapbox-gl/examples/zoom-constrained.html b/mapbox-gl/examples/zoom-constrained.html
    new file mode 100644
    index 0000000000..e1ccbab1e3
    --- /dev/null
    +++ b/mapbox-gl/examples/zoom-constrained.html
    @@ -0,0 +1,52 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Zoom Constrained Example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Zoom constrained example</h4>
    +          <p id="shortdesc">Example of a zoom constrained view.</p>
    +          <div id="docs">
    +            <p>This map has a view that is constrained between zoom levels 9 and 13.  This is done using the <code>minZoom</code> and <code>maxZoom</code> view options.</p>
    +            <p>See the <a href="zoom-constrained.js" target="_blank">zoom-constrained.js source</a> for details.</p>
    +          </div>
    +          <div id="tags">bing, zoom, minZoom, maxZoom</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=zoom-constrained" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/zoom-constrained.js b/mapbox-gl/examples/zoom-constrained.js
    new file mode 100644
    index 0000000000..9b956ef7ed
    --- /dev/null
    +++ b/mapbox-gl/examples/zoom-constrained.js
    @@ -0,0 +1,17 @@
    +var map = new ol.Map({
    +  target: 'map',
    +  layers: [
    +    new ol.layer.Tile({
    +      source: new ol.source.BingMaps({
    +        key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
    +        imagerySet: 'Aerial'
    +      })
    +    })
    +  ],
    +  view: new ol.View({
    +    center: [-13553864, 5918250],
    +    zoom: 11,
    +    minZoom: 9,
    +    maxZoom: 13
    +  })
    +});
    diff --git a/mapbox-gl/examples/zoomify-require.js b/mapbox-gl/examples/zoomify-require.js
    new file mode 100644
    index 0000000000..34518ebde7
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomify-require.js
    @@ -0,0 +1,6 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.proj');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.source.Zoomify');
    diff --git a/mapbox-gl/examples/zoomify.html b/mapbox-gl/examples/zoomify.html
    new file mode 100644
    index 0000000000..f6be63b091
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomify.html
    @@ -0,0 +1,51 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <title>Zoomify example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span12">
    +          <div id="map" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">Zoomify example</h4>
    +          <p id="shortdesc">Example of a Zoomify source.</p>
    +          <div id="docs">
    +            <p>See the <a href="zoomify.js" target="_blank">zoomify.js source</a> to see how this is done.</p>
    +          </div>
    +          <div id="tags">zoomify</div>
    +        </div>
    +
    +      </div>
    +
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=zoomify" type="text/javascript"></script>
    +
    +  </body>
    +</html>
    diff --git a/mapbox-gl/examples/zoomify.js b/mapbox-gl/examples/zoomify.js
    new file mode 100644
    index 0000000000..1c8679b0a8
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomify.js
    @@ -0,0 +1,46 @@
    +// This server does not support CORS, and so is incompatible with WebGL.
    +//var imgWidth = 8001;
    +//var imgHeight = 6943;
    +//var url = 'http://mapy.mzk.cz/AA22/0103/';
    +//var crossOrigin = undefined;
    +
    +var imgWidth = 9911;
    +var imgHeight = 6100;
    +var url = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?zoomify=' +
    +        '/mnt/MD1/AD00/plan_CHU-4HD-01/FOND.TIF/';
    +var crossOrigin = 'anonymous';
    +
    +var imgCenter = [imgWidth / 2, - imgHeight / 2];
    +
    +// Maps always need a projection, but Zoomify layers are not geo-referenced, and
    +// are only measured in pixels.  So, we create a fake projection that the map
    +// can use to properly display the layer.
    +var proj = new ol.proj.Projection({
    +  code: 'ZOOMIFY',
    +  units: 'pixels',
    +  extent: [0, 0, imgWidth, imgHeight]
    +});
    +
    +var source = new ol.source.Zoomify({
    +  url: url,
    +  size: [imgWidth, imgHeight],
    +  crossOrigin: crossOrigin
    +});
    +
    +var map = new ol.Map({
    +  layers: [
    +    new ol.layer.Tile({
    +      source: source
    +    })
    +  ],
    +  renderer: exampleNS.getRendererFromQueryString(),
    +  target: 'map',
    +  view: new ol.View({
    +    projection: proj,
    +    center: imgCenter,
    +    zoom: 0,
    +    // constrain the center: center cannot be set outside
    +    // this extent
    +    extent: [0, -imgHeight, imgWidth, 0]
    +  })
    +});
    diff --git a/mapbox-gl/examples/zoomslider-require.js b/mapbox-gl/examples/zoomslider-require.js
    new file mode 100644
    index 0000000000..272af78d9f
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomslider-require.js
    @@ -0,0 +1,5 @@
    +goog.require('ol.Map');
    +goog.require('ol.View');
    +goog.require('ol.control.ZoomSlider');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.source.MapQuest');
    diff --git a/mapbox-gl/examples/zoomslider.html b/mapbox-gl/examples/zoomslider.html
    new file mode 100644
    index 0000000000..f5da737e26
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomslider.html
    @@ -0,0 +1,126 @@
    +<!doctype html>
    +<html lang="en">
    +  <head>
    +    <meta charset="utf-8">
    +    <meta http-equiv="X-UA-Compatible" content="chrome=1">
    +    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    +    <link rel="stylesheet" href="../css/ol.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap.min.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/layout.css" type="text/css">
    +    <link rel="stylesheet" href="../resources/bootstrap/css/bootstrap-responsive.min.css" type="text/css">
    +    <style type="text/css">
    +/**
    + * The zoomslider in the second map shall be placed between the zoom-in and
    + * zoom-out buttons.
    + */
    +#map2 .ol-zoom .ol-zoom-out {
    +  margin-top: 204px;
    +}
    +#map2 .ol-zoomslider {
    +  background-color: transparent;
    +  top: 2.3em;
    +}
    +
    +#map2 .ol-touch .ol-zoom .ol-zoom-out { 
    +  margin-top: 212px;
    +}
    +#map2 .ol-touch .ol-zoomslider {
    +  top: 2.75em;
    +}
    +
    +#map2 .ol-zoom-in.ol-has-tooltip:hover [role=tooltip],
    +#map2 .ol-zoom-in.ol-has-tooltip:focus [role=tooltip] {
    +  top: 3px;
    +}
    +
    +#map2 .ol-zoom-out.ol-has-tooltip:hover [role=tooltip],
    +#map2 .ol-zoom-out.ol-has-tooltip:focus [role=tooltip] {
    +  top: 232px;
    +}
    +
    +/**
    + * The zoomslider in the third map shall be horizontal, placed in the top-right
    + * corner, smaller and orange.
    + */
    +#map3 .ol-zoomslider {
    +  top: 8px;
    +  left: auto;
    +  right: 8px;
    +  background-color: rgba(255,69,0,0.2);
    +  width: 200px;
    +  height: 15px;
    +  padding: 0;
    +  box-shadow: 0 0 5px rgb(255,69,0);
    +  border-radius: 20px;
    +}
    +
    +#map3 .ol-zoomslider:hover {
    +  background-color: rgba(255,69,0,0.3);
    +}
    +
    +#map3 .ol-zoomslider-thumb {
    +  height: 15px;
    +  width: 15px;
    +  margin: 0;
    +  filter: none;
    +  background-color: rgba(255,69,0,0.6);
    +  border-radius: 20px;
    +}
    +#map3 a.ol-zoomslider-handle:hover {
    +  background-color: rgba(255,69,0,0.7);
    +}
    +    </style>
    +    <title>Zoom slider example</title>
    +  </head>
    +  <body>
    +
    +    <div class="navbar navbar-inverse navbar-fixed-top">
    +      <div class="navbar-inner">
    +        <div class="container">
    +          <a class="brand" href="./"><img src="../resources/logo.png"> OpenLayers 3 Examples</a>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <div class="container-fluid">
    +
    +      <div class="row-fluid">
    +        <div class="span4">
    +          <h4>Default style</h4>
    +          <div id="map1" class="map"></div>
    +        </div>
    +        <div class="span4">
    +          <h4>Placed between zoom controls</h4>
    +          <div id="map2" class="map"></div>
    +        </div>
    +        <div class="span4">
    +          <h4>Horizontal and completely re-styled</h4>
    +          <div id="map3" class="map"></div>
    +        </div>
    +      </div>
    +
    +      <div class="row-fluid">
    +
    +        <div class="span12">
    +          <h4 id="title">ZoomSlider control</h4>
    +          <p id="shortdesc">Example of various ZoomSlider controls.</p>
    +          <div id="docs">
    +            <p>
    +              See the <a href="zoomslider.js" target="_blank">zoomslider.js
    +              source</a> to see how this is done.
    +            </p>
    +          </div>
    +          <div id="tags">
    +            zoom, zoomslider, slider, style, styling, css, control
    +          </div>
    +        </div>
    +      </div>
    +    </div>
    +
    +    <script src="../resources/jquery.min.js" type="text/javascript"></script>
    +    <script src="../resources/example-behaviour.js" type="text/javascript"></script>
    +    <script src="loader.js?id=zoomslider" type="text/javascript"></script>
    +
    +  </body>
    +
    +</html>
    diff --git a/mapbox-gl/examples/zoomslider.js b/mapbox-gl/examples/zoomslider.js
    new file mode 100644
    index 0000000000..a1159482d5
    --- /dev/null
    +++ b/mapbox-gl/examples/zoomslider.js
    @@ -0,0 +1,29 @@
    +/**
    + * Helper method for map-creation.
    + *
    + * @param {string} divId The id of the div for the map.
    + * @return {ol.Map} The ol.Map instance.
    + */
    +var createMap = function(divId) {
    +  var source, layer, map, zoomslider, resolutions;
    +
    +  source = new ol.source.MapQuest({layer: 'sat'});
    +  layer = new ol.layer.Tile({
    +    source: source
    +  });
    +  map = new ol.Map({
    +    layers: [layer],
    +    target: divId,
    +    view: new ol.View({
    +      center: [0, 0],
    +      zoom: 2
    +    })
    +  });
    +  zoomslider = new ol.control.ZoomSlider();
    +  map.addControl(zoomslider);
    +  return map;
    +};
    +
    +var map1 = createMap('map1');
    +var map2 = createMap('map2');
    +var map3 = createMap('map3');
    diff --git a/mapbox-gl/ol.ext/rbush.js b/mapbox-gl/ol.ext/rbush.js
    new file mode 100644
    index 0000000000..67b62a2e6f
    --- /dev/null
    +++ b/mapbox-gl/ol.ext/rbush.js
    @@ -0,0 +1,600 @@
    +goog.provide('ol.ext.rbush');
    +/** @typedef {function(*)} */
    +ol.ext.rbush;
    +(function() {
    +var exports = {};
    +var module = {exports: exports};
    +/**
    + * @fileoverview
    + * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility}
    + */
    +/*
    + (c) 2013, Vladimir Agafonkin
    + RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
    + https://github.com/mourner/rbush
    +*/
    +
    +(function () { 'use strict';
    +
    +function rbush(maxEntries, format) {
    +
    +    // jshint newcap: false, validthis: true
    +    if (!(this instanceof rbush)) return new rbush(maxEntries, format);
    +
    +    // max entries in a node is 9 by default; min node fill is 40% for best performance
    +    this._maxEntries = Math.max(4, maxEntries || 9);
    +    this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
    +
    +    if (format) {
    +        this._initFormat(format);
    +    }
    +
    +    this.clear();
    +}
    +
    +rbush.prototype = {
    +
    +    all: function () {
    +        return this._all(this.data, []);
    +    },
    +
    +    search: function (bbox) {
    +
    +        var node = this.data,
    +            result = [],
    +            toBBox = this.toBBox;
    +
    +        if (!intersects(bbox, node.bbox)) return result;
    +
    +        var nodesToSearch = [],
    +            i, len, child, childBBox;
    +
    +        while (node) {
    +            for (i = 0, len = node.children.length; i < len; i++) {
    +
    +                child = node.children[i];
    +                childBBox = node.leaf ? toBBox(child) : child.bbox;
    +
    +                if (intersects(bbox, childBBox)) {
    +                    if (node.leaf) result.push(child);
    +                    else if (contains(bbox, childBBox)) this._all(child, result);
    +                    else nodesToSearch.push(child);
    +                }
    +            }
    +            node = nodesToSearch.pop();
    +        }
    +
    +        return result;
    +    },
    +
    +    load: function (data) {
    +        if (!(data && data.length)) return this;
    +
    +        if (data.length < this._minEntries) {
    +            for (var i = 0, len = data.length; i < len; i++) {
    +                this.insert(data[i]);
    +            }
    +            return this;
    +        }
    +
    +        // recursively build the tree with the given data from stratch using OMT algorithm
    +        var node = this._build(data.slice(), 0, data.length - 1, 0);
    +
    +        if (!this.data.children.length) {
    +            // save as is if tree is empty
    +            this.data = node;
    +
    +        } else if (this.data.height === node.height) {
    +            // split root if trees have the same height
    +            this._splitRoot(this.data, node);
    +
    +        } else {
    +            if (this.data.height < node.height) {
    +                // swap trees if inserted one is bigger
    +                var tmpNode = this.data;
    +                this.data = node;
    +                node = tmpNode;
    +            }
    +
    +            // insert the small tree into the large tree at appropriate level
    +            this._insert(node, this.data.height - node.height - 1, true);
    +        }
    +
    +        return this;
    +    },
    +
    +    insert: function (item) {
    +        if (item) this._insert(item, this.data.height - 1);
    +        return this;
    +    },
    +
    +    clear: function () {
    +        this.data = {
    +            children: [],
    +            height: 1,
    +            bbox: empty(),
    +            leaf: true
    +        };
    +        return this;
    +    },
    +
    +    remove: function (item) {
    +        if (!item) return this;
    +
    +        var node = this.data,
    +            bbox = this.toBBox(item),
    +            path = [],
    +            indexes = [],
    +            i, parent, index, goingUp;
    +
    +        // depth-first iterative tree traversal
    +        while (node || path.length) {
    +
    +            if (!node) { // go up
    +                node = path.pop();
    +                parent = path[path.length - 1];
    +                i = indexes.pop();
    +                goingUp = true;
    +            }
    +
    +            if (node.leaf) { // check current node
    +                index = node.children.indexOf(item);
    +
    +                if (index !== -1) {
    +                    // item found, remove the item and condense tree upwards
    +                    node.children.splice(index, 1);
    +                    path.push(node);
    +                    this._condense(path);
    +                    return this;
    +                }
    +            }
    +
    +            if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
    +                path.push(node);
    +                indexes.push(i);
    +                i = 0;
    +                parent = node;
    +                node = node.children[0];
    +
    +            } else if (parent) { // go right
    +                i++;
    +                node = parent.children[i];
    +                goingUp = false;
    +
    +            } else node = null; // nothing found
    +        }
    +
    +        return this;
    +    },
    +
    +    toBBox: function (item) { return item; },
    +
    +    compareMinX: function (a, b) { return a[0] - b[0]; },
    +    compareMinY: function (a, b) { return a[1] - b[1]; },
    +
    +    toJSON: function () { return this.data; },
    +
    +    fromJSON: function (data) {
    +        this.data = data;
    +        return this;
    +    },
    +
    +    _all: function (node, result) {
    +        var nodesToSearch = [];
    +        while (node) {
    +            if (node.leaf) result.push.apply(result, node.children);
    +            else nodesToSearch.push.apply(nodesToSearch, node.children);
    +
    +            node = nodesToSearch.pop();
    +        }
    +        return result;
    +    },
    +
    +    _build: function (items, left, right, height) {
    +
    +        var N = right - left + 1,
    +            M = this._maxEntries,
    +            node;
    +
    +        if (N <= M) {
    +            // reached leaf level; return leaf
    +            node = {
    +                children: items.slice(left, right + 1),
    +                height: 1,
    +                bbox: null,
    +                leaf: true
    +            };
    +            calcBBox(node, this.toBBox);
    +            return node;
    +        }
    +
    +        if (!height) {
    +            // target height of the bulk-loaded tree
    +            height = Math.ceil(Math.log(N) / Math.log(M));
    +
    +            // target number of root entries to maximize storage utilization
    +            M = Math.ceil(N / Math.pow(M, height - 1));
    +        }
    +
    +        // TODO eliminate recursion?
    +
    +        node = {
    +            children: [],
    +            height: height,
    +            bbox: null
    +        };
    +
    +        // split the items into M mostly square tiles
    +
    +        var N2 = Math.ceil(N / M),
    +            N1 = N2 * Math.ceil(Math.sqrt(M)),
    +            i, j, right2, right3;
    +
    +        multiSelect(items, left, right, N1, this.compareMinX);
    +
    +        for (i = left; i <= right; i += N1) {
    +
    +            right2 = Math.min(i + N1 - 1, right);
    +
    +            multiSelect(items, i, right2, N2, this.compareMinY);
    +
    +            for (j = i; j <= right2; j += N2) {
    +
    +                right3 = Math.min(j + N2 - 1, right2);
    +
    +                // pack each entry recursively
    +                node.children.push(this._build(items, j, right3, height - 1));
    +            }
    +        }
    +
    +        calcBBox(node, this.toBBox);
    +
    +        return node;
    +    },
    +
    +    _chooseSubtree: function (bbox, node, level, path) {
    +
    +        var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
    +
    +        while (true) {
    +            path.push(node);
    +
    +            if (node.leaf || path.length - 1 === level) break;
    +
    +            minArea = minEnlargement = Infinity;
    +
    +            for (i = 0, len = node.children.length; i < len; i++) {
    +                child = node.children[i];
    +                area = bboxArea(child.bbox);
    +                enlargement = enlargedArea(bbox, child.bbox) - area;
    +
    +                // choose entry with the least area enlargement
    +                if (enlargement < minEnlargement) {
    +                    minEnlargement = enlargement;
    +                    minArea = area < minArea ? area : minArea;
    +                    targetNode = child;
    +
    +                } else if (enlargement === minEnlargement) {
    +                    // otherwise choose one with the smallest area
    +                    if (area < minArea) {
    +                        minArea = area;
    +                        targetNode = child;
    +                    }
    +                }
    +            }
    +
    +            node = targetNode;
    +        }
    +
    +        return node;
    +    },
    +
    +    _insert: function (item, level, isNode) {
    +
    +        var toBBox = this.toBBox,
    +            bbox = isNode ? item.bbox : toBBox(item),
    +            insertPath = [];
    +
    +        // find the best node for accommodating the item, saving all nodes along the path too
    +        var node = this._chooseSubtree(bbox, this.data, level, insertPath);
    +
    +        // put the item into the node
    +        node.children.push(item);
    +        extend(node.bbox, bbox);
    +
    +        // split on node overflow; propagate upwards if necessary
    +        while (level >= 0) {
    +            if (insertPath[level].children.length > this._maxEntries) {
    +                this._split(insertPath, level);
    +                level--;
    +            } else break;
    +        }
    +
    +        // adjust bboxes along the insertion path
    +        this._adjustParentBBoxes(bbox, insertPath, level);
    +    },
    +
    +    // split overflowed node into two
    +    _split: function (insertPath, level) {
    +
    +        var node = insertPath[level],
    +            M = node.children.length,
    +            m = this._minEntries;
    +
    +        this._chooseSplitAxis(node, m, M);
    +
    +        var newNode = {
    +            children: node.children.splice(this._chooseSplitIndex(node, m, M)),
    +            height: node.height
    +        };
    +
    +        if (node.leaf) newNode.leaf = true;
    +
    +        calcBBox(node, this.toBBox);
    +        calcBBox(newNode, this.toBBox);
    +
    +        if (level) insertPath[level - 1].children.push(newNode);
    +        else this._splitRoot(node, newNode);
    +    },
    +
    +    _splitRoot: function (node, newNode) {
    +        // split root node
    +        this.data = {
    +            children: [node, newNode],
    +            height: node.height + 1
    +        };
    +        calcBBox(this.data, this.toBBox);
    +    },
    +
    +    _chooseSplitIndex: function (node, m, M) {
    +
    +        var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
    +
    +        minOverlap = minArea = Infinity;
    +
    +        for (i = m; i <= M - m; i++) {
    +            bbox1 = distBBox(node, 0, i, this.toBBox);
    +            bbox2 = distBBox(node, i, M, this.toBBox);
    +
    +            overlap = intersectionArea(bbox1, bbox2);
    +            area = bboxArea(bbox1) + bboxArea(bbox2);
    +
    +            // choose distribution with minimum overlap
    +            if (overlap < minOverlap) {
    +                minOverlap = overlap;
    +                index = i;
    +
    +                minArea = area < minArea ? area : minArea;
    +
    +            } else if (overlap === minOverlap) {
    +                // otherwise choose distribution with minimum area
    +                if (area < minArea) {
    +                    minArea = area;
    +                    index = i;
    +                }
    +            }
    +        }
    +
    +        return index;
    +    },
    +
    +    // sorts node children by the best axis for split
    +    _chooseSplitAxis: function (node, m, M) {
    +
    +        var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
    +            compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
    +            xMargin = this._allDistMargin(node, m, M, compareMinX),
    +            yMargin = this._allDistMargin(node, m, M, compareMinY);
    +
    +        // if total distributions margin value is minimal for x, sort by minX,
    +        // otherwise it's already sorted by minY
    +        if (xMargin < yMargin) node.children.sort(compareMinX);
    +    },
    +
    +    // total margin of all possible split distributions where each node is at least m full
    +    _allDistMargin: function (node, m, M, compare) {
    +
    +        node.children.sort(compare);
    +
    +        var toBBox = this.toBBox,
    +            leftBBox = distBBox(node, 0, m, toBBox),
    +            rightBBox = distBBox(node, M - m, M, toBBox),
    +            margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
    +            i, child;
    +
    +        for (i = m; i < M - m; i++) {
    +            child = node.children[i];
    +            extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
    +            margin += bboxMargin(leftBBox);
    +        }
    +
    +        for (i = M - m - 1; i >= m; i--) {
    +            child = node.children[i];
    +            extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
    +            margin += bboxMargin(rightBBox);
    +        }
    +
    +        return margin;
    +    },
    +
    +    _adjustParentBBoxes: function (bbox, path, level) {
    +        // adjust bboxes along the given tree path
    +        for (var i = level; i >= 0; i--) {
    +            extend(path[i].bbox, bbox);
    +        }
    +    },
    +
    +    _condense: function (path) {
    +        // go through the path, removing empty nodes and updating bboxes
    +        for (var i = path.length - 1, siblings; i >= 0; i--) {
    +            if (path[i].children.length === 0) {
    +                if (i > 0) {
    +                    siblings = path[i - 1].children;
    +                    siblings.splice(siblings.indexOf(path[i]), 1);
    +
    +                } else this.clear();
    +
    +            } else calcBBox(path[i], this.toBBox);
    +        }
    +    },
    +
    +    _initFormat: function (format) {
    +        // data format (minX, minY, maxX, maxY accessors)
    +
    +        // uses eval-type function compilation instead of just accepting a toBBox function
    +        // because the algorithms are very sensitive to sorting functions performance,
    +        // so they should be dead simple and without inner calls
    +
    +        // jshint evil: true
    +
    +        var compareArr = ['return a', ' - b', ';'];
    +
    +        this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
    +        this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
    +
    +        this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
    +    }
    +};
    +
    +
    +// calculate node's bbox from bboxes of its children
    +function calcBBox(node, toBBox) {
    +    node.bbox = distBBox(node, 0, node.children.length, toBBox);
    +}
    +
    +// min bounding rectangle of node children from k to p-1
    +function distBBox(node, k, p, toBBox) {
    +    var bbox = empty();
    +
    +    for (var i = k, child; i < p; i++) {
    +        child = node.children[i];
    +        extend(bbox, node.leaf ? toBBox(child) : child.bbox);
    +    }
    +
    +    return bbox;
    +}
    +
    +function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
    +
    +function extend(a, b) {
    +    a[0] = Math.min(a[0], b[0]);
    +    a[1] = Math.min(a[1], b[1]);
    +    a[2] = Math.max(a[2], b[2]);
    +    a[3] = Math.max(a[3], b[3]);
    +    return a;
    +}
    +
    +function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
    +function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
    +
    +function bboxArea(a)   { return (a[2] - a[0]) * (a[3] - a[1]); }
    +function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
    +
    +function enlargedArea(a, b) {
    +    return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
    +           (Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
    +}
    +
    +function intersectionArea(a, b) {
    +    var minX = Math.max(a[0], b[0]),
    +        minY = Math.max(a[1], b[1]),
    +        maxX = Math.min(a[2], b[2]),
    +        maxY = Math.min(a[3], b[3]);
    +
    +    return Math.max(0, maxX - minX) *
    +           Math.max(0, maxY - minY);
    +}
    +
    +function contains(a, b) {
    +    return a[0] <= b[0] &&
    +           a[1] <= b[1] &&
    +           b[2] <= a[2] &&
    +           b[3] <= a[3];
    +}
    +
    +function intersects(a, b) {
    +    return b[0] <= a[2] &&
    +           b[1] <= a[3] &&
    +           b[2] >= a[0] &&
    +           b[3] >= a[1];
    +}
    +
    +// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
    +// combines selection algorithm with binary divide & conquer approach
    +
    +function multiSelect(arr, left, right, n, compare) {
    +    var stack = [left, right],
    +        mid;
    +
    +    while (stack.length) {
    +        right = stack.pop();
    +        left = stack.pop();
    +
    +        if (right - left <= n) continue;
    +
    +        mid = left + Math.ceil((right - left) / n / 2) * n;
    +        select(arr, left, right, mid, compare);
    +
    +        stack.push(left, mid, mid, right);
    +    }
    +}
    +
    +// sort array between left and right (inclusive) so that the smallest k elements come first (unordered)
    +function select(arr, left, right, k, compare) {
    +    var n, i, z, s, sd, newLeft, newRight, t, j;
    +
    +    while (right > left) {
    +        if (right - left > 600) {
    +            n = right - left + 1;
    +            i = k - left + 1;
    +            z = Math.log(n);
    +            s = 0.5 * Math.exp(2 * z / 3);
    +            sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
    +            newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
    +            newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
    +            select(arr, newLeft, newRight, k, compare);
    +        }
    +
    +        t = arr[k];
    +        i = left;
    +        j = right;
    +
    +        swap(arr, left, k);
    +        if (compare(arr[right], t) > 0) swap(arr, left, right);
    +
    +        while (i < j) {
    +            swap(arr, i, j);
    +            i++;
    +            j--;
    +            while (compare(arr[i], t) < 0) i++;
    +            while (compare(arr[j], t) > 0) j--;
    +        }
    +
    +        if (compare(arr[left], t) === 0) swap(arr, left, j);
    +        else {
    +            j++;
    +            swap(arr, j, right);
    +        }
    +
    +        if (j <= k) left = j + 1;
    +        if (k <= j) right = j - 1;
    +    }
    +}
    +
    +function swap(arr, i, j) {
    +    var tmp = arr[i];
    +    arr[i] = arr[j];
    +    arr[j] = tmp;
    +}
    +
    +
    +// export as AMD/CommonJS module or global variable
    +if (typeof define === 'function' && define.amd) define('rbush', function() { return rbush; });
    +else if (typeof module !== 'undefined') module.exports = rbush;
    +else if (typeof self !== 'undefined') self.rbush = rbush;
    +else window.rbush = rbush;
    +
    +})();
    +
    +ol.ext.rbush = module.exports;
    +})();
    diff --git a/mapbox-gl/ol/ol/animation.js b/mapbox-gl/ol/ol/animation.js
    new file mode 100644
    index 0000000000..d04c2d37fa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/animation.js
    @@ -0,0 +1,161 @@
    +goog.provide('ol.animation');
    +
    +goog.require('ol.PreRenderFunction');
    +goog.require('ol.ViewHint');
    +goog.require('ol.coordinate');
    +goog.require('ol.easing');
    +
    +
    +/**
    + * @param {olx.animation.BounceOptions} options Bounce options.
    + * @return {ol.PreRenderFunction} Pre-render function.
    + * @api
    + */
    +ol.animation.bounce = function(options) {
    +  var resolution = options.resolution;
    +  var start = goog.isDef(options.start) ? options.start : goog.now();
    +  var duration = goog.isDef(options.duration) ? options.duration : 1000;
    +  var easing = goog.isDef(options.easing) ?
    +      options.easing : ol.easing.upAndDown;
    +  return (
    +      /**
    +       * @param {ol.Map} map Map.
    +       * @param {?olx.FrameState} frameState Frame state.
    +       */
    +      function(map, frameState) {
    +        if (frameState.time < start) {
    +          frameState.animate = true;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else if (frameState.time < start + duration) {
    +          var delta = easing((frameState.time - start) / duration);
    +          var deltaResolution = resolution - frameState.viewState.resolution;
    +          frameState.animate = true;
    +          frameState.viewState.resolution += delta * deltaResolution;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else {
    +          return false;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {olx.animation.PanOptions} options Pan options.
    + * @return {ol.PreRenderFunction} Pre-render function.
    + * @api
    + */
    +ol.animation.pan = function(options) {
    +  var source = options.source;
    +  var start = goog.isDef(options.start) ? options.start : goog.now();
    +  var sourceX = source[0];
    +  var sourceY = source[1];
    +  var duration = goog.isDef(options.duration) ? options.duration : 1000;
    +  var easing = goog.isDef(options.easing) ?
    +      options.easing : ol.easing.inAndOut;
    +  return (
    +      /**
    +       * @param {ol.Map} map Map.
    +       * @param {?olx.FrameState} frameState Frame state.
    +       */
    +      function(map, frameState) {
    +        if (frameState.time < start) {
    +          frameState.animate = true;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else if (frameState.time < start + duration) {
    +          var delta = 1 - easing((frameState.time - start) / duration);
    +          var deltaX = sourceX - frameState.viewState.center[0];
    +          var deltaY = sourceY - frameState.viewState.center[1];
    +          frameState.animate = true;
    +          frameState.viewState.center[0] += delta * deltaX;
    +          frameState.viewState.center[1] += delta * deltaY;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else {
    +          return false;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {olx.animation.RotateOptions} options Rotate options.
    + * @return {ol.PreRenderFunction} Pre-render function.
    + * @api
    + */
    +ol.animation.rotate = function(options) {
    +  var sourceRotation = goog.isDef(options.rotation) ? options.rotation : 0;
    +  var start = goog.isDef(options.start) ? options.start : goog.now();
    +  var duration = goog.isDef(options.duration) ? options.duration : 1000;
    +  var easing = goog.isDef(options.easing) ?
    +      options.easing : ol.easing.inAndOut;
    +  var anchor = goog.isDef(options.anchor) ?
    +      options.anchor : null;
    +
    +  return (
    +      /**
    +       * @param {ol.Map} map Map.
    +       * @param {?olx.FrameState} frameState Frame state.
    +       */
    +      function(map, frameState) {
    +        if (frameState.time < start) {
    +          frameState.animate = true;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else if (frameState.time < start + duration) {
    +          var delta = 1 - easing((frameState.time - start) / duration);
    +          var deltaRotation =
    +              (sourceRotation - frameState.viewState.rotation) * delta;
    +          frameState.animate = true;
    +          frameState.viewState.rotation += deltaRotation;
    +          if (!goog.isNull(anchor)) {
    +            var center = frameState.viewState.center;
    +            ol.coordinate.sub(center, anchor);
    +            ol.coordinate.rotate(center, deltaRotation);
    +            ol.coordinate.add(center, anchor);
    +          }
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else {
    +          return false;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {olx.animation.ZoomOptions} options Zoom options.
    + * @return {ol.PreRenderFunction} Pre-render function.
    + * @api
    + */
    +ol.animation.zoom = function(options) {
    +  var sourceResolution = options.resolution;
    +  var start = goog.isDef(options.start) ? options.start : goog.now();
    +  var duration = goog.isDef(options.duration) ? options.duration : 1000;
    +  var easing = goog.isDef(options.easing) ?
    +      options.easing : ol.easing.inAndOut;
    +  return (
    +      /**
    +       * @param {ol.Map} map Map.
    +       * @param {?olx.FrameState} frameState Frame state.
    +       */
    +      function(map, frameState) {
    +        if (frameState.time < start) {
    +          frameState.animate = true;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else if (frameState.time < start + duration) {
    +          var delta = 1 - easing((frameState.time - start) / duration);
    +          var deltaResolution =
    +              sourceResolution - frameState.viewState.resolution;
    +          frameState.animate = true;
    +          frameState.viewState.resolution += delta * deltaResolution;
    +          frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
    +          return true;
    +        } else {
    +          return false;
    +        }
    +      });
    +};
    diff --git a/mapbox-gl/ol/ol/animation.jsdoc b/mapbox-gl/ol/ol/animation.jsdoc
    new file mode 100644
    index 0000000000..3c56f28229
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/animation.jsdoc
    @@ -0,0 +1,13 @@
    +/**
    + * The animation static methods are designed to be used with the
    + * {@link ol.Map#beforeRender} method.  For example:
    + *
    + *     var map = new ol.Map({ ... });
    + *     var zoom = ol.animation.zoom({
    + *       resolution: map.getView().getResolution()
    + *     });
    + *     map.beforeRender(zoom);
    + *     map.getView().setResolution(map.getView().getResolution() * 2);
    + *
    + * @namespace ol.animation
    + */
    diff --git a/mapbox-gl/ol/ol/array.js b/mapbox-gl/ol/ol/array.js
    new file mode 100644
    index 0000000000..882a501262
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/array.js
    @@ -0,0 +1,104 @@
    +goog.provide('ol.array');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * @param {Array.<number>} arr Array.
    + * @param {number} target Target.
    + * @return {number} Index.
    + */
    +ol.array.binaryFindNearest = function(arr, target) {
    +  var index = goog.array.binarySearch(arr, target,
    +      /**
    +       * @param {number} a A.
    +       * @param {number} b B.
    +       * @return {number} b minus a.
    +       */
    +      function(a, b) {
    +        return b - a;
    +      });
    +  if (index >= 0) {
    +    return index;
    +  } else if (index == -1) {
    +    return 0;
    +  } else if (index == -arr.length - 1) {
    +    return arr.length - 1;
    +  } else {
    +    var left = -index - 2;
    +    var right = -index - 1;
    +    if (arr[left] - target < target - arr[right]) {
    +      return left;
    +    } else {
    +      return right;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<number>} arr Array.
    + * @param {number} target Target.
    + * @param {number} direction 0 means return the nearest, > 0
    + *    means return the largest nearest, < 0 means return the
    + *    smallest nearest.
    + * @return {number} Index.
    + */
    +ol.array.linearFindNearest = function(arr, target, direction) {
    +  var n = arr.length;
    +  if (arr[0] <= target) {
    +    return 0;
    +  } else if (target <= arr[n - 1]) {
    +    return n - 1;
    +  } else {
    +    var i;
    +    if (direction > 0) {
    +      for (i = 1; i < n; ++i) {
    +        if (arr[i] < target) {
    +          return i - 1;
    +        }
    +      }
    +    } else if (direction < 0) {
    +      for (i = 1; i < n; ++i) {
    +        if (arr[i] <= target) {
    +          return i;
    +        }
    +      }
    +    } else {
    +      for (i = 1; i < n; ++i) {
    +        if (arr[i] == target) {
    +          return i;
    +        } else if (arr[i] < target) {
    +          if (arr[i - 1] - target < target - arr[i]) {
    +            return i - 1;
    +          } else {
    +            return i;
    +          }
    +        }
    +      }
    +    }
    +    // We should never get here, but the compiler complains
    +    // if it finds a path for which no number is returned.
    +    goog.asserts.fail();
    +    return n - 1;
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<*>} arr Array.
    + * @param {number} begin Begin index.
    + * @param {number} end End index.
    + */
    +ol.array.reverseSubArray = function(arr, begin, end) {
    +  goog.asserts.assert(begin >= 0);
    +  goog.asserts.assert(end < arr.length);
    +  while (begin < end) {
    +    var tmp = arr[begin];
    +    arr[begin] = arr[end];
    +    arr[end] = tmp;
    +    ++begin;
    +    --end;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/attribution.js b/mapbox-gl/ol/ol/attribution.js
    new file mode 100644
    index 0000000000..da38e4a388
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/attribution.js
    @@ -0,0 +1,76 @@
    +goog.provide('ol.Attribution');
    +
    +goog.require('ol.TileRange');
    +
    +
    +
    +/**
    + * @classdesc
    + * An attribution for a layer source.
    + *
    + * Example:
    + *
    + *     source: new ol.source.OSM({
    + *       attributions: [
    + *         new ol.Attribution({
    + *           html: 'All maps &copy; ' +
    + *               '<a href="http://www.opencyclemap.org/">OpenCycleMap</a>'
    + *         }),
    + *         ol.source.OSM.ATTRIBUTION
    + *       ],
    + *     ..
    + *
    + * @constructor
    + * @param {olx.AttributionOptions} options Attribution options.
    + * @struct
    + * @api stable
    + */
    +ol.Attribution = function(options) {
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.html_ = options.html;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Array.<ol.TileRange>>}
    +   */
    +  this.tileRanges_ = goog.isDef(options.tileRanges) ?
    +      options.tileRanges : null;
    +
    +};
    +
    +
    +/**
    + * @return {string} HTML.
    + * @api stable
    + */
    +ol.Attribution.prototype.getHTML = function() {
    +  return this.html_;
    +};
    +
    +
    +/**
    + * @param {Object.<string, ol.TileRange>} tileRanges Tile ranges.
    + * @return {boolean} Intersects any tile range.
    + */
    +ol.Attribution.prototype.intersectsAnyTileRange = function(tileRanges) {
    +  if (goog.isNull(this.tileRanges_)) {
    +    return true;
    +  }
    +  var i, ii, tileRange, z;
    +  for (z in tileRanges) {
    +    if (!(z in this.tileRanges_)) {
    +      continue;
    +    }
    +    tileRange = tileRanges[z];
    +    for (i = 0, ii = this.tileRanges_[z].length; i < ii; ++i) {
    +      if (this.tileRanges_[z][i].intersects(tileRange)) {
    +        return true;
    +      }
    +    }
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/ol/ol/binary.js b/mapbox-gl/ol/ol/binary.js
    new file mode 100644
    index 0000000000..05e0086c25
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/binary.js
    @@ -0,0 +1,174 @@
    +// FIXME Test on Internet Explorer with VBArray
    +
    +goog.provide('ol.binary.Buffer');
    +goog.provide('ol.binary.IReader');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.userAgent');
    +goog.require('ol.has');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {ArrayBuffer|string} data Data.
    + */
    +ol.binary.Buffer = function(data) {
    +
    +  /**
    +   * @private
    +   * @type {ArrayBuffer|string}
    +   */
    +  this.data_ = data;
    +
    +};
    +
    +
    +/**
    + * @return {ol.binary.IReader} Reader.
    + */
    +ol.binary.Buffer.prototype.getReader = function() {
    +  var data = this.data_;
    +  if (ol.has.ARRAY_BUFFER) {
    +    var arrayBuffer;
    +    if (data instanceof ArrayBuffer) {
    +      arrayBuffer = data;
    +    } else if (goog.isString(data)) {
    +      // FIXME check what happens with Unicode
    +      arrayBuffer = new ArrayBuffer(data.length);
    +      var uint8View = new Uint8Array(arrayBuffer);
    +      var i, ii;
    +      for (i = 0, ii = data.length; i < ii; ++i) {
    +        uint8View[i] = data.charCodeAt(i);
    +      }
    +    } else {
    +      goog.asserts.fail();
    +      return null;
    +    }
    +    return new ol.binary.ArrayBufferReader(arrayBuffer);
    +  } else {
    +    goog.asserts.assert(goog.isString(data));
    +    goog.asserts.assert(
    +        goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('10.0'));
    +    return new ol.binary.ArrayReader(new VBArray(data).toArray());
    +  }
    +};
    +
    +
    +
    +/**
    + * @interface
    + */
    +ol.binary.IReader = function() {};
    +
    +
    +/**
    + * @return {boolean} At EOF.
    + */
    +ol.binary.IReader.prototype.atEOF = function() {};
    +
    +
    +/**
    + * @return {number} Byte.
    + */
    +ol.binary.IReader.prototype.readByte = function() {};
    +
    +
    +
    +/**
    + * @constructor
    + * @param {ArrayBuffer} arrayBuffer Array buffer.
    + * @implements {ol.binary.IReader}
    + */
    +ol.binary.ArrayBufferReader = function(arrayBuffer) {
    +
    +  /**
    +   * @private
    +   * @type {Uint8Array}
    +   */
    +  this.uint8View_ = new Uint8Array(arrayBuffer);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.length_ = this.uint8View_.length;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.offset_ = 0;
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.binary.ArrayBufferReader.prototype.atEOF = function() {
    +  return this.offset_ == this.length_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.binary.ArrayBufferReader.prototype.readByte = function() {
    +  if (this.offset_ < this.length_) {
    +    return this.uint8View_[this.offset_++];
    +  } else {
    +    goog.asserts.fail();
    +    return 0;
    +  }
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.binary.IReader}
    + * @param {Array.<number>} array Array.
    + */
    +ol.binary.ArrayReader = function(array) {
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.array_ = array;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.length_ = array.length;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.offset_ = 0;
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.binary.ArrayReader.prototype.atEOF = function() {
    +  return this.offset_ == this.length_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.binary.ArrayReader.prototype.readByte = function() {
    +  if (this.offset_ < this.length_) {
    +    return this.array_[this.offset_++];
    +  } else {
    +    goog.asserts.fail();
    +    return 0;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/canvasfunction.js b/mapbox-gl/ol/ol/canvasfunction.js
    new file mode 100644
    index 0000000000..db4820faa2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/canvasfunction.js
    @@ -0,0 +1,17 @@
    +goog.provide('ol.CanvasFunctionType');
    +
    +
    +/**
    + * A function returning the canvas element (`{HTMLCanvasElement}`)
    + * used by the source as an image. The arguments passed to the function are:
    + * {@link ol.Extent} the image extent, `{number}` the image resolution,
    + * `{number}` the device pixel ratio, {@link ol.Size} the image size, and
    + * {@link ol.proj.Projection} the image projection. The canvas returned by
    + * this function is cached by the source. The this keyword inside the function
    + * references the {@link ol.source.ImageCanvas}.
    + *
    + * @typedef {function(this:ol.source.ImageCanvas, ol.Extent, number,
    + *     number, ol.Size, ol.proj.Projection): HTMLCanvasElement}
    + * @api
    + */
    +ol.CanvasFunctionType;
    diff --git a/mapbox-gl/ol/ol/centerconstraint.js b/mapbox-gl/ol/ol/centerconstraint.js
    new file mode 100644
    index 0000000000..2cc4db9592
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/centerconstraint.js
    @@ -0,0 +1,42 @@
    +goog.provide('ol.CenterConstraint');
    +goog.provide('ol.CenterConstraintType');
    +
    +goog.require('goog.math');
    +
    +
    +/**
    + * @typedef {function((ol.Coordinate|undefined)): (ol.Coordinate|undefined)}
    + */
    +ol.CenterConstraintType;
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.CenterConstraintType}
    + */
    +ol.CenterConstraint.createExtent = function(extent) {
    +  return (
    +      /**
    +       * @param {ol.Coordinate|undefined} center Center.
    +       * @return {ol.Coordinate|undefined} Center.
    +       */
    +      function(center) {
    +        if (goog.isDef(center)) {
    +          return [
    +            goog.math.clamp(center[0], extent[0], extent[2]),
    +            goog.math.clamp(center[1], extent[1], extent[3])
    +          ];
    +        } else {
    +          return undefined;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate|undefined} center Center.
    + * @return {ol.Coordinate|undefined} Center.
    + */
    +ol.CenterConstraint.none = function(center) {
    +  return center;
    +};
    diff --git a/mapbox-gl/ol/ol/collection.js b/mapbox-gl/ol/ol/collection.js
    new file mode 100644
    index 0000000000..b7fd26bd30
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/collection.js
    @@ -0,0 +1,283 @@
    +/**
    + * An implementation of Google Maps' MVCArray.
    + * @see https://developers.google.com/maps/documentation/javascript/reference
    + */
    +
    +goog.provide('ol.Collection');
    +goog.provide('ol.CollectionEvent');
    +goog.provide('ol.CollectionEventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.events.Event');
    +goog.require('ol.Object');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.CollectionEventType = {
    +  /**
    +   * Triggered when an item is added to the collection.
    +   * @event ol.CollectionEvent#add
    +   * @api stable
    +   */
    +  ADD: 'add',
    +  /**
    +   * Triggered when an item is removed from the collection.
    +   * @event ol.CollectionEvent#remove
    +   * @api stable
    +   */
    +  REMOVE: 'remove'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.Collection} instances are instances of this
    + * type.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.CollectionEvent}
    + * @param {ol.CollectionEventType} type Type.
    + * @param {*=} opt_element Element.
    + * @param {Object=} opt_target Target.
    + */
    +ol.CollectionEvent = function(type, opt_element, opt_target) {
    +
    +  goog.base(this, type, opt_target);
    +
    +  /**
    +   * The element that is added to or removed from the collection.
    +   * @type {*}
    +   * @api stable
    +   */
    +  this.element = opt_element;
    +
    +};
    +goog.inherits(ol.CollectionEvent, goog.events.Event);
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.CollectionProperty = {
    +  LENGTH: 'length'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * An expanded version of standard JS Array, adding convenience methods for
    + * manipulation. Add and remove changes to the Collection trigger a Collection
    + * event. Note that this does not cover changes to the objects _within_ the
    + * Collection; they trigger events on the appropriate object, not on the
    + * Collection as a whole.
    + *
    + * Because a Collection is itself an {@link ol.Object}, it can be bound to any
    + * other Object or Collection such that a change in one will automatically be
    + * reflected in the other.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @fires ol.CollectionEvent
    + * @param {Array.<T>=} opt_array Array.
    + * @template T
    + * @api stable
    + */
    +ol.Collection = function(opt_array) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {Array.<T>}
    +   */
    +  this.array_ = goog.isDef(opt_array) ? opt_array : [];
    +
    +  this.updateLength_();
    +
    +};
    +goog.inherits(ol.Collection, ol.Object);
    +
    +
    +/**
    + * Remove all elements from the collection.
    + * @api stable
    + */
    +ol.Collection.prototype.clear = function() {
    +  while (this.getLength() > 0) {
    +    this.pop();
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<T>} arr Array.
    + * @return {ol.Collection.<T>} This collection.
    + * @api stable
    + */
    +ol.Collection.prototype.extend = function(arr) {
    +  var i, ii;
    +  for (i = 0, ii = arr.length; i < ii; ++i) {
    +    this.push(arr[i]);
    +  }
    +  return this;
    +};
    +
    +
    +/**
    + * Iterate over each element, calling the provided callback.
    + * @param {function(this: S, T, number, Array.<T>): *} f The function to call
    + *     for every element. This function takes 3 arguments (the element, the
    + *     index and the array). The return value is ignored.
    + * @param {S=} opt_this The object to use as `this` in `f`.
    + * @template S
    + * @api stable
    + */
    +ol.Collection.prototype.forEach = function(f, opt_this) {
    +  goog.array.forEach(this.array_, f, opt_this);
    +};
    +
    +
    +/**
    + * Get a reference to the underlying Array object. Warning: if the array
    + * is mutated, no events will be dispatched by the collection, and the
    + * collection's "length" property won't be in sync with the actual length
    + * of the array.
    + * @return {Array.<T>} Array.
    + * @api stable
    + */
    +ol.Collection.prototype.getArray = function() {
    +  return this.array_;
    +};
    +
    +
    +/**
    + * Get the element at the provided index.
    + * @param {number} index Index.
    + * @return {T} Element.
    + * @api stable
    + */
    +ol.Collection.prototype.item = function(index) {
    +  return this.array_[index];
    +};
    +
    +
    +/**
    + * Get the length of this collection.
    + * @return {number} The length of the array.
    + * @observable
    + * @api stable
    + */
    +ol.Collection.prototype.getLength = function() {
    +  return /** @type {number} */ (this.get(ol.CollectionProperty.LENGTH));
    +};
    +
    +
    +/**
    + * Insert an element at the provided index.
    + * @param {number} index Index.
    + * @param {T} elem Element.
    + * @api stable
    + */
    +ol.Collection.prototype.insertAt = function(index, elem) {
    +  goog.array.insertAt(this.array_, elem, index);
    +  this.updateLength_();
    +  this.dispatchEvent(
    +      new ol.CollectionEvent(ol.CollectionEventType.ADD, elem, this));
    +};
    +
    +
    +/**
    + * Remove the last element of the collection and return it.
    + * Return `undefined` if the collection is empty.
    + * @return {T|undefined} Element.
    + * @api stable
    + */
    +ol.Collection.prototype.pop = function() {
    +  return this.removeAt(this.getLength() - 1);
    +};
    +
    +
    +/**
    + * Insert the provided element at the end of the collection.
    + * @param {T} elem Element.
    + * @return {number} Length.
    + * @api stable
    + */
    +ol.Collection.prototype.push = function(elem) {
    +  var n = this.array_.length;
    +  this.insertAt(n, elem);
    +  return n;
    +};
    +
    +
    +/**
    + * Removes the first occurrence of elem from the collection.
    + * @param {T} elem Element.
    + * @return {T|undefined} The removed element or undefined if elem was not found.
    + * @api stable
    + */
    +ol.Collection.prototype.remove = function(elem) {
    +  var arr = this.array_;
    +  var i, ii;
    +  for (i = 0, ii = arr.length; i < ii; ++i) {
    +    if (arr[i] === elem) {
    +      return this.removeAt(i);
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * Remove the element at the provided index and return it.
    + * Return `undefined` if the collection does not contain this index.
    + * @param {number} index Index.
    + * @return {T|undefined} Value.
    + * @api stable
    + */
    +ol.Collection.prototype.removeAt = function(index) {
    +  var prev = this.array_[index];
    +  goog.array.removeAt(this.array_, index);
    +  this.updateLength_();
    +  this.dispatchEvent(
    +      new ol.CollectionEvent(ol.CollectionEventType.REMOVE, prev, this));
    +  return prev;
    +};
    +
    +
    +/**
    + * Set the element at the provided index.
    + * @param {number} index Index.
    + * @param {T} elem Element.
    + * @api stable
    + */
    +ol.Collection.prototype.setAt = function(index, elem) {
    +  var n = this.getLength();
    +  if (index < n) {
    +    var prev = this.array_[index];
    +    this.array_[index] = elem;
    +    this.dispatchEvent(
    +        new ol.CollectionEvent(ol.CollectionEventType.REMOVE, prev, this));
    +    this.dispatchEvent(
    +        new ol.CollectionEvent(ol.CollectionEventType.ADD, elem, this));
    +  } else {
    +    var j;
    +    for (j = n; j < index; ++j) {
    +      this.insertAt(j, undefined);
    +    }
    +    this.insertAt(index, elem);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Collection.prototype.updateLength_ = function() {
    +  this.set(ol.CollectionProperty.LENGTH, this.array_.length);
    +};
    diff --git a/mapbox-gl/ol/ol/color/color.js b/mapbox-gl/ol/ol/color/color.js
    new file mode 100644
    index 0000000000..a857308d57
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/color/color.js
    @@ -0,0 +1,332 @@
    +// We can't use goog.color or goog.color.alpha because they interally use a hex
    +// string representation that encodes each channel in a single byte.  This
    +// causes occasional loss of precision and rounding errors, especially in the
    +// alpha channel.
    +
    +goog.provide('ol.color');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.color');
    +goog.require('goog.color.names');
    +goog.require('goog.math');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol');
    +
    +
    +/**
    + * A color represented as a short array [red, green, blue, alpha].
    + * red, green, and blue should be integers in the range 0..255 inclusive.
    + * alpha should be a float in the range 0..1 inclusive.
    + * @typedef {Array.<number>}
    + * @api
    + */
    +ol.Color;
    +
    +
    +/**
    + * This RegExp matches # followed by 3 or 6 hex digits.
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.color.hexColorRe_ = /^#(?:[0-9a-f]{3}){1,2}$/i;
    +
    +
    +/**
    + * @see goog.color.rgbColorRe_
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.color.rgbColorRe_ =
    +    /^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;
    +
    +
    +/**
    + * @see goog.color.alpha.rgbaColorRe_
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.color.rgbaColorRe_ =
    +    /^(?:rgba)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|1|0\.\d{0,10})\)$/i;
    +
    +
    +/**
    + * @param {ol.Color} dst Destination.
    + * @param {ol.Color} src Source.
    + * @param {ol.Color=} opt_color Color.
    + * @return {ol.Color} Color.
    + */
    +ol.color.blend = function(dst, src, opt_color) {
    +  // http://en.wikipedia.org/wiki/Alpha_compositing
    +  // FIXME do we need to scale by 255?
    +  var out = goog.isDef(opt_color) ? opt_color : [];
    +  var dstA = dst[3];
    +  var srcA = dst[3];
    +  if (dstA == 1) {
    +    out[0] = (src[0] * srcA + dst[0] * (1 - srcA) + 0.5) | 0;
    +    out[1] = (src[1] * srcA + dst[1] * (1 - srcA) + 0.5) | 0;
    +    out[2] = (src[2] * srcA + dst[2] * (1 - srcA) + 0.5) | 0;
    +    out[4] = 1;
    +  } else if (srcA === 0) {
    +    out[0] = dst[0];
    +    out[1] = dst[1];
    +    out[2] = dst[2];
    +    out[3] = dstA;
    +  } else {
    +    var outA = srcA + dstA * (1 - srcA);
    +    if (outA === 0) {
    +      out[0] = 0;
    +      out[1] = 0;
    +      out[2] = 0;
    +      out[3] = 0;
    +    } else {
    +      out[0] = ((src[0] * srcA + dst[0] * dstA * (1 - srcA)) / outA + 0.5) | 0;
    +      out[1] = ((src[1] * srcA + dst[1] * dstA * (1 - srcA)) / outA + 0.5) | 0;
    +      out[2] = ((src[2] * srcA + dst[2] * dstA * (1 - srcA)) / outA + 0.5) | 0;
    +      out[3] = outA;
    +    }
    +  }
    +  goog.asserts.assert(ol.color.isValid(out));
    +  return out;
    +};
    +
    +
    +/**
    + * Return the color as an array. This function maintains a cache of calculated
    + * arrays which means the result should not be modified.
    + * @param {ol.Color|string} color Color.
    + * @return {ol.Color} Color.
    + * @api
    + */
    +ol.color.asArray = function(color) {
    +  if (goog.isArray(color)) {
    +    return color;
    +  } else {
    +    goog.asserts.assert(goog.isString(color));
    +    return ol.color.fromString(color);
    +  }
    +};
    +
    +
    +/**
    + * Return the color as an rgba string.
    + * @param {ol.Color|string} color Color.
    + * @return {string} Rgba string.
    + * @api
    + */
    +ol.color.asString = function(color) {
    +  if (goog.isString(color)) {
    +    return color;
    +  } else {
    +    goog.asserts.assert(goog.isArray(color));
    +    return ol.color.toString(color);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Color} color1 Color1.
    + * @param {ol.Color} color2 Color2.
    + * @return {boolean} Equals.
    + */
    +ol.color.equals = function(color1, color2) {
    +  return color1 === color2 || (
    +      color1[0] == color2[0] && color1[1] == color2[1] &&
    +      color1[2] == color2[2] && color1[3] == color2[3]);
    +};
    +
    +
    +/**
    + * @param {string} s String.
    + * @return {ol.Color} Color.
    + */
    +ol.color.fromString = (
    +    /**
    +     * @return {function(string): ol.Color}
    +     */
    +    function() {
    +
    +      // We maintain a small cache of parsed strings.  To provide cheap LRU-like
    +      // semantics, whenever the cache grows too large we simply delete an
    +      // arbitrary 25% of the entries.
    +
    +      /**
    +       * @const
    +       * @type {number}
    +       */
    +      var MAX_CACHE_SIZE = 1024;
    +
    +      /**
    +       * @type {Object.<string, ol.Color>}
    +       */
    +      var cache = {};
    +
    +      /**
    +       * @type {number}
    +       */
    +      var cacheSize = 0;
    +
    +      return (
    +          /**
    +           * @param {string} s String.
    +           * @return {ol.Color} Color.
    +           */
    +          function(s) {
    +            var color;
    +            if (cache.hasOwnProperty(s)) {
    +              color = cache[s];
    +            } else {
    +              if (cacheSize >= MAX_CACHE_SIZE) {
    +                var i = 0;
    +                var key;
    +                for (key in cache) {
    +                  if ((i++ & 3) === 0) {
    +                    delete cache[key];
    +                    --cacheSize;
    +                  }
    +                }
    +              }
    +              color = ol.color.fromStringInternal_(s);
    +              cache[s] = color;
    +              ++cacheSize;
    +            }
    +            return color;
    +          });
    +
    +    })();
    +
    +
    +/**
    + * @param {string} s String.
    + * @private
    + * @return {ol.Color} Color.
    + */
    +ol.color.fromStringInternal_ = function(s) {
    +
    +  var isHex = false;
    +  if (ol.ENABLE_NAMED_COLORS && goog.color.names.hasOwnProperty(s)) {
    +    // goog.color.names does not have a type declaration, so add a typecast
    +    s = /** @type {string} */ (goog.color.names[s]);
    +    isHex = true;
    +  }
    +
    +  var r, g, b, a, color, match;
    +  if (isHex || (match = ol.color.hexColorRe_.exec(s))) { // hex
    +    var n = s.length - 1; // number of hex digits
    +    goog.asserts.assert(n == 3 || n == 6);
    +    var d = n == 3 ? 1 : 2; // number of digits per channel
    +    r = parseInt(s.substr(1 + 0 * d, d), 16);
    +    g = parseInt(s.substr(1 + 1 * d, d), 16);
    +    b = parseInt(s.substr(1 + 2 * d, d), 16);
    +    if (d == 1) {
    +      r = (r << 4) + r;
    +      g = (g << 4) + g;
    +      b = (b << 4) + b;
    +    }
    +    a = 1;
    +    color = [r, g, b, a];
    +    goog.asserts.assert(ol.color.isValid(color));
    +    return color;
    +  } else if ((match = ol.color.rgbaColorRe_.exec(s))) { // rgba()
    +    r = Number(match[1]);
    +    g = Number(match[2]);
    +    b = Number(match[3]);
    +    a = Number(match[4]);
    +    color = [r, g, b, a];
    +    return ol.color.normalize(color, color);
    +  } else if ((match = ol.color.rgbColorRe_.exec(s))) { // rgb()
    +    r = Number(match[1]);
    +    g = Number(match[2]);
    +    b = Number(match[3]);
    +    color = [r, g, b, 1];
    +    return ol.color.normalize(color, color);
    +  } else {
    +    goog.asserts.fail(s + ' is not a valid color');
    +  }
    +
    +};
    +
    +
    +/**
    + * @param {ol.Color} color Color.
    + * @return {boolean} Is valid.
    + */
    +ol.color.isValid = function(color) {
    +  return 0 <= color[0] && color[0] < 256 &&
    +      0 <= color[1] && color[1] < 256 &&
    +      0 <= color[2] && color[2] < 256 &&
    +      0 <= color[3] && color[3] <= 1;
    +};
    +
    +
    +/**
    + * @param {ol.Color} color Color.
    + * @param {ol.Color=} opt_color Color.
    + * @return {ol.Color} Clamped color.
    + */
    +ol.color.normalize = function(color, opt_color) {
    +  var result = goog.isDef(opt_color) ? opt_color : [];
    +  result[0] = goog.math.clamp((color[0] + 0.5) | 0, 0, 255);
    +  result[1] = goog.math.clamp((color[1] + 0.5) | 0, 0, 255);
    +  result[2] = goog.math.clamp((color[2] + 0.5) | 0, 0, 255);
    +  result[3] = goog.math.clamp(color[3], 0, 1);
    +  return result;
    +};
    +
    +
    +/**
    + * @param {ol.Color} color Color.
    + * @return {string} String.
    + */
    +ol.color.toString = function(color) {
    +  var r = color[0];
    +  if (r != (r | 0)) {
    +    r = (r + 0.5) | 0;
    +  }
    +  var g = color[1];
    +  if (g != (g | 0)) {
    +    g = (g + 0.5) | 0;
    +  }
    +  var b = color[2];
    +  if (b != (b | 0)) {
    +    b = (b + 0.5) | 0;
    +  }
    +  var a = color[3];
    +  return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
    +};
    +
    +
    +/**
    + * @param {!ol.Color} color Color.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {!ol.Color=} opt_color Color.
    + * @return {ol.Color} Transformed color.
    + */
    +ol.color.transform = function(color, transform, opt_color) {
    +  var result = goog.isDef(opt_color) ? opt_color : [];
    +  result = goog.vec.Mat4.multVec3(transform, color, result);
    +  goog.asserts.assert(goog.isArray(result));
    +  result[3] = color[3];
    +  return ol.color.normalize(result, result);
    +};
    +
    +
    +/**
    + * @param {ol.Color|string} color1 Color2.
    + * @param {ol.Color|string} color2 Color2.
    + * @return {boolean} Equals.
    + */
    +ol.color.stringOrColorEquals = function(color1, color2) {
    +  if (color1 === color2 || color1 == color2) {
    +    return true;
    +  }
    +  if (goog.isString(color1)) {
    +    color1 = ol.color.fromString(color1);
    +  }
    +  if (goog.isString(color2)) {
    +    color2 = ol.color.fromString(color2);
    +  }
    +  return ol.color.equals(color1, color2);
    +};
    diff --git a/mapbox-gl/ol/ol/color/color.jsdoc b/mapbox-gl/ol/ol/color/color.jsdoc
    new file mode 100644
    index 0000000000..569033933c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/color/color.jsdoc
    @@ -0,0 +1,8 @@
    +/**
    + * Colors can be defined as a {@link ol.Color} array, or as strings in
    + * `rgb(r,g,b)` or `rgba(r,g,b,a)` format, or in hex `#rrggbb` or `#rgb` format.
    + * Color names, like 'red', 'blue' or 'green', may also be used with the
    + * Canvas renderer.
    + *
    + * @namespace ol.color
    + */
    diff --git a/mapbox-gl/ol/ol/color/colormatrix.js b/mapbox-gl/ol/ol/color/colormatrix.js
    new file mode 100644
    index 0000000000..ac947263fc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/color/colormatrix.js
    @@ -0,0 +1,205 @@
    +goog.provide('ol.color.Matrix');
    +
    +goog.require('goog.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + */
    +ol.color.Matrix = function() {
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.colorMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.brightness_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.brightnessMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.contrast_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.contrastMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.hue_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.hueMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.saturation_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.saturationMatrix_ = goog.vec.Mat4.createNumber();
    +
    +};
    +
    +
    +/**
    + * @param {!goog.vec.Mat4.Number} matrix Matrix.
    + * @param {number} value Brightness value.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.color.Matrix.makeBrightness = function(matrix, value) {
    +  goog.vec.Mat4.makeTranslate(matrix, value, value, value);
    +  return matrix;
    +};
    +
    +
    +/**
    + * @param {!goog.vec.Mat4.Number} matrix Matrix.
    + * @param {number} value Contrast value.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.color.Matrix.makeContrast = function(matrix, value) {
    +  goog.vec.Mat4.makeScale(matrix, value, value, value);
    +  var translateValue = (-0.5 * value + 0.5);
    +  goog.vec.Mat4.setColumnValues(matrix, 3,
    +      translateValue, translateValue, translateValue, 1);
    +  return matrix;
    +};
    +
    +
    +/**
    + * @param {!goog.vec.Mat4.Number} matrix Matrix.
    + * @param {number} value Hue value.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.color.Matrix.makeHue = function(matrix, value) {
    +  var cosHue = Math.cos(value);
    +  var sinHue = Math.sin(value);
    +  var v00 = 0.213 + cosHue * 0.787 - sinHue * 0.213;
    +  var v01 = 0.715 - cosHue * 0.715 - sinHue * 0.715;
    +  var v02 = 0.072 - cosHue * 0.072 + sinHue * 0.928;
    +  var v03 = 0;
    +  var v10 = 0.213 - cosHue * 0.213 + sinHue * 0.143;
    +  var v11 = 0.715 + cosHue * 0.285 + sinHue * 0.140;
    +  var v12 = 0.072 - cosHue * 0.072 - sinHue * 0.283;
    +  var v13 = 0;
    +  var v20 = 0.213 - cosHue * 0.213 - sinHue * 0.787;
    +  var v21 = 0.715 - cosHue * 0.715 + sinHue * 0.715;
    +  var v22 = 0.072 + cosHue * 0.928 + sinHue * 0.072;
    +  var v23 = 0;
    +  var v30 = 0;
    +  var v31 = 0;
    +  var v32 = 0;
    +  var v33 = 1;
    +  goog.vec.Mat4.setFromValues(matrix,
    +      v00, v10, v20, v30,
    +      v01, v11, v21, v31,
    +      v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +  return matrix;
    +};
    +
    +
    +/**
    + * @param {!goog.vec.Mat4.Number} matrix Matrix.
    + * @param {number} value Saturation value.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.color.Matrix.makeSaturation = function(matrix, value) {
    +  var v00 = 0.213 + 0.787 * value;
    +  var v01 = 0.715 - 0.715 * value;
    +  var v02 = 0.072 - 0.072 * value;
    +  var v03 = 0;
    +  var v10 = 0.213 - 0.213 * value;
    +  var v11 = 0.715 + 0.285 * value;
    +  var v12 = 0.072 - 0.072 * value;
    +  var v13 = 0;
    +  var v20 = 0.213 - 0.213 * value;
    +  var v21 = 0.715 - 0.715 * value;
    +  var v22 = 0.072 + 0.928 * value;
    +  var v23 = 0;
    +  var v30 = 0;
    +  var v31 = 0;
    +  var v32 = 0;
    +  var v33 = 1;
    +  goog.vec.Mat4.setFromValues(matrix,
    +      v00, v10, v20, v30,
    +      v01, v11, v21, v31,
    +      v02, v12, v22, v32,
    +      v03, v13, v23, v33);
    +  return matrix;
    +};
    +
    +
    +/**
    + * @param {number|undefined} brightness Brightness.
    + * @param {number|undefined} contrast Contrast.
    + * @param {number|undefined} hue Hue.
    + * @param {number|undefined} saturation Saturation.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.color.Matrix.prototype.getMatrix = function(
    +    brightness, contrast, hue, saturation) {
    +  var colorMatrixDirty = false;
    +  if (goog.isDef(brightness) && brightness !== this.brightness_) {
    +    ol.color.Matrix.makeBrightness(this.brightnessMatrix_, brightness);
    +    this.brightness_ = brightness;
    +    colorMatrixDirty = true;
    +  }
    +  if (goog.isDef(contrast) && contrast !== this.contrast_) {
    +    ol.color.Matrix.makeContrast(this.contrastMatrix_, contrast);
    +    this.contrast_ = contrast;
    +    colorMatrixDirty = true;
    +  }
    +  if (goog.isDef(hue) && hue !== this.hue_) {
    +    ol.color.Matrix.makeHue(this.hueMatrix_, hue);
    +    this.hue_ = hue;
    +    colorMatrixDirty = true;
    +  }
    +  if (goog.isDef(saturation) && saturation !== this.saturation_) {
    +    ol.color.Matrix.makeSaturation(this.saturationMatrix_, saturation);
    +    this.saturation_ = saturation;
    +    colorMatrixDirty = true;
    +  }
    +  if (colorMatrixDirty) {
    +    var colorMatrix = this.colorMatrix_;
    +    goog.vec.Mat4.makeIdentity(colorMatrix);
    +    if (goog.isDef(contrast)) {
    +      goog.vec.Mat4.multMat(colorMatrix, this.contrastMatrix_, colorMatrix);
    +    }
    +    if (goog.isDef(brightness)) {
    +      goog.vec.Mat4.multMat(colorMatrix, this.brightnessMatrix_, colorMatrix);
    +    }
    +    if (goog.isDef(saturation)) {
    +      goog.vec.Mat4.multMat(colorMatrix, this.saturationMatrix_, colorMatrix);
    +    }
    +    if (goog.isDef(hue)) {
    +      goog.vec.Mat4.multMat(colorMatrix, this.hueMatrix_, colorMatrix);
    +    }
    +  }
    +  return this.colorMatrix_;
    +};
    diff --git a/mapbox-gl/ol/ol/constraints.js b/mapbox-gl/ol/ol/constraints.js
    new file mode 100644
    index 0000000000..84fc289b6f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/constraints.js
    @@ -0,0 +1,35 @@
    +goog.provide('ol.Constraints');
    +
    +goog.require('ol.CenterConstraintType');
    +goog.require('ol.ResolutionConstraintType');
    +goog.require('ol.RotationConstraintType');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {ol.CenterConstraintType} centerConstraint Center constraint.
    + * @param {ol.ResolutionConstraintType} resolutionConstraint
    + *     Resolution constraint.
    + * @param {ol.RotationConstraintType} rotationConstraint
    + *     Rotation constraint.
    + */
    +ol.Constraints =
    +    function(centerConstraint, resolutionConstraint, rotationConstraint) {
    +
    +  /**
    +   * @type {ol.CenterConstraintType}
    +   */
    +  this.center = centerConstraint;
    +
    +  /**
    +   * @type {ol.ResolutionConstraintType}
    +   */
    +  this.resolution = resolutionConstraint;
    +
    +  /**
    +   * @type {ol.RotationConstraintType}
    +   */
    +  this.rotation = rotationConstraint;
    +
    +};
    diff --git a/mapbox-gl/ol/ol/control.jsdoc b/mapbox-gl/ol/ol/control.jsdoc
    new file mode 100644
    index 0000000000..bd09e4c9b2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.control
    + */
    diff --git a/mapbox-gl/ol/ol/control/attributioncontrol.js b/mapbox-gl/ol/ol/control/attributioncontrol.js
    new file mode 100644
    index 0000000000..a2623b956b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/attributioncontrol.js
    @@ -0,0 +1,397 @@
    +// FIXME handle date line wrap
    +
    +goog.provide('ol.control.Attribution');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('ol.Attribution');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +
    +
    +
    +/**
    + * @classdesc
    + * Control to show all the attributions associated with the layer sources
    + * in the map. This control is one of the default controls included in maps.
    + * By default it will show in the bottom right portion of the map, but this can
    + * be changed by using a css selector for `.ol-attribution`.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.AttributionOptions=} opt_options Attribution options.
    + * @api stable
    + */
    +ol.control.Attribution = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.ulElement_ = goog.dom.createElement(goog.dom.TagName.UL);
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.logoLi_ = goog.dom.createElement(goog.dom.TagName.LI);
    +
    +  goog.dom.appendChild(this.ulElement_, this.logoLi_);
    +  goog.style.setElementShown(this.logoLi_, false);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.collapsed_ = goog.isDef(options.collapsed) ? options.collapsed : true;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.collapsible_ = goog.isDef(options.collapsible) ?
    +      options.collapsible : true;
    +
    +  if (!this.collapsible_) {
    +    this.collapsed_ = false;
    +  }
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-attribution';
    +
    +  var tipLabel = goog.isDef(options.tipLabel) ?
    +      options.tipLabel : 'Attributions';
    +
    +  var collapseLabel = goog.isDef(options.collapseLabel) ?
    +      options.collapseLabel : '\u00BB';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.collapseLabel_ = /** @type {Node} */ (goog.isString(collapseLabel) ?
    +      goog.dom.createDom(goog.dom.TagName.SPAN, {}, collapseLabel) :
    +      collapseLabel);
    +
    +  var label = goog.isDef(options.label) ? options.label : 'i';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.label_ = /** @type {Node} */ (goog.isString(label) ?
    +      goog.dom.createDom(goog.dom.TagName.SPAN, {}, label) :
    +      label);
    +
    +  var activeLabel = (this.collapsible_ && !this.collapsed_) ?
    +      this.collapseLabel_ : this.label_;
    +  var button = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'type': 'button',
    +    'title': tipLabel
    +  }, activeLabel);
    +
    +  goog.events.listen(button, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +
    +  goog.events.listen(button, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
    +      ol.css.CLASS_CONTROL +
    +      (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') +
    +      (this.collapsible_ ? '' : ' ol-uncollapsible');
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV,
    +      cssClasses, this.ulElement_, button);
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.Attribution.render;
    +
    +  goog.base(this, {
    +    element: element,
    +    render: render,
    +    target: options.target
    +  });
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = true;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Element>}
    +   */
    +  this.attributionElements_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, boolean>}
    +   */
    +  this.attributionElementRenderedVisible_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Element>}
    +   */
    +  this.logoElements_ = {};
    +
    +};
    +goog.inherits(ol.control.Attribution, ol.control.Control);
    +
    +
    +/**
    + * @param {?olx.FrameState} frameState Frame state.
    + * @return {Array.<Object.<string, ol.Attribution>>} Attributions.
    + */
    +ol.control.Attribution.prototype.getSourceAttributions = function(frameState) {
    +  var i, ii, j, jj, tileRanges, source, sourceAttribution,
    +      sourceAttributionKey, sourceAttributions, sourceKey;
    +  var layerStatesArray = frameState.layerStatesArray;
    +  /** @type {Object.<string, ol.Attribution>} */
    +  var attributions = goog.object.clone(frameState.attributions);
    +  /** @type {Object.<string, ol.Attribution>} */
    +  var hiddenAttributions = {};
    +  for (i = 0, ii = layerStatesArray.length; i < ii; i++) {
    +    source = layerStatesArray[i].layer.getSource();
    +    if (goog.isNull(source)) {
    +      continue;
    +    }
    +    sourceKey = goog.getUid(source).toString();
    +    sourceAttributions = source.getAttributions();
    +    if (goog.isNull(sourceAttributions)) {
    +      continue;
    +    }
    +    for (j = 0, jj = sourceAttributions.length; j < jj; j++) {
    +      sourceAttribution = sourceAttributions[j];
    +      sourceAttributionKey = goog.getUid(sourceAttribution).toString();
    +      if (sourceAttributionKey in attributions) {
    +        continue;
    +      }
    +      tileRanges = frameState.usedTiles[sourceKey];
    +      if (goog.isDef(tileRanges) &&
    +          sourceAttribution.intersectsAnyTileRange(tileRanges)) {
    +        if (sourceAttributionKey in hiddenAttributions) {
    +          delete hiddenAttributions[sourceAttributionKey];
    +        }
    +        attributions[sourceAttributionKey] = sourceAttribution;
    +      }
    +      else {
    +        hiddenAttributions[sourceAttributionKey] = sourceAttribution;
    +      }
    +    }
    +  }
    +  return [attributions, hiddenAttributions];
    +};
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.Attribution}
    + * @api
    + */
    +ol.control.Attribution.render = function(mapEvent) {
    +  this.updateElement_(mapEvent.frameState);
    +};
    +
    +
    +/**
    + * @private
    + * @param {?olx.FrameState} frameState Frame state.
    + */
    +ol.control.Attribution.prototype.updateElement_ = function(frameState) {
    +
    +  if (goog.isNull(frameState)) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.element, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return;
    +  }
    +
    +  var attributions = this.getSourceAttributions(frameState);
    +  /** @type {Object.<string, ol.Attribution>} */
    +  var visibleAttributions = attributions[0];
    +  /** @type {Object.<string, ol.Attribution>} */
    +  var hiddenAttributions = attributions[1];
    +
    +  var attributionElement, attributionKey;
    +  for (attributionKey in this.attributionElements_) {
    +    if (attributionKey in visibleAttributions) {
    +      if (!this.attributionElementRenderedVisible_[attributionKey]) {
    +        goog.style.setElementShown(
    +            this.attributionElements_[attributionKey], true);
    +        this.attributionElementRenderedVisible_[attributionKey] = true;
    +      }
    +      delete visibleAttributions[attributionKey];
    +    }
    +    else if (attributionKey in hiddenAttributions) {
    +      if (this.attributionElementRenderedVisible_[attributionKey]) {
    +        goog.style.setElementShown(
    +            this.attributionElements_[attributionKey], false);
    +        delete this.attributionElementRenderedVisible_[attributionKey];
    +      }
    +      delete hiddenAttributions[attributionKey];
    +    }
    +    else {
    +      goog.dom.removeNode(this.attributionElements_[attributionKey]);
    +      delete this.attributionElements_[attributionKey];
    +      delete this.attributionElementRenderedVisible_[attributionKey];
    +    }
    +  }
    +  for (attributionKey in visibleAttributions) {
    +    attributionElement = goog.dom.createElement(goog.dom.TagName.LI);
    +    attributionElement.innerHTML =
    +        visibleAttributions[attributionKey].getHTML();
    +    goog.dom.appendChild(this.ulElement_, attributionElement);
    +    this.attributionElements_[attributionKey] = attributionElement;
    +    this.attributionElementRenderedVisible_[attributionKey] = true;
    +  }
    +  for (attributionKey in hiddenAttributions) {
    +    attributionElement = goog.dom.createElement(goog.dom.TagName.LI);
    +    attributionElement.innerHTML =
    +        hiddenAttributions[attributionKey].getHTML();
    +    goog.style.setElementShown(attributionElement, false);
    +    goog.dom.appendChild(this.ulElement_, attributionElement);
    +    this.attributionElements_[attributionKey] = attributionElement;
    +  }
    +
    +  var renderVisible =
    +      !goog.object.isEmpty(this.attributionElementRenderedVisible_) ||
    +      !goog.object.isEmpty(frameState.logos);
    +  if (this.renderedVisible_ != renderVisible) {
    +    goog.style.setElementShown(this.element, renderVisible);
    +    this.renderedVisible_ = renderVisible;
    +  }
    +  if (renderVisible &&
    +      goog.object.isEmpty(this.attributionElementRenderedVisible_)) {
    +    goog.dom.classlist.add(this.element, 'ol-logo-only');
    +  } else {
    +    goog.dom.classlist.remove(this.element, 'ol-logo-only');
    +  }
    +
    +  this.insertLogos_(frameState);
    +
    +};
    +
    +
    +/**
    + * @param {?olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.control.Attribution.prototype.insertLogos_ = function(frameState) {
    +
    +  var logo;
    +  var logos = frameState.logos;
    +  var logoElements = this.logoElements_;
    +
    +  for (logo in logoElements) {
    +    if (!(logo in logos)) {
    +      goog.dom.removeNode(logoElements[logo]);
    +      delete logoElements[logo];
    +    }
    +  }
    +
    +  var image, logoElement, logoKey;
    +  for (logoKey in logos) {
    +    if (!(logoKey in logoElements)) {
    +      image = new Image();
    +      image.src = logoKey;
    +      var logoValue = logos[logoKey];
    +      if (logoValue === '') {
    +        logoElement = image;
    +      } else {
    +        logoElement = goog.dom.createDom(goog.dom.TagName.A, {
    +          'href': logoValue
    +        });
    +        logoElement.appendChild(image);
    +      }
    +      goog.dom.appendChild(this.logoLi_, logoElement);
    +      logoElements[logoKey] = logoElement;
    +    }
    +  }
    +
    +  goog.style.setElementShown(this.logoLi_, !goog.object.isEmpty(logos));
    +
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.Attribution.prototype.handleClick_ = function(event) {
    +  event.preventDefault();
    +  this.handleToggle_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.Attribution.prototype.handleToggle_ = function() {
    +  goog.dom.classlist.toggle(this.element, 'ol-collapsed');
    +  if (this.collapsed_) {
    +    goog.dom.replaceNode(this.collapseLabel_, this.label_);
    +  } else {
    +    goog.dom.replaceNode(this.label_, this.collapseLabel_);
    +  }
    +  this.collapsed_ = !this.collapsed_;
    +};
    +
    +
    +/**
    + * @return {boolean} True if the widget is collapsible.
    + * @api stable
    + */
    +ol.control.Attribution.prototype.getCollapsible = function() {
    +  return this.collapsible_;
    +};
    +
    +
    +/**
    + * @param {boolean} collapsible True if the widget is collapsible.
    + * @api stable
    + */
    +ol.control.Attribution.prototype.setCollapsible = function(collapsible) {
    +  if (this.collapsible_ === collapsible) {
    +    return;
    +  }
    +  this.collapsible_ = collapsible;
    +  goog.dom.classlist.toggle(this.element, 'ol-uncollapsible');
    +  if (!collapsible && this.collapsed_) {
    +    this.handleToggle_();
    +  }
    +};
    +
    +
    +/**
    + * @param {boolean} collapsed True if the widget is collapsed.
    + * @api stable
    + */
    +ol.control.Attribution.prototype.setCollapsed = function(collapsed) {
    +  if (!this.collapsible_ || this.collapsed_ === collapsed) {
    +    return;
    +  }
    +  this.handleToggle_();
    +};
    +
    +
    +/**
    + * @return {boolean} True if the widget is collapsed.
    + * @api stable
    + */
    +ol.control.Attribution.prototype.getCollapsed = function() {
    +  return this.collapsed_;
    +};
    diff --git a/mapbox-gl/ol/ol/control/control.js b/mapbox-gl/ol/ol/control/control.js
    new file mode 100644
    index 0000000000..ead9a74b8e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/control.js
    @@ -0,0 +1,140 @@
    +goog.provide('ol.control.Control');
    +
    +goog.require('goog.array');
    +goog.require('goog.dom');
    +goog.require('goog.events');
    +goog.require('ol.MapEventType');
    +goog.require('ol.Object');
    +
    +
    +
    +/**
    + * @classdesc
    + * A control is a visible widget with a DOM element in a fixed position on the
    + * screen. They can involve user input (buttons), or be informational only;
    + * the position is determined using CSS. By default these are placed in the
    + * container with CSS class name `ol-overlaycontainer-stopevent`, but can use
    + * any outside DOM element.
    + *
    + * This is the base class for controls. You can use it for simple custom
    + * controls by creating the element with listeners, creating an instance:
    + * ```js
    + * var myControl = new ol.control.Control({element: myElement});
    + * ```
    + * and then adding this to the map.
    + *
    + * The main advantage of having this as a control rather than a simple separate
    + * DOM element is that preventing propagation is handled for you. Controls
    + * will also be `ol.Object`s in a `ol.Collection`, so you can use their
    + * methods.
    + *
    + * You can also extend this base for your own control class. See
    + * examples/custom-controls for an example of how to do this.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @implements {oli.control.Control}
    + * @param {olx.control.ControlOptions} options Control options.
    + * @api stable
    + */
    +ol.control.Control = function(options) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @protected
    +   * @type {Element}
    +   */
    +  this.element = goog.isDef(options.element) ? options.element : null;
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.target_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Map}
    +   */
    +  this.map_ = null;
    +
    +  /**
    +   * @protected
    +   * @type {!Array.<?number>}
    +   */
    +  this.listenerKeys = [];
    +
    +  /**
    +   * @type {function(ol.MapEvent)}
    +   */
    +  this.render = goog.isDef(options.render) ? options.render : goog.nullFunction;
    +
    +  if (goog.isDef(options.target)) {
    +    this.setTarget(options.target);
    +  }
    +
    +};
    +goog.inherits(ol.control.Control, ol.Object);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.control.Control.prototype.disposeInternal = function() {
    +  goog.dom.removeNode(this.element);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Get the map associated with this control.
    + * @return {ol.Map} Map.
    + * @api stable
    + */
    +ol.control.Control.prototype.getMap = function() {
    +  return this.map_;
    +};
    +
    +
    +/**
    + * Remove the control from its current map and attach it to the new map.
    + * Subclasses may set up event handlers to get notified about changes to
    + * the map here.
    + * @param {ol.Map} map Map.
    + * @api stable
    + */
    +ol.control.Control.prototype.setMap = function(map) {
    +  if (!goog.isNull(this.map_)) {
    +    goog.dom.removeNode(this.element);
    +  }
    +  if (!goog.array.isEmpty(this.listenerKeys)) {
    +    goog.array.forEach(this.listenerKeys, goog.events.unlistenByKey);
    +    this.listenerKeys.length = 0;
    +  }
    +  this.map_ = map;
    +  if (!goog.isNull(this.map_)) {
    +    var target = !goog.isNull(this.target_) ?
    +        this.target_ : map.getOverlayContainerStopEvent();
    +    goog.dom.appendChild(target, this.element);
    +    if (this.render !== goog.nullFunction) {
    +      this.listenerKeys.push(goog.events.listen(map,
    +          ol.MapEventType.POSTRENDER, this.render, false, this));
    +    }
    +    map.render();
    +  }
    +};
    +
    +
    +/**
    + * This function is used to set a target element for the control. It has no
    + * effect if it is called after the control has been added to the map (i.e.
    + * after `setMap` is called on the control). If no `target` is set in the
    + * options passed to the control constructor and if `setTarget` is not called
    + * then the control is added to the map's overlay container.
    + * @param {Element|string} target Target.
    + * @api
    + */
    +ol.control.Control.prototype.setTarget = function(target) {
    +  this.target_ = goog.dom.getElement(target);
    +};
    diff --git a/mapbox-gl/ol/ol/control/controldefaults.js b/mapbox-gl/ol/ol/control/controldefaults.js
    new file mode 100644
    index 0000000000..c9c6170ac9
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/controldefaults.js
    @@ -0,0 +1,47 @@
    +goog.provide('ol.control');
    +
    +goog.require('ol.Collection');
    +goog.require('ol.control.Attribution');
    +goog.require('ol.control.Rotate');
    +goog.require('ol.control.Zoom');
    +
    +
    +/**
    + * Set of controls included in maps by default. Unless configured otherwise,
    + * this returns a collection containing an instance of each of the following
    + * controls:
    + * * {@link ol.control.Zoom}
    + * * {@link ol.control.Rotate}
    + * * {@link ol.control.Attribution}
    + *
    + * @param {olx.control.DefaultsOptions=} opt_options Defaults options.
    + * @return {ol.Collection.<ol.control.Control>} Controls.
    + * @api stable
    + */
    +ol.control.defaults = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var controls = new ol.Collection();
    +
    +  var zoomControl = goog.isDef(options.zoom) ?
    +      options.zoom : true;
    +  if (zoomControl) {
    +    controls.push(new ol.control.Zoom(options.zoomOptions));
    +  }
    +
    +  var rotateControl = goog.isDef(options.rotate) ?
    +      options.rotate : true;
    +  if (rotateControl) {
    +    controls.push(new ol.control.Rotate(options.rotateOptions));
    +  }
    +
    +  var attributionControl = goog.isDef(options.attribution) ?
    +      options.attribution : true;
    +  if (attributionControl) {
    +    controls.push(new ol.control.Attribution(options.attributionOptions));
    +  }
    +
    +  return controls;
    +
    +};
    diff --git a/mapbox-gl/ol/ol/control/fullscreencontrol.js b/mapbox-gl/ol/ol/control/fullscreencontrol.js
    new file mode 100644
    index 0000000000..6ddf69b072
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/fullscreencontrol.js
    @@ -0,0 +1,150 @@
    +goog.provide('ol.control.FullScreen');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.fullscreen');
    +goog.require('goog.dom.fullscreen.EventType');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +
    +
    +
    +/**
    + * @classdesc
    + * Provides a button that when clicked fills up the full screen with the map.
    + * When in full screen mode, a close button is shown to exit full screen mode.
    + * The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to
    + * toggle the map in full screen mode.
    + *
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.FullScreenOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.control.FullScreen = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.cssClassName_ = goog.isDef(options.className) ?
    +      options.className : 'ol-full-screen';
    +
    +  var label = goog.isDef(options.label) ? options.label : '\u2194';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.labelNode_ = /** @type {Node} */ (goog.isString(label) ?
    +          goog.dom.createTextNode(label) : label);
    +
    +  var labelActive = goog.isDef(options.labelActive) ?
    +      options.labelActive : '\u00d7';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.labelActiveNode_ = /** @type {Node} */ (goog.isString(labelActive) ?
    +          goog.dom.createTextNode(labelActive) : labelActive);
    +
    +  var tipLabel = goog.isDef(options.tipLabel) ?
    +      options.tipLabel : 'Toggle full-screen';
    +  var button = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'class': this.cssClassName_ + '-' + goog.dom.fullscreen.isFullScreen(),
    +    'type': 'button',
    +    'title': tipLabel
    +  }, this.labelNode_);
    +
    +  goog.events.listen(button, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +
    +  goog.events.listen(button, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  goog.events.listen(goog.global.document,
    +      goog.dom.fullscreen.EventType.CHANGE,
    +      this.handleFullScreenChange_, false, this);
    +
    +  var cssClasses = this.cssClassName_ + ' ' + ol.css.CLASS_UNSELECTABLE +
    +      ' ' + ol.css.CLASS_CONTROL + ' ' +
    +      (!goog.dom.fullscreen.isSupported() ? ol.css.CLASS_UNSUPPORTED : '');
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, button);
    +
    +  goog.base(this, {
    +    element: element,
    +    target: options.target
    +  });
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.keys_ = goog.isDef(options.keys) ? options.keys : false;
    +
    +};
    +goog.inherits(ol.control.FullScreen, ol.control.Control);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.FullScreen.prototype.handleClick_ = function(event) {
    +  event.preventDefault();
    +  this.handleFullScreen_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.FullScreen.prototype.handleFullScreen_ = function() {
    +  if (!goog.dom.fullscreen.isSupported()) {
    +    return;
    +  }
    +  var map = this.getMap();
    +  if (goog.isNull(map)) {
    +    return;
    +  }
    +  if (goog.dom.fullscreen.isFullScreen()) {
    +    goog.dom.fullscreen.exitFullScreen();
    +  } else {
    +    var target = map.getTarget();
    +    goog.asserts.assert(goog.isDefAndNotNull(target));
    +    var element = goog.dom.getElement(target);
    +    goog.asserts.assert(goog.isDefAndNotNull(element));
    +    if (this.keys_) {
    +      goog.dom.fullscreen.requestFullScreenWithKeys(element);
    +    } else {
    +      goog.dom.fullscreen.requestFullScreen(element);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.FullScreen.prototype.handleFullScreenChange_ = function() {
    +  var map = this.getMap();
    +  if (goog.dom.fullscreen.isFullScreen()) {
    +    goog.dom.replaceNode(this.labelActiveNode_, this.labelNode_);
    +  } else {
    +    goog.dom.replaceNode(this.labelNode_, this.labelActiveNode_);
    +  }
    +  if (!goog.isNull(map)) {
    +    map.updateSize();
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/control/mousepositioncontrol.js b/mapbox-gl/ol/ol/control/mousepositioncontrol.js
    new file mode 100644
    index 0000000000..ef558344aa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/mousepositioncontrol.js
    @@ -0,0 +1,265 @@
    +// FIXME should listen on appropriate pane, once it is defined
    +
    +goog.provide('ol.control.MousePosition');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.CoordinateFormatType');
    +goog.require('ol.Object');
    +goog.require('ol.Pixel');
    +goog.require('ol.TransformFunction');
    +goog.require('ol.control.Control');
    +goog.require('ol.proj');
    +goog.require('ol.proj.Projection');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.control.MousePositionProperty = {
    +  PROJECTION: 'projection',
    +  COORDINATE_FORMAT: 'coordinateFormat'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * A control to show the 2D coordinates of the mouse cursor. By default, these
    + * are in the view projection, but can be in any supported projection.
    + * By default the control is shown in the top right corner of the map, but this
    + * can be changed by using the css selector `.ol-mouse-position`.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.MousePositionOptions=} opt_options Mouse position
    + *     options.
    + * @api stable
    + */
    +ol.control.MousePosition = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-mouse-position';
    +
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, className);
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.MousePosition.render;
    +
    +  goog.base(this, {
    +    element: element,
    +    render: render,
    +    target: options.target
    +  });
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.control.MousePositionProperty.PROJECTION),
    +      this.handleProjectionChanged_, false, this);
    +
    +  if (goog.isDef(options.coordinateFormat)) {
    +    this.setCoordinateFormat(options.coordinateFormat);
    +  }
    +  if (goog.isDef(options.projection)) {
    +    this.setProjection(ol.proj.get(options.projection));
    +  }
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.undefinedHTML_ = goog.isDef(options.undefinedHTML) ?
    +      options.undefinedHTML : '';
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.renderedHTML_ = element.innerHTML;
    +
    +  /**
    +   * @private
    +   * @type {ol.proj.Projection}
    +   */
    +  this.mapProjection_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.TransformFunction}
    +   */
    +  this.transform_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Pixel}
    +   */
    +  this.lastMouseMovePixel_ = null;
    +
    +};
    +goog.inherits(ol.control.MousePosition, ol.control.Control);
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.MousePosition}
    + * @api
    + */
    +ol.control.MousePosition.render = function(mapEvent) {
    +  var frameState = mapEvent.frameState;
    +  if (goog.isNull(frameState)) {
    +    this.mapProjection_ = null;
    +  } else {
    +    if (this.mapProjection_ != frameState.viewState.projection) {
    +      this.mapProjection_ = frameState.viewState.projection;
    +      this.transform_ = null;
    +    }
    +  }
    +  this.updateHTML_(this.lastMouseMovePixel_);
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.MousePosition.prototype.handleProjectionChanged_ = function() {
    +  this.transform_ = null;
    +};
    +
    +
    +/**
    + * @return {ol.CoordinateFormatType|undefined} The format to render the current
    + *     position in.
    + * @observable
    + * @api stable
    + */
    +ol.control.MousePosition.prototype.getCoordinateFormat = function() {
    +  return /** @type {ol.CoordinateFormatType|undefined} */ (
    +      this.get(ol.control.MousePositionProperty.COORDINATE_FORMAT));
    +};
    +goog.exportProperty(
    +    ol.control.MousePosition.prototype,
    +    'getCoordinateFormat',
    +    ol.control.MousePosition.prototype.getCoordinateFormat);
    +
    +
    +/**
    + * @return {ol.proj.Projection|undefined} The projection to report mouse
    + *     position in.
    + * @observable
    + * @api stable
    + */
    +ol.control.MousePosition.prototype.getProjection = function() {
    +  return /** @type {ol.proj.Projection|undefined} */ (
    +      this.get(ol.control.MousePositionProperty.PROJECTION));
    +};
    +goog.exportProperty(
    +    ol.control.MousePosition.prototype,
    +    'getProjection',
    +    ol.control.MousePosition.prototype.getProjection);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @protected
    + */
    +ol.control.MousePosition.prototype.handleMouseMove = function(browserEvent) {
    +  var map = this.getMap();
    +  this.lastMouseMovePixel_ = map.getEventPixel(browserEvent.getBrowserEvent());
    +  this.updateHTML_(this.lastMouseMovePixel_);
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @protected
    + */
    +ol.control.MousePosition.prototype.handleMouseOut = function(browserEvent) {
    +  this.updateHTML_(null);
    +  this.lastMouseMovePixel_ = null;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.control.MousePosition.prototype.setMap = function(map) {
    +  goog.base(this, 'setMap', map);
    +  if (!goog.isNull(map)) {
    +    var viewport = map.getViewport();
    +    this.listenerKeys.push(
    +        goog.events.listen(viewport, goog.events.EventType.MOUSEMOVE,
    +            this.handleMouseMove, false, this),
    +        goog.events.listen(viewport, goog.events.EventType.MOUSEOUT,
    +            this.handleMouseOut, false, this)
    +    );
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.CoordinateFormatType} format The format to render the current
    + *     position in.
    + * @observable
    + * @api stable
    + */
    +ol.control.MousePosition.prototype.setCoordinateFormat = function(format) {
    +  this.set(ol.control.MousePositionProperty.COORDINATE_FORMAT, format);
    +};
    +goog.exportProperty(
    +    ol.control.MousePosition.prototype,
    +    'setCoordinateFormat',
    +    ol.control.MousePosition.prototype.setCoordinateFormat);
    +
    +
    +/**
    + * @param {ol.proj.Projection} projection The projection to report mouse
    + *     position in.
    + * @observable
    + * @api stable
    + */
    +ol.control.MousePosition.prototype.setProjection = function(projection) {
    +  this.set(ol.control.MousePositionProperty.PROJECTION, projection);
    +};
    +goog.exportProperty(
    +    ol.control.MousePosition.prototype,
    +    'setProjection',
    +    ol.control.MousePosition.prototype.setProjection);
    +
    +
    +/**
    + * @param {?ol.Pixel} pixel Pixel.
    + * @private
    + */
    +ol.control.MousePosition.prototype.updateHTML_ = function(pixel) {
    +  var html = this.undefinedHTML_;
    +  if (!goog.isNull(pixel) && !goog.isNull(this.mapProjection_)) {
    +    if (goog.isNull(this.transform_)) {
    +      var projection = this.getProjection();
    +      if (goog.isDef(projection)) {
    +        this.transform_ = ol.proj.getTransformFromProjections(
    +            this.mapProjection_, projection);
    +      } else {
    +        this.transform_ = ol.proj.identityTransform;
    +      }
    +    }
    +    var map = this.getMap();
    +    var coordinate = map.getCoordinateFromPixel(pixel);
    +    if (!goog.isNull(coordinate)) {
    +      this.transform_(coordinate, coordinate);
    +      var coordinateFormat = this.getCoordinateFormat();
    +      if (goog.isDef(coordinateFormat)) {
    +        html = coordinateFormat(coordinate);
    +      } else {
    +        html = coordinate.toString();
    +      }
    +    }
    +  }
    +  if (!goog.isDef(this.renderedHTML_) || html != this.renderedHTML_) {
    +    this.element.innerHTML = html;
    +    this.renderedHTML_ = html;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/control/overviewmapcontrol.js b/mapbox-gl/ol/ol/control/overviewmapcontrol.js
    new file mode 100644
    index 0000000000..307c76b7bf
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/overviewmapcontrol.js
    @@ -0,0 +1,497 @@
    +goog.provide('ol.control.OverviewMap');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math.Size');
    +goog.require('goog.style');
    +goog.require('ol.Collection');
    +goog.require('ol.Map');
    +goog.require('ol.MapEventType');
    +goog.require('ol.Object');
    +goog.require('ol.Overlay');
    +goog.require('ol.OverlayPositioning');
    +goog.require('ol.View');
    +goog.require('ol.control.Control');
    +goog.require('ol.coordinate');
    +goog.require('ol.css');
    +goog.require('ol.extent');
    +
    +
    +
    +/**
    + * Create a new control with a map acting as an overview map for an other
    + * defined map.
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.OverviewMapOptions=} opt_options OverviewMap options.
    + * @api
    + */
    +ol.control.OverviewMap = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.collapsed_ = goog.isDef(options.collapsed) ? options.collapsed : true;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.collapsible_ = goog.isDef(options.collapsible) ?
    +      options.collapsible : true;
    +
    +  if (!this.collapsible_) {
    +    this.collapsed_ = false;
    +  }
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-overviewmap';
    +
    +  var tipLabel = goog.isDef(options.tipLabel) ?
    +      options.tipLabel : 'Overview map';
    +
    +  var collapseLabel = goog.isDef(options.collapseLabel) ?
    +      options.collapseLabel : '\u00AB';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.collapseLabel_ = /** @type {Node} */ (goog.isString(collapseLabel) ?
    +      goog.dom.createDom(goog.dom.TagName.SPAN, {}, collapseLabel) :
    +      collapseLabel);
    +
    +  var label = goog.isDef(options.label) ? options.label : '\u00BB';
    +
    +  /**
    +   * @private
    +   * @type {Node}
    +   */
    +  this.label_ = /** @type {Node} */ (goog.isString(label) ?
    +      goog.dom.createDom(goog.dom.TagName.SPAN, {}, label) :
    +      label);
    +
    +  var activeLabel = (this.collapsible_ && !this.collapsed_) ?
    +      this.collapseLabel_ : this.label_;
    +  var button = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'type': 'button',
    +    'title': tipLabel
    +  }, activeLabel);
    +
    +  goog.events.listen(button, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +
    +  goog.events.listen(button, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var ovmapDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'ol-overviewmap-map');
    +
    +  /**
    +   * @type {ol.Map}
    +   * @private
    +   */
    +  this.ovmap_ = new ol.Map({
    +    controls: new ol.Collection(),
    +    interactions: new ol.Collection(),
    +    target: ovmapDiv
    +  });
    +  var ovmap = this.ovmap_;
    +
    +  if (goog.isDef(options.layers)) {
    +    options.layers.forEach(
    +        /**
    +       * @param {ol.layer.Layer} layer Layer.
    +       */
    +        function(layer) {
    +          ovmap.addLayer(layer);
    +        }, this);
    +  }
    +
    +  var box = goog.dom.createDom(goog.dom.TagName.DIV, 'ol-overviewmap-box');
    +
    +  /**
    +   * @type {ol.Overlay}
    +   * @private
    +   */
    +  this.boxOverlay_ = new ol.Overlay({
    +    position: [0, 0],
    +    positioning: ol.OverlayPositioning.BOTTOM_LEFT,
    +    element: box
    +  });
    +  this.ovmap_.addOverlay(this.boxOverlay_);
    +
    +  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
    +      ol.css.CLASS_CONTROL +
    +      (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') +
    +      (this.collapsible_ ? '' : ' ol-uncollapsible');
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV,
    +      cssClasses, ovmapDiv, button);
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.OverviewMap.render;
    +
    +  goog.base(this, {
    +    element: element,
    +    render: render,
    +    target: options.target
    +  });
    +};
    +goog.inherits(ol.control.OverviewMap, ol.control.Control);
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.control.OverviewMap.prototype.setMap = function(map) {
    +  var currentMap = this.getMap();
    +
    +  if (goog.isNull(map) && !goog.isNull(currentMap)) {
    +    goog.events.unlisten(
    +        currentMap, ol.Object.getChangeEventType(ol.MapProperty.VIEW),
    +        this.handleViewChanged_, false, this);
    +  }
    +
    +  goog.base(this, 'setMap', map);
    +
    +  if (!goog.isNull(map)) {
    +
    +    // if no layers were set for the overviewmap map, then bind with
    +    // those in the main map
    +    if (this.ovmap_.getLayers().getLength() === 0) {
    +      this.ovmap_.bindTo(ol.MapProperty.LAYERGROUP, map);
    +    }
    +
    +    // bind current map view, or any new one
    +    this.bindView_();
    +
    +    goog.events.listen(
    +        map, ol.Object.getChangeEventType(ol.MapProperty.VIEW),
    +        this.handleViewChanged_, false, this);
    +
    +    this.ovmap_.updateSize();
    +    this.resetExtent_();
    +  }
    +};
    +
    +
    +/**
    + * Bind some actions to the main map view.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.bindView_ = function() {
    +  var map = this.getMap();
    +  var view = map.getView();
    +
    +  // if the map does not have a view, we can't act upon it
    +  if (goog.isNull(view)) {
    +    return;
    +  }
    +
    +  // FIXME - the overviewmap view rotation currently follows the one used
    +  // by the main map view.  We could support box rotation instead.  The choice
    +  // between the 2 modes would be made in a single option
    +  this.ovmap_.getView().bindTo(ol.ViewProperty.ROTATION, view);
    +};
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.OverviewMap}
    + * @api
    + */
    +ol.control.OverviewMap.render = function(mapEvent) {
    +  this.validateExtent_();
    +  this.updateBox_();
    +};
    +
    +
    +/**
    + * Called on main map view changed.
    + * @param {goog.events.Event} event Event.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.handleViewChanged_ = function(event) {
    +  this.bindView_();
    +};
    +
    +
    +/**
    + * Reset the overview map extent if the box size (width or
    + * height) is less than the size of the overview map size times minRatio
    + * or is greater than the size of the overview size times maxRatio.
    + *
    + * If the map extent was not reset, the box size can fits in the defined
    + * ratio sizes. This method then checks if is contained inside the overview
    + * map current extent. If not, recenter the overview map to the current
    + * main map center location.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.validateExtent_ = function() {
    +  var map = this.getMap();
    +  var ovmap = this.ovmap_;
    +
    +  if (!map.isRendered() || !ovmap.isRendered()) {
    +    return;
    +  }
    +
    +  var mapSize = map.getSize();
    +  goog.asserts.assertArray(mapSize);
    +
    +  var view = map.getView();
    +  goog.asserts.assert(goog.isDef(view));
    +  var extent = view.calculateExtent(mapSize);
    +
    +  var ovmapSize = ovmap.getSize();
    +  goog.asserts.assertArray(ovmapSize);
    +
    +  var ovview = ovmap.getView();
    +  goog.asserts.assert(goog.isDef(ovview));
    +  var ovextent = ovview.calculateExtent(ovmapSize);
    +
    +  var topLeftPixel =
    +      ovmap.getPixelFromCoordinate(ol.extent.getTopLeft(extent));
    +  var bottomRightPixel =
    +      ovmap.getPixelFromCoordinate(ol.extent.getBottomRight(extent));
    +  var boxSize = new goog.math.Size(
    +      Math.abs(topLeftPixel[0] - bottomRightPixel[0]),
    +      Math.abs(topLeftPixel[1] - bottomRightPixel[1]));
    +
    +  var ovmapWidth = ovmapSize[0];
    +  var ovmapHeight = ovmapSize[1];
    +
    +  if (boxSize.width < ovmapWidth * ol.OVERVIEWMAP_MIN_RATIO ||
    +      boxSize.height < ovmapHeight * ol.OVERVIEWMAP_MIN_RATIO ||
    +      boxSize.width > ovmapWidth * ol.OVERVIEWMAP_MAX_RATIO ||
    +      boxSize.height > ovmapHeight * ol.OVERVIEWMAP_MAX_RATIO) {
    +    this.resetExtent_();
    +  } else if (!ol.extent.containsExtent(ovextent, extent)) {
    +    this.recenter_();
    +  }
    +};
    +
    +
    +/**
    + * Reset the overview map extent to half calculated min and max ratio times
    + * the extent of the main map.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.resetExtent_ = function() {
    +  if (ol.OVERVIEWMAP_MAX_RATIO === 0 || ol.OVERVIEWMAP_MIN_RATIO === 0) {
    +    return;
    +  }
    +
    +  var map = this.getMap();
    +  var ovmap = this.ovmap_;
    +
    +  var mapSize = map.getSize();
    +  goog.asserts.assertArray(mapSize);
    +
    +  var view = map.getView();
    +  goog.asserts.assert(goog.isDef(view));
    +  var extent = view.calculateExtent(mapSize);
    +
    +  var ovmapSize = ovmap.getSize();
    +  goog.asserts.assertArray(ovmapSize);
    +
    +  var ovview = ovmap.getView();
    +  goog.asserts.assert(goog.isDef(ovview));
    +
    +  // get how many times the current map overview could hold different
    +  // box sizes using the min and max ratio, pick the step in the middle used
    +  // to calculate the extent from the main map to set it to the overview map,
    +  var steps = Math.log(
    +      ol.OVERVIEWMAP_MAX_RATIO / ol.OVERVIEWMAP_MIN_RATIO) / Math.LN2;
    +  var ratio = 1 / (Math.pow(2, steps / 2) * ol.OVERVIEWMAP_MIN_RATIO);
    +  ol.extent.scaleFromCenter(extent, ratio);
    +  ovview.fitExtent(extent, ovmapSize);
    +};
    +
    +
    +/**
    + * Set the center of the overview map to the map center without changing its
    + * resolution.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.recenter_ = function() {
    +  var map = this.getMap();
    +  var ovmap = this.ovmap_;
    +
    +  var view = map.getView();
    +  goog.asserts.assert(goog.isDef(view));
    +
    +  var ovview = ovmap.getView();
    +  goog.asserts.assert(goog.isDef(ovview));
    +
    +  ovview.setCenter(view.getCenter());
    +};
    +
    +
    +/**
    + * Update the box using the main map extent
    + * @private
    + */
    +ol.control.OverviewMap.prototype.updateBox_ = function() {
    +  var map = this.getMap();
    +  var ovmap = this.ovmap_;
    +
    +  if (!map.isRendered() || !ovmap.isRendered()) {
    +    return;
    +  }
    +
    +  var mapSize = map.getSize();
    +  goog.asserts.assertArray(mapSize);
    +
    +  var view = map.getView();
    +  goog.asserts.assert(goog.isDef(view));
    +
    +  var ovview = ovmap.getView();
    +  goog.asserts.assert(goog.isDef(ovview));
    +
    +  var ovmapSize = ovmap.getSize();
    +  goog.asserts.assertArray(ovmapSize);
    +
    +  var rotation = view.getRotation();
    +  goog.asserts.assert(goog.isDef(rotation));
    +
    +  var overlay = this.boxOverlay_;
    +  var box = this.boxOverlay_.getElement();
    +  var extent = view.calculateExtent(mapSize);
    +  var ovresolution = ovview.getResolution();
    +  var bottomLeft = ol.extent.getBottomLeft(extent);
    +  var topRight = ol.extent.getTopRight(extent);
    +
    +  // set position using bottom left coordinates
    +  var rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft);
    +  overlay.setPosition(rotateBottomLeft);
    +
    +  // set box size calculated from map extent size and overview map resolution
    +  if (goog.isDefAndNotNull(box)) {
    +    var boxWidth = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution);
    +    var boxHeight = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution);
    +    goog.style.setBorderBoxSize(box, new goog.math.Size(
    +        boxWidth, boxHeight));
    +  }
    +};
    +
    +
    +/**
    + * @param {number} rotation Target rotation.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {ol.Coordinate|undefined} Coordinate for rotation and center anchor.
    + * @private
    + */
    +ol.control.OverviewMap.prototype.calculateCoordinateRotate_ = function(
    +    rotation, coordinate) {
    +  var coordinateRotate;
    +
    +  var map = this.getMap();
    +  var view = map.getView();
    +  goog.asserts.assert(goog.isDef(view));
    +
    +  var currentCenter = view.getCenter();
    +
    +  if (goog.isDef(currentCenter)) {
    +    coordinateRotate = [
    +      coordinate[0] - currentCenter[0],
    +      coordinate[1] - currentCenter[1]
    +    ];
    +    ol.coordinate.rotate(coordinateRotate, rotation);
    +    ol.coordinate.add(coordinateRotate, currentCenter);
    +  }
    +  return coordinateRotate;
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.OverviewMap.prototype.handleClick_ = function(event) {
    +  event.preventDefault();
    +  this.handleToggle_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.OverviewMap.prototype.handleToggle_ = function() {
    +  goog.dom.classlist.toggle(this.element, 'ol-collapsed');
    +  if (this.collapsed_) {
    +    goog.dom.replaceNode(this.collapseLabel_, this.label_);
    +  } else {
    +    goog.dom.replaceNode(this.label_, this.collapseLabel_);
    +  }
    +  this.collapsed_ = !this.collapsed_;
    +
    +  // manage overview map if it had not been rendered before and control
    +  // is expanded
    +  var ovmap = this.ovmap_;
    +  if (!this.collapsed_ && !ovmap.isRendered()) {
    +    ovmap.updateSize();
    +    this.resetExtent_();
    +    goog.events.listenOnce(ovmap, ol.MapEventType.POSTRENDER,
    +        function(event) {
    +          this.updateBox_();
    +        },
    +        false, this);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} True if the widget is collapsible.
    + * @api stable
    + */
    +ol.control.OverviewMap.prototype.getCollapsible = function() {
    +  return this.collapsible_;
    +};
    +
    +
    +/**
    + * @param {boolean} collapsible True if the widget is collapsible.
    + * @api stable
    + */
    +ol.control.OverviewMap.prototype.setCollapsible = function(collapsible) {
    +  if (this.collapsible_ === collapsible) {
    +    return;
    +  }
    +  this.collapsible_ = collapsible;
    +  goog.dom.classlist.toggle(this.element, 'ol-uncollapsible');
    +  if (!collapsible && this.collapsed_) {
    +    this.handleToggle_();
    +  }
    +};
    +
    +
    +/**
    + * @param {boolean} collapsed True if the widget is collapsed.
    + * @api stable
    + */
    +ol.control.OverviewMap.prototype.setCollapsed = function(collapsed) {
    +  if (!this.collapsible_ || this.collapsed_ === collapsed) {
    +    return;
    +  }
    +  this.handleToggle_();
    +};
    +
    +
    +/**
    + * @return {boolean} True if the widget is collapsed.
    + * @api stable
    + */
    +ol.control.OverviewMap.prototype.getCollapsed = function() {
    +  return this.collapsed_;
    +};
    diff --git a/mapbox-gl/ol/ol/control/rotatecontrol.js b/mapbox-gl/ol/ol/control/rotatecontrol.js
    new file mode 100644
    index 0000000000..785380217b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/rotatecontrol.js
    @@ -0,0 +1,172 @@
    +goog.provide('ol.control.Rotate');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.classlist');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math');
    +goog.require('ol.animation');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +goog.require('ol.easing');
    +
    +
    +
    +/**
    + * @classdesc
    + * A button control to reset rotation to 0.
    + * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css
    + * selector is added to the button when the rotation is 0.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.RotateOptions=} opt_options Rotate options.
    + * @api stable
    + */
    +ol.control.Rotate = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-rotate';
    +
    +  var label = goog.isDef(options.label) ?
    +      options.label : '\u21E7';
    +
    +  /**
    +   * @type {Node}
    +   * @private
    +   */
    +  this.label_ = null;
    +
    +  if (goog.isString(label)) {
    +    this.label_ = goog.dom.createDom(goog.dom.TagName.SPAN,
    +        'ol-compass', label);
    +  } else {
    +    this.label_ = label;
    +    goog.dom.classlist.add(this.label_, 'ol-compass');
    +  }
    +
    +  var tipLabel = goog.isDef(options.tipLabel) ?
    +      options.tipLabel : 'Reset rotation';
    +
    +  var button = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'class': className + '-reset',
    +    'type' : 'button',
    +    'title': tipLabel
    +  }, this.label_);
    +
    +  goog.events.listen(button, goog.events.EventType.CLICK,
    +      ol.control.Rotate.prototype.handleClick_, false, this);
    +
    +  goog.events.listen(button, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
    +      ol.css.CLASS_CONTROL;
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, button);
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.Rotate.render;
    +
    +  goog.base(this, {
    +    element: element,
    +    render: render,
    +    target: options.target
    +  });
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 250;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.autoHide_ = goog.isDef(options.autoHide) ? options.autoHide : true;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.rotation_ = undefined;
    +
    +  if (this.autoHide_) {
    +    goog.dom.classlist.add(this.element, ol.css.CLASS_HIDDEN);
    +  }
    +
    +};
    +goog.inherits(ol.control.Rotate, ol.control.Control);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.Rotate.prototype.handleClick_ = function(event) {
    +  event.preventDefault();
    +  this.resetNorth_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.Rotate.prototype.resetNorth_ = function() {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  if (goog.isNull(view)) {
    +    // the map does not have a view, so we can't act
    +    // upon it
    +    return;
    +  }
    +  var currentRotation = view.getRotation();
    +  while (currentRotation < -Math.PI) {
    +    currentRotation += 2 * Math.PI;
    +  }
    +  while (currentRotation > Math.PI) {
    +    currentRotation -= 2 * Math.PI;
    +  }
    +  if (goog.isDef(currentRotation)) {
    +    if (this.duration_ > 0) {
    +      map.beforeRender(ol.animation.rotate({
    +        rotation: currentRotation,
    +        duration: this.duration_,
    +        easing: ol.easing.easeOut
    +      }));
    +    }
    +    view.setRotation(0);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.Rotate}
    + * @api
    + */
    +ol.control.Rotate.render = function(mapEvent) {
    +  var frameState = mapEvent.frameState;
    +  if (goog.isNull(frameState)) {
    +    return;
    +  }
    +  var rotation = frameState.viewState.rotation;
    +  if (rotation != this.rotation_) {
    +    var transform = 'rotate(' + goog.math.toDegrees(rotation) + 'deg)';
    +    if (this.autoHide_) {
    +      goog.dom.classlist.enable(
    +          this.element, ol.css.CLASS_HIDDEN, rotation === 0);
    +    }
    +    this.label_.style.msTransform = transform;
    +    this.label_.style.webkitTransform = transform;
    +    this.label_.style.transform = transform;
    +  }
    +  this.rotation_ = rotation;
    +};
    diff --git a/mapbox-gl/ol/ol/control/scalelinecontrol.js b/mapbox-gl/ol/ol/control/scalelinecontrol.js
    new file mode 100644
    index 0000000000..7efa6247f0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/scalelinecontrol.js
    @@ -0,0 +1,343 @@
    +goog.provide('ol.control.ScaleLine');
    +goog.provide('ol.control.ScaleLineProperty');
    +goog.provide('ol.control.ScaleLineUnits');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.math');
    +goog.require('goog.style');
    +goog.require('ol.Object');
    +goog.require('ol.TransformFunction');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +goog.require('ol.proj');
    +goog.require('ol.proj.METERS_PER_UNIT');
    +goog.require('ol.proj.Units');
    +goog.require('ol.sphere.NORMAL');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.control.ScaleLineProperty = {
    +  UNITS: 'units'
    +};
    +
    +
    +/**
    + * Units for the scale line. Supported values are `'degrees'`, `'imperial'`,
    + * `'nautical'`, `'metric'`, `'us'`.
    + * @enum {string}
    + * @api stable
    + */
    +ol.control.ScaleLineUnits = {
    +  DEGREES: 'degrees',
    +  IMPERIAL: 'imperial',
    +  NAUTICAL: 'nautical',
    +  METRIC: 'metric',
    +  US: 'us'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * A control displaying rough x-axis distances, calculated for the center of the
    + * viewport.
    + * No scale line will be shown when the x-axis distance cannot be calculated in
    + * the view projection (e.g. at or beyond the poles in EPSG:4326).
    + * By default the scale line will show in the bottom left portion of the map,
    + * but this can be changed by using the css selector `.ol-scale-line`.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.ScaleLineOptions=} opt_options Scale line options.
    + * @api stable
    + */
    +ol.control.ScaleLine = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-scale-line';
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.innerElement_ = goog.dom.createDom(goog.dom.TagName.DIV,
    +      className + '-inner');
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.element_ = goog.dom.createDom(goog.dom.TagName.DIV,
    +      className + ' ' + ol.css.CLASS_UNSELECTABLE, this.innerElement_);
    +
    +  /**
    +   * @private
    +   * @type {?olx.ViewState}
    +   */
    +  this.viewState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.minWidth_ = goog.isDef(options.minWidth) ? options.minWidth : 64;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.renderedWidth_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.renderedHTML_ = '';
    +
    +  /**
    +   * @private
    +   * @type {?ol.TransformFunction}
    +   */
    +  this.toEPSG4326_ = null;
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.ScaleLine.render;
    +
    +  goog.base(this, {
    +    element: this.element_,
    +    render: render,
    +    target: options.target
    +  });
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.control.ScaleLineProperty.UNITS),
    +      this.handleUnitsChanged_, false, this);
    +
    +  this.setUnits(/** @type {ol.control.ScaleLineUnits} */ (options.units) ||
    +      ol.control.ScaleLineUnits.METRIC);
    +
    +};
    +goog.inherits(ol.control.ScaleLine, ol.control.Control);
    +
    +
    +/**
    + * @const
    + * @type {Array.<number>}
    + */
    +ol.control.ScaleLine.LEADING_DIGITS = [1, 2, 5];
    +
    +
    +/**
    + * @return {ol.control.ScaleLineUnits|undefined} The units to use in the scale
    + *     line.
    + * @observable
    + * @api stable
    + */
    +ol.control.ScaleLine.prototype.getUnits = function() {
    +  return /** @type {ol.control.ScaleLineUnits|undefined} */ (
    +      this.get(ol.control.ScaleLineProperty.UNITS));
    +};
    +goog.exportProperty(
    +    ol.control.ScaleLine.prototype,
    +    'getUnits',
    +    ol.control.ScaleLine.prototype.getUnits);
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.ScaleLine}
    + * @api
    + */
    +ol.control.ScaleLine.render = function(mapEvent) {
    +  var frameState = mapEvent.frameState;
    +  if (goog.isNull(frameState)) {
    +    this.viewState_ = null;
    +  } else {
    +    this.viewState_ = frameState.viewState;
    +  }
    +  this.updateElement_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.ScaleLine.prototype.handleUnitsChanged_ = function() {
    +  this.updateElement_();
    +};
    +
    +
    +/**
    + * @param {ol.control.ScaleLineUnits} units The units to use in the scale line.
    + * @observable
    + * @api stable
    + */
    +ol.control.ScaleLine.prototype.setUnits = function(units) {
    +  this.set(ol.control.ScaleLineProperty.UNITS, units);
    +};
    +goog.exportProperty(
    +    ol.control.ScaleLine.prototype,
    +    'setUnits',
    +    ol.control.ScaleLine.prototype.setUnits);
    +
    +
    +/**
    + * @private
    + */
    +ol.control.ScaleLine.prototype.updateElement_ = function() {
    +  var viewState = this.viewState_;
    +
    +  if (goog.isNull(viewState)) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.element_, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return;
    +  }
    +
    +  var center = viewState.center;
    +  var projection = viewState.projection;
    +  var pointResolution =
    +      projection.getPointResolution(viewState.resolution, center);
    +  var projectionUnits = projection.getUnits();
    +
    +  var cosLatitude;
    +  var units = this.getUnits();
    +  if (projectionUnits == ol.proj.Units.DEGREES &&
    +      (units == ol.control.ScaleLineUnits.METRIC ||
    +       units == ol.control.ScaleLineUnits.IMPERIAL ||
    +       units == ol.control.ScaleLineUnits.US ||
    +       units == ol.control.ScaleLineUnits.NAUTICAL)) {
    +
    +    // Convert pointResolution from degrees to meters
    +    this.toEPSG4326_ = null;
    +    cosLatitude = Math.cos(goog.math.toRadians(center[1]));
    +    pointResolution *= Math.PI * cosLatitude * ol.sphere.NORMAL.radius / 180;
    +    projectionUnits = ol.proj.Units.METERS;
    +
    +  } else if (projectionUnits != ol.proj.Units.DEGREES &&
    +      units == ol.control.ScaleLineUnits.DEGREES) {
    +
    +    // Convert pointResolution from other units to degrees
    +    if (goog.isNull(this.toEPSG4326_)) {
    +      this.toEPSG4326_ = ol.proj.getTransformFromProjections(
    +          projection, ol.proj.get('EPSG:4326'));
    +    }
    +    cosLatitude = Math.cos(goog.math.toRadians(this.toEPSG4326_(center)[1]));
    +    var radius = ol.sphere.NORMAL.radius;
    +    goog.asserts.assert(goog.isDef(ol.proj.METERS_PER_UNIT[projectionUnits]));
    +    radius /= ol.proj.METERS_PER_UNIT[projectionUnits];
    +    pointResolution *= 180 / (Math.PI * cosLatitude * radius);
    +    projectionUnits = ol.proj.Units.DEGREES;
    +
    +  } else {
    +    this.toEPSG4326_ = null;
    +  }
    +
    +  goog.asserts.assert(
    +      ((units == ol.control.ScaleLineUnits.METRIC ||
    +        units == ol.control.ScaleLineUnits.IMPERIAL ||
    +        units == ol.control.ScaleLineUnits.US ||
    +        units == ol.control.ScaleLineUnits.NAUTICAL) &&
    +       projectionUnits == ol.proj.Units.METERS) ||
    +      (units == ol.control.ScaleLineUnits.DEGREES &&
    +       projectionUnits == ol.proj.Units.DEGREES));
    +
    +  var nominalCount = this.minWidth_ * pointResolution;
    +  var suffix = '';
    +  if (units == ol.control.ScaleLineUnits.DEGREES) {
    +    if (nominalCount < 1 / 60) {
    +      suffix = '\u2033'; // seconds
    +      pointResolution *= 3600;
    +    } else if (nominalCount < 1) {
    +      suffix = '\u2032'; // minutes
    +      pointResolution *= 60;
    +    } else {
    +      suffix = '\u00b0'; // degrees
    +    }
    +  } else if (units == ol.control.ScaleLineUnits.IMPERIAL) {
    +    if (nominalCount < 0.9144) {
    +      suffix = 'in';
    +      pointResolution /= 0.0254;
    +    } else if (nominalCount < 1609.344) {
    +      suffix = 'ft';
    +      pointResolution /= 0.3048;
    +    } else {
    +      suffix = 'mi';
    +      pointResolution /= 1609.344;
    +    }
    +  } else if (units == ol.control.ScaleLineUnits.NAUTICAL) {
    +    pointResolution /= 1852;
    +    suffix = 'nm';
    +  } else if (units == ol.control.ScaleLineUnits.METRIC) {
    +    if (nominalCount < 1) {
    +      suffix = 'mm';
    +      pointResolution *= 1000;
    +    } else if (nominalCount < 1000) {
    +      suffix = 'm';
    +    } else {
    +      suffix = 'km';
    +      pointResolution /= 1000;
    +    }
    +  } else if (units == ol.control.ScaleLineUnits.US) {
    +    if (nominalCount < 0.9144) {
    +      suffix = 'in';
    +      pointResolution *= 39.37;
    +    } else if (nominalCount < 1609.344) {
    +      suffix = 'ft';
    +      pointResolution /= 0.30480061;
    +    } else {
    +      suffix = 'mi';
    +      pointResolution /= 1609.3472;
    +    }
    +  } else {
    +    goog.asserts.fail();
    +  }
    +
    +  var i = 3 * Math.floor(
    +      Math.log(this.minWidth_ * pointResolution) / Math.log(10));
    +  var count, width;
    +  while (true) {
    +    count = ol.control.ScaleLine.LEADING_DIGITS[i % 3] *
    +        Math.pow(10, Math.floor(i / 3));
    +    width = Math.round(count / pointResolution);
    +    if (isNaN(width)) {
    +      goog.style.setElementShown(this.element_, false);
    +      this.renderedVisible_ = false;
    +      return;
    +    } else if (width >= this.minWidth_) {
    +      break;
    +    }
    +    ++i;
    +  }
    +
    +  var html = count + ' ' + suffix;
    +  if (this.renderedHTML_ != html) {
    +    this.innerElement_.innerHTML = html;
    +    this.renderedHTML_ = html;
    +  }
    +
    +  if (this.renderedWidth_ != width) {
    +    this.innerElement_.style.width = width + 'px';
    +    this.renderedWidth_ = width;
    +  }
    +
    +  if (!this.renderedVisible_) {
    +    goog.style.setElementShown(this.element_, true);
    +    this.renderedVisible_ = true;
    +  }
    +
    +};
    diff --git a/mapbox-gl/ol/ol/control/zoomcontrol.js b/mapbox-gl/ol/ol/control/zoomcontrol.js
    new file mode 100644
    index 0000000000..d9ec25bb8d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/zoomcontrol.js
    @@ -0,0 +1,132 @@
    +goog.provide('ol.control.Zoom');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.animation');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +goog.require('ol.easing');
    +
    +
    +
    +/**
    + * @classdesc
    + * A control with 2 buttons, one for zoom in and one for zoom out.
    + * This control is one of the default controls of a map. To style this control
    + * use css selectors `.ol-zoom-in` and `.ol-zoom-out`.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.ZoomOptions=} opt_options Zoom options.
    + * @api stable
    + */
    +ol.control.Zoom = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var className = goog.isDef(options.className) ? options.className : 'ol-zoom';
    +
    +  var delta = goog.isDef(options.delta) ? options.delta : 1;
    +
    +  var zoomInLabel = goog.isDef(options.zoomInLabel) ?
    +      options.zoomInLabel : '+';
    +  var zoomOutLabel = goog.isDef(options.zoomOutLabel) ?
    +      options.zoomOutLabel : '\u2212';
    +
    +  var zoomInTipLabel = goog.isDef(options.zoomInTipLabel) ?
    +      options.zoomInTipLabel : 'Zoom in';
    +  var zoomOutTipLabel = goog.isDef(options.zoomOutTipLabel) ?
    +      options.zoomOutTipLabel : 'Zoom out';
    +
    +  var inElement = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'class': className + '-in',
    +    'type' : 'button',
    +    'title': zoomInTipLabel
    +  }, zoomInLabel);
    +
    +  goog.events.listen(inElement,
    +      goog.events.EventType.CLICK, goog.partial(
    +          ol.control.Zoom.prototype.handleClick_, delta), false, this);
    +
    +  goog.events.listen(inElement, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var outElement = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'class': className + '-out',
    +    'type' : 'button',
    +    'title': zoomOutTipLabel
    +  }, zoomOutLabel);
    +
    +  goog.events.listen(outElement,
    +      goog.events.EventType.CLICK, goog.partial(
    +          ol.control.Zoom.prototype.handleClick_, -delta), false, this);
    +
    +  goog.events.listen(outElement, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
    +      ol.css.CLASS_CONTROL;
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, inElement,
    +      outElement);
    +
    +  goog.base(this, {
    +    element: element,
    +    target: options.target
    +  });
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 250;
    +
    +};
    +goog.inherits(ol.control.Zoom, ol.control.Control);
    +
    +
    +/**
    + * @param {number} delta Zoom delta.
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.Zoom.prototype.handleClick_ = function(delta, event) {
    +  event.preventDefault();
    +  this.zoomByDelta_(delta);
    +};
    +
    +
    +/**
    + * @param {number} delta Zoom delta.
    + * @private
    + */
    +ol.control.Zoom.prototype.zoomByDelta_ = function(delta) {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  if (goog.isNull(view)) {
    +    // the map does not have a view, so we can't act
    +    // upon it
    +    return;
    +  }
    +  var currentResolution = view.getResolution();
    +  if (goog.isDef(currentResolution)) {
    +    if (this.duration_ > 0) {
    +      map.beforeRender(ol.animation.zoom({
    +        resolution: currentResolution,
    +        duration: this.duration_,
    +        easing: ol.easing.easeOut
    +      }));
    +    }
    +    var newResolution = view.constrainResolution(currentResolution, delta);
    +    view.setResolution(newResolution);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/control/zoomslidercontrol.js b/mapbox-gl/ol/ol/control/zoomslidercontrol.js
    new file mode 100644
    index 0000000000..a2fb2162f5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/zoomslidercontrol.js
    @@ -0,0 +1,327 @@
    +// FIXME should possibly show tooltip when dragging?
    +
    +goog.provide('ol.control.ZoomSlider');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.fx.DragDropEvent');
    +goog.require('goog.fx.Dragger');
    +goog.require('goog.fx.Dragger.EventType');
    +goog.require('goog.math');
    +goog.require('goog.math.Rect');
    +goog.require('goog.style');
    +goog.require('ol');
    +goog.require('ol.Size');
    +goog.require('ol.ViewHint');
    +goog.require('ol.animation');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +goog.require('ol.easing');
    +
    +
    +
    +/**
    + * @classdesc
    + * A slider type of control for zooming.
    + *
    + * Example:
    + *
    + *     map.addControl(new ol.control.ZoomSlider());
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.ZoomSliderOptions=} opt_options Zoom slider options.
    + * @api stable
    + */
    +ol.control.ZoomSlider = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * Will hold the current resolution of the view.
    +   *
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.currentResolution_ = undefined;
    +
    +  /**
    +   * The direction of the slider. Will be determined from actual display of the
    +   * container and defaults to ol.control.ZoomSlider.direction.VERTICAL.
    +   *
    +   * @type {ol.control.ZoomSlider.direction}
    +   * @private
    +   */
    +  this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
    +
    +  /**
    +   * The calculated thumb size (border box plus margins).  Set when initSlider_
    +   * is called.
    +   * @type {ol.Size}
    +   * @private
    +   */
    +  this.thumbSize_ = null;
    +
    +  /**
    +   * Whether the slider is initialized.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.sliderInitialized_ = false;
    +
    +  var className = goog.isDef(options.className) ?
    +      options.className : 'ol-zoomslider';
    +  var thumbElement = goog.dom.createDom(goog.dom.TagName.DIV,
    +      [className + '-thumb', ol.css.CLASS_UNSELECTABLE]);
    +  var containerElement = goog.dom.createDom(goog.dom.TagName.DIV,
    +      [className, ol.css.CLASS_UNSELECTABLE, ol.css.CLASS_CONTROL],
    +      thumbElement);
    +
    +  /**
    +   * @type {goog.fx.Dragger}
    +   * @private
    +   */
    +  this.dragger_ = new goog.fx.Dragger(thumbElement);
    +  this.registerDisposable(this.dragger_);
    +
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.START,
    +      this.handleDraggerStart_, false, this);
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG,
    +      this.handleDraggerDrag_, false, this);
    +  goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END,
    +      this.handleDraggerEnd_, false, this);
    +
    +  goog.events.listen(containerElement, goog.events.EventType.CLICK,
    +      this.handleContainerClick_, false, this);
    +  goog.events.listen(thumbElement, goog.events.EventType.CLICK,
    +      goog.events.Event.stopPropagation);
    +
    +  var render = goog.isDef(options.render) ?
    +      options.render : ol.control.ZoomSlider.render;
    +
    +  goog.base(this, {
    +    element: containerElement,
    +    render: render
    +  });
    +};
    +goog.inherits(ol.control.ZoomSlider, ol.control.Control);
    +
    +
    +/**
    + * The enum for available directions.
    + *
    + * @enum {number}
    + */
    +ol.control.ZoomSlider.direction = {
    +  VERTICAL: 0,
    +  HORIZONTAL: 1
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.control.ZoomSlider.prototype.setMap = function(map) {
    +  goog.base(this, 'setMap', map);
    +  if (!goog.isNull(map)) {
    +    map.render();
    +  }
    +};
    +
    +
    +/**
    + * Initializes the slider element. This will determine and set this controls
    + * direction_ and also constrain the dragging of the thumb to always be within
    + * the bounds of the container.
    + *
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.initSlider_ = function() {
    +  var container = this.element;
    +  var containerSize = goog.style.getSize(container);
    +
    +  var thumb = goog.dom.getFirstElementChild(container);
    +  var thumbMargins = goog.style.getMarginBox(thumb);
    +  var thumbBorderBoxSize = goog.style.getBorderBoxSize(thumb);
    +  var thumbWidth = thumbBorderBoxSize.width +
    +      thumbMargins.right + thumbMargins.left;
    +  var thumbHeight = thumbBorderBoxSize.height +
    +      thumbMargins.top + thumbMargins.bottom;
    +  this.thumbSize_ = [thumbWidth, thumbHeight];
    +
    +  var width = containerSize.width - thumbWidth;
    +  var height = containerSize.height - thumbHeight;
    +
    +  var limits;
    +  if (containerSize.width > containerSize.height) {
    +    this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL;
    +    limits = new goog.math.Rect(0, 0, width, 0);
    +  } else {
    +    this.direction_ = ol.control.ZoomSlider.direction.VERTICAL;
    +    limits = new goog.math.Rect(0, 0, 0, height);
    +  }
    +  this.dragger_.setLimits(limits);
    +  this.sliderInitialized_ = true;
    +};
    +
    +
    +/**
    + * @param {ol.MapEvent} mapEvent Map event.
    + * @this {ol.control.ZoomSlider}
    + * @api
    + */
    +ol.control.ZoomSlider.render = function(mapEvent) {
    +  if (goog.isNull(mapEvent.frameState)) {
    +    return;
    +  }
    +  goog.asserts.assert(goog.isDefAndNotNull(mapEvent.frameState.viewState));
    +  if (!this.sliderInitialized_) {
    +    this.initSlider_();
    +  }
    +  var res = mapEvent.frameState.viewState.resolution;
    +  if (res !== this.currentResolution_) {
    +    this.currentResolution_ = res;
    +    this.setThumbPosition_(res);
    +  }
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent The browser event to handle.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.handleContainerClick_ = function(browserEvent) {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  var currentResolution = view.getResolution();
    +  goog.asserts.assert(goog.isDef(currentResolution));
    +  map.beforeRender(ol.animation.zoom({
    +    resolution: currentResolution,
    +    duration: ol.ZOOMSLIDER_ANIMATION_DURATION,
    +    easing: ol.easing.easeOut
    +  }));
    +  var relativePosition = this.getRelativePosition_(
    +      browserEvent.offsetX - this.thumbSize_[0] / 2,
    +      browserEvent.offsetY - this.thumbSize_[1] / 2);
    +  var resolution = this.getResolutionForPosition_(relativePosition);
    +  view.setResolution(view.constrainResolution(resolution));
    +};
    +
    +
    +/**
    + * Handle dragger start events.
    + * @param {goog.fx.DragDropEvent} event The dragdropevent.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) {
    +  this.getMap().getView().setHint(ol.ViewHint.INTERACTING, 1);
    +};
    +
    +
    +/**
    + * Handle dragger drag events.
    + *
    + * @param {goog.fx.DragDropEvent} event The dragdropevent.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) {
    +  var relativePosition = this.getRelativePosition_(event.left, event.top);
    +  this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
    +  this.getMap().getView().setResolution(this.currentResolution_);
    +};
    +
    +
    +/**
    + * Handle dragger end events.
    + * @param {goog.fx.DragDropEvent} event The dragdropevent.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.handleDraggerEnd_ = function(event) {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  view.setHint(ol.ViewHint.INTERACTING, -1);
    +  goog.asserts.assert(goog.isDef(this.currentResolution_));
    +  map.beforeRender(ol.animation.zoom({
    +    resolution: this.currentResolution_,
    +    duration: ol.ZOOMSLIDER_ANIMATION_DURATION,
    +    easing: ol.easing.easeOut
    +  }));
    +  var resolution = view.constrainResolution(this.currentResolution_);
    +  view.setResolution(resolution);
    +};
    +
    +
    +/**
    + * Positions the thumb inside its container according to the given resolution.
    + *
    + * @param {number} res The res.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.setThumbPosition_ = function(res) {
    +  var position = this.getPositionForResolution_(res);
    +  var dragger = this.dragger_;
    +  var thumb = goog.dom.getFirstElementChild(this.element);
    +
    +  if (this.direction_ == ol.control.ZoomSlider.direction.HORIZONTAL) {
    +    var left = dragger.limits.left + dragger.limits.width * position;
    +    goog.style.setPosition(thumb, left);
    +  } else {
    +    var top = dragger.limits.top + dragger.limits.height * position;
    +    goog.style.setPosition(thumb, dragger.limits.left, top);
    +  }
    +};
    +
    +
    +/**
    + * Calculates the relative position of the thumb given x and y offsets.  The
    + * relative position scales from 0 to 1.  The x and y offsets are assumed to be
    + * in pixel units within the dragger limits.
    + *
    + * @param {number} x Pixel position relative to the left of the slider.
    + * @param {number} y Pixel position relative to the top of the slider.
    + * @return {number} The relative position of the thumb.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.getRelativePosition_ = function(x, y) {
    +  var draggerLimits = this.dragger_.limits;
    +  var amount;
    +  if (this.direction_ === ol.control.ZoomSlider.direction.HORIZONTAL) {
    +    amount = (x - draggerLimits.left) / draggerLimits.width;
    +  } else {
    +    amount = (y - draggerLimits.top) / draggerLimits.height;
    +  }
    +  return goog.math.clamp(amount, 0, 1);
    +};
    +
    +
    +/**
    + * Calculates the corresponding resolution of the thumb given its relative
    + * position (where 0 is the minimum and 1 is the maximum).
    + *
    + * @param {number} position The relative position of the thumb.
    + * @return {number} The corresponding resolution.
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.getResolutionForPosition_ = function(position) {
    +  var fn = this.getMap().getView().getResolutionForValueFunction();
    +  return fn(1 - position);
    +};
    +
    +
    +/**
    + * Determines the relative position of the slider for the given resolution.  A
    + * relative position of 0 corresponds to the minimum view resolution.  A
    + * relative position of 1 corresponds to the maximum view resolution.
    + *
    + * @param {number} res The resolution.
    + * @return {number} The relative position value (between 0 and 1).
    + * @private
    + */
    +ol.control.ZoomSlider.prototype.getPositionForResolution_ = function(res) {
    +  var fn = this.getMap().getView().getValueForResolutionFunction();
    +  return 1 - fn(res);
    +};
    diff --git a/mapbox-gl/ol/ol/control/zoomtoextentcontrol.js b/mapbox-gl/ol/ol/control/zoomtoextentcontrol.js
    new file mode 100644
    index 0000000000..52053fecb2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/control/zoomtoextentcontrol.js
    @@ -0,0 +1,86 @@
    +goog.provide('ol.control.ZoomToExtent');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.control.Control');
    +goog.require('ol.css');
    +
    +
    +
    +/**
    + * @classdesc
    + * A button control which, when pressed, changes the map view to a specific
    + * extent. To style this control use the css selector `.ol-zoom-extent`.
    + *
    + * @constructor
    + * @extends {ol.control.Control}
    + * @param {olx.control.ZoomToExtentOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.control.ZoomToExtent = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @type {ol.Extent}
    +   * @private
    +   */
    +  this.extent_ = goog.isDef(options.extent) ? options.extent : null;
    +
    +  var className = goog.isDef(options.className) ? options.className :
    +      'ol-zoom-extent';
    +
    +  var label = goog.isDef(options.label) ? options.label : 'E';
    +  var tipLabel = goog.isDef(options.tipLabel) ?
    +      options.tipLabel : 'Fit to extent';
    +  var button = goog.dom.createDom(goog.dom.TagName.BUTTON, {
    +    'type': 'button',
    +    'title': tipLabel
    +  }, label);
    +
    +  goog.events.listen(button, goog.events.EventType.CLICK,
    +      this.handleClick_, false, this);
    +
    +  goog.events.listen(button, [
    +    goog.events.EventType.MOUSEOUT,
    +    goog.events.EventType.FOCUSOUT
    +  ], function() {
    +    this.blur();
    +  }, false);
    +
    +  var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
    +      ol.css.CLASS_CONTROL;
    +  var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, button);
    +
    +  goog.base(this, {
    +    element: element,
    +    target: options.target
    +  });
    +};
    +goog.inherits(ol.control.ZoomToExtent, ol.control.Control);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event The event to handle
    + * @private
    + */
    +ol.control.ZoomToExtent.prototype.handleClick_ = function(event) {
    +  event.preventDefault();
    +  this.handleZoomToExtent_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.control.ZoomToExtent.prototype.handleZoomToExtent_ = function() {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  var extent = goog.isNull(this.extent_) ?
    +      view.getProjection().getExtent() : this.extent_;
    +  var size = map.getSize();
    +  goog.asserts.assert(goog.isDef(size));
    +  view.fitExtent(extent, size);
    +};
    diff --git a/mapbox-gl/ol/ol/coordinate.js b/mapbox-gl/ol/ol/coordinate.js
    new file mode 100644
    index 0000000000..a39f354aca
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/coordinate.js
    @@ -0,0 +1,349 @@
    +goog.provide('ol.Coordinate');
    +goog.provide('ol.CoordinateFormatType');
    +goog.provide('ol.coordinate');
    +
    +goog.require('goog.math');
    +
    +
    +/**
    + * A function that takes a {@link ol.Coordinate} and transforms it into a
    + * `{string}`.
    + *
    + * @typedef {function((ol.Coordinate|undefined)): string}
    + * @api stable
    + */
    +ol.CoordinateFormatType;
    +
    +
    +/**
    + * An array of numbers representing an xy coordinate. Example: `[16, 48]`.
    + * @typedef {Array.<number>} ol.Coordinate
    + * @api stable
    + */
    +ol.Coordinate;
    +
    +
    +/**
    + * Add `delta` to `coordinate`. `coordinate` is modified in place and returned
    + * by the function.
    + *
    + * Example:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     ol.coordinate.add(coord, [-2, 4]);
    + *     // coord is now [5.85, 51.983333]
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.Coordinate} delta Delta.
    + * @return {ol.Coordinate} The input coordinate adjusted by the given delta.
    + * @api stable
    + */
    +ol.coordinate.add = function(coordinate, delta) {
    +  coordinate[0] += delta[0];
    +  coordinate[1] += delta[1];
    +  return coordinate;
    +};
    +
    +
    +/**
    + * Calculates the point closest to the passed coordinate on the passed segment.
    + * This is the foot of the perpendicular of the coordinate to the segment when
    + * the foot is on the segment, or the closest segment coordinate when the foot
    + * is outside the segment.
    + *
    + * @param {ol.Coordinate} coordinate The coordinate.
    + * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment.
    + * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to
    + *     the segment.
    + */
    +ol.coordinate.closestOnSegment = function(coordinate, segment) {
    +  var x0 = coordinate[0];
    +  var y0 = coordinate[1];
    +  var start = segment[0];
    +  var end = segment[1];
    +  var x1 = start[0];
    +  var y1 = start[1];
    +  var x2 = end[0];
    +  var y2 = end[1];
    +  var dx = x2 - x1;
    +  var dy = y2 - y1;
    +  var along = (dx === 0 && dy === 0) ? 0 :
    +      ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0);
    +  var x, y;
    +  if (along <= 0) {
    +    x = x1;
    +    y = y1;
    +  } else if (along >= 1) {
    +    x = x2;
    +    y = y2;
    +  } else {
    +    x = x1 + along * dx;
    +    y = y1 + along * dy;
    +  }
    +  return [x, y];
    +};
    +
    +
    +/**
    + * Returns a {@link ol.CoordinateFormatType} function that can be used to format
    + * a {ol.Coordinate} to a string.
    + *
    + * Example without specifying the fractional digits:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var stringifyFunc = ol.coordinate.createStringXY();
    + *     var out = stringifyFunc(coord);
    + *     // out is now '8, 48'
    + *
    + * Example with explicitly specifying 2 fractional digits:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var stringifyFunc = ol.coordinate.createStringXY(2);
    + *     var out = stringifyFunc(coord);
    + *     // out is now '7.85, 47.98'
    + *
    + * @param {number=} opt_fractionDigits The number of digits to include
    + *    after the decimal point. Default is `0`.
    + * @return {ol.CoordinateFormatType} Coordinate format.
    + * @api stable
    + */
    +ol.coordinate.createStringXY = function(opt_fractionDigits) {
    +  return (
    +      /**
    +       * @param {ol.Coordinate|undefined} coordinate Coordinate.
    +       * @return {string} String XY.
    +       */
    +      function(coordinate) {
    +        return ol.coordinate.toStringXY(coordinate, opt_fractionDigits);
    +      });
    +};
    +
    +
    +/**
    + * @private
    + * @param {number} degrees Degrees.
    + * @param {string} hemispheres Hemispheres.
    + * @return {string} String.
    + */
    +ol.coordinate.degreesToStringHDMS_ = function(degrees, hemispheres) {
    +  var normalizedDegrees = goog.math.modulo(degrees + 180, 360) - 180;
    +  var x = Math.abs(Math.round(3600 * normalizedDegrees));
    +  return Math.floor(x / 3600) + '\u00b0 ' +
    +      Math.floor((x / 60) % 60) + '\u2032 ' +
    +      Math.floor(x % 60) + '\u2033 ' +
    +      hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0);
    +};
    +
    +
    +/**
    + * Transforms the given {@link ol.Coordinate} to a string using the given string
    + * template. The strings `{x}` and `{y}` in the template will be replaced with
    + * the first and second coordinate values respectively.
    + *
    + * Example without specifying the fractional digits:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var template = 'Coordinate is ({x}|{y}).';
    + *     var out = ol.coordinate.format(coord, template);
    + *     // out is now 'Coordinate is (8|48).'
    + *
    + * Example explicitly specifying the fractional digits:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var template = 'Coordinate is ({x}|{y}).';
    + *     var out = ol.coordinate.format(coord, template, 2);
    + *     // out is now 'Coordinate is (7.85|47.98).'
    + *
    + * @param {ol.Coordinate|undefined} coordinate Coordinate.
    + * @param {string} template A template string with `{x}` and `{y}` placeholders
    + *     that will be replaced by first and second coordinate values.
    + * @param {number=} opt_fractionDigits The number of digits to include
    + *    after the decimal point. Default is `0`.
    + * @return {string} Formatted coordinate.
    + * @api stable
    + */
    +ol.coordinate.format = function(coordinate, template, opt_fractionDigits) {
    +  if (goog.isDef(coordinate)) {
    +    return template
    +      .replace('{x}', coordinate[0].toFixed(opt_fractionDigits))
    +      .replace('{y}', coordinate[1].toFixed(opt_fractionDigits));
    +  } else {
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate1 First coordinate.
    + * @param {ol.Coordinate} coordinate2 Second coordinate.
    + * @return {boolean} Whether the passed coordinates are equal.
    + */
    +ol.coordinate.equals = function(coordinate1, coordinate2) {
    +  var equals = true;
    +  for (var i = coordinate1.length - 1; i >= 0; --i) {
    +    if (coordinate1[i] != coordinate2[i]) {
    +      equals = false;
    +      break;
    +    }
    +  }
    +  return equals;
    +};
    +
    +
    +/**
    + * Rotate `coordinate` by `angle`. `coordinate` is modified in place and
    + * returned by the function.
    + *
    + * Example:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var rotateRadians = Math.PI / 2; // 90 degrees
    + *     ol.coordinate.rotate(coord, rotateRadians);
    + *     // coord is now [-47.983333, 7.85]
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} angle Angle in radian.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.coordinate.rotate = function(coordinate, angle) {
    +  var cosAngle = Math.cos(angle);
    +  var sinAngle = Math.sin(angle);
    +  var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
    +  var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
    +  coordinate[0] = x;
    +  coordinate[1] = y;
    +  return coordinate;
    +};
    +
    +
    +/**
    + * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned
    + * by the function.
    + *
    + * Example:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var scale = 1.2;
    + *     ol.coordinate.scale(coord, scale);
    + *     // coord is now [9.42, 57.5799996]
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} scale Scale factor.
    + * @return {ol.Coordinate} Coordinate.
    + */
    +ol.coordinate.scale = function(coordinate, scale) {
    +  coordinate[0] *= scale;
    +  coordinate[1] *= scale;
    +  return coordinate;
    +};
    +
    +
    +/**
    + * Subtract `delta` to `coordinate`. `coordinate` is modified in place and
    + * returned by the function.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.Coordinate} delta Delta.
    + * @return {ol.Coordinate} Coordinate.
    + */
    +ol.coordinate.sub = function(coordinate, delta) {
    +  coordinate[0] -= delta[0];
    +  coordinate[1] -= delta[1];
    +  return coordinate;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coord1 First coordinate.
    + * @param {ol.Coordinate} coord2 Second coordinate.
    + * @return {number} Squared distance between coord1 and coord2.
    + */
    +ol.coordinate.squaredDistance = function(coord1, coord2) {
    +  var dx = coord1[0] - coord2[0];
    +  var dy = coord1[1] - coord2[1];
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * Calculate the squared distance from a coordinate to a line segment.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate of the point.
    + * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates).
    + * @return {number} Squared distance from the point to the line segment.
    + */
    +ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) {
    +  return ol.coordinate.squaredDistance(coordinate,
    +      ol.coordinate.closestOnSegment(coordinate, segment));
    +};
    +
    +
    +/**
    + * Example:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var out = ol.coordinate.toStringHDMS(coord);
    + *     // out is now '47° 59′ 0″ N 7° 51′ 0″ E'
    + *
    + * @param {ol.Coordinate|undefined} coordinate Coordinate.
    + * @return {string} Hemisphere, degrees, minutes and seconds.
    + * @api stable
    + */
    +ol.coordinate.toStringHDMS = function(coordinate) {
    +  if (goog.isDef(coordinate)) {
    +    return ol.coordinate.degreesToStringHDMS_(coordinate[1], 'NS') + ' ' +
    +        ol.coordinate.degreesToStringHDMS_(coordinate[0], 'EW');
    +  } else {
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * Example without specifying fractional digits:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var out = ol.coordinate.toStringXY(coord);
    + *     // out is now '8, 48'
    + *
    + * Example explicitly specifying 1 fractional digit:
    + *
    + *     var coord = [7.85, 47.983333];
    + *     var out = ol.coordinate.toStringXY(coord, 1);
    + *     // out is now '7.8, 48.0'
    + *
    + * @param {ol.Coordinate|undefined} coordinate Coordinate.
    + * @param {number=} opt_fractionDigits The number of digits to include
    + *    after the decimal point. Default is `0`.
    + * @return {string} XY.
    + * @api stable
    + */
    +ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) {
    +  return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits);
    +};
    +
    +
    +/**
    + * Create an ol.Coordinate from an Array and take into account axis order.
    + *
    + * Examples:
    + *
    + *     var northCoord = ol.coordinate.fromProjectedArray([1, 2], 'n');
    + *     // northCoord is now [2, 1]
    + *
    + *     var eastCoord = ol.coordinate.fromProjectedArray([1, 2], 'e');
    + *     // eastCoord is now [1, 2]
    + *
    + * @param {Array} array The array with coordinates.
    + * @param {string} axis the axis info.
    + * @return {ol.Coordinate} The coordinate created.
    + */
    +ol.coordinate.fromProjectedArray = function(array, axis) {
    +  var firstAxis = axis.charAt(0);
    +  if (firstAxis === 'n' || firstAxis === 's') {
    +    return [array[1], array[0]];
    +  } else {
    +    return array;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/coordinate.jsdoc b/mapbox-gl/ol/ol/coordinate.jsdoc
    new file mode 100644
    index 0000000000..282f35ac87
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/coordinate.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.coordinate
    + */
    diff --git a/mapbox-gl/ol/ol/css.js b/mapbox-gl/ol/ol/css.js
    new file mode 100644
    index 0000000000..2d4247be14
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/css.js
    @@ -0,0 +1,37 @@
    +goog.provide('ol.css');
    +
    +
    +/**
    + * The CSS class for hidden feature.
    + *
    + * @const
    + * @type {string}
    + */
    +ol.css.CLASS_HIDDEN = 'ol-hidden';
    +
    +
    +/**
    + * The CSS class that we'll give the DOM elements to have them unselectable.
    + *
    + * @const
    + * @type {string}
    + */
    +ol.css.CLASS_UNSELECTABLE = 'ol-unselectable';
    +
    +
    +/**
    + * The CSS class for unsupported feature.
    + *
    + * @const
    + * @type {string}
    + */
    +ol.css.CLASS_UNSUPPORTED = 'ol-unsupported';
    +
    +
    +/**
    + * The CSS class for controls.
    + *
    + * @const
    + * @type {string}
    + */
    +ol.css.CLASS_CONTROL = 'ol-control';
    diff --git a/mapbox-gl/ol/ol/deviceorientation.js b/mapbox-gl/ol/ol/deviceorientation.js
    new file mode 100644
    index 0000000000..5b8b07a476
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/deviceorientation.js
    @@ -0,0 +1,250 @@
    +goog.provide('ol.DeviceOrientation');
    +goog.provide('ol.DeviceOrientationProperty');
    +
    +goog.require('goog.events');
    +goog.require('goog.math');
    +goog.require('ol.Object');
    +goog.require('ol.has');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.DeviceOrientationProperty = {
    +  ALPHA: 'alpha',
    +  BETA: 'beta',
    +  GAMMA: 'gamma',
    +  HEADING: 'heading',
    +  TRACKING: 'tracking'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * The ol.DeviceOrientation class provides access to DeviceOrientation
    + * information and events, see the [HTML 5 DeviceOrientation Specification](
    + * http://www.w3.org/TR/orientation-event/) for more details.
    + *
    + * Many new computers, and especially mobile phones
    + * and tablets, provide hardware support for device orientation. Web
    + * developers targeting mobile devices will be especially interested in this
    + * class.
    + *
    + * Device orientation data are relative to a common starting point. For mobile
    + * devices, the starting point is to lay your phone face up on a table with the
    + * top of the phone pointing north. This represents the zero state. All
    + * angles are then relative to this state. For computers, it is the same except
    + * the screen is open at 90 degrees.
    + *
    + * Device orientation is reported as three angles - `alpha`, `beta`, and
    + * `gamma` - relative to the starting position along the three planar axes X, Y
    + * and Z. The X axis runs from the left edge to the right edge through the
    + * middle of the device. Similarly, the Y axis runs from the bottom to the top
    + * of the device through the middle. The Z axis runs from the back to the front
    + * through the middle. In the starting position, the X axis points to the
    + * right, the Y axis points away from you and the Z axis points straight up
    + * from the device lying flat.
    + *
    + * The three angles representing the device orientation are relative to the
    + * three axes. `alpha` indicates how much the device has been rotated around the
    + * Z axis, which is commonly interpreted as the compass heading (see note
    + * below). `beta` indicates how much the device has been rotated around the X
    + * axis, or how much it is tilted from front to back.  `gamma` indicates how
    + * much the device has been rotated around the Y axis, or how much it is tilted
    + * from left to right.
    + *
    + * For most browsers, the `alpha` value returns the compass heading so if the
    + * device points north, it will be 0.  With Safari on iOS, the 0 value of
    + * `alpha` is calculated from when device orientation was first requested.
    + * ol.DeviceOrientation provides the `heading` property which normalizes this
    + * behavior across all browsers for you.
    + *
    + * It is important to note that the HTML 5 DeviceOrientation specification
    + * indicates that `alpha`, `beta` and `gamma` are in degrees while the
    + * equivalent properties in ol.DeviceOrientation are in radians for consistency
    + * with all other uses of angles throughout OpenLayers.
    + *
    + * @see http://www.w3.org/TR/orientation-event/
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @fires change Triggered when the device orientation changes.
    + * @param {olx.DeviceOrientationOptions=} opt_options Options.
    + * @api
    + */
    +ol.DeviceOrientation = function(opt_options) {
    +
    +  goog.base(this);
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.listenerKey_ = null;
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.DeviceOrientationProperty.TRACKING),
    +      this.handleTrackingChanged_, false, this);
    +
    +  this.setTracking(goog.isDef(options.tracking) ? options.tracking : false);
    +
    +};
    +goog.inherits(ol.DeviceOrientation, ol.Object);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.DeviceOrientation.prototype.disposeInternal = function() {
    +  this.setTracking(false);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent Event.
    + */
    +ol.DeviceOrientation.prototype.orientationChange_ = function(browserEvent) {
    +  var event = /** @type {DeviceOrientationEvent} */
    +      (browserEvent.getBrowserEvent());
    +  if (goog.isDefAndNotNull(event.alpha)) {
    +    var alpha = goog.math.toRadians(event.alpha);
    +    this.set(ol.DeviceOrientationProperty.ALPHA, alpha);
    +    // event.absolute is undefined in iOS.
    +    if (goog.isBoolean(event.absolute) && event.absolute) {
    +      this.set(ol.DeviceOrientationProperty.HEADING, alpha);
    +    } else if (goog.isDefAndNotNull(event.webkitCompassHeading) &&
    +               goog.isDefAndNotNull(event.webkitCompassAccuracy) &&
    +               event.webkitCompassAccuracy != -1) {
    +      var heading = goog.math.toRadians(event.webkitCompassHeading);
    +      this.set(ol.DeviceOrientationProperty.HEADING, heading);
    +    }
    +  }
    +  if (goog.isDefAndNotNull(event.beta)) {
    +    this.set(ol.DeviceOrientationProperty.BETA,
    +        goog.math.toRadians(event.beta));
    +  }
    +  if (goog.isDefAndNotNull(event.gamma)) {
    +    this.set(ol.DeviceOrientationProperty.GAMMA,
    +        goog.math.toRadians(event.gamma));
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * @return {number|undefined} The euler angle in radians of the device from the
    + *     standard Z axis.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.getAlpha = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.DeviceOrientationProperty.ALPHA));
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'getAlpha',
    +    ol.DeviceOrientation.prototype.getAlpha);
    +
    +
    +/**
    + * @return {number|undefined} The euler angle in radians of the device from the
    + *     planar X axis.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.getBeta = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.DeviceOrientationProperty.BETA));
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'getBeta',
    +    ol.DeviceOrientation.prototype.getBeta);
    +
    +
    +/**
    + * @return {number|undefined} The euler angle in radians of the device from the
    + *     planar Y axis.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.getGamma = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.DeviceOrientationProperty.GAMMA));
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'getGamma',
    +    ol.DeviceOrientation.prototype.getGamma);
    +
    +
    +/**
    + * @return {number|undefined} The heading of the device relative to north, in
    + *     radians, normalizing for different browser behavior.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.getHeading = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.DeviceOrientationProperty.HEADING));
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'getHeading',
    +    ol.DeviceOrientation.prototype.getHeading);
    +
    +
    +/**
    + * Are we tracking the device's orientation?
    + * @return {boolean} The status of tracking changes to alpha, beta and gamma.
    + *     If true, changes are tracked and reported immediately.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.getTracking = function() {
    +  return /** @type {boolean} */ (
    +      this.get(ol.DeviceOrientationProperty.TRACKING));
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'getTracking',
    +    ol.DeviceOrientation.prototype.getTracking);
    +
    +
    +/**
    + * @private
    + */
    +ol.DeviceOrientation.prototype.handleTrackingChanged_ = function() {
    +  if (ol.has.DEVICE_ORIENTATION) {
    +    var tracking = this.getTracking();
    +    if (tracking && goog.isNull(this.listenerKey_)) {
    +      this.listenerKey_ = goog.events.listen(goog.global, 'deviceorientation',
    +          this.orientationChange_, false, this);
    +    } else if (!tracking && !goog.isNull(this.listenerKey_)) {
    +      goog.events.unlistenByKey(this.listenerKey_);
    +      this.listenerKey_ = null;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Enable or disable tracking of DeviceOrientation events.
    + * @param {boolean} tracking The status of tracking changes to alpha, beta and
    + *     gamma. If true, changes are tracked and reported immediately.
    + * @observable
    + * @api
    + */
    +ol.DeviceOrientation.prototype.setTracking = function(tracking) {
    +  this.set(ol.DeviceOrientationProperty.TRACKING, tracking);
    +};
    +goog.exportProperty(
    +    ol.DeviceOrientation.prototype,
    +    'setTracking',
    +    ol.DeviceOrientation.prototype.setTracking);
    diff --git a/mapbox-gl/ol/ol/dom/dom.js b/mapbox-gl/ol/ol/dom/dom.js
    new file mode 100644
    index 0000000000..4e6da97f8d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/dom/dom.js
    @@ -0,0 +1,332 @@
    +// FIXME add tests for browser features (Modernizr?)
    +
    +goog.provide('ol.dom');
    +goog.provide('ol.dom.BrowserFeature');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.userAgent');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol');
    +
    +
    +/**
    + * Create an html canvas element and returns its 2d context.
    + * @param {number=} opt_width Canvas width.
    + * @param {number=} opt_height Canvas height.
    + * @return {CanvasRenderingContext2D}
    + */
    +ol.dom.createCanvasContext2D = function(opt_width, opt_height) {
    +  var canvas = goog.dom.createElement(goog.dom.TagName.CANVAS);
    +  if (goog.isDef(opt_width)) {
    +    canvas.width = opt_width;
    +  }
    +  if (goog.isDef(opt_height)) {
    +    canvas.height = opt_height;
    +  }
    +  return canvas.getContext('2d');
    +};
    +
    +
    +/**
    + * @enum {boolean}
    + */
    +ol.dom.BrowserFeature = {
    +  USE_MS_MATRIX_TRANSFORM: ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE,
    +  USE_MS_ALPHA_FILTER: ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE
    +};
    +
    +
    +/**
    + * Detect 2d transform.
    + * Adapted from http://stackoverflow.com/q/5661671/130442
    + * http://caniuse.com/#feat=transforms2d
    + * @return {boolean}
    + */
    +ol.dom.canUseCssTransform = (function() {
    +  var canUseCssTransform;
    +  return function() {
    +    if (!goog.isDef(canUseCssTransform)) {
    +      goog.asserts.assert(!goog.isNull(document.body));
    +      if (!goog.global.getComputedStyle) {
    +        // this browser is ancient
    +        canUseCssTransform = false;
    +      } else {
    +        var el = goog.dom.createElement(goog.dom.TagName.P),
    +            has2d,
    +            transforms = {
    +              'webkitTransform': '-webkit-transform',
    +              'OTransform': '-o-transform',
    +              'msTransform': '-ms-transform',
    +              'MozTransform': '-moz-transform',
    +              'transform': 'transform'
    +            };
    +        goog.dom.appendChild(document.body, el);
    +        for (var t in transforms) {
    +          if (t in el.style) {
    +            el.style[t] = 'translate(1px,1px)';
    +            has2d = goog.global.getComputedStyle(el).getPropertyValue(
    +                transforms[t]);
    +          }
    +        }
    +        goog.dom.removeNode(el);
    +
    +        canUseCssTransform = (has2d && has2d !== 'none');
    +      }
    +    }
    +    return canUseCssTransform;
    +  };
    +}());
    +
    +
    +/**
    + * Detect 3d transform.
    + * Adapted from http://stackoverflow.com/q/5661671/130442
    + * http://caniuse.com/#feat=transforms3d
    + * @return {boolean}
    + */
    +ol.dom.canUseCssTransform3D = (function() {
    +  var canUseCssTransform3D;
    +  return function() {
    +    if (!goog.isDef(canUseCssTransform3D)) {
    +      goog.asserts.assert(!goog.isNull(document.body));
    +      if (!goog.global.getComputedStyle) {
    +        // this browser is ancient
    +        canUseCssTransform3D = false;
    +      } else {
    +        var el = goog.dom.createElement(goog.dom.TagName.P),
    +            has3d,
    +            transforms = {
    +              'webkitTransform': '-webkit-transform',
    +              'OTransform': '-o-transform',
    +              'msTransform': '-ms-transform',
    +              'MozTransform': '-moz-transform',
    +              'transform': 'transform'
    +            };
    +        goog.dom.appendChild(document.body, el);
    +        for (var t in transforms) {
    +          if (t in el.style) {
    +            el.style[t] = 'translate3d(1px,1px,1px)';
    +            has3d = goog.global.getComputedStyle(el).getPropertyValue(
    +                transforms[t]);
    +          }
    +        }
    +        goog.dom.removeNode(el);
    +
    +        canUseCssTransform3D = (has3d && has3d !== 'none');
    +      }
    +    }
    +    return canUseCssTransform3D;
    +  };
    +}());
    +
    +
    +/**
    + * @param {Element} element Element.
    + * @param {string} value Value.
    + */
    +ol.dom.setTransform = function(element, value) {
    +  var style = element.style;
    +  style.WebkitTransform = value;
    +  style.MozTransform = value;
    +  style.OTransform = value;
    +  style.msTransform = value;
    +  style.transform = value;
    +
    +  // IE 9+ seems to assume transform-origin: 100% 100%; for some unknown reason
    +  if (goog.userAgent.IE && !ol.IS_LEGACY_IE) {
    +    element.style.transformOrigin = '0 0';
    +  }
    +};
    +
    +
    +/**
    + * Sets the opacity of an element, in an IE-compatible way
    + * @param {!Element} element Element
    + * @param {number} value Opacity, [0..1]
    + */
    +ol.dom.setOpacity = function(element, value) {
    +  if (ol.dom.BrowserFeature.USE_MS_ALPHA_FILTER) {
    +    /** @type {string} */
    +    var filter = element.currentStyle.filter;
    +
    +    /** @type {RegExp} */
    +    var regex;
    +
    +    /** @type {string} */
    +    var alpha;
    +
    +    if (goog.userAgent.VERSION == '8.0') {
    +      regex = /progid:DXImageTransform\.Microsoft\.Alpha\(.*?\)/i;
    +      alpha = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' +
    +          (value * 100) + ')';
    +    } else {
    +      regex = /alpha\(.*?\)/i;
    +      alpha = 'alpha(opacity=' + (value * 100) + ')';
    +    }
    +
    +    var newFilter = filter.replace(regex, alpha);
    +    if (newFilter === filter) {
    +      // no replace was made? just append the new alpha filter instead
    +      newFilter += ' ' + alpha;
    +    }
    +
    +    element.style.filter = newFilter;
    +
    +    // Fix to apply filter to absolutely-positioned children element
    +    if (element.currentStyle.zIndex === 'auto') {
    +      element.style.zIndex = 0;
    +    }
    +  } else {
    +    element.style.opacity = value;
    +  }
    +};
    +
    +
    +/**
    + * Sets the IE matrix transform without replacing other filters
    + * @private
    + * @param {!Element} element Element
    + * @param {string} value The new progid string
    + */
    +ol.dom.setIEMatrix_ = function(element, value) {
    +  var filter = element.currentStyle.filter;
    +  var newFilter =
    +      filter.replace(/progid:DXImageTransform.Microsoft.Matrix\(.*?\)/i, value);
    +
    +  if (newFilter === filter) {
    +    newFilter = ' ' + value;
    +  }
    +
    +  element.style.filter = newFilter;
    +
    +  // Fix to apply filter to absolutely-positioned children element
    +  if (element.currentStyle.zIndex === 'auto') {
    +    element.style.zIndex = 0;
    +  }
    +};
    +
    +
    +/**
    + * @param {!Element} element Element.
    + * @param {goog.vec.Mat4.Number} transform Matrix.
    + * @param {number=} opt_precision Precision.
    + * @param {Element=} opt_translationElement Required for IE7-8
    + */
    +ol.dom.transformElement2D =
    +    function(element, transform, opt_precision, opt_translationElement) {
    +  // using matrix() causes gaps in Chrome and Firefox on Mac OS X, so prefer
    +  // matrix3d()
    +  var i;
    +  if (ol.dom.canUseCssTransform3D()) {
    +    var value3D;
    +
    +    if (goog.isDef(opt_precision)) {
    +      /** @type {Array.<string>} */
    +      var strings3D = new Array(16);
    +      for (i = 0; i < 16; ++i) {
    +        strings3D[i] = transform[i].toFixed(opt_precision);
    +      }
    +      value3D = strings3D.join(',');
    +    } else {
    +      value3D = transform.join(',');
    +    }
    +    ol.dom.setTransform(element, 'matrix3d(' + value3D + ')');
    +  } else if (ol.dom.canUseCssTransform()) {
    +    /** @type {Array.<number>} */
    +    var transform2D = [
    +      goog.vec.Mat4.getElement(transform, 0, 0),
    +      goog.vec.Mat4.getElement(transform, 1, 0),
    +      goog.vec.Mat4.getElement(transform, 0, 1),
    +      goog.vec.Mat4.getElement(transform, 1, 1),
    +      goog.vec.Mat4.getElement(transform, 0, 3),
    +      goog.vec.Mat4.getElement(transform, 1, 3)
    +    ];
    +    var value2D;
    +    if (goog.isDef(opt_precision)) {
    +      /** @type {Array.<string>} */
    +      var strings2D = new Array(6);
    +      for (i = 0; i < 6; ++i) {
    +        strings2D[i] = transform2D[i].toFixed(opt_precision);
    +      }
    +      value2D = strings2D.join(',');
    +    } else {
    +      value2D = transform2D.join(',');
    +    }
    +    ol.dom.setTransform(element, 'matrix(' + value2D + ')');
    +  } else if (ol.dom.BrowserFeature.USE_MS_MATRIX_TRANSFORM) {
    +    var m11 = goog.vec.Mat4.getElement(transform, 0, 0),
    +        m12 = goog.vec.Mat4.getElement(transform, 0, 1),
    +        m21 = goog.vec.Mat4.getElement(transform, 1, 0),
    +        m22 = goog.vec.Mat4.getElement(transform, 1, 1),
    +        dx = goog.vec.Mat4.getElement(transform, 0, 3),
    +        dy = goog.vec.Mat4.getElement(transform, 1, 3);
    +
    +    // See: http://msdn.microsoft.com/en-us/library/ms533014(v=vs.85).aspx
    +    // and: http://extremelysatisfactorytotalitarianism.com/blog/?p=1002
    +    // @TODO: fix terrible IE bbox rotation issue.
    +    var s = 'progid:DXImageTransform.Microsoft.Matrix(';
    +    s += 'sizingMethod="auto expand"';
    +    s += ',M11=' + m11.toFixed(opt_precision || 20);
    +    s += ',M12=' + m12.toFixed(opt_precision || 20);
    +    s += ',M21=' + m21.toFixed(opt_precision || 20);
    +    s += ',M22=' + m22.toFixed(opt_precision || 20);
    +    s += ')';
    +    ol.dom.setIEMatrix_(element, s);
    +
    +    // scale = m11 = m22 = target resolution [m/px] / current res [m/px]
    +    // dx = (viewport width [px] / 2) * scale
    +    //      + (layer.x [m] - view.x [m]) / target resolution [m / px]
    +    // except that we're positioning the child element relative to the
    +    // viewport, not the map.
    +    // dividing by the scale factor isn't the exact correction, but it's
    +    // close enough that you can barely tell unless you're looking for it
    +    dx /= m11;
    +    dy /= m22;
    +
    +    opt_translationElement.style.left = Math.round(dx) + 'px';
    +    opt_translationElement.style.top = Math.round(dy) + 'px';
    +  } else {
    +    element.style.left =
    +        Math.round(goog.vec.Mat4.getElement(transform, 0, 3)) + 'px';
    +    element.style.top =
    +        Math.round(goog.vec.Mat4.getElement(transform, 1, 3)) + 'px';
    +
    +    // TODO: Add scaling here. This isn't quite as simple as multiplying
    +    // width/height, because that only changes the container size, not the
    +    // content size.
    +  }
    +};
    +
    +
    +/**
    + * Get the current computed width for the given element including margin,
    + * padding and border.
    + * Equivalent to jQuery's `$(el).outerWidth(true)`.
    + * @param {!Element} element Element.
    + * @return {number}
    + */
    +ol.dom.outerWidth = function(element) {
    +  var width = element.offsetWidth;
    +  var style = element.currentStyle || window.getComputedStyle(element);
    +  width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10);
    +
    +  return width;
    +};
    +
    +
    +/**
    + * Get the current computed height for the given element including margin,
    + * padding and border.
    + * Equivalent to jQuery's `$(el).outerHeight(true)`.
    + * @param {!Element} element Element.
    + * @return {number}
    + */
    +ol.dom.outerHeight = function(element) {
    +  var height = element.offsetHeight;
    +  var style = element.currentStyle || window.getComputedStyle(element);
    +  height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10);
    +
    +  return height;
    +};
    diff --git a/mapbox-gl/ol/ol/dom/input.js b/mapbox-gl/ol/ol/dom/input.js
    new file mode 100644
    index 0000000000..bb3a61fbb8
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/dom/input.js
    @@ -0,0 +1,148 @@
    +goog.provide('ol.dom.Input');
    +goog.provide('ol.dom.InputProperty');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.Object');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.dom.InputProperty = {
    +  VALUE: 'value',
    +  CHECKED: 'checked'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Helper class for binding HTML input to an {@link ol.Object}.
    + *
    + * Example:
    + *
    + *     // bind a checkbox with id 'visible' to a layer's visibility
    + *     var visible = new ol.dom.Input(document.getElementById('visible'));
    + *     visible.bindTo('checked', layer, 'visible');
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @param {Element} target Target element.
    + * @api
    + */
    +ol.dom.Input = function(target) {
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {HTMLInputElement}
    +   */
    +  this.target_ = /** @type {HTMLInputElement} */ (target);
    +
    +  goog.events.listen(this.target_,
    +      [goog.events.EventType.CHANGE, goog.events.EventType.INPUT],
    +      this.handleInputChanged_, false, this);
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.dom.InputProperty.VALUE),
    +      this.handleValueChanged_, false, this);
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.dom.InputProperty.CHECKED),
    +      this.handleCheckedChanged_, false, this);
    +};
    +goog.inherits(ol.dom.Input, ol.Object);
    +
    +
    +/**
    + * If the input is a checkbox, return whether or not the checkbox is checked.
    + * @return {boolean|undefined} The checked state of the Input.
    + * @observable
    + * @api
    + */
    +ol.dom.Input.prototype.getChecked = function() {
    +  return /** @type {boolean} */ (this.get(ol.dom.InputProperty.CHECKED));
    +};
    +goog.exportProperty(
    +    ol.dom.Input.prototype,
    +    'getChecked',
    +    ol.dom.Input.prototype.getChecked);
    +
    +
    +/**
    + * Get the value of the input.
    + * @return {string|undefined} The value of the Input.
    + * @observable
    + * @api
    + */
    +ol.dom.Input.prototype.getValue = function() {
    +  return /** @type {string} */ (this.get(ol.dom.InputProperty.VALUE));
    +};
    +goog.exportProperty(
    +    ol.dom.Input.prototype,
    +    'getValue',
    +    ol.dom.Input.prototype.getValue);
    +
    +
    +/**
    + * Sets the value of the input.
    + * @param {string} value The value of the Input.
    + * @observable
    + * @api
    + */
    +ol.dom.Input.prototype.setValue = function(value) {
    +  this.set(ol.dom.InputProperty.VALUE, value);
    +};
    +goog.exportProperty(
    +    ol.dom.Input.prototype,
    +    'setValue',
    +    ol.dom.Input.prototype.setValue);
    +
    +
    +/**
    + * Set whether or not a checkbox is checked.
    + * @param {boolean} checked The checked state of the Input.
    + * @observable
    + * @api
    + */
    +ol.dom.Input.prototype.setChecked = function(checked) {
    +  this.set(ol.dom.InputProperty.CHECKED, checked);
    +};
    +goog.exportProperty(
    +    ol.dom.Input.prototype,
    +    'setChecked',
    +    ol.dom.Input.prototype.setChecked);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @private
    + */
    +ol.dom.Input.prototype.handleInputChanged_ = function(browserEvent) {
    +  goog.asserts.assert(browserEvent.currentTarget == this.target_);
    +  var target = this.target_;
    +  if (target.type === 'checkbox' || target.type === 'radio') {
    +    this.setChecked(target.checked);
    +  } else {
    +    this.setValue(target.value);
    +  }
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Change event.
    + * @private
    + */
    +ol.dom.Input.prototype.handleCheckedChanged_ = function(event) {
    +  this.target_.checked = /** @type {boolean} */ (this.getChecked());
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Change event.
    + * @private
    + */
    +ol.dom.Input.prototype.handleValueChanged_ = function(event) {
    +  this.target_.value =  /** @type {string} */ (this.getValue());
    +};
    diff --git a/mapbox-gl/ol/ol/easing.js b/mapbox-gl/ol/ol/easing.js
    new file mode 100644
    index 0000000000..328bffe051
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/easing.js
    @@ -0,0 +1,54 @@
    +goog.provide('ol.easing');
    +
    +goog.require('goog.fx.easing');
    +
    +
    +/**
    + * @function
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + * @api
    + */
    +ol.easing.easeIn = goog.fx.easing.easeIn;
    +
    +
    +/**
    + * @function
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + * @api
    + */
    +ol.easing.easeOut = goog.fx.easing.easeOut;
    +
    +
    +/**
    + * @function
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + * @api
    + */
    +ol.easing.inAndOut = goog.fx.easing.inAndOut;
    +
    +
    +/**
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + * @api
    + */
    +ol.easing.linear = function(t) {
    +  return t;
    +};
    +
    +
    +/**
    + * @param {number} t Input between 0 and 1.
    + * @return {number} Output between 0 and 1.
    + * @api
    + */
    +ol.easing.upAndDown = function(t) {
    +  if (t < 0.5) {
    +    return ol.easing.inAndOut(2 * t);
    +  } else {
    +    return 1 - ol.easing.inAndOut(2 * (t - 0.5));
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/easing.jsdoc b/mapbox-gl/ol/ol/easing.jsdoc
    new file mode 100644
    index 0000000000..2d63bec167
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/easing.jsdoc
    @@ -0,0 +1,4 @@
    +/**
    + * Easing functions for {@link ol.animation}.
    + * @namespace ol.easing
    + */
    diff --git a/mapbox-gl/ol/ol/ellipsoid/ellipsoid.js b/mapbox-gl/ol/ol/ellipsoid/ellipsoid.js
    new file mode 100644
    index 0000000000..88ef01335f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/ellipsoid/ellipsoid.js
    @@ -0,0 +1,185 @@
    +goog.provide('ol.Ellipsoid');
    +
    +goog.require('goog.math');
    +goog.require('ol.Coordinate');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {number} a Major radius.
    + * @param {number} flattening Flattening.
    + */
    +ol.Ellipsoid = function(a, flattening) {
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.a = a;
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.flattening = flattening;
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.b = this.a * (1 - this.flattening);
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.eSquared = 2 * flattening - flattening * flattening;
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.e = Math.sqrt(this.eSquared);
    +
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 1.
    + * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
    + * @param {number=} opt_maxIterations Maximum iterations.
    + * @return {{distance: number, initialBearing: number, finalBearing: number}}
    + *     Vincenty.
    + */
    +ol.Ellipsoid.prototype.vincenty =
    +    function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
    +  var minDeltaLambda = goog.isDef(opt_minDeltaLambda) ?
    +      opt_minDeltaLambda : 1e-12;
    +  var maxIterations = goog.isDef(opt_maxIterations) ?
    +      opt_maxIterations : 100;
    +  var f = this.flattening;
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var deltaLon = goog.math.toRadians(c2[0] - c1[0]);
    +  var U1 = Math.atan((1 - f) * Math.tan(lat1));
    +  var cosU1 = Math.cos(U1);
    +  var sinU1 = Math.sin(U1);
    +  var U2 = Math.atan((1 - f) * Math.tan(lat2));
    +  var cosU2 = Math.cos(U2);
    +  var sinU2 = Math.sin(U2);
    +  var lambda = deltaLon;
    +  var cosSquaredAlpha, sinAlpha;
    +  var cosLambda, deltaLambda = Infinity, sinLambda;
    +  var cos2SigmaM, cosSigma, sigma, sinSigma;
    +  var i;
    +  for (i = maxIterations; i > 0; --i) {
    +    cosLambda = Math.cos(lambda);
    +    sinLambda = Math.sin(lambda);
    +    var x = cosU2 * sinLambda;
    +    var y = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
    +    sinSigma = Math.sqrt(x * x + y * y);
    +    if (sinSigma === 0) {
    +      return {
    +        distance: 0,
    +        initialBearing: 0,
    +        finalBearing: 0
    +      };
    +    }
    +    cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
    +    sigma = Math.atan2(sinSigma, cosSigma);
    +    sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
    +    cosSquaredAlpha = 1 - sinAlpha * sinAlpha;
    +    cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSquaredAlpha;
    +    if (isNaN(cos2SigmaM)) {
    +      cos2SigmaM = 0;
    +    }
    +    var C = f / 16 * cosSquaredAlpha * (4 + f * (4 - 3 * cosSquaredAlpha));
    +    var lambdaPrime = deltaLon + (1 - C) * f * sinAlpha * (sigma +
    +        C * sinSigma * (cos2SigmaM +
    +        C * cosSigma * (2 * cos2SigmaM * cos2SigmaM - 1)));
    +    deltaLambda = Math.abs(lambdaPrime - lambda);
    +    lambda = lambdaPrime;
    +    if (deltaLambda < minDeltaLambda) {
    +      break;
    +    }
    +  }
    +  if (i === 0) {
    +    return {
    +      distance: NaN,
    +      finalBearing: NaN,
    +      initialBearing: NaN
    +    };
    +  }
    +  var aSquared = this.a * this.a;
    +  var bSquared = this.b * this.b;
    +  var uSquared = cosSquaredAlpha * (aSquared - bSquared) / bSquared;
    +  var A = 1 + uSquared / 16384 *
    +      (4096 + uSquared * (uSquared * (320 - 175 * uSquared) - 768));
    +  var B = uSquared / 1024 *
    +      (256 + uSquared * (uSquared * (74 - 47 * uSquared) - 128));
    +  var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 *
    +      (cosSigma * (2 * cos2SigmaM * cos2SigmaM - 1) -
    +       B / 6 * cos2SigmaM * (4 * sinSigma * sinSigma - 3) *
    +       (4 * cos2SigmaM * cos2SigmaM - 3)));
    +  cosLambda = Math.cos(lambda);
    +  sinLambda = Math.sin(lambda);
    +  var alpha1 = Math.atan2(cosU2 * sinLambda,
    +                          cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
    +  var alpha2 = Math.atan2(cosU1 * sinLambda,
    +                          cosU1 * sinU2 * cosLambda - sinU1 * cosU2);
    +  return {
    +    distance: this.b * A * (sigma - deltaSigma),
    +    initialBearing: goog.math.toDegrees(alpha1),
    +    finalBearing: goog.math.toDegrees(alpha2)
    +  };
    +};
    +
    +
    +/**
    + * Returns the distance from c1 to c2 using Vincenty.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 1.
    + * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
    + * @param {number=} opt_maxIterations Maximum iterations.
    + * @return {number} Vincenty distance.
    + */
    +ol.Ellipsoid.prototype.vincentyDistance =
    +    function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
    +  var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
    +  return vincenty.distance;
    +};
    +
    +
    +/**
    + * Returns the final bearing from c1 to c2 using Vincenty.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 1.
    + * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
    + * @param {number=} opt_maxIterations Maximum iterations.
    + * @return {number} Initial bearing.
    + */
    +ol.Ellipsoid.prototype.vincentyFinalBearing =
    +    function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
    +  var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
    +  return vincenty.finalBearing;
    +};
    +
    +
    +/**
    + * Returns the initial bearing from c1 to c2 using Vincenty.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 1.
    + * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
    + * @param {number=} opt_maxIterations Maximum iterations.
    + * @return {number} Initial bearing.
    + */
    +ol.Ellipsoid.prototype.vincentyInitialBearing =
    +    function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
    +  var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
    +  return vincenty.initialBearing;
    +};
    diff --git a/mapbox-gl/ol/ol/ellipsoid/wgs84ellipsoid.js b/mapbox-gl/ol/ol/ellipsoid/wgs84ellipsoid.js
    new file mode 100644
    index 0000000000..e5244b80d7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/ellipsoid/wgs84ellipsoid.js
    @@ -0,0 +1,10 @@
    +goog.provide('ol.ellipsoid.WGS84');
    +
    +goog.require('ol.Ellipsoid');
    +
    +
    +/**
    + * @const
    + * @type {ol.Ellipsoid}
    + */
    +ol.ellipsoid.WGS84 = new ol.Ellipsoid(6378137, 1 / 298.257223563);
    diff --git a/mapbox-gl/ol/ol/events/condition.js b/mapbox-gl/ol/ol/events/condition.js
    new file mode 100644
    index 0000000000..586b0f4be1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/events/condition.js
    @@ -0,0 +1,168 @@
    +goog.provide('ol.events.ConditionType');
    +goog.provide('ol.events.condition');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.functions');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.MapBrowserPointerEvent');
    +
    +
    +/**
    + * A function that takes an {@link ol.MapBrowserEvent} and returns a
    + * `{boolean}`. If the condition is met, true should be returned.
    + *
    + * @typedef {function(ol.MapBrowserEvent): boolean}
    + * @api stable
    + */
    +ol.events.ConditionType;
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if only the alt key is pressed.
    + * @api stable
    + */
    +ol.events.condition.altKeyOnly = function(mapBrowserEvent) {
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  return (
    +      browserEvent.altKey &&
    +      !browserEvent.platformModifierKey &&
    +      !browserEvent.shiftKey);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if only the alt and shift keys are pressed.
    + * @api stable
    + */
    +ol.events.condition.altShiftKeysOnly = function(mapBrowserEvent) {
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  return (
    +      browserEvent.altKey &&
    +      !browserEvent.platformModifierKey &&
    +      browserEvent.shiftKey);
    +};
    +
    +
    +/**
    + * Always true.
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True.
    + * @function
    + * @api stable
    + */
    +ol.events.condition.always = goog.functions.TRUE;
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if the event is a map `click` event.
    + * @api stable
    + */
    +ol.events.condition.click = function(mapBrowserEvent) {
    +  return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.CLICK;
    +};
    +
    +
    +/**
    + * Always false.
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} False.
    + * @function
    + * @api stable
    + */
    +ol.events.condition.never = goog.functions.FALSE;
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if the browser event is a `pointermove` event.
    + * @api
    + */
    +ol.events.condition.pointerMove = function(mapBrowserEvent) {
    +  return mapBrowserEvent.type == 'pointermove';
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if the event is a map `singleclick` event.
    + * @api stable
    + */
    +ol.events.condition.singleClick = function(mapBrowserEvent) {
    +  return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True only if there no modifier keys are pressed.
    + * @api stable
    + */
    +ol.events.condition.noModifierKeys = function(mapBrowserEvent) {
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  return (
    +      !browserEvent.altKey &&
    +      !browserEvent.platformModifierKey &&
    +      !browserEvent.shiftKey);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if only the platform modifier key is pressed.
    + * @api stable
    + */
    +ol.events.condition.platformModifierKeyOnly = function(mapBrowserEvent) {
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  return (
    +      !browserEvent.altKey &&
    +      browserEvent.platformModifierKey &&
    +      !browserEvent.shiftKey);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if only the shift key is pressed.
    + * @api stable
    + */
    +ol.events.condition.shiftKeyOnly = function(mapBrowserEvent) {
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  return (
    +      !browserEvent.altKey &&
    +      !browserEvent.platformModifierKey &&
    +      browserEvent.shiftKey);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True only if the target element is not editable.
    + * @api
    + */
    +ol.events.condition.targetNotEditable = function(mapBrowserEvent) {
    +  var target = mapBrowserEvent.browserEvent.target;
    +  goog.asserts.assertInstanceof(target, Element);
    +  var tagName = target.tagName;
    +  return (
    +      tagName !== goog.dom.TagName.INPUT &&
    +      tagName !== goog.dom.TagName.SELECT &&
    +      tagName !== goog.dom.TagName.TEXTAREA);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} True if the event originates from a mouse device.
    + * @api stable
    + */
    +ol.events.condition.mouseOnly = function(mapBrowserEvent) {
    +  goog.asserts.assertInstanceof(mapBrowserEvent, ol.MapBrowserPointerEvent);
    +  /* pointerId must be 1 for mouse devices,
    +   * see: http://www.w3.org/Submission/pointer-events/#pointerevent-interface
    +   */
    +  return mapBrowserEvent.pointerEvent.pointerId == 1;
    +};
    diff --git a/mapbox-gl/ol/ol/events/condition.jsdoc b/mapbox-gl/ol/ol/events/condition.jsdoc
    new file mode 100644
    index 0000000000..e7cefa866e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/events/condition.jsdoc
    @@ -0,0 +1,18 @@
    +/**
    + * Applications do not normally create event instances. They register (and
    + * unregister) event listener functions, which, when called by the library as
    + * the result of an event being dispatched, are passed event instances as their
    + * first argument. Listeners can be registered and unregistered on all objects
    + * descending from {@link ol.Observable}. All event instances have a `target`
    + * property, which corresponds to the object on which the event was dispatched.
    + * By default, `this` within the listener also refers to the target, though
    + * this can be configured in the listener registration function.
    + * Some classes have their own event type, which return additional
    + * properties; see the specific event class page for details.
    + *
    + * @namespace ol.events
    + */
    +
    +/**
    + * @namespace ol.events.condition
    + */
    diff --git a/mapbox-gl/ol/ol/extent.js b/mapbox-gl/ol/ol/extent.js
    new file mode 100644
    index 0000000000..1b6a9ac488
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/extent.js
    @@ -0,0 +1,890 @@
    +goog.provide('ol.Extent');
    +goog.provide('ol.extent');
    +goog.provide('ol.extent.Corner');
    +goog.provide('ol.extent.Relationship');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.Coordinate');
    +goog.require('ol.Size');
    +goog.require('ol.TransformFunction');
    +
    +
    +/**
    + * An array of numbers representing an extent: `[minx, miny, maxx, maxy]`.
    + * @typedef {Array.<number>}
    + * @api stable
    + */
    +ol.Extent;
    +
    +
    +/**
    + * Extent corner.
    + * @enum {string}
    + */
    +ol.extent.Corner = {
    +  BOTTOM_LEFT: 'bottom-left',
    +  BOTTOM_RIGHT: 'bottom-right',
    +  TOP_LEFT: 'top-left',
    +  TOP_RIGHT: 'top-right'
    +};
    +
    +
    +/**
    + * Relationship to an extent.
    + * @enum {number}
    + */
    +ol.extent.Relationship = {
    +  UNKNOWN: 0,
    +  INTERSECTING: 1,
    +  ABOVE: 2,
    +  RIGHT: 4,
    +  BELOW: 8,
    +  LEFT: 16
    +};
    +
    +
    +/**
    + * Builds an extent that includes all given coordinates.
    + *
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @return {ol.Extent} Bounding extent.
    + * @api stable
    + */
    +ol.extent.boundingExtent = function(coordinates) {
    +  var extent = ol.extent.createEmpty();
    +  for (var i = 0, ii = coordinates.length; i < ii; ++i) {
    +    ol.extent.extendCoordinate(extent, coordinates[i]);
    +  }
    +  return extent;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} xs Xs.
    + * @param {Array.<number>} ys Ys.
    + * @param {ol.Extent=} opt_extent Destination extent.
    + * @private
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) {
    +  goog.asserts.assert(xs.length > 0);
    +  goog.asserts.assert(ys.length > 0);
    +  var minX = Math.min.apply(null, xs);
    +  var minY = Math.min.apply(null, ys);
    +  var maxX = Math.max.apply(null, xs);
    +  var maxY = Math.max.apply(null, ys);
    +  return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
    +};
    +
    +
    +/**
    + * Return extent increased by the provided value.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} value The amount by which the extent should be buffered.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + * @api stable
    + */
    +ol.extent.buffer = function(extent, value, opt_extent) {
    +  if (goog.isDef(opt_extent)) {
    +    opt_extent[0] = extent[0] - value;
    +    opt_extent[1] = extent[1] - value;
    +    opt_extent[2] = extent[2] + value;
    +    opt_extent[3] = extent[3] + value;
    +    return opt_extent;
    +  } else {
    +    return [
    +      extent[0] - value,
    +      extent[1] - value,
    +      extent[2] + value,
    +      extent[3] + value
    +    ];
    +  }
    +};
    +
    +
    +/**
    + * Creates a clone of an extent.
    + *
    + * @param {ol.Extent} extent Extent to clone.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} The clone.
    + */
    +ol.extent.clone = function(extent, opt_extent) {
    +  if (goog.isDef(opt_extent)) {
    +    opt_extent[0] = extent[0];
    +    opt_extent[1] = extent[1];
    +    opt_extent[2] = extent[2];
    +    opt_extent[3] = extent[3];
    +    return opt_extent;
    +  } else {
    +    return extent.slice();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {number} Closest squared distance.
    + */
    +ol.extent.closestSquaredDistanceXY = function(extent, x, y) {
    +  var dx, dy;
    +  if (x < extent[0]) {
    +    dx = extent[0] - x;
    +  } else if (extent[2] < x) {
    +    dx = x - extent[2];
    +  } else {
    +    dx = 0;
    +  }
    +  if (y < extent[1]) {
    +    dy = extent[1] - y;
    +  } else if (extent[3] < y) {
    +    dy = y - extent[3];
    +  } else {
    +    dy = 0;
    +  }
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * Checks if the passed coordinate is contained or on the edge of the extent.
    + *
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {boolean} Contains.
    + * @api stable
    + */
    +ol.extent.containsCoordinate = function(extent, coordinate) {
    +  return ol.extent.containsXY(extent, coordinate[0], coordinate[1]);
    +};
    +
    +
    +/**
    + * Checks if `extent2` is contained by or on the edge of `extent1`.
    + *
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {boolean} Contains.
    + * @api stable
    + */
    +ol.extent.containsExtent = function(extent1, extent2) {
    +  return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] &&
    +      extent1[1] <= extent2[1] && extent2[3] <= extent1[3];
    +};
    +
    +
    +/**
    + * Checks if the passed coordinate is contained or on the edge of the extent.
    + *
    + * @param {ol.Extent} extent Extent.
    + * @param {number} x X coordinate.
    + * @param {number} y Y coordinate.
    + * @return {boolean} Contains.
    + * @api stable
    + */
    +ol.extent.containsXY = function(extent, x, y) {
    +  return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3];
    +};
    +
    +
    +/**
    + * Get the relationship between a coordinate and extent.
    + * @param {ol.Extent} extent The extent.
    + * @param {ol.Coordinate} coordinate The coordinate.
    + * @return {number} The relationship (bitwise compare with
    + *     ol.extent.Relationship).
    + */
    +ol.extent.coordinateRelationship = function(extent, coordinate) {
    +  var minX = extent[0];
    +  var minY = extent[1];
    +  var maxX = extent[2];
    +  var maxY = extent[3];
    +  var x = coordinate[0];
    +  var y = coordinate[1];
    +  var relationship = ol.extent.Relationship.UNKNOWN;
    +  if (x < minX) {
    +    relationship = relationship | ol.extent.Relationship.LEFT;
    +  } else if (x > maxX) {
    +    relationship = relationship | ol.extent.Relationship.RIGHT;
    +  }
    +  if (y < minY) {
    +    relationship = relationship | ol.extent.Relationship.BELOW;
    +  } else if (y > maxY) {
    +    relationship = relationship | ol.extent.Relationship.ABOVE;
    +  }
    +  if (relationship === ol.extent.Relationship.UNKNOWN) {
    +    relationship = ol.extent.Relationship.INTERSECTING;
    +  }
    +  return relationship;
    +};
    +
    +
    +/**
    + * @return {ol.Extent} Empty extent.
    + * @api stable
    + */
    +ol.extent.createEmpty = function() {
    +  return [Infinity, Infinity, -Infinity, -Infinity];
    +};
    +
    +
    +/**
    + * @param {number} minX Minimum X.
    + * @param {number} minY Minimum Y.
    + * @param {number} maxX Maximum X.
    + * @param {number} maxY Maximum Y.
    + * @param {ol.Extent=} opt_extent Destination extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdate = function(minX, minY, maxX, maxY, opt_extent) {
    +  if (goog.isDef(opt_extent)) {
    +    opt_extent[0] = minX;
    +    opt_extent[1] = minY;
    +    opt_extent[2] = maxX;
    +    opt_extent[3] = maxY;
    +    return opt_extent;
    +  } else {
    +    return [minX, minY, maxX, maxY];
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdateEmpty = function(opt_extent) {
    +  return ol.extent.createOrUpdate(
    +      Infinity, Infinity, -Infinity, -Infinity, opt_extent);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdateFromCoordinate = function(coordinate, opt_extent) {
    +  var x = coordinate[0];
    +  var y = coordinate[1];
    +  return ol.extent.createOrUpdate(x, y, x, y, opt_extent);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdateFromCoordinates = function(coordinates, opt_extent) {
    +  var extent = ol.extent.createOrUpdateEmpty(opt_extent);
    +  return ol.extent.extendCoordinates(extent, coordinates);
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdateFromFlatCoordinates =
    +    function(flatCoordinates, offset, end, stride, opt_extent) {
    +  var extent = ol.extent.createOrUpdateEmpty(opt_extent);
    +  return ol.extent.extendFlatCoordinates(
    +      extent, flatCoordinates, offset, end, stride);
    +};
    +
    +
    +/**
    + * @param {Array.<Array.<ol.Coordinate>>} rings Rings.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.createOrUpdateFromRings = function(rings, opt_extent) {
    +  var extent = ol.extent.createOrUpdateEmpty(opt_extent);
    +  return ol.extent.extendRings(extent, rings);
    +};
    +
    +
    +/**
    + * Empties extent in place.
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.empty = function(extent) {
    +  extent[0] = extent[1] = Infinity;
    +  extent[2] = extent[3] = -Infinity;
    +  return extent;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {boolean} Equals.
    + * @api stable
    + */
    +ol.extent.equals = function(extent1, extent2) {
    +  return extent1[0] == extent2[0] && extent1[2] == extent2[2] &&
    +      extent1[1] == extent2[1] && extent1[3] == extent2[3];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {ol.Extent} Extent.
    + * @api stable
    + */
    +ol.extent.extend = function(extent1, extent2) {
    +  if (extent2[0] < extent1[0]) {
    +    extent1[0] = extent2[0];
    +  }
    +  if (extent2[2] > extent1[2]) {
    +    extent1[2] = extent2[2];
    +  }
    +  if (extent2[1] < extent1[1]) {
    +    extent1[1] = extent2[1];
    +  }
    +  if (extent2[3] > extent1[3]) {
    +    extent1[3] = extent2[3];
    +  }
    +  return extent1;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + */
    +ol.extent.extendCoordinate = function(extent, coordinate) {
    +  if (coordinate[0] < extent[0]) {
    +    extent[0] = coordinate[0];
    +  }
    +  if (coordinate[0] > extent[2]) {
    +    extent[2] = coordinate[0];
    +  }
    +  if (coordinate[1] < extent[1]) {
    +    extent[1] = coordinate[1];
    +  }
    +  if (coordinate[1] > extent[3]) {
    +    extent[3] = coordinate[1];
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.extendCoordinates = function(extent, coordinates) {
    +  var i, ii;
    +  for (i = 0, ii = coordinates.length; i < ii; ++i) {
    +    ol.extent.extendCoordinate(extent, coordinates[i]);
    +  }
    +  return extent;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.extendFlatCoordinates =
    +    function(extent, flatCoordinates, offset, end, stride) {
    +  for (; offset < end; offset += stride) {
    +    ol.extent.extendXY(
    +        extent, flatCoordinates[offset], flatCoordinates[offset + 1]);
    +  }
    +  return extent;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {Array.<Array.<ol.Coordinate>>} rings Rings.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.extendRings = function(extent, rings) {
    +  var i, ii;
    +  for (i = 0, ii = rings.length; i < ii; ++i) {
    +    ol.extent.extendCoordinates(extent, rings[i]);
    +  }
    +  return extent;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} x X.
    + * @param {number} y Y.
    + */
    +ol.extent.extendXY = function(extent, x, y) {
    +  extent[0] = Math.min(extent[0], x);
    +  extent[1] = Math.min(extent[1], y);
    +  extent[2] = Math.max(extent[2], x);
    +  extent[3] = Math.max(extent[3], y);
    +};
    +
    +
    +/**
    + * This function calls `callback` for each corner of the extent. If the
    + * callback returns a truthy value the function returns that value
    + * immediately. Otherwise the function returns `false`.
    + * @param {ol.Extent} extent Extent.
    + * @param {function(this:T, ol.Coordinate): S} callback Callback.
    + * @param {T=} opt_this Value to use as `this` when executing `callback`.
    + * @return {S|boolean} Value.
    + * @template S, T
    + */
    +ol.extent.forEachCorner = function(extent, callback, opt_this) {
    +  var val;
    +  val = callback.call(opt_this, ol.extent.getBottomLeft(extent));
    +  if (val) {
    +    return val;
    +  }
    +  val = callback.call(opt_this, ol.extent.getBottomRight(extent));
    +  if (val) {
    +    return val;
    +  }
    +  val = callback.call(opt_this, ol.extent.getTopRight(extent));
    +  if (val) {
    +    return val;
    +  }
    +  val = callback.call(opt_this, ol.extent.getTopLeft(extent));
    +  if (val) {
    +    return val;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {number} Area.
    + */
    +ol.extent.getArea = function(extent) {
    +  var area = 0;
    +  if (!ol.extent.isEmpty(extent)) {
    +    area = ol.extent.getWidth(extent) * ol.extent.getHeight(extent);
    +  }
    +  return area;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Coordinate} Bottom left coordinate.
    + * @api stable
    + */
    +ol.extent.getBottomLeft = function(extent) {
    +  return [extent[0], extent[1]];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Coordinate} Bottom right coordinate.
    + * @api stable
    + */
    +ol.extent.getBottomRight = function(extent) {
    +  return [extent[2], extent[1]];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Coordinate} Center.
    + * @api stable
    + */
    +ol.extent.getCenter = function(extent) {
    +  return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2];
    +};
    +
    +
    +/**
    + * Get a corner coordinate of an extent.
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.extent.Corner} corner Corner.
    + * @return {ol.Coordinate} Corner coordinate.
    + */
    +ol.extent.getCorner = function(extent, corner) {
    +  var coordinate;
    +  if (corner === ol.extent.Corner.BOTTOM_LEFT) {
    +    coordinate = ol.extent.getBottomLeft(extent);
    +  } else if (corner === ol.extent.Corner.BOTTOM_RIGHT) {
    +    coordinate = ol.extent.getBottomRight(extent);
    +  } else if (corner === ol.extent.Corner.TOP_LEFT) {
    +    coordinate = ol.extent.getTopLeft(extent);
    +  } else if (corner === ol.extent.Corner.TOP_RIGHT) {
    +    coordinate = ol.extent.getTopRight(extent);
    +  } else {
    +    goog.asserts.fail('Invalid corner: %s', corner);
    +  }
    +  goog.asserts.assert(goog.isDef(coordinate));
    +  return coordinate;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {number} Enlarged area.
    + */
    +ol.extent.getEnlargedArea = function(extent1, extent2) {
    +  var minX = Math.min(extent1[0], extent2[0]);
    +  var minY = Math.min(extent1[1], extent2[1]);
    +  var maxX = Math.max(extent1[2], extent2[2]);
    +  var maxY = Math.max(extent1[3], extent2[3]);
    +  return (maxX - minX) * (maxY - minY);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {ol.Extent=} opt_extent Destination extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.getForViewAndSize =
    +    function(center, resolution, rotation, size, opt_extent) {
    +  var dx = resolution * size[0] / 2;
    +  var dy = resolution * size[1] / 2;
    +  var cosRotation = Math.cos(rotation);
    +  var sinRotation = Math.sin(rotation);
    +  /** @type {Array.<number>} */
    +  var xs = [-dx, -dx, dx, dx];
    +  /** @type {Array.<number>} */
    +  var ys = [-dy, dy, -dy, dy];
    +  var i, x, y;
    +  for (i = 0; i < 4; ++i) {
    +    x = xs[i];
    +    y = ys[i];
    +    xs[i] = center[0] + x * cosRotation - y * sinRotation;
    +    ys[i] = center[1] + x * sinRotation + y * cosRotation;
    +  }
    +  return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {number} Height.
    + * @api stable
    + */
    +ol.extent.getHeight = function(extent) {
    +  return extent[3] - extent[1];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {number} Intersection area.
    + */
    +ol.extent.getIntersectionArea = function(extent1, extent2) {
    +  var intersection = ol.extent.getIntersection(extent1, extent2);
    +  return ol.extent.getArea(intersection);
    +};
    +
    +
    +/**
    + * Get the intersection of two extents.
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @param {ol.Extent=} opt_extent Optional extent to populate with intersection.
    + * @return {ol.Extent} Intersecting extent.
    + * @api stable
    + */
    +ol.extent.getIntersection = function(extent1, extent2, opt_extent) {
    +  var intersection = goog.isDef(opt_extent) ?
    +      opt_extent : ol.extent.createEmpty();
    +  if (ol.extent.intersects(extent1, extent2)) {
    +    if (extent1[0] > extent2[0]) {
    +      intersection[0] = extent1[0];
    +    } else {
    +      intersection[0] = extent2[0];
    +    }
    +    if (extent1[1] > extent2[1]) {
    +      intersection[1] = extent1[1];
    +    } else {
    +      intersection[1] = extent2[1];
    +    }
    +    if (extent1[2] < extent2[2]) {
    +      intersection[2] = extent1[2];
    +    } else {
    +      intersection[2] = extent2[2];
    +    }
    +    if (extent1[3] < extent2[3]) {
    +      intersection[3] = extent1[3];
    +    } else {
    +      intersection[3] = extent2[3];
    +    }
    +  }
    +  return intersection;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {number} Margin.
    + */
    +ol.extent.getMargin = function(extent) {
    +  return ol.extent.getWidth(extent) + ol.extent.getHeight(extent);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Size} Size.
    + * @api stable
    + */
    +ol.extent.getSize = function(extent) {
    +  return [extent[2] - extent[0], extent[3] - extent[1]];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Coordinate} Top left coordinate.
    + * @api stable
    + */
    +ol.extent.getTopLeft = function(extent) {
    +  return [extent[0], extent[3]];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {ol.Coordinate} Top right coordinate.
    + * @api stable
    + */
    +ol.extent.getTopRight = function(extent) {
    +  return [extent[2], extent[3]];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {number} Width.
    + * @api stable
    + */
    +ol.extent.getWidth = function(extent) {
    +  return extent[2] - extent[0];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent.
    + * @return {boolean} Intersects.
    + * @api stable
    + */
    +ol.extent.intersects = function(extent1, extent2) {
    +  return extent1[0] <= extent2[2] &&
    +      extent1[2] >= extent2[0] &&
    +      extent1[1] <= extent2[3] &&
    +      extent1[3] >= extent2[1];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} Is empty.
    + * @api stable
    + */
    +ol.extent.isEmpty = function(extent) {
    +  return extent[2] < extent[0] || extent[3] < extent[1];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} Is infinite.
    + */
    +ol.extent.isInfinite = function(extent) {
    +  return extent[0] == -Infinity || extent[1] == -Infinity ||
    +      extent[2] == Infinity || extent[3] == Infinity;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {ol.Coordinate} Coordinate.
    + */
    +ol.extent.normalize = function(extent, coordinate) {
    +  return [
    +    (coordinate[0] - extent[0]) / (extent[2] - extent[0]),
    +    (coordinate[1] - extent[1]) / (extent[3] - extent[1])
    +  ];
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.extent.returnOrUpdate = function(extent, opt_extent) {
    +  if (goog.isDef(opt_extent)) {
    +    opt_extent[0] = extent[0];
    +    opt_extent[1] = extent[1];
    +    opt_extent[2] = extent[2];
    +    opt_extent[3] = extent[3];
    +    return opt_extent;
    +  } else {
    +    return extent;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} value Value.
    + */
    +ol.extent.scaleFromCenter = function(extent, value) {
    +  var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1);
    +  var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1);
    +  extent[0] -= deltaX;
    +  extent[2] += deltaX;
    +  extent[1] -= deltaY;
    +  extent[3] += deltaY;
    +};
    +
    +
    +/**
    + * Determine if the segment between two coordinates intersects (crosses,
    + * touches, or is contained by) the provided extent.
    + * @param {ol.Extent} extent The extent.
    + * @param {ol.Coordinate} start Segment start coordinate.
    + * @param {ol.Coordinate} end Segment end coordinate.
    + * @return {boolean} The segment intersects the extent.
    + */
    +ol.extent.intersectsSegment = function(extent, start, end) {
    +  var intersects = false;
    +  var startRel = ol.extent.coordinateRelationship(extent, start);
    +  var endRel = ol.extent.coordinateRelationship(extent, end);
    +  if (startRel === ol.extent.Relationship.INTERSECTING ||
    +      endRel === ol.extent.Relationship.INTERSECTING) {
    +    intersects = true;
    +  } else {
    +    var minX = extent[0];
    +    var minY = extent[1];
    +    var maxX = extent[2];
    +    var maxY = extent[3];
    +    var startX = start[0];
    +    var startY = start[1];
    +    var endX = end[0];
    +    var endY = end[1];
    +    var slope = (endY - startY) / (endX - startX);
    +    var x, y;
    +    if (!!(endRel & ol.extent.Relationship.ABOVE) &&
    +        !(startRel & ol.extent.Relationship.ABOVE)) {
    +      // potentially intersects top
    +      x = endX - ((endY - maxY) / slope);
    +      intersects = x >= minX && x <= maxX;
    +    } else if (!!(endRel & ol.extent.Relationship.RIGHT) &&
    +        !(startRel & ol.extent.Relationship.RIGHT)) {
    +      // potentially intersects right
    +      y = endY - ((endX - maxX) * slope);
    +      intersects = y >= minY && y <= maxY;
    +    } else if (!!(endRel & ol.extent.Relationship.BELOW) &&
    +        !(startRel & ol.extent.Relationship.BELOW)) {
    +      // potentially intersects bottom
    +      x = endX - ((endY - minY) / slope);
    +      intersects = x >= minX && x <= maxX;
    +    } else if (!!(endRel & ol.extent.Relationship.LEFT) &&
    +        !(startRel & ol.extent.Relationship.LEFT)) {
    +      // potentially intersects left
    +      y = endY - ((endX - minX) * slope);
    +      intersects = y >= minY && y <= maxY;
    +    }
    +
    +  }
    +  return intersects;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent1 Extent 1.
    + * @param {ol.Extent} extent2 Extent 2.
    + * @return {boolean} Touches.
    + */
    +ol.extent.touches = function(extent1, extent2) {
    +  var intersects = ol.extent.intersects(extent1, extent2);
    +  return intersects &&
    +      (extent1[0] == extent2[2] || extent1[2] == extent2[0] ||
    +       extent1[1] == extent2[3] || extent1[3] == extent2[1]);
    +};
    +
    +
    +/**
    + * Apply a transform function to the extent.
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.TransformFunction} transformFn Transform function.  Called with
    + * [minX, minY, maxX, maxY] extent coordinates.
    + * @param {ol.Extent=} opt_extent Destination extent.
    + * @return {ol.Extent} Extent.
    + * @api stable
    + */
    +ol.extent.applyTransform = function(extent, transformFn, opt_extent) {
    +  var coordinates = [
    +    extent[0], extent[1],
    +    extent[0], extent[3],
    +    extent[2], extent[1],
    +    extent[2], extent[3]
    +  ];
    +  transformFn(coordinates, coordinates, 2);
    +  var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];
    +  var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
    +  return ol.extent.boundingExtentXYs_(xs, ys, opt_extent);
    +};
    +
    +
    +/**
    + * Apply a 2d transform to an extent.
    + * @param {ol.Extent} extent Input extent.
    + * @param {goog.vec.Mat4.Number} transform The transform matrix.
    + * @param {ol.Extent=} opt_extent Optional extent for return values.
    + * @return {ol.Extent} The transformed extent.
    + */
    +ol.extent.transform2D = function(extent, transform, opt_extent) {
    +  var dest = goog.isDef(opt_extent) ? opt_extent : [];
    +  var m00 = goog.vec.Mat4.getElement(transform, 0, 0);
    +  var m10 = goog.vec.Mat4.getElement(transform, 1, 0);
    +  var m01 = goog.vec.Mat4.getElement(transform, 0, 1);
    +  var m11 = goog.vec.Mat4.getElement(transform, 1, 1);
    +  var m03 = goog.vec.Mat4.getElement(transform, 0, 3);
    +  var m13 = goog.vec.Mat4.getElement(transform, 1, 3);
    +  var xi = [0, 2, 0, 2];
    +  var yi = [1, 1, 3, 3];
    +  var xs = [];
    +  var ys = [];
    +  var i, x, y;
    +  for (i = 0; i < 4; ++i) {
    +    x = extent[xi[i]];
    +    y = extent[yi[i]];
    +    xs[i] = m00 * x + m01 * y + m03;
    +    ys[i] = m10 * x + m11 * y + m13;
    +  }
    +  dest[0] = Math.min.apply(null, xs);
    +  dest[1] = Math.min.apply(null, ys);
    +  dest[2] = Math.max.apply(null, xs);
    +  dest[3] = Math.max.apply(null, ys);
    +  return dest;
    +};
    diff --git a/mapbox-gl/ol/ol/extent.jsdoc b/mapbox-gl/ol/ol/extent.jsdoc
    new file mode 100644
    index 0000000000..4af9963e3c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/extent.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.extent
    + */
    diff --git a/mapbox-gl/ol/ol/feature.js b/mapbox-gl/ol/ol/feature.js
    new file mode 100644
    index 0000000000..947a68568d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/feature.js
    @@ -0,0 +1,329 @@
    +goog.provide('ol.Feature');
    +goog.provide('ol.feature');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('ol.Object');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.style.Style');
    +
    +
    +
    +/**
    + * @classdesc
    + * A vector object for geographic features with a geometry and other
    + * attribute properties, similar to the features in vector file formats like
    + * GeoJSON.
    + *
    + * Features can be styled individually with `setStyle`; otherwise they use the
    + * style of their vector layer or feature overlay.
    + *
    + * Note that attribute properties are set as {@link ol.Object} properties on
    + * the feature object, so they are observable, and have get/set accessors.
    + *
    + * Typically, a feature has a single geometry property. You can set the
    + * geometry using the `setGeometry` method and get it with `getGeometry`.
    + * It is possible to store more than one geometry on a feature using attribute
    + * properties. By default, the geometry used for rendering is identified by
    + * the property name `geometry`. If you want to use another geometry property
    + * for rendering, use the `setGeometryName` method to change the attribute
    + * property associated with the geometry for the feature.  For example:
    + *
    + * ```js
    + * var feature = new ol.Feature({
    + *   geometry: new ol.geom.Polygon(polyCoords),
    + *   labelPoint: new ol.geom.Point(labelCoords),
    + *   name: 'My Polygon'
    + * });
    + *
    + * // get the polygon geometry
    + * var poly = feature.getGeometry();
    + *
    + * // Render the feature as a point using the coordinates from labelPoint
    + * feature.setGeometryName('labelPoint');
    + *
    + * // get the point geometry
    + * var point = feature.getGeometry();
    + * ```
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @fires change Triggered when the id, the geometry or the style of the
    + *     feature changes.
    + * @param {ol.geom.Geometry|Object.<string, *>=} opt_geometryOrProperties
    + *     You may pass a Geometry object directly, or an object literal
    + *     containing properties.  If you pass an object literal, you may
    + *     include a Geometry associated with a `geometry` key.
    + * @api stable
    + */
    +ol.Feature = function(opt_geometryOrProperties) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {number|string|undefined}
    +   */
    +  this.id_ = undefined;
    +
    +  /**
    +   * @type {string}
    +   * @private
    +   */
    +  this.geometryName_ = 'geometry';
    +
    +  /**
    +   * User provided style.
    +   * @private
    +   * @type {ol.style.Style|Array.<ol.style.Style>|
    +   *     ol.feature.FeatureStyleFunction}
    +   */
    +  this.style_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.feature.FeatureStyleFunction|undefined}
    +   */
    +  this.styleFunction_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.geometryChangeKey_ = null;
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(this.geometryName_),
    +      this.handleGeometryChanged_, false, this);
    +
    +  if (goog.isDef(opt_geometryOrProperties)) {
    +    if (opt_geometryOrProperties instanceof ol.geom.Geometry ||
    +        goog.isNull(opt_geometryOrProperties)) {
    +      var geometry = /** @type {ol.geom.Geometry} */ (opt_geometryOrProperties);
    +      this.setGeometry(geometry);
    +    } else {
    +      goog.asserts.assert(goog.isObject(opt_geometryOrProperties));
    +      var properties = /** @type {Object.<string, *>} */
    +          (opt_geometryOrProperties);
    +      this.setProperties(properties);
    +    }
    +  }
    +};
    +goog.inherits(ol.Feature, ol.Object);
    +
    +
    +/**
    + * Clone this feature. If the original feature has a geometry it
    + * is also cloned. The feature id is not set in the clone.
    + * @return {ol.Feature} The clone.
    + * @api stable
    + */
    +ol.Feature.prototype.clone = function() {
    +  var clone = new ol.Feature(this.getProperties());
    +  clone.setGeometryName(this.getGeometryName());
    +  var geometry = this.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    clone.setGeometry(geometry.clone());
    +  }
    +  var style = this.getStyle();
    +  if (!goog.isNull(style)) {
    +    clone.setStyle(style);
    +  }
    +  return clone;
    +};
    +
    +
    +/**
    + * @return {ol.geom.Geometry|undefined} Returns the Geometry associated
    + *     with this feature using the current geometry name property.  By
    + *     default, this is `geometry` but it may be changed by calling
    + *     `setGeometryName`.
    + * @api stable
    + * @observable
    + */
    +ol.Feature.prototype.getGeometry = function() {
    +  return /** @type {ol.geom.Geometry|undefined} */ (
    +      this.get(this.geometryName_));
    +};
    +goog.exportProperty(
    +    ol.Feature.prototype,
    +    'getGeometry',
    +    ol.Feature.prototype.getGeometry);
    +
    +
    +/**
    + * @return {number|string|undefined} Id.
    + * @api stable
    + */
    +ol.Feature.prototype.getId = function() {
    +  return this.id_;
    +};
    +
    +
    +/**
    + * @return {string} Get the property name associated with the geometry for
    + *     this feature.  By default, this is `geometry` but it may be changed by
    + *     calling `setGeometryName`.
    + * @api stable
    + */
    +ol.Feature.prototype.getGeometryName = function() {
    +  return this.geometryName_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Style|Array.<ol.style.Style>|
    + *     ol.feature.FeatureStyleFunction} Return the style as set by `setStyle`
    + * in the same format that it was provided in. If `setStyle` has not been
    + * called, or if it was called with `null`, then `getStyle()` will return
    + * `null`.
    + * @api stable
    + */
    +ol.Feature.prototype.getStyle = function() {
    +  return this.style_;
    +};
    +
    +
    +/**
    + * @return {ol.feature.FeatureStyleFunction|undefined} Return a function
    + * representing the current style of this feature.
    + * @api stable
    + */
    +ol.Feature.prototype.getStyleFunction = function() {
    +  return this.styleFunction_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Feature.prototype.handleGeometryChange_ = function() {
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Feature.prototype.handleGeometryChanged_ = function() {
    +  if (!goog.isNull(this.geometryChangeKey_)) {
    +    goog.events.unlistenByKey(this.geometryChangeKey_);
    +    this.geometryChangeKey_ = null;
    +  }
    +  var geometry = this.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    this.geometryChangeKey_ = goog.events.listen(geometry,
    +        goog.events.EventType.CHANGE, this.handleGeometryChange_, false, this);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry|undefined} geometry Set the geometry for this
    + * feature. This will update the property associated with the current
    + * geometry property name.  By default, this is `geometry` but it can be
    + * changed by calling `setGeometryName`.
    + * @api stable
    + * @observable
    + */
    +ol.Feature.prototype.setGeometry = function(geometry) {
    +  this.set(this.geometryName_, geometry);
    +};
    +goog.exportProperty(
    +    ol.Feature.prototype,
    +    'setGeometry',
    +    ol.Feature.prototype.setGeometry);
    +
    +
    +/**
    + * Set the style for the feature.  This can be a single style object, an array
    + * of styles, or a function that takes a resolution and returns an array of
    + * styles. If it is `null` the feature has no style (a `null` style).
    + * @param {ol.style.Style|Array.<ol.style.Style>|
    + *     ol.feature.FeatureStyleFunction} style Style for this feature.
    + * @api stable
    + */
    +ol.Feature.prototype.setStyle = function(style) {
    +  this.style_ = style;
    +  this.styleFunction_ = goog.isNull(style) ?
    +      undefined : ol.feature.createFeatureStyleFunction(style);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {number|string|undefined} id Set a unique id for this feature.
    + * The id may be used to retrieve a feature from a vector source with the
    + * {@link ol.source.Vector#getFeatureById} method.
    + * @api stable
    + */
    +ol.Feature.prototype.setId = function(id) {
    +  this.id_ = id;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {string} name Set the property name from which this feature's
    + *     geometry will be fetched when calling `getGeometry`.
    + * @api stable
    + */
    +ol.Feature.prototype.setGeometryName = function(name) {
    +  goog.events.unlisten(
    +      this, ol.Object.getChangeEventType(this.geometryName_),
    +      this.handleGeometryChanged_, false, this);
    +  this.geometryName_ = name;
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(this.geometryName_),
    +      this.handleGeometryChanged_, false, this);
    +  this.handleGeometryChanged_();
    +};
    +
    +
    +/**
    + * A function that takes a `{number}` representing the view's resolution. It
    + * returns an Array of {@link ol.style.Style}. This way individual features
    + * can be styled. The this keyword inside the function references the
    + * {@link ol.Feature} to be styled.
    + *
    + * @typedef {function(this: ol.Feature, number): Array.<ol.style.Style>}
    + * @api stable
    + */
    +ol.feature.FeatureStyleFunction;
    +
    +
    +/**
    + * Convert the provided object into a feature style function.  Functions passed
    + * through unchanged.  Arrays of ol.style.Style or single style objects wrapped
    + * in a new feature style function.
    + * @param {ol.feature.FeatureStyleFunction|!Array.<ol.style.Style>|
    + *     !ol.style.Style} obj A feature style function, a single style, or an
    + *     array of styles.
    + * @return {ol.feature.FeatureStyleFunction} A style function.
    + */
    +ol.feature.createFeatureStyleFunction = function(obj) {
    +  /**
    +   * @type {ol.feature.FeatureStyleFunction}
    +   */
    +  var styleFunction;
    +
    +  if (goog.isFunction(obj)) {
    +    styleFunction = /** @type {ol.feature.FeatureStyleFunction} */ (obj);
    +  } else {
    +    /**
    +     * @type {Array.<ol.style.Style>}
    +     */
    +    var styles;
    +    if (goog.isArray(obj)) {
    +      styles = obj;
    +    } else {
    +      goog.asserts.assertInstanceof(obj, ol.style.Style);
    +      styles = [obj];
    +    }
    +    styleFunction = goog.functions.constant(styles);
    +  }
    +  return styleFunction;
    +};
    diff --git a/mapbox-gl/ol/ol/feature.jsdoc b/mapbox-gl/ol/ol/feature.jsdoc
    new file mode 100644
    index 0000000000..7eeb59f500
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/feature.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.feature
    + */
    diff --git a/mapbox-gl/ol/ol/featureoverlay.js b/mapbox-gl/ol/ol/featureoverlay.js
    new file mode 100644
    index 0000000000..c21e5db33b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/featureoverlay.js
    @@ -0,0 +1,316 @@
    +goog.provide('ol.FeatureOverlay');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.Collection');
    +goog.require('ol.CollectionEventType');
    +goog.require('ol.Feature');
    +goog.require('ol.render.EventType');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.style.Style');
    +
    +
    +
    +/**
    + * @classdesc
    + * A mechanism for changing the style of a small number of features on a
    + * temporary basis, for example highlighting. This is necessary with the Canvas
    + * renderer, where, unlike in SVG, features cannot be individually referenced.
    + * See examples/vector-layers for an example: create a FeatureOverlay with a
    + * different style, copy the feature(s) you want rendered in this different
    + * style into it, and then remove them again when you're finished.
    + *
    + * @constructor
    + * @param {olx.FeatureOverlayOptions=} opt_options Options.
    + * @api
    + */
    +ol.FeatureOverlay = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Collection.<ol.Feature>}
    +   */
    +  this.features_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<goog.events.Key>}
    +   */
    +  this.featuresListenerKeys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, goog.events.Key>}
    +   */
    +  this.featureChangeListenerKeys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Map}
    +   */
    +  this.map_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.postComposeListenerKey_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    +   */
    +  this.style_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.StyleFunction|undefined}
    +   */
    +  this.styleFunction_ = undefined;
    +
    +  this.setStyle(goog.isDef(options.style) ?
    +      options.style : ol.style.defaultStyleFunction);
    +
    +  if (goog.isDef(options.features)) {
    +    if (goog.isArray(options.features)) {
    +      this.setFeatures(new ol.Collection(options.features.slice()));
    +    } else {
    +      goog.asserts.assertInstanceof(options.features, ol.Collection);
    +      this.setFeatures(options.features);
    +    }
    +  } else {
    +    this.setFeatures(new ol.Collection());
    +  }
    +
    +  if (goog.isDef(options.map)) {
    +    this.setMap(options.map);
    +  }
    +
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.addFeature = function(feature) {
    +  this.features_.push(feature);
    +};
    +
    +
    +/**
    + * @return {ol.Collection.<ol.Feature>} Features collection.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.getFeatures = function() {
    +  return this.features_;
    +};
    +
    +
    +/**
    + * @return {?ol.Map} The map with which this feature overlay is associated.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.getMap = function() {
    +  return this.map_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.FeatureOverlay.prototype.handleFeatureChange_ = function() {
    +  this.render_();
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.CollectionEvent} collectionEvent Collection event.
    + */
    +ol.FeatureOverlay.prototype.handleFeaturesAdd_ = function(collectionEvent) {
    +  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
    +  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
    +  this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
    +      goog.events.listen(feature, goog.events.EventType.CHANGE,
    +      this.handleFeatureChange_, false, this);
    +  this.render_();
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.CollectionEvent} collectionEvent Collection event.
    + */
    +ol.FeatureOverlay.prototype.handleFeaturesRemove_ = function(collectionEvent) {
    +  goog.asserts.assert(!goog.isNull(this.featureChangeListenerKeys_));
    +  var feature = /** @type {ol.Feature} */ (collectionEvent.element);
    +  var key = goog.getUid(feature).toString();
    +  goog.events.unlistenByKey(this.featureChangeListenerKeys_[key]);
    +  delete this.featureChangeListenerKeys_[key];
    +  this.render_();
    +};
    +
    +
    +/**
    + * Handle changes in image style state.
    + * @param {goog.events.Event} event Image style change event.
    + * @private
    + */
    +ol.FeatureOverlay.prototype.handleImageChange_ = function(event) {
    +  this.render_();
    +};
    +
    +
    +/**
    + * @param {ol.render.Event} event Event.
    + * @private
    + */
    +ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) {
    +  if (goog.isNull(this.features_)) {
    +    return;
    +  }
    +  var styleFunction = this.styleFunction_;
    +  if (!goog.isDef(styleFunction)) {
    +    styleFunction = ol.style.defaultStyleFunction;
    +  }
    +  var replayGroup = /** @type {ol.render.IReplayGroup} */
    +      (event.replayGroup);
    +  goog.asserts.assert(goog.isDef(replayGroup));
    +  var frameState = event.frameState;
    +  var pixelRatio = frameState.pixelRatio;
    +  var resolution = frameState.viewState.resolution;
    +  var squaredTolerance = ol.renderer.vector.getSquaredTolerance(resolution,
    +      pixelRatio);
    +  var i, ii, styles, featureStyleFunction;
    +  this.features_.forEach(function(feature) {
    +    featureStyleFunction = feature.getStyleFunction();
    +    styles = goog.isDef(featureStyleFunction) ?
    +        featureStyleFunction.call(feature, resolution) :
    +        styleFunction(feature, resolution);
    +
    +    if (!goog.isDefAndNotNull(styles)) {
    +      return;
    +    }
    +    ii = styles.length;
    +    for (i = 0; i < ii; ++i) {
    +      ol.renderer.vector.renderFeature(replayGroup, feature, styles[i],
    +          squaredTolerance, this.handleImageChange_, this);
    +    }
    +  }, this);
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.removeFeature = function(feature) {
    +  this.features_.remove(feature);
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.FeatureOverlay.prototype.render_ = function() {
    +  if (!goog.isNull(this.map_)) {
    +    this.map_.render();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Collection.<ol.Feature>} features Features collection.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.setFeatures = function(features) {
    +  if (!goog.isNull(this.featuresListenerKeys_)) {
    +    goog.array.forEach(this.featuresListenerKeys_, goog.events.unlistenByKey);
    +    this.featuresListenerKeys_ = null;
    +  }
    +  if (!goog.isNull(this.featureChangeListenerKeys_)) {
    +    goog.array.forEach(
    +        goog.object.getValues(this.featureChangeListenerKeys_),
    +        goog.events.unlistenByKey);
    +    this.featureChangeListenerKeys_ = null;
    +  }
    +  this.features_ = features;
    +  if (!goog.isNull(features)) {
    +    this.featuresListenerKeys_ = [
    +      goog.events.listen(features, ol.CollectionEventType.ADD,
    +          this.handleFeaturesAdd_, false, this),
    +      goog.events.listen(features, ol.CollectionEventType.REMOVE,
    +          this.handleFeaturesRemove_, false, this)
    +    ];
    +    this.featureChangeListenerKeys_ = {};
    +    features.forEach(function(feature) {
    +      this.featureChangeListenerKeys_[goog.getUid(feature).toString()] =
    +          goog.events.listen(feature, goog.events.EventType.CHANGE,
    +          this.handleFeatureChange_, false, this);
    +    }, this);
    +  }
    +  this.render_();
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.setMap = function(map) {
    +  if (!goog.isNull(this.postComposeListenerKey_)) {
    +    goog.events.unlistenByKey(this.postComposeListenerKey_);
    +    this.postComposeListenerKey_ = null;
    +  }
    +  this.render_();
    +  this.map_ = map;
    +  if (!goog.isNull(map)) {
    +    this.postComposeListenerKey_ = goog.events.listen(
    +        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
    +        this);
    +    map.render();
    +  }
    +};
    +
    +
    +/**
    + * Set the style for features.  This can be a single style object, an array
    + * of styles, or a function that takes a feature and resolution and returns
    + * an array of styles.
    + * @param {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction} style
    + *     Overlay style.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.setStyle = function(style) {
    +  this.style_ = style;
    +  this.styleFunction_ = ol.style.createStyleFunction(style);
    +  this.render_();
    +};
    +
    +
    +/**
    + * Get the style for features.  This returns whatever was passed to the `style`
    + * option at construction or to the `setStyle` method.
    + * @return {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    + *     Overlay style.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.getStyle = function() {
    +  return this.style_;
    +};
    +
    +
    +/**
    + * Get the style function.
    + * @return {ol.style.StyleFunction|undefined} Style function.
    + * @api
    + */
    +ol.FeatureOverlay.prototype.getStyleFunction = function() {
    +  return this.styleFunction_;
    +};
    diff --git a/mapbox-gl/ol/ol/format.jsdoc b/mapbox-gl/ol/ol/format.jsdoc
    new file mode 100644
    index 0000000000..d70dd02b50
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.format
    + */
    diff --git a/mapbox-gl/ol/ol/format/binaryfeatureformat.js b/mapbox-gl/ol/ol/format/binaryfeatureformat.js
    new file mode 100644
    index 0000000000..ff84c4dd2b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/binaryfeatureformat.js
    @@ -0,0 +1,111 @@
    +goog.provide('ol.format.BinaryFeature');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.binary.Buffer');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.FormatType');
    +goog.require('ol.has');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.format.Feature}
    + */
    +ol.format.BinaryFeature = function() {
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.BinaryFeature, ol.format.Feature);
    +
    +
    +/**
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @private
    + * @return {ol.binary.Buffer} Buffer.
    + */
    +ol.format.BinaryFeature.getBuffer_ = function(source) {
    +  if (ol.has.ARRAY_BUFFER && source instanceof ArrayBuffer) {
    +    return new ol.binary.Buffer(source);
    +  } else if (goog.isString(source)) {
    +    return new ol.binary.Buffer(source);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.BinaryFeature.prototype.getType = function() {
    +  return ol.format.FormatType.BINARY;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.BinaryFeature.prototype.readFeature = function(source) {
    +  return this.readFeatureFromBuffer(ol.format.BinaryFeature.getBuffer_(source));
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.BinaryFeature.prototype.readFeatures = function(source) {
    +  return this.readFeaturesFromBuffer(
    +      ol.format.BinaryFeature.getBuffer_(source));
    +};
    +
    +
    +/**
    + * @param {ol.binary.Buffer} buffer Buffer.
    + * @protected
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.BinaryFeature.prototype.readFeatureFromBuffer = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.binary.Buffer} buffer Buffer.
    + * @protected
    + * @return {Array.<ol.Feature>} Feature.
    + */
    +ol.format.BinaryFeature.prototype.readFeaturesFromBuffer = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.BinaryFeature.prototype.readGeometry = function(source) {
    +  return this.readGeometryFromBuffer(
    +      ol.format.BinaryFeature.getBuffer_(source));
    +};
    +
    +
    +/**
    + * @param {ol.binary.Buffer} buffer Buffer.
    + * @protected
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.BinaryFeature.prototype.readGeometryFromBuffer = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.BinaryFeature.prototype.readProjection = function(source) {
    +  return this.readProjectionFromBuffer(
    +      ol.format.BinaryFeature.getBuffer_(source));
    +};
    +
    +
    +/**
    + * @param {ol.binary.Buffer} buffer Buffer.
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.BinaryFeature.prototype.readProjectionFromBuffer =
    +    goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/format/featureformat.js b/mapbox-gl/ol/ol/format/featureformat.js
    new file mode 100644
    index 0000000000..a553dc1a70
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/featureformat.js
    @@ -0,0 +1,184 @@
    +goog.provide('ol.format.Feature');
    +
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for feature formats.
    + * {ol.format.Feature} subclasses provide the ability to decode and encode
    + * {@link ol.Feature} objects from a variety of commonly used geospatial
    + * file formats.  See the documentation for each format for more details.
    + *
    + * @constructor
    + * @api stable
    + */
    +ol.format.Feature = function() {
    +
    +  /**
    +   * @protected
    +   * @type {ol.proj.Projection}
    +   */
    +  this.defaultDataProjection = null;
    +};
    +
    +
    +/**
    + * @return {Array.<string>} Extensions.
    + */
    +ol.format.Feature.prototype.getExtensions = goog.abstractMethod;
    +
    +
    +/**
    + * Adds the data projection to the read options.
    + * @param {Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @return {olx.format.ReadOptions|undefined} Options.
    + * @protected
    + */
    +ol.format.Feature.prototype.getReadOptions = function(source, opt_options) {
    +  var options;
    +  if (goog.isDef(opt_options)) {
    +    options = {
    +      dataProjection: goog.isDef(opt_options.dataProjection) ?
    +          opt_options.dataProjection : this.readProjection(source),
    +      featureProjection: opt_options.featureProjection
    +    };
    +  }
    +  return this.adaptOptions(options);
    +};
    +
    +
    +/**
    + * Sets the `defaultDataProjection` on the options, if no `dataProjection`
    + * is set.
    + * @param {olx.format.WriteOptions|olx.format.ReadOptions|undefined} options
    + *     Options.
    + * @protected
    + * @return {olx.format.WriteOptions|olx.format.ReadOptions|undefined}
    + *     Updated options.
    + */
    +ol.format.Feature.prototype.adaptOptions = function(options) {
    +  var updatedOptions;
    +  if (goog.isDef(options)) {
    +    updatedOptions = {
    +      featureProjection: options.featureProjection,
    +      dataProjection: goog.isDefAndNotNull(options.dataProjection) ?
    +          options.dataProjection : this.defaultDataProjection
    +    };
    +  }
    +  return updatedOptions;
    +};
    +
    +
    +/**
    + * @return {ol.format.FormatType} Format.
    + */
    +ol.format.Feature.prototype.getType = goog.abstractMethod;
    +
    +
    +/**
    + * Read a single feature from a source.
    + *
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.Feature.prototype.readFeature = goog.abstractMethod;
    +
    +
    +/**
    + * Read all features from a source.
    + *
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.Feature.prototype.readFeatures = goog.abstractMethod;
    +
    +
    +/**
    + * Read a single geometry from a source.
    + *
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.Feature.prototype.readGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * Read the projection from a source.
    + *
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.Feature.prototype.readProjection = goog.abstractMethod;
    +
    +
    +/**
    + * Encode a feature in this format.
    + *
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} Result.
    + */
    +ol.format.Feature.prototype.writeFeature = goog.abstractMethod;
    +
    +
    +/**
    + * Encode an array of features in this format.
    + *
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} Result.
    + */
    +ol.format.Feature.prototype.writeFeatures = goog.abstractMethod;
    +
    +
    +/**
    + * Write a single geometry in this format.
    + *
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} Result.
    + */
    +ol.format.Feature.prototype.writeGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.geom.Geometry|ol.Extent} geometry Geometry.
    + * @param {boolean} write Set to true for writing, false for reading.
    + * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options
    + *     Options.
    + * @return {ol.geom.Geometry|ol.Extent} Transformed geometry.
    + * @protected
    + */
    +ol.format.Feature.transformWithOptions = function(
    +    geometry, write, opt_options) {
    +  var featureProjection = goog.isDef(opt_options) ?
    +      ol.proj.get(opt_options.featureProjection) : null;
    +  var dataProjection = goog.isDef(opt_options) ?
    +      ol.proj.get(opt_options.dataProjection) : null;
    +  if (!goog.isNull(featureProjection) && !goog.isNull(dataProjection) &&
    +      !ol.proj.equivalent(featureProjection, dataProjection)) {
    +    if (geometry instanceof ol.geom.Geometry) {
    +      return (write ? geometry.clone() : geometry).transform(
    +          write ? featureProjection : dataProjection,
    +          write ? dataProjection : featureProjection);
    +    } else {
    +      // FIXME this is necessary because ol.format.GML treats extents
    +      // as geometries
    +      return ol.proj.transformExtent(
    +          write ? geometry.slice() : geometry,
    +          write ? featureProjection : dataProjection,
    +          write ? dataProjection : featureProjection);
    +    }
    +  } else {
    +    return geometry;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/format/format.js b/mapbox-gl/ol/ol/format/format.js
    new file mode 100644
    index 0000000000..bdedb916d1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/format.js
    @@ -0,0 +1,12 @@
    +goog.provide('ol.format.FormatType');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.format.FormatType = {
    +  BINARY: 'binary',
    +  JSON: 'json',
    +  TEXT: 'text',
    +  XML: 'xml'
    +};
    diff --git a/mapbox-gl/ol/ol/format/geojsonformat.js b/mapbox-gl/ol/ol/format/geojsonformat.js
    new file mode 100644
    index 0000000000..ffe142ede1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/geojsonformat.js
    @@ -0,0 +1,585 @@
    +// FIXME coordinate order
    +// FIXME reprojection
    +
    +goog.provide('ol.format.GeoJSON');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.JSONFeature');
    +goog.require('ol.geom.GeometryCollection');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the GeoJSON format.
    + *
    + * @constructor
    + * @extends {ol.format.JSONFeature}
    + * @param {olx.format.GeoJSONOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.format.GeoJSON = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get(
    +      goog.isDefAndNotNull(options.defaultDataProjection) ?
    +          options.defaultDataProjection : 'EPSG:4326');
    +
    +
    +  /**
    +   * Name of the geometry attribute for features.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.geometryName_ = options.geometryName;
    +
    +};
    +goog.inherits(ol.format.GeoJSON, ol.format.JSONFeature);
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.GeoJSON.EXTENSIONS_ = ['.geojson'];
    +
    +
    +/**
    + * @param {GeoJSONGeometry|GeoJSONGeometryCollection} object Object.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @private
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.GeoJSON.readGeometry_ = function(object, opt_options) {
    +  if (goog.isNull(object)) {
    +    return null;
    +  }
    +  var geometryReader = ol.format.GeoJSON.GEOMETRY_READERS_[object.type];
    +  goog.asserts.assert(goog.isDef(geometryReader));
    +  return /** @type {ol.geom.Geometry} */ (
    +      ol.format.Feature.transformWithOptions(
    +          geometryReader(object), false, opt_options));
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometryCollection} object Object.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @private
    + * @return {ol.geom.GeometryCollection} Geometry collection.
    + */
    +ol.format.GeoJSON.readGeometryCollectionGeometry_ = function(
    +    object, opt_options) {
    +  goog.asserts.assert(object.type == 'GeometryCollection');
    +  var geometries = goog.array.map(object.geometries,
    +      /**
    +       * @param {GeoJSONGeometry} geometry Geometry.
    +       * @return {ol.geom.Geometry} geometry Geometry.
    +       */
    +      function(geometry) {
    +        return ol.format.GeoJSON.readGeometry_(geometry, opt_options);
    +      });
    +  return new ol.geom.GeometryCollection(geometries);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.Point} Point.
    + */
    +ol.format.GeoJSON.readPointGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'Point');
    +  return new ol.geom.Point(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.LineString} LineString.
    + */
    +ol.format.GeoJSON.readLineStringGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'LineString');
    +  return new ol.geom.LineString(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.MultiLineString} MultiLineString.
    + */
    +ol.format.GeoJSON.readMultiLineStringGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'MultiLineString');
    +  return new ol.geom.MultiLineString(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.MultiPoint} MultiPoint.
    + */
    +ol.format.GeoJSON.readMultiPointGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'MultiPoint');
    +  return new ol.geom.MultiPoint(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.MultiPolygon} MultiPolygon.
    + */
    +ol.format.GeoJSON.readMultiPolygonGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'MultiPolygon');
    +  return new ol.geom.MultiPolygon(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {GeoJSONGeometry} object Object.
    + * @private
    + * @return {ol.geom.Polygon} Polygon.
    + */
    +ol.format.GeoJSON.readPolygonGeometry_ = function(object) {
    +  goog.asserts.assert(object.type == 'Polygon');
    +  return new ol.geom.Polygon(object.coordinates);
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @private
    + * @return {GeoJSONGeometry|GeoJSONGeometryCollection} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) {
    +  var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()];
    +  goog.asserts.assert(goog.isDef(geometryWriter));
    +  return geometryWriter(/** @type {ol.geom.Geometry} */ (
    +      ol.format.Feature.transformWithOptions(geometry, true, opt_options)));
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometryCollection} Empty GeoJSON geometry collection.
    + */
    +ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ = function(geometry) {
    +  return /** @type {GeoJSONGeometryCollection} */ ({
    +    'type': 'GeometryCollection',
    +    'geometries': []
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @private
    + * @return {GeoJSONGeometryCollection} GeoJSON geometry collection.
    + */
    +ol.format.GeoJSON.writeGeometryCollectionGeometry_ = function(
    +    geometry, opt_options) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.GeometryCollection);
    +  var geometries = goog.array.map(
    +      geometry.getGeometriesArray(), function(geometry) {
    +        return ol.format.GeoJSON.writeGeometry_(geometry, opt_options);
    +      });
    +  return /** @type {GeoJSONGeometryCollection} */ ({
    +    'type': 'GeometryCollection',
    +    'geometries': geometries
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'LineString',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString);
    +  goog.asserts.assert(
    +      geometry.getType() == ol.geom.GeometryType.MULTI_LINE_STRING);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'MultiLineString',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiPoint);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'MultiPoint',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'MultiPolygon',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writePointGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'Point',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @private
    + * @return {GeoJSONGeometry} GeoJSON geometry.
    + */
    +ol.format.GeoJSON.writePolygonGeometry_ = function(geometry) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +  return /** @type {GeoJSONGeometry} */ ({
    +    'type': 'Polygon',
    +    'coordinates': geometry.getCoordinates()
    +  });
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<string, function(GeoJSONObject): ol.geom.Geometry>}
    + */
    +ol.format.GeoJSON.GEOMETRY_READERS_ = {
    +  'Point': ol.format.GeoJSON.readPointGeometry_,
    +  'LineString': ol.format.GeoJSON.readLineStringGeometry_,
    +  'Polygon': ol.format.GeoJSON.readPolygonGeometry_,
    +  'MultiPoint': ol.format.GeoJSON.readMultiPointGeometry_,
    +  'MultiLineString': ol.format.GeoJSON.readMultiLineStringGeometry_,
    +  'MultiPolygon': ol.format.GeoJSON.readMultiPolygonGeometry_,
    +  'GeometryCollection': ol.format.GeoJSON.readGeometryCollectionGeometry_
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<string, function(ol.geom.Geometry): (GeoJSONGeometry|GeoJSONGeometryCollection)>}
    + */
    +ol.format.GeoJSON.GEOMETRY_WRITERS_ = {
    +  'Point': ol.format.GeoJSON.writePointGeometry_,
    +  'LineString': ol.format.GeoJSON.writeLineStringGeometry_,
    +  'Polygon': ol.format.GeoJSON.writePolygonGeometry_,
    +  'MultiPoint': ol.format.GeoJSON.writeMultiPointGeometry_,
    +  'MultiLineString': ol.format.GeoJSON.writeMultiLineStringGeometry_,
    +  'MultiPolygon': ol.format.GeoJSON.writeMultiPolygonGeometry_,
    +  'GeometryCollection': ol.format.GeoJSON.writeGeometryCollectionGeometry_,
    +  'Circle': ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GeoJSON.prototype.getExtensions = function() {
    +  return ol.format.GeoJSON.EXTENSIONS_;
    +};
    +
    +
    +/**
    + * Read a feature from a GeoJSON Feature source.  Only works for Feature,
    + * use `readFeatures` to read FeatureCollection source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.readFeature;
    +
    +
    +/**
    + * Read all features from a GeoJSON source.  Works with both Feature and
    + * FeatureCollection sources.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GeoJSON.prototype.readFeatureFromObject = function(
    +    object, opt_options) {
    +  var geoJSONFeature = /** @type {GeoJSONFeature} */ (object);
    +  goog.asserts.assert(geoJSONFeature.type == 'Feature');
    +  var geometry = ol.format.GeoJSON.readGeometry_(geoJSONFeature.geometry,
    +      opt_options);
    +  var feature = new ol.Feature();
    +  if (goog.isDef(this.geometryName_)) {
    +    feature.setGeometryName(this.geometryName_);
    +  }
    +  feature.setGeometry(geometry);
    +  if (goog.isDef(geoJSONFeature.id)) {
    +    feature.setId(geoJSONFeature.id);
    +  }
    +  if (goog.isDef(geoJSONFeature.properties)) {
    +    feature.setProperties(geoJSONFeature.properties);
    +  }
    +  return feature;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GeoJSON.prototype.readFeaturesFromObject = function(
    +    object, opt_options) {
    +  var geoJSONObject = /** @type {GeoJSONObject} */ (object);
    +  if (geoJSONObject.type == 'Feature') {
    +    return [this.readFeatureFromObject(object, opt_options)];
    +  } else if (geoJSONObject.type == 'FeatureCollection') {
    +    var geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */
    +        (object);
    +    /** @type {Array.<ol.Feature>} */
    +    var features = [];
    +    var geoJSONFeatures = geoJSONFeatureCollection.features;
    +    var i, ii;
    +    for (i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
    +      features.push(this.readFeatureFromObject(geoJSONFeatures[i],
    +          opt_options));
    +    }
    +    return features;
    +  } else {
    +    goog.asserts.fail();
    +    return [];
    +  }
    +};
    +
    +
    +/**
    + * Read a geometry from a GeoJSON source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.geom.Geometry} Geometry.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.readGeometry;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GeoJSON.prototype.readGeometryFromObject = function(
    +    object, opt_options) {
    +  return ol.format.GeoJSON.readGeometry_(
    +      /** @type {GeoJSONGeometry} */ (object), opt_options);
    +};
    +
    +
    +/**
    + * Read the projection from a GeoJSON source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.readProjection;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GeoJSON.prototype.readProjectionFromObject = function(object) {
    +  var geoJSONObject = /** @type {GeoJSONObject} */ (object);
    +  var crs = geoJSONObject.crs;
    +  if (goog.isDefAndNotNull(crs)) {
    +    if (crs.type == 'name') {
    +      return ol.proj.get(crs.properties.name);
    +    } else if (crs.type == 'EPSG') {
    +      // 'EPSG' is not part of the GeoJSON specification, but is generated by
    +      // GeoServer.
    +      // TODO: remove this when http://jira.codehaus.org/browse/GEOS-5996
    +      // is fixed and widely deployed.
    +      return ol.proj.get('EPSG:' + crs.properties.code);
    +    } else {
    +      goog.asserts.fail();
    +      return null;
    +    }
    +  } else {
    +    return this.defaultDataProjection;
    +  }
    +};
    +
    +
    +/**
    + * Encode a feature as a GeoJSON Feature string.
    + *
    + * @function
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} GeoJSON.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.writeFeature;
    +
    +
    +/**
    + * Encode a feature as a GeoJSON Feature object.
    + *
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @api
    + * @return {Object} Object.
    + */
    +ol.format.GeoJSON.prototype.writeFeatureObject = function(
    +    feature, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  var object = {
    +    'type': 'Feature'
    +  };
    +  var id = feature.getId();
    +  if (goog.isDefAndNotNull(id)) {
    +    object['id'] = id;
    +  }
    +  var geometry = feature.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    object['geometry'] =
    +        ol.format.GeoJSON.writeGeometry_(geometry, opt_options);
    +  }
    +  var properties = feature.getProperties();
    +  goog.object.remove(properties, feature.getGeometryName());
    +  if (!goog.object.isEmpty(properties)) {
    +    object['properties'] = properties;
    +  } else {
    +    object['properties'] = null;
    +  }
    +  return object;
    +};
    +
    +
    +/**
    + * Encode an array of features as GeoJSON.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} GeoJSON.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.writeFeatures;
    +
    +
    +/**
    + * Encode an array of features as a GeoJSON object.
    + *
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {Object} GeoJSON Object.
    + * @api
    + */
    +ol.format.GeoJSON.prototype.writeFeaturesObject =
    +    function(features, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  var objects = [];
    +  var i, ii;
    +  for (i = 0, ii = features.length; i < ii; ++i) {
    +    objects.push(this.writeFeatureObject(features[i], opt_options));
    +  }
    +  return /** @type {GeoJSONFeatureCollection} */ ({
    +    'type': 'FeatureCollection',
    +    'features': objects
    +  });
    +};
    +
    +
    +/**
    + * Encode a geometry as a GeoJSON string.
    + *
    + * @function
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} GeoJSON.
    + * @api stable
    + */
    +ol.format.GeoJSON.prototype.writeGeometry;
    +
    +
    +/**
    + * Encode a geometry as a GeoJSON object.
    + *
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object.
    + * @api
    + */
    +ol.format.GeoJSON.prototype.writeGeometryObject = function(geometry,
    +    opt_options) {
    +  return ol.format.GeoJSON.writeGeometry_(geometry,
    +      this.adaptOptions(opt_options));
    +};
    diff --git a/mapbox-gl/ol/ol/format/gml/gml2format.js b/mapbox-gl/ol/ol/format/gml/gml2format.js
    new file mode 100644
    index 0000000000..9b5774a75e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/gml/gml2format.js
    @@ -0,0 +1,216 @@
    +goog.provide('ol.format.GML2');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('ol.extent');
    +goog.require('ol.format.GML');
    +goog.require('ol.format.GMLBase');
    +goog.require('ol.format.XSD');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the GML format,
    + * version 2.1.2.
    + *
    + * @constructor
    + * @param {olx.format.GMLOptions=} opt_options Optional configuration object.
    + * @extends {ol.format.GMLBase}
    + * @api
    + */
    +ol.format.GML2 = function(opt_options) {
    +  var options = /** @type {olx.format.GMLOptions} */
    +      (goog.isDef(opt_options) ? opt_options : {});
    +
    +  goog.base(this, options);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.schemaLocation = goog.isDef(options.schemaLocation) ?
    +      options.schemaLocation : ol.format.GML2.schemaLocation_;
    +
    +};
    +goog.inherits(ol.format.GML2, ol.format.GMLBase);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.GML2.schemaLocation_ = ol.format.GMLBase.GMLNS +
    +    ' http://schemas.opengis.net/gml/2.1.2/feature.xsd';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} Flat coordinates.
    + */
    +ol.format.GML2.prototype.readFlatCoordinates_ = function(node, objectStack) {
    +  var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, '');
    +  var context = objectStack[0];
    +  goog.asserts.assert(goog.isObject(context));
    +  var containerSrs = context['srsName'];
    +  var containerDimension = node.parentNode.getAttribute('srsDimension');
    +  var axisOrientation = 'enu';
    +  if (!goog.isNull(containerSrs)) {
    +    var proj = ol.proj.get(containerSrs);
    +    axisOrientation = proj.getAxisOrientation();
    +  }
    +  var coords = s.split(/[\s,]+/);
    +  // The "dimension" attribute is from the GML 3.0.1 spec.
    +  var dim = 2;
    +  if (!goog.isNull(node.getAttribute('srsDimension'))) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(
    +        node.getAttribute('srsDimension'));
    +  } else if (!goog.isNull(node.getAttribute('dimension'))) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(
    +        node.getAttribute('dimension'));
    +  } else if (!goog.isNull(containerDimension)) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension);
    +  }
    +  var x, y, z;
    +  var flatCoordinates = [];
    +  for (var i = 0, ii = coords.length; i < ii; i += dim) {
    +    x = parseFloat(coords[i]);
    +    y = parseFloat(coords[i + 1]);
    +    z = (dim === 3) ? parseFloat(coords[i + 2]) : 0;
    +    if (axisOrientation.substr(0, 2) === 'en') {
    +      flatCoordinates.push(x, y, z);
    +    } else {
    +      flatCoordinates.push(y, x, z);
    +    }
    +  }
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Extent|undefined} Envelope.
    + */
    +ol.format.GML2.prototype.readBox_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Box');
    +  var flatCoordinates = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>} */ ([null]),
    +      this.BOX_PARSERS_, node, objectStack, this);
    +  return ol.extent.createOrUpdate(flatCoordinates[1][0],
    +      flatCoordinates[1][1], flatCoordinates[1][3],
    +      flatCoordinates[1][4]);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML2.prototype.innerBoundaryIsParser_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'innerBoundaryIs');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      this.RING_PARSERS, node, objectStack, this);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings.push(flatLinearRing);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML2.prototype.outerBoundaryIsParser_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'outerBoundaryIs');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      this.RING_PARSERS, node, objectStack, this);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings[0] = flatLinearRing;
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'coordinates': ol.xml.makeReplacer(
    +        ol.format.GML2.prototype.readFlatCoordinates_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'innerBoundaryIs': ol.format.GML2.prototype.innerBoundaryIsParser_,
    +    'outerBoundaryIs': ol.format.GML2.prototype.outerBoundaryIsParser_
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML2.prototype.BOX_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'coordinates': ol.xml.makeArrayPusher(
    +        ol.format.GML2.prototype.readFlatCoordinates_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML2.prototype.GEOMETRY_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint),
    +    'MultiPoint': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiPoint),
    +    'LineString': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readLineString),
    +    'MultiLineString': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiLineString),
    +    'LinearRing' : ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readLinearRing),
    +    'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon),
    +    'MultiPolygon': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiPolygon),
    +    'Box': ol.xml.makeReplacer(ol.format.GML2.prototype.readBox_)
    +  }
    +});
    diff --git a/mapbox-gl/ol/ol/format/gml/gml3format.js b/mapbox-gl/ol/ol/format/gml/gml3format.js
    new file mode 100644
    index 0000000000..1595f9ada0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/gml/gml3format.js
    @@ -0,0 +1,1350 @@
    +goog.provide('ol.format.GML');
    +goog.provide('ol.format.GML3');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.extent');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.GMLBase');
    +goog.require('ol.format.XSD');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.LinearRing');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the GML format
    + * version 3.1.1.
    + * Currently only supports GML 3.1.1 Simple Features profile.
    + *
    + * @constructor
    + * @param {olx.format.GMLOptions=} opt_options
    + *     Optional configuration object.
    + * @extends {ol.format.GMLBase}
    + * @api
    + */
    +ol.format.GML3 = function(opt_options) {
    +  var options = /** @type {olx.format.GMLOptions} */
    +      (goog.isDef(opt_options) ? opt_options : {});
    +
    +  goog.base(this, options);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.surface_ = goog.isDef(options.surface) ?
    +      options.surface : false;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.curve_ = goog.isDef(options.curve) ?
    +      options.curve : false;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.multiCurve_ = goog.isDef(options.multiCurve) ?
    +      options.multiCurve : true;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.multiSurface_ = goog.isDef(options.multiSurface) ?
    +      options.multiSurface : true;
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.schemaLocation = goog.isDef(options.schemaLocation) ?
    +      options.schemaLocation : ol.format.GML3.schemaLocation_;
    +
    +};
    +goog.inherits(ol.format.GML3, ol.format.GMLBase);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.GML3.schemaLocation_ = ol.format.GMLBase.GMLNS +
    +    ' http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/' +
    +    '1.0.0/gmlsf.xsd';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.MultiLineString|undefined} MultiLineString.
    + */
    +ol.format.GML3.prototype.readMultiCurve_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiCurve');
    +  var lineStrings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.LineString>} */ ([]),
    +      this.MULTICURVE_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(lineStrings)) {
    +    var multiLineString = new ol.geom.MultiLineString(null);
    +    multiLineString.setLineStrings(lineStrings);
    +    return multiLineString;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.MultiPolygon|undefined} MultiPolygon.
    + */
    +ol.format.GML3.prototype.readMultiSurface_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiSurface');
    +  var polygons = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.Polygon>} */ ([]),
    +      this.MULTISURFACE_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(polygons)) {
    +    var multiPolygon = new ol.geom.MultiPolygon(null);
    +    multiPolygon.setPolygons(polygons);
    +    return multiPolygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML3.prototype.curveMemberParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'curveMember' ||
    +      node.localName == 'curveMembers');
    +  ol.xml.parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML3.prototype.surfaceMemberParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'surfaceMember' ||
    +      node.localName == 'surfaceMembers');
    +  ol.xml.parseNode(this.SURFACEMEMBER_PARSERS_,
    +      node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<(Array.<number>)>|undefined} flat coordinates.
    + */
    +ol.format.GML3.prototype.readPatch_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'patches');
    +  return ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([null]),
    +      this.PATCHES_PARSERS_, node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} flat coordinates.
    + */
    +ol.format.GML3.prototype.readSegment_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'segments');
    +  return ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>} */ ([null]),
    +      this.SEGMENTS_PARSERS_, node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<(Array.<number>)>|undefined} flat coordinates.
    + */
    +ol.format.GML3.prototype.readPolygonPatch_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'PolygonPatch');
    +  return ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([null]),
    +      this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} flat coordinates.
    + */
    +ol.format.GML3.prototype.readLineStringSegment_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LineStringSegment');
    +  return ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>} */ ([null]),
    +      this.GEOMETRY_FLAT_COORDINATES_PARSERS_,
    +      node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML3.prototype.interiorParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'interior');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      this.RING_PARSERS, node, objectStack, this);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings.push(flatLinearRing);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GML3.prototype.exteriorParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'exterior');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      this.RING_PARSERS, node, objectStack, this);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings[0] = flatLinearRing;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.Polygon|undefined} Polygon.
    + */
    +ol.format.GML3.prototype.readSurface_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Surface');
    +  var flatLinearRings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([null]),
    +      this.SURFACE_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(flatLinearRings) &&
    +      !goog.isNull(flatLinearRings[0])) {
    +    var polygon = new ol.geom.Polygon(null);
    +    var flatCoordinates = flatLinearRings[0];
    +    var ends = [flatCoordinates.length];
    +    var i, ii;
    +    for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
    +      goog.array.extend(flatCoordinates, flatLinearRings[i]);
    +      ends.push(flatCoordinates.length);
    +    }
    +    polygon.setFlatCoordinates(
    +        ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
    +    return polygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.LineString|undefined} LineString.
    + */
    +ol.format.GML3.prototype.readCurve_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Curve');
    +  var flatCoordinates = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>} */ ([null]),
    +      this.CURVE_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(flatCoordinates)) {
    +    var lineString = new ol.geom.LineString(null);
    +    lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return lineString;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Extent|undefined} Envelope.
    + */
    +ol.format.GML3.prototype.readEnvelope_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Envelope');
    +  var flatCoordinates = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>} */ ([null]),
    +      this.ENVELOPE_PARSERS_, node, objectStack, this);
    +  return ol.extent.createOrUpdate(flatCoordinates[1][0],
    +      flatCoordinates[1][1], flatCoordinates[2][0],
    +      flatCoordinates[2][1]);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} Flat coordinates.
    + */
    +ol.format.GML3.prototype.readFlatPos_ = function(node, objectStack) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  var re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/;
    +  /** @type {Array.<number>} */
    +  var flatCoordinates = [];
    +  var m;
    +  while ((m = re.exec(s))) {
    +    flatCoordinates.push(parseFloat(m[1]));
    +    s = s.substr(m[0].length);
    +  }
    +  if (s !== '') {
    +    return undefined;
    +  }
    +  var context = objectStack[0];
    +  goog.asserts.assert(goog.isObject(context));
    +  var containerSrs = context['srsName'];
    +  var axisOrientation = 'enu';
    +  if (!goog.isNull(containerSrs)) {
    +    var proj = ol.proj.get(containerSrs);
    +    axisOrientation = proj.getAxisOrientation();
    +  }
    +  if (axisOrientation === 'neu') {
    +    var i, ii;
    +    for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) {
    +      var y = flatCoordinates[i];
    +      var x = flatCoordinates[i + 1];
    +      flatCoordinates[i] = x;
    +      flatCoordinates[i + 1] = y;
    +    }
    +  }
    +  var len = flatCoordinates.length;
    +  if (len == 2) {
    +    flatCoordinates.push(0);
    +  }
    +  if (len === 0) {
    +    return undefined;
    +  }
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} Flat coordinates.
    + */
    +ol.format.GML3.prototype.readFlatPosList_ = function(node, objectStack) {
    +  var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, '');
    +  var context = objectStack[0];
    +  goog.asserts.assert(goog.isObject(context));
    +  var containerSrs = context['srsName'];
    +  var containerDimension = node.parentNode.getAttribute('srsDimension');
    +  var axisOrientation = 'enu';
    +  if (!goog.isNull(containerSrs)) {
    +    var proj = ol.proj.get(containerSrs);
    +    axisOrientation = proj.getAxisOrientation();
    +  }
    +  var coords = s.split(/\s+/);
    +  // The "dimension" attribute is from the GML 3.0.1 spec.
    +  var dim = 2;
    +  if (!goog.isNull(node.getAttribute('srsDimension'))) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(
    +        node.getAttribute('srsDimension'));
    +  } else if (!goog.isNull(node.getAttribute('dimension'))) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(
    +        node.getAttribute('dimension'));
    +  } else if (!goog.isNull(containerDimension)) {
    +    dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension);
    +  }
    +  var x, y, z;
    +  var flatCoordinates = [];
    +  for (var i = 0, ii = coords.length; i < ii; i += dim) {
    +    x = parseFloat(coords[i]);
    +    y = parseFloat(coords[i + 1]);
    +    z = (dim === 3) ? parseFloat(coords[i + 2]) : 0;
    +    if (axisOrientation.substr(0, 2) === 'en') {
    +      flatCoordinates.push(x, y, z);
    +    } else {
    +      flatCoordinates.push(y, x, z);
    +    }
    +  }
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'pos': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPos_),
    +    'posList': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPosList_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'interior': ol.format.GML3.prototype.interiorParser_,
    +    'exterior': ol.format.GML3.prototype.exteriorParser_
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.GEOMETRY_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint),
    +    'MultiPoint': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiPoint),
    +    'LineString': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readLineString),
    +    'MultiLineString': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiLineString),
    +    'LinearRing' : ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readLinearRing),
    +    'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon),
    +    'MultiPolygon': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readMultiPolygon),
    +    'Surface': ol.xml.makeReplacer(ol.format.GML3.prototype.readSurface_),
    +    'MultiSurface': ol.xml.makeReplacer(
    +        ol.format.GML3.prototype.readMultiSurface_),
    +    'Curve': ol.xml.makeReplacer(ol.format.GML3.prototype.readCurve_),
    +    'MultiCurve': ol.xml.makeReplacer(
    +        ol.format.GML3.prototype.readMultiCurve_),
    +    'Envelope': ol.xml.makeReplacer(ol.format.GML3.prototype.readEnvelope_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.MULTICURVE_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'curveMember': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.curveMemberParser_),
    +    'curveMembers': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.curveMemberParser_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.MULTISURFACE_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'surfaceMember': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.surfaceMemberParser_),
    +    'surfaceMembers': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.surfaceMemberParser_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.CURVEMEMBER_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'LineString': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.readLineString),
    +    'Curve': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readCurve_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.SURFACEMEMBER_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'Polygon': ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readPolygon),
    +    'Surface': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readSurface_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.SURFACE_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'patches': ol.xml.makeReplacer(ol.format.GML3.prototype.readPatch_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.CURVE_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'segments': ol.xml.makeReplacer(ol.format.GML3.prototype.readSegment_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.ENVELOPE_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'lowerCorner': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.readFlatPosList_),
    +    'upperCorner': ol.xml.makeArrayPusher(
    +        ol.format.GML3.prototype.readFlatPosList_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.PATCHES_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'PolygonPatch': ol.xml.makeReplacer(
    +        ol.format.GML3.prototype.readPolygonPatch_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GML3.prototype.SEGMENTS_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'LineStringSegment': ol.xml.makeReplacer(
    +        ol.format.GML3.prototype.readLineStringSegment_)
    +  }
    +});
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Point} value Point geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  var axisOrientation = 'enu';
    +  if (goog.isDefAndNotNull(srsName)) {
    +    axisOrientation = ol.proj.get(srsName).getAxisOrientation();
    +  }
    +  var point = value.getCoordinates();
    +  var coords;
    +  // only 2d for simple features profile
    +  if (axisOrientation.substr(0, 2) === 'en') {
    +    coords = (point[0] + ' ' + point[1]);
    +  } else {
    +    coords = (point[1] + ' ' + point[0]);
    +  }
    +  ol.format.XSD.writeStringTextNode(node, coords);
    +};
    +
    +
    +/**
    + * @param {Array.<number>} point Point geometry.
    + * @param {string=} opt_srsName Optional srsName
    + * @return {string}
    + * @private
    + */
    +ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName) {
    +  var axisOrientation = 'enu';
    +  if (goog.isDefAndNotNull(opt_srsName)) {
    +    axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation();
    +  }
    +  return ((axisOrientation.substr(0, 2) === 'en') ?
    +      point[0] + ' ' + point[1] :
    +      point[1] + ' ' + point[0]);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  // only 2d for simple features profile
    +  var points = value.getCoordinates();
    +  var len = points.length;
    +  var parts = new Array(len);
    +  var point;
    +  for (var i = 0; i < len; ++i) {
    +    point = points[i];
    +    parts[i] = this.getCoords_(point, srsName);
    +  }
    +  ol.format.XSD.writeStringTextNode(node, parts.join(' '));
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Point} geometry Point geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writePoint_ = function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var pos = ol.xml.createElementNS(node.namespaceURI, 'pos');
    +  node.appendChild(pos);
    +  this.writePos_(pos, geometry, objectStack);
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.ENVELOPE_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +    'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Extent} extent Extent.
    + * @param {Array.<*>} objectStack Node stack.
    + */
    +ol.format.GML3.prototype.writeEnvelope = function(node, extent, objectStack) {
    +  goog.asserts.assert(extent.length == 4);
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (goog.isDef(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var keys = ['lowerCorner', 'upperCorner'];
    +  var values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]];
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      ({node: node}), ol.format.GML3.ENVELOPE_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      values,
    +      objectStack, keys, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LinearRing} geometry LinearRing geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeLinearRing_ =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var posList = ol.xml.createElementNS(node.namespaceURI, 'posList');
    +  node.appendChild(posList);
    +  this.writePosList_(posList, geometry, objectStack);
    +};
    +
    +
    +/**
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node} Node.
    + * @private
    + */
    +ol.format.GML3.prototype.RING_NODE_FACTORY_ =
    +    function(value, objectStack, opt_nodeName) {
    +  var context = objectStack[objectStack.length - 1];
    +  var parentNode = context.node;
    +  goog.asserts.assert(goog.isObject(context));
    +  var exteriorWritten = context['exteriorWritten'];
    +  if (!goog.isDef(exteriorWritten)) {
    +    context['exteriorWritten'] = true;
    +  }
    +  return ol.xml.createElementNS(parentNode.namespaceURI,
    +      goog.isDef(exteriorWritten) ? 'interior' : 'exterior');
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Polygon} geometry Polygon geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeSurfaceOrPolygon_ =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (node.nodeName !== 'PolygonPatch' && goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') {
    +    var rings = geometry.getLinearRings();
    +    ol.xml.pushSerializeAndPop(
    +        {node: node, srsName: srsName},
    +        ol.format.GML3.RING_SERIALIZERS_,
    +        this.RING_NODE_FACTORY_,
    +        rings, objectStack, undefined, this);
    +  } else if (node.nodeName === 'Surface') {
    +    var patches = ol.xml.createElementNS(node.namespaceURI, 'patches');
    +    node.appendChild(patches);
    +    this.writeSurfacePatches_(
    +        patches, geometry, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LineString} geometry LineString geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeCurveOrLineString_ =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (node.nodeName !== 'LineStringSegment' &&
    +      goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  if (node.nodeName === 'LineString' ||
    +      node.nodeName === 'LineStringSegment') {
    +    var posList = ol.xml.createElementNS(node.namespaceURI, 'posList');
    +    node.appendChild(posList);
    +    this.writePosList_(posList, geometry, objectStack);
    +  } else if (node.nodeName === 'Curve') {
    +    var segments = ol.xml.createElementNS(node.namespaceURI, 'segments');
    +    node.appendChild(segments);
    +    this.writeCurveSegments_(segments,
    +        geometry, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_ =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  var surface = context['surface'];
    +  if (goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var polygons = geometry.getPolygons();
    +  ol.xml.pushSerializeAndPop({node: node, srsName: srsName, surface: surface},
    +      ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_,
    +      this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons,
    +      objectStack, undefined, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.MultiPoint} geometry MultiPoint geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry,
    +    objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  if (goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var points = geometry.getPoints();
    +  ol.xml.pushSerializeAndPop({node: node, srsName: srsName},
    +      ol.format.GML3.POINTMEMBER_SERIALIZERS_,
    +      ol.xml.makeSimpleNodeFactory('pointMember'), points,
    +      objectStack, undefined, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.MultiLineString} geometry MultiLineString geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeMultiCurveOrLineString_ =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var srsName = context['srsName'];
    +  var curve = context['curve'];
    +  if (goog.isDefAndNotNull(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  var lines = geometry.getLineStrings();
    +  ol.xml.pushSerializeAndPop({node: node, srsName: srsName, curve: curve},
    +      ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_,
    +      this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines,
    +      objectStack, undefined, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LinearRing} ring LinearRing geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeRing_ = function(node, ring, objectStack) {
    +  var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing');
    +  node.appendChild(linearRing);
    +  this.writeLinearRing_(linearRing, ring, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Polygon} polygon Polygon geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeSurfaceOrPolygonMember_ =
    +    function(node, polygon, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var child = this.GEOMETRY_NODE_FACTORY_(
    +      polygon, objectStack);
    +  if (goog.isDef(child)) {
    +    node.appendChild(child);
    +    this.writeSurfaceOrPolygon_(child, polygon, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Point} point Point geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writePointMember_ =
    +    function(node, point, objectStack) {
    +  var child = ol.xml.createElementNS(node.namespaceURI, 'Point');
    +  node.appendChild(child);
    +  this.writePoint_(child, point, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LineString} line LineString geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeLineStringOrCurveMember_ =
    +    function(node, line, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var child = this.GEOMETRY_NODE_FACTORY_(line, objectStack);
    +  if (goog.isDef(child)) {
    +    node.appendChild(child);
    +    this.writeCurveOrLineString_(child, line, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Polygon} polygon Polygon geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeSurfacePatches_ =
    +    function(node, polygon, objectStack) {
    +  var child = ol.xml.createElementNS(node.namespaceURI, 'PolygonPatch');
    +  node.appendChild(child);
    +  this.writeSurfaceOrPolygon_(child, polygon, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LineString} line LineString geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeCurveSegments_ =
    +    function(node, line, objectStack) {
    +  var child = ol.xml.createElementNS(node.namespaceURI,
    +      'LineStringSegment');
    +  node.appendChild(child);
    +  this.writeCurveOrLineString_(child, line, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Geometry|ol.Extent} geometry Geometry.
    + * @param {Array.<*>} objectStack Node stack.
    + */
    +ol.format.GML3.prototype.writeGeometryElement =
    +    function(node, geometry, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var item = goog.object.clone(context);
    +  item.node = node;
    +  var value;
    +  if (goog.isArray(geometry)) {
    +    if (goog.isDef(context.dataProjection)) {
    +      value = ol.proj.transformExtent(
    +          geometry, context.featureProjection, context.dataProjection);
    +    } else {
    +      value = geometry;
    +    }
    +  } else {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Geometry);
    +    value =
    +        ol.format.Feature.transformWithOptions(geometry, true, context);
    +  }
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      (item), ol.format.GML3.GEOMETRY_SERIALIZERS_,
    +      this.GEOMETRY_NODE_FACTORY_, [value],
    +      objectStack, undefined, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Node stack.
    + */
    +ol.format.GML3.prototype.writeFeatureElement =
    +    function(node, feature, objectStack) {
    +  var fid = feature.getId();
    +  if (goog.isDef(fid)) {
    +    node.setAttribute('fid', fid);
    +  }
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featureNS = context['featureNS'];
    +  var geometryName = feature.getGeometryName();
    +  if (!goog.isDef(context.serializers)) {
    +    context.serializers = {};
    +    context.serializers[featureNS] = {};
    +  }
    +  var properties = feature.getProperties();
    +  var keys = [], values = [];
    +  for (var key in properties) {
    +    var value = properties[key];
    +    if (!goog.isNull(value)) {
    +      keys.push(key);
    +      values.push(value);
    +      if (key == geometryName) {
    +        if (!(key in context.serializers[featureNS])) {
    +          context.serializers[featureNS][key] = ol.xml.makeChildAppender(
    +              this.writeGeometryElement, this);
    +        }
    +      } else {
    +        if (!(key in context.serializers[featureNS])) {
    +          context.serializers[featureNS][key] = ol.xml.makeChildAppender(
    +              ol.format.XSD.writeStringTextNode);
    +        }
    +      }
    +    }
    +  }
    +  var item = goog.object.clone(context);
    +  item.node = node;
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      (item), context.serializers,
    +      ol.xml.makeSimpleNodeFactory(undefined, featureNS),
    +      values,
    +      objectStack, keys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GML3.prototype.writeFeatureMembers_ =
    +    function(node, features, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featureType = context['featureType'];
    +  var featureNS = context['featureNS'];
    +  var serializers = {};
    +  serializers[featureNS] = {};
    +  serializers[featureNS][featureType] = ol.xml.makeChildAppender(
    +      this.writeFeatureElement, this);
    +  var item = goog.object.clone(context);
    +  item.node = node;
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      (item),
    +      serializers,
    +      ol.xml.makeSimpleNodeFactory(featureType, featureNS), features,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'surfaceMember': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeSurfaceOrPolygonMember_),
    +    'polygonMember': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeSurfaceOrPolygonMember_)
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.POINTMEMBER_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'pointMember': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writePointMember_)
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'lineStringMember': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeLineStringOrCurveMember_),
    +    'curveMember': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeLineStringOrCurveMember_)
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.RING_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'exterior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_),
    +    'interior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_)
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GML3.GEOMETRY_SERIALIZERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'Curve': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeCurveOrLineString_),
    +    'MultiCurve': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeMultiCurveOrLineString_),
    +    'Point': ol.xml.makeChildAppender(ol.format.GML3.prototype.writePoint_),
    +    'MultiPoint': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeMultiPoint_),
    +    'LineString': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeCurveOrLineString_),
    +    'MultiLineString': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeMultiCurveOrLineString_),
    +    'LinearRing': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeLinearRing_),
    +    'Polygon': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeSurfaceOrPolygon_),
    +    'MultiPolygon': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_),
    +    'Surface': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeSurfaceOrPolygon_),
    +    'MultiSurface': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_),
    +    'Envelope': ol.xml.makeChildAppender(
    +        ol.format.GML3.prototype.writeEnvelope)
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, string>}
    + * @private
    + */
    +ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = {
    +  'MultiLineString': 'lineStringMember',
    +  'MultiCurve': 'curveMember',
    +  'MultiPolygon': 'polygonMember',
    +  'MultiSurface': 'surfaceMember'
    +};
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.GML3.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ =
    +    function(value, objectStack, opt_nodeName) {
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  goog.asserts.assert(ol.xml.isNode(parentNode));
    +  return ol.xml.createElementNS('http://www.opengis.net/gml',
    +      ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]);
    +};
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.GML3.prototype.GEOMETRY_NODE_FACTORY_ =
    +    function(value, objectStack, opt_nodeName) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var multiSurface = context['multiSurface'];
    +  var surface = context['surface'];
    +  var curve = context['curve'];
    +  var multiCurve = context['multiCurve'];
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  goog.asserts.assert(ol.xml.isNode(parentNode));
    +  var nodeName;
    +  if (!goog.isArray(value)) {
    +    goog.asserts.assertInstanceof(value, ol.geom.Geometry);
    +    nodeName = value.getType();
    +    if (nodeName === 'MultiPolygon' && multiSurface === true) {
    +      nodeName = 'MultiSurface';
    +    } else if (nodeName === 'Polygon' && surface === true) {
    +      nodeName = 'Surface';
    +    } else if (nodeName === 'LineString' && curve === true) {
    +      nodeName = 'Curve';
    +    } else if (nodeName === 'MultiLineString' && multiCurve === true) {
    +      nodeName = 'MultiCurve';
    +    }
    +  } else {
    +    nodeName = 'Envelope';
    +  }
    +  return ol.xml.createElementNS('http://www.opengis.net/gml',
    +      nodeName);
    +};
    +
    +
    +/**
    + * Encode a geometry in GML 3.1.1 Simple Features.
    + *
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + * @api
    + */
    +ol.format.GML3.prototype.writeGeometryNode = function(geometry, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  var geom = ol.xml.createElementNS('http://www.opengis.net/gml', 'geom');
    +  var context = {node: geom, srsName: this.srsName,
    +    curve: this.curve_, surface: this.surface_,
    +    multiSurface: this.multiSurface_, multiCurve: this.multiCurve_};
    +  if (goog.isDef(opt_options)) {
    +    goog.object.extend(context, opt_options);
    +  }
    +  this.writeGeometryElement(geom, geometry, [context]);
    +  return geom;
    +};
    +
    +
    +/**
    + * Encode an array of features in GML 3.1.1 Simple Features.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {string} Result.
    + * @api stable
    + */
    +ol.format.GML3.prototype.writeFeatures;
    +
    +
    +/**
    + * Encode an array of features in the GML 3.1.1 format as an XML node.
    + *
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + * @api
    + */
    +ol.format.GML3.prototype.writeFeaturesNode = function(features, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  var node = ol.xml.createElementNS('http://www.opengis.net/gml',
    +      'featureMembers');
    +  ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
    +      'xsi:schemaLocation', this.schemaLocation);
    +  var context = {
    +    srsName: this.srsName,
    +    curve: this.curve_,
    +    surface: this.surface_,
    +    multiSurface: this.multiSurface_,
    +    multiCurve: this.multiCurve_,
    +    featureNS: this.featureNS,
    +    featureType: this.featureType
    +  };
    +  if (goog.isDef(opt_options)) {
    +    goog.object.extend(context, opt_options);
    +  }
    +  this.writeFeatureMembers_(node, features, [context]);
    +  return node;
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the GML format
    + * version 3.1.1.
    + * Currently only supports GML 3.1.1 Simple Features profile.
    + *
    + * @constructor
    + * @param {olx.format.GMLOptions=} opt_options
    + *     Optional configuration object.
    + * @extends {ol.format.GMLBase}
    + * @api stable
    + */
    +ol.format.GML = ol.format.GML3;
    +
    +
    +/**
    + * Encode an array of features in GML 3.1.1 Simple Features.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {string} Result.
    + * @api stable
    + */
    +ol.format.GML.prototype.writeFeatures;
    +
    +
    +/**
    + * Encode an array of features in the GML 3.1.1 format as an XML node.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + * @api
    + */
    +ol.format.GML.prototype.writeFeaturesNode;
    diff --git a/mapbox-gl/ol/ol/format/gml/gmlbaseformat.js b/mapbox-gl/ol/ol/format/gml/gmlbaseformat.js
    new file mode 100644
    index 0000000000..97333bf651
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/gml/gmlbaseformat.js
    @@ -0,0 +1,599 @@
    +// FIXME Envelopes should not be treated as geometries! readEnvelope_ is part
    +// of GEOMETRY_PARSERS_ and methods using GEOMETRY_PARSERS_ do not expect
    +// envelopes/extents, only geometries!
    +goog.provide('ol.format.GMLBase');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.LinearRing');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Feature base format for reading and writing data in the GML format.
    + * This class cannot be instantiate, it contains only base content that
    + * is shared with versioned format classes ol.format.GML2 and
    + * ol.format.GML3.
    + *
    + * @constructor
    + * @param {olx.format.GMLOptions=} opt_options
    + *     Optional configuration object.
    + * @extends {ol.format.XMLFeature}
    + * @api
    + */
    +ol.format.GMLBase = function(opt_options) {
    +  var options = /** @type {olx.format.GMLOptions} */
    +      (goog.isDef(opt_options) ? opt_options : {});
    +
    +  /**
    +   * @protected
    +   * @type {Array.<string>|string|undefined}
    +   */
    +  this.featureType = options.featureType;
    +
    +  /**
    +   * @protected
    +   * @type {Object.<string, string>|string|undefined}
    +   */
    +  this.featureNS = options.featureNS;
    +
    +  /**
    +   * @protected
    +   * @type {string}
    +   */
    +  this.srsName = options.srsName;
    +
    +  /**
    +   * @protected
    +   * @type {string}
    +   */
    +  this.schemaLocation = '';
    +
    +  /**
    +   * @type {Object.<string, Object.<string, Object>>}
    +   */
    +  this.FEATURE_COLLECTION_PARSERS = {};
    +  this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS] = {
    +    'featureMember': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readFeaturesInternal),
    +    'featureMembers': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readFeaturesInternal)
    +  };
    +
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.GMLBase, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.GMLBase.GMLNS = 'http://www.opengis.net/gml';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.GMLBase.prototype.readFeaturesInternal = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var localName = ol.xml.getLocalName(node);
    +  var features;
    +  if (localName == 'FeatureCollection') {
    +    features = ol.xml.pushParseAndPop(null,
    +        this.FEATURE_COLLECTION_PARSERS, node,
    +        objectStack, this);
    +  } else if (localName == 'featureMembers' || localName == 'featureMember') {
    +    var context = objectStack[0];
    +    goog.asserts.assert(goog.isObject(context));
    +    var featureType = context['featureType'];
    +    var featureNS = context['featureNS'];
    +    var i, ii, prefix = 'p', defaultPrefix = 'p0';
    +    if (!goog.isDef(featureType) && goog.isDefAndNotNull(node.childNodes)) {
    +      featureType = [], featureNS = {};
    +      for (i = 0, ii = node.childNodes.length; i < ii; ++i) {
    +        var child = node.childNodes[i];
    +        if (child.nodeType === 1) {
    +          var ft = child.nodeName.split(':').pop();
    +          if (goog.array.indexOf(featureType, ft) === -1) {
    +            var key;
    +            if (!goog.object.contains(featureNS, child.namespaceURI)) {
    +              key = prefix + goog.object.getCount(featureNS);
    +              featureNS[key] = child.namespaceURI;
    +            } else {
    +              key = goog.object.findKey(featureNS, function(value) {
    +                return value === child.namespaceURI;
    +              });
    +            }
    +            featureType.push(key + ':' + ft);
    +          }
    +        }
    +      }
    +      context['featureType'] = featureType;
    +      context['featureNS'] = featureNS;
    +    }
    +    if (goog.isString(featureNS)) {
    +      var ns = featureNS;
    +      featureNS = {};
    +      featureNS[defaultPrefix] = ns;
    +    }
    +    var parsersNS = {};
    +    var featureTypes = goog.isArray(featureType) ? featureType : [featureType];
    +    for (var p in featureNS) {
    +      var parsers = {};
    +      for (i = 0, ii = featureTypes.length; i < ii; ++i) {
    +        var featurePrefix = featureTypes[i].indexOf(':') === -1 ?
    +            defaultPrefix : featureTypes[i].split(':')[0];
    +        if (featurePrefix === p) {
    +          parsers[featureTypes[i].split(':').pop()] =
    +              (localName == 'featureMembers') ?
    +              ol.xml.makeArrayPusher(this.readFeatureElement, this) :
    +              ol.xml.makeReplacer(this.readFeatureElement, this);
    +        }
    +      }
    +      parsersNS[featureNS[p]] = parsers;
    +    }
    +    features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack);
    +  }
    +  if (!goog.isDef(features)) {
    +    features = [];
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.Geometry|undefined} Geometry.
    + */
    +ol.format.GMLBase.prototype.readGeometryElement = function(node, objectStack) {
    +  var context = objectStack[0];
    +  goog.asserts.assert(goog.isObject(context));
    +  context['srsName'] = node.firstElementChild.getAttribute('srsName');
    +  var geometry = ol.xml.pushParseAndPop(/** @type {ol.geom.Geometry} */(null),
    +      this.GEOMETRY_PARSERS_, node, objectStack, this);
    +  if (goog.isDefAndNotNull(geometry)) {
    +    return /** @type {ol.geom.Geometry} */ (
    +        ol.format.Feature.transformWithOptions(geometry, false, context));
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.GMLBase.prototype.readFeatureElement = function(node, objectStack) {
    +  var n;
    +  var fid = node.getAttribute('fid') ||
    +      ol.xml.getAttributeNS(node, ol.format.GMLBase.GMLNS, 'id');
    +  var values = {}, geometryName;
    +  for (n = node.firstElementChild; !goog.isNull(n);
    +      n = n.nextElementSibling) {
    +    var localName = ol.xml.getLocalName(n);
    +    // Assume attribute elements have one child node and that the child
    +    // is a text node.  Otherwise assume it is a geometry node.
    +    if (n.childNodes.length === 0 ||
    +        (n.childNodes.length === 1 &&
    +        n.firstChild.nodeType === 3)) {
    +      var value = ol.xml.getAllTextContent(n, false);
    +      if (goog.string.isEmpty(value)) {
    +        value = undefined;
    +      }
    +      values[localName] = value;
    +    } else {
    +      // boundedBy is an extent and must not be considered as a geometry
    +      if (localName !== 'boundedBy') {
    +        geometryName = localName;
    +      }
    +      values[localName] = this.readGeometryElement(n, objectStack);
    +    }
    +  }
    +  var feature = new ol.Feature(values);
    +  if (goog.isDef(geometryName)) {
    +    feature.setGeometryName(geometryName);
    +  }
    +  if (fid) {
    +    feature.setId(fid);
    +  }
    +  return feature;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.Point|undefined} Point.
    + */
    +ol.format.GMLBase.prototype.readPoint = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Point');
    +  var flatCoordinates =
    +      this.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDefAndNotNull(flatCoordinates)) {
    +    var point = new ol.geom.Point(null);
    +    goog.asserts.assert(flatCoordinates.length == 3);
    +    point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return point;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.MultiPoint|undefined} MultiPoint.
    + */
    +ol.format.GMLBase.prototype.readMultiPoint = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiPoint');
    +  var coordinates = ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([]),
    +      this.MULTIPOINT_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(coordinates)) {
    +    return new ol.geom.MultiPoint(coordinates);
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.MultiLineString|undefined} MultiLineString.
    + */
    +ol.format.GMLBase.prototype.readMultiLineString = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiLineString');
    +  var lineStrings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.LineString>} */ ([]),
    +      this.MULTILINESTRING_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(lineStrings)) {
    +    var multiLineString = new ol.geom.MultiLineString(null);
    +    multiLineString.setLineStrings(lineStrings);
    +    return multiLineString;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.MultiPolygon|undefined} MultiPolygon.
    + */
    +ol.format.GMLBase.prototype.readMultiPolygon = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiPolygon');
    +  var polygons = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.Polygon>} */ ([]),
    +      this.MULTIPOLYGON_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(polygons)) {
    +    var multiPolygon = new ol.geom.MultiPolygon(null);
    +    multiPolygon.setPolygons(polygons);
    +    return multiPolygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GMLBase.prototype.pointMemberParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'pointMember' ||
    +      node.localName == 'pointMembers');
    +  ol.xml.parseNode(this.POINTMEMBER_PARSERS_,
    +      node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GMLBase.prototype.lineStringMemberParser_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'lineStringMember' ||
    +      node.localName == 'lineStringMembers');
    +  ol.xml.parseNode(this.LINESTRINGMEMBER_PARSERS_,
    +      node, objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GMLBase.prototype.polygonMemberParser_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'polygonMember' ||
    +      node.localName == 'polygonMembers');
    +  ol.xml.parseNode(this.POLYGONMEMBER_PARSERS_, node,
    +      objectStack, this);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.LineString|undefined} LineString.
    + */
    +ol.format.GMLBase.prototype.readLineString = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LineString');
    +  var flatCoordinates =
    +      this.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDefAndNotNull(flatCoordinates)) {
    +    var lineString = new ol.geom.LineString(null);
    +    lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return lineString;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>|undefined} LinearRing flat coordinates.
    + */
    +ol.format.GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LinearRing');
    +  var ring = ol.xml.pushParseAndPop(/** @type {Array.<number>} */(null),
    +      this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
    +      objectStack, this);
    +  if (goog.isDefAndNotNull(ring)) {
    +    return ring;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.LinearRing|undefined} LinearRing.
    + */
    +ol.format.GMLBase.prototype.readLinearRing = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LinearRing');
    +  var flatCoordinates =
    +      this.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDef(flatCoordinates)) {
    +    var ring = new ol.geom.LinearRing(null);
    +    ring.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return ring;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.geom.Polygon|undefined} Polygon.
    + */
    +ol.format.GMLBase.prototype.readPolygon = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Polygon');
    +  var flatLinearRings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([null]),
    +      this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
    +  if (goog.isDef(flatLinearRings) &&
    +      !goog.isNull(flatLinearRings[0])) {
    +    var polygon = new ol.geom.Polygon(null);
    +    var flatCoordinates = flatLinearRings[0];
    +    var ends = [flatCoordinates.length];
    +    var i, ii;
    +    for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
    +      goog.array.extend(flatCoordinates, flatLinearRings[i]);
    +      ends.push(flatCoordinates.length);
    +    }
    +    polygon.setFlatCoordinates(
    +        ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
    +    return polygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return /** @type {Array.<number>} */ (ol.xml.pushParseAndPop(
    +      null,
    +      this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
    +      objectStack, this));
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.MULTIPOINT_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'pointMember': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.pointMemberParser_),
    +    'pointMembers': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.pointMemberParser_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.MULTILINESTRING_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'lineStringMember': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.lineStringMemberParser_),
    +    'lineStringMembers': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.lineStringMemberParser_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.MULTIPOLYGON_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'polygonMember': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.polygonMemberParser_),
    +    'polygonMembers': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.polygonMemberParser_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.POINTMEMBER_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'Point': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'LineString': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.readLineString)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GMLBase.prototype.POLYGONMEMBER_PARSERS_ = Object({
    +  'http://www.opengis.net/gml' : {
    +    'Polygon': ol.xml.makeArrayPusher(
    +        ol.format.GMLBase.prototype.readPolygon)
    +  }
    +});
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @protected
    + */
    +ol.format.GMLBase.prototype.RING_PARSERS = Object({
    +  'http://www.opengis.net/gml' : {
    +    'LinearRing': ol.xml.makeReplacer(
    +        ol.format.GMLBase.prototype.readFlatLinearRing_)
    +  }
    +});
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GMLBase.prototype.readGeometryFromNode =
    +    function(node, opt_options) {
    +  var geometry = this.readGeometryElement(node,
    +      [this.getReadOptions(node, goog.isDef(opt_options) ? opt_options : {})]);
    +  return goog.isDef(geometry) ? geometry : null;
    +};
    +
    +
    +/**
    + * Read all features from a GML FeatureCollection.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.GMLBase.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GMLBase.prototype.readFeaturesFromNode =
    +    function(node, opt_options) {
    +  var options = {
    +    featureType: this.featureType,
    +    featureNS: this.featureNS
    +  };
    +  if (goog.isDef(opt_options)) {
    +    goog.object.extend(options, this.getReadOptions(node, opt_options));
    +  }
    +  return this.readFeaturesInternal(node, [options]);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GMLBase.prototype.readProjectionFromNode = function(node) {
    +  return ol.proj.get(goog.isDef(this.srsName_) ? this.srsName_ :
    +      node.firstElementChild.getAttribute('srsName'));
    +};
    diff --git a/mapbox-gl/ol/ol/format/gpxformat.js b/mapbox-gl/ol/ol/format/gpxformat.js
    new file mode 100644
    index 0000000000..ec234de049
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/gpxformat.js
    @@ -0,0 +1,876 @@
    +goog.provide('ol.format.GPX');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.format.XSD');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.Point');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the GPX format.
    + *
    + * @constructor
    + * @extends {ol.format.XMLFeature}
    + * @param {olx.format.GPXOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.format.GPX = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get('EPSG:4326');
    +
    +  /**
    +   * @type {function(ol.Feature, Node)|undefined}
    +   * @private
    +   */
    +  this.readExtensions_ = options.readExtensions;
    +};
    +goog.inherits(ol.format.GPX, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.format.GPX.NAMESPACE_URIS_ = [
    +  null,
    +  'http://www.topografix.com/GPX/1/0',
    +  'http://www.topografix.com/GPX/1/1'
    +];
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {Node} node Node.
    + * @param {Object} values Values.
    + * @private
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  flatCoordinates.push(
    +      parseFloat(node.getAttribute('lon')),
    +      parseFloat(node.getAttribute('lat')));
    +  if (goog.object.containsKey(values, 'ele')) {
    +    flatCoordinates.push(
    +        /** @type {number} */ (values['ele']));
    +    goog.object.remove(values, 'ele');
    +  } else {
    +    flatCoordinates.push(0);
    +  }
    +  if (goog.object.containsKey(values, 'time')) {
    +    flatCoordinates.push(
    +        /** @type {number} */ (values['time']));
    +    goog.object.remove(values, 'time');
    +  } else {
    +    flatCoordinates.push(0);
    +  }
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.parseLink_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'link');
    +  var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  var href = node.getAttribute('href');
    +  if (!goog.isNull(href)) {
    +    values['link'] = href;
    +  }
    +  ol.xml.parseNode(ol.format.GPX.LINK_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.parseExtensions_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'extensions');
    +  var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  values['extensionsNode_'] = node;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.parseRtePt_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'rtept');
    +  var values = ol.xml.pushParseAndPop(
    +      {}, ol.format.GPX.RTEPT_PARSERS_, node, objectStack);
    +  if (goog.isDef(values)) {
    +    var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +    var flatCoordinates = /** @type {Array.<number>} */
    +        (rteValues['flatCoordinates']);
    +    ol.format.GPX.appendCoordinate_(flatCoordinates, node, values);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.parseTrkPt_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'trkpt');
    +  var values = ol.xml.pushParseAndPop(
    +      {}, ol.format.GPX.TRKPT_PARSERS_, node, objectStack);
    +  if (goog.isDef(values)) {
    +    var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +    var flatCoordinates = /** @type {Array.<number>} */
    +        (trkValues['flatCoordinates']);
    +    ol.format.GPX.appendCoordinate_(flatCoordinates, node, values);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.parseTrkSeg_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'trkseg');
    +  var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  ol.xml.parseNode(ol.format.GPX.TRKSEG_PARSERS_, node, objectStack);
    +  var flatCoordinates = /** @type {Array.<number>} */
    +      (values['flatCoordinates']);
    +  var ends = /** @type {Array.<number>} */ (values['ends']);
    +  ends.push(flatCoordinates.length);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Track.
    + */
    +ol.format.GPX.readRte_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'rte');
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  var values = ol.xml.pushParseAndPop({
    +    'flatCoordinates': []
    +  }, ol.format.GPX.RTE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(values)) {
    +    return undefined;
    +  }
    +  var flatCoordinates = /** @type {Array.<number>} */
    +      (values['flatCoordinates']);
    +  goog.object.remove(values, 'flatCoordinates');
    +  var geometry = new ol.geom.LineString(null);
    +  geometry.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates);
    +  ol.format.Feature.transformWithOptions(geometry, false, options);
    +  var feature = new ol.Feature(geometry);
    +  feature.setProperties(values);
    +  return feature;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Track.
    + */
    +ol.format.GPX.readTrk_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'trk');
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  var values = ol.xml.pushParseAndPop({
    +    'flatCoordinates': [],
    +    'ends': []
    +  }, ol.format.GPX.TRK_PARSERS_, node, objectStack);
    +  if (!goog.isDef(values)) {
    +    return undefined;
    +  }
    +  var flatCoordinates = /** @type {Array.<number>} */
    +      (values['flatCoordinates']);
    +  goog.object.remove(values, 'flatCoordinates');
    +  var ends = /** @type {Array.<number>} */ (values['ends']);
    +  goog.object.remove(values, 'ends');
    +  var geometry = new ol.geom.MultiLineString(null);
    +  geometry.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XYZM, flatCoordinates, ends);
    +  ol.format.Feature.transformWithOptions(geometry, false, options);
    +  var feature = new ol.Feature(geometry);
    +  feature.setProperties(values);
    +  return feature;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Waypoint.
    + */
    +ol.format.GPX.readWpt_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'wpt');
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  var values = ol.xml.pushParseAndPop(
    +      {}, ol.format.GPX.WPT_PARSERS_, node, objectStack);
    +  if (!goog.isDef(values)) {
    +    return undefined;
    +  }
    +  var coordinates = ol.format.GPX.appendCoordinate_([], node, values);
    +  var geometry = new ol.geom.Point(
    +      coordinates, ol.geom.GeometryLayout.XYZM);
    +  ol.format.Feature.transformWithOptions(geometry, false, options);
    +  var feature = new ol.Feature(geometry);
    +  feature.setProperties(values);
    +  return feature;
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, function(Node, Array.<*>): (ol.Feature|undefined)>}
    + * @private
    + */
    +ol.format.GPX.FEATURE_READER_ = {
    +  'rte': ol.format.GPX.readRte_,
    +  'trk': ol.format.GPX.readTrk_,
    +  'wpt': ol.format.GPX.readWpt_
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.GPX_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'rte': ol.xml.makeArrayPusher(ol.format.GPX.readRte_),
    +      'trk': ol.xml.makeArrayPusher(ol.format.GPX.readTrk_),
    +      'wpt': ol.xml.makeArrayPusher(ol.format.GPX.readWpt_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.LINK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'text':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkText'),
    +      'type':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkType')
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.RTE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'link': ol.format.GPX.parseLink_,
    +      'number':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
    +      'extensions': ol.format.GPX.parseExtensions_,
    +      'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'rtept': ol.format.GPX.parseRtePt_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.RTEPT_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.TRK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'link': ol.format.GPX.parseLink_,
    +      'number':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
    +      'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'extensions': ol.format.GPX.parseExtensions_,
    +      'trkseg': ol.format.GPX.parseTrkSeg_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.TRKSEG_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'trkpt': ol.format.GPX.parseTrkPt_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.TRKPT_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.GPX.WPT_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime),
    +      'magvar': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'geoidheight': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'link': ol.format.GPX.parseLink_,
    +      'sym': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'fix': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'sat': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'hdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'vdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'pdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'ageofdgpsdata':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'dgpsid':
    +          ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
    +      'extensions': ol.format.GPX.parseExtensions_
    +    });
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features
    + * @private
    + */
    +ol.format.GPX.prototype.handleReadExtensions_ = function(features) {
    +  if (goog.isNull(features)) {
    +    features = [];
    +  }
    +  for (var i = 0, ii = features.length; i < ii; ++i) {
    +    var feature = features[i];
    +    if (goog.isDef(this.readExtensions_)) {
    +      var extensionsNode = feature.get('extensionsNode_') || null;
    +      this.readExtensions_(feature, extensionsNode);
    +    }
    +    feature.set('extensionsNode_', undefined);
    +  }
    +};
    +
    +
    +/**
    + * Read the first feature from a GPX source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api stable
    + */
    +ol.format.GPX.prototype.readFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  if (!goog.array.contains(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) {
    +    return null;
    +  }
    +  var featureReader = ol.format.GPX.FEATURE_READER_[node.localName];
    +  if (!goog.isDef(featureReader)) {
    +    return null;
    +  }
    +  var feature = featureReader(node, [this.getReadOptions(node, opt_options)]);
    +  if (!goog.isDef(feature)) {
    +    return null;
    +  }
    +  this.handleReadExtensions_([feature]);
    +  return feature;
    +};
    +
    +
    +/**
    + * Read all features from a GPX source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.GPX.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  if (!goog.array.contains(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) {
    +    return [];
    +  }
    +  if (node.localName == 'gpx') {
    +    var features = ol.xml.pushParseAndPop(
    +        /** @type {Array.<ol.Feature>} */ ([]), ol.format.GPX.GPX_PARSERS_,
    +        node, [this.getReadOptions(node, opt_options)]);
    +    if (goog.isDef(features)) {
    +      this.handleReadExtensions_(features);
    +      return features;
    +    } else {
    +      return [];
    +    }
    +  }
    +  return [];
    +};
    +
    +
    +/**
    + * Read the projection from a GPX source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.GPX.prototype.readProjection;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {string} value Value for the link's `href` attribute.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.GPX.writeLink_ = function(node, value, objectStack) {
    +  node.setAttribute('href', value);
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var properties = context['properties'];
    +  var link = [
    +    properties['linkText'],
    +    properties['linkType']
    +  ];
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ ({node: node}),
    +      ol.format.GPX.LINK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      link, objectStack, ol.format.GPX.LINK_SEQUENCE_);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.writeWptType_ = function(node, coordinate, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var parentNode = context.node;
    +  goog.asserts.assert(ol.xml.isNode(parentNode));
    +  var namespaceURI = parentNode.namespaceURI;
    +  var properties = context['properties'];
    +  //FIXME Projection handling
    +  ol.xml.setAttributeNS(node, null, 'lat', coordinate[1]);
    +  ol.xml.setAttributeNS(node, null, 'lon', coordinate[0]);
    +  var geometryLayout = context['geometryLayout'];
    +  /* jshint -W086 */
    +  switch (geometryLayout) {
    +    case ol.geom.GeometryLayout.XYZM:
    +      if (coordinate[3] !== 0) {
    +        properties['time'] = coordinate[3];
    +      }
    +    case ol.geom.GeometryLayout.XYZ:
    +      if (coordinate[2] !== 0) {
    +        properties['ele'] = coordinate[2];
    +      }
    +      break;
    +    case ol.geom.GeometryLayout.XYM:
    +      if (coordinate[2] !== 0) {
    +        properties['time'] = coordinate[2];
    +      }
    +  }
    +  /* jshint +W086 */
    +  var orderedKeys = ol.format.GPX.WPT_TYPE_SEQUENCE_[namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      ({node: node, 'properties': properties}),
    +      ol.format.GPX.WPT_TYPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.writeRte_ = function(node, feature, objectStack) {
    +  var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
    +  var properties = feature.getProperties();
    +  var context = {node: node, 'properties': properties};
    +  var geometry = feature.getGeometry();
    +  if (goog.isDef(geometry)) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +    geometry = /** @type {ol.geom.LineString} */
    +        (ol.format.Feature.transformWithOptions(geometry, true, options));
    +    context['geometryLayout'] = geometry.getLayout();
    +    properties['rtept'] = geometry.getCoordinates();
    +  }
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.GPX.RTE_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
    +      ol.format.GPX.RTE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.writeTrk_ = function(node, feature, objectStack) {
    +  var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
    +  var properties = feature.getProperties();
    +  var context = {node: node, 'properties': properties};
    +  var geometry = feature.getGeometry();
    +  if (goog.isDef(geometry)) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString);
    +    geometry = /** @type {ol.geom.MultiLineString} */
    +        (ol.format.Feature.transformWithOptions(geometry, true, options));
    +    properties['trkseg'] = geometry.getLineStrings();
    +  }
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.GPX.TRK_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
    +      ol.format.GPX.TRK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LineString} lineString LineString.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.writeTrkSeg_ = function(node, lineString, objectStack) {
    +  var context = {node: node, 'geometryLayout': lineString.getLayout(),
    +    'properties': {}};
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
    +      ol.format.GPX.TRKSEG_SERIALIZERS_, ol.format.GPX.TRKSEG_NODE_FACTORY_,
    +      lineString.getCoordinates(), objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.GPX.writeWpt_ = function(node, feature, objectStack) {
    +  var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  context['properties'] = feature.getProperties();
    +  var geometry = feature.getGeometry();
    +  if (goog.isDef(geometry)) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +    geometry = /** @type {ol.geom.Point} */
    +        (ol.format.Feature.transformWithOptions(geometry, true, options));
    +    context['geometryLayout'] = geometry.getLayout();
    +    ol.format.GPX.writeWptType_(node, geometry.getCoordinates(), objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.GPX.LINK_SEQUENCE_ = ['text', 'type'];
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.LINK_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'text': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.GPX.RTE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, [
    +      'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'rtept'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.RTE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
    +      'number': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeNonNegativeIntegerTextNode),
    +      'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'rtept': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
    +          ol.format.GPX.writeWptType_))
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.GPX.TRK_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, [
    +      'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'trkseg'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.TRK_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
    +      'number': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeNonNegativeIntegerTextNode),
    +      'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'trkseg': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
    +          ol.format.GPX.writeTrkSeg_))
    +    });
    +
    +
    +/**
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.GPX.TRKSEG_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('trkpt');
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.TRKSEG_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'trkpt': ol.xml.makeChildAppender(ol.format.GPX.writeWptType_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.GPX.WPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, [
    +      'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src',
    +      'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop',
    +      'ageofdgpsdata', 'dgpsid'
    +    ]);
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.WPT_TYPE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'ele': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'time': ol.xml.makeChildAppender(ol.format.XSD.writeDateTimeTextNode),
    +      'magvar': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'geoidheight': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeDecimalTextNode),
    +      'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
    +      'sym': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'fix': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'sat': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeNonNegativeIntegerTextNode),
    +      'hdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'vdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'pdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'ageofdgpsdata': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeDecimalTextNode),
    +      'dgpsid': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeNonNegativeIntegerTextNode)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, string>}
    + * @private
    + */
    +ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_ = {
    +  'Point': 'wpt',
    +  'LineString': 'rte',
    +  'MultiLineString': 'trk'
    +};
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.GPX.GPX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
    +  goog.asserts.assertInstanceof(value, ol.Feature);
    +  var geometry = value.getGeometry();
    +  if (goog.isDef(geometry)) {
    +    var parentNode = objectStack[objectStack.length - 1].node;
    +    goog.asserts.assert(ol.xml.isNode(parentNode));
    +    return ol.xml.createElementNS(parentNode.namespaceURI,
    +        ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_[geometry.getType()]);
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.GPX.NAMESPACE_URIS_, {
    +      'rte': ol.xml.makeChildAppender(ol.format.GPX.writeRte_),
    +      'trk': ol.xml.makeChildAppender(ol.format.GPX.writeTrk_),
    +      'wpt': ol.xml.makeChildAppender(ol.format.GPX.writeWpt_)
    +    });
    +
    +
    +/**
    + * Encode an array of features in the GPX format.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} Result.
    + * @api stable
    + */
    +ol.format.GPX.prototype.writeFeatures;
    +
    +
    +/**
    + * Encode an array of features in the GPX format as an XML node.
    + *
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + * @api
    + */
    +ol.format.GPX.prototype.writeFeaturesNode = function(features, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  //FIXME Serialize metadata
    +  var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
    +
    +  ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
    +      ({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_,
    +      ol.format.GPX.GPX_NODE_FACTORY_, features, [opt_options]);
    +  return gpx;
    +};
    diff --git a/mapbox-gl/ol/ol/format/igcformat.js b/mapbox-gl/ol/ol/format/igcformat.js
    new file mode 100644
    index 0000000000..aa57c6273e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/igcformat.js
    @@ -0,0 +1,220 @@
    +goog.provide('ol.format.IGC');
    +goog.provide('ol.format.IGCZ');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +goog.require('goog.string.newlines');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.TextFeature');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.proj');
    +
    +
    +/**
    + * IGC altitude/z. One of 'barometric', 'gps', 'none'.
    + * @enum {string}
    + * @api
    + */
    +ol.format.IGCZ = {
    +  BAROMETRIC: 'barometric',
    +  GPS: 'gps',
    +  NONE: 'none'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for `*.igc` flight recording files.
    + *
    + * @constructor
    + * @extends {ol.format.TextFeature}
    + * @param {olx.format.IGCOptions=} opt_options Options.
    + * @api
    + */
    +ol.format.IGC = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get('EPSG:4326');
    +
    +  /**
    +   * @private
    +   * @type {ol.format.IGCZ}
    +   */
    +  this.altitudeMode_ = goog.isDef(options.altitudeMode) ?
    +      options.altitudeMode : ol.format.IGCZ.NONE;
    +
    +};
    +goog.inherits(ol.format.IGC, ol.format.TextFeature);
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.IGC.EXTENSIONS_ = ['.igc'];
    +
    +
    +/**
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.format.IGC.B_RECORD_RE_ =
    +    /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/;
    +
    +
    +/**
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.format.IGC.H_RECORD_RE_ = /^H.([A-Z]{3}).*?:(.*)/;
    +
    +
    +/**
    + * @const
    + * @type {RegExp}
    + * @private
    + */
    +ol.format.IGC.HFDTE_RECORD_RE_ = /^HFDTE(\d{2})(\d{2})(\d{2})/;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.IGC.prototype.getExtensions = function() {
    +  return ol.format.IGC.EXTENSIONS_;
    +};
    +
    +
    +/**
    + * Read the feature from the IGC source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api
    + */
    +ol.format.IGC.prototype.readFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.IGC.prototype.readFeatureFromText = function(text, opt_options) {
    +  var altitudeMode = this.altitudeMode_;
    +  var lines = goog.string.newlines.splitLines(text);
    +  /** @type {Object.<string, string>} */
    +  var properties = {};
    +  var flatCoordinates = [];
    +  var year = 2000;
    +  var month = 0;
    +  var day = 1;
    +  var i, ii;
    +  for (i = 0, ii = lines.length; i < ii; ++i) {
    +    var line = lines[i];
    +    var m;
    +    if (line.charAt(0) == 'B') {
    +      m = ol.format.IGC.B_RECORD_RE_.exec(line);
    +      if (m) {
    +        var hour = parseInt(m[1], 10);
    +        var minute = parseInt(m[2], 10);
    +        var second = parseInt(m[3], 10);
    +        var y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000;
    +        if (m[6] == 'S') {
    +          y = -y;
    +        }
    +        var x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000;
    +        if (m[9] == 'W') {
    +          x = -x;
    +        }
    +        flatCoordinates.push(x, y);
    +        if (altitudeMode != ol.format.IGCZ.NONE) {
    +          var z;
    +          if (altitudeMode == ol.format.IGCZ.GPS) {
    +            z = parseInt(m[11], 10);
    +          } else if (altitudeMode == ol.format.IGCZ.BAROMETRIC) {
    +            z = parseInt(m[12], 10);
    +          } else {
    +            goog.asserts.fail();
    +            z = 0;
    +          }
    +          flatCoordinates.push(z);
    +        }
    +        var dateTime = Date.UTC(year, month, day, hour, minute, second);
    +        flatCoordinates.push(dateTime / 1000);
    +      }
    +    } else if (line.charAt(0) == 'H') {
    +      m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line);
    +      if (m) {
    +        day = parseInt(m[1], 10);
    +        month = parseInt(m[2], 10) - 1;
    +        year = 2000 + parseInt(m[3], 10);
    +      } else {
    +        m = ol.format.IGC.H_RECORD_RE_.exec(line);
    +        if (m) {
    +          properties[m[1]] = goog.string.trim(m[2]);
    +          m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line);
    +        }
    +      }
    +    }
    +  }
    +  if (flatCoordinates.length === 0) {
    +    return null;
    +  }
    +  var lineString = new ol.geom.LineString(null);
    +  var layout = altitudeMode == ol.format.IGCZ.NONE ?
    +      ol.geom.GeometryLayout.XYM : ol.geom.GeometryLayout.XYZM;
    +  lineString.setFlatCoordinates(layout, flatCoordinates);
    +  var feature = new ol.Feature(ol.format.Feature.transformWithOptions(
    +      lineString, false, opt_options));
    +  feature.setProperties(properties);
    +  return feature;
    +};
    +
    +
    +/**
    + * Read the feature from the source. As IGC sources contain a single
    + * feature, this will return the feature in an array.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api
    + */
    +ol.format.IGC.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.IGC.prototype.readFeaturesFromText = function(text, opt_options) {
    +  var feature = this.readFeatureFromText(text, opt_options);
    +  if (!goog.isNull(feature)) {
    +    return [feature];
    +  } else {
    +    return [];
    +  }
    +};
    +
    +
    +/**
    + * Read the projection from the IGC source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api
    + */
    +ol.format.IGC.prototype.readProjection;
    diff --git a/mapbox-gl/ol/ol/format/jsonfeatureformat.js b/mapbox-gl/ol/ol/format/jsonfeatureformat.js
    new file mode 100644
    index 0000000000..0ef66bc725
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/jsonfeatureformat.js
    @@ -0,0 +1,168 @@
    +goog.provide('ol.format.JSONFeature');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.json');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.FormatType');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for JSON feature formats.
    + *
    + * @constructor
    + * @extends {ol.format.Feature}
    + */
    +ol.format.JSONFeature = function() {
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.JSONFeature, ol.format.Feature);
    +
    +
    +/**
    + * @param {Document|Node|Object|string} source Source.
    + * @private
    + * @return {Object} Object.
    + */
    +ol.format.JSONFeature.prototype.getObject_ = function(source) {
    +  if (goog.isObject(source)) {
    +    return source;
    +  } else if (goog.isString(source)) {
    +    var object = goog.json.parse(source);
    +    return goog.isDef(object) ? object : null;
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.getType = function() {
    +  return ol.format.FormatType.JSON;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.readFeature = function(source, opt_options) {
    +  return this.readFeatureFromObject(
    +      this.getObject_(source), this.getReadOptions(source, opt_options));
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.readFeatures = function(source, opt_options) {
    +  return this.readFeaturesFromObject(
    +      this.getObject_(source), this.getReadOptions(source, opt_options));
    +};
    +
    +
    +/**
    + * @param {Object} object Object.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.JSONFeature.prototype.readFeatureFromObject = goog.abstractMethod;
    +
    +
    +/**
    + * @param {Object} object Object.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.JSONFeature.prototype.readFeaturesFromObject = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.readGeometry = function(source, opt_options) {
    +  return this.readGeometryFromObject(
    +      this.getObject_(source), this.getReadOptions(source, opt_options));
    +};
    +
    +
    +/**
    + * @param {Object} object Object.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.JSONFeature.prototype.readGeometryFromObject = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.readProjection = function(source) {
    +  return this.readProjectionFromObject(this.getObject_(source));
    +};
    +
    +
    +/**
    + * @param {Object} object Object.
    + * @protected
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.JSONFeature.prototype.readProjectionFromObject = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.writeFeature = function(feature, opt_options) {
    +  return goog.json.serialize(this.writeFeatureObject(feature, opt_options));
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {Object} Object.
    + */
    +ol.format.JSONFeature.prototype.writeFeatureObject = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.writeFeatures = function(
    +    features, opt_options) {
    +  return goog.json.serialize(this.writeFeaturesObject(features, opt_options));
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {Object} Object.
    + */
    +ol.format.JSONFeature.prototype.writeFeaturesObject = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.JSONFeature.prototype.writeGeometry = function(
    +    geometry, opt_options) {
    +  return goog.json.serialize(this.writeGeometryObject(geometry, opt_options));
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {Object} Object.
    + */
    +ol.format.JSONFeature.prototype.writeGeometryObject = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/format/kmlformat.js b/mapbox-gl/ol/ol/format/kmlformat.js
    new file mode 100644
    index 0000000000..de4c4d42aa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/kmlformat.js
    @@ -0,0 +1,2710 @@
    +// FIXME http://earth.google.com/kml/1.0 namespace?
    +// FIXME why does node.getAttribute return an unknown type?
    +// FIXME text
    +// FIXME serialize arbitrary feature properties
    +// FIXME don't parse style if extractStyles is false
    +
    +goog.provide('ol.format.KML');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.Feature');
    +goog.require('ol.color');
    +goog.require('ol.feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.format.XSD');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.GeometryCollection');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.LinearRing');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.IconAnchorUnits');
    +goog.require('ol.style.IconOrigin');
    +goog.require('ol.style.Image');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +goog.require('ol.style.Text');
    +goog.require('ol.xml');
    +
    +
    +/**
    + * @typedef {{x: number, xunits: (ol.style.IconAnchorUnits|undefined),
    + *            y: number, yunits: (ol.style.IconAnchorUnits|undefined)}}
    + */
    +ol.format.KMLVec2_;
    +
    +
    +/**
    + * @typedef {{flatCoordinates: Array.<number>,
    + *            whens: Array.<number>}}
    + */
    +ol.format.KMLGxTrackObject_;
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the KML format.
    + *
    + * @constructor
    + * @extends {ol.format.XMLFeature}
    + * @param {olx.format.KMLOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.format.KML = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get('EPSG:4326');
    +
    +  var defaultStyle = goog.isDef(options.defaultStyle) ?
    +      options.defaultStyle : ol.format.KML.DEFAULT_STYLE_ARRAY_;
    +
    +  /** @type {Object.<string, (Array.<ol.style.Style>|string)>} */
    +  var sharedStyles = {};
    +
    +  var findStyle =
    +      /**
    +       * @param {Array.<ol.style.Style>|string|undefined} styleValue Style
    +       *     value.
    +       * @return {Array.<ol.style.Style>} Style.
    +       */
    +      function(styleValue) {
    +    if (goog.isArray(styleValue)) {
    +      return styleValue;
    +    } else if (goog.isString(styleValue)) {
    +      // KML files in the wild occasionally forget the leading `#` on styleUrls
    +      // defined in the same document.  Add a leading `#` if it enables to find
    +      // a style.
    +      if (!(styleValue in sharedStyles) && ('#' + styleValue in sharedStyles)) {
    +        styleValue = '#' + styleValue;
    +      }
    +      return findStyle(sharedStyles[styleValue]);
    +    } else {
    +      return defaultStyle;
    +    }
    +  };
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.extractStyles_ = goog.isDef(options.extractStyles) ?
    +      options.extractStyles : true;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, (Array.<ol.style.Style>|string)>}
    +   */
    +  this.sharedStyles_ = sharedStyles;
    +
    +  /**
    +   * @private
    +   * @type {ol.feature.FeatureStyleFunction}
    +   */
    +  this.featureStyleFunction_ =
    +      /**
    +       * @param {number} resolution Resolution.
    +       * @return {Array.<ol.style.Style>} Style.
    +       * @this {ol.Feature}
    +       */
    +      function(resolution) {
    +    var style = /** @type {Array.<ol.style.Style>|undefined} */
    +        (this.get('Style'));
    +    if (goog.isDef(style)) {
    +      return style;
    +    }
    +    var styleUrl = /** @type {string|undefined} */ (this.get('styleUrl'));
    +    if (goog.isDef(styleUrl)) {
    +      return findStyle(styleUrl);
    +    }
    +    return defaultStyle;
    +  };
    +
    +};
    +goog.inherits(ol.format.KML, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.KML.EXTENSIONS_ = ['.kml'];
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.KML.GX_NAMESPACE_URIS_ = [
    +  'http://www.google.com/kml/ext/2.2'
    +];
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.KML.NAMESPACE_URIS_ = [
    +  null,
    +  'http://earth.google.com/kml/2.0',
    +  'http://earth.google.com/kml/2.1',
    +  'http://earth.google.com/kml/2.2',
    +  'http://www.opengis.net/kml/2.2'
    +];
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.KML.SCHEMA_LOCATION_ = 'http://www.opengis.net/kml/2.2 ' +
    +    'https://developers.google.com/kml/schema/kml22gx.xsd';
    +
    +
    +/**
    + * @const
    + * @type {ol.Color}
    + * @private
    + */
    +ol.format.KML.DEFAULT_COLOR_ = [255, 255, 255, 1];
    +
    +
    +/**
    + * @const
    + * @type {ol.style.Fill}
    + * @private
    + */
    +ol.format.KML.DEFAULT_FILL_STYLE_ = new ol.style.Fill({
    +  color: ol.format.KML.DEFAULT_COLOR_
    +});
    +
    +
    +/**
    + * @const
    + * @type {ol.Size}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_ = [20, 2]; // FIXME maybe [8, 32] ?
    +
    +
    +/**
    + * @const
    + * @type {ol.style.IconAnchorUnits}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_ =
    +    ol.style.IconAnchorUnits.PIXELS;
    +
    +
    +/**
    + * @const
    + * @type {ol.style.IconAnchorUnits}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_ =
    +    ol.style.IconAnchorUnits.PIXELS;
    +
    +
    +/**
    + * @const
    + * @type {ol.Size}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_ = [64, 64];
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ =
    +    'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png';
    +
    +
    +/**
    + * @const
    + * @type {ol.style.Image}
    + * @private
    + */
    +ol.format.KML.DEFAULT_IMAGE_STYLE_ = new ol.style.Icon({
    +  anchor: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_,
    +  anchorOrigin: ol.style.IconOrigin.BOTTOM_LEFT,
    +  anchorXUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_,
    +  anchorYUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_,
    +  crossOrigin: 'anonymous',
    +  rotation: 0,
    +  scale: 0.5,
    +  size: ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_,
    +  src: ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_
    +});
    +
    +
    +/**
    + * @const
    + * @type {ol.style.Stroke}
    + * @private
    + */
    +ol.format.KML.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({
    +  color: ol.format.KML.DEFAULT_COLOR_,
    +  width: 1
    +});
    +
    +
    +/**
    + * @const
    + * @type {ol.style.Text}
    + * @private
    + */
    +ol.format.KML.DEFAULT_TEXT_STYLE_ = new ol.style.Text({
    +  font: 'normal 16px Helvetica',
    +  fill: ol.format.KML.DEFAULT_FILL_STYLE_,
    +  stroke: ol.format.KML.DEFAULT_STROKE_STYLE_,
    +  scale: 1
    +});
    +
    +
    +/**
    + * @const
    + * @type {ol.style.Style}
    + * @private
    + */
    +ol.format.KML.DEFAULT_STYLE_ = new ol.style.Style({
    +  fill: ol.format.KML.DEFAULT_FILL_STYLE_,
    +  image: ol.format.KML.DEFAULT_IMAGE_STYLE_,
    +  text: ol.format.KML.DEFAULT_TEXT_STYLE_,
    +  stroke: ol.format.KML.DEFAULT_STROKE_STYLE_,
    +  zIndex: 0
    +});
    +
    +
    +/**
    + * @const
    + * @type {Array.<ol.style.Style>}
    + * @private
    + */
    +ol.format.KML.DEFAULT_STYLE_ARRAY_ = [ol.format.KML.DEFAULT_STYLE_];
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, ol.style.IconAnchorUnits>}
    + * @private
    + */
    +ol.format.KML.ICON_ANCHOR_UNITS_MAP_ = {
    +  'fraction': ol.style.IconAnchorUnits.FRACTION,
    +  'pixels': ol.style.IconAnchorUnits.PIXELS
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {ol.Color|undefined} Color.
    + */
    +ol.format.KML.readColor_ = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  // The KML specification states that colors should not include a leading `#`
    +  // but we tolerate them.
    +  var m = /^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(s);
    +  if (m) {
    +    var hexColor = m[1];
    +    return [
    +      parseInt(hexColor.substr(6, 2), 16),
    +      parseInt(hexColor.substr(4, 2), 16),
    +      parseInt(hexColor.substr(2, 2), 16),
    +      parseInt(hexColor.substr(0, 2), 16) / 255
    +    ];
    +
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {Array.<number>|undefined} Flat coordinates.
    + */
    +ol.format.KML.readFlatCoordinates_ = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  var flatCoordinates = [];
    +  // The KML specification states that coordinate tuples should not include
    +  // spaces, but we tolerate them.
    +  var re =
    +      /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i;
    +  var m;
    +  while ((m = re.exec(s))) {
    +    var x = parseFloat(m[1]);
    +    var y = parseFloat(m[2]);
    +    var z = m[3] ? parseFloat(m[3]) : 0;
    +    flatCoordinates.push(x, y, z);
    +    s = s.substr(m[0].length);
    +  }
    +  if (s !== '') {
    +    return undefined;
    +  }
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {string|undefined} Style URL.
    + */
    +ol.format.KML.readStyleUrl_ = function(node) {
    +  var s = goog.string.trim(ol.xml.getAllTextContent(node, false));
    +  if (goog.isDefAndNotNull(node.baseURI)) {
    +    return goog.Uri.resolve(node.baseURI, s).toString();
    +  } else {
    +    return s;
    +  }
    +
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {string} URI.
    + */
    +ol.format.KML.readURI_ = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  if (goog.isDefAndNotNull(node.baseURI)) {
    +    return goog.Uri.resolve(node.baseURI, goog.string.trim(s)).toString();
    +  } else {
    +    return goog.string.trim(s);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {ol.format.KMLVec2_} Vec2.
    + */
    +ol.format.KML.readVec2_ = function(node) {
    +  var xunits = node.getAttribute('xunits');
    +  var yunits = node.getAttribute('yunits');
    +  return {
    +    x: parseFloat(node.getAttribute('x')),
    +    xunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[xunits],
    +    y: parseFloat(node.getAttribute('y')),
    +    yunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[yunits]
    +  };
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {number|undefined} Scale.
    + */
    +ol.format.KML.readScale_ = function(node) {
    +  var number = ol.format.XSD.readDecimal(node);
    +  if (goog.isDef(number)) {
    +    return Math.sqrt(number);
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<ol.style.Style>|string|undefined} StyleMap.
    + */
    +ol.format.KML.readStyleMapValue_ = function(node, objectStack) {
    +  return ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.style.Style>|string|undefined} */ (undefined),
    +      ol.format.KML.STYLE_MAP_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.IconStyleParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'IconStyle');
    +  // FIXME refreshMode
    +  // FIXME refreshInterval
    +  // FIXME viewRefreshTime
    +  // FIXME viewBoundScale
    +  // FIXME viewFormat
    +  // FIXME httpQuery
    +  var object = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.ICON_STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(object)) {
    +    return;
    +  }
    +  var styleObject = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  goog.asserts.assert(goog.isObject(styleObject));
    +  var IconObject = /** @type {Object} */ (goog.object.get(object, 'Icon', {}));
    +  var src;
    +  var href = /** @type {string|undefined} */
    +      (IconObject['href']);
    +  if (goog.isDef(href)) {
    +    src = href;
    +  } else {
    +    src = ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_;
    +  }
    +  var anchor, anchorXUnits, anchorYUnits;
    +  var hotSpot = /** @type {ol.format.KMLVec2_|undefined} */
    +      (object['hotSpot']);
    +  if (goog.isDef(hotSpot)) {
    +    anchor = [hotSpot.x, hotSpot.y];
    +    anchorXUnits = hotSpot.xunits;
    +    anchorYUnits = hotSpot.yunits;
    +  } else if (src === ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) {
    +    anchor = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_;
    +    anchorXUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_;
    +    anchorYUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_;
    +  } else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) {
    +    anchor = [0.5, 0];
    +    anchorXUnits = ol.style.IconAnchorUnits.FRACTION;
    +    anchorYUnits = ol.style.IconAnchorUnits.FRACTION;
    +  }
    +
    +  var offset;
    +  var x = /** @type {number|undefined} */
    +      (IconObject['x']);
    +  var y = /** @type {number|undefined} */
    +      (IconObject['y']);
    +  if (goog.isDef(x) && goog.isDef(y)) {
    +    offset = [x, y];
    +  }
    +
    +  var size;
    +  var w = /** @type {number|undefined} */
    +      (IconObject['w']);
    +  var h = /** @type {number|undefined} */
    +      (IconObject['h']);
    +  if (goog.isDef(w) && goog.isDef(h)) {
    +    size = [w, h];
    +  }
    +
    +  var rotation;
    +  var heading = /** @type {number|undefined} */
    +      (object['heading']);
    +  if (goog.isDef(heading)) {
    +    rotation = goog.math.toRadians(heading);
    +  }
    +
    +  var scale = /** @type {number|undefined} */
    +      (object['scale']);
    +  if (src == ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) {
    +    size = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_;
    +  }
    +
    +  var imageStyle = new ol.style.Icon({
    +    anchor: anchor,
    +    anchorOrigin: ol.style.IconOrigin.BOTTOM_LEFT,
    +    anchorXUnits: anchorXUnits,
    +    anchorYUnits: anchorYUnits,
    +    crossOrigin: 'anonymous', // FIXME should this be configurable?
    +    offset: offset,
    +    offsetOrigin: ol.style.IconOrigin.BOTTOM_LEFT,
    +    rotation: rotation,
    +    scale: scale,
    +    size: size,
    +    src: src
    +  });
    +  styleObject['imageStyle'] = imageStyle;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.LabelStyleParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LabelStyle');
    +  // FIXME colorMode
    +  var object = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.LABEL_STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(object)) {
    +    return;
    +  }
    +  var styleObject = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(styleObject));
    +  var textStyle = new ol.style.Text({
    +    fill: new ol.style.Fill({
    +      color: /** @type {ol.Color} */
    +          (goog.object.get(object, 'color', ol.format.KML.DEFAULT_COLOR_))
    +    }),
    +    scale: /** @type {number|undefined} */
    +        (object['scale'])
    +  });
    +  styleObject['textStyle'] = textStyle;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.LineStyleParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LineStyle');
    +  // FIXME colorMode
    +  // FIXME gx:outerColor
    +  // FIXME gx:outerWidth
    +  // FIXME gx:physicalWidth
    +  // FIXME gx:labelVisibility
    +  var object = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.LINE_STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(object)) {
    +    return;
    +  }
    +  var styleObject = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(styleObject));
    +  var strokeStyle = new ol.style.Stroke({
    +    color: /** @type {ol.Color} */
    +        (goog.object.get(object, 'color', ol.format.KML.DEFAULT_COLOR_)),
    +    width: /** @type {number} */ (goog.object.get(object, 'width', 1))
    +  });
    +  styleObject['strokeStyle'] = strokeStyle;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.PolyStyleParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'PolyStyle');
    +  // FIXME colorMode
    +  var object = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.POLY_STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(object)) {
    +    return;
    +  }
    +  var styleObject = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(styleObject));
    +  var fillStyle = new ol.style.Fill({
    +    color: /** @type {ol.Color} */
    +        (goog.object.get(object, 'color', ol.format.KML.DEFAULT_COLOR_))
    +  });
    +  styleObject['fillStyle'] = fillStyle;
    +  var fill = /** @type {boolean|undefined} */ (object['fill']);
    +  if (goog.isDef(fill)) {
    +    styleObject['fill'] = fill;
    +  }
    +  var outline =
    +      /** @type {boolean|undefined} */ (object['outline']);
    +  if (goog.isDef(outline)) {
    +    styleObject['outline'] = outline;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>} LinearRing flat coordinates.
    + */
    +ol.format.KML.readFlatLinearRing_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LinearRing');
    +  return /** @type {Array.<number>} */ (ol.xml.pushParseAndPop(
    +      null, ol.format.KML.FLAT_LINEAR_RING_PARSERS_, node, objectStack));
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.gxCoordParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(goog.array.contains(
    +      ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI));
    +  goog.asserts.assert(node.localName == 'coord');
    +  var gxTrackObject = /** @type {ol.format.KMLGxTrackObject_} */
    +      (objectStack[objectStack.length - 1]);
    +  goog.asserts.assert(goog.isObject(gxTrackObject));
    +  var flatCoordinates = gxTrackObject.flatCoordinates;
    +  var s = ol.xml.getAllTextContent(node, false);
    +  var re =
    +      /^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i;
    +  var m = re.exec(s);
    +  if (m) {
    +    var x = parseFloat(m[1]);
    +    var y = parseFloat(m[2]);
    +    var z = parseFloat(m[3]);
    +    flatCoordinates.push(x, y, z, 0);
    +  } else {
    +    flatCoordinates.push(0, 0, 0, 0);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.MultiLineString|undefined} MultiLineString.
    + */
    +ol.format.KML.readGxMultiTrack_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(goog.array.contains(
    +      ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI));
    +  goog.asserts.assert(node.localName == 'MultiTrack');
    +  var lineStrings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.LineString>} */ ([]),
    +      ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_, node, objectStack);
    +  if (!goog.isDef(lineStrings)) {
    +    return undefined;
    +  }
    +  var multiLineString = new ol.geom.MultiLineString(null);
    +  multiLineString.setLineStrings(lineStrings);
    +  return multiLineString;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.LineString|undefined} LineString.
    + */
    +ol.format.KML.readGxTrack_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(goog.array.contains(
    +      ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI));
    +  goog.asserts.assert(node.localName == 'Track');
    +  var gxTrackObject = ol.xml.pushParseAndPop(
    +      /** @type {ol.format.KMLGxTrackObject_} */ ({
    +        flatCoordinates: [],
    +        whens: []
    +      }), ol.format.KML.GX_TRACK_PARSERS_, node, objectStack);
    +  if (!goog.isDef(gxTrackObject)) {
    +    return undefined;
    +  }
    +  var flatCoordinates = gxTrackObject.flatCoordinates;
    +  var whens = gxTrackObject.whens;
    +  goog.asserts.assert(flatCoordinates.length / 4 == whens.length);
    +  var i, ii;
    +  for (i = 0, ii = Math.min(flatCoordinates.length, whens.length); i < ii;
    +       ++i) {
    +    flatCoordinates[4 * i + 3] = whens[i];
    +  }
    +  var lineString = new ol.geom.LineString(null);
    +  lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates);
    +  return lineString;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object} Icon object.
    + */
    +ol.format.KML.readIcon_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Icon');
    +  var iconObject = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.ICON_PARSERS_, node, objectStack);
    +  if (goog.isDef(iconObject)) {
    +    return iconObject;
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.format.KML.readFlatCoordinatesFromNode_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return /** @type {Array.<number>} */ (ol.xml.pushParseAndPop(null,
    +      ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack));
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.LineString|undefined} LineString.
    + */
    +ol.format.KML.readLineString_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LineString');
    +  var flatCoordinates =
    +      ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDef(flatCoordinates)) {
    +    var lineString = new ol.geom.LineString(null);
    +    lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return lineString;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.Polygon|undefined} Polygon.
    + */
    +ol.format.KML.readLinearRing_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'LinearRing');
    +  var flatCoordinates =
    +      ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDef(flatCoordinates)) {
    +    var polygon = new ol.geom.Polygon(null);
    +    polygon.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates,
    +        [flatCoordinates.length]);
    +    return polygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.KML.readMultiGeometry_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MultiGeometry');
    +  var geometries = ol.xml.pushParseAndPop(
    +      /** @type {Array.<ol.geom.Geometry>} */ ([]),
    +      ol.format.KML.MULTI_GEOMETRY_PARSERS_, node, objectStack);
    +  if (!goog.isDef(geometries)) {
    +    return null;
    +  }
    +  if (geometries.length === 0) {
    +    return new ol.geom.GeometryCollection(geometries);
    +  }
    +  var homogeneous = true;
    +  var type = geometries[0].getType();
    +  var geometry, i, ii;
    +  for (i = 1, ii = geometries.length; i < ii; ++i) {
    +    geometry = geometries[i];
    +    if (geometry.getType() != type) {
    +      homogeneous = false;
    +      break;
    +    }
    +  }
    +  if (homogeneous) {
    +    /** @type {ol.geom.GeometryLayout} */
    +    var layout;
    +    /** @type {Array.<number>} */
    +    var flatCoordinates;
    +    if (type == ol.geom.GeometryType.POINT) {
    +      var point = geometries[0];
    +      goog.asserts.assertInstanceof(point, ol.geom.Point);
    +      layout = point.getLayout();
    +      flatCoordinates = point.getFlatCoordinates();
    +      for (i = 1, ii = geometries.length; i < ii; ++i) {
    +        geometry = geometries[i];
    +        goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +        goog.asserts.assert(geometry.getLayout() == layout);
    +        goog.array.extend(flatCoordinates, geometry.getFlatCoordinates());
    +      }
    +      var multiPoint = new ol.geom.MultiPoint(null);
    +      multiPoint.setFlatCoordinates(layout, flatCoordinates);
    +      return multiPoint;
    +    } else if (type == ol.geom.GeometryType.LINE_STRING) {
    +      var multiLineString = new ol.geom.MultiLineString(null);
    +      multiLineString.setLineStrings(geometries);
    +      return multiLineString;
    +    } else if (type == ol.geom.GeometryType.POLYGON) {
    +      var multiPolygon = new ol.geom.MultiPolygon(null);
    +      multiPolygon.setPolygons(geometries);
    +      return multiPolygon;
    +    } else if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) {
    +      return new ol.geom.GeometryCollection(geometries);
    +    } else {
    +      goog.asserts.fail();
    +      return null;
    +    }
    +  } else {
    +    return new ol.geom.GeometryCollection(geometries);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.Point|undefined} Point.
    + */
    +ol.format.KML.readPoint_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Point');
    +  var flatCoordinates =
    +      ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack);
    +  if (goog.isDefAndNotNull(flatCoordinates)) {
    +    var point = new ol.geom.Point(null);
    +    goog.asserts.assert(flatCoordinates.length == 3);
    +    point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates);
    +    return point;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.geom.Polygon|undefined} Polygon.
    + */
    +ol.format.KML.readPolygon_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Polygon');
    +  var flatLinearRings = ol.xml.pushParseAndPop(
    +      /** @type {Array.<Array.<number>>} */ ([null]),
    +      ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack);
    +  if (goog.isDefAndNotNull(flatLinearRings) &&
    +      !goog.isNull(flatLinearRings[0])) {
    +    var polygon = new ol.geom.Polygon(null);
    +    var flatCoordinates = flatLinearRings[0];
    +    var ends = [flatCoordinates.length];
    +    var i, ii;
    +    for (i = 1, ii = flatLinearRings.length; i < ii; ++i) {
    +      goog.array.extend(flatCoordinates, flatLinearRings[i]);
    +      ends.push(flatCoordinates.length);
    +    }
    +    polygon.setFlatCoordinates(
    +        ol.geom.GeometryLayout.XYZ, flatCoordinates, ends);
    +    return polygon;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<ol.style.Style>} Style.
    + */
    +ol.format.KML.readStyle_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Style');
    +  var styleObject = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(styleObject)) {
    +    return null;
    +  }
    +  var fillStyle = /** @type {ol.style.Fill} */ (goog.object.get(
    +      styleObject, 'fillStyle', ol.format.KML.DEFAULT_FILL_STYLE_));
    +  var fill = /** @type {boolean|undefined} */
    +      (styleObject['fill']);
    +  if (goog.isDef(fill) && !fill) {
    +    fillStyle = null;
    +  }
    +  var imageStyle = /** @type {ol.style.Image} */ (goog.object.get(
    +      styleObject, 'imageStyle', ol.format.KML.DEFAULT_IMAGE_STYLE_));
    +  var textStyle = /** @type {ol.style.Text} */ (goog.object.get(
    +      styleObject, 'textStyle', ol.format.KML.DEFAULT_TEXT_STYLE_));
    +  var strokeStyle = /** @type {ol.style.Stroke} */ (goog.object.get(
    +      styleObject, 'strokeStyle', ol.format.KML.DEFAULT_STROKE_STYLE_));
    +  var outline = /** @type {boolean|undefined} */
    +      (styleObject['outline']);
    +  if (goog.isDef(outline) && !outline) {
    +    strokeStyle = null;
    +  }
    +  return [new ol.style.Style({
    +    fill: fillStyle,
    +    image: imageStyle,
    +    stroke: strokeStyle,
    +    text: textStyle,
    +    zIndex: undefined // FIXME
    +  })];
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.DataParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Data');
    +  var name = node.getAttribute('name');
    +  if (!goog.isNull(name)) {
    +    var data = ol.xml.pushParseAndPop(
    +        undefined, ol.format.KML.DATA_PARSERS_, node, objectStack);
    +    if (goog.isDef(data)) {
    +      var featureObject =
    +          /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +      goog.asserts.assert(goog.isObject(featureObject));
    +      featureObject[name] = data;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.ExtendedDataParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ExtendedData');
    +  ol.xml.parseNode(ol.format.KML.EXTENDED_DATA_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.PairDataParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Pair');
    +  var pairObject = ol.xml.pushParseAndPop(
    +      {}, ol.format.KML.PAIR_PARSERS_, node, objectStack);
    +  if (!goog.isDef(pairObject)) {
    +    return;
    +  }
    +  var key = /** @type {string|undefined} */
    +      (pairObject['key']);
    +  if (goog.isDef(key) && key == 'normal') {
    +    var styleUrl = /** @type {string|undefined} */
    +        (pairObject['styleUrl']);
    +    if (goog.isDef(styleUrl)) {
    +      objectStack[objectStack.length - 1] = styleUrl;
    +    }
    +    var Style = /** @type {ol.style.Style} */
    +        (pairObject['Style']);
    +    if (goog.isDef(Style)) {
    +      objectStack[objectStack.length - 1] = Style;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.PlacemarkStyleMapParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'StyleMap');
    +  var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack);
    +  if (!goog.isDef(styleMapValue)) {
    +    return;
    +  }
    +  var placemarkObject = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(placemarkObject));
    +  if (goog.isArray(styleMapValue)) {
    +    placemarkObject['Style'] = styleMapValue;
    +  } else if (goog.isString(styleMapValue)) {
    +    placemarkObject['styleUrl'] = styleMapValue;
    +  } else {
    +    goog.asserts.fail();
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.SchemaDataParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'SchemaData');
    +  ol.xml.parseNode(ol.format.KML.SCHEMA_DATA_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.SimpleDataParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'SimpleData');
    +  var name = node.getAttribute('name');
    +  if (!goog.isNull(name)) {
    +    var data = ol.format.XSD.readString(node);
    +    var featureObject =
    +        /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +    featureObject[name] = data;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.innerBoundaryIsParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'innerBoundaryIs');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      ol.format.KML.INNER_BOUNDARY_IS_PARSERS_, node, objectStack);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings.push(flatLinearRing);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.outerBoundaryIsParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'outerBoundaryIs');
    +  var flatLinearRing = ol.xml.pushParseAndPop(
    +      /** @type {Array.<number>|undefined} */ (undefined),
    +      ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_, node, objectStack);
    +  if (goog.isDef(flatLinearRing)) {
    +    var flatLinearRings = /** @type {Array.<Array.<number>>} */
    +        (objectStack[objectStack.length - 1]);
    +    goog.asserts.assert(goog.isArray(flatLinearRings));
    +    goog.asserts.assert(flatLinearRings.length > 0);
    +    flatLinearRings[0] = flatLinearRing;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.LinkParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Link');
    +  ol.xml.parseNode(ol.format.KML.LINK_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.whenParser_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'when');
    +  var gxTrackObject = /** @type {ol.format.KMLGxTrackObject_} */
    +      (objectStack[objectStack.length - 1]);
    +  goog.asserts.assert(goog.isObject(gxTrackObject));
    +  var whens = gxTrackObject.whens;
    +  var s = ol.xml.getAllTextContent(node, false);
    +  var re =
    +      /^\s*(\d{4})($|-(\d{2})($|-(\d{2})($|T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?)))))\s*$/;
    +  var m = re.exec(s);
    +  if (m) {
    +    var year = parseInt(m[1], 10);
    +    var month = goog.isDef(m[3]) ? parseInt(m[3], 10) - 1 : 0;
    +    var day = goog.isDef(m[5]) ? parseInt(m[5], 10) : 1;
    +    var hour = goog.isDef(m[7]) ? parseInt(m[7], 10) : 0;
    +    var minute = goog.isDef(m[8]) ? parseInt(m[8], 10) : 0;
    +    var second = goog.isDef(m[9]) ? parseInt(m[9], 10) : 0;
    +    var when = Date.UTC(year, month, day, hour, minute, second);
    +    if (goog.isDef(m[10]) && m[10] != 'Z') {
    +      var sign = m[11] == '-' ? -1 : 1;
    +      when += sign * 60 * parseInt(m[12], 10);
    +      if (goog.isDef(m[13])) {
    +        when += sign * 60 * 60 * parseInt(m[13], 10);
    +      }
    +    }
    +    whens.push(when);
    +  } else {
    +    whens.push(0);
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.DATA_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'value': ol.xml.makeReplacer(ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.EXTENDED_DATA_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Data': ol.format.KML.DataParser_,
    +      'SchemaData': ol.format.KML.SchemaDataParser_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.FLAT_LINEAR_RING_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'innerBoundaryIs': ol.format.KML.innerBoundaryIsParser_,
    +      'outerBoundaryIs': ol.format.KML.outerBoundaryIsParser_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.GX_TRACK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'when': ol.format.KML.whenParser_
    +    }, ol.xml.makeParsersNS(
    +        ol.format.KML.GX_NAMESPACE_URIS_, {
    +          'coord': ol.format.KML.gxCoordParser_
    +        }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.ICON_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_)
    +    }, ol.xml.makeParsersNS(
    +        ol.format.KML.GX_NAMESPACE_URIS_, {
    +          'x': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +          'y': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +          'w': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +          'h': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
    +        }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.ICON_STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Icon': ol.xml.makeObjectPropertySetter(ol.format.KML.readIcon_),
    +      'heading': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
    +      'hotSpot': ol.xml.makeObjectPropertySetter(ol.format.KML.readVec2_),
    +      'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.INNER_BOUNDARY_IS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.LABEL_STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
    +      'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.LINE_STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
    +      'width': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.MULTI_GEOMETRY_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'LineString': ol.xml.makeArrayPusher(ol.format.KML.readLineString_),
    +      'LinearRing': ol.xml.makeArrayPusher(ol.format.KML.readLinearRing_),
    +      'MultiGeometry': ol.xml.makeArrayPusher(ol.format.KML.readMultiGeometry_),
    +      'Point': ol.xml.makeArrayPusher(ol.format.KML.readPoint_),
    +      'Polygon': ol.xml.makeArrayPusher(ol.format.KML.readPolygon_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.GX_NAMESPACE_URIS_, {
    +      'Track': ol.xml.makeArrayPusher(ol.format.KML.readGxTrack_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.NETWORK_LINK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'ExtendedData': ol.format.KML.ExtendedDataParser_,
    +      'Link': ol.format.KML.LinkParser_,
    +      'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
    +      'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.LINK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.PAIR_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_),
    +      'key': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyleUrl_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.PLACEMARK_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'ExtendedData': ol.format.KML.ExtendedDataParser_,
    +      'MultiGeometry': ol.xml.makeObjectPropertySetter(
    +          ol.format.KML.readMultiGeometry_, 'geometry'),
    +      'LineString': ol.xml.makeObjectPropertySetter(
    +          ol.format.KML.readLineString_, 'geometry'),
    +      'LinearRing': ol.xml.makeObjectPropertySetter(
    +          ol.format.KML.readLinearRing_, 'geometry'),
    +      'Point': ol.xml.makeObjectPropertySetter(
    +          ol.format.KML.readPoint_, 'geometry'),
    +      'Polygon': ol.xml.makeObjectPropertySetter(
    +          ol.format.KML.readPolygon_, 'geometry'),
    +      'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_),
    +      'StyleMap': ol.format.KML.PlacemarkStyleMapParser_,
    +      'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
    +      'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_),
    +      'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
    +    }, ol.xml.makeParsersNS(
    +        ol.format.KML.GX_NAMESPACE_URIS_, {
    +          'MultiTrack': ol.xml.makeObjectPropertySetter(
    +              ol.format.KML.readGxMultiTrack_, 'geometry'),
    +          'Track': ol.xml.makeObjectPropertySetter(
    +              ol.format.KML.readGxTrack_, 'geometry')
    +        }
    +    ));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.POLY_STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_),
    +      'fill': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean),
    +      'outline': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.SCHEMA_DATA_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'SimpleData': ol.format.KML.SimpleDataParser_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'IconStyle': ol.format.KML.IconStyleParser_,
    +      'LabelStyle': ol.format.KML.LabelStyleParser_,
    +      'LineStyle': ol.format.KML.LineStyleParser_,
    +      'PolyStyle': ol.format.KML.PolyStyleParser_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.KML.STYLE_MAP_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Pair': ol.format.KML.PairDataParser_
    +    });
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.KML.prototype.getExtensions = function() {
    +  return ol.format.KML.EXTENSIONS_;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<ol.Feature>|undefined} Features.
    + */
    +ol.format.KML.prototype.readDocumentOrFolder_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var localName = ol.xml.getLocalName(node);
    +  goog.asserts.assert(localName == 'Document' || localName == 'Folder');
    +  // FIXME use scope somehow
    +  var parsersNS = ol.xml.makeParsersNS(
    +      ol.format.KML.NAMESPACE_URIS_, {
    +        'Folder': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this),
    +        'Placemark': ol.xml.makeArrayPusher(this.readPlacemark_, this),
    +        'Style': goog.bind(this.readSharedStyle_, this),
    +        'StyleMap': goog.bind(this.readSharedStyleMap_, this)
    +      });
    +  var features = ol.xml.pushParseAndPop(/** @type {Array.<ol.Feature>} */ ([]),
    +      parsersNS, node, objectStack, this);
    +  if (goog.isDef(features)) {
    +    return features;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Feature.
    + */
    +ol.format.KML.prototype.readPlacemark_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Placemark');
    +  var object = ol.xml.pushParseAndPop({'geometry': null},
    +      ol.format.KML.PLACEMARK_PARSERS_, node, objectStack);
    +  if (!goog.isDef(object)) {
    +    return undefined;
    +  }
    +  var feature = new ol.Feature();
    +  var id = node.getAttribute('id');
    +  if (!goog.isNull(id)) {
    +    feature.setId(id);
    +  }
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  if (goog.isDefAndNotNull(object.geometry)) {
    +    ol.format.Feature.transformWithOptions(object.geometry, false, options);
    +  }
    +  feature.setProperties(object);
    +  if (this.extractStyles_) {
    +    feature.setStyle(this.featureStyleFunction_);
    +  }
    +  return feature;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.prototype.readSharedStyle_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Style');
    +  var id = node.getAttribute('id');
    +  if (!goog.isNull(id)) {
    +    var style = ol.format.KML.readStyle_(node, objectStack);
    +    if (goog.isDef(style)) {
    +      var styleUri;
    +      if (goog.isDefAndNotNull(node.baseURI)) {
    +        styleUri = goog.Uri.resolve(node.baseURI, '#' + id).toString();
    +      } else {
    +        styleUri = '#' + id;
    +      }
    +      this.sharedStyles_[styleUri] = style;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.prototype.readSharedStyleMap_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'StyleMap');
    +  var id = node.getAttribute('id');
    +  if (goog.isNull(id)) {
    +    return;
    +  }
    +  var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack);
    +  if (!goog.isDef(styleMapValue)) {
    +    return;
    +  }
    +  var styleUri;
    +  if (goog.isDefAndNotNull(node.baseURI)) {
    +    styleUri = goog.Uri.resolve(node.baseURI, '#' + id).toString();
    +  } else {
    +    styleUri = '#' + id;
    +  }
    +  this.sharedStyles_[styleUri] = styleMapValue;
    +};
    +
    +
    +/**
    + * Read the first feature from a KML source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api stable
    + */
    +ol.format.KML.prototype.readFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.KML.prototype.readFeatureFromNode = function(node, opt_options) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  if (!goog.array.contains(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) {
    +    return null;
    +  }
    +  goog.asserts.assert(node.localName == 'Placemark');
    +  var feature = this.readPlacemark_(
    +      node, [this.getReadOptions(node, opt_options)]);
    +  if (goog.isDef(feature)) {
    +    return feature;
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Read all features from a KML source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.KML.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.KML.prototype.readFeaturesFromNode = function(node, opt_options) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  if (!goog.array.contains(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) {
    +    return [];
    +  }
    +  var features;
    +  var localName = ol.xml.getLocalName(node);
    +  if (localName == 'Document' || localName == 'Folder') {
    +    features = this.readDocumentOrFolder_(
    +        node, [this.getReadOptions(node, opt_options)]);
    +    if (goog.isDef(features)) {
    +      return features;
    +    } else {
    +      return [];
    +    }
    +  } else if (localName == 'Placemark') {
    +    var feature = this.readPlacemark_(
    +        node, [this.getReadOptions(node, opt_options)]);
    +    if (goog.isDef(feature)) {
    +      return [feature];
    +    } else {
    +      return [];
    +    }
    +  } else if (localName == 'kml') {
    +    features = [];
    +    var n;
    +    for (n = node.firstElementChild; !goog.isNull(n);
    +         n = n.nextElementSibling) {
    +      var fs = this.readFeaturesFromNode(n, opt_options);
    +      if (goog.isDef(fs)) {
    +        goog.array.extend(features, fs);
    +      }
    +    }
    +    return features;
    +  } else {
    +    return [];
    +  }
    +};
    +
    +
    +/**
    + * @param {Document|Node|string} source Souce.
    + * @return {string|undefined} Name.
    + * @api stable
    + */
    +ol.format.KML.prototype.readName = function(source) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readNameFromDocument(/** @type {Document} */ (source));
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readNameFromNode(/** @type {Node} */ (source));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readNameFromDocument(doc);
    +  } else {
    +    goog.asserts.fail();
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {string|undefined} Name.
    + */
    +ol.format.KML.prototype.readNameFromDocument = function(doc) {
    +  var n;
    +  for (n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      var name = this.readNameFromNode(n);
    +      if (goog.isDef(name)) {
    +        return name;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {string|undefined} Name.
    + */
    +ol.format.KML.prototype.readNameFromNode = function(node) {
    +  var n;
    +  for (n = node.firstElementChild; !goog.isNull(n); n = n.nextElementSibling) {
    +    if (goog.array.contains(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
    +        n.localName == 'name') {
    +      return ol.format.XSD.readString(n);
    +    }
    +  }
    +  for (n = node.firstElementChild; !goog.isNull(n); n = n.nextElementSibling) {
    +    var localName = ol.xml.getLocalName(n);
    +    if (goog.array.contains(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
    +        (localName == 'Document' ||
    +         localName == 'Folder' ||
    +         localName == 'Placemark' ||
    +         localName == 'kml')) {
    +      var name = this.readNameFromNode(n);
    +      if (goog.isDef(name)) {
    +        return name;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {Document|Node|string} source Souce.
    + * @return {Array.<Object>} Network links.
    + * @api
    + */
    +ol.format.KML.prototype.readNetworkLinks = function(source) {
    +  var networkLinks = [];
    +  if (ol.xml.isDocument(source)) {
    +    goog.array.extend(networkLinks, this.readNetworkLinksFromDocument(
    +        /** @type {Document} */ (source)));
    +  } else if (ol.xml.isNode(source)) {
    +    goog.array.extend(networkLinks, this.readNetworkLinksFromNode(
    +        /** @type {Node} */ (source)));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    goog.array.extend(networkLinks, this.readNetworkLinksFromDocument(doc));
    +  } else {
    +    goog.asserts.fail();
    +  }
    +  return networkLinks;
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {Array.<Object>} Network links.
    + */
    +ol.format.KML.prototype.readNetworkLinksFromDocument = function(doc) {
    +  var n, networkLinks = [];
    +  for (n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      goog.array.extend(networkLinks, this.readNetworkLinksFromNode(n));
    +    }
    +  }
    +  return networkLinks;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {Array.<Object>} Network links.
    + */
    +ol.format.KML.prototype.readNetworkLinksFromNode = function(node) {
    +  var n, networkLinks = [];
    +  for (n = node.firstElementChild; !goog.isNull(n); n = n.nextElementSibling) {
    +    if (goog.array.contains(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
    +        n.localName == 'NetworkLink') {
    +      var obj = ol.xml.pushParseAndPop({}, ol.format.KML.NETWORK_LINK_PARSERS_,
    +          n, []);
    +      networkLinks.push(obj);
    +    }
    +  }
    +  for (n = node.firstElementChild; !goog.isNull(n); n = n.nextElementSibling) {
    +    var localName = ol.xml.getLocalName(n);
    +    if (goog.array.contains(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) &&
    +        (localName == 'Document' ||
    +         localName == 'Folder' ||
    +         localName == 'kml')) {
    +      goog.array.extend(networkLinks, this.readNetworkLinksFromNode(n));
    +    }
    +  }
    +  return networkLinks;
    +};
    +
    +
    +/**
    + * Read the projection from a KML source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.KML.prototype.readProjection;
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the color to.
    + * @param {ol.Color|string} color Color.
    + * @private
    + */
    +ol.format.KML.writeColorTextNode_ = function(node, color) {
    +  var rgba = ol.color.asArray(color);
    +  var opacity = (rgba.length == 4) ? rgba[3] : 1;
    +  var abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]];
    +  var i;
    +  for (i = 0; i < 4; ++i) {
    +    var hex = parseInt(abgr[i], 10).toString(16);
    +    abgr[i] = (hex.length == 1) ? '0' + hex : hex;
    +  }
    +  ol.format.XSD.writeStringTextNode(node, abgr.join(''));
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the coordinates to.
    + * @param {Array.<number>} coordinates Coordinates.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeCoordinatesTextNode_ =
    +    function(node, coordinates, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +
    +  var layout = context['layout'];
    +  var stride = context['stride'];
    +
    +  var dimension;
    +  if (layout == ol.geom.GeometryLayout.XY ||
    +      layout == ol.geom.GeometryLayout.XYM) {
    +    dimension = 2;
    +  } else if (layout == ol.geom.GeometryLayout.XYZ ||
    +      layout == ol.geom.GeometryLayout.XYZM) {
    +    dimension = 3;
    +  } else {
    +    goog.asserts.fail();
    +  }
    +
    +  var d, i;
    +  var ii = coordinates.length;
    +  var text = '';
    +  if (ii > 0) {
    +    text += coordinates[0];
    +    for (d = 1; d < dimension; ++d) {
    +      text += ',' + coordinates[d];
    +    }
    +    for (i = stride; i < ii; i += stride) {
    +      text += ' ' + coordinates[i];
    +      for (d = 1; d < dimension; ++d) {
    +        text += ',' + coordinates[i + d];
    +      }
    +    }
    +  }
    +  ol.format.XSD.writeStringTextNode(node, text);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeDocument_ = function(node, features, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.DOCUMENT_SERIALIZERS_,
    +      ol.format.KML.DOCUMENT_NODE_FACTORY_, features, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Object} icon Icon object.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeIcon_ = function(node, icon, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.KML.ICON_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(icon, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.ICON_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
    +      values, objectStack, orderedKeys);
    +  orderedKeys =
    +      ol.format.KML.ICON_SEQUENCE_[ol.format.KML.GX_NAMESPACE_URIS_[0]];
    +  values = ol.xml.makeSequence(icon, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_SERIALIZERS_,
    +      ol.format.KML.GX_NODE_FACTORY_, values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.style.Icon} style Icon style.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeIconStyle_ = function(node, style, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  var properties = {};
    +  var src = style.getSrc();
    +  var size = style.getSize();
    +  var iconImageSize = style.getImageSize();
    +  var iconProperties = {
    +    'href': src
    +  };
    +
    +  if (!goog.isNull(size)) {
    +    iconProperties['w'] = size[0];
    +    iconProperties['h'] = size[1];
    +    var anchor = style.getAnchor(); // top-left
    +    var origin = style.getOrigin(); // top-left
    +
    +    if (!goog.isNull(origin) && !goog.isNull(iconImageSize) &&
    +        origin[0] !== 0 && origin[1] !== size[1]) {
    +      iconProperties['x'] = origin[0];
    +      iconProperties['y'] = iconImageSize[1] - (origin[1] + size[1]);
    +    }
    +
    +    if (!goog.isNull(anchor) &&
    +        anchor[0] !== 0 && anchor[1] !== size[1]) {
    +      var /** @type {ol.format.KMLVec2_} */ hotSpot = {
    +        x: anchor[0],
    +        xunits: ol.style.IconAnchorUnits.PIXELS,
    +        y: size[1] - anchor[1],
    +        yunits: ol.style.IconAnchorUnits.PIXELS
    +      };
    +      properties['hotSpot'] = hotSpot;
    +    }
    +  }
    +
    +  properties['Icon'] = iconProperties;
    +
    +  var scale = style.getScale();
    +  if (scale !== 1) {
    +    properties['scale'] = scale;
    +  }
    +
    +  var rotation = style.getRotation();
    +  if (rotation !== 0) {
    +    properties['heading'] = rotation; // 0-360
    +  }
    +
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.KML.ICON_STYLE_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_STYLE_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.style.Text} style style.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeLabelStyle_ = function(node, style, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  var properties = {};
    +  var fill = style.getFill();
    +  if (!goog.isNull(fill)) {
    +    properties['color'] = fill.getColor();
    +  }
    +  var scale = style.getScale();
    +  if (goog.isDef(scale) && scale !== 1) {
    +    properties['scale'] = scale;
    +  }
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys =
    +      ol.format.KML.LABEL_STYLE_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.LABEL_STYLE_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.style.Stroke} style style.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeLineStyle_ = function(node, style, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  var properties = {
    +    'color': style.getColor(),
    +    'width': style.getWidth()
    +  };
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.KML.LINE_STYLE_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.LINE_STYLE_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeMultiGeometry_ =
    +    function(node, geometry, objectStack) {
    +  goog.asserts.assert(
    +      (geometry instanceof ol.geom.MultiPoint) ||
    +      (geometry instanceof ol.geom.MultiLineString) ||
    +      (geometry instanceof ol.geom.MultiPolygon));
    +  /** @type {ol.xml.NodeStackItem} */
    +  var context = {node: node};
    +  var type = geometry.getType();
    +  /** @type {Array.<ol.geom.Geometry>} */
    +  var geometries;
    +  /** @type {function(*, Array.<*>, string=): (Node|undefined)} */
    +  var factory;
    +  if (type == ol.geom.GeometryType.MULTI_POINT) {
    +    geometries =
    +        (/** @type {ol.geom.MultiPoint} */ (geometry)).getPoints();
    +    factory = ol.format.KML.POINT_NODE_FACTORY_;
    +  } else if (type == ol.geom.GeometryType.MULTI_LINE_STRING) {
    +    geometries =
    +        (/** @type {ol.geom.MultiLineString} */ (geometry)).getLineStrings();
    +    factory = ol.format.KML.LINE_STRING_NODE_FACTORY_;
    +  } else if (type == ol.geom.GeometryType.MULTI_POLYGON) {
    +    geometries =
    +        (/** @type {ol.geom.MultiPolygon} */ (geometry)).getPolygons();
    +    factory = ol.format.KML.POLYGON_NODE_FACTORY_;
    +  } else {
    +    goog.asserts.fail();
    +  }
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_, factory,
    +      geometries, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.LinearRing} linearRing Linear ring.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeBoundaryIs_ = function(node, linearRing, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.BOUNDARY_IS_SERIALIZERS_,
    +      ol.format.KML.LINEAR_RING_NODE_FACTORY_, [linearRing], objectStack);
    +};
    +
    +
    +/**
    + * FIXME currently we do serialize arbitrary/custom feature properties
    + * (ExtendedData).
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writePlacemark_ = function(node, feature, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +
    +  // set id
    +  if (goog.isDefAndNotNull(feature.getId())) {
    +    node.setAttribute('id', feature.getId());
    +  }
    +
    +  // serialize properties (properties unknown to KML are not serialized)
    +  var properties = feature.getProperties();
    +  var styleFunction = feature.getStyleFunction();
    +  if (goog.isDef(styleFunction)) {
    +    // FIXME the styles returned by the style function are supposed to be
    +    // resolution-independent here
    +    var styles = styleFunction.call(feature, 0);
    +    if (!goog.isNull(styles) && styles.length > 0) {
    +      properties['Style'] = styles[0];
    +      var textStyle = styles[0].getText();
    +      if (!goog.isNull(textStyle)) {
    +        properties['name'] = textStyle.getText();
    +      }
    +    }
    +  }
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.KML.PLACEMARK_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
    +
    +  // serialize geometry
    +  var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]);
    +  var geometry = feature.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    geometry =
    +        ol.format.Feature.transformWithOptions(geometry, true, options);
    +  }
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_,
    +      ol.format.KML.GEOMETRY_NODE_FACTORY_, [geometry], objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.SimpleGeometry} geometry Geometry.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writePrimitiveGeometry_ = function(node, geometry, objectStack) {
    +  goog.asserts.assert(
    +      (geometry instanceof ol.geom.Point) ||
    +      (geometry instanceof ol.geom.LineString) ||
    +      (geometry instanceof ol.geom.LinearRing));
    +  var flatCoordinates = geometry.getFlatCoordinates();
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  context['layout'] = geometry.getLayout();
    +  context['stride'] = geometry.getStride();
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_,
    +      ol.format.KML.COORDINATES_NODE_FACTORY_,
    +      [flatCoordinates], objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.geom.Polygon} polygon Polygon.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writePolygon_ = function(node, polygon, objectStack) {
    +  goog.asserts.assertInstanceof(polygon, ol.geom.Polygon);
    +  var linearRings = polygon.getLinearRings();
    +  goog.asserts.assert(linearRings.length > 0);
    +  var outerRing = linearRings.shift();
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  // inner rings
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.POLYGON_SERIALIZERS_,
    +      ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_,
    +      linearRings, objectStack);
    +  // outer ring
    +  ol.xml.pushSerializeAndPop(context,
    +      ol.format.KML.POLYGON_SERIALIZERS_,
    +      ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_,
    +      [outerRing], objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.style.Fill} style Style.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writePolyStyle_ = function(node, style, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.POLY_STYLE_SERIALIZERS_,
    +      ol.format.KML.COLOR_NODE_FACTORY_, [style.getColor()], objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the scale to.
    + * @param {number|undefined} scale Scale.
    + * @private
    + */
    +ol.format.KML.writeScaleTextNode_ = function(node, scale) {
    +  ol.format.XSD.writeDecimalTextNode(node, scale * scale);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.style.Style} style Style.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.KML.writeStyle_ = function(node, style, objectStack) {
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: node};
    +  var properties = {};
    +  var fillStyle = style.getFill();
    +  var strokeStyle = style.getStroke();
    +  var imageStyle = style.getImage();
    +  var textStyle = style.getText();
    +  if (!goog.isNull(imageStyle)) {
    +    properties['IconStyle'] = imageStyle;
    +  }
    +  if (!goog.isNull(textStyle)) {
    +    properties['LabelStyle'] = textStyle;
    +  }
    +  if (!goog.isNull(strokeStyle)) {
    +    properties['LineStyle'] = strokeStyle;
    +  }
    +  if (!goog.isNull(fillStyle)) {
    +    properties['PolyStyle'] = fillStyle;
    +  }
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  var orderedKeys = ol.format.KML.STYLE_SEQUENCE_[parentNode.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.STYLE_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the Vec2 to.
    + * @param {ol.format.KMLVec2_} vec2 Vec2.
    + * @private
    + */
    +ol.format.KML.writeVec2_ = function(node, vec2) {
    +  node.setAttribute('x', vec2.x);
    +  node.setAttribute('y', vec2.y);
    +  node.setAttribute('xunits', vec2.xunits);
    +  node.setAttribute('yunits', vec2.yunits);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.KML_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'Document', 'Placemark'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.KML_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Document': ol.xml.makeChildAppender(ol.format.KML.writeDocument_),
    +      'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.DOCUMENT_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, string>}
    + * @private
    + */
    +ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_ = {
    +  'Point': 'Point',
    +  'LineString': 'LineString',
    +  'LinearRing': 'LinearRing',
    +  'Polygon': 'Polygon',
    +  'MultiPoint': 'MultiGeometry',
    +  'MultiLineString': 'MultiGeometry',
    +  'MultiPolygon': 'MultiGeometry'
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.ICON_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'href'
    +    ],
    +    ol.xml.makeStructureNS(
    +        ol.format.KML.GX_NAMESPACE_URIS_, [
    +          'x', 'y', 'w', 'h'
    +    ]));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.ICON_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'href': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
    +    }, ol.xml.makeStructureNS(
    +        ol.format.KML.GX_NAMESPACE_URIS_, {
    +          'x': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +          'y': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +          'w': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +          'h': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode)
    +        }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.ICON_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'scale', 'heading', 'Icon', 'hotSpot'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.ICON_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'Icon': ol.xml.makeChildAppender(ol.format.KML.writeIcon_),
    +      'heading': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
    +      'hotSpot': ol.xml.makeChildAppender(ol.format.KML.writeVec2_),
    +      'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.LABEL_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'color', 'scale'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.LABEL_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_),
    +      'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.LINE_STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'color', 'width'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.LINE_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_),
    +      'width': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.BOUNDARY_IS_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'LinearRing': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'LineString': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_),
    +      'Point': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_),
    +      'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.PLACEMARK_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'name', 'open', 'visibility', 'address', 'phoneNumber', 'description',
    +      'styleUrl', 'Style'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.PLACEMARK_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'MultiGeometry': ol.xml.makeChildAppender(
    +          ol.format.KML.writeMultiGeometry_),
    +      'LineString': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_),
    +      'LinearRing': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_),
    +      'Point': ol.xml.makeChildAppender(
    +          ol.format.KML.writePrimitiveGeometry_),
    +      'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_),
    +      'Style': ol.xml.makeChildAppender(ol.format.KML.writeStyle_),
    +      'address': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'description': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeStringTextNode),
    +      'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'open': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode),
    +      'phoneNumber': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeStringTextNode),
    +      'styleUrl': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
    +      'visibility': ol.xml.makeChildAppender(
    +          ol.format.XSD.writeBooleanTextNode)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'coordinates': ol.xml.makeChildAppender(
    +          ol.format.KML.writeCoordinatesTextNode_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.POLYGON_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'outerBoundaryIs': ol.xml.makeChildAppender(
    +          ol.format.KML.writeBoundaryIs_),
    +      'innerBoundaryIs': ol.xml.makeChildAppender(
    +          ol.format.KML.writeBoundaryIs_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.POLY_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Array.<string>>}
    + * @private
    + */
    +ol.format.KML.STYLE_SEQUENCE_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, [
    +      'IconStyle', 'LabelStyle', 'LineStyle', 'PolyStyle'
    +    ]);
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.KML.STYLE_SERIALIZERS_ = ol.xml.makeStructureNS(
    +    ol.format.KML.NAMESPACE_URIS_, {
    +      'IconStyle': ol.xml.makeChildAppender(ol.format.KML.writeIconStyle_),
    +      'LabelStyle': ol.xml.makeChildAppender(ol.format.KML.writeLabelStyle_),
    +      'LineStyle': ol.xml.makeChildAppender(ol.format.KML.writeLineStyle_),
    +      'PolyStyle': ol.xml.makeChildAppender(ol.format.KML.writePolyStyle_)
    +    });
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.KML.GX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
    +  return ol.xml.createElementNS(ol.format.KML.GX_NAMESPACE_URIS_[0],
    +      'gx:' + opt_nodeName);
    +};
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.KML.DOCUMENT_NODE_FACTORY_ = function(value, objectStack,
    +    opt_nodeName) {
    +  goog.asserts.assertInstanceof(value, ol.Feature);
    +  var parentNode = objectStack[objectStack.length - 1].node;
    +  goog.asserts.assert(ol.xml.isNode(parentNode));
    +  return ol.xml.createElementNS(parentNode.namespaceURI, 'Placemark');
    +};
    +
    +
    +/**
    + * @const
    + * @param {*} value Value.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {string=} opt_nodeName Node name.
    + * @return {Node|undefined} Node.
    + * @private
    + */
    +ol.format.KML.GEOMETRY_NODE_FACTORY_ = function(value, objectStack,
    +    opt_nodeName) {
    +  if (goog.isDefAndNotNull(value)) {
    +    goog.asserts.assertInstanceof(value, ol.geom.Geometry);
    +    var parentNode = objectStack[objectStack.length - 1].node;
    +    goog.asserts.assert(ol.xml.isNode(parentNode));
    +    return ol.xml.createElementNS(parentNode.namespaceURI,
    +        ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_[value.getType()]);
    +  }
    +};
    +
    +
    +/**
    + * A factory for creating coordinates nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.COLOR_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('color');
    +
    +
    +/**
    + * A factory for creating coordinates nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.COORDINATES_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('coordinates');
    +
    +
    +/**
    + * A factory for creating innerBoundaryIs nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('innerBoundaryIs');
    +
    +
    +/**
    + * A factory for creating Point nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.POINT_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('Point');
    +
    +
    +/**
    + * A factory for creating LineString nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.LINE_STRING_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('LineString');
    +
    +
    +/**
    + * A factory for creating LinearRing nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.LINEAR_RING_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('LinearRing');
    +
    +
    +/**
    + * A factory for creating Polygon nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.POLYGON_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('Polygon');
    +
    +
    +/**
    + * A factory for creating outerBoundaryIs nodes.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + * @private
    + */
    +ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_ =
    +    ol.xml.makeSimpleNodeFactory('outerBoundaryIs');
    +
    +
    +/**
    + * Encode an array of features in the KML format.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {string} Result.
    + * @api stable
    + */
    +ol.format.KML.prototype.writeFeatures;
    +
    +
    +/**
    + * Encode an array of features in the KML format as an XML node.
    + *
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + * @api
    + */
    +ol.format.KML.prototype.writeFeaturesNode = function(features, opt_options) {
    +  opt_options = this.adaptOptions(opt_options);
    +  var kml = ol.xml.createElementNS(ol.format.KML.NAMESPACE_URIS_[4], 'kml');
    +  var xmlnsUri = 'http://www.w3.org/2000/xmlns/';
    +  var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance';
    +  ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:gx',
    +      ol.format.KML.GX_NAMESPACE_URIS_[0]);
    +  ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri);
    +  ol.xml.setAttributeNS(kml, xmlSchemaInstanceUri, 'xsi:schemaLocation',
    +      ol.format.KML.SCHEMA_LOCATION_);
    +
    +  var /** @type {ol.xml.NodeStackItem} */ context = {node: kml};
    +  var properties = {};
    +  if (features.length > 1) {
    +    properties['Document'] = features;
    +  } else if (features.length == 1) {
    +    properties['Placemark'] = features[0];
    +  }
    +  var orderedKeys = ol.format.KML.KML_SEQUENCE_[kml.namespaceURI];
    +  var values = ol.xml.makeSequence(properties, orderedKeys);
    +  ol.xml.pushSerializeAndPop(context, ol.format.KML.KML_SERIALIZERS_,
    +      ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys);
    +  return kml;
    +};
    diff --git a/mapbox-gl/ol/ol/format/osmxmlformat.js b/mapbox-gl/ol/ol/format/osmxmlformat.js
    new file mode 100644
    index 0000000000..7f7d7959b8
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/osmxmlformat.js
    @@ -0,0 +1,236 @@
    +// FIXME add typedef for stack state objects
    +goog.provide('ol.format.OSMXML');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading data in the
    + * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML).
    + *
    + * @constructor
    + * @extends {ol.format.XMLFeature}
    + * @api stable
    + */
    +ol.format.OSMXML = function() {
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get('EPSG:4326');
    +};
    +goog.inherits(ol.format.OSMXML, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.OSMXML.EXTENSIONS_ = ['.osm'];
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.OSMXML.prototype.getExtensions = function() {
    +  return ol.format.OSMXML.EXTENSIONS_;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.OSMXML.readNode_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'node');
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  var state = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  var id = node.getAttribute('id');
    +  var coordinates = /** @type {Array.<number>} */ ([
    +    parseFloat(node.getAttribute('lon')),
    +    parseFloat(node.getAttribute('lat'))
    +  ]);
    +  state.nodes[id] = coordinates;
    +
    +  var values = ol.xml.pushParseAndPop({
    +    tags: {}
    +  }, ol.format.OSMXML.NODE_PARSERS_, node, objectStack);
    +  if (!goog.object.isEmpty(values.tags)) {
    +    var geometry = new ol.geom.Point(coordinates);
    +    ol.format.Feature.transformWithOptions(geometry, false, options);
    +    var feature = new ol.Feature(geometry);
    +    feature.setId(id);
    +    feature.setProperties(values.tags);
    +    state.features.push(feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.OSMXML.readWay_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'way');
    +  var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]);
    +  var id = node.getAttribute('id');
    +  var values = ol.xml.pushParseAndPop({
    +    ndrefs: [],
    +    tags: {}
    +  }, ol.format.OSMXML.WAY_PARSERS_, node, objectStack);
    +  var state = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  var flatCoordinates = /** @type {Array.<number>} */ ([]);
    +  for (var i = 0, ii = values.ndrefs.length; i < ii; i++) {
    +    var point = state.nodes[values.ndrefs[i]];
    +    goog.array.extend(flatCoordinates, point);
    +  }
    +  var geometry;
    +  if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) {
    +    // closed way
    +    geometry = new ol.geom.Polygon(null);
    +    geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates,
    +        [flatCoordinates.length]);
    +  } else {
    +    geometry = new ol.geom.LineString(null);
    +    geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
    +  }
    +  ol.format.Feature.transformWithOptions(geometry, false, options);
    +  var feature = new ol.Feature(geometry);
    +  feature.setId(id);
    +  feature.setProperties(values.tags);
    +  state.features.push(feature);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Track.
    + */
    +ol.format.OSMXML.readNd_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'nd');
    +  var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  values.ndrefs.push(node.getAttribute('ref'));
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {ol.Feature|undefined} Track.
    + */
    +ol.format.OSMXML.readTag_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'tag');
    +  var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
    +  values.tags[node.getAttribute('k')] = node.getAttribute('v');
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.format.OSMXML.NAMESPACE_URIS_ = [
    +  null
    +];
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OSMXML.WAY_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OSMXML.NAMESPACE_URIS_, {
    +      'nd': ol.format.OSMXML.readNd_,
    +      'tag': ol.format.OSMXML.readTag_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OSMXML.PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OSMXML.NAMESPACE_URIS_, {
    +      'node': ol.format.OSMXML.readNode_,
    +      'way': ol.format.OSMXML.readWay_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OSMXML.NODE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OSMXML.NAMESPACE_URIS_, {
    +      'tag': ol.format.OSMXML.readTag_
    +    });
    +
    +
    +/**
    + * Read all features from an OSM source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.OSMXML.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var options = this.getReadOptions(node, opt_options);
    +  if (node.localName == 'osm') {
    +    var state = ol.xml.pushParseAndPop({
    +      nodes: {},
    +      features: []
    +    }, ol.format.OSMXML.PARSERS_, node, [options]);
    +    if (goog.isDef(state.features)) {
    +      return state.features;
    +    }
    +  }
    +  return [];
    +};
    +
    +
    +/**
    + * Read the projection from an OSM source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.OSMXML.prototype.readProjection;
    diff --git a/mapbox-gl/ol/ol/format/owsformat.js b/mapbox-gl/ol/ol/format/owsformat.js
    new file mode 100644
    index 0000000000..8bd7590045
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/owsformat.js
    @@ -0,0 +1,461 @@
    +goog.provide('ol.format.OWS');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('ol.format.XLink');
    +goog.require('ol.format.XML');
    +goog.require('ol.format.XSD');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.format.XML}
    + */
    +ol.format.OWS = function() {
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.OWS, ol.format.XML);
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {Object} OWS object.
    + */
    +ol.format.OWS.prototype.readFromDocument = function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readFromNode(n);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {Object} OWS object.
    + */
    +ol.format.OWS.prototype.readFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var owsObject = ol.xml.pushParseAndPop({},
    +      ol.format.OWS.PARSERS_, node, []);
    +  return goog.isDef(owsObject) ? owsObject : null;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readAddress_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Address');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.ADDRESS_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readAllowedValues_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'AllowedValues');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.ALLOWED_VALUES_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readConstraint_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Constraint');
    +  var name = node.getAttribute('name');
    +  if (!goog.isDef(name)) {
    +    return undefined;
    +  }
    +  return ol.xml.pushParseAndPop({'name': name},
    +      ol.format.OWS.CONSTRAINT_PARSERS_, node,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readContactInfo_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ContactInfo');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.CONTACT_INFO_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readDcp_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'DCP');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.DCP_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readGet_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Get');
    +  var href = ol.format.XLink.readHref(node);
    +  if (!goog.isDef(href)) {
    +    return undefined;
    +  }
    +  return ol.xml.pushParseAndPop({'href': href},
    +      ol.format.OWS.REQUEST_METHOD_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readHttp_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'HTTP');
    +  return ol.xml.pushParseAndPop({}, ol.format.OWS.HTTP_PARSERS_,
    +      node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readOperation_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Operation');
    +  var name = node.getAttribute('name');
    +  var value = ol.xml.pushParseAndPop({},
    +      ol.format.OWS.OPERATION_PARSERS_, node, objectStack);
    +  if (!goog.isDef(value)) {
    +    return undefined;
    +  }
    +  var object = /** @type {Object} */
    +      (objectStack[objectStack.length - 1]);
    +  goog.asserts.assert(goog.isObject(object));
    +  object[name] = value;
    +
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readOperationsMetadata_ = function(node,
    +    objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'OperationsMetadata');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.OPERATIONS_METADATA_PARSERS_, node,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readPhone_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Phone');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.OWS.PHONE_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readServiceIdentification_ = function(node,
    +    objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ServiceIdentification');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_, node,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readServiceContact_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ServiceContact');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.OWS.SERVICE_CONTACT_PARSERS_, node,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined}
    + */
    +ol.format.OWS.readServiceProvider_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ServiceProvider');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.OWS.SERVICE_PROVIDER_PARSERS_, node,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {string|undefined}
    + */
    +ol.format.OWS.readValue_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Value');
    +  return ol.format.XSD.readString(node);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + * @private
    + */
    +ol.format.OWS.NAMESPACE_URIS_ = [
    +  null,
    +  'http://www.opengis.net/ows/1.1'
    +];
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'ServiceIdentification': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readServiceIdentification_),
    +      'ServiceProvider': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readServiceProvider_),
    +      'OperationsMetadata': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readOperationsMetadata_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.ADDRESS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'DeliveryPoint': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'AdministrativeArea': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'PostalCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'ElectronicMailAddress': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.ALLOWED_VALUES_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Value': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readValue_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.CONSTRAINT_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'AllowedValues': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readAllowedValues_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.CONTACT_INFO_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Phone': ol.xml.makeObjectPropertySetter(ol.format.OWS.readPhone_),
    +      'Address': ol.xml.makeObjectPropertySetter(ol.format.OWS.readAddress_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.DCP_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'HTTP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readHttp_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.HTTP_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Get': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readGet_),
    +      'Post': undefined // TODO
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.OPERATION_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'DCP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readDcp_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.OPERATIONS_METADATA_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Operation': ol.format.OWS.readOperation_
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.PHONE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Voice': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Facsimile': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.REQUEST_METHOD_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Constraint': ol.xml.makeObjectPropertyPusher(
    +          ol.format.OWS.readConstraint_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.SERVICE_CONTACT_PARSERS_ =
    +    ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'IndividualName': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'PositionName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'ContactInfo': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readContactInfo_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_ =
    +    ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'ServiceTypeVersion': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'ServiceType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.OWS.SERVICE_PROVIDER_PARSERS_ =
    +    ol.xml.makeParsersNS(
    +    ol.format.OWS.NAMESPACE_URIS_, {
    +      'ProviderName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'ProviderSite': ol.xml.makeObjectPropertySetter(ol.format.XLink.readHref),
    +      'ServiceContact': ol.xml.makeObjectPropertySetter(
    +          ol.format.OWS.readServiceContact_)
    +    });
    diff --git a/mapbox-gl/ol/ol/format/polylineformat.js b/mapbox-gl/ol/ol/format/polylineformat.js
    new file mode 100644
    index 0000000000..d5196d555c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/polylineformat.js
    @@ -0,0 +1,393 @@
    +goog.provide('ol.format.Polyline');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.TextFeature');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.flat.flip');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the Encoded
    + * Polyline Algorithm Format.
    + *
    + * @constructor
    + * @extends {ol.format.TextFeature}
    + * @param {olx.format.PolylineOptions=} opt_options
    + *     Optional configuration object.
    + * @api stable
    + */
    +ol.format.Polyline = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get('EPSG:4326');
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.factor_ = goog.isDef(options.factor) ? options.factor : 1e5;
    +};
    +goog.inherits(ol.format.Polyline, ol.format.TextFeature);
    +
    +
    +/**
    + * Encode a list of n-dimensional points and return an encoded string
    + *
    + * Attention: This function will modify the passed array!
    + *
    + * @param {Array.<number>} numbers A list of n-dimensional points.
    + * @param {number} stride The number of dimension of the points in the list.
    + * @param {number=} opt_factor The factor by which the numbers will be
    + *     multiplied. The remaining decimal places will get rounded away.
    + *     Default is `1e5`.
    + * @return {string} The encoded string.
    + * @api
    + */
    +ol.format.Polyline.encodeDeltas = function(numbers, stride, opt_factor) {
    +  var factor = goog.isDef(opt_factor) ? opt_factor : 1e5;
    +  var d;
    +
    +  var lastNumbers = new Array(stride);
    +  for (d = 0; d < stride; ++d) {
    +    lastNumbers[d] = 0;
    +  }
    +
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii;) {
    +    for (d = 0; d < stride; ++d, ++i) {
    +      var num = numbers[i];
    +      var delta = num - lastNumbers[d];
    +      lastNumbers[d] = num;
    +
    +      numbers[i] = delta;
    +    }
    +  }
    +
    +  return ol.format.Polyline.encodeFloats(numbers, factor);
    +};
    +
    +
    +/**
    + * Decode a list of n-dimensional points from an encoded string
    + *
    + * @param {string} encoded An encoded string.
    + * @param {number} stride The number of dimension of the points in the
    + *     encoded string.
    + * @param {number=} opt_factor The factor by which the resulting numbers will
    + *     be divided. Default is `1e5`.
    + * @return {Array.<number>} A list of n-dimensional points.
    + * @api
    + */
    +ol.format.Polyline.decodeDeltas = function(encoded, stride, opt_factor) {
    +  var factor = goog.isDef(opt_factor) ? opt_factor : 1e5;
    +  var d;
    +
    +  /** @type {Array.<number>} */
    +  var lastNumbers = new Array(stride);
    +  for (d = 0; d < stride; ++d) {
    +    lastNumbers[d] = 0;
    +  }
    +
    +  var numbers = ol.format.Polyline.decodeFloats(encoded, factor);
    +
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii;) {
    +    for (d = 0; d < stride; ++d, ++i) {
    +      lastNumbers[d] += numbers[i];
    +
    +      numbers[i] = lastNumbers[d];
    +    }
    +  }
    +
    +  return numbers;
    +};
    +
    +
    +/**
    + * Encode a list of floating point numbers and return an encoded string
    + *
    + * Attention: This function will modify the passed array!
    + *
    + * @param {Array.<number>} numbers A list of floating point numbers.
    + * @param {number=} opt_factor The factor by which the numbers will be
    + *     multiplied. The remaining decimal places will get rounded away.
    + *     Default is `1e5`.
    + * @return {string} The encoded string.
    + * @api
    + */
    +ol.format.Polyline.encodeFloats = function(numbers, opt_factor) {
    +  var factor = goog.isDef(opt_factor) ? opt_factor : 1e5;
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii; ++i) {
    +    numbers[i] = Math.round(numbers[i] * factor);
    +  }
    +
    +  return ol.format.Polyline.encodeSignedIntegers(numbers);
    +};
    +
    +
    +/**
    + * Decode a list of floating point numbers from an encoded string
    + *
    + * @param {string} encoded An encoded string.
    + * @param {number=} opt_factor The factor by which the result will be divided.
    + *     Default is `1e5`.
    + * @return {Array.<number>} A list of floating point numbers.
    + * @api
    + */
    +ol.format.Polyline.decodeFloats = function(encoded, opt_factor) {
    +  var factor = goog.isDef(opt_factor) ? opt_factor : 1e5;
    +  var numbers = ol.format.Polyline.decodeSignedIntegers(encoded);
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii; ++i) {
    +    numbers[i] /= factor;
    +  }
    +  return numbers;
    +};
    +
    +
    +/**
    + * Encode a list of signed integers and return an encoded string
    + *
    + * Attention: This function will modify the passed array!
    + *
    + * @param {Array.<number>} numbers A list of signed integers.
    + * @return {string} The encoded string.
    + */
    +ol.format.Polyline.encodeSignedIntegers = function(numbers) {
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii; ++i) {
    +    var num = numbers[i];
    +    numbers[i] = (num < 0) ? ~(num << 1) : (num << 1);
    +  }
    +  return ol.format.Polyline.encodeUnsignedIntegers(numbers);
    +};
    +
    +
    +/**
    + * Decode a list of signed integers from an encoded string
    + *
    + * @param {string} encoded An encoded string.
    + * @return {Array.<number>} A list of signed integers.
    + */
    +ol.format.Polyline.decodeSignedIntegers = function(encoded) {
    +  var numbers = ol.format.Polyline.decodeUnsignedIntegers(encoded);
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii; ++i) {
    +    var num = numbers[i];
    +    numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
    +  }
    +  return numbers;
    +};
    +
    +
    +/**
    + * Encode a list of unsigned integers and return an encoded string
    + *
    + * @param {Array.<number>} numbers A list of unsigned integers.
    + * @return {string} The encoded string.
    + */
    +ol.format.Polyline.encodeUnsignedIntegers = function(numbers) {
    +  var encoded = '';
    +  var i, ii;
    +  for (i = 0, ii = numbers.length; i < ii; ++i) {
    +    encoded += ol.format.Polyline.encodeUnsignedInteger(numbers[i]);
    +  }
    +  return encoded;
    +};
    +
    +
    +/**
    + * Decode a list of unsigned integers from an encoded string
    + *
    + * @param {string} encoded An encoded string.
    + * @return {Array.<number>} A list of unsigned integers.
    + */
    +ol.format.Polyline.decodeUnsignedIntegers = function(encoded) {
    +  var numbers = [];
    +  var current = 0;
    +  var shift = 0;
    +  var i, ii;
    +  for (i = 0, ii = encoded.length; i < ii; ++i) {
    +    var b = encoded.charCodeAt(i) - 63;
    +    current |= (b & 0x1f) << shift;
    +    if (b < 0x20) {
    +      numbers.push(current);
    +      current = 0;
    +      shift = 0;
    +    } else {
    +      shift += 5;
    +    }
    +  }
    +  return numbers;
    +};
    +
    +
    +/**
    + * Encode one single unsigned integer and return an encoded string
    + *
    + * @param {number} num Unsigned integer that should be encoded.
    + * @return {string} The encoded string.
    + */
    +ol.format.Polyline.encodeUnsignedInteger = function(num) {
    +  var value, encoded = '';
    +  while (num >= 0x20) {
    +    value = (0x20 | (num & 0x1f)) + 63;
    +    encoded += String.fromCharCode(value);
    +    num >>= 5;
    +  }
    +  value = num + 63;
    +  encoded += String.fromCharCode(value);
    +  return encoded;
    +};
    +
    +
    +/**
    + * Read the feature from the Polyline source. The coordinates are assumed to be
    + * in two dimensions and in latitude, longitude order.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api stable
    + */
    +ol.format.Polyline.prototype.readFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.readFeatureFromText = function(text, opt_options) {
    +  var geometry = this.readGeometryFromText(text, opt_options);
    +  return new ol.Feature(geometry);
    +};
    +
    +
    +/**
    + * Read the feature from the source. As Polyline sources contain a single
    + * feature, this will return the feature in an array.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.Polyline.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.readFeaturesFromText =
    +    function(text, opt_options) {
    +  var feature = this.readFeatureFromText(text, opt_options);
    +  return [feature];
    +};
    +
    +
    +/**
    + * Read the geometry from the source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.geom.Geometry} Geometry.
    + * @api stable
    + */
    +ol.format.Polyline.prototype.readGeometry;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.readGeometryFromText =
    +    function(text, opt_options) {
    +  var flatCoordinates = ol.format.Polyline.decodeDeltas(text, 2, this.factor_);
    +  ol.geom.flat.flip.flipXY(
    +      flatCoordinates, 0, flatCoordinates.length, 2, flatCoordinates);
    +  var coordinates = ol.geom.flat.inflate.coordinates(
    +      flatCoordinates, 0, flatCoordinates.length, 2);
    +
    +  return /** @type {ol.geom.Geometry} */ (
    +      ol.format.Feature.transformWithOptions(
    +          new ol.geom.LineString(coordinates), false,
    +          this.adaptOptions(opt_options)));
    +};
    +
    +
    +/**
    + * Read the projection from a Polyline source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.Polyline.prototype.readProjection;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.writeFeatureText = function(feature, opt_options) {
    +  var geometry = feature.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    return this.writeGeometryText(geometry, opt_options);
    +  } else {
    +    goog.asserts.fail();
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.writeFeaturesText =
    +    function(features, opt_options) {
    +  goog.asserts.assert(features.length == 1);
    +  return this.writeFeatureText(features[0], opt_options);
    +};
    +
    +
    +/**
    + * Write a single geometry in Polyline format.
    + *
    + * @function
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} Geometry.
    + * @api stable
    + */
    +ol.format.Polyline.prototype.writeGeometry;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.Polyline.prototype.writeGeometryText =
    +    function(geometry, opt_options) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +  geometry = /** @type {ol.geom.LineString} */
    +      (ol.format.Feature.transformWithOptions(
    +          geometry, true, this.adaptOptions(opt_options)));
    +  var flatCoordinates = geometry.getFlatCoordinates();
    +  var stride = geometry.getStride();
    +  ol.geom.flat.flip.flipXY(
    +      flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates);
    +  return ol.format.Polyline.encodeDeltas(flatCoordinates, stride, this.factor_);
    +};
    diff --git a/mapbox-gl/ol/ol/format/textfeatureformat.js b/mapbox-gl/ol/ol/format/textfeatureformat.js
    new file mode 100644
    index 0000000000..bdaeaf7f44
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/textfeatureformat.js
    @@ -0,0 +1,169 @@
    +goog.provide('ol.format.TextFeature');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.FormatType');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for text feature formats.
    + *
    + * @constructor
    + * @extends {ol.format.Feature}
    + */
    +ol.format.TextFeature = function() {
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.TextFeature, ol.format.Feature);
    +
    +
    +/**
    + * @param {Document|Node|Object|string} source Source.
    + * @private
    + * @return {string} Text.
    + */
    +ol.format.TextFeature.prototype.getText_ = function(source) {
    +  if (goog.isString(source)) {
    +    return source;
    +  } else {
    +    goog.asserts.fail();
    +    return '';
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.getType = function() {
    +  return ol.format.FormatType.TEXT;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.readFeature = function(source, opt_options) {
    +  return this.readFeatureFromText(
    +      this.getText_(source), this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {string} text Text.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.TextFeature.prototype.readFeatureFromText = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.readFeatures = function(source, opt_options) {
    +  return this.readFeaturesFromText(
    +      this.getText_(source), this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {string} text Text.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.TextFeature.prototype.readFeaturesFromText = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.readGeometry = function(source, opt_options) {
    +  return this.readGeometryFromText(
    +      this.getText_(source), this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {string} text Text.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @protected
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.TextFeature.prototype.readGeometryFromText = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.readProjection = function(source) {
    +  return this.readProjectionFromText(this.getText_(source));
    +};
    +
    +
    +/**
    + * @param {string} text Text.
    + * @protected
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.TextFeature.prototype.readProjectionFromText = function(text) {
    +  return this.defaultDataProjection;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.writeFeature = function(feature, opt_options) {
    +  return this.writeFeatureText(feature, this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @protected
    + * @return {string} Text.
    + */
    +ol.format.TextFeature.prototype.writeFeatureText = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.writeFeatures = function(
    +    features, opt_options) {
    +  return this.writeFeaturesText(features, this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @protected
    + * @return {string} Text.
    + */
    +ol.format.TextFeature.prototype.writeFeaturesText = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TextFeature.prototype.writeGeometry = function(
    +    geometry, opt_options) {
    +  return this.writeGeometryText(geometry, this.adaptOptions(opt_options));
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @protected
    + * @return {string} Text.
    + */
    +ol.format.TextFeature.prototype.writeGeometryText = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/format/topojsonformat.js b/mapbox-gl/ol/ol/format/topojsonformat.js
    new file mode 100644
    index 0000000000..03bc9c5ca1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/topojsonformat.js
    @@ -0,0 +1,409 @@
    +goog.provide('ol.format.TopoJSON');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.JSONFeature');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the TopoJSON format.
    + *
    + * @constructor
    + * @extends {ol.format.JSONFeature}
    + * @param {olx.format.TopoJSONOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.format.TopoJSON = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * @inheritDoc
    +   */
    +  this.defaultDataProjection = ol.proj.get(
    +      goog.isDefAndNotNull(options.defaultDataProjection) ?
    +          options.defaultDataProjection : 'EPSG:4326');
    +
    +};
    +goog.inherits(ol.format.TopoJSON, ol.format.JSONFeature);
    +
    +
    +/**
    + * @const {Array.<string>}
    + * @private
    + */
    +ol.format.TopoJSON.EXTENSIONS_ = ['.topojson'];
    +
    +
    +/**
    + * Concatenate arcs into a coordinate array.
    + * @param {Array.<number>} indices Indices of arcs to concatenate.  Negative
    + *     values indicate arcs need to be reversed.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs (already
    + *     transformed).
    + * @return {Array.<ol.Coordinate>} Coordinates array.
    + * @private
    + */
    +ol.format.TopoJSON.concatenateArcs_ = function(indices, arcs) {
    +  /** @type {Array.<ol.Coordinate>} */
    +  var coordinates = [];
    +  var index, arc;
    +  var i, ii;
    +  var j, jj;
    +  for (i = 0, ii = indices.length; i < ii; ++i) {
    +    index = indices[i];
    +    if (i > 0) {
    +      // splicing together arcs, discard last point
    +      coordinates.pop();
    +    }
    +    if (index >= 0) {
    +      // forward arc
    +      arc = arcs[index];
    +    } else {
    +      // reverse arc
    +      arc = arcs[~index].slice().reverse();
    +    }
    +    coordinates.push.apply(coordinates, arc);
    +  }
    +  // provide fresh copies of coordinate arrays
    +  for (j = 0, jj = coordinates.length; j < jj; ++j) {
    +    coordinates[j] = coordinates[j].slice();
    +  }
    +  return coordinates;
    +};
    +
    +
    +/**
    + * Create a point from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @return {ol.geom.Point} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readPointGeometry_ = function(object, scale, translate) {
    +  var coordinates = object.coordinates;
    +  if (!goog.isNull(scale) && !goog.isNull(translate)) {
    +    ol.format.TopoJSON.transformVertex_(coordinates, scale, translate);
    +  }
    +  return new ol.geom.Point(coordinates);
    +};
    +
    +
    +/**
    + * Create a multi-point from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @return {ol.geom.MultiPoint} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readMultiPointGeometry_ = function(object, scale,
    +    translate) {
    +  var coordinates = object.coordinates;
    +  var i, ii;
    +  if (!goog.isNull(scale) && !goog.isNull(translate)) {
    +    for (i = 0, ii = coordinates.length; i < ii; ++i) {
    +      ol.format.TopoJSON.transformVertex_(coordinates[i], scale, translate);
    +    }
    +  }
    +  return new ol.geom.MultiPoint(coordinates);
    +};
    +
    +
    +/**
    + * Create a linestring from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @return {ol.geom.LineString} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readLineStringGeometry_ = function(object, arcs) {
    +  var coordinates = ol.format.TopoJSON.concatenateArcs_(object.arcs, arcs);
    +  return new ol.geom.LineString(coordinates);
    +};
    +
    +
    +/**
    + * Create a multi-linestring from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @return {ol.geom.MultiLineString} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readMultiLineStringGeometry_ = function(object, arcs) {
    +  var coordinates = [];
    +  var i, ii;
    +  for (i = 0, ii = object.arcs.length; i < ii; ++i) {
    +    coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs);
    +  }
    +  return new ol.geom.MultiLineString(coordinates);
    +};
    +
    +
    +/**
    + * Create a polygon from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @return {ol.geom.Polygon} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readPolygonGeometry_ = function(object, arcs) {
    +  var coordinates = [];
    +  var i, ii;
    +  for (i = 0, ii = object.arcs.length; i < ii; ++i) {
    +    coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs);
    +  }
    +  return new ol.geom.Polygon(coordinates);
    +};
    +
    +
    +/**
    + * Create a multi-polygon from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @return {ol.geom.MultiPolygon} Geometry.
    + * @private
    + */
    +ol.format.TopoJSON.readMultiPolygonGeometry_ = function(object, arcs) {
    +  var coordinates = [];
    +  var polyArray, ringCoords, j, jj;
    +  var i, ii;
    +  for (i = 0, ii = object.arcs.length; i < ii; ++i) {
    +    // for each polygon
    +    polyArray = object.arcs[i];
    +    ringCoords = [];
    +    for (j = 0, jj = polyArray.length; j < jj; ++j) {
    +      // for each ring
    +      ringCoords[j] = ol.format.TopoJSON.concatenateArcs_(polyArray[j], arcs);
    +    }
    +    coordinates[i] = ringCoords;
    +  }
    +  return new ol.geom.MultiPolygon(coordinates);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TopoJSON.prototype.getExtensions = function() {
    +  return ol.format.TopoJSON.EXTENSIONS_;
    +};
    +
    +
    +/**
    + * Create features from a TopoJSON GeometryCollection object.
    + *
    + * @param {TopoJSONGeometryCollection} collection TopoJSON Geometry
    + *     object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Array of features.
    + * @private
    + */
    +ol.format.TopoJSON.readFeaturesFromGeometryCollection_ = function(
    +    collection, arcs, scale, translate, opt_options) {
    +  var geometries = collection.geometries;
    +  var features = [];
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    features[i] = ol.format.TopoJSON.readFeatureFromGeometry_(
    +        geometries[i], arcs, scale, translate, opt_options);
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * Create a feature from a TopoJSON geometry object.
    + *
    + * @param {TopoJSONGeometry} object TopoJSON geometry object.
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @private
    + */
    +ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs,
    +    scale, translate, opt_options) {
    +  var geometry;
    +  var type = object.type;
    +  var geometryReader = ol.format.TopoJSON.GEOMETRY_READERS_[type];
    +  goog.asserts.assert(goog.isDef(geometryReader));
    +  if ((type === 'Point') || (type === 'MultiPoint')) {
    +    geometry = geometryReader(object, scale, translate);
    +  } else {
    +    geometry = geometryReader(object, arcs);
    +  }
    +  var feature = new ol.Feature();
    +  feature.setGeometry(/** @type {ol.geom.Geometry} */ (
    +      ol.format.Feature.transformWithOptions(geometry, false, opt_options)));
    +  if (goog.isDef(object.id)) {
    +    feature.setId(object.id);
    +  }
    +  if (goog.isDef(object.properties)) {
    +    feature.setProperties(object.properties);
    +  }
    +  return feature;
    +};
    +
    +
    +/**
    + * Read all features from a TopoJSON source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.TopoJSON.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.TopoJSON.prototype.readFeaturesFromObject = function(
    +    object, opt_options) {
    +  if (object.type == 'Topology') {
    +    var topoJSONTopology = /** @type {TopoJSONTopology} */ (object);
    +    var transform, scale = null, translate = null;
    +    if (goog.isDef(topoJSONTopology.transform)) {
    +      transform = /** @type {TopoJSONTransform} */
    +          (topoJSONTopology.transform);
    +      scale = transform.scale;
    +      translate = transform.translate;
    +    }
    +    var arcs = topoJSONTopology.arcs;
    +    if (goog.isDef(transform)) {
    +      ol.format.TopoJSON.transformArcs_(arcs, scale, translate);
    +    }
    +    /** @type {Array.<ol.Feature>} */
    +    var features = [];
    +    var topoJSONFeatures = goog.object.getValues(topoJSONTopology.objects);
    +    var i, ii;
    +    var feature;
    +    for (i = 0, ii = topoJSONFeatures.length; i < ii; ++i) {
    +      if (topoJSONFeatures[i].type === 'GeometryCollection') {
    +        feature = /** @type {TopoJSONGeometryCollection} */
    +            (topoJSONFeatures[i]);
    +        features.push.apply(features,
    +            ol.format.TopoJSON.readFeaturesFromGeometryCollection_(
    +                feature, arcs, scale, translate, opt_options));
    +      } else {
    +        feature = /** @type {TopoJSONGeometry} */
    +            (topoJSONFeatures[i]);
    +        features.push(ol.format.TopoJSON.readFeatureFromGeometry_(
    +            feature, arcs, scale, translate, opt_options));
    +      }
    +    }
    +    return features;
    +  } else {
    +    return [];
    +  }
    +};
    +
    +
    +/**
    + * Apply a linear transform to array of arcs.  The provided array of arcs is
    + * modified in place.
    + *
    + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @private
    + */
    +ol.format.TopoJSON.transformArcs_ = function(arcs, scale, translate) {
    +  var i, ii;
    +  for (i = 0, ii = arcs.length; i < ii; ++i) {
    +    ol.format.TopoJSON.transformArc_(arcs[i], scale, translate);
    +  }
    +};
    +
    +
    +/**
    + * Apply a linear transform to an arc.  The provided arc is modified in place.
    + *
    + * @param {Array.<ol.Coordinate>} arc Arc.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @private
    + */
    +ol.format.TopoJSON.transformArc_ = function(arc, scale, translate) {
    +  var x = 0;
    +  var y = 0;
    +  var vertex;
    +  var i, ii;
    +  for (i = 0, ii = arc.length; i < ii; ++i) {
    +    vertex = arc[i];
    +    x += vertex[0];
    +    y += vertex[1];
    +    vertex[0] = x;
    +    vertex[1] = y;
    +    ol.format.TopoJSON.transformVertex_(vertex, scale, translate);
    +  }
    +};
    +
    +
    +/**
    + * Apply a linear transform to a vertex.  The provided vertex is modified in
    + * place.
    + *
    + * @param {ol.Coordinate} vertex Vertex.
    + * @param {Array.<number>} scale Scale for each dimension.
    + * @param {Array.<number>} translate Translation for each dimension.
    + * @private
    + */
    +ol.format.TopoJSON.transformVertex_ = function(vertex, scale, translate) {
    +  vertex[0] = vertex[0] * scale[0] + translate[0];
    +  vertex[1] = vertex[1] * scale[1] + translate[1];
    +};
    +
    +
    +/**
    + * Read the projection from a TopoJSON source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} object Source.
    + * @return {ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.TopoJSON.prototype.readProjection = function(object) {
    +  return this.defaultDataProjection;
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<string, function(TopoJSONGeometry, Array, ...Array): ol.geom.Geometry>}
    + */
    +ol.format.TopoJSON.GEOMETRY_READERS_ = {
    +  'Point': ol.format.TopoJSON.readPointGeometry_,
    +  'LineString': ol.format.TopoJSON.readLineStringGeometry_,
    +  'Polygon': ol.format.TopoJSON.readPolygonGeometry_,
    +  'MultiPoint': ol.format.TopoJSON.readMultiPointGeometry_,
    +  'MultiLineString': ol.format.TopoJSON.readMultiLineStringGeometry_,
    +  'MultiPolygon': ol.format.TopoJSON.readMultiPolygonGeometry_
    +};
    diff --git a/mapbox-gl/ol/ol/format/wfsformat.js b/mapbox-gl/ol/ol/format/wfsformat.js
    new file mode 100644
    index 0000000000..8bc16a317f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/wfsformat.js
    @@ -0,0 +1,770 @@
    +goog.provide('ol.format.WFS');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('ol.format.GML3');
    +goog.require('ol.format.GMLBase');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.format.XSD');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Feature format for reading and writing data in the WFS format.
    + * By default, supports WFS version 1.1.0. You can pass a GML format
    + * as option if you want to read a WFS that contains GML2 (WFS 1.0.0).
    + * Also see {@link ol.format.GMLBase} which is used by this format.
    + *
    + * @constructor
    + * @param {olx.format.WFSOptions=} opt_options
    + *     Optional configuration object.
    + * @extends {ol.format.XMLFeature}
    + * @api stable
    + */
    +ol.format.WFS = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<string>|string|undefined}
    +   */
    +  this.featureType_ = options.featureType;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, string>|string|undefined}
    +   */
    +  this.featureNS_ = options.featureNS;
    +
    +  /**
    +   * @private
    +   * @type {ol.format.GMLBase}
    +   */
    +  this.gmlFormat_ = goog.isDef(options.gmlFormat) ?
    +      options.gmlFormat : new ol.format.GML3();
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.schemaLocation_ = goog.isDef(options.schemaLocation) ?
    +      options.schemaLocation : ol.format.WFS.SCHEMA_LOCATION;
    +
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.WFS, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.WFS.FEATURE_PREFIX = 'feature';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.WFS.XMLNS = 'http://www.w3.org/2000/xmlns/';
    +
    +
    +/**
    + * Number of features; bounds/extent.
    + * @typedef {{numberOfFeatures: number,
    + *            bounds: ol.Extent}}
    + * @api stable
    + */
    +ol.format.WFS.FeatureCollectionMetadata;
    +
    +
    +/**
    + * Total deleted; total inserted; total updated; array of insert ids.
    + * @typedef {{totalDeleted: number,
    + *            totalInserted: number,
    + *            totalUpdated: number,
    + *            insertIds: Array.<string>}}
    + * @api stable
    + */
    +ol.format.WFS.TransactionResponse;
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.WFS.SCHEMA_LOCATION = 'http://www.opengis.net/wfs ' +
    +    'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd';
    +
    +
    +/**
    + * Read all features from a WFS FeatureCollection.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.WFS.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WFS.prototype.readFeaturesFromNode = function(node, opt_options) {
    +  var context = {
    +    'featureType': this.featureType_,
    +    'featureNS': this.featureNS_
    +  };
    +  goog.object.extend(context, this.getReadOptions(node,
    +      goog.isDef(opt_options) ? opt_options : {}));
    +  var objectStack = [context];
    +  this.gmlFormat_.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][
    +      'featureMember'] =
    +      ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal);
    +  var features = ol.xml.pushParseAndPop([],
    +      this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
    +      objectStack, this.gmlFormat_);
    +  if (!goog.isDef(features)) {
    +    features = [];
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.format.WFS.TransactionResponse|undefined} Transaction response.
    + * @api stable
    + */
    +ol.format.WFS.prototype.readTransactionResponse = function(source) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readTransactionResponseFromDocument(
    +        /** @type {Document} */ (source));
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readTransactionResponseFromNode(/** @type {Node} */ (source));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readTransactionResponseFromDocument(doc);
    +  } else {
    +    goog.asserts.fail();
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {ol.format.WFS.FeatureCollectionMetadata|undefined}
    + *     FeatureCollection metadata.
    + * @api stable
    + */
    +ol.format.WFS.prototype.readFeatureCollectionMetadata = function(source) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readFeatureCollectionMetadataFromDocument(
    +        /** @type {Document} */ (source));
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readFeatureCollectionMetadataFromNode(
    +        /** @type {Node} */ (source));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readFeatureCollectionMetadataFromDocument(doc);
    +  } else {
    +    goog.asserts.fail();
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {ol.format.WFS.FeatureCollectionMetadata|undefined}
    + *     FeatureCollection metadata.
    + */
    +ol.format.WFS.prototype.readFeatureCollectionMetadataFromDocument =
    +    function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readFeatureCollectionMetadataFromNode(n);
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WFS.FEATURE_COLLECTION_PARSERS_ = {
    +  'http://www.opengis.net/gml': {
    +    'boundedBy': ol.xml.makeObjectPropertySetter(
    +        ol.format.GMLBase.prototype.readGeometryElement, 'bounds')
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {ol.format.WFS.FeatureCollectionMetadata|undefined}
    + *     FeatureCollection metadata.
    + */
    +ol.format.WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'FeatureCollection');
    +  var result = {};
    +  var value = ol.format.XSD.readNonNegativeIntegerString(
    +      node.getAttribute('numberOfFeatures'));
    +  result['numberOfFeatures'] = value;
    +  return ol.xml.pushParseAndPop(
    +      /** @type {ol.format.WFS.FeatureCollectionMetadata} */ (result),
    +      ol.format.WFS.FEATURE_COLLECTION_PARSERS_, node, [], this.gmlFormat_);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'totalInserted': ol.xml.makeObjectPropertySetter(
    +        ol.format.XSD.readNonNegativeInteger),
    +    'totalUpdated': ol.xml.makeObjectPropertySetter(
    +        ol.format.XSD.readNonNegativeInteger),
    +    'totalDeleted': ol.xml.makeObjectPropertySetter(
    +        ol.format.XSD.readNonNegativeInteger)
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Transaction Summary.
    + * @private
    + */
    +ol.format.WFS.readTransactionSummary_ = function(node, objectStack) {
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WFS.OGC_FID_PARSERS_ = {
    +  'http://www.opengis.net/ogc': {
    +    'FeatureId': ol.xml.makeArrayPusher(function(node, objectStack) {
    +      return node.getAttribute('fid');
    +    })
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + */
    +ol.format.WFS.fidParser_ = function(node, objectStack) {
    +  ol.xml.parseNode(ol.format.WFS.OGC_FID_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WFS.INSERT_RESULTS_PARSERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'Feature': ol.format.WFS.fidParser_
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Array.<string>|undefined} Insert results.
    + * @private
    + */
    +ol.format.WFS.readInsertResults_ = function(node, objectStack) {
    +  return ol.xml.pushParseAndPop(
    +      [], ol.format.WFS.INSERT_RESULTS_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'TransactionSummary': ol.xml.makeObjectPropertySetter(
    +        ol.format.WFS.readTransactionSummary_, 'transactionSummary'),
    +    'InsertResults': ol.xml.makeObjectPropertySetter(
    +        ol.format.WFS.readInsertResults_, 'insertIds')
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {ol.format.WFS.TransactionResponse|undefined} Transaction response.
    + */
    +ol.format.WFS.prototype.readTransactionResponseFromDocument = function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readTransactionResponseFromNode(n);
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {ol.format.WFS.TransactionResponse|undefined} Transaction response.
    + */
    +ol.format.WFS.prototype.readTransactionResponseFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'TransactionResponse');
    +  return ol.xml.pushParseAndPop(
    +      /** @type {ol.format.WFS.TransactionResponse} */({}),
    +      ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_, node, []);
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.WFS.QUERY_SERIALIZERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'PropertyName': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeFeature_ = function(node, feature, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featureType = context['featureType'];
    +  var featureNS = context['featureNS'];
    +  var child = ol.xml.createElementNS(featureNS, featureType);
    +  node.appendChild(child);
    +  ol.format.GML3.prototype.writeFeatureElement(child, feature, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {number|string} fid Feature identifier.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeOgcFidFilter_ = function(node, fid, objectStack) {
    +  var filter = ol.xml.createElementNS('http://www.opengis.net/ogc', 'Filter');
    +  var child = ol.xml.createElementNS('http://www.opengis.net/ogc', 'FeatureId');
    +  filter.appendChild(child);
    +  child.setAttribute('fid', fid);
    +  node.appendChild(filter);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeDelete_ = function(node, feature, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featureType = context['featureType'];
    +  var featurePrefix = context['featurePrefix'];
    +  featurePrefix = goog.isDef(featurePrefix) ? featurePrefix :
    +      ol.format.WFS.FEATURE_PREFIX;
    +  var featureNS = context['featureNS'];
    +  node.setAttribute('typeName', featurePrefix + ':' + featureType);
    +  ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
    +      featureNS);
    +  var fid = feature.getId();
    +  if (goog.isDef(fid)) {
    +    ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Feature} feature Feature.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeUpdate_ = function(node, feature, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featureType = context['featureType'];
    +  var featurePrefix = context['featurePrefix'];
    +  featurePrefix = goog.isDef(featurePrefix) ? featurePrefix :
    +      ol.format.WFS.FEATURE_PREFIX;
    +  var featureNS = context['featureNS'];
    +  node.setAttribute('typeName', featurePrefix + ':' + featureType);
    +  ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
    +      featureNS);
    +  var fid = feature.getId();
    +  if (goog.isDef(fid)) {
    +    var keys = feature.getKeys();
    +    var values = [];
    +    for (var i = 0, ii = keys.length; i < ii; i++) {
    +      var value = feature.get(keys[i]);
    +      if (goog.isDef(value)) {
    +        values.push({name: keys[i], value: value});
    +      }
    +    }
    +    ol.xml.pushSerializeAndPop({node: node, srsName:
    +          context['srsName']},
    +    ol.format.WFS.TRANSACTION_SERIALIZERS_,
    +    ol.xml.makeSimpleNodeFactory('Property'), values,
    +    objectStack);
    +    ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Object} pair Property name and value.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeProperty_ = function(node, pair, objectStack) {
    +  var name = ol.xml.createElementNS('http://www.opengis.net/wfs', 'Name');
    +  node.appendChild(name);
    +  ol.format.XSD.writeStringTextNode(name, pair.name);
    +  if (goog.isDefAndNotNull(pair.value)) {
    +    var value = ol.xml.createElementNS('http://www.opengis.net/wfs', 'Value');
    +    node.appendChild(value);
    +    if (pair.value instanceof ol.geom.Geometry) {
    +      ol.format.GML3.prototype.writeGeometryElement(value,
    +          pair.value, objectStack);
    +    } else {
    +      ol.format.XSD.writeStringTextNode(value, pair.value);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {{vendorId: string, safeToIgnore: boolean, value: string}}
    + *     nativeElement The native element.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeNative_ = function(node, nativeElement, objectStack) {
    +  if (goog.isDef(nativeElement.vendorId)) {
    +    node.setAttribute('vendorId', nativeElement.vendorId);
    +  }
    +  if (goog.isDef(nativeElement.safeToIgnore)) {
    +    node.setAttribute('safeToIgnore', nativeElement.safeToIgnore);
    +  }
    +  if (goog.isDef(nativeElement.value)) {
    +    ol.format.XSD.writeStringTextNode(node, nativeElement.value);
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.WFS.TRANSACTION_SERIALIZERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'Insert': ol.xml.makeChildAppender(ol.format.WFS.writeFeature_),
    +    'Update': ol.xml.makeChildAppender(ol.format.WFS.writeUpdate_),
    +    'Delete': ol.xml.makeChildAppender(ol.format.WFS.writeDelete_),
    +    'Property': ol.xml.makeChildAppender(ol.format.WFS.writeProperty_),
    +    'Native': ol.xml.makeChildAppender(ol.format.WFS.writeNative_)
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {string} featureType Feature type.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeQuery_ = function(node, featureType, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var featurePrefix = context['featurePrefix'];
    +  var featureNS = context['featureNS'];
    +  var propertyNames = context['propertyNames'];
    +  var srsName = context['srsName'];
    +  var prefix = goog.isDef(featurePrefix) ? featurePrefix + ':' : '';
    +  node.setAttribute('typeName', prefix + featureType);
    +  if (goog.isDef(srsName)) {
    +    node.setAttribute('srsName', srsName);
    +  }
    +  if (goog.isDef(featureNS)) {
    +    ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix,
    +        featureNS);
    +  }
    +  var item = goog.object.clone(context);
    +  item.node = node;
    +  ol.xml.pushSerializeAndPop(item,
    +      ol.format.WFS.QUERY_SERIALIZERS_,
    +      ol.xml.makeSimpleNodeFactory('PropertyName'), propertyNames,
    +      objectStack);
    +  var bbox = context['bbox'];
    +  if (goog.isDef(bbox)) {
    +    var child = ol.xml.createElementNS('http://www.opengis.net/ogc', 'Filter');
    +    ol.format.WFS.writeOgcBBOX_(child, bbox, objectStack);
    +    node.appendChild(child);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {string} value PropertyName value.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeOgcPropertyName_ = function(node, value, objectStack) {
    +  var property = ol.xml.createElementNS('http://www.opengis.net/ogc',
    +      'PropertyName');
    +  ol.format.XSD.writeStringTextNode(property, value);
    +  node.appendChild(property);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {ol.Extent} bbox Bounding box.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeOgcBBOX_ = function(node, bbox, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var geometryName = context['geometryName'];
    +  var bboxNode = ol.xml.createElementNS('http://www.opengis.net/ogc', 'BBOX');
    +  node.appendChild(bboxNode);
    +  ol.format.WFS.writeOgcPropertyName_(bboxNode, geometryName, objectStack);
    +  ol.format.GML3.prototype.writeGeometryElement(bboxNode, bbox, objectStack);
    +};
    +
    +
    +/**
    + * @type {Object.<string, Object.<string, ol.xml.Serializer>>}
    + * @private
    + */
    +ol.format.WFS.GETFEATURE_SERIALIZERS_ = {
    +  'http://www.opengis.net/wfs': {
    +    'Query': ol.xml.makeChildAppender(
    +        ol.format.WFS.writeQuery_)
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<{string}>} featureTypes Feature types.
    + * @param {Array.<*>} objectStack Node stack.
    + * @private
    + */
    +ol.format.WFS.writeGetFeature_ = function(node, featureTypes, objectStack) {
    +  var context = objectStack[objectStack.length - 1];
    +  goog.asserts.assert(goog.isObject(context));
    +  var item = goog.object.clone(context);
    +  item.node = node;
    +  ol.xml.pushSerializeAndPop(item,
    +      ol.format.WFS.GETFEATURE_SERIALIZERS_,
    +      ol.xml.makeSimpleNodeFactory('Query'), featureTypes,
    +      objectStack);
    +};
    +
    +
    +/**
    + * @param {olx.format.WFSWriteGetFeatureOptions} options Options.
    + * @return {Node} Result.
    + * @api stable
    + */
    +ol.format.WFS.prototype.writeGetFeature = function(options) {
    +  var node = ol.xml.createElementNS('http://www.opengis.net/wfs',
    +      'GetFeature');
    +  node.setAttribute('service', 'WFS');
    +  node.setAttribute('version', '1.1.0');
    +  if (goog.isDef(options)) {
    +    if (goog.isDef(options.handle)) {
    +      node.setAttribute('handle', options.handle);
    +    }
    +    if (goog.isDef(options.outputFormat)) {
    +      node.setAttribute('outputFormat', options.outputFormat);
    +    }
    +    if (goog.isDef(options.maxFeatures)) {
    +      node.setAttribute('maxFeatures', options.maxFeatures);
    +    }
    +    if (goog.isDef(options.resultType)) {
    +      node.setAttribute('resultType', options.resultType);
    +    }
    +    if (goog.isDef(options.startIndex)) {
    +      node.setAttribute('startIndex', options.startIndex);
    +    }
    +    if (goog.isDef(options.count)) {
    +      node.setAttribute('count', options.count);
    +    }
    +  }
    +  ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
    +      'xsi:schemaLocation', this.schemaLocation_);
    +  var context = {
    +    node: node,
    +    srsName: options.srsName,
    +    featureNS: goog.isDef(options.featureNS) ?
    +        options.featureNS : this.featureNS_,
    +    featurePrefix: options.featurePrefix,
    +    geometryName: options.geometryName,
    +    bbox: options.bbox,
    +    propertyNames: goog.isDef(options.propertyNames) ?
    +        options.propertyNames : []
    +  };
    +  goog.asserts.assert(goog.isArray(options.featureTypes));
    +  ol.format.WFS.writeGetFeature_(node, options.featureTypes, [context]);
    +  return node;
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} inserts The features to insert.
    + * @param {Array.<ol.Feature>} updates The features to update.
    + * @param {Array.<ol.Feature>} deletes The features to delete.
    + * @param {olx.format.WFSWriteTransactionOptions} options Write options.
    + * @return {Node} Result.
    + * @api stable
    + */
    +ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes,
    +    options) {
    +  var objectStack = [];
    +  var node = ol.xml.createElementNS('http://www.opengis.net/wfs',
    +      'Transaction');
    +  node.setAttribute('service', 'WFS');
    +  node.setAttribute('version', '1.1.0');
    +  var baseObj, obj;
    +  if (goog.isDef(options)) {
    +    baseObj = goog.isDef(options.gmlOptions) ? options.gmlOptions : {};
    +    if (goog.isDef(options.handle)) {
    +      node.setAttribute('handle', options.handle);
    +    }
    +  }
    +  ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance',
    +      'xsi:schemaLocation', this.schemaLocation_);
    +  if (goog.isDefAndNotNull(inserts)) {
    +    obj = {node: node, featureNS: options.featureNS,
    +      featureType: options.featureType, featurePrefix: options.featurePrefix};
    +    goog.object.extend(obj, baseObj);
    +    ol.xml.pushSerializeAndPop(obj,
    +        ol.format.WFS.TRANSACTION_SERIALIZERS_,
    +        ol.xml.makeSimpleNodeFactory('Insert'), inserts,
    +        objectStack);
    +  }
    +  if (goog.isDefAndNotNull(updates)) {
    +    obj = {node: node, featureNS: options.featureNS,
    +      featureType: options.featureType, featurePrefix: options.featurePrefix};
    +    goog.object.extend(obj, baseObj);
    +    ol.xml.pushSerializeAndPop(obj,
    +        ol.format.WFS.TRANSACTION_SERIALIZERS_,
    +        ol.xml.makeSimpleNodeFactory('Update'), updates,
    +        objectStack);
    +  }
    +  if (goog.isDefAndNotNull(deletes)) {
    +    ol.xml.pushSerializeAndPop({node: node, featureNS: options.featureNS,
    +      featureType: options.featureType, featurePrefix: options.featurePrefix},
    +    ol.format.WFS.TRANSACTION_SERIALIZERS_,
    +    ol.xml.makeSimpleNodeFactory('Delete'), deletes,
    +    objectStack);
    +  }
    +  if (goog.isDef(options.nativeElements)) {
    +    ol.xml.pushSerializeAndPop({node: node, featureNS: options.featureNS,
    +      featureType: options.featureType, featurePrefix: options.featurePrefix},
    +    ol.format.WFS.TRANSACTION_SERIALIZERS_,
    +    ol.xml.makeSimpleNodeFactory('Native'), options.nativeElements,
    +    objectStack);
    +  }
    +  return node;
    +};
    +
    +
    +/**
    + * Read the projection from a WFS source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {?ol.proj.Projection} Projection.
    + * @api stable
    + */
    +ol.format.WFS.prototype.readProjection;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WFS.prototype.readProjectionFromDocument = function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readProjectionFromNode(n);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WFS.prototype.readProjectionFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'FeatureCollection');
    +
    +  if (goog.isDefAndNotNull(node.firstElementChild) &&
    +      goog.isDefAndNotNull(node.firstElementChild.firstElementChild)) {
    +    node = node.firstElementChild.firstElementChild;
    +    for (var n = node.firstElementChild; !goog.isNull(n);
    +        n = n.nextElementSibling) {
    +      if (!(n.childNodes.length === 0 ||
    +          (n.childNodes.length === 1 &&
    +          n.firstChild.nodeType === 3))) {
    +        var objectStack = [{}];
    +        this.gmlFormat_.readGeometryElement(n, objectStack);
    +        return ol.proj.get(objectStack.pop().srsName);
    +      }
    +    }
    +  }
    +
    +  return null;
    +};
    diff --git a/mapbox-gl/ol/ol/format/wktformat.js b/mapbox-gl/ol/ol/format/wktformat.js
    new file mode 100644
    index 0000000000..97505f0191
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/wktformat.js
    @@ -0,0 +1,852 @@
    +goog.provide('ol.format.WKT');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.Feature');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.TextFeature');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.GeometryCollection');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.format.TextFeature}
    + * @param {olx.format.WKTOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.format.WKT = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this);
    +
    +  /**
    +   * Split GeometryCollection into multiple features.
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.splitCollection_ = goog.isDef(options.splitCollection) ?
    +      options.splitCollection : false;
    +
    +};
    +goog.inherits(ol.format.WKT, ol.format.TextFeature);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.WKT.EMPTY = 'EMPTY';
    +
    +
    +/**
    + * @param {ol.geom.Point} geom Point geometry.
    + * @return {string} Coordinates part of Point as WKT.
    + * @private
    + */
    +ol.format.WKT.encodePointGeometry_ = function(geom) {
    +  var coordinates = geom.getCoordinates();
    +  if (goog.array.isEmpty(coordinates)) {
    +    return '';
    +  }
    +  return coordinates[0] + ' ' + coordinates[1];
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiPoint} geom MultiPoint geometry.
    + * @return {string} Coordinates part of MultiPoint as WKT.
    + * @private
    + */
    +ol.format.WKT.encodeMultiPointGeometry_ = function(geom) {
    +  var array = [];
    +  var components = geom.getPoints();
    +  for (var i = 0, ii = components.length; i < ii; ++i) {
    +    array.push('(' + ol.format.WKT.encodePointGeometry_(components[i]) + ')');
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryCollection} geom GeometryCollection geometry.
    + * @return {string} Coordinates part of GeometryCollection as WKT.
    + * @private
    + */
    +ol.format.WKT.encodeGeometryCollectionGeometry_ = function(geom) {
    +  var array = [];
    +  var geoms = geom.getGeometries();
    +  for (var i = 0, ii = geoms.length; i < ii; ++i) {
    +    array.push(ol.format.WKT.encode_(geoms[i]));
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * @param {ol.geom.LineString|ol.geom.LinearRing} geom LineString geometry.
    + * @return {string} Coordinates part of LineString as WKT.
    + * @private
    + */
    +ol.format.WKT.encodeLineStringGeometry_ = function(geom) {
    +  var coordinates = geom.getCoordinates();
    +  var array = [];
    +  for (var i = 0, ii = coordinates.length; i < ii; ++i) {
    +    array.push(coordinates[i][0] + ' ' + coordinates[i][1]);
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiLineString} geom MultiLineString geometry.
    + * @return {string} Coordinates part of MultiLineString as WKT.
    + * @private
    + */
    +ol.format.WKT.encodeMultiLineStringGeometry_ = function(geom) {
    +  var array = [];
    +  var components = geom.getLineStrings();
    +  for (var i = 0, ii = components.length; i < ii; ++i) {
    +    array.push('(' + ol.format.WKT.encodeLineStringGeometry_(
    +        components[i]) + ')');
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * @param {ol.geom.Polygon} geom Polygon geometry.
    + * @return {string} Coordinates part of Polygon as WKT.
    + * @private
    + */
    +ol.format.WKT.encodePolygonGeometry_ = function(geom) {
    +  var array = [];
    +  var rings = geom.getLinearRings();
    +  for (var i = 0, ii = rings.length; i < ii; ++i) {
    +    array.push('(' + ol.format.WKT.encodeLineStringGeometry_(
    +        rings[i]) + ')');
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiPolygon} geom MultiPolygon geometry.
    + * @return {string} Coordinates part of MultiPolygon as WKT.
    + * @private
    + */
    +ol.format.WKT.encodeMultiPolygonGeometry_ = function(geom) {
    +  var array = [];
    +  var components = geom.getPolygons();
    +  for (var i = 0, ii = components.length; i < ii; ++i) {
    +    array.push('(' + ol.format.WKT.encodePolygonGeometry_(
    +        components[i]) + ')');
    +  }
    +  return array.join(',');
    +};
    +
    +
    +/**
    + * Encode a geometry as WKT.
    + * @param {ol.geom.Geometry} geom The geometry to encode.
    + * @return {string} WKT string for the geometry.
    + * @private
    + */
    +ol.format.WKT.encode_ = function(geom) {
    +  var type = geom.getType();
    +  var geometryEncoder = ol.format.WKT.GeometryEncoder_[type];
    +  goog.asserts.assert(goog.isDef(geometryEncoder));
    +  var enc = geometryEncoder(geom);
    +  type = type.toUpperCase();
    +  if (enc.length === 0) {
    +    return type + ' ' + ol.format.WKT.EMPTY;
    +  }
    +  return type + '(' + enc + ')';
    +};
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, function(ol.geom.Geometry): string>}
    + * @private
    + */
    +ol.format.WKT.GeometryEncoder_ = {
    +  'Point': ol.format.WKT.encodePointGeometry_,
    +  'LineString': ol.format.WKT.encodeLineStringGeometry_,
    +  'Polygon': ol.format.WKT.encodePolygonGeometry_,
    +  'MultiPoint': ol.format.WKT.encodeMultiPointGeometry_,
    +  'MultiLineString': ol.format.WKT.encodeMultiLineStringGeometry_,
    +  'MultiPolygon': ol.format.WKT.encodeMultiPolygonGeometry_,
    +  'GeometryCollection': ol.format.WKT.encodeGeometryCollectionGeometry_
    +};
    +
    +
    +/**
    + * Parse a WKT string.
    + * @param {string} wkt WKT string.
    + * @return {ol.geom.Geometry|ol.geom.GeometryCollection|undefined}
    + *     The geometry created.
    + * @private
    + */
    +ol.format.WKT.prototype.parse_ = function(wkt) {
    +  var lexer = new ol.format.WKT.Lexer(wkt);
    +  var parser = new ol.format.WKT.Parser(lexer);
    +  return parser.parse();
    +};
    +
    +
    +/**
    + * Read a feature from a WKT source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.Feature} Feature.
    + * @api stable
    + */
    +ol.format.WKT.prototype.readFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.readFeatureFromText = function(text, opt_options) {
    +  var geom = this.readGeometryFromText(text, opt_options);
    +  if (goog.isDef(geom)) {
    +    var feature = new ol.Feature();
    +    feature.setGeometry(geom);
    +    return feature;
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * Read all features from a WKT source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.WKT.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.readFeaturesFromText = function(text, opt_options) {
    +  var geometries = [];
    +  var geometry = this.readGeometryFromText(text, opt_options);
    +  if (this.splitCollection_ &&
    +      geometry.getType() == ol.geom.GeometryType.GEOMETRY_COLLECTION) {
    +    geometries = (/** @type {ol.geom.GeometryCollection} */ (geometry))
    +        .getGeometriesArray();
    +  } else {
    +    geometries = [geometry];
    +  }
    +  var feature, features = [];
    +  for (var i = 0, ii = geometries.length; i < ii; ++i) {
    +    feature = new ol.Feature();
    +    feature.setGeometry(geometries[i]);
    +    features.push(feature);
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * Read a single geometry from a WKT source.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Read options.
    + * @return {ol.geom.Geometry} Geometry.
    + * @api stable
    + */
    +ol.format.WKT.prototype.readGeometry;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.readGeometryFromText = function(text, opt_options) {
    +  var geometry = this.parse_(text);
    +  if (goog.isDef(geometry)) {
    +    return /** @type {ol.geom.Geometry} */ (
    +        ol.format.Feature.transformWithOptions(geometry, false, opt_options));
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * Encode a feature as a WKT string.
    + *
    + * @function
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} WKT string.
    + * @api stable
    + */
    +ol.format.WKT.prototype.writeFeature;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.writeFeatureText = function(feature, opt_options) {
    +  var geometry = feature.getGeometry();
    +  if (goog.isDef(geometry)) {
    +    return this.writeGeometryText(geometry, opt_options);
    +  }
    +  return '';
    +};
    +
    +
    +/**
    + * Encode an array of features as a WKT string.
    + *
    + * @function
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Write options.
    + * @return {string} WKT string.
    + * @api stable
    + */
    +ol.format.WKT.prototype.writeFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.writeFeaturesText = function(features, opt_options) {
    +  if (features.length == 1) {
    +    return this.writeFeatureText(features[0], opt_options);
    +  }
    +  var geometries = [];
    +  for (var i = 0, ii = features.length; i < ii; ++i) {
    +    geometries.push(features[i].getGeometry());
    +  }
    +  var collection = new ol.geom.GeometryCollection(geometries);
    +  return this.writeGeometryText(collection, opt_options);
    +};
    +
    +
    +/**
    + * Write a single geometry as a WKT string.
    + *
    + * @function
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @return {string} WKT string.
    + * @api stable
    + */
    +ol.format.WKT.prototype.writeGeometry;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WKT.prototype.writeGeometryText = function(geometry, opt_options) {
    +  return ol.format.WKT.encode_(/** @type {ol.geom.Geometry} */ (
    +      ol.format.Feature.transformWithOptions(geometry, true, opt_options)));
    +};
    +
    +
    +/**
    + * @typedef {{type: number, value: (number|string|undefined), position: number}}
    + */
    +ol.format.WKT.Token;
    +
    +
    +/**
    + * @const
    + * @enum {number}
    + */
    +ol.format.WKT.TokenType = {
    +  TEXT: 1,
    +  LEFT_PAREN: 2,
    +  RIGHT_PAREN: 3,
    +  NUMBER: 4,
    +  COMMA: 5,
    +  EOF: 6
    +};
    +
    +
    +
    +/**
    + * Class to tokenize a WKT string.
    + * @param {string} wkt WKT string.
    + * @constructor
    + * @protected
    + */
    +ol.format.WKT.Lexer = function(wkt) {
    +
    +  /**
    +   * @type {string}
    +   */
    +  this.wkt = wkt;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.index_ = -1;
    +};
    +
    +
    +/**
    + * @param {string} c Character.
    + * @return {boolean} Whether the character is alphabetic.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.isAlpha_ = function(c) {
    +  return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
    +};
    +
    +
    +/**
    + * @param {string} c Character.
    + * @param {boolean=} opt_decimal Whether the string number
    + *     contains a dot, i.e. is a decimal number.
    + * @return {boolean} Whether the character is numeric.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.isNumeric_ = function(c, opt_decimal) {
    +  var decimal = goog.isDef(opt_decimal) ? opt_decimal : false;
    +  return c >= '0' && c <= '9' || c == '.' && !decimal;
    +};
    +
    +
    +/**
    + * @param {string} c Character.
    + * @return {boolean} Whether the character is whitespace.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.isWhiteSpace_ = function(c) {
    +  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
    +};
    +
    +
    +/**
    + * @return {string} Next string character.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.nextChar_ = function() {
    +  return this.wkt.charAt(++this.index_);
    +};
    +
    +
    +/**
    + * Fetch and return the next token.
    + * @return {!ol.format.WKT.Token} Next string token.
    + */
    +ol.format.WKT.Lexer.prototype.nextToken = function() {
    +  var c = this.nextChar_();
    +  var token = {position: this.index_, value: c};
    +
    +  if (c == '(') {
    +    token.type = ol.format.WKT.TokenType.LEFT_PAREN;
    +  } else if (c == ',') {
    +    token.type = ol.format.WKT.TokenType.COMMA;
    +  } else if (c == ')') {
    +    token.type = ol.format.WKT.TokenType.RIGHT_PAREN;
    +  } else if (this.isNumeric_(c) || c == '-') {
    +    token.type = ol.format.WKT.TokenType.NUMBER;
    +    token.value = this.readNumber_();
    +  } else if (this.isAlpha_(c)) {
    +    token.type = ol.format.WKT.TokenType.TEXT;
    +    token.value = this.readText_();
    +  } else if (this.isWhiteSpace_(c)) {
    +    return this.nextToken();
    +  } else if (c === '') {
    +    token.type = ol.format.WKT.TokenType.EOF;
    +  } else {
    +    throw new Error('Unexpected character: ' + c);
    +  }
    +
    +  return token;
    +};
    +
    +
    +/**
    + * @return {number} Numeric token value.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.readNumber_ = function() {
    +  var c, index = this.index_;
    +  var decimal = false;
    +  do {
    +    if (c == '.') {
    +      decimal = true;
    +    }
    +    c = this.nextChar_();
    +  } while (this.isNumeric_(c, decimal));
    +  return parseFloat(this.wkt.substring(index, this.index_--));
    +};
    +
    +
    +/**
    + * @return {string} String token value.
    + * @private
    + */
    +ol.format.WKT.Lexer.prototype.readText_ = function() {
    +  var c, index = this.index_;
    +  do {
    +    c = this.nextChar_();
    +  } while (this.isAlpha_(c));
    +  return this.wkt.substring(index, this.index_--).toUpperCase();
    +};
    +
    +
    +
    +/**
    + * Class to parse the tokens from the WKT string.
    + * @param {ol.format.WKT.Lexer} lexer
    + * @constructor
    + * @protected
    + */
    +ol.format.WKT.Parser = function(lexer) {
    +
    +  /**
    +   * @type {ol.format.WKT.Lexer}
    +   * @private
    +   */
    +  this.lexer_ = lexer;
    +
    +  /**
    +   * @type {ol.format.WKT.Token}
    +   * @private
    +   */
    +  this.token_;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.dimension_ = 2;
    +};
    +
    +
    +/**
    + * Fetch the next token form the lexer and replace the active token.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.consume_ = function() {
    +  this.token_ = this.lexer_.nextToken();
    +};
    +
    +
    +/**
    + * If the given type matches the current token, consume it.
    + * @param {ol.format.WKT.TokenType.<number>} type Token type.
    + * @return {boolean} Whether the token matches the given type.
    + */
    +ol.format.WKT.Parser.prototype.match = function(type) {
    +  var isMatch = this.token_.type == type;
    +  if (isMatch) {
    +    this.consume_();
    +  }
    +  return isMatch;
    +};
    +
    +
    +/**
    + * Try to parse the tokens provided by the lexer.
    + * @return {ol.geom.Geometry|ol.geom.GeometryCollection} The geometry.
    + */
    +ol.format.WKT.Parser.prototype.parse = function() {
    +  this.consume_();
    +  var geometry = this.parseGeometry_();
    +  goog.asserts.assert(this.token_.type == ol.format.WKT.TokenType.EOF);
    +  return geometry;
    +};
    +
    +
    +/**
    + * @return {!(ol.geom.Geometry|ol.geom.GeometryCollection)} The geometry.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseGeometry_ = function() {
    +  var token = this.token_;
    +  if (this.match(ol.format.WKT.TokenType.TEXT)) {
    +    var geomType = token.value;
    +    if (geomType == ol.geom.GeometryType.GEOMETRY_COLLECTION.toUpperCase()) {
    +      var geometries = this.parseGeometryCollectionText_();
    +      return new ol.geom.GeometryCollection(geometries);
    +    } else {
    +      var parser = ol.format.WKT.Parser.GeometryParser_[geomType];
    +      var ctor = ol.format.WKT.Parser.GeometryConstructor_[geomType];
    +      if (!goog.isDef(parser) || !goog.isDef(ctor)) {
    +        throw new Error('Invalid geometry type: ' + geomType);
    +      }
    +      var coordinates = parser.call(this);
    +      return new ctor(coordinates);
    +    }
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<ol.geom.Geometry>} A collection of geometries.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseGeometryCollectionText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var geometries = [];
    +    do {
    +      geometries.push(this.parseGeometry_());
    +    } while (this.match(ol.format.WKT.TokenType.COMMA));
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return geometries;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {Array.<number>} All values in a point.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePointText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates = this.parsePoint_();
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return null;
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} All points in a linestring.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseLineStringText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates = this.parsePointList_();
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} All points in a polygon.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePolygonText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates = this.parseLineStringTextList_();
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} All points in a multipoint.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseMultiPointText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates;
    +    if (this.token_.type == ol.format.WKT.TokenType.LEFT_PAREN) {
    +      coordinates = this.parsePointTextList_();
    +    } else {
    +      coordinates = this.parsePointList_();
    +    }
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} All linestring points
    + *                                        in a multilinestring.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseMultiLineStringText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates = this.parseLineStringTextList_();
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} All polygon points in a multipolygon.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseMultiPolygonText_ = function() {
    +  if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) {
    +    var coordinates = this.parsePolygonTextList_();
    +    if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) {
    +      return coordinates;
    +    }
    +  } else if (this.isEmptyGeometry_()) {
    +    return [];
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<number>} A point.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePoint_ = function() {
    +  var coordinates = [];
    +  for (var i = 0; i < this.dimension_; ++i) {
    +    var token = this.token_;
    +    if (this.match(ol.format.WKT.TokenType.NUMBER)) {
    +      coordinates.push(token.value);
    +    } else {
    +      break;
    +    }
    +  }
    +  if (coordinates.length == this.dimension_) {
    +    return coordinates;
    +  }
    +  throw new Error(this.formatErrorMessage_());
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} An array of points.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePointList_ = function() {
    +  var coordinates = [this.parsePoint_()];
    +  while (this.match(ol.format.WKT.TokenType.COMMA)) {
    +    coordinates.push(this.parsePoint_());
    +  }
    +  return coordinates;
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} An array of points.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePointTextList_ = function() {
    +  var coordinates = [this.parsePointText_()];
    +  while (this.match(ol.format.WKT.TokenType.COMMA)) {
    +    coordinates.push(this.parsePointText_());
    +  }
    +  return coordinates;
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} An array of points.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parseLineStringTextList_ = function() {
    +  var coordinates = [this.parseLineStringText_()];
    +  while (this.match(ol.format.WKT.TokenType.COMMA)) {
    +    coordinates.push(this.parseLineStringText_());
    +  }
    +  return coordinates;
    +};
    +
    +
    +/**
    + * @return {!Array.<!Array.<number>>} An array of points.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.parsePolygonTextList_ = function() {
    +  var coordinates = [this.parsePolygonText_()];
    +  while (this.match(ol.format.WKT.TokenType.COMMA)) {
    +    coordinates.push(this.parsePolygonText_());
    +  }
    +  return coordinates;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the token implies an empty geometry.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.isEmptyGeometry_ = function() {
    +  var isEmpty = this.token_.type == ol.format.WKT.TokenType.TEXT &&
    +      this.token_.value == ol.format.WKT.EMPTY;
    +  if (isEmpty) {
    +    this.consume_();
    +  }
    +  return isEmpty;
    +};
    +
    +
    +/**
    + * Create an error message for an unexpected token error.
    + * @return {string} Error message.
    + * @private
    + */
    +ol.format.WKT.Parser.prototype.formatErrorMessage_ = function() {
    +  return 'Unexpected `' + this.token_.value + '` at position ' +
    +      this.token_.position + ' in `' + this.lexer_.wkt + '`';
    +};
    +
    +
    +/**
    + * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout.<string>=)}
    + * @private
    + */
    +ol.format.WKT.Parser.GeometryConstructor_ = {
    +  'POINT': ol.geom.Point,
    +  'LINESTRING': ol.geom.LineString,
    +  'POLYGON': ol.geom.Polygon,
    +  'MULTIPOINT': ol.geom.MultiPoint,
    +  'MULTILINESTRING': ol.geom.MultiLineString,
    +  'MULTIPOLYGON': ol.geom.MultiPolygon
    +};
    +
    +
    +/**
    + * @enum {(function(): Array)}
    + * @private
    + */
    +ol.format.WKT.Parser.GeometryParser_ = {
    +  'POINT': ol.format.WKT.Parser.prototype.parsePointText_,
    +  'LINESTRING': ol.format.WKT.Parser.prototype.parseLineStringText_,
    +  'POLYGON': ol.format.WKT.Parser.prototype.parsePolygonText_,
    +  'MULTIPOINT': ol.format.WKT.Parser.prototype.parseMultiPointText_,
    +  'MULTILINESTRING': ol.format.WKT.Parser.prototype.parseMultiLineStringText_,
    +  'MULTIPOLYGON': ol.format.WKT.Parser.prototype.parseMultiPolygonText_
    +};
    diff --git a/mapbox-gl/ol/ol/format/wmscapabilitiesformat.js b/mapbox-gl/ol/ol/format/wmscapabilitiesformat.js
    new file mode 100644
    index 0000000000..b5062b320c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/wmscapabilitiesformat.js
    @@ -0,0 +1,832 @@
    +goog.provide('ol.format.WMSCapabilities');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.format.XLink');
    +goog.require('ol.format.XML');
    +goog.require('ol.format.XSD');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Format for reading WMS capabilities data
    + *
    + * @constructor
    + * @extends {ol.format.XML}
    + * @api
    + */
    +ol.format.WMSCapabilities = function() {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {string|undefined}
    +   */
    +  this.version = undefined;
    +};
    +goog.inherits(ol.format.WMSCapabilities, ol.format.XML);
    +
    +
    +/**
    + * Read a WMS capabilities document.
    + *
    + * @function
    + * @param {Document|Node|string} source The XML source.
    + * @return {Object} An object representing the WMS capabilities.
    + * @api
    + */
    +ol.format.WMSCapabilities.prototype.read;
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {Object} WMS Capability object.
    + */
    +ol.format.WMSCapabilities.prototype.readFromDocument = function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readFromNode(n);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {Object} WMS Capability object.
    + */
    +ol.format.WMSCapabilities.prototype.readFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'WMS_Capabilities' ||
    +      node.localName == 'WMT_MS_Capabilities');
    +  this.version = goog.string.trim(node.getAttribute('version'));
    +  goog.asserts.assertString(this.version);
    +  var wmsCapabilityObject = ol.xml.pushParseAndPop({
    +    'version': this.version
    +  }, ol.format.WMSCapabilities.PARSERS_, node, []);
    +  return goog.isDef(wmsCapabilityObject) ? wmsCapabilityObject : null;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Attribution object.
    + */
    +ol.format.WMSCapabilities.readAttribution_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Attribution');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object} Bounding box object.
    + */
    +ol.format.WMSCapabilities.readBoundingBox_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'BoundingBox');
    +
    +  var extent = [
    +    ol.format.XSD.readDecimalString(node.getAttribute('minx')),
    +    ol.format.XSD.readDecimalString(node.getAttribute('miny')),
    +    ol.format.XSD.readDecimalString(node.getAttribute('maxx')),
    +    ol.format.XSD.readDecimalString(node.getAttribute('maxy'))
    +  ];
    +
    +  var resolutions = [
    +    ol.format.XSD.readDecimalString(node.getAttribute('resx')),
    +    ol.format.XSD.readDecimalString(node.getAttribute('resy'))
    +  ];
    +
    +  return {
    +    'crs': node.getAttribute('CRS'),
    +    'extent': extent,
    +    'res': resolutions
    +  };
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {ol.Extent|undefined} Bounding box object.
    + */
    +ol.format.WMSCapabilities.readEXGeographicBoundingBox_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'EX_GeographicBoundingBox');
    +  var geographicBoundingBox = ol.xml.pushParseAndPop(
    +      {},
    +      ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_,
    +      node, objectStack);
    +  if (!goog.isDef(geographicBoundingBox)) {
    +    return undefined;
    +  }
    +  var westBoundLongitude = /** @type {number|undefined} */
    +      (geographicBoundingBox['westBoundLongitude']);
    +  var southBoundLatitude = /** @type {number|undefined} */
    +      (geographicBoundingBox['southBoundLatitude']);
    +  var eastBoundLongitude = /** @type {number|undefined} */
    +      (geographicBoundingBox['eastBoundLongitude']);
    +  var northBoundLatitude = /** @type {number|undefined} */
    +      (geographicBoundingBox['northBoundLatitude']);
    +  if (!goog.isDef(westBoundLongitude) || !goog.isDef(southBoundLatitude) ||
    +      !goog.isDef(eastBoundLongitude) || !goog.isDef(northBoundLatitude)) {
    +    return undefined;
    +  }
    +  return [
    +    westBoundLongitude, southBoundLatitude,
    +    eastBoundLongitude, northBoundLatitude
    +  ];
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Capability object.
    + */
    +ol.format.WMSCapabilities.readCapability_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Capability');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.CAPABILITY_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Service object.
    + */
    +ol.format.WMSCapabilities.readService_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Service');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.SERVICE_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Contact information object.
    + */
    +ol.format.WMSCapabilities.readContactInformation_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ContactInformation');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_,
    +      node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Contact person object.
    + */
    +ol.format.WMSCapabilities.readContactPersonPrimary_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ContactPersonPrimary');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_,
    +      node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Contact address object.
    + */
    +ol.format.WMSCapabilities.readContactAddress_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'ContactAddress');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_,
    +      node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Array.<string>|undefined} Format array.
    + */
    +ol.format.WMSCapabilities.readException_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Exception');
    +  return ol.xml.pushParseAndPop(
    +      [], ol.format.WMSCapabilities.EXCEPTION_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @private
    + * @return {Object|undefined} Layer object.
    + */
    +ol.format.WMSCapabilities.readCapabilityLayer_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Layer');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Layer object.
    + */
    +ol.format.WMSCapabilities.readLayer_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Layer');
    +  var parentLayerObject = /**  @type {Object.<string,*>} */
    +      (objectStack[objectStack.length - 1]);
    +
    +  var layerObject = /**  @type {Object.<string,*>} */ (ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack));
    +
    +  if (!goog.isDef(layerObject)) {
    +    return undefined;
    +  }
    +  var queryable =
    +      ol.format.XSD.readBooleanString(node.getAttribute('queryable'));
    +  if (!goog.isDef(queryable)) {
    +    queryable = parentLayerObject['queryable'];
    +  }
    +  layerObject['queryable'] = goog.isDef(queryable) ? queryable : false;
    +
    +  var cascaded = ol.format.XSD.readNonNegativeIntegerString(
    +      node.getAttribute('cascaded'));
    +  if (!goog.isDef(cascaded)) {
    +    cascaded = parentLayerObject['cascaded'];
    +  }
    +  layerObject['cascaded'] = cascaded;
    +
    +  var opaque = ol.format.XSD.readBooleanString(node.getAttribute('opaque'));
    +  if (!goog.isDef(opaque)) {
    +    opaque = parentLayerObject['opaque'];
    +  }
    +  layerObject['opaque'] = goog.isDef(opaque) ? opaque : false;
    +
    +  var noSubsets =
    +      ol.format.XSD.readBooleanString(node.getAttribute('noSubsets'));
    +  if (!goog.isDef(noSubsets)) {
    +    noSubsets = parentLayerObject['noSubsets'];
    +  }
    +  layerObject['noSubsets'] = goog.isDef(noSubsets) ? noSubsets : false;
    +
    +  var fixedWidth =
    +      ol.format.XSD.readDecimalString(node.getAttribute('fixedWidth'));
    +  if (!goog.isDef(fixedWidth)) {
    +    fixedWidth = parentLayerObject['fixedWidth'];
    +  }
    +  layerObject['fixedWidth'] = fixedWidth;
    +
    +  var fixedHeight =
    +      ol.format.XSD.readDecimalString(node.getAttribute('fixedHeight'));
    +  if (!goog.isDef(fixedHeight)) {
    +    fixedHeight = parentLayerObject['fixedHeight'];
    +  }
    +  layerObject['fixedHeight'] = fixedHeight;
    +
    +  // See 7.2.4.8
    +  var addKeys = ['Style', 'CRS', 'AuthorityURL'];
    +  goog.array.forEach(addKeys, function(key) {
    +    var parentValue = parentLayerObject[key];
    +    if (goog.isDef(parentValue)) {
    +      var childValue = goog.object.setIfUndefined(layerObject, key, []);
    +      childValue = childValue.concat(parentValue);
    +      layerObject[key] = childValue;
    +    }
    +  });
    +
    +  var replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension',
    +    'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator'];
    +  goog.array.forEach(replaceKeys, function(key) {
    +    var childValue = layerObject[key];
    +    if (!goog.isDef(childValue)) {
    +      var parentValue = parentLayerObject[key];
    +      layerObject[key] = parentValue;
    +    }
    +  });
    +
    +  return layerObject;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object} Dimension object.
    + */
    +ol.format.WMSCapabilities.readDimension_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Dimension');
    +  var dimensionObject = {
    +    'name': node.getAttribute('name'),
    +    'units': node.getAttribute('units'),
    +    'unitSymbol': node.getAttribute('unitSymbol'),
    +    'default': node.getAttribute('default'),
    +    'multipleValues': ol.format.XSD.readBooleanString(
    +        node.getAttribute('multipleValues')),
    +    'nearestValue': ol.format.XSD.readBooleanString(
    +        node.getAttribute('nearestValue')),
    +    'current': ol.format.XSD.readBooleanString(node.getAttribute('current')),
    +    'values': ol.format.XSD.readString(node)
    +  };
    +  return dimensionObject;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Online resource object.
    + */
    +ol.format.WMSCapabilities.readFormatOnlineresource_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_,
    +      node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Request object.
    + */
    +ol.format.WMSCapabilities.readRequest_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Request');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.REQUEST_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} DCP type object.
    + */
    +ol.format.WMSCapabilities.readDCPType_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'DCPType');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.DCPTYPE_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} HTTP object.
    + */
    +ol.format.WMSCapabilities.readHTTP_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'HTTP');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.HTTP_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Operation type object.
    + */
    +ol.format.WMSCapabilities.readOperationType_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Online resource object.
    + */
    +ol.format.WMSCapabilities.readSizedFormatOnlineresource_ =
    +    function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var formatOnlineresource =
    +      ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
    +  if (goog.isDef(formatOnlineresource)) {
    +    var size = [
    +      ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('width')),
    +      ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('height'))
    +    ];
    +    formatOnlineresource['size'] = size;
    +    return formatOnlineresource;
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Authority URL object.
    + */
    +ol.format.WMSCapabilities.readAuthorityURL_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'AuthorityURL');
    +  var authorityObject =
    +      ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
    +  if (goog.isDef(authorityObject)) {
    +    authorityObject['name'] = node.getAttribute('name');
    +    return authorityObject;
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Metadata URL object.
    + */
    +ol.format.WMSCapabilities.readMetadataURL_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'MetadataURL');
    +  var metadataObject =
    +      ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack);
    +  if (goog.isDef(metadataObject)) {
    +    metadataObject['type'] = node.getAttribute('type');
    +    return metadataObject;
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Style object.
    + */
    +ol.format.WMSCapabilities.readStyle_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Style');
    +  return ol.xml.pushParseAndPop(
    +      {}, ol.format.WMSCapabilities.STYLE_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Array.<string>|undefined} Keyword list.
    + */
    +ol.format.WMSCapabilities.readKeywordList_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'KeywordList');
    +  return ol.xml.pushParseAndPop(
    +      [], ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.format.WMSCapabilities.NAMESPACE_URIS_ = [
    +  null,
    +  'http://www.opengis.net/wms'
    +];
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Service': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readService_),
    +      'Capability': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readCapability_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.CAPABILITY_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Request': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readRequest_),
    +      'Exception': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readException_),
    +      'Layer': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readCapabilityLayer_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.SERVICE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'KeywordList': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readKeywordList_),
    +      'OnlineResource': ol.xml.makeObjectPropertySetter(
    +          ol.format.XLink.readHref),
    +      'ContactInformation': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readContactInformation_),
    +      'Fees': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'AccessConstraints': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'LayerLimit': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'MaxWidth': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'MaxHeight': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'ContactPersonPrimary': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readContactPersonPrimary_),
    +      'ContactPosition': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'ContactAddress': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readContactAddress_),
    +      'ContactVoiceTelephone': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'ContactFacsimileTelephone': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'ContactElectronicMailAddress': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'ContactPerson': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'ContactOrganization': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'AddressType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'StateOrProvince': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'PostCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.EXCEPTION_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Format': ol.xml.makeArrayPusher(ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.LAYER_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'KeywordList': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readKeywordList_),
    +      'CRS': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
    +      'EX_GeographicBoundingBox': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readEXGeographicBoundingBox_),
    +      'BoundingBox': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readBoundingBox_),
    +      'Dimension': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readDimension_),
    +      'Attribution': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readAttribution_),
    +      'AuthorityURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readAuthorityURL_),
    +      'Identifier': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
    +      'MetadataURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readMetadataURL_),
    +      'DataURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_),
    +      'FeatureListURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_),
    +      'Style': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readStyle_),
    +      'MinScaleDenominator': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'MaxScaleDenominator': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'Layer': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readLayer_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'OnlineResource': ol.xml.makeObjectPropertySetter(
    +          ol.format.XLink.readHref),
    +      'LogoURL': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readSizedFormatOnlineresource_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_ =
    +    ol.xml.makeParsersNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'westBoundLongitude': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'eastBoundLongitude': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'southBoundLatitude': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'northBoundLatitude': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.REQUEST_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'GetCapabilities': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readOperationType_),
    +      'GetMap': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readOperationType_),
    +      'GetFeatureInfo': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readOperationType_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Format': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString),
    +      'DCPType': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readDCPType_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.DCPTYPE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'HTTP': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readHTTP_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.HTTP_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Get': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_),
    +      'Post': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'LegendURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMSCapabilities.readSizedFormatOnlineresource_),
    +      'StyleSheetURL': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_),
    +      'StyleURL': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMSCapabilities.readFormatOnlineresource_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Format': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
    +      'OnlineResource': ol.xml.makeObjectPropertySetter(
    +          ol.format.XLink.readHref)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMSCapabilities.NAMESPACE_URIS_, {
    +      'Keyword': ol.xml.makeArrayPusher(ol.format.XSD.readString)
    +    });
    diff --git a/mapbox-gl/ol/ol/format/wmsgetfeatureinfoformat.js b/mapbox-gl/ol/ol/format/wmsgetfeatureinfoformat.js
    new file mode 100644
    index 0000000000..e0e51fe4ab
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/wmsgetfeatureinfoformat.js
    @@ -0,0 +1,145 @@
    +goog.provide('ol.format.WMSGetFeatureInfo');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.format.GML');
    +goog.require('ol.format.GML2');
    +goog.require('ol.format.XMLFeature');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Format for reading WMSGetFeatureInfo format. It uses
    + * {@link ol.format.GML2} to read features.
    + *
    + * @constructor
    + * @extends {ol.format.XMLFeature}
    + * @api
    + */
    +ol.format.WMSGetFeatureInfo = function() {
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver';
    +
    +
    +  /**
    +   * @private
    +   * @type {ol.format.GML2}
    +   */
    +  this.gmlFormat_ = new ol.format.GML2();
    +
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.WMSGetFeatureInfo, ol.format.XMLFeature);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.WMSGetFeatureInfo.featureIdentifier_ = '_feature';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + * @private
    + */
    +ol.format.WMSGetFeatureInfo.layerIdentifier_ = '_layer';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Array.<ol.Feature>} Features.
    + * @private
    + */
    +ol.format.WMSGetFeatureInfo.prototype.readFeatures_ =
    +    function(node, objectStack) {
    +
    +  node.namespaceURI = this.featureNS_;
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  var localName = ol.xml.getLocalName(node);
    +  /** @type {Array.<ol.Feature>} */
    +  var features = [];
    +  if (node.childNodes.length === 0) {
    +    return features;
    +  }
    +  if (localName == 'msGMLOutput') {
    +    goog.array.forEach(node.childNodes, function(layer) {
    +      if (layer.nodeType !== goog.dom.NodeType.ELEMENT) {
    +        return;
    +      }
    +      var context = objectStack[0];
    +      goog.asserts.assert(goog.isObject(context));
    +
    +      goog.asserts.assert(layer.localName.indexOf(
    +          ol.format.WMSGetFeatureInfo.layerIdentifier_) >= 0);
    +
    +      var featureType = goog.string.remove(layer.localName,
    +          ol.format.WMSGetFeatureInfo.layerIdentifier_) +
    +          ol.format.WMSGetFeatureInfo.featureIdentifier_;
    +
    +      context['featureType'] = featureType;
    +      context['featureNS'] = this.featureNS_;
    +
    +      var parsers = {};
    +      parsers[featureType] = ol.xml.makeArrayPusher(
    +          this.gmlFormat_.readFeatureElement, this.gmlFormat_);
    +      var parsersNS = ol.xml.makeParsersNS(
    +          [context['featureNS'], null], parsers);
    +      layer.namespaceURI = this.featureNS_;
    +      var layerFeatures = ol.xml.pushParseAndPop(
    +          [], parsersNS, layer, objectStack, this.gmlFormat_);
    +      if (goog.isDef(layerFeatures)) {
    +        goog.array.extend(features, layerFeatures);
    +      }
    +    }, this);
    +  }
    +  if (localName == 'FeatureCollection') {
    +    var gmlFeatures = ol.xml.pushParseAndPop([],
    +        this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node,
    +        [{}], this.gmlFormat_);
    +    if (goog.isDef(gmlFeatures)) {
    +      features = gmlFeatures;
    +    }
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * Read all features from a WMSGetFeatureInfo response.
    + *
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.format.WMSGetFeatureInfo.prototype.readFeatures;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.WMSGetFeatureInfo.prototype.readFeaturesFromNode =
    +    function(node, opt_options) {
    +  var options = {
    +    'featureType': this.featureType,
    +    'featureNS': this.featureNS
    +  };
    +  if (goog.isDef(opt_options)) {
    +    goog.object.extend(options, this.getReadOptions(node, opt_options));
    +  }
    +  return this.readFeatures_(node, [options]);
    +};
    diff --git a/mapbox-gl/ol/ol/format/wmtscapabilitiesformat.js b/mapbox-gl/ol/ol/format/wmtscapabilitiesformat.js
    new file mode 100644
    index 0000000000..ccaf9d5c99
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/wmtscapabilitiesformat.js
    @@ -0,0 +1,400 @@
    +goog.provide('ol.format.WMTSCapabilities');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.extent');
    +goog.require('ol.format.OWS');
    +goog.require('ol.format.XLink');
    +goog.require('ol.format.XML');
    +goog.require('ol.format.XSD');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Format for reading WMTS capabilities data.
    + *
    + * @constructor
    + * @extends {ol.format.XML}
    + * @api
    + */
    +ol.format.WMTSCapabilities = function() {
    +  goog.base(this);
    +
    +  /**
    +   * @type {ol.format.OWS}
    +   * @private
    +   */
    +  this.owsParser_ = new ol.format.OWS();
    +};
    +goog.inherits(ol.format.WMTSCapabilities, ol.format.XML);
    +
    +
    +/**
    + * Read a WMTS capabilities document.
    + *
    + * @function
    + * @param {Document|Node|string} source The XML source.
    + * @return {Object} An object representing the WMTS capabilities.
    + * @api
    + */
    +ol.format.WMTSCapabilities.prototype.read;
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {Object} WMTS Capability object.
    + */
    +ol.format.WMTSCapabilities.prototype.readFromDocument = function(doc) {
    +  goog.asserts.assert(doc.nodeType == goog.dom.NodeType.DOCUMENT);
    +  for (var n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      return this.readFromNode(n);
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {Object} WMTS Capability object.
    + */
    +ol.format.WMTSCapabilities.prototype.readFromNode = function(node) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Capabilities');
    +  this.version = goog.string.trim(node.getAttribute('version'));
    +  goog.asserts.assertString(this.version);
    +  var WMTSCapabilityObject = this.owsParser_.readFromNode(node);
    +  if (!goog.isDef(WMTSCapabilityObject)) {
    +    return null;
    +  }
    +  goog.object.set(WMTSCapabilityObject, 'version', this.version);
    +  WMTSCapabilityObject = ol.xml.pushParseAndPop(WMTSCapabilityObject,
    +      ol.format.WMTSCapabilities.PARSERS_, node, []);
    +  return goog.isDef(WMTSCapabilityObject) ? WMTSCapabilityObject : null;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Attribution object.
    + */
    +ol.format.WMTSCapabilities.readContents_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Contents');
    +
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.CONTENTS_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Layers object.
    + */
    +ol.format.WMTSCapabilities.readLayer_ = function(node, objectStack) {
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  goog.asserts.assert(node.localName == 'Layer');
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.LAYER_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Tile Matrix Set object.
    + */
    +ol.format.WMTSCapabilities.readTileMatrixSet_ = function(node, objectStack) {
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.TMS_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Style object.
    + */
    +ol.format.WMTSCapabilities.readStyle_ = function(node, objectStack) {
    +  var style = ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.STYLE_PARSERS_, node, objectStack);
    +  if (!goog.isDef(style)) {
    +    return undefined;
    +  }
    +  var isDefault = node.getAttribute('isDefault') === 'true';
    +  style['isDefault'] = isDefault;
    +  return style;
    +
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Tile Matrix Set Link object.
    + */
    +ol.format.WMTSCapabilities.readTileMatrixSetLink_ = function(node,
    +    objectStack) {
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Resource URL object.
    + */
    +ol.format.WMTSCapabilities.readResourceUrl_ = function(node, objectStack) {
    +  var format = node.getAttribute('format');
    +  var template = node.getAttribute('template');
    +  var resourceType = node.getAttribute('resourceType');
    +  var resource = {};
    +  if (goog.isDef(format)) {
    +    resource['format'] = format;
    +  }
    +  if (goog.isDef(template)) {
    +    resource['template'] = template;
    +  }
    +  if (goog.isDef(resourceType)) {
    +    resource['resourceType'] = resourceType;
    +  }
    +  return resource;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} WGS84 BBox object.
    + */
    +ol.format.WMTSCapabilities.readWgs84BoundingBox_ = function(node, objectStack) {
    +  var coordinates = ol.xml.pushParseAndPop([],
    +      ol.format.WMTSCapabilities.WGS84_BBOX_READERS_, node, objectStack);
    +  if (coordinates.length != 2) {
    +    return undefined;
    +  }
    +  return ol.extent.boundingExtent(coordinates);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Legend object.
    + */
    +ol.format.WMTSCapabilities.readLegendUrl_ = function(node, objectStack) {
    +  var legend = {};
    +  legend['format'] = node.getAttribute('format');
    +  legend['href'] = ol.format.XLink.readHref(node);
    +  return legend;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} Coordinates object.
    + */
    +ol.format.WMTSCapabilities.readCoordinates_ = function(node, objectStack) {
    +  var coordinates = ol.format.XSD.readString(node).split(' ');
    +  if (!goog.isDef(coordinates) || coordinates.length != 2) {
    +    return undefined;
    +  }
    +  var x = +coordinates[0];
    +  var y = +coordinates[1];
    +  if (isNaN(x) || isNaN(y)) {
    +    return undefined;
    +  }
    +  return [x, y];
    +};
    +
    +
    +/**
    + * @private
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @return {Object|undefined} TileMatrix object.
    + */
    +ol.format.WMTSCapabilities.readTileMatrix_ = function(node, objectStack) {
    +  return ol.xml.pushParseAndPop({},
    +      ol.format.WMTSCapabilities.TM_PARSERS_, node, objectStack);
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.format.WMTSCapabilities.NAMESPACE_URIS_ = [
    +  null,
    +  'http://www.opengis.net/wmts/1.0'
    +];
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_ = [
    +  null,
    +  'http://www.opengis.net/ows/1.1'
    +];
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'Contents': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMTSCapabilities.readContents_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.CONTENTS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'Layer': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readLayer_),
    +      'TileMatrixSet': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readTileMatrixSet_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.LAYER_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'Style': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readStyle_),
    +      'Format': ol.xml.makeObjectPropertyPusher(
    +          ol.format.XSD.readString),
    +      'TileMatrixSetLink': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readTileMatrixSetLink_),
    +      'ResourceURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readResourceUrl_)
    +    }, ol.xml.makeParsersNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
    +      'Title': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'Abstract': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'WGS84BoundingBox': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMTSCapabilities.readWgs84BoundingBox_),
    +      'Identifier': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.STYLE_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'LegendURL': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readLegendUrl_)
    +    }, ol.xml.makeParsersNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
    +      'Title': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'Identifier': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'TileMatrixSet': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.WGS84_BBOX_READERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
    +      'LowerCorner': ol.xml.makeArrayPusher(
    +          ol.format.WMTSCapabilities.readCoordinates_),
    +      'UpperCorner': ol.xml.makeArrayPusher(
    +          ol.format.WMTSCapabilities.readCoordinates_)
    +    });
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.TMS_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'WellKnownScaleSet': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'TileMatrix': ol.xml.makeObjectPropertyPusher(
    +          ol.format.WMTSCapabilities.readTileMatrix_)
    +    }, ol.xml.makeParsersNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
    +      'SupportedCRS': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString),
    +      'Identifier': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    }));
    +
    +
    +/**
    + * @const
    + * @type {Object.<string, Object.<string, ol.xml.Parser>>}
    + * @private
    + */
    +ol.format.WMTSCapabilities.TM_PARSERS_ = ol.xml.makeParsersNS(
    +    ol.format.WMTSCapabilities.NAMESPACE_URIS_, {
    +      'TopLeftCorner': ol.xml.makeObjectPropertySetter(
    +          ol.format.WMTSCapabilities.readCoordinates_),
    +      'ScaleDenominator': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readDecimal),
    +      'TileWidth': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'TileHeight': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'MatrixWidth': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger),
    +      'MatrixHeight': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readNonNegativeInteger)
    +    }, ol.xml.makeParsersNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, {
    +      'Identifier': ol.xml.makeObjectPropertySetter(
    +          ol.format.XSD.readString)
    +    }));
    diff --git a/mapbox-gl/ol/ol/format/xlinkformat.js b/mapbox-gl/ol/ol/format/xlinkformat.js
    new file mode 100644
    index 0000000000..a01b61ec7e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/xlinkformat.js
    @@ -0,0 +1,17 @@
    +goog.provide('ol.format.XLink');
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.XLink.NAMESPACE_URI = 'http://www.w3.org/1999/xlink';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {boolean|undefined} Boolean.
    + */
    +ol.format.XLink.readHref = function(node) {
    +  return node.getAttributeNS(ol.format.XLink.NAMESPACE_URI, 'href');
    +};
    diff --git a/mapbox-gl/ol/ol/format/xmlfeatureformat.js b/mapbox-gl/ol/ol/format/xmlfeatureformat.js
    new file mode 100644
    index 0000000000..48a08db421
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/xmlfeatureformat.js
    @@ -0,0 +1,255 @@
    +goog.provide('ol.format.XMLFeature');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.xml');
    +goog.require('ol.format.Feature');
    +goog.require('ol.format.FormatType');
    +goog.require('ol.proj');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for XML feature formats.
    + *
    + * @constructor
    + * @extends {ol.format.Feature}
    + */
    +ol.format.XMLFeature = function() {
    +  goog.base(this);
    +};
    +goog.inherits(ol.format.XMLFeature, ol.format.Feature);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.getType = function() {
    +  return ol.format.FormatType.XML;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.readFeature = function(source, opt_options) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readFeatureFromDocument(
    +        /** @type {Document} */ (source), opt_options);
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readFeatureFromDocument(doc, opt_options);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.XMLFeature.prototype.readFeatureFromDocument = function(
    +    doc, opt_options) {
    +  var features = this.readFeaturesFromDocument(doc, opt_options);
    +  if (features.length > 0) {
    +    return features[0];
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @return {ol.Feature} Feature.
    + */
    +ol.format.XMLFeature.prototype.readFeatureFromNode = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.readFeatures = function(source, opt_options) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readFeaturesFromDocument(
    +        /** @type {Document} */ (source), opt_options);
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readFeaturesFromDocument(doc, opt_options);
    +  } else {
    +    goog.asserts.fail();
    +    return [];
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @protected
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.XMLFeature.prototype.readFeaturesFromDocument = function(
    +    doc, opt_options) {
    +  /** @type {Array.<ol.Feature>} */
    +  var features = [];
    +  var n;
    +  for (n = doc.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +    if (n.nodeType == goog.dom.NodeType.ELEMENT) {
    +      goog.array.extend(features, this.readFeaturesFromNode(n, opt_options));
    +    }
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @protected
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.format.XMLFeature.prototype.readFeaturesFromNode = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.readGeometry = function(source, opt_options) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readGeometryFromDocument(
    +        /** @type {Document} */ (source), opt_options);
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readGeometryFromDocument(doc, opt_options);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @protected
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.XMLFeature.prototype.readGeometryFromDocument = goog.abstractMethod;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {olx.format.ReadOptions=} opt_options Options.
    + * @protected
    + * @return {ol.geom.Geometry} Geometry.
    + */
    +ol.format.XMLFeature.prototype.readGeometryFromNode = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.readProjection = function(source) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readProjectionFromDocument(/** @type {Document} */ (source));
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readProjectionFromNode(/** @type {Node} */ (source));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readProjectionFromDocument(doc);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @protected
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.XMLFeature.prototype.readProjectionFromDocument = function(doc) {
    +  return this.defaultDataProjection;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @protected
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.format.XMLFeature.prototype.readProjectionFromNode = function(node) {
    +  return this.defaultDataProjection;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.writeFeature = function(feature, opt_options) {
    +  var node = this.writeFeatureNode(feature, opt_options);
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return goog.dom.xml.serialize(/** @type {Element} */(node));
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @protected
    + * @return {Node} Node.
    + */
    +ol.format.XMLFeature.prototype.writeFeatureNode = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.writeFeatures = function(features, opt_options) {
    +  var node = this.writeFeaturesNode(features, opt_options);
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return goog.dom.xml.serialize(/** @type {Element} */(node));
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features Features.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + */
    +ol.format.XMLFeature.prototype.writeFeaturesNode = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) {
    +  var node = this.writeGeometryNode(geometry, opt_options);
    +  goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
    +  return goog.dom.xml.serialize(/** @type {Element} */(node));
    +};
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {olx.format.WriteOptions=} opt_options Options.
    + * @return {Node} Node.
    + */
    +ol.format.XMLFeature.prototype.writeGeometryNode = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/format/xmlformat.js b/mapbox-gl/ol/ol/format/xmlformat.js
    new file mode 100644
    index 0000000000..11d46fbe6a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/xmlformat.js
    @@ -0,0 +1,48 @@
    +goog.provide('ol.format.XML');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Generic format for reading non-feature XML data
    + *
    + * @constructor
    + */
    +ol.format.XML = function() {
    +};
    +
    +
    +/**
    + * @param {Document|Node|string} source Source.
    + * @return {Object}
    + */
    +ol.format.XML.prototype.read = function(source) {
    +  if (ol.xml.isDocument(source)) {
    +    return this.readFromDocument(/** @type {Document} */ (source));
    +  } else if (ol.xml.isNode(source)) {
    +    return this.readFromNode(/** @type {Node} */ (source));
    +  } else if (goog.isString(source)) {
    +    var doc = ol.xml.parse(source);
    +    return this.readFromDocument(doc);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {Document} doc Document.
    + * @return {Object}
    + */
    +ol.format.XML.prototype.readFromDocument = goog.abstractMethod;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {Object}
    + */
    +ol.format.XML.prototype.readFromNode = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/format/xsdformat.js b/mapbox-gl/ol/ol/format/xsdformat.js
    new file mode 100644
    index 0000000000..5198859f93
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/format/xsdformat.js
    @@ -0,0 +1,183 @@
    +goog.provide('ol.format.XSD');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.string');
    +goog.require('ol.xml');
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.format.XSD.NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema';
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {boolean|undefined} Boolean.
    + */
    +ol.format.XSD.readBoolean = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  return ol.format.XSD.readBooleanString(s);
    +};
    +
    +
    +/**
    + * @param {string} string String.
    + * @return {boolean|undefined} Boolean.
    + */
    +ol.format.XSD.readBooleanString = function(string) {
    +  var m = /^\s*(true|1)|(false|0)\s*$/.exec(string);
    +  if (m) {
    +    return goog.isDef(m[1]) || false;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {number|undefined} DateTime in seconds.
    + */
    +ol.format.XSD.readDateTime = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  var re =
    +      /^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?))\s*$/;
    +  var m = re.exec(s);
    +  if (m) {
    +    var year = parseInt(m[1], 10);
    +    var month = parseInt(m[2], 10) - 1;
    +    var day = parseInt(m[3], 10);
    +    var hour = parseInt(m[4], 10);
    +    var minute = parseInt(m[5], 10);
    +    var second = parseInt(m[6], 10);
    +    var dateTime = Date.UTC(year, month, day, hour, minute, second) / 1000;
    +    if (m[7] != 'Z') {
    +      var sign = m[8] == '-' ? -1 : 1;
    +      dateTime += sign * 60 * parseInt(m[9], 10);
    +      if (goog.isDef(m[10])) {
    +        dateTime += sign * 60 * 60 * parseInt(m[10], 10);
    +      }
    +    }
    +    return dateTime;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {number|undefined} Decimal.
    + */
    +ol.format.XSD.readDecimal = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  return ol.format.XSD.readDecimalString(s);
    +};
    +
    +
    +/**
    + * @param {string} string String.
    + * @return {number|undefined} Decimal.
    + */
    +ol.format.XSD.readDecimalString = function(string) {
    +  // FIXME check spec
    +  var m = /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(string);
    +  if (m) {
    +    return parseFloat(m[1]);
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {number|undefined} Non negative integer.
    + */
    +ol.format.XSD.readNonNegativeInteger = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  return ol.format.XSD.readNonNegativeIntegerString(s);
    +};
    +
    +
    +/**
    + * @param {string} string String.
    + * @return {number|undefined} Non negative integer.
    + */
    +ol.format.XSD.readNonNegativeIntegerString = function(string) {
    +  var m = /^\s*(\d+)\s*$/.exec(string);
    +  if (m) {
    +    return parseInt(m[1], 10);
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {string|undefined} String.
    + */
    +ol.format.XSD.readString = function(node) {
    +  var s = ol.xml.getAllTextContent(node, false);
    +  return goog.string.trim(s);
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the boolean to.
    + * @param {boolean} bool Boolean.
    + */
    +ol.format.XSD.writeBooleanTextNode = function(node, bool) {
    +  ol.format.XSD.writeStringTextNode(node, (bool) ? '1' : '0');
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the dateTime to.
    + * @param {number} dateTime DateTime in seconds.
    + */
    +ol.format.XSD.writeDateTimeTextNode = function(node, dateTime) {
    +  var date = new Date(dateTime * 1000);
    +  var string = date.getUTCFullYear() + '-' +
    +      goog.string.padNumber(date.getUTCMonth() + 1, 2) + '-' +
    +      goog.string.padNumber(date.getUTCDate(), 2) + 'T' +
    +      goog.string.padNumber(date.getUTCHours(), 2) + ':' +
    +      goog.string.padNumber(date.getUTCMinutes(), 2) + ':' +
    +      goog.string.padNumber(date.getUTCSeconds(), 2) + 'Z';
    +  node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the decimal to.
    + * @param {number} decimal Decimal.
    + */
    +ol.format.XSD.writeDecimalTextNode = function(node, decimal) {
    +  var string = decimal.toPrecision();
    +  node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the decimal to.
    + * @param {number} nonNegativeInteger Non negative integer.
    + */
    +ol.format.XSD.writeNonNegativeIntegerTextNode =
    +    function(node, nonNegativeInteger) {
    +  goog.asserts.assert(nonNegativeInteger >= 0);
    +  goog.asserts.assert(nonNegativeInteger == (nonNegativeInteger | 0));
    +  var string = nonNegativeInteger.toString();
    +  node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
    +};
    +
    +
    +/**
    + * @param {Node} node Node to append a TextNode with the string to.
    + * @param {string} string String.
    + */
    +ol.format.XSD.writeStringTextNode = function(node, string) {
    +  node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
    +};
    diff --git a/mapbox-gl/ol/ol/framestate.js b/mapbox-gl/ol/ol/framestate.js
    new file mode 100644
    index 0000000000..a1847f52f8
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/framestate.js
    @@ -0,0 +1,21 @@
    +// FIXME factor out common code between usedTiles and wantedTiles
    +
    +goog.provide('ol.PostRenderFunction');
    +goog.provide('ol.PreRenderFunction');
    +
    +
    +/**
    + * @typedef {function(ol.Map, ?olx.FrameState): boolean}
    + */
    +ol.PostRenderFunction;
    +
    +
    +/**
    + * Function to perform manipulations before rendering. This function is called
    + * with the {@link ol.Map} as first and an optional {@link olx.FrameState} as
    + * second argument. Return `true` to keep this function for the next frame,
    + * `false` to remove it.
    + * @typedef {function(ol.Map, ?olx.FrameState): boolean}
    + * @api
    + */
    +ol.PreRenderFunction;
    diff --git a/mapbox-gl/ol/ol/geolocation.js b/mapbox-gl/ol/ol/geolocation.js
    new file mode 100644
    index 0000000000..aea6daf368
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geolocation.js
    @@ -0,0 +1,406 @@
    +// FIXME handle geolocation not supported
    +
    +goog.provide('ol.Geolocation');
    +goog.provide('ol.GeolocationProperty');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math');
    +goog.require('ol.Coordinate');
    +goog.require('ol.Object');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.has');
    +goog.require('ol.proj');
    +goog.require('ol.sphere.WGS84');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.GeolocationProperty = {
    +  ACCURACY: 'accuracy',
    +  ACCURACY_GEOMETRY: 'accuracyGeometry',
    +  ALTITUDE: 'altitude',
    +  ALTITUDE_ACCURACY: 'altitudeAccuracy',
    +  HEADING: 'heading',
    +  POSITION: 'position',
    +  PROJECTION: 'projection',
    +  SPEED: 'speed',
    +  TRACKING: 'tracking',
    +  TRACKING_OPTIONS: 'trackingOptions'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Helper class for providing HTML5 Geolocation capabilities.
    + * The [Geolocation API](http://www.w3.org/TR/geolocation-API/)
    + * is used to locate a user's position.
    + *
    + * Example:
    + *
    + *     var geolocation = new ol.Geolocation({
    + *       // take the projection to use from the map's view
    + *       projection: view.getProjection()
    + *     });
    + *     // listen to changes in position
    + *     geolocation.on('change', function(evt) {
    + *       window.console.log(geolocation.getPosition());
    + *     });
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @fires change Triggered when the position changes.
    + * @param {olx.GeolocationOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.Geolocation = function(opt_options) {
    +
    +  goog.base(this);
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * The unprojected (EPSG:4326) device position.
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.position_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.TransformFunction}
    +   */
    +  this.transform_ = ol.proj.identityTransform;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.watchId_ = undefined;
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.GeolocationProperty.PROJECTION),
    +      this.handleProjectionChanged_, false, this);
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.GeolocationProperty.TRACKING),
    +      this.handleTrackingChanged_, false, this);
    +
    +  if (goog.isDef(options.projection)) {
    +    this.setProjection(ol.proj.get(options.projection));
    +  }
    +  if (goog.isDef(options.trackingOptions)) {
    +    this.setTrackingOptions(options.trackingOptions);
    +  }
    +
    +  this.setTracking(goog.isDef(options.tracking) ? options.tracking : false);
    +
    +};
    +goog.inherits(ol.Geolocation, ol.Object);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.Geolocation.prototype.disposeInternal = function() {
    +  this.setTracking(false);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Geolocation.prototype.handleProjectionChanged_ = function() {
    +  var projection = this.getProjection();
    +  if (goog.isDefAndNotNull(projection)) {
    +    this.transform_ = ol.proj.getTransformFromProjections(
    +        ol.proj.get('EPSG:4326'), projection);
    +    if (!goog.isNull(this.position_)) {
    +      this.set(
    +          ol.GeolocationProperty.POSITION, this.transform_(this.position_));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Geolocation.prototype.handleTrackingChanged_ = function() {
    +  if (ol.has.GEOLOCATION) {
    +    var tracking = this.getTracking();
    +    if (tracking && !goog.isDef(this.watchId_)) {
    +      this.watchId_ = goog.global.navigator.geolocation.watchPosition(
    +          goog.bind(this.positionChange_, this),
    +          goog.bind(this.positionError_, this),
    +          this.getTrackingOptions());
    +    } else if (!tracking && goog.isDef(this.watchId_)) {
    +      goog.global.navigator.geolocation.clearWatch(this.watchId_);
    +      this.watchId_ = undefined;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {GeolocationPosition} position position event.
    + */
    +ol.Geolocation.prototype.positionChange_ = function(position) {
    +  var coords = position.coords;
    +  this.set(ol.GeolocationProperty.ACCURACY, coords.accuracy);
    +  this.set(ol.GeolocationProperty.ALTITUDE,
    +      goog.isNull(coords.altitude) ? undefined : coords.altitude);
    +  this.set(ol.GeolocationProperty.ALTITUDE_ACCURACY,
    +      goog.isNull(coords.altitudeAccuracy) ?
    +      undefined : coords.altitudeAccuracy);
    +  this.set(ol.GeolocationProperty.HEADING, goog.isNull(coords.heading) ?
    +      undefined : goog.math.toRadians(coords.heading));
    +  if (goog.isNull(this.position_)) {
    +    this.position_ = [coords.longitude, coords.latitude];
    +  } else {
    +    this.position_[0] = coords.longitude;
    +    this.position_[1] = coords.latitude;
    +  }
    +  var projectedPosition = this.transform_(this.position_);
    +  this.set(ol.GeolocationProperty.POSITION, projectedPosition);
    +  this.set(ol.GeolocationProperty.SPEED,
    +      goog.isNull(coords.speed) ? undefined : coords.speed);
    +  var geometry = ol.geom.Polygon.circular(
    +      ol.sphere.WGS84, this.position_, coords.accuracy);
    +  geometry.applyTransform(this.transform_);
    +  this.set(ol.GeolocationProperty.ACCURACY_GEOMETRY, geometry);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + * @param {GeolocationPositionError} error error object.
    + */
    +ol.Geolocation.prototype.positionError_ = function(error) {
    +  error.type = goog.events.EventType.ERROR;
    +  this.setTracking(false);
    +  this.dispatchEvent(error);
    +};
    +
    +
    +/**
    + * Get the accuracy of the position in meters.
    + * @return {number|undefined} The accuracy of the position measurement in
    + *     meters.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getAccuracy = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.GeolocationProperty.ACCURACY));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getAccuracy',
    +    ol.Geolocation.prototype.getAccuracy);
    +
    +
    +/**
    + * Get a geometry of the position accuracy.
    + * @return {?ol.geom.Geometry} A geometry of the position accuracy.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getAccuracyGeometry = function() {
    +  return /** @type {?ol.geom.Geometry} */ (
    +      this.get(ol.GeolocationProperty.ACCURACY_GEOMETRY) || null);
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getAccuracyGeometry',
    +    ol.Geolocation.prototype.getAccuracyGeometry);
    +
    +
    +/**
    + * Get the altitude associated with the position.
    + * @return {number|undefined} The altitude of the position in meters above mean
    + *     sea level.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getAltitude = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.GeolocationProperty.ALTITUDE));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getAltitude',
    +    ol.Geolocation.prototype.getAltitude);
    +
    +
    +/**
    + * Get the altitude accuracy of the position.
    + * @return {number|undefined} The accuracy of the altitude measurement in
    + *     meters.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getAltitudeAccuracy = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.GeolocationProperty.ALTITUDE_ACCURACY));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getAltitudeAccuracy',
    +    ol.Geolocation.prototype.getAltitudeAccuracy);
    +
    +
    +/**
    + * Get the heading as radians clockwise from North.
    + * @return {number|undefined} The heading of the device in radians from north.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getHeading = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.GeolocationProperty.HEADING));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getHeading',
    +    ol.Geolocation.prototype.getHeading);
    +
    +
    +/**
    + * Get the position of the device.
    + * @return {ol.Coordinate|undefined} The current position of the device reported
    + *     in the current projection.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getPosition = function() {
    +  return /** @type {ol.Coordinate|undefined} */ (
    +      this.get(ol.GeolocationProperty.POSITION));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getPosition',
    +    ol.Geolocation.prototype.getPosition);
    +
    +
    +/**
    + * Get the projection associated with the position.
    + * @return {ol.proj.Projection|undefined} The projection the position is
    + *     reported in.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getProjection = function() {
    +  return /** @type {ol.proj.Projection|undefined} */ (
    +      this.get(ol.GeolocationProperty.PROJECTION));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getProjection',
    +    ol.Geolocation.prototype.getProjection);
    +
    +
    +/**
    + * Get the speed in meters per second.
    + * @return {number|undefined} The instantaneous speed of the device in meters
    + *     per second.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getSpeed = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.GeolocationProperty.SPEED));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getSpeed',
    +    ol.Geolocation.prototype.getSpeed);
    +
    +
    +/**
    + * Are we tracking the user's position?
    + * @return {boolean} Whether to track the device's position.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getTracking = function() {
    +  return /** @type {boolean} */ (
    +      this.get(ol.GeolocationProperty.TRACKING));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getTracking',
    +    ol.Geolocation.prototype.getTracking);
    +
    +
    +/**
    + * Get the tracking options.
    + * @see http://www.w3.org/TR/geolocation-API/#position-options
    + * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by
    + *     the HTML5 Geolocation spec at
    + *     {@link http://www.w3.org/TR/geolocation-API/#position_options_interface}
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.getTrackingOptions = function() {
    +  return /** @type {GeolocationPositionOptions|undefined} */ (
    +      this.get(ol.GeolocationProperty.TRACKING_OPTIONS));
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'getTrackingOptions',
    +    ol.Geolocation.prototype.getTrackingOptions);
    +
    +
    +/**
    + * Set the projection to use for transforming the coordinates.
    + * @param {ol.proj.Projection} projection The projection the position is
    + *     reported in.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.setProjection = function(projection) {
    +  this.set(ol.GeolocationProperty.PROJECTION, projection);
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'setProjection',
    +    ol.Geolocation.prototype.setProjection);
    +
    +
    +/**
    + * Enable/disable tracking.
    + * @param {boolean} tracking Whether to track the device's position.
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.setTracking = function(tracking) {
    +  this.set(ol.GeolocationProperty.TRACKING, tracking);
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'setTracking',
    +    ol.Geolocation.prototype.setTracking);
    +
    +
    +/**
    + * Set the tracking options.
    + * @see http://www.w3.org/TR/geolocation-API/#position-options
    + * @param {GeolocationPositionOptions} options PositionOptions as defined by the
    + *     HTML5 Geolocation spec at
    + *     {@link http://www.w3.org/TR/geolocation-API/#position_options_interface}
    + * @observable
    + * @api stable
    + */
    +ol.Geolocation.prototype.setTrackingOptions = function(options) {
    +  this.set(ol.GeolocationProperty.TRACKING_OPTIONS, options);
    +};
    +goog.exportProperty(
    +    ol.Geolocation.prototype,
    +    'setTrackingOptions',
    +    ol.Geolocation.prototype.setTrackingOptions);
    diff --git a/mapbox-gl/ol/ol/geom/circle.js b/mapbox-gl/ol/ol/geom/circle.js
    new file mode 100644
    index 0000000000..e654472d25
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/circle.js
    @@ -0,0 +1,230 @@
    +goog.provide('ol.geom.Circle');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Circle geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {ol.Coordinate} center Center.
    + * @param {number=} opt_radius Radius.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api
    + */
    +ol.geom.Circle = function(center, opt_radius, opt_layout) {
    +  goog.base(this);
    +  var radius = goog.isDef(opt_radius) ? opt_radius : 0;
    +  this.setCenterAndRadius(center, radius,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +};
    +goog.inherits(ol.geom.Circle, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.Circle} Clone.
    + * @api
    + */
    +ol.geom.Circle.prototype.clone = function() {
    +  var circle = new ol.geom.Circle(null);
    +  circle.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
    +  return circle;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Circle.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  var flatCoordinates = this.flatCoordinates;
    +  var dx = x - flatCoordinates[0];
    +  var dy = y - flatCoordinates[1];
    +  var squaredDistance = dx * dx + dy * dy;
    +  if (squaredDistance < minSquaredDistance) {
    +    var i;
    +    if (squaredDistance === 0) {
    +      for (i = 0; i < this.stride; ++i) {
    +        closestPoint[i] = flatCoordinates[i];
    +      }
    +    } else {
    +      var delta = this.getRadius() / Math.sqrt(squaredDistance);
    +      closestPoint[0] = flatCoordinates[0] + delta * dx;
    +      closestPoint[1] = flatCoordinates[1] + delta * dy;
    +      for (i = 2; i < this.stride; ++i) {
    +        closestPoint[i] = flatCoordinates[i];
    +      }
    +    }
    +    closestPoint.length = this.stride;
    +    return squaredDistance;
    +  } else {
    +    return minSquaredDistance;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Circle.prototype.containsXY = function(x, y) {
    +  var flatCoordinates = this.flatCoordinates;
    +  var dx = x - flatCoordinates[0];
    +  var dy = y - flatCoordinates[1];
    +  return dx * dx + dy * dy <= this.getRadiusSquared_();
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate} Center.
    + * @api
    + */
    +ol.geom.Circle.prototype.getCenter = function() {
    +  return this.flatCoordinates.slice(0, this.stride);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Circle.prototype.computeExtent = function(extent) {
    +  var flatCoordinates = this.flatCoordinates;
    +  var radius = flatCoordinates[this.stride] - flatCoordinates[0];
    +  return ol.extent.createOrUpdate(
    +      flatCoordinates[0] - radius, flatCoordinates[1] - radius,
    +      flatCoordinates[0] + radius, flatCoordinates[1] + radius,
    +      extent);
    +};
    +
    +
    +/**
    + * @return {number} Radius.
    + * @api
    + */
    +ol.geom.Circle.prototype.getRadius = function() {
    +  return Math.sqrt(this.getRadiusSquared_());
    +};
    +
    +
    +/**
    + * @private
    + * @return {number} Radius squared.
    + */
    +ol.geom.Circle.prototype.getRadiusSquared_ = function() {
    +  var dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0];
    +  var dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1];
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.Circle.prototype.getType = function() {
    +  return ol.geom.GeometryType.CIRCLE;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} center Center.
    + * @api
    + */
    +ol.geom.Circle.prototype.setCenter = function(center) {
    +  var stride = this.stride;
    +  goog.asserts.assert(center.length == stride);
    +  var radius = this.flatCoordinates[stride] - this.flatCoordinates[0];
    +  var flatCoordinates = center.slice();
    +  flatCoordinates[stride] = flatCoordinates[0] + radius;
    +  var i;
    +  for (i = 1; i < stride; ++i) {
    +    flatCoordinates[stride + i] = center[i];
    +  }
    +  this.setFlatCoordinates(this.layout, flatCoordinates);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} center Center.
    + * @param {number} radius Radius.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api
    + */
    +ol.geom.Circle.prototype.setCenterAndRadius =
    +    function(center, radius, opt_layout) {
    +  if (goog.isNull(center)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
    +  } else {
    +    this.setLayout(opt_layout, center, 0);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    /** @type {Array.<number>} */
    +    var flatCoordinates = this.flatCoordinates;
    +    var offset = ol.geom.flat.deflate.coordinate(
    +        flatCoordinates, 0, center, this.stride);
    +    flatCoordinates[offset++] = flatCoordinates[0] + radius;
    +    var i, ii;
    +    for (i = 1, ii = this.stride; i < ii; ++i) {
    +      flatCoordinates[offset++] = flatCoordinates[i];
    +    }
    +    flatCoordinates.length = offset;
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + */
    +ol.geom.Circle.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates) {
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.changed();
    +};
    +
    +
    +/**
    + * The radius is in the units of the projection.
    + * @param {number} radius Radius.
    + * @api
    + */
    +ol.geom.Circle.prototype.setRadius = function(radius) {
    +  goog.asserts.assert(!goog.isNull(this.flatCoordinates));
    +  this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
    +  this.changed();
    +};
    +
    +
    +/**
    + * Transform each coordinate of the circle from one coordinate reference system
    + * to another. The geometry is modified in place.
    + * If you do not want the geometry modified in place, first clone() it and
    + * then use this function on the clone.
    + *
    + * Internally a circle is currently represented by two points: the center of
    + * the circle `[cx, cy]`, and the point to the right of the circle
    + * `[cx + r, cy]`. This `transform` function just transforms these two points.
    + * So the resulting geometry is also a circle, and that circle does not
    + * correspond to the shape that would be obtained by transforming every point
    + * of the original circle.
    + *
    + * @param {ol.proj.ProjectionLike} source The current projection.  Can be a
    + *     string identifier or a {@link ol.proj.Projection} object.
    + * @param {ol.proj.ProjectionLike} destination The desired projection.  Can be a
    + *     string identifier or a {@link ol.proj.Projection} object.
    + * @return {ol.geom.Circle} This geometry.  Note that original geometry is
    + *     modified in place.
    + * @function
    + * @api stable
    + */
    +ol.geom.Circle.prototype.transform;
    diff --git a/mapbox-gl/ol/ol/geom/flat/areaflatgeom.js b/mapbox-gl/ol/ol/geom/flat/areaflatgeom.js
    new file mode 100644
    index 0000000000..eb0b1e4275
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/areaflatgeom.js
    @@ -0,0 +1,64 @@
    +goog.provide('ol.geom.flat.area');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {number} Area.
    + */
    +ol.geom.flat.area.linearRing = function(flatCoordinates, offset, end, stride) {
    +  var twiceArea = 0;
    +  var x1 = flatCoordinates[end - stride];
    +  var y1 = flatCoordinates[end - stride + 1];
    +  for (; offset < end; offset += stride) {
    +    var x2 = flatCoordinates[offset];
    +    var y2 = flatCoordinates[offset + 1];
    +    twiceArea += y1 * x2 - x1 * y2;
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return twiceArea / 2;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @return {number} Area.
    + */
    +ol.geom.flat.area.linearRings =
    +    function(flatCoordinates, offset, ends, stride) {
    +  var area = 0;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    area += ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride);
    +    offset = end;
    +  }
    +  return area;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @return {number} Area.
    + */
    +ol.geom.flat.area.linearRingss =
    +    function(flatCoordinates, offset, endss, stride) {
    +  var area = 0;
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    area +=
    +        ol.geom.flat.area.linearRings(flatCoordinates, offset, ends, stride);
    +    offset = ends[ends.length - 1];
    +  }
    +  return area;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/centerflatgeom.js b/mapbox-gl/ol/ol/geom/flat/centerflatgeom.js
    new file mode 100644
    index 0000000000..81f24369ff
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/centerflatgeom.js
    @@ -0,0 +1,26 @@
    +goog.provide('ol.geom.flat.center');
    +
    +goog.require('ol.extent');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @return {Array.<number>} Flat centers.
    + */
    +ol.geom.flat.center.linearRingss =
    +    function(flatCoordinates, offset, endss, stride) {
    +  var flatCenters = [];
    +  var i, ii;
    +  var extent = ol.extent.createEmpty();
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    extent = ol.extent.createOrUpdateFromFlatCoordinates(
    +        flatCoordinates, offset, ends[0], stride);
    +    flatCenters.push((extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2);
    +    offset = ends[ends.length - 1];
    +  }
    +  return flatCenters;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/closestflatgeom.js b/mapbox-gl/ol/ol/geom/flat/closestflatgeom.js
    new file mode 100644
    index 0000000000..9170d2478c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/closestflatgeom.js
    @@ -0,0 +1,260 @@
    +goog.provide('ol.geom.flat.closest');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('ol.math');
    +
    +
    +/**
    + * Returns the point on the 2D line segment flatCoordinates[offset1] to
    + * flatCoordinates[offset2] that is closest to the point (x, y).  Extra
    + * dimensions are linearly interpolated.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset1 Offset 1.
    + * @param {number} offset2 Offset 2.
    + * @param {number} stride Stride.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {Array.<number>} closestPoint Closest point.
    + */
    +ol.geom.flat.closest.point =
    +    function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) {
    +  var x1 = flatCoordinates[offset1];
    +  var y1 = flatCoordinates[offset1 + 1];
    +  var dx = flatCoordinates[offset2] - x1;
    +  var dy = flatCoordinates[offset2 + 1] - y1;
    +  var i, offset;
    +  if (dx === 0 && dy === 0) {
    +    offset = offset1;
    +  } else {
    +    var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
    +    if (t > 1) {
    +      offset = offset2;
    +    } else if (t > 0) {
    +      for (i = 0; i < stride; ++i) {
    +        closestPoint[i] = goog.math.lerp(flatCoordinates[offset1 + i],
    +            flatCoordinates[offset2 + i], t);
    +      }
    +      closestPoint.length = stride;
    +      return;
    +    } else {
    +      offset = offset1;
    +    }
    +  }
    +  for (i = 0; i < stride; ++i) {
    +    closestPoint[i] = flatCoordinates[offset + i];
    +  }
    +  closestPoint.length = stride;
    +};
    +
    +
    +/**
    + * Return the squared of the largest distance between any pair of consecutive
    + * coordinates.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} maxSquaredDelta Max squared delta.
    + * @return {number} Max squared delta.
    + */
    +ol.geom.flat.closest.getMaxSquaredDelta =
    +    function(flatCoordinates, offset, end, stride, maxSquaredDelta) {
    +  var x1 = flatCoordinates[offset];
    +  var y1 = flatCoordinates[offset + 1];
    +  for (offset += stride; offset < end; offset += stride) {
    +    var x2 = flatCoordinates[offset];
    +    var y2 = flatCoordinates[offset + 1];
    +    var squaredDelta = ol.math.squaredDistance(x1, y1, x2, y2);
    +    if (squaredDelta > maxSquaredDelta) {
    +      maxSquaredDelta = squaredDelta;
    +    }
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return maxSquaredDelta;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} maxSquaredDelta Max squared delta.
    + * @return {number} Max squared delta.
    + */
    +ol.geom.flat.closest.getsMaxSquaredDelta =
    +    function(flatCoordinates, offset, ends, stride, maxSquaredDelta) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    maxSquaredDelta = ol.geom.flat.closest.getMaxSquaredDelta(
    +        flatCoordinates, offset, end, stride, maxSquaredDelta);
    +    offset = end;
    +  }
    +  return maxSquaredDelta;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {number} maxSquaredDelta Max squared delta.
    + * @return {number} Max squared delta.
    + */
    +ol.geom.flat.closest.getssMaxSquaredDelta =
    +    function(flatCoordinates, offset, endss, stride, maxSquaredDelta) {
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    maxSquaredDelta = ol.geom.flat.closest.getsMaxSquaredDelta(
    +        flatCoordinates, offset, ends, stride, maxSquaredDelta);
    +    offset = ends[ends.length - 1];
    +  }
    +  return maxSquaredDelta;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} maxDelta Max delta.
    + * @param {boolean} isRing Is ring.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {Array.<number>} closestPoint Closest point.
    + * @param {number} minSquaredDistance Minimum squared distance.
    + * @param {Array.<number>=} opt_tmpPoint Temporary point object.
    + * @return {number} Minimum squared distance.
    + */
    +ol.geom.flat.closest.getClosestPoint = function(flatCoordinates, offset, end,
    +    stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
    +    opt_tmpPoint) {
    +  if (offset == end) {
    +    return minSquaredDistance;
    +  }
    +  var i, squaredDistance;
    +  if (maxDelta === 0) {
    +    // All points are identical, so just test the first point.
    +    squaredDistance = ol.math.squaredDistance(
    +        x, y, flatCoordinates[offset], flatCoordinates[offset + 1]);
    +    if (squaredDistance < minSquaredDistance) {
    +      for (i = 0; i < stride; ++i) {
    +        closestPoint[i] = flatCoordinates[offset + i];
    +      }
    +      closestPoint.length = stride;
    +      return squaredDistance;
    +    } else {
    +      return minSquaredDistance;
    +    }
    +  }
    +  goog.asserts.assert(maxDelta > 0);
    +  var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN];
    +  var index = offset + stride;
    +  while (index < end) {
    +    ol.geom.flat.closest.point(
    +        flatCoordinates, index - stride, index, stride, x, y, tmpPoint);
    +    squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
    +    if (squaredDistance < minSquaredDistance) {
    +      minSquaredDistance = squaredDistance;
    +      for (i = 0; i < stride; ++i) {
    +        closestPoint[i] = tmpPoint[i];
    +      }
    +      closestPoint.length = stride;
    +      index += stride;
    +    } else {
    +      // Skip ahead multiple points, because we know that all the skipped
    +      // points cannot be any closer than the closest point we have found so
    +      // far.  We know this because we know how close the current point is, how
    +      // close the closest point we have found so far is, and the maximum
    +      // distance between consecutive points.  For example, if we're currently
    +      // at distance 10, the best we've found so far is 3, and that the maximum
    +      // distance between consecutive points is 2, then we'll need to skip at
    +      // least (10 - 3) / 2 == 3 (rounded down) points to have any chance of
    +      // finding a closer point.  We use Math.max(..., 1) to ensure that we
    +      // always advance at least one point, to avoid an infinite loop.
    +      index += stride * Math.max(
    +          ((Math.sqrt(squaredDistance) -
    +            Math.sqrt(minSquaredDistance)) / maxDelta) | 0, 1);
    +    }
    +  }
    +  if (isRing) {
    +    // Check the closing segment.
    +    ol.geom.flat.closest.point(
    +        flatCoordinates, end - stride, offset, stride, x, y, tmpPoint);
    +    squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
    +    if (squaredDistance < minSquaredDistance) {
    +      minSquaredDistance = squaredDistance;
    +      for (i = 0; i < stride; ++i) {
    +        closestPoint[i] = tmpPoint[i];
    +      }
    +      closestPoint.length = stride;
    +    }
    +  }
    +  return minSquaredDistance;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} maxDelta Max delta.
    + * @param {boolean} isRing Is ring.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {Array.<number>} closestPoint Closest point.
    + * @param {number} minSquaredDistance Minimum squared distance.
    + * @param {Array.<number>=} opt_tmpPoint Temporary point object.
    + * @return {number} Minimum squared distance.
    + */
    +ol.geom.flat.closest.getsClosestPoint = function(flatCoordinates, offset, ends,
    +    stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
    +    opt_tmpPoint) {
    +  var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN];
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    minSquaredDistance = ol.geom.flat.closest.getClosestPoint(
    +        flatCoordinates, offset, end, stride,
    +        maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);
    +    offset = end;
    +  }
    +  return minSquaredDistance;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {number} maxDelta Max delta.
    + * @param {boolean} isRing Is ring.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {Array.<number>} closestPoint Closest point.
    + * @param {number} minSquaredDistance Minimum squared distance.
    + * @param {Array.<number>=} opt_tmpPoint Temporary point object.
    + * @return {number} Minimum squared distance.
    + */
    +ol.geom.flat.closest.getssClosestPoint = function(flatCoordinates, offset,
    +    endss, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance,
    +    opt_tmpPoint) {
    +  var tmpPoint = goog.isDef(opt_tmpPoint) ? opt_tmpPoint : [NaN, NaN];
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    minSquaredDistance = ol.geom.flat.closest.getsClosestPoint(
    +        flatCoordinates, offset, ends, stride,
    +        maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);
    +    offset = ends[ends.length - 1];
    +  }
    +  return minSquaredDistance;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/containsflatgeom.js b/mapbox-gl/ol/ol/geom/flat/containsflatgeom.js
    new file mode 100644
    index 0000000000..18379450ac
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/containsflatgeom.js
    @@ -0,0 +1,114 @@
    +goog.provide('ol.geom.flat.contains');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} Contains extent.
    + */
    +ol.geom.flat.contains.linearRingContainsExtent =
    +    function(flatCoordinates, offset, end, stride, extent) {
    +  var outside = ol.extent.forEachCorner(extent,
    +      /**
    +       * @param {ol.Coordinate} coordinate Coordinate.
    +       */
    +      function(coordinate) {
    +        return !ol.geom.flat.contains.linearRingContainsXY(flatCoordinates,
    +            offset, end, stride, coordinate[0], coordinate[1]);
    +      });
    +  return !outside;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {boolean} Contains (x, y).
    + */
    +ol.geom.flat.contains.linearRingContainsXY =
    +    function(flatCoordinates, offset, end, stride, x, y) {
    +  // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
    +  var contains = false;
    +  var x1 = flatCoordinates[end - stride];
    +  var y1 = flatCoordinates[end - stride + 1];
    +  for (; offset < end; offset += stride) {
    +    var x2 = flatCoordinates[offset];
    +    var y2 = flatCoordinates[offset + 1];
    +    var intersect = ((y1 > y) != (y2 > y)) &&
    +        (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1);
    +    if (intersect) {
    +      contains = !contains;
    +    }
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return contains;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {boolean} Contains (x, y).
    + */
    +ol.geom.flat.contains.linearRingsContainsXY =
    +    function(flatCoordinates, offset, ends, stride, x, y) {
    +  goog.asserts.assert(ends.length > 0);
    +  if (ends.length === 0) {
    +    return false;
    +  }
    +  if (!ol.geom.flat.contains.linearRingContainsXY(
    +      flatCoordinates, offset, ends[0], stride, x, y)) {
    +    return false;
    +  }
    +  var i, ii;
    +  for (i = 1, ii = ends.length; i < ii; ++i) {
    +    if (ol.geom.flat.contains.linearRingContainsXY(
    +        flatCoordinates, ends[i - 1], ends[i], stride, x, y)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {boolean} Contains (x, y).
    + */
    +ol.geom.flat.contains.linearRingssContainsXY =
    +    function(flatCoordinates, offset, endss, stride, x, y) {
    +  goog.asserts.assert(endss.length > 0);
    +  if (endss.length === 0) {
    +    return false;
    +  }
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    if (ol.geom.flat.contains.linearRingsContainsXY(
    +        flatCoordinates, offset, ends, stride, x, y)) {
    +      return true;
    +    }
    +    offset = ends[ends.length - 1];
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/deflateflatgeom.js b/mapbox-gl/ol/ol/geom/flat/deflateflatgeom.js
    new file mode 100644
    index 0000000000..e0aca18d91
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/deflateflatgeom.js
    @@ -0,0 +1,91 @@
    +goog.provide('ol.geom.flat.deflate');
    +
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} stride Stride.
    + * @return {number} offset Offset.
    + */
    +ol.geom.flat.deflate.coordinate =
    +    function(flatCoordinates, offset, coordinate, stride) {
    +  goog.asserts.assert(coordinate.length == stride);
    +  var i, ii;
    +  for (i = 0, ii = coordinate.length; i < ii; ++i) {
    +    flatCoordinates[offset++] = coordinate[i];
    +  }
    +  return offset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {number} stride Stride.
    + * @return {number} offset Offset.
    + */
    +ol.geom.flat.deflate.coordinates =
    +    function(flatCoordinates, offset, coordinates, stride) {
    +  var i, ii;
    +  for (i = 0, ii = coordinates.length; i < ii; ++i) {
    +    var coordinate = coordinates[i];
    +    goog.asserts.assert(coordinate.length == stride);
    +    var j;
    +    for (j = 0; j < stride; ++j) {
    +      flatCoordinates[offset++] = coordinate[j];
    +    }
    +  }
    +  return offset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<ol.Coordinate>>} coordinatess Coordinatess.
    + * @param {number} stride Stride.
    + * @param {Array.<number>=} opt_ends Ends.
    + * @return {Array.<number>} Ends.
    + */
    +ol.geom.flat.deflate.coordinatess =
    +    function(flatCoordinates, offset, coordinatess, stride, opt_ends) {
    +  var ends = goog.isDef(opt_ends) ? opt_ends : [];
    +  var i = 0;
    +  var j, jj;
    +  for (j = 0, jj = coordinatess.length; j < jj; ++j) {
    +    var end = ol.geom.flat.deflate.coordinates(
    +        flatCoordinates, offset, coordinatess[j], stride);
    +    ends[i++] = end;
    +    offset = end;
    +  }
    +  ends.length = i;
    +  return ends;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinatesss Coordinatesss.
    + * @param {number} stride Stride.
    + * @param {Array.<Array.<number>>=} opt_endss Endss.
    + * @return {Array.<Array.<number>>} Endss.
    + */
    +ol.geom.flat.deflate.coordinatesss =
    +    function(flatCoordinates, offset, coordinatesss, stride, opt_endss) {
    +  var endss = goog.isDef(opt_endss) ? opt_endss : [];
    +  var i = 0;
    +  var j, jj;
    +  for (j = 0, jj = coordinatesss.length; j < jj; ++j) {
    +    var ends = ol.geom.flat.deflate.coordinatess(
    +        flatCoordinates, offset, coordinatesss[j], stride, endss[i]);
    +    endss[i++] = ends;
    +    offset = ends[ends.length - 1];
    +  }
    +  endss.length = i;
    +  return endss;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/flipflatgeom.js b/mapbox-gl/ol/ol/geom/flat/flipflatgeom.js
    new file mode 100644
    index 0000000000..220f2115ac
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/flipflatgeom.js
    @@ -0,0 +1,37 @@
    +goog.provide('ol.geom.flat.flip');
    +
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @param {number=} opt_destOffset Destination offset.
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.geom.flat.flip.flipXY =
    +    function(flatCoordinates, offset, end, stride, opt_dest, opt_destOffset) {
    +  var dest, destOffset;
    +  if (goog.isDef(opt_dest)) {
    +    dest = opt_dest;
    +    destOffset = goog.isDef(opt_destOffset) ? opt_destOffset : 0;
    +  } else {
    +    goog.asserts.assert(!goog.isDef(opt_destOffset));
    +    dest = [];
    +    destOffset = 0;
    +  }
    +  var j, k;
    +  for (j = offset; j < end; ) {
    +    var x = flatCoordinates[j++];
    +    dest[destOffset++] = flatCoordinates[j++];
    +    dest[destOffset++] = x;
    +    for (k = 2; k < stride; ++k) {
    +      dest[destOffset++] = flatCoordinates[j++];
    +    }
    +  }
    +  dest.length = destOffset;
    +  return dest;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/geodesicflatgeom.js b/mapbox-gl/ol/ol/geom/flat/geodesicflatgeom.js
    new file mode 100644
    index 0000000000..48403f93bb
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/geodesicflatgeom.js
    @@ -0,0 +1,180 @@
    +goog.provide('ol.geom.flat.geodesic');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('ol.TransformFunction');
    +goog.require('ol.math');
    +goog.require('ol.proj');
    +
    +
    +/**
    + * @private
    + * @param {function(number): ol.Coordinate} interpolate Interpolate function.
    + * @param {ol.TransformFunction} transform Transform from longitude/latitude to
    + *     projected coordinates.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.geom.flat.geodesic.line_ =
    +    function(interpolate, transform, squaredTolerance) {
    +  // FIXME reduce garbage generation
    +  // FIXME optimize stack operations
    +
    +  /** @type {Array.<number>} */
    +  var flatCoordinates = [];
    +
    +  var geoA = interpolate(0);
    +  var geoB = interpolate(1);
    +
    +  var a = transform(geoA);
    +  var b = transform(geoB);
    +
    +  /** @type {Array.<ol.Coordinate>} */
    +  var geoStack = [geoB, geoA];
    +  /** @type {Array.<ol.Coordinate>} */
    +  var stack = [b, a];
    +  /** @type {Array.<number>} */
    +  var fractionStack = [1, 0];
    +
    +  /** @type {Object.<string, boolean>} */
    +  var fractions = {};
    +
    +  var maxIterations = 1e5;
    +  var geoM, m, fracA, fracB, fracM, key;
    +
    +  while (--maxIterations > 0 && fractionStack.length > 0) {
    +    // Pop the a coordinate off the stack
    +    fracA = fractionStack.pop();
    +    geoA = geoStack.pop();
    +    a = stack.pop();
    +    // Add the a coordinate if it has not been added yet
    +    key = fracA.toString();
    +    if (!goog.object.containsKey(fractions, key)) {
    +      flatCoordinates.push(a[0], a[1]);
    +      fractions[key] = true;
    +    }
    +    // Pop the b coordinate off the stack
    +    fracB = fractionStack.pop();
    +    geoB = geoStack.pop();
    +    b = stack.pop();
    +    // Find the m point between the a and b coordinates
    +    fracM = (fracA + fracB) / 2;
    +    geoM = interpolate(fracM);
    +    m = transform(geoM);
    +    if (ol.math.squaredSegmentDistance(m[0], m[1], a[0], a[1],
    +        b[0], b[1]) < squaredTolerance) {
    +      // If the m point is sufficiently close to the straight line, then we
    +      // discard it.  Just use the b coordinate and move on to the next line
    +      // segment.
    +      flatCoordinates.push(b[0], b[1]);
    +      key = fracB.toString();
    +      goog.asserts.assert(!goog.object.containsKey(fractions, key));
    +      fractions[key] = true;
    +    } else {
    +      // Otherwise, we need to subdivide the current line segment.  Split it
    +      // into two and push the two line segments onto the stack.
    +      fractionStack.push(fracB, fracM, fracM, fracA);
    +      stack.push(b, m, m, a);
    +      geoStack.push(geoB, geoM, geoM, geoA);
    +    }
    +  }
    +  goog.asserts.assert(maxIterations > 0);
    +
    +  return flatCoordinates;
    +};
    +
    +
    +/**
    +* Generate a great-circle arcs between two lat/lon points.
    +* @param {number} lon1 Longitude 1 in degrees.
    +* @param {number} lat1 Latitude 1 in degrees.
    +* @param {number} lon2 Longitude 2 in degrees.
    +* @param {number} lat2 Latitude 2 in degrees.
    + * @param {ol.proj.Projection} projection Projection.
    +* @param {number} squaredTolerance Squared tolerance.
    +* @return {Array.<number>} Flat coordinates.
    +*/
    +ol.geom.flat.geodesic.greatCircleArc = function(
    +    lon1, lat1, lon2, lat2, projection, squaredTolerance) {
    +
    +  var geoProjection = ol.proj.get('EPSG:4326');
    +
    +  var cosLat1 = Math.cos(goog.math.toRadians(lat1));
    +  var sinLat1 = Math.sin(goog.math.toRadians(lat1));
    +  var cosLat2 = Math.cos(goog.math.toRadians(lat2));
    +  var sinLat2 = Math.sin(goog.math.toRadians(lat2));
    +  var cosDeltaLon = Math.cos(goog.math.toRadians(lon2 - lon1));
    +  var sinDeltaLon = Math.sin(goog.math.toRadians(lon2 - lon1));
    +  var d = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosDeltaLon;
    +
    +  return ol.geom.flat.geodesic.line_(
    +      /**
    +       * @param {number} frac Fraction.
    +       * @return {ol.Coordinate} Coordinate.
    +       */
    +      function(frac) {
    +        if (1 <= d) {
    +          return [lon2, lat2];
    +        }
    +        var D = frac * Math.acos(d);
    +        var cosD = Math.cos(D);
    +        var sinD = Math.sin(D);
    +        var y = sinDeltaLon * cosLat2;
    +        var x = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosDeltaLon;
    +        var theta = Math.atan2(y, x);
    +        var lat = Math.asin(sinLat1 * cosD + cosLat1 * sinD * Math.cos(theta));
    +        var lon = goog.math.toRadians(lon1) +
    +            Math.atan2(Math.sin(theta) * sinD * cosLat1,
    +                       cosD - sinLat1 * Math.sin(lat));
    +        return [goog.math.toDegrees(lon), goog.math.toDegrees(lat)];
    +      }, ol.proj.getTransform(geoProjection, projection), squaredTolerance);
    +};
    +
    +
    +/**
    + * Generate a meridian (line at constant longitude).
    + * @param {number} lon Longitude.
    + * @param {number} lat1 Latitude 1.
    + * @param {number} lat2 Latitude 2.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.geom.flat.geodesic.meridian =
    +    function(lon, lat1, lat2, projection, squaredTolerance) {
    +  var epsg4326Projection = ol.proj.get('EPSG:4326');
    +  return ol.geom.flat.geodesic.line_(
    +      /**
    +       * @param {number} frac Fraction.
    +       * @return {ol.Coordinate} Coordinate.
    +       */
    +      function(frac) {
    +        return [lon, lat1 + ((lat2 - lat1) * frac)];
    +      },
    +      ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance);
    +};
    +
    +
    +/**
    + * Generate a parallel (line at constant latitude).
    + * @param {number} lat Latitude.
    + * @param {number} lon1 Longitude 1.
    + * @param {number} lon2 Longitude 2.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.geom.flat.geodesic.parallel =
    +    function(lat, lon1, lon2, projection, squaredTolerance) {
    +  var epsg4326Projection = ol.proj.get('EPSG:4326');
    +  return ol.geom.flat.geodesic.line_(
    +      /**
    +       * @param {number} frac Fraction.
    +       * @return {ol.Coordinate} Coordinate.
    +       */
    +      function(frac) {
    +        return [lon1 + ((lon2 - lon1) * frac), lat];
    +      },
    +      ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance);
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/inflateflatgeom.js b/mapbox-gl/ol/ol/geom/flat/inflateflatgeom.js
    new file mode 100644
    index 0000000000..53c4620c3f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/inflateflatgeom.js
    @@ -0,0 +1,71 @@
    +goog.provide('ol.geom.flat.inflate');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {Array.<ol.Coordinate>=} opt_coordinates Coordinates.
    + * @return {Array.<ol.Coordinate>} Coordinates.
    + */
    +ol.geom.flat.inflate.coordinates =
    +    function(flatCoordinates, offset, end, stride, opt_coordinates) {
    +  var coordinates = goog.isDef(opt_coordinates) ? opt_coordinates : [];
    +  var i = 0;
    +  var j;
    +  for (j = offset; j < end; j += stride) {
    +    coordinates[i++] = flatCoordinates.slice(j, j + stride);
    +  }
    +  coordinates.length = i;
    +  return coordinates;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {Array.<Array.<ol.Coordinate>>=} opt_coordinatess Coordinatess.
    + * @return {Array.<Array.<ol.Coordinate>>} Coordinatess.
    + */
    +ol.geom.flat.inflate.coordinatess =
    +    function(flatCoordinates, offset, ends, stride, opt_coordinatess) {
    +  var coordinatess = goog.isDef(opt_coordinatess) ? opt_coordinatess : [];
    +  var i = 0;
    +  var j, jj;
    +  for (j = 0, jj = ends.length; j < jj; ++j) {
    +    var end = ends[j];
    +    coordinatess[i++] = ol.geom.flat.inflate.coordinates(
    +        flatCoordinates, offset, end, stride, coordinatess[i]);
    +    offset = end;
    +  }
    +  coordinatess.length = i;
    +  return coordinatess;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {Array.<Array.<Array.<ol.Coordinate>>>=} opt_coordinatesss
    + *     Coordinatesss.
    + * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinatesss.
    + */
    +ol.geom.flat.inflate.coordinatesss =
    +    function(flatCoordinates, offset, endss, stride, opt_coordinatesss) {
    +  var coordinatesss = goog.isDef(opt_coordinatesss) ? opt_coordinatesss : [];
    +  var i = 0;
    +  var j, jj;
    +  for (j = 0, jj = endss.length; j < jj; ++j) {
    +    var ends = endss[j];
    +    coordinatesss[i++] = ol.geom.flat.inflate.coordinatess(
    +        flatCoordinates, offset, ends, stride, coordinatesss[i]);
    +    offset = ends[ends.length - 1];
    +  }
    +  coordinatesss.length = i;
    +  return coordinatesss;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/interiorpointflatgeom.js b/mapbox-gl/ol/ol/geom/flat/interiorpointflatgeom.js
    new file mode 100644
    index 0000000000..bc8d108222
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/interiorpointflatgeom.js
    @@ -0,0 +1,92 @@
    +goog.provide('ol.geom.flat.interiorpoint');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.geom.flat.contains');
    +
    +
    +/**
    + * Calculates a point that is likely to lie in the interior of the linear rings.
    + * Inspired by JTS's com.vividsolutions.jts.geom.Geometry#getInteriorPoint.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {Array.<number>} flatCenters Flat centers.
    + * @param {number} flatCentersOffset Flat center offset.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @return {Array.<number>} Destination.
    + */
    +ol.geom.flat.interiorpoint.linearRings = function(flatCoordinates, offset,
    +    ends, stride, flatCenters, flatCentersOffset, opt_dest) {
    +  var i, ii, x, x1, x2, y1, y2;
    +  var y = flatCenters[flatCentersOffset + 1];
    +  /** @type {Array.<number>} */
    +  var intersections = [];
    +  // Calculate intersections with the horizontal line
    +  var end = ends[0];
    +  x1 = flatCoordinates[end - stride];
    +  y1 = flatCoordinates[end - stride + 1];
    +  for (i = offset; i < end; i += stride) {
    +    x2 = flatCoordinates[i];
    +    y2 = flatCoordinates[i + 1];
    +    if ((y <= y1 && y2 <= y) || (y1 <= y && y <= y2)) {
    +      x = (y - y1) / (y2 - y1) * (x2 - x1) + x1;
    +      intersections.push(x);
    +    }
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  // Find the longest segment of the horizontal line that has its center point
    +  // inside the linear ring.
    +  var pointX = NaN;
    +  var maxSegmentLength = -Infinity;
    +  intersections.sort();
    +  x1 = intersections[0];
    +  for (i = 1, ii = intersections.length; i < ii; ++i) {
    +    x2 = intersections[i];
    +    var segmentLength = Math.abs(x2 - x1);
    +    if (segmentLength > maxSegmentLength) {
    +      x = (x1 + x2) / 2;
    +      if (ol.geom.flat.contains.linearRingsContainsXY(
    +          flatCoordinates, offset, ends, stride, x, y)) {
    +        pointX = x;
    +        maxSegmentLength = segmentLength;
    +      }
    +    }
    +    x1 = x2;
    +  }
    +  if (isNaN(pointX)) {
    +    // There is no horizontal line that has its center point inside the linear
    +    // ring.  Use the center of the the linear ring's extent.
    +    pointX = flatCenters[flatCentersOffset];
    +  }
    +  if (goog.isDef(opt_dest)) {
    +    opt_dest.push(pointX, y);
    +    return opt_dest;
    +  } else {
    +    return [pointX, y];
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {Array.<number>} flatCenters Flat centers.
    + * @return {Array.<number>} Interior points.
    + */
    +ol.geom.flat.interiorpoint.linearRingss =
    +    function(flatCoordinates, offset, endss, stride, flatCenters) {
    +  goog.asserts.assert(2 * endss.length == flatCenters.length);
    +  var interiorPoints = [];
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    interiorPoints = ol.geom.flat.interiorpoint.linearRings(flatCoordinates,
    +        offset, ends, stride, flatCenters, 2 * i, interiorPoints);
    +    offset = ends[ends.length - 1];
    +  }
    +  return interiorPoints;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/interpolateflatgeom.js b/mapbox-gl/ol/ol/geom/flat/interpolateflatgeom.js
    new file mode 100644
    index 0000000000..9d24deb9da
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/interpolateflatgeom.js
    @@ -0,0 +1,190 @@
    +goog.provide('ol.geom.flat.interpolate');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} fraction Fraction.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @return {Array.<number>} Destination.
    + */
    +ol.geom.flat.interpolate.lineString =
    +    function(flatCoordinates, offset, end, stride, fraction, opt_dest) {
    +  // FIXME interpolate extra dimensions
    +  goog.asserts.assert(0 <= fraction && fraction <= 1);
    +  var pointX = NaN;
    +  var pointY = NaN;
    +  var n = (end - offset) / stride;
    +  if (n === 0) {
    +    goog.asserts.fail();
    +  } else if (n == 1) {
    +    pointX = flatCoordinates[offset];
    +    pointY = flatCoordinates[offset + 1];
    +  } else if (n == 2) {
    +    pointX = (1 - fraction) * flatCoordinates[offset] +
    +        fraction * flatCoordinates[offset + stride];
    +    pointY = (1 - fraction) * flatCoordinates[offset + 1] +
    +        fraction * flatCoordinates[offset + stride + 1];
    +  } else {
    +    var x1 = flatCoordinates[offset];
    +    var y1 = flatCoordinates[offset + 1];
    +    var length = 0;
    +    var cumulativeLengths = [0];
    +    var i;
    +    for (i = offset + stride; i < end; i += stride) {
    +      var x2 = flatCoordinates[i];
    +      var y2 = flatCoordinates[i + 1];
    +      length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    +      cumulativeLengths.push(length);
    +      x1 = x2;
    +      y1 = y2;
    +    }
    +    var target = fraction * length;
    +    var index = goog.array.binarySearch(cumulativeLengths, target);
    +    if (index < 0) {
    +      var t = (target - cumulativeLengths[-index - 2]) /
    +          (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]);
    +      var o = offset + (-index - 2) * stride;
    +      pointX = goog.math.lerp(
    +          flatCoordinates[o], flatCoordinates[o + stride], t);
    +      pointY = goog.math.lerp(
    +          flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t);
    +    } else {
    +      pointX = flatCoordinates[offset + index * stride];
    +      pointY = flatCoordinates[offset + index * stride + 1];
    +    }
    +  }
    +  if (goog.isDefAndNotNull(opt_dest)) {
    +    opt_dest[0] = pointX;
    +    opt_dest[1] = pointY;
    +    return opt_dest;
    +  } else {
    +    return [pointX, pointY];
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} m M.
    + * @param {boolean} extrapolate Extrapolate.
    + * @return {ol.Coordinate} Coordinate.
    + */
    +ol.geom.flat.lineStringCoordinateAtM =
    +    function(flatCoordinates, offset, end, stride, m, extrapolate) {
    +  if (end == offset) {
    +    return null;
    +  }
    +  var coordinate;
    +  if (m < flatCoordinates[offset + stride - 1]) {
    +    if (extrapolate) {
    +      coordinate = flatCoordinates.slice(offset, offset + stride);
    +      coordinate[stride - 1] = m;
    +      return coordinate;
    +    } else {
    +      return null;
    +    }
    +  } else if (flatCoordinates[end - 1] < m) {
    +    if (extrapolate) {
    +      coordinate = flatCoordinates.slice(end - stride, end);
    +      coordinate[stride - 1] = m;
    +      return coordinate;
    +    } else {
    +      return null;
    +    }
    +  }
    +  // FIXME use O(1) search
    +  if (m == flatCoordinates[offset + stride - 1]) {
    +    return flatCoordinates.slice(offset, offset + stride);
    +  }
    +  var lo = offset / stride;
    +  var hi = end / stride;
    +  while (lo < hi) {
    +    var mid = (lo + hi) >> 1;
    +    if (m < flatCoordinates[(mid + 1) * stride - 1]) {
    +      hi = mid;
    +    } else {
    +      lo = mid + 1;
    +    }
    +  }
    +  var m0 = flatCoordinates[lo * stride - 1];
    +  if (m == m0) {
    +    return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride);
    +  }
    +  var m1 = flatCoordinates[(lo + 1) * stride - 1];
    +  goog.asserts.assert(m0 < m);
    +  goog.asserts.assert(m <= m1);
    +  var t = (m - m0) / (m1 - m0);
    +  coordinate = [];
    +  var i;
    +  for (i = 0; i < stride - 1; ++i) {
    +    coordinate.push(goog.math.lerp(flatCoordinates[(lo - 1) * stride + i],
    +        flatCoordinates[lo * stride + i], t));
    +  }
    +  coordinate.push(m);
    +  goog.asserts.assert(coordinate.length == stride);
    +  return coordinate;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} m M.
    + * @param {boolean} extrapolate Extrapolate.
    + * @param {boolean} interpolate Interpolate.
    + * @return {ol.Coordinate} Coordinate.
    + */
    +ol.geom.flat.lineStringsCoordinateAtM = function(
    +    flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) {
    +  if (interpolate) {
    +    return ol.geom.flat.lineStringCoordinateAtM(
    +        flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate);
    +  }
    +  var coordinate;
    +  if (m < flatCoordinates[stride - 1]) {
    +    if (extrapolate) {
    +      coordinate = flatCoordinates.slice(0, stride);
    +      coordinate[stride - 1] = m;
    +      return coordinate;
    +    } else {
    +      return null;
    +    }
    +  }
    +  if (flatCoordinates[flatCoordinates.length - 1] < m) {
    +    if (extrapolate) {
    +      coordinate = flatCoordinates.slice(flatCoordinates.length - stride);
    +      coordinate[stride - 1] = m;
    +      return coordinate;
    +    } else {
    +      return null;
    +    }
    +  }
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    if (offset == end) {
    +      continue;
    +    }
    +    if (m < flatCoordinates[offset + stride - 1]) {
    +      return null;
    +    } else if (m <= flatCoordinates[end - 1]) {
    +      return ol.geom.flat.lineStringCoordinateAtM(
    +          flatCoordinates, offset, end, stride, m, false);
    +    }
    +    offset = end;
    +  }
    +  goog.asserts.fail();
    +  return null;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/intersectsextent.js b/mapbox-gl/ol/ol/geom/flat/intersectsextent.js
    new file mode 100644
    index 0000000000..a86ca4f2ff
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/intersectsextent.js
    @@ -0,0 +1,154 @@
    +goog.provide('ol.geom.flat.intersectsextent');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.flat.contains');
    +goog.require('ol.geom.flat.segments');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} True if the geometry and the extent intersect.
    + */
    +ol.geom.flat.intersectsextent.lineString =
    +    function(flatCoordinates, offset, end, stride, extent) {
    +  var coordinatesExtent = ol.extent.extendFlatCoordinates(
    +      ol.extent.createEmpty(), flatCoordinates, offset, end, stride);
    +  if (!ol.extent.intersects(extent, coordinatesExtent)) {
    +    return false;
    +  }
    +  if (ol.extent.containsExtent(extent, coordinatesExtent)) {
    +    return true;
    +  }
    +  if (coordinatesExtent[0] >= extent[0] &&
    +      coordinatesExtent[2] <= extent[2]) {
    +    return true;
    +  }
    +  if (coordinatesExtent[1] >= extent[1] &&
    +      coordinatesExtent[3] <= extent[3]) {
    +    return true;
    +  }
    +  return ol.geom.flat.segments.forEach(flatCoordinates, offset, end, stride,
    +      /**
    +       * @param {ol.Coordinate} point1 Start point.
    +       * @param {ol.Coordinate} point2 End point.
    +       * @return {boolean} `true` if the segment and the extent intersect,
    +       *     `false` otherwise.
    +       */
    +      function(point1, point2) {
    +        return ol.extent.intersectsSegment(extent, point1, point2);
    +      });
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} True if the geometry and the extent intersect.
    + */
    +ol.geom.flat.intersectsextent.lineStrings =
    +    function(flatCoordinates, offset, ends, stride, extent) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    if (ol.geom.flat.intersectsextent.lineString(
    +        flatCoordinates, offset, ends[i], stride, extent)) {
    +      return true;
    +    }
    +    offset = ends[i];
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} True if the geometry and the extent intersect.
    + */
    +ol.geom.flat.intersectsextent.linearRing =
    +    function(flatCoordinates, offset, end, stride, extent) {
    +  if (ol.geom.flat.intersectsextent.lineString(
    +      flatCoordinates, offset, end, stride, extent)) {
    +    return true;
    +  }
    +  if (ol.geom.flat.contains.linearRingContainsXY(
    +      flatCoordinates, offset, end, stride, extent[0], extent[1])) {
    +    return true;
    +  }
    +  if (ol.geom.flat.contains.linearRingContainsXY(
    +      flatCoordinates, offset, end, stride, extent[0], extent[3])) {
    +    return true;
    +  }
    +  if (ol.geom.flat.contains.linearRingContainsXY(
    +      flatCoordinates, offset, end, stride, extent[2], extent[1])) {
    +    return true;
    +  }
    +  if (ol.geom.flat.contains.linearRingContainsXY(
    +      flatCoordinates, offset, end, stride, extent[2], extent[3])) {
    +    return true;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} True if the geometry and the extent intersect.
    + */
    +ol.geom.flat.intersectsextent.linearRings =
    +    function(flatCoordinates, offset, ends, stride, extent) {
    +  goog.asserts.assert(ends.length > 0);
    +  if (!ol.geom.flat.intersectsextent.linearRing(
    +      flatCoordinates, offset, ends[0], stride, extent)) {
    +    return false;
    +  }
    +  if (ends.length === 1) {
    +    return true;
    +  }
    +  var i, ii;
    +  for (i = 1, ii = ends.length; i < ii; ++i) {
    +    if (ol.geom.flat.contains.linearRingContainsExtent(
    +        flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} True if the geometry and the extent intersect.
    + */
    +ol.geom.flat.intersectsextent.linearRingss =
    +    function(flatCoordinates, offset, endss, stride, extent) {
    +  goog.asserts.assert(endss.length > 0);
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    if (ol.geom.flat.intersectsextent.linearRings(
    +        flatCoordinates, offset, ends, stride, extent)) {
    +      return true;
    +    }
    +    offset = ends[ends.length - 1];
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/lengthflatgeom.js b/mapbox-gl/ol/ol/geom/flat/lengthflatgeom.js
    new file mode 100644
    index 0000000000..ac1c685504
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/lengthflatgeom.js
    @@ -0,0 +1,43 @@
    +goog.provide('ol.geom.flat.length');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {number} Length.
    + */
    +ol.geom.flat.length.lineString =
    +    function(flatCoordinates, offset, end, stride) {
    +  var x1 = flatCoordinates[offset];
    +  var y1 = flatCoordinates[offset + 1];
    +  var length = 0;
    +  var i;
    +  for (i = offset + stride; i < end; i += stride) {
    +    var x2 = flatCoordinates[i];
    +    var y2 = flatCoordinates[i + 1];
    +    length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return length;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {number} Perimeter.
    + */
    +ol.geom.flat.length.linearRing =
    +    function(flatCoordinates, offset, end, stride) {
    +  var perimeter =
    +      ol.geom.flat.length.lineString(flatCoordinates, offset, end, stride);
    +  var dx = flatCoordinates[end - stride] - flatCoordinates[offset];
    +  var dy = flatCoordinates[end - stride + 1] - flatCoordinates[offset + 1];
    +  perimeter += Math.sqrt(dx * dx + dy * dy);
    +  return perimeter;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/orientflatgeom.js b/mapbox-gl/ol/ol/geom/flat/orientflatgeom.js
    new file mode 100644
    index 0000000000..48c7dd5008
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/orientflatgeom.js
    @@ -0,0 +1,115 @@
    +goog.provide('ol.geom.flat.orient');
    +
    +goog.require('ol.geom.flat.reverse');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {boolean} Is clockwise.
    + */
    +ol.geom.flat.orient.linearRingIsClockwise =
    +    function(flatCoordinates, offset, end, stride) {
    +  // http://tinyurl.com/clockwise-method
    +  // https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp
    +  var edge = 0;
    +  var x1 = flatCoordinates[end - stride];
    +  var y1 = flatCoordinates[end - stride + 1];
    +  for (; offset < end; offset += stride) {
    +    var x2 = flatCoordinates[offset];
    +    var y2 = flatCoordinates[offset + 1];
    +    edge += (x2 - x1) * (y2 + y1);
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return edge > 0;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @return {boolean} `true` if all rings are correctly oriented, `false`
    + *     otherwise.
    + */
    +ol.geom.flat.orient.linearRingsAreOriented =
    +    function(flatCoordinates, offset, ends, stride) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(
    +        flatCoordinates, offset, end, stride);
    +    if (i === 0 ? !isClockwise : isClockwise) {
    +      return false;
    +    }
    +    offset = end;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @return {boolean} `true` if all rings are correctly oriented, `false`
    + *     otherwise.
    + */
    +ol.geom.flat.linearRingssAreOriented =
    +    function(flatCoordinates, offset, endss, stride) {
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    if (!ol.geom.flat.orient.linearRingsAreOriented(
    +        flatCoordinates, offset, endss[i], stride)) {
    +      return false;
    +    }
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @return {number} End.
    + */
    +ol.geom.flat.orient.orientLinearRings =
    +    function(flatCoordinates, offset, ends, stride) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var isClockwise = ol.geom.flat.orient.linearRingIsClockwise(
    +        flatCoordinates, offset, end, stride);
    +    var reverse = i === 0 ? !isClockwise : isClockwise;
    +    if (reverse) {
    +      ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride);
    +    }
    +    offset = end;
    +  }
    +  return offset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @return {number} End.
    + */
    +ol.geom.flat.orient.orientLinearRingss =
    +    function(flatCoordinates, offset, endss, stride) {
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    offset = ol.geom.flat.orient.orientLinearRings(
    +        flatCoordinates, offset, endss[i], stride);
    +  }
    +  return offset;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/reverseflatgeom.js b/mapbox-gl/ol/ol/geom/flat/reverseflatgeom.js
    new file mode 100644
    index 0000000000..32ad1dbe01
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/reverseflatgeom.js
    @@ -0,0 +1,22 @@
    +goog.provide('ol.geom.flat.reverse');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + */
    +ol.geom.flat.reverse.coordinates =
    +    function(flatCoordinates, offset, end, stride) {
    +  while (offset < end - stride) {
    +    var i;
    +    for (i = 0; i < stride; ++i) {
    +      var tmp = flatCoordinates[offset + i];
    +      flatCoordinates[offset + i] = flatCoordinates[end - stride + i];
    +      flatCoordinates[end - stride + i] = tmp;
    +    }
    +    offset += stride;
    +    end -= stride;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/segmentsflatgeom.js b/mapbox-gl/ol/ol/geom/flat/segmentsflatgeom.js
    new file mode 100644
    index 0000000000..b834eecbdc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/segmentsflatgeom.js
    @@ -0,0 +1,35 @@
    +goog.provide('ol.geom.flat.segments');
    +
    +
    +/**
    + * This function calls `callback` for each segment of the flat coordinates
    + * array. If the callback returns a truthy value the function returns that
    + * value immediately. Otherwise the function returns `false`.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
    + *     called for each segment.
    + * @param {S=} opt_this The object to be used as the value of 'this'
    + *     within callback.
    + * @return {T|boolean} Value.
    + * @template T,S
    + */
    +ol.geom.flat.segments.forEach =
    +    function(flatCoordinates, offset, end, stride, callback, opt_this) {
    +  var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]];
    +  var point2 = [];
    +  var ret;
    +  for (; (offset + stride) < end; offset += stride) {
    +    point2[0] = flatCoordinates[offset + stride];
    +    point2[1] = flatCoordinates[offset + stride + 1];
    +    ret = callback.call(opt_this, point1, point2);
    +    if (ret) {
    +      return ret;
    +    }
    +    point1[0] = point2[0];
    +    point1[1] = point2[1];
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/simplifyflatgeom.js b/mapbox-gl/ol/ol/geom/flat/simplifyflatgeom.js
    new file mode 100644
    index 0000000000..7b747c6280
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/simplifyflatgeom.js
    @@ -0,0 +1,400 @@
    +// Based on simplify-js https://github.com/mourner/simplify-js
    +// Copyright (c) 2012, Vladimir Agafonkin
    +// All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are met:
    +//
    +//    1. Redistributions of source code must retain the above copyright notice,
    +//       this list of conditions and the following disclaimer.
    +//
    +//    2. Redistributions in binary form must reproduce the above copyright
    +//       notice, this list of conditions and the following disclaimer in the
    +//       documentation and/or other materials provided with the distribution.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +// POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.geom.flat.simplify');
    +
    +goog.require('ol.math');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {boolean} highQuality Highest quality.
    + * @param {Array.<number>=} opt_simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @return {Array.<number>} Simplified line string.
    + */
    +ol.geom.flat.simplify.lineString = function(flatCoordinates, offset, end,
    +    stride, squaredTolerance, highQuality, opt_simplifiedFlatCoordinates) {
    +  var simplifiedFlatCoordinates = goog.isDef(opt_simplifiedFlatCoordinates) ?
    +      opt_simplifiedFlatCoordinates : [];
    +  if (!highQuality) {
    +    end = ol.geom.flat.simplify.radialDistance(flatCoordinates, offset, end,
    +        stride, squaredTolerance,
    +        simplifiedFlatCoordinates, 0);
    +    flatCoordinates = simplifiedFlatCoordinates;
    +    offset = 0;
    +    stride = 2;
    +  }
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
    +      flatCoordinates, offset, end, stride, squaredTolerance,
    +      simplifiedFlatCoordinates, 0);
    +  return simplifiedFlatCoordinates;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.douglasPeucker = function(flatCoordinates, offset, end,
    +    stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {
    +  var n = (end - offset) / stride;
    +  if (n < 3) {
    +    for (; offset < end; offset += stride) {
    +      simplifiedFlatCoordinates[simplifiedOffset++] =
    +          flatCoordinates[offset];
    +      simplifiedFlatCoordinates[simplifiedOffset++] =
    +          flatCoordinates[offset + 1];
    +    }
    +    return simplifiedOffset;
    +  }
    +  /** @type {Array.<number>} */
    +  var markers = new Array(n);
    +  markers[0] = 1;
    +  markers[n - 1] = 1;
    +  /** @type {Array.<number>} */
    +  var stack = [offset, end - stride];
    +  var index = 0;
    +  var i;
    +  while (stack.length > 0) {
    +    var last = stack.pop();
    +    var first = stack.pop();
    +    var maxSquaredDistance = 0;
    +    var x1 = flatCoordinates[first];
    +    var y1 = flatCoordinates[first + 1];
    +    var x2 = flatCoordinates[last];
    +    var y2 = flatCoordinates[last + 1];
    +    for (i = first + stride; i < last; i += stride) {
    +      var x = flatCoordinates[i];
    +      var y = flatCoordinates[i + 1];
    +      var squaredDistance = ol.math.squaredSegmentDistance(
    +          x, y, x1, y1, x2, y2);
    +      if (squaredDistance > maxSquaredDistance) {
    +        index = i;
    +        maxSquaredDistance = squaredDistance;
    +      }
    +    }
    +    if (maxSquaredDistance > squaredTolerance) {
    +      markers[(index - offset) / stride] = 1;
    +      if (first + stride < index) {
    +        stack.push(first, index);
    +      }
    +      if (index + stride < last) {
    +        stack.push(index, last);
    +      }
    +    }
    +  }
    +  for (i = 0; i < n; ++i) {
    +    if (markers[i]) {
    +      simplifiedFlatCoordinates[simplifiedOffset++] =
    +          flatCoordinates[offset + i * stride];
    +      simplifiedFlatCoordinates[simplifiedOffset++] =
    +          flatCoordinates[offset + i * stride + 1];
    +    }
    +  }
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @param {Array.<number>} simplifiedEnds Simplified ends.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.douglasPeuckers = function(flatCoordinates, offset,
    +    ends, stride, squaredTolerance, simplifiedFlatCoordinates,
    +    simplifiedOffset, simplifiedEnds) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    simplifiedOffset = ol.geom.flat.simplify.douglasPeucker(
    +        flatCoordinates, offset, end, stride, squaredTolerance,
    +        simplifiedFlatCoordinates, simplifiedOffset);
    +    simplifiedEnds.push(simplifiedOffset);
    +    offset = end;
    +  }
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.douglasPeuckerss = function(
    +    flatCoordinates, offset, endss, stride, squaredTolerance,
    +    simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    var simplifiedEnds = [];
    +    simplifiedOffset = ol.geom.flat.simplify.douglasPeuckers(
    +        flatCoordinates, offset, ends, stride, squaredTolerance,
    +        simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);
    +    simplifiedEndss.push(simplifiedEnds);
    +    offset = ends[ends.length - 1];
    +  }
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.radialDistance = function(flatCoordinates, offset, end,
    +    stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {
    +  if (end <= offset + stride) {
    +    // zero or one point, no simplification possible, so copy and return
    +    for (; offset < end; offset += stride) {
    +      simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset];
    +      simplifiedFlatCoordinates[simplifiedOffset++] =
    +          flatCoordinates[offset + 1];
    +    }
    +    return simplifiedOffset;
    +  }
    +  var x1 = flatCoordinates[offset];
    +  var y1 = flatCoordinates[offset + 1];
    +  // copy first point
    +  simplifiedFlatCoordinates[simplifiedOffset++] = x1;
    +  simplifiedFlatCoordinates[simplifiedOffset++] = y1;
    +  var x2 = x1;
    +  var y2 = y1;
    +  for (offset += stride; offset < end; offset += stride) {
    +    x2 = flatCoordinates[offset];
    +    y2 = flatCoordinates[offset + 1];
    +    if (ol.math.squaredDistance(x1, y1, x2, y2) > squaredTolerance) {
    +      // copy point at offset
    +      simplifiedFlatCoordinates[simplifiedOffset++] = x2;
    +      simplifiedFlatCoordinates[simplifiedOffset++] = y2;
    +      x1 = x2;
    +      y1 = y2;
    +    }
    +  }
    +  if (x2 != x1 || y2 != y1) {
    +    // copy last point
    +    simplifiedFlatCoordinates[simplifiedOffset++] = x2;
    +    simplifiedFlatCoordinates[simplifiedOffset++] = y2;
    +  }
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {number} value Value.
    + * @param {number} tolerance Tolerance.
    + * @return {number} Rounded value.
    + */
    +ol.geom.flat.simplify.snap = function(value, tolerance) {
    +  return tolerance * Math.round(value / tolerance);
    +};
    +
    +
    +/**
    + * Simplifies a line string using an algorithm designed by Tim Schaub.
    + * Coordinates are snapped to the nearest value in a virtual grid and
    + * consecutive duplicate coordinates are discarded.  This effectively preserves
    + * topology as the simplification of any subsection of a line string is
    + * independent of the rest of the line string.  This means that, for examples,
    + * the common edge between two polygons will be simplified to the same line
    + * string independently in both polygons.  This implementation uses a single
    + * pass over the coordinates and eliminates intermediate collinear points.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} tolerance Tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.quantize = function(flatCoordinates, offset, end, stride,
    +    tolerance, simplifiedFlatCoordinates, simplifiedOffset) {
    +  // do nothing if the line is empty
    +  if (offset == end) {
    +    return simplifiedOffset;
    +  }
    +  // snap the first coordinate (P1)
    +  var x1 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
    +  var y1 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
    +  offset += stride;
    +  // add the first coordinate to the output
    +  simplifiedFlatCoordinates[simplifiedOffset++] = x1;
    +  simplifiedFlatCoordinates[simplifiedOffset++] = y1;
    +  // find the next coordinate that does not snap to the same value as the first
    +  // coordinate (P2)
    +  var x2, y2;
    +  do {
    +    x2 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
    +    y2 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
    +    offset += stride;
    +    if (offset == end) {
    +      // all coordinates snap to the same value, the line collapses to a point
    +      // push the last snapped value anyway to ensure that the output contains
    +      // at least two points
    +      // FIXME should we really return at least two points anyway?
    +      simplifiedFlatCoordinates[simplifiedOffset++] = x2;
    +      simplifiedFlatCoordinates[simplifiedOffset++] = y2;
    +      return simplifiedOffset;
    +    }
    +  } while (x2 == x1 && y2 == y1);
    +  while (offset < end) {
    +    var x3, y3;
    +    // snap the next coordinate (P3)
    +    x3 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance);
    +    y3 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance);
    +    offset += stride;
    +    // skip P3 if it is equal to P2
    +    if (x3 == x2 && y3 == y2) {
    +      continue;
    +    }
    +    // calculate the delta between P1 and P2
    +    var dx1 = x2 - x1;
    +    var dy1 = y2 - y1;
    +    // calculate the delta between P3 and P1
    +    var dx2 = x3 - x1;
    +    var dy2 = y3 - y1;
    +    // if P1, P2, and P3 are colinear and P3 is further from P1 than P2 is from
    +    // P1 in the same direction then P2 is on the straight line between P1 and
    +    // P3
    +    if ((dx1 * dy2 == dy1 * dx2) &&
    +        ((dx1 < 0 && dx2 < dx1) || dx1 == dx2 || (dx1 > 0 && dx2 > dx1)) &&
    +        ((dy1 < 0 && dy2 < dy1) || dy1 == dy2 || (dy1 > 0 && dy2 > dy1))) {
    +      // discard P2 and set P2 = P3
    +      x2 = x3;
    +      y2 = y3;
    +      continue;
    +    }
    +    // either P1, P2, and P3 are not colinear, or they are colinear but P3 is
    +    // between P3 and P1 or on the opposite half of the line to P2.  add P2,
    +    // and continue with P1 = P2 and P2 = P3
    +    simplifiedFlatCoordinates[simplifiedOffset++] = x2;
    +    simplifiedFlatCoordinates[simplifiedOffset++] = y2;
    +    x1 = x2;
    +    y1 = y2;
    +    x2 = x3;
    +    y2 = y3;
    +  }
    +  // add the last point (P2)
    +  simplifiedFlatCoordinates[simplifiedOffset++] = x2;
    +  simplifiedFlatCoordinates[simplifiedOffset++] = y2;
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @param {number} tolerance Tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @param {Array.<number>} simplifiedEnds Simplified ends.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.quantizes = function(
    +    flatCoordinates, offset, ends, stride,
    +    tolerance,
    +    simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    simplifiedOffset = ol.geom.flat.simplify.quantize(
    +        flatCoordinates, offset, end, stride,
    +        tolerance,
    +        simplifiedFlatCoordinates, simplifiedOffset);
    +    simplifiedEnds.push(simplifiedOffset);
    +    offset = end;
    +  }
    +  return simplifiedOffset;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<Array.<number>>} endss Endss.
    + * @param {number} stride Stride.
    + * @param {number} tolerance Tolerance.
    + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat
    + *     coordinates.
    + * @param {number} simplifiedOffset Simplified offset.
    + * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss.
    + * @return {number} Simplified offset.
    + */
    +ol.geom.flat.simplify.quantizess = function(
    +    flatCoordinates, offset, endss, stride,
    +    tolerance,
    +    simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) {
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i];
    +    var simplifiedEnds = [];
    +    simplifiedOffset = ol.geom.flat.simplify.quantizes(
    +        flatCoordinates, offset, ends, stride,
    +        tolerance,
    +        simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds);
    +    simplifiedEndss.push(simplifiedEnds);
    +    offset = ends[ends.length - 1];
    +  }
    +  return simplifiedOffset;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/flat/transformflatgeom.js b/mapbox-gl/ol/ol/geom/flat/transformflatgeom.js
    new file mode 100644
    index 0000000000..7bb08e8a83
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/flat/transformflatgeom.js
    @@ -0,0 +1,65 @@
    +goog.provide('ol.geom.flat.transform');
    +
    +goog.require('goog.vec.Mat4');
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @return {Array.<number>} Transformed coordinates.
    + */
    +ol.geom.flat.transform.transform2D =
    +    function(flatCoordinates, offset, end, stride, transform, opt_dest) {
    +  var m00 = goog.vec.Mat4.getElement(transform, 0, 0);
    +  var m10 = goog.vec.Mat4.getElement(transform, 1, 0);
    +  var m01 = goog.vec.Mat4.getElement(transform, 0, 1);
    +  var m11 = goog.vec.Mat4.getElement(transform, 1, 1);
    +  var m03 = goog.vec.Mat4.getElement(transform, 0, 3);
    +  var m13 = goog.vec.Mat4.getElement(transform, 1, 3);
    +  var dest = goog.isDef(opt_dest) ? opt_dest : [];
    +  var i = 0;
    +  var j;
    +  for (j = offset; j < end; j += stride) {
    +    var x = flatCoordinates[j];
    +    var y = flatCoordinates[j + 1];
    +    dest[i++] = m00 * x + m01 * y + m03;
    +    dest[i++] = m10 * x + m11 * y + m13;
    +  }
    +  if (goog.isDef(opt_dest) && dest.length != i) {
    +    dest.length = i;
    +  }
    +  return dest;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {number} deltaX Delta X.
    + * @param {number} deltaY Delta Y.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @return {Array.<number>} Transformed coordinates.
    + */
    +ol.geom.flat.transform.translate =
    +    function(flatCoordinates, offset, end, stride, deltaX, deltaY, opt_dest) {
    +  var dest = goog.isDef(opt_dest) ? opt_dest : [];
    +  var i = 0;
    +  var j, k;
    +  for (j = offset; j < end; j += stride) {
    +    dest[i++] = flatCoordinates[j] + deltaX;
    +    dest[i++] = flatCoordinates[j + 1] + deltaY;
    +    for (k = j + 2; k < j + stride; ++k) {
    +      dest[i++] = flatCoordinates[k];
    +    }
    +  }
    +  if (goog.isDef(opt_dest) && dest.length != i) {
    +    dest.length = i;
    +  }
    +  return dest;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/geom.jsdoc b/mapbox-gl/ol/ol/geom/geom.jsdoc
    new file mode 100644
    index 0000000000..1a947e51f3
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/geom.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.geom
    + */
    diff --git a/mapbox-gl/ol/ol/geom/geometry.js b/mapbox-gl/ol/ol/geom/geometry.js
    new file mode 100644
    index 0000000000..c1ff3da2c9
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/geometry.js
    @@ -0,0 +1,238 @@
    +goog.provide('ol.geom.Geometry');
    +goog.provide('ol.geom.GeometryType');
    +
    +goog.require('goog.functions');
    +goog.require('ol.Observable');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +
    +
    +/**
    + * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`,
    + * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`,
    + * `'GeometryCollection'`, `'Circle'`.
    + * @enum {string}
    + * @api stable
    + */
    +ol.geom.GeometryType = {
    +  POINT: 'Point',
    +  LINE_STRING: 'LineString',
    +  LINEAR_RING: 'LinearRing',
    +  POLYGON: 'Polygon',
    +  MULTI_POINT: 'MultiPoint',
    +  MULTI_LINE_STRING: 'MultiLineString',
    +  MULTI_POLYGON: 'MultiPolygon',
    +  GEOMETRY_COLLECTION: 'GeometryCollection',
    +  CIRCLE: 'Circle'
    +};
    +
    +
    +/**
    + * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z')
    + * or measure ('M') coordinate is available. Supported values are `'XY'`,
    + * `'XYZ'`, `'XYM'`, `'XYZM'`.
    + * @enum {string}
    + * @api stable
    + */
    +ol.geom.GeometryLayout = {
    +  XY: 'XY',
    +  XYZ: 'XYZ',
    +  XYM: 'XYM',
    +  XYZM: 'XYZM'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for vector geometries.
    + *
    + * @constructor
    + * @extends {ol.Observable}
    + * @fires change Triggered when the geometry changes.
    + * @api stable
    + */
    +ol.geom.Geometry = function() {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.extent_ = ol.extent.createEmpty();
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.extentRevision_ = -1;
    +
    +  /**
    +   * @protected
    +   * @type {Object.<string, ol.geom.Geometry>}
    +   */
    +  this.simplifiedGeometryCache = {};
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.simplifiedGeometryMaxMinSquaredTolerance = 0;
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.simplifiedGeometryRevision = 0;
    +
    +};
    +goog.inherits(ol.geom.Geometry, ol.Observable);
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @function
    + * @return {!ol.geom.Geometry} Clone.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.clone = goog.abstractMethod;
    +
    +
    +/**
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {ol.Coordinate} closestPoint Closest point.
    + * @param {number} minSquaredDistance Minimum squared distance.
    + * @return {number} Minimum squared distance.
    + */
    +ol.geom.Geometry.prototype.closestPointXY = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.Coordinate} point Point.
    + * @param {ol.Coordinate=} opt_closestPoint Closest point.
    + * @return {ol.Coordinate} Closest point.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) {
    +  var closestPoint = goog.isDef(opt_closestPoint) ?
    +      opt_closestPoint : [NaN, NaN];
    +  this.closestPointXY(point[0], point[1], closestPoint, Infinity);
    +  return closestPoint;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {boolean} Contains coordinate.
    + */
    +ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) {
    +  return this.containsXY(coordinate[0], coordinate[1]);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @protected
    + * @return {ol.Extent} extent Extent.
    + */
    +ol.geom.Geometry.prototype.computeExtent = goog.abstractMethod;
    +
    +
    +/**
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {boolean} Contains (x, y).
    + */
    +ol.geom.Geometry.prototype.containsXY = goog.functions.FALSE;
    +
    +
    +/**
    + * Get the extent of the geometry.
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} extent Extent.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
    +  if (this.extentRevision_ != this.getRevision()) {
    +    this.extent_ = this.computeExtent(this.extent_);
    +    this.extentRevision_ = this.getRevision();
    +  }
    +  return ol.extent.returnOrUpdate(this.extent_, opt_extent);
    +};
    +
    +
    +/**
    + * Create a simplified version of this geometry using the Douglas Peucker
    + * algorithm.
    + * @see http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
    + * @function
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {ol.geom.Geometry} Simplified geometry.
    + */
    +ol.geom.Geometry.prototype.getSimplifiedGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * Get the type of this geometry.
    + * @function
    + * @return {ol.geom.GeometryType} Geometry type.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.getType = goog.abstractMethod;
    +
    +
    +/**
    + * Apply a transform function to each coordinate of the geometry.
    + * The geometry is modified in place.
    + * If you do not want the geometry modified in place, first clone() it and
    + * then use this function on the clone.
    + * @function
    + * @param {ol.TransformFunction} transformFn Transform.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.applyTransform = goog.abstractMethod;
    +
    +
    +/**
    + * Test if the geometry and the passed extent intersect.
    + * @param {ol.Extent} extent Extent.
    + * @return {boolean} `true` if the geometry and the extent intersect.
    + * @function
    + * @api
    + */
    +ol.geom.Geometry.prototype.intersectsExtent = goog.abstractMethod;
    +
    +
    +/**
    + * Translate the geometry.
    + * @param {number} deltaX Delta X.
    + * @param {number} deltaY Delta Y.
    + * @function
    + * @api
    + */
    +ol.geom.Geometry.prototype.translate = goog.abstractMethod;
    +
    +
    +/**
    + * Transform each coordinate of the geometry from one coordinate reference
    + * system to another. The geometry is modified in place.
    + * For example, a line will be transformed to a line and a circle to a circle.
    + * If you do not want the geometry modified in place, first clone() it and
    + * then use this function on the clone.
    + *
    + * @param {ol.proj.ProjectionLike} source The current projection.  Can be a
    + *     string identifier or a {@link ol.proj.Projection} object.
    + * @param {ol.proj.ProjectionLike} destination The desired projection.  Can be a
    + *     string identifier or a {@link ol.proj.Projection} object.
    + * @return {ol.geom.Geometry} This geometry.  Note that original geometry is
    + *     modified in place.
    + * @api stable
    + */
    +ol.geom.Geometry.prototype.transform = function(source, destination) {
    +  this.applyTransform(ol.proj.getTransform(source, destination));
    +  return this;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/geometrycollection.js b/mapbox-gl/ol/ol/geom/geometrycollection.js
    new file mode 100644
    index 0000000000..6cbe1c2559
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/geometrycollection.js
    @@ -0,0 +1,294 @@
    +goog.provide('ol.geom.GeometryCollection');
    +
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.extent');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.GeometryType');
    +
    +
    +
    +/**
    + * @classdesc
    + * An array of {@link ol.geom.Geometry} objects.
    + *
    + * @constructor
    + * @extends {ol.geom.Geometry}
    + * @param {Array.<ol.geom.Geometry>=} opt_geometries Geometries.
    + * @api stable
    + */
    +ol.geom.GeometryCollection = function(opt_geometries) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.geom.Geometry>}
    +   */
    +  this.geometries_ = goog.isDef(opt_geometries) ? opt_geometries : null;
    +
    +  this.listenGeometriesChange_();
    +};
    +goog.inherits(ol.geom.GeometryCollection, ol.geom.Geometry);
    +
    +
    +/**
    + * @param {Array.<ol.geom.Geometry>} geometries Geometries.
    + * @private
    + * @return {Array.<ol.geom.Geometry>} Cloned geometries.
    + */
    +ol.geom.GeometryCollection.cloneGeometries_ = function(geometries) {
    +  var clonedGeometries = [];
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    clonedGeometries.push(geometries[i].clone());
    +  }
    +  return clonedGeometries;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.geom.GeometryCollection.prototype.unlistenGeometriesChange_ = function() {
    +  var i, ii;
    +  if (goog.isNull(this.geometries_)) {
    +    return;
    +  }
    +  for (i = 0, ii = this.geometries_.length; i < ii; ++i) {
    +    goog.events.unlisten(
    +        this.geometries_[i], goog.events.EventType.CHANGE,
    +        this.changed, false, this);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.geom.GeometryCollection.prototype.listenGeometriesChange_ = function() {
    +  var i, ii;
    +  if (goog.isNull(this.geometries_)) {
    +    return;
    +  }
    +  for (i = 0, ii = this.geometries_.length; i < ii; ++i) {
    +    goog.events.listen(
    +        this.geometries_[i], goog.events.EventType.CHANGE,
    +        this.changed, false, this);
    +  }
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.GeometryCollection} Clone.
    + * @api stable
    + */
    +ol.geom.GeometryCollection.prototype.clone = function() {
    +  var geometryCollection = new ol.geom.GeometryCollection(null);
    +  geometryCollection.setGeometries(this.geometries_);
    +  return geometryCollection;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.GeometryCollection.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  var geometries = this.geometries_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    minSquaredDistance = geometries[i].closestPointXY(
    +        x, y, closestPoint, minSquaredDistance);
    +  }
    +  return minSquaredDistance;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.GeometryCollection.prototype.containsXY = function(x, y) {
    +  var geometries = this.geometries_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    if (geometries[i].containsXY(x, y)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.GeometryCollection.prototype.computeExtent = function(extent) {
    +  ol.extent.createOrUpdateEmpty(extent);
    +  var geometries = this.geometries_;
    +  for (var i = 0, ii = geometries.length; i < ii; ++i) {
    +    ol.extent.extend(extent, geometries[i].getExtent());
    +  }
    +  return extent;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.Geometry>} Geometries.
    + * @api stable
    + */
    +ol.geom.GeometryCollection.prototype.getGeometries = function() {
    +  return ol.geom.GeometryCollection.cloneGeometries_(this.geometries_);
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.Geometry>} Geometries.
    + */
    +ol.geom.GeometryCollection.prototype.getGeometriesArray = function() {
    +  return this.geometries_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.GeometryCollection.prototype.getSimplifiedGeometry =
    +    function(squaredTolerance) {
    +  if (this.simplifiedGeometryRevision != this.getRevision()) {
    +    goog.object.clear(this.simplifiedGeometryCache);
    +    this.simplifiedGeometryMaxMinSquaredTolerance = 0;
    +    this.simplifiedGeometryRevision = this.getRevision();
    +  }
    +  if (squaredTolerance < 0 ||
    +      (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
    +       squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) {
    +    return this;
    +  }
    +  var key = squaredTolerance.toString();
    +  if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
    +    return this.simplifiedGeometryCache[key];
    +  } else {
    +    var simplifiedGeometries = [];
    +    var geometries = this.geometries_;
    +    var simplified = false;
    +    var i, ii;
    +    for (i = 0, ii = geometries.length; i < ii; ++i) {
    +      var geometry = geometries[i];
    +      var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
    +      simplifiedGeometries.push(simplifiedGeometry);
    +      if (simplifiedGeometry !== geometry) {
    +        simplified = true;
    +      }
    +    }
    +    if (simplified) {
    +      var simplifiedGeometryCollection = new ol.geom.GeometryCollection(null);
    +      simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries);
    +      this.simplifiedGeometryCache[key] = simplifiedGeometryCollection;
    +      return simplifiedGeometryCollection;
    +    } else {
    +      this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
    +      return this;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.GeometryCollection.prototype.getType = function() {
    +  return ol.geom.GeometryType.GEOMETRY_COLLECTION;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.GeometryCollection.prototype.intersectsExtent = function(extent) {
    +  var geometries = this.geometries_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    if (geometries[i].intersectsExtent(extent)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @return {boolean} Is empty.
    + */
    +ol.geom.GeometryCollection.prototype.isEmpty = function() {
    +  return goog.array.isEmpty(this.geometries_);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.geom.Geometry>} geometries Geometries.
    + * @api stable
    + */
    +ol.geom.GeometryCollection.prototype.setGeometries = function(geometries) {
    +  this.setGeometriesArray(
    +      ol.geom.GeometryCollection.cloneGeometries_(geometries));
    +};
    +
    +
    +/**
    + * @param {Array.<ol.geom.Geometry>} geometries Geometries.
    + */
    +ol.geom.GeometryCollection.prototype.setGeometriesArray = function(geometries) {
    +  this.unlistenGeometriesChange_();
    +  this.geometries_ = geometries;
    +  this.listenGeometriesChange_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.GeometryCollection.prototype.applyTransform = function(transformFn) {
    +  var geometries = this.geometries_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    geometries[i].applyTransform(transformFn);
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * Translate the geometry.
    + * @param {number} deltaX Delta X.
    + * @param {number} deltaY Delta Y.
    + * @api
    + */
    +ol.geom.GeometryCollection.prototype.translate = function(deltaX, deltaY) {
    +  var geometries = this.geometries_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    geometries[i].translate(deltaX, deltaY);
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.GeometryCollection.prototype.disposeInternal = function() {
    +  this.unlistenGeometriesChange_();
    +  goog.base(this, 'disposeInternal');
    +};
    diff --git a/mapbox-gl/ol/ol/geom/linearring.js b/mapbox-gl/ol/ol/geom/linearring.js
    new file mode 100644
    index 0000000000..f527d13944
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/linearring.js
    @@ -0,0 +1,154 @@
    +goog.provide('ol.geom.LinearRing');
    +
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.area');
    +goog.require('ol.geom.flat.closest');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.geom.flat.simplify');
    +
    +
    +
    +/**
    + * @classdesc
    + * Linear ring geometry. Only used as part of polygon; cannot be rendered
    + * on its own.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.LinearRing = function(coordinates, opt_layout) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDelta_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDeltaRevision_ = -1;
    +
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +
    +};
    +goog.inherits(ol.geom.LinearRing, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.LinearRing} Clone.
    + * @api stable
    + */
    +ol.geom.LinearRing.prototype.clone = function() {
    +  var linearRing = new ol.geom.LinearRing(null);
    +  linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
    +  return linearRing;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.LinearRing.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  if (this.maxDeltaRevision_ != this.getRevision()) {
    +    this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta(
    +        this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
    +    this.maxDeltaRevision_ = this.getRevision();
    +  }
    +  return ol.geom.flat.closest.getClosestPoint(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
    +};
    +
    +
    +/**
    + * @return {number} Area (on projected plane).
    + * @api stable
    + */
    +ol.geom.LinearRing.prototype.getArea = function() {
    +  return ol.geom.flat.area.linearRing(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<ol.Coordinate>} Coordinates.
    + * @api stable
    + */
    +ol.geom.LinearRing.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinates(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.LinearRing.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  var simplifiedFlatCoordinates = [];
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      squaredTolerance, simplifiedFlatCoordinates, 0);
    +  var simplifiedLinearRing = new ol.geom.LinearRing(null);
    +  simplifiedLinearRing.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates);
    +  return simplifiedLinearRing;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.LinearRing.prototype.getType = function() {
    +  return ol.geom.GeometryType.LINEAR_RING;
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.LinearRing.prototype.setCoordinates =
    +    function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 1);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
    +        this.flatCoordinates, 0, coordinates, this.stride);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + */
    +ol.geom.LinearRing.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates) {
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/geom/linestring.js b/mapbox-gl/ol/ol/geom/linestring.js
    new file mode 100644
    index 0000000000..08c99fc771
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/linestring.js
    @@ -0,0 +1,254 @@
    +goog.provide('ol.geom.LineString');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.closest');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.geom.flat.interpolate');
    +goog.require('ol.geom.flat.intersectsextent');
    +goog.require('ol.geom.flat.length');
    +goog.require('ol.geom.flat.segments');
    +goog.require('ol.geom.flat.simplify');
    +
    +
    +
    +/**
    + * @classdesc
    + * Linestring geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.LineString = function(coordinates, opt_layout) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.flatMidpoint_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.flatMidpointRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDelta_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDeltaRevision_ = -1;
    +
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +
    +};
    +goog.inherits(ol.geom.LineString, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @api stable
    + */
    +ol.geom.LineString.prototype.appendCoordinate = function(coordinate) {
    +  goog.asserts.assert(coordinate.length == this.stride);
    +  if (goog.isNull(this.flatCoordinates)) {
    +    this.flatCoordinates = coordinate.slice();
    +  } else {
    +    goog.array.extend(this.flatCoordinates, coordinate);
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.LineString} Clone.
    + * @api stable
    + */
    +ol.geom.LineString.prototype.clone = function() {
    +  var lineString = new ol.geom.LineString(null);
    +  lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
    +  return lineString;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.LineString.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  if (this.maxDeltaRevision_ != this.getRevision()) {
    +    this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta(
    +        this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
    +    this.maxDeltaRevision_ = this.getRevision();
    +  }
    +  return ol.geom.flat.closest.getClosestPoint(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
    +};
    +
    +
    +/**
    + * Iterate over each segment, calling the provided callback.
    + * If the callback returns a truthy value the function returns that
    + * value immediately. Otherwise the function returns `false`.
    + *
    + * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function
    + *     called for each segment.
    + * @param {S=} opt_this The object to be used as the value of 'this'
    + *     within callback.
    + * @return {T|boolean} Value.
    + * @template T,S
    + * @api
    + */
    +ol.geom.LineString.prototype.forEachSegment = function(callback, opt_this) {
    +  return ol.geom.flat.segments.forEach(this.flatCoordinates, 0,
    +      this.flatCoordinates.length, this.stride, callback, opt_this);
    +};
    +
    +
    +/**
    + * Returns the coordinate at `m` using linear interpolation, or `null` if no
    + * such coordinate exists.
    + *
    + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the
    + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
    + * M will return the first coordinate and Ms greater than the last M will
    + * return the last coordinate.
    + *
    + * @param {number} m M.
    + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.geom.LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) {
    +  if (this.layout != ol.geom.GeometryLayout.XYM &&
    +      this.layout != ol.geom.GeometryLayout.XYZM) {
    +    return null;
    +  }
    +  var extrapolate = goog.isDef(opt_extrapolate) ? opt_extrapolate : false;
    +  return ol.geom.flat.lineStringCoordinateAtM(this.flatCoordinates, 0,
    +      this.flatCoordinates.length, this.stride, m, extrapolate);
    +};
    +
    +
    +/**
    + * @return {Array.<ol.Coordinate>} Coordinates.
    + * @api stable
    + */
    +ol.geom.LineString.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinates(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
    +};
    +
    +
    +/**
    + * @return {number} Length (on projected plane).
    + * @api stable
    + */
    +ol.geom.LineString.prototype.getLength = function() {
    +  return ol.geom.flat.length.lineString(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Flat midpoint.
    + */
    +ol.geom.LineString.prototype.getFlatMidpoint = function() {
    +  if (this.flatMidpointRevision_ != this.getRevision()) {
    +    this.flatMidpoint_ = ol.geom.flat.interpolate.lineString(
    +        this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +        0.5, this.flatMidpoint_);
    +    this.flatMidpointRevision_ = this.getRevision();
    +  }
    +  return this.flatMidpoint_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.LineString.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  var simplifiedFlatCoordinates = [];
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      squaredTolerance, simplifiedFlatCoordinates, 0);
    +  var simplifiedLineString = new ol.geom.LineString(null);
    +  simplifiedLineString.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates);
    +  return simplifiedLineString;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.LineString.prototype.getType = function() {
    +  return ol.geom.GeometryType.LINE_STRING;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.LineString.prototype.intersectsExtent = function(extent) {
    +  return ol.geom.flat.intersectsextent.lineString(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      extent);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.LineString.prototype.setCoordinates =
    +    function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 1);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
    +        this.flatCoordinates, 0, coordinates, this.stride);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + */
    +ol.geom.LineString.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates) {
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/geom/multilinestring.js b/mapbox-gl/ol/ol/geom/multilinestring.js
    new file mode 100644
    index 0000000000..b419475733
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/multilinestring.js
    @@ -0,0 +1,320 @@
    +goog.provide('ol.geom.MultiLineString');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.closest');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.geom.flat.interpolate');
    +goog.require('ol.geom.flat.intersectsextent');
    +goog.require('ol.geom.flat.simplify');
    +
    +
    +
    +/**
    + * @classdesc
    + * Multi-linestring geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiLineString = function(coordinates, opt_layout) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.ends_ = [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDelta_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDeltaRevision_ = -1;
    +
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +
    +};
    +goog.inherits(ol.geom.MultiLineString, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * @param {ol.geom.LineString} lineString LineString.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.appendLineString = function(lineString) {
    +  goog.asserts.assert(lineString.getLayout() == this.layout);
    +  if (goog.isNull(this.flatCoordinates)) {
    +    this.flatCoordinates = lineString.getFlatCoordinates().slice();
    +  } else {
    +    goog.array.extend(
    +        this.flatCoordinates, lineString.getFlatCoordinates().slice());
    +  }
    +  this.ends_.push(this.flatCoordinates.length);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.MultiLineString} Clone.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.clone = function() {
    +  var multiLineString = new ol.geom.MultiLineString(null);
    +  multiLineString.setFlatCoordinates(
    +      this.layout, this.flatCoordinates.slice(), this.ends_.slice());
    +  return multiLineString;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiLineString.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  if (this.maxDeltaRevision_ != this.getRevision()) {
    +    this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta(
    +        this.flatCoordinates, 0, this.ends_, this.stride, 0));
    +    this.maxDeltaRevision_ = this.getRevision();
    +  }
    +  return ol.geom.flat.closest.getsClosestPoint(
    +      this.flatCoordinates, 0, this.ends_, this.stride,
    +      this.maxDelta_, false, x, y, closestPoint, minSquaredDistance);
    +};
    +
    +
    +/**
    + * Returns the coordinate at `m` using linear interpolation, or `null` if no
    + * such coordinate exists.
    + *
    + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the
    + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first
    + * M will return the first coordinate and Ms greater than the last M will
    + * return the last coordinate.
    + *
    + * `opt_interpolate` controls interpolation between consecutive LineStrings
    + * within the MultiLineString. If `opt_interpolate` is `true` the coordinates
    + * will be linearly interpolated between the last coordinate of one LineString
    + * and the first coordinate of the next LineString.  If `opt_interpolate` is
    + * `false` then the function will return `null` for Ms falling between
    + * LineStrings.
    + *
    + * @param {number} m M.
    + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`.
    + * @param {boolean=} opt_interpolate Interpolate. Default is `false`.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.getCoordinateAtM =
    +    function(m, opt_extrapolate, opt_interpolate) {
    +  if ((this.layout != ol.geom.GeometryLayout.XYM &&
    +       this.layout != ol.geom.GeometryLayout.XYZM) ||
    +      this.flatCoordinates.length === 0) {
    +    return null;
    +  }
    +  var extrapolate = goog.isDef(opt_extrapolate) ? opt_extrapolate : false;
    +  var interpolate = goog.isDef(opt_interpolate) ? opt_interpolate : false;
    +  return ol.geom.flat.lineStringsCoordinateAtM(this.flatCoordinates, 0,
    +      this.ends_, this.stride, m, extrapolate, interpolate);
    +};
    +
    +
    +/**
    + * @return {Array.<Array.<ol.Coordinate>>} Coordinates.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinatess(
    +      this.flatCoordinates, 0, this.ends_, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Ends.
    + */
    +ol.geom.MultiLineString.prototype.getEnds = function() {
    +  return this.ends_;
    +};
    +
    +
    +/**
    + * @param {number} index Index.
    + * @return {ol.geom.LineString} LineString.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.getLineString = function(index) {
    +  goog.asserts.assert(0 <= index && index < this.ends_.length);
    +  if (index < 0 || this.ends_.length <= index) {
    +    return null;
    +  }
    +  var lineString = new ol.geom.LineString(null);
    +  lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
    +      index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]));
    +  return lineString;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.LineString>} LineStrings.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.getLineStrings = function() {
    +  var flatCoordinates = this.flatCoordinates;
    +  var ends = this.ends_;
    +  var layout = this.layout;
    +  /** @type {Array.<ol.geom.LineString>} */
    +  var lineStrings = [];
    +  var offset = 0;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var lineString = new ol.geom.LineString(null);
    +    lineString.setFlatCoordinates(layout, flatCoordinates.slice(offset, end));
    +    lineStrings.push(lineString);
    +    offset = end;
    +  }
    +  return lineStrings;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Flat midpoints.
    + */
    +ol.geom.MultiLineString.prototype.getFlatMidpoints = function() {
    +  var midpoints = [];
    +  var flatCoordinates = this.flatCoordinates;
    +  var offset = 0;
    +  var ends = this.ends_;
    +  var stride = this.stride;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var midpoint = ol.geom.flat.interpolate.lineString(
    +        flatCoordinates, offset, end, stride, 0.5);
    +    goog.array.extend(midpoints, midpoint);
    +    offset = end;
    +  }
    +  return midpoints;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiLineString.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  var simplifiedFlatCoordinates = [];
    +  var simplifiedEnds = [];
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeuckers(
    +      this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance,
    +      simplifiedFlatCoordinates, 0, simplifiedEnds);
    +  var simplifiedMultiLineString = new ol.geom.MultiLineString(null);
    +  simplifiedMultiLineString.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds);
    +  return simplifiedMultiLineString;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.getType = function() {
    +  return ol.geom.GeometryType.MULTI_LINE_STRING;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.MultiLineString.prototype.intersectsExtent = function(extent) {
    +  return ol.geom.flat.intersectsextent.lineStrings(
    +      this.flatCoordinates, 0, this.ends_, this.stride, extent);
    +};
    +
    +
    +/**
    + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiLineString.prototype.setCoordinates =
    +    function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 2);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    var ends = ol.geom.flat.deflate.coordinatess(
    +        this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
    +    this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {Array.<number>} ends Ends.
    + */
    +ol.geom.MultiLineString.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates, ends) {
    +  if (goog.isNull(flatCoordinates)) {
    +    goog.asserts.assert(!goog.isNull(ends) && ends.length === 0);
    +  } else if (ends.length === 0) {
    +    goog.asserts.assert(flatCoordinates.length === 0);
    +  } else {
    +    goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1]);
    +  }
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.ends_ = ends;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {Array.<ol.geom.LineString>} lineStrings LineStrings.
    + */
    +ol.geom.MultiLineString.prototype.setLineStrings = function(lineStrings) {
    +  var layout = ol.geom.GeometryLayout.XY;
    +  var flatCoordinates = [];
    +  var ends = [];
    +  var i, ii;
    +  for (i = 0, ii = lineStrings.length; i < ii; ++i) {
    +    var lineString = lineStrings[i];
    +    if (i === 0) {
    +      layout = lineString.getLayout();
    +    } else {
    +      // FIXME better handle the case of non-matching layouts
    +      goog.asserts.assert(lineString.getLayout() == layout);
    +    }
    +    goog.array.extend(flatCoordinates, lineString.getFlatCoordinates());
    +    ends.push(flatCoordinates.length);
    +  }
    +  this.setFlatCoordinates(layout, flatCoordinates, ends);
    +};
    diff --git a/mapbox-gl/ol/ol/geom/multipoint.js b/mapbox-gl/ol/ol/geom/multipoint.js
    new file mode 100644
    index 0000000000..32b1cc02ae
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/multipoint.js
    @@ -0,0 +1,193 @@
    +goog.provide('ol.geom.MultiPoint');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.math');
    +
    +
    +
    +/**
    + * @classdesc
    + * Multi-point geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiPoint = function(coordinates, opt_layout) {
    +  goog.base(this);
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +};
    +goog.inherits(ol.geom.MultiPoint, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * @param {ol.geom.Point} point Point.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.appendPoint = function(point) {
    +  goog.asserts.assert(point.getLayout() == this.layout);
    +  if (goog.isNull(this.flatCoordinates)) {
    +    this.flatCoordinates = point.getFlatCoordinates().slice();
    +  } else {
    +    goog.array.extend(this.flatCoordinates, point.getFlatCoordinates());
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.MultiPoint} Clone.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.clone = function() {
    +  var multiPoint = new ol.geom.MultiPoint(null);
    +  multiPoint.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
    +  return multiPoint;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiPoint.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  var flatCoordinates = this.flatCoordinates;
    +  var stride = this.stride;
    +  var i, ii, j;
    +  for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
    +    var squaredDistance = ol.math.squaredDistance(
    +        x, y, flatCoordinates[i], flatCoordinates[i + 1]);
    +    if (squaredDistance < minSquaredDistance) {
    +      minSquaredDistance = squaredDistance;
    +      for (j = 0; j < stride; ++j) {
    +        closestPoint[j] = flatCoordinates[i + j];
    +      }
    +      closestPoint.length = stride;
    +    }
    +  }
    +  return minSquaredDistance;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.Coordinate>} Coordinates.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinates(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
    +};
    +
    +
    +/**
    + * @param {number} index Index.
    + * @return {ol.geom.Point} Point.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.getPoint = function(index) {
    +  var n = goog.isNull(this.flatCoordinates) ?
    +      0 : this.flatCoordinates.length / this.stride;
    +  goog.asserts.assert(0 <= index && index < n);
    +  if (index < 0 || n <= index) {
    +    return null;
    +  }
    +  var point = new ol.geom.Point(null);
    +  point.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
    +      index * this.stride, (index + 1) * this.stride));
    +  return point;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.Point>} Points.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.getPoints = function() {
    +  var flatCoordinates = this.flatCoordinates;
    +  var layout = this.layout;
    +  var stride = this.stride;
    +  /** @type {Array.<ol.geom.Point>} */
    +  var points = [];
    +  var i, ii;
    +  for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
    +    var point = new ol.geom.Point(null);
    +    point.setFlatCoordinates(layout, flatCoordinates.slice(i, i + stride));
    +    points.push(point);
    +  }
    +  return points;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.getType = function() {
    +  return ol.geom.GeometryType.MULTI_POINT;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.MultiPoint.prototype.intersectsExtent = function(extent) {
    +  var flatCoordinates = this.flatCoordinates;
    +  var stride = this.stride;
    +  var i, ii, x, y;
    +  for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
    +    x = flatCoordinates[i];
    +    y = flatCoordinates[i + 1];
    +    if (ol.extent.containsXY(extent, x, y)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Coordinate>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiPoint.prototype.setCoordinates =
    +    function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 1);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    this.flatCoordinates.length = ol.geom.flat.deflate.coordinates(
    +        this.flatCoordinates, 0, coordinates, this.stride);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + */
    +ol.geom.MultiPoint.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates) {
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/geom/multipolygon.js b/mapbox-gl/ol/ol/geom/multipolygon.js
    new file mode 100644
    index 0000000000..3dafa464d6
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/multipolygon.js
    @@ -0,0 +1,404 @@
    +goog.provide('ol.geom.MultiPolygon');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.area');
    +goog.require('ol.geom.flat.center');
    +goog.require('ol.geom.flat.closest');
    +goog.require('ol.geom.flat.contains');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.geom.flat.interiorpoint');
    +goog.require('ol.geom.flat.intersectsextent');
    +goog.require('ol.geom.flat.orient');
    +goog.require('ol.geom.flat.simplify');
    +
    +
    +
    +/**
    + * @classdesc
    + * Multi-polygon geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiPolygon = function(coordinates, opt_layout) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {Array.<Array.<number>>}
    +   * @private
    +   */
    +  this.endss_ = [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.flatInteriorPointsRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.flatInteriorPoints_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDelta_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDeltaRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.orientedRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.orientedFlatCoordinates_ = null;
    +
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +
    +};
    +goog.inherits(ol.geom.MultiPolygon, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * @param {ol.geom.Polygon} polygon Polygon.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) {
    +  goog.asserts.assert(polygon.getLayout() == this.layout);
    +  /** @type {Array.<number>} */
    +  var ends;
    +  if (goog.isNull(this.flatCoordinates)) {
    +    this.flatCoordinates = polygon.getFlatCoordinates().slice();
    +    ends = polygon.getEnds().slice();
    +    this.endss_.push();
    +  } else {
    +    var offset = this.flatCoordinates.length;
    +    goog.array.extend(this.flatCoordinates, polygon.getFlatCoordinates());
    +    ends = polygon.getEnds().slice();
    +    var i, ii;
    +    for (i = 0, ii = ends.length; i < ii; ++i) {
    +      ends[i] += offset;
    +    }
    +  }
    +  this.endss_.push(ends);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.MultiPolygon} Clone.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.clone = function() {
    +  var multiPolygon = new ol.geom.MultiPolygon(null);
    +  multiPolygon.setFlatCoordinates(
    +      this.layout, this.flatCoordinates.slice(), this.endss_.slice());
    +  return multiPolygon;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiPolygon.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  if (this.maxDeltaRevision_ != this.getRevision()) {
    +    this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getssMaxSquaredDelta(
    +        this.flatCoordinates, 0, this.endss_, this.stride, 0));
    +    this.maxDeltaRevision_ = this.getRevision();
    +  }
    +  return ol.geom.flat.closest.getssClosestPoint(
    +      this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
    +      this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiPolygon.prototype.containsXY = function(x, y) {
    +  return ol.geom.flat.contains.linearRingssContainsXY(
    +      this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y);
    +};
    +
    +
    +/**
    + * @return {number} Area (on projected plane).
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getArea = function() {
    +  return ol.geom.flat.area.linearRingss(
    +      this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinates.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinatesss(
    +      this.flatCoordinates, 0, this.endss_, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<Array.<number>>} Endss.
    + */
    +ol.geom.MultiPolygon.prototype.getEndss = function() {
    +  return this.endss_;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Flat interior points.
    + */
    +ol.geom.MultiPolygon.prototype.getFlatInteriorPoints = function() {
    +  if (this.flatInteriorPointsRevision_ != this.getRevision()) {
    +    var flatCenters = ol.geom.flat.center.linearRingss(
    +        this.flatCoordinates, 0, this.endss_, this.stride);
    +    this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRingss(
    +        this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride,
    +        flatCenters);
    +    this.flatInteriorPointsRevision_ = this.getRevision();
    +  }
    +  return this.flatInteriorPoints_;
    +};
    +
    +
    +/**
    + * @return {ol.geom.MultiPoint} Interior points.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getInteriorPoints = function() {
    +  var interiorPoints = new ol.geom.MultiPoint(null);
    +  interiorPoints.setFlatCoordinates(ol.geom.GeometryLayout.XY,
    +      this.getFlatInteriorPoints().slice());
    +  return interiorPoints;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Oriented flat coordinates.
    + */
    +ol.geom.MultiPolygon.prototype.getOrientedFlatCoordinates = function() {
    +  if (this.orientedRevision_ != this.getRevision()) {
    +    var flatCoordinates = this.flatCoordinates;
    +    if (ol.geom.flat.linearRingssAreOriented(
    +        flatCoordinates, 0, this.endss_, this.stride)) {
    +      this.orientedFlatCoordinates_ = flatCoordinates;
    +    } else {
    +      this.orientedFlatCoordinates_ = flatCoordinates.slice();
    +      this.orientedFlatCoordinates_.length =
    +          ol.geom.flat.orient.orientLinearRingss(
    +              this.orientedFlatCoordinates_, 0, this.endss_, this.stride);
    +    }
    +    this.orientedRevision_ = this.getRevision();
    +  }
    +  return this.orientedFlatCoordinates_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.MultiPolygon.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  var simplifiedFlatCoordinates = [];
    +  var simplifiedEndss = [];
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizess(
    +      this.flatCoordinates, 0, this.endss_, this.stride,
    +      Math.sqrt(squaredTolerance),
    +      simplifiedFlatCoordinates, 0, simplifiedEndss);
    +  var simplifiedMultiPolygon = new ol.geom.MultiPolygon(null);
    +  simplifiedMultiPolygon.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEndss);
    +  return simplifiedMultiPolygon;
    +};
    +
    +
    +/**
    + * @param {number} index Index.
    + * @return {ol.geom.Polygon} Polygon.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getPolygon = function(index) {
    +  goog.asserts.assert(0 <= index && index < this.endss_.length);
    +  if (index < 0 || this.endss_.length <= index) {
    +    return null;
    +  }
    +  var offset;
    +  if (index === 0) {
    +    offset = 0;
    +  } else {
    +    var prevEnds = this.endss_[index - 1];
    +    offset = prevEnds[prevEnds.length - 1];
    +  }
    +  var ends = this.endss_[index].slice();
    +  var end = ends[ends.length - 1];
    +  if (offset !== 0) {
    +    var i, ii;
    +    for (i = 0, ii = ends.length; i < ii; ++i) {
    +      ends[i] -= offset;
    +    }
    +  }
    +  var polygon = new ol.geom.Polygon(null);
    +  polygon.setFlatCoordinates(
    +      this.layout, this.flatCoordinates.slice(offset, end), ends);
    +  return polygon;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.Polygon>} Polygons.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getPolygons = function() {
    +  var layout = this.layout;
    +  var flatCoordinates = this.flatCoordinates;
    +  var endss = this.endss_;
    +  var polygons = [];
    +  var offset = 0;
    +  var i, ii, j, jj;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    var ends = endss[i].slice();
    +    var end = ends[ends.length - 1];
    +    if (offset !== 0) {
    +      for (j = 0, jj = ends.length; j < jj; ++j) {
    +        ends[j] -= offset;
    +      }
    +    }
    +    var polygon = new ol.geom.Polygon(null);
    +    polygon.setFlatCoordinates(
    +        layout, flatCoordinates.slice(offset, end), ends);
    +    polygons.push(polygon);
    +    offset = end;
    +  }
    +  return polygons;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.getType = function() {
    +  return ol.geom.GeometryType.MULTI_POLYGON;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.MultiPolygon.prototype.intersectsExtent = function(extent) {
    +  return ol.geom.flat.intersectsextent.linearRingss(
    +      this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent);
    +};
    +
    +
    +/**
    + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.MultiPolygon.prototype.setCoordinates =
    +    function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.endss_);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 3);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    var endss = ol.geom.flat.deflate.coordinatesss(
    +        this.flatCoordinates, 0, coordinates, this.stride, this.endss_);
    +    if (endss.length === 0) {
    +      this.flatCoordinates.length = 0;
    +    } else {
    +      var lastEnds = endss[endss.length - 1];
    +      this.flatCoordinates.length = lastEnds.length === 0 ?
    +          0 : lastEnds[lastEnds.length - 1];
    +    }
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {Array.<Array.<number>>} endss Endss.
    + */
    +ol.geom.MultiPolygon.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates, endss) {
    +  goog.asserts.assert(!goog.isNull(endss));
    +  if (goog.isNull(flatCoordinates) || flatCoordinates.length === 0) {
    +    goog.asserts.assert(endss.length === 0);
    +  } else {
    +    goog.asserts.assert(endss.length > 0);
    +    var ends = endss[endss.length - 1];
    +    goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1]);
    +  }
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.endss_ = endss;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {Array.<ol.geom.Polygon>} polygons Polygons.
    + */
    +ol.geom.MultiPolygon.prototype.setPolygons = function(polygons) {
    +  var layout = ol.geom.GeometryLayout.XY;
    +  var flatCoordinates = [];
    +  var endss = [];
    +  var i, ii, ends;
    +  for (i = 0, ii = polygons.length; i < ii; ++i) {
    +    var polygon = polygons[i];
    +    if (i === 0) {
    +      layout = polygon.getLayout();
    +    } else {
    +      // FIXME better handle the case of non-matching layouts
    +      goog.asserts.assert(polygon.getLayout() == layout);
    +    }
    +    var offset = flatCoordinates.length;
    +    ends = polygon.getEnds();
    +    var j, jj;
    +    for (j = 0, jj = ends.length; j < jj; ++j) {
    +      ends[j] += offset;
    +    }
    +    goog.array.extend(flatCoordinates, polygon.getFlatCoordinates());
    +    endss.push(ends);
    +  }
    +  this.setFlatCoordinates(layout, flatCoordinates, endss);
    +};
    diff --git a/mapbox-gl/ol/ol/geom/point.js b/mapbox-gl/ol/ol/geom/point.js
    new file mode 100644
    index 0000000000..cbd895ab19
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/point.js
    @@ -0,0 +1,126 @@
    +goog.provide('ol.geom.Point');
    +
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.math');
    +
    +
    +
    +/**
    + * @classdesc
    + * Point geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {ol.Coordinate} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.Point = function(coordinates, opt_layout) {
    +  goog.base(this);
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +};
    +goog.inherits(ol.geom.Point, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.Point} Clone.
    + * @api stable
    + */
    +ol.geom.Point.prototype.clone = function() {
    +  var point = new ol.geom.Point(null);
    +  point.setFlatCoordinates(this.layout, this.flatCoordinates.slice());
    +  return point;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Point.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  var flatCoordinates = this.flatCoordinates;
    +  var squaredDistance = ol.math.squaredDistance(
    +      x, y, flatCoordinates[0], flatCoordinates[1]);
    +  if (squaredDistance < minSquaredDistance) {
    +    var stride = this.stride;
    +    var i;
    +    for (i = 0; i < stride; ++i) {
    +      closestPoint[i] = flatCoordinates[i];
    +    }
    +    closestPoint.length = stride;
    +    return squaredDistance;
    +  } else {
    +    return minSquaredDistance;
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate} Coordinates.
    + * @api stable
    + */
    +ol.geom.Point.prototype.getCoordinates = function() {
    +  return goog.isNull(this.flatCoordinates) ? [] : this.flatCoordinates.slice();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Point.prototype.computeExtent = function(extent) {
    +  return ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates, extent);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.Point.prototype.getType = function() {
    +  return ol.geom.GeometryType.POINT;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.Point.prototype.intersectsExtent = function(extent) {
    +  return ol.extent.containsXY(extent,
    +      this.flatCoordinates[0], this.flatCoordinates[1]);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.Point.prototype.setCoordinates = function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 0);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    this.flatCoordinates.length = ol.geom.flat.deflate.coordinate(
    +        this.flatCoordinates, 0, coordinates, this.stride);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + */
    +ol.geom.Point.prototype.setFlatCoordinates = function(layout, flatCoordinates) {
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/geom/polygon.js b/mapbox-gl/ol/ol/geom/polygon.js
    new file mode 100644
    index 0000000000..4eea4150e3
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/polygon.js
    @@ -0,0 +1,395 @@
    +goog.provide('ol.geom.Polygon');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LinearRing');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.SimpleGeometry');
    +goog.require('ol.geom.flat.area');
    +goog.require('ol.geom.flat.closest');
    +goog.require('ol.geom.flat.contains');
    +goog.require('ol.geom.flat.deflate');
    +goog.require('ol.geom.flat.inflate');
    +goog.require('ol.geom.flat.interiorpoint');
    +goog.require('ol.geom.flat.intersectsextent');
    +goog.require('ol.geom.flat.orient');
    +goog.require('ol.geom.flat.simplify');
    +
    +
    +
    +/**
    + * @classdesc
    + * Polygon geometry.
    + *
    + * @constructor
    + * @extends {ol.geom.SimpleGeometry}
    + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.Polygon = function(coordinates, opt_layout) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.ends_ = [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.flatInteriorPointRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.flatInteriorPoint_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDelta_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxDeltaRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.orientedRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.orientedFlatCoordinates_ = null;
    +
    +  this.setCoordinates(coordinates,
    +      /** @type {ol.geom.GeometryLayout|undefined} */ (opt_layout));
    +
    +};
    +goog.inherits(ol.geom.Polygon, ol.geom.SimpleGeometry);
    +
    +
    +/**
    + * @param {ol.geom.LinearRing} linearRing Linear ring.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.appendLinearRing = function(linearRing) {
    +  goog.asserts.assert(linearRing.getLayout() == this.layout);
    +  if (goog.isNull(this.flatCoordinates)) {
    +    this.flatCoordinates = linearRing.getFlatCoordinates().slice();
    +  } else {
    +    goog.array.extend(this.flatCoordinates, linearRing.getFlatCoordinates());
    +  }
    +  this.ends_.push(this.flatCoordinates.length);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Make a complete copy of the geometry.
    + * @return {!ol.geom.Polygon} Clone.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.clone = function() {
    +  var polygon = new ol.geom.Polygon(null);
    +  polygon.setFlatCoordinates(
    +      this.layout, this.flatCoordinates.slice(), this.ends_.slice());
    +  return polygon;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Polygon.prototype.closestPointXY =
    +    function(x, y, closestPoint, minSquaredDistance) {
    +  if (minSquaredDistance <
    +      ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) {
    +    return minSquaredDistance;
    +  }
    +  if (this.maxDeltaRevision_ != this.getRevision()) {
    +    this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta(
    +        this.flatCoordinates, 0, this.ends_, this.stride, 0));
    +    this.maxDeltaRevision_ = this.getRevision();
    +  }
    +  return ol.geom.flat.closest.getsClosestPoint(
    +      this.flatCoordinates, 0, this.ends_, this.stride,
    +      this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Polygon.prototype.containsXY = function(x, y) {
    +  return ol.geom.flat.contains.linearRingsContainsXY(
    +      this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);
    +};
    +
    +
    +/**
    + * @return {number} Area (on projected plane).
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getArea = function() {
    +  return ol.geom.flat.area.linearRings(
    +      this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<Array.<ol.Coordinate>>} Coordinates.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getCoordinates = function() {
    +  return ol.geom.flat.inflate.coordinatess(
    +      this.flatCoordinates, 0, this.ends_, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Ends.
    + */
    +ol.geom.Polygon.prototype.getEnds = function() {
    +  return this.ends_;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Interior point.
    + */
    +ol.geom.Polygon.prototype.getFlatInteriorPoint = function() {
    +  if (this.flatInteriorPointRevision_ != this.getRevision()) {
    +    var flatCenter = ol.extent.getCenter(this.getExtent());
    +    this.flatInteriorPoint_ = ol.geom.flat.interiorpoint.linearRings(
    +        this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride,
    +        flatCenter, 0);
    +    this.flatInteriorPointRevision_ = this.getRevision();
    +  }
    +  return this.flatInteriorPoint_;
    +};
    +
    +
    +/**
    + * @return {ol.geom.Point} Interior point.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getInteriorPoint = function() {
    +  return new ol.geom.Point(this.getFlatInteriorPoint());
    +};
    +
    +
    +/**
    + * Return the number of rings of the polygon,  this includes the exterior
    + * ring and any interior rings.
    + *
    + * @return {number} Number of rings.
    + * @api
    + */
    +ol.geom.Polygon.prototype.getLinearRingCount = function() {
    +  return this.ends_.length;
    +};
    +
    +
    +/**
    + * Return the Nth linear ring of the polygon geometry. Return `null` if the
    + * given index is out of range.
    + * The exterior linear ring is available at index `0` and the interior rings
    + * at index `1` and beyond.
    + *
    + * @param {number} index Index.
    + * @return {ol.geom.LinearRing} Linear ring.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getLinearRing = function(index) {
    +  goog.asserts.assert(0 <= index && index < this.ends_.length);
    +  if (index < 0 || this.ends_.length <= index) {
    +    return null;
    +  }
    +  var linearRing = new ol.geom.LinearRing(null);
    +  linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice(
    +      index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]));
    +  return linearRing;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.LinearRing>} Linear rings.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getLinearRings = function() {
    +  var layout = this.layout;
    +  var flatCoordinates = this.flatCoordinates;
    +  var ends = this.ends_;
    +  var linearRings = [];
    +  var offset = 0;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var linearRing = new ol.geom.LinearRing(null);
    +    linearRing.setFlatCoordinates(layout, flatCoordinates.slice(offset, end));
    +    linearRings.push(linearRing);
    +    offset = end;
    +  }
    +  return linearRings;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Oriented flat coordinates.
    + */
    +ol.geom.Polygon.prototype.getOrientedFlatCoordinates = function() {
    +  if (this.orientedRevision_ != this.getRevision()) {
    +    var flatCoordinates = this.flatCoordinates;
    +    if (ol.geom.flat.orient.linearRingsAreOriented(
    +        flatCoordinates, 0, this.ends_, this.stride)) {
    +      this.orientedFlatCoordinates_ = flatCoordinates;
    +    } else {
    +      this.orientedFlatCoordinates_ = flatCoordinates.slice();
    +      this.orientedFlatCoordinates_.length =
    +          ol.geom.flat.orient.orientLinearRings(
    +              this.orientedFlatCoordinates_, 0, this.ends_, this.stride);
    +    }
    +    this.orientedRevision_ = this.getRevision();
    +  }
    +  return this.orientedFlatCoordinates_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.Polygon.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  var simplifiedFlatCoordinates = [];
    +  var simplifiedEnds = [];
    +  simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizes(
    +      this.flatCoordinates, 0, this.ends_, this.stride,
    +      Math.sqrt(squaredTolerance),
    +      simplifiedFlatCoordinates, 0, simplifiedEnds);
    +  var simplifiedPolygon = new ol.geom.Polygon(null);
    +  simplifiedPolygon.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds);
    +  return simplifiedPolygon;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.getType = function() {
    +  return ol.geom.GeometryType.POLYGON;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.geom.Polygon.prototype.intersectsExtent = function(extent) {
    +  return ol.geom.flat.intersectsextent.linearRings(
    +      this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);
    +};
    +
    +
    +/**
    + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates.
    + * @param {ol.geom.GeometryLayout=} opt_layout Layout.
    + * @api stable
    + */
    +ol.geom.Polygon.prototype.setCoordinates = function(coordinates, opt_layout) {
    +  if (goog.isNull(coordinates)) {
    +    this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_);
    +  } else {
    +    this.setLayout(opt_layout, coordinates, 2);
    +    if (goog.isNull(this.flatCoordinates)) {
    +      this.flatCoordinates = [];
    +    }
    +    var ends = ol.geom.flat.deflate.coordinatess(
    +        this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
    +    this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {Array.<number>} ends Ends.
    + */
    +ol.geom.Polygon.prototype.setFlatCoordinates =
    +    function(layout, flatCoordinates, ends) {
    +  if (goog.isNull(flatCoordinates)) {
    +    goog.asserts.assert(!goog.isNull(ends) && ends.length === 0);
    +  } else if (ends.length === 0) {
    +    goog.asserts.assert(flatCoordinates.length === 0);
    +  } else {
    +    goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1]);
    +  }
    +  this.setFlatCoordinatesInternal(layout, flatCoordinates);
    +  this.ends_ = ends;
    +  this.changed();
    +};
    +
    +
    +/**
    + * Create an approximation of a circle on the surface of a sphere.
    + * @param {ol.Sphere} sphere The sphere.
    + * @param {ol.Coordinate} center Center (`[lon, lat]` in degrees).
    + * @param {number} radius The great-circle distance from the center to
    + *     the polygon vertices.
    + * @param {number=} opt_n Optional number of vertices for the resulting
    + *     polygon. Default is `32`.
    + * @return {ol.geom.Polygon} The "circular" polygon.
    + * @api stable
    + */
    +ol.geom.Polygon.circular = function(sphere, center, radius, opt_n) {
    +  var n = goog.isDef(opt_n) ? opt_n : 32;
    +  /** @type {Array.<number>} */
    +  var flatCoordinates = [];
    +  var i;
    +  for (i = 0; i < n; ++i) {
    +    goog.array.extend(
    +        flatCoordinates, sphere.offset(center, radius, 2 * Math.PI * i / n));
    +  }
    +  flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]);
    +  var polygon = new ol.geom.Polygon(null);
    +  polygon.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
    +  return polygon;
    +};
    +
    +
    +/**
    + * Create a polygon from an extent. The layout used is `XY`.
    + * @param {ol.Extent} extent The extent.
    + * @return {ol.geom.Polygon} The polygon.
    + * @api
    + */
    +ol.geom.Polygon.fromExtent = function(extent) {
    +  var minX = extent[0];
    +  var minY = extent[1];
    +  var maxX = extent[2];
    +  var maxY = extent[3];
    +  var flatCoordinates =
    +      [minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY];
    +  var polygon = new ol.geom.Polygon(null);
    +  polygon.setFlatCoordinates(
    +      ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
    +  return polygon;
    +};
    diff --git a/mapbox-gl/ol/ol/geom/simplegeometry.js b/mapbox-gl/ol/ol/geom/simplegeometry.js
    new file mode 100644
    index 0000000000..ccd82ea539
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/geom/simplegeometry.js
    @@ -0,0 +1,287 @@
    +goog.provide('ol.geom.SimpleGeometry');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('ol.extent');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.flat.transform');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; only used for creating subclasses; do not instantiate
    + * in apps, as cannot be rendered.
    + *
    + * @constructor
    + * @extends {ol.geom.Geometry}
    + * @api stable
    + */
    +ol.geom.SimpleGeometry = function() {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @protected
    +   * @type {ol.geom.GeometryLayout}
    +   */
    +  this.layout = ol.geom.GeometryLayout.XY;
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.stride = 2;
    +
    +  /**
    +   * @protected
    +   * @type {Array.<number>}
    +   */
    +  this.flatCoordinates = null;
    +
    +};
    +goog.inherits(ol.geom.SimpleGeometry, ol.geom.Geometry);
    +
    +
    +/**
    + * @param {number} stride Stride.
    + * @private
    + * @return {ol.geom.GeometryLayout} layout Layout.
    + */
    +ol.geom.SimpleGeometry.getLayoutForStride_ = function(stride) {
    +  if (stride == 2) {
    +    return ol.geom.GeometryLayout.XY;
    +  } else if (stride == 3) {
    +    return ol.geom.GeometryLayout.XYZ;
    +  } else if (stride == 4) {
    +    return ol.geom.GeometryLayout.XYZM;
    +  } else {
    +    goog.asserts.fail('unsupported stride: ' + stride);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @private
    + * @return {number} Stride.
    + */
    +ol.geom.SimpleGeometry.getStrideForLayout_ = function(layout) {
    +  if (layout == ol.geom.GeometryLayout.XY) {
    +    return 2;
    +  } else if (layout == ol.geom.GeometryLayout.XYZ) {
    +    return 3;
    +  } else if (layout == ol.geom.GeometryLayout.XYM) {
    +    return 3;
    +  } else if (layout == ol.geom.GeometryLayout.XYZM) {
    +    return 4;
    +  } else {
    +    goog.asserts.fail('unsupported layout: ' + layout);
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.SimpleGeometry.prototype.containsXY = goog.functions.FALSE;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.SimpleGeometry.prototype.computeExtent = function(extent) {
    +  return ol.extent.createOrUpdateFromFlatCoordinates(
    +      this.flatCoordinates, 0, this.flatCoordinates.length, this.stride,
    +      extent);
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate} First coordinate.
    + * @api stable
    + */
    +ol.geom.SimpleGeometry.prototype.getFirstCoordinate = function() {
    +  return this.flatCoordinates.slice(0, this.stride);
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Flat coordinates.
    + */
    +ol.geom.SimpleGeometry.prototype.getFlatCoordinates = function() {
    +  return this.flatCoordinates;
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate} Last point.
    + * @api stable
    + */
    +ol.geom.SimpleGeometry.prototype.getLastCoordinate = function() {
    +  return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);
    +};
    +
    +
    +/**
    + * @return {ol.geom.GeometryLayout} Layout.
    + * @api stable
    + */
    +ol.geom.SimpleGeometry.prototype.getLayout = function() {
    +  return this.layout;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.geom.SimpleGeometry.prototype.getSimplifiedGeometry =
    +    function(squaredTolerance) {
    +  if (this.simplifiedGeometryRevision != this.getRevision()) {
    +    goog.object.clear(this.simplifiedGeometryCache);
    +    this.simplifiedGeometryMaxMinSquaredTolerance = 0;
    +    this.simplifiedGeometryRevision = this.getRevision();
    +  }
    +  // If squaredTolerance is negative or if we know that simplification will not
    +  // have any effect then just return this.
    +  if (squaredTolerance < 0 ||
    +      (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 &&
    +       squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) {
    +    return this;
    +  }
    +  var key = squaredTolerance.toString();
    +  if (this.simplifiedGeometryCache.hasOwnProperty(key)) {
    +    return this.simplifiedGeometryCache[key];
    +  } else {
    +    var simplifiedGeometry =
    +        this.getSimplifiedGeometryInternal(squaredTolerance);
    +    var simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();
    +    if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {
    +      this.simplifiedGeometryCache[key] = simplifiedGeometry;
    +      return simplifiedGeometry;
    +    } else {
    +      // Simplification did not actually remove any coordinates.  We now know
    +      // that any calls to getSimplifiedGeometry with a squaredTolerance less
    +      // than or equal to the current squaredTolerance will also not have any
    +      // effect.  This allows us to short circuit simplification (saving CPU
    +      // cycles) and prevents the cache of simplified geometries from filling
    +      // up with useless identical copies of this geometry (saving memory).
    +      this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
    +      return this;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {ol.geom.SimpleGeometry} Simplified geometry.
    + * @protected
    + */
    +ol.geom.SimpleGeometry.prototype.getSimplifiedGeometryInternal =
    +    function(squaredTolerance) {
    +  return this;
    +};
    +
    +
    +/**
    + * @return {number} Stride.
    + */
    +ol.geom.SimpleGeometry.prototype.getStride = function() {
    +  return this.stride;
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout} layout Layout.
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @protected
    + */
    +ol.geom.SimpleGeometry.prototype.setFlatCoordinatesInternal =
    +    function(layout, flatCoordinates) {
    +  this.stride = ol.geom.SimpleGeometry.getStrideForLayout_(layout);
    +  this.layout = layout;
    +  this.flatCoordinates = flatCoordinates;
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryLayout|undefined} layout Layout.
    + * @param {Array} coordinates Coordinates.
    + * @param {number} nesting Nesting.
    + * @protected
    + */
    +ol.geom.SimpleGeometry.prototype.setLayout =
    +    function(layout, coordinates, nesting) {
    +  /** @type {number} */
    +  var stride;
    +  if (goog.isDef(layout)) {
    +    stride = ol.geom.SimpleGeometry.getStrideForLayout_(layout);
    +  } else {
    +    var i;
    +    for (i = 0; i < nesting; ++i) {
    +      if (coordinates.length === 0) {
    +        this.layout = ol.geom.GeometryLayout.XY;
    +        this.stride = 2;
    +        return;
    +      } else {
    +        coordinates = /** @type {Array} */ (coordinates[0]);
    +      }
    +    }
    +    stride = (/** @type {Array} */ (coordinates)).length;
    +    layout = ol.geom.SimpleGeometry.getLayoutForStride_(stride);
    +  }
    +  this.layout = layout;
    +  this.stride = stride;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.geom.SimpleGeometry.prototype.applyTransform = function(transformFn) {
    +  if (!goog.isNull(this.flatCoordinates)) {
    +    transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * Translate the geometry.
    + * @param {number} deltaX Delta X.
    + * @param {number} deltaY Delta Y.
    + * @api
    + */
    +ol.geom.SimpleGeometry.prototype.translate = function(deltaX, deltaY) {
    +  var flatCoordinates = this.getFlatCoordinates();
    +  if (!goog.isNull(flatCoordinates)) {
    +    var stride = this.getStride();
    +    ol.geom.flat.transform.translate(
    +        flatCoordinates, 0, flatCoordinates.length, stride,
    +        deltaX, deltaY, flatCoordinates);
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.geom.SimpleGeometry} simpleGeometry Simple geometry.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {Array.<number>=} opt_dest Destination.
    + * @return {Array.<number>} Transformed flat coordinates.
    + */
    +ol.geom.transformSimpleGeometry2D =
    +    function(simpleGeometry, transform, opt_dest) {
    +  var flatCoordinates = simpleGeometry.getFlatCoordinates();
    +  if (goog.isNull(flatCoordinates)) {
    +    return null;
    +  } else {
    +    var stride = simpleGeometry.getStride();
    +    return ol.geom.flat.transform.transform2D(
    +        flatCoordinates, 0, flatCoordinates.length, stride,
    +        transform, opt_dest);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/graticule.js b/mapbox-gl/ol/ol/graticule.js
    new file mode 100644
    index 0000000000..3b4fe7d54b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/graticule.js
    @@ -0,0 +1,435 @@
    +goog.provide('ol.Graticule');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('ol.extent');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.flat.geodesic');
    +goog.require('ol.proj');
    +goog.require('ol.render.EventType');
    +goog.require('ol.style.Stroke');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {olx.GraticuleOptions=} opt_options Options.
    + * @api
    + */
    +ol.Graticule = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @type {ol.Map}
    +   * @private
    +   */
    +  this.map_ = null;
    +
    +  /**
    +   * @type {ol.proj.Projection}
    +   * @private
    +   */
    +  this.projection_ = null;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxLat_ = Infinity;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxLon_ = Infinity;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.minLat_ = -Infinity;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.minLon_ = -Infinity;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.targetSize_ = goog.isDef(options.targetSize) ?
    +      options.targetSize : 100;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxLines_ = goog.isDef(options.maxLines) ? options.maxLines : 100;
    +  goog.asserts.assert(this.maxLines_ > 0);
    +
    +  /**
    +   * @type {Array.<ol.geom.LineString>}
    +   * @private
    +   */
    +  this.meridians_ = [];
    +
    +  /**
    +   * @type {Array.<ol.geom.LineString>}
    +   * @private
    +   */
    +  this.parallels_ = [];
    +
    +  /**
    +   * @type {ol.style.Stroke}
    +   * @private
    +   */
    +  this.strokeStyle_ = goog.isDef(options.strokeStyle) ?
    +      options.strokeStyle : ol.Graticule.DEFAULT_STROKE_STYLE_;
    +
    +  /**
    +   * @type {ol.TransformFunction|undefined}
    +   * @private
    +   */
    +  this.fromLonLatTransform_ = undefined;
    +
    +  /**
    +   * @type {ol.TransformFunction|undefined}
    +   * @private
    +   */
    +  this.toLonLatTransform_ = undefined;
    +
    +  /**
    +   * @type {ol.Coordinate}
    +   * @private
    +   */
    +  this.projectionCenterLonLat_ = null;
    +
    +  this.setMap(goog.isDef(options.map) ? options.map : null);
    +};
    +
    +
    +/**
    + * @type {ol.style.Stroke}
    + * @private
    + * @const
    + */
    +ol.Graticule.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({
    +  color: 'rgba(0,0,0,0.2)'
    +});
    +
    +
    +/**
    + * TODO can be configurable
    + * @type {Array.<number>}
    + * @private
    + */
    +ol.Graticule.intervals_ = [90, 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05,
    +  0.01, 0.005, 0.002, 0.001];
    +
    +
    +/**
    + * @param {number} lon Longitude.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} index Index.
    + * @return {number} Index.
    + * @private
    + */
    +ol.Graticule.prototype.addMeridian_ =
    +    function(lon, squaredTolerance, extent, index) {
    +  var lineString = this.getMeridian_(lon, squaredTolerance, index);
    +  if (ol.extent.intersects(lineString.getExtent(), extent)) {
    +    this.meridians_[index++] = lineString;
    +  }
    +  return index;
    +};
    +
    +
    +/**
    + * @param {number} lat Latitude.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} index Index.
    + * @return {number} Index.
    + * @private
    + */
    +ol.Graticule.prototype.addParallel_ =
    +    function(lat, squaredTolerance, extent, index) {
    +  var lineString = this.getParallel_(lat, squaredTolerance, index);
    +  if (ol.extent.intersects(lineString.getExtent(), extent)) {
    +    this.parallels_[index++] = lineString;
    +  }
    +  return index;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @private
    + */
    +ol.Graticule.prototype.createGraticule_ =
    +    function(extent, center, resolution, squaredTolerance) {
    +
    +  var interval = this.getInterval_(resolution);
    +  if (interval == -1) {
    +    this.meridians_.length = this.parallels_.length = 0;
    +    return;
    +  }
    +
    +  var centerLonLat = this.toLonLatTransform_(center);
    +  var centerLon = centerLonLat[0];
    +  var centerLat = centerLonLat[1];
    +  var maxLines = this.maxLines_;
    +  var cnt, idx, lat, lon;
    +
    +  // Create meridians
    +
    +  centerLon = Math.floor(centerLon / interval) * interval;
    +  lon = goog.math.clamp(centerLon, this.minLon_, this.maxLon_);
    +
    +  idx = this.addMeridian_(lon, squaredTolerance, extent, 0);
    +
    +  cnt = 0;
    +  while (lon != this.minLon_ && cnt++ < maxLines) {
    +    lon = Math.max(lon - interval, this.minLon_);
    +    idx = this.addMeridian_(lon, squaredTolerance, extent, idx);
    +  }
    +
    +  lon = goog.math.clamp(centerLon, this.minLon_, this.maxLon_);
    +
    +  cnt = 0;
    +  while (lon != this.maxLon_ && cnt++ < maxLines) {
    +    lon = Math.min(lon + interval, this.maxLon_);
    +    idx = this.addMeridian_(lon, squaredTolerance, extent, idx);
    +  }
    +
    +  this.meridians_.length = idx;
    +
    +  // Create parallels
    +
    +  centerLat = Math.floor(centerLat / interval) * interval;
    +  lat = goog.math.clamp(centerLat, this.minLat_, this.maxLat_);
    +
    +  idx = this.addParallel_(lat, squaredTolerance, extent, 0);
    +
    +  cnt = 0;
    +  while (lat != this.minLat_ && cnt++ < maxLines) {
    +    lat = Math.max(lat - interval, this.minLat_);
    +    idx = this.addParallel_(lat, squaredTolerance, extent, idx);
    +  }
    +
    +  lat = goog.math.clamp(centerLat, this.minLat_, this.maxLat_);
    +
    +  cnt = 0;
    +  while (lat != this.maxLat_ && cnt++ < maxLines) {
    +    lat = Math.min(lat + interval, this.maxLat_);
    +    idx = this.addParallel_(lat, squaredTolerance, extent, idx);
    +  }
    +
    +  this.parallels_.length = idx;
    +
    +};
    +
    +
    +/**
    + * @param {number} resolution Resolution.
    + * @return {number} The interval in degrees.
    + * @private
    + */
    +ol.Graticule.prototype.getInterval_ = function(resolution) {
    +  var centerLon = this.projectionCenterLonLat_[0];
    +  var centerLat = this.projectionCenterLonLat_[1];
    +  var interval = -1;
    +  var i, ii, delta, dist;
    +  var target = Math.pow(this.targetSize_ * resolution, 2);
    +  /** @type {Array.<number>} **/
    +  var p1 = [];
    +  /** @type {Array.<number>} **/
    +  var p2 = [];
    +  for (i = 0, ii = ol.Graticule.intervals_.length; i < ii; ++i) {
    +    delta = ol.Graticule.intervals_[i] / 2;
    +    p1[0] = centerLon - delta;
    +    p1[1] = centerLat - delta;
    +    p2[0] = centerLon + delta;
    +    p2[1] = centerLat + delta;
    +    this.fromLonLatTransform_(p1, p1);
    +    this.fromLonLatTransform_(p2, p2);
    +    dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2);
    +    if (dist <= target) {
    +      break;
    +    }
    +    interval = ol.Graticule.intervals_[i];
    +  }
    +  return interval;
    +};
    +
    +
    +/**
    + * @return {ol.Map} The map.
    + * @api
    + */
    +ol.Graticule.prototype.getMap = function() {
    +  return this.map_;
    +};
    +
    +
    +/**
    + * @param {number} lon Longitude.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {ol.geom.LineString} The meridian line string.
    + * @param {number} index Index.
    + * @private
    + */
    +ol.Graticule.prototype.getMeridian_ = function(lon, squaredTolerance, index) {
    +  goog.asserts.assert(lon >= this.minLon_);
    +  goog.asserts.assert(lon <= this.maxLon_);
    +  var flatCoordinates = ol.geom.flat.geodesic.meridian(lon,
    +      this.minLat_, this.maxLat_, this.projection_, squaredTolerance);
    +  goog.asserts.assert(flatCoordinates.length > 0);
    +  var lineString = goog.isDef(this.meridians_[index]) ?
    +      this.meridians_[index] : new ol.geom.LineString(null);
    +  lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
    +  return lineString;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.LineString>} The meridians.
    + * @api
    + */
    +ol.Graticule.prototype.getMeridians = function() {
    +  return this.meridians_;
    +};
    +
    +
    +/**
    + * @param {number} lat Latitude.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @return {ol.geom.LineString} The parallel line string.
    + * @param {number} index Index.
    + * @private
    + */
    +ol.Graticule.prototype.getParallel_ = function(lat, squaredTolerance, index) {
    +  goog.asserts.assert(lat >= this.minLat_);
    +  goog.asserts.assert(lat <= this.maxLat_);
    +  var flatCoordinates = ol.geom.flat.geodesic.parallel(lat,
    +      this.minLon_, this.maxLon_, this.projection_, squaredTolerance);
    +  goog.asserts.assert(flatCoordinates.length > 0);
    +  var lineString = goog.isDef(this.parallels_[index]) ?
    +      this.parallels_[index] : new ol.geom.LineString(null);
    +  lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates);
    +  return lineString;
    +};
    +
    +
    +/**
    + * @return {Array.<ol.geom.LineString>} The parallels.
    + * @api
    + */
    +ol.Graticule.prototype.getParallels = function() {
    +  return this.parallels_;
    +};
    +
    +
    +/**
    + * @param {ol.render.Event} e Event.
    + * @private
    + */
    +ol.Graticule.prototype.handlePostCompose_ = function(e) {
    +  var vectorContext = e.vectorContext;
    +  var frameState = e.frameState;
    +  var extent = frameState.extent;
    +  var viewState = frameState.viewState;
    +  var center = viewState.center;
    +  var projection = viewState.projection;
    +  var resolution = viewState.resolution;
    +  var pixelRatio = frameState.pixelRatio;
    +  var squaredTolerance =
    +      resolution * resolution / (4 * pixelRatio * pixelRatio);
    +
    +  var updateProjectionInfo = goog.isNull(this.projection_) ||
    +      !ol.proj.equivalent(this.projection_, projection);
    +
    +  if (updateProjectionInfo) {
    +    this.updateProjectionInfo_(projection);
    +  }
    +
    +  this.createGraticule_(extent, center, resolution, squaredTolerance);
    +
    +  // Draw the lines
    +  vectorContext.setFillStrokeStyle(null, this.strokeStyle_);
    +  var i, l, line;
    +  for (i = 0, l = this.meridians_.length; i < l; ++i) {
    +    line = this.meridians_[i];
    +    vectorContext.drawLineStringGeometry(line, null);
    +  }
    +  for (i = 0, l = this.parallels_.length; i < l; ++i) {
    +    line = this.parallels_[i];
    +    vectorContext.drawLineStringGeometry(line, null);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.proj.Projection} projection Projection.
    + * @private
    + */
    +ol.Graticule.prototype.updateProjectionInfo_ = function(projection) {
    +  goog.asserts.assert(!goog.isNull(projection));
    +
    +  var extent = projection.getExtent();
    +  var worldExtent = projection.getWorldExtent();
    +  var maxLat = worldExtent[3];
    +  var maxLon = worldExtent[2];
    +  var minLat = worldExtent[1];
    +  var minLon = worldExtent[0];
    +
    +  goog.asserts.assert(!goog.isNull(extent));
    +  goog.asserts.assert(goog.isDef(maxLat));
    +  goog.asserts.assert(goog.isDef(maxLon));
    +  goog.asserts.assert(goog.isDef(minLat));
    +  goog.asserts.assert(goog.isDef(minLon));
    +
    +  this.maxLat_ = maxLat;
    +  this.maxLon_ = maxLon;
    +  this.minLat_ = minLat;
    +  this.minLon_ = minLon;
    +
    +  var epsg4326Projection = ol.proj.get('EPSG:4326');
    +
    +  this.fromLonLatTransform_ = ol.proj.getTransform(
    +      epsg4326Projection, projection);
    +
    +  this.toLonLatTransform_ = ol.proj.getTransform(
    +      projection, epsg4326Projection);
    +
    +  this.projectionCenterLonLat_ = this.toLonLatTransform_(
    +      ol.extent.getCenter(extent));
    +
    +  this.projection_ = projection;
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @api
    + */
    +ol.Graticule.prototype.setMap = function(map) {
    +  if (!goog.isNull(this.map_)) {
    +    this.map_.un(ol.render.EventType.POSTCOMPOSE,
    +        this.handlePostCompose_, this);
    +    this.map_.render();
    +  }
    +  if (!goog.isNull(map)) {
    +    map.on(ol.render.EventType.POSTCOMPOSE,
    +        this.handlePostCompose_, this);
    +    map.render();
    +  }
    +  this.map_ = map;
    +};
    diff --git a/mapbox-gl/ol/ol/has.js b/mapbox-gl/ol/ol/has.js
    new file mode 100644
    index 0000000000..f86249f976
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/has.js
    @@ -0,0 +1,150 @@
    +goog.provide('ol.has');
    +
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('ol');
    +goog.require('ol.dom');
    +goog.require('ol.webgl');
    +
    +
    +/**
    + * The ratio between physical pixels and device-independent pixels
    + * (dips) on the device (`window.devicePixelRatio`).
    + * @const
    + * @type {number}
    + * @api stable
    + */
    +ol.has.DEVICE_PIXEL_RATIO = goog.global.devicePixelRatio || 1;
    +
    +
    +/**
    + * True if the browser supports ArrayBuffers.
    + * @const
    + * @type {boolean}
    + */
    +ol.has.ARRAY_BUFFER = 'ArrayBuffer' in goog.global;
    +
    +
    +/**
    + * True if the browser's Canvas implementation implements {get,set}LineDash.
    + * @type {boolean}
    + */
    +ol.has.CANVAS_LINE_DASH = false;
    +
    +
    +/**
    + * True if browser supports Canvas.
    + * @const
    + * @type {boolean}
    + * @api stable
    + */
    +ol.has.CANVAS = ol.ENABLE_CANVAS && (
    +    /**
    +     * @return {boolean} Canvas supported.
    +     */
    +    function() {
    +      if (!('HTMLCanvasElement' in goog.global)) {
    +        return false;
    +      }
    +      try {
    +        var context = ol.dom.createCanvasContext2D();
    +        if (goog.isNull(context)) {
    +          return false;
    +        } else {
    +          if (goog.isDef(context.setLineDash)) {
    +            ol.has.CANVAS_LINE_DASH = true;
    +          }
    +          return true;
    +        }
    +      } catch (e) {
    +        return false;
    +      }
    +    })();
    +
    +
    +/**
    + * Indicates if DeviceOrientation is supported in the user's browser.
    + * @const
    + * @type {boolean}
    + * @api stable
    + */
    +ol.has.DEVICE_ORIENTATION = 'DeviceOrientationEvent' in goog.global;
    +
    +
    +/**
    + * True if browser supports DOM.
    + * @const
    + * @type {boolean}
    + */
    +ol.has.DOM = ol.ENABLE_DOM;
    +
    +
    +/**
    + * Is HTML5 geolocation supported in the current browser?
    + * @const
    + * @type {boolean}
    + * @api stable
    + */
    +ol.has.GEOLOCATION = 'geolocation' in goog.global.navigator;
    +
    +
    +/**
    + * True if browser supports touch events.
    + * @const
    + * @type {boolean}
    + * @api stable
    + */
    +ol.has.TOUCH = ol.ASSUME_TOUCH || 'ontouchstart' in goog.global;
    +
    +
    +/**
    + * True if browser supports pointer events.
    + * @const
    + * @type {boolean}
    + */
    +ol.has.POINTER = 'PointerEvent' in goog.global;
    +
    +
    +/**
    + * True if browser supports ms pointer events (IE 10).
    + * @const
    + * @type {boolean}
    + */
    +ol.has.MSPOINTER = !!(goog.global.navigator.msPointerEnabled);
    +
    +
    +/**
    + * True if browser supports WebGL.
    + * @const
    + * @type {boolean}
    + * @api stable
    + */
    +ol.has.WEBGL;
    +
    +
    +(function() {
    +  if (ol.ENABLE_WEBGL) {
    +    var hasWebGL = false;
    +    var textureSize;
    +    var /** @type {Array.<string>} */ extensions = [];
    +
    +    if ('WebGLRenderingContext' in goog.global) {
    +      try {
    +        var canvas = /** @type {HTMLCanvasElement} */
    +            (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +        var gl = ol.webgl.getContext(canvas, {
    +          failIfMajorPerformanceCaveat: true
    +        });
    +        if (!goog.isNull(gl)) {
    +          hasWebGL = true;
    +          textureSize = /** @type {number} */
    +              (gl.getParameter(gl.MAX_TEXTURE_SIZE));
    +          extensions = gl.getSupportedExtensions();
    +        }
    +      } catch (e) {}
    +    }
    +    ol.has.WEBGL = hasWebGL;
    +    ol.WEBGL_EXTENSIONS = extensions;
    +    ol.WEBGL_MAX_TEXTURE_SIZE = textureSize;
    +  }
    +})();
    diff --git a/mapbox-gl/ol/ol/has.jsdoc b/mapbox-gl/ol/ol/has.jsdoc
    new file mode 100644
    index 0000000000..ca1dca69ab
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/has.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.has
    + */
    diff --git a/mapbox-gl/ol/ol/image.js b/mapbox-gl/ol/ol/image.js
    new file mode 100644
    index 0000000000..f2492ceaa8
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/image.js
    @@ -0,0 +1,152 @@
    +goog.provide('ol.Image');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.ImageBase');
    +goog.require('ol.ImageState');
    +goog.require('ol.extent');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.ImageBase}
    + * @param {ol.Extent} extent Extent.
    + * @param {number|undefined} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {Array.<ol.Attribution>} attributions Attributions.
    + * @param {string} src Image source URI.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
    + */
    +ol.Image = function(extent, resolution, pixelRatio, attributions, src,
    +    crossOrigin, imageLoadFunction) {
    +
    +  goog.base(this, extent, resolution, pixelRatio, ol.ImageState.IDLE,
    +      attributions);
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.src_ = src;
    +
    +  /**
    +   * @private
    +   * @type {Image}
    +   */
    +  this.image_ = new Image();
    +  if (!goog.isNull(crossOrigin)) {
    +    this.image_.crossOrigin = crossOrigin;
    +  }
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, Image>}
    +   */
    +  this.imageByContext_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<goog.events.Key>}
    +   */
    +  this.imageListenerKeys_ = null;
    +
    +  /**
    +   * @protected
    +   * @type {ol.ImageState}
    +   */
    +  this.state = ol.ImageState.IDLE;
    +
    +  /**
    +   * @private
    +   * @type {ol.ImageLoadFunctionType}
    +   */
    +  this.imageLoadFunction_ = imageLoadFunction;
    +
    +};
    +goog.inherits(ol.Image, ol.ImageBase);
    +
    +
    +/**
    + * @param {Object=} opt_context Object.
    + * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image.
    + * @api
    + */
    +ol.Image.prototype.getImage = function(opt_context) {
    +  if (goog.isDef(opt_context)) {
    +    var image;
    +    var key = goog.getUid(opt_context);
    +    if (key in this.imageByContext_) {
    +      return this.imageByContext_[key];
    +    } else if (goog.object.isEmpty(this.imageByContext_)) {
    +      image = this.image_;
    +    } else {
    +      image = /** @type {Image} */ (this.image_.cloneNode(false));
    +    }
    +    this.imageByContext_[key] = image;
    +    return image;
    +  } else {
    +    return this.image_;
    +  }
    +};
    +
    +
    +/**
    + * Tracks loading or read errors.
    + *
    + * @private
    + */
    +ol.Image.prototype.handleImageError_ = function() {
    +  this.state = ol.ImageState.ERROR;
    +  this.unlistenImage_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * Tracks successful image load.
    + *
    + * @private
    + */
    +ol.Image.prototype.handleImageLoad_ = function() {
    +  if (!goog.isDef(this.resolution)) {
    +    this.resolution = ol.extent.getHeight(this.extent) / this.image_.height;
    +  }
    +  this.state = ol.ImageState.LOADED;
    +  this.unlistenImage_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.Image.prototype.load = function() {
    +  if (this.state == ol.ImageState.IDLE) {
    +    this.state = ol.ImageState.LOADING;
    +    goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
    +    this.imageListenerKeys_ = [
    +      goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
    +          this.handleImageError_, false, this),
    +      goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
    +          this.handleImageLoad_, false, this)
    +    ];
    +    this.imageLoadFunction_(this, this.src_);
    +  }
    +};
    +
    +
    +/**
    + * Discards event handlers which listen for load completion or errors.
    + *
    + * @private
    + */
    +ol.Image.prototype.unlistenImage_ = function() {
    +  goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
    +  goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
    +  this.imageListenerKeys_ = null;
    +};
    diff --git a/mapbox-gl/ol/ol/imagebase.js b/mapbox-gl/ol/ol/imagebase.js
    new file mode 100644
    index 0000000000..5e0a464fbb
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/imagebase.js
    @@ -0,0 +1,129 @@
    +goog.provide('ol.ImageBase');
    +goog.provide('ol.ImageState');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('ol.Attribution');
    +goog.require('ol.Extent');
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.ImageState = {
    +  IDLE: 0,
    +  LOADING: 1,
    +  LOADED: 2,
    +  ERROR: 3
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @param {ol.Extent} extent Extent.
    + * @param {number|undefined} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.ImageState} state State.
    + * @param {Array.<ol.Attribution>} attributions Attributions.
    + */
    +ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.Attribution>}
    +   */
    +  this.attributions_ = attributions;
    +
    +  /**
    +   * @protected
    +   * @type {ol.Extent}
    +   */
    +  this.extent = extent;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.pixelRatio_ = pixelRatio;
    +
    +  /**
    +   * @protected
    +   * @type {number|undefined}
    +   */
    +  this.resolution = resolution;
    +
    +  /**
    +   * @protected
    +   * @type {ol.ImageState}
    +   */
    +  this.state = state;
    +
    +};
    +goog.inherits(ol.ImageBase, goog.events.EventTarget);
    +
    +
    +/**
    + * @protected
    + */
    +ol.ImageBase.prototype.changed = function() {
    +  this.dispatchEvent(goog.events.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * @return {Array.<ol.Attribution>} Attributions.
    + */
    +ol.ImageBase.prototype.getAttributions = function() {
    +  return this.attributions_;
    +};
    +
    +
    +/**
    + * @return {ol.Extent} Extent.
    + */
    +ol.ImageBase.prototype.getExtent = function() {
    +  return this.extent;
    +};
    +
    +
    +/**
    + * @param {Object=} opt_context Object.
    + * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image.
    + */
    +ol.ImageBase.prototype.getImage = goog.abstractMethod;
    +
    +
    +/**
    + * @return {number} PixelRatio.
    + */
    +ol.ImageBase.prototype.getPixelRatio = function() {
    +  return this.pixelRatio_;
    +};
    +
    +
    +/**
    + * @return {number} Resolution.
    + */
    +ol.ImageBase.prototype.getResolution = function() {
    +  goog.asserts.assert(goog.isDef(this.resolution), 'resolution not yet set');
    +  return this.resolution;
    +};
    +
    +
    +/**
    + * @return {ol.ImageState} State.
    + */
    +ol.ImageBase.prototype.getState = function() {
    +  return this.state;
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.ImageBase.prototype.load = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/imagecanvas.js b/mapbox-gl/ol/ol/imagecanvas.js
    new file mode 100644
    index 0000000000..6cc9b7295b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/imagecanvas.js
    @@ -0,0 +1,38 @@
    +goog.provide('ol.ImageCanvas');
    +
    +goog.require('ol.ImageBase');
    +goog.require('ol.ImageState');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.ImageBase}
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {Array.<ol.Attribution>} attributions Attributions.
    + * @param {HTMLCanvasElement} canvas Canvas.
    + */
    +ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
    +    canvas) {
    +
    +  goog.base(this, extent, resolution, pixelRatio, ol.ImageState.LOADED,
    +      attributions);
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = canvas;
    +
    +};
    +goog.inherits(ol.ImageCanvas, ol.ImageBase);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.ImageCanvas.prototype.getImage = function(opt_context) {
    +  return this.canvas_;
    +};
    diff --git a/mapbox-gl/ol/ol/imageloadfunction.js b/mapbox-gl/ol/ol/imageloadfunction.js
    new file mode 100644
    index 0000000000..0d4b74cda2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/imageloadfunction.js
    @@ -0,0 +1,21 @@
    +goog.provide('ol.ImageLoadFunctionType');
    +
    +
    +/**
    + * A function that takes an {@link ol.Image} for the image and a `{string}` for
    + * the src as arguments. It is supposed to make it so the underlying image
    + * {@link ol.Image#getImage} is assigned the content specified by the src. If
    + * not specified, the default is
    + *
    + *     function(image, src) {
    + *       image.getImage().src = src;
    + *     }
    + *
    + * Providing a custom `imageLoadFunction` can be useful to load images with
    + * post requests or - in general - through XHR requests, where the src of the
    + * image element would be set to a data URI when the content is loaded.
    + *
    + * @typedef {function(ol.Image, string)}
    + * @api
    + */
    +ol.ImageLoadFunctionType;
    diff --git a/mapbox-gl/ol/ol/imagetile.js b/mapbox-gl/ol/ol/imagetile.js
    new file mode 100644
    index 0000000000..1baec02742
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/imagetile.js
    @@ -0,0 +1,170 @@
    +goog.provide('ol.ImageTile');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.Tile');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileLoadFunctionType');
    +goog.require('ol.TileState');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.Tile}
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.TileState} state State.
    + * @param {string} src Image source URI.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
    + */
    +ol.ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction) {
    +
    +  goog.base(this, tileCoord, state);
    +
    +  /**
    +   * Image URI
    +   *
    +   * @private
    +   * @type {string}
    +   */
    +  this.src_ = src;
    +
    +  /**
    +   * @private
    +   * @type {Image}
    +   */
    +  this.image_ = new Image();
    +  if (!goog.isNull(crossOrigin)) {
    +    this.image_.crossOrigin = crossOrigin;
    +  }
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, Image>}
    +   */
    +  this.imageByContext_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<goog.events.Key>}
    +   */
    +  this.imageListenerKeys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.TileLoadFunctionType}
    +   */
    +  this.tileLoadFunction_ = tileLoadFunction;
    +
    +};
    +goog.inherits(ol.ImageTile, ol.Tile);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.ImageTile.prototype.disposeInternal = function() {
    +  this.unlistenImage_();
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.ImageTile.prototype.getImage = function(opt_context) {
    +  if (goog.isDef(opt_context)) {
    +    var image;
    +    var key = goog.getUid(opt_context);
    +    if (key in this.imageByContext_) {
    +      return this.imageByContext_[key];
    +    } else if (goog.object.isEmpty(this.imageByContext_)) {
    +      image = this.image_;
    +    } else {
    +      image = /** @type {Image} */ (this.image_.cloneNode(false));
    +    }
    +    this.imageByContext_[key] = image;
    +    return image;
    +  } else {
    +    return this.image_;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.ImageTile.prototype.getKey = function() {
    +  return this.src_;
    +};
    +
    +
    +/**
    + * Tracks loading or read errors.
    + *
    + * @private
    + */
    +ol.ImageTile.prototype.handleImageError_ = function() {
    +  this.state = ol.TileState.ERROR;
    +  this.unlistenImage_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * Tracks successful image load.
    + *
    + * @private
    + */
    +ol.ImageTile.prototype.handleImageLoad_ = function() {
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    if (!goog.isDef(this.image_.naturalWidth)) {
    +      this.image_.naturalWidth = this.image_.width;
    +      this.image_.naturalHeight = this.image_.height;
    +    }
    +  }
    +
    +  if (this.image_.naturalWidth && this.image_.naturalHeight) {
    +    this.state = ol.TileState.LOADED;
    +  } else {
    +    this.state = ol.TileState.EMPTY;
    +  }
    +  this.unlistenImage_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.ImageTile.prototype.load = function() {
    +  if (this.state == ol.TileState.IDLE) {
    +    this.state = ol.TileState.LOADING;
    +    this.changed();
    +    goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
    +    this.imageListenerKeys_ = [
    +      goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
    +          this.handleImageError_, false, this),
    +      goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
    +          this.handleImageLoad_, false, this)
    +    ];
    +    this.tileLoadFunction_(this, this.src_);
    +  }
    +};
    +
    +
    +/**
    + * Discards event handlers which listen for load completion or errors.
    + *
    + * @private
    + */
    +ol.ImageTile.prototype.unlistenImage_ = function() {
    +  goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
    +  goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
    +  this.imageListenerKeys_ = null;
    +};
    diff --git a/mapbox-gl/ol/ol/imageurlfunction.js b/mapbox-gl/ol/ol/imageurlfunction.js
    new file mode 100644
    index 0000000000..fe52c718c0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/imageurlfunction.js
    @@ -0,0 +1,46 @@
    +goog.provide('ol.ImageUrlFunction');
    +goog.provide('ol.ImageUrlFunctionType');
    +
    +goog.require('ol.Size');
    +
    +
    +/**
    + * @typedef {function(this:ol.source.Image, ol.Extent, ol.Size,
    + *     ol.proj.Projection): (string|undefined)}
    + */
    +ol.ImageUrlFunctionType;
    +
    +
    +/**
    + * @param {string} baseUrl Base URL (may have query data).
    + * @param {Object.<string,*>} params to encode in the URL.
    + * @param {function(string, Object.<string,*>, ol.Extent, ol.Size,
    + *     ol.proj.Projection): (string|undefined)} paramsFunction params function.
    + * @return {ol.ImageUrlFunctionType} Image URL function.
    + */
    +ol.ImageUrlFunction.createFromParamsFunction =
    +    function(baseUrl, params, paramsFunction) {
    +  return (
    +      /**
    +       * @this {ol.source.Image}
    +       * @param {ol.Extent} extent Extent.
    +       * @param {ol.Size} size Size.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @return {string|undefined} URL.
    +       */
    +      function(extent, size, projection) {
    +        return paramsFunction(baseUrl, params, extent, size, projection);
    +      });
    +};
    +
    +
    +/**
    + * @this {ol.source.Image}
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Size} size Size.
    + * @return {string|undefined} Image URL.
    + */
    +ol.ImageUrlFunction.nullImageUrlFunction =
    +    function(extent, size) {
    +  return undefined;
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/doubleclickzoominteraction.js b/mapbox-gl/ol/ol/interaction/doubleclickzoominteraction.js
    new file mode 100644
    index 0000000000..a01d7ba25c
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/doubleclickzoominteraction.js
    @@ -0,0 +1,64 @@
    +goog.provide('ol.interaction.DoubleClickZoom');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.MapBrowserEvent');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.interaction.Interaction');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom by double-clicking on the map.
    + *
    + * @constructor
    + * @extends {ol.interaction.Interaction}
    + * @param {olx.interaction.DoubleClickZoomOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DoubleClickZoom = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.delta_ = goog.isDef(options.delta) ? options.delta : 1;
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.DoubleClickZoom.handleEvent
    +  });
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 250;
    +
    +};
    +goog.inherits(ol.interaction.DoubleClickZoom, ol.interaction.Interaction);
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.DoubleClickZoom}
    + * @api
    + */
    +ol.interaction.DoubleClickZoom.handleEvent = function(mapBrowserEvent) {
    +  var stopEvent = false;
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK) {
    +    var map = mapBrowserEvent.map;
    +    var anchor = mapBrowserEvent.coordinate;
    +    var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;
    +    var view = map.getView();
    +    goog.asserts.assert(!goog.isNull(view));
    +    ol.interaction.Interaction.zoomByDelta(
    +        map, view, delta, anchor, this.duration_);
    +    mapBrowserEvent.preventDefault();
    +    stopEvent = true;
    +  }
    +  return !stopEvent;
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/draganddropinteraction.js b/mapbox-gl/ol/ol/interaction/draganddropinteraction.js
    new file mode 100644
    index 0000000000..3769fa094b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/draganddropinteraction.js
    @@ -0,0 +1,236 @@
    +// FIXME should handle all geo-referenced data, not just vector data
    +
    +goog.provide('ol.interaction.DragAndDrop');
    +goog.provide('ol.interaction.DragAndDropEvent');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.FileDropHandler');
    +goog.require('goog.events.FileDropHandler.EventType');
    +goog.require('goog.fs.FileReader');
    +goog.require('goog.functions');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.proj');
    +
    +
    +
    +/**
    + * @classdesc
    + * Handles input of vector data by drag and drop.
    + *
    + * @constructor
    + * @extends {ol.interaction.Interaction}
    + * @fires ol.interaction.DragAndDropEvent
    + * @param {olx.interaction.DragAndDropOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragAndDrop = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.DragAndDrop.handleEvent
    +  });
    +
    +  /**
    +   * @private
    +   * @type {Array.<function(new: ol.format.Feature)>}
    +   */
    +  this.formatConstructors_ = goog.isDef(options.formatConstructors) ?
    +      options.formatConstructors : [];
    +
    +  /**
    +   * @private
    +   * @type {ol.proj.Projection}
    +   */
    +  this.projection_ = goog.isDef(options.projection) ?
    +      ol.proj.get(options.projection) : null;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.FileDropHandler}
    +   */
    +  this.fileDropHandler_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key|undefined}
    +   */
    +  this.dropListenKey_ = undefined;
    +
    +};
    +goog.inherits(ol.interaction.DragAndDrop, ol.interaction.Interaction);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.DragAndDrop.prototype.disposeInternal = function() {
    +  if (goog.isDef(this.dropListenKey_)) {
    +    goog.events.unlistenByKey(this.dropListenKey_);
    +  }
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} event Event.
    + * @private
    + */
    +ol.interaction.DragAndDrop.prototype.handleDrop_ = function(event) {
    +  var files = event.getBrowserEvent().dataTransfer.files;
    +  var i, ii, file;
    +  for (i = 0, ii = files.length; i < ii; ++i) {
    +    file = files[i];
    +    // The empty string param is a workaround for
    +    // https://code.google.com/p/closure-library/issues/detail?id=524
    +    var reader = goog.fs.FileReader.readAsText(file, '');
    +    reader.addCallback(goog.partial(this.handleResult_, file), this);
    +  }
    +};
    +
    +
    +/**
    + * @param {File} file File.
    + * @param {string} result Result.
    + * @private
    + */
    +ol.interaction.DragAndDrop.prototype.handleResult_ = function(file, result) {
    +  var map = this.getMap();
    +  goog.asserts.assert(!goog.isNull(map));
    +  var projection = this.projection_;
    +  if (goog.isNull(projection)) {
    +    var view = map.getView();
    +    goog.asserts.assert(!goog.isNull(view));
    +    projection = view.getProjection();
    +    goog.asserts.assert(goog.isDef(projection));
    +  }
    +  var formatConstructors = this.formatConstructors_;
    +  var features = [];
    +  var i, ii;
    +  for (i = 0, ii = formatConstructors.length; i < ii; ++i) {
    +    var formatConstructor = formatConstructors[i];
    +    var format = new formatConstructor();
    +    var readFeatures = this.tryReadFeatures_(format, result);
    +    if (!goog.isNull(readFeatures)) {
    +      var featureProjection = format.readProjection(result);
    +      var transform = ol.proj.getTransform(featureProjection, projection);
    +      var j, jj;
    +      for (j = 0, jj = readFeatures.length; j < jj; ++j) {
    +        var feature = readFeatures[j];
    +        var geometry = feature.getGeometry();
    +        if (goog.isDefAndNotNull(geometry)) {
    +          geometry.applyTransform(transform);
    +        }
    +        features.push(feature);
    +      }
    +    }
    +  }
    +  this.dispatchEvent(
    +      new ol.interaction.DragAndDropEvent(
    +          ol.interaction.DragAndDropEventType.ADD_FEATURES, this, file,
    +          features, projection));
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.DragAndDrop}
    + * @api
    + */
    +ol.interaction.DragAndDrop.handleEvent = goog.functions.TRUE;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.DragAndDrop.prototype.setMap = function(map) {
    +  if (goog.isDef(this.dropListenKey_)) {
    +    goog.events.unlistenByKey(this.dropListenKey_);
    +    this.dropListenKey_ = undefined;
    +  }
    +  if (!goog.isNull(this.fileDropHandler_)) {
    +    goog.dispose(this.fileDropHandler_);
    +    this.fileDropHandler_ = null;
    +  }
    +  goog.asserts.assert(!goog.isDef(this.dropListenKey_));
    +  goog.base(this, 'setMap', map);
    +  if (!goog.isNull(map)) {
    +    this.fileDropHandler_ = new goog.events.FileDropHandler(map.getViewport());
    +    this.dropListenKey_ = goog.events.listen(
    +        this.fileDropHandler_, goog.events.FileDropHandler.EventType.DROP,
    +        this.handleDrop_, false, this);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.format.Feature} format Format.
    + * @param {string} text Text.
    + * @private
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.interaction.DragAndDrop.prototype.tryReadFeatures_ = function(format, text) {
    +  try {
    +    return format.readFeatures(text);
    +  } catch (e) {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.interaction.DragAndDropEventType = {
    +  /**
    +   * Triggered when features are added
    +   * @event ol.interaction.DragAndDropEvent#addfeatures
    +   * @api stable
    +   */
    +  ADD_FEATURES: 'addfeatures'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.interaction.DragAndDrop} instances are instances
    + * of this type.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.interaction.DragAndDropEvent}
    + * @param {ol.interaction.DragAndDropEventType} type Type.
    + * @param {Object} target Target.
    + * @param {File} file File.
    + * @param {Array.<ol.Feature>=} opt_features Features.
    + * @param {ol.proj.Projection=} opt_projection Projection.
    + */
    +ol.interaction.DragAndDropEvent =
    +    function(type, target, file, opt_features, opt_projection) {
    +
    +  goog.base(this, type, target);
    +
    +  /**
    +   * @type {Array.<ol.Feature>|undefined}
    +   * @api stable
    +   */
    +  this.features = opt_features;
    +
    +  /**
    +   * @type {File}
    +   * @api stable
    +   */
    +  this.file = file;
    +
    +  /**
    +   * @type {ol.proj.Projection|undefined}
    +   * @api
    +   */
    +  this.projection = opt_projection;
    +
    +};
    +goog.inherits(ol.interaction.DragAndDropEvent, goog.events.Event);
    diff --git a/mapbox-gl/ol/ol/interaction/dragboxinteraction.js b/mapbox-gl/ol/ol/interaction/dragboxinteraction.js
    new file mode 100644
    index 0000000000..9438dca7dc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/dragboxinteraction.js
    @@ -0,0 +1,206 @@
    +// FIXME draw drag box
    +goog.provide('ol.DragBoxEvent');
    +goog.provide('ol.interaction.DragBox');
    +
    +goog.require('goog.events.Event');
    +goog.require('ol');
    +goog.require('ol.events.ConditionType');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Pointer');
    +goog.require('ol.render.Box');
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED =
    +    ol.DRAG_BOX_HYSTERESIS_PIXELS *
    +    ol.DRAG_BOX_HYSTERESIS_PIXELS;
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.DragBoxEventType = {
    +  /**
    +   * Triggered upon drag box start.
    +   * @event ol.DragBoxEvent#boxstart
    +   * @api stable
    +   */
    +  BOXSTART: 'boxstart',
    +  /**
    +   * Triggered upon drag box end.
    +   * @event ol.DragBoxEvent#boxend
    +   * @api stable
    +   */
    +  BOXEND: 'boxend'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.interaction.DragBox} instances are instances of
    + * this type.
    + *
    + * @param {string} type The event type.
    + * @param {ol.Coordinate} coordinate The event coordinate.
    + * @extends {goog.events.Event}
    + * @constructor
    + * @implements {oli.DragBoxEvent}
    + */
    +ol.DragBoxEvent = function(type, coordinate) {
    +  goog.base(this, type);
    +
    +  /**
    +   * The coordinate of the drag event.
    +   * @const
    +   * @type {ol.Coordinate}
    +   * @api stable
    +   */
    +  this.coordinate = coordinate;
    +
    +};
    +goog.inherits(ol.DragBoxEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to draw a vector box by clicking and dragging on the map,
    + * normally combined with an {@link ol.events.condition} that limits
    + * it to when the shift or other key is held down. This is used, for example,
    + * for zooming to a specific area of the map
    + * (see {@link ol.interaction.DragZoom} and
    + * {@link ol.interaction.DragRotateAndZoom}).
    + *
    + * This interaction is only supported for mouse devices.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @fires ol.DragBoxEvent
    + * @param {olx.interaction.DragBoxOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragBox = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.DragBox.handleDownEvent_,
    +    handleDragEvent: ol.interaction.DragBox.handleDragEvent_,
    +    handleUpEvent: ol.interaction.DragBox.handleUpEvent_
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Style}
    +   */
    +  var style = goog.isDef(options.style) ? options.style : null;
    +
    +  /**
    +   * @type {ol.render.Box}
    +   * @private
    +   */
    +  this.box_ = new ol.render.Box(style);
    +
    +  /**
    +   * @type {ol.Pixel}
    +   * @private
    +   */
    +  this.startPixel_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.always;
    +
    +};
    +goog.inherits(ol.interaction.DragBox, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.DragBox}
    + * @private
    + */
    +ol.interaction.DragBox.handleDragEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return;
    +  }
    +
    +  this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
    +};
    +
    +
    +/**
    + * Returns geometry of last drawn box.
    + * @return {ol.geom.Polygon} Geometry.
    + * @api stable
    + */
    +ol.interaction.DragBox.prototype.getGeometry = function() {
    +  return this.box_.getGeometry();
    +};
    +
    +
    +/**
    + * To be overriden by child classes.
    + * FIXME: use constructor option instead of relying on overridding.
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @protected
    + */
    +ol.interaction.DragBox.prototype.onBoxEnd = goog.nullFunction;
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.DragBox}
    + * @private
    + */
    +ol.interaction.DragBox.handleUpEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return true;
    +  }
    +
    +  this.box_.setMap(null);
    +
    +  var deltaX = mapBrowserEvent.pixel[0] - this.startPixel_[0];
    +  var deltaY = mapBrowserEvent.pixel[1] - this.startPixel_[1];
    +
    +  if (deltaX * deltaX + deltaY * deltaY >=
    +      ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED) {
    +    this.onBoxEnd(mapBrowserEvent);
    +    this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXEND,
    +        mapBrowserEvent.coordinate));
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.DragBox}
    + * @private
    + */
    +ol.interaction.DragBox.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return false;
    +  }
    +
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  if (browserEvent.isMouseActionButton() && this.condition_(mapBrowserEvent)) {
    +    this.startPixel_ = mapBrowserEvent.pixel;
    +    this.box_.setMap(mapBrowserEvent.map);
    +    this.box_.setPixels(this.startPixel_, this.startPixel_);
    +    this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXSTART,
    +        mapBrowserEvent.coordinate));
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/dragpaninteraction.js b/mapbox-gl/ol/ol/interaction/dragpaninteraction.js
    new file mode 100644
    index 0000000000..22b207eef1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/dragpaninteraction.js
    @@ -0,0 +1,168 @@
    +goog.provide('ol.interaction.DragPan');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.Kinetic');
    +goog.require('ol.Pixel');
    +goog.require('ol.PreRenderFunction');
    +goog.require('ol.ViewHint');
    +goog.require('ol.coordinate');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Pointer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to pan the map by dragging the map.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.DragPanOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragPan = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.DragPan.handleDownEvent_,
    +    handleDragEvent: ol.interaction.DragPan.handleDragEvent_,
    +    handleUpEvent: ol.interaction.DragPan.handleUpEvent_
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Kinetic|undefined}
    +   */
    +  this.kinetic_ = options.kinetic;
    +
    +  /**
    +   * @private
    +   * @type {?ol.PreRenderFunction}
    +   */
    +  this.kineticPreRenderFn_ = null;
    +
    +  /**
    +   * @type {ol.Pixel}
    +   */
    +  this.lastCentroid = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.noModifierKeys;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.noKinetic_ = false;
    +
    +};
    +goog.inherits(ol.interaction.DragPan, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.DragPan}
    + * @private
    + */
    +ol.interaction.DragPan.handleDragEvent_ = function(mapBrowserEvent) {
    +  goog.asserts.assert(this.targetPointers.length >= 1);
    +  var centroid =
    +      ol.interaction.Pointer.centroid(this.targetPointers);
    +  if (this.kinetic_) {
    +    this.kinetic_.update(centroid[0], centroid[1]);
    +  }
    +  if (!goog.isNull(this.lastCentroid)) {
    +    var deltaX = this.lastCentroid[0] - centroid[0];
    +    var deltaY = centroid[1] - this.lastCentroid[1];
    +    var map = mapBrowserEvent.map;
    +    var view = map.getView();
    +    var viewState = view.getState();
    +    var center = [deltaX, deltaY];
    +    ol.coordinate.scale(center, viewState.resolution);
    +    ol.coordinate.rotate(center, viewState.rotation);
    +    ol.coordinate.add(center, viewState.center);
    +    center = view.constrainCenter(center);
    +    map.render();
    +    view.setCenter(center);
    +  }
    +  this.lastCentroid = centroid;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.DragPan}
    + * @private
    + */
    +ol.interaction.DragPan.handleUpEvent_ = function(mapBrowserEvent) {
    +  var map = mapBrowserEvent.map;
    +  var view = map.getView();
    +  if (this.targetPointers.length === 0) {
    +    if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
    +      var distance = this.kinetic_.getDistance();
    +      var angle = this.kinetic_.getAngle();
    +      var center = view.getCenter();
    +      goog.asserts.assert(goog.isDef(center));
    +      this.kineticPreRenderFn_ = this.kinetic_.pan(center);
    +      map.beforeRender(this.kineticPreRenderFn_);
    +      var centerpx = map.getPixelFromCoordinate(center);
    +      var dest = map.getCoordinateFromPixel([
    +        centerpx[0] - distance * Math.cos(angle),
    +        centerpx[1] - distance * Math.sin(angle)
    +      ]);
    +      dest = view.constrainCenter(dest);
    +      view.setCenter(dest);
    +    }
    +    view.setHint(ol.ViewHint.INTERACTING, -1);
    +    map.render();
    +    return false;
    +  } else {
    +    this.lastCentroid = null;
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.DragPan}
    + * @private
    + */
    +ol.interaction.DragPan.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
    +    var map = mapBrowserEvent.map;
    +    var view = map.getView();
    +    this.lastCentroid = null;
    +    if (!this.handlingDownUpSequence) {
    +      view.setHint(ol.ViewHint.INTERACTING, 1);
    +    }
    +    map.render();
    +    if (!goog.isNull(this.kineticPreRenderFn_) &&
    +        map.removePreRenderFunction(this.kineticPreRenderFn_)) {
    +      view.setCenter(mapBrowserEvent.frameState.viewState.center);
    +      this.kineticPreRenderFn_ = null;
    +    }
    +    if (this.kinetic_) {
    +      this.kinetic_.begin();
    +    }
    +    // No kinetic as soon as more than one pointer on the screen is
    +    // detected. This is to prevent nasty pans after pinch.
    +    this.noKinetic_ = this.targetPointers.length > 1;
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.DragPan.prototype.shouldStopEvent = goog.functions.FALSE;
    diff --git a/mapbox-gl/ol/ol/interaction/dragrotateandzoominteraction.js b/mapbox-gl/ol/ol/interaction/dragrotateandzoominteraction.js
    new file mode 100644
    index 0000000000..4c31e8f3a6
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/dragrotateandzoominteraction.js
    @@ -0,0 +1,149 @@
    +goog.provide('ol.interaction.DragRotateAndZoom');
    +
    +goog.require('goog.math.Vec2');
    +goog.require('ol');
    +goog.require('ol.ViewHint');
    +goog.require('ol.events.ConditionType');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.interaction.Pointer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom and rotate the map by clicking and dragging
    + * on the map.  By default, this interaction is limited to when the shift
    + * key is held down.
    + *
    + * This interaction is only supported for mouse devices.
    + *
    + * And this interaction is not included in the default interactions.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.DragRotateAndZoomOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragRotateAndZoom = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.DragRotateAndZoom.handleDownEvent_,
    +    handleDragEvent: ol.interaction.DragRotateAndZoom.handleDragEvent_,
    +    handleUpEvent: ol.interaction.DragRotateAndZoom.handleUpEvent_
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.shiftKeyOnly;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.lastAngle_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.lastMagnitude_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.lastScaleDelta_ = 0;
    +
    +};
    +goog.inherits(ol.interaction.DragRotateAndZoom, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.DragRotateAndZoom}
    + * @private
    + */
    +ol.interaction.DragRotateAndZoom.handleDragEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return;
    +  }
    +
    +  var map = mapBrowserEvent.map;
    +  var size = map.getSize();
    +  var offset = mapBrowserEvent.pixel;
    +  var delta = new goog.math.Vec2(
    +      offset[0] - size[0] / 2,
    +      size[1] / 2 - offset[1]);
    +  var theta = Math.atan2(delta.y, delta.x);
    +  var magnitude = delta.magnitude();
    +  var view = map.getView();
    +  var viewState = view.getState();
    +  map.render();
    +  if (goog.isDef(this.lastAngle_)) {
    +    var angleDelta = theta - this.lastAngle_;
    +    ol.interaction.Interaction.rotateWithoutConstraints(
    +        map, view, viewState.rotation - angleDelta);
    +  }
    +  this.lastAngle_ = theta;
    +  if (goog.isDef(this.lastMagnitude_)) {
    +    var resolution = this.lastMagnitude_ * (viewState.resolution / magnitude);
    +    ol.interaction.Interaction.zoomWithoutConstraints(map, view, resolution);
    +  }
    +  if (goog.isDef(this.lastMagnitude_)) {
    +    this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
    +  }
    +  this.lastMagnitude_ = magnitude;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.DragRotateAndZoom}
    + * @private
    + */
    +ol.interaction.DragRotateAndZoom.handleUpEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return true;
    +  }
    +
    +  var map = mapBrowserEvent.map;
    +  var view = map.getView();
    +  view.setHint(ol.ViewHint.INTERACTING, -1);
    +  var viewState = view.getState();
    +  var direction = this.lastScaleDelta_ - 1;
    +  ol.interaction.Interaction.rotate(map, view, viewState.rotation);
    +  ol.interaction.Interaction.zoom(map, view, viewState.resolution,
    +      undefined, ol.DRAGROTATEANDZOOM_ANIMATION_DURATION,
    +      direction);
    +  this.lastScaleDelta_ = 0;
    +  return false;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.DragRotateAndZoom}
    + * @private
    + */
    +ol.interaction.DragRotateAndZoom.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return false;
    +  }
    +
    +  if (this.condition_(mapBrowserEvent)) {
    +    mapBrowserEvent.map.getView().setHint(ol.ViewHint.INTERACTING, 1);
    +    this.lastAngle_ = undefined;
    +    this.lastMagnitude_ = undefined;
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/dragrotateinteraction.js b/mapbox-gl/ol/ol/interaction/dragrotateinteraction.js
    new file mode 100644
    index 0000000000..877aa227dc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/dragrotateinteraction.js
    @@ -0,0 +1,127 @@
    +goog.provide('ol.interaction.DragRotate');
    +
    +goog.require('ol');
    +goog.require('ol.ViewHint');
    +goog.require('ol.events.ConditionType');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.interaction.Pointer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to rotate the map by clicking and dragging on the map,
    + * normally combined with an {@link ol.events.condition} that limits
    + * it to when the alt and shift keys are held down.
    + *
    + * This interaction is only supported for mouse devices.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.DragRotateOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragRotate = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.DragRotate.handleDownEvent_,
    +    handleDragEvent: ol.interaction.DragRotate.handleDragEvent_,
    +    handleUpEvent: ol.interaction.DragRotate.handleUpEvent_
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.altShiftKeysOnly;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.lastAngle_ = undefined;
    +
    +};
    +goog.inherits(ol.interaction.DragRotate, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.DragRotate}
    + * @private
    + */
    +ol.interaction.DragRotate.handleDragEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return;
    +  }
    +
    +  var map = mapBrowserEvent.map;
    +  var size = map.getSize();
    +  var offset = mapBrowserEvent.pixel;
    +  var theta =
    +      Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
    +  if (goog.isDef(this.lastAngle_)) {
    +    var delta = theta - this.lastAngle_;
    +    var view = map.getView();
    +    var rotation = view.getRotation();
    +    map.render();
    +    ol.interaction.Interaction.rotateWithoutConstraints(
    +        map, view, rotation - delta);
    +  }
    +  this.lastAngle_ = theta;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.DragRotate}
    + * @private
    + */
    +ol.interaction.DragRotate.handleUpEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return true;
    +  }
    +
    +  var map = mapBrowserEvent.map;
    +  var view = map.getView();
    +  view.setHint(ol.ViewHint.INTERACTING, -1);
    +  var rotation = view.getRotation();
    +  ol.interaction.Interaction.rotate(map, view, rotation,
    +      undefined, ol.DRAGROTATE_ANIMATION_DURATION);
    +  return false;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.DragRotate}
    + * @private
    + */
    +ol.interaction.DragRotate.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (!ol.events.condition.mouseOnly(mapBrowserEvent)) {
    +    return false;
    +  }
    +
    +  var browserEvent = mapBrowserEvent.browserEvent;
    +  if (browserEvent.isMouseActionButton() && this.condition_(mapBrowserEvent)) {
    +    var map = mapBrowserEvent.map;
    +    map.getView().setHint(ol.ViewHint.INTERACTING, 1);
    +    map.render();
    +    this.lastAngle_ = undefined;
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.DragRotate.prototype.shouldStopEvent = goog.functions.FALSE;
    diff --git a/mapbox-gl/ol/ol/interaction/dragzoominteraction.js b/mapbox-gl/ol/ol/interaction/dragzoominteraction.js
    new file mode 100644
    index 0000000000..db190f2111
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/dragzoominteraction.js
    @@ -0,0 +1,65 @@
    +goog.provide('ol.interaction.DragZoom');
    +
    +goog.require('goog.asserts');
    +goog.require('ol');
    +goog.require('ol.events.condition');
    +goog.require('ol.extent');
    +goog.require('ol.interaction.DragBox');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.style.Stroke');
    +goog.require('ol.style.Style');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom the map by clicking and dragging on the map,
    + * normally combined with an {@link ol.events.condition} that limits
    + * it to when a key, shift by default, is held down.
    + *
    + * @constructor
    + * @extends {ol.interaction.DragBox}
    + * @param {olx.interaction.DragZoomOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.DragZoom = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var condition = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.shiftKeyOnly;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Style}
    +   */
    +  var style = goog.isDef(options.style) ?
    +      options.style : new ol.style.Style({
    +        stroke: new ol.style.Stroke({
    +          color: [0, 0, 255, 1]
    +        })
    +      });
    +
    +  goog.base(this, {
    +    condition: condition,
    +    style: style
    +  });
    +
    +};
    +goog.inherits(ol.interaction.DragZoom, ol.interaction.DragBox);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.DragZoom.prototype.onBoxEnd = function() {
    +  var map = this.getMap();
    +  var view = map.getView();
    +  goog.asserts.assert(!goog.isNull(view));
    +  var extent = this.getGeometry().getExtent();
    +  var center = ol.extent.getCenter(extent);
    +  var size = map.getSize();
    +  goog.asserts.assert(goog.isDef(size));
    +  ol.interaction.Interaction.zoom(map, view,
    +      view.getResolutionForExtent(extent, size),
    +      center, ol.DRAGZOOM_ANIMATION_DURATION);
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/drawinteraction.js b/mapbox-gl/ol/ol/interaction/drawinteraction.js
    new file mode 100644
    index 0000000000..0153b73bdc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/drawinteraction.js
    @@ -0,0 +1,643 @@
    +goog.provide('ol.DrawEvent');
    +goog.provide('ol.DrawEventType');
    +goog.provide('ol.interaction.Draw');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('ol.Collection');
    +goog.require('ol.Coordinate');
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.MapBrowserEvent');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.Object');
    +goog.require('ol.events.condition');
    +goog.require('ol.geom.Circle');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.interaction.InteractionProperty');
    +goog.require('ol.interaction.Pointer');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.DrawEventType = {
    +  /**
    +   * Triggered upon feature draw start
    +   * @event ol.DrawEvent#drawstart
    +   * @api stable
    +   */
    +  DRAWSTART: 'drawstart',
    +  /**
    +   * Triggered upon feature draw end
    +   * @event ol.DrawEvent#drawend
    +   * @api stable
    +   */
    +  DRAWEND: 'drawend'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.interaction.Draw} instances are instances of
    + * this type.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.DrawEvent}
    + * @param {ol.DrawEventType} type Type.
    + * @param {ol.Feature} feature The feature drawn.
    + */
    +ol.DrawEvent = function(type, feature) {
    +
    +  goog.base(this, type);
    +
    +  /**
    +   * The feature being drawn.
    +   * @type {ol.Feature}
    +   * @api stable
    +   */
    +  this.feature = feature;
    +
    +};
    +goog.inherits(ol.DrawEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * @classdesc
    + * Interaction that allows drawing geometries.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @fires ol.DrawEvent
    + * @param {olx.interaction.DrawOptions} options Options.
    + * @api stable
    + */
    +ol.interaction.Draw = function(options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.Draw.handleDownEvent_,
    +    handleEvent: ol.interaction.Draw.handleEvent,
    +    handleUpEvent: ol.interaction.Draw.handleUpEvent_
    +  });
    +
    +  /**
    +   * @type {ol.Pixel}
    +   * @private
    +   */
    +  this.downPx_ = null;
    +
    +  /**
    +   * Target source for drawn features.
    +   * @type {ol.source.Vector}
    +   * @private
    +   */
    +  this.source_ = goog.isDef(options.source) ? options.source : null;
    +
    +  /**
    +   * Target collection for drawn features.
    +   * @type {ol.Collection.<ol.Feature>}
    +   * @private
    +   */
    +  this.features_ = goog.isDef(options.features) ? options.features : null;
    +
    +  /**
    +   * Pixel distance for snapping.
    +   * @type {number}
    +   * @private
    +   */
    +  this.snapTolerance_ = goog.isDef(options.snapTolerance) ?
    +      options.snapTolerance : 12;
    +
    +  /**
    +   * The number of points that must be drawn before a polygon ring can be
    +   * finished.  The default is 3.
    +   * @type {number}
    +   * @private
    +   */
    +  this.minPointsPerRing_ = goog.isDef(options.minPointsPerRing) ?
    +      options.minPointsPerRing : 3;
    +
    +  /**
    +   * Geometry type.
    +   * @type {ol.geom.GeometryType}
    +   * @private
    +   */
    +  this.type_ = options.type;
    +
    +  /**
    +   * Drawing mode (derived from geometry type.
    +   * @type {ol.interaction.DrawMode}
    +   * @private
    +   */
    +  this.mode_ = ol.interaction.Draw.getMode_(this.type_);
    +
    +  /**
    +   * Finish coordinate for the feature (first point for polygons, last point for
    +   * linestrings).
    +   * @type {ol.Coordinate}
    +   * @private
    +   */
    +  this.finishCoordinate_ = null;
    +
    +  /**
    +   * Sketch feature.
    +   * @type {ol.Feature}
    +   * @private
    +   */
    +  this.sketchFeature_ = null;
    +
    +  /**
    +   * Sketch point.
    +   * @type {ol.Feature}
    +   * @private
    +   */
    +  this.sketchPoint_ = null;
    +
    +  /**
    +   * Sketch line. Used when drawing polygon.
    +   * @type {ol.Feature}
    +   * @private
    +   */
    +  this.sketchLine_ = null;
    +
    +  /**
    +   * Sketch polygon. Used when drawing polygon.
    +   * @type {Array.<Array.<ol.Coordinate>>}
    +   * @private
    +   */
    +  this.sketchPolygonCoords_ = null;
    +
    +  /**
    +   * Squared tolerance for handling up events.  If the squared distance
    +   * between a down and up event is greater than this tolerance, up events
    +   * will not be handled.
    +   * @type {number}
    +   * @private
    +   */
    +  this.squaredClickTolerance_ = 4;
    +
    +  /**
    +   * Draw overlay where our sketch features are drawn.
    +   * @type {ol.FeatureOverlay}
    +   * @private
    +   */
    +  this.overlay_ = new ol.FeatureOverlay({
    +    style: goog.isDef(options.style) ?
    +        options.style : ol.interaction.Draw.getDefaultStyleFunction()
    +  });
    +
    +  /**
    +   * Name of the geometry attribute for newly created features.
    +   * @type {string|undefined}
    +   * @private
    +   */
    +  this.geometryName_ = options.geometryName;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.noModifierKeys;
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.interaction.InteractionProperty.ACTIVE),
    +      this.updateState_, false, this);
    +
    +};
    +goog.inherits(ol.interaction.Draw, ol.interaction.Pointer);
    +
    +
    +/**
    + * @return {ol.style.StyleFunction} Styles.
    + */
    +ol.interaction.Draw.getDefaultStyleFunction = function() {
    +  var styles = ol.style.createDefaultEditingStyles();
    +  return function(feature, resolution) {
    +    return styles[feature.getGeometry().getType()];
    +  };
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.Draw.prototype.setMap = function(map) {
    +  goog.base(this, 'setMap', map);
    +  this.updateState_();
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.Draw}
    + * @api
    + */
    +ol.interaction.Draw.handleEvent = function(mapBrowserEvent) {
    +  var pass = true;
    +  if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) {
    +    pass = this.handlePointerMove_(mapBrowserEvent);
    +  } else if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.DBLCLICK) {
    +    pass = false;
    +  }
    +  return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} event Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.Draw}
    + * @private
    + */
    +ol.interaction.Draw.handleDownEvent_ = function(event) {
    +  if (this.condition_(event)) {
    +    this.downPx_ = event.pixel;
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} event Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.Draw}
    + * @private
    + */
    +ol.interaction.Draw.handleUpEvent_ = function(event) {
    +  var downPx = this.downPx_;
    +  var clickPx = event.pixel;
    +  var dx = downPx[0] - clickPx[0];
    +  var dy = downPx[1] - clickPx[1];
    +  var squaredDistance = dx * dx + dy * dy;
    +  var pass = true;
    +  if (squaredDistance <= this.squaredClickTolerance_) {
    +    this.handlePointerMove_(event);
    +    if (goog.isNull(this.finishCoordinate_)) {
    +      this.startDrawing_(event);
    +    } else if (this.mode_ === ol.interaction.DrawMode.POINT ||
    +        this.mode_ === ol.interaction.DrawMode.CIRCLE &&
    +            !goog.isNull(this.finishCoordinate_) ||
    +        this.atFinish_(event)) {
    +      this.finishDrawing();
    +    } else {
    +      this.addToDrawing_(event);
    +    }
    +    pass = false;
    +  }
    +  return pass;
    +};
    +
    +
    +/**
    + * Handle move events.
    + * @param {ol.MapBrowserEvent} event A move event.
    + * @return {boolean} Pass the event to other interactions.
    + * @private
    + */
    +ol.interaction.Draw.prototype.handlePointerMove_ = function(event) {
    +  if (this.mode_ === ol.interaction.DrawMode.POINT &&
    +      goog.isNull(this.finishCoordinate_)) {
    +    this.startDrawing_(event);
    +  } else if (!goog.isNull(this.finishCoordinate_)) {
    +    this.modifyDrawing_(event);
    +  } else {
    +    this.createOrUpdateSketchPoint_(event);
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * Determine if an event is within the snapping tolerance of the start coord.
    + * @param {ol.MapBrowserEvent} event Event.
    + * @return {boolean} The event is within the snapping tolerance of the start.
    + * @private
    + */
    +ol.interaction.Draw.prototype.atFinish_ = function(event) {
    +  var at = false;
    +  if (!goog.isNull(this.sketchFeature_)) {
    +    var geometry = this.sketchFeature_.getGeometry();
    +    var potentiallyDone = false;
    +    var potentiallyFinishCoordinates = [this.finishCoordinate_];
    +    if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +      potentiallyDone = geometry.getCoordinates().length > 2;
    +    } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +      potentiallyDone = geometry.getCoordinates()[0].length >
    +          this.minPointsPerRing_;
    +      potentiallyFinishCoordinates = [this.sketchPolygonCoords_[0][0],
    +        this.sketchPolygonCoords_[0][this.sketchPolygonCoords_[0].length - 2]];
    +    }
    +    if (potentiallyDone) {
    +      var map = event.map;
    +      for (var i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) {
    +        var finishCoordinate = potentiallyFinishCoordinates[i];
    +        var finishPixel = map.getPixelFromCoordinate(finishCoordinate);
    +        var pixel = event.pixel;
    +        var dx = pixel[0] - finishPixel[0];
    +        var dy = pixel[1] - finishPixel[1];
    +        at = Math.sqrt(dx * dx + dy * dy) <= this.snapTolerance_;
    +        if (at) {
    +          this.finishCoordinate_ = finishCoordinate;
    +          break;
    +        }
    +      }
    +    }
    +  }
    +  return at;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} event Event.
    + * @private
    + */
    +ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) {
    +  var coordinates = event.coordinate.slice();
    +  if (goog.isNull(this.sketchPoint_)) {
    +    this.sketchPoint_ = new ol.Feature(new ol.geom.Point(coordinates));
    +    this.updateSketchFeatures_();
    +  } else {
    +    var sketchPointGeom = this.sketchPoint_.getGeometry();
    +    goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point);
    +    sketchPointGeom.setCoordinates(coordinates);
    +  }
    +};
    +
    +
    +/**
    + * Start the drawing.
    + * @param {ol.MapBrowserEvent} event Event.
    + * @private
    + */
    +ol.interaction.Draw.prototype.startDrawing_ = function(event) {
    +  var start = event.coordinate;
    +  this.finishCoordinate_ = start;
    +  var geometry;
    +  if (this.mode_ === ol.interaction.DrawMode.POINT) {
    +    geometry = new ol.geom.Point(start.slice());
    +  } else {
    +    if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +      geometry = new ol.geom.LineString([start.slice(), start.slice()]);
    +    } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +      this.sketchLine_ = new ol.Feature(new ol.geom.LineString([start.slice(),
    +            start.slice()]));
    +      this.sketchPolygonCoords_ = [[start.slice(), start.slice()]];
    +      geometry = new ol.geom.Polygon(this.sketchPolygonCoords_);
    +    } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
    +      geometry = new ol.geom.Circle(start.slice(), 0);
    +      this.sketchLine_ = new ol.Feature(new ol.geom.LineString([start.slice(),
    +            start.slice()]));
    +    }
    +  }
    +  goog.asserts.assert(goog.isDef(geometry));
    +  this.sketchFeature_ = new ol.Feature();
    +  if (goog.isDef(this.geometryName_)) {
    +    this.sketchFeature_.setGeometryName(this.geometryName_);
    +  }
    +  this.sketchFeature_.setGeometry(geometry);
    +  this.updateSketchFeatures_();
    +  this.dispatchEvent(new ol.DrawEvent(ol.DrawEventType.DRAWSTART,
    +      this.sketchFeature_));
    +};
    +
    +
    +/**
    + * Modify the drawing.
    + * @param {ol.MapBrowserEvent} event Event.
    + * @private
    + */
    +ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
    +  var coordinate = event.coordinate;
    +  var geometry = this.sketchFeature_.getGeometry();
    +  var coordinates, last, sketchLineGeom;
    +  if (this.mode_ === ol.interaction.DrawMode.POINT) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +    last = geometry.getCoordinates();
    +    last[0] = coordinate[0];
    +    last[1] = coordinate[1];
    +    geometry.setCoordinates(last);
    +  } else {
    +    if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +      coordinates = geometry.getCoordinates();
    +    } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +      coordinates = this.sketchPolygonCoords_[0];
    +    } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Circle);
    +      coordinates = geometry.getCenter();
    +    }
    +    if (this.atFinish_(event)) {
    +      // snap to finish
    +      coordinate = this.finishCoordinate_.slice();
    +    }
    +    var sketchPointGeom = this.sketchPoint_.getGeometry();
    +    goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point);
    +    sketchPointGeom.setCoordinates(coordinate);
    +    last = coordinates[coordinates.length - 1];
    +    last[0] = coordinate[0];
    +    last[1] = coordinate[1];
    +    if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +      geometry.setCoordinates(coordinates);
    +    } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +      sketchLineGeom = this.sketchLine_.getGeometry();
    +      goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString);
    +      sketchLineGeom.setCoordinates(coordinates);
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +      geometry.setCoordinates(this.sketchPolygonCoords_);
    +    } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Circle);
    +      sketchLineGeom = this.sketchLine_.getGeometry();
    +      goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString);
    +      sketchLineGeom.setCoordinates([geometry.getCenter(), coordinate]);
    +      geometry.setRadius(sketchLineGeom.getLength());
    +    }
    +  }
    +  this.updateSketchFeatures_();
    +};
    +
    +
    +/**
    + * Add a new coordinate to the drawing.
    + * @param {ol.MapBrowserEvent} event Event.
    + * @private
    + */
    +ol.interaction.Draw.prototype.addToDrawing_ = function(event) {
    +  var coordinate = event.coordinate;
    +  var geometry = this.sketchFeature_.getGeometry();
    +  var coordinates;
    +  if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +    this.finishCoordinate_ = coordinate.slice();
    +    goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +    coordinates = geometry.getCoordinates();
    +    coordinates.push(coordinate.slice());
    +    geometry.setCoordinates(coordinates);
    +  } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +    this.sketchPolygonCoords_[0].push(coordinate.slice());
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +    geometry.setCoordinates(this.sketchPolygonCoords_);
    +  }
    +  this.updateSketchFeatures_();
    +};
    +
    +
    +/**
    + * Stop drawing and add the sketch feature to the target layer.
    + * @api
    + */
    +ol.interaction.Draw.prototype.finishDrawing = function() {
    +  var sketchFeature = this.abortDrawing_();
    +  goog.asserts.assert(!goog.isNull(sketchFeature));
    +  var coordinates;
    +  var geometry = sketchFeature.getGeometry();
    +  if (this.mode_ === ol.interaction.DrawMode.POINT) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +    coordinates = geometry.getCoordinates();
    +  } else if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +    coordinates = geometry.getCoordinates();
    +    // remove the redundant last point
    +    coordinates.pop();
    +    geometry.setCoordinates(coordinates);
    +  } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +    // When we finish drawing a polygon on the last point,
    +    // the last coordinate is duplicated as for LineString
    +    // we force the replacement by the first point
    +    this.sketchPolygonCoords_[0].pop();
    +    this.sketchPolygonCoords_[0].push(this.sketchPolygonCoords_[0][0]);
    +    geometry.setCoordinates(this.sketchPolygonCoords_);
    +    coordinates = geometry.getCoordinates();
    +  }
    +
    +  // cast multi-part geometries
    +  if (this.type_ === ol.geom.GeometryType.MULTI_POINT) {
    +    sketchFeature.setGeometry(new ol.geom.MultiPoint([coordinates]));
    +  } else if (this.type_ === ol.geom.GeometryType.MULTI_LINE_STRING) {
    +    sketchFeature.setGeometry(new ol.geom.MultiLineString([coordinates]));
    +  } else if (this.type_ === ol.geom.GeometryType.MULTI_POLYGON) {
    +    sketchFeature.setGeometry(new ol.geom.MultiPolygon([coordinates]));
    +  }
    +
    +  if (!goog.isNull(this.features_)) {
    +    this.features_.push(sketchFeature);
    +  }
    +  if (!goog.isNull(this.source_)) {
    +    this.source_.addFeature(sketchFeature);
    +  }
    +  this.dispatchEvent(new ol.DrawEvent(ol.DrawEventType.DRAWEND, sketchFeature));
    +};
    +
    +
    +/**
    + * Stop drawing without adding the sketch feature to the target layer.
    + * @return {ol.Feature} The sketch feature (or null if none).
    + * @private
    + */
    +ol.interaction.Draw.prototype.abortDrawing_ = function() {
    +  this.finishCoordinate_ = null;
    +  var sketchFeature = this.sketchFeature_;
    +  if (!goog.isNull(sketchFeature)) {
    +    this.sketchFeature_ = null;
    +    this.sketchPoint_ = null;
    +    this.sketchLine_ = null;
    +    this.overlay_.getFeatures().clear();
    +  }
    +  return sketchFeature;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.Draw.prototype.shouldStopEvent = goog.functions.FALSE;
    +
    +
    +/**
    + * Redraw the sketch features.
    + * @private
    + */
    +ol.interaction.Draw.prototype.updateSketchFeatures_ = function() {
    +  var sketchFeatures = [];
    +  if (!goog.isNull(this.sketchFeature_)) {
    +    sketchFeatures.push(this.sketchFeature_);
    +  }
    +  if (!goog.isNull(this.sketchLine_)) {
    +    sketchFeatures.push(this.sketchLine_);
    +  }
    +  if (!goog.isNull(this.sketchPoint_)) {
    +    sketchFeatures.push(this.sketchPoint_);
    +  }
    +  this.overlay_.setFeatures(new ol.Collection(sketchFeatures));
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.interaction.Draw.prototype.updateState_ = function() {
    +  var map = this.getMap();
    +  var active = this.getActive();
    +  if (goog.isNull(map) || !active) {
    +    this.abortDrawing_();
    +  }
    +  this.overlay_.setMap(active ? map : null);
    +};
    +
    +
    +/**
    + * Get the drawing mode.  The mode for mult-part geometries is the same as for
    + * their single-part cousins.
    + * @param {ol.geom.GeometryType} type Geometry type.
    + * @return {ol.interaction.DrawMode} Drawing mode.
    + * @private
    + */
    +ol.interaction.Draw.getMode_ = function(type) {
    +  var mode;
    +  if (type === ol.geom.GeometryType.POINT ||
    +      type === ol.geom.GeometryType.MULTI_POINT) {
    +    mode = ol.interaction.DrawMode.POINT;
    +  } else if (type === ol.geom.GeometryType.LINE_STRING ||
    +      type === ol.geom.GeometryType.MULTI_LINE_STRING) {
    +    mode = ol.interaction.DrawMode.LINE_STRING;
    +  } else if (type === ol.geom.GeometryType.POLYGON ||
    +      type === ol.geom.GeometryType.MULTI_POLYGON) {
    +    mode = ol.interaction.DrawMode.POLYGON;
    +  } else if (type === ol.geom.GeometryType.CIRCLE) {
    +    mode = ol.interaction.DrawMode.CIRCLE;
    +  }
    +  goog.asserts.assert(goog.isDef(mode));
    +  return mode;
    +};
    +
    +
    +/**
    + * Draw mode.  This collapses multi-part geometry types with their single-part
    + * cousins.
    + * @enum {string}
    + */
    +ol.interaction.DrawMode = {
    +  POINT: 'Point',
    +  LINE_STRING: 'LineString',
    +  POLYGON: 'Polygon',
    +  CIRCLE: 'Circle'
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/interaction.js b/mapbox-gl/ol/ol/interaction/interaction.js
    new file mode 100644
    index 0000000000..dae4ef1e4e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/interaction.js
    @@ -0,0 +1,251 @@
    +// FIXME factor out key precondition (shift et. al)
    +
    +goog.provide('ol.interaction.Interaction');
    +goog.provide('ol.interaction.InteractionProperty');
    +
    +goog.require('ol.MapBrowserEvent');
    +goog.require('ol.Object');
    +goog.require('ol.animation');
    +goog.require('ol.easing');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.interaction.InteractionProperty = {
    +  ACTIVE: 'active'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * User actions that change the state of the map. Some are similar to controls,
    + * but are not associated with a DOM element.
    + * For example, {@link ol.interaction.KeyboardZoom} is functionally the same as
    + * {@link ol.control.Zoom}, but triggered by a keyboard event not a button
    + * element event.
    + * Although interactions do not have a DOM element, some of them do render
    + * vectors and so are visible on the screen.
    + *
    + * @constructor
    + * @param {olx.interaction.InteractionOptions} options Options.
    + * @extends {ol.Object}
    + * @api
    + */
    +ol.interaction.Interaction = function(options) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.Map}
    +   */
    +  this.map_ = null;
    +
    +  this.setActive(true);
    +
    +  /**
    +   * @type {function(ol.MapBrowserEvent):boolean}
    +   */
    +  this.handleEvent = options.handleEvent;
    +
    +};
    +goog.inherits(ol.interaction.Interaction, ol.Object);
    +
    +
    +/**
    + * @return {boolean} `true` if the interaction is active, `false` otherwise.
    + * @observable
    + * @api
    + */
    +ol.interaction.Interaction.prototype.getActive = function() {
    +  return /** @type {boolean} */ (
    +      this.get(ol.interaction.InteractionProperty.ACTIVE));
    +};
    +goog.exportProperty(
    +    ol.interaction.Interaction.prototype,
    +    'getActive',
    +    ol.interaction.Interaction.prototype.getActive);
    +
    +
    +/**
    + * Get the map associated with this interaction.
    + * @return {ol.Map} Map.
    + */
    +ol.interaction.Interaction.prototype.getMap = function() {
    +  return this.map_;
    +};
    +
    +
    +/**
    + * Activate or deactivate the interaction.
    + * @param {boolean} active Active.
    + * @observable
    + * @api
    + */
    +ol.interaction.Interaction.prototype.setActive = function(active) {
    +  this.set(ol.interaction.InteractionProperty.ACTIVE, active);
    +};
    +goog.exportProperty(
    +    ol.interaction.Interaction.prototype,
    +    'setActive',
    +    ol.interaction.Interaction.prototype.setActive);
    +
    +
    +/**
    + * Remove the interaction from its current map and attach it to the new map.
    + * Subclasses may set up event handlers to get notified about changes to
    + * the map here.
    + * @param {ol.Map} map Map.
    + */
    +ol.interaction.Interaction.prototype.setMap = function(map) {
    +  this.map_ = map;
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {ol.Coordinate} delta Delta.
    + * @param {number=} opt_duration Duration.
    + */
    +ol.interaction.Interaction.pan = function(map, view, delta, opt_duration) {
    +  var currentCenter = view.getCenter();
    +  if (goog.isDef(currentCenter)) {
    +    if (goog.isDef(opt_duration) && opt_duration > 0) {
    +      map.beforeRender(ol.animation.pan({
    +        source: currentCenter,
    +        duration: opt_duration,
    +        easing: ol.easing.linear
    +      }));
    +    }
    +    var center = view.constrainCenter(
    +        [currentCenter[0] + delta[0], currentCenter[1] + delta[1]]);
    +    view.setCenter(center);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {number|undefined} rotation Rotation.
    + * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
    + * @param {number=} opt_duration Duration.
    + */
    +ol.interaction.Interaction.rotate =
    +    function(map, view, rotation, opt_anchor, opt_duration) {
    +  rotation = view.constrainRotation(rotation, 0);
    +  ol.interaction.Interaction.rotateWithoutConstraints(
    +      map, view, rotation, opt_anchor, opt_duration);
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {number|undefined} rotation Rotation.
    + * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
    + * @param {number=} opt_duration Duration.
    + */
    +ol.interaction.Interaction.rotateWithoutConstraints =
    +    function(map, view, rotation, opt_anchor, opt_duration) {
    +  if (goog.isDefAndNotNull(rotation)) {
    +    var currentRotation = view.getRotation();
    +    var currentCenter = view.getCenter();
    +    if (goog.isDef(currentRotation) && goog.isDef(currentCenter) &&
    +        goog.isDef(opt_duration) && opt_duration > 0) {
    +      map.beforeRender(ol.animation.rotate({
    +        rotation: currentRotation,
    +        duration: opt_duration,
    +        easing: ol.easing.easeOut
    +      }));
    +      if (goog.isDef(opt_anchor)) {
    +        map.beforeRender(ol.animation.pan({
    +          source: currentCenter,
    +          duration: opt_duration,
    +          easing: ol.easing.easeOut
    +        }));
    +      }
    +    }
    +    view.rotate(rotation, opt_anchor);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {number|undefined} resolution Resolution to go to.
    + * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
    + * @param {number=} opt_duration Duration.
    + * @param {number=} opt_direction Zooming direction; > 0 indicates
    + *     zooming out, in which case the constraints system will select
    + *     the largest nearest resolution; < 0 indicates zooming in, in
    + *     which case the constraints system will select the smallest
    + *     nearest resolution; == 0 indicates that the zooming direction
    + *     is unknown/not relevant, in which case the constraints system
    + *     will select the nearest resolution. If not defined 0 is
    + *     assumed.
    + */
    +ol.interaction.Interaction.zoom =
    +    function(map, view, resolution, opt_anchor, opt_duration, opt_direction) {
    +  resolution = view.constrainResolution(resolution, 0, opt_direction);
    +  ol.interaction.Interaction.zoomWithoutConstraints(
    +      map, view, resolution, opt_anchor, opt_duration);
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {number} delta Delta from previous zoom level.
    + * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
    + * @param {number=} opt_duration Duration.
    + */
    +ol.interaction.Interaction.zoomByDelta =
    +    function(map, view, delta, opt_anchor, opt_duration) {
    +  var currentResolution = view.getResolution();
    +  var resolution = view.constrainResolution(currentResolution, delta, 0);
    +  ol.interaction.Interaction.zoomWithoutConstraints(
    +      map, view, resolution, opt_anchor, opt_duration);
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {ol.View} view View.
    + * @param {number|undefined} resolution Resolution to go to.
    + * @param {ol.Coordinate=} opt_anchor Anchor coordinate.
    + * @param {number=} opt_duration Duration.
    + */
    +ol.interaction.Interaction.zoomWithoutConstraints =
    +    function(map, view, resolution, opt_anchor, opt_duration) {
    +  if (goog.isDefAndNotNull(resolution)) {
    +    var currentResolution = view.getResolution();
    +    var currentCenter = view.getCenter();
    +    if (goog.isDef(currentResolution) && goog.isDef(currentCenter) &&
    +        goog.isDef(opt_duration) && opt_duration > 0) {
    +      map.beforeRender(ol.animation.zoom({
    +        resolution: currentResolution,
    +        duration: opt_duration,
    +        easing: ol.easing.easeOut
    +      }));
    +      if (goog.isDef(opt_anchor)) {
    +        map.beforeRender(ol.animation.pan({
    +          source: currentCenter,
    +          duration: opt_duration,
    +          easing: ol.easing.easeOut
    +        }));
    +      }
    +    }
    +    if (goog.isDefAndNotNull(opt_anchor)) {
    +      var center = view.calculateCenterZoom(resolution, opt_anchor);
    +      view.setCenter(center);
    +    }
    +    view.setResolution(resolution);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/interaction.jsdoc b/mapbox-gl/ol/ol/interaction/interaction.jsdoc
    new file mode 100644
    index 0000000000..6da0d140d4
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/interaction.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.interaction
    + */
    diff --git a/mapbox-gl/ol/ol/interaction/interactiondefaults.js b/mapbox-gl/ol/ol/interaction/interactiondefaults.js
    new file mode 100644
    index 0000000000..4b0815a9ad
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/interactiondefaults.js
    @@ -0,0 +1,113 @@
    +goog.provide('ol.interaction');
    +
    +goog.require('ol.Collection');
    +goog.require('ol.Kinetic');
    +goog.require('ol.interaction.DoubleClickZoom');
    +goog.require('ol.interaction.DragPan');
    +goog.require('ol.interaction.DragRotate');
    +goog.require('ol.interaction.DragZoom');
    +goog.require('ol.interaction.KeyboardPan');
    +goog.require('ol.interaction.KeyboardZoom');
    +goog.require('ol.interaction.MouseWheelZoom');
    +goog.require('ol.interaction.PinchRotate');
    +goog.require('ol.interaction.PinchZoom');
    +
    +
    +/**
    + * Set of interactions included in maps by default. Specific interactions can be
    + * excluded by setting the appropriate option to false in the constructor
    + * options, but the order of the interactions is fixed.  If you want to specify
    + * a different order for interactions, you will need to create your own
    + * {@link ol.interaction.Interaction} instances and insert them into a
    + * {@link ol.Collection} in the order you want before creating your
    + * {@link ol.Map} instance. The default set of interactions, in sequence, is:
    + * * {@link ol.interaction.DragRotate}
    + * * {@link ol.interaction.DoubleClickZoom}
    + * * {@link ol.interaction.DragPan}
    + * * {@link ol.interaction.PinchRotate}
    + * * {@link ol.interaction.PinchZoom}
    + * * {@link ol.interaction.KeyboardPan}
    + * * {@link ol.interaction.KeyboardZoom}
    + * * {@link ol.interaction.MouseWheelZoom}
    + * * {@link ol.interaction.DragZoom}
    + *
    + * Note that DragZoom renders a box as a vector polygon, so this interaction
    + * should be excluded if you want a build with no vector support.
    + *
    + * @param {olx.interaction.DefaultsOptions=} opt_options Defaults options.
    + * @return {ol.Collection.<ol.interaction.Interaction>} A collection of
    + * interactions to be used with the ol.Map constructor's interactions option.
    + * @api stable
    + */
    +ol.interaction.defaults = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var interactions = new ol.Collection();
    +
    +  var kinetic = new ol.Kinetic(-0.005, 0.05, 100);
    +
    +  var altShiftDragRotate = goog.isDef(options.altShiftDragRotate) ?
    +      options.altShiftDragRotate : true;
    +  if (altShiftDragRotate) {
    +    interactions.push(new ol.interaction.DragRotate());
    +  }
    +
    +  var doubleClickZoom = goog.isDef(options.doubleClickZoom) ?
    +      options.doubleClickZoom : true;
    +  if (doubleClickZoom) {
    +    interactions.push(new ol.interaction.DoubleClickZoom({
    +      delta: options.zoomDelta,
    +      duration: options.zoomDuration
    +    }));
    +  }
    +
    +  var dragPan = goog.isDef(options.dragPan) ?
    +      options.dragPan : true;
    +  if (dragPan) {
    +    interactions.push(new ol.interaction.DragPan({
    +      kinetic: kinetic
    +    }));
    +  }
    +
    +  var pinchRotate = goog.isDef(options.pinchRotate) ?
    +      options.pinchRotate : true;
    +  if (pinchRotate) {
    +    interactions.push(new ol.interaction.PinchRotate());
    +  }
    +
    +  var pinchZoom = goog.isDef(options.pinchZoom) ?
    +      options.pinchZoom : true;
    +  if (pinchZoom) {
    +    interactions.push(new ol.interaction.PinchZoom({
    +      duration: options.zoomDuration
    +    }));
    +  }
    +
    +  var keyboard = goog.isDef(options.keyboard) ?
    +      options.keyboard : true;
    +  if (keyboard) {
    +    interactions.push(new ol.interaction.KeyboardPan());
    +    interactions.push(new ol.interaction.KeyboardZoom({
    +      delta: options.zoomDelta,
    +      duration: options.zoomDuration
    +    }));
    +  }
    +
    +  var mouseWheelZoom = goog.isDef(options.mouseWheelZoom) ?
    +      options.mouseWheelZoom : true;
    +  if (mouseWheelZoom) {
    +    interactions.push(new ol.interaction.MouseWheelZoom({
    +      duration: options.zoomDuration
    +    }));
    +  }
    +
    +  var shiftDragZoom = goog.isDef(options.shiftDragZoom) ?
    +      options.shiftDragZoom : true;
    +  if (shiftDragZoom) {
    +    interactions.push(new ol.interaction.DragZoom());
    +  }
    +
    +  return interactions;
    +
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/keyboardpaninteraction.js b/mapbox-gl/ol/ol/interaction/keyboardpaninteraction.js
    new file mode 100644
    index 0000000000..9cfee2d707
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/keyboardpaninteraction.js
    @@ -0,0 +1,99 @@
    +goog.provide('ol.interaction.KeyboardPan');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.KeyCodes');
    +goog.require('goog.events.KeyHandler.EventType');
    +goog.require('goog.functions');
    +goog.require('ol');
    +goog.require('ol.coordinate');
    +goog.require('ol.events.ConditionType');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Interaction');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to pan the map using keyboard arrows.
    + * Note that, although this interaction is by default included in maps,
    + * the keys can only be used when browser focus is on the element to which
    + * the keyboard events are attached. By default, this is the map div,
    + * though you can change this with the `keyboardEventTarget` in
    + * {@link ol.Map}. `document` never loses focus but, for any other element,
    + * focus will have to be on, and returned to, this element if the keys are to
    + * function.
    + * See also {@link ol.interaction.KeyboardZoom}.
    + *
    + * @constructor
    + * @extends {ol.interaction.Interaction}
    + * @param {olx.interaction.KeyboardPanOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.KeyboardPan = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.KeyboardPan.handleEvent
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ? options.condition :
    +      goog.functions.and(ol.events.condition.noModifierKeys,
    +          ol.events.condition.targetNotEditable);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.pixelDelta_ = goog.isDef(options.pixelDelta) ? options.pixelDelta : 128;
    +
    +};
    +goog.inherits(ol.interaction.KeyboardPan, ol.interaction.Interaction);
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.KeyboardPan}
    + * @api
    + */
    +ol.interaction.KeyboardPan.handleEvent = function(mapBrowserEvent) {
    +  var stopEvent = false;
    +  if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
    +    var keyEvent = /** @type {goog.events.KeyEvent} */
    +        (mapBrowserEvent.browserEvent);
    +    var keyCode = keyEvent.keyCode;
    +    if (this.condition_(mapBrowserEvent) &&
    +        (keyCode == goog.events.KeyCodes.DOWN ||
    +        keyCode == goog.events.KeyCodes.LEFT ||
    +        keyCode == goog.events.KeyCodes.RIGHT ||
    +        keyCode == goog.events.KeyCodes.UP)) {
    +      var map = mapBrowserEvent.map;
    +      var view = map.getView();
    +      goog.asserts.assert(!goog.isNull(view));
    +      var viewState = view.getState();
    +      var mapUnitsDelta = viewState.resolution * this.pixelDelta_;
    +      var deltaX = 0, deltaY = 0;
    +      if (keyCode == goog.events.KeyCodes.DOWN) {
    +        deltaY = -mapUnitsDelta;
    +      } else if (keyCode == goog.events.KeyCodes.LEFT) {
    +        deltaX = -mapUnitsDelta;
    +      } else if (keyCode == goog.events.KeyCodes.RIGHT) {
    +        deltaX = mapUnitsDelta;
    +      } else {
    +        deltaY = mapUnitsDelta;
    +      }
    +      var delta = [deltaX, deltaY];
    +      ol.coordinate.rotate(delta, viewState.rotation);
    +      ol.interaction.Interaction.pan(
    +          map, view, delta, ol.KEYBOARD_PAN_DURATION);
    +      mapBrowserEvent.preventDefault();
    +      stopEvent = true;
    +    }
    +  }
    +  return !stopEvent;
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/keyboardzoominteraction.js b/mapbox-gl/ol/ol/interaction/keyboardzoominteraction.js
    new file mode 100644
    index 0000000000..b76f8a0587
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/keyboardzoominteraction.js
    @@ -0,0 +1,85 @@
    +goog.provide('ol.interaction.KeyboardZoom');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.KeyHandler.EventType');
    +goog.require('ol.events.ConditionType');
    +goog.require('ol.events.condition');
    +goog.require('ol.interaction.Interaction');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom the map using keyboard + and -.
    + * Note that, although this interaction is by default included in maps,
    + * the keys can only be used when browser focus is on the element to which
    + * the keyboard events are attached. By default, this is the map div,
    + * though you can change this with the `keyboardEventTarget` in
    + * {@link ol.Map}. `document` never loses focus but, for any other element,
    + * focus will have to be on, and returned to, this element if the keys are to
    + * function.
    + * See also {@link ol.interaction.KeyboardPan}.
    + *
    + * @constructor
    + * @param {olx.interaction.KeyboardZoomOptions=} opt_options Options.
    + * @extends {ol.interaction.Interaction}
    + * @api stable
    + */
    +ol.interaction.KeyboardZoom = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.KeyboardZoom.handleEvent
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ? options.condition :
    +          ol.events.condition.targetNotEditable;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.delta_ = goog.isDef(options.delta) ? options.delta : 1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 100;
    +
    +};
    +goog.inherits(ol.interaction.KeyboardZoom, ol.interaction.Interaction);
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.KeyboardZoom}
    + * @api
    + */
    +ol.interaction.KeyboardZoom.handleEvent = function(mapBrowserEvent) {
    +  var stopEvent = false;
    +  if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
    +    var keyEvent = /** @type {goog.events.KeyEvent} */
    +        (mapBrowserEvent.browserEvent);
    +    var charCode = keyEvent.charCode;
    +    if (this.condition_(mapBrowserEvent) &&
    +        (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0))) {
    +      var map = mapBrowserEvent.map;
    +      var delta = (charCode == '+'.charCodeAt(0)) ? this.delta_ : -this.delta_;
    +      map.render();
    +      var view = map.getView();
    +      goog.asserts.assert(!goog.isNull(view));
    +      ol.interaction.Interaction.zoomByDelta(
    +          map, view, delta, undefined, this.duration_);
    +      mapBrowserEvent.preventDefault();
    +      stopEvent = true;
    +    }
    +  }
    +  return !stopEvent;
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/modifyinteraction.js b/mapbox-gl/ol/ol/interaction/modifyinteraction.js
    new file mode 100644
    index 0000000000..8446a7b6ca
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/modifyinteraction.js
    @@ -0,0 +1,791 @@
    +goog.provide('ol.interaction.Modify');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.functions');
    +goog.require('ol.Collection');
    +goog.require('ol.CollectionEventType');
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.ViewHint');
    +goog.require('ol.coordinate');
    +goog.require('ol.events.condition');
    +goog.require('ol.extent');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.interaction.Pointer');
    +goog.require('ol.structs.RBush');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @typedef {{depth: (Array.<number>|undefined),
    + *            feature: ol.Feature,
    + *            geometry: ol.geom.SimpleGeometry,
    + *            index: (number|undefined),
    + *            segment: Array.<ol.Extent>}}
    + */
    +ol.interaction.SegmentDataType;
    +
    +
    +
    +/**
    + * @classdesc
    + * Interaction for modifying vector data.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.ModifyOptions} options Options.
    + * @api stable
    + */
    +ol.interaction.Modify = function(options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.Modify.handleDownEvent_,
    +    handleDragEvent: ol.interaction.Modify.handleDragEvent_,
    +    handleEvent: ol.interaction.Modify.handleEvent,
    +    handleUpEvent: ol.interaction.Modify.handleUpEvent_
    +  });
    +
    +  /**
    +   * @type {ol.events.ConditionType}
    +   * @private
    +   */
    +  this.deleteCondition_ = goog.isDef(options.deleteCondition) ?
    +      options.deleteCondition :
    +      /** @type {ol.events.ConditionType} */ (goog.functions.and(
    +          ol.events.condition.noModifierKeys,
    +          ol.events.condition.singleClick));
    +
    +  /**
    +   * Editing vertex.
    +   * @type {ol.Feature}
    +   * @private
    +   */
    +  this.vertexFeature_ = null;
    +
    +  /**
    +   * Segments intersecting {@link this.vertexFeature_} by segment uid.
    +   * @type {Object.<string, boolean>}
    +   * @private
    +   */
    +  this.vertexSegments_ = null;
    +
    +  /**
    +   * @type {ol.Pixel}
    +   * @private
    +   */
    +  this.lastPixel_ = [0, 0];
    +
    +  /**
    +   * Segment RTree for each layer
    +   * @type {Object.<*, ol.structs.RBush>}
    +   * @private
    +   */
    +  this.rBush_ = new ol.structs.RBush();
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ?
    +      options.pixelTolerance : 10;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.snappedToVertex_ = false;
    +
    +  /**
    +   * @type {Array}
    +   * @private
    +   */
    +  this.dragSegments_ = null;
    +
    +  /**
    +   * Draw overlay where are sketch features are drawn.
    +   * @type {ol.FeatureOverlay}
    +   * @private
    +   */
    +  this.overlay_ = new ol.FeatureOverlay({
    +    style: goog.isDef(options.style) ? options.style :
    +        ol.interaction.Modify.getDefaultStyleFunction()
    +  });
    +
    +  /**
    +  * @const
    +  * @private
    +  * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)> }
    +  */
    +  this.SEGMENT_WRITERS_ = {
    +    'Point': this.writePointGeometry_,
    +    'LineString': this.writeLineStringGeometry_,
    +    'LinearRing': this.writeLineStringGeometry_,
    +    'Polygon': this.writePolygonGeometry_,
    +    'MultiPoint': this.writeMultiPointGeometry_,
    +    'MultiLineString': this.writeMultiLineStringGeometry_,
    +    'MultiPolygon': this.writeMultiPolygonGeometry_,
    +    'GeometryCollection': this.writeGeometryCollectionGeometry_
    +  };
    +
    +  /**
    +   * @type {ol.Collection.<ol.Feature>}
    +   * @private
    +   */
    +  this.features_ = options.features;
    +
    +  this.features_.forEach(this.addFeature_, this);
    +  goog.events.listen(this.features_, ol.CollectionEventType.ADD,
    +      this.handleFeatureAdd_, false, this);
    +  goog.events.listen(this.features_, ol.CollectionEventType.REMOVE,
    +      this.handleFeatureRemove_, false, this);
    +
    +};
    +goog.inherits(ol.interaction.Modify, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.interaction.Modify.prototype.addFeature_ = function(feature) {
    +  var geometry = feature.getGeometry();
    +  if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) {
    +    this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry);
    +  }
    +  var map = this.getMap();
    +  if (!goog.isNull(map)) {
    +    this.handlePointerAtPixel_(this.lastPixel_, map);
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.Modify.prototype.setMap = function(map) {
    +  this.overlay_.setMap(map);
    +  goog.base(this, 'setMap', map);
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} evt Event.
    + * @private
    + */
    +ol.interaction.Modify.prototype.handleFeatureAdd_ = function(evt) {
    +  var feature = evt.element;
    +  goog.asserts.assertInstanceof(feature, ol.Feature);
    +  this.addFeature_(feature);
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} evt Event.
    + * @private
    + */
    +ol.interaction.Modify.prototype.handleFeatureRemove_ = function(evt) {
    +  var feature = evt.element;
    +  var rBush = this.rBush_;
    +  var i, nodesToRemove = [];
    +  rBush.forEachInExtent(feature.getGeometry().getExtent(), function(node) {
    +    if (feature === node.feature) {
    +      nodesToRemove.push(node);
    +    }
    +  });
    +  for (i = nodesToRemove.length - 1; i >= 0; --i) {
    +    rBush.remove(nodesToRemove[i]);
    +  }
    +  // There remains only vertexFeature…
    +  if (!goog.isNull(this.vertexFeature_) &&
    +      this.features_.getLength() === 0) {
    +    this.overlay_.removeFeature(this.vertexFeature_);
    +    this.vertexFeature_ = null;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.Point} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writePointGeometry_ =
    +    function(feature, geometry) {
    +  var coordinates = geometry.getCoordinates();
    +  var segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +    feature: feature,
    +    geometry: geometry,
    +    segment: [coordinates, coordinates]
    +  });
    +  this.rBush_.insert(geometry.getExtent(), segmentData);
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.MultiPoint} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writeMultiPointGeometry_ =
    +    function(feature, geometry) {
    +  var points = geometry.getCoordinates();
    +  var coordinates, i, ii, segmentData;
    +  for (i = 0, ii = points.length; i < ii; ++i) {
    +    coordinates = points[i];
    +    segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +      feature: feature,
    +      geometry: geometry,
    +      depth: [i],
    +      index: i,
    +      segment: [coordinates, coordinates]
    +    });
    +    this.rBush_.insert(geometry.getExtent(), segmentData);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.LineString} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writeLineStringGeometry_ =
    +    function(feature, geometry) {
    +  var coordinates = geometry.getCoordinates();
    +  var i, ii, segment, segmentData;
    +  for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
    +    segment = coordinates.slice(i, i + 2);
    +    segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +      feature: feature,
    +      geometry: geometry,
    +      index: i,
    +      segment: segment
    +    });
    +    this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.MultiLineString} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writeMultiLineStringGeometry_ =
    +    function(feature, geometry) {
    +  var lines = geometry.getCoordinates();
    +  var coordinates, i, ii, j, jj, segment, segmentData;
    +  for (j = 0, jj = lines.length; j < jj; ++j) {
    +    coordinates = lines[j];
    +    for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
    +      segment = coordinates.slice(i, i + 2);
    +      segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +        feature: feature,
    +        geometry: geometry,
    +        depth: [j],
    +        index: i,
    +        segment: segment
    +      });
    +      this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.Polygon} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writePolygonGeometry_ =
    +    function(feature, geometry) {
    +  var rings = geometry.getCoordinates();
    +  var coordinates, i, ii, j, jj, segment, segmentData;
    +  for (j = 0, jj = rings.length; j < jj; ++j) {
    +    coordinates = rings[j];
    +    for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
    +      segment = coordinates.slice(i, i + 2);
    +      segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +        feature: feature,
    +        geometry: geometry,
    +        depth: [j],
    +        index: i,
    +        segment: segment
    +      });
    +      this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.MultiPolygon} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ =
    +    function(feature, geometry) {
    +  var polygons = geometry.getCoordinates();
    +  var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData;
    +  for (k = 0, kk = polygons.length; k < kk; ++k) {
    +    rings = polygons[k];
    +    for (j = 0, jj = rings.length; j < jj; ++j) {
    +      coordinates = rings[j];
    +      for (i = 0, ii = coordinates.length - 1; i < ii; ++i) {
    +        segment = coordinates.slice(i, i + 2);
    +        segmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +          feature: feature,
    +          geometry: geometry,
    +          depth: [j, k],
    +          index: i,
    +          segment: segment
    +        });
    +        this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature
    + * @param {ol.geom.GeometryCollection} geometry Geometry.
    + * @private
    + */
    +ol.interaction.Modify.prototype.writeGeometryCollectionGeometry_ =
    +    function(feature, geometry) {
    +  var i, geometries = geometry.getGeometriesArray();
    +  for (i = 0; i < geometries.length; ++i) {
    +    this.SEGMENT_WRITERS_[geometries[i].getType()].call(
    +        this, feature, geometries[i]);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinates Coordinates.
    + * @return {ol.Feature} Vertex feature.
    + * @private
    + */
    +ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ =
    +    function(coordinates) {
    +  var vertexFeature = this.vertexFeature_;
    +  if (goog.isNull(vertexFeature)) {
    +    vertexFeature = new ol.Feature(new ol.geom.Point(coordinates));
    +    this.vertexFeature_ = vertexFeature;
    +    this.overlay_.addFeature(vertexFeature);
    +  } else {
    +    var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
    +    geometry.setCoordinates(coordinates);
    +  }
    +  return vertexFeature;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} evt Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.Modify}
    + * @private
    + */
    +ol.interaction.Modify.handleDownEvent_ = function(evt) {
    +  this.handlePointerAtPixel_(evt.pixel, evt.map);
    +  this.dragSegments_ = [];
    +  var vertexFeature = this.vertexFeature_;
    +  if (!goog.isNull(vertexFeature)) {
    +    var insertVertices = [];
    +    var geometry =  /** @type {ol.geom.Point} */ (vertexFeature.getGeometry());
    +    var vertex = geometry.getCoordinates();
    +    var vertexExtent = ol.extent.boundingExtent([vertex]);
    +    var segmentDataMatches = this.rBush_.getInExtent(vertexExtent);
    +    for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
    +      var segmentDataMatch = segmentDataMatches[i];
    +      var segment = segmentDataMatch.segment;
    +      if (ol.coordinate.equals(segment[0], vertex)) {
    +        this.dragSegments_.push([segmentDataMatch, 0]);
    +      } else if (ol.coordinate.equals(segment[1], vertex)) {
    +        this.dragSegments_.push([segmentDataMatch, 1]);
    +      } else if (goog.getUid(segment) in this.vertexSegments_) {
    +        insertVertices.push([segmentDataMatch, vertex]);
    +      }
    +    }
    +    for (i = insertVertices.length - 1; i >= 0; --i) {
    +      this.insertVertex_.apply(this, insertVertices[i]);
    +    }
    +  }
    +  return !goog.isNull(this.vertexFeature_);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} evt Event.
    + * @this {ol.interaction.Modify}
    + * @private
    + */
    +ol.interaction.Modify.handleDragEvent_ = function(evt) {
    +  var vertex = evt.coordinate;
    +  for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
    +    var dragSegment = this.dragSegments_[i];
    +    var segmentData = dragSegment[0];
    +    var depth = segmentData.depth;
    +    var geometry = segmentData.geometry;
    +    var coordinates = geometry.getCoordinates();
    +    var segment = segmentData.segment;
    +    var index = dragSegment[1];
    +
    +    while (vertex.length < geometry.getStride()) {
    +      vertex.push(0);
    +    }
    +
    +    switch (geometry.getType()) {
    +      case ol.geom.GeometryType.POINT:
    +        coordinates = vertex;
    +        segment[0] = segment[1] = vertex;
    +        break;
    +      case ol.geom.GeometryType.MULTI_POINT:
    +        coordinates[segmentData.index] = vertex;
    +        segment[0] = segment[1] = vertex;
    +        break;
    +      case ol.geom.GeometryType.LINE_STRING:
    +        coordinates[segmentData.index + index] = vertex;
    +        segment[index] = vertex;
    +        break;
    +      case ol.geom.GeometryType.MULTI_LINE_STRING:
    +        coordinates[depth[0]][segmentData.index + index] = vertex;
    +        segment[index] = vertex;
    +        break;
    +      case ol.geom.GeometryType.POLYGON:
    +        coordinates[depth[0]][segmentData.index + index] = vertex;
    +        segment[index] = vertex;
    +        break;
    +      case ol.geom.GeometryType.MULTI_POLYGON:
    +        coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex;
    +        segment[index] = vertex;
    +        break;
    +    }
    +
    +    geometry.setCoordinates(coordinates);
    +    this.createOrUpdateVertexFeature_(vertex);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} evt Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.Modify}
    + * @private
    + */
    +ol.interaction.Modify.handleUpEvent_ = function(evt) {
    +  var segmentData;
    +  for (var i = this.dragSegments_.length - 1; i >= 0; --i) {
    +    segmentData = this.dragSegments_[i][0];
    +    this.rBush_.update(ol.extent.boundingExtent(segmentData.segment),
    +        segmentData);
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.Modify}
    + * @api
    + */
    +ol.interaction.Modify.handleEvent = function(mapBrowserEvent) {
    +  var handled;
    +  if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] &&
    +      mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE) {
    +    this.handlePointerMove_(mapBrowserEvent);
    +  }
    +  if (!goog.isNull(this.vertexFeature_) && this.snappedToVertex_ &&
    +      this.deleteCondition_(mapBrowserEvent)) {
    +    var geometry = this.vertexFeature_.getGeometry();
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +    handled = this.removeVertex_();
    +  }
    +  return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) &&
    +      !handled;
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} evt Event.
    + * @private
    + */
    +ol.interaction.Modify.prototype.handlePointerMove_ = function(evt) {
    +  this.lastPixel_ = evt.pixel;
    +  this.handlePointerAtPixel_(evt.pixel, evt.map);
    +};
    +
    +
    +/**
    + * @param {ol.Pixel} pixel Pixel
    + * @param {ol.Map} map Map.
    + * @private
    + */
    +ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) {
    +  var pixelCoordinate = map.getCoordinateFromPixel(pixel);
    +  var sortByDistance = function(a, b) {
    +    return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) -
    +        ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment);
    +  };
    +
    +  var lowerLeft = map.getCoordinateFromPixel(
    +      [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]);
    +  var upperRight = map.getCoordinateFromPixel(
    +      [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]);
    +  var box = ol.extent.boundingExtent([lowerLeft, upperRight]);
    +
    +  var rBush = this.rBush_;
    +  var nodes = rBush.getInExtent(box);
    +  if (nodes.length > 0) {
    +    nodes.sort(sortByDistance);
    +    var node = nodes[0];
    +    var closestSegment = node.segment;
    +    var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate,
    +        closestSegment));
    +    var vertexPixel = map.getPixelFromCoordinate(vertex);
    +    if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <=
    +        this.pixelTolerance_) {
    +      var pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
    +      var pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
    +      var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1);
    +      var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2);
    +      var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
    +      this.snappedToVertex_ = dist <= this.pixelTolerance_;
    +      if (this.snappedToVertex_) {
    +        vertex = squaredDist1 > squaredDist2 ?
    +            closestSegment[1] : closestSegment[0];
    +      }
    +      this.createOrUpdateVertexFeature_(vertex);
    +      var vertexSegments = {};
    +      vertexSegments[goog.getUid(closestSegment)] = true;
    +      var segment;
    +      for (var i = 1, ii = nodes.length; i < ii; ++i) {
    +        segment = nodes[i].segment;
    +        if ((ol.coordinate.equals(closestSegment[0], segment[0]) &&
    +            ol.coordinate.equals(closestSegment[1], segment[1]) ||
    +            (ol.coordinate.equals(closestSegment[0], segment[1]) &&
    +            ol.coordinate.equals(closestSegment[1], segment[0])))) {
    +          vertexSegments[goog.getUid(segment)] = true;
    +        } else {
    +          break;
    +        }
    +      }
    +      this.vertexSegments_ = vertexSegments;
    +      return;
    +    }
    +  }
    +  if (!goog.isNull(this.vertexFeature_)) {
    +    this.overlay_.removeFeature(this.vertexFeature_);
    +    this.vertexFeature_ = null;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.interaction.SegmentDataType} segmentData Segment data.
    + * @param {ol.Coordinate} vertex Vertex.
    + * @private
    + */
    +ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) {
    +  var segment = segmentData.segment;
    +  var feature = segmentData.feature;
    +  var geometry = segmentData.geometry;
    +  var depth = segmentData.depth;
    +  var index = segmentData.index;
    +  var coordinates;
    +
    +  while (vertex.length < geometry.getStride()) {
    +    vertex.push(0);
    +  }
    +
    +  switch (geometry.getType()) {
    +    case ol.geom.GeometryType.MULTI_LINE_STRING:
    +      goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString);
    +      coordinates = geometry.getCoordinates();
    +      coordinates[depth[0]].splice(index + 1, 0, vertex);
    +      break;
    +    case ol.geom.GeometryType.POLYGON:
    +      goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +      coordinates = geometry.getCoordinates();
    +      coordinates[depth[0]].splice(index + 1, 0, vertex);
    +      break;
    +    case ol.geom.GeometryType.MULTI_POLYGON:
    +      goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon);
    +      coordinates = geometry.getCoordinates();
    +      coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex);
    +      break;
    +    case ol.geom.GeometryType.LINE_STRING:
    +      goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +      coordinates = geometry.getCoordinates();
    +      coordinates.splice(index + 1, 0, vertex);
    +      break;
    +    default:
    +      return;
    +  }
    +
    +  geometry.setCoordinates(coordinates);
    +  var rTree = this.rBush_;
    +  goog.asserts.assert(goog.isDef(segment));
    +  rTree.remove(segmentData);
    +  goog.asserts.assert(goog.isDef(index));
    +  this.updateSegmentIndices_(geometry, index, depth, 1);
    +  var newSegmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +    segment: [segment[0], vertex],
    +    feature: feature,
    +    geometry: geometry,
    +    depth: depth,
    +    index: index
    +  });
    +  rTree.insert(ol.extent.boundingExtent(newSegmentData.segment),
    +      newSegmentData);
    +  this.dragSegments_.push([newSegmentData, 1]);
    +
    +  var newSegmentData2 = /** @type {ol.interaction.SegmentDataType} */ ({
    +    segment: [vertex, segment[1]],
    +    feature: feature,
    +    geometry: geometry,
    +    depth: depth,
    +    index: index + 1
    +  });
    +  rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment),
    +      newSegmentData2);
    +  this.dragSegments_.push([newSegmentData2, 0]);
    +};
    +
    +
    +/**
    + * Removes a vertex from all matching features.
    + * @return {boolean} True when a vertex was removed.
    + * @private
    + */
    +ol.interaction.Modify.prototype.removeVertex_ = function() {
    +  var dragSegments = this.dragSegments_;
    +  var segmentsByFeature = {};
    +  var deleted = false;
    +  var component, coordinates, dragSegment, geometry, i, index, left;
    +  var newIndex, newSegment, right, segmentData, uid;
    +  for (i = dragSegments.length - 1; i >= 0; --i) {
    +    dragSegment = dragSegments[i];
    +    segmentData = dragSegment[0];
    +    geometry = segmentData.geometry;
    +    coordinates = geometry.getCoordinates();
    +    uid = goog.getUid(segmentData.feature);
    +    left = right = index = undefined;
    +    if (dragSegment[1] === 0) {
    +      right = segmentData;
    +      index = segmentData.index;
    +    } else if (dragSegment[1] == 1) {
    +      left = segmentData;
    +      index = segmentData.index + 1;
    +    }
    +    if (!(uid in segmentsByFeature)) {
    +      segmentsByFeature[uid] = [left, right, index];
    +    }
    +    newSegment = segmentsByFeature[uid];
    +    if (goog.isDef(left)) {
    +      newSegment[0] = left;
    +    }
    +    if (goog.isDef(right)) {
    +      newSegment[1] = right;
    +    }
    +    if (goog.isDef(newSegment[0]) && goog.isDef(newSegment[1])) {
    +      component = coordinates;
    +      deleted = false;
    +      newIndex = index - 1;
    +      switch (geometry.getType()) {
    +        case ol.geom.GeometryType.MULTI_LINE_STRING:
    +          coordinates[segmentData.depth[0]].splice(index, 1);
    +          deleted = true;
    +          break;
    +        case ol.geom.GeometryType.LINE_STRING:
    +          coordinates.splice(index, 1);
    +          deleted = true;
    +          break;
    +        case ol.geom.GeometryType.MULTI_POLYGON:
    +          component = component[segmentData.depth[1]];
    +          /* falls through */
    +        case ol.geom.GeometryType.POLYGON:
    +          component = component[segmentData.depth[0]];
    +          if (component.length > 4) {
    +            if (index == component.length - 1) {
    +              index = 0;
    +            }
    +            component.splice(index, 1);
    +            deleted = true;
    +            if (index === 0) {
    +              // close the ring again
    +              component.pop();
    +              component.push(component[0]);
    +              newIndex = component.length - 1;
    +            }
    +          }
    +          break;
    +      }
    +
    +      if (deleted) {
    +        this.rBush_.remove(newSegment[0]);
    +        this.rBush_.remove(newSegment[1]);
    +        geometry.setCoordinates(coordinates);
    +        goog.asserts.assert(newIndex >= 0);
    +        var newSegmentData = /** @type {ol.interaction.SegmentDataType} */ ({
    +          depth: segmentData.depth,
    +          feature: segmentData.feature,
    +          geometry: segmentData.geometry,
    +          index: newIndex,
    +          segment: [newSegment[0].segment[0], newSegment[1].segment[1]]
    +        });
    +        this.rBush_.insert(ol.extent.boundingExtent(newSegmentData.segment),
    +            newSegmentData);
    +        this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
    +
    +        this.overlay_.removeFeature(this.vertexFeature_);
    +        this.vertexFeature_ = null;
    +      }
    +    }
    +  }
    +  return deleted;
    +};
    +
    +
    +/**
    + * @param {ol.geom.SimpleGeometry} geometry Geometry.
    + * @param {number} index Index.
    + * @param {Array.<number>|undefined} depth Depth.
    + * @param {number} delta Delta (1 or -1).
    + * @private
    + */
    +ol.interaction.Modify.prototype.updateSegmentIndices_ = function(
    +    geometry, index, depth, delta) {
    +  this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) {
    +    if (segmentDataMatch.geometry === geometry &&
    +        (!goog.isDef(depth) ||
    +        goog.array.equals(segmentDataMatch.depth, depth)) &&
    +        segmentDataMatch.index > index) {
    +      segmentDataMatch.index += delta;
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @return {ol.style.StyleFunction} Styles.
    + */
    +ol.interaction.Modify.getDefaultStyleFunction = function() {
    +  var style = ol.style.createDefaultEditingStyles();
    +  return function(feature, resolution) {
    +    return style[ol.geom.GeometryType.POINT];
    +  };
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/mousewheelzoominteraction.js b/mapbox-gl/ol/ol/interaction/mousewheelzoominteraction.js
    new file mode 100644
    index 0000000000..547ccb577f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/mousewheelzoominteraction.js
    @@ -0,0 +1,118 @@
    +goog.provide('ol.interaction.MouseWheelZoom');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events.MouseWheelEvent');
    +goog.require('goog.events.MouseWheelHandler.EventType');
    +goog.require('goog.math');
    +goog.require('ol');
    +goog.require('ol.Coordinate');
    +goog.require('ol.interaction.Interaction');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom the map by scrolling the mouse wheel.
    + *
    + * @constructor
    + * @extends {ol.interaction.Interaction}
    + * @param {olx.interaction.MouseWheelZoomOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.MouseWheelZoom = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.MouseWheelZoom.handleEvent
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.delta_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 250;
    +
    +  /**
    +   * @private
    +   * @type {?ol.Coordinate}
    +   */
    +  this.lastAnchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.startTime_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.timeoutId_ = undefined;
    +
    +};
    +goog.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction);
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.MouseWheelZoom}
    + * @api
    + */
    +ol.interaction.MouseWheelZoom.handleEvent = function(mapBrowserEvent) {
    +  var stopEvent = false;
    +  if (mapBrowserEvent.type ==
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL) {
    +    var map = mapBrowserEvent.map;
    +    var mouseWheelEvent = mapBrowserEvent.browserEvent;
    +    goog.asserts.assertInstanceof(mouseWheelEvent, goog.events.MouseWheelEvent);
    +
    +    this.lastAnchor_ = mapBrowserEvent.coordinate;
    +    this.delta_ += mouseWheelEvent.deltaY;
    +
    +    if (!goog.isDef(this.startTime_)) {
    +      this.startTime_ = goog.now();
    +    }
    +
    +    var duration = ol.MOUSEWHEELZOOM_TIMEOUT_DURATION;
    +    var timeLeft = Math.max(duration - (goog.now() - this.startTime_), 0);
    +
    +    goog.global.clearTimeout(this.timeoutId_);
    +    this.timeoutId_ = goog.global.setTimeout(
    +        goog.bind(this.doZoom_, this, map), timeLeft);
    +
    +    mapBrowserEvent.preventDefault();
    +    stopEvent = true;
    +  }
    +  return !stopEvent;
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.Map} map Map.
    + */
    +ol.interaction.MouseWheelZoom.prototype.doZoom_ = function(map) {
    +  var maxDelta = ol.MOUSEWHEELZOOM_MAXDELTA;
    +  var delta = goog.math.clamp(this.delta_, -maxDelta, maxDelta);
    +
    +  var view = map.getView();
    +  goog.asserts.assert(!goog.isNull(view));
    +
    +  map.render();
    +  ol.interaction.Interaction.zoomByDelta(map, view, -delta, this.lastAnchor_,
    +      this.duration_);
    +
    +  this.delta_ = 0;
    +  this.lastAnchor_ = null;
    +  this.startTime_ = undefined;
    +  this.timeoutId_ = undefined;
    +};
    diff --git a/mapbox-gl/ol/ol/interaction/pinchrotateinteraction.js b/mapbox-gl/ol/ol/interaction/pinchrotateinteraction.js
    new file mode 100644
    index 0000000000..08911fceb6
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/pinchrotateinteraction.js
    @@ -0,0 +1,169 @@
    +goog.provide('ol.interaction.PinchRotate');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.style');
    +goog.require('ol');
    +goog.require('ol.Coordinate');
    +goog.require('ol.ViewHint');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.interaction.Pointer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to rotate the map by twisting with two fingers
    + * on a touch screen.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.PinchRotateOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.PinchRotate = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.PinchRotate.handleDownEvent_,
    +    handleDragEvent: ol.interaction.PinchRotate.handleDragEvent_,
    +    handleUpEvent: ol.interaction.PinchRotate.handleUpEvent_
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.anchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.lastAngle_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.rotating_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.rotationDelta_ = 0.0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.threshold_ = goog.isDef(options.threshold) ? options.threshold : 0.3;
    +
    +};
    +goog.inherits(ol.interaction.PinchRotate, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.PinchRotate}
    + * @private
    + */
    +ol.interaction.PinchRotate.handleDragEvent_ = function(mapBrowserEvent) {
    +  goog.asserts.assert(this.targetPointers.length >= 2);
    +  var rotationDelta = 0.0;
    +
    +  var touch0 = this.targetPointers[0];
    +  var touch1 = this.targetPointers[1];
    +
    +  // angle between touches
    +  var angle = Math.atan2(
    +      touch1.clientY - touch0.clientY,
    +      touch1.clientX - touch0.clientX);
    +
    +  if (goog.isDef(this.lastAngle_)) {
    +    var delta = angle - this.lastAngle_;
    +    this.rotationDelta_ += delta;
    +    if (!this.rotating_ &&
    +        Math.abs(this.rotationDelta_) > this.threshold_) {
    +      this.rotating_ = true;
    +    }
    +    rotationDelta = delta;
    +  }
    +  this.lastAngle_ = angle;
    +
    +  var map = mapBrowserEvent.map;
    +
    +  // rotate anchor point.
    +  // FIXME: should be the intersection point between the lines:
    +  //     touch0,touch1 and previousTouch0,previousTouch1
    +  var viewportPosition = goog.style.getClientPosition(map.getViewport());
    +  var centroid =
    +      ol.interaction.Pointer.centroid(this.targetPointers);
    +  centroid[0] -= viewportPosition.x;
    +  centroid[1] -= viewportPosition.y;
    +  this.anchor_ = map.getCoordinateFromPixel(centroid);
    +
    +  // rotate
    +  if (this.rotating_) {
    +    var view = map.getView();
    +    var rotation = view.getRotation();
    +    map.render();
    +    ol.interaction.Interaction.rotateWithoutConstraints(map, view,
    +        rotation + rotationDelta, this.anchor_);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.PinchRotate}
    + * @private
    + */
    +ol.interaction.PinchRotate.handleUpEvent_ = function(mapBrowserEvent) {
    +  if (this.targetPointers.length < 2) {
    +    var map = mapBrowserEvent.map;
    +    var view = map.getView();
    +    view.setHint(ol.ViewHint.INTERACTING, -1);
    +    if (this.rotating_) {
    +      var rotation = view.getRotation();
    +      ol.interaction.Interaction.rotate(
    +          map, view, rotation, this.anchor_, ol.ROTATE_ANIMATION_DURATION);
    +    }
    +    return false;
    +  } else {
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.PinchRotate}
    + * @private
    + */
    +ol.interaction.PinchRotate.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (this.targetPointers.length >= 2) {
    +    var map = mapBrowserEvent.map;
    +    this.anchor_ = null;
    +    this.lastAngle_ = undefined;
    +    this.rotating_ = false;
    +    this.rotationDelta_ = 0.0;
    +    if (!this.handlingDownUpSequence) {
    +      map.getView().setHint(ol.ViewHint.INTERACTING, 1);
    +    }
    +    map.render();
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.PinchRotate.prototype.shouldStopEvent = goog.functions.FALSE;
    diff --git a/mapbox-gl/ol/ol/interaction/pinchzoominteraction.js b/mapbox-gl/ol/ol/interaction/pinchzoominteraction.js
    new file mode 100644
    index 0000000000..7d200ba4de
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/pinchzoominteraction.js
    @@ -0,0 +1,157 @@
    +goog.provide('ol.interaction.PinchZoom');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.style');
    +goog.require('ol.Coordinate');
    +goog.require('ol.ViewHint');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.interaction.Pointer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Allows the user to zoom the map by pinching with two fingers
    + * on a touch screen.
    + *
    + * @constructor
    + * @extends {ol.interaction.Pointer}
    + * @param {olx.interaction.PinchZoomOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.PinchZoom = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleDownEvent: ol.interaction.PinchZoom.handleDownEvent_,
    +    handleDragEvent: ol.interaction.PinchZoom.handleDragEvent_,
    +    handleUpEvent: ol.interaction.PinchZoom.handleUpEvent_
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.anchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.duration_ = goog.isDef(options.duration) ? options.duration : 400;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.lastDistance_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.lastScaleDelta_ = 1;
    +
    +};
    +goog.inherits(ol.interaction.PinchZoom, ol.interaction.Pointer);
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.PinchZoom}
    + * @private
    + */
    +ol.interaction.PinchZoom.handleDragEvent_ = function(mapBrowserEvent) {
    +  goog.asserts.assert(this.targetPointers.length >= 2);
    +  var scaleDelta = 1.0;
    +
    +  var touch0 = this.targetPointers[0];
    +  var touch1 = this.targetPointers[1];
    +  var dx = touch0.clientX - touch1.clientX;
    +  var dy = touch0.clientY - touch1.clientY;
    +
    +  // distance between touches
    +  var distance = Math.sqrt(dx * dx + dy * dy);
    +
    +  if (goog.isDef(this.lastDistance_)) {
    +    scaleDelta = this.lastDistance_ / distance;
    +  }
    +  this.lastDistance_ = distance;
    +  if (scaleDelta != 1.0) {
    +    this.lastScaleDelta_ = scaleDelta;
    +  }
    +
    +  var map = mapBrowserEvent.map;
    +  var view = map.getView();
    +  var resolution = view.getResolution();
    +
    +  // scale anchor point.
    +  var viewportPosition = goog.style.getClientPosition(map.getViewport());
    +  var centroid =
    +      ol.interaction.Pointer.centroid(this.targetPointers);
    +  centroid[0] -= viewportPosition.x;
    +  centroid[1] -= viewportPosition.y;
    +  this.anchor_ = map.getCoordinateFromPixel(centroid);
    +
    +  // scale, bypass the resolution constraint
    +  map.render();
    +  ol.interaction.Interaction.zoomWithoutConstraints(
    +      map, view, resolution * scaleDelta, this.anchor_);
    +
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Stop drag sequence?
    + * @this {ol.interaction.PinchZoom}
    + * @private
    + */
    +ol.interaction.PinchZoom.handleUpEvent_ = function(mapBrowserEvent) {
    +  if (this.targetPointers.length < 2) {
    +    var map = mapBrowserEvent.map;
    +    var view = map.getView();
    +    view.setHint(ol.ViewHint.INTERACTING, -1);
    +    var resolution = view.getResolution();
    +    // Zoom to final resolution, with an animation, and provide a
    +    // direction not to zoom out/in if user was pinching in/out.
    +    // Direction is > 0 if pinching out, and < 0 if pinching in.
    +    var direction = this.lastScaleDelta_ - 1;
    +    ol.interaction.Interaction.zoom(map, view, resolution,
    +        this.anchor_, this.duration_, direction);
    +    return false;
    +  } else {
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Start drag sequence?
    + * @this {ol.interaction.PinchZoom}
    + * @private
    + */
    +ol.interaction.PinchZoom.handleDownEvent_ = function(mapBrowserEvent) {
    +  if (this.targetPointers.length >= 2) {
    +    var map = mapBrowserEvent.map;
    +    this.anchor_ = null;
    +    this.lastDistance_ = undefined;
    +    this.lastScaleDelta_ = 1;
    +    if (!this.handlingDownUpSequence) {
    +      map.getView().setHint(ol.ViewHint.INTERACTING, 1);
    +    }
    +    map.render();
    +    return true;
    +  } else {
    +    return false;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.interaction.PinchZoom.prototype.shouldStopEvent = goog.functions.FALSE;
    diff --git a/mapbox-gl/ol/ol/interaction/pointerinteraction.js b/mapbox-gl/ol/ol/interaction/pointerinteraction.js
    new file mode 100644
    index 0000000000..8df39d5747
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/pointerinteraction.js
    @@ -0,0 +1,220 @@
    +goog.provide('ol.interaction.Pointer');
    +
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.MapBrowserPointerEvent');
    +goog.require('ol.Pixel');
    +goog.require('ol.interaction.Interaction');
    +
    +
    +
    +/**
    + * @classdesc
    + * Base class that calls user-defined functions on `down`, `move` and `up`
    + * events. This class also manages "drag sequences".
    + *
    + * When the `handleDownEvent` user function returns `true` a drag sequence is
    + * started. During a drag sequence the `handleDragEvent` user function is
    + * called on `move` events. The drag sequence ends when the `handleUpEvent`
    + * user function is called and returns `false`.
    + *
    + * @constructor
    + * @param {olx.interaction.PointerOptions=} opt_options Options.
    + * @extends {ol.interaction.Interaction}
    + * @api
    + */
    +ol.interaction.Pointer = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var handleEvent = goog.isDef(options.handleEvent) ?
    +      options.handleEvent : ol.interaction.Pointer.handleEvent;
    +
    +  goog.base(this, {
    +    handleEvent: handleEvent
    +  });
    +
    +  /**
    +   * @type {function(ol.MapBrowserPointerEvent):boolean}
    +   * @private
    +   */
    +  this.handleDownEvent_ = goog.isDef(options.handleDownEvent) ?
    +      options.handleDownEvent : ol.interaction.Pointer.handleDownEvent;
    +
    +  /**
    +   * @type {function(ol.MapBrowserPointerEvent)}
    +   * @private
    +   */
    +  this.handleDragEvent_ = goog.isDef(options.handleDragEvent) ?
    +      options.handleDragEvent : ol.interaction.Pointer.handleDragEvent;
    +
    +  /**
    +   * @type {function(ol.MapBrowserPointerEvent)}
    +   * @private
    +   */
    +  this.handleMoveEvent_ = goog.isDef(options.handleMoveEvent) ?
    +      options.handleMoveEvent : ol.interaction.Pointer.handleMoveEvent;
    +
    +  /**
    +   * @type {function(ol.MapBrowserPointerEvent):boolean}
    +   * @private
    +   */
    +  this.handleUpEvent_ = goog.isDef(options.handleUpEvent) ?
    +      options.handleUpEvent : ol.interaction.Pointer.handleUpEvent;
    +
    +  /**
    +   * @type {boolean}
    +   * @protected
    +   */
    +  this.handlingDownUpSequence = false;
    +
    +  /**
    +   * @type {Object.<number, ol.pointer.PointerEvent>}
    +   * @private
    +   */
    +  this.trackedPointers_ = {};
    +
    +  /**
    +   * @type {Array.<ol.pointer.PointerEvent>}
    +   * @protected
    +   */
    +  this.targetPointers = [];
    +
    +};
    +goog.inherits(ol.interaction.Pointer, ol.interaction.Interaction);
    +
    +
    +/**
    + * @param {Array.<ol.pointer.PointerEvent>} pointerEvents
    + * @return {ol.Pixel} Centroid pixel.
    + */
    +ol.interaction.Pointer.centroid = function(pointerEvents) {
    +  var length = pointerEvents.length;
    +  var clientX = 0;
    +  var clientY = 0;
    +  for (var i = 0; i < length; i++) {
    +    clientX += pointerEvents[i].clientX;
    +    clientY += pointerEvents[i].clientY;
    +  }
    +  return [clientX / length, clientY / length];
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Whether the event is a pointerdown, pointerdrag
    + *     or pointerup event.
    + * @private
    + */
    +ol.interaction.Pointer.prototype.isPointerDraggingEvent_ =
    +    function(mapBrowserEvent) {
    +  var type = mapBrowserEvent.type;
    +  return (
    +      type === ol.MapBrowserEvent.EventType.POINTERDOWN ||
    +      type === ol.MapBrowserEvent.EventType.POINTERDRAG ||
    +      type === ol.MapBrowserEvent.EventType.POINTERUP);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @private
    + */
    +ol.interaction.Pointer.prototype.updateTrackedPointers_ =
    +    function(mapBrowserEvent) {
    +  if (this.isPointerDraggingEvent_(mapBrowserEvent)) {
    +    var event = mapBrowserEvent.pointerEvent;
    +
    +    if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) {
    +      delete this.trackedPointers_[event.pointerId];
    +    } else if (mapBrowserEvent.type ==
    +        ol.MapBrowserEvent.EventType.POINTERDOWN) {
    +      this.trackedPointers_[event.pointerId] = event;
    +    } else if (event.pointerId in this.trackedPointers_) {
    +      // update only when there was a pointerdown event for this pointer
    +      this.trackedPointers_[event.pointerId] = event;
    +    }
    +    this.targetPointers = goog.object.getValues(this.trackedPointers_);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.Pointer}
    + */
    +ol.interaction.Pointer.handleDragEvent = goog.nullFunction;
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Capture dragging.
    + * @this {ol.interaction.Pointer}
    + */
    +ol.interaction.Pointer.handleUpEvent = goog.functions.FALSE;
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @return {boolean} Capture dragging.
    + * @this {ol.interaction.Pointer}
    + */
    +ol.interaction.Pointer.handleDownEvent = goog.functions.FALSE;
    +
    +
    +/**
    + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event.
    + * @this {ol.interaction.Pointer}
    + */
    +ol.interaction.Pointer.handleMoveEvent = goog.nullFunction;
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.Pointer}
    + * @api
    + */
    +ol.interaction.Pointer.handleEvent = function(mapBrowserEvent) {
    +  if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) {
    +    return true;
    +  }
    +
    +  var stopEvent = false;
    +  this.updateTrackedPointers_(mapBrowserEvent);
    +  if (this.handlingDownUpSequence) {
    +    if (mapBrowserEvent.type ==
    +        ol.MapBrowserEvent.EventType.POINTERDRAG) {
    +      this.handleDragEvent_(mapBrowserEvent);
    +    } else if (mapBrowserEvent.type ==
    +        ol.MapBrowserEvent.EventType.POINTERUP) {
    +      this.handlingDownUpSequence =
    +          this.handleUpEvent_(mapBrowserEvent);
    +    }
    +  }
    +  if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
    +    var handled = this.handleDownEvent_(mapBrowserEvent);
    +    this.handlingDownUpSequence = handled;
    +    stopEvent = this.shouldStopEvent(handled);
    +  } else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE) {
    +    this.handleMoveEvent_(mapBrowserEvent);
    +  }
    +  return !stopEvent;
    +};
    +
    +
    +/**
    + * This method is used to determine if "down" events should be propagated to
    + * other interactions or should be stopped.
    + *
    + * The method receives the return code of the "handleDownEvent" function.
    + *
    + * By default this function is the "identity" function. It's overidden in
    + * child classes.
    + *
    + * @param {boolean} handled Was the event handled by the interaction?
    + * @return {boolean} Should the event be stopped?
    + * @protected
    + */
    +ol.interaction.Pointer.prototype.shouldStopEvent = goog.functions.identity;
    diff --git a/mapbox-gl/ol/ol/interaction/selectinteraction.js b/mapbox-gl/ol/ol/interaction/selectinteraction.js
    new file mode 100644
    index 0000000000..c205f65f23
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/interaction/selectinteraction.js
    @@ -0,0 +1,312 @@
    +goog.provide('ol.interaction.Select');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.functions');
    +goog.require('ol.CollectionEventType');
    +goog.require('ol.Feature');
    +goog.require('ol.FeatureOverlay');
    +goog.require('ol.events.condition');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.interaction.Interaction');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.SelectEventType = {
    +  /**
    +   * Triggered when feature(s) has been (de)selected.
    +   * @event ol.SelectEvent#select
    +   * @api
    +   */
    +  SELECT: 'select'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.interaction.Select} instances are instances of
    + * this type.
    + *
    + * @param {string} type The event type.
    + * @param {Array.<ol.Feature>} selected Selected features.
    + * @param {Array.<ol.Feature>} deselected Deselected features.
    + * @implements {oli.SelectEvent}
    + * @extends {goog.events.Event}
    + * @constructor
    + */
    +ol.SelectEvent = function(type, selected, deselected) {
    +  goog.base(this, type);
    +
    +  /**
    +   * Selected features array.
    +   * @type {Array.<ol.Feature>}
    +   * @api
    +   */
    +  this.selected = selected;
    +
    +  /**
    +   * Deselected features array.
    +   * @type {Array.<ol.Feature>}
    +   * @api
    +   */
    +  this.deselected = deselected;
    +};
    +goog.inherits(ol.SelectEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * @classdesc
    + * Handles selection of vector data. A {@link ol.FeatureOverlay} is maintained
    + * internally to store the selected feature(s). Which features are selected is
    + * determined by the `condition` option, and optionally the `toggle` or
    + * `add`/`remove` options.
    + *
    + * @constructor
    + * @extends {ol.interaction.Interaction}
    + * @param {olx.interaction.SelectOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.interaction.Select = function(opt_options) {
    +
    +  goog.base(this, {
    +    handleEvent: ol.interaction.Select.handleEvent
    +  });
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.condition_ = goog.isDef(options.condition) ?
    +      options.condition : ol.events.condition.singleClick;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.addCondition_ = goog.isDef(options.addCondition) ?
    +      options.addCondition : ol.events.condition.never;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.removeCondition_ = goog.isDef(options.removeCondition) ?
    +      options.removeCondition : ol.events.condition.never;
    +
    +  /**
    +   * @private
    +   * @type {ol.events.ConditionType}
    +   */
    +  this.toggleCondition_ = goog.isDef(options.toggleCondition) ?
    +      options.toggleCondition : ol.events.condition.shiftKeyOnly;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.multi_ = goog.isDef(options.multi) ? options.multi : false;
    +
    +  var layerFilter;
    +  if (goog.isDef(options.layers)) {
    +    if (goog.isFunction(options.layers)) {
    +      layerFilter = options.layers;
    +    } else {
    +      var layers = options.layers;
    +      layerFilter =
    +          /**
    +           * @param {ol.layer.Layer} layer Layer.
    +           * @return {boolean} Include.
    +           */
    +          function(layer) {
    +        return goog.array.contains(layers, layer);
    +      };
    +    }
    +  } else {
    +    layerFilter = goog.functions.TRUE;
    +  }
    +
    +  /**
    +   * @private
    +   * @type {function(ol.layer.Layer): boolean}
    +   */
    +  this.layerFilter_ = layerFilter;
    +
    +  /**
    +   * @private
    +   * @type {ol.FeatureOverlay}
    +   */
    +  this.featureOverlay_ = new ol.FeatureOverlay({
    +    style: goog.isDef(options.style) ? options.style :
    +        ol.interaction.Select.getDefaultStyleFunction()
    +  });
    +
    +  var features = this.featureOverlay_.getFeatures();
    +  goog.events.listen(features, ol.CollectionEventType.ADD,
    +      this.addFeature_, false, this);
    +  goog.events.listen(features, ol.CollectionEventType.REMOVE,
    +      this.removeFeature_, false, this);
    +
    +};
    +goog.inherits(ol.interaction.Select, ol.interaction.Interaction);
    +
    +
    +/**
    + * Get the selected features.
    + * @return {ol.Collection.<ol.Feature>} Features collection.
    + * @api stable
    + */
    +ol.interaction.Select.prototype.getFeatures = function() {
    +  return this.featureOverlay_.getFeatures();
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
    + * @return {boolean} `false` to stop event propagation.
    + * @this {ol.interaction.Select}
    + * @api
    + */
    +ol.interaction.Select.handleEvent = function(mapBrowserEvent) {
    +  if (!this.condition_(mapBrowserEvent)) {
    +    return true;
    +  }
    +  var add = this.addCondition_(mapBrowserEvent);
    +  var remove = this.removeCondition_(mapBrowserEvent);
    +  var toggle = this.toggleCondition_(mapBrowserEvent);
    +  var set = !add && !remove && !toggle;
    +  var map = mapBrowserEvent.map;
    +  var features = this.featureOverlay_.getFeatures();
    +  var /** @type {Array.<ol.Feature>} */ deselected = [];
    +  var /** @type {Array.<ol.Feature>} */ selected = [];
    +  var change = false;
    +  if (set) {
    +    // Replace the currently selected feature(s) with the feature(s) at the
    +    // pixel, or clear the selected feature(s) if there is no feature at
    +    // the pixel.
    +    map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @param {ol.layer.Layer} layer Layer.
    +         */
    +        function(feature, layer) {
    +          selected.push(feature);
    +          return !this.multi_;
    +        }, this, this.layerFilter_);
    +    if (selected.length > 0 && features.getLength() == 1 &&
    +        features.item(0) == selected[0]) {
    +      // No change
    +    } else {
    +      change = true;
    +      if (features.getLength() !== 0) {
    +        deselected = Array.prototype.concat(features.getArray());
    +        features.clear();
    +      }
    +      features.extend(selected);
    +    }
    +  } else {
    +    // Modify the currently selected feature(s).
    +    map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @param {ol.layer.Layer} layer Layer.
    +         */
    +        function(feature, layer) {
    +          var index = goog.array.indexOf(features.getArray(), feature);
    +          if (index == -1) {
    +            if (add || toggle) {
    +              selected.push(feature);
    +            }
    +          } else {
    +            if (remove || toggle) {
    +              deselected.push(feature);
    +            }
    +          }
    +        }, undefined, this.layerFilter_);
    +    var i;
    +    for (i = deselected.length - 1; i >= 0; --i) {
    +      features.remove(deselected[i]);
    +    }
    +    features.extend(selected);
    +    if (selected.length > 0 || deselected.length > 0) {
    +      change = true;
    +    }
    +  }
    +  if (change) {
    +    this.dispatchEvent(
    +        new ol.SelectEvent(ol.SelectEventType.SELECT, selected, deselected));
    +  }
    +  return ol.events.condition.pointerMove(mapBrowserEvent);
    +};
    +
    +
    +/**
    + * Remove the interaction from its current map, if any,  and attach it to a new
    + * map, if any. Pass `null` to just remove the interaction from the current map.
    + * @param {ol.Map} map Map.
    + * @api stable
    + */
    +ol.interaction.Select.prototype.setMap = function(map) {
    +  var currentMap = this.getMap();
    +  var selectedFeatures = this.featureOverlay_.getFeatures();
    +  if (!goog.isNull(currentMap)) {
    +    selectedFeatures.forEach(currentMap.unskipFeature, currentMap);
    +  }
    +  goog.base(this, 'setMap', map);
    +  this.featureOverlay_.setMap(map);
    +  if (!goog.isNull(map)) {
    +    selectedFeatures.forEach(map.skipFeature, map);
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.style.StyleFunction} Styles.
    + */
    +ol.interaction.Select.getDefaultStyleFunction = function() {
    +  var styles = ol.style.createDefaultEditingStyles();
    +  goog.array.extend(styles[ol.geom.GeometryType.POLYGON],
    +      styles[ol.geom.GeometryType.LINE_STRING]);
    +  goog.array.extend(styles[ol.geom.GeometryType.GEOMETRY_COLLECTION],
    +      styles[ol.geom.GeometryType.LINE_STRING]);
    +
    +  return function(feature, resolution) {
    +    return styles[feature.getGeometry().getType()];
    +  };
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} evt Event.
    + * @private
    + */
    +ol.interaction.Select.prototype.addFeature_ = function(evt) {
    +  var feature = evt.element;
    +  var map = this.getMap();
    +  goog.asserts.assertInstanceof(feature, ol.Feature);
    +  if (!goog.isNull(map)) {
    +    map.skipFeature(feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} evt Event.
    + * @private
    + */
    +ol.interaction.Select.prototype.removeFeature_ = function(evt) {
    +  var feature = evt.element;
    +  var map = this.getMap();
    +  goog.asserts.assertInstanceof(feature, ol.Feature);
    +  if (!goog.isNull(map)) {
    +    map.unskipFeature(feature);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/kinetic.js b/mapbox-gl/ol/ol/kinetic.js
    new file mode 100644
    index 0000000000..c4cf3b99c2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/kinetic.js
    @@ -0,0 +1,159 @@
    +goog.provide('ol.Kinetic');
    +
    +goog.require('ol.Coordinate');
    +goog.require('ol.PreRenderFunction');
    +goog.require('ol.animation');
    +
    +
    +
    +/**
    + * @classdesc
    + * Implementation of inertial deceleration for map movement.
    + *
    + * @constructor
    + * @param {number} decay Rate of decay (must be negative).
    + * @param {number} minVelocity Minimum velocity (pixels/millisecond).
    + * @param {number} delay Delay to consider to calculate the kinetic
    + *     initial values (milliseconds).
    + * @struct
    + * @api
    + */
    +ol.Kinetic = function(decay, minVelocity, delay) {
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.decay_ = decay;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.minVelocity_ = minVelocity;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.delay_ = delay;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.points_ = [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.angle_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.initialVelocity_ = 0;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.Kinetic.prototype.begin = function() {
    +  this.points_.length = 0;
    +  this.angle_ = 0;
    +  this.initialVelocity_ = 0;
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @param {number} y Y.
    + */
    +ol.Kinetic.prototype.update = function(x, y) {
    +  this.points_.push(x, y, goog.now());
    +};
    +
    +
    +/**
    + * @return {boolean} Whether we should do kinetic animation.
    + */
    +ol.Kinetic.prototype.end = function() {
    +  if (this.points_.length < 6) {
    +    // at least 2 points are required (i.e. there must be at least 6 elements
    +    // in the array)
    +    return false;
    +  }
    +  var delay = goog.now() - this.delay_;
    +  var lastIndex = this.points_.length - 3;
    +  if (this.points_[lastIndex + 2] < delay) {
    +    // the last tracked point is too old, which means that the user stopped
    +    // panning before releasing the map
    +    return false;
    +  }
    +
    +  // get the first point which still falls into the delay time
    +  var firstIndex = lastIndex - 3;
    +  while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {
    +    firstIndex -= 3;
    +  }
    +  var duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];
    +  var dx = this.points_[lastIndex] - this.points_[firstIndex];
    +  var dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];
    +  this.angle_ = Math.atan2(dy, dx);
    +  this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;
    +  return this.initialVelocity_ > this.minVelocity_;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} source Source coordinate for the animation.
    + * @return {ol.PreRenderFunction} Pre-render function for kinetic animation.
    + */
    +ol.Kinetic.prototype.pan = function(source) {
    +  var decay = this.decay_;
    +  var initialVelocity = this.initialVelocity_;
    +  var velocity = this.minVelocity_ - initialVelocity;
    +  var duration = this.getDuration_();
    +  var easingFunction = (
    +      /**
    +       * @param {number} t T.
    +       * @return {number} Easing.
    +       */
    +      function(t) {
    +        return initialVelocity * (Math.exp((decay * t) * duration) - 1) /
    +            velocity;
    +      });
    +  return ol.animation.pan({
    +    source: source,
    +    duration: duration,
    +    easing: easingFunction
    +  });
    +};
    +
    +
    +/**
    + * @private
    + * @return {number} Duration of animation (milliseconds).
    + */
    +ol.Kinetic.prototype.getDuration_ = function() {
    +  return Math.log(this.minVelocity_ / this.initialVelocity_) / this.decay_;
    +};
    +
    +
    +/**
    + * @return {number} Total distance travelled (pixels).
    + */
    +ol.Kinetic.prototype.getDistance = function() {
    +  return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
    +};
    +
    +
    +/**
    + * @return {number} Angle of the kinetic panning animation (radians).
    + */
    +ol.Kinetic.prototype.getAngle = function() {
    +  return this.angle_;
    +};
    diff --git a/mapbox-gl/ol/ol/layer/heatmaplayer.js b/mapbox-gl/ol/ol/layer/heatmaplayer.js
    new file mode 100644
    index 0000000000..957da02f76
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/heatmaplayer.js
    @@ -0,0 +1,313 @@
    +goog.provide('ol.layer.Heatmap');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('ol.Object');
    +goog.require('ol.dom');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.EventType');
    +goog.require('ol.style.Icon');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.layer.HeatmapLayerProperty = {
    +  BLUR: 'blur',
    +  GRADIENT: 'gradient',
    +  RADIUS: 'radius'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer for rendering vector data as a heatmap.
    + * Note that any property set in the options is set as a {@link ol.Object}
    + * property on the layer object; for example, setting `title: 'My Title'` in the
    + * options means that `title` is observable, and has get/set accessors.
    + *
    + * @constructor
    + * @extends {ol.layer.Vector}
    + * @fires ol.render.Event
    + * @param {olx.layer.HeatmapOptions=} opt_options Options.
    + * @api
    + */
    +ol.layer.Heatmap = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var baseOptions = goog.object.clone(options);
    +
    +  delete baseOptions.gradient;
    +  delete baseOptions.radius;
    +  delete baseOptions.blur;
    +  delete baseOptions.shadow;
    +  delete baseOptions.weight;
    +  goog.base(this, /** @type {olx.layer.VectorOptions} */ (baseOptions));
    +
    +  /**
    +   * @private
    +   * @type {Uint8ClampedArray}
    +   */
    +  this.gradient_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.shadow_ = goog.isDef(options.shadow) ? options.shadow : 250;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.circleImage_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {Array.<Array.<ol.style.Style>>}
    +   */
    +  this.styleCache_ = null;
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.GRADIENT),
    +      this.handleGradientChanged_, false, this);
    +
    +  this.setGradient(goog.isDef(options.gradient) ?
    +      options.gradient : ol.layer.Heatmap.DEFAULT_GRADIENT);
    +
    +  this.setBlur(goog.isDef(options.blur) ? options.blur : 15);
    +
    +  this.setRadius(goog.isDef(options.radius) ? options.radius : 8);
    +
    +  goog.events.listen(this, [
    +    ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.BLUR),
    +    ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.RADIUS)
    +  ], this.handleStyleChanged_, false, this);
    +
    +  this.handleStyleChanged_();
    +
    +  var weight = goog.isDef(options.weight) ? options.weight : 'weight';
    +  var weightFunction;
    +  if (goog.isString(weight)) {
    +    weightFunction = function(feature) {
    +      return feature.get(weight);
    +    };
    +  } else {
    +    weightFunction = weight;
    +  }
    +  goog.asserts.assert(goog.isFunction(weightFunction));
    +
    +  this.setStyle(goog.bind(function(feature, resolution) {
    +    goog.asserts.assert(!goog.isNull(this.styleCache_));
    +    goog.asserts.assert(goog.isDef(this.circleImage_));
    +    var weight = weightFunction(feature);
    +    var opacity = goog.isDef(weight) ? goog.math.clamp(weight, 0, 1) : 1;
    +    // cast to 8 bits
    +    var index = (255 * opacity) | 0;
    +    var style = this.styleCache_[index];
    +    if (!goog.isDef(style)) {
    +      style = [
    +        new ol.style.Style({
    +          image: new ol.style.Icon({
    +            opacity: opacity,
    +            src: this.circleImage_
    +          })
    +        })
    +      ];
    +      this.styleCache_[index] = style;
    +    }
    +    return style;
    +  }, this));
    +
    +  // For performance reasons, don't sort the features before rendering.
    +  // The render order is not relevant for a heatmap representation.
    +  this.setRenderOrder(null);
    +
    +  goog.events.listen(this, ol.render.EventType.RENDER,
    +      this.handleRender_, false, this);
    +
    +};
    +goog.inherits(ol.layer.Heatmap, ol.layer.Vector);
    +
    +
    +/**
    + * @const
    + * @type {Array.<string>}
    + */
    +ol.layer.Heatmap.DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00'];
    +
    +
    +/**
    + * @param {Array.<string>} colors
    + * @return {Uint8ClampedArray}
    + * @private
    + */
    +ol.layer.Heatmap.createGradient_ = function(colors) {
    +  var width = 1;
    +  var height = 256;
    +  var context = ol.dom.createCanvasContext2D(width, height);
    +
    +  var gradient = context.createLinearGradient(0, 0, width, height);
    +  var step = 1 / (colors.length - 1);
    +  for (var i = 0, ii = colors.length; i < ii; ++i) {
    +    gradient.addColorStop(i * step, colors[i]);
    +  }
    +
    +  context.fillStyle = gradient;
    +  context.fillRect(0, 0, width, height);
    +
    +  return context.getImageData(0, 0, width, height).data;
    +};
    +
    +
    +/**
    + * @return {string}
    + * @private
    + */
    +ol.layer.Heatmap.prototype.createCircle_ = function() {
    +  var radius = this.getRadius();
    +  var blur = this.getBlur();
    +  goog.asserts.assert(goog.isDef(radius) && goog.isDef(blur));
    +  var halfSize = radius + blur + 1;
    +  var size = 2 * halfSize;
    +  var context = ol.dom.createCanvasContext2D(size, size);
    +  context.shadowOffsetX = context.shadowOffsetY = this.shadow_;
    +  context.shadowBlur = blur;
    +  context.shadowColor = '#000';
    +  context.beginPath();
    +  var center = halfSize - this.shadow_;
    +  context.arc(center, center, radius, 0, Math.PI * 2, true);
    +  context.fill();
    +  return context.canvas.toDataURL();
    +};
    +
    +
    +/**
    + * @return {number} Blur size in pixels.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.getBlur = function() {
    +  return /** @type {number} */ (this.get(ol.layer.HeatmapLayerProperty.BLUR));
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'getBlur',
    +    ol.layer.Heatmap.prototype.getBlur);
    +
    +
    +/**
    + * @return {Array.<string>} Colors.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.getGradient = function() {
    +  return /** @type {Array.<string>} */ (
    +      this.get(ol.layer.HeatmapLayerProperty.GRADIENT));
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'getGradient',
    +    ol.layer.Heatmap.prototype.getGradient);
    +
    +
    +/**
    + * @return {number} Radius size in pixel.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.getRadius = function() {
    +  return /** @type {number} */ (this.get(ol.layer.HeatmapLayerProperty.RADIUS));
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'getRadius',
    +    ol.layer.Heatmap.prototype.getRadius);
    +
    +
    +/**
    + * @private
    + */
    +ol.layer.Heatmap.prototype.handleGradientChanged_ = function() {
    +  this.gradient_ = ol.layer.Heatmap.createGradient_(this.getGradient());
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.layer.Heatmap.prototype.handleStyleChanged_ = function() {
    +  this.circleImage_ = this.createCircle_();
    +  this.styleCache_ = new Array(256);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.render.Event} event Post compose event
    + * @private
    + */
    +ol.layer.Heatmap.prototype.handleRender_ = function(event) {
    +  goog.asserts.assert(event.type == ol.render.EventType.RENDER);
    +  goog.asserts.assert(!goog.isNull(this.gradient_));
    +  var context = event.context;
    +  var canvas = context.canvas;
    +  var image = context.getImageData(0, 0, canvas.width, canvas.height);
    +  var view8 = image.data;
    +  var i, ii, alpha, offset;
    +  for (i = 0, ii = view8.length; i < ii; i += 4) {
    +    alpha = view8[i + 3] * 4;
    +    if (alpha) {
    +      view8[i] = this.gradient_[alpha];
    +      view8[i + 1] = this.gradient_[alpha + 1];
    +      view8[i + 2] = this.gradient_[alpha + 2];
    +    }
    +  }
    +  context.putImageData(image, 0, 0);
    +};
    +
    +
    +/**
    + * @param {number} blur Blur size in pixels.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.setBlur = function(blur) {
    +  this.set(ol.layer.HeatmapLayerProperty.BLUR, blur);
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'setBlur',
    +    ol.layer.Heatmap.prototype.setBlur);
    +
    +
    +/**
    + * @param {Array.<string>} colors Gradient.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.setGradient = function(colors) {
    +  this.set(ol.layer.HeatmapLayerProperty.GRADIENT, colors);
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'setGradient',
    +    ol.layer.Heatmap.prototype.setGradient);
    +
    +
    +/**
    + * @param {number} radius Radius size in pixel.
    + * @api
    + * @observable
    + */
    +ol.layer.Heatmap.prototype.setRadius = function(radius) {
    +  this.set(ol.layer.HeatmapLayerProperty.RADIUS, radius);
    +};
    +goog.exportProperty(
    +    ol.layer.Heatmap.prototype,
    +    'setRadius',
    +    ol.layer.Heatmap.prototype.setRadius);
    diff --git a/mapbox-gl/ol/ol/layer/imagelayer.js b/mapbox-gl/ol/ol/layer/imagelayer.js
    new file mode 100644
    index 0000000000..f4b3a23cee
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/imagelayer.js
    @@ -0,0 +1,33 @@
    +goog.provide('ol.layer.Image');
    +
    +goog.require('ol.layer.Layer');
    +
    +
    +
    +/**
    + * @classdesc
    + * Server-rendered images that are available for arbitrary extents and
    + * resolutions.
    + * Note that any property set in the options is set as a {@link ol.Object}
    + * property on the layer object; for example, setting `title: 'My Title'` in the
    + * options means that `title` is observable, and has get/set accessors.
    + *
    + * @constructor
    + * @extends {ol.layer.Layer}
    + * @fires ol.render.Event
    + * @param {olx.layer.ImageOptions=} opt_options Layer options.
    + * @api stable
    + */
    +ol.layer.Image = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +  goog.base(this,  /** @type {olx.layer.LayerOptions} */ (options));
    +};
    +goog.inherits(ol.layer.Image, ol.layer.Layer);
    +
    +
    +/**
    + * @function
    + * @return {ol.source.Image} Source.
    + * @api stable
    + */
    +ol.layer.Image.prototype.getSource;
    diff --git a/mapbox-gl/ol/ol/layer/layer.js b/mapbox-gl/ol/ol/layer/layer.js
    new file mode 100644
    index 0000000000..b50d21942e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/layer.js
    @@ -0,0 +1,148 @@
    +goog.provide('ol.layer.Layer');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.Object');
    +goog.require('ol.layer.Base');
    +goog.require('ol.layer.LayerProperty');
    +goog.require('ol.source.State');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * A visual representation of raster or vector map data.
    + * Layers group together those properties that pertain to how the data is to be
    + * displayed, irrespective of the source of that data.
    + *
    + * @constructor
    + * @extends {ol.layer.Base}
    + * @fires ol.render.Event
    + * @fires change Triggered when the state of the source changes.
    + * @param {olx.layer.LayerOptions} options Layer options.
    + * @api stable
    + */
    +ol.layer.Layer = function(options) {
    +
    +  var baseOptions = goog.object.clone(options);
    +  delete baseOptions.source;
    +
    +  goog.base(this, /** @type {olx.layer.LayerOptions} */ (baseOptions));
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.sourceChangeKey_ = null;
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.layer.LayerProperty.SOURCE),
    +      this.handleSourcePropertyChange_, false, this);
    +
    +  var source = goog.isDef(options.source) ? options.source : null;
    +  this.setSource(source);
    +};
    +goog.inherits(ol.layer.Layer, ol.layer.Base);
    +
    +
    +/**
    + * Return `true` if the layer is visible, and if the passed resolution is
    + * between the layer's minResolution and maxResolution. The comparison is
    + * inclusive for `minResolution` and exclusive for `maxResolution`.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @param {number} resolution Resolution.
    + * @return {boolean} The layer is visible at the given resolution.
    + */
    +ol.layer.Layer.visibleAtResolution = function(layerState, resolution) {
    +  return layerState.visible && resolution >= layerState.minResolution &&
    +      resolution < layerState.maxResolution;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.layer.Layer.prototype.getLayersArray = function(opt_array) {
    +  var array = goog.isDef(opt_array) ? opt_array : [];
    +  array.push(this);
    +  return array;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.layer.Layer.prototype.getLayerStatesArray = function(opt_states) {
    +  var states = goog.isDef(opt_states) ? opt_states : [];
    +  states.push(this.getLayerState());
    +  return states;
    +};
    +
    +
    +/**
    + * Get the layer source.
    + * @return {ol.source.Source} The layer source (or `null` if not yet set).
    + * @observable
    + * @api stable
    + */
    +ol.layer.Layer.prototype.getSource = function() {
    +  var source = this.get(ol.layer.LayerProperty.SOURCE);
    +  return goog.isDef(source) ?
    +      /** @type {ol.source.Source} */ (source) : null;
    +};
    +goog.exportProperty(
    +    ol.layer.Layer.prototype,
    +    'getSource',
    +    ol.layer.Layer.prototype.getSource);
    +
    +
    +/**
    +  * @inheritDoc
    +  */
    +ol.layer.Layer.prototype.getSourceState = function() {
    +  var source = this.getSource();
    +  return goog.isNull(source) ? ol.source.State.UNDEFINED : source.getState();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.layer.Layer.prototype.handleSourceChange_ = function() {
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.layer.Layer.prototype.handleSourcePropertyChange_ = function() {
    +  if (!goog.isNull(this.sourceChangeKey_)) {
    +    goog.events.unlistenByKey(this.sourceChangeKey_);
    +    this.sourceChangeKey_ = null;
    +  }
    +  var source = this.getSource();
    +  if (!goog.isNull(source)) {
    +    this.sourceChangeKey_ = goog.events.listen(source,
    +        goog.events.EventType.CHANGE, this.handleSourceChange_, false, this);
    +  }
    +  this.changed();
    +};
    +
    +
    +/**
    + * Set the layer source.
    + * @param {ol.source.Source} source The layer source.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Layer.prototype.setSource = function(source) {
    +  this.set(ol.layer.LayerProperty.SOURCE, source);
    +};
    +goog.exportProperty(
    +    ol.layer.Layer.prototype,
    +    'setSource',
    +    ol.layer.Layer.prototype.setSource);
    diff --git a/mapbox-gl/ol/ol/layer/layer.jsdoc b/mapbox-gl/ol/ol/layer/layer.jsdoc
    new file mode 100644
    index 0000000000..29e9851de5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/layer.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.layer
    + */
    diff --git a/mapbox-gl/ol/ol/layer/layerbase.js b/mapbox-gl/ol/ol/layer/layerbase.js
    new file mode 100644
    index 0000000000..c1aca6f463
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/layerbase.js
    @@ -0,0 +1,423 @@
    +goog.provide('ol.layer.Base');
    +goog.provide('ol.layer.LayerProperty');
    +goog.provide('ol.layer.LayerState');
    +
    +goog.require('goog.events');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('ol.Object');
    +goog.require('ol.source.State');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.layer.LayerProperty = {
    +  BRIGHTNESS: 'brightness',
    +  CONTRAST: 'contrast',
    +  HUE: 'hue',
    +  OPACITY: 'opacity',
    +  SATURATION: 'saturation',
    +  VISIBLE: 'visible',
    +  EXTENT: 'extent',
    +  MAX_RESOLUTION: 'maxResolution',
    +  MIN_RESOLUTION: 'minResolution',
    +  SOURCE: 'source'
    +};
    +
    +
    +/**
    + * @typedef {{layer: ol.layer.Layer,
    + *            brightness: number,
    + *            contrast: number,
    + *            hue: number,
    + *            opacity: number,
    + *            saturation: number,
    + *            sourceState: ol.source.State,
    + *            visible: boolean,
    + *            extent: (ol.Extent|undefined),
    + *            maxResolution: number,
    + *            minResolution: number}}
    + */
    +ol.layer.LayerState;
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Note that with `ol.layer.Base` and all its subclasses, any property set in
    + * the options is set as a {@link ol.Object} property on the layer object, so
    + * is observable, and has get/set accessors.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @param {olx.layer.BaseOptions} options Layer options.
    + * @api stable
    + */
    +ol.layer.Base = function(options) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {Object.<string, *>}
    +   */
    +  var properties = goog.object.clone(options);
    +  properties[ol.layer.LayerProperty.BRIGHTNESS] =
    +      goog.isDef(options.brightness) ? options.brightness : 0;
    +  properties[ol.layer.LayerProperty.CONTRAST] =
    +      goog.isDef(options.contrast) ? options.contrast : 1;
    +  properties[ol.layer.LayerProperty.HUE] =
    +      goog.isDef(options.hue) ? options.hue : 0;
    +  properties[ol.layer.LayerProperty.OPACITY] =
    +      goog.isDef(options.opacity) ? options.opacity : 1;
    +  properties[ol.layer.LayerProperty.SATURATION] =
    +      goog.isDef(options.saturation) ? options.saturation : 1;
    +  properties[ol.layer.LayerProperty.VISIBLE] =
    +      goog.isDef(options.visible) ? options.visible : true;
    +  properties[ol.layer.LayerProperty.MAX_RESOLUTION] =
    +      goog.isDef(options.maxResolution) ? options.maxResolution : Infinity;
    +  properties[ol.layer.LayerProperty.MIN_RESOLUTION] =
    +      goog.isDef(options.minResolution) ? options.minResolution : 0;
    +
    +  this.setProperties(properties);
    +};
    +goog.inherits(ol.layer.Base, ol.Object);
    +
    +
    +/**
    + * @return {number} The brightness of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.getBrightness = function() {
    +  return /** @type {number} */ (this.get(ol.layer.LayerProperty.BRIGHTNESS));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getBrightness',
    +    ol.layer.Base.prototype.getBrightness);
    +
    +
    +/**
    + * @return {number} The contrast of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.getContrast = function() {
    +  return /** @type {number} */ (this.get(ol.layer.LayerProperty.CONTRAST));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getContrast',
    +    ol.layer.Base.prototype.getContrast);
    +
    +
    +/**
    + * @return {number} The hue of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.getHue = function() {
    +  return /** @type {number} */ (this.get(ol.layer.LayerProperty.HUE));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getHue',
    +    ol.layer.Base.prototype.getHue);
    +
    +
    +/**
    + * @return {ol.layer.LayerState} Layer state.
    + */
    +ol.layer.Base.prototype.getLayerState = function() {
    +  var brightness = this.getBrightness();
    +  var contrast = this.getContrast();
    +  var hue = this.getHue();
    +  var opacity = this.getOpacity();
    +  var saturation = this.getSaturation();
    +  var sourceState = this.getSourceState();
    +  var visible = this.getVisible();
    +  var extent = this.getExtent();
    +  var maxResolution = this.getMaxResolution();
    +  var minResolution = this.getMinResolution();
    +  return {
    +    layer: /** @type {ol.layer.Layer} */ (this),
    +    brightness: goog.math.clamp(brightness, -1, 1),
    +    contrast: Math.max(contrast, 0),
    +    hue: hue,
    +    opacity: goog.math.clamp(opacity, 0, 1),
    +    saturation: Math.max(saturation, 0),
    +    sourceState: sourceState,
    +    visible: visible,
    +    extent: extent,
    +    maxResolution: maxResolution,
    +    minResolution: Math.max(minResolution, 0)
    +  };
    +};
    +
    +
    +/**
    + * @param {Array.<ol.layer.Layer>=} opt_array Array of layers (to be
    + *     modified in place).
    + * @return {Array.<ol.layer.Layer>} Array of layers.
    + */
    +ol.layer.Base.prototype.getLayersArray = goog.abstractMethod;
    +
    +
    +/**
    + * @param {Array.<ol.layer.LayerState>=} opt_states Optional list of layer
    + *     states (to be modified in place).
    + * @return {Array.<ol.layer.LayerState>} List of layer states.
    + */
    +ol.layer.Base.prototype.getLayerStatesArray = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.Extent|undefined} The layer extent.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.getExtent = function() {
    +  return /** @type {ol.Extent|undefined} */ (
    +      this.get(ol.layer.LayerProperty.EXTENT));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getExtent',
    +    ol.layer.Base.prototype.getExtent);
    +
    +
    +/**
    + * @return {number} The maximum resolution of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.getMaxResolution = function() {
    +  return /** @type {number} */ (
    +      this.get(ol.layer.LayerProperty.MAX_RESOLUTION));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getMaxResolution',
    +    ol.layer.Base.prototype.getMaxResolution);
    +
    +
    +/**
    + * @return {number} The minimum resolution of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.getMinResolution = function() {
    +  return /** @type {number} */ (
    +      this.get(ol.layer.LayerProperty.MIN_RESOLUTION));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getMinResolution',
    +    ol.layer.Base.prototype.getMinResolution);
    +
    +
    +/**
    + * @return {number} The opacity of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.getOpacity = function() {
    +  return /** @type {number} */ (this.get(ol.layer.LayerProperty.OPACITY));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getOpacity',
    +    ol.layer.Base.prototype.getOpacity);
    +
    +
    +/**
    + * @return {number} The saturation of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.getSaturation = function() {
    +  return /** @type {number} */ (this.get(ol.layer.LayerProperty.SATURATION));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getSaturation',
    +    ol.layer.Base.prototype.getSaturation);
    +
    +
    +/**
    + * @return {ol.source.State} Source state.
    + */
    +ol.layer.Base.prototype.getSourceState = goog.abstractMethod;
    +
    +
    +/**
    + * @return {boolean} The visibility of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.getVisible = function() {
    +  return /** @type {boolean} */ (this.get(ol.layer.LayerProperty.VISIBLE));
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'getVisible',
    +    ol.layer.Base.prototype.getVisible);
    +
    +
    +/**
    + * Adjust the layer brightness.  A value of -1 will render the layer completely
    + * black.  A value of 0 will leave the brightness unchanged.  A value of 1 will
    + * render the layer completely white.  Other values are linear multipliers on
    + * the effect (values are clamped between -1 and 1).
    + *
    + * The filter effects draft [1] says the brightness function is supposed to
    + * render 0 black, 1 unchanged, and all other values as a linear multiplier.
    + *
    + * The current WebKit implementation clamps values between -1 (black) and 1
    + * (white) [2].  There is a bug open to change the filter effect spec [3].
    + *
    + * TODO: revisit this if the spec is still unmodified before we release
    + *
    + * [1] https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
    + * [2] https://github.com/WebKit/webkit/commit/8f4765e569
    + * [3] https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647
    + *
    + * @param {number} brightness The brightness of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.setBrightness = function(brightness) {
    +  this.set(ol.layer.LayerProperty.BRIGHTNESS, brightness);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setBrightness',
    +    ol.layer.Base.prototype.setBrightness);
    +
    +
    +/**
    + * Adjust the layer contrast.  A value of 0 will render the layer completely
    + * grey.  A value of 1 will leave the contrast unchanged.  Other values are
    + * linear multipliers on the effect (and values over 1 are permitted).
    + *
    + * @param {number} contrast The contrast of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.setContrast = function(contrast) {
    +  this.set(ol.layer.LayerProperty.CONTRAST, contrast);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setContrast',
    +    ol.layer.Base.prototype.setContrast);
    +
    +
    +/**
    + * Apply a hue-rotation to the layer.  A value of 0 will leave the hue
    + * unchanged.  Other values are radians around the color circle.
    + * @param {number} hue The hue of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.setHue = function(hue) {
    +  this.set(ol.layer.LayerProperty.HUE, hue);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setHue',
    +    ol.layer.Base.prototype.setHue);
    +
    +
    +/**
    + * Set the extent at which the layer is visible.  If `undefined`, the layer
    + * will be visible at all extents.
    + * @param {ol.Extent|undefined} extent The extent of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.setExtent = function(extent) {
    +  this.set(ol.layer.LayerProperty.EXTENT, extent);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setExtent',
    +    ol.layer.Base.prototype.setExtent);
    +
    +
    +/**
    + * @param {number} maxResolution The maximum resolution of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.setMaxResolution = function(maxResolution) {
    +  this.set(ol.layer.LayerProperty.MAX_RESOLUTION, maxResolution);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setMaxResolution',
    +    ol.layer.Base.prototype.setMaxResolution);
    +
    +
    +/**
    + * @param {number} minResolution The minimum resolution of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.setMinResolution = function(minResolution) {
    +  this.set(ol.layer.LayerProperty.MIN_RESOLUTION, minResolution);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setMinResolution',
    +    ol.layer.Base.prototype.setMinResolution);
    +
    +
    +/**
    + * @param {number} opacity The opacity of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.setOpacity = function(opacity) {
    +  this.set(ol.layer.LayerProperty.OPACITY, opacity);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setOpacity',
    +    ol.layer.Base.prototype.setOpacity);
    +
    +
    +/**
    + * Adjust layer saturation.  A value of 0 will render the layer completely
    + * unsaturated.  A value of 1 will leave the saturation unchanged.  Other
    + * values are linear multipliers of the effect (and values over 1 are
    + * permitted).
    + *
    + * @param {number} saturation The saturation of the layer.
    + * @observable
    + * @api
    + */
    +ol.layer.Base.prototype.setSaturation = function(saturation) {
    +  this.set(ol.layer.LayerProperty.SATURATION, saturation);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setSaturation',
    +    ol.layer.Base.prototype.setSaturation);
    +
    +
    +/**
    + * @param {boolean} visible The visibility of the layer.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Base.prototype.setVisible = function(visible) {
    +  this.set(ol.layer.LayerProperty.VISIBLE, visible);
    +};
    +goog.exportProperty(
    +    ol.layer.Base.prototype,
    +    'setVisible',
    +    ol.layer.Base.prototype.setVisible);
    diff --git a/mapbox-gl/ol/ol/layer/layergroup.js b/mapbox-gl/ol/ol/layer/layergroup.js
    new file mode 100644
    index 0000000000..b973710a1e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/layergroup.js
    @@ -0,0 +1,236 @@
    +goog.provide('ol.layer.Group');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('ol.Collection');
    +goog.require('ol.CollectionEvent');
    +goog.require('ol.CollectionEventType');
    +goog.require('ol.Object');
    +goog.require('ol.ObjectEventType');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Base');
    +goog.require('ol.source.State');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.layer.GroupProperty = {
    +  LAYERS: 'layers'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * A {@link ol.Collection} of layers that are handled together.
    + *
    + * @constructor
    + * @extends {ol.layer.Base}
    + * @fires change Triggered when the group/Collection changes.
    + * @param {olx.layer.GroupOptions=} opt_options Layer options.
    + * @api stable
    + */
    +ol.layer.Group = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +  var baseOptions = /** @type {olx.layer.GroupOptions} */
    +      (goog.object.clone(options));
    +  delete baseOptions.layers;
    +
    +  var layers = options.layers;
    +
    +  goog.base(this, baseOptions);
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, goog.events.Key>}
    +   */
    +  this.listenerKeys_ = null;
    +
    +  goog.events.listen(this,
    +      ol.Object.getChangeEventType(ol.layer.GroupProperty.LAYERS),
    +      this.handleLayersChanged_, false, this);
    +
    +  if (goog.isDefAndNotNull(layers)) {
    +    if (goog.isArray(layers)) {
    +      layers = new ol.Collection(layers.slice());
    +    } else {
    +      goog.asserts.assertInstanceof(layers, ol.Collection);
    +      layers = layers;
    +    }
    +  } else {
    +    layers = new ol.Collection();
    +  }
    +
    +  this.setLayers(layers);
    +
    +};
    +goog.inherits(ol.layer.Group, ol.layer.Base);
    +
    +
    +/**
    + * @private
    + */
    +ol.layer.Group.prototype.handleLayerChange_ = function() {
    +  if (this.getVisible()) {
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Event.
    + * @private
    + */
    +ol.layer.Group.prototype.handleLayersChanged_ = function(event) {
    +  if (!goog.isNull(this.listenerKeys_)) {
    +    goog.array.forEach(
    +        goog.object.getValues(this.listenerKeys_), goog.events.unlistenByKey);
    +    this.listenerKeys_ = null;
    +  }
    +
    +  var layers = this.getLayers();
    +  if (goog.isDefAndNotNull(layers)) {
    +    this.listenerKeys_ = {
    +      'add': goog.events.listen(layers, ol.CollectionEventType.ADD,
    +          this.handleLayersAdd_, false, this),
    +      'remove': goog.events.listen(layers, ol.CollectionEventType.REMOVE,
    +          this.handleLayersRemove_, false, this)
    +    };
    +
    +    var layersArray = layers.getArray();
    +    var i, ii, layer;
    +    for (i = 0, ii = layersArray.length; i < ii; i++) {
    +      layer = layersArray[i];
    +      this.listenerKeys_[goog.getUid(layer).toString()] =
    +          goog.events.listen(layer,
    +              [ol.ObjectEventType.PROPERTYCHANGE, goog.events.EventType.CHANGE],
    +              this.handleLayerChange_, false, this);
    +    }
    +  }
    +
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} collectionEvent Collection event.
    + * @private
    + */
    +ol.layer.Group.prototype.handleLayersAdd_ = function(collectionEvent) {
    +  var layer = /** @type {ol.layer.Base} */ (collectionEvent.element);
    +  this.listenerKeys_[goog.getUid(layer).toString()] = goog.events.listen(
    +      layer, [ol.ObjectEventType.PROPERTYCHANGE, goog.events.EventType.CHANGE],
    +      this.handleLayerChange_, false, this);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.CollectionEvent} collectionEvent Collection event.
    + * @private
    + */
    +ol.layer.Group.prototype.handleLayersRemove_ = function(collectionEvent) {
    +  var layer = /** @type {ol.layer.Base} */ (collectionEvent.element);
    +  var key = goog.getUid(layer).toString();
    +  goog.events.unlistenByKey(this.listenerKeys_[key]);
    +  delete this.listenerKeys_[key];
    +  this.changed();
    +};
    +
    +
    +/**
    + * @return {!ol.Collection.<ol.layer.Base>} Collection of
    + * {@link ol.layer.Layer layers} that are part of this group.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Group.prototype.getLayers = function() {
    +  return /** @type {!ol.Collection.<ol.layer.Base>} */ (this.get(
    +      ol.layer.GroupProperty.LAYERS));
    +};
    +goog.exportProperty(
    +    ol.layer.Group.prototype,
    +    'getLayers',
    +    ol.layer.Group.prototype.getLayers);
    +
    +
    +/**
    + * @param {!ol.Collection.<ol.layer.Base>} layers Collection of
    + * {@link ol.layer.Layer layers} that are part of this group.
    + * @observable
    + * @api stable
    + */
    +ol.layer.Group.prototype.setLayers = function(layers) {
    +  this.set(ol.layer.GroupProperty.LAYERS, layers);
    +};
    +goog.exportProperty(
    +    ol.layer.Group.prototype,
    +    'setLayers',
    +    ol.layer.Group.prototype.setLayers);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.layer.Group.prototype.getLayersArray = function(opt_array) {
    +  var array = goog.isDef(opt_array) ? opt_array : [];
    +  this.getLayers().forEach(function(layer) {
    +    layer.getLayersArray(array);
    +  });
    +  return array;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.layer.Group.prototype.getLayerStatesArray = function(opt_states) {
    +  var states = goog.isDef(opt_states) ? opt_states : [];
    +
    +  var pos = states.length;
    +
    +  this.getLayers().forEach(function(layer) {
    +    layer.getLayerStatesArray(states);
    +  });
    +
    +  var ownLayerState = this.getLayerState();
    +  var i, ii, layerState;
    +  for (i = pos, ii = states.length; i < ii; i++) {
    +    layerState = states[i];
    +    layerState.brightness = goog.math.clamp(
    +        layerState.brightness + ownLayerState.brightness, -1, 1);
    +    layerState.contrast *= ownLayerState.contrast;
    +    layerState.hue += ownLayerState.hue;
    +    layerState.opacity *= ownLayerState.opacity;
    +    layerState.saturation *= ownLayerState.saturation;
    +    layerState.visible = layerState.visible && ownLayerState.visible;
    +    layerState.maxResolution = Math.min(
    +        layerState.maxResolution, ownLayerState.maxResolution);
    +    layerState.minResolution = Math.max(
    +        layerState.minResolution, ownLayerState.minResolution);
    +    if (goog.isDef(ownLayerState.extent)) {
    +      if (goog.isDef(layerState.extent)) {
    +        layerState.extent = ol.extent.getIntersection(
    +            layerState.extent, ownLayerState.extent);
    +      } else {
    +        layerState.extent = ownLayerState.extent;
    +      }
    +    }
    +  }
    +
    +  return states;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.layer.Group.prototype.getSourceState = function() {
    +  return ol.source.State.READY;
    +};
    diff --git a/mapbox-gl/ol/ol/layer/tilelayer.js b/mapbox-gl/ol/ol/layer/tilelayer.js
    new file mode 100644
    index 0000000000..394fd1a00e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/tilelayer.js
    @@ -0,0 +1,111 @@
    +goog.provide('ol.layer.Tile');
    +
    +goog.require('goog.object');
    +goog.require('ol.layer.Layer');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.layer.TileProperty = {
    +  PRELOAD: 'preload',
    +  USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * For layer sources that provide pre-rendered, tiled images in grids that are
    + * organized by zoom levels for specific resolutions.
    + * Note that any property set in the options is set as a {@link ol.Object}
    + * property on the layer object; for example, setting `title: 'My Title'` in the
    + * options means that `title` is observable, and has get/set accessors.
    + *
    + * @constructor
    + * @extends {ol.layer.Layer}
    + * @fires ol.render.Event
    + * @param {olx.layer.TileOptions=} opt_options Tile layer options.
    + * @api stable
    + */
    +ol.layer.Tile = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var baseOptions = goog.object.clone(options);
    +
    +  delete baseOptions.preload;
    +  delete baseOptions.useInterimTilesOnError;
    +  goog.base(this,  /** @type {olx.layer.LayerOptions} */ (baseOptions));
    +
    +  this.setPreload(goog.isDef(options.preload) ? options.preload : 0);
    +  this.setUseInterimTilesOnError(goog.isDef(options.useInterimTilesOnError) ?
    +      options.useInterimTilesOnError : true);
    +};
    +goog.inherits(ol.layer.Tile, ol.layer.Layer);
    +
    +
    +/**
    + * @return {number} The level to preload tiles up to.
    + * @observable
    + * @api
    + */
    +ol.layer.Tile.prototype.getPreload = function() {
    +  return /** @type {number} */ (this.get(ol.layer.TileProperty.PRELOAD));
    +};
    +goog.exportProperty(
    +    ol.layer.Tile.prototype,
    +    'getPreload',
    +    ol.layer.Tile.prototype.getPreload);
    +
    +
    +/**
    + * @function
    + * @return {ol.source.Tile} Source.
    + * @api stable
    + */
    +ol.layer.Tile.prototype.getSource;
    +
    +
    +/**
    + * @param {number} preload The level to preload tiles up to.
    + * @observable
    + * @api
    + */
    +ol.layer.Tile.prototype.setPreload = function(preload) {
    +  this.set(ol.layer.TileProperty.PRELOAD, preload);
    +};
    +goog.exportProperty(
    +    ol.layer.Tile.prototype,
    +    'setPreload',
    +    ol.layer.Tile.prototype.setPreload);
    +
    +
    +/**
    + * @return {boolean} Use interim tiles on error.
    + * @observable
    + * @api
    + */
    +ol.layer.Tile.prototype.getUseInterimTilesOnError = function() {
    +  return /** @type {boolean} */ (
    +      this.get(ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR));
    +};
    +goog.exportProperty(
    +    ol.layer.Tile.prototype,
    +    'getUseInterimTilesOnError',
    +    ol.layer.Tile.prototype.getUseInterimTilesOnError);
    +
    +
    +/**
    + * @param {boolean} useInterimTilesOnError Use interim tiles on error.
    + * @observable
    + * @api
    + */
    +ol.layer.Tile.prototype.setUseInterimTilesOnError =
    +    function(useInterimTilesOnError) {
    +  this.set(
    +      ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
    +};
    +goog.exportProperty(
    +    ol.layer.Tile.prototype,
    +    'setUseInterimTilesOnError',
    +    ol.layer.Tile.prototype.setUseInterimTilesOnError);
    diff --git a/mapbox-gl/ol/ol/layer/vectorlayer.js b/mapbox-gl/ol/ol/layer/vectorlayer.js
    new file mode 100644
    index 0000000000..90d5e8657a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/layer/vectorlayer.js
    @@ -0,0 +1,158 @@
    +goog.provide('ol.layer.Vector');
    +
    +goog.require('goog.object');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.layer.VectorProperty = {
    +  RENDER_ORDER: 'renderOrder'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Vector data that is rendered client-side.
    + * Note that any property set in the options is set as a {@link ol.Object}
    + * property on the layer object; for example, setting `title: 'My Title'` in the
    + * options means that `title` is observable, and has get/set accessors.
    + *
    + * @constructor
    + * @extends {ol.layer.Layer}
    + * @fires ol.render.Event
    + * @param {olx.layer.VectorOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.layer.Vector = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ?
    +      opt_options : /** @type {olx.layer.VectorOptions} */ ({});
    +
    +  var baseOptions = goog.object.clone(options);
    +
    +  delete baseOptions.style;
    +  delete baseOptions.renderBuffer;
    +  delete baseOptions.updateWhileAnimating;
    +  goog.base(this, /** @type {olx.layer.LayerOptions} */ (baseOptions));
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.renderBuffer_ = goog.isDef(options.renderBuffer) ?
    +      options.renderBuffer : 100;
    +
    +  /**
    +   * User provided style.
    +   * @type {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    +   * @private
    +   */
    +  this.style_ = null;
    +
    +  /**
    +   * Style function for use within the library.
    +   * @type {ol.style.StyleFunction|undefined}
    +   * @private
    +   */
    +  this.styleFunction_ = undefined;
    +
    +  this.setStyle(options.style);
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.updateWhileAnimating_ = goog.isDef(options.updateWhileAnimating) ?
    +      options.updateWhileAnimating : false;
    +
    +};
    +goog.inherits(ol.layer.Vector, ol.layer.Layer);
    +
    +
    +/**
    + * @return {number|undefined} Render buffer.
    + */
    +ol.layer.Vector.prototype.getRenderBuffer = function() {
    +  return this.renderBuffer_;
    +};
    +
    +
    +/**
    + * @return {function(ol.Feature, ol.Feature): number|null|undefined} Render
    + *     order.
    + */
    +ol.layer.Vector.prototype.getRenderOrder = function() {
    +  return /** @type {function(ol.Feature, ol.Feature):number|null|undefined} */ (
    +      this.get(ol.layer.VectorProperty.RENDER_ORDER));
    +};
    +
    +
    +/**
    + * @function
    + * @return {ol.source.Vector} Source.
    + * @api stable
    + */
    +ol.layer.Vector.prototype.getSource;
    +
    +
    +/**
    + * Get the style for features.  This returns whatever was passed to the `style`
    + * option at construction or to the `setStyle` method.
    + * @return {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    + *     Layer style.
    + * @api stable
    + */
    +ol.layer.Vector.prototype.getStyle = function() {
    +  return this.style_;
    +};
    +
    +
    +/**
    + * Get the style function.
    + * @return {ol.style.StyleFunction|undefined} Layer style function.
    + * @api stable
    + */
    +ol.layer.Vector.prototype.getStyleFunction = function() {
    +  return this.styleFunction_;
    +};
    +
    +
    +/**
    + * @return {boolean} Whether the rendered layer should be updated while
    + *     animating.
    + */
    +ol.layer.Vector.prototype.getUpdateWhileAnimating = function() {
    +  return this.updateWhileAnimating_;
    +};
    +
    +
    +/**
    + * @param {function(ol.Feature, ol.Feature):number|null|undefined} renderOrder
    + *     Render order.
    + */
    +ol.layer.Vector.prototype.setRenderOrder = function(renderOrder) {
    +  this.set(ol.layer.VectorProperty.RENDER_ORDER, renderOrder);
    +};
    +
    +
    +/**
    + * Set the style for features.  This can be a single style object, an array
    + * of styles, or a function that takes a feature and resolution and returns
    + * an array of styles. If it is `undefined` the default style is used. If
    + * it is `null` the layer has no style (a `null` style), so only features
    + * that have their own styles will be rendered in the layer. See
    + * {@link ol.style} for information on the default style.
    + * @param {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction|undefined}
    + *     style Layer style.
    + * @api stable
    + */
    +ol.layer.Vector.prototype.setStyle = function(style) {
    +  this.style_ = goog.isDef(style) ? style : ol.style.defaultStyleFunction;
    +  this.styleFunction_ = goog.isNull(style) ?
    +      undefined : ol.style.createStyleFunction(this.style_);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/loadingstrategy.js b/mapbox-gl/ol/ol/loadingstrategy.js
    new file mode 100644
    index 0000000000..418fc62282
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/loadingstrategy.js
    @@ -0,0 +1,60 @@
    +goog.provide('ol.loadingstrategy');
    +
    +goog.require('ol.TileCoord');
    +
    +
    +/**
    + * Strategy function for loading all features with a single request.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @return {Array.<ol.Extent>} Extents.
    + * @api
    + */
    +ol.loadingstrategy.all = function(extent, resolution) {
    +  return [[-Infinity, -Infinity, Infinity, Infinity]];
    +};
    +
    +
    +/**
    + * Strategy function for loading features based on the view's extent and
    + * resolution.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @return {Array.<ol.Extent>} Extents.
    + * @api
    + */
    +ol.loadingstrategy.bbox = function(extent, resolution) {
    +  return [extent];
    +};
    +
    +
    +/**
    + * Creates a strategy function for loading features based on a tile grid.
    + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
    + * @return {function(ol.Extent, number): Array.<ol.Extent>} Loading strategy.
    + * @api
    + */
    +ol.loadingstrategy.createTile = function(tileGrid) {
    +  return (
    +      /**
    +       * @param {ol.Extent} extent Extent.
    +       * @param {number} resolution Resolution.
    +       * @return {Array.<ol.Extent>} Extents.
    +       */
    +      function(extent, resolution) {
    +        var z = tileGrid.getZForResolution(resolution);
    +        var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
    +        /** @type {Array.<ol.Extent>} */
    +        var extents = [];
    +        /** @type {ol.TileCoord} */
    +        var tileCoord = [z, 0, 0];
    +        for (tileCoord[1] = tileRange.minX; tileCoord[1] <= tileRange.maxX;
    +             ++tileCoord[1]) {
    +          for (tileCoord[2] = tileRange.minY; tileCoord[2] <= tileRange.maxY;
    +               ++tileCoord[2]) {
    +            extents.push(tileGrid.getTileCoordExtent(tileCoord));
    +          }
    +        }
    +        return extents;
    +      });
    +};
    diff --git a/mapbox-gl/ol/ol/loadingstrategy.jsdoc b/mapbox-gl/ol/ol/loadingstrategy.jsdoc
    new file mode 100644
    index 0000000000..baecef7850
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/loadingstrategy.jsdoc
    @@ -0,0 +1,5 @@
    +/**
    + * Strategies for loading vector data.
    + * @see ol.source.ServerVector
    + * @namespace ol.loadingstrategy
    + */
    diff --git a/mapbox-gl/ol/ol/map.js b/mapbox-gl/ol/ol/map.js
    new file mode 100644
    index 0000000000..aa3c2a11cf
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/map.js
    @@ -0,0 +1,1610 @@
    +// FIXME recheck layer/map projection compatibility when projection changes
    +// FIXME layer renderers should skip when they can't reproject
    +// FIXME add tilt and height?
    +
    +goog.provide('ol.Map');
    +goog.provide('ol.MapProperty');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.async.AnimationDelay');
    +goog.require('goog.async.nextTick');
    +goog.require('goog.debug.Console');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.dom.ViewportSizeMonitor');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.events.KeyHandler');
    +goog.require('goog.events.KeyHandler.EventType');
    +goog.require('goog.events.MouseWheelHandler');
    +goog.require('goog.events.MouseWheelHandler.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.log');
    +goog.require('goog.log.Level');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.Collection');
    +goog.require('ol.CollectionEventType');
    +goog.require('ol.MapBrowserEvent');
    +goog.require('ol.MapBrowserEvent.EventType');
    +goog.require('ol.MapBrowserEventHandler');
    +goog.require('ol.MapEvent');
    +goog.require('ol.MapEventType');
    +goog.require('ol.Object');
    +goog.require('ol.ObjectEvent');
    +goog.require('ol.ObjectEventType');
    +goog.require('ol.Pixel');
    +goog.require('ol.PostRenderFunction');
    +goog.require('ol.PreRenderFunction');
    +goog.require('ol.RendererType');
    +goog.require('ol.Size');
    +goog.require('ol.TileQueue');
    +goog.require('ol.View');
    +goog.require('ol.ViewHint');
    +goog.require('ol.control');
    +goog.require('ol.extent');
    +goog.require('ol.has');
    +goog.require('ol.interaction');
    +goog.require('ol.layer.Base');
    +goog.require('ol.layer.Group');
    +goog.require('ol.proj');
    +goog.require('ol.proj.common');
    +goog.require('ol.renderer.Map');
    +goog.require('ol.renderer.canvas.Map');
    +goog.require('ol.renderer.dom.Map');
    +goog.require('ol.renderer.webgl.Map');
    +goog.require('ol.structs.PriorityQueue');
    +goog.require('ol.tilecoord');
    +goog.require('ol.vec.Mat4');
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.OL3_URL = 'http://openlayers.org/';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.OL3_LOGO_URL = 'data:image/png;base64,' +
    +    'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBI' +
    +    'WXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAA' +
    +    'AhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszW' +
    +    'WMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvY' +
    +    'asvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvX' +
    +    'H1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1Vk' +
    +    'bMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW' +
    +    '2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLP' +
    +    'VcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqT' +
    +    'acrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaar' +
    +    'ldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+Hi' +
    +    'zeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDn' +
    +    'BAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSF' +
    +    'hYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJ' +
    +    'REFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxC' +
    +    'Brb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe' +
    +    '0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8' +
    +    'a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7a' +
    +    'hgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCn' +
    +    'B3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDg' +
    +    'q82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC';
    +
    +
    +/**
    + * @type {Array.<ol.RendererType>}
    + */
    +ol.DEFAULT_RENDERER_TYPES = [
    +  ol.RendererType.CANVAS,
    +  ol.RendererType.WEBGL,
    +  ol.RendererType.DOM
    +];
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.MapProperty = {
    +  LAYERGROUP: 'layergroup',
    +  SIZE: 'size',
    +  TARGET: 'target',
    +  VIEW: 'view'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * The map is the core component of OpenLayers. For a map to render, a view,
    + * one or more layers, and a target container are needed:
    + *
    + *     var map = new ol.Map({
    + *       view: new ol.View({
    + *         center: [0, 0],
    + *         zoom: 1
    + *       }),
    + *       layers: [
    + *         new ol.layer.Tile({
    + *           source: new ol.source.MapQuest({layer: 'osm'})
    + *         })
    + *       ],
    + *       target: 'map'
    + *     });
    + *
    + * The above snippet creates a map using a {@link ol.layer.Tile} to display
    + * {@link ol.source.MapQuest} OSM data and render it to a DOM element with the
    + * id `map`.
    + *
    + * The constructor places a viewport container (with CSS class name
    + * `ol-viewport`) in the target element (see `getViewport()`), and then two
    + * further elements within the viewport: one with CSS class name
    + * `ol-overlaycontainer-stopevent` for controls and some overlays, and one with
    + * CSS class name `ol-overlaycontainer` for other overlays (see the `stopEvent`
    + * option of {@link ol.Overlay} for the difference). The map itself is placed in
    + * a further element within the viewport, either DOM or Canvas, depending on the
    + * renderer.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @param {olx.MapOptions} options Map options.
    + * @fires ol.MapBrowserEvent
    + * @fires ol.MapEvent
    + * @fires ol.render.Event#postcompose
    + * @fires ol.render.Event#precompose
    + * @api stable
    + */
    +ol.Map = function(options) {
    +
    +  goog.base(this);
    +
    +  var optionsInternal = ol.Map.createOptionsInternal(options);
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.loadTilesWhileAnimating_ = goog.isDef(options.loadTilesWhileAnimating) ?
    +      options.loadTilesWhileAnimating : false;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.loadTilesWhileInteracting_ =
    +      goog.isDef(options.loadTilesWhileInteracting) ?
    +          options.loadTilesWhileInteracting : false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.pixelRatio_ = goog.isDef(options.pixelRatio) ?
    +      options.pixelRatio : ol.has.DEVICE_PIXEL_RATIO;
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.logos_ = optionsInternal.logos;
    +
    +  /**
    +   * @private
    +   * @type {goog.async.AnimationDelay}
    +   */
    +  this.animationDelay_ =
    +      new goog.async.AnimationDelay(this.renderFrame_, undefined, this);
    +  this.registerDisposable(this.animationDelay_);
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.coordinateToPixelMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.pixelToCoordinateMatrix_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.frameIndex_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {?olx.FrameState}
    +   */
    +  this.frameState_ = null;
    +
    +  /**
    +   * The extent at the previous 'moveend' event.
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.previousExtent_ = ol.extent.createEmpty();
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.viewPropertyListenerKey_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<goog.events.Key>}
    +   */
    +  this.layerGroupPropertyListenerKeys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.viewport_ = goog.dom.createDom(goog.dom.TagName.DIV, 'ol-viewport');
    +  this.viewport_.style.position = 'relative';
    +  this.viewport_.style.overflow = 'hidden';
    +  this.viewport_.style.width = '100%';
    +  this.viewport_.style.height = '100%';
    +  // prevent page zoom on IE >= 10 browsers
    +  this.viewport_.style.msTouchAction = 'none';
    +  if (ol.has.TOUCH) {
    +    this.viewport_.className = 'ol-touch';
    +  }
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.overlayContainer_ = goog.dom.createDom(goog.dom.TagName.DIV,
    +      'ol-overlaycontainer');
    +  goog.dom.appendChild(this.viewport_, this.overlayContainer_);
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.overlayContainerStopEvent_ = goog.dom.createDom(goog.dom.TagName.DIV,
    +      'ol-overlaycontainer-stopevent');
    +  goog.events.listen(this.overlayContainerStopEvent_, [
    +    goog.events.EventType.CLICK,
    +    goog.events.EventType.DBLCLICK,
    +    goog.events.EventType.MOUSEDOWN,
    +    goog.events.EventType.TOUCHSTART,
    +    goog.events.EventType.MSPOINTERDOWN,
    +    ol.MapBrowserEvent.EventType.POINTERDOWN,
    +    goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel'
    +  ], goog.events.Event.stopPropagation);
    +  goog.dom.appendChild(this.viewport_, this.overlayContainerStopEvent_);
    +
    +  var mapBrowserEventHandler = new ol.MapBrowserEventHandler(this);
    +  goog.events.listen(mapBrowserEventHandler,
    +      goog.object.getValues(ol.MapBrowserEvent.EventType),
    +      this.handleMapBrowserEvent, false, this);
    +  this.registerDisposable(mapBrowserEventHandler);
    +
    +  /**
    +   * @private
    +   * @type {Element|Document}
    +   */
    +  this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.KeyHandler}
    +   */
    +  this.keyHandler_ = new goog.events.KeyHandler();
    +  goog.events.listen(this.keyHandler_, goog.events.KeyHandler.EventType.KEY,
    +      this.handleBrowserEvent, false, this);
    +  this.registerDisposable(this.keyHandler_);
    +
    +  var mouseWheelHandler = new goog.events.MouseWheelHandler(this.viewport_);
    +  goog.events.listen(mouseWheelHandler,
    +      goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
    +      this.handleBrowserEvent, false, this);
    +  this.registerDisposable(mouseWheelHandler);
    +
    +  /**
    +   * @type {ol.Collection.<ol.control.Control>}
    +   * @private
    +   */
    +  this.controls_ = optionsInternal.controls;
    +
    +  /**
    +   * @type {ol.Collection.<ol.interaction.Interaction>}
    +   * @private
    +   */
    +  this.interactions_ = optionsInternal.interactions;
    +
    +  /**
    +   * @type {ol.Collection.<ol.Overlay>}
    +   * @private
    +   */
    +  this.overlays_ = optionsInternal.overlays;
    +
    +  /**
    +   * @type {ol.renderer.Map}
    +   * @private
    +   */
    +  this.renderer_ =
    +      new optionsInternal.rendererConstructor(this.viewport_, this);
    +  this.registerDisposable(this.renderer_);
    +
    +  /**
    +   * @private
    +   */
    +  this.viewportSizeMonitor_ = new goog.dom.ViewportSizeMonitor();
    +  this.registerDisposable(this.viewportSizeMonitor_);
    +
    +  goog.events.listen(this.viewportSizeMonitor_, goog.events.EventType.RESIZE,
    +      this.updateSize, false, this);
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.focus_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.PreRenderFunction>}
    +   */
    +  this.preRenderFunctions_ = [];
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.PostRenderFunction>}
    +   */
    +  this.postRenderFunctions_ = [];
    +
    +  /**
    +   * @private
    +   * @type {ol.TileQueue}
    +   */
    +  this.tileQueue_ = new ol.TileQueue(
    +      goog.bind(this.getTilePriority, this),
    +      goog.bind(this.handleTileChange_, this));
    +
    +  /**
    +   * Uids of features to skip at rendering time.
    +   * @type {Object.<string, boolean>}
    +   * @private
    +   */
    +  this.skippedFeatureUids_ = {};
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.MapProperty.LAYERGROUP),
    +      this.handleLayerGroupChanged_, false, this);
    +  goog.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.VIEW),
    +      this.handleViewChanged_, false, this);
    +  goog.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE),
    +      this.handleSizeChanged_, false, this);
    +  goog.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.TARGET),
    +      this.handleTargetChanged_, false, this);
    +
    +  // setProperties will trigger the rendering of the map if the map
    +  // is "defined" already.
    +  this.setProperties(optionsInternal.values);
    +
    +  this.controls_.forEach(
    +      /**
    +       * @param {ol.control.Control} control Control.
    +       * @this {ol.Map}
    +       */
    +      function(control) {
    +        control.setMap(this);
    +      }, this);
    +
    +  goog.events.listen(this.controls_, ol.CollectionEventType.ADD,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(this);
    +      }, false, this);
    +
    +  goog.events.listen(this.controls_, ol.CollectionEventType.REMOVE,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(null);
    +      }, false, this);
    +
    +  this.interactions_.forEach(
    +      /**
    +       * @param {ol.interaction.Interaction} interaction Interaction.
    +       * @this {ol.Map}
    +       */
    +      function(interaction) {
    +        interaction.setMap(this);
    +      }, this);
    +
    +  goog.events.listen(this.interactions_, ol.CollectionEventType.ADD,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(this);
    +      }, false, this);
    +
    +  goog.events.listen(this.interactions_, ol.CollectionEventType.REMOVE,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(null);
    +      }, false, this);
    +
    +  this.overlays_.forEach(
    +      /**
    +       * @param {ol.Overlay} overlay Overlay.
    +       * @this {ol.Map}
    +       */
    +      function(overlay) {
    +        overlay.setMap(this);
    +      }, this);
    +
    +  goog.events.listen(this.overlays_, ol.CollectionEventType.ADD,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(this);
    +      }, false, this);
    +
    +  goog.events.listen(this.overlays_, ol.CollectionEventType.REMOVE,
    +      /**
    +       * @param {ol.CollectionEvent} event Collection event.
    +       */
    +      function(event) {
    +        event.element.setMap(null);
    +      }, false, this);
    +
    +};
    +goog.inherits(ol.Map, ol.Object);
    +
    +
    +/**
    + * Add the given control to the map.
    + * @param {ol.control.Control} control Control.
    + * @api stable
    + */
    +ol.Map.prototype.addControl = function(control) {
    +  var controls = this.getControls();
    +  goog.asserts.assert(goog.isDef(controls));
    +  controls.push(control);
    +};
    +
    +
    +/**
    + * Add the given interaction to the map.
    + * @param {ol.interaction.Interaction} interaction Interaction to add.
    + * @api stable
    + */
    +ol.Map.prototype.addInteraction = function(interaction) {
    +  var interactions = this.getInteractions();
    +  goog.asserts.assert(goog.isDef(interactions));
    +  interactions.push(interaction);
    +};
    +
    +
    +/**
    + * Adds the given layer to the top of this map.
    + * @param {ol.layer.Base} layer Layer.
    + * @api stable
    + */
    +ol.Map.prototype.addLayer = function(layer) {
    +  var layers = this.getLayerGroup().getLayers();
    +  layers.push(layer);
    +};
    +
    +
    +/**
    + * Add the given overlay to the map.
    + * @param {ol.Overlay} overlay Overlay.
    + * @api stable
    + */
    +ol.Map.prototype.addOverlay = function(overlay) {
    +  var overlays = this.getOverlays();
    +  goog.asserts.assert(goog.isDef(overlays));
    +  overlays.push(overlay);
    +};
    +
    +
    +/**
    + * Add functions to be called before rendering. This can be used for attaching
    + * animations before updating the map's view.  The {@link ol.animation}
    + * namespace provides several static methods for creating prerender functions.
    + * @param {...ol.PreRenderFunction} var_args Any number of pre-render functions.
    + * @api
    + */
    +ol.Map.prototype.beforeRender = function(var_args) {
    +  this.render();
    +  Array.prototype.push.apply(this.preRenderFunctions_, arguments);
    +};
    +
    +
    +/**
    + * @param {ol.PreRenderFunction} preRenderFunction Pre-render function.
    + * @return {boolean} Whether the preRenderFunction has been found and removed.
    + */
    +ol.Map.prototype.removePreRenderFunction = function(preRenderFunction) {
    +  return goog.array.remove(this.preRenderFunctions_, preRenderFunction);
    +};
    +
    +
    +/**
    + *
    + * @inheritDoc
    + */
    +ol.Map.prototype.disposeInternal = function() {
    +  goog.dom.removeNode(this.viewport_);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Detect features that intersect a pixel on the viewport, and execute a
    + * callback with each intersecting feature. Layers included in the detection can
    + * be configured through `opt_layerFilter`. Feature overlays will always be
    + * included in the detection.
    + * @param {ol.Pixel} pixel Pixel.
    + * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature
    + *     callback. If the detected feature is not on a layer, but on a
    + *     {@link ol.FeatureOverlay}, then the 2nd argument to this function will
    + *     be `null`. To stop detection, callback functions can return a truthy
    + *     value.
    + * @param {S=} opt_this Value to use as `this` when executing `callback`.
    + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer
    + *     filter function, only layers which are visible and for which this
    + *     function returns `true` will be tested for features. By default, all
    + *     visible layers will be tested. Feature overlays will always be tested.
    + * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`.
    + * @return {T|undefined} Callback result, i.e. the return value of last
    + * callback execution, or the first truthy callback return value.
    + * @template S,T,U
    + * @api stable
    + */
    +ol.Map.prototype.forEachFeatureAtPixel =
    +    function(pixel, callback, opt_this, opt_layerFilter, opt_this2) {
    +  if (goog.isNull(this.frameState_)) {
    +    return;
    +  }
    +  var coordinate = this.getCoordinateFromPixel(pixel);
    +  var thisArg = goog.isDef(opt_this) ? opt_this : null;
    +  var layerFilter = goog.isDef(opt_layerFilter) ?
    +      opt_layerFilter : goog.functions.TRUE;
    +  var thisArg2 = goog.isDef(opt_this2) ? opt_this2 : null;
    +  return this.renderer_.forEachFeatureAtCoordinate(
    +      coordinate, this.frameState_, callback, thisArg,
    +      layerFilter, thisArg2);
    +};
    +
    +
    +/**
    + * Detect layers that have a color value at a pixel on the viewport, and
    + * execute a callback with each matching layer. Layers included in the
    + * detection can be configured through `opt_layerFilter`. Feature overlays will
    + * always be included in the detection.
    + * @param {ol.Pixel} pixel Pixel.
    + * @param {function(this: S, ol.layer.Layer): T} callback Layer
    + *     callback. If the detected feature is not on a layer, but on a
    + *     {@link ol.FeatureOverlay}, then the argument to this function will
    + *     be `null`. To stop detection, callback functions can return a truthy
    + *     value.
    + * @param {S=} opt_this Value to use as `this` when executing `callback`.
    + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer
    + *     filter function, only layers which are visible and for which this
    + *     function returns `true` will be tested for features. By default, all
    + *     visible layers will be tested. Feature overlays will always be tested.
    + * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`.
    + * @return {T|undefined} Callback result, i.e. the return value of last
    + * callback execution, or the first truthy callback return value.
    + * @template S,T,U
    + * @api stable
    + */
    +ol.Map.prototype.forEachLayerAtPixel =
    +    function(pixel, callback, opt_this, opt_layerFilter, opt_this2) {
    +  if (goog.isNull(this.frameState_)) {
    +    return;
    +  }
    +  var thisArg = goog.isDef(opt_this) ? opt_this : null;
    +  var layerFilter = goog.isDef(opt_layerFilter) ?
    +      opt_layerFilter : goog.functions.TRUE;
    +  var thisArg2 = goog.isDef(opt_this2) ? opt_this2 : null;
    +  return this.renderer_.forEachLayerAtPixel(
    +      pixel, this.frameState_, callback, thisArg,
    +      layerFilter, thisArg2);
    +};
    +
    +
    +/**
    + * Detect if features intersect a pixel on the viewport. Layers included in the
    + * detection can be configured through `opt_layerFilter`. Feature overlays will
    + * always be included in the detection.
    + * @param {ol.Pixel} pixel Pixel.
    + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer
    + *     filter function, only layers which are visible and for which this
    + *     function returns `true` will be tested for features. By default, all
    + *     visible layers will be tested. Feature overlays will always be tested.
    + * @param {U=} opt_this Value to use as `this` when executing `layerFilter`.
    + * @return {boolean} Is there a feature at the given pixel?
    + * @template U
    + * @api
    + */
    +ol.Map.prototype.hasFeatureAtPixel =
    +    function(pixel, opt_layerFilter, opt_this) {
    +  if (goog.isNull(this.frameState_)) {
    +    return false;
    +  }
    +  var coordinate = this.getCoordinateFromPixel(pixel);
    +  var layerFilter = goog.isDef(opt_layerFilter) ?
    +      opt_layerFilter : goog.functions.TRUE;
    +  var thisArg = goog.isDef(opt_this) ? opt_this : null;
    +  return this.renderer_.hasFeatureAtCoordinate(
    +      coordinate, this.frameState_, layerFilter, thisArg);
    +};
    +
    +
    +/**
    + * Returns the geographical coordinate for a browser event.
    + * @param {Event} event Event.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.Map.prototype.getEventCoordinate = function(event) {
    +  return this.getCoordinateFromPixel(this.getEventPixel(event));
    +};
    +
    +
    +/**
    + * Returns the map pixel position for a browser event relative to the viewport.
    + * @param {Event} event Event.
    + * @return {ol.Pixel} Pixel.
    + * @api stable
    + */
    +ol.Map.prototype.getEventPixel = function(event) {
    +  // goog.style.getRelativePosition is based on event.targetTouches,
    +  // but touchend and touchcancel events have no targetTouches when
    +  // the last finger is removed from the screen.
    +  // So we ourselves compute the position of touch events.
    +  // See https://github.com/google/closure-library/pull/323
    +  if (goog.isDef(event.changedTouches)) {
    +    var touch = event.changedTouches[0];
    +    var viewportPosition = goog.style.getClientPosition(this.viewport_);
    +    return [
    +      touch.clientX - viewportPosition.x,
    +      touch.clientY - viewportPosition.y
    +    ];
    +  } else {
    +    var eventPosition = goog.style.getRelativePosition(event, this.viewport_);
    +    return [eventPosition.x, eventPosition.y];
    +  }
    +};
    +
    +
    +/**
    + * Get the target in which this map is rendered.
    + * Note that this returns what is entered as an option or in setTarget:
    + * if that was an element, it returns an element; if a string, it returns that.
    + * @return {Element|string|undefined} The Element or id of the Element that the
    + *     map is rendered in.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.getTarget = function() {
    +  return /** @type {Element|string|undefined} */ (
    +      this.get(ol.MapProperty.TARGET));
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'getTarget',
    +    ol.Map.prototype.getTarget);
    +
    +
    +/**
    + * Get the DOM element into which this map is rendered. In contrast to
    + * `getTarget` this method always return an `Element`, or `null` if the
    + * map has no target.
    + * @return {Element} The element that the map is rendered in.
    + * @api
    + */
    +ol.Map.prototype.getTargetElement = function() {
    +  var target = this.getTarget();
    +  return goog.isDef(target) ? goog.dom.getElement(target) : null;
    +};
    +
    +
    +/**
    + * @param {ol.Pixel} pixel Pixel.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.Map.prototype.getCoordinateFromPixel = function(pixel) {
    +  var frameState = this.frameState_;
    +  if (goog.isNull(frameState)) {
    +    return null;
    +  } else {
    +    var vec2 = pixel.slice();
    +    return ol.vec.Mat4.multVec2(frameState.pixelToCoordinateMatrix, vec2, vec2);
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.Collection.<ol.control.Control>} Controls.
    + * @api stable
    + */
    +ol.Map.prototype.getControls = function() {
    +  return this.controls_;
    +};
    +
    +
    +/**
    + * @return {ol.Collection.<ol.Overlay>} Overlays.
    + * @api stable
    + */
    +ol.Map.prototype.getOverlays = function() {
    +  return this.overlays_;
    +};
    +
    +
    +/**
    + * Gets the collection of {@link ol.interaction.Interaction} instances
    + * associated with this map. Modifying this collection changes the interactions
    + * associated with the map.
    + *
    + * Interactions are used for e.g. pan, zoom and rotate.
    + * @return {ol.Collection.<ol.interaction.Interaction>} Interactions.
    + * @api stable
    + */
    +ol.Map.prototype.getInteractions = function() {
    +  return this.interactions_;
    +};
    +
    +
    +/**
    + * Get the layergroup associated with this map.
    + * @return {ol.layer.Group} A layer group containing the layers in this map.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.getLayerGroup = function() {
    +  return /** @type {ol.layer.Group} */ (this.get(ol.MapProperty.LAYERGROUP));
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'getLayerGroup',
    +    ol.Map.prototype.getLayerGroup);
    +
    +
    +/**
    + * Get the collection of layers associated with this map.
    + * @return {!ol.Collection.<ol.layer.Base>} Layers.
    + * @api stable
    + */
    +ol.Map.prototype.getLayers = function() {
    +  var layers = this.getLayerGroup().getLayers();
    +  return layers;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {ol.Pixel} Pixel.
    + * @api stable
    + */
    +ol.Map.prototype.getPixelFromCoordinate = function(coordinate) {
    +  var frameState = this.frameState_;
    +  if (goog.isNull(frameState)) {
    +    return null;
    +  } else {
    +    var vec2 = coordinate.slice(0, 2);
    +    return ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, vec2, vec2);
    +  }
    +};
    +
    +
    +/**
    + * Get the map renderer.
    + * @return {ol.renderer.Map} Renderer
    + */
    +ol.Map.prototype.getRenderer = function() {
    +  return this.renderer_;
    +};
    +
    +
    +/**
    + * Get the size of this map.
    + * @return {ol.Size|undefined} The size in pixels of the map in the DOM.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.getSize = function() {
    +  return /** @type {ol.Size|undefined} */ (this.get(ol.MapProperty.SIZE));
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'getSize',
    +    ol.Map.prototype.getSize);
    +
    +
    +/**
    + * Get the view associated with this map. A view manages properties such as
    + * center and resolution.
    + * @return {ol.View} The view that controls this map.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.getView = function() {
    +  return /** @type {ol.View} */ (this.get(ol.MapProperty.VIEW));
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'getView',
    +    ol.Map.prototype.getView);
    +
    +
    +/**
    + * @return {Element} Viewport.
    + * @api stable
    + */
    +ol.Map.prototype.getViewport = function() {
    +  return this.viewport_;
    +};
    +
    +
    +/**
    + * @return {Element} The map's overlay container. Elements added to this
    + * container will let mousedown and touchstart events through to the map, so
    + * clicks and gestures on an overlay will trigger {@link ol.MapBrowserEvent}
    + * events.
    + */
    +ol.Map.prototype.getOverlayContainer = function() {
    +  return this.overlayContainer_;
    +};
    +
    +
    +/**
    + * @return {Element} The map's overlay container. Elements added to this
    + * container won't let mousedown and touchstart events through to the map, so
    + * clicks and gestures on an overlay don't trigger any
    + * {@link ol.MapBrowserEvent}.
    + */
    +ol.Map.prototype.getOverlayContainerStopEvent = function() {
    +  return this.overlayContainerStopEvent_;
    +};
    +
    +
    +/**
    + * @param {ol.Tile} tile Tile.
    + * @param {string} tileSourceKey Tile source key.
    + * @param {ol.Coordinate} tileCenter Tile center.
    + * @param {number} tileResolution Tile resolution.
    + * @return {number} Tile priority.
    + */
    +ol.Map.prototype.getTilePriority =
    +    function(tile, tileSourceKey, tileCenter, tileResolution) {
    +  // Filter out tiles at higher zoom levels than the current zoom level, or that
    +  // are outside the visible extent.
    +  var frameState = this.frameState_;
    +  if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) {
    +    return ol.structs.PriorityQueue.DROP;
    +  }
    +  var coordKey = ol.tilecoord.toString(tile.tileCoord);
    +  if (!frameState.wantedTiles[tileSourceKey][coordKey]) {
    +    return ol.structs.PriorityQueue.DROP;
    +  }
    +  // Prioritize the highest zoom level tiles closest to the focus.
    +  // Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
    +  // Within a zoom level, tiles are prioritized by the distance in pixels
    +  // between the center of the tile and the focus.  The factor of 65536 means
    +  // that the prioritization should behave as desired for tiles up to
    +  // 65536 * Math.log(2) = 45426 pixels from the focus.
    +  var deltaX = tileCenter[0] - frameState.focus[0];
    +  var deltaY = tileCenter[1] - frameState.focus[1];
    +  return 65536 * Math.log(tileResolution) +
    +      Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
    +};
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @param {string=} opt_type Type.
    + */
    +ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) {
    +  var type = opt_type || browserEvent.type;
    +  var mapBrowserEvent = new ol.MapBrowserEvent(type, this, browserEvent);
    +  this.handleMapBrowserEvent(mapBrowserEvent);
    +};
    +
    +
    +/**
    + * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle.
    + */
    +ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
    +  if (goog.isNull(this.frameState_)) {
    +    // With no view defined, we cannot translate pixels into geographical
    +    // coordinates so interactions cannot be used.
    +    return;
    +  }
    +  this.focus_ = mapBrowserEvent.coordinate;
    +  mapBrowserEvent.frameState = this.frameState_;
    +  var interactions = this.getInteractions();
    +  goog.asserts.assert(goog.isDef(interactions));
    +  var interactionsArray = interactions.getArray();
    +  var i;
    +  if (this.dispatchEvent(mapBrowserEvent) !== false) {
    +    for (i = interactionsArray.length - 1; i >= 0; i--) {
    +      var interaction = interactionsArray[i];
    +      if (!interaction.getActive()) {
    +        continue;
    +      }
    +      var cont = interaction.handleEvent(mapBrowserEvent);
    +      if (!cont) {
    +        break;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Map.prototype.handlePostRender = function() {
    +
    +  var frameState = this.frameState_;
    +
    +  // Manage the tile queue
    +  // Image loads are expensive and a limited resource, so try to use them
    +  // efficiently:
    +  // * When the view is static we allow a large number of parallel tile loads
    +  //   to complete the frame as quickly as possible.
    +  // * When animating or interacting, image loads can cause janks, so we reduce
    +  //   the maximum number of loads per frame and limit the number of parallel
    +  //   tile loads to remain reactive to view changes and to reduce the chance of
    +  //   loading tiles that will quickly disappear from view.
    +  var tileQueue = this.tileQueue_;
    +  if (!tileQueue.isEmpty()) {
    +    var maxTotalLoading = 16;
    +    var maxNewLoads = maxTotalLoading;
    +    var tileSourceCount = 0;
    +    if (!goog.isNull(frameState)) {
    +      var hints = frameState.viewHints;
    +      if (hints[ol.ViewHint.ANIMATING]) {
    +        maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0;
    +        maxNewLoads = 2;
    +      }
    +      if (hints[ol.ViewHint.INTERACTING]) {
    +        maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0;
    +        maxNewLoads = 2;
    +      }
    +      tileSourceCount = goog.object.getCount(frameState.wantedTiles);
    +    }
    +    maxTotalLoading *= tileSourceCount;
    +    maxNewLoads *= tileSourceCount;
    +    if (tileQueue.getTilesLoading() < maxTotalLoading) {
    +      tileQueue.reprioritize(); // FIXME only call if view has changed
    +      tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
    +    }
    +  }
    +
    +  var postRenderFunctions = this.postRenderFunctions_;
    +  var i, ii;
    +  for (i = 0, ii = postRenderFunctions.length; i < ii; ++i) {
    +    postRenderFunctions[i](this, frameState);
    +  }
    +  postRenderFunctions.length = 0;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleSizeChanged_ = function() {
    +  this.render();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleTargetChanged_ = function() {
    +  // target may be undefined, null, a string or an Element.
    +  // If it's a string we convert it to an Element before proceeding.
    +  // If it's not now an Element we remove the viewport from the DOM.
    +  // If it's an Element we append the viewport element to it.
    +
    +  var targetElement = this.getTargetElement();
    +
    +  this.keyHandler_.detach();
    +
    +  if (goog.isNull(targetElement)) {
    +    goog.dom.removeNode(this.viewport_);
    +  } else {
    +    goog.dom.appendChild(targetElement, this.viewport_);
    +
    +    var keyboardEventTarget = goog.isNull(this.keyboardEventTarget_) ?
    +        targetElement : this.keyboardEventTarget_;
    +    this.keyHandler_.attach(keyboardEventTarget);
    +  }
    +
    +  this.updateSize();
    +  // updateSize calls setSize, so no need to call this.render
    +  // ourselves here.
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleTileChange_ = function() {
    +  this.render();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleViewPropertyChanged_ = function() {
    +  this.render();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleViewChanged_ = function() {
    +  if (!goog.isNull(this.viewPropertyListenerKey_)) {
    +    goog.events.unlistenByKey(this.viewPropertyListenerKey_);
    +    this.viewPropertyListenerKey_ = null;
    +  }
    +  var view = this.getView();
    +  if (!goog.isNull(view)) {
    +    this.viewPropertyListenerKey_ = goog.events.listen(
    +        view, ol.ObjectEventType.PROPERTYCHANGE,
    +        this.handleViewPropertyChanged_, false, this);
    +  }
    +  this.render();
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Event.
    + * @private
    + */
    +ol.Map.prototype.handleLayerGroupMemberChanged_ = function(event) {
    +  goog.asserts.assertInstanceof(event, goog.events.Event);
    +  this.render();
    +};
    +
    +
    +/**
    + * @param {ol.ObjectEvent} event Event.
    + * @private
    + */
    +ol.Map.prototype.handleLayerGroupPropertyChanged_ = function(event) {
    +  goog.asserts.assertInstanceof(event, ol.ObjectEvent);
    +  this.render();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.Map.prototype.handleLayerGroupChanged_ = function() {
    +  if (!goog.isNull(this.layerGroupPropertyListenerKeys_)) {
    +    var length = this.layerGroupPropertyListenerKeys_.length;
    +    for (var i = 0; i < length; ++i) {
    +      goog.events.unlistenByKey(this.layerGroupPropertyListenerKeys_[i]);
    +    }
    +    this.layerGroupPropertyListenerKeys_ = null;
    +  }
    +  var layerGroup = this.getLayerGroup();
    +  if (goog.isDefAndNotNull(layerGroup)) {
    +    this.layerGroupPropertyListenerKeys_ = [
    +      goog.events.listen(
    +          layerGroup, ol.ObjectEventType.PROPERTYCHANGE,
    +          this.handleLayerGroupPropertyChanged_, false, this),
    +      goog.events.listen(
    +          layerGroup, goog.events.EventType.CHANGE,
    +          this.handleLayerGroupMemberChanged_, false, this)
    +    ];
    +  }
    +  this.render();
    +};
    +
    +
    +/**
    + * Returns `true` if the map is defined, `false` otherwise. The map is defined
    + * if it is contained in `document`, visible, has non-zero height and width, and
    + * has a defined view.
    + * @return {boolean} Is defined.
    + */
    +ol.Map.prototype.isDef = function() {
    +  if (!goog.dom.contains(document, this.viewport_)) {
    +    return false;
    +  }
    +  if (!goog.style.isElementShown(this.viewport_)) {
    +    return false;
    +  }
    +  var size = this.getSize();
    +  if (!goog.isDefAndNotNull(size) || size[0] <= 0 || size[1] <= 0) {
    +    return false;
    +  }
    +  var view = this.getView();
    +  if (goog.isNull(view) || !view.isDef()) {
    +    return false;
    +  }
    +  return true;
    +};
    +
    +
    +/**
    + * @return {boolean} Is rendered.
    + */
    +ol.Map.prototype.isRendered = function() {
    +  return !goog.isNull(this.frameState_);
    +};
    +
    +
    +/**
    + * Requests an immediate render in a synchronous manner.
    + * @api stable
    + */
    +ol.Map.prototype.renderSync = function() {
    +  this.animationDelay_.fire();
    +};
    +
    +
    +/**
    + * Requests a render frame; rendering will effectively occur at the next browser
    + * animation frame.
    + * @api stable
    + */
    +ol.Map.prototype.render = function() {
    +  if (!this.animationDelay_.isActive()) {
    +    this.animationDelay_.start();
    +  }
    +};
    +
    +
    +/**
    + * Remove the given control from the map.
    + * @param {ol.control.Control} control Control.
    + * @return {ol.control.Control|undefined} The removed control (or undefined
    + *     if the control was not found).
    + * @api stable
    + */
    +ol.Map.prototype.removeControl = function(control) {
    +  var controls = this.getControls();
    +  goog.asserts.assert(goog.isDef(controls));
    +  if (goog.isDef(controls.remove(control))) {
    +    return control;
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * Remove the given interaction from the map.
    + * @param {ol.interaction.Interaction} interaction Interaction to remove.
    + * @return {ol.interaction.Interaction|undefined} The removed interaction (or
    + *     undefined if the interaction was not found).
    + * @api stable
    + */
    +ol.Map.prototype.removeInteraction = function(interaction) {
    +  var removed;
    +  var interactions = this.getInteractions();
    +  goog.asserts.assert(goog.isDef(interactions));
    +  if (goog.isDef(interactions.remove(interaction))) {
    +    removed = interaction;
    +  }
    +  return removed;
    +};
    +
    +
    +/**
    + * Removes the given layer from the map.
    + * @param {ol.layer.Base} layer Layer.
    + * @return {ol.layer.Base|undefined} The removed layer (or undefined if the
    + *     layer was not found).
    + * @api stable
    + */
    +ol.Map.prototype.removeLayer = function(layer) {
    +  var layers = this.getLayerGroup().getLayers();
    +  return layers.remove(layer);
    +};
    +
    +
    +/**
    + * Remove the given overlay from the map.
    + * @param {ol.Overlay} overlay Overlay.
    + * @return {ol.Overlay|undefined} The removed overlay (or undefined
    + *     if the overlay was not found).
    + * @api stable
    + */
    +ol.Map.prototype.removeOverlay = function(overlay) {
    +  var overlays = this.getOverlays();
    +  goog.asserts.assert(goog.isDef(overlays));
    +  if (goog.isDef(overlays.remove(overlay))) {
    +    return overlay;
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {number} time Time.
    + * @private
    + */
    +ol.Map.prototype.renderFrame_ = function(time) {
    +
    +  var i, ii, viewState;
    +
    +  /**
    +   * Check whether a size has non-zero width and height.  Note that this
    +   * function is here because the compiler doesn't recognize that size is
    +   * defined in the frameState assignment below when the same code is inline in
    +   * the condition below.  The compiler inlines this function itself, so the
    +   * resulting code is the same.
    +   *
    +   * @param {ol.Size} size The size to test.
    +   * @return {boolean} Has non-zero width and height.
    +   */
    +  function hasArea(size) {
    +    return size[0] > 0 && size[1] > 0;
    +  }
    +
    +  var size = this.getSize();
    +  var view = this.getView();
    +  /** @type {?olx.FrameState} */
    +  var frameState = null;
    +  if (goog.isDef(size) && hasArea(size) &&
    +      !goog.isNull(view) && view.isDef()) {
    +    var viewHints = view.getHints();
    +    var layerStatesArray = this.getLayerGroup().getLayerStatesArray();
    +    var layerStates = {};
    +    for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    +      layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
    +    }
    +    viewState = view.getState();
    +    frameState = /** @type {olx.FrameState} */ ({
    +      animate: false,
    +      attributions: {},
    +      coordinateToPixelMatrix: this.coordinateToPixelMatrix_,
    +      extent: null,
    +      focus: goog.isNull(this.focus_) ? viewState.center : this.focus_,
    +      index: this.frameIndex_++,
    +      layerStates: layerStates,
    +      layerStatesArray: layerStatesArray,
    +      logos: goog.object.clone(this.logos_),
    +      pixelRatio: this.pixelRatio_,
    +      pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_,
    +      postRenderFunctions: [],
    +      size: size,
    +      skippedFeatureUids: this.skippedFeatureUids_,
    +      tileQueue: this.tileQueue_,
    +      time: time,
    +      usedTiles: {},
    +      viewState: viewState,
    +      viewHints: viewHints,
    +      wantedTiles: {}
    +    });
    +  }
    +
    +  if (!goog.isNull(frameState)) {
    +    var preRenderFunctions = this.preRenderFunctions_;
    +    var n = 0, preRenderFunction;
    +    for (i = 0, ii = preRenderFunctions.length; i < ii; ++i) {
    +      preRenderFunction = preRenderFunctions[i];
    +      if (preRenderFunction(this, frameState)) {
    +        preRenderFunctions[n++] = preRenderFunction;
    +      }
    +    }
    +    preRenderFunctions.length = n;
    +
    +    frameState.extent = ol.extent.getForViewAndSize(viewState.center,
    +        viewState.resolution, viewState.rotation, frameState.size);
    +  }
    +
    +  this.frameState_ = frameState;
    +  this.renderer_.renderFrame(frameState);
    +
    +  if (!goog.isNull(frameState)) {
    +    if (frameState.animate) {
    +      this.render();
    +    }
    +    Array.prototype.push.apply(
    +        this.postRenderFunctions_, frameState.postRenderFunctions);
    +
    +    var idle = this.preRenderFunctions_.length === 0 &&
    +        !frameState.viewHints[ol.ViewHint.ANIMATING] &&
    +        !frameState.viewHints[ol.ViewHint.INTERACTING] &&
    +        !ol.extent.equals(frameState.extent, this.previousExtent_);
    +
    +    if (idle) {
    +      this.dispatchEvent(
    +          new ol.MapEvent(ol.MapEventType.MOVEEND, this, frameState));
    +      ol.extent.clone(frameState.extent, this.previousExtent_);
    +    }
    +  }
    +
    +  this.dispatchEvent(
    +      new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState));
    +
    +  goog.async.nextTick(this.handlePostRender, this);
    +
    +};
    +
    +
    +/**
    + * Sets the layergroup of this map.
    + * @param {ol.layer.Group} layerGroup A layer group containing the layers in
    + *     this map.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.setLayerGroup = function(layerGroup) {
    +  this.set(ol.MapProperty.LAYERGROUP, layerGroup);
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'setLayerGroup',
    +    ol.Map.prototype.setLayerGroup);
    +
    +
    +/**
    + * Set the size of this map.
    + * @param {ol.Size|undefined} size The size in pixels of the map in the DOM.
    + * @observable
    + * @api
    + */
    +ol.Map.prototype.setSize = function(size) {
    +  this.set(ol.MapProperty.SIZE, size);
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'setSize',
    +    ol.Map.prototype.setSize);
    +
    +
    +/**
    + * Set the target element to render this map into.
    + * @param {Element|string|undefined} target The Element or id of the Element
    + *     that the map is rendered in.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.setTarget = function(target) {
    +  this.set(ol.MapProperty.TARGET, target);
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'setTarget',
    +    ol.Map.prototype.setTarget);
    +
    +
    +/**
    + * Set the view for this map.
    + * @param {ol.View} view The view that controls this map.
    + * @observable
    + * @api stable
    + */
    +ol.Map.prototype.setView = function(view) {
    +  this.set(ol.MapProperty.VIEW, view);
    +};
    +goog.exportProperty(
    +    ol.Map.prototype,
    +    'setView',
    +    ol.Map.prototype.setView);
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.Map.prototype.skipFeature = function(feature) {
    +  var featureUid = goog.getUid(feature).toString();
    +  this.skippedFeatureUids_[featureUid] = true;
    +  this.render();
    +};
    +
    +
    +/**
    + * Force a recalculation of the map viewport size.  This should be called when
    + * third-party code changes the size of the map viewport.
    + * @api stable
    + */
    +ol.Map.prototype.updateSize = function() {
    +  var targetElement = this.getTargetElement();
    +
    +  if (goog.isNull(targetElement)) {
    +    this.setSize(undefined);
    +  } else {
    +    var size = goog.style.getContentBoxSize(targetElement);
    +    this.setSize([size.width, size.height]);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.Map.prototype.unskipFeature = function(feature) {
    +  var featureUid = goog.getUid(feature).toString();
    +  delete this.skippedFeatureUids_[featureUid];
    +  this.render();
    +};
    +
    +
    +/**
    + * @typedef {{controls: ol.Collection.<ol.control.Control>,
    + *            interactions: ol.Collection.<ol.interaction.Interaction>,
    + *            keyboardEventTarget: (Element|Document),
    + *            logos: Object,
    + *            overlays: ol.Collection.<ol.Overlay>,
    + *            rendererConstructor:
    + *                function(new: ol.renderer.Map, Element, ol.Map),
    + *            values: Object.<string, *>}}
    + */
    +ol.MapOptionsInternal;
    +
    +
    +/**
    + * @param {olx.MapOptions} options Map options.
    + * @return {ol.MapOptionsInternal} Internal map options.
    + */
    +ol.Map.createOptionsInternal = function(options) {
    +
    +  /**
    +   * @type {Element|Document}
    +   */
    +  var keyboardEventTarget = null;
    +  if (goog.isDef(options.keyboardEventTarget)) {
    +    // cannot use goog.dom.getElement because its argument cannot be
    +    // of type Document
    +    keyboardEventTarget = goog.isString(options.keyboardEventTarget) ?
    +        document.getElementById(options.keyboardEventTarget) :
    +        options.keyboardEventTarget;
    +  }
    +
    +  /**
    +   * @type {Object.<string, *>}
    +   */
    +  var values = {};
    +
    +  var logos = {};
    +  if (!goog.isDef(options.logo) ||
    +      (goog.isBoolean(options.logo) && options.logo)) {
    +    logos[ol.OL3_LOGO_URL] = ol.OL3_URL;
    +  } else {
    +    var logo = options.logo;
    +    if (goog.isString(logo)) {
    +      logos[logo] = '';
    +    } else if (goog.isObject(logo)) {
    +      goog.asserts.assertString(logo.href);
    +      goog.asserts.assertString(logo.src);
    +      logos[logo.src] = logo.href;
    +    }
    +  }
    +
    +  var layerGroup = (options.layers instanceof ol.layer.Group) ?
    +      options.layers : new ol.layer.Group({layers: options.layers});
    +  values[ol.MapProperty.LAYERGROUP] = layerGroup;
    +
    +  values[ol.MapProperty.TARGET] = options.target;
    +
    +  values[ol.MapProperty.VIEW] = goog.isDef(options.view) ?
    +      options.view : new ol.View();
    +
    +  /**
    +   * @type {function(new: ol.renderer.Map, Element, ol.Map)}
    +   */
    +  var rendererConstructor = ol.renderer.Map;
    +
    +  /**
    +   * @type {Array.<ol.RendererType>}
    +   */
    +  var rendererTypes;
    +  if (goog.isDef(options.renderer)) {
    +    if (goog.isArray(options.renderer)) {
    +      rendererTypes = options.renderer;
    +    } else if (goog.isString(options.renderer)) {
    +      rendererTypes = [options.renderer];
    +    } else {
    +      goog.asserts.fail('Incorrect format for renderer option');
    +    }
    +  } else {
    +    rendererTypes = ol.DEFAULT_RENDERER_TYPES;
    +  }
    +
    +  var i, ii;
    +  for (i = 0, ii = rendererTypes.length; i < ii; ++i) {
    +    /** @type {ol.RendererType} */
    +    var rendererType = rendererTypes[i];
    +    if (ol.ENABLE_CANVAS && rendererType == ol.RendererType.CANVAS) {
    +      if (ol.has.CANVAS) {
    +        rendererConstructor = ol.renderer.canvas.Map;
    +        break;
    +      }
    +    } else if (ol.ENABLE_DOM && rendererType == ol.RendererType.DOM) {
    +      if (ol.has.DOM) {
    +        rendererConstructor = ol.renderer.dom.Map;
    +        break;
    +      }
    +    } else if (ol.ENABLE_WEBGL && rendererType == ol.RendererType.WEBGL) {
    +      if (ol.has.WEBGL) {
    +        rendererConstructor = ol.renderer.webgl.Map;
    +        break;
    +      }
    +    }
    +  }
    +
    +  var controls;
    +  if (goog.isDef(options.controls)) {
    +    if (goog.isArray(options.controls)) {
    +      controls = new ol.Collection(options.controls.slice());
    +    } else {
    +      goog.asserts.assertInstanceof(options.controls, ol.Collection);
    +      controls = options.controls;
    +    }
    +  } else {
    +    controls = ol.control.defaults();
    +  }
    +
    +  var interactions;
    +  if (goog.isDef(options.interactions)) {
    +    if (goog.isArray(options.interactions)) {
    +      interactions = new ol.Collection(options.interactions.slice());
    +    } else {
    +      goog.asserts.assertInstanceof(options.interactions, ol.Collection);
    +      interactions = options.interactions;
    +    }
    +  } else {
    +    interactions = ol.interaction.defaults();
    +  }
    +
    +  var overlays;
    +  if (goog.isDef(options.overlays)) {
    +    if (goog.isArray(options.overlays)) {
    +      overlays = new ol.Collection(options.overlays.slice());
    +    } else {
    +      goog.asserts.assertInstanceof(options.overlays, ol.Collection);
    +      overlays = options.overlays;
    +    }
    +  } else {
    +    overlays = new ol.Collection();
    +  }
    +
    +  return {
    +    controls: controls,
    +    interactions: interactions,
    +    keyboardEventTarget: keyboardEventTarget,
    +    logos: logos,
    +    overlays: overlays,
    +    rendererConstructor: rendererConstructor,
    +    values: values
    +  };
    +
    +};
    +
    +
    +ol.proj.common.add();
    +
    +
    +if (goog.DEBUG) {
    +  (function() {
    +    goog.debug.Console.autoInstall();
    +    var logger = goog.log.getLogger('ol');
    +    logger.setLevel(goog.log.Level.FINEST);
    +  })();
    +}
    diff --git a/mapbox-gl/ol/ol/mapbrowserevent.js b/mapbox-gl/ol/ol/mapbrowserevent.js
    new file mode 100644
    index 0000000000..03a1d576fc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/mapbrowserevent.js
    @@ -0,0 +1,529 @@
    +goog.provide('ol.MapBrowserEvent');
    +goog.provide('ol.MapBrowserEvent.EventType');
    +goog.provide('ol.MapBrowserEventHandler');
    +goog.provide('ol.MapBrowserPointerEvent');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol');
    +goog.require('ol.Coordinate');
    +goog.require('ol.MapEvent');
    +goog.require('ol.Pixel');
    +goog.require('ol.pointer.PointerEvent');
    +goog.require('ol.pointer.PointerEventHandler');
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted as map browser events are instances of this type.
    + * See {@link ol.Map} for which events trigger a map browser event.
    + *
    + * @constructor
    + * @extends {ol.MapEvent}
    + * @implements {oli.MapBrowserEvent}
    + * @param {string} type Event type.
    + * @param {ol.Map} map Map.
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @param {boolean=} opt_dragging Is the map currently being dragged?
    + * @param {?olx.FrameState=} opt_frameState Frame state.
    + */
    +ol.MapBrowserEvent = function(type, map, browserEvent, opt_dragging,
    +    opt_frameState) {
    +
    +  goog.base(this, type, map, opt_frameState);
    +
    +  /**
    +   * @const
    +   * @type {goog.events.BrowserEvent}
    +   */
    +  this.browserEvent = browserEvent;
    +
    +  /**
    +   * @const
    +   * @type {Event}
    +   * @api stable
    +   */
    +  this.originalEvent = browserEvent.getBrowserEvent();
    +
    +  /**
    +   * @type {ol.Pixel}
    +   * @api stable
    +   */
    +  this.pixel = map.getEventPixel(this.originalEvent);
    +
    +  /**
    +   * @type {ol.Coordinate}
    +   * @api stable
    +   */
    +  this.coordinate = map.getCoordinateFromPixel(this.pixel);
    +
    +  /**
    +   * Indicates if the map is currently being dragged. Only set for
    +   * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`.
    +   *
    +   * @type {boolean}
    +   * @api stable
    +   */
    +  this.dragging = goog.isDef(opt_dragging) ? opt_dragging : false;
    +
    +};
    +goog.inherits(ol.MapBrowserEvent, ol.MapEvent);
    +
    +
    +/**
    + * Prevents the default browser action.
    + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
    + * @override
    + * @api stable
    + */
    +ol.MapBrowserEvent.prototype.preventDefault = function() {
    +  goog.base(this, 'preventDefault');
    +  this.browserEvent.preventDefault();
    +};
    +
    +
    +/**
    + * Prevents further propagation of the current event.
    + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
    + * @override
    + * @api stable
    + */
    +ol.MapBrowserEvent.prototype.stopPropagation = function() {
    +  goog.base(this, 'stopPropagation');
    +  this.browserEvent.stopPropagation();
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.MapBrowserEvent}
    + * @param {string} type Event type.
    + * @param {ol.Map} map Map.
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @param {boolean=} opt_dragging Is the map currently being dragged?
    + * @param {?olx.FrameState=} opt_frameState Frame state.
    + */
    +ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_dragging,
    +    opt_frameState) {
    +
    +  goog.base(this, type, map, pointerEvent.browserEvent, opt_dragging,
    +      opt_frameState);
    +
    +  /**
    +   * @const
    +   * @type {ol.pointer.PointerEvent}
    +   */
    +  this.pointerEvent = pointerEvent;
    +
    +};
    +goog.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent);
    +
    +
    +
    +/**
    + * @param {ol.Map} map The map with the viewport to listen to events on.
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + */
    +ol.MapBrowserEventHandler = function(map) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * This is the element that we will listen to the real events on.
    +   * @type {ol.Map}
    +   * @private
    +   */
    +  this.map_ = map;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.clickTimeoutId_ = 0;
    +
    +  /**
    +   * @type {boolean}
    +   * @private
    +   */
    +  this.dragging_ = false;
    +
    +  /**
    +   * @type {Array.<goog.events.Key>}
    +   * @private
    +   */
    +  this.dragListenerKeys_ = null;
    +
    +  /**
    +   * @type {goog.events.Key}
    +   * @private
    +   */
    +  this.pointerdownListenerKey_ = null;
    +
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    /**
    +     * @type {goog.events.Key}
    +     * @private
    +     */
    +    this.ieDblclickListenerKey_ = null;
    +  }
    +
    +  /**
    +   * The most recent "down" type event (or null if none have occurred).
    +   * Set on pointerdown.
    +   * @type {ol.pointer.PointerEvent}
    +   * @private
    +   */
    +  this.down_ = null;
    +
    +  var element = this.map_.getViewport();
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.activePointers_ = 0;
    +
    +  /**
    +   * @type {Object.<number, boolean>}
    +   * @private
    +   */
    +  this.trackedTouches_ = {};
    +
    +  /**
    +   * Event handler which generates pointer events for
    +   * the viewport element.
    +   *
    +   * @type {ol.pointer.PointerEventHandler}
    +   * @private
    +   */
    +  this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element);
    +
    +  /**
    +   * Event handler which generates pointer events for
    +   * the document (used when dragging).
    +   *
    +   * @type {ol.pointer.PointerEventHandler}
    +   * @private
    +   */
    +  this.documentPointerEventHandler_ = null;
    +
    +  this.pointerdownListenerKey_ = goog.events.listen(this.pointerEventHandler_,
    +      ol.pointer.EventType.POINTERDOWN,
    +      this.handlePointerDown_, false, this);
    +
    +  this.relayedListenerKey_ = goog.events.listen(this.pointerEventHandler_,
    +      ol.pointer.EventType.POINTERMOVE,
    +      this.relayEvent_, false, this);
    +
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    /*
    +     * On legacy IE, double clicks do not produce two mousedown and
    +     * mouseup events. That is why a separate DBLCLICK event listener
    +     * is used.
    +     */
    +    this.ieDblclickListenerKey_ = goog.events.listen(element,
    +        goog.events.EventType.DBLCLICK,
    +        this.emulateClickLegacyIE_, false, this);
    +  }
    +
    +};
    +goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * @param {goog.events.BrowserEvent} browserEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.emulateClickLegacyIE_ =
    +    function(browserEvent) {
    +  var pointerEvent = this.pointerEventHandler_.wrapMouseEvent(
    +      ol.MapBrowserEvent.EventType.POINTERUP,
    +      browserEvent
    +      );
    +  this.emulateClick_(pointerEvent);
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
    +  var newEvent;
    +  newEvent = new ol.MapBrowserPointerEvent(
    +      ol.MapBrowserEvent.EventType.CLICK, this.map_, pointerEvent);
    +  this.dispatchEvent(newEvent);
    +  if (this.clickTimeoutId_ !== 0) {
    +    // double-click
    +    goog.global.clearTimeout(this.clickTimeoutId_);
    +    this.clickTimeoutId_ = 0;
    +    newEvent = new ol.MapBrowserPointerEvent(
    +        ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent);
    +    this.dispatchEvent(newEvent);
    +  } else {
    +    // click
    +    this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
    +      this.clickTimeoutId_ = 0;
    +      var newEvent = new ol.MapBrowserPointerEvent(
    +          ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent);
    +      this.dispatchEvent(newEvent);
    +    }, this), 250);
    +  }
    +};
    +
    +
    +/**
    + * Keeps track on how many pointers are currently active.
    + *
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.updateActivePointers_ =
    +    function(pointerEvent) {
    +  var event = pointerEvent;
    +
    +  if (event.type == ol.MapBrowserEvent.EventType.POINTERUP ||
    +      event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) {
    +    delete this.trackedTouches_[event.pointerId];
    +  } else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
    +    this.trackedTouches_[event.pointerId] = true;
    +  }
    +  this.activePointers_ = goog.object.getCount(this.trackedTouches_);
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
    +  this.updateActivePointers_(pointerEvent);
    +  var newEvent = new ol.MapBrowserPointerEvent(
    +      ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent);
    +  this.dispatchEvent(newEvent);
    +
    +  // We emulate click events on left mouse button click, touch contact, and pen
    +  // contact. isMouseActionButton returns true in these cases (evt.button is set
    +  // to 0).
    +  // See http://www.w3.org/TR/pointerevents/#button-states
    +  if (!this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
    +    goog.asserts.assert(!goog.isNull(this.down_));
    +    this.emulateClick_(this.down_);
    +  }
    +
    +  goog.asserts.assert(this.activePointers_ >= 0);
    +  if (this.activePointers_ === 0) {
    +    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    +    this.dragListenerKeys_ = null;
    +    this.dragging_ = false;
    +    this.down_ = null;
    +    goog.dispose(this.documentPointerEventHandler_);
    +    this.documentPointerEventHandler_ = null;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @return {boolean} If the left mouse button was pressed.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.isMouseActionButton_ =
    +    function(pointerEvent) {
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    return pointerEvent.button == 1;
    +  } else {
    +    return pointerEvent.button === 0;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.handlePointerDown_ =
    +    function(pointerEvent) {
    +  this.updateActivePointers_(pointerEvent);
    +  var newEvent = new ol.MapBrowserPointerEvent(
    +      ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent);
    +  this.dispatchEvent(newEvent);
    +
    +  this.down_ = pointerEvent;
    +
    +  if (goog.isNull(this.dragListenerKeys_)) {
    +    /* Set up a pointer event handler on the `document`,
    +     * which is required when the pointer is moved outside
    +     * the viewport when dragging.
    +     */
    +    this.documentPointerEventHandler_ =
    +        new ol.pointer.PointerEventHandler(document);
    +
    +    this.dragListenerKeys_ = [
    +      goog.events.listen(this.documentPointerEventHandler_,
    +          ol.MapBrowserEvent.EventType.POINTERMOVE,
    +          this.handlePointerMove_, false, this),
    +      goog.events.listen(this.documentPointerEventHandler_,
    +          ol.MapBrowserEvent.EventType.POINTERUP,
    +          this.handlePointerUp_, false, this),
    +      /* Note that the listener for `pointercancel is set up on
    +       * `pointerEventHandler_` and not `documentPointerEventHandler_` like
    +       * the `pointerup` and `pointermove` listeners.
    +       *
    +       * The reason for this is the following: `TouchSource.vacuumTouches_()`
    +       * issues `pointercancel` events, when there was no `touchend` for a
    +       * `touchstart`. Now, let's say a first `touchstart` is registered on
    +       * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
    +       * But `documentPointerEventHandler_` doesn't know about the first
    +       * `touchstart`. If there is no `touchend` for the `touchstart`, we can
    +       * only receive a `touchcancel` from `pointerEventHandler_`, because it is
    +       * only registered there.
    +       */
    +      goog.events.listen(this.pointerEventHandler_,
    +          ol.MapBrowserEvent.EventType.POINTERCANCEL,
    +          this.handlePointerUp_, false, this)
    +    ];
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.handlePointerMove_ =
    +    function(pointerEvent) {
    +  // Fix IE10 on windows Surface : When you tap the tablet, it triggers
    +  // multiple pointermove events between pointerdown and pointerup with
    +  // the exact same coordinates of the pointerdown event. To avoid a
    +  // 'false' touchmove event to be dispatched , we test if the pointer
    +  // effectively moved.
    +  if (this.isMoving_(pointerEvent)) {
    +    this.dragging_ = true;
    +    var newEvent = new ol.MapBrowserPointerEvent(
    +        ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent,
    +        this.dragging_);
    +    this.dispatchEvent(newEvent);
    +  }
    +
    +  // Some native android browser triggers mousemove events during small period
    +  // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
    +  // https://code.google.com/p/android/issues/detail?id=19827
    +  // ex: Galaxy Tab P3110 + Android 4.1.1
    +  pointerEvent.preventDefault();
    +};
    +
    +
    +/**
    + * Wrap and relay a pointer event.  Note that this requires that the type
    + * string for the MapBrowserPointerEvent matches the PointerEvent type.
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
    +  var dragging = !goog.isNull(this.down_) && this.isMoving_(pointerEvent);
    +  this.dispatchEvent(new ol.MapBrowserPointerEvent(
    +      pointerEvent.type, this.map_, pointerEvent, dragging));
    +};
    +
    +
    +/**
    + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
    + * @return {boolean}
    + * @private
    + */
    +ol.MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) {
    +  return pointerEvent.clientX != this.down_.clientX ||
    +      pointerEvent.clientY != this.down_.clientY;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
    +  if (!goog.isNull(this.relayedListenerKey_)) {
    +    goog.events.unlistenByKey(this.relayedListenerKey_);
    +    this.relayedListenerKey_ = null;
    +  }
    +  if (!goog.isNull(this.pointerdownListenerKey_)) {
    +    goog.events.unlistenByKey(this.pointerdownListenerKey_);
    +    this.pointerdownListenerKey_ = null;
    +  }
    +  if (!goog.isNull(this.dragListenerKeys_)) {
    +    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    +    this.dragListenerKeys_ = null;
    +  }
    +  if (!goog.isNull(this.documentPointerEventHandler_)) {
    +    goog.dispose(this.documentPointerEventHandler_);
    +    this.documentPointerEventHandler_ = null;
    +  }
    +  if (!goog.isNull(this.pointerEventHandler_)) {
    +    goog.dispose(this.pointerEventHandler_);
    +    this.pointerEventHandler_ = null;
    +  }
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE &&
    +      !goog.isNull(this.ieDblclickListenerKey_)) {
    +    goog.events.unlistenByKey(this.ieDblclickListenerKey_);
    +    this.ieDblclickListenerKey_ = null;
    +  }
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +ol.MapBrowserEvent.EventType = {
    +  // derived event types
    +  /**
    +   * A true single click with no dragging and no double click. Note that this
    +   * event is delayed by 250 ms to ensure that it is not a double click.
    +   * @event ol.MapBrowserEvent#singleclick
    +   * @api
    +   */
    +  SINGLECLICK: 'singleclick',
    +  /**
    +   * A click with no dragging. A double click will fire two of this.
    +   * @event ol.MapBrowserEvent#click
    +   * @api
    +   */
    +  CLICK: goog.events.EventType.CLICK,
    +  /**
    +   * A true double click, with no dragging.
    +   * @event ol.MapBrowserEvent#dblclick
    +   * @api
    +   */
    +  DBLCLICK: goog.events.EventType.DBLCLICK,
    +  /**
    +   * Triggered when a pointer is dragged.
    +   * @event ol.MapBrowserEvent#pointerdrag
    +   * @api
    +   */
    +  POINTERDRAG: 'pointerdrag',
    +
    +  // original pointer event types
    +  /**
    +   * Triggered when a pointer is moved. Note that on touch devices this is
    +   * triggered when the map is panned, so is not the same as mousemove.
    +   * @event ol.MapBrowserEvent#pointermove
    +   * @api
    +   */
    +  POINTERMOVE: 'pointermove',
    +  POINTERDOWN: 'pointerdown',
    +  POINTERUP: 'pointerup',
    +  POINTEROVER: 'pointerover',
    +  POINTEROUT: 'pointerout',
    +  POINTERENTER: 'pointerenter',
    +  POINTERLEAVE: 'pointerleave',
    +  POINTERCANCEL: 'pointercancel'
    +};
    diff --git a/mapbox-gl/ol/ol/mapevent.js b/mapbox-gl/ol/ol/mapevent.js
    new file mode 100644
    index 0000000000..260d002ac5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/mapevent.js
    @@ -0,0 +1,58 @@
    +goog.provide('ol.MapEvent');
    +goog.provide('ol.MapEventType');
    +
    +goog.require('goog.events.Event');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.MapEventType = {
    +  /**
    +   * Triggered after a map frame is rendered.
    +   * @event ol.MapEvent#postrender
    +   * @api
    +   */
    +  POSTRENDER: 'postrender',
    +  /**
    +   * Triggered after the map is moved.
    +   * @event ol.MapEvent#moveend
    +   * @api
    +   */
    +  MOVEEND: 'moveend'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted as map events are instances of this type.
    + * See {@link ol.Map} for which events trigger a map event.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.MapEvent}
    + * @param {string} type Event type.
    + * @param {ol.Map} map Map.
    + * @param {?olx.FrameState=} opt_frameState Frame state.
    + */
    +ol.MapEvent = function(type, map, opt_frameState) {
    +
    +  goog.base(this, type);
    +
    +  /**
    +   * The map where the event occurred.
    +   * @type {ol.Map}
    +   * @api stable
    +   */
    +  this.map = map;
    +
    +  /**
    +   * The frame state at the time of the event.
    +   * @type {?olx.FrameState}
    +   * @api
    +   */
    +  this.frameState = goog.isDef(opt_frameState) ? opt_frameState : null;
    +
    +};
    +goog.inherits(ol.MapEvent, goog.events.Event);
    diff --git a/mapbox-gl/ol/ol/math.js b/mapbox-gl/ol/ol/math.js
    new file mode 100644
    index 0000000000..70c46df4c6
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/math.js
    @@ -0,0 +1,112 @@
    +goog.provide('ol.math');
    +
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic cosine of x.
    + */
    +ol.math.cosh = function(x) {
    +  return (Math.exp(x) + Math.exp(-x)) / 2;
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic cotangent of x.
    + */
    +ol.math.coth = function(x) {
    +  var expMinusTwoX = Math.exp(-2 * x);
    +  return (1 + expMinusTwoX) / (1 - expMinusTwoX);
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic cosecant of x.
    + */
    +ol.math.csch = function(x) {
    +  return 2 / (Math.exp(x) - Math.exp(-x));
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} The smallest power of two greater than or equal to x.
    + */
    +ol.math.roundUpToPowerOfTwo = function(x) {
    +  goog.asserts.assert(0 < x);
    +  return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2));
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic secant of x.
    + */
    +ol.math.sech = function(x) {
    +  return 2 / (Math.exp(x) + Math.exp(-x));
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic sine of x.
    + */
    +ol.math.sinh = function(x) {
    +  return (Math.exp(x) - Math.exp(-x)) / 2;
    +};
    +
    +
    +/**
    + * Returns the square of the closest distance between the point (x, y) and the
    + * line segment (x1, y1) to (x2, y2).
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {number} x1 X1.
    + * @param {number} y1 Y1.
    + * @param {number} x2 X2.
    + * @param {number} y2 Y2.
    + * @return {number} Squared distance.
    + */
    +ol.math.squaredSegmentDistance = function(x, y, x1, y1, x2, y2) {
    +  var dx = x2 - x1;
    +  var dy = y2 - y1;
    +  if (dx !== 0 || dy !== 0) {
    +    var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
    +    if (t > 1) {
    +      x1 = x2;
    +      y1 = y2;
    +    } else if (t > 0) {
    +      x1 += dx * t;
    +      y1 += dy * t;
    +    }
    +  }
    +  return ol.math.squaredDistance(x, y, x1, y1);
    +};
    +
    +
    +/**
    + * Returns the square of the distance between the points (x1, y1) and (x2, y2).
    + * @param {number} x1 X1.
    + * @param {number} y1 Y1.
    + * @param {number} x2 X2.
    + * @param {number} y2 Y2.
    + * @return {number} Squared distance.
    + */
    +ol.math.squaredDistance = function(x1, y1, x2, y2) {
    +  var dx = x2 - x1;
    +  var dy = y2 - y1;
    +  return dx * dx + dy * dy;
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @return {number} Hyperbolic tangent of x.
    + */
    +ol.math.tanh = function(x) {
    +  var expMinusTwoX = Math.exp(-2 * x);
    +  return (1 - expMinusTwoX) / (1 + expMinusTwoX);
    +};
    diff --git a/mapbox-gl/ol/ol/object.js b/mapbox-gl/ol/ol/object.js
    new file mode 100644
    index 0000000000..0e2f7ab88d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/object.js
    @@ -0,0 +1,491 @@
    +/**
    + * An implementation of Google Maps' MVCObject.
    + * @see https://developers.google.com/maps/articles/mvcfun
    + * @see https://developers.google.com/maps/documentation/javascript/reference
    + */
    +
    +goog.provide('ol.Object');
    +goog.provide('ol.ObjectEvent');
    +goog.provide('ol.ObjectEventType');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('ol.Observable');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.ObjectEventType = {
    +  /**
    +   * Triggered when a property is changed.
    +   * @event ol.ObjectEvent#propertychange
    +   * @api
    +   */
    +  PROPERTYCHANGE: 'propertychange'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.Object} instances are instances of this type.
    + *
    + * @param {string} type The event type.
    + * @param {string} key The property name.
    + * @param {*} oldValue The old value for `key`.
    + * @extends {goog.events.Event}
    + * @implements {oli.ObjectEvent}
    + * @constructor
    + */
    +ol.ObjectEvent = function(type, key, oldValue) {
    +  goog.base(this, type);
    +
    +  /**
    +   * The name of the property whose value is changing.
    +   * @type {string}
    +   * @api
    +   */
    +  this.key = key;
    +
    +  /**
    +   * The old value. To get the new value use `e.target.get(e.key)` where
    +   * `e` is the event object.
    +   * @type {*}
    +   * @api
    +   */
    +  this.oldValue = oldValue;
    +
    +};
    +goog.inherits(ol.ObjectEvent, goog.events.Event);
    +
    +
    +
    +/**
    + * @constructor
    + * @param {ol.Object} source Source object.
    + * @param {ol.Object} target Target object.
    + * @param {string} sourceKey Source key.
    + * @param {string} targetKey Target key.
    + */
    +ol.ObjectAccessor = function(source, target, sourceKey, targetKey) {
    +
    +  /**
    +   * @type {ol.Object}
    +   */
    +  this.source = source;
    +
    +  /**
    +   * @type {ol.Object}
    +   */
    +  this.target = target;
    +
    +  /**
    +   * @type {string}
    +   */
    +  this.sourceKey = sourceKey;
    +
    +  /**
    +   * @type {string}
    +   */
    +  this.targetKey = targetKey;
    +
    +  /**
    +   * @type {function(?): ?}
    +   */
    +  this.from = goog.functions.identity;
    +
    +  /**
    +   * @type {function(?): ?}
    +   */
    +  this.to = goog.functions.identity;
    +};
    +
    +
    +/**
    + * @param {function(?): ?} from A function that transforms the source value
    + *     before it is set to the target.
    + * @param {function(?): ?} to A function that transforms the target value
    + *     before it is set to the source.
    + * @api
    + */
    +ol.ObjectAccessor.prototype.transform = function(from, to) {
    +  var oldValue = ol.Object.getKeyValue_(this.source, this.sourceKey);
    +  this.from = from;
    +  this.to = to;
    +  this.source.notify(this.sourceKey, oldValue);
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Most non-trivial classes inherit from this.
    + *
    + * This extends {@link ol.Observable} with observable properties, where each
    + * property is observable as well as the object as a whole.
    + *
    + * Classes that inherit from this have pre-defined properties, to which you can
    + * add your own. The pre-defined properties are listed in this documentation as
    + * 'Observable Properties', and have their own accessors; for example,
    + * {@link ol.Map} has a `target` property, accessed with `getTarget()`  and
    + * changed with `setTarget()`. Not all properties are however settable. There
    + * are also general-purpose accessors `get()` and `set()`. For example,
    + * `get('target')` is equivalent to `getTarget()`.
    + *
    + * The `set` accessors trigger a change event, and you can monitor this by
    + * registering a listener. For example, {@link ol.View} has a `center`
    + * property, so `view.on('change:center', function(evt) {...});` would call the
    + * function whenever the value of the center property changes. Within the
    + * function, `evt.target` would be the view, so `evt.target.getCenter()` would
    + * return the new center.
    + *
    + * You can add your own observable properties with
    + * `object.set('prop', 'value')`, and retrieve that with `object.get('prop')`.
    + * You can listen for changes on that property value with
    + * `object.on('change:prop', listener)`. You can get a list of all
    + * properties with {@link ol.Object#getProperties object.getProperties()}.
    + *
    + * Note that the observable properties are separate from standard JS properties.
    + * You can, for example, give your map object a title with
    + * `map.title='New title'` and with `map.set('title', 'Another title')`. The
    + * first will be a `hasOwnProperty`; the second will appear in
    + * `getProperties()`. Only the second is observable.
    + *
    + * The observable properties also implement a form of Key Value Observing.
    + * Two objects can be bound together such that a change in one will
    + * automatically be reflected in the other. See `bindTo` method for more
    + * details, and see {@link ol.dom.Input} for the specific case of binding an
    + * object with an HTML element.
    + *
    + * @constructor
    + * @extends {ol.Observable}
    + * @param {Object.<string, *>=} opt_values An object with key-value pairs.
    + * @fires ol.ObjectEvent
    + * @api
    + */
    +ol.Object = function(opt_values) {
    +  goog.base(this);
    +
    +  // Call goog.getUid to ensure that the order of objects' ids is the same as
    +  // the order in which they were created.  This also helps to ensure that
    +  // object properties are always added in the same order, which helps many
    +  // JavaScript engines generate faster code.
    +  goog.getUid(this);
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, *>}
    +   */
    +  this.values_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.ObjectAccessor>}
    +   */
    +  this.accessors_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, goog.events.Key>}
    +   */
    +  this.listeners_ = {};
    +
    +  if (goog.isDef(opt_values)) {
    +    this.setProperties(opt_values);
    +  }
    +};
    +goog.inherits(ol.Object, ol.Observable);
    +
    +
    +/**
    + * @private
    + * @type {Object.<string, string>}
    + */
    +ol.Object.changeEventTypeCache_ = {};
    +
    +
    +/**
    + * @private
    + * @type {Object.<string, string>}
    + */
    +ol.Object.getterNameCache_ = {};
    +
    +
    +/**
    + * @private
    + * @type {Object.<string, string>}
    + */
    +ol.Object.setterNameCache_ = {};
    +
    +
    +/**
    + * @param {string} key Key name.
    + * @return {string} Change name.
    + */
    +ol.Object.getChangeEventType = function(key) {
    +  return ol.Object.changeEventTypeCache_.hasOwnProperty(key) ?
    +      ol.Object.changeEventTypeCache_[key] :
    +      (ol.Object.changeEventTypeCache_[key] = 'change:' + key);
    +};
    +
    +
    +/**
    + * @param {string} key String.
    + * @return {string} Getter name.
    + */
    +ol.Object.getGetterName = function(key) {
    +  return ol.Object.getterNameCache_.hasOwnProperty(key) ?
    +      ol.Object.getterNameCache_[key] :
    +      (ol.Object.getterNameCache_[key] = 'get' + goog.string.capitalize(key));
    +};
    +
    +
    +/**
    + * @param {string} key String.
    + * @return {string} Setter name.
    + */
    +ol.Object.getSetterName = function(key) {
    +  return ol.Object.setterNameCache_.hasOwnProperty(key) ?
    +      ol.Object.setterNameCache_[key] :
    +      (ol.Object.setterNameCache_[key] = 'set' + goog.string.capitalize(key));
    +};
    +
    +
    +/**
    + * Get the value for an object and a key. Use the getter (`getX`) if it exists,
    + * otherwise use the generic `get` function.
    + * @param {ol.Object} obj Object.
    + * @param {string} key Key;
    + * @return {*} Value;
    + * @private
    + */
    +ol.Object.getKeyValue_ = function(obj, key) {
    +  var getterName = ol.Object.getGetterName(key);
    +  var getter = /** @type {function(): *|undefined} */
    +      (/** @type {Object} */ (obj)[getterName]);
    +  return goog.isDef(getter) ? getter.call(obj) : obj.get(key);
    +};
    +
    +
    +/**
    + * Set the value for an object and a key. Use the setter (`setX`) if it exists,
    + * otherwise use the generic `set` function.
    + * @param {ol.Object} obj Object.
    + * @param {string} key Key.
    + * @param {*} value Value.
    + * @private
    + */
    +ol.Object.setKeyValue_ = function(obj, key, value) {
    +  var setterName = ol.Object.getSetterName(key);
    +  var setter = /** @type {function(*)|undefined} */
    +      (/** @type {Object} */ (obj)[setterName]);
    +  if (goog.isDef(setter)) {
    +    setter.call(obj, value);
    +  } else {
    +    obj.set(key, value);
    +  }
    +};
    +
    +
    +/**
    + * The bindTo method allows you to set up a two-way binding between a
    + * `source` and `target` object. The method returns an object with a
    + * `transform` method that you can use to provide `from` and `to`
    + * functions to transform values on the way from the source to the
    + * target and on the way back.
    +*
    + * For example, if you had two map views (sourceView and targetView)
    + * and you wanted the target view to have double the resolution of the
    + * source view, you could transform the resolution on the way to and
    + * from the target with the following:
    + *
    + *     sourceView.bindTo('resolution', targetView)
    + *       .transform(
    + *         function(sourceResolution) {
    + *           // from sourceView.resolution to targetView.resolution
    + *           return 2 * sourceResolution;
    + *         },
    + *         function(targetResolution) {
    + *           // from targetView.resolution to sourceView.resolution
    + *           return targetResolution / 2;
    + *         }
    + *       );
    + *
    + * @param {string} key Key name.
    + * @param {ol.Object} target Target.
    + * @param {string=} opt_targetKey Target key.
    + * @return {ol.ObjectAccessor}
    + * @api
    + */
    +ol.Object.prototype.bindTo = function(key, target, opt_targetKey) {
    +  var targetKey = opt_targetKey || key;
    +  this.unbind(key);
    +
    +  // listen for change:targetkey events
    +  var eventType = ol.Object.getChangeEventType(targetKey);
    +  this.listeners_[key] = goog.events.listen(target, eventType,
    +      /**
    +       * @param {ol.ObjectEvent} e Event.
    +       * @this {ol.Object}
    +       */
    +      function(e) {
    +        this.notify(key, e.oldValue);
    +      }, undefined, this);
    +
    +  var accessor = new ol.ObjectAccessor(this, target, key, targetKey);
    +  this.accessors_[key] = accessor;
    +  this.notify(key, this.values_[key]);
    +  return accessor;
    +};
    +
    +
    +/**
    + * Gets a value.
    + * @param {string} key Key name.
    + * @return {*} Value.
    + * @api
    + */
    +ol.Object.prototype.get = function(key) {
    +  var value;
    +  var accessors = this.accessors_;
    +  if (accessors.hasOwnProperty(key)) {
    +    var accessor = accessors[key];
    +    value = ol.Object.getKeyValue_(accessor.target, accessor.targetKey);
    +    value = accessor.to(value);
    +  } else if (this.values_.hasOwnProperty(key)) {
    +    value = this.values_[key];
    +  }
    +  return value;
    +};
    +
    +
    +/**
    + * Get a list of object property names.
    + * @return {Array.<string>} List of property names.
    + * @api
    + */
    +ol.Object.prototype.getKeys = function() {
    +  var accessors = this.accessors_;
    +  var keysObject;
    +  if (goog.object.isEmpty(this.values_)) {
    +    if (goog.object.isEmpty(accessors)) {
    +      return [];
    +    } else {
    +      keysObject = accessors;
    +    }
    +  } else {
    +    if (goog.object.isEmpty(accessors)) {
    +      keysObject = this.values_;
    +    } else {
    +      keysObject = {};
    +      var key;
    +      for (key in this.values_) {
    +        keysObject[key] = true;
    +      }
    +      for (key in accessors) {
    +        keysObject[key] = true;
    +      }
    +    }
    +  }
    +  return goog.object.getKeys(keysObject);
    +};
    +
    +
    +/**
    + * Get an object of all property names and values.
    + * @return {Object.<string, *>} Object.
    + * @api
    + */
    +ol.Object.prototype.getProperties = function() {
    +  var properties = {};
    +  var key;
    +  for (key in this.values_) {
    +    properties[key] = this.values_[key];
    +  }
    +  for (key in this.accessors_) {
    +    properties[key] = this.get(key);
    +  }
    +  return properties;
    +};
    +
    +
    +/**
    + * @param {string} key Key name.
    + * @param {*} oldValue Old value.
    + */
    +ol.Object.prototype.notify = function(key, oldValue) {
    +  var eventType;
    +  eventType = ol.Object.getChangeEventType(key);
    +  this.dispatchEvent(new ol.ObjectEvent(eventType, key, oldValue));
    +  eventType = ol.ObjectEventType.PROPERTYCHANGE;
    +  this.dispatchEvent(new ol.ObjectEvent(eventType, key, oldValue));
    +};
    +
    +
    +/**
    + * Sets a value.
    + * @param {string} key Key name.
    + * @param {*} value Value.
    + * @api
    + */
    +ol.Object.prototype.set = function(key, value) {
    +  var accessors = this.accessors_;
    +  if (accessors.hasOwnProperty(key)) {
    +    var accessor = accessors[key];
    +    value = accessor.from(value);
    +    ol.Object.setKeyValue_(accessor.target, accessor.targetKey, value);
    +  } else {
    +    var oldValue = this.values_[key];
    +    this.values_[key] = value;
    +    this.notify(key, oldValue);
    +  }
    +};
    +
    +
    +/**
    + * Sets a collection of key-value pairs.
    + * @param {Object.<string, *>} values Values.
    + * @api
    + */
    +ol.Object.prototype.setProperties = function(values) {
    +  var key;
    +  for (key in values) {
    +    this.set(key, values[key]);
    +  }
    +};
    +
    +
    +/**
    + * Removes a binding. Unbinding will set the unbound property to the current
    + *     value. The object will not be notified, as the value has not changed.
    + * @param {string} key Key name.
    + * @api
    + */
    +ol.Object.prototype.unbind = function(key) {
    +  var listeners = this.listeners_;
    +  var listener = listeners[key];
    +  if (listener) {
    +    delete listeners[key];
    +    goog.events.unlistenByKey(listener);
    +    var value = this.get(key);
    +    delete this.accessors_[key];
    +    this.values_[key] = value;
    +  }
    +};
    +
    +
    +/**
    + * Removes all bindings.
    + * @api
    + */
    +ol.Object.prototype.unbindAll = function() {
    +  for (var key in this.listeners_) {
    +    this.unbind(key);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/observable.js b/mapbox-gl/ol/ol/observable.js
    new file mode 100644
    index 0000000000..0f48f60ea0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/observable.js
    @@ -0,0 +1,114 @@
    +goog.provide('ol.Observable');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * An event target providing convenient methods for listener registration
    + * and unregistration. A generic `change` event is always available through
    + * {@link ol.Observable#changed}.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @suppress {checkStructDictInheritance}
    + * @struct
    + * @api stable
    + */
    +ol.Observable = function() {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.revision_ = 0;
    +
    +};
    +goog.inherits(ol.Observable, goog.events.EventTarget);
    +
    +
    +/**
    + * Removes an event listener using the key returned by `on()` or `once()`.
    + * @param {goog.events.Key} key The key returned by `on()` or `once()`.
    + * @api stable
    + */
    +ol.Observable.unByKey = function(key) {
    +  goog.events.unlistenByKey(key);
    +};
    +
    +
    +/**
    + * Increases the revision counter and disptches a 'change' event.
    + * @fires change
    + * @api
    + */
    +ol.Observable.prototype.changed = function() {
    +  ++this.revision_;
    +  this.dispatchEvent(goog.events.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * @return {number} Revision.
    + * @api
    + */
    +ol.Observable.prototype.getRevision = function() {
    +  return this.revision_;
    +};
    +
    +
    +/**
    + * Listen for a certain type of event.
    + * @param {string|Array.<string>} type The event type or array of event types.
    + * @param {function(?): ?} listener The listener function.
    + * @param {Object=} opt_this The object to use as `this` in `listener`.
    + * @return {goog.events.Key} Unique key for the listener.
    + * @api stable
    + */
    +ol.Observable.prototype.on = function(type, listener, opt_this) {
    +  return goog.events.listen(this, type, listener, false, opt_this);
    +};
    +
    +
    +/**
    + * Listen once for a certain type of event.
    + * @param {string|Array.<string>} type The event type or array of event types.
    + * @param {function(?): ?} listener The listener function.
    + * @param {Object=} opt_this The object to use as `this` in `listener`.
    + * @return {goog.events.Key} Unique key for the listener.
    + * @api stable
    + */
    +ol.Observable.prototype.once = function(type, listener, opt_this) {
    +  return goog.events.listenOnce(this, type, listener, false, opt_this);
    +};
    +
    +
    +/**
    + * Unlisten for a certain type of event.
    + * @param {string|Array.<string>} type The event type or array of event types.
    + * @param {function(?): ?} listener The listener function.
    + * @param {Object=} opt_this The object which was used as `this` by the
    + * `listener`.
    + * @api stable
    + */
    +ol.Observable.prototype.un = function(type, listener, opt_this) {
    +  goog.events.unlisten(this, type, listener, false, opt_this);
    +};
    +
    +
    +/**
    + * Removes an event listener using the key returned by `on()` or `once()`.
    + * Note that using the {@link ol.Observable.unByKey} static function is to
    + * be preferred.
    + * @param {goog.events.Key} key The key returned by `on()` or `once()`.
    + * @function
    + * @api stable
    + */
    +ol.Observable.prototype.unByKey = ol.Observable.unByKey;
    diff --git a/mapbox-gl/ol/ol/ol.js b/mapbox-gl/ol/ol/ol.js
    new file mode 100644
    index 0000000000..01ca364939
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/ol.js
    @@ -0,0 +1,293 @@
    +goog.require('goog.userAgent');
    +
    +goog.provide('ol');
    +
    +
    +/**
    + * Constants defined with the define tag cannot be changed in application
    + * code, but can be set at compile time.
    + * Some reduce the size of the build in advanced compile mode.
    + */
    +
    +
    +/**
    + * @define {boolean} Assume touch.  Default is `false`.
    + */
    +ol.ASSUME_TOUCH = false;
    +
    +
    +/**
    + * @define {boolean} Replace unused entries with NaNs.
    + */
    +ol.BUFFER_REPLACE_UNUSED_ENTRIES_WITH_NANS = goog.DEBUG;
    +
    +
    +/**
    + * TODO: rename this to something having to do with tile grids
    + * see https://github.com/openlayers/ol3/issues/2076
    + * @define {number} Default maximum zoom for default tile grids.
    + */
    +ol.DEFAULT_MAX_ZOOM = 42;
    +
    +
    +/**
    + * @define {number} Default min zoom level for the map view.  Default is `0`.
    + */
    +ol.DEFAULT_MIN_ZOOM = 0;
    +
    +
    +/**
    + * @define {number} Default high water mark.
    + */
    +ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK = 2048;
    +
    +
    +/**
    + * @define {number} Default tile size.
    + */
    +ol.DEFAULT_TILE_SIZE = 256;
    +
    +
    +/**
    + * @define {string} Default WMS version.
    + */
    +ol.DEFAULT_WMS_VERSION = '1.3.0';
    +
    +
    +/**
    + * @define {number} Drag-rotate-zoom animation duration.
    + */
    +ol.DRAGROTATEANDZOOM_ANIMATION_DURATION = 400;
    +
    +
    +/**
    + * @define {number} Drag-rotate animation duration.
    + */
    +ol.DRAGROTATE_ANIMATION_DURATION = 250;
    +
    +
    +/**
    + * @define {number} Drag-zoom animation duration.
    + */
    +ol.DRAGZOOM_ANIMATION_DURATION = 200;
    +
    +
    +/**
    + * @define {number} Hysteresis pixels.
    + */
    +ol.DRAG_BOX_HYSTERESIS_PIXELS = 8;
    +
    +
    +/**
    + * @define {boolean} Enable the Canvas renderer.  Default is `true`. Setting
    + *     this to false at compile time in advanced mode removes all code
    + *     supporting the Canvas renderer from the build.
    + */
    +ol.ENABLE_CANVAS = true;
    +
    +
    +/**
    + * @define {boolean} Enable the DOM renderer (used as a fallback where Canvas is
    + *     not available).  Default is `true`. Setting this to false at compile time
    + *     in advanced mode removes all code supporting the DOM renderer from the
    + *     build.
    + */
    +ol.ENABLE_DOM = true;
    +
    +
    +/**
    + * @define {boolean} Enable rendering of ol.layer.Image based layers.  Default
    + *     is `true`. Setting this to false at compile time in advanced mode removes
    + *     all code supporting Image layers from the build.
    + */
    +ol.ENABLE_IMAGE = true;
    +
    +
    +/**
    + * @define {boolean} Enable Closure named colors (`goog.color.names`).
    + *     Enabling these colors adds about 3KB uncompressed / 1.5KB compressed to
    + *     the final build size.  Default is `false`. This setting has no effect
    + *     with Canvas renderer, which uses its own names, whether this is true or
    + *     false.
    + */
    +ol.ENABLE_NAMED_COLORS = false;
    +
    +
    +/**
    + * @define {boolean} Enable integration with the Proj4js library.  Default is
    + *     `true`.
    + */
    +ol.ENABLE_PROJ4JS = true;
    +
    +
    +/**
    + * @define {boolean} Enable rendering of ol.layer.Tile based layers.  Default is
    + *     `true`. Setting this to false at compile time in advanced mode removes
    + *     all code supporting Tile layers from the build.
    + */
    +ol.ENABLE_TILE = true;
    +
    +
    +/**
    + * @define {boolean} Enable rendering of ol.layer.Vector based layers.  Default
    + *     is `true`. Setting this to false at compile time in advanced mode removes
    + *     all code supporting Vector layers from the build.
    + */
    +ol.ENABLE_VECTOR = true;
    +
    +
    +/**
    + * @define {boolean} Enable the WebGL renderer.  Default is `true`. Setting
    + *     this to false at compile time in advanced mode removes all code
    + *     supporting the WebGL renderer from the build.
    + */
    +ol.ENABLE_WEBGL = true;
    +
    +
    +/**
    + * @define {boolean} Support legacy IE (7-8).  Default is `false`.
    + *     If set to `true`, `goog.array.ASSUME_NATIVE_FUNCTIONS` must be set
    + *     to `false` because legacy IE do not support ECMAScript 5 array functions.
    + */
    +ol.LEGACY_IE_SUPPORT = false;
    +
    +
    +/**
    + * @define {number} The size in pixels of the first atlas image. Default is
    + * `256`.
    + */
    +ol.INITIAL_ATLAS_SIZE = 256;
    +
    +
    +/**
    + * The page is loaded using HTTPS.
    + * @const
    + * @type {boolean}
    + */
    +ol.IS_HTTPS = goog.global.location.protocol === 'https:';
    +
    +
    +/**
    + * Whether the current browser is legacy IE
    + * @const
    + * @type {boolean}
    + */
    +ol.IS_LEGACY_IE = goog.userAgent.IE &&
    +    !goog.userAgent.isVersionOrHigher('9.0') && goog.userAgent.VERSION !== '';
    +
    +
    +/**
    + * @define {number} Keyboard pan duration.
    + */
    +ol.KEYBOARD_PAN_DURATION = 100;
    +
    +
    +/**
    + * @define {number} The maximum size in pixels of atlas images. Default is
    + * `-1`, meaning it is not used (and `ol.ol.WEBGL_MAX_TEXTURE_SIZE` is
    + * used instead).
    + */
    +ol.MAX_ATLAS_SIZE = -1;
    +
    +
    +/**
    + * @define {number} Maximum mouse wheel delta.
    + */
    +ol.MOUSEWHEELZOOM_MAXDELTA = 1;
    +
    +
    +/**
    + * @define {number} Mouse wheel timeout duration.
    + */
    +ol.MOUSEWHEELZOOM_TIMEOUT_DURATION = 80;
    +
    +
    +/**
    + * @define {number} Maximum width and/or height extent ratio that determines
    + * when the overview map should be zoomed out.
    + */
    +ol.OVERVIEWMAP_MAX_RATIO = 0.75;
    +
    +
    +/**
    + * @define {number} Minimum width and/or height extent ratio that determines
    + * when the overview map should be zoomed in.
    + */
    +ol.OVERVIEWMAP_MIN_RATIO = 0.1;
    +
    +
    +/**
    + * @define {number} Rotate animation duration.
    + */
    +ol.ROTATE_ANIMATION_DURATION = 250;
    +
    +
    +/**
    + * @define {number} Tolerance for geometry simplification in device pixels.
    + */
    +ol.SIMPLIFY_TOLERANCE = 0.5;
    +
    +
    +/**
    + * @define {number} Texture cache high water mark.
    + */
    +ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024;
    +
    +
    +/**
    + * The maximum supported WebGL texture size in pixels. If WebGL is not
    + * supported, the value is set to `undefined`.
    + * @const
    + * @type {number|undefined}
    + * @api
    + */
    +ol.WEBGL_MAX_TEXTURE_SIZE; // value is set in `ol.has`
    +
    +
    +/**
    + * List of supported WebGL extensions.
    + * @const
    + * @type {Array.<string>}
    + */
    +ol.WEBGL_EXTENSIONS; // value is set in `ol.has`
    +
    +
    +/**
    + * @define {number} Zoom slider animation duration.
    + */
    +ol.ZOOMSLIDER_ANIMATION_DURATION = 200;
    +
    +
    +/**
    + * Inherit the prototype methods from one constructor into another.
    + *
    + * Usage:
    + *
    + *     function ParentClass(a, b) { }
    + *     ParentClass.prototype.foo = function(a) { }
    + *
    + *     function ChildClass(a, b, c) {
    + *       // Call parent constructor
    + *       ParentClass.call(this, a, b);
    + *     }
    + *     ol.inherits(ChildClass, ParentClass);
    + *
    + *     var child = new ChildClass('a', 'b', 'see');
    + *     child.foo(); // This works.
    + *
    + * In addition, a superclass' implementation of a method can be invoked as
    + * follows:
    + *
    + *     ChildClass.prototype.foo = function(a) {
    + *       ChildClass.base(this, 'foo', a);
    + *       // Other code here.
    + *     };
    + *
    + * @param {Function} childCtor Child constructor.
    + * @param {Function} parentCtor Parent constructor.
    + * @function
    + * @api
    + */
    +ol.inherits =
    +    goog.inherits;
    +// note that the newline above is necessary to satisfy the linter
    diff --git a/mapbox-gl/ol/ol/overlay.js b/mapbox-gl/ol/ol/overlay.js
    new file mode 100644
    index 0000000000..759fe0f348
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/overlay.js
    @@ -0,0 +1,575 @@
    +goog.provide('ol.Overlay');
    +goog.provide('ol.OverlayPositioning');
    +goog.provide('ol.OverlayProperty');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.style');
    +goog.require('ol.Coordinate');
    +goog.require('ol.Map');
    +goog.require('ol.MapEventType');
    +goog.require('ol.Object');
    +goog.require('ol.animation');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.OverlayProperty = {
    +  ELEMENT: 'element',
    +  MAP: 'map',
    +  OFFSET: 'offset',
    +  POSITION: 'position',
    +  POSITIONING: 'positioning'
    +};
    +
    +
    +/**
    + * Overlay position: `'bottom-left'`, `'bottom-center'`,  `'bottom-right'`,
    + * `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`,
    + * `'top-center'`, `'top-right'`
    + * @enum {string}
    + * @api stable
    + */
    +ol.OverlayPositioning = {
    +  BOTTOM_LEFT: 'bottom-left',
    +  BOTTOM_CENTER: 'bottom-center',
    +  BOTTOM_RIGHT: 'bottom-right',
    +  CENTER_LEFT: 'center-left',
    +  CENTER_CENTER: 'center-center',
    +  CENTER_RIGHT: 'center-right',
    +  TOP_LEFT: 'top-left',
    +  TOP_CENTER: 'top-center',
    +  TOP_RIGHT: 'top-right'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Like {@link ol.control.Control}, Overlays are visible widgets.
    + * Unlike Controls, they are not in a fixed position on the screen, but are tied
    + * to a geographical coordinate, so panning the map will move an Overlay but not
    + * a Control.
    + *
    + * Example:
    + *
    + *     var popup = new ol.Overlay({
    + *       element: document.getElementById('popup')
    + *     });
    + *     popup.setPosition(coordinate);
    + *     map.addOverlay(popup);
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @param {olx.OverlayOptions} options Overlay options.
    + * @api stable
    + */
    +ol.Overlay = function(options) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.insertFirst_ = goog.isDef(options.insertFirst) ?
    +      options.insertFirst : true;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.stopEvent_ = goog.isDef(options.stopEvent) ? options.stopEvent : true;
    +
    +  /**
    +   * @private
    +   * @type {Element}
    +   */
    +  this.element_ = goog.dom.createDom(goog.dom.TagName.DIV, {
    +    'class': 'ol-overlay-container'
    +  });
    +  this.element_.style.position = 'absolute';
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.autoPan_ = goog.isDef(options.autoPan) ? options.autoPan : false;
    +
    +  /**
    +   * @private
    +   * @type {olx.animation.PanOptions}
    +   */
    +  this.autoPanAnimation_ = goog.isDef(options.autoPanAnimation) ?
    +      options.autoPanAnimation : /** @type {olx.animation.PanOptions} */ ({});
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.autoPanMargin_ = goog.isDef(options.autoPanMargin) ?
    +      options.autoPanMargin : 20;
    +
    +  /**
    +   * @private
    +   * @type {{bottom_: string,
    +   *         left_: string,
    +   *         right_: string,
    +   *         top_: string,
    +   *         visible: boolean}}
    +   */
    +  this.rendered_ = {
    +    bottom_: '',
    +    left_: '',
    +    right_: '',
    +    top_: '',
    +    visible: true
    +  };
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.mapPostrenderListenerKey_ = null;
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.OverlayProperty.ELEMENT),
    +      this.handleElementChanged, false, this);
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.OverlayProperty.MAP),
    +      this.handleMapChanged, false, this);
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.OverlayProperty.OFFSET),
    +      this.handleOffsetChanged, false, this);
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.OverlayProperty.POSITION),
    +      this.handlePositionChanged, false, this);
    +
    +  goog.events.listen(
    +      this, ol.Object.getChangeEventType(ol.OverlayProperty.POSITIONING),
    +      this.handlePositioningChanged, false, this);
    +
    +  if (goog.isDef(options.element)) {
    +    this.setElement(options.element);
    +  }
    +
    +  this.setOffset(goog.isDef(options.offset) ? options.offset : [0, 0]);
    +
    +  this.setPositioning(goog.isDef(options.positioning) ?
    +      /** @type {ol.OverlayPositioning} */ (options.positioning) :
    +      ol.OverlayPositioning.TOP_LEFT);
    +
    +  if (goog.isDef(options.position)) {
    +    this.setPosition(options.position);
    +  }
    +
    +};
    +goog.inherits(ol.Overlay, ol.Object);
    +
    +
    +/**
    + * Get the DOM element of this overlay.
    + * @return {Element|undefined} The Element containing the overlay.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.getElement = function() {
    +  return /** @type {Element|undefined} */ (
    +      this.get(ol.OverlayProperty.ELEMENT));
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'getElement',
    +    ol.Overlay.prototype.getElement);
    +
    +
    +/**
    + * Get the map associated with this overlay.
    + * @return {ol.Map|undefined} The map that the overlay is part of.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.getMap = function() {
    +  return /** @type {ol.Map|undefined} */ (
    +      this.get(ol.OverlayProperty.MAP));
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'getMap',
    +    ol.Overlay.prototype.getMap);
    +
    +
    +/**
    + * Get the offset of this overlay.
    + * @return {Array.<number>} The offset.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.getOffset = function() {
    +  return /** @type {Array.<number>} */ (
    +      this.get(ol.OverlayProperty.OFFSET));
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'getOffset',
    +    ol.Overlay.prototype.getOffset);
    +
    +
    +/**
    + * Get the current position of this overlay.
    + * @return {ol.Coordinate|undefined} The spatial point that the overlay is
    + *     anchored at.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.getPosition = function() {
    +  return /** @type {ol.Coordinate|undefined} */ (
    +      this.get(ol.OverlayProperty.POSITION));
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'getPosition',
    +    ol.Overlay.prototype.getPosition);
    +
    +
    +/**
    + * Get the current positioning of this overlay.
    + * @return {ol.OverlayPositioning} How the overlay is positioned
    + *     relative to its point on the map.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.getPositioning = function() {
    +  return /** @type {ol.OverlayPositioning} */ (
    +      this.get(ol.OverlayProperty.POSITIONING));
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'getPositioning',
    +    ol.Overlay.prototype.getPositioning);
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.handleElementChanged = function() {
    +  goog.dom.removeChildren(this.element_);
    +  var element = this.getElement();
    +  if (goog.isDefAndNotNull(element)) {
    +    goog.dom.append(/** @type {!Node} */ (this.element_), element);
    +  }
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.handleMapChanged = function() {
    +  if (!goog.isNull(this.mapPostrenderListenerKey_)) {
    +    goog.dom.removeNode(this.element_);
    +    goog.events.unlistenByKey(this.mapPostrenderListenerKey_);
    +    this.mapPostrenderListenerKey_ = null;
    +  }
    +  var map = this.getMap();
    +  if (goog.isDefAndNotNull(map)) {
    +    this.mapPostrenderListenerKey_ = goog.events.listen(map,
    +        ol.MapEventType.POSTRENDER, this.render, false, this);
    +    this.updatePixelPosition_();
    +    var container = this.stopEvent_ ?
    +        map.getOverlayContainerStopEvent() : map.getOverlayContainer();
    +    if (this.insertFirst_) {
    +      goog.dom.insertChildAt(/** @type {!Element} */ (
    +          container), this.element_, 0);
    +    } else {
    +      goog.dom.append(/** @type {!Node} */ (container), this.element_);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.render = function() {
    +  this.updatePixelPosition_();
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.handleOffsetChanged = function() {
    +  this.updatePixelPosition_();
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.handlePositionChanged = function() {
    +  this.updatePixelPosition_();
    +  if (goog.isDef(this.get(ol.OverlayProperty.POSITION)) && this.autoPan_) {
    +    this.panIntoView_();
    +  }
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.Overlay.prototype.handlePositioningChanged = function() {
    +  this.updatePixelPosition_();
    +};
    +
    +
    +/**
    + * Set the DOM element to be associated with this overlay.
    + * @param {Element|undefined} element The Element containing the overlay.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.setElement = function(element) {
    +  this.set(ol.OverlayProperty.ELEMENT, element);
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'setElement',
    +    ol.Overlay.prototype.setElement);
    +
    +
    +/**
    + * Set the map to be associated with this overlay.
    + * @param {ol.Map|undefined} map The map that the overlay is part of.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.setMap = function(map) {
    +  this.set(ol.OverlayProperty.MAP, map);
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'setMap',
    +    ol.Overlay.prototype.setMap);
    +
    +
    +/**
    + * Set the offset for this overlay.
    + * @param {Array.<number>} offset Offset.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.setOffset = function(offset) {
    +  this.set(ol.OverlayProperty.OFFSET, offset);
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'setOffset',
    +    ol.Overlay.prototype.setOffset);
    +
    +
    +/**
    + * Set the position for this overlay. If the position is `undefined` the
    + * overlay is hidden.
    + * @param {ol.Coordinate|undefined} position The spatial point that the overlay
    + *     is anchored at.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.setPosition = function(position) {
    +  this.set(ol.OverlayProperty.POSITION, position);
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'setPosition',
    +    ol.Overlay.prototype.setPosition);
    +
    +
    +/**
    + * Pan the map so that the overlay is entirely visible in the current viewport
    + * (if necessary).
    + * @private
    + */
    +ol.Overlay.prototype.panIntoView_ = function() {
    +  goog.asserts.assert(this.autoPan_);
    +  var map = this.getMap();
    +
    +  if (!goog.isDef(map) || goog.isNull(map.getTargetElement())) {
    +    return;
    +  }
    +
    +  var mapRect = this.getRect_(map.getTargetElement(), map.getSize());
    +  var element = this.getElement();
    +  goog.asserts.assert(!goog.isNull(element) && goog.isDef(element));
    +  var overlayRect = this.getRect_(element,
    +      [ol.dom.outerWidth(element), ol.dom.outerHeight(element)]);
    +
    +  var margin = this.autoPanMargin_;
    +  if (!ol.extent.containsExtent(mapRect, overlayRect)) {
    +    // the overlay is not completely inside the viewport, so pan the map
    +    var offsetLeft = overlayRect[0] - mapRect[0];
    +    var offsetRight = mapRect[2] - overlayRect[2];
    +    var offsetTop = overlayRect[1] - mapRect[1];
    +    var offsetBottom = mapRect[3] - overlayRect[3];
    +
    +    var delta = [0, 0];
    +    if (offsetLeft < 0) {
    +      // move map to the left
    +      delta[0] = offsetLeft - margin;
    +    } else if (offsetRight < 0) {
    +      // move map to the right
    +      delta[0] = Math.abs(offsetRight) + margin;
    +    }
    +    if (offsetTop < 0) {
    +      // move map up
    +      delta[1] = offsetTop - margin;
    +    } else if (offsetBottom < 0) {
    +      // move map down
    +      delta[1] = Math.abs(offsetBottom) + margin;
    +    }
    +
    +    if (delta[0] !== 0 || delta[1] !== 0) {
    +      var center = map.getView().getCenter();
    +      goog.asserts.assert(goog.isDef(center));
    +      var centerPx = map.getPixelFromCoordinate(center);
    +      var newCenterPx = [
    +        centerPx[0] + delta[0],
    +        centerPx[1] + delta[1]
    +      ];
    +
    +      if (!goog.isNull(this.autoPanAnimation_)) {
    +        this.autoPanAnimation_.source = center;
    +        map.beforeRender(ol.animation.pan(this.autoPanAnimation_));
    +      }
    +      map.getView().setCenter(map.getCoordinateFromPixel(newCenterPx));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Get the extent of an element relative to the document
    + * @param {Element|undefined} element The element.
    + * @param {ol.Size|undefined} size The size of the element.
    + * @return {ol.Extent}
    + * @private
    + */
    +ol.Overlay.prototype.getRect_ = function(element, size) {
    +  goog.asserts.assert(!goog.isNull(element) && goog.isDef(element));
    +  goog.asserts.assert(goog.isDef(size));
    +
    +  var offset = goog.style.getPageOffset(element);
    +  return [
    +    offset.x,
    +    offset.y,
    +    offset.x + size[0],
    +    offset.y + size[1]
    +  ];
    +};
    +
    +
    +/**
    + * Set the positioning for this overlay.
    + * @param {ol.OverlayPositioning} positioning how the overlay is
    + *     positioned relative to its point on the map.
    + * @observable
    + * @api stable
    + */
    +ol.Overlay.prototype.setPositioning = function(positioning) {
    +  this.set(ol.OverlayProperty.POSITIONING, positioning);
    +};
    +goog.exportProperty(
    +    ol.Overlay.prototype,
    +    'setPositioning',
    +    ol.Overlay.prototype.setPositioning);
    +
    +
    +/**
    + * @private
    + */
    +ol.Overlay.prototype.updatePixelPosition_ = function() {
    +
    +  var map = this.getMap();
    +  var position = this.getPosition();
    +  if (!goog.isDef(map) || !map.isRendered() || !goog.isDef(position)) {
    +    if (this.rendered_.visible) {
    +      goog.style.setElementShown(this.element_, false);
    +      this.rendered_.visible = false;
    +    }
    +    return;
    +  }
    +
    +  var pixel = map.getPixelFromCoordinate(position);
    +  goog.asserts.assert(!goog.isNull(pixel));
    +  var mapSize = map.getSize();
    +  goog.asserts.assert(goog.isDef(mapSize));
    +  var style = this.element_.style;
    +  var offset = this.getOffset();
    +  goog.asserts.assert(goog.isArray(offset));
    +  var positioning = this.getPositioning();
    +  goog.asserts.assert(goog.isDef(positioning));
    +
    +  var offsetX = offset[0];
    +  var offsetY = offset[1];
    +  if (positioning == ol.OverlayPositioning.BOTTOM_RIGHT ||
    +      positioning == ol.OverlayPositioning.CENTER_RIGHT ||
    +      positioning == ol.OverlayPositioning.TOP_RIGHT) {
    +    if (this.rendered_.left_ !== '') {
    +      this.rendered_.left_ = style.left = '';
    +    }
    +    var right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
    +    if (this.rendered_.right_ != right) {
    +      this.rendered_.right_ = style.right = right;
    +    }
    +  } else {
    +    if (this.rendered_.right_ !== '') {
    +      this.rendered_.right_ = style.right = '';
    +    }
    +    if (positioning == ol.OverlayPositioning.BOTTOM_CENTER ||
    +        positioning == ol.OverlayPositioning.CENTER_CENTER ||
    +        positioning == ol.OverlayPositioning.TOP_CENTER) {
    +      offsetX -= goog.style.getSize(this.element_).width / 2;
    +    }
    +    var left = Math.round(pixel[0] + offsetX) + 'px';
    +    if (this.rendered_.left_ != left) {
    +      this.rendered_.left_ = style.left = left;
    +    }
    +  }
    +  if (positioning == ol.OverlayPositioning.BOTTOM_LEFT ||
    +      positioning == ol.OverlayPositioning.BOTTOM_CENTER ||
    +      positioning == ol.OverlayPositioning.BOTTOM_RIGHT) {
    +    if (this.rendered_.top_ !== '') {
    +      this.rendered_.top_ = style.top = '';
    +    }
    +    var bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
    +    if (this.rendered_.bottom_ != bottom) {
    +      this.rendered_.bottom_ = style.bottom = bottom;
    +    }
    +  } else {
    +    if (this.rendered_.bottom_ !== '') {
    +      this.rendered_.bottom_ = style.bottom = '';
    +    }
    +    if (positioning == ol.OverlayPositioning.CENTER_LEFT ||
    +        positioning == ol.OverlayPositioning.CENTER_CENTER ||
    +        positioning == ol.OverlayPositioning.CENTER_RIGHT) {
    +      offsetY -= goog.style.getSize(this.element_).height / 2;
    +    }
    +    var top = Math.round(pixel[1] + offsetY) + 'px';
    +    if (this.rendered_.top_ != top) {
    +      this.rendered_.top_ = style.top = top;
    +    }
    +  }
    +
    +  if (!this.rendered_.visible) {
    +    goog.style.setElementShown(this.element_, true);
    +    this.rendered_.visible = true;
    +  }
    +
    +};
    diff --git a/mapbox-gl/ol/ol/pixel.js b/mapbox-gl/ol/ol/pixel.js
    new file mode 100644
    index 0000000000..dbf1d451da
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pixel.js
    @@ -0,0 +1,10 @@
    +goog.provide('ol.Pixel');
    +
    +
    +/**
    + * An array with two elements, representing a pixel. The first element is the
    + * x-coordinate, the second the y-coordinate of the pixel.
    + * @typedef {Array.<number>}
    + * @api stable
    + */
    +ol.Pixel;
    diff --git a/mapbox-gl/ol/ol/pointer/eventsource.js b/mapbox-gl/ol/ol/pointer/eventsource.js
    new file mode 100644
    index 0000000000..cf17cba7c5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/eventsource.js
    @@ -0,0 +1,55 @@
    +goog.provide('ol.pointer.EventSource');
    +
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @param {Object.<string, function(goog.events.BrowserEvent)>} mapping
    + * @constructor
    + */
    +ol.pointer.EventSource = function(dispatcher, mapping) {
    +  /**
    +   * @type {ol.pointer.PointerEventHandler}
    +   */
    +  this.dispatcher = dispatcher;
    +
    +  /**
    +   * @private
    +   * @const
    +   * @type {Object.<string, function(goog.events.BrowserEvent)>}
    +   */
    +  this.mapping_ = mapping;
    +};
    +
    +
    +/**
    + * List of events supported by this source.
    + * @return {Array.<string>} Event names
    + */
    +ol.pointer.EventSource.prototype.getEvents = function() {
    +  return goog.object.getKeys(this.mapping_);
    +};
    +
    +
    +/**
    + * Returns a mapping between the supported event types and
    + * the handlers that should handle an event.
    + * @return {Object.<string, function(goog.events.BrowserEvent)>}
    + *         Event/Handler mapping
    + */
    +ol.pointer.EventSource.prototype.getMapping = function() {
    +  return this.mapping_;
    +};
    +
    +
    +/**
    + * Returns the handler that should handle a given event type.
    + * @param {string} eventType
    + * @return {function(goog.events.BrowserEvent)} Handler
    + */
    +ol.pointer.EventSource.prototype.getHandlerForEvent = function(eventType) {
    +  return this.mapping_[eventType];
    +};
    diff --git a/mapbox-gl/ol/ol/pointer/mousesource.js b/mapbox-gl/ol/ol/pointer/mousesource.js
    new file mode 100644
    index 0000000000..3e007cfef0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/mousesource.js
    @@ -0,0 +1,253 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.MouseSource');
    +
    +goog.require('goog.object');
    +goog.require('ol.pointer.EventSource');
    +
    +
    +
    +/**
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @constructor
    + * @extends {ol.pointer.EventSource}
    + */
    +ol.pointer.MouseSource = function(dispatcher) {
    +  var mapping = {
    +    'mousedown': this.mousedown,
    +    'mousemove': this.mousemove,
    +    'mouseup': this.mouseup,
    +    'mouseover': this.mouseover,
    +    'mouseout': this.mouseout
    +  };
    +  goog.base(this, dispatcher, mapping);
    +
    +  /**
    +   * @const
    +   * @type {Object.<string, goog.events.BrowserEvent|Object>}
    +   */
    +  this.pointerMap = dispatcher.pointerMap;
    +
    +  /**
    +   * @const
    +   * @type {Array.<ol.Pixel>}
    +   */
    +  this.lastTouches = [];
    +};
    +goog.inherits(ol.pointer.MouseSource, ol.pointer.EventSource);
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.pointer.MouseSource.POINTER_ID = 1;
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.pointer.MouseSource.POINTER_TYPE = 'mouse';
    +
    +
    +/**
    + * Radius around touchend that swallows mouse events.
    + *
    + * @const
    + * @type {number}
    + */
    +ol.pointer.MouseSource.DEDUP_DIST = 25;
    +
    +
    +/**
    + * Detect if a mouse event was simulated from a touch by
    + * checking if previously there was a touch event at the
    + * same position.
    + *
    + * FIXME - Known problem with the native Android browser on
    + * Samsung GT-I9100 (Android 4.1.2):
    + * In case the page is scrolled, this function does not work
    + * correctly when a canvas is used (WebGL or canvas renderer).
    + * Mouse listeners on canvas elements (for this browser), create
    + * two mouse events: One 'good' and one 'bad' one (on other browsers or
    + * when a div is used, there is only one event). For the 'bad' one,
    + * clientX/clientY and also pageX/pageY are wrong when the page
    + * is scrolled. Because of that, this function can not detect if
    + * the events were simulated from a touch event. As result, a
    + * pointer event at a wrong position is dispatched, which confuses
    + * the map interactions.
    + * It is unclear, how one can get the correct position for the event
    + * or detect that the positions are invalid.
    + *
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent
    + * @return {boolean} True, if the event was generated by a touch.
    + */
    +ol.pointer.MouseSource.prototype.isEventSimulatedFromTouch_ =
    +    function(inEvent) {
    +  var lts = this.lastTouches;
    +  var x = inEvent.clientX, y = inEvent.clientY;
    +  for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
    +    // simulated mouse events will be swallowed near a primary touchend
    +    var dx = Math.abs(x - t[0]), dy = Math.abs(y - t[1]);
    +    if (dx <= ol.pointer.MouseSource.DEDUP_DIST &&
    +        dy <= ol.pointer.MouseSource.DEDUP_DIST) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * Creates a copy of the original event that will be used
    + * for the fake pointer event.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @return {Object}
    + */
    +ol.pointer.MouseSource.prepareEvent = function(inEvent, dispatcher) {
    +  var e = dispatcher.cloneEvent(inEvent, inEvent.getBrowserEvent());
    +
    +  // forward mouse preventDefault
    +  var pd = e.preventDefault;
    +  e.preventDefault = function() {
    +    inEvent.preventDefault();
    +    pd();
    +  };
    +
    +  e.pointerId = ol.pointer.MouseSource.POINTER_ID;
    +  e.isPrimary = true;
    +  e.pointerType = ol.pointer.MouseSource.POINTER_TYPE;
    +
    +  return e;
    +};
    +
    +
    +/**
    + * Handler for `mousedown`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.mousedown = function(inEvent) {
    +  if (!this.isEventSimulatedFromTouch_(inEvent)) {
    +    var p = goog.object.containsKey(this.pointerMap,
    +        ol.pointer.MouseSource.POINTER_ID.toString());
    +    // TODO(dfreedman) workaround for some elements not sending mouseup
    +    // http://crbug/149091
    +    if (p) {
    +      this.cancel(inEvent);
    +    }
    +    var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +    this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()] = inEvent;
    +    this.dispatcher.down(e, inEvent);
    +  }
    +};
    +
    +
    +/**
    + * Handler for `mousemove`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.mousemove = function(inEvent) {
    +  if (!this.isEventSimulatedFromTouch_(inEvent)) {
    +    var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +    this.dispatcher.move(e, inEvent);
    +  }
    +};
    +
    +
    +/**
    + * Handler for `mouseup`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.mouseup = function(inEvent) {
    +  if (!this.isEventSimulatedFromTouch_(inEvent)) {
    +    var p = this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()];
    +
    +    if (p && p.button === inEvent.button) {
    +      var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +      this.dispatcher.up(e, inEvent);
    +      this.cleanupMouse();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for `mouseover`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.mouseover = function(inEvent) {
    +  if (!this.isEventSimulatedFromTouch_(inEvent)) {
    +    var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +    this.dispatcher.enterOver(e, inEvent);
    +  }
    +};
    +
    +
    +/**
    + * Handler for `mouseout`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.mouseout = function(inEvent) {
    +  if (!this.isEventSimulatedFromTouch_(inEvent)) {
    +    var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +    this.dispatcher.leaveOut(e, inEvent);
    +  }
    +};
    +
    +
    +/**
    + * Dispatches a `pointercancel` event.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MouseSource.prototype.cancel = function(inEvent) {
    +  var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher);
    +  this.dispatcher.cancel(e, inEvent);
    +  this.cleanupMouse();
    +};
    +
    +
    +/**
    + * Remove the mouse from the list of active pointers.
    + */
    +ol.pointer.MouseSource.prototype.cleanupMouse = function() {
    +  goog.object.remove(this.pointerMap,
    +      ol.pointer.MouseSource.POINTER_ID.toString());
    +};
    diff --git a/mapbox-gl/ol/ol/pointer/mssource.js b/mapbox-gl/ol/ol/pointer/mssource.js
    new file mode 100644
    index 0000000000..bbf17e9713
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/mssource.js
    @@ -0,0 +1,195 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.MsSource');
    +
    +goog.require('goog.object');
    +goog.require('ol.pointer.EventSource');
    +
    +
    +
    +/**
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @constructor
    + * @extends {ol.pointer.EventSource}
    + */
    +ol.pointer.MsSource = function(dispatcher) {
    +  var mapping = {
    +    'MSPointerDown': this.msPointerDown,
    +    'MSPointerMove': this.msPointerMove,
    +    'MSPointerUp': this.msPointerUp,
    +    'MSPointerOut': this.msPointerOut,
    +    'MSPointerOver': this.msPointerOver,
    +    'MSPointerCancel': this.msPointerCancel,
    +    'MSGotPointerCapture': this.msGotPointerCapture,
    +    'MSLostPointerCapture': this.msLostPointerCapture
    +  };
    +  goog.base(this, dispatcher, mapping);
    +
    +  /**
    +   * @const
    +   * @type {Object.<string, goog.events.BrowserEvent|Object>}
    +   */
    +  this.pointerMap = dispatcher.pointerMap;
    +
    +  /**
    +   * @const
    +   * @type {Array.<string>}
    +   */
    +  this.POINTER_TYPES = [
    +    '',
    +    'unavailable',
    +    'touch',
    +    'pen',
    +    'mouse'
    +  ];
    +};
    +goog.inherits(ol.pointer.MsSource, ol.pointer.EventSource);
    +
    +
    +/**
    + * Creates a copy of the original event that will be used
    + * for the fake pointer event.
    + *
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent
    + * @return {Object}
    + */
    +ol.pointer.MsSource.prototype.prepareEvent_ = function(inEvent) {
    +  var e = inEvent;
    +  if (goog.isNumber(inEvent.getBrowserEvent().pointerType)) {
    +    e = this.dispatcher.cloneEvent(inEvent, inEvent.getBrowserEvent());
    +    e.pointerType = this.POINTER_TYPES[inEvent.getBrowserEvent().pointerType];
    +  }
    +
    +  return e;
    +};
    +
    +
    +/**
    + * Remove this pointer from the list of active pointers.
    + * @param {number} pointerId
    + */
    +ol.pointer.MsSource.prototype.cleanup = function(pointerId) {
    +  goog.object.remove(this.pointerMap, pointerId);
    +};
    +
    +
    +/**
    + * Handler for `msPointerDown`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerDown = function(inEvent) {
    +  this.pointerMap[inEvent.getBrowserEvent().pointerId] = inEvent;
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.down(e, inEvent);
    +};
    +
    +
    +/**
    + * Handler for `msPointerMove`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerMove = function(inEvent) {
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.move(e, inEvent);
    +};
    +
    +
    +/**
    + * Handler for `msPointerUp`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerUp = function(inEvent) {
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.up(e, inEvent);
    +  this.cleanup(inEvent.getBrowserEvent().pointerId);
    +};
    +
    +
    +/**
    + * Handler for `msPointerOut`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerOut = function(inEvent) {
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.leaveOut(e, inEvent);
    +};
    +
    +
    +/**
    + * Handler for `msPointerOver`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerOver = function(inEvent) {
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.enterOver(e, inEvent);
    +};
    +
    +
    +/**
    + * Handler for `msPointerCancel`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msPointerCancel = function(inEvent) {
    +  var e = this.prepareEvent_(inEvent);
    +  this.dispatcher.cancel(e, inEvent);
    +  this.cleanup(inEvent.getBrowserEvent().pointerId);
    +};
    +
    +
    +/**
    + * Handler for `msLostPointerCapture`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msLostPointerCapture = function(inEvent) {
    +  var e = this.dispatcher.makeEvent('lostpointercapture',
    +      inEvent.getBrowserEvent(), inEvent);
    +  this.dispatcher.dispatchEvent(e);
    +};
    +
    +
    +/**
    + * Handler for `msGotPointerCapture`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.MsSource.prototype.msGotPointerCapture = function(inEvent) {
    +  var e = this.dispatcher.makeEvent('gotpointercapture',
    +      inEvent.getBrowserEvent(), inEvent);
    +  this.dispatcher.dispatchEvent(e);
    +};
    diff --git a/mapbox-gl/ol/ol/pointer/nativesource.js b/mapbox-gl/ol/ol/pointer/nativesource.js
    new file mode 100644
    index 0000000000..4509064e8e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/nativesource.js
    @@ -0,0 +1,136 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.NativeSource');
    +
    +goog.require('goog.object');
    +goog.require('ol.pointer.EventSource');
    +
    +
    +
    +/**
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @constructor
    + * @extends {ol.pointer.EventSource}
    + */
    +ol.pointer.NativeSource = function(dispatcher) {
    +  var mapping = {
    +    'pointerdown': this.pointerDown,
    +    'pointermove': this.pointerMove,
    +    'pointerup': this.pointerUp,
    +    'pointerout': this.pointerOut,
    +    'pointerover': this.pointerOver,
    +    'pointercancel': this.pointerCancel,
    +    'gotpointercapture': this.gotPointerCapture,
    +    'lostpointercapture': this.lostPointerCapture
    +  };
    +  goog.base(this, dispatcher, mapping);
    +};
    +goog.inherits(ol.pointer.NativeSource, ol.pointer.EventSource);
    +
    +
    +/**
    + * Handler for `pointerdown`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerDown = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `pointermove`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerMove = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `pointerup`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerUp = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `pointerout`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerOut = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `pointerover`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerOver = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `pointercancel`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.pointerCancel = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `lostpointercapture`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.lostPointerCapture = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    +
    +
    +/**
    + * Handler for `gotpointercapture`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.NativeSource.prototype.gotPointerCapture = function(inEvent) {
    +  this.dispatcher.fireNativeEvent(inEvent);
    +};
    diff --git a/mapbox-gl/ol/ol/pointer/pointerevent.js b/mapbox-gl/ol/ol/pointer/pointerevent.js
    new file mode 100644
    index 0000000000..d17c1ba612
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/pointerevent.js
    @@ -0,0 +1,276 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.PointerEvent');
    +
    +
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * A class for pointer events.
    + *
    + * This class is used as an abstraction for mouse events,
    + * touch events and even native pointer events.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @param {string} type The type of the event to create.
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @param {Object.<string, ?>=} opt_eventDict An optional dictionary of
    + *    initial event properties.
    + */
    +ol.pointer.PointerEvent = function(type, browserEvent, opt_eventDict) {
    +  goog.base(this, type);
    +
    +  /**
    +   * @const
    +   * @type {goog.events.BrowserEvent}
    +   */
    +  this.browserEvent = browserEvent;
    +
    +  var eventDict = goog.isDef(opt_eventDict) ? opt_eventDict : {};
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.buttons = this.getButtons_(eventDict);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.pressure = this.getPressure_(eventDict, this.buttons);
    +
    +  // MouseEvent related properties
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.bubbles = goog.object.get(eventDict, 'bubbles', false);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.cancelable = goog.object.get(eventDict, 'cancelable', false);
    +
    +  /**
    +   * @type {Object}
    +   */
    +  this.view = goog.object.get(eventDict, 'view', null);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.detail = goog.object.get(eventDict, 'detail', null);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.screenX = goog.object.get(eventDict, 'screenX', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.screenY = goog.object.get(eventDict, 'screenY', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.clientX = goog.object.get(eventDict, 'clientX', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.clientY = goog.object.get(eventDict, 'clientY', 0);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.ctrlKey = goog.object.get(eventDict, 'ctrlKey', false);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.altKey = goog.object.get(eventDict, 'altKey', false);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.shiftKey = goog.object.get(eventDict, 'shiftKey', false);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.metaKey = goog.object.get(eventDict, 'metaKey', false);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.button = goog.object.get(eventDict, 'button', 0);
    +
    +  /**
    +   * @type {Node}
    +   */
    +  this.relatedTarget = goog.object.get(eventDict, 'relatedTarget', null);
    +
    +  // PointerEvent related properties
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   */
    +  this.pointerId = goog.object.get(eventDict, 'pointerId', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.width = goog.object.get(eventDict, 'width', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.height = goog.object.get(eventDict, 'height', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.tiltX = goog.object.get(eventDict, 'tiltX', 0);
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.tiltY = goog.object.get(eventDict, 'tiltY', 0);
    +
    +  /**
    +   * @type {string}
    +   */
    +  this.pointerType = goog.object.get(eventDict, 'pointerType', '');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.hwTimestamp = goog.object.get(eventDict, 'hwTimestamp', 0);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.isPrimary = goog.object.get(eventDict, 'isPrimary', false);
    +
    +  // keep the semantics of preventDefault
    +  if (browserEvent.preventDefault) {
    +    this.preventDefault = function() {
    +      browserEvent.preventDefault();
    +    };
    +  }
    +};
    +goog.inherits(ol.pointer.PointerEvent, goog.events.Event);
    +
    +
    +/**
    + * @private
    + * @param {Object.<string, ?>} eventDict
    + * @return {number}
    + */
    +ol.pointer.PointerEvent.prototype.getButtons_ = function(eventDict) {
    +  // According to the w3c spec,
    +  // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
    +  // MouseEvent.button == 0 can mean either no mouse button depressed, or the
    +  // left mouse button depressed.
    +  //
    +  // As of now, the only way to distinguish between the two states of
    +  // MouseEvent.button is by using the deprecated MouseEvent.which property, as
    +  // this maps mouse buttons to positive integers > 0, and uses 0 to mean that
    +  // no mouse button is held.
    +  //
    +  // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
    +  // but initMouseEvent does not expose an argument with which to set
    +  // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
    +  // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
    +  // of app developers.
    +  //
    +  // The only way to propagate the correct state of MouseEvent.which and
    +  // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
    +  // is to call initMouseEvent with a buttonArg value of -1.
    +  //
    +  // This is fixed with DOM Level 4's use of buttons
    +  var buttons;
    +  if (eventDict.buttons || ol.pointer.PointerEvent.HAS_BUTTONS) {
    +    buttons = eventDict.buttons;
    +  } else {
    +    switch (eventDict.which) {
    +      case 1: buttons = 1; break;
    +      case 2: buttons = 4; break;
    +      case 3: buttons = 2; break;
    +      default: buttons = 0;
    +    }
    +  }
    +  return buttons;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Object.<string, ?>} eventDict
    + * @param {number} buttons
    + * @return {number}
    + */
    +ol.pointer.PointerEvent.prototype.getPressure_ = function(eventDict, buttons) {
    +  // Spec requires that pointers without pressure specified use 0.5 for down
    +  // state and 0 for up state.
    +  var pressure = 0;
    +  if (eventDict.pressure) {
    +    pressure = eventDict.pressure;
    +  } else {
    +    pressure = buttons ? 0.5 : 0;
    +  }
    +  return pressure;
    +};
    +
    +
    +/**
    + * Is the `buttons` property supported?
    + * @type {boolean}
    + */
    +ol.pointer.PointerEvent.HAS_BUTTONS = false;
    +
    +
    +/**
    + * Checks if the `buttons` property is supported.
    + */
    +(function() {
    +  try {
    +    var ev = new MouseEvent('click', {buttons: 1});
    +    ol.pointer.PointerEvent.HAS_BUTTONS = ev.buttons === 1;
    +  } catch (e) {
    +  }
    +})();
    diff --git a/mapbox-gl/ol/ol/pointer/pointereventhandler.js b/mapbox-gl/ol/ol/pointer/pointereventhandler.js
    new file mode 100644
    index 0000000000..367fb77193
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/pointereventhandler.js
    @@ -0,0 +1,496 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.PointerEventHandler');
    +
    +goog.require('goog.array');
    +goog.require('goog.events');
    +goog.require('goog.events.BrowserEvent');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventTarget');
    +
    +goog.require('ol.has');
    +goog.require('ol.pointer.MouseSource');
    +goog.require('ol.pointer.MsSource');
    +goog.require('ol.pointer.NativeSource');
    +goog.require('ol.pointer.PointerEvent');
    +goog.require('ol.pointer.TouchSource');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @param {Element|HTMLDocument} element Viewport element.
    + */
    +ol.pointer.PointerEventHandler = function(element) {
    +  goog.base(this);
    +
    +  /**
    +   * @const
    +   * @private
    +   * @type {Element|HTMLDocument}
    +   */
    +  this.element_ = element;
    +
    +  /**
    +   * @const
    +   * @type {Object.<string, goog.events.BrowserEvent|Object>}
    +   */
    +  this.pointerMap = {};
    +
    +  /**
    +   * @type {Object.<string, function(goog.events.BrowserEvent)>}
    +   * @private
    +   */
    +  this.eventMap_ = {};
    +
    +  /**
    +   * @type {Array.<ol.pointer.EventSource>}
    +   * @private
    +   */
    +  this.eventSourceList_ = [];
    +
    +  this.registerSources();
    +};
    +goog.inherits(ol.pointer.PointerEventHandler, goog.events.EventTarget);
    +
    +
    +/**
    + * Set up the event sources (mouse, touch and native pointers)
    + * that generate pointer events.
    + */
    +ol.pointer.PointerEventHandler.prototype.registerSources = function() {
    +  if (ol.has.POINTER) {
    +    this.registerSource('native', new ol.pointer.NativeSource(this));
    +  } else if (ol.has.MSPOINTER) {
    +    this.registerSource('ms', new ol.pointer.MsSource(this));
    +  } else {
    +    var mouseSource = new ol.pointer.MouseSource(this);
    +    this.registerSource('mouse', mouseSource);
    +
    +    if (ol.has.TOUCH) {
    +      this.registerSource('touch',
    +          new ol.pointer.TouchSource(this, mouseSource));
    +    }
    +  }
    +
    +  // register events on the viewport element
    +  this.register_();
    +};
    +
    +
    +/**
    + * Add a new event source that will generate pointer events.
    + *
    + * @param {string} name A name for the event source
    + * @param {ol.pointer.EventSource} source
    + */
    +ol.pointer.PointerEventHandler.prototype.registerSource =
    +    function(name, source) {
    +  var s = source;
    +  var newEvents = s.getEvents();
    +
    +  if (newEvents) {
    +    goog.array.forEach(newEvents, function(e) {
    +      var handler = s.getHandlerForEvent(e);
    +
    +      if (handler) {
    +        this.eventMap_[e] = goog.bind(handler, s);
    +      }
    +    }, this);
    +    this.eventSourceList_.push(s);
    +  }
    +};
    +
    +
    +/**
    + * Set up the events for all registered event sources.
    + * @private
    + */
    +ol.pointer.PointerEventHandler.prototype.register_ = function() {
    +  var l = this.eventSourceList_.length;
    +  var eventSource;
    +  for (var i = 0; i < l; i++) {
    +    eventSource = this.eventSourceList_[i];
    +    this.addEvents_(eventSource.getEvents());
    +  }
    +};
    +
    +
    +/**
    + * Remove all registered events.
    + * @private
    + */
    +ol.pointer.PointerEventHandler.prototype.unregister_ = function() {
    +  var l = this.eventSourceList_.length;
    +  var eventSource;
    +  for (var i = 0; i < l; i++) {
    +    eventSource = this.eventSourceList_[i];
    +    this.removeEvents_(eventSource.getEvents());
    +  }
    +};
    +
    +
    +/**
    + * Calls the right handler for a new event.
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent Browser event.
    + */
    +ol.pointer.PointerEventHandler.prototype.eventHandler_ = function(inEvent) {
    +  var type = inEvent.type;
    +  var handler = this.eventMap_[type];
    +  if (handler) {
    +    handler(inEvent);
    +  }
    +};
    +
    +
    +/**
    + * Setup listeners for the given events.
    + * @private
    + * @param {Array.<string>} events List of events.
    + */
    +ol.pointer.PointerEventHandler.prototype.addEvents_ = function(events) {
    +  goog.array.forEach(events, function(eventName) {
    +    goog.events.listen(this.element_, eventName,
    +        this.eventHandler_, false, this);
    +  }, this);
    +};
    +
    +
    +/**
    + * Unregister listeners for the given events.
    + * @private
    + * @param {Array.<string>} events List of events.
    + */
    +ol.pointer.PointerEventHandler.prototype.removeEvents_ = function(events) {
    +  goog.array.forEach(events, function(e) {
    +    goog.events.unlisten(this.element_, e,
    +        this.eventHandler_, false, this);
    +  }, this);
    +};
    +
    +
    +/**
    + * Returns a snapshot of inEvent, with writable properties.
    + *
    + * @param {goog.events.BrowserEvent} browserEvent Browser event.
    + * @param {Event|Touch} inEvent An event that contains
    + *    properties to copy.
    + * @return {Object} An object containing shallow copies of
    + *    `inEvent`'s properties.
    + */
    +ol.pointer.PointerEventHandler.prototype.cloneEvent =
    +    function(browserEvent, inEvent) {
    +  var eventCopy = {}, p;
    +  for (var i = 0, ii = ol.pointer.CLONE_PROPS.length; i < ii; i++) {
    +    p = ol.pointer.CLONE_PROPS[i][0];
    +    eventCopy[p] =
    +        browserEvent[p] ||
    +        inEvent[p] ||
    +        ol.pointer.CLONE_PROPS[i][1];
    +  }
    +
    +  return eventCopy;
    +};
    +
    +
    +// EVENTS
    +
    +
    +/**
    + * Triggers a 'pointerdown' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.down =
    +    function(pointerEventData, browserEvent) {
    +  this.fireEvent(ol.pointer.EventType.POINTERDOWN,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointermove' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.move =
    +    function(pointerEventData, browserEvent) {
    +  this.fireEvent(ol.pointer.EventType.POINTERMOVE,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointerup' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.up =
    +    function(pointerEventData, browserEvent) {
    +  this.fireEvent(ol.pointer.EventType.POINTERUP,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointerenter' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.enter =
    +    function(pointerEventData, browserEvent) {
    +  pointerEventData.bubbles = false;
    +  this.fireEvent(ol.pointer.EventType.POINTERENTER,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointerleave' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.leave =
    +    function(pointerEventData, browserEvent) {
    +  pointerEventData.bubbles = false;
    +  this.fireEvent(ol.pointer.EventType.POINTERLEAVE,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointerover' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.over =
    +    function(pointerEventData, browserEvent) {
    +  pointerEventData.bubbles = true;
    +  this.fireEvent(ol.pointer.EventType.POINTEROVER,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointerout' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.out =
    +    function(pointerEventData, browserEvent) {
    +  pointerEventData.bubbles = true;
    +  this.fireEvent(ol.pointer.EventType.POINTEROUT,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a 'pointercancel' event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.cancel =
    +    function(pointerEventData, browserEvent) {
    +  this.fireEvent(ol.pointer.EventType.POINTERCANCEL,
    +      pointerEventData, browserEvent);
    +};
    +
    +
    +/**
    + * Triggers a combination of 'pointerout' and 'pointerleave' events.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.leaveOut =
    +    function(pointerEventData, browserEvent) {
    +  this.out(pointerEventData, browserEvent);
    +  if (!this.contains_(
    +      pointerEventData.target,
    +      pointerEventData.relatedTarget)) {
    +    this.leave(pointerEventData, browserEvent);
    +  }
    +};
    +
    +
    +/**
    + * Triggers a combination of 'pointerover' and 'pointerevents' events.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.enterOver =
    +    function(pointerEventData, browserEvent) {
    +  this.over(pointerEventData, browserEvent);
    +  if (!this.contains_(
    +      pointerEventData.target,
    +      pointerEventData.relatedTarget)) {
    +    this.enter(pointerEventData, browserEvent);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {Element} container
    + * @param {Element} contained
    + * @return {boolean} Returns true if the container element
    + *   contains the other element.
    + */
    +ol.pointer.PointerEventHandler.prototype.contains_ =
    +    function(container, contained) {
    +  return container.contains(contained);
    +};
    +
    +
    +// EVENT CREATION AND TRACKING
    +/**
    + * Creates a new Event of type `inType`, based on the information in
    + * `pointerEventData`.
    + *
    + * @param {string} inType A string representing the type of event to create.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + * @return {ol.pointer.PointerEvent} A PointerEvent of type `inType`.
    + */
    +ol.pointer.PointerEventHandler.prototype.makeEvent =
    +    function(inType, pointerEventData, browserEvent) {
    +  return new ol.pointer.PointerEvent(inType, browserEvent, pointerEventData);
    +};
    +
    +
    +/**
    + * Make and dispatch an event in one call.
    + * @param {string} inType A string representing the type of event.
    + * @param {Object} pointerEventData
    + * @param {goog.events.BrowserEvent } browserEvent
    + */
    +ol.pointer.PointerEventHandler.prototype.fireEvent =
    +    function(inType, pointerEventData, browserEvent) {
    +  var e = this.makeEvent(inType, pointerEventData, browserEvent);
    +  this.dispatchEvent(e);
    +};
    +
    +
    +/**
    + * Creates a pointer event from a native pointer event
    + * and dispatches this event.
    + * @param {goog.events.BrowserEvent} nativeEvent A platform event with a target.
    + */
    +ol.pointer.PointerEventHandler.prototype.fireNativeEvent =
    +    function(nativeEvent) {
    +  var e = this.makeEvent(nativeEvent.type, nativeEvent.getBrowserEvent(),
    +      nativeEvent);
    +  this.dispatchEvent(e);
    +};
    +
    +
    +/**
    + * Wrap a native mouse event into a pointer event.
    + * This proxy method is required for the legacy IE support.
    + * @param {string} eventType The pointer event type.
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @return {ol.pointer.PointerEvent}
    + */
    +ol.pointer.PointerEventHandler.prototype.wrapMouseEvent =
    +    function(eventType, browserEvent) {
    +  var pointerEvent = this.makeEvent(
    +      eventType,
    +      ol.pointer.MouseSource.prepareEvent(browserEvent, this),
    +      browserEvent
    +      );
    +  return pointerEvent;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.pointer.PointerEventHandler.prototype.disposeInternal = function() {
    +  this.unregister_();
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Constants for event names.
    + * @enum {string}
    + */
    +ol.pointer.EventType = {
    +  POINTERMOVE: 'pointermove',
    +  POINTERDOWN: 'pointerdown',
    +  POINTERUP: 'pointerup',
    +  POINTEROVER: 'pointerover',
    +  POINTEROUT: 'pointerout',
    +  POINTERENTER: 'pointerenter',
    +  POINTERLEAVE: 'pointerleave',
    +  POINTERCANCEL: 'pointercancel'
    +};
    +
    +
    +/**
    + * Properties to copy when cloning an event, with default values.
    + * @type {Array.<Array>}
    + */
    +ol.pointer.CLONE_PROPS = [
    +  // MouseEvent
    +  ['bubbles', false],
    +  ['cancelable', false],
    +  ['view', null],
    +  ['detail', null],
    +  ['screenX', 0],
    +  ['screenY', 0],
    +  ['clientX', 0],
    +  ['clientY', 0],
    +  ['ctrlKey', false],
    +  ['altKey', false],
    +  ['shiftKey', false],
    +  ['metaKey', false],
    +  ['button', 0],
    +  ['relatedTarget', null],
    +  // DOM Level 3
    +  ['buttons', 0],
    +  // PointerEvent
    +  ['pointerId', 0],
    +  ['width', 0],
    +  ['height', 0],
    +  ['pressure', 0],
    +  ['tiltX', 0],
    +  ['tiltY', 0],
    +  ['pointerType', ''],
    +  ['hwTimestamp', 0],
    +  ['isPrimary', false],
    +  // event instance
    +  ['type', ''],
    +  ['target', null],
    +  ['currentTarget', null],
    +  ['which', 0]
    +];
    diff --git a/mapbox-gl/ol/ol/pointer/touchsource.js b/mapbox-gl/ol/ol/pointer/touchsource.js
    new file mode 100644
    index 0000000000..17befaeac4
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/pointer/touchsource.js
    @@ -0,0 +1,453 @@
    +// Based on https://github.com/Polymer/PointerEvents
    +
    +// Copyright (c) 2013 The Polymer Authors. All rights reserved.
    +//
    +// Redistribution and use in source and binary forms, with or without
    +// modification, are permitted provided that the following conditions are
    +// met:
    +//
    +// * Redistributions of source code must retain the above copyright
    +// notice, this list of conditions and the following disclaimer.
    +// * Redistributions in binary form must reproduce the above
    +// copyright notice, this list of conditions and the following disclaimer
    +// in the documentation and/or other materials provided with the
    +// distribution.
    +// * Neither the name of Google Inc. nor the names of its
    +// contributors may be used to endorse or promote products derived from
    +// this software without specific prior written permission.
    +//
    +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +goog.provide('ol.pointer.TouchSource');
    +
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('ol.pointer.EventSource');
    +goog.require('ol.pointer.MouseSource');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {ol.pointer.PointerEventHandler} dispatcher
    + * @param {ol.pointer.MouseSource} mouseSource
    + * @extends {ol.pointer.EventSource}
    + */
    +ol.pointer.TouchSource = function(dispatcher, mouseSource) {
    +  var mapping = {
    +    'touchstart': this.touchstart,
    +    'touchmove': this.touchmove,
    +    'touchend': this.touchend,
    +    'touchcancel': this.touchcancel
    +  };
    +  goog.base(this, dispatcher, mapping);
    +
    +  /**
    +   * @const
    +   * @type {Object.<string, goog.events.BrowserEvent|Object>}
    +   */
    +  this.pointerMap = dispatcher.pointerMap;
    +
    +  /**
    +   * @const
    +   * @type {ol.pointer.MouseSource}
    +   */
    +  this.mouseSource = mouseSource;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.firstTouchId_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.clickCount_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.resetId_ = undefined;
    +};
    +goog.inherits(ol.pointer.TouchSource, ol.pointer.EventSource);
    +
    +
    +/**
    + * Mouse event timeout: This should be long enough to
    + * ignore compat mouse events made by touch.
    + * @const
    + * @type {number}
    + */
    +ol.pointer.TouchSource.DEDUP_TIMEOUT = 2500;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT = 200;
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.pointer.TouchSource.POINTER_TYPE = 'touch';
    +
    +
    +/**
    + * @private
    + * @param {Touch} inTouch
    + * @return {boolean} True, if this is the primary touch.
    + */
    +ol.pointer.TouchSource.prototype.isPrimaryTouch_ = function(inTouch) {
    +  return this.firstTouchId_ === inTouch.identifier;
    +};
    +
    +
    +/**
    + * Set primary touch if there are no pointers, or the only pointer is the mouse.
    + * @param {Touch} inTouch
    + * @private
    + */
    +ol.pointer.TouchSource.prototype.setPrimaryTouch_ = function(inTouch) {
    +  var count = goog.object.getCount(this.pointerMap);
    +  if (count === 0 || (count === 1 && goog.object.containsKey(this.pointerMap,
    +      ol.pointer.MouseSource.POINTER_ID.toString()))) {
    +    this.firstTouchId_ = inTouch.identifier;
    +    this.cancelResetClickCount_();
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.removePrimaryPointer_ = function(inPointer) {
    +  if (inPointer.isPrimary) {
    +    this.firstTouchId_ = undefined;
    +    this.resetClickCount_();
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.pointer.TouchSource.prototype.resetClickCount_ = function() {
    +  this.resetId_ = goog.global.setTimeout(
    +      goog.bind(this.resetClickCountHandler_, this),
    +      ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT);
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.pointer.TouchSource.prototype.resetClickCountHandler_ = function() {
    +  this.clickCount_ = 0;
    +  this.resetId_ = undefined;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.pointer.TouchSource.prototype.cancelResetClickCount_ = function() {
    +  if (goog.isDef(this.resetId_)) {
    +    goog.global.clearTimeout(this.resetId_);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent Browser event
    + * @param {Touch} inTouch Touch event
    + * @return {Object}
    + */
    +ol.pointer.TouchSource.prototype.touchToPointer_ =
    +    function(browserEvent, inTouch) {
    +  var e = this.dispatcher.cloneEvent(browserEvent, inTouch);
    +  // Spec specifies that pointerId 1 is reserved for Mouse.
    +  // Touch identifiers can start at 0.
    +  // Add 2 to the touch identifier for compatibility.
    +  e.pointerId = inTouch.identifier + 2;
    +  // TODO: check if this is necessary?
    +  //e.target = findTarget(e);
    +  e.bubbles = true;
    +  e.cancelable = true;
    +  e.detail = this.clickCount_;
    +  e.button = 0;
    +  e.buttons = 1;
    +  e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
    +  e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
    +  e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
    +  e.isPrimary = this.isPrimaryTouch_(inTouch);
    +  e.pointerType = ol.pointer.TouchSource.POINTER_TYPE;
    +
    +  // make sure that the properties that are different for
    +  // each `Touch` object are not copied from the BrowserEvent object
    +  e.clientX = inTouch.clientX;
    +  e.clientY = inTouch.clientY;
    +  e.screenX = inTouch.screenX;
    +  e.screenY = inTouch.screenY;
    +
    +  return e;
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent Touch event
    + * @param {function(goog.events.BrowserEvent, Object)} inFunction
    + */
    +ol.pointer.TouchSource.prototype.processTouches_ =
    +    function(inEvent, inFunction) {
    +  var touches = Array.prototype.slice.call(
    +      inEvent.getBrowserEvent().changedTouches);
    +  var count = touches.length;
    +  function preventDefault() {
    +    inEvent.preventDefault();
    +  }
    +  var i, pointer;
    +  for (i = 0; i < count; ++i) {
    +    pointer = this.touchToPointer_(inEvent, touches[i]);
    +    // forward touch preventDefaults
    +    pointer.preventDefault = preventDefault;
    +    inFunction.call(this, inEvent, pointer);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {TouchList} touchList
    + * @param {number} searchId
    + * @return {boolean} True, if the `Touch` with the given id is in the list.
    + */
    +ol.pointer.TouchSource.prototype.findTouch_ = function(touchList, searchId) {
    +  var l = touchList.length;
    +  var touch;
    +  for (var i = 0; i < l; i++) {
    +    touch = touchList[i];
    +    if (touch.identifier === searchId) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * In some instances, a touchstart can happen without a touchend. This
    + * leaves the pointermap in a broken state.
    + * Therefore, on every touchstart, we remove the touches that did not fire a
    + * touchend event.
    + * To keep state globally consistent, we fire a pointercancel for
    + * this "abandoned" touch
    + *
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.vacuumTouches_ = function(inEvent) {
    +  var touchList = inEvent.getBrowserEvent().touches;
    +  // pointerMap.getCount() should be < touchList.length here,
    +  // as the touchstart has not been processed yet.
    +  var keys = goog.object.getKeys(this.pointerMap);
    +  var count = keys.length;
    +  if (count >= touchList.length) {
    +    var d = [];
    +    var i, key, value;
    +    for (i = 0; i < count; ++i) {
    +      key = keys[i];
    +      value = this.pointerMap[key];
    +      // Never remove pointerId == 1, which is mouse.
    +      // Touch identifiers are 2 smaller than their pointerId, which is the
    +      // index in pointermap.
    +      if (key != ol.pointer.MouseSource.POINTER_ID &&
    +          !this.findTouch_(touchList, key - 2)) {
    +        d.push(value.out);
    +      }
    +    }
    +    for (i = 0; i < d.length; ++i) {
    +      this.cancelOut_(inEvent, d[i]);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Handler for `touchstart`, triggers `pointerover`,
    + * `pointerenter` and `pointerdown` events.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.touchstart = function(inEvent) {
    +  this.vacuumTouches_(inEvent);
    +  this.setPrimaryTouch_(inEvent.getBrowserEvent().changedTouches[0]);
    +  this.dedupSynthMouse_(inEvent);
    +  this.clickCount_++;
    +  this.processTouches_(inEvent, this.overDown_);
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.overDown_ = function(browserEvent, inPointer) {
    +  this.pointerMap[inPointer.pointerId] = {
    +    target: inPointer.target,
    +    out: inPointer,
    +    outTarget: inPointer.target
    +  };
    +  this.dispatcher.over(inPointer, browserEvent);
    +  this.dispatcher.enter(inPointer, browserEvent);
    +  this.dispatcher.down(inPointer, browserEvent);
    +};
    +
    +
    +/**
    + * Handler for `touchmove`.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.touchmove = function(inEvent) {
    +  inEvent.preventDefault();
    +  this.processTouches_(inEvent, this.moveOverOut_);
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.moveOverOut_ =
    +    function(browserEvent, inPointer) {
    +  var event = inPointer;
    +  var pointer = this.pointerMap[event.pointerId];
    +  // a finger drifted off the screen, ignore it
    +  if (!pointer) {
    +    return;
    +  }
    +  var outEvent = pointer.out;
    +  var outTarget = pointer.outTarget;
    +  this.dispatcher.move(event, browserEvent);
    +  if (outEvent && outTarget !== event.target) {
    +    outEvent.relatedTarget = event.target;
    +    event.relatedTarget = outTarget;
    +    // recover from retargeting by shadow
    +    outEvent.target = outTarget;
    +    if (event.target) {
    +      this.dispatcher.leaveOut(outEvent, browserEvent);
    +      this.dispatcher.enterOver(event, browserEvent);
    +    } else {
    +      // clean up case when finger leaves the screen
    +      event.target = outTarget;
    +      event.relatedTarget = null;
    +      this.cancelOut_(browserEvent, event);
    +    }
    +  }
    +  pointer.out = event;
    +  pointer.outTarget = event.target;
    +};
    +
    +
    +/**
    + * Handler for `touchend`, triggers `pointerup`,
    + * `pointerout` and `pointerleave` events.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.touchend = function(inEvent) {
    +  this.dedupSynthMouse_(inEvent);
    +  this.processTouches_(inEvent, this.upOut_);
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.upOut_ = function(browserEvent, inPointer) {
    +  this.dispatcher.up(inPointer, browserEvent);
    +  this.dispatcher.out(inPointer, browserEvent);
    +  this.dispatcher.leave(inPointer, browserEvent);
    +  this.cleanUpPointer_(inPointer);
    +};
    +
    +
    +/**
    + * Handler for `touchcancel`, triggers `pointercancel`,
    + * `pointerout` and `pointerleave` events.
    + *
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.touchcancel = function(inEvent) {
    +  this.processTouches_(inEvent, this.cancelOut_);
    +};
    +
    +
    +/**
    + * @private
    + * @param {goog.events.BrowserEvent} browserEvent
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.cancelOut_ =
    +    function(browserEvent, inPointer) {
    +  this.dispatcher.cancel(inPointer, browserEvent);
    +  this.dispatcher.out(inPointer, browserEvent);
    +  this.dispatcher.leave(inPointer, browserEvent);
    +  this.cleanUpPointer_(inPointer);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Object} inPointer
    + */
    +ol.pointer.TouchSource.prototype.cleanUpPointer_ = function(inPointer) {
    +  goog.object.remove(this.pointerMap, inPointer.pointerId);
    +  this.removePrimaryPointer_(inPointer);
    +};
    +
    +
    +/**
    + * Prevent synth mouse events from creating pointer events.
    + *
    + * @private
    + * @param {goog.events.BrowserEvent} inEvent
    + */
    +ol.pointer.TouchSource.prototype.dedupSynthMouse_ = function(inEvent) {
    +  var lts = this.mouseSource.lastTouches;
    +  var t = inEvent.getBrowserEvent().changedTouches[0];
    +  // only the primary finger will synth mouse events
    +  if (this.isPrimaryTouch_(t)) {
    +    // remember x/y of last touch
    +    var lt = /** @type {ol.Pixel} */ ([t.clientX, t.clientY]);
    +    lts.push(lt);
    +
    +    goog.global.setTimeout(function() {
    +      // remove touch after timeout
    +      goog.array.remove(lts, lt);
    +    }, ol.pointer.TouchSource.DEDUP_TIMEOUT);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/proj/common.js b/mapbox-gl/ol/ol/proj/common.js
    new file mode 100644
    index 0000000000..96e57dff68
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/proj/common.js
    @@ -0,0 +1,24 @@
    +goog.provide('ol.proj.common');
    +
    +goog.require('ol.proj');
    +goog.require('ol.proj.EPSG3857');
    +goog.require('ol.proj.EPSG4326');
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + * @api
    + */
    +ol.proj.common.add = function() {
    +  // Add transformations that don't alter coordinates to convert within set of
    +  // projections with equal meaning.
    +  ol.proj.addEquivalentProjections(ol.proj.EPSG3857.PROJECTIONS);
    +  ol.proj.addEquivalentProjections(ol.proj.EPSG4326.PROJECTIONS);
    +  // Add transformations to convert EPSG:4326 like coordinates to EPSG:3857 like
    +  // coordinates and back.
    +  ol.proj.addEquivalentTransforms(
    +      ol.proj.EPSG4326.PROJECTIONS,
    +      ol.proj.EPSG3857.PROJECTIONS,
    +      ol.proj.EPSG3857.fromEPSG4326,
    +      ol.proj.EPSG3857.toEPSG4326);
    +};
    diff --git a/mapbox-gl/ol/ol/proj/epsg3857projection.js b/mapbox-gl/ol/ol/proj/epsg3857projection.js
    new file mode 100644
    index 0000000000..e5ed7c0598
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/proj/epsg3857projection.js
    @@ -0,0 +1,158 @@
    +goog.provide('ol.proj.EPSG3857');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.math');
    +goog.require('ol.proj');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.proj.Units');
    +
    +
    +
    +/**
    + * @classdesc
    + * Projection object for web/spherical Mercator (EPSG:3857).
    + *
    + * @constructor
    + * @extends {ol.proj.Projection}
    + * @param {string} code Code.
    + * @private
    + */
    +ol.proj.EPSG3857_ = function(code) {
    +  goog.base(this, {
    +    code: code,
    +    units: ol.proj.Units.METERS,
    +    extent: ol.proj.EPSG3857.EXTENT,
    +    global: true,
    +    worldExtent: ol.proj.EPSG3857.WORLD_EXTENT
    +  });
    +};
    +goog.inherits(ol.proj.EPSG3857_, ol.proj.Projection);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.proj.EPSG3857_.prototype.getPointResolution = function(resolution, point) {
    +  return resolution / ol.math.cosh(point[1] / ol.proj.EPSG3857.RADIUS);
    +};
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.proj.EPSG3857.RADIUS = 6378137;
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.proj.EPSG3857.HALF_SIZE = Math.PI * ol.proj.EPSG3857.RADIUS;
    +
    +
    +/**
    + * @const
    + * @type {ol.Extent}
    + */
    +ol.proj.EPSG3857.EXTENT = [
    +  -ol.proj.EPSG3857.HALF_SIZE, -ol.proj.EPSG3857.HALF_SIZE,
    +  ol.proj.EPSG3857.HALF_SIZE, ol.proj.EPSG3857.HALF_SIZE
    +];
    +
    +
    +/**
    + * @const
    + * @type {ol.Extent}
    + */
    +ol.proj.EPSG3857.WORLD_EXTENT = [-180, -85, 180, 85];
    +
    +
    +/**
    + * Lists several projection codes with the same meaning as EPSG:3857.
    + *
    + * @type {Array.<string>}
    + */
    +ol.proj.EPSG3857.CODES = [
    +  'EPSG:3857',
    +  'EPSG:102100',
    +  'EPSG:102113',
    +  'EPSG:900913',
    +  'urn:ogc:def:crs:EPSG:6.18:3:3857',
    +  'urn:ogc:def:crs:EPSG::3857',
    +  'http://www.opengis.net/gml/srs/epsg.xml#3857'
    +];
    +
    +
    +/**
    + * Projections equal to EPSG:3857.
    + *
    + * @const
    + * @type {Array.<ol.proj.Projection>}
    + */
    +ol.proj.EPSG3857.PROJECTIONS = goog.array.map(
    +    ol.proj.EPSG3857.CODES,
    +    function(code) {
    +      return new ol.proj.EPSG3857_(code);
    +    });
    +
    +
    +/**
    + * Transformation from EPSG:4326 to EPSG:3857.
    + *
    + * @param {Array.<number>} input Input array of coordinate values.
    + * @param {Array.<number>=} opt_output Output array of coordinate values.
    + * @param {number=} opt_dimension Dimension (default is `2`).
    + * @return {Array.<number>} Output array of coordinate values.
    + */
    +ol.proj.EPSG3857.fromEPSG4326 = function(input, opt_output, opt_dimension) {
    +  var length = input.length,
    +      dimension = opt_dimension > 1 ? opt_dimension : 2,
    +      output = opt_output;
    +  if (!goog.isDef(output)) {
    +    if (dimension > 2) {
    +      // preserve values beyond second dimension
    +      output = input.slice();
    +    } else {
    +      output = new Array(length);
    +    }
    +  }
    +  goog.asserts.assert(output.length % dimension === 0);
    +  for (var i = 0; i < length; i += dimension) {
    +    output[i] = ol.proj.EPSG3857.RADIUS * Math.PI * input[i] / 180;
    +    output[i + 1] = ol.proj.EPSG3857.RADIUS *
    +        Math.log(Math.tan(Math.PI * (input[i + 1] + 90) / 360));
    +  }
    +  return output;
    +};
    +
    +
    +/**
    + * Transformation from EPSG:3857 to EPSG:4326.
    + *
    + * @param {Array.<number>} input Input array of coordinate values.
    + * @param {Array.<number>=} opt_output Output array of coordinate values.
    + * @param {number=} opt_dimension Dimension (default is `2`).
    + * @return {Array.<number>} Output array of coordinate values.
    + */
    +ol.proj.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) {
    +  var length = input.length,
    +      dimension = opt_dimension > 1 ? opt_dimension : 2,
    +      output = opt_output;
    +  if (!goog.isDef(output)) {
    +    if (dimension > 2) {
    +      // preserve values beyond second dimension
    +      output = input.slice();
    +    } else {
    +      output = new Array(length);
    +    }
    +  }
    +  goog.asserts.assert(output.length % dimension === 0);
    +  for (var i = 0; i < length; i += dimension) {
    +    output[i] = 180 * input[i] / (ol.proj.EPSG3857.RADIUS * Math.PI);
    +    output[i + 1] = 360 * Math.atan(
    +        Math.exp(input[i + 1] / ol.proj.EPSG3857.RADIUS)) / Math.PI - 90;
    +  }
    +  return output;
    +};
    diff --git a/mapbox-gl/ol/ol/proj/epsg4326projection.js b/mapbox-gl/ol/ol/proj/epsg4326projection.js
    new file mode 100644
    index 0000000000..cbbf571d8e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/proj/epsg4326projection.js
    @@ -0,0 +1,68 @@
    +goog.provide('ol.proj.EPSG4326');
    +
    +goog.require('ol.proj');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.proj.Units');
    +
    +
    +
    +/**
    + * @classdesc
    + * Projection object for WGS84 geographic coordinates (EPSG:4326).
    + *
    + * Note that OpenLayers does not strictly comply with the EPSG definition.
    + * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x).
    + * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates.
    + *
    + * @constructor
    + * @extends {ol.proj.Projection}
    + * @param {string} code Code.
    + * @param {string=} opt_axisOrientation Axis orientation.
    + * @private
    + */
    +ol.proj.EPSG4326_ = function(code, opt_axisOrientation) {
    +  goog.base(this, {
    +    code: code,
    +    units: ol.proj.Units.DEGREES,
    +    extent: ol.proj.EPSG4326.EXTENT,
    +    axisOrientation: opt_axisOrientation,
    +    global: true,
    +    worldExtent: ol.proj.EPSG4326.EXTENT
    +  });
    +};
    +goog.inherits(ol.proj.EPSG4326_, ol.proj.Projection);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.proj.EPSG4326_.prototype.getPointResolution = function(resolution, point) {
    +  return resolution;
    +};
    +
    +
    +/**
    + * Extent of the EPSG:4326 projection which is the whole world.
    + *
    + * @const
    + * @type {ol.Extent}
    + */
    +ol.proj.EPSG4326.EXTENT = [-180, -90, 180, 90];
    +
    +
    +/**
    + * Projections equal to EPSG:4326.
    + *
    + * @const
    + * @type {Array.<ol.proj.Projection>}
    + */
    +ol.proj.EPSG4326.PROJECTIONS = [
    +  new ol.proj.EPSG4326_('CRS:84'),
    +  new ol.proj.EPSG4326_('EPSG:4326', 'neu'),
    +  new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG::4326', 'neu'),
    +  new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'),
    +  new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:1.3:CRS84'),
    +  new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:2:84'),
    +  new ol.proj.EPSG4326_('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'),
    +  new ol.proj.EPSG4326_('urn:x-ogc:def:crs:EPSG:4326', 'neu')
    +];
    diff --git a/mapbox-gl/ol/ol/proj/proj.js b/mapbox-gl/ol/ol/proj/proj.js
    new file mode 100644
    index 0000000000..33c9da0f15
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/proj/proj.js
    @@ -0,0 +1,716 @@
    +goog.provide('ol.proj');
    +goog.provide('ol.proj.METERS_PER_UNIT');
    +goog.provide('ol.proj.Projection');
    +goog.provide('ol.proj.ProjectionLike');
    +goog.provide('ol.proj.Units');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('ol');
    +goog.require('ol.Extent');
    +goog.require('ol.TransformFunction');
    +goog.require('ol.extent');
    +goog.require('ol.sphere.NORMAL');
    +
    +
    +/**
    + * A projection as {@link ol.proj.Projection}, SRS identifier string or
    + * undefined.
    + * @typedef {ol.proj.Projection|string|undefined} ol.proj.ProjectionLike
    + * @api stable
    + */
    +ol.proj.ProjectionLike;
    +
    +
    +/**
    + * Projection units: `'degrees'`, `'ft'`, `'m'` or `'pixels'`.
    + * @enum {string}
    + * @api stable
    + */
    +ol.proj.Units = {
    +  DEGREES: 'degrees',
    +  FEET: 'ft',
    +  METERS: 'm',
    +  PIXELS: 'pixels',
    +  USFEET: 'us-ft'
    +};
    +
    +
    +/**
    + * Meters per unit lookup table.
    + * @const
    + * @type {Object.<ol.proj.Units, number>}
    + * @api stable
    + */
    +ol.proj.METERS_PER_UNIT = {};
    +ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] =
    +    2 * Math.PI * ol.sphere.NORMAL.radius / 360;
    +ol.proj.METERS_PER_UNIT[ol.proj.Units.FEET] = 0.3048;
    +ol.proj.METERS_PER_UNIT[ol.proj.Units.METERS] = 1;
    +ol.proj.METERS_PER_UNIT[ol.proj.Units.USFEET] = 1200 / 3937;
    +
    +
    +
    +/**
    + * @classdesc
    + * Projection definition class. One of these is created for each projection
    + * supported in the application and stored in the {@link ol.proj} namespace.
    + * You can use these in applications, but this is not required, as API params
    + * and options use {@link ol.proj.ProjectionLike} which means the simple string
    + * code will suffice.
    + *
    + * You can use {@link ol.proj.get} to retrieve the object for a particular
    + * projection.
    + *
    + * The library includes definitions for `EPSG:4326` and `EPSG:3857`, together
    + * with the following aliases:
    + * * `EPSG:4326`: CRS:84, urn:ogc:def:crs:EPSG:6.6:4326,
    + *     urn:ogc:def:crs:OGC:1.3:CRS84, urn:ogc:def:crs:OGC:2:84,
    + *     http://www.opengis.net/gml/srs/epsg.xml#4326,
    + *     urn:x-ogc:def:crs:EPSG:4326
    + * * `EPSG:3857`: EPSG:102100, EPSG:102113, EPSG:900913,
    + *     urn:ogc:def:crs:EPSG:6.18:3:3857,
    + *     http://www.opengis.net/gml/srs/epsg.xml#3857
    + *
    + * If you use proj4js, aliases can be added using `proj4.defs()`; see
    + * [documentation](https://github.com/proj4js/proj4js).
    + *
    + * @constructor
    + * @param {olx.ProjectionOptions} options Projection options.
    + * @struct
    + * @api stable
    + */
    +ol.proj.Projection = function(options) {
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.code_ = options.code;
    +
    +  /**
    +   * @private
    +   * @type {ol.proj.Units}
    +   */
    +  this.units_ = /** @type {ol.proj.Units} */ (options.units);
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.extent_ = goog.isDef(options.extent) ? options.extent : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.worldExtent_ = goog.isDef(options.worldExtent) ?
    +      options.worldExtent : null;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.axisOrientation_ = goog.isDef(options.axisOrientation) ?
    +      options.axisOrientation : 'enu';
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.global_ = goog.isDef(options.global) ? options.global : false;
    +
    +  /**
    +   * @private
    +   * @type {ol.tilegrid.TileGrid}
    +   */
    +  this.defaultTileGrid_ = null;
    +
    +};
    +
    +
    +/**
    + * Get the code for this projection, e.g. 'EPSG:4326'.
    + * @return {string} Code.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.getCode = function() {
    +  return this.code_;
    +};
    +
    +
    +/**
    + * Get the validity extent for this projection.
    + * @return {ol.Extent} Extent.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.getExtent = function() {
    +  return this.extent_;
    +};
    +
    +
    +/**
    + * Get the units of this projection.
    + * @return {ol.proj.Units} Units.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.getUnits = function() {
    +  return this.units_;
    +};
    +
    +
    +/**
    + * Get the amount of meters per unit of this projection.  If the projection is
    + * not configured with a units identifier, the return is `undefined`.
    + * @return {number|undefined} Meters.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.getMetersPerUnit = function() {
    +  return ol.proj.METERS_PER_UNIT[this.units_];
    +};
    +
    +
    +/**
    + * Get the world extent for this projection.
    + * @return {ol.Extent} Extent.
    + * @api
    + */
    +ol.proj.Projection.prototype.getWorldExtent = function() {
    +  return this.worldExtent_;
    +};
    +
    +
    +/**
    + * Get the axis orientation of this projection.
    + * Example values are:
    + * enu - the default easting, northing, elevation.
    + * neu - northing, easting, up - useful for "lat/long" geographic coordinates,
    + *     or south orientated transverse mercator.
    + * wnu - westing, northing, up - some planetary coordinate systems have
    + *     "west positive" coordinate systems
    + * @return {string} Axis orientation.
    + */
    +ol.proj.Projection.prototype.getAxisOrientation = function() {
    +  return this.axisOrientation_;
    +};
    +
    +
    +/**
    + * Is this projection a global projection which spans the whole world?
    + * @return {boolean} Whether the projection is global.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.isGlobal = function() {
    +  return this.global_;
    +};
    +
    +
    +/**
    + * @return {ol.tilegrid.TileGrid} The default tile grid.
    + */
    +ol.proj.Projection.prototype.getDefaultTileGrid = function() {
    +  return this.defaultTileGrid_;
    +};
    +
    +
    +/**
    + * @param {ol.tilegrid.TileGrid} tileGrid The default tile grid.
    + */
    +ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) {
    +  this.defaultTileGrid_ = tileGrid;
    +};
    +
    +
    +/**
    + * Set the validity extent for this projection.
    + * @param {ol.Extent} extent Extent.
    + * @api stable
    + */
    +ol.proj.Projection.prototype.setExtent = function(extent) {
    +  this.extent_ = extent;
    +};
    +
    +
    +/**
    + * Set the world extent for this projection.
    + * @param {ol.Extent} worldExtent World extent
    + *     [minlon, minlat, maxlon, maxlat].
    + * @api
    + */
    +ol.proj.Projection.prototype.setWorldExtent = function(worldExtent) {
    +  this.worldExtent_ = worldExtent;
    +};
    +
    +
    +/**
    + * Get the resolution of the point in degrees. For projections with degrees as
    + * the unit this will simply return the provided resolution. For other
    + * projections the point resolution is estimated by transforming the center
    + * pixel to EPSG:4326, measuring its width and height on the normal sphere,
    + * and taking the average of the width and height.
    + * @param {number} resolution Resolution.
    + * @param {ol.Coordinate} point Point.
    + * @return {number} Point resolution.
    + */
    +ol.proj.Projection.prototype.getPointResolution = function(resolution, point) {
    +  var units = this.getUnits();
    +  if (units == ol.proj.Units.DEGREES) {
    +    return resolution;
    +  } else {
    +    // Estimate point resolution by transforming the center pixel to EPSG:4326,
    +    // measuring its width and height on the normal sphere, and taking the
    +    // average of the width and height.
    +    var toEPSG4326 = ol.proj.getTransformFromProjections(
    +        this, ol.proj.get('EPSG:4326'));
    +    var vertices = [
    +      point[0] - resolution / 2, point[1],
    +      point[0] + resolution / 2, point[1],
    +      point[0], point[1] - resolution / 2,
    +      point[0], point[1] + resolution / 2
    +    ];
    +    vertices = toEPSG4326(vertices, vertices, 2);
    +    var width = ol.sphere.NORMAL.haversineDistance(
    +        vertices.slice(0, 2), vertices.slice(2, 4));
    +    var height = ol.sphere.NORMAL.haversineDistance(
    +        vertices.slice(4, 6), vertices.slice(6, 8));
    +    var pointResolution = (width + height) / 2;
    +    var metersPerUnit = this.getMetersPerUnit();
    +    if (goog.isDef(metersPerUnit)) {
    +      pointResolution /= metersPerUnit;
    +    }
    +    return pointResolution;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @type {Object.<string, ol.proj.Projection>}
    + */
    +ol.proj.projections_ = {};
    +
    +
    +/**
    + * @private
    + * @type {Object.<string, Object.<string, ol.TransformFunction>>}
    + */
    +ol.proj.transforms_ = {};
    +
    +
    +/**
    + * Registers transformation functions that don't alter coordinates. Those allow
    + * to transform between projections with equal meaning.
    + *
    + * @param {Array.<ol.proj.Projection>} projections Projections.
    + * @api
    + */
    +ol.proj.addEquivalentProjections = function(projections) {
    +  ol.proj.addProjections(projections);
    +  goog.array.forEach(projections, function(source) {
    +    goog.array.forEach(projections, function(destination) {
    +      if (source !== destination) {
    +        ol.proj.addTransform(source, destination, ol.proj.cloneTransform);
    +      }
    +    });
    +  });
    +};
    +
    +
    +/**
    + * Registers transformation functions to convert coordinates in any projection
    + * in projection1 to any projection in projection2.
    + *
    + * @param {Array.<ol.proj.Projection>} projections1 Projections with equal
    + *     meaning.
    + * @param {Array.<ol.proj.Projection>} projections2 Projections with equal
    + *     meaning.
    + * @param {ol.TransformFunction} forwardTransform Transformation from any
    + *   projection in projection1 to any projection in projection2.
    + * @param {ol.TransformFunction} inverseTransform Transform from any projection
    + *   in projection2 to any projection in projection1..
    + */
    +ol.proj.addEquivalentTransforms =
    +    function(projections1, projections2, forwardTransform, inverseTransform) {
    +  goog.array.forEach(projections1, function(projection1) {
    +    goog.array.forEach(projections2, function(projection2) {
    +      ol.proj.addTransform(projection1, projection2, forwardTransform);
    +      ol.proj.addTransform(projection2, projection1, inverseTransform);
    +    });
    +  });
    +};
    +
    +
    +/**
    + * Add a Projection object to the list of supported projections.
    + *
    + * @param {ol.proj.Projection} projection Projection instance.
    + * @api stable
    + */
    +ol.proj.addProjection = function(projection) {
    +  ol.proj.projections_[projection.getCode()] = projection;
    +  ol.proj.addTransform(projection, projection, ol.proj.cloneTransform);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.proj.Projection>} projections Projections.
    + */
    +ol.proj.addProjections = function(projections) {
    +  var addedProjections = [];
    +  goog.array.forEach(projections, function(projection) {
    +    addedProjections.push(ol.proj.addProjection(projection));
    +  });
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.proj.clearAllProjections = function() {
    +  ol.proj.projections_ = {};
    +  ol.proj.transforms_ = {};
    +};
    +
    +
    +/**
    + * @param {ol.proj.Projection|string|undefined} projection Projection.
    + * @param {string} defaultCode Default code.
    + * @return {ol.proj.Projection} Projection.
    + */
    +ol.proj.createProjection = function(projection, defaultCode) {
    +  if (!goog.isDefAndNotNull(projection)) {
    +    return ol.proj.get(defaultCode);
    +  } else if (goog.isString(projection)) {
    +    return ol.proj.get(projection);
    +  } else {
    +    goog.asserts.assertInstanceof(projection, ol.proj.Projection);
    +    return projection;
    +  }
    +};
    +
    +
    +/**
    + * Registers a conversion function to convert coordinates from the source
    + * projection to the destination projection.
    + *
    + * @param {ol.proj.Projection} source Source.
    + * @param {ol.proj.Projection} destination Destination.
    + * @param {ol.TransformFunction} transformFn Transform.
    + */
    +ol.proj.addTransform = function(source, destination, transformFn) {
    +  var sourceCode = source.getCode();
    +  var destinationCode = destination.getCode();
    +  var transforms = ol.proj.transforms_;
    +  if (!goog.object.containsKey(transforms, sourceCode)) {
    +    transforms[sourceCode] = {};
    +  }
    +  transforms[sourceCode][destinationCode] = transformFn;
    +};
    +
    +
    +/**
    + * Registers coordinate transform functions to convert coordinates between the
    + * source projection and the destination projection.
    + * The forward and inverse functions convert coordinate pairs; this function
    + * converts these into the functions used internally which also handle
    + * extents and coordinate arrays.
    + *
    + * @param {ol.proj.ProjectionLike} source Source projection.
    + * @param {ol.proj.ProjectionLike} destination Destination projection.
    + * @param {function(ol.Coordinate): ol.Coordinate} forward The forward transform
    + *     function (that is, from the source projection to the destination
    + *     projection) that takes a {@link ol.Coordinate} as argument and returns
    + *     the transformed {@link ol.Coordinate}.
    + * @param {function(ol.Coordinate): ol.Coordinate} inverse The inverse transform
    + *     function (that is, from the destination projection to the source
    + *     projection) that takes a {@link ol.Coordinate} as argument and returns
    + *     the transformed {@link ol.Coordinate}.
    + * @api stable
    + */
    +ol.proj.addCoordinateTransforms =
    +    function(source, destination, forward, inverse) {
    +  var sourceProj = ol.proj.get(source);
    +  var destProj = ol.proj.get(destination);
    +  ol.proj.addTransform(sourceProj, destProj,
    +      ol.proj.createTransformFromCoordinateTransform(forward));
    +  ol.proj.addTransform(destProj, sourceProj,
    +      ol.proj.createTransformFromCoordinateTransform(inverse));
    +};
    +
    +
    +/**
    + * Creates a {@link ol.TransformFunction} from a simple 2D coordinate transform
    + * function.
    + * @param {function(ol.Coordinate): ol.Coordinate} transform Coordinate
    + *     transform.
    + * @return {ol.TransformFunction} Transform function.
    + */
    +ol.proj.createTransformFromCoordinateTransform = function(transform) {
    +  return (
    +      /**
    +       * @param {Array.<number>} input Input.
    +       * @param {Array.<number>=} opt_output Output.
    +       * @param {number=} opt_dimension Dimension.
    +       * @return {Array.<number>} Output.
    +       */
    +      function(input, opt_output, opt_dimension) {
    +        var length = input.length;
    +        var dimension = goog.isDef(opt_dimension) ? opt_dimension : 2;
    +        var output = goog.isDef(opt_output) ? opt_output : new Array(length);
    +        var point, i, j;
    +        for (i = 0; i < length; i += dimension) {
    +          point = transform([input[i], input[i + 1]]);
    +          output[i] = point[0];
    +          output[i + 1] = point[1];
    +          for (j = dimension - 1; j >= 2; --j) {
    +            output[i + j] = input[i + j];
    +          }
    +        }
    +        return output;
    +      });
    +};
    +
    +
    +/**
    + * Unregisters the conversion function to convert coordinates from the source
    + * projection to the destination projection.  This method is used to clean up
    + * cached transforms during testing.
    + *
    + * @param {ol.proj.Projection} source Source projection.
    + * @param {ol.proj.Projection} destination Destination projection.
    + * @return {ol.TransformFunction} transformFn The unregistered transform.
    + */
    +ol.proj.removeTransform = function(source, destination) {
    +  var sourceCode = source.getCode();
    +  var destinationCode = destination.getCode();
    +  var transforms = ol.proj.transforms_;
    +  goog.asserts.assert(sourceCode in transforms);
    +  goog.asserts.assert(destinationCode in transforms[sourceCode]);
    +  var transform = transforms[sourceCode][destinationCode];
    +  delete transforms[sourceCode][destinationCode];
    +  var keys = goog.object.getKeys(transforms[sourceCode]);
    +  if (keys.length === 0) {
    +    delete transforms[sourceCode];
    +  }
    +  return transform;
    +};
    +
    +
    +/**
    + * Fetches a Projection object for the code specified.
    + *
    + * @param {ol.proj.ProjectionLike} projectionLike Either a code string which is
    + *     a combination of authority and identifier such as "EPSG:4326", or an
    + *     existing projection object, or undefined.
    + * @return {ol.proj.Projection} Projection object, or null if not in list.
    + * @api stable
    + */
    +ol.proj.get = function(projectionLike) {
    +  var projection;
    +  if (projectionLike instanceof ol.proj.Projection) {
    +    projection = projectionLike;
    +  } else if (goog.isString(projectionLike)) {
    +    var code = projectionLike;
    +    var projections = ol.proj.projections_;
    +    projection = projections[code];
    +    if (ol.ENABLE_PROJ4JS && !goog.isDef(projection) &&
    +        typeof proj4 == 'function') {
    +      var def = proj4.defs(code);
    +      if (goog.isDef(def)) {
    +        var units = def.units;
    +        if (!goog.isDef(units)) {
    +          if (goog.isDef(def.to_meter)) {
    +            units = def.to_meter.toString();
    +            ol.proj.METERS_PER_UNIT[units] = def.to_meter;
    +          }
    +        }
    +        projection = new ol.proj.Projection({
    +          code: code,
    +          units: units,
    +          axisOrientation: def.axis
    +        });
    +        ol.proj.addProjection(projection);
    +        var currentCode, currentDef, currentProj, proj4Transform;
    +        for (currentCode in projections) {
    +          currentDef = proj4.defs(currentCode);
    +          if (goog.isDef(currentDef)) {
    +            currentProj = ol.proj.get(currentCode);
    +            if (currentDef === def) {
    +              ol.proj.addEquivalentProjections([currentProj, projection]);
    +            } else {
    +              proj4Transform = proj4(currentCode, code);
    +              ol.proj.addCoordinateTransforms(currentProj, projection,
    +                  proj4Transform.forward, proj4Transform.inverse);
    +            }
    +          }
    +        }
    +      } else {
    +        goog.asserts.assert(goog.isDef(projection));
    +        projection = null;
    +      }
    +    }
    +  } else {
    +    projection = null;
    +  }
    +  return projection;
    +};
    +
    +
    +/**
    + * Checks if two projections are the same, that is every coordinate in one
    + * projection does represent the same geographic point as the same coordinate in
    + * the other projection.
    + *
    + * @param {ol.proj.Projection} projection1 Projection 1.
    + * @param {ol.proj.Projection} projection2 Projection 2.
    + * @return {boolean} Equivalent.
    + */
    +ol.proj.equivalent = function(projection1, projection2) {
    +  if (projection1 === projection2) {
    +    return true;
    +  } else if (projection1.getUnits() != projection2.getUnits()) {
    +    return false;
    +  } else {
    +    var transformFn = ol.proj.getTransformFromProjections(
    +        projection1, projection2);
    +    return transformFn === ol.proj.cloneTransform;
    +  }
    +};
    +
    +
    +/**
    + * Given the projection-like objects, searches for a transformation
    + * function to convert a coordinates array from the source projection to the
    + * destination projection.
    + *
    + * @param {ol.proj.ProjectionLike} source Source.
    + * @param {ol.proj.ProjectionLike} destination Destination.
    + * @return {ol.TransformFunction} Transform function.
    + * @api stable
    + */
    +ol.proj.getTransform = function(source, destination) {
    +  var sourceProjection = ol.proj.get(source);
    +  var destinationProjection = ol.proj.get(destination);
    +  return ol.proj.getTransformFromProjections(
    +      sourceProjection, destinationProjection);
    +};
    +
    +
    +/**
    + * Searches in the list of transform functions for the function for converting
    + * coordinates from the source projection to the destination projection.
    + *
    + * @param {ol.proj.Projection} sourceProjection Source Projection object.
    + * @param {ol.proj.Projection} destinationProjection Destination Projection
    + *     object.
    + * @return {ol.TransformFunction} Transform function.
    + */
    +ol.proj.getTransformFromProjections =
    +    function(sourceProjection, destinationProjection) {
    +  var transforms = ol.proj.transforms_;
    +  var sourceCode = sourceProjection.getCode();
    +  var destinationCode = destinationProjection.getCode();
    +  var transform;
    +  if (goog.object.containsKey(transforms, sourceCode) &&
    +      goog.object.containsKey(transforms[sourceCode], destinationCode)) {
    +    transform = transforms[sourceCode][destinationCode];
    +  }
    +  if (!goog.isDef(transform)) {
    +    goog.asserts.assert(goog.isDef(transform));
    +    transform = ol.proj.identityTransform;
    +  }
    +  return transform;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} input Input coordinate array.
    + * @param {Array.<number>=} opt_output Output array of coordinate values.
    + * @param {number=} opt_dimension Dimension.
    + * @return {Array.<number>} Input coordinate array (same array as input).
    + */
    +ol.proj.identityTransform = function(input, opt_output, opt_dimension) {
    +  if (goog.isDef(opt_output) && input !== opt_output) {
    +    // TODO: consider making this a warning instead
    +    goog.asserts.fail('This should not be used internally.');
    +    for (var i = 0, ii = input.length; i < ii; ++i) {
    +      opt_output[i] = input[i];
    +    }
    +    input = opt_output;
    +  }
    +  return input;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} input Input coordinate array.
    + * @param {Array.<number>=} opt_output Output array of coordinate values.
    + * @param {number=} opt_dimension Dimension.
    + * @return {Array.<number>} Output coordinate array (new array, same coordinate
    + *     values).
    + */
    +ol.proj.cloneTransform = function(input, opt_output, opt_dimension) {
    +  var output;
    +  if (goog.isDef(opt_output)) {
    +    for (var i = 0, ii = input.length; i < ii; ++i) {
    +      opt_output[i] = input[i];
    +    }
    +    output = opt_output;
    +  } else {
    +    output = input.slice();
    +  }
    +  return output;
    +};
    +
    +
    +/**
    + * Transforms a coordinate from source projection to destination projection.
    + * This returns a new coordinate (and does not modify the original).
    + *
    + * See {@link ol.proj.transformExtent} for extent transformation.
    + * See the transform method of {@link ol.geom.Geometry} and its subclasses for
    + * geometry transforms.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.proj.ProjectionLike} source Source projection-like.
    + * @param {ol.proj.ProjectionLike} destination Destination projection-like.
    + * @return {ol.Coordinate} Coordinate.
    + * @api stable
    + */
    +ol.proj.transform = function(coordinate, source, destination) {
    +  var transformFn = ol.proj.getTransform(source, destination);
    +  return transformFn(coordinate, undefined, coordinate.length);
    +};
    +
    +
    +/**
    + * Transforms an extent from source projection to destination projection.  This
    + * returns a new extent (and does not modify the original).
    + *
    + * @param {ol.Extent} extent The extent to transform.
    + * @param {ol.proj.ProjectionLike} source Source projection-like.
    + * @param {ol.proj.ProjectionLike} destination Destination projection-like.
    + * @return {ol.Extent} The transformed extent.
    + * @api stable
    + */
    +ol.proj.transformExtent = function(extent, source, destination) {
    +  var transformFn = ol.proj.getTransform(source, destination);
    +  return ol.extent.applyTransform(extent, transformFn);
    +};
    +
    +
    +/**
    + * Transforms the given point to the destination projection.
    + *
    + * @param {ol.Coordinate} point Point.
    + * @param {ol.proj.Projection} sourceProjection Source projection.
    + * @param {ol.proj.Projection} destinationProjection Destination projection.
    + * @return {ol.Coordinate} Point.
    + */
    +ol.proj.transformWithProjections =
    +    function(point, sourceProjection, destinationProjection) {
    +  var transformFn = ol.proj.getTransformFromProjections(
    +      sourceProjection, destinationProjection);
    +  return transformFn(point);
    +};
    diff --git a/mapbox-gl/ol/ol/proj/proj.jsdoc b/mapbox-gl/ol/ol/proj/proj.jsdoc
    new file mode 100644
    index 0000000000..d188ef7cb8
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/proj/proj.jsdoc
    @@ -0,0 +1,54 @@
    +/**
    + * The ol.proj namespace stores:
    + * * a list of {@link ol.proj.Projection}
    + * objects, one for each projection supported by the application
    + * * a list of transform functions needed to convert coordinates in one projection
    + * into another.
    + *
    + * The static functions are the methods used to maintain these.
    + * Each transform function can handle not only simple coordinate pairs, but also
    + * large arrays of coordinates such as vector geometries.
    + *
    + * When loaded, the library adds projection objects for EPSG:4326 (WGS84
    + * geographic coordinates) and EPSG:3857 (Web or Spherical Mercator, as used
    + * for example by Bing Maps or OpenStreetMap), together with the relevant
    + * transform functions.
    + *
    + * Additional transforms may be added by using the {@link http://proj4js.org/}
    + * library (version 2.2 or later). You can use the full build supplied by
    + * Proj4js, or create a custom build to support those projections you need; see
    + * the Proj4js website for how to do this. You also need the Proj4js definitions
    + * for the required projections. These definitions can be obtained from
    + * {@link http://epsg.io/}, and are a JS function, so can be loaded in a script
    + * tag (as in the examples) or pasted into your application.
    + * The first time there is a request for a projection, either with a
    + * {@link ol.proj.projectionLike} or directly with {@link ol.proj.get}, the
    + * code will check if the Proj4js library and the necessary definition are
    + * loaded; if so, it will register the appropriate {@link ol.proj.Projection}
    + * object and add transform functions between the new projection and all the
    + * existing ones. See examples/wms-image-custom-proj for an example of this.
    + * Because the check for presence of the Proj4js library and the definition only
    + * takes place on the first request for them, this means they can be loaded
    + * dynamically as needed; for example, with user-supplied data where you don't
    + * know in advance what projections are needed, you can initially load minimal
    + * support and then load whichever are requested.
    + *
    + * Note that Proj4js does not support projection extents. If you want to add
    + * one for creating default tile grids, you can add it after the Projection
    + * object has been created with `setExtent`, for example,
    + * `ol.proj.get('EPSG:1234').setExtent(extent)`.
    + *
    + * In addition to Proj4js support, any transform functions can be added with
    + * {@link ol.proj.addCoordinateTransforms}. To use this, you must first create
    + * a {@link ol.proj.Projection} object for the new projection and add it with
    + * {@link ol.proj.addProjection}. You can then add the forward and inverse
    + * functions with {@link ol.proj.addCoordinateTransforms}. See
    + * examples/wms-custom-proj for an example of this.
    + *
    + * Note that if no transforms are needed and you only need to define the
    + * projection, just add a {@link ol.proj.Projection} with
    + * {@link ol.proj.addProjection}. See examples/wms-no-proj for an example of
    + * this.
    + *
    + * @namespace ol.proj
    + */
    diff --git a/mapbox-gl/ol/ol/render.jsdoc b/mapbox-gl/ol/ol/render.jsdoc
    new file mode 100644
    index 0000000000..a167ba1d5f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.render
    + */
    diff --git a/mapbox-gl/ol/ol/render/box.js b/mapbox-gl/ol/ol/render/box.js
    new file mode 100644
    index 0000000000..8c58eef8b3
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/box.js
    @@ -0,0 +1,160 @@
    +// FIXME add rotation
    +
    +goog.provide('ol.render.Box');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.render.EventType');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {goog.Disposable}
    + * @param {ol.style.Style} style Style.
    + */
    +ol.render.Box = function(style) {
    +
    +  /**
    +   * @private
    +   * @type {ol.Map}
    +   */
    +  this.map_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.events.Key}
    +   */
    +  this.postComposeListenerKey_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Pixel}
    +   */
    +  this.startPixel_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Pixel}
    +   */
    +  this.endPixel_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.geom.Polygon}
    +   */
    +  this.geometry_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Style}
    +   */
    +  this.style_ = style;
    +
    +};
    +goog.inherits(ol.render.Box, goog.Disposable);
    +
    +
    +/**
    + * @private
    + * @return {ol.geom.Polygon} Geometry.
    + */
    +ol.render.Box.prototype.createGeometry_ = function() {
    +  goog.asserts.assert(!goog.isNull(this.startPixel_));
    +  goog.asserts.assert(!goog.isNull(this.endPixel_));
    +  goog.asserts.assert(!goog.isNull(this.map_));
    +  var startPixel = this.startPixel_;
    +  var endPixel = this.endPixel_;
    +  var pixels = [
    +    startPixel,
    +    [startPixel[0], endPixel[1]],
    +    endPixel,
    +    [endPixel[0], startPixel[1]]
    +  ];
    +  var coordinates = goog.array.map(pixels,
    +      this.map_.getCoordinateFromPixel, this.map_);
    +  // close the polygon
    +  coordinates[4] = coordinates[0].slice();
    +  return new ol.geom.Polygon([coordinates]);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.Box.prototype.disposeInternal = function() {
    +  this.setMap(null);
    +};
    +
    +
    +/**
    + * @param {ol.render.Event} event Event.
    + * @private
    + */
    +ol.render.Box.prototype.handleMapPostCompose_ = function(event) {
    +  var geometry = this.geometry_;
    +  goog.asserts.assert(goog.isDefAndNotNull(geometry));
    +  var style = this.style_;
    +  goog.asserts.assert(!goog.isNull(style));
    +  // use drawAsync(Infinity) to draw above everything
    +  event.vectorContext.drawAsync(Infinity, function(render) {
    +    render.setFillStrokeStyle(style.getFill(), style.getStroke());
    +    render.setTextStyle(style.getText());
    +    render.drawPolygonGeometry(geometry, null);
    +  });
    +};
    +
    +
    +/**
    + * @return {ol.geom.Polygon} Geometry.
    + */
    +ol.render.Box.prototype.getGeometry = function() {
    +  return this.geometry_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.render.Box.prototype.requestMapRenderFrame_ = function() {
    +  if (!goog.isNull(this.map_) &&
    +      !goog.isNull(this.startPixel_) &&
    +      !goog.isNull(this.endPixel_)) {
    +    this.map_.render();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + */
    +ol.render.Box.prototype.setMap = function(map) {
    +  if (!goog.isNull(this.postComposeListenerKey_)) {
    +    goog.events.unlistenByKey(this.postComposeListenerKey_);
    +    this.postComposeListenerKey_ = null;
    +    this.map_.render();
    +    this.map_ = null;
    +  }
    +  this.map_ = map;
    +  if (!goog.isNull(this.map_)) {
    +    this.postComposeListenerKey_ = goog.events.listen(
    +        map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false,
    +        this);
    +    this.requestMapRenderFrame_();
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Pixel} startPixel Start pixel.
    + * @param {ol.Pixel} endPixel End pixel.
    + */
    +ol.render.Box.prototype.setPixels = function(startPixel, endPixel) {
    +  this.startPixel_ = startPixel;
    +  this.endPixel_ = endPixel;
    +  this.geometry_ = this.createGeometry_();
    +  this.requestMapRenderFrame_();
    +};
    diff --git a/mapbox-gl/ol/ol/render/canvas/canvas.js b/mapbox-gl/ol/ol/render/canvas/canvas.js
    new file mode 100644
    index 0000000000..21e546d579
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/canvas/canvas.js
    @@ -0,0 +1,96 @@
    +goog.provide('ol.render.canvas');
    +
    +
    +/**
    + * @typedef {{fillStyle: string}}
    + */
    +ol.render.canvas.FillState;
    +
    +
    +/**
    + * @typedef {{lineCap: string,
    + *            lineDash: Array.<number>,
    + *            lineJoin: string,
    + *            lineWidth: number,
    + *            miterLimit: number,
    + *            strokeStyle: string}}
    + */
    +ol.render.canvas.StrokeState;
    +
    +
    +/**
    + * @typedef {{font: string,
    + *            textAlign: string,
    + *            textBaseline: string}}
    + */
    +ol.render.canvas.TextState;
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.canvas.defaultFont = '10px sans-serif';
    +
    +
    +/**
    + * @const
    + * @type {ol.Color}
    + */
    +ol.render.canvas.defaultFillStyle = [0, 0, 0, 1];
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.canvas.defaultLineCap = 'round';
    +
    +
    +/**
    + * @const
    + * @type {Array.<number>}
    + */
    +ol.render.canvas.defaultLineDash = [];
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.canvas.defaultLineJoin = 'round';
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.render.canvas.defaultMiterLimit = 10;
    +
    +
    +/**
    + * @const
    + * @type {ol.Color}
    + */
    +ol.render.canvas.defaultStrokeStyle = [0, 0, 0, 1];
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.canvas.defaultTextAlign = 'center';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.canvas.defaultTextBaseline = 'middle';
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.render.canvas.defaultLineWidth = 1;
    diff --git a/mapbox-gl/ol/ol/render/canvas/canvas.jsdoc b/mapbox-gl/ol/ol/render/canvas/canvas.jsdoc
    new file mode 100644
    index 0000000000..cea6b9f9f7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/canvas/canvas.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.render.canvas
    + */
    diff --git a/mapbox-gl/ol/ol/render/canvas/canvasimmediate.js b/mapbox-gl/ol/ol/render/canvas/canvasimmediate.js
    new file mode 100644
    index 0000000000..efaaac5893
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/canvas/canvasimmediate.js
    @@ -0,0 +1,1016 @@
    +// FIXME test, especially polygons with holes and multipolygons
    +// FIXME need to handle large thick features (where pixel size matters)
    +// FIXME add offset and end to ol.geom.flat.transform.transform2D?
    +
    +goog.provide('ol.render.canvas.Immediate');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.color');
    +goog.require('ol.extent');
    +goog.require('ol.geom.flat.transform');
    +goog.require('ol.has');
    +goog.require('ol.render.IVectorContext');
    +goog.require('ol.render.canvas');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @classdesc
    + * A concrete subclass of {@link ol.render.IVectorContext} that implements
    + * direct rendering of features and geometries to an HTML5 Canvas context.
    + * Instances of this class are created internally by the library and
    + * provided to application code as vectorContext member of the
    + * {@link ol.render.Event} object associated with postcompose, precompose and
    + * render events emitted by layers and maps.
    + *
    + * @constructor
    + * @implements {ol.render.IVectorContext}
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.Extent} extent Extent.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @struct
    + */
    +ol.render.canvas.Immediate =
    +    function(context, pixelRatio, extent, transform, viewRotation) {
    +
    +  /**
    +   * @private
    +   * @type {Object.<string,
    +   *        Array.<function(ol.render.canvas.Immediate)>>}
    +   */
    +  this.callbacksByZIndex_ = {};
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = context;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.pixelRatio_ = pixelRatio;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.extent_ = extent;
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = transform;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.viewRotation_ = viewRotation;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.FillState}
    +   */
    +  this.contextFillState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.StrokeState}
    +   */
    +  this.contextStrokeState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.TextState}
    +   */
    +  this.contextTextState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.FillState}
    +   */
    +  this.fillState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.StrokeState}
    +   */
    +  this.strokeState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement|HTMLVideoElement|Image}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageAnchorX_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageAnchorY_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageHeight_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageOpacity_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageOriginX_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageOriginY_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.imageRotateWithView_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageRotation_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageScale_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.imageSnapToPixel_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.imageWidth_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.text_ = '';
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textOffsetX_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textOffsetY_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textRotation_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textScale_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.FillState}
    +   */
    +  this.textFillState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.StrokeState}
    +   */
    +  this.textStrokeState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.TextState}
    +   */
    +  this.textState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.pixelCoordinates_ = [];
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.tmpLocalTransform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @private
    + */
    +ol.render.canvas.Immediate.prototype.drawImages_ =
    +    function(flatCoordinates, offset, end, stride) {
    +  if (goog.isNull(this.image_)) {
    +    return;
    +  }
    +  goog.asserts.assert(offset === 0);
    +  goog.asserts.assert(end == flatCoordinates.length);
    +  var pixelCoordinates = ol.geom.flat.transform.transform2D(
    +      flatCoordinates, offset, end, 2, this.transform_,
    +      this.pixelCoordinates_);
    +  var context = this.context_;
    +  var localTransform = this.tmpLocalTransform_;
    +  var alpha = context.globalAlpha;
    +  if (this.imageOpacity_ != 1) {
    +    context.globalAlpha = alpha * this.imageOpacity_;
    +  }
    +  var rotation = this.imageRotation_;
    +  if (this.imageRotateWithView_) {
    +    rotation += this.viewRotation_;
    +  }
    +  var i, ii;
    +  for (i = 0, ii = pixelCoordinates.length; i < ii; i += 2) {
    +    var x = pixelCoordinates[i] - this.imageAnchorX_;
    +    var y = pixelCoordinates[i + 1] - this.imageAnchorY_;
    +    if (this.imageSnapToPixel_) {
    +      x = (x + 0.5) | 0;
    +      y = (y + 0.5) | 0;
    +    }
    +    if (rotation !== 0 || this.imageScale_ != 1) {
    +      var centerX = x + this.imageAnchorX_;
    +      var centerY = y + this.imageAnchorY_;
    +      ol.vec.Mat4.makeTransform2D(localTransform,
    +          centerX, centerY, this.imageScale_, this.imageScale_,
    +          rotation, -centerX, -centerY);
    +      context.setTransform(
    +          goog.vec.Mat4.getElement(localTransform, 0, 0),
    +          goog.vec.Mat4.getElement(localTransform, 1, 0),
    +          goog.vec.Mat4.getElement(localTransform, 0, 1),
    +          goog.vec.Mat4.getElement(localTransform, 1, 1),
    +          goog.vec.Mat4.getElement(localTransform, 0, 3),
    +          goog.vec.Mat4.getElement(localTransform, 1, 3));
    +    }
    +    context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_,
    +        this.imageWidth_, this.imageHeight_, x, y,
    +        this.imageWidth_, this.imageHeight_);
    +  }
    +  if (rotation !== 0 || this.imageScale_ != 1) {
    +    context.setTransform(1, 0, 0, 1, 0, 0);
    +  }
    +  if (this.imageOpacity_ != 1) {
    +    context.globalAlpha = alpha;
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @private
    + */
    +ol.render.canvas.Immediate.prototype.drawText_ =
    +    function(flatCoordinates, offset, end, stride) {
    +  if (goog.isNull(this.textState_) || this.text_ === '') {
    +    return;
    +  }
    +  if (!goog.isNull(this.textFillState_)) {
    +    this.setContextFillState_(this.textFillState_);
    +  }
    +  if (!goog.isNull(this.textStrokeState_)) {
    +    this.setContextStrokeState_(this.textStrokeState_);
    +  }
    +  this.setContextTextState_(this.textState_);
    +  goog.asserts.assert(offset === 0);
    +  goog.asserts.assert(end == flatCoordinates.length);
    +  var pixelCoordinates = ol.geom.flat.transform.transform2D(
    +      flatCoordinates, offset, end, stride, this.transform_,
    +      this.pixelCoordinates_);
    +  var context = this.context_;
    +  for (; offset < end; offset += stride) {
    +    var x = pixelCoordinates[offset] + this.textOffsetX_;
    +    var y = pixelCoordinates[offset + 1] + this.textOffsetY_;
    +    if (this.textRotation_ !== 0 || this.textScale_ != 1) {
    +      var localTransform = ol.vec.Mat4.makeTransform2D(this.tmpLocalTransform_,
    +          x, y, this.textScale_, this.textScale_, this.textRotation_, -x, -y);
    +      context.setTransform(
    +          goog.vec.Mat4.getElement(localTransform, 0, 0),
    +          goog.vec.Mat4.getElement(localTransform, 1, 0),
    +          goog.vec.Mat4.getElement(localTransform, 0, 1),
    +          goog.vec.Mat4.getElement(localTransform, 1, 1),
    +          goog.vec.Mat4.getElement(localTransform, 0, 3),
    +          goog.vec.Mat4.getElement(localTransform, 1, 3));
    +    }
    +    if (!goog.isNull(this.textStrokeState_)) {
    +      context.strokeText(this.text_, x, y);
    +    }
    +    if (!goog.isNull(this.textFillState_)) {
    +      context.fillText(this.text_, x, y);
    +    }
    +  }
    +  if (this.textRotation_ !== 0 || this.textScale_ != 1) {
    +    context.setTransform(1, 0, 0, 1, 0, 0);
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {boolean} close Close.
    + * @private
    + * @return {number} end End.
    + */
    +ol.render.canvas.Immediate.prototype.moveToLineTo_ =
    +    function(flatCoordinates, offset, end, stride, close) {
    +  var context = this.context_;
    +  var pixelCoordinates = ol.geom.flat.transform.transform2D(
    +      flatCoordinates, offset, end, stride, this.transform_,
    +      this.pixelCoordinates_);
    +  context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
    +  var i;
    +  for (i = 2; i < pixelCoordinates.length; i += 2) {
    +    context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]);
    +  }
    +  if (close) {
    +    context.lineTo(pixelCoordinates[0], pixelCoordinates[1]);
    +  }
    +  return end;
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @private
    + * @return {number} End.
    + */
    +ol.render.canvas.Immediate.prototype.drawRings_ =
    +    function(flatCoordinates, offset, ends, stride) {
    +  var context = this.context_;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    offset = this.moveToLineTo_(
    +        flatCoordinates, offset, ends[i], stride, true);
    +    context.closePath(); // FIXME is this needed here?
    +  }
    +  return offset;
    +};
    +
    +
    +/**
    + * Register a function to be called for rendering at a given zIndex.  The
    + * function will be called asynchronously.  The callback will receive a
    + * reference to {@link ol.render.canvas.Immediate} context for drawing.
    + *
    + * @param {number} zIndex Z index.
    + * @param {function(ol.render.canvas.Immediate)} callback Callback.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawAsync = function(zIndex, callback) {
    +  var zIndexKey = zIndex.toString();
    +  var callbacks = this.callbacksByZIndex_[zIndexKey];
    +  if (goog.isDef(callbacks)) {
    +    callbacks.push(callback);
    +  } else {
    +    this.callbacksByZIndex_[zIndexKey] = [callback];
    +  }
    +};
    +
    +
    +/**
    + * Render a circle geometry into the canvas.  Rendering is immediate and uses
    + * the current fill and stroke styles.
    + *
    + * @param {ol.geom.Circle} circleGeometry Circle geometry.
    + * @param {ol.Feature} feature Feature,
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawCircleGeometry =
    +    function(circleGeometry, feature) {
    +  if (!ol.extent.intersects(this.extent_, circleGeometry.getExtent())) {
    +    return;
    +  }
    +  if (!goog.isNull(this.fillState_) || !goog.isNull(this.strokeState_)) {
    +    if (!goog.isNull(this.fillState_)) {
    +      this.setContextFillState_(this.fillState_);
    +    }
    +    if (!goog.isNull(this.strokeState_)) {
    +      this.setContextStrokeState_(this.strokeState_);
    +    }
    +    var pixelCoordinates = ol.geom.transformSimpleGeometry2D(
    +        circleGeometry, this.transform_, this.pixelCoordinates_);
    +    var dx = pixelCoordinates[2] - pixelCoordinates[0];
    +    var dy = pixelCoordinates[3] - pixelCoordinates[1];
    +    var radius = Math.sqrt(dx * dx + dy * dy);
    +    var context = this.context_;
    +    context.beginPath();
    +    context.arc(
    +        pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI);
    +    if (!goog.isNull(this.fillState_)) {
    +      context.fill();
    +    }
    +    if (!goog.isNull(this.strokeState_)) {
    +      context.stroke();
    +    }
    +  }
    +  if (this.text_ !== '') {
    +    this.drawText_(circleGeometry.getCenter(), 0, 2, 2);
    +  }
    +};
    +
    +
    +/**
    + * Render a feature into the canvas.  In order to respect the zIndex of the
    + * style this method draws asynchronously and thus *after* calls to
    + * drawXxxxGeometry have been finished, effectively drawing the feature
    + * *on top* of everything else.  You probably should be using
    + * {@link ol.FeatureOverlay} instead of calling this method directly.
    + *
    + * @param {ol.Feature} feature Feature.
    + * @param {ol.style.Style} style Style.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawFeature = function(feature, style) {
    +  var geometry = style.getGeometryFunction()(feature);
    +  if (!goog.isDefAndNotNull(geometry) ||
    +      !ol.extent.intersects(this.extent_, geometry.getExtent())) {
    +    return;
    +  }
    +  var zIndex = style.getZIndex();
    +  if (!goog.isDef(zIndex)) {
    +    zIndex = 0;
    +  }
    +  this.drawAsync(zIndex, function(render) {
    +    render.setFillStrokeStyle(style.getFill(), style.getStroke());
    +    render.setImageStyle(style.getImage());
    +    render.setTextStyle(style.getText());
    +    var renderGeometry =
    +        ol.render.canvas.Immediate.GEOMETRY_RENDERERS_[geometry.getType()];
    +    goog.asserts.assert(goog.isDef(renderGeometry));
    +    renderGeometry.call(render, geometry, null);
    +  });
    +};
    +
    +
    +/**
    + * Render a GeometryCollection to the canvas.  Rendering is immediate and
    + * uses the current styles appropriate for each geometry in the collection.
    + *
    + * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry
    + *     collection.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.canvas.Immediate.prototype.drawGeometryCollectionGeometry =
    +    function(geometryCollectionGeometry, feature) {
    +  var geometries = geometryCollectionGeometry.getGeometriesArray();
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    var geometry = geometries[i];
    +    var geometryRenderer =
    +        ol.render.canvas.Immediate.GEOMETRY_RENDERERS_[geometry.getType()];
    +    goog.asserts.assert(goog.isDef(geometryRenderer));
    +    geometryRenderer.call(this, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * Render a Point geometry into the canvas.  Rendering is immediate and uses
    + * the current style.
    + *
    + * @param {ol.geom.Point} pointGeometry Point geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawPointGeometry =
    +    function(pointGeometry, feature) {
    +  var flatCoordinates = pointGeometry.getFlatCoordinates();
    +  var stride = pointGeometry.getStride();
    +  if (!goog.isNull(this.image_)) {
    +    this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
    +  }
    +  if (this.text_ !== '') {
    +    this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
    +  }
    +};
    +
    +
    +/**
    + * Render a MultiPoint geometry  into the canvas.  Rendering is immediate and
    + * uses the current style.
    + *
    + * @param {ol.geom.MultiPoint} multiPointGeometry MultiPoint geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawMultiPointGeometry =
    +    function(multiPointGeometry, feature) {
    +  var flatCoordinates = multiPointGeometry.getFlatCoordinates();
    +  var stride = multiPointGeometry.getStride();
    +  if (!goog.isNull(this.image_)) {
    +    this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
    +  }
    +  if (this.text_ !== '') {
    +    this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
    +  }
    +};
    +
    +
    +/**
    + * Render a LineString into the canvas.  Rendering is immediate and uses
    + * the current style.
    + *
    + * @param {ol.geom.LineString} lineStringGeometry Line string geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawLineStringGeometry =
    +    function(lineStringGeometry, feature) {
    +  if (!ol.extent.intersects(this.extent_, lineStringGeometry.getExtent())) {
    +    return;
    +  }
    +  if (!goog.isNull(this.strokeState_)) {
    +    this.setContextStrokeState_(this.strokeState_);
    +    var context = this.context_;
    +    var flatCoordinates = lineStringGeometry.getFlatCoordinates();
    +    context.beginPath();
    +    this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length,
    +        lineStringGeometry.getStride(), false);
    +    context.stroke();
    +  }
    +  if (this.text_ !== '') {
    +    var flatMidpoint = lineStringGeometry.getFlatMidpoint();
    +    this.drawText_(flatMidpoint, 0, 2, 2);
    +  }
    +};
    +
    +
    +/**
    + * Render a MultiLineString geometry into the canvas.  Rendering is immediate
    + * and uses the current style.
    + *
    + * @param {ol.geom.MultiLineString} multiLineStringGeometry
    + *     MultiLineString geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawMultiLineStringGeometry =
    +    function(multiLineStringGeometry, feature) {
    +  var geometryExtent = multiLineStringGeometry.getExtent();
    +  if (!ol.extent.intersects(this.extent_, geometryExtent)) {
    +    return;
    +  }
    +  if (!goog.isNull(this.strokeState_)) {
    +    this.setContextStrokeState_(this.strokeState_);
    +    var context = this.context_;
    +    var flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
    +    var offset = 0;
    +    var ends = multiLineStringGeometry.getEnds();
    +    var stride = multiLineStringGeometry.getStride();
    +    context.beginPath();
    +    var i, ii;
    +    for (i = 0, ii = ends.length; i < ii; ++i) {
    +      offset = this.moveToLineTo_(
    +          flatCoordinates, offset, ends[i], stride, false);
    +    }
    +    context.stroke();
    +  }
    +  if (this.text_ !== '') {
    +    var flatMidpoints = multiLineStringGeometry.getFlatMidpoints();
    +    this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2);
    +  }
    +};
    +
    +
    +/**
    + * Render a Polygon geometry into the canvas.  Rendering is immediate and uses
    + * the current style.
    + *
    + * @param {ol.geom.Polygon} polygonGeometry Polygon geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawPolygonGeometry =
    +    function(polygonGeometry, feature) {
    +  if (!ol.extent.intersects(this.extent_, polygonGeometry.getExtent())) {
    +    return;
    +  }
    +  if (!goog.isNull(this.strokeState_) || !goog.isNull(this.fillState_)) {
    +    if (!goog.isNull(this.fillState_)) {
    +      this.setContextFillState_(this.fillState_);
    +    }
    +    if (!goog.isNull(this.strokeState_)) {
    +      this.setContextStrokeState_(this.strokeState_);
    +    }
    +    var context = this.context_;
    +    context.beginPath();
    +    this.drawRings_(polygonGeometry.getOrientedFlatCoordinates(),
    +        0, polygonGeometry.getEnds(), polygonGeometry.getStride());
    +    if (!goog.isNull(this.fillState_)) {
    +      context.fill();
    +    }
    +    if (!goog.isNull(this.strokeState_)) {
    +      context.stroke();
    +    }
    +  }
    +  if (this.text_ !== '') {
    +    var flatInteriorPoint = polygonGeometry.getFlatInteriorPoint();
    +    this.drawText_(flatInteriorPoint, 0, 2, 2);
    +  }
    +};
    +
    +
    +/**
    + * Render MultiPolygon geometry into the canvas.  Rendering is immediate and
    + * uses the current style.
    + * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry.
    + * @param {ol.Feature} feature Feature.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.drawMultiPolygonGeometry =
    +    function(multiPolygonGeometry, feature) {
    +  if (!ol.extent.intersects(this.extent_, multiPolygonGeometry.getExtent())) {
    +    return;
    +  }
    +  if (!goog.isNull(this.strokeState_) || !goog.isNull(this.fillState_)) {
    +    if (!goog.isNull(this.fillState_)) {
    +      this.setContextFillState_(this.fillState_);
    +    }
    +    if (!goog.isNull(this.strokeState_)) {
    +      this.setContextStrokeState_(this.strokeState_);
    +    }
    +    var context = this.context_;
    +    var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
    +    var offset = 0;
    +    var endss = multiPolygonGeometry.getEndss();
    +    var stride = multiPolygonGeometry.getStride();
    +    var i, ii;
    +    for (i = 0, ii = endss.length; i < ii; ++i) {
    +      var ends = endss[i];
    +      context.beginPath();
    +      offset = this.drawRings_(flatCoordinates, offset, ends, stride);
    +      if (!goog.isNull(this.fillState_)) {
    +        context.fill();
    +      }
    +      if (!goog.isNull(this.strokeState_)) {
    +        context.stroke();
    +      }
    +    }
    +  }
    +  if (this.text_ !== '') {
    +    var flatInteriorPoints = multiPolygonGeometry.getFlatInteriorPoints();
    +    this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2);
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Immediate.prototype.drawText = goog.abstractMethod;
    +
    +
    +/**
    + * FIXME: empty description for jsdoc
    + */
    +ol.render.canvas.Immediate.prototype.flush = function() {
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(this.callbacksByZIndex_), Number);
    +  goog.array.sort(zs);
    +  var i, ii, callbacks, j, jj;
    +  for (i = 0, ii = zs.length; i < ii; ++i) {
    +    callbacks = this.callbacksByZIndex_[zs[i].toString()];
    +    for (j = 0, jj = callbacks.length; j < jj; ++j) {
    +      callbacks[j](this);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.FillState} fillState Fill state.
    + * @private
    + */
    +ol.render.canvas.Immediate.prototype.setContextFillState_ =
    +    function(fillState) {
    +  var context = this.context_;
    +  var contextFillState = this.contextFillState_;
    +  if (goog.isNull(contextFillState)) {
    +    context.fillStyle = fillState.fillStyle;
    +    this.contextFillState_ = {
    +      fillStyle: fillState.fillStyle
    +    };
    +  } else {
    +    if (contextFillState.fillStyle != fillState.fillStyle) {
    +      contextFillState.fillStyle = context.fillStyle = fillState.fillStyle;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.StrokeState} strokeState Stroke state.
    + * @private
    + */
    +ol.render.canvas.Immediate.prototype.setContextStrokeState_ =
    +    function(strokeState) {
    +  var context = this.context_;
    +  var contextStrokeState = this.contextStrokeState_;
    +  if (goog.isNull(contextStrokeState)) {
    +    context.lineCap = strokeState.lineCap;
    +    if (ol.has.CANVAS_LINE_DASH) {
    +      context.setLineDash(strokeState.lineDash);
    +    }
    +    context.lineJoin = strokeState.lineJoin;
    +    context.lineWidth = strokeState.lineWidth;
    +    context.miterLimit = strokeState.miterLimit;
    +    context.strokeStyle = strokeState.strokeStyle;
    +    this.contextStrokeState_ = {
    +      lineCap: strokeState.lineCap,
    +      lineDash: strokeState.lineDash,
    +      lineJoin: strokeState.lineJoin,
    +      lineWidth: strokeState.lineWidth,
    +      miterLimit: strokeState.miterLimit,
    +      strokeStyle: strokeState.strokeStyle
    +    };
    +  } else {
    +    if (contextStrokeState.lineCap != strokeState.lineCap) {
    +      contextStrokeState.lineCap = context.lineCap = strokeState.lineCap;
    +    }
    +    if (ol.has.CANVAS_LINE_DASH) {
    +      if (!goog.array.equals(
    +          contextStrokeState.lineDash, strokeState.lineDash)) {
    +        context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash);
    +      }
    +    }
    +    if (contextStrokeState.lineJoin != strokeState.lineJoin) {
    +      contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin;
    +    }
    +    if (contextStrokeState.lineWidth != strokeState.lineWidth) {
    +      contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth;
    +    }
    +    if (contextStrokeState.miterLimit != strokeState.miterLimit) {
    +      contextStrokeState.miterLimit = context.miterLimit =
    +          strokeState.miterLimit;
    +    }
    +    if (contextStrokeState.strokeStyle != strokeState.strokeStyle) {
    +      contextStrokeState.strokeStyle = context.strokeStyle =
    +          strokeState.strokeStyle;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.TextState} textState Text state.
    + * @private
    + */
    +ol.render.canvas.Immediate.prototype.setContextTextState_ =
    +    function(textState) {
    +  var context = this.context_;
    +  var contextTextState = this.contextTextState_;
    +  if (goog.isNull(contextTextState)) {
    +    context.font = textState.font;
    +    context.textAlign = textState.textAlign;
    +    context.textBaseline = textState.textBaseline;
    +    this.contextTextState_ = {
    +      font: textState.font,
    +      textAlign: textState.textAlign,
    +      textBaseline: textState.textBaseline
    +    };
    +  } else {
    +    if (contextTextState.font != textState.font) {
    +      contextTextState.font = context.font = textState.font;
    +    }
    +    if (contextTextState.textAlign != textState.textAlign) {
    +      contextTextState.textAlign = context.textAlign = textState.textAlign;
    +    }
    +    if (contextTextState.textBaseline != textState.textBaseline) {
    +      contextTextState.textBaseline = context.textBaseline =
    +          textState.textBaseline;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Set the fill and stroke style for subsequent draw operations.  To clear
    + * either fill or stroke styles, pass null for the appropriate parameter.
    + *
    + * @param {ol.style.Fill} fillStyle Fill style.
    + * @param {ol.style.Stroke} strokeStyle Stroke style.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.setFillStrokeStyle =
    +    function(fillStyle, strokeStyle) {
    +  if (goog.isNull(fillStyle)) {
    +    this.fillState_ = null;
    +  } else {
    +    var fillStyleColor = fillStyle.getColor();
    +    this.fillState_ = {
    +      fillStyle: ol.color.asString(!goog.isNull(fillStyleColor) ?
    +          fillStyleColor : ol.render.canvas.defaultFillStyle)
    +    };
    +  }
    +  if (goog.isNull(strokeStyle)) {
    +    this.strokeState_ = null;
    +  } else {
    +    var strokeStyleColor = strokeStyle.getColor();
    +    var strokeStyleLineCap = strokeStyle.getLineCap();
    +    var strokeStyleLineDash = strokeStyle.getLineDash();
    +    var strokeStyleLineJoin = strokeStyle.getLineJoin();
    +    var strokeStyleWidth = strokeStyle.getWidth();
    +    var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
    +    this.strokeState_ = {
    +      lineCap: goog.isDef(strokeStyleLineCap) ?
    +          strokeStyleLineCap : ol.render.canvas.defaultLineCap,
    +      lineDash: goog.isDefAndNotNull(strokeStyleLineDash) ?
    +          strokeStyleLineDash : ol.render.canvas.defaultLineDash,
    +      lineJoin: goog.isDef(strokeStyleLineJoin) ?
    +          strokeStyleLineJoin : ol.render.canvas.defaultLineJoin,
    +      lineWidth: this.pixelRatio_ * (goog.isDef(strokeStyleWidth) ?
    +          strokeStyleWidth : ol.render.canvas.defaultLineWidth),
    +      miterLimit: goog.isDef(strokeStyleMiterLimit) ?
    +          strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit,
    +      strokeStyle: ol.color.asString(!goog.isNull(strokeStyleColor) ?
    +          strokeStyleColor : ol.render.canvas.defaultStrokeStyle)
    +    };
    +  }
    +};
    +
    +
    +/**
    + * Set the image style for subsequent draw operations.  Pass null to remove
    + * the image style.
    + *
    + * @param {ol.style.Image} imageStyle Image style.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) {
    +  if (goog.isNull(imageStyle)) {
    +    this.image_ = null;
    +  } else {
    +    var imageAnchor = imageStyle.getAnchor();
    +    // FIXME pixel ratio
    +    var imageImage = imageStyle.getImage(1);
    +    var imageOrigin = imageStyle.getOrigin();
    +    var imageSize = imageStyle.getSize();
    +    goog.asserts.assert(!goog.isNull(imageAnchor));
    +    goog.asserts.assert(!goog.isNull(imageImage));
    +    goog.asserts.assert(!goog.isNull(imageOrigin));
    +    goog.asserts.assert(!goog.isNull(imageSize));
    +    this.imageAnchorX_ = imageAnchor[0];
    +    this.imageAnchorY_ = imageAnchor[1];
    +    this.imageHeight_ = imageSize[1];
    +    this.image_ = imageImage;
    +    this.imageOpacity_ = imageStyle.getOpacity();
    +    this.imageOriginX_ = imageOrigin[0];
    +    this.imageOriginY_ = imageOrigin[1];
    +    this.imageRotateWithView_ = imageStyle.getRotateWithView();
    +    this.imageRotation_ = imageStyle.getRotation();
    +    this.imageScale_ = imageStyle.getScale();
    +    this.imageSnapToPixel_ = imageStyle.getSnapToPixel();
    +    this.imageWidth_ = imageSize[0];
    +  }
    +};
    +
    +
    +/**
    + * Set the text style for subsequent draw operations.  Pass null to
    + * remove the text style.
    + *
    + * @param {ol.style.Text} textStyle Text style.
    + * @api
    + */
    +ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) {
    +  if (goog.isNull(textStyle)) {
    +    this.text_ = '';
    +  } else {
    +    var textFillStyle = textStyle.getFill();
    +    if (goog.isNull(textFillStyle)) {
    +      this.textFillState_ = null;
    +    } else {
    +      var textFillStyleColor = textFillStyle.getColor();
    +      this.textFillState_ = {
    +        fillStyle: ol.color.asString(!goog.isNull(textFillStyleColor) ?
    +            textFillStyleColor : ol.render.canvas.defaultFillStyle)
    +      };
    +    }
    +    var textStrokeStyle = textStyle.getStroke();
    +    if (goog.isNull(textStrokeStyle)) {
    +      this.textStrokeState_ = null;
    +    } else {
    +      var textStrokeStyleColor = textStrokeStyle.getColor();
    +      var textStrokeStyleLineCap = textStrokeStyle.getLineCap();
    +      var textStrokeStyleLineDash = textStrokeStyle.getLineDash();
    +      var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin();
    +      var textStrokeStyleWidth = textStrokeStyle.getWidth();
    +      var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
    +      this.textStrokeState_ = {
    +        lineCap: goog.isDef(textStrokeStyleLineCap) ?
    +            textStrokeStyleLineCap : ol.render.canvas.defaultLineCap,
    +        lineDash: goog.isDefAndNotNull(textStrokeStyleLineDash) ?
    +            textStrokeStyleLineDash : ol.render.canvas.defaultLineDash,
    +        lineJoin: goog.isDef(textStrokeStyleLineJoin) ?
    +            textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin,
    +        lineWidth: goog.isDef(textStrokeStyleWidth) ?
    +            textStrokeStyleWidth : ol.render.canvas.defaultLineWidth,
    +        miterLimit: goog.isDef(textStrokeStyleMiterLimit) ?
    +            textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit,
    +        strokeStyle: ol.color.asString(!goog.isNull(textStrokeStyleColor) ?
    +            textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle)
    +      };
    +    }
    +    var textFont = textStyle.getFont();
    +    var textOffsetX = textStyle.getOffsetX();
    +    var textOffsetY = textStyle.getOffsetY();
    +    var textRotation = textStyle.getRotation();
    +    var textScale = textStyle.getScale();
    +    var textText = textStyle.getText();
    +    var textTextAlign = textStyle.getTextAlign();
    +    var textTextBaseline = textStyle.getTextBaseline();
    +    this.textState_ = {
    +      font: goog.isDef(textFont) ?
    +          textFont : ol.render.canvas.defaultFont,
    +      textAlign: goog.isDef(textTextAlign) ?
    +          textTextAlign : ol.render.canvas.defaultTextAlign,
    +      textBaseline: goog.isDef(textTextBaseline) ?
    +          textTextBaseline : ol.render.canvas.defaultTextBaseline
    +    };
    +    this.text_ = goog.isDef(textText) ? textText : '';
    +    this.textOffsetX_ =
    +        goog.isDef(textOffsetX) ? (this.pixelRatio_ * textOffsetX) : 0;
    +    this.textOffsetY_ =
    +        goog.isDef(textOffsetY) ? (this.pixelRatio_ * textOffsetY) : 0;
    +    this.textRotation_ = goog.isDef(textRotation) ? textRotation : 0;
    +    this.textScale_ = this.pixelRatio_ * (goog.isDef(textScale) ?
    +        textScale : 1);
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<ol.geom.GeometryType,
    + *                function(this: ol.render.canvas.Immediate, ol.geom.Geometry,
    + *                         Object)>}
    + */
    +ol.render.canvas.Immediate.GEOMETRY_RENDERERS_ = {
    +  'Point': ol.render.canvas.Immediate.prototype.drawPointGeometry,
    +  'LineString': ol.render.canvas.Immediate.prototype.drawLineStringGeometry,
    +  'Polygon': ol.render.canvas.Immediate.prototype.drawPolygonGeometry,
    +  'MultiPoint': ol.render.canvas.Immediate.prototype.drawMultiPointGeometry,
    +  'MultiLineString':
    +      ol.render.canvas.Immediate.prototype.drawMultiLineStringGeometry,
    +  'MultiPolygon': ol.render.canvas.Immediate.prototype.drawMultiPolygonGeometry,
    +  'GeometryCollection':
    +      ol.render.canvas.Immediate.prototype.drawGeometryCollectionGeometry,
    +  'Circle': ol.render.canvas.Immediate.prototype.drawCircleGeometry
    +};
    diff --git a/mapbox-gl/ol/ol/render/canvas/canvasreplay.js b/mapbox-gl/ol/ol/render/canvas/canvasreplay.js
    new file mode 100644
    index 0000000000..d8ef6cd66d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/canvas/canvasreplay.js
    @@ -0,0 +1,2060 @@
    +// FIXME add option to apply snapToPixel to all coordinates?
    +// FIXME can eliminate empty set styles and strokes (when all geoms skipped)
    +
    +goog.provide('ol.render.canvas.Replay');
    +goog.provide('ol.render.canvas.ReplayGroup');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.array');
    +goog.require('ol.color');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.extent.Relationship');
    +goog.require('ol.geom.flat.simplify');
    +goog.require('ol.geom.flat.transform');
    +goog.require('ol.has');
    +goog.require('ol.render.IReplayGroup');
    +goog.require('ol.render.IVectorContext');
    +goog.require('ol.render.canvas');
    +goog.require('ol.vec.Mat4');
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.render.canvas.Instruction = {
    +  BEGIN_GEOMETRY: 0,
    +  BEGIN_PATH: 1,
    +  CIRCLE: 2,
    +  CLOSE_PATH: 3,
    +  DRAW_IMAGE: 4,
    +  DRAW_TEXT: 5,
    +  END_GEOMETRY: 6,
    +  FILL: 7,
    +  MOVE_TO_LINE_TO: 8,
    +  SET_FILL_STYLE: 9,
    +  SET_STROKE_STYLE: 10,
    +  SET_TEXT_STYLE: 11,
    +  STROKE: 12
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.render.IVectorContext}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Maximum extent.
    + * @param {number} resolution Resolution.
    + * @protected
    + * @struct
    + */
    +ol.render.canvas.Replay = function(tolerance, maxExtent, resolution) {
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.tolerance = tolerance;
    +
    +  /**
    +   * @protected
    +   * @const
    +   * @type {ol.Extent}
    +   */
    +  this.maxExtent = maxExtent;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.bufferedMaxExtent_ = null;
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.maxLineWidth = 0;
    +
    +  /**
    +   * @protected
    +   * @const
    +   * @type {number}
    +   */
    +  this.resolution = resolution;
    +
    +  /**
    +   * @private
    +   * @type {Array.<*>}
    +   */
    +  this.beginGeometryInstruction1_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<*>}
    +   */
    +  this.beginGeometryInstruction2_ = null;
    +
    +  /**
    +   * @protected
    +   * @type {Array.<*>}
    +   */
    +  this.instructions = [];
    +
    +  /**
    +   * @protected
    +   * @type {Array.<number>}
    +   */
    +  this.coordinates = [];
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.renderedTransform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @protected
    +   * @type {Array.<*>}
    +   */
    +  this.hitDetectionInstructions = [];
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.pixelCoordinates_ = [];
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.tmpLocalTransform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {boolean} close Close.
    + * @protected
    + * @return {number} My end.
    + */
    +ol.render.canvas.Replay.prototype.appendFlatCoordinates =
    +    function(flatCoordinates, offset, end, stride, close) {
    +
    +  var myEnd = this.coordinates.length;
    +  var extent = this.getBufferedMaxExtent();
    +  var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]];
    +  var nextCoord = [NaN, NaN];
    +  var skipped = true;
    +
    +  var i, lastRel, nextRel;
    +  for (i = offset + stride; i < end; i += stride) {
    +    nextCoord[0] = flatCoordinates[i];
    +    nextCoord[1] = flatCoordinates[i + 1];
    +    nextRel = ol.extent.coordinateRelationship(extent, nextCoord);
    +    if (nextRel !== lastRel) {
    +      if (skipped) {
    +        this.coordinates[myEnd++] = lastCoord[0];
    +        this.coordinates[myEnd++] = lastCoord[1];
    +      }
    +      this.coordinates[myEnd++] = nextCoord[0];
    +      this.coordinates[myEnd++] = nextCoord[1];
    +      skipped = false;
    +    } else if (nextRel === ol.extent.Relationship.INTERSECTING) {
    +      this.coordinates[myEnd++] = nextCoord[0];
    +      this.coordinates[myEnd++] = nextCoord[1];
    +      skipped = false;
    +    } else {
    +      skipped = true;
    +    }
    +    lastCoord[0] = nextCoord[0];
    +    lastCoord[1] = nextCoord[1];
    +    lastRel = nextRel;
    +  }
    +
    +  // handle case where there is only one point to append
    +  if (i === offset + stride) {
    +    this.coordinates[myEnd++] = lastCoord[0];
    +    this.coordinates[myEnd++] = lastCoord[1];
    +  }
    +
    +  if (close) {
    +    this.coordinates[myEnd++] = flatCoordinates[offset];
    +    this.coordinates[myEnd++] = flatCoordinates[offset + 1];
    +  }
    +  return myEnd;
    +};
    +
    +
    +/**
    + * @protected
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) {
    +  this.beginGeometryInstruction1_ =
    +      [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0];
    +  this.instructions.push(this.beginGeometryInstruction1_);
    +  this.beginGeometryInstruction2_ =
    +      [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0];
    +  this.hitDetectionInstructions.push(this.beginGeometryInstruction2_);
    +};
    +
    +
    +/**
    + * @private
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {Array.<*>} instructions Instructions array.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
    + *     extent.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.canvas.Replay.prototype.replay_ = function(
    +    context, pixelRatio, transform, viewRotation, skippedFeaturesHash,
    +    instructions, featureCallback, opt_hitExtent) {
    +  /** @type {Array.<number>} */
    +  var pixelCoordinates;
    +  if (ol.vec.Mat4.equals2D(transform, this.renderedTransform_)) {
    +    pixelCoordinates = this.pixelCoordinates_;
    +  } else {
    +    pixelCoordinates = ol.geom.flat.transform.transform2D(
    +        this.coordinates, 0, this.coordinates.length, 2,
    +        transform, this.pixelCoordinates_);
    +    goog.vec.Mat4.setFromArray(this.renderedTransform_, transform);
    +    goog.asserts.assert(pixelCoordinates === this.pixelCoordinates_);
    +  }
    +  var i = 0; // instruction index
    +  var ii = instructions.length; // end of instructions
    +  var d = 0; // data index
    +  var dd; // end of per-instruction data
    +  var localTransform = this.tmpLocalTransform_;
    +  while (i < ii) {
    +    var instruction = instructions[i];
    +    var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]);
    +    var feature, fill, stroke, text, x, y;
    +    switch (type) {
    +      case ol.render.canvas.Instruction.BEGIN_GEOMETRY:
    +        feature = /** @type {ol.Feature} */ (instruction[1]);
    +        var featureUid = goog.getUid(feature).toString();
    +        if (goog.isDef(skippedFeaturesHash[featureUid])) {
    +          i = /** @type {number} */ (instruction[2]);
    +        } else if (goog.isDef(opt_hitExtent) && !ol.extent.intersects(
    +            opt_hitExtent, feature.getGeometry().getExtent())) {
    +          i = /** @type {number} */ (instruction[2]);
    +        } else {
    +          ++i;
    +        }
    +        break;
    +      case ol.render.canvas.Instruction.BEGIN_PATH:
    +        context.beginPath();
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.CIRCLE:
    +        goog.asserts.assert(goog.isNumber(instruction[1]));
    +        d = /** @type {number} */ (instruction[1]);
    +        var x1 = pixelCoordinates[d];
    +        var y1 = pixelCoordinates[d + 1];
    +        var x2 = pixelCoordinates[d + 2];
    +        var y2 = pixelCoordinates[d + 3];
    +        var dx = x2 - x1;
    +        var dy = y2 - y1;
    +        var r = Math.sqrt(dx * dx + dy * dy);
    +        context.arc(x1, y1, r, 0, 2 * Math.PI, true);
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.CLOSE_PATH:
    +        context.closePath();
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.DRAW_IMAGE:
    +        goog.asserts.assert(goog.isNumber(instruction[1]));
    +        d = /** @type {number} */ (instruction[1]);
    +        goog.asserts.assert(goog.isNumber(instruction[2]));
    +        dd = /** @type {number} */ (instruction[2]);
    +        var image =  /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */
    +            (instruction[3]);
    +        // Remaining arguments in DRAW_IMAGE are in alphabetical order
    +        var anchorX = /** @type {number} */ (instruction[4]) * pixelRatio;
    +        var anchorY = /** @type {number} */ (instruction[5]) * pixelRatio;
    +        var height = /** @type {number} */ (instruction[6]);
    +        var opacity = /** @type {number} */ (instruction[7]);
    +        var originX = /** @type {number} */ (instruction[8]);
    +        var originY = /** @type {number} */ (instruction[9]);
    +        var rotateWithView = /** @type {boolean} */ (instruction[10]);
    +        var rotation = /** @type {number} */ (instruction[11]);
    +        var scale = /** @type {number} */ (instruction[12]);
    +        var snapToPixel = /** @type {boolean} */ (instruction[13]);
    +        var width = /** @type {number} */ (instruction[14]);
    +        if (rotateWithView) {
    +          rotation += viewRotation;
    +        }
    +        for (; d < dd; d += 2) {
    +          x = pixelCoordinates[d] - anchorX;
    +          y = pixelCoordinates[d + 1] - anchorY;
    +          if (snapToPixel) {
    +            x = (x + 0.5) | 0;
    +            y = (y + 0.5) | 0;
    +          }
    +          if (scale != 1 || rotation !== 0) {
    +            var centerX = x + anchorX;
    +            var centerY = y + anchorY;
    +            ol.vec.Mat4.makeTransform2D(
    +                localTransform, centerX, centerY, scale, scale,
    +                rotation, -centerX, -centerY);
    +            context.setTransform(
    +                goog.vec.Mat4.getElement(localTransform, 0, 0),
    +                goog.vec.Mat4.getElement(localTransform, 1, 0),
    +                goog.vec.Mat4.getElement(localTransform, 0, 1),
    +                goog.vec.Mat4.getElement(localTransform, 1, 1),
    +                goog.vec.Mat4.getElement(localTransform, 0, 3),
    +                goog.vec.Mat4.getElement(localTransform, 1, 3));
    +          }
    +          var alpha = context.globalAlpha;
    +          if (opacity != 1) {
    +            context.globalAlpha = alpha * opacity;
    +          }
    +
    +          context.drawImage(image, originX, originY, width, height,
    +              x, y, width * pixelRatio, height * pixelRatio);
    +
    +          if (opacity != 1) {
    +            context.globalAlpha = alpha;
    +          }
    +          if (scale != 1 || rotation !== 0) {
    +            context.setTransform(1, 0, 0, 1, 0, 0);
    +          }
    +        }
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.DRAW_TEXT:
    +        goog.asserts.assert(goog.isNumber(instruction[1]));
    +        d = /** @type {number} */ (instruction[1]);
    +        goog.asserts.assert(goog.isNumber(instruction[2]));
    +        dd = /** @type {number} */ (instruction[2]);
    +        goog.asserts.assert(goog.isString(instruction[3]));
    +        text = /** @type {string} */ (instruction[3]);
    +        goog.asserts.assert(goog.isNumber(instruction[4]));
    +        var offsetX = /** @type {number} */ (instruction[4]) * pixelRatio;
    +        goog.asserts.assert(goog.isNumber(instruction[5]));
    +        var offsetY = /** @type {number} */ (instruction[5]) * pixelRatio;
    +        goog.asserts.assert(goog.isNumber(instruction[6]));
    +        rotation = /** @type {number} */ (instruction[6]);
    +        goog.asserts.assert(goog.isNumber(instruction[7]));
    +        scale = /** @type {number} */ (instruction[7]) * pixelRatio;
    +        goog.asserts.assert(goog.isBoolean(instruction[8]));
    +        fill = /** @type {boolean} */ (instruction[8]);
    +        goog.asserts.assert(goog.isBoolean(instruction[9]));
    +        stroke = /** @type {boolean} */ (instruction[9]);
    +        for (; d < dd; d += 2) {
    +          x = pixelCoordinates[d] + offsetX;
    +          y = pixelCoordinates[d + 1] + offsetY;
    +          if (scale != 1 || rotation !== 0) {
    +            ol.vec.Mat4.makeTransform2D(
    +                localTransform, x, y, scale, scale, rotation, -x, -y);
    +            context.setTransform(
    +                goog.vec.Mat4.getElement(localTransform, 0, 0),
    +                goog.vec.Mat4.getElement(localTransform, 1, 0),
    +                goog.vec.Mat4.getElement(localTransform, 0, 1),
    +                goog.vec.Mat4.getElement(localTransform, 1, 1),
    +                goog.vec.Mat4.getElement(localTransform, 0, 3),
    +                goog.vec.Mat4.getElement(localTransform, 1, 3));
    +          }
    +          if (stroke) {
    +            context.strokeText(text, x, y);
    +          }
    +          if (fill) {
    +            context.fillText(text, x, y);
    +          }
    +          if (scale != 1 || rotation !== 0) {
    +            context.setTransform(1, 0, 0, 1, 0, 0);
    +          }
    +        }
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.END_GEOMETRY:
    +        if (goog.isDef(featureCallback)) {
    +          feature = /** @type {ol.Feature} */ (instruction[1]);
    +          var result = featureCallback(feature);
    +          if (result) {
    +            return result;
    +          }
    +        }
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.FILL:
    +        context.fill();
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.MOVE_TO_LINE_TO:
    +        goog.asserts.assert(goog.isNumber(instruction[1]));
    +        d = /** @type {number} */ (instruction[1]);
    +        goog.asserts.assert(goog.isNumber(instruction[2]));
    +        dd = /** @type {number} */ (instruction[2]);
    +        context.moveTo(pixelCoordinates[d], pixelCoordinates[d + 1]);
    +        for (d += 2; d < dd; d += 2) {
    +          context.lineTo(pixelCoordinates[d], pixelCoordinates[d + 1]);
    +        }
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.SET_FILL_STYLE:
    +        goog.asserts.assert(goog.isString(instruction[1]));
    +        context.fillStyle = /** @type {string} */ (instruction[1]);
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.SET_STROKE_STYLE:
    +        goog.asserts.assert(goog.isString(instruction[1]));
    +        goog.asserts.assert(goog.isNumber(instruction[2]));
    +        goog.asserts.assert(goog.isString(instruction[3]));
    +        goog.asserts.assert(goog.isString(instruction[4]));
    +        goog.asserts.assert(goog.isNumber(instruction[5]));
    +        goog.asserts.assert(!goog.isNull(instruction[6]));
    +        var usePixelRatio = goog.isDef(instruction[7]) ? instruction[7] : true;
    +        var lineWidth = /** @type {number} */ (instruction[2]);
    +        context.strokeStyle = /** @type {string} */ (instruction[1]);
    +        context.lineWidth = usePixelRatio ? lineWidth * pixelRatio : lineWidth;
    +        context.lineCap = /** @type {string} */ (instruction[3]);
    +        context.lineJoin = /** @type {string} */ (instruction[4]);
    +        context.miterLimit = /** @type {number} */ (instruction[5]);
    +        if (ol.has.CANVAS_LINE_DASH) {
    +          context.setLineDash(/** @type {Array.<number>} */ (instruction[6]));
    +        }
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.SET_TEXT_STYLE:
    +        goog.asserts.assert(goog.isString(instruction[1]));
    +        goog.asserts.assert(goog.isString(instruction[2]));
    +        goog.asserts.assert(goog.isString(instruction[3]));
    +        context.font = /** @type {string} */ (instruction[1]);
    +        context.textAlign = /** @type {string} */ (instruction[2]);
    +        context.textBaseline = /** @type {string} */ (instruction[3]);
    +        ++i;
    +        break;
    +      case ol.render.canvas.Instruction.STROKE:
    +        context.stroke();
    +        ++i;
    +        break;
    +      default:
    +        goog.asserts.fail();
    +        ++i; // consume the instruction anyway, to avoid an infinite loop
    +        break;
    +    }
    +  }
    +  // assert that all instructions were consumed
    +  goog.asserts.assert(i == instructions.length);
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip
    + */
    +ol.render.canvas.Replay.prototype.replay = function(
    +    context, pixelRatio, transform, viewRotation, skippedFeaturesHash) {
    +  var instructions = this.instructions;
    +  this.replay_(context, pixelRatio, transform, viewRotation,
    +      skippedFeaturesHash, instructions, undefined);
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip
    + * @param {function(ol.Feature): T=} opt_featureCallback Feature callback.
    + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
    + *     extent.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.canvas.Replay.prototype.replayHitDetection = function(
    +    context, transform, viewRotation, skippedFeaturesHash,
    +    opt_featureCallback, opt_hitExtent) {
    +  var instructions = this.hitDetectionInstructions;
    +  return this.replay_(context, 1, transform, viewRotation,
    +      skippedFeaturesHash, instructions, opt_featureCallback, opt_hitExtent);
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.render.canvas.Replay.prototype.reverseHitDetectionInstructions_ =
    +    function() {
    +  var hitDetectionInstructions = this.hitDetectionInstructions;
    +  // step 1 - reverse array
    +  hitDetectionInstructions.reverse();
    +  // step 2 - reverse instructions within geometry blocks
    +  var i;
    +  var n = hitDetectionInstructions.length;
    +  var instruction;
    +  var type;
    +  var begin = -1;
    +  for (i = 0; i < n; ++i) {
    +    instruction = hitDetectionInstructions[i];
    +    type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]);
    +    if (type == ol.render.canvas.Instruction.END_GEOMETRY) {
    +      goog.asserts.assert(begin == -1);
    +      begin = i;
    +    } else if (type == ol.render.canvas.Instruction.BEGIN_GEOMETRY) {
    +      instruction[2] = i;
    +      goog.asserts.assert(begin >= 0);
    +      ol.array.reverseSubArray(this.hitDetectionInstructions, begin, i);
    +      begin = -1;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawAsync = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawCircleGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawFeature = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawGeometryCollectionGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawLineStringGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawMultiLineStringGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawPointGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawMultiPointGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawPolygonGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawMultiPolygonGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.drawText = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.canvas.Replay.prototype.endGeometry = function(geometry, feature) {
    +  goog.asserts.assert(!goog.isNull(this.beginGeometryInstruction1_));
    +  this.beginGeometryInstruction1_[2] = this.instructions.length;
    +  this.beginGeometryInstruction1_ = null;
    +  goog.asserts.assert(!goog.isNull(this.beginGeometryInstruction2_));
    +  this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;
    +  this.beginGeometryInstruction2_ = null;
    +  var endGeometryInstruction =
    +      [ol.render.canvas.Instruction.END_GEOMETRY, feature];
    +  this.instructions.push(endGeometryInstruction);
    +  this.hitDetectionInstructions.push(endGeometryInstruction);
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.render.canvas.Replay.prototype.finish = goog.nullFunction;
    +
    +
    +/**
    + * Get the buffered rendering extent.  Rendering will be clipped to the extent
    + * provided to the constructor.  To account for symbolizers that may intersect
    + * this extent, we calculate a buffered extent (e.g. based on stroke width).
    + * @return {ol.Extent} The buffered rendering extent.
    + * @protected
    + */
    +ol.render.canvas.Replay.prototype.getBufferedMaxExtent = function() {
    +  return this.maxExtent;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.setFillStrokeStyle = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.setImageStyle = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.Replay.prototype.setTextStyle = goog.abstractMethod;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.render.canvas.Replay}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Maximum extent.
    + * @param {number} resolution Resolution.
    + * @protected
    + * @struct
    + */
    +ol.render.canvas.ImageReplay = function(tolerance, maxExtent, resolution) {
    +
    +  goog.base(this, tolerance, maxExtent, resolution);
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement|HTMLVideoElement|Image}
    +   */
    +  this.hitDetectionImage_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement|HTMLVideoElement|Image}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.anchorX_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.anchorY_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.height_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.opacity_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.originX_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.originY_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {boolean|undefined}
    +   */
    +  this.rotateWithView_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.rotation_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.scale_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {boolean|undefined}
    +   */
    +  this.snapToPixel_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.width_ = undefined;
    +
    +};
    +goog.inherits(ol.render.canvas.ImageReplay, ol.render.canvas.Replay);
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @private
    + * @return {number} My end.
    + */
    +ol.render.canvas.ImageReplay.prototype.drawCoordinates_ =
    +    function(flatCoordinates, offset, end, stride) {
    +  return this.appendFlatCoordinates(
    +      flatCoordinates, offset, end, stride, false);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ImageReplay.prototype.drawPointGeometry =
    +    function(pointGeometry, feature) {
    +  if (goog.isNull(this.image_)) {
    +    return;
    +  }
    +  goog.asserts.assert(goog.isDef(this.anchorX_));
    +  goog.asserts.assert(goog.isDef(this.anchorY_));
    +  goog.asserts.assert(goog.isDef(this.height_));
    +  goog.asserts.assert(goog.isDef(this.opacity_));
    +  goog.asserts.assert(goog.isDef(this.originX_));
    +  goog.asserts.assert(goog.isDef(this.originY_));
    +  goog.asserts.assert(goog.isDef(this.rotateWithView_));
    +  goog.asserts.assert(goog.isDef(this.rotation_));
    +  goog.asserts.assert(goog.isDef(this.scale_));
    +  goog.asserts.assert(goog.isDef(this.width_));
    +  this.beginGeometry(pointGeometry, feature);
    +  var flatCoordinates = pointGeometry.getFlatCoordinates();
    +  var stride = pointGeometry.getStride();
    +  var myBegin = this.coordinates.length;
    +  var myEnd = this.drawCoordinates_(
    +      flatCoordinates, 0, flatCoordinates.length, stride);
    +  this.instructions.push([
    +    ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
    +    // Remaining arguments to DRAW_IMAGE are in alphabetical order
    +    this.anchorX_, this.anchorY_, this.height_, this.opacity_,
    +    this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
    +    this.scale_, this.snapToPixel_, this.width_
    +  ]);
    +  this.hitDetectionInstructions.push([
    +    ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd,
    +    this.hitDetectionImage_,
    +    // Remaining arguments to DRAW_IMAGE are in alphabetical order
    +    this.anchorX_, this.anchorY_, this.height_, this.opacity_,
    +    this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
    +    this.scale_, this.snapToPixel_, this.width_
    +  ]);
    +  this.endGeometry(pointGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ImageReplay.prototype.drawMultiPointGeometry =
    +    function(multiPointGeometry, feature) {
    +  if (goog.isNull(this.image_)) {
    +    return;
    +  }
    +  goog.asserts.assert(goog.isDef(this.anchorX_));
    +  goog.asserts.assert(goog.isDef(this.anchorY_));
    +  goog.asserts.assert(goog.isDef(this.height_));
    +  goog.asserts.assert(goog.isDef(this.opacity_));
    +  goog.asserts.assert(goog.isDef(this.originX_));
    +  goog.asserts.assert(goog.isDef(this.originY_));
    +  goog.asserts.assert(goog.isDef(this.rotateWithView_));
    +  goog.asserts.assert(goog.isDef(this.rotation_));
    +  goog.asserts.assert(goog.isDef(this.scale_));
    +  goog.asserts.assert(goog.isDef(this.width_));
    +  this.beginGeometry(multiPointGeometry, feature);
    +  var flatCoordinates = multiPointGeometry.getFlatCoordinates();
    +  var stride = multiPointGeometry.getStride();
    +  var myBegin = this.coordinates.length;
    +  var myEnd = this.drawCoordinates_(
    +      flatCoordinates, 0, flatCoordinates.length, stride);
    +  this.instructions.push([
    +    ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_,
    +    // Remaining arguments to DRAW_IMAGE are in alphabetical order
    +    this.anchorX_, this.anchorY_, this.height_, this.opacity_,
    +    this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
    +    this.scale_, this.snapToPixel_, this.width_
    +  ]);
    +  this.hitDetectionInstructions.push([
    +    ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd,
    +    this.hitDetectionImage_,
    +    // Remaining arguments to DRAW_IMAGE are in alphabetical order
    +    this.anchorX_, this.anchorY_, this.height_, this.opacity_,
    +    this.originX_, this.originY_, this.rotateWithView_, this.rotation_,
    +    this.scale_, this.snapToPixel_, this.width_
    +  ]);
    +  this.endGeometry(multiPointGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ImageReplay.prototype.finish = function() {
    +  this.reverseHitDetectionInstructions_();
    +  // FIXME this doesn't really protect us against further calls to draw*Geometry
    +  this.anchorX_ = undefined;
    +  this.anchorY_ = undefined;
    +  this.hitDetectionImage_ = null;
    +  this.image_ = null;
    +  this.height_ = undefined;
    +  this.scale_ = undefined;
    +  this.opacity_ = undefined;
    +  this.originX_ = undefined;
    +  this.originY_ = undefined;
    +  this.rotateWithView_ = undefined;
    +  this.rotation_ = undefined;
    +  this.snapToPixel_ = undefined;
    +  this.width_ = undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) {
    +  goog.asserts.assert(!goog.isNull(imageStyle));
    +  var anchor = imageStyle.getAnchor();
    +  goog.asserts.assert(!goog.isNull(anchor));
    +  var size = imageStyle.getSize();
    +  goog.asserts.assert(!goog.isNull(size));
    +  var hitDetectionImage = imageStyle.getHitDetectionImage(1);
    +  goog.asserts.assert(!goog.isNull(hitDetectionImage));
    +  var image = imageStyle.getImage(1);
    +  goog.asserts.assert(!goog.isNull(image));
    +  var origin = imageStyle.getOrigin();
    +  goog.asserts.assert(!goog.isNull(origin));
    +  this.anchorX_ = anchor[0];
    +  this.anchorY_ = anchor[1];
    +  this.hitDetectionImage_ = hitDetectionImage;
    +  this.image_ = image;
    +  this.height_ = size[1];
    +  this.opacity_ = imageStyle.getOpacity();
    +  this.originX_ = origin[0];
    +  this.originY_ = origin[1];
    +  this.rotateWithView_ = imageStyle.getRotateWithView();
    +  this.rotation_ = imageStyle.getRotation();
    +  this.scale_ = imageStyle.getScale();
    +  this.snapToPixel_ = imageStyle.getSnapToPixel();
    +  this.width_ = size[0];
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.render.canvas.Replay}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Maximum extent.
    + * @param {number} resolution Resolution.
    + * @protected
    + * @struct
    + */
    +ol.render.canvas.LineStringReplay = function(tolerance, maxExtent, resolution) {
    +
    +  goog.base(this, tolerance, maxExtent, resolution);
    +
    +  /**
    +   * @private
    +   * @type {{currentStrokeStyle: (string|undefined),
    +   *         currentLineCap: (string|undefined),
    +   *         currentLineDash: Array.<number>,
    +   *         currentLineJoin: (string|undefined),
    +   *         currentLineWidth: (number|undefined),
    +   *         currentMiterLimit: (number|undefined),
    +   *         lastStroke: number,
    +   *         strokeStyle: (string|undefined),
    +   *         lineCap: (string|undefined),
    +   *         lineDash: Array.<number>,
    +   *         lineJoin: (string|undefined),
    +   *         lineWidth: (number|undefined),
    +   *         miterLimit: (number|undefined)}|null}
    +   */
    +  this.state_ = {
    +    currentStrokeStyle: undefined,
    +    currentLineCap: undefined,
    +    currentLineDash: null,
    +    currentLineJoin: undefined,
    +    currentLineWidth: undefined,
    +    currentMiterLimit: undefined,
    +    lastStroke: 0,
    +    strokeStyle: undefined,
    +    lineCap: undefined,
    +    lineDash: null,
    +    lineJoin: undefined,
    +    lineWidth: undefined,
    +    miterLimit: undefined
    +  };
    +
    +};
    +goog.inherits(ol.render.canvas.LineStringReplay, ol.render.canvas.Replay);
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @private
    + * @return {number} end.
    + */
    +ol.render.canvas.LineStringReplay.prototype.drawFlatCoordinates_ =
    +    function(flatCoordinates, offset, end, stride) {
    +  var myBegin = this.coordinates.length;
    +  var myEnd = this.appendFlatCoordinates(
    +      flatCoordinates, offset, end, stride, false);
    +  var moveToLineToInstruction =
    +      [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd];
    +  this.instructions.push(moveToLineToInstruction);
    +  this.hitDetectionInstructions.push(moveToLineToInstruction);
    +  return end;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.LineStringReplay.prototype.getBufferedMaxExtent = function() {
    +  if (goog.isNull(this.bufferedMaxExtent_)) {
    +    this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent);
    +    if (this.maxLineWidth > 0) {
    +      var width = this.resolution * (this.maxLineWidth + 1) / 2;
    +      ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
    +    }
    +  }
    +  return this.bufferedMaxExtent_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.render.canvas.LineStringReplay.prototype.setStrokeStyle_ = function() {
    +  var state = this.state_;
    +  var strokeStyle = state.strokeStyle;
    +  var lineCap = state.lineCap;
    +  var lineDash = state.lineDash;
    +  var lineJoin = state.lineJoin;
    +  var lineWidth = state.lineWidth;
    +  var miterLimit = state.miterLimit;
    +  goog.asserts.assert(goog.isDef(strokeStyle));
    +  goog.asserts.assert(goog.isDef(lineCap));
    +  goog.asserts.assert(!goog.isNull(lineDash));
    +  goog.asserts.assert(goog.isDef(lineJoin));
    +  goog.asserts.assert(goog.isDef(lineWidth));
    +  goog.asserts.assert(goog.isDef(miterLimit));
    +  if (state.currentStrokeStyle != strokeStyle ||
    +      state.currentLineCap != lineCap ||
    +      !goog.array.equals(state.currentLineDash, lineDash) ||
    +      state.currentLineJoin != lineJoin ||
    +      state.currentLineWidth != lineWidth ||
    +      state.currentMiterLimit != miterLimit) {
    +    if (state.lastStroke != this.coordinates.length) {
    +      this.instructions.push(
    +          [ol.render.canvas.Instruction.STROKE]);
    +      state.lastStroke = this.coordinates.length;
    +    }
    +    this.instructions.push(
    +        [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +         strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash],
    +        [ol.render.canvas.Instruction.BEGIN_PATH]);
    +    state.currentStrokeStyle = strokeStyle;
    +    state.currentLineCap = lineCap;
    +    state.currentLineDash = lineDash;
    +    state.currentLineJoin = lineJoin;
    +    state.currentLineWidth = lineWidth;
    +    state.currentMiterLimit = miterLimit;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.LineStringReplay.prototype.drawLineStringGeometry =
    +    function(lineStringGeometry, feature) {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  var strokeStyle = state.strokeStyle;
    +  var lineWidth = state.lineWidth;
    +  if (!goog.isDef(strokeStyle) || !goog.isDef(lineWidth)) {
    +    return;
    +  }
    +  this.setStrokeStyle_();
    +  this.beginGeometry(lineStringGeometry, feature);
    +  this.hitDetectionInstructions.push(
    +      [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +       state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
    +       state.miterLimit, state.lineDash],
    +      [ol.render.canvas.Instruction.BEGIN_PATH]);
    +  var flatCoordinates = lineStringGeometry.getFlatCoordinates();
    +  var stride = lineStringGeometry.getStride();
    +  this.drawFlatCoordinates_(
    +      flatCoordinates, 0, flatCoordinates.length, stride);
    +  this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]);
    +  this.endGeometry(lineStringGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.LineStringReplay.prototype.drawMultiLineStringGeometry =
    +    function(multiLineStringGeometry, feature) {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  var strokeStyle = state.strokeStyle;
    +  var lineWidth = state.lineWidth;
    +  if (!goog.isDef(strokeStyle) || !goog.isDef(lineWidth)) {
    +    return;
    +  }
    +  this.setStrokeStyle_();
    +  this.beginGeometry(multiLineStringGeometry, feature);
    +  this.hitDetectionInstructions.push(
    +      [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +       state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
    +       state.miterLimit, state.lineDash],
    +      [ol.render.canvas.Instruction.BEGIN_PATH]);
    +  var ends = multiLineStringGeometry.getEnds();
    +  var flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
    +  var stride = multiLineStringGeometry.getStride();
    +  var offset = 0;
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    offset = this.drawFlatCoordinates_(
    +        flatCoordinates, offset, ends[i], stride);
    +  }
    +  this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]);
    +  this.endGeometry(multiLineStringGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.LineStringReplay.prototype.finish = function() {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  if (state.lastStroke != this.coordinates.length) {
    +    this.instructions.push([ol.render.canvas.Instruction.STROKE]);
    +  }
    +  this.reverseHitDetectionInstructions_();
    +  this.state_ = null;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle =
    +    function(fillStyle, strokeStyle) {
    +  goog.asserts.assert(!goog.isNull(this.state_));
    +  goog.asserts.assert(goog.isNull(fillStyle));
    +  goog.asserts.assert(!goog.isNull(strokeStyle));
    +  var strokeStyleColor = strokeStyle.getColor();
    +  this.state_.strokeStyle = ol.color.asString(!goog.isNull(strokeStyleColor) ?
    +      strokeStyleColor : ol.render.canvas.defaultStrokeStyle);
    +  var strokeStyleLineCap = strokeStyle.getLineCap();
    +  this.state_.lineCap = goog.isDef(strokeStyleLineCap) ?
    +      strokeStyleLineCap : ol.render.canvas.defaultLineCap;
    +  var strokeStyleLineDash = strokeStyle.getLineDash();
    +  this.state_.lineDash = !goog.isNull(strokeStyleLineDash) ?
    +      strokeStyleLineDash : ol.render.canvas.defaultLineDash;
    +  var strokeStyleLineJoin = strokeStyle.getLineJoin();
    +  this.state_.lineJoin = goog.isDef(strokeStyleLineJoin) ?
    +      strokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
    +  var strokeStyleWidth = strokeStyle.getWidth();
    +  this.state_.lineWidth = goog.isDef(strokeStyleWidth) ?
    +      strokeStyleWidth : ol.render.canvas.defaultLineWidth;
    +  var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
    +  this.state_.miterLimit = goog.isDef(strokeStyleMiterLimit) ?
    +      strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
    +
    +  if (this.state_.lineWidth > this.maxLineWidth) {
    +    this.maxLineWidth = this.state_.lineWidth;
    +    // invalidate the buffered max extent cache
    +    this.bufferedMaxExtent_ = null;
    +  }
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.render.canvas.Replay}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Maximum extent.
    + * @param {number} resolution Resolution.
    + * @protected
    + * @struct
    + */
    +ol.render.canvas.PolygonReplay = function(tolerance, maxExtent, resolution) {
    +
    +  goog.base(this, tolerance, maxExtent, resolution);
    +
    +  /**
    +   * @private
    +   * @type {{currentFillStyle: (string|undefined),
    +   *         currentStrokeStyle: (string|undefined),
    +   *         currentLineCap: (string|undefined),
    +   *         currentLineDash: Array.<number>,
    +   *         currentLineJoin: (string|undefined),
    +   *         currentLineWidth: (number|undefined),
    +   *         currentMiterLimit: (number|undefined),
    +   *         fillStyle: (string|undefined),
    +   *         strokeStyle: (string|undefined),
    +   *         lineCap: (string|undefined),
    +   *         lineDash: Array.<number>,
    +   *         lineJoin: (string|undefined),
    +   *         lineWidth: (number|undefined),
    +   *         miterLimit: (number|undefined)}|null}
    +   */
    +  this.state_ = {
    +    currentFillStyle: undefined,
    +    currentStrokeStyle: undefined,
    +    currentLineCap: undefined,
    +    currentLineDash: null,
    +    currentLineJoin: undefined,
    +    currentLineWidth: undefined,
    +    currentMiterLimit: undefined,
    +    fillStyle: undefined,
    +    strokeStyle: undefined,
    +    lineCap: undefined,
    +    lineDash: null,
    +    lineJoin: undefined,
    +    lineWidth: undefined,
    +    miterLimit: undefined
    +  };
    +
    +};
    +goog.inherits(ol.render.canvas.PolygonReplay, ol.render.canvas.Replay);
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {Array.<number>} ends Ends.
    + * @param {number} stride Stride.
    + * @private
    + * @return {number} End.
    + */
    +ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ =
    +    function(flatCoordinates, offset, ends, stride) {
    +  var state = this.state_;
    +  var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH];
    +  this.instructions.push(beginPathInstruction);
    +  this.hitDetectionInstructions.push(beginPathInstruction);
    +  var i, ii;
    +  for (i = 0, ii = ends.length; i < ii; ++i) {
    +    var end = ends[i];
    +    var myBegin = this.coordinates.length;
    +    var myEnd = this.appendFlatCoordinates(
    +        flatCoordinates, offset, end, stride, true);
    +    var moveToLineToInstruction =
    +        [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd];
    +    var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH];
    +    this.instructions.push(moveToLineToInstruction, closePathInstruction);
    +    this.hitDetectionInstructions.push(moveToLineToInstruction,
    +        closePathInstruction);
    +    offset = end;
    +  }
    +  // FIXME is it quicker to fill and stroke each polygon individually,
    +  // FIXME or all polygons together?
    +  var fillInstruction = [ol.render.canvas.Instruction.FILL];
    +  this.hitDetectionInstructions.push(fillInstruction);
    +  if (goog.isDef(state.fillStyle)) {
    +    this.instructions.push(fillInstruction);
    +  }
    +  if (goog.isDef(state.strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(state.lineWidth));
    +    var strokeInstruction = [ol.render.canvas.Instruction.STROKE];
    +    this.instructions.push(strokeInstruction);
    +    this.hitDetectionInstructions.push(strokeInstruction);
    +  }
    +  return offset;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.drawCircleGeometry =
    +    function(circleGeometry, feature) {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  var fillStyle = state.fillStyle;
    +  var strokeStyle = state.strokeStyle;
    +  if (!goog.isDef(fillStyle) && !goog.isDef(strokeStyle)) {
    +    return;
    +  }
    +  if (goog.isDef(strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(state.lineWidth));
    +  }
    +  this.setFillStrokeStyles_();
    +  this.beginGeometry(circleGeometry, feature);
    +  // always fill the circle for hit detection
    +  this.hitDetectionInstructions.push(
    +      [ol.render.canvas.Instruction.SET_FILL_STYLE,
    +       ol.color.asString(ol.render.canvas.defaultFillStyle)]);
    +  if (goog.isDef(state.strokeStyle)) {
    +    this.hitDetectionInstructions.push(
    +        [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +         state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
    +         state.miterLimit, state.lineDash]);
    +  }
    +  var flatCoordinates = circleGeometry.getFlatCoordinates();
    +  var stride = circleGeometry.getStride();
    +  var myBegin = this.coordinates.length;
    +  this.appendFlatCoordinates(
    +      flatCoordinates, 0, flatCoordinates.length, stride, false);
    +  var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH];
    +  var circleInstruction = [ol.render.canvas.Instruction.CIRCLE, myBegin];
    +  this.instructions.push(beginPathInstruction, circleInstruction);
    +  this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
    +  var fillInstruction = [ol.render.canvas.Instruction.FILL];
    +  this.hitDetectionInstructions.push(fillInstruction);
    +  if (goog.isDef(state.fillStyle)) {
    +    this.instructions.push(fillInstruction);
    +  }
    +  if (goog.isDef(state.strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(state.lineWidth));
    +    var strokeInstruction = [ol.render.canvas.Instruction.STROKE];
    +    this.instructions.push(strokeInstruction);
    +    this.hitDetectionInstructions.push(strokeInstruction);
    +  }
    +  this.endGeometry(circleGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.drawPolygonGeometry =
    +    function(polygonGeometry, feature) {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  var fillStyle = state.fillStyle;
    +  var strokeStyle = state.strokeStyle;
    +  if (!goog.isDef(fillStyle) && !goog.isDef(strokeStyle)) {
    +    return;
    +  }
    +  if (goog.isDef(strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(state.lineWidth));
    +  }
    +  this.setFillStrokeStyles_();
    +  this.beginGeometry(polygonGeometry, feature);
    +  // always fill the polygon for hit detection
    +  this.hitDetectionInstructions.push(
    +      [ol.render.canvas.Instruction.SET_FILL_STYLE,
    +       ol.color.asString(ol.render.canvas.defaultFillStyle)]);
    +  if (goog.isDef(state.strokeStyle)) {
    +    this.hitDetectionInstructions.push(
    +        [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +         state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
    +         state.miterLimit, state.lineDash]);
    +  }
    +  var ends = polygonGeometry.getEnds();
    +  var flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
    +  var stride = polygonGeometry.getStride();
    +  this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
    +  this.endGeometry(polygonGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.drawMultiPolygonGeometry =
    +    function(multiPolygonGeometry, feature) {
    +  var state = this.state_;
    +  goog.asserts.assert(!goog.isNull(state));
    +  var fillStyle = state.fillStyle;
    +  var strokeStyle = state.strokeStyle;
    +  if (!goog.isDef(fillStyle) && !goog.isDef(strokeStyle)) {
    +    return;
    +  }
    +  if (goog.isDef(strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(state.lineWidth));
    +  }
    +  this.setFillStrokeStyles_();
    +  this.beginGeometry(multiPolygonGeometry, feature);
    +  // always fill the multi-polygon for hit detection
    +  this.hitDetectionInstructions.push(
    +      [ol.render.canvas.Instruction.SET_FILL_STYLE,
    +       ol.color.asString(ol.render.canvas.defaultFillStyle)]);
    +  if (goog.isDef(state.strokeStyle)) {
    +    this.hitDetectionInstructions.push(
    +        [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +         state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin,
    +         state.miterLimit, state.lineDash]);
    +  }
    +  var endss = multiPolygonGeometry.getEndss();
    +  var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
    +  var stride = multiPolygonGeometry.getStride();
    +  var offset = 0;
    +  var i, ii;
    +  for (i = 0, ii = endss.length; i < ii; ++i) {
    +    offset = this.drawFlatCoordinatess_(
    +        flatCoordinates, offset, endss[i], stride);
    +  }
    +  this.endGeometry(multiPolygonGeometry, feature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.finish = function() {
    +  goog.asserts.assert(!goog.isNull(this.state_));
    +  this.reverseHitDetectionInstructions_();
    +  this.state_ = null;
    +  // We want to preserve topology when drawing polygons.  Polygons are
    +  // simplified using quantization and point elimination. However, we might
    +  // have received a mix of quantized and non-quantized geometries, so ensure
    +  // that all are quantized by quantizing all coordinates in the batch.
    +  var tolerance = this.tolerance;
    +  if (tolerance !== 0) {
    +    var coordinates = this.coordinates;
    +    var i, ii;
    +    for (i = 0, ii = coordinates.length; i < ii; ++i) {
    +      coordinates[i] = ol.geom.flat.simplify.snap(coordinates[i], tolerance);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.getBufferedMaxExtent = function() {
    +  if (goog.isNull(this.bufferedMaxExtent_)) {
    +    this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent);
    +    if (this.maxLineWidth > 0) {
    +      var width = this.resolution * (this.maxLineWidth + 1) / 2;
    +      ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
    +    }
    +  }
    +  return this.bufferedMaxExtent_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle =
    +    function(fillStyle, strokeStyle) {
    +  goog.asserts.assert(!goog.isNull(this.state_));
    +  goog.asserts.assert(!goog.isNull(fillStyle) || !goog.isNull(strokeStyle));
    +  var state = this.state_;
    +  if (!goog.isNull(fillStyle)) {
    +    var fillStyleColor = fillStyle.getColor();
    +    state.fillStyle = ol.color.asString(!goog.isNull(fillStyleColor) ?
    +        fillStyleColor : ol.render.canvas.defaultFillStyle);
    +  } else {
    +    state.fillStyle = undefined;
    +  }
    +  if (!goog.isNull(strokeStyle)) {
    +    var strokeStyleColor = strokeStyle.getColor();
    +    state.strokeStyle = ol.color.asString(!goog.isNull(strokeStyleColor) ?
    +        strokeStyleColor : ol.render.canvas.defaultStrokeStyle);
    +    var strokeStyleLineCap = strokeStyle.getLineCap();
    +    state.lineCap = goog.isDef(strokeStyleLineCap) ?
    +        strokeStyleLineCap : ol.render.canvas.defaultLineCap;
    +    var strokeStyleLineDash = strokeStyle.getLineDash();
    +    state.lineDash = !goog.isNull(strokeStyleLineDash) ?
    +        strokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash;
    +    var strokeStyleLineJoin = strokeStyle.getLineJoin();
    +    state.lineJoin = goog.isDef(strokeStyleLineJoin) ?
    +        strokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
    +    var strokeStyleWidth = strokeStyle.getWidth();
    +    state.lineWidth = goog.isDef(strokeStyleWidth) ?
    +        strokeStyleWidth : ol.render.canvas.defaultLineWidth;
    +    var strokeStyleMiterLimit = strokeStyle.getMiterLimit();
    +    state.miterLimit = goog.isDef(strokeStyleMiterLimit) ?
    +        strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
    +
    +    if (state.lineWidth > this.maxLineWidth) {
    +      this.maxLineWidth = state.lineWidth;
    +      // invalidate the buffered max extent cache
    +      this.bufferedMaxExtent_ = null;
    +    }
    +  } else {
    +    state.strokeStyle = undefined;
    +    state.lineCap = undefined;
    +    state.lineDash = null;
    +    state.lineJoin = undefined;
    +    state.lineWidth = undefined;
    +    state.miterLimit = undefined;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() {
    +  var state = this.state_;
    +  var fillStyle = state.fillStyle;
    +  var strokeStyle = state.strokeStyle;
    +  var lineCap = state.lineCap;
    +  var lineDash = state.lineDash;
    +  var lineJoin = state.lineJoin;
    +  var lineWidth = state.lineWidth;
    +  var miterLimit = state.miterLimit;
    +  if (goog.isDef(fillStyle) && state.currentFillStyle != fillStyle) {
    +    this.instructions.push(
    +        [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle]);
    +    state.currentFillStyle = state.fillStyle;
    +  }
    +  if (goog.isDef(strokeStyle)) {
    +    goog.asserts.assert(goog.isDef(lineCap));
    +    goog.asserts.assert(!goog.isNull(lineDash));
    +    goog.asserts.assert(goog.isDef(lineJoin));
    +    goog.asserts.assert(goog.isDef(lineWidth));
    +    goog.asserts.assert(goog.isDef(miterLimit));
    +    if (state.currentStrokeStyle != strokeStyle ||
    +        state.currentLineCap != lineCap ||
    +        state.currentLineDash != lineDash ||
    +        state.currentLineJoin != lineJoin ||
    +        state.currentLineWidth != lineWidth ||
    +        state.currentMiterLimit != miterLimit) {
    +      this.instructions.push(
    +          [ol.render.canvas.Instruction.SET_STROKE_STYLE,
    +           strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash]);
    +      state.currentStrokeStyle = strokeStyle;
    +      state.currentLineCap = lineCap;
    +      state.currentLineDash = lineDash;
    +      state.currentLineJoin = lineJoin;
    +      state.currentLineWidth = lineWidth;
    +      state.currentMiterLimit = miterLimit;
    +    }
    +  }
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.render.canvas.Replay}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Maximum extent.
    + * @param {number} resolution Resolution.
    + * @protected
    + * @struct
    + */
    +ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution) {
    +
    +  goog.base(this, tolerance, maxExtent, resolution);
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.FillState}
    +   */
    +  this.replayFillState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.StrokeState}
    +   */
    +  this.replayStrokeState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.TextState}
    +   */
    +  this.replayTextState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.text_ = '';
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textOffsetX_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textOffsetY_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textRotation_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textScale_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.FillState}
    +   */
    +  this.textFillState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.StrokeState}
    +   */
    +  this.textStrokeState_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.render.canvas.TextState}
    +   */
    +  this.textState_ = null;
    +
    +};
    +goog.inherits(ol.render.canvas.TextReplay, ol.render.canvas.Replay);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.TextReplay.prototype.drawText =
    +    function(flatCoordinates, offset, end, stride, geometry, feature) {
    +  if (this.text_ === '' ||
    +      goog.isNull(this.textState_) ||
    +      (goog.isNull(this.textFillState_) &&
    +       goog.isNull(this.textStrokeState_))) {
    +    return;
    +  }
    +  if (!goog.isNull(this.textFillState_)) {
    +    this.setReplayFillState_(this.textFillState_);
    +  }
    +  if (!goog.isNull(this.textStrokeState_)) {
    +    this.setReplayStrokeState_(this.textStrokeState_);
    +  }
    +  this.setReplayTextState_(this.textState_);
    +  this.beginGeometry(geometry, feature);
    +  var myBegin = this.coordinates.length;
    +  var myEnd =
    +      this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false);
    +  var fill = !goog.isNull(this.textFillState_);
    +  var stroke = !goog.isNull(this.textStrokeState_);
    +  var drawTextInstruction = [
    +    ol.render.canvas.Instruction.DRAW_TEXT, myBegin, myEnd, this.text_,
    +    this.textOffsetX_, this.textOffsetY_, this.textRotation_, this.textScale_,
    +    fill, stroke];
    +  this.instructions.push(drawTextInstruction);
    +  this.hitDetectionInstructions.push(drawTextInstruction);
    +  this.endGeometry(geometry, feature);
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.FillState} fillState Fill state.
    + * @private
    + */
    +ol.render.canvas.TextReplay.prototype.setReplayFillState_ =
    +    function(fillState) {
    +  var replayFillState = this.replayFillState_;
    +  if (!goog.isNull(replayFillState) &&
    +      replayFillState.fillStyle == fillState.fillStyle) {
    +    return;
    +  }
    +  var setFillStyleInstruction =
    +      [ol.render.canvas.Instruction.SET_FILL_STYLE, fillState.fillStyle];
    +  this.instructions.push(setFillStyleInstruction);
    +  this.hitDetectionInstructions.push(setFillStyleInstruction);
    +  if (goog.isNull(replayFillState)) {
    +    this.replayFillState_ = {
    +      fillStyle: fillState.fillStyle
    +    };
    +  } else {
    +    replayFillState.fillStyle = fillState.fillStyle;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.StrokeState} strokeState Stroke state.
    + * @private
    + */
    +ol.render.canvas.TextReplay.prototype.setReplayStrokeState_ =
    +    function(strokeState) {
    +  var replayStrokeState = this.replayStrokeState_;
    +  if (!goog.isNull(replayStrokeState) &&
    +      replayStrokeState.lineCap == strokeState.lineCap &&
    +      replayStrokeState.lineDash == strokeState.lineDash &&
    +      replayStrokeState.lineJoin == strokeState.lineJoin &&
    +      replayStrokeState.lineWidth == strokeState.lineWidth &&
    +      replayStrokeState.miterLimit == strokeState.miterLimit &&
    +      replayStrokeState.strokeStyle == strokeState.strokeStyle) {
    +    return;
    +  }
    +  var setStrokeStyleInstruction = [
    +    ol.render.canvas.Instruction.SET_STROKE_STYLE, strokeState.strokeStyle,
    +    strokeState.lineWidth, strokeState.lineCap, strokeState.lineJoin,
    +    strokeState.miterLimit, strokeState.lineDash, false
    +  ];
    +  this.instructions.push(setStrokeStyleInstruction);
    +  this.hitDetectionInstructions.push(setStrokeStyleInstruction);
    +  if (goog.isNull(replayStrokeState)) {
    +    this.replayStrokeState_ = {
    +      lineCap: strokeState.lineCap,
    +      lineDash: strokeState.lineDash,
    +      lineJoin: strokeState.lineJoin,
    +      lineWidth: strokeState.lineWidth,
    +      miterLimit: strokeState.miterLimit,
    +      strokeStyle: strokeState.strokeStyle
    +    };
    +  } else {
    +    replayStrokeState.lineCap = strokeState.lineCap;
    +    replayStrokeState.lineDash = strokeState.lineDash;
    +    replayStrokeState.lineJoin = strokeState.lineJoin;
    +    replayStrokeState.lineWidth = strokeState.lineWidth;
    +    replayStrokeState.miterLimit = strokeState.miterLimit;
    +    replayStrokeState.strokeStyle = strokeState.strokeStyle;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.canvas.TextState} textState Text state.
    + * @private
    + */
    +ol.render.canvas.TextReplay.prototype.setReplayTextState_ =
    +    function(textState) {
    +  var replayTextState = this.replayTextState_;
    +  if (!goog.isNull(replayTextState) &&
    +      replayTextState.font == textState.font &&
    +      replayTextState.textAlign == textState.textAlign &&
    +      replayTextState.textBaseline == textState.textBaseline) {
    +    return;
    +  }
    +  var setTextStyleInstruction = [ol.render.canvas.Instruction.SET_TEXT_STYLE,
    +    textState.font, textState.textAlign, textState.textBaseline];
    +  this.instructions.push(setTextStyleInstruction);
    +  this.hitDetectionInstructions.push(setTextStyleInstruction);
    +  if (goog.isNull(replayTextState)) {
    +    this.replayTextState_ = {
    +      font: textState.font,
    +      textAlign: textState.textAlign,
    +      textBaseline: textState.textBaseline
    +    };
    +  } else {
    +    replayTextState.font = textState.font;
    +    replayTextState.textAlign = textState.textAlign;
    +    replayTextState.textBaseline = textState.textBaseline;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) {
    +  if (goog.isNull(textStyle)) {
    +    this.text_ = '';
    +  } else {
    +    var textFillStyle = textStyle.getFill();
    +    if (goog.isNull(textFillStyle)) {
    +      this.textFillState_ = null;
    +    } else {
    +      var textFillStyleColor = textFillStyle.getColor();
    +      var fillStyle = ol.color.asString(!goog.isNull(textFillStyleColor) ?
    +          textFillStyleColor : ol.render.canvas.defaultFillStyle);
    +      if (goog.isNull(this.textFillState_)) {
    +        this.textFillState_ = {
    +          fillStyle: fillStyle
    +        };
    +      } else {
    +        var textFillState = this.textFillState_;
    +        textFillState.fillStyle = fillStyle;
    +      }
    +    }
    +    var textStrokeStyle = textStyle.getStroke();
    +    if (goog.isNull(textStrokeStyle)) {
    +      this.textStrokeState_ = null;
    +    } else {
    +      var textStrokeStyleColor = textStrokeStyle.getColor();
    +      var textStrokeStyleLineCap = textStrokeStyle.getLineCap();
    +      var textStrokeStyleLineDash = textStrokeStyle.getLineDash();
    +      var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin();
    +      var textStrokeStyleWidth = textStrokeStyle.getWidth();
    +      var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
    +      var lineCap = goog.isDef(textStrokeStyleLineCap) ?
    +          textStrokeStyleLineCap : ol.render.canvas.defaultLineCap;
    +      var lineDash = goog.isDefAndNotNull(textStrokeStyleLineDash) ?
    +          textStrokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash;
    +      var lineJoin = goog.isDef(textStrokeStyleLineJoin) ?
    +          textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin;
    +      var lineWidth = goog.isDef(textStrokeStyleWidth) ?
    +          textStrokeStyleWidth : ol.render.canvas.defaultLineWidth;
    +      var miterLimit = goog.isDef(textStrokeStyleMiterLimit) ?
    +          textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit;
    +      var strokeStyle = ol.color.asString(!goog.isNull(textStrokeStyleColor) ?
    +          textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle);
    +      if (goog.isNull(this.textStrokeState_)) {
    +        this.textStrokeState_ = {
    +          lineCap: lineCap,
    +          lineDash: lineDash,
    +          lineJoin: lineJoin,
    +          lineWidth: lineWidth,
    +          miterLimit: miterLimit,
    +          strokeStyle: strokeStyle
    +        };
    +      } else {
    +        var textStrokeState = this.textStrokeState_;
    +        textStrokeState.lineCap = lineCap;
    +        textStrokeState.lineDash = lineDash;
    +        textStrokeState.lineJoin = lineJoin;
    +        textStrokeState.lineWidth = lineWidth;
    +        textStrokeState.miterLimit = miterLimit;
    +        textStrokeState.strokeStyle = strokeStyle;
    +      }
    +    }
    +    var textFont = textStyle.getFont();
    +    var textOffsetX = textStyle.getOffsetX();
    +    var textOffsetY = textStyle.getOffsetY();
    +    var textRotation = textStyle.getRotation();
    +    var textScale = textStyle.getScale();
    +    var textText = textStyle.getText();
    +    var textTextAlign = textStyle.getTextAlign();
    +    var textTextBaseline = textStyle.getTextBaseline();
    +    var font = goog.isDef(textFont) ?
    +        textFont : ol.render.canvas.defaultFont;
    +    var textAlign = goog.isDef(textTextAlign) ?
    +        textTextAlign : ol.render.canvas.defaultTextAlign;
    +    var textBaseline = goog.isDef(textTextBaseline) ?
    +        textTextBaseline : ol.render.canvas.defaultTextBaseline;
    +    if (goog.isNull(this.textState_)) {
    +      this.textState_ = {
    +        font: font,
    +        textAlign: textAlign,
    +        textBaseline: textBaseline
    +      };
    +    } else {
    +      var textState = this.textState_;
    +      textState.font = font;
    +      textState.textAlign = textAlign;
    +      textState.textBaseline = textBaseline;
    +    }
    +    this.text_ = goog.isDef(textText) ? textText : '';
    +    this.textOffsetX_ = goog.isDef(textOffsetX) ? textOffsetX : 0;
    +    this.textOffsetY_ = goog.isDef(textOffsetY) ? textOffsetY : 0;
    +    this.textRotation_ = goog.isDef(textRotation) ? textRotation : 0;
    +    this.textScale_ = goog.isDef(textScale) ? textScale : 1;
    +  }
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.render.IReplayGroup}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Max extent.
    + * @param {number} resolution Resolution.
    + * @param {number=} opt_renderBuffer Optional rendering buffer.
    + * @struct
    + */
    +ol.render.canvas.ReplayGroup = function(
    +    tolerance, maxExtent, resolution, opt_renderBuffer) {
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.tolerance_ = tolerance;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.maxExtent_ = maxExtent;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.resolution_ = resolution;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.renderBuffer_ = opt_renderBuffer;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string,
    +   *        Object.<ol.render.ReplayType, ol.render.canvas.Replay>>}
    +   */
    +  this.replaysByZIndex_ = {};
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.hitDetectionContext_ = ol.dom.createCanvasContext2D(1, 1);
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.hitDetectionTransform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.render.canvas.ReplayGroup.prototype.finish = function() {
    +  var zKey;
    +  for (zKey in this.replaysByZIndex_) {
    +    var replays = this.replaysByZIndex_[zKey];
    +    var replayKey;
    +    for (replayKey in replays) {
    +      replays[replayKey].finish();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip
    + * @param {function(ol.Feature): T} callback Feature callback.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
    +    coordinate, resolution, rotation, skippedFeaturesHash, callback) {
    +
    +  var transform = this.hitDetectionTransform_;
    +  ol.vec.Mat4.makeTransform2D(transform, 0.5, 0.5,
    +      1 / resolution, -1 / resolution, -rotation,
    +      -coordinate[0], -coordinate[1]);
    +
    +  var context = this.hitDetectionContext_;
    +  context.clearRect(0, 0, 1, 1);
    +
    +  /**
    +   * @type {ol.Extent}
    +   */
    +  var hitExtent;
    +  if (goog.isDef(this.renderBuffer_)) {
    +    hitExtent = ol.extent.createEmpty();
    +    ol.extent.extendCoordinate(hitExtent, coordinate);
    +    ol.extent.buffer(hitExtent, resolution * this.renderBuffer_, hitExtent);
    +  }
    +
    +  return this.replayHitDetection_(context, transform, rotation,
    +      skippedFeaturesHash,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {?} Callback result.
    +       */
    +      function(feature) {
    +        var imageData = context.getImageData(0, 0, 1, 1).data;
    +        if (imageData[3] > 0) {
    +          var result = callback(feature);
    +          if (result) {
    +            return result;
    +          }
    +          context.clearRect(0, 0, 1, 1);
    +        }
    +      }, hitExtent);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ReplayGroup.prototype.getReplay =
    +    function(zIndex, replayType) {
    +  var zIndexKey = goog.isDef(zIndex) ? zIndex.toString() : '0';
    +  var replays = this.replaysByZIndex_[zIndexKey];
    +  if (!goog.isDef(replays)) {
    +    replays = {};
    +    this.replaysByZIndex_[zIndexKey] = replays;
    +  }
    +  var replay = replays[replayType];
    +  if (!goog.isDef(replay)) {
    +    var Constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType];
    +    goog.asserts.assert(goog.isDef(Constructor));
    +    replay = new Constructor(this.tolerance_, this.maxExtent_,
    +        this.resolution_);
    +    replays[replayType] = replay;
    +  }
    +  return replay;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.canvas.ReplayGroup.prototype.isEmpty = function() {
    +  return goog.object.isEmpty(this.replaysByZIndex_);
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip
    + */
    +ol.render.canvas.ReplayGroup.prototype.replay = function(
    +    context, pixelRatio, transform, viewRotation, skippedFeaturesHash) {
    +
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number);
    +  goog.array.sort(zs);
    +
    +  // setup clipping so that the parts of over-simplified geometries are not
    +  // visible outside the current extent when panning
    +  var maxExtent = this.maxExtent_;
    +  var minX = maxExtent[0];
    +  var minY = maxExtent[1];
    +  var maxX = maxExtent[2];
    +  var maxY = maxExtent[3];
    +  var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
    +  ol.geom.flat.transform.transform2D(
    +      flatClipCoords, 0, 8, 2, transform, flatClipCoords);
    +  context.save();
    +  context.beginPath();
    +  context.moveTo(flatClipCoords[0], flatClipCoords[1]);
    +  context.lineTo(flatClipCoords[2], flatClipCoords[3]);
    +  context.lineTo(flatClipCoords[4], flatClipCoords[5]);
    +  context.lineTo(flatClipCoords[6], flatClipCoords[7]);
    +  context.closePath();
    +  context.clip();
    +
    +  var i, ii, j, jj, replays, replay, result;
    +  for (i = 0, ii = zs.length; i < ii; ++i) {
    +    replays = this.replaysByZIndex_[zs[i].toString()];
    +    for (j = 0, jj = ol.render.REPLAY_ORDER.length; j < jj; ++j) {
    +      replay = replays[ol.render.REPLAY_ORDER[j]];
    +      if (goog.isDef(replay)) {
    +        replay.replay(context, pixelRatio, transform, viewRotation,
    +            skippedFeaturesHash);
    +      }
    +    }
    +  }
    +
    +  context.restore();
    +};
    +
    +
    +/**
    + * @private
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @param {number} viewRotation View rotation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip
    + * @param {function(ol.Feature): T} featureCallback Feature callback.
    + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this
    + *     extent.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function(
    +    context, transform, viewRotation, skippedFeaturesHash,
    +    featureCallback, opt_hitExtent) {
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(this.replaysByZIndex_), Number);
    +  goog.array.sort(zs, function(a, b) { return b - a; });
    +
    +  var i, ii, j, replays, replay, result;
    +  for (i = 0, ii = zs.length; i < ii; ++i) {
    +    replays = this.replaysByZIndex_[zs[i].toString()];
    +    for (j = ol.render.REPLAY_ORDER.length - 1; j >= 0; --j) {
    +      replay = replays[ol.render.REPLAY_ORDER[j]];
    +      if (goog.isDef(replay)) {
    +        result = replay.replayHitDetection(context, transform, viewRotation,
    +            skippedFeaturesHash, featureCallback, opt_hitExtent);
    +        if (result) {
    +          return result;
    +        }
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<ol.render.ReplayType,
    + *                function(new: ol.render.canvas.Replay, number, ol.Extent,
    + *                number)>}
    + */
    +ol.render.canvas.BATCH_CONSTRUCTORS_ = {
    +  'Image': ol.render.canvas.ImageReplay,
    +  'LineString': ol.render.canvas.LineStringReplay,
    +  'Polygon': ol.render.canvas.PolygonReplay,
    +  'Text': ol.render.canvas.TextReplay
    +};
    diff --git a/mapbox-gl/ol/ol/render/ireplay.js b/mapbox-gl/ol/ol/render/ireplay.js
    new file mode 100644
    index 0000000000..701b391be7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/ireplay.js
    @@ -0,0 +1,50 @@
    +goog.provide('ol.render.IReplayGroup');
    +
    +goog.require('ol.render.IVectorContext');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.render.ReplayType = {
    +  IMAGE: 'Image',
    +  LINE_STRING: 'LineString',
    +  POLYGON: 'Polygon',
    +  TEXT: 'Text'
    +};
    +
    +
    +/**
    + * @const
    + * @type {Array.<ol.render.ReplayType>}
    + */
    +ol.render.REPLAY_ORDER = [
    +  ol.render.ReplayType.POLYGON,
    +  ol.render.ReplayType.LINE_STRING,
    +  ol.render.ReplayType.IMAGE,
    +  ol.render.ReplayType.TEXT
    +];
    +
    +
    +
    +/**
    + * @interface
    + */
    +ol.render.IReplayGroup = function() {
    +};
    +
    +
    +/**
    + * @param {number|undefined} zIndex Z index.
    + * @param {ol.render.ReplayType} replayType Replay type.
    + * @return {ol.render.IVectorContext} Replay.
    + */
    +ol.render.IReplayGroup.prototype.getReplay = function(zIndex, replayType) {
    +};
    +
    +
    +/**
    + * @return {boolean} Is empty.
    + */
    +ol.render.IReplayGroup.prototype.isEmpty = function() {
    +};
    diff --git a/mapbox-gl/ol/ol/render/ivectorcontext.js b/mapbox-gl/ol/ol/render/ivectorcontext.js
    new file mode 100644
    index 0000000000..3b491515ae
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/ivectorcontext.js
    @@ -0,0 +1,139 @@
    +// FIXME remove trailing "Geometry" in method names
    +
    +goog.provide('ol.render.IVectorContext');
    +
    +
    +
    +/**
    + * VectorContext interface. Implemented by
    + * {@link ol.render.canvas.Immediate} and {@link ol.render.webgl.Immediate}.
    + * @interface
    + */
    +ol.render.IVectorContext = function() {
    +};
    +
    +
    +/**
    + * @param {number} zIndex Z index.
    + * @param {function(ol.render.IVectorContext)} callback Callback.
    + */
    +ol.render.IVectorContext.prototype.drawAsync = function(zIndex, callback) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.Circle} circleGeometry Circle geometry.
    + * @param {ol.Feature} feature Feature,
    + */
    +ol.render.IVectorContext.prototype.drawCircleGeometry =
    +    function(circleGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {ol.style.Style} style Style.
    + */
    +ol.render.IVectorContext.prototype.drawFeature = function(feature, style) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry
    + *     collection.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawGeometryCollectionGeometry =
    +    function(geometryCollectionGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.Point} pointGeometry Point geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawPointGeometry =
    +    function(pointGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.LineString} lineStringGeometry Line string geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawLineStringGeometry =
    +    function(lineStringGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiLineString} multiLineStringGeometry
    + *     MultiLineString geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawMultiLineStringGeometry =
    +    function(multiLineStringGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiPoint} multiPointGeometry MultiPoint geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawMultiPointGeometry =
    +    function(multiPointGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawMultiPolygonGeometry =
    +    function(multiPolygonGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.geom.Polygon} polygonGeometry Polygon geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawPolygonGeometry =
    +    function(polygonGeometry, feature) {
    +};
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.Feature} feature Feature.
    + */
    +ol.render.IVectorContext.prototype.drawText =
    +    function(flatCoordinates, offset, end, stride, geometry, feature) {
    +};
    +
    +
    +/**
    + * @param {ol.style.Fill} fillStyle Fill style.
    + * @param {ol.style.Stroke} strokeStyle Stroke style.
    + */
    +ol.render.IVectorContext.prototype.setFillStrokeStyle =
    +    function(fillStyle, strokeStyle) {
    +};
    +
    +
    +/**
    + * @param {ol.style.Image} imageStyle Image style.
    + */
    +ol.render.IVectorContext.prototype.setImageStyle = function(imageStyle) {
    +};
    +
    +
    +/**
    + * @param {ol.style.Text} textStyle Text style.
    + */
    +ol.render.IVectorContext.prototype.setTextStyle = function(textStyle) {
    +};
    diff --git a/mapbox-gl/ol/ol/render/renderevent.js b/mapbox-gl/ol/ol/render/renderevent.js
    new file mode 100644
    index 0000000000..568619c91f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/renderevent.js
    @@ -0,0 +1,84 @@
    +goog.provide('ol.render.Event');
    +goog.provide('ol.render.EventType');
    +
    +goog.require('goog.events.Event');
    +goog.require('ol.render.IVectorContext');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.render.EventType = {
    +  /**
    +   * @event ol.render.Event#postcompose
    +   * @api
    +   */
    +  POSTCOMPOSE: 'postcompose',
    +  /**
    +   * @event ol.render.Event#precompose
    +   * @api
    +   */
    +  PRECOMPOSE: 'precompose',
    +  /**
    +   * @event ol.render.Event#render
    +   * @api
    +   */
    +  RENDER: 'render'
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.render.Event}
    + * @param {ol.render.EventType} type Type.
    + * @param {Object=} opt_target Target.
    + * @param {ol.render.IVectorContext=} opt_vectorContext Vector context.
    + * @param {ol.render.IReplayGroup=} opt_replayGroup Replay group.
    + * @param {olx.FrameState=} opt_frameState Frame state.
    + * @param {?CanvasRenderingContext2D=} opt_context Context.
    + * @param {?ol.webgl.Context=} opt_glContext WebGL Context.
    + */
    +ol.render.Event = function(
    +    type, opt_target, opt_vectorContext, opt_replayGroup, opt_frameState,
    +    opt_context, opt_glContext) {
    +
    +  goog.base(this, type, opt_target);
    +
    +  /**
    +   * For canvas, this is an instance of {@link ol.render.canvas.Immediate}.
    +   * @type {ol.render.IVectorContext|undefined}
    +   * @api
    +   */
    +  this.vectorContext = opt_vectorContext;
    +
    +  /**
    +   * @type {ol.render.IReplayGroup|undefined}
    +   */
    +  this.replayGroup = opt_replayGroup;
    +
    +  /**
    +   * @type {olx.FrameState|undefined}
    +   * @api
    +   */
    +  this.frameState = opt_frameState;
    +
    +  /**
    +   * Canvas context. Only available when a Canvas renderer is used, null
    +   * otherwise.
    +   * @type {CanvasRenderingContext2D|null|undefined}
    +   * @api
    +   */
    +  this.context = opt_context;
    +
    +  /**
    +   * WebGL context. Only available when a WebGL renderer is used, null
    +   * otherwise.
    +   * @type {ol.webgl.Context|null|undefined}
    +   * @api
    +   */
    +  this.glContext = opt_glContext;
    +
    +};
    +goog.inherits(ol.render.Event, goog.events.Event);
    diff --git a/mapbox-gl/ol/ol/render/vector.js b/mapbox-gl/ol/ol/render/vector.js
    new file mode 100644
    index 0000000000..4adafac19f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/vector.js
    @@ -0,0 +1,347 @@
    +goog.provide('ol.renderer.vector');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.geom.Circle');
    +goog.require('ol.geom.GeometryCollection');
    +goog.require('ol.geom.LineString');
    +goog.require('ol.geom.MultiLineString');
    +goog.require('ol.geom.MultiPoint');
    +goog.require('ol.geom.MultiPolygon');
    +goog.require('ol.geom.Point');
    +goog.require('ol.geom.Polygon');
    +goog.require('ol.render.IReplayGroup');
    +goog.require('ol.style.ImageState');
    +goog.require('ol.style.Style');
    +
    +
    +/**
    + * @param {ol.Feature} feature1 Feature 1.
    + * @param {ol.Feature} feature2 Feature 2.
    + * @return {number} Order.
    + */
    +ol.renderer.vector.defaultOrder = function(feature1, feature2) {
    +  return goog.getUid(feature1) - goog.getUid(feature2);
    +};
    +
    +
    +/**
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {number} Squared pixel tolerance.
    + */
    +ol.renderer.vector.getSquaredTolerance = function(resolution, pixelRatio) {
    +  var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
    +  return tolerance * tolerance;
    +};
    +
    +
    +/**
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {number} Pixel tolerance.
    + */
    +ol.renderer.vector.getTolerance = function(resolution, pixelRatio) {
    +  return ol.SIMPLIFY_TOLERANCE * resolution / pixelRatio;
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderCircleGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.Circle);
    +  var fillStyle = style.getFill();
    +  var strokeStyle = style.getStroke();
    +  if (!goog.isNull(fillStyle) || !goog.isNull(strokeStyle)) {
    +    var polygonReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.POLYGON);
    +    polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
    +    polygonReplay.drawCircleGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    textReplay.drawText(geometry.getCenter(), 0, 2, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.Feature} feature Feature.
    + * @param {ol.style.Style} style Style.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @param {function(this: T, goog.events.Event)} listener Listener function.
    + * @param {T} thisArg Value to use as `this` when executing `listener`.
    + * @return {boolean} `true` if style is loading.
    + * @template T
    + */
    +ol.renderer.vector.renderFeature = function(
    +    replayGroup, feature, style, squaredTolerance, listener, thisArg) {
    +  var loading = false;
    +  var imageStyle, imageState;
    +  imageStyle = style.getImage();
    +  if (!goog.isNull(imageStyle)) {
    +    imageState = imageStyle.getImageState();
    +    if (imageState == ol.style.ImageState.LOADED ||
    +        imageState == ol.style.ImageState.ERROR) {
    +      imageStyle.unlistenImageChange(listener, thisArg);
    +    } else {
    +      if (imageState == ol.style.ImageState.IDLE) {
    +        imageStyle.load();
    +      }
    +      imageState = imageStyle.getImageState();
    +      goog.asserts.assert(imageState == ol.style.ImageState.LOADING);
    +      imageStyle.listenImageChange(listener, thisArg);
    +      loading = true;
    +    }
    +  }
    +  ol.renderer.vector.renderFeature_(replayGroup, feature, style,
    +      squaredTolerance);
    +  return loading;
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.Feature} feature Feature.
    + * @param {ol.style.Style} style Style.
    + * @param {number} squaredTolerance Squared tolerance.
    + * @private
    + */
    +ol.renderer.vector.renderFeature_ = function(
    +    replayGroup, feature, style, squaredTolerance) {
    +  var geometry = style.getGeometryFunction()(feature);
    +  if (!goog.isDefAndNotNull(geometry)) {
    +    return;
    +  }
    +  var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance);
    +  var geometryRenderer =
    +      ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()];
    +  goog.asserts.assert(goog.isDef(geometryRenderer));
    +  geometryRenderer(replayGroup, simplifiedGeometry, style, feature);
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderGeometryCollectionGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.GeometryCollection);
    +  var geometries = geometry.getGeometriesArray();
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    var geometryRenderer =
    +        ol.renderer.vector.GEOMETRY_RENDERERS_[geometries[i].getType()];
    +    goog.asserts.assert(goog.isDef(geometryRenderer));
    +    geometryRenderer(replayGroup, geometries[i], style, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderLineStringGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
    +  var strokeStyle = style.getStroke();
    +  if (!goog.isNull(strokeStyle)) {
    +    var lineStringReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.LINE_STRING);
    +    lineStringReplay.setFillStrokeStyle(null, strokeStyle);
    +    lineStringReplay.drawLineStringGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    textReplay.drawText(geometry.getFlatMidpoint(), 0, 2, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderMultiLineStringGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString);
    +  var strokeStyle = style.getStroke();
    +  if (!goog.isNull(strokeStyle)) {
    +    var lineStringReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.LINE_STRING);
    +    lineStringReplay.setFillStrokeStyle(null, strokeStyle);
    +    lineStringReplay.drawMultiLineStringGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    var flatMidpointCoordinates = geometry.getFlatMidpoints();
    +    textReplay.drawText(flatMidpointCoordinates, 0,
    +        flatMidpointCoordinates.length, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderMultiPolygonGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon);
    +  var fillStyle = style.getFill();
    +  var strokeStyle = style.getStroke();
    +  if (!goog.isNull(strokeStyle) || !goog.isNull(fillStyle)) {
    +    var polygonReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.POLYGON);
    +    polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
    +    polygonReplay.drawMultiPolygonGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    var flatInteriorPointCoordinates = geometry.getFlatInteriorPoints();
    +    textReplay.drawText(flatInteriorPointCoordinates, 0,
    +        flatInteriorPointCoordinates.length, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderPointGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.Point);
    +  var imageStyle = style.getImage();
    +  if (!goog.isNull(imageStyle)) {
    +    if (imageStyle.getImageState() != ol.style.ImageState.LOADED) {
    +      return;
    +    }
    +    var imageReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.IMAGE);
    +    imageReplay.setImageStyle(imageStyle);
    +    imageReplay.drawPointGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    textReplay.drawText(geometry.getCoordinates(), 0, 2, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderMultiPointGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.MultiPoint);
    +  var imageStyle = style.getImage();
    +  if (!goog.isNull(imageStyle)) {
    +    if (imageStyle.getImageState() != ol.style.ImageState.LOADED) {
    +      return;
    +    }
    +    var imageReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.IMAGE);
    +    imageReplay.setImageStyle(imageStyle);
    +    imageReplay.drawMultiPointGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    var flatCoordinates = geometry.getFlatCoordinates();
    +    textReplay.drawText(flatCoordinates, 0, flatCoordinates.length,
    +        geometry.getStride(), geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.IReplayGroup} replayGroup Replay group.
    + * @param {ol.geom.Geometry} geometry Geometry.
    + * @param {ol.style.Style} style Style.
    + * @param {ol.Feature} feature Feature.
    + * @private
    + */
    +ol.renderer.vector.renderPolygonGeometry_ =
    +    function(replayGroup, geometry, style, feature) {
    +  goog.asserts.assertInstanceof(geometry, ol.geom.Polygon);
    +  var fillStyle = style.getFill();
    +  var strokeStyle = style.getStroke();
    +  if (!goog.isNull(fillStyle) || !goog.isNull(strokeStyle)) {
    +    var polygonReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.POLYGON);
    +    polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
    +    polygonReplay.drawPolygonGeometry(geometry, feature);
    +  }
    +  var textStyle = style.getText();
    +  if (!goog.isNull(textStyle)) {
    +    var textReplay = replayGroup.getReplay(
    +        style.getZIndex(), ol.render.ReplayType.TEXT);
    +    textReplay.setTextStyle(textStyle);
    +    textReplay.drawText(
    +        geometry.getFlatInteriorPoint(), 0, 2, 2, geometry, feature);
    +  }
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<ol.geom.GeometryType,
    + *                function(ol.render.IReplayGroup, ol.geom.Geometry,
    + *                         ol.style.Style, Object)>}
    + */
    +ol.renderer.vector.GEOMETRY_RENDERERS_ = {
    +  'Point': ol.renderer.vector.renderPointGeometry_,
    +  'LineString': ol.renderer.vector.renderLineStringGeometry_,
    +  'Polygon': ol.renderer.vector.renderPolygonGeometry_,
    +  'MultiPoint': ol.renderer.vector.renderMultiPointGeometry_,
    +  'MultiLineString': ol.renderer.vector.renderMultiLineStringGeometry_,
    +  'MultiPolygon': ol.renderer.vector.renderMultiPolygonGeometry_,
    +  'GeometryCollection': ol.renderer.vector.renderGeometryCollectionGeometry_,
    +  'Circle': ol.renderer.vector.renderCircleGeometry_
    +};
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglimagecolor.glsl b/mapbox-gl/ol/ol/render/webgl/webglimagecolor.glsl
    new file mode 100644
    index 0000000000..56e045f402
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglimagecolor.glsl
    @@ -0,0 +1,46 @@
    +//! NAMESPACE=ol.render.webgl.imagereplay.shader.Color
    +//! CLASS=ol.render.webgl.imagereplay.shader.Color
    +
    +
    +//! COMMON
    +varying vec2 v_texCoord;
    +varying float v_opacity;
    +
    +//! VERTEX
    +attribute vec2 a_position;
    +attribute vec2 a_texCoord;
    +attribute vec2 a_offsets;
    +attribute float a_opacity;
    +attribute float a_rotateWithView;
    +
    +uniform mat4 u_projectionMatrix;
    +uniform mat4 u_offsetScaleMatrix;
    +uniform mat4 u_offsetRotateMatrix;
    +
    +void main(void) {
    +  mat4 offsetMatrix = u_offsetScaleMatrix;
    +  if (a_rotateWithView == 1.0) {
    +    offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
    +  }
    +  vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);
    +  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;
    +  v_texCoord = a_texCoord;
    +  v_opacity = a_opacity;
    +}
    +
    +
    +//! FRAGMENT
    +// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp
    +uniform mat4 u_colorMatrix;
    +uniform float u_opacity;
    +uniform sampler2D u_image;
    +
    +void main(void) {
    +  vec4 texColor = texture2D(u_image, v_texCoord);
    +  float alpha = texColor.a * v_opacity * u_opacity;
    +  if (alpha == 0.0) {
    +    discard;
    +  }
    +  gl_FragColor.a = alpha;
    +  gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;
    +}
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglimagecolorshader.js b/mapbox-gl/ol/ol/render/webgl/webglimagecolorshader.js
    new file mode 100644
    index 0000000000..94f1b8a74b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglimagecolorshader.js
    @@ -0,0 +1,153 @@
    +// This file is automatically generated, do not edit
    +goog.provide('ol.render.webgl.imagereplay.shader.Color');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.ColorFragment = function() {
    +  goog.base(this, ol.render.webgl.imagereplay.shader.ColorFragment.SOURCE);
    +};
    +goog.inherits(ol.render.webgl.imagereplay.shader.ColorFragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.ColorFragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\n// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp\nuniform mat4 u_colorMatrix;\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n  vec4 texColor = texture2D(u_image, v_texCoord);\n  float alpha = texColor.a * v_opacity * u_opacity;\n  if (alpha == 0.0) {\n    discard;\n  }\n  gl_FragColor.a = alpha;\n  gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;\n}\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform mat4 k;uniform float l;uniform sampler2D m;void main(void){vec4 texColor=texture2D(m,a);float alpha=texColor.a*b*l;if(alpha==0.0){discard;}gl_FragColor.a=alpha;gl_FragColor.rgb=(k*vec4(texColor.rgb,1.)).rgb;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorFragment.SOURCE = goog.DEBUG ?
    +    ol.render.webgl.imagereplay.shader.ColorFragment.DEBUG_SOURCE :
    +    ol.render.webgl.imagereplay.shader.ColorFragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.ColorVertex = function() {
    +  goog.base(this, ol.render.webgl.imagereplay.shader.ColorVertex.SOURCE);
    +};
    +goog.inherits(ol.render.webgl.imagereplay.shader.ColorVertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.ColorVertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n  mat4 offsetMatrix = u_offsetScaleMatrix;\n  if (a_rotateWithView == 1.0) {\n    offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n  }\n  vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);\n  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n  v_texCoord = a_texCoord;\n  v_opacity = a_opacity;\n}\n\n\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorVertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.ColorVertex.SOURCE = goog.DEBUG ?
    +    ol.render.webgl.imagereplay.shader.ColorVertex.DEBUG_SOURCE :
    +    ol.render.webgl.imagereplay.shader.ColorVertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.Color.Locations = function(gl, program) {
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_colorMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_colorMatrix' : 'k');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_image = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_image' : 'm');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_offsetRotateMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_offsetScaleMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_opacity = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_opacity' : 'l');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_projectionMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_projectionMatrix' : 'h');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_offsets = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_offsets' : 'e');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_opacity = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_opacity' : 'f');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_position = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_position' : 'c');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_rotateWithView = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_rotateWithView' : 'g');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_texCoord = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_texCoord' : 'd');
    +};
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglimagedefault.glsl b/mapbox-gl/ol/ol/render/webgl/webglimagedefault.glsl
    new file mode 100644
    index 0000000000..15d2bce128
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglimagedefault.glsl
    @@ -0,0 +1,44 @@
    +//! NAMESPACE=ol.render.webgl.imagereplay.shader.Default
    +//! CLASS=ol.render.webgl.imagereplay.shader.Default
    +
    +
    +//! COMMON
    +varying vec2 v_texCoord;
    +varying float v_opacity;
    +
    +//! VERTEX
    +attribute vec2 a_position;
    +attribute vec2 a_texCoord;
    +attribute vec2 a_offsets;
    +attribute float a_opacity;
    +attribute float a_rotateWithView;
    +
    +uniform mat4 u_projectionMatrix;
    +uniform mat4 u_offsetScaleMatrix;
    +uniform mat4 u_offsetRotateMatrix;
    +
    +void main(void) {
    +  mat4 offsetMatrix = u_offsetScaleMatrix;
    +  if (a_rotateWithView == 1.0) {
    +    offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;
    +  }
    +  vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);
    +  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;
    +  v_texCoord = a_texCoord;
    +  v_opacity = a_opacity;
    +}
    +
    +
    +//! FRAGMENT
    +uniform float u_opacity;
    +uniform sampler2D u_image;
    +
    +void main(void) {
    +  vec4 texColor = texture2D(u_image, v_texCoord);
    +  gl_FragColor.rgb = texColor.rgb;
    +  float alpha = texColor.a * v_opacity * u_opacity;
    +  if (alpha == 0.0) {
    +    discard;
    +  }
    +  gl_FragColor.a = alpha;
    +}
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglimagedefaultshader.js b/mapbox-gl/ol/ol/render/webgl/webglimagedefaultshader.js
    new file mode 100644
    index 0000000000..7df0fe26c5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglimagedefaultshader.js
    @@ -0,0 +1,147 @@
    +// This file is automatically generated, do not edit
    +goog.provide('ol.render.webgl.imagereplay.shader.Default');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.DefaultFragment = function() {
    +  goog.base(this, ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE);
    +};
    +goog.inherits(ol.render.webgl.imagereplay.shader.DefaultFragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultFragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n  vec4 texColor = texture2D(u_image, v_texCoord);\n  gl_FragColor.rgb = texColor.rgb;\n  float alpha = texColor.a * v_opacity * u_opacity;\n  if (alpha == 0.0) {\n    discard;\n  }\n  gl_FragColor.a = alpha;\n}\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE = goog.DEBUG ?
    +    ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE :
    +    ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.DefaultVertex = function() {
    +  goog.base(this, ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE);
    +};
    +goog.inherits(ol.render.webgl.imagereplay.shader.DefaultVertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultVertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n  mat4 offsetMatrix = u_offsetScaleMatrix;\n  if (a_rotateWithView == 1.0) {\n    offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n  }\n  vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);\n  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n  v_texCoord = a_texCoord;\n  v_opacity = a_opacity;\n}\n\n\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE = goog.DEBUG ?
    +    ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE :
    +    ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +ol.render.webgl.imagereplay.shader.Default.Locations = function(gl, program) {
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_image = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_image' : 'l');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_offsetRotateMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_offsetScaleMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_opacity = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_opacity' : 'k');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_projectionMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_projectionMatrix' : 'h');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_offsets = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_offsets' : 'e');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_opacity = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_opacity' : 'f');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_position = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_position' : 'c');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_rotateWithView = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_rotateWithView' : 'g');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_texCoord = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_texCoord' : 'd');
    +};
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglimmediate.js b/mapbox-gl/ol/ol/render/webgl/webglimmediate.js
    new file mode 100644
    index 0000000000..ca3d481f1a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglimmediate.js
    @@ -0,0 +1,301 @@
    +goog.provide('ol.render.webgl.Immediate');
    +goog.require('goog.array');
    +goog.require('goog.object');
    +goog.require('ol.extent');
    +goog.require('ol.render.IVectorContext');
    +goog.require('ol.render.webgl.ReplayGroup');
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.render.IVectorContext}
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} pixelRatio Pixel ratio.
    + * @struct
    + */
    +ol.render.webgl.Immediate = function(context,
    +    center, resolution, rotation, size, extent, pixelRatio) {
    +
    +  /**
    +   * @private
    +   */
    +  this.context_ = context;
    +
    +  /**
    +   * @private
    +   */
    +  this.center_ = center;
    +
    +  /**
    +   * @private
    +   */
    +  this.extent_ = extent;
    +
    +  /**
    +   * @private
    +   */
    +  this.pixelRatio_ = pixelRatio;
    +
    +  /**
    +   * @private
    +   */
    +  this.size_ = size;
    +
    +  /**
    +   * @private
    +   */
    +  this.rotation_ = rotation;
    +
    +  /**
    +   * @private
    +   */
    +  this.resolution_ = resolution;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Image}
    +   */
    +  this.imageStyle_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string,
    +   *        Array.<function(ol.render.webgl.Immediate)>>}
    +   */
    +  this.callbacksByZIndex_ = {};
    +};
    +
    +
    +/**
    + * FIXME: empty description for jsdoc
    + */
    +ol.render.webgl.Immediate.prototype.flush = function() {
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(this.callbacksByZIndex_), Number);
    +  goog.array.sort(zs);
    +  var i, ii, callbacks, j, jj;
    +  for (i = 0, ii = zs.length; i < ii; ++i) {
    +    callbacks = this.callbacksByZIndex_[zs[i].toString()];
    +    for (j = 0, jj = callbacks.length; j < jj; ++j) {
    +      callbacks[j](this);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {number} zIndex Z index.
    + * @param {function(ol.render.webgl.Immediate)} callback Callback.
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawAsync = function(zIndex, callback) {
    +  var zIndexKey = zIndex.toString();
    +  var callbacks = this.callbacksByZIndex_[zIndexKey];
    +  if (goog.isDef(callbacks)) {
    +    callbacks.push(callback);
    +  } else {
    +    this.callbacksByZIndex_[zIndexKey] = [callback];
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawCircleGeometry =
    +    function(circleGeometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) {
    +  var geometry = style.getGeometryFunction()(feature);
    +  if (!goog.isDefAndNotNull(geometry) ||
    +      !ol.extent.intersects(this.extent_, geometry.getExtent())) {
    +    return;
    +  }
    +  var zIndex = style.getZIndex();
    +  if (!goog.isDef(zIndex)) {
    +    zIndex = 0;
    +  }
    +  this.drawAsync(zIndex, function(render) {
    +    render.setFillStrokeStyle(style.getFill(), style.getStroke());
    +    render.setImageStyle(style.getImage());
    +    render.setTextStyle(style.getText());
    +    var type = geometry.getType();
    +    var renderGeometry = ol.render.webgl.Immediate.GEOMETRY_RENDERERS_[type];
    +    // Do not assert since all kinds of geometries are not handled yet.
    +    // In spite, render what we support.
    +    if (renderGeometry) {
    +      renderGeometry.call(render, geometry, null);
    +    }
    +  });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry =
    +    function(geometryCollectionGeometry, data) {
    +  var geometries = geometryCollectionGeometry.getGeometriesArray();
    +  var renderers = ol.render.webgl.Immediate.GEOMETRY_RENDERERS_;
    +  var i, ii;
    +  for (i = 0, ii = geometries.length; i < ii; ++i) {
    +    var geometry = geometries[i];
    +    var geometryRenderer = renderers[geometry.getType()];
    +    // Do not assert since all kinds of geometries are not handled yet.
    +    // In order to support hierarchies, delegate instead what we can to
    +    // valid renderers.
    +    if (geometryRenderer) {
    +      geometryRenderer.call(this, geometry, data);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawPointGeometry =
    +    function(pointGeometry, data) {
    +  var context = this.context_;
    +  var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
    +  var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE);
    +  replay.setImageStyle(this.imageStyle_);
    +  replay.drawPointGeometry(pointGeometry, data);
    +  replay.finish(context);
    +  // default colors
    +  var opacity = 1;
    +  var brightness = 0;
    +  var contrast = 1;
    +  var hue = 0;
    +  var saturation = 1;
    +  replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
    +      this.size_, this.extent_, this.pixelRatio_, opacity, brightness,
    +      contrast, hue, saturation, {});
    +  replay.getDeleteResourcesFunction(context)();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawLineStringGeometry =
    +    function(lineStringGeometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawMultiLineStringGeometry =
    +    function(multiLineStringGeometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawMultiPointGeometry =
    +    function(multiPointGeometry, data) {
    +  var context = this.context_;
    +  var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_);
    +  var replay = replayGroup.getReplay(0, ol.render.ReplayType.IMAGE);
    +  replay.setImageStyle(this.imageStyle_);
    +  replay.drawMultiPointGeometry(multiPointGeometry, data);
    +  replay.finish(context);
    +  // default colors
    +  var opacity = 1;
    +  var brightness = 0;
    +  var contrast = 1;
    +  var hue = 0;
    +  var saturation = 1;
    +  replay.replay(this.context_, this.center_, this.resolution_, this.rotation_,
    +      this.size_, this.extent_, this.pixelRatio_, opacity, brightness,
    +      contrast, hue, saturation, {});
    +  replay.getDeleteResourcesFunction(context)();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawMultiPolygonGeometry =
    +    function(multiPolygonGeometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawPolygonGeometry =
    +    function(polygonGeometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.drawText =
    +    function(flatCoordinates, offset, end, stride, geometry, data) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.setFillStrokeStyle =
    +    function(fillStyle, strokeStyle) {
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) {
    +  this.imageStyle_ = imageStyle;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.render.webgl.Immediate.prototype.setTextStyle = function(textStyle) {
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<ol.geom.GeometryType,
    + *                function(this: ol.render.webgl.Immediate, ol.geom.Geometry,
    + *                         Object)>}
    + */
    +ol.render.webgl.Immediate.GEOMETRY_RENDERERS_ = {
    +  'Point': ol.render.webgl.Immediate.prototype.drawPointGeometry,
    +  'MultiPoint': ol.render.webgl.Immediate.prototype.drawMultiPointGeometry,
    +  'GeometryCollection':
    +      ol.render.webgl.Immediate.prototype.drawGeometryCollectionGeometry
    +};
    diff --git a/mapbox-gl/ol/ol/render/webgl/webglreplay.js b/mapbox-gl/ol/ol/render/webgl/webglreplay.js
    new file mode 100644
    index 0000000000..3d1f743139
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/render/webgl/webglreplay.js
    @@ -0,0 +1,1276 @@
    +goog.provide('ol.render.webgl.ImageReplay');
    +goog.provide('ol.render.webgl.ReplayGroup');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.color.Matrix');
    +goog.require('ol.extent');
    +goog.require('ol.render.IReplayGroup');
    +goog.require('ol.render.IVectorContext');
    +goog.require('ol.render.webgl.imagereplay.shader.Color');
    +goog.require('ol.render.webgl.imagereplay.shader.Default');
    +goog.require('ol.vec.Mat4');
    +goog.require('ol.webgl.Buffer');
    +goog.require('ol.webgl.Context');
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.render.IVectorContext}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Max extent.
    + * @protected
    + * @struct
    + */
    +ol.render.webgl.ImageReplay = function(tolerance, maxExtent) {
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.anchorX_ = undefined;
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.anchorY_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {ol.color.Matrix}
    +   */
    +  this.colorMatrix_ = new ol.color.Matrix();
    +
    +  /**
    +   * The origin of the coordinate system for the point coordinates sent to
    +   * the GPU. To eliminate jitter caused by precision problems in the GPU
    +   * we use the "Rendering Relative to Eye" technique described in the "3D
    +   * Engine Design for Virtual Globes" book.
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.origin_ = ol.extent.getCenter(maxExtent);
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.groupIndices_ = [];
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.hitDetectionGroupIndices_ = [];
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.height_ = undefined;
    +
    +  /**
    +   * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
    +   * @private
    +   */
    +  this.images_ = [];
    +
    +  /**
    +   * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
    +   * @private
    +   */
    +  this.hitDetectionImages_ = [];
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.imageHeight_ = undefined;
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.imageWidth_ = undefined;
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.indices_ = [];
    +
    +  /**
    +   * @type {ol.webgl.Buffer}
    +   * @private
    +   */
    +  this.indicesBuffer_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.render.webgl.imagereplay.shader.Color.Locations}
    +   */
    +  this.colorLocations_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.render.webgl.imagereplay.shader.Default.Locations}
    +   */
    +  this.defaultLocations_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.opacity_ = undefined;
    +
    +  /**
    +   * @type {!goog.vec.Mat4.Number}
    +   * @private
    +   */
    +  this.offsetRotateMatrix_ = goog.vec.Mat4.createNumberIdentity();
    +
    +  /**
    +   * @type {!goog.vec.Mat4.Number}
    +   * @private
    +   */
    +  this.offsetScaleMatrix_ = goog.vec.Mat4.createNumberIdentity();
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.originX_ = undefined;
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.originY_ = undefined;
    +
    +  /**
    +   * @type {!goog.vec.Mat4.Number}
    +   * @private
    +   */
    +  this.projectionMatrix_ = goog.vec.Mat4.createNumberIdentity();
    +
    +  /**
    +   * @private
    +   * @type {boolean|undefined}
    +   */
    +  this.rotateWithView_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.rotation_ = undefined;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.scale_ = undefined;
    +
    +  /**
    +   * @type {Array.<WebGLTexture>}
    +   * @private
    +   */
    +  this.textures_ = [];
    +
    +  /**
    +   * @type {Array.<WebGLTexture>}
    +   * @private
    +   */
    +  this.hitDetectionTextures_ = [];
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.vertices_ = [];
    +
    +  /**
    +   * @type {ol.webgl.Buffer}
    +   * @private
    +   */
    +  this.verticesBuffer_ = null;
    +
    +  /**
    +   * Start index per feature (the index).
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.startIndices_ = [];
    +
    +  /**
    +   * Start index per feature (the feature).
    +   * @type {Array.<ol.Feature>}
    +   * @private
    +   */
    +  this.startIndicesFeature_ = [];
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.width_ = undefined;
    +
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Context} context WebGL context.
    + * @return {function()} Delete resources function.
    + */
    +ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction =
    +    function(context) {
    +  // We only delete our stuff here. The shaders and the program may
    +  // be used by other ImageReplay instances (for other layers). And
    +  // they will be deleted when disposing of the ol.webgl.Context
    +  // object.
    +  goog.asserts.assert(!goog.isNull(this.verticesBuffer_));
    +  goog.asserts.assert(!goog.isNull(this.indicesBuffer_));
    +  var verticesBuffer = this.verticesBuffer_;
    +  var indicesBuffer = this.indicesBuffer_;
    +  var textures = this.textures_;
    +  var hitDetectionTextures = this.hitDetectionTextures_;
    +  var gl = context.getGL();
    +  return function() {
    +    if (!gl.isContextLost()) {
    +      var i, ii;
    +      for (i = 0, ii = textures.length; i < ii; ++i) {
    +        gl.deleteTexture(textures[i]);
    +      }
    +      for (i = 0, ii = hitDetectionTextures.length; i < ii; ++i) {
    +        gl.deleteTexture(hitDetectionTextures[i]);
    +      }
    +    }
    +    context.deleteBuffer(verticesBuffer);
    +    context.deleteBuffer(indicesBuffer);
    +  };
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawAsync = goog.abstractMethod;
    +
    +
    +/**
    + * @param {Array.<number>} flatCoordinates Flat coordinates.
    + * @param {number} offset Offset.
    + * @param {number} end End.
    + * @param {number} stride Stride.
    + * @return {number} My end.
    + * @private
    + */
    +ol.render.webgl.ImageReplay.prototype.drawCoordinates_ =
    +    function(flatCoordinates, offset, end, stride) {
    +  goog.asserts.assert(goog.isDef(this.anchorX_));
    +  goog.asserts.assert(goog.isDef(this.anchorY_));
    +  goog.asserts.assert(goog.isDef(this.height_));
    +  goog.asserts.assert(goog.isDef(this.imageHeight_));
    +  goog.asserts.assert(goog.isDef(this.imageWidth_));
    +  goog.asserts.assert(goog.isDef(this.opacity_));
    +  goog.asserts.assert(goog.isDef(this.originX_));
    +  goog.asserts.assert(goog.isDef(this.originY_));
    +  goog.asserts.assert(goog.isDef(this.rotateWithView_));
    +  goog.asserts.assert(goog.isDef(this.rotation_));
    +  goog.asserts.assert(goog.isDef(this.scale_));
    +  goog.asserts.assert(goog.isDef(this.width_));
    +  var anchorX = this.anchorX_;
    +  var anchorY = this.anchorY_;
    +  var height = this.height_;
    +  var imageHeight = this.imageHeight_;
    +  var imageWidth = this.imageWidth_;
    +  var opacity = this.opacity_;
    +  var originX = this.originX_;
    +  var originY = this.originY_;
    +  var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0;
    +  var rotation = this.rotation_;
    +  var scale = this.scale_;
    +  var width = this.width_;
    +  var cos = Math.cos(rotation);
    +  var sin = Math.sin(rotation);
    +  var numIndices = this.indices_.length;
    +  var numVertices = this.vertices_.length;
    +  var i, n, offsetX, offsetY, x, y;
    +  for (i = offset; i < end; i += stride) {
    +    x = flatCoordinates[i] - this.origin_[0];
    +    y = flatCoordinates[i + 1] - this.origin_[1];
    +
    +    // There are 4 vertices per [x, y] point, one for each corner of the
    +    // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if
    +    // WebGL supported Geometry Shaders (which can emit new vertices), but that
    +    // is not currently the case.
    +    //
    +    // And each vertex includes 8 values: the x and y coordinates, the x and
    +    // y offsets used to calculate the position of the corner, the u and
    +    // v texture coordinates for the corner, the opacity, and whether the
    +    // the image should be rotated with the view (rotateWithView).
    +
    +    n = numVertices / 8;
    +
    +    // bottom-left corner
    +    offsetX = -scale * anchorX;
    +    offsetY = -scale * (height - anchorY);
    +    this.vertices_[numVertices++] = x;
    +    this.vertices_[numVertices++] = y;
    +    this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
    +    this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
    +    this.vertices_[numVertices++] = originX / imageWidth;
    +    this.vertices_[numVertices++] = (originY + height) / imageHeight;
    +    this.vertices_[numVertices++] = opacity;
    +    this.vertices_[numVertices++] = rotateWithView;
    +
    +    // bottom-right corner
    +    offsetX = scale * (width - anchorX);
    +    offsetY = -scale * (height - anchorY);
    +    this.vertices_[numVertices++] = x;
    +    this.vertices_[numVertices++] = y;
    +    this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
    +    this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
    +    this.vertices_[numVertices++] = (originX + width) / imageWidth;
    +    this.vertices_[numVertices++] = (originY + height) / imageHeight;
    +    this.vertices_[numVertices++] = opacity;
    +    this.vertices_[numVertices++] = rotateWithView;
    +
    +    // top-right corner
    +    offsetX = scale * (width - anchorX);
    +    offsetY = scale * anchorY;
    +    this.vertices_[numVertices++] = x;
    +    this.vertices_[numVertices++] = y;
    +    this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
    +    this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
    +    this.vertices_[numVertices++] = (originX + width) / imageWidth;
    +    this.vertices_[numVertices++] = originY / imageHeight;
    +    this.vertices_[numVertices++] = opacity;
    +    this.vertices_[numVertices++] = rotateWithView;
    +
    +    // top-left corner
    +    offsetX = -scale * anchorX;
    +    offsetY = scale * anchorY;
    +    this.vertices_[numVertices++] = x;
    +    this.vertices_[numVertices++] = y;
    +    this.vertices_[numVertices++] = offsetX * cos - offsetY * sin;
    +    this.vertices_[numVertices++] = offsetX * sin + offsetY * cos;
    +    this.vertices_[numVertices++] = originX / imageWidth;
    +    this.vertices_[numVertices++] = originY / imageHeight;
    +    this.vertices_[numVertices++] = opacity;
    +    this.vertices_[numVertices++] = rotateWithView;
    +
    +    this.indices_[numIndices++] = n;
    +    this.indices_[numIndices++] = n + 1;
    +    this.indices_[numIndices++] = n + 2;
    +    this.indices_[numIndices++] = n;
    +    this.indices_[numIndices++] = n + 2;
    +    this.indices_[numIndices++] = n + 3;
    +  }
    +
    +  return numVertices;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawCircleGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawFeature = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawGeometryCollectionGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawLineStringGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawMultiLineStringGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawMultiPointGeometry =
    +    function(multiPointGeometry, feature) {
    +  this.startIndices_.push(this.indices_.length);
    +  this.startIndicesFeature_.push(feature);
    +  var flatCoordinates = multiPointGeometry.getFlatCoordinates();
    +  var stride = multiPointGeometry.getStride();
    +  this.drawCoordinates_(
    +      flatCoordinates, 0, flatCoordinates.length, stride);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawMultiPolygonGeometry =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawPointGeometry =
    +    function(pointGeometry, feature) {
    +  this.startIndices_.push(this.indices_.length);
    +  this.startIndicesFeature_.push(feature);
    +  var flatCoordinates = pointGeometry.getFlatCoordinates();
    +  var stride = pointGeometry.getStride();
    +  this.drawCoordinates_(
    +      flatCoordinates, 0, flatCoordinates.length, stride);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawPolygonGeometry = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.drawText = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.webgl.Context} context Context.
    + */
    +ol.render.webgl.ImageReplay.prototype.finish = function(context) {
    +  var gl = context.getGL();
    +
    +  this.groupIndices_.push(this.indices_.length);
    +  goog.asserts.assert(this.images_.length === this.groupIndices_.length);
    +  this.hitDetectionGroupIndices_.push(this.indices_.length);
    +  goog.asserts.assert(this.hitDetectionImages_.length ===
    +      this.hitDetectionGroupIndices_.length);
    +
    +  // create, bind, and populate the vertices buffer
    +  this.verticesBuffer_ = new ol.webgl.Buffer(this.vertices_);
    +  context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_);
    +
    +  var indices = this.indices_;
    +  var bits = context.hasOESElementIndexUint ? 32 : 16;
    +  goog.asserts.assert(indices[indices.length - 1] < Math.pow(2, bits),
    +      'Too large element index detected [%s] (OES_element_index_uint "%s")',
    +      indices[indices.length - 1], context.hasOESElementIndexUint);
    +
    +  // create, bind, and populate the indices buffer
    +  this.indicesBuffer_ = new ol.webgl.Buffer(indices);
    +  context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
    +
    +  // create textures
    +  /** @type {Object.<string, WebGLTexture>} */
    +  var texturePerImage = {};
    +
    +  this.createTextures_(this.textures_, this.images_, texturePerImage, gl);
    +  goog.asserts.assert(this.textures_.length === this.groupIndices_.length);
    +
    +  this.createTextures_(this.hitDetectionTextures_, this.hitDetectionImages_,
    +      texturePerImage, gl);
    +  goog.asserts.assert(this.hitDetectionTextures_.length ===
    +      this.hitDetectionGroupIndices_.length);
    +
    +  this.anchorX_ = undefined;
    +  this.anchorY_ = undefined;
    +  this.height_ = undefined;
    +  this.images_ = null;
    +  this.hitDetectionImages_ = null;
    +  this.imageHeight_ = undefined;
    +  this.imageWidth_ = undefined;
    +  this.indices_ = null;
    +  this.opacity_ = undefined;
    +  this.originX_ = undefined;
    +  this.originY_ = undefined;
    +  this.rotateWithView_ = undefined;
    +  this.rotation_ = undefined;
    +  this.scale_ = undefined;
    +  this.vertices_ = null;
    +  this.width_ = undefined;
    +};
    +
    +
    +/**
    + * @private
    + * @param {Array.<WebGLTexture>} textures Textures.
    + * @param {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} images
    + *    Images.
    + * @param {Object.<string, WebGLTexture>} texturePerImage Texture cache.
    + * @param {WebGLRenderingContext} gl Gl.
    + */
    +ol.render.webgl.ImageReplay.prototype.createTextures_ =
    +    function(textures, images, texturePerImage, gl) {
    +  goog.asserts.assert(textures.length === 0);
    +
    +  var texture, image, uid, i;
    +  var ii = images.length;
    +  for (i = 0; i < ii; ++i) {
    +    image = images[i];
    +
    +    uid = goog.getUid(image).toString();
    +    if (goog.object.containsKey(texturePerImage, uid)) {
    +      texture = texturePerImage[uid];
    +    } else {
    +      texture = ol.webgl.Context.createTexture(
    +          gl, image, goog.webgl.CLAMP_TO_EDGE, goog.webgl.CLAMP_TO_EDGE);
    +      texturePerImage[uid] = texture;
    +    }
    +    textures[i] = texture;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {number} opacity Global opacity.
    + * @param {number} brightness Global brightness.
    + * @param {number} contrast Global contrast.
    + * @param {number} hue Global hue.
    + * @param {number} saturation Global saturation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
    + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
    + *  this extent are checked.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ImageReplay.prototype.replay = function(context,
    +    center, resolution, rotation, size, pixelRatio,
    +    opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
    +    featureCallback, oneByOne, opt_hitExtent) {
    +  var gl = context.getGL();
    +
    +  // bind the vertices buffer
    +  goog.asserts.assert(!goog.isNull(this.verticesBuffer_));
    +  context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_);
    +
    +  // bind the indices buffer
    +  goog.asserts.assert(!goog.isNull(this.indicesBuffer_));
    +  context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
    +
    +  var useColor = brightness || contrast != 1 || hue || saturation != 1;
    +
    +  // get the program
    +  var fragmentShader, vertexShader;
    +  if (useColor) {
    +    fragmentShader =
    +        ol.render.webgl.imagereplay.shader.ColorFragment.getInstance();
    +    vertexShader =
    +        ol.render.webgl.imagereplay.shader.ColorVertex.getInstance();
    +  } else {
    +    fragmentShader =
    +        ol.render.webgl.imagereplay.shader.DefaultFragment.getInstance();
    +    vertexShader =
    +        ol.render.webgl.imagereplay.shader.DefaultVertex.getInstance();
    +  }
    +  var program = context.getProgram(fragmentShader, vertexShader);
    +
    +  // get the locations
    +  var locations;
    +  if (useColor) {
    +    if (goog.isNull(this.colorLocations_)) {
    +      locations =
    +          new ol.render.webgl.imagereplay.shader.Color.Locations(gl, program);
    +      this.colorLocations_ = locations;
    +    } else {
    +      locations = this.colorLocations_;
    +    }
    +  } else {
    +    if (goog.isNull(this.defaultLocations_)) {
    +      locations =
    +          new ol.render.webgl.imagereplay.shader.Default.Locations(gl, program);
    +      this.defaultLocations_ = locations;
    +    } else {
    +      locations = this.defaultLocations_;
    +    }
    +  }
    +
    +  // use the program (FIXME: use the return value)
    +  context.useProgram(program);
    +
    +  // enable the vertex attrib arrays
    +  gl.enableVertexAttribArray(locations.a_position);
    +  gl.vertexAttribPointer(locations.a_position, 2, goog.webgl.FLOAT,
    +      false, 32, 0);
    +
    +  gl.enableVertexAttribArray(locations.a_offsets);
    +  gl.vertexAttribPointer(locations.a_offsets, 2, goog.webgl.FLOAT,
    +      false, 32, 8);
    +
    +  gl.enableVertexAttribArray(locations.a_texCoord);
    +  gl.vertexAttribPointer(locations.a_texCoord, 2, goog.webgl.FLOAT,
    +      false, 32, 16);
    +
    +  gl.enableVertexAttribArray(locations.a_opacity);
    +  gl.vertexAttribPointer(locations.a_opacity, 1, goog.webgl.FLOAT,
    +      false, 32, 24);
    +
    +  gl.enableVertexAttribArray(locations.a_rotateWithView);
    +  gl.vertexAttribPointer(locations.a_rotateWithView, 1, goog.webgl.FLOAT,
    +      false, 32, 28);
    +
    +  // set the "uniform" values
    +  var projectionMatrix = this.projectionMatrix_;
    +  ol.vec.Mat4.makeTransform2D(projectionMatrix,
    +      0.0, 0.0,
    +      2 / (resolution * size[0]),
    +      2 / (resolution * size[1]),
    +      -rotation,
    +      -(center[0] - this.origin_[0]), -(center[1] - this.origin_[1]));
    +
    +  var offsetScaleMatrix = this.offsetScaleMatrix_;
    +  goog.vec.Mat4.makeScale(offsetScaleMatrix, 2 / size[0], 2 / size[1], 1);
    +
    +  var offsetRotateMatrix = this.offsetRotateMatrix_;
    +  goog.vec.Mat4.makeIdentity(offsetRotateMatrix);
    +  if (rotation !== 0) {
    +    goog.vec.Mat4.rotateZ(offsetRotateMatrix, -rotation);
    +  }
    +
    +  gl.uniformMatrix4fv(locations.u_projectionMatrix, false, projectionMatrix);
    +  gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, offsetScaleMatrix);
    +  gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false,
    +      offsetRotateMatrix);
    +  gl.uniform1f(locations.u_opacity, opacity);
    +  if (useColor) {
    +    gl.uniformMatrix4fv(locations.u_colorMatrix, false,
    +        this.colorMatrix_.getMatrix(brightness, contrast, hue, saturation));
    +  }
    +
    +  // draw!
    +  var result;
    +  if (!goog.isDef(featureCallback)) {
    +    this.drawReplay_(gl, context, skippedFeaturesHash,
    +        this.textures_, this.groupIndices_);
    +  } else {
    +    // draw feature by feature for the hit-detection
    +    result = this.drawHitDetectionReplay_(gl, context, skippedFeaturesHash,
    +        featureCallback, oneByOne, opt_hitExtent);
    +  }
    +
    +  // disable the vertex attrib arrays
    +  gl.disableVertexAttribArray(locations.a_position);
    +  gl.disableVertexAttribArray(locations.a_offsets);
    +  gl.disableVertexAttribArray(locations.a_texCoord);
    +  gl.disableVertexAttribArray(locations.a_opacity);
    +  gl.disableVertexAttribArray(locations.a_rotateWithView);
    +
    +  return result;
    +};
    +
    +
    +/**
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {ol.webgl.Context} context Context.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {Array.<WebGLTexture>} textures Textures.
    + * @param {Array.<number>} groupIndices Texture group indices.
    + */
    +ol.render.webgl.ImageReplay.prototype.drawReplay_ =
    +    function(gl, context, skippedFeaturesHash, textures, groupIndices) {
    +  goog.asserts.assert(textures.length === groupIndices.length);
    +  var elementType = context.hasOESElementIndexUint ?
    +      goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT;
    +  var elementSize = context.hasOESElementIndexUint ? 4 : 2;
    +
    +  if (!goog.object.isEmpty(skippedFeaturesHash)) {
    +    this.drawReplaySkipping_(
    +        gl, skippedFeaturesHash, textures, groupIndices,
    +        elementType, elementSize);
    +  } else {
    +    var i, ii, start;
    +    for (i = 0, ii = textures.length, start = 0; i < ii; ++i) {
    +      gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]);
    +      var end = groupIndices[i];
    +      this.drawElements_(gl, start, end, elementType, elementSize);
    +      start = end;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Draw the replay while paying attention to skipped features.
    + *
    + * This functions creates groups of features that can be drawn to together,
    + * so that the number of `drawElements` calls is minimized.
    + *
    + * For example given the following texture groups:
    + *
    + *    Group 1: A B C
    + *    Group 2: D [E] F G
    + *
    + * If feature E should be skipped, the following `drawElements` calls will be
    + * made:
    + *
    + *    drawElements with feature A, B and C
    + *    drawElements with feature D
    + *    drawElements with feature F and G
    + *
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {Array.<WebGLTexture>} textures Textures.
    + * @param {Array.<number>} groupIndices Texture group indices.
    + * @param {number} elementType Element type.
    + * @param {number} elementSize Element Size.
    + */
    +ol.render.webgl.ImageReplay.prototype.drawReplaySkipping_ =
    +    function(gl, skippedFeaturesHash, textures, groupIndices,
    +    elementType, elementSize) {
    +  var featureIndex = 0;
    +
    +  var i, ii;
    +  for (i = 0, ii = textures.length; i < ii; ++i) {
    +    gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]);
    +    var groupStart = (i > 0) ? groupIndices[i - 1] : 0;
    +    var groupEnd = groupIndices[i];
    +
    +    var start = groupStart;
    +    var end = groupStart;
    +    while (featureIndex < this.startIndices_.length &&
    +        this.startIndices_[featureIndex] <= groupEnd) {
    +      var feature = this.startIndicesFeature_[featureIndex];
    +
    +      var featureUid = goog.getUid(feature).toString();
    +      if (goog.isDef(skippedFeaturesHash[featureUid])) {
    +        // feature should be skipped
    +        if (start !== end) {
    +          // draw the features so far
    +          this.drawElements_(gl, start, end, elementType, elementSize);
    +        }
    +        // continue with the next feature
    +        start = (featureIndex === this.startIndices_.length - 1) ?
    +            groupEnd : this.startIndices_[featureIndex + 1];
    +        end = start;
    +      } else {
    +        // the feature is not skipped, augment the end index
    +        end = (featureIndex === this.startIndices_.length - 1) ?
    +            groupEnd : this.startIndices_[featureIndex + 1];
    +      }
    +      featureIndex++;
    +    }
    +
    +    if (start !== end) {
    +      // draw the remaining features (in case there was no skipped feature
    +      // in this texture group, all features of a group are drawn together)
    +      this.drawElements_(gl, start, end, elementType, elementSize);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {number} start Start index.
    + * @param {number} end End index.
    + * @param {number} elementType Element type.
    + * @param {number} elementSize Element Size.
    + */
    +ol.render.webgl.ImageReplay.prototype.drawElements_ = function(
    +    gl, start, end, elementType, elementSize) {
    +  var numItems = end - start;
    +  var offsetInBytes = start * elementSize;
    +  gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes);
    +};
    +
    +
    +/**
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {ol.webgl.Context} context Context.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
    + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
    + *  this extent are checked.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ =
    +    function(gl, context, skippedFeaturesHash, featureCallback, oneByOne,
    +    opt_hitExtent) {
    +  if (!oneByOne) {
    +    // draw all hit-detection features in "once" (by texture group)
    +    return this.drawHitDetectionReplayAll_(gl, context,
    +        skippedFeaturesHash, featureCallback);
    +  } else {
    +    // draw hit-detection features one by one
    +    return this.drawHitDetectionReplayOneByOne_(gl, context,
    +        skippedFeaturesHash, featureCallback, opt_hitExtent);
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {ol.webgl.Context} context Context.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayAll_ =
    +    function(gl, context, skippedFeaturesHash, featureCallback) {
    +  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    +  this.drawReplay_(gl, context, skippedFeaturesHash,
    +      this.hitDetectionTextures_, this.hitDetectionGroupIndices_);
    +
    +  var result = featureCallback(null);
    +  if (result) {
    +    return result;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {WebGLRenderingContext} gl gl.
    + * @param {ol.webgl.Context} context Context.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
    + *  this extent are checked.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne_ =
    +    function(gl, context, skippedFeaturesHash, featureCallback,
    +    opt_hitExtent) {
    +  goog.asserts.assert(this.hitDetectionTextures_.length ===
    +      this.hitDetectionGroupIndices_.length);
    +  var elementType = context.hasOESElementIndexUint ?
    +      goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT;
    +  var elementSize = context.hasOESElementIndexUint ? 4 : 2;
    +
    +  var i, groupStart, start, end, feature, featureUid;
    +  var featureIndex = this.startIndices_.length - 1;
    +  for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) {
    +    gl.bindTexture(goog.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]);
    +    groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0;
    +    end = this.hitDetectionGroupIndices_[i];
    +
    +    // draw all features for this texture group
    +    while (featureIndex >= 0 &&
    +        this.startIndices_[featureIndex] >= groupStart) {
    +      start = this.startIndices_[featureIndex];
    +      feature = this.startIndicesFeature_[featureIndex];
    +      featureUid = goog.getUid(feature).toString();
    +
    +      if (!goog.isDef(skippedFeaturesHash[featureUid]) &&
    +          (!goog.isDef(opt_hitExtent) || ol.extent.intersects(
    +              opt_hitExtent, feature.getGeometry().getExtent()))) {
    +        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    +        this.drawElements_(gl, start, end, elementType, elementSize);
    +
    +        var result = featureCallback(feature);
    +        if (result) {
    +          return result;
    +        }
    +      }
    +
    +      end = start;
    +      featureIndex--;
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.setFillStrokeStyle = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) {
    +  var anchor = imageStyle.getAnchor();
    +  goog.asserts.assert(!goog.isNull(anchor));
    +  var image = imageStyle.getImage(1);
    +  goog.asserts.assert(!goog.isNull(image));
    +  var imageSize = imageStyle.getImageSize();
    +  goog.asserts.assert(!goog.isNull(imageSize));
    +  var hitDetectionImage = imageStyle.getHitDetectionImage(1);
    +  goog.asserts.assert(!goog.isNull(hitDetectionImage));
    +  var hitDetectionImageSize = imageStyle.getHitDetectionImageSize();
    +  goog.asserts.assert(!goog.isNull(hitDetectionImageSize));
    +  var opacity = imageStyle.getOpacity();
    +  goog.asserts.assert(goog.isDef(opacity));
    +  var origin = imageStyle.getOrigin();
    +  goog.asserts.assert(!goog.isNull(origin));
    +  var rotateWithView = imageStyle.getRotateWithView();
    +  goog.asserts.assert(goog.isDef(rotateWithView));
    +  var rotation = imageStyle.getRotation();
    +  goog.asserts.assert(goog.isDef(rotation));
    +  var size = imageStyle.getSize();
    +  goog.asserts.assert(!goog.isNull(size));
    +  var scale = imageStyle.getScale();
    +  goog.asserts.assert(goog.isDef(scale));
    +
    +  var currentImage;
    +  if (this.images_.length === 0) {
    +    this.images_.push(image);
    +  } else {
    +    currentImage = this.images_[this.images_.length - 1];
    +    if (goog.getUid(currentImage) != goog.getUid(image)) {
    +      this.groupIndices_.push(this.indices_.length);
    +      goog.asserts.assert(this.groupIndices_.length === this.images_.length);
    +      this.images_.push(image);
    +    }
    +  }
    +
    +  if (this.hitDetectionImages_.length === 0) {
    +    this.hitDetectionImages_.push(hitDetectionImage);
    +  } else {
    +    currentImage =
    +        this.hitDetectionImages_[this.hitDetectionImages_.length - 1];
    +    if (goog.getUid(currentImage) != goog.getUid(hitDetectionImage)) {
    +      this.hitDetectionGroupIndices_.push(this.indices_.length);
    +      goog.asserts.assert(this.hitDetectionGroupIndices_.length ===
    +          this.hitDetectionImages_.length);
    +      this.hitDetectionImages_.push(hitDetectionImage);
    +    }
    +  }
    +
    +  this.anchorX_ = anchor[0];
    +  this.anchorY_ = anchor[1];
    +  this.height_ = size[1];
    +  this.imageHeight_ = imageSize[1];
    +  this.imageWidth_ = imageSize[0];
    +  this.opacity_ = opacity;
    +  this.originX_ = origin[0];
    +  this.originY_ = origin[1];
    +  this.rotation_ = rotation;
    +  this.rotateWithView_ = rotateWithView;
    +  this.scale_ = scale;
    +  this.width_ = size[0];
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ImageReplay.prototype.setTextStyle = goog.abstractMethod;
    +
    +
    +
    +/**
    + * @constructor
    + * @implements {ol.render.IReplayGroup}
    + * @param {number} tolerance Tolerance.
    + * @param {ol.Extent} maxExtent Max extent.
    + * @param {number=} opt_renderBuffer Render buffer.
    + * @struct
    + */
    +ol.render.webgl.ReplayGroup = function(
    +    tolerance, maxExtent, opt_renderBuffer) {
    +
    +  /**
    +   * @type {ol.Extent}
    +   * @private
    +   */
    +  this.maxExtent_ = maxExtent;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.tolerance_ = tolerance;
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.renderBuffer_ = opt_renderBuffer;
    +
    +  /**
    +   * ImageReplay only is supported at this point.
    +   * @type {Object.<ol.render.ReplayType, ol.render.webgl.ImageReplay>}
    +   * @private
    +   */
    +  this.replays_ = {};
    +
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Context} context WebGL context.
    + * @return {function()} Delete resources function.
    + */
    +ol.render.webgl.ReplayGroup.prototype.getDeleteResourcesFunction =
    +    function(context) {
    +  var functions = [];
    +  var replayKey;
    +  for (replayKey in this.replays_) {
    +    functions.push(
    +        this.replays_[replayKey].getDeleteResourcesFunction(context));
    +  }
    +  return goog.functions.sequence.apply(null, functions);
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Context} context Context.
    + */
    +ol.render.webgl.ReplayGroup.prototype.finish = function(context) {
    +  var replayKey;
    +  for (replayKey in this.replays_) {
    +    this.replays_[replayKey].finish(context);
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ReplayGroup.prototype.getReplay =
    +    function(zIndex, replayType) {
    +  var replay = this.replays_[replayType];
    +  if (!goog.isDef(replay)) {
    +    var constructor = ol.render.webgl.BATCH_CONSTRUCTORS_[replayType];
    +    goog.asserts.assert(goog.isDef(constructor));
    +    replay = new constructor(this.tolerance_, this.maxExtent_);
    +    this.replays_[replayType] = replay;
    +  }
    +  return replay;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.render.webgl.ReplayGroup.prototype.isEmpty = function() {
    +  return goog.object.isEmpty(this.replays_);
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {number} opacity Global opacity.
    + * @param {number} brightness Global brightness.
    + * @param {number} contrast Global contrast.
    + * @param {number} hue Global hue.
    + * @param {number} saturation Global saturation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + */
    +ol.render.webgl.ReplayGroup.prototype.replay = function(context,
    +    center, resolution, rotation, size, pixelRatio,
    +    opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
    +  var i, ii, replay, result;
    +  for (i = 0, ii = ol.render.REPLAY_ORDER.length; i < ii; ++i) {
    +    replay = this.replays_[ol.render.REPLAY_ORDER[i]];
    +    if (goog.isDef(replay)) {
    +      replay.replay(context,
    +          center, resolution, rotation, size, pixelRatio,
    +          opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
    +          undefined, false);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {number} opacity Global opacity.
    + * @param {number} brightness Global brightness.
    + * @param {number} contrast Global contrast.
    + * @param {number} hue Global hue.
    + * @param {number} saturation Global saturation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} featureCallback Feature callback.
    + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion.
    + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting
    + *  this extent are checked.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context,
    +    center, resolution, rotation, size, pixelRatio,
    +    opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
    +    featureCallback, oneByOne, opt_hitExtent) {
    +  var i, replay, result;
    +  for (i = ol.render.REPLAY_ORDER.length - 1; i >= 0; --i) {
    +    replay = this.replays_[ol.render.REPLAY_ORDER[i]];
    +    if (goog.isDef(replay)) {
    +      result = replay.replay(context,
    +          center, resolution, rotation, size, pixelRatio,
    +          opacity, brightness, contrast, hue, saturation,
    +          skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {number} opacity Global opacity.
    + * @param {number} brightness Global brightness.
    + * @param {number} contrast Global contrast.
    + * @param {number} hue Global hue.
    + * @param {number} saturation Global saturation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @param {function(ol.Feature): T|undefined} callback Feature callback.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.render.webgl.ReplayGroup.prototype.forEachFeatureAtCoordinate = function(
    +    coordinate, context, center, resolution, rotation, size, pixelRatio,
    +    opacity, brightness, contrast, hue, saturation, skippedFeaturesHash,
    +    callback) {
    +  var gl = context.getGL();
    +  gl.bindFramebuffer(
    +      gl.FRAMEBUFFER, context.getHitDetectionFramebuffer());
    +
    +
    +  /**
    +   * @type {ol.Extent}
    +   */
    +  var hitExtent;
    +  if (goog.isDef(this.renderBuffer_)) {
    +    // build an extent around the coordinate, so that only features that
    +    // intersect this extent are checked
    +    hitExtent = ol.extent.buffer(
    +        ol.extent.createOrUpdateFromCoordinate(coordinate),
    +        resolution * this.renderBuffer_);
    +  }
    +
    +  return this.replayHitDetection_(context,
    +      coordinate, resolution, rotation, ol.render.webgl.HIT_DETECTION_SIZE_,
    +      pixelRatio, opacity, brightness, contrast, hue, saturation,
    +      skippedFeaturesHash,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {?} Callback result.
    +       */
    +      function(feature) {
    +        var imageData = new Uint8Array(4);
    +        gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
    +
    +        if (imageData[3] > 0) {
    +          var result = callback(feature);
    +          if (result) {
    +            return result;
    +          }
    +        }
    +      }, true, hitExtent);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.webgl.Context} context Context.
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {number} opacity Global opacity.
    + * @param {number} brightness Global brightness.
    + * @param {number} contrast Global contrast.
    + * @param {number} hue Global hue.
    + * @param {number} saturation Global saturation.
    + * @param {Object} skippedFeaturesHash Ids of features to skip.
    + * @return {boolean} Is there a feature at the given coordinate?
    + */
    +ol.render.webgl.ReplayGroup.prototype.hasFeatureAtCoordinate = function(
    +    coordinate, context, center, resolution, rotation, size, pixelRatio,
    +    opacity, brightness, contrast, hue, saturation, skippedFeaturesHash) {
    +  var gl = context.getGL();
    +  gl.bindFramebuffer(
    +      gl.FRAMEBUFFER, context.getHitDetectionFramebuffer());
    +
    +  var hasFeature = this.replayHitDetection_(context,
    +      coordinate, resolution, rotation, ol.render.webgl.HIT_DETECTION_SIZE_,
    +      pixelRatio, opacity, brightness, contrast, hue, saturation,
    +      skippedFeaturesHash,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {boolean} Is there a feature?
    +       */
    +      function(feature) {
    +        var imageData = new Uint8Array(4);
    +        gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData);
    +        return imageData[3] > 0;
    +      }, false);
    +
    +  return goog.isDef(hasFeature);
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Object.<ol.render.ReplayType,
    + *                function(new: ol.render.webgl.ImageReplay, number,
    + *                ol.Extent)>}
    + */
    +ol.render.webgl.BATCH_CONSTRUCTORS_ = {
    +  'Image': ol.render.webgl.ImageReplay
    +};
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<number>}
    + */
    +ol.render.webgl.HIT_DETECTION_SIZE_ = [1, 1];
    diff --git a/mapbox-gl/ol/ol/renderer/canvas/canvasimagelayerrenderer.js b/mapbox-gl/ol/ol/renderer/canvas/canvasimagelayerrenderer.js
    new file mode 100644
    index 0000000000..d5cb82c7a4
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/canvas/canvasimagelayerrenderer.js
    @@ -0,0 +1,208 @@
    +goog.provide('ol.renderer.canvas.ImageLayer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.ImageBase');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj');
    +goog.require('ol.renderer.canvas.Layer');
    +goog.require('ol.source.ImageVector');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.canvas.Layer}
    + * @param {ol.layer.Image} imageLayer Single image layer.
    + */
    +ol.renderer.canvas.ImageLayer = function(imageLayer) {
    +
    +  goog.base(this, imageLayer);
    +
    +  /**
    +   * @private
    +   * @type {?ol.ImageBase}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.imageTransform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {?goog.vec.Mat4.Number}
    +   */
    +  this.imageTransformInv_ = null;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.hitCanvasContext_ = null;
    +
    +};
    +goog.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  var layer = this.getLayer();
    +  var source = layer.getSource();
    +  var resolution = frameState.viewState.resolution;
    +  var rotation = frameState.viewState.rotation;
    +  var skippedFeatureUids = frameState.skippedFeatureUids;
    +  return source.forEachFeatureAtCoordinate(
    +      coordinate, resolution, rotation, skippedFeatureUids,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {?} Callback result.
    +       */
    +      function(feature) {
    +        return callback.call(thisArg, feature, layer);
    +      });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  if (goog.isNull(this.getImage())) {
    +    return undefined;
    +  }
    +
    +  if (this.getLayer().getSource() instanceof ol.source.ImageVector) {
    +    // for ImageVector sources use the original hit-detection logic,
    +    // so that for example also transparent polygons are detected
    +    var coordinate = pixel.slice();
    +    ol.vec.Mat4.multVec2(
    +        frameState.pixelToCoordinateMatrix, coordinate, coordinate);
    +    var hasFeature = this.forEachFeatureAtCoordinate(
    +        coordinate, frameState, goog.functions.TRUE, this);
    +
    +    if (hasFeature) {
    +      return callback.call(thisArg, this.getLayer());
    +    } else {
    +      return undefined;
    +    }
    +  } else {
    +    // for all other image sources directly check the image
    +    if (goog.isNull(this.imageTransformInv_)) {
    +      this.imageTransformInv_ = goog.vec.Mat4.createNumber();
    +      goog.vec.Mat4.invert(this.imageTransform_, this.imageTransformInv_);
    +    }
    +
    +    var pixelOnCanvas =
    +        this.getPixelOnCanvas(pixel, this.imageTransformInv_);
    +
    +    if (goog.isNull(this.hitCanvasContext_)) {
    +      this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1);
    +    }
    +
    +    this.hitCanvasContext_.clearRect(0, 0, 1, 1);
    +    this.hitCanvasContext_.drawImage(
    +        this.getImage(), pixelOnCanvas[0], pixelOnCanvas[1], 1, 1, 0, 0, 1, 1);
    +
    +    var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
    +    if (imageData[3] > 0) {
    +      return callback.call(thisArg, this.getLayer());
    +    } else {
    +      return undefined;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.ImageLayer.prototype.getImage = function() {
    +  return goog.isNull(this.image_) ?
    +      null : this.image_.getImage();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.ImageLayer.prototype.getImageTransform = function() {
    +  return this.imageTransform_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.ImageLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  var pixelRatio = frameState.pixelRatio;
    +  var viewState = frameState.viewState;
    +  var viewCenter = viewState.center;
    +  var viewResolution = viewState.resolution;
    +  var viewRotation = viewState.rotation;
    +
    +  var image;
    +  var imageLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(imageLayer, ol.layer.Image);
    +  var imageSource = imageLayer.getSource();
    +
    +  var hints = frameState.viewHints;
    +
    +  var renderedExtent = frameState.extent;
    +  if (goog.isDef(layerState.extent)) {
    +    renderedExtent = ol.extent.getIntersection(
    +        renderedExtent, layerState.extent);
    +  }
    +
    +  if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
    +      !ol.extent.isEmpty(renderedExtent)) {
    +    var projection = viewState.projection;
    +    var sourceProjection = imageSource.getProjection();
    +    if (!goog.isNull(sourceProjection)) {
    +      goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection));
    +      projection = sourceProjection;
    +    }
    +    image = imageSource.getImage(
    +        renderedExtent, viewResolution, pixelRatio, projection);
    +    if (!goog.isNull(image)) {
    +      var loaded = this.loadImage(image);
    +      if (loaded) {
    +        this.image_ = image;
    +      }
    +    }
    +  }
    +
    +  if (!goog.isNull(this.image_)) {
    +    image = this.image_;
    +    var imageExtent = image.getExtent();
    +    var imageResolution = image.getResolution();
    +    var imagePixelRatio = image.getPixelRatio();
    +    var scale = pixelRatio * imageResolution /
    +        (viewResolution * imagePixelRatio);
    +    ol.vec.Mat4.makeTransform2D(this.imageTransform_,
    +        pixelRatio * frameState.size[0] / 2,
    +        pixelRatio * frameState.size[1] / 2,
    +        scale, scale,
    +        viewRotation,
    +        imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution,
    +        imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution);
    +    this.imageTransformInv_ = null;
    +    this.updateAttributions(frameState.attributions, image.getAttributions());
    +    this.updateLogos(frameState, imageSource);
    +  }
    +
    +  return true;
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/canvas/canvaslayerrenderer.js b/mapbox-gl/ol/ol/renderer/canvas/canvaslayerrenderer.js
    new file mode 100644
    index 0000000000..21d0d23c90
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/canvas/canvaslayerrenderer.js
    @@ -0,0 +1,273 @@
    +goog.provide('ol.renderer.canvas.Layer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.canvas.Immediate');
    +goog.require('ol.renderer.Layer');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Layer}
    + * @param {ol.layer.Layer} layer Layer.
    + */
    +ol.renderer.canvas.Layer = function(layer) {
    +
    +  goog.base(this, layer);
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +goog.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer);
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @param {CanvasRenderingContext2D} context Context.
    + */
    +ol.renderer.canvas.Layer.prototype.composeFrame =
    +    function(frameState, layerState, context) {
    +
    +  this.dispatchPreComposeEvent(context, frameState);
    +
    +  var image = this.getImage();
    +  if (!goog.isNull(image)) {
    +
    +    // clipped rendering if layer extent is set
    +    var extent = layerState.extent;
    +    var clipped = goog.isDef(extent);
    +    if (clipped) {
    +      goog.asserts.assert(goog.isDef(extent));
    +      var pixelRatio = frameState.pixelRatio;
    +      var topLeft = ol.extent.getTopLeft(extent);
    +      var topRight = ol.extent.getTopRight(extent);
    +      var bottomRight = ol.extent.getBottomRight(extent);
    +      var bottomLeft = ol.extent.getBottomLeft(extent);
    +
    +      ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix,
    +          topLeft, topLeft);
    +      ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix,
    +          topRight, topRight);
    +      ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix,
    +          bottomRight, bottomRight);
    +      ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix,
    +          bottomLeft, bottomLeft);
    +
    +      context.save();
    +      context.beginPath();
    +      context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio);
    +      context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio);
    +      context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio);
    +      context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio);
    +      context.clip();
    +    }
    +
    +    var imageTransform = this.getImageTransform();
    +    // for performance reasons, context.save / context.restore is not used
    +    // to save and restore the transformation matrix and the opacity.
    +    // see http://jsperf.com/context-save-restore-versus-variable
    +    var alpha = context.globalAlpha;
    +    context.globalAlpha = layerState.opacity;
    +
    +    // for performance reasons, context.setTransform is only used
    +    // when the view is rotated. see http://jsperf.com/canvas-transform
    +    if (frameState.viewState.rotation === 0) {
    +      var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3);
    +      var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3);
    +      var dw = image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0);
    +      var dh = image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1);
    +      context.drawImage(image, 0, 0, +image.width, +image.height,
    +          Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
    +    } else {
    +      context.setTransform(
    +          goog.vec.Mat4.getElement(imageTransform, 0, 0),
    +          goog.vec.Mat4.getElement(imageTransform, 1, 0),
    +          goog.vec.Mat4.getElement(imageTransform, 0, 1),
    +          goog.vec.Mat4.getElement(imageTransform, 1, 1),
    +          goog.vec.Mat4.getElement(imageTransform, 0, 3),
    +          goog.vec.Mat4.getElement(imageTransform, 1, 3));
    +      context.drawImage(image, 0, 0);
    +      context.setTransform(1, 0, 0, 1, 0, 0);
    +    }
    +    context.globalAlpha = alpha;
    +
    +    if (clipped) {
    +      context.restore();
    +    }
    +  }
    +
    +  this.dispatchPostComposeEvent(context, frameState);
    +
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {goog.vec.Mat4.Number=} opt_transform Transform.
    + * @private
    + */
    +ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ =
    +    function(type, context, frameState, opt_transform) {
    +  var layer = this.getLayer();
    +  if (layer.hasListener(type)) {
    +    var transform = goog.isDef(opt_transform) ?
    +        opt_transform : this.getTransform(frameState);
    +    var render = new ol.render.canvas.Immediate(
    +        context, frameState.pixelRatio, frameState.extent, transform,
    +        frameState.viewState.rotation);
    +    var composeEvent = new ol.render.Event(type, layer, render, null,
    +        frameState, context, null);
    +    layer.dispatchEvent(composeEvent);
    +    render.flush();
    +  }
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {goog.vec.Mat4.Number=} opt_transform Transform.
    + * @protected
    + */
    +ol.renderer.canvas.Layer.prototype.dispatchPostComposeEvent =
    +    function(context, frameState, opt_transform) {
    +  this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, context,
    +      frameState, opt_transform);
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {goog.vec.Mat4.Number=} opt_transform Transform.
    + * @protected
    + */
    +ol.renderer.canvas.Layer.prototype.dispatchPreComposeEvent =
    +    function(context, frameState, opt_transform) {
    +  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, context,
    +      frameState, opt_transform);
    +};
    +
    +
    +/**
    + * @param {CanvasRenderingContext2D} context Context.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {goog.vec.Mat4.Number=} opt_transform Transform.
    + * @protected
    + */
    +ol.renderer.canvas.Layer.prototype.dispatchRenderEvent =
    +    function(context, frameState, opt_transform) {
    +  this.dispatchComposeEvent_(ol.render.EventType.RENDER, context,
    +      frameState, opt_transform);
    +};
    +
    +
    +/**
    + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Canvas.
    + */
    +ol.renderer.canvas.Layer.prototype.getImage = goog.abstractMethod;
    +
    +
    +/**
    + * @return {!goog.vec.Mat4.Number} Image transform.
    + */
    +ol.renderer.canvas.Layer.prototype.getImageTransform = goog.abstractMethod;
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @protected
    + * @return {!goog.vec.Mat4.Number} Transform.
    + */
    +ol.renderer.canvas.Layer.prototype.getTransform = function(frameState) {
    +  var viewState = frameState.viewState;
    +  var pixelRatio = frameState.pixelRatio;
    +  return ol.vec.Mat4.makeTransform2D(this.transform_,
    +      pixelRatio * frameState.size[0] / 2,
    +      pixelRatio * frameState.size[1] / 2,
    +      pixelRatio / viewState.resolution,
    +      -pixelRatio / viewState.resolution,
    +      -viewState.rotation,
    +      -viewState.center[0], -viewState.center[1]);
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @return {boolean} whether composeFrame should be called.
    + */
    +ol.renderer.canvas.Layer.prototype.prepareFrame = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.Pixel} pixelOnMap Pixel.
    + * @param {goog.vec.Mat4.Number} imageTransformInv The transformation matrix
    + *        to convert from a map pixel to a canvas pixel.
    + * @return {ol.Pixel}
    + * @protected
    + */
    +ol.renderer.canvas.Layer.prototype.getPixelOnCanvas =
    +    function(pixelOnMap, imageTransformInv) {
    +  var pixelOnCanvas = [0, 0];
    +  ol.vec.Mat4.multVec2(imageTransformInv, pixelOnMap, pixelOnCanvas);
    +  return pixelOnCanvas;
    +};
    +
    +
    +/**
    + * @param {ol.Size} size Size.
    + * @return {boolean} True when the canvas with the current size does not exceed
    + *     the maximum dimensions.
    + */
    +ol.renderer.canvas.Layer.testCanvasSize = (function() {
    +
    +  /**
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  var context = null;
    +
    +  /**
    +   * @type {ImageData}
    +   */
    +  var imageData = null;
    +
    +  return function(size) {
    +    if (goog.isNull(context)) {
    +      context = ol.dom.createCanvasContext2D(1, 1);
    +      imageData = context.createImageData(1, 1);
    +      var data = imageData.data;
    +      data[0] = 42;
    +      data[1] = 84;
    +      data[2] = 126;
    +      data[3] = 255;
    +    }
    +    var canvas = context.canvas;
    +    var good = size[0] <= canvas.width && size[1] <= canvas.height;
    +    if (!good) {
    +      canvas.width = size[0];
    +      canvas.height = size[1];
    +      var x = size[0] - 1;
    +      var y = size[1] - 1;
    +      context.putImageData(imageData, x, y);
    +      var result = context.getImageData(x, y, 1, 1);
    +      good = goog.array.equals(imageData.data, result.data);
    +    }
    +    return good;
    +  };
    +})();
    diff --git a/mapbox-gl/ol/ol/renderer/canvas/canvasmaprenderer.js b/mapbox-gl/ol/ol/renderer/canvas/canvasmaprenderer.js
    new file mode 100644
    index 0000000000..2074a898ad
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/canvas/canvasmaprenderer.js
    @@ -0,0 +1,207 @@
    +// FIXME offset panning
    +
    +goog.provide('ol.renderer.canvas.Map');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.style');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol');
    +goog.require('ol.RendererType');
    +goog.require('ol.css');
    +goog.require('ol.dom');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.canvas.Immediate');
    +goog.require('ol.render.canvas.ReplayGroup');
    +goog.require('ol.renderer.Map');
    +goog.require('ol.renderer.canvas.ImageLayer');
    +goog.require('ol.renderer.canvas.Layer');
    +goog.require('ol.renderer.canvas.TileLayer');
    +goog.require('ol.renderer.canvas.VectorLayer');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.source.State');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Map}
    + * @param {Element} container Container.
    + * @param {ol.Map} map Map.
    + */
    +ol.renderer.canvas.Map = function(container, map) {
    +
    +  goog.base(this, container, map);
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = ol.dom.createCanvasContext2D();
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = this.context_.canvas;
    +
    +  this.canvas_.style.width = '100%';
    +  this.canvas_.style.height = '100%';
    +  this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
    +  goog.dom.insertChildAt(container, this.canvas_, 0);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = true;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
    +  if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
    +    return new ol.renderer.canvas.ImageLayer(layer);
    +  } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
    +    return new ol.renderer.canvas.TileLayer(layer);
    +  } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
    +    return new ol.renderer.canvas.VectorLayer(layer);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ =
    +    function(type, frameState) {
    +  var map = this.getMap();
    +  var context = this.context_;
    +  if (map.hasListener(type)) {
    +    var extent = frameState.extent;
    +    var pixelRatio = frameState.pixelRatio;
    +    var viewState = frameState.viewState;
    +    var resolution = viewState.resolution;
    +    var rotation = viewState.rotation;
    +
    +    ol.vec.Mat4.makeTransform2D(this.transform_,
    +        this.canvas_.width / 2, this.canvas_.height / 2,
    +        pixelRatio / resolution, -pixelRatio / resolution,
    +        -rotation,
    +        -viewState.center[0], -viewState.center[1]);
    +
    +    var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
    +    var replayGroup = new ol.render.canvas.ReplayGroup(tolerance, extent,
    +        resolution);
    +
    +    var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio,
    +        extent, this.transform_, rotation);
    +    var composeEvent = new ol.render.Event(type, map, vectorContext,
    +        replayGroup, frameState, context, null);
    +    map.dispatchEvent(composeEvent);
    +
    +    replayGroup.finish();
    +    if (!replayGroup.isEmpty()) {
    +      replayGroup.replay(context, pixelRatio, this.transform_,
    +          rotation, {});
    +    }
    +    vectorContext.flush();
    +    this.replayGroup = replayGroup;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.layer.Layer} layer Layer.
    + * @return {ol.renderer.canvas.Layer} Canvas layer renderer.
    + */
    +ol.renderer.canvas.Map.prototype.getCanvasLayerRenderer = function(layer) {
    +  var layerRenderer = this.getLayerRenderer(layer);
    +  goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer);
    +  return /** @type {ol.renderer.canvas.Layer} */ (layerRenderer);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.Map.prototype.getType = function() {
    +  return ol.RendererType.CANVAS;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
    +
    +  if (goog.isNull(frameState)) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.canvas_, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return;
    +  }
    +
    +  var context = this.context_;
    +  var width = frameState.size[0] * frameState.pixelRatio;
    +  var height = frameState.size[1] * frameState.pixelRatio;
    +  if (this.canvas_.width != width || this.canvas_.height != height) {
    +    this.canvas_.width = width;
    +    this.canvas_.height = height;
    +  } else {
    +    context.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
    +  }
    +
    +  this.calculateMatrices2D(frameState);
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
    +
    +  var layerStatesArray = frameState.layerStatesArray;
    +  var viewResolution = frameState.viewState.resolution;
    +  var i, ii, layer, layerRenderer, layerState;
    +  for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    +    layerState = layerStatesArray[i];
    +    layer = layerState.layer;
    +    layerRenderer = this.getLayerRenderer(layer);
    +    goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer);
    +    if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) ||
    +        layerState.sourceState != ol.source.State.READY) {
    +      continue;
    +    }
    +    if (layerRenderer.prepareFrame(frameState, layerState)) {
    +      layerRenderer.composeFrame(frameState, layerState, context);
    +    }
    +  }
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState);
    +
    +  if (!this.renderedVisible_) {
    +    goog.style.setElementShown(this.canvas_, true);
    +    this.renderedVisible_ = true;
    +  }
    +
    +  this.scheduleRemoveUnusedLayerRenderers(frameState);
    +  this.scheduleExpireIconCache(frameState);
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/canvas/canvastilelayerrenderer.js b/mapbox-gl/ol/ol/renderer/canvas/canvastilelayerrenderer.js
    new file mode 100644
    index 0000000000..ccf1db3d2e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/canvas/canvastilelayerrenderer.js
    @@ -0,0 +1,451 @@
    +// FIXME find correct globalCompositeOperation
    +// FIXME optimize :-)
    +
    +goog.provide('ol.renderer.canvas.TileLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.Size');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileState');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.renderer.canvas.Layer');
    +goog.require('ol.tilecoord');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.canvas.Layer}
    + * @param {ol.layer.Tile} tileLayer Tile layer.
    + */
    +ol.renderer.canvas.TileLayer = function(tileLayer) {
    +
    +  goog.base(this, tileLayer);
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.canvasSize_ = null;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.canvasTooBig_ = false;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = null;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.imageTransform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {?goog.vec.Mat4.Number}
    +   */
    +  this.imageTransformInv_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedCanvasZ_ = NaN;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedTileSize_ = NaN;
    +
    +  /**
    +   * @private
    +   * @type {ol.TileRange}
    +   */
    +  this.renderedCanvasTileRange_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.Tile|undefined>}
    +   */
    +  this.renderedTiles_ = null;
    +
    +};
    +goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.TileLayer.prototype.getImage = function() {
    +  return this.canvas_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.TileLayer.prototype.getImageTransform = function() {
    +  return this.imageTransform_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.TileLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  //
    +  // Warning! You're entering a dangerous zone!
    +  //
    +  // The canvas tile layer renderering is highly optimized, hence
    +  // the complexity of this function. For best performance we try
    +  // to minimize the number of pixels to update on the canvas. This
    +  // includes:
    +  //
    +  // - Only drawing pixels that will be visible.
    +  // - Not re-drawing pixels/tiles that are already correct.
    +  // - Minimizing calls to clearRect.
    +  // - Never shrink the canvas. Just make it bigger when necessary.
    +  //   Re-sizing the canvas also clears it, which further means
    +  //   re-creating it (expensive).
    +  //
    +  // The various steps performed by this functions:
    +  //
    +  // - Create a canvas element if none has been created yet.
    +  //
    +  // - Make the canvas bigger if it's too small. Note that we never shrink
    +  //   the canvas, we just make it bigger when necessary, when rotating for
    +  //   example. Note also that the canvas always contains a whole number
    +  //   of tiles.
    +  //
    +  // - Invalidate the canvas tile range (renderedCanvasTileRange_ = null)
    +  //   if (1) the canvas has been enlarged, or (2) the zoom level changes,
    +  //   or (3) the canvas tile range doesn't contain the required tile
    +  //   range. This canvas tile range invalidation thing is related to
    +  //   an optimization where we attempt to redraw as few pixels as
    +  //   possible on each prepareFrame call.
    +  //
    +  // - If the canvas tile range has been invalidated we reset
    +  //   renderedCanvasTileRange_ and reset the renderedTiles_ array.
    +  //   The renderedTiles_ array is the structure used to determine
    +  //   the canvas pixels that need not be redrawn from one prepareFrame
    +  //   call to another. It records while tile has been rendered at
    +  //   which position in the canvas.
    +  //
    +  // - We then determine the tiles to draw on the canvas. Tiles for
    +  //   the target resolution may not be loaded yet. In that case we
    +  //   use low-resolution/interim tiles if loaded already. And, if
    +  //   for a non-yet-loaded tile we haven't found a corresponding
    +  //   low-resolution tile we indicate that the pixels for that
    +  //   tile must be cleared on the canvas. Note: determining the
    +  //   interim tiles is based on tile extents instead of tile
    +  //   coords, this is to be able to handler irregular tile grids.
    +  //
    +  // - We're now ready to render. We start by calling clearRect
    +  //   for the tiles that aren't loaded yet and are not fully covered
    +  //   by a low-resolution tile (if they're loaded, we'll draw them;
    +  //   if they're fully covered by a low-resolution tile then there's
    +  //   no need to clear). We then render the tiles "back to front",
    +  //   i.e. starting with the low-resolution tiles.
    +  //
    +  // - After rendering some bookkeeping is performed (updateUsedTiles,
    +  //   etc.). manageTilePyramid is what enqueue tiles in the tile
    +  //   queue for loading.
    +  //
    +  // - The last step involves updating the image transform matrix,
    +  //   which will be used by the map renderer for the final
    +  //   composition and positioning.
    +  //
    +
    +  var pixelRatio = frameState.pixelRatio;
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +
    +  var tileLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile);
    +  var tileSource = tileLayer.getSource();
    +  var tileGrid = tileSource.getTileGridForProjection(projection);
    +  var tileGutter = tileSource.getGutter();
    +  var z = tileGrid.getZForResolution(viewState.resolution);
    +  var tilePixelSize =
    +      tileSource.getTilePixelSize(z, frameState.pixelRatio, projection);
    +  var tilePixelRatio = tilePixelSize / tileGrid.getTileSize(z);
    +  var tileResolution = tileGrid.getResolution(z);
    +  var tilePixelResolution = tileResolution / tilePixelRatio;
    +  var center = viewState.center;
    +  var extent;
    +  if (tileResolution == viewState.resolution) {
    +    center = this.snapCenterToPixel(center, tileResolution, frameState.size);
    +    extent = ol.extent.getForViewAndSize(
    +        center, tileResolution, viewState.rotation, frameState.size);
    +  } else {
    +    extent = frameState.extent;
    +  }
    +
    +  if (goog.isDef(layerState.extent)) {
    +    extent = ol.extent.getIntersection(extent, layerState.extent);
    +  }
    +  if (ol.extent.isEmpty(extent)) {
    +    // Return false to prevent the rendering of the layer.
    +    return false;
    +  }
    +
    +  var tileRange = tileGrid.getTileRangeForExtentAndResolution(
    +      extent, tileResolution);
    +
    +  var canvasWidth = tilePixelSize * tileRange.getWidth();
    +  var canvasHeight = tilePixelSize * tileRange.getHeight();
    +
    +  var canvas, context;
    +  if (goog.isNull(this.canvas_)) {
    +    goog.asserts.assert(goog.isNull(this.canvasSize_));
    +    goog.asserts.assert(goog.isNull(this.context_));
    +    goog.asserts.assert(goog.isNull(this.renderedCanvasTileRange_));
    +    context = ol.dom.createCanvasContext2D(canvasWidth, canvasHeight);
    +    this.canvas_ = context.canvas;
    +    this.canvasSize_ = [canvasWidth, canvasHeight];
    +    this.context_ = context;
    +    this.canvasTooBig_ =
    +        !ol.renderer.canvas.Layer.testCanvasSize(this.canvasSize_);
    +  } else {
    +    goog.asserts.assert(!goog.isNull(this.canvasSize_));
    +    goog.asserts.assert(!goog.isNull(this.context_));
    +    canvas = this.canvas_;
    +    context = this.context_;
    +    if (this.canvasSize_[0] < canvasWidth ||
    +        this.canvasSize_[1] < canvasHeight ||
    +        this.renderedTileSize_ !== tilePixelSize ||
    +        (this.canvasTooBig_ && (this.canvasSize_[0] > canvasWidth ||
    +        this.canvasSize_[1] > canvasHeight))) {
    +      // Canvas is too small or tileSize has changed, resize it.
    +      // We never shrink the canvas, unless
    +      // we know that the current canvas size exceeds the maximum size
    +      canvas.width = canvasWidth;
    +      canvas.height = canvasHeight;
    +      this.canvasSize_ = [canvasWidth, canvasHeight];
    +      this.canvasTooBig_ =
    +          !ol.renderer.canvas.Layer.testCanvasSize(this.canvasSize_);
    +      this.renderedCanvasTileRange_ = null;
    +    } else {
    +      canvasWidth = this.canvasSize_[0];
    +      canvasHeight = this.canvasSize_[1];
    +      if (z != this.renderedCanvasZ_ ||
    +          !this.renderedCanvasTileRange_.containsTileRange(tileRange)) {
    +        this.renderedCanvasTileRange_ = null;
    +      }
    +    }
    +  }
    +
    +  var canvasTileRange, canvasTileRangeWidth, minX, minY;
    +  if (goog.isNull(this.renderedCanvasTileRange_)) {
    +    canvasTileRangeWidth = canvasWidth / tilePixelSize;
    +    var canvasTileRangeHeight = canvasHeight / tilePixelSize;
    +    minX = tileRange.minX -
    +        Math.floor((canvasTileRangeWidth - tileRange.getWidth()) / 2);
    +    minY = tileRange.minY -
    +        Math.floor((canvasTileRangeHeight - tileRange.getHeight()) / 2);
    +    this.renderedCanvasZ_ = z;
    +    this.renderedTileSize_ = tilePixelSize;
    +    this.renderedCanvasTileRange_ = new ol.TileRange(
    +        minX, minX + canvasTileRangeWidth - 1,
    +        minY, minY + canvasTileRangeHeight - 1);
    +    this.renderedTiles_ =
    +        new Array(canvasTileRangeWidth * canvasTileRangeHeight);
    +    canvasTileRange = this.renderedCanvasTileRange_;
    +  } else {
    +    canvasTileRange = this.renderedCanvasTileRange_;
    +    canvasTileRangeWidth = canvasTileRange.getWidth();
    +  }
    +
    +  goog.asserts.assert(canvasTileRange.containsTileRange(tileRange));
    +
    +  /**
    +   * @type {Object.<number, Object.<string, ol.Tile>>}
    +   */
    +  var tilesToDrawByZ = {};
    +  tilesToDrawByZ[z] = {};
    +  /** @type {Array.<ol.Tile>} */
    +  var tilesToClear = [];
    +
    +  var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
    +
    +  var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
    +
    +  var tmpExtent = ol.extent.createEmpty();
    +  var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
    +  var childTileRange, fullyLoaded, tile, tileState, x, y;
    +  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +
    +      tile = tileSource.getTile(z, x, y, pixelRatio, projection);
    +      tileState = tile.getState();
    +      if (tileState == ol.TileState.LOADED ||
    +          tileState == ol.TileState.EMPTY ||
    +          (tileState == ol.TileState.ERROR && !useInterimTilesOnError)) {
    +        tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
    +        continue;
    +      }
    +
    +      fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
    +          tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
    +      if (!fullyLoaded) {
    +        // FIXME we do not need to clear the tile if it is fully covered by its
    +        //       children
    +        tilesToClear.push(tile);
    +        childTileRange = tileGrid.getTileCoordChildTileRange(
    +            tile.tileCoord, tmpTileRange, tmpExtent);
    +        if (!goog.isNull(childTileRange)) {
    +          findLoadedTiles(z + 1, childTileRange);
    +        }
    +      }
    +
    +    }
    +  }
    +
    +  var i, ii;
    +  for (i = 0, ii = tilesToClear.length; i < ii; ++i) {
    +    tile = tilesToClear[i];
    +    x = tilePixelSize * (tile.tileCoord[1] - canvasTileRange.minX);
    +    y = tilePixelSize * (canvasTileRange.maxY - tile.tileCoord[2]);
    +    context.clearRect(x, y, tilePixelSize, tilePixelSize);
    +  }
    +
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
    +  goog.array.sort(zs);
    +  var opaque = tileSource.getOpaque();
    +  var origin = ol.extent.getTopLeft(tileGrid.getTileCoordExtent(
    +      [z, canvasTileRange.minX, canvasTileRange.maxY],
    +      tmpExtent));
    +  var currentZ, index, scale, tileCoordKey, tileExtent, tilesToDraw;
    +  var ix, iy, interimTileExtent, interimTileRange, maxX, maxY;
    +  var height, width;
    +  for (i = 0, ii = zs.length; i < ii; ++i) {
    +    currentZ = zs[i];
    +    tilePixelSize =
    +        tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
    +    tilesToDraw = tilesToDrawByZ[currentZ];
    +    if (currentZ == z) {
    +      for (tileCoordKey in tilesToDraw) {
    +        tile = tilesToDraw[tileCoordKey];
    +        index =
    +            (tile.tileCoord[2] - canvasTileRange.minY) * canvasTileRangeWidth +
    +            (tile.tileCoord[1] - canvasTileRange.minX);
    +        if (this.renderedTiles_[index] != tile) {
    +          x = tilePixelSize * (tile.tileCoord[1] - canvasTileRange.minX);
    +          y = tilePixelSize * (canvasTileRange.maxY - tile.tileCoord[2]);
    +          tileState = tile.getState();
    +          if (tileState == ol.TileState.EMPTY ||
    +              (tileState == ol.TileState.ERROR && !useInterimTilesOnError) ||
    +              !opaque) {
    +            context.clearRect(x, y, tilePixelSize, tilePixelSize);
    +          }
    +          if (tileState == ol.TileState.LOADED) {
    +            context.drawImage(tile.getImage(),
    +                tileGutter, tileGutter, tilePixelSize, tilePixelSize,
    +                x, y, tilePixelSize, tilePixelSize);
    +          }
    +          this.renderedTiles_[index] = tile;
    +        }
    +      }
    +    } else {
    +      scale = tileGrid.getResolution(currentZ) / tileResolution;
    +      for (tileCoordKey in tilesToDraw) {
    +        tile = tilesToDraw[tileCoordKey];
    +        tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
    +        x = (tileExtent[0] - origin[0]) / tilePixelResolution;
    +        y = (origin[1] - tileExtent[3]) / tilePixelResolution;
    +        width = scale * tilePixelSize;
    +        height = scale * tilePixelSize;
    +        tileState = tile.getState();
    +        if (tileState == ol.TileState.EMPTY || !opaque) {
    +          context.clearRect(x, y, width, height);
    +        }
    +        if (tileState == ol.TileState.LOADED) {
    +          context.drawImage(tile.getImage(),
    +              tileGutter, tileGutter, tilePixelSize, tilePixelSize,
    +              x, y, width, height);
    +        }
    +        interimTileRange =
    +            tileGrid.getTileRangeForExtentAndZ(tileExtent, z, tmpTileRange);
    +        minX = Math.max(interimTileRange.minX, canvasTileRange.minX);
    +        maxX = Math.min(interimTileRange.maxX, canvasTileRange.maxX);
    +        minY = Math.max(interimTileRange.minY, canvasTileRange.minY);
    +        maxY = Math.min(interimTileRange.maxY, canvasTileRange.maxY);
    +        for (ix = minX; ix <= maxX; ++ix) {
    +          for (iy = minY; iy <= maxY; ++iy) {
    +            index = (iy - canvasTileRange.minY) * canvasTileRangeWidth +
    +                    (ix - canvasTileRange.minX);
    +            this.renderedTiles_[index] = undefined;
    +          }
    +        }
    +      }
    +    }
    +  }
    +
    +  this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
    +  this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
    +      projection, extent, z, tileLayer.getPreload());
    +  this.scheduleExpireCache(frameState, tileSource);
    +  this.updateLogos(frameState, tileSource);
    +
    +  ol.vec.Mat4.makeTransform2D(this.imageTransform_,
    +      pixelRatio * frameState.size[0] / 2,
    +      pixelRatio * frameState.size[1] / 2,
    +      pixelRatio * tilePixelResolution / viewState.resolution,
    +      pixelRatio * tilePixelResolution / viewState.resolution,
    +      viewState.rotation,
    +      (origin[0] - center[0]) / tilePixelResolution,
    +      (center[1] - origin[1]) / tilePixelResolution);
    +  this.imageTransformInv_ = null;
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  if (goog.isNull(this.context_)) {
    +    return undefined;
    +  }
    +
    +  if (goog.isNull(this.imageTransformInv_)) {
    +    this.imageTransformInv_ = goog.vec.Mat4.createNumber();
    +    goog.vec.Mat4.invert(this.imageTransform_, this.imageTransformInv_);
    +  }
    +
    +  var pixelOnCanvas =
    +      this.getPixelOnCanvas(pixel, this.imageTransformInv_);
    +
    +  var imageData = this.context_.getImageData(
    +      pixelOnCanvas[0], pixelOnCanvas[1], 1, 1).data;
    +
    +  if (imageData[3] > 0) {
    +    return callback.call(thisArg, this.getLayer());
    +  } else {
    +    return undefined;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/canvas/canvasvectorlayerrenderer.js b/mapbox-gl/ol/ol/renderer/canvas/canvasvectorlayerrenderer.js
    new file mode 100644
    index 0000000000..2ccec425c3
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/canvas/canvasvectorlayerrenderer.js
    @@ -0,0 +1,279 @@
    +goog.provide('ol.renderer.canvas.VectorLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.canvas.ReplayGroup');
    +goog.require('ol.renderer.canvas.Layer');
    +goog.require('ol.renderer.vector');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.canvas.Layer}
    + * @param {ol.layer.Vector} vectorLayer Vector layer.
    + */
    +ol.renderer.canvas.VectorLayer = function(vectorLayer) {
    +
    +  goog.base(this, vectorLayer);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.dirty_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedResolution_ = NaN;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.renderedExtent_ = ol.extent.createEmpty();
    +
    +  /**
    +   * @private
    +   * @type {function(ol.Feature, ol.Feature): number|null}
    +   */
    +  this.renderedRenderOrder_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.render.canvas.ReplayGroup}
    +   */
    +  this.replayGroup_ = null;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = ol.dom.createCanvasContext2D();
    +
    +};
    +goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.VectorLayer.prototype.composeFrame =
    +    function(frameState, layerState, context) {
    +
    +  var transform = this.getTransform(frameState);
    +
    +  this.dispatchPreComposeEvent(context, frameState, transform);
    +
    +  var replayGroup = this.replayGroup_;
    +  if (!goog.isNull(replayGroup) && !replayGroup.isEmpty()) {
    +    var layer = this.getLayer();
    +    var replayContext;
    +    if (layer.hasListener(ol.render.EventType.RENDER)) {
    +      // resize and clear
    +      this.context_.canvas.width = context.canvas.width;
    +      this.context_.canvas.height = context.canvas.height;
    +      replayContext = this.context_;
    +    } else {
    +      replayContext = context;
    +    }
    +    // for performance reasons, context.save / context.restore is not used
    +    // to save and restore the transformation matrix and the opacity.
    +    // see http://jsperf.com/context-save-restore-versus-variable
    +    var alpha = replayContext.globalAlpha;
    +    replayContext.globalAlpha = layerState.opacity;
    +    replayGroup.replay(
    +        replayContext, frameState.pixelRatio, transform,
    +        frameState.viewState.rotation, frameState.skippedFeatureUids);
    +
    +    if (replayContext != context) {
    +      this.dispatchRenderEvent(replayContext, frameState, transform);
    +      context.drawImage(replayContext.canvas, 0, 0);
    +    }
    +    replayContext.globalAlpha = alpha;
    +  }
    +
    +  this.dispatchPostComposeEvent(context, frameState, transform);
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  if (goog.isNull(this.replayGroup_)) {
    +    return undefined;
    +  } else {
    +    var resolution = frameState.viewState.resolution;
    +    var rotation = frameState.viewState.rotation;
    +    var layer = this.getLayer();
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +    return this.replayGroup_.forEachFeatureAtCoordinate(coordinate,
    +        resolution, rotation, frameState.skippedFeatureUids,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback.call(thisArg, feature, layer);
    +          }
    +        });
    +  }
    +};
    +
    +
    +/**
    + * Handle changes in image style state.
    + * @param {goog.events.Event} event Image style change event.
    + * @private
    + */
    +ol.renderer.canvas.VectorLayer.prototype.handleStyleImageChange_ =
    +    function(event) {
    +  this.renderIfReadyAndVisible();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.canvas.VectorLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
    +  goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector);
    +  var vectorSource = vectorLayer.getSource();
    +
    +  this.updateAttributions(
    +      frameState.attributions, vectorSource.getAttributions());
    +  this.updateLogos(frameState, vectorSource);
    +
    +  if (!this.dirty_ && (!vectorLayer.getUpdateWhileAnimating() &&
    +      frameState.viewHints[ol.ViewHint.ANIMATING] ||
    +      frameState.viewHints[ol.ViewHint.INTERACTING])) {
    +    return true;
    +  }
    +
    +  var frameStateExtent = frameState.extent;
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +  var resolution = viewState.resolution;
    +  var pixelRatio = frameState.pixelRatio;
    +  var vectorLayerRevision = vectorLayer.getRevision();
    +  var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
    +  var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
    +
    +  if (!goog.isDef(vectorLayerRenderOrder)) {
    +    vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
    +  }
    +
    +  var extent = ol.extent.buffer(frameStateExtent,
    +      vectorLayerRenderBuffer * resolution);
    +
    +  if (!this.dirty_ &&
    +      this.renderedResolution_ == resolution &&
    +      this.renderedRevision_ == vectorLayerRevision &&
    +      this.renderedRenderOrder_ == vectorLayerRenderOrder &&
    +      ol.extent.containsExtent(this.renderedExtent_, extent)) {
    +    return true;
    +  }
    +
    +  // FIXME dispose of old replayGroup in post render
    +  goog.dispose(this.replayGroup_);
    +  this.replayGroup_ = null;
    +
    +  this.dirty_ = false;
    +
    +  var replayGroup =
    +      new ol.render.canvas.ReplayGroup(
    +          ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
    +          resolution, vectorLayer.getRenderBuffer());
    +  vectorSource.loadFeatures(extent, resolution, projection);
    +  var renderFeature =
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @this {ol.renderer.canvas.VectorLayer}
    +       */
    +      function(feature) {
    +    var styles;
    +    if (goog.isDef(feature.getStyleFunction())) {
    +      styles = feature.getStyleFunction().call(feature, resolution);
    +    } else if (goog.isDef(vectorLayer.getStyleFunction())) {
    +      styles = vectorLayer.getStyleFunction()(feature, resolution);
    +    }
    +    if (goog.isDefAndNotNull(styles)) {
    +      var dirty = this.renderFeature(
    +          feature, resolution, pixelRatio, styles, replayGroup);
    +      this.dirty_ = this.dirty_ || dirty;
    +    }
    +  };
    +  if (!goog.isNull(vectorLayerRenderOrder)) {
    +    /** @type {Array.<ol.Feature>} */
    +    var features = [];
    +    vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         */
    +        function(feature) {
    +          features.push(feature);
    +        }, this);
    +    goog.array.sort(features, vectorLayerRenderOrder);
    +    goog.array.forEach(features, renderFeature, this);
    +  } else {
    +    vectorSource.forEachFeatureInExtentAtResolution(
    +        extent, resolution, renderFeature, this);
    +  }
    +  replayGroup.finish();
    +
    +  this.renderedResolution_ = resolution;
    +  this.renderedRevision_ = vectorLayerRevision;
    +  this.renderedRenderOrder_ = vectorLayerRenderOrder;
    +  this.renderedExtent_ = extent;
    +  this.replayGroup_ = replayGroup;
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {Array.<ol.style.Style>} styles Array of styles
    + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
    + * @return {boolean} `true` if an image is loading.
    + */
    +ol.renderer.canvas.VectorLayer.prototype.renderFeature =
    +    function(feature, resolution, pixelRatio, styles, replayGroup) {
    +  if (!goog.isDefAndNotNull(styles)) {
    +    return false;
    +  }
    +  var i, ii, loading = false;
    +  for (i = 0, ii = styles.length; i < ii; ++i) {
    +    loading = ol.renderer.vector.renderFeature(
    +        replayGroup, feature, styles[i],
    +        ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
    +        this.handleStyleImageChange_, this) || loading;
    +  }
    +  return loading;
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/dom/domimagelayerrenderer.js b/mapbox-gl/ol/ol/renderer/dom/domimagelayerrenderer.js
    new file mode 100644
    index 0000000000..6746de6691
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/dom/domimagelayerrenderer.js
    @@ -0,0 +1,158 @@
    +goog.provide('ol.renderer.dom.ImageLayer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.ImageBase');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj');
    +goog.require('ol.renderer.dom.Layer');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.dom.Layer}
    + * @param {ol.layer.Image} imageLayer Image layer.
    + */
    +ol.renderer.dom.ImageLayer = function(imageLayer) {
    +  var target = goog.dom.createElement(goog.dom.TagName.DIV);
    +  target.style.position = 'absolute';
    +
    +  goog.base(this, imageLayer, target);
    +
    +  /**
    +   * The last rendered image.
    +   * @private
    +   * @type {?ol.ImageBase}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumberIdentity();
    +
    +};
    +goog.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.ImageLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  var layer = this.getLayer();
    +  var source = layer.getSource();
    +  var resolution = frameState.viewState.resolution;
    +  var rotation = frameState.viewState.rotation;
    +  var skippedFeatureUids = frameState.skippedFeatureUids;
    +  return source.forEachFeatureAtCoordinate(
    +      coordinate, resolution, rotation, skippedFeatureUids,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {?} Callback result.
    +       */
    +      function(feature) {
    +        return callback.call(thisArg, feature, layer);
    +      });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.ImageLayer.prototype.clearFrame = function() {
    +  goog.dom.removeChildren(this.target);
    +  this.image_ = null;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.ImageLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  var viewState = frameState.viewState;
    +  var viewCenter = viewState.center;
    +  var viewResolution = viewState.resolution;
    +  var viewRotation = viewState.rotation;
    +
    +  var image = this.image_;
    +  var imageLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(imageLayer, ol.layer.Image);
    +  var imageSource = imageLayer.getSource();
    +
    +  var hints = frameState.viewHints;
    +
    +  var renderedExtent = frameState.extent;
    +  if (goog.isDef(layerState.extent)) {
    +    renderedExtent = ol.extent.getIntersection(
    +        renderedExtent, layerState.extent);
    +  }
    +
    +  if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
    +      !ol.extent.isEmpty(renderedExtent)) {
    +    var projection = viewState.projection;
    +    var sourceProjection = imageSource.getProjection();
    +    if (!goog.isNull(sourceProjection)) {
    +      goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection));
    +      projection = sourceProjection;
    +    }
    +    var image_ = imageSource.getImage(renderedExtent, viewResolution,
    +        frameState.pixelRatio, projection);
    +    if (!goog.isNull(image_)) {
    +      var loaded = this.loadImage(image_);
    +      if (loaded) {
    +        image = image_;
    +      }
    +    }
    +  }
    +
    +  if (!goog.isNull(image)) {
    +    var imageExtent = image.getExtent();
    +    var imageResolution = image.getResolution();
    +    var transform = goog.vec.Mat4.createNumber();
    +    ol.vec.Mat4.makeTransform2D(transform,
    +        frameState.size[0] / 2, frameState.size[1] / 2,
    +        imageResolution / viewResolution, imageResolution / viewResolution,
    +        viewRotation,
    +        (imageExtent[0] - viewCenter[0]) / imageResolution,
    +        (viewCenter[1] - imageExtent[3]) / imageResolution);
    +    if (image != this.image_) {
    +      var imageElement = image.getImage(this);
    +      // Bootstrap sets the style max-width: 100% for all images, which breaks
    +      // prevents the image from being displayed in FireFox.  Workaround by
    +      // overriding the max-width style.
    +      imageElement.style.maxWidth = 'none';
    +      imageElement.style.position = 'absolute';
    +      goog.dom.removeChildren(this.target);
    +      goog.dom.appendChild(this.target, imageElement);
    +      this.image_ = image;
    +    }
    +    this.setTransform_(transform);
    +    this.updateAttributions(frameState.attributions, image.getAttributions());
    +    this.updateLogos(frameState, imageSource);
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @private
    + */
    +ol.renderer.dom.ImageLayer.prototype.setTransform_ = function(transform) {
    +  if (!ol.vec.Mat4.equals2D(transform, this.transform_)) {
    +    ol.dom.transformElement2D(this.target, transform, 6);
    +    goog.vec.Mat4.setFromArray(this.transform_, transform);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/dom/domlayerrenderer.js b/mapbox-gl/ol/ol/renderer/dom/domlayerrenderer.js
    new file mode 100644
    index 0000000000..991ceef4e9
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/dom/domlayerrenderer.js
    @@ -0,0 +1,54 @@
    +goog.provide('ol.renderer.dom.Layer');
    +
    +goog.require('ol.layer.Layer');
    +goog.require('ol.renderer.Layer');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Layer}
    + * @param {ol.layer.Layer} layer Layer.
    + * @param {!Element} target Target.
    + */
    +ol.renderer.dom.Layer = function(layer, target) {
    +
    +  goog.base(this, layer);
    +
    +  /**
    +   * @type {!Element}
    +   * @protected
    +   */
    +  this.target = target;
    +
    +};
    +goog.inherits(ol.renderer.dom.Layer, ol.renderer.Layer);
    +
    +
    +/**
    + * Clear rendered elements.
    + */
    +ol.renderer.dom.Layer.prototype.clearFrame = goog.nullFunction;
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + */
    +ol.renderer.dom.Layer.prototype.composeFrame = goog.nullFunction;
    +
    +
    +/**
    + * @return {!Element} Target.
    + */
    +ol.renderer.dom.Layer.prototype.getTarget = function() {
    +  return this.target;
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @return {boolean} whether composeFrame should be called.
    + */
    +ol.renderer.dom.Layer.prototype.prepareFrame = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/renderer/dom/dommaprenderer.js b/mapbox-gl/ol/ol/renderer/dom/dommaprenderer.js
    new file mode 100644
    index 0000000000..4b966df86a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/dom/dommaprenderer.js
    @@ -0,0 +1,272 @@
    +goog.provide('ol.renderer.dom.Map');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.style');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol');
    +goog.require('ol.RendererType');
    +goog.require('ol.css');
    +goog.require('ol.dom');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.canvas.Immediate');
    +goog.require('ol.render.canvas.ReplayGroup');
    +goog.require('ol.renderer.Map');
    +goog.require('ol.renderer.dom.ImageLayer');
    +goog.require('ol.renderer.dom.Layer');
    +goog.require('ol.renderer.dom.TileLayer');
    +goog.require('ol.renderer.dom.VectorLayer');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.source.State');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Map}
    + * @param {Element} container Container.
    + * @param {ol.Map} map Map.
    + */
    +ol.renderer.dom.Map = function(container, map) {
    +
    +  goog.base(this, container, map);
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = null;
    +  if (!(ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE)) {
    +    this.context_ = ol.dom.createCanvasContext2D();
    +    var canvas = this.context_.canvas;
    +    canvas.style.position = 'absolute';
    +    canvas.style.width = '100%';
    +    canvas.style.height = '100%';
    +    canvas.className = ol.css.CLASS_UNSELECTABLE;
    +    goog.dom.insertChildAt(container, canvas, 0);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @type {!Element}
    +   * @private
    +   */
    +  this.layersPane_ = goog.dom.createElement(goog.dom.TagName.DIV);
    +  this.layersPane_.className = ol.css.CLASS_UNSELECTABLE;
    +  var style = this.layersPane_.style;
    +  style.position = 'absolute';
    +  style.width = '100%';
    +  style.height = '100%';
    +
    +  // in IE < 9, we need to return false from ondragstart to cancel the default
    +  // behavior of dragging images, which is interfering with the custom handler
    +  // in the Drag interaction subclasses
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    this.layersPane_.ondragstart = goog.functions.FALSE;
    +    this.layersPane_.onselectstart = goog.functions.FALSE;
    +  }
    +
    +  // prevent the img context menu on mobile devices
    +  goog.events.listen(this.layersPane_, goog.events.EventType.TOUCHSTART,
    +      goog.events.Event.preventDefault);
    +
    +  goog.dom.insertChildAt(container, this.layersPane_, 0);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = true;
    +
    +};
    +goog.inherits(ol.renderer.dom.Map, ol.renderer.Map);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.Map.prototype.disposeInternal = function() {
    +  goog.dom.removeNode(this.layersPane_);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) {
    +  var layerRenderer;
    +  if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
    +    layerRenderer = new ol.renderer.dom.ImageLayer(layer);
    +  } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
    +    layerRenderer = new ol.renderer.dom.TileLayer(layer);
    +  } else if (!(ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) &&
    +      ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
    +    layerRenderer = new ol.renderer.dom.VectorLayer(layer);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +  return layerRenderer;
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.dom.Map.prototype.dispatchComposeEvent_ =
    +    function(type, frameState) {
    +  var map = this.getMap();
    +  if (!(ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) && map.hasListener(type)) {
    +    var extent = frameState.extent;
    +    var pixelRatio = frameState.pixelRatio;
    +    var viewState = frameState.viewState;
    +    var resolution = viewState.resolution;
    +    var rotation = viewState.rotation;
    +    var context = this.context_;
    +    var canvas = context.canvas;
    +
    +    ol.vec.Mat4.makeTransform2D(this.transform_,
    +        canvas.width / 2,
    +        canvas.height / 2,
    +        pixelRatio / viewState.resolution,
    +        -pixelRatio / viewState.resolution,
    +        -viewState.rotation,
    +        -viewState.center[0], -viewState.center[1]);
    +    var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio,
    +        extent, this.transform_, rotation);
    +    var replayGroup = new ol.render.canvas.ReplayGroup(
    +        ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
    +        resolution);
    +    var composeEvent = new ol.render.Event(type, map, vectorContext,
    +        replayGroup, frameState, context, null);
    +    map.dispatchEvent(composeEvent);
    +    replayGroup.finish();
    +    if (!replayGroup.isEmpty()) {
    +      replayGroup.replay(context, pixelRatio, this.transform_, rotation, {});
    +    }
    +    vectorContext.flush();
    +    this.replayGroup = replayGroup;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.Map.prototype.getType = function() {
    +  return ol.RendererType.DOM;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.Map.prototype.renderFrame = function(frameState) {
    +
    +  if (goog.isNull(frameState)) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.layersPane_, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return;
    +  }
    +
    +  /**
    +   * @this {ol.renderer.dom.Map}
    +   * @param {Element} elem
    +   * @param {number} i
    +   */
    +  var addChild;
    +
    +  // appendChild is actually more performant than insertBefore
    +  // in IE 7 and 8. http://jsperf.com/reattaching-dom-nodes
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    addChild =
    +        /**
    +         * @this {ol.renderer.dom.Map}
    +         * @param {Element} elem
    +         */ (
    +        function(elem) {
    +          goog.dom.appendChild(this.layersPane_, elem);
    +        });
    +  } else {
    +    addChild =
    +        /**
    +         * @this {ol.renderer.dom.Map}
    +         * @param {Element} elem
    +         * @param {number} i
    +         */ (
    +        function(elem, i) {
    +          goog.dom.insertChildAt(this.layersPane_, elem, i);
    +        });
    +  }
    +
    +  var map = this.getMap();
    +  if (!(ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) &&
    +      (map.hasListener(ol.render.EventType.PRECOMPOSE) ||
    +      map.hasListener(ol.render.EventType.POSTCOMPOSE))) {
    +    var canvas = this.context_.canvas;
    +    var pixelRatio = frameState.pixelRatio;
    +    canvas.width = frameState.size[0] * pixelRatio;
    +    canvas.height = frameState.size[1] * pixelRatio;
    +  }
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
    +
    +  var layerStatesArray = frameState.layerStatesArray;
    +  var i, ii, layer, layerRenderer, layerState;
    +  for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    +    layerState = layerStatesArray[i];
    +    layer = layerState.layer;
    +    layerRenderer = /** @type {ol.renderer.dom.Layer} */ (
    +        this.getLayerRenderer(layer));
    +    goog.asserts.assertInstanceof(layerRenderer, ol.renderer.dom.Layer);
    +    addChild.call(this, layerRenderer.getTarget(), i);
    +    if (layerState.sourceState == ol.source.State.READY) {
    +      if (layerRenderer.prepareFrame(frameState, layerState)) {
    +        layerRenderer.composeFrame(frameState, layerState);
    +      }
    +    } else {
    +      layerRenderer.clearFrame();
    +    }
    +  }
    +
    +  var layerStates = frameState.layerStates;
    +  var layerKey;
    +  for (layerKey in this.getLayerRenderers()) {
    +    if (!(layerKey in layerStates)) {
    +      layerRenderer = this.getLayerRendererByKey(layerKey);
    +      goog.asserts.assertInstanceof(layerRenderer, ol.renderer.dom.Layer);
    +      goog.dom.removeNode(layerRenderer.getTarget());
    +    }
    +  }
    +
    +  if (!this.renderedVisible_) {
    +    goog.style.setElementShown(this.layersPane_, true);
    +    this.renderedVisible_ = true;
    +  }
    +
    +  this.calculateMatrices2D(frameState);
    +  this.scheduleRemoveUnusedLayerRenderers(frameState);
    +  this.scheduleExpireIconCache(frameState);
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState);
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/dom/domtilelayerrenderer.js b/mapbox-gl/ol/ol/renderer/dom/domtilelayerrenderer.js
    new file mode 100644
    index 0000000000..091de7701d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/dom/domtilelayerrenderer.js
    @@ -0,0 +1,472 @@
    +// FIXME probably need to reset TileLayerZ if offsets get too large
    +// FIXME when zooming out, preserve higher Z divs to avoid white flash
    +
    +goog.provide('ol.renderer.dom.TileLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol');
    +goog.require('ol.Coordinate');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileState');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.dom.BrowserFeature');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.renderer.dom.Layer');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.TileGrid');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.dom.Layer}
    + * @param {ol.layer.Tile} tileLayer Tile layer.
    + */
    +ol.renderer.dom.TileLayer = function(tileLayer) {
    +
    +  var target = goog.dom.createElement(goog.dom.TagName.DIV);
    +  target.style.position = 'absolute';
    +
    +  // Needed for IE7-8 to render a transformed element correctly
    +  if (ol.dom.BrowserFeature.USE_MS_MATRIX_TRANSFORM) {
    +    target.style.width = '100%';
    +    target.style.height = '100%';
    +  }
    +
    +  goog.base(this, tileLayer, target);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = true;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedOpacity_ = 1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, ol.renderer.dom.TileLayerZ_>}
    +   */
    +  this.tileLayerZs_ = {};
    +
    +};
    +goog.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.TileLayer.prototype.clearFrame = function() {
    +  goog.dom.removeChildren(this.target);
    +  this.renderedRevision_ = 0;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.TileLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  if (!layerState.visible) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.target, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return true;
    +  }
    +
    +  var pixelRatio = frameState.pixelRatio;
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +
    +  var tileLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile);
    +  var tileSource = tileLayer.getSource();
    +  var tileGrid = tileSource.getTileGridForProjection(projection);
    +  var tileGutter = tileSource.getGutter();
    +  var z = tileGrid.getZForResolution(viewState.resolution);
    +  var tileResolution = tileGrid.getResolution(z);
    +  var center = viewState.center;
    +  var extent;
    +  if (tileResolution == viewState.resolution) {
    +    center = this.snapCenterToPixel(center, tileResolution, frameState.size);
    +    extent = ol.extent.getForViewAndSize(
    +        center, tileResolution, viewState.rotation, frameState.size);
    +  } else {
    +    extent = frameState.extent;
    +  }
    +
    +  if (goog.isDef(layerState.extent)) {
    +    extent = ol.extent.getIntersection(extent, layerState.extent);
    +  }
    +
    +  var tileRange = tileGrid.getTileRangeForExtentAndResolution(
    +      extent, tileResolution);
    +
    +  /** @type {Object.<number, Object.<string, ol.Tile>>} */
    +  var tilesToDrawByZ = {};
    +  tilesToDrawByZ[z] = {};
    +
    +  var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
    +
    +  var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
    +
    +  var tmpExtent = ol.extent.createEmpty();
    +  var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
    +  var childTileRange, fullyLoaded, tile, tileState, x, y;
    +  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +
    +      tile = tileSource.getTile(z, x, y, pixelRatio, projection);
    +      tileState = tile.getState();
    +      if (tileState == ol.TileState.LOADED) {
    +        tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
    +        continue;
    +      } else if (tileState == ol.TileState.EMPTY ||
    +                 (tileState == ol.TileState.ERROR &&
    +                  !useInterimTilesOnError)) {
    +        continue;
    +      }
    +
    +      fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
    +          tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
    +      if (!fullyLoaded) {
    +        childTileRange = tileGrid.getTileCoordChildTileRange(
    +            tile.tileCoord, tmpTileRange, tmpExtent);
    +        if (!goog.isNull(childTileRange)) {
    +          findLoadedTiles(z + 1, childTileRange);
    +        }
    +      }
    +
    +    }
    +
    +  }
    +
    +  // If the tile source revision changes, we destroy the existing DOM structure
    +  // so that a new one will be created.  It would be more efficient to modify
    +  // the existing structure.
    +  var tileLayerZ, tileLayerZKey;
    +  if (this.renderedRevision_ != tileSource.getRevision()) {
    +    for (tileLayerZKey in this.tileLayerZs_) {
    +      tileLayerZ = this.tileLayerZs_[+tileLayerZKey];
    +      goog.dom.removeNode(tileLayerZ.target);
    +    }
    +    this.tileLayerZs_ = {};
    +    this.renderedRevision_ = tileSource.getRevision();
    +  }
    +
    +  /** @type {Array.<number>} */
    +  var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
    +  goog.array.sort(zs);
    +
    +  /** @type {Object.<number, boolean>} */
    +  var newTileLayerZKeys = {};
    +
    +  var iz, iziz, tileCoordKey, tileCoordOrigin, tilesToDraw;
    +  for (iz = 0, iziz = zs.length; iz < iziz; ++iz) {
    +    tileLayerZKey = zs[iz];
    +    if (tileLayerZKey in this.tileLayerZs_) {
    +      tileLayerZ = this.tileLayerZs_[tileLayerZKey];
    +    } else {
    +      tileCoordOrigin =
    +          tileGrid.getTileCoordForCoordAndZ(center, tileLayerZKey);
    +      tileLayerZ = new ol.renderer.dom.TileLayerZ_(tileGrid, tileCoordOrigin);
    +      newTileLayerZKeys[tileLayerZKey] = true;
    +      this.tileLayerZs_[tileLayerZKey] = tileLayerZ;
    +    }
    +    tilesToDraw = tilesToDrawByZ[tileLayerZKey];
    +    for (tileCoordKey in tilesToDraw) {
    +      tileLayerZ.addTile(tilesToDraw[tileCoordKey], tileGutter);
    +    }
    +    tileLayerZ.finalizeAddTiles();
    +  }
    +
    +  /** @type {Array.<number>} */
    +  var tileLayerZKeys =
    +      goog.array.map(goog.object.getKeys(this.tileLayerZs_), Number);
    +  goog.array.sort(tileLayerZKeys);
    +
    +  var i, ii, j, origin, resolution;
    +  var transform = goog.vec.Mat4.createNumber();
    +  for (i = 0, ii = tileLayerZKeys.length; i < ii; ++i) {
    +    tileLayerZKey = tileLayerZKeys[i];
    +    tileLayerZ = this.tileLayerZs_[tileLayerZKey];
    +    if (!(tileLayerZKey in tilesToDrawByZ)) {
    +      goog.dom.removeNode(tileLayerZ.target);
    +      delete this.tileLayerZs_[tileLayerZKey];
    +      continue;
    +    }
    +    resolution = tileLayerZ.getResolution();
    +    origin = tileLayerZ.getOrigin();
    +    ol.vec.Mat4.makeTransform2D(transform,
    +        frameState.size[0] / 2, frameState.size[1] / 2,
    +        resolution / viewState.resolution,
    +        resolution / viewState.resolution,
    +        viewState.rotation,
    +        (origin[0] - center[0]) / resolution,
    +        (center[1] - origin[1]) / resolution);
    +    tileLayerZ.setTransform(transform);
    +    if (tileLayerZKey in newTileLayerZKeys) {
    +      for (j = tileLayerZKey - 1; j >= 0; --j) {
    +        if (j in this.tileLayerZs_) {
    +          goog.dom.insertSiblingAfter(
    +              tileLayerZ.target, this.tileLayerZs_[j].target);
    +          break;
    +        }
    +      }
    +      if (j < 0) {
    +        goog.dom.insertChildAt(this.target, tileLayerZ.target, 0);
    +      }
    +    } else {
    +      if (!frameState.viewHints[ol.ViewHint.ANIMATING] &&
    +          !frameState.viewHints[ol.ViewHint.INTERACTING]) {
    +        tileLayerZ.removeTilesOutsideExtent(extent, tmpTileRange);
    +      }
    +    }
    +  }
    +
    +  if (layerState.opacity != this.renderedOpacity_) {
    +    ol.dom.setOpacity(this.target, layerState.opacity);
    +    this.renderedOpacity_ = layerState.opacity;
    +  }
    +
    +  if (layerState.visible && !this.renderedVisible_) {
    +    goog.style.setElementShown(this.target, true);
    +    this.renderedVisible_ = true;
    +  }
    +
    +  this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
    +  this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
    +      projection, extent, z, tileLayer.getPreload());
    +  this.scheduleExpireCache(frameState, tileSource);
    +  this.updateLogos(frameState, tileSource);
    +
    +  return true;
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @private
    + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
    + * @param {ol.TileCoord} tileCoordOrigin Tile coord origin.
    + */
    +ol.renderer.dom.TileLayerZ_ = function(tileGrid, tileCoordOrigin) {
    +
    +  /**
    +   * @type {!Element}
    +   */
    +  this.target = goog.dom.createElement(goog.dom.TagName.DIV);
    +  this.target.style.position = 'absolute';
    +  this.target.style.width = '100%';
    +  this.target.style.height = '100%';
    +
    +  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +    /**
    +     * Needed due to issues with IE7-8 clipping of transformed elements
    +     * Solution is to translate separately from the scaled/rotated elements
    +     * @private
    +     * @type {!Element}
    +     */
    +    this.translateTarget_ = goog.dom.createElement(goog.dom.TagName.DIV);
    +    this.translateTarget_.style.position = 'absolute';
    +    this.translateTarget_.style.width = '100%';
    +    this.translateTarget_.style.height = '100%';
    +
    +    goog.dom.appendChild(this.target, this.translateTarget_);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {ol.tilegrid.TileGrid}
    +   */
    +  this.tileGrid_ = tileGrid;
    +
    +  /**
    +   * @private
    +   * @type {ol.TileCoord}
    +   */
    +  this.tileCoordOrigin_ = tileCoordOrigin;
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.origin_ =
    +      ol.extent.getTopLeft(tileGrid.getTileCoordExtent(tileCoordOrigin));
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.resolution_ = tileGrid.getResolution(tileCoordOrigin[0]);
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.Tile>}
    +   */
    +  this.tiles_ = {};
    +
    +  /**
    +   * @private
    +   * @type {DocumentFragment}
    +   */
    +  this.documentFragment_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumberIdentity();
    +
    +};
    +
    +
    +/**
    + * @param {ol.Tile} tile Tile.
    + * @param {number} tileGutter Tile gutter.
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.addTile = function(tile, tileGutter) {
    +  var tileCoord = tile.tileCoord;
    +  var tileCoordZ = tileCoord[0];
    +  var tileCoordX = tileCoord[1];
    +  var tileCoordY = tileCoord[2];
    +  goog.asserts.assert(tileCoordZ == this.tileCoordOrigin_[0]);
    +  var tileCoordKey = ol.tilecoord.toString(tileCoord);
    +  if (tileCoordKey in this.tiles_) {
    +    return;
    +  }
    +  var tileSize = this.tileGrid_.getTileSize(tileCoordZ);
    +  var image = tile.getImage(this);
    +  var imageStyle = image.style;
    +  // Bootstrap sets the style max-width: 100% for all images, which
    +  // prevents the tile from being displayed in FireFox.  Workaround
    +  // by overriding the max-width style.
    +  imageStyle.maxWidth = 'none';
    +  var tileElement;
    +  var tileElementStyle;
    +  if (tileGutter > 0) {
    +    tileElement = goog.dom.createElement(goog.dom.TagName.DIV);
    +    tileElementStyle = tileElement.style;
    +    tileElementStyle.overflow = 'hidden';
    +    tileElementStyle.width = tileSize + 'px';
    +    tileElementStyle.height = tileSize + 'px';
    +    imageStyle.position = 'absolute';
    +    imageStyle.left = -tileGutter + 'px';
    +    imageStyle.top = -tileGutter + 'px';
    +    imageStyle.width = (tileSize + 2 * tileGutter) + 'px';
    +    imageStyle.height = (tileSize + 2 * tileGutter) + 'px';
    +    goog.dom.appendChild(tileElement, image);
    +  } else {
    +    imageStyle.width = tileSize + 'px';
    +    imageStyle.height = tileSize + 'px';
    +    tileElement = image;
    +    tileElementStyle = imageStyle;
    +  }
    +  tileElementStyle.position = 'absolute';
    +  tileElementStyle.left =
    +      ((tileCoordX - this.tileCoordOrigin_[1]) * tileSize) + 'px';
    +  tileElementStyle.top =
    +      ((this.tileCoordOrigin_[2] - tileCoordY) * tileSize) + 'px';
    +  if (goog.isNull(this.documentFragment_)) {
    +    this.documentFragment_ = document.createDocumentFragment();
    +  }
    +  goog.dom.appendChild(this.documentFragment_, tileElement);
    +  this.tiles_[tileCoordKey] = tile;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.finalizeAddTiles = function() {
    +  if (!goog.isNull(this.documentFragment_)) {
    +    if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +      goog.dom.appendChild(this.translateTarget_, this.documentFragment_);
    +    } else {
    +      goog.dom.appendChild(this.target, this.documentFragment_);
    +    }
    +    this.documentFragment_ = null;
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate} Origin.
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.getOrigin = function() {
    +  return this.origin_;
    +};
    +
    +
    +/**
    + * @return {number} Resolution.
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.getResolution = function() {
    +  return this.resolution_;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object.
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.removeTilesOutsideExtent =
    +    function(extent, opt_tileRange) {
    +  var tileRange = this.tileGrid_.getTileRangeForExtentAndZ(
    +      extent, this.tileCoordOrigin_[0], opt_tileRange);
    +  /** @type {Array.<ol.Tile>} */
    +  var tilesToRemove = [];
    +  var tile, tileCoordKey;
    +  for (tileCoordKey in this.tiles_) {
    +    tile = this.tiles_[tileCoordKey];
    +    if (!tileRange.contains(tile.tileCoord)) {
    +      tilesToRemove.push(tile);
    +    }
    +  }
    +  var i, ii;
    +  for (i = 0, ii = tilesToRemove.length; i < ii; ++i) {
    +    tile = tilesToRemove[i];
    +    tileCoordKey = ol.tilecoord.toString(tile.tileCoord);
    +    goog.dom.removeNode(tile.getImage(this));
    +    delete this.tiles_[tileCoordKey];
    +  }
    +};
    +
    +
    +/**
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + */
    +ol.renderer.dom.TileLayerZ_.prototype.setTransform = function(transform) {
    +  if (!ol.vec.Mat4.equals2D(transform, this.transform_)) {
    +    if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    +      ol.dom.transformElement2D(this.target, transform, 6,
    +          this.translateTarget_);
    +    } else {
    +      ol.dom.transformElement2D(this.target, transform, 6);
    +    }
    +    goog.vec.Mat4.setFromArray(this.transform_, transform);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/dom/domvectorlayerrenderer.js b/mapbox-gl/ol/ol/renderer/dom/domvectorlayerrenderer.js
    new file mode 100644
    index 0000000000..08aa7af139
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/dom/domvectorlayerrenderer.js
    @@ -0,0 +1,339 @@
    +goog.provide('ol.renderer.dom.VectorLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.canvas.Immediate');
    +goog.require('ol.render.canvas.ReplayGroup');
    +goog.require('ol.renderer.dom.Layer');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.dom.Layer}
    + * @param {ol.layer.Vector} vectorLayer Vector layer.
    + */
    +ol.renderer.dom.VectorLayer = function(vectorLayer) {
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = ol.dom.createCanvasContext2D();
    +
    +  var target = this.context_.canvas;
    +  // Bootstrap sets the style max-width: 100% for all images, which breaks
    +  // prevents the image from being displayed in FireFox.  Workaround by
    +  // overriding the max-width style.
    +  target.style.maxWidth = 'none';
    +  target.style.position = 'absolute';
    +
    +  goog.base(this, vectorLayer, target);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.dirty_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedResolution_ = NaN;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.renderedExtent_ = ol.extent.createEmpty();
    +
    +  /**
    +   * @private
    +   * @type {function(ol.Feature, ol.Feature): number|null}
    +   */
    +  this.renderedRenderOrder_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.render.canvas.ReplayGroup}
    +   */
    +  this.replayGroup_ = null;
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {goog.vec.Mat4.Number}
    +   */
    +  this.elementTransform_ = goog.vec.Mat4.createNumber();
    +
    +};
    +goog.inherits(ol.renderer.dom.VectorLayer, ol.renderer.dom.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.VectorLayer.prototype.composeFrame =
    +    function(frameState, layerState) {
    +
    +  var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
    +  goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector);
    +
    +  var viewState = frameState.viewState;
    +  var viewCenter = viewState.center;
    +  var viewRotation = viewState.rotation;
    +  var viewResolution = viewState.resolution;
    +  var pixelRatio = frameState.pixelRatio;
    +  var viewWidth = frameState.size[0];
    +  var viewHeight = frameState.size[1];
    +  var imageWidth = viewWidth * pixelRatio;
    +  var imageHeight = viewHeight * pixelRatio;
    +
    +  var transform = ol.vec.Mat4.makeTransform2D(this.transform_,
    +      pixelRatio * viewWidth / 2,
    +      pixelRatio * viewHeight / 2,
    +      pixelRatio / viewResolution,
    +      -pixelRatio / viewResolution,
    +      -viewRotation,
    +      -viewCenter[0], -viewCenter[1]);
    +
    +  var context = this.context_;
    +
    +  // Clear the canvas and set the correct size
    +  context.canvas.width = imageWidth;
    +  context.canvas.height = imageHeight;
    +
    +  var elementTransform = ol.vec.Mat4.makeTransform2D(this.elementTransform_,
    +      0, 0,
    +      1 / pixelRatio, 1 / pixelRatio,
    +      0,
    +      -(imageWidth - viewWidth) / 2 * pixelRatio,
    +      -(imageHeight - viewHeight) / 2 * pixelRatio);
    +  ol.dom.transformElement2D(context.canvas, elementTransform, 6);
    +
    +  this.dispatchEvent_(ol.render.EventType.PRECOMPOSE, frameState, transform);
    +
    +  var replayGroup = this.replayGroup_;
    +
    +  if (!goog.isNull(replayGroup) && !replayGroup.isEmpty()) {
    +
    +    context.globalAlpha = layerState.opacity;
    +    replayGroup.replay(context, pixelRatio, transform, viewRotation,
    +        frameState.skippedFeatureUids);
    +
    +    this.dispatchEvent_(ol.render.EventType.RENDER, frameState, transform);
    +  }
    +
    +  this.dispatchEvent_(ol.render.EventType.POSTCOMPOSE, frameState, transform);
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {goog.vec.Mat4.Number} transform Transform.
    + * @private
    + */
    +ol.renderer.dom.VectorLayer.prototype.dispatchEvent_ =
    +    function(type, frameState, transform) {
    +  var context = this.context_;
    +  var layer = this.getLayer();
    +  if (layer.hasListener(type)) {
    +    var render = new ol.render.canvas.Immediate(
    +        context, frameState.pixelRatio, frameState.extent, transform,
    +        frameState.viewState.rotation);
    +    var event = new ol.render.Event(type, layer, render, null,
    +        frameState, context, null);
    +    layer.dispatchEvent(event);
    +    render.flush();
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.VectorLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  if (goog.isNull(this.replayGroup_)) {
    +    return undefined;
    +  } else {
    +    var resolution = frameState.viewState.resolution;
    +    var rotation = frameState.viewState.rotation;
    +    var layer = this.getLayer();
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +    return this.replayGroup_.forEachFeatureAtCoordinate(coordinate,
    +        resolution, rotation, frameState.skippedFeatureUids,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback.call(thisArg, feature, layer);
    +          }
    +        });
    +  }
    +};
    +
    +
    +/**
    + * Handle changes in image style state.
    + * @param {goog.events.Event} event Image style change event.
    + * @private
    + */
    +ol.renderer.dom.VectorLayer.prototype.handleStyleImageChange_ =
    +    function(event) {
    +  this.renderIfReadyAndVisible();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.dom.VectorLayer.prototype.prepareFrame =
    +    function(frameState, layerState) {
    +
    +  var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
    +  goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector);
    +  var vectorSource = vectorLayer.getSource();
    +
    +  this.updateAttributions(
    +      frameState.attributions, vectorSource.getAttributions());
    +  this.updateLogos(frameState, vectorSource);
    +
    +  if (!this.dirty_ && (!vectorLayer.getUpdateWhileAnimating() &&
    +      frameState.viewHints[ol.ViewHint.ANIMATING] ||
    +      frameState.viewHints[ol.ViewHint.INTERACTING])) {
    +    return true;
    +  }
    +
    +  var frameStateExtent = frameState.extent;
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +  var resolution = viewState.resolution;
    +  var pixelRatio = frameState.pixelRatio;
    +  var vectorLayerRevision = vectorLayer.getRevision();
    +  var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
    +  var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
    +
    +  if (!goog.isDef(vectorLayerRenderOrder)) {
    +    vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
    +  }
    +
    +  var extent = ol.extent.buffer(frameStateExtent,
    +      vectorLayerRenderBuffer * resolution);
    +
    +  if (!this.dirty_ &&
    +      this.renderedResolution_ == resolution &&
    +      this.renderedRevision_ == vectorLayerRevision &&
    +      this.renderedRenderOrder_ == vectorLayerRenderOrder &&
    +      ol.extent.containsExtent(this.renderedExtent_, extent)) {
    +    return true;
    +  }
    +
    +  // FIXME dispose of old replayGroup in post render
    +  goog.dispose(this.replayGroup_);
    +  this.replayGroup_ = null;
    +
    +  this.dirty_ = false;
    +
    +  var replayGroup =
    +      new ol.render.canvas.ReplayGroup(
    +          ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
    +          resolution, vectorLayer.getRenderBuffer());
    +  vectorSource.loadFeatures(extent, resolution, projection);
    +  var renderFeature =
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @this {ol.renderer.dom.VectorLayer}
    +       */
    +      function(feature) {
    +    var styles;
    +    if (goog.isDef(feature.getStyleFunction())) {
    +      styles = feature.getStyleFunction().call(feature, resolution);
    +    } else if (goog.isDef(vectorLayer.getStyleFunction())) {
    +      styles = vectorLayer.getStyleFunction()(feature, resolution);
    +    }
    +    if (goog.isDefAndNotNull(styles)) {
    +      var dirty = this.renderFeature(
    +          feature, resolution, pixelRatio, styles, replayGroup);
    +      this.dirty_ = this.dirty_ || dirty;
    +    }
    +  };
    +  if (!goog.isNull(vectorLayerRenderOrder)) {
    +    /** @type {Array.<ol.Feature>} */
    +    var features = [];
    +    vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         */
    +        function(feature) {
    +          features.push(feature);
    +        }, this);
    +    goog.array.sort(features, vectorLayerRenderOrder);
    +    goog.array.forEach(features, renderFeature, this);
    +  } else {
    +    vectorSource.forEachFeatureInExtentAtResolution(
    +        extent, resolution, renderFeature, this);
    +  }
    +  replayGroup.finish();
    +
    +  this.renderedResolution_ = resolution;
    +  this.renderedRevision_ = vectorLayerRevision;
    +  this.renderedRenderOrder_ = vectorLayerRenderOrder;
    +  this.renderedExtent_ = extent;
    +  this.replayGroup_ = replayGroup;
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {Array.<ol.style.Style>} styles Array of styles
    + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
    + * @return {boolean} `true` if an image is loading.
    + */
    +ol.renderer.dom.VectorLayer.prototype.renderFeature =
    +    function(feature, resolution, pixelRatio, styles, replayGroup) {
    +  if (!goog.isDefAndNotNull(styles)) {
    +    return false;
    +  }
    +  var i, ii, loading = false;
    +  for (i = 0, ii = styles.length; i < ii; ++i) {
    +    loading = ol.renderer.vector.renderFeature(
    +        replayGroup, feature, styles[i],
    +        ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
    +        this.handleStyleImageChange_, this) || loading;
    +  }
    +  return loading;
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/layerrenderer.js b/mapbox-gl/ol/ol/renderer/layerrenderer.js
    new file mode 100644
    index 0000000000..53adca1c18
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/layerrenderer.js
    @@ -0,0 +1,332 @@
    +goog.provide('ol.renderer.Layer');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('ol.ImageState');
    +goog.require('ol.Observable');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileState');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.source.Source');
    +goog.require('ol.source.State');
    +goog.require('ol.source.Tile');
    +goog.require('ol.tilecoord');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.Observable}
    + * @param {ol.layer.Layer} layer Layer.
    + * @suppress {checkStructDictInheritance}
    + * @struct
    + */
    +ol.renderer.Layer = function(layer) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.layer.Layer}
    +   */
    +  this.layer_ = layer;
    +
    +
    +};
    +goog.inherits(ol.renderer.Layer, ol.Observable);
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature
    + *     callback.
    + * @param {S} thisArg Value to use as `this` when executing `callback`.
    + * @return {T|undefined} Callback result.
    + * @template S,T
    + */
    +ol.renderer.Layer.prototype.forEachFeatureAtCoordinate = goog.nullFunction;
    +
    +
    +/**
    + * @param {ol.Pixel} pixel Pixel.
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {function(this: S, ol.layer.Layer): T} callback Layer callback.
    + * @param {S} thisArg Value to use as `this` when executing `callback`.
    + * @return {T|undefined} Callback result.
    + * @template S,T
    + */
    +ol.renderer.Layer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  var coordinate = pixel.slice();
    +  ol.vec.Mat4.multVec2(
    +      frameState.pixelToCoordinateMatrix, coordinate, coordinate);
    +
    +  var hasFeature = this.forEachFeatureAtCoordinate(
    +      coordinate, frameState, goog.functions.TRUE, this);
    +
    +  if (hasFeature) {
    +    return callback.call(thisArg, this.layer_);
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {olx.FrameState} frameState Frame state.
    + * @return {boolean} Is there a feature at the given coordinate?
    + */
    +ol.renderer.Layer.prototype.hasFeatureAtCoordinate = goog.functions.FALSE;
    +
    +
    +/**
    + * Create a function that adds loaded tiles to the tile lookup.
    + * @param {ol.source.Tile} source Tile source.
    + * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
    + *     tiles by zoom level.
    + * @return {function(number, ol.TileRange):boolean} A function that can be
    + *     called with a zoom level and a tile range to add loaded tiles to the
    + *     lookup.
    + * @protected
    + */
    +ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
    +  return (
    +      /**
    +       * @param {number} zoom Zoom level.
    +       * @param {ol.TileRange} tileRange Tile range.
    +       * @return {boolean} The tile range is fully loaded.
    +       */
    +      function(zoom, tileRange) {
    +        return source.forEachLoadedTile(zoom, tileRange, function(tile) {
    +          if (!tiles[zoom]) {
    +            tiles[zoom] = {};
    +          }
    +          tiles[zoom][tile.tileCoord.toString()] = tile;
    +        });
    +      });
    +};
    +
    +
    +/**
    + * @protected
    + * @return {ol.layer.Layer} Layer.
    + */
    +ol.renderer.Layer.prototype.getLayer = function() {
    +  return this.layer_;
    +};
    +
    +
    +/**
    + * Handle changes in image state.
    + * @param {goog.events.Event} event Image change event.
    + * @private
    + */
    +ol.renderer.Layer.prototype.handleImageChange_ = function(event) {
    +  var image = /** @type {ol.Image} */ (event.target);
    +  if (image.getState() === ol.ImageState.LOADED) {
    +    this.renderIfReadyAndVisible();
    +  }
    +};
    +
    +
    +/**
    + * Load the image if not already loaded, and register the image change
    + * listener if needed.
    + * @param {ol.ImageBase} image Image.
    + * @return {boolean} `true` if the image is already loaded, `false`
    + *     otherwise.
    + * @protected
    + */
    +ol.renderer.Layer.prototype.loadImage = function(image) {
    +  var imageState = image.getState();
    +  if (imageState != ol.ImageState.LOADED &&
    +      imageState != ol.ImageState.ERROR) {
    +    // the image is either "idle" or "loading", register the change
    +    // listener (a noop if the listener was already registered)
    +    goog.asserts.assert(imageState == ol.ImageState.IDLE ||
    +        imageState == ol.ImageState.LOADING);
    +    goog.events.listenOnce(image, goog.events.EventType.CHANGE,
    +        this.handleImageChange_, false, this);
    +  }
    +  if (imageState == ol.ImageState.IDLE) {
    +    image.load();
    +    imageState = image.getState();
    +    goog.asserts.assert(imageState == ol.ImageState.LOADING);
    +  }
    +  return imageState == ol.ImageState.LOADED;
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.renderer.Layer.prototype.renderIfReadyAndVisible = function() {
    +  var layer = this.getLayer();
    +  if (layer.getVisible() && layer.getSourceState() == ol.source.State.READY) {
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.source.Tile} tileSource Tile source.
    + * @protected
    + */
    +ol.renderer.Layer.prototype.scheduleExpireCache =
    +    function(frameState, tileSource) {
    +  if (tileSource.canExpireCache()) {
    +    frameState.postRenderFunctions.push(
    +        goog.partial(
    +            /**
    +             * @param {ol.source.Tile} tileSource Tile source.
    +             * @param {ol.Map} map Map.
    +             * @param {olx.FrameState} frameState Frame state.
    +             */
    +            function(tileSource, map, frameState) {
    +              var tileSourceKey = goog.getUid(tileSource).toString();
    +              tileSource.expireCache(frameState.usedTiles[tileSourceKey]);
    +            }, tileSource));
    +  }
    +};
    +
    +
    +/**
    + * @param {Object.<string, ol.Attribution>} attributionsSet Attributions
    + *     set (target).
    + * @param {Array.<ol.Attribution>} attributions Attributions (source).
    + * @protected
    + */
    +ol.renderer.Layer.prototype.updateAttributions =
    +    function(attributionsSet, attributions) {
    +  if (goog.isDefAndNotNull(attributions)) {
    +    var attribution, i, ii;
    +    for (i = 0, ii = attributions.length; i < ii; ++i) {
    +      attribution = attributions[i];
    +      attributionsSet[goog.getUid(attribution).toString()] = attribution;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.source.Source} source Source.
    + * @protected
    + */
    +ol.renderer.Layer.prototype.updateLogos = function(frameState, source) {
    +  var logo = source.getLogo();
    +  if (goog.isDef(logo)) {
    +    if (goog.isString(logo)) {
    +      frameState.logos[logo] = '';
    +    } else if (goog.isObject(logo)) {
    +      goog.asserts.assertString(logo.href);
    +      goog.asserts.assertString(logo.src);
    +      frameState.logos[logo.src] = logo.href;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles.
    + * @param {ol.source.Tile} tileSource Tile source.
    + * @param {number} z Z.
    + * @param {ol.TileRange} tileRange Tile range.
    + * @protected
    + */
    +ol.renderer.Layer.prototype.updateUsedTiles =
    +    function(usedTiles, tileSource, z, tileRange) {
    +  // FIXME should we use tilesToDrawByZ instead?
    +  var tileSourceKey = goog.getUid(tileSource).toString();
    +  var zKey = z.toString();
    +  if (tileSourceKey in usedTiles) {
    +    if (zKey in usedTiles[tileSourceKey]) {
    +      usedTiles[tileSourceKey][zKey].extend(tileRange);
    +    } else {
    +      usedTiles[tileSourceKey][zKey] = tileRange;
    +    }
    +  } else {
    +    usedTiles[tileSourceKey] = {};
    +    usedTiles[tileSourceKey][zKey] = tileRange;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {ol.Size} size Size.
    + * @protected
    + * @return {ol.Coordinate} Snapped center.
    + */
    +ol.renderer.Layer.prototype.snapCenterToPixel =
    +    function(center, resolution, size) {
    +  return [
    +    resolution * (Math.round(center[0] / resolution) + (size[0] % 2) / 2),
    +    resolution * (Math.round(center[1] / resolution) + (size[1] % 2) / 2)
    +  ];
    +};
    +
    +
    +/**
    + * Manage tile pyramid.
    + * This function performs a number of functions related to the tiles at the
    + * current zoom and lower zoom levels:
    + * - registers idle tiles in frameState.wantedTiles so that they are not
    + *   discarded by the tile queue
    + * - enqueues missing tiles
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.source.Tile} tileSource Tile source.
    + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {ol.Extent} extent Extent.
    + * @param {number} currentZ Current Z.
    + * @param {number} preload Load low resolution tiles up to 'preload' levels.
    + * @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback.
    + * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`.
    + * @protected
    + * @template T
    + */
    +ol.renderer.Layer.prototype.manageTilePyramid = function(
    +    frameState, tileSource, tileGrid, pixelRatio, projection, extent,
    +    currentZ, preload, opt_tileCallback, opt_this) {
    +  var tileSourceKey = goog.getUid(tileSource).toString();
    +  if (!(tileSourceKey in frameState.wantedTiles)) {
    +    frameState.wantedTiles[tileSourceKey] = {};
    +  }
    +  var wantedTiles = frameState.wantedTiles[tileSourceKey];
    +  var tileQueue = frameState.tileQueue;
    +  var minZoom = tileGrid.getMinZoom();
    +  var tile, tileRange, tileResolution, x, y, z;
    +  for (z = currentZ; z >= minZoom; --z) {
    +    tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange);
    +    tileResolution = tileGrid.getResolution(z);
    +    for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +      for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +        if (currentZ - z <= preload) {
    +          tile = tileSource.getTile(z, x, y, pixelRatio, projection);
    +          if (tile.getState() == ol.TileState.IDLE) {
    +            wantedTiles[ol.tilecoord.toString(tile.tileCoord)] = true;
    +            if (!tileQueue.isKeyQueued(tile.getKey())) {
    +              tileQueue.enqueue([tile, tileSourceKey,
    +                tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]);
    +            }
    +          }
    +          if (goog.isDef(opt_tileCallback)) {
    +            opt_tileCallback.call(opt_this, tile);
    +          }
    +        } else {
    +          tileSource.useTile(z, x, y);
    +        }
    +      }
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/maprenderer.js b/mapbox-gl/ol/ol/renderer/maprenderer.js
    new file mode 100644
    index 0000000000..c1c7748e67
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/maprenderer.js
    @@ -0,0 +1,381 @@
    +goog.provide('ol.RendererType');
    +goog.provide('ol.renderer.Map');
    +
    +goog.require('goog.Disposable');
    +goog.require('goog.asserts');
    +goog.require('goog.dispose');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.renderer.Layer');
    +goog.require('ol.style.IconImageCache');
    +goog.require('ol.vec.Mat4');
    +
    +
    +/**
    + * Available renderers: `'canvas'`, `'dom'` or `'webgl'`.
    + * @enum {string}
    + * @api stable
    + */
    +ol.RendererType = {
    +  CANVAS: 'canvas',
    +  DOM: 'dom',
    +  WEBGL: 'webgl'
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {goog.Disposable}
    + * @param {Element} container Container.
    + * @param {ol.Map} map Map.
    + * @suppress {checkStructDictInheritance}
    + * @struct
    + */
    +ol.renderer.Map = function(container, map) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.Map}
    +   */
    +  this.map_ = map;
    +
    +  /**
    +   * @protected
    +   * @type {ol.render.IReplayGroup}
    +   */
    +  this.replayGroup = null;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.renderer.Layer>}
    +   */
    +  this.layerRenderers_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, goog.events.Key>}
    +   */
    +  this.layerRendererListeners_ = {};
    +
    +};
    +goog.inherits(ol.renderer.Map, goog.Disposable);
    +
    +
    +/**
    + * @param {olx.FrameState} frameState FrameState.
    + * @protected
    + */
    +ol.renderer.Map.prototype.calculateMatrices2D = function(frameState) {
    +  var viewState = frameState.viewState;
    +  var coordinateToPixelMatrix = frameState.coordinateToPixelMatrix;
    +  goog.asserts.assert(!goog.isNull(coordinateToPixelMatrix));
    +  ol.vec.Mat4.makeTransform2D(coordinateToPixelMatrix,
    +      frameState.size[0] / 2, frameState.size[1] / 2,
    +      1 / viewState.resolution, -1 / viewState.resolution,
    +      -viewState.rotation,
    +      -viewState.center[0], -viewState.center[1]);
    +  var inverted = goog.vec.Mat4.invert(
    +      coordinateToPixelMatrix, frameState.pixelToCoordinateMatrix);
    +  goog.asserts.assert(inverted);
    +};
    +
    +
    +/**
    + * @param {ol.layer.Layer} layer Layer.
    + * @protected
    + * @return {ol.renderer.Layer} layerRenderer Layer renderer.
    + */
    +ol.renderer.Map.prototype.createLayerRenderer = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.Map.prototype.disposeInternal = function() {
    +  goog.object.forEach(this.layerRenderers_, goog.dispose);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.Map.expireIconCache_ = function(map, frameState) {
    +  ol.style.IconImageCache.getInstance().expire();
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {olx.FrameState} frameState FrameState.
    + * @param {function(this: S, ol.Feature, ol.layer.Layer): T} callback Feature
    + *     callback.
    + * @param {S} thisArg Value to use as `this` when executing `callback`.
    + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
    + *     function, only layers which are visible and for which this function
    + *     returns `true` will be tested for features.  By default, all visible
    + *     layers will be tested.
    + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
    + * @return {T|undefined} Callback result.
    + * @template S,T,U
    + */
    +ol.renderer.Map.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg,
    +        layerFilter, thisArg2) {
    +  var result;
    +  var viewState = frameState.viewState;
    +  var viewResolution = viewState.resolution;
    +  var viewRotation = viewState.rotation;
    +  if (!goog.isNull(this.replayGroup)) {
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +    result = this.replayGroup.forEachFeatureAtCoordinate(coordinate,
    +        viewResolution, viewRotation, {},
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback.call(thisArg, feature, null);
    +          }
    +        });
    +    if (result) {
    +      return result;
    +    }
    +  }
    +  var layerStates = frameState.layerStatesArray;
    +  var numLayers = layerStates.length;
    +  var i;
    +  for (i = numLayers - 1; i >= 0; --i) {
    +    var layerState = layerStates[i];
    +    var layer = layerState.layer;
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
    +        layerFilter.call(thisArg2, layer)) {
    +      var layerRenderer = this.getLayerRenderer(layer);
    +      result = layerRenderer.forEachFeatureAtCoordinate(
    +          coordinate, frameState, callback, thisArg);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {ol.Pixel} pixel Pixel.
    + * @param {olx.FrameState} frameState FrameState.
    + * @param {function(this: S, ol.layer.Layer): T} callback Layer
    + *     callback.
    + * @param {S} thisArg Value to use as `this` when executing `callback`.
    + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
    + *     function, only layers which are visible and for which this function
    + *     returns `true` will be tested for features.  By default, all visible
    + *     layers will be tested.
    + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
    + * @return {T|undefined} Callback result.
    + * @template S,T,U
    + */
    +ol.renderer.Map.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg,
    +        layerFilter, thisArg2) {
    +  var result;
    +  var viewState = frameState.viewState;
    +  var viewResolution = viewState.resolution;
    +  var viewRotation = viewState.rotation;
    +
    +  if (!goog.isNull(this.replayGroup)) {
    +    var coordinate = this.getMap().getCoordinateFromPixel(pixel);
    +    var hasFeature = this.replayGroup.forEachFeatureAtCoordinate(coordinate,
    +        viewResolution, viewRotation, {}, goog.functions.TRUE);
    +
    +    if (hasFeature) {
    +      result = callback.call(thisArg, null);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  var layerStates = frameState.layerStatesArray;
    +  var numLayers = layerStates.length;
    +  var i;
    +  for (i = numLayers - 1; i >= 0; --i) {
    +    var layerState = layerStates[i];
    +    var layer = layerState.layer;
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
    +        layerFilter.call(thisArg2, layer)) {
    +      var layerRenderer = this.getLayerRenderer(layer);
    +      result = layerRenderer.forEachLayerAtPixel(
    +          pixel, frameState, callback, thisArg);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {olx.FrameState} frameState FrameState.
    + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter
    + *     function, only layers which are visible and for which this function
    + *     returns `true` will be tested for features.  By default, all visible
    + *     layers will be tested.
    + * @param {U} thisArg Value to use as `this` when executing `layerFilter`.
    + * @return {boolean} Is there a feature at the given coordinate?
    + * @template U
    + */
    +ol.renderer.Map.prototype.hasFeatureAtCoordinate =
    +    function(coordinate, frameState, layerFilter, thisArg) {
    +  var hasFeature = this.forEachFeatureAtCoordinate(
    +      coordinate, frameState, goog.functions.TRUE, this, layerFilter, thisArg);
    +
    +  return goog.isDef(hasFeature);
    +};
    +
    +
    +/**
    + * @param {ol.layer.Layer} layer Layer.
    + * @protected
    + * @return {ol.renderer.Layer} Layer renderer.
    + */
    +ol.renderer.Map.prototype.getLayerRenderer = function(layer) {
    +  var layerKey = goog.getUid(layer).toString();
    +  if (layerKey in this.layerRenderers_) {
    +    return this.layerRenderers_[layerKey];
    +  } else {
    +    var layerRenderer = this.createLayerRenderer(layer);
    +    this.layerRenderers_[layerKey] = layerRenderer;
    +    this.layerRendererListeners_[layerKey] = goog.events.listen(layerRenderer,
    +        goog.events.EventType.CHANGE, this.handleLayerRendererChange_,
    +        false, this);
    +
    +    return layerRenderer;
    +  }
    +};
    +
    +
    +/**
    + * @param {string} layerKey Layer key.
    + * @protected
    + * @return {ol.renderer.Layer} Layer renderer.
    + */
    +ol.renderer.Map.prototype.getLayerRendererByKey = function(layerKey) {
    +  goog.asserts.assert(layerKey in this.layerRenderers_);
    +  return this.layerRenderers_[layerKey];
    +};
    +
    +
    +/**
    + * @protected
    + * @return {Object.<number, ol.renderer.Layer>} Layer renderers.
    + */
    +ol.renderer.Map.prototype.getLayerRenderers = function() {
    +  return this.layerRenderers_;
    +};
    +
    +
    +/**
    + * @return {ol.Map} Map.
    + */
    +ol.renderer.Map.prototype.getMap = function() {
    +  return this.map_;
    +};
    +
    +
    +/**
    + * @return {string} Type
    + */
    +ol.renderer.Map.prototype.getType = goog.abstractMethod;
    +
    +
    +/**
    + * Handle changes in a layer renderer.
    + * @private
    + */
    +ol.renderer.Map.prototype.handleLayerRendererChange_ = function() {
    +  this.map_.render();
    +};
    +
    +
    +/**
    + * @param {string} layerKey Layer key.
    + * @return {ol.renderer.Layer} Layer renderer.
    + * @private
    + */
    +ol.renderer.Map.prototype.removeLayerRendererByKey_ = function(layerKey) {
    +  goog.asserts.assert(layerKey in this.layerRenderers_);
    +  var layerRenderer = this.layerRenderers_[layerKey];
    +  delete this.layerRenderers_[layerKey];
    +
    +  goog.asserts.assert(layerKey in this.layerRendererListeners_);
    +  goog.events.unlistenByKey(this.layerRendererListeners_[layerKey]);
    +  delete this.layerRendererListeners_[layerKey];
    +
    +  return layerRenderer;
    +};
    +
    +
    +/**
    + * Render.
    + * @param {?olx.FrameState} frameState Frame state.
    + */
    +ol.renderer.Map.prototype.renderFrame = goog.nullFunction;
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.Map.prototype.removeUnusedLayerRenderers_ =
    +    function(map, frameState) {
    +  var layerKey;
    +  for (layerKey in this.layerRenderers_) {
    +    if (goog.isNull(frameState) || !(layerKey in frameState.layerStates)) {
    +      goog.dispose(this.removeLayerRendererByKey_(layerKey));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @protected
    + */
    +ol.renderer.Map.prototype.scheduleExpireIconCache = function(frameState) {
    +  frameState.postRenderFunctions.push(ol.renderer.Map.expireIconCache_);
    +};
    +
    +
    +/**
    + * @param {!olx.FrameState} frameState Frame state.
    + * @protected
    + */
    +ol.renderer.Map.prototype.scheduleRemoveUnusedLayerRenderers =
    +    function(frameState) {
    +  var layerKey;
    +  for (layerKey in this.layerRenderers_) {
    +    if (!(layerKey in frameState.layerStates)) {
    +      frameState.postRenderFunctions.push(
    +          goog.bind(this.removeUnusedLayerRenderers_, this));
    +      return;
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglimagelayerrenderer.js b/mapbox-gl/ol/ol/renderer/webgl/webglimagelayerrenderer.js
    new file mode 100644
    index 0000000000..51f8394b23
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglimagelayerrenderer.js
    @@ -0,0 +1,325 @@
    +goog.provide('ol.renderer.webgl.ImageLayer');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('goog.vec.Mat4');
    +goog.require('goog.webgl');
    +goog.require('ol.Coordinate');
    +goog.require('ol.Extent');
    +goog.require('ol.ImageBase');
    +goog.require('ol.ViewHint');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Image');
    +goog.require('ol.proj');
    +goog.require('ol.renderer.webgl.Layer');
    +goog.require('ol.source.ImageVector');
    +goog.require('ol.vec.Mat4');
    +goog.require('ol.webgl.Context');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.webgl.Layer}
    + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
    + * @param {ol.layer.Image} imageLayer Tile layer.
    + */
    +ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) {
    +
    +  goog.base(this, mapRenderer, imageLayer);
    +
    +  /**
    +   * The last rendered image.
    +   * @private
    +   * @type {?ol.ImageBase}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.hitCanvasContext_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?goog.vec.Mat4.Number}
    +   */
    +  this.hitTransformationMatrix_ = null;
    +
    +};
    +goog.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer);
    +
    +
    +/**
    + * @param {ol.ImageBase} image Image.
    + * @private
    + * @return {WebGLTexture} Texture.
    + */
    +ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) {
    +
    +  // We meet the conditions to work with non-power of two textures.
    +  // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support
    +  // http://learningwebgl.com/blog/?p=2101
    +
    +  var imageElement = image.getImage();
    +  var gl = this.mapRenderer.getGL();
    +
    +  return ol.webgl.Context.createTexture(
    +      gl, imageElement, goog.webgl.CLAMP_TO_EDGE, goog.webgl.CLAMP_TO_EDGE);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  var layer = this.getLayer();
    +  var source = layer.getSource();
    +  var resolution = frameState.viewState.resolution;
    +  var rotation = frameState.viewState.rotation;
    +  var skippedFeatureUids = frameState.skippedFeatureUids;
    +  return source.forEachFeatureAtCoordinate(
    +      coordinate, resolution, rotation, skippedFeatureUids,
    +
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {?} Callback result.
    +       */
    +      function(feature) {
    +        return callback.call(thisArg, feature, layer);
    +      });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.ImageLayer.prototype.prepareFrame =
    +    function(frameState, layerState, context) {
    +
    +  var gl = this.mapRenderer.getGL();
    +
    +  var viewState = frameState.viewState;
    +  var viewCenter = viewState.center;
    +  var viewResolution = viewState.resolution;
    +  var viewRotation = viewState.rotation;
    +
    +  var image = this.image_;
    +  var texture = this.texture;
    +  var imageLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(imageLayer, ol.layer.Image);
    +  var imageSource = imageLayer.getSource();
    +
    +  var hints = frameState.viewHints;
    +
    +  var renderedExtent = frameState.extent;
    +  if (goog.isDef(layerState.extent)) {
    +    renderedExtent = ol.extent.getIntersection(
    +        renderedExtent, layerState.extent);
    +  }
    +  if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
    +      !ol.extent.isEmpty(renderedExtent)) {
    +    var projection = viewState.projection;
    +    var sourceProjection = imageSource.getProjection();
    +    if (!goog.isNull(sourceProjection)) {
    +      goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection));
    +      projection = sourceProjection;
    +    }
    +    var image_ = imageSource.getImage(renderedExtent, viewResolution,
    +        frameState.pixelRatio, projection);
    +    if (!goog.isNull(image_)) {
    +      var loaded = this.loadImage(image_);
    +      if (loaded) {
    +        image = image_;
    +        texture = this.createTexture_(image_);
    +        if (!goog.isNull(this.texture)) {
    +          frameState.postRenderFunctions.push(
    +              goog.partial(
    +                  /**
    +                   * @param {WebGLRenderingContext} gl GL.
    +                   * @param {WebGLTexture} texture Texture.
    +                   */
    +                  function(gl, texture) {
    +                    if (!gl.isContextLost()) {
    +                      gl.deleteTexture(texture);
    +                    }
    +                  }, gl, this.texture));
    +        }
    +      }
    +    }
    +  }
    +
    +  if (!goog.isNull(image)) {
    +    goog.asserts.assert(!goog.isNull(texture));
    +
    +    var canvas = this.mapRenderer.getContext().getCanvas();
    +
    +    this.updateProjectionMatrix_(canvas.width, canvas.height,
    +        viewCenter, viewResolution, viewRotation, image.getExtent());
    +    this.hitTransformationMatrix_ = null;
    +
    +    // Translate and scale to flip the Y coord.
    +    var texCoordMatrix = this.texCoordMatrix;
    +    goog.vec.Mat4.makeIdentity(texCoordMatrix);
    +    goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1);
    +    goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0);
    +
    +    this.image_ = image;
    +    this.texture = texture;
    +
    +    this.updateAttributions(frameState.attributions, image.getAttributions());
    +    this.updateLogos(frameState, imageSource);
    +  }
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {number} canvasWidth Canvas width.
    + * @param {number} canvasHeight Canvas height.
    + * @param {ol.Coordinate} viewCenter View center.
    + * @param {number} viewResolution View resolution.
    + * @param {number} viewRotation View rotation.
    + * @param {ol.Extent} imageExtent Image extent.
    + * @private
    + */
    +ol.renderer.webgl.ImageLayer.prototype.updateProjectionMatrix_ =
    +    function(canvasWidth, canvasHeight, viewCenter,
    +        viewResolution, viewRotation, imageExtent) {
    +
    +  var canvasExtentWidth = canvasWidth * viewResolution;
    +  var canvasExtentHeight = canvasHeight * viewResolution;
    +
    +  var projectionMatrix = this.projectionMatrix;
    +  goog.vec.Mat4.makeIdentity(projectionMatrix);
    +  goog.vec.Mat4.scale(projectionMatrix,
    +      2 / canvasExtentWidth, 2 / canvasExtentHeight, 1);
    +  goog.vec.Mat4.rotateZ(projectionMatrix, -viewRotation);
    +  goog.vec.Mat4.translate(projectionMatrix,
    +      imageExtent[0] - viewCenter[0],
    +      imageExtent[1] - viewCenter[1],
    +      0);
    +  goog.vec.Mat4.scale(projectionMatrix,
    +      (imageExtent[2] - imageExtent[0]) / 2,
    +      (imageExtent[3] - imageExtent[1]) / 2,
    +      1);
    +  goog.vec.Mat4.translate(projectionMatrix, 1, 1, 0);
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.ImageLayer.prototype.hasFeatureAtCoordinate =
    +    function(coordinate, frameState) {
    +  var hasFeature = this.forEachFeatureAtCoordinate(
    +      coordinate, frameState, goog.functions.TRUE, this);
    +  return goog.isDef(hasFeature);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.ImageLayer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  if (goog.isNull(this.image_) || goog.isNull(this.image_.getImage())) {
    +    return undefined;
    +  }
    +
    +  if (this.getLayer().getSource() instanceof ol.source.ImageVector) {
    +    // for ImageVector sources use the original hit-detection logic,
    +    // so that for example also transparent polygons are detected
    +    var coordinate = pixel.slice();
    +    ol.vec.Mat4.multVec2(
    +        frameState.pixelToCoordinateMatrix, coordinate, coordinate);
    +    var hasFeature = this.forEachFeatureAtCoordinate(
    +        coordinate, frameState, goog.functions.TRUE, this);
    +
    +    if (hasFeature) {
    +      return callback.call(thisArg, this.getLayer());
    +    } else {
    +      return undefined;
    +    }
    +  } else {
    +    var imageSize =
    +        [this.image_.getImage().width, this.image_.getImage().height];
    +
    +    if (goog.isNull(this.hitTransformationMatrix_)) {
    +      this.hitTransformationMatrix_ = this.getHitTransformationMatrix_(
    +          frameState.size, imageSize);
    +    }
    +
    +    var pixelOnFrameBuffer = [0, 0];
    +    ol.vec.Mat4.multVec2(
    +        this.hitTransformationMatrix_, pixel, pixelOnFrameBuffer);
    +
    +    if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] ||
    +        pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) {
    +      // outside the image, no need to check
    +      return undefined;
    +    }
    +
    +    if (goog.isNull(this.hitCanvasContext_)) {
    +      this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1);
    +    }
    +
    +    this.hitCanvasContext_.clearRect(0, 0, 1, 1);
    +    this.hitCanvasContext_.drawImage(this.image_.getImage(),
    +        pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1);
    +
    +    var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data;
    +    if (imageData[3] > 0) {
    +      return callback.call(thisArg, this.getLayer());
    +    } else {
    +      return undefined;
    +    }
    +  }
    +};
    +
    +
    +/**
    + * The transformation matrix to get the pixel on the image for a
    + * pixel on the map.
    + * @param {ol.Size} mapSize
    + * @param {ol.Size} imageSize
    + * @return {goog.vec.Mat4.Number}
    + * @private
    + */
    +ol.renderer.webgl.ImageLayer.prototype.getHitTransformationMatrix_ =
    +    function(mapSize, imageSize) {
    +  // the first matrix takes a map pixel, flips the y-axis and scales to
    +  // a range between -1 ... 1
    +  var mapCoordMatrix = goog.vec.Mat4.createNumber();
    +  goog.vec.Mat4.makeIdentity(mapCoordMatrix);
    +  goog.vec.Mat4.translate(mapCoordMatrix, -1, -1, 0);
    +  goog.vec.Mat4.scale(mapCoordMatrix, 2 / mapSize[0], 2 / mapSize[1], 1);
    +  goog.vec.Mat4.translate(mapCoordMatrix, 0, mapSize[1], 0);
    +  goog.vec.Mat4.scale(mapCoordMatrix, 1, -1, 1);
    +
    +  // the second matrix is the inverse of the projection matrix used in the
    +  // shader for drawing
    +  var projectionMatrixInv = goog.vec.Mat4.createNumber();
    +  goog.vec.Mat4.invert(this.projectionMatrix, projectionMatrixInv);
    +
    +  // the third matrix scales to the image dimensions and flips the y-axis again
    +  var imageCoordMatrix = goog.vec.Mat4.createNumber();
    +  goog.vec.Mat4.makeIdentity(imageCoordMatrix);
    +  goog.vec.Mat4.translate(imageCoordMatrix, 0, imageSize[1], 0);
    +  goog.vec.Mat4.scale(imageCoordMatrix, 1, -1, 1);
    +  goog.vec.Mat4.scale(imageCoordMatrix, imageSize[0] / 2, imageSize[1] / 2, 1);
    +  goog.vec.Mat4.translate(imageCoordMatrix, 1, 1, 0);
    +
    +  var transformMatrix = goog.vec.Mat4.createNumber();
    +  goog.vec.Mat4.multMat(
    +      imageCoordMatrix, projectionMatrixInv, transformMatrix);
    +  goog.vec.Mat4.multMat(
    +      transformMatrix, mapCoordMatrix, transformMatrix);
    +
    +  return transformMatrix;
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webgllayerrenderer.js b/mapbox-gl/ol/ol/renderer/webgl/webgllayerrenderer.js
    new file mode 100644
    index 0000000000..45b66f2512
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webgllayerrenderer.js
    @@ -0,0 +1,296 @@
    +goog.provide('ol.renderer.webgl.Layer');
    +
    +goog.require('goog.vec.Mat4');
    +goog.require('goog.webgl');
    +goog.require('ol.color.Matrix');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.webgl.Immediate');
    +goog.require('ol.renderer.Layer');
    +goog.require('ol.renderer.webgl.map.shader.Color');
    +goog.require('ol.renderer.webgl.map.shader.Default');
    +goog.require('ol.webgl.Buffer');
    +goog.require('ol.webgl.Context');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Layer}
    + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
    + * @param {ol.layer.Layer} layer Layer.
    + */
    +ol.renderer.webgl.Layer = function(mapRenderer, layer) {
    +
    +  goog.base(this, layer);
    +
    +  /**
    +   * @protected
    +   * @type {ol.renderer.webgl.Map}
    +   */
    +  this.mapRenderer = mapRenderer;
    +
    +  /**
    +   * @private
    +   * @type {ol.webgl.Buffer}
    +   */
    +  this.arrayBuffer_ = new ol.webgl.Buffer([
    +    -1, -1, 0, 0,
    +    1, -1, 1, 0,
    +    -1, 1, 0, 1,
    +    1, 1, 1, 1
    +  ]);
    +
    +  /**
    +   * @protected
    +   * @type {WebGLTexture}
    +   */
    +  this.texture = null;
    +
    +  /**
    +   * @protected
    +   * @type {WebGLFramebuffer}
    +   */
    +  this.framebuffer = null;
    +
    +  /**
    +   * @protected
    +   * @type {number|undefined}
    +   */
    +  this.framebufferDimension = undefined;
    +
    +  /**
    +   * @protected
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.texCoordMatrix = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @protected
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.projectionMatrix = goog.vec.Mat4.createNumberIdentity();
    +
    +  /**
    +   * @private
    +   * @type {ol.color.Matrix}
    +   */
    +  this.colorMatrix_ = new ol.color.Matrix();
    +
    +  /**
    +   * @private
    +   * @type {ol.renderer.webgl.map.shader.Color.Locations}
    +   */
    +  this.colorLocations_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.renderer.webgl.map.shader.Default.Locations}
    +   */
    +  this.defaultLocations_ = null;
    +
    +};
    +goog.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer);
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {number} framebufferDimension Framebuffer dimension.
    + * @protected
    + */
    +ol.renderer.webgl.Layer.prototype.bindFramebuffer =
    +    function(frameState, framebufferDimension) {
    +
    +  var gl = this.mapRenderer.getGL();
    +
    +  if (!goog.isDef(this.framebufferDimension) ||
    +      this.framebufferDimension != framebufferDimension) {
    +
    +    frameState.postRenderFunctions.push(
    +        goog.partial(
    +            /**
    +             * @param {WebGLRenderingContext} gl GL.
    +             * @param {WebGLFramebuffer} framebuffer Framebuffer.
    +             * @param {WebGLTexture} texture Texture.
    +             */
    +            function(gl, framebuffer, texture) {
    +              if (!gl.isContextLost()) {
    +                gl.deleteFramebuffer(framebuffer);
    +                gl.deleteTexture(texture);
    +              }
    +            }, gl, this.framebuffer, this.texture));
    +
    +    var texture = ol.webgl.Context.createEmptyTexture(
    +        gl, framebufferDimension, framebufferDimension);
    +
    +    var framebuffer = gl.createFramebuffer();
    +    gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer);
    +    gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER,
    +        goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0);
    +
    +    this.texture = texture;
    +    this.framebuffer = framebuffer;
    +    this.framebufferDimension = framebufferDimension;
    +
    +  } else {
    +    gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer);
    +  }
    +
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @param {ol.webgl.Context} context Context.
    + */
    +ol.renderer.webgl.Layer.prototype.composeFrame =
    +    function(frameState, layerState, context) {
    +
    +  this.dispatchComposeEvent_(
    +      ol.render.EventType.PRECOMPOSE, context, frameState);
    +
    +  context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
    +
    +  var gl = context.getGL();
    +
    +  var useColor =
    +      layerState.brightness ||
    +      layerState.contrast != 1 ||
    +      layerState.hue ||
    +      layerState.saturation != 1;
    +
    +  var fragmentShader, vertexShader;
    +  if (useColor) {
    +    fragmentShader = ol.renderer.webgl.map.shader.ColorFragment.getInstance();
    +    vertexShader = ol.renderer.webgl.map.shader.ColorVertex.getInstance();
    +  } else {
    +    fragmentShader =
    +        ol.renderer.webgl.map.shader.DefaultFragment.getInstance();
    +    vertexShader = ol.renderer.webgl.map.shader.DefaultVertex.getInstance();
    +  }
    +
    +  var program = context.getProgram(fragmentShader, vertexShader);
    +
    +  // FIXME colorLocations_ and defaultLocations_ should be shared somehow
    +  var locations;
    +  if (useColor) {
    +    if (goog.isNull(this.colorLocations_)) {
    +      locations =
    +          new ol.renderer.webgl.map.shader.Color.Locations(gl, program);
    +      this.colorLocations_ = locations;
    +    } else {
    +      locations = this.colorLocations_;
    +    }
    +  } else {
    +    if (goog.isNull(this.defaultLocations_)) {
    +      locations =
    +          new ol.renderer.webgl.map.shader.Default.Locations(gl, program);
    +      this.defaultLocations_ = locations;
    +    } else {
    +      locations = this.defaultLocations_;
    +    }
    +  }
    +
    +  if (context.useProgram(program)) {
    +    gl.enableVertexAttribArray(locations.a_position);
    +    gl.vertexAttribPointer(
    +        locations.a_position, 2, goog.webgl.FLOAT, false, 16, 0);
    +    gl.enableVertexAttribArray(locations.a_texCoord);
    +    gl.vertexAttribPointer(
    +        locations.a_texCoord, 2, goog.webgl.FLOAT, false, 16, 8);
    +    gl.uniform1i(locations.u_texture, 0);
    +  }
    +
    +  gl.uniformMatrix4fv(
    +      locations.u_texCoordMatrix, false, this.getTexCoordMatrix());
    +  gl.uniformMatrix4fv(locations.u_projectionMatrix, false,
    +      this.getProjectionMatrix());
    +  if (useColor) {
    +    gl.uniformMatrix4fv(locations.u_colorMatrix, false,
    +        this.colorMatrix_.getMatrix(
    +            layerState.brightness,
    +            layerState.contrast,
    +            layerState.hue,
    +            layerState.saturation
    +        ));
    +  }
    +  gl.uniform1f(locations.u_opacity, layerState.opacity);
    +  gl.bindTexture(goog.webgl.TEXTURE_2D, this.getTexture());
    +  gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
    +
    +  this.dispatchComposeEvent_(
    +      ol.render.EventType.POSTCOMPOSE, context, frameState);
    +
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {ol.webgl.Context} context WebGL context.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ =
    +    function(type, context, frameState) {
    +  var layer = this.getLayer();
    +  if (layer.hasListener(type)) {
    +    var viewState = frameState.viewState;
    +    var resolution = viewState.resolution;
    +    var pixelRatio = frameState.pixelRatio;
    +    var extent = frameState.extent;
    +    var center = viewState.center;
    +    var rotation = viewState.rotation;
    +    var size = frameState.size;
    +
    +    var render = new ol.render.webgl.Immediate(
    +        context, center, resolution, rotation, size, extent, pixelRatio);
    +    var composeEvent = new ol.render.Event(
    +        type, layer, render, null, frameState, null, context);
    +    layer.dispatchEvent(composeEvent);
    +  }
    +};
    +
    +
    +/**
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = function() {
    +  return this.texCoordMatrix;
    +};
    +
    +
    +/**
    + * @return {WebGLTexture} Texture.
    + */
    +ol.renderer.webgl.Layer.prototype.getTexture = function() {
    +  return this.texture;
    +};
    +
    +
    +/**
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.renderer.webgl.Layer.prototype.getProjectionMatrix = function() {
    +  return this.projectionMatrix;
    +};
    +
    +
    +/**
    + * Handle webglcontextlost.
    + */
    +ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() {
    +  this.texture = null;
    +  this.framebuffer = null;
    +  this.framebufferDimension = undefined;
    +};
    +
    +
    +/**
    + * @param {olx.FrameState} frameState Frame state.
    + * @param {ol.layer.LayerState} layerState Layer state.
    + * @param {ol.webgl.Context} context Context.
    + * @return {boolean} whether composeFrame should be called.
    + */
    +ol.renderer.webgl.Layer.prototype.prepareFrame = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglmapcolor.glsl b/mapbox-gl/ol/ol/renderer/webgl/webglmapcolor.glsl
    new file mode 100644
    index 0000000000..4ed15cd954
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglmapcolor.glsl
    @@ -0,0 +1,32 @@
    +//! NAMESPACE=ol.renderer.webgl.map.shader.Color
    +//! CLASS=ol.renderer.webgl.map.shader.Color
    +
    +
    +//! COMMON
    +varying vec2 v_texCoord;
    +
    +
    +//! VERTEX
    +attribute vec2 a_position;
    +attribute vec2 a_texCoord;
    +
    +uniform mat4 u_texCoordMatrix;
    +uniform mat4 u_projectionMatrix;
    +
    +void main(void) {
    +  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);
    +  v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;
    +}
    +
    +
    +//! FRAGMENT
    +// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp
    +uniform mat4 u_colorMatrix;
    +uniform float u_opacity;
    +uniform sampler2D u_texture;
    +
    +void main(void) {
    +  vec4 texColor = texture2D(u_texture, v_texCoord);
    +  gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;
    +  gl_FragColor.a = texColor.a * u_opacity;
    +}
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglmapcolorshader.js b/mapbox-gl/ol/ol/renderer/webgl/webglmapcolorshader.js
    new file mode 100644
    index 0000000000..eeff8162d2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglmapcolorshader.js
    @@ -0,0 +1,129 @@
    +// This file is automatically generated, do not edit
    +goog.provide('ol.renderer.webgl.map.shader.Color');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.ColorFragment = function() {
    +  goog.base(this, ol.renderer.webgl.map.shader.ColorFragment.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.map.shader.ColorFragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter(ol.renderer.webgl.map.shader.ColorFragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\n// @see https://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/filters/skia/SkiaImageFilterBuilder.cpp\nuniform mat4 u_colorMatrix;\nuniform float u_opacity;\nuniform sampler2D u_texture;\n\nvoid main(void) {\n  vec4 texColor = texture2D(u_texture, v_texCoord);\n  gl_FragColor.rgb = (u_colorMatrix * vec4(texColor.rgb, 1.)).rgb;\n  gl_FragColor.a = texColor.a * u_opacity;\n}\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform mat4 f;uniform float g;uniform sampler2D h;void main(void){vec4 texColor=texture2D(h,a);gl_FragColor.rgb=(f*vec4(texColor.rgb,1.)).rgb;gl_FragColor.a=texColor.a*g;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorFragment.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.map.shader.ColorFragment.DEBUG_SOURCE :
    +    ol.renderer.webgl.map.shader.ColorFragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.ColorVertex = function() {
    +  goog.base(this, ol.renderer.webgl.map.shader.ColorVertex.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.map.shader.ColorVertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter(ol.renderer.webgl.map.shader.ColorVertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\n\nuniform mat4 u_texCoordMatrix;\nuniform mat4 u_projectionMatrix;\n\nvoid main(void) {\n  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);\n  v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;\n}\n\n\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorVertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.ColorVertex.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.map.shader.ColorVertex.DEBUG_SOURCE :
    +    ol.renderer.webgl.map.shader.ColorVertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.Color.Locations = function(gl, program) {
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_colorMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_colorMatrix' : 'f');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_opacity = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_opacity' : 'g');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_projectionMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_projectionMatrix' : 'e');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_texCoordMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_texCoordMatrix' : 'd');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_texture = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_texture' : 'h');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_position = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_position' : 'b');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_texCoord = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_texCoord' : 'c');
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglmapdefault.glsl b/mapbox-gl/ol/ol/renderer/webgl/webglmapdefault.glsl
    new file mode 100644
    index 0000000000..b07838dc3b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglmapdefault.glsl
    @@ -0,0 +1,30 @@
    +//! NAMESPACE=ol.renderer.webgl.map.shader.Default
    +//! CLASS=ol.renderer.webgl.map.shader.Default
    +
    +
    +//! COMMON
    +varying vec2 v_texCoord;
    +
    +
    +//! VERTEX
    +attribute vec2 a_position;
    +attribute vec2 a_texCoord;
    +
    +uniform mat4 u_texCoordMatrix;
    +uniform mat4 u_projectionMatrix;
    +
    +void main(void) {
    +  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);
    +  v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;
    +}
    +
    +
    +//! FRAGMENT
    +uniform float u_opacity;
    +uniform sampler2D u_texture;
    +
    +void main(void) {
    +  vec4 texColor = texture2D(u_texture, v_texCoord);
    +  gl_FragColor.rgb = texColor.rgb;
    +  gl_FragColor.a = texColor.a * u_opacity;
    +}
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglmapdefaultshader.js b/mapbox-gl/ol/ol/renderer/webgl/webglmapdefaultshader.js
    new file mode 100644
    index 0000000000..47aff44a22
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglmapdefaultshader.js
    @@ -0,0 +1,123 @@
    +// This file is automatically generated, do not edit
    +goog.provide('ol.renderer.webgl.map.shader.Default');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.DefaultFragment = function() {
    +  goog.base(this, ol.renderer.webgl.map.shader.DefaultFragment.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.map.shader.DefaultFragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter(ol.renderer.webgl.map.shader.DefaultFragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform float u_opacity;\nuniform sampler2D u_texture;\n\nvoid main(void) {\n  vec4 texColor = texture2D(u_texture, v_texCoord);\n  gl_FragColor.rgb = texColor.rgb;\n  gl_FragColor.a = texColor.a * u_opacity;\n}\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultFragment.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.map.shader.DefaultFragment.DEBUG_SOURCE :
    +    ol.renderer.webgl.map.shader.DefaultFragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.DefaultVertex = function() {
    +  goog.base(this, ol.renderer.webgl.map.shader.DefaultVertex.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.map.shader.DefaultVertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter(ol.renderer.webgl.map.shader.DefaultVertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\n\nuniform mat4 u_texCoordMatrix;\nuniform mat4 u_projectionMatrix;\n\nvoid main(void) {\n  gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);\n  v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;\n}\n\n\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultVertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.map.shader.DefaultVertex.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.map.shader.DefaultVertex.DEBUG_SOURCE :
    +    ol.renderer.webgl.map.shader.DefaultVertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +ol.renderer.webgl.map.shader.Default.Locations = function(gl, program) {
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_opacity = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_opacity' : 'f');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_projectionMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_projectionMatrix' : 'e');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_texCoordMatrix = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_texCoordMatrix' : 'd');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_texture = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_texture' : 'g');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_position = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_position' : 'b');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_texCoord = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_texCoord' : 'c');
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglmaprenderer.js b/mapbox-gl/ol/ol/renderer/webgl/webglmaprenderer.js
    new file mode 100644
    index 0000000000..7fd95f3580
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglmaprenderer.js
    @@ -0,0 +1,711 @@
    +// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE)
    +
    +goog.provide('ol.renderer.webgl.Map');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.log');
    +goog.require('goog.log.Logger');
    +goog.require('goog.object');
    +goog.require('goog.style');
    +goog.require('goog.webgl');
    +goog.require('ol');
    +goog.require('ol.RendererType');
    +goog.require('ol.css');
    +goog.require('ol.dom');
    +goog.require('ol.layer.Image');
    +goog.require('ol.layer.Layer');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.Event');
    +goog.require('ol.render.EventType');
    +goog.require('ol.render.webgl.Immediate');
    +goog.require('ol.render.webgl.ReplayGroup');
    +goog.require('ol.renderer.Map');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.renderer.webgl.ImageLayer');
    +goog.require('ol.renderer.webgl.Layer');
    +goog.require('ol.renderer.webgl.TileLayer');
    +goog.require('ol.renderer.webgl.VectorLayer');
    +goog.require('ol.source.State');
    +goog.require('ol.structs.LRUCache');
    +goog.require('ol.structs.PriorityQueue');
    +goog.require('ol.webgl');
    +goog.require('ol.webgl.Context');
    +goog.require('ol.webgl.WebGLContextEventType');
    +
    +
    +/**
    + * @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}}
    + */
    +ol.renderer.webgl.TextureCacheEntry;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.Map}
    + * @param {Element} container Container.
    + * @param {ol.Map} map Map.
    + */
    +ol.renderer.webgl.Map = function(container, map) {
    +
    +  goog.base(this, container, map);
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = /** @type {HTMLCanvasElement} */
    +      (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +  this.canvas_.style.width = '100%';
    +  this.canvas_.style.height = '100%';
    +  this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
    +  goog.dom.insertChildAt(container, this.canvas_, 0);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.clipTileCanvasSize_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.clipTileContext_ = ol.dom.createCanvasContext2D();
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.renderedVisible_ = true;
    +
    +  /**
    +   * @private
    +   * @type {WebGLRenderingContext}
    +   */
    +  this.gl_ = ol.webgl.getContext(this.canvas_, {
    +    antialias: true,
    +    depth: false,
    +    failIfMajorPerformanceCaveat: true,
    +    preserveDrawingBuffer: false,
    +    stencil: true
    +  });
    +  goog.asserts.assert(!goog.isNull(this.gl_));
    +
    +  /**
    +   * @private
    +   * @type {ol.webgl.Context}
    +   */
    +  this.context_ = new ol.webgl.Context(this.canvas_, this.gl_);
    +
    +  goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST,
    +      this.handleWebGLContextLost, false, this);
    +  goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
    +      this.handleWebGLContextRestored, false, this);
    +
    +  /**
    +   * @private
    +   * @type {ol.structs.LRUCache.<ol.renderer.webgl.TextureCacheEntry|null>}
    +   */
    +  this.textureCache_ = new ol.structs.LRUCache();
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.focus_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.structs.PriorityQueue.<Array>}
    +   */
    +  this.tileTextureQueue_ = new ol.structs.PriorityQueue(
    +      goog.bind(
    +          /**
    +           * @param {Array.<*>} element Element.
    +           * @return {number} Priority.
    +           * @this {ol.renderer.webgl.Map}
    +           */
    +          function(element) {
    +            var tileCenter = /** @type {ol.Coordinate} */ (element[1]);
    +            var tileResolution = /** @type {number} */ (element[2]);
    +            var deltaX = tileCenter[0] - this.focus_[0];
    +            var deltaY = tileCenter[1] - this.focus_[1];
    +            return 65536 * Math.log(tileResolution) +
    +                Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
    +          }, this),
    +      /**
    +       * @param {Array.<*>} element Element.
    +       * @return {string} Key.
    +       */
    +      function(element) {
    +        return /** @type {ol.Tile} */ (element[0]).getKey();
    +      });
    +
    +  /**
    +   * @private
    +   * @type {ol.PostRenderFunction}
    +   */
    +  this.loadNextTileTexture_ = goog.bind(
    +      function(map, frameState) {
    +        if (!this.tileTextureQueue_.isEmpty()) {
    +          this.tileTextureQueue_.reprioritize();
    +          var element = this.tileTextureQueue_.dequeue();
    +          var tile = /** @type {ol.Tile} */ (element[0]);
    +          var tileSize = /** @type {number} */ (element[3]);
    +          var tileGutter = /** @type {number} */ (element[4]);
    +          this.bindTileTexture(
    +              tile, tileSize, tileGutter, goog.webgl.LINEAR, goog.webgl.LINEAR);
    +        }
    +      }, this);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.textureCacheFrameMarkerCount_ = 0;
    +
    +  this.initializeGL_();
    +
    +};
    +goog.inherits(ol.renderer.webgl.Map, ol.renderer.Map);
    +
    +
    +/**
    + * @param {ol.Tile} tile Tile.
    + * @param {number} tileSize Tile size.
    + * @param {number} tileGutter Tile gutter.
    + * @param {number} magFilter Mag filter.
    + * @param {number} minFilter Min filter.
    + */
    +ol.renderer.webgl.Map.prototype.bindTileTexture =
    +    function(tile, tileSize, tileGutter, magFilter, minFilter) {
    +  var gl = this.getGL();
    +  var tileKey = tile.getKey();
    +  if (this.textureCache_.containsKey(tileKey)) {
    +    var textureCacheEntry = this.textureCache_.get(tileKey);
    +    goog.asserts.assert(!goog.isNull(textureCacheEntry));
    +    gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
    +    if (textureCacheEntry.magFilter != magFilter) {
    +      gl.texParameteri(
    +          goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
    +      textureCacheEntry.magFilter = magFilter;
    +    }
    +    if (textureCacheEntry.minFilter != minFilter) {
    +      gl.texParameteri(
    +          goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, minFilter);
    +      textureCacheEntry.minFilter = minFilter;
    +    }
    +  } else {
    +    var texture = gl.createTexture();
    +    gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
    +    if (tileGutter > 0) {
    +      var clipTileCanvas = this.clipTileContext_.canvas;
    +      var clipTileContext = this.clipTileContext_;
    +      if (this.clipTileCanvasSize_ != tileSize) {
    +        clipTileCanvas.width = tileSize;
    +        clipTileCanvas.height = tileSize;
    +        this.clipTileCanvasSize_ = tileSize;
    +      } else {
    +        clipTileContext.clearRect(0, 0, tileSize, tileSize);
    +      }
    +      clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter,
    +          tileSize, tileSize, 0, 0, tileSize, tileSize);
    +      gl.texImage2D(goog.webgl.TEXTURE_2D, 0,
    +          goog.webgl.RGBA, goog.webgl.RGBA,
    +          goog.webgl.UNSIGNED_BYTE, clipTileCanvas);
    +    } else {
    +      gl.texImage2D(goog.webgl.TEXTURE_2D, 0,
    +          goog.webgl.RGBA, goog.webgl.RGBA,
    +          goog.webgl.UNSIGNED_BYTE, tile.getImage());
    +    }
    +    gl.texParameteri(
    +        goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
    +    gl.texParameteri(
    +        goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, minFilter);
    +    gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S,
    +        goog.webgl.CLAMP_TO_EDGE);
    +    gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
    +        goog.webgl.CLAMP_TO_EDGE);
    +    this.textureCache_.set(tileKey, {
    +      texture: texture,
    +      magFilter: magFilter,
    +      minFilter: minFilter
    +    });
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
    +  if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
    +    return new ol.renderer.webgl.ImageLayer(this, layer);
    +  } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
    +    return new ol.renderer.webgl.TileLayer(this, layer);
    +  } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
    +    return new ol.renderer.webgl.VectorLayer(this, layer);
    +  } else {
    +    goog.asserts.fail();
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.render.EventType} type Event type.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ =
    +    function(type, frameState) {
    +  var map = this.getMap();
    +  if (map.hasListener(type)) {
    +    var context = this.context_;
    +
    +    var extent = frameState.extent;
    +    var size = frameState.size;
    +    var viewState = frameState.viewState;
    +    var pixelRatio = frameState.pixelRatio;
    +
    +    var resolution = viewState.resolution;
    +    var center = viewState.center;
    +    var rotation = viewState.rotation;
    +    var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
    +
    +    var vectorContext = new ol.render.webgl.Immediate(context,
    +        center, resolution, rotation, size, extent, pixelRatio);
    +    var replayGroup = new ol.render.webgl.ReplayGroup(tolerance, extent);
    +    var composeEvent = new ol.render.Event(type, map, vectorContext,
    +        replayGroup, frameState, null, context);
    +    map.dispatchEvent(composeEvent);
    +
    +    replayGroup.finish(context);
    +    if (!replayGroup.isEmpty()) {
    +      // use default color values
    +      var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
    +      replayGroup.replay(context, center, resolution, rotation, size,
    +          pixelRatio, d.opacity, d.brightness, d.contrast,
    +          d.hue, d.saturation, {});
    +    }
    +    replayGroup.getDeleteResourcesFunction(context)();
    +
    +    vectorContext.flush();
    +    this.replayGroup = replayGroup;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.disposeInternal = function() {
    +  var gl = this.getGL();
    +  if (!gl.isContextLost()) {
    +    this.textureCache_.forEach(
    +        /**
    +         * @param {?ol.renderer.webgl.TextureCacheEntry} textureCacheEntry
    +         *     Texture cache entry.
    +         */
    +        function(textureCacheEntry) {
    +          if (!goog.isNull(textureCacheEntry)) {
    +            gl.deleteTexture(textureCacheEntry.texture);
    +          }
    +        });
    +  }
    +  goog.dispose(this.context_);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @param {ol.Map} map Map.
    + * @param {olx.FrameState} frameState Frame state.
    + * @private
    + */
    +ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) {
    +  var gl = this.getGL();
    +  var textureCacheEntry;
    +  while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
    +      ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
    +    textureCacheEntry = this.textureCache_.peekLast();
    +    if (goog.isNull(textureCacheEntry)) {
    +      if (+this.textureCache_.peekLastKey() == frameState.index) {
    +        break;
    +      } else {
    +        --this.textureCacheFrameMarkerCount_;
    +      }
    +    } else {
    +      gl.deleteTexture(textureCacheEntry.texture);
    +    }
    +    this.textureCache_.pop();
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.webgl.Context}
    + */
    +ol.renderer.webgl.Map.prototype.getContext = function() {
    +  return this.context_;
    +};
    +
    +
    +/**
    + * @return {WebGLRenderingContext} GL.
    + */
    +ol.renderer.webgl.Map.prototype.getGL = function() {
    +  return this.gl_;
    +};
    +
    +
    +/**
    + * @return {ol.structs.PriorityQueue.<Array>} Tile texture queue.
    + */
    +ol.renderer.webgl.Map.prototype.getTileTextureQueue = function() {
    +  return this.tileTextureQueue_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.getType = function() {
    +  return ol.RendererType.WEBGL;
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Event.
    + * @protected
    + */
    +ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) {
    +  event.preventDefault();
    +  this.textureCache_.clear();
    +  this.textureCacheFrameMarkerCount_ = 0;
    +  goog.object.forEach(this.getLayerRenderers(),
    +      /**
    +       * @param {ol.renderer.Layer} layerRenderer Layer renderer.
    +       * @param {string} key Key.
    +       * @param {Object.<string, ol.renderer.Layer>} object Object.
    +       */
    +      function(layerRenderer, key, object) {
    +        goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer);
    +        var webGLLayerRenderer = /** @type {ol.renderer.webgl.Layer} */
    +            (layerRenderer);
    +        webGLLayerRenderer.handleWebGLContextLost();
    +      });
    +};
    +
    +
    +/**
    + * @protected
    + */
    +ol.renderer.webgl.Map.prototype.handleWebGLContextRestored = function() {
    +  this.initializeGL_();
    +  this.getMap().render();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
    +  var gl = this.gl_;
    +  gl.activeTexture(goog.webgl.TEXTURE0);
    +  gl.blendFuncSeparate(
    +      goog.webgl.SRC_ALPHA, goog.webgl.ONE_MINUS_SRC_ALPHA,
    +      goog.webgl.ONE, goog.webgl.ONE_MINUS_SRC_ALPHA);
    +  gl.disable(goog.webgl.CULL_FACE);
    +  gl.disable(goog.webgl.DEPTH_TEST);
    +  gl.disable(goog.webgl.SCISSOR_TEST);
    +  gl.disable(goog.webgl.STENCIL_TEST);
    +};
    +
    +
    +/**
    + * @param {ol.Tile} tile Tile.
    + * @return {boolean} Is tile texture loaded.
    + */
    +ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
    +  return this.textureCache_.containsKey(tile.getKey());
    +};
    +
    +
    +/**
    + * @private
    + * @type {goog.log.Logger}
    + */
    +ol.renderer.webgl.Map.prototype.logger_ =
    +    goog.log.getLogger('ol.renderer.webgl.Map');
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
    +
    +  var context = this.getContext();
    +  var gl = this.getGL();
    +
    +  if (gl.isContextLost()) {
    +    return false;
    +  }
    +
    +  if (goog.isNull(frameState)) {
    +    if (this.renderedVisible_) {
    +      goog.style.setElementShown(this.canvas_, false);
    +      this.renderedVisible_ = false;
    +    }
    +    return false;
    +  }
    +
    +  this.focus_ = frameState.focus;
    +
    +  this.textureCache_.set((-frameState.index).toString(), null);
    +  ++this.textureCacheFrameMarkerCount_;
    +
    +  /** @type {Array.<ol.layer.LayerState>} */
    +  var layerStatesToDraw = [];
    +  var layerStatesArray = frameState.layerStatesArray;
    +  var viewResolution = frameState.viewState.resolution;
    +  var i, ii, layerRenderer, layerState;
    +  for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    +    layerState = layerStatesArray[i];
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
    +        layerState.sourceState == ol.source.State.READY) {
    +      layerRenderer = this.getLayerRenderer(layerState.layer);
    +      goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer);
    +      if (layerRenderer.prepareFrame(frameState, layerState, context)) {
    +        layerStatesToDraw.push(layerState);
    +      }
    +    }
    +  }
    +
    +  var width = frameState.size[0] * frameState.pixelRatio;
    +  var height = frameState.size[1] * frameState.pixelRatio;
    +  if (this.canvas_.width != width || this.canvas_.height != height) {
    +    this.canvas_.width = width;
    +    this.canvas_.height = height;
    +  }
    +
    +  gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null);
    +
    +  gl.clearColor(0, 0, 0, 0);
    +  gl.clear(goog.webgl.COLOR_BUFFER_BIT);
    +  gl.enable(goog.webgl.BLEND);
    +  gl.viewport(0, 0, this.canvas_.width, this.canvas_.height);
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
    +
    +  for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) {
    +    layerState = layerStatesToDraw[i];
    +    layerRenderer = this.getLayerRenderer(layerState.layer);
    +    goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer);
    +    layerRenderer.composeFrame(frameState, layerState, context);
    +  }
    +
    +  if (!this.renderedVisible_) {
    +    goog.style.setElementShown(this.canvas_, true);
    +    this.renderedVisible_ = true;
    +  }
    +
    +  this.calculateMatrices2D(frameState);
    +
    +  if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
    +      ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
    +    frameState.postRenderFunctions.push(goog.bind(this.expireCache_, this));
    +  }
    +
    +  if (!this.tileTextureQueue_.isEmpty()) {
    +    frameState.postRenderFunctions.push(this.loadNextTileTexture_);
    +    frameState.animate = true;
    +  }
    +
    +  this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState);
    +
    +  this.scheduleRemoveUnusedLayerRenderers(frameState);
    +  this.scheduleExpireIconCache(frameState);
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg,
    +        layerFilter, thisArg2) {
    +  var result;
    +
    +  if (this.getGL().isContextLost()) {
    +    return false;
    +  }
    +
    +  var context = this.getContext();
    +  var viewState = frameState.viewState;
    +
    +  // do the hit-detection for the overlays first
    +  if (!goog.isNull(this.replayGroup)) {
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +
    +    // use default color values
    +    var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
    +
    +    result = this.replayGroup.forEachFeatureAtCoordinate(coordinate,
    +        context, viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio,
    +        d.opacity, d.brightness, d.contrast, d.hue, d.saturation, {},
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback.call(thisArg, feature, null);
    +          }
    +        });
    +    if (result) {
    +      return result;
    +    }
    +  }
    +  var layerStates = frameState.layerStatesArray;
    +  var numLayers = layerStates.length;
    +  var i;
    +  for (i = numLayers - 1; i >= 0; --i) {
    +    var layerState = layerStates[i];
    +    var layer = layerState.layer;
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
    +        layerFilter.call(thisArg2, layer)) {
    +      var layerRenderer = this.getLayerRenderer(layer);
    +      result = layerRenderer.forEachFeatureAtCoordinate(
    +          coordinate, frameState, callback, thisArg);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.hasFeatureAtCoordinate =
    +    function(coordinate, frameState, layerFilter, thisArg) {
    +  var hasFeature = false;
    +
    +  if (this.getGL().isContextLost()) {
    +    return false;
    +  }
    +
    +  var context = this.getContext();
    +  var viewState = frameState.viewState;
    +
    +  // do the hit-detection for the overlays first
    +  if (!goog.isNull(this.replayGroup)) {
    +    // use default color values
    +    var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
    +
    +    hasFeature = this.replayGroup.hasFeatureAtCoordinate(coordinate,
    +        context, viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio,
    +        d.opacity, d.brightness, d.contrast, d.hue, d.saturation, {});
    +    if (hasFeature) {
    +      return true;
    +    }
    +  }
    +  var layerStates = frameState.layerStatesArray;
    +  var numLayers = layerStates.length;
    +  var i;
    +  for (i = numLayers - 1; i >= 0; --i) {
    +    var layerState = layerStates[i];
    +    var layer = layerState.layer;
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
    +        layerFilter.call(thisArg, layer)) {
    +      var layerRenderer = this.getLayerRenderer(layer);
    +      hasFeature =
    +          layerRenderer.hasFeatureAtCoordinate(coordinate, frameState);
    +      if (hasFeature) {
    +        return true;
    +      }
    +    }
    +  }
    +  return hasFeature;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.Map.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg,
    +        layerFilter, thisArg2) {
    +  if (this.getGL().isContextLost()) {
    +    return false;
    +  }
    +
    +  var context = this.getContext();
    +  var viewState = frameState.viewState;
    +  var result;
    +
    +  // do the hit-detection for the overlays first
    +  if (!goog.isNull(this.replayGroup)) {
    +    // use default color values
    +    var d = ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_;
    +    var coordinate = this.getMap().getCoordinateFromPixel(pixel);
    +
    +    var hasFeature = this.replayGroup.hasFeatureAtCoordinate(coordinate,
    +        context, viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio,
    +        d.opacity, d.brightness, d.contrast, d.hue, d.saturation, {});
    +    if (hasFeature) {
    +      result = callback.call(thisArg, null);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  var layerStates = frameState.layerStatesArray;
    +  var numLayers = layerStates.length;
    +  var i;
    +  for (i = numLayers - 1; i >= 0; --i) {
    +    var layerState = layerStates[i];
    +    var layer = layerState.layer;
    +    if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) &&
    +        layerFilter.call(thisArg, layer)) {
    +      var layerRenderer = this.getLayerRenderer(layer);
    +      result = layerRenderer.forEachLayerAtPixel(
    +          pixel, frameState, callback, thisArg);
    +      if (result) {
    +        return result;
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @private
    + * @const
    + */
    +ol.renderer.webgl.Map.DEFAULT_COLOR_VALUES_ = {
    +  opacity: 1,
    +  brightness: 0,
    +  contrast: 1,
    +  hue: 0,
    +  saturation: 1
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webgltilelayer.glsl b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayer.glsl
    new file mode 100644
    index 0000000000..5262f97c26
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayer.glsl
    @@ -0,0 +1,25 @@
    +//! NAMESPACE=ol.renderer.webgl.tilelayer.shader
    +//! CLASS=ol.renderer.webgl.tilelayer.shader.
    +
    +
    +//! COMMON
    +varying vec2 v_texCoord;
    +
    +
    +//! VERTEX
    +attribute vec2 a_position;
    +attribute vec2 a_texCoord;
    +uniform vec4 u_tileOffset;
    +
    +void main(void) {
    +  gl_Position = vec4(a_position * u_tileOffset.xy + u_tileOffset.zw, 0., 1.);
    +  v_texCoord = a_texCoord;
    +}
    +
    +
    +//! FRAGMENT
    +uniform sampler2D u_texture;
    +
    +void main(void) {
    +  gl_FragColor = texture2D(u_texture, v_texCoord);
    +}
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webgltilelayerrenderer.js b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayerrenderer.js
    new file mode 100644
    index 0000000000..311608ff1a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayerrenderer.js
    @@ -0,0 +1,393 @@
    +// FIXME large resolutions lead to too large framebuffers :-(
    +// FIXME animated shaders! check in redraw
    +
    +goog.provide('ol.renderer.webgl.TileLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.vec.Mat4');
    +goog.require('goog.vec.Vec4');
    +goog.require('goog.webgl');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileState');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Tile');
    +goog.require('ol.math');
    +goog.require('ol.renderer.webgl.Layer');
    +goog.require('ol.renderer.webgl.tilelayer.shader');
    +goog.require('ol.tilecoord');
    +goog.require('ol.vec.Mat4');
    +goog.require('ol.webgl.Buffer');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.webgl.Layer}
    + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
    + * @param {ol.layer.Tile} tileLayer Tile layer.
    + */
    +ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
    +
    +  goog.base(this, mapRenderer, tileLayer);
    +
    +  /**
    +   * @private
    +   * @type {ol.webgl.shader.Fragment}
    +   */
    +  this.fragmentShader_ =
    +      ol.renderer.webgl.tilelayer.shader.Fragment.getInstance();
    +
    +  /**
    +   * @private
    +   * @type {ol.webgl.shader.Vertex}
    +   */
    +  this.vertexShader_ = ol.renderer.webgl.tilelayer.shader.Vertex.getInstance();
    +
    +  /**
    +   * @private
    +   * @type {ol.renderer.webgl.tilelayer.shader.Locations}
    +   */
    +  this.locations_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.webgl.Buffer}
    +   */
    +  this.renderArrayBuffer_ = new ol.webgl.Buffer([
    +    0, 0, 0, 1,
    +    1, 0, 1, 1,
    +    0, 1, 0, 0,
    +    1, 1, 1, 0
    +  ]);
    +
    +  /**
    +   * @private
    +   * @type {ol.TileRange}
    +   */
    +  this.renderedTileRange_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.renderedFramebufferExtent_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = -1;
    +
    +};
    +goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
    +  var context = this.mapRenderer.getContext();
    +  context.deleteBuffer(this.renderArrayBuffer_);
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * Create a function that adds loaded tiles to the tile lookup.
    + * @param {ol.source.Tile} source Tile source.
    + * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
    + *     tiles by zoom level.
    + * @return {function(number, ol.TileRange):boolean} A function that can be
    + *     called with a zoom level and a tile range to add loaded tiles to the
    + *     lookup.
    + * @protected
    + */
    +ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder =
    +    function(source, tiles) {
    +  var mapRenderer = this.mapRenderer;
    +
    +  return (
    +      /**
    +       * @param {number} zoom Zoom level.
    +       * @param {ol.TileRange} tileRange Tile range.
    +       * @return {boolean} The tile range is fully loaded.
    +       */
    +      function(zoom, tileRange) {
    +        return source.forEachLoadedTile(zoom, tileRange, function(tile) {
    +          var loaded = mapRenderer.isTileTextureLoaded(tile);
    +          if (loaded) {
    +            if (!tiles[zoom]) {
    +              tiles[zoom] = {};
    +            }
    +            tiles[zoom][tile.tileCoord.toString()] = tile;
    +          }
    +          return loaded;
    +        });
    +      });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
    +  goog.base(this, 'handleWebGLContextLost');
    +  this.locations_ = null;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.TileLayer.prototype.prepareFrame =
    +    function(frameState, layerState, context) {
    +
    +  var mapRenderer = this.mapRenderer;
    +  var gl = context.getGL();
    +
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +
    +  var tileLayer = this.getLayer();
    +  goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile);
    +  var tileSource = tileLayer.getSource();
    +  var tileGrid = tileSource.getTileGridForProjection(projection);
    +  var z = tileGrid.getZForResolution(viewState.resolution);
    +  var tileResolution = tileGrid.getResolution(z);
    +
    +  var tilePixelSize =
    +      tileSource.getTilePixelSize(z, frameState.pixelRatio, projection);
    +  var pixelRatio = tilePixelSize / tileGrid.getTileSize(z);
    +  var tilePixelResolution = tileResolution / pixelRatio;
    +  var tileGutter = tileSource.getGutter();
    +
    +  var center = viewState.center;
    +  var extent;
    +  if (tileResolution == viewState.resolution) {
    +    center = this.snapCenterToPixel(center, tileResolution, frameState.size);
    +    extent = ol.extent.getForViewAndSize(
    +        center, tileResolution, viewState.rotation, frameState.size);
    +  } else {
    +    extent = frameState.extent;
    +  }
    +  var tileRange = tileGrid.getTileRangeForExtentAndResolution(
    +      extent, tileResolution);
    +
    +  var framebufferExtent;
    +  if (!goog.isNull(this.renderedTileRange_) &&
    +      this.renderedTileRange_.equals(tileRange) &&
    +      this.renderedRevision_ == tileSource.getRevision()) {
    +    framebufferExtent = this.renderedFramebufferExtent_;
    +  } else {
    +
    +    var tileRangeSize = tileRange.getSize();
    +
    +    var maxDimension = Math.max(
    +        tileRangeSize[0] * tilePixelSize, tileRangeSize[1] * tilePixelSize);
    +    var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension);
    +    var framebufferExtentDimension = tilePixelResolution * framebufferDimension;
    +    var origin = tileGrid.getOrigin(z);
    +    var minX = origin[0] + tileRange.minX * tilePixelSize * tilePixelResolution;
    +    var minY = origin[1] + tileRange.minY * tilePixelSize * tilePixelResolution;
    +    framebufferExtent = [
    +      minX, minY,
    +      minX + framebufferExtentDimension, minY + framebufferExtentDimension
    +    ];
    +
    +    this.bindFramebuffer(frameState, framebufferDimension);
    +    gl.viewport(0, 0, framebufferDimension, framebufferDimension);
    +
    +    gl.clearColor(0, 0, 0, 0);
    +    gl.clear(goog.webgl.COLOR_BUFFER_BIT);
    +    gl.disable(goog.webgl.BLEND);
    +
    +    var program = context.getProgram(this.fragmentShader_, this.vertexShader_);
    +    context.useProgram(program);
    +    if (goog.isNull(this.locations_)) {
    +      this.locations_ =
    +          new ol.renderer.webgl.tilelayer.shader.Locations(gl, program);
    +    }
    +
    +    context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.renderArrayBuffer_);
    +    gl.enableVertexAttribArray(this.locations_.a_position);
    +    gl.vertexAttribPointer(
    +        this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0);
    +    gl.enableVertexAttribArray(this.locations_.a_texCoord);
    +    gl.vertexAttribPointer(
    +        this.locations_.a_texCoord, 2, goog.webgl.FLOAT, false, 16, 8);
    +    gl.uniform1i(this.locations_.u_texture, 0);
    +
    +    /**
    +     * @type {Object.<number, Object.<string, ol.Tile>>}
    +     */
    +    var tilesToDrawByZ = {};
    +    tilesToDrawByZ[z] = {};
    +
    +    var findLoadedTiles = this.createLoadedTileFinder(
    +        tileSource, tilesToDrawByZ);
    +
    +    var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
    +    var allTilesLoaded = true;
    +    var tmpExtent = ol.extent.createEmpty();
    +    var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
    +    var childTileRange, fullyLoaded, tile, tileState, x, y, tileExtent;
    +    for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +      for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +
    +        tile = tileSource.getTile(z, x, y, pixelRatio, projection);
    +        if (goog.isDef(layerState.extent)) {
    +          // ignore tiles outside layer extent
    +          tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
    +          if (!ol.extent.intersects(tileExtent, layerState.extent)) {
    +            continue;
    +          }
    +        }
    +        tileState = tile.getState();
    +        if (tileState == ol.TileState.LOADED) {
    +          if (mapRenderer.isTileTextureLoaded(tile)) {
    +            tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
    +            continue;
    +          }
    +        } else if (tileState == ol.TileState.EMPTY ||
    +                   (tileState == ol.TileState.ERROR &&
    +                    !useInterimTilesOnError)) {
    +          continue;
    +        }
    +
    +        allTilesLoaded = false;
    +        fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
    +            tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
    +        if (!fullyLoaded) {
    +          childTileRange = tileGrid.getTileCoordChildTileRange(
    +              tile.tileCoord, tmpTileRange, tmpExtent);
    +          if (!goog.isNull(childTileRange)) {
    +            findLoadedTiles(z + 1, childTileRange);
    +          }
    +        }
    +
    +      }
    +
    +    }
    +
    +    /** @type {Array.<number>} */
    +    var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
    +    goog.array.sort(zs);
    +    var u_tileOffset = goog.vec.Vec4.createFloat32();
    +    var i, ii, sx, sy, tileKey, tilesToDraw, tx, ty;
    +    for (i = 0, ii = zs.length; i < ii; ++i) {
    +      tilesToDraw = tilesToDrawByZ[zs[i]];
    +      for (tileKey in tilesToDraw) {
    +        tile = tilesToDraw[tileKey];
    +        tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent);
    +        sx = 2 * (tileExtent[2] - tileExtent[0]) /
    +            framebufferExtentDimension;
    +        sy = 2 * (tileExtent[3] - tileExtent[1]) /
    +            framebufferExtentDimension;
    +        tx = 2 * (tileExtent[0] - framebufferExtent[0]) /
    +            framebufferExtentDimension - 1;
    +        ty = 2 * (tileExtent[1] - framebufferExtent[1]) /
    +            framebufferExtentDimension - 1;
    +        goog.vec.Vec4.setFromValues(u_tileOffset, sx, sy, tx, ty);
    +        gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset);
    +        mapRenderer.bindTileTexture(tile, tilePixelSize,
    +            tileGutter * pixelRatio, goog.webgl.LINEAR, goog.webgl.LINEAR);
    +        gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
    +      }
    +    }
    +
    +    if (allTilesLoaded) {
    +      this.renderedTileRange_ = tileRange;
    +      this.renderedFramebufferExtent_ = framebufferExtent;
    +      this.renderedRevision_ = tileSource.getRevision();
    +    } else {
    +      this.renderedTileRange_ = null;
    +      this.renderedFramebufferExtent_ = null;
    +      this.renderedRevision_ = -1;
    +      frameState.animate = true;
    +    }
    +
    +  }
    +
    +  this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
    +  var tileTextureQueue = mapRenderer.getTileTextureQueue();
    +  this.manageTilePyramid(
    +      frameState, tileSource, tileGrid, pixelRatio, projection, extent, z,
    +      tileLayer.getPreload(),
    +      /**
    +       * @param {ol.Tile} tile Tile.
    +       */
    +      function(tile) {
    +        if (tile.getState() == ol.TileState.LOADED &&
    +            !mapRenderer.isTileTextureLoaded(tile) &&
    +            !tileTextureQueue.isKeyQueued(tile.getKey())) {
    +          tileTextureQueue.enqueue([
    +            tile,
    +            tileGrid.getTileCoordCenter(tile.tileCoord),
    +            tileGrid.getResolution(tile.tileCoord[0]),
    +            tilePixelSize, tileGutter * pixelRatio
    +          ]);
    +        }
    +      }, this);
    +  this.scheduleExpireCache(frameState, tileSource);
    +  this.updateLogos(frameState, tileSource);
    +
    +  var texCoordMatrix = this.texCoordMatrix;
    +  goog.vec.Mat4.makeIdentity(texCoordMatrix);
    +  goog.vec.Mat4.translate(texCoordMatrix,
    +      (center[0] - framebufferExtent[0]) /
    +          (framebufferExtent[2] - framebufferExtent[0]),
    +      (center[1] - framebufferExtent[1]) /
    +          (framebufferExtent[3] - framebufferExtent[1]),
    +      0);
    +  if (viewState.rotation !== 0) {
    +    goog.vec.Mat4.rotateZ(texCoordMatrix, viewState.rotation);
    +  }
    +  goog.vec.Mat4.scale(texCoordMatrix,
    +      frameState.size[0] * viewState.resolution /
    +          (framebufferExtent[2] - framebufferExtent[0]),
    +      frameState.size[1] * viewState.resolution /
    +          (framebufferExtent[3] - framebufferExtent[1]),
    +      1);
    +  goog.vec.Mat4.translate(texCoordMatrix,
    +      -0.5,
    +      -0.5,
    +      0);
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.TileLayer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  if (goog.isNull(this.framebuffer)) {
    +    return undefined;
    +  }
    +
    +  var pixelOnMapScaled = [
    +    pixel[0] / frameState.size[0],
    +    (frameState.size[1] - pixel[1]) / frameState.size[1]];
    +
    +  var pixelOnFrameBufferScaled = [0, 0];
    +  ol.vec.Mat4.multVec2(
    +      this.texCoordMatrix, pixelOnMapScaled, pixelOnFrameBufferScaled);
    +  var pixelOnFrameBuffer = [
    +    pixelOnFrameBufferScaled[0] * this.framebufferDimension,
    +    pixelOnFrameBufferScaled[1] * this.framebufferDimension];
    +
    +  var gl = this.mapRenderer.getContext().getGL();
    +  gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
    +  var imageData = new Uint8Array(4);
    +  gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1,
    +      gl.RGBA, gl.UNSIGNED_BYTE, imageData);
    +
    +  if (imageData[3] > 0) {
    +    return callback.call(thisArg, this.getLayer());
    +  } else {
    +    return undefined;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webgltilelayershader.js b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayershader.js
    new file mode 100644
    index 0000000000..6cbb58103b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webgltilelayershader.js
    @@ -0,0 +1,111 @@
    +// This file is automatically generated, do not edit
    +goog.provide('ol.renderer.webgl.tilelayer.shader');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +ol.renderer.webgl.tilelayer.shader.Fragment = function() {
    +  goog.base(this, ol.renderer.webgl.tilelayer.shader.Fragment.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.tilelayer.shader.Fragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Fragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform sampler2D u_texture;\n\nvoid main(void) {\n  gl_FragColor = texture2D(u_texture, v_texCoord);\n}\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Fragment.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.tilelayer.shader.Fragment.DEBUG_SOURCE :
    +    ol.renderer.webgl.tilelayer.shader.Fragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +ol.renderer.webgl.tilelayer.shader.Vertex = function() {
    +  goog.base(this, ol.renderer.webgl.tilelayer.shader.Vertex.SOURCE);
    +};
    +goog.inherits(ol.renderer.webgl.tilelayer.shader.Vertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Vertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nuniform vec4 u_tileOffset;\n\nvoid main(void) {\n  gl_Position = vec4(a_position * u_tileOffset.xy + u_tileOffset.zw, 0., 1.);\n  v_texCoord = a_texCoord;\n}\n\n\n';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +ol.renderer.webgl.tilelayer.shader.Vertex.SOURCE = goog.DEBUG ?
    +    ol.renderer.webgl.tilelayer.shader.Vertex.DEBUG_SOURCE :
    +    ol.renderer.webgl.tilelayer.shader.Vertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +ol.renderer.webgl.tilelayer.shader.Locations = function(gl, program) {
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_texture = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_texture' : 'e');
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.u_tileOffset = gl.getUniformLocation(
    +      program, goog.DEBUG ? 'u_tileOffset' : 'd');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_position = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_position' : 'b');
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.a_texCoord = gl.getAttribLocation(
    +      program, goog.DEBUG ? 'a_texCoord' : 'c');
    +};
    diff --git a/mapbox-gl/ol/ol/renderer/webgl/webglvectorlayerrenderer.js b/mapbox-gl/ol/ol/renderer/webgl/webglvectorlayerrenderer.js
    new file mode 100644
    index 0000000000..4a39054f34
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/renderer/webgl/webglvectorlayerrenderer.js
    @@ -0,0 +1,312 @@
    +goog.provide('ol.renderer.webgl.VectorLayer');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('ol.ViewHint');
    +goog.require('ol.extent');
    +goog.require('ol.layer.Vector');
    +goog.require('ol.render.webgl.ReplayGroup');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.renderer.webgl.Layer');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.renderer.webgl.Layer}
    + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer.
    + * @param {ol.layer.Vector} vectorLayer Vector layer.
    + */
    +ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) {
    +
    +  goog.base(this, mapRenderer, vectorLayer);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.dirty_ = false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = -1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedResolution_ = NaN;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.renderedExtent_ = ol.extent.createEmpty();
    +
    +  /**
    +   * @private
    +   * @type {function(ol.Feature, ol.Feature): number|null}
    +   */
    +  this.renderedRenderOrder_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.render.webgl.ReplayGroup}
    +   */
    +  this.replayGroup_ = null;
    +
    +  /**
    +   * The last layer state.
    +   * @private
    +   * @type {?ol.layer.LayerState}
    +   */
    +  this.layerState_ = null;
    +
    +};
    +goog.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.composeFrame =
    +    function(frameState, layerState, context) {
    +  this.layerState_ = layerState;
    +  var viewState = frameState.viewState;
    +  var replayGroup = this.replayGroup_;
    +  if (!goog.isNull(replayGroup) && !replayGroup.isEmpty()) {
    +    replayGroup.replay(context,
    +        viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio, layerState.opacity,
    +        layerState.brightness, layerState.contrast, layerState.hue,
    +        layerState.saturation, frameState.skippedFeatureUids);
    +  }
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() {
    +  var replayGroup = this.replayGroup_;
    +  if (!goog.isNull(replayGroup)) {
    +    var context = this.mapRenderer.getContext();
    +    replayGroup.getDeleteResourcesFunction(context)();
    +    this.replayGroup_ = null;
    +  }
    +  goog.base(this, 'disposeInternal');
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtCoordinate =
    +    function(coordinate, frameState, callback, thisArg) {
    +  if (goog.isNull(this.replayGroup_) || goog.isNull(this.layerState_)) {
    +    return undefined;
    +  } else {
    +    var context = this.mapRenderer.getContext();
    +    var viewState = frameState.viewState;
    +    var layer = this.getLayer();
    +    var layerState = this.layerState_;
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +    return this.replayGroup_.forEachFeatureAtCoordinate(coordinate,
    +        context, viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio,
    +        layerState.opacity, layerState.brightness, layerState.contrast,
    +        layerState.hue, layerState.saturation, frameState.skippedFeatureUids,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback.call(thisArg, feature, layer);
    +          }
    +        });
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.hasFeatureAtCoordinate =
    +    function(coordinate, frameState) {
    +  if (goog.isNull(this.replayGroup_) || goog.isNull(this.layerState_)) {
    +    return false;
    +  } else {
    +    var context = this.mapRenderer.getContext();
    +    var viewState = frameState.viewState;
    +    var layerState = this.layerState_;
    +    return this.replayGroup_.hasFeatureAtCoordinate(coordinate,
    +        context, viewState.center, viewState.resolution, viewState.rotation,
    +        frameState.size, frameState.pixelRatio,
    +        layerState.opacity, layerState.brightness, layerState.contrast,
    +        layerState.hue, layerState.saturation, frameState.skippedFeatureUids);
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.forEachLayerAtPixel =
    +    function(pixel, frameState, callback, thisArg) {
    +  var coordinate = pixel.slice();
    +  ol.vec.Mat4.multVec2(
    +      frameState.pixelToCoordinateMatrix, coordinate, coordinate);
    +  var hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState);
    +
    +  if (hasFeature) {
    +    return callback.call(thisArg, this.getLayer());
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * Handle changes in image style state.
    + * @param {goog.events.Event} event Image style change event.
    + * @private
    + */
    +ol.renderer.webgl.VectorLayer.prototype.handleStyleImageChange_ =
    +    function(event) {
    +  this.renderIfReadyAndVisible();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.renderer.webgl.VectorLayer.prototype.prepareFrame =
    +    function(frameState, layerState, context) {
    +
    +  var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer());
    +  goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector);
    +  var vectorSource = vectorLayer.getSource();
    +
    +  this.updateAttributions(
    +      frameState.attributions, vectorSource.getAttributions());
    +  this.updateLogos(frameState, vectorSource);
    +
    +  if (!this.dirty_ && (!vectorLayer.getUpdateWhileAnimating() &&
    +      frameState.viewHints[ol.ViewHint.ANIMATING] ||
    +      frameState.viewHints[ol.ViewHint.INTERACTING])) {
    +    return true;
    +  }
    +
    +  var frameStateExtent = frameState.extent;
    +  var viewState = frameState.viewState;
    +  var projection = viewState.projection;
    +  var resolution = viewState.resolution;
    +  var pixelRatio = frameState.pixelRatio;
    +  var vectorLayerRevision = vectorLayer.getRevision();
    +  var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
    +  var vectorLayerRenderOrder = vectorLayer.getRenderOrder();
    +
    +  if (!goog.isDef(vectorLayerRenderOrder)) {
    +    vectorLayerRenderOrder = ol.renderer.vector.defaultOrder;
    +  }
    +
    +  var extent = ol.extent.buffer(frameStateExtent,
    +      vectorLayerRenderBuffer * resolution);
    +
    +  if (!this.dirty_ &&
    +      this.renderedResolution_ == resolution &&
    +      this.renderedRevision_ == vectorLayerRevision &&
    +      this.renderedRenderOrder_ == vectorLayerRenderOrder &&
    +      ol.extent.containsExtent(this.renderedExtent_, extent)) {
    +    return true;
    +  }
    +
    +  if (!goog.isNull(this.replayGroup_)) {
    +    frameState.postRenderFunctions.push(
    +        this.replayGroup_.getDeleteResourcesFunction(context));
    +  }
    +
    +  this.dirty_ = false;
    +
    +  var replayGroup = new ol.render.webgl.ReplayGroup(
    +      ol.renderer.vector.getTolerance(resolution, pixelRatio),
    +      extent, vectorLayer.getRenderBuffer());
    +  vectorSource.loadFeatures(extent, resolution, projection);
    +  var renderFeature =
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @this {ol.renderer.webgl.VectorLayer}
    +       */
    +      function(feature) {
    +    var styles;
    +    if (goog.isDef(feature.getStyleFunction())) {
    +      styles = feature.getStyleFunction().call(feature, resolution);
    +    } else if (goog.isDef(vectorLayer.getStyleFunction())) {
    +      styles = vectorLayer.getStyleFunction()(feature, resolution);
    +    }
    +    if (goog.isDefAndNotNull(styles)) {
    +      var dirty = this.renderFeature(
    +          feature, resolution, pixelRatio, styles, replayGroup);
    +      this.dirty_ = this.dirty_ || dirty;
    +    }
    +  };
    +  if (!goog.isNull(vectorLayerRenderOrder)) {
    +    /** @type {Array.<ol.Feature>} */
    +    var features = [];
    +    vectorSource.forEachFeatureInExtentAtResolution(extent, resolution,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         */
    +        function(feature) {
    +          features.push(feature);
    +        }, this);
    +    goog.array.sort(features, vectorLayerRenderOrder);
    +    goog.array.forEach(features, renderFeature, this);
    +  } else {
    +    vectorSource.forEachFeatureInExtentAtResolution(
    +        extent, resolution, renderFeature, this);
    +  }
    +  replayGroup.finish(context);
    +
    +  this.renderedResolution_ = resolution;
    +  this.renderedRevision_ = vectorLayerRevision;
    +  this.renderedRenderOrder_ = vectorLayerRenderOrder;
    +  this.renderedExtent_ = extent;
    +  this.replayGroup_ = replayGroup;
    +
    +  return true;
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {Array.<ol.style.Style>} styles Array of styles
    + * @param {ol.render.webgl.ReplayGroup} replayGroup Replay group.
    + * @return {boolean} `true` if an image is loading.
    + */
    +ol.renderer.webgl.VectorLayer.prototype.renderFeature =
    +    function(feature, resolution, pixelRatio, styles, replayGroup) {
    +  if (!goog.isDefAndNotNull(styles)) {
    +    return false;
    +  }
    +  var i, ii, loading = false;
    +  for (i = 0, ii = styles.length; i < ii; ++i) {
    +    loading = ol.renderer.vector.renderFeature(
    +        replayGroup, feature, styles[i],
    +        ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
    +        this.handleStyleImageChange_, this) || loading;
    +  }
    +  return loading;
    +};
    diff --git a/mapbox-gl/ol/ol/resolutionconstraint.js b/mapbox-gl/ol/ol/resolutionconstraint.js
    new file mode 100644
    index 0000000000..a90162d6b5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/resolutionconstraint.js
    @@ -0,0 +1,76 @@
    +goog.provide('ol.ResolutionConstraint');
    +goog.provide('ol.ResolutionConstraintType');
    +
    +goog.require('goog.math');
    +goog.require('ol.array');
    +
    +
    +/**
    + * @typedef {function((number|undefined), number, number): (number|undefined)}
    + */
    +ol.ResolutionConstraintType;
    +
    +
    +/**
    + * @param {Array.<number>} resolutions Resolutions.
    + * @return {ol.ResolutionConstraintType} Zoom function.
    + */
    +ol.ResolutionConstraint.createSnapToResolutions =
    +    function(resolutions) {
    +  return (
    +      /**
    +       * @param {number|undefined} resolution Resolution.
    +       * @param {number} delta Delta.
    +       * @param {number} direction Direction.
    +       * @return {number|undefined} Resolution.
    +       */
    +      function(resolution, delta, direction) {
    +        if (goog.isDef(resolution)) {
    +          var z =
    +              ol.array.linearFindNearest(resolutions, resolution, direction);
    +          z = goog.math.clamp(z + delta, 0, resolutions.length - 1);
    +          return resolutions[z];
    +        } else {
    +          return undefined;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {number} power Power.
    + * @param {number} maxResolution Maximum resolution.
    + * @param {number=} opt_maxLevel Maximum level.
    + * @return {ol.ResolutionConstraintType} Zoom function.
    + */
    +ol.ResolutionConstraint.createSnapToPower =
    +    function(power, maxResolution, opt_maxLevel) {
    +  return (
    +      /**
    +       * @param {number|undefined} resolution Resolution.
    +       * @param {number} delta Delta.
    +       * @param {number} direction Direction.
    +       * @return {number|undefined} Resolution.
    +       */
    +      function(resolution, delta, direction) {
    +        if (goog.isDef(resolution)) {
    +          var offset;
    +          if (direction > 0) {
    +            offset = 0;
    +          } else if (direction < 0) {
    +            offset = 1;
    +          } else {
    +            offset = 0.5;
    +          }
    +          var oldLevel = Math.floor(
    +              Math.log(maxResolution / resolution) / Math.log(power) + offset);
    +          var newLevel = Math.max(oldLevel + delta, 0);
    +          if (goog.isDef(opt_maxLevel)) {
    +            newLevel = Math.min(newLevel, opt_maxLevel);
    +          }
    +          return maxResolution / Math.pow(power, newLevel);
    +        } else {
    +          return undefined;
    +        }
    +      });
    +};
    diff --git a/mapbox-gl/ol/ol/rotationconstraint.js b/mapbox-gl/ol/ol/rotationconstraint.js
    new file mode 100644
    index 0000000000..fc73600f4e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/rotationconstraint.js
    @@ -0,0 +1,87 @@
    +goog.provide('ol.RotationConstraint');
    +goog.provide('ol.RotationConstraintType');
    +
    +goog.require('goog.math');
    +
    +
    +/**
    + * @typedef {function((number|undefined), number): (number|undefined)}
    + */
    +ol.RotationConstraintType;
    +
    +
    +/**
    + * @param {number|undefined} rotation Rotation.
    + * @param {number} delta Delta.
    + * @return {number|undefined} Rotation.
    + */
    +ol.RotationConstraint.disable = function(rotation, delta) {
    +  if (goog.isDef(rotation)) {
    +    return 0;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {number|undefined} rotation Rotation.
    + * @param {number} delta Delta.
    + * @return {number|undefined} Rotation.
    + */
    +ol.RotationConstraint.none = function(rotation, delta) {
    +  if (goog.isDef(rotation)) {
    +    return rotation + delta;
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +
    +/**
    + * @param {number} n N.
    + * @return {ol.RotationConstraintType} Rotation constraint.
    + */
    +ol.RotationConstraint.createSnapToN = function(n) {
    +  var theta = 2 * Math.PI / n;
    +  return (
    +      /**
    +       * @param {number|undefined} rotation Rotation.
    +       * @param {number} delta Delta.
    +       * @return {number|undefined} Rotation.
    +       */
    +      function(rotation, delta) {
    +        if (goog.isDef(rotation)) {
    +          rotation = Math.floor((rotation + delta) / theta + 0.5) * theta;
    +          return rotation;
    +        } else {
    +          return undefined;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {number=} opt_tolerance Tolerance.
    + * @return {ol.RotationConstraintType} Rotation constraint.
    + */
    +ol.RotationConstraint.createSnapToZero = function(opt_tolerance) {
    +  var tolerance = opt_tolerance || goog.math.toRadians(5);
    +  return (
    +      /**
    +       * @param {number|undefined} rotation Rotation.
    +       * @param {number} delta Delta.
    +       * @return {number|undefined} Rotation.
    +       */
    +      function(rotation, delta) {
    +        if (goog.isDef(rotation)) {
    +          if (Math.abs(rotation + delta) <= tolerance) {
    +            return 0;
    +          } else {
    +            return rotation + delta;
    +          }
    +        } else {
    +          return undefined;
    +        }
    +      });
    +};
    diff --git a/mapbox-gl/ol/ol/size.js b/mapbox-gl/ol/ol/size.js
    new file mode 100644
    index 0000000000..2fb84be2a7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/size.js
    @@ -0,0 +1,21 @@
    +goog.provide('ol.Size');
    +goog.provide('ol.size');
    +
    +
    +/**
    + * An array of numbers representing a size: `[width, height]`.
    + * @typedef {Array.<number>}
    + * @api stable
    + */
    +ol.Size;
    +
    +
    +/**
    + * Compares sizes for equality.
    + * @param {ol.Size} a Size.
    + * @param {ol.Size} b Size.
    + * @return {boolean} Equals.
    + */
    +ol.size.equals = function(a, b) {
    +  return a[0] == b[0] && a[1] == b[1];
    +};
    diff --git a/mapbox-gl/ol/ol/source.jsdoc b/mapbox-gl/ol/ol/source.jsdoc
    new file mode 100644
    index 0000000000..7c86502e0e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.source
    + */
    diff --git a/mapbox-gl/ol/ol/source/bingmapssource.js b/mapbox-gl/ol/ol/source/bingmapssource.js
    new file mode 100644
    index 0000000000..b2a9bff0fc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/bingmapssource.js
    @@ -0,0 +1,187 @@
    +goog.provide('ol.source.BingMaps');
    +
    +goog.require('goog.Uri');
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.net.Jsonp');
    +goog.require('ol');
    +goog.require('ol.Attribution');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.State');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for Bing Maps tile data.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.BingMapsOptions} options Bing Maps options.
    + * @api stable
    + */
    +ol.source.BingMaps = function(options) {
    +
    +  goog.base(this, {
    +    crossOrigin: 'anonymous',
    +    opaque: true,
    +    projection: ol.proj.get('EPSG:3857'),
    +    state: ol.source.State.LOADING,
    +    tileLoadFunction: options.tileLoadFunction
    +  });
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.culture_ = goog.isDef(options.culture) ? options.culture : 'en-us';
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxZoom_ = goog.isDef(options.maxZoom) ? options.maxZoom : -1;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.wrapX_ = goog.isDef(options.wrapX) ? options.wrapX : true;
    +
    +  var protocol = ol.IS_HTTPS ? 'https:' : 'http:';
    +  var uri = new goog.Uri(
    +      protocol + '//dev.virtualearth.net/REST/v1/Imagery/Metadata/' +
    +      options.imagerySet);
    +
    +  var jsonp = new goog.net.Jsonp(uri, 'jsonp');
    +  jsonp.send({
    +    'include': 'ImageryProviders',
    +    'uriScheme': ol.IS_HTTPS ? 'https' : 'http',
    +    'key': options.key
    +  }, goog.bind(this.handleImageryMetadataResponse, this));
    +
    +};
    +goog.inherits(ol.source.BingMaps, ol.source.TileImage);
    +
    +
    +/**
    + * @const
    + * @type {ol.Attribution}
    + * @api
    + */
    +ol.source.BingMaps.TOS_ATTRIBUTION = new ol.Attribution({
    +  html: '<a class="ol-attribution-bing-tos" ' +
    +      'href="http://www.microsoft.com/maps/product/terms.html">' +
    +      'Terms of Use</a>'
    +});
    +
    +
    +/**
    + * @param {BingMapsImageryMetadataResponse} response Response.
    + */
    +ol.source.BingMaps.prototype.handleImageryMetadataResponse =
    +    function(response) {
    +
    +  if (response.statusCode != 200 ||
    +      response.statusDescription != 'OK' ||
    +      response.authenticationResultCode != 'ValidCredentials' ||
    +      response.resourceSets.length != 1 ||
    +      response.resourceSets[0].resources.length != 1) {
    +    this.setState(ol.source.State.ERROR);
    +    return;
    +  }
    +
    +  var brandLogoUri = response.brandLogoUri;
    +  if (ol.IS_HTTPS && brandLogoUri.indexOf('https') == -1) {
    +    brandLogoUri = brandLogoUri.replace('http', 'https');
    +  }
    +  //var copyright = response.copyright;  // FIXME do we need to display this?
    +  var resource = response.resourceSets[0].resources[0];
    +  goog.asserts.assert(resource.imageWidth == resource.imageHeight);
    +  var maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_;
    +
    +  var sourceProjection = this.getProjection();
    +  var tileGrid = new ol.tilegrid.XYZ({
    +    extent: ol.tilegrid.extentFromProjection(sourceProjection),
    +    minZoom: resource.zoomMin,
    +    maxZoom: maxZoom,
    +    tileSize: resource.imageWidth
    +  });
    +  this.tileGrid = tileGrid;
    +
    +  var culture = this.culture_;
    +  this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
    +      tileGrid.createTileCoordTransform({wrapX: this.wrapX_}),
    +      ol.TileUrlFunction.createFromTileUrlFunctions(
    +          goog.array.map(
    +              resource.imageUrlSubdomains,
    +              function(subdomain) {
    +                var imageUrl = resource.imageUrl
    +                    .replace('{subdomain}', subdomain)
    +                    .replace('{culture}', culture);
    +                return (
    +                    /**
    +                     * @param {ol.TileCoord} tileCoord Tile coordinate.
    +                     * @param {number} pixelRatio Pixel ratio.
    +                     * @param {ol.proj.Projection} projection Projection.
    +                     * @return {string|undefined} Tile URL.
    +                     */
    +                    function(tileCoord, pixelRatio, projection) {
    +                      goog.asserts.assert(ol.proj.equivalent(
    +                          projection, sourceProjection));
    +                      if (goog.isNull(tileCoord)) {
    +                        return undefined;
    +                      } else {
    +                        return imageUrl.replace(
    +                            '{quadkey}', ol.tilecoord.quadKey(tileCoord));
    +                      }
    +                    });
    +              })));
    +
    +  if (resource.imageryProviders) {
    +    var transform = ol.proj.getTransformFromProjections(
    +        ol.proj.get('EPSG:4326'), this.getProjection());
    +
    +    var attributions = goog.array.map(
    +        resource.imageryProviders,
    +        function(imageryProvider) {
    +          var html = imageryProvider.attribution;
    +          /** @type {Object.<string, Array.<ol.TileRange>>} */
    +          var tileRanges = {};
    +          goog.array.forEach(
    +              imageryProvider.coverageAreas,
    +              function(coverageArea) {
    +                var minZ = coverageArea.zoomMin;
    +                var maxZ = Math.min(coverageArea.zoomMax, maxZoom);
    +                var bbox = coverageArea.bbox;
    +                var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]];
    +                var extent = ol.extent.applyTransform(
    +                    epsg4326Extent, transform);
    +                var tileRange, z, zKey;
    +                for (z = minZ; z <= maxZ; ++z) {
    +                  zKey = z.toString();
    +                  tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
    +                  if (zKey in tileRanges) {
    +                    tileRanges[zKey].push(tileRange);
    +                  } else {
    +                    tileRanges[zKey] = [tileRange];
    +                  }
    +                }
    +              });
    +          return new ol.Attribution({html: html, tileRanges: tileRanges});
    +        });
    +    attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION);
    +    this.setAttributions(attributions);
    +  }
    +
    +  this.setLogo(brandLogoUri);
    +
    +  this.setState(ol.source.State.READY);
    +
    +};
    diff --git a/mapbox-gl/ol/ol/source/clustersource.js b/mapbox-gl/ol/ol/source/clustersource.js
    new file mode 100644
    index 0000000000..92a41e0581
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/clustersource.js
    @@ -0,0 +1,163 @@
    +// FIXME keep cluster cache by resolution ?
    +// FIXME distance not respected because of the centroid
    +
    +goog.provide('ol.source.Cluster');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.Feature');
    +goog.require('ol.coordinate');
    +goog.require('ol.extent');
    +goog.require('ol.geom.Point');
    +goog.require('ol.source.Vector');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {olx.source.ClusterOptions} options
    + * @extends {ol.source.Vector}
    + * @api
    + */
    +ol.source.Cluster = function(options) {
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    logo: options.logo,
    +    projection: options.projection
    +  });
    +
    +  /**
    +   * @type {number|undefined}
    +   * @private
    +   */
    +  this.resolution_ = undefined;
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.distance_ = goog.isDef(options.distance) ? options.distance : 20;
    +
    +  /**
    +   * @type {Array.<ol.Feature>}
    +   * @private
    +   */
    +  this.features_ = [];
    +
    +  /**
    +   * @type {ol.source.Vector}
    +   * @private
    +   */
    +  this.source_ = options.source;
    +
    +  this.source_.on(goog.events.EventType.CHANGE,
    +      ol.source.Cluster.prototype.onSourceChange_, this);
    +};
    +goog.inherits(ol.source.Cluster, ol.source.Vector);
    +
    +
    +/**
    + * Get a reference to the wrapped source.
    + * @return {ol.source.Vector} Source.
    + * @api
    + */
    +ol.source.Cluster.prototype.getSource = function() {
    +  return this.source_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.Cluster.prototype.loadFeatures = function(extent, resolution,
    +    projection) {
    +  if (resolution !== this.resolution_) {
    +    this.clear();
    +    this.resolution_ = resolution;
    +    this.source_.loadFeatures(extent, resolution, projection);
    +    this.cluster_();
    +    this.addFeatures(this.features_);
    +  }
    +};
    +
    +
    +/**
    + * handle the source changing
    + * @private
    + */
    +ol.source.Cluster.prototype.onSourceChange_ = function() {
    +  this.clear();
    +  this.cluster_();
    +  this.addFeatures(this.features_);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.Cluster.prototype.cluster_ = function() {
    +  if (!goog.isDef(this.resolution_)) {
    +    return;
    +  }
    +  this.features_.length = 0;
    +  var extent = ol.extent.createEmpty();
    +  var mapDistance = this.distance_ * this.resolution_;
    +  var features = this.source_.getFeatures();
    +
    +  /**
    +   * @type {Object.<string, boolean>}
    +   */
    +  var clustered = {};
    +
    +  for (var i = 0, ii = features.length; i < ii; i++) {
    +    var feature = features[i];
    +    if (!goog.object.containsKey(clustered, goog.getUid(feature).toString())) {
    +      var geometry = feature.getGeometry();
    +      goog.asserts.assert(geometry instanceof ol.geom.Point);
    +      var coordinates = geometry.getCoordinates();
    +      ol.extent.createOrUpdateFromCoordinate(coordinates, extent);
    +      ol.extent.buffer(extent, mapDistance, extent);
    +
    +      var neighbors = this.source_.getFeaturesInExtent(extent);
    +      goog.asserts.assert(neighbors.length >= 1);
    +      neighbors = goog.array.filter(neighbors, function(neighbor) {
    +        var uid = goog.getUid(neighbor).toString();
    +        if (!goog.object.containsKey(clustered, uid)) {
    +          clustered[uid] = true;
    +          return true;
    +        } else {
    +          return false;
    +        }
    +      });
    +      this.features_.push(this.createCluster_(neighbors));
    +    }
    +  }
    +  goog.asserts.assert(
    +      goog.object.getCount(clustered) == this.source_.getFeatures().length);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features Features
    + * @return {ol.Feature}
    + * @private
    + */
    +ol.source.Cluster.prototype.createCluster_ = function(features) {
    +  var length = features.length;
    +  var centroid = [0, 0];
    +  for (var i = 0; i < length; i++) {
    +    var geometry = features[i].getGeometry();
    +    goog.asserts.assert(geometry instanceof ol.geom.Point);
    +    var coordinates = geometry.getCoordinates();
    +    ol.coordinate.add(centroid, coordinates);
    +  }
    +  ol.coordinate.scale(centroid, 1 / length);
    +
    +  var cluster = new ol.Feature(new ol.geom.Point(centroid));
    +  cluster.set('features', features);
    +  return cluster;
    +};
    diff --git a/mapbox-gl/ol/ol/source/formatvectorsource.js b/mapbox-gl/ol/ol/source/formatvectorsource.js
    new file mode 100644
    index 0000000000..7c94bf5fa2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/formatvectorsource.js
    @@ -0,0 +1,125 @@
    +// FIXME consider delaying feature reading so projection can be provided by
    +// consumer (e.g. the view)
    +
    +goog.provide('ol.source.FormatVector');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dispose');
    +goog.require('goog.events');
    +goog.require('goog.net.EventType');
    +goog.require('goog.net.XhrIo');
    +goog.require('goog.net.XhrIo.ResponseType');
    +goog.require('goog.userAgent');
    +goog.require('ol.format.FormatType');
    +goog.require('ol.has');
    +goog.require('ol.source.State');
    +goog.require('ol.source.Vector');
    +goog.require('ol.xml');
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for vector sources in one of the supported formats.
    + *
    + * @constructor
    + * @extends {ol.source.Vector}
    + * @param {olx.source.FormatVectorOptions} options Options.
    + */
    +ol.source.FormatVector = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    logo: options.logo,
    +    projection: options.projection
    +  });
    +
    +  /**
    +   * @protected
    +   * @type {ol.format.Feature}
    +   */
    +  this.format = options.format;
    +
    +};
    +goog.inherits(ol.source.FormatVector, ol.source.Vector);
    +
    +
    +/**
    + * @param {goog.Uri|string} url URL.
    + * @param {function(this: T, Array.<ol.Feature>)} success Success Callback.
    + * @param {function(this: T)} error Error callback.
    + * @param {T} thisArg Value to use as `this` when executing `success` or
    + *     `error`.
    + * @template T
    + */
    +ol.source.FormatVector.prototype.loadFeaturesFromURL =
    +    function(url, success, error, thisArg) {
    +  var xhrIo = new goog.net.XhrIo();
    +  var type = this.format.getType();
    +  var responseType;
    +  // FIXME maybe use ResponseType.DOCUMENT?
    +  if (type == ol.format.FormatType.BINARY &&
    +      ol.has.ARRAY_BUFFER) {
    +    responseType = goog.net.XhrIo.ResponseType.ARRAY_BUFFER;
    +  } else {
    +    responseType = goog.net.XhrIo.ResponseType.TEXT;
    +  }
    +  xhrIo.setResponseType(responseType);
    +  goog.events.listen(xhrIo, goog.net.EventType.COMPLETE,
    +      /**
    +       * @param {Event} event Event.
    +       * @private
    +       * @this {ol.source.FormatVector}
    +       */
    +      function(event) {
    +        var xhrIo = event.target;
    +        goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo);
    +        if (xhrIo.isSuccess()) {
    +          var type = this.format.getType();
    +          /** @type {ArrayBuffer|Document|Node|Object|string|undefined} */
    +          var source;
    +          if (type == ol.format.FormatType.BINARY &&
    +              ol.has.ARRAY_BUFFER) {
    +            source = xhrIo.getResponse();
    +            goog.asserts.assertInstanceof(source, ArrayBuffer);
    +          } else if (type == ol.format.FormatType.JSON) {
    +            source = xhrIo.getResponseText();
    +          } else if (type == ol.format.FormatType.TEXT) {
    +            source = xhrIo.getResponseText();
    +          } else if (type == ol.format.FormatType.XML) {
    +            if (!goog.userAgent.IE) {
    +              source = xhrIo.getResponseXml();
    +            }
    +            if (!goog.isDefAndNotNull(source)) {
    +              source = ol.xml.parse(xhrIo.getResponseText());
    +            }
    +          } else {
    +            goog.asserts.fail();
    +          }
    +          if (goog.isDefAndNotNull(source)) {
    +            success.call(thisArg, this.readFeatures(source));
    +          } else {
    +            this.setState(ol.source.State.ERROR);
    +            goog.asserts.fail();
    +          }
    +        } else {
    +          error.call(thisArg);
    +        }
    +        goog.dispose(xhrIo);
    +      }, false, this);
    +  xhrIo.send(url);
    +};
    +
    +
    +/**
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {Array.<ol.Feature>} Features.
    + * @api
    + */
    +ol.source.FormatVector.prototype.readFeatures = function(source) {
    +  var format = this.format;
    +  var projection = this.getProjection();
    +  return format.readFeatures(source, {featureProjection: projection});
    +};
    diff --git a/mapbox-gl/ol/ol/source/geojsonsource.js b/mapbox-gl/ol/ol/source/geojsonsource.js
    new file mode 100644
    index 0000000000..b1f11c89c7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/geojsonsource.js
    @@ -0,0 +1,37 @@
    +goog.provide('ol.source.GeoJSON');
    +
    +goog.require('ol.format.GeoJSON');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in GeoJSON format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.GeoJSONOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.GeoJSON = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    format: new ol.format.GeoJSON({
    +      defaultDataProjection: options.defaultProjection
    +    }),
    +    logo: options.logo,
    +    object: options.object,
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url,
    +    urls: options.urls
    +  });
    +
    +};
    +goog.inherits(ol.source.GeoJSON, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/gpxsource.js b/mapbox-gl/ol/ol/source/gpxsource.js
    new file mode 100644
    index 0000000000..7f45499be7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/gpxsource.js
    @@ -0,0 +1,36 @@
    +goog.provide('ol.source.GPX');
    +
    +goog.require('ol.format.GPX');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in GPX format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.GPXOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.GPX = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    doc: options.doc,
    +    extent: options.extent,
    +    format: new ol.format.GPX(),
    +    logo: options.logo,
    +    node: options.node,
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url,
    +    urls: options.urls
    +  });
    +
    +};
    +goog.inherits(ol.source.GPX, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/igcsource.js b/mapbox-gl/ol/ol/source/igcsource.js
    new file mode 100644
    index 0000000000..ebe220bad7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/igcsource.js
    @@ -0,0 +1,33 @@
    +goog.provide('ol.source.IGC');
    +
    +goog.require('ol.format.IGC');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in IGC format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.IGCOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.IGC = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    format: new ol.format.IGC({
    +      altitudeMode: options.altitudeMode
    +    }),
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url,
    +    urls: options.urls
    +  });
    +
    +};
    +goog.inherits(ol.source.IGC, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/imagecanvassource.js b/mapbox-gl/ol/ol/source/imagecanvassource.js
    new file mode 100644
    index 0000000000..16e390ea17
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagecanvassource.js
    @@ -0,0 +1,91 @@
    +goog.provide('ol.source.ImageCanvas');
    +
    +goog.require('ol.CanvasFunctionType');
    +goog.require('ol.ImageCanvas');
    +goog.require('ol.extent');
    +goog.require('ol.source.Image');
    +
    +
    +
    +/**
    + * @classdesc
    + * Base class for image sources where a canvas element is the image.
    + *
    + * @constructor
    + * @extends {ol.source.Image}
    + * @param {olx.source.ImageCanvasOptions} options
    + * @api
    + */
    +ol.source.ImageCanvas = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    logo: options.logo,
    +    projection: options.projection,
    +    resolutions: options.resolutions,
    +    state: goog.isDef(options.state) ?
    +        /** @type {ol.source.State} */ (options.state) : undefined
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.CanvasFunctionType}
    +   */
    +  this.canvasFunction_ = options.canvasFunction;
    +
    +  /**
    +   * @private
    +   * @type {ol.ImageCanvas}
    +   */
    +  this.canvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.ratio_ = goog.isDef(options.ratio) ?
    +      options.ratio : 1.5;
    +
    +};
    +goog.inherits(ol.source.ImageCanvas, ol.source.Image);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ImageCanvas.prototype.getImage =
    +    function(extent, resolution, pixelRatio, projection) {
    +  resolution = this.findNearestResolution(resolution);
    +
    +  var canvas = this.canvas_;
    +  if (!goog.isNull(canvas) &&
    +      this.renderedRevision_ == this.getRevision() &&
    +      canvas.getResolution() == resolution &&
    +      canvas.getPixelRatio() == pixelRatio &&
    +      ol.extent.containsExtent(canvas.getExtent(), extent)) {
    +    return canvas;
    +  }
    +
    +  extent = extent.slice();
    +  ol.extent.scaleFromCenter(extent, this.ratio_);
    +  var width = ol.extent.getWidth(extent) / resolution;
    +  var height = ol.extent.getHeight(extent) / resolution;
    +  var size = [width * pixelRatio, height * pixelRatio];
    +
    +  var canvasElement = this.canvasFunction_(
    +      extent, resolution, pixelRatio, size, projection);
    +  if (!goog.isNull(canvasElement)) {
    +    canvas = new ol.ImageCanvas(extent, resolution, pixelRatio,
    +        this.getAttributions(), canvasElement);
    +  }
    +  this.canvas_ = canvas;
    +  this.renderedRevision_ = this.getRevision();
    +
    +  return canvas;
    +};
    diff --git a/mapbox-gl/ol/ol/source/imagemapguidesource.js b/mapbox-gl/ol/ol/source/imagemapguidesource.js
    new file mode 100644
    index 0000000000..de728275eb
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagemapguidesource.js
    @@ -0,0 +1,244 @@
    +goog.provide('ol.source.ImageMapGuide');
    +
    +goog.require('goog.object');
    +goog.require('goog.uri.utils');
    +goog.require('ol.Image');
    +goog.require('ol.ImageLoadFunctionType');
    +goog.require('ol.ImageUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.source.Image');
    +
    +
    +
    +/**
    + * @classdesc
    + * Source for images from Mapguide servers
    + *
    + * @constructor
    + * @extends {ol.source.Image}
    + * @param {olx.source.ImageMapGuideOptions} options Options.
    + * @api stable
    + */
    +ol.source.ImageMapGuide = function(options) {
    +
    +  goog.base(this, {
    +    projection: options.projection,
    +    resolutions: options.resolutions
    +  });
    +
    +  /**
    +   * @private
    +   * @type {?string}
    +   */
    +  this.crossOrigin_ =
    +      goog.isDef(options.crossOrigin) ? options.crossOrigin : null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.displayDpi_ = goog.isDef(options.displayDpi) ?
    +      options.displayDpi : 96;
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.params_ = goog.isDef(options.params) ? options.params : {};
    +
    +  var imageUrlFunction;
    +  if (goog.isDef(options.url)) {
    +    imageUrlFunction = ol.ImageUrlFunction.createFromParamsFunction(
    +        options.url, this.params_, goog.bind(this.getUrl, this));
    +  } else {
    +    imageUrlFunction = ol.ImageUrlFunction.nullImageUrlFunction;
    +  }
    +
    +  /**
    +   * @private
    +   * @type {ol.ImageUrlFunctionType}
    +   */
    +  this.imageUrlFunction_ = imageUrlFunction;
    +
    +  /**
    +   * @private
    +   * @type {ol.ImageLoadFunctionType}
    +   */
    +  this.imageLoadFunction_ = goog.isDef(options.imageLoadFunction) ?
    +      options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.hidpi_ = goog.isDef(options.hidpi) ? options.hidpi : true;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.metersPerUnit_ = goog.isDef(options.metersPerUnit) ?
    +      options.metersPerUnit : 1;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.ratio_ = goog.isDef(options.ratio) ? options.ratio : 1;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.useOverlay_ = goog.isDef(options.useOverlay) ?
    +      options.useOverlay : false;
    +
    +  /**
    +   * @private
    +   * @type {ol.Image}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = 0;
    +
    +};
    +goog.inherits(ol.source.ImageMapGuide, ol.source.Image);
    +
    +
    +/**
    + * Get the user-provided params, i.e. those passed to the constructor through
    + * the "params" option, and possibly updated using the updateParams method.
    + * @return {Object} Params.
    + * @api stable
    + */
    +ol.source.ImageMapGuide.prototype.getParams = function() {
    +  return this.params_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ImageMapGuide.prototype.getImage =
    +    function(extent, resolution, pixelRatio, projection) {
    +  resolution = this.findNearestResolution(resolution);
    +  pixelRatio = this.hidpi_ ? pixelRatio : 1;
    +
    +  var image = this.image_;
    +  if (!goog.isNull(image) &&
    +      this.renderedRevision_ == this.getRevision() &&
    +      image.getResolution() == resolution &&
    +      image.getPixelRatio() == pixelRatio &&
    +      ol.extent.containsExtent(image.getExtent(), extent)) {
    +    return image;
    +  }
    +
    +  if (this.ratio_ != 1) {
    +    extent = extent.slice();
    +    ol.extent.scaleFromCenter(extent, this.ratio_);
    +  }
    +  var width = ol.extent.getWidth(extent) / resolution;
    +  var height = ol.extent.getHeight(extent) / resolution;
    +  var size = [width * pixelRatio, height * pixelRatio];
    +
    +  var imageUrl = this.imageUrlFunction_(extent, size, projection);
    +  if (goog.isDef(imageUrl)) {
    +    image = new ol.Image(extent, resolution, pixelRatio,
    +        this.getAttributions(), imageUrl, this.crossOrigin_,
    +        this.imageLoadFunction_);
    +  } else {
    +    image = null;
    +  }
    +  this.image_ = image;
    +  this.renderedRevision_ = this.getRevision();
    +
    +  return image;
    +};
    +
    +
    +/**
    + * @return {ol.ImageLoadFunctionType} The image load function.
    + * @api
    + */
    +ol.source.ImageMapGuide.prototype.getImageLoadFunction = function() {
    +  return this.imageLoadFunction_;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent The map extents.
    + * @param {ol.Size} size the viewport size.
    + * @param {number} metersPerUnit The meters-per-unit value.
    + * @param {number} dpi The display resolution.
    + * @return {number} The computed map scale.
    + */
    +ol.source.ImageMapGuide.getScale = function(extent, size, metersPerUnit, dpi) {
    +  var mcsW = ol.extent.getWidth(extent);
    +  var mcsH = ol.extent.getHeight(extent);
    +  var devW = size[0];
    +  var devH = size[1];
    +  var mpp = 0.0254 / dpi;
    +  if (devH * mcsW > devW * mcsH) {
    +    return mcsW * metersPerUnit / (devW * mpp); // width limited
    +  } else {
    +    return mcsH * metersPerUnit / (devH * mpp); // height limited
    +  }
    +};
    +
    +
    +/**
    + * Update the user-provided params.
    + * @param {Object} params Params.
    + * @api stable
    + */
    +ol.source.ImageMapGuide.prototype.updateParams = function(params) {
    +  goog.object.extend(this.params_, params);
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {string} baseUrl The mapagent url.
    + * @param {Object.<string, string|number>} params Request parameters.
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Size} size Size.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {string} The mapagent map image request URL.
    + */
    +ol.source.ImageMapGuide.prototype.getUrl =
    +    function(baseUrl, params, extent, size, projection) {
    +  var scale = ol.source.ImageMapGuide.getScale(extent, size,
    +      this.metersPerUnit_, this.displayDpi_);
    +  var center = ol.extent.getCenter(extent);
    +  var baseParams = {
    +    'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE',
    +    'VERSION': '2.0.0',
    +    'LOCALE': 'en',
    +    'CLIENTAGENT': 'ol.source.ImageMapGuide source',
    +    'CLIP': '1',
    +    'SETDISPLAYDPI': this.displayDpi_,
    +    'SETDISPLAYWIDTH': Math.round(size[0]),
    +    'SETDISPLAYHEIGHT': Math.round(size[1]),
    +    'SETVIEWSCALE': scale,
    +    'SETVIEWCENTERX': center[0],
    +    'SETVIEWCENTERY': center[1]
    +  };
    +  goog.object.extend(baseParams, params);
    +  return goog.uri.utils.appendParamsFromMap(baseUrl, baseParams);
    +};
    +
    +
    +/**
    + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
    + * @api
    + */
    +ol.source.ImageMapGuide.prototype.setImageLoadFunction = function(
    +    imageLoadFunction) {
    +  this.image_ = null;
    +  this.imageLoadFunction_ = imageLoadFunction;
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/source/imagesource.js b/mapbox-gl/ol/ol/source/imagesource.js
    new file mode 100644
    index 0000000000..6cab2fe42d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagesource.js
    @@ -0,0 +1,101 @@
    +goog.provide('ol.source.Image');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.Attribution');
    +goog.require('ol.Extent');
    +goog.require('ol.array');
    +goog.require('ol.source.Source');
    +
    +
    +/**
    + * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
    + *            extent: (null|ol.Extent|undefined),
    + *            logo: (string|olx.LogoOptions|undefined),
    + *            projection: ol.proj.ProjectionLike,
    + *            resolutions: (Array.<number>|undefined),
    + *            state: (ol.source.State|undefined)}}
    + */
    +ol.source.ImageOptions;
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for sources providing a single image.
    + *
    + * @constructor
    + * @extends {ol.source.Source}
    + * @param {ol.source.ImageOptions} options Single image source options.
    + * @api
    + */
    +ol.source.Image = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    logo: options.logo,
    +    projection: options.projection,
    +    state: options.state
    +  });
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.resolutions_ = goog.isDef(options.resolutions) ?
    +      options.resolutions : null;
    +  goog.asserts.assert(goog.isNull(this.resolutions_) ||
    +      goog.array.isSorted(this.resolutions_,
    +          function(a, b) {
    +            return b - a;
    +          }, true));
    +
    +};
    +goog.inherits(ol.source.Image, ol.source.Source);
    +
    +
    +/**
    + * @return {Array.<number>} Resolutions.
    + */
    +ol.source.Image.prototype.getResolutions = function() {
    +  return this.resolutions_;
    +};
    +
    +
    +/**
    + * @protected
    + * @param {number} resolution Resolution.
    + * @return {number} Resolution.
    + */
    +ol.source.Image.prototype.findNearestResolution =
    +    function(resolution) {
    +  if (!goog.isNull(this.resolutions_)) {
    +    var idx = ol.array.linearFindNearest(this.resolutions_, resolution, 0);
    +    resolution = this.resolutions_[idx];
    +  }
    +  return resolution;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {ol.ImageBase} Single image.
    + */
    +ol.source.Image.prototype.getImage = goog.abstractMethod;
    +
    +
    +/**
    + * Default image load function for image sources that use ol.Image image
    + * instances.
    + * @param {ol.Image} image Image.
    + * @param {string} src Source.
    + */
    +ol.source.Image.defaultImageLoadFunction = function(image, src) {
    +  image.getImage().src = src;
    +};
    diff --git a/mapbox-gl/ol/ol/source/imagestaticsource.js b/mapbox-gl/ol/ol/source/imagestaticsource.js
    new file mode 100644
    index 0000000000..04fa00fa78
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagestaticsource.js
    @@ -0,0 +1,66 @@
    +goog.provide('ol.source.ImageStatic');
    +
    +goog.require('goog.events');
    +goog.require('ol.Image');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.Image');
    +
    +
    +
    +/**
    + * @classdesc
    + * A layer source for displaying a single, static image.
    + *
    + * @constructor
    + * @extends {ol.source.Image}
    + * @param {olx.source.ImageStaticOptions} options Options.
    + * @api stable
    + */
    +ol.source.ImageStatic = function(options) {
    +
    +  var attributions = goog.isDef(options.attributions) ?
    +      options.attributions : null;
    +
    +  var imageExtent = options.imageExtent;
    +
    +  var resolution, resolutions;
    +  if (goog.isDef(options.imageSize)) {
    +    resolution = ol.extent.getHeight(imageExtent) / options.imageSize[1];
    +    resolutions = [resolution];
    +  }
    +
    +  var crossOrigin = goog.isDef(options.crossOrigin) ?
    +      options.crossOrigin : null;
    +
    +  var imageLoadFunction = goog.isDef(options.imageLoadFunction) ?
    +      options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
    +
    +  goog.base(this, {
    +    attributions: attributions,
    +    logo: options.logo,
    +    projection: ol.proj.get(options.projection),
    +    resolutions: resolutions
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.Image}
    +   */
    +  this.image_ = new ol.Image(imageExtent, resolution, 1, attributions,
    +      options.url, crossOrigin, imageLoadFunction);
    +
    +};
    +goog.inherits(ol.source.ImageStatic, ol.source.Image);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ImageStatic.prototype.getImage =
    +    function(extent, resolution, pixelRatio, projection) {
    +  if (ol.extent.intersects(extent, this.image_.getExtent())) {
    +    return this.image_;
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/ol/ol/source/imagevectorsource.js b/mapbox-gl/ol/ol/source/imagevectorsource.js
    new file mode 100644
    index 0000000000..67923a7b85
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagevectorsource.js
    @@ -0,0 +1,297 @@
    +goog.provide('ol.source.ImageVector');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.vec.Mat4');
    +goog.require('ol.dom');
    +goog.require('ol.extent');
    +goog.require('ol.render.canvas.ReplayGroup');
    +goog.require('ol.renderer.vector');
    +goog.require('ol.source.ImageCanvas');
    +goog.require('ol.source.Vector');
    +goog.require('ol.style.Style');
    +goog.require('ol.vec.Mat4');
    +
    +
    +
    +/**
    + * @classdesc
    + * An image source whose images are canvas elements into which vector features
    + * read from a vector source (`ol.source.Vector`) are drawn. An
    + * `ol.source.ImageVector` object is to be used as the `source` of an image
    + * layer (`ol.layer.Image`). Image layers are rotated, scaled, and translated,
    + * as opposed to being re-rendered, during animations and interactions. So, like
    + * any other image layer, an image layer configured with an
    + * `ol.source.ImageVector` will exhibit this behaviour. This is in contrast to a
    + * vector layer, where vector features are re-drawn during animations and
    + * interactions.
    + *
    + * @constructor
    + * @extends {ol.source.ImageCanvas}
    + * @param {olx.source.ImageVectorOptions} options Options.
    + * @api
    + */
    +ol.source.ImageVector = function(options) {
    +
    +  /**
    +   * @private
    +   * @type {ol.source.Vector}
    +   */
    +  this.source_ = options.source;
    +
    +  /**
    +   * @private
    +   * @type {!goog.vec.Mat4.Number}
    +   */
    +  this.transform_ = goog.vec.Mat4.createNumber();
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.canvasContext_ = ol.dom.createCanvasContext2D();
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.canvasSize_ = [0, 0];
    +
    +  /**
    +   * @private
    +   * @type {ol.render.canvas.ReplayGroup}
    +   */
    +  this.replayGroup_ = null;
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    canvasFunction: goog.bind(this.canvasFunctionInternal_, this),
    +    logo: options.logo,
    +    projection: options.projection,
    +    ratio: options.ratio,
    +    resolutions: options.resolutions,
    +    state: this.source_.getState()
    +  });
    +
    +  /**
    +   * User provided style.
    +   * @type {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    +   * @private
    +   */
    +  this.style_ = null;
    +
    +  /**
    +   * Style function for use within the library.
    +   * @type {ol.style.StyleFunction|undefined}
    +   * @private
    +   */
    +  this.styleFunction_ = undefined;
    +
    +  this.setStyle(options.style);
    +
    +  goog.events.listen(this.source_, goog.events.EventType.CHANGE,
    +      this.handleSourceChange_, undefined, this);
    +
    +};
    +goog.inherits(ol.source.ImageVector, ol.source.ImageCanvas);
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.Size} size Size.
    + * @param {ol.proj.Projection} projection Projection;
    + * @return {HTMLCanvasElement} Canvas element.
    + * @private
    + */
    +ol.source.ImageVector.prototype.canvasFunctionInternal_ =
    +    function(extent, resolution, pixelRatio, size, projection) {
    +
    +  var replayGroup = new ol.render.canvas.ReplayGroup(
    +      ol.renderer.vector.getTolerance(resolution, pixelRatio), extent,
    +      resolution);
    +
    +  this.source_.loadFeatures(extent, resolution, projection);
    +
    +  var loading = false;
    +  this.source_.forEachFeatureInExtentAtResolution(extent, resolution,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       */
    +      function(feature) {
    +        loading = loading ||
    +            this.renderFeature_(feature, resolution, pixelRatio, replayGroup);
    +      }, this);
    +  replayGroup.finish();
    +
    +  if (loading) {
    +    return null;
    +  }
    +
    +  if (this.canvasSize_[0] != size[0] || this.canvasSize_[1] != size[1]) {
    +    this.canvasContext_.canvas.width = size[0];
    +    this.canvasContext_.canvas.height = size[1];
    +    this.canvasSize_[0] = size[0];
    +    this.canvasSize_[1] = size[1];
    +  } else {
    +    this.canvasContext_.clearRect(0, 0, size[0], size[1]);
    +  }
    +
    +  var transform = this.getTransform_(ol.extent.getCenter(extent),
    +      resolution, pixelRatio, size);
    +  replayGroup.replay(this.canvasContext_, pixelRatio, transform, 0, {});
    +
    +  this.replayGroup_ = replayGroup;
    +
    +  return this.canvasContext_.canvas;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ImageVector.prototype.forEachFeatureAtCoordinate = function(
    +    coordinate, resolution, rotation, skippedFeatureUids, callback) {
    +  if (goog.isNull(this.replayGroup_)) {
    +    return undefined;
    +  } else {
    +    /** @type {Object.<string, boolean>} */
    +    var features = {};
    +    return this.replayGroup_.forEachFeatureAtCoordinate(
    +        coordinate, resolution, 0, skippedFeatureUids,
    +        /**
    +         * @param {ol.Feature} feature Feature.
    +         * @return {?} Callback result.
    +         */
    +        function(feature) {
    +          goog.asserts.assert(goog.isDef(feature));
    +          var key = goog.getUid(feature).toString();
    +          if (!(key in features)) {
    +            features[key] = true;
    +            return callback(feature);
    +          }
    +        });
    +  }
    +};
    +
    +
    +/**
    + * Get a reference to the wrapped source.
    + * @return {ol.source.Vector} Source.
    + * @api
    + */
    +ol.source.ImageVector.prototype.getSource = function() {
    +  return this.source_;
    +};
    +
    +
    +/**
    + * Get the style for features.  This returns whatever was passed to the `style`
    + * option at construction or to the `setStyle` method.
    + * @return {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction}
    + *     Layer style.
    + * @api stable
    + */
    +ol.source.ImageVector.prototype.getStyle = function() {
    +  return this.style_;
    +};
    +
    +
    +/**
    + * Get the style function.
    + * @return {ol.style.StyleFunction|undefined} Layer style function.
    + * @api stable
    + */
    +ol.source.ImageVector.prototype.getStyleFunction = function() {
    +  return this.styleFunction_;
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} center Center.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.Size} size Size.
    + * @return {!goog.vec.Mat4.Number} Transform.
    + * @private
    + */
    +ol.source.ImageVector.prototype.getTransform_ =
    +    function(center, resolution, pixelRatio, size) {
    +  return ol.vec.Mat4.makeTransform2D(this.transform_,
    +      size[0] / 2, size[1] / 2,
    +      pixelRatio / resolution, -pixelRatio / resolution,
    +      0,
    +      -center[0], -center[1]);
    +};
    +
    +
    +/**
    + * Handle changes in image style state.
    + * @param {goog.events.Event} event Image style change event.
    + * @private
    + */
    +ol.source.ImageVector.prototype.handleImageChange_ =
    +    function(event) {
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.ImageVector.prototype.handleSourceChange_ = function() {
    +  // setState will trigger a CHANGE event, so we always rely
    +  // change events by calling setState.
    +  this.setState(this.source_.getState());
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {number} resolution Resolution.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group.
    + * @return {boolean} `true` if an image is loading.
    + * @private
    + */
    +ol.source.ImageVector.prototype.renderFeature_ =
    +    function(feature, resolution, pixelRatio, replayGroup) {
    +  var styles;
    +  if (goog.isDef(feature.getStyleFunction())) {
    +    styles = feature.getStyleFunction().call(feature, resolution);
    +  } else if (goog.isDef(this.styleFunction_)) {
    +    styles = this.styleFunction_(feature, resolution);
    +  }
    +  if (!goog.isDefAndNotNull(styles)) {
    +    return false;
    +  }
    +  var i, ii, loading = false;
    +  for (i = 0, ii = styles.length; i < ii; ++i) {
    +    loading = ol.renderer.vector.renderFeature(
    +        replayGroup, feature, styles[i],
    +        ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio),
    +        this.handleImageChange_, this) || loading;
    +  }
    +  return loading;
    +};
    +
    +
    +/**
    + * Set the style for features.  This can be a single style object, an array
    + * of styles, or a function that takes a feature and resolution and returns
    + * an array of styles. If it is `undefined` the default style is used. If
    + * it is `null` the layer has no style (a `null` style), so only features
    + * that have their own styles will be rendered in the layer. See
    + * {@link ol.style} for information on the default style.
    + * @param {ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction|undefined}
    + *     style Layer style.
    + * @api stable
    + */
    +ol.source.ImageVector.prototype.setStyle = function(style) {
    +  this.style_ = goog.isDef(style) ? style : ol.style.defaultStyleFunction;
    +  this.styleFunction_ = goog.isNull(style) ?
    +      undefined : ol.style.createStyleFunction(this.style_);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/source/imagewmssource.js b/mapbox-gl/ol/ol/source/imagewmssource.js
    new file mode 100644
    index 0000000000..b058b82557
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/imagewmssource.js
    @@ -0,0 +1,376 @@
    +// FIXME cannot be shared between maps with different projections
    +
    +goog.provide('ol.source.ImageWMS');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('ol');
    +goog.require('ol.Image');
    +goog.require('ol.ImageLoadFunctionType');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.Image');
    +goog.require('ol.source.wms');
    +goog.require('ol.source.wms.ServerType');
    +
    +
    +
    +/**
    + * @classdesc
    + * Source for WMS servers providing single, untiled images.
    + *
    + * @constructor
    + * @extends {ol.source.Image}
    + * @param {olx.source.ImageWMSOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.source.ImageWMS = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    logo: options.logo,
    +    projection: options.projection,
    +    resolutions: options.resolutions
    +  });
    +
    +  /**
    +   * @private
    +   * @type {?string}
    +   */
    +  this.crossOrigin_ =
    +      goog.isDef(options.crossOrigin) ? options.crossOrigin : null;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.url_ = options.url;
    +
    +  /**
    +   * @private
    +   * @type {ol.ImageLoadFunctionType}
    +   */
    +  this.imageLoadFunction_ = goog.isDef(options.imageLoadFunction) ?
    +      options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction;
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.params_ = options.params;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.v13_ = true;
    +  this.updateV13_();
    +
    +  /**
    +   * @private
    +   * @type {ol.source.wms.ServerType|undefined}
    +   */
    +  this.serverType_ =
    +      /** @type {ol.source.wms.ServerType|undefined} */ (options.serverType);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.hidpi_ = goog.isDef(options.hidpi) ? options.hidpi : true;
    +
    +  /**
    +   * @private
    +   * @type {ol.Image}
    +   */
    +  this.image_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.imageSize_ = [0, 0];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.renderedRevision_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.ratio_ = goog.isDef(options.ratio) ? options.ratio : 1.5;
    +
    +};
    +goog.inherits(ol.source.ImageWMS, ol.source.Image);
    +
    +
    +/**
    + * @const
    + * @type {ol.Size}
    + * @private
    + */
    +ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_ = [101, 101];
    +
    +
    +/**
    + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
    + * projection. Return `undefined` if the GetFeatureInfo URL cannot be
    + * constructed.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {ol.proj.ProjectionLike} projection Projection.
    + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
    + *     be provided. If `QUERY_LAYERS` is not provided then the layers specified
    + *     in the `LAYERS` parameter will be used. `VERSION` should not be
    + *     specified here.
    + * @return {string|undefined} GetFeatureInfo URL.
    + * @api stable
    + */
    +ol.source.ImageWMS.prototype.getGetFeatureInfoUrl =
    +    function(coordinate, resolution, projection, params) {
    +
    +  goog.asserts.assert(!('VERSION' in params));
    +
    +  if (!goog.isDef(this.url_)) {
    +    return undefined;
    +  }
    +
    +  var extent = ol.extent.getForViewAndSize(
    +      coordinate, resolution, 0,
    +      ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_);
    +
    +  var baseParams = {
    +    'SERVICE': 'WMS',
    +    'VERSION': ol.DEFAULT_WMS_VERSION,
    +    'REQUEST': 'GetFeatureInfo',
    +    'FORMAT': 'image/png',
    +    'TRANSPARENT': true,
    +    'QUERY_LAYERS': this.params_['LAYERS']
    +  };
    +  goog.object.extend(baseParams, this.params_, params);
    +
    +  var x = Math.floor((coordinate[0] - extent[0]) / resolution);
    +  var y = Math.floor((extent[3] - coordinate[1]) / resolution);
    +  baseParams[this.v13_ ? 'I' : 'X'] = x;
    +  baseParams[this.v13_ ? 'J' : 'Y'] = y;
    +
    +  return this.getRequestUrl_(
    +      extent, ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_,
    +      1, ol.proj.get(projection), baseParams);
    +};
    +
    +
    +/**
    + * Get the user-provided params, i.e. those passed to the constructor through
    + * the "params" option, and possibly updated using the updateParams method.
    + * @return {Object} Params.
    + * @api stable
    + */
    +ol.source.ImageWMS.prototype.getParams = function() {
    +  return this.params_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ImageWMS.prototype.getImage =
    +    function(extent, resolution, pixelRatio, projection) {
    +
    +  if (!goog.isDef(this.url_)) {
    +    return null;
    +  }
    +
    +  resolution = this.findNearestResolution(resolution);
    +
    +  if (pixelRatio != 1 && (!this.hidpi_ || !goog.isDef(this.serverType_))) {
    +    pixelRatio = 1;
    +  }
    +
    +  var image = this.image_;
    +  if (!goog.isNull(image) &&
    +      this.renderedRevision_ == this.getRevision() &&
    +      image.getResolution() == resolution &&
    +      image.getPixelRatio() == pixelRatio &&
    +      ol.extent.containsExtent(image.getExtent(), extent)) {
    +    return image;
    +  }
    +
    +  var params = {
    +    'SERVICE': 'WMS',
    +    'VERSION': ol.DEFAULT_WMS_VERSION,
    +    'REQUEST': 'GetMap',
    +    'FORMAT': 'image/png',
    +    'TRANSPARENT': true
    +  };
    +  goog.object.extend(params, this.params_);
    +
    +  extent = extent.slice();
    +  var centerX = (extent[0] + extent[2]) / 2;
    +  var centerY = (extent[1] + extent[3]) / 2;
    +  if (this.ratio_ != 1) {
    +    var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2;
    +    var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2;
    +    extent[0] = centerX - halfWidth;
    +    extent[1] = centerY - halfHeight;
    +    extent[2] = centerX + halfWidth;
    +    extent[3] = centerY + halfHeight;
    +  }
    +
    +  var imageResolution = resolution / pixelRatio;
    +
    +  // Compute an integer width and height.
    +  var width = Math.ceil(ol.extent.getWidth(extent) / imageResolution);
    +  var height = Math.ceil(ol.extent.getHeight(extent) / imageResolution);
    +
    +  // Modify the extent to match the integer width and height.
    +  extent[0] = centerX - imageResolution * width / 2;
    +  extent[2] = centerX + imageResolution * width / 2;
    +  extent[1] = centerY - imageResolution * height / 2;
    +  extent[3] = centerY + imageResolution * height / 2;
    +
    +  this.imageSize_[0] = width;
    +  this.imageSize_[1] = height;
    +
    +  var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio,
    +      projection, params);
    +
    +  this.image_ = new ol.Image(extent, resolution, pixelRatio,
    +      this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_);
    +
    +  this.renderedRevision_ = this.getRevision();
    +
    +  return this.image_;
    +
    +};
    +
    +
    +/**
    + * @return {ol.ImageLoadFunctionType} The image load function.
    + * @api
    + */
    +ol.source.ImageWMS.prototype.getImageLoadFunction = function() {
    +  return this.imageLoadFunction_;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Size} size Size.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {Object} params Params.
    + * @return {string} Request URL.
    + * @private
    + */
    +ol.source.ImageWMS.prototype.getRequestUrl_ =
    +    function(extent, size, pixelRatio, projection, params) {
    +
    +  goog.asserts.assert(goog.isDef(this.url_));
    +
    +  params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode();
    +
    +  if (!('STYLES' in this.params_)) {
    +    /* jshint -W053 */
    +    params['STYLES'] = new String('');
    +    /* jshint +W053 */
    +  }
    +
    +  if (pixelRatio != 1) {
    +    switch (this.serverType_) {
    +      case ol.source.wms.ServerType.GEOSERVER:
    +        var dpi = (90 * pixelRatio + 0.5) | 0;
    +        params['FORMAT_OPTIONS'] = 'dpi:' + dpi;
    +        break;
    +      case ol.source.wms.ServerType.MAPSERVER:
    +        params['MAP_RESOLUTION'] = 90 * pixelRatio;
    +        break;
    +      case ol.source.wms.ServerType.CARMENTA_SERVER:
    +      case ol.source.wms.ServerType.QGIS:
    +        params['DPI'] = 90 * pixelRatio;
    +        break;
    +      default:
    +        goog.asserts.fail();
    +        break;
    +    }
    +  }
    +
    +  params['WIDTH'] = size[0];
    +  params['HEIGHT'] = size[1];
    +
    +  var axisOrientation = projection.getAxisOrientation();
    +  var bbox;
    +  if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') {
    +    bbox = [extent[1], extent[0], extent[3], extent[2]];
    +  } else {
    +    bbox = extent;
    +  }
    +  params['BBOX'] = bbox.join(',');
    +
    +  return goog.uri.utils.appendParamsFromMap(this.url_, params);
    +};
    +
    +
    +/**
    + * Return the URL used for this WMS source.
    + * @return {string|undefined} URL.
    + * @api stable
    + */
    +ol.source.ImageWMS.prototype.getUrl = function() {
    +  return this.url_;
    +};
    +
    +
    +/**
    + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function.
    + * @api
    + */
    +ol.source.ImageWMS.prototype.setImageLoadFunction = function(
    +    imageLoadFunction) {
    +  this.image_ = null;
    +  this.imageLoadFunction_ = imageLoadFunction;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {string|undefined} url URL.
    + * @api stable
    + */
    +ol.source.ImageWMS.prototype.setUrl = function(url) {
    +  if (url != this.url_) {
    +    this.url_ = url;
    +    this.image_ = null;
    +    this.changed();
    +  }
    +};
    +
    +
    +/**
    + * Update the user-provided params.
    + * @param {Object} params Params.
    + * @api stable
    + */
    +ol.source.ImageWMS.prototype.updateParams = function(params) {
    +  goog.object.extend(this.params_, params);
    +  this.updateV13_();
    +  this.image_ = null;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.ImageWMS.prototype.updateV13_ = function() {
    +  var version =
    +      goog.object.get(this.params_, 'VERSION', ol.DEFAULT_WMS_VERSION);
    +  this.v13_ = goog.string.compareVersions(version, '1.3') >= 0;
    +};
    diff --git a/mapbox-gl/ol/ol/source/kmlsource.js b/mapbox-gl/ol/ol/source/kmlsource.js
    new file mode 100644
    index 0000000000..13c30f7f8e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/kmlsource.js
    @@ -0,0 +1,38 @@
    +goog.provide('ol.source.KML');
    +
    +goog.require('ol.format.KML');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in KML format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.KMLOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.KML = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    doc: options.doc,
    +    format: new ol.format.KML({
    +      extractStyles: options.extractStyles,
    +      defaultStyle: options.defaultStyle
    +    }),
    +    logo: options.logo,
    +    node: options.node,
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url,
    +    urls: options.urls
    +  });
    +
    +};
    +goog.inherits(ol.source.KML, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/mapquestsource.js b/mapbox-gl/ol/ol/source/mapquestsource.js
    new file mode 100644
    index 0000000000..b0f7f05fe5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/mapquestsource.js
    @@ -0,0 +1,99 @@
    +goog.provide('ol.source.MapQuest');
    +
    +goog.require('goog.asserts');
    +goog.require('ol');
    +goog.require('ol.Attribution');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for the MapQuest tile server.
    + *
    + * @constructor
    + * @extends {ol.source.XYZ}
    + * @param {olx.source.MapQuestOptions=} opt_options MapQuest options.
    + * @api stable
    + */
    +ol.source.MapQuest = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +  goog.asserts.assert(options.layer in ol.source.MapQuestConfig);
    +
    +  var layerConfig = ol.source.MapQuestConfig[options.layer];
    +
    +  /**
    +   * Layer. Possible values are `osm`, `sat`, and `hyb`.
    +   * @type {string}
    +   * @private
    +   */
    +  this.layer_ = options.layer;
    +
    +  var protocol = ol.IS_HTTPS ? 'https:' : 'http:';
    +  var url = goog.isDef(options.url) ? options.url :
    +      protocol + '//otile{1-4}-s.mqcdn.com/tiles/1.0.0/' +
    +      this.layer_ + '/{z}/{x}/{y}.jpg';
    +
    +  goog.base(this, {
    +    attributions: layerConfig.attributions,
    +    crossOrigin: 'anonymous',
    +    logo: '//developer.mapquest.com/content/osm/mq_logo.png',
    +    maxZoom: layerConfig.maxZoom,
    +    opaque: true,
    +    tileLoadFunction: options.tileLoadFunction,
    +    url: url
    +  });
    +
    +};
    +goog.inherits(ol.source.MapQuest, ol.source.XYZ);
    +
    +
    +/**
    + * @const
    + * @type {ol.Attribution}
    + */
    +ol.source.MapQuest.TILE_ATTRIBUTION = new ol.Attribution({
    +  html: 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a>'
    +});
    +
    +
    +/**
    + * @type {Object.<string, {maxZoom: number, attributions: (Array.<ol.Attribution>)}>}
    + */
    +ol.source.MapQuestConfig = {
    +  'osm': {
    +    maxZoom: 19,
    +    attributions: [
    +      ol.source.MapQuest.TILE_ATTRIBUTION,
    +      ol.source.OSM.ATTRIBUTION
    +    ]
    +  },
    +  'sat': {
    +    maxZoom: 18,
    +    attributions: [
    +      ol.source.MapQuest.TILE_ATTRIBUTION,
    +      new ol.Attribution({
    +        html: 'Portions Courtesy NASA/JPL-Caltech and ' +
    +            'U.S. Depart. of Agriculture, Farm Service Agency'
    +      })
    +    ]
    +  },
    +  'hyb': {
    +    maxZoom: 18,
    +    attributions: [
    +      ol.source.MapQuest.TILE_ATTRIBUTION,
    +      ol.source.OSM.ATTRIBUTION
    +    ]
    +  }
    +};
    +
    +
    +/**
    + * @return {string} Layer.
    + * @api
    + */
    +ol.source.MapQuest.prototype.getLayer = function() {
    +  return this.layer_;
    +};
    diff --git a/mapbox-gl/ol/ol/source/osmsource.js b/mapbox-gl/ol/ol/source/osmsource.js
    new file mode 100644
    index 0000000000..7f992f136e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/osmsource.js
    @@ -0,0 +1,59 @@
    +goog.provide('ol.source.OSM');
    +
    +goog.require('ol');
    +goog.require('ol.Attribution');
    +goog.require('ol.source.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for the OpenStreetMap tile server.
    + *
    + * @constructor
    + * @extends {ol.source.XYZ}
    + * @param {olx.source.OSMOptions=} opt_options Open Street Map options.
    + * @api stable
    + */
    +ol.source.OSM = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var attributions;
    +  if (goog.isDef(options.attributions)) {
    +    attributions = options.attributions;
    +  } else {
    +    attributions = [ol.source.OSM.ATTRIBUTION];
    +  }
    +
    +  var crossOrigin = goog.isDef(options.crossOrigin) ?
    +      options.crossOrigin : 'anonymous';
    +
    +  var protocol = ol.IS_HTTPS ? 'https:' : 'http:';
    +  var url = goog.isDef(options.url) ?
    +      options.url : protocol + '//{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png';
    +
    +  goog.base(this, {
    +    attributions: attributions,
    +    crossOrigin: crossOrigin,
    +    opaque: true,
    +    maxZoom: goog.isDef(options.maxZoom) ? options.maxZoom : 19,
    +    tileLoadFunction: options.tileLoadFunction,
    +    url: url,
    +    wrapX: options.wrapX
    +  });
    +
    +};
    +goog.inherits(ol.source.OSM, ol.source.XYZ);
    +
    +
    +/**
    + * @const
    + * @type {ol.Attribution}
    + * @api
    + */
    +ol.source.OSM.ATTRIBUTION = new ol.Attribution({
    +  html: '&copy; ' +
    +      '<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> ' +
    +      'contributors.'
    +});
    diff --git a/mapbox-gl/ol/ol/source/osmxmlsource.js b/mapbox-gl/ol/ol/source/osmxmlsource.js
    new file mode 100644
    index 0000000000..d1ba44f49b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/osmxmlsource.js
    @@ -0,0 +1,35 @@
    +goog.provide('ol.source.OSMXML');
    +
    +goog.require('ol.format.OSMXML');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in OSMXML format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.OSMXMLOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.OSMXML = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    doc: options.doc,
    +    format: new ol.format.OSMXML(),
    +    logo: options.logo,
    +    node: options.node,
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url,
    +    urls: options.urls
    +  });
    +
    +};
    +goog.inherits(ol.source.OSMXML, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/servervectorsource.js b/mapbox-gl/ol/ol/source/servervectorsource.js
    new file mode 100644
    index 0000000000..8d36a4c0bc
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/servervectorsource.js
    @@ -0,0 +1,126 @@
    +// FIXME cache expiration
    +
    +goog.provide('ol.source.ServerVector');
    +
    +goog.require('goog.object');
    +goog.require('ol.extent');
    +goog.require('ol.loadingstrategy');
    +goog.require('ol.source.FormatVector');
    +goog.require('ol.structs.RBush');
    +
    +
    +
    +/**
    + * @classdesc
    + * A vector source in one of the supported formats, using a custom function to
    + * read in the data from a remote server.
    + *
    + * @constructor
    + * @extends {ol.source.FormatVector}
    + * @param {olx.source.ServerVectorOptions} options Options.
    + * @api
    + */
    +ol.source.ServerVector = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    format: options.format,
    +    logo: options.logo,
    +    projection: options.projection
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.structs.RBush.<{extent: ol.Extent}>}
    +   */
    +  this.loadedExtents_ = new ol.structs.RBush();
    +
    +  /**
    +   * @private
    +   * @type {function(this: ol.source.ServerVector, ol.Extent, number,
    +   *                 ol.proj.Projection)}
    +   */
    +  this.loader_ = options.loader;
    +
    +  /**
    +   * @private
    +   * @type {function(ol.Extent, number): Array.<ol.Extent>}
    +   */
    +  this.strategy_ = goog.isDef(options.strategy) ?
    +      options.strategy : ol.loadingstrategy.bbox;
    +
    +  /**
    +   * @private
    +   * @type {Object.<number|string, boolean>}
    +   */
    +  this.loadedFeatures_ = {};
    +
    +};
    +goog.inherits(ol.source.ServerVector, ol.source.FormatVector);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ServerVector.prototype.addFeaturesInternal = function(features) {
    +  /** @type {Array.<ol.Feature>} */
    +  var notLoadedFeatures = [];
    +  var i, ii;
    +  for (i = 0, ii = features.length; i < ii; ++i) {
    +    var feature = features[i];
    +    var featureId = feature.getId();
    +    if (!goog.isDef(featureId)) {
    +      notLoadedFeatures.push(feature);
    +    } else if (!(featureId in this.loadedFeatures_)) {
    +      notLoadedFeatures.push(feature);
    +      this.loadedFeatures_[featureId] = true;
    +    }
    +  }
    +  goog.base(this, 'addFeaturesInternal', notLoadedFeatures);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api stable
    + */
    +ol.source.ServerVector.prototype.clear = function(opt_fast) {
    +  goog.object.clear(this.loadedFeatures_);
    +  this.loadedExtents_.clear();
    +  goog.base(this, 'clear', opt_fast);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ServerVector.prototype.loadFeatures =
    +    function(extent, resolution, projection) {
    +  var loadedExtents = this.loadedExtents_;
    +  var extentsToLoad = this.strategy_(extent, resolution);
    +  var i, ii;
    +  for (i = 0, ii = extentsToLoad.length; i < ii; ++i) {
    +    var extentToLoad = extentsToLoad[i];
    +    var alreadyLoaded = loadedExtents.forEachInExtent(extentToLoad,
    +        /**
    +         * @param {{extent: ol.Extent}} object Object.
    +         * @return {boolean} Contains.
    +         */
    +        function(object) {
    +          return ol.extent.containsExtent(object.extent, extentToLoad);
    +        });
    +    if (!alreadyLoaded) {
    +      this.loader_.call(this, extentToLoad, resolution, projection);
    +      loadedExtents.insert(extentToLoad, {extent: extentToLoad.slice()});
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @function
    + * @param {ArrayBuffer|Document|Node|Object|string} source Source.
    + * @return {Array.<ol.Feature>} Features.
    + * @api
    + */
    +ol.source.ServerVector.prototype.readFeatures;
    diff --git a/mapbox-gl/ol/ol/source/source.js b/mapbox-gl/ol/ol/source/source.js
    new file mode 100644
    index 0000000000..58745b22db
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/source.js
    @@ -0,0 +1,165 @@
    +goog.provide('ol.source.Source');
    +goog.provide('ol.source.State');
    +
    +goog.require('goog.events.EventType');
    +goog.require('ol.Attribution');
    +goog.require('ol.Object');
    +goog.require('ol.proj');
    +
    +
    +/**
    + * State of the source, one of 'undefined', 'loading', 'ready' or 'error'.
    + * @enum {string}
    + * @api
    + */
    +ol.source.State = {
    +  UNDEFINED: 'undefined',
    +  LOADING: 'loading',
    +  READY: 'ready',
    +  ERROR: 'error'
    +};
    +
    +
    +/**
    + * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
    + *            logo: (string|olx.LogoOptions|undefined),
    + *            projection: ol.proj.ProjectionLike,
    + *            state: (ol.source.State|undefined)}}
    + */
    +ol.source.SourceOptions;
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for {@link ol.layer.Layer} sources.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @fires change Triggered when the state of the source changes.
    + * @param {ol.source.SourceOptions} options Source options.
    + * @api stable
    + */
    +ol.source.Source = function(options) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {ol.proj.Projection}
    +   */
    +  this.projection_ = ol.proj.get(options.projection);
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.Attribution>}
    +   */
    +  this.attributions_ = goog.isDef(options.attributions) ?
    +      options.attributions : null;
    +
    +  /**
    +   * @private
    +   * @type {string|olx.LogoOptions|undefined}
    +   */
    +  this.logo_ = options.logo;
    +
    +  /**
    +   * @private
    +   * @type {ol.source.State}
    +   */
    +  this.state_ = goog.isDef(options.state) ?
    +      options.state : ol.source.State.READY;
    +
    +};
    +goog.inherits(ol.source.Source, ol.Object);
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {number} rotation Rotation.
    + * @param {Object.<string, boolean>} skippedFeatureUids Skipped feature uids.
    + * @param {function(ol.Feature): T} callback Feature callback.
    + * @return {T|undefined} Callback result.
    + * @template T
    + */
    +ol.source.Source.prototype.forEachFeatureAtCoordinate =
    +    goog.nullFunction;
    +
    +
    +/**
    + * @return {Array.<ol.Attribution>} Attributions.
    + * @api stable
    + */
    +ol.source.Source.prototype.getAttributions = function() {
    +  return this.attributions_;
    +};
    +
    +
    +/**
    + * @return {string|olx.LogoOptions|undefined} Logo.
    + * @api stable
    + */
    +ol.source.Source.prototype.getLogo = function() {
    +  return this.logo_;
    +};
    +
    +
    +/**
    + * @return {ol.proj.Projection} Projection.
    + * @api
    + */
    +ol.source.Source.prototype.getProjection = function() {
    +  return this.projection_;
    +};
    +
    +
    +/**
    + * @return {Array.<number>|undefined} Resolutions.
    + */
    +ol.source.Source.prototype.getResolutions = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.source.State} State.
    + * @api
    + */
    +ol.source.Source.prototype.getState = function() {
    +  return this.state_;
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Attribution>} attributions Attributions.
    + */
    +ol.source.Source.prototype.setAttributions = function(attributions) {
    +  this.attributions_ = attributions;
    +};
    +
    +
    +/**
    + * @param {string|olx.LogoOptions|undefined} logo Logo.
    + */
    +ol.source.Source.prototype.setLogo = function(logo) {
    +  this.logo_ = logo;
    +};
    +
    +
    +/**
    + * @param {ol.source.State} state State.
    + * @protected
    + */
    +ol.source.Source.prototype.setState = function(state) {
    +  this.state_ = state;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.proj.Projection} projection Projetion.
    + */
    +ol.source.Source.prototype.setProjection = function(projection) {
    +  this.projection_ = projection;
    +};
    diff --git a/mapbox-gl/ol/ol/source/stamensource.js b/mapbox-gl/ol/ol/source/stamensource.js
    new file mode 100644
    index 0000000000..2980756c2b
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/stamensource.js
    @@ -0,0 +1,132 @@
    +goog.provide('ol.source.Stamen');
    +
    +goog.require('goog.asserts');
    +goog.require('ol');
    +goog.require('ol.Attribution');
    +goog.require('ol.source.OSM');
    +goog.require('ol.source.XYZ');
    +
    +
    +/**
    + * @type {Object.<string, {extension: string, opaque: boolean}>}
    + */
    +ol.source.StamenLayerConfig = {
    +  'terrain': {
    +    extension: 'jpg',
    +    opaque: true
    +  },
    +  'terrain-background': {
    +    extension: 'jpg',
    +    opaque: true
    +  },
    +  'terrain-labels': {
    +    extension: 'png',
    +    opaque: false
    +  },
    +  'terrain-lines': {
    +    extension: 'png',
    +    opaque: false
    +  },
    +  'toner-background': {
    +    extension: 'png',
    +    opaque: true
    +  },
    +  'toner': {
    +    extension: 'png',
    +    opaque: true
    +  },
    +  'toner-hybrid': {
    +    extension: 'png',
    +    opaque: false
    +  },
    +  'toner-labels': {
    +    extension: 'png',
    +    opaque: false
    +  },
    +  'toner-lines': {
    +    extension: 'png',
    +    opaque: false
    +  },
    +  'toner-lite': {
    +    extension: 'png',
    +    opaque: true
    +  },
    +  'watercolor': {
    +    extension: 'jpg',
    +    opaque: true
    +  }
    +};
    +
    +
    +/**
    + * @type {Object.<string, {minZoom: number, maxZoom: number}>}
    + */
    +ol.source.StamenProviderConfig = {
    +  'terrain': {
    +    minZoom: 4,
    +    maxZoom: 18
    +  },
    +  'toner': {
    +    minZoom: 0,
    +    maxZoom: 20
    +  },
    +  'watercolor': {
    +    minZoom: 3,
    +    maxZoom: 16
    +  }
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for the Stamen tile server.
    + *
    + * @constructor
    + * @extends {ol.source.XYZ}
    + * @param {olx.source.StamenOptions} options Stamen options.
    + * @api stable
    + */
    +ol.source.Stamen = function(options) {
    +
    +  var i = options.layer.indexOf('-');
    +  var provider = i == -1 ? options.layer : options.layer.slice(0, i);
    +  goog.asserts.assert(provider in ol.source.StamenProviderConfig);
    +  var providerConfig = ol.source.StamenProviderConfig[provider];
    +
    +  goog.asserts.assert(options.layer in ol.source.StamenLayerConfig);
    +  var layerConfig = ol.source.StamenLayerConfig[options.layer];
    +
    +  var root = ol.IS_HTTPS ? 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' :
    +      'http://{a-d}.tile.stamen.com/';
    +  var url = goog.isDef(options.url) ? options.url :
    +      root + options.layer + '/{z}/{x}/{y}.' +
    +      layerConfig.extension;
    +
    +  goog.base(this, {
    +    attributions: ol.source.Stamen.ATTRIBUTIONS,
    +    crossOrigin: 'anonymous',
    +    maxZoom: providerConfig.maxZoom,
    +    // FIXME uncomment the following when tilegrid supports minZoom
    +    //minZoom: providerConfig.minZoom,
    +    opaque: layerConfig.opaque,
    +    tileLoadFunction: options.tileLoadFunction,
    +    url: url
    +  });
    +
    +};
    +goog.inherits(ol.source.Stamen, ol.source.XYZ);
    +
    +
    +/**
    + * @const
    + * @type {Array.<ol.Attribution>}
    + */
    +ol.source.Stamen.ATTRIBUTIONS = [
    +  new ol.Attribution({
    +    html: 'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, ' +
    +        'under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY' +
    +        ' 3.0</a>.'
    +  }),
    +  ol.source.OSM.ATTRIBUTION
    +];
    diff --git a/mapbox-gl/ol/ol/source/staticvectorsource.js b/mapbox-gl/ol/ol/source/staticvectorsource.js
    new file mode 100644
    index 0000000000..30b5e330e7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/staticvectorsource.js
    @@ -0,0 +1,83 @@
    +goog.provide('ol.source.StaticVector');
    +
    +goog.require('ol.source.FormatVector');
    +goog.require('ol.source.State');
    +
    +
    +
    +/**
    + * @classdesc
    + * A vector source that uses one of the supported formats to read the data from
    + * a file or other static source.
    + *
    + * @constructor
    + * @extends {ol.source.FormatVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.StaticVectorOptions} options Options.
    + * @api
    + */
    +ol.source.StaticVector = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    format: options.format,
    +    logo: options.logo,
    +    projection: options.projection
    +  });
    +
    +  if (goog.isDef(options.arrayBuffer)) {
    +    this.addFeaturesInternal(this.readFeatures(options.arrayBuffer));
    +  }
    +
    +  if (goog.isDef(options.doc)) {
    +    this.addFeaturesInternal(this.readFeatures(options.doc));
    +  }
    +
    +  if (goog.isDef(options.node)) {
    +    this.addFeaturesInternal(this.readFeatures(options.node));
    +  }
    +
    +  if (goog.isDef(options.object)) {
    +    this.addFeaturesInternal(this.readFeatures(options.object));
    +  }
    +
    +  if (goog.isDef(options.text)) {
    +    this.addFeaturesInternal(this.readFeatures(options.text));
    +  }
    +
    +  if (goog.isDef(options.url) || goog.isDef(options.urls)) {
    +    this.setState(ol.source.State.LOADING);
    +    if (goog.isDef(options.url)) {
    +      this.loadFeaturesFromURL(options.url,
    +          this.onFeaturesLoadedSuccess_, this.onFeaturesLoadedError_, this);
    +    }
    +    if (goog.isDef(options.urls)) {
    +      var urls = options.urls;
    +      var i, ii;
    +      for (i = 0, ii = urls.length; i < ii; ++i) {
    +        this.loadFeaturesFromURL(urls[i],
    +            this.onFeaturesLoadedSuccess_, this.onFeaturesLoadedError_, this);
    +      }
    +    }
    +  }
    +
    +};
    +goog.inherits(ol.source.StaticVector, ol.source.FormatVector);
    +
    +
    +/**
    + * @private
    + */
    +ol.source.StaticVector.prototype.onFeaturesLoadedError_ = function() {
    +  this.setState(ol.source.State.ERROR);
    +};
    +
    +
    +/**
    + * @param {Array.<ol.Feature>} features Features.
    + * @private
    + */
    +ol.source.StaticVector.prototype.onFeaturesLoadedSuccess_ = function(features) {
    +  this.addFeaturesInternal(features);
    +  this.setState(ol.source.State.READY);
    +};
    diff --git a/mapbox-gl/ol/ol/source/tilearcgisrestsource.js b/mapbox-gl/ol/ol/source/tilearcgisrestsource.js
    new file mode 100644
    index 0000000000..a0c387ad8a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tilearcgisrestsource.js
    @@ -0,0 +1,236 @@
    +goog.provide('ol.source.TileArcGISRest');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('ol');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilecoord');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data from ArcGIS Rest services. Map and Image
    + * Services are supported.
    + *
    + * For cached ArcGIS services, better performance is available using the
    + * {@link ol.source.XYZ} data source.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.TileArcGISRestOptions=} opt_options Tile ArcGIS Rest
    + *     options.
    + * @api
    + */
    +ol.source.TileArcGISRest = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var params = goog.isDef(options.params) ? options.params : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    logo: options.logo,
    +    projection: options.projection,
    +    tileGrid: options.tileGrid,
    +    tileLoadFunction: options.tileLoadFunction,
    +    tileUrlFunction: goog.bind(this.tileUrlFunction_, this)
    +  });
    +
    +  var urls = options.urls;
    +  if (!goog.isDef(urls) && goog.isDef(options.url)) {
    +    urls = ol.TileUrlFunction.expandUrl(options.url);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {!Array.<string>}
    +   */
    +  this.urls_ = goog.isDefAndNotNull(urls) ? urls : [];
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.params_ = params;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.tmpExtent_ = ol.extent.createEmpty();
    +
    +};
    +goog.inherits(ol.source.TileArcGISRest, ol.source.TileImage);
    +
    +
    +/**
    + * Get the user-provided params, i.e. those passed to the constructor through
    + * the "params" option, and possibly updated using the updateParams method.
    + * @return {Object} Params.
    + * @api
    + */
    +ol.source.TileArcGISRest.prototype.getParams = function() {
    +  return this.params_;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {number} tileSize Tile size.
    + * @param {ol.Extent} tileExtent Tile extent.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {Object} params Params.
    + * @return {string|undefined} Request URL.
    + * @private
    + */
    +ol.source.TileArcGISRest.prototype.getRequestUrl_ =
    +    function(tileCoord, tileSize, tileExtent,
    +        pixelRatio, projection, params) {
    +
    +  var urls = this.urls_;
    +  if (goog.array.isEmpty(urls)) {
    +    return undefined;
    +  }
    +
    +  // ArcGIS Server only wants the numeric portion of the projection ID.
    +  var srid = projection.getCode().split(':').pop();
    +
    +  params['SIZE'] = tileSize + ',' + tileSize;
    +  params['BBOX'] = tileExtent.join(',');
    +  params['BBOXSR'] = srid;
    +  params['IMAGESR'] = srid;
    +  params['DPI'] = 90 * pixelRatio;
    +
    +  var url;
    +  if (urls.length == 1) {
    +    url = urls[0];
    +  } else {
    +    var index = goog.math.modulo(ol.tilecoord.hash(tileCoord), urls.length);
    +    url = urls[index];
    +  }
    +
    +  if (!goog.string.endsWith(url, '/')) {
    +    url = url + '/';
    +  }
    +
    +  // If a MapServer, use export. If an ImageServer, use exportImage.
    +  if (goog.string.endsWith(url, 'MapServer/')) {
    +    url = url + 'export';
    +  }
    +  else if (goog.string.endsWith(url, 'ImageServer/')) {
    +    url = url + 'exportImage';
    +  }
    +  else {
    +    goog.asserts.fail('Unknown Rest Service', url);
    +  }
    +
    +  return goog.uri.utils.appendParamsFromMap(url, params);
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {number} Size.
    + */
    +ol.source.TileArcGISRest.prototype.getTilePixelSize =
    +    function(z, pixelRatio, projection) {
    +  var tileSize = goog.base(this, 'getTilePixelSize', z, pixelRatio, projection);
    +  if (pixelRatio == 1) {
    +    return tileSize;
    +  } else {
    +    return (tileSize * pixelRatio + 0.5) | 0;
    +  }
    +};
    +
    +
    +/**
    + * Return the URLs used for this ArcGIS source.
    + * @return {!Array.<string>} URLs.
    + * @api stable
    + */
    +ol.source.TileArcGISRest.prototype.getUrls = function() {
    +  return this.urls_;
    +};
    +
    +
    +/**
    + * @param {string|undefined} url URL.
    + * @api stable
    + */
    +ol.source.TileArcGISRest.prototype.setUrl = function(url) {
    +  var urls = goog.isDef(url) ? ol.TileUrlFunction.expandUrl(url) : null;
    +  this.setUrls(urls);
    +};
    +
    +
    +/**
    + * @param {Array.<string>|undefined} urls URLs.
    + * @api stable
    + */
    +ol.source.TileArcGISRest.prototype.setUrls = function(urls) {
    +  this.urls_ = goog.isDefAndNotNull(urls) ? urls : [];
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {string|undefined} Tile URL.
    + * @private
    + */
    +ol.source.TileArcGISRest.prototype.tileUrlFunction_ =
    +    function(tileCoord, pixelRatio, projection) {
    +
    +  var tileGrid = this.getTileGrid();
    +  if (goog.isNull(tileGrid)) {
    +    tileGrid = this.getTileGridForProjection(projection);
    +  }
    +
    +  if (tileGrid.getResolutions().length <= tileCoord[0]) {
    +    return undefined;
    +  }
    +
    +  var tileExtent = tileGrid.getTileCoordExtent(
    +      tileCoord, this.tmpExtent_);
    +  var tileSize = tileGrid.getTileSize(tileCoord[0]);
    +
    +  if (pixelRatio != 1) {
    +    tileSize = (tileSize * pixelRatio + 0.5) | 0;
    +  }
    +
    +  // Apply default params and override with user specified values.
    +  var baseParams = {
    +    'F': 'image',
    +    'FORMAT': 'PNG32',
    +    'TRANSPARENT': true
    +  };
    +  goog.object.extend(baseParams, this.params_);
    +
    +  return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
    +      pixelRatio, projection, baseParams);
    +};
    +
    +
    +/**
    + * Update the user-provided params.
    + * @param {Object} params Params.
    + * @api stable
    + */
    +ol.source.TileArcGISRest.prototype.updateParams = function(params) {
    +  goog.object.extend(this.params_, params);
    +  this.changed();
    +};
    diff --git a/mapbox-gl/ol/ol/source/tiledebugsource.js b/mapbox-gl/ol/ol/source/tiledebugsource.js
    new file mode 100644
    index 0000000000..ae49401d95
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tiledebugsource.js
    @@ -0,0 +1,107 @@
    +goog.provide('ol.source.TileDebug');
    +
    +goog.require('ol.Tile');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileState');
    +goog.require('ol.dom');
    +goog.require('ol.source.Tile');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.Tile}
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
    + * @private
    + */
    +ol.DebugTile_ = function(tileCoord, tileGrid) {
    +
    +  goog.base(this, tileCoord, ol.TileState.LOADED);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.tileSize_ = tileGrid.getTileSize(tileCoord[0]);
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, HTMLCanvasElement>}
    +   */
    +  this.canvasByContext_ = {};
    +
    +};
    +goog.inherits(ol.DebugTile_, ol.Tile);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.DebugTile_.prototype.getImage = function(opt_context) {
    +  var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1;
    +  if (key in this.canvasByContext_) {
    +    return this.canvasByContext_[key];
    +  } else {
    +
    +    var tileSize = this.tileSize_;
    +    var context = ol.dom.createCanvasContext2D(tileSize, tileSize);
    +
    +    context.strokeStyle = 'black';
    +    context.strokeRect(0.5, 0.5, tileSize + 0.5, tileSize + 0.5);
    +
    +    context.fillStyle = 'black';
    +    context.textAlign = 'center';
    +    context.textBaseline = 'middle';
    +    context.font = '24px sans-serif';
    +    context.fillText(ol.tilecoord.toString(this.tileCoord),
    +        tileSize / 2, tileSize / 2);
    +
    +    this.canvasByContext_[key] = context.canvas;
    +    return context.canvas;
    +
    +  }
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * A pseudo tile source, which does not fetch tiles from a server, but renders
    + * a grid outline for the tile grid/projection along with the coordinates for
    + * each tile. See examples/canvas-tiles for an example.
    + *
    + * Uses Canvas context2d, so requires Canvas support.
    + *
    + * @constructor
    + * @extends {ol.source.Tile}
    + * @param {olx.source.TileDebugOptions} options Debug tile options.
    + * @api
    + */
    +ol.source.TileDebug = function(options) {
    +
    +  goog.base(this, {
    +    opaque: false,
    +    projection: options.projection,
    +    tileGrid: options.tileGrid
    +  });
    +
    +};
    +goog.inherits(ol.source.TileDebug, ol.source.Tile);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileDebug.prototype.getTile = function(z, x, y) {
    +  var tileCoordKey = this.getKeyZXY(z, x, y);
    +  if (this.tileCache.containsKey(tileCoordKey)) {
    +    return /** @type {!ol.DebugTile_} */ (this.tileCache.get(tileCoordKey));
    +  } else {
    +    var tile = new ol.DebugTile_([z, x, y], this.tileGrid);
    +    this.tileCache.set(tileCoordKey, tile);
    +    return tile;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/source/tileimagesource.js b/mapbox-gl/ol/ol/source/tileimagesource.js
    new file mode 100644
    index 0000000000..9b1babe5d2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tileimagesource.js
    @@ -0,0 +1,185 @@
    +goog.provide('ol.source.TileImage');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.ImageTile');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileLoadFunctionType');
    +goog.require('ol.TileState');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.TileUrlFunctionType');
    +goog.require('ol.source.Tile');
    +
    +
    +
    +/**
    + * @classdesc
    + * Base class for sources providing images divided into a tile grid.
    + *
    + * @constructor
    + * @fires ol.source.TileEvent
    + * @extends {ol.source.Tile}
    + * @param {olx.source.TileImageOptions} options Image tile options.
    + * @api
    + */
    +ol.source.TileImage = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    logo: options.logo,
    +    opaque: options.opaque,
    +    projection: options.projection,
    +    state: goog.isDef(options.state) ?
    +        /** @type {ol.source.State} */ (options.state) : undefined,
    +    tileGrid: options.tileGrid,
    +    tilePixelRatio: options.tilePixelRatio
    +  });
    +
    +  /**
    +   * @protected
    +   * @type {ol.TileUrlFunctionType}
    +   */
    +  this.tileUrlFunction = goog.isDef(options.tileUrlFunction) ?
    +      options.tileUrlFunction :
    +      ol.TileUrlFunction.nullTileUrlFunction;
    +
    +  /**
    +   * @protected
    +   * @type {?string}
    +   */
    +  this.crossOrigin =
    +      goog.isDef(options.crossOrigin) ? options.crossOrigin : null;
    +
    +  /**
    +   * @protected
    +   * @type {ol.TileLoadFunctionType}
    +   */
    +  this.tileLoadFunction = goog.isDef(options.tileLoadFunction) ?
    +      options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction;
    +
    +  /**
    +   * @protected
    +   * @type {function(new: ol.ImageTile, ol.TileCoord, ol.TileState, string,
    +   *        ?string, ol.TileLoadFunctionType)}
    +   */
    +  this.tileClass = goog.isDef(options.tileClass) ?
    +      options.tileClass : ol.ImageTile;
    +
    +};
    +goog.inherits(ol.source.TileImage, ol.source.Tile);
    +
    +
    +/**
    + * @param {ol.ImageTile} imageTile Image tile.
    + * @param {string} src Source.
    + */
    +ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) {
    +  imageTile.getImage().src = src;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileImage.prototype.getTile =
    +    function(z, x, y, pixelRatio, projection) {
    +  var tileCoordKey = this.getKeyZXY(z, x, y);
    +  if (this.tileCache.containsKey(tileCoordKey)) {
    +    return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
    +  } else {
    +    goog.asserts.assert(projection);
    +    var tileCoord = [z, x, y];
    +    var tileUrl = this.tileUrlFunction(tileCoord, pixelRatio, projection);
    +    var tile = new this.tileClass(
    +        tileCoord,
    +        goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY,
    +        goog.isDef(tileUrl) ? tileUrl : '',
    +        this.crossOrigin,
    +        this.tileLoadFunction);
    +    goog.events.listen(tile, goog.events.EventType.CHANGE,
    +        this.handleTileChange_, false, this);
    +
    +    this.tileCache.set(tileCoordKey, tile);
    +    return tile;
    +  }
    +};
    +
    +
    +/**
    + * @return {ol.TileLoadFunctionType} TileLoadFunction
    + * @api
    + */
    +ol.source.TileImage.prototype.getTileLoadFunction = function() {
    +  return this.tileLoadFunction;
    +};
    +
    +
    +/**
    + * @return {ol.TileUrlFunctionType} TileUrlFunction
    + * @api
    + */
    +ol.source.TileImage.prototype.getTileUrlFunction = function() {
    +  return this.tileUrlFunction;
    +};
    +
    +
    +/**
    + * Handle tile change events.
    + * @param {goog.events.Event} event Event.
    + * @private
    + */
    +ol.source.TileImage.prototype.handleTileChange_ = function(event) {
    +  var tile = /** @type {ol.Tile} */ (event.target);
    +  switch (tile.getState()) {
    +    case ol.TileState.LOADING:
    +      this.dispatchEvent(
    +          new ol.source.TileEvent(ol.source.TileEventType.TILELOADSTART, tile));
    +      break;
    +    case ol.TileState.LOADED:
    +      this.dispatchEvent(
    +          new ol.source.TileEvent(ol.source.TileEventType.TILELOADEND, tile));
    +      break;
    +    case ol.TileState.ERROR:
    +      this.dispatchEvent(
    +          new ol.source.TileEvent(ol.source.TileEventType.TILELOADERROR, tile));
    +      break;
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
    + * @api
    + */
    +ol.source.TileImage.prototype.setTileLoadFunction = function(tileLoadFunction) {
    +  this.tileCache.clear();
    +  this.tileLoadFunction = tileLoadFunction;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function.
    + * @api
    + */
    +ol.source.TileImage.prototype.setTileUrlFunction = function(tileUrlFunction) {
    +  // FIXME It should be possible to be more intelligent and avoid clearing the
    +  // FIXME cache.  The tile URL function would need to be incorporated into the
    +  // FIXME cache key somehow.
    +  this.tileCache.clear();
    +  this.tileUrlFunction = tileUrlFunction;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileImage.prototype.useTile = function(z, x, y) {
    +  var tileCoordKey = this.getKeyZXY(z, x, y);
    +  if (this.tileCache.containsKey(tileCoordKey)) {
    +    this.tileCache.get(tileCoordKey);
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/source/tilejsonsource.js b/mapbox-gl/ol/ol/source/tilejsonsource.js
    new file mode 100644
    index 0000000000..4246908224
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tilejsonsource.js
    @@ -0,0 +1,113 @@
    +// FIXME add some error checking
    +// FIXME check order of async callbacks
    +
    +/**
    + * @see http://mapbox.com/developers/api/
    + */
    +
    +goog.provide('ol.source.TileJSON');
    +goog.provide('ol.tilejson');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.net.Jsonp');
    +goog.require('ol.Attribution');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.State');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilegrid.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data in TileJSON format.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.TileJSONOptions} options TileJSON options.
    + * @api stable
    + */
    +ol.source.TileJSON = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    crossOrigin: options.crossOrigin,
    +    projection: ol.proj.get('EPSG:3857'),
    +    state: ol.source.State.LOADING,
    +    tileLoadFunction: options.tileLoadFunction
    +  });
    +
    +  /**
    +   * @type {boolean|undefined}
    +   * @private
    +   */
    +  this.wrapX_ = options.wrapX;
    +
    +  var request = new goog.net.Jsonp(options.url);
    +  request.send(undefined, goog.bind(this.handleTileJSONResponse, this));
    +
    +};
    +goog.inherits(ol.source.TileJSON, ol.source.TileImage);
    +
    +
    +/**
    + * @protected
    + * @param {TileJSON} tileJSON Tile JSON.
    + */
    +ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) {
    +
    +  var epsg4326Projection = ol.proj.get('EPSG:4326');
    +
    +  var sourceProjection = this.getProjection();
    +  var extent;
    +  if (goog.isDef(tileJSON.bounds)) {
    +    var transform = ol.proj.getTransformFromProjections(
    +        epsg4326Projection, sourceProjection);
    +    extent = ol.extent.applyTransform(tileJSON.bounds, transform);
    +  }
    +
    +  if (goog.isDef(tileJSON.scheme)) {
    +    goog.asserts.assert(tileJSON.scheme == 'xyz');
    +  }
    +  var minZoom = tileJSON.minzoom || 0;
    +  var maxZoom = tileJSON.maxzoom || 22;
    +  var tileGrid = new ol.tilegrid.XYZ({
    +    extent: ol.tilegrid.extentFromProjection(sourceProjection),
    +    maxZoom: maxZoom,
    +    minZoom: minZoom
    +  });
    +  this.tileGrid = tileGrid;
    +
    +  this.tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
    +      tileGrid.createTileCoordTransform({
    +        extent: extent,
    +        wrapX: this.wrapX_
    +      }),
    +      ol.TileUrlFunction.createFromTemplates(tileJSON.tiles));
    +
    +  if (goog.isDef(tileJSON.attribution) &&
    +      goog.isNull(this.getAttributions())) {
    +    var attributionExtent = goog.isDef(extent) ?
    +        extent : epsg4326Projection.getExtent();
    +    /** @type {Object.<string, Array.<ol.TileRange>>} */
    +    var tileRanges = {};
    +    var z, zKey;
    +    for (z = minZoom; z <= maxZoom; ++z) {
    +      zKey = z.toString();
    +      tileRanges[zKey] =
    +          [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)];
    +    }
    +    this.setAttributions([
    +      new ol.Attribution({
    +        html: tileJSON.attribution,
    +        tileRanges: tileRanges
    +      })
    +    ]);
    +  }
    +
    +  this.setState(ol.source.State.READY);
    +
    +};
    diff --git a/mapbox-gl/ol/ol/source/tilesource.js b/mapbox-gl/ol/ol/source/tilesource.js
    new file mode 100644
    index 0000000000..ec360628b5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tilesource.js
    @@ -0,0 +1,268 @@
    +goog.provide('ol.source.Tile');
    +goog.provide('ol.source.TileOptions');
    +
    +goog.require('goog.events.Event');
    +goog.require('ol.Attribution');
    +goog.require('ol.Extent');
    +goog.require('ol.TileCache');
    +goog.require('ol.TileRange');
    +goog.require('ol.TileState');
    +goog.require('ol.source.Source');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +/**
    + * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
    + *            extent: (ol.Extent|undefined),
    + *            logo: (string|olx.LogoOptions|undefined),
    + *            opaque: (boolean|undefined),
    + *            tilePixelRatio: (number|undefined),
    + *            projection: ol.proj.ProjectionLike,
    + *            state: (ol.source.State|undefined),
    + *            tileGrid: (ol.tilegrid.TileGrid|undefined)}}
    + */
    +ol.source.TileOptions;
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; normally only used for creating subclasses and not
    + * instantiated in apps.
    + * Base class for sources providing images divided into a tile grid.
    + *
    + * @constructor
    + * @extends {ol.source.Source}
    + * @param {ol.source.TileOptions} options Tile source options.
    + * @api
    + */
    +ol.source.Tile = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    logo: options.logo,
    +    projection: options.projection,
    +    state: options.state
    +  });
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.opaque_ = goog.isDef(options.opaque) ? options.opaque : false;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.tilePixelRatio_ = goog.isDef(options.tilePixelRatio) ?
    +      options.tilePixelRatio : 1;
    +
    +  /**
    +   * @protected
    +   * @type {ol.tilegrid.TileGrid}
    +   */
    +  this.tileGrid = goog.isDef(options.tileGrid) ? options.tileGrid : null;
    +
    +  /**
    +   * @protected
    +   * @type {ol.TileCache}
    +   */
    +  this.tileCache = new ol.TileCache();
    +
    +};
    +goog.inherits(ol.source.Tile, ol.source.Source);
    +
    +
    +/**
    + * @return {boolean} Can expire cache.
    + */
    +ol.source.Tile.prototype.canExpireCache = function() {
    +  return this.tileCache.canExpireCache();
    +};
    +
    +
    +/**
    + * @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
    + */
    +ol.source.Tile.prototype.expireCache = function(usedTiles) {
    +  this.tileCache.expireCache(usedTiles);
    +};
    +
    +
    +/**
    + * @param {number} z Zoom level.
    + * @param {ol.TileRange} tileRange Tile range.
    + * @param {function(ol.Tile):(boolean|undefined)} callback Called with each
    + *     loaded tile.  If the callback returns `false`, the tile will not be
    + *     considered loaded.
    + * @return {boolean} The tile range is fully covered with loaded tiles.
    + */
    +ol.source.Tile.prototype.forEachLoadedTile = function(z, tileRange, callback) {
    +  var covered = true;
    +  var tile, tileCoordKey, loaded;
    +  for (var x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +    for (var y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +      tileCoordKey = this.getKeyZXY(z, x, y);
    +      loaded = false;
    +      if (this.tileCache.containsKey(tileCoordKey)) {
    +        tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
    +        loaded = tile.getState() === ol.TileState.LOADED;
    +        if (loaded) {
    +          loaded = (callback(tile) !== false);
    +        }
    +      }
    +      if (!loaded) {
    +        covered = false;
    +      }
    +    }
    +  }
    +  return covered;
    +};
    +
    +
    +/**
    + * @return {number} Gutter.
    + */
    +ol.source.Tile.prototype.getGutter = function() {
    +  return 0;
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {string} Key.
    + * @protected
    + */
    +ol.source.Tile.prototype.getKeyZXY = ol.tilecoord.getKeyZXY;
    +
    +
    +/**
    + * @return {boolean} Opaque.
    + */
    +ol.source.Tile.prototype.getOpaque = function() {
    +  return this.opaque_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.Tile.prototype.getResolutions = function() {
    +  return this.tileGrid.getResolutions();
    +};
    +
    +
    +/**
    + * @param {number} z Tile coordinate z.
    + * @param {number} x Tile coordinate x.
    + * @param {number} y Tile coordinate y.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection=} opt_projection Projection.
    + * @return {!ol.Tile} Tile.
    + */
    +ol.source.Tile.prototype.getTile = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.tilegrid.TileGrid} Tile grid.
    + * @api stable
    + */
    +ol.source.Tile.prototype.getTileGrid = function() {
    +  return this.tileGrid;
    +};
    +
    +
    +/**
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {ol.tilegrid.TileGrid} Tile grid.
    + */
    +ol.source.Tile.prototype.getTileGridForProjection = function(projection) {
    +  if (goog.isNull(this.tileGrid)) {
    +    return ol.tilegrid.getForProjection(projection);
    +  } else {
    +    return this.tileGrid;
    +  }
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {number} Tile size.
    + */
    +ol.source.Tile.prototype.getTilePixelSize =
    +    function(z, pixelRatio, projection) {
    +  var tileGrid = this.getTileGridForProjection(projection);
    +  return tileGrid.getTileSize(z) * this.tilePixelRatio_;
    +};
    +
    +
    +/**
    + * Marks a tile coord as being used, without triggering a load.
    + * @param {number} z Tile coordinate z.
    + * @param {number} x Tile coordinate x.
    + * @param {number} y Tile coordinate y.
    + */
    +ol.source.Tile.prototype.useTile = goog.nullFunction;
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.source.Tile} instances are instances of this
    + * type.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.source.TileEvent}
    + * @param {string} type Type.
    + * @param {ol.Tile} tile The tile.
    + */
    +ol.source.TileEvent = function(type, tile) {
    +
    +  goog.base(this, type);
    +
    +  /**
    +   * The tile related to the event.
    +   * @type {ol.Tile}
    +   * @api
    +   */
    +  this.tile = tile;
    +
    +};
    +goog.inherits(ol.source.TileEvent, goog.events.Event);
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.source.TileEventType = {
    +
    +  /**
    +   * Triggered when a tile starts loading.
    +   * @event ol.source.TileEvent#tileloadstart
    +   * @api
    +   */
    +  TILELOADSTART: 'tileloadstart',
    +
    +  /**
    +   * Triggered when a tile finishes loading.
    +   * @event ol.source.TileEvent#tileloadend
    +   * @api
    +   */
    +  TILELOADEND: 'tileloadend',
    +
    +  /**
    +   * Triggered if tile loading results in an error.
    +   * @event ol.source.TileEvent#tileloaderror
    +   * @api
    +   */
    +  TILELOADERROR: 'tileloaderror'
    +
    +};
    diff --git a/mapbox-gl/ol/ol/source/tileutfgridsource.js b/mapbox-gl/ol/ol/source/tileutfgridsource.js
    new file mode 100644
    index 0000000000..7a476840f5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tileutfgridsource.js
    @@ -0,0 +1,383 @@
    +goog.provide('ol.source.TileUTFGrid');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.async.nextTick');
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('goog.net.Jsonp');
    +goog.require('ol.Attribution');
    +goog.require('ol.Tile');
    +goog.require('ol.TileState');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.State');
    +goog.require('ol.source.Tile');
    +goog.require('ol.tilegrid.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for UTFGrid interaction data loaded from TileJSON format.
    + *
    + * @constructor
    + * @extends {ol.source.Tile}
    + * @param {olx.source.TileUTFGridOptions} options Source options.
    + * @api
    + */
    +ol.source.TileUTFGrid = function(options) {
    +  goog.base(this, {
    +    projection: ol.proj.get('EPSG:3857'),
    +    state: ol.source.State.LOADING
    +  });
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.preemptive_ = goog.isDef(options.preemptive) ?
    +      options.preemptive : true;
    +
    +  /**
    +   * @private
    +   * @type {!ol.TileUrlFunctionType}
    +   */
    +  this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.template_ = undefined;
    +
    +  var request = new goog.net.Jsonp(options.url);
    +  request.send(undefined, goog.bind(this.handleTileJSONResponse, this));
    +};
    +goog.inherits(ol.source.TileUTFGrid, ol.source.Tile);
    +
    +
    +/**
    + * @return {string|undefined} The template from TileJSON.
    + * @api
    + */
    +ol.source.TileUTFGrid.prototype.getTemplate = function() {
    +  return this.template_;
    +};
    +
    +
    +/**
    + * Calls the callback (synchronously by default) with the available data
    + * for given coordinate and resolution (or `null` if not yet loaded or
    + * in case of an error).
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {function(this: T, Object)} callback Callback.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @param {boolean=} opt_request If `true` the callback is always async.
    + *                               The tile data is requested if not yet loaded.
    + * @template T
    + * @api
    + */
    +ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function(
    +    coordinate, resolution, callback, opt_this, opt_request) {
    +  if (!goog.isNull(this.tileGrid)) {
    +    var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution(
    +        coordinate, resolution);
    +    var tile = /** @type {!ol.source.TileUTFGridTile_} */(this.getTile(
    +        tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection()));
    +    tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request);
    +  } else {
    +    if (opt_request === true) {
    +      goog.async.nextTick(function() {
    +        callback.call(opt_this, null);
    +      });
    +    } else {
    +      callback.call(opt_this, null);
    +    }
    +  }
    +};
    +
    +
    +/**
    + * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse
    + * @protected
    + * @param {TileJSON} tileJSON Tile JSON.
    + */
    +ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) {
    +
    +  var epsg4326Projection = ol.proj.get('EPSG:4326');
    +
    +  var sourceProjection = this.getProjection();
    +  var extent;
    +  if (goog.isDef(tileJSON.bounds)) {
    +    var transform = ol.proj.getTransformFromProjections(
    +        epsg4326Projection, sourceProjection);
    +    extent = ol.extent.applyTransform(tileJSON.bounds, transform);
    +  }
    +
    +  if (goog.isDef(tileJSON.scheme)) {
    +    goog.asserts.assert(tileJSON.scheme == 'xyz');
    +  }
    +  var minZoom = tileJSON.minzoom || 0;
    +  var maxZoom = tileJSON.maxzoom || 22;
    +  var tileGrid = new ol.tilegrid.XYZ({
    +    extent: ol.tilegrid.extentFromProjection(sourceProjection),
    +    maxZoom: maxZoom,
    +    minZoom: minZoom
    +  });
    +  this.tileGrid = tileGrid;
    +
    +  this.template_ = tileJSON.template;
    +
    +  var grids = tileJSON.grids;
    +  if (!goog.isDefAndNotNull(grids)) {
    +    this.setState(ol.source.State.ERROR);
    +    return;
    +  }
    +
    +  this.tileUrlFunction_ = ol.TileUrlFunction.withTileCoordTransform(
    +      tileGrid.createTileCoordTransform({
    +        extent: extent
    +      }),
    +      ol.TileUrlFunction.createFromTemplates(grids));
    +
    +  if (goog.isDef(tileJSON.attribution)) {
    +    var attributionExtent = goog.isDef(extent) ?
    +        extent : epsg4326Projection.getExtent();
    +    /** @type {Object.<string, Array.<ol.TileRange>>} */
    +    var tileRanges = {};
    +    var z, zKey;
    +    for (z = minZoom; z <= maxZoom; ++z) {
    +      zKey = z.toString();
    +      tileRanges[zKey] =
    +          [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)];
    +    }
    +    this.setAttributions([
    +      new ol.Attribution({
    +        html: tileJSON.attribution,
    +        tileRanges: tileRanges
    +      })
    +    ]);
    +  }
    +
    +  this.setState(ol.source.State.READY);
    +
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileUTFGrid.prototype.getTile =
    +    function(z, x, y, pixelRatio, projection) {
    +  var tileCoordKey = this.getKeyZXY(z, x, y);
    +  if (this.tileCache.containsKey(tileCoordKey)) {
    +    return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
    +  } else {
    +    goog.asserts.assert(projection);
    +    var tileCoord = [z, x, y];
    +    var tileUrl = this.tileUrlFunction_(tileCoord, pixelRatio, projection);
    +    var tile = new ol.source.TileUTFGridTile_(
    +        tileCoord,
    +        goog.isDef(tileUrl) ? ol.TileState.IDLE : ol.TileState.EMPTY,
    +        goog.isDef(tileUrl) ? tileUrl : '',
    +        this.tileGrid.getTileCoordExtent(tileCoord),
    +        this.preemptive_);
    +    this.tileCache.set(tileCoordKey, tile);
    +    return tile;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) {
    +  var tileCoordKey = this.getKeyZXY(z, x, y);
    +  if (this.tileCache.containsKey(tileCoordKey)) {
    +    this.tileCache.get(tileCoordKey);
    +  }
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.Tile}
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.TileState} state State.
    + * @param {string} src Image source URI.
    + * @param {ol.Extent} extent Extent of the tile.
    + * @param {boolean} preemptive Load the tile when visible (before it's needed).
    + * @private
    + */
    +ol.source.TileUTFGridTile_ =
    +    function(tileCoord, state, src, extent, preemptive) {
    +
    +  goog.base(this, tileCoord, state);
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.src_ = src;
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.extent_ = extent;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.preemptive_ = preemptive;
    +
    +  /**
    +   * @private
    +   * @type {Array.<string>}
    +   */
    +  this.grid_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<string>}
    +   */
    +  this.keys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Object>|undefined}
    +   */
    +  this.data_ = null;
    +};
    +goog.inherits(ol.source.TileUTFGridTile_, ol.Tile);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileUTFGridTile_.prototype.getImage = function(opt_context) {
    +  return null;
    +};
    +
    +
    +/**
    + * Synchronously returns data at given coordinate (if available).
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {Object}
    + */
    +ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) {
    +  if (goog.isNull(this.grid_) || goog.isNull(this.keys_) ||
    +      goog.isNull(this.data_)) {
    +    return null;
    +  }
    +  var xRelative = (coordinate[0] - this.extent_[0]) /
    +      (this.extent_[2] - this.extent_[0]);
    +  var yRelative = (coordinate[1] - this.extent_[1]) /
    +      (this.extent_[3] - this.extent_[1]);
    +
    +  var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)];
    +
    +  if (!goog.isString(row)) {
    +    return null;
    +  }
    +
    +  var code = row.charCodeAt(Math.floor(xRelative * row.length));
    +  if (code >= 93) {
    +    code--;
    +  }
    +  if (code >= 35) {
    +    code--;
    +  }
    +  code -= 32;
    +
    +  var key = this.keys_[code];
    +
    +  return goog.isDefAndNotNull(key) ? this.data_[key] : null;
    +};
    +
    +
    +/**
    + * Calls the callback (synchronously by default) with the available data
    + * for given coordinate (or `null` if not yet loaded).
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {function(this: T, Object)} callback Callback.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @param {boolean=} opt_request If `true` the callback is always async.
    + *                               The tile data is requested if not yet loaded.
    + * @template T
    + */
    +ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate =
    +    function(coordinate, callback, opt_this, opt_request) {
    +  if (this.state == ol.TileState.IDLE && opt_request === true) {
    +    goog.events.listenOnce(this, goog.events.EventType.CHANGE, function(e) {
    +      callback.call(opt_this, this.getData(coordinate));
    +    }, false, this);
    +    this.loadInternal_();
    +  } else {
    +    if (opt_request === true) {
    +      goog.async.nextTick(function() {
    +        callback.call(opt_this, this.getData(coordinate));
    +      }, this);
    +    } else {
    +      callback.call(opt_this, this.getData(coordinate));
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileUTFGridTile_.prototype.getKey = function() {
    +  return this.src_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.TileUTFGridTile_.prototype.handleError_ = function() {
    +  this.state = ol.TileState.ERROR;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {!UTFGridJSON} json
    + * @private
    + */
    +ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) {
    +  this.grid_ = json.grid;
    +  this.keys_ = json.keys;
    +  this.data_ = json.data;
    +
    +  this.state = ol.TileState.EMPTY;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.TileUTFGridTile_.prototype.loadInternal_ = function() {
    +  if (this.state == ol.TileState.IDLE) {
    +    this.state = ol.TileState.LOADING;
    +    var request = new goog.net.Jsonp(this.src_);
    +    request.send(undefined, goog.bind(this.handleLoad_, this),
    +                 goog.bind(this.handleError_, this));
    +  }
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.source.TileUTFGridTile_.prototype.load = function() {
    +  if (this.preemptive_) {
    +    this.loadInternal_();
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/source/tilevectorsource.js b/mapbox-gl/ol/ol/source/tilevectorsource.js
    new file mode 100644
    index 0000000000..84eea90faa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tilevectorsource.js
    @@ -0,0 +1,311 @@
    +goog.provide('ol.source.TileVector');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.source.FormatVector');
    +goog.require('ol.source.State');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +
    +/**
    + * @classdesc
    + * A vector source in one of the supported formats, where the data is divided
    + * into tiles in a fixed grid pattern.
    + *
    + * @constructor
    + * @extends {ol.source.FormatVector}
    + * @param {olx.source.TileVectorOptions} options Options.
    + * @api
    + */
    +ol.source.TileVector = function(options) {
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    format: options.format,
    +    logo: options.logo,
    +    projection: options.projection
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.tilegrid.TileGrid}
    +   */
    +  this.tileGrid_ = options.tileGrid;
    +
    +  /**
    +   * @private
    +   * @type {ol.TileUrlFunctionType}
    +   */
    +  this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction;
    +
    +  /**
    +   * @private
    +   * @type {ol.TileCoordTransformType}
    +   */
    +  this.tileCoordTransform_ = this.tileGrid_.createTileCoordTransform();
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Array.<ol.Feature>>}
    +   */
    +  this.tiles_ = {};
    +
    +  if (goog.isDef(options.tileUrlFunction)) {
    +    this.setTileUrlFunction(options.tileUrlFunction);
    +  } else if (goog.isDef(options.urls)) {
    +    this.setUrls(options.urls);
    +  } else if (goog.isDef(options.url)) {
    +    this.setUrl(options.url);
    +  }
    +
    +};
    +goog.inherits(ol.source.TileVector, ol.source.FormatVector);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.addFeature = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.addFeatures = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.clear = function() {
    +  goog.object.clear(this.tiles_);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.forEachFeature = goog.abstractMethod;
    +
    +
    +/**
    + * Iterate through all features whose geometries contain the provided
    + * coordinate at the provided resolution, calling the callback with each
    + * feature. If the callback returns a "truthy" value, iteration will stop and
    + * the function will return the same value.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {function(this: T, ol.Feature): S} callback Called with each feature
    + *     whose goemetry contains the provided coordinate.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @return {S|undefined} The return value from the last call to the callback.
    + * @template T,S
    + */
    +ol.source.TileVector.prototype.forEachFeatureAtCoordinateAndResolution =
    +    function(coordinate, resolution, callback, opt_this) {
    +
    +  var tileGrid = this.tileGrid_;
    +  var tiles = this.tiles_;
    +  var tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate,
    +      resolution);
    +
    +  var tileKey = this.getTileKeyZXY_(tileCoord[0], tileCoord[1], tileCoord[2]);
    +  var features = tiles[tileKey];
    +  if (goog.isDef(features)) {
    +    var i, ii;
    +    for (i = 0, ii = features.length; i < ii; ++i) {
    +      var feature = features[i];
    +      var geometry = feature.getGeometry();
    +      goog.asserts.assert(goog.isDefAndNotNull(geometry));
    +      if (geometry.containsCoordinate(coordinate)) {
    +        var result = callback.call(opt_this, feature);
    +        if (result) {
    +          return result;
    +        }
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.forEachFeatureInExtent = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.forEachFeatureInExtentAtResolution =
    +    function(extent, resolution, f, opt_this) {
    +  var tileGrid = this.tileGrid_;
    +  var tiles = this.tiles_;
    +  var z = tileGrid.getZForResolution(resolution);
    +  var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
    +  var x, y;
    +  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +      var tileKey = this.getTileKeyZXY_(z, x, y);
    +      var features = tiles[tileKey];
    +      if (goog.isDef(features)) {
    +        var i, ii;
    +        for (i = 0, ii = features.length; i < ii; ++i) {
    +          var result = f.call(opt_this, features[i]);
    +          if (result) {
    +            return result;
    +          }
    +        }
    +      }
    +    }
    +  }
    +  return undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.getClosestFeatureToCoordinate =
    +    goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.getExtent = goog.abstractMethod;
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.source.TileVector.prototype.getFeatures = function() {
    +  var tiles = this.tiles_;
    +  var features = [];
    +  var tileKey;
    +  for (tileKey in tiles) {
    +    goog.array.extend(features, tiles[tileKey]);
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * Get all features whose geometry intersects the provided coordinate for the
    + * provided resolution.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @return {Array.<ol.Feature>} Features.
    + * @api
    + */
    +ol.source.TileVector.prototype.getFeaturesAtCoordinateAndResolution =
    +    function(coordinate, resolution) {
    +  var features = [];
    +  this.forEachFeatureAtCoordinateAndResolution(coordinate, resolution,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       */
    +      function(feature) {
    +        features.push(feature);
    +      });
    +  return features;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.getFeaturesInExtent = goog.abstractMethod;
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @private
    + * @return {string} Tile key.
    + */
    +ol.source.TileVector.prototype.getTileKeyZXY_ = function(z, x, y) {
    +  return z + '/' + x + '/' + y;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.loadFeatures =
    +    function(extent, resolution, projection) {
    +  var tileCoordTransform = this.tileCoordTransform_;
    +  var tileGrid = this.tileGrid_;
    +  var tileUrlFunction = this.tileUrlFunction_;
    +  var tiles = this.tiles_;
    +  var z = tileGrid.getZForResolution(resolution);
    +  var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
    +  var tileCoord = [z, 0, 0];
    +  var x, y;
    +  /**
    +   * @param {string} tileKey Tile key.
    +   * @param {Array.<ol.Feature>} features Features.
    +   * @this {ol.source.TileVector}
    +   */
    +  function success(tileKey, features) {
    +    tiles[tileKey] = features;
    +    this.setState(ol.source.State.READY);
    +  }
    +  for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
    +    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
    +      var tileKey = this.getTileKeyZXY_(z, x, y);
    +      if (!(tileKey in tiles)) {
    +        tileCoord[0] = z;
    +        tileCoord[1] = x;
    +        tileCoord[2] = y;
    +        tileCoordTransform(tileCoord, projection, tileCoord);
    +        var url = tileUrlFunction(tileCoord, 1, projection);
    +        if (goog.isDef(url)) {
    +          tiles[tileKey] = [];
    +          this.loadFeaturesFromURL(url, goog.partial(success, tileKey),
    +              goog.nullFunction, this);
    +        }
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileVector.prototype.removeFeature = goog.abstractMethod;
    +
    +
    +/**
    + * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function.
    + */
    +ol.source.TileVector.prototype.setTileUrlFunction = function(tileUrlFunction) {
    +  this.tileUrlFunction_ = tileUrlFunction;
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {string} url URL.
    + */
    +ol.source.TileVector.prototype.setUrl = function(url) {
    +  this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(
    +      ol.TileUrlFunction.expandUrl(url)));
    +};
    +
    +
    +/**
    + * @param {Array.<string>} urls URLs.
    + */
    +ol.source.TileVector.prototype.setUrls = function(urls) {
    +  this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(urls));
    +};
    diff --git a/mapbox-gl/ol/ol/source/tilewmssource.js b/mapbox-gl/ol/ol/source/tilewmssource.js
    new file mode 100644
    index 0000000000..a1873ad99f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/tilewmssource.js
    @@ -0,0 +1,420 @@
    +// FIXME add minZoom support
    +// FIXME add date line wrap (tile coord transform)
    +// FIXME cannot be shared between maps with different projections
    +
    +goog.provide('ol.source.TileWMS');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('ol');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.source.wms');
    +goog.require('ol.source.wms.ServerType');
    +goog.require('ol.tilecoord');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data from WMS servers.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.TileWMSOptions=} opt_options Tile WMS options.
    + * @api stable
    + */
    +ol.source.TileWMS = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var params = goog.isDef(options.params) ? options.params : {};
    +
    +  var transparent = goog.object.get(params, 'TRANSPARENT', true);
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    crossOrigin: options.crossOrigin,
    +    logo: options.logo,
    +    opaque: !transparent,
    +    projection: options.projection,
    +    tileGrid: options.tileGrid,
    +    tileLoadFunction: options.tileLoadFunction,
    +    tileUrlFunction: goog.bind(this.tileUrlFunction_, this)
    +  });
    +
    +  var urls = options.urls;
    +  if (!goog.isDef(urls) && goog.isDef(options.url)) {
    +    urls = ol.TileUrlFunction.expandUrl(options.url);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {!Array.<string>}
    +   */
    +  this.urls_ = goog.isDefAndNotNull(urls) ? urls : [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.gutter_ = goog.isDef(options.gutter) ? options.gutter : 0;
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.params_ = params;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.v13_ = true;
    +
    +  /**
    +   * @private
    +   * @type {ol.source.wms.ServerType|undefined}
    +   */
    +  this.serverType_ =
    +      /** @type {ol.source.wms.ServerType|undefined} */ (options.serverType);
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.hidpi_ = goog.isDef(options.hidpi) ? options.hidpi : true;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.coordKeyPrefix_ = '';
    +  this.resetCoordKeyPrefix_();
    +
    +  /**
    +   * @private
    +   * @type {ol.Extent}
    +   */
    +  this.tmpExtent_ = ol.extent.createEmpty();
    +
    +  this.updateV13_();
    +
    +};
    +goog.inherits(ol.source.TileWMS, ol.source.TileImage);
    +
    +
    +/**
    + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
    + * projection. Return `undefined` if the GetFeatureInfo URL cannot be
    + * constructed.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {ol.proj.ProjectionLike} projection Projection.
    + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
    + *     be provided. If `QUERY_LAYERS` is not provided then the layers specified
    + *     in the `LAYERS` parameter will be used. `VERSION` should not be
    + *     specified here.
    + * @return {string|undefined} GetFeatureInfo URL.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.getGetFeatureInfoUrl =
    +    function(coordinate, resolution, projection, params) {
    +
    +  goog.asserts.assert(!('VERSION' in params));
    +
    +  var projectionObj = ol.proj.get(projection);
    +
    +  var tileGrid = this.getTileGrid();
    +  if (goog.isNull(tileGrid)) {
    +    tileGrid = this.getTileGridForProjection(projectionObj);
    +  }
    +
    +  var tileCoord = tileGrid.getTileCoordForCoordAndResolution(
    +      coordinate, resolution);
    +
    +  if (tileGrid.getResolutions().length <= tileCoord[0]) {
    +    return undefined;
    +  }
    +
    +  var tileResolution = tileGrid.getResolution(tileCoord[0]);
    +  var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
    +  var tileSize = tileGrid.getTileSize(tileCoord[0]);
    +
    +  var gutter = this.gutter_;
    +  if (gutter !== 0) {
    +    tileSize += 2 * gutter;
    +    tileExtent = ol.extent.buffer(tileExtent,
    +        tileResolution * gutter, tileExtent);
    +  }
    +
    +  var baseParams = {
    +    'SERVICE': 'WMS',
    +    'VERSION': ol.DEFAULT_WMS_VERSION,
    +    'REQUEST': 'GetFeatureInfo',
    +    'FORMAT': 'image/png',
    +    'TRANSPARENT': true,
    +    'QUERY_LAYERS': this.params_['LAYERS']
    +  };
    +  goog.object.extend(baseParams, this.params_, params);
    +
    +  var x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution);
    +  var y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution);
    +
    +  baseParams[this.v13_ ? 'I' : 'X'] = x;
    +  baseParams[this.v13_ ? 'J' : 'Y'] = y;
    +
    +  return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
    +      1, projectionObj, baseParams);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileWMS.prototype.getGutter = function() {
    +  return this.gutter_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.TileWMS.prototype.getKeyZXY = function(z, x, y) {
    +  return this.coordKeyPrefix_ + goog.base(this, 'getKeyZXY', z, x, y);
    +};
    +
    +
    +/**
    + * Get the user-provided params, i.e. those passed to the constructor through
    + * the "params" option, and possibly updated using the updateParams method.
    + * @return {Object} Params.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.getParams = function() {
    +  return this.params_;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {number} tileSize Tile size.
    + * @param {ol.Extent} tileExtent Tile extent.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @param {Object} params Params.
    + * @return {string|undefined} Request URL.
    + * @private
    + */
    +ol.source.TileWMS.prototype.getRequestUrl_ =
    +    function(tileCoord, tileSize, tileExtent,
    +        pixelRatio, projection, params) {
    +
    +  var urls = this.urls_;
    +  if (goog.array.isEmpty(urls)) {
    +    return undefined;
    +  }
    +
    +  params['WIDTH'] = tileSize;
    +  params['HEIGHT'] = tileSize;
    +
    +  params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode();
    +
    +  if (!('STYLES' in this.params_)) {
    +    /* jshint -W053 */
    +    params['STYLES'] = new String('');
    +    /* jshint +W053 */
    +  }
    +
    +  if (pixelRatio != 1) {
    +    switch (this.serverType_) {
    +      case ol.source.wms.ServerType.GEOSERVER:
    +        var dpi = (90 * pixelRatio + 0.5) | 0;
    +        params['FORMAT_OPTIONS'] = 'dpi:' + dpi;
    +        break;
    +      case ol.source.wms.ServerType.MAPSERVER:
    +        params['MAP_RESOLUTION'] = 90 * pixelRatio;
    +        break;
    +      case ol.source.wms.ServerType.CARMENTA_SERVER:
    +      case ol.source.wms.ServerType.QGIS:
    +        params['DPI'] = 90 * pixelRatio;
    +        break;
    +      default:
    +        goog.asserts.fail();
    +        break;
    +    }
    +  }
    +
    +  var axisOrientation = projection.getAxisOrientation();
    +  var bbox = tileExtent;
    +  if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') {
    +    var tmp;
    +    tmp = tileExtent[0];
    +    bbox[0] = tileExtent[1];
    +    bbox[1] = tmp;
    +    tmp = tileExtent[2];
    +    bbox[2] = tileExtent[3];
    +    bbox[3] = tmp;
    +  }
    +  params['BBOX'] = bbox.join(',');
    +
    +  var url;
    +  if (urls.length == 1) {
    +    url = urls[0];
    +  } else {
    +    var index = goog.math.modulo(ol.tilecoord.hash(tileCoord), urls.length);
    +    url = urls[index];
    +  }
    +  return goog.uri.utils.appendParamsFromMap(url, params);
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {number} Size.
    + */
    +ol.source.TileWMS.prototype.getTilePixelSize =
    +    function(z, pixelRatio, projection) {
    +  var tileSize = goog.base(this, 'getTilePixelSize', z, pixelRatio, projection);
    +  if (pixelRatio == 1 || !this.hidpi_ || !goog.isDef(this.serverType_)) {
    +    return tileSize;
    +  } else {
    +    return (tileSize * pixelRatio + 0.5) | 0;
    +  }
    +};
    +
    +
    +/**
    + * Return the URLs used for this WMS source.
    + * @return {!Array.<string>} URLs.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.getUrls = function() {
    +  return this.urls_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() {
    +  var i = 0;
    +  var res = [];
    +
    +  var j, jj;
    +  for (j = 0, jj = this.urls_.length; j < jj; ++j) {
    +    res[i++] = this.urls_[j];
    +  }
    +
    +  var key;
    +  for (key in this.params_) {
    +    res[i++] = key + '-' + this.params_[key];
    +  }
    +
    +  this.coordKeyPrefix_ = res.join('#');
    +};
    +
    +
    +/**
    + * @param {string|undefined} url URL.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.setUrl = function(url) {
    +  var urls = goog.isDef(url) ? ol.TileUrlFunction.expandUrl(url) : null;
    +  this.setUrls(urls);
    +};
    +
    +
    +/**
    + * @param {Array.<string>|undefined} urls URLs.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.setUrls = function(urls) {
    +  this.urls_ = goog.isDefAndNotNull(urls) ? urls : [];
    +  this.resetCoordKeyPrefix_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {string|undefined} Tile URL.
    + * @private
    + */
    +ol.source.TileWMS.prototype.tileUrlFunction_ =
    +    function(tileCoord, pixelRatio, projection) {
    +
    +  var tileGrid = this.getTileGrid();
    +  if (goog.isNull(tileGrid)) {
    +    tileGrid = this.getTileGridForProjection(projection);
    +  }
    +
    +  if (tileGrid.getResolutions().length <= tileCoord[0]) {
    +    return undefined;
    +  }
    +
    +  if (pixelRatio != 1 && (!this.hidpi_ || !goog.isDef(this.serverType_))) {
    +    pixelRatio = 1;
    +  }
    +
    +  var tileResolution = tileGrid.getResolution(tileCoord[0]);
    +  var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
    +  var tileSize = tileGrid.getTileSize(tileCoord[0]);
    +
    +  var gutter = this.gutter_;
    +  if (gutter !== 0) {
    +    tileSize += 2 * gutter;
    +    tileExtent = ol.extent.buffer(tileExtent,
    +        tileResolution * gutter, tileExtent);
    +  }
    +
    +  if (pixelRatio != 1) {
    +    tileSize = (tileSize * pixelRatio + 0.5) | 0;
    +  }
    +
    +  var baseParams = {
    +    'SERVICE': 'WMS',
    +    'VERSION': ol.DEFAULT_WMS_VERSION,
    +    'REQUEST': 'GetMap',
    +    'FORMAT': 'image/png',
    +    'TRANSPARENT': true
    +  };
    +  goog.object.extend(baseParams, this.params_);
    +
    +  return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
    +      pixelRatio, projection, baseParams);
    +};
    +
    +
    +/**
    + * Update the user-provided params.
    + * @param {Object} params Params.
    + * @api stable
    + */
    +ol.source.TileWMS.prototype.updateParams = function(params) {
    +  goog.object.extend(this.params_, params);
    +  this.resetCoordKeyPrefix_();
    +  this.updateV13_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.TileWMS.prototype.updateV13_ = function() {
    +  var version =
    +      goog.object.get(this.params_, 'VERSION', ol.DEFAULT_WMS_VERSION);
    +  this.v13_ = goog.string.compareVersions(version, '1.3') >= 0;
    +};
    diff --git a/mapbox-gl/ol/ol/source/topojsonsource.js b/mapbox-gl/ol/ol/source/topojsonsource.js
    new file mode 100644
    index 0000000000..8eec88abd1
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/topojsonsource.js
    @@ -0,0 +1,36 @@
    +goog.provide('ol.source.TopoJSON');
    +
    +goog.require('ol.format.TopoJSON');
    +goog.require('ol.source.StaticVector');
    +
    +
    +
    +/**
    + * @classdesc
    + * Static vector source in TopoJSON format
    + *
    + * @constructor
    + * @extends {ol.source.StaticVector}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.TopoJSONOptions=} opt_options Options.
    + * @api
    + */
    +ol.source.TopoJSON = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    extent: options.extent,
    +    format: new ol.format.TopoJSON({
    +      defaultDataProjection: options.defaultProjection
    +    }),
    +    logo: options.logo,
    +    object: options.object,
    +    projection: options.projection,
    +    text: options.text,
    +    url: options.url
    +  });
    +
    +};
    +goog.inherits(ol.source.TopoJSON, ol.source.StaticVector);
    diff --git a/mapbox-gl/ol/ol/source/vectorsource.js b/mapbox-gl/ol/ol/source/vectorsource.js
    new file mode 100644
    index 0000000000..ccfe2cbaa0
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/vectorsource.js
    @@ -0,0 +1,656 @@
    +// FIXME bulk feature upload - suppress events
    +// FIXME put features in an ol.Collection
    +// FIXME make change-detection more refined (notably, geometry hint)
    +
    +goog.provide('ol.source.Vector');
    +goog.provide('ol.source.VectorEvent');
    +goog.provide('ol.source.VectorEventType');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.Event');
    +goog.require('goog.events.EventType');
    +goog.require('goog.object');
    +goog.require('ol.ObjectEventType');
    +goog.require('ol.proj');
    +goog.require('ol.source.Source');
    +goog.require('ol.structs.RBush');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.source.VectorEventType = {
    +  /**
    +   * Triggered when a feature is added to the source.
    +   * @event ol.source.VectorEvent#addfeature
    +   * @api stable
    +   */
    +  ADDFEATURE: 'addfeature',
    +
    +  /**
    +   * Triggered when a feature is updated.
    +   * @event ol.source.VectorEvent#changefeature
    +   * @api
    +   */
    +  CHANGEFEATURE: 'changefeature',
    +
    +  /**
    +   * Triggered when the clear method is called on the source.
    +   * @event ol.source.VectorEvent#clear
    +   * @api
    +   */
    +  CLEAR: 'clear',
    +
    +  /**
    +   * Triggered when a feature is removed from the source.
    +   * See {@link ol.source.Vector#clear source.clear()} for exceptions.
    +   * @event ol.source.VectorEvent#removefeature
    +   * @api stable
    +   */
    +  REMOVEFEATURE: 'removefeature'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Provides a source of features for vector layers.
    + *
    + * @constructor
    + * @extends {ol.source.Source}
    + * @fires ol.source.VectorEvent
    + * @param {olx.source.VectorOptions=} opt_options Vector source options.
    + * @api stable
    + */
    +ol.source.Vector = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    logo: options.logo,
    +    projection: options.projection,
    +    state: goog.isDef(options.state) ?
    +        /** @type {ol.source.State} */ (options.state) : undefined
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.structs.RBush.<ol.Feature>}
    +   */
    +  this.rBush_ = new ol.structs.RBush();
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.Feature>}
    +   */
    +  this.nullGeometryFeatures_ = {};
    +
    +  /**
    +   * A lookup of features by id (the return from feature.getId()).
    +   * @private
    +   * @type {Object.<string, ol.Feature>}
    +   */
    +  this.idIndex_ = {};
    +
    +  /**
    +   * A lookup of features without id (keyed by goog.getUid(feature)).
    +   * @private
    +   * @type {Object.<string, ol.Feature>}
    +   */
    +  this.undefIdIndex_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, Array.<goog.events.Key>>}
    +   */
    +  this.featureChangeKeys_ = {};
    +
    +  if (goog.isDef(options.features)) {
    +    this.addFeaturesInternal(options.features);
    +  }
    +
    +};
    +goog.inherits(ol.source.Vector, ol.source.Source);
    +
    +
    +/**
    + * Add a single feature to the source.  If you want to add a batch of features
    + * at once, call {@link ol.source.Vector#addFeatures source.addFeatures()}
    + * instead.
    + * @param {ol.Feature} feature Feature to add.
    + * @api stable
    + */
    +ol.source.Vector.prototype.addFeature = function(feature) {
    +  this.addFeatureInternal(feature);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Add a feature without firing a `change` event.
    + * @param {ol.Feature} feature Feature.
    + * @protected
    + */
    +ol.source.Vector.prototype.addFeatureInternal = function(feature) {
    +  var featureKey = goog.getUid(feature).toString();
    +  this.setupChangeEvents_(featureKey, feature);
    +
    +  var geometry = feature.getGeometry();
    +  if (goog.isDefAndNotNull(geometry)) {
    +    var extent = geometry.getExtent();
    +    this.rBush_.insert(extent, feature);
    +  } else {
    +    this.nullGeometryFeatures_[featureKey] = feature;
    +  }
    +
    +  this.addToIndex_(featureKey, feature);
    +  this.dispatchEvent(
    +      new ol.source.VectorEvent(ol.source.VectorEventType.ADDFEATURE, feature));
    +};
    +
    +
    +/**
    + * @param {string} featureKey
    + * @param {ol.Feature} feature
    + * @private
    + */
    +ol.source.Vector.prototype.setupChangeEvents_ = function(featureKey, feature) {
    +  goog.asserts.assert(!(featureKey in this.featureChangeKeys_));
    +  this.featureChangeKeys_[featureKey] = [
    +    goog.events.listen(feature,
    +        goog.events.EventType.CHANGE,
    +        this.handleFeatureChange_, false, this),
    +    goog.events.listen(feature,
    +        ol.ObjectEventType.PROPERTYCHANGE,
    +        this.handleFeatureChange_, false, this)
    +  ];
    +};
    +
    +
    +/**
    + * @param {string} featureKey
    + * @param {ol.Feature} feature
    + * @private
    + */
    +ol.source.Vector.prototype.addToIndex_ = function(featureKey, feature) {
    +  var id = feature.getId();
    +  if (goog.isDef(id)) {
    +    this.idIndex_[id.toString()] = feature;
    +  } else {
    +    goog.asserts.assert(!(featureKey in this.undefIdIndex_),
    +        'Feature already added to the source');
    +    this.undefIdIndex_[featureKey] = feature;
    +  }
    +};
    +
    +
    +/**
    + * Add a batch of features to the source.
    + * @param {Array.<ol.Feature>} features Features to add.
    + * @api stable
    + */
    +ol.source.Vector.prototype.addFeatures = function(features) {
    +  this.addFeaturesInternal(features);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Add features without firing a `change` event.
    + * @param {Array.<ol.Feature>} features Features.
    + * @protected
    + */
    +ol.source.Vector.prototype.addFeaturesInternal = function(features) {
    +  var featureKey, i, length, feature;
    +  var extents = [];
    +  var validFeatures = [];
    +  for (i = 0, length = features.length; i < length; i++) {
    +    feature = features[i];
    +    featureKey = goog.getUid(feature).toString();
    +    this.setupChangeEvents_(featureKey, feature);
    +
    +    var geometry = feature.getGeometry();
    +    if (goog.isDefAndNotNull(geometry)) {
    +      var extent = geometry.getExtent();
    +      extents.push(extent);
    +      validFeatures.push(feature);
    +    } else {
    +      this.nullGeometryFeatures_[featureKey] = feature;
    +    }
    +  }
    +  this.rBush_.load(extents, validFeatures);
    +
    +  for (i = 0, length = features.length; i < length; i++) {
    +    feature = features[i];
    +    featureKey = goog.getUid(feature).toString();
    +    this.addToIndex_(featureKey, feature);
    +    this.dispatchEvent(new ol.source.VectorEvent(
    +        ol.source.VectorEventType.ADDFEATURE, feature));
    +  }
    +};
    +
    +
    +/**
    + * Remove all features from the source.
    + * @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events.
    + * @api stable
    + */
    +ol.source.Vector.prototype.clear = function(opt_fast) {
    +  if (opt_fast) {
    +    for (var featureId in this.featureChangeKeys_) {
    +      var keys = this.featureChangeKeys_[featureId];
    +      goog.array.forEach(keys, goog.events.unlistenByKey);
    +    }
    +    this.featureChangeKeys_ = {};
    +    this.idIndex_ = {};
    +    this.undefIdIndex_ = {};
    +  } else {
    +    var rmFeatureInternal = this.removeFeatureInternal;
    +    this.rBush_.forEach(rmFeatureInternal, this);
    +    goog.object.forEach(this.nullGeometryFeatures_, rmFeatureInternal, this);
    +    goog.asserts.assert(goog.object.isEmpty(this.featureChangeKeys_));
    +    goog.asserts.assert(goog.object.isEmpty(this.idIndex_));
    +    goog.asserts.assert(goog.object.isEmpty(this.undefIdIndex_));
    +  }
    +
    +  this.rBush_.clear();
    +  this.nullGeometryFeatures_ = {};
    +
    +  var clearEvent = new ol.source.VectorEvent(ol.source.VectorEventType.CLEAR);
    +  this.dispatchEvent(clearEvent);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Iterate through all features on the source, calling the provided callback
    + * with each one.  If the callback returns any "truthy" value, iteration will
    + * stop and the function will return the same value.
    + *
    + * @param {function(this: T, ol.Feature): S} callback Called with each feature
    + *     on the source.  Return a truthy value to stop iteration.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @return {S|undefined} The return value from the last call to the callback.
    + * @template T,S
    + * @api stable
    + */
    +ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) {
    +  return this.rBush_.forEach(callback, opt_this);
    +};
    +
    +
    +/**
    + * Iterate through all features whose geometries contain the provided
    + * coordinate, calling the callback with each feature.  If the callback returns
    + * a "truthy" value, iteration will stop and the function will return the same
    + * value.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {function(this: T, ol.Feature): S} callback Called with each feature
    + *     whose goemetry contains the provided coordinate.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @return {S|undefined} The return value from the last call to the callback.
    + * @template T,S
    + */
    +ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect =
    +    function(coordinate, callback, opt_this) {
    +  var extent = [coordinate[0], coordinate[1], coordinate[0], coordinate[1]];
    +  return this.forEachFeatureInExtent(extent, function(feature) {
    +    var geometry = feature.getGeometry();
    +    goog.asserts.assert(goog.isDefAndNotNull(geometry));
    +    if (geometry.containsCoordinate(coordinate)) {
    +      return callback.call(opt_this, feature);
    +    } else {
    +      return undefined;
    +    }
    +  });
    +};
    +
    +
    +/**
    + * Iterate through all features whose bounding box intersects the provided
    + * extent (note that the feature's geometry may not intersect the extent),
    + * calling the callback with each feature.  If the callback returns a "truthy"
    + * value, iteration will stop and the function will return the same value.
    + *
    + * If you are interested in features whose geometry intersects an extent, call
    + * the {@link ol.source.Vector#forEachFeatureIntersectingExtent
    + * source.forEachFeatureIntersectingExtent()} method instead.
    + *
    + * @param {ol.Extent} extent Extent.
    + * @param {function(this: T, ol.Feature): S} callback Called with each feature
    + *     whose bounding box intersects the provided extent.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @return {S|undefined} The return value from the last call to the callback.
    + * @template T,S
    + * @api
    + */
    +ol.source.Vector.prototype.forEachFeatureInExtent =
    +    function(extent, callback, opt_this) {
    +  return this.rBush_.forEachInExtent(extent, callback, opt_this);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {function(this: T, ol.Feature): S} f Callback.
    + * @param {T=} opt_this The object to use as `this` in `f`.
    + * @return {S|undefined}
    + * @template T,S
    + */
    +ol.source.Vector.prototype.forEachFeatureInExtentAtResolution =
    +    function(extent, resolution, f, opt_this) {
    +  return this.forEachFeatureInExtent(extent, f, opt_this);
    +};
    +
    +
    +/**
    + * Iterate through all features whose geometry intersects the provided extent,
    + * calling the callback with each feature.  If the callback returns a "truthy"
    + * value, iteration will stop and the function will return the same value.
    + *
    + * If you only want to test for bounding box intersection, call the
    + * {@link ol.source.Vector#forEachFeatureInExtent
    + * source.forEachFeatureInExtent()} method instead.
    + *
    + * @param {ol.Extent} extent Extent.
    + * @param {function(this: T, ol.Feature): S} callback Called with each feature
    + *     whose geometry intersects the provided extent.
    + * @param {T=} opt_this The object to use as `this` in the callback.
    + * @return {S|undefined} The return value from the last call to the callback.
    + * @template T,S
    + * @api
    + */
    +ol.source.Vector.prototype.forEachFeatureIntersectingExtent =
    +    function(extent, callback, opt_this) {
    +  return this.forEachFeatureInExtent(extent,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       * @return {S|undefined}
    +       * @template S
    +       */
    +      function(feature) {
    +        var geometry = feature.getGeometry();
    +        goog.asserts.assert(goog.isDefAndNotNull(geometry));
    +        if (geometry.intersectsExtent(extent)) {
    +          var result = callback.call(opt_this, feature);
    +          if (result) {
    +            return result;
    +          }
    +        }
    +      });
    +};
    +
    +
    +/**
    + * Get all features on the source.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.source.Vector.prototype.getFeatures = function() {
    +  var features = this.rBush_.getAll();
    +  if (!goog.object.isEmpty(this.nullGeometryFeatures_)) {
    +    goog.array.extend(
    +        features, goog.object.getValues(this.nullGeometryFeatures_));
    +  }
    +  return features;
    +};
    +
    +
    +/**
    + * Get all features whose geometry intersects the provided coordinate.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {Array.<ol.Feature>} Features.
    + * @api stable
    + */
    +ol.source.Vector.prototype.getFeaturesAtCoordinate = function(coordinate) {
    +  var features = [];
    +  this.forEachFeatureAtCoordinateDirect(coordinate, function(feature) {
    +    features.push(feature);
    +  });
    +  return features;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @return {Array.<ol.Feature>} Features.
    + */
    +ol.source.Vector.prototype.getFeaturesInExtent = function(extent) {
    +  return this.rBush_.getInExtent(extent);
    +};
    +
    +
    +/**
    + * Get the closest feature to the provided coordinate.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @return {ol.Feature} Closest feature.
    + * @api stable
    + */
    +ol.source.Vector.prototype.getClosestFeatureToCoordinate =
    +    function(coordinate) {
    +  // Find the closest feature using branch and bound.  We start searching an
    +  // infinite extent, and find the distance from the first feature found.  This
    +  // becomes the closest feature.  We then compute a smaller extent which any
    +  // closer feature must intersect.  We continue searching with this smaller
    +  // extent, trying to find a closer feature.  Every time we find a closer
    +  // feature, we update the extent being searched so that any even closer
    +  // feature must intersect it.  We continue until we run out of features.
    +  var x = coordinate[0];
    +  var y = coordinate[1];
    +  var closestFeature = null;
    +  var closestPoint = [NaN, NaN];
    +  var minSquaredDistance = Infinity;
    +  var extent = [-Infinity, -Infinity, Infinity, Infinity];
    +  this.rBush_.forEachInExtent(extent,
    +      /**
    +       * @param {ol.Feature} feature Feature.
    +       */
    +      function(feature) {
    +        var geometry = feature.getGeometry();
    +        goog.asserts.assert(goog.isDefAndNotNull(geometry));
    +        var previousMinSquaredDistance = minSquaredDistance;
    +        minSquaredDistance = geometry.closestPointXY(
    +            x, y, closestPoint, minSquaredDistance);
    +        if (minSquaredDistance < previousMinSquaredDistance) {
    +          closestFeature = feature;
    +          // This is sneaky.  Reduce the extent that it is currently being
    +          // searched while the R-Tree traversal using this same extent object
    +          // is still in progress.  This is safe because the new extent is
    +          // strictly contained by the old extent.
    +          var minDistance = Math.sqrt(minSquaredDistance);
    +          extent[0] = x - minDistance;
    +          extent[1] = y - minDistance;
    +          extent[2] = x + minDistance;
    +          extent[3] = y + minDistance;
    +        }
    +      });
    +  return closestFeature;
    +};
    +
    +
    +/**
    + * Get the extent of the features currently in the source.
    + * @return {ol.Extent} Extent.
    + * @api stable
    + */
    +ol.source.Vector.prototype.getExtent = function() {
    +  return this.rBush_.getExtent();
    +};
    +
    +
    +/**
    + * Get a feature by its identifier (the value returned by feature.getId()).
    + * Note that the index treats string and numeric identifiers as the same.  So
    + * `source.getFeatureById(2)` will return a feature with id `'2'` or `2`.
    + *
    + * @param {string|number} id Feature identifier.
    + * @return {ol.Feature} The feature (or `null` if not found).
    + * @api stable
    + */
    +ol.source.Vector.prototype.getFeatureById = function(id) {
    +  var feature = this.idIndex_[id.toString()];
    +  return goog.isDef(feature) ? feature : null;
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Event.
    + * @private
    + */
    +ol.source.Vector.prototype.handleFeatureChange_ = function(event) {
    +  var feature = /** @type {ol.Feature} */ (event.target);
    +  var featureKey = goog.getUid(feature).toString();
    +  var geometry = feature.getGeometry();
    +  if (!goog.isDefAndNotNull(geometry)) {
    +    if (!(featureKey in this.nullGeometryFeatures_)) {
    +      this.rBush_.remove(feature);
    +      this.nullGeometryFeatures_[featureKey] = feature;
    +    }
    +  } else {
    +    var extent = geometry.getExtent();
    +    if (featureKey in this.nullGeometryFeatures_) {
    +      delete this.nullGeometryFeatures_[featureKey];
    +      this.rBush_.insert(extent, feature);
    +    } else {
    +      this.rBush_.update(extent, feature);
    +    }
    +  }
    +  var id = feature.getId();
    +  var removed;
    +  if (goog.isDef(id)) {
    +    var sid = id.toString();
    +    if (featureKey in this.undefIdIndex_) {
    +      delete this.undefIdIndex_[featureKey];
    +      this.idIndex_[sid] = feature;
    +    } else {
    +      if (this.idIndex_[sid] !== feature) {
    +        removed = this.removeFromIdIndex_(feature);
    +        goog.asserts.assert(removed,
    +            'Expected feature to be removed from index');
    +        this.idIndex_[sid] = feature;
    +      }
    +    }
    +  } else {
    +    if (!(featureKey in this.undefIdIndex_)) {
    +      removed = this.removeFromIdIndex_(feature);
    +      goog.asserts.assert(removed,
    +          'Expected feature to be removed from index');
    +      this.undefIdIndex_[featureKey] = feature;
    +    } else {
    +      goog.asserts.assert(this.undefIdIndex_[featureKey] === feature);
    +    }
    +  }
    +  this.changed();
    +  this.dispatchEvent(new ol.source.VectorEvent(
    +      ol.source.VectorEventType.CHANGEFEATURE, feature));
    +};
    +
    +
    +/**
    + * @return {boolean} Is empty.
    + */
    +ol.source.Vector.prototype.isEmpty = function() {
    +  return this.rBush_.isEmpty() &&
    +      goog.object.isEmpty(this.nullGeometryFeatures_);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {ol.proj.Projection} projection Projection.
    + */
    +ol.source.Vector.prototype.loadFeatures = goog.nullFunction;
    +
    +
    +/**
    + * Remove a single feature from the source.  If you want to remove all features
    + * at once, use the {@link ol.source.Vector#clear source.clear()} method
    + * instead.
    + * @param {ol.Feature} feature Feature to remove.
    + * @api stable
    + */
    +ol.source.Vector.prototype.removeFeature = function(feature) {
    +  var featureKey = goog.getUid(feature).toString();
    +  if (featureKey in this.nullGeometryFeatures_) {
    +    delete this.nullGeometryFeatures_[featureKey];
    +  } else {
    +    this.rBush_.remove(feature);
    +  }
    +  this.removeFeatureInternal(feature);
    +  this.changed();
    +};
    +
    +
    +/**
    + * Remove feature without firing a `change` event.
    + * @param {ol.Feature} feature Feature.
    + * @protected
    + */
    +ol.source.Vector.prototype.removeFeatureInternal = function(feature) {
    +  var featureKey = goog.getUid(feature).toString();
    +  goog.asserts.assert(featureKey in this.featureChangeKeys_);
    +  goog.array.forEach(this.featureChangeKeys_[featureKey],
    +      goog.events.unlistenByKey);
    +  delete this.featureChangeKeys_[featureKey];
    +  var id = feature.getId();
    +  if (goog.isDef(id)) {
    +    delete this.idIndex_[id.toString()];
    +  } else {
    +    delete this.undefIdIndex_[featureKey];
    +  }
    +  this.dispatchEvent(new ol.source.VectorEvent(
    +      ol.source.VectorEventType.REMOVEFEATURE, feature));
    +};
    +
    +
    +/**
    + * Remove a feature from the id index.  Called internally when the feature id
    + * may have changed.
    + * @param {ol.Feature} feature The feature.
    + * @return {boolean} Removed the feature from the index.
    + * @private
    + */
    +ol.source.Vector.prototype.removeFromIdIndex_ = function(feature) {
    +  var removed = false;
    +  for (var id in this.idIndex_) {
    +    if (this.idIndex_[id] === feature) {
    +      delete this.idIndex_[id];
    +      removed = true;
    +      break;
    +    }
    +  }
    +  return removed;
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Events emitted by {@link ol.source.Vector} instances are instances of this
    + * type.
    + *
    + * @constructor
    + * @extends {goog.events.Event}
    + * @implements {oli.source.VectorEvent}
    + * @param {string} type Type.
    + * @param {ol.Feature=} opt_feature Feature.
    + */
    +ol.source.VectorEvent = function(type, opt_feature) {
    +
    +  goog.base(this, type);
    +
    +  /**
    +   * The feature being added or removed.
    +   * @type {ol.Feature|undefined}
    +   * @api stable
    +   */
    +  this.feature = opt_feature;
    +
    +};
    +goog.inherits(ol.source.VectorEvent, goog.events.Event);
    diff --git a/mapbox-gl/ol/ol/source/wmssource.js b/mapbox-gl/ol/ol/source/wmssource.js
    new file mode 100644
    index 0000000000..4f69175c46
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/wmssource.js
    @@ -0,0 +1,17 @@
    +goog.provide('ol.source.wms');
    +goog.provide('ol.source.wms.ServerType');
    +
    +
    +/**
    + * Available server types: `'carmentaserver'`, `'geoserver'`, `'mapserver'`,
    + *     `'qgis'`. These are servers that have vendor parameters beyond the WMS
    + *     specification that OpenLayers can make use of.
    + * @enum {string}
    + * @api
    + */
    +ol.source.wms.ServerType = {
    +  CARMENTA_SERVER: 'carmentaserver',
    +  GEOSERVER: 'geoserver',
    +  MAPSERVER: 'mapserver',
    +  QGIS: 'qgis'
    +};
    diff --git a/mapbox-gl/ol/ol/source/wmssource.jsdoc b/mapbox-gl/ol/ol/source/wmssource.jsdoc
    new file mode 100644
    index 0000000000..ad8776496a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/wmssource.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.source.wms
    + */
    diff --git a/mapbox-gl/ol/ol/source/wmtssource.js b/mapbox-gl/ol/ol/source/wmtssource.js
    new file mode 100644
    index 0000000000..2fca62db1e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/wmtssource.js
    @@ -0,0 +1,471 @@
    +goog.provide('ol.source.WMTS');
    +goog.provide('ol.source.WMTSRequestEncoding');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.math');
    +goog.require('goog.object');
    +goog.require('goog.string');
    +goog.require('goog.uri.utils');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.TileUrlFunctionType');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.WMTS');
    +
    +
    +/**
    + * Request encoding. One of 'KVP', 'REST'.
    + * @enum {string}
    + * @api
    + */
    +ol.source.WMTSRequestEncoding = {
    +  KVP: 'KVP',  // see spec §8
    +  REST: 'REST' // see spec §10
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data from WMTS servers.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.WMTSOptions} options WMTS options.
    + * @api
    + */
    +ol.source.WMTS = function(options) {
    +
    +  // TODO: add support for TileMatrixLimits
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.version_ = goog.isDef(options.version) ? options.version : '1.0.0';
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.format_ = goog.isDef(options.format) ? options.format : 'image/jpeg';
    +
    +  /**
    +   * @private
    +   * @type {Object}
    +   */
    +  this.dimensions_ = goog.isDef(options.dimensions) ? options.dimensions : {};
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.coordKeyPrefix_ = '';
    +  this.resetCoordKeyPrefix_();
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.layer_ = options.layer;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.matrixSet_ = options.matrixSet;
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.style_ = options.style;
    +
    +  // FIXME: should we guess this requestEncoding from options.url(s)
    +  //        structure? that would mean KVP only if a template is not provided.
    +  var requestEncoding = goog.isDef(options.requestEncoding) ?
    +      /** @type {ol.source.WMTSRequestEncoding} */ (options.requestEncoding) :
    +      ol.source.WMTSRequestEncoding.KVP;
    +
    +  // FIXME: should we create a default tileGrid?
    +  // we could issue a getCapabilities xhr to retrieve missing configuration
    +  var tileGrid = options.tileGrid;
    +
    +  // context property names are lower case to allow for a case insensitive
    +  // replacement as some services use different naming conventions
    +  var context = {
    +    'layer': this.layer_,
    +    'style': this.style_,
    +    'tilematrixset': this.matrixSet_
    +  };
    +
    +  if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) {
    +    goog.object.extend(context, {
    +      'Service': 'WMTS',
    +      'Request': 'GetTile',
    +      'Version': this.version_,
    +      'Format': this.format_
    +    });
    +  }
    +
    +  var dimensions = this.dimensions_;
    +
    +  /**
    +   * @param {string} template Template.
    +   * @return {ol.TileUrlFunctionType} Tile URL function.
    +   */
    +  function createFromWMTSTemplate(template) {
    +
    +    // TODO: we may want to create our own appendParams function so that params
    +    // order conforms to wmts spec guidance, and so that we can avoid to escape
    +    // special template params
    +
    +    template = (requestEncoding == ol.source.WMTSRequestEncoding.KVP) ?
    +        goog.uri.utils.appendParamsFromMap(template, context) :
    +        template.replace(/\{(\w+?)\}/g, function(m, p) {
    +          return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m;
    +        });
    +
    +    return (
    +        /**
    +         * @param {ol.TileCoord} tileCoord Tile coordinate.
    +         * @param {number} pixelRatio Pixel ratio.
    +         * @param {ol.proj.Projection} projection Projection.
    +         * @return {string|undefined} Tile URL.
    +         */
    +        function(tileCoord, pixelRatio, projection) {
    +          if (goog.isNull(tileCoord)) {
    +            return undefined;
    +          } else {
    +            var localContext = {
    +              'TileMatrix': tileGrid.getMatrixId(tileCoord[0]),
    +              'TileCol': tileCoord[1],
    +              'TileRow': tileCoord[2]
    +            };
    +            goog.object.extend(localContext, dimensions);
    +            var url = template;
    +            if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) {
    +              url = goog.uri.utils.appendParamsFromMap(url, localContext);
    +            } else {
    +              url = url.replace(/\{(\w+?)\}/g, function(m, p) {
    +                return localContext[p];
    +              });
    +            }
    +            return url;
    +          }
    +        });
    +  }
    +
    +  var tileUrlFunction = ol.TileUrlFunction.nullTileUrlFunction;
    +  var urls = options.urls;
    +  if (!goog.isDef(urls) && goog.isDef(options.url)) {
    +    urls = ol.TileUrlFunction.expandUrl(options.url);
    +  }
    +  if (goog.isDef(urls)) {
    +    tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions(
    +        goog.array.map(urls, createFromWMTSTemplate));
    +  }
    +
    +  var tmpExtent = ol.extent.createEmpty();
    +  var tmpTileCoord = [0, 0, 0];
    +  tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile coordinate.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @param {ol.TileCoord=} opt_tileCoord Tile coordinate.
    +       * @return {ol.TileCoord} Tile coordinate.
    +       */
    +      function(tileCoord, projection, opt_tileCoord) {
    +        goog.asserts.assert(!goog.isNull(tileGrid));
    +        if (tileGrid.getResolutions().length <= tileCoord[0]) {
    +          return null;
    +        }
    +        var x = tileCoord[1];
    +        var y = -tileCoord[2] - 1;
    +        var tileExtent = tileGrid.getTileCoordExtent(tileCoord, tmpExtent);
    +        var extent = projection.getExtent();
    +
    +        if (!goog.isNull(extent) && projection.isGlobal()) {
    +          var numCols = Math.ceil(
    +              ol.extent.getWidth(extent) /
    +              ol.extent.getWidth(tileExtent));
    +          x = goog.math.modulo(x, numCols);
    +          tmpTileCoord[0] = tileCoord[0];
    +          tmpTileCoord[1] = x;
    +          tmpTileCoord[2] = tileCoord[2];
    +          tileExtent = tileGrid.getTileCoordExtent(tmpTileCoord, tmpExtent);
    +        }
    +        if (!ol.extent.intersects(tileExtent, extent) ||
    +            ol.extent.touches(tileExtent, extent)) {
    +          return null;
    +        }
    +        return ol.tilecoord.createOrUpdate(tileCoord[0], x, y, opt_tileCoord);
    +      },
    +      tileUrlFunction);
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    crossOrigin: options.crossOrigin,
    +    logo: options.logo,
    +    projection: options.projection,
    +    tileClass: options.tileClass,
    +    tileGrid: tileGrid,
    +    tileLoadFunction: options.tileLoadFunction,
    +    tilePixelRatio: options.tilePixelRatio,
    +    tileUrlFunction: tileUrlFunction
    +  });
    +
    +};
    +goog.inherits(ol.source.WMTS, ol.source.TileImage);
    +
    +
    +/**
    + * Get the dimensions, i.e. those passed to the constructor through the
    + * "dimensions" option, and possibly updated using the updateDimensions
    + * method.
    + * @return {Object} Dimensions.
    + * @api
    + */
    +ol.source.WMTS.prototype.getDimensions = function() {
    +  return this.dimensions_;
    +};
    +
    +
    +/**
    + * @return {string} Format.
    + * @api
    + */
    +ol.source.WMTS.prototype.getFormat = function() {
    +  return this.format_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.WMTS.prototype.getKeyZXY = function(z, x, y) {
    +  return this.coordKeyPrefix_ + goog.base(this, 'getKeyZXY', z, x, y);
    +};
    +
    +
    +/**
    + * @return {string} Layer.
    + * @api
    + */
    +ol.source.WMTS.prototype.getLayer = function() {
    +  return this.layer_;
    +};
    +
    +
    +/**
    + * @return {string} MatrixSet.
    + * @api
    + */
    +ol.source.WMTS.prototype.getMatrixSet = function() {
    +  return this.matrixSet_;
    +};
    +
    +
    +/**
    + * @return {string} Style.
    + * @api
    + */
    +ol.source.WMTS.prototype.getStyle = function() {
    +  return this.style_;
    +};
    +
    +
    +/**
    + * @return {string} Version.
    + * @api
    + */
    +ol.source.WMTS.prototype.getVersion = function() {
    +  return this.version_;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.source.WMTS.prototype.resetCoordKeyPrefix_ = function() {
    +  var i = 0;
    +  var res = [];
    +  for (var key in this.dimensions_) {
    +    res[i++] = key + '-' + this.dimensions_[key];
    +  }
    +  this.coordKeyPrefix_ = res.join('/');
    +};
    +
    +
    +/**
    + * Update the dimensions.
    + * @param {Object} dimensions Dimensions.
    + * @api
    + */
    +ol.source.WMTS.prototype.updateDimensions = function(dimensions) {
    +  goog.object.extend(this.dimensions_, dimensions);
    +  this.resetCoordKeyPrefix_();
    +  this.changed();
    +};
    +
    +
    +/**
    + * @param {Object} wmtsCap An object representing the capabilities document.
    + * @param {Object} config Configuration properties for the layer.  Defaults for
    + *                  the layer will apply if not provided.
    + *
    + * Required config properties:
    + * layer - {String} The layer identifier.
    + *
    + * Optional config properties:
    + * matrixSet - {String} The matrix set identifier, required if there is
    + *      more than one matrix set in the layer capabilities.
    + * projection - {String} The desired CRS when no matrixSet is specified.
    + *     eg: "EPSG:3857". If the desired projection is not available,
    + *     an error is thrown.
    + * requestEncoding - {String} url encoding format for the layer. Default is the
    + *     first tile url format found in the GetCapabilities response.
    + * style - {String} The name of the style
    + * format - {String} Image format for the layer. Default is the first
    + *     format returned in the GetCapabilities response.
    + * @return {olx.source.WMTSOptions} WMTS source options object.
    + * @api
    + */
    +ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) {
    +
    +  /* jshint -W069 */
    +
    +  // TODO: add support for TileMatrixLimits
    +  goog.asserts.assert(!goog.isNull(config['layer']));
    +
    +  var layers = wmtsCap['Contents']['Layer'];
    +  var l = goog.array.find(layers, function(elt, index, array) {
    +    return elt['Identifier'] == config['layer'];
    +  });
    +  goog.asserts.assert(!goog.isNull(l));
    +
    +  goog.asserts.assert(l['TileMatrixSetLink'].length > 0);
    +  var idx, matrixSet;
    +  if (l['TileMatrixSetLink'].length > 1) {
    +    idx = goog.array.findIndex(l['TileMatrixSetLink'],
    +        function(elt, index, array) {
    +          return elt['TileMatrixSet'] == config['matrixSet'];
    +        });
    +  } else if (goog.isDef(config['projection'])) {
    +    idx = goog.array.findIndex(l['TileMatrixSetLink'],
    +        function(elt, index, array) {
    +          return elt['TileMatrixSet']['SupportedCRS'].replace(
    +              /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'
    +                 ) == config['projection'];
    +        });
    +  } else {
    +    idx = 0;
    +  }
    +  if (idx < 0) {
    +    idx = 0;
    +  }
    +  matrixSet = /** @type {string} */
    +      (l['TileMatrixSetLink'][idx]['TileMatrixSet']);
    +
    +  goog.asserts.assert(!goog.isNull(matrixSet));
    +
    +  var format = /** @type {string} */ (l['Format'][0]);
    +  if (goog.isDef(config['format'])) {
    +    format = config['format'];
    +  }
    +  idx = goog.array.findIndex(l['Style'], function(elt, index, array) {
    +    if (goog.isDef(config['style'])) {
    +      return elt['Title'] == config['style'];
    +    } else {
    +      return elt['isDefault'];
    +    }
    +  });
    +  if (idx < 0) {
    +    idx = 0;
    +  }
    +  var style = /** @type {string} */ (l['Style'][idx]['Identifier']);
    +
    +  var dimensions = {};
    +  if (goog.isDef(l['Dimension'])) {
    +    goog.array.forEach(l['Dimension'], function(elt, index, array) {
    +      var key = elt['Identifier'];
    +      var value = elt['default'];
    +      if (goog.isDef(value)) {
    +        goog.asserts.assert(goog.array.contains(elt['values'], value));
    +      } else {
    +        value = elt['values'][0];
    +      }
    +      goog.asserts.assert(goog.isDef(value));
    +      dimensions[key] = value;
    +    });
    +  }
    +
    +  var matrixSets = wmtsCap['Contents']['TileMatrixSet'];
    +  var matrixSetObj = goog.array.find(matrixSets, function(elt, index, array) {
    +    return elt['Identifier'] == matrixSet;
    +  });
    +  goog.asserts.assert(!goog.isNull(matrixSetObj));
    +
    +  var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet(
    +      matrixSetObj);
    +
    +  var projection;
    +  if (goog.isDef(config['projection'])) {
    +    projection = ol.proj.get(config['projection']);
    +  } else {
    +    projection = ol.proj.get(matrixSetObj['SupportedCRS'].replace(
    +        /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'));
    +  }
    +
    +  /** @type {!Array.<string>} */
    +  var urls = [];
    +  var requestEncoding = config['requestEncoding'];
    +  requestEncoding = goog.isDef(requestEncoding) ? requestEncoding : '';
    +
    +  goog.asserts.assert(
    +      goog.array.contains(['REST', 'RESTful', 'KVP', ''], requestEncoding));
    +
    +  if (!wmtsCap['OperationsMetadata'].hasOwnProperty('GetTile') ||
    +      goog.string.startsWith(requestEncoding, 'REST')) {
    +    // Add REST tile resource url
    +    requestEncoding = ol.source.WMTSRequestEncoding.REST;
    +    goog.array.forEach(l['ResourceURL'], function(elt, index, array) {
    +      if (elt['resourceType'] == 'tile') {
    +        format = elt['format'];
    +        urls.push(/** @type {string} */ (elt['template']));
    +      }
    +    });
    +  } else {
    +    var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get'];
    +
    +    var constraint = goog.array.find(gets[0]['Constraint'],
    +        function(elt, index, array) {
    +          return elt['name'] == 'GetEncoding';
    +        });
    +    var encodings = constraint['AllowedValues']['Value'];
    +    if (encodings.length > 0 && goog.array.contains(encodings, 'KVP')) {
    +      requestEncoding = ol.source.WMTSRequestEncoding.KVP;
    +      urls.push(/** @type {string} */ (gets[0]['href']));
    +
    +    }
    +  }
    +  goog.asserts.assert(urls.length > 0);
    +
    +  return {
    +    urls: urls,
    +    layer: config['layer'],
    +    matrixSet: matrixSet,
    +    format: format,
    +    projection: projection,
    +    requestEncoding: requestEncoding,
    +    tileGrid: tileGrid,
    +    style: style,
    +    dimensions: dimensions
    +  };
    +
    +  /* jshint +W069 */
    +
    +};
    diff --git a/mapbox-gl/ol/ol/source/xyzsource.js b/mapbox-gl/ol/ol/source/xyzsource.js
    new file mode 100644
    index 0000000000..1b5d2ec2d9
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/xyzsource.js
    @@ -0,0 +1,86 @@
    +goog.provide('ol.source.XYZ');
    +
    +goog.require('ol.Attribution');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilegrid.XYZ');
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data with URLs in a set XYZ format.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.XYZOptions} options XYZ options.
    + * @api stable
    + */
    +ol.source.XYZ = function(options) {
    +  var projection = goog.isDef(options.projection) ?
    +      options.projection : 'EPSG:3857';
    +
    +  var tileGrid = new ol.tilegrid.XYZ({
    +    extent: ol.tilegrid.extentFromProjection(projection),
    +    maxZoom: options.maxZoom,
    +    tileSize: options.tileSize
    +  });
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    crossOrigin: options.crossOrigin,
    +    logo: options.logo,
    +    projection: projection,
    +    tileGrid: tileGrid,
    +    tileLoadFunction: options.tileLoadFunction,
    +    tilePixelRatio: options.tilePixelRatio,
    +    tileUrlFunction: ol.TileUrlFunction.nullTileUrlFunction
    +  });
    +
    +  /**
    +   * @private
    +   * @type {ol.TileCoordTransformType}
    +   */
    +  this.tileCoordTransform_ = tileGrid.createTileCoordTransform({
    +    wrapX: options.wrapX
    +  });
    +
    +  if (goog.isDef(options.tileUrlFunction)) {
    +    this.setTileUrlFunction(options.tileUrlFunction);
    +  } else if (goog.isDef(options.urls)) {
    +    this.setUrls(options.urls);
    +  } else if (goog.isDef(options.url)) {
    +    this.setUrl(options.url);
    +  }
    +
    +};
    +goog.inherits(ol.source.XYZ, ol.source.TileImage);
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.source.XYZ.prototype.setTileUrlFunction = function(tileUrlFunction) {
    +  goog.base(this, 'setTileUrlFunction',
    +      ol.TileUrlFunction.withTileCoordTransform(
    +          this.tileCoordTransform_, tileUrlFunction));
    +};
    +
    +
    +/**
    + * @param {string} url URL.
    + * @api stable
    + */
    +ol.source.XYZ.prototype.setUrl = function(url) {
    +  this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(
    +      ol.TileUrlFunction.expandUrl(url)));
    +};
    +
    +
    +/**
    + * @param {Array.<string>} urls URLs.
    + */
    +ol.source.XYZ.prototype.setUrls = function(urls) {
    +  this.setTileUrlFunction(ol.TileUrlFunction.createFromTemplates(urls));
    +};
    diff --git a/mapbox-gl/ol/ol/source/zoomifysource.js b/mapbox-gl/ol/ol/source/zoomifysource.js
    new file mode 100644
    index 0000000000..7c7bd3dd79
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/source/zoomifysource.js
    @@ -0,0 +1,185 @@
    +goog.provide('ol.source.Zoomify');
    +
    +goog.require('goog.asserts');
    +goog.require('ol');
    +goog.require('ol.ImageTile');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileState');
    +goog.require('ol.TileUrlFunction');
    +goog.require('ol.dom');
    +goog.require('ol.proj');
    +goog.require('ol.source.TileImage');
    +goog.require('ol.tilegrid.Zoomify');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.source.ZoomifyTierSizeCalculation = {
    +  DEFAULT: 'default',
    +  TRUNCATED: 'truncated'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Layer source for tile data in Zoomify format.
    + *
    + * @constructor
    + * @extends {ol.source.TileImage}
    + * @param {olx.source.ZoomifyOptions=} opt_options Options.
    + * @api stable
    + */
    +ol.source.Zoomify = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var size = options.size;
    +  var tierSizeCalculation = goog.isDef(options.tierSizeCalculation) ?
    +      options.tierSizeCalculation :
    +      ol.source.ZoomifyTierSizeCalculation.DEFAULT;
    +
    +  var imageWidth = size[0];
    +  var imageHeight = size[1];
    +  var tierSizeInTiles = [];
    +  var tileSize = ol.DEFAULT_TILE_SIZE;
    +
    +  switch (tierSizeCalculation) {
    +    case ol.source.ZoomifyTierSizeCalculation.DEFAULT:
    +      while (imageWidth > tileSize || imageHeight > tileSize) {
    +        tierSizeInTiles.push([
    +          Math.ceil(imageWidth / tileSize),
    +          Math.ceil(imageHeight / tileSize)
    +        ]);
    +        tileSize += tileSize;
    +      }
    +      break;
    +    case ol.source.ZoomifyTierSizeCalculation.TRUNCATED:
    +      var width = imageWidth;
    +      var height = imageHeight;
    +      while (width > tileSize || height > tileSize) {
    +        tierSizeInTiles.push([
    +          Math.ceil(width / tileSize),
    +          Math.ceil(height / tileSize)
    +        ]);
    +        width >>= 1;
    +        height >>= 1;
    +      }
    +      break;
    +    default:
    +      goog.asserts.fail();
    +      break;
    +  }
    +
    +  tierSizeInTiles.push([1, 1]);
    +  tierSizeInTiles.reverse();
    +
    +  var resolutions = [1];
    +  var tileCountUpToTier = [0];
    +  var i, ii;
    +  for (i = 1, ii = tierSizeInTiles.length; i < ii; i++) {
    +    resolutions.push(1 << i);
    +    tileCountUpToTier.push(
    +        tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] +
    +        tileCountUpToTier[i - 1]
    +    );
    +  }
    +  resolutions.reverse();
    +
    +  var tileGrid = new ol.tilegrid.Zoomify({
    +    resolutions: resolutions
    +  });
    +
    +  var url = options.url;
    +  var tileUrlFunction = ol.TileUrlFunction.withTileCoordTransform(
    +      tileGrid.createTileCoordTransform({extent: [0, 0, size[0], size[1]]}),
    +      /**
    +       * @this {ol.source.TileImage}
    +       * @param {ol.TileCoord} tileCoord Tile Coordinate.
    +       * @param {number} pixelRatio Pixel ratio.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @return {string|undefined} Tile URL.
    +       */
    +      function(tileCoord, pixelRatio, projection) {
    +        if (goog.isNull(tileCoord)) {
    +          return undefined;
    +        } else {
    +          var tileCoordZ = tileCoord[0];
    +          var tileCoordX = tileCoord[1];
    +          var tileCoordY = tileCoord[2];
    +          var tileIndex =
    +              tileCoordX +
    +              tileCoordY * tierSizeInTiles[tileCoordZ][0] +
    +              tileCountUpToTier[tileCoordZ];
    +          var tileGroup = (tileIndex / ol.DEFAULT_TILE_SIZE) | 0;
    +          return url + 'TileGroup' + tileGroup + '/' +
    +              tileCoordZ + '-' + tileCoordX + '-' + tileCoordY + '.jpg';
    +        }
    +      });
    +
    +  goog.base(this, {
    +    attributions: options.attributions,
    +    crossOrigin: options.crossOrigin,
    +    logo: options.logo,
    +    tileClass: ol.source.ZoomifyTile_,
    +    tileGrid: tileGrid,
    +    tileUrlFunction: tileUrlFunction
    +  });
    +
    +};
    +goog.inherits(ol.source.Zoomify, ol.source.TileImage);
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.ImageTile}
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.TileState} state State.
    + * @param {string} src Image source URI.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
    + * @private
    + */
    +ol.source.ZoomifyTile_ = function(
    +    tileCoord, state, src, crossOrigin, tileLoadFunction) {
    +
    +  goog.base(this, tileCoord, state, src, crossOrigin, tileLoadFunction);
    +
    +  /**
    +   * @private
    +   * @type {Object.<string,
    +   *                HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>}
    +   */
    +  this.zoomifyImageByContext_ = {};
    +
    +};
    +goog.inherits(ol.source.ZoomifyTile_, ol.ImageTile);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.source.ZoomifyTile_.prototype.getImage = function(opt_context) {
    +  var tileSize = ol.DEFAULT_TILE_SIZE;
    +  var key = goog.isDef(opt_context) ? goog.getUid(opt_context).toString() : '';
    +  if (key in this.zoomifyImageByContext_) {
    +    return this.zoomifyImageByContext_[key];
    +  } else {
    +    var image = goog.base(this, 'getImage', opt_context);
    +    if (this.state == ol.TileState.LOADED) {
    +      if (image.width == tileSize && image.height == tileSize) {
    +        this.zoomifyImageByContext_[key] = image;
    +        return image;
    +      } else {
    +        var context = ol.dom.createCanvasContext2D(tileSize, tileSize);
    +        context.drawImage(image, 0, 0);
    +        this.zoomifyImageByContext_[key] = context.canvas;
    +        return context.canvas;
    +      }
    +    } else {
    +      return image;
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/sphere/normal.js b/mapbox-gl/ol/ol/sphere/normal.js
    new file mode 100644
    index 0000000000..70e6f8e165
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/sphere/normal.js
    @@ -0,0 +1,11 @@
    +goog.provide('ol.sphere.NORMAL');
    +
    +goog.require('ol.Sphere');
    +
    +
    +/**
    + * The normal sphere.
    + * @const
    + * @type {ol.Sphere}
    + */
    +ol.sphere.NORMAL = new ol.Sphere(6370997);
    diff --git a/mapbox-gl/ol/ol/sphere/sphere.js b/mapbox-gl/ol/ol/sphere/sphere.js
    new file mode 100644
    index 0000000000..11beda86f3
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/sphere/sphere.js
    @@ -0,0 +1,267 @@
    +/**
    + * @license
    + * Latitude/longitude spherical geodesy formulae taken from
    + * http://www.movable-type.co.uk/scripts/latlong.html
    + * Licensed under CC-BY-3.0.
    + */
    +
    +// FIXME add intersection of two paths given start points and bearings
    +// FIXME add rhumb lines
    +
    +goog.provide('ol.Sphere');
    +
    +goog.require('goog.math');
    +
    +
    +
    +/**
    + * @classdesc
    + * Class to create objects that can be used with {@link
    + * ol.geom.Polygon.circular}.
    + *
    + * For example to create a sphere whose radius is equal to the semi-major
    + * axis of the WGS84 ellipsoid:
    + *
    + * ```js
    + * var wgs84Sphere= new ol.Sphere(6378137);
    + * ```
    + *
    + * @constructor
    + * @param {number} radius Radius.
    + * @api
    + */
    +ol.Sphere = function(radius) {
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.radius = radius;
    +
    +};
    +
    +
    +/**
    + * Returns the distance from c1 to c2 using the spherical law of cosines.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {number} Spherical law of cosines distance.
    + */
    +ol.Sphere.prototype.cosineDistance = function(c1, c2) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var deltaLon = goog.math.toRadians(c2[0] - c1[0]);
    +  return this.radius * Math.acos(
    +      Math.sin(lat1) * Math.sin(lat2) +
    +      Math.cos(lat1) * Math.cos(lat2) * Math.cos(deltaLon));
    +};
    +
    +
    +/**
    + * Returns the geodesic area for a list of coordinates.
    + *
    + * [Reference](http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409)
    + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
    + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
    + * Laboratory, Pasadena, CA, June 2007
    + *
    + * @param {Array.<ol.Coordinate>} coordinates List of coordinates of a linear
    + * ring. If the ring is oriented clockwise, the area will be positive,
    + * otherwise it will be negative.
    + * @return {number} Area.
    + * @api
    + */
    +ol.Sphere.prototype.geodesicArea = function(coordinates) {
    +  var area = 0, len = coordinates.length;
    +  var x1 = coordinates[len - 1][0];
    +  var y1 = coordinates[len - 1][1];
    +  for (var i = 0; i < len; i++) {
    +    var x2 = coordinates[i][0], y2 = coordinates[i][1];
    +    area += goog.math.toRadians(x2 - x1) *
    +        (2 + Math.sin(goog.math.toRadians(y1)) +
    +        Math.sin(goog.math.toRadians(y2)));
    +    x1 = x2;
    +    y1 = y2;
    +  }
    +  return area * this.radius * this.radius / 2.0;
    +};
    +
    +
    +/**
    + * Returns the distance of c3 from the great circle path defined by c1 and c2.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @param {ol.Coordinate} c3 Coordinate 3.
    + * @return {number} Cross-track distance.
    + */
    +ol.Sphere.prototype.crossTrackDistance = function(c1, c2, c3) {
    +  var d13 = this.cosineDistance(c1, c2);
    +  var theta12 = goog.math.toRadians(this.initialBearing(c1, c2));
    +  var theta13 = goog.math.toRadians(this.initialBearing(c1, c3));
    +  return this.radius *
    +      Math.asin(Math.sin(d13 / this.radius) * Math.sin(theta13 - theta12));
    +};
    +
    +
    +/**
    + * Returns the distance from c1 to c2 using Pythagoras's theorem on an
    + * equirectangular projection.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {number} Equirectangular distance.
    + */
    +ol.Sphere.prototype.equirectangularDistance = function(c1, c2) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var deltaLon = goog.math.toRadians(c2[0] - c1[0]);
    +  var x = deltaLon * Math.cos((lat1 + lat2) / 2);
    +  var y = lat2 - lat1;
    +  return this.radius * Math.sqrt(x * x + y * y);
    +};
    +
    +
    +/**
    + * Returns the final bearing from c1 to c2.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {number} Initial bearing.
    + */
    +ol.Sphere.prototype.finalBearing = function(c1, c2) {
    +  return (this.initialBearing(c2, c1) + 180) % 360;
    +};
    +
    +
    +/**
    + * Returns the distance from c1 to c2 using the haversine formula.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {number} Haversine distance.
    + * @api
    + */
    +ol.Sphere.prototype.haversineDistance = function(c1, c2) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var deltaLatBy2 = (lat2 - lat1) / 2;
    +  var deltaLonBy2 = goog.math.toRadians(c2[0] - c1[0]) / 2;
    +  var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
    +      Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
    +      Math.cos(lat1) * Math.cos(lat2);
    +  return 2 * this.radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    +};
    +
    +
    +/**
    + * Returns the point at `fraction` along the segment of the great circle passing
    + * through c1 and c2.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @param {number} fraction Fraction.
    + * @return {ol.Coordinate} Coordinate between c1 and c2.
    + */
    +ol.Sphere.prototype.interpolate = function(c1, c2, fraction) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lon1 = goog.math.toRadians(c1[0]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var lon2 = goog.math.toRadians(c2[0]);
    +  var cosLat1 = Math.cos(lat1);
    +  var sinLat1 = Math.sin(lat1);
    +  var cosLat2 = Math.cos(lat2);
    +  var sinLat2 = Math.sin(lat2);
    +  var cosDeltaLon = Math.cos(lon2 - lon1);
    +  var d = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosDeltaLon;
    +  if (1 <= d) {
    +    return c2.slice();
    +  }
    +  d = fraction * Math.acos(d);
    +  var cosD = Math.cos(d);
    +  var sinD = Math.sin(d);
    +  var y = Math.sin(lon2 - lon1) * cosLat2;
    +  var x = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosDeltaLon;
    +  var theta = Math.atan2(y, x);
    +  var lat = Math.asin(sinLat1 * cosD + cosLat1 * sinD * Math.cos(theta));
    +  var lon = lon1 + Math.atan2(Math.sin(theta) * sinD * cosLat1,
    +                              cosD - sinLat1 * Math.sin(lat));
    +  return [goog.math.toDegrees(lon), goog.math.toDegrees(lat)];
    +};
    +
    +
    +/**
    + * Returns the initial bearing from c1 to c2.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {number} Initial bearing.
    + */
    +ol.Sphere.prototype.initialBearing = function(c1, c2) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var deltaLon = goog.math.toRadians(c2[0] - c1[0]);
    +  var y = Math.sin(deltaLon) * Math.cos(lat2);
    +  var x = Math.cos(lat1) * Math.sin(lat2) -
    +      Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon);
    +  return goog.math.toDegrees(Math.atan2(y, x));
    +};
    +
    +
    +/**
    + * Returns the maximum latitude of the great circle defined by bearing and
    + * latitude.
    + *
    + * @param {number} bearing Bearing.
    + * @param {number} latitude Latitude.
    + * @return {number} Maximum latitude.
    + */
    +ol.Sphere.prototype.maximumLatitude = function(bearing, latitude) {
    +  return Math.cos(Math.abs(Math.sin(goog.math.toRadians(bearing)) *
    +                           Math.cos(goog.math.toRadians(latitude))));
    +};
    +
    +
    +/**
    + * Returns the midpoint between c1 and c2.
    + *
    + * @param {ol.Coordinate} c1 Coordinate 1.
    + * @param {ol.Coordinate} c2 Coordinate 2.
    + * @return {ol.Coordinate} Midpoint.
    + */
    +ol.Sphere.prototype.midpoint = function(c1, c2) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lat2 = goog.math.toRadians(c2[1]);
    +  var lon1 = goog.math.toRadians(c1[0]);
    +  var deltaLon = goog.math.toRadians(c2[0] - c1[0]);
    +  var Bx = Math.cos(lat2) * Math.cos(deltaLon);
    +  var By = Math.cos(lat2) * Math.sin(deltaLon);
    +  var cosLat1PlusBx = Math.cos(lat1) + Bx;
    +  var lat = Math.atan2(Math.sin(lat1) + Math.sin(lat2),
    +                       Math.sqrt(cosLat1PlusBx * cosLat1PlusBx + By * By));
    +  var lon = lon1 + Math.atan2(By, cosLat1PlusBx);
    +  return [goog.math.toDegrees(lon), goog.math.toDegrees(lat)];
    +};
    +
    +
    +/**
    + * Returns the coordinate at the given distance and bearing from `c1`.
    + *
    + * @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees).
    + * @param {number} distance The great-circle distance between the origin
    + *     point and the target point.
    + * @param {number} bearing The bearing (in radians).
    + * @return {ol.Coordinate} The target point.
    + */
    +ol.Sphere.prototype.offset = function(c1, distance, bearing) {
    +  var lat1 = goog.math.toRadians(c1[1]);
    +  var lon1 = goog.math.toRadians(c1[0]);
    +  var dByR = distance / this.radius;
    +  var lat = Math.asin(
    +      Math.sin(lat1) * Math.cos(dByR) +
    +      Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
    +  var lon = lon1 + Math.atan2(
    +      Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
    +      Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
    +  return [goog.math.toDegrees(lon), goog.math.toDegrees(lat)];
    +};
    diff --git a/mapbox-gl/ol/ol/sphere/wgs84sphere.js b/mapbox-gl/ol/ol/sphere/wgs84sphere.js
    new file mode 100644
    index 0000000000..19624fd6b2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/sphere/wgs84sphere.js
    @@ -0,0 +1,11 @@
    +goog.provide('ol.sphere.WGS84');
    +
    +goog.require('ol.Sphere');
    +
    +
    +/**
    + * A sphere with radius equal to the semi-major axis of the WGS84 ellipsoid.
    + * @const
    + * @type {ol.Sphere}
    + */
    +ol.sphere.WGS84 = new ol.Sphere(6378137);
    diff --git a/mapbox-gl/ol/ol/structs/checksum.js b/mapbox-gl/ol/ol/structs/checksum.js
    new file mode 100644
    index 0000000000..ff72308ad2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/structs/checksum.js
    @@ -0,0 +1,16 @@
    +goog.provide('ol.structs.IHasChecksum');
    +
    +
    +
    +/**
    + * @interface
    + */
    +ol.structs.IHasChecksum = function() {
    +};
    +
    +
    +/**
    + * @return {string} The checksum.
    + */
    +ol.structs.IHasChecksum.prototype.getChecksum = function() {
    +};
    diff --git a/mapbox-gl/ol/ol/structs/lrucache.js b/mapbox-gl/ol/ol/structs/lrucache.js
    new file mode 100644
    index 0000000000..d1b1f3a6c5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/structs/lrucache.js
    @@ -0,0 +1,248 @@
    +goog.provide('ol.structs.LRUCache');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Implements a Least-Recently-Used cache where the keys do not conflict with
    + * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring
    + * items from the cache is the responsibility of the user.
    + * @constructor
    + * @struct
    + * @template T
    + */
    +ol.structs.LRUCache = function() {
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.count_ = 0;
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.structs.LRUCacheEntry>}
    +   */
    +  this.entries_ = {};
    +
    +  /**
    +   * @private
    +   * @type {?ol.structs.LRUCacheEntry}
    +   */
    +  this.oldest_ = null;
    +
    +  /**
    +   * @private
    +   * @type {?ol.structs.LRUCacheEntry}
    +   */
    +  this.newest_ = null;
    +
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.structs.LRUCache.prototype.assertValid = function() {
    +  if (this.count_ === 0) {
    +    goog.asserts.assert(goog.object.isEmpty(this.entries_));
    +    goog.asserts.assert(goog.isNull(this.oldest_));
    +    goog.asserts.assert(goog.isNull(this.newest_));
    +  } else {
    +    goog.asserts.assert(goog.object.getCount(this.entries_) == this.count_);
    +    goog.asserts.assert(!goog.isNull(this.oldest_));
    +    goog.asserts.assert(goog.isNull(this.oldest_.older));
    +    goog.asserts.assert(!goog.isNull(this.newest_));
    +    goog.asserts.assert(goog.isNull(this.newest_.newer));
    +    var i, entry;
    +    var older = null;
    +    i = 0;
    +    for (entry = this.oldest_; !goog.isNull(entry); entry = entry.newer) {
    +      goog.asserts.assert(entry.older === older);
    +      older = entry;
    +      ++i;
    +    }
    +    goog.asserts.assert(i == this.count_);
    +    var newer = null;
    +    i = 0;
    +    for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    +      goog.asserts.assert(entry.newer === newer);
    +      newer = entry;
    +      ++i;
    +    }
    +    goog.asserts.assert(i == this.count_);
    +  }
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.structs.LRUCache.prototype.clear = function() {
    +  this.count_ = 0;
    +  this.entries_ = {};
    +  this.oldest_ = null;
    +  this.newest_ = null;
    +};
    +
    +
    +/**
    + * @param {string} key Key.
    + * @return {boolean} Contains key.
    + */
    +ol.structs.LRUCache.prototype.containsKey = function(key) {
    +  return this.entries_.hasOwnProperty(key);
    +};
    +
    +
    +/**
    + * @param {function(this: S, T, string, ol.structs.LRUCache): ?} f The function
    + *     to call for every entry from the oldest to the newer. This function takes
    + *     3 arguments (the entry value, the entry key and the LRUCache object).
    + *     The return value is ignored.
    + * @param {S=} opt_this The object to use as `this` in `f`.
    + * @template S
    + */
    +ol.structs.LRUCache.prototype.forEach = function(f, opt_this) {
    +  var entry = this.oldest_;
    +  while (!goog.isNull(entry)) {
    +    f.call(opt_this, entry.value_, entry.key_, this);
    +    entry = entry.newer;
    +  }
    +};
    +
    +
    +/**
    + * @param {string} key Key.
    + * @return {T} Value.
    + */
    +ol.structs.LRUCache.prototype.get = function(key) {
    +  var entry = this.entries_[key];
    +  goog.asserts.assert(goog.isDef(entry));
    +  if (entry === this.newest_) {
    +    return entry.value_;
    +  } else if (entry === this.oldest_) {
    +    this.oldest_ = this.oldest_.newer;
    +    this.oldest_.older = null;
    +  } else {
    +    entry.newer.older = entry.older;
    +    entry.older.newer = entry.newer;
    +  }
    +  entry.newer = null;
    +  entry.older = this.newest_;
    +  this.newest_.newer = entry;
    +  this.newest_ = entry;
    +  return entry.value_;
    +};
    +
    +
    +/**
    + * @return {number} Count.
    + */
    +ol.structs.LRUCache.prototype.getCount = function() {
    +  return this.count_;
    +};
    +
    +
    +/**
    + * @return {Array.<string>} Keys.
    + */
    +ol.structs.LRUCache.prototype.getKeys = function() {
    +  var keys = new Array(this.count_);
    +  var i = 0;
    +  var entry;
    +  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    +    keys[i++] = entry.key_;
    +  }
    +  goog.asserts.assert(i == this.count_);
    +  return keys;
    +};
    +
    +
    +/**
    + * @return {Array.<T>} Values.
    + */
    +ol.structs.LRUCache.prototype.getValues = function() {
    +  var values = new Array(this.count_);
    +  var i = 0;
    +  var entry;
    +  for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) {
    +    values[i++] = entry.value_;
    +  }
    +  goog.asserts.assert(i == this.count_);
    +  return values;
    +};
    +
    +
    +/**
    + * @return {T} Last value.
    + */
    +ol.structs.LRUCache.prototype.peekLast = function() {
    +  goog.asserts.assert(!goog.isNull(this.oldest_));
    +  return this.oldest_.value_;
    +};
    +
    +
    +/**
    + * @return {string} Last key.
    + */
    +ol.structs.LRUCache.prototype.peekLastKey = function() {
    +  goog.asserts.assert(!goog.isNull(this.oldest_));
    +  return this.oldest_.key_;
    +};
    +
    +
    +/**
    + * @return {T} value Value.
    + */
    +ol.structs.LRUCache.prototype.pop = function() {
    +  goog.asserts.assert(!goog.isNull(this.oldest_));
    +  goog.asserts.assert(!goog.isNull(this.newest_));
    +  var entry = this.oldest_;
    +  goog.asserts.assert(entry.key_ in this.entries_);
    +  delete this.entries_[entry.key_];
    +  if (!goog.isNull(entry.newer)) {
    +    entry.newer.older = null;
    +  }
    +  this.oldest_ = entry.newer;
    +  if (goog.isNull(this.oldest_)) {
    +    this.newest_ = null;
    +  }
    +  --this.count_;
    +  return entry.value_;
    +};
    +
    +
    +/**
    + * @param {string} key Key.
    + * @param {T} value Value.
    + */
    +ol.structs.LRUCache.prototype.set = function(key, value) {
    +  goog.asserts.assert(!(key in {}));
    +  goog.asserts.assert(!(key in this.entries_));
    +  var entry = {
    +    key_: key,
    +    newer: null,
    +    older: this.newest_,
    +    value_: value
    +  };
    +  if (goog.isNull(this.newest_)) {
    +    this.oldest_ = entry;
    +  } else {
    +    this.newest_.newer = entry;
    +  }
    +  this.newest_ = entry;
    +  this.entries_[key] = entry;
    +  ++this.count_;
    +};
    +
    +
    +/**
    + * @typedef {{key_: string,
    + *            newer: ol.structs.LRUCacheEntry,
    + *            older: ol.structs.LRUCacheEntry,
    + *            value_: *}}
    + */
    +ol.structs.LRUCacheEntry;
    diff --git a/mapbox-gl/ol/ol/structs/priorityqueue.js b/mapbox-gl/ol/ol/structs/priorityqueue.js
    new file mode 100644
    index 0000000000..714c59dd64
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/structs/priorityqueue.js
    @@ -0,0 +1,291 @@
    +goog.provide('ol.structs.PriorityQueue');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +
    +
    +
    +/**
    + * Priority queue.
    + *
    + * The implementation is inspired from the Closure Library's Heap class and
    + * Python's heapq module.
    + *
    + * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html
    + * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py
    + *
    + * @constructor
    + * @param {function(T): number} priorityFunction Priority function.
    + * @param {function(T): string} keyFunction Key function.
    + * @struct
    + * @template T
    + */
    +ol.structs.PriorityQueue = function(priorityFunction, keyFunction) {
    +
    +  /**
    +   * @type {function(T): number}
    +   * @private
    +   */
    +  this.priorityFunction_ = priorityFunction;
    +
    +  /**
    +   * @type {function(T): string}
    +   * @private
    +   */
    +  this.keyFunction_ = keyFunction;
    +
    +  /**
    +   * @type {Array.<T>}
    +   * @private
    +   */
    +  this.elements_ = [];
    +
    +  /**
    +   * @type {Array.<number>}
    +   * @private
    +   */
    +  this.priorities_ = [];
    +
    +  /**
    +   * @type {Object.<string, boolean>}
    +   * @private
    +   */
    +  this.queuedElements_ = {};
    +
    +};
    +
    +
    +/**
    + * @const
    + * @type {number}
    + */
    +ol.structs.PriorityQueue.DROP = Infinity;
    +
    +
    +/**
    + * FIXME empty desciption for jsdoc
    + */
    +ol.structs.PriorityQueue.prototype.assertValid = function() {
    +  var elements = this.elements_;
    +  var priorities = this.priorities_;
    +  var n = elements.length;
    +  goog.asserts.assert(priorities.length == n);
    +  var i, priority;
    +  for (i = 0; i < (n >> 1) - 1; ++i) {
    +    priority = priorities[i];
    +    goog.asserts.assert(priority <= priorities[this.getLeftChildIndex_(i)]);
    +    goog.asserts.assert(priority <= priorities[this.getRightChildIndex_(i)]);
    +  }
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.structs.PriorityQueue.prototype.clear = function() {
    +  this.elements_.length = 0;
    +  this.priorities_.length = 0;
    +  goog.object.clear(this.queuedElements_);
    +};
    +
    +
    +/**
    + * Remove and return the highest-priority element. O(log N).
    + * @return {T} Element.
    + */
    +ol.structs.PriorityQueue.prototype.dequeue = function() {
    +  var elements = this.elements_;
    +  goog.asserts.assert(elements.length > 0);
    +  var priorities = this.priorities_;
    +  var element = elements[0];
    +  if (elements.length == 1) {
    +    elements.length = 0;
    +    priorities.length = 0;
    +  } else {
    +    elements[0] = elements.pop();
    +    priorities[0] = priorities.pop();
    +    this.siftUp_(0);
    +  }
    +  var elementKey = this.keyFunction_(element);
    +  goog.asserts.assert(elementKey in this.queuedElements_);
    +  delete this.queuedElements_[elementKey];
    +  return element;
    +};
    +
    +
    +/**
    + * Enqueue an element. O(log N).
    + * @param {T} element Element.
    + */
    +ol.structs.PriorityQueue.prototype.enqueue = function(element) {
    +  goog.asserts.assert(!(this.keyFunction_(element) in this.queuedElements_));
    +  var priority = this.priorityFunction_(element);
    +  if (priority != ol.structs.PriorityQueue.DROP) {
    +    this.elements_.push(element);
    +    this.priorities_.push(priority);
    +    this.queuedElements_[this.keyFunction_(element)] = true;
    +    this.siftDown_(0, this.elements_.length - 1);
    +  }
    +};
    +
    +
    +/**
    + * @return {number} Count.
    + */
    +ol.structs.PriorityQueue.prototype.getCount = function() {
    +  return this.elements_.length;
    +};
    +
    +
    +/**
    + * Gets the index of the left child of the node at the given index.
    + * @param {number} index The index of the node to get the left child for.
    + * @return {number} The index of the left child.
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.getLeftChildIndex_ = function(index) {
    +  return index * 2 + 1;
    +};
    +
    +
    +/**
    + * Gets the index of the right child of the node at the given index.
    + * @param {number} index The index of the node to get the right child for.
    + * @return {number} The index of the right child.
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.getRightChildIndex_ = function(index) {
    +  return index * 2 + 2;
    +};
    +
    +
    +/**
    + * Gets the index of the parent of the node at the given index.
    + * @param {number} index The index of the node to get the parent for.
    + * @return {number} The index of the parent.
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.getParentIndex_ = function(index) {
    +  return (index - 1) >> 1;
    +};
    +
    +
    +/**
    + * Make this a heap. O(N).
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.heapify_ = function() {
    +  var i;
    +  for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
    +    this.siftUp_(i);
    +  }
    +};
    +
    +
    +/**
    + * @return {boolean} Is empty.
    + */
    +ol.structs.PriorityQueue.prototype.isEmpty = function() {
    +  return this.elements_.length === 0;
    +};
    +
    +
    +/**
    + * @param {string} key Key.
    + * @return {boolean} Is key queued.
    + */
    +ol.structs.PriorityQueue.prototype.isKeyQueued = function(key) {
    +  return key in this.queuedElements_;
    +};
    +
    +
    +/**
    + * @param {T} element Element.
    + * @return {boolean} Is queued.
    + */
    +ol.structs.PriorityQueue.prototype.isQueued = function(element) {
    +  return this.isKeyQueued(this.keyFunction_(element));
    +};
    +
    +
    +/**
    + * @param {number} index The index of the node to move down.
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.siftUp_ = function(index) {
    +  var elements = this.elements_;
    +  var priorities = this.priorities_;
    +  var count = elements.length;
    +  var element = elements[index];
    +  var priority = priorities[index];
    +  var startIndex = index;
    +
    +  while (index < (count >> 1)) {
    +    var lIndex = this.getLeftChildIndex_(index);
    +    var rIndex = this.getRightChildIndex_(index);
    +
    +    var smallerChildIndex = rIndex < count &&
    +        priorities[rIndex] < priorities[lIndex] ?
    +        rIndex : lIndex;
    +
    +    elements[index] = elements[smallerChildIndex];
    +    priorities[index] = priorities[smallerChildIndex];
    +    index = smallerChildIndex;
    +  }
    +
    +  elements[index] = element;
    +  priorities[index] = priority;
    +  this.siftDown_(startIndex, index);
    +};
    +
    +
    +/**
    + * @param {number} startIndex The index of the root.
    + * @param {number} index The index of the node to move up.
    + * @private
    + */
    +ol.structs.PriorityQueue.prototype.siftDown_ = function(startIndex, index) {
    +  var elements = this.elements_;
    +  var priorities = this.priorities_;
    +  var element = elements[index];
    +  var priority = priorities[index];
    +
    +  while (index > startIndex) {
    +    var parentIndex = this.getParentIndex_(index);
    +    if (priorities[parentIndex] > priority) {
    +      elements[index] = elements[parentIndex];
    +      priorities[index] = priorities[parentIndex];
    +      index = parentIndex;
    +    } else {
    +      break;
    +    }
    +  }
    +  elements[index] = element;
    +  priorities[index] = priority;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.structs.PriorityQueue.prototype.reprioritize = function() {
    +  var priorityFunction = this.priorityFunction_;
    +  var elements = this.elements_;
    +  var priorities = this.priorities_;
    +  var index = 0;
    +  var n = elements.length;
    +  var element, i, priority;
    +  for (i = 0; i < n; ++i) {
    +    element = elements[i];
    +    priority = priorityFunction(element);
    +    if (priority == ol.structs.PriorityQueue.DROP) {
    +      delete this.queuedElements_[this.keyFunction_(element)];
    +    } else {
    +      priorities[index] = priority;
    +      elements[index++] = element;
    +    }
    +  }
    +  elements.length = index;
    +  priorities.length = index;
    +  this.heapify_();
    +};
    diff --git a/mapbox-gl/ol/ol/structs/rbush.js b/mapbox-gl/ol/ol/structs/rbush.js
    new file mode 100644
    index 0000000000..7efa537782
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/structs/rbush.js
    @@ -0,0 +1,256 @@
    +goog.provide('ol.structs.RBush');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.object');
    +goog.require('ol.ext.rbush');
    +goog.require('ol.extent');
    +
    +
    +
    +/**
    + * Wrapper around the RBush by Vladimir Agafonkin.
    + *
    + * @constructor
    + * @param {number=} opt_maxEntries Max entries.
    + * @see https://github.com/mourner/rbush
    + * @struct
    + * @template T
    + */
    +ol.structs.RBush = function(opt_maxEntries) {
    +
    +  /**
    +   * @private
    +   */
    +  this.rbush_ = ol.ext.rbush(opt_maxEntries);
    +
    +  /**
    +   * A mapping between the objects added to this rbush wrapper
    +   * and the objects that are actually added to the internal rbush.
    +   * @private
    +   * @type {Object.<number, Object>}
    +   */
    +  this.items_ = {};
    +
    +  if (goog.DEBUG) {
    +    /**
    +     * @private
    +     * @type {number}
    +     */
    +    this.readers_ = 0;
    +  }
    +};
    +
    +
    +/**
    + * Insert a value into the RBush.
    + * @param {ol.Extent} extent Extent.
    + * @param {T} value Value.
    + */
    +ol.structs.RBush.prototype.insert = function(extent, value) {
    +  if (goog.DEBUG && this.readers_) {
    +    throw new Error('Can not insert value while reading');
    +  }
    +  var item = [
    +    extent[0],
    +    extent[1],
    +    extent[2],
    +    extent[3],
    +    value
    +  ];
    +  this.rbush_.insert(item);
    +  // remember the object that was added to the internal rbush
    +  goog.asserts.assert(
    +      !goog.object.containsKey(this.items_, goog.getUid(value)));
    +  this.items_[goog.getUid(value)] = item;
    +};
    +
    +
    +/**
    + * Bulk-insert values into the RBush.
    + * @param {Array.<ol.Extent>} extents Extents.
    + * @param {Array.<T>} values Values.
    + */
    +ol.structs.RBush.prototype.load = function(extents, values) {
    +  if (goog.DEBUG && this.readers_) {
    +    throw new Error('Can not insert values while reading');
    +  }
    +  goog.asserts.assert(extents.length === values.length);
    +
    +  var items = new Array(values.length);
    +  for (var i = 0, l = values.length; i < l; i++) {
    +    var extent = extents[i];
    +    var value = values[i];
    +
    +    var item = [
    +      extent[0],
    +      extent[1],
    +      extent[2],
    +      extent[3],
    +      value
    +    ];
    +    items[i] = item;
    +    goog.asserts.assert(
    +        !goog.object.containsKey(this.items_, goog.getUid(value)));
    +    this.items_[goog.getUid(value)] = item;
    +  }
    +  this.rbush_.load(items);
    +};
    +
    +
    +/**
    + * Remove a value from the RBush.
    + * @param {T} value Value.
    + * @return {boolean} Removed.
    + */
    +ol.structs.RBush.prototype.remove = function(value) {
    +  if (goog.DEBUG && this.readers_) {
    +    throw new Error('Can not remove value while reading');
    +  }
    +  var uid = goog.getUid(value);
    +  goog.asserts.assert(goog.object.containsKey(this.items_, uid));
    +
    +  // get the object in which the value was wrapped when adding to the
    +  // internal rbush. then use that object to do the removal.
    +  var item = this.items_[uid];
    +  goog.object.remove(this.items_, uid);
    +  return this.rbush_.remove(item) !== null;
    +};
    +
    +
    +/**
    + * Update the extent of a value in the RBush.
    + * @param {ol.Extent} extent Extent.
    + * @param {T} value Value.
    + */
    +ol.structs.RBush.prototype.update = function(extent, value) {
    +  var uid = goog.getUid(value);
    +  goog.asserts.assert(goog.object.containsKey(this.items_, uid));
    +
    +  var item = this.items_[uid];
    +  if (!ol.extent.equals(item.slice(0, 4), extent)) {
    +    if (goog.DEBUG && this.readers_) {
    +      throw new Error('Can not update extent while reading');
    +    }
    +    this.remove(value);
    +    this.insert(extent, value);
    +  }
    +};
    +
    +
    +/**
    + * Return all values in the RBush.
    + * @return {Array.<T>} All.
    + */
    +ol.structs.RBush.prototype.getAll = function() {
    +  var items = this.rbush_.all();
    +  return goog.array.map(items, function(item) {
    +    return item[4];
    +  });
    +};
    +
    +
    +/**
    + * Return all values in the given extent.
    + * @param {ol.Extent} extent Extent.
    + * @return {Array.<T>} All in extent.
    + */
    +ol.structs.RBush.prototype.getInExtent = function(extent) {
    +  var items = this.rbush_.search(extent);
    +  return goog.array.map(items, function(item) {
    +    return item[4];
    +  });
    +};
    +
    +
    +/**
    + * Calls a callback function with each value in the tree.
    + * If the callback returns a truthy value, this value is returned without
    + * checking the rest of the tree.
    + * @param {function(this: S, T): *} callback Callback.
    + * @param {S=} opt_this The object to use as `this` in `callback`.
    + * @return {*} Callback return value.
    + * @template S
    + */
    +ol.structs.RBush.prototype.forEach = function(callback, opt_this) {
    +  if (goog.DEBUG) {
    +    ++this.readers_;
    +    try {
    +      return this.forEach_(this.getAll(), callback, opt_this);
    +    } finally {
    +      --this.readers_;
    +    }
    +  } else {
    +    return this.forEach_(this.getAll(), callback, opt_this);
    +  }
    +};
    +
    +
    +/**
    + * Calls a callback function with each value in the provided extent.
    + * @param {ol.Extent} extent Extent.
    + * @param {function(this: S, T): *} callback Callback.
    + * @param {S=} opt_this The object to use as `this` in `callback`.
    + * @return {*} Callback return value.
    + * @template S
    + */
    +ol.structs.RBush.prototype.forEachInExtent =
    +    function(extent, callback, opt_this) {
    +  if (goog.DEBUG) {
    +    ++this.readers_;
    +    try {
    +      return this.forEach_(this.getInExtent(extent), callback, opt_this);
    +    } finally {
    +      --this.readers_;
    +    }
    +  } else {
    +    return this.forEach_(this.getInExtent(extent), callback, opt_this);
    +  }
    +};
    +
    +
    +/**
    + * @param {Array.<T>} values Values.
    + * @param {function(this: S, T): *} callback Callback.
    + * @param {S=} opt_this The object to use as `this` in `callback`.
    + * @private
    + * @return {*} Callback return value.
    + * @template S
    + */
    +ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) {
    +  var result;
    +  for (var i = 0, l = values.length; i < l; i++) {
    +    result = callback.call(opt_this, values[i]);
    +    if (result) {
    +      return result;
    +    }
    +  }
    +  return result;
    +};
    +
    +
    +/**
    + * @return {boolean} Is empty.
    + */
    +ol.structs.RBush.prototype.isEmpty = function() {
    +  return goog.object.isEmpty(this.items_);
    +};
    +
    +
    +/**
    + * Remove all values from the RBush.
    + */
    +ol.structs.RBush.prototype.clear = function() {
    +  this.rbush_.clear();
    +  this.items_ = {};
    +};
    +
    +
    +/**
    + * @param {ol.Extent=} opt_extent Extent.
    + * @return {ol.Extent} Extent.
    + */
    +ol.structs.RBush.prototype.getExtent = function(opt_extent) {
    +  // FIXME add getExtent() to rbush
    +  return this.rbush_.data.bbox;
    +};
    diff --git a/mapbox-gl/ol/ol/style.jsdoc b/mapbox-gl/ol/ol/style.jsdoc
    new file mode 100644
    index 0000000000..2386a1007e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style.jsdoc
    @@ -0,0 +1,80 @@
    +/**
    + * Feature styles.
    + *
    + * If no style is defined, the following default style is used:
    + * ```js
    + *  var fill = new ol.style.Fill({
    + *    color: 'rgba(255,255,255,0.4)'
    + *  });
    + *  var stroke = new ol.style.Stroke({
    + *    color: '#3399CC',
    + *    width: 1.25
    + *  });
    + *  var styles = [
    + *    new ol.style.Style({
    + *      image: new ol.style.Circle({
    + *        fill: fill,
    + *        stroke: stroke,
    + *        radius: 5
    + *      }),
    + *      fill: fill,
    + *      stroke: stroke
    + *    })
    + *  ];
    + * ```
    + *
    + * A separate editing style has the following defaults:
    + * ```js
    + *  var white = [255, 255, 255, 1];
    + *  var blue = [0, 153, 255, 1];
    + *  var width = 3;
    + *  styles[ol.geom.GeometryType.POLYGON] = [
    + *    new ol.style.Style({
    + *      fill: new ol.style.Fill({
    + *        color: [255, 255, 255, 0.5]
    + *      })
    + *    })
    + *  ];
    + *  styles[ol.geom.GeometryType.MULTI_POLYGON] =
    + *      styles[ol.geom.GeometryType.POLYGON];
    + *  styles[ol.geom.GeometryType.LINE_STRING] = [
    + *    new ol.style.Style({
    + *      stroke: new ol.style.Stroke({
    + *        color: white,
    + *        width: width + 2
    + *      })
    + *    }),
    + *    new ol.style.Style({
    + *      stroke: new ol.style.Stroke({
    + *        color: blue,
    + *        width: width
    + *      })
    + *    })
    + *  ];
    + *  styles[ol.geom.GeometryType.MULTI_LINE_STRING] =
    + *      styles[ol.geom.GeometryType.LINE_STRING];
    + *  styles[ol.geom.GeometryType.POINT] = [
    + *    new ol.style.Style({
    + *      image: new ol.style.Circle({
    + *        radius: width * 2,
    + *        fill: new ol.style.Fill({
    + *          color: blue
    + *        }),
    + *        stroke: new ol.style.Stroke({
    + *          color: white,
    + *          width: width / 2
    + *        })
    + *      }),
    + *      zIndex: Infinity
    + *    })
    + *  ];
    + *  styles[ol.geom.GeometryType.MULTI_POINT] =
    + *      styles[ol.geom.GeometryType.POINT];
    + *  styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] =
    + *      styles[ol.geom.GeometryType.POLYGON].concat(
    + *          styles[ol.geom.GeometryType.POINT]
    + *      );
    + *```
    + *
    + * @namespace ol.style
    + */
    diff --git a/mapbox-gl/ol/ol/style/atlasmanager.js b/mapbox-gl/ol/ol/style/atlasmanager.js
    new file mode 100644
    index 0000000000..8daea3b279
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/atlasmanager.js
    @@ -0,0 +1,445 @@
    +goog.provide('ol.style.Atlas');
    +goog.provide('ol.style.AtlasManager');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('goog.functions');
    +goog.require('goog.object');
    +goog.require('ol');
    +
    +
    +/**
    + * Provides information for an image inside an atlas manager.
    + * `offsetX` and `offsetY` is the position of the image inside
    + * the atlas image `image` and the position of the hit-detection image
    + * inside the hit-detection atlas image `hitImage`.
    + * @typedef {{offsetX: number, offsetY: number, image: HTMLCanvasElement,
    + *    hitImage: HTMLCanvasElement}}
    + */
    +ol.style.AtlasManagerInfo;
    +
    +
    +
    +/**
    + * Manages the creation of image atlases.
    + *
    + * Images added to this manager will be inserted into an atlas, which
    + * will be used for rendering.
    + * The `size` given in the constructor is the size for the first
    + * atlas. After that, when new atlases are created, they will have
    + * twice the size as the latest atlas (until `maxSize` is reached).
    + *
    + * If an application uses many images or very large images, it is recommended
    + * to set a higher `size` value to avoid the creation of too many atlases.
    + *
    + * @constructor
    + * @struct
    + * @api
    + * @param {olx.style.AtlasManagerOptions=} opt_options Options.
    + */
    +ol.style.AtlasManager = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * The size in pixels of the latest atlas image.
    +   * @private
    +   * @type {number}
    +   */
    +  this.currentSize_ = goog.isDef(options.initialSize) ?
    +      options.initialSize : ol.INITIAL_ATLAS_SIZE;
    +
    +  /**
    +   * The maximum size in pixels of atlas images.
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxSize_ = goog.isDef(options.maxSize) ?
    +      options.maxSize : ol.MAX_ATLAS_SIZE != -1 ?
    +          ol.MAX_ATLAS_SIZE : goog.isDef(ol.WEBGL_MAX_TEXTURE_SIZE) ?
    +              ol.WEBGL_MAX_TEXTURE_SIZE : 2048;
    +
    +  /**
    +   * The size in pixels between images.
    +   * @private
    +   * @type {number}
    +   */
    +  this.space_ = goog.isDef(options.space) ? options.space : 1;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.style.Atlas>}
    +   */
    +  this.atlases_ = [new ol.style.Atlas(this.currentSize_, this.space_)];
    +
    +  /**
    +   * The size in pixels of the latest atlas image for hit-detection images.
    +   * @private
    +   * @type {number}
    +   */
    +  this.currentHitSize_ = this.currentSize_;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.style.Atlas>}
    +   */
    +  this.hitAtlases_ = [new ol.style.Atlas(this.currentHitSize_, this.space_)];
    +};
    +
    +
    +/**
    + * @param {string} id The identifier of the entry to check.
    + * @return {?ol.style.AtlasManagerInfo} The position and atlas image for the
    + *    entry, or `null` if the entry is not part of the atlas manager.
    + */
    +ol.style.AtlasManager.prototype.getInfo = function(id) {
    +  /** @type {?ol.style.AtlasInfo} */
    +  var info = this.getInfo_(this.atlases_, id);
    +
    +  if (goog.isNull(info)) {
    +    return null;
    +  }
    +  /** @type {?ol.style.AtlasInfo} */
    +  var hitInfo = this.getInfo_(this.hitAtlases_, id);
    +  goog.asserts.assert(!goog.isNull(hitInfo));
    +
    +  return this.mergeInfos_(info, hitInfo);
    +};
    +
    +
    +/**
    + * @private
    + * @param {Array.<ol.style.Atlas>} atlases The atlases to search.
    + * @param {string} id The identifier of the entry to check.
    + * @return {?ol.style.AtlasInfo} The position and atlas image for the entry,
    + *    or `null` if the entry is not part of the atlases.
    + */
    +ol.style.AtlasManager.prototype.getInfo_ = function(atlases, id) {
    +  var atlas, info, i, ii;
    +  for (i = 0, ii = atlases.length; i < ii; ++i) {
    +    atlas = atlases[i];
    +    info = atlas.get(id);
    +    if (!goog.isNull(info)) {
    +      return info;
    +    }
    +  }
    +  return null;
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.AtlasInfo} info The info for the real image.
    + * @param {ol.style.AtlasInfo} hitInfo The info for the hit-detection
    + *    image.
    + * @return {?ol.style.AtlasManagerInfo} The position and atlas image for the
    + *    entry, or `null` if the entry is not part of the atlases.
    + */
    +ol.style.AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) {
    +  goog.asserts.assert(info.offsetX === hitInfo.offsetX);
    +  goog.asserts.assert(info.offsetY === hitInfo.offsetY);
    +  return /** @type {ol.style.AtlasManagerInfo} */ ({
    +    offsetX: info.offsetX,
    +    offsetY: info.offsetY,
    +    image: info.image,
    +    hitImage: hitInfo.image
    +  });
    +};
    +
    +
    +/**
    + * Add an image to the atlas manager.
    + *
    + * If an entry for the given id already exists, the entry will
    + * be overridden (but the space on the atlas graphic will not be freed).
    + *
    + * If `renderHitCallback` is provided, the image (or the hit-detection version
    + * of the image) will be rendered into a separate hit-detection atlas image.
    + *
    + * @param {string} id The identifier of the entry to add.
    + * @param {number} width The width.
    + * @param {number} height The height.
    + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
    + *    Called to render the new image onto an atlas image.
    + * @param {function(CanvasRenderingContext2D, number, number)=}
    + *    opt_renderHitCallback Called to render a hit-detection image onto a hit
    + *    detection atlas image.
    + * @param {Object=} opt_this Value to use as `this` when executing
    + *    `renderCallback` and `renderHitCallback`.
    + * @return {?ol.style.AtlasManagerInfo}  The position and atlas image for the
    + *    entry, or `null` if the image is too big.
    + */
    +ol.style.AtlasManager.prototype.add =
    +    function(id, width, height,
    +        renderCallback, opt_renderHitCallback, opt_this) {
    +  if (width + this.space_ > this.maxSize_ ||
    +      height + this.space_ > this.maxSize_) {
    +    return null;
    +  }
    +
    +  /** @type {?ol.style.AtlasInfo} */
    +  var info = this.add_(false,
    +      id, width, height, renderCallback, opt_this);
    +  if (goog.isNull(info)) {
    +    return null;
    +  }
    +
    +  // even if no hit-detection entry is requested, we insert a fake entry into
    +  // the hit-detection atlas, to make sure that the offset is the same for
    +  // the original image and the hit-detection image.
    +  var renderHitCallback = goog.isDef(opt_renderHitCallback) ?
    +      opt_renderHitCallback : goog.functions.NULL;
    +
    +  /** @type {?ol.style.AtlasInfo} */
    +  var hitInfo = this.add_(true,
    +      id, width, height, renderHitCallback, opt_this);
    +  goog.asserts.assert(!goog.isNull(hitInfo));
    +
    +  return this.mergeInfos_(info, hitInfo);
    +};
    +
    +
    +/**
    + * @private
    + * @param {boolean} isHitAtlas If the hit-detection atlases are used.
    + * @param {string} id The identifier of the entry to add.
    + * @param {number} width The width.
    + * @param {number} height The height.
    + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
    + *    Called to render the new image onto an atlas image.
    + * @param {Object=} opt_this Value to use as `this` when executing
    + *    `renderCallback` and `renderHitCallback`.
    + * @return {?ol.style.AtlasInfo}  The position and atlas image for the entry,
    + *    or `null` if the image is too big.
    + */
    +ol.style.AtlasManager.prototype.add_ =
    +    function(isHitAtlas, id, width, height,
    +        renderCallback, opt_this) {
    +  var atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_;
    +  var atlas, info, i, ii;
    +  for (i = 0, ii = atlases.length; i < ii; ++i) {
    +    atlas = atlases[i];
    +    info = atlas.add(id, width, height, renderCallback, opt_this);
    +    if (!goog.isNull(info)) {
    +      return info;
    +    } else if (goog.isNull(info) && i === ii - 1) {
    +      // the entry could not be added to one of the existing atlases,
    +      // create a new atlas that is twice as big and try to add to this one.
    +      var size;
    +      if (isHitAtlas) {
    +        size = Math.min(this.currentHitSize_ * 2, this.maxSize_);
    +        this.currentHitSize_ = size;
    +      } else {
    +        size = Math.min(this.currentSize_ * 2, this.maxSize_);
    +        this.currentSize_ = size;
    +      }
    +      atlas = new ol.style.Atlas(size, this.space_);
    +      atlases.push(atlas);
    +      // run the loop another time
    +      ++ii;
    +    }
    +  }
    +  goog.asserts.fail();
    +};
    +
    +
    +/**
    + * Provides information for an image inside an atlas.
    + * `offsetX` and `offsetY` are the position of the image inside
    + * the atlas image `image`.
    + * @typedef {{offsetX: number, offsetY: number, image: HTMLCanvasElement}}
    + */
    +ol.style.AtlasInfo;
    +
    +
    +
    +/**
    + * This class facilitates the creation of image atlases.
    + *
    + * Images added to an atlas will be rendered onto a single
    + * atlas canvas. The distribution of images on the canvas is
    + * managed with the bin packing algorithm described in:
    + * http://www.blackpawn.com/texts/lightmaps/
    + *
    + * @constructor
    + * @struct
    + * @param {number} size The size in pixels of the sprite image.
    + * @param {number} space The space in pixels between images.
    + *    Because texture coordinates are float values, the edges of
    + *    images might not be completely correct (in a way that the
    + *    edges overlap when being rendered). To avoid this we add a
    + *    padding around each image.
    + */
    +ol.style.Atlas = function(size, space) {
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.space_ = space;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.style.Atlas.Block>}
    +   */
    +  this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}];
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, ol.style.AtlasInfo>}
    +   */
    +  this.entries_ = {};
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = /** @type {HTMLCanvasElement} */
    +      (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +  this.canvas_.width = size;
    +  this.canvas_.height = size;
    +
    +  /**
    +   * @private
    +   * @type {CanvasRenderingContext2D}
    +   */
    +  this.context_ = /** @type {CanvasRenderingContext2D} */
    +      (this.canvas_.getContext('2d'));
    +};
    +
    +
    +/**
    + * @param {string} id The identifier of the entry to check.
    + * @return {?ol.style.AtlasInfo}
    + */
    +ol.style.Atlas.prototype.get = function(id) {
    +  return /** @type {?ol.style.AtlasInfo} */ (
    +      goog.object.get(this.entries_, id, null));
    +};
    +
    +
    +/**
    + * @param {string} id The identifier of the entry to add.
    + * @param {number} width The width.
    + * @param {number} height The height.
    + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback
    + *    Called to render the new image onto an atlas image.
    + * @param {Object=} opt_this Value to use as `this` when executing
    + *    `renderCallback`.
    + * @return {?ol.style.AtlasInfo} The position and atlas image for the entry.
    + */
    +ol.style.Atlas.prototype.add =
    +    function(id, width, height, renderCallback, opt_this) {
    +  var block, i, ii;
    +  for (i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) {
    +    block = this.emptyBlocks_[i];
    +    if (block.width >= width + this.space_ &&
    +        block.height >= height + this.space_) {
    +      // we found a block that is big enough for our entry
    +      var entry = {
    +        offsetX: block.x + this.space_,
    +        offsetY: block.y + this.space_,
    +        image: this.canvas_
    +      };
    +      this.entries_[id] = entry;
    +
    +      // render the image on the atlas image
    +      renderCallback.call(opt_this, this.context_,
    +          block.x + this.space_, block.y + this.space_);
    +
    +      // split the block after the insertion, either horizontally or vertically
    +      this.split_(i, block, width + this.space_, height + this.space_);
    +
    +      return entry;
    +    }
    +  }
    +
    +  // there is no space for the new entry in this atlas
    +  return null;
    +};
    +
    +
    +/**
    + * @private
    + * @param {number} index The index of the block.
    + * @param {ol.style.Atlas.Block} block The block to split.
    + * @param {number} width The width of the entry to insert.
    + * @param {number} height The height of the entry to insert.
    + */
    +ol.style.Atlas.prototype.split_ =
    +    function(index, block, width, height) {
    +  var deltaWidth = block.width - width;
    +  var deltaHeight = block.height - height;
    +
    +  /** @type {ol.style.Atlas.Block} */
    +  var newBlock1;
    +  /** @type {ol.style.Atlas.Block} */
    +  var newBlock2;
    +
    +  if (deltaWidth > deltaHeight) {
    +    // split vertically
    +    // block right of the inserted entry
    +    newBlock1 = {
    +      x: block.x + width,
    +      y: block.y,
    +      width: block.width - width,
    +      height: block.height
    +    };
    +
    +    // block below the inserted entry
    +    newBlock2 = {
    +      x: block.x,
    +      y: block.y + height,
    +      width: width,
    +      height: block.height - height
    +    };
    +    this.updateBlocks_(index, newBlock1, newBlock2);
    +  } else {
    +    // split horizontally
    +    // block right of the inserted entry
    +    newBlock1 = {
    +      x: block.x + width,
    +      y: block.y,
    +      width: block.width - width,
    +      height: height
    +    };
    +
    +    // block below the inserted entry
    +    newBlock2 = {
    +      x: block.x,
    +      y: block.y + height,
    +      width: block.width,
    +      height: block.height - height
    +    };
    +    this.updateBlocks_(index, newBlock1, newBlock2);
    +  }
    +};
    +
    +
    +/**
    + * Remove the old block and insert new blocks at the same array position.
    + * The new blocks are inserted at the same position, so that splitted
    + * blocks (that are potentially smaller) are filled first.
    + * @private
    + * @param {number} index The index of the block to remove.
    + * @param {ol.style.Atlas.Block} newBlock1 The 1st block to add.
    + * @param {ol.style.Atlas.Block} newBlock2 The 2nd block to add.
    + */
    +ol.style.Atlas.prototype.updateBlocks_ =
    +    function(index, newBlock1, newBlock2) {
    +  var args = [index, 1];
    +  if (newBlock1.width > 0 && newBlock1.height > 0) {
    +    args.push(newBlock1);
    +  }
    +  if (newBlock2.width > 0 && newBlock2.height > 0) {
    +    args.push(newBlock2);
    +  }
    +  this.emptyBlocks_.splice.apply(this.emptyBlocks_, args);
    +};
    +
    +
    +/**
    + * @typedef {{x: number, y: number, width: number, height: number}}
    + */
    +ol.style.Atlas.Block;
    diff --git a/mapbox-gl/ol/ol/style/circlestyle.js b/mapbox-gl/ol/ol/style/circlestyle.js
    new file mode 100644
    index 0000000000..d6b459b89f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/circlestyle.js
    @@ -0,0 +1,442 @@
    +goog.provide('ol.style.Circle');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('ol.color');
    +goog.require('ol.has');
    +goog.require('ol.render.canvas');
    +goog.require('ol.structs.IHasChecksum');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Image');
    +goog.require('ol.style.ImageState');
    +goog.require('ol.style.Stroke');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set circle style for vector features.
    + *
    + * @constructor
    + * @param {olx.style.CircleOptions=} opt_options Options.
    + * @extends {ol.style.Image}
    + * @implements {ol.structs.IHasChecksum}
    + * @api
    + */
    +ol.style.Circle = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<string>}
    +   */
    +  this.checksums_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.hitDetectionCanvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Fill}
    +   */
    +  this.fill_ = goog.isDef(options.fill) ? options.fill : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Stroke}
    +   */
    +  this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.radius_ = options.radius;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.origin_ = [0, 0];
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.anchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.size_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.imageSize_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.hitDetectionImageSize_ = null;
    +
    +  this.render_(options.atlasManager);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  var snapToPixel = goog.isDef(options.snapToPixel) ?
    +      options.snapToPixel : true;
    +
    +  goog.base(this, {
    +    opacity: 1,
    +    rotateWithView: false,
    +    rotation: 0,
    +    scale: 1,
    +    snapToPixel: snapToPixel
    +  });
    +
    +};
    +goog.inherits(ol.style.Circle, ol.style.Image);
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Circle.prototype.getAnchor = function() {
    +  return this.anchor_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Fill} Fill style.
    + * @api
    + */
    +ol.style.Circle.prototype.getFill = function() {
    +  return this.fill_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.getHitDetectionImage = function(pixelRatio) {
    +  return this.hitDetectionCanvas_;
    +};
    +
    +
    +/**
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {HTMLCanvasElement} Canvas element.
    + * @api
    + */
    +ol.style.Circle.prototype.getImage = function(pixelRatio) {
    +  return this.canvas_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.getImageState = function() {
    +  return ol.style.ImageState.LOADED;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.getImageSize = function() {
    +  return this.imageSize_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.getHitDetectionImageSize = function() {
    +  return this.hitDetectionImageSize_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Circle.prototype.getOrigin = function() {
    +  return this.origin_;
    +};
    +
    +
    +/**
    + * @return {number} Radius.
    + * @api
    + */
    +ol.style.Circle.prototype.getRadius = function() {
    +  return this.radius_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Circle.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Stroke} Stroke style.
    + * @api
    + */
    +ol.style.Circle.prototype.getStroke = function() {
    +  return this.stroke_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.listenImageChange = goog.nullFunction;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.load = goog.nullFunction;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.unlistenImageChange = goog.nullFunction;
    +
    +
    +/**
    + * @typedef {{strokeStyle: (string|undefined), strokeWidth: number,
    + *   size: number, lineDash: Array.<number>}}
    + */
    +ol.style.Circle.RenderOptions;
    +
    +
    +/**
    + * @private
    + * @param {ol.style.AtlasManager|undefined} atlasManager
    + */
    +ol.style.Circle.prototype.render_ = function(atlasManager) {
    +  var imageSize;
    +  var lineDash = null;
    +  var strokeStyle;
    +  var strokeWidth = 0;
    +
    +  if (!goog.isNull(this.stroke_)) {
    +    strokeStyle = ol.color.asString(this.stroke_.getColor());
    +    strokeWidth = this.stroke_.getWidth();
    +    if (!goog.isDef(strokeWidth)) {
    +      strokeWidth = ol.render.canvas.defaultLineWidth;
    +    }
    +    lineDash = this.stroke_.getLineDash();
    +    if (!ol.has.CANVAS_LINE_DASH) {
    +      lineDash = null;
    +    }
    +  }
    +
    +
    +  var size = 2 * (this.radius_ + strokeWidth) + 1;
    +
    +  /** @type {ol.style.Circle.RenderOptions} */
    +  var renderOptions = {
    +    strokeStyle: strokeStyle,
    +    strokeWidth: strokeWidth,
    +    size: size,
    +    lineDash: lineDash
    +  };
    +
    +  if (!goog.isDef(atlasManager)) {
    +    // no atlas manager is used, create a new canvas
    +    this.canvas_ = /** @type {HTMLCanvasElement} */
    +        (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +    this.canvas_.height = size;
    +    this.canvas_.width = size;
    +
    +    // canvas.width and height are rounded to the closest integer
    +    size = this.canvas_.width;
    +    imageSize = size;
    +
    +    // draw the circle on the canvas
    +    var context = /** @type {CanvasRenderingContext2D} */
    +        (this.canvas_.getContext('2d'));
    +    this.draw_(renderOptions, context, 0, 0);
    +
    +    this.createHitDetectionCanvas_(renderOptions);
    +  } else {
    +    // an atlas manager is used, add the symbol to an atlas
    +    size = Math.round(size);
    +
    +    var hasCustomHitDetectionImage = goog.isNull(this.fill_);
    +    var renderHitDetectionCallback;
    +    if (hasCustomHitDetectionImage) {
    +      // render the hit-detection image into a separate atlas image
    +      renderHitDetectionCallback =
    +          goog.bind(this.drawHitDetectionCanvas_, this, renderOptions);
    +    }
    +
    +    var id = this.getChecksum();
    +    var info = atlasManager.add(
    +        id, size, size, goog.bind(this.draw_, this, renderOptions),
    +        renderHitDetectionCallback);
    +    goog.asserts.assert(info !== null, 'circle radius is too large');
    +
    +    this.canvas_ = info.image;
    +    this.origin_ = [info.offsetX, info.offsetY];
    +    imageSize = info.image.width;
    +
    +    if (hasCustomHitDetectionImage) {
    +      this.hitDetectionCanvas_ = info.hitImage;
    +      this.hitDetectionImageSize_ =
    +          [info.hitImage.width, info.hitImage.height];
    +    } else {
    +      this.hitDetectionCanvas_ = this.canvas_;
    +      this.hitDetectionImageSize_ = [imageSize, imageSize];
    +    }
    +  }
    +
    +  this.anchor_ = [size / 2, size / 2];
    +  this.size_ = [size, size];
    +  this.imageSize_ = [imageSize, imageSize];
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.Circle.RenderOptions} renderOptions
    + * @param {CanvasRenderingContext2D} context
    + * @param {number} x The origin for the symbol (x).
    + * @param {number} y The origin for the symbol (y).
    + */
    +ol.style.Circle.prototype.draw_ = function(renderOptions, context, x, y) {
    +  // reset transform
    +  context.setTransform(1, 0, 0, 1, 0, 0);
    +
    +  // then move to (x, y)
    +  context.translate(x, y);
    +
    +  context.beginPath();
    +  context.arc(
    +      renderOptions.size / 2, renderOptions.size / 2,
    +      this.radius_, 0, 2 * Math.PI, true);
    +
    +  if (!goog.isNull(this.fill_)) {
    +    context.fillStyle = ol.color.asString(this.fill_.getColor());
    +    context.fill();
    +  }
    +  if (!goog.isNull(this.stroke_)) {
    +    context.strokeStyle = renderOptions.strokeStyle;
    +    context.lineWidth = renderOptions.strokeWidth;
    +    if (!goog.isNull(renderOptions.lineDash)) {
    +      context.setLineDash(renderOptions.lineDash);
    +    }
    +    context.stroke();
    +  }
    +  context.closePath();
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.Circle.RenderOptions} renderOptions
    + */
    +ol.style.Circle.prototype.createHitDetectionCanvas_ = function(renderOptions) {
    +  this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
    +  if (!goog.isNull(this.fill_)) {
    +    this.hitDetectionCanvas_ = this.canvas_;
    +    return;
    +  }
    +
    +  // if no fill style is set, create an extra hit-detection image with a
    +  // default fill style
    +  this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
    +      (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +  var canvas = this.hitDetectionCanvas_;
    +
    +  canvas.height = renderOptions.size;
    +  canvas.width = renderOptions.size;
    +
    +  var context = /** @type {CanvasRenderingContext2D} */
    +      (canvas.getContext('2d'));
    +  this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.Circle.RenderOptions} renderOptions
    + * @param {CanvasRenderingContext2D} context
    + * @param {number} x The origin for the symbol (x).
    + * @param {number} y The origin for the symbol (y).
    + */
    +ol.style.Circle.prototype.drawHitDetectionCanvas_ =
    +    function(renderOptions, context, x, y) {
    +  // reset transform
    +  context.setTransform(1, 0, 0, 1, 0, 0);
    +
    +  // then move to (x, y)
    +  context.translate(x, y);
    +
    +  context.beginPath();
    +  context.arc(
    +      renderOptions.size / 2, renderOptions.size / 2,
    +      this.radius_, 0, 2 * Math.PI, true);
    +
    +  context.fillStyle = ol.render.canvas.defaultFillStyle;
    +  context.fill();
    +  if (!goog.isNull(this.stroke_)) {
    +    context.strokeStyle = renderOptions.strokeStyle;
    +    context.lineWidth = renderOptions.strokeWidth;
    +    if (!goog.isNull(renderOptions.lineDash)) {
    +      context.setLineDash(renderOptions.lineDash);
    +    }
    +    context.stroke();
    +  }
    +  context.closePath();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Circle.prototype.getChecksum = function() {
    +  var strokeChecksum = !goog.isNull(this.stroke_) ?
    +      this.stroke_.getChecksum() : '-';
    +  var fillChecksum = !goog.isNull(this.fill_) ?
    +      this.fill_.getChecksum() : '-';
    +
    +  var recalculate = goog.isNull(this.checksums_) ||
    +      (strokeChecksum != this.checksums_[1] ||
    +      fillChecksum != this.checksums_[2] ||
    +      this.radius_ != this.checksums_[3]);
    +
    +  if (recalculate) {
    +    var checksum = 'c' + strokeChecksum + fillChecksum +
    +        (goog.isDef(this.radius_) ? this.radius_.toString() : '-');
    +    this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_];
    +  }
    +
    +  return this.checksums_[0];
    +};
    diff --git a/mapbox-gl/ol/ol/style/fillstyle.js b/mapbox-gl/ol/ol/style/fillstyle.js
    new file mode 100644
    index 0000000000..4fd62d77e7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/fillstyle.js
    @@ -0,0 +1,66 @@
    +goog.provide('ol.style.Fill');
    +
    +goog.require('ol.color');
    +goog.require('ol.structs.IHasChecksum');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set fill style for vector features.
    + *
    + * @constructor
    + * @param {olx.style.FillOptions=} opt_options Options.
    + * @implements {ol.structs.IHasChecksum}
    + * @api
    + */
    +ol.style.Fill = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Color|string}
    +   */
    +  this.color_ = goog.isDef(options.color) ? options.color : null;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * @return {ol.Color|string} Color.
    + * @api
    + */
    +ol.style.Fill.prototype.getColor = function() {
    +  return this.color_;
    +};
    +
    +
    +/**
    + * Set the color.
    + *
    + * @param {ol.Color|string} color Color.
    + * @api
    + */
    +ol.style.Fill.prototype.setColor = function(color) {
    +  this.color_ = color;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Fill.prototype.getChecksum = function() {
    +  if (!goog.isDef(this.checksum_)) {
    +    this.checksum_ = 'f' + (!goog.isNull(this.color_) ?
    +        ol.color.asString(this.color_) : '-');
    +  }
    +
    +  return this.checksum_;
    +};
    diff --git a/mapbox-gl/ol/ol/style/iconstyle.js b/mapbox-gl/ol/ol/style/iconstyle.js
    new file mode 100644
    index 0000000000..c25d58874e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/iconstyle.js
    @@ -0,0 +1,648 @@
    +goog.provide('ol.style.Icon');
    +goog.provide('ol.style.IconAnchorUnits');
    +goog.provide('ol.style.IconImageCache');
    +goog.provide('ol.style.IconOrigin');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('ol.dom');
    +goog.require('ol.style.Image');
    +goog.require('ol.style.ImageState');
    +
    +
    +/**
    + * Icon anchor units. One of 'fraction', 'pixels'.
    + * @enum {string}
    + * @api
    + */
    +ol.style.IconAnchorUnits = {
    +  FRACTION: 'fraction',
    +  PIXELS: 'pixels'
    +};
    +
    +
    +/**
    + * Icon origin. One of 'bottom-left', 'bottom-right', 'top-left', 'top-right'.
    + * @enum {string}
    + * @api
    + */
    +ol.style.IconOrigin = {
    +  BOTTOM_LEFT: 'bottom-left',
    +  BOTTOM_RIGHT: 'bottom-right',
    +  TOP_LEFT: 'top-left',
    +  TOP_RIGHT: 'top-right'
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Set icon style for vector features.
    + *
    + * @constructor
    + * @param {olx.style.IconOptions=} opt_options Options.
    + * @extends {ol.style.Image}
    + * @api
    + */
    +ol.style.Icon = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.anchor_ = goog.isDef(options.anchor) ? options.anchor : [0.5, 0.5];
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.normalizedAnchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.IconOrigin}
    +   */
    +  this.anchorOrigin_ = goog.isDef(options.anchorOrigin) ?
    +      options.anchorOrigin : ol.style.IconOrigin.TOP_LEFT;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.IconAnchorUnits}
    +   */
    +  this.anchorXUnits_ = goog.isDef(options.anchorXUnits) ?
    +      options.anchorXUnits : ol.style.IconAnchorUnits.FRACTION;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.IconAnchorUnits}
    +   */
    +  this.anchorYUnits_ = goog.isDef(options.anchorYUnits) ?
    +      options.anchorYUnits : ol.style.IconAnchorUnits.FRACTION;
    +
    +  /**
    +   * @type {?string}
    +   */
    +  var crossOrigin =
    +      goog.isDef(options.crossOrigin) ? options.crossOrigin : null;
    +
    +  /**
    +   * @type {Image}
    +   */
    +  var image = goog.isDef(options.img) ? options.img : null;
    +
    +  /**
    +   * @type {string|undefined}
    +   */
    +  var src = options.src;
    +
    +  if ((!goog.isDef(src) || src.length === 0) && !goog.isNull(image)) {
    +    src = image.src;
    +  }
    +  goog.asserts.assert(goog.isDef(src) && src.length > 0);
    +
    +  /**
    +   * @type {ol.style.ImageState}
    +   */
    +  var imageState = goog.isDef(options.src) ?
    +      ol.style.ImageState.IDLE : ol.style.ImageState.LOADED;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.IconImage_}
    +   */
    +  this.iconImage_ = ol.style.IconImage_.get(
    +      image, src, crossOrigin, imageState);
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.offset_ = goog.isDef(options.offset) ? options.offset : [0, 0];
    +
    +  /**
    +   * @private
    +   * @type {ol.style.IconOrigin}
    +   */
    +  this.offsetOrigin_ = goog.isDef(options.offsetOrigin) ?
    +      options.offsetOrigin : ol.style.IconOrigin.TOP_LEFT;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.origin_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.size_ = goog.isDef(options.size) ? options.size : null;
    +
    +  /**
    +   * @type {number}
    +   */
    +  var opacity = goog.isDef(options.opacity) ? options.opacity : 1;
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  var rotateWithView = goog.isDef(options.rotateWithView) ?
    +      options.rotateWithView : false;
    +
    +  /**
    +   * @type {number}
    +   */
    +  var rotation = goog.isDef(options.rotation) ? options.rotation : 0;
    +
    +  /**
    +   * @type {number}
    +   */
    +  var scale = goog.isDef(options.scale) ? options.scale : 1;
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  var snapToPixel = goog.isDef(options.snapToPixel) ?
    +      options.snapToPixel : true;
    +
    +  goog.base(this, {
    +    opacity: opacity,
    +    rotation: rotation,
    +    scale: scale,
    +    snapToPixel: snapToPixel,
    +    rotateWithView: rotateWithView
    +  });
    +
    +};
    +goog.inherits(ol.style.Icon, ol.style.Image);
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Icon.prototype.getAnchor = function() {
    +  if (!goog.isNull(this.normalizedAnchor_)) {
    +    return this.normalizedAnchor_;
    +  }
    +  var anchor = this.anchor_;
    +  var size = this.getSize();
    +  if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION ||
    +      this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) {
    +    if (goog.isNull(size)) {
    +      return null;
    +    }
    +    anchor = this.anchor_.slice();
    +    if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) {
    +      anchor[0] *= size[0];
    +    }
    +    if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) {
    +      anchor[1] *= size[1];
    +    }
    +  }
    +
    +  if (this.anchorOrigin_ != ol.style.IconOrigin.TOP_LEFT) {
    +    if (goog.isNull(size)) {
    +      return null;
    +    }
    +    if (anchor === this.anchor_) {
    +      anchor = this.anchor_.slice();
    +    }
    +    if (this.anchorOrigin_ == ol.style.IconOrigin.TOP_RIGHT ||
    +        this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) {
    +      anchor[0] = -anchor[0] + size[0];
    +    }
    +    if (this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT ||
    +        this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) {
    +      anchor[1] = -anchor[1] + size[1];
    +    }
    +  }
    +  this.normalizedAnchor_ = anchor;
    +  return this.normalizedAnchor_;
    +};
    +
    +
    +/**
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {Image} Image element.
    + * @api
    + */
    +ol.style.Icon.prototype.getImage = function(pixelRatio) {
    +  return this.iconImage_.getImage(pixelRatio);
    +};
    +
    +
    +/**
    + * Real Image size used.
    + * @return {ol.Size} Size.
    + */
    +ol.style.Icon.prototype.getImageSize = function() {
    +  return this.iconImage_.getSize();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Icon.prototype.getHitDetectionImageSize = function() {
    +  return this.getImageSize();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Icon.prototype.getImageState = function() {
    +  return this.iconImage_.getImageState();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) {
    +  return this.iconImage_.getHitDetectionImage(pixelRatio);
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Icon.prototype.getOrigin = function() {
    +  if (!goog.isNull(this.origin_)) {
    +    return this.origin_;
    +  }
    +  var offset = this.offset_;
    +
    +  if (this.offsetOrigin_ != ol.style.IconOrigin.TOP_LEFT) {
    +    var size = this.getSize();
    +    var iconImageSize = this.iconImage_.getSize();
    +    if (goog.isNull(size) || goog.isNull(iconImageSize)) {
    +      return null;
    +    }
    +    offset = offset.slice();
    +    if (this.offsetOrigin_ == ol.style.IconOrigin.TOP_RIGHT ||
    +        this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) {
    +      offset[0] = iconImageSize[0] - size[0] - offset[0];
    +    }
    +    if (this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT ||
    +        this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) {
    +      offset[1] = iconImageSize[1] - size[1] - offset[1];
    +    }
    +  }
    +  this.origin_ = offset;
    +  return this.origin_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Image src.
    + * @api
    + */
    +ol.style.Icon.prototype.getSrc = function() {
    +  return this.iconImage_.getSrc();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.Icon.prototype.getSize = function() {
    +  return goog.isNull(this.size_) ? this.iconImage_.getSize() : this.size_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Icon.prototype.listenImageChange = function(listener, thisArg) {
    +  return goog.events.listen(this.iconImage_, goog.events.EventType.CHANGE,
    +      listener, false, thisArg);
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.style.Icon.prototype.load = function() {
    +  this.iconImage_.load();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) {
    +  goog.events.unlisten(this.iconImage_, goog.events.EventType.CHANGE,
    +      listener, false, thisArg);
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @param {Image} image Image.
    + * @param {string|undefined} src Src.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.style.ImageState} imageState Image state.
    + * @extends {goog.events.EventTarget}
    + * @private
    + */
    +ol.style.IconImage_ = function(image, src, crossOrigin, imageState) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {Image|HTMLCanvasElement}
    +   */
    +  this.hitDetectionImage_ = null;
    +
    +  /**
    +   * @private
    +   * @type {Image}
    +   */
    +  this.image_ = goog.isNull(image) ? new Image() : image;
    +
    +  if (!goog.isNull(crossOrigin)) {
    +    this.image_.crossOrigin = crossOrigin;
    +  }
    +
    +  /**
    +   * @private
    +   * @type {Array.<goog.events.Key>}
    +   */
    +  this.imageListenerKeys_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.ImageState}
    +   */
    +  this.imageState_ = imageState;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.size_ = null;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.src_ = src;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.tainting_ = false;
    +
    +};
    +goog.inherits(ol.style.IconImage_, goog.events.EventTarget);
    +
    +
    +/**
    + * @param {Image} image Image.
    + * @param {string} src Src.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.style.ImageState} imageState Image state.
    + * @return {ol.style.IconImage_} Icon image.
    + */
    +ol.style.IconImage_.get = function(image, src, crossOrigin, imageState) {
    +  var iconImageCache = ol.style.IconImageCache.getInstance();
    +  var iconImage = iconImageCache.get(src, crossOrigin);
    +  if (goog.isNull(iconImage)) {
    +    iconImage = new ol.style.IconImage_(image, src, crossOrigin, imageState);
    +    iconImageCache.set(src, crossOrigin, iconImage);
    +  }
    +  return iconImage;
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.style.IconImage_.prototype.determineTainting_ = function() {
    +  var context = ol.dom.createCanvasContext2D(1, 1);
    +  context.drawImage(this.image_, 0, 0);
    +  try {
    +    context.getImageData(0, 0, 1, 1);
    +  } catch (e) {
    +    this.tainting_ = true;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.style.IconImage_.prototype.dispatchChangeEvent_ = function() {
    +  this.dispatchEvent(goog.events.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.style.IconImage_.prototype.handleImageError_ = function() {
    +  this.imageState_ = ol.style.ImageState.ERROR;
    +  this.unlistenImage_();
    +  this.dispatchChangeEvent_();
    +};
    +
    +
    +/**
    + * @private
    + */
    +ol.style.IconImage_.prototype.handleImageLoad_ = function() {
    +  this.imageState_ = ol.style.ImageState.LOADED;
    +  this.size_ = [this.image_.width, this.image_.height];
    +  this.unlistenImage_();
    +  this.determineTainting_();
    +  this.dispatchChangeEvent_();
    +};
    +
    +
    +/**
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {Image} Image element.
    + */
    +ol.style.IconImage_.prototype.getImage = function(pixelRatio) {
    +  return this.image_;
    +};
    +
    +
    +/**
    + * @return {ol.style.ImageState} Image state.
    + */
    +ol.style.IconImage_.prototype.getImageState = function() {
    +  return this.imageState_;
    +};
    +
    +
    +/**
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {Image|HTMLCanvasElement} Image element.
    + */
    +ol.style.IconImage_.prototype.getHitDetectionImage = function(pixelRatio) {
    +  if (goog.isNull(this.hitDetectionImage_)) {
    +    if (this.tainting_) {
    +      var width = this.size_[0];
    +      var height = this.size_[1];
    +      var context = ol.dom.createCanvasContext2D(width, height);
    +      context.fillRect(0, 0, width, height);
    +      this.hitDetectionImage_ = context.canvas;
    +    } else {
    +      this.hitDetectionImage_ = this.image_;
    +    }
    +  }
    +  return this.hitDetectionImage_;
    +};
    +
    +
    +/**
    + * @return {ol.Size} Image size.
    + */
    +ol.style.IconImage_.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Image src.
    + */
    +ol.style.IconImage_.prototype.getSrc = function() {
    +  return this.src_;
    +};
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.style.IconImage_.prototype.load = function() {
    +  if (this.imageState_ == ol.style.ImageState.IDLE) {
    +    goog.asserts.assert(goog.isDef(this.src_));
    +    goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
    +    this.imageState_ = ol.style.ImageState.LOADING;
    +    this.imageListenerKeys_ = [
    +      goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
    +          this.handleImageError_, false, this),
    +      goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
    +          this.handleImageLoad_, false, this)
    +    ];
    +    try {
    +      this.image_.src = this.src_;
    +    } catch (e) {
    +      this.handleImageError_();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Discards event handlers which listen for load completion or errors.
    + *
    + * @private
    + */
    +ol.style.IconImage_.prototype.unlistenImage_ = function() {
    +  goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
    +  goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
    +  this.imageListenerKeys_ = null;
    +};
    +
    +
    +
    +/**
    + * @constructor
    + */
    +ol.style.IconImageCache = function() {
    +
    +  /**
    +   * @type {Object.<string, ol.style.IconImage_>}
    +   * @private
    +   */
    +  this.cache_ = {};
    +
    +  /**
    +   * @type {number}
    +   * @private
    +   */
    +  this.cacheSize_ = 0;
    +
    +  /**
    +   * @const
    +   * @type {number}
    +   * @private
    +   */
    +  this.maxCacheSize_ = 32;
    +};
    +goog.addSingletonGetter(ol.style.IconImageCache);
    +
    +
    +/**
    + * @param {string} src Src.
    + * @param {?string} crossOrigin Cross origin.
    + * @return {string} Cache key.
    + */
    +ol.style.IconImageCache.getKey = function(src, crossOrigin) {
    +  goog.asserts.assert(goog.isDef(crossOrigin));
    +  return crossOrigin + ':' + src;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.style.IconImageCache.prototype.clear = function() {
    +  this.cache_ = {};
    +  this.cacheSize_ = 0;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.style.IconImageCache.prototype.expire = function() {
    +  if (this.cacheSize_ > this.maxCacheSize_) {
    +    var i = 0;
    +    var key, iconImage;
    +    for (key in this.cache_) {
    +      iconImage = this.cache_[key];
    +      if ((i++ & 3) === 0 && !goog.events.hasListener(iconImage)) {
    +        delete this.cache_[key];
    +        --this.cacheSize_;
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {string} src Src.
    + * @param {?string} crossOrigin Cross origin.
    + * @return {ol.style.IconImage_} Icon image.
    + */
    +ol.style.IconImageCache.prototype.get = function(src, crossOrigin) {
    +  var key = ol.style.IconImageCache.getKey(src, crossOrigin);
    +  return key in this.cache_ ? this.cache_[key] : null;
    +};
    +
    +
    +/**
    + * @param {string} src Src.
    + * @param {?string} crossOrigin Cross origin.
    + * @param {ol.style.IconImage_} iconImage Icon image.
    + */
    +ol.style.IconImageCache.prototype.set = function(src, crossOrigin, iconImage) {
    +  var key = ol.style.IconImageCache.getKey(src, crossOrigin);
    +  this.cache_[key] = iconImage;
    +  ++this.cacheSize_;
    +};
    diff --git a/mapbox-gl/ol/ol/style/imagestyle.js b/mapbox-gl/ol/ol/style/imagestyle.js
    new file mode 100644
    index 0000000000..1cb95b241e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/imagestyle.js
    @@ -0,0 +1,243 @@
    +goog.provide('ol.style.Image');
    +goog.provide('ol.style.ImageState');
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.style.ImageState = {
    +  IDLE: 0,
    +  LOADING: 1,
    +  LOADED: 2,
    +  ERROR: 3
    +};
    +
    +
    +/**
    + * @typedef {{opacity: number,
    + *            rotateWithView: boolean,
    + *            rotation: number,
    + *            scale: number,
    + *            snapToPixel: boolean}}
    + */
    +ol.style.ImageOptions;
    +
    +
    +
    +/**
    + * @classdesc
    + * Abstract base class; used for creating subclasses and not instantiated in
    + * apps. Base class for {@link ol.style.Icon} and {@link ol.style.Circle}.
    + *
    + * @constructor
    + * @param {ol.style.ImageOptions} options Options.
    + * @api
    + */
    +ol.style.Image = function(options) {
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.opacity_ = options.opacity;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.rotateWithView_ = options.rotateWithView;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.rotation_ = options.rotation;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.scale_ = options.scale;
    +
    +  /**
    +   * @private
    +   * @type {boolean}
    +   */
    +  this.snapToPixel_ = options.snapToPixel;
    +
    +};
    +
    +
    +/**
    + * @return {number} Opacity.
    + * @api
    + */
    +ol.style.Image.prototype.getOpacity = function() {
    +  return this.opacity_;
    +};
    +
    +
    +/**
    + * @return {boolean} Rotate with map.
    + * @api
    + */
    +ol.style.Image.prototype.getRotateWithView = function() {
    +  return this.rotateWithView_;
    +};
    +
    +
    +/**
    + * @return {number} Rotation.
    + * @api
    + */
    +ol.style.Image.prototype.getRotation = function() {
    +  return this.rotation_;
    +};
    +
    +
    +/**
    + * @return {number} Scale.
    + * @api
    + */
    +ol.style.Image.prototype.getScale = function() {
    +  return this.scale_;
    +};
    +
    +
    +/**
    + * @return {boolean} Snap to pixel?
    + * @api
    + */
    +ol.style.Image.prototype.getSnapToPixel = function() {
    +  return this.snapToPixel_;
    +};
    +
    +
    +/**
    + * @function
    + * @return {Array.<number>} Anchor.
    + */
    +ol.style.Image.prototype.getAnchor = goog.abstractMethod;
    +
    +
    +/**
    + * @function
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element.
    + * @api
    + */
    +ol.style.Image.prototype.getImage = goog.abstractMethod;
    +
    +
    +/**
    + * @param {number} pixelRatio Pixel ratio.
    + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element.
    + */
    +ol.style.Image.prototype.getHitDetectionImage = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.style.ImageState} Image state.
    + */
    +ol.style.Image.prototype.getImageState = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.Size} Image size.
    + */
    +ol.style.Image.prototype.getImageSize = goog.abstractMethod;
    +
    +
    +/**
    + * @return {ol.Size} Size of the hit-detection image.
    + */
    +ol.style.Image.prototype.getHitDetectionImageSize = goog.abstractMethod;
    +
    +
    +/**
    + * @function
    + * @return {Array.<number>} Origin.
    + */
    +ol.style.Image.prototype.getOrigin = goog.abstractMethod;
    +
    +
    +/**
    + * @function
    + * @return {ol.Size} Size.
    + */
    +ol.style.Image.prototype.getSize = goog.abstractMethod;
    +
    +
    +/**
    + * Set the opacity.
    + *
    + * @param {number} opacity Opacity.
    + */
    +ol.style.Image.prototype.setOpacity = function(opacity) {
    +  this.opacity_ = opacity;
    +};
    +
    +
    +/**
    + * Set whether to rotate the style with the view.
    + *
    + * @param {boolean} rotateWithView Rotate with map.
    + */
    +ol.style.Image.prototype.setRotateWithView = function(rotateWithView) {
    +  this.rotateWithView_ = rotateWithView;
    +};
    +
    +
    +/**
    + * Set the rotation.
    + *
    + * @param {number} rotation Rotation.
    + * @api
    + */
    +ol.style.Image.prototype.setRotation = function(rotation) {
    +  this.rotation_ = rotation;
    +};
    +
    +
    +/**
    + * Set the scale.
    + *
    + * @param {number} scale Scale.
    + * @api
    + */
    +ol.style.Image.prototype.setScale = function(scale) {
    +  this.scale_ = scale;
    +};
    +
    +
    +/**
    + * Set whether to snap the image to the closest pixel.
    + *
    + * @param {boolean} snapToPixel Snap to pixel?
    + */
    +ol.style.Image.prototype.setSnapToPixel = function(snapToPixel) {
    +  this.snapToPixel_ = snapToPixel;
    +};
    +
    +
    +/**
    + * @param {function(this: T, goog.events.Event)} listener Listener function.
    + * @param {T} thisArg Value to use as `this` when executing `listener`.
    + * @return {goog.events.Key|undefined} Listener key.
    + * @template T
    + */
    +ol.style.Image.prototype.listenImageChange = goog.abstractMethod;
    +
    +
    +/**
    + * Load not yet loaded URI.
    + */
    +ol.style.Image.prototype.load = goog.abstractMethod;
    +
    +
    +/**
    + * @param {function(this: T, goog.events.Event)} listener Listener function.
    + * @param {T} thisArg Value to use as `this` when executing `listener`.
    + * @template T
    + */
    +ol.style.Image.prototype.unlistenImageChange = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/style/regularshapestyle.js b/mapbox-gl/ol/ol/style/regularshapestyle.js
    new file mode 100644
    index 0000000000..2fa0707ac5
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/regularshapestyle.js
    @@ -0,0 +1,540 @@
    +goog.provide('ol.style.RegularShape');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.dom');
    +goog.require('goog.dom.TagName');
    +goog.require('ol.color');
    +goog.require('ol.has');
    +goog.require('ol.render.canvas');
    +goog.require('ol.structs.IHasChecksum');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Image');
    +goog.require('ol.style.ImageState');
    +goog.require('ol.style.Stroke');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set regular shape style for vector features. The resulting shape will be
    + * a regular polygon when `radius` is provided, or a star when `radius1` and
    + * `radius2` are provided.
    + *
    + * @constructor
    + * @param {olx.style.RegularShapeOptions} options Options.
    + * @extends {ol.style.Image}
    + * @implements {ol.structs.IHasChecksum}
    + * @api
    + */
    +ol.style.RegularShape = function(options) {
    +
    +  goog.asserts.assert(goog.isDef(options.radius) ||
    +      goog.isDef(options.radius1));
    +
    +  /**
    +   * @private
    +   * @type {Array.<string>}
    +   */
    +  this.checksums_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.hitDetectionCanvas_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Fill}
    +   */
    +  this.fill_ = goog.isDef(options.fill) ? options.fill : null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.origin_ = [0, 0];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.points_ = options.points;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.radius_ = /** @type {number} */ (goog.isDef(options.radius) ?
    +      options.radius : options.radius1);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.radius2_ =
    +      goog.isDef(options.radius2) ? options.radius2 : this.radius_;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.angle_ = goog.isDef(options.angle) ? options.angle : 0;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Stroke}
    +   */
    +  this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.anchor_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.size_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.imageSize_ = null;
    +
    +  /**
    +   * @private
    +   * @type {ol.Size}
    +   */
    +  this.hitDetectionImageSize_ = null;
    +
    +  this.render_(options.atlasManager);
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  var snapToPixel = goog.isDef(options.snapToPixel) ?
    +      options.snapToPixel : true;
    +
    +  goog.base(this, {
    +    opacity: 1,
    +    rotateWithView: false,
    +    rotation: goog.isDef(options.rotation) ? options.rotation : 0,
    +    scale: 1,
    +    snapToPixel: snapToPixel
    +  });
    +
    +};
    +goog.inherits(ol.style.RegularShape, ol.style.Image);
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.RegularShape.prototype.getAnchor = function() {
    +  return this.anchor_;
    +};
    +
    +
    +/**
    + * @return {number} Shape's rotation in radians.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getAngle = function() {
    +  return this.angle_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Fill} Fill style.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getFill = function() {
    +  return this.fill_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.getHitDetectionImage = function(pixelRatio) {
    +  return this.hitDetectionCanvas_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.RegularShape.prototype.getImage = function(pixelRatio) {
    +  return this.canvas_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.getImageSize = function() {
    +  return this.imageSize_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.getHitDetectionImageSize = function() {
    +  return this.hitDetectionImageSize_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.getImageState = function() {
    +  return ol.style.ImageState.LOADED;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.RegularShape.prototype.getOrigin = function() {
    +  return this.origin_;
    +};
    +
    +
    +/**
    + * @return {number} Number of points for stars and regular polygons.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getPoints = function() {
    +  return this.points_;
    +};
    +
    +
    +/**
    + * @return {number} Radius.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getRadius = function() {
    +  return this.radius_;
    +};
    +
    +
    +/**
    + * @return {number} Radius2.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getRadius2 = function() {
    +  return this.radius2_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + * @api
    + */
    +ol.style.RegularShape.prototype.getSize = function() {
    +  return this.size_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Stroke} Stroke style.
    + * @api
    + */
    +ol.style.RegularShape.prototype.getStroke = function() {
    +  return this.stroke_;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.listenImageChange = goog.nullFunction;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.load = goog.nullFunction;
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.unlistenImageChange = goog.nullFunction;
    +
    +
    +/**
    + * @typedef {{
    + *   strokeStyle: (string|undefined),
    + *   strokeWidth: number,
    + *   size: number,
    + *   lineCap: string,
    + *   lineDash: Array.<number>,
    + *   lineJoin: string,
    + *   miterLimit: number
    + * }}
    + */
    +ol.style.RegularShape.RenderOptions;
    +
    +
    +/**
    + * @private
    + * @param {ol.style.AtlasManager|undefined} atlasManager
    + */
    +ol.style.RegularShape.prototype.render_ = function(atlasManager) {
    +  var imageSize;
    +  var lineCap = '';
    +  var lineJoin = '';
    +  var miterLimit = 0;
    +  var lineDash = null;
    +  var strokeStyle;
    +  var strokeWidth = 0;
    +
    +  if (!goog.isNull(this.stroke_)) {
    +    strokeStyle = ol.color.asString(this.stroke_.getColor());
    +    strokeWidth = this.stroke_.getWidth();
    +    if (!goog.isDef(strokeWidth)) {
    +      strokeWidth = ol.render.canvas.defaultLineWidth;
    +    }
    +    lineDash = this.stroke_.getLineDash();
    +    if (!ol.has.CANVAS_LINE_DASH) {
    +      lineDash = null;
    +    }
    +    lineJoin = this.stroke_.getLineJoin();
    +    if (!goog.isDef(lineJoin)) {
    +      lineJoin = ol.render.canvas.defaultLineJoin;
    +    }
    +    lineCap = this.stroke_.getLineCap();
    +    if (!goog.isDef(lineCap)) {
    +      lineCap = ol.render.canvas.defaultLineCap;
    +    }
    +    miterLimit = this.stroke_.getMiterLimit();
    +    if (!goog.isDef(miterLimit)) {
    +      miterLimit = ol.render.canvas.defaultMiterLimit;
    +    }
    +  }
    +
    +  var size = 2 * (this.radius_ + strokeWidth) + 1;
    +
    +  /** @type {ol.style.RegularShape.RenderOptions} */
    +  var renderOptions = {
    +    strokeStyle: strokeStyle,
    +    strokeWidth: strokeWidth,
    +    size: size,
    +    lineCap: lineCap,
    +    lineDash: lineDash,
    +    lineJoin: lineJoin,
    +    miterLimit: miterLimit
    +  };
    +
    +  if (!goog.isDef(atlasManager)) {
    +    // no atlas manager is used, create a new canvas
    +    this.canvas_ = /** @type {HTMLCanvasElement} */
    +        (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +
    +    this.canvas_.height = size;
    +    this.canvas_.width = size;
    +
    +    // canvas.width and height are rounded to the closest integer
    +    size = this.canvas_.width;
    +    imageSize = size;
    +
    +    var context = /** @type {CanvasRenderingContext2D} */
    +        (this.canvas_.getContext('2d'));
    +    this.draw_(renderOptions, context, 0, 0);
    +
    +    this.createHitDetectionCanvas_(renderOptions);
    +  } else {
    +    // an atlas manager is used, add the symbol to an atlas
    +    size = Math.round(size);
    +
    +    var hasCustomHitDetectionImage = goog.isNull(this.fill_);
    +    var renderHitDetectionCallback;
    +    if (hasCustomHitDetectionImage) {
    +      // render the hit-detection image into a separate atlas image
    +      renderHitDetectionCallback =
    +          goog.bind(this.drawHitDetectionCanvas_, this, renderOptions);
    +    }
    +
    +    var id = this.getChecksum();
    +    var info = atlasManager.add(
    +        id, size, size, goog.bind(this.draw_, this, renderOptions),
    +        renderHitDetectionCallback);
    +    goog.asserts.assert(!goog.isNull(info), 'shape size is too large');
    +
    +    this.canvas_ = info.image;
    +    this.origin_ = [info.offsetX, info.offsetY];
    +    imageSize = info.image.width;
    +
    +    if (hasCustomHitDetectionImage) {
    +      this.hitDetectionCanvas_ = info.hitImage;
    +      this.hitDetectionImageSize_ =
    +          [info.hitImage.width, info.hitImage.height];
    +    } else {
    +      this.hitDetectionCanvas_ = this.canvas_;
    +      this.hitDetectionImageSize_ = [imageSize, imageSize];
    +    }
    +  }
    +
    +  this.anchor_ = [size / 2, size / 2];
    +  this.size_ = [size, size];
    +  this.imageSize_ = [imageSize, imageSize];
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.RegularShape.RenderOptions} renderOptions
    + * @param {CanvasRenderingContext2D} context
    + * @param {number} x The origin for the symbol (x).
    + * @param {number} y The origin for the symbol (y).
    + */
    +ol.style.RegularShape.prototype.draw_ = function(renderOptions, context, x, y) {
    +  var i, angle0, radiusC;
    +  // reset transform
    +  context.setTransform(1, 0, 0, 1, 0, 0);
    +
    +  // then move to (x, y)
    +  context.translate(x, y);
    +
    +  context.beginPath();
    +  if (this.radius2_ !== this.radius_) {
    +    this.points_ = 2 * this.points_;
    +  }
    +  for (i = 0; i <= this.points_; i++) {
    +    angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
    +    radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
    +    context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
    +                   renderOptions.size / 2 + radiusC * Math.sin(angle0));
    +  }
    +
    +  if (!goog.isNull(this.fill_)) {
    +    context.fillStyle = ol.color.asString(this.fill_.getColor());
    +    context.fill();
    +  }
    +  if (!goog.isNull(this.stroke_)) {
    +    context.strokeStyle = renderOptions.strokeStyle;
    +    context.lineWidth = renderOptions.strokeWidth;
    +    if (!goog.isNull(renderOptions.lineDash)) {
    +      context.setLineDash(renderOptions.lineDash);
    +    }
    +    context.lineCap = renderOptions.lineCap;
    +    context.lineJoin = renderOptions.lineJoin;
    +    context.miterLimit = renderOptions.miterLimit;
    +    context.stroke();
    +  }
    +  context.closePath();
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.RegularShape.RenderOptions} renderOptions
    + */
    +ol.style.RegularShape.prototype.createHitDetectionCanvas_ =
    +    function(renderOptions) {
    +  this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
    +  if (!goog.isNull(this.fill_)) {
    +    this.hitDetectionCanvas_ = this.canvas_;
    +    return;
    +  }
    +
    +  // if no fill style is set, create an extra hit-detection image with a
    +  // default fill style
    +  this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
    +      (goog.dom.createElement(goog.dom.TagName.CANVAS));
    +  var canvas = this.hitDetectionCanvas_;
    +
    +  canvas.height = renderOptions.size;
    +  canvas.width = renderOptions.size;
    +
    +  var context = /** @type {CanvasRenderingContext2D} */
    +      (canvas.getContext('2d'));
    +  this.drawHitDetectionCanvas_(renderOptions, context, 0, 0);
    +};
    +
    +
    +/**
    + * @private
    + * @param {ol.style.RegularShape.RenderOptions} renderOptions
    + * @param {CanvasRenderingContext2D} context
    + * @param {number} x The origin for the symbol (x).
    + * @param {number} y The origin for the symbol (y).
    + */
    +ol.style.RegularShape.prototype.drawHitDetectionCanvas_ =
    +    function(renderOptions, context, x, y) {
    +  // reset transform
    +  context.setTransform(1, 0, 0, 1, 0, 0);
    +
    +  // then move to (x, y)
    +  context.translate(x, y);
    +
    +  context.beginPath();
    +  if (this.radius2_ !== this.radius_) {
    +    this.points_ = 2 * this.points_;
    +  }
    +  var i, radiusC, angle0;
    +  for (i = 0; i <= this.points_; i++) {
    +    angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_;
    +    radiusC = i % 2 === 0 ? this.radius_ : this.radius2_;
    +    context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0),
    +                   renderOptions.size / 2 + radiusC * Math.sin(angle0));
    +  }
    +
    +  context.fillStyle = ol.render.canvas.defaultFillStyle;
    +  context.fill();
    +  if (!goog.isNull(this.stroke_)) {
    +    context.strokeStyle = renderOptions.strokeStyle;
    +    context.lineWidth = renderOptions.strokeWidth;
    +    if (!goog.isNull(renderOptions.lineDash)) {
    +      context.setLineDash(renderOptions.lineDash);
    +    }
    +    context.stroke();
    +  }
    +  context.closePath();
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.RegularShape.prototype.getChecksum = function() {
    +  var strokeChecksum = !goog.isNull(this.stroke_) ?
    +      this.stroke_.getChecksum() : '-';
    +  var fillChecksum = !goog.isNull(this.fill_) ?
    +      this.fill_.getChecksum() : '-';
    +
    +  var recalculate = goog.isNull(this.checksums_) ||
    +      (strokeChecksum != this.checksums_[1] ||
    +      fillChecksum != this.checksums_[2] ||
    +      this.radius_ != this.checksums_[3] ||
    +      this.radius2_ != this.checksums_[4] ||
    +      this.angle_ != this.checksums_[5] ||
    +      this.points_ != this.checksums_[6]);
    +
    +  if (recalculate) {
    +    var checksum = 'r' + strokeChecksum + fillChecksum +
    +        (goog.isDef(this.radius_) ? this.radius_.toString() : '-') +
    +        (goog.isDef(this.radius2_) ? this.radius2_.toString() : '-') +
    +        (goog.isDef(this.angle_) ? this.angle_.toString() : '-') +
    +        (goog.isDef(this.points_) ? this.points_.toString() : '-');
    +    this.checksums_ = [checksum, strokeChecksum, fillChecksum,
    +      this.radius_, this.radius2_, this.angle_, this.points_];
    +  }
    +
    +  return this.checksums_[0];
    +};
    diff --git a/mapbox-gl/ol/ol/style/strokestyle.js b/mapbox-gl/ol/ol/style/strokestyle.js
    new file mode 100644
    index 0000000000..a679df2876
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/strokestyle.js
    @@ -0,0 +1,221 @@
    +goog.provide('ol.style.Stroke');
    +
    +goog.require('goog.crypt');
    +goog.require('goog.crypt.Md5');
    +goog.require('ol.color');
    +goog.require('ol.structs.IHasChecksum');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set stroke style for vector features.
    + * Note that the defaults given are the Canvas defaults, which will be used if
    + * option is not defined. The `get` functions return whatever was entered in
    + * the options; they will not return the default.
    + *
    + * @constructor
    + * @param {olx.style.StrokeOptions=} opt_options Options.
    + * @implements {ol.structs.IHasChecksum}
    + * @api
    + */
    +ol.style.Stroke = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {ol.Color|string}
    +   */
    +  this.color_ = goog.isDef(options.color) ? options.color : null;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.lineCap_ = options.lineCap;
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.lineDash_ = goog.isDef(options.lineDash) ? options.lineDash : null;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.lineJoin_ = options.lineJoin;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.miterLimit_ = options.miterLimit;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.width_ = options.width;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * @return {ol.Color|string} Color.
    + * @api
    + */
    +ol.style.Stroke.prototype.getColor = function() {
    +  return this.color_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Line cap.
    + * @api
    + */
    +ol.style.Stroke.prototype.getLineCap = function() {
    +  return this.lineCap_;
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Line dash.
    + * @api
    + */
    +ol.style.Stroke.prototype.getLineDash = function() {
    +  return this.lineDash_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Line join.
    + * @api
    + */
    +ol.style.Stroke.prototype.getLineJoin = function() {
    +  return this.lineJoin_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} Miter limit.
    + * @api
    + */
    +ol.style.Stroke.prototype.getMiterLimit = function() {
    +  return this.miterLimit_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} Width.
    + * @api
    + */
    +ol.style.Stroke.prototype.getWidth = function() {
    +  return this.width_;
    +};
    +
    +
    +/**
    + * Set the color.
    + *
    + * @param {ol.Color|string} color Color.
    + * @api
    + */
    +ol.style.Stroke.prototype.setColor = function(color) {
    +  this.color_ = color;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * Set the line cap.
    + *
    + * @param {string|undefined} lineCap Line cap.
    + * @api
    + */
    +ol.style.Stroke.prototype.setLineCap = function(lineCap) {
    +  this.lineCap_ = lineCap;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * Set the line dash.
    + *
    + * @param {Array.<number>} lineDash Line dash.
    + * @api
    + */
    +ol.style.Stroke.prototype.setLineDash = function(lineDash) {
    +  this.lineDash_ = lineDash;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * Set the line join.
    + *
    + * @param {string|undefined} lineJoin Line join.
    + * @api
    + */
    +ol.style.Stroke.prototype.setLineJoin = function(lineJoin) {
    +  this.lineJoin_ = lineJoin;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * Set the miter limit.
    + *
    + * @param {number|undefined} miterLimit Miter limit.
    + * @api
    + */
    +ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) {
    +  this.miterLimit_ = miterLimit;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * Set the width.
    + *
    + * @param {number|undefined} width Width.
    + * @api
    + */
    +ol.style.Stroke.prototype.setWidth = function(width) {
    +  this.width_ = width;
    +  this.checksum_ = undefined;
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.style.Stroke.prototype.getChecksum = function() {
    +  if (!goog.isDef(this.checksum_)) {
    +    var raw = 's' +
    +        (!goog.isNull(this.color_) ?
    +            ol.color.asString(this.color_) : '-') + ',' +
    +        (goog.isDef(this.lineCap_) ?
    +            this.lineCap_.toString() : '-') + ',' +
    +        (!goog.isNull(this.lineDash_) ?
    +            this.lineDash_.toString() : '-') + ',' +
    +        (goog.isDef(this.lineJoin_) ?
    +            this.lineJoin_ : '-') + ',' +
    +        (goog.isDef(this.miterLimit_) ?
    +            this.miterLimit_.toString() : '-') + ',' +
    +        (goog.isDef(this.width_) ?
    +            this.width_.toString() : '-');
    +
    +    var md5 = new goog.crypt.Md5();
    +    md5.update(raw);
    +    this.checksum_ = goog.crypt.byteArrayToString(md5.digest());
    +  }
    +
    +  return this.checksum_;
    +};
    diff --git a/mapbox-gl/ol/ol/style/style.js b/mapbox-gl/ol/ol/style/style.js
    new file mode 100644
    index 0000000000..23bd55d93f
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/style.js
    @@ -0,0 +1,363 @@
    +goog.provide('ol.style.Style');
    +goog.provide('ol.style.defaultGeometryFunction');
    +
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('ol.geom.Geometry');
    +goog.require('ol.geom.GeometryType');
    +goog.require('ol.style.Circle');
    +goog.require('ol.style.Fill');
    +goog.require('ol.style.Image');
    +goog.require('ol.style.Stroke');
    +
    +
    +
    +/**
    + * @classdesc
    + * Container for vector feature rendering styles. Any changes made to the style
    + * or its children through `set*()` methods will not take effect until the
    + * feature, layer or FeatureOverlay that uses the style is re-rendered.
    + *
    + * @constructor
    + * @param {olx.style.StyleOptions=} opt_options Style options.
    + * @api
    + */
    +ol.style.Style = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {string|ol.geom.Geometry|ol.style.GeometryFunction}
    +   */
    +  this.geometry_ = null;
    +
    +  /**
    +   * @private
    +   * @type {!ol.style.GeometryFunction}
    +   */
    +  this.geometryFunction_ = ol.style.defaultGeometryFunction;
    +
    +  if (goog.isDef(options.geometry)) {
    +    this.setGeometry(options.geometry);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Fill}
    +   */
    +  this.fill_ = goog.isDef(options.fill) ? options.fill : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Image}
    +   */
    +  this.image_ = goog.isDef(options.image) ? options.image : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Stroke}
    +   */
    +  this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Text}
    +   */
    +  this.text_ = goog.isDef(options.text) ? options.text : null;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.zIndex_ = options.zIndex;
    +
    +};
    +
    +
    +/**
    + * @return {string|ol.geom.Geometry|ol.style.GeometryFunction}
    + * Feature property or geometry or function that returns the geometry that will
    + * be rendered with this style.
    + * @api
    + */
    +ol.style.Style.prototype.getGeometry = function() {
    +  return this.geometry_;
    +};
    +
    +
    +/**
    + * @return {!ol.style.GeometryFunction} Function that is called with a feature
    + * and returns the geometry to render instead of the feature's geometry.
    + * @api
    + */
    +ol.style.Style.prototype.getGeometryFunction = function() {
    +  return this.geometryFunction_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Fill} Fill style.
    + * @api
    + */
    +ol.style.Style.prototype.getFill = function() {
    +  return this.fill_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Image} Image style.
    + * @api
    + */
    +ol.style.Style.prototype.getImage = function() {
    +  return this.image_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Stroke} Stroke style.
    + * @api
    + */
    +ol.style.Style.prototype.getStroke = function() {
    +  return this.stroke_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Text} Text style.
    + * @api
    + */
    +ol.style.Style.prototype.getText = function() {
    +  return this.text_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} ZIndex.
    + * @api
    + */
    +ol.style.Style.prototype.getZIndex = function() {
    +  return this.zIndex_;
    +};
    +
    +
    +/**
    + * Set a geometry that is rendered instead of the feature's geometry.
    + *
    + * @param {string|ol.geom.Geometry|ol.style.GeometryFunction} geometry
    + *     Feature property or geometry or function returning a geometry to render
    + *     for this style.
    + * @api
    + */
    +ol.style.Style.prototype.setGeometry = function(geometry) {
    +  if (goog.isFunction(geometry)) {
    +    this.geometryFunction_ = geometry;
    +  } else if (goog.isString(geometry)) {
    +    this.geometryFunction_ = function(feature) {
    +      var result = feature.get(geometry);
    +      if (goog.isDefAndNotNull(result)) {
    +        goog.asserts.assertInstanceof(result, ol.geom.Geometry);
    +      }
    +      return result;
    +    };
    +  } else if (goog.isNull(geometry)) {
    +    this.geometryFunction_ = ol.style.defaultGeometryFunction;
    +  } else if (goog.isDef(geometry)) {
    +    goog.asserts.assertInstanceof(geometry, ol.geom.Geometry);
    +    this.geometryFunction_ = function() {
    +      return geometry;
    +    };
    +  }
    +  this.geometry_ = geometry;
    +};
    +
    +
    +/**
    + * Set the zIndex.
    + *
    + * @param {number|undefined} zIndex ZIndex.
    + * @api
    + */
    +ol.style.Style.prototype.setZIndex = function(zIndex) {
    +  this.zIndex_ = zIndex;
    +};
    +
    +
    +/**
    + * A function that takes an {@link ol.Feature} and a `{number}` representing
    + * the view's resolution. The function should return an array of
    + * {@link ol.style.Style}. This way e.g. a vector layer can be styled.
    + *
    + * @typedef {function(ol.Feature, number): Array.<ol.style.Style>}
    + * @api
    + */
    +ol.style.StyleFunction;
    +
    +
    +/**
    + * Convert the provided object into a style function.  Functions passed through
    + * unchanged.  Arrays of ol.style.Style or single style objects wrapped in a
    + * new style function.
    + * @param {ol.style.StyleFunction|Array.<ol.style.Style>|ol.style.Style} obj
    + *     A style function, a single style, or an array of styles.
    + * @return {ol.style.StyleFunction} A style function.
    + */
    +ol.style.createStyleFunction = function(obj) {
    +  /**
    +   * @type {ol.style.StyleFunction}
    +   */
    +  var styleFunction;
    +
    +  if (goog.isFunction(obj)) {
    +    styleFunction = /** @type {ol.style.StyleFunction} */ (obj);
    +  } else {
    +    /**
    +     * @type {Array.<ol.style.Style>}
    +     */
    +    var styles;
    +    if (goog.isArray(obj)) {
    +      styles = obj;
    +    } else {
    +      goog.asserts.assertInstanceof(obj, ol.style.Style);
    +      styles = [obj];
    +    }
    +    styleFunction = goog.functions.constant(styles);
    +  }
    +  return styleFunction;
    +};
    +
    +
    +/**
    + * @param {ol.Feature} feature Feature.
    + * @param {number} resolution Resolution.
    + * @return {Array.<ol.style.Style>} Style.
    + */
    +ol.style.defaultStyleFunction = function(feature, resolution) {
    +  var fill = new ol.style.Fill({
    +    color: 'rgba(255,255,255,0.4)'
    +  });
    +  var stroke = new ol.style.Stroke({
    +    color: '#3399CC',
    +    width: 1.25
    +  });
    +  var styles = [
    +    new ol.style.Style({
    +      image: new ol.style.Circle({
    +        fill: fill,
    +        stroke: stroke,
    +        radius: 5
    +      }),
    +      fill: fill,
    +      stroke: stroke
    +    })
    +  ];
    +
    +  // Now that we've run it the first time, replace the function with
    +  // a constant version. We don't use an immediately-invoked function
    +  // and a closure so we don't get an error at script evaluation time in
    +  // browsers that do not support Canvas. (ol.style.Circle does
    +  // canvas.getContext('2d') at construction time, which will cause an.error
    +  // in such browsers.)
    +
    +  /**
    +   * @param {ol.Feature} feature Feature.
    +   * @param {number} resolution Resolution.
    +   * @return {Array.<ol.style.Style>} Style.
    +   */
    +  ol.style.defaultStyleFunction = function(feature, resolution) {
    +    return styles;
    +  };
    +
    +  return styles;
    +};
    +
    +
    +/**
    + * Default styles for editing features.
    + * @return {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} Styles
    + */
    +ol.style.createDefaultEditingStyles = function() {
    +  /** @type {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} */
    +  var styles = {};
    +  var white = [255, 255, 255, 1];
    +  var blue = [0, 153, 255, 1];
    +  var width = 3;
    +  styles[ol.geom.GeometryType.POLYGON] = [
    +    new ol.style.Style({
    +      fill: new ol.style.Fill({
    +        color: [255, 255, 255, 0.5]
    +      })
    +    })
    +  ];
    +  styles[ol.geom.GeometryType.MULTI_POLYGON] =
    +      styles[ol.geom.GeometryType.POLYGON];
    +
    +  styles[ol.geom.GeometryType.LINE_STRING] = [
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: white,
    +        width: width + 2
    +      })
    +    }),
    +    new ol.style.Style({
    +      stroke: new ol.style.Stroke({
    +        color: blue,
    +        width: width
    +      })
    +    })
    +  ];
    +  styles[ol.geom.GeometryType.MULTI_LINE_STRING] =
    +      styles[ol.geom.GeometryType.LINE_STRING];
    +
    +  styles[ol.geom.GeometryType.CIRCLE] =
    +      styles[ol.geom.GeometryType.POLYGON].concat(
    +          styles[ol.geom.GeometryType.LINE_STRING]
    +      );
    +
    +
    +  styles[ol.geom.GeometryType.POINT] = [
    +    new ol.style.Style({
    +      image: new ol.style.Circle({
    +        radius: width * 2,
    +        fill: new ol.style.Fill({
    +          color: blue
    +        }),
    +        stroke: new ol.style.Stroke({
    +          color: white,
    +          width: width / 2
    +        })
    +      }),
    +      zIndex: Infinity
    +    })
    +  ];
    +  styles[ol.geom.GeometryType.MULTI_POINT] =
    +      styles[ol.geom.GeometryType.POINT];
    +
    +  styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] =
    +      styles[ol.geom.GeometryType.POLYGON].concat(
    +          styles[ol.geom.GeometryType.POINT]
    +      );
    +
    +  return styles;
    +};
    +
    +
    +/**
    + * A function that takes an {@link ol.Feature} as argument and returns an
    + * {@link ol.geom.Geometry} that will be rendered and styled for the feature.
    + *
    + * @typedef {function(ol.Feature): (ol.geom.Geometry|undefined)}
    + * @api
    + */
    +ol.style.GeometryFunction;
    +
    +
    +/**
    + * Function that is called with a feature and returns its default geometry.
    + * @param {ol.Feature} feature Feature to get the geometry for.
    + * @return {ol.geom.Geometry|undefined} Geometry to render.
    + */
    +ol.style.defaultGeometryFunction = function(feature) {
    +  goog.asserts.assert(!goog.isNull(feature));
    +  return feature.getGeometry();
    +};
    diff --git a/mapbox-gl/ol/ol/style/textstyle.js b/mapbox-gl/ol/ol/style/textstyle.js
    new file mode 100644
    index 0000000000..65412dff78
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/style/textstyle.js
    @@ -0,0 +1,274 @@
    +goog.provide('ol.style.Text');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set text style for vector features.
    + *
    + * @constructor
    + * @param {olx.style.TextOptions=} opt_options Options.
    + * @api
    + */
    +ol.style.Text = function(opt_options) {
    +
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.font_ = options.font;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.rotation_ = options.rotation;
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.scale_ = options.scale;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.text_ = options.text;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.textAlign_ = options.textAlign;
    +
    +  /**
    +   * @private
    +   * @type {string|undefined}
    +   */
    +  this.textBaseline_ = options.textBaseline;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Fill}
    +   */
    +  this.fill_ = goog.isDef(options.fill) ? options.fill : null;
    +
    +  /**
    +   * @private
    +   * @type {ol.style.Stroke}
    +   */
    +  this.stroke_ = goog.isDef(options.stroke) ? options.stroke : null;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.offsetX_ = goog.isDef(options.offsetX) ? options.offsetX : 0;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.offsetY_ = goog.isDef(options.offsetY) ? options.offsetY : 0;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Font.
    + * @api
    + */
    +ol.style.Text.prototype.getFont = function() {
    +  return this.font_;
    +};
    +
    +
    +/**
    + * @return {number} Horizontal text offset.
    + * @api
    + */
    +ol.style.Text.prototype.getOffsetX = function() {
    +  return this.offsetX_;
    +};
    +
    +
    +/**
    + * @return {number} Vertical text offset.
    + * @api
    + */
    +ol.style.Text.prototype.getOffsetY = function() {
    +  return this.offsetY_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Fill} Fill style.
    + * @api
    + */
    +ol.style.Text.prototype.getFill = function() {
    +  return this.fill_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} Rotation.
    + * @api
    + */
    +ol.style.Text.prototype.getRotation = function() {
    +  return this.rotation_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} Scale.
    + * @api
    + */
    +ol.style.Text.prototype.getScale = function() {
    +  return this.scale_;
    +};
    +
    +
    +/**
    + * @return {ol.style.Stroke} Stroke style.
    + * @api
    + */
    +ol.style.Text.prototype.getStroke = function() {
    +  return this.stroke_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Text.
    + * @api
    + */
    +ol.style.Text.prototype.getText = function() {
    +  return this.text_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Text align.
    + * @api
    + */
    +ol.style.Text.prototype.getTextAlign = function() {
    +  return this.textAlign_;
    +};
    +
    +
    +/**
    + * @return {string|undefined} Text baseline.
    + * @api
    + */
    +ol.style.Text.prototype.getTextBaseline = function() {
    +  return this.textBaseline_;
    +};
    +
    +
    +/**
    + * Set the font.
    + *
    + * @param {string|undefined} font Font.
    + * @api
    + */
    +ol.style.Text.prototype.setFont = function(font) {
    +  this.font_ = font;
    +};
    +
    +
    +/**
    + * Set the x offset.
    + *
    + * @param {number} offsetX Horizontal text offset.
    + */
    +ol.style.Text.prototype.setOffsetX = function(offsetX) {
    +  this.offsetX_ = offsetX;
    +};
    +
    +
    +/**
    + * Set the y offset.
    + *
    + * @param {number} offsetY Vertical text offset.
    + */
    +ol.style.Text.prototype.setOffsetY = function(offsetY) {
    +  this.offsetY_ = offsetY;
    +};
    +
    +
    +/**
    + * Set the fill.
    + *
    + * @param {ol.style.Fill} fill Fill style.
    + * @api
    + */
    +ol.style.Text.prototype.setFill = function(fill) {
    +  this.fill_ = fill;
    +};
    +
    +
    +/**
    + * Set the rotation.
    + *
    + * @param {number|undefined} rotation Rotation.
    + * @api
    + */
    +ol.style.Text.prototype.setRotation = function(rotation) {
    +  this.rotation_ = rotation;
    +};
    +
    +
    +/**
    + * Set the scale.
    + *
    + * @param {number|undefined} scale Scale.
    + * @api
    + */
    +ol.style.Text.prototype.setScale = function(scale) {
    +  this.scale_ = scale;
    +};
    +
    +
    +/**
    + * Set the stroke.
    + *
    + * @param {ol.style.Stroke} stroke Stroke style.
    + * @api
    + */
    +ol.style.Text.prototype.setStroke = function(stroke) {
    +  this.stroke_ = stroke;
    +};
    +
    +
    +/**
    + * Set the text.
    + *
    + * @param {string|undefined} text Text.
    + * @api
    + */
    +ol.style.Text.prototype.setText = function(text) {
    +  this.text_ = text;
    +};
    +
    +
    +/**
    + * Set the text alignment.
    + *
    + * @param {string|undefined} textAlign Text align.
    + * @api
    + */
    +ol.style.Text.prototype.setTextAlign = function(textAlign) {
    +  this.textAlign_ = textAlign;
    +};
    +
    +
    +/**
    + * Set the text baseline.
    + *
    + * @param {string|undefined} textBaseline Text baseline.
    + * @api
    + */
    +ol.style.Text.prototype.setTextBaseline = function(textBaseline) {
    +  this.textBaseline_ = textBaseline;
    +};
    diff --git a/mapbox-gl/ol/ol/tile.js b/mapbox-gl/ol/ol/tile.js
    new file mode 100644
    index 0000000000..ddec563059
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tile.js
    @@ -0,0 +1,95 @@
    +goog.provide('ol.Tile');
    +goog.provide('ol.TileState');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventTarget');
    +goog.require('goog.events.EventType');
    +goog.require('ol.TileCoord');
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.TileState = {
    +  IDLE: 0,
    +  LOADING: 1,
    +  LOADED: 2,
    +  ERROR: 3,
    +  EMPTY: 4
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * Base class for tiles.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.TileState} state State.
    + */
    +ol.Tile = function(tileCoord, state) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @type {ol.TileCoord}
    +   */
    +  this.tileCoord = tileCoord;
    +
    +  /**
    +   * @protected
    +   * @type {ol.TileState}
    +   */
    +  this.state = state;
    +
    +};
    +goog.inherits(ol.Tile, goog.events.EventTarget);
    +
    +
    +/**
    + * @protected
    + */
    +ol.Tile.prototype.changed = function() {
    +  this.dispatchEvent(goog.events.EventType.CHANGE);
    +};
    +
    +
    +/**
    + * @function
    + * @param {Object=} opt_context Object.
    + * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
    + */
    +ol.Tile.prototype.getImage = goog.abstractMethod;
    +
    +
    +/**
    + * @return {string} Key.
    + */
    +ol.Tile.prototype.getKey = function() {
    +  return goog.getUid(this).toString();
    +};
    +
    +
    +/**
    + * @return {ol.TileCoord}
    + * @api
    + */
    +ol.Tile.prototype.getTileCoord = function() {
    +  return this.tileCoord;
    +};
    +
    +
    +/**
    + * @return {ol.TileState} State.
    + */
    +ol.Tile.prototype.getState = function() {
    +  return this.state;
    +};
    +
    +
    +/**
    + * FIXME empty description for jsdoc
    + */
    +ol.Tile.prototype.load = goog.abstractMethod;
    diff --git a/mapbox-gl/ol/ol/tilecache.js b/mapbox-gl/ol/ol/tilecache.js
    new file mode 100644
    index 0000000000..e5af596239
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilecache.js
    @@ -0,0 +1,71 @@
    +goog.provide('ol.TileCache');
    +
    +goog.require('ol');
    +goog.require('ol.TileRange');
    +goog.require('ol.structs.LRUCache');
    +goog.require('ol.tilecoord');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.structs.LRUCache.<ol.Tile>}
    + * @param {number=} opt_highWaterMark High water mark.
    + * @struct
    + */
    +ol.TileCache = function(opt_highWaterMark) {
    +
    +  goog.base(this);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.highWaterMark_ = goog.isDef(opt_highWaterMark) ?
    +      opt_highWaterMark : ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK;
    +
    +};
    +goog.inherits(ol.TileCache, ol.structs.LRUCache);
    +
    +
    +/**
    + * @return {boolean} Can expire cache.
    + */
    +ol.TileCache.prototype.canExpireCache = function() {
    +  return this.getCount() > this.highWaterMark_;
    +};
    +
    +
    +/**
    + * @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
    + */
    +ol.TileCache.prototype.expireCache = function(usedTiles) {
    +  var tile, zKey;
    +  while (this.canExpireCache()) {
    +    tile = /** @type {ol.Tile} */ (this.peekLast());
    +    zKey = tile.tileCoord[0].toString();
    +    if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) {
    +      break;
    +    } else {
    +      this.pop().dispose();
    +    }
    +  }
    +};
    +
    +
    +/**
    + * Remove a tile range from the cache, e.g. to invalidate tiles.
    + * @param {ol.TileRange} tileRange The tile range to prune.
    + */
    +ol.TileCache.prototype.pruneTileRange = function(tileRange) {
    +  var i = this.getCount(),
    +      key;
    +  while (i--) {
    +    key = this.peekLastKey();
    +    if (tileRange.contains(ol.tilecoord.createFromString(key))) {
    +      this.pop().dispose();
    +    } else {
    +      this.get(key);
    +    }
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/tilecoord.js b/mapbox-gl/ol/ol/tilecoord.js
    new file mode 100644
    index 0000000000..25fcaeeba2
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilecoord.js
    @@ -0,0 +1,139 @@
    +goog.provide('ol.TileCoord');
    +goog.provide('ol.tilecoord');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +
    +
    +/**
    + * An array of three numbers representing the location of a tile in a tile
    + * grid. The order is `z`, `x`, and `y`. `z` is the zoom level.
    + * @typedef {Array.<number>} ol.TileCoord
    + * @api
    + */
    +ol.TileCoord;
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.QuadKeyCharCode = {
    +  ZERO: '0'.charCodeAt(0),
    +  ONE: '1'.charCodeAt(0),
    +  TWO: '2'.charCodeAt(0),
    +  THREE: '3'.charCodeAt(0)
    +};
    +
    +
    +/**
    + * @param {string} quadKey Quad key.
    + * @return {ol.TileCoord} Tile coordinate.
    + */
    +ol.tilecoord.createFromQuadKey = function(quadKey) {
    +  var z = quadKey.length, x = 0, y = 0;
    +  var mask = 1 << (z - 1);
    +  var i;
    +  for (i = 0; i < z; ++i) {
    +    switch (quadKey.charCodeAt(i)) {
    +      case ol.QuadKeyCharCode.ONE:
    +        x += mask;
    +        break;
    +      case ol.QuadKeyCharCode.TWO:
    +        y += mask;
    +        break;
    +      case ol.QuadKeyCharCode.THREE:
    +        x += mask;
    +        y += mask;
    +        break;
    +    }
    +    mask >>= 1;
    +  }
    +  return [z, x, y];
    +};
    +
    +
    +/**
    + * @param {string} str String that follows pattern “z/x/y” where x, y and z are
    + *   numbers.
    + * @return {ol.TileCoord} Tile coord.
    + */
    +ol.tilecoord.createFromString = function(str) {
    +  var v = str.split('/');
    +  goog.asserts.assert(v.length === 3);
    +  v = goog.array.map(v, function(e, i, a) {
    +    return parseInt(e, 10);
    +  });
    +  return v;
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {ol.TileCoord=} opt_tileCoord Tile coordinate.
    + * @return {ol.TileCoord} Tile coordinate.
    + */
    +ol.tilecoord.createOrUpdate = function(z, x, y, opt_tileCoord) {
    +  if (goog.isDef(opt_tileCoord)) {
    +    opt_tileCoord[0] = z;
    +    opt_tileCoord[1] = x;
    +    opt_tileCoord[2] = y;
    +    return opt_tileCoord;
    +  } else {
    +    return [z, x, y];
    +  }
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @return {string} Key.
    + */
    +ol.tilecoord.getKeyZXY = function(z, x, y) {
    +  return z + '/' + x + '/' + y;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coord.
    + * @return {number} Hash.
    + */
    +ol.tilecoord.hash = function(tileCoord) {
    +  return (tileCoord[1] << tileCoord[0]) + tileCoord[2];
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coord.
    + * @return {string} Quad key.
    + */
    +ol.tilecoord.quadKey = function(tileCoord) {
    +  var z = tileCoord[0];
    +  var digits = new Array(z);
    +  var mask = 1 << (z - 1);
    +  var i, charCode;
    +  for (i = 0; i < z; ++i) {
    +    charCode = ol.QuadKeyCharCode.ZERO;
    +    if (tileCoord[1] & mask) {
    +      charCode += 1;
    +    }
    +    if (tileCoord[2] & mask) {
    +      charCode += 2;
    +    }
    +    digits[i] = String.fromCharCode(charCode);
    +    mask >>= 1;
    +  }
    +  return digits.join('');
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coord.
    + * @return {string} String.
    + */
    +ol.tilecoord.toString = function(tileCoord) {
    +  return ol.tilecoord.getKeyZXY(tileCoord[0], tileCoord[1], tileCoord[2]);
    +};
    diff --git a/mapbox-gl/ol/ol/tilegrid.jsdoc b/mapbox-gl/ol/ol/tilegrid.jsdoc
    new file mode 100644
    index 0000000000..5f6ed64aaa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilegrid.jsdoc
    @@ -0,0 +1,3 @@
    +/**
    + * @namespace ol.tilegrid
    + */
    diff --git a/mapbox-gl/ol/ol/tilegrid/tilegrid.js b/mapbox-gl/ol/ol/tilegrid/tilegrid.js
    new file mode 100644
    index 0000000000..e24e8e02b4
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilegrid/tilegrid.js
    @@ -0,0 +1,505 @@
    +goog.provide('ol.tilegrid.TileGrid');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.functions');
    +goog.require('ol');
    +goog.require('ol.Coordinate');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileRange');
    +goog.require('ol.array');
    +goog.require('ol.extent');
    +goog.require('ol.extent.Corner');
    +goog.require('ol.proj');
    +goog.require('ol.proj.METERS_PER_UNIT');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.proj.Units');
    +goog.require('ol.tilecoord');
    +
    +
    +
    +/**
    + * @classdesc
    + * Base class for setting the grid pattern for sources accessing tiled-image
    + * servers.
    + *
    + * @constructor
    + * @param {olx.tilegrid.TileGridOptions} options Tile grid options.
    + * @struct
    + * @api stable
    + */
    +ol.tilegrid.TileGrid = function(options) {
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.minZoom = goog.isDef(options.minZoom) ? options.minZoom : 0;
    +
    +  /**
    +   * @private
    +   * @type {!Array.<number>}
    +   */
    +  this.resolutions_ = options.resolutions;
    +  goog.asserts.assert(goog.array.isSorted(this.resolutions_, function(a, b) {
    +    return b - a;
    +  }, true));
    +
    +  /**
    +   * @protected
    +   * @type {number}
    +   */
    +  this.maxZoom = this.resolutions_.length - 1;
    +
    +  /**
    +   * @private
    +   * @type {ol.Coordinate}
    +   */
    +  this.origin_ = goog.isDef(options.origin) ? options.origin : null;
    +
    +  /**
    +   * @private
    +   * @type {Array.<ol.Coordinate>}
    +   */
    +  this.origins_ = null;
    +  if (goog.isDef(options.origins)) {
    +    this.origins_ = options.origins;
    +    goog.asserts.assert(this.origins_.length == this.maxZoom + 1);
    +  }
    +  goog.asserts.assert(
    +      (goog.isNull(this.origin_) && !goog.isNull(this.origins_)) ||
    +      (!goog.isNull(this.origin_) && goog.isNull(this.origins_)));
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.tileSizes_ = null;
    +  if (goog.isDef(options.tileSizes)) {
    +    this.tileSizes_ = options.tileSizes;
    +    goog.asserts.assert(this.tileSizes_.length == this.maxZoom + 1);
    +  }
    +
    +  /**
    +   * @private
    +   * @type {number|undefined}
    +   */
    +  this.tileSize_ = goog.isDef(options.tileSize) ?
    +      options.tileSize :
    +      goog.isNull(this.tileSizes_) ? ol.DEFAULT_TILE_SIZE : undefined;
    +  goog.asserts.assert(
    +      (!goog.isDef(this.tileSize_) && !goog.isNull(this.tileSizes_)) ||
    +      (goog.isDef(this.tileSize_) && goog.isNull(this.tileSizes_)));
    +
    +};
    +
    +
    +/**
    + * @private
    + * @type {ol.TileCoord}
    + */
    +ol.tilegrid.TileGrid.tmpTileCoord_ = [0, 0, 0];
    +
    +
    +/**
    + * Returns the identity function. May be overridden in subclasses.
    + * @param {{extent: (ol.Extent|undefined),
    + *          wrapX: (boolean|undefined)}=} opt_options Options.
    + * @return {function(ol.TileCoord, ol.proj.Projection, ol.TileCoord=):
    + *     ol.TileCoord} Tile coordinate transform.
    + */
    +ol.tilegrid.TileGrid.prototype.createTileCoordTransform =
    +    function(opt_options) {
    +  return goog.functions.identity;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {function(this: T, number, ol.TileRange): boolean} callback Callback.
    + * @param {T=} opt_this The object to use as `this` in `callback`.
    + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object.
    + * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
    + * @return {boolean} Callback succeeded.
    + * @template T
    + */
    +ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange =
    +    function(tileCoord, callback, opt_this, opt_tileRange, opt_extent) {
    +  var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent);
    +  var z = tileCoord[0] - 1;
    +  while (z >= this.minZoom) {
    +    if (callback.call(opt_this, z,
    +        this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange))) {
    +      return true;
    +    }
    +    --z;
    +  }
    +  return false;
    +};
    +
    +
    +/**
    + * @return {number} Max zoom.
    + * @api
    + */
    +ol.tilegrid.TileGrid.prototype.getMaxZoom = function() {
    +  return this.maxZoom;
    +};
    +
    +
    +/**
    + * @return {number} Min zoom.
    + * @api
    + */
    +ol.tilegrid.TileGrid.prototype.getMinZoom = function() {
    +  return this.minZoom;
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @return {ol.Coordinate} Origin.
    + * @api stable
    + */
    +ol.tilegrid.TileGrid.prototype.getOrigin = function(z) {
    +  if (!goog.isNull(this.origin_)) {
    +    return this.origin_;
    +  } else {
    +    goog.asserts.assert(!goog.isNull(this.origins_));
    +    goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom);
    +    return this.origins_[z];
    +  }
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @return {number} Resolution.
    + * @api stable
    + */
    +ol.tilegrid.TileGrid.prototype.getResolution = function(z) {
    +  goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom);
    +  return this.resolutions_[z];
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Resolutions.
    + * @api stable
    + */
    +ol.tilegrid.TileGrid.prototype.getResolutions = function() {
    +  return this.resolutions_;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object.
    + * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
    + * @return {ol.TileRange} Tile range.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordChildTileRange =
    +    function(tileCoord, opt_tileRange, opt_extent) {
    +  if (tileCoord[0] < this.maxZoom) {
    +    var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent);
    +    return this.getTileRangeForExtentAndZ(
    +        tileCoordExtent, tileCoord[0] + 1, opt_tileRange);
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @param {ol.TileRange} tileRange Tile range.
    + * @param {ol.Extent=} opt_extent Temporary ol.Extent object.
    + * @return {ol.Extent} Extent.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileRangeExtent =
    +    function(z, tileRange, opt_extent) {
    +  var origin = this.getOrigin(z);
    +  var resolution = this.getResolution(z);
    +  var tileSize = this.getTileSize(z);
    +  var minX = origin[0] + tileRange.minX * tileSize * resolution;
    +  var maxX = origin[0] + (tileRange.maxX + 1) * tileSize * resolution;
    +  var minY = origin[1] + tileRange.minY * tileSize * resolution;
    +  var maxY = origin[1] + (tileRange.maxY + 1) * tileSize * resolution;
    +  return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} resolution Resolution.
    + * @param {ol.TileRange=} opt_tileRange Temporary tile range object.
    + * @return {ol.TileRange} Tile range.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution =
    +    function(extent, resolution, opt_tileRange) {
    +  var tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_;
    +  this.getTileCoordForXYAndResolution_(
    +      extent[0], extent[1], resolution, false, tileCoord);
    +  var minX = tileCoord[1];
    +  var minY = tileCoord[2];
    +  this.getTileCoordForXYAndResolution_(
    +      extent[2], extent[3], resolution, true, tileCoord);
    +  return ol.TileRange.createOrUpdate(
    +      minX, tileCoord[1], minY, tileCoord[2], opt_tileRange);
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number} z Z.
    + * @param {ol.TileRange=} opt_tileRange Temporary tile range object.
    + * @return {ol.TileRange} Tile range.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ =
    +    function(extent, z, opt_tileRange) {
    +  var resolution = this.getResolution(z);
    +  return this.getTileRangeForExtentAndResolution(
    +      extent, resolution, opt_tileRange);
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @return {ol.Coordinate} Tile center.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) {
    +  var origin = this.getOrigin(tileCoord[0]);
    +  var resolution = this.getResolution(tileCoord[0]);
    +  var tileSize = this.getTileSize(tileCoord[0]);
    +  return [
    +    origin[0] + (tileCoord[1] + 0.5) * tileSize * resolution,
    +    origin[1] + (tileCoord[2] + 0.5) * tileSize * resolution
    +  ];
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {ol.Extent=} opt_extent Temporary extent object.
    + * @return {ol.Extent} Extent.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordExtent =
    +    function(tileCoord, opt_extent) {
    +  var origin = this.getOrigin(tileCoord[0]);
    +  var resolution = this.getResolution(tileCoord[0]);
    +  var tileSize = this.getTileSize(tileCoord[0]);
    +  var minX = origin[0] + tileCoord[1] * tileSize * resolution;
    +  var minY = origin[1] + tileCoord[2] * tileSize * resolution;
    +  var maxX = minX + tileSize * resolution;
    +  var maxY = minY + tileSize * resolution;
    +  return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent);
    +};
    +
    +
    +/**
    + * Get the tile coordinate for the given map coordinate and resolution.  This
    + * method considers that coordinates that intersect tile boundaries should be
    + * assigned the higher tile coordinate.
    + *
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} resolution Resolution.
    + * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object.
    + * @return {ol.TileCoord} Tile coordinate.
    + * @api
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(
    +    coordinate, resolution, opt_tileCoord) {
    +  return this.getTileCoordForXYAndResolution_(
    +      coordinate[0], coordinate[1], resolution, false, opt_tileCoord);
    +};
    +
    +
    +/**
    + * @param {number} x X.
    + * @param {number} y Y.
    + * @param {number} resolution Resolution.
    + * @param {boolean} reverseIntersectionPolicy Instead of letting edge
    + *     intersections go to the higher tile coordinate, let edge intersections
    + *     go to the lower tile coordinate.
    + * @param {ol.TileCoord=} opt_tileCoord Temporary ol.TileCoord object.
    + * @return {ol.TileCoord} Tile coordinate.
    + * @private
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function(
    +    x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) {
    +  var z = this.getZForResolution(resolution);
    +  var scale = resolution / this.getResolution(z);
    +  var origin = this.getOrigin(z);
    +  var tileSize = this.getTileSize(z);
    +
    +  var tileCoordX = scale * (x - origin[0]) / (resolution * tileSize);
    +  var tileCoordY = scale * (y - origin[1]) / (resolution * tileSize);
    +
    +  if (reverseIntersectionPolicy) {
    +    tileCoordX = Math.ceil(tileCoordX) - 1;
    +    tileCoordY = Math.ceil(tileCoordY) - 1;
    +  } else {
    +    tileCoordX = Math.floor(tileCoordX);
    +    tileCoordY = Math.floor(tileCoordY);
    +  }
    +
    +  return ol.tilecoord.createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord);
    +};
    +
    +
    +/**
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {number} z Z.
    + * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object.
    + * @return {ol.TileCoord} Tile coordinate.
    + * @api
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ =
    +    function(coordinate, z, opt_tileCoord) {
    +  var resolution = this.getResolution(z);
    +  return this.getTileCoordForXYAndResolution_(
    +      coordinate[0], coordinate[1], resolution, false, opt_tileCoord);
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @return {number} Tile resolution.
    + */
    +ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) {
    +  goog.asserts.assert(
    +      this.minZoom <= tileCoord[0] && tileCoord[0] <= this.maxZoom);
    +  return this.resolutions_[tileCoord[0]];
    +};
    +
    +
    +/**
    + * @param {number} z Z.
    + * @return {number} Tile size.
    + * @api stable
    + */
    +ol.tilegrid.TileGrid.prototype.getTileSize = function(z) {
    +  if (goog.isDef(this.tileSize_)) {
    +    return this.tileSize_;
    +  } else {
    +    goog.asserts.assert(!goog.isNull(this.tileSizes_));
    +    goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom);
    +    return this.tileSizes_[z];
    +  }
    +};
    +
    +
    +/**
    + * @param {number} resolution Resolution.
    + * @return {number} Z.
    + */
    +ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) {
    +  return ol.array.linearFindNearest(this.resolutions_, resolution, 0);
    +};
    +
    +
    +/**
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {ol.tilegrid.TileGrid} Default tile grid for the passed projection.
    + */
    +ol.tilegrid.getForProjection = function(projection) {
    +  var tileGrid = projection.getDefaultTileGrid();
    +  if (goog.isNull(tileGrid)) {
    +    tileGrid = ol.tilegrid.createForProjection(projection);
    +    projection.setDefaultTileGrid(tileGrid);
    +  }
    +  return tileGrid;
    +};
    +
    +
    +/**
    + * @param {ol.Extent} extent Extent.
    + * @param {number=} opt_maxZoom Maximum zoom level (default is
    + *     ol.DEFAULT_MAX_ZOOM).
    + * @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
    + * @param {ol.extent.Corner=} opt_corner Extent corner (default is
    + *     ol.extent.Corner.BOTTOM_LEFT).
    + * @return {ol.tilegrid.TileGrid} TileGrid instance.
    + */
    +ol.tilegrid.createForExtent =
    +    function(extent, opt_maxZoom, opt_tileSize, opt_corner) {
    +  var tileSize = goog.isDef(opt_tileSize) ?
    +      opt_tileSize : ol.DEFAULT_TILE_SIZE;
    +
    +  var corner = goog.isDef(opt_corner) ?
    +      opt_corner : ol.extent.Corner.BOTTOM_LEFT;
    +
    +  var resolutions = ol.tilegrid.resolutionsFromExtent(
    +      extent, opt_maxZoom, tileSize);
    +
    +  return new ol.tilegrid.TileGrid({
    +    origin: ol.extent.getCorner(extent, corner),
    +    resolutions: resolutions,
    +    tileSize: tileSize
    +  });
    +};
    +
    +
    +/**
    + * Create a resolutions array from an extent.  A zoom factor of 2 is assumed.
    + * @param {ol.Extent} extent Extent.
    + * @param {number=} opt_maxZoom Maximum zoom level (default is
    + *     ol.DEFAULT_MAX_ZOOM).
    + * @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
    + * @return {!Array.<number>} Resolutions array.
    + */
    +ol.tilegrid.resolutionsFromExtent =
    +    function(extent, opt_maxZoom, opt_tileSize) {
    +  var maxZoom = goog.isDef(opt_maxZoom) ?
    +      opt_maxZoom : ol.DEFAULT_MAX_ZOOM;
    +
    +  var height = ol.extent.getHeight(extent);
    +  var width = ol.extent.getWidth(extent);
    +
    +  var tileSize = goog.isDef(opt_tileSize) ?
    +      opt_tileSize : ol.DEFAULT_TILE_SIZE;
    +  var maxResolution = Math.max(
    +      width / tileSize, height / tileSize);
    +
    +  var length = maxZoom + 1;
    +  var resolutions = new Array(length);
    +  for (var z = 0; z < length; ++z) {
    +    resolutions[z] = maxResolution / Math.pow(2, z);
    +  }
    +  return resolutions;
    +};
    +
    +
    +/**
    + * @param {ol.proj.ProjectionLike} projection Projection.
    + * @param {number=} opt_maxZoom Maximum zoom level (default is
    + *     ol.DEFAULT_MAX_ZOOM).
    + * @param {number=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE).
    + * @param {ol.extent.Corner=} opt_corner Extent corner (default is
    + *     ol.extent.Corner.BOTTOM_LEFT).
    + * @return {ol.tilegrid.TileGrid} TileGrid instance.
    + */
    +ol.tilegrid.createForProjection =
    +    function(projection, opt_maxZoom, opt_tileSize, opt_corner) {
    +  var extent = ol.tilegrid.extentFromProjection(projection);
    +  return ol.tilegrid.createForExtent(
    +      extent, opt_maxZoom, opt_tileSize, opt_corner);
    +};
    +
    +
    +/**
    + * Generate a tile grid extent from a projection.  If the projection has an
    + * extent, it is used.  If not, a global extent is assumed.
    + * @param {ol.proj.ProjectionLike} projection Projection.
    + * @return {ol.Extent} Extent.
    + */
    +ol.tilegrid.extentFromProjection = function(projection) {
    +  projection = ol.proj.get(projection);
    +  var extent = projection.getExtent();
    +  if (goog.isNull(extent)) {
    +    var half = 180 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
    +        projection.getMetersPerUnit();
    +    extent = ol.extent.createOrUpdate(-half, -half, half, half);
    +  }
    +  return extent;
    +};
    diff --git a/mapbox-gl/ol/ol/tilegrid/wmtstilegrid.js b/mapbox-gl/ol/ol/tilegrid/wmtstilegrid.js
    new file mode 100644
    index 0000000000..228d25715a
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilegrid/wmtstilegrid.js
    @@ -0,0 +1,123 @@
    +goog.provide('ol.tilegrid.WMTS');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol.proj');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set the grid pattern for sources accessing WMTS tiled-image servers.
    + *
    + * @constructor
    + * @extends {ol.tilegrid.TileGrid}
    + * @param {olx.tilegrid.WMTSOptions} options WMTS options.
    + * @struct
    + * @api
    + */
    +ol.tilegrid.WMTS = function(options) {
    +
    +  goog.asserts.assert(
    +      options.resolutions.length == options.matrixIds.length);
    +
    +  /**
    +   * @private
    +   * @type {!Array.<string>}
    +   */
    +  this.matrixIds_ = options.matrixIds;
    +  // FIXME: should the matrixIds become optionnal?
    +
    +  goog.base(this, {
    +    origin: options.origin,
    +    origins: options.origins,
    +    resolutions: options.resolutions,
    +    tileSize: options.tileSize,
    +    tileSizes: options.tileSizes
    +  });
    +
    +};
    +goog.inherits(ol.tilegrid.WMTS, ol.tilegrid.TileGrid);
    +
    +
    +/**
    + * @param {number} z Z.
    + * @return {string} MatrixId..
    + */
    +ol.tilegrid.WMTS.prototype.getMatrixId = function(z) {
    +  goog.asserts.assert(0 <= z && z < this.matrixIds_.length);
    +  return this.matrixIds_[z];
    +};
    +
    +
    +/**
    + * @return {Array.<string>} MatrixIds.
    + * @api
    + */
    +ol.tilegrid.WMTS.prototype.getMatrixIds = function() {
    +  return this.matrixIds_;
    +};
    +
    +
    +/**
    + * @param {Object} matrixSet An object representing a matrixSet in the
    + *     capabilities document.
    + * @return {ol.tilegrid.WMTS} WMTS tileGrid instance.
    + * @api
    + */
    +ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet =
    +    function(matrixSet) {
    +
    +  /** @type {!Array.<number>} */
    +  var resolutions = [];
    +  /** @type {!Array.<string>} */
    +  var matrixIds = [];
    +  /** @type {!Array.<ol.Coordinate>} */
    +  var origins = [];
    +  /** @type {!Array.<number>} */
    +  var tileSizes = [];
    +
    +  var supportedCRSPropName = 'SupportedCRS';
    +  var matrixIdsPropName = 'TileMatrix';
    +  var identifierPropName = 'Identifier';
    +  var scaleDenominatorPropName = 'ScaleDenominator';
    +  var topLeftCornerPropName = 'TopLeftCorner';
    +  var tileWidthPropName = 'TileWidth';
    +  var tileHeightPropName = 'TileHeight';
    +
    +  var projection;
    +  projection = ol.proj.get(matrixSet[supportedCRSPropName].replace(
    +      /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'));
    +  var metersPerUnit = projection.getMetersPerUnit();
    +  // swap origin x and y coordinates if axis orientation is lat/long
    +  var switchOriginXY = projection.getAxisOrientation().substr(0, 2) == 'ne';
    +
    +  goog.array.sort(matrixSet[matrixIdsPropName], function(a, b) {
    +    return b[scaleDenominatorPropName] - a[scaleDenominatorPropName];
    +  });
    +
    +  goog.array.forEach(matrixSet[matrixIdsPropName],
    +      function(elt, index, array) {
    +        matrixIds.push(elt[identifierPropName]);
    +        if (switchOriginXY) {
    +          origins.push([elt[topLeftCornerPropName][1],
    +            elt[topLeftCornerPropName][0]]);
    +        } else {
    +          origins.push(elt[topLeftCornerPropName]);
    +        }
    +        resolutions.push(elt[scaleDenominatorPropName] * 0.28E-3 /
    +            metersPerUnit);
    +        var tileWidth = elt[tileWidthPropName];
    +        var tileHeight = elt[tileHeightPropName];
    +        goog.asserts.assert(tileWidth == tileHeight);
    +        tileSizes.push(tileWidth);
    +      });
    +
    +  return new ol.tilegrid.WMTS({
    +    origins: origins,
    +    resolutions: resolutions,
    +    matrixIds: matrixIds,
    +    tileSizes: tileSizes
    +  });
    +};
    diff --git a/mapbox-gl/ol/ol/tilegrid/xyztilegrid.js b/mapbox-gl/ol/ol/tilegrid/xyztilegrid.js
    new file mode 100644
    index 0000000000..399df2b860
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilegrid/xyztilegrid.js
    @@ -0,0 +1,131 @@
    +goog.provide('ol.tilegrid.XYZ');
    +
    +goog.require('goog.math');
    +goog.require('ol');
    +goog.require('ol.TileCoord');
    +goog.require('ol.TileRange');
    +goog.require('ol.extent');
    +goog.require('ol.extent.Corner');
    +goog.require('ol.proj');
    +goog.require('ol.proj.EPSG3857');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set the grid pattern for sources accessing XYZ tiled-image servers.
    + *
    + * @constructor
    + * @extends {ol.tilegrid.TileGrid}
    + * @param {olx.tilegrid.XYZOptions} options XYZ options.
    + * @struct
    + * @api
    + */
    +ol.tilegrid.XYZ = function(options) {
    +  var extent = goog.isDef(options.extent) ?
    +      options.extent : ol.proj.EPSG3857.EXTENT;
    +  var resolutions = ol.tilegrid.resolutionsFromExtent(
    +      extent, options.maxZoom, options.tileSize);
    +
    +  goog.base(this, {
    +    minZoom: options.minZoom,
    +    origin: ol.extent.getCorner(extent, ol.extent.Corner.TOP_LEFT),
    +    resolutions: resolutions,
    +    tileSize: options.tileSize
    +  });
    +
    +};
    +goog.inherits(ol.tilegrid.XYZ, ol.tilegrid.TileGrid);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.tilegrid.XYZ.prototype.createTileCoordTransform = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +  var minZ = this.minZoom;
    +  var maxZ = this.maxZoom;
    +  var wrapX = goog.isDef(options.wrapX) ? options.wrapX : true;
    +  /** @type {Array.<ol.TileRange>} */
    +  var tileRangeByZ = null;
    +  if (goog.isDef(options.extent)) {
    +    tileRangeByZ = new Array(maxZ + 1);
    +    var z;
    +    for (z = 0; z <= maxZ; ++z) {
    +      if (z < minZ) {
    +        tileRangeByZ[z] = null;
    +      } else {
    +        tileRangeByZ[z] = this.getTileRangeForExtentAndZ(options.extent, z);
    +      }
    +    }
    +  }
    +  return (
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile coordinate.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @param {ol.TileCoord=} opt_tileCoord Destination tile coordinate.
    +       * @return {ol.TileCoord} Tile coordinate.
    +       */
    +      function(tileCoord, projection, opt_tileCoord) {
    +        var z = tileCoord[0];
    +        if (z < minZ || maxZ < z) {
    +          return null;
    +        }
    +        var n = Math.pow(2, z);
    +        var x = tileCoord[1];
    +        if (wrapX) {
    +          x = goog.math.modulo(x, n);
    +        } else if (x < 0 || n <= x) {
    +          return null;
    +        }
    +        var y = tileCoord[2];
    +        if (y < -n || -1 < y) {
    +          return null;
    +        }
    +        if (!goog.isNull(tileRangeByZ)) {
    +          if (!tileRangeByZ[z].containsXY(x, y)) {
    +            return null;
    +          }
    +        }
    +        return ol.tilecoord.createOrUpdate(z, x, -y - 1, opt_tileCoord);
    +      });
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.tilegrid.XYZ.prototype.getTileCoordChildTileRange =
    +    function(tileCoord, opt_tileRange) {
    +  if (tileCoord[0] < this.maxZoom) {
    +    var doubleX = 2 * tileCoord[1];
    +    var doubleY = 2 * tileCoord[2];
    +    return ol.TileRange.createOrUpdate(
    +        doubleX, doubleX + 1,
    +        doubleY, doubleY + 1,
    +        opt_tileRange);
    +  } else {
    +    return null;
    +  }
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.tilegrid.XYZ.prototype.forEachTileCoordParentTileRange =
    +    function(tileCoord, callback, opt_this, opt_tileRange) {
    +  var tileRange = ol.TileRange.createOrUpdate(
    +      0, tileCoord[1], 0, tileCoord[2], opt_tileRange);
    +  var z;
    +  for (z = tileCoord[0] - 1; z >= this.minZoom; --z) {
    +    tileRange.minX = tileRange.maxX >>= 1;
    +    tileRange.minY = tileRange.maxY >>= 1;
    +    if (callback.call(opt_this, z, tileRange)) {
    +      return true;
    +    }
    +  }
    +  return false;
    +};
    diff --git a/mapbox-gl/ol/ol/tilegrid/zoomifytilegrid.js b/mapbox-gl/ol/ol/tilegrid/zoomifytilegrid.js
    new file mode 100644
    index 0000000000..379e23c03e
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilegrid/zoomifytilegrid.js
    @@ -0,0 +1,79 @@
    +goog.provide('ol.tilegrid.Zoomify');
    +
    +goog.require('goog.math');
    +goog.require('ol.TileCoord');
    +goog.require('ol.proj');
    +goog.require('ol.tilecoord');
    +goog.require('ol.tilegrid.TileGrid');
    +
    +
    +
    +/**
    + * @classdesc
    + * Set the grid pattern for sources accessing Zoomify tiled-image servers.
    + *
    + * @constructor
    + * @extends {ol.tilegrid.TileGrid}
    + * @param {olx.tilegrid.ZoomifyOptions=} opt_options Options.
    + * @api
    + */
    +ol.tilegrid.Zoomify = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : options;
    +  goog.base(this, {
    +    origin: [0, 0],
    +    resolutions: options.resolutions
    +  });
    +
    +};
    +goog.inherits(ol.tilegrid.Zoomify, ol.tilegrid.TileGrid);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.tilegrid.Zoomify.prototype.createTileCoordTransform = function(opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +  var minZ = this.minZoom;
    +  var maxZ = this.maxZoom;
    +  /** @type {Array.<ol.TileRange>} */
    +  var tileRangeByZ = null;
    +  if (goog.isDef(options.extent)) {
    +    tileRangeByZ = new Array(maxZ + 1);
    +    var z;
    +    for (z = 0; z <= maxZ; ++z) {
    +      if (z < minZ) {
    +        tileRangeByZ[z] = null;
    +      } else {
    +        tileRangeByZ[z] = this.getTileRangeForExtentAndZ(options.extent, z);
    +      }
    +    }
    +  }
    +  return (
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile coordinate.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @param {ol.TileCoord=} opt_tileCoord Destination tile coordinate.
    +       * @return {ol.TileCoord} Tile coordinate.
    +       */
    +      function(tileCoord, projection, opt_tileCoord) {
    +        var z = tileCoord[0];
    +        if (z < minZ || maxZ < z) {
    +          return null;
    +        }
    +        var n = Math.pow(2, z);
    +        var x = tileCoord[1];
    +        if (x < 0 || n <= x) {
    +          return null;
    +        }
    +        var y = tileCoord[2];
    +        if (y < -n || -1 < y) {
    +          return null;
    +        }
    +        if (!goog.isNull(tileRangeByZ)) {
    +          if (!tileRangeByZ[z].containsXY(x, -y - 1)) {
    +            return null;
    +          }
    +        }
    +        return ol.tilecoord.createOrUpdate(z, x, -y - 1, opt_tileCoord);
    +      });
    +};
    diff --git a/mapbox-gl/ol/ol/tileloadfunction.js b/mapbox-gl/ol/ol/tileloadfunction.js
    new file mode 100644
    index 0000000000..887eda62f9
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tileloadfunction.js
    @@ -0,0 +1,11 @@
    +goog.provide('ol.TileLoadFunctionType');
    +
    +
    +/**
    + * A function that takes an {@link ol.ImageTile} for the image tile and a
    + * `{string}` for the src as arguments.
    + *
    + * @typedef {function(ol.ImageTile, string)}
    + * @api
    + */
    +ol.TileLoadFunctionType;
    diff --git a/mapbox-gl/ol/ol/tilequeue.js b/mapbox-gl/ol/ol/tilequeue.js
    new file mode 100644
    index 0000000000..68da9ed5fa
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilequeue.js
    @@ -0,0 +1,100 @@
    +goog.provide('ol.TilePriorityFunction');
    +goog.provide('ol.TileQueue');
    +
    +goog.require('goog.events');
    +goog.require('goog.events.EventType');
    +goog.require('ol.Coordinate');
    +goog.require('ol.TileState');
    +goog.require('ol.structs.PriorityQueue');
    +
    +
    +/**
    + * @typedef {function(ol.Tile, string, ol.Coordinate, number): number}
    + */
    +ol.TilePriorityFunction;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.structs.PriorityQueue.<Array>}
    + * @param {ol.TilePriorityFunction} tilePriorityFunction
    + *     Tile priority function.
    + * @param {function(): ?} tileChangeCallback
    + *     Function called on each tile change event.
    + * @struct
    + */
    +ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) {
    +
    +  goog.base(
    +      this,
    +      /**
    +       * @param {Array} element Element.
    +       * @return {number} Priority.
    +       */
    +      function(element) {
    +        return tilePriorityFunction.apply(null, element);
    +      },
    +      /**
    +       * @param {Array} element Element.
    +       * @return {string} Key.
    +       */
    +      function(element) {
    +        return /** @type {ol.Tile} */ (element[0]).getKey();
    +      });
    +
    +  /**
    +   * @private
    +   * @type {function(): ?}
    +   */
    +  this.tileChangeCallback_ = tileChangeCallback;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.tilesLoading_ = 0;
    +
    +};
    +goog.inherits(ol.TileQueue, ol.structs.PriorityQueue);
    +
    +
    +/**
    + * @return {number} Number of tiles loading.
    + */
    +ol.TileQueue.prototype.getTilesLoading = function() {
    +  return this.tilesLoading_;
    +};
    +
    +
    +/**
    + * @param {goog.events.Event} event Event.
    + * @protected
    + */
    +ol.TileQueue.prototype.handleTileChange = function(event) {
    +  var tile = /** @type {ol.Tile} */ (event.target);
    +  var state = tile.getState();
    +  if (state === ol.TileState.LOADED || state === ol.TileState.ERROR ||
    +      state === ol.TileState.EMPTY) {
    +    --this.tilesLoading_;
    +    this.tileChangeCallback_();
    +  }
    +};
    +
    +
    +/**
    + * @param {number} maxTotalLoading Maximum number tiles to load simultaneously.
    + * @param {number} maxNewLoads Maximum number of new tiles to load.
    + */
    +ol.TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) {
    +  var newLoads = Math.min(
    +      maxTotalLoading - this.getTilesLoading(), maxNewLoads, this.getCount());
    +  var i, tile;
    +  for (i = 0; i < newLoads; ++i) {
    +    tile = /** @type {ol.Tile} */ (this.dequeue()[0]);
    +    goog.events.listen(tile, goog.events.EventType.CHANGE,
    +        this.handleTileChange, false, this);
    +    tile.load();
    +  }
    +  this.tilesLoading_ += newLoads;
    +};
    diff --git a/mapbox-gl/ol/ol/tilerange.js b/mapbox-gl/ol/ol/tilerange.js
    new file mode 100644
    index 0000000000..2a2b51b61d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tilerange.js
    @@ -0,0 +1,184 @@
    +goog.provide('ol.TileRange');
    +
    +goog.require('goog.asserts');
    +goog.require('ol.Size');
    +goog.require('ol.TileCoord');
    +
    +
    +
    +/**
    + * A representation of a contiguous block of tiles.  A tile range is specified
    + * by its min/max tile coordinates and is inclusive of coordinates.
    + *
    + * @constructor
    + * @param {number} minX Minimum X.
    + * @param {number} maxX Maximum X.
    + * @param {number} minY Minimum Y.
    + * @param {number} maxY Maximum Y.
    + * @struct
    + */
    +ol.TileRange = function(minX, maxX, minY, maxY) {
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.minX = minX;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.maxX = maxX;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.minY = minY;
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.maxY = maxY;
    +
    +};
    +
    +
    +/**
    + * @param {...ol.TileCoord} var_args Tile coordinates.
    + * @return {!ol.TileRange} Bounding tile box.
    + */
    +ol.TileRange.boundingTileRange = function(var_args) {
    +  var tileCoord0 = /** @type {ol.TileCoord} */ (arguments[0]);
    +  var tileCoord0Z = tileCoord0[0];
    +  var tileCoord0X = tileCoord0[1];
    +  var tileCoord0Y = tileCoord0[2];
    +  var tileRange = new ol.TileRange(tileCoord0X, tileCoord0X,
    +                                   tileCoord0Y, tileCoord0Y);
    +  var i, ii, tileCoord, tileCoordX, tileCoordY, tileCoordZ;
    +  for (i = 1, ii = arguments.length; i < ii; ++i) {
    +    tileCoord = /** @type {ol.TileCoord} */ (arguments[i]);
    +    tileCoordZ = tileCoord[0];
    +    tileCoordX = tileCoord[1];
    +    tileCoordY = tileCoord[2];
    +    goog.asserts.assert(tileCoordZ == tileCoord0Z);
    +    tileRange.minX = Math.min(tileRange.minX, tileCoordX);
    +    tileRange.maxX = Math.max(tileRange.maxX, tileCoordX);
    +    tileRange.minY = Math.min(tileRange.minY, tileCoordY);
    +    tileRange.maxY = Math.max(tileRange.maxY, tileCoordY);
    +  }
    +  return tileRange;
    +};
    +
    +
    +/**
    + * @param {number} minX Minimum X.
    + * @param {number} maxX Maximum X.
    + * @param {number} minY Minimum Y.
    + * @param {number} maxY Maximum Y.
    + * @param {ol.TileRange|undefined} tileRange TileRange.
    + * @return {ol.TileRange} Tile range.
    + */
    +ol.TileRange.createOrUpdate = function(minX, maxX, minY, maxY, tileRange) {
    +  if (goog.isDef(tileRange)) {
    +    tileRange.minX = minX;
    +    tileRange.maxX = maxX;
    +    tileRange.minY = minY;
    +    tileRange.maxY = maxY;
    +    return tileRange;
    +  } else {
    +    return new ol.TileRange(minX, maxX, minY, maxY);
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @return {boolean} Contains tile coordinate.
    + */
    +ol.TileRange.prototype.contains = function(tileCoord) {
    +  return this.containsXY(tileCoord[1], tileCoord[2]);
    +};
    +
    +
    +/**
    + * @param {ol.TileRange} tileRange Tile range.
    + * @return {boolean} Contains.
    + */
    +ol.TileRange.prototype.containsTileRange = function(tileRange) {
    +  return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX &&
    +      this.minY <= tileRange.minY && tileRange.maxY <= this.maxY;
    +};
    +
    +
    +/**
    + * @param {number} x Tile coordinate x.
    + * @param {number} y Tile coordinate y.
    + * @return {boolean} Contains coordinate.
    + */
    +ol.TileRange.prototype.containsXY = function(x, y) {
    +  return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
    +};
    +
    +
    +/**
    + * @param {ol.TileRange} tileRange Tile range.
    + * @return {boolean} Equals.
    + */
    +ol.TileRange.prototype.equals = function(tileRange) {
    +  return this.minX == tileRange.minX && this.minY == tileRange.minY &&
    +      this.maxX == tileRange.maxX && this.maxY == tileRange.maxY;
    +};
    +
    +
    +/**
    + * @param {ol.TileRange} tileRange Tile range.
    + */
    +ol.TileRange.prototype.extend = function(tileRange) {
    +  if (tileRange.minX < this.minX) {
    +    this.minX = tileRange.minX;
    +  }
    +  if (tileRange.maxX > this.maxX) {
    +    this.maxX = tileRange.maxX;
    +  }
    +  if (tileRange.minY < this.minY) {
    +    this.minY = tileRange.minY;
    +  }
    +  if (tileRange.maxY > this.maxY) {
    +    this.maxY = tileRange.maxY;
    +  }
    +};
    +
    +
    +/**
    + * @return {number} Height.
    + */
    +ol.TileRange.prototype.getHeight = function() {
    +  return this.maxY - this.minY + 1;
    +};
    +
    +
    +/**
    + * @return {ol.Size} Size.
    + */
    +ol.TileRange.prototype.getSize = function() {
    +  return [this.getWidth(), this.getHeight()];
    +};
    +
    +
    +/**
    + * @return {number} Width.
    + */
    +ol.TileRange.prototype.getWidth = function() {
    +  return this.maxX - this.minX + 1;
    +};
    +
    +
    +/**
    + * @param {ol.TileRange} tileRange Tile range.
    + * @return {boolean} Intersects.
    + */
    +ol.TileRange.prototype.intersects = function(tileRange) {
    +  return this.minX <= tileRange.maxX &&
    +      this.maxX >= tileRange.minX &&
    +      this.minY <= tileRange.maxY &&
    +      this.maxY >= tileRange.minY;
    +};
    diff --git a/mapbox-gl/ol/ol/tileurlfunction.js b/mapbox-gl/ol/ol/tileurlfunction.js
    new file mode 100644
    index 0000000000..00b4e822b4
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/tileurlfunction.js
    @@ -0,0 +1,157 @@
    +goog.provide('ol.TileUrlFunction');
    +goog.provide('ol.TileUrlFunctionType');
    +
    +goog.require('goog.array');
    +goog.require('goog.math');
    +goog.require('ol.TileCoord');
    +goog.require('ol.tilecoord');
    +
    +
    +/**
    + * A function that takes an {@link ol.TileCoord} for the tile coordinate,
    + * a `{number}` representing the pixel ratio and an {@link ol.proj.Projection}
    + * for the projection  as arguments and returns a `{string}` or
    + * undefined representing the tile URL.
    + *
    + * @typedef {function(ol.TileCoord, number,
    + *           ol.proj.Projection): (string|undefined)}
    + * @api
    + */
    +ol.TileUrlFunctionType;
    +
    +
    +/**
    + * @typedef {function(ol.TileCoord, ol.proj.Projection, ol.TileCoord=):
    + *     ol.TileCoord}
    + */
    +ol.TileCoordTransformType;
    +
    +
    +/**
    + * @param {string} template Template.
    + * @return {ol.TileUrlFunctionType} Tile URL function.
    + */
    +ol.TileUrlFunction.createFromTemplate = function(template) {
    +  var zRegEx = /\{z\}/g;
    +  var xRegEx = /\{x\}/g;
    +  var yRegEx = /\{y\}/g;
    +  var dashYRegEx = /\{-y\}/g;
    +  return (
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile Coordinate.
    +       * @param {number} pixelRatio Pixel ratio.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @return {string|undefined} Tile URL.
    +       */
    +      function(tileCoord, pixelRatio, projection) {
    +        if (goog.isNull(tileCoord)) {
    +          return undefined;
    +        } else {
    +          return template.replace(zRegEx, tileCoord[0].toString())
    +                         .replace(xRegEx, tileCoord[1].toString())
    +                         .replace(yRegEx, tileCoord[2].toString())
    +                         .replace(dashYRegEx, function() {
    +                           var y = (1 << tileCoord[0]) - tileCoord[2] - 1;
    +                           return y.toString();
    +                         });
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {Array.<string>} templates Templates.
    + * @return {ol.TileUrlFunctionType} Tile URL function.
    + */
    +ol.TileUrlFunction.createFromTemplates = function(templates) {
    +  return ol.TileUrlFunction.createFromTileUrlFunctions(
    +      goog.array.map(templates, ol.TileUrlFunction.createFromTemplate));
    +};
    +
    +
    +/**
    + * @param {Array.<ol.TileUrlFunctionType>} tileUrlFunctions Tile URL Functions.
    + * @return {ol.TileUrlFunctionType} Tile URL function.
    + */
    +ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
    +  if (tileUrlFunctions.length === 1) {
    +    return tileUrlFunctions[0];
    +  }
    +  return (
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile Coordinate.
    +       * @param {number} pixelRatio Pixel ratio.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @return {string|undefined} Tile URL.
    +       */
    +      function(tileCoord, pixelRatio, projection) {
    +        if (goog.isNull(tileCoord)) {
    +          return undefined;
    +        } else {
    +          var h = ol.tilecoord.hash(tileCoord);
    +          var index = goog.math.modulo(h, tileUrlFunctions.length);
    +          return tileUrlFunctions[index](tileCoord, pixelRatio, projection);
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {ol.TileCoord} tileCoord Tile coordinate.
    + * @param {number} pixelRatio Pixel ratio.
    + * @param {ol.proj.Projection} projection Projection.
    + * @return {string|undefined} Tile URL.
    + */
    +ol.TileUrlFunction.nullTileUrlFunction =
    +    function(tileCoord, pixelRatio, projection) {
    +  return undefined;
    +};
    +
    +
    +/**
    + * @param {ol.TileCoordTransformType} transformFn Transform function.
    + * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function.
    + * @return {ol.TileUrlFunctionType} Tile URL function.
    + */
    +ol.TileUrlFunction.withTileCoordTransform =
    +    function(transformFn, tileUrlFunction) {
    +  var tmpTileCoord = [0, 0, 0];
    +  return (
    +      /**
    +       * @param {ol.TileCoord} tileCoord Tile Coordinate.
    +       * @param {number} pixelRatio Pixel ratio.
    +       * @param {ol.proj.Projection} projection Projection.
    +       * @return {string|undefined} Tile URL.
    +       */
    +      function(tileCoord, pixelRatio, projection) {
    +        if (goog.isNull(tileCoord)) {
    +          return undefined;
    +        } else {
    +          return tileUrlFunction(
    +              transformFn(tileCoord, projection, tmpTileCoord),
    +              pixelRatio,
    +              projection);
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {string} url URL.
    + * @return {Array.<string>} Array of urls.
    + */
    +ol.TileUrlFunction.expandUrl = function(url) {
    +  var urls = [];
    +  var match = /\{(\d)-(\d)\}/.exec(url) || /\{([a-z])-([a-z])\}/.exec(url);
    +  if (match) {
    +    var startCharCode = match[1].charCodeAt(0);
    +    var stopCharCode = match[2].charCodeAt(0);
    +    var charCode;
    +    for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) {
    +      urls.push(url.replace(match[0], String.fromCharCode(charCode)));
    +    }
    +  } else {
    +    urls.push(url);
    +  }
    +  return urls;
    +};
    diff --git a/mapbox-gl/ol/ol/transformfunction.js b/mapbox-gl/ol/ol/transformfunction.js
    new file mode 100644
    index 0000000000..d734d241fd
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/transformfunction.js
    @@ -0,0 +1,13 @@
    +goog.provide('ol.TransformFunction');
    +
    +
    +/**
    + * A transform function accepts an array of input coordinate values, an optional
    + * output array, and an optional dimension (default should be 2).  The function
    + * transforms the input coordinate values, populates the output array, and
    + * returns the output array.
    + *
    + * @typedef {function(Array.<number>, Array.<number>=, number=): Array.<number>}
    + * @api stable
    + */
    +ol.TransformFunction;
    diff --git a/mapbox-gl/ol/ol/vec/mat4.js b/mapbox-gl/ol/ol/vec/mat4.js
    new file mode 100644
    index 0000000000..54e7df7e71
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/vec/mat4.js
    @@ -0,0 +1,82 @@
    +goog.provide('ol.vec.Mat4');
    +
    +goog.require('goog.vec.Mat4');
    +
    +
    +/**
    + * @param {!goog.vec.Mat4.Number} mat Matrix.
    + * @param {number} translateX1 Translate X1.
    + * @param {number} translateY1 Translate Y1.
    + * @param {number} scaleX Scale X.
    + * @param {number} scaleY Scale Y.
    + * @param {number} rotation Rotation.
    + * @param {number} translateX2 Translate X2.
    + * @param {number} translateY2 Translate Y2.
    + * @return {!goog.vec.Mat4.Number} Matrix.
    + */
    +ol.vec.Mat4.makeTransform2D = function(mat, translateX1, translateY1,
    +    scaleX, scaleY, rotation, translateX2, translateY2) {
    +  goog.vec.Mat4.makeIdentity(mat);
    +  if (translateX1 !== 0 || translateY1 !== 0) {
    +    goog.vec.Mat4.translate(mat, translateX1, translateY1, 0);
    +  }
    +  if (scaleX != 1 || scaleY != 1) {
    +    goog.vec.Mat4.scale(mat, scaleX, scaleY, 1);
    +  }
    +  if (rotation !== 0) {
    +    goog.vec.Mat4.rotateZ(mat, rotation);
    +  }
    +  if (translateX2 !== 0 || translateY2 !== 0) {
    +    goog.vec.Mat4.translate(mat, translateX2, translateY2, 0);
    +  }
    +  return mat;
    +};
    +
    +
    +/**
    + * Returns true if mat1 and mat2 represent the same 2D transformation.
    + * @param {goog.vec.Mat4.Number} mat1 Matrix 1.
    + * @param {goog.vec.Mat4.Number} mat2 Matrix 2.
    + * @return {boolean} Equal 2D.
    + */
    +ol.vec.Mat4.equals2D = function(mat1, mat2) {
    +  return (
    +      goog.vec.Mat4.getElement(mat1, 0, 0) ==
    +      goog.vec.Mat4.getElement(mat2, 0, 0) &&
    +      goog.vec.Mat4.getElement(mat1, 1, 0) ==
    +      goog.vec.Mat4.getElement(mat2, 1, 0) &&
    +      goog.vec.Mat4.getElement(mat1, 0, 1) ==
    +      goog.vec.Mat4.getElement(mat2, 0, 1) &&
    +      goog.vec.Mat4.getElement(mat1, 1, 1) ==
    +      goog.vec.Mat4.getElement(mat2, 1, 1) &&
    +      goog.vec.Mat4.getElement(mat1, 0, 3) ==
    +      goog.vec.Mat4.getElement(mat2, 0, 3) &&
    +      goog.vec.Mat4.getElement(mat1, 1, 3) ==
    +      goog.vec.Mat4.getElement(mat2, 1, 3));
    +};
    +
    +
    +/**
    + * Transforms the given vector with the given matrix storing the resulting,
    + * transformed vector into resultVec. The input vector is multiplied against the
    + * upper 2x4 matrix omitting the projective component.
    + *
    + * @param {goog.vec.Mat4.Number} mat The matrix supplying the transformation.
    + * @param {Array.<number>} vec The 3 element vector to transform.
    + * @param {Array.<number>} resultVec The 3 element vector to receive the results
    + *     (may be vec).
    + * @return {Array.<number>} return resultVec so that operations can be
    + *     chained together.
    + */
    +ol.vec.Mat4.multVec2 = function(mat, vec, resultVec) {
    +  var m00 = goog.vec.Mat4.getElement(mat, 0, 0);
    +  var m10 = goog.vec.Mat4.getElement(mat, 1, 0);
    +  var m01 = goog.vec.Mat4.getElement(mat, 0, 1);
    +  var m11 = goog.vec.Mat4.getElement(mat, 1, 1);
    +  var m03 = goog.vec.Mat4.getElement(mat, 0, 3);
    +  var m13 = goog.vec.Mat4.getElement(mat, 1, 3);
    +  var x = vec[0], y = vec[1];
    +  resultVec[0] = m00 * x + m01 * y + m03;
    +  resultVec[1] = m10 * x + m11 * y + m13;
    +  return resultVec;
    +};
    diff --git a/mapbox-gl/ol/ol/view.js b/mapbox-gl/ol/ol/view.js
    new file mode 100644
    index 0000000000..37bbdb3c86
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/view.js
    @@ -0,0 +1,773 @@
    +goog.provide('ol.View');
    +goog.provide('ol.ViewHint');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('ol');
    +goog.require('ol.CenterConstraint');
    +goog.require('ol.Constraints');
    +goog.require('ol.Object');
    +goog.require('ol.ResolutionConstraint');
    +goog.require('ol.RotationConstraint');
    +goog.require('ol.RotationConstraintType');
    +goog.require('ol.Size');
    +goog.require('ol.coordinate');
    +goog.require('ol.extent');
    +goog.require('ol.proj');
    +goog.require('ol.proj.METERS_PER_UNIT');
    +goog.require('ol.proj.Projection');
    +goog.require('ol.proj.Units');
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.ViewProperty = {
    +  CENTER: 'center',
    +  RESOLUTION: 'resolution',
    +  ROTATION: 'rotation'
    +};
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.ViewHint = {
    +  ANIMATING: 0,
    +  INTERACTING: 1
    +};
    +
    +
    +
    +/**
    + * @classdesc
    + * An ol.View object represents a simple 2D view of the map.
    + *
    + * This is the object to act upon to change the center, resolution,
    + * and rotation of the map.
    + *
    + * ### The view states
    + *
    + * An `ol.View` is determined by three states: `center`, `resolution`,
    + * and `rotation`. Each state has a corresponding getter and setter, e.g.
    + * `getCenter` and `setCenter` for the `center` state.
    + *
    + * An `ol.View` has a `projection`. The projection determines the
    + * coordinate system of the center, and its units determine the units of the
    + * resolution (projection units per pixel). The default projection is
    + * Spherical Mercator (EPSG:3857).
    + *
    + * ### The constraints
    + *
    + * `setCenter`, `setResolution` and `setRotation` can be used to change the
    + * states of the view. Any value can be passed to the setters. And the value
    + * that is passed to a setter will effectively be the value set in the view,
    + * and returned by the corresponding getter.
    + *
    + * But an `ol.View` object also has a *resolution constraint*, a
    + * *rotation constraint* and a *center constraint*.
    + *
    + * As said above, no constraints are applied when the setters are used to set
    + * new states for the view. Applying constraints is done explicitly through
    + * the use of the `constrain*` functions (`constrainResolution` and
    + * `constrainRotation` and `constrainCenter`).
    + *
    + * The main users of the constraints are the interactions and the
    + * controls. For example, double-clicking on the map changes the view to
    + * the "next" resolution. And releasing the fingers after pinch-zooming
    + * snaps to the closest resolution (with an animation).
    + *
    + * The *resolution constraint* snaps to specific resolutions. It is
    + * determined by the following options: `resolutions`, `maxResolution`,
    + * `maxZoom`, and `zoomFactor`. If `resolutions` is set, the other three
    + * options are ignored. See documentation for each option for more
    + * information.
    + *
    + * The *rotation constraint* snaps to specific angles. It is determined
    + * by the following options: `enableRotation` and `constrainRotation`.
    + * By default the rotation value is snapped to zero when approaching the
    + * horizontal.
    + *
    + * The *center constraint* is determined by the `extent` option. By
    + * default the center is not constrained at all.
    + *
    + * @constructor
    + * @extends {ol.Object}
    + * @param {olx.ViewOptions=} opt_options View options.
    + * @api stable
    + */
    +ol.View = function(opt_options) {
    +  goog.base(this);
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.hints_ = [0, 0];
    +
    +  /**
    +   * @type {Object.<string, *>}
    +   */
    +  var properties = {};
    +  properties[ol.ViewProperty.CENTER] = goog.isDef(options.center) ?
    +      options.center : null;
    +
    +  /**
    +   * @private
    +   * @const
    +   * @type {ol.proj.Projection}
    +   */
    +  this.projection_ = ol.proj.createProjection(options.projection, 'EPSG:3857');
    +
    +  var resolutionConstraintInfo = ol.View.createResolutionConstraint_(
    +      options);
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.maxResolution_ = resolutionConstraintInfo.maxResolution;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.minResolution_ = resolutionConstraintInfo.minResolution;
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.minZoom_ = resolutionConstraintInfo.minZoom;
    +
    +  var centerConstraint = ol.View.createCenterConstraint_(options);
    +  var resolutionConstraint = resolutionConstraintInfo.constraint;
    +  var rotationConstraint = ol.View.createRotationConstraint_(options);
    +
    +  /**
    +   * @private
    +   * @type {ol.Constraints}
    +   */
    +  this.constraints_ = new ol.Constraints(
    +      centerConstraint, resolutionConstraint, rotationConstraint);
    +
    +  if (goog.isDef(options.resolution)) {
    +    properties[ol.ViewProperty.RESOLUTION] = options.resolution;
    +  } else if (goog.isDef(options.zoom)) {
    +    properties[ol.ViewProperty.RESOLUTION] = this.constrainResolution(
    +        this.maxResolution_, options.zoom - this.minZoom_);
    +  }
    +  properties[ol.ViewProperty.ROTATION] =
    +      goog.isDef(options.rotation) ? options.rotation : 0;
    +  this.setProperties(properties);
    +};
    +goog.inherits(ol.View, ol.Object);
    +
    +
    +/**
    + * @param {number} rotation Target rotation.
    + * @param {ol.Coordinate} anchor Rotation anchor.
    + * @return {ol.Coordinate|undefined} Center for rotation and anchor.
    + */
    +ol.View.prototype.calculateCenterRotate = function(rotation, anchor) {
    +  var center;
    +  var currentCenter = this.getCenter();
    +  if (goog.isDef(currentCenter)) {
    +    center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]];
    +    ol.coordinate.rotate(center, rotation - this.getRotation());
    +    ol.coordinate.add(center, anchor);
    +  }
    +  return center;
    +};
    +
    +
    +/**
    + * @param {number} resolution Target resolution.
    + * @param {ol.Coordinate} anchor Zoom anchor.
    + * @return {ol.Coordinate|undefined} Center for resolution and anchor.
    + */
    +ol.View.prototype.calculateCenterZoom = function(resolution, anchor) {
    +  var center;
    +  var currentCenter = this.getCenter();
    +  var currentResolution = this.getResolution();
    +  if (goog.isDef(currentCenter) && goog.isDef(currentResolution)) {
    +    var x = anchor[0] -
    +        resolution * (anchor[0] - currentCenter[0]) / currentResolution;
    +    var y = anchor[1] -
    +        resolution * (anchor[1] - currentCenter[1]) / currentResolution;
    +    center = [x, y];
    +  }
    +  return center;
    +};
    +
    +
    +/**
    + * Get the constrained center of this view.
    + * @param {ol.Coordinate|undefined} center Center.
    + * @return {ol.Coordinate|undefined} Constrained center.
    + * @api
    + */
    +ol.View.prototype.constrainCenter = function(center) {
    +  return this.constraints_.center(center);
    +};
    +
    +
    +/**
    + * Get the constrained resolution of this view.
    + * @param {number|undefined} resolution Resolution.
    + * @param {number=} opt_delta Delta. Default is `0`.
    + * @param {number=} opt_direction Direction. Default is `0`.
    + * @return {number|undefined} Constrained resolution.
    + * @api
    + */
    +ol.View.prototype.constrainResolution = function(
    +    resolution, opt_delta, opt_direction) {
    +  var delta = opt_delta || 0;
    +  var direction = opt_direction || 0;
    +  return this.constraints_.resolution(resolution, delta, direction);
    +};
    +
    +
    +/**
    + * Get the constrained rotation of this view.
    + * @param {number|undefined} rotation Rotation.
    + * @param {number=} opt_delta Delta. Default is `0`.
    + * @return {number|undefined} Constrained rotation.
    + * @api
    + */
    +ol.View.prototype.constrainRotation = function(rotation, opt_delta) {
    +  var delta = opt_delta || 0;
    +  return this.constraints_.rotation(rotation, delta);
    +};
    +
    +
    +/**
    + * @return {ol.Coordinate|undefined} The center of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.getCenter = function() {
    +  return /** @type {ol.Coordinate|undefined} */ (
    +      this.get(ol.ViewProperty.CENTER));
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'getCenter',
    +    ol.View.prototype.getCenter);
    +
    +
    +/**
    + * @return {Array.<number>} Hint.
    + */
    +ol.View.prototype.getHints = function() {
    +  return this.hints_.slice();
    +};
    +
    +
    +/**
    + * Calculate the extent for the current view state and the passed `size`.
    + * `size` is the size in pixels of the box into which the calculated extent
    + * should fit. In most cases you want to get the extent of the entire map,
    + * that is `map.getSize()`.
    + * @param {ol.Size} size Box pixel size.
    + * @return {ol.Extent} Extent.
    + * @api
    + */
    +ol.View.prototype.calculateExtent = function(size) {
    +  goog.asserts.assert(this.isDef());
    +  var center = this.getCenter();
    +  var resolution = this.getResolution();
    +  var minX = center[0] - resolution * size[0] / 2;
    +  var maxX = center[0] + resolution * size[0] / 2;
    +  var minY = center[1] - resolution * size[1] / 2;
    +  var maxY = center[1] + resolution * size[1] / 2;
    +  return [minX, minY, maxX, maxY];
    +};
    +
    +
    +/**
    + * @return {ol.proj.Projection} The projection of the view.
    + * @api stable
    + */
    +ol.View.prototype.getProjection = function() {
    +  return this.projection_;
    +};
    +
    +
    +/**
    + * @return {number|undefined} The resolution of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.getResolution = function() {
    +  return /** @type {number|undefined} */ (
    +      this.get(ol.ViewProperty.RESOLUTION));
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'getResolution',
    +    ol.View.prototype.getResolution);
    +
    +
    +/**
    + * Get the resolution for a provided extent (in map units) and size (in pixels).
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Size} size Box pixel size.
    + * @return {number} The resolution at which the provided extent will render at
    + *     the given size.
    + * @api
    + */
    +ol.View.prototype.getResolutionForExtent = function(extent, size) {
    +  var xResolution = ol.extent.getWidth(extent) / size[0];
    +  var yResolution = ol.extent.getHeight(extent) / size[1];
    +  return Math.max(xResolution, yResolution);
    +};
    +
    +
    +/**
    + * Return a function that returns a value between 0 and 1 for a
    + * resolution. Exponential scaling is assumed.
    + * @param {number=} opt_power Power.
    + * @return {function(number): number} Resolution for value function.
    + */
    +ol.View.prototype.getResolutionForValueFunction = function(opt_power) {
    +  var power = opt_power || 2;
    +  var maxResolution = this.maxResolution_;
    +  var minResolution = this.minResolution_;
    +  var max = Math.log(maxResolution / minResolution) / Math.log(power);
    +  return (
    +      /**
    +       * @param {number} value Value.
    +       * @return {number} Resolution.
    +       */
    +      function(value) {
    +        var resolution = maxResolution / Math.pow(power, value * max);
    +        goog.asserts.assert(resolution >= minResolution &&
    +            resolution <= maxResolution);
    +        return resolution;
    +      });
    +};
    +
    +
    +/**
    + * @return {number} The rotation of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.getRotation = function() {
    +  return /** @type {number} */ (this.get(ol.ViewProperty.ROTATION));
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'getRotation',
    +    ol.View.prototype.getRotation);
    +
    +
    +/**
    + * Return a function that returns a resolution for a value between
    + * 0 and 1. Exponential scaling is assumed.
    + * @param {number=} opt_power Power.
    + * @return {function(number): number} Value for resolution function.
    + */
    +ol.View.prototype.getValueForResolutionFunction = function(opt_power) {
    +  var power = opt_power || 2;
    +  var maxResolution = this.maxResolution_;
    +  var minResolution = this.minResolution_;
    +  var max = Math.log(maxResolution / minResolution) / Math.log(power);
    +  return (
    +      /**
    +       * @param {number} resolution Resolution.
    +       * @return {number} Value.
    +       */
    +      function(resolution) {
    +        var value =
    +            (Math.log(maxResolution / resolution) / Math.log(power)) / max;
    +        goog.asserts.assert(value >= 0 && value <= 1);
    +        return value;
    +      });
    +};
    +
    +
    +/**
    + * @return {olx.ViewState} View state.
    + */
    +ol.View.prototype.getState = function() {
    +  goog.asserts.assert(this.isDef());
    +  var center = /** @type {ol.Coordinate} */ (this.getCenter());
    +  var projection = this.getProjection();
    +  var resolution = /** @type {number} */ (this.getResolution());
    +  var rotation = this.getRotation();
    +  return /** @type {olx.ViewState} */ ({
    +    center: center.slice(),
    +    projection: goog.isDef(projection) ? projection : null,
    +    resolution: resolution,
    +    rotation: rotation
    +  });
    +};
    +
    +
    +/**
    + * Get the current zoom level. Return undefined if the current
    + * resolution is undefined or not a "constrained resolution".
    + * @return {number|undefined} Zoom.
    + * @api stable
    + */
    +ol.View.prototype.getZoom = function() {
    +  var offset;
    +  var resolution = this.getResolution();
    +
    +  if (goog.isDef(resolution)) {
    +    var res, z = 0;
    +    do {
    +      res = this.constrainResolution(this.maxResolution_, z);
    +      if (res == resolution) {
    +        offset = z;
    +        break;
    +      }
    +      ++z;
    +    } while (res > this.minResolution_);
    +  }
    +
    +  return goog.isDef(offset) ? this.minZoom_ + offset : offset;
    +};
    +
    +
    +/**
    + * Fit the map view to the passed `extent` and `size`. `size` is the size in
    + * pixels of the box to fit the extent into. In most cases you will want to
    + * use the map size, that is `map.getSize()`.
    + * @param {ol.Extent} extent Extent.
    + * @param {ol.Size} size Box pixel size.
    + * @api
    + */
    +ol.View.prototype.fitExtent = function(extent, size) {
    +  if (!ol.extent.isEmpty(extent)) {
    +    this.setCenter(ol.extent.getCenter(extent));
    +    var resolution = this.getResolutionForExtent(extent, size);
    +    var constrainedResolution = this.constrainResolution(resolution, 0, 0);
    +    if (constrainedResolution < resolution) {
    +      constrainedResolution =
    +          this.constrainResolution(constrainedResolution, -1, 0);
    +    }
    +    this.setResolution(constrainedResolution);
    +  }
    +};
    +
    +
    +/**
    + * Fit the given geometry based on the given map size and border.
    + * Take care on the map angle.
    + * @param {ol.geom.SimpleGeometry} geometry Geometry.
    + * @param {ol.Size} size Box pixel size.
    + * @param {olx.view.FitGeometryOptions=} opt_options Options.
    + * @api
    + */
    +ol.View.prototype.fitGeometry = function(geometry, size, opt_options) {
    +  var options = goog.isDef(opt_options) ? opt_options : {};
    +
    +  var padding = goog.isDef(options.padding) ? options.padding : [0, 0, 0, 0];
    +  var constrainResolution = goog.isDef(options.constrainResolution) ?
    +      options.constrainResolution : true;
    +  var nearest = goog.isDef(options.nearest) ? options.nearest : false;
    +  var minResolution;
    +  if (goog.isDef(options.minResolution)) {
    +    minResolution = options.minResolution;
    +  } else if (goog.isDef(options.maxZoom)) {
    +    minResolution = this.constrainResolution(
    +        this.maxResolution_, options.maxZoom - this.minZoom_, 0);
    +  } else {
    +    minResolution = 0;
    +  }
    +  var coords = geometry.getFlatCoordinates();
    +
    +  // calculate rotated extent
    +  var rotation = this.getRotation();
    +  goog.asserts.assert(goog.isDef(rotation));
    +  var cosAngle = Math.cos(-rotation);
    +  var sinAngle = Math.sin(-rotation);
    +  var minRotX = +Infinity;
    +  var minRotY = +Infinity;
    +  var maxRotX = -Infinity;
    +  var maxRotY = -Infinity;
    +  var stride = geometry.getStride();
    +  for (var i = 0, ii = coords.length; i < ii; i += stride) {
    +    var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle;
    +    var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle;
    +    minRotX = Math.min(minRotX, rotX);
    +    minRotY = Math.min(minRotY, rotY);
    +    maxRotX = Math.max(maxRotX, rotX);
    +    maxRotY = Math.max(maxRotY, rotY);
    +  }
    +
    +  // calculate resolution
    +  var resolution = this.getResolutionForExtent(
    +      [minRotX, minRotY, maxRotX, maxRotY],
    +      [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
    +  resolution = isNaN(resolution) ? minResolution :
    +      Math.max(resolution, minResolution);
    +  if (constrainResolution) {
    +    var constrainedResolution = this.constrainResolution(resolution, 0, 0);
    +    if (!nearest && constrainedResolution < resolution) {
    +      constrainedResolution = this.constrainResolution(
    +          constrainedResolution, -1, 0);
    +    }
    +    resolution = constrainedResolution;
    +  }
    +  this.setResolution(resolution);
    +
    +  // calculate center
    +  sinAngle = -sinAngle; // go back to original rotation
    +  var centerRotX = (minRotX + maxRotX) / 2;
    +  var centerRotY = (minRotY + maxRotY) / 2;
    +  centerRotX += (padding[1] - padding[3]) / 2 * resolution;
    +  centerRotY += (padding[0] - padding[2]) / 2 * resolution;
    +  var centerX = centerRotX * cosAngle - centerRotY * sinAngle;
    +  var centerY = centerRotY * cosAngle + centerRotX * sinAngle;
    +
    +  this.setCenter([centerX, centerY]);
    +};
    +
    +
    +/**
    + * Center on coordinate and view position.
    + * Take care on the map angle.
    + * @param {ol.Coordinate} coordinate Coordinate.
    + * @param {ol.Size} size Box pixel size.
    + * @param {ol.Pixel} position Position on the view to center on.
    + * @api
    + */
    +ol.View.prototype.centerOn = function(coordinate, size, position) {
    +  // calculate rotated position
    +  var rotation = this.getRotation();
    +  var cosAngle = Math.cos(-rotation);
    +  var sinAngle = Math.sin(-rotation);
    +  var rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
    +  var rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
    +  var resolution = this.getResolution();
    +  rotX += (size[0] / 2 - position[0]) * resolution;
    +  rotY += (position[1] - size[1] / 2) * resolution;
    +
    +  // go back to original angle
    +  sinAngle = -sinAngle; // go back to original rotation
    +  var centerX = rotX * cosAngle - rotY * sinAngle;
    +  var centerY = rotY * cosAngle + rotX * sinAngle;
    +
    +  this.setCenter([centerX, centerY]);
    +};
    +
    +
    +/**
    + * @return {boolean} Is defined.
    + */
    +ol.View.prototype.isDef = function() {
    +  return goog.isDefAndNotNull(this.getCenter()) &&
    +      goog.isDef(this.getResolution());
    +};
    +
    +
    +/**
    + * Rotate the view around a given coordinate.
    + * @param {number} rotation New rotation value for the view.
    + * @param {ol.Coordinate=} opt_anchor The rotation center.
    + * @api stable
    + */
    +ol.View.prototype.rotate = function(rotation, opt_anchor) {
    +  if (goog.isDef(opt_anchor)) {
    +    var center = this.calculateCenterRotate(rotation, opt_anchor);
    +    this.setCenter(center);
    +  }
    +  this.setRotation(rotation);
    +};
    +
    +
    +/**
    + * Set the center of the current view.
    + * @param {ol.Coordinate|undefined} center The center of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.setCenter = function(center) {
    +  this.set(ol.ViewProperty.CENTER, center);
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'setCenter',
    +    ol.View.prototype.setCenter);
    +
    +
    +/**
    + * @param {ol.ViewHint} hint Hint.
    + * @param {number} delta Delta.
    + * @return {number} New value.
    + */
    +ol.View.prototype.setHint = function(hint, delta) {
    +  goog.asserts.assert(0 <= hint && hint < this.hints_.length);
    +  this.hints_[hint] += delta;
    +  goog.asserts.assert(this.hints_[hint] >= 0);
    +  return this.hints_[hint];
    +};
    +
    +
    +/**
    + * Set the resolution for this view.
    + * @param {number|undefined} resolution The resolution of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.setResolution = function(resolution) {
    +  this.set(ol.ViewProperty.RESOLUTION, resolution);
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'setResolution',
    +    ol.View.prototype.setResolution);
    +
    +
    +/**
    + * Set the rotation for this view.
    + * @param {number} rotation The rotation of the view.
    + * @observable
    + * @api stable
    + */
    +ol.View.prototype.setRotation = function(rotation) {
    +  this.set(ol.ViewProperty.ROTATION, rotation);
    +};
    +goog.exportProperty(
    +    ol.View.prototype,
    +    'setRotation',
    +    ol.View.prototype.setRotation);
    +
    +
    +/**
    + * Zoom to a specific zoom level.
    + * @param {number} zoom Zoom level.
    + * @api stable
    + */
    +ol.View.prototype.setZoom = function(zoom) {
    +  var resolution = this.constrainResolution(
    +      this.maxResolution_, zoom - this.minZoom_, 0);
    +  this.setResolution(resolution);
    +};
    +
    +
    +/**
    + * @param {olx.ViewOptions} options View options.
    + * @private
    + * @return {ol.CenterConstraintType}
    + */
    +ol.View.createCenterConstraint_ = function(options) {
    +  if (goog.isDef(options.extent)) {
    +    return ol.CenterConstraint.createExtent(options.extent);
    +  } else {
    +    return ol.CenterConstraint.none;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @param {olx.ViewOptions} options View options.
    + * @return {{constraint: ol.ResolutionConstraintType, maxResolution: number,
    + *     minResolution: number}}
    + */
    +ol.View.createResolutionConstraint_ = function(options) {
    +  var resolutionConstraint;
    +  var maxResolution;
    +  var minResolution;
    +
    +  // TODO: move these to be ol constants
    +  // see https://github.com/openlayers/ol3/issues/2076
    +  var defaultMaxZoom = 28;
    +  var defaultZoomFactor = 2;
    +
    +  var minZoom = goog.isDef(options.minZoom) ?
    +      options.minZoom : ol.DEFAULT_MIN_ZOOM;
    +
    +  var maxZoom = goog.isDef(options.maxZoom) ?
    +      options.maxZoom : defaultMaxZoom;
    +
    +  var zoomFactor = goog.isDef(options.zoomFactor) ?
    +      options.zoomFactor : defaultZoomFactor;
    +
    +  if (goog.isDef(options.resolutions)) {
    +    var resolutions = options.resolutions;
    +    maxResolution = resolutions[0];
    +    minResolution = resolutions[resolutions.length - 1];
    +    resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions(
    +        resolutions);
    +  } else {
    +    // calculate the default min and max resolution
    +    var projection = ol.proj.createProjection(options.projection, 'EPSG:3857');
    +    var extent = projection.getExtent();
    +    var size = goog.isNull(extent) ?
    +        // use an extent that can fit the whole world if need be
    +        360 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] /
    +            ol.proj.METERS_PER_UNIT[projection.getUnits()] :
    +        Math.max(ol.extent.getWidth(extent), ol.extent.getHeight(extent));
    +
    +    var defaultMaxResolution = size / ol.DEFAULT_TILE_SIZE / Math.pow(
    +        defaultZoomFactor, ol.DEFAULT_MIN_ZOOM);
    +
    +    var defaultMinResolution = defaultMaxResolution / Math.pow(
    +        defaultZoomFactor, defaultMaxZoom - ol.DEFAULT_MIN_ZOOM);
    +
    +    // user provided maxResolution takes precedence
    +    maxResolution = options.maxResolution;
    +    if (goog.isDef(maxResolution)) {
    +      minZoom = 0;
    +    } else {
    +      maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom);
    +    }
    +
    +    // user provided minResolution takes precedence
    +    minResolution = options.minResolution;
    +    if (!goog.isDef(minResolution)) {
    +      if (goog.isDef(options.maxZoom)) {
    +        if (goog.isDef(options.maxResolution)) {
    +          minResolution = maxResolution / Math.pow(zoomFactor, maxZoom);
    +        } else {
    +          minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom);
    +        }
    +      } else {
    +        minResolution = defaultMinResolution;
    +      }
    +    }
    +
    +    // given discrete zoom levels, minResolution may be different than provided
    +    maxZoom = minZoom + Math.floor(
    +        Math.log(maxResolution / minResolution) / Math.log(zoomFactor));
    +    minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);
    +
    +    resolutionConstraint = ol.ResolutionConstraint.createSnapToPower(
    +        zoomFactor, maxResolution, maxZoom - minZoom);
    +  }
    +  return {constraint: resolutionConstraint, maxResolution: maxResolution,
    +    minResolution: minResolution, minZoom: minZoom};
    +};
    +
    +
    +/**
    + * @private
    + * @param {olx.ViewOptions} options View options.
    + * @return {ol.RotationConstraintType} Rotation constraint.
    + */
    +ol.View.createRotationConstraint_ = function(options) {
    +  var enableRotation = goog.isDef(options.enableRotation) ?
    +      options.enableRotation : true;
    +  if (enableRotation) {
    +    var constrainRotation = options.constrainRotation;
    +    if (!goog.isDef(constrainRotation) || constrainRotation === true) {
    +      return ol.RotationConstraint.createSnapToZero();
    +    } else if (constrainRotation === false) {
    +      return ol.RotationConstraint.none;
    +    } else if (goog.isNumber(constrainRotation)) {
    +      return ol.RotationConstraint.createSnapToN(constrainRotation);
    +    } else {
    +      goog.asserts.fail();
    +      return ol.RotationConstraint.none;
    +    }
    +  } else {
    +    return ol.RotationConstraint.disable;
    +  }
    +};
    diff --git a/mapbox-gl/ol/ol/webgl/buffer.js b/mapbox-gl/ol/ol/webgl/buffer.js
    new file mode 100644
    index 0000000000..a39194ccf6
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/webgl/buffer.js
    @@ -0,0 +1,56 @@
    +goog.provide('ol.webgl.Buffer');
    +
    +goog.require('goog.array');
    +goog.require('goog.webgl');
    +goog.require('ol');
    +
    +
    +/**
    + * @enum {number}
    + */
    +ol.webgl.BufferUsage = {
    +  STATIC_DRAW: goog.webgl.STATIC_DRAW,
    +  STREAM_DRAW: goog.webgl.STREAM_DRAW,
    +  DYNAMIC_DRAW: goog.webgl.DYNAMIC_DRAW
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @param {Array.<number>=} opt_arr Array.
    + * @param {number=} opt_usage Usage.
    + * @struct
    + */
    +ol.webgl.Buffer = function(opt_arr, opt_usage) {
    +
    +  /**
    +   * @private
    +   * @type {Array.<number>}
    +   */
    +  this.arr_ = goog.isDef(opt_arr) ? opt_arr : [];
    +
    +  /**
    +   * @private
    +   * @type {number}
    +   */
    +  this.usage_ = goog.isDef(opt_usage) ?
    +      opt_usage : ol.webgl.BufferUsage.STATIC_DRAW;
    +
    +};
    +
    +
    +/**
    + * @return {Array.<number>} Array.
    + */
    +ol.webgl.Buffer.prototype.getArray = function() {
    +  return this.arr_;
    +};
    +
    +
    +/**
    + * @return {number} Usage.
    + */
    +ol.webgl.Buffer.prototype.getUsage = function() {
    +  return this.usage_;
    +};
    diff --git a/mapbox-gl/ol/ol/webgl/context.js b/mapbox-gl/ol/ol/webgl/context.js
    new file mode 100644
    index 0000000000..616a2d7048
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/webgl/context.js
    @@ -0,0 +1,410 @@
    +goog.provide('ol.webgl.Context');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.events');
    +goog.require('goog.log');
    +goog.require('goog.object');
    +goog.require('ol');
    +goog.require('ol.webgl.Buffer');
    +goog.require('ol.webgl.WebGLContextEventType');
    +
    +
    +/**
    + * @typedef {{buf: ol.webgl.Buffer,
    + *            buffer: WebGLBuffer}}
    + */
    +ol.webgl.BufferCacheEntry;
    +
    +
    +
    +/**
    + * @classdesc
    + * A WebGL context for accessing low-level WebGL capabilities.
    + *
    + * @constructor
    + * @extends {goog.events.EventTarget}
    + * @param {HTMLCanvasElement} canvas Canvas.
    + * @param {WebGLRenderingContext} gl GL.
    + * @api
    + */
    +ol.webgl.Context = function(canvas, gl) {
    +
    +  /**
    +   * @private
    +   * @type {HTMLCanvasElement}
    +   */
    +  this.canvas_ = canvas;
    +
    +  /**
    +   * @private
    +   * @type {WebGLRenderingContext}
    +   */
    +  this.gl_ = gl;
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, ol.webgl.BufferCacheEntry>}
    +   */
    +  this.bufferCache_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<number, WebGLShader>}
    +   */
    +  this.shaderCache_ = {};
    +
    +  /**
    +   * @private
    +   * @type {Object.<string, WebGLProgram>}
    +   */
    +  this.programCache_ = {};
    +
    +  /**
    +   * @private
    +   * @type {WebGLProgram}
    +   */
    +  this.currentProgram_ = null;
    +
    +  /**
    +   * @private
    +   * @type {WebGLFramebuffer}
    +   */
    +  this.hitDetectionFramebuffer_ = null;
    +
    +  /**
    +   * @private
    +   * @type {WebGLTexture}
    +   */
    +  this.hitDetectionTexture_ = null;
    +
    +  /**
    +   * @private
    +   * @type {WebGLRenderbuffer}
    +   */
    +  this.hitDetectionRenderbuffer_ = null;
    +
    +  /**
    +   * @type {boolean}
    +   */
    +  this.hasOESElementIndexUint = goog.array.contains(
    +      ol.WEBGL_EXTENSIONS, 'OES_element_index_uint');
    +
    +  // use the OES_element_index_uint extension if available
    +  if (this.hasOESElementIndexUint) {
    +    var ext = gl.getExtension('OES_element_index_uint');
    +    goog.asserts.assert(!goog.isNull(ext));
    +  }
    +
    +  goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST,
    +      this.handleWebGLContextLost, false, this);
    +  goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
    +      this.handleWebGLContextRestored, false, this);
    +
    +};
    +
    +
    +/**
    + * Just bind the buffer if it's in the cache. Otherwise create
    + * the WebGL buffer, bind it, populate it, and add an entry to
    + * the cache.
    + * @param {number} target Target.
    + * @param {ol.webgl.Buffer} buf Buffer.
    + */
    +ol.webgl.Context.prototype.bindBuffer = function(target, buf) {
    +  var gl = this.getGL();
    +  var arr = buf.getArray();
    +  var bufferKey = goog.getUid(buf);
    +  if (bufferKey in this.bufferCache_) {
    +    var bufferCacheEntry = this.bufferCache_[bufferKey];
    +    gl.bindBuffer(target, bufferCacheEntry.buffer);
    +  } else {
    +    var buffer = gl.createBuffer();
    +    gl.bindBuffer(target, buffer);
    +    goog.asserts.assert(target == goog.webgl.ARRAY_BUFFER ||
    +        target == goog.webgl.ELEMENT_ARRAY_BUFFER);
    +    var /** @type {ArrayBufferView} */ arrayBuffer;
    +    if (target == goog.webgl.ARRAY_BUFFER) {
    +      arrayBuffer = new Float32Array(arr);
    +    } else if (target == goog.webgl.ELEMENT_ARRAY_BUFFER) {
    +      arrayBuffer = this.hasOESElementIndexUint ?
    +          new Uint32Array(arr) : new Uint16Array(arr);
    +    } else {
    +      goog.asserts.fail();
    +    }
    +    gl.bufferData(target, arrayBuffer, buf.getUsage());
    +    this.bufferCache_[bufferKey] = {
    +      buf: buf,
    +      buffer: buffer
    +    };
    +  }
    +};
    +
    +
    +/**
    + * @param {ol.webgl.Buffer} buf Buffer.
    + */
    +ol.webgl.Context.prototype.deleteBuffer = function(buf) {
    +  var gl = this.getGL();
    +  var bufferKey = goog.getUid(buf);
    +  goog.asserts.assert(bufferKey in this.bufferCache_);
    +  var bufferCacheEntry = this.bufferCache_[bufferKey];
    +  if (!gl.isContextLost()) {
    +    gl.deleteBuffer(bufferCacheEntry.buffer);
    +  }
    +  delete this.bufferCache_[bufferKey];
    +};
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.webgl.Context.prototype.disposeInternal = function() {
    +  var gl = this.getGL();
    +  if (!gl.isContextLost()) {
    +    goog.object.forEach(this.bufferCache_, function(bufferCacheEntry) {
    +      gl.deleteBuffer(bufferCacheEntry.buffer);
    +    });
    +    goog.object.forEach(this.programCache_, function(program) {
    +      gl.deleteProgram(program);
    +    });
    +    goog.object.forEach(this.shaderCache_, function(shader) {
    +      gl.deleteShader(shader);
    +    });
    +    // delete objects for hit-detection
    +    gl.deleteFramebuffer(this.hitDetectionFramebuffer_);
    +    gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_);
    +    gl.deleteTexture(this.hitDetectionTexture_);
    +  }
    +};
    +
    +
    +/**
    + * @return {HTMLCanvasElement} Canvas.
    + */
    +ol.webgl.Context.prototype.getCanvas = function() {
    +  return this.canvas_;
    +};
    +
    +
    +/**
    + * @return {WebGLRenderingContext} GL.
    + * @api
    + */
    +ol.webgl.Context.prototype.getGL = function() {
    +  return this.gl_;
    +};
    +
    +
    +/**
    + * @return {WebGLFramebuffer} The framebuffer for the hit-detection.
    + * @api
    + */
    +ol.webgl.Context.prototype.getHitDetectionFramebuffer = function() {
    +  if (goog.isNull(this.hitDetectionFramebuffer_)) {
    +    this.initHitDetectionFramebuffer_();
    +  }
    +  return this.hitDetectionFramebuffer_;
    +};
    +
    +
    +/**
    + * Get shader from the cache if it's in the cache. Otherwise, create
    + * the WebGL shader, compile it, and add entry to cache.
    + * @param {ol.webgl.Shader} shaderObject Shader object.
    + * @return {WebGLShader} Shader.
    + */
    +ol.webgl.Context.prototype.getShader = function(shaderObject) {
    +  var shaderKey = goog.getUid(shaderObject);
    +  if (shaderKey in this.shaderCache_) {
    +    return this.shaderCache_[shaderKey];
    +  } else {
    +    var gl = this.getGL();
    +    var shader = gl.createShader(shaderObject.getType());
    +    gl.shaderSource(shader, shaderObject.getSource());
    +    gl.compileShader(shader);
    +    if (goog.DEBUG) {
    +      if (!gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) &&
    +          !gl.isContextLost()) {
    +        goog.log.error(this.logger_, gl.getShaderInfoLog(shader));
    +      }
    +    }
    +    goog.asserts.assert(
    +        gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) ||
    +        gl.isContextLost());
    +    this.shaderCache_[shaderKey] = shader;
    +    return shader;
    +  }
    +};
    +
    +
    +/**
    + * Get the program from the cache if it's in the cache. Otherwise create
    + * the WebGL program, attach the shaders to it, and add an entry to the
    + * cache.
    + * @param {ol.webgl.shader.Fragment} fragmentShaderObject Fragment shader.
    + * @param {ol.webgl.shader.Vertex} vertexShaderObject Vertex shader.
    + * @return {WebGLProgram} Program.
    + */
    +ol.webgl.Context.prototype.getProgram = function(
    +    fragmentShaderObject, vertexShaderObject) {
    +  var programKey =
    +      goog.getUid(fragmentShaderObject) + '/' + goog.getUid(vertexShaderObject);
    +  if (programKey in this.programCache_) {
    +    return this.programCache_[programKey];
    +  } else {
    +    var gl = this.getGL();
    +    var program = gl.createProgram();
    +    gl.attachShader(program, this.getShader(fragmentShaderObject));
    +    gl.attachShader(program, this.getShader(vertexShaderObject));
    +    gl.linkProgram(program);
    +    if (goog.DEBUG) {
    +      if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS) &&
    +          !gl.isContextLost()) {
    +        goog.log.error(this.logger_, gl.getProgramInfoLog(program));
    +      }
    +    }
    +    goog.asserts.assert(
    +        gl.getProgramParameter(program, goog.webgl.LINK_STATUS) ||
    +        gl.isContextLost());
    +    this.programCache_[programKey] = program;
    +    return program;
    +  }
    +};
    +
    +
    +/**
    + * FIXME empy description for jsdoc
    + */
    +ol.webgl.Context.prototype.handleWebGLContextLost = function() {
    +  goog.object.clear(this.bufferCache_);
    +  goog.object.clear(this.shaderCache_);
    +  goog.object.clear(this.programCache_);
    +  this.currentProgram_ = null;
    +  this.hitDetectionFramebuffer_ = null;
    +  this.hitDetectionTexture_ = null;
    +  this.hitDetectionRenderbuffer_ = null;
    +};
    +
    +
    +/**
    + * FIXME empy description for jsdoc
    + */
    +ol.webgl.Context.prototype.handleWebGLContextRestored = function() {
    +};
    +
    +
    +/**
    + * Creates a 1x1 pixel framebuffer for the hit-detection.
    + * @private
    + */
    +ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() {
    +  var gl = this.gl_;
    +  var framebuffer = gl.createFramebuffer();
    +  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
    +
    +  var texture = ol.webgl.Context.createEmptyTexture(gl, 1, 1);
    +  var renderbuffer = gl.createRenderbuffer();
    +  gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
    +  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1);
    +  gl.framebufferTexture2D(
    +      gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    +  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
    +      gl.RENDERBUFFER, renderbuffer);
    +
    +  gl.bindTexture(gl.TEXTURE_2D, null);
    +  gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    +  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    +
    +  this.hitDetectionFramebuffer_ = framebuffer;
    +  this.hitDetectionTexture_ = texture;
    +  this.hitDetectionRenderbuffer_ = renderbuffer;
    +};
    +
    +
    +/**
    + * Just return false if that program is used already. Other use
    + * that program (call `gl.useProgram`) and make it the "current
    + * program".
    + * @param {WebGLProgram} program Program.
    + * @return {boolean} Changed.
    + * @api
    + */
    +ol.webgl.Context.prototype.useProgram = function(program) {
    +  if (program == this.currentProgram_) {
    +    return false;
    +  } else {
    +    var gl = this.getGL();
    +    gl.useProgram(program);
    +    this.currentProgram_ = program;
    +    return true;
    +  }
    +};
    +
    +
    +/**
    + * @private
    + * @type {goog.log.Logger}
    + */
    +ol.webgl.Context.prototype.logger_ = goog.log.getLogger('ol.webgl.Context');
    +
    +
    +/**
    + * @param {WebGLRenderingContext} gl WebGL rendering context.
    + * @param {number=} opt_wrapS wrapS.
    + * @param {number=} opt_wrapT wrapT.
    + * @return {WebGLTexture}
    + * @private
    + */
    +ol.webgl.Context.createTexture_ = function(gl, opt_wrapS, opt_wrapT) {
    +  var texture = gl.createTexture();
    +  gl.bindTexture(gl.TEXTURE_2D, texture);
    +  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    +  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    +
    +  if (goog.isDef(opt_wrapS)) {
    +    gl.texParameteri(
    +        goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, opt_wrapS);
    +  }
    +  if (goog.isDef(opt_wrapT)) {
    +    gl.texParameteri(
    +        goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, opt_wrapT);
    +  }
    +
    +  return texture;
    +};
    +
    +
    +/**
    + * @param {WebGLRenderingContext} gl WebGL rendering context.
    + * @param {number} width Width.
    + * @param {number} height Height.
    + * @param {number=} opt_wrapS wrapS.
    + * @param {number=} opt_wrapT wrapT.
    + * @return {WebGLTexture}
    + */
    +ol.webgl.Context.createEmptyTexture = function(
    +    gl, width, height, opt_wrapS, opt_wrapT) {
    +  var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT);
    +  gl.texImage2D(
    +      gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE,
    +      null);
    +
    +  return texture;
    +};
    +
    +
    +/**
    + * @param {WebGLRenderingContext} gl WebGL rendering context.
    + * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image.
    + * @param {number=} opt_wrapS wrapS.
    + * @param {number=} opt_wrapT wrapT.
    + * @return {WebGLTexture}
    + */
    +ol.webgl.Context.createTexture = function(gl, image, opt_wrapS, opt_wrapT) {
    +  var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT);
    +  gl.texImage2D(
    +      gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    +
    +  return texture;
    +};
    diff --git a/mapbox-gl/ol/ol/webgl/shader.js b/mapbox-gl/ol/ol/webgl/shader.js
    new file mode 100644
    index 0000000000..f9a2271724
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/webgl/shader.js
    @@ -0,0 +1,84 @@
    +goog.provide('ol.webgl.shader');
    +
    +goog.require('goog.functions');
    +goog.require('goog.webgl');
    +goog.require('ol.webgl');
    +
    +
    +
    +/**
    + * @constructor
    + * @param {string} source Source.
    + * @struct
    + */
    +ol.webgl.Shader = function(source) {
    +
    +  /**
    +   * @private
    +   * @type {string}
    +   */
    +  this.source_ = source;
    +
    +};
    +
    +
    +/**
    + * @return {number} Type.
    + */
    +ol.webgl.Shader.prototype.getType = goog.abstractMethod;
    +
    +
    +/**
    + * @return {string} Source.
    + */
    +ol.webgl.Shader.prototype.getSource = function() {
    +  return this.source_;
    +};
    +
    +
    +/**
    + * @return {boolean} Is animated?
    + */
    +ol.webgl.Shader.prototype.isAnimated = goog.functions.FALSE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.Shader}
    + * @param {string} source Source.
    + * @struct
    + */
    +ol.webgl.shader.Fragment = function(source) {
    +  goog.base(this, source);
    +};
    +goog.inherits(ol.webgl.shader.Fragment, ol.webgl.Shader);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.webgl.shader.Fragment.prototype.getType = function() {
    +  return goog.webgl.FRAGMENT_SHADER;
    +};
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.Shader}
    + * @param {string} source Source.
    + * @struct
    + */
    +ol.webgl.shader.Vertex = function(source) {
    +  goog.base(this, source);
    +};
    +goog.inherits(ol.webgl.shader.Vertex, ol.webgl.Shader);
    +
    +
    +/**
    + * @inheritDoc
    + */
    +ol.webgl.shader.Vertex.prototype.getType = function() {
    +  return goog.webgl.VERTEX_SHADER;
    +};
    diff --git a/mapbox-gl/ol/ol/webgl/shader.mustache b/mapbox-gl/ol/ol/webgl/shader.mustache
    new file mode 100644
    index 0000000000..ed62dc9718
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/webgl/shader.mustache
    @@ -0,0 +1,103 @@
    +// This file is automatically generated, do not edit
    +goog.provide('{{namespace}}');
    +
    +goog.require('ol.webgl.shader');
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Fragment}
    + * @struct
    + */
    +{{className}}Fragment = function() {
    +  goog.base(this, {{className}}Fragment.SOURCE);
    +};
    +goog.inherits({{className}}Fragment, ol.webgl.shader.Fragment);
    +goog.addSingletonGetter({{className}}Fragment);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Fragment.DEBUG_SOURCE = 'precision mediump float;\n{{{getOriginalFragmentSource}}}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Fragment.OPTIMIZED_SOURCE = 'precision mediump float;{{{getFragmentSource}}}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Fragment.SOURCE = goog.DEBUG ?
    +    {{className}}Fragment.DEBUG_SOURCE :
    +    {{className}}Fragment.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @extends {ol.webgl.shader.Vertex}
    + * @struct
    + */
    +{{className}}Vertex = function() {
    +  goog.base(this, {{className}}Vertex.SOURCE);
    +};
    +goog.inherits({{className}}Vertex, ol.webgl.shader.Vertex);
    +goog.addSingletonGetter({{className}}Vertex);
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Vertex.DEBUG_SOURCE = '{{{getOriginalVertexSource}}}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Vertex.OPTIMIZED_SOURCE = '{{{getVertexSource}}}';
    +
    +
    +/**
    + * @const
    + * @type {string}
    + */
    +{{className}}Vertex.SOURCE = goog.DEBUG ?
    +    {{className}}Vertex.DEBUG_SOURCE :
    +    {{className}}Vertex.OPTIMIZED_SOURCE;
    +
    +
    +
    +/**
    + * @constructor
    + * @param {WebGLRenderingContext} gl GL.
    + * @param {WebGLProgram} program Program.
    + * @struct
    + */
    +{{namespace}}.Locations = function(gl, program) {
    +{{#getUniforms}}
    +
    +  /**
    +   * @type {WebGLUniformLocation}
    +   */
    +  this.{{originalName}} = gl.getUniformLocation(
    +      program, goog.DEBUG ? '{{originalName}}' : '{{shortName}}');
    +{{/getUniforms}}
    +{{#getAttributes}}
    +
    +  /**
    +   * @type {number}
    +   */
    +  this.{{originalName}} = gl.getAttribLocation(
    +      program, goog.DEBUG ? '{{originalName}}' : '{{shortName}}');
    +{{/getAttributes}}
    +};
    diff --git a/mapbox-gl/ol/ol/webgl/webgl.js b/mapbox-gl/ol/ol/webgl/webgl.js
    new file mode 100644
    index 0000000000..6b39a1edc7
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/webgl/webgl.js
    @@ -0,0 +1,44 @@
    +goog.provide('ol.webgl');
    +goog.provide('ol.webgl.WebGLContextEventType');
    +
    +
    +/**
    + * @const
    + * @private
    + * @type {Array.<string>}
    + */
    +ol.webgl.CONTEXT_IDS_ = [
    +  'experimental-webgl',
    +  'webgl',
    +  'webkit-3d',
    +  'moz-webgl'
    +];
    +
    +
    +/**
    + * @enum {string}
    + */
    +ol.webgl.WebGLContextEventType = {
    +  LOST: 'webglcontextlost',
    +  RESTORED: 'webglcontextrestored'
    +};
    +
    +
    +/**
    + * @param {HTMLCanvasElement} canvas Canvas.
    + * @param {Object=} opt_attributes Attributes.
    + * @return {WebGLRenderingContext} WebGL rendering context.
    + */
    +ol.webgl.getContext = function(canvas, opt_attributes) {
    +  var context, i, ii = ol.webgl.CONTEXT_IDS_.length;
    +  for (i = 0; i < ii; ++i) {
    +    try {
    +      context = canvas.getContext(ol.webgl.CONTEXT_IDS_[i], opt_attributes);
    +      if (!goog.isNull(context)) {
    +        return /** @type {!WebGLRenderingContext} */ (context);
    +      }
    +    } catch (e) {
    +    }
    +  }
    +  return null;
    +};
    diff --git a/mapbox-gl/ol/ol/xml.js b/mapbox-gl/ol/ol/xml.js
    new file mode 100644
    index 0000000000..5f2a92b42d
    --- /dev/null
    +++ b/mapbox-gl/ol/ol/xml.js
    @@ -0,0 +1,751 @@
    +// FIXME Remove ol.xml.makeParsersNS, and use ol.xml.makeStructureNS instead.
    +
    +goog.provide('ol.xml');
    +
    +goog.require('goog.array');
    +goog.require('goog.asserts');
    +goog.require('goog.dom.NodeType');
    +goog.require('goog.dom.xml');
    +goog.require('goog.object');
    +goog.require('goog.userAgent');
    +
    +
    +/**
    + * When using {@link ol.xml.makeChildAppender} or
    + * {@link ol.xml.makeSimpleNodeFactory}, the top `objectStack` item needs to
    + * have this structure.
    + * @typedef {{node:Node}}
    + */
    +ol.xml.NodeStackItem;
    +
    +
    +/**
    + * @typedef {function(Node, Array.<*>)}
    + */
    +ol.xml.Parser;
    +
    +
    +/**
    + * @typedef {function(Node, *, Array.<*>)}
    + */
    +ol.xml.Serializer;
    +
    +
    +/**
    + * This document should be used when creating nodes for XML serializations. This
    + * document is also used by {@link ol.xml.createElementNS} and
    + * {@link ol.xml.setAttributeNS}
    + * @const
    + * @type {Document}
    + */
    +ol.xml.DOCUMENT = goog.dom.xml.createDocument();
    +
    +
    +/**
    + * @param {string} namespaceURI Namespace URI.
    + * @param {string} qualifiedName Qualified name.
    + * @return {Node} Node.
    + * @private
    + */
    +ol.xml.createElementNS_ = function(namespaceURI, qualifiedName) {
    +  return ol.xml.DOCUMENT.createElementNS(namespaceURI, qualifiedName);
    +};
    +
    +
    +/**
    + * @param {string} namespaceURI Namespace URI.
    + * @param {string} qualifiedName Qualified name.
    + * @return {Node} Node.
    + * @private
    + */
    +ol.xml.createElementNSActiveX_ = function(namespaceURI, qualifiedName) {
    +  if (goog.isNull(namespaceURI)) {
    +    namespaceURI = '';
    +  }
    +  return ol.xml.DOCUMENT.createNode(1, qualifiedName, namespaceURI);
    +};
    +
    +
    +/**
    + * @param {string} namespaceURI Namespace URI.
    + * @param {string} qualifiedName Qualified name.
    + * @return {Node} Node.
    + */
    +ol.xml.createElementNS =
    +    (document.implementation && document.implementation.createDocument) ?
    +        ol.xml.createElementNS_ : ol.xml.createElementNSActiveX_;
    +
    +
    +/**
    + * Recursively grab all text content of child nodes into a single string.
    + * @param {Node} node Node.
    + * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line
    + * breaks.
    + * @return {string} All text content.
    + * @api
    + */
    +ol.xml.getAllTextContent = function(node, normalizeWhitespace) {
    +  return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join('');
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line
    + * breaks.
    + * @param {Array.<String|string>} accumulator Accumulator.
    + * @private
    + * @return {Array.<String|string>} Accumulator.
    + */
    +ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) {
    +  if (node.nodeType == goog.dom.NodeType.CDATA_SECTION ||
    +      node.nodeType == goog.dom.NodeType.TEXT) {
    +    if (normalizeWhitespace) {
    +      // FIXME understand why goog.dom.getTextContent_ uses String here
    +      accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
    +    } else {
    +      accumulator.push(node.nodeValue);
    +    }
    +  } else {
    +    var n;
    +    for (n = node.firstChild; !goog.isNull(n); n = n.nextSibling) {
    +      ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator);
    +    }
    +  }
    +  return accumulator;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {string} Local name.
    + */
    +ol.xml.getLocalName_ = function(node) {
    +  return node.localName;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @private
    + * @return {string} Local name.
    + */
    +ol.xml.getLocalNameIE_ = function(node) {
    +  var localName = node.localName;
    +  if (goog.isDef(localName)) {
    +    return localName;
    +  }
    +  var baseName = node.baseName;
    +  goog.asserts.assert(goog.isDefAndNotNull(baseName));
    +  return baseName;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @return {string} Local name.
    + */
    +ol.xml.getLocalName = goog.userAgent.IE ?
    +    ol.xml.getLocalNameIE_ : ol.xml.getLocalName_;
    +
    +
    +/**
    + * @param {?} value Value.
    + * @private
    + * @return {boolean} Is document.
    + */
    +ol.xml.isDocument_ = function(value) {
    +  return value instanceof Document;
    +};
    +
    +
    +/**
    + * @param {?} value Value.
    + * @private
    + * @return {boolean} Is document.
    + */
    +ol.xml.isDocumentIE_ = function(value) {
    +  return goog.isObject(value) && value.nodeType == goog.dom.NodeType.DOCUMENT;
    +};
    +
    +
    +/**
    + * @param {?} value Value.
    + * @return {boolean} Is document.
    + */
    +ol.xml.isDocument = goog.userAgent.IE ?
    +    ol.xml.isDocumentIE_ : ol.xml.isDocument_;
    +
    +
    +/**
    + * @param {?} value Value.
    + * @private
    + * @return {boolean} Is node.
    + */
    +ol.xml.isNode_ = function(value) {
    +  return value instanceof Node;
    +};
    +
    +
    +/**
    + * @param {?} value Value.
    + * @private
    + * @return {boolean} Is node.
    + */
    +ol.xml.isNodeIE_ = function(value) {
    +  return goog.isObject(value) && goog.isDef(value.nodeType);
    +};
    +
    +
    +/**
    + * @param {?} value Value.
    + * @return {boolean} Is node.
    + */
    +ol.xml.isNode = goog.userAgent.IE ? ol.xml.isNodeIE_ : ol.xml.isNode_;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {string} Value
    + * @private
    + */
    +ol.xml.getAttributeNS_ = function(node, namespaceURI, name) {
    +  return node.getAttributeNS(namespaceURI, name) || '';
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {string} Value
    + * @private
    + */
    +ol.xml.getAttributeNSActiveX_ = function(node, namespaceURI, name) {
    +  var attributeValue = '';
    +  var attributeNode = ol.xml.getAttributeNodeNS(node, namespaceURI, name);
    +  if (goog.isDef(attributeNode)) {
    +    attributeValue = attributeNode.nodeValue;
    +  }
    +  return attributeValue;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {string} Value
    + */
    +ol.xml.getAttributeNS =
    +    (document.implementation && document.implementation.createDocument) ?
    +        ol.xml.getAttributeNS_ : ol.xml.getAttributeNSActiveX_;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {?Node} Attribute node or null if none found.
    + * @private
    + */
    +ol.xml.getAttributeNodeNS_ = function(node, namespaceURI, name) {
    +  return node.getAttributeNodeNS(namespaceURI, name);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {?Node} Attribute node or null if none found.
    + * @private
    + */
    +ol.xml.getAttributeNodeNSActiveX_ = function(node, namespaceURI, name) {
    +  var attributeNode = null;
    +  var attributes = node.attributes;
    +  var potentialNode, fullName;
    +  for (var i = 0, len = attributes.length; i < len; ++i) {
    +    potentialNode = attributes[i];
    +    if (potentialNode.namespaceURI == namespaceURI) {
    +      fullName = (potentialNode.prefix) ?
    +          (potentialNode.prefix + ':' + name) : name;
    +      if (fullName == potentialNode.nodeName) {
    +        attributeNode = potentialNode;
    +        break;
    +      }
    +    }
    +  }
    +  return attributeNode;
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @return {?Node} Attribute node or null if none found.
    + */
    +ol.xml.getAttributeNodeNS =
    +    (document.implementation && document.implementation.createDocument) ?
    +        ol.xml.getAttributeNodeNS_ : ol.xml.getAttributeNodeNSActiveX_;
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @param {string|number} value Value.
    + * @private
    + */
    +ol.xml.setAttributeNS_ = function(node, namespaceURI, name, value) {
    +  node.setAttributeNS(namespaceURI, name, value);
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @param {string|number} value Value.
    + * @private
    + */
    +ol.xml.setAttributeNSActiveX_ = function(node, namespaceURI, name, value) {
    +  if (!goog.isNull(namespaceURI)) {
    +    var attribute = node.ownerDocument.createNode(2, name, namespaceURI);
    +    attribute.nodeValue = value;
    +    node.setAttributeNode(attribute);
    +  } else {
    +    node.setAttribute(name, value);
    +  }
    +};
    +
    +
    +/**
    + * @param {Node} node Node.
    + * @param {?string} namespaceURI Namespace URI.
    + * @param {string} name Attribute name.
    + * @param {string|number} value Value.
    + */
    +ol.xml.setAttributeNS =
    +    (document.implementation && document.implementation.createDocument) ?
    +        ol.xml.setAttributeNS_ : ol.xml.setAttributeNSActiveX_;
    +
    +
    +/**
    + * Parse an XML string to a XML Document
    + * @param {string} xml XML.
    + * @return {Document} Document.
    + * @api
    + */
    +ol.xml.parse = function(xml) {
    +  return new DOMParser().parseFromString(xml, 'application/xml');
    +};
    +
    +
    +/**
    + * @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)}
    + *     valueReader Value reader.
    + * @param {T=} opt_this The object to use as `this` in `valueReader`.
    + * @return {ol.xml.Parser} Parser.
    + * @template T
    + */
    +ol.xml.makeArrayExtender = function(valueReader, opt_this) {
    +  return (
    +      /**
    +       * @param {Node} node Node.
    +       * @param {Array.<*>} objectStack Object stack.
    +       */
    +      function(node, objectStack) {
    +        var value = valueReader.call(opt_this, node, objectStack);
    +        if (goog.isDef(value)) {
    +          goog.asserts.assert(goog.isArray(value));
    +          var array = /** @type {Array.<*>} */
    +              (objectStack[objectStack.length - 1]);
    +          goog.asserts.assert(goog.isArray(array));
    +          goog.array.extend(array, value);
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
    + * @param {T=} opt_this The object to use as `this` in `valueReader`.
    + * @return {ol.xml.Parser} Parser.
    + * @template T
    + */
    +ol.xml.makeArrayPusher = function(valueReader, opt_this) {
    +  return (
    +      /**
    +       * @param {Node} node Node.
    +       * @param {Array.<*>} objectStack Object stack.
    +       */
    +      function(node, objectStack) {
    +        var value = valueReader.call(goog.isDef(opt_this) ? opt_this : this,
    +            node, objectStack);
    +        if (goog.isDef(value)) {
    +          var array = objectStack[objectStack.length - 1];
    +          goog.asserts.assert(goog.isArray(array));
    +          array.push(value);
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
    + * @param {T=} opt_this The object to use as `this` in `valueReader`.
    + * @return {ol.xml.Parser} Parser.
    + * @template T
    + */
    +ol.xml.makeReplacer = function(valueReader, opt_this) {
    +  return (
    +      /**
    +       * @param {Node} node Node.
    +       * @param {Array.<*>} objectStack Object stack.
    +       */
    +      function(node, objectStack) {
    +        var value = valueReader.call(goog.isDef(opt_this) ? opt_this : this,
    +            node, objectStack);
    +        if (goog.isDef(value)) {
    +          objectStack[objectStack.length - 1] = value;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
    + * @param {string=} opt_property Property.
    + * @param {T=} opt_this The object to use as `this` in `valueReader`.
    + * @return {ol.xml.Parser} Parser.
    + * @template T
    + */
    +ol.xml.makeObjectPropertyPusher =
    +    function(valueReader, opt_property, opt_this) {
    +  goog.asserts.assert(goog.isDef(valueReader));
    +  return (
    +      /**
    +       * @param {Node} node Node.
    +       * @param {Array.<*>} objectStack Object stack.
    +       */
    +      function(node, objectStack) {
    +        var value = valueReader.call(goog.isDef(opt_this) ? opt_this : this,
    +            node, objectStack);
    +        if (goog.isDef(value)) {
    +          var object = /** @type {Object} */
    +              (objectStack[objectStack.length - 1]);
    +          var property = goog.isDef(opt_property) ?
    +              opt_property : node.localName;
    +          goog.asserts.assert(goog.isObject(object));
    +          var array = goog.object.setIfUndefined(object, property, []);
    +          array.push(value);
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader.
    + * @param {string=} opt_property Property.
    + * @param {T=} opt_this The object to use as `this` in `valueReader`.
    + * @return {ol.xml.Parser} Parser.
    + * @template T
    + */
    +ol.xml.makeObjectPropertySetter =
    +    function(valueReader, opt_property, opt_this) {
    +  goog.asserts.assert(goog.isDef(valueReader));
    +  return (
    +      /**
    +       * @param {Node} node Node.
    +       * @param {Array.<*>} objectStack Object stack.
    +       */
    +      function(node, objectStack) {
    +        var value = valueReader.call(goog.isDef(opt_this) ? opt_this : this,
    +            node, objectStack);
    +        if (goog.isDef(value)) {
    +          var object = /** @type {Object} */
    +              (objectStack[objectStack.length - 1]);
    +          var property = goog.isDef(opt_property) ?
    +              opt_property : node.localName;
    +          goog.asserts.assert(goog.isObject(object));
    +          object[property] = value;
    +        }
    +      });
    +};
    +
    +
    +/**
    + * @param {Array.<string>} namespaceURIs Namespace URIs.
    + * @param {Object.<string, ol.xml.Parser>} parsers Parsers.
    + * @param {Object.<string, Object.<string, ol.xml.Parser>>=} opt_parsersNS
    + *     ParsersNS.
    + * @return {Object.<string, Object.<string, ol.xml.Parser>>} Parsers NS.
    + */
    +ol.xml.makeParsersNS = function(namespaceURIs, parsers, opt_parsersNS) {
    +  return /** @type {Object.<string, Object.<string, ol.xml.Parser>>} */ (
    +      ol.xml.makeStructureNS(namespaceURIs, parsers, opt_parsersNS));
    +};
    +
    +
    +/**
    + * Creates a serializer that appends nodes written by its `nodeWriter` to its
    + * designated parent. The parent is the `node` of the
    + * {@link ol.xml.NodeStackItem} at the top of the `objectStack`.
    + * @param {function(this: T, Node, V, Array.<*>)}
    + *     nodeWriter Node writer.
    + * @param {T=} opt_this The object to use as `this` in `nodeWriter`.
    + * @return {ol.xml.Serializer} Serializer.
    + * @template T, V
    + */
    +ol.xml.makeChildAppender = function(nodeWriter, opt_this) {
    +  return function(node, value, objectStack) {
    +    nodeWriter.call(goog.isDef(opt_this) ? opt_this : this,
    +        node, value, objectStack);
    +    var parent = objectStack[objectStack.length - 1];
    +    goog.asserts.assert(goog.isObject(parent));
    +    var parentNode = parent.node;
    +    goog.asserts.assert(ol.xml.isNode(parentNode) ||
    +        ol.xml.isDocument(parentNode));
    +    parentNode.appendChild(node);
    +  };
    +};
    +
    +
    +/**
    + * Creates a serializer that calls the provided `nodeWriter` from
    + * {@link ol.xml.serialize}. This can be used by the parent writer to have the
    + * 'nodeWriter' called with an array of values when the `nodeWriter` was
    + * designed to serialize a single item. An example would be a LineString
    + * geometry writer, which could be reused for writing MultiLineString
    + * geometries.
    + * @param {function(this: T, Node, V, Array.<*>)}
    + *     nodeWriter Node writer.
    + * @param {T=} opt_this The object to use as `this` in `nodeWriter`.
    + * @return {ol.xml.Serializer} Serializer.
    + * @template T, V
    + */
    +ol.xml.makeArraySerializer = function(nodeWriter, opt_this) {
    +  var serializersNS, nodeFactory;
    +  return function(node, value, objectStack) {
    +    if (!goog.isDef(serializersNS)) {
    +      serializersNS = {};
    +      var serializers = {};
    +      serializers[node.localName] = nodeWriter;
    +      serializersNS[node.namespaceURI] = serializers;
    +      nodeFactory = ol.xml.makeSimpleNodeFactory(node.localName);
    +    }
    +    ol.xml.serialize(serializersNS, nodeFactory, value, objectStack);
    +  };
    +};
    +
    +
    +/**
    + * Creates a node factory which can use the `opt_keys` passed to
    + * {@link ol.xml.serialize} or {@link ol.xml.pushSerializeAndPop} as node names,
    + * or a fixed node name. The namespace of the created nodes can either be fixed,
    + * or the parent namespace will be used.
    + * @param {string=} opt_nodeName Fixed node name which will be used for all
    + *     created nodes. If not provided, the 3rd argument to the resulting node
    + *     factory needs to be provided and will be the nodeName.
    + * @param {string=} opt_namespaceURI Fixed namespace URI which will be used for
    + *     all created nodes. If not provided, the namespace of the parent node will
    + *     be used.
    + * @return {function(*, Array.<*>, string=): (Node|undefined)} Node factory.
    + */
    +ol.xml.makeSimpleNodeFactory = function(opt_nodeName, opt_namespaceURI) {
    +  var fixedNodeName = opt_nodeName;
    +  return (
    +      /**
    +       * @param {*} value Value.
    +       * @param {Array.<*>} objectStack Object stack.
    +       * @param {string=} opt_nodeName Node name.
    +       * @return {Node} Node.
    +       */
    +      function(value, objectStack, opt_nodeName) {
    +        var context = objectStack[objectStack.length - 1];
    +        var node = context.node;
    +        goog.asserts.assert(ol.xml.isNode(node) || ol.xml.isDocument(node));
    +        var nodeName = fixedNodeName;
    +        if (!goog.isDef(nodeName)) {
    +          nodeName = opt_nodeName;
    +        }
    +        var namespaceURI = opt_namespaceURI;
    +        if (!goog.isDef(opt_namespaceURI)) {
    +          namespaceURI = node.namespaceURI;
    +        }
    +        goog.asserts.assert(goog.isDef(nodeName));
    +        return ol.xml.createElementNS(namespaceURI, nodeName);
    +      }
    +  );
    +};
    +
    +
    +/**
    + * A node factory that creates a node using the parent's `namespaceURI` and the
    + * `nodeName` passed by {@link ol.xml.serialize} or
    + * {@link ol.xml.pushSerializeAndPop} to the node factory.
    + * @const
    + * @type {function(*, Array.<*>, string=): (Node|undefined)}
    + */
    +ol.xml.OBJECT_PROPERTY_NODE_FACTORY = ol.xml.makeSimpleNodeFactory();
    +
    +
    +/**
    + * Creates an array of `values` to be used with {@link ol.xml.serialize} or
    + * {@link ol.xml.pushSerializeAndPop}, where `orderedKeys` has to be provided as
    + * `opt_key` argument.
    + * @param {Object.<string, V>} object Key-value pairs for the sequence. Keys can
    + *     be a subset of the `orderedKeys`.
    + * @param {Array.<string>} orderedKeys Keys in the order of the sequence.
    + * @return {Array.<V>} Values in the order of the sequence. The resulting array
    + *     has the same length as the `orderedKeys` array. Values that are not
    + *     present in `object` will be `undefined` in the resulting array.
    + * @template V
    + */
    +ol.xml.makeSequence = function(object, orderedKeys) {
    +  var length = orderedKeys.length;
    +  var sequence = new Array(length);
    +  for (var i = 0; i < length; ++i) {
    +    sequence[i] = object[orderedKeys[i]];
    +  }
    +  return sequence;
    +};
    +
    +
    +/**
    + * Creates a namespaced structure, using the same values for each namespace.
    + * This can be used as a starting point for versioned parsers, when only a few
    + * values are version specific.
    + * @param {Array.<string>} namespaceURIs Namespace URIs.
    + * @param {T} structure Structure.
    + * @param {Object.<string, T>=} opt_structureNS Namespaced structure to add to.
    + * @return {Object.<string, T>} Namespaced structure.
    + * @template T
    + */
    +ol.xml.makeStructureNS = function(namespaceURIs, structure, opt_structureNS) {
    +  /**
    +   * @type {Object.<string, *>}
    +   */
    +  var structureNS = goog.isDef(opt_structureNS) ? opt_structureNS : {};
    +  var i, ii;
    +  for (i = 0, ii = namespaceURIs.length; i < ii; ++i) {
    +    structureNS[namespaceURIs[i]] = structure;
    +  }
    +  return structureNS;
    +};
    +
    +
    +/**
    + * @param {Object.<string, Object.<string, ol.xml.Parser>>} parsersNS
    + *     Parsers by namespace.
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {*=} opt_this The object to use as `this`.
    + */
    +ol.xml.parseNode = function(parsersNS, node, objectStack, opt_this) {
    +  var n;
    +  for (n = node.firstElementChild; !goog.isNull(n); n = n.nextElementSibling) {
    +    var parsers = parsersNS[n.namespaceURI];
    +    if (goog.isDef(parsers)) {
    +      var parser = parsers[n.localName];
    +      if (goog.isDef(parser)) {
    +        parser.call(opt_this, n, objectStack);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {T} object Object.
    + * @param {Object.<string, Object.<string, ol.xml.Parser>>} parsersNS
    + *     Parsers by namespace.
    + * @param {Node} node Node.
    + * @param {Array.<*>} objectStack Object stack.
    + * @param {*=} opt_this The object to use as `this`.
    + * @return {T|undefined} Object.
    + * @template T
    + */
    +ol.xml.pushParseAndPop = function(
    +    object, parsersNS, node, objectStack, opt_this) {
    +  objectStack.push(object);
    +  ol.xml.parseNode(parsersNS, node, objectStack, opt_this);
    +  return objectStack.pop();
    +};
    +
    +
    +/**
    + * Walks through an array of `values` and calls a serializer for each value.
    + * @param {Object.<string, Object.<string, ol.xml.Serializer>>} serializersNS
    + *     Namespaced serializers.
    + * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
    + *     Node factory. The `nodeFactory` creates the node whose namespace and name
    + *     will be used to choose a node writer from `serializersNS`. This
    + *     separation allows us to decide what kind of node to create, depending on
    + *     the value we want to serialize. An example for this would be different
    + *     geometry writers based on the geometry type.
    + * @param {Array.<*>} values Values to serialize. An example would be an array
    + *     of {@link ol.Feature} instances.
    + * @param {Array.<*>} objectStack Node stack.
    + * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
    + *     `nodeFactory`. This is used for serializing object literals where the
    + *     node name relates to the property key. The array length of `opt_keys` has
    + *     to match the length of `values`. For serializing a sequence, `opt_keys`
    + *     determines the order of the sequence.
    + * @param {T=} opt_this The object to use as `this` for the node factory and
    + *     serializers.
    + * @template T
    + */
    +ol.xml.serialize = function(
    +    serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
    +  var length = (goog.isDef(opt_keys) ? opt_keys : values).length;
    +  var value, node;
    +  for (var i = 0; i < length; ++i) {
    +    value = values[i];
    +    if (goog.isDef(value)) {
    +      node = nodeFactory.call(opt_this, value, objectStack,
    +          goog.isDef(opt_keys) ? opt_keys[i] : undefined);
    +      if (goog.isDef(node)) {
    +        serializersNS[node.namespaceURI][node.localName]
    +            .call(opt_this, node, value, objectStack);
    +      }
    +    }
    +  }
    +};
    +
    +
    +/**
    + * @param {O} object Object.
    + * @param {Object.<string, Object.<string, ol.xml.Serializer>>} serializersNS
    + *     Namespaced serializers.
    + * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
    + *     Node factory. The `nodeFactory` creates the node whose namespace and name
    + *     will be used to choose a node writer from `serializersNS`. This
    + *     separation allows us to decide what kind of node to create, depending on
    + *     the value we want to serialize. An example for this would be different
    + *     geometry writers based on the geometry type.
    + * @param {Array.<*>} values Values to serialize. An example would be an array
    + *     of {@link ol.Feature} instances.
    + * @param {Array.<*>} objectStack Node stack.
    + * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
    + *     `nodeFactory`. This is used for serializing object literals where the
    + *     node name relates to the property key. The array length of `opt_keys` has
    + *     to match the length of `values`. For serializing a sequence, `opt_keys`
    + *     determines the order of the sequence.
    + * @param {T=} opt_this The object to use as `this` for the node factory and
    + *     serializers.
    + * @return {O|undefined} Object.
    + * @template O, T
    + */
    +ol.xml.pushSerializeAndPop = function(object,
    +    serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
    +  objectStack.push(object);
    +  ol.xml.serialize(
    +      serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this);
    +  return objectStack.pop();
    +};
    diff --git a/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.css b/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.css
    new file mode 100644
    index 0000000000..fcd72f7a77
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.css
    @@ -0,0 +1,1109 @@
    +/*!
    + * Bootstrap Responsive v2.3.1
    + *
    + * Copyright 2012 Twitter, Inc
    + * Licensed under the Apache License v2.0
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Designed and built with all the love in the world @twitter by @mdo and @fat.
    + */
    +
    +.clearfix {
    +  *zoom: 1;
    +}
    +
    +.clearfix:before,
    +.clearfix:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.clearfix:after {
    +  clear: both;
    +}
    +
    +.hide-text {
    +  font: 0/0 a;
    +  color: transparent;
    +  text-shadow: none;
    +  background-color: transparent;
    +  border: 0;
    +}
    +
    +.input-block-level {
    +  display: block;
    +  width: 100%;
    +  min-height: 30px;
    +  -webkit-box-sizing: border-box;
    +     -moz-box-sizing: border-box;
    +          box-sizing: border-box;
    +}
    +
    +@-ms-viewport {
    +  width: device-width;
    +}
    +
    +.hidden {
    +  display: none;
    +  visibility: hidden;
    +}
    +
    +.visible-phone {
    +  display: none !important;
    +}
    +
    +.visible-tablet {
    +  display: none !important;
    +}
    +
    +.hidden-desktop {
    +  display: none !important;
    +}
    +
    +.visible-desktop {
    +  display: inherit !important;
    +}
    +
    +@media (min-width: 768px) and (max-width: 979px) {
    +  .hidden-desktop {
    +    display: inherit !important;
    +  }
    +  .visible-desktop {
    +    display: none !important ;
    +  }
    +  .visible-tablet {
    +    display: inherit !important;
    +  }
    +  .hidden-tablet {
    +    display: none !important;
    +  }
    +}
    +
    +@media (max-width: 767px) {
    +  .hidden-desktop {
    +    display: inherit !important;
    +  }
    +  .visible-desktop {
    +    display: none !important;
    +  }
    +  .visible-phone {
    +    display: inherit !important;
    +  }
    +  .hidden-phone {
    +    display: none !important;
    +  }
    +}
    +
    +.visible-print {
    +  display: none !important;
    +}
    +
    +@media print {
    +  .visible-print {
    +    display: inherit !important;
    +  }
    +  .hidden-print {
    +    display: none !important;
    +  }
    +}
    +
    +@media (min-width: 1200px) {
    +  .row {
    +    margin-left: -30px;
    +    *zoom: 1;
    +  }
    +  .row:before,
    +  .row:after {
    +    display: table;
    +    line-height: 0;
    +    content: "";
    +  }
    +  .row:after {
    +    clear: both;
    +  }
    +  [class*="span"] {
    +    float: left;
    +    min-height: 1px;
    +    margin-left: 30px;
    +  }
    +  .container,
    +  .navbar-static-top .container,
    +  .navbar-fixed-top .container,
    +  .navbar-fixed-bottom .container {
    +    width: 1170px;
    +  }
    +  .span12 {
    +    width: 1170px;
    +  }
    +  .span11 {
    +    width: 1070px;
    +  }
    +  .span10 {
    +    width: 970px;
    +  }
    +  .span9 {
    +    width: 870px;
    +  }
    +  .span8 {
    +    width: 770px;
    +  }
    +  .span7 {
    +    width: 670px;
    +  }
    +  .span6 {
    +    width: 570px;
    +  }
    +  .span5 {
    +    width: 470px;
    +  }
    +  .span4 {
    +    width: 370px;
    +  }
    +  .span3 {
    +    width: 270px;
    +  }
    +  .span2 {
    +    width: 170px;
    +  }
    +  .span1 {
    +    width: 70px;
    +  }
    +  .offset12 {
    +    margin-left: 1230px;
    +  }
    +  .offset11 {
    +    margin-left: 1130px;
    +  }
    +  .offset10 {
    +    margin-left: 1030px;
    +  }
    +  .offset9 {
    +    margin-left: 930px;
    +  }
    +  .offset8 {
    +    margin-left: 830px;
    +  }
    +  .offset7 {
    +    margin-left: 730px;
    +  }
    +  .offset6 {
    +    margin-left: 630px;
    +  }
    +  .offset5 {
    +    margin-left: 530px;
    +  }
    +  .offset4 {
    +    margin-left: 430px;
    +  }
    +  .offset3 {
    +    margin-left: 330px;
    +  }
    +  .offset2 {
    +    margin-left: 230px;
    +  }
    +  .offset1 {
    +    margin-left: 130px;
    +  }
    +  .row-fluid {
    +    width: 100%;
    +    *zoom: 1;
    +  }
    +  .row-fluid:before,
    +  .row-fluid:after {
    +    display: table;
    +    line-height: 0;
    +    content: "";
    +  }
    +  .row-fluid:after {
    +    clear: both;
    +  }
    +  .row-fluid [class*="span"] {
    +    display: block;
    +    float: left;
    +    width: 100%;
    +    min-height: 30px;
    +    margin-left: 2.564102564102564%;
    +    *margin-left: 2.5109110747408616%;
    +    -webkit-box-sizing: border-box;
    +       -moz-box-sizing: border-box;
    +            box-sizing: border-box;
    +  }
    +  .row-fluid [class*="span"]:first-child {
    +    margin-left: 0;
    +  }
    +  .row-fluid .controls-row [class*="span"] + [class*="span"] {
    +    margin-left: 2.564102564102564%;
    +  }
    +  .row-fluid .span12 {
    +    width: 100%;
    +    *width: 99.94680851063829%;
    +  }
    +  .row-fluid .span11 {
    +    width: 91.45299145299145%;
    +    *width: 91.39979996362975%;
    +  }
    +  .row-fluid .span10 {
    +    width: 82.90598290598291%;
    +    *width: 82.8527914166212%;
    +  }
    +  .row-fluid .span9 {
    +    width: 74.35897435897436%;
    +    *width: 74.30578286961266%;
    +  }
    +  .row-fluid .span8 {
    +    width: 65.81196581196582%;
    +    *width: 65.75877432260411%;
    +  }
    +  .row-fluid .span7 {
    +    width: 57.26495726495726%;
    +    *width: 57.21176577559556%;
    +  }
    +  .row-fluid .span6 {
    +    width: 48.717948717948715%;
    +    *width: 48.664757228587014%;
    +  }
    +  .row-fluid .span5 {
    +    width: 40.17094017094017%;
    +    *width: 40.11774868157847%;
    +  }
    +  .row-fluid .span4 {
    +    width: 31.623931623931625%;
    +    *width: 31.570740134569924%;
    +  }
    +  .row-fluid .span3 {
    +    width: 23.076923076923077%;
    +    *width: 23.023731587561375%;
    +  }
    +  .row-fluid .span2 {
    +    width: 14.52991452991453%;
    +    *width: 14.476723040552828%;
    +  }
    +  .row-fluid .span1 {
    +    width: 5.982905982905983%;
    +    *width: 5.929714493544281%;
    +  }
    +  .row-fluid .offset12 {
    +    margin-left: 105.12820512820512%;
    +    *margin-left: 105.02182214948171%;
    +  }
    +  .row-fluid .offset12:first-child {
    +    margin-left: 102.56410256410257%;
    +    *margin-left: 102.45771958537915%;
    +  }
    +  .row-fluid .offset11 {
    +    margin-left: 96.58119658119658%;
    +    *margin-left: 96.47481360247316%;
    +  }
    +  .row-fluid .offset11:first-child {
    +    margin-left: 94.01709401709402%;
    +    *margin-left: 93.91071103837061%;
    +  }
    +  .row-fluid .offset10 {
    +    margin-left: 88.03418803418803%;
    +    *margin-left: 87.92780505546462%;
    +  }
    +  .row-fluid .offset10:first-child {
    +    margin-left: 85.47008547008548%;
    +    *margin-left: 85.36370249136206%;
    +  }
    +  .row-fluid .offset9 {
    +    margin-left: 79.48717948717949%;
    +    *margin-left: 79.38079650845607%;
    +  }
    +  .row-fluid .offset9:first-child {
    +    margin-left: 76.92307692307693%;
    +    *margin-left: 76.81669394435352%;
    +  }
    +  .row-fluid .offset8 {
    +    margin-left: 70.94017094017094%;
    +    *margin-left: 70.83378796144753%;
    +  }
    +  .row-fluid .offset8:first-child {
    +    margin-left: 68.37606837606839%;
    +    *margin-left: 68.26968539734497%;
    +  }
    +  .row-fluid .offset7 {
    +    margin-left: 62.393162393162385%;
    +    *margin-left: 62.28677941443899%;
    +  }
    +  .row-fluid .offset7:first-child {
    +    margin-left: 59.82905982905982%;
    +    *margin-left: 59.72267685033642%;
    +  }
    +  .row-fluid .offset6 {
    +    margin-left: 53.84615384615384%;
    +    *margin-left: 53.739770867430444%;
    +  }
    +  .row-fluid .offset6:first-child {
    +    margin-left: 51.28205128205128%;
    +    *margin-left: 51.175668303327875%;
    +  }
    +  .row-fluid .offset5 {
    +    margin-left: 45.299145299145295%;
    +    *margin-left: 45.1927623204219%;
    +  }
    +  .row-fluid .offset5:first-child {
    +    margin-left: 42.73504273504273%;
    +    *margin-left: 42.62865975631933%;
    +  }
    +  .row-fluid .offset4 {
    +    margin-left: 36.75213675213675%;
    +    *margin-left: 36.645753773413354%;
    +  }
    +  .row-fluid .offset4:first-child {
    +    margin-left: 34.18803418803419%;
    +    *margin-left: 34.081651209310785%;
    +  }
    +  .row-fluid .offset3 {
    +    margin-left: 28.205128205128204%;
    +    *margin-left: 28.0987452264048%;
    +  }
    +  .row-fluid .offset3:first-child {
    +    margin-left: 25.641025641025642%;
    +    *margin-left: 25.53464266230224%;
    +  }
    +  .row-fluid .offset2 {
    +    margin-left: 19.65811965811966%;
    +    *margin-left: 19.551736679396257%;
    +  }
    +  .row-fluid .offset2:first-child {
    +    margin-left: 17.094017094017094%;
    +    *margin-left: 16.98763411529369%;
    +  }
    +  .row-fluid .offset1 {
    +    margin-left: 11.11111111111111%;
    +    *margin-left: 11.004728132387708%;
    +  }
    +  .row-fluid .offset1:first-child {
    +    margin-left: 8.547008547008547%;
    +    *margin-left: 8.440625568285142%;
    +  }
    +  input,
    +  textarea,
    +  .uneditable-input {
    +    margin-left: 0;
    +  }
    +  .controls-row [class*="span"] + [class*="span"] {
    +    margin-left: 30px;
    +  }
    +  input.span12,
    +  textarea.span12,
    +  .uneditable-input.span12 {
    +    width: 1156px;
    +  }
    +  input.span11,
    +  textarea.span11,
    +  .uneditable-input.span11 {
    +    width: 1056px;
    +  }
    +  input.span10,
    +  textarea.span10,
    +  .uneditable-input.span10 {
    +    width: 956px;
    +  }
    +  input.span9,
    +  textarea.span9,
    +  .uneditable-input.span9 {
    +    width: 856px;
    +  }
    +  input.span8,
    +  textarea.span8,
    +  .uneditable-input.span8 {
    +    width: 756px;
    +  }
    +  input.span7,
    +  textarea.span7,
    +  .uneditable-input.span7 {
    +    width: 656px;
    +  }
    +  input.span6,
    +  textarea.span6,
    +  .uneditable-input.span6 {
    +    width: 556px;
    +  }
    +  input.span5,
    +  textarea.span5,
    +  .uneditable-input.span5 {
    +    width: 456px;
    +  }
    +  input.span4,
    +  textarea.span4,
    +  .uneditable-input.span4 {
    +    width: 356px;
    +  }
    +  input.span3,
    +  textarea.span3,
    +  .uneditable-input.span3 {
    +    width: 256px;
    +  }
    +  input.span2,
    +  textarea.span2,
    +  .uneditable-input.span2 {
    +    width: 156px;
    +  }
    +  input.span1,
    +  textarea.span1,
    +  .uneditable-input.span1 {
    +    width: 56px;
    +  }
    +  .thumbnails {
    +    margin-left: -30px;
    +  }
    +  .thumbnails > li {
    +    margin-left: 30px;
    +  }
    +  .row-fluid .thumbnails {
    +    margin-left: 0;
    +  }
    +}
    +
    +@media (min-width: 768px) and (max-width: 979px) {
    +  .row {
    +    margin-left: -20px;
    +    *zoom: 1;
    +  }
    +  .row:before,
    +  .row:after {
    +    display: table;
    +    line-height: 0;
    +    content: "";
    +  }
    +  .row:after {
    +    clear: both;
    +  }
    +  [class*="span"] {
    +    float: left;
    +    min-height: 1px;
    +    margin-left: 20px;
    +  }
    +  .container,
    +  .navbar-static-top .container,
    +  .navbar-fixed-top .container,
    +  .navbar-fixed-bottom .container {
    +    width: 724px;
    +  }
    +  .span12 {
    +    width: 724px;
    +  }
    +  .span11 {
    +    width: 662px;
    +  }
    +  .span10 {
    +    width: 600px;
    +  }
    +  .span9 {
    +    width: 538px;
    +  }
    +  .span8 {
    +    width: 476px;
    +  }
    +  .span7 {
    +    width: 414px;
    +  }
    +  .span6 {
    +    width: 352px;
    +  }
    +  .span5 {
    +    width: 290px;
    +  }
    +  .span4 {
    +    width: 228px;
    +  }
    +  .span3 {
    +    width: 166px;
    +  }
    +  .span2 {
    +    width: 104px;
    +  }
    +  .span1 {
    +    width: 42px;
    +  }
    +  .offset12 {
    +    margin-left: 764px;
    +  }
    +  .offset11 {
    +    margin-left: 702px;
    +  }
    +  .offset10 {
    +    margin-left: 640px;
    +  }
    +  .offset9 {
    +    margin-left: 578px;
    +  }
    +  .offset8 {
    +    margin-left: 516px;
    +  }
    +  .offset7 {
    +    margin-left: 454px;
    +  }
    +  .offset6 {
    +    margin-left: 392px;
    +  }
    +  .offset5 {
    +    margin-left: 330px;
    +  }
    +  .offset4 {
    +    margin-left: 268px;
    +  }
    +  .offset3 {
    +    margin-left: 206px;
    +  }
    +  .offset2 {
    +    margin-left: 144px;
    +  }
    +  .offset1 {
    +    margin-left: 82px;
    +  }
    +  .row-fluid {
    +    width: 100%;
    +    *zoom: 1;
    +  }
    +  .row-fluid:before,
    +  .row-fluid:after {
    +    display: table;
    +    line-height: 0;
    +    content: "";
    +  }
    +  .row-fluid:after {
    +    clear: both;
    +  }
    +  .row-fluid [class*="span"] {
    +    display: block;
    +    float: left;
    +    width: 100%;
    +    min-height: 30px;
    +    margin-left: 2.7624309392265194%;
    +    *margin-left: 2.709239449864817%;
    +    -webkit-box-sizing: border-box;
    +       -moz-box-sizing: border-box;
    +            box-sizing: border-box;
    +  }
    +  .row-fluid [class*="span"]:first-child {
    +    margin-left: 0;
    +  }
    +  .row-fluid .controls-row [class*="span"] + [class*="span"] {
    +    margin-left: 2.7624309392265194%;
    +  }
    +  .row-fluid .span12 {
    +    width: 100%;
    +    *width: 99.94680851063829%;
    +  }
    +  .row-fluid .span11 {
    +    width: 91.43646408839778%;
    +    *width: 91.38327259903608%;
    +  }
    +  .row-fluid .span10 {
    +    width: 82.87292817679558%;
    +    *width: 82.81973668743387%;
    +  }
    +  .row-fluid .span9 {
    +    width: 74.30939226519337%;
    +    *width: 74.25620077583166%;
    +  }
    +  .row-fluid .span8 {
    +    width: 65.74585635359117%;
    +    *width: 65.69266486422946%;
    +  }
    +  .row-fluid .span7 {
    +    width: 57.18232044198895%;
    +    *width: 57.12912895262725%;
    +  }
    +  .row-fluid .span6 {
    +    width: 48.61878453038674%;
    +    *width: 48.56559304102504%;
    +  }
    +  .row-fluid .span5 {
    +    width: 40.05524861878453%;
    +    *width: 40.00205712942283%;
    +  }
    +  .row-fluid .span4 {
    +    width: 31.491712707182323%;
    +    *width: 31.43852121782062%;
    +  }
    +  .row-fluid .span3 {
    +    width: 22.92817679558011%;
    +    *width: 22.87498530621841%;
    +  }
    +  .row-fluid .span2 {
    +    width: 14.3646408839779%;
    +    *width: 14.311449394616199%;
    +  }
    +  .row-fluid .span1 {
    +    width: 5.801104972375691%;
    +    *width: 5.747913483013988%;
    +  }
    +  .row-fluid .offset12 {
    +    margin-left: 105.52486187845304%;
    +    *margin-left: 105.41847889972962%;
    +  }
    +  .row-fluid .offset12:first-child {
    +    margin-left: 102.76243093922652%;
    +    *margin-left: 102.6560479605031%;
    +  }
    +  .row-fluid .offset11 {
    +    margin-left: 96.96132596685082%;
    +    *margin-left: 96.8549429881274%;
    +  }
    +  .row-fluid .offset11:first-child {
    +    margin-left: 94.1988950276243%;
    +    *margin-left: 94.09251204890089%;
    +  }
    +  .row-fluid .offset10 {
    +    margin-left: 88.39779005524862%;
    +    *margin-left: 88.2914070765252%;
    +  }
    +  .row-fluid .offset10:first-child {
    +    margin-left: 85.6353591160221%;
    +    *margin-left: 85.52897613729868%;
    +  }
    +  .row-fluid .offset9 {
    +    margin-left: 79.8342541436464%;
    +    *margin-left: 79.72787116492299%;
    +  }
    +  .row-fluid .offset9:first-child {
    +    margin-left: 77.07182320441989%;
    +    *margin-left: 76.96544022569647%;
    +  }
    +  .row-fluid .offset8 {
    +    margin-left: 71.2707182320442%;
    +    *margin-left: 71.16433525332079%;
    +  }
    +  .row-fluid .offset8:first-child {
    +    margin-left: 68.50828729281768%;
    +    *margin-left: 68.40190431409427%;
    +  }
    +  .row-fluid .offset7 {
    +    margin-left: 62.70718232044199%;
    +    *margin-left: 62.600799341718584%;
    +  }
    +  .row-fluid .offset7:first-child {
    +    margin-left: 59.94475138121547%;
    +    *margin-left: 59.838368402492065%;
    +  }
    +  .row-fluid .offset6 {
    +    margin-left: 54.14364640883978%;
    +    *margin-left: 54.037263430116376%;
    +  }
    +  .row-fluid .offset6:first-child {
    +    margin-left: 51.38121546961326%;
    +    *margin-left: 51.27483249088986%;
    +  }
    +  .row-fluid .offset5 {
    +    margin-left: 45.58011049723757%;
    +    *margin-left: 45.47372751851417%;
    +  }
    +  .row-fluid .offset5:first-child {
    +    margin-left: 42.81767955801105%;
    +    *margin-left: 42.71129657928765%;
    +  }
    +  .row-fluid .offset4 {
    +    margin-left: 37.01657458563536%;
    +    *margin-left: 36.91019160691196%;
    +  }
    +  .row-fluid .offset4:first-child {
    +    margin-left: 34.25414364640884%;
    +    *margin-left: 34.14776066768544%;
    +  }
    +  .row-fluid .offset3 {
    +    margin-left: 28.45303867403315%;
    +    *margin-left: 28.346655695309746%;
    +  }
    +  .row-fluid .offset3:first-child {
    +    margin-left: 25.69060773480663%;
    +    *margin-left: 25.584224756083227%;
    +  }
    +  .row-fluid .offset2 {
    +    margin-left: 19.88950276243094%;
    +    *margin-left: 19.783119783707537%;
    +  }
    +  .row-fluid .offset2:first-child {
    +    margin-left: 17.12707182320442%;
    +    *margin-left: 17.02068884448102%;
    +  }
    +  .row-fluid .offset1 {
    +    margin-left: 11.32596685082873%;
    +    *margin-left: 11.219583872105325%;
    +  }
    +  .row-fluid .offset1:first-child {
    +    margin-left: 8.56353591160221%;
    +    *margin-left: 8.457152932878806%;
    +  }
    +  input,
    +  textarea,
    +  .uneditable-input {
    +    margin-left: 0;
    +  }
    +  .controls-row [class*="span"] + [class*="span"] {
    +    margin-left: 20px;
    +  }
    +  input.span12,
    +  textarea.span12,
    +  .uneditable-input.span12 {
    +    width: 710px;
    +  }
    +  input.span11,
    +  textarea.span11,
    +  .uneditable-input.span11 {
    +    width: 648px;
    +  }
    +  input.span10,
    +  textarea.span10,
    +  .uneditable-input.span10 {
    +    width: 586px;
    +  }
    +  input.span9,
    +  textarea.span9,
    +  .uneditable-input.span9 {
    +    width: 524px;
    +  }
    +  input.span8,
    +  textarea.span8,
    +  .uneditable-input.span8 {
    +    width: 462px;
    +  }
    +  input.span7,
    +  textarea.span7,
    +  .uneditable-input.span7 {
    +    width: 400px;
    +  }
    +  input.span6,
    +  textarea.span6,
    +  .uneditable-input.span6 {
    +    width: 338px;
    +  }
    +  input.span5,
    +  textarea.span5,
    +  .uneditable-input.span5 {
    +    width: 276px;
    +  }
    +  input.span4,
    +  textarea.span4,
    +  .uneditable-input.span4 {
    +    width: 214px;
    +  }
    +  input.span3,
    +  textarea.span3,
    +  .uneditable-input.span3 {
    +    width: 152px;
    +  }
    +  input.span2,
    +  textarea.span2,
    +  .uneditable-input.span2 {
    +    width: 90px;
    +  }
    +  input.span1,
    +  textarea.span1,
    +  .uneditable-input.span1 {
    +    width: 28px;
    +  }
    +}
    +
    +@media (max-width: 767px) {
    +  body {
    +    padding-right: 20px;
    +    padding-left: 20px;
    +  }
    +  .navbar-fixed-top,
    +  .navbar-fixed-bottom,
    +  .navbar-static-top {
    +    margin-right: -20px;
    +    margin-left: -20px;
    +  }
    +  .container-fluid {
    +    padding: 0;
    +  }
    +  .dl-horizontal dt {
    +    float: none;
    +    width: auto;
    +    clear: none;
    +    text-align: left;
    +  }
    +  .dl-horizontal dd {
    +    margin-left: 0;
    +  }
    +  .container {
    +    width: auto;
    +  }
    +  .row-fluid {
    +    width: 100%;
    +  }
    +  .row,
    +  .thumbnails {
    +    margin-left: 0;
    +  }
    +  .thumbnails > li {
    +    float: none;
    +    margin-left: 0;
    +  }
    +  [class*="span"],
    +  .uneditable-input[class*="span"],
    +  .row-fluid [class*="span"] {
    +    display: block;
    +    float: none;
    +    width: 100%;
    +    margin-left: 0;
    +    -webkit-box-sizing: border-box;
    +       -moz-box-sizing: border-box;
    +            box-sizing: border-box;
    +  }
    +  .span12,
    +  .row-fluid .span12 {
    +    width: 100%;
    +    -webkit-box-sizing: border-box;
    +       -moz-box-sizing: border-box;
    +            box-sizing: border-box;
    +  }
    +  .row-fluid [class*="offset"]:first-child {
    +    margin-left: 0;
    +  }
    +  .input-large,
    +  .input-xlarge,
    +  .input-xxlarge,
    +  input[class*="span"],
    +  select[class*="span"],
    +  textarea[class*="span"],
    +  .uneditable-input {
    +    display: block;
    +    width: 100%;
    +    min-height: 30px;
    +    -webkit-box-sizing: border-box;
    +       -moz-box-sizing: border-box;
    +            box-sizing: border-box;
    +  }
    +  .input-prepend input,
    +  .input-append input,
    +  .input-prepend input[class*="span"],
    +  .input-append input[class*="span"] {
    +    display: inline-block;
    +    width: auto;
    +  }
    +  .controls-row [class*="span"] + [class*="span"] {
    +    margin-left: 0;
    +  }
    +  .modal {
    +    position: fixed;
    +    top: 20px;
    +    right: 20px;
    +    left: 20px;
    +    width: auto;
    +    margin: 0;
    +  }
    +  .modal.fade {
    +    top: -100px;
    +  }
    +  .modal.fade.in {
    +    top: 20px;
    +  }
    +}
    +
    +@media (max-width: 480px) {
    +  .nav-collapse {
    +    -webkit-transform: translate3d(0, 0, 0);
    +  }
    +  .page-header h1 small {
    +    display: block;
    +    line-height: 20px;
    +  }
    +  input[type="checkbox"],
    +  input[type="radio"] {
    +    border: 1px solid #ccc;
    +  }
    +  .form-horizontal .control-label {
    +    float: none;
    +    width: auto;
    +    padding-top: 0;
    +    text-align: left;
    +  }
    +  .form-horizontal .controls {
    +    margin-left: 0;
    +  }
    +  .form-horizontal .control-list {
    +    padding-top: 0;
    +  }
    +  .form-horizontal .form-actions {
    +    padding-right: 10px;
    +    padding-left: 10px;
    +  }
    +  .media .pull-left,
    +  .media .pull-right {
    +    display: block;
    +    float: none;
    +    margin-bottom: 10px;
    +  }
    +  .media-object {
    +    margin-right: 0;
    +    margin-left: 0;
    +  }
    +  .modal {
    +    top: 10px;
    +    right: 10px;
    +    left: 10px;
    +  }
    +  .modal-header .close {
    +    padding: 10px;
    +    margin: -10px;
    +  }
    +  .carousel-caption {
    +    position: static;
    +  }
    +}
    +
    +@media (max-width: 979px) {
    +  body {
    +    padding-top: 0;
    +  }
    +  .navbar-fixed-top,
    +  .navbar-fixed-bottom {
    +    position: static;
    +  }
    +  .navbar-fixed-top {
    +    margin-bottom: 20px;
    +  }
    +  .navbar-fixed-bottom {
    +    margin-top: 20px;
    +  }
    +  .navbar-fixed-top .navbar-inner,
    +  .navbar-fixed-bottom .navbar-inner {
    +    padding: 5px;
    +  }
    +  .navbar .container {
    +    width: auto;
    +    padding: 0;
    +  }
    +  .navbar .brand {
    +    padding-right: 10px;
    +    padding-left: 10px;
    +    margin: 0 0 0 -5px;
    +  }
    +  .nav-collapse {
    +    clear: both;
    +  }
    +  .nav-collapse .nav {
    +    float: none;
    +    margin: 0 0 10px;
    +  }
    +  .nav-collapse .nav > li {
    +    float: none;
    +  }
    +  .nav-collapse .nav > li > a {
    +    margin-bottom: 2px;
    +  }
    +  .nav-collapse .nav > .divider-vertical {
    +    display: none;
    +  }
    +  .nav-collapse .nav .nav-header {
    +    color: #777777;
    +    text-shadow: none;
    +  }
    +  .nav-collapse .nav > li > a,
    +  .nav-collapse .dropdown-menu a {
    +    padding: 9px 15px;
    +    font-weight: bold;
    +    color: #777777;
    +    -webkit-border-radius: 3px;
    +       -moz-border-radius: 3px;
    +            border-radius: 3px;
    +  }
    +  .nav-collapse .btn {
    +    padding: 4px 10px 4px;
    +    font-weight: normal;
    +    -webkit-border-radius: 4px;
    +       -moz-border-radius: 4px;
    +            border-radius: 4px;
    +  }
    +  .nav-collapse .dropdown-menu li + li a {
    +    margin-bottom: 2px;
    +  }
    +  .nav-collapse .nav > li > a:hover,
    +  .nav-collapse .nav > li > a:focus,
    +  .nav-collapse .dropdown-menu a:hover,
    +  .nav-collapse .dropdown-menu a:focus {
    +    background-color: #f2f2f2;
    +  }
    +  .navbar-inverse .nav-collapse .nav > li > a,
    +  .navbar-inverse .nav-collapse .dropdown-menu a {
    +    color: #999999;
    +  }
    +  .navbar-inverse .nav-collapse .nav > li > a:hover,
    +  .navbar-inverse .nav-collapse .nav > li > a:focus,
    +  .navbar-inverse .nav-collapse .dropdown-menu a:hover,
    +  .navbar-inverse .nav-collapse .dropdown-menu a:focus {
    +    background-color: #111111;
    +  }
    +  .nav-collapse.in .btn-group {
    +    padding: 0;
    +    margin-top: 5px;
    +  }
    +  .nav-collapse .dropdown-menu {
    +    position: static;
    +    top: auto;
    +    left: auto;
    +    display: none;
    +    float: none;
    +    max-width: none;
    +    padding: 0;
    +    margin: 0 15px;
    +    background-color: transparent;
    +    border: none;
    +    -webkit-border-radius: 0;
    +       -moz-border-radius: 0;
    +            border-radius: 0;
    +    -webkit-box-shadow: none;
    +       -moz-box-shadow: none;
    +            box-shadow: none;
    +  }
    +  .nav-collapse .open > .dropdown-menu {
    +    display: block;
    +  }
    +  .nav-collapse .dropdown-menu:before,
    +  .nav-collapse .dropdown-menu:after {
    +    display: none;
    +  }
    +  .nav-collapse .dropdown-menu .divider {
    +    display: none;
    +  }
    +  .nav-collapse .nav > li > .dropdown-menu:before,
    +  .nav-collapse .nav > li > .dropdown-menu:after {
    +    display: none;
    +  }
    +  .nav-collapse .navbar-form,
    +  .nav-collapse .navbar-search {
    +    float: none;
    +    padding: 10px 15px;
    +    margin: 10px 0;
    +    border-top: 1px solid #f2f2f2;
    +    border-bottom: 1px solid #f2f2f2;
    +    -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
    +       -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
    +            box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
    +  }
    +  .navbar-inverse .nav-collapse .navbar-form,
    +  .navbar-inverse .nav-collapse .navbar-search {
    +    border-top-color: #111111;
    +    border-bottom-color: #111111;
    +  }
    +  .navbar .nav-collapse .nav.pull-right {
    +    float: none;
    +    margin-left: 0;
    +  }
    +  .nav-collapse,
    +  .nav-collapse.collapse {
    +    height: 0;
    +    overflow: hidden;
    +  }
    +  .navbar .btn-navbar {
    +    display: block;
    +  }
    +  .navbar-static .navbar-inner {
    +    padding-right: 10px;
    +    padding-left: 10px;
    +  }
    +}
    +
    +@media (min-width: 980px) {
    +  .nav-collapse.collapse {
    +    height: auto !important;
    +    overflow: visible !important;
    +  }
    +}
    diff --git a/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.min.css b/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.min.css
    new file mode 100644
    index 0000000000..d1b7f4b0b8
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/css/bootstrap-responsive.min.css
    @@ -0,0 +1,9 @@
    +/*!
    + * Bootstrap Responsive v2.3.1
    + *
    + * Copyright 2012 Twitter, Inc
    + * Licensed under the Apache License v2.0
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Designed and built with all the love in the world @twitter by @mdo and @fat.
    + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
    diff --git a/mapbox-gl/resources/bootstrap/css/bootstrap.css b/mapbox-gl/resources/bootstrap/css/bootstrap.css
    new file mode 100644
    index 0000000000..2f56af33f3
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/css/bootstrap.css
    @@ -0,0 +1,6158 @@
    +/*!
    + * Bootstrap v2.3.1
    + *
    + * Copyright 2012 Twitter, Inc
    + * Licensed under the Apache License v2.0
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Designed and built with all the love in the world @twitter by @mdo and @fat.
    + */
    +
    +.clearfix {
    +  *zoom: 1;
    +}
    +
    +.clearfix:before,
    +.clearfix:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.clearfix:after {
    +  clear: both;
    +}
    +
    +.hide-text {
    +  font: 0/0 a;
    +  color: transparent;
    +  text-shadow: none;
    +  background-color: transparent;
    +  border: 0;
    +}
    +
    +.input-block-level {
    +  display: block;
    +  width: 100%;
    +  min-height: 30px;
    +  -webkit-box-sizing: border-box;
    +     -moz-box-sizing: border-box;
    +          box-sizing: border-box;
    +}
    +
    +article,
    +aside,
    +details,
    +figcaption,
    +figure,
    +footer,
    +header,
    +hgroup,
    +nav,
    +section {
    +  display: block;
    +}
    +
    +audio,
    +canvas,
    +video {
    +  display: inline-block;
    +  *display: inline;
    +  *zoom: 1;
    +}
    +
    +audio:not([controls]) {
    +  display: none;
    +}
    +
    +html {
    +  font-size: 100%;
    +  -webkit-text-size-adjust: 100%;
    +      -ms-text-size-adjust: 100%;
    +}
    +
    +a:focus {
    +  outline: thin dotted #333;
    +  outline: 5px auto -webkit-focus-ring-color;
    +  outline-offset: -2px;
    +}
    +
    +a:hover,
    +a:active {
    +  outline: 0;
    +}
    +
    +sub,
    +sup {
    +  position: relative;
    +  font-size: 75%;
    +  line-height: 0;
    +  vertical-align: baseline;
    +}
    +
    +sup {
    +  top: -0.5em;
    +}
    +
    +sub {
    +  bottom: -0.25em;
    +}
    +
    +img {
    +  width: auto\9;
    +  height: auto;
    +  max-width: 100%;
    +  vertical-align: middle;
    +  border: 0;
    +  -ms-interpolation-mode: bicubic;
    +}
    +
    +#map_canvas img,
    +.google-maps img {
    +  max-width: none;
    +}
    +
    +button,
    +input,
    +select,
    +textarea {
    +  margin: 0;
    +  font-size: 100%;
    +  vertical-align: middle;
    +}
    +
    +button,
    +input {
    +  *overflow: visible;
    +  line-height: normal;
    +}
    +
    +button::-moz-focus-inner,
    +input::-moz-focus-inner {
    +  padding: 0;
    +  border: 0;
    +}
    +
    +button,
    +html input[type="button"],
    +input[type="reset"],
    +input[type="submit"] {
    +  cursor: pointer;
    +  -webkit-appearance: button;
    +}
    +
    +label,
    +select,
    +button,
    +input[type="button"],
    +input[type="reset"],
    +input[type="submit"],
    +input[type="radio"],
    +input[type="checkbox"] {
    +  cursor: pointer;
    +}
    +
    +input[type="search"] {
    +  -webkit-box-sizing: content-box;
    +     -moz-box-sizing: content-box;
    +          box-sizing: content-box;
    +  -webkit-appearance: textfield;
    +}
    +
    +input[type="search"]::-webkit-search-decoration,
    +input[type="search"]::-webkit-search-cancel-button {
    +  -webkit-appearance: none;
    +}
    +
    +textarea {
    +  overflow: auto;
    +  vertical-align: top;
    +}
    +
    +@media print {
    +  * {
    +    color: #000 !important;
    +    text-shadow: none !important;
    +    background: transparent !important;
    +    box-shadow: none !important;
    +  }
    +  a,
    +  a:visited {
    +    text-decoration: underline;
    +  }
    +  a[href]:after {
    +    content: " (" attr(href) ")";
    +  }
    +  abbr[title]:after {
    +    content: " (" attr(title) ")";
    +  }
    +  .ir a:after,
    +  a[href^="javascript:"]:after,
    +  a[href^="#"]:after {
    +    content: "";
    +  }
    +  pre,
    +  blockquote {
    +    border: 1px solid #999;
    +    page-break-inside: avoid;
    +  }
    +  thead {
    +    display: table-header-group;
    +  }
    +  tr,
    +  img {
    +    page-break-inside: avoid;
    +  }
    +  img {
    +    max-width: 100% !important;
    +  }
    +  @page  {
    +    margin: 0.5cm;
    +  }
    +  p,
    +  h2,
    +  h3 {
    +    orphans: 3;
    +    widows: 3;
    +  }
    +  h2,
    +  h3 {
    +    page-break-after: avoid;
    +  }
    +}
    +
    +body {
    +  margin: 0;
    +  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    +  font-size: 14px;
    +  line-height: 20px;
    +  color: #333333;
    +  background-color: #ffffff;
    +}
    +
    +a {
    +  color: #0088cc;
    +  text-decoration: none;
    +}
    +
    +a:hover,
    +a:focus {
    +  color: #005580;
    +  text-decoration: underline;
    +}
    +
    +.img-rounded {
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +}
    +
    +.img-polaroid {
    +  padding: 4px;
    +  background-color: #fff;
    +  border: 1px solid #ccc;
    +  border: 1px solid rgba(0, 0, 0, 0.2);
    +  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    +     -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    +          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    +}
    +
    +.img-circle {
    +  -webkit-border-radius: 500px;
    +     -moz-border-radius: 500px;
    +          border-radius: 500px;
    +}
    +
    +.row {
    +  margin-left: -20px;
    +  *zoom: 1;
    +}
    +
    +.row:before,
    +.row:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.row:after {
    +  clear: both;
    +}
    +
    +[class*="span"] {
    +  float: left;
    +  min-height: 1px;
    +  margin-left: 20px;
    +}
    +
    +.container,
    +.navbar-static-top .container,
    +.navbar-fixed-top .container,
    +.navbar-fixed-bottom .container {
    +  width: 940px;
    +}
    +
    +.span12 {
    +  width: 940px;
    +}
    +
    +.span11 {
    +  width: 860px;
    +}
    +
    +.span10 {
    +  width: 780px;
    +}
    +
    +.span9 {
    +  width: 700px;
    +}
    +
    +.span8 {
    +  width: 620px;
    +}
    +
    +.span7 {
    +  width: 540px;
    +}
    +
    +.span6 {
    +  width: 460px;
    +}
    +
    +.span5 {
    +  width: 380px;
    +}
    +
    +.span4 {
    +  width: 300px;
    +}
    +
    +.span3 {
    +  width: 220px;
    +}
    +
    +.span2 {
    +  width: 140px;
    +}
    +
    +.span1 {
    +  width: 60px;
    +}
    +
    +.offset12 {
    +  margin-left: 980px;
    +}
    +
    +.offset11 {
    +  margin-left: 900px;
    +}
    +
    +.offset10 {
    +  margin-left: 820px;
    +}
    +
    +.offset9 {
    +  margin-left: 740px;
    +}
    +
    +.offset8 {
    +  margin-left: 660px;
    +}
    +
    +.offset7 {
    +  margin-left: 580px;
    +}
    +
    +.offset6 {
    +  margin-left: 500px;
    +}
    +
    +.offset5 {
    +  margin-left: 420px;
    +}
    +
    +.offset4 {
    +  margin-left: 340px;
    +}
    +
    +.offset3 {
    +  margin-left: 260px;
    +}
    +
    +.offset2 {
    +  margin-left: 180px;
    +}
    +
    +.offset1 {
    +  margin-left: 100px;
    +}
    +
    +.row-fluid {
    +  width: 100%;
    +  *zoom: 1;
    +}
    +
    +.row-fluid:before,
    +.row-fluid:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.row-fluid:after {
    +  clear: both;
    +}
    +
    +.row-fluid [class*="span"] {
    +  display: block;
    +  float: left;
    +  width: 100%;
    +  min-height: 30px;
    +  margin-left: 2.127659574468085%;
    +  *margin-left: 2.074468085106383%;
    +  -webkit-box-sizing: border-box;
    +     -moz-box-sizing: border-box;
    +          box-sizing: border-box;
    +}
    +
    +.row-fluid [class*="span"]:first-child {
    +  margin-left: 0;
    +}
    +
    +.row-fluid .controls-row [class*="span"] + [class*="span"] {
    +  margin-left: 2.127659574468085%;
    +}
    +
    +.row-fluid .span12 {
    +  width: 100%;
    +  *width: 99.94680851063829%;
    +}
    +
    +.row-fluid .span11 {
    +  width: 91.48936170212765%;
    +  *width: 91.43617021276594%;
    +}
    +
    +.row-fluid .span10 {
    +  width: 82.97872340425532%;
    +  *width: 82.92553191489361%;
    +}
    +
    +.row-fluid .span9 {
    +  width: 74.46808510638297%;
    +  *width: 74.41489361702126%;
    +}
    +
    +.row-fluid .span8 {
    +  width: 65.95744680851064%;
    +  *width: 65.90425531914893%;
    +}
    +
    +.row-fluid .span7 {
    +  width: 57.44680851063829%;
    +  *width: 57.39361702127659%;
    +}
    +
    +.row-fluid .span6 {
    +  width: 48.93617021276595%;
    +  *width: 48.88297872340425%;
    +}
    +
    +.row-fluid .span5 {
    +  width: 40.42553191489362%;
    +  *width: 40.37234042553192%;
    +}
    +
    +.row-fluid .span4 {
    +  width: 31.914893617021278%;
    +  *width: 31.861702127659576%;
    +}
    +
    +.row-fluid .span3 {
    +  width: 23.404255319148934%;
    +  *width: 23.351063829787233%;
    +}
    +
    +.row-fluid .span2 {
    +  width: 14.893617021276595%;
    +  *width: 14.840425531914894%;
    +}
    +
    +.row-fluid .span1 {
    +  width: 6.382978723404255%;
    +  *width: 6.329787234042553%;
    +}
    +
    +.row-fluid .offset12 {
    +  margin-left: 104.25531914893617%;
    +  *margin-left: 104.14893617021275%;
    +}
    +
    +.row-fluid .offset12:first-child {
    +  margin-left: 102.12765957446808%;
    +  *margin-left: 102.02127659574467%;
    +}
    +
    +.row-fluid .offset11 {
    +  margin-left: 95.74468085106382%;
    +  *margin-left: 95.6382978723404%;
    +}
    +
    +.row-fluid .offset11:first-child {
    +  margin-left: 93.61702127659574%;
    +  *margin-left: 93.51063829787232%;
    +}
    +
    +.row-fluid .offset10 {
    +  margin-left: 87.23404255319149%;
    +  *margin-left: 87.12765957446807%;
    +}
    +
    +.row-fluid .offset10:first-child {
    +  margin-left: 85.1063829787234%;
    +  *margin-left: 84.99999999999999%;
    +}
    +
    +.row-fluid .offset9 {
    +  margin-left: 78.72340425531914%;
    +  *margin-left: 78.61702127659572%;
    +}
    +
    +.row-fluid .offset9:first-child {
    +  margin-left: 76.59574468085106%;
    +  *margin-left: 76.48936170212764%;
    +}
    +
    +.row-fluid .offset8 {
    +  margin-left: 70.2127659574468%;
    +  *margin-left: 70.10638297872339%;
    +}
    +
    +.row-fluid .offset8:first-child {
    +  margin-left: 68.08510638297872%;
    +  *margin-left: 67.9787234042553%;
    +}
    +
    +.row-fluid .offset7 {
    +  margin-left: 61.70212765957446%;
    +  *margin-left: 61.59574468085106%;
    +}
    +
    +.row-fluid .offset7:first-child {
    +  margin-left: 59.574468085106375%;
    +  *margin-left: 59.46808510638297%;
    +}
    +
    +.row-fluid .offset6 {
    +  margin-left: 53.191489361702125%;
    +  *margin-left: 53.085106382978715%;
    +}
    +
    +.row-fluid .offset6:first-child {
    +  margin-left: 51.063829787234035%;
    +  *margin-left: 50.95744680851063%;
    +}
    +
    +.row-fluid .offset5 {
    +  margin-left: 44.68085106382979%;
    +  *margin-left: 44.57446808510638%;
    +}
    +
    +.row-fluid .offset5:first-child {
    +  margin-left: 42.5531914893617%;
    +  *margin-left: 42.4468085106383%;
    +}
    +
    +.row-fluid .offset4 {
    +  margin-left: 36.170212765957444%;
    +  *margin-left: 36.06382978723405%;
    +}
    +
    +.row-fluid .offset4:first-child {
    +  margin-left: 34.04255319148936%;
    +  *margin-left: 33.93617021276596%;
    +}
    +
    +.row-fluid .offset3 {
    +  margin-left: 27.659574468085104%;
    +  *margin-left: 27.5531914893617%;
    +}
    +
    +.row-fluid .offset3:first-child {
    +  margin-left: 25.53191489361702%;
    +  *margin-left: 25.425531914893618%;
    +}
    +
    +.row-fluid .offset2 {
    +  margin-left: 19.148936170212764%;
    +  *margin-left: 19.04255319148936%;
    +}
    +
    +.row-fluid .offset2:first-child {
    +  margin-left: 17.02127659574468%;
    +  *margin-left: 16.914893617021278%;
    +}
    +
    +.row-fluid .offset1 {
    +  margin-left: 10.638297872340425%;
    +  *margin-left: 10.53191489361702%;
    +}
    +
    +.row-fluid .offset1:first-child {
    +  margin-left: 8.51063829787234%;
    +  *margin-left: 8.404255319148938%;
    +}
    +
    +[class*="span"].hide,
    +.row-fluid [class*="span"].hide {
    +  display: none;
    +}
    +
    +[class*="span"].pull-right,
    +.row-fluid [class*="span"].pull-right {
    +  float: right;
    +}
    +
    +.container {
    +  margin-right: auto;
    +  margin-left: auto;
    +  *zoom: 1;
    +}
    +
    +.container:before,
    +.container:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.container:after {
    +  clear: both;
    +}
    +
    +.container-fluid {
    +  padding-right: 20px;
    +  padding-left: 20px;
    +  *zoom: 1;
    +}
    +
    +.container-fluid:before,
    +.container-fluid:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.container-fluid:after {
    +  clear: both;
    +}
    +
    +p {
    +  margin: 0 0 10px;
    +}
    +
    +.lead {
    +  margin-bottom: 20px;
    +  font-size: 21px;
    +  font-weight: 200;
    +  line-height: 30px;
    +}
    +
    +small {
    +  font-size: 85%;
    +}
    +
    +strong {
    +  font-weight: bold;
    +}
    +
    +em {
    +  font-style: italic;
    +}
    +
    +cite {
    +  font-style: normal;
    +}
    +
    +.muted {
    +  color: #999999;
    +}
    +
    +a.muted:hover,
    +a.muted:focus {
    +  color: #808080;
    +}
    +
    +.text-warning {
    +  color: #c09853;
    +}
    +
    +a.text-warning:hover,
    +a.text-warning:focus {
    +  color: #a47e3c;
    +}
    +
    +.text-error {
    +  color: #b94a48;
    +}
    +
    +a.text-error:hover,
    +a.text-error:focus {
    +  color: #953b39;
    +}
    +
    +.text-info {
    +  color: #3a87ad;
    +}
    +
    +a.text-info:hover,
    +a.text-info:focus {
    +  color: #2d6987;
    +}
    +
    +.text-success {
    +  color: #468847;
    +}
    +
    +a.text-success:hover,
    +a.text-success:focus {
    +  color: #356635;
    +}
    +
    +.text-left {
    +  text-align: left;
    +}
    +
    +.text-right {
    +  text-align: right;
    +}
    +
    +.text-center {
    +  text-align: center;
    +}
    +
    +h1,
    +h2,
    +h3,
    +h4,
    +h5,
    +h6 {
    +  margin: 10px 0;
    +  font-family: inherit;
    +  font-weight: bold;
    +  line-height: 20px;
    +  color: inherit;
    +  text-rendering: optimizelegibility;
    +}
    +
    +h1 small,
    +h2 small,
    +h3 small,
    +h4 small,
    +h5 small,
    +h6 small {
    +  font-weight: normal;
    +  line-height: 1;
    +  color: #999999;
    +}
    +
    +h1,
    +h2,
    +h3 {
    +  line-height: 40px;
    +}
    +
    +h1 {
    +  font-size: 38.5px;
    +}
    +
    +h2 {
    +  font-size: 31.5px;
    +}
    +
    +h3 {
    +  font-size: 24.5px;
    +}
    +
    +h4 {
    +  font-size: 17.5px;
    +}
    +
    +h5 {
    +  font-size: 14px;
    +}
    +
    +h6 {
    +  font-size: 11.9px;
    +}
    +
    +h1 small {
    +  font-size: 24.5px;
    +}
    +
    +h2 small {
    +  font-size: 17.5px;
    +}
    +
    +h3 small {
    +  font-size: 14px;
    +}
    +
    +h4 small {
    +  font-size: 14px;
    +}
    +
    +.page-header {
    +  padding-bottom: 9px;
    +  margin: 20px 0 30px;
    +  border-bottom: 1px solid #eeeeee;
    +}
    +
    +ul,
    +ol {
    +  padding: 0;
    +  margin: 0 0 10px 25px;
    +}
    +
    +ul ul,
    +ul ol,
    +ol ol,
    +ol ul {
    +  margin-bottom: 0;
    +}
    +
    +li {
    +  line-height: 20px;
    +}
    +
    +ul.unstyled,
    +ol.unstyled {
    +  margin-left: 0;
    +  list-style: none;
    +}
    +
    +ul.inline,
    +ol.inline {
    +  margin-left: 0;
    +  list-style: none;
    +}
    +
    +ul.inline > li,
    +ol.inline > li {
    +  display: inline-block;
    +  *display: inline;
    +  padding-right: 5px;
    +  padding-left: 5px;
    +  *zoom: 1;
    +}
    +
    +dl {
    +  margin-bottom: 20px;
    +}
    +
    +dt,
    +dd {
    +  line-height: 20px;
    +}
    +
    +dt {
    +  font-weight: bold;
    +}
    +
    +dd {
    +  margin-left: 10px;
    +}
    +
    +.dl-horizontal {
    +  *zoom: 1;
    +}
    +
    +.dl-horizontal:before,
    +.dl-horizontal:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.dl-horizontal:after {
    +  clear: both;
    +}
    +
    +.dl-horizontal dt {
    +  float: left;
    +  width: 160px;
    +  overflow: hidden;
    +  clear: left;
    +  text-align: right;
    +  text-overflow: ellipsis;
    +  white-space: nowrap;
    +}
    +
    +.dl-horizontal dd {
    +  margin-left: 180px;
    +}
    +
    +hr {
    +  margin: 20px 0;
    +  border: 0;
    +  border-top: 1px solid #eeeeee;
    +  border-bottom: 1px solid #ffffff;
    +}
    +
    +abbr[title],
    +abbr[data-original-title] {
    +  cursor: help;
    +  border-bottom: 1px dotted #999999;
    +}
    +
    +abbr.initialism {
    +  font-size: 90%;
    +  text-transform: uppercase;
    +}
    +
    +blockquote {
    +  padding: 0 0 0 15px;
    +  margin: 0 0 20px;
    +  border-left: 5px solid #eeeeee;
    +}
    +
    +blockquote p {
    +  margin-bottom: 0;
    +  font-size: 17.5px;
    +  font-weight: 300;
    +  line-height: 1.25;
    +}
    +
    +blockquote small {
    +  display: block;
    +  line-height: 20px;
    +  color: #999999;
    +}
    +
    +blockquote small:before {
    +  content: '\2014 \00A0';
    +}
    +
    +blockquote.pull-right {
    +  float: right;
    +  padding-right: 15px;
    +  padding-left: 0;
    +  border-right: 5px solid #eeeeee;
    +  border-left: 0;
    +}
    +
    +blockquote.pull-right p,
    +blockquote.pull-right small {
    +  text-align: right;
    +}
    +
    +blockquote.pull-right small:before {
    +  content: '';
    +}
    +
    +blockquote.pull-right small:after {
    +  content: '\00A0 \2014';
    +}
    +
    +q:before,
    +q:after,
    +blockquote:before,
    +blockquote:after {
    +  content: "";
    +}
    +
    +address {
    +  display: block;
    +  margin-bottom: 20px;
    +  font-style: normal;
    +  line-height: 20px;
    +}
    +
    +code,
    +pre {
    +  padding: 0 3px 2px;
    +  font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
    +  font-size: 12px;
    +  color: #333333;
    +  -webkit-border-radius: 3px;
    +     -moz-border-radius: 3px;
    +          border-radius: 3px;
    +}
    +
    +code {
    +  padding: 2px 4px;
    +  color: #d14;
    +  white-space: nowrap;
    +  background-color: #f7f7f9;
    +  border: 1px solid #e1e1e8;
    +}
    +
    +pre {
    +  display: block;
    +  padding: 9.5px;
    +  margin: 0 0 10px;
    +  font-size: 13px;
    +  line-height: 20px;
    +  word-break: break-all;
    +  word-wrap: break-word;
    +  white-space: pre;
    +  white-space: pre-wrap;
    +  background-color: #f5f5f5;
    +  border: 1px solid #ccc;
    +  border: 1px solid rgba(0, 0, 0, 0.15);
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +pre.prettyprint {
    +  margin-bottom: 20px;
    +}
    +
    +pre code {
    +  padding: 0;
    +  color: inherit;
    +  white-space: pre;
    +  white-space: pre-wrap;
    +  background-color: transparent;
    +  border: 0;
    +}
    +
    +.pre-scrollable {
    +  max-height: 340px;
    +  overflow-y: scroll;
    +}
    +
    +form {
    +  margin: 0 0 20px;
    +}
    +
    +fieldset {
    +  padding: 0;
    +  margin: 0;
    +  border: 0;
    +}
    +
    +legend {
    +  display: block;
    +  width: 100%;
    +  padding: 0;
    +  margin-bottom: 20px;
    +  font-size: 21px;
    +  line-height: 40px;
    +  color: #333333;
    +  border: 0;
    +  border-bottom: 1px solid #e5e5e5;
    +}
    +
    +legend small {
    +  font-size: 15px;
    +  color: #999999;
    +}
    +
    +label,
    +input,
    +button,
    +select,
    +textarea {
    +  font-size: 14px;
    +  font-weight: normal;
    +  line-height: 20px;
    +}
    +
    +input,
    +button,
    +select,
    +textarea {
    +  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    +}
    +
    +label {
    +  display: block;
    +  margin-bottom: 5px;
    +}
    +
    +select,
    +textarea,
    +input[type="text"],
    +input[type="password"],
    +input[type="datetime"],
    +input[type="datetime-local"],
    +input[type="date"],
    +input[type="month"],
    +input[type="time"],
    +input[type="week"],
    +input[type="number"],
    +input[type="email"],
    +input[type="url"],
    +input[type="search"],
    +input[type="tel"],
    +input[type="color"],
    +.uneditable-input {
    +  display: inline-block;
    +  height: 20px;
    +  padding: 4px 6px;
    +  margin-bottom: 10px;
    +  font-size: 14px;
    +  line-height: 20px;
    +  color: #555555;
    +  vertical-align: middle;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +input,
    +textarea,
    +.uneditable-input {
    +  width: 206px;
    +}
    +
    +textarea {
    +  height: auto;
    +}
    +
    +textarea,
    +input[type="text"],
    +input[type="password"],
    +input[type="datetime"],
    +input[type="datetime-local"],
    +input[type="date"],
    +input[type="month"],
    +input[type="time"],
    +input[type="week"],
    +input[type="number"],
    +input[type="email"],
    +input[type="url"],
    +input[type="search"],
    +input[type="tel"],
    +input[type="color"],
    +.uneditable-input {
    +  background-color: #ffffff;
    +  border: 1px solid #cccccc;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +  -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
    +     -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
    +       -o-transition: border linear 0.2s, box-shadow linear 0.2s;
    +          transition: border linear 0.2s, box-shadow linear 0.2s;
    +}
    +
    +textarea:focus,
    +input[type="text"]:focus,
    +input[type="password"]:focus,
    +input[type="datetime"]:focus,
    +input[type="datetime-local"]:focus,
    +input[type="date"]:focus,
    +input[type="month"]:focus,
    +input[type="time"]:focus,
    +input[type="week"]:focus,
    +input[type="number"]:focus,
    +input[type="email"]:focus,
    +input[type="url"]:focus,
    +input[type="search"]:focus,
    +input[type="tel"]:focus,
    +input[type="color"]:focus,
    +.uneditable-input:focus {
    +  border-color: rgba(82, 168, 236, 0.8);
    +  outline: 0;
    +  outline: thin dotted \9;
    +  /* IE6-9 */
    +
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
    +}
    +
    +input[type="radio"],
    +input[type="checkbox"] {
    +  margin: 4px 0 0;
    +  margin-top: 1px \9;
    +  *margin-top: 0;
    +  line-height: normal;
    +}
    +
    +input[type="file"],
    +input[type="image"],
    +input[type="submit"],
    +input[type="reset"],
    +input[type="button"],
    +input[type="radio"],
    +input[type="checkbox"] {
    +  width: auto;
    +}
    +
    +select,
    +input[type="file"] {
    +  height: 30px;
    +  /* In IE7, the height of the select element cannot be changed by height, only font-size */
    +
    +  *margin-top: 4px;
    +  /* For IE7, add top margin to align select with labels */
    +
    +  line-height: 30px;
    +}
    +
    +select {
    +  width: 220px;
    +  background-color: #ffffff;
    +  border: 1px solid #cccccc;
    +}
    +
    +select[multiple],
    +select[size] {
    +  height: auto;
    +}
    +
    +select:focus,
    +input[type="file"]:focus,
    +input[type="radio"]:focus,
    +input[type="checkbox"]:focus {
    +  outline: thin dotted #333;
    +  outline: 5px auto -webkit-focus-ring-color;
    +  outline-offset: -2px;
    +}
    +
    +.uneditable-input,
    +.uneditable-textarea {
    +  color: #999999;
    +  cursor: not-allowed;
    +  background-color: #fcfcfc;
    +  border-color: #cccccc;
    +  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
    +     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
    +          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
    +}
    +
    +.uneditable-input {
    +  overflow: hidden;
    +  white-space: nowrap;
    +}
    +
    +.uneditable-textarea {
    +  width: auto;
    +  height: auto;
    +}
    +
    +input:-moz-placeholder,
    +textarea:-moz-placeholder {
    +  color: #999999;
    +}
    +
    +input:-ms-input-placeholder,
    +textarea:-ms-input-placeholder {
    +  color: #999999;
    +}
    +
    +input::-webkit-input-placeholder,
    +textarea::-webkit-input-placeholder {
    +  color: #999999;
    +}
    +
    +.radio,
    +.checkbox {
    +  min-height: 20px;
    +  padding-left: 20px;
    +}
    +
    +.radio input[type="radio"],
    +.checkbox input[type="checkbox"] {
    +  float: left;
    +  margin-left: -20px;
    +}
    +
    +.controls > .radio:first-child,
    +.controls > .checkbox:first-child {
    +  padding-top: 5px;
    +}
    +
    +.radio.inline,
    +.checkbox.inline {
    +  display: inline-block;
    +  padding-top: 5px;
    +  margin-bottom: 0;
    +  vertical-align: middle;
    +}
    +
    +.radio.inline + .radio.inline,
    +.checkbox.inline + .checkbox.inline {
    +  margin-left: 10px;
    +}
    +
    +.input-mini {
    +  width: 60px;
    +}
    +
    +.input-small {
    +  width: 90px;
    +}
    +
    +.input-medium {
    +  width: 150px;
    +}
    +
    +.input-large {
    +  width: 210px;
    +}
    +
    +.input-xlarge {
    +  width: 270px;
    +}
    +
    +.input-xxlarge {
    +  width: 530px;
    +}
    +
    +input[class*="span"],
    +select[class*="span"],
    +textarea[class*="span"],
    +.uneditable-input[class*="span"],
    +.row-fluid input[class*="span"],
    +.row-fluid select[class*="span"],
    +.row-fluid textarea[class*="span"],
    +.row-fluid .uneditable-input[class*="span"] {
    +  float: none;
    +  margin-left: 0;
    +}
    +
    +.input-append input[class*="span"],
    +.input-append .uneditable-input[class*="span"],
    +.input-prepend input[class*="span"],
    +.input-prepend .uneditable-input[class*="span"],
    +.row-fluid input[class*="span"],
    +.row-fluid select[class*="span"],
    +.row-fluid textarea[class*="span"],
    +.row-fluid .uneditable-input[class*="span"],
    +.row-fluid .input-prepend [class*="span"],
    +.row-fluid .input-append [class*="span"] {
    +  display: inline-block;
    +}
    +
    +input,
    +textarea,
    +.uneditable-input {
    +  margin-left: 0;
    +}
    +
    +.controls-row [class*="span"] + [class*="span"] {
    +  margin-left: 20px;
    +}
    +
    +input.span12,
    +textarea.span12,
    +.uneditable-input.span12 {
    +  width: 926px;
    +}
    +
    +input.span11,
    +textarea.span11,
    +.uneditable-input.span11 {
    +  width: 846px;
    +}
    +
    +input.span10,
    +textarea.span10,
    +.uneditable-input.span10 {
    +  width: 766px;
    +}
    +
    +input.span9,
    +textarea.span9,
    +.uneditable-input.span9 {
    +  width: 686px;
    +}
    +
    +input.span8,
    +textarea.span8,
    +.uneditable-input.span8 {
    +  width: 606px;
    +}
    +
    +input.span7,
    +textarea.span7,
    +.uneditable-input.span7 {
    +  width: 526px;
    +}
    +
    +input.span6,
    +textarea.span6,
    +.uneditable-input.span6 {
    +  width: 446px;
    +}
    +
    +input.span5,
    +textarea.span5,
    +.uneditable-input.span5 {
    +  width: 366px;
    +}
    +
    +input.span4,
    +textarea.span4,
    +.uneditable-input.span4 {
    +  width: 286px;
    +}
    +
    +input.span3,
    +textarea.span3,
    +.uneditable-input.span3 {
    +  width: 206px;
    +}
    +
    +input.span2,
    +textarea.span2,
    +.uneditable-input.span2 {
    +  width: 126px;
    +}
    +
    +input.span1,
    +textarea.span1,
    +.uneditable-input.span1 {
    +  width: 46px;
    +}
    +
    +.controls-row {
    +  *zoom: 1;
    +}
    +
    +.controls-row:before,
    +.controls-row:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.controls-row:after {
    +  clear: both;
    +}
    +
    +.controls-row [class*="span"],
    +.row-fluid .controls-row [class*="span"] {
    +  float: left;
    +}
    +
    +.controls-row .checkbox[class*="span"],
    +.controls-row .radio[class*="span"] {
    +  padding-top: 5px;
    +}
    +
    +input[disabled],
    +select[disabled],
    +textarea[disabled],
    +input[readonly],
    +select[readonly],
    +textarea[readonly] {
    +  cursor: not-allowed;
    +  background-color: #eeeeee;
    +}
    +
    +input[type="radio"][disabled],
    +input[type="checkbox"][disabled],
    +input[type="radio"][readonly],
    +input[type="checkbox"][readonly] {
    +  background-color: transparent;
    +}
    +
    +.control-group.warning .control-label,
    +.control-group.warning .help-block,
    +.control-group.warning .help-inline {
    +  color: #c09853;
    +}
    +
    +.control-group.warning .checkbox,
    +.control-group.warning .radio,
    +.control-group.warning input,
    +.control-group.warning select,
    +.control-group.warning textarea {
    +  color: #c09853;
    +}
    +
    +.control-group.warning input,
    +.control-group.warning select,
    +.control-group.warning textarea {
    +  border-color: #c09853;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +}
    +
    +.control-group.warning input:focus,
    +.control-group.warning select:focus,
    +.control-group.warning textarea:focus {
    +  border-color: #a47e3c;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
    +}
    +
    +.control-group.warning .input-prepend .add-on,
    +.control-group.warning .input-append .add-on {
    +  color: #c09853;
    +  background-color: #fcf8e3;
    +  border-color: #c09853;
    +}
    +
    +.control-group.error .control-label,
    +.control-group.error .help-block,
    +.control-group.error .help-inline {
    +  color: #b94a48;
    +}
    +
    +.control-group.error .checkbox,
    +.control-group.error .radio,
    +.control-group.error input,
    +.control-group.error select,
    +.control-group.error textarea {
    +  color: #b94a48;
    +}
    +
    +.control-group.error input,
    +.control-group.error select,
    +.control-group.error textarea {
    +  border-color: #b94a48;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +}
    +
    +.control-group.error input:focus,
    +.control-group.error select:focus,
    +.control-group.error textarea:focus {
    +  border-color: #953b39;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
    +}
    +
    +.control-group.error .input-prepend .add-on,
    +.control-group.error .input-append .add-on {
    +  color: #b94a48;
    +  background-color: #f2dede;
    +  border-color: #b94a48;
    +}
    +
    +.control-group.success .control-label,
    +.control-group.success .help-block,
    +.control-group.success .help-inline {
    +  color: #468847;
    +}
    +
    +.control-group.success .checkbox,
    +.control-group.success .radio,
    +.control-group.success input,
    +.control-group.success select,
    +.control-group.success textarea {
    +  color: #468847;
    +}
    +
    +.control-group.success input,
    +.control-group.success select,
    +.control-group.success textarea {
    +  border-color: #468847;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +}
    +
    +.control-group.success input:focus,
    +.control-group.success select:focus,
    +.control-group.success textarea:focus {
    +  border-color: #356635;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
    +}
    +
    +.control-group.success .input-prepend .add-on,
    +.control-group.success .input-append .add-on {
    +  color: #468847;
    +  background-color: #dff0d8;
    +  border-color: #468847;
    +}
    +
    +.control-group.info .control-label,
    +.control-group.info .help-block,
    +.control-group.info .help-inline {
    +  color: #3a87ad;
    +}
    +
    +.control-group.info .checkbox,
    +.control-group.info .radio,
    +.control-group.info input,
    +.control-group.info select,
    +.control-group.info textarea {
    +  color: #3a87ad;
    +}
    +
    +.control-group.info input,
    +.control-group.info select,
    +.control-group.info textarea {
    +  border-color: #3a87ad;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    +}
    +
    +.control-group.info input:focus,
    +.control-group.info select:focus,
    +.control-group.info textarea:focus {
    +  border-color: #2d6987;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3;
    +}
    +
    +.control-group.info .input-prepend .add-on,
    +.control-group.info .input-append .add-on {
    +  color: #3a87ad;
    +  background-color: #d9edf7;
    +  border-color: #3a87ad;
    +}
    +
    +input:focus:invalid,
    +textarea:focus:invalid,
    +select:focus:invalid {
    +  color: #b94a48;
    +  border-color: #ee5f5b;
    +}
    +
    +input:focus:invalid:focus,
    +textarea:focus:invalid:focus,
    +select:focus:invalid:focus {
    +  border-color: #e9322d;
    +  -webkit-box-shadow: 0 0 6px #f8b9b7;
    +     -moz-box-shadow: 0 0 6px #f8b9b7;
    +          box-shadow: 0 0 6px #f8b9b7;
    +}
    +
    +.form-actions {
    +  padding: 19px 20px 20px;
    +  margin-top: 20px;
    +  margin-bottom: 20px;
    +  background-color: #f5f5f5;
    +  border-top: 1px solid #e5e5e5;
    +  *zoom: 1;
    +}
    +
    +.form-actions:before,
    +.form-actions:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.form-actions:after {
    +  clear: both;
    +}
    +
    +.help-block,
    +.help-inline {
    +  color: #595959;
    +}
    +
    +.help-block {
    +  display: block;
    +  margin-bottom: 10px;
    +}
    +
    +.help-inline {
    +  display: inline-block;
    +  *display: inline;
    +  padding-left: 5px;
    +  vertical-align: middle;
    +  *zoom: 1;
    +}
    +
    +.input-append,
    +.input-prepend {
    +  display: inline-block;
    +  margin-bottom: 10px;
    +  font-size: 0;
    +  white-space: nowrap;
    +  vertical-align: middle;
    +}
    +
    +.input-append input,
    +.input-prepend input,
    +.input-append select,
    +.input-prepend select,
    +.input-append .uneditable-input,
    +.input-prepend .uneditable-input,
    +.input-append .dropdown-menu,
    +.input-prepend .dropdown-menu,
    +.input-append .popover,
    +.input-prepend .popover {
    +  font-size: 14px;
    +}
    +
    +.input-append input,
    +.input-prepend input,
    +.input-append select,
    +.input-prepend select,
    +.input-append .uneditable-input,
    +.input-prepend .uneditable-input {
    +  position: relative;
    +  margin-bottom: 0;
    +  *margin-left: 0;
    +  vertical-align: top;
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.input-append input:focus,
    +.input-prepend input:focus,
    +.input-append select:focus,
    +.input-prepend select:focus,
    +.input-append .uneditable-input:focus,
    +.input-prepend .uneditable-input:focus {
    +  z-index: 2;
    +}
    +
    +.input-append .add-on,
    +.input-prepend .add-on {
    +  display: inline-block;
    +  width: auto;
    +  height: 20px;
    +  min-width: 16px;
    +  padding: 4px 5px;
    +  font-size: 14px;
    +  font-weight: normal;
    +  line-height: 20px;
    +  text-align: center;
    +  text-shadow: 0 1px 0 #ffffff;
    +  background-color: #eeeeee;
    +  border: 1px solid #ccc;
    +}
    +
    +.input-append .add-on,
    +.input-prepend .add-on,
    +.input-append .btn,
    +.input-prepend .btn,
    +.input-append .btn-group > .dropdown-toggle,
    +.input-prepend .btn-group > .dropdown-toggle {
    +  vertical-align: top;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.input-append .active,
    +.input-prepend .active {
    +  background-color: #a9dba9;
    +  border-color: #46a546;
    +}
    +
    +.input-prepend .add-on,
    +.input-prepend .btn {
    +  margin-right: -1px;
    +}
    +
    +.input-prepend .add-on:first-child,
    +.input-prepend .btn:first-child {
    +  -webkit-border-radius: 4px 0 0 4px;
    +     -moz-border-radius: 4px 0 0 4px;
    +          border-radius: 4px 0 0 4px;
    +}
    +
    +.input-append input,
    +.input-append select,
    +.input-append .uneditable-input {
    +  -webkit-border-radius: 4px 0 0 4px;
    +     -moz-border-radius: 4px 0 0 4px;
    +          border-radius: 4px 0 0 4px;
    +}
    +
    +.input-append input + .btn-group .btn:last-child,
    +.input-append select + .btn-group .btn:last-child,
    +.input-append .uneditable-input + .btn-group .btn:last-child {
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.input-append .add-on,
    +.input-append .btn,
    +.input-append .btn-group {
    +  margin-left: -1px;
    +}
    +
    +.input-append .add-on:last-child,
    +.input-append .btn:last-child,
    +.input-append .btn-group:last-child > .dropdown-toggle {
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.input-prepend.input-append input,
    +.input-prepend.input-append select,
    +.input-prepend.input-append .uneditable-input {
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.input-prepend.input-append input + .btn-group .btn,
    +.input-prepend.input-append select + .btn-group .btn,
    +.input-prepend.input-append .uneditable-input + .btn-group .btn {
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.input-prepend.input-append .add-on:first-child,
    +.input-prepend.input-append .btn:first-child {
    +  margin-right: -1px;
    +  -webkit-border-radius: 4px 0 0 4px;
    +     -moz-border-radius: 4px 0 0 4px;
    +          border-radius: 4px 0 0 4px;
    +}
    +
    +.input-prepend.input-append .add-on:last-child,
    +.input-prepend.input-append .btn:last-child {
    +  margin-left: -1px;
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.input-prepend.input-append .btn-group:first-child {
    +  margin-left: 0;
    +}
    +
    +input.search-query {
    +  padding-right: 14px;
    +  padding-right: 4px \9;
    +  padding-left: 14px;
    +  padding-left: 4px \9;
    +  /* IE7-8 doesn't have border-radius, so don't indent the padding */
    +
    +  margin-bottom: 0;
    +  -webkit-border-radius: 15px;
    +     -moz-border-radius: 15px;
    +          border-radius: 15px;
    +}
    +
    +/* Allow for input prepend/append in search forms */
    +
    +.form-search .input-append .search-query,
    +.form-search .input-prepend .search-query {
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.form-search .input-append .search-query {
    +  -webkit-border-radius: 14px 0 0 14px;
    +     -moz-border-radius: 14px 0 0 14px;
    +          border-radius: 14px 0 0 14px;
    +}
    +
    +.form-search .input-append .btn {
    +  -webkit-border-radius: 0 14px 14px 0;
    +     -moz-border-radius: 0 14px 14px 0;
    +          border-radius: 0 14px 14px 0;
    +}
    +
    +.form-search .input-prepend .search-query {
    +  -webkit-border-radius: 0 14px 14px 0;
    +     -moz-border-radius: 0 14px 14px 0;
    +          border-radius: 0 14px 14px 0;
    +}
    +
    +.form-search .input-prepend .btn {
    +  -webkit-border-radius: 14px 0 0 14px;
    +     -moz-border-radius: 14px 0 0 14px;
    +          border-radius: 14px 0 0 14px;
    +}
    +
    +.form-search input,
    +.form-inline input,
    +.form-horizontal input,
    +.form-search textarea,
    +.form-inline textarea,
    +.form-horizontal textarea,
    +.form-search select,
    +.form-inline select,
    +.form-horizontal select,
    +.form-search .help-inline,
    +.form-inline .help-inline,
    +.form-horizontal .help-inline,
    +.form-search .uneditable-input,
    +.form-inline .uneditable-input,
    +.form-horizontal .uneditable-input,
    +.form-search .input-prepend,
    +.form-inline .input-prepend,
    +.form-horizontal .input-prepend,
    +.form-search .input-append,
    +.form-inline .input-append,
    +.form-horizontal .input-append {
    +  display: inline-block;
    +  *display: inline;
    +  margin-bottom: 0;
    +  vertical-align: middle;
    +  *zoom: 1;
    +}
    +
    +.form-search .hide,
    +.form-inline .hide,
    +.form-horizontal .hide {
    +  display: none;
    +}
    +
    +.form-search label,
    +.form-inline label,
    +.form-search .btn-group,
    +.form-inline .btn-group {
    +  display: inline-block;
    +}
    +
    +.form-search .input-append,
    +.form-inline .input-append,
    +.form-search .input-prepend,
    +.form-inline .input-prepend {
    +  margin-bottom: 0;
    +}
    +
    +.form-search .radio,
    +.form-search .checkbox,
    +.form-inline .radio,
    +.form-inline .checkbox {
    +  padding-left: 0;
    +  margin-bottom: 0;
    +  vertical-align: middle;
    +}
    +
    +.form-search .radio input[type="radio"],
    +.form-search .checkbox input[type="checkbox"],
    +.form-inline .radio input[type="radio"],
    +.form-inline .checkbox input[type="checkbox"] {
    +  float: left;
    +  margin-right: 3px;
    +  margin-left: 0;
    +}
    +
    +.control-group {
    +  margin-bottom: 10px;
    +}
    +
    +legend + .control-group {
    +  margin-top: 20px;
    +  -webkit-margin-top-collapse: separate;
    +}
    +
    +.form-horizontal .control-group {
    +  margin-bottom: 20px;
    +  *zoom: 1;
    +}
    +
    +.form-horizontal .control-group:before,
    +.form-horizontal .control-group:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.form-horizontal .control-group:after {
    +  clear: both;
    +}
    +
    +.form-horizontal .control-label {
    +  float: left;
    +  width: 160px;
    +  padding-top: 5px;
    +  text-align: right;
    +}
    +
    +.form-horizontal .controls {
    +  *display: inline-block;
    +  *padding-left: 20px;
    +  margin-left: 180px;
    +  *margin-left: 0;
    +}
    +
    +.form-horizontal .controls:first-child {
    +  *padding-left: 180px;
    +}
    +
    +.form-horizontal .help-block {
    +  margin-bottom: 0;
    +}
    +
    +.form-horizontal input + .help-block,
    +.form-horizontal select + .help-block,
    +.form-horizontal textarea + .help-block,
    +.form-horizontal .uneditable-input + .help-block,
    +.form-horizontal .input-prepend + .help-block,
    +.form-horizontal .input-append + .help-block {
    +  margin-top: 10px;
    +}
    +
    +.form-horizontal .form-actions {
    +  padding-left: 180px;
    +}
    +
    +table {
    +  max-width: 100%;
    +  background-color: transparent;
    +  border-collapse: collapse;
    +  border-spacing: 0;
    +}
    +
    +.table {
    +  width: 100%;
    +  margin-bottom: 20px;
    +}
    +
    +.table th,
    +.table td {
    +  padding: 8px;
    +  line-height: 20px;
    +  text-align: left;
    +  vertical-align: top;
    +  border-top: 1px solid #dddddd;
    +}
    +
    +.table th {
    +  font-weight: bold;
    +}
    +
    +.table thead th {
    +  vertical-align: bottom;
    +}
    +
    +.table caption + thead tr:first-child th,
    +.table caption + thead tr:first-child td,
    +.table colgroup + thead tr:first-child th,
    +.table colgroup + thead tr:first-child td,
    +.table thead:first-child tr:first-child th,
    +.table thead:first-child tr:first-child td {
    +  border-top: 0;
    +}
    +
    +.table tbody + tbody {
    +  border-top: 2px solid #dddddd;
    +}
    +
    +.table .table {
    +  background-color: #ffffff;
    +}
    +
    +.table-condensed th,
    +.table-condensed td {
    +  padding: 4px 5px;
    +}
    +
    +.table-bordered {
    +  border: 1px solid #dddddd;
    +  border-collapse: separate;
    +  *border-collapse: collapse;
    +  border-left: 0;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.table-bordered th,
    +.table-bordered td {
    +  border-left: 1px solid #dddddd;
    +}
    +
    +.table-bordered caption + thead tr:first-child th,
    +.table-bordered caption + tbody tr:first-child th,
    +.table-bordered caption + tbody tr:first-child td,
    +.table-bordered colgroup + thead tr:first-child th,
    +.table-bordered colgroup + tbody tr:first-child th,
    +.table-bordered colgroup + tbody tr:first-child td,
    +.table-bordered thead:first-child tr:first-child th,
    +.table-bordered tbody:first-child tr:first-child th,
    +.table-bordered tbody:first-child tr:first-child td {
    +  border-top: 0;
    +}
    +
    +.table-bordered thead:first-child tr:first-child > th:first-child,
    +.table-bordered tbody:first-child tr:first-child > td:first-child,
    +.table-bordered tbody:first-child tr:first-child > th:first-child {
    +  -webkit-border-top-left-radius: 4px;
    +          border-top-left-radius: 4px;
    +  -moz-border-radius-topleft: 4px;
    +}
    +
    +.table-bordered thead:first-child tr:first-child > th:last-child,
    +.table-bordered tbody:first-child tr:first-child > td:last-child,
    +.table-bordered tbody:first-child tr:first-child > th:last-child {
    +  -webkit-border-top-right-radius: 4px;
    +          border-top-right-radius: 4px;
    +  -moz-border-radius-topright: 4px;
    +}
    +
    +.table-bordered thead:last-child tr:last-child > th:first-child,
    +.table-bordered tbody:last-child tr:last-child > td:first-child,
    +.table-bordered tbody:last-child tr:last-child > th:first-child,
    +.table-bordered tfoot:last-child tr:last-child > td:first-child,
    +.table-bordered tfoot:last-child tr:last-child > th:first-child {
    +  -webkit-border-bottom-left-radius: 4px;
    +          border-bottom-left-radius: 4px;
    +  -moz-border-radius-bottomleft: 4px;
    +}
    +
    +.table-bordered thead:last-child tr:last-child > th:last-child,
    +.table-bordered tbody:last-child tr:last-child > td:last-child,
    +.table-bordered tbody:last-child tr:last-child > th:last-child,
    +.table-bordered tfoot:last-child tr:last-child > td:last-child,
    +.table-bordered tfoot:last-child tr:last-child > th:last-child {
    +  -webkit-border-bottom-right-radius: 4px;
    +          border-bottom-right-radius: 4px;
    +  -moz-border-radius-bottomright: 4px;
    +}
    +
    +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
    +  -webkit-border-bottom-left-radius: 0;
    +          border-bottom-left-radius: 0;
    +  -moz-border-radius-bottomleft: 0;
    +}
    +
    +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
    +  -webkit-border-bottom-right-radius: 0;
    +          border-bottom-right-radius: 0;
    +  -moz-border-radius-bottomright: 0;
    +}
    +
    +.table-bordered caption + thead tr:first-child th:first-child,
    +.table-bordered caption + tbody tr:first-child td:first-child,
    +.table-bordered colgroup + thead tr:first-child th:first-child,
    +.table-bordered colgroup + tbody tr:first-child td:first-child {
    +  -webkit-border-top-left-radius: 4px;
    +          border-top-left-radius: 4px;
    +  -moz-border-radius-topleft: 4px;
    +}
    +
    +.table-bordered caption + thead tr:first-child th:last-child,
    +.table-bordered caption + tbody tr:first-child td:last-child,
    +.table-bordered colgroup + thead tr:first-child th:last-child,
    +.table-bordered colgroup + tbody tr:first-child td:last-child {
    +  -webkit-border-top-right-radius: 4px;
    +          border-top-right-radius: 4px;
    +  -moz-border-radius-topright: 4px;
    +}
    +
    +.table-striped tbody > tr:nth-child(odd) > td,
    +.table-striped tbody > tr:nth-child(odd) > th {
    +  background-color: #f9f9f9;
    +}
    +
    +.table-hover tbody tr:hover > td,
    +.table-hover tbody tr:hover > th {
    +  background-color: #f5f5f5;
    +}
    +
    +table td[class*="span"],
    +table th[class*="span"],
    +.row-fluid table td[class*="span"],
    +.row-fluid table th[class*="span"] {
    +  display: table-cell;
    +  float: none;
    +  margin-left: 0;
    +}
    +
    +.table td.span1,
    +.table th.span1 {
    +  float: none;
    +  width: 44px;
    +  margin-left: 0;
    +}
    +
    +.table td.span2,
    +.table th.span2 {
    +  float: none;
    +  width: 124px;
    +  margin-left: 0;
    +}
    +
    +.table td.span3,
    +.table th.span3 {
    +  float: none;
    +  width: 204px;
    +  margin-left: 0;
    +}
    +
    +.table td.span4,
    +.table th.span4 {
    +  float: none;
    +  width: 284px;
    +  margin-left: 0;
    +}
    +
    +.table td.span5,
    +.table th.span5 {
    +  float: none;
    +  width: 364px;
    +  margin-left: 0;
    +}
    +
    +.table td.span6,
    +.table th.span6 {
    +  float: none;
    +  width: 444px;
    +  margin-left: 0;
    +}
    +
    +.table td.span7,
    +.table th.span7 {
    +  float: none;
    +  width: 524px;
    +  margin-left: 0;
    +}
    +
    +.table td.span8,
    +.table th.span8 {
    +  float: none;
    +  width: 604px;
    +  margin-left: 0;
    +}
    +
    +.table td.span9,
    +.table th.span9 {
    +  float: none;
    +  width: 684px;
    +  margin-left: 0;
    +}
    +
    +.table td.span10,
    +.table th.span10 {
    +  float: none;
    +  width: 764px;
    +  margin-left: 0;
    +}
    +
    +.table td.span11,
    +.table th.span11 {
    +  float: none;
    +  width: 844px;
    +  margin-left: 0;
    +}
    +
    +.table td.span12,
    +.table th.span12 {
    +  float: none;
    +  width: 924px;
    +  margin-left: 0;
    +}
    +
    +.table tbody tr.success > td {
    +  background-color: #dff0d8;
    +}
    +
    +.table tbody tr.error > td {
    +  background-color: #f2dede;
    +}
    +
    +.table tbody tr.warning > td {
    +  background-color: #fcf8e3;
    +}
    +
    +.table tbody tr.info > td {
    +  background-color: #d9edf7;
    +}
    +
    +.table-hover tbody tr.success:hover > td {
    +  background-color: #d0e9c6;
    +}
    +
    +.table-hover tbody tr.error:hover > td {
    +  background-color: #ebcccc;
    +}
    +
    +.table-hover tbody tr.warning:hover > td {
    +  background-color: #faf2cc;
    +}
    +
    +.table-hover tbody tr.info:hover > td {
    +  background-color: #c4e3f3;
    +}
    +
    +[class^="icon-"],
    +[class*=" icon-"] {
    +  display: inline-block;
    +  width: 14px;
    +  height: 14px;
    +  margin-top: 1px;
    +  *margin-right: .3em;
    +  line-height: 14px;
    +  vertical-align: text-top;
    +  background-image: url("../img/glyphicons-halflings.png");
    +  background-position: 14px 14px;
    +  background-repeat: no-repeat;
    +}
    +
    +/* White icons with optional class, or on hover/focus/active states of certain elements */
    +
    +.icon-white,
    +.nav-pills > .active > a > [class^="icon-"],
    +.nav-pills > .active > a > [class*=" icon-"],
    +.nav-list > .active > a > [class^="icon-"],
    +.nav-list > .active > a > [class*=" icon-"],
    +.navbar-inverse .nav > .active > a > [class^="icon-"],
    +.navbar-inverse .nav > .active > a > [class*=" icon-"],
    +.dropdown-menu > li > a:hover > [class^="icon-"],
    +.dropdown-menu > li > a:focus > [class^="icon-"],
    +.dropdown-menu > li > a:hover > [class*=" icon-"],
    +.dropdown-menu > li > a:focus > [class*=" icon-"],
    +.dropdown-menu > .active > a > [class^="icon-"],
    +.dropdown-menu > .active > a > [class*=" icon-"],
    +.dropdown-submenu:hover > a > [class^="icon-"],
    +.dropdown-submenu:focus > a > [class^="icon-"],
    +.dropdown-submenu:hover > a > [class*=" icon-"],
    +.dropdown-submenu:focus > a > [class*=" icon-"] {
    +  background-image: url("../img/glyphicons-halflings-white.png");
    +}
    +
    +.icon-glass {
    +  background-position: 0      0;
    +}
    +
    +.icon-music {
    +  background-position: -24px 0;
    +}
    +
    +.icon-search {
    +  background-position: -48px 0;
    +}
    +
    +.icon-envelope {
    +  background-position: -72px 0;
    +}
    +
    +.icon-heart {
    +  background-position: -96px 0;
    +}
    +
    +.icon-star {
    +  background-position: -120px 0;
    +}
    +
    +.icon-star-empty {
    +  background-position: -144px 0;
    +}
    +
    +.icon-user {
    +  background-position: -168px 0;
    +}
    +
    +.icon-film {
    +  background-position: -192px 0;
    +}
    +
    +.icon-th-large {
    +  background-position: -216px 0;
    +}
    +
    +.icon-th {
    +  background-position: -240px 0;
    +}
    +
    +.icon-th-list {
    +  background-position: -264px 0;
    +}
    +
    +.icon-ok {
    +  background-position: -288px 0;
    +}
    +
    +.icon-remove {
    +  background-position: -312px 0;
    +}
    +
    +.icon-zoom-in {
    +  background-position: -336px 0;
    +}
    +
    +.icon-zoom-out {
    +  background-position: -360px 0;
    +}
    +
    +.icon-off {
    +  background-position: -384px 0;
    +}
    +
    +.icon-signal {
    +  background-position: -408px 0;
    +}
    +
    +.icon-cog {
    +  background-position: -432px 0;
    +}
    +
    +.icon-trash {
    +  background-position: -456px 0;
    +}
    +
    +.icon-home {
    +  background-position: 0 -24px;
    +}
    +
    +.icon-file {
    +  background-position: -24px -24px;
    +}
    +
    +.icon-time {
    +  background-position: -48px -24px;
    +}
    +
    +.icon-road {
    +  background-position: -72px -24px;
    +}
    +
    +.icon-download-alt {
    +  background-position: -96px -24px;
    +}
    +
    +.icon-download {
    +  background-position: -120px -24px;
    +}
    +
    +.icon-upload {
    +  background-position: -144px -24px;
    +}
    +
    +.icon-inbox {
    +  background-position: -168px -24px;
    +}
    +
    +.icon-play-circle {
    +  background-position: -192px -24px;
    +}
    +
    +.icon-repeat {
    +  background-position: -216px -24px;
    +}
    +
    +.icon-refresh {
    +  background-position: -240px -24px;
    +}
    +
    +.icon-list-alt {
    +  background-position: -264px -24px;
    +}
    +
    +.icon-lock {
    +  background-position: -287px -24px;
    +}
    +
    +.icon-flag {
    +  background-position: -312px -24px;
    +}
    +
    +.icon-headphones {
    +  background-position: -336px -24px;
    +}
    +
    +.icon-volume-off {
    +  background-position: -360px -24px;
    +}
    +
    +.icon-volume-down {
    +  background-position: -384px -24px;
    +}
    +
    +.icon-volume-up {
    +  background-position: -408px -24px;
    +}
    +
    +.icon-qrcode {
    +  background-position: -432px -24px;
    +}
    +
    +.icon-barcode {
    +  background-position: -456px -24px;
    +}
    +
    +.icon-tag {
    +  background-position: 0 -48px;
    +}
    +
    +.icon-tags {
    +  background-position: -25px -48px;
    +}
    +
    +.icon-book {
    +  background-position: -48px -48px;
    +}
    +
    +.icon-bookmark {
    +  background-position: -72px -48px;
    +}
    +
    +.icon-print {
    +  background-position: -96px -48px;
    +}
    +
    +.icon-camera {
    +  background-position: -120px -48px;
    +}
    +
    +.icon-font {
    +  background-position: -144px -48px;
    +}
    +
    +.icon-bold {
    +  background-position: -167px -48px;
    +}
    +
    +.icon-italic {
    +  background-position: -192px -48px;
    +}
    +
    +.icon-text-height {
    +  background-position: -216px -48px;
    +}
    +
    +.icon-text-width {
    +  background-position: -240px -48px;
    +}
    +
    +.icon-align-left {
    +  background-position: -264px -48px;
    +}
    +
    +.icon-align-center {
    +  background-position: -288px -48px;
    +}
    +
    +.icon-align-right {
    +  background-position: -312px -48px;
    +}
    +
    +.icon-align-justify {
    +  background-position: -336px -48px;
    +}
    +
    +.icon-list {
    +  background-position: -360px -48px;
    +}
    +
    +.icon-indent-left {
    +  background-position: -384px -48px;
    +}
    +
    +.icon-indent-right {
    +  background-position: -408px -48px;
    +}
    +
    +.icon-facetime-video {
    +  background-position: -432px -48px;
    +}
    +
    +.icon-picture {
    +  background-position: -456px -48px;
    +}
    +
    +.icon-pencil {
    +  background-position: 0 -72px;
    +}
    +
    +.icon-map-marker {
    +  background-position: -24px -72px;
    +}
    +
    +.icon-adjust {
    +  background-position: -48px -72px;
    +}
    +
    +.icon-tint {
    +  background-position: -72px -72px;
    +}
    +
    +.icon-edit {
    +  background-position: -96px -72px;
    +}
    +
    +.icon-share {
    +  background-position: -120px -72px;
    +}
    +
    +.icon-check {
    +  background-position: -144px -72px;
    +}
    +
    +.icon-move {
    +  background-position: -168px -72px;
    +}
    +
    +.icon-step-backward {
    +  background-position: -192px -72px;
    +}
    +
    +.icon-fast-backward {
    +  background-position: -216px -72px;
    +}
    +
    +.icon-backward {
    +  background-position: -240px -72px;
    +}
    +
    +.icon-play {
    +  background-position: -264px -72px;
    +}
    +
    +.icon-pause {
    +  background-position: -288px -72px;
    +}
    +
    +.icon-stop {
    +  background-position: -312px -72px;
    +}
    +
    +.icon-forward {
    +  background-position: -336px -72px;
    +}
    +
    +.icon-fast-forward {
    +  background-position: -360px -72px;
    +}
    +
    +.icon-step-forward {
    +  background-position: -384px -72px;
    +}
    +
    +.icon-eject {
    +  background-position: -408px -72px;
    +}
    +
    +.icon-chevron-left {
    +  background-position: -432px -72px;
    +}
    +
    +.icon-chevron-right {
    +  background-position: -456px -72px;
    +}
    +
    +.icon-plus-sign {
    +  background-position: 0 -96px;
    +}
    +
    +.icon-minus-sign {
    +  background-position: -24px -96px;
    +}
    +
    +.icon-remove-sign {
    +  background-position: -48px -96px;
    +}
    +
    +.icon-ok-sign {
    +  background-position: -72px -96px;
    +}
    +
    +.icon-question-sign {
    +  background-position: -96px -96px;
    +}
    +
    +.icon-info-sign {
    +  background-position: -120px -96px;
    +}
    +
    +.icon-screenshot {
    +  background-position: -144px -96px;
    +}
    +
    +.icon-remove-circle {
    +  background-position: -168px -96px;
    +}
    +
    +.icon-ok-circle {
    +  background-position: -192px -96px;
    +}
    +
    +.icon-ban-circle {
    +  background-position: -216px -96px;
    +}
    +
    +.icon-arrow-left {
    +  background-position: -240px -96px;
    +}
    +
    +.icon-arrow-right {
    +  background-position: -264px -96px;
    +}
    +
    +.icon-arrow-up {
    +  background-position: -289px -96px;
    +}
    +
    +.icon-arrow-down {
    +  background-position: -312px -96px;
    +}
    +
    +.icon-share-alt {
    +  background-position: -336px -96px;
    +}
    +
    +.icon-resize-full {
    +  background-position: -360px -96px;
    +}
    +
    +.icon-resize-small {
    +  background-position: -384px -96px;
    +}
    +
    +.icon-plus {
    +  background-position: -408px -96px;
    +}
    +
    +.icon-minus {
    +  background-position: -433px -96px;
    +}
    +
    +.icon-asterisk {
    +  background-position: -456px -96px;
    +}
    +
    +.icon-exclamation-sign {
    +  background-position: 0 -120px;
    +}
    +
    +.icon-gift {
    +  background-position: -24px -120px;
    +}
    +
    +.icon-leaf {
    +  background-position: -48px -120px;
    +}
    +
    +.icon-fire {
    +  background-position: -72px -120px;
    +}
    +
    +.icon-eye-open {
    +  background-position: -96px -120px;
    +}
    +
    +.icon-eye-close {
    +  background-position: -120px -120px;
    +}
    +
    +.icon-warning-sign {
    +  background-position: -144px -120px;
    +}
    +
    +.icon-plane {
    +  background-position: -168px -120px;
    +}
    +
    +.icon-calendar {
    +  background-position: -192px -120px;
    +}
    +
    +.icon-random {
    +  width: 16px;
    +  background-position: -216px -120px;
    +}
    +
    +.icon-comment {
    +  background-position: -240px -120px;
    +}
    +
    +.icon-magnet {
    +  background-position: -264px -120px;
    +}
    +
    +.icon-chevron-up {
    +  background-position: -288px -120px;
    +}
    +
    +.icon-chevron-down {
    +  background-position: -313px -119px;
    +}
    +
    +.icon-retweet {
    +  background-position: -336px -120px;
    +}
    +
    +.icon-shopping-cart {
    +  background-position: -360px -120px;
    +}
    +
    +.icon-folder-close {
    +  width: 16px;
    +  background-position: -384px -120px;
    +}
    +
    +.icon-folder-open {
    +  width: 16px;
    +  background-position: -408px -120px;
    +}
    +
    +.icon-resize-vertical {
    +  background-position: -432px -119px;
    +}
    +
    +.icon-resize-horizontal {
    +  background-position: -456px -118px;
    +}
    +
    +.icon-hdd {
    +  background-position: 0 -144px;
    +}
    +
    +.icon-bullhorn {
    +  background-position: -24px -144px;
    +}
    +
    +.icon-bell {
    +  background-position: -48px -144px;
    +}
    +
    +.icon-certificate {
    +  background-position: -72px -144px;
    +}
    +
    +.icon-thumbs-up {
    +  background-position: -96px -144px;
    +}
    +
    +.icon-thumbs-down {
    +  background-position: -120px -144px;
    +}
    +
    +.icon-hand-right {
    +  background-position: -144px -144px;
    +}
    +
    +.icon-hand-left {
    +  background-position: -168px -144px;
    +}
    +
    +.icon-hand-up {
    +  background-position: -192px -144px;
    +}
    +
    +.icon-hand-down {
    +  background-position: -216px -144px;
    +}
    +
    +.icon-circle-arrow-right {
    +  background-position: -240px -144px;
    +}
    +
    +.icon-circle-arrow-left {
    +  background-position: -264px -144px;
    +}
    +
    +.icon-circle-arrow-up {
    +  background-position: -288px -144px;
    +}
    +
    +.icon-circle-arrow-down {
    +  background-position: -312px -144px;
    +}
    +
    +.icon-globe {
    +  background-position: -336px -144px;
    +}
    +
    +.icon-wrench {
    +  background-position: -360px -144px;
    +}
    +
    +.icon-tasks {
    +  background-position: -384px -144px;
    +}
    +
    +.icon-filter {
    +  background-position: -408px -144px;
    +}
    +
    +.icon-briefcase {
    +  background-position: -432px -144px;
    +}
    +
    +.icon-fullscreen {
    +  background-position: -456px -144px;
    +}
    +
    +.dropup,
    +.dropdown {
    +  position: relative;
    +}
    +
    +.dropdown-toggle {
    +  *margin-bottom: -3px;
    +}
    +
    +.dropdown-toggle:active,
    +.open .dropdown-toggle {
    +  outline: 0;
    +}
    +
    +.caret {
    +  display: inline-block;
    +  width: 0;
    +  height: 0;
    +  vertical-align: top;
    +  border-top: 4px solid #000000;
    +  border-right: 4px solid transparent;
    +  border-left: 4px solid transparent;
    +  content: "";
    +}
    +
    +.dropdown .caret {
    +  margin-top: 8px;
    +  margin-left: 2px;
    +}
    +
    +.dropdown-menu {
    +  position: absolute;
    +  top: 100%;
    +  left: 0;
    +  z-index: 1000;
    +  display: none;
    +  float: left;
    +  min-width: 160px;
    +  padding: 5px 0;
    +  margin: 2px 0 0;
    +  list-style: none;
    +  background-color: #ffffff;
    +  border: 1px solid #ccc;
    +  border: 1px solid rgba(0, 0, 0, 0.2);
    +  *border-right-width: 2px;
    +  *border-bottom-width: 2px;
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +  -webkit-background-clip: padding-box;
    +     -moz-background-clip: padding;
    +          background-clip: padding-box;
    +}
    +
    +.dropdown-menu.pull-right {
    +  right: 0;
    +  left: auto;
    +}
    +
    +.dropdown-menu .divider {
    +  *width: 100%;
    +  height: 1px;
    +  margin: 9px 1px;
    +  *margin: -5px 0 5px;
    +  overflow: hidden;
    +  background-color: #e5e5e5;
    +  border-bottom: 1px solid #ffffff;
    +}
    +
    +.dropdown-menu > li > a {
    +  display: block;
    +  padding: 3px 20px;
    +  clear: both;
    +  font-weight: normal;
    +  line-height: 20px;
    +  color: #333333;
    +  white-space: nowrap;
    +}
    +
    +.dropdown-menu > li > a:hover,
    +.dropdown-menu > li > a:focus,
    +.dropdown-submenu:hover > a,
    +.dropdown-submenu:focus > a {
    +  color: #ffffff;
    +  text-decoration: none;
    +  background-color: #0081c2;
    +  background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
    +  background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: -o-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: linear-gradient(to bottom, #0088cc, #0077b3);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
    +}
    +
    +.dropdown-menu > .active > a,
    +.dropdown-menu > .active > a:hover,
    +.dropdown-menu > .active > a:focus {
    +  color: #ffffff;
    +  text-decoration: none;
    +  background-color: #0081c2;
    +  background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
    +  background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: -o-linear-gradient(top, #0088cc, #0077b3);
    +  background-image: linear-gradient(to bottom, #0088cc, #0077b3);
    +  background-repeat: repeat-x;
    +  outline: 0;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
    +}
    +
    +.dropdown-menu > .disabled > a,
    +.dropdown-menu > .disabled > a:hover,
    +.dropdown-menu > .disabled > a:focus {
    +  color: #999999;
    +}
    +
    +.dropdown-menu > .disabled > a:hover,
    +.dropdown-menu > .disabled > a:focus {
    +  text-decoration: none;
    +  cursor: default;
    +  background-color: transparent;
    +  background-image: none;
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.open {
    +  *z-index: 1000;
    +}
    +
    +.open > .dropdown-menu {
    +  display: block;
    +}
    +
    +.pull-right > .dropdown-menu {
    +  right: 0;
    +  left: auto;
    +}
    +
    +.dropup .caret,
    +.navbar-fixed-bottom .dropdown .caret {
    +  border-top: 0;
    +  border-bottom: 4px solid #000000;
    +  content: "";
    +}
    +
    +.dropup .dropdown-menu,
    +.navbar-fixed-bottom .dropdown .dropdown-menu {
    +  top: auto;
    +  bottom: 100%;
    +  margin-bottom: 1px;
    +}
    +
    +.dropdown-submenu {
    +  position: relative;
    +}
    +
    +.dropdown-submenu > .dropdown-menu {
    +  top: 0;
    +  left: 100%;
    +  margin-top: -6px;
    +  margin-left: -1px;
    +  -webkit-border-radius: 0 6px 6px 6px;
    +     -moz-border-radius: 0 6px 6px 6px;
    +          border-radius: 0 6px 6px 6px;
    +}
    +
    +.dropdown-submenu:hover > .dropdown-menu {
    +  display: block;
    +}
    +
    +.dropup .dropdown-submenu > .dropdown-menu {
    +  top: auto;
    +  bottom: 0;
    +  margin-top: 0;
    +  margin-bottom: -2px;
    +  -webkit-border-radius: 5px 5px 5px 0;
    +     -moz-border-radius: 5px 5px 5px 0;
    +          border-radius: 5px 5px 5px 0;
    +}
    +
    +.dropdown-submenu > a:after {
    +  display: block;
    +  float: right;
    +  width: 0;
    +  height: 0;
    +  margin-top: 5px;
    +  margin-right: -10px;
    +  border-color: transparent;
    +  border-left-color: #cccccc;
    +  border-style: solid;
    +  border-width: 5px 0 5px 5px;
    +  content: " ";
    +}
    +
    +.dropdown-submenu:hover > a:after {
    +  border-left-color: #ffffff;
    +}
    +
    +.dropdown-submenu.pull-left {
    +  float: none;
    +}
    +
    +.dropdown-submenu.pull-left > .dropdown-menu {
    +  left: -100%;
    +  margin-left: 10px;
    +  -webkit-border-radius: 6px 0 6px 6px;
    +     -moz-border-radius: 6px 0 6px 6px;
    +          border-radius: 6px 0 6px 6px;
    +}
    +
    +.dropdown .dropdown-menu .nav-header {
    +  padding-right: 20px;
    +  padding-left: 20px;
    +}
    +
    +.typeahead {
    +  z-index: 1051;
    +  margin-top: 2px;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.well {
    +  min-height: 20px;
    +  padding: 19px;
    +  margin-bottom: 20px;
    +  background-color: #f5f5f5;
    +  border: 1px solid #e3e3e3;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
    +          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
    +}
    +
    +.well blockquote {
    +  border-color: #ddd;
    +  border-color: rgba(0, 0, 0, 0.15);
    +}
    +
    +.well-large {
    +  padding: 24px;
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +}
    +
    +.well-small {
    +  padding: 9px;
    +  -webkit-border-radius: 3px;
    +     -moz-border-radius: 3px;
    +          border-radius: 3px;
    +}
    +
    +.fade {
    +  opacity: 0;
    +  -webkit-transition: opacity 0.15s linear;
    +     -moz-transition: opacity 0.15s linear;
    +       -o-transition: opacity 0.15s linear;
    +          transition: opacity 0.15s linear;
    +}
    +
    +.fade.in {
    +  opacity: 1;
    +}
    +
    +.collapse {
    +  position: relative;
    +  height: 0;
    +  overflow: hidden;
    +  -webkit-transition: height 0.35s ease;
    +     -moz-transition: height 0.35s ease;
    +       -o-transition: height 0.35s ease;
    +          transition: height 0.35s ease;
    +}
    +
    +.collapse.in {
    +  height: auto;
    +}
    +
    +.close {
    +  float: right;
    +  font-size: 20px;
    +  font-weight: bold;
    +  line-height: 20px;
    +  color: #000000;
    +  text-shadow: 0 1px 0 #ffffff;
    +  opacity: 0.2;
    +  filter: alpha(opacity=20);
    +}
    +
    +.close:hover,
    +.close:focus {
    +  color: #000000;
    +  text-decoration: none;
    +  cursor: pointer;
    +  opacity: 0.4;
    +  filter: alpha(opacity=40);
    +}
    +
    +button.close {
    +  padding: 0;
    +  cursor: pointer;
    +  background: transparent;
    +  border: 0;
    +  -webkit-appearance: none;
    +}
    +
    +.btn {
    +  display: inline-block;
    +  *display: inline;
    +  padding: 4px 12px;
    +  margin-bottom: 0;
    +  *margin-left: .3em;
    +  font-size: 14px;
    +  line-height: 20px;
    +  color: #333333;
    +  text-align: center;
    +  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
    +  vertical-align: middle;
    +  cursor: pointer;
    +  background-color: #f5f5f5;
    +  *background-color: #e6e6e6;
    +  background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
    +  background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
    +  background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
    +  background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
    +  background-repeat: repeat-x;
    +  border: 1px solid #cccccc;
    +  *border: 0;
    +  border-color: #e6e6e6 #e6e6e6 #bfbfbf;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  border-bottom-color: #b3b3b3;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +  *zoom: 1;
    +  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +}
    +
    +.btn:hover,
    +.btn:focus,
    +.btn:active,
    +.btn.active,
    +.btn.disabled,
    +.btn[disabled] {
    +  color: #333333;
    +  background-color: #e6e6e6;
    +  *background-color: #d9d9d9;
    +}
    +
    +.btn:active,
    +.btn.active {
    +  background-color: #cccccc \9;
    +}
    +
    +.btn:first-child {
    +  *margin-left: 0;
    +}
    +
    +.btn:hover,
    +.btn:focus {
    +  color: #333333;
    +  text-decoration: none;
    +  background-position: 0 -15px;
    +  -webkit-transition: background-position 0.1s linear;
    +     -moz-transition: background-position 0.1s linear;
    +       -o-transition: background-position 0.1s linear;
    +          transition: background-position 0.1s linear;
    +}
    +
    +.btn:focus {
    +  outline: thin dotted #333;
    +  outline: 5px auto -webkit-focus-ring-color;
    +  outline-offset: -2px;
    +}
    +
    +.btn.active,
    +.btn:active {
    +  background-image: none;
    +  outline: 0;
    +  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +}
    +
    +.btn.disabled,
    +.btn[disabled] {
    +  cursor: default;
    +  background-image: none;
    +  opacity: 0.65;
    +  filter: alpha(opacity=65);
    +  -webkit-box-shadow: none;
    +     -moz-box-shadow: none;
    +          box-shadow: none;
    +}
    +
    +.btn-large {
    +  padding: 11px 19px;
    +  font-size: 17.5px;
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +}
    +
    +.btn-large [class^="icon-"],
    +.btn-large [class*=" icon-"] {
    +  margin-top: 4px;
    +}
    +
    +.btn-small {
    +  padding: 2px 10px;
    +  font-size: 11.9px;
    +  -webkit-border-radius: 3px;
    +     -moz-border-radius: 3px;
    +          border-radius: 3px;
    +}
    +
    +.btn-small [class^="icon-"],
    +.btn-small [class*=" icon-"] {
    +  margin-top: 0;
    +}
    +
    +.btn-mini [class^="icon-"],
    +.btn-mini [class*=" icon-"] {
    +  margin-top: -1px;
    +}
    +
    +.btn-mini {
    +  padding: 0 6px;
    +  font-size: 10.5px;
    +  -webkit-border-radius: 3px;
    +     -moz-border-radius: 3px;
    +          border-radius: 3px;
    +}
    +
    +.btn-block {
    +  display: block;
    +  width: 100%;
    +  padding-right: 0;
    +  padding-left: 0;
    +  -webkit-box-sizing: border-box;
    +     -moz-box-sizing: border-box;
    +          box-sizing: border-box;
    +}
    +
    +.btn-block + .btn-block {
    +  margin-top: 5px;
    +}
    +
    +input[type="submit"].btn-block,
    +input[type="reset"].btn-block,
    +input[type="button"].btn-block {
    +  width: 100%;
    +}
    +
    +.btn-primary.active,
    +.btn-warning.active,
    +.btn-danger.active,
    +.btn-success.active,
    +.btn-info.active,
    +.btn-inverse.active {
    +  color: rgba(255, 255, 255, 0.75);
    +}
    +
    +.btn-primary {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #006dcc;
    +  *background-color: #0044cc;
    +  background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
    +  background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
    +  background-image: -o-linear-gradient(top, #0088cc, #0044cc);
    +  background-image: linear-gradient(to bottom, #0088cc, #0044cc);
    +  background-repeat: repeat-x;
    +  border-color: #0044cc #0044cc #002a80;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-primary:hover,
    +.btn-primary:focus,
    +.btn-primary:active,
    +.btn-primary.active,
    +.btn-primary.disabled,
    +.btn-primary[disabled] {
    +  color: #ffffff;
    +  background-color: #0044cc;
    +  *background-color: #003bb3;
    +}
    +
    +.btn-primary:active,
    +.btn-primary.active {
    +  background-color: #003399 \9;
    +}
    +
    +.btn-warning {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #faa732;
    +  *background-color: #f89406;
    +  background-image: -moz-linear-gradient(top, #fbb450, #f89406);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
    +  background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
    +  background-image: -o-linear-gradient(top, #fbb450, #f89406);
    +  background-image: linear-gradient(to bottom, #fbb450, #f89406);
    +  background-repeat: repeat-x;
    +  border-color: #f89406 #f89406 #ad6704;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-warning:hover,
    +.btn-warning:focus,
    +.btn-warning:active,
    +.btn-warning.active,
    +.btn-warning.disabled,
    +.btn-warning[disabled] {
    +  color: #ffffff;
    +  background-color: #f89406;
    +  *background-color: #df8505;
    +}
    +
    +.btn-warning:active,
    +.btn-warning.active {
    +  background-color: #c67605 \9;
    +}
    +
    +.btn-danger {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #da4f49;
    +  *background-color: #bd362f;
    +  background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
    +  background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
    +  background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
    +  background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
    +  background-repeat: repeat-x;
    +  border-color: #bd362f #bd362f #802420;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-danger:hover,
    +.btn-danger:focus,
    +.btn-danger:active,
    +.btn-danger.active,
    +.btn-danger.disabled,
    +.btn-danger[disabled] {
    +  color: #ffffff;
    +  background-color: #bd362f;
    +  *background-color: #a9302a;
    +}
    +
    +.btn-danger:active,
    +.btn-danger.active {
    +  background-color: #942a25 \9;
    +}
    +
    +.btn-success {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #5bb75b;
    +  *background-color: #51a351;
    +  background-image: -moz-linear-gradient(top, #62c462, #51a351);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
    +  background-image: -webkit-linear-gradient(top, #62c462, #51a351);
    +  background-image: -o-linear-gradient(top, #62c462, #51a351);
    +  background-image: linear-gradient(to bottom, #62c462, #51a351);
    +  background-repeat: repeat-x;
    +  border-color: #51a351 #51a351 #387038;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-success:hover,
    +.btn-success:focus,
    +.btn-success:active,
    +.btn-success.active,
    +.btn-success.disabled,
    +.btn-success[disabled] {
    +  color: #ffffff;
    +  background-color: #51a351;
    +  *background-color: #499249;
    +}
    +
    +.btn-success:active,
    +.btn-success.active {
    +  background-color: #408140 \9;
    +}
    +
    +.btn-info {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #49afcd;
    +  *background-color: #2f96b4;
    +  background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
    +  background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
    +  background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
    +  background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
    +  background-repeat: repeat-x;
    +  border-color: #2f96b4 #2f96b4 #1f6377;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-info:hover,
    +.btn-info:focus,
    +.btn-info:active,
    +.btn-info.active,
    +.btn-info.disabled,
    +.btn-info[disabled] {
    +  color: #ffffff;
    +  background-color: #2f96b4;
    +  *background-color: #2a85a0;
    +}
    +
    +.btn-info:active,
    +.btn-info.active {
    +  background-color: #24748c \9;
    +}
    +
    +.btn-inverse {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #363636;
    +  *background-color: #222222;
    +  background-image: -moz-linear-gradient(top, #444444, #222222);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
    +  background-image: -webkit-linear-gradient(top, #444444, #222222);
    +  background-image: -o-linear-gradient(top, #444444, #222222);
    +  background-image: linear-gradient(to bottom, #444444, #222222);
    +  background-repeat: repeat-x;
    +  border-color: #222222 #222222 #000000;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.btn-inverse:hover,
    +.btn-inverse:focus,
    +.btn-inverse:active,
    +.btn-inverse.active,
    +.btn-inverse.disabled,
    +.btn-inverse[disabled] {
    +  color: #ffffff;
    +  background-color: #222222;
    +  *background-color: #151515;
    +}
    +
    +.btn-inverse:active,
    +.btn-inverse.active {
    +  background-color: #080808 \9;
    +}
    +
    +button.btn,
    +input[type="submit"].btn {
    +  *padding-top: 3px;
    +  *padding-bottom: 3px;
    +}
    +
    +button.btn::-moz-focus-inner,
    +input[type="submit"].btn::-moz-focus-inner {
    +  padding: 0;
    +  border: 0;
    +}
    +
    +button.btn.btn-large,
    +input[type="submit"].btn.btn-large {
    +  *padding-top: 7px;
    +  *padding-bottom: 7px;
    +}
    +
    +button.btn.btn-small,
    +input[type="submit"].btn.btn-small {
    +  *padding-top: 3px;
    +  *padding-bottom: 3px;
    +}
    +
    +button.btn.btn-mini,
    +input[type="submit"].btn.btn-mini {
    +  *padding-top: 1px;
    +  *padding-bottom: 1px;
    +}
    +
    +.btn-link,
    +.btn-link:active,
    +.btn-link[disabled] {
    +  background-color: transparent;
    +  background-image: none;
    +  -webkit-box-shadow: none;
    +     -moz-box-shadow: none;
    +          box-shadow: none;
    +}
    +
    +.btn-link {
    +  color: #0088cc;
    +  cursor: pointer;
    +  border-color: transparent;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.btn-link:hover,
    +.btn-link:focus {
    +  color: #005580;
    +  text-decoration: underline;
    +  background-color: transparent;
    +}
    +
    +.btn-link[disabled]:hover,
    +.btn-link[disabled]:focus {
    +  color: #333333;
    +  text-decoration: none;
    +}
    +
    +.btn-group {
    +  position: relative;
    +  display: inline-block;
    +  *display: inline;
    +  *margin-left: .3em;
    +  font-size: 0;
    +  white-space: nowrap;
    +  vertical-align: middle;
    +  *zoom: 1;
    +}
    +
    +.btn-group:first-child {
    +  *margin-left: 0;
    +}
    +
    +.btn-group + .btn-group {
    +  margin-left: 5px;
    +}
    +
    +.btn-toolbar {
    +  margin-top: 10px;
    +  margin-bottom: 10px;
    +  font-size: 0;
    +}
    +
    +.btn-toolbar > .btn + .btn,
    +.btn-toolbar > .btn-group + .btn,
    +.btn-toolbar > .btn + .btn-group {
    +  margin-left: 5px;
    +}
    +
    +.btn-group > .btn {
    +  position: relative;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.btn-group > .btn + .btn {
    +  margin-left: -1px;
    +}
    +
    +.btn-group > .btn,
    +.btn-group > .dropdown-menu,
    +.btn-group > .popover {
    +  font-size: 14px;
    +}
    +
    +.btn-group > .btn-mini {
    +  font-size: 10.5px;
    +}
    +
    +.btn-group > .btn-small {
    +  font-size: 11.9px;
    +}
    +
    +.btn-group > .btn-large {
    +  font-size: 17.5px;
    +}
    +
    +.btn-group > .btn:first-child {
    +  margin-left: 0;
    +  -webkit-border-bottom-left-radius: 4px;
    +          border-bottom-left-radius: 4px;
    +  -webkit-border-top-left-radius: 4px;
    +          border-top-left-radius: 4px;
    +  -moz-border-radius-bottomleft: 4px;
    +  -moz-border-radius-topleft: 4px;
    +}
    +
    +.btn-group > .btn:last-child,
    +.btn-group > .dropdown-toggle {
    +  -webkit-border-top-right-radius: 4px;
    +          border-top-right-radius: 4px;
    +  -webkit-border-bottom-right-radius: 4px;
    +          border-bottom-right-radius: 4px;
    +  -moz-border-radius-topright: 4px;
    +  -moz-border-radius-bottomright: 4px;
    +}
    +
    +.btn-group > .btn.large:first-child {
    +  margin-left: 0;
    +  -webkit-border-bottom-left-radius: 6px;
    +          border-bottom-left-radius: 6px;
    +  -webkit-border-top-left-radius: 6px;
    +          border-top-left-radius: 6px;
    +  -moz-border-radius-bottomleft: 6px;
    +  -moz-border-radius-topleft: 6px;
    +}
    +
    +.btn-group > .btn.large:last-child,
    +.btn-group > .large.dropdown-toggle {
    +  -webkit-border-top-right-radius: 6px;
    +          border-top-right-radius: 6px;
    +  -webkit-border-bottom-right-radius: 6px;
    +          border-bottom-right-radius: 6px;
    +  -moz-border-radius-topright: 6px;
    +  -moz-border-radius-bottomright: 6px;
    +}
    +
    +.btn-group > .btn:hover,
    +.btn-group > .btn:focus,
    +.btn-group > .btn:active,
    +.btn-group > .btn.active {
    +  z-index: 2;
    +}
    +
    +.btn-group .dropdown-toggle:active,
    +.btn-group.open .dropdown-toggle {
    +  outline: 0;
    +}
    +
    +.btn-group > .btn + .dropdown-toggle {
    +  *padding-top: 5px;
    +  padding-right: 8px;
    +  *padding-bottom: 5px;
    +  padding-left: 8px;
    +  -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +          box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    +}
    +
    +.btn-group > .btn-mini + .dropdown-toggle {
    +  *padding-top: 2px;
    +  padding-right: 5px;
    +  *padding-bottom: 2px;
    +  padding-left: 5px;
    +}
    +
    +.btn-group > .btn-small + .dropdown-toggle {
    +  *padding-top: 5px;
    +  *padding-bottom: 4px;
    +}
    +
    +.btn-group > .btn-large + .dropdown-toggle {
    +  *padding-top: 7px;
    +  padding-right: 12px;
    +  *padding-bottom: 7px;
    +  padding-left: 12px;
    +}
    +
    +.btn-group.open .dropdown-toggle {
    +  background-image: none;
    +  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
    +}
    +
    +.btn-group.open .btn.dropdown-toggle {
    +  background-color: #e6e6e6;
    +}
    +
    +.btn-group.open .btn-primary.dropdown-toggle {
    +  background-color: #0044cc;
    +}
    +
    +.btn-group.open .btn-warning.dropdown-toggle {
    +  background-color: #f89406;
    +}
    +
    +.btn-group.open .btn-danger.dropdown-toggle {
    +  background-color: #bd362f;
    +}
    +
    +.btn-group.open .btn-success.dropdown-toggle {
    +  background-color: #51a351;
    +}
    +
    +.btn-group.open .btn-info.dropdown-toggle {
    +  background-color: #2f96b4;
    +}
    +
    +.btn-group.open .btn-inverse.dropdown-toggle {
    +  background-color: #222222;
    +}
    +
    +.btn .caret {
    +  margin-top: 8px;
    +  margin-left: 0;
    +}
    +
    +.btn-large .caret {
    +  margin-top: 6px;
    +}
    +
    +.btn-large .caret {
    +  border-top-width: 5px;
    +  border-right-width: 5px;
    +  border-left-width: 5px;
    +}
    +
    +.btn-mini .caret,
    +.btn-small .caret {
    +  margin-top: 8px;
    +}
    +
    +.dropup .btn-large .caret {
    +  border-bottom-width: 5px;
    +}
    +
    +.btn-primary .caret,
    +.btn-warning .caret,
    +.btn-danger .caret,
    +.btn-info .caret,
    +.btn-success .caret,
    +.btn-inverse .caret {
    +  border-top-color: #ffffff;
    +  border-bottom-color: #ffffff;
    +}
    +
    +.btn-group-vertical {
    +  display: inline-block;
    +  *display: inline;
    +  /* IE7 inline-block hack */
    +
    +  *zoom: 1;
    +}
    +
    +.btn-group-vertical > .btn {
    +  display: block;
    +  float: none;
    +  max-width: 100%;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.btn-group-vertical > .btn + .btn {
    +  margin-top: -1px;
    +  margin-left: 0;
    +}
    +
    +.btn-group-vertical > .btn:first-child {
    +  -webkit-border-radius: 4px 4px 0 0;
    +     -moz-border-radius: 4px 4px 0 0;
    +          border-radius: 4px 4px 0 0;
    +}
    +
    +.btn-group-vertical > .btn:last-child {
    +  -webkit-border-radius: 0 0 4px 4px;
    +     -moz-border-radius: 0 0 4px 4px;
    +          border-radius: 0 0 4px 4px;
    +}
    +
    +.btn-group-vertical > .btn-large:first-child {
    +  -webkit-border-radius: 6px 6px 0 0;
    +     -moz-border-radius: 6px 6px 0 0;
    +          border-radius: 6px 6px 0 0;
    +}
    +
    +.btn-group-vertical > .btn-large:last-child {
    +  -webkit-border-radius: 0 0 6px 6px;
    +     -moz-border-radius: 0 0 6px 6px;
    +          border-radius: 0 0 6px 6px;
    +}
    +
    +.alert {
    +  padding: 8px 35px 8px 14px;
    +  margin-bottom: 20px;
    +  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
    +  background-color: #fcf8e3;
    +  border: 1px solid #fbeed5;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.alert,
    +.alert h4 {
    +  color: #c09853;
    +}
    +
    +.alert h4 {
    +  margin: 0;
    +}
    +
    +.alert .close {
    +  position: relative;
    +  top: -2px;
    +  right: -21px;
    +  line-height: 20px;
    +}
    +
    +.alert-success {
    +  color: #468847;
    +  background-color: #dff0d8;
    +  border-color: #d6e9c6;
    +}
    +
    +.alert-success h4 {
    +  color: #468847;
    +}
    +
    +.alert-danger,
    +.alert-error {
    +  color: #b94a48;
    +  background-color: #f2dede;
    +  border-color: #eed3d7;
    +}
    +
    +.alert-danger h4,
    +.alert-error h4 {
    +  color: #b94a48;
    +}
    +
    +.alert-info {
    +  color: #3a87ad;
    +  background-color: #d9edf7;
    +  border-color: #bce8f1;
    +}
    +
    +.alert-info h4 {
    +  color: #3a87ad;
    +}
    +
    +.alert-block {
    +  padding-top: 14px;
    +  padding-bottom: 14px;
    +}
    +
    +.alert-block > p,
    +.alert-block > ul {
    +  margin-bottom: 0;
    +}
    +
    +.alert-block p + p {
    +  margin-top: 5px;
    +}
    +
    +.nav {
    +  margin-bottom: 20px;
    +  margin-left: 0;
    +  list-style: none;
    +}
    +
    +.nav > li > a {
    +  display: block;
    +}
    +
    +.nav > li > a:hover,
    +.nav > li > a:focus {
    +  text-decoration: none;
    +  background-color: #eeeeee;
    +}
    +
    +.nav > li > a > img {
    +  max-width: none;
    +}
    +
    +.nav > .pull-right {
    +  float: right;
    +}
    +
    +.nav-header {
    +  display: block;
    +  padding: 3px 15px;
    +  font-size: 11px;
    +  font-weight: bold;
    +  line-height: 20px;
    +  color: #999999;
    +  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
    +  text-transform: uppercase;
    +}
    +
    +.nav li + .nav-header {
    +  margin-top: 9px;
    +}
    +
    +.nav-list {
    +  padding-right: 15px;
    +  padding-left: 15px;
    +  margin-bottom: 0;
    +}
    +
    +.nav-list > li > a,
    +.nav-list .nav-header {
    +  margin-right: -15px;
    +  margin-left: -15px;
    +  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
    +}
    +
    +.nav-list > li > a {
    +  padding: 3px 15px;
    +}
    +
    +.nav-list > .active > a,
    +.nav-list > .active > a:hover,
    +.nav-list > .active > a:focus {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
    +  background-color: #0088cc;
    +}
    +
    +.nav-list [class^="icon-"],
    +.nav-list [class*=" icon-"] {
    +  margin-right: 2px;
    +}
    +
    +.nav-list .divider {
    +  *width: 100%;
    +  height: 1px;
    +  margin: 9px 1px;
    +  *margin: -5px 0 5px;
    +  overflow: hidden;
    +  background-color: #e5e5e5;
    +  border-bottom: 1px solid #ffffff;
    +}
    +
    +.nav-tabs,
    +.nav-pills {
    +  *zoom: 1;
    +}
    +
    +.nav-tabs:before,
    +.nav-pills:before,
    +.nav-tabs:after,
    +.nav-pills:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.nav-tabs:after,
    +.nav-pills:after {
    +  clear: both;
    +}
    +
    +.nav-tabs > li,
    +.nav-pills > li {
    +  float: left;
    +}
    +
    +.nav-tabs > li > a,
    +.nav-pills > li > a {
    +  padding-right: 12px;
    +  padding-left: 12px;
    +  margin-right: 2px;
    +  line-height: 14px;
    +}
    +
    +.nav-tabs {
    +  border-bottom: 1px solid #ddd;
    +}
    +
    +.nav-tabs > li {
    +  margin-bottom: -1px;
    +}
    +
    +.nav-tabs > li > a {
    +  padding-top: 8px;
    +  padding-bottom: 8px;
    +  line-height: 20px;
    +  border: 1px solid transparent;
    +  -webkit-border-radius: 4px 4px 0 0;
    +     -moz-border-radius: 4px 4px 0 0;
    +          border-radius: 4px 4px 0 0;
    +}
    +
    +.nav-tabs > li > a:hover,
    +.nav-tabs > li > a:focus {
    +  border-color: #eeeeee #eeeeee #dddddd;
    +}
    +
    +.nav-tabs > .active > a,
    +.nav-tabs > .active > a:hover,
    +.nav-tabs > .active > a:focus {
    +  color: #555555;
    +  cursor: default;
    +  background-color: #ffffff;
    +  border: 1px solid #ddd;
    +  border-bottom-color: transparent;
    +}
    +
    +.nav-pills > li > a {
    +  padding-top: 8px;
    +  padding-bottom: 8px;
    +  margin-top: 2px;
    +  margin-bottom: 2px;
    +  -webkit-border-radius: 5px;
    +     -moz-border-radius: 5px;
    +          border-radius: 5px;
    +}
    +
    +.nav-pills > .active > a,
    +.nav-pills > .active > a:hover,
    +.nav-pills > .active > a:focus {
    +  color: #ffffff;
    +  background-color: #0088cc;
    +}
    +
    +.nav-stacked > li {
    +  float: none;
    +}
    +
    +.nav-stacked > li > a {
    +  margin-right: 0;
    +}
    +
    +.nav-tabs.nav-stacked {
    +  border-bottom: 0;
    +}
    +
    +.nav-tabs.nav-stacked > li > a {
    +  border: 1px solid #ddd;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.nav-tabs.nav-stacked > li:first-child > a {
    +  -webkit-border-top-right-radius: 4px;
    +          border-top-right-radius: 4px;
    +  -webkit-border-top-left-radius: 4px;
    +          border-top-left-radius: 4px;
    +  -moz-border-radius-topright: 4px;
    +  -moz-border-radius-topleft: 4px;
    +}
    +
    +.nav-tabs.nav-stacked > li:last-child > a {
    +  -webkit-border-bottom-right-radius: 4px;
    +          border-bottom-right-radius: 4px;
    +  -webkit-border-bottom-left-radius: 4px;
    +          border-bottom-left-radius: 4px;
    +  -moz-border-radius-bottomright: 4px;
    +  -moz-border-radius-bottomleft: 4px;
    +}
    +
    +.nav-tabs.nav-stacked > li > a:hover,
    +.nav-tabs.nav-stacked > li > a:focus {
    +  z-index: 2;
    +  border-color: #ddd;
    +}
    +
    +.nav-pills.nav-stacked > li > a {
    +  margin-bottom: 3px;
    +}
    +
    +.nav-pills.nav-stacked > li:last-child > a {
    +  margin-bottom: 1px;
    +}
    +
    +.nav-tabs .dropdown-menu {
    +  -webkit-border-radius: 0 0 6px 6px;
    +     -moz-border-radius: 0 0 6px 6px;
    +          border-radius: 0 0 6px 6px;
    +}
    +
    +.nav-pills .dropdown-menu {
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +}
    +
    +.nav .dropdown-toggle .caret {
    +  margin-top: 6px;
    +  border-top-color: #0088cc;
    +  border-bottom-color: #0088cc;
    +}
    +
    +.nav .dropdown-toggle:hover .caret,
    +.nav .dropdown-toggle:focus .caret {
    +  border-top-color: #005580;
    +  border-bottom-color: #005580;
    +}
    +
    +/* move down carets for tabs */
    +
    +.nav-tabs .dropdown-toggle .caret {
    +  margin-top: 8px;
    +}
    +
    +.nav .active .dropdown-toggle .caret {
    +  border-top-color: #fff;
    +  border-bottom-color: #fff;
    +}
    +
    +.nav-tabs .active .dropdown-toggle .caret {
    +  border-top-color: #555555;
    +  border-bottom-color: #555555;
    +}
    +
    +.nav > .dropdown.active > a:hover,
    +.nav > .dropdown.active > a:focus {
    +  cursor: pointer;
    +}
    +
    +.nav-tabs .open .dropdown-toggle,
    +.nav-pills .open .dropdown-toggle,
    +.nav > li.dropdown.open.active > a:hover,
    +.nav > li.dropdown.open.active > a:focus {
    +  color: #ffffff;
    +  background-color: #999999;
    +  border-color: #999999;
    +}
    +
    +.nav li.dropdown.open .caret,
    +.nav li.dropdown.open.active .caret,
    +.nav li.dropdown.open a:hover .caret,
    +.nav li.dropdown.open a:focus .caret {
    +  border-top-color: #ffffff;
    +  border-bottom-color: #ffffff;
    +  opacity: 1;
    +  filter: alpha(opacity=100);
    +}
    +
    +.tabs-stacked .open > a:hover,
    +.tabs-stacked .open > a:focus {
    +  border-color: #999999;
    +}
    +
    +.tabbable {
    +  *zoom: 1;
    +}
    +
    +.tabbable:before,
    +.tabbable:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.tabbable:after {
    +  clear: both;
    +}
    +
    +.tab-content {
    +  overflow: auto;
    +}
    +
    +.tabs-below > .nav-tabs,
    +.tabs-right > .nav-tabs,
    +.tabs-left > .nav-tabs {
    +  border-bottom: 0;
    +}
    +
    +.tab-content > .tab-pane,
    +.pill-content > .pill-pane {
    +  display: none;
    +}
    +
    +.tab-content > .active,
    +.pill-content > .active {
    +  display: block;
    +}
    +
    +.tabs-below > .nav-tabs {
    +  border-top: 1px solid #ddd;
    +}
    +
    +.tabs-below > .nav-tabs > li {
    +  margin-top: -1px;
    +  margin-bottom: 0;
    +}
    +
    +.tabs-below > .nav-tabs > li > a {
    +  -webkit-border-radius: 0 0 4px 4px;
    +     -moz-border-radius: 0 0 4px 4px;
    +          border-radius: 0 0 4px 4px;
    +}
    +
    +.tabs-below > .nav-tabs > li > a:hover,
    +.tabs-below > .nav-tabs > li > a:focus {
    +  border-top-color: #ddd;
    +  border-bottom-color: transparent;
    +}
    +
    +.tabs-below > .nav-tabs > .active > a,
    +.tabs-below > .nav-tabs > .active > a:hover,
    +.tabs-below > .nav-tabs > .active > a:focus {
    +  border-color: transparent #ddd #ddd #ddd;
    +}
    +
    +.tabs-left > .nav-tabs > li,
    +.tabs-right > .nav-tabs > li {
    +  float: none;
    +}
    +
    +.tabs-left > .nav-tabs > li > a,
    +.tabs-right > .nav-tabs > li > a {
    +  min-width: 74px;
    +  margin-right: 0;
    +  margin-bottom: 3px;
    +}
    +
    +.tabs-left > .nav-tabs {
    +  float: left;
    +  margin-right: 19px;
    +  border-right: 1px solid #ddd;
    +}
    +
    +.tabs-left > .nav-tabs > li > a {
    +  margin-right: -1px;
    +  -webkit-border-radius: 4px 0 0 4px;
    +     -moz-border-radius: 4px 0 0 4px;
    +          border-radius: 4px 0 0 4px;
    +}
    +
    +.tabs-left > .nav-tabs > li > a:hover,
    +.tabs-left > .nav-tabs > li > a:focus {
    +  border-color: #eeeeee #dddddd #eeeeee #eeeeee;
    +}
    +
    +.tabs-left > .nav-tabs .active > a,
    +.tabs-left > .nav-tabs .active > a:hover,
    +.tabs-left > .nav-tabs .active > a:focus {
    +  border-color: #ddd transparent #ddd #ddd;
    +  *border-right-color: #ffffff;
    +}
    +
    +.tabs-right > .nav-tabs {
    +  float: right;
    +  margin-left: 19px;
    +  border-left: 1px solid #ddd;
    +}
    +
    +.tabs-right > .nav-tabs > li > a {
    +  margin-left: -1px;
    +  -webkit-border-radius: 0 4px 4px 0;
    +     -moz-border-radius: 0 4px 4px 0;
    +          border-radius: 0 4px 4px 0;
    +}
    +
    +.tabs-right > .nav-tabs > li > a:hover,
    +.tabs-right > .nav-tabs > li > a:focus {
    +  border-color: #eeeeee #eeeeee #eeeeee #dddddd;
    +}
    +
    +.tabs-right > .nav-tabs .active > a,
    +.tabs-right > .nav-tabs .active > a:hover,
    +.tabs-right > .nav-tabs .active > a:focus {
    +  border-color: #ddd #ddd #ddd transparent;
    +  *border-left-color: #ffffff;
    +}
    +
    +.nav > .disabled > a {
    +  color: #999999;
    +}
    +
    +.nav > .disabled > a:hover,
    +.nav > .disabled > a:focus {
    +  text-decoration: none;
    +  cursor: default;
    +  background-color: transparent;
    +}
    +
    +.navbar {
    +  *position: relative;
    +  *z-index: 2;
    +  margin-bottom: 20px;
    +  overflow: visible;
    +}
    +
    +.navbar-inner {
    +  min-height: 40px;
    +  padding-right: 20px;
    +  padding-left: 20px;
    +  background-color: #fafafa;
    +  background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
    +  background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
    +  background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
    +  background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
    +  background-repeat: repeat-x;
    +  border: 1px solid #d4d4d4;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
    +  *zoom: 1;
    +  -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
    +     -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
    +          box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
    +}
    +
    +.navbar-inner:before,
    +.navbar-inner:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.navbar-inner:after {
    +  clear: both;
    +}
    +
    +.navbar .container {
    +  width: auto;
    +}
    +
    +.nav-collapse.collapse {
    +  height: auto;
    +  overflow: visible;
    +}
    +
    +.navbar .brand {
    +  display: block;
    +  float: left;
    +  padding: 10px 20px 10px;
    +  margin-left: -20px;
    +  font-size: 20px;
    +  font-weight: 200;
    +  color: #777777;
    +  text-shadow: 0 1px 0 #ffffff;
    +}
    +
    +.navbar .brand:hover,
    +.navbar .brand:focus {
    +  text-decoration: none;
    +}
    +
    +.navbar-text {
    +  margin-bottom: 0;
    +  line-height: 40px;
    +  color: #777777;
    +}
    +
    +.navbar-link {
    +  color: #777777;
    +}
    +
    +.navbar-link:hover,
    +.navbar-link:focus {
    +  color: #333333;
    +}
    +
    +.navbar .divider-vertical {
    +  height: 40px;
    +  margin: 0 9px;
    +  border-right: 1px solid #ffffff;
    +  border-left: 1px solid #f2f2f2;
    +}
    +
    +.navbar .btn,
    +.navbar .btn-group {
    +  margin-top: 5px;
    +}
    +
    +.navbar .btn-group .btn,
    +.navbar .input-prepend .btn,
    +.navbar .input-append .btn,
    +.navbar .input-prepend .btn-group,
    +.navbar .input-append .btn-group {
    +  margin-top: 0;
    +}
    +
    +.navbar-form {
    +  margin-bottom: 0;
    +  *zoom: 1;
    +}
    +
    +.navbar-form:before,
    +.navbar-form:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.navbar-form:after {
    +  clear: both;
    +}
    +
    +.navbar-form input,
    +.navbar-form select,
    +.navbar-form .radio,
    +.navbar-form .checkbox {
    +  margin-top: 5px;
    +}
    +
    +.navbar-form input,
    +.navbar-form select,
    +.navbar-form .btn {
    +  display: inline-block;
    +  margin-bottom: 0;
    +}
    +
    +.navbar-form input[type="image"],
    +.navbar-form input[type="checkbox"],
    +.navbar-form input[type="radio"] {
    +  margin-top: 3px;
    +}
    +
    +.navbar-form .input-append,
    +.navbar-form .input-prepend {
    +  margin-top: 5px;
    +  white-space: nowrap;
    +}
    +
    +.navbar-form .input-append input,
    +.navbar-form .input-prepend input {
    +  margin-top: 0;
    +}
    +
    +.navbar-search {
    +  position: relative;
    +  float: left;
    +  margin-top: 5px;
    +  margin-bottom: 0;
    +}
    +
    +.navbar-search .search-query {
    +  padding: 4px 14px;
    +  margin-bottom: 0;
    +  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    +  font-size: 13px;
    +  font-weight: normal;
    +  line-height: 1;
    +  -webkit-border-radius: 15px;
    +     -moz-border-radius: 15px;
    +          border-radius: 15px;
    +}
    +
    +.navbar-static-top {
    +  position: static;
    +  margin-bottom: 0;
    +}
    +
    +.navbar-static-top .navbar-inner {
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.navbar-fixed-top,
    +.navbar-fixed-bottom {
    +  position: fixed;
    +  right: 0;
    +  left: 0;
    +  z-index: 1030;
    +  margin-bottom: 0;
    +}
    +
    +.navbar-fixed-top .navbar-inner,
    +.navbar-static-top .navbar-inner {
    +  border-width: 0 0 1px;
    +}
    +
    +.navbar-fixed-bottom .navbar-inner {
    +  border-width: 1px 0 0;
    +}
    +
    +.navbar-fixed-top .navbar-inner,
    +.navbar-fixed-bottom .navbar-inner {
    +  padding-right: 0;
    +  padding-left: 0;
    +  -webkit-border-radius: 0;
    +     -moz-border-radius: 0;
    +          border-radius: 0;
    +}
    +
    +.navbar-static-top .container,
    +.navbar-fixed-top .container,
    +.navbar-fixed-bottom .container {
    +  width: 940px;
    +}
    +
    +.navbar-fixed-top {
    +  top: 0;
    +}
    +
    +.navbar-fixed-top .navbar-inner,
    +.navbar-static-top .navbar-inner {
    +  -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
    +     -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
    +          box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
    +}
    +
    +.navbar-fixed-bottom {
    +  bottom: 0;
    +}
    +
    +.navbar-fixed-bottom .navbar-inner {
    +  -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
    +     -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
    +          box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
    +}
    +
    +.navbar .nav {
    +  position: relative;
    +  left: 0;
    +  display: block;
    +  float: left;
    +  margin: 0 10px 0 0;
    +}
    +
    +.navbar .nav.pull-right {
    +  float: right;
    +  margin-right: 0;
    +}
    +
    +.navbar .nav > li {
    +  float: left;
    +}
    +
    +.navbar .nav > li > a {
    +  float: none;
    +  padding: 10px 15px 10px;
    +  color: #777777;
    +  text-decoration: none;
    +  text-shadow: 0 1px 0 #ffffff;
    +}
    +
    +.navbar .nav .dropdown-toggle .caret {
    +  margin-top: 8px;
    +}
    +
    +.navbar .nav > li > a:focus,
    +.navbar .nav > li > a:hover {
    +  color: #333333;
    +  text-decoration: none;
    +  background-color: transparent;
    +}
    +
    +.navbar .nav > .active > a,
    +.navbar .nav > .active > a:hover,
    +.navbar .nav > .active > a:focus {
    +  color: #555555;
    +  text-decoration: none;
    +  background-color: #e5e5e5;
    +  -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
    +     -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
    +          box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
    +}
    +
    +.navbar .btn-navbar {
    +  display: none;
    +  float: right;
    +  padding: 7px 10px;
    +  margin-right: 5px;
    +  margin-left: 5px;
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #ededed;
    +  *background-color: #e5e5e5;
    +  background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
    +  background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
    +  background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
    +  background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
    +  background-repeat: repeat-x;
    +  border-color: #e5e5e5 #e5e5e5 #bfbfbf;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
    +     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
    +          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
    +}
    +
    +.navbar .btn-navbar:hover,
    +.navbar .btn-navbar:focus,
    +.navbar .btn-navbar:active,
    +.navbar .btn-navbar.active,
    +.navbar .btn-navbar.disabled,
    +.navbar .btn-navbar[disabled] {
    +  color: #ffffff;
    +  background-color: #e5e5e5;
    +  *background-color: #d9d9d9;
    +}
    +
    +.navbar .btn-navbar:active,
    +.navbar .btn-navbar.active {
    +  background-color: #cccccc \9;
    +}
    +
    +.navbar .btn-navbar .icon-bar {
    +  display: block;
    +  width: 18px;
    +  height: 2px;
    +  background-color: #f5f5f5;
    +  -webkit-border-radius: 1px;
    +     -moz-border-radius: 1px;
    +          border-radius: 1px;
    +  -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
    +     -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
    +          box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
    +}
    +
    +.btn-navbar .icon-bar + .icon-bar {
    +  margin-top: 3px;
    +}
    +
    +.navbar .nav > li > .dropdown-menu:before {
    +  position: absolute;
    +  top: -7px;
    +  left: 9px;
    +  display: inline-block;
    +  border-right: 7px solid transparent;
    +  border-bottom: 7px solid #ccc;
    +  border-left: 7px solid transparent;
    +  border-bottom-color: rgba(0, 0, 0, 0.2);
    +  content: '';
    +}
    +
    +.navbar .nav > li > .dropdown-menu:after {
    +  position: absolute;
    +  top: -6px;
    +  left: 10px;
    +  display: inline-block;
    +  border-right: 6px solid transparent;
    +  border-bottom: 6px solid #ffffff;
    +  border-left: 6px solid transparent;
    +  content: '';
    +}
    +
    +.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
    +  top: auto;
    +  bottom: -7px;
    +  border-top: 7px solid #ccc;
    +  border-bottom: 0;
    +  border-top-color: rgba(0, 0, 0, 0.2);
    +}
    +
    +.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
    +  top: auto;
    +  bottom: -6px;
    +  border-top: 6px solid #ffffff;
    +  border-bottom: 0;
    +}
    +
    +.navbar .nav li.dropdown > a:hover .caret,
    +.navbar .nav li.dropdown > a:focus .caret {
    +  border-top-color: #333333;
    +  border-bottom-color: #333333;
    +}
    +
    +.navbar .nav li.dropdown.open > .dropdown-toggle,
    +.navbar .nav li.dropdown.active > .dropdown-toggle,
    +.navbar .nav li.dropdown.open.active > .dropdown-toggle {
    +  color: #555555;
    +  background-color: #e5e5e5;
    +}
    +
    +.navbar .nav li.dropdown > .dropdown-toggle .caret {
    +  border-top-color: #777777;
    +  border-bottom-color: #777777;
    +}
    +
    +.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
    +.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
    +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
    +  border-top-color: #555555;
    +  border-bottom-color: #555555;
    +}
    +
    +.navbar .pull-right > li > .dropdown-menu,
    +.navbar .nav > li > .dropdown-menu.pull-right {
    +  right: 0;
    +  left: auto;
    +}
    +
    +.navbar .pull-right > li > .dropdown-menu:before,
    +.navbar .nav > li > .dropdown-menu.pull-right:before {
    +  right: 12px;
    +  left: auto;
    +}
    +
    +.navbar .pull-right > li > .dropdown-menu:after,
    +.navbar .nav > li > .dropdown-menu.pull-right:after {
    +  right: 13px;
    +  left: auto;
    +}
    +
    +.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
    +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
    +  right: 100%;
    +  left: auto;
    +  margin-right: -1px;
    +  margin-left: 0;
    +  -webkit-border-radius: 6px 0 6px 6px;
    +     -moz-border-radius: 6px 0 6px 6px;
    +          border-radius: 6px 0 6px 6px;
    +}
    +
    +.navbar-inverse .navbar-inner {
    +  background-color: #1b1b1b;
    +  background-image: -moz-linear-gradient(top, #222222, #111111);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
    +  background-image: -webkit-linear-gradient(top, #222222, #111111);
    +  background-image: -o-linear-gradient(top, #222222, #111111);
    +  background-image: linear-gradient(to bottom, #222222, #111111);
    +  background-repeat: repeat-x;
    +  border-color: #252525;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
    +}
    +
    +.navbar-inverse .brand,
    +.navbar-inverse .nav > li > a {
    +  color: #999999;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +}
    +
    +.navbar-inverse .brand:hover,
    +.navbar-inverse .nav > li > a:hover,
    +.navbar-inverse .brand:focus,
    +.navbar-inverse .nav > li > a:focus {
    +  color: #ffffff;
    +}
    +
    +.navbar-inverse .brand {
    +  color: #999999;
    +}
    +
    +.navbar-inverse .navbar-text {
    +  color: #999999;
    +}
    +
    +.navbar-inverse .nav > li > a:focus,
    +.navbar-inverse .nav > li > a:hover {
    +  color: #ffffff;
    +  background-color: transparent;
    +}
    +
    +.navbar-inverse .nav .active > a,
    +.navbar-inverse .nav .active > a:hover,
    +.navbar-inverse .nav .active > a:focus {
    +  color: #ffffff;
    +  background-color: #111111;
    +}
    +
    +.navbar-inverse .navbar-link {
    +  color: #999999;
    +}
    +
    +.navbar-inverse .navbar-link:hover,
    +.navbar-inverse .navbar-link:focus {
    +  color: #ffffff;
    +}
    +
    +.navbar-inverse .divider-vertical {
    +  border-right-color: #222222;
    +  border-left-color: #111111;
    +}
    +
    +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
    +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
    +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
    +  color: #ffffff;
    +  background-color: #111111;
    +}
    +
    +.navbar-inverse .nav li.dropdown > a:hover .caret,
    +.navbar-inverse .nav li.dropdown > a:focus .caret {
    +  border-top-color: #ffffff;
    +  border-bottom-color: #ffffff;
    +}
    +
    +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
    +  border-top-color: #999999;
    +  border-bottom-color: #999999;
    +}
    +
    +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
    +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
    +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
    +  border-top-color: #ffffff;
    +  border-bottom-color: #ffffff;
    +}
    +
    +.navbar-inverse .navbar-search .search-query {
    +  color: #ffffff;
    +  background-color: #515151;
    +  border-color: #111111;
    +  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
    +     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
    +          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
    +  -webkit-transition: none;
    +     -moz-transition: none;
    +       -o-transition: none;
    +          transition: none;
    +}
    +
    +.navbar-inverse .navbar-search .search-query:-moz-placeholder {
    +  color: #cccccc;
    +}
    +
    +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
    +  color: #cccccc;
    +}
    +
    +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
    +  color: #cccccc;
    +}
    +
    +.navbar-inverse .navbar-search .search-query:focus,
    +.navbar-inverse .navbar-search .search-query.focused {
    +  padding: 5px 15px;
    +  color: #333333;
    +  text-shadow: 0 1px 0 #ffffff;
    +  background-color: #ffffff;
    +  border: 0;
    +  outline: 0;
    +  -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
    +     -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
    +          box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
    +}
    +
    +.navbar-inverse .btn-navbar {
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #0e0e0e;
    +  *background-color: #040404;
    +  background-image: -moz-linear-gradient(top, #151515, #040404);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
    +  background-image: -webkit-linear-gradient(top, #151515, #040404);
    +  background-image: -o-linear-gradient(top, #151515, #040404);
    +  background-image: linear-gradient(to bottom, #151515, #040404);
    +  background-repeat: repeat-x;
    +  border-color: #040404 #040404 #000000;
    +  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
    +  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    +}
    +
    +.navbar-inverse .btn-navbar:hover,
    +.navbar-inverse .btn-navbar:focus,
    +.navbar-inverse .btn-navbar:active,
    +.navbar-inverse .btn-navbar.active,
    +.navbar-inverse .btn-navbar.disabled,
    +.navbar-inverse .btn-navbar[disabled] {
    +  color: #ffffff;
    +  background-color: #040404;
    +  *background-color: #000000;
    +}
    +
    +.navbar-inverse .btn-navbar:active,
    +.navbar-inverse .btn-navbar.active {
    +  background-color: #000000 \9;
    +}
    +
    +.breadcrumb {
    +  padding: 8px 15px;
    +  margin: 0 0 20px;
    +  list-style: none;
    +  background-color: #f5f5f5;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.breadcrumb > li {
    +  display: inline-block;
    +  *display: inline;
    +  text-shadow: 0 1px 0 #ffffff;
    +  *zoom: 1;
    +}
    +
    +.breadcrumb > li > .divider {
    +  padding: 0 5px;
    +  color: #ccc;
    +}
    +
    +.breadcrumb > .active {
    +  color: #999999;
    +}
    +
    +.pagination {
    +  margin: 20px 0;
    +}
    +
    +.pagination ul {
    +  display: inline-block;
    +  *display: inline;
    +  margin-bottom: 0;
    +  margin-left: 0;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  *zoom: 1;
    +  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
    +     -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
    +          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
    +}
    +
    +.pagination ul > li {
    +  display: inline;
    +}
    +
    +.pagination ul > li > a,
    +.pagination ul > li > span {
    +  float: left;
    +  padding: 4px 12px;
    +  line-height: 20px;
    +  text-decoration: none;
    +  background-color: #ffffff;
    +  border: 1px solid #dddddd;
    +  border-left-width: 0;
    +}
    +
    +.pagination ul > li > a:hover,
    +.pagination ul > li > a:focus,
    +.pagination ul > .active > a,
    +.pagination ul > .active > span {
    +  background-color: #f5f5f5;
    +}
    +
    +.pagination ul > .active > a,
    +.pagination ul > .active > span {
    +  color: #999999;
    +  cursor: default;
    +}
    +
    +.pagination ul > .disabled > span,
    +.pagination ul > .disabled > a,
    +.pagination ul > .disabled > a:hover,
    +.pagination ul > .disabled > a:focus {
    +  color: #999999;
    +  cursor: default;
    +  background-color: transparent;
    +}
    +
    +.pagination ul > li:first-child > a,
    +.pagination ul > li:first-child > span {
    +  border-left-width: 1px;
    +  -webkit-border-bottom-left-radius: 4px;
    +          border-bottom-left-radius: 4px;
    +  -webkit-border-top-left-radius: 4px;
    +          border-top-left-radius: 4px;
    +  -moz-border-radius-bottomleft: 4px;
    +  -moz-border-radius-topleft: 4px;
    +}
    +
    +.pagination ul > li:last-child > a,
    +.pagination ul > li:last-child > span {
    +  -webkit-border-top-right-radius: 4px;
    +          border-top-right-radius: 4px;
    +  -webkit-border-bottom-right-radius: 4px;
    +          border-bottom-right-radius: 4px;
    +  -moz-border-radius-topright: 4px;
    +  -moz-border-radius-bottomright: 4px;
    +}
    +
    +.pagination-centered {
    +  text-align: center;
    +}
    +
    +.pagination-right {
    +  text-align: right;
    +}
    +
    +.pagination-large ul > li > a,
    +.pagination-large ul > li > span {
    +  padding: 11px 19px;
    +  font-size: 17.5px;
    +}
    +
    +.pagination-large ul > li:first-child > a,
    +.pagination-large ul > li:first-child > span {
    +  -webkit-border-bottom-left-radius: 6px;
    +          border-bottom-left-radius: 6px;
    +  -webkit-border-top-left-radius: 6px;
    +          border-top-left-radius: 6px;
    +  -moz-border-radius-bottomleft: 6px;
    +  -moz-border-radius-topleft: 6px;
    +}
    +
    +.pagination-large ul > li:last-child > a,
    +.pagination-large ul > li:last-child > span {
    +  -webkit-border-top-right-radius: 6px;
    +          border-top-right-radius: 6px;
    +  -webkit-border-bottom-right-radius: 6px;
    +          border-bottom-right-radius: 6px;
    +  -moz-border-radius-topright: 6px;
    +  -moz-border-radius-bottomright: 6px;
    +}
    +
    +.pagination-mini ul > li:first-child > a,
    +.pagination-small ul > li:first-child > a,
    +.pagination-mini ul > li:first-child > span,
    +.pagination-small ul > li:first-child > span {
    +  -webkit-border-bottom-left-radius: 3px;
    +          border-bottom-left-radius: 3px;
    +  -webkit-border-top-left-radius: 3px;
    +          border-top-left-radius: 3px;
    +  -moz-border-radius-bottomleft: 3px;
    +  -moz-border-radius-topleft: 3px;
    +}
    +
    +.pagination-mini ul > li:last-child > a,
    +.pagination-small ul > li:last-child > a,
    +.pagination-mini ul > li:last-child > span,
    +.pagination-small ul > li:last-child > span {
    +  -webkit-border-top-right-radius: 3px;
    +          border-top-right-radius: 3px;
    +  -webkit-border-bottom-right-radius: 3px;
    +          border-bottom-right-radius: 3px;
    +  -moz-border-radius-topright: 3px;
    +  -moz-border-radius-bottomright: 3px;
    +}
    +
    +.pagination-small ul > li > a,
    +.pagination-small ul > li > span {
    +  padding: 2px 10px;
    +  font-size: 11.9px;
    +}
    +
    +.pagination-mini ul > li > a,
    +.pagination-mini ul > li > span {
    +  padding: 0 6px;
    +  font-size: 10.5px;
    +}
    +
    +.pager {
    +  margin: 20px 0;
    +  text-align: center;
    +  list-style: none;
    +  *zoom: 1;
    +}
    +
    +.pager:before,
    +.pager:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.pager:after {
    +  clear: both;
    +}
    +
    +.pager li {
    +  display: inline;
    +}
    +
    +.pager li > a,
    +.pager li > span {
    +  display: inline-block;
    +  padding: 5px 14px;
    +  background-color: #fff;
    +  border: 1px solid #ddd;
    +  -webkit-border-radius: 15px;
    +     -moz-border-radius: 15px;
    +          border-radius: 15px;
    +}
    +
    +.pager li > a:hover,
    +.pager li > a:focus {
    +  text-decoration: none;
    +  background-color: #f5f5f5;
    +}
    +
    +.pager .next > a,
    +.pager .next > span {
    +  float: right;
    +}
    +
    +.pager .previous > a,
    +.pager .previous > span {
    +  float: left;
    +}
    +
    +.pager .disabled > a,
    +.pager .disabled > a:hover,
    +.pager .disabled > a:focus,
    +.pager .disabled > span {
    +  color: #999999;
    +  cursor: default;
    +  background-color: #fff;
    +}
    +
    +.modal-backdrop {
    +  position: fixed;
    +  top: 0;
    +  right: 0;
    +  bottom: 0;
    +  left: 0;
    +  z-index: 1040;
    +  background-color: #000000;
    +}
    +
    +.modal-backdrop.fade {
    +  opacity: 0;
    +}
    +
    +.modal-backdrop,
    +.modal-backdrop.fade.in {
    +  opacity: 0.8;
    +  filter: alpha(opacity=80);
    +}
    +
    +.modal {
    +  position: fixed;
    +  top: 10%;
    +  left: 50%;
    +  z-index: 1050;
    +  width: 560px;
    +  margin-left: -280px;
    +  background-color: #ffffff;
    +  border: 1px solid #999;
    +  border: 1px solid rgba(0, 0, 0, 0.3);
    +  *border: 1px solid #999;
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +  outline: none;
    +  -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
    +     -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
    +          box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
    +  -webkit-background-clip: padding-box;
    +     -moz-background-clip: padding-box;
    +          background-clip: padding-box;
    +}
    +
    +.modal.fade {
    +  top: -25%;
    +  -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
    +     -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
    +       -o-transition: opacity 0.3s linear, top 0.3s ease-out;
    +          transition: opacity 0.3s linear, top 0.3s ease-out;
    +}
    +
    +.modal.fade.in {
    +  top: 10%;
    +}
    +
    +.modal-header {
    +  padding: 9px 15px;
    +  border-bottom: 1px solid #eee;
    +}
    +
    +.modal-header .close {
    +  margin-top: 2px;
    +}
    +
    +.modal-header h3 {
    +  margin: 0;
    +  line-height: 30px;
    +}
    +
    +.modal-body {
    +  position: relative;
    +  max-height: 400px;
    +  padding: 15px;
    +  overflow-y: auto;
    +}
    +
    +.modal-form {
    +  margin-bottom: 0;
    +}
    +
    +.modal-footer {
    +  padding: 14px 15px 15px;
    +  margin-bottom: 0;
    +  text-align: right;
    +  background-color: #f5f5f5;
    +  border-top: 1px solid #ddd;
    +  -webkit-border-radius: 0 0 6px 6px;
    +     -moz-border-radius: 0 0 6px 6px;
    +          border-radius: 0 0 6px 6px;
    +  *zoom: 1;
    +  -webkit-box-shadow: inset 0 1px 0 #ffffff;
    +     -moz-box-shadow: inset 0 1px 0 #ffffff;
    +          box-shadow: inset 0 1px 0 #ffffff;
    +}
    +
    +.modal-footer:before,
    +.modal-footer:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.modal-footer:after {
    +  clear: both;
    +}
    +
    +.modal-footer .btn + .btn {
    +  margin-bottom: 0;
    +  margin-left: 5px;
    +}
    +
    +.modal-footer .btn-group .btn + .btn {
    +  margin-left: -1px;
    +}
    +
    +.modal-footer .btn-block + .btn-block {
    +  margin-left: 0;
    +}
    +
    +.tooltip {
    +  position: absolute;
    +  z-index: 1030;
    +  display: block;
    +  font-size: 11px;
    +  line-height: 1.4;
    +  opacity: 0;
    +  filter: alpha(opacity=0);
    +  visibility: visible;
    +}
    +
    +.tooltip.in {
    +  opacity: 0.8;
    +  filter: alpha(opacity=80);
    +}
    +
    +.tooltip.top {
    +  padding: 5px 0;
    +  margin-top: -3px;
    +}
    +
    +.tooltip.right {
    +  padding: 0 5px;
    +  margin-left: 3px;
    +}
    +
    +.tooltip.bottom {
    +  padding: 5px 0;
    +  margin-top: 3px;
    +}
    +
    +.tooltip.left {
    +  padding: 0 5px;
    +  margin-left: -3px;
    +}
    +
    +.tooltip-inner {
    +  max-width: 200px;
    +  padding: 8px;
    +  color: #ffffff;
    +  text-align: center;
    +  text-decoration: none;
    +  background-color: #000000;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.tooltip-arrow {
    +  position: absolute;
    +  width: 0;
    +  height: 0;
    +  border-color: transparent;
    +  border-style: solid;
    +}
    +
    +.tooltip.top .tooltip-arrow {
    +  bottom: 0;
    +  left: 50%;
    +  margin-left: -5px;
    +  border-top-color: #000000;
    +  border-width: 5px 5px 0;
    +}
    +
    +.tooltip.right .tooltip-arrow {
    +  top: 50%;
    +  left: 0;
    +  margin-top: -5px;
    +  border-right-color: #000000;
    +  border-width: 5px 5px 5px 0;
    +}
    +
    +.tooltip.left .tooltip-arrow {
    +  top: 50%;
    +  right: 0;
    +  margin-top: -5px;
    +  border-left-color: #000000;
    +  border-width: 5px 0 5px 5px;
    +}
    +
    +.tooltip.bottom .tooltip-arrow {
    +  top: 0;
    +  left: 50%;
    +  margin-left: -5px;
    +  border-bottom-color: #000000;
    +  border-width: 0 5px 5px;
    +}
    +
    +.popover {
    +  position: absolute;
    +  top: 0;
    +  left: 0;
    +  z-index: 1010;
    +  display: none;
    +  max-width: 276px;
    +  padding: 1px;
    +  text-align: left;
    +  white-space: normal;
    +  background-color: #ffffff;
    +  border: 1px solid #ccc;
    +  border: 1px solid rgba(0, 0, 0, 0.2);
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    +  -webkit-background-clip: padding-box;
    +     -moz-background-clip: padding;
    +          background-clip: padding-box;
    +}
    +
    +.popover.top {
    +  margin-top: -10px;
    +}
    +
    +.popover.right {
    +  margin-left: 10px;
    +}
    +
    +.popover.bottom {
    +  margin-top: 10px;
    +}
    +
    +.popover.left {
    +  margin-left: -10px;
    +}
    +
    +.popover-title {
    +  padding: 8px 14px;
    +  margin: 0;
    +  font-size: 14px;
    +  font-weight: normal;
    +  line-height: 18px;
    +  background-color: #f7f7f7;
    +  border-bottom: 1px solid #ebebeb;
    +  -webkit-border-radius: 5px 5px 0 0;
    +     -moz-border-radius: 5px 5px 0 0;
    +          border-radius: 5px 5px 0 0;
    +}
    +
    +.popover-title:empty {
    +  display: none;
    +}
    +
    +.popover-content {
    +  padding: 9px 14px;
    +}
    +
    +.popover .arrow,
    +.popover .arrow:after {
    +  position: absolute;
    +  display: block;
    +  width: 0;
    +  height: 0;
    +  border-color: transparent;
    +  border-style: solid;
    +}
    +
    +.popover .arrow {
    +  border-width: 11px;
    +}
    +
    +.popover .arrow:after {
    +  border-width: 10px;
    +  content: "";
    +}
    +
    +.popover.top .arrow {
    +  bottom: -11px;
    +  left: 50%;
    +  margin-left: -11px;
    +  border-top-color: #999;
    +  border-top-color: rgba(0, 0, 0, 0.25);
    +  border-bottom-width: 0;
    +}
    +
    +.popover.top .arrow:after {
    +  bottom: 1px;
    +  margin-left: -10px;
    +  border-top-color: #ffffff;
    +  border-bottom-width: 0;
    +}
    +
    +.popover.right .arrow {
    +  top: 50%;
    +  left: -11px;
    +  margin-top: -11px;
    +  border-right-color: #999;
    +  border-right-color: rgba(0, 0, 0, 0.25);
    +  border-left-width: 0;
    +}
    +
    +.popover.right .arrow:after {
    +  bottom: -10px;
    +  left: 1px;
    +  border-right-color: #ffffff;
    +  border-left-width: 0;
    +}
    +
    +.popover.bottom .arrow {
    +  top: -11px;
    +  left: 50%;
    +  margin-left: -11px;
    +  border-bottom-color: #999;
    +  border-bottom-color: rgba(0, 0, 0, 0.25);
    +  border-top-width: 0;
    +}
    +
    +.popover.bottom .arrow:after {
    +  top: 1px;
    +  margin-left: -10px;
    +  border-bottom-color: #ffffff;
    +  border-top-width: 0;
    +}
    +
    +.popover.left .arrow {
    +  top: 50%;
    +  right: -11px;
    +  margin-top: -11px;
    +  border-left-color: #999;
    +  border-left-color: rgba(0, 0, 0, 0.25);
    +  border-right-width: 0;
    +}
    +
    +.popover.left .arrow:after {
    +  right: 1px;
    +  bottom: -10px;
    +  border-left-color: #ffffff;
    +  border-right-width: 0;
    +}
    +
    +.thumbnails {
    +  margin-left: -20px;
    +  list-style: none;
    +  *zoom: 1;
    +}
    +
    +.thumbnails:before,
    +.thumbnails:after {
    +  display: table;
    +  line-height: 0;
    +  content: "";
    +}
    +
    +.thumbnails:after {
    +  clear: both;
    +}
    +
    +.row-fluid .thumbnails {
    +  margin-left: 0;
    +}
    +
    +.thumbnails > li {
    +  float: left;
    +  margin-bottom: 20px;
    +  margin-left: 20px;
    +}
    +
    +.thumbnail {
    +  display: block;
    +  padding: 4px;
    +  line-height: 20px;
    +  border: 1px solid #ddd;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
    +     -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
    +          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
    +  -webkit-transition: all 0.2s ease-in-out;
    +     -moz-transition: all 0.2s ease-in-out;
    +       -o-transition: all 0.2s ease-in-out;
    +          transition: all 0.2s ease-in-out;
    +}
    +
    +a.thumbnail:hover,
    +a.thumbnail:focus {
    +  border-color: #0088cc;
    +  -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
    +     -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
    +          box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
    +}
    +
    +.thumbnail > img {
    +  display: block;
    +  max-width: 100%;
    +  margin-right: auto;
    +  margin-left: auto;
    +}
    +
    +.thumbnail .caption {
    +  padding: 9px;
    +  color: #555555;
    +}
    +
    +.media,
    +.media-body {
    +  overflow: hidden;
    +  *overflow: visible;
    +  zoom: 1;
    +}
    +
    +.media,
    +.media .media {
    +  margin-top: 15px;
    +}
    +
    +.media:first-child {
    +  margin-top: 0;
    +}
    +
    +.media-object {
    +  display: block;
    +}
    +
    +.media-heading {
    +  margin: 0 0 5px;
    +}
    +
    +.media > .pull-left {
    +  margin-right: 10px;
    +}
    +
    +.media > .pull-right {
    +  margin-left: 10px;
    +}
    +
    +.media-list {
    +  margin-left: 0;
    +  list-style: none;
    +}
    +
    +.label,
    +.badge {
    +  display: inline-block;
    +  padding: 2px 4px;
    +  font-size: 11.844px;
    +  font-weight: bold;
    +  line-height: 14px;
    +  color: #ffffff;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  white-space: nowrap;
    +  vertical-align: baseline;
    +  background-color: #999999;
    +}
    +
    +.label {
    +  -webkit-border-radius: 3px;
    +     -moz-border-radius: 3px;
    +          border-radius: 3px;
    +}
    +
    +.badge {
    +  padding-right: 9px;
    +  padding-left: 9px;
    +  -webkit-border-radius: 9px;
    +     -moz-border-radius: 9px;
    +          border-radius: 9px;
    +}
    +
    +.label:empty,
    +.badge:empty {
    +  display: none;
    +}
    +
    +a.label:hover,
    +a.label:focus,
    +a.badge:hover,
    +a.badge:focus {
    +  color: #ffffff;
    +  text-decoration: none;
    +  cursor: pointer;
    +}
    +
    +.label-important,
    +.badge-important {
    +  background-color: #b94a48;
    +}
    +
    +.label-important[href],
    +.badge-important[href] {
    +  background-color: #953b39;
    +}
    +
    +.label-warning,
    +.badge-warning {
    +  background-color: #f89406;
    +}
    +
    +.label-warning[href],
    +.badge-warning[href] {
    +  background-color: #c67605;
    +}
    +
    +.label-success,
    +.badge-success {
    +  background-color: #468847;
    +}
    +
    +.label-success[href],
    +.badge-success[href] {
    +  background-color: #356635;
    +}
    +
    +.label-info,
    +.badge-info {
    +  background-color: #3a87ad;
    +}
    +
    +.label-info[href],
    +.badge-info[href] {
    +  background-color: #2d6987;
    +}
    +
    +.label-inverse,
    +.badge-inverse {
    +  background-color: #333333;
    +}
    +
    +.label-inverse[href],
    +.badge-inverse[href] {
    +  background-color: #1a1a1a;
    +}
    +
    +.btn .label,
    +.btn .badge {
    +  position: relative;
    +  top: -1px;
    +}
    +
    +.btn-mini .label,
    +.btn-mini .badge {
    +  top: 0;
    +}
    +
    +@-webkit-keyframes progress-bar-stripes {
    +  from {
    +    background-position: 40px 0;
    +  }
    +  to {
    +    background-position: 0 0;
    +  }
    +}
    +
    +@-moz-keyframes progress-bar-stripes {
    +  from {
    +    background-position: 40px 0;
    +  }
    +  to {
    +    background-position: 0 0;
    +  }
    +}
    +
    +@-ms-keyframes progress-bar-stripes {
    +  from {
    +    background-position: 40px 0;
    +  }
    +  to {
    +    background-position: 0 0;
    +  }
    +}
    +
    +@-o-keyframes progress-bar-stripes {
    +  from {
    +    background-position: 0 0;
    +  }
    +  to {
    +    background-position: 40px 0;
    +  }
    +}
    +
    +@keyframes progress-bar-stripes {
    +  from {
    +    background-position: 40px 0;
    +  }
    +  to {
    +    background-position: 0 0;
    +  }
    +}
    +
    +.progress {
    +  height: 20px;
    +  margin-bottom: 20px;
    +  overflow: hidden;
    +  background-color: #f7f7f7;
    +  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
    +  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
    +  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
    +  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
    +  background-repeat: repeat-x;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
    +  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
    +     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
    +          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
    +}
    +
    +.progress .bar {
    +  float: left;
    +  width: 0;
    +  height: 100%;
    +  font-size: 12px;
    +  color: #ffffff;
    +  text-align: center;
    +  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
    +  background-color: #0e90d2;
    +  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
    +  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
    +  background-image: -o-linear-gradient(top, #149bdf, #0480be);
    +  background-image: linear-gradient(to bottom, #149bdf, #0480be);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
    +  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +     -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +  -webkit-box-sizing: border-box;
    +     -moz-box-sizing: border-box;
    +          box-sizing: border-box;
    +  -webkit-transition: width 0.6s ease;
    +     -moz-transition: width 0.6s ease;
    +       -o-transition: width 0.6s ease;
    +          transition: width 0.6s ease;
    +}
    +
    +.progress .bar + .bar {
    +  -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +     -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +          box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
    +}
    +
    +.progress-striped .bar {
    +  background-color: #149bdf;
    +  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
    +  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  -webkit-background-size: 40px 40px;
    +     -moz-background-size: 40px 40px;
    +       -o-background-size: 40px 40px;
    +          background-size: 40px 40px;
    +}
    +
    +.progress.active .bar {
    +  -webkit-animation: progress-bar-stripes 2s linear infinite;
    +     -moz-animation: progress-bar-stripes 2s linear infinite;
    +      -ms-animation: progress-bar-stripes 2s linear infinite;
    +       -o-animation: progress-bar-stripes 2s linear infinite;
    +          animation: progress-bar-stripes 2s linear infinite;
    +}
    +
    +.progress-danger .bar,
    +.progress .bar-danger {
    +  background-color: #dd514c;
    +  background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
    +  background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
    +  background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
    +  background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
    +}
    +
    +.progress-danger.progress-striped .bar,
    +.progress-striped .bar-danger {
    +  background-color: #ee5f5b;
    +  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
    +  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +}
    +
    +.progress-success .bar,
    +.progress .bar-success {
    +  background-color: #5eb95e;
    +  background-image: -moz-linear-gradient(top, #62c462, #57a957);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
    +  background-image: -webkit-linear-gradient(top, #62c462, #57a957);
    +  background-image: -o-linear-gradient(top, #62c462, #57a957);
    +  background-image: linear-gradient(to bottom, #62c462, #57a957);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
    +}
    +
    +.progress-success.progress-striped .bar,
    +.progress-striped .bar-success {
    +  background-color: #62c462;
    +  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
    +  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +}
    +
    +.progress-info .bar,
    +.progress .bar-info {
    +  background-color: #4bb1cf;
    +  background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
    +  background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
    +  background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
    +  background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
    +}
    +
    +.progress-info.progress-striped .bar,
    +.progress-striped .bar-info {
    +  background-color: #5bc0de;
    +  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
    +  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +}
    +
    +.progress-warning .bar,
    +.progress .bar-warning {
    +  background-color: #faa732;
    +  background-image: -moz-linear-gradient(top, #fbb450, #f89406);
    +  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
    +  background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
    +  background-image: -o-linear-gradient(top, #fbb450, #f89406);
    +  background-image: linear-gradient(to bottom, #fbb450, #f89406);
    +  background-repeat: repeat-x;
    +  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
    +}
    +
    +.progress-warning.progress-striped .bar,
    +.progress-striped .bar-warning {
    +  background-color: #fbb450;
    +  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
    +  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
    +}
    +
    +.accordion {
    +  margin-bottom: 20px;
    +}
    +
    +.accordion-group {
    +  margin-bottom: 2px;
    +  border: 1px solid #e5e5e5;
    +  -webkit-border-radius: 4px;
    +     -moz-border-radius: 4px;
    +          border-radius: 4px;
    +}
    +
    +.accordion-heading {
    +  border-bottom: 0;
    +}
    +
    +.accordion-heading .accordion-toggle {
    +  display: block;
    +  padding: 8px 15px;
    +}
    +
    +.accordion-toggle {
    +  cursor: pointer;
    +}
    +
    +.accordion-inner {
    +  padding: 9px 15px;
    +  border-top: 1px solid #e5e5e5;
    +}
    +
    +.carousel {
    +  position: relative;
    +  margin-bottom: 20px;
    +  line-height: 1;
    +}
    +
    +.carousel-inner {
    +  position: relative;
    +  width: 100%;
    +  overflow: hidden;
    +}
    +
    +.carousel-inner > .item {
    +  position: relative;
    +  display: none;
    +  -webkit-transition: 0.6s ease-in-out left;
    +     -moz-transition: 0.6s ease-in-out left;
    +       -o-transition: 0.6s ease-in-out left;
    +          transition: 0.6s ease-in-out left;
    +}
    +
    +.carousel-inner > .item > img,
    +.carousel-inner > .item > a > img {
    +  display: block;
    +  line-height: 1;
    +}
    +
    +.carousel-inner > .active,
    +.carousel-inner > .next,
    +.carousel-inner > .prev {
    +  display: block;
    +}
    +
    +.carousel-inner > .active {
    +  left: 0;
    +}
    +
    +.carousel-inner > .next,
    +.carousel-inner > .prev {
    +  position: absolute;
    +  top: 0;
    +  width: 100%;
    +}
    +
    +.carousel-inner > .next {
    +  left: 100%;
    +}
    +
    +.carousel-inner > .prev {
    +  left: -100%;
    +}
    +
    +.carousel-inner > .next.left,
    +.carousel-inner > .prev.right {
    +  left: 0;
    +}
    +
    +.carousel-inner > .active.left {
    +  left: -100%;
    +}
    +
    +.carousel-inner > .active.right {
    +  left: 100%;
    +}
    +
    +.carousel-control {
    +  position: absolute;
    +  top: 40%;
    +  left: 15px;
    +  width: 40px;
    +  height: 40px;
    +  margin-top: -20px;
    +  font-size: 60px;
    +  font-weight: 100;
    +  line-height: 30px;
    +  color: #ffffff;
    +  text-align: center;
    +  background: #222222;
    +  border: 3px solid #ffffff;
    +  -webkit-border-radius: 23px;
    +     -moz-border-radius: 23px;
    +          border-radius: 23px;
    +  opacity: 0.5;
    +  filter: alpha(opacity=50);
    +}
    +
    +.carousel-control.right {
    +  right: 15px;
    +  left: auto;
    +}
    +
    +.carousel-control:hover,
    +.carousel-control:focus {
    +  color: #ffffff;
    +  text-decoration: none;
    +  opacity: 0.9;
    +  filter: alpha(opacity=90);
    +}
    +
    +.carousel-indicators {
    +  position: absolute;
    +  top: 15px;
    +  right: 15px;
    +  z-index: 5;
    +  margin: 0;
    +  list-style: none;
    +}
    +
    +.carousel-indicators li {
    +  display: block;
    +  float: left;
    +  width: 10px;
    +  height: 10px;
    +  margin-left: 5px;
    +  text-indent: -999px;
    +  background-color: #ccc;
    +  background-color: rgba(255, 255, 255, 0.25);
    +  border-radius: 5px;
    +}
    +
    +.carousel-indicators .active {
    +  background-color: #fff;
    +}
    +
    +.carousel-caption {
    +  position: absolute;
    +  right: 0;
    +  bottom: 0;
    +  left: 0;
    +  padding: 15px;
    +  background: #333333;
    +  background: rgba(0, 0, 0, 0.75);
    +}
    +
    +.carousel-caption h4,
    +.carousel-caption p {
    +  line-height: 20px;
    +  color: #ffffff;
    +}
    +
    +.carousel-caption h4 {
    +  margin: 0 0 5px;
    +}
    +
    +.carousel-caption p {
    +  margin-bottom: 0;
    +}
    +
    +.hero-unit {
    +  padding: 60px;
    +  margin-bottom: 30px;
    +  font-size: 18px;
    +  font-weight: 200;
    +  line-height: 30px;
    +  color: inherit;
    +  background-color: #eeeeee;
    +  -webkit-border-radius: 6px;
    +     -moz-border-radius: 6px;
    +          border-radius: 6px;
    +}
    +
    +.hero-unit h1 {
    +  margin-bottom: 0;
    +  font-size: 60px;
    +  line-height: 1;
    +  letter-spacing: -1px;
    +  color: inherit;
    +}
    +
    +.hero-unit li {
    +  line-height: 30px;
    +}
    +
    +.pull-right {
    +  float: right;
    +}
    +
    +.pull-left {
    +  float: left;
    +}
    +
    +.hide {
    +  display: none;
    +}
    +
    +.show {
    +  display: block;
    +}
    +
    +.invisible {
    +  visibility: hidden;
    +}
    +
    +.affix {
    +  position: fixed;
    +}
    diff --git a/mapbox-gl/resources/bootstrap/css/bootstrap.min.css b/mapbox-gl/resources/bootstrap/css/bootstrap.min.css
    new file mode 100644
    index 0000000000..c10c7f417f
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/css/bootstrap.min.css
    @@ -0,0 +1,9 @@
    +/*!
    + * Bootstrap v2.3.1
    + *
    + * Copyright 2012 Twitter, Inc
    + * Licensed under the Apache License v2.0
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Designed and built with all the love in the world @twitter by @mdo and @fat.
    + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
    diff --git a/mapbox-gl/resources/bootstrap/img/glyphicons-halflings-white.png b/mapbox-gl/resources/bootstrap/img/glyphicons-halflings-white.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae
    GIT binary patch
    literal 8777
    zcmZvC1yGz#v+m*$LXcp=A$ZWB0fL7wNbp_U*$~{_gL`my3oP#L!5tQYy99Ta`+g_q
    zKlj|KJ2f@c)ARJx{q*b<Rc{fZDE|-E3z8Qg5C}{9v!pTzga8NZOmrk*O`5892Z0dh
    z6y;PuJwHDK9$?(w-u|_L_3`o1($W%e0`}kWUyy&dCnqOQPfu4@SAgf?;o*P$z|s8t
    zJh1KR>bkhN_!|Wn*Vos8{TEhUT@5e;_WJsIMMcG5%>DiS&dv_N`4@J0cnAQ-#>RjZ
    z00W5t&tJ^l-QC*ST1-p~00u^9XJ=AUl7oW-;2a+x2k__T=grN{+1c4XK0ZL~^z^i$
    zp&>vEhr@4fZWb380S18T&!0cQ3IKpHF)?v=b_NIm0Q>v<fKgXh*W25>wY7D0baZ)n
    z31Fa5sELUQARIVaU0nqf0XzT+fB_63aA;@<$l~wse|mcA;^G1TmX?-)e)jkGPfkuA
    z92@|!<>h5S_4f8QP-JRq>d&7)^Yin8l7K8gED$&_FaV?gY+wLjpoW%~7NDe=nHfMG
    z5DO3j{R9kv5GbssrUpO)<pElNvVjx;Inad7%}rnn)BtoiIXM{s0C>Oyv<s*i2m!7M
    zNCXUk1jq|?5|99_k&%%AIlu-a0ty3=KxY8j%*;&S3IIajE_Qc!f%*X_5DScgf&xH0
    zumu>Vrlx>u0UKD0i;Dpm5S5dY16(DL5l{ixz|mhJU@&-OWCTb7_%}8-fE(P~+XIRO
    zJU|wp1|S>|J3KrLcz^+v1f&BDpd>&MAaibR4#5A_4(MucZwG9E1h4@u0P@C8;oo+g
    zIVj7kfJi{oV~E(NZ*h(@^<JQ`7oGGHtP>-(Q(C`Psb3KZ{N;^GB(a8NE*Vwc715!9
    zr-H4Ao|T_c6+VT_JH9H+P3>iXSt!a$F`>s`jn`w9GZ_~B!{<w2b}Uz=xRP0Noee!5
    zHGxHKH;uZjouChSB9)ldcOm@{14~ct04{b8>0soaiV|O_c^R2aWa%}O3jUE)WO=pa
    zs~_Wz08z|ieY5A%$@FcBF9^!1a}m5ks@7gjn;67N>}S~Hrm`4sM5Hh`q7&5-N{|31
    z6x1{ol7Bn<k_m&K*9NkB7ANp6;_WSmra!UL^eY+pz_w5LlB(g$UY9|-AP@zsw4|7-
    zi|#>skoViZ<brlX21G1wL@^v%v2P&MSTZc8SKT&&Tq!~%Uw%k^(D<O<S;ewoH)@(b
    zb2Z<#wBV6y-?HHFVJFRg^me&@Reg!dys6F1>0GqbLa#kW`Z<Hy>)VCjt1MysKg|rT
    zi!?s#<KsBd5lg=VLu4^|xo0%enAx0mMXMSpk0KF_*gOS;jx!zP=@5TPN+S>#Ck>8c
    zpi|>$lGlw#@yMNi&V4`6OBGJ(H&7lqLlcTQ&1zWriG_fL>BnFcr~?;E93{M-xIozQ
    zO=EHQ#+?<}%@wbWWv23#!V70h9MOuUVaU>3kpTvYfc|LBw?&b*89~Gc9i&8tlT#kF
    ztpbZoAzkdB+UTy=tx%L3Z4)I{zY(Kb)eg{InobSJmNwPZt$14aS-uc4eKuY<?xyi!
    z`TeGpun(kP^7#~<fX0r^ExRQwveWDF;DOQbL}?LBzt>8h$dtfyxu^a%zA)<y|4;I#
    zFU8x7%0eT|Hd@3!T6Anh3IoHrN%@H8e6ge;3u)_$N2H&Rv2`ml6;kL~xS07C5Nzt<
    z>>fYI&)@ZXky?^{5>xSC?;w4r&td6vBdi%vHm4=XJH!3yL3?Ep+T5aU_>i;yr_XGq
    zxZfCzUU@GvnoIk+_Nd`aky>S&H!b*{A%L>?*XPAgWL(Vf(k7qUS}>Zn=U(ZfcOc{B
    z3*tOHH@t5Ub5D~#N7!Fxx}P2)sy{vE_l(R7$aW&CX>c|&HY+7};vUIietK%}!ph<X
    z*_6&Ee=)&D@nDa!y{$f<(Q`UdM+|H2ksGEhG7utFYl`Y6pD#+4LC8Hw@6|1H-x{D`
    zE$uaNS!i^Rx(%B(My5}1#H73>rCuh+;C@1usp;XLU<8Gq8P!rEI3<U)y>ieg#W$!=
    zQcZr{hp>8sF?k&Yl0?B84OneiQxef-4TEFrq3O~JAZR}yEJHA|Xkqd49tR&8oq{zP
    zY@>J^HBV*(gJvJZc_0VFN7Sx?H7#75E3#?N8<p*btH>Z!C+_f53YU}py<FUNWgSuj
    zi^M}p>ggxx1?wQi5Yb-_`I`_V*SMx5+*P^b=ec5RON-k1cIlsBLk}(HiaJyab0`CI
    zo0{<v3Q5P3@oM!6@v&t6RJy0OS}M??mGqk1x;(pa`FWA#n+2z37<uPHl{#HvB!^?r
    zm9?WOv;Tt(gt*?Pw;;%nF3|I0gDBXPM>=1_LO$~oE2%Tl_}KURuX<`+mQN_sTdM&*
    zkFf!Xtl^e^gTy6ON=&gTn6)$JHQq2)33R@_!#9?BLNq-Wi{U|rVX7Vny$l6#+S<va
    z%-r+y8D)Cm{5=IM8|<{prj)kZfIZ$NiW0)fE9{-SR)@-;NBJtHk@DI_v*mK(N0#s#
    z?S8~jyotdcJJAAUt_;Tr)fa|*cT)~*JZ!c_7yVpSb{r2MllfJDbfI~-7n_#K6lw4G
    z^Eyhsh^z8eZs2;adrfk9ip%h;IP|>Z@KvQt@VYb%<9JfapI^b9j=wa+Tqb4ei;8c5
    z&1>Uz@lVFv6T4Z*YU$r4G`g=91lSeA<=GRZ!*KTWKDPR}NPUW%peCUj`Ix_LDq!8|
    zMH-V`Pv!a~QkTL||L@cqiTz)*G-0=ytr1KqTuFPan9y4gYD5>PleK`NZB$ev@W%t=
    zkp)_=lBUTLZJpAtZg;pjI;7r2y|26-N7&a(h<zryrg`J^oeC|8V|qszB+|*eQ-(Dy
    zbn*nJ1W|b4-1y?dTI6}3IPMw+-O0;Q@eMMtjjQ+G6QfN3ae61Yd9LfQx_UREWecK4
    zMn7A~fOz)be1)Yg{2Ysl9G%s8-h-~@C;ALAL0r=<JP2uCe!T|wAywH1r;F|f_q8N(
    zYp^0FkyL9uj<8bK@fyTtgo+DT)14B^<SigcSJotgDV02O!M(CS6_B&^bILwyV?Ng4
    zm7WQp?{l<Obhuy=22?5<oQDiM22&u4rZrRVG|L9ABfY{=95aTyd~@a$o~1P#ji`=w
    zBKmQqX}r3Nlk9Q|gR7)~#n6AzYk`#!R*d5x`A)hU(!1R1%^zXxNJ(kPCw4htU9^(O
    zP4cYV^F(I>X|`1YNM9N8{>8JAu<en5+94bD>v}hp1v`3JHT-=5lbXpbMq7X~2J5Kl
    zh7tyU`_AusMFZ{ej9D;Uyy;SQ!4nwgSnngsYBwdS&EO3NS*o04)*j<g2BLf;iAZ2(
    z7Key$cc6ey>uAYl;57c2Ly0(DEZ8IY?zSph-kyxu+D`tt@oU{32J#I{vmy=#0ySPK
    zA+i(A3yl)qmTz*$dZi#y9FS;$;h%bY+;StNx{_R56Otq+?pGe^T^{5d7Gs&?`_r`8
    zD&dzOA|j8@3<oPyCd}SOX6AZj_;pT>A&FR5U3*eQNBf<4^4W_iS_()*8b4aaUzfk2
    zzIcMWSEjm;EPZPk{j{1>oXd}pXAj!NaRm8{Sjz!D=~q3WJ@vmt6ND_?HI~|wUS1j5
    z9!S1MKr7%nxoJ3k`GB^7yV~*{n~O~n6($~x5Bu{7s|JyXbAyKI4+tO(zZYMslK;Zc
    zzeHGVl{`iP@jfSKq>R;{+djJ9n%$%EL()Uw+sykjNQdflkJZSjqV_QDWivbZS~S{K
    zkE@T^Jcv)Dfm93!mf$XYnCT--_A$zo9MOkPB6&diM8MwOfV?+ApNv`moV@nqn>&lv
    zYbN1-M|jc~sG|yLN^1R2=`+1ih3jCshg`iP&mY$GMTcY^W^T`WOCX!{-KHmZ#GiRH
    zYl{|+KLn5!PCLtBy~9i}`#d^gCDDx$+GQb~uc;V#K3OgbbOG0j5{BRG-si%Bo{@lB
    zGIt+Ain8^C`!*S0d0OSWVO+Z8<kqm;qPrHIJ!qB8;9h5*>9}}O8aFTZ>p&k}2gGCV
    zh#<$gswePFxWGT$4DC^8@84_e*^KT74?7n8!$8cg=sL$OlKr&HMh@Rr5%*Wr!xoOl
    zo7jItnj-xYgVTX)H1=A2bD(tle<tL7^Z!nJ*fwgn&QUe>EH57#V{xAeW_ezISg5OC
    zg=k>hOLA^urTH_e6*vSYRqCm$J{xo}-x3@HH;bsHD1Z`Pzvsn}%cvfw%Q(}h`Dgtb
    z0_J^niUmoCM5$*f)6}}qi(u;cPgxfyeV<wtcQgsqG?QDyA@6XXM7siU#+0#mP~AnX
    z9f=bMes~9>aaVmOsG<)5`6tzU4wyhF;k|~|x>7-2hXpVBpc5k{L4M`Wbe6Q?tr^*B
    z`Y*>6*&R#~%JlBIitlZ^qGe3s21~h3U|&k%%jeMM;6!~UH|+0+<5V-_zDqZQN7<fD
    zM2vP&&BMr(%$M51tLpycNES^{gnGn-o~t&>9?n?!Aj!Nj`YMO9?j>uqI9-Tex+nJD
    z%e0#Yca6(zqGUR|KITa?9x-#C0!JKJHO(+fy@1!B$%ZwJwncQW7vGYv?~!^`#L~Um
    zOL++>4qmqW`0Chc0T23G8|vO)tK=Z2`gvS4*qpqhIJCEv9i&&$09VO8YOz|oZ+ubd
    zNXVdLc&p=KsSgtmIPLN69P7xYkYQ1vJ?u1g)T!6Ru`k2wkdj*wDC)VryGu2=yb0?F
    z>q~~e>KZ0d<sP$M^)hrN7IC)eGuv*?pAk#*4fxII<8rIx545@9E}-};{IJdo*}!V1
    zkUgWQp<TD%7(QQhWkf*vd;SiT1P@}N?jaoKEV?lzqfa1pG1Y^}ikjNMM*Kb?m5(n&
    zOz8{+G2z7JatI<J95R%#%#ATAzlwPl$?6)w6WH~ku?(FhO)k1eRlF4I5UqR?T`Iy=
    z_bVtkxqs3lQGny-BS%nkzwrXhI_M|P4l_VNVoMjVRoZ*0(JkMQ#AdJLFBj%$oTBx9
    z_5|g_ll0@cfLf<j;&lJ>_#7f3UgV%9MY1}vMgF{B8yfE{HL*pMyhYF)WDZ^^3vS8F
    zGlOhs%g_~pS3=WQ#494@jAXwOtr^Y|TnQ5zki>qRG)(oPY*f}U_=ip_{qB0!%w7~G
    zWE!P4p3khyW-JJnE>eECuYfI?^d366Shq!Wm#x&jA<tFBO~aWRutYg|6S!-V%dvXb
    zjpm3-7^fYCzbWmx*ts$8ECu=f{D#|=T{2_Q?C-SVQTSi8ey{G^D$8U&*bY{vQ$kGG
    zq$8)>o>=HdCllE$>DPO0N;y#4G)D2y#B@5=N=+F%Xo2n{gKcPcK2!hP*^WSXl+ut;
    zyLvVoY>VL{H%Kd9^i~lsb8j4>$EllrparEOJNT?Ym>vJa$(P^tOG)5aVb_5w^*&M0
    zYOJ`I`}<NkH4X@iCc57jNSqY3D>9}UoSnYg#E(&yyK(tqr^@n}qU2H2DhkK-`2He%
    zgXr_4kpXoQHxAO9S`wEdmqGU4j=1JdG!OixdqB4PPP6<nq;ZS)73s_@N{54U_<mt#
    zR{@UUroZJ1=lVB~3y%RbLLE=9Mh=pj4wNruVxXLk8pKH)JVr{Hbx`P1XQ>RXA}>GM
    zumruUUH|ZG2$bBj)Qluj&uB=dRb)?^qomw?Z$X%#D+Q*O97eHrgVB2*mR$bFBU`*}
    zIem?dM)i}raTFDn@5^caxE^XFXVhBePmH9fqcTi`TLaXiueH=@06sl}>F%}h9H_e9
    z>^O?LxM1EjX}NVppaO@NNQr=AtHcH-BU{yBT_vejJ#J)l^cl69Z7$sk`82Zyw7Wxt
    z=~J?hZm{f@W}|96FUJfy65Gk8?^{^yjhOahUMCNNpt5DJw}ZKH7b!bGiFY9y6OY&T
    z_N)?Jj(MuLTN36ZCJ6<obtKS{VOOSzs>I5Xy7uVlrb$o*Z%=-)kPo9s?<^Yqz~!Z*
    z_mP<Y8YDC3(vm~>8(unFq65XSi!$@YtieSQ!<7IEOaA9VkKI?lA`*(nURv<D`3vIl
    zzk?RMHDq|}aqs!Q7n{<V(L>fKL8cX}-+~uw9|_5)uC2`ZHca<BJSyCJ7L7R3^ezpJ
    zixdU%^Arizo-zh;Lga89_J>eX7L8aG6Ghleg@F9aG%X$#g6^yP5apnB>YTz&EfS{q
    z9UVfSyEIczebC)qlVu5cOoMzS_jrC|)rQlAzK7sfiW0`M8mVIohazPE9Jzn*qPt%6
    zZL8RELY@L09B83@Be;x5V-IHnn$}{RAT#<2JA%ttlk#^(%u}CGze|1JY5MPhbfnYG
    zIw%$XfBmA-<_pKLpGKwbRF$#P;@_)ech#>vj25sv25VM$ouo)?BXdRcO{)*OwTw)G
    zv43W~T6ekBMtUD%5Bm>`<n0ehww;K9t*_z=^iZoM2Gjm6Wx6QTWDzOX28g|i7p-G(
    znPo(pGb2-Hja^(5g>^Ltv!w4~65N!Ut5twl!Agrzyq4O2Fi3pUMtCU~>9gt_=h-f%
    z;1&OuSu?A_sJvIvQ+dZNo3?m1%b1+s&UAx?8sUHEe_sB7zkm4R%6)<@oYB_i5>3Ip
    zIA+?jVdX|zL{)?TGpx+=Ta>G80}0}Ax+722$XFNJsC1gcH56{8B)*)eU#r~HrC&}`
    z|EWW92&;6y;3}!L5zXa385@?-D%>dSvyK;?jqU2t_R3wvBW;$!j45uQ7tyEIQv<v(
    zw)qBpyRhiKBMR9HV)v2ZJdk>a;Db}r&bR3kqNSh)Q_$MJ#Uj3Gj1F;)sO|%6z#@<+
    zi{pbYsYS#u`X$Nf($OS+lhw>xgjos1OnF^$-I$u;qhJswhH~p|ab*nO>zBrtb0ndn
    zxV0uh!LN`&xckTP+JW}gznSpU492)u+`f{9Yr)js`NmfYH#Wdtradc0TnKNz@Su!e
    zu$9}G_=ku;%4xk}eXl>)KgpuT>_<`Ud(A^a++K&pm3LbN;gI}ku@YVrA%FJBZ5$;m
    zobR8}OLtW4-i+qPPLS-(7<>M{)rhiPoi@?&vDeVq5%fmZk=mDdRV>Pb-l7pP1y6|J
    z8I>sF+TypKV=_<SBxSgNFy@5`t70+_4F<*(g54PNEt&4u%OoVR^n+$TL)qKdP6c)n
    z-CoP*_kXZ4vBsj8M^2Y0nDq-^4r-wgu2Y-3fmi6ooPIXTI%UdJhw@7KgR=N+Vl3NO
    zcl8-&i~^e%3E1G+u&^#M&5!sI)la$uQ2y&KsaZjx^r8D68BTZd^NrAV{0u$=#SH#4
    zLE2)q%<UADH&I$um|>^NwBU^>4JJq<*14GLfM2*XQzYdlqqjnE)gZsPW^E@mp&ww*
    zW9i>XL=uwLVZ9pO*8K>t>vdL~Ek_NUL$?LQi5sc#1Q-f6-ywKcIT8Kw?C<o*=Aa~-
    z*eA0Mgmu5-j8rTh^;={1$#X=Ck5Gk;@KK#haYa^sXr0^_^Q84%+WOl3?#Mc#{{d}B
    z>(_3pbR`e|)%9S-({if|E+hR2W!&qfQ&UiF^I!|M#xhdWsen<tq75@@WHX{+T3S~F
    znoMw2v{^ia4`fkd=3p<6XkL)!lsI%8iq@>v^wpKCBiuxXbnp85`{i|;BM?Ba`lqTA
    zyRm=UWJl&E{8JzYDHFu>*Z10-?#A8D|5jW9Ho0*CAs0fAy~MqbwYuOq9jjt9*nuHI
    zbDwKvh)5Ir$r!fS5|;?Dt>V+@F*v8=TJJF)TdnC#Mk>+tGDGCw;A~^PC`gUt*<(|i
    zB{{g{`uFehu`$fm4)&k7`u{xIV)yvA(%5SxX9MS80p2EKnL<HSdiWFiAy=3UmV-rj
    zc%^|o`X!t!vuYErrUzbG?ostY(qs7GE^=Z33k*P+F6r($h_?W-bHJ|GUK@Wlv9++M
    zG}?Z?8{_X${_c9aOXw4qfk0vTaVRH6FMOnFD?w|zo{zKKg$8wzW&yufWk&idB=+9!
    z^dTI@g=>t<HJ%Cd%{u~X`lRpMFg&X{m?Nw#T4cg*?z{+rC($M4z9RHV@8KoueD7_)
    z8T@i-6RG$5%_Y`lSjj|?wSvITK5c4g0!Uq49VAn-H<9~;vn7~hBdYuDOt2$gtNuBm
    zo8$Y{2lwMxZNbfb$Hm0T528Og7Jfl!35edSr>CZ>tlX>*Z6nd&6-<c}7z{sZ9V^Ux
    zMNgR3$iH97>Mv$5rHD*<Fmux@1NkgiA%VmyOAwal{&*L*?*@Cl?&!jtcf3KL{{|8z
    z_($$R;SoAei#gUO@=7)M7s~2aAxJ>db;&IBK3KH&M<+ArlGXDRdX1VVO4)&R$f4<g
    z`M~bg9+=(|cc^a3vB10?3GZiq$o|Zromh?lE2%m!alG4CIrvmRZHZVSM>NxXI>GBh
    zSv|h>5GDAI(4E`@F?En<q4iBUtn-fux#Jt=qU6#PBE4-GhP)}OK!CI;i(sJ6^VIJF
    zwJMEAeGKMb_^`VbA1hFYio)roSCrLG-NL5Yqhb{sh3_zt(Zg93UP*;!m?}k&V`1AB
    zNYPri&yVkXW8uO1geXM3Oj&$G%~#Jd%h;?JDKwrq;P+!t&4W1Z^1?Ikguvk#bK?Bx
    z$w5M*LxgRe=jz?UiDBbfC1I3!cjeMD*ueh4W0S*z6=TAf+ZYkG$}FGti`ipjpIK>W
    zS>#c&Gw6~_XL`qQG4bK`W*>hek4LX*efn6|_MY+rXkNyAuu?NxS%L7~9tD3cn7&p(
    zCtfqe6sjB&Q-Vs7BP5+%;#Gk};4xtwU!KY0XXbmkUy$kR9)!~?*v)qw00!+Yg^#H>
    zc#8*z6zZo>+(bud?K<*!QO<vKd$8TBt^HLIw%iB>4ehiTCK&PD4G&n)Tr9X_3r-we
    z?fI+}-G~Yn93gI6F{}Dw_SC*FLZ)5(85zp4%uubtD)J)UELLkvGk4#tw&Tuss<g@J
    zd3(n+h;=s-joD7pea}*kl|?T5<3W!rK}V)#HpvFL3uRc{oe_mV<z1l~^m1_TkJDu3
    z;JtNs6#g&&@E09TG{#Z`zh|EKwRTiJr)s50$5?Nrhn68HAr=rV#m>a)mTD$R2&O~{
    zCI3>fr-!-b@EGRI%g0L8UU%%u_<;e9439JNV;4KSxd|78v+I+8^rmM<g+mx0&Si$a
    zgf1uYC03KcCN)Lz!>f3f40Jb}wEszROD?xBZu>Ll3;sUIoNxDK3|j3*sam2tC@@e$
    z^!;+AK>efeBJB%ALsQ{uFui)oD<x}JL&L^@dTz{b&_?*nsS;lNnoJ@(k9d5xVq$|w
    z<ejC>oq()2USi?n=6C3#eetz?wPswc={I<8x=(8lE4EIsUfyGNZ{|KYn1IR|=E==f
    z(;!A5(-2y^2xRFCSPqzHAZn5RCN_bp22T(KEtjA(rFZ%>a4@STrHZflxKoqe9Z4@^
    zM*scx_y73<sFS1_?6+u!sT9fvjld*kU~edMy>?Q{<Kw(x)TAd1JfBpLz7(Nk)Jsdz
    zj7#eyM{0^=a(C#N_pwZ(&^&zZP@5Qw`oUBRW0i<S2ql<0tEs~>vt6?~WEl?2q*;@8
    z3M*&@%l)SQmXkcUm)d@GT2#JdzhfSAP9|n#C;$E8X|pwD!r#X?0P>0ZisQ~TNqupW
    z*lUY~+ikD`vQb?@SAWX#r*Y+;=_|oacL$2CL$^(mV}aKO77pg}O+-=T1oLBT5sL2i
    z42Qth<Jh0Ysw=K%u7GarF`3bIM1>2+0@C`c+*D0*5!qy26sis<9a7>LN2{z%Qj49t
    z=L@x`4$ALHb*3COHoT?5S_c(Hs}g!V>W^=6Q0}zaubkDn)(lTax0+!+%B}9Vqw6{H
    zvL|BRM`O<@;eVi1DzM!tXtBrA20Ce@^Jz|>%X-t`vi-%WweXCh_LhI#bUg2*pcP~R
    z*RuTUzBKLXO~~uMd&o$v3@d0shHfUjC6c539PE6rF&;Ufa(Rw@K1*m7?f5)t`MjH0
    z)_V(cajV5Am>f!kWcI@5rE8t6$S>5M=k=aRZROH6fA^jJp~2NlR4;Q2>L$7F#RT#9
    z>4@1RhWG`Khy>P2j1Yx^BBL{S`niMaxlSWV-JBU0-T9zZ%>7mR3l$~QV$({o0;jTI
    ze5=cN^!Bc2bT|BcojXp~K#2cM>OTe*cM{Kg-j*CkiW)EGQot^}s;cy8_1_@JA0Whq
    zlrNr+R;Efa+`6N)s5rH*|E)nYZ3uqkk2C(E7@A|3YI`ozP~9Lexx#*1(r8luq+YPk
    z{J}c$<WQa$CfVIhsE>s`<i2`cEPYHzF!ZIy?L$}MhAPFqQe@_8Lh#cQAH~-zZ5p$u
    zZauEKr<oluR2T6z2A|B^#roi2jr3F<X4&!ZjiXo?9nIbJ4iAii=A_@&#n$TqH^#R&
    z{$qMQO7u^&7KEB6l{H~A;ylPsJw2kA4#E2@7dO%lsi+3{VJ4?~e4(Bz-tw&^YR9P1
    zTlpCH(W_%+@#|?%RN0HM=U?pU5$E2f<RPK1fw%3KLs--hd|lj})1h|Y<6CA3NsuSI
    zl=<<g*vcJW=6yZY`aXe5QUB~awgg5fxlu%7u#A8=UXt61U-7wGtR{L&XvKbUf-}PL
    z<eXA6<<r^;=`XwtFN1~2J^$Y${#Q0Tyev?j!*Z4q^mjQ4ah)uW_s=JkrRS%l*Ut`>
    zPM35Fx(YWB3Z5IYnN+L_4|jaR(5iWJi2~l&xy}aU7kW?o-V*6Av2wyZTG!E2KSW2*
    zGRLQkQU;Oz##ie-Z4fI)WSRxn$(ZcD;TL+;^r=a4(G~H3ZhK$lSXZj?cvyY8%d9JM
    zzc3#pD^W_QnWy#rx#;<pgDoauRid_B6w$J6XKKeAcZHU9rH9=s!y`%~e@hGc<c#A7
    zRRTR`&dt`*;~VYcVGk-~aNB!?q#4B&%52?dI@=%LQ>c&N@sqHhrnHRmj<I9Tx4aSD
    zVUQ}9lh=Kd&QIx0uCqYm3pFs_*L;b|$xyZks(AAwgYsH85PAL~ndH7DNUoZKBHCWu
    z_<;@&ed^tpoO=DG4Hem|2>#i;s%zLm6SE(n&BWpd&f7>XnjV}OlZntI70fq%8~9<7
    zMYaw`E-rp49-oC1N_uZTo)Cu%RR2QWdHpzQIcNsoDp`3xfP+`gI?tVQZ4X={qU?(n
    zV>0ASES^Xuc;9JBji{)RnFL(Lez;8XbB1uWaMp@p?7xhXk6V#!6B@aP4Rz7-K%a>i
    z?fvf}va_DGUXlI#4--`A3qK7J?-HwnG7O~H2;zR~RLW)_^#La!=}+>KW#anZ{|^D3
    B7G?kd
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/resources/bootstrap/img/glyphicons-halflings.png b/mapbox-gl/resources/bootstrap/img/glyphicons-halflings.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643
    GIT binary patch
    literal 12799
    zcma*OWmH^Ivn@*S;K3nSf_t!#;0f+&pm7Po8`nk}2q8f5;M%x$<L>SdAkd9FAvlc$
    zx660V9e3Ox@4WZ^?7jZ%QFGU-T~%||Ug4iK6bbQY@zBuF2$hxOw9wF=A)nUSxR_5@
    zEX>HBryGrjyuOFFv$Y4<+|3H@gQfEqD<)+}a~mryD|1U9*I_FOG&F%+Ww{SJ-V2BR
    zjt<81Ek$}Yb*95D4RS0HCps|uLyovt;P05hchQb-u2bzLtmog&f2}1VlNhxXV);S9
    zM2buBg~!q9PtF)&KGRgf3#z7B(hm5WlNClaCWFs!-P!4-u*u5+=+D|ZE9e`KvhTHT
    zJBnLwGM%!u&vlE%1ytJ=!xt~y_YkFLQb6bS!E+s8l7PiPGSt9xrmg?LV&&SL?J~cI
    zS(e9TF1?SGyh+M_p@o1dyWu7o7_6p;N6hO!;4~<t3w3SV570<|$VWNPP~TbX3|=X>
    z2B`I;y`;$ZdtBpvK5%oQ^p4eR2L)BH>B$FQeC*t)c`L71gXHPUa|vyu`Bnz)H$Z<N
    z7UVAHFsR+HLO+(tK~=M@pM7ZMPj5gkz>cXGve(}XvR!+*8a>BLV;+ryG1kt0=)ytl
    zNJxFUN{V7P?#|Cp85QTa@(*Q3%K-R(Pkv1N8YU*(d(Y}9?PQ(j<e|z%-Bnrh*J1R%
    z%JAF*cdp#Zk#h09fv12$TuGUsX=V-wgNcEGe0hhp%mK8EVPi6@!a;xi$k!wcIO|bJ
    zPx8DZ*0Y(ggKhnp2=Ax#f<wKp{=pA29>;NzWoEVWRD-~H$=f>j<LsfOZ;WLF*F0cm
    z9PSRSlSFQE>9~PN^BM2okI(gY-&_&BCV6RP&I$FnSEM3d=0fCxbxA6~l>54-upTrw
    zYgX@%m>jsSGi`0cQt6b8cX~+02IghVlNblR7eI;0ps}mpWUcxty1yG56C5rh%ep(X
    z?)#2d?C<4t-KLc*EAn>>M8%HvC1TyBSoPNg(4id~H8JwO#I)Bf;N*y6ai6K9_bA`4
    z_g9(-R;qyH&6I$`b<fg~;S@}+8_8-ItZ!TS<!|pei*+CWiVH?M1CEFM{ij_eP4dL+
    zsn%eDn^Kp7vLEn|Dq0`Wt&GpZ?eq^%pqXVR^PA!ZyoGLI7ihDaWiNi$M6h)PNwvHR
    zEcA82H5fM6RnpZ!R872>42v|0V3Z8IXN*p*8g$gE98+JpXNY+jXxU0zsR^W$#V=KP
    z3AEFp@OL}WqwOfsV<)A^UTF4&HF1vQecz?LWE@p^Z2){=KEC_3Iopx_eS42>DeiDG
    zWMXGbYfG~W7C8s@@m<_?#Gqk;!&)_Key@^0xJxrJahv{B&{^!>TV7TEDZlP|$=ZCz
    zmX=ZWtt4QZK<Y>x**)lQQoW8y-XLiOQy#T`2t}p6l*S`68ojyH@UXJ-b~@tN`WpjF
    z%7%Yzv807gsO!v=!(2uR)16!&U5~VPrPHtGzUU?2w(b1Xchq}(5<TwC<%h0ow%K}h
    zTlz}37c^dc?7rEmt7Zy9#q|V+5bE1c06?X{e~%TDZ!@uG_uU!n6VJy=odWKS?p#j?
    zn;v){i#`+1X;Ls^(9p!?42vli(fu1D-%nf?-3VKCs1JT^-;{Pg82EGZ&|T}A#wtP(
    zR^df|3P4JZ0|weuCV=JopL6MLvYycbd;-Xx_r)Hm1~(2>Ed^G|SD7IG+kvgyVksU)
    z(0R)SW1V(>&q2nM%Z!C9=;pTg!(8pPSc%H01urXmQI6Gi^dkYCYfu6b4^tW))b^U+
    z$2K&iOgN_OU7n#GC2jgiXU{caO5hZt0(>k+c^(r><#m|#J^s?zA6pi;^#*rp&;aqL
    zRcZi0Q4HhVX3$ybclxo4FFJW*`IV`)Bj_L3rQe?5{wLJh168Ve1jZv+f1D}f0S$N=
    zm4i|9cEWz&C9~ZI3q*gwWH^<6sBWuphgy@S3Qy?MJiL>gwd|E<2h9-$3;gT9V~S6r
    z)cAcmE0KXOwDA5eJ02-75d~f?3;n7a9d_xPBJaO;Z)#@s7gk5$Qn(Fc^w@9c5W0zY
    z59is0?Mt^@Rolcn{4%)Ioat(kxQH6}hIykSA)zht=9F_W*D#<}N(k&&;k;&gKkWIL
    z0Of*sP=X(Uyu$Pw;?F@?j{}=>{aSHFcii#78FC^6JGrg-)!)MV4AKz>pXnhVgTgx8
    z1&5Y=>|8RGA6++FrSy=__k_imx|z-EI@foKi>tK0Hq2LetjUotCgk2QFXaej!BWYL
    zJc{fv(&qA7UUJ|AXL<Te#svgLe$GRVt~C0`%AZ+-=S0D^On=i42k@^tJ-LZGdLpRi
    zdrV5?>c5z*_NW#yWzKtl(c8mEW{A>5Hj^gfZ^HC9lQNQ?RowXjmuCj4!!54Us1=hY
    z0{@-phvC}yls!PmA~_z>Y&n&IW9FQcj}9(OLO-t^NN$c0o}YksCUWt|DV(MJB%%Sr
    zdf}8!9ylU2TW!=T{?)g-ojAMKc>3pW;KiZ7f0;&g)k}K^#HBhE5ot)%oxq$*$W@b#
    zg4p<<e2}@}ZtI091*fR6EHmhc2JFT&S+9NWaDJ!A80$GFF7R`A%xl6?3MWwFH)kiY
    zKkO7P(Y}AIYl!b@wU{Hfoy`qG`h+F#SJJ{&-s<{+@b9bRRm+2<>Ou`ME|Kd1WHK@8
    zzLD+0(NHWa`B{em3Ye?@aVsEi>y#0XVZfaFuq#;X5C3{*ikRx7UY4FF{ZtNHNO?A_
    z#Q?hwRv~D8fPEc%B5E-ZMI&TAmikl||EERumQCRh7p;)>fdZMxvKq;ky0}7IjhJph
    zW*uuu*<F&)uV|73Nr>(Y6)S;Od--8uR^R#sb$cmFCnPcj9PPCWhPN;n`i1Q#Qn>ii
    z{WR|0>8F`vf&#E(c2NsoH=I7Cd-FV|%(7a`i}gZw4N~QFFG2WtS^H%@c?%9UZ+kez
    z;PwGgg_r6V>Kn5n(nZ40P4qMyrCP3bDkJp@hp6&X3>gzC>=f@Hsen<%I~7W+x@}b>
    z0}Et*vx_50-q@PIV=(3&Tbm}}QRo*FP2@)A#XX-8jYspIhah`9ukPBr)$8>Tmtg&R
    z?JBoH17?+1@Y@r>anoKPQ}F8o9?vhcG79Cjv^V6ct709VOQwg{c0Q#rBSsSmK3Q;O
    zBpNihl3S0_IGVE)^`#94#j~$;<ISbQ+zLM8Q_sWpD4<&Sicl|!a~&A@PH`UFRr4^t
    zSjAA>7+u870yWiV$@={|GrBmuz4b)*bCOPkaN0{6$MvazOEBxFdKZDlbVvv{8_*kJ
    zfE6C`4&Kkz<5u%dEdStd85-5UHG5IOWbo8i9azgg#zw-(P1AA049hddAB*UdG3Vn0
    zX`OgM+EM|<+KhJ<=k?z~WA5waVj?T9eBdfJGebVifBKS1u<$#vl^<Wg*!!OoyJ@GG
    z%+_%2Ex-A(=Z(Bs6q~agBwBL+Pcns5yTYUCI_zEv3JOnOB;7f=h8xGf|IQl+Qw37#
    z{BhR?wjaFo)FpPNNRkn616I`fE=rl+<Vv=sXw)oTB*nsxZd}^hq|lwuLq2tPYK9Ch
    zP~rW|kx{-S+q;ojdznAWu9)x>BvSg)xsnT5Aw_ZY#}v*LXO#htB>f}x3qDdDHoFeb
    zAq7;0CW;XJ`d&G*9V)@H&739DpfWYzdQt+Kx_E1K#Cg1EMtFa8eQRk_JuUdHD*2;W
    zR~XFnl!L2A?48O;_iqCVr1oxEXvOIiN_9CUVTZs3C~P+11}ebyTRLACiJuMIG#`xP
    zKlC|E(S@QvN+%pBc6vPiQS8KgQAUh75C0<L{Rx=;M-*LCs2Bp<jfOoZepIeH1&E9@
    zECcRp6~TSaxo9}VYr%Om){SqtW<MPRfw2-K1_c9&KORpSyh3Z*9=_y`d-Pn0_zAw+
    z=kYI%Xg`=LN{&qw<HTtk2MKE0r;WoX$l}>a2xcPQDD$}*bM&z~g8+=9ltmkT$;c;s
    z5_=8%i0H^fEAOQbHXf0;?D<BP;<HVQI1JZt*v)6RAq&gagO^!F$spXEh)>N5z-5+1
    zDxj50yYkz4ox9p$HbZ|H?8ukAbLE^P$@h}L%i6QVcY>)i!w=hkv2zvrduut%!8>6b
    zcus3bh1w~L804EZ*s96?GB&<V5y;va8bgv&LhJ<YYLxjoJ6PJ;r2T$n2GZZ+&blBq
    zN@;fP%v^kz^?uH{Kpq(Ih{eCW5OnE5%HakzY6sMl!wfw!(lBl{oyDuNM|bEKU#YtR
    zTTK?n-{?&5Szx)y^~WKl(fG>F7c5?m?|t$-tp2rKMy>F*=4;w*jW}^;8v`st&8)c;
    z2Ct2{)?S(Z;@_mjAEjb8x=qAQvx=}S6l9?~H?PmP`-xu;ME*B8sm|!h@BX4>u(xg_
    zIHmQzp4Tgf*J}Y=8STR5_s)GKcmgV!<zLBv<JCu*R*$7_b_L{9GvwPbpvkT@1&MS$
    zijYfuLM?Pa-BA2}iX9A(2K)AF@cP6QkvvCLyswdDf?LI~tZ|qKPtWR#^oamFBRcUk
    zs5b$Sc+=%VrL*7Ba(pp>$JKTg@LO402{{Wrg>#D4-L%vjmtJ4r?p&$F!o-BOf7ej~
    z6)BuK^^g1b#(E>$s`t3i13{6-mmSp7{;QkeG5v}GAN&lM2lQT$@(aQCcFP(%UyZbF
    z#$HLTqGT^@F#A29b0HqiJ<ZOKS1P#S0IU6AksffR*wx4ca5r>sRJAlh8kngU`BDI6
    zJUE~&!cQ*&f95Ot$#mxU5+*^$qg_DWNdfu+1irglB7yDglzH()2!@#rpu)^3S8weW
    z_FE$=j^GTY*|5SH95O8o8W9FluYwB=2PwtbW|JG6kcV^dMVmX(wG+Otj;E$%gfu^K
    z!t~<3??8=()WQSycsBKy24>NjRtuZ>zxJIED;YXaU<x|u=Vd7uuZ|>z$@0z4rl+TW
    zWxmvM$%4jYIpO>j5k1t1&}1VKM~s!<EQ6q8U;EP6<gFYZ!m%POxUBC$P89e*7OnrM
    zdWQA)CjX#LYDI-i*mnQZr;sN<6@SPOXNM}9Rp_hcE;y>eLsCVQ`TTjn3JRXZD~>GM
    z$-IT~(Y)flNqDkC%DfbxaV9?QuWCV&-U1yzrV@0jRhE;)ZO0=r-{s@W?HOFbRHDDV
    zq;eLo+wOW;nI|#mNf(J?RImB9{YSO2Y`9825Lz#u4(nk3)RGv3X8B(A$TsontJ8L!
    z9JP^eWxtKC?G8^xAZa1HECx*rp35s!^%;&@Jyk)NexVc)@U4$^<D$wmm?XpH-Sg4*
    z8B^w;<H>X1Dag6`WKs|(HhZ#rzO2KEw3xh~-0<;|zcs0L>OcO#YYX{S<TTw)*(lZC
    zIx888OkDY0a@=pFP3fhTGE0#kua@EqJ8hp4VSNt-Xfx&Iq8mr)#UbJIBdW*?_9fdi
    z7f!0)Iy{xeM7LDi+*QJ?BdGeD5e0(0aSm&GvjQ!V6CD0we*R)~MbsZ|>N8m6`9pp+
    zQG@q$I)T?aoe#AoR@%om_#z=c@ych!bj~lV13Qi-xg$i$hXEAB#l=t7QWENGbma4L
    zbBf*X*4oNYZUd_;1{Ln_ZeAwQv4z?n9$eoxJeI?lU9^!AB2Y~AwOSq67dT9ADZ)s@
    zCRYS7W$Zpkdx$3T>7$I%3EI2ik~m!f7&$Djpt6kZqDWZJ-G{*_eXs*B8$1R4+I}Kf
    zqniwCI64r;>h2Lu{0c(#Atn)%E8&)=0S4BMhq9$`vu|Ct;^ur~gL`bD>J@l)P$q_A
    zO7b3HGOUG`vgH{}&&Agr<FnKy|IF(G1iR*`GW247VX<aAlJ2F?Q<={Aib+`}_HyE*
    zujP5~Z9@I2PBhiOY}cNA6jXAuIimavj#$XIs@HezE!U24{*GtAdHFvr(O>Fy%K^>?
    z>wf**coZ2vdSDcNYSm~dZ(vk6&m6bVKmVgrx-X<>{QzA!)2*L+HLTQz$e8UcB&Djq
    zl)-%s$ZtUN-R!4ZiG=L0#_P=BbUyH+YPmFl_ogkkQ$=s@T1v}rNnZ^eMaqJ|quc+6
    z*ygceDOrldsL30w`H;rNu+I<VKUrjL=bDy~WtS;;K#ThRGVRMNFq&Gco*pd+ChOJI
    zqAbbk-&kSt%3!MCpue~I%|gblH{=P#-)jqQC%xCp|J^jUO>jlS+G~p&0SawXCA1+D
    zC%cZtjUkLNq%FadtHE?O(yQTP486A{1x<{krq#rpauNQaeyhM3*i0%tBpQHQo-u)x
    z{0{&KS`>}vf2_}b160XZO2$b)cyrHq7ZSeiSbRvaxnKUH{Q`-P(nL&^fcF2){vhN-
    zbX&WEjP7?b4A%0y6n_=m%l00uZ+}mCYO(!x?j$+O$*TqoD_Q5EoyDJ?w?^UIa491H
    zE}87(bR`X;@u#3Qy~9wWdWQIg1`cXrk$x9=ccR|RY1~%{fAJ@uq@J3e872x0v$hmv
    ze_KcL(wM|n0EOp;t{hKoohYyDmYO;!`7^Lx;0k=PWPGZpI>V5qYlzjSL_(%|mud50
    z7#{p97s`U|Sn$WYF>-i{i4`kzlrV6a<}=72q2sAT7Zh{>P%*6B;Zl;~0xWymt10Mo
    zl5{bmR(wJefJpNGK=fSRP|mpCI-)Nf6?Pv==FcFmpSwF1%CTOucV{yqxSyx4Zws3O
    z8hr5Uyd%ezIO7?PnEO0T%af#KOiXD$e?V&OX-B|ZX-YsgSs%sv-6U+sLPuz{D4bq|
    zpd&|o5tNCmpT>(uIbRf?8c}d3IpOb3sn6>_dr*26R#ev<_~vi)wleW$P<Wyn_7n0-
    zl)LIgF0z;$xTz(0JgW0t|K0{|pl+d7{+{fAW)lB*Qg({z1~qrplnmDSP!2>X|5)$_
    z+_|=pi(0D(AB_sjQ;sQQSM&AWqzDO1@NHw;C9cPdXRKRI#@nUW)CgFxzQ1nyd!+h&
    zcjU!U=&u|>@}R(9D$%lu2TlV>@I2-n@fCr5Pr<dtPlfA<Z*`%$WS?W!M7-X@Sw}lf
    zu7sLkI`BK6gTBwv0nqdk^SqiGBO}U16-Ky}DlzfpVxxnEAc|MG(;#A7b;H&MP*riE
    zHr?l)sap(Q`P6U_@Ov18QJwI7yr|=6Y+TbD2PUEPfsh&V{s?8AA2dT>ZNVyKWR7hm
    zWjoy^<!R*J%IXEk=E5cj6b=;i9u3uQuMH4{qOT^=OGnt_=n2>p7v8m#$qN0K#8jT-
    zq`mSirDZDa1Jxm;Rg3<Jf$!Bj9`<kE;Sz+T_M)m3-f__2l^&CsYnIwV?+%t2FG{Ta
    zI-67-X7Fu-xbrdN@cn6z3_k9VZ?2i{<ie%nx)UUiUTLNtHEK)0HD_qUYpV0X30}z?
    zM!*@omRu>rAPhC)LcI4@-RvKT+@9&KsR3b0_0zuM!Fg7u>oF>3bzOxZPU&$ab$Z9@
    zY)f7<va9`_LvY6!5H@PMYi?(=yM97@*rbrsB=oh`t5ydnN2A;15DysI3n?zsE3{ZX
    zq+yK*u5H1rVq8mwv!|dvE&PWazz!0^LY7dozu5qaS3Q5~q}uAQUJN5WW+A&wvpho?
    z=!z1Q9;>pKh22I7ZykL{YsdjcqeN++=0a}elQM-4;Q)(`Ep3|VFHqnXOh14`!Bus&
    z9w%*EWK6AiAM{s$6~SEQS;A>ey$#`7)khZvamem{P?>k)5&7Sl&&NXKk}o!%vd;-!
    zpo2p-_h^b$D<fdz<@`H3n|HeSVR76K@6|_9&-VHAVO=;`v1rN8I|9P)PS7vp83efu
    z`yTr9OVLz|?h*IHce7sdT@Ktb#!>NBO>{h4JdGB=D>fvGIYN8v&XsfxU~VaefL?q}
    z3ekM?<wNDtI4J<DC6XBgM26Nv#0iut=ZwA#^>iOKkCzQHkBkhg=hD!@&(L}FcHKoa
    zbZ7)H1C|lHjwEb@tu=n^OvdHOo7o+W`0-y3KdP#bb~wM=Vr_gyoEq|#B?$&d$tals
    ziIs-&7isBpvS|CjC|7C&3I0SE?~`a%g~$PI%;au^cUp@ER3?mn-|vyu!$7MV6(uvt
    z+CcGuM(Ku2&G0tcRCo7#D$Dirfqef2qPOE5I)oCGzmR5G!o#Q~(k~)c=LpIfrhHQk
    zeAva6MilEifE7rgP1M7AyWmLOXK}i8?=z<j)TsCg#MI>2;N=no)`IGm#y%aGE>-FN
    zyXCp0Sln{IsfOBuCdE*#@CQof%jzuU*jkR*Su3?5t}F(#g0BD0Zzu|1MDes8U7f9;
    z$JBg|mqTXt`muZ8=Z`3wx$uizZG_7>GI7tcfOHW`C2bKxNOR)XAwRkLOaHS4xwlH4
    zDpU29#6wLXI;H?0Se`SRa&I_QmI{zo7p%uveBZ0KZKd9H6@U?YGArbfm)D*^5=&Rp
    z`k{35?Z5GbZnv>z@NmJ%+sx=1WanWg)8r}C_>EGR8mk(NR$pW<-l8OTU^_u3M@gwS
    z7}GGa1)`z5G|DZirw;FB@VhH7Dq*0qc=|9lLe{w2#`g+_nt<uBB~iQoK%j+BR{KW$
    zxUoEE;u<56rl_>>_%o<~9(VZe=zI*SSz4w43-_o>4E4`M@NPKTWZuQJs)?KXbWp1M
    zimd5F;?AP(LWcaI-^Sl{`~>tmxsQB9Y$Xi*{Zr#py_+I$vx7@NY`S?HFfS!hUiz$a
    z{>!&e1(16T!Om)m)&k1W#*d#GslD^4!TwiF2WjFBvi=Ms!ADT)ArEW6zfVuIXcXVk
    z>AHjPADW+mJzY`_Ieq(s?jbk4iD2Rb8*V3t6?I+E06(K8H!!xnDzO%GB;Z$N-{M|B
    zeT`jo%9)s%op*XZKDd6*)-^lWO{#RaIGFdBH+;XXjI(8RxpBc~azG1H^2v7c^bkFE
    zZ<!d@6;Xr=zrz^$h_Zbcf~Z$lrrBw0nL?BbB`hkkx&01qcs_@(`dj5M$3rI2JKgsr
    zS^x~?G~LTF&PL>CVPE+E*Q=FSe8Vm&6|^3ki{9~qafiMAf7i4APZg>b%&5>nT@pHH
    z%O*pOv(77<h_P}M1fVl@bA%;8!%G$2v2^1K;a|J|258iaFK<JsY+PvseEryJp$5<!
    z9lXGNp5qrv`T=s~_@3Ry-B6o<m;T-lQtjLZ)m`X2mKrN#6`?5SI5G#qCc`>?ZiT{W
    zBibx}Q12tRc7Py1NcZTp`Q4ey%T_nj@<r4RLoFiQ1cOG!U!@-f&DrHzjFreg6r@E|
    zvE{2Q=kFJS$gwo*FVtl=epg~LzgZ(&E7V*y3ct|~AGvI-3JcYr{%DF#=;?cH6~ge-
    zxOld^6>1WKg5Fz_Rjl4wlJQj)rtp8yL3r!S<K<bid;Q+mY&EMZN}!KaieT~EVI>hy
    zvZvnmh!tH4T6Js-?vI0<-rzzl{mgT*S0d_7^AU_8gBg^03o-J=p(1o6kww2hx|!%T
    z-jqp}m^G*W?$!R#M%Ef?&2jYxmx+lXWZszpI4d$p<r;|3!?@3AW<2Zgi0<hN9ff)N
    z(zo6I+-$9Bx*(c$-bk0EGqBsb91nmH7yrN`CVj(QCaD{RJgvV-JPkoBQAwGD;nyzn
    z*I;L?L=(3oeAQ<rjW4NvWy!bHdLOHMjezGb#Hb+lSX`#>UN`(S)|*c^CgdwY>Fa>>
    zgGBJhwe8y#Xd*q0=@SLEgPF>+Qe4?%E*v{a`||luZ~&dqMBrRfJ{SDMaJ!s_;cSJp
    zSqZHXIdc@@XteNySUZs^9SG7xK`8=NBN<V=E)OCgg+S0s%X@m8dOqs;y*2U#C_D)u
    z81;Mt5p^uC3PVJP@9PH9!<3b5IE^n;kwm}NvP7!(7^P%;1DOYVJumd1Eg9zSvb@M<
    z=8_n~reVNX{Rwy18un@y&;emesWi1XQooSmDu!<kFo)-HRP5pn?;0r-+4i~5mY$28
    z(;>M)fRVOjw)D^)w%L2OPkTQ$Tel-J)GD3=YXy+F4in(ILy*A3m@3o73uv?JC}Q>f
    zr<Ie&tGbM^0N<roTuDj*?S_O(I}B&He=e8Pl8`tjGg-O~5%TUI<1yQ05r*$Oc2#s#
    z8%FWrdDtn79-cwa2pX4M_-JFx9zK7mChDM?zK(~_K9>Y&8SWmesiba0|3X-jmlMT3
    z*ST|_U@O=i*sM_*48G)dgXqlwoFp5G6qSM3&%_f_*<qxyINw1$We6It<0I>n!P<uj
    z?87vdPOI3mk{cGX^R<>iT>?cNI)fAUkA{qWnqdMi+aNK_yVQ&lx4UZknAc9FIzVk%
    zo6JmFH~c{_tK!gt4+o2>)zoP{sR}!!vfRjI=13!z<fc;{t9y2@_q+%poab^!jwREr
    z2+#Zf9d~36snX-iZ(5U>5}ijMFQ4a4?QIg-BE4T6!#%?d&L;`j5=a`4is>U;%@Rd~
    zXC<xcC%fK=hCSNPW&)8o$8W+KO-SU#5LbV{{RyL+099LpC;6!uxU&{MmE<Y{b<h52
    z$81YnCmIWu(0dlOntRk)&>~H7eGQhhYWhMPWf9znDbYIgwud(6$W3e>$W4$~d%qoJ
    z+JE`1g$qJ%>b|z*xCKenmpV$0pM=Gl-Y*LT8K+P)2X#;XYEFF4mRb<YTI|Oo*wqC5
    z0h9Vcyd1-aYw_k;tVodW95W2hdEX}FLSrp|R+GE56fkm-P)-t$V)|A=l7x|mefFZC
    zXMAilrJt8o)%dz@>c~jj?DM@(1e`nL=F4Syv)TKIePQUz)bZ<lVCgA$*!Fmgxl6o%
    zjdFR@&JKgonL5u$SS;U)hR2JO%(X!<3`;2ma}g7i__wVr1m~_yKAfNhm3c!NlBG8F
    zi*)rX!5cY!j#B&Bh5F)#rbPS@4QDD~@ulB?(x|5|p4JWn*dAG|<;_kq<4J3{W|V%$
    zFux+io?Ym>?Bi3@G@HO$Aps1DvDGkYF50O$_welu^cL7;vPiMGho74$;4fDqKbE{U
    zd1h{;LfM#Fb|Z&uH~Rm_J)R~Vy4b;1?tW_A)Iz#S_=F|~pISaVkCnQ0&u%Yz%o#|!
    zS-TSg87LUfFSs{tTuM3$!06ZzH&MFtG)X-l7>3)V?Txuj2HyG*5u;EY2_5vU0ujA?
    zHXh5G%6e3y7v?AjhyX79pnRBVr}RmPmtrxoB7lkxEzChX^(vKd+sLh?SBic=Q)5nA
    zdz7Mw3_iA>;T^_Kl~?1|5t%GZ;ki_+i>Q~Q1EVdKZ)$Sh3LM@ea&D~{2HOG++7*wF
    zAC6jW4>fa~!Vp5+$Z{<)Qxb|<doy+ePfu6oC(7$`&WuO0q0$+a9a%yz_{5phPWBz7
    zW*;>{unMgCv2)@%3j=7)Zc%U<^i|SAF88s!A^+Xs!OASYT%7;Jx?olg_6NFP1475N
    z#0s<@E~FI}#LNQ{?B1;t+N$2k*`K$Hxb%#8tRQi*Z#No0J}Pl;HWb){l7{A8(pu#@
    zfE<FZzTROa?{|??!(1M&=4t#qdoS<^Na+oYIxC;QnUK0am@X-v$)ut<3yca1@z&t9
    zM)d{X_R6>-OTvEreoz1+p`9sUI%<waswQ*s(MUS7r-ADfL?@KW0)mbJ;|S&qT$0vX
    z+3A>Y{e5L-oTP_^NkgpYhZjp&ykinnW;(fu1;ttpSsgYM8ABX4dHe_HxU+%M(D=~)
    zYM}XUJ5guZ;=_ZcOsC`_{CiU$zN3$+x&5C`vX-V3`8&RjlBs^rf00MNYZW+jCd~7N
    z%{jJuUUwY(M`8$`B>K&_48!Li682ZaRknMgQ3~dnlp8C?__!P2z@=Auv;T^$yrsNy
    zCARmaA@^Yo2sS%2$`031-+h9K<HTVTe5)EQvp!MW(iadmCJS1wSbK_@ufo=dlOY}z
    zCO9zVYKg|I&o<%8Sb*|F!S|!19op-p&g=TZ%N9@L#(UmyHRFj))9t+gQpBfbTesf-
    za`2nVU~8Sd4Kd<Xb>MZsIHfB>s@}>Y(z988e!`%4=EDoAQ0kbk>+lCoK60Mx9P!~I
    zlq~wf7kcm_NFImt3ZYlE(b3O1K^QWiFb$V^a2Jlwvm(!XYx<`i@ZMS3UwFt{;x+-v
    zhx{m=m;4dgvkKp5{*lfSN3o^keSpp9{hlXj%=}e_7Ou{Yiw(J@NXuh*;pL6@$HsfB
    zh?v+r^cp@jQ4E<vE>spC#RqpwPY(}_SS$wZ{S959`C25777&sgtNh%XTCo9VHJC-G
    z;;wi9{-iv+ETiY;K9qvlEc04f;ZnUP>cUL_T*ms``EtGoP^B#Q>n2dSrbAg8a>*Lg
    zd0EJ^=tdW~7fbcLFsqryFEcy*-<UjNQKPSE=_Pn2>8!?;n%;F+8i{eZyCDaiYxghr
    z$8k>L|2&-!lhvuVdk!r-kpSFl`5F5d4DJr%M4-qOy3<bq6e{+%w<EWihn1$%KzFfu
    z`LKHky~)zdoi4^H8U?2zL}?l1u6MD%jgB7&*;Qf>gdmQb<G$UVN?JmKSKB~L!OR=i
    zI@^y#3#{3i>qF1=aBtRM<!CT741&i5jO+s2lsMXtwRPLCm;Sn!-GpQ>7)c_Ae?$b8
    zQg4c8*KQ{XJmL)1c7#0Yn0#PTMEs4-IH<W7>Pjkn0!=;JdhMXqzMLeh`yOylXROP-
    zl#z3+fwM9l3%VN(6R77ua*uI9%hO7l7{+Hcbr(peh;afUK?B4EC09J{-u{mv)+u#?
    zdKVBCPt`eU@IzL)OXA`E<o1(5;mC6=k@-!Ol2~E}J9hOE??)KsP;2EQ2{Z(0gwv}f
    z!It<n&*dKHQo4x|g+0u^h~lZ5Ov4IC#Tfq*CptilVN;HXz`iK4{1F;tZh8So5XLY*
    zXxgB;G7CZ#<Iv1X4e=NIfHyT;2#ek12;Y}7qA*ja41jVbduyrB$HRMX3i4#!N49oM
    z=DRz&*@5P2{)@K+w!!IcW58;P<<)I=(H60m7Iz@T{w1f<%~zS?f9pR^Y*#fpT<Noz
    z19vhe>bu`Xp?u0m%h&X41}FNfnJ*g1!1wcbbpo%F4x!-#R9ft!8{5`Ho}04?FI#Kg
    zL|k`tF1t_`ywdy8(wnTut>HND(qNnq%Sq=AvvZbXnLx|mJhi!*&lwG2g|edBdVgLy
    zjvVTKHAx(+&P;P#2Xobo7_RttUi)Nllc}}hX>|N?-u5g7VJ-NNdwYcaOG?NK=5)}`
    zMtOL;o|i0mSKm(UI_7BL_^6HnVOTkuPI6y@ZLR(H?c1cr-_ouSLp{5!bx^DiKd*Yb
    z{K78Ci<l%%epWQ$#NR9uIf5|S3KV`ZTJ$&qJ6`ry!VhqBuPs(j#jC&+5r^-xzR6fB
    zK27~T)ZekimVRRz-lpCAJu2yR?1~gIvHR5a1NYj$*q3Netl55}ts!oix2<m^q4oKA
    zx&s$GFeBD?)7%@b7gCQPQkbzcY-#e<IqbmH&`NOUj{m_7zrJE%0%MGK`P$ftHCCyA
    z#QEOkdexcb5q+aRNqFbL{IkS#hFvjjH9v~WbirfMFFJD$DOv0$f8V^PmC)h@B?4Tt
    zm|Lni^t};e&92Z{h%k-#j#z#sF&$u2EIp%nX3YhhH9Z@UzRMIVYuCt&$V#l>&Twup
    zTKm)ioN|wcYy%Qnwb)Izb<b#d)i{+1p{kvKer6Fm8jK>H>W!;Ah5Zdm_jRY`+VRJ2
    zhkspZ9hbK3iQD91A$d!0*-1i#%x81|s+SPRmD}d~<1p6!A13(!vABP<Z{iwC7e4%~
    z_Ln8-%lvcLY32-Y@1SO1*q92_(j#+rhCS=CLMntrY3Mry$(OvuZNSYRrU>2kNgqEG
    z?AMgl^P+iRoIY(9@_I?n1829lGvAsRnHwS~|5vD2+Zi53j<5N4wNn0{q>>jF9*bI)
    zL$kMXM-awNOElF>{?Jr^tOz1glbwaD-<Z?hQEA3Pbch{-zrz(GmD@~J*ag^+fZsaw
    zY>M0OKOlTeW3C!1ZyxRbB>8JDof(O&R1bh%3x#>y2~<>OXO#IIedH0Q`(&&?eo-c~
    z>*Ah#3~09unym~UC-UFqqI>{dmUD$Y4@evG#ORLI*{ZM)J<p{vwhmRDEF0r$s4y_e
    z=sJVWn|ZM-lg`hKmi%p5C*Kde*o`ZFJEf1Ej+^5AxXqpoV)MlQbue7)^k_qkb+e;`
    zWde0R#5(=H5cM$dK9LAsdS=Yk0oGNTPVR(|j6Ls{ih2+`6_F=VxMEkqB<u_yrMn-7
    zem-jG!zg{VfBK=QGIg$ZuYze9uWx?aDxho7OdK|L{6b`Vwt6C>l=e1it!XzY($S3V
    zLG!Y6fCjE>x6r@5FG1n|8ompSZaJ>9)q6jqU;XxCQk9zV(?C9<V#w?Lf%1Im<}?28
    z%fv0sO4GSZ%zfKH*&?O&xk<I#mt_{KWN@l7yB^%JPt=7^LfPgcr~mEkBmfFP7Db0M
    zd#E!M<3epZs@^{m3?RG}!71NRBMkEamf~hxD%`6taJAN-7_P+KIU~cqcmswNPF@u0
    zBEd?J2tVMNdm+C_OO1xnDaP<CvO06_?;7EsCcbdr{cefhRUYuKyPaC&4Q})>+i*>w
    z21+KYt1gXX&0`x3E)hS7I5}snbBzox9C@Xzcr|{B8Hw;SY1$}&BoYKXH^hpjW-RgJ
    z-Fb}tannKCv>y~^`r|(1Q9;+sZlYf3XPSX|^gR01UFtu$B*R;$sPZdIZShRr>|b@J
    z;#G{EdoY+O;REEjQ}X7_YzWL<b@Mth=4xckE^wJmIQPsUfw>O+Ey3>a_KDe1CjSe|
    z6arqcEZ)CX!8r(si`dqbF$uu&pnf^Np{1f*TdJ<q2__L6D@tfPK*~rzVm(OhYZi{~
    zO7D1Cy0z3WdT1AOu^h7D1_(%nFOYSW(8K@CEF1cpVqIf7{ZixjH(=6Z%>`r2;@SaZ
    z#hb4xlaCA@Pwqj#LlUEe5L{I$k(Zj$d3(~)u(F%&xb8={N9hKxlZIO1ABsM{Mt|)2
    zJ^t9Id;?%4PfR4&Ph9B9cFK~@tG3wlFW-0<w~5R`uK#F{bA6_apO|PKuT2G1V=wh!
    zZWPJWbbu)nGiWn?;_;mE<K|T11{jR4I#*v{H=AUuEc3+UXA@7uIuDpTy`jcYhUz%o
    zBA}z0OR6}0Iqx8Rc?*~((>fXZS_L4U*EiAA%+`h%q2^6BCC;t0iO<j7`ENmUd8a;m
    zq?b}^r<Irhn?t82<3YNwQO;C@tCYRR<pR}s5&giTT+nc?H}mtH3ZX|EFpV#H_g4in
    z8Tbrg7JdfQvFh#<ovHft;`1YsxU2!leoc~Y)qNFc1mAL8P2+9584$1X7q1nBToy)y
    z$s4}XIl~zQ7=m5m-cT@n8wijJJ$|#uxO(nL+IWs9qk?i9%s#W2ZxqfW`jt6{wIS^q
    z*iUq6jHCeqca?Re1w*!C)k-nH(eV#(PnPU`?~ov%Y+nj9)j3~WBrKHnC<W0QlTNC*
    z<u_q0O?_PoEKdE%)ty@V5F=^-=y+E`(D|T`;&Jjf?_7CST84~oRyM!RwLEZ{ZM@iY
    zIB{U~Ge+IK^?H|Bpj8js3(0P2EU%fWNhAH!9B5rA(2TXL071s~i2t!VlQfp=S*6A2
    zkt-CN_z|1uc9QB1_^Gpz5);n_@pEbj*T#DvuqJuuKb_PutQhcu6?7{m7g7o;mzZA9
    zf{W$DK$@&k565^Y7M*vmK#vF0i(Zb4TM%~5g7C?du<oAbjjU>4V=s4Qug{M|iDV@s
    zC7|ef-dxiR7T&Mpre!%hiUhHM%3Qxi$Lzw6&(Tvlx9QA_7LhYq<(o~=Y>3ka-zrQa
    zhGpfFK@)#)rtfz61w35^sN1=IFw&Oc!Nah+8@qhJ0UEGr;JplaxOGI82OVqZHsqfX
    ze1}r{jy;G?&}Da}a7>S<aX|!tNbjGLu?E#M_FQ+tx7QwU!f|T#|0pGw8beze%W}X8
    zTh%o9Dbrk*KF8LN?^<3buL7%?KbkRMr_jMII=xY`U$vl5f0r@#H-|^ToExGU<wfLd
    zXr+GANZ(jz6qI7<1HwuGyQ7H^naJ1E$XxZfl>CDsFDuzusee<BvkaOnN;I1*%q9kj
    z^#m2ll1tq&oMv5g`}?0u!-DOva7&B0@Z!bH=K`f(k?GfNkG{%)>CKof|Dz2BPsP8?
    zY;a)Tkr2P~0^2BeO?wnzF_<l4Nvqf<W`7QjWtJDSw)B?FOMa{8DG?kxHAQnVhPF5z
    zxnU_-^up4Prel^ed-PkB1+y((Pnm`A;p#0KHiAU@r9|EKB!f~*!CI?=fpguhu1lxJ
    zNfwd#_vJ<v;}^GGOcxE|6OXh~-#_DXMEuzGXcF>Ul-ekY=-w26VnU%U3f19Z-pj&2
    z4J_a|o4Dci+MO)mPQIM>kdPG1<w<ic`+WErB>xydiR9@#<n}&^Z@zb@F^w%zU4>8m
    zh27D7GF{p|a{8({Q-Pr-;#jV{2zHR><r}G)UYxpAdB=!PS*(C~*1H#i#3#T1$j2)t
    z81k%ZC~^7K<oMng7XOD4<}b)aGe_1j<vxx~;=~OWNZThvqsq&|9D#PlGC$L88fM!1
    ziqq3RXQ^4C*>lGoFtIfIpoMo?exuQyX_A;;l0AP4!)JEM$EwMInZkj+8*IHP4vKRd
    zKx_l-i*>A*C@{u%ct`y~s6MWAfO{@FPIX&sg8H{GMDc{4M3%$@c8&RAlw0-R<4DO3
    trJqdc$mBpWeznn?E0M$F`|3v=`3%T2A17h;rxP7$%JLd=6(2u;`(N3pt&so#
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/resources/bootstrap/js/bootstrap.js b/mapbox-gl/resources/bootstrap/js/bootstrap.js
    new file mode 100644
    index 0000000000..c298ee42e3
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/js/bootstrap.js
    @@ -0,0 +1,2276 @@
    +/* ===================================================
    + * bootstrap-transition.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#transitions
    + * ===================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    +  /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
    +   * ======================================================= */
    +
    +  $(function () {
    +
    +    $.support.transition = (function () {
    +
    +      var transitionEnd = (function () {
    +
    +        var el = document.createElement('bootstrap')
    +          , transEndEventNames = {
    +               'WebkitTransition' : 'webkitTransitionEnd'
    +            ,  'MozTransition'    : 'transitionend'
    +            ,  'OTransition'      : 'oTransitionEnd otransitionend'
    +            ,  'transition'       : 'transitionend'
    +            }
    +          , name
    +
    +        for (name in transEndEventNames){
    +          if (el.style[name] !== undefined) {
    +            return transEndEventNames[name]
    +          }
    +        }
    +
    +      }())
    +
    +      return transitionEnd && {
    +        end: transitionEnd
    +      }
    +
    +    })()
    +
    +  })
    +
    +}(window.jQuery);/* ==========================================================
    + * bootstrap-alert.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#alerts
    + * ==========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* ALERT CLASS DEFINITION
    +  * ====================== */
    +
    +  var dismiss = '[data-dismiss="alert"]'
    +    , Alert = function (el) {
    +        $(el).on('click', dismiss, this.close)
    +      }
    +
    +  Alert.prototype.close = function (e) {
    +    var $this = $(this)
    +      , selector = $this.attr('data-target')
    +      , $parent
    +
    +    if (!selector) {
    +      selector = $this.attr('href')
    +      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
    +    }
    +
    +    $parent = $(selector)
    +
    +    e && e.preventDefault()
    +
    +    $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
    +
    +    $parent.trigger(e = $.Event('close'))
    +
    +    if (e.isDefaultPrevented()) return
    +
    +    $parent.removeClass('in')
    +
    +    function removeElement() {
    +      $parent
    +        .trigger('closed')
    +        .remove()
    +    }
    +
    +    $.support.transition && $parent.hasClass('fade') ?
    +      $parent.on($.support.transition.end, removeElement) :
    +      removeElement()
    +  }
    +
    +
    + /* ALERT PLUGIN DEFINITION
    +  * ======================= */
    +
    +  var old = $.fn.alert
    +
    +  $.fn.alert = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('alert')
    +      if (!data) $this.data('alert', (data = new Alert(this)))
    +      if (typeof option == 'string') data[option].call($this)
    +    })
    +  }
    +
    +  $.fn.alert.Constructor = Alert
    +
    +
    + /* ALERT NO CONFLICT
    +  * ================= */
    +
    +  $.fn.alert.noConflict = function () {
    +    $.fn.alert = old
    +    return this
    +  }
    +
    +
    + /* ALERT DATA-API
    +  * ============== */
    +
    +  $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
    +
    +}(window.jQuery);/* ============================================================
    + * bootstrap-button.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#buttons
    + * ============================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ============================================================ */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* BUTTON PUBLIC CLASS DEFINITION
    +  * ============================== */
    +
    +  var Button = function (element, options) {
    +    this.$element = $(element)
    +    this.options = $.extend({}, $.fn.button.defaults, options)
    +  }
    +
    +  Button.prototype.setState = function (state) {
    +    var d = 'disabled'
    +      , $el = this.$element
    +      , data = $el.data()
    +      , val = $el.is('input') ? 'val' : 'html'
    +
    +    state = state + 'Text'
    +    data.resetText || $el.data('resetText', $el[val]())
    +
    +    $el[val](data[state] || this.options[state])
    +
    +    // push to event loop to allow forms to submit
    +    setTimeout(function () {
    +      state == 'loadingText' ?
    +        $el.addClass(d).attr(d, d) :
    +        $el.removeClass(d).removeAttr(d)
    +    }, 0)
    +  }
    +
    +  Button.prototype.toggle = function () {
    +    var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
    +
    +    $parent && $parent
    +      .find('.active')
    +      .removeClass('active')
    +
    +    this.$element.toggleClass('active')
    +  }
    +
    +
    + /* BUTTON PLUGIN DEFINITION
    +  * ======================== */
    +
    +  var old = $.fn.button
    +
    +  $.fn.button = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('button')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('button', (data = new Button(this, options)))
    +      if (option == 'toggle') data.toggle()
    +      else if (option) data.setState(option)
    +    })
    +  }
    +
    +  $.fn.button.defaults = {
    +    loadingText: 'loading...'
    +  }
    +
    +  $.fn.button.Constructor = Button
    +
    +
    + /* BUTTON NO CONFLICT
    +  * ================== */
    +
    +  $.fn.button.noConflict = function () {
    +    $.fn.button = old
    +    return this
    +  }
    +
    +
    + /* BUTTON DATA-API
    +  * =============== */
    +
    +  $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
    +    var $btn = $(e.target)
    +    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
    +    $btn.button('toggle')
    +  })
    +
    +}(window.jQuery);/* ==========================================================
    + * bootstrap-carousel.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#carousel
    + * ==========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* CAROUSEL CLASS DEFINITION
    +  * ========================= */
    +
    +  var Carousel = function (element, options) {
    +    this.$element = $(element)
    +    this.$indicators = this.$element.find('.carousel-indicators')
    +    this.options = options
    +    this.options.pause == 'hover' && this.$element
    +      .on('mouseenter', $.proxy(this.pause, this))
    +      .on('mouseleave', $.proxy(this.cycle, this))
    +  }
    +
    +  Carousel.prototype = {
    +
    +    cycle: function (e) {
    +      if (!e) this.paused = false
    +      if (this.interval) clearInterval(this.interval);
    +      this.options.interval
    +        && !this.paused
    +        && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
    +      return this
    +    }
    +
    +  , getActiveIndex: function () {
    +      this.$active = this.$element.find('.item.active')
    +      this.$items = this.$active.parent().children()
    +      return this.$items.index(this.$active)
    +    }
    +
    +  , to: function (pos) {
    +      var activeIndex = this.getActiveIndex()
    +        , that = this
    +
    +      if (pos > (this.$items.length - 1) || pos < 0) return
    +
    +      if (this.sliding) {
    +        return this.$element.one('slid', function () {
    +          that.to(pos)
    +        })
    +      }
    +
    +      if (activeIndex == pos) {
    +        return this.pause().cycle()
    +      }
    +
    +      return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
    +    }
    +
    +  , pause: function (e) {
    +      if (!e) this.paused = true
    +      if (this.$element.find('.next, .prev').length && $.support.transition.end) {
    +        this.$element.trigger($.support.transition.end)
    +        this.cycle(true)
    +      }
    +      clearInterval(this.interval)
    +      this.interval = null
    +      return this
    +    }
    +
    +  , next: function () {
    +      if (this.sliding) return
    +      return this.slide('next')
    +    }
    +
    +  , prev: function () {
    +      if (this.sliding) return
    +      return this.slide('prev')
    +    }
    +
    +  , slide: function (type, next) {
    +      var $active = this.$element.find('.item.active')
    +        , $next = next || $active[type]()
    +        , isCycling = this.interval
    +        , direction = type == 'next' ? 'left' : 'right'
    +        , fallback  = type == 'next' ? 'first' : 'last'
    +        , that = this
    +        , e
    +
    +      this.sliding = true
    +
    +      isCycling && this.pause()
    +
    +      $next = $next.length ? $next : this.$element.find('.item')[fallback]()
    +
    +      e = $.Event('slide', {
    +        relatedTarget: $next[0]
    +      , direction: direction
    +      })
    +
    +      if ($next.hasClass('active')) return
    +
    +      if (this.$indicators.length) {
    +        this.$indicators.find('.active').removeClass('active')
    +        this.$element.one('slid', function () {
    +          var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
    +          $nextIndicator && $nextIndicator.addClass('active')
    +        })
    +      }
    +
    +      if ($.support.transition && this.$element.hasClass('slide')) {
    +        this.$element.trigger(e)
    +        if (e.isDefaultPrevented()) return
    +        $next.addClass(type)
    +        $next[0].offsetWidth // force reflow
    +        $active.addClass(direction)
    +        $next.addClass(direction)
    +        this.$element.one($.support.transition.end, function () {
    +          $next.removeClass([type, direction].join(' ')).addClass('active')
    +          $active.removeClass(['active', direction].join(' '))
    +          that.sliding = false
    +          setTimeout(function () { that.$element.trigger('slid') }, 0)
    +        })
    +      } else {
    +        this.$element.trigger(e)
    +        if (e.isDefaultPrevented()) return
    +        $active.removeClass('active')
    +        $next.addClass('active')
    +        this.sliding = false
    +        this.$element.trigger('slid')
    +      }
    +
    +      isCycling && this.cycle()
    +
    +      return this
    +    }
    +
    +  }
    +
    +
    + /* CAROUSEL PLUGIN DEFINITION
    +  * ========================== */
    +
    +  var old = $.fn.carousel
    +
    +  $.fn.carousel = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('carousel')
    +        , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
    +        , action = typeof option == 'string' ? option : options.slide
    +      if (!data) $this.data('carousel', (data = new Carousel(this, options)))
    +      if (typeof option == 'number') data.to(option)
    +      else if (action) data[action]()
    +      else if (options.interval) data.pause().cycle()
    +    })
    +  }
    +
    +  $.fn.carousel.defaults = {
    +    interval: 5000
    +  , pause: 'hover'
    +  }
    +
    +  $.fn.carousel.Constructor = Carousel
    +
    +
    + /* CAROUSEL NO CONFLICT
    +  * ==================== */
    +
    +  $.fn.carousel.noConflict = function () {
    +    $.fn.carousel = old
    +    return this
    +  }
    +
    + /* CAROUSEL DATA-API
    +  * ================= */
    +
    +  $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
    +    var $this = $(this), href
    +      , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
    +      , options = $.extend({}, $target.data(), $this.data())
    +      , slideIndex
    +
    +    $target.carousel(options)
    +
    +    if (slideIndex = $this.attr('data-slide-to')) {
    +      $target.data('carousel').pause().to(slideIndex).cycle()
    +    }
    +
    +    e.preventDefault()
    +  })
    +
    +}(window.jQuery);/* =============================================================
    + * bootstrap-collapse.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#collapse
    + * =============================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ============================================================ */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* COLLAPSE PUBLIC CLASS DEFINITION
    +  * ================================ */
    +
    +  var Collapse = function (element, options) {
    +    this.$element = $(element)
    +    this.options = $.extend({}, $.fn.collapse.defaults, options)
    +
    +    if (this.options.parent) {
    +      this.$parent = $(this.options.parent)
    +    }
    +
    +    this.options.toggle && this.toggle()
    +  }
    +
    +  Collapse.prototype = {
    +
    +    constructor: Collapse
    +
    +  , dimension: function () {
    +      var hasWidth = this.$element.hasClass('width')
    +      return hasWidth ? 'width' : 'height'
    +    }
    +
    +  , show: function () {
    +      var dimension
    +        , scroll
    +        , actives
    +        , hasData
    +
    +      if (this.transitioning || this.$element.hasClass('in')) return
    +
    +      dimension = this.dimension()
    +      scroll = $.camelCase(['scroll', dimension].join('-'))
    +      actives = this.$parent && this.$parent.find('> .accordion-group > .in')
    +
    +      if (actives && actives.length) {
    +        hasData = actives.data('collapse')
    +        if (hasData && hasData.transitioning) return
    +        actives.collapse('hide')
    +        hasData || actives.data('collapse', null)
    +      }
    +
    +      this.$element[dimension](0)
    +      this.transition('addClass', $.Event('show'), 'shown')
    +      $.support.transition && this.$element[dimension](this.$element[0][scroll])
    +    }
    +
    +  , hide: function () {
    +      var dimension
    +      if (this.transitioning || !this.$element.hasClass('in')) return
    +      dimension = this.dimension()
    +      this.reset(this.$element[dimension]())
    +      this.transition('removeClass', $.Event('hide'), 'hidden')
    +      this.$element[dimension](0)
    +    }
    +
    +  , reset: function (size) {
    +      var dimension = this.dimension()
    +
    +      this.$element
    +        .removeClass('collapse')
    +        [dimension](size || 'auto')
    +        [0].offsetWidth
    +
    +      this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
    +
    +      return this
    +    }
    +
    +  , transition: function (method, startEvent, completeEvent) {
    +      var that = this
    +        , complete = function () {
    +            if (startEvent.type == 'show') that.reset()
    +            that.transitioning = 0
    +            that.$element.trigger(completeEvent)
    +          }
    +
    +      this.$element.trigger(startEvent)
    +
    +      if (startEvent.isDefaultPrevented()) return
    +
    +      this.transitioning = 1
    +
    +      this.$element[method]('in')
    +
    +      $.support.transition && this.$element.hasClass('collapse') ?
    +        this.$element.one($.support.transition.end, complete) :
    +        complete()
    +    }
    +
    +  , toggle: function () {
    +      this[this.$element.hasClass('in') ? 'hide' : 'show']()
    +    }
    +
    +  }
    +
    +
    + /* COLLAPSE PLUGIN DEFINITION
    +  * ========================== */
    +
    +  var old = $.fn.collapse
    +
    +  $.fn.collapse = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('collapse')
    +        , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
    +      if (!data) $this.data('collapse', (data = new Collapse(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.collapse.defaults = {
    +    toggle: true
    +  }
    +
    +  $.fn.collapse.Constructor = Collapse
    +
    +
    + /* COLLAPSE NO CONFLICT
    +  * ==================== */
    +
    +  $.fn.collapse.noConflict = function () {
    +    $.fn.collapse = old
    +    return this
    +  }
    +
    +
    + /* COLLAPSE DATA-API
    +  * ================= */
    +
    +  $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
    +    var $this = $(this), href
    +      , target = $this.attr('data-target')
    +        || e.preventDefault()
    +        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
    +      , option = $(target).data('collapse') ? 'toggle' : $this.data()
    +    $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
    +    $(target).collapse(option)
    +  })
    +
    +}(window.jQuery);/* ============================================================
    + * bootstrap-dropdown.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#dropdowns
    + * ============================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ============================================================ */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* DROPDOWN CLASS DEFINITION
    +  * ========================= */
    +
    +  var toggle = '[data-toggle=dropdown]'
    +    , Dropdown = function (element) {
    +        var $el = $(element).on('click.dropdown.data-api', this.toggle)
    +        $('html').on('click.dropdown.data-api', function () {
    +          $el.parent().removeClass('open')
    +        })
    +      }
    +
    +  Dropdown.prototype = {
    +
    +    constructor: Dropdown
    +
    +  , toggle: function (e) {
    +      var $this = $(this)
    +        , $parent
    +        , isActive
    +
    +      if ($this.is('.disabled, :disabled')) return
    +
    +      $parent = getParent($this)
    +
    +      isActive = $parent.hasClass('open')
    +
    +      clearMenus()
    +
    +      if (!isActive) {
    +        $parent.toggleClass('open')
    +      }
    +
    +      $this.focus()
    +
    +      return false
    +    }
    +
    +  , keydown: function (e) {
    +      var $this
    +        , $items
    +        , $active
    +        , $parent
    +        , isActive
    +        , index
    +
    +      if (!/(38|40|27)/.test(e.keyCode)) return
    +
    +      $this = $(this)
    +
    +      e.preventDefault()
    +      e.stopPropagation()
    +
    +      if ($this.is('.disabled, :disabled')) return
    +
    +      $parent = getParent($this)
    +
    +      isActive = $parent.hasClass('open')
    +
    +      if (!isActive || (isActive && e.keyCode == 27)) {
    +        if (e.which == 27) $parent.find(toggle).focus()
    +        return $this.click()
    +      }
    +
    +      $items = $('[role=menu] li:not(.divider):visible a', $parent)
    +
    +      if (!$items.length) return
    +
    +      index = $items.index($items.filter(':focus'))
    +
    +      if (e.keyCode == 38 && index > 0) index--                                        // up
    +      if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
    +      if (!~index) index = 0
    +
    +      $items
    +        .eq(index)
    +        .focus()
    +    }
    +
    +  }
    +
    +  function clearMenus() {
    +    $(toggle).each(function () {
    +      getParent($(this)).removeClass('open')
    +    })
    +  }
    +
    +  function getParent($this) {
    +    var selector = $this.attr('data-target')
    +      , $parent
    +
    +    if (!selector) {
    +      selector = $this.attr('href')
    +      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
    +    }
    +
    +    $parent = selector && $(selector)
    +
    +    if (!$parent || !$parent.length) $parent = $this.parent()
    +
    +    return $parent
    +  }
    +
    +
    +  /* DROPDOWN PLUGIN DEFINITION
    +   * ========================== */
    +
    +  var old = $.fn.dropdown
    +
    +  $.fn.dropdown = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('dropdown')
    +      if (!data) $this.data('dropdown', (data = new Dropdown(this)))
    +      if (typeof option == 'string') data[option].call($this)
    +    })
    +  }
    +
    +  $.fn.dropdown.Constructor = Dropdown
    +
    +
    + /* DROPDOWN NO CONFLICT
    +  * ==================== */
    +
    +  $.fn.dropdown.noConflict = function () {
    +    $.fn.dropdown = old
    +    return this
    +  }
    +
    +
    +  /* APPLY TO STANDARD DROPDOWN ELEMENTS
    +   * =================================== */
    +
    +  $(document)
    +    .on('click.dropdown.data-api', clearMenus)
    +    .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
    +    .on('click.dropdown-menu', function (e) { e.stopPropagation() })
    +    .on('click.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)
    +    .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
    +
    +}(window.jQuery);
    +/* =========================================================
    + * bootstrap-modal.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#modals
    + * =========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================= */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* MODAL CLASS DEFINITION
    +  * ====================== */
    +
    +  var Modal = function (element, options) {
    +    this.options = options
    +    this.$element = $(element)
    +      .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
    +    this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
    +  }
    +
    +  Modal.prototype = {
    +
    +      constructor: Modal
    +
    +    , toggle: function () {
    +        return this[!this.isShown ? 'show' : 'hide']()
    +      }
    +
    +    , show: function () {
    +        var that = this
    +          , e = $.Event('show')
    +
    +        this.$element.trigger(e)
    +
    +        if (this.isShown || e.isDefaultPrevented()) return
    +
    +        this.isShown = true
    +
    +        this.escape()
    +
    +        this.backdrop(function () {
    +          var transition = $.support.transition && that.$element.hasClass('fade')
    +
    +          if (!that.$element.parent().length) {
    +            that.$element.appendTo(document.body) //don't move modals dom position
    +          }
    +
    +          that.$element.show()
    +
    +          if (transition) {
    +            that.$element[0].offsetWidth // force reflow
    +          }
    +
    +          that.$element
    +            .addClass('in')
    +            .attr('aria-hidden', false)
    +
    +          that.enforceFocus()
    +
    +          transition ?
    +            that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
    +            that.$element.focus().trigger('shown')
    +
    +        })
    +      }
    +
    +    , hide: function (e) {
    +        e && e.preventDefault()
    +
    +        var that = this
    +
    +        e = $.Event('hide')
    +
    +        this.$element.trigger(e)
    +
    +        if (!this.isShown || e.isDefaultPrevented()) return
    +
    +        this.isShown = false
    +
    +        this.escape()
    +
    +        $(document).off('focusin.modal')
    +
    +        this.$element
    +          .removeClass('in')
    +          .attr('aria-hidden', true)
    +
    +        $.support.transition && this.$element.hasClass('fade') ?
    +          this.hideWithTransition() :
    +          this.hideModal()
    +      }
    +
    +    , enforceFocus: function () {
    +        var that = this
    +        $(document).on('focusin.modal', function (e) {
    +          if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
    +            that.$element.focus()
    +          }
    +        })
    +      }
    +
    +    , escape: function () {
    +        var that = this
    +        if (this.isShown && this.options.keyboard) {
    +          this.$element.on('keyup.dismiss.modal', function ( e ) {
    +            e.which == 27 && that.hide()
    +          })
    +        } else if (!this.isShown) {
    +          this.$element.off('keyup.dismiss.modal')
    +        }
    +      }
    +
    +    , hideWithTransition: function () {
    +        var that = this
    +          , timeout = setTimeout(function () {
    +              that.$element.off($.support.transition.end)
    +              that.hideModal()
    +            }, 500)
    +
    +        this.$element.one($.support.transition.end, function () {
    +          clearTimeout(timeout)
    +          that.hideModal()
    +        })
    +      }
    +
    +    , hideModal: function () {
    +        var that = this
    +        this.$element.hide()
    +        this.backdrop(function () {
    +          that.removeBackdrop()
    +          that.$element.trigger('hidden')
    +        })
    +      }
    +
    +    , removeBackdrop: function () {
    +        this.$backdrop && this.$backdrop.remove()
    +        this.$backdrop = null
    +      }
    +
    +    , backdrop: function (callback) {
    +        var that = this
    +          , animate = this.$element.hasClass('fade') ? 'fade' : ''
    +
    +        if (this.isShown && this.options.backdrop) {
    +          var doAnimate = $.support.transition && animate
    +
    +          this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
    +            .appendTo(document.body)
    +
    +          this.$backdrop.click(
    +            this.options.backdrop == 'static' ?
    +              $.proxy(this.$element[0].focus, this.$element[0])
    +            : $.proxy(this.hide, this)
    +          )
    +
    +          if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
    +
    +          this.$backdrop.addClass('in')
    +
    +          if (!callback) return
    +
    +          doAnimate ?
    +            this.$backdrop.one($.support.transition.end, callback) :
    +            callback()
    +
    +        } else if (!this.isShown && this.$backdrop) {
    +          this.$backdrop.removeClass('in')
    +
    +          $.support.transition && this.$element.hasClass('fade')?
    +            this.$backdrop.one($.support.transition.end, callback) :
    +            callback()
    +
    +        } else if (callback) {
    +          callback()
    +        }
    +      }
    +  }
    +
    +
    + /* MODAL PLUGIN DEFINITION
    +  * ======================= */
    +
    +  var old = $.fn.modal
    +
    +  $.fn.modal = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('modal')
    +        , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
    +      if (!data) $this.data('modal', (data = new Modal(this, options)))
    +      if (typeof option == 'string') data[option]()
    +      else if (options.show) data.show()
    +    })
    +  }
    +
    +  $.fn.modal.defaults = {
    +      backdrop: true
    +    , keyboard: true
    +    , show: true
    +  }
    +
    +  $.fn.modal.Constructor = Modal
    +
    +
    + /* MODAL NO CONFLICT
    +  * ================= */
    +
    +  $.fn.modal.noConflict = function () {
    +    $.fn.modal = old
    +    return this
    +  }
    +
    +
    + /* MODAL DATA-API
    +  * ============== */
    +
    +  $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
    +    var $this = $(this)
    +      , href = $this.attr('href')
    +      , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
    +      , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
    +
    +    e.preventDefault()
    +
    +    $target
    +      .modal(option)
    +      .one('hide', function () {
    +        $this.focus()
    +      })
    +  })
    +
    +}(window.jQuery);
    +/* ===========================================================
    + * bootstrap-tooltip.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#tooltips
    + * Inspired by the original jQuery.tipsy by Jason Frame
    + * ===========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* TOOLTIP PUBLIC CLASS DEFINITION
    +  * =============================== */
    +
    +  var Tooltip = function (element, options) {
    +    this.init('tooltip', element, options)
    +  }
    +
    +  Tooltip.prototype = {
    +
    +    constructor: Tooltip
    +
    +  , init: function (type, element, options) {
    +      var eventIn
    +        , eventOut
    +        , triggers
    +        , trigger
    +        , i
    +
    +      this.type = type
    +      this.$element = $(element)
    +      this.options = this.getOptions(options)
    +      this.enabled = true
    +
    +      triggers = this.options.trigger.split(' ')
    +
    +      for (i = triggers.length; i--;) {
    +        trigger = triggers[i]
    +        if (trigger == 'click') {
    +          this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
    +        } else if (trigger != 'manual') {
    +          eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
    +          eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
    +          this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
    +          this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
    +        }
    +      }
    +
    +      this.options.selector ?
    +        (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
    +        this.fixTitle()
    +    }
    +
    +  , getOptions: function (options) {
    +      options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
    +
    +      if (options.delay && typeof options.delay == 'number') {
    +        options.delay = {
    +          show: options.delay
    +        , hide: options.delay
    +        }
    +      }
    +
    +      return options
    +    }
    +
    +  , enter: function (e) {
    +      var defaults = $.fn[this.type].defaults
    +        , options = {}
    +        , self
    +
    +      this._options && $.each(this._options, function (key, value) {
    +        if (defaults[key] != value) options[key] = value
    +      }, this)
    +
    +      self = $(e.currentTarget)[this.type](options).data(this.type)
    +
    +      if (!self.options.delay || !self.options.delay.show) return self.show()
    +
    +      clearTimeout(this.timeout)
    +      self.hoverState = 'in'
    +      this.timeout = setTimeout(function() {
    +        if (self.hoverState == 'in') self.show()
    +      }, self.options.delay.show)
    +    }
    +
    +  , leave: function (e) {
    +      var self = $(e.currentTarget)[this.type](this._options).data(this.type)
    +
    +      if (this.timeout) clearTimeout(this.timeout)
    +      if (!self.options.delay || !self.options.delay.hide) return self.hide()
    +
    +      self.hoverState = 'out'
    +      this.timeout = setTimeout(function() {
    +        if (self.hoverState == 'out') self.hide()
    +      }, self.options.delay.hide)
    +    }
    +
    +  , show: function () {
    +      var $tip
    +        , pos
    +        , actualWidth
    +        , actualHeight
    +        , placement
    +        , tp
    +        , e = $.Event('show')
    +
    +      if (this.hasContent() && this.enabled) {
    +        this.$element.trigger(e)
    +        if (e.isDefaultPrevented()) return
    +        $tip = this.tip()
    +        this.setContent()
    +
    +        if (this.options.animation) {
    +          $tip.addClass('fade')
    +        }
    +
    +        placement = typeof this.options.placement == 'function' ?
    +          this.options.placement.call(this, $tip[0], this.$element[0]) :
    +          this.options.placement
    +
    +        $tip
    +          .detach()
    +          .css({ top: 0, left: 0, display: 'block' })
    +
    +        this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
    +
    +        pos = this.getPosition()
    +
    +        actualWidth = $tip[0].offsetWidth
    +        actualHeight = $tip[0].offsetHeight
    +
    +        switch (placement) {
    +          case 'bottom':
    +            tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
    +            break
    +          case 'top':
    +            tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
    +            break
    +          case 'left':
    +            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
    +            break
    +          case 'right':
    +            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
    +            break
    +        }
    +
    +        this.applyPlacement(tp, placement)
    +        this.$element.trigger('shown')
    +      }
    +    }
    +
    +  , applyPlacement: function(offset, placement){
    +      var $tip = this.tip()
    +        , width = $tip[0].offsetWidth
    +        , height = $tip[0].offsetHeight
    +        , actualWidth
    +        , actualHeight
    +        , delta
    +        , replace
    +
    +      $tip
    +        .offset(offset)
    +        .addClass(placement)
    +        .addClass('in')
    +
    +      actualWidth = $tip[0].offsetWidth
    +      actualHeight = $tip[0].offsetHeight
    +
    +      if (placement == 'top' && actualHeight != height) {
    +        offset.top = offset.top + height - actualHeight
    +        replace = true
    +      }
    +
    +      if (placement == 'bottom' || placement == 'top') {
    +        delta = 0
    +
    +        if (offset.left < 0){
    +          delta = offset.left * -2
    +          offset.left = 0
    +          $tip.offset(offset)
    +          actualWidth = $tip[0].offsetWidth
    +          actualHeight = $tip[0].offsetHeight
    +        }
    +
    +        this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
    +      } else {
    +        this.replaceArrow(actualHeight - height, actualHeight, 'top')
    +      }
    +
    +      if (replace) $tip.offset(offset)
    +    }
    +
    +  , replaceArrow: function(delta, dimension, position){
    +      this
    +        .arrow()
    +        .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
    +    }
    +
    +  , setContent: function () {
    +      var $tip = this.tip()
    +        , title = this.getTitle()
    +
    +      $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
    +      $tip.removeClass('fade in top bottom left right')
    +    }
    +
    +  , hide: function () {
    +      var that = this
    +        , $tip = this.tip()
    +        , e = $.Event('hide')
    +
    +      this.$element.trigger(e)
    +      if (e.isDefaultPrevented()) return
    +
    +      $tip.removeClass('in')
    +
    +      function removeWithAnimation() {
    +        var timeout = setTimeout(function () {
    +          $tip.off($.support.transition.end).detach()
    +        }, 500)
    +
    +        $tip.one($.support.transition.end, function () {
    +          clearTimeout(timeout)
    +          $tip.detach()
    +        })
    +      }
    +
    +      $.support.transition && this.$tip.hasClass('fade') ?
    +        removeWithAnimation() :
    +        $tip.detach()
    +
    +      this.$element.trigger('hidden')
    +
    +      return this
    +    }
    +
    +  , fixTitle: function () {
    +      var $e = this.$element
    +      if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
    +        $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
    +      }
    +    }
    +
    +  , hasContent: function () {
    +      return this.getTitle()
    +    }
    +
    +  , getPosition: function () {
    +      var el = this.$element[0]
    +      return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
    +        width: el.offsetWidth
    +      , height: el.offsetHeight
    +      }, this.$element.offset())
    +    }
    +
    +  , getTitle: function () {
    +      var title
    +        , $e = this.$element
    +        , o = this.options
    +
    +      title = $e.attr('data-original-title')
    +        || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
    +
    +      return title
    +    }
    +
    +  , tip: function () {
    +      return this.$tip = this.$tip || $(this.options.template)
    +    }
    +
    +  , arrow: function(){
    +      return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
    +    }
    +
    +  , validate: function () {
    +      if (!this.$element[0].parentNode) {
    +        this.hide()
    +        this.$element = null
    +        this.options = null
    +      }
    +    }
    +
    +  , enable: function () {
    +      this.enabled = true
    +    }
    +
    +  , disable: function () {
    +      this.enabled = false
    +    }
    +
    +  , toggleEnabled: function () {
    +      this.enabled = !this.enabled
    +    }
    +
    +  , toggle: function (e) {
    +      var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
    +      self.tip().hasClass('in') ? self.hide() : self.show()
    +    }
    +
    +  , destroy: function () {
    +      this.hide().$element.off('.' + this.type).removeData(this.type)
    +    }
    +
    +  }
    +
    +
    + /* TOOLTIP PLUGIN DEFINITION
    +  * ========================= */
    +
    +  var old = $.fn.tooltip
    +
    +  $.fn.tooltip = function ( option ) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('tooltip')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.tooltip.Constructor = Tooltip
    +
    +  $.fn.tooltip.defaults = {
    +    animation: true
    +  , placement: 'top'
    +  , selector: false
    +  , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
    +  , trigger: 'hover focus'
    +  , title: ''
    +  , delay: 0
    +  , html: false
    +  , container: false
    +  }
    +
    +
    + /* TOOLTIP NO CONFLICT
    +  * =================== */
    +
    +  $.fn.tooltip.noConflict = function () {
    +    $.fn.tooltip = old
    +    return this
    +  }
    +
    +}(window.jQuery);
    +/* ===========================================================
    + * bootstrap-popover.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#popovers
    + * ===========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * =========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* POPOVER PUBLIC CLASS DEFINITION
    +  * =============================== */
    +
    +  var Popover = function (element, options) {
    +    this.init('popover', element, options)
    +  }
    +
    +
    +  /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
    +     ========================================== */
    +
    +  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
    +
    +    constructor: Popover
    +
    +  , setContent: function () {
    +      var $tip = this.tip()
    +        , title = this.getTitle()
    +        , content = this.getContent()
    +
    +      $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
    +      $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
    +
    +      $tip.removeClass('fade top bottom left right in')
    +    }
    +
    +  , hasContent: function () {
    +      return this.getTitle() || this.getContent()
    +    }
    +
    +  , getContent: function () {
    +      var content
    +        , $e = this.$element
    +        , o = this.options
    +
    +      content = (typeof o.content == 'function' ? o.content.call($e[0]) :  o.content)
    +        || $e.attr('data-content')
    +
    +      return content
    +    }
    +
    +  , tip: function () {
    +      if (!this.$tip) {
    +        this.$tip = $(this.options.template)
    +      }
    +      return this.$tip
    +    }
    +
    +  , destroy: function () {
    +      this.hide().$element.off('.' + this.type).removeData(this.type)
    +    }
    +
    +  })
    +
    +
    + /* POPOVER PLUGIN DEFINITION
    +  * ======================= */
    +
    +  var old = $.fn.popover
    +
    +  $.fn.popover = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('popover')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('popover', (data = new Popover(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.popover.Constructor = Popover
    +
    +  $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
    +    placement: 'right'
    +  , trigger: 'click'
    +  , content: ''
    +  , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
    +  })
    +
    +
    + /* POPOVER NO CONFLICT
    +  * =================== */
    +
    +  $.fn.popover.noConflict = function () {
    +    $.fn.popover = old
    +    return this
    +  }
    +
    +}(window.jQuery);
    +/* =============================================================
    + * bootstrap-scrollspy.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#scrollspy
    + * =============================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ============================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* SCROLLSPY CLASS DEFINITION
    +  * ========================== */
    +
    +  function ScrollSpy(element, options) {
    +    var process = $.proxy(this.process, this)
    +      , $element = $(element).is('body') ? $(window) : $(element)
    +      , href
    +    this.options = $.extend({}, $.fn.scrollspy.defaults, options)
    +    this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
    +    this.selector = (this.options.target
    +      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
    +      || '') + ' .nav li > a'
    +    this.$body = $('body')
    +    this.refresh()
    +    this.process()
    +  }
    +
    +  ScrollSpy.prototype = {
    +
    +      constructor: ScrollSpy
    +
    +    , refresh: function () {
    +        var self = this
    +          , $targets
    +
    +        this.offsets = $([])
    +        this.targets = $([])
    +
    +        $targets = this.$body
    +          .find(this.selector)
    +          .map(function () {
    +            var $el = $(this)
    +              , href = $el.data('target') || $el.attr('href')
    +              , $href = /^#\w/.test(href) && $(href)
    +            return ( $href
    +              && $href.length
    +              && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
    +          })
    +          .sort(function (a, b) { return a[0] - b[0] })
    +          .each(function () {
    +            self.offsets.push(this[0])
    +            self.targets.push(this[1])
    +          })
    +      }
    +
    +    , process: function () {
    +        var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
    +          , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
    +          , maxScroll = scrollHeight - this.$scrollElement.height()
    +          , offsets = this.offsets
    +          , targets = this.targets
    +          , activeTarget = this.activeTarget
    +          , i
    +
    +        if (scrollTop >= maxScroll) {
    +          return activeTarget != (i = targets.last()[0])
    +            && this.activate ( i )
    +        }
    +
    +        for (i = offsets.length; i--;) {
    +          activeTarget != targets[i]
    +            && scrollTop >= offsets[i]
    +            && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
    +            && this.activate( targets[i] )
    +        }
    +      }
    +
    +    , activate: function (target) {
    +        var active
    +          , selector
    +
    +        this.activeTarget = target
    +
    +        $(this.selector)
    +          .parent('.active')
    +          .removeClass('active')
    +
    +        selector = this.selector
    +          + '[data-target="' + target + '"],'
    +          + this.selector + '[href="' + target + '"]'
    +
    +        active = $(selector)
    +          .parent('li')
    +          .addClass('active')
    +
    +        if (active.parent('.dropdown-menu').length)  {
    +          active = active.closest('li.dropdown').addClass('active')
    +        }
    +
    +        active.trigger('activate')
    +      }
    +
    +  }
    +
    +
    + /* SCROLLSPY PLUGIN DEFINITION
    +  * =========================== */
    +
    +  var old = $.fn.scrollspy
    +
    +  $.fn.scrollspy = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('scrollspy')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.scrollspy.Constructor = ScrollSpy
    +
    +  $.fn.scrollspy.defaults = {
    +    offset: 10
    +  }
    +
    +
    + /* SCROLLSPY NO CONFLICT
    +  * ===================== */
    +
    +  $.fn.scrollspy.noConflict = function () {
    +    $.fn.scrollspy = old
    +    return this
    +  }
    +
    +
    + /* SCROLLSPY DATA-API
    +  * ================== */
    +
    +  $(window).on('load', function () {
    +    $('[data-spy="scroll"]').each(function () {
    +      var $spy = $(this)
    +      $spy.scrollspy($spy.data())
    +    })
    +  })
    +
    +}(window.jQuery);/* ========================================================
    + * bootstrap-tab.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#tabs
    + * ========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ======================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* TAB CLASS DEFINITION
    +  * ==================== */
    +
    +  var Tab = function (element) {
    +    this.element = $(element)
    +  }
    +
    +  Tab.prototype = {
    +
    +    constructor: Tab
    +
    +  , show: function () {
    +      var $this = this.element
    +        , $ul = $this.closest('ul:not(.dropdown-menu)')
    +        , selector = $this.attr('data-target')
    +        , previous
    +        , $target
    +        , e
    +
    +      if (!selector) {
    +        selector = $this.attr('href')
    +        selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
    +      }
    +
    +      if ( $this.parent('li').hasClass('active') ) return
    +
    +      previous = $ul.find('.active:last a')[0]
    +
    +      e = $.Event('show', {
    +        relatedTarget: previous
    +      })
    +
    +      $this.trigger(e)
    +
    +      if (e.isDefaultPrevented()) return
    +
    +      $target = $(selector)
    +
    +      this.activate($this.parent('li'), $ul)
    +      this.activate($target, $target.parent(), function () {
    +        $this.trigger({
    +          type: 'shown'
    +        , relatedTarget: previous
    +        })
    +      })
    +    }
    +
    +  , activate: function ( element, container, callback) {
    +      var $active = container.find('> .active')
    +        , transition = callback
    +            && $.support.transition
    +            && $active.hasClass('fade')
    +
    +      function next() {
    +        $active
    +          .removeClass('active')
    +          .find('> .dropdown-menu > .active')
    +          .removeClass('active')
    +
    +        element.addClass('active')
    +
    +        if (transition) {
    +          element[0].offsetWidth // reflow for transition
    +          element.addClass('in')
    +        } else {
    +          element.removeClass('fade')
    +        }
    +
    +        if ( element.parent('.dropdown-menu') ) {
    +          element.closest('li.dropdown').addClass('active')
    +        }
    +
    +        callback && callback()
    +      }
    +
    +      transition ?
    +        $active.one($.support.transition.end, next) :
    +        next()
    +
    +      $active.removeClass('in')
    +    }
    +  }
    +
    +
    + /* TAB PLUGIN DEFINITION
    +  * ===================== */
    +
    +  var old = $.fn.tab
    +
    +  $.fn.tab = function ( option ) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('tab')
    +      if (!data) $this.data('tab', (data = new Tab(this)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.tab.Constructor = Tab
    +
    +
    + /* TAB NO CONFLICT
    +  * =============== */
    +
    +  $.fn.tab.noConflict = function () {
    +    $.fn.tab = old
    +    return this
    +  }
    +
    +
    + /* TAB DATA-API
    +  * ============ */
    +
    +  $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
    +    e.preventDefault()
    +    $(this).tab('show')
    +  })
    +
    +}(window.jQuery);/* =============================================================
    + * bootstrap-typeahead.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#typeahead
    + * =============================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ============================================================ */
    +
    +
    +!function($){
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* TYPEAHEAD PUBLIC CLASS DEFINITION
    +  * ================================= */
    +
    +  var Typeahead = function (element, options) {
    +    this.$element = $(element)
    +    this.options = $.extend({}, $.fn.typeahead.defaults, options)
    +    this.matcher = this.options.matcher || this.matcher
    +    this.sorter = this.options.sorter || this.sorter
    +    this.highlighter = this.options.highlighter || this.highlighter
    +    this.updater = this.options.updater || this.updater
    +    this.source = this.options.source
    +    this.$menu = $(this.options.menu)
    +    this.shown = false
    +    this.listen()
    +  }
    +
    +  Typeahead.prototype = {
    +
    +    constructor: Typeahead
    +
    +  , select: function () {
    +      var val = this.$menu.find('.active').attr('data-value')
    +      this.$element
    +        .val(this.updater(val))
    +        .change()
    +      return this.hide()
    +    }
    +
    +  , updater: function (item) {
    +      return item
    +    }
    +
    +  , show: function () {
    +      var pos = $.extend({}, this.$element.position(), {
    +        height: this.$element[0].offsetHeight
    +      })
    +
    +      this.$menu
    +        .insertAfter(this.$element)
    +        .css({
    +          top: pos.top + pos.height
    +        , left: pos.left
    +        })
    +        .show()
    +
    +      this.shown = true
    +      return this
    +    }
    +
    +  , hide: function () {
    +      this.$menu.hide()
    +      this.shown = false
    +      return this
    +    }
    +
    +  , lookup: function (event) {
    +      var items
    +
    +      this.query = this.$element.val()
    +
    +      if (!this.query || this.query.length < this.options.minLength) {
    +        return this.shown ? this.hide() : this
    +      }
    +
    +      items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
    +
    +      return items ? this.process(items) : this
    +    }
    +
    +  , process: function (items) {
    +      var that = this
    +
    +      items = $.grep(items, function (item) {
    +        return that.matcher(item)
    +      })
    +
    +      items = this.sorter(items)
    +
    +      if (!items.length) {
    +        return this.shown ? this.hide() : this
    +      }
    +
    +      return this.render(items.slice(0, this.options.items)).show()
    +    }
    +
    +  , matcher: function (item) {
    +      return ~item.toLowerCase().indexOf(this.query.toLowerCase())
    +    }
    +
    +  , sorter: function (items) {
    +      var beginswith = []
    +        , caseSensitive = []
    +        , caseInsensitive = []
    +        , item
    +
    +      while (item = items.shift()) {
    +        if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
    +        else if (~item.indexOf(this.query)) caseSensitive.push(item)
    +        else caseInsensitive.push(item)
    +      }
    +
    +      return beginswith.concat(caseSensitive, caseInsensitive)
    +    }
    +
    +  , highlighter: function (item) {
    +      var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
    +      return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
    +        return '<strong>' + match + '</strong>'
    +      })
    +    }
    +
    +  , render: function (items) {
    +      var that = this
    +
    +      items = $(items).map(function (i, item) {
    +        i = $(that.options.item).attr('data-value', item)
    +        i.find('a').html(that.highlighter(item))
    +        return i[0]
    +      })
    +
    +      items.first().addClass('active')
    +      this.$menu.html(items)
    +      return this
    +    }
    +
    +  , next: function (event) {
    +      var active = this.$menu.find('.active').removeClass('active')
    +        , next = active.next()
    +
    +      if (!next.length) {
    +        next = $(this.$menu.find('li')[0])
    +      }
    +
    +      next.addClass('active')
    +    }
    +
    +  , prev: function (event) {
    +      var active = this.$menu.find('.active').removeClass('active')
    +        , prev = active.prev()
    +
    +      if (!prev.length) {
    +        prev = this.$menu.find('li').last()
    +      }
    +
    +      prev.addClass('active')
    +    }
    +
    +  , listen: function () {
    +      this.$element
    +        .on('focus',    $.proxy(this.focus, this))
    +        .on('blur',     $.proxy(this.blur, this))
    +        .on('keypress', $.proxy(this.keypress, this))
    +        .on('keyup',    $.proxy(this.keyup, this))
    +
    +      if (this.eventSupported('keydown')) {
    +        this.$element.on('keydown', $.proxy(this.keydown, this))
    +      }
    +
    +      this.$menu
    +        .on('click', $.proxy(this.click, this))
    +        .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
    +        .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
    +    }
    +
    +  , eventSupported: function(eventName) {
    +      var isSupported = eventName in this.$element
    +      if (!isSupported) {
    +        this.$element.setAttribute(eventName, 'return;')
    +        isSupported = typeof this.$element[eventName] === 'function'
    +      }
    +      return isSupported
    +    }
    +
    +  , move: function (e) {
    +      if (!this.shown) return
    +
    +      switch(e.keyCode) {
    +        case 9: // tab
    +        case 13: // enter
    +        case 27: // escape
    +          e.preventDefault()
    +          break
    +
    +        case 38: // up arrow
    +          e.preventDefault()
    +          this.prev()
    +          break
    +
    +        case 40: // down arrow
    +          e.preventDefault()
    +          this.next()
    +          break
    +      }
    +
    +      e.stopPropagation()
    +    }
    +
    +  , keydown: function (e) {
    +      this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
    +      this.move(e)
    +    }
    +
    +  , keypress: function (e) {
    +      if (this.suppressKeyPressRepeat) return
    +      this.move(e)
    +    }
    +
    +  , keyup: function (e) {
    +      switch(e.keyCode) {
    +        case 40: // down arrow
    +        case 38: // up arrow
    +        case 16: // shift
    +        case 17: // ctrl
    +        case 18: // alt
    +          break
    +
    +        case 9: // tab
    +        case 13: // enter
    +          if (!this.shown) return
    +          this.select()
    +          break
    +
    +        case 27: // escape
    +          if (!this.shown) return
    +          this.hide()
    +          break
    +
    +        default:
    +          this.lookup()
    +      }
    +
    +      e.stopPropagation()
    +      e.preventDefault()
    +  }
    +
    +  , focus: function (e) {
    +      this.focused = true
    +    }
    +
    +  , blur: function (e) {
    +      this.focused = false
    +      if (!this.mousedover && this.shown) this.hide()
    +    }
    +
    +  , click: function (e) {
    +      e.stopPropagation()
    +      e.preventDefault()
    +      this.select()
    +      this.$element.focus()
    +    }
    +
    +  , mouseenter: function (e) {
    +      this.mousedover = true
    +      this.$menu.find('.active').removeClass('active')
    +      $(e.currentTarget).addClass('active')
    +    }
    +
    +  , mouseleave: function (e) {
    +      this.mousedover = false
    +      if (!this.focused && this.shown) this.hide()
    +    }
    +
    +  }
    +
    +
    +  /* TYPEAHEAD PLUGIN DEFINITION
    +   * =========================== */
    +
    +  var old = $.fn.typeahead
    +
    +  $.fn.typeahead = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('typeahead')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.typeahead.defaults = {
    +    source: []
    +  , items: 8
    +  , menu: '<ul class="typeahead dropdown-menu"></ul>'
    +  , item: '<li><a href="#"></a></li>'
    +  , minLength: 1
    +  }
    +
    +  $.fn.typeahead.Constructor = Typeahead
    +
    +
    + /* TYPEAHEAD NO CONFLICT
    +  * =================== */
    +
    +  $.fn.typeahead.noConflict = function () {
    +    $.fn.typeahead = old
    +    return this
    +  }
    +
    +
    + /* TYPEAHEAD DATA-API
    +  * ================== */
    +
    +  $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
    +    var $this = $(this)
    +    if ($this.data('typeahead')) return
    +    $this.typeahead($this.data())
    +  })
    +
    +}(window.jQuery);
    +/* ==========================================================
    + * bootstrap-affix.js v2.3.1
    + * http://twitter.github.com/bootstrap/javascript.html#affix
    + * ==========================================================
    + * Copyright 2012 Twitter, Inc.
    + *
    + * Licensed under the Apache License, Version 2.0 (the "License");
    + * you may not use this file except in compliance with the License.
    + * You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + * ========================================================== */
    +
    +
    +!function ($) {
    +
    +  "use strict"; // jshint ;_;
    +
    +
    + /* AFFIX CLASS DEFINITION
    +  * ====================== */
    +
    +  var Affix = function (element, options) {
    +    this.options = $.extend({}, $.fn.affix.defaults, options)
    +    this.$window = $(window)
    +      .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
    +      .on('click.affix.data-api',  $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
    +    this.$element = $(element)
    +    this.checkPosition()
    +  }
    +
    +  Affix.prototype.checkPosition = function () {
    +    if (!this.$element.is(':visible')) return
    +
    +    var scrollHeight = $(document).height()
    +      , scrollTop = this.$window.scrollTop()
    +      , position = this.$element.offset()
    +      , offset = this.options.offset
    +      , offsetBottom = offset.bottom
    +      , offsetTop = offset.top
    +      , reset = 'affix affix-top affix-bottom'
    +      , affix
    +
    +    if (typeof offset != 'object') offsetBottom = offsetTop = offset
    +    if (typeof offsetTop == 'function') offsetTop = offset.top()
    +    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
    +
    +    affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
    +      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
    +      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
    +      'top'    : false
    +
    +    if (this.affixed === affix) return
    +
    +    this.affixed = affix
    +    this.unpin = affix == 'bottom' ? position.top - scrollTop : null
    +
    +    this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
    +  }
    +
    +
    + /* AFFIX PLUGIN DEFINITION
    +  * ======================= */
    +
    +  var old = $.fn.affix
    +
    +  $.fn.affix = function (option) {
    +    return this.each(function () {
    +      var $this = $(this)
    +        , data = $this.data('affix')
    +        , options = typeof option == 'object' && option
    +      if (!data) $this.data('affix', (data = new Affix(this, options)))
    +      if (typeof option == 'string') data[option]()
    +    })
    +  }
    +
    +  $.fn.affix.Constructor = Affix
    +
    +  $.fn.affix.defaults = {
    +    offset: 0
    +  }
    +
    +
    + /* AFFIX NO CONFLICT
    +  * ================= */
    +
    +  $.fn.affix.noConflict = function () {
    +    $.fn.affix = old
    +    return this
    +  }
    +
    +
    + /* AFFIX DATA-API
    +  * ============== */
    +
    +  $(window).on('load', function () {
    +    $('[data-spy="affix"]').each(function () {
    +      var $spy = $(this)
    +        , data = $spy.data()
    +
    +      data.offset = data.offset || {}
    +
    +      data.offsetBottom && (data.offset.bottom = data.offsetBottom)
    +      data.offsetTop && (data.offset.top = data.offsetTop)
    +
    +      $spy.affix(data)
    +    })
    +  })
    +
    +
    +}(window.jQuery);
    \ No newline at end of file
    diff --git a/mapbox-gl/resources/bootstrap/js/bootstrap.min.js b/mapbox-gl/resources/bootstrap/js/bootstrap.min.js
    new file mode 100644
    index 0000000000..95c5ac5ee6
    --- /dev/null
    +++ b/mapbox-gl/resources/bootstrap/js/bootstrap.min.js
    @@ -0,0 +1,6 @@
    +/*!
    +* Bootstrap.js by @fat & @mdo
    +* Copyright 2012 Twitter, Inc.
    +* http://www.apache.org/licenses/LICENSE-2.0.txt
    +*/
    +!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
    \ No newline at end of file
    diff --git a/mapbox-gl/resources/display-frame-rate.js b/mapbox-gl/resources/display-frame-rate.js
    new file mode 100644
    index 0000000000..241e0af380
    --- /dev/null
    +++ b/mapbox-gl/resources/display-frame-rate.js
    @@ -0,0 +1,39 @@
    +/**
    + * Display frame rate in a span element added to the navigation bar.
    + */
    +(function() {
    +
    +  var container = document.querySelector('.navbar .navbar-inner .container');
    +  if (!container) {
    +    return;
    +  }
    +
    +  if (!window.requestAnimationFrame) {
    +    return;
    +  }
    +
    +  var fpsElement = document.createElement('span');
    +  fpsElement.style.color = 'white';
    +
    +  container.appendChild(fpsElement);
    +
    +  var frameCount = 0;
    +  var begin = +new Date();
    +
    +  window.setInterval(function() {
    +    var end = +new Date();
    +    var milliseconds = end - begin;
    +    var seconds = milliseconds / 1000.0;
    +    var frameRate = frameCount / seconds;
    +    fpsElement.innerHTML = frameRate.toPrecision(4) + ' fps';
    +    frameCount = 0;
    +    begin = end;
    +  }, 500);
    +
    +  var go = function() {
    +    frameCount++;
    +    window.requestAnimationFrame(go);
    +  };
    +  go();
    +
    +})();
    diff --git a/mapbox-gl/resources/example-behaviour.js b/mapbox-gl/resources/example-behaviour.js
    new file mode 100644
    index 0000000000..161f51d790
    --- /dev/null
    +++ b/mapbox-gl/resources/example-behaviour.js
    @@ -0,0 +1,263 @@
    +;(function() {
    +  if (window.location.host === 'localhost:3000') {
    +    return;
    +  }
    +  var container = $('.navbar .navbar-inner .container')[0],
    +      form = document.createElement('form'),
    +      select = document.createElement('select'),
    +      possibleModes = {
    +        'raw' : 'Development',
    +        'advanced': 'Production'
    +      },
    +      urlMode = window.location.href.match(/mode=([a-z0-9\-]+)\&?/i),
    +      curMode = urlMode ? urlMode[1] : 'advanced',
    +      option,
    +      modeIdx,
    +      mode,
    +      modeTxt,
    +      modeChangedMethod;
    +
    +  if (!container) {
    +    return;
    +  }
    +
    +  modeChangedMethod = function() {
    +    var newMode = this.value,
    +        search = window.location.search.substring(1),
    +        baseUrl = window.location.href.split('?')[0],
    +        chunks = search ? search.split('&') : [],
    +        pairs = [],
    +        i,
    +        pair,
    +        adjusted,
    +        modeFound = false;
    +    for (i = chunks.length - 1; i >= 0; --i) {
    +      pair = chunks[i].split('=');
    +      if (pair[0].toLowerCase() === 'mode') {
    +        pair[1] = newMode;
    +        modeFound = true;
    +      }
    +      adjusted = encodeURIComponent(pair[0]);
    +      if (typeof pair[1] !== undefined) {
    +        adjusted += '=' + encodeURIComponent(pair[1] || '');
    +      }
    +      pairs.push(adjusted);
    +    }
    +    if (!modeFound) {
    +      pairs.push('mode=' + encodeURIComponent(newMode));
    +    }
    +    location.href = baseUrl + '?' + pairs.join('&');
    +  };
    +
    +  for (mode in possibleModes) {
    +    if ( possibleModes.hasOwnProperty(mode) ) {
    +      option = document.createElement('option');
    +      modeTxt = possibleModes[mode];
    +      option.value = mode;
    +      option.innerHTML = modeTxt;
    +      option.selected = curMode === mode;
    +      select.appendChild(option);
    +    }
    +  }
    +
    +  $(select).change(modeChangedMethod);
    +  select.className = 'input-medium';
    +
    +  form.className = 'navbar-form pull-right';
    +  form.appendChild(select);
    +
    +  container.appendChild(form);
    +})();
    +
    +var exampleNS = {};
    +
    +exampleNS.getRendererFromQueryString = function() {
    +  var obj = {}, queryString = location.search.slice(1),
    +      re = /([^&=]+)=([^&]*)/g, m;
    +
    +  while (m = re.exec(queryString)) {
    +    obj[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
    +  }
    +  if ('renderers' in obj) {
    +    return obj['renderers'].split(',');
    +  } else if ('renderer' in obj) {
    +    return [obj['renderer']];
    +  } else {
    +    return undefined;
    +  }
    +};
    +
    +/*\
    +|*|
    +|*|  Base64 / binary data / UTF-8 strings utilities
    +|*|
    +|*|  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
    +|*|
    +\*/
    +
    +/* Array of bytes to base64 string decoding */
    +
    +function b64ToUint6 (nChr) {
    +
    +  return nChr > 64 && nChr < 91 ?
    +      nChr - 65
    +    : nChr > 96 && nChr < 123 ?
    +      nChr - 71
    +    : nChr > 47 && nChr < 58 ?
    +      nChr + 4
    +    : nChr === 43 ?
    +      62
    +    : nChr === 47 ?
    +      63
    +    :
    +      0;
    +
    +}
    +
    +function base64DecToArr (sBase64, nBlocksSize) {
    +
    +  var
    +    sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
    +    nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
    +
    +  for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
    +    nMod4 = nInIdx & 3;
    +    nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
    +    if (nMod4 === 3 || nInLen - nInIdx === 1) {
    +      for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
    +        taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
    +      }
    +      nUint24 = 0;
    +
    +    }
    +  }
    +
    +  return taBytes;
    +}
    +
    +/* Base64 string to array encoding */
    +
    +function uint6ToB64 (nUint6) {
    +
    +  return nUint6 < 26 ?
    +      nUint6 + 65
    +    : nUint6 < 52 ?
    +      nUint6 + 71
    +    : nUint6 < 62 ?
    +      nUint6 - 4
    +    : nUint6 === 62 ?
    +      43
    +    : nUint6 === 63 ?
    +      47
    +    :
    +      65;
    +
    +}
    +
    +function base64EncArr (aBytes) {
    +
    +  var nMod3 = 2, sB64Enc = "";
    +
    +  for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
    +    nMod3 = nIdx % 3;
    +    if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
    +    nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
    +    if (nMod3 === 2 || aBytes.length - nIdx === 1) {
    +      sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
    +      nUint24 = 0;
    +    }
    +  }
    +
    +  return sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? '' : nMod3 === 1 ? '=' : '==');
    +
    +}
    +
    +/* UTF-8 array to DOMString and vice versa */
    +
    +function UTF8ArrToStr (aBytes) {
    +
    +  var sView = "";
    +
    +  for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
    +    nPart = aBytes[nIdx];
    +    sView += String.fromCharCode(
    +      nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
    +        /* (nPart - 252 << 32) is not possible in ECMAScript! So...: */
    +        (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
    +      : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
    +        (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
    +      : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
    +        (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
    +      : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
    +        (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
    +      : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
    +        (nPart - 192 << 6) + aBytes[++nIdx] - 128
    +      : /* nPart < 127 ? */ /* one byte */
    +        nPart
    +    );
    +  }
    +
    +  return sView;
    +
    +}
    +
    +function strToUTF8Arr (sDOMStr) {
    +
    +  var aBytes, nChr, nStrLen = sDOMStr.length, nArrLen = 0;
    +
    +  /* mapping... */
    +
    +  for (var nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++) {
    +    nChr = sDOMStr.charCodeAt(nMapIdx);
    +    nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6;
    +  }
    +
    +  aBytes = new Uint8Array(nArrLen);
    +
    +  /* transcription... */
    +
    +  for (var nIdx = 0, nChrIdx = 0; nIdx < nArrLen; nChrIdx++) {
    +    nChr = sDOMStr.charCodeAt(nChrIdx);
    +    if (nChr < 128) {
    +      /* one byte */
    +      aBytes[nIdx++] = nChr;
    +    } else if (nChr < 0x800) {
    +      /* two bytes */
    +      aBytes[nIdx++] = 192 + (nChr >>> 6);
    +      aBytes[nIdx++] = 128 + (nChr & 63);
    +    } else if (nChr < 0x10000) {
    +      /* three bytes */
    +      aBytes[nIdx++] = 224 + (nChr >>> 12);
    +      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    +      aBytes[nIdx++] = 128 + (nChr & 63);
    +    } else if (nChr < 0x200000) {
    +      /* four bytes */
    +      aBytes[nIdx++] = 240 + (nChr >>> 18);
    +      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    +      aBytes[nIdx++] = 128 + (nChr & 63);
    +    } else if (nChr < 0x4000000) {
    +      /* five bytes */
    +      aBytes[nIdx++] = 248 + (nChr >>> 24);
    +      aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    +      aBytes[nIdx++] = 128 + (nChr & 63);
    +    } else /* if (nChr <= 0x7fffffff) */ {
    +      /* six bytes */
    +      aBytes[nIdx++] = 252 + /* (nChr >>> 32) is not possible in ECMAScript! So...: */ (nChr / 1073741824);
    +      aBytes[nIdx++] = 128 + (nChr >>> 24 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
    +      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
    +      aBytes[nIdx++] = 128 + (nChr & 63);
    +    }
    +  }
    +
    +  return aBytes;
    +
    +}
    +
    +exampleNS.strToBase64 = function(str) {
    +  return base64EncArr(strToUTF8Arr(str));
    +};
    diff --git a/mapbox-gl/resources/jquery.min.js b/mapbox-gl/resources/jquery.min.js
    new file mode 100644
    index 0000000000..686ae4bf3b
    --- /dev/null
    +++ b/mapbox-gl/resources/jquery.min.js
    @@ -0,0 +1,4 @@
    +/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
    +*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
    +return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="<a name='"+x+"'></a><div name='"+x+"'></div>",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&&gt(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox|radio)$/i,Ct=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
    +}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b.trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(cn,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCase())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("<div>").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
    diff --git a/mapbox-gl/resources/layout.css b/mapbox-gl/resources/layout.css
    new file mode 100644
    index 0000000000..f4d25d5391
    --- /dev/null
    +++ b/mapbox-gl/resources/layout.css
    @@ -0,0 +1,31 @@
    +@import url(http://fonts.googleapis.com/css?family=Quattrocento+Sans:400,400italic,700);
    +
    +body {
    +  padding-top: 60px;
    +}
    +.map {
    +  height: 400px;
    +  width: 100%;
    +  background: url(textured_paper.jpeg) repeat;
    +}
    +.ol-attribution {
    +  max-width: calc(100% - 3em);
    +}
    +.ol-control button, .ol-attribution, .ol-scale-line-inner {
    +  font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif;
    +}
    +
    +#tags {
    +  display: none;
    +}
    +
    +body, h1, h2, h3, h4, p, li, td, th {
    +  font-family: Quattrocento Sans;
    +}
    +.navbar-inverse .navbar-inner {
    +  background: #1F6B75;
    +}
    +.navbar-inverse .brand {
    +  color: white;
    +  padding: 5px;
    +}
    diff --git a/mapbox-gl/resources/logo-32x32-optimized.png b/mapbox-gl/resources/logo-32x32-optimized.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..b9ae9071624c589dfc328c32835134ca1225d8f7
    GIT binary patch
    literal 1116
    zcmYjPe=yr;7!T5jlCHPW4LgFe`7zZNx6$@UMMZmlCErBo5&0zqr9wE}&QM)#dRAxS
    zbgK2U)9bNm+S<AziAcU7k>8{uKO}M8u6J{~;kw#u+aG)Hc|XtdKF{-h-sidZxt^7A
    z4s+1&AOeBF5Ykd|fX41`v>U*b!L~V|+^TT|5*m#*S~AVx=uw$QtwtbRK*(jz<=vUx
    zRN(aG;d6P!OKcv$=rRkz=kudWD!A2*qDofurOU;=FHiX)5U%fJT_EB2_Vy4!8XFq{
    zLQql?(0h>(L=YHh&(=Q3px25FNAvT`=Z#@hHe0UC6)j?OIRQjETdw9RSF>jp0*EY%
    zcqxcNFO_Km$c!K|gCo@jl2}+0GY|-wRSFwQVLe%>41|~^QY{zOfgCJ}K@uvtGJO!l
    zED$e^8%<b<i6ygwAjYcMbnVV?8LST^GXV)#rUUkV6k-4@gLQ!r10WV+mH&fWSo^QP
    z4Az1t{ug4F!&-3sN^JxQ#976(d;u8ca&VVf0slvvT6qQV7}wYH#Y?~+OlE~rIbnI6
    zh>P6l!txXJ%1?@U37i^y)s+l>Q)XRrR$VioBF!owAVMWsr~s`|gvwlzGEbz;7b!1_
    zRiLB7miEFq6-}ZplBnqtHDg}GlBn2H4X3TMM5Zm3X}R<2{npyMK!u<HUT1F&tf`f2
    z_^`I_o4$rc!>0?n##;lecShQjhW6pHF7<NX4}yN3@$SPX13Ke?-Z&_j8e3ky|8)An
    z^6JF2aO(NmPZH_0$vkT^ixo<l#R6Nb3l^(VuUFe_`sYUdx^2b2Zd@~&tmZYVWzAu?
    z+HF>+-R88fJM9i28xH4&LyC1G!3^c`ax#br7gtv|BogJ0#$XN|^7i$^28V=&M@Ais
    zOFV-kWabb_R62vh<<;^(Yq@&$+V%GKj+?#x_a+_+9z7Atm1?cQWOx4h+wYrOTYvny
    zy}iA&v%CBHFR<P;pHYp2k%b5;$)t~A2CYBw@L}h42z=k6(s<LOo6h&?r#!rV_MU(}
    z6XTPUA0LUY_k9$GjIoJ2D3~%FO7NPqc(MZLws_NU%N_X&vWJEav3%l8>FD~3hVHWZ
    zf&?!nK0@fzojmA&{)Fo5&u@Q7>RWP)D(vdLfr>4P_Coa(bl-5{(a77eaaspmR{R2I
    zUk*+fmyTq{xAd5wP5KZM8eX76Mo)1KZE>eJHu2T*7nYiPXx~oN36GMF={{PCIZh53
    zPKv;mylm~+N!$D`QeT6o9-ldzt~*_^(t4n)a_5&<@RtKok2`|T%EPDlLe-Hkc#B&Y
    z&&~m_?yS3=im>mCv@0G5l6D03{_IfXz5WsRTJ`9p*q^Mp{gf$eee)irB#Mw85?eKg
    z!shDUw_{u4vSszDLz8@p<Q#R=oThubHap2Yw$OBv5fi@AJL;)Q{LvNRH#t-FR@+bu
    P_(lW)pONwaM{D{U2JtM&
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/resources/logo-32x32.png b/mapbox-gl/resources/logo-32x32.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..12382832d9dcd1fd9c81ac3651642c829985c084
    GIT binary patch
    literal 1205
    zcmV;m1WNmfP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800004b3#c}2nYxW
    zd<bNS00009a7bBm0005T0005T0T;pgMgRZ+8FWQhbW?9;ba!ELWdL_~cP?peYja~^
    zaAhuUa%Y?FJQ@H11TslPK~z|U?U!9plvNnV|Ic}M-(}flfmMP000GMnC~7jRk&9#;
    zXP8Mq4QF+xu?E6OZMq2UqLJ2%xN(}A2@8-u92{h&?1Qu!BS&U*62)>PL@1C7bdyj%
    zmi>6&_na=YT99RTfrc)6?%wn9|NrL9bDs0OB!s|h2Afs){{nz+I-9qg`F_42$vQL`
    z*?ByBiFYa*6mAaK0e17km12T;oj?fyAV3;`uY_JMc{d}a<6i@K`P@$yAt^@*SU2$s
    z;nE;ISH5y-eW;ot=fL)s)>JO}ngg2y0GTB4x}E3S_lBw)(g_qdw`J)VIS7~&sxTLD
    zxLn3<A3n9%GcACZn%iE~N%Rf`<WSB33IVkE2<s~zU4A`m06W0?FSNf+5WB)P9tY9K
    z1<xygEbV;A0Kt>(pWp&e1c%B6w7XhF1OmW_ATxqQ<mRA)@|DYKg97Y1b2Z9s;Aa7(
    zPiE@YG>mE*6dpW>?t%z~i^96hj1R`ljbH4N`N2Tu0>F@>!s+p1P*FoNzXL4b(Ku;B
    z9N5@&ad|)h0*Dc^Clc{;j+-7IMpO;cc)ko0lXCnA#+2eW02%-y{0+DO1{D=<O`E!{
    zUqmvp_<xT%fl`1cM(F=`(u6?2*EeJ448X>?sC&lzLzf$Clr0j2CItTQDo|&zU&xtf
    z#)Eo9Oa`F#-f$}0;>|EKF=OTu0k(Jx*2bFPIQ^Xz5Ma$3yY9`JV|u-&Vb_9KJZ>>g
    z+uS5E>|79w%xEJTFSeqh;ahh=fK#VR)s9~0<yQ_I!iZ}W&n8%qH`ffBqIrU>LrJ0)
    z_eaV&*>V~CYCeS!7*i4NsRNw&j!QrH_zLz{qU-waSZg(*XnqU~EIf=ACIengv|^5q
    zp`xJ?dp|k~pQ3)<lfLjX|8*0V;>Gzz*+e)JX^<`Zp3leXv<J}VRq=k`Z44;EZ$wsX
    zBsR>8g2(5B{m>^ked!7S95;Bmt^M=r;h+Ejkn~JZ8e`&1MwGlGZymN~t;Vp%QPp=F
    zU2bK<JrWUYaZz|O+6d>ZL6lS;M(1xgKoD<rr!6WGrKNoQo=KnMrCYbp^{U!20%bn^
    z#3R^k%K>8y-`pC-$pIHUex-PcQO2e?GZq>2Xzl(Ld#Y+NFg&6Np!k}jsydKI(0(9&
    z^Jdv4qANkz_Hb$v$_j0WF`J+W0jE!cOXG-_CB*3N+^!SNKcKAk3;0yEpOM(qRd=vy
    zGX0Qy!sPtI-5@Hv&l<yC+mee_%TmY8kGMUkXlO)z(>Vat#<{k>r@p!?m~L22X>$Gx
    zxgZ<{V0tJe5i61w!eTPw7iT}t{@8(`Q8xf_LMMA{oi#N>p=gHPmZ#VXG6a`C21LfV
    zDK7#0yD?ZQ9FCA5kFWs%fb4db)YiL!fK>v*${?-^rmyd=eeYV>TL1r?&NjfG-=om~
    Tx~Ly-00000NkvXXu0mjfVCE)f
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/resources/logo.png b/mapbox-gl/resources/logo.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..3d9702eb3c2ab40a8613e934624cd7398ca2eaf2
    GIT binary patch
    literal 1365
    zcmV-b1*-aqP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T70000PbVXQnQ*UN;
    zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$_en%SRCwCVm}^WNRTRh1?2CP|?DA}Z
    z%C?|VBQ2&Ssig{utuaPZG(H+r)6%G^8WWAyU^QAzKgb8u_Je3OkrHY(wrL>!P^+d`
    zwLwE4(5ftoWx?+9D34_+y9>KJ<2lnAI<5=Mva^dPxw&&^?w$GN-2eaHnPDK#;5g2J
    zl?97&e`jC0c>d*;x17bwhsDb<jDMl`XW{@;VU>$em0e!nHeZ<C7}>|DR(-e3q(8p<
    zPWuUL4j^bKDS!x<(l+9~qv5aqw<bd2N9XHLpgW3kUoXqB{Em=Q2?azr3&Is1{KNHm
    zkmFX$9j9fO&WH0eU);Pb>pVhw69R~E8J~5#%YPX1G;%j|7wdF+@6EP8joEBP$ZK-O
    zXkyS?);k?nw)aj3b}s~uzYmT(GvsN;d(_}P8wFjmab-||%YXI!pRWy11z!S%!m86W
    z99VnX+j!`n%RG#dW{5T9n1@l?46(+1=3x?H1|`j-NrV}cG!K&~GlV$~{8*;(>C;$8
    zh8l}O#bC|wbrkaqk}esg%s@cnEC<(F77GVKF1GO|gft5!TdTQg9wBoD&;R21B-(yD
    zUl7o>5DQ+GQ*6o!C{3;EY^f-zAixaN+%wL}!27Y>WHijeKqwq@recikWD=9hH?>Xf
    z2tQ`Zg<z8`mkj^xAwa#Hb&fPPfKxdfW+91T*T~IUUoZp#4w4?E#{jM7vvnB9jCD^h
    z4jhJ)04E>HG^fsH1ZOY|EG$kuo?)~S;Mm=1`utp-=09+MBiv%eiWChzk!iB3XiH()
    zuxDq!6;>Fu{~NekyA8~06|X_K*wg3K;Y9AtvqG^+x2SL%)AaCRVFnL(srRarwnXT$
    zE&ce(^<}i>L8(~>Ia)QePD~{o?ztQ@?8v!khngeDtURF4e)}HR=os8ra$`9x)N5gt
    zQO83Dli*(3himfE;C`EN=G>cK9Ea1tIrx_fFqEvR>~as-9qxW8FD`-<trl{0YFLBW
    zbohcWmcYe1TdRV-1vXf1O5xjHzaQQ^b^@BuwNtZQ1oU9FrDtduesXlf286U243Mr-
    zLj`KW;Sa)4kWGjg_AO6`CBh0qaMgA7aJH*^W|nzC|K$r^2x-T|(vgYl(A?I}LuOi1
    zL5JzzWHrFxbQrEog%mXdWlkr&3p!l=@Yn|*fqTp&b~!Y9<kNT6Guzqa3(X?rbSMO0
    zwVs3AbaZvzQvUjSi(WDFM4F+g?IP@IJPH$mfE2D;e6fWHNGBdvQTz0-4nFN0if>iK
    zJkiWhcd7+yn@<8IMf~rCaBoQ8ydeUz10lZ`AoZ8}u7VQgnavD`fBaeO!utT>8X`Xp
    zAPQtJLVk^h>#!_|*E}-JK(qD$!kvlf!#(|}XVXX`SSqQVs@FsP3)M5ar1;k3ho%u<
    zR0C1gwq}`mlZioQAUKKMXg#S!xbX%5JVMZVdi@sLd$&9SRb_Yms}H$+P_jjmp-2wW
    zAT!i9os4RRxc_b;Bz+bf65CtvSqC*$56adcnW65R7U?N6!-4t6sbznQ%wnEZ1^K)#
    zXA9EXuER(9Wos@oDE0#rGr3yGWT|*=^V(7<D_RbDHmd|IfXh@Tzf?)4lwZOSt|Otp
    zfFsQ#Gv+jdqJR?6eWWEBUH2o{D+_ssIvzmO<tagz{3jx5Q*xQK_N8EkgA0AeKLQK@
    X%&nu2)K80z00000NkvXXu0mjfNf?ED
    
    literal 0
    HcmV?d00001
    
    diff --git a/mapbox-gl/resources/textured_paper.jpeg b/mapbox-gl/resources/textured_paper.jpeg
    new file mode 100644
    index 0000000000000000000000000000000000000000..b0bfa66c44529617dfe5d929451b1f6c5fb1a570
    GIT binary patch
    literal 3445
    zcmV-*4T|#r*#F=F5K2Z#MgRc;0RTt<NB{u;+W<oV3I_)W2L=iV2nY)b3JVSx5DpCu
    z4jmO15f~#MB_tvrA|WO#H!mhBG$|n>FhMaiIXgZ+J|!+hNkcqHH$6T(|G)qX2mr_c
    z$N>Qn0RO}Q7ytnQ0RaI30000000000000310s{yC!~iW20RRC60s;X80|WyB0RR91
    z0TBQKAu&Nw5@8~7fw3}?p##C;@Iq42@e}{r00;pA002J#^t(WoMmg%H3@lGGRk&TZ
    zRNE3DPF&`RK}R&WJg)1P9_V83(bQEckjcq4L|wx@n5i<sm#%47d{FsB2`fdSgqrHL
    zZ&HDZ8B)=@d53kCl_8IG<;K3%qEXMPpz)PO-3K!bK4TtFs+D3ed*-uk=k+Zw4-zpu
    zD?{NTg7Y<-OR%}lBdS49j``}ef&&TFXPi!?5~z%2oz)&Q@2Vwko^hJ8T{)jz)MD|2
    zIjvj`+K;VDXQQ^M@_|K)v31A4R0YG=aZ<*08mn;WI%<xkc1>w>`dA7*KcsO*%3R#W
    z4bF;y1)e{8QZ1h<2#IBQ^-J;zwpHSbgT@s1>XP6P%$(3$7jIkYm=*bPByO!v^)x<Q
    z;NDHrvm?H{pg@+WZ}@~wUN5#POng!TmBhr6)X|C2_A0eiM~W8cosBk^Yo30UCg91P
    zRcw;tIvrDLIXt1_YG9bQeQK!}8TwTV$@Zbr4ow~RQ$9HHUYeH~B{`ZgAB^giT|jqQ
    zwmiEU&PNQssn@xxMa~1dt-dEx<5We<9Q{AFWjv4{6v)<_n3FVIK`Rvbmf?dGmicsj
    zYJM{Y`lKYy8<p0vOo5SOS**7)9yyHF<lsL3scai&7w=XJe1{xV$2*+CRlgg&UUEAc
    zk$C$}Y6jH@dg`)=(a+wq4~d3a-PXh|AJ4iL7<s5%q%p`+TNbQtu-S2Ul>BYj^Gq;s
    z_e{&v#yF^D&m|}X!8&U(8DQpPG|bu#(~c?`;nhfM3|4Iq6FO;f#?M&(YKdl9=hYC3
    zU2&eNQ2iw~u6%+&6_svx0jX<tv4EG}ilN47ZkW&+`p^ur5OmgZ%rO*eeACBpZnP4_
    zfE{Y4El(S@T+5rU%(mw#1lI43Yrl1$Bv(vzN&0l1Qs4$dxTs<A2Nj<mE3pi7)oDw2
    zED^3~pVW`<M!<fRti)(d4(lkXfSxwezbMpW6>~mVs`lRy@jbN$VUM*WA-r`}-{}=Y
    zR(Ks$2zf>d$&|o6m7xio<C+{1!__uH3;L#9QrhWvqH0*oWXIOB0uSzWQJCNMs&nLn
    z8pTR7hTLaWNaS<03JJh&cG4GaN$9AxvjHDkkQJPcyQ>Va&qsBl+eO6wDQ#T?>8vQh
    zoz)R%Q~XpuJ3Us6rnNCfN63kauTuy9QR8jUIjLKNX*B{}4EHq*-9AP%RA(;Eiqgnu
    zU6qu$2gFg;)Lm_`!sW|J;t;6~R@!*8B>Sm2-ACw?KBA1S^WLjc2x$CJsO^T9LK0(`
    zsKbxH6*zK{xOt?yZ-f5;m6^U+C5fW~xb}OkU<okQDWBr9qF~p3(i6w3vMG<+mhItt
    zI;cVK(bZR4evpQ9SBnq@vECB1t>P*!RU}`nHy<87MN28`RVv+r2OFqZ&w8lLjt-`w
    zwOmSmYG*deO=x2YJv2wk2RzRdiE*v^p|}Eh=7rCc<kMG_NicO(NZ=EWYQYOT<)uRG
    zPJL2|bi^@1shTdeNqEsYs_Ui9zlsZmKm(?shX_V7wzWU;UH4GS8G52$ElpGLpBL#L
    zimotcW~uSuLUZ-3lB__wIIUcPj(0UIg9qM(6w@!>mPCSIr6*oLy-HhyJ-VT~0zy}g
    zXqcNzk8#CNa7SxVIWdEpM%eu`j;KVqQWH<sr{sM(t7)ltn$))adaPW<up8VpZxAqk
    z)gXdQ8pkvdY<OUtuN9<+Qah!Tn8y`&AU}%F24*?+T9tk_{pc_;#;9=<T5cF;?NUK%
    z46LD6CGud54(iawpm9Ouc;QF*{{R%+!pLWWv1w@C;tH{KYb~SCG`8J>dC&Q(VNO1k
    zmAUd%ZCE@=#Y2ZN6pm%`US^PI%IQ+G5|N4Jr6zJ`Q&)~ZR%d+FfM>7Xq&$9G-`1z8
    zB{jkRXvMfE8)-lXpG`C67}lyr;kPr#y39doA84FZo*vrLzB9M|-4>BI0{8BoCExb?
    z(FpY!sAgv|%wn}=fjNn0d#EQCkq+p#;@QSVYGt-YWtY`J1~HsgRPxsHQXbz~s0*!?
    zV9(yGt)8P#)}`_d3I5Gi+zbG4Q>I=x-B7EUl4>ALeHzLvt;{C5s?=bKBa)W^Y@5wM
    z%#in;(6I{4YL#sX3zuEfmfzDaR2LT*VTzzGy6#xR>YI$Snai4jS=7DJrH5RUlCs3{
    zLksz*3ZP@wsw~5Y)M|!eB2(2;Y~zXNsz`;56ywBQ{?(S*^*X8xTpZSB?Mxk|XiIYi
    z(^Nob4^^hznfUb9Z?JwUgPAE|!4T-F*lBlFwhqaR(7F>7il6Ewxnc}`C@x!VP$0;2
    zTDh29Bbu{3{{VVRg5ML5;<0W;mS1#akp62}yE4B@M8VG!x+d*TPxlopW!tKpjscC7
    zmW;Ek?vEtdC~B54Bdq5&V%Ili%u)hGN&e=n8_C3CvnC7yvXX9*a^R#9i(uk|E_045
    z{{WFVpc2>_&lOs-&Yn7{vp*yW+E%qVo}Mc_%4>L~ZWN-~Sqw)zXfB&@ajHv3I-xQ|
    z=bmdrnPPpk0;*x`sxI6a?Ba(KA^vMX7RiRNIGdm~?zUP-l*!dWZ=aAY*AW#L0f)+~
    zvX2KfF)<JurvU^r-xZhR9d}ohObq~+V2SlxF)`j>b(?TvD_gW@R9@SUlZ;kLA18aI
    zXE}AMW1P_c0NIaJ0z`G1+`d8v@immp%Zg<!-rw4l7d$cQgm`^3QfeU%D;6cz+TA$@
    zAH{BU7UP=A<dQn|OpVTA-4|XKO#RwH2xG>d9vl&jR38oJ)0!a&S@LjF4u>_HU+Ld>
    z#UA0^Z6Z}8y=Jmqo+UL@c@d2FRoF{-4bf{1&Z|=s8h>gnTnu5=EsW#hZh=WZRyWl|
    zxbi*CGRPPW&;khyF%?WnE6hDoTXUGf%w}r3Fzqw-pjO$z**dCzWr3a`)g)X*3{jbu
    zAaHx7KjP``t~IU2Qcq=GON|W!H~9*^_f8TmL!1<#kh|9rS-RX$6PH!1OK0Nyo@*CM
    z5&r-u6y0t!fvtF+AmgvC2^d+9nwYgc?=^m+daSnGKdTQ3TJCxNC|CoD$F{Vujz`fI
    zA)#k+G+M%`o~UAQ5bBU)Y@C&s`eVsI_A&OY%vwtvXPU~9iOW_7QYN)Nv01uPvHVmj
    zJ}!=Fvf}%TZUNuco4;3$#KmQk0U+?u8@CY@mlr_fG4!b*aX(&Yw*;;t61Ks+bH~z#
    z&~il?n4EuV2Zv8JtQN|-<gDFbwz9rDKD486XZyOMObov1(A)dZRh5grGaVdOn~SaZ
    zxfv0wDvllNbwQ^otZUr}x(Hw5uYGsLXjn)lHCTluQ8ZI+zDab#YOD~fwz#Q>Yb9Ae
    zOOJGf2A|DCxs1>g%s%T`wedl9L;;m36UH+K9Mv|(%tUcnOPsv!v?}S|DcSPpgZ}_)
    z9$|IA)46WGIsDV*GX{SZY2**hDHglW=BDW<9b%DHw}UZM=QkviJe868MrG&Ub*X%C
    zZ@tvZfSC7Dn1YZeEIqX(LzmQ6Zg95XofP>OTZo-vpoSMN>X;XC%uq0-OcR=xQXsps
    ztsDC#CInOJ)=WM(XFOJs7+K88OC%CVPw!d6&SH-XTp_I7ekpO7`sQi4mNK83Gvb0l
    z?^Ie_a4MkE`2x&)qU7gXyQoR><BFroK|wzC(D_}Njjds47UYezs75o)>Y_&-e$`v3
    zS&FTa*@=p-AZ5w4W}nnW*L+eMAUuqa!q!G}F^DNyh!&_!&I35z1jedtnDotR)Gk1f
    zn5hNBGx?|z*<kfSK3bMBQ+D&7x~fz^>i0#{wZLkjTSqp?W15Li7A5ykq|8b0e@fVw
    zTa_Z9yAq~w(c^5bu`c8DRiwm{^F^dxaLvbmnjp{0Bl)eFA{&1V5B-Uga!`wzCyHX=
    zP9s!ZxQt?yljCt}I;>0#a>Ua<C@#3}k+r+1=hau-+c#YRbWtLFbdxl<+@Bw$)<YP6
    z?P^O|bKNqTj8;oxZa@1`B9{b=F#gq`9zV}?Bm*4&^)to}YgthqA}19hlaHymqx8AT
    zXIQE6g)tuLACyL28_gM0$z<Tyy3zWGaT=(Rkb;GqV_v3!SYVglDrVG|4-Fs=BPTVV
    z*Ry}kY6;AxEuw}cp}?5jO%IT)I3g&#OGgtijwp8U8l(s`Q|034PU_w#eN#L2#RO%7
    z9m|?ats}%bhK77(NaGZVr}0(a)GfE;XOw0GG+T3UDGFqGshK=j2PAC%)p;7mb?@m}
    zRbXKa^EAu<0Op}LPjEC`$Uny=X|w`0K%?V^+K=jCf!vYBAuEriGRd5G6(y@OvCR-%
    Xq`Bn&)m?nw2&8T?TJa}ra##P^0S#YZ
    
    literal 0
    HcmV?d00001